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"
27 #ifndef FSCTL_CSV_CONTROL
28 #define FSCTL_CSV_CONTROL CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 181, METHOD_BUFFERED, FILE_ANY_ACCESS)
31 #ifndef FSCTL_QUERY_VOLUME_CONTAINER_STATE
32 #define FSCTL_QUERY_VOLUME_CONTAINER_STATE CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 228, METHOD_BUFFERED, FILE_ANY_ACCESS)
37 #define SEF_AVOID_PRIVILEGE_CHECK 0x08 // on MSDN but not in any header files(?)
39 #define SEF_SACL_AUTO_INHERIT 0x02
41 extern LIST_ENTRY VcbList
;
42 extern ERESOURCE global_loading_lock
;
43 extern PDRIVER_OBJECT drvobj
;
44 extern tFsRtlCheckLockForOplockRequest fFsRtlCheckLockForOplockRequest
;
45 extern tFsRtlAreThereCurrentOrInProgressFileLocks fFsRtlAreThereCurrentOrInProgressFileLocks
;
47 static void mark_subvol_dirty(device_extension
* Vcb
, root
* r
);
49 static NTSTATUS
get_file_ids(PFILE_OBJECT FileObject
, void* data
, ULONG length
) {
50 btrfs_get_file_ids
* bgfi
;
53 if (length
< sizeof(btrfs_get_file_ids
))
54 return STATUS_BUFFER_OVERFLOW
;
57 return STATUS_INVALID_PARAMETER
;
59 fcb
= FileObject
->FsContext
;
62 return STATUS_INVALID_PARAMETER
;
66 bgfi
->subvol
= fcb
->subvol
->id
;
67 bgfi
->inode
= fcb
->inode
;
68 bgfi
->top
= fcb
->Vcb
->root_fileref
->fcb
== fcb
? true : false;
70 return STATUS_SUCCESS
;
73 static void get_uuid(BTRFS_UUID
* uuid
) {
77 seed
= KeQueryPerformanceCounter(NULL
);
79 for (i
= 0; i
< 16; i
+=2) {
80 ULONG rand
= RtlRandomEx(&seed
.LowPart
);
82 uuid
->uuid
[i
] = (rand
& 0xff00) >> 8;
83 uuid
->uuid
[i
+1] = rand
& 0xff;
87 static NTSTATUS
snapshot_tree_copy(device_extension
* Vcb
, uint64_t addr
, root
* subvol
, uint64_t* newaddr
, PIRP Irp
, LIST_ENTRY
* rollback
) {
90 write_data_context wtc
;
96 buf
= ExAllocatePoolWithTag(NonPagedPool
, Vcb
->superblock
.node_size
, ALLOC_TAG
);
98 ERR("out of memory\n");
99 return STATUS_INSUFFICIENT_RESOURCES
;
102 wtc
.parity1
= wtc
.parity2
= wtc
.scratch
= NULL
;
103 wtc
.mdl
= wtc
.parity1_mdl
= wtc
.parity2_mdl
= NULL
;
105 Status
= read_data(Vcb
, addr
, Vcb
->superblock
.node_size
, NULL
, true, buf
, NULL
, NULL
, Irp
, 0, false, NormalPagePriority
);
106 if (!NT_SUCCESS(Status
)) {
107 ERR("read_data returned %08lx\n", Status
);
111 th
= (tree_header
*)buf
;
113 RtlZeroMemory(&t
, sizeof(tree
));
115 t
.header
.level
= th
->level
;
116 t
.header
.tree_id
= t
.root
->id
;
118 Status
= get_tree_new_address(Vcb
, &t
, Irp
, rollback
);
119 if (!NT_SUCCESS(Status
)) {
120 ERR("get_tree_new_address returned %08lx\n", Status
);
124 if (!t
.has_new_address
) {
125 ERR("tree new address not set\n");
126 Status
= STATUS_INTERNAL_ERROR
;
130 c
= get_chunk_from_address(Vcb
, t
.new_address
);
133 c
->used
+= Vcb
->superblock
.node_size
;
135 ERR("could not find chunk for address %I64x\n", t
.new_address
);
136 Status
= STATUS_INTERNAL_ERROR
;
140 th
->address
= t
.new_address
;
141 th
->tree_id
= subvol
->id
;
142 th
->generation
= Vcb
->superblock
.generation
;
143 th
->fs_uuid
= Vcb
->superblock
.metadata_uuid
;
145 if (th
->level
== 0) {
147 leaf_node
* ln
= (leaf_node
*)&th
[1];
149 for (i
= 0; i
< th
->num_items
; i
++) {
150 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
)) {
151 EXTENT_DATA
* ed
= (EXTENT_DATA
*)(((uint8_t*)&th
[1]) + ln
[i
].offset
);
153 if ((ed
->type
== EXTENT_TYPE_REGULAR
|| ed
->type
== EXTENT_TYPE_PREALLOC
) && ln
[i
].size
>= sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
)) {
154 EXTENT_DATA2
* ed2
= (EXTENT_DATA2
*)&ed
->data
[0];
156 if (ed2
->size
!= 0) { // not sparse
157 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
);
159 if (!NT_SUCCESS(Status
)) {
160 ERR("increase_extent_refcount_data returned %08lx\n", Status
);
169 internal_node
* in
= (internal_node
*)&th
[1];
171 for (i
= 0; i
< th
->num_items
; i
++) {
174 tbr
.offset
= subvol
->id
;
176 Status
= increase_extent_refcount(Vcb
, in
[i
].address
, Vcb
->superblock
.node_size
, TYPE_TREE_BLOCK_REF
, &tbr
, NULL
, th
->level
- 1, Irp
);
177 if (!NT_SUCCESS(Status
)) {
178 ERR("increase_extent_refcount returned %08lx\n", Status
);
184 calc_tree_checksum(Vcb
, th
);
186 KeInitializeEvent(&wtc
.Event
, NotificationEvent
, false);
187 InitializeListHead(&wtc
.stripes
);
188 wtc
.stripes_left
= 0;
190 Status
= write_data(Vcb
, t
.new_address
, buf
, Vcb
->superblock
.node_size
, &wtc
, NULL
, NULL
, false, 0, NormalPagePriority
);
191 if (!NT_SUCCESS(Status
)) {
192 ERR("write_data returned %08lx\n", Status
);
196 if (wtc
.stripes
.Flink
!= &wtc
.stripes
) {
197 bool need_wait
= false;
199 // launch writes and wait
200 le
= wtc
.stripes
.Flink
;
201 while (le
!= &wtc
.stripes
) {
202 write_data_stripe
* stripe
= CONTAINING_RECORD(le
, write_data_stripe
, list_entry
);
204 if (stripe
->status
!= WriteDataStatus_Ignore
) {
206 IoCallDriver(stripe
->device
->devobj
, stripe
->Irp
);
213 KeWaitForSingleObject(&wtc
.Event
, Executive
, KernelMode
, false, NULL
);
215 le
= wtc
.stripes
.Flink
;
216 while (le
!= &wtc
.stripes
) {
217 write_data_stripe
* stripe
= CONTAINING_RECORD(le
, write_data_stripe
, list_entry
);
219 if (stripe
->status
!= WriteDataStatus_Ignore
&& !NT_SUCCESS(stripe
->iosb
.Status
)) {
220 Status
= stripe
->iosb
.Status
;
221 log_device_error(Vcb
, stripe
->device
, BTRFS_DEV_STAT_WRITE_ERRORS
);
228 free_write_data_stripes(&wtc
);
232 if (NT_SUCCESS(Status
))
233 *newaddr
= t
.new_address
;
243 void flush_subvol_fcbs(root
* subvol
) {
244 LIST_ENTRY
* le
= subvol
->fcbs
.Flink
;
246 if (IsListEmpty(&subvol
->fcbs
))
249 while (le
!= &subvol
->fcbs
) {
250 struct _fcb
* fcb
= CONTAINING_RECORD(le
, struct _fcb
, list_entry
);
251 IO_STATUS_BLOCK iosb
;
253 if (fcb
->type
!= BTRFS_TYPE_DIRECTORY
&& !fcb
->deleted
)
254 CcFlushCache(&fcb
->nonpaged
->segment_object
, NULL
, 0, &iosb
);
260 static NTSTATUS
do_create_snapshot(device_extension
* Vcb
, PFILE_OBJECT parent
, fcb
* subvol_fcb
, PANSI_STRING utf8
, PUNICODE_STRING name
, bool readonly
, PIRP Irp
) {
264 root
*r
, *subvol
= subvol_fcb
->subvol
;
267 uint64_t address
, *root_num
;
270 fcb
* fcb
= parent
->FsContext
;
271 ccb
* ccb
= parent
->FsContext2
;
273 file_ref
*fileref
, *fr
;
274 dir_child
* dc
= NULL
;
277 ERR("error - ccb was NULL\n");
278 return STATUS_INTERNAL_ERROR
;
281 if (!(ccb
->access
& FILE_ADD_SUBDIRECTORY
)) {
282 WARN("insufficient privileges\n");
283 return STATUS_ACCESS_DENIED
;
286 fileref
= ccb
->fileref
;
288 if (fileref
->fcb
== Vcb
->dummy_fcb
)
289 return STATUS_ACCESS_DENIED
;
291 // flush open files on this subvol
293 flush_subvol_fcbs(subvol
);
298 Status
= do_write(Vcb
, Irp
);
300 Status
= STATUS_SUCCESS
;
304 if (!NT_SUCCESS(Status
)) {
305 ERR("do_write returned %08lx\n", Status
);
309 InitializeListHead(&rollback
);
313 id
= InterlockedIncrement64(&Vcb
->root_root
->lastinode
);
314 Status
= create_root(Vcb
, id
, &r
, true, Vcb
->superblock
.generation
, Irp
);
316 if (!NT_SUCCESS(Status
)) {
317 ERR("create_root returned %08lx\n", Status
);
321 r
->lastinode
= subvol
->lastinode
;
323 if (!Vcb
->uuid_root
) {
326 TRACE("uuid root doesn't exist, creating it\n");
328 Status
= create_root(Vcb
, BTRFS_ROOT_UUID
, &uuid_root
, false, 0, Irp
);
330 if (!NT_SUCCESS(Status
)) {
331 ERR("create_root returned %08lx\n", Status
);
335 Vcb
->uuid_root
= uuid_root
;
338 root_num
= ExAllocatePoolWithTag(PagedPool
, sizeof(uint64_t), ALLOC_TAG
);
340 ERR("out of memory\n");
341 Status
= STATUS_INSUFFICIENT_RESOURCES
;
348 get_uuid(&r
->root_item
.uuid
);
350 RtlCopyMemory(&searchkey
.obj_id
, &r
->root_item
.uuid
, sizeof(uint64_t));
351 searchkey
.obj_type
= TYPE_SUBVOL_UUID
;
352 RtlCopyMemory(&searchkey
.offset
, &r
->root_item
.uuid
.uuid
[sizeof(uint64_t)], sizeof(uint64_t));
354 Status
= find_item(Vcb
, Vcb
->uuid_root
, &tp
, &searchkey
, false, Irp
);
355 } while (NT_SUCCESS(Status
) && !keycmp(searchkey
, tp
.item
->key
));
359 Status
= insert_tree_item(Vcb
, Vcb
->uuid_root
, searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
, root_num
, sizeof(uint64_t), NULL
, Irp
);
360 if (!NT_SUCCESS(Status
)) {
361 ERR("insert_tree_item returned %08lx\n", Status
);
362 ExFreePool(root_num
);
366 searchkey
.obj_id
= r
->id
;
367 searchkey
.obj_type
= TYPE_ROOT_ITEM
;
368 searchkey
.offset
= 0xffffffffffffffff;
370 Status
= find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, false, Irp
);
371 if (!NT_SUCCESS(Status
)) {
372 ERR("error - find_item returned %08lx\n", Status
);
376 Status
= snapshot_tree_copy(Vcb
, subvol
->root_item
.block_number
, r
, &address
, Irp
, &rollback
);
377 if (!NT_SUCCESS(Status
)) {
378 ERR("snapshot_tree_copy returned %08lx\n", Status
);
382 KeQuerySystemTime(&time
);
383 win_time_to_unix(time
, &now
);
385 r
->root_item
.inode
.generation
= 1;
386 r
->root_item
.inode
.st_size
= 3;
387 r
->root_item
.inode
.st_blocks
= subvol
->root_item
.inode
.st_blocks
;
388 r
->root_item
.inode
.st_nlink
= 1;
389 r
->root_item
.inode
.st_mode
= __S_IFDIR
| S_IRUSR
| S_IWUSR
| S_IXUSR
| S_IRGRP
| S_IXGRP
| S_IROTH
| S_IXOTH
; // 40755
390 r
->root_item
.inode
.flags
= 0x80000000; // FIXME - find out what these mean
391 r
->root_item
.inode
.flags_ro
= 0xffffffff; // 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 %I64x\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
, false, fcb
, &fr
->fcb
, PagedPool
, Irp
);
436 if (!NT_SUCCESS(Status
)) {
437 ERR("open_fcb returned %08lx\n", Status
);
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 %08lx\n", Status
);
451 ExAcquireResourceExclusiveLite(&fileref
->fcb
->nonpaged
->dir_children_lock
, true);
452 InsertTailList(&fileref
->children
, &fr
->list_entry
);
453 ExReleaseResourceLite(&fileref
->fcb
->nonpaged
->dir_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
;
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 %08lx\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 Status
= check_file_name_valid(&nameus
, posix
, false);
602 if (!NT_SUCCESS(Status
))
607 Status
= utf16_to_utf8(NULL
, 0, &len
, nameus
.Buffer
, nameus
.Length
);
608 if (!NT_SUCCESS(Status
)) {
609 ERR("utf16_to_utf8 failed with error %08lx\n", Status
);
614 ERR("utf16_to_utf8 returned a length of 0\n");
615 return STATUS_INTERNAL_ERROR
;
619 ERR("len was too long\n");
620 return STATUS_INVALID_PARAMETER
;
623 utf8
.MaximumLength
= utf8
.Length
= (USHORT
)len
;
624 utf8
.Buffer
= ExAllocatePoolWithTag(PagedPool
, utf8
.Length
, ALLOC_TAG
);
627 ERR("out of memory\n");
628 return STATUS_INSUFFICIENT_RESOURCES
;
631 Status
= utf16_to_utf8(utf8
.Buffer
, len
, &len
, nameus
.Buffer
, nameus
.Length
);
632 if (!NT_SUCCESS(Status
)) {
633 ERR("utf16_to_utf8 failed with error %08lx\n", Status
);
637 ExAcquireResourceExclusiveLite(&Vcb
->tree_lock
, true);
639 // no need for fcb_lock as we have tree_lock exclusively
640 Status
= open_fileref(fcb
->Vcb
, &fr2
, &nameus
, fileref
, false, NULL
, NULL
, PagedPool
, ccb
->case_sensitive
|| posix
, Irp
);
642 if (NT_SUCCESS(Status
)) {
644 WARN("file already exists\n");
646 Status
= STATUS_OBJECT_NAME_COLLISION
;
650 } else if (!NT_SUCCESS(Status
) && Status
!= STATUS_OBJECT_NAME_NOT_FOUND
) {
651 ERR("open_fileref returned %08lx\n", Status
);
655 Status
= ObReferenceObjectByHandle(subvolh
, 0, *IoFileObjectType
, Irp
->RequestorMode
, (void**)&subvol_obj
, NULL
);
656 if (!NT_SUCCESS(Status
)) {
657 ERR("ObReferenceObjectByHandle returned %08lx\n", Status
);
661 if (subvol_obj
->DeviceObject
!= FileObject
->DeviceObject
) {
662 Status
= STATUS_INVALID_PARAMETER
;
666 subvol_fcb
= subvol_obj
->FsContext
;
668 Status
= STATUS_INVALID_PARAMETER
;
672 if (subvol_fcb
->inode
!= subvol_fcb
->subvol
->root_item
.objid
) {
673 WARN("handle inode was %I64x, expected %I64x\n", subvol_fcb
->inode
, subvol_fcb
->subvol
->root_item
.objid
);
674 Status
= STATUS_INVALID_PARAMETER
;
678 ccb
= subvol_obj
->FsContext2
;
681 Status
= STATUS_INVALID_PARAMETER
;
685 if (!(ccb
->access
& FILE_TRAVERSE
)) {
686 WARN("insufficient privileges\n");
687 Status
= STATUS_ACCESS_DENIED
;
691 if (fcb
== Vcb
->dummy_fcb
) {
692 Status
= STATUS_ACCESS_DENIED
;
696 // clear unique flag on extents of open files in subvol
697 if (!IsListEmpty(&subvol_fcb
->subvol
->fcbs
)) {
698 LIST_ENTRY
* le
= subvol_fcb
->subvol
->fcbs
.Flink
;
700 while (le
!= &subvol_fcb
->subvol
->fcbs
) {
701 struct _fcb
* openfcb
= CONTAINING_RECORD(le
, struct _fcb
, list_entry
);
704 le2
= openfcb
->extents
.Flink
;
706 while (le2
!= &openfcb
->extents
) {
707 extent
* ext
= CONTAINING_RECORD(le2
, extent
, list_entry
);
718 Status
= do_create_snapshot(Vcb
, FileObject
, subvol_fcb
, &utf8
, &nameus
, readonly
, Irp
);
720 if (NT_SUCCESS(Status
)) {
723 Status
= open_fileref(Vcb
, &fr
, &nameus
, fileref
, false, NULL
, NULL
, PagedPool
, false, Irp
);
725 if (!NT_SUCCESS(Status
)) {
726 ERR("open_fileref returned %08lx\n", Status
);
727 Status
= STATUS_SUCCESS
;
729 send_notification_fileref(fr
, FILE_NOTIFY_CHANGE_DIR_NAME
, FILE_ACTION_ADDED
, NULL
);
735 ObDereferenceObject(subvol_obj
);
738 ExReleaseResourceLite(&Vcb
->tree_lock
);
741 ExFreePool(utf8
.Buffer
);
746 static NTSTATUS
create_subvol(device_extension
* Vcb
, PFILE_OBJECT FileObject
, void* data
, ULONG datalen
, PIRP Irp
) {
747 btrfs_create_subvol
* bcs
;
748 fcb
*fcb
, *rootfcb
= NULL
;
758 UNICODE_STRING nameus
;
763 SECURITY_SUBJECT_CONTEXT subjcont
;
767 file_ref
*fr
= NULL
, *fr2
;
768 dir_child
* dc
= NULL
;
770 fcb
= FileObject
->FsContext
;
772 ERR("error - fcb was NULL\n");
773 return STATUS_INTERNAL_ERROR
;
776 ccb
= FileObject
->FsContext2
;
778 ERR("error - ccb was NULL\n");
779 return STATUS_INTERNAL_ERROR
;
782 fileref
= ccb
->fileref
;
784 if (fcb
->type
!= BTRFS_TYPE_DIRECTORY
) {
785 ERR("parent FCB was not a directory\n");
786 return STATUS_NOT_A_DIRECTORY
;
790 ERR("fileref was NULL\n");
791 return STATUS_INVALID_PARAMETER
;
794 if (fileref
->deleted
|| fcb
->deleted
) {
795 ERR("parent has been deleted\n");
796 return STATUS_FILE_DELETED
;
799 if (!(ccb
->access
& FILE_ADD_SUBDIRECTORY
)) {
800 WARN("insufficient privileges\n");
801 return STATUS_ACCESS_DENIED
;
805 return STATUS_MEDIA_WRITE_PROTECTED
;
807 if (is_subvol_readonly(fcb
->subvol
, Irp
))
808 return STATUS_ACCESS_DENIED
;
810 if (fcb
== Vcb
->dummy_fcb
)
811 return STATUS_ACCESS_DENIED
;
813 if (!data
|| datalen
< sizeof(btrfs_create_subvol
))
814 return STATUS_INVALID_PARAMETER
;
816 bcs
= (btrfs_create_subvol
*)data
;
818 if (offsetof(btrfs_create_subvol
, name
[0]) + bcs
->namelen
> datalen
)
819 return STATUS_INVALID_PARAMETER
;
821 nameus
.Length
= nameus
.MaximumLength
= bcs
->namelen
;
822 nameus
.Buffer
= bcs
->name
;
824 Status
= check_file_name_valid(&nameus
, bcs
->posix
, false);
825 if (!NT_SUCCESS(Status
))
830 Status
= utf16_to_utf8(NULL
, 0, &len
, nameus
.Buffer
, nameus
.Length
);
831 if (!NT_SUCCESS(Status
)) {
832 ERR("utf16_to_utf8 failed with error %08lx\n", Status
);
837 ERR("utf16_to_utf8 returned a length of 0\n");
838 return STATUS_INTERNAL_ERROR
;
842 ERR("len was too long\n");
843 return STATUS_INVALID_PARAMETER
;
846 utf8
.MaximumLength
= utf8
.Length
= (USHORT
)len
;
847 utf8
.Buffer
= ExAllocatePoolWithTag(PagedPool
, utf8
.Length
, ALLOC_TAG
);
850 ERR("out of memory\n");
851 return STATUS_INSUFFICIENT_RESOURCES
;
854 Status
= utf16_to_utf8(utf8
.Buffer
, len
, &len
, nameus
.Buffer
, nameus
.Length
);
855 if (!NT_SUCCESS(Status
)) {
856 ERR("utf16_to_utf8 failed with error %08lx\n", Status
);
860 ExAcquireResourceExclusiveLite(&Vcb
->tree_lock
, true);
862 KeQuerySystemTime(&time
);
863 win_time_to_unix(time
, &now
);
865 // no need for fcb_lock as we have tree_lock exclusively
866 Status
= open_fileref(fcb
->Vcb
, &fr2
, &nameus
, fileref
, false, NULL
, NULL
, PagedPool
, ccb
->case_sensitive
|| bcs
->posix
, Irp
);
868 if (NT_SUCCESS(Status
)) {
870 WARN("file already exists\n");
872 Status
= STATUS_OBJECT_NAME_COLLISION
;
876 } else if (!NT_SUCCESS(Status
) && Status
!= STATUS_OBJECT_NAME_NOT_FOUND
) {
877 ERR("open_fileref returned %08lx\n", Status
);
881 id
= InterlockedIncrement64(&Vcb
->root_root
->lastinode
);
882 Status
= create_root(Vcb
, id
, &r
, false, 0, Irp
);
884 if (!NT_SUCCESS(Status
)) {
885 ERR("create_root returned %08lx\n", Status
);
889 TRACE("created root %I64x\n", id
);
891 if (!Vcb
->uuid_root
) {
894 TRACE("uuid root doesn't exist, creating it\n");
896 Status
= create_root(Vcb
, BTRFS_ROOT_UUID
, &uuid_root
, false, 0, Irp
);
898 if (!NT_SUCCESS(Status
)) {
899 ERR("create_root returned %08lx\n", Status
);
903 Vcb
->uuid_root
= uuid_root
;
906 root_num
= ExAllocatePoolWithTag(PagedPool
, sizeof(uint64_t), ALLOC_TAG
);
908 ERR("out of memory\n");
909 Status
= STATUS_INSUFFICIENT_RESOURCES
;
916 get_uuid(&r
->root_item
.uuid
);
918 RtlCopyMemory(&searchkey
.obj_id
, &r
->root_item
.uuid
, sizeof(uint64_t));
919 searchkey
.obj_type
= TYPE_SUBVOL_UUID
;
920 RtlCopyMemory(&searchkey
.offset
, &r
->root_item
.uuid
.uuid
[sizeof(uint64_t)], sizeof(uint64_t));
922 Status
= find_item(Vcb
, Vcb
->uuid_root
, &tp
, &searchkey
, false, Irp
);
923 } while (NT_SUCCESS(Status
) && !keycmp(searchkey
, tp
.item
->key
));
927 Status
= insert_tree_item(Vcb
, Vcb
->uuid_root
, searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
, root_num
, sizeof(uint64_t), NULL
, Irp
);
928 if (!NT_SUCCESS(Status
)) {
929 ERR("insert_tree_item returned %08lx\n", Status
);
930 ExFreePool(root_num
);
934 r
->root_item
.inode
.generation
= 1;
935 r
->root_item
.inode
.st_size
= 3;
936 r
->root_item
.inode
.st_blocks
= Vcb
->superblock
.node_size
;
937 r
->root_item
.inode
.st_nlink
= 1;
938 r
->root_item
.inode
.st_mode
= __S_IFDIR
| S_IRUSR
| S_IWUSR
| S_IXUSR
| S_IRGRP
| S_IXGRP
| S_IROTH
| S_IXOTH
; // 40755
939 r
->root_item
.inode
.flags
= 0x80000000; // FIXME - find out what these mean
940 r
->root_item
.inode
.flags_ro
= 0xffffffff; // FIXME - find out what these mean
943 r
->root_item
.flags
|= BTRFS_SUBVOL_READONLY
;
945 r
->root_item
.objid
= SUBVOL_ROOT_INODE
;
946 r
->root_item
.bytes_used
= Vcb
->superblock
.node_size
;
947 r
->root_item
.ctransid
= Vcb
->superblock
.generation
;
948 r
->root_item
.otransid
= Vcb
->superblock
.generation
;
949 r
->root_item
.ctime
= now
;
950 r
->root_item
.otime
= now
;
952 // add .. inode to new subvol
954 rootfcb
= create_fcb(Vcb
, PagedPool
);
956 ERR("out of memory\n");
957 Status
= STATUS_INSUFFICIENT_RESOURCES
;
964 rootfcb
->type
= BTRFS_TYPE_DIRECTORY
;
965 rootfcb
->inode
= SUBVOL_ROOT_INODE
;
966 rootfcb
->hash
= calc_crc32c(0xffffffff, (uint8_t*)&rootfcb
->inode
, sizeof(uint64_t)); // FIXME - we can hardcode this
968 rootfcb
->inode_item
.generation
= Vcb
->superblock
.generation
;
969 rootfcb
->inode_item
.transid
= Vcb
->superblock
.generation
;
970 rootfcb
->inode_item
.st_nlink
= 1;
971 rootfcb
->inode_item
.st_mode
= __S_IFDIR
| inherit_mode(fileref
->fcb
, true);
972 rootfcb
->inode_item
.st_atime
= rootfcb
->inode_item
.st_ctime
= rootfcb
->inode_item
.st_mtime
= rootfcb
->inode_item
.otime
= now
;
973 rootfcb
->inode_item
.st_gid
= GID_NOBODY
;
975 rootfcb
->atts
= get_file_attributes(Vcb
, rootfcb
->subvol
, rootfcb
->inode
, rootfcb
->type
, false, true, Irp
);
977 if (r
->root_item
.flags
& BTRFS_SUBVOL_READONLY
)
978 rootfcb
->atts
|= FILE_ATTRIBUTE_READONLY
;
980 SeCaptureSubjectContext(&subjcont
);
982 Status
= SeAssignSecurity(fcb
->sd
, NULL
, (void**)&rootfcb
->sd
, true, &subjcont
, IoGetFileObjectGenericMapping(), PagedPool
);
984 if (!NT_SUCCESS(Status
)) {
985 ERR("SeAssignSecurity returned %08lx\n", Status
);
990 ERR("SeAssignSecurity returned NULL security descriptor\n");
991 Status
= STATUS_INTERNAL_ERROR
;
995 Status
= RtlGetOwnerSecurityDescriptor(rootfcb
->sd
, &owner
, &defaulted
);
996 if (!NT_SUCCESS(Status
)) {
997 ERR("RtlGetOwnerSecurityDescriptor returned %08lx\n", Status
);
998 rootfcb
->inode_item
.st_uid
= UID_NOBODY
;
999 rootfcb
->sd_dirty
= true;
1001 rootfcb
->inode_item
.st_uid
= sid_to_uid(owner
);
1002 rootfcb
->sd_dirty
= rootfcb
->inode_item
.st_uid
== UID_NOBODY
;
1005 find_gid(rootfcb
, fileref
->fcb
, &subjcont
);
1007 rootfcb
->inode_item_changed
= true;
1009 acquire_fcb_lock_exclusive(Vcb
);
1010 add_fcb_to_subvol(rootfcb
);
1011 InsertTailList(&Vcb
->all_fcbs
, &rootfcb
->list_entry_all
);
1013 release_fcb_lock(Vcb
);
1015 rootfcb
->Header
.IsFastIoPossible
= fast_io_possible(rootfcb
);
1016 rootfcb
->Header
.AllocationSize
.QuadPart
= 0;
1017 rootfcb
->Header
.FileSize
.QuadPart
= 0;
1018 rootfcb
->Header
.ValidDataLength
.QuadPart
= 0;
1020 rootfcb
->created
= true;
1022 if (fileref
->fcb
->inode_item
.flags
& BTRFS_INODE_COMPRESS
)
1023 rootfcb
->inode_item
.flags
|= BTRFS_INODE_COMPRESS
;
1025 rootfcb
->prop_compression
= fileref
->fcb
->prop_compression
;
1026 rootfcb
->prop_compression_changed
= rootfcb
->prop_compression
!= PropCompression_None
;
1028 r
->lastinode
= rootfcb
->inode
;
1032 irsize
= (uint16_t)(offsetof(INODE_REF
, name
[0]) + sizeof(DOTDOT
) - 1);
1033 ir
= ExAllocatePoolWithTag(PagedPool
, irsize
, ALLOC_TAG
);
1035 ERR("out of memory\n");
1036 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1041 ir
->n
= sizeof(DOTDOT
) - 1;
1042 RtlCopyMemory(ir
->name
, DOTDOT
, ir
->n
);
1044 Status
= insert_tree_item(Vcb
, r
, r
->root_item
.objid
, TYPE_INODE_REF
, r
->root_item
.objid
, ir
, irsize
, NULL
, Irp
);
1045 if (!NT_SUCCESS(Status
)) {
1046 ERR("insert_tree_item returned %08lx\n", Status
);
1051 // create fileref for entry in other subvolume
1053 fr
= create_fileref(Vcb
);
1055 ERR("out of memory\n");
1059 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1065 mark_fcb_dirty(rootfcb
);
1067 fr
->parent
= fileref
;
1069 Status
= add_dir_child(fileref
->fcb
, r
->id
, true, &utf8
, &nameus
, BTRFS_TYPE_DIRECTORY
, &dc
);
1070 if (!NT_SUCCESS(Status
))
1071 WARN("add_dir_child returned %08lx\n", Status
);
1076 fr
->fcb
->hash_ptrs
= ExAllocatePoolWithTag(PagedPool
, sizeof(LIST_ENTRY
*) * 256, ALLOC_TAG
);
1077 if (!fr
->fcb
->hash_ptrs
) {
1078 ERR("out of memory\n");
1080 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1084 RtlZeroMemory(fr
->fcb
->hash_ptrs
, sizeof(LIST_ENTRY
*) * 256);
1086 fr
->fcb
->hash_ptrs_uc
= ExAllocatePoolWithTag(PagedPool
, sizeof(LIST_ENTRY
*) * 256, ALLOC_TAG
);
1087 if (!fr
->fcb
->hash_ptrs_uc
) {
1088 ERR("out of memory\n");
1090 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1094 RtlZeroMemory(fr
->fcb
->hash_ptrs_uc
, sizeof(LIST_ENTRY
*) * 256);
1096 ExAcquireResourceExclusiveLite(&fileref
->fcb
->nonpaged
->dir_children_lock
, true);
1097 InsertTailList(&fileref
->children
, &fr
->list_entry
);
1098 ExReleaseResourceLite(&fileref
->fcb
->nonpaged
->dir_children_lock
);
1100 increase_fileref_refcount(fileref
);
1102 if (fr
->fcb
->type
== BTRFS_TYPE_DIRECTORY
)
1103 fr
->fcb
->fileref
= fr
;
1106 mark_fileref_dirty(fr
);
1108 // change fcb->subvol's ROOT_ITEM
1110 fcb
->subvol
->root_item
.ctransid
= Vcb
->superblock
.generation
;
1111 fcb
->subvol
->root_item
.ctime
= now
;
1113 // change fcb's INODE_ITEM
1115 fcb
->inode_item
.transid
= Vcb
->superblock
.generation
;
1116 fcb
->inode_item
.st_size
+= utf8
.Length
* 2;
1117 fcb
->inode_item
.sequence
++;
1119 if (!ccb
->user_set_change_time
)
1120 fcb
->inode_item
.st_ctime
= now
;
1122 if (!ccb
->user_set_write_time
)
1123 fcb
->inode_item
.st_mtime
= now
;
1125 fcb
->inode_item_changed
= true;
1126 mark_fcb_dirty(fcb
);
1128 fr
->fcb
->subvol
->parent
= fcb
->subvol
->id
;
1130 Status
= STATUS_SUCCESS
;
1133 if (!NT_SUCCESS(Status
)) {
1136 mark_fileref_dirty(fr
);
1137 } else if (rootfcb
) {
1138 rootfcb
->deleted
= true;
1139 mark_fcb_dirty(rootfcb
);
1143 RemoveEntryList(&r
->list_entry
);
1144 InsertTailList(&Vcb
->drop_roots
, &r
->list_entry
);
1148 ExReleaseResourceLite(&Vcb
->tree_lock
);
1150 if (NT_SUCCESS(Status
)) {
1151 send_notification_fileref(fr
, FILE_NOTIFY_CHANGE_DIR_NAME
, FILE_ACTION_ADDED
, NULL
);
1152 send_notification_fileref(fr
->parent
, FILE_NOTIFY_CHANGE_LAST_WRITE
, FILE_ACTION_MODIFIED
, NULL
);
1162 static NTSTATUS
get_inode_info(PFILE_OBJECT FileObject
, void* data
, ULONG length
) {
1163 btrfs_inode_info
* bii
= data
;
1168 if (length
< offsetof(btrfs_inode_info
, disk_size_zstd
))
1169 return STATUS_BUFFER_OVERFLOW
;
1172 return STATUS_INVALID_PARAMETER
;
1174 fcb
= FileObject
->FsContext
;
1177 return STATUS_INVALID_PARAMETER
;
1179 ccb
= FileObject
->FsContext2
;
1182 return STATUS_INVALID_PARAMETER
;
1184 if (!(ccb
->access
& FILE_READ_ATTRIBUTES
)) {
1185 WARN("insufficient privileges\n");
1186 return STATUS_ACCESS_DENIED
;
1190 fcb
= ccb
->fileref
->parent
->fcb
;
1192 old_style
= length
< offsetof(btrfs_inode_info
, sparse_size
) + sizeof(((btrfs_inode_info
*)NULL
)->sparse_size
);
1194 ExAcquireResourceSharedLite(fcb
->Header
.Resource
, true);
1196 bii
->subvol
= fcb
->subvol
->id
;
1197 bii
->inode
= fcb
->inode
;
1198 bii
->top
= fcb
->Vcb
->root_fileref
->fcb
== fcb
? true : false;
1199 bii
->type
= fcb
->type
;
1200 bii
->st_uid
= fcb
->inode_item
.st_uid
;
1201 bii
->st_gid
= fcb
->inode_item
.st_gid
;
1202 bii
->st_mode
= fcb
->inode_item
.st_mode
;
1204 if (fcb
->inode_item
.st_rdev
== 0)
1207 bii
->st_rdev
= makedev((fcb
->inode_item
.st_rdev
& 0xFFFFFFFFFFF) >> 20, fcb
->inode_item
.st_rdev
& 0xFFFFF);
1209 bii
->flags
= fcb
->inode_item
.flags
;
1211 bii
->inline_length
= 0;
1212 bii
->disk_size_uncompressed
= 0;
1213 bii
->disk_size_zlib
= 0;
1214 bii
->disk_size_lzo
= 0;
1217 bii
->disk_size_zstd
= 0;
1218 bii
->sparse_size
= 0;
1221 if (fcb
->type
!= BTRFS_TYPE_DIRECTORY
) {
1222 uint64_t last_end
= 0;
1224 bool extents_inline
= false;
1226 le
= fcb
->extents
.Flink
;
1227 while (le
!= &fcb
->extents
) {
1228 extent
* ext
= CONTAINING_RECORD(le
, extent
, list_entry
);
1231 if (!old_style
&& ext
->offset
> last_end
)
1232 bii
->sparse_size
+= ext
->offset
- last_end
;
1234 if (ext
->extent_data
.type
== EXTENT_TYPE_INLINE
) {
1235 bii
->inline_length
+= ext
->datalen
- (uint16_t)offsetof(EXTENT_DATA
, data
[0]);
1236 last_end
= ext
->offset
+ ext
->extent_data
.decoded_size
;
1237 extents_inline
= true;
1239 EXTENT_DATA2
* ed2
= (EXTENT_DATA2
*)ext
->extent_data
.data
;
1241 // FIXME - compressed extents with a hole in them are counted more than once
1242 if (ed2
->size
!= 0) {
1243 switch (ext
->extent_data
.compression
) {
1244 case BTRFS_COMPRESSION_NONE
:
1245 bii
->disk_size_uncompressed
+= ed2
->num_bytes
;
1248 case BTRFS_COMPRESSION_ZLIB
:
1249 bii
->disk_size_zlib
+= ed2
->size
;
1252 case BTRFS_COMPRESSION_LZO
:
1253 bii
->disk_size_lzo
+= ed2
->size
;
1256 case BTRFS_COMPRESSION_ZSTD
:
1258 bii
->disk_size_zstd
+= ed2
->size
;
1263 last_end
= ext
->offset
+ ed2
->num_bytes
;
1270 if (!extents_inline
&& !old_style
&& sector_align(fcb
->inode_item
.st_size
, fcb
->Vcb
->superblock
.sector_size
) > last_end
)
1271 bii
->sparse_size
+= sector_align(fcb
->inode_item
.st_size
, fcb
->Vcb
->superblock
.sector_size
) - last_end
;
1273 if (length
>= offsetof(btrfs_inode_info
, num_extents
) + sizeof(((btrfs_inode_info
*)NULL
)->num_extents
)) {
1274 EXTENT_DATA2
* last_ed2
= NULL
;
1276 le
= fcb
->extents
.Flink
;
1278 bii
->num_extents
= 0;
1280 while (le
!= &fcb
->extents
) {
1281 extent
* ext
= CONTAINING_RECORD(le
, extent
, list_entry
);
1283 if (!ext
->ignore
&& ext
->extent_data
.type
!= EXTENT_TYPE_INLINE
) {
1284 EXTENT_DATA2
* ed2
= (EXTENT_DATA2
*)ext
->extent_data
.data
;
1286 if (ed2
->size
!= 0) {
1287 if (!last_ed2
|| ed2
->offset
!= last_ed2
->offset
+ last_ed2
->num_bytes
)
1300 switch (fcb
->prop_compression
) {
1301 case PropCompression_Zlib
:
1302 bii
->compression_type
= BTRFS_COMPRESSION_ZLIB
;
1305 case PropCompression_LZO
:
1306 bii
->compression_type
= BTRFS_COMPRESSION_LZO
;
1309 case PropCompression_ZSTD
:
1310 bii
->compression_type
= BTRFS_COMPRESSION_ZSTD
;
1314 bii
->compression_type
= BTRFS_COMPRESSION_ANY
;
1318 ExReleaseResourceLite(fcb
->Header
.Resource
);
1320 return STATUS_SUCCESS
;
1323 static NTSTATUS
set_inode_info(PFILE_OBJECT FileObject
, void* data
, ULONG length
, PIRP Irp
) {
1324 btrfs_set_inode_info
* bsii
= data
;
1329 if (length
< sizeof(btrfs_set_inode_info
))
1330 return STATUS_INVALID_PARAMETER
;
1333 return STATUS_INVALID_PARAMETER
;
1335 fcb
= FileObject
->FsContext
;
1338 return STATUS_INVALID_PARAMETER
;
1340 ccb
= FileObject
->FsContext2
;
1343 return STATUS_INVALID_PARAMETER
;
1345 if (bsii
->flags_changed
&& !(ccb
->access
& FILE_WRITE_ATTRIBUTES
)) {
1346 WARN("insufficient privileges\n");
1347 return STATUS_ACCESS_DENIED
;
1350 if ((bsii
->mode_changed
|| bsii
->uid_changed
|| bsii
->gid_changed
) && !(ccb
->access
& WRITE_DAC
)) {
1351 WARN("insufficient privileges\n");
1352 return STATUS_ACCESS_DENIED
;
1355 if (bsii
->compression_type_changed
&& bsii
->compression_type
> BTRFS_COMPRESSION_ZSTD
)
1356 return STATUS_INVALID_PARAMETER
;
1359 fcb
= ccb
->fileref
->parent
->fcb
;
1361 if (is_subvol_readonly(fcb
->subvol
, Irp
)) {
1362 WARN("trying to change inode on readonly subvolume\n");
1363 return STATUS_ACCESS_DENIED
;
1366 // nocow and compression are mutually exclusive
1367 if (bsii
->flags_changed
&& bsii
->flags
& BTRFS_INODE_NODATACOW
&& bsii
->flags
& BTRFS_INODE_COMPRESS
)
1368 return STATUS_INVALID_PARAMETER
;
1370 ExAcquireResourceExclusiveLite(fcb
->Header
.Resource
, true);
1372 if (bsii
->flags_changed
) {
1373 if (fcb
->type
!= BTRFS_TYPE_DIRECTORY
&& fcb
->inode_item
.st_size
> 0 &&
1374 (bsii
->flags
& BTRFS_INODE_NODATACOW
) != (fcb
->inode_item
.flags
& BTRFS_INODE_NODATACOW
)) {
1375 WARN("trying to change nocow flag on non-empty file\n");
1376 Status
= STATUS_INVALID_PARAMETER
;
1380 fcb
->inode_item
.flags
= bsii
->flags
;
1382 if (fcb
->inode_item
.flags
& BTRFS_INODE_NODATACOW
)
1383 fcb
->inode_item
.flags
|= BTRFS_INODE_NODATASUM
;
1385 fcb
->inode_item
.flags
&= ~(uint64_t)BTRFS_INODE_NODATASUM
;
1388 if (bsii
->mode_changed
) {
1389 uint32_t allowed
= S_IRUSR
| S_IWUSR
| S_IXUSR
| S_IRGRP
| S_IWGRP
| S_IXGRP
| S_IROTH
| S_IWOTH
| S_IXOTH
|
1392 if (ccb
->access
& WRITE_OWNER
)
1395 fcb
->inode_item
.st_mode
&= ~allowed
;
1396 fcb
->inode_item
.st_mode
|= bsii
->st_mode
& allowed
;
1399 if (bsii
->uid_changed
&& fcb
->inode_item
.st_uid
!= bsii
->st_uid
) {
1400 fcb
->inode_item
.st_uid
= bsii
->st_uid
;
1402 fcb
->sd_dirty
= true;
1403 fcb
->sd_deleted
= false;
1406 if (bsii
->gid_changed
)
1407 fcb
->inode_item
.st_gid
= bsii
->st_gid
;
1409 if (bsii
->compression_type_changed
) {
1410 switch (bsii
->compression_type
) {
1411 case BTRFS_COMPRESSION_ANY
:
1412 fcb
->prop_compression
= PropCompression_None
;
1415 case BTRFS_COMPRESSION_ZLIB
:
1416 fcb
->prop_compression
= PropCompression_Zlib
;
1419 case BTRFS_COMPRESSION_LZO
:
1420 fcb
->prop_compression
= PropCompression_LZO
;
1423 case BTRFS_COMPRESSION_ZSTD
:
1424 fcb
->prop_compression
= PropCompression_ZSTD
;
1428 fcb
->prop_compression_changed
= true;
1431 if (bsii
->flags_changed
|| bsii
->mode_changed
|| bsii
->uid_changed
|| bsii
->gid_changed
|| bsii
->compression_type_changed
) {
1432 fcb
->inode_item_changed
= true;
1433 mark_fcb_dirty(fcb
);
1436 Status
= STATUS_SUCCESS
;
1439 ExReleaseResourceLite(fcb
->Header
.Resource
);
1444 static NTSTATUS
get_devices(device_extension
* Vcb
, void* data
, ULONG length
) {
1445 btrfs_device
* dev
= NULL
;
1449 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, true);
1451 le
= Vcb
->devices
.Flink
;
1452 while (le
!= &Vcb
->devices
) {
1453 device
* dev2
= CONTAINING_RECORD(le
, device
, list_entry
);
1456 if (length
< sizeof(btrfs_device
) - sizeof(WCHAR
)) {
1457 Status
= STATUS_BUFFER_OVERFLOW
;
1464 dev
->next_entry
= sizeof(btrfs_device
) - sizeof(WCHAR
) + dev
->namelen
;
1465 dev
= (btrfs_device
*)((uint8_t*)dev
+ dev
->next_entry
);
1468 structlen
= length
- offsetof(btrfs_device
, namelen
);
1471 Status
= dev_ioctl(dev2
->devobj
, IOCTL_MOUNTDEV_QUERY_DEVICE_NAME
, NULL
, 0, &dev
->namelen
, structlen
, true, NULL
);
1472 if (!NT_SUCCESS(Status
))
1475 dev
->missing
= false;
1478 dev
->missing
= true;
1481 dev
->next_entry
= 0;
1482 dev
->dev_id
= dev2
->devitem
.dev_id
;
1483 dev
->readonly
= (Vcb
->readonly
|| dev2
->readonly
) ? true : false;
1484 dev
->device_number
= dev2
->disk_num
;
1485 dev
->partition_number
= dev2
->part_num
;
1486 dev
->size
= dev2
->devitem
.num_bytes
;
1489 GET_LENGTH_INFORMATION gli
;
1491 Status
= dev_ioctl(dev2
->devobj
, IOCTL_DISK_GET_LENGTH_INFO
, NULL
, 0, &gli
, sizeof(gli
), true, NULL
);
1492 if (!NT_SUCCESS(Status
))
1495 dev
->max_size
= gli
.Length
.QuadPart
;
1497 dev
->max_size
= dev
->size
;
1499 RtlCopyMemory(dev
->stats
, dev2
->stats
, sizeof(uint64_t) * 5);
1501 length
-= sizeof(btrfs_device
) - sizeof(WCHAR
) + dev
->namelen
;
1507 ExReleaseResourceLite(&Vcb
->tree_lock
);
1512 static NTSTATUS
get_usage(device_extension
* Vcb
, void* data
, ULONG length
, PIRP Irp
) {
1513 btrfs_usage
* usage
= (btrfs_usage
*)data
;
1514 btrfs_usage
* lastbue
= NULL
;
1518 if (length
< sizeof(btrfs_usage
))
1519 return STATUS_BUFFER_OVERFLOW
;
1521 if (!Vcb
->chunk_usage_found
) {
1522 ExAcquireResourceExclusiveLite(&Vcb
->tree_lock
, true);
1524 if (!Vcb
->chunk_usage_found
)
1525 Status
= find_chunk_usage(Vcb
, Irp
);
1527 Status
= STATUS_SUCCESS
;
1529 ExReleaseResourceLite(&Vcb
->tree_lock
);
1531 if (!NT_SUCCESS(Status
)) {
1532 ERR("find_chunk_usage returned %08lx\n", Status
);
1537 length
-= offsetof(btrfs_usage
, devices
);
1539 ExAcquireResourceSharedLite(&Vcb
->chunk_lock
, true);
1541 le
= Vcb
->chunks
.Flink
;
1542 while (le
!= &Vcb
->chunks
) {
1543 bool addnew
= false;
1545 chunk
* c
= CONTAINING_RECORD(le
, chunk
, list_entry
);
1547 if (!lastbue
) // first entry
1550 btrfs_usage
* bue
= usage
;
1555 if (bue
->type
== c
->chunk_item
->type
) {
1560 if (bue
->next_entry
== 0)
1563 bue
= (btrfs_usage
*)((uint8_t*)bue
+ bue
->next_entry
);
1575 if (length
< offsetof(btrfs_usage
, devices
)) {
1576 Status
= STATUS_BUFFER_OVERFLOW
;
1580 length
-= offsetof(btrfs_usage
, devices
);
1582 lastbue
->next_entry
= offsetof(btrfs_usage
, devices
) + (ULONG
)(lastbue
->num_devices
* sizeof(btrfs_usage_device
));
1584 bue
= (btrfs_usage
*)((uint8_t*)lastbue
+ lastbue
->next_entry
);
1587 bue
->next_entry
= 0;
1588 bue
->type
= c
->chunk_item
->type
;
1591 bue
->num_devices
= 0;
1593 if (c
->chunk_item
->type
& BLOCK_FLAG_RAID0
)
1594 factor
= c
->chunk_item
->num_stripes
;
1595 else if (c
->chunk_item
->type
& BLOCK_FLAG_RAID10
)
1596 factor
= c
->chunk_item
->num_stripes
/ c
->chunk_item
->sub_stripes
;
1597 else if (c
->chunk_item
->type
& BLOCK_FLAG_RAID5
)
1598 factor
= c
->chunk_item
->num_stripes
- 1;
1599 else if (c
->chunk_item
->type
& BLOCK_FLAG_RAID6
)
1600 factor
= c
->chunk_item
->num_stripes
- 2;
1605 while (le2
!= &Vcb
->chunks
) {
1606 chunk
* c2
= CONTAINING_RECORD(le2
, chunk
, list_entry
);
1608 if (c2
->chunk_item
->type
== c
->chunk_item
->type
) {
1610 CHUNK_ITEM_STRIPE
* cis
= (CHUNK_ITEM_STRIPE
*)&c2
->chunk_item
[1];
1611 uint64_t stripesize
;
1613 bue
->size
+= c2
->chunk_item
->size
;
1614 bue
->used
+= c2
->used
;
1616 stripesize
= c2
->chunk_item
->size
/ factor
;
1618 for (i
= 0; i
< c2
->chunk_item
->num_stripes
; i
++) {
1622 for (j
= 0; j
< bue
->num_devices
; j
++) {
1623 if (bue
->devices
[j
].dev_id
== cis
[i
].dev_id
) {
1624 bue
->devices
[j
].alloc
+= stripesize
;
1631 if (length
< sizeof(btrfs_usage_device
)) {
1632 Status
= STATUS_BUFFER_OVERFLOW
;
1636 length
-= sizeof(btrfs_usage_device
);
1638 bue
->devices
[bue
->num_devices
].dev_id
= cis
[i
].dev_id
;
1639 bue
->devices
[bue
->num_devices
].alloc
= stripesize
;
1654 Status
= STATUS_SUCCESS
;
1657 ExReleaseResourceLite(&Vcb
->chunk_lock
);
1662 static NTSTATUS
is_volume_mounted(device_extension
* Vcb
, PIRP Irp
) {
1665 IO_STATUS_BLOCK iosb
;
1666 bool verify
= false;
1669 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, true);
1671 le
= Vcb
->devices
.Flink
;
1672 while (le
!= &Vcb
->devices
) {
1673 device
* dev
= CONTAINING_RECORD(le
, device
, list_entry
);
1675 if (dev
->devobj
&& dev
->removable
) {
1676 Status
= dev_ioctl(dev
->devobj
, IOCTL_STORAGE_CHECK_VERIFY
, NULL
, 0, &cc
, sizeof(ULONG
), false, &iosb
);
1678 if (iosb
.Information
!= sizeof(ULONG
))
1681 if (Status
== STATUS_VERIFY_REQUIRED
|| (NT_SUCCESS(Status
) && cc
!= dev
->change_count
)) {
1682 dev
->devobj
->Flags
|= DO_VERIFY_VOLUME
;
1686 if (NT_SUCCESS(Status
) && iosb
.Information
== sizeof(ULONG
))
1687 dev
->change_count
= cc
;
1689 if (!NT_SUCCESS(Status
) || verify
) {
1690 IoSetHardErrorOrVerifyDevice(Irp
, dev
->devobj
);
1691 ExReleaseResourceLite(&Vcb
->tree_lock
);
1693 return verify
? STATUS_VERIFY_REQUIRED
: Status
;
1700 ExReleaseResourceLite(&Vcb
->tree_lock
);
1702 return STATUS_SUCCESS
;
1705 static NTSTATUS
fs_get_statistics(void* buffer
, DWORD buflen
, ULONG_PTR
* retlen
) {
1706 FILESYSTEM_STATISTICS
* fss
;
1708 WARN("STUB: FSCTL_FILESYSTEM_GET_STATISTICS\n");
1710 // This is hideously wrong, but at least it stops SMB from breaking
1712 if (buflen
< sizeof(FILESYSTEM_STATISTICS
))
1713 return STATUS_BUFFER_TOO_SMALL
;
1716 RtlZeroMemory(fss
, sizeof(FILESYSTEM_STATISTICS
));
1719 fss
->FileSystemType
= FILESYSTEM_STATISTICS_TYPE_NTFS
;
1720 fss
->SizeOfCompleteStructure
= sizeof(FILESYSTEM_STATISTICS
);
1722 *retlen
= sizeof(FILESYSTEM_STATISTICS
);
1724 return STATUS_SUCCESS
;
1727 static NTSTATUS
set_sparse(device_extension
* Vcb
, PFILE_OBJECT FileObject
, void* data
, ULONG length
, PIRP Irp
) {
1728 FILE_SET_SPARSE_BUFFER
* fssb
= data
;
1732 ccb
* ccb
= FileObject
->FsContext2
;
1733 file_ref
* fileref
= ccb
? ccb
->fileref
: NULL
;
1735 if (data
&& length
< sizeof(FILE_SET_SPARSE_BUFFER
))
1736 return STATUS_INVALID_PARAMETER
;
1739 ERR("FileObject was NULL\n");
1740 return STATUS_INVALID_PARAMETER
;
1743 fcb
= FileObject
->FsContext
;
1746 ERR("FCB was NULL\n");
1747 return STATUS_INVALID_PARAMETER
;
1751 ERR("CCB was NULL\n");
1752 return STATUS_INVALID_PARAMETER
;
1755 if (Irp
->RequestorMode
== UserMode
&& !(ccb
->access
& FILE_WRITE_ATTRIBUTES
)) {
1756 WARN("insufficient privileges\n");
1757 return STATUS_ACCESS_DENIED
;
1761 ERR("no fileref\n");
1762 return STATUS_INVALID_PARAMETER
;
1766 fileref
= fileref
->parent
;
1770 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, true);
1771 ExAcquireResourceExclusiveLite(fcb
->Header
.Resource
, true);
1773 if (fcb
->type
!= BTRFS_TYPE_FILE
) {
1774 WARN("FileObject did not point to a file\n");
1775 Status
= STATUS_INVALID_PARAMETER
;
1780 set
= fssb
->SetSparse
;
1785 fcb
->atts
|= FILE_ATTRIBUTE_SPARSE_FILE
;
1786 fcb
->atts_changed
= true;
1790 fcb
->atts
&= ~FILE_ATTRIBUTE_SPARSE_FILE
;
1791 fcb
->atts_changed
= true;
1793 defda
= get_file_attributes(Vcb
, fcb
->subvol
, fcb
->inode
, fcb
->type
,
1794 fileref
&& fileref
->dc
&& fileref
->dc
->name
.Length
>= sizeof(WCHAR
) && fileref
->dc
->name
.Buffer
[0] == '.', true, Irp
);
1796 fcb
->atts_deleted
= defda
== fcb
->atts
;
1799 mark_fcb_dirty(fcb
);
1800 queue_notification_fcb(fileref
, FILE_NOTIFY_CHANGE_ATTRIBUTES
, FILE_ACTION_MODIFIED
, NULL
);
1802 Status
= STATUS_SUCCESS
;
1805 ExReleaseResourceLite(fcb
->Header
.Resource
);
1806 ExReleaseResourceLite(&Vcb
->tree_lock
);
1811 static NTSTATUS
zero_data(device_extension
* Vcb
, fcb
* fcb
, uint64_t start
, uint64_t length
, PIRP Irp
, LIST_ENTRY
* rollback
) {
1813 bool make_inline
, compress
;
1814 uint64_t start_data
, end_data
;
1818 make_inline
= fcb
->inode_item
.st_size
<= Vcb
->options
.max_inline
|| fcb_is_inline(fcb
);
1821 compress
= write_fcb_compressed(fcb
);
1825 end_data
= fcb
->inode_item
.st_size
;
1826 buf_head
= (ULONG
)offsetof(EXTENT_DATA
, data
[0]);
1827 } else if (compress
) {
1828 start_data
= start
& ~(uint64_t)(COMPRESSED_EXTENT_SIZE
- 1);
1829 end_data
= min(sector_align(start
+ length
, COMPRESSED_EXTENT_SIZE
),
1830 sector_align(fcb
->inode_item
.st_size
, Vcb
->superblock
.sector_size
));
1833 start_data
= start
& ~(uint64_t)(Vcb
->superblock
.sector_size
- 1);
1834 end_data
= sector_align(start
+ length
, Vcb
->superblock
.sector_size
);
1838 data
= ExAllocatePoolWithTag(PagedPool
, (ULONG
)(buf_head
+ end_data
- start_data
), ALLOC_TAG
);
1840 ERR("out of memory\n");
1841 return STATUS_INSUFFICIENT_RESOURCES
;
1844 RtlZeroMemory(data
+ buf_head
, (ULONG
)(end_data
- start_data
));
1846 if (start
> start_data
|| start
+ length
< end_data
) {
1847 Status
= read_file(fcb
, data
+ buf_head
, start_data
, end_data
- start_data
, NULL
, Irp
);
1849 if (!NT_SUCCESS(Status
)) {
1850 ERR("read_file returned %08lx\n", Status
);
1856 RtlZeroMemory(data
+ buf_head
+ start
- start_data
, (ULONG
)length
);
1860 EXTENT_DATA
* ed
= (EXTENT_DATA
*)data
;
1862 Status
= excise_extents(Vcb
, fcb
, 0, sector_align(end_data
, Vcb
->superblock
.sector_size
), Irp
, rollback
);
1863 if (!NT_SUCCESS(Status
)) {
1864 ERR("excise_extents returned %08lx\n", Status
);
1869 edsize
= (uint16_t)(offsetof(EXTENT_DATA
, data
[0]) + end_data
);
1871 ed
->generation
= Vcb
->superblock
.generation
;
1872 ed
->decoded_size
= end_data
;
1873 ed
->compression
= BTRFS_COMPRESSION_NONE
;
1874 ed
->encryption
= BTRFS_ENCRYPTION_NONE
;
1875 ed
->encoding
= BTRFS_ENCODING_NONE
;
1876 ed
->type
= EXTENT_TYPE_INLINE
;
1878 Status
= add_extent_to_fcb(fcb
, 0, ed
, edsize
, false, NULL
, rollback
);
1879 if (!NT_SUCCESS(Status
)) {
1880 ERR("add_extent_to_fcb returned %08lx\n", Status
);
1887 fcb
->inode_item
.st_blocks
+= end_data
;
1888 } else if (compress
) {
1889 Status
= write_compressed(fcb
, start_data
, end_data
, data
, Irp
, rollback
);
1893 if (!NT_SUCCESS(Status
)) {
1894 ERR("write_compressed returned %08lx\n", Status
);
1898 Status
= do_write_file(fcb
, start_data
, end_data
, data
, Irp
, false, 0, rollback
);
1902 if (!NT_SUCCESS(Status
)) {
1903 ERR("do_write_file returned %08lx\n", Status
);
1908 return STATUS_SUCCESS
;
1911 static NTSTATUS
set_zero_data(device_extension
* Vcb
, PFILE_OBJECT FileObject
, void* data
, ULONG length
, PIRP Irp
) {
1912 FILE_ZERO_DATA_INFORMATION
* fzdi
= data
;
1917 LIST_ENTRY rollback
, *le
;
1920 uint64_t start
, end
;
1922 IO_STATUS_BLOCK iosb
;
1924 if (!data
|| length
< sizeof(FILE_ZERO_DATA_INFORMATION
))
1925 return STATUS_INVALID_PARAMETER
;
1928 ERR("FileObject was NULL\n");
1929 return STATUS_INVALID_PARAMETER
;
1932 if (fzdi
->BeyondFinalZero
.QuadPart
<= fzdi
->FileOffset
.QuadPart
) {
1933 WARN("BeyondFinalZero was less than or equal to FileOffset (%I64x <= %I64x)\n", fzdi
->BeyondFinalZero
.QuadPart
, fzdi
->FileOffset
.QuadPart
);
1934 return STATUS_INVALID_PARAMETER
;
1937 fcb
= FileObject
->FsContext
;
1940 ERR("FCB was NULL\n");
1941 return STATUS_INVALID_PARAMETER
;
1944 ccb
= FileObject
->FsContext2
;
1947 ERR("ccb was NULL\n");
1948 return STATUS_INVALID_PARAMETER
;
1951 if (Irp
->RequestorMode
== UserMode
&& !(ccb
->access
& FILE_WRITE_DATA
)) {
1952 WARN("insufficient privileges\n");
1953 return STATUS_ACCESS_DENIED
;
1956 fileref
= ccb
->fileref
;
1959 ERR("fileref was NULL\n");
1960 return STATUS_INVALID_PARAMETER
;
1963 InitializeListHead(&rollback
);
1965 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, true);
1966 ExAcquireResourceExclusiveLite(fcb
->Header
.Resource
, true);
1968 CcFlushCache(FileObject
->SectionObjectPointer
, NULL
, 0, &iosb
);
1970 if (fcb
->type
!= BTRFS_TYPE_FILE
) {
1971 WARN("FileObject did not point to a file\n");
1972 Status
= STATUS_INVALID_PARAMETER
;
1977 ERR("FileObject is stream\n");
1978 Status
= STATUS_INVALID_PARAMETER
;
1982 if ((uint64_t)fzdi
->FileOffset
.QuadPart
>= fcb
->inode_item
.st_size
) {
1983 Status
= STATUS_SUCCESS
;
1988 le
= fcb
->extents
.Flink
;
1989 while (le
!= &fcb
->extents
) {
1990 extent
* ext2
= CONTAINING_RECORD(le
, extent
, list_entry
);
1992 if (!ext2
->ignore
) {
2001 Status
= STATUS_SUCCESS
;
2005 if (ext
->extent_data
.type
== EXTENT_TYPE_INLINE
) {
2006 Status
= zero_data(Vcb
, fcb
, fzdi
->FileOffset
.QuadPart
, fzdi
->BeyondFinalZero
.QuadPart
- fzdi
->FileOffset
.QuadPart
, Irp
, &rollback
);
2007 if (!NT_SUCCESS(Status
)) {
2008 ERR("zero_data returned %08lx\n", Status
);
2012 start
= sector_align(fzdi
->FileOffset
.QuadPart
, Vcb
->superblock
.sector_size
);
2014 if ((uint64_t)fzdi
->BeyondFinalZero
.QuadPart
> fcb
->inode_item
.st_size
)
2015 end
= sector_align(fcb
->inode_item
.st_size
, Vcb
->superblock
.sector_size
);
2017 end
= (fzdi
->BeyondFinalZero
.QuadPart
>> Vcb
->sector_shift
) << Vcb
->sector_shift
;
2020 Status
= zero_data(Vcb
, fcb
, fzdi
->FileOffset
.QuadPart
, fzdi
->BeyondFinalZero
.QuadPart
- fzdi
->FileOffset
.QuadPart
, Irp
, &rollback
);
2021 if (!NT_SUCCESS(Status
)) {
2022 ERR("zero_data returned %08lx\n", Status
);
2026 if (start
> (uint64_t)fzdi
->FileOffset
.QuadPart
) {
2027 Status
= zero_data(Vcb
, fcb
, fzdi
->FileOffset
.QuadPart
, start
- fzdi
->FileOffset
.QuadPart
, Irp
, &rollback
);
2028 if (!NT_SUCCESS(Status
)) {
2029 ERR("zero_data returned %08lx\n", Status
);
2034 if (end
< (uint64_t)fzdi
->BeyondFinalZero
.QuadPart
) {
2035 Status
= zero_data(Vcb
, fcb
, end
, fzdi
->BeyondFinalZero
.QuadPart
- end
, Irp
, &rollback
);
2036 if (!NT_SUCCESS(Status
)) {
2037 ERR("zero_data returned %08lx\n", Status
);
2043 Status
= excise_extents(Vcb
, fcb
, start
, end
, Irp
, &rollback
);
2044 if (!NT_SUCCESS(Status
)) {
2045 ERR("excise_extents returned %08lx\n", Status
);
2052 CcPurgeCacheSection(FileObject
->SectionObjectPointer
, &fzdi
->FileOffset
, (ULONG
)(fzdi
->BeyondFinalZero
.QuadPart
- fzdi
->FileOffset
.QuadPart
), false);
2054 KeQuerySystemTime(&time
);
2055 win_time_to_unix(time
, &now
);
2057 fcb
->inode_item
.transid
= Vcb
->superblock
.generation
;
2058 fcb
->inode_item
.sequence
++;
2060 if (!ccb
->user_set_change_time
)
2061 fcb
->inode_item
.st_ctime
= now
;
2063 if (!ccb
->user_set_write_time
)
2064 fcb
->inode_item
.st_mtime
= now
;
2066 fcb
->extents_changed
= true;
2067 fcb
->inode_item_changed
= true;
2068 mark_fcb_dirty(fcb
);
2070 queue_notification_fcb(fileref
, FILE_NOTIFY_CHANGE_LAST_WRITE
, FILE_ACTION_MODIFIED
, NULL
);
2072 fcb
->subvol
->root_item
.ctransid
= Vcb
->superblock
.generation
;
2073 fcb
->subvol
->root_item
.ctime
= now
;
2075 Status
= STATUS_SUCCESS
;
2078 if (!NT_SUCCESS(Status
))
2079 do_rollback(Vcb
, &rollback
);
2081 clear_rollback(&rollback
);
2083 ExReleaseResourceLite(fcb
->Header
.Resource
);
2084 ExReleaseResourceLite(&Vcb
->tree_lock
);
2089 static NTSTATUS
query_ranges(PFILE_OBJECT FileObject
, FILE_ALLOCATED_RANGE_BUFFER
* inbuf
, ULONG inbuflen
, void* outbuf
, ULONG outbuflen
, ULONG_PTR
* retlen
) {
2093 FILE_ALLOCATED_RANGE_BUFFER
* ranges
= outbuf
;
2095 uint64_t last_start
, last_end
;
2097 TRACE("FSCTL_QUERY_ALLOCATED_RANGES\n");
2100 ERR("FileObject was NULL\n");
2101 return STATUS_INVALID_PARAMETER
;
2104 if (!inbuf
|| inbuflen
< sizeof(FILE_ALLOCATED_RANGE_BUFFER
) || !outbuf
)
2105 return STATUS_INVALID_PARAMETER
;
2107 fcb
= FileObject
->FsContext
;
2110 ERR("FCB was NULL\n");
2111 return STATUS_INVALID_PARAMETER
;
2114 ExAcquireResourceSharedLite(fcb
->Header
.Resource
, true);
2116 // If file is not marked as sparse, claim the whole thing as an allocated range
2118 if (!(fcb
->atts
& FILE_ATTRIBUTE_SPARSE_FILE
)) {
2119 if (fcb
->inode_item
.st_size
== 0)
2120 Status
= STATUS_SUCCESS
;
2121 else if (outbuflen
< sizeof(FILE_ALLOCATED_RANGE_BUFFER
))
2122 Status
= STATUS_BUFFER_TOO_SMALL
;
2124 ranges
[i
].FileOffset
.QuadPart
= 0;
2125 ranges
[i
].Length
.QuadPart
= fcb
->inode_item
.st_size
;
2127 Status
= STATUS_SUCCESS
;
2134 le
= fcb
->extents
.Flink
;
2139 while (le
!= &fcb
->extents
) {
2140 extent
* ext
= CONTAINING_RECORD(le
, extent
, list_entry
);
2143 EXTENT_DATA2
* ed2
= (ext
->extent_data
.type
== EXTENT_TYPE_REGULAR
|| ext
->extent_data
.type
== EXTENT_TYPE_PREALLOC
) ? (EXTENT_DATA2
*)ext
->extent_data
.data
: NULL
;
2144 uint64_t len
= ed2
? ed2
->num_bytes
: ext
->extent_data
.decoded_size
;
2146 if (ext
->offset
> last_end
) { // first extent after a hole
2147 if (last_end
> last_start
) {
2148 if ((i
+ 1) * sizeof(FILE_ALLOCATED_RANGE_BUFFER
) <= outbuflen
) {
2149 ranges
[i
].FileOffset
.QuadPart
= last_start
;
2150 ranges
[i
].Length
.QuadPart
= min(fcb
->inode_item
.st_size
, last_end
) - last_start
;
2153 Status
= STATUS_BUFFER_TOO_SMALL
;
2158 last_start
= ext
->offset
;
2161 last_end
= ext
->offset
+ len
;
2167 if (last_end
> last_start
) {
2168 if ((i
+ 1) * sizeof(FILE_ALLOCATED_RANGE_BUFFER
) <= outbuflen
) {
2169 ranges
[i
].FileOffset
.QuadPart
= last_start
;
2170 ranges
[i
].Length
.QuadPart
= min(fcb
->inode_item
.st_size
, last_end
) - last_start
;
2173 Status
= STATUS_BUFFER_TOO_SMALL
;
2178 Status
= STATUS_SUCCESS
;
2181 *retlen
= i
* sizeof(FILE_ALLOCATED_RANGE_BUFFER
);
2183 ExReleaseResourceLite(fcb
->Header
.Resource
);
2188 static NTSTATUS
get_object_id(PFILE_OBJECT FileObject
, FILE_OBJECTID_BUFFER
* buf
, ULONG buflen
, ULONG_PTR
* retlen
) {
2191 TRACE("(%p, %p, %lx, %p)\n", FileObject
, buf
, buflen
, retlen
);
2194 ERR("FileObject was NULL\n");
2195 return STATUS_INVALID_PARAMETER
;
2198 if (!buf
|| buflen
< sizeof(FILE_OBJECTID_BUFFER
))
2199 return STATUS_INVALID_PARAMETER
;
2201 fcb
= FileObject
->FsContext
;
2204 ERR("FCB was NULL\n");
2205 return STATUS_INVALID_PARAMETER
;
2208 ExAcquireResourceSharedLite(fcb
->Header
.Resource
, true);
2210 RtlCopyMemory(&buf
->ObjectId
[0], &fcb
->inode
, sizeof(uint64_t));
2211 RtlCopyMemory(&buf
->ObjectId
[sizeof(uint64_t)], &fcb
->subvol
->id
, sizeof(uint64_t));
2213 ExReleaseResourceLite(fcb
->Header
.Resource
);
2215 RtlZeroMemory(&buf
->ExtendedInfo
, sizeof(buf
->ExtendedInfo
));
2217 *retlen
= sizeof(FILE_OBJECTID_BUFFER
);
2219 return STATUS_SUCCESS
;
2222 static void flush_fcb_caches(device_extension
* Vcb
) {
2225 le
= Vcb
->all_fcbs
.Flink
;
2226 while (le
!= &Vcb
->all_fcbs
) {
2227 struct _fcb
* fcb
= CONTAINING_RECORD(le
, struct _fcb
, list_entry_all
);
2228 IO_STATUS_BLOCK iosb
;
2230 if (fcb
->type
!= BTRFS_TYPE_DIRECTORY
&& !fcb
->deleted
)
2231 CcFlushCache(&fcb
->nonpaged
->segment_object
, NULL
, 0, &iosb
);
2237 static NTSTATUS
lock_volume(device_extension
* Vcb
, PIRP Irp
) {
2238 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
2241 bool lock_paused_balance
= false;
2243 TRACE("FSCTL_LOCK_VOLUME\n");
2245 if (Vcb
->scrub
.thread
) {
2246 WARN("cannot lock while scrub running\n");
2247 return STATUS_DEVICE_NOT_READY
;
2250 if (Vcb
->balance
.thread
) {
2251 WARN("cannot lock while balance running\n");
2252 return STATUS_DEVICE_NOT_READY
;
2255 TRACE("locking volume\n");
2257 FsRtlNotifyVolumeEvent(IrpSp
->FileObject
, FSRTL_VOLUME_LOCK
);
2260 return STATUS_SUCCESS
;
2262 ExAcquireResourceExclusiveLite(&Vcb
->fileref_lock
, true);
2264 if (Vcb
->root_fileref
&& Vcb
->root_fileref
->fcb
&& (Vcb
->root_fileref
->open_count
> 0 || has_open_children(Vcb
->root_fileref
))) {
2265 Status
= STATUS_ACCESS_DENIED
;
2266 ExReleaseResourceLite(&Vcb
->fileref_lock
);
2270 ExReleaseResourceLite(&Vcb
->fileref_lock
);
2272 if (Vcb
->balance
.thread
&& KeReadStateEvent(&Vcb
->balance
.event
)) {
2273 ExAcquireResourceExclusiveLite(&Vcb
->tree_lock
, true);
2274 KeClearEvent(&Vcb
->balance
.event
);
2275 ExReleaseResourceLite(&Vcb
->tree_lock
);
2277 lock_paused_balance
= true;
2280 ExAcquireResourceExclusiveLite(&Vcb
->tree_lock
, true);
2282 flush_fcb_caches(Vcb
);
2284 if (Vcb
->need_write
&& !Vcb
->readonly
)
2285 Status
= do_write(Vcb
, Irp
);
2287 Status
= STATUS_SUCCESS
;
2291 ExReleaseResourceLite(&Vcb
->tree_lock
);
2293 if (!NT_SUCCESS(Status
)) {
2294 ERR("do_write returned %08lx\n", Status
);
2298 IoAcquireVpbSpinLock(&irql
);
2300 if (!(Vcb
->Vpb
->Flags
& VPB_LOCKED
)) {
2301 Vcb
->Vpb
->Flags
|= VPB_LOCKED
;
2303 Vcb
->locked_fileobj
= IrpSp
->FileObject
;
2304 Vcb
->lock_paused_balance
= lock_paused_balance
;
2306 Status
= STATUS_ACCESS_DENIED
;
2307 IoReleaseVpbSpinLock(irql
);
2309 if (lock_paused_balance
)
2310 KeSetEvent(&Vcb
->balance
.event
, 0, false);
2315 IoReleaseVpbSpinLock(irql
);
2317 Status
= STATUS_SUCCESS
;
2320 if (!NT_SUCCESS(Status
))
2321 FsRtlNotifyVolumeEvent(IrpSp
->FileObject
, FSRTL_VOLUME_LOCK_FAILED
);
2326 void do_unlock_volume(device_extension
* Vcb
) {
2329 IoAcquireVpbSpinLock(&irql
);
2331 Vcb
->locked
= false;
2332 Vcb
->Vpb
->Flags
&= ~VPB_LOCKED
;
2333 Vcb
->locked_fileobj
= NULL
;
2335 IoReleaseVpbSpinLock(irql
);
2337 if (Vcb
->lock_paused_balance
)
2338 KeSetEvent(&Vcb
->balance
.event
, 0, false);
2341 static NTSTATUS
unlock_volume(device_extension
* Vcb
, PIRP Irp
) {
2342 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
2344 TRACE("FSCTL_UNLOCK_VOLUME\n");
2346 if (!Vcb
->locked
|| IrpSp
->FileObject
!= Vcb
->locked_fileobj
)
2347 return STATUS_NOT_LOCKED
;
2349 TRACE("unlocking volume\n");
2351 do_unlock_volume(Vcb
);
2353 FsRtlNotifyVolumeEvent(IrpSp
->FileObject
, FSRTL_VOLUME_UNLOCK
);
2355 return STATUS_SUCCESS
;
2358 static NTSTATUS
invalidate_volumes(PIRP Irp
) {
2359 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
2360 LUID TcbPrivilege
= {SE_TCB_PRIVILEGE
, 0};
2363 PFILE_OBJECT fileobj
;
2364 PDEVICE_OBJECT devobj
;
2367 TRACE("FSCTL_INVALIDATE_VOLUMES\n");
2369 if (!SeSinglePrivilegeCheck(TcbPrivilege
, Irp
->RequestorMode
))
2370 return STATUS_PRIVILEGE_NOT_HELD
;
2373 if (IoIs32bitProcess(Irp
)) {
2374 if (IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
!= sizeof(uint32_t))
2375 return STATUS_INVALID_PARAMETER
;
2377 h
= (HANDLE
)LongToHandle((*(uint32_t*)Irp
->AssociatedIrp
.SystemBuffer
));
2380 if (IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
!= sizeof(HANDLE
))
2381 return STATUS_INVALID_PARAMETER
;
2383 h
= *(PHANDLE
)Irp
->AssociatedIrp
.SystemBuffer
;
2388 Status
= ObReferenceObjectByHandle(h
, 0, *IoFileObjectType
, Irp
->RequestorMode
, (void**)&fileobj
, NULL
);
2390 if (!NT_SUCCESS(Status
)) {
2391 ERR("ObReferenceObjectByHandle returned %08lx\n", Status
);
2395 devobj
= fileobj
->DeviceObject
;
2397 ExAcquireResourceSharedLite(&global_loading_lock
, true);
2401 while (le
!= &VcbList
) {
2402 device_extension
* Vcb
= CONTAINING_RECORD(le
, device_extension
, list_entry
);
2404 if (Vcb
->Vpb
&& Vcb
->Vpb
->RealDevice
== devobj
) {
2405 if (Vcb
->Vpb
== devobj
->Vpb
) {
2408 bool free_newvpb
= false;
2410 newvpb
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(VPB
), ALLOC_TAG
);
2412 ERR("out of memory\n");
2413 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2417 RtlZeroMemory(newvpb
, sizeof(VPB
));
2419 ObReferenceObject(devobj
);
2421 ExAcquireResourceExclusiveLite(&Vcb
->tree_lock
, true);
2423 Vcb
->removing
= true;
2425 ExReleaseResourceLite(&Vcb
->tree_lock
);
2427 CcWaitForCurrentLazyWriterActivity();
2429 ExAcquireResourceExclusiveLite(&Vcb
->tree_lock
, true);
2431 flush_fcb_caches(Vcb
);
2433 if (Vcb
->need_write
&& !Vcb
->readonly
)
2434 Status
= do_write(Vcb
, Irp
);
2436 Status
= STATUS_SUCCESS
;
2440 if (!NT_SUCCESS(Status
)) {
2441 ERR("do_write returned %08lx\n", Status
);
2442 ExReleaseResourceLite(&Vcb
->tree_lock
);
2444 ObDereferenceObject(devobj
);
2448 flush_fcb_caches(Vcb
);
2450 ExReleaseResourceLite(&Vcb
->tree_lock
);
2452 IoAcquireVpbSpinLock(&irql
);
2454 if (devobj
->Vpb
->Flags
& VPB_MOUNTED
) {
2455 newvpb
->Type
= IO_TYPE_VPB
;
2456 newvpb
->Size
= sizeof(VPB
);
2457 newvpb
->RealDevice
= devobj
;
2458 newvpb
->Flags
= devobj
->Vpb
->Flags
& VPB_REMOVE_PENDING
;
2460 devobj
->Vpb
= newvpb
;
2464 IoReleaseVpbSpinLock(irql
);
2469 if (Vcb
->open_files
== 0)
2472 ObDereferenceObject(devobj
);
2481 Status
= STATUS_SUCCESS
;
2484 ExReleaseResourceLite(&global_loading_lock
);
2486 ObDereferenceObject(fileobj
);
2491 static NTSTATUS
is_volume_dirty(device_extension
* Vcb
, PIRP Irp
) {
2492 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
2495 if (Irp
->AssociatedIrp
.SystemBuffer
) {
2496 volstate
= Irp
->AssociatedIrp
.SystemBuffer
;
2497 } else if (Irp
->MdlAddress
!= NULL
) {
2498 volstate
= MmGetSystemAddressForMdlSafe(Irp
->MdlAddress
, LowPagePriority
);
2501 return STATUS_INSUFFICIENT_RESOURCES
;
2503 return STATUS_INVALID_USER_BUFFER
;
2505 if (IrpSp
->Parameters
.FileSystemControl
.OutputBufferLength
< sizeof(ULONG
))
2506 return STATUS_INVALID_PARAMETER
;
2510 if (IrpSp
->FileObject
->FsContext
!= Vcb
->volume_fcb
)
2511 return STATUS_INVALID_PARAMETER
;
2513 Irp
->IoStatus
.Information
= sizeof(ULONG
);
2515 return STATUS_SUCCESS
;
2518 static NTSTATUS
get_compression(PIRP Irp
) {
2519 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
2520 USHORT
* compression
;
2522 TRACE("FSCTL_GET_COMPRESSION\n");
2524 if (Irp
->AssociatedIrp
.SystemBuffer
) {
2525 compression
= Irp
->AssociatedIrp
.SystemBuffer
;
2526 } else if (Irp
->MdlAddress
!= NULL
) {
2527 compression
= MmGetSystemAddressForMdlSafe(Irp
->MdlAddress
, LowPagePriority
);
2530 return STATUS_INSUFFICIENT_RESOURCES
;
2532 return STATUS_INVALID_USER_BUFFER
;
2534 if (IrpSp
->Parameters
.FileSystemControl
.OutputBufferLength
< sizeof(USHORT
))
2535 return STATUS_INVALID_PARAMETER
;
2537 *compression
= COMPRESSION_FORMAT_NONE
;
2539 Irp
->IoStatus
.Information
= sizeof(USHORT
);
2541 return STATUS_SUCCESS
;
2544 static NTSTATUS
set_compression(PIRP Irp
) {
2545 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
2546 USHORT
* compression
;
2548 TRACE("FSCTL_SET_COMPRESSION\n");
2550 if (IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
< sizeof(USHORT
))
2551 return STATUS_INVALID_PARAMETER
;
2553 compression
= Irp
->AssociatedIrp
.SystemBuffer
;
2555 if (*compression
!= COMPRESSION_FORMAT_NONE
)
2556 return STATUS_INVALID_PARAMETER
;
2558 return STATUS_SUCCESS
;
2561 static void update_volumes(device_extension
* Vcb
) {
2563 volume_device_extension
* vde
= Vcb
->vde
;
2564 pdo_device_extension
* pdode
= vde
->pdode
;
2566 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, true);
2568 ExAcquireResourceExclusiveLite(&pdode
->child_lock
, true);
2570 le
= pdode
->children
.Flink
;
2571 while (le
!= &pdode
->children
) {
2572 volume_child
* vc
= CONTAINING_RECORD(le
, volume_child
, list_entry
);
2574 vc
->generation
= Vcb
->superblock
.generation
- 1;
2579 ExReleaseResourceLite(&pdode
->child_lock
);
2581 ExReleaseResourceLite(&Vcb
->tree_lock
);
2584 NTSTATUS
dismount_volume(device_extension
* Vcb
, bool shutdown
, PIRP Irp
) {
2588 TRACE("FSCTL_DISMOUNT_VOLUME\n");
2590 if (!(Vcb
->Vpb
->Flags
& VPB_MOUNTED
))
2591 return STATUS_SUCCESS
;
2594 if (Vcb
->disallow_dismount
|| Vcb
->page_file_count
!= 0) {
2595 WARN("attempting to dismount boot volume or one containing a pagefile\n");
2596 return STATUS_ACCESS_DENIED
;
2599 Status
= FsRtlNotifyVolumeEvent(Vcb
->root_file
, FSRTL_VOLUME_DISMOUNT
);
2600 if (!NT_SUCCESS(Status
)) {
2601 WARN("FsRtlNotifyVolumeEvent returned %08lx\n", Status
);
2605 ExAcquireResourceExclusiveLite(&Vcb
->tree_lock
, true);
2608 flush_fcb_caches(Vcb
);
2610 if (Vcb
->need_write
&& !Vcb
->readonly
) {
2611 Status
= do_write(Vcb
, Irp
);
2613 if (!NT_SUCCESS(Status
))
2614 ERR("do_write returned %08lx\n", Status
);
2620 Vcb
->removing
= true;
2622 open_files
= Vcb
->open_files
> 0;
2625 update_volumes(Vcb
);
2626 Vcb
->vde
->mounted_device
= NULL
;
2629 ExReleaseResourceLite(&Vcb
->tree_lock
);
2634 return STATUS_SUCCESS
;
2637 static NTSTATUS
is_device_part_of_mounted_btrfs_raid(PDEVICE_OBJECT devobj
, PFILE_OBJECT fileobj
) {
2641 BTRFS_UUID fsuuid
, devuuid
;
2644 to_read
= devobj
->SectorSize
== 0 ? sizeof(superblock
) : (ULONG
)sector_align(sizeof(superblock
), devobj
->SectorSize
);
2646 sb
= ExAllocatePoolWithTag(PagedPool
, to_read
, ALLOC_TAG
);
2648 ERR("out of memory\n");
2649 return STATUS_INSUFFICIENT_RESOURCES
;
2652 Status
= sync_read_phys(devobj
, fileobj
, superblock_addrs
[0], to_read
, (uint8_t*)sb
, true);
2653 if (!NT_SUCCESS(Status
)) {
2654 ERR("sync_read_phys returned %08lx\n", Status
);
2659 if (sb
->magic
!= BTRFS_MAGIC
) {
2660 TRACE("device is not Btrfs\n");
2662 return STATUS_SUCCESS
;
2665 if (!check_superblock_checksum(sb
)) {
2666 TRACE("device has Btrfs magic, but invalid superblock checksum\n");
2668 return STATUS_SUCCESS
;
2672 devuuid
= sb
->dev_item
.device_uuid
;
2676 ExAcquireResourceSharedLite(&global_loading_lock
, true);
2680 while (le
!= &VcbList
) {
2681 device_extension
* Vcb
= CONTAINING_RECORD(le
, device_extension
, list_entry
);
2683 if (RtlCompareMemory(&Vcb
->superblock
.uuid
, &fsuuid
, sizeof(BTRFS_UUID
)) == sizeof(BTRFS_UUID
)) {
2686 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, true);
2688 if (Vcb
->superblock
.num_devices
> 1) {
2689 le2
= Vcb
->devices
.Flink
;
2690 while (le2
!= &Vcb
->devices
) {
2691 device
* dev
= CONTAINING_RECORD(le2
, device
, list_entry
);
2693 if (RtlCompareMemory(&dev
->devitem
.device_uuid
, &devuuid
, sizeof(BTRFS_UUID
)) == sizeof(BTRFS_UUID
)) {
2694 ExReleaseResourceLite(&Vcb
->tree_lock
);
2695 ExReleaseResourceLite(&global_loading_lock
);
2696 return STATUS_DEVICE_NOT_READY
;
2703 ExReleaseResourceLite(&Vcb
->tree_lock
);
2704 ExReleaseResourceLite(&global_loading_lock
);
2705 return STATUS_SUCCESS
;
2711 ExReleaseResourceLite(&global_loading_lock
);
2713 return STATUS_SUCCESS
;
2716 void trim_whole_device(device
* dev
) {
2717 DEVICE_MANAGE_DATA_SET_ATTRIBUTES dmdsa
;
2720 // FIXME - avoid "bootloader area"??
2722 dmdsa
.Size
= sizeof(DEVICE_MANAGE_DATA_SET_ATTRIBUTES
);
2723 dmdsa
.Action
= DeviceDsmAction_Trim
;
2724 dmdsa
.Flags
= DEVICE_DSM_FLAG_ENTIRE_DATA_SET_RANGE
| DEVICE_DSM_FLAG_TRIM_NOT_FS_ALLOCATED
;
2725 dmdsa
.ParameterBlockOffset
= 0;
2726 dmdsa
.ParameterBlockLength
= 0;
2727 dmdsa
.DataSetRangesOffset
= 0;
2728 dmdsa
.DataSetRangesLength
= 0;
2730 Status
= dev_ioctl(dev
->devobj
, IOCTL_STORAGE_MANAGE_DATA_SET_ATTRIBUTES
, &dmdsa
, sizeof(DEVICE_MANAGE_DATA_SET_ATTRIBUTES
), NULL
, 0, true, NULL
);
2731 if (!NT_SUCCESS(Status
))
2732 WARN("IOCTL_STORAGE_MANAGE_DATA_SET_ATTRIBUTES returned %08lx\n", Status
);
2735 static NTSTATUS
add_device(device_extension
* Vcb
, PIRP Irp
, KPROCESSOR_MODE processor_mode
) {
2736 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
2738 PFILE_OBJECT fileobj
, mountmgrfo
;
2739 PDEVICE_OBJECT DeviceObject
;
2744 uint64_t dev_id
, size
;
2747 UNICODE_STRING mmdevpath
, pnp_name
, pnp_name2
;
2749 PDEVICE_OBJECT mountmgr
;
2752 STORAGE_DEVICE_NUMBER sdn
;
2753 volume_device_extension
* vde
;
2754 pdo_device_extension
* pdode
;
2755 const GUID
* pnp_guid
;
2756 GET_LENGTH_INFORMATION gli
;
2758 pnp_name
.Buffer
= NULL
;
2760 if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE
), processor_mode
))
2761 return STATUS_PRIVILEGE_NOT_HELD
;
2764 WARN("not allowing second device to be added to non-PNP device\n");
2765 return STATUS_NOT_SUPPORTED
;
2768 if (Vcb
->readonly
) // FIXME - handle adding R/W device to seeding device
2769 return STATUS_MEDIA_WRITE_PROTECTED
;
2772 if (IoIs32bitProcess(Irp
)) {
2773 if (IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
!= sizeof(uint32_t))
2774 return STATUS_INVALID_PARAMETER
;
2776 h
= (HANDLE
)LongToHandle((*(uint32_t*)Irp
->AssociatedIrp
.SystemBuffer
));
2779 if (IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
!= sizeof(HANDLE
))
2780 return STATUS_INVALID_PARAMETER
;
2782 h
= *(PHANDLE
)Irp
->AssociatedIrp
.SystemBuffer
;
2787 Status
= ObReferenceObjectByHandle(h
, 0, *IoFileObjectType
, Irp
->RequestorMode
, (void**)&fileobj
, NULL
);
2789 if (!NT_SUCCESS(Status
)) {
2790 ERR("ObReferenceObjectByHandle returned %08lx\n", Status
);
2794 DeviceObject
= fileobj
->DeviceObject
;
2796 Status
= get_device_pnp_name(DeviceObject
, &pnp_name
, &pnp_guid
);
2797 if (!NT_SUCCESS(Status
)) {
2798 ERR("get_device_pnp_name returned %08lx\n", Status
);
2799 ObDereferenceObject(fileobj
);
2803 // If this is a disk, we have been handed the PDO, so need to go up to find something we can use
2804 if (RtlCompareMemory(pnp_guid
, &GUID_DEVINTERFACE_DISK
, sizeof(GUID
)) == sizeof(GUID
) && DeviceObject
->AttachedDevice
)
2805 DeviceObject
= DeviceObject
->AttachedDevice
;
2807 Status
= dev_ioctl(DeviceObject
, IOCTL_DISK_IS_WRITABLE
, NULL
, 0, NULL
, 0, true, NULL
);
2808 if (!NT_SUCCESS(Status
)) {
2809 ERR("IOCTL_DISK_IS_WRITABLE returned %08lx\n", Status
);
2810 ObDereferenceObject(fileobj
);
2814 Status
= is_device_part_of_mounted_btrfs_raid(DeviceObject
, fileobj
);
2815 if (!NT_SUCCESS(Status
)) {
2816 ERR("is_device_part_of_mounted_btrfs_raid returned %08lx\n", Status
);
2817 ObDereferenceObject(fileobj
);
2821 // if disk, check it has no partitions
2822 if (RtlCompareMemory(pnp_guid
, &GUID_DEVINTERFACE_DISK
, sizeof(GUID
)) == sizeof(GUID
)) {
2824 DRIVE_LAYOUT_INFORMATION_EX
* dli
= NULL
;
2834 dli
= ExAllocatePoolWithTag(PagedPool
, dlisize
, ALLOC_TAG
);
2836 ERR("out of memory\n");
2837 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2841 Status
= dev_ioctl(DeviceObject
, IOCTL_DISK_GET_DRIVE_LAYOUT_EX
, NULL
, 0, dli
, dlisize
, true, NULL
);
2842 } while (Status
== STATUS_BUFFER_TOO_SMALL
);
2844 if (NT_SUCCESS(Status
) && dli
->PartitionCount
> 0) {
2846 ERR("not adding disk which has partitions\n");
2847 Status
= STATUS_DEVICE_NOT_READY
;
2854 Status
= dev_ioctl(DeviceObject
, IOCTL_STORAGE_GET_DEVICE_NUMBER
, NULL
, 0,
2855 &sdn
, sizeof(STORAGE_DEVICE_NUMBER
), true, NULL
);
2856 if (NT_SUCCESS(Status
)) {
2857 if (sdn
.DeviceType
!= FILE_DEVICE_DISK
) { // FIXME - accept floppies and CDs?
2858 WARN("device was not disk\n");
2859 ObDereferenceObject(fileobj
);
2860 return STATUS_INVALID_PARAMETER
;
2863 sdn
.DeviceNumber
= 0xffffffff;
2864 sdn
.PartitionNumber
= 0xffffffff;
2867 Status
= dev_ioctl(DeviceObject
, IOCTL_DISK_GET_LENGTH_INFO
, NULL
, 0,
2868 &gli
, sizeof(gli
), true, NULL
);
2869 if (!NT_SUCCESS(Status
)) {
2870 ERR("error reading length information: %08lx\n", Status
);
2871 ObDereferenceObject(fileobj
);
2875 size
= gli
.Length
.QuadPart
;
2877 if (size
< 0x100000) {
2878 ERR("device was not large enough to hold FS (%I64x bytes, need at least 1 MB)\n", size
);
2879 ObDereferenceObject(fileobj
);
2880 return STATUS_INTERNAL_ERROR
;
2883 volume_removal(&pnp_name
);
2885 ExAcquireResourceExclusiveLite(&Vcb
->tree_lock
, true);
2887 if (Vcb
->need_write
)
2888 Status
= do_write(Vcb
, Irp
);
2890 Status
= STATUS_SUCCESS
;
2894 if (!NT_SUCCESS(Status
)) {
2895 ERR("do_write returned %08lx\n", Status
);
2899 dev
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(device
), ALLOC_TAG
);
2901 ERR("out of memory\n");
2902 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2906 RtlZeroMemory(dev
, sizeof(device
));
2908 dev
->devobj
= DeviceObject
;
2909 dev
->fileobj
= fileobj
;
2910 dev
->seeding
= false;
2911 init_device(Vcb
, dev
, true);
2913 InitializeListHead(&dev
->space
);
2915 if (size
> 0x100000) { // add disk hole - the first MB is marked as used
2916 Status
= add_space_entry(&dev
->space
, NULL
, 0x100000, size
- 0x100000);
2917 if (!NT_SUCCESS(Status
)) {
2918 ERR("add_space_entry returned %08lx\n", Status
);
2925 le
= Vcb
->devices
.Flink
;
2926 while (le
!= &Vcb
->devices
) {
2927 device
* dev2
= CONTAINING_RECORD(le
, device
, list_entry
);
2929 if (dev2
->devitem
.dev_id
> dev_id
)
2930 dev_id
= dev2
->devitem
.dev_id
;
2937 dev
->devitem
.dev_id
= dev_id
;
2938 dev
->devitem
.num_bytes
= size
;
2939 dev
->devitem
.bytes_used
= 0;
2940 dev
->devitem
.optimal_io_align
= Vcb
->superblock
.sector_size
;
2941 dev
->devitem
.optimal_io_width
= Vcb
->superblock
.sector_size
;
2942 dev
->devitem
.minimal_io_size
= Vcb
->superblock
.sector_size
;
2943 dev
->devitem
.type
= 0;
2944 dev
->devitem
.generation
= 0;
2945 dev
->devitem
.start_offset
= 0;
2946 dev
->devitem
.dev_group
= 0;
2947 dev
->devitem
.seek_speed
= 0;
2948 dev
->devitem
.bandwidth
= 0;
2949 get_uuid(&dev
->devitem
.device_uuid
);
2950 dev
->devitem
.fs_uuid
= Vcb
->superblock
.uuid
;
2952 di
= ExAllocatePoolWithTag(PagedPool
, sizeof(DEV_ITEM
), ALLOC_TAG
);
2954 ERR("out of memory\n");
2958 RtlCopyMemory(di
, &dev
->devitem
, sizeof(DEV_ITEM
));
2960 Status
= insert_tree_item(Vcb
, Vcb
->chunk_root
, 1, TYPE_DEV_ITEM
, di
->dev_id
, di
, sizeof(DEV_ITEM
), NULL
, Irp
);
2961 if (!NT_SUCCESS(Status
)) {
2962 ERR("insert_tree_item returned %08lx\n", Status
);
2967 // add stats entry to dev tree
2968 stats
= ExAllocatePoolWithTag(PagedPool
, sizeof(uint64_t) * 5, ALLOC_TAG
);
2970 ERR("out of memory\n");
2971 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2975 RtlZeroMemory(stats
, sizeof(uint64_t) * 5);
2977 searchkey
.obj_id
= 0;
2978 searchkey
.obj_type
= TYPE_DEV_STATS
;
2979 searchkey
.offset
= di
->dev_id
;
2981 Status
= find_item(Vcb
, Vcb
->dev_root
, &tp
, &searchkey
, false, Irp
);
2982 if (!NT_SUCCESS(Status
)) {
2983 ERR("error - find_item returned %08lx\n", Status
);
2988 if (!keycmp(tp
.item
->key
, searchkey
)) {
2989 Status
= delete_tree_item(Vcb
, &tp
);
2990 if (!NT_SUCCESS(Status
)) {
2991 ERR("delete_tree_item returned %08lx\n", Status
);
2997 Status
= insert_tree_item(Vcb
, Vcb
->dev_root
, 0, TYPE_DEV_STATS
, di
->dev_id
, stats
, sizeof(uint64_t) * 5, NULL
, Irp
);
2998 if (!NT_SUCCESS(Status
)) {
2999 ERR("insert_tree_item returned %08lx\n", Status
);
3004 if (dev
->trim
&& !dev
->readonly
&& !Vcb
->options
.no_trim
)
3005 trim_whole_device(dev
);
3007 // We clear the first megabyte of the device, so Windows doesn't identify it as another FS
3008 mb
= ExAllocatePoolWithTag(PagedPool
, 0x100000, ALLOC_TAG
);
3010 ERR("out of memory\n");
3011 Status
= STATUS_INSUFFICIENT_RESOURCES
;
3015 RtlZeroMemory(mb
, 0x100000);
3017 Status
= write_data_phys(DeviceObject
, fileobj
, 0, mb
, 0x100000);
3018 if (!NT_SUCCESS(Status
)) {
3019 ERR("write_data_phys returned %08lx\n", Status
);
3029 vc
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(volume_child
), ALLOC_TAG
);
3031 ERR("out of memory\n");
3032 Status
= STATUS_INSUFFICIENT_RESOURCES
;
3036 vc
->uuid
= dev
->devitem
.device_uuid
;
3038 vc
->generation
= Vcb
->superblock
.generation
;
3039 vc
->devobj
= DeviceObject
;
3040 vc
->fileobj
= fileobj
;
3041 vc
->notification_entry
= NULL
;
3042 vc
->boot_volume
= false;
3044 Status
= IoRegisterPlugPlayNotification(EventCategoryTargetDeviceChange
, 0, fileobj
,
3045 drvobj
, pnp_removal
, vde
->pdode
, &vc
->notification_entry
);
3046 if (!NT_SUCCESS(Status
))
3047 WARN("IoRegisterPlugPlayNotification returned %08lx\n", Status
);
3049 pnp_name2
= pnp_name
;
3051 if (pnp_name
.Length
> 4 * sizeof(WCHAR
) && pnp_name
.Buffer
[0] == '\\' && (pnp_name
.Buffer
[1] == '\\' || pnp_name
.Buffer
[1] == '?') &&
3052 pnp_name
.Buffer
[2] == '?' && pnp_name
.Buffer
[3] == '\\') {
3053 pnp_name2
.Buffer
= &pnp_name2
.Buffer
[3];
3054 pnp_name2
.Length
-= 3 * sizeof(WCHAR
);
3055 pnp_name2
.MaximumLength
-= 3 * sizeof(WCHAR
);
3058 vc
->pnp_name
.Length
= vc
->pnp_name
.MaximumLength
= pnp_name2
.Length
;
3060 if (pnp_name2
.Length
== 0)
3061 vc
->pnp_name
.Buffer
= NULL
;
3063 vc
->pnp_name
.Buffer
= ExAllocatePoolWithTag(PagedPool
, pnp_name2
.Length
, ALLOC_TAG
);
3064 if (!vc
->pnp_name
.Buffer
) {
3065 ERR("out of memory\n");
3066 Status
= STATUS_INSUFFICIENT_RESOURCES
;
3070 RtlCopyMemory(vc
->pnp_name
.Buffer
, pnp_name2
.Buffer
, pnp_name2
.Length
);
3074 vc
->seeding
= false;
3075 vc
->disk_num
= sdn
.DeviceNumber
;
3076 vc
->part_num
= sdn
.PartitionNumber
;
3077 vc
->had_drive_letter
= false;
3079 ExAcquireResourceExclusiveLite(&pdode
->child_lock
, true);
3080 InsertTailList(&pdode
->children
, &vc
->list_entry
);
3081 pdode
->num_children
++;
3082 pdode
->children_loaded
++;
3083 ExReleaseResourceLite(&pdode
->child_lock
);
3085 RtlInitUnicodeString(&mmdevpath
, MOUNTMGR_DEVICE_NAME
);
3086 Status
= IoGetDeviceObjectPointer(&mmdevpath
, FILE_READ_ATTRIBUTES
, &mountmgrfo
, &mountmgr
);
3087 if (!NT_SUCCESS(Status
))
3088 ERR("IoGetDeviceObjectPointer returned %08lx\n", Status
);
3090 Status
= remove_drive_letter(mountmgr
, &pnp_name
);
3091 if (!NT_SUCCESS(Status
) && Status
!= STATUS_NOT_FOUND
)
3092 WARN("remove_drive_letter returned %08lx\n", Status
);
3094 vc
->had_drive_letter
= NT_SUCCESS(Status
);
3096 ObDereferenceObject(mountmgrfo
);
3099 Vcb
->superblock
.num_devices
++;
3100 Vcb
->superblock
.total_bytes
+= size
;
3101 Vcb
->devices_loaded
++;
3102 InsertTailList(&Vcb
->devices
, &dev
->list_entry
);
3104 // FIXME - send notification that volume size has increased
3106 ObReferenceObject(DeviceObject
); // for Vcb
3108 Status
= do_write(Vcb
, Irp
);
3109 if (!NT_SUCCESS(Status
))
3110 ERR("do_write returned %08lx\n", Status
);
3112 ObReferenceObject(fileobj
);
3117 ExReleaseResourceLite(&Vcb
->tree_lock
);
3120 ObDereferenceObject(fileobj
);
3122 if (pnp_name
.Buffer
)
3123 ExFreePool(pnp_name
.Buffer
);
3125 if (NT_SUCCESS(Status
))
3126 FsRtlNotifyVolumeEvent(Vcb
->root_file
, FSRTL_VOLUME_CHANGE_SIZE
);
3131 static NTSTATUS
allow_extended_dasd_io(device_extension
* Vcb
, PFILE_OBJECT FileObject
) {
3135 TRACE("FSCTL_ALLOW_EXTENDED_DASD_IO\n");
3138 return STATUS_INVALID_PARAMETER
;
3140 fcb
= FileObject
->FsContext
;
3141 ccb
= FileObject
->FsContext2
;
3144 return STATUS_INVALID_PARAMETER
;
3146 if (fcb
!= Vcb
->volume_fcb
)
3147 return STATUS_INVALID_PARAMETER
;
3150 return STATUS_INVALID_PARAMETER
;
3152 ccb
->allow_extended_dasd_io
= true;
3154 return STATUS_SUCCESS
;
3157 static NTSTATUS
query_uuid(device_extension
* Vcb
, void* data
, ULONG length
) {
3158 if (length
< sizeof(BTRFS_UUID
))
3159 return STATUS_BUFFER_OVERFLOW
;
3161 RtlCopyMemory(data
, &Vcb
->superblock
.uuid
, sizeof(BTRFS_UUID
));
3163 return STATUS_SUCCESS
;
3166 static NTSTATUS
reset_stats(device_extension
* Vcb
, void* data
, ULONG length
, KPROCESSOR_MODE processor_mode
) {
3171 if (length
< sizeof(uint64_t))
3172 return STATUS_INVALID_PARAMETER
;
3175 return STATUS_MEDIA_WRITE_PROTECTED
;
3177 if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE
), processor_mode
))
3178 return STATUS_PRIVILEGE_NOT_HELD
;
3180 devid
= *((uint64_t*)data
);
3182 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, true);
3184 le
= Vcb
->devices
.Flink
;
3186 while (le
!= &Vcb
->devices
) {
3187 device
* dev
= CONTAINING_RECORD(le
, device
, list_entry
);
3189 if (dev
->devitem
.dev_id
== devid
) {
3190 RtlZeroMemory(dev
->stats
, sizeof(uint64_t) * 5);
3191 dev
->stats_changed
= true;
3192 Vcb
->stats_changed
= true;
3193 Vcb
->need_write
= true;
3194 Status
= STATUS_SUCCESS
;
3201 WARN("device %I64x not found\n", devid
);
3202 Status
= STATUS_INVALID_PARAMETER
;
3205 ExReleaseResourceLite(&Vcb
->tree_lock
);
3210 static NTSTATUS
get_integrity_information(device_extension
* Vcb
, PFILE_OBJECT FileObject
, void* data
, ULONG datalen
) {
3211 FSCTL_GET_INTEGRITY_INFORMATION_BUFFER
* fgiib
= (FSCTL_GET_INTEGRITY_INFORMATION_BUFFER
*)data
;
3213 TRACE("FSCTL_GET_INTEGRITY_INFORMATION\n");
3218 return STATUS_INVALID_PARAMETER
;
3220 if (!data
|| datalen
< sizeof(FSCTL_GET_INTEGRITY_INFORMATION_BUFFER
))
3221 return STATUS_INVALID_PARAMETER
;
3223 fgiib
->ChecksumAlgorithm
= 0;
3224 fgiib
->Reserved
= 0;
3226 fgiib
->ChecksumChunkSizeInBytes
= Vcb
->superblock
.sector_size
;
3227 fgiib
->ClusterSizeInBytes
= Vcb
->superblock
.sector_size
;
3229 return STATUS_SUCCESS
;
3232 static NTSTATUS
set_integrity_information(PFILE_OBJECT FileObject
, void* data
, ULONG datalen
) {
3233 TRACE("FSCTL_SET_INTEGRITY_INFORMATION\n");
3238 return STATUS_INVALID_PARAMETER
;
3240 if (!data
|| datalen
< sizeof(FSCTL_SET_INTEGRITY_INFORMATION_BUFFER
))
3241 return STATUS_INVALID_PARAMETER
;
3243 return STATUS_SUCCESS
;
3246 bool fcb_is_inline(fcb
* fcb
) {
3249 le
= fcb
->extents
.Flink
;
3250 while (le
!= &fcb
->extents
) {
3251 extent
* ext
= CONTAINING_RECORD(le
, extent
, list_entry
);
3254 return ext
->extent_data
.type
== EXTENT_TYPE_INLINE
;
3262 static NTSTATUS
duplicate_extents(device_extension
* Vcb
, PFILE_OBJECT FileObject
, void* data
, ULONG datalen
, PIRP Irp
) {
3263 DUPLICATE_EXTENTS_DATA
* ded
= (DUPLICATE_EXTENTS_DATA
*)data
;
3264 fcb
*fcb
= FileObject
? FileObject
->FsContext
: NULL
, *sourcefcb
;
3265 ccb
*ccb
= FileObject
? FileObject
->FsContext2
: NULL
, *sourceccb
;
3267 PFILE_OBJECT sourcefo
;
3268 uint64_t sourcelen
, nbytes
= 0;
3269 LIST_ENTRY rollback
, *le
, newexts
;
3274 if (!ded
|| datalen
< sizeof(DUPLICATE_EXTENTS_DATA
))
3275 return STATUS_BUFFER_TOO_SMALL
;
3278 return STATUS_MEDIA_WRITE_PROTECTED
;
3280 if (ded
->ByteCount
.QuadPart
== 0)
3281 return STATUS_SUCCESS
;
3283 if (!fcb
|| !ccb
|| fcb
== Vcb
->volume_fcb
)
3284 return STATUS_INVALID_PARAMETER
;
3286 if (is_subvol_readonly(fcb
->subvol
, Irp
))
3287 return STATUS_ACCESS_DENIED
;
3289 if (Irp
->RequestorMode
== UserMode
&& !(ccb
->access
& FILE_WRITE_DATA
)) {
3290 WARN("insufficient privileges\n");
3291 return STATUS_ACCESS_DENIED
;
3294 if (!fcb
->ads
&& fcb
->type
!= BTRFS_TYPE_FILE
&& fcb
->type
!= BTRFS_TYPE_SYMLINK
)
3295 return STATUS_INVALID_PARAMETER
;
3297 Status
= ObReferenceObjectByHandle(ded
->FileHandle
, 0, *IoFileObjectType
, Irp
->RequestorMode
, (void**)&sourcefo
, NULL
);
3298 if (!NT_SUCCESS(Status
)) {
3299 ERR("ObReferenceObjectByHandle returned %08lx\n", Status
);
3303 if (sourcefo
->DeviceObject
!= FileObject
->DeviceObject
) {
3304 WARN("source and destination are on different volumes\n");
3305 ObDereferenceObject(sourcefo
);
3306 return STATUS_INVALID_PARAMETER
;
3309 sourcefcb
= sourcefo
->FsContext
;
3310 sourceccb
= sourcefo
->FsContext2
;
3312 if (!sourcefcb
|| !sourceccb
|| sourcefcb
== Vcb
->volume_fcb
) {
3313 ObDereferenceObject(sourcefo
);
3314 return STATUS_INVALID_PARAMETER
;
3317 if (!sourcefcb
->ads
&& !fcb
->ads
) {
3318 if ((ded
->SourceFileOffset
.QuadPart
& (Vcb
->superblock
.sector_size
- 1)) || (ded
->TargetFileOffset
.QuadPart
& (Vcb
->superblock
.sector_size
- 1))) {
3319 ObDereferenceObject(sourcefo
);
3320 return STATUS_INVALID_PARAMETER
;
3323 if (ded
->ByteCount
.QuadPart
& (Vcb
->superblock
.sector_size
- 1)) {
3324 ObDereferenceObject(sourcefo
);
3325 return STATUS_INVALID_PARAMETER
;
3329 if (Irp
->RequestorMode
== UserMode
&& (!(sourceccb
->access
& FILE_READ_DATA
) || !(sourceccb
->access
& FILE_READ_ATTRIBUTES
))) {
3330 WARN("insufficient privileges\n");
3331 ObDereferenceObject(sourcefo
);
3332 return STATUS_ACCESS_DENIED
;
3335 if (!sourcefcb
->ads
&& sourcefcb
->type
!= BTRFS_TYPE_FILE
&& sourcefcb
->type
!= BTRFS_TYPE_SYMLINK
) {
3336 ObDereferenceObject(sourcefo
);
3337 return STATUS_INVALID_PARAMETER
;
3340 sourcelen
= sourcefcb
->ads
? sourcefcb
->adsdata
.Length
: sourcefcb
->inode_item
.st_size
;
3342 if (sector_align(sourcelen
, Vcb
->superblock
.sector_size
) < (uint64_t)ded
->SourceFileOffset
.QuadPart
+ (uint64_t)ded
->ByteCount
.QuadPart
) {
3343 ObDereferenceObject(sourcefo
);
3344 return STATUS_NOT_SUPPORTED
;
3347 if (fcb
== sourcefcb
&&
3348 ((ded
->SourceFileOffset
.QuadPart
>= ded
->TargetFileOffset
.QuadPart
&& ded
->SourceFileOffset
.QuadPart
< ded
->TargetFileOffset
.QuadPart
+ ded
->ByteCount
.QuadPart
) ||
3349 (ded
->TargetFileOffset
.QuadPart
>= ded
->SourceFileOffset
.QuadPart
&& ded
->TargetFileOffset
.QuadPart
< ded
->SourceFileOffset
.QuadPart
+ ded
->ByteCount
.QuadPart
))) {
3350 WARN("source and destination are the same, and the ranges overlap\n");
3351 ObDereferenceObject(sourcefo
);
3352 return STATUS_INVALID_PARAMETER
;
3355 // fail if nocsum flag set on one file but not the other
3356 if (!fcb
->ads
&& !sourcefcb
->ads
&& (fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
) != (sourcefcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
)) {
3357 ObDereferenceObject(sourcefo
);
3358 return STATUS_INVALID_PARAMETER
;
3361 InitializeListHead(&rollback
);
3362 InitializeListHead(&newexts
);
3364 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, true);
3366 ExAcquireResourceExclusiveLite(fcb
->Header
.Resource
, true);
3368 if (fcb
!= sourcefcb
)
3369 ExAcquireResourceSharedLite(sourcefcb
->Header
.Resource
, true);
3371 if (!FsRtlFastCheckLockForWrite(&fcb
->lock
, &ded
->TargetFileOffset
, &ded
->ByteCount
, 0, FileObject
, PsGetCurrentProcess())) {
3372 Status
= STATUS_FILE_LOCK_CONFLICT
;
3376 if (!FsRtlFastCheckLockForRead(&sourcefcb
->lock
, &ded
->SourceFileOffset
, &ded
->ByteCount
, 0, FileObject
, PsGetCurrentProcess())) {
3377 Status
= STATUS_FILE_LOCK_CONFLICT
;
3381 make_inline
= fcb
->ads
? false : (fcb
->inode_item
.st_size
<= Vcb
->options
.max_inline
|| fcb_is_inline(fcb
));
3383 if (fcb
->ads
|| sourcefcb
->ads
|| make_inline
|| fcb_is_inline(sourcefcb
)) {
3385 ULONG bytes_read
, dataoff
, datalen2
;
3388 dataoff
= (ULONG
)ded
->TargetFileOffset
.QuadPart
;
3389 datalen2
= (ULONG
)fcb
->inode_item
.st_size
;
3390 } else if (fcb
->ads
) {
3392 datalen2
= (ULONG
)ded
->ByteCount
.QuadPart
;
3394 dataoff
= ded
->TargetFileOffset
.QuadPart
& (Vcb
->superblock
.sector_size
- 1);
3395 datalen2
= (ULONG
)sector_align(ded
->ByteCount
.QuadPart
+ dataoff
, Vcb
->superblock
.sector_size
);
3398 data2
= ExAllocatePoolWithTag(PagedPool
, datalen2
, ALLOC_TAG
);
3400 ERR("out of memory\n");
3401 Status
= STATUS_INSUFFICIENT_RESOURCES
;
3407 Status
= read_file(fcb
, data2
, 0, datalen2
, NULL
, Irp
);
3409 Status
= read_file(fcb
, data2
, ded
->TargetFileOffset
.QuadPart
- dataoff
, dataoff
, NULL
, Irp
);
3411 if (!NT_SUCCESS(Status
)) {
3412 ERR("read_file returned %08lx\n", Status
);
3418 if (sourcefcb
->ads
) {
3419 Status
= read_stream(sourcefcb
, data2
+ dataoff
, ded
->SourceFileOffset
.QuadPart
, (ULONG
)ded
->ByteCount
.QuadPart
, &bytes_read
);
3420 if (!NT_SUCCESS(Status
)) {
3421 ERR("read_stream returned %08lx\n", Status
);
3426 Status
= read_file(sourcefcb
, data2
+ dataoff
, ded
->SourceFileOffset
.QuadPart
, ded
->ByteCount
.QuadPart
, &bytes_read
, Irp
);
3427 if (!NT_SUCCESS(Status
)) {
3428 ERR("read_file returned %08lx\n", Status
);
3434 if (dataoff
+ bytes_read
< datalen2
)
3435 RtlZeroMemory(data2
+ dataoff
+ bytes_read
, datalen2
- bytes_read
);
3438 RtlCopyMemory(&fcb
->adsdata
.Buffer
[ded
->TargetFileOffset
.QuadPart
], data2
, (USHORT
)min(ded
->ByteCount
.QuadPart
, fcb
->adsdata
.Length
- ded
->TargetFileOffset
.QuadPart
));
3439 else if (make_inline
) {
3443 Status
= excise_extents(Vcb
, fcb
, 0, sector_align(fcb
->inode_item
.st_size
, Vcb
->superblock
.sector_size
), Irp
, &rollback
);
3444 if (!NT_SUCCESS(Status
)) {
3445 ERR("excise_extents returned %08lx\n", Status
);
3450 edsize
= (uint16_t)(offsetof(EXTENT_DATA
, data
[0]) + datalen2
);
3452 ed
= ExAllocatePoolWithTag(PagedPool
, edsize
, ALLOC_TAG
);
3454 ERR("out of memory\n");
3456 Status
= STATUS_INSUFFICIENT_RESOURCES
;
3460 ed
->generation
= Vcb
->superblock
.generation
;
3461 ed
->decoded_size
= fcb
->inode_item
.st_size
;
3462 ed
->compression
= BTRFS_COMPRESSION_NONE
;
3463 ed
->encryption
= BTRFS_ENCRYPTION_NONE
;
3464 ed
->encoding
= BTRFS_ENCODING_NONE
;
3465 ed
->type
= EXTENT_TYPE_INLINE
;
3467 RtlCopyMemory(ed
->data
, data2
, datalen2
);
3469 Status
= add_extent_to_fcb(fcb
, 0, ed
, edsize
, false, NULL
, &rollback
);
3470 if (!NT_SUCCESS(Status
)) {
3471 ERR("add_extent_to_fcb returned %08lx\n", Status
);
3476 fcb
->inode_item
.st_blocks
+= datalen2
;
3478 uint64_t start
= ded
->TargetFileOffset
.QuadPart
- (ded
->TargetFileOffset
.QuadPart
& (Vcb
->superblock
.sector_size
- 1));
3480 Status
= do_write_file(fcb
, start
, start
+ datalen2
, data2
, Irp
, false, 0, &rollback
);
3481 if (!NT_SUCCESS(Status
)) {
3482 ERR("do_write_file returned %08lx\n", Status
);
3490 LIST_ENTRY
* lastextle
;
3492 le
= sourcefcb
->extents
.Flink
;
3493 while (le
!= &sourcefcb
->extents
) {
3494 extent
* ext
= CONTAINING_RECORD(le
, extent
, list_entry
);
3497 if (ext
->offset
>= (uint64_t)ded
->SourceFileOffset
.QuadPart
+ (uint64_t)ded
->ByteCount
.QuadPart
)
3500 if (ext
->extent_data
.type
!= EXTENT_TYPE_INLINE
) {
3501 ULONG extlen
= offsetof(extent
, extent_data
) + sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
);
3503 EXTENT_DATA2
*ed2s
, *ed2d
;
3506 ed2s
= (EXTENT_DATA2
*)ext
->extent_data
.data
;
3508 if (ext
->offset
+ ed2s
->num_bytes
<= (uint64_t)ded
->SourceFileOffset
.QuadPart
) {
3513 ext2
= ExAllocatePoolWithTag(PagedPool
, extlen
, ALLOC_TAG
);
3515 ERR("out of memory\n");
3516 Status
= STATUS_INSUFFICIENT_RESOURCES
;
3520 if (ext
->offset
< (uint64_t)ded
->SourceFileOffset
.QuadPart
)
3521 ext2
->offset
= ded
->TargetFileOffset
.QuadPart
;
3523 ext2
->offset
= ext
->offset
- ded
->SourceFileOffset
.QuadPart
+ ded
->TargetFileOffset
.QuadPart
;
3525 ext2
->datalen
= sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
);
3526 ext2
->unique
= false;
3527 ext2
->ignore
= false;
3528 ext2
->inserted
= true;
3530 ext2
->extent_data
.generation
= Vcb
->superblock
.generation
;
3531 ext2
->extent_data
.decoded_size
= ext
->extent_data
.decoded_size
;
3532 ext2
->extent_data
.compression
= ext
->extent_data
.compression
;
3533 ext2
->extent_data
.encryption
= ext
->extent_data
.encryption
;
3534 ext2
->extent_data
.encoding
= ext
->extent_data
.encoding
;
3535 ext2
->extent_data
.type
= ext
->extent_data
.type
;
3537 ed2d
= (EXTENT_DATA2
*)ext2
->extent_data
.data
;
3539 ed2d
->address
= ed2s
->address
;
3540 ed2d
->size
= ed2s
->size
;
3542 if (ext
->offset
< (uint64_t)ded
->SourceFileOffset
.QuadPart
) {
3543 ed2d
->offset
= ed2s
->offset
+ ded
->SourceFileOffset
.QuadPart
- ext
->offset
;
3544 ed2d
->num_bytes
= min((uint64_t)ded
->ByteCount
.QuadPart
, ed2s
->num_bytes
+ ext
->offset
- ded
->SourceFileOffset
.QuadPart
);
3546 ed2d
->offset
= ed2s
->offset
;
3547 ed2d
->num_bytes
= min(ded
->SourceFileOffset
.QuadPart
+ ded
->ByteCount
.QuadPart
- ext
->offset
, ed2s
->num_bytes
);
3551 if (ext
->extent_data
.compression
== BTRFS_COMPRESSION_NONE
) {
3552 ext2
->csum
= ExAllocatePoolWithTag(PagedPool
, (ULONG
)((ed2d
->num_bytes
* Vcb
->csum_size
) >> Vcb
->sector_shift
), ALLOC_TAG
);
3554 ERR("out of memory\n");
3555 Status
= STATUS_INSUFFICIENT_RESOURCES
;
3560 RtlCopyMemory(ext2
->csum
, (uint8_t*)ext
->csum
+ (((ed2d
->offset
- ed2s
->offset
) * Vcb
->csum_size
) >> Vcb
->sector_shift
),
3561 (ULONG
)((ed2d
->num_bytes
* Vcb
->csum_size
) >> Vcb
->sector_shift
));
3563 ext2
->csum
= ExAllocatePoolWithTag(PagedPool
, (ULONG
)((ed2d
->size
* Vcb
->csum_size
) >> Vcb
->sector_shift
), ALLOC_TAG
);
3565 ERR("out of memory\n");
3566 Status
= STATUS_INSUFFICIENT_RESOURCES
;
3571 RtlCopyMemory(ext2
->csum
, ext
->csum
, (ULONG
)((ed2s
->size
* Vcb
->csum_size
) >> Vcb
->sector_shift
));
3576 InsertTailList(&newexts
, &ext2
->list_entry
);
3578 c
= get_chunk_from_address(Vcb
, ed2s
->address
);
3580 ERR("get_chunk_from_address(%I64x) failed\n", ed2s
->address
);
3581 Status
= STATUS_INTERNAL_ERROR
;
3585 Status
= update_changed_extent_ref(Vcb
, c
, ed2s
->address
, ed2s
->size
, fcb
->subvol
->id
, fcb
->inode
, ext2
->offset
- ed2d
->offset
,
3586 1, fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
, false, Irp
);
3587 if (!NT_SUCCESS(Status
)) {
3588 ERR("update_changed_extent_ref returned %08lx\n", Status
);
3592 nbytes
+= ed2d
->num_bytes
;
3599 Status
= excise_extents(Vcb
, fcb
, ded
->TargetFileOffset
.QuadPart
, ded
->TargetFileOffset
.QuadPart
+ ded
->ByteCount
.QuadPart
, Irp
, &rollback
);
3600 if (!NT_SUCCESS(Status
)) {
3601 ERR("excise_extents returned %08lx\n", Status
);
3603 while (!IsListEmpty(&newexts
)) {
3604 extent
* ext
= CONTAINING_RECORD(RemoveHeadList(&newexts
), extent
, list_entry
);
3611 // clear unique flags in source fcb
3612 le
= sourcefcb
->extents
.Flink
;
3613 while (le
!= &sourcefcb
->extents
) {
3614 extent
* ext
= CONTAINING_RECORD(le
, extent
, list_entry
);
3616 if (!ext
->ignore
&& ext
->unique
&& (ext
->extent_data
.type
== EXTENT_TYPE_REGULAR
|| ext
->extent_data
.type
== EXTENT_TYPE_PREALLOC
)) {
3617 EXTENT_DATA2
* ed2s
= (EXTENT_DATA2
*)ext
->extent_data
.data
;
3620 le2
= newexts
.Flink
;
3621 while (le2
!= &newexts
) {
3622 extent
* ext2
= CONTAINING_RECORD(le2
, extent
, list_entry
);
3624 if (ext2
->extent_data
.type
== EXTENT_TYPE_REGULAR
|| ext2
->extent_data
.type
== EXTENT_TYPE_PREALLOC
) {
3625 EXTENT_DATA2
* ed2d
= (EXTENT_DATA2
*)ext2
->extent_data
.data
;
3627 if (ed2d
->address
== ed2s
->address
&& ed2d
->size
== ed2s
->size
) {
3628 ext
->unique
= false;
3640 lastextle
= &fcb
->extents
;
3641 while (!IsListEmpty(&newexts
)) {
3642 extent
* ext
= CONTAINING_RECORD(RemoveHeadList(&newexts
), extent
, list_entry
);
3644 add_extent(fcb
, lastextle
, ext
);
3645 lastextle
= &ext
->list_entry
;
3649 KeQuerySystemTime(&time
);
3650 win_time_to_unix(time
, &now
);
3653 ccb
->fileref
->parent
->fcb
->inode_item
.sequence
++;
3655 if (!ccb
->user_set_change_time
)
3656 ccb
->fileref
->parent
->fcb
->inode_item
.st_ctime
= now
;
3658 ccb
->fileref
->parent
->fcb
->inode_item_changed
= true;
3659 mark_fcb_dirty(ccb
->fileref
->parent
->fcb
);
3661 fcb
->inode_item
.st_blocks
+= nbytes
;
3662 fcb
->inode_item
.sequence
++;
3664 if (!ccb
->user_set_change_time
)
3665 fcb
->inode_item
.st_ctime
= now
;
3667 if (!ccb
->user_set_write_time
) {
3668 fcb
->inode_item
.st_mtime
= now
;
3669 queue_notification_fcb(ccb
->fileref
, FILE_NOTIFY_CHANGE_LAST_WRITE
, FILE_ACTION_MODIFIED
, NULL
);
3672 fcb
->inode_item_changed
= true;
3673 fcb
->extents_changed
= true;
3676 mark_fcb_dirty(fcb
);
3678 if (FileObject
->SectionObjectPointer
->DataSectionObject
)
3679 CcPurgeCacheSection(FileObject
->SectionObjectPointer
, &ded
->TargetFileOffset
, (ULONG
)ded
->ByteCount
.QuadPart
, false);
3681 Status
= STATUS_SUCCESS
;
3684 ObDereferenceObject(sourcefo
);
3686 if (NT_SUCCESS(Status
))
3687 clear_rollback(&rollback
);
3689 do_rollback(Vcb
, &rollback
);
3691 if (fcb
!= sourcefcb
)
3692 ExReleaseResourceLite(sourcefcb
->Header
.Resource
);
3694 ExReleaseResourceLite(fcb
->Header
.Resource
);
3696 ExReleaseResourceLite(&Vcb
->tree_lock
);
3701 static NTSTATUS
check_inode_used(_In_
_Requires_exclusive_lock_held_(_Curr_
->tree_lock
) device_extension
* Vcb
,
3702 _In_ root
* subvol
, _In_
uint64_t inode
, _In_
uint32_t hash
, _In_opt_ PIRP Irp
) {
3706 uint8_t c
= hash
>> 24;
3708 if (subvol
->fcbs_ptrs
[c
]) {
3709 LIST_ENTRY
* le
= subvol
->fcbs_ptrs
[c
];
3711 while (le
!= &subvol
->fcbs
) {
3712 struct _fcb
* fcb2
= CONTAINING_RECORD(le
, struct _fcb
, list_entry
);
3714 if (fcb2
->inode
== inode
)
3715 return STATUS_SUCCESS
;
3716 else if (fcb2
->hash
> hash
)
3723 searchkey
.obj_id
= inode
;
3724 searchkey
.obj_type
= TYPE_INODE_ITEM
;
3725 searchkey
.offset
= 0xffffffffffffffff;
3727 Status
= find_item(Vcb
, subvol
, &tp
, &searchkey
, false, Irp
);
3728 if (!NT_SUCCESS(Status
)) {
3729 ERR("find_item returned %08lx\n", Status
);
3733 if (tp
.item
->key
.obj_id
== searchkey
.obj_id
&& tp
.item
->key
.obj_type
== searchkey
.obj_type
)
3734 return STATUS_SUCCESS
;
3736 return STATUS_NOT_FOUND
;
3739 static NTSTATUS
mknod(device_extension
* Vcb
, PFILE_OBJECT FileObject
, void* data
, ULONG datalen
, PIRP Irp
) {
3744 file_ref
*parfileref
, *fileref
;
3745 UNICODE_STRING name
;
3753 SECURITY_SUBJECT_CONTEXT subjcont
;
3757 TRACE("(%p, %p, %p, %lu)\n", Vcb
, FileObject
, data
, datalen
);
3759 if (!FileObject
|| !FileObject
->FsContext
|| !FileObject
->FsContext2
|| FileObject
->FsContext
== Vcb
->volume_fcb
)
3760 return STATUS_INVALID_PARAMETER
;
3763 return STATUS_MEDIA_WRITE_PROTECTED
;
3765 parfcb
= FileObject
->FsContext
;
3767 if (parfcb
->type
!= BTRFS_TYPE_DIRECTORY
) {
3768 WARN("trying to create file in something other than a directory\n");
3769 return STATUS_INVALID_PARAMETER
;
3772 if (is_subvol_readonly(parfcb
->subvol
, Irp
))
3773 return STATUS_ACCESS_DENIED
;
3775 parccb
= FileObject
->FsContext2
;
3776 parfileref
= parccb
->fileref
;
3779 return STATUS_INVALID_PARAMETER
;
3781 if (datalen
< sizeof(btrfs_mknod
))
3782 return STATUS_INVALID_PARAMETER
;
3784 bmn
= (btrfs_mknod
*)data
;
3786 if (datalen
< offsetof(btrfs_mknod
, name
[0]) + bmn
->namelen
|| bmn
->namelen
< sizeof(WCHAR
))
3787 return STATUS_INVALID_PARAMETER
;
3789 if (bmn
->type
== BTRFS_TYPE_UNKNOWN
|| bmn
->type
> BTRFS_TYPE_SYMLINK
)
3790 return STATUS_INVALID_PARAMETER
;
3792 if ((bmn
->type
== BTRFS_TYPE_DIRECTORY
&& !(parccb
->access
& FILE_ADD_SUBDIRECTORY
)) ||
3793 (bmn
->type
!= BTRFS_TYPE_DIRECTORY
&& !(parccb
->access
& FILE_ADD_FILE
))) {
3794 WARN("insufficient privileges\n");
3795 return STATUS_ACCESS_DENIED
;
3798 if (bmn
->inode
!= 0) {
3799 if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE
), Irp
->RequestorMode
))
3800 return STATUS_PRIVILEGE_NOT_HELD
;
3803 for (i
= 0; i
< bmn
->namelen
/ sizeof(WCHAR
); i
++) {
3804 if (bmn
->name
[i
] == 0 || bmn
->name
[i
] == '/')
3805 return STATUS_OBJECT_NAME_INVALID
;
3808 // don't allow files called . or ..
3809 if (bmn
->name
[0] == '.' && (bmn
->namelen
== sizeof(WCHAR
) || (bmn
->namelen
== 2 * sizeof(WCHAR
) && bmn
->name
[1] == '.')))
3810 return STATUS_OBJECT_NAME_INVALID
;
3812 Status
= utf16_to_utf8(NULL
, 0, &len
, bmn
->name
, bmn
->namelen
);
3813 if (!NT_SUCCESS(Status
)) {
3814 ERR("utf16_to_utf8 returned %08lx\n", Status
);
3819 ERR("utf16_to_utf8 returned a length of 0\n");
3820 return STATUS_INTERNAL_ERROR
;
3824 ERR("len was too long (%lx)\n", len
);
3825 return STATUS_INVALID_PARAMETER
;
3828 utf8
.MaximumLength
= utf8
.Length
= (USHORT
)len
;
3829 utf8
.Buffer
= ExAllocatePoolWithTag(PagedPool
, utf8
.MaximumLength
, ALLOC_TAG
);
3832 ERR("out of memory\n");
3833 return STATUS_INSUFFICIENT_RESOURCES
;
3836 Status
= utf16_to_utf8(utf8
.Buffer
, len
, &len
, bmn
->name
, bmn
->namelen
);
3837 if (!NT_SUCCESS(Status
)) {
3838 ERR("utf16_to_utf8 failed with error %08lx\n", Status
);
3839 ExFreePool(utf8
.Buffer
);
3843 name
.Length
= name
.MaximumLength
= bmn
->namelen
;
3844 name
.Buffer
= bmn
->name
;
3846 Status
= find_file_in_dir(&name
, parfcb
, &subvol
, &inode
, &dc
, true);
3847 if (!NT_SUCCESS(Status
) && Status
!= STATUS_OBJECT_NAME_NOT_FOUND
) {
3848 ERR("find_file_in_dir returned %08lx\n", Status
);
3852 if (NT_SUCCESS(Status
)) {
3853 WARN("filename already exists\n");
3854 Status
= STATUS_OBJECT_NAME_COLLISION
;
3858 KeQuerySystemTime(&time
);
3859 win_time_to_unix(time
, &now
);
3861 fcb
= create_fcb(Vcb
, PagedPool
);
3863 ERR("out of memory\n");
3864 Status
= STATUS_INSUFFICIENT_RESOURCES
;
3870 fcb
->inode_item
.generation
= Vcb
->superblock
.generation
;
3871 fcb
->inode_item
.transid
= Vcb
->superblock
.generation
;
3872 fcb
->inode_item
.st_size
= 0;
3873 fcb
->inode_item
.st_blocks
= 0;
3874 fcb
->inode_item
.block_group
= 0;
3875 fcb
->inode_item
.st_nlink
= 1;
3876 fcb
->inode_item
.st_uid
= UID_NOBODY
;
3877 fcb
->inode_item
.st_gid
= GID_NOBODY
;
3878 fcb
->inode_item
.st_mode
= inherit_mode(parfcb
, bmn
->type
== BTRFS_TYPE_DIRECTORY
);
3880 if (bmn
->type
== BTRFS_TYPE_BLOCKDEV
|| bmn
->type
== BTRFS_TYPE_CHARDEV
)
3881 fcb
->inode_item
.st_rdev
= (minor(bmn
->st_rdev
) & 0xFFFFF) | ((major(bmn
->st_rdev
) & 0xFFFFFFFFFFF) << 20);
3883 fcb
->inode_item
.st_rdev
= 0;
3885 fcb
->inode_item
.flags
= 0;
3886 fcb
->inode_item
.sequence
= 1;
3887 fcb
->inode_item
.st_atime
= now
;
3888 fcb
->inode_item
.st_ctime
= now
;
3889 fcb
->inode_item
.st_mtime
= now
;
3890 fcb
->inode_item
.otime
= now
;
3892 if (bmn
->type
== BTRFS_TYPE_DIRECTORY
)
3893 fcb
->inode_item
.st_mode
|= __S_IFDIR
;
3894 else if (bmn
->type
== BTRFS_TYPE_CHARDEV
)
3895 fcb
->inode_item
.st_mode
|= __S_IFCHR
;
3896 else if (bmn
->type
== BTRFS_TYPE_BLOCKDEV
)
3897 fcb
->inode_item
.st_mode
|= __S_IFBLK
;
3898 else if (bmn
->type
== BTRFS_TYPE_FIFO
)
3899 fcb
->inode_item
.st_mode
|= __S_IFIFO
;
3900 else if (bmn
->type
== BTRFS_TYPE_SOCKET
)
3901 fcb
->inode_item
.st_mode
|= __S_IFSOCK
;
3902 else if (bmn
->type
== BTRFS_TYPE_SYMLINK
)
3903 fcb
->inode_item
.st_mode
|= __S_IFLNK
;
3905 fcb
->inode_item
.st_mode
|= __S_IFREG
;
3907 if (bmn
->type
!= BTRFS_TYPE_DIRECTORY
)
3908 fcb
->inode_item
.st_mode
&= ~(S_IXUSR
| S_IXGRP
| S_IXOTH
); // remove executable bit if not directory
3910 // inherit nodatacow flag from parent directory
3911 if (parfcb
->inode_item
.flags
& BTRFS_INODE_NODATACOW
) {
3912 fcb
->inode_item
.flags
|= BTRFS_INODE_NODATACOW
;
3914 if (bmn
->type
!= BTRFS_TYPE_DIRECTORY
)
3915 fcb
->inode_item
.flags
|= BTRFS_INODE_NODATASUM
;
3918 if (parfcb
->inode_item
.flags
& BTRFS_INODE_COMPRESS
)
3919 fcb
->inode_item
.flags
|= BTRFS_INODE_COMPRESS
;
3921 fcb
->prop_compression
= parfcb
->prop_compression
;
3922 fcb
->prop_compression_changed
= fcb
->prop_compression
!= PropCompression_None
;
3924 fcb
->inode_item_changed
= true;
3926 fcb
->Header
.IsFastIoPossible
= fast_io_possible(fcb
);
3927 fcb
->Header
.AllocationSize
.QuadPart
= 0;
3928 fcb
->Header
.FileSize
.QuadPart
= 0;
3929 fcb
->Header
.ValidDataLength
.QuadPart
= 0;
3933 if (bmn
->name
[0] == '.')
3934 fcb
->atts
|= FILE_ATTRIBUTE_HIDDEN
;
3936 if (bmn
->type
== BTRFS_TYPE_DIRECTORY
)
3937 fcb
->atts
|= FILE_ATTRIBUTE_DIRECTORY
;
3939 fcb
->atts_changed
= false;
3941 InterlockedIncrement(&parfcb
->refcount
);
3942 fcb
->subvol
= parfcb
->subvol
;
3944 SeCaptureSubjectContext(&subjcont
);
3946 Status
= SeAssignSecurityEx(parfileref
? parfileref
->fcb
->sd
: NULL
, NULL
, (void**)&fcb
->sd
, NULL
, fcb
->type
== BTRFS_TYPE_DIRECTORY
,
3947 SEF_SACL_AUTO_INHERIT
, &subjcont
, IoGetFileObjectGenericMapping(), PagedPool
);
3949 if (!NT_SUCCESS(Status
)) {
3950 ERR("SeAssignSecurityEx returned %08lx\n", Status
);
3955 Status
= RtlGetOwnerSecurityDescriptor(fcb
->sd
, &owner
, &defaulted
);
3956 if (!NT_SUCCESS(Status
)) {
3957 WARN("RtlGetOwnerSecurityDescriptor returned %08lx\n", Status
);
3958 fcb
->sd_dirty
= true;
3960 fcb
->inode_item
.st_uid
= sid_to_uid(owner
);
3961 fcb
->sd_dirty
= fcb
->inode_item
.st_uid
== UID_NOBODY
;
3964 find_gid(fcb
, parfcb
, &subjcont
);
3966 ExAcquireResourceExclusiveLite(&Vcb
->fileref_lock
, true);
3967 acquire_fcb_lock_exclusive(Vcb
);
3969 if (bmn
->inode
== 0) {
3970 fcb
->inode
= InterlockedIncrement64(&parfcb
->subvol
->lastinode
);
3971 fcb
->hash
= calc_crc32c(0xffffffff, (uint8_t*)&fcb
->inode
, sizeof(uint64_t));
3973 if (bmn
->inode
> (uint64_t)parfcb
->subvol
->lastinode
) {
3974 fcb
->inode
= parfcb
->subvol
->lastinode
= bmn
->inode
;
3975 fcb
->hash
= calc_crc32c(0xffffffff, (uint8_t*)&fcb
->inode
, sizeof(uint64_t));
3977 uint32_t hash
= calc_crc32c(0xffffffff, (uint8_t*)&bmn
->inode
, sizeof(uint64_t));
3979 Status
= check_inode_used(Vcb
, subvol
, bmn
->inode
, hash
, Irp
);
3980 if (NT_SUCCESS(Status
)) { // STATUS_SUCCESS means inode found
3981 release_fcb_lock(Vcb
);
3982 ExReleaseResourceLite(&Vcb
->fileref_lock
);
3984 WARN("inode collision\n");
3985 Status
= STATUS_INVALID_PARAMETER
;
3987 } else if (Status
!= STATUS_NOT_FOUND
) {
3988 ERR("check_inode_used returned %08lx\n", Status
);
3990 release_fcb_lock(Vcb
);
3991 ExReleaseResourceLite(&Vcb
->fileref_lock
);
3995 fcb
->inode
= bmn
->inode
;
4001 fcb
->type
= bmn
->type
;
4003 fileref
= create_fileref(Vcb
);
4005 release_fcb_lock(Vcb
);
4006 ExReleaseResourceLite(&Vcb
->fileref_lock
);
4008 ERR("out of memory\n");
4010 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4016 fcb
->created
= true;
4017 fileref
->created
= true;
4019 fcb
->subvol
->root_item
.ctransid
= Vcb
->superblock
.generation
;
4020 fcb
->subvol
->root_item
.ctime
= now
;
4022 fileref
->parent
= parfileref
;
4024 mark_fcb_dirty(fcb
);
4025 mark_fileref_dirty(fileref
);
4027 Status
= add_dir_child(fileref
->parent
->fcb
, fcb
->inode
, false, &utf8
, &name
, fcb
->type
, &dc
);
4028 if (!NT_SUCCESS(Status
))
4029 WARN("add_dir_child returned %08lx\n", Status
);
4032 dc
->fileref
= fileref
;
4034 ExAcquireResourceExclusiveLite(&parfileref
->fcb
->nonpaged
->dir_children_lock
, true);
4035 InsertTailList(&parfileref
->children
, &fileref
->list_entry
);
4036 ExReleaseResourceLite(&parfileref
->fcb
->nonpaged
->dir_children_lock
);
4038 increase_fileref_refcount(parfileref
);
4040 if (fcb
->type
== BTRFS_TYPE_DIRECTORY
) {
4041 fcb
->hash_ptrs
= ExAllocatePoolWithTag(PagedPool
, sizeof(LIST_ENTRY
*) * 256, ALLOC_TAG
);
4042 if (!fcb
->hash_ptrs
) {
4043 release_fcb_lock(Vcb
);
4044 ExReleaseResourceLite(&Vcb
->fileref_lock
);
4046 ERR("out of memory\n");
4047 free_fileref(fileref
);
4048 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4052 RtlZeroMemory(fcb
->hash_ptrs
, sizeof(LIST_ENTRY
*) * 256);
4054 fcb
->hash_ptrs_uc
= ExAllocatePoolWithTag(PagedPool
, sizeof(LIST_ENTRY
*) * 256, ALLOC_TAG
);
4055 if (!fcb
->hash_ptrs_uc
) {
4056 release_fcb_lock(Vcb
);
4057 ExReleaseResourceLite(&Vcb
->fileref_lock
);
4059 ERR("out of memory\n");
4060 free_fileref(fileref
);
4061 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4065 RtlZeroMemory(fcb
->hash_ptrs_uc
, sizeof(LIST_ENTRY
*) * 256);
4068 add_fcb_to_subvol(fcb
);
4069 InsertTailList(&Vcb
->all_fcbs
, &fcb
->list_entry_all
);
4071 if (bmn
->type
== BTRFS_TYPE_DIRECTORY
)
4072 fileref
->fcb
->fileref
= fileref
;
4074 ExAcquireResourceExclusiveLite(parfcb
->Header
.Resource
, true);
4075 parfcb
->inode_item
.st_size
+= utf8
.Length
* 2;
4076 parfcb
->inode_item
.transid
= Vcb
->superblock
.generation
;
4077 parfcb
->inode_item
.sequence
++;
4079 if (!parccb
->user_set_change_time
)
4080 parfcb
->inode_item
.st_ctime
= now
;
4082 if (!parccb
->user_set_write_time
)
4083 parfcb
->inode_item
.st_mtime
= now
;
4085 parfcb
->subvol
->fcbs_version
++;
4087 ExReleaseResourceLite(parfcb
->Header
.Resource
);
4088 release_fcb_lock(Vcb
);
4089 ExReleaseResourceLite(&Vcb
->fileref_lock
);
4091 parfcb
->inode_item_changed
= true;
4092 mark_fcb_dirty(parfcb
);
4094 send_notification_fileref(fileref
, bmn
->type
== BTRFS_TYPE_DIRECTORY
? FILE_NOTIFY_CHANGE_DIR_NAME
: FILE_NOTIFY_CHANGE_FILE_NAME
, FILE_ACTION_ADDED
, NULL
);
4096 if (!parccb
->user_set_write_time
)
4097 queue_notification_fcb(parfileref
, FILE_NOTIFY_CHANGE_LAST_WRITE
, FILE_ACTION_MODIFIED
, NULL
);
4099 Status
= STATUS_SUCCESS
;
4103 ExFreePool(utf8
.Buffer
);
4108 static void mark_subvol_dirty(device_extension
* Vcb
, root
* r
) {
4112 ExAcquireResourceExclusiveLite(&Vcb
->dirty_subvols_lock
, true);
4113 InsertTailList(&Vcb
->dirty_subvols
, &r
->list_entry_dirty
);
4114 ExReleaseResourceLite(&Vcb
->dirty_subvols_lock
);
4117 Vcb
->need_write
= true;
4120 static NTSTATUS
recvd_subvol(device_extension
* Vcb
, PFILE_OBJECT FileObject
, void* data
, ULONG datalen
, KPROCESSOR_MODE processor_mode
) {
4121 btrfs_received_subvol
* brs
= (btrfs_received_subvol
*)data
;
4127 TRACE("(%p, %p, %p, %lu)\n", Vcb
, FileObject
, data
, datalen
);
4129 if (!data
|| datalen
< sizeof(btrfs_received_subvol
))
4130 return STATUS_INVALID_PARAMETER
;
4132 if (!FileObject
|| !FileObject
->FsContext
|| FileObject
->FsContext
== Vcb
->volume_fcb
)
4133 return STATUS_INVALID_PARAMETER
;
4135 fcb
= FileObject
->FsContext
;
4138 return STATUS_INVALID_PARAMETER
;
4140 if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE
), processor_mode
))
4141 return STATUS_PRIVILEGE_NOT_HELD
;
4143 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, true);
4145 if (fcb
->subvol
->root_item
.rtransid
!= 0) {
4146 WARN("subvol already has received information set\n");
4147 Status
= STATUS_INVALID_PARAMETER
;
4151 KeQuerySystemTime(&time
);
4152 win_time_to_unix(time
, &now
);
4154 RtlCopyMemory(&fcb
->subvol
->root_item
.received_uuid
, &brs
->uuid
, sizeof(BTRFS_UUID
));
4155 fcb
->subvol
->root_item
.stransid
= brs
->generation
;
4156 fcb
->subvol
->root_item
.rtransid
= Vcb
->superblock
.generation
;
4157 fcb
->subvol
->root_item
.rtime
= now
;
4159 fcb
->subvol
->received
= true;
4160 mark_subvol_dirty(Vcb
, fcb
->subvol
);
4162 Status
= STATUS_SUCCESS
;
4165 ExReleaseResourceLite(&Vcb
->tree_lock
);
4170 static NTSTATUS
fsctl_get_xattrs(device_extension
* Vcb
, PFILE_OBJECT FileObject
, void* data
, ULONG datalen
, KPROCESSOR_MODE processor_mode
) {
4172 btrfs_set_xattr
* bsxa
;
4173 ULONG reqlen
= (ULONG
)offsetof(btrfs_set_xattr
, data
[0]);
4177 if (!data
|| datalen
< reqlen
)
4178 return STATUS_INVALID_PARAMETER
;
4180 if (!FileObject
|| !FileObject
->FsContext
|| !FileObject
->FsContext2
|| FileObject
->FsContext
== Vcb
->volume_fcb
)
4181 return STATUS_INVALID_PARAMETER
;
4183 fcb
= FileObject
->FsContext
;
4184 ccb
= FileObject
->FsContext2
;
4186 if (!(ccb
->access
& (FILE_READ_ATTRIBUTES
| FILE_WRITE_ATTRIBUTES
)) && processor_mode
== UserMode
) {
4187 WARN("insufficient privileges\n");
4188 return STATUS_ACCESS_DENIED
;
4191 ExAcquireResourceSharedLite(fcb
->Header
.Resource
, true);
4193 le
= fcb
->xattrs
.Flink
;
4194 while (le
!= &fcb
->xattrs
) {
4195 xattr
* xa
= CONTAINING_RECORD(le
, xattr
, list_entry
);
4197 if (xa
->valuelen
> 0)
4198 reqlen
+= (ULONG
)offsetof(btrfs_set_xattr
, data
[0]) + xa
->namelen
+ xa
->valuelen
;
4203 if (datalen
< reqlen
) {
4204 ExReleaseResourceLite(fcb
->Header
.Resource
);
4205 return STATUS_BUFFER_OVERFLOW
;
4208 bsxa
= (btrfs_set_xattr
*)data
;
4211 le
= fcb
->xattrs
.Flink
;
4212 while (le
!= &fcb
->xattrs
) {
4213 xattr
* xa
= CONTAINING_RECORD(le
, xattr
, list_entry
);
4215 if (xa
->valuelen
> 0) {
4216 bsxa
->namelen
= xa
->namelen
;
4217 bsxa
->valuelen
= xa
->valuelen
;
4218 memcpy(bsxa
->data
, xa
->data
, xa
->namelen
+ xa
->valuelen
);
4220 bsxa
= (btrfs_set_xattr
*)&bsxa
->data
[xa
->namelen
+ xa
->valuelen
];
4230 ExReleaseResourceLite(fcb
->Header
.Resource
);
4232 return STATUS_SUCCESS
;
4235 static NTSTATUS
fsctl_set_xattr(device_extension
* Vcb
, PFILE_OBJECT FileObject
, void* data
, ULONG datalen
, PIRP Irp
) {
4237 btrfs_set_xattr
* bsxa
;
4243 static const char stream_pref
[] = "user.";
4245 TRACE("(%p, %p, %p, %lu)\n", Vcb
, FileObject
, data
, datalen
);
4247 if (!data
|| datalen
< sizeof(btrfs_set_xattr
))
4248 return STATUS_INVALID_PARAMETER
;
4250 bsxa
= (btrfs_set_xattr
*)data
;
4252 if (datalen
< offsetof(btrfs_set_xattr
, data
[0]) + bsxa
->namelen
+ bsxa
->valuelen
)
4253 return STATUS_INVALID_PARAMETER
;
4255 if (bsxa
->namelen
+ bsxa
->valuelen
+ sizeof(tree_header
) + sizeof(leaf_node
) + offsetof(DIR_ITEM
, name
[0]) > Vcb
->superblock
.node_size
)
4256 return STATUS_INVALID_PARAMETER
;
4258 if (!FileObject
|| !FileObject
->FsContext
|| !FileObject
->FsContext2
|| FileObject
->FsContext
== Vcb
->volume_fcb
)
4259 return STATUS_INVALID_PARAMETER
;
4262 return STATUS_MEDIA_WRITE_PROTECTED
;
4264 fcb
= FileObject
->FsContext
;
4265 ccb
= FileObject
->FsContext2
;
4267 if (is_subvol_readonly(fcb
->subvol
, Irp
))
4268 return STATUS_ACCESS_DENIED
;
4270 if (!(ccb
->access
& FILE_WRITE_ATTRIBUTES
) && Irp
->RequestorMode
== UserMode
) {
4271 WARN("insufficient privileges\n");
4272 return STATUS_ACCESS_DENIED
;
4275 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, true);
4277 ExAcquireResourceExclusiveLite(fcb
->Header
.Resource
, true);
4279 if (bsxa
->namelen
== sizeof(EA_NTACL
) - 1 && RtlCompareMemory(bsxa
->data
, EA_NTACL
, sizeof(EA_NTACL
) - 1) == sizeof(EA_NTACL
) - 1) {
4280 if ((!(ccb
->access
& WRITE_DAC
) || !(ccb
->access
& WRITE_OWNER
)) && Irp
->RequestorMode
== UserMode
) {
4281 WARN("insufficient privileges\n");
4282 Status
= STATUS_ACCESS_DENIED
;
4286 if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE
), Irp
->RequestorMode
)) {
4287 Status
= STATUS_PRIVILEGE_NOT_HELD
;
4292 ExFreePool(fcb
->sd
);
4294 if (bsxa
->valuelen
> 0 && RtlValidRelativeSecurityDescriptor(bsxa
->data
+ bsxa
->namelen
, bsxa
->valuelen
, 0)) {
4295 fcb
->sd
= ExAllocatePoolWithTag(PagedPool
, bsxa
->valuelen
, ALLOC_TAG
);
4297 ERR("out of memory\n");
4298 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4302 RtlCopyMemory(fcb
->sd
, bsxa
->data
+ bsxa
->namelen
, bsxa
->valuelen
);
4306 fcb
->sd_dirty
= true;
4309 fcb_get_sd(fcb
, ccb
->fileref
->parent
->fcb
, false, Irp
);
4310 fcb
->sd_deleted
= true;
4313 mark_fcb_dirty(fcb
);
4315 Status
= STATUS_SUCCESS
;
4317 } else if (bsxa
->namelen
== sizeof(EA_DOSATTRIB
) - 1 && RtlCompareMemory(bsxa
->data
, EA_DOSATTRIB
, sizeof(EA_DOSATTRIB
) - 1) == sizeof(EA_DOSATTRIB
) - 1) {
4320 if (bsxa
->valuelen
> 0 && get_file_attributes_from_xattr(bsxa
->data
+ bsxa
->namelen
, bsxa
->valuelen
, &atts
)) {
4323 if (fcb
->type
== BTRFS_TYPE_DIRECTORY
)
4324 fcb
->atts
|= FILE_ATTRIBUTE_DIRECTORY
;
4325 else if (fcb
->type
== BTRFS_TYPE_SYMLINK
)
4326 fcb
->atts
|= FILE_ATTRIBUTE_REPARSE_POINT
;
4328 if (fcb
->inode
== SUBVOL_ROOT_INODE
) {
4329 if (fcb
->subvol
->root_item
.flags
& BTRFS_SUBVOL_READONLY
)
4330 fcb
->atts
|= FILE_ATTRIBUTE_READONLY
;
4332 fcb
->atts
&= ~FILE_ATTRIBUTE_READONLY
;
4335 fcb
->atts_deleted
= false;
4337 bool hidden
= ccb
->fileref
&& ccb
->fileref
->dc
&& ccb
->fileref
->dc
->utf8
.Buffer
&& ccb
->fileref
->dc
->utf8
.Buffer
[0] == '.';
4339 fcb
->atts
= get_file_attributes(Vcb
, fcb
->subvol
, fcb
->inode
, fcb
->type
, hidden
, true, Irp
);
4340 fcb
->atts_deleted
= true;
4343 fcb
->atts_changed
= true;
4344 mark_fcb_dirty(fcb
);
4346 Status
= STATUS_SUCCESS
;
4348 } else if (bsxa
->namelen
== sizeof(EA_REPARSE
) - 1 && RtlCompareMemory(bsxa
->data
, EA_REPARSE
, sizeof(EA_REPARSE
) - 1) == sizeof(EA_REPARSE
) - 1) {
4349 if (fcb
->reparse_xattr
.Buffer
) {
4350 ExFreePool(fcb
->reparse_xattr
.Buffer
);
4351 fcb
->reparse_xattr
.Buffer
= NULL
;
4352 fcb
->reparse_xattr
.Length
= fcb
->reparse_xattr
.MaximumLength
= 0;
4355 if (bsxa
->valuelen
> 0) {
4356 fcb
->reparse_xattr
.Buffer
= ExAllocatePoolWithTag(PagedPool
, bsxa
->valuelen
, ALLOC_TAG
);
4357 if (!fcb
->reparse_xattr
.Buffer
) {
4358 ERR("out of memory\n");
4359 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4363 RtlCopyMemory(fcb
->reparse_xattr
.Buffer
, bsxa
->data
+ bsxa
->namelen
, bsxa
->valuelen
);
4364 fcb
->reparse_xattr
.Length
= fcb
->reparse_xattr
.MaximumLength
= bsxa
->valuelen
;
4367 fcb
->reparse_xattr_changed
= true;
4368 mark_fcb_dirty(fcb
);
4370 Status
= STATUS_SUCCESS
;
4372 } else if (bsxa
->namelen
== sizeof(EA_EA
) - 1 && RtlCompareMemory(bsxa
->data
, EA_EA
, sizeof(EA_EA
) - 1) == sizeof(EA_EA
) - 1) {
4373 if (!(ccb
->access
& FILE_WRITE_EA
) && Irp
->RequestorMode
== UserMode
) {
4374 WARN("insufficient privileges\n");
4375 Status
= STATUS_ACCESS_DENIED
;
4379 if (fcb
->ea_xattr
.Buffer
) {
4380 ExFreePool(fcb
->ea_xattr
.Buffer
);
4381 fcb
->ea_xattr
.Length
= fcb
->ea_xattr
.MaximumLength
= 0;
4382 fcb
->ea_xattr
.Buffer
= NULL
;
4387 if (bsxa
->valuelen
> 0) {
4390 Status
= IoCheckEaBufferValidity((FILE_FULL_EA_INFORMATION
*)(bsxa
->data
+ bsxa
->namelen
), bsxa
->valuelen
, &offset
);
4392 if (!NT_SUCCESS(Status
))
4393 WARN("IoCheckEaBufferValidity returned %08lx (error at offset %lu)\n", Status
, offset
);
4395 FILE_FULL_EA_INFORMATION
* eainfo
;
4397 fcb
->ea_xattr
.Buffer
= ExAllocatePoolWithTag(PagedPool
, bsxa
->valuelen
, ALLOC_TAG
);
4398 if (!fcb
->ea_xattr
.Buffer
) {
4399 ERR("out of memory\n");
4400 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4404 RtlCopyMemory(fcb
->ea_xattr
.Buffer
, bsxa
->data
+ bsxa
->namelen
, bsxa
->valuelen
);
4406 fcb
->ea_xattr
.Length
= fcb
->ea_xattr
.MaximumLength
= bsxa
->valuelen
;
4411 eainfo
= (FILE_FULL_EA_INFORMATION
*)(bsxa
->data
+ bsxa
->namelen
);
4413 fcb
->ealen
+= 5 + eainfo
->EaNameLength
+ eainfo
->EaValueLength
;
4415 if (eainfo
->NextEntryOffset
== 0)
4418 eainfo
= (FILE_FULL_EA_INFORMATION
*)(((uint8_t*)eainfo
) + eainfo
->NextEntryOffset
);
4423 fcb
->ea_changed
= true;
4424 mark_fcb_dirty(fcb
);
4426 Status
= STATUS_SUCCESS
;
4428 } else if (bsxa
->namelen
== sizeof(EA_CASE_SENSITIVE
) - 1 && RtlCompareMemory(bsxa
->data
, EA_CASE_SENSITIVE
, sizeof(EA_CASE_SENSITIVE
) - 1) == sizeof(EA_CASE_SENSITIVE
) - 1) {
4429 if (bsxa
->valuelen
> 0 && bsxa
->data
[bsxa
->namelen
] == '1') {
4430 fcb
->case_sensitive
= true;
4431 mark_fcb_dirty(fcb
);
4434 Status
= STATUS_SUCCESS
;
4436 } else if (bsxa
->namelen
== sizeof(EA_PROP_COMPRESSION
) - 1 && RtlCompareMemory(bsxa
->data
, EA_PROP_COMPRESSION
, sizeof(EA_PROP_COMPRESSION
) - 1) == sizeof(EA_PROP_COMPRESSION
) - 1) {
4437 static const char lzo
[] = "lzo";
4438 static const char zlib
[] = "zlib";
4439 static const char zstd
[] = "zstd";
4441 if (bsxa
->valuelen
== sizeof(zstd
) - 1 && RtlCompareMemory(bsxa
->data
+ bsxa
->namelen
, zstd
, bsxa
->valuelen
) == bsxa
->valuelen
)
4442 fcb
->prop_compression
= PropCompression_ZSTD
;
4443 else if (bsxa
->valuelen
== sizeof(lzo
) - 1 && RtlCompareMemory(bsxa
->data
+ bsxa
->namelen
, lzo
, bsxa
->valuelen
) == bsxa
->valuelen
)
4444 fcb
->prop_compression
= PropCompression_LZO
;
4445 else if (bsxa
->valuelen
== sizeof(zlib
) - 1 && RtlCompareMemory(bsxa
->data
+ bsxa
->namelen
, zlib
, bsxa
->valuelen
) == bsxa
->valuelen
)
4446 fcb
->prop_compression
= PropCompression_Zlib
;
4448 fcb
->prop_compression
= PropCompression_None
;
4450 if (fcb
->prop_compression
!= PropCompression_None
) {
4451 fcb
->inode_item
.flags
|= BTRFS_INODE_COMPRESS
;
4452 fcb
->inode_item_changed
= true;
4455 fcb
->prop_compression_changed
= true;
4456 mark_fcb_dirty(fcb
);
4458 Status
= STATUS_SUCCESS
;
4460 } else if (bsxa
->namelen
>= (sizeof(stream_pref
) - 1) && RtlCompareMemory(bsxa
->data
, stream_pref
, sizeof(stream_pref
) - 1) == sizeof(stream_pref
) - 1) {
4461 // don't allow xattrs beginning with user., as these appear as streams instead
4462 Status
= STATUS_OBJECT_NAME_INVALID
;
4466 xa
= ExAllocatePoolWithTag(PagedPool
, offsetof(xattr
, data
[0]) + bsxa
->namelen
+ bsxa
->valuelen
, ALLOC_TAG
);
4468 ERR("out of memory\n");
4469 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4473 le
= fcb
->xattrs
.Flink
;
4474 while (le
!= &fcb
->xattrs
) {
4475 xattr
* xa2
= CONTAINING_RECORD(le
, xattr
, list_entry
);
4477 if (xa2
->namelen
== bsxa
->namelen
&& RtlCompareMemory(xa2
->data
, bsxa
->data
, xa2
->namelen
) == xa2
->namelen
) {
4478 RemoveEntryList(&xa2
->list_entry
);
4486 xa
->namelen
= bsxa
->namelen
;
4487 xa
->valuelen
= bsxa
->valuelen
;
4489 RtlCopyMemory(xa
->data
, bsxa
->data
, bsxa
->namelen
+ bsxa
->valuelen
);
4491 InsertTailList(&fcb
->xattrs
, &xa
->list_entry
);
4493 fcb
->xattrs_changed
= true;
4494 mark_fcb_dirty(fcb
);
4496 Status
= STATUS_SUCCESS
;
4499 ExReleaseResourceLite(fcb
->Header
.Resource
);
4501 ExReleaseResourceLite(&Vcb
->tree_lock
);
4506 static NTSTATUS
reserve_subvol(device_extension
* Vcb
, PFILE_OBJECT FileObject
, PIRP Irp
) {
4510 TRACE("(%p, %p)\n", Vcb
, FileObject
);
4512 // "Reserving" a readonly subvol allows the calling process to write into it until the handle is closed.
4514 if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE
), Irp
->RequestorMode
))
4515 return STATUS_PRIVILEGE_NOT_HELD
;
4517 if (!FileObject
|| !FileObject
->FsContext
|| !FileObject
->FsContext2
|| FileObject
->FsContext
== Vcb
->volume_fcb
)
4518 return STATUS_INVALID_PARAMETER
;
4520 fcb
= FileObject
->FsContext
;
4521 ccb
= FileObject
->FsContext2
;
4523 if (!(fcb
->subvol
->root_item
.flags
& BTRFS_SUBVOL_READONLY
))
4524 return STATUS_INVALID_PARAMETER
;
4526 if (fcb
->subvol
->reserved
)
4527 return STATUS_INVALID_PARAMETER
;
4529 fcb
->subvol
->reserved
= PsGetCurrentProcess();
4530 ccb
->reserving
= true;
4532 return STATUS_SUCCESS
;
4535 static NTSTATUS
get_subvol_path(device_extension
* Vcb
, uint64_t id
, WCHAR
* out
, ULONG outlen
, PIRP Irp
) {
4542 le
= Vcb
->roots
.Flink
;
4543 while (le
!= &Vcb
->roots
) {
4544 root
* r2
= CONTAINING_RECORD(le
, root
, list_entry
);
4555 ERR("couldn't find subvol %I64x\n", id
);
4556 return STATUS_INTERNAL_ERROR
;
4559 ExAcquireResourceExclusiveLite(&Vcb
->fileref_lock
, true);
4561 Status
= open_fileref_by_inode(Vcb
, r
, r
->root_item
.objid
, &fr
, Irp
);
4562 if (!NT_SUCCESS(Status
)) {
4563 ExReleaseResourceLite(&Vcb
->fileref_lock
);
4564 ERR("open_fileref_by_inode returned %08lx\n", Status
);
4570 us
.MaximumLength
= (USHORT
)min(0xffff, outlen
) - sizeof(WCHAR
);
4572 Status
= fileref_get_filename(fr
, &us
, NULL
, NULL
);
4574 if (NT_SUCCESS(Status
) || Status
== STATUS_BUFFER_OVERFLOW
)
4575 out
[us
.Length
/ sizeof(WCHAR
)] = 0;
4577 ERR("fileref_get_filename returned %08lx\n", Status
);
4581 ExReleaseResourceLite(&Vcb
->fileref_lock
);
4586 static NTSTATUS
find_subvol(device_extension
* Vcb
, void* in
, ULONG inlen
, void* out
, ULONG outlen
, PIRP Irp
) {
4587 btrfs_find_subvol
* bfs
;
4592 if (!in
|| inlen
< sizeof(btrfs_find_subvol
))
4593 return STATUS_INVALID_PARAMETER
;
4595 if (!out
|| outlen
< sizeof(WCHAR
))
4596 return STATUS_INVALID_PARAMETER
;
4598 if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE
), Irp
->RequestorMode
))
4599 return STATUS_PRIVILEGE_NOT_HELD
;
4601 bfs
= (btrfs_find_subvol
*)in
;
4603 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, true);
4605 if (!Vcb
->uuid_root
) {
4606 ERR("couldn't find uuid root\n");
4607 Status
= STATUS_NOT_FOUND
;
4611 RtlCopyMemory(&searchkey
.obj_id
, &bfs
->uuid
, sizeof(uint64_t));
4612 searchkey
.obj_type
= TYPE_SUBVOL_UUID
;
4613 RtlCopyMemory(&searchkey
.offset
, &bfs
->uuid
.uuid
[sizeof(uint64_t)], sizeof(uint64_t));
4615 Status
= find_item(Vcb
, Vcb
->uuid_root
, &tp
, &searchkey
, false, Irp
);
4617 if (!NT_SUCCESS(Status
)) {
4618 ERR("find_item returned %08lx\n", Status
);
4622 if (!keycmp(searchkey
, tp
.item
->key
) && tp
.item
->size
>= sizeof(uint64_t)) {
4623 uint64_t* id
= (uint64_t*)tp
.item
->data
;
4625 if (bfs
->ctransid
!= 0) {
4629 searchkey2
.obj_id
= *id
;
4630 searchkey2
.obj_type
= TYPE_ROOT_ITEM
;
4631 searchkey2
.offset
= 0xffffffffffffffff;
4633 Status
= find_item(Vcb
, Vcb
->root_root
, &tp2
, &searchkey2
, false, Irp
);
4634 if (!NT_SUCCESS(Status
)) {
4635 ERR("find_item returned %08lx\n", Status
);
4639 if (tp2
.item
->key
.obj_id
== searchkey2
.obj_id
&& tp2
.item
->key
.obj_type
== searchkey2
.obj_type
&&
4640 tp2
.item
->size
>= offsetof(ROOT_ITEM
, otransid
)) {
4641 ROOT_ITEM
* ri
= (ROOT_ITEM
*)tp2
.item
->data
;
4643 if (ri
->ctransid
== bfs
->ctransid
) {
4644 TRACE("found subvol %I64x\n", *id
);
4645 Status
= get_subvol_path(Vcb
, *id
, out
, outlen
, Irp
);
4650 TRACE("found subvol %I64x\n", *id
);
4651 Status
= get_subvol_path(Vcb
, *id
, out
, outlen
, Irp
);
4656 searchkey
.obj_type
= TYPE_SUBVOL_REC_UUID
;
4658 Status
= find_item(Vcb
, Vcb
->uuid_root
, &tp
, &searchkey
, false, Irp
);
4660 if (!NT_SUCCESS(Status
)) {
4661 ERR("find_item returned %08lx\n", Status
);
4665 if (!keycmp(searchkey
, tp
.item
->key
) && tp
.item
->size
>= sizeof(uint64_t)) {
4666 uint64_t* ids
= (uint64_t*)tp
.item
->data
;
4669 for (i
= 0; i
< tp
.item
->size
/ sizeof(uint64_t); i
++) {
4670 if (bfs
->ctransid
!= 0) {
4674 searchkey2
.obj_id
= ids
[i
];
4675 searchkey2
.obj_type
= TYPE_ROOT_ITEM
;
4676 searchkey2
.offset
= 0xffffffffffffffff;
4678 Status
= find_item(Vcb
, Vcb
->root_root
, &tp2
, &searchkey2
, false, Irp
);
4679 if (!NT_SUCCESS(Status
)) {
4680 ERR("find_item returned %08lx\n", Status
);
4684 if (tp2
.item
->key
.obj_id
== searchkey2
.obj_id
&& tp2
.item
->key
.obj_type
== searchkey2
.obj_type
&&
4685 tp2
.item
->size
>= offsetof(ROOT_ITEM
, otransid
)) {
4686 ROOT_ITEM
* ri
= (ROOT_ITEM
*)tp2
.item
->data
;
4688 if (ri
->ctransid
== bfs
->ctransid
) {
4689 TRACE("found subvol %I64x\n", ids
[i
]);
4690 Status
= get_subvol_path(Vcb
, ids
[i
], out
, outlen
, Irp
);
4695 TRACE("found subvol %I64x\n", ids
[i
]);
4696 Status
= get_subvol_path(Vcb
, ids
[i
], out
, outlen
, Irp
);
4702 Status
= STATUS_NOT_FOUND
;
4705 ExReleaseResourceLite(&Vcb
->tree_lock
);
4710 static NTSTATUS
resize_device(device_extension
* Vcb
, void* data
, ULONG len
, PIRP Irp
) {
4711 btrfs_resize
* br
= (btrfs_resize
*)data
;
4716 TRACE("(%p, %p, %lu)\n", Vcb
, data
, len
);
4718 if (!data
|| len
< sizeof(btrfs_resize
) || (br
->size
& (Vcb
->superblock
.sector_size
- 1)) != 0)
4719 return STATUS_INVALID_PARAMETER
;
4721 if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE
), Irp
->RequestorMode
))
4722 return STATUS_PRIVILEGE_NOT_HELD
;
4725 return STATUS_MEDIA_WRITE_PROTECTED
;
4727 ExAcquireResourceExclusiveLite(&Vcb
->tree_lock
, true);
4729 le
= Vcb
->devices
.Flink
;
4730 while (le
!= &Vcb
->devices
) {
4731 device
* dev2
= CONTAINING_RECORD(le
, device
, list_entry
);
4733 if (dev2
->devitem
.dev_id
== br
->device
) {
4742 ERR("could not find device %I64x\n", br
->device
);
4743 Status
= STATUS_INVALID_PARAMETER
;
4748 ERR("trying to resize missing device\n");
4749 Status
= STATUS_INVALID_PARAMETER
;
4753 if (dev
->readonly
) {
4754 ERR("trying to resize readonly device\n");
4755 Status
= STATUS_INVALID_PARAMETER
;
4759 if (br
->size
> 0 && dev
->devitem
.num_bytes
== br
->size
) {
4760 TRACE("size unchanged, returning STATUS_SUCCESS\n");
4761 Status
= STATUS_SUCCESS
;
4765 if (br
->size
> 0 && dev
->devitem
.num_bytes
> br
->size
) { // shrink device
4766 bool need_balance
= true;
4767 uint64_t old_size
, delta
;
4769 le
= dev
->space
.Flink
;
4770 while (le
!= &dev
->space
) {
4771 space
* s
= CONTAINING_RECORD(le
, space
, list_entry
);
4773 if (s
->address
<= br
->size
&& s
->address
+ s
->size
>= dev
->devitem
.num_bytes
) {
4774 need_balance
= false;
4781 delta
= dev
->devitem
.num_bytes
- br
->size
;
4784 OBJECT_ATTRIBUTES oa
;
4787 if (Vcb
->balance
.thread
) {
4788 WARN("balance already running\n");
4789 Status
= STATUS_DEVICE_NOT_READY
;
4793 RtlZeroMemory(Vcb
->balance
.opts
, sizeof(btrfs_balance_opts
) * 3);
4795 for (i
= 0; i
< 3; i
++) {
4796 Vcb
->balance
.opts
[i
].flags
= BTRFS_BALANCE_OPTS_ENABLED
| BTRFS_BALANCE_OPTS_DEVID
| BTRFS_BALANCE_OPTS_DRANGE
;
4797 Vcb
->balance
.opts
[i
].devid
= dev
->devitem
.dev_id
;
4798 Vcb
->balance
.opts
[i
].drange_start
= br
->size
;
4799 Vcb
->balance
.opts
[i
].drange_end
= dev
->devitem
.num_bytes
;
4802 Vcb
->balance
.paused
= false;
4803 Vcb
->balance
.shrinking
= true;
4804 Vcb
->balance
.status
= STATUS_SUCCESS
;
4805 KeInitializeEvent(&Vcb
->balance
.event
, NotificationEvent
, !Vcb
->balance
.paused
);
4807 space_list_subtract2(&dev
->space
, NULL
, br
->size
, delta
, NULL
, NULL
);
4809 InitializeObjectAttributes(&oa
, NULL
, OBJ_KERNEL_HANDLE
, NULL
, NULL
);
4811 Status
= PsCreateSystemThread(&Vcb
->balance
.thread
, 0, &oa
, NULL
, NULL
, balance_thread
, Vcb
);
4812 if (!NT_SUCCESS(Status
)) {
4813 ERR("PsCreateSystemThread returned %08lx\n", Status
);
4817 Status
= STATUS_MORE_PROCESSING_REQUIRED
;
4822 old_size
= dev
->devitem
.num_bytes
;
4823 dev
->devitem
.num_bytes
= br
->size
;
4825 Status
= update_dev_item(Vcb
, dev
, Irp
);
4826 if (!NT_SUCCESS(Status
)) {
4827 ERR("update_dev_item returned %08lx\n", Status
);
4828 dev
->devitem
.num_bytes
= old_size
;
4832 space_list_subtract2(&dev
->space
, NULL
, br
->size
, delta
, NULL
, NULL
);
4834 Vcb
->superblock
.total_bytes
-= delta
;
4835 } else { // extend device
4836 GET_LENGTH_INFORMATION gli
;
4837 uint64_t old_size
, delta
;
4839 Status
= dev_ioctl(dev
->devobj
, IOCTL_DISK_GET_LENGTH_INFO
, NULL
, 0,
4840 &gli
, sizeof(gli
), true, NULL
);
4841 if (!NT_SUCCESS(Status
)) {
4842 ERR("IOCTL_DISK_GET_LENGTH_INFO returned %08lx\n", Status
);
4846 if (br
->size
== 0) {
4847 br
->size
= gli
.Length
.QuadPart
;
4849 if (dev
->devitem
.num_bytes
== br
->size
) {
4850 TRACE("size unchanged, returning STATUS_SUCCESS\n");
4851 Status
= STATUS_SUCCESS
;
4855 if (br
->size
== 0) {
4856 ERR("IOCTL_DISK_GET_LENGTH_INFO returned 0 length\n");
4857 Status
= STATUS_INTERNAL_ERROR
;
4860 } else if ((uint64_t)gli
.Length
.QuadPart
< br
->size
) {
4861 ERR("device was %I64x bytes, trying to extend to %I64x\n", gli
.Length
.QuadPart
, br
->size
);
4862 Status
= STATUS_INVALID_PARAMETER
;
4866 delta
= br
->size
- dev
->devitem
.num_bytes
;
4868 old_size
= dev
->devitem
.num_bytes
;
4869 dev
->devitem
.num_bytes
= br
->size
;
4871 Status
= update_dev_item(Vcb
, dev
, Irp
);
4872 if (!NT_SUCCESS(Status
)) {
4873 ERR("update_dev_item returned %08lx\n", Status
);
4874 dev
->devitem
.num_bytes
= old_size
;
4878 space_list_add2(&dev
->space
, NULL
, dev
->devitem
.num_bytes
, delta
, NULL
, NULL
);
4880 Vcb
->superblock
.total_bytes
+= delta
;
4883 Status
= STATUS_SUCCESS
;
4884 Vcb
->need_write
= true;
4887 ExReleaseResourceLite(&Vcb
->tree_lock
);
4889 if (NT_SUCCESS(Status
))
4890 FsRtlNotifyVolumeEvent(Vcb
->root_file
, FSRTL_VOLUME_CHANGE_SIZE
);
4895 static NTSTATUS
fsctl_oplock(device_extension
* Vcb
, PIRP
* Pirp
) {
4898 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
4899 uint32_t fsctl
= IrpSp
->Parameters
.FileSystemControl
.FsControlCode
;
4900 PFILE_OBJECT FileObject
= IrpSp
->FileObject
;
4901 fcb
* fcb
= FileObject
? FileObject
->FsContext
: NULL
;
4902 ccb
* ccb
= FileObject
? FileObject
->FsContext2
: NULL
;
4903 file_ref
* fileref
= ccb
? ccb
->fileref
: NULL
;
4904 #if (NTDDI_VERSION >= NTDDI_WIN7)
4905 PREQUEST_OPLOCK_INPUT_BUFFER buf
= NULL
;
4906 bool oplock_request
= false, oplock_ack
= false;
4908 bool oplock_request
= false;
4910 ULONG oplock_count
= 0;
4912 bool shared_request
;
4916 ERR("fcb was NULL\n");
4917 return STATUS_INVALID_PARAMETER
;
4921 ERR("fileref was NULL\n");
4922 return STATUS_INVALID_PARAMETER
;
4925 if (fcb
->type
!= BTRFS_TYPE_FILE
&& fcb
->type
!= BTRFS_TYPE_DIRECTORY
)
4926 return STATUS_INVALID_PARAMETER
;
4928 #if (NTDDI_VERSION >= NTDDI_WIN7)
4929 if (fsctl
== FSCTL_REQUEST_OPLOCK
) {
4930 if (IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
< sizeof(REQUEST_OPLOCK_INPUT_BUFFER
))
4931 return STATUS_BUFFER_TOO_SMALL
;
4933 if (IrpSp
->Parameters
.FileSystemControl
.OutputBufferLength
< sizeof(REQUEST_OPLOCK_OUTPUT_BUFFER
))
4934 return STATUS_BUFFER_TOO_SMALL
;
4936 buf
= Irp
->AssociatedIrp
.SystemBuffer
;
4938 // flags are mutually exclusive
4939 if (buf
->Flags
& REQUEST_OPLOCK_INPUT_FLAG_REQUEST
&& buf
->Flags
& REQUEST_OPLOCK_INPUT_FLAG_ACK
)
4940 return STATUS_INVALID_PARAMETER
;
4942 oplock_request
= buf
->Flags
& REQUEST_OPLOCK_INPUT_FLAG_REQUEST
;
4943 oplock_ack
= buf
->Flags
& REQUEST_OPLOCK_INPUT_FLAG_ACK
;
4945 if (!oplock_request
&& !oplock_ack
)
4946 return STATUS_INVALID_PARAMETER
;
4950 #if (NTDDI_VERSION >= NTDDI_WIN7)
4951 bool shared_request
= (fsctl
== FSCTL_REQUEST_OPLOCK_LEVEL_2
) || (fsctl
== FSCTL_REQUEST_OPLOCK
&& !(buf
->RequestedOplockLevel
& OPLOCK_LEVEL_CACHE_WRITE
));
4953 shared_request
= (fsctl
== FSCTL_REQUEST_OPLOCK_LEVEL_2
);
4956 #if (NTDDI_VERSION >= NTDDI_WIN7)
4957 if (fcb
->type
== BTRFS_TYPE_DIRECTORY
&& (fsctl
!= FSCTL_REQUEST_OPLOCK
|| !shared_request
)) {
4959 if (fcb
->type
== BTRFS_TYPE_DIRECTORY
&& !shared_request
) {
4961 WARN("oplock requests on directories can only be for read or read-handle oplocks\n");
4962 return STATUS_INVALID_PARAMETER
;
4965 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, true);
4967 ExAcquireResourceExclusiveLite(fcb
->Header
.Resource
, true);
4969 if (fsctl
== FSCTL_REQUEST_OPLOCK_LEVEL_1
|| fsctl
== FSCTL_REQUEST_BATCH_OPLOCK
|| fsctl
== FSCTL_REQUEST_FILTER_OPLOCK
||
4970 fsctl
== FSCTL_REQUEST_OPLOCK_LEVEL_2
|| oplock_request
) {
4971 if (shared_request
) {
4972 if (fcb
->type
== BTRFS_TYPE_FILE
) {
4973 if (fFsRtlCheckLockForOplockRequest
)
4974 oplock_count
= !fFsRtlCheckLockForOplockRequest(&fcb
->lock
, &fcb
->Header
.AllocationSize
);
4975 else if (fFsRtlAreThereCurrentOrInProgressFileLocks
)
4976 oplock_count
= fFsRtlAreThereCurrentOrInProgressFileLocks(&fcb
->lock
);
4978 oplock_count
= FsRtlAreThereCurrentFileLocks(&fcb
->lock
);
4981 oplock_count
= fileref
->open_count
;
4984 #if (NTDDI_VERSION >= NTDDI_WIN7)
4985 if ((fsctl
== FSCTL_REQUEST_FILTER_OPLOCK
|| fsctl
== FSCTL_REQUEST_BATCH_OPLOCK
||
4986 (fsctl
== FSCTL_REQUEST_OPLOCK
&& buf
->RequestedOplockLevel
& OPLOCK_LEVEL_CACHE_HANDLE
)) &&
4988 if ((fsctl
== FSCTL_REQUEST_FILTER_OPLOCK
|| fsctl
== FSCTL_REQUEST_BATCH_OPLOCK
) &&
4990 fileref
->delete_on_close
) {
4991 ExReleaseResourceLite(fcb
->Header
.Resource
);
4992 ExReleaseResourceLite(&Vcb
->tree_lock
);
4993 return STATUS_DELETE_PENDING
;
4996 Status
= FsRtlOplockFsctrl(fcb_oplock(fcb
), Irp
, oplock_count
);
5000 fcb
->Header
.IsFastIoPossible
= fast_io_possible(fcb
);
5002 ExReleaseResourceLite(fcb
->Header
.Resource
);
5003 ExReleaseResourceLite(&Vcb
->tree_lock
);
5008 static NTSTATUS
get_retrieval_pointers(device_extension
* Vcb
, PFILE_OBJECT FileObject
, STARTING_VCN_INPUT_BUFFER
* in
,
5009 ULONG inlen
, RETRIEVAL_POINTERS_BUFFER
* out
, ULONG outlen
, ULONG_PTR
* retlen
) {
5013 TRACE("get_retrieval_pointers(%p, %p, %p, %lx, %p, %lx, %p)\n", Vcb
, FileObject
, in
, inlen
,
5014 out
, outlen
, retlen
);
5017 return STATUS_INVALID_PARAMETER
;
5019 fcb
= FileObject
->FsContext
;
5022 return STATUS_INVALID_PARAMETER
;
5024 if (inlen
< sizeof(STARTING_VCN_INPUT_BUFFER
) || in
->StartingVcn
.QuadPart
< 0)
5025 return STATUS_INVALID_PARAMETER
;
5028 return STATUS_INVALID_PARAMETER
;
5030 if (outlen
< offsetof(RETRIEVAL_POINTERS_BUFFER
, Extents
[0]))
5031 return STATUS_BUFFER_TOO_SMALL
;
5033 ExAcquireResourceSharedLite(fcb
->Header
.Resource
, true);
5036 LIST_ENTRY
* le
= fcb
->extents
.Flink
;
5037 extent
* first_ext
= NULL
;
5038 unsigned int num_extents
= 0, first_extent_num
= 0, i
;
5039 uint64_t num_sectors
, last_off
= 0;
5041 num_sectors
= (fcb
->inode_item
.st_size
+ Vcb
->superblock
.sector_size
- 1) >> Vcb
->sector_shift
;
5043 while (le
!= &fcb
->extents
) {
5044 extent
* ext
= CONTAINING_RECORD(le
, extent
, list_entry
);
5046 if (ext
->ignore
|| ext
->extent_data
.type
== EXTENT_TYPE_INLINE
) {
5051 if (ext
->offset
> last_off
)
5054 if ((ext
->offset
>> Vcb
->sector_shift
) <= (uint64_t)in
->StartingVcn
.QuadPart
&&
5055 (ext
->offset
+ ext
->extent_data
.decoded_size
) >> Vcb
->sector_shift
> (uint64_t)in
->StartingVcn
.QuadPart
) {
5057 first_extent_num
= num_extents
;
5062 last_off
= ext
->offset
+ ext
->extent_data
.decoded_size
;
5067 if (num_sectors
> last_off
>> Vcb
->sector_shift
)
5071 Status
= STATUS_END_OF_FILE
;
5075 out
->ExtentCount
= num_extents
- first_extent_num
;
5076 out
->StartingVcn
.QuadPart
= first_ext
->offset
>> Vcb
->sector_shift
;
5077 outlen
-= offsetof(RETRIEVAL_POINTERS_BUFFER
, Extents
[0]);
5078 *retlen
= offsetof(RETRIEVAL_POINTERS_BUFFER
, Extents
[0]);
5080 le
= &first_ext
->list_entry
;
5084 while (le
!= &fcb
->extents
) {
5085 extent
* ext
= CONTAINING_RECORD(le
, extent
, list_entry
);
5087 if (ext
->ignore
|| ext
->extent_data
.type
== EXTENT_TYPE_INLINE
) {
5092 if (ext
->offset
> last_off
) {
5093 if (outlen
< sizeof(LARGE_INTEGER
) + sizeof(LARGE_INTEGER
)) {
5094 Status
= STATUS_BUFFER_OVERFLOW
;
5098 out
->Extents
[i
].NextVcn
.QuadPart
= ext
->offset
>> Vcb
->sector_shift
;
5099 out
->Extents
[i
].Lcn
.QuadPart
= -1;
5101 outlen
-= sizeof(LARGE_INTEGER
) + sizeof(LARGE_INTEGER
);
5102 *retlen
+= sizeof(LARGE_INTEGER
) + sizeof(LARGE_INTEGER
);
5106 if (outlen
< sizeof(LARGE_INTEGER
) + sizeof(LARGE_INTEGER
)) {
5107 Status
= STATUS_BUFFER_OVERFLOW
;
5111 out
->Extents
[i
].NextVcn
.QuadPart
= (ext
->offset
+ ext
->extent_data
.decoded_size
) >> Vcb
->sector_shift
;
5113 if (ext
->extent_data
.compression
== BTRFS_COMPRESSION_NONE
) {
5114 EXTENT_DATA2
* ed2
= (EXTENT_DATA2
*)ext
->extent_data
.data
;
5116 out
->Extents
[i
].Lcn
.QuadPart
= (ed2
->address
+ ed2
->offset
) >> Vcb
->sector_shift
;
5118 out
->Extents
[i
].Lcn
.QuadPart
= -1;
5120 outlen
-= sizeof(LARGE_INTEGER
) + sizeof(LARGE_INTEGER
);
5121 *retlen
+= sizeof(LARGE_INTEGER
) + sizeof(LARGE_INTEGER
);
5127 if (num_sectors
<< Vcb
->sector_shift
> last_off
) {
5128 if (outlen
< sizeof(LARGE_INTEGER
) + sizeof(LARGE_INTEGER
)) {
5129 Status
= STATUS_BUFFER_OVERFLOW
;
5133 out
->Extents
[i
].NextVcn
.QuadPart
= num_sectors
;
5134 out
->Extents
[i
].Lcn
.QuadPart
= -1;
5136 outlen
-= sizeof(LARGE_INTEGER
) + sizeof(LARGE_INTEGER
);
5137 *retlen
+= sizeof(LARGE_INTEGER
) + sizeof(LARGE_INTEGER
);
5140 Status
= STATUS_SUCCESS
;
5142 ExReleaseResourceLite(fcb
->Header
.Resource
);
5148 static NTSTATUS
add_csum_sparse_extents(device_extension
* Vcb
, uint64_t sparse_extents
, uint8_t** ptr
, bool found
, void* hash_ptr
) {
5150 uint8_t* sector
= ExAllocatePoolWithTag(PagedPool
, Vcb
->superblock
.sector_size
, ALLOC_TAG
);
5153 ERR("out of memory\n");
5154 return STATUS_INSUFFICIENT_RESOURCES
;
5157 memset(sector
, 0, Vcb
->superblock
.sector_size
);
5159 get_sector_csum(Vcb
, sector
, hash_ptr
);
5164 switch (Vcb
->superblock
.csum_type
) {
5165 case CSUM_TYPE_CRC32C
: {
5166 uint32_t* csum
= (uint32_t*)*ptr
;
5167 uint32_t sparse_hash
= *(uint32_t*)hash_ptr
;
5169 for (uint64_t i
= 0; i
< sparse_extents
; i
++) {
5170 csum
[i
] = sparse_hash
;
5176 case CSUM_TYPE_XXHASH
: {
5177 uint64_t* csum
= (uint64_t*)*ptr
;
5178 uint64_t sparse_hash
= *(uint64_t*)hash_ptr
;
5180 for (uint64_t i
= 0; i
< sparse_extents
; i
++) {
5181 csum
[i
] = sparse_hash
;
5187 case CSUM_TYPE_SHA256
:
5188 case CSUM_TYPE_BLAKE2
: {
5189 uint8_t* csum
= (uint8_t*)*ptr
;
5191 for (uint64_t i
= 0; i
< sparse_extents
; i
++) {
5192 memcpy(csum
, hash_ptr
, 32);
5200 ERR("unrecognized hash type %x\n", Vcb
->superblock
.csum_type
);
5201 return STATUS_INTERNAL_ERROR
;
5204 *ptr
+= sparse_extents
* Vcb
->csum_size
;
5206 return STATUS_SUCCESS
;
5209 static NTSTATUS
get_csum_info(device_extension
* Vcb
, PFILE_OBJECT FileObject
, btrfs_csum_info
* buf
, ULONG buflen
,
5210 ULONG_PTR
* retlen
, KPROCESSOR_MODE processor_mode
) {
5215 TRACE("get_csum_info(%p, %p, %p, %lx, %p, %x)\n", Vcb
, FileObject
, buf
, buflen
, retlen
, processor_mode
);
5218 return STATUS_INVALID_PARAMETER
;
5220 fcb
= FileObject
->FsContext
;
5221 ccb
= FileObject
->FsContext2
;
5224 return STATUS_INVALID_PARAMETER
;
5227 return STATUS_INVALID_PARAMETER
;
5229 if (buflen
< offsetof(btrfs_csum_info
, data
[0]))
5230 return STATUS_BUFFER_TOO_SMALL
;
5233 if (processor_mode
== UserMode
&& !(ccb
->access
& (FILE_READ_DATA
| FILE_WRITE_DATA
))) {
5234 WARN("insufficient privileges\n");
5235 return STATUS_ACCESS_DENIED
;
5238 ExAcquireResourceSharedLite(fcb
->Header
.Resource
, true);
5244 uint8_t sparse_hash
[MAX_HASH_SIZE
];
5245 bool sparse_hash_found
= false;
5248 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5252 if (fcb
->type
== BTRFS_TYPE_DIRECTORY
) {
5253 Status
= STATUS_FILE_IS_A_DIRECTORY
;
5257 if (fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
) {
5258 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5262 buf
->csum_type
= Vcb
->superblock
.csum_type
;
5263 buf
->csum_length
= Vcb
->csum_size
;
5265 le
= fcb
->extents
.Flink
;
5266 while (le
!= &fcb
->extents
) {
5267 extent
* ext
= CONTAINING_RECORD(le
, extent
, list_entry
);
5274 if (ext
->extent_data
.type
== EXTENT_TYPE_INLINE
) {
5275 buf
->num_sectors
= 0;
5276 *retlen
= offsetof(btrfs_csum_info
, data
[0]);
5277 Status
= STATUS_SUCCESS
;
5284 buf
->num_sectors
= (fcb
->inode_item
.st_size
+ Vcb
->superblock
.sector_size
- 1) >> Vcb
->sector_shift
;
5286 if (buflen
< offsetof(btrfs_csum_info
, data
[0]) + (buf
->csum_length
* buf
->num_sectors
)) {
5287 Status
= STATUS_BUFFER_OVERFLOW
;
5288 *retlen
= offsetof(btrfs_csum_info
, data
[0]);
5295 le
= fcb
->extents
.Flink
;
5296 while (le
!= &fcb
->extents
) {
5297 extent
* ext
= CONTAINING_RECORD(le
, extent
, list_entry
);
5300 if (ext
->ignore
|| ext
->extent_data
.type
== EXTENT_TYPE_INLINE
) {
5305 if (ext
->offset
> last_off
) {
5306 uint64_t sparse_extents
= (ext
->offset
- last_off
) >> Vcb
->sector_shift
;
5308 add_csum_sparse_extents(Vcb
, sparse_extents
, &ptr
, sparse_hash_found
, sparse_hash
);
5309 sparse_hash_found
= true;
5312 ed2
= (EXTENT_DATA2
*)ext
->extent_data
.data
;
5314 if (ext
->extent_data
.compression
!= BTRFS_COMPRESSION_NONE
)
5315 memset(ptr
, 0, (ed2
->num_bytes
>> Vcb
->sector_shift
) * Vcb
->csum_size
); // dummy value for compressed extents
5318 memcpy(ptr
, ext
->csum
, (ed2
->num_bytes
>> Vcb
->sector_shift
) * Vcb
->csum_size
);
5320 memset(ptr
, 0, (ed2
->num_bytes
>> Vcb
->sector_shift
) * Vcb
->csum_size
);
5322 ptr
+= (ed2
->num_bytes
>> Vcb
->sector_shift
) * Vcb
->csum_size
;
5325 last_off
= ext
->offset
+ ed2
->num_bytes
;
5330 if (buf
->num_sectors
> last_off
>> Vcb
->sector_shift
) {
5331 uint64_t sparse_extents
= buf
->num_sectors
- (last_off
>> Vcb
->sector_shift
);
5333 add_csum_sparse_extents(Vcb
, sparse_extents
, &ptr
, sparse_hash_found
, sparse_hash
);
5336 *retlen
= offsetof(btrfs_csum_info
, data
[0]) + (buf
->csum_length
* buf
->num_sectors
);
5337 Status
= STATUS_SUCCESS
;
5339 ExReleaseResourceLite(fcb
->Header
.Resource
);
5345 NTSTATUS
fsctl_request(PDEVICE_OBJECT DeviceObject
, PIRP
* Pirp
, uint32_t type
) {
5347 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
5350 if (IrpSp
->FileObject
&& IrpSp
->FileObject
->FsContext
) {
5351 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
5353 if (Vcb
->type
== VCB_TYPE_FS
)
5354 FsRtlCheckOplock(fcb_oplock(IrpSp
->FileObject
->FsContext
), Irp
, NULL
, NULL
, NULL
);
5358 case FSCTL_REQUEST_OPLOCK_LEVEL_1
:
5359 case FSCTL_REQUEST_OPLOCK_LEVEL_2
:
5360 case FSCTL_REQUEST_BATCH_OPLOCK
:
5361 case FSCTL_OPLOCK_BREAK_ACKNOWLEDGE
:
5362 case FSCTL_OPBATCH_ACK_CLOSE_PENDING
:
5363 case FSCTL_OPLOCK_BREAK_NOTIFY
:
5364 case FSCTL_OPLOCK_BREAK_ACK_NO_2
:
5365 case FSCTL_REQUEST_FILTER_OPLOCK
:
5366 #if (NTDDI_VERSION >= NTDDI_WIN7)
5367 case FSCTL_REQUEST_OPLOCK
:
5369 Status
= fsctl_oplock(DeviceObject
->DeviceExtension
, Pirp
);
5372 case FSCTL_LOCK_VOLUME
:
5373 Status
= lock_volume(DeviceObject
->DeviceExtension
, Irp
);
5376 case FSCTL_UNLOCK_VOLUME
:
5377 Status
= unlock_volume(DeviceObject
->DeviceExtension
, Irp
);
5380 case FSCTL_DISMOUNT_VOLUME
:
5381 Status
= dismount_volume(DeviceObject
->DeviceExtension
, false, Irp
);
5384 case FSCTL_IS_VOLUME_MOUNTED
:
5385 Status
= is_volume_mounted(DeviceObject
->DeviceExtension
, Irp
);
5388 case FSCTL_IS_PATHNAME_VALID
:
5389 WARN("STUB: FSCTL_IS_PATHNAME_VALID\n");
5390 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5393 case FSCTL_MARK_VOLUME_DIRTY
:
5394 WARN("STUB: FSCTL_MARK_VOLUME_DIRTY\n");
5395 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5398 case FSCTL_QUERY_RETRIEVAL_POINTERS
:
5399 WARN("STUB: FSCTL_QUERY_RETRIEVAL_POINTERS\n");
5400 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5403 case FSCTL_GET_COMPRESSION
:
5404 Status
= get_compression(Irp
);
5407 case FSCTL_SET_COMPRESSION
:
5408 Status
= set_compression(Irp
);
5411 case FSCTL_SET_BOOTLOADER_ACCESSED
:
5412 WARN("STUB: FSCTL_SET_BOOTLOADER_ACCESSED\n");
5413 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5416 case FSCTL_INVALIDATE_VOLUMES
:
5417 Status
= invalidate_volumes(Irp
);
5420 case FSCTL_QUERY_FAT_BPB
:
5421 WARN("STUB: FSCTL_QUERY_FAT_BPB\n");
5422 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5425 case FSCTL_FILESYSTEM_GET_STATISTICS
:
5426 Status
= fs_get_statistics(Irp
->AssociatedIrp
.SystemBuffer
, IrpSp
->Parameters
.FileSystemControl
.OutputBufferLength
, &Irp
->IoStatus
.Information
);
5429 case FSCTL_GET_NTFS_VOLUME_DATA
:
5430 WARN("STUB: FSCTL_GET_NTFS_VOLUME_DATA\n");
5431 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5434 case FSCTL_GET_NTFS_FILE_RECORD
:
5435 WARN("STUB: FSCTL_GET_NTFS_FILE_RECORD\n");
5436 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5439 case FSCTL_GET_VOLUME_BITMAP
:
5440 WARN("STUB: FSCTL_GET_VOLUME_BITMAP\n");
5441 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5444 case FSCTL_GET_RETRIEVAL_POINTERS
:
5445 Status
= get_retrieval_pointers(DeviceObject
->DeviceExtension
, IrpSp
->FileObject
,
5446 IrpSp
->Parameters
.FileSystemControl
.Type3InputBuffer
,
5447 IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
,
5448 Irp
->UserBuffer
, IrpSp
->Parameters
.FileSystemControl
.OutputBufferLength
,
5449 &Irp
->IoStatus
.Information
);
5452 case FSCTL_MOVE_FILE
:
5453 WARN("STUB: FSCTL_MOVE_FILE\n");
5454 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5457 case FSCTL_IS_VOLUME_DIRTY
:
5458 Status
= is_volume_dirty(DeviceObject
->DeviceExtension
, Irp
);
5461 case FSCTL_ALLOW_EXTENDED_DASD_IO
:
5462 Status
= allow_extended_dasd_io(DeviceObject
->DeviceExtension
, IrpSp
->FileObject
);
5465 case FSCTL_FIND_FILES_BY_SID
:
5466 WARN("STUB: FSCTL_FIND_FILES_BY_SID\n");
5467 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5470 case FSCTL_SET_OBJECT_ID
:
5471 WARN("STUB: FSCTL_SET_OBJECT_ID\n");
5472 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5475 case FSCTL_GET_OBJECT_ID
:
5476 Status
= get_object_id(IrpSp
->FileObject
, Irp
->UserBuffer
, IrpSp
->Parameters
.FileSystemControl
.OutputBufferLength
,
5477 &Irp
->IoStatus
.Information
);
5480 case FSCTL_DELETE_OBJECT_ID
:
5481 WARN("STUB: FSCTL_DELETE_OBJECT_ID\n");
5482 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5485 case FSCTL_SET_REPARSE_POINT
:
5486 Status
= set_reparse_point(Irp
);
5489 case FSCTL_GET_REPARSE_POINT
:
5490 Status
= get_reparse_point(IrpSp
->FileObject
, Irp
->AssociatedIrp
.SystemBuffer
,
5491 IrpSp
->Parameters
.FileSystemControl
.OutputBufferLength
, &Irp
->IoStatus
.Information
);
5494 case FSCTL_DELETE_REPARSE_POINT
:
5495 Status
= delete_reparse_point(Irp
);
5498 case FSCTL_ENUM_USN_DATA
:
5499 WARN("STUB: FSCTL_ENUM_USN_DATA\n");
5500 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5503 case FSCTL_SECURITY_ID_CHECK
:
5504 WARN("STUB: FSCTL_SECURITY_ID_CHECK\n");
5505 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5508 case FSCTL_READ_USN_JOURNAL
:
5509 WARN("STUB: FSCTL_READ_USN_JOURNAL\n");
5510 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5513 case FSCTL_SET_OBJECT_ID_EXTENDED
:
5514 WARN("STUB: FSCTL_SET_OBJECT_ID_EXTENDED\n");
5515 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5518 case FSCTL_CREATE_OR_GET_OBJECT_ID
:
5519 Status
= get_object_id(IrpSp
->FileObject
, Irp
->UserBuffer
, IrpSp
->Parameters
.FileSystemControl
.OutputBufferLength
,
5520 &Irp
->IoStatus
.Information
);
5523 case FSCTL_SET_SPARSE
:
5524 Status
= set_sparse(DeviceObject
->DeviceExtension
, IrpSp
->FileObject
, Irp
->AssociatedIrp
.SystemBuffer
,
5525 IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
, Irp
);
5528 case FSCTL_SET_ZERO_DATA
:
5529 Status
= set_zero_data(DeviceObject
->DeviceExtension
, IrpSp
->FileObject
, Irp
->AssociatedIrp
.SystemBuffer
,
5530 IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
, Irp
);
5533 case FSCTL_QUERY_ALLOCATED_RANGES
:
5534 Status
= query_ranges(IrpSp
->FileObject
, IrpSp
->Parameters
.FileSystemControl
.Type3InputBuffer
,
5535 IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
, Irp
->UserBuffer
,
5536 IrpSp
->Parameters
.FileSystemControl
.OutputBufferLength
, &Irp
->IoStatus
.Information
);
5539 case FSCTL_ENABLE_UPGRADE
:
5540 WARN("STUB: FSCTL_ENABLE_UPGRADE\n");
5541 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5544 case FSCTL_SET_ENCRYPTION
:
5545 WARN("STUB: FSCTL_SET_ENCRYPTION\n");
5546 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5549 case FSCTL_ENCRYPTION_FSCTL_IO
:
5550 WARN("STUB: FSCTL_ENCRYPTION_FSCTL_IO\n");
5551 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5554 case FSCTL_WRITE_RAW_ENCRYPTED
:
5555 WARN("STUB: FSCTL_WRITE_RAW_ENCRYPTED\n");
5556 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5559 case FSCTL_READ_RAW_ENCRYPTED
:
5560 WARN("STUB: FSCTL_READ_RAW_ENCRYPTED\n");
5561 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5564 case FSCTL_CREATE_USN_JOURNAL
:
5565 WARN("STUB: FSCTL_CREATE_USN_JOURNAL\n");
5566 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5569 case FSCTL_READ_FILE_USN_DATA
:
5570 WARN("STUB: FSCTL_READ_FILE_USN_DATA\n");
5571 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5574 case FSCTL_WRITE_USN_CLOSE_RECORD
:
5575 WARN("STUB: FSCTL_WRITE_USN_CLOSE_RECORD\n");
5576 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5579 case FSCTL_EXTEND_VOLUME
:
5580 WARN("STUB: FSCTL_EXTEND_VOLUME\n");
5581 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5584 case FSCTL_QUERY_USN_JOURNAL
:
5585 WARN("STUB: FSCTL_QUERY_USN_JOURNAL\n");
5586 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5589 case FSCTL_DELETE_USN_JOURNAL
:
5590 WARN("STUB: FSCTL_DELETE_USN_JOURNAL\n");
5591 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5594 case FSCTL_MARK_HANDLE
:
5595 WARN("STUB: FSCTL_MARK_HANDLE\n");
5596 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5599 case FSCTL_SIS_COPYFILE
:
5600 WARN("STUB: FSCTL_SIS_COPYFILE\n");
5601 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5604 case FSCTL_SIS_LINK_FILES
:
5605 WARN("STUB: FSCTL_SIS_LINK_FILES\n");
5606 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5609 case FSCTL_RECALL_FILE
:
5610 WARN("STUB: FSCTL_RECALL_FILE\n");
5611 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5614 case FSCTL_READ_FROM_PLEX
:
5615 WARN("STUB: FSCTL_READ_FROM_PLEX\n");
5616 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5619 case FSCTL_FILE_PREFETCH
:
5620 WARN("STUB: FSCTL_FILE_PREFETCH\n");
5621 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5624 #if _WIN32_WINNT >= 0x0600
5625 case FSCTL_MAKE_MEDIA_COMPATIBLE
:
5626 WARN("STUB: FSCTL_MAKE_MEDIA_COMPATIBLE\n");
5627 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5630 case FSCTL_SET_DEFECT_MANAGEMENT
:
5631 WARN("STUB: FSCTL_SET_DEFECT_MANAGEMENT\n");
5632 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5635 case FSCTL_QUERY_SPARING_INFO
:
5636 WARN("STUB: FSCTL_QUERY_SPARING_INFO\n");
5637 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5640 case FSCTL_QUERY_ON_DISK_VOLUME_INFO
:
5641 WARN("STUB: FSCTL_QUERY_ON_DISK_VOLUME_INFO\n");
5642 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5645 case FSCTL_SET_VOLUME_COMPRESSION_STATE
:
5646 WARN("STUB: FSCTL_SET_VOLUME_COMPRESSION_STATE\n");
5647 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5650 case FSCTL_TXFS_MODIFY_RM
:
5651 WARN("STUB: FSCTL_TXFS_MODIFY_RM\n");
5652 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5655 case FSCTL_TXFS_QUERY_RM_INFORMATION
:
5656 WARN("STUB: FSCTL_TXFS_QUERY_RM_INFORMATION\n");
5657 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5660 case FSCTL_TXFS_ROLLFORWARD_REDO
:
5661 WARN("STUB: FSCTL_TXFS_ROLLFORWARD_REDO\n");
5662 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5665 case FSCTL_TXFS_ROLLFORWARD_UNDO
:
5666 WARN("STUB: FSCTL_TXFS_ROLLFORWARD_UNDO\n");
5667 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5670 case FSCTL_TXFS_START_RM
:
5671 WARN("STUB: FSCTL_TXFS_START_RM\n");
5672 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5675 case FSCTL_TXFS_SHUTDOWN_RM
:
5676 WARN("STUB: FSCTL_TXFS_SHUTDOWN_RM\n");
5677 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5680 case FSCTL_TXFS_READ_BACKUP_INFORMATION
:
5681 WARN("STUB: FSCTL_TXFS_READ_BACKUP_INFORMATION\n");
5682 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5685 case FSCTL_TXFS_WRITE_BACKUP_INFORMATION
:
5686 WARN("STUB: FSCTL_TXFS_WRITE_BACKUP_INFORMATION\n");
5687 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5690 case FSCTL_TXFS_CREATE_SECONDARY_RM
:
5691 WARN("STUB: FSCTL_TXFS_CREATE_SECONDARY_RM\n");
5692 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5695 case FSCTL_TXFS_GET_METADATA_INFO
:
5696 WARN("STUB: FSCTL_TXFS_GET_METADATA_INFO\n");
5697 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5700 case FSCTL_TXFS_GET_TRANSACTED_VERSION
:
5701 WARN("STUB: FSCTL_TXFS_GET_TRANSACTED_VERSION\n");
5702 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5705 case FSCTL_TXFS_SAVEPOINT_INFORMATION
:
5706 WARN("STUB: FSCTL_TXFS_SAVEPOINT_INFORMATION\n");
5707 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5710 case FSCTL_TXFS_CREATE_MINIVERSION
:
5711 WARN("STUB: FSCTL_TXFS_CREATE_MINIVERSION\n");
5712 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5715 case FSCTL_TXFS_TRANSACTION_ACTIVE
:
5716 WARN("STUB: FSCTL_TXFS_TRANSACTION_ACTIVE\n");
5717 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5720 case FSCTL_SET_ZERO_ON_DEALLOCATION
:
5721 WARN("STUB: FSCTL_SET_ZERO_ON_DEALLOCATION\n");
5722 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5725 case FSCTL_SET_REPAIR
:
5726 WARN("STUB: FSCTL_SET_REPAIR\n");
5727 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5730 case FSCTL_GET_REPAIR
:
5731 WARN("STUB: FSCTL_GET_REPAIR\n");
5732 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5735 case FSCTL_WAIT_FOR_REPAIR
:
5736 WARN("STUB: FSCTL_WAIT_FOR_REPAIR\n");
5737 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5740 case FSCTL_INITIATE_REPAIR
:
5741 WARN("STUB: FSCTL_INITIATE_REPAIR\n");
5742 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5745 case FSCTL_CSC_INTERNAL
:
5746 WARN("STUB: FSCTL_CSC_INTERNAL\n");
5747 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5750 case FSCTL_SHRINK_VOLUME
:
5751 WARN("STUB: FSCTL_SHRINK_VOLUME\n");
5752 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5755 case FSCTL_SET_SHORT_NAME_BEHAVIOR
:
5756 WARN("STUB: FSCTL_SET_SHORT_NAME_BEHAVIOR\n");
5757 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5760 case FSCTL_DFSR_SET_GHOST_HANDLE_STATE
:
5761 WARN("STUB: FSCTL_DFSR_SET_GHOST_HANDLE_STATE\n");
5762 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5765 case FSCTL_TXFS_LIST_TRANSACTION_LOCKED_FILES
:
5766 WARN("STUB: FSCTL_TXFS_LIST_TRANSACTION_LOCKED_FILES\n");
5767 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5770 case FSCTL_TXFS_LIST_TRANSACTIONS
:
5771 WARN("STUB: FSCTL_TXFS_LIST_TRANSACTIONS\n");
5772 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5775 case FSCTL_QUERY_PAGEFILE_ENCRYPTION
:
5776 WARN("STUB: FSCTL_QUERY_PAGEFILE_ENCRYPTION\n");
5777 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5780 case FSCTL_RESET_VOLUME_ALLOCATION_HINTS
:
5781 WARN("STUB: FSCTL_RESET_VOLUME_ALLOCATION_HINTS\n");
5782 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5785 case FSCTL_TXFS_READ_BACKUP_INFORMATION2
:
5786 WARN("STUB: FSCTL_TXFS_READ_BACKUP_INFORMATION2\n");
5787 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5790 case FSCTL_CSV_CONTROL
:
5791 WARN("STUB: FSCTL_CSV_CONTROL\n");
5792 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5795 // TRACE rather than WARN because Windows 10 spams this undocumented fsctl
5796 case FSCTL_QUERY_VOLUME_CONTAINER_STATE
:
5797 TRACE("STUB: FSCTL_QUERY_VOLUME_CONTAINER_STATE\n");
5798 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5801 case FSCTL_GET_INTEGRITY_INFORMATION
:
5802 Status
= get_integrity_information(DeviceObject
->DeviceExtension
, IrpSp
->FileObject
, map_user_buffer(Irp
, NormalPagePriority
),
5803 IrpSp
->Parameters
.FileSystemControl
.OutputBufferLength
);
5806 case FSCTL_SET_INTEGRITY_INFORMATION
:
5807 Status
= set_integrity_information(IrpSp
->FileObject
, Irp
->AssociatedIrp
.SystemBuffer
, IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
);
5810 case FSCTL_DUPLICATE_EXTENTS_TO_FILE
:
5811 Status
= duplicate_extents(DeviceObject
->DeviceExtension
, IrpSp
->FileObject
, Irp
->AssociatedIrp
.SystemBuffer
,
5812 IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
, Irp
);
5815 case FSCTL_BTRFS_GET_FILE_IDS
:
5816 Status
= get_file_ids(IrpSp
->FileObject
, map_user_buffer(Irp
, NormalPagePriority
), IrpSp
->Parameters
.FileSystemControl
.OutputBufferLength
);
5819 case FSCTL_BTRFS_CREATE_SUBVOL
:
5820 Status
= create_subvol(DeviceObject
->DeviceExtension
, IrpSp
->FileObject
, Irp
->AssociatedIrp
.SystemBuffer
, IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
, Irp
);
5823 case FSCTL_BTRFS_CREATE_SNAPSHOT
:
5824 Status
= create_snapshot(DeviceObject
->DeviceExtension
, IrpSp
->FileObject
, Irp
->AssociatedIrp
.SystemBuffer
, IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
, Irp
);
5827 case FSCTL_BTRFS_GET_INODE_INFO
:
5828 Status
= get_inode_info(IrpSp
->FileObject
, map_user_buffer(Irp
, NormalPagePriority
), IrpSp
->Parameters
.FileSystemControl
.OutputBufferLength
);
5831 case FSCTL_BTRFS_SET_INODE_INFO
:
5832 Status
= set_inode_info(IrpSp
->FileObject
, Irp
->AssociatedIrp
.SystemBuffer
, IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
, Irp
);
5835 case FSCTL_BTRFS_GET_DEVICES
:
5836 Status
= get_devices(DeviceObject
->DeviceExtension
, map_user_buffer(Irp
, NormalPagePriority
), IrpSp
->Parameters
.FileSystemControl
.OutputBufferLength
);
5839 case FSCTL_BTRFS_GET_USAGE
:
5840 Status
= get_usage(DeviceObject
->DeviceExtension
, map_user_buffer(Irp
, NormalPagePriority
), IrpSp
->Parameters
.FileSystemControl
.OutputBufferLength
, Irp
);
5843 case FSCTL_BTRFS_START_BALANCE
:
5844 Status
= start_balance(DeviceObject
->DeviceExtension
, Irp
->AssociatedIrp
.SystemBuffer
, IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
, Irp
->RequestorMode
);
5847 case FSCTL_BTRFS_QUERY_BALANCE
:
5848 Status
= query_balance(DeviceObject
->DeviceExtension
, map_user_buffer(Irp
, NormalPagePriority
), IrpSp
->Parameters
.FileSystemControl
.OutputBufferLength
);
5851 case FSCTL_BTRFS_PAUSE_BALANCE
:
5852 Status
= pause_balance(DeviceObject
->DeviceExtension
, Irp
->RequestorMode
);
5855 case FSCTL_BTRFS_RESUME_BALANCE
:
5856 Status
= resume_balance(DeviceObject
->DeviceExtension
, Irp
->RequestorMode
);
5859 case FSCTL_BTRFS_STOP_BALANCE
:
5860 Status
= stop_balance(DeviceObject
->DeviceExtension
, Irp
->RequestorMode
);
5863 case FSCTL_BTRFS_ADD_DEVICE
:
5864 Status
= add_device(DeviceObject
->DeviceExtension
, Irp
, Irp
->RequestorMode
);
5867 case FSCTL_BTRFS_REMOVE_DEVICE
:
5868 Status
= remove_device(DeviceObject
->DeviceExtension
, Irp
->AssociatedIrp
.SystemBuffer
, IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
, Irp
->RequestorMode
);
5871 case FSCTL_BTRFS_GET_UUID
:
5872 Status
= query_uuid(DeviceObject
->DeviceExtension
, map_user_buffer(Irp
, NormalPagePriority
), IrpSp
->Parameters
.FileSystemControl
.OutputBufferLength
);
5875 case FSCTL_BTRFS_START_SCRUB
:
5876 Status
= start_scrub(DeviceObject
->DeviceExtension
, Irp
->RequestorMode
);
5879 case FSCTL_BTRFS_QUERY_SCRUB
:
5880 Status
= query_scrub(DeviceObject
->DeviceExtension
, Irp
->RequestorMode
, map_user_buffer(Irp
, NormalPagePriority
), IrpSp
->Parameters
.FileSystemControl
.OutputBufferLength
);
5883 case FSCTL_BTRFS_PAUSE_SCRUB
:
5884 Status
= pause_scrub(DeviceObject
->DeviceExtension
, Irp
->RequestorMode
);
5887 case FSCTL_BTRFS_RESUME_SCRUB
:
5888 Status
= resume_scrub(DeviceObject
->DeviceExtension
, Irp
->RequestorMode
);
5891 case FSCTL_BTRFS_STOP_SCRUB
:
5892 Status
= stop_scrub(DeviceObject
->DeviceExtension
, Irp
->RequestorMode
);
5895 case FSCTL_BTRFS_RESET_STATS
:
5896 Status
= reset_stats(DeviceObject
->DeviceExtension
, Irp
->AssociatedIrp
.SystemBuffer
, IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
, Irp
->RequestorMode
);
5899 case FSCTL_BTRFS_MKNOD
:
5900 Status
= mknod(DeviceObject
->DeviceExtension
, IrpSp
->FileObject
, Irp
->AssociatedIrp
.SystemBuffer
, IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
, Irp
);
5903 case FSCTL_BTRFS_RECEIVED_SUBVOL
:
5904 Status
= recvd_subvol(DeviceObject
->DeviceExtension
, IrpSp
->FileObject
, Irp
->AssociatedIrp
.SystemBuffer
,
5905 IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
, Irp
->RequestorMode
);
5908 case FSCTL_BTRFS_GET_XATTRS
:
5909 Status
= fsctl_get_xattrs(DeviceObject
->DeviceExtension
, IrpSp
->FileObject
, Irp
->UserBuffer
, IrpSp
->Parameters
.FileSystemControl
.OutputBufferLength
, Irp
->RequestorMode
);
5912 case FSCTL_BTRFS_SET_XATTR
:
5913 Status
= fsctl_set_xattr(DeviceObject
->DeviceExtension
, IrpSp
->FileObject
, Irp
->AssociatedIrp
.SystemBuffer
,
5914 IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
, Irp
);
5917 case FSCTL_BTRFS_RESERVE_SUBVOL
:
5918 Status
= reserve_subvol(DeviceObject
->DeviceExtension
, IrpSp
->FileObject
, Irp
);
5921 case FSCTL_BTRFS_FIND_SUBVOL
:
5922 Status
= find_subvol(DeviceObject
->DeviceExtension
, Irp
->AssociatedIrp
.SystemBuffer
, IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
,
5923 Irp
->UserBuffer
, IrpSp
->Parameters
.FileSystemControl
.OutputBufferLength
, Irp
);
5926 case FSCTL_BTRFS_SEND_SUBVOL
:
5927 Status
= send_subvol(DeviceObject
->DeviceExtension
, Irp
->AssociatedIrp
.SystemBuffer
, IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
,
5928 IrpSp
->FileObject
, Irp
);
5931 case FSCTL_BTRFS_READ_SEND_BUFFER
:
5932 Status
= read_send_buffer(DeviceObject
->DeviceExtension
, IrpSp
->FileObject
, map_user_buffer(Irp
, NormalPagePriority
), IrpSp
->Parameters
.FileSystemControl
.OutputBufferLength
,
5933 &Irp
->IoStatus
.Information
, Irp
->RequestorMode
);
5936 case FSCTL_BTRFS_RESIZE
:
5937 Status
= resize_device(DeviceObject
->DeviceExtension
, Irp
->AssociatedIrp
.SystemBuffer
,
5938 IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
, Irp
);
5941 case FSCTL_BTRFS_GET_CSUM_INFO
:
5942 Status
= get_csum_info(DeviceObject
->DeviceExtension
, IrpSp
->FileObject
, Irp
->AssociatedIrp
.SystemBuffer
,
5943 IrpSp
->Parameters
.FileSystemControl
.OutputBufferLength
, &Irp
->IoStatus
.Information
,
5944 Irp
->RequestorMode
);
5948 WARN("unknown control code %lx (DeviceType = %lx, Access = %lx, Function = %lx, Method = %lx)\n",
5949 IrpSp
->Parameters
.FileSystemControl
.FsControlCode
, (IrpSp
->Parameters
.FileSystemControl
.FsControlCode
& 0xff0000) >> 16,
5950 (IrpSp
->Parameters
.FileSystemControl
.FsControlCode
& 0xc000) >> 14, (IrpSp
->Parameters
.FileSystemControl
.FsControlCode
& 0x3ffc) >> 2,
5951 IrpSp
->Parameters
.FileSystemControl
.FsControlCode
& 0x3);
5952 Status
= STATUS_INVALID_DEVICE_REQUEST
;