1 /* Copyright (c) Mark Harmstone 2016-17
3 * This file is part of WinBtrfs.
5 * WinBtrfs is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public Licence as published by
7 * the Free Software Foundation, either version 3 of the Licence, or
8 * (at your option) any later version.
10 * WinBtrfs is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Lesser General Public Licence for more details.
15 * You should have received a copy of the GNU Lesser General Public Licence
16 * along with WinBtrfs. If not, see <http://www.gnu.org/licenses/>. */
18 #include "btrfs_drv.h"
19 #include "btrfsioctl.h"
26 #ifndef FSCTL_CSV_CONTROL
27 #define FSCTL_CSV_CONTROL CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 181, METHOD_BUFFERED, FILE_ANY_ACCESS)
30 #ifndef FSCTL_QUERY_VOLUME_CONTAINER_STATE
31 #define FSCTL_QUERY_VOLUME_CONTAINER_STATE CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 228, METHOD_BUFFERED, FILE_ANY_ACCESS)
36 #define SEF_AVOID_PRIVILEGE_CHECK 0x08 // on MSDN but not in any header files(?)
38 #ifndef _MSC_VER // not in mingw yet
39 #define DEVICE_DSM_FLAG_TRIM_NOT_FS_ALLOCATED 0x80000000
42 #define SEF_SACL_AUTO_INHERIT 0x02
44 extern LIST_ENTRY VcbList
;
45 extern ERESOURCE global_loading_lock
;
46 extern PDRIVER_OBJECT drvobj
;
48 static void mark_subvol_dirty(device_extension
* Vcb
, root
* r
);
50 static NTSTATUS
get_file_ids(PFILE_OBJECT FileObject
, void* data
, ULONG length
) {
51 btrfs_get_file_ids
* bgfi
;
54 if (length
< sizeof(btrfs_get_file_ids
))
55 return STATUS_BUFFER_OVERFLOW
;
58 return STATUS_INVALID_PARAMETER
;
60 fcb
= FileObject
->FsContext
;
63 return STATUS_INVALID_PARAMETER
;
67 bgfi
->subvol
= fcb
->subvol
->id
;
68 bgfi
->inode
= fcb
->inode
;
69 bgfi
->top
= fcb
->Vcb
->root_fileref
->fcb
== fcb
? TRUE
: FALSE
;
71 return STATUS_SUCCESS
;
74 static void get_uuid(BTRFS_UUID
* uuid
) {
78 seed
= KeQueryPerformanceCounter(NULL
);
80 for (i
= 0; i
< 16; i
+=2) {
81 ULONG rand
= RtlRandomEx(&seed
.LowPart
);
83 uuid
->uuid
[i
] = (rand
& 0xff00) >> 8;
84 uuid
->uuid
[i
+1] = rand
& 0xff;
88 static NTSTATUS
snapshot_tree_copy(device_extension
* Vcb
, UINT64 addr
, root
* subvol
, UINT64
* newaddr
, PIRP Irp
, LIST_ENTRY
* rollback
) {
91 write_data_context wtc
;
97 buf
= ExAllocatePoolWithTag(NonPagedPool
, Vcb
->superblock
.node_size
, ALLOC_TAG
);
99 ERR("out of memory\n");
100 return STATUS_INSUFFICIENT_RESOURCES
;
103 wtc
.parity1
= wtc
.parity2
= wtc
.scratch
= NULL
;
104 wtc
.mdl
= wtc
.parity1_mdl
= wtc
.parity2_mdl
= NULL
;
106 Status
= read_data(Vcb
, addr
, Vcb
->superblock
.node_size
, NULL
, TRUE
, buf
, NULL
, NULL
, Irp
, 0, FALSE
, NormalPagePriority
);
107 if (!NT_SUCCESS(Status
)) {
108 ERR("read_data returned %08x\n", Status
);
112 th
= (tree_header
*)buf
;
114 RtlZeroMemory(&t
, sizeof(tree
));
116 t
.header
.level
= th
->level
;
117 t
.header
.tree_id
= t
.root
->id
;
119 Status
= get_tree_new_address(Vcb
, &t
, Irp
, rollback
);
120 if (!NT_SUCCESS(Status
)) {
121 ERR("get_tree_new_address returned %08x\n", Status
);
125 if (!t
.has_new_address
) {
126 ERR("tree new address not set\n");
127 Status
= STATUS_INTERNAL_ERROR
;
131 c
= get_chunk_from_address(Vcb
, t
.new_address
);
134 c
->used
+= Vcb
->superblock
.node_size
;
136 ERR("could not find chunk for address %llx\n", t
.new_address
);
137 Status
= STATUS_INTERNAL_ERROR
;
141 th
->address
= t
.new_address
;
142 th
->tree_id
= subvol
->id
;
143 th
->generation
= Vcb
->superblock
.generation
;
144 th
->fs_uuid
= Vcb
->superblock
.uuid
;
146 if (th
->level
== 0) {
148 leaf_node
* ln
= (leaf_node
*)&th
[1];
150 for (i
= 0; i
< th
->num_items
; i
++) {
151 if (ln
[i
].key
.obj_type
== TYPE_EXTENT_DATA
&& ln
[i
].size
>= sizeof(EXTENT_DATA
) && ln
[i
].offset
+ ln
[i
].size
<= Vcb
->superblock
.node_size
- sizeof(tree_header
)) {
152 EXTENT_DATA
* ed
= (EXTENT_DATA
*)(((UINT8
*)&th
[1]) + ln
[i
].offset
);
154 if ((ed
->type
== EXTENT_TYPE_REGULAR
|| ed
->type
== EXTENT_TYPE_PREALLOC
) && ln
[i
].size
>= sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
)) {
155 EXTENT_DATA2
* ed2
= (EXTENT_DATA2
*)&ed
->data
[0];
157 if (ed2
->size
!= 0) { // not sparse
158 Status
= increase_extent_refcount_data(Vcb
, ed2
->address
, ed2
->size
, subvol
->id
, ln
[i
].key
.obj_id
, ln
[i
].key
.offset
- ed2
->offset
, 1, Irp
);
160 if (!NT_SUCCESS(Status
)) {
161 ERR("increase_extent_refcount_data returned %08x\n", Status
);
170 internal_node
* in
= (internal_node
*)&th
[1];
172 for (i
= 0; i
< th
->num_items
; i
++) {
175 tbr
.offset
= subvol
->id
;
177 Status
= increase_extent_refcount(Vcb
, in
[i
].address
, Vcb
->superblock
.node_size
, TYPE_TREE_BLOCK_REF
, &tbr
, NULL
, th
->level
- 1, Irp
);
178 if (!NT_SUCCESS(Status
)) {
179 ERR("increase_extent_refcount returned %08x\n", Status
);
185 *((UINT32
*)buf
) = ~calc_crc32c(0xffffffff, (UINT8
*)&th
->fs_uuid
, Vcb
->superblock
.node_size
- sizeof(th
->csum
));
187 KeInitializeEvent(&wtc
.Event
, NotificationEvent
, FALSE
);
188 InitializeListHead(&wtc
.stripes
);
189 wtc
.stripes_left
= 0;
191 Status
= write_data(Vcb
, t
.new_address
, buf
, Vcb
->superblock
.node_size
, &wtc
, NULL
, NULL
, FALSE
, 0, NormalPagePriority
);
192 if (!NT_SUCCESS(Status
)) {
193 ERR("write_data returned %08x\n", Status
);
197 if (wtc
.stripes
.Flink
!= &wtc
.stripes
) {
198 BOOL need_wait
= FALSE
;
200 // launch writes and wait
201 le
= wtc
.stripes
.Flink
;
202 while (le
!= &wtc
.stripes
) {
203 write_data_stripe
* stripe
= CONTAINING_RECORD(le
, write_data_stripe
, list_entry
);
205 if (stripe
->status
!= WriteDataStatus_Ignore
) {
207 IoCallDriver(stripe
->device
->devobj
, stripe
->Irp
);
214 KeWaitForSingleObject(&wtc
.Event
, Executive
, KernelMode
, FALSE
, NULL
);
216 le
= wtc
.stripes
.Flink
;
217 while (le
!= &wtc
.stripes
) {
218 write_data_stripe
* stripe
= CONTAINING_RECORD(le
, write_data_stripe
, list_entry
);
220 if (stripe
->status
!= WriteDataStatus_Ignore
&& !NT_SUCCESS(stripe
->iosb
.Status
)) {
221 Status
= stripe
->iosb
.Status
;
222 log_device_error(Vcb
, stripe
->device
, BTRFS_DEV_STAT_WRITE_ERRORS
);
229 free_write_data_stripes(&wtc
);
233 if (NT_SUCCESS(Status
))
234 *newaddr
= t
.new_address
;
244 void flush_subvol_fcbs(root
* subvol
) {
245 LIST_ENTRY
* le
= subvol
->fcbs
.Flink
;
247 if (IsListEmpty(&subvol
->fcbs
))
250 while (le
!= &subvol
->fcbs
) {
251 struct _fcb
* fcb
= CONTAINING_RECORD(le
, struct _fcb
, list_entry
);
252 IO_STATUS_BLOCK iosb
;
254 if (fcb
->type
!= BTRFS_TYPE_DIRECTORY
&& !fcb
->deleted
)
255 CcFlushCache(&fcb
->nonpaged
->segment_object
, NULL
, 0, &iosb
);
261 static NTSTATUS
do_create_snapshot(device_extension
* Vcb
, PFILE_OBJECT parent
, fcb
* subvol_fcb
, PANSI_STRING utf8
, PUNICODE_STRING name
, BOOL readonly
, PIRP Irp
) {
265 root
*r
, *subvol
= subvol_fcb
->subvol
;
268 UINT64 address
, *root_num
;
271 fcb
* fcb
= parent
->FsContext
;
272 ccb
* ccb
= parent
->FsContext2
;
274 file_ref
*fileref
, *fr
;
275 dir_child
* dc
= NULL
;
278 ERR("error - ccb was NULL\n");
279 return STATUS_INTERNAL_ERROR
;
282 if (!(ccb
->access
& FILE_ADD_SUBDIRECTORY
)) {
283 WARN("insufficient privileges\n");
284 return STATUS_ACCESS_DENIED
;
287 fileref
= ccb
->fileref
;
289 if (fileref
->fcb
== Vcb
->dummy_fcb
)
290 return STATUS_ACCESS_DENIED
;
292 // flush open files on this subvol
294 flush_subvol_fcbs(subvol
);
299 Status
= do_write(Vcb
, Irp
);
301 Status
= STATUS_SUCCESS
;
305 if (!NT_SUCCESS(Status
)) {
306 ERR("do_write returned %08x\n", Status
);
310 InitializeListHead(&rollback
);
314 id
= InterlockedIncrement64(&Vcb
->root_root
->lastinode
);
315 Status
= create_root(Vcb
, id
, &r
, TRUE
, Vcb
->superblock
.generation
, Irp
);
317 if (!NT_SUCCESS(Status
)) {
318 ERR("create_root returned %08x\n", Status
);
322 r
->lastinode
= subvol
->lastinode
;
324 if (!Vcb
->uuid_root
) {
327 TRACE("uuid root doesn't exist, creating it\n");
329 Status
= create_root(Vcb
, BTRFS_ROOT_UUID
, &uuid_root
, FALSE
, 0, Irp
);
331 if (!NT_SUCCESS(Status
)) {
332 ERR("create_root returned %08x\n", Status
);
336 Vcb
->uuid_root
= uuid_root
;
339 root_num
= ExAllocatePoolWithTag(PagedPool
, sizeof(UINT64
), ALLOC_TAG
);
341 ERR("out of memory\n");
342 Status
= STATUS_INSUFFICIENT_RESOURCES
;
349 get_uuid(&r
->root_item
.uuid
);
351 RtlCopyMemory(&searchkey
.obj_id
, &r
->root_item
.uuid
, sizeof(UINT64
));
352 searchkey
.obj_type
= TYPE_SUBVOL_UUID
;
353 RtlCopyMemory(&searchkey
.offset
, &r
->root_item
.uuid
.uuid
[sizeof(UINT64
)], sizeof(UINT64
));
355 Status
= find_item(Vcb
, Vcb
->uuid_root
, &tp
, &searchkey
, FALSE
, Irp
);
356 } while (NT_SUCCESS(Status
) && !keycmp(searchkey
, tp
.item
->key
));
360 Status
= insert_tree_item(Vcb
, Vcb
->uuid_root
, searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
, root_num
, sizeof(UINT64
), NULL
, Irp
);
361 if (!NT_SUCCESS(Status
)) {
362 ERR("insert_tree_item returned %08x\n", Status
);
363 ExFreePool(root_num
);
367 searchkey
.obj_id
= r
->id
;
368 searchkey
.obj_type
= TYPE_ROOT_ITEM
;
369 searchkey
.offset
= 0xffffffffffffffff;
371 Status
= find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, FALSE
, Irp
);
372 if (!NT_SUCCESS(Status
)) {
373 ERR("error - find_item returned %08x\n", Status
);
377 Status
= snapshot_tree_copy(Vcb
, subvol
->root_item
.block_number
, r
, &address
, Irp
, &rollback
);
378 if (!NT_SUCCESS(Status
)) {
379 ERR("snapshot_tree_copy returned %08x\n", Status
);
383 KeQuerySystemTime(&time
);
384 win_time_to_unix(time
, &now
);
386 r
->root_item
.inode
.generation
= 1;
387 r
->root_item
.inode
.st_size
= 3;
388 r
->root_item
.inode
.st_blocks
= subvol
->root_item
.inode
.st_blocks
;
389 r
->root_item
.inode
.st_nlink
= 1;
390 r
->root_item
.inode
.st_mode
= __S_IFDIR
| S_IRUSR
| S_IWUSR
| S_IXUSR
| S_IRGRP
| S_IXGRP
| S_IROTH
| S_IXOTH
; // 40755
391 r
->root_item
.inode
.flags
= 0xffffffff80000000; // FIXME - find out what these mean
392 r
->root_item
.generation
= Vcb
->superblock
.generation
;
393 r
->root_item
.objid
= subvol
->root_item
.objid
;
394 r
->root_item
.block_number
= address
;
395 r
->root_item
.bytes_used
= subvol
->root_item
.bytes_used
;
396 r
->root_item
.last_snapshot_generation
= Vcb
->superblock
.generation
;
397 r
->root_item
.root_level
= subvol
->root_item
.root_level
;
398 r
->root_item
.generation2
= Vcb
->superblock
.generation
;
399 r
->root_item
.parent_uuid
= subvol
->root_item
.uuid
;
400 r
->root_item
.ctransid
= subvol
->root_item
.ctransid
;
401 r
->root_item
.otransid
= Vcb
->superblock
.generation
;
402 r
->root_item
.ctime
= subvol
->root_item
.ctime
;
403 r
->root_item
.otime
= now
;
406 r
->root_item
.flags
|= BTRFS_SUBVOL_READONLY
;
408 r
->treeholder
.address
= address
;
410 // FIXME - do we need to copy over the send and receive fields too?
412 if (tp
.item
->key
.obj_id
!= searchkey
.obj_id
|| tp
.item
->key
.obj_type
!= searchkey
.obj_type
) {
413 ERR("error - could not find ROOT_ITEM for subvol %llx\n", r
->id
);
414 Status
= STATUS_INTERNAL_ERROR
;
418 RtlCopyMemory(tp
.item
->data
, &r
->root_item
, sizeof(ROOT_ITEM
));
420 // update ROOT_ITEM of original subvol
422 subvol
->root_item
.last_snapshot_generation
= Vcb
->superblock
.generation
;
424 mark_subvol_dirty(Vcb
, subvol
);
426 // create fileref for entry in other subvolume
428 fr
= create_fileref(Vcb
);
430 ERR("out of memory\n");
431 Status
= STATUS_INSUFFICIENT_RESOURCES
;
435 Status
= open_fcb(Vcb
, r
, r
->root_item
.objid
, BTRFS_TYPE_DIRECTORY
, utf8
, fcb
, &fr
->fcb
, PagedPool
, Irp
);
436 if (!NT_SUCCESS(Status
)) {
437 ERR("open_fcb returned %08x\n", Status
);
438 free_fileref(Vcb
, fr
);
442 fr
->parent
= fileref
;
444 Status
= add_dir_child(fileref
->fcb
, r
->id
, TRUE
, utf8
, name
, BTRFS_TYPE_DIRECTORY
, &dc
);
445 if (!NT_SUCCESS(Status
))
446 WARN("add_dir_child returned %08x\n", Status
);
451 ExAcquireResourceExclusiveLite(&fileref
->nonpaged
->children_lock
, TRUE
);
452 InsertTailList(&fileref
->children
, &fr
->list_entry
);
453 ExReleaseResourceLite(&fileref
->nonpaged
->children_lock
);
455 increase_fileref_refcount(fileref
);
458 mark_fileref_dirty(fr
);
460 if (fr
->fcb
->type
== BTRFS_TYPE_DIRECTORY
)
461 fr
->fcb
->fileref
= fr
;
463 fr
->fcb
->subvol
->parent
= fileref
->fcb
->subvol
->id
;
465 free_fileref(Vcb
, fr
);
467 // change fcb's INODE_ITEM
469 fcb
->inode_item
.transid
= Vcb
->superblock
.generation
;
470 fcb
->inode_item
.sequence
++;
471 fcb
->inode_item
.st_size
+= utf8
->Length
* 2;
473 if (!ccb
->user_set_change_time
)
474 fcb
->inode_item
.st_ctime
= now
;
476 if (!ccb
->user_set_write_time
)
477 fcb
->inode_item
.st_mtime
= now
;
479 fcb
->inode_item_changed
= TRUE
;
482 fcb
->subvol
->root_item
.ctime
= now
;
483 fcb
->subvol
->root_item
.ctransid
= Vcb
->superblock
.generation
;
485 send_notification_fileref(fr
, FILE_NOTIFY_CHANGE_DIR_NAME
, FILE_ACTION_ADDED
, NULL
);
486 send_notification_fileref(fr
->parent
, FILE_NOTIFY_CHANGE_LAST_WRITE
, FILE_ACTION_MODIFIED
, NULL
);
488 le
= subvol
->fcbs
.Flink
;
489 while (le
!= &subvol
->fcbs
) {
490 struct _fcb
* fcb2
= CONTAINING_RECORD(le
, struct _fcb
, list_entry
);
491 LIST_ENTRY
* le2
= fcb2
->extents
.Flink
;
493 while (le2
!= &fcb2
->extents
) {
494 extent
* ext
= CONTAINING_RECORD(le2
, extent
, list_entry
);
505 Status
= do_write(Vcb
, Irp
);
509 if (!NT_SUCCESS(Status
))
510 ERR("do_write returned %08x\n", Status
);
513 if (NT_SUCCESS(Status
))
514 clear_rollback(&rollback
);
516 do_rollback(Vcb
, &rollback
);
521 static NTSTATUS
create_snapshot(device_extension
* Vcb
, PFILE_OBJECT FileObject
, void* data
, ULONG length
, PIRP Irp
) {
522 PFILE_OBJECT subvol_obj
;
524 btrfs_create_snapshot
* bcs
= data
;
527 BOOL readonly
, posix
;
529 UNICODE_STRING nameus
;
533 file_ref
*fileref
, *fr2
;
536 if (IoIs32bitProcess(Irp
)) {
537 btrfs_create_snapshot32
* bcs32
= data
;
539 if (length
< offsetof(btrfs_create_snapshot32
, name
))
540 return STATUS_INVALID_PARAMETER
;
542 if (length
< offsetof(btrfs_create_snapshot32
, name
) + bcs32
->namelen
)
543 return STATUS_INVALID_PARAMETER
;
545 subvolh
= Handle32ToHandle(bcs32
->subvol
);
547 nameus
.Buffer
= bcs32
->name
;
548 nameus
.Length
= nameus
.MaximumLength
= bcs32
->namelen
;
550 readonly
= bcs32
->readonly
;
551 posix
= bcs32
->posix
;
554 if (length
< offsetof(btrfs_create_snapshot
, name
))
555 return STATUS_INVALID_PARAMETER
;
557 if (length
< offsetof(btrfs_create_snapshot
, name
) + bcs
->namelen
)
558 return STATUS_INVALID_PARAMETER
;
560 subvolh
= bcs
->subvol
;
562 nameus
.Buffer
= bcs
->name
;
563 nameus
.Length
= nameus
.MaximumLength
= bcs
->namelen
;
565 readonly
= bcs
->readonly
;
572 return STATUS_INVALID_PARAMETER
;
574 if (!FileObject
|| !FileObject
->FsContext
)
575 return STATUS_INVALID_PARAMETER
;
577 fcb
= FileObject
->FsContext
;
578 ccb
= FileObject
->FsContext2
;
580 if (!fcb
|| !ccb
|| fcb
->type
!= BTRFS_TYPE_DIRECTORY
)
581 return STATUS_INVALID_PARAMETER
;
583 fileref
= ccb
->fileref
;
586 ERR("fileref was NULL\n");
587 return STATUS_INVALID_PARAMETER
;
590 if (!(ccb
->access
& FILE_ADD_SUBDIRECTORY
)) {
591 WARN("insufficient privileges\n");
592 return STATUS_ACCESS_DENIED
;
596 return STATUS_MEDIA_WRITE_PROTECTED
;
598 if (is_subvol_readonly(fcb
->subvol
, Irp
))
599 return STATUS_ACCESS_DENIED
;
601 if (!is_file_name_valid(&nameus
, posix
))
602 return STATUS_OBJECT_NAME_INVALID
;
606 Status
= RtlUnicodeToUTF8N(NULL
, 0, &len
, nameus
.Buffer
, nameus
.Length
);
607 if (!NT_SUCCESS(Status
)) {
608 ERR("RtlUnicodeToUTF8N failed with error %08x\n", Status
);
613 ERR("RtlUnicodeToUTF8N returned a length of 0\n");
614 return STATUS_INTERNAL_ERROR
;
618 ERR("len was too long\n");
619 return STATUS_INVALID_PARAMETER
;
622 utf8
.MaximumLength
= utf8
.Length
= (USHORT
)len
;
623 utf8
.Buffer
= ExAllocatePoolWithTag(PagedPool
, utf8
.Length
, ALLOC_TAG
);
626 ERR("out of memory\n");
627 return STATUS_INSUFFICIENT_RESOURCES
;
630 Status
= RtlUnicodeToUTF8N(utf8
.Buffer
, len
, &len
, nameus
.Buffer
, nameus
.Length
);
631 if (!NT_SUCCESS(Status
)) {
632 ERR("RtlUnicodeToUTF8N failed with error %08x\n", Status
);
636 ExAcquireResourceExclusiveLite(&Vcb
->tree_lock
, TRUE
);
638 // no need for fcb_lock as we have tree_lock exclusively
639 Status
= open_fileref(fcb
->Vcb
, &fr2
, &nameus
, fileref
, FALSE
, NULL
, NULL
, PagedPool
, ccb
->case_sensitive
|| posix
, Irp
);
641 if (NT_SUCCESS(Status
)) {
643 WARN("file already exists\n");
644 free_fileref(Vcb
, fr2
);
645 Status
= STATUS_OBJECT_NAME_COLLISION
;
648 free_fileref(Vcb
, fr2
);
649 } else if (!NT_SUCCESS(Status
) && Status
!= STATUS_OBJECT_NAME_NOT_FOUND
) {
650 ERR("open_fileref returned %08x\n", Status
);
654 Status
= ObReferenceObjectByHandle(subvolh
, 0, *IoFileObjectType
, Irp
->RequestorMode
, (void**)&subvol_obj
, NULL
);
655 if (!NT_SUCCESS(Status
)) {
656 ERR("ObReferenceObjectByHandle returned %08x\n", Status
);
660 if (subvol_obj
->DeviceObject
!= FileObject
->DeviceObject
) {
661 Status
= STATUS_INVALID_PARAMETER
;
665 subvol_fcb
= subvol_obj
->FsContext
;
667 Status
= STATUS_INVALID_PARAMETER
;
671 if (subvol_fcb
->inode
!= subvol_fcb
->subvol
->root_item
.objid
) {
672 WARN("handle inode was %llx, expected %llx\n", subvol_fcb
->inode
, subvol_fcb
->subvol
->root_item
.objid
);
673 Status
= STATUS_INVALID_PARAMETER
;
677 ccb
= subvol_obj
->FsContext2
;
680 Status
= STATUS_INVALID_PARAMETER
;
684 if (!(ccb
->access
& FILE_TRAVERSE
)) {
685 WARN("insufficient privileges\n");
686 Status
= STATUS_ACCESS_DENIED
;
690 if (fcb
== Vcb
->dummy_fcb
) {
691 Status
= STATUS_ACCESS_DENIED
;
695 // clear unique flag on extents of open files in subvol
696 if (!IsListEmpty(&subvol_fcb
->subvol
->fcbs
)) {
697 LIST_ENTRY
* le
= subvol_fcb
->subvol
->fcbs
.Flink
;
699 while (le
!= &subvol_fcb
->subvol
->fcbs
) {
700 struct _fcb
* openfcb
= CONTAINING_RECORD(le
, struct _fcb
, list_entry
);
703 le2
= openfcb
->extents
.Flink
;
705 while (le2
!= &openfcb
->extents
) {
706 extent
* ext
= CONTAINING_RECORD(le2
, extent
, list_entry
);
717 Status
= do_create_snapshot(Vcb
, FileObject
, subvol_fcb
, &utf8
, &nameus
, readonly
, Irp
);
719 if (NT_SUCCESS(Status
)) {
722 Status
= open_fileref(Vcb
, &fr
, &nameus
, fileref
, FALSE
, NULL
, NULL
, PagedPool
, FALSE
, Irp
);
724 if (!NT_SUCCESS(Status
)) {
725 ERR("open_fileref returned %08x\n", Status
);
726 Status
= STATUS_SUCCESS
;
728 send_notification_fileref(fr
, FILE_NOTIFY_CHANGE_DIR_NAME
, FILE_ACTION_ADDED
, NULL
);
729 free_fileref(Vcb
, fr
);
734 ObDereferenceObject(subvol_obj
);
737 ExReleaseResourceLite(&Vcb
->tree_lock
);
740 ExFreePool(utf8
.Buffer
);
745 static NTSTATUS
create_subvol(device_extension
* Vcb
, PFILE_OBJECT FileObject
, void* data
, ULONG datalen
, PIRP Irp
) {
746 btrfs_create_subvol
* bcs
;
747 fcb
*fcb
, *rootfcb
= NULL
;
757 UNICODE_STRING nameus
;
762 SECURITY_SUBJECT_CONTEXT subjcont
;
766 file_ref
*fr
= NULL
, *fr2
;
767 dir_child
* dc
= NULL
;
769 fcb
= FileObject
->FsContext
;
771 ERR("error - fcb was NULL\n");
772 return STATUS_INTERNAL_ERROR
;
775 ccb
= FileObject
->FsContext2
;
777 ERR("error - ccb was NULL\n");
778 return STATUS_INTERNAL_ERROR
;
781 fileref
= ccb
->fileref
;
783 if (fcb
->type
!= BTRFS_TYPE_DIRECTORY
) {
784 ERR("parent FCB was not a directory\n");
785 return STATUS_NOT_A_DIRECTORY
;
789 ERR("fileref was NULL\n");
790 return STATUS_INVALID_PARAMETER
;
793 if (fileref
->deleted
|| fcb
->deleted
) {
794 ERR("parent has been deleted\n");
795 return STATUS_FILE_DELETED
;
798 if (!(ccb
->access
& FILE_ADD_SUBDIRECTORY
)) {
799 WARN("insufficient privileges\n");
800 return STATUS_ACCESS_DENIED
;
804 return STATUS_MEDIA_WRITE_PROTECTED
;
806 if (is_subvol_readonly(fcb
->subvol
, Irp
))
807 return STATUS_ACCESS_DENIED
;
809 if (fcb
== Vcb
->dummy_fcb
)
810 return STATUS_ACCESS_DENIED
;
812 if (!data
|| datalen
< sizeof(btrfs_create_subvol
))
813 return STATUS_INVALID_PARAMETER
;
815 bcs
= (btrfs_create_subvol
*)data
;
817 if (offsetof(btrfs_create_subvol
, name
[0]) + bcs
->namelen
> datalen
)
818 return STATUS_INVALID_PARAMETER
;
820 nameus
.Length
= nameus
.MaximumLength
= bcs
->namelen
;
821 nameus
.Buffer
= bcs
->name
;
823 if (!is_file_name_valid(&nameus
, bcs
->posix
))
824 return STATUS_OBJECT_NAME_INVALID
;
828 Status
= RtlUnicodeToUTF8N(NULL
, 0, &len
, nameus
.Buffer
, nameus
.Length
);
829 if (!NT_SUCCESS(Status
)) {
830 ERR("RtlUnicodeToUTF8N failed with error %08x\n", Status
);
835 ERR("RtlUnicodeToUTF8N returned a length of 0\n");
836 return STATUS_INTERNAL_ERROR
;
840 ERR("len was too long\n");
841 return STATUS_INVALID_PARAMETER
;
844 utf8
.MaximumLength
= utf8
.Length
= (USHORT
)len
;
845 utf8
.Buffer
= ExAllocatePoolWithTag(PagedPool
, utf8
.Length
, ALLOC_TAG
);
848 ERR("out of memory\n");
849 return STATUS_INSUFFICIENT_RESOURCES
;
852 Status
= RtlUnicodeToUTF8N(utf8
.Buffer
, len
, &len
, nameus
.Buffer
, nameus
.Length
);
853 if (!NT_SUCCESS(Status
)) {
854 ERR("RtlUnicodeToUTF8N failed with error %08x\n", Status
);
858 ExAcquireResourceExclusiveLite(&Vcb
->tree_lock
, TRUE
);
860 KeQuerySystemTime(&time
);
861 win_time_to_unix(time
, &now
);
863 // no need for fcb_lock as we have tree_lock exclusively
864 Status
= open_fileref(fcb
->Vcb
, &fr2
, &nameus
, fileref
, FALSE
, NULL
, NULL
, PagedPool
, ccb
->case_sensitive
|| bcs
->posix
, Irp
);
866 if (NT_SUCCESS(Status
)) {
868 WARN("file already exists\n");
869 free_fileref(Vcb
, fr2
);
870 Status
= STATUS_OBJECT_NAME_COLLISION
;
873 free_fileref(Vcb
, fr2
);
874 } else if (!NT_SUCCESS(Status
) && Status
!= STATUS_OBJECT_NAME_NOT_FOUND
) {
875 ERR("open_fileref returned %08x\n", Status
);
879 id
= InterlockedIncrement64(&Vcb
->root_root
->lastinode
);
880 Status
= create_root(Vcb
, id
, &r
, FALSE
, 0, Irp
);
882 if (!NT_SUCCESS(Status
)) {
883 ERR("create_root returned %08x\n", Status
);
887 TRACE("created root %llx\n", id
);
889 if (!Vcb
->uuid_root
) {
892 TRACE("uuid root doesn't exist, creating it\n");
894 Status
= create_root(Vcb
, BTRFS_ROOT_UUID
, &uuid_root
, FALSE
, 0, Irp
);
896 if (!NT_SUCCESS(Status
)) {
897 ERR("create_root returned %08x\n", Status
);
901 Vcb
->uuid_root
= uuid_root
;
904 root_num
= ExAllocatePoolWithTag(PagedPool
, sizeof(UINT64
), ALLOC_TAG
);
906 ERR("out of memory\n");
907 Status
= STATUS_INSUFFICIENT_RESOURCES
;
914 get_uuid(&r
->root_item
.uuid
);
916 RtlCopyMemory(&searchkey
.obj_id
, &r
->root_item
.uuid
, sizeof(UINT64
));
917 searchkey
.obj_type
= TYPE_SUBVOL_UUID
;
918 RtlCopyMemory(&searchkey
.offset
, &r
->root_item
.uuid
.uuid
[sizeof(UINT64
)], sizeof(UINT64
));
920 Status
= find_item(Vcb
, Vcb
->uuid_root
, &tp
, &searchkey
, FALSE
, Irp
);
921 } while (NT_SUCCESS(Status
) && !keycmp(searchkey
, tp
.item
->key
));
925 Status
= insert_tree_item(Vcb
, Vcb
->uuid_root
, searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
, root_num
, sizeof(UINT64
), NULL
, Irp
);
926 if (!NT_SUCCESS(Status
)) {
927 ERR("insert_tree_item returned %08x\n", Status
);
928 ExFreePool(root_num
);
932 r
->root_item
.inode
.generation
= 1;
933 r
->root_item
.inode
.st_size
= 3;
934 r
->root_item
.inode
.st_blocks
= Vcb
->superblock
.node_size
;
935 r
->root_item
.inode
.st_nlink
= 1;
936 r
->root_item
.inode
.st_mode
= __S_IFDIR
| S_IRUSR
| S_IWUSR
| S_IXUSR
| S_IRGRP
| S_IXGRP
| S_IROTH
| S_IXOTH
; // 40755
937 r
->root_item
.inode
.flags
= 0xffffffff80000000; // FIXME - find out what these mean
940 r
->root_item
.flags
|= BTRFS_SUBVOL_READONLY
;
942 r
->root_item
.objid
= SUBVOL_ROOT_INODE
;
943 r
->root_item
.bytes_used
= Vcb
->superblock
.node_size
;
944 r
->root_item
.ctransid
= Vcb
->superblock
.generation
;
945 r
->root_item
.otransid
= Vcb
->superblock
.generation
;
946 r
->root_item
.ctime
= now
;
947 r
->root_item
.otime
= now
;
949 // add .. inode to new subvol
951 rootfcb
= create_fcb(Vcb
, PagedPool
);
953 ERR("out of memory\n");
954 Status
= STATUS_INSUFFICIENT_RESOURCES
;
961 rootfcb
->inode
= SUBVOL_ROOT_INODE
;
962 rootfcb
->type
= BTRFS_TYPE_DIRECTORY
;
964 rootfcb
->inode_item
.generation
= Vcb
->superblock
.generation
;
965 rootfcb
->inode_item
.transid
= Vcb
->superblock
.generation
;
966 rootfcb
->inode_item
.st_nlink
= 1;
967 rootfcb
->inode_item
.st_mode
= __S_IFDIR
| inherit_mode(fileref
->fcb
, TRUE
);
968 rootfcb
->inode_item
.st_atime
= rootfcb
->inode_item
.st_ctime
= rootfcb
->inode_item
.st_mtime
= rootfcb
->inode_item
.otime
= now
;
969 rootfcb
->inode_item
.st_gid
= GID_NOBODY
;
971 rootfcb
->atts
= get_file_attributes(Vcb
, rootfcb
->subvol
, rootfcb
->inode
, rootfcb
->type
, FALSE
, TRUE
, Irp
);
973 if (r
->root_item
.flags
& BTRFS_SUBVOL_READONLY
)
974 rootfcb
->atts
|= FILE_ATTRIBUTE_READONLY
;
976 SeCaptureSubjectContext(&subjcont
);
978 Status
= SeAssignSecurity(fcb
->sd
, NULL
, (void**)&rootfcb
->sd
, TRUE
, &subjcont
, IoGetFileObjectGenericMapping(), PagedPool
);
980 if (!NT_SUCCESS(Status
)) {
981 ERR("SeAssignSecurity returned %08x\n", Status
);
986 ERR("SeAssignSecurity returned NULL security descriptor\n");
987 Status
= STATUS_INTERNAL_ERROR
;
991 Status
= RtlGetOwnerSecurityDescriptor(rootfcb
->sd
, &owner
, &defaulted
);
992 if (!NT_SUCCESS(Status
)) {
993 ERR("RtlGetOwnerSecurityDescriptor returned %08x\n", Status
);
994 rootfcb
->inode_item
.st_uid
= UID_NOBODY
;
995 rootfcb
->sd_dirty
= TRUE
;
997 rootfcb
->inode_item
.st_uid
= sid_to_uid(owner
);
998 rootfcb
->sd_dirty
= rootfcb
->inode_item
.st_uid
== UID_NOBODY
;
1001 find_gid(rootfcb
, fileref
->fcb
, &subjcont
);
1003 rootfcb
->inode_item_changed
= TRUE
;
1005 ExAcquireResourceExclusiveLite(&Vcb
->fcb_lock
, TRUE
);
1006 InsertTailList(&r
->fcbs
, &rootfcb
->list_entry
);
1007 InsertTailList(&Vcb
->all_fcbs
, &rootfcb
->list_entry_all
);
1008 ExReleaseResourceLite(&Vcb
->fcb_lock
);
1010 rootfcb
->Header
.IsFastIoPossible
= fast_io_possible(rootfcb
);
1011 rootfcb
->Header
.AllocationSize
.QuadPart
= 0;
1012 rootfcb
->Header
.FileSize
.QuadPart
= 0;
1013 rootfcb
->Header
.ValidDataLength
.QuadPart
= 0;
1015 rootfcb
->created
= TRUE
;
1017 if (fileref
->fcb
->inode_item
.flags
& BTRFS_INODE_COMPRESS
)
1018 rootfcb
->inode_item
.flags
|= BTRFS_INODE_COMPRESS
;
1020 rootfcb
->prop_compression
= fileref
->fcb
->prop_compression
;
1021 rootfcb
->prop_compression_changed
= rootfcb
->prop_compression
!= PropCompression_None
;
1023 r
->lastinode
= rootfcb
->inode
;
1027 irsize
= (UINT16
)(offsetof(INODE_REF
, name
[0]) + strlen(DOTDOT
));
1028 ir
= ExAllocatePoolWithTag(PagedPool
, irsize
, ALLOC_TAG
);
1030 ERR("out of memory\n");
1031 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1036 ir
->n
= (USHORT
)strlen(DOTDOT
);
1037 RtlCopyMemory(ir
->name
, DOTDOT
, ir
->n
);
1039 Status
= insert_tree_item(Vcb
, r
, r
->root_item
.objid
, TYPE_INODE_REF
, r
->root_item
.objid
, ir
, irsize
, NULL
, Irp
);
1040 if (!NT_SUCCESS(Status
)) {
1041 ERR("insert_tree_item returned %08x\n", Status
);
1046 // create fileref for entry in other subvolume
1048 fr
= create_fileref(Vcb
);
1050 ERR("out of memory\n");
1052 ExAcquireResourceExclusiveLite(&Vcb
->fcb_lock
, TRUE
);
1053 free_fcb(Vcb
, rootfcb
);
1054 ExReleaseResourceLite(&Vcb
->fcb_lock
);
1056 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1062 mark_fcb_dirty(rootfcb
);
1064 fr
->parent
= fileref
;
1066 Status
= add_dir_child(fileref
->fcb
, r
->id
, TRUE
, &utf8
, &nameus
, BTRFS_TYPE_DIRECTORY
, &dc
);
1067 if (!NT_SUCCESS(Status
))
1068 WARN("add_dir_child returned %08x\n", Status
);
1073 fr
->fcb
->hash_ptrs
= ExAllocatePoolWithTag(PagedPool
, sizeof(LIST_ENTRY
*) * 256, ALLOC_TAG
);
1074 if (!fr
->fcb
->hash_ptrs
) {
1075 ERR("out of memory\n");
1076 ExAcquireResourceExclusiveLite(&Vcb
->fcb_lock
, TRUE
);
1077 free_fileref(Vcb
, fr
);
1078 ExReleaseResourceLite(&Vcb
->fcb_lock
);
1079 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1083 RtlZeroMemory(fr
->fcb
->hash_ptrs
, sizeof(LIST_ENTRY
*) * 256);
1085 fr
->fcb
->hash_ptrs_uc
= ExAllocatePoolWithTag(PagedPool
, sizeof(LIST_ENTRY
*) * 256, ALLOC_TAG
);
1086 if (!fr
->fcb
->hash_ptrs_uc
) {
1087 ERR("out of memory\n");
1088 ExAcquireResourceExclusiveLite(&Vcb
->fcb_lock
, TRUE
);
1089 free_fileref(Vcb
, fr
);
1090 ExReleaseResourceLite(&Vcb
->fcb_lock
);
1091 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1095 RtlZeroMemory(fr
->fcb
->hash_ptrs_uc
, sizeof(LIST_ENTRY
*) * 256);
1097 ExAcquireResourceExclusiveLite(&fileref
->nonpaged
->children_lock
, TRUE
);
1098 InsertTailList(&fileref
->children
, &fr
->list_entry
);
1099 ExReleaseResourceLite(&fileref
->nonpaged
->children_lock
);
1101 increase_fileref_refcount(fileref
);
1103 if (fr
->fcb
->type
== BTRFS_TYPE_DIRECTORY
)
1104 fr
->fcb
->fileref
= fr
;
1107 mark_fileref_dirty(fr
);
1109 // change fcb->subvol's ROOT_ITEM
1111 fcb
->subvol
->root_item
.ctransid
= Vcb
->superblock
.generation
;
1112 fcb
->subvol
->root_item
.ctime
= now
;
1114 // change fcb's INODE_ITEM
1116 fcb
->inode_item
.transid
= Vcb
->superblock
.generation
;
1117 fcb
->inode_item
.st_size
+= utf8
.Length
* 2;
1118 fcb
->inode_item
.sequence
++;
1120 if (!ccb
->user_set_change_time
)
1121 fcb
->inode_item
.st_ctime
= now
;
1123 if (!ccb
->user_set_write_time
)
1124 fcb
->inode_item
.st_mtime
= now
;
1126 fcb
->inode_item_changed
= TRUE
;
1127 mark_fcb_dirty(fcb
);
1129 fr
->fcb
->subvol
->parent
= fcb
->subvol
->id
;
1131 Status
= STATUS_SUCCESS
;
1134 if (!NT_SUCCESS(Status
)) {
1137 mark_fileref_dirty(fr
);
1138 } else if (rootfcb
) {
1139 rootfcb
->deleted
= TRUE
;
1140 mark_fcb_dirty(rootfcb
);
1144 RemoveEntryList(&r
->list_entry
);
1145 InsertTailList(&Vcb
->drop_roots
, &r
->list_entry
);
1149 ExReleaseResourceLite(&Vcb
->tree_lock
);
1151 if (NT_SUCCESS(Status
)) {
1152 send_notification_fileref(fr
, FILE_NOTIFY_CHANGE_DIR_NAME
, FILE_ACTION_ADDED
, NULL
);
1153 send_notification_fileref(fr
->parent
, FILE_NOTIFY_CHANGE_LAST_WRITE
, FILE_ACTION_MODIFIED
, NULL
);
1158 ExAcquireResourceExclusiveLite(&Vcb
->fcb_lock
, TRUE
);
1159 free_fileref(Vcb
, fr
);
1160 ExReleaseResourceLite(&Vcb
->fcb_lock
);
1166 static NTSTATUS
get_inode_info(PFILE_OBJECT FileObject
, void* data
, ULONG length
) {
1167 btrfs_inode_info
* bii
= data
;
1171 if (length
< sizeof(btrfs_inode_info
))
1172 return STATUS_BUFFER_OVERFLOW
;
1175 return STATUS_INVALID_PARAMETER
;
1177 fcb
= FileObject
->FsContext
;
1180 return STATUS_INVALID_PARAMETER
;
1182 ccb
= FileObject
->FsContext2
;
1185 return STATUS_INVALID_PARAMETER
;
1187 if (!(ccb
->access
& FILE_READ_ATTRIBUTES
)) {
1188 WARN("insufficient privileges\n");
1189 return STATUS_ACCESS_DENIED
;
1193 fcb
= ccb
->fileref
->parent
->fcb
;
1195 ExAcquireResourceSharedLite(fcb
->Header
.Resource
, TRUE
);
1197 bii
->subvol
= fcb
->subvol
->id
;
1198 bii
->inode
= fcb
->inode
;
1199 bii
->top
= fcb
->Vcb
->root_fileref
->fcb
== fcb
? TRUE
: FALSE
;
1200 bii
->type
= fcb
->type
;
1201 bii
->st_uid
= fcb
->inode_item
.st_uid
;
1202 bii
->st_gid
= fcb
->inode_item
.st_gid
;
1203 bii
->st_mode
= fcb
->inode_item
.st_mode
;
1205 if (fcb
->inode_item
.st_rdev
== 0)
1208 bii
->st_rdev
= makedev((fcb
->inode_item
.st_rdev
& 0xFFFFFFFFFFF) >> 20, fcb
->inode_item
.st_rdev
& 0xFFFFF);
1210 bii
->flags
= fcb
->inode_item
.flags
;
1212 bii
->inline_length
= 0;
1213 bii
->disk_size
[0] = 0;
1214 bii
->disk_size
[1] = 0;
1215 bii
->disk_size
[2] = 0;
1217 if (fcb
->type
!= BTRFS_TYPE_DIRECTORY
) {
1220 le
= fcb
->extents
.Flink
;
1221 while (le
!= &fcb
->extents
) {
1222 extent
* ext
= CONTAINING_RECORD(le
, extent
, list_entry
);
1225 if (ext
->extent_data
.type
== EXTENT_TYPE_INLINE
) {
1226 bii
->inline_length
+= ext
->datalen
- (UINT16
)offsetof(EXTENT_DATA
, data
[0]);
1228 EXTENT_DATA2
* ed2
= (EXTENT_DATA2
*)ext
->extent_data
.data
;
1230 // FIXME - compressed extents with a hole in them are counted more than once
1231 if (ed2
->size
!= 0) {
1232 if (ext
->extent_data
.compression
== BTRFS_COMPRESSION_NONE
) {
1233 bii
->disk_size
[0] += ed2
->num_bytes
;
1234 } else if (ext
->extent_data
.compression
== BTRFS_COMPRESSION_ZLIB
) {
1235 bii
->disk_size
[1] += ed2
->size
;
1236 } else if (ext
->extent_data
.compression
== BTRFS_COMPRESSION_LZO
) {
1237 bii
->disk_size
[2] += ed2
->size
;
1247 switch (fcb
->prop_compression
) {
1248 case PropCompression_Zlib
:
1249 bii
->compression_type
= BTRFS_COMPRESSION_ZLIB
;
1252 case PropCompression_LZO
:
1253 bii
->compression_type
= BTRFS_COMPRESSION_LZO
;
1257 bii
->compression_type
= BTRFS_COMPRESSION_ANY
;
1261 ExReleaseResourceLite(fcb
->Header
.Resource
);
1263 return STATUS_SUCCESS
;
1266 static NTSTATUS
set_inode_info(PFILE_OBJECT FileObject
, void* data
, ULONG length
, PIRP Irp
) {
1267 btrfs_set_inode_info
* bsii
= data
;
1272 if (length
< sizeof(btrfs_set_inode_info
))
1273 return STATUS_INVALID_PARAMETER
;
1276 return STATUS_INVALID_PARAMETER
;
1278 fcb
= FileObject
->FsContext
;
1281 return STATUS_INVALID_PARAMETER
;
1283 ccb
= FileObject
->FsContext2
;
1286 return STATUS_INVALID_PARAMETER
;
1288 if (bsii
->flags_changed
&& !(ccb
->access
& FILE_WRITE_ATTRIBUTES
)) {
1289 WARN("insufficient privileges\n");
1290 return STATUS_ACCESS_DENIED
;
1293 if ((bsii
->mode_changed
|| bsii
->uid_changed
|| bsii
->gid_changed
) && !(ccb
->access
& WRITE_DAC
)) {
1294 WARN("insufficient privileges\n");
1295 return STATUS_ACCESS_DENIED
;
1298 if (bsii
->compression_type_changed
&& bsii
->compression_type
> BTRFS_COMPRESSION_LZO
)
1299 return STATUS_INVALID_PARAMETER
;
1302 fcb
= ccb
->fileref
->parent
->fcb
;
1304 if (is_subvol_readonly(fcb
->subvol
, Irp
)) {
1305 WARN("trying to change inode on readonly subvolume\n");
1306 return STATUS_ACCESS_DENIED
;
1309 ExAcquireResourceExclusiveLite(fcb
->Header
.Resource
, TRUE
);
1311 if (bsii
->flags_changed
) {
1312 if (fcb
->type
!= BTRFS_TYPE_DIRECTORY
&& fcb
->inode_item
.st_size
> 0 &&
1313 (bsii
->flags
& BTRFS_INODE_NODATACOW
) != (fcb
->inode_item
.flags
& BTRFS_INODE_NODATACOW
)) {
1314 WARN("trying to change nocow flag on non-empty file\n");
1315 Status
= STATUS_INVALID_PARAMETER
;
1319 fcb
->inode_item
.flags
= bsii
->flags
;
1321 if (fcb
->inode_item
.flags
& BTRFS_INODE_NODATACOW
)
1322 fcb
->inode_item
.flags
|= BTRFS_INODE_NODATASUM
;
1324 fcb
->inode_item
.flags
&= ~(UINT64
)BTRFS_INODE_NODATASUM
;
1327 if (bsii
->mode_changed
) {
1328 UINT32 allowed
= S_IRUSR
| S_IWUSR
| S_IXUSR
| S_IRGRP
| S_IWGRP
| S_IXGRP
| S_IROTH
| S_IWOTH
| S_IXOTH
|
1331 if (ccb
->access
& WRITE_OWNER
)
1334 fcb
->inode_item
.st_mode
&= ~allowed
;
1335 fcb
->inode_item
.st_mode
|= bsii
->st_mode
& allowed
;
1338 if (bsii
->uid_changed
&& fcb
->inode_item
.st_uid
!= bsii
->st_uid
) {
1339 fcb
->inode_item
.st_uid
= bsii
->st_uid
;
1341 fcb
->sd_dirty
= TRUE
;
1342 fcb
->sd_deleted
= FALSE
;
1345 if (bsii
->gid_changed
)
1346 fcb
->inode_item
.st_gid
= bsii
->st_gid
;
1348 if (bsii
->compression_type_changed
) {
1349 switch (bsii
->compression_type
) {
1350 case BTRFS_COMPRESSION_ANY
:
1351 fcb
->prop_compression
= PropCompression_None
;
1354 case BTRFS_COMPRESSION_ZLIB
:
1355 fcb
->prop_compression
= PropCompression_Zlib
;
1358 case BTRFS_COMPRESSION_LZO
:
1359 fcb
->prop_compression
= PropCompression_LZO
;
1363 fcb
->prop_compression_changed
= TRUE
;
1366 if (bsii
->flags_changed
|| bsii
->mode_changed
|| bsii
->uid_changed
|| bsii
->gid_changed
|| bsii
->compression_type_changed
) {
1367 fcb
->inode_item_changed
= TRUE
;
1368 mark_fcb_dirty(fcb
);
1371 Status
= STATUS_SUCCESS
;
1374 ExReleaseResourceLite(fcb
->Header
.Resource
);
1379 static NTSTATUS
get_devices(device_extension
* Vcb
, void* data
, ULONG length
) {
1380 btrfs_device
* dev
= NULL
;
1384 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, TRUE
);
1386 le
= Vcb
->devices
.Flink
;
1387 while (le
!= &Vcb
->devices
) {
1388 device
* dev2
= CONTAINING_RECORD(le
, device
, list_entry
);
1391 if (length
< sizeof(btrfs_device
) - sizeof(WCHAR
)) {
1392 Status
= STATUS_BUFFER_OVERFLOW
;
1399 dev
->next_entry
= sizeof(btrfs_device
) - sizeof(WCHAR
) + dev
->namelen
;
1400 dev
= (btrfs_device
*)((UINT8
*)dev
+ dev
->next_entry
);
1403 structlen
= length
- offsetof(btrfs_device
, namelen
);
1406 Status
= dev_ioctl(dev2
->devobj
, IOCTL_MOUNTDEV_QUERY_DEVICE_NAME
, NULL
, 0, &dev
->namelen
, structlen
, TRUE
, NULL
);
1407 if (!NT_SUCCESS(Status
))
1410 dev
->missing
= FALSE
;
1413 dev
->missing
= TRUE
;
1416 dev
->next_entry
= 0;
1417 dev
->dev_id
= dev2
->devitem
.dev_id
;
1418 dev
->readonly
= (Vcb
->readonly
|| dev2
->readonly
) ? TRUE
: FALSE
;
1419 dev
->device_number
= dev2
->disk_num
;
1420 dev
->partition_number
= dev2
->part_num
;
1421 dev
->size
= dev2
->devitem
.num_bytes
;
1424 GET_LENGTH_INFORMATION gli
;
1426 Status
= dev_ioctl(dev2
->devobj
, IOCTL_DISK_GET_LENGTH_INFO
, NULL
, 0, &gli
, sizeof(gli
), TRUE
, NULL
);
1427 if (!NT_SUCCESS(Status
))
1430 dev
->max_size
= gli
.Length
.QuadPart
;
1432 dev
->max_size
= dev
->size
;
1434 RtlCopyMemory(dev
->stats
, dev2
->stats
, sizeof(UINT64
) * 5);
1436 length
-= sizeof(btrfs_device
) - sizeof(WCHAR
) + dev
->namelen
;
1442 ExReleaseResourceLite(&Vcb
->tree_lock
);
1447 static NTSTATUS
get_usage(device_extension
* Vcb
, void* data
, ULONG length
, PIRP Irp
) {
1448 btrfs_usage
* usage
= (btrfs_usage
*)data
;
1449 btrfs_usage
* lastbue
= NULL
;
1453 if (length
< sizeof(btrfs_usage
))
1454 return STATUS_BUFFER_OVERFLOW
;
1456 if (!Vcb
->chunk_usage_found
) {
1457 ExAcquireResourceExclusiveLite(&Vcb
->tree_lock
, TRUE
);
1459 if (!Vcb
->chunk_usage_found
)
1460 Status
= find_chunk_usage(Vcb
, Irp
);
1462 Status
= STATUS_SUCCESS
;
1464 ExReleaseResourceLite(&Vcb
->tree_lock
);
1466 if (!NT_SUCCESS(Status
)) {
1467 ERR("find_chunk_usage returned %08x\n", Status
);
1472 length
-= offsetof(btrfs_usage
, devices
);
1474 ExAcquireResourceSharedLite(&Vcb
->chunk_lock
, TRUE
);
1476 le
= Vcb
->chunks
.Flink
;
1477 while (le
!= &Vcb
->chunks
) {
1478 BOOL addnew
= FALSE
;
1480 chunk
* c
= CONTAINING_RECORD(le
, chunk
, list_entry
);
1482 if (!lastbue
) // first entry
1485 btrfs_usage
* bue
= usage
;
1490 if (bue
->type
== c
->chunk_item
->type
) {
1495 if (bue
->next_entry
== 0)
1498 bue
= (btrfs_usage
*)((UINT8
*)bue
+ bue
->next_entry
);
1510 if (length
< offsetof(btrfs_usage
, devices
)) {
1511 Status
= STATUS_BUFFER_OVERFLOW
;
1515 length
-= offsetof(btrfs_usage
, devices
);
1517 lastbue
->next_entry
= offsetof(btrfs_usage
, devices
) + (ULONG
)(lastbue
->num_devices
* sizeof(btrfs_usage_device
));
1519 bue
= (btrfs_usage
*)((UINT8
*)lastbue
+ lastbue
->next_entry
);
1522 bue
->next_entry
= 0;
1523 bue
->type
= c
->chunk_item
->type
;
1526 bue
->num_devices
= 0;
1528 if (c
->chunk_item
->type
& BLOCK_FLAG_RAID0
)
1529 factor
= c
->chunk_item
->num_stripes
;
1530 else if (c
->chunk_item
->type
& BLOCK_FLAG_RAID10
)
1531 factor
= c
->chunk_item
->num_stripes
/ c
->chunk_item
->sub_stripes
;
1532 else if (c
->chunk_item
->type
& BLOCK_FLAG_RAID5
)
1533 factor
= c
->chunk_item
->num_stripes
- 1;
1534 else if (c
->chunk_item
->type
& BLOCK_FLAG_RAID6
)
1535 factor
= c
->chunk_item
->num_stripes
- 2;
1540 while (le2
!= &Vcb
->chunks
) {
1541 chunk
* c2
= CONTAINING_RECORD(le2
, chunk
, list_entry
);
1543 if (c2
->chunk_item
->type
== c
->chunk_item
->type
) {
1545 CHUNK_ITEM_STRIPE
* cis
= (CHUNK_ITEM_STRIPE
*)&c2
->chunk_item
[1];
1548 bue
->size
+= c2
->chunk_item
->size
;
1549 bue
->used
+= c2
->used
;
1551 stripesize
= c2
->chunk_item
->size
/ factor
;
1553 for (i
= 0; i
< c2
->chunk_item
->num_stripes
; i
++) {
1557 for (j
= 0; j
< bue
->num_devices
; j
++) {
1558 if (bue
->devices
[j
].dev_id
== cis
[i
].dev_id
) {
1559 bue
->devices
[j
].alloc
+= stripesize
;
1566 if (length
< sizeof(btrfs_usage_device
)) {
1567 Status
= STATUS_BUFFER_OVERFLOW
;
1571 length
-= sizeof(btrfs_usage_device
);
1573 bue
->devices
[bue
->num_devices
].dev_id
= cis
[i
].dev_id
;
1574 bue
->devices
[bue
->num_devices
].alloc
= stripesize
;
1589 Status
= STATUS_SUCCESS
;
1592 ExReleaseResourceLite(&Vcb
->chunk_lock
);
1597 static NTSTATUS
is_volume_mounted(device_extension
* Vcb
, PIRP Irp
) {
1600 IO_STATUS_BLOCK iosb
;
1601 BOOL verify
= FALSE
;
1604 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, TRUE
);
1606 le
= Vcb
->devices
.Flink
;
1607 while (le
!= &Vcb
->devices
) {
1608 device
* dev
= CONTAINING_RECORD(le
, device
, list_entry
);
1610 if (dev
->devobj
&& dev
->removable
) {
1611 Status
= dev_ioctl(dev
->devobj
, IOCTL_STORAGE_CHECK_VERIFY
, NULL
, 0, &cc
, sizeof(ULONG
), FALSE
, &iosb
);
1613 if (iosb
.Information
!= sizeof(ULONG
))
1616 if (Status
== STATUS_VERIFY_REQUIRED
|| (NT_SUCCESS(Status
) && cc
!= dev
->change_count
)) {
1617 dev
->devobj
->Flags
|= DO_VERIFY_VOLUME
;
1621 if (NT_SUCCESS(Status
) && iosb
.Information
== sizeof(ULONG
))
1622 dev
->change_count
= cc
;
1624 if (!NT_SUCCESS(Status
) || verify
) {
1625 IoSetHardErrorOrVerifyDevice(Irp
, dev
->devobj
);
1626 ExReleaseResourceLite(&Vcb
->tree_lock
);
1628 return verify
? STATUS_VERIFY_REQUIRED
: Status
;
1635 ExReleaseResourceLite(&Vcb
->tree_lock
);
1637 return STATUS_SUCCESS
;
1640 static NTSTATUS
fs_get_statistics(void* buffer
, DWORD buflen
, ULONG_PTR
* retlen
) {
1641 FILESYSTEM_STATISTICS
* fss
;
1643 WARN("STUB: FSCTL_FILESYSTEM_GET_STATISTICS\n");
1645 // This is hideously wrong, but at least it stops SMB from breaking
1647 if (buflen
< sizeof(FILESYSTEM_STATISTICS
))
1648 return STATUS_BUFFER_TOO_SMALL
;
1651 RtlZeroMemory(fss
, sizeof(FILESYSTEM_STATISTICS
));
1654 fss
->FileSystemType
= FILESYSTEM_STATISTICS_TYPE_NTFS
;
1655 fss
->SizeOfCompleteStructure
= sizeof(FILESYSTEM_STATISTICS
);
1657 *retlen
= sizeof(FILESYSTEM_STATISTICS
);
1659 return STATUS_SUCCESS
;
1662 static NTSTATUS
set_sparse(device_extension
* Vcb
, PFILE_OBJECT FileObject
, void* data
, ULONG length
, PIRP Irp
) {
1663 FILE_SET_SPARSE_BUFFER
* fssb
= data
;
1667 ccb
* ccb
= FileObject
->FsContext2
;
1668 file_ref
* fileref
= ccb
? ccb
->fileref
: NULL
;
1670 if (data
&& length
< sizeof(FILE_SET_SPARSE_BUFFER
))
1671 return STATUS_INVALID_PARAMETER
;
1674 ERR("FileObject was NULL\n");
1675 return STATUS_INVALID_PARAMETER
;
1678 fcb
= FileObject
->FsContext
;
1681 ERR("FCB was NULL\n");
1682 return STATUS_INVALID_PARAMETER
;
1686 ERR("CCB was NULL\n");
1687 return STATUS_INVALID_PARAMETER
;
1690 if (Irp
->RequestorMode
== UserMode
&& !(ccb
->access
& FILE_WRITE_ATTRIBUTES
)) {
1691 WARN("insufficient privileges\n");
1692 return STATUS_ACCESS_DENIED
;
1696 ERR("no fileref\n");
1697 return STATUS_INVALID_PARAMETER
;
1701 fileref
= fileref
->parent
;
1705 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, TRUE
);
1706 ExAcquireResourceExclusiveLite(fcb
->Header
.Resource
, TRUE
);
1708 if (fcb
->type
!= BTRFS_TYPE_FILE
) {
1709 WARN("FileObject did not point to a file\n");
1710 Status
= STATUS_INVALID_PARAMETER
;
1715 set
= fssb
->SetSparse
;
1720 fcb
->atts
|= FILE_ATTRIBUTE_SPARSE_FILE
;
1721 fcb
->atts_changed
= TRUE
;
1725 fcb
->atts
&= ~FILE_ATTRIBUTE_SPARSE_FILE
;
1726 fcb
->atts_changed
= TRUE
;
1728 defda
= get_file_attributes(Vcb
, fcb
->subvol
, fcb
->inode
, fcb
->type
,
1729 fileref
&& fileref
->dc
&& fileref
->dc
->name
.Length
>= sizeof(WCHAR
) && fileref
->dc
->name
.Buffer
[0] == '.', TRUE
, Irp
);
1731 fcb
->atts_deleted
= defda
== fcb
->atts
;
1734 mark_fcb_dirty(fcb
);
1735 send_notification_fcb(fileref
, FILE_NOTIFY_CHANGE_ATTRIBUTES
, FILE_ACTION_MODIFIED
, NULL
);
1737 Status
= STATUS_SUCCESS
;
1740 ExReleaseResourceLite(fcb
->Header
.Resource
);
1741 ExReleaseResourceLite(&Vcb
->tree_lock
);
1746 static NTSTATUS
zero_data(device_extension
* Vcb
, fcb
* fcb
, UINT64 start
, UINT64 length
, PIRP Irp
, LIST_ENTRY
* rollback
) {
1748 BOOL make_inline
, compress
;
1749 UINT64 start_data
, end_data
;
1753 make_inline
= fcb
->inode_item
.st_size
<= Vcb
->options
.max_inline
|| fcb_is_inline(fcb
);
1756 compress
= write_fcb_compressed(fcb
);
1760 end_data
= fcb
->inode_item
.st_size
;
1761 buf_head
= (ULONG
)offsetof(EXTENT_DATA
, data
[0]);
1762 } else if (compress
) {
1763 start_data
= start
& ~(UINT64
)(COMPRESSED_EXTENT_SIZE
- 1);
1764 end_data
= min(sector_align(start
+ length
, COMPRESSED_EXTENT_SIZE
),
1765 sector_align(fcb
->inode_item
.st_size
, Vcb
->superblock
.sector_size
));
1768 start_data
= start
& ~(UINT64
)(Vcb
->superblock
.sector_size
- 1);
1769 end_data
= sector_align(start
+ length
, Vcb
->superblock
.sector_size
);
1773 data
= ExAllocatePoolWithTag(PagedPool
, (ULONG
)(buf_head
+ end_data
- start_data
), ALLOC_TAG
);
1775 ERR("out of memory\n");
1776 return STATUS_INSUFFICIENT_RESOURCES
;
1779 RtlZeroMemory(data
+ buf_head
, (ULONG
)(end_data
- start_data
));
1781 if (start
> start_data
|| start
+ length
< end_data
) {
1782 Status
= read_file(fcb
, data
+ buf_head
, start_data
, end_data
- start_data
, NULL
, Irp
);
1784 if (!NT_SUCCESS(Status
)) {
1785 ERR("read_file returned %08x\n", Status
);
1791 RtlZeroMemory(data
+ buf_head
+ start
- start_data
, (ULONG
)length
);
1795 EXTENT_DATA
* ed
= (EXTENT_DATA
*)data
;
1797 Status
= excise_extents(Vcb
, fcb
, 0, sector_align(end_data
, Vcb
->superblock
.sector_size
), Irp
, rollback
);
1798 if (!NT_SUCCESS(Status
)) {
1799 ERR("excise_extents returned %08x\n", Status
);
1804 edsize
= (UINT16
)(offsetof(EXTENT_DATA
, data
[0]) + end_data
);
1806 ed
->generation
= Vcb
->superblock
.generation
;
1807 ed
->decoded_size
= end_data
;
1808 ed
->compression
= BTRFS_COMPRESSION_NONE
;
1809 ed
->encryption
= BTRFS_ENCRYPTION_NONE
;
1810 ed
->encoding
= BTRFS_ENCODING_NONE
;
1811 ed
->type
= EXTENT_TYPE_INLINE
;
1813 Status
= add_extent_to_fcb(fcb
, 0, ed
, edsize
, FALSE
, NULL
, rollback
);
1814 if (!NT_SUCCESS(Status
)) {
1815 ERR("add_extent_to_fcb returned %08x\n", Status
);
1822 fcb
->inode_item
.st_blocks
+= end_data
;
1823 } else if (compress
) {
1824 Status
= write_compressed(fcb
, start_data
, end_data
, data
, Irp
, rollback
);
1828 if (!NT_SUCCESS(Status
)) {
1829 ERR("write_compressed returned %08x\n", Status
);
1833 Status
= do_write_file(fcb
, start_data
, end_data
, data
, Irp
, FALSE
, 0, rollback
);
1837 if (!NT_SUCCESS(Status
)) {
1838 ERR("do_write_file returned %08x\n", Status
);
1843 return STATUS_SUCCESS
;
1846 static NTSTATUS
set_zero_data(device_extension
* Vcb
, PFILE_OBJECT FileObject
, void* data
, ULONG length
, PIRP Irp
) {
1847 FILE_ZERO_DATA_INFORMATION
* fzdi
= data
;
1852 LIST_ENTRY rollback
, *le
;
1857 IO_STATUS_BLOCK iosb
;
1859 if (!data
|| length
< sizeof(FILE_ZERO_DATA_INFORMATION
))
1860 return STATUS_INVALID_PARAMETER
;
1863 ERR("FileObject was NULL\n");
1864 return STATUS_INVALID_PARAMETER
;
1867 if (fzdi
->BeyondFinalZero
.QuadPart
<= fzdi
->FileOffset
.QuadPart
) {
1868 WARN("BeyondFinalZero was less than or equal to FileOffset (%llx <= %llx)\n", fzdi
->BeyondFinalZero
.QuadPart
, fzdi
->FileOffset
.QuadPart
);
1869 return STATUS_INVALID_PARAMETER
;
1872 fcb
= FileObject
->FsContext
;
1875 ERR("FCB was NULL\n");
1876 return STATUS_INVALID_PARAMETER
;
1879 ccb
= FileObject
->FsContext2
;
1882 ERR("ccb was NULL\n");
1883 return STATUS_INVALID_PARAMETER
;
1886 if (Irp
->RequestorMode
== UserMode
&& !(ccb
->access
& FILE_WRITE_DATA
)) {
1887 WARN("insufficient privileges\n");
1888 return STATUS_ACCESS_DENIED
;
1891 fileref
= ccb
->fileref
;
1894 ERR("fileref was NULL\n");
1895 return STATUS_INVALID_PARAMETER
;
1898 InitializeListHead(&rollback
);
1900 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, TRUE
);
1901 ExAcquireResourceExclusiveLite(fcb
->Header
.Resource
, TRUE
);
1903 CcFlushCache(&fcb
->nonpaged
->segment_object
, NULL
, 0, &iosb
);
1905 if (fcb
->type
!= BTRFS_TYPE_FILE
) {
1906 WARN("FileObject did not point to a file\n");
1907 Status
= STATUS_INVALID_PARAMETER
;
1912 ERR("FileObject is stream\n");
1913 Status
= STATUS_INVALID_PARAMETER
;
1917 if ((UINT64
)fzdi
->FileOffset
.QuadPart
>= fcb
->inode_item
.st_size
) {
1918 Status
= STATUS_SUCCESS
;
1923 le
= fcb
->extents
.Flink
;
1924 while (le
!= &fcb
->extents
) {
1925 extent
* ext2
= CONTAINING_RECORD(le
, extent
, list_entry
);
1927 if (!ext2
->ignore
) {
1936 Status
= STATUS_SUCCESS
;
1940 if (ext
->extent_data
.type
== EXTENT_TYPE_INLINE
) {
1941 Status
= zero_data(Vcb
, fcb
, fzdi
->FileOffset
.QuadPart
, fzdi
->BeyondFinalZero
.QuadPart
- fzdi
->FileOffset
.QuadPart
, Irp
, &rollback
);
1942 if (!NT_SUCCESS(Status
)) {
1943 ERR("zero_data returned %08x\n", Status
);
1947 start
= sector_align(fzdi
->FileOffset
.QuadPart
, Vcb
->superblock
.sector_size
);
1949 if ((UINT64
)fzdi
->BeyondFinalZero
.QuadPart
> fcb
->inode_item
.st_size
)
1950 end
= sector_align(fcb
->inode_item
.st_size
, Vcb
->superblock
.sector_size
);
1952 end
= (fzdi
->BeyondFinalZero
.QuadPart
/ Vcb
->superblock
.sector_size
) * Vcb
->superblock
.sector_size
;
1955 Status
= zero_data(Vcb
, fcb
, fzdi
->FileOffset
.QuadPart
, fzdi
->BeyondFinalZero
.QuadPart
- fzdi
->FileOffset
.QuadPart
, Irp
, &rollback
);
1956 if (!NT_SUCCESS(Status
)) {
1957 ERR("zero_data returned %08x\n", Status
);
1961 if (start
> (UINT64
)fzdi
->FileOffset
.QuadPart
) {
1962 Status
= zero_data(Vcb
, fcb
, fzdi
->FileOffset
.QuadPart
, start
- fzdi
->FileOffset
.QuadPart
, Irp
, &rollback
);
1963 if (!NT_SUCCESS(Status
)) {
1964 ERR("zero_data returned %08x\n", Status
);
1969 if (end
< (UINT64
)fzdi
->BeyondFinalZero
.QuadPart
) {
1970 Status
= zero_data(Vcb
, fcb
, end
, fzdi
->BeyondFinalZero
.QuadPart
- end
, Irp
, &rollback
);
1971 if (!NT_SUCCESS(Status
)) {
1972 ERR("zero_data returned %08x\n", Status
);
1978 Status
= excise_extents(Vcb
, fcb
, start
, end
, Irp
, &rollback
);
1979 if (!NT_SUCCESS(Status
)) {
1980 ERR("excise_extents returned %08x\n", Status
);
1987 CcPurgeCacheSection(&fcb
->nonpaged
->segment_object
, &fzdi
->FileOffset
, (ULONG
)(fzdi
->BeyondFinalZero
.QuadPart
- fzdi
->FileOffset
.QuadPart
), FALSE
);
1989 KeQuerySystemTime(&time
);
1990 win_time_to_unix(time
, &now
);
1992 fcb
->inode_item
.transid
= Vcb
->superblock
.generation
;
1993 fcb
->inode_item
.sequence
++;
1995 if (!ccb
->user_set_change_time
)
1996 fcb
->inode_item
.st_ctime
= now
;
1998 if (!ccb
->user_set_write_time
)
1999 fcb
->inode_item
.st_mtime
= now
;
2001 fcb
->extents_changed
= TRUE
;
2002 fcb
->inode_item_changed
= TRUE
;
2003 mark_fcb_dirty(fcb
);
2005 send_notification_fcb(fileref
, FILE_NOTIFY_CHANGE_LAST_WRITE
, FILE_ACTION_MODIFIED
, NULL
);
2007 fcb
->subvol
->root_item
.ctransid
= Vcb
->superblock
.generation
;
2008 fcb
->subvol
->root_item
.ctime
= now
;
2010 Status
= STATUS_SUCCESS
;
2013 if (!NT_SUCCESS(Status
))
2014 do_rollback(Vcb
, &rollback
);
2016 clear_rollback(&rollback
);
2018 ExReleaseResourceLite(fcb
->Header
.Resource
);
2019 ExReleaseResourceLite(&Vcb
->tree_lock
);
2024 static NTSTATUS
query_ranges(PFILE_OBJECT FileObject
, FILE_ALLOCATED_RANGE_BUFFER
* inbuf
, ULONG inbuflen
, void* outbuf
, ULONG outbuflen
, ULONG_PTR
* retlen
) {
2028 FILE_ALLOCATED_RANGE_BUFFER
* ranges
= outbuf
;
2030 UINT64 last_start
, last_end
;
2032 TRACE("FSCTL_QUERY_ALLOCATED_RANGES\n");
2035 ERR("FileObject was NULL\n");
2036 return STATUS_INVALID_PARAMETER
;
2039 if (!inbuf
|| inbuflen
< sizeof(FILE_ALLOCATED_RANGE_BUFFER
) || !outbuf
)
2040 return STATUS_INVALID_PARAMETER
;
2042 fcb
= FileObject
->FsContext
;
2045 ERR("FCB was NULL\n");
2046 return STATUS_INVALID_PARAMETER
;
2049 ExAcquireResourceSharedLite(fcb
->Header
.Resource
, TRUE
);
2051 // If file is not marked as sparse, claim the whole thing as an allocated range
2053 if (!(fcb
->atts
& FILE_ATTRIBUTE_SPARSE_FILE
)) {
2054 if (fcb
->inode_item
.st_size
== 0)
2055 Status
= STATUS_SUCCESS
;
2056 else if (outbuflen
< sizeof(FILE_ALLOCATED_RANGE_BUFFER
))
2057 Status
= STATUS_BUFFER_TOO_SMALL
;
2059 ranges
[i
].FileOffset
.QuadPart
= 0;
2060 ranges
[i
].Length
.QuadPart
= fcb
->inode_item
.st_size
;
2062 Status
= STATUS_SUCCESS
;
2069 le
= fcb
->extents
.Flink
;
2074 while (le
!= &fcb
->extents
) {
2075 extent
* ext
= CONTAINING_RECORD(le
, extent
, list_entry
);
2078 EXTENT_DATA2
* ed2
= (ext
->extent_data
.type
== EXTENT_TYPE_REGULAR
|| ext
->extent_data
.type
== EXTENT_TYPE_PREALLOC
) ? (EXTENT_DATA2
*)ext
->extent_data
.data
: NULL
;
2079 UINT64 len
= ed2
? ed2
->num_bytes
: ext
->extent_data
.decoded_size
;
2081 if (ext
->offset
> last_end
) { // first extent after a hole
2082 if (last_end
> last_start
) {
2083 if ((i
+ 1) * sizeof(FILE_ALLOCATED_RANGE_BUFFER
) <= outbuflen
) {
2084 ranges
[i
].FileOffset
.QuadPart
= last_start
;
2085 ranges
[i
].Length
.QuadPart
= min(fcb
->inode_item
.st_size
, last_end
) - last_start
;
2088 Status
= STATUS_BUFFER_TOO_SMALL
;
2093 last_start
= ext
->offset
;
2096 last_end
= ext
->offset
+ len
;
2102 if (last_end
> last_start
) {
2103 if ((i
+ 1) * sizeof(FILE_ALLOCATED_RANGE_BUFFER
) <= outbuflen
) {
2104 ranges
[i
].FileOffset
.QuadPart
= last_start
;
2105 ranges
[i
].Length
.QuadPart
= min(fcb
->inode_item
.st_size
, last_end
) - last_start
;
2108 Status
= STATUS_BUFFER_TOO_SMALL
;
2113 Status
= STATUS_SUCCESS
;
2116 *retlen
= i
* sizeof(FILE_ALLOCATED_RANGE_BUFFER
);
2118 ExReleaseResourceLite(fcb
->Header
.Resource
);
2123 static NTSTATUS
get_object_id(device_extension
* Vcb
, PFILE_OBJECT FileObject
, FILE_OBJECTID_BUFFER
* buf
, ULONG buflen
, ULONG_PTR
* retlen
) {
2126 TRACE("(%p, %p, %p, %x, %p)\n", Vcb
, FileObject
, buf
, buflen
, retlen
);
2129 ERR("FileObject was NULL\n");
2130 return STATUS_INVALID_PARAMETER
;
2133 if (!buf
|| buflen
< sizeof(FILE_OBJECTID_BUFFER
))
2134 return STATUS_INVALID_PARAMETER
;
2136 fcb
= FileObject
->FsContext
;
2139 ERR("FCB was NULL\n");
2140 return STATUS_INVALID_PARAMETER
;
2143 ExAcquireResourceSharedLite(fcb
->Header
.Resource
, TRUE
);
2145 RtlCopyMemory(&buf
->ObjectId
[0], &fcb
->inode
, sizeof(UINT64
));
2146 RtlCopyMemory(&buf
->ObjectId
[sizeof(UINT64
)], &fcb
->subvol
->id
, sizeof(UINT64
));
2148 ExReleaseResourceLite(fcb
->Header
.Resource
);
2150 RtlZeroMemory(&buf
->ExtendedInfo
, sizeof(buf
->ExtendedInfo
));
2152 *retlen
= sizeof(FILE_OBJECTID_BUFFER
);
2154 return STATUS_SUCCESS
;
2157 static void flush_fcb_caches(device_extension
* Vcb
) {
2160 le
= Vcb
->all_fcbs
.Flink
;
2161 while (le
!= &Vcb
->all_fcbs
) {
2162 struct _fcb
* fcb
= CONTAINING_RECORD(le
, struct _fcb
, list_entry_all
);
2163 IO_STATUS_BLOCK iosb
;
2165 if (fcb
->type
!= BTRFS_TYPE_DIRECTORY
&& !fcb
->deleted
)
2166 CcFlushCache(&fcb
->nonpaged
->segment_object
, NULL
, 0, &iosb
);
2172 static NTSTATUS
lock_volume(device_extension
* Vcb
, PIRP Irp
) {
2173 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
2176 BOOL lock_paused_balance
= FALSE
;
2178 TRACE("FSCTL_LOCK_VOLUME\n");
2180 if (Vcb
->scrub
.thread
) {
2181 WARN("cannot lock while scrub running\n");
2182 return STATUS_DEVICE_NOT_READY
;
2185 if (Vcb
->balance
.thread
) {
2186 WARN("cannot lock while balance running\n");
2187 return STATUS_DEVICE_NOT_READY
;
2190 TRACE("locking volume\n");
2192 FsRtlNotifyVolumeEvent(IrpSp
->FileObject
, FSRTL_VOLUME_LOCK
);
2195 return STATUS_SUCCESS
;
2197 ExAcquireResourceExclusiveLite(&Vcb
->fcb_lock
, TRUE
);
2199 if (Vcb
->root_fileref
&& Vcb
->root_fileref
->fcb
&& (Vcb
->root_fileref
->open_count
> 0 || has_open_children(Vcb
->root_fileref
))) {
2200 Status
= STATUS_ACCESS_DENIED
;
2201 ExReleaseResourceLite(&Vcb
->fcb_lock
);
2205 ExReleaseResourceLite(&Vcb
->fcb_lock
);
2207 if (Vcb
->balance
.thread
&& KeReadStateEvent(&Vcb
->balance
.event
)) {
2208 ExAcquireResourceExclusiveLite(&Vcb
->tree_lock
, TRUE
);
2209 KeClearEvent(&Vcb
->balance
.event
);
2210 ExReleaseResourceLite(&Vcb
->tree_lock
);
2212 lock_paused_balance
= TRUE
;
2215 ExAcquireResourceExclusiveLite(&Vcb
->tree_lock
, TRUE
);
2217 flush_fcb_caches(Vcb
);
2219 if (Vcb
->need_write
&& !Vcb
->readonly
)
2220 Status
= do_write(Vcb
, Irp
);
2222 Status
= STATUS_SUCCESS
;
2226 ExReleaseResourceLite(&Vcb
->tree_lock
);
2228 if (!NT_SUCCESS(Status
)) {
2229 ERR("do_write returned %08x\n", Status
);
2233 IoAcquireVpbSpinLock(&irql
);
2235 if (!(Vcb
->Vpb
->Flags
& VPB_LOCKED
)) {
2236 Vcb
->Vpb
->Flags
|= VPB_LOCKED
;
2238 Vcb
->locked_fileobj
= IrpSp
->FileObject
;
2239 Vcb
->lock_paused_balance
= lock_paused_balance
;
2241 Status
= STATUS_ACCESS_DENIED
;
2242 IoReleaseVpbSpinLock(irql
);
2244 if (lock_paused_balance
)
2245 KeSetEvent(&Vcb
->balance
.event
, 0, FALSE
);
2250 IoReleaseVpbSpinLock(irql
);
2252 Status
= STATUS_SUCCESS
;
2255 if (!NT_SUCCESS(Status
))
2256 FsRtlNotifyVolumeEvent(IrpSp
->FileObject
, FSRTL_VOLUME_LOCK_FAILED
);
2261 void do_unlock_volume(device_extension
* Vcb
) {
2264 IoAcquireVpbSpinLock(&irql
);
2266 Vcb
->locked
= FALSE
;
2267 Vcb
->Vpb
->Flags
&= ~VPB_LOCKED
;
2268 Vcb
->locked_fileobj
= NULL
;
2270 IoReleaseVpbSpinLock(irql
);
2272 if (Vcb
->lock_paused_balance
)
2273 KeSetEvent(&Vcb
->balance
.event
, 0, FALSE
);
2276 static NTSTATUS
unlock_volume(device_extension
* Vcb
, PIRP Irp
) {
2277 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
2279 TRACE("FSCTL_UNLOCK_VOLUME\n");
2281 if (!Vcb
->locked
|| IrpSp
->FileObject
!= Vcb
->locked_fileobj
)
2282 return STATUS_NOT_LOCKED
;
2284 TRACE("unlocking volume\n");
2286 do_unlock_volume(Vcb
);
2288 FsRtlNotifyVolumeEvent(IrpSp
->FileObject
, FSRTL_VOLUME_UNLOCK
);
2290 return STATUS_SUCCESS
;
2293 static NTSTATUS
invalidate_volumes(PIRP Irp
) {
2294 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
2295 LUID TcbPrivilege
= {SE_TCB_PRIVILEGE
, 0};
2298 PFILE_OBJECT fileobj
;
2299 PDEVICE_OBJECT devobj
;
2302 TRACE("FSCTL_INVALIDATE_VOLUMES\n");
2304 if (!SeSinglePrivilegeCheck(TcbPrivilege
, Irp
->RequestorMode
))
2305 return STATUS_PRIVILEGE_NOT_HELD
;
2308 if (IoIs32bitProcess(Irp
)) {
2309 if (IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
!= sizeof(UINT32
))
2310 return STATUS_INVALID_PARAMETER
;
2312 h
= (HANDLE
)LongToHandle((*(PUINT32
)Irp
->AssociatedIrp
.SystemBuffer
));
2315 if (IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
!= sizeof(HANDLE
))
2316 return STATUS_INVALID_PARAMETER
;
2318 h
= *(PHANDLE
)Irp
->AssociatedIrp
.SystemBuffer
;
2323 Status
= ObReferenceObjectByHandle(h
, 0, *IoFileObjectType
, Irp
->RequestorMode
, (void**)&fileobj
, NULL
);
2325 if (!NT_SUCCESS(Status
)) {
2326 ERR("ObReferenceObjectByHandle returned %08x\n", Status
);
2330 devobj
= fileobj
->DeviceObject
;
2332 ExAcquireResourceSharedLite(&global_loading_lock
, TRUE
);
2336 while (le
!= &VcbList
) {
2337 device_extension
* Vcb
= CONTAINING_RECORD(le
, device_extension
, list_entry
);
2339 if (Vcb
->Vpb
&& Vcb
->Vpb
->RealDevice
== devobj
) {
2340 if (Vcb
->Vpb
== devobj
->Vpb
) {
2343 BOOL free_newvpb
= FALSE
;
2345 newvpb
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(VPB
), ALLOC_TAG
);
2347 ERR("out of memory\n");
2348 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2352 RtlZeroMemory(newvpb
, sizeof(VPB
));
2354 IoAcquireVpbSpinLock(&irql
);
2355 devobj
->Vpb
->Flags
&= ~VPB_MOUNTED
;
2356 IoReleaseVpbSpinLock(irql
);
2358 ExAcquireResourceExclusiveLite(&Vcb
->tree_lock
, TRUE
);
2360 Vcb
->removing
= TRUE
;
2362 ExReleaseResourceLite(&Vcb
->tree_lock
);
2364 CcWaitForCurrentLazyWriterActivity();
2366 ExAcquireResourceExclusiveLite(&Vcb
->tree_lock
, TRUE
);
2368 flush_fcb_caches(Vcb
);
2370 if (Vcb
->need_write
&& !Vcb
->readonly
)
2371 Status
= do_write(Vcb
, Irp
);
2373 Status
= STATUS_SUCCESS
;
2377 if (!NT_SUCCESS(Status
)) {
2378 ERR("do_write returned %08x\n", Status
);
2379 ExReleaseResourceLite(&Vcb
->tree_lock
);
2383 flush_fcb_caches(Vcb
);
2385 ExReleaseResourceLite(&Vcb
->tree_lock
);
2387 IoAcquireVpbSpinLock(&irql
);
2389 if (devobj
->Vpb
->Flags
& VPB_MOUNTED
) {
2390 newvpb
->Type
= IO_TYPE_VPB
;
2391 newvpb
->Size
= sizeof(VPB
);
2392 newvpb
->RealDevice
= devobj
;
2393 newvpb
->Flags
= devobj
->Vpb
->Flags
& VPB_REMOVE_PENDING
;
2395 devobj
->Vpb
= newvpb
;
2399 IoReleaseVpbSpinLock(irql
);
2404 if (Vcb
->open_files
== 0)
2414 Status
= STATUS_SUCCESS
;
2417 ExReleaseResourceLite(&global_loading_lock
);
2419 ObDereferenceObject(fileobj
);
2424 static NTSTATUS
is_volume_dirty(device_extension
* Vcb
, PIRP Irp
) {
2425 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
2428 if (Irp
->AssociatedIrp
.SystemBuffer
) {
2429 volstate
= Irp
->AssociatedIrp
.SystemBuffer
;
2430 } else if (Irp
->MdlAddress
!= NULL
) {
2431 volstate
= MmGetSystemAddressForMdlSafe(Irp
->MdlAddress
, LowPagePriority
);
2434 return STATUS_INSUFFICIENT_RESOURCES
;
2436 return STATUS_INVALID_USER_BUFFER
;
2438 if (IrpSp
->Parameters
.FileSystemControl
.OutputBufferLength
< sizeof(ULONG
))
2439 return STATUS_INVALID_PARAMETER
;
2443 if (IrpSp
->FileObject
->FsContext
!= Vcb
->volume_fcb
)
2444 return STATUS_INVALID_PARAMETER
;
2446 Irp
->IoStatus
.Information
= sizeof(ULONG
);
2448 return STATUS_SUCCESS
;
2451 static NTSTATUS
get_compression(PIRP Irp
) {
2452 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
2453 USHORT
* compression
;
2455 TRACE("FSCTL_GET_COMPRESSION\n");
2457 if (Irp
->AssociatedIrp
.SystemBuffer
) {
2458 compression
= Irp
->AssociatedIrp
.SystemBuffer
;
2459 } else if (Irp
->MdlAddress
!= NULL
) {
2460 compression
= MmGetSystemAddressForMdlSafe(Irp
->MdlAddress
, LowPagePriority
);
2463 return STATUS_INSUFFICIENT_RESOURCES
;
2465 return STATUS_INVALID_USER_BUFFER
;
2467 if (IrpSp
->Parameters
.FileSystemControl
.OutputBufferLength
< sizeof(USHORT
))
2468 return STATUS_INVALID_PARAMETER
;
2470 *compression
= COMPRESSION_FORMAT_NONE
;
2472 Irp
->IoStatus
.Information
= sizeof(USHORT
);
2474 return STATUS_SUCCESS
;
2477 static NTSTATUS
set_compression(PIRP Irp
) {
2478 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
2479 USHORT
* compression
;
2481 TRACE("FSCTL_SET_COMPRESSION\n");
2483 if (IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
< sizeof(USHORT
))
2484 return STATUS_INVALID_PARAMETER
;
2486 compression
= Irp
->AssociatedIrp
.SystemBuffer
;
2488 if (*compression
!= COMPRESSION_FORMAT_NONE
)
2489 return STATUS_INVALID_PARAMETER
;
2491 return STATUS_SUCCESS
;
2494 static void update_volumes(device_extension
* Vcb
) {
2496 volume_device_extension
* vde
= Vcb
->vde
;
2497 pdo_device_extension
* pdode
= vde
->pdode
;
2499 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, TRUE
);
2501 ExAcquireResourceExclusiveLite(&pdode
->child_lock
, TRUE
);
2503 le
= pdode
->children
.Flink
;
2504 while (le
!= &pdode
->children
) {
2505 volume_child
* vc
= CONTAINING_RECORD(le
, volume_child
, list_entry
);
2507 vc
->generation
= Vcb
->superblock
.generation
- 1;
2512 ExReleaseResourceLite(&pdode
->child_lock
);
2514 ExReleaseResourceLite(&Vcb
->tree_lock
);
2517 static NTSTATUS
dismount_volume(device_extension
* Vcb
, PIRP Irp
) {
2521 TRACE("FSCTL_DISMOUNT_VOLUME\n");
2523 if (!(Vcb
->Vpb
->Flags
& VPB_MOUNTED
))
2524 return STATUS_SUCCESS
;
2526 if (Vcb
->disallow_dismount
) {
2527 WARN("attempting to dismount boot volume or one containing a pagefile\n");
2528 return STATUS_ACCESS_DENIED
;
2531 Status
= FsRtlNotifyVolumeEvent(Vcb
->root_file
, FSRTL_VOLUME_DISMOUNT
);
2532 if (!NT_SUCCESS(Status
)) {
2533 WARN("FsRtlNotifyVolumeEvent returned %08x\n", Status
);
2536 ExAcquireResourceExclusiveLite(&Vcb
->tree_lock
, TRUE
);
2539 flush_fcb_caches(Vcb
);
2541 if (Vcb
->need_write
&& !Vcb
->readonly
) {
2542 Status
= do_write(Vcb
, Irp
);
2544 if (!NT_SUCCESS(Status
))
2545 ERR("do_write returned %08x\n", Status
);
2551 Vcb
->removing
= TRUE
;
2554 update_volumes(Vcb
);
2555 Vcb
->vde
->mounted_device
= NULL
;
2558 ExReleaseResourceLite(&Vcb
->tree_lock
);
2560 IoAcquireVpbSpinLock(&irql
);
2561 Vcb
->Vpb
->Flags
&= ~VPB_MOUNTED
;
2562 Vcb
->Vpb
->Flags
|= VPB_DIRECT_WRITES_ALLOWED
;
2563 IoReleaseVpbSpinLock(irql
);
2565 return STATUS_SUCCESS
;
2568 static NTSTATUS
is_device_part_of_mounted_btrfs_raid(PDEVICE_OBJECT devobj
) {
2573 BTRFS_UUID fsuuid
, devuuid
;
2576 to_read
= devobj
->SectorSize
== 0 ? sizeof(superblock
) : (ULONG
)sector_align(sizeof(superblock
), devobj
->SectorSize
);
2578 sb
= ExAllocatePoolWithTag(PagedPool
, to_read
, ALLOC_TAG
);
2580 ERR("out of memory\n");
2581 return STATUS_INSUFFICIENT_RESOURCES
;
2584 Status
= sync_read_phys(devobj
, superblock_addrs
[0], to_read
, (UINT8
*)sb
, TRUE
);
2585 if (!NT_SUCCESS(Status
)) {
2586 ERR("sync_read_phys returned %08x\n", Status
);
2591 if (sb
->magic
!= BTRFS_MAGIC
) {
2592 TRACE("device is not Btrfs\n");
2594 return STATUS_SUCCESS
;
2597 crc32
= ~calc_crc32c(0xffffffff, (UINT8
*)&sb
->uuid
, (ULONG
)sizeof(superblock
) - sizeof(sb
->checksum
));
2599 if (crc32
!= *((UINT32
*)sb
->checksum
)) {
2600 TRACE("device has Btrfs magic, but invalid superblock checksum\n");
2602 return STATUS_SUCCESS
;
2606 devuuid
= sb
->dev_item
.device_uuid
;
2610 ExAcquireResourceSharedLite(&global_loading_lock
, TRUE
);
2614 while (le
!= &VcbList
) {
2615 device_extension
* Vcb
= CONTAINING_RECORD(le
, device_extension
, list_entry
);
2617 if (RtlCompareMemory(&Vcb
->superblock
.uuid
, &fsuuid
, sizeof(BTRFS_UUID
)) == sizeof(BTRFS_UUID
)) {
2620 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, TRUE
);
2622 if (Vcb
->superblock
.num_devices
> 1) {
2623 le2
= Vcb
->devices
.Flink
;
2624 while (le2
!= &Vcb
->devices
) {
2625 device
* dev
= CONTAINING_RECORD(le2
, device
, list_entry
);
2627 if (RtlCompareMemory(&dev
->devitem
.device_uuid
, &devuuid
, sizeof(BTRFS_UUID
)) == sizeof(BTRFS_UUID
)) {
2628 ExReleaseResourceLite(&Vcb
->tree_lock
);
2629 ExReleaseResourceLite(&global_loading_lock
);
2630 return STATUS_DEVICE_NOT_READY
;
2637 ExReleaseResourceLite(&Vcb
->tree_lock
);
2638 ExReleaseResourceLite(&global_loading_lock
);
2639 return STATUS_SUCCESS
;
2645 ExReleaseResourceLite(&global_loading_lock
);
2647 return STATUS_SUCCESS
;
2650 void trim_whole_device(device
* dev
) {
2651 DEVICE_MANAGE_DATA_SET_ATTRIBUTES dmdsa
;
2654 // FIXME - avoid "bootloader area"??
2656 dmdsa
.Size
= sizeof(DEVICE_MANAGE_DATA_SET_ATTRIBUTES
);
2657 dmdsa
.Action
= DeviceDsmAction_Trim
;
2658 dmdsa
.Flags
= DEVICE_DSM_FLAG_ENTIRE_DATA_SET_RANGE
| DEVICE_DSM_FLAG_TRIM_NOT_FS_ALLOCATED
;
2659 dmdsa
.ParameterBlockOffset
= 0;
2660 dmdsa
.ParameterBlockLength
= 0;
2661 dmdsa
.DataSetRangesOffset
= 0;
2662 dmdsa
.DataSetRangesLength
= 0;
2664 Status
= dev_ioctl(dev
->devobj
, IOCTL_STORAGE_MANAGE_DATA_SET_ATTRIBUTES
, &dmdsa
, sizeof(DEVICE_MANAGE_DATA_SET_ATTRIBUTES
), NULL
, 0, TRUE
, NULL
);
2665 if (!NT_SUCCESS(Status
))
2666 WARN("IOCTL_STORAGE_MANAGE_DATA_SET_ATTRIBUTES returned %08x\n", Status
);
2669 static NTSTATUS
add_device(device_extension
* Vcb
, PIRP Irp
, KPROCESSOR_MODE processor_mode
) {
2670 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
2672 PFILE_OBJECT fileobj
, mountmgrfo
;
2673 PDEVICE_OBJECT DeviceObject
;
2678 UINT64 dev_id
, size
;
2681 UNICODE_STRING mmdevpath
, pnp_name
, pnp_name2
;
2683 PDEVICE_OBJECT mountmgr
;
2686 STORAGE_DEVICE_NUMBER sdn
;
2687 volume_device_extension
* vde
;
2688 pdo_device_extension
* pdode
;
2689 const GUID
* pnp_guid
;
2690 GET_LENGTH_INFORMATION gli
;
2692 pnp_name
.Buffer
= NULL
;
2694 if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE
), processor_mode
))
2695 return STATUS_PRIVILEGE_NOT_HELD
;
2698 WARN("not allowing second device to be added to non-PNP device\n");
2699 return STATUS_NOT_SUPPORTED
;
2702 if (Vcb
->readonly
) // FIXME - handle adding R/W device to seeding device
2703 return STATUS_MEDIA_WRITE_PROTECTED
;
2706 if (IoIs32bitProcess(Irp
)) {
2707 if (IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
!= sizeof(UINT32
))
2708 return STATUS_INVALID_PARAMETER
;
2710 h
= (HANDLE
)LongToHandle((*(PUINT32
)Irp
->AssociatedIrp
.SystemBuffer
));
2713 if (IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
!= sizeof(HANDLE
))
2714 return STATUS_INVALID_PARAMETER
;
2716 h
= *(PHANDLE
)Irp
->AssociatedIrp
.SystemBuffer
;
2721 Status
= ObReferenceObjectByHandle(h
, 0, *IoFileObjectType
, Irp
->RequestorMode
, (void**)&fileobj
, NULL
);
2723 if (!NT_SUCCESS(Status
)) {
2724 ERR("ObReferenceObjectByHandle returned %08x\n", Status
);
2728 DeviceObject
= fileobj
->DeviceObject
;
2730 Status
= get_device_pnp_name(DeviceObject
, &pnp_name
, &pnp_guid
);
2731 if (!NT_SUCCESS(Status
)) {
2732 ERR("get_device_pnp_name returned %08x\n", Status
);
2733 ObDereferenceObject(fileobj
);
2737 // If this is a disk, we have been handed the PDO, so need to go up to find something we can use
2738 if (RtlCompareMemory(pnp_guid
, &GUID_DEVINTERFACE_DISK
, sizeof(GUID
)) == sizeof(GUID
) && DeviceObject
->AttachedDevice
)
2739 DeviceObject
= DeviceObject
->AttachedDevice
;
2741 Status
= dev_ioctl(DeviceObject
, IOCTL_DISK_IS_WRITABLE
, NULL
, 0, NULL
, 0, TRUE
, NULL
);
2742 if (!NT_SUCCESS(Status
)) {
2743 ERR("IOCTL_DISK_IS_WRITABLE returned %08x\n", Status
);
2744 ObDereferenceObject(fileobj
);
2748 Status
= is_device_part_of_mounted_btrfs_raid(DeviceObject
);
2749 if (!NT_SUCCESS(Status
)) {
2750 ERR("is_device_part_of_mounted_btrfs_raid returned %08x\n", Status
);
2751 ObDereferenceObject(fileobj
);
2755 // if disk, check it has no partitions
2756 if (RtlCompareMemory(pnp_guid
, &GUID_DEVINTERFACE_DISK
, sizeof(GUID
)) == sizeof(GUID
)) {
2758 DRIVE_LAYOUT_INFORMATION_EX
* dli
= NULL
;
2768 dli
= ExAllocatePoolWithTag(PagedPool
, dlisize
, ALLOC_TAG
);
2770 ERR("out of memory\n");
2771 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2775 Status
= dev_ioctl(DeviceObject
, IOCTL_DISK_GET_DRIVE_LAYOUT_EX
, NULL
, 0, dli
, dlisize
, TRUE
, NULL
);
2776 } while (Status
== STATUS_BUFFER_TOO_SMALL
);
2778 if (NT_SUCCESS(Status
) && dli
->PartitionCount
> 0) {
2780 ERR("not adding disk which has partitions\n");
2781 Status
= STATUS_DEVICE_NOT_READY
;
2788 Status
= dev_ioctl(DeviceObject
, IOCTL_STORAGE_GET_DEVICE_NUMBER
, NULL
, 0,
2789 &sdn
, sizeof(STORAGE_DEVICE_NUMBER
), TRUE
, NULL
);
2790 if (NT_SUCCESS(Status
)) {
2791 if (sdn
.DeviceType
!= FILE_DEVICE_DISK
) { // FIXME - accept floppies and CDs?
2792 WARN("device was not disk\n");
2793 ObDereferenceObject(fileobj
);
2794 return STATUS_INVALID_PARAMETER
;
2797 sdn
.DeviceNumber
= 0xffffffff;
2798 sdn
.PartitionNumber
= 0xffffffff;
2801 Status
= dev_ioctl(DeviceObject
, IOCTL_DISK_GET_LENGTH_INFO
, NULL
, 0,
2802 &gli
, sizeof(gli
), TRUE
, NULL
);
2803 if (!NT_SUCCESS(Status
)) {
2804 ERR("error reading length information: %08x\n", Status
);
2805 ObDereferenceObject(fileobj
);
2809 size
= gli
.Length
.QuadPart
;
2811 if (size
< 0x100000) {
2812 ERR("device was not large enough to hold FS (%llx bytes, need at least 1 MB)\n", size
);
2813 ObDereferenceObject(fileobj
);
2814 return STATUS_INTERNAL_ERROR
;
2817 volume_removal(drvobj
, &pnp_name
);
2819 ExAcquireResourceExclusiveLite(&Vcb
->tree_lock
, TRUE
);
2821 if (Vcb
->need_write
)
2822 Status
= do_write(Vcb
, Irp
);
2824 Status
= STATUS_SUCCESS
;
2828 if (!NT_SUCCESS(Status
)) {
2829 ERR("do_write returned %08x\n", Status
);
2833 dev
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(device
), ALLOC_TAG
);
2835 ERR("out of memory\n");
2836 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2840 RtlZeroMemory(dev
, sizeof(device
));
2842 dev
->devobj
= DeviceObject
;
2843 dev
->seeding
= FALSE
;
2844 init_device(Vcb
, dev
, TRUE
);
2846 InitializeListHead(&dev
->space
);
2848 if (size
> 0x100000) { // add disk hole - the first MB is marked as used
2849 Status
= add_space_entry(&dev
->space
, NULL
, 0x100000, size
- 0x100000);
2850 if (!NT_SUCCESS(Status
)) {
2851 ERR("add_space_entry returned %08x\n", Status
);
2858 le
= Vcb
->devices
.Flink
;
2859 while (le
!= &Vcb
->devices
) {
2860 device
* dev2
= CONTAINING_RECORD(le
, device
, list_entry
);
2862 if (dev2
->devitem
.dev_id
> dev_id
)
2863 dev_id
= dev2
->devitem
.dev_id
;
2870 dev
->devitem
.dev_id
= dev_id
;
2871 dev
->devitem
.num_bytes
= size
;
2872 dev
->devitem
.bytes_used
= 0;
2873 dev
->devitem
.optimal_io_align
= Vcb
->superblock
.sector_size
;
2874 dev
->devitem
.optimal_io_width
= Vcb
->superblock
.sector_size
;
2875 dev
->devitem
.minimal_io_size
= Vcb
->superblock
.sector_size
;
2876 dev
->devitem
.type
= 0;
2877 dev
->devitem
.generation
= 0;
2878 dev
->devitem
.start_offset
= 0;
2879 dev
->devitem
.dev_group
= 0;
2880 dev
->devitem
.seek_speed
= 0;
2881 dev
->devitem
.bandwidth
= 0;
2882 get_uuid(&dev
->devitem
.device_uuid
);
2883 dev
->devitem
.fs_uuid
= Vcb
->superblock
.uuid
;
2885 di
= ExAllocatePoolWithTag(PagedPool
, sizeof(DEV_ITEM
), ALLOC_TAG
);
2887 ERR("out of memory\n");
2891 RtlCopyMemory(di
, &dev
->devitem
, sizeof(DEV_ITEM
));
2893 Status
= insert_tree_item(Vcb
, Vcb
->chunk_root
, 1, TYPE_DEV_ITEM
, di
->dev_id
, di
, sizeof(DEV_ITEM
), NULL
, Irp
);
2894 if (!NT_SUCCESS(Status
)) {
2895 ERR("insert_tree_item returned %08x\n", Status
);
2900 // add stats entry to dev tree
2901 stats
= ExAllocatePoolWithTag(PagedPool
, sizeof(UINT64
) * 5, ALLOC_TAG
);
2903 ERR("out of memory\n");
2904 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2908 RtlZeroMemory(stats
, sizeof(UINT64
) * 5);
2910 searchkey
.obj_id
= 0;
2911 searchkey
.obj_type
= TYPE_DEV_STATS
;
2912 searchkey
.offset
= di
->dev_id
;
2914 Status
= find_item(Vcb
, Vcb
->dev_root
, &tp
, &searchkey
, FALSE
, Irp
);
2915 if (!NT_SUCCESS(Status
)) {
2916 ERR("error - find_item returned %08x\n", Status
);
2921 if (!keycmp(tp
.item
->key
, searchkey
)) {
2922 Status
= delete_tree_item(Vcb
, &tp
);
2923 if (!NT_SUCCESS(Status
)) {
2924 ERR("delete_tree_item returned %08x\n", Status
);
2930 Status
= insert_tree_item(Vcb
, Vcb
->dev_root
, 0, TYPE_DEV_STATS
, di
->dev_id
, stats
, sizeof(UINT64
) * 5, NULL
, Irp
);
2931 if (!NT_SUCCESS(Status
)) {
2932 ERR("insert_tree_item returned %08x\n", Status
);
2937 if (dev
->trim
&& !dev
->readonly
&& !Vcb
->options
.no_trim
)
2938 trim_whole_device(dev
);
2940 // We clear the first megabyte of the device, so Windows doesn't identify it as another FS
2941 mb
= ExAllocatePoolWithTag(PagedPool
, 0x100000, ALLOC_TAG
);
2943 ERR("out of memory\n");
2944 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2948 RtlZeroMemory(mb
, 0x100000);
2950 Status
= write_data_phys(DeviceObject
, 0, mb
, 0x100000);
2951 if (!NT_SUCCESS(Status
)) {
2952 ERR("write_data_phys returned %08x\n", Status
);
2962 vc
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(volume_child
), ALLOC_TAG
);
2964 ERR("out of memory\n");
2965 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2969 vc
->uuid
= dev
->devitem
.device_uuid
;
2971 vc
->generation
= Vcb
->superblock
.generation
;
2972 vc
->devobj
= DeviceObject
;
2973 vc
->fileobj
= fileobj
;
2974 vc
->notification_entry
= NULL
;
2976 Status
= IoRegisterPlugPlayNotification(EventCategoryTargetDeviceChange
, 0, fileobj
,
2977 drvobj
, pnp_removal
, vde
->pdode
, &vc
->notification_entry
);
2978 if (!NT_SUCCESS(Status
))
2979 WARN("IoRegisterPlugPlayNotification returned %08x\n", Status
);
2981 pnp_name2
= pnp_name
;
2983 if (pnp_name
.Length
> 4 * sizeof(WCHAR
) && pnp_name
.Buffer
[0] == '\\' && (pnp_name
.Buffer
[1] == '\\' || pnp_name
.Buffer
[1] == '?') &&
2984 pnp_name
.Buffer
[2] == '?' && pnp_name
.Buffer
[3] == '\\') {
2985 pnp_name2
.Buffer
= &pnp_name2
.Buffer
[3];
2986 pnp_name2
.Length
-= 3 * sizeof(WCHAR
);
2987 pnp_name2
.MaximumLength
-= 3 * sizeof(WCHAR
);
2990 vc
->pnp_name
.Length
= vc
->pnp_name
.MaximumLength
= pnp_name2
.Length
;
2992 if (pnp_name2
.Length
== 0)
2993 vc
->pnp_name
.Buffer
= NULL
;
2995 vc
->pnp_name
.Buffer
= ExAllocatePoolWithTag(PagedPool
, pnp_name2
.Length
, ALLOC_TAG
);
2996 if (!vc
->pnp_name
.Buffer
) {
2997 ERR("out of memory\n");
2998 Status
= STATUS_INSUFFICIENT_RESOURCES
;
3002 RtlCopyMemory(vc
->pnp_name
.Buffer
, pnp_name2
.Buffer
, pnp_name2
.Length
);
3006 vc
->seeding
= FALSE
;
3007 vc
->disk_num
= sdn
.DeviceNumber
;
3008 vc
->part_num
= sdn
.PartitionNumber
;
3009 vc
->had_drive_letter
= FALSE
;
3011 ExAcquireResourceExclusiveLite(&pdode
->child_lock
, TRUE
);
3012 InsertTailList(&pdode
->children
, &vc
->list_entry
);
3013 pdode
->num_children
++;
3014 pdode
->children_loaded
++;
3015 ExReleaseResourceLite(&pdode
->child_lock
);
3017 RtlInitUnicodeString(&mmdevpath
, MOUNTMGR_DEVICE_NAME
);
3018 Status
= IoGetDeviceObjectPointer(&mmdevpath
, FILE_READ_ATTRIBUTES
, &mountmgrfo
, &mountmgr
);
3019 if (!NT_SUCCESS(Status
))
3020 ERR("IoGetDeviceObjectPointer returned %08x\n", Status
);
3022 Status
= remove_drive_letter(mountmgr
, &pnp_name
);
3023 if (!NT_SUCCESS(Status
) && Status
!= STATUS_NOT_FOUND
)
3024 WARN("remove_drive_letter returned %08x\n", Status
);
3026 vc
->had_drive_letter
= NT_SUCCESS(Status
);
3028 ObDereferenceObject(mountmgrfo
);
3031 Vcb
->superblock
.num_devices
++;
3032 Vcb
->superblock
.total_bytes
+= size
;
3033 Vcb
->devices_loaded
++;
3034 InsertTailList(&Vcb
->devices
, &dev
->list_entry
);
3036 // FIXME - send notification that volume size has increased
3038 ObReferenceObject(DeviceObject
); // for Vcb
3040 Status
= do_write(Vcb
, Irp
);
3041 if (!NT_SUCCESS(Status
))
3042 ERR("do_write returned %08x\n", Status
);
3044 ObReferenceObject(fileobj
);
3049 ExReleaseResourceLite(&Vcb
->tree_lock
);
3052 ObDereferenceObject(fileobj
);
3054 if (pnp_name
.Buffer
)
3055 ExFreePool(pnp_name
.Buffer
);
3057 if (NT_SUCCESS(Status
))
3058 FsRtlNotifyVolumeEvent(Vcb
->root_file
, FSRTL_VOLUME_CHANGE_SIZE
);
3063 static NTSTATUS
allow_extended_dasd_io(device_extension
* Vcb
, PFILE_OBJECT FileObject
) {
3067 TRACE("FSCTL_ALLOW_EXTENDED_DASD_IO\n");
3070 return STATUS_INVALID_PARAMETER
;
3072 fcb
= FileObject
->FsContext
;
3073 ccb
= FileObject
->FsContext2
;
3076 return STATUS_INVALID_PARAMETER
;
3078 if (fcb
!= Vcb
->volume_fcb
)
3079 return STATUS_INVALID_PARAMETER
;
3082 return STATUS_INVALID_PARAMETER
;
3084 ccb
->allow_extended_dasd_io
= TRUE
;
3086 return STATUS_SUCCESS
;
3089 static NTSTATUS
query_uuid(device_extension
* Vcb
, void* data
, ULONG length
) {
3090 if (length
< sizeof(BTRFS_UUID
))
3091 return STATUS_BUFFER_OVERFLOW
;
3093 RtlCopyMemory(data
, &Vcb
->superblock
.uuid
, sizeof(BTRFS_UUID
));
3095 return STATUS_SUCCESS
;
3098 static NTSTATUS
reset_stats(device_extension
* Vcb
, void* data
, ULONG length
, KPROCESSOR_MODE processor_mode
) {
3103 if (length
< sizeof(UINT64
))
3104 return STATUS_INVALID_PARAMETER
;
3107 return STATUS_MEDIA_WRITE_PROTECTED
;
3109 if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE
), processor_mode
))
3110 return STATUS_PRIVILEGE_NOT_HELD
;
3112 devid
= *((UINT64
*)data
);
3114 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, TRUE
);
3116 le
= Vcb
->devices
.Flink
;
3118 while (le
!= &Vcb
->devices
) {
3119 device
* dev
= CONTAINING_RECORD(le
, device
, list_entry
);
3121 if (dev
->devitem
.dev_id
== devid
) {
3122 RtlZeroMemory(dev
->stats
, sizeof(UINT64
) * 5);
3123 dev
->stats_changed
= TRUE
;
3124 Vcb
->stats_changed
= TRUE
;
3125 Vcb
->need_write
= TRUE
;
3126 Status
= STATUS_SUCCESS
;
3133 WARN("device %llx not found\n", devid
);
3134 Status
= STATUS_INVALID_PARAMETER
;
3137 ExReleaseResourceLite(&Vcb
->tree_lock
);
3142 static NTSTATUS
get_integrity_information(device_extension
* Vcb
, PFILE_OBJECT FileObject
, void* data
, ULONG datalen
) {
3143 FSCTL_GET_INTEGRITY_INFORMATION_BUFFER
* fgiib
= (FSCTL_GET_INTEGRITY_INFORMATION_BUFFER
*)data
;
3145 TRACE("FSCTL_GET_INTEGRITY_INFORMATION\n");
3150 return STATUS_INVALID_PARAMETER
;
3152 if (!data
|| datalen
< sizeof(FSCTL_GET_INTEGRITY_INFORMATION_BUFFER
))
3153 return STATUS_INVALID_PARAMETER
;
3155 fgiib
->ChecksumAlgorithm
= 0;
3156 fgiib
->Reserved
= 0;
3158 fgiib
->ChecksumChunkSizeInBytes
= Vcb
->superblock
.sector_size
;
3159 fgiib
->ClusterSizeInBytes
= Vcb
->superblock
.sector_size
;
3161 return STATUS_SUCCESS
;
3164 static NTSTATUS
set_integrity_information(PFILE_OBJECT FileObject
, void* data
, ULONG datalen
) {
3165 TRACE("FSCTL_SET_INTEGRITY_INFORMATION\n");
3170 return STATUS_INVALID_PARAMETER
;
3172 if (!data
|| datalen
< sizeof(FSCTL_SET_INTEGRITY_INFORMATION_BUFFER
))
3173 return STATUS_INVALID_PARAMETER
;
3175 return STATUS_SUCCESS
;
3178 BOOL
fcb_is_inline(fcb
* fcb
) {
3181 le
= fcb
->extents
.Flink
;
3182 while (le
!= &fcb
->extents
) {
3183 extent
* ext
= CONTAINING_RECORD(le
, extent
, list_entry
);
3186 return ext
->extent_data
.type
== EXTENT_TYPE_INLINE
;
3194 static NTSTATUS
duplicate_extents(device_extension
* Vcb
, PFILE_OBJECT FileObject
, void* data
, ULONG datalen
, PIRP Irp
) {
3195 DUPLICATE_EXTENTS_DATA
* ded
= (DUPLICATE_EXTENTS_DATA
*)data
;
3196 fcb
*fcb
= FileObject
? FileObject
->FsContext
: NULL
, *sourcefcb
;
3197 ccb
*ccb
= FileObject
? FileObject
->FsContext2
: NULL
, *sourceccb
;
3199 PFILE_OBJECT sourcefo
;
3200 UINT64 sourcelen
, nbytes
= 0;
3201 LIST_ENTRY rollback
, *le
, newexts
;
3206 if (!ded
|| datalen
< sizeof(DUPLICATE_EXTENTS_DATA
))
3207 return STATUS_BUFFER_TOO_SMALL
;
3210 return STATUS_MEDIA_WRITE_PROTECTED
;
3212 if (ded
->ByteCount
.QuadPart
== 0)
3213 return STATUS_SUCCESS
;
3215 if (!fcb
|| !ccb
|| fcb
== Vcb
->volume_fcb
)
3216 return STATUS_INVALID_PARAMETER
;
3218 if (is_subvol_readonly(fcb
->subvol
, Irp
))
3219 return STATUS_ACCESS_DENIED
;
3221 if (Irp
->RequestorMode
== UserMode
&& !(ccb
->access
& FILE_WRITE_DATA
)) {
3222 WARN("insufficient privileges\n");
3223 return STATUS_ACCESS_DENIED
;
3226 if (!fcb
->ads
&& fcb
->type
!= BTRFS_TYPE_FILE
&& fcb
->type
!= BTRFS_TYPE_SYMLINK
)
3227 return STATUS_INVALID_PARAMETER
;
3229 Status
= ObReferenceObjectByHandle(ded
->FileHandle
, 0, *IoFileObjectType
, Irp
->RequestorMode
, (void**)&sourcefo
, NULL
);
3230 if (!NT_SUCCESS(Status
)) {
3231 ERR("ObReferenceObjectByHandle returned %08x\n", Status
);
3235 if (sourcefo
->DeviceObject
!= FileObject
->DeviceObject
) {
3236 WARN("source and destination are on different volumes\n");
3237 ObDereferenceObject(sourcefo
);
3238 return STATUS_INVALID_PARAMETER
;
3241 sourcefcb
= sourcefo
->FsContext
;
3242 sourceccb
= sourcefo
->FsContext2
;
3244 if (!sourcefcb
|| !sourceccb
|| sourcefcb
== Vcb
->volume_fcb
) {
3245 ObDereferenceObject(sourcefo
);
3246 return STATUS_INVALID_PARAMETER
;
3249 if (!sourcefcb
->ads
&& !fcb
->ads
) {
3250 if ((ded
->SourceFileOffset
.QuadPart
& (Vcb
->superblock
.sector_size
- 1)) || (ded
->TargetFileOffset
.QuadPart
& (Vcb
->superblock
.sector_size
- 1))) {
3251 ObDereferenceObject(sourcefo
);
3252 return STATUS_INVALID_PARAMETER
;
3255 if (ded
->ByteCount
.QuadPart
& (Vcb
->superblock
.sector_size
- 1)) {
3256 ObDereferenceObject(sourcefo
);
3257 return STATUS_INVALID_PARAMETER
;
3261 if (Irp
->RequestorMode
== UserMode
&& (!(sourceccb
->access
& FILE_READ_DATA
) || !(sourceccb
->access
& FILE_READ_ATTRIBUTES
))) {
3262 WARN("insufficient privileges\n");
3263 ObDereferenceObject(sourcefo
);
3264 return STATUS_ACCESS_DENIED
;
3267 if (!sourcefcb
->ads
&& sourcefcb
->type
!= BTRFS_TYPE_FILE
&& sourcefcb
->type
!= BTRFS_TYPE_SYMLINK
) {
3268 ObDereferenceObject(sourcefo
);
3269 return STATUS_INVALID_PARAMETER
;
3272 sourcelen
= sourcefcb
->ads
? sourcefcb
->adsdata
.Length
: sourcefcb
->inode_item
.st_size
;
3274 if (sector_align(sourcelen
, Vcb
->superblock
.sector_size
) < (UINT64
)ded
->SourceFileOffset
.QuadPart
+ (UINT64
)ded
->ByteCount
.QuadPart
) {
3275 ObDereferenceObject(sourcefo
);
3276 return STATUS_NOT_SUPPORTED
;
3279 if (fcb
== sourcefcb
&&
3280 ((ded
->SourceFileOffset
.QuadPart
>= ded
->TargetFileOffset
.QuadPart
&& ded
->SourceFileOffset
.QuadPart
< ded
->TargetFileOffset
.QuadPart
+ ded
->ByteCount
.QuadPart
) ||
3281 (ded
->TargetFileOffset
.QuadPart
>= ded
->SourceFileOffset
.QuadPart
&& ded
->TargetFileOffset
.QuadPart
< ded
->SourceFileOffset
.QuadPart
+ ded
->ByteCount
.QuadPart
))) {
3282 WARN("source and destination are the same, and the ranges overlap\n");
3283 ObDereferenceObject(sourcefo
);
3284 return STATUS_INVALID_PARAMETER
;
3287 // fail if nocsum flag set on one file but not the other
3288 if (!fcb
->ads
&& !sourcefcb
->ads
&& (fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
) != (sourcefcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
)) {
3289 ObDereferenceObject(sourcefo
);
3290 return STATUS_INVALID_PARAMETER
;
3293 InitializeListHead(&rollback
);
3294 InitializeListHead(&newexts
);
3296 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, TRUE
);
3298 ExAcquireResourceExclusiveLite(fcb
->Header
.Resource
, TRUE
);
3300 if (fcb
!= sourcefcb
)
3301 ExAcquireResourceSharedLite(sourcefcb
->Header
.Resource
, TRUE
);
3303 if (!FsRtlFastCheckLockForWrite(&fcb
->lock
, &ded
->TargetFileOffset
, &ded
->ByteCount
, 0, FileObject
, PsGetCurrentProcess())) {
3304 Status
= STATUS_FILE_LOCK_CONFLICT
;
3308 if (!FsRtlFastCheckLockForRead(&sourcefcb
->lock
, &ded
->SourceFileOffset
, &ded
->ByteCount
, 0, FileObject
, PsGetCurrentProcess())) {
3309 Status
= STATUS_FILE_LOCK_CONFLICT
;
3313 make_inline
= fcb
->ads
? FALSE
: (fcb
->inode_item
.st_size
<= Vcb
->options
.max_inline
|| fcb_is_inline(fcb
));
3315 if (fcb
->ads
|| sourcefcb
->ads
|| make_inline
|| fcb_is_inline(sourcefcb
)) {
3317 ULONG bytes_read
, dataoff
, datalen2
;
3320 dataoff
= (ULONG
)ded
->TargetFileOffset
.QuadPart
;
3321 datalen2
= (ULONG
)fcb
->inode_item
.st_size
;
3322 } else if (fcb
->ads
) {
3324 datalen2
= (ULONG
)ded
->ByteCount
.QuadPart
;
3326 dataoff
= ded
->TargetFileOffset
.QuadPart
% Vcb
->superblock
.sector_size
;
3327 datalen2
= (ULONG
)sector_align(ded
->ByteCount
.QuadPart
+ dataoff
, Vcb
->superblock
.sector_size
);
3330 data2
= ExAllocatePoolWithTag(PagedPool
, datalen2
, ALLOC_TAG
);
3332 ERR("out of memory\n");
3333 Status
= STATUS_INSUFFICIENT_RESOURCES
;
3339 Status
= read_file(fcb
, data2
, 0, datalen2
, NULL
, Irp
);
3341 Status
= read_file(fcb
, data2
, ded
->TargetFileOffset
.QuadPart
- dataoff
, dataoff
, NULL
, Irp
);
3343 if (!NT_SUCCESS(Status
)) {
3344 ERR("read_file returned %08x\n", Status
);
3350 if (sourcefcb
->ads
) {
3351 Status
= read_stream(sourcefcb
, data2
+ dataoff
, ded
->SourceFileOffset
.QuadPart
, (ULONG
)ded
->ByteCount
.QuadPart
, &bytes_read
);
3352 if (!NT_SUCCESS(Status
)) {
3353 ERR("read_stream returned %08x\n", Status
);
3358 Status
= read_file(sourcefcb
, data2
+ dataoff
, ded
->SourceFileOffset
.QuadPart
, ded
->ByteCount
.QuadPart
, &bytes_read
, Irp
);
3359 if (!NT_SUCCESS(Status
)) {
3360 ERR("read_file returned %08x\n", Status
);
3366 if (dataoff
+ bytes_read
< datalen2
)
3367 RtlZeroMemory(data2
+ dataoff
+ bytes_read
, datalen2
- bytes_read
);
3370 RtlCopyMemory(&fcb
->adsdata
.Buffer
[ded
->TargetFileOffset
.QuadPart
], data2
, (USHORT
)min(ded
->ByteCount
.QuadPart
, fcb
->adsdata
.Length
- ded
->TargetFileOffset
.QuadPart
));
3371 else if (make_inline
) {
3375 Status
= excise_extents(Vcb
, fcb
, 0, sector_align(fcb
->inode_item
.st_size
, Vcb
->superblock
.sector_size
), Irp
, &rollback
);
3376 if (!NT_SUCCESS(Status
)) {
3377 ERR("excise_extents returned %08x\n", Status
);
3382 edsize
= (UINT16
)(offsetof(EXTENT_DATA
, data
[0]) + datalen2
);
3384 ed
= ExAllocatePoolWithTag(PagedPool
, edsize
, ALLOC_TAG
);
3386 ERR("out of memory\n");
3388 Status
= STATUS_INSUFFICIENT_RESOURCES
;
3392 ed
->generation
= Vcb
->superblock
.generation
;
3393 ed
->decoded_size
= fcb
->inode_item
.st_size
;
3394 ed
->compression
= BTRFS_COMPRESSION_NONE
;
3395 ed
->encryption
= BTRFS_ENCRYPTION_NONE
;
3396 ed
->encoding
= BTRFS_ENCODING_NONE
;
3397 ed
->type
= EXTENT_TYPE_INLINE
;
3399 RtlCopyMemory(ed
->data
, data2
, datalen2
);
3401 Status
= add_extent_to_fcb(fcb
, 0, ed
, edsize
, FALSE
, NULL
, &rollback
);
3402 if (!NT_SUCCESS(Status
)) {
3403 ERR("add_extent_to_fcb returned %08x\n", Status
);
3408 fcb
->inode_item
.st_blocks
+= datalen2
;
3410 UINT64 start
= ded
->TargetFileOffset
.QuadPart
- (ded
->TargetFileOffset
.QuadPart
% Vcb
->superblock
.sector_size
);
3412 Status
= do_write_file(fcb
, start
, start
+ datalen2
, data2
, Irp
, FALSE
, 0, &rollback
);
3413 if (!NT_SUCCESS(Status
)) {
3414 ERR("do_write_file returned %08x\n", Status
);
3422 LIST_ENTRY
* lastextle
;
3424 le
= sourcefcb
->extents
.Flink
;
3425 while (le
!= &sourcefcb
->extents
) {
3426 extent
* ext
= CONTAINING_RECORD(le
, extent
, list_entry
);
3429 if (ext
->offset
>= (UINT64
)ded
->SourceFileOffset
.QuadPart
+ (UINT64
)ded
->ByteCount
.QuadPart
)
3432 if (ext
->extent_data
.type
!= EXTENT_TYPE_INLINE
) {
3433 ULONG extlen
= offsetof(extent
, extent_data
) + sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
);
3435 EXTENT_DATA2
*ed2s
, *ed2d
;
3438 ed2s
= (EXTENT_DATA2
*)ext
->extent_data
.data
;
3440 if (ext
->offset
+ ed2s
->num_bytes
<= (UINT64
)ded
->SourceFileOffset
.QuadPart
) {
3445 ext2
= ExAllocatePoolWithTag(PagedPool
, extlen
, ALLOC_TAG
);
3447 ERR("out of memory\n");
3448 Status
= STATUS_INSUFFICIENT_RESOURCES
;
3452 if (ext
->offset
< (UINT64
)ded
->SourceFileOffset
.QuadPart
)
3453 ext2
->offset
= ded
->TargetFileOffset
.QuadPart
;
3455 ext2
->offset
= ext
->offset
- ded
->SourceFileOffset
.QuadPart
+ ded
->TargetFileOffset
.QuadPart
;
3457 ext2
->datalen
= sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
);
3458 ext2
->unique
= FALSE
;
3459 ext2
->ignore
= FALSE
;
3460 ext2
->inserted
= TRUE
;
3462 ext2
->extent_data
.generation
= Vcb
->superblock
.generation
;
3463 ext2
->extent_data
.decoded_size
= ext
->extent_data
.decoded_size
;
3464 ext2
->extent_data
.compression
= ext
->extent_data
.compression
;
3465 ext2
->extent_data
.encryption
= ext
->extent_data
.encryption
;
3466 ext2
->extent_data
.encoding
= ext
->extent_data
.encoding
;
3467 ext2
->extent_data
.type
= ext
->extent_data
.type
;
3469 ed2d
= (EXTENT_DATA2
*)ext2
->extent_data
.data
;
3471 ed2d
->address
= ed2s
->address
;
3472 ed2d
->size
= ed2s
->size
;
3474 if (ext
->offset
< (UINT64
)ded
->SourceFileOffset
.QuadPart
) {
3475 ed2d
->offset
= ed2s
->offset
+ ded
->SourceFileOffset
.QuadPart
- ext
->offset
;
3476 ed2d
->num_bytes
= min((UINT64
)ded
->ByteCount
.QuadPart
, ed2s
->num_bytes
+ ext
->offset
- ded
->SourceFileOffset
.QuadPart
);
3478 ed2d
->offset
= ed2s
->offset
;
3479 ed2d
->num_bytes
= min(ded
->SourceFileOffset
.QuadPart
+ ded
->ByteCount
.QuadPart
- ext
->offset
, ed2s
->num_bytes
);
3483 if (ext
->extent_data
.compression
== BTRFS_COMPRESSION_NONE
) {
3484 ext2
->csum
= ExAllocatePoolWithTag(PagedPool
, (ULONG
)(ed2d
->num_bytes
* sizeof(UINT32
) / Vcb
->superblock
.sector_size
), ALLOC_TAG
);
3486 ERR("out of memory\n");
3487 Status
= STATUS_INSUFFICIENT_RESOURCES
;
3492 RtlCopyMemory(ext2
->csum
, &ext
->csum
[(ed2d
->offset
- ed2s
->offset
) / Vcb
->superblock
.sector_size
],
3493 (ULONG
)(ed2d
->num_bytes
* sizeof(UINT32
) / Vcb
->superblock
.sector_size
));
3495 ext2
->csum
= ExAllocatePoolWithTag(PagedPool
, (ULONG
)(ed2d
->size
* sizeof(UINT32
) / Vcb
->superblock
.sector_size
), ALLOC_TAG
);
3497 ERR("out of memory\n");
3498 Status
= STATUS_INSUFFICIENT_RESOURCES
;
3503 RtlCopyMemory(ext2
->csum
, ext
->csum
, (ULONG
)(ed2s
->size
* sizeof(UINT32
) / Vcb
->superblock
.sector_size
));
3508 InsertTailList(&newexts
, &ext2
->list_entry
);
3510 c
= get_chunk_from_address(Vcb
, ed2s
->address
);
3512 ERR("get_chunk_from_address(%llx) failed\n", ed2s
->address
);
3513 Status
= STATUS_INTERNAL_ERROR
;
3517 Status
= update_changed_extent_ref(Vcb
, c
, ed2s
->address
, ed2s
->size
, fcb
->subvol
->id
, fcb
->inode
, ext2
->offset
- ed2d
->offset
,
3518 1, fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
, FALSE
, Irp
);
3519 if (!NT_SUCCESS(Status
)) {
3520 ERR("update_changed_extent_ref returned %08x\n", Status
);
3524 nbytes
+= ed2d
->num_bytes
;
3531 Status
= excise_extents(Vcb
, fcb
, ded
->TargetFileOffset
.QuadPart
, ded
->TargetFileOffset
.QuadPart
+ ded
->ByteCount
.QuadPart
, Irp
, &rollback
);
3532 if (!NT_SUCCESS(Status
)) {
3533 ERR("excise_extents returned %08x\n", Status
);
3535 while (!IsListEmpty(&newexts
)) {
3536 extent
* ext
= CONTAINING_RECORD(RemoveHeadList(&newexts
), extent
, list_entry
);
3543 // clear unique flags in source fcb
3544 le
= sourcefcb
->extents
.Flink
;
3545 while (le
!= &sourcefcb
->extents
) {
3546 extent
* ext
= CONTAINING_RECORD(le
, extent
, list_entry
);
3548 if (!ext
->ignore
&& ext
->unique
&& (ext
->extent_data
.type
== EXTENT_TYPE_REGULAR
|| ext
->extent_data
.type
== EXTENT_TYPE_PREALLOC
)) {
3549 EXTENT_DATA2
* ed2s
= (EXTENT_DATA2
*)ext
->extent_data
.data
;
3552 le2
= newexts
.Flink
;
3553 while (le2
!= &newexts
) {
3554 extent
* ext2
= CONTAINING_RECORD(le2
, extent
, list_entry
);
3556 if (ext2
->extent_data
.type
== EXTENT_TYPE_REGULAR
|| ext2
->extent_data
.type
== EXTENT_TYPE_PREALLOC
) {
3557 EXTENT_DATA2
* ed2d
= (EXTENT_DATA2
*)ext2
->extent_data
.data
;
3559 if (ed2d
->address
== ed2s
->address
&& ed2d
->size
== ed2s
->size
) {
3560 ext
->unique
= FALSE
;
3572 lastextle
= &fcb
->extents
;
3573 while (!IsListEmpty(&newexts
)) {
3574 extent
* ext
= CONTAINING_RECORD(RemoveHeadList(&newexts
), extent
, list_entry
);
3576 add_extent(fcb
, lastextle
, ext
);
3577 lastextle
= &ext
->list_entry
;
3581 KeQuerySystemTime(&time
);
3582 win_time_to_unix(time
, &now
);
3585 ccb
->fileref
->parent
->fcb
->inode_item
.sequence
++;
3587 if (!ccb
->user_set_change_time
)
3588 ccb
->fileref
->parent
->fcb
->inode_item
.st_ctime
= now
;
3590 ccb
->fileref
->parent
->fcb
->inode_item_changed
= TRUE
;
3591 mark_fcb_dirty(ccb
->fileref
->parent
->fcb
);
3593 fcb
->inode_item
.st_blocks
+= nbytes
;
3594 fcb
->inode_item
.sequence
++;
3596 if (!ccb
->user_set_change_time
)
3597 fcb
->inode_item
.st_ctime
= now
;
3599 if (!ccb
->user_set_write_time
) {
3600 fcb
->inode_item
.st_mtime
= now
;
3601 send_notification_fcb(ccb
->fileref
, FILE_NOTIFY_CHANGE_LAST_WRITE
, FILE_ACTION_MODIFIED
, NULL
);
3604 fcb
->inode_item_changed
= TRUE
;
3605 fcb
->extents_changed
= TRUE
;
3608 mark_fcb_dirty(fcb
);
3610 if (fcb
->nonpaged
->segment_object
.DataSectionObject
)
3611 CcPurgeCacheSection(&fcb
->nonpaged
->segment_object
, &ded
->TargetFileOffset
, (ULONG
)ded
->ByteCount
.QuadPart
, FALSE
);
3613 Status
= STATUS_SUCCESS
;
3616 ObDereferenceObject(sourcefo
);
3618 if (NT_SUCCESS(Status
))
3619 clear_rollback(&rollback
);
3621 do_rollback(Vcb
, &rollback
);
3623 if (fcb
!= sourcefcb
)
3624 ExReleaseResourceLite(sourcefcb
->Header
.Resource
);
3626 ExReleaseResourceLite(fcb
->Header
.Resource
);
3628 ExReleaseResourceLite(&Vcb
->tree_lock
);
3633 // based on functions in sys/sysmacros.h
3634 #define major(rdev) ((((rdev) >> 8) & 0xFFF) | ((UINT32)((rdev) >> 32) & ~0xFFF))
3635 #define minor(rdev) (((rdev) & 0xFF) | ((UINT32)((rdev) >> 12) & ~0xFF))
3637 static NTSTATUS
mknod(device_extension
* Vcb
, PFILE_OBJECT FileObject
, void* data
, ULONG datalen
, PIRP Irp
) {
3642 file_ref
*parfileref
, *fileref
;
3643 UNICODE_STRING name
;
3652 SECURITY_SUBJECT_CONTEXT subjcont
;
3656 TRACE("(%p, %p, %p, %u)\n", Vcb
, FileObject
, data
, datalen
);
3658 if (!FileObject
|| !FileObject
->FsContext
|| !FileObject
->FsContext2
|| FileObject
->FsContext
== Vcb
->volume_fcb
)
3659 return STATUS_INVALID_PARAMETER
;
3662 return STATUS_MEDIA_WRITE_PROTECTED
;
3664 parfcb
= FileObject
->FsContext
;
3666 if (parfcb
->type
!= BTRFS_TYPE_DIRECTORY
) {
3667 WARN("trying to create file in something other than a directory\n");
3668 return STATUS_INVALID_PARAMETER
;
3671 if (is_subvol_readonly(parfcb
->subvol
, Irp
))
3672 return STATUS_ACCESS_DENIED
;
3674 parccb
= FileObject
->FsContext2
;
3675 parfileref
= parccb
->fileref
;
3678 return STATUS_INVALID_PARAMETER
;
3680 if (datalen
< sizeof(btrfs_mknod
))
3681 return STATUS_INVALID_PARAMETER
;
3683 bmn
= (btrfs_mknod
*)data
;
3685 if (datalen
< offsetof(btrfs_mknod
, name
[0]) + bmn
->namelen
|| bmn
->namelen
< sizeof(WCHAR
))
3686 return STATUS_INVALID_PARAMETER
;
3688 if (bmn
->type
== BTRFS_TYPE_UNKNOWN
|| bmn
->type
> BTRFS_TYPE_SYMLINK
)
3689 return STATUS_INVALID_PARAMETER
;
3691 if ((bmn
->type
== BTRFS_TYPE_DIRECTORY
&& !(parccb
->access
& FILE_ADD_SUBDIRECTORY
)) ||
3692 (bmn
->type
!= BTRFS_TYPE_DIRECTORY
&& !(parccb
->access
& FILE_ADD_FILE
))) {
3693 WARN("insufficient privileges\n");
3694 return STATUS_ACCESS_DENIED
;
3697 if (bmn
->inode
!= 0) {
3698 if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE
), Irp
->RequestorMode
))
3699 return STATUS_PRIVILEGE_NOT_HELD
;
3702 for (i
= 0; i
< bmn
->namelen
/ sizeof(WCHAR
); i
++) {
3703 if (bmn
->name
[i
] == 0 || bmn
->name
[i
] == '/')
3704 return STATUS_OBJECT_NAME_INVALID
;
3707 // don't allow files called . or ..
3708 if (bmn
->name
[0] == '.' && (bmn
->namelen
== sizeof(WCHAR
) || (bmn
->namelen
== 2 * sizeof(WCHAR
) && bmn
->name
[1] == '.')))
3709 return STATUS_OBJECT_NAME_INVALID
;
3711 Status
= RtlUnicodeToUTF8N(NULL
, 0, &len
, bmn
->name
, bmn
->namelen
);
3712 if (!NT_SUCCESS(Status
)) {
3713 ERR("RtlUnicodeToUTF8N return %08x\n", Status
);
3718 ERR("RtlUnicodeToUTF8N returned a length of 0\n");
3719 return STATUS_INTERNAL_ERROR
;
3723 ERR("len was too long (%x)\n", len
);
3724 return STATUS_INVALID_PARAMETER
;
3727 utf8
.MaximumLength
= utf8
.Length
= (USHORT
)len
;
3728 utf8
.Buffer
= ExAllocatePoolWithTag(PagedPool
, utf8
.MaximumLength
, ALLOC_TAG
);
3731 ERR("out of memory\n");
3732 return STATUS_INSUFFICIENT_RESOURCES
;
3735 Status
= RtlUnicodeToUTF8N(utf8
.Buffer
, len
, &len
, bmn
->name
, bmn
->namelen
);
3736 if (!NT_SUCCESS(Status
)) {
3737 ERR("RtlUnicodeToUTF8N failed with error %08x\n", Status
);
3738 ExFreePool(utf8
.Buffer
);
3742 name
.Length
= name
.MaximumLength
= bmn
->namelen
;
3743 name
.Buffer
= bmn
->name
;
3745 ExAcquireResourceExclusiveLite(&Vcb
->fcb_lock
, TRUE
);
3747 Status
= find_file_in_dir(&name
, parfcb
, &subvol
, &inode
, &dc
, TRUE
);
3748 if (!NT_SUCCESS(Status
) && Status
!= STATUS_OBJECT_NAME_NOT_FOUND
) {
3749 ERR("find_file_in_dir returned %08x\n", Status
);
3753 if (NT_SUCCESS(Status
)) {
3754 WARN("filename already exists\n");
3755 Status
= STATUS_OBJECT_NAME_COLLISION
;
3759 if (bmn
->inode
== 0) {
3760 inode
= InterlockedIncrement64(&parfcb
->subvol
->lastinode
);
3761 lastle
= parfcb
->subvol
->fcbs
.Blink
;
3763 if (bmn
->inode
> (UINT64
)parfcb
->subvol
->lastinode
) {
3764 inode
= parfcb
->subvol
->lastinode
= bmn
->inode
;
3765 lastle
= parfcb
->subvol
->fcbs
.Blink
;
3767 LIST_ENTRY
* le
= parfcb
->subvol
->fcbs
.Flink
;
3769 lastle
= parfcb
->subvol
->fcbs
.Blink
;;
3770 while (le
!= &parfcb
->subvol
->fcbs
) {
3771 struct _fcb
* fcb2
= CONTAINING_RECORD(le
, struct _fcb
, list_entry
);
3773 if (fcb2
->inode
== bmn
->inode
&& !fcb2
->deleted
) {
3774 WARN("inode collision\n");
3775 Status
= STATUS_INVALID_PARAMETER
;
3777 } else if (fcb2
->inode
> bmn
->inode
) {
3778 lastle
= fcb2
->list_entry
.Blink
;
3789 KeQuerySystemTime(&time
);
3790 win_time_to_unix(time
, &now
);
3792 fcb
= create_fcb(Vcb
, PagedPool
);
3794 ERR("out of memory\n");
3795 Status
= STATUS_INSUFFICIENT_RESOURCES
;
3801 fcb
->inode_item
.generation
= Vcb
->superblock
.generation
;
3802 fcb
->inode_item
.transid
= Vcb
->superblock
.generation
;
3803 fcb
->inode_item
.st_size
= 0;
3804 fcb
->inode_item
.st_blocks
= 0;
3805 fcb
->inode_item
.block_group
= 0;
3806 fcb
->inode_item
.st_nlink
= 1;
3807 fcb
->inode_item
.st_uid
= UID_NOBODY
;
3808 fcb
->inode_item
.st_gid
= GID_NOBODY
;
3809 fcb
->inode_item
.st_mode
= inherit_mode(parfcb
, bmn
->type
== BTRFS_TYPE_DIRECTORY
);
3811 if (bmn
->type
== BTRFS_TYPE_BLOCKDEV
|| bmn
->type
== BTRFS_TYPE_CHARDEV
)
3812 fcb
->inode_item
.st_rdev
= (minor(bmn
->st_rdev
) & 0xFFFFF) | ((major(bmn
->st_rdev
) & 0xFFFFFFFFFFF) << 20);
3814 fcb
->inode_item
.st_rdev
= 0;
3816 fcb
->inode_item
.flags
= 0;
3817 fcb
->inode_item
.sequence
= 1;
3818 fcb
->inode_item
.st_atime
= now
;
3819 fcb
->inode_item
.st_ctime
= now
;
3820 fcb
->inode_item
.st_mtime
= now
;
3821 fcb
->inode_item
.otime
= now
;
3823 if (bmn
->type
== BTRFS_TYPE_DIRECTORY
)
3824 fcb
->inode_item
.st_mode
|= __S_IFDIR
;
3825 else if (bmn
->type
== BTRFS_TYPE_CHARDEV
)
3826 fcb
->inode_item
.st_mode
|= __S_IFCHR
;
3827 else if (bmn
->type
== BTRFS_TYPE_BLOCKDEV
)
3828 fcb
->inode_item
.st_mode
|= __S_IFBLK
;
3829 else if (bmn
->type
== BTRFS_TYPE_FIFO
)
3830 fcb
->inode_item
.st_mode
|= __S_IFIFO
;
3831 else if (bmn
->type
== BTRFS_TYPE_SOCKET
)
3832 fcb
->inode_item
.st_mode
|= __S_IFSOCK
;
3833 else if (bmn
->type
== BTRFS_TYPE_SYMLINK
)
3834 fcb
->inode_item
.st_mode
|= __S_IFLNK
;
3836 fcb
->inode_item
.st_mode
|= __S_IFREG
;
3838 if (bmn
->type
!= BTRFS_TYPE_DIRECTORY
)
3839 fcb
->inode_item
.st_mode
&= ~(S_IXUSR
| S_IXGRP
| S_IXOTH
); // remove executable bit if not directory
3841 // inherit nodatacow flag from parent directory
3842 if (parfcb
->inode_item
.flags
& BTRFS_INODE_NODATACOW
) {
3843 fcb
->inode_item
.flags
|= BTRFS_INODE_NODATACOW
;
3845 if (bmn
->type
!= BTRFS_TYPE_DIRECTORY
)
3846 fcb
->inode_item
.flags
|= BTRFS_INODE_NODATASUM
;
3849 if (parfcb
->inode_item
.flags
& BTRFS_INODE_COMPRESS
)
3850 fcb
->inode_item
.flags
|= BTRFS_INODE_COMPRESS
;
3852 fcb
->prop_compression
= parfcb
->prop_compression
;
3853 fcb
->prop_compression_changed
= fcb
->prop_compression
!= PropCompression_None
;
3855 fcb
->inode_item_changed
= TRUE
;
3857 fcb
->Header
.IsFastIoPossible
= fast_io_possible(fcb
);
3858 fcb
->Header
.AllocationSize
.QuadPart
= 0;
3859 fcb
->Header
.FileSize
.QuadPart
= 0;
3860 fcb
->Header
.ValidDataLength
.QuadPart
= 0;
3864 if (bmn
->name
[0] == '.')
3865 fcb
->atts
|= FILE_ATTRIBUTE_HIDDEN
;
3867 if (bmn
->type
== BTRFS_TYPE_DIRECTORY
)
3868 fcb
->atts
|= FILE_ATTRIBUTE_DIRECTORY
;
3870 fcb
->atts_changed
= FALSE
;
3872 InterlockedIncrement(&parfcb
->refcount
);
3873 fcb
->subvol
= parfcb
->subvol
;
3875 fcb
->type
= bmn
->type
;
3877 SeCaptureSubjectContext(&subjcont
);
3879 Status
= SeAssignSecurityEx(parfileref
? parfileref
->fcb
->sd
: NULL
, NULL
, (void**)&fcb
->sd
, NULL
, fcb
->type
== BTRFS_TYPE_DIRECTORY
,
3880 SEF_SACL_AUTO_INHERIT
, &subjcont
, IoGetFileObjectGenericMapping(), PagedPool
);
3882 if (!NT_SUCCESS(Status
)) {
3883 ERR("SeAssignSecurityEx returned %08x\n", Status
);
3888 Status
= RtlGetOwnerSecurityDescriptor(fcb
->sd
, &owner
, &defaulted
);
3889 if (!NT_SUCCESS(Status
)) {
3890 WARN("RtlGetOwnerSecurityDescriptor returned %08x\n", Status
);
3891 fcb
->sd_dirty
= TRUE
;
3893 fcb
->inode_item
.st_uid
= sid_to_uid(owner
);
3894 fcb
->sd_dirty
= fcb
->inode_item
.st_uid
== UID_NOBODY
;
3897 find_gid(fcb
, parfcb
, &subjcont
);
3899 fileref
= create_fileref(Vcb
);
3901 ERR("out of memory\n");
3903 Status
= STATUS_INSUFFICIENT_RESOURCES
;
3909 fcb
->created
= TRUE
;
3910 mark_fcb_dirty(fcb
);
3912 fileref
->created
= TRUE
;
3913 mark_fileref_dirty(fileref
);
3915 fcb
->subvol
->root_item
.ctransid
= Vcb
->superblock
.generation
;
3916 fcb
->subvol
->root_item
.ctime
= now
;
3918 fileref
->parent
= parfileref
;
3920 Status
= add_dir_child(fileref
->parent
->fcb
, fcb
->inode
, FALSE
, &utf8
, &name
, fcb
->type
, &dc
);
3921 if (!NT_SUCCESS(Status
))
3922 WARN("add_dir_child returned %08x\n", Status
);
3925 dc
->fileref
= fileref
;
3927 ExAcquireResourceExclusiveLite(&parfileref
->nonpaged
->children_lock
, TRUE
);
3928 InsertTailList(&parfileref
->children
, &fileref
->list_entry
);
3929 ExReleaseResourceLite(&parfileref
->nonpaged
->children_lock
);
3931 increase_fileref_refcount(parfileref
);
3933 if (fcb
->type
== BTRFS_TYPE_DIRECTORY
) {
3934 fcb
->hash_ptrs
= ExAllocatePoolWithTag(PagedPool
, sizeof(LIST_ENTRY
*) * 256, ALLOC_TAG
);
3935 if (!fcb
->hash_ptrs
) {
3936 ERR("out of memory\n");
3937 free_fileref(Vcb
, fileref
);
3938 Status
= STATUS_INSUFFICIENT_RESOURCES
;
3942 RtlZeroMemory(fcb
->hash_ptrs
, sizeof(LIST_ENTRY
*) * 256);
3944 fcb
->hash_ptrs_uc
= ExAllocatePoolWithTag(PagedPool
, sizeof(LIST_ENTRY
*) * 256, ALLOC_TAG
);
3945 if (!fcb
->hash_ptrs_uc
) {
3946 ERR("out of memory\n");
3947 free_fileref(Vcb
, fileref
);
3948 Status
= STATUS_INSUFFICIENT_RESOURCES
;
3952 RtlZeroMemory(fcb
->hash_ptrs_uc
, sizeof(LIST_ENTRY
*) * 256);
3955 InsertHeadList(lastle
, &fcb
->list_entry
);
3956 InsertTailList(&Vcb
->all_fcbs
, &fcb
->list_entry_all
);
3958 if (bmn
->type
== BTRFS_TYPE_DIRECTORY
)
3959 fileref
->fcb
->fileref
= fileref
;
3961 ExAcquireResourceExclusiveLite(parfcb
->Header
.Resource
, TRUE
);
3962 parfcb
->inode_item
.st_size
+= utf8
.Length
* 2;
3963 parfcb
->inode_item
.transid
= Vcb
->superblock
.generation
;
3964 parfcb
->inode_item
.sequence
++;
3966 if (!parccb
->user_set_change_time
)
3967 parfcb
->inode_item
.st_ctime
= now
;
3969 if (!parccb
->user_set_write_time
)
3970 parfcb
->inode_item
.st_mtime
= now
;
3972 ExReleaseResourceLite(parfcb
->Header
.Resource
);
3974 parfcb
->inode_item_changed
= TRUE
;
3975 mark_fcb_dirty(parfcb
);
3977 send_notification_fileref(fileref
, bmn
->type
== BTRFS_TYPE_DIRECTORY
? FILE_NOTIFY_CHANGE_DIR_NAME
: FILE_NOTIFY_CHANGE_FILE_NAME
, FILE_ACTION_ADDED
, NULL
);
3979 if (!parccb
->user_set_write_time
)
3980 send_notification_fcb(parfileref
, FILE_NOTIFY_CHANGE_LAST_WRITE
, FILE_ACTION_MODIFIED
, NULL
);
3982 Status
= STATUS_SUCCESS
;
3985 ExReleaseResourceLite(&Vcb
->fcb_lock
);
3987 ExFreePool(utf8
.Buffer
);
3992 static void mark_subvol_dirty(device_extension
* Vcb
, root
* r
) {
3996 ExAcquireResourceExclusiveLite(&Vcb
->dirty_subvols_lock
, TRUE
);
3997 InsertTailList(&Vcb
->dirty_subvols
, &r
->list_entry_dirty
);
3998 ExReleaseResourceLite(&Vcb
->dirty_subvols_lock
);
4001 Vcb
->need_write
= TRUE
;
4004 static NTSTATUS
recvd_subvol(device_extension
* Vcb
, PFILE_OBJECT FileObject
, void* data
, ULONG datalen
, KPROCESSOR_MODE processor_mode
) {
4005 btrfs_received_subvol
* brs
= (btrfs_received_subvol
*)data
;
4011 TRACE("(%p, %p, %p, %u)\n", Vcb
, FileObject
, data
, datalen
);
4013 if (!data
|| datalen
< sizeof(btrfs_received_subvol
))
4014 return STATUS_INVALID_PARAMETER
;
4016 if (!FileObject
|| !FileObject
->FsContext
|| FileObject
->FsContext
== Vcb
->volume_fcb
)
4017 return STATUS_INVALID_PARAMETER
;
4019 fcb
= FileObject
->FsContext
;
4022 return STATUS_INVALID_PARAMETER
;
4024 if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE
), processor_mode
))
4025 return STATUS_PRIVILEGE_NOT_HELD
;
4027 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, TRUE
);
4029 if (fcb
->subvol
->root_item
.rtransid
!= 0) {
4030 WARN("subvol already has received information set\n");
4031 Status
= STATUS_INVALID_PARAMETER
;
4035 KeQuerySystemTime(&time
);
4036 win_time_to_unix(time
, &now
);
4038 RtlCopyMemory(&fcb
->subvol
->root_item
.received_uuid
, &brs
->uuid
, sizeof(BTRFS_UUID
));
4039 fcb
->subvol
->root_item
.stransid
= brs
->generation
;
4040 fcb
->subvol
->root_item
.rtransid
= Vcb
->superblock
.generation
;
4041 fcb
->subvol
->root_item
.rtime
= now
;
4043 fcb
->subvol
->received
= TRUE
;
4044 mark_subvol_dirty(Vcb
, fcb
->subvol
);
4046 Status
= STATUS_SUCCESS
;
4049 ExReleaseResourceLite(&Vcb
->tree_lock
);
4054 static NTSTATUS
fsctl_get_xattrs(device_extension
* Vcb
, PFILE_OBJECT FileObject
, void* data
, ULONG datalen
, KPROCESSOR_MODE processor_mode
) {
4056 btrfs_set_xattr
* bsxa
;
4057 ULONG reqlen
= (ULONG
)offsetof(btrfs_set_xattr
, data
[0]);
4061 if (!data
|| datalen
< reqlen
)
4062 return STATUS_INVALID_PARAMETER
;
4064 if (!FileObject
|| !FileObject
->FsContext
|| !FileObject
->FsContext2
|| FileObject
->FsContext
== Vcb
->volume_fcb
)
4065 return STATUS_INVALID_PARAMETER
;
4067 fcb
= FileObject
->FsContext
;
4068 ccb
= FileObject
->FsContext2
;
4070 if (!(ccb
->access
& (FILE_READ_ATTRIBUTES
| FILE_WRITE_ATTRIBUTES
)) && processor_mode
== UserMode
) {
4071 WARN("insufficient privileges\n");
4072 return STATUS_ACCESS_DENIED
;
4075 ExAcquireResourceSharedLite(fcb
->Header
.Resource
, TRUE
);
4077 le
= fcb
->xattrs
.Flink
;
4078 while (le
!= &fcb
->xattrs
) {
4079 xattr
* xa
= CONTAINING_RECORD(le
, xattr
, list_entry
);
4081 if (xa
->valuelen
> 0)
4082 reqlen
+= (ULONG
)offsetof(btrfs_set_xattr
, data
[0]) + xa
->namelen
+ xa
->valuelen
;
4087 if (datalen
< reqlen
) {
4088 ExReleaseResourceLite(fcb
->Header
.Resource
);
4089 return STATUS_BUFFER_OVERFLOW
;
4092 bsxa
= (btrfs_set_xattr
*)data
;
4095 le
= fcb
->xattrs
.Flink
;
4096 while (le
!= &fcb
->xattrs
) {
4097 xattr
* xa
= CONTAINING_RECORD(le
, xattr
, list_entry
);
4099 if (xa
->valuelen
> 0) {
4100 bsxa
->namelen
= xa
->namelen
;
4101 bsxa
->valuelen
= xa
->valuelen
;
4102 memcpy(bsxa
->data
, xa
->data
, xa
->namelen
+ xa
->valuelen
);
4104 bsxa
= (btrfs_set_xattr
*)&bsxa
->data
[xa
->namelen
+ xa
->valuelen
];
4114 ExReleaseResourceLite(fcb
->Header
.Resource
);
4116 return STATUS_SUCCESS
;
4119 static NTSTATUS
fsctl_set_xattr(device_extension
* Vcb
, PFILE_OBJECT FileObject
, void* data
, ULONG datalen
, PIRP Irp
) {
4121 btrfs_set_xattr
* bsxa
;
4127 static const char stream_pref
[] = "user.";
4129 TRACE("(%p, %p, %p, %u)\n", Vcb
, FileObject
, data
, datalen
);
4131 if (!data
|| datalen
< sizeof(btrfs_set_xattr
))
4132 return STATUS_INVALID_PARAMETER
;
4134 bsxa
= (btrfs_set_xattr
*)data
;
4136 if (datalen
< offsetof(btrfs_set_xattr
, data
[0]) + bsxa
->namelen
+ bsxa
->valuelen
)
4137 return STATUS_INVALID_PARAMETER
;
4139 if (bsxa
->namelen
+ bsxa
->valuelen
+ sizeof(tree_header
) + sizeof(leaf_node
) + offsetof(DIR_ITEM
, name
[0]) > Vcb
->superblock
.node_size
)
4140 return STATUS_INVALID_PARAMETER
;
4142 if (!FileObject
|| !FileObject
->FsContext
|| !FileObject
->FsContext2
|| FileObject
->FsContext
== Vcb
->volume_fcb
)
4143 return STATUS_INVALID_PARAMETER
;
4146 return STATUS_MEDIA_WRITE_PROTECTED
;
4148 fcb
= FileObject
->FsContext
;
4149 ccb
= FileObject
->FsContext2
;
4151 if (is_subvol_readonly(fcb
->subvol
, Irp
))
4152 return STATUS_ACCESS_DENIED
;
4154 if (!(ccb
->access
& FILE_WRITE_ATTRIBUTES
) && Irp
->RequestorMode
== UserMode
) {
4155 WARN("insufficient privileges\n");
4156 return STATUS_ACCESS_DENIED
;
4159 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, TRUE
);
4161 ExAcquireResourceExclusiveLite(fcb
->Header
.Resource
, TRUE
);
4163 if (bsxa
->namelen
== strlen(EA_NTACL
) && RtlCompareMemory(bsxa
->data
, EA_NTACL
, strlen(EA_NTACL
)) == strlen(EA_NTACL
)) {
4164 if ((!(ccb
->access
& WRITE_DAC
) || !(ccb
->access
& WRITE_OWNER
)) && Irp
->RequestorMode
== UserMode
) {
4165 WARN("insufficient privileges\n");
4166 Status
= STATUS_ACCESS_DENIED
;
4170 if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE
), Irp
->RequestorMode
)) {
4171 Status
= STATUS_PRIVILEGE_NOT_HELD
;
4176 ExFreePool(fcb
->sd
);
4178 if (bsxa
->valuelen
> 0 && RtlValidRelativeSecurityDescriptor(bsxa
->data
+ bsxa
->namelen
, bsxa
->valuelen
, 0)) {
4179 fcb
->sd
= ExAllocatePoolWithTag(PagedPool
, bsxa
->valuelen
, ALLOC_TAG
);
4181 ERR("out of memory\n");
4182 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4186 RtlCopyMemory(fcb
->sd
, bsxa
->data
+ bsxa
->namelen
, bsxa
->valuelen
);
4190 fcb
->sd_dirty
= TRUE
;
4193 fcb_get_sd(fcb
, ccb
->fileref
->parent
->fcb
, FALSE
, Irp
);
4194 fcb
->sd_deleted
= TRUE
;
4197 mark_fcb_dirty(fcb
);
4199 Status
= STATUS_SUCCESS
;
4201 } else if (bsxa
->namelen
== strlen(EA_DOSATTRIB
) && RtlCompareMemory(bsxa
->data
, EA_DOSATTRIB
, strlen(EA_DOSATTRIB
)) == strlen(EA_DOSATTRIB
)) {
4204 if (bsxa
->valuelen
> 0 && get_file_attributes_from_xattr(bsxa
->data
+ bsxa
->namelen
, bsxa
->valuelen
, &atts
)) {
4207 if (fcb
->type
== BTRFS_TYPE_DIRECTORY
)
4208 fcb
->atts
|= FILE_ATTRIBUTE_DIRECTORY
;
4209 else if (fcb
->type
== BTRFS_TYPE_SYMLINK
)
4210 fcb
->atts
|= FILE_ATTRIBUTE_REPARSE_POINT
;
4212 if (fcb
->inode
== SUBVOL_ROOT_INODE
) {
4213 if (fcb
->subvol
->root_item
.flags
& BTRFS_SUBVOL_READONLY
)
4214 fcb
->atts
|= FILE_ATTRIBUTE_READONLY
;
4216 fcb
->atts
&= ~FILE_ATTRIBUTE_READONLY
;
4219 fcb
->atts_deleted
= FALSE
;
4221 BOOL hidden
= ccb
->fileref
&& ccb
->fileref
->dc
&& ccb
->fileref
->dc
->utf8
.Buffer
&& ccb
->fileref
->dc
->utf8
.Buffer
[0] == '.';
4223 fcb
->atts
= get_file_attributes(Vcb
, fcb
->subvol
, fcb
->inode
, fcb
->type
, hidden
, TRUE
, Irp
);
4224 fcb
->atts_deleted
= TRUE
;
4227 fcb
->atts_changed
= TRUE
;
4228 mark_fcb_dirty(fcb
);
4230 Status
= STATUS_SUCCESS
;
4232 } else if (bsxa
->namelen
== strlen(EA_REPARSE
) && RtlCompareMemory(bsxa
->data
, EA_REPARSE
, strlen(EA_REPARSE
)) == strlen(EA_REPARSE
)) {
4233 if (fcb
->reparse_xattr
.Buffer
) {
4234 ExFreePool(fcb
->reparse_xattr
.Buffer
);
4235 fcb
->reparse_xattr
.Buffer
= NULL
;
4236 fcb
->reparse_xattr
.Length
= fcb
->reparse_xattr
.MaximumLength
= 0;
4239 if (bsxa
->valuelen
> 0) {
4240 fcb
->reparse_xattr
.Buffer
= ExAllocatePoolWithTag(PagedPool
, bsxa
->valuelen
, ALLOC_TAG
);
4241 if (!fcb
->reparse_xattr
.Buffer
) {
4242 ERR("out of memory\n");
4243 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4247 RtlCopyMemory(fcb
->reparse_xattr
.Buffer
, bsxa
->data
+ bsxa
->namelen
, bsxa
->valuelen
);
4248 fcb
->reparse_xattr
.Length
= fcb
->reparse_xattr
.MaximumLength
= bsxa
->valuelen
;
4251 fcb
->reparse_xattr_changed
= TRUE
;
4252 mark_fcb_dirty(fcb
);
4254 Status
= STATUS_SUCCESS
;
4256 } else if (bsxa
->namelen
== strlen(EA_EA
) && RtlCompareMemory(bsxa
->data
, EA_EA
, strlen(EA_EA
)) == strlen(EA_EA
)) {
4257 if (!(ccb
->access
& FILE_WRITE_EA
) && Irp
->RequestorMode
== UserMode
) {
4258 WARN("insufficient privileges\n");
4259 Status
= STATUS_ACCESS_DENIED
;
4263 if (fcb
->ea_xattr
.Buffer
) {
4264 ExFreePool(fcb
->ea_xattr
.Buffer
);
4265 fcb
->ea_xattr
.Length
= fcb
->ea_xattr
.MaximumLength
= 0;
4266 fcb
->ea_xattr
.Buffer
= NULL
;
4271 if (bsxa
->valuelen
> 0) {
4274 Status
= IoCheckEaBufferValidity((FILE_FULL_EA_INFORMATION
*)(bsxa
->data
+ bsxa
->namelen
), bsxa
->valuelen
, &offset
);
4276 if (!NT_SUCCESS(Status
))
4277 WARN("IoCheckEaBufferValidity returned %08x (error at offset %u)\n", Status
, offset
);
4279 FILE_FULL_EA_INFORMATION
* eainfo
;
4281 fcb
->ea_xattr
.Buffer
= ExAllocatePoolWithTag(PagedPool
, bsxa
->valuelen
, ALLOC_TAG
);
4282 if (!fcb
->ea_xattr
.Buffer
) {
4283 ERR("out of memory\n");
4284 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4288 RtlCopyMemory(fcb
->ea_xattr
.Buffer
, bsxa
->data
+ bsxa
->namelen
, bsxa
->valuelen
);
4290 fcb
->ea_xattr
.Length
= fcb
->ea_xattr
.MaximumLength
= bsxa
->valuelen
;
4295 eainfo
= (FILE_FULL_EA_INFORMATION
*)(bsxa
->data
+ bsxa
->namelen
);
4297 fcb
->ealen
+= 5 + eainfo
->EaNameLength
+ eainfo
->EaValueLength
;
4299 if (eainfo
->NextEntryOffset
== 0)
4302 eainfo
= (FILE_FULL_EA_INFORMATION
*)(((UINT8
*)eainfo
) + eainfo
->NextEntryOffset
);
4307 fcb
->ea_changed
= TRUE
;
4308 mark_fcb_dirty(fcb
);
4310 Status
= STATUS_SUCCESS
;
4312 } else if (bsxa
->namelen
== strlen(EA_PROP_COMPRESSION
) && RtlCompareMemory(bsxa
->data
, EA_PROP_COMPRESSION
, strlen(EA_PROP_COMPRESSION
)) == strlen(EA_PROP_COMPRESSION
)) {
4313 const char lzo
[] = "lzo";
4314 const char zlib
[] = "zlib";
4316 if (bsxa
->valuelen
== strlen(lzo
) && RtlCompareMemory(bsxa
->data
+ bsxa
->namelen
, lzo
, bsxa
->valuelen
) == bsxa
->valuelen
)
4317 fcb
->prop_compression
= PropCompression_LZO
;
4318 else if (bsxa
->valuelen
== strlen(zlib
) && RtlCompareMemory(bsxa
->data
+ bsxa
->namelen
, zlib
, bsxa
->valuelen
) == bsxa
->valuelen
)
4319 fcb
->prop_compression
= PropCompression_Zlib
;
4321 fcb
->prop_compression
= PropCompression_None
;
4323 if (fcb
->prop_compression
!= PropCompression_None
) {
4324 fcb
->inode_item
.flags
|= BTRFS_INODE_COMPRESS
;
4325 fcb
->inode_item_changed
= TRUE
;
4328 fcb
->prop_compression_changed
= TRUE
;
4329 mark_fcb_dirty(fcb
);
4331 Status
= STATUS_SUCCESS
;
4333 } else if (bsxa
->namelen
>= strlen(stream_pref
) && RtlCompareMemory(bsxa
->data
, stream_pref
, strlen(stream_pref
)) == strlen(stream_pref
)) {
4334 // don't allow xattrs beginning with user., as these appear as streams instead
4335 Status
= STATUS_OBJECT_NAME_INVALID
;
4339 xa
= ExAllocatePoolWithTag(PagedPool
, offsetof(xattr
, data
[0]) + bsxa
->namelen
+ bsxa
->valuelen
, ALLOC_TAG
);
4341 ERR("out of memory\n");
4342 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4346 le
= fcb
->xattrs
.Flink
;
4347 while (le
!= &fcb
->xattrs
) {
4348 xattr
* xa2
= CONTAINING_RECORD(le
, xattr
, list_entry
);
4350 if (xa2
->namelen
== bsxa
->namelen
&& RtlCompareMemory(xa2
->data
, bsxa
->data
, xa2
->namelen
) == xa2
->namelen
) {
4351 RemoveEntryList(&xa2
->list_entry
);
4359 xa
->namelen
= bsxa
->namelen
;
4360 xa
->valuelen
= bsxa
->valuelen
;
4362 RtlCopyMemory(xa
->data
, bsxa
->data
, bsxa
->namelen
+ bsxa
->valuelen
);
4364 InsertTailList(&fcb
->xattrs
, &xa
->list_entry
);
4366 fcb
->xattrs_changed
= TRUE
;
4367 mark_fcb_dirty(fcb
);
4369 Status
= STATUS_SUCCESS
;
4372 ExReleaseResourceLite(fcb
->Header
.Resource
);
4374 ExReleaseResourceLite(&Vcb
->tree_lock
);
4379 static NTSTATUS
reserve_subvol(device_extension
* Vcb
, PFILE_OBJECT FileObject
, PIRP Irp
) {
4383 TRACE("(%p, %p)\n", Vcb
, FileObject
);
4385 // "Reserving" a readonly subvol allows the calling process to write into it until the handle is closed.
4387 if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE
), Irp
->RequestorMode
))
4388 return STATUS_PRIVILEGE_NOT_HELD
;
4390 if (!FileObject
|| !FileObject
->FsContext
|| !FileObject
->FsContext2
|| FileObject
->FsContext
== Vcb
->volume_fcb
)
4391 return STATUS_INVALID_PARAMETER
;
4393 fcb
= FileObject
->FsContext
;
4394 ccb
= FileObject
->FsContext2
;
4396 if (!(fcb
->subvol
->root_item
.flags
& BTRFS_SUBVOL_READONLY
))
4397 return STATUS_INVALID_PARAMETER
;
4399 if (fcb
->subvol
->reserved
)
4400 return STATUS_INVALID_PARAMETER
;
4402 fcb
->subvol
->reserved
= PsGetCurrentProcess();
4403 ccb
->reserving
= TRUE
;
4405 return STATUS_SUCCESS
;
4408 static NTSTATUS
get_subvol_path(device_extension
* Vcb
, UINT64 id
, WCHAR
* out
, ULONG outlen
, PIRP Irp
) {
4415 le
= Vcb
->roots
.Flink
;
4416 while (le
!= &Vcb
->roots
) {
4417 root
* r2
= CONTAINING_RECORD(le
, root
, list_entry
);
4428 ERR("couldn't find subvol %llx\n", id
);
4429 return STATUS_INTERNAL_ERROR
;
4432 ExAcquireResourceSharedLite(&Vcb
->fcb_lock
, TRUE
);
4434 Status
= open_fileref_by_inode(Vcb
, r
, r
->root_item
.objid
, &fr
, Irp
);
4435 if (!NT_SUCCESS(Status
)) {
4436 ExReleaseResourceLite(&Vcb
->fcb_lock
);
4437 ERR("open_fileref_by_inode returned %08x\n", Status
);
4443 us
.MaximumLength
= (USHORT
)min(0xffff, outlen
) - sizeof(WCHAR
);
4445 Status
= fileref_get_filename(fr
, &us
, NULL
, NULL
);
4447 if (NT_SUCCESS(Status
) || Status
== STATUS_BUFFER_OVERFLOW
)
4448 out
[us
.Length
/ sizeof(WCHAR
)] = 0;
4450 ERR("fileref_get_filename returned %08x\n", Status
);
4452 free_fileref(Vcb
, fr
);
4454 ExReleaseResourceLite(&Vcb
->fcb_lock
);
4459 static NTSTATUS
find_subvol(device_extension
* Vcb
, void* in
, ULONG inlen
, void* out
, ULONG outlen
, PIRP Irp
) {
4460 btrfs_find_subvol
* bfs
;
4465 if (!in
|| inlen
< sizeof(btrfs_find_subvol
))
4466 return STATUS_INVALID_PARAMETER
;
4468 if (!out
|| outlen
< sizeof(WCHAR
))
4469 return STATUS_INVALID_PARAMETER
;
4471 if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE
), Irp
->RequestorMode
))
4472 return STATUS_PRIVILEGE_NOT_HELD
;
4474 bfs
= (btrfs_find_subvol
*)in
;
4476 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, TRUE
);
4478 if (!Vcb
->uuid_root
) {
4479 ERR("couldn't find uuid root\n");
4480 Status
= STATUS_NOT_FOUND
;
4484 RtlCopyMemory(&searchkey
.obj_id
, &bfs
->uuid
, sizeof(UINT64
));
4485 searchkey
.obj_type
= TYPE_SUBVOL_UUID
;
4486 RtlCopyMemory(&searchkey
.offset
, &bfs
->uuid
.uuid
[sizeof(UINT64
)], sizeof(UINT64
));
4488 Status
= find_item(Vcb
, Vcb
->uuid_root
, &tp
, &searchkey
, FALSE
, Irp
);
4490 if (!NT_SUCCESS(Status
)) {
4491 ERR("find_item returned %08x\n", Status
);
4495 if (!keycmp(searchkey
, tp
.item
->key
) && tp
.item
->size
>= sizeof(UINT64
)) {
4496 UINT64
* id
= (UINT64
*)tp
.item
->data
;
4498 if (bfs
->ctransid
!= 0) {
4502 searchkey2
.obj_id
= *id
;
4503 searchkey2
.obj_type
= TYPE_ROOT_ITEM
;
4504 searchkey2
.offset
= 0xffffffffffffffff;
4506 Status
= find_item(Vcb
, Vcb
->root_root
, &tp2
, &searchkey2
, FALSE
, Irp
);
4507 if (!NT_SUCCESS(Status
)) {
4508 ERR("find_item returned %08x\n", Status
);
4512 if (tp2
.item
->key
.obj_id
== searchkey2
.obj_id
&& tp2
.item
->key
.obj_type
== searchkey2
.obj_type
&&
4513 tp2
.item
->size
>= offsetof(ROOT_ITEM
, otransid
)) {
4514 ROOT_ITEM
* ri
= (ROOT_ITEM
*)tp2
.item
->data
;
4516 if (ri
->ctransid
== bfs
->ctransid
) {
4517 TRACE("found subvol %llx\n", *id
);
4518 Status
= get_subvol_path(Vcb
, *id
, out
, outlen
, Irp
);
4523 TRACE("found subvol %llx\n", *id
);
4524 Status
= get_subvol_path(Vcb
, *id
, out
, outlen
, Irp
);
4529 searchkey
.obj_type
= TYPE_SUBVOL_REC_UUID
;
4531 Status
= find_item(Vcb
, Vcb
->uuid_root
, &tp
, &searchkey
, FALSE
, Irp
);
4533 if (!NT_SUCCESS(Status
)) {
4534 ERR("find_item returned %08x\n", Status
);
4538 if (!keycmp(searchkey
, tp
.item
->key
) && tp
.item
->size
>= sizeof(UINT64
)) {
4539 UINT64
* ids
= (UINT64
*)tp
.item
->data
;
4542 for (i
= 0; i
< tp
.item
->size
/ sizeof(UINT64
); i
++) {
4543 if (bfs
->ctransid
!= 0) {
4547 searchkey2
.obj_id
= ids
[i
];
4548 searchkey2
.obj_type
= TYPE_ROOT_ITEM
;
4549 searchkey2
.offset
= 0xffffffffffffffff;
4551 Status
= find_item(Vcb
, Vcb
->root_root
, &tp2
, &searchkey2
, FALSE
, Irp
);
4552 if (!NT_SUCCESS(Status
)) {
4553 ERR("find_item returned %08x\n", Status
);
4557 if (tp2
.item
->key
.obj_id
== searchkey2
.obj_id
&& tp2
.item
->key
.obj_type
== searchkey2
.obj_type
&&
4558 tp2
.item
->size
>= offsetof(ROOT_ITEM
, otransid
)) {
4559 ROOT_ITEM
* ri
= (ROOT_ITEM
*)tp2
.item
->data
;
4561 if (ri
->ctransid
== bfs
->ctransid
) {
4562 TRACE("found subvol %llx\n", ids
[i
]);
4563 Status
= get_subvol_path(Vcb
, ids
[i
], out
, outlen
, Irp
);
4568 TRACE("found subvol %llx\n", ids
[i
]);
4569 Status
= get_subvol_path(Vcb
, ids
[i
], out
, outlen
, Irp
);
4575 Status
= STATUS_NOT_FOUND
;
4578 ExReleaseResourceLite(&Vcb
->tree_lock
);
4583 static NTSTATUS
resize_device(device_extension
* Vcb
, void* data
, ULONG len
, PIRP Irp
) {
4584 btrfs_resize
* br
= (btrfs_resize
*)data
;
4589 TRACE("(%p, %p, %u)\n", Vcb
, data
, len
);
4591 if (!data
|| len
< sizeof(btrfs_resize
) || (br
->size
% Vcb
->superblock
.sector_size
) != 0)
4592 return STATUS_INVALID_PARAMETER
;
4594 if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE
), Irp
->RequestorMode
))
4595 return STATUS_PRIVILEGE_NOT_HELD
;
4598 return STATUS_MEDIA_WRITE_PROTECTED
;
4600 ExAcquireResourceExclusiveLite(&Vcb
->tree_lock
, TRUE
);
4602 le
= Vcb
->devices
.Flink
;
4603 while (le
!= &Vcb
->devices
) {
4604 device
* dev2
= CONTAINING_RECORD(le
, device
, list_entry
);
4606 if (dev2
->devitem
.dev_id
== br
->device
) {
4615 ERR("could not find device %llx\n", br
->device
);
4616 Status
= STATUS_INVALID_PARAMETER
;
4621 ERR("trying to resize missing device\n");
4622 Status
= STATUS_INVALID_PARAMETER
;
4626 if (dev
->readonly
) {
4627 ERR("trying to resize readonly device\n");
4628 Status
= STATUS_INVALID_PARAMETER
;
4632 if (br
->size
> 0 && dev
->devitem
.num_bytes
== br
->size
) {
4633 TRACE("size unchanged, returning STATUS_SUCCESS\n");
4634 Status
= STATUS_SUCCESS
;
4638 if (br
->size
> 0 && dev
->devitem
.num_bytes
> br
->size
) { // shrink device
4639 BOOL need_balance
= TRUE
;
4640 UINT64 old_size
, delta
;
4642 le
= dev
->space
.Flink
;
4643 while (le
!= &dev
->space
) {
4644 space
* s
= CONTAINING_RECORD(le
, space
, list_entry
);
4646 if (s
->address
<= br
->size
&& s
->address
+ s
->size
>= dev
->devitem
.num_bytes
) {
4647 need_balance
= FALSE
;
4654 delta
= dev
->devitem
.num_bytes
- br
->size
;
4659 if (Vcb
->balance
.thread
) {
4660 WARN("balance already running\n");
4661 Status
= STATUS_DEVICE_NOT_READY
;
4665 RtlZeroMemory(Vcb
->balance
.opts
, sizeof(btrfs_balance_opts
) * 3);
4667 for (i
= 0; i
< 3; i
++) {
4668 Vcb
->balance
.opts
[i
].flags
= BTRFS_BALANCE_OPTS_ENABLED
| BTRFS_BALANCE_OPTS_DEVID
| BTRFS_BALANCE_OPTS_DRANGE
;
4669 Vcb
->balance
.opts
[i
].devid
= dev
->devitem
.dev_id
;
4670 Vcb
->balance
.opts
[i
].drange_start
= br
->size
;
4671 Vcb
->balance
.opts
[i
].drange_end
= dev
->devitem
.num_bytes
;
4674 Vcb
->balance
.paused
= FALSE
;
4675 Vcb
->balance
.shrinking
= TRUE
;
4676 Vcb
->balance
.status
= STATUS_SUCCESS
;
4677 KeInitializeEvent(&Vcb
->balance
.event
, NotificationEvent
, !Vcb
->balance
.paused
);
4679 space_list_subtract2(&dev
->space
, NULL
, br
->size
, delta
, NULL
, NULL
);
4681 Status
= PsCreateSystemThread(&Vcb
->balance
.thread
, 0, NULL
, NULL
, NULL
, balance_thread
, Vcb
);
4682 if (!NT_SUCCESS(Status
)) {
4683 ERR("PsCreateSystemThread returned %08x\n", Status
);
4687 Status
= STATUS_MORE_PROCESSING_REQUIRED
;
4692 old_size
= dev
->devitem
.num_bytes
;
4693 dev
->devitem
.num_bytes
= br
->size
;
4695 Status
= update_dev_item(Vcb
, dev
, Irp
);
4696 if (!NT_SUCCESS(Status
)) {
4697 ERR("update_dev_item returned %08x\n", Status
);
4698 dev
->devitem
.num_bytes
= old_size
;
4702 space_list_subtract2(&dev
->space
, NULL
, br
->size
, delta
, NULL
, NULL
);
4704 Vcb
->superblock
.total_bytes
-= delta
;
4705 } else { // extend device
4706 GET_LENGTH_INFORMATION gli
;
4707 UINT64 old_size
, delta
;
4709 Status
= dev_ioctl(dev
->devobj
, IOCTL_DISK_GET_LENGTH_INFO
, NULL
, 0,
4710 &gli
, sizeof(gli
), TRUE
, NULL
);
4711 if (!NT_SUCCESS(Status
)) {
4712 ERR("IOCTL_DISK_GET_LENGTH_INFO returned %08x\n", Status
);
4716 if (br
->size
== 0) {
4717 br
->size
= gli
.Length
.QuadPart
;
4719 if (dev
->devitem
.num_bytes
== br
->size
) {
4720 TRACE("size unchanged, returning STATUS_SUCCESS\n");
4721 Status
= STATUS_SUCCESS
;
4725 if (br
->size
== 0) {
4726 ERR("IOCTL_DISK_GET_LENGTH_INFO returned 0 length\n");
4727 Status
= STATUS_INTERNAL_ERROR
;
4730 } else if ((UINT64
)gli
.Length
.QuadPart
< br
->size
) {
4731 ERR("device was %llx bytes, trying to extend to %llx\n", gli
.Length
.QuadPart
, br
->size
);
4732 Status
= STATUS_INVALID_PARAMETER
;
4736 delta
= br
->size
- dev
->devitem
.num_bytes
;
4738 old_size
= dev
->devitem
.num_bytes
;
4739 dev
->devitem
.num_bytes
= br
->size
;
4741 Status
= update_dev_item(Vcb
, dev
, Irp
);
4742 if (!NT_SUCCESS(Status
)) {
4743 ERR("update_dev_item returned %08x\n", Status
);
4744 dev
->devitem
.num_bytes
= old_size
;
4748 space_list_add2(&dev
->space
, NULL
, dev
->devitem
.num_bytes
, delta
, NULL
, NULL
);
4750 Vcb
->superblock
.total_bytes
+= delta
;
4753 Status
= STATUS_SUCCESS
;
4754 Vcb
->need_write
= TRUE
;
4757 ExReleaseResourceLite(&Vcb
->tree_lock
);
4759 if (NT_SUCCESS(Status
))
4760 FsRtlNotifyVolumeEvent(Vcb
->root_file
, FSRTL_VOLUME_CHANGE_SIZE
);
4765 NTSTATUS
fsctl_request(PDEVICE_OBJECT DeviceObject
, PIRP
* Pirp
, UINT32 type
) {
4767 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
4771 #if (NTDDI_VERSION >= NTDDI_WIN7)
4772 case FSCTL_REQUEST_OPLOCK
:
4773 WARN("STUB: FSCTL_REQUEST_OPLOCK\n");
4774 Status
= STATUS_INVALID_DEVICE_REQUEST
;
4778 case FSCTL_REQUEST_OPLOCK_LEVEL_1
:
4779 WARN("STUB: FSCTL_REQUEST_OPLOCK_LEVEL_1\n");
4780 Status
= STATUS_INVALID_DEVICE_REQUEST
;
4783 case FSCTL_REQUEST_OPLOCK_LEVEL_2
:
4784 WARN("STUB: FSCTL_REQUEST_OPLOCK_LEVEL_2\n");
4785 Status
= STATUS_INVALID_DEVICE_REQUEST
;
4788 case FSCTL_REQUEST_BATCH_OPLOCK
:
4789 WARN("STUB: FSCTL_REQUEST_BATCH_OPLOCK\n");
4790 Status
= STATUS_INVALID_DEVICE_REQUEST
;
4793 case FSCTL_OPLOCK_BREAK_ACKNOWLEDGE
:
4794 WARN("STUB: FSCTL_OPLOCK_BREAK_ACKNOWLEDGE\n");
4795 Status
= STATUS_INVALID_DEVICE_REQUEST
;
4798 case FSCTL_OPLOCK_BREAK_ACK_NO_2
:
4799 WARN("STUB: FSCTL_OPLOCK_BREAK_ACK_NO_2\n");
4800 Status
= STATUS_INVALID_DEVICE_REQUEST
;
4803 case FSCTL_OPBATCH_ACK_CLOSE_PENDING
:
4804 WARN("STUB: FSCTL_OPBATCH_ACK_CLOSE_PENDING\n");
4805 Status
= STATUS_INVALID_DEVICE_REQUEST
;
4808 case FSCTL_OPLOCK_BREAK_NOTIFY
:
4809 WARN("STUB: FSCTL_OPLOCK_BREAK_NOTIFY\n");
4810 Status
= STATUS_INVALID_DEVICE_REQUEST
;
4813 case FSCTL_REQUEST_FILTER_OPLOCK
:
4814 WARN("STUB: FSCTL_REQUEST_FILTER_OPLOCK\n");
4815 Status
= STATUS_INVALID_DEVICE_REQUEST
;
4818 case FSCTL_LOCK_VOLUME
:
4819 Status
= lock_volume(DeviceObject
->DeviceExtension
, Irp
);
4822 case FSCTL_UNLOCK_VOLUME
:
4823 Status
= unlock_volume(DeviceObject
->DeviceExtension
, Irp
);
4826 case FSCTL_DISMOUNT_VOLUME
:
4827 Status
= dismount_volume(DeviceObject
->DeviceExtension
, Irp
);
4830 case FSCTL_IS_VOLUME_MOUNTED
:
4831 Status
= is_volume_mounted(DeviceObject
->DeviceExtension
, Irp
);
4834 case FSCTL_IS_PATHNAME_VALID
:
4835 WARN("STUB: FSCTL_IS_PATHNAME_VALID\n");
4836 Status
= STATUS_INVALID_DEVICE_REQUEST
;
4839 case FSCTL_MARK_VOLUME_DIRTY
:
4840 WARN("STUB: FSCTL_MARK_VOLUME_DIRTY\n");
4841 Status
= STATUS_INVALID_DEVICE_REQUEST
;
4844 case FSCTL_QUERY_RETRIEVAL_POINTERS
:
4845 WARN("STUB: FSCTL_QUERY_RETRIEVAL_POINTERS\n");
4846 Status
= STATUS_INVALID_DEVICE_REQUEST
;
4849 case FSCTL_GET_COMPRESSION
:
4850 Status
= get_compression(Irp
);
4853 case FSCTL_SET_COMPRESSION
:
4854 Status
= set_compression(Irp
);
4857 case FSCTL_SET_BOOTLOADER_ACCESSED
:
4858 WARN("STUB: FSCTL_SET_BOOTLOADER_ACCESSED\n");
4859 Status
= STATUS_INVALID_DEVICE_REQUEST
;
4862 case FSCTL_INVALIDATE_VOLUMES
:
4863 Status
= invalidate_volumes(Irp
);
4866 case FSCTL_QUERY_FAT_BPB
:
4867 WARN("STUB: FSCTL_QUERY_FAT_BPB\n");
4868 Status
= STATUS_INVALID_DEVICE_REQUEST
;
4871 case FSCTL_FILESYSTEM_GET_STATISTICS
:
4872 Status
= fs_get_statistics(Irp
->AssociatedIrp
.SystemBuffer
, IrpSp
->Parameters
.FileSystemControl
.OutputBufferLength
, &Irp
->IoStatus
.Information
);
4875 case FSCTL_GET_NTFS_VOLUME_DATA
:
4876 WARN("STUB: FSCTL_GET_NTFS_VOLUME_DATA\n");
4877 Status
= STATUS_INVALID_DEVICE_REQUEST
;
4880 case FSCTL_GET_NTFS_FILE_RECORD
:
4881 WARN("STUB: FSCTL_GET_NTFS_FILE_RECORD\n");
4882 Status
= STATUS_INVALID_DEVICE_REQUEST
;
4885 case FSCTL_GET_VOLUME_BITMAP
:
4886 WARN("STUB: FSCTL_GET_VOLUME_BITMAP\n");
4887 Status
= STATUS_INVALID_DEVICE_REQUEST
;
4890 case FSCTL_GET_RETRIEVAL_POINTERS
:
4891 WARN("STUB: FSCTL_GET_RETRIEVAL_POINTERS\n");
4892 Status
= STATUS_INVALID_DEVICE_REQUEST
;
4895 case FSCTL_MOVE_FILE
:
4896 WARN("STUB: FSCTL_MOVE_FILE\n");
4897 Status
= STATUS_INVALID_DEVICE_REQUEST
;
4900 case FSCTL_IS_VOLUME_DIRTY
:
4901 Status
= is_volume_dirty(DeviceObject
->DeviceExtension
, Irp
);
4904 case FSCTL_ALLOW_EXTENDED_DASD_IO
:
4905 Status
= allow_extended_dasd_io(DeviceObject
->DeviceExtension
, IrpSp
->FileObject
);
4908 case FSCTL_FIND_FILES_BY_SID
:
4909 WARN("STUB: FSCTL_FIND_FILES_BY_SID\n");
4910 Status
= STATUS_INVALID_DEVICE_REQUEST
;
4913 case FSCTL_SET_OBJECT_ID
:
4914 WARN("STUB: FSCTL_SET_OBJECT_ID\n");
4915 Status
= STATUS_INVALID_DEVICE_REQUEST
;
4918 case FSCTL_GET_OBJECT_ID
:
4919 Status
= get_object_id(DeviceObject
->DeviceExtension
, IrpSp
->FileObject
, Irp
->UserBuffer
,
4920 IrpSp
->Parameters
.FileSystemControl
.OutputBufferLength
, &Irp
->IoStatus
.Information
);
4923 case FSCTL_DELETE_OBJECT_ID
:
4924 WARN("STUB: FSCTL_DELETE_OBJECT_ID\n");
4925 Status
= STATUS_INVALID_DEVICE_REQUEST
;
4928 case FSCTL_SET_REPARSE_POINT
:
4929 Status
= set_reparse_point(DeviceObject
, Irp
);
4932 case FSCTL_GET_REPARSE_POINT
:
4933 Status
= get_reparse_point(DeviceObject
, IrpSp
->FileObject
, Irp
->AssociatedIrp
.SystemBuffer
,
4934 IrpSp
->Parameters
.FileSystemControl
.OutputBufferLength
, &Irp
->IoStatus
.Information
);
4937 case FSCTL_DELETE_REPARSE_POINT
:
4938 Status
= delete_reparse_point(DeviceObject
, Irp
);
4941 case FSCTL_ENUM_USN_DATA
:
4942 WARN("STUB: FSCTL_ENUM_USN_DATA\n");
4943 Status
= STATUS_INVALID_DEVICE_REQUEST
;
4946 case FSCTL_SECURITY_ID_CHECK
:
4947 WARN("STUB: FSCTL_SECURITY_ID_CHECK\n");
4948 Status
= STATUS_INVALID_DEVICE_REQUEST
;
4951 case FSCTL_READ_USN_JOURNAL
:
4952 WARN("STUB: FSCTL_READ_USN_JOURNAL\n");
4953 Status
= STATUS_INVALID_DEVICE_REQUEST
;
4956 case FSCTL_SET_OBJECT_ID_EXTENDED
:
4957 WARN("STUB: FSCTL_SET_OBJECT_ID_EXTENDED\n");
4958 Status
= STATUS_INVALID_DEVICE_REQUEST
;
4961 case FSCTL_CREATE_OR_GET_OBJECT_ID
:
4962 Status
= get_object_id(DeviceObject
->DeviceExtension
, IrpSp
->FileObject
, Irp
->UserBuffer
,
4963 IrpSp
->Parameters
.FileSystemControl
.OutputBufferLength
, &Irp
->IoStatus
.Information
);
4966 case FSCTL_SET_SPARSE
:
4967 Status
= set_sparse(DeviceObject
->DeviceExtension
, IrpSp
->FileObject
, Irp
->AssociatedIrp
.SystemBuffer
,
4968 IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
, Irp
);
4971 case FSCTL_SET_ZERO_DATA
:
4972 Status
= set_zero_data(DeviceObject
->DeviceExtension
, IrpSp
->FileObject
, Irp
->AssociatedIrp
.SystemBuffer
,
4973 IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
, Irp
);
4976 case FSCTL_QUERY_ALLOCATED_RANGES
:
4977 Status
= query_ranges(IrpSp
->FileObject
, IrpSp
->Parameters
.FileSystemControl
.Type3InputBuffer
,
4978 IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
, Irp
->UserBuffer
,
4979 IrpSp
->Parameters
.FileSystemControl
.OutputBufferLength
, &Irp
->IoStatus
.Information
);
4982 case FSCTL_ENABLE_UPGRADE
:
4983 WARN("STUB: FSCTL_ENABLE_UPGRADE\n");
4984 Status
= STATUS_INVALID_DEVICE_REQUEST
;
4987 case FSCTL_SET_ENCRYPTION
:
4988 WARN("STUB: FSCTL_SET_ENCRYPTION\n");
4989 Status
= STATUS_INVALID_DEVICE_REQUEST
;
4992 case FSCTL_ENCRYPTION_FSCTL_IO
:
4993 WARN("STUB: FSCTL_ENCRYPTION_FSCTL_IO\n");
4994 Status
= STATUS_INVALID_DEVICE_REQUEST
;
4997 case FSCTL_WRITE_RAW_ENCRYPTED
:
4998 WARN("STUB: FSCTL_WRITE_RAW_ENCRYPTED\n");
4999 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5002 case FSCTL_READ_RAW_ENCRYPTED
:
5003 WARN("STUB: FSCTL_READ_RAW_ENCRYPTED\n");
5004 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5007 case FSCTL_CREATE_USN_JOURNAL
:
5008 WARN("STUB: FSCTL_CREATE_USN_JOURNAL\n");
5009 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5012 case FSCTL_READ_FILE_USN_DATA
:
5013 WARN("STUB: FSCTL_READ_FILE_USN_DATA\n");
5014 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5017 case FSCTL_WRITE_USN_CLOSE_RECORD
:
5018 WARN("STUB: FSCTL_WRITE_USN_CLOSE_RECORD\n");
5019 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5022 case FSCTL_EXTEND_VOLUME
:
5023 WARN("STUB: FSCTL_EXTEND_VOLUME\n");
5024 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5027 case FSCTL_QUERY_USN_JOURNAL
:
5028 WARN("STUB: FSCTL_QUERY_USN_JOURNAL\n");
5029 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5032 case FSCTL_DELETE_USN_JOURNAL
:
5033 WARN("STUB: FSCTL_DELETE_USN_JOURNAL\n");
5034 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5037 case FSCTL_MARK_HANDLE
:
5038 WARN("STUB: FSCTL_MARK_HANDLE\n");
5039 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5042 case FSCTL_SIS_COPYFILE
:
5043 WARN("STUB: FSCTL_SIS_COPYFILE\n");
5044 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5047 case FSCTL_SIS_LINK_FILES
:
5048 WARN("STUB: FSCTL_SIS_LINK_FILES\n");
5049 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5052 case FSCTL_RECALL_FILE
:
5053 WARN("STUB: FSCTL_RECALL_FILE\n");
5054 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5057 case FSCTL_READ_FROM_PLEX
:
5058 WARN("STUB: FSCTL_READ_FROM_PLEX\n");
5059 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5062 case FSCTL_FILE_PREFETCH
:
5063 WARN("STUB: FSCTL_FILE_PREFETCH\n");
5064 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5067 #if WIN32_WINNT >= 0x0600
5068 case FSCTL_MAKE_MEDIA_COMPATIBLE
:
5069 WARN("STUB: FSCTL_MAKE_MEDIA_COMPATIBLE\n");
5070 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5073 case FSCTL_SET_DEFECT_MANAGEMENT
:
5074 WARN("STUB: FSCTL_SET_DEFECT_MANAGEMENT\n");
5075 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5078 case FSCTL_QUERY_SPARING_INFO
:
5079 WARN("STUB: FSCTL_QUERY_SPARING_INFO\n");
5080 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5083 case FSCTL_QUERY_ON_DISK_VOLUME_INFO
:
5084 WARN("STUB: FSCTL_QUERY_ON_DISK_VOLUME_INFO\n");
5085 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5088 case FSCTL_SET_VOLUME_COMPRESSION_STATE
:
5089 WARN("STUB: FSCTL_SET_VOLUME_COMPRESSION_STATE\n");
5090 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5093 case FSCTL_TXFS_MODIFY_RM
:
5094 WARN("STUB: FSCTL_TXFS_MODIFY_RM\n");
5095 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5098 case FSCTL_TXFS_QUERY_RM_INFORMATION
:
5099 WARN("STUB: FSCTL_TXFS_QUERY_RM_INFORMATION\n");
5100 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5103 case FSCTL_TXFS_ROLLFORWARD_REDO
:
5104 WARN("STUB: FSCTL_TXFS_ROLLFORWARD_REDO\n");
5105 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5108 case FSCTL_TXFS_ROLLFORWARD_UNDO
:
5109 WARN("STUB: FSCTL_TXFS_ROLLFORWARD_UNDO\n");
5110 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5113 case FSCTL_TXFS_START_RM
:
5114 WARN("STUB: FSCTL_TXFS_START_RM\n");
5115 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5118 case FSCTL_TXFS_SHUTDOWN_RM
:
5119 WARN("STUB: FSCTL_TXFS_SHUTDOWN_RM\n");
5120 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5123 case FSCTL_TXFS_READ_BACKUP_INFORMATION
:
5124 WARN("STUB: FSCTL_TXFS_READ_BACKUP_INFORMATION\n");
5125 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5128 case FSCTL_TXFS_WRITE_BACKUP_INFORMATION
:
5129 WARN("STUB: FSCTL_TXFS_WRITE_BACKUP_INFORMATION\n");
5130 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5133 case FSCTL_TXFS_CREATE_SECONDARY_RM
:
5134 WARN("STUB: FSCTL_TXFS_CREATE_SECONDARY_RM\n");
5135 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5138 case FSCTL_TXFS_GET_METADATA_INFO
:
5139 WARN("STUB: FSCTL_TXFS_GET_METADATA_INFO\n");
5140 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5143 case FSCTL_TXFS_GET_TRANSACTED_VERSION
:
5144 WARN("STUB: FSCTL_TXFS_GET_TRANSACTED_VERSION\n");
5145 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5148 case FSCTL_TXFS_SAVEPOINT_INFORMATION
:
5149 WARN("STUB: FSCTL_TXFS_SAVEPOINT_INFORMATION\n");
5150 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5153 case FSCTL_TXFS_CREATE_MINIVERSION
:
5154 WARN("STUB: FSCTL_TXFS_CREATE_MINIVERSION\n");
5155 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5158 case FSCTL_TXFS_TRANSACTION_ACTIVE
:
5159 WARN("STUB: FSCTL_TXFS_TRANSACTION_ACTIVE\n");
5160 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5163 case FSCTL_SET_ZERO_ON_DEALLOCATION
:
5164 WARN("STUB: FSCTL_SET_ZERO_ON_DEALLOCATION\n");
5165 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5168 case FSCTL_SET_REPAIR
:
5169 WARN("STUB: FSCTL_SET_REPAIR\n");
5170 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5173 case FSCTL_GET_REPAIR
:
5174 WARN("STUB: FSCTL_GET_REPAIR\n");
5175 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5178 case FSCTL_WAIT_FOR_REPAIR
:
5179 WARN("STUB: FSCTL_WAIT_FOR_REPAIR\n");
5180 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5183 case FSCTL_INITIATE_REPAIR
:
5184 WARN("STUB: FSCTL_INITIATE_REPAIR\n");
5185 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5188 case FSCTL_CSC_INTERNAL
:
5189 WARN("STUB: FSCTL_CSC_INTERNAL\n");
5190 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5193 case FSCTL_SHRINK_VOLUME
:
5194 WARN("STUB: FSCTL_SHRINK_VOLUME\n");
5195 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5198 case FSCTL_SET_SHORT_NAME_BEHAVIOR
:
5199 WARN("STUB: FSCTL_SET_SHORT_NAME_BEHAVIOR\n");
5200 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5203 case FSCTL_DFSR_SET_GHOST_HANDLE_STATE
:
5204 WARN("STUB: FSCTL_DFSR_SET_GHOST_HANDLE_STATE\n");
5205 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5208 case FSCTL_TXFS_LIST_TRANSACTION_LOCKED_FILES
:
5209 WARN("STUB: FSCTL_TXFS_LIST_TRANSACTION_LOCKED_FILES\n");
5210 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5213 case FSCTL_TXFS_LIST_TRANSACTIONS
:
5214 WARN("STUB: FSCTL_TXFS_LIST_TRANSACTIONS\n");
5215 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5218 case FSCTL_QUERY_PAGEFILE_ENCRYPTION
:
5219 WARN("STUB: FSCTL_QUERY_PAGEFILE_ENCRYPTION\n");
5220 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5223 case FSCTL_RESET_VOLUME_ALLOCATION_HINTS
:
5224 WARN("STUB: FSCTL_RESET_VOLUME_ALLOCATION_HINTS\n");
5225 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5228 case FSCTL_TXFS_READ_BACKUP_INFORMATION2
:
5229 WARN("STUB: FSCTL_TXFS_READ_BACKUP_INFORMATION2\n");
5230 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5233 case FSCTL_CSV_CONTROL
:
5234 WARN("STUB: FSCTL_CSV_CONTROL\n");
5235 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5238 // TRACE rather than WARN because Windows 10 spams this undocumented fsctl
5239 case FSCTL_QUERY_VOLUME_CONTAINER_STATE
:
5240 TRACE("STUB: FSCTL_QUERY_VOLUME_CONTAINER_STATE\n");
5241 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5244 case FSCTL_GET_INTEGRITY_INFORMATION
:
5245 Status
= get_integrity_information(DeviceObject
->DeviceExtension
, IrpSp
->FileObject
, map_user_buffer(Irp
, NormalPagePriority
),
5246 IrpSp
->Parameters
.FileSystemControl
.OutputBufferLength
);
5249 case FSCTL_SET_INTEGRITY_INFORMATION
:
5250 Status
= set_integrity_information(IrpSp
->FileObject
, Irp
->AssociatedIrp
.SystemBuffer
, IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
);
5253 case FSCTL_DUPLICATE_EXTENTS_TO_FILE
:
5254 Status
= duplicate_extents(DeviceObject
->DeviceExtension
, IrpSp
->FileObject
, Irp
->AssociatedIrp
.SystemBuffer
,
5255 IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
, Irp
);
5258 case FSCTL_BTRFS_GET_FILE_IDS
:
5259 Status
= get_file_ids(IrpSp
->FileObject
, map_user_buffer(Irp
, NormalPagePriority
), IrpSp
->Parameters
.FileSystemControl
.OutputBufferLength
);
5262 case FSCTL_BTRFS_CREATE_SUBVOL
:
5263 Status
= create_subvol(DeviceObject
->DeviceExtension
, IrpSp
->FileObject
, Irp
->AssociatedIrp
.SystemBuffer
, IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
, Irp
);
5266 case FSCTL_BTRFS_CREATE_SNAPSHOT
:
5267 Status
= create_snapshot(DeviceObject
->DeviceExtension
, IrpSp
->FileObject
, Irp
->AssociatedIrp
.SystemBuffer
, IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
, Irp
);
5270 case FSCTL_BTRFS_GET_INODE_INFO
:
5271 Status
= get_inode_info(IrpSp
->FileObject
, map_user_buffer(Irp
, NormalPagePriority
), IrpSp
->Parameters
.FileSystemControl
.OutputBufferLength
);
5274 case FSCTL_BTRFS_SET_INODE_INFO
:
5275 Status
= set_inode_info(IrpSp
->FileObject
, Irp
->AssociatedIrp
.SystemBuffer
, IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
, Irp
);
5278 case FSCTL_BTRFS_GET_DEVICES
:
5279 Status
= get_devices(DeviceObject
->DeviceExtension
, map_user_buffer(Irp
, NormalPagePriority
), IrpSp
->Parameters
.FileSystemControl
.OutputBufferLength
);
5282 case FSCTL_BTRFS_GET_USAGE
:
5283 Status
= get_usage(DeviceObject
->DeviceExtension
, map_user_buffer(Irp
, NormalPagePriority
), IrpSp
->Parameters
.FileSystemControl
.OutputBufferLength
, Irp
);
5286 case FSCTL_BTRFS_START_BALANCE
:
5287 Status
= start_balance(DeviceObject
->DeviceExtension
, Irp
->AssociatedIrp
.SystemBuffer
, IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
, Irp
->RequestorMode
);
5290 case FSCTL_BTRFS_QUERY_BALANCE
:
5291 Status
= query_balance(DeviceObject
->DeviceExtension
, map_user_buffer(Irp
, NormalPagePriority
), IrpSp
->Parameters
.FileSystemControl
.OutputBufferLength
);
5294 case FSCTL_BTRFS_PAUSE_BALANCE
:
5295 Status
= pause_balance(DeviceObject
->DeviceExtension
, Irp
->RequestorMode
);
5298 case FSCTL_BTRFS_RESUME_BALANCE
:
5299 Status
= resume_balance(DeviceObject
->DeviceExtension
, Irp
->RequestorMode
);
5302 case FSCTL_BTRFS_STOP_BALANCE
:
5303 Status
= stop_balance(DeviceObject
->DeviceExtension
, Irp
->RequestorMode
);
5306 case FSCTL_BTRFS_ADD_DEVICE
:
5307 Status
= add_device(DeviceObject
->DeviceExtension
, Irp
, Irp
->RequestorMode
);
5310 case FSCTL_BTRFS_REMOVE_DEVICE
:
5311 Status
= remove_device(DeviceObject
->DeviceExtension
, Irp
->AssociatedIrp
.SystemBuffer
, IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
, Irp
->RequestorMode
);
5314 case FSCTL_BTRFS_GET_UUID
:
5315 Status
= query_uuid(DeviceObject
->DeviceExtension
, map_user_buffer(Irp
, NormalPagePriority
), IrpSp
->Parameters
.FileSystemControl
.OutputBufferLength
);
5318 case FSCTL_BTRFS_START_SCRUB
:
5319 Status
= start_scrub(DeviceObject
->DeviceExtension
, Irp
->RequestorMode
);
5322 case FSCTL_BTRFS_QUERY_SCRUB
:
5323 Status
= query_scrub(DeviceObject
->DeviceExtension
, Irp
->RequestorMode
, map_user_buffer(Irp
, NormalPagePriority
), IrpSp
->Parameters
.FileSystemControl
.OutputBufferLength
);
5326 case FSCTL_BTRFS_PAUSE_SCRUB
:
5327 Status
= pause_scrub(DeviceObject
->DeviceExtension
, Irp
->RequestorMode
);
5330 case FSCTL_BTRFS_RESUME_SCRUB
:
5331 Status
= resume_scrub(DeviceObject
->DeviceExtension
, Irp
->RequestorMode
);
5334 case FSCTL_BTRFS_STOP_SCRUB
:
5335 Status
= stop_scrub(DeviceObject
->DeviceExtension
, Irp
->RequestorMode
);
5338 case FSCTL_BTRFS_RESET_STATS
:
5339 Status
= reset_stats(DeviceObject
->DeviceExtension
, Irp
->AssociatedIrp
.SystemBuffer
, IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
, Irp
->RequestorMode
);
5342 case FSCTL_BTRFS_MKNOD
:
5343 Status
= mknod(DeviceObject
->DeviceExtension
, IrpSp
->FileObject
, Irp
->AssociatedIrp
.SystemBuffer
, IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
, Irp
);
5346 case FSCTL_BTRFS_RECEIVED_SUBVOL
:
5347 Status
= recvd_subvol(DeviceObject
->DeviceExtension
, IrpSp
->FileObject
, Irp
->AssociatedIrp
.SystemBuffer
,
5348 IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
, Irp
->RequestorMode
);
5351 case FSCTL_BTRFS_GET_XATTRS
:
5352 Status
= fsctl_get_xattrs(DeviceObject
->DeviceExtension
, IrpSp
->FileObject
, Irp
->UserBuffer
, IrpSp
->Parameters
.FileSystemControl
.OutputBufferLength
, Irp
->RequestorMode
);
5355 case FSCTL_BTRFS_SET_XATTR
:
5356 Status
= fsctl_set_xattr(DeviceObject
->DeviceExtension
, IrpSp
->FileObject
, Irp
->AssociatedIrp
.SystemBuffer
,
5357 IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
, Irp
);
5360 case FSCTL_BTRFS_RESERVE_SUBVOL
:
5361 Status
= reserve_subvol(DeviceObject
->DeviceExtension
, IrpSp
->FileObject
, Irp
);
5364 case FSCTL_BTRFS_FIND_SUBVOL
:
5365 Status
= find_subvol(DeviceObject
->DeviceExtension
, Irp
->AssociatedIrp
.SystemBuffer
, IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
,
5366 Irp
->UserBuffer
, IrpSp
->Parameters
.FileSystemControl
.OutputBufferLength
, Irp
);
5369 case FSCTL_BTRFS_SEND_SUBVOL
:
5370 Status
= send_subvol(DeviceObject
->DeviceExtension
, Irp
->AssociatedIrp
.SystemBuffer
, IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
,
5371 IrpSp
->FileObject
, Irp
);
5374 case FSCTL_BTRFS_READ_SEND_BUFFER
:
5375 Status
= read_send_buffer(DeviceObject
->DeviceExtension
, IrpSp
->FileObject
, map_user_buffer(Irp
, NormalPagePriority
), IrpSp
->Parameters
.FileSystemControl
.OutputBufferLength
,
5376 &Irp
->IoStatus
.Information
, Irp
->RequestorMode
);
5379 case FSCTL_BTRFS_RESIZE
:
5380 Status
= resize_device(DeviceObject
->DeviceExtension
, Irp
->AssociatedIrp
.SystemBuffer
,
5381 IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
, Irp
);
5385 WARN("unknown control code %x (DeviceType = %x, Access = %x, Function = %x, Method = %x)\n",
5386 IrpSp
->Parameters
.FileSystemControl
.FsControlCode
, (IrpSp
->Parameters
.FileSystemControl
.FsControlCode
& 0xff0000) >> 16,
5387 (IrpSp
->Parameters
.FileSystemControl
.FsControlCode
& 0xc000) >> 14, (IrpSp
->Parameters
.FileSystemControl
.FsControlCode
& 0x3ffc) >> 2,
5388 IrpSp
->Parameters
.FileSystemControl
.FsControlCode
& 0x3);
5389 Status
= STATUS_INVALID_DEVICE_REQUEST
;