1 /* Copyright (c) Mark Harmstone 2016-17
3 * This file is part of WinBtrfs.
5 * WinBtrfs is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public Licence as published by
7 * the Free Software Foundation, either version 3 of the Licence, or
8 * (at your option) any later version.
10 * WinBtrfs is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Lesser General Public Licence for more details.
15 * You should have received a copy of the GNU Lesser General Public Licence
16 * along with WinBtrfs. If not, see <http://www.gnu.org/licenses/>. */
18 #include "btrfs_drv.h"
19 #include "btrfsioctl.h"
26 #ifndef FSCTL_CSV_CONTROL
27 #define FSCTL_CSV_CONTROL CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 181, METHOD_BUFFERED, FILE_ANY_ACCESS)
30 #ifndef FSCTL_QUERY_VOLUME_CONTAINER_STATE
31 #define FSCTL_QUERY_VOLUME_CONTAINER_STATE CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 228, METHOD_BUFFERED, FILE_ANY_ACCESS)
36 #define SEF_AVOID_PRIVILEGE_CHECK 0x08 // on MSDN but not in any header files(?)
38 #ifndef _MSC_VER // not in mingw yet
39 #define DEVICE_DSM_FLAG_TRIM_NOT_FS_ALLOCATED 0x80000000
42 #define SEF_SACL_AUTO_INHERIT 0x02
44 extern LIST_ENTRY VcbList
;
45 extern ERESOURCE global_loading_lock
;
46 extern PDRIVER_OBJECT drvobj
;
48 static void mark_subvol_dirty(device_extension
* Vcb
, root
* r
);
50 static NTSTATUS
get_file_ids(PFILE_OBJECT FileObject
, void* data
, ULONG length
) {
51 btrfs_get_file_ids
* bgfi
;
54 if (length
< sizeof(btrfs_get_file_ids
))
55 return STATUS_BUFFER_OVERFLOW
;
58 return STATUS_INVALID_PARAMETER
;
60 fcb
= FileObject
->FsContext
;
63 return STATUS_INVALID_PARAMETER
;
67 bgfi
->subvol
= fcb
->subvol
->id
;
68 bgfi
->inode
= fcb
->inode
;
69 bgfi
->top
= fcb
->Vcb
->root_fileref
->fcb
== fcb
? TRUE
: FALSE
;
71 return STATUS_SUCCESS
;
74 static void get_uuid(BTRFS_UUID
* uuid
) {
78 seed
= KeQueryPerformanceCounter(NULL
);
80 for (i
= 0; i
< 16; i
+=2) {
81 ULONG rand
= RtlRandomEx(&seed
.LowPart
);
83 uuid
->uuid
[i
] = (rand
& 0xff00) >> 8;
84 uuid
->uuid
[i
+1] = rand
& 0xff;
88 static NTSTATUS
snapshot_tree_copy(device_extension
* Vcb
, UINT64 addr
, root
* subvol
, UINT64
* newaddr
, PIRP Irp
, LIST_ENTRY
* rollback
) {
91 write_data_context wtc
;
97 buf
= ExAllocatePoolWithTag(NonPagedPool
, Vcb
->superblock
.node_size
, ALLOC_TAG
);
99 ERR("out of memory\n");
100 return STATUS_INSUFFICIENT_RESOURCES
;
103 wtc
.parity1
= wtc
.parity2
= wtc
.scratch
= NULL
;
104 wtc
.mdl
= wtc
.parity1_mdl
= wtc
.parity2_mdl
= NULL
;
106 Status
= read_data(Vcb
, addr
, Vcb
->superblock
.node_size
, NULL
, TRUE
, buf
, NULL
, NULL
, Irp
, 0, FALSE
, NormalPagePriority
);
107 if (!NT_SUCCESS(Status
)) {
108 ERR("read_data returned %08x\n", Status
);
112 th
= (tree_header
*)buf
;
114 RtlZeroMemory(&t
, sizeof(tree
));
116 t
.header
.level
= th
->level
;
117 t
.header
.tree_id
= t
.root
->id
;
119 Status
= get_tree_new_address(Vcb
, &t
, Irp
, rollback
);
120 if (!NT_SUCCESS(Status
)) {
121 ERR("get_tree_new_address returned %08x\n", Status
);
125 if (!t
.has_new_address
) {
126 ERR("tree new address not set\n");
127 Status
= STATUS_INTERNAL_ERROR
;
131 c
= get_chunk_from_address(Vcb
, t
.new_address
);
134 c
->used
+= Vcb
->superblock
.node_size
;
136 ERR("could not find chunk for address %llx\n", t
.new_address
);
137 Status
= STATUS_INTERNAL_ERROR
;
141 th
->address
= t
.new_address
;
142 th
->tree_id
= subvol
->id
;
143 th
->generation
= Vcb
->superblock
.generation
;
144 th
->fs_uuid
= Vcb
->superblock
.uuid
;
146 if (th
->level
== 0) {
148 leaf_node
* ln
= (leaf_node
*)&th
[1];
150 for (i
= 0; i
< th
->num_items
; i
++) {
151 if (ln
[i
].key
.obj_type
== TYPE_EXTENT_DATA
&& ln
[i
].size
>= sizeof(EXTENT_DATA
) && ln
[i
].offset
+ ln
[i
].size
<= Vcb
->superblock
.node_size
- sizeof(tree_header
)) {
152 EXTENT_DATA
* ed
= (EXTENT_DATA
*)(((UINT8
*)&th
[1]) + ln
[i
].offset
);
154 if ((ed
->type
== EXTENT_TYPE_REGULAR
|| ed
->type
== EXTENT_TYPE_PREALLOC
) && ln
[i
].size
>= sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
)) {
155 EXTENT_DATA2
* ed2
= (EXTENT_DATA2
*)&ed
->data
[0];
157 if (ed2
->size
!= 0) { // not sparse
158 Status
= increase_extent_refcount_data(Vcb
, ed2
->address
, ed2
->size
, subvol
->id
, ln
[i
].key
.obj_id
, ln
[i
].key
.offset
- ed2
->offset
, 1, Irp
);
160 if (!NT_SUCCESS(Status
)) {
161 ERR("increase_extent_refcount_data returned %08x\n", Status
);
170 internal_node
* in
= (internal_node
*)&th
[1];
172 for (i
= 0; i
< th
->num_items
; i
++) {
175 tbr
.offset
= subvol
->id
;
177 Status
= increase_extent_refcount(Vcb
, in
[i
].address
, Vcb
->superblock
.node_size
, TYPE_TREE_BLOCK_REF
, &tbr
, NULL
, th
->level
- 1, Irp
);
178 if (!NT_SUCCESS(Status
)) {
179 ERR("increase_extent_refcount returned %08x\n", Status
);
185 *((UINT32
*)buf
) = ~calc_crc32c(0xffffffff, (UINT8
*)&th
->fs_uuid
, Vcb
->superblock
.node_size
- sizeof(th
->csum
));
187 KeInitializeEvent(&wtc
.Event
, NotificationEvent
, FALSE
);
188 InitializeListHead(&wtc
.stripes
);
189 wtc
.stripes_left
= 0;
191 Status
= write_data(Vcb
, t
.new_address
, buf
, Vcb
->superblock
.node_size
, &wtc
, NULL
, NULL
, FALSE
, 0, NormalPagePriority
);
192 if (!NT_SUCCESS(Status
)) {
193 ERR("write_data returned %08x\n", Status
);
197 if (wtc
.stripes
.Flink
!= &wtc
.stripes
) {
198 BOOL need_wait
= FALSE
;
200 // launch writes and wait
201 le
= wtc
.stripes
.Flink
;
202 while (le
!= &wtc
.stripes
) {
203 write_data_stripe
* stripe
= CONTAINING_RECORD(le
, write_data_stripe
, list_entry
);
205 if (stripe
->status
!= WriteDataStatus_Ignore
) {
207 IoCallDriver(stripe
->device
->devobj
, stripe
->Irp
);
214 KeWaitForSingleObject(&wtc
.Event
, Executive
, KernelMode
, FALSE
, NULL
);
216 le
= wtc
.stripes
.Flink
;
217 while (le
!= &wtc
.stripes
) {
218 write_data_stripe
* stripe
= CONTAINING_RECORD(le
, write_data_stripe
, list_entry
);
220 if (stripe
->status
!= WriteDataStatus_Ignore
&& !NT_SUCCESS(stripe
->iosb
.Status
)) {
221 Status
= stripe
->iosb
.Status
;
222 log_device_error(Vcb
, stripe
->device
, BTRFS_DEV_STAT_WRITE_ERRORS
);
229 free_write_data_stripes(&wtc
);
233 if (NT_SUCCESS(Status
))
234 *newaddr
= t
.new_address
;
244 void flush_subvol_fcbs(root
* subvol
) {
245 LIST_ENTRY
* le
= subvol
->fcbs
.Flink
;
247 if (IsListEmpty(&subvol
->fcbs
))
250 while (le
!= &subvol
->fcbs
) {
251 struct _fcb
* fcb
= CONTAINING_RECORD(le
, struct _fcb
, list_entry
);
252 IO_STATUS_BLOCK iosb
;
254 if (fcb
->type
!= BTRFS_TYPE_DIRECTORY
&& !fcb
->deleted
)
255 CcFlushCache(&fcb
->nonpaged
->segment_object
, NULL
, 0, &iosb
);
261 static NTSTATUS
do_create_snapshot(device_extension
* Vcb
, PFILE_OBJECT parent
, fcb
* subvol_fcb
, PANSI_STRING utf8
, PUNICODE_STRING name
, BOOL readonly
, PIRP Irp
) {
265 root
*r
, *subvol
= subvol_fcb
->subvol
;
268 UINT64 address
, *root_num
;
271 fcb
* fcb
= parent
->FsContext
;
272 ccb
* ccb
= parent
->FsContext2
;
274 file_ref
*fileref
, *fr
;
275 dir_child
* dc
= NULL
;
278 ERR("error - ccb was NULL\n");
279 return STATUS_INTERNAL_ERROR
;
282 if (!(ccb
->access
& FILE_ADD_SUBDIRECTORY
)) {
283 WARN("insufficient privileges\n");
284 return STATUS_ACCESS_DENIED
;
287 fileref
= ccb
->fileref
;
289 if (fileref
->fcb
== Vcb
->dummy_fcb
)
290 return STATUS_ACCESS_DENIED
;
292 // flush open files on this subvol
294 flush_subvol_fcbs(subvol
);
299 Status
= do_write(Vcb
, Irp
);
301 Status
= STATUS_SUCCESS
;
305 if (!NT_SUCCESS(Status
)) {
306 ERR("do_write returned %08x\n", Status
);
310 InitializeListHead(&rollback
);
314 id
= InterlockedIncrement64(&Vcb
->root_root
->lastinode
);
315 Status
= create_root(Vcb
, id
, &r
, TRUE
, Vcb
->superblock
.generation
, Irp
);
317 if (!NT_SUCCESS(Status
)) {
318 ERR("create_root returned %08x\n", Status
);
322 r
->lastinode
= subvol
->lastinode
;
324 if (!Vcb
->uuid_root
) {
327 TRACE("uuid root doesn't exist, creating it\n");
329 Status
= create_root(Vcb
, BTRFS_ROOT_UUID
, &uuid_root
, FALSE
, 0, Irp
);
331 if (!NT_SUCCESS(Status
)) {
332 ERR("create_root returned %08x\n", Status
);
336 Vcb
->uuid_root
= uuid_root
;
339 root_num
= ExAllocatePoolWithTag(PagedPool
, sizeof(UINT64
), ALLOC_TAG
);
341 ERR("out of memory\n");
342 Status
= STATUS_INSUFFICIENT_RESOURCES
;
349 get_uuid(&r
->root_item
.uuid
);
351 RtlCopyMemory(&searchkey
.obj_id
, &r
->root_item
.uuid
, sizeof(UINT64
));
352 searchkey
.obj_type
= TYPE_SUBVOL_UUID
;
353 RtlCopyMemory(&searchkey
.offset
, &r
->root_item
.uuid
.uuid
[sizeof(UINT64
)], sizeof(UINT64
));
355 Status
= find_item(Vcb
, Vcb
->uuid_root
, &tp
, &searchkey
, FALSE
, Irp
);
356 } while (NT_SUCCESS(Status
) && !keycmp(searchkey
, tp
.item
->key
));
360 Status
= insert_tree_item(Vcb
, Vcb
->uuid_root
, searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
, root_num
, sizeof(UINT64
), NULL
, Irp
);
361 if (!NT_SUCCESS(Status
)) {
362 ERR("insert_tree_item returned %08x\n", Status
);
363 ExFreePool(root_num
);
367 searchkey
.obj_id
= r
->id
;
368 searchkey
.obj_type
= TYPE_ROOT_ITEM
;
369 searchkey
.offset
= 0xffffffffffffffff;
371 Status
= find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, FALSE
, Irp
);
372 if (!NT_SUCCESS(Status
)) {
373 ERR("error - find_item returned %08x\n", Status
);
377 Status
= snapshot_tree_copy(Vcb
, subvol
->root_item
.block_number
, r
, &address
, Irp
, &rollback
);
378 if (!NT_SUCCESS(Status
)) {
379 ERR("snapshot_tree_copy returned %08x\n", Status
);
383 KeQuerySystemTime(&time
);
384 win_time_to_unix(time
, &now
);
386 r
->root_item
.inode
.generation
= 1;
387 r
->root_item
.inode
.st_size
= 3;
388 r
->root_item
.inode
.st_blocks
= subvol
->root_item
.inode
.st_blocks
;
389 r
->root_item
.inode
.st_nlink
= 1;
390 r
->root_item
.inode
.st_mode
= __S_IFDIR
| S_IRUSR
| S_IWUSR
| S_IXUSR
| S_IRGRP
| S_IXGRP
| S_IROTH
| S_IXOTH
; // 40755
391 r
->root_item
.inode
.flags
= 0xffffffff80000000; // FIXME - find out what these mean
392 r
->root_item
.generation
= Vcb
->superblock
.generation
;
393 r
->root_item
.objid
= subvol
->root_item
.objid
;
394 r
->root_item
.block_number
= address
;
395 r
->root_item
.bytes_used
= subvol
->root_item
.bytes_used
;
396 r
->root_item
.last_snapshot_generation
= Vcb
->superblock
.generation
;
397 r
->root_item
.root_level
= subvol
->root_item
.root_level
;
398 r
->root_item
.generation2
= Vcb
->superblock
.generation
;
399 r
->root_item
.parent_uuid
= subvol
->root_item
.uuid
;
400 r
->root_item
.ctransid
= subvol
->root_item
.ctransid
;
401 r
->root_item
.otransid
= Vcb
->superblock
.generation
;
402 r
->root_item
.ctime
= subvol
->root_item
.ctime
;
403 r
->root_item
.otime
= now
;
406 r
->root_item
.flags
|= BTRFS_SUBVOL_READONLY
;
408 r
->treeholder
.address
= address
;
410 // FIXME - do we need to copy over the send and receive fields too?
412 if (tp
.item
->key
.obj_id
!= searchkey
.obj_id
|| tp
.item
->key
.obj_type
!= searchkey
.obj_type
) {
413 ERR("error - could not find ROOT_ITEM for subvol %llx\n", r
->id
);
414 Status
= STATUS_INTERNAL_ERROR
;
418 RtlCopyMemory(tp
.item
->data
, &r
->root_item
, sizeof(ROOT_ITEM
));
420 // update ROOT_ITEM of original subvol
422 subvol
->root_item
.last_snapshot_generation
= Vcb
->superblock
.generation
;
424 mark_subvol_dirty(Vcb
, subvol
);
426 // create fileref for entry in other subvolume
428 fr
= create_fileref(Vcb
);
430 ERR("out of memory\n");
431 Status
= STATUS_INSUFFICIENT_RESOURCES
;
435 Status
= open_fcb(Vcb
, r
, r
->root_item
.objid
, BTRFS_TYPE_DIRECTORY
, utf8
, fcb
, &fr
->fcb
, PagedPool
, Irp
);
436 if (!NT_SUCCESS(Status
)) {
437 ERR("open_fcb returned %08x\n", Status
);
442 fr
->parent
= fileref
;
444 Status
= add_dir_child(fileref
->fcb
, r
->id
, TRUE
, utf8
, name
, BTRFS_TYPE_DIRECTORY
, &dc
);
445 if (!NT_SUCCESS(Status
))
446 WARN("add_dir_child returned %08x\n", Status
);
451 ExAcquireResourceExclusiveLite(&fileref
->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 %08x\n", Status
);
513 if (NT_SUCCESS(Status
))
514 clear_rollback(&rollback
);
516 do_rollback(Vcb
, &rollback
);
521 static NTSTATUS
create_snapshot(device_extension
* Vcb
, PFILE_OBJECT FileObject
, void* data
, ULONG length
, PIRP Irp
) {
522 PFILE_OBJECT subvol_obj
;
524 btrfs_create_snapshot
* bcs
= data
;
527 BOOL readonly
, posix
;
529 UNICODE_STRING nameus
;
533 file_ref
*fileref
, *fr2
;
536 if (IoIs32bitProcess(Irp
)) {
537 btrfs_create_snapshot32
* bcs32
= data
;
539 if (length
< offsetof(btrfs_create_snapshot32
, name
))
540 return STATUS_INVALID_PARAMETER
;
542 if (length
< offsetof(btrfs_create_snapshot32
, name
) + bcs32
->namelen
)
543 return STATUS_INVALID_PARAMETER
;
545 subvolh
= Handle32ToHandle(bcs32
->subvol
);
547 nameus
.Buffer
= bcs32
->name
;
548 nameus
.Length
= nameus
.MaximumLength
= bcs32
->namelen
;
550 readonly
= bcs32
->readonly
;
551 posix
= bcs32
->posix
;
554 if (length
< offsetof(btrfs_create_snapshot
, name
))
555 return STATUS_INVALID_PARAMETER
;
557 if (length
< offsetof(btrfs_create_snapshot
, name
) + bcs
->namelen
)
558 return STATUS_INVALID_PARAMETER
;
560 subvolh
= bcs
->subvol
;
562 nameus
.Buffer
= bcs
->name
;
563 nameus
.Length
= nameus
.MaximumLength
= bcs
->namelen
;
565 readonly
= bcs
->readonly
;
572 return STATUS_INVALID_PARAMETER
;
574 if (!FileObject
|| !FileObject
->FsContext
)
575 return STATUS_INVALID_PARAMETER
;
577 fcb
= FileObject
->FsContext
;
578 ccb
= FileObject
->FsContext2
;
580 if (!fcb
|| !ccb
|| fcb
->type
!= BTRFS_TYPE_DIRECTORY
)
581 return STATUS_INVALID_PARAMETER
;
583 fileref
= ccb
->fileref
;
586 ERR("fileref was NULL\n");
587 return STATUS_INVALID_PARAMETER
;
590 if (!(ccb
->access
& FILE_ADD_SUBDIRECTORY
)) {
591 WARN("insufficient privileges\n");
592 return STATUS_ACCESS_DENIED
;
596 return STATUS_MEDIA_WRITE_PROTECTED
;
598 if (is_subvol_readonly(fcb
->subvol
, Irp
))
599 return STATUS_ACCESS_DENIED
;
601 if (!is_file_name_valid(&nameus
, posix
))
602 return STATUS_OBJECT_NAME_INVALID
;
606 Status
= RtlUnicodeToUTF8N(NULL
, 0, &len
, nameus
.Buffer
, nameus
.Length
);
607 if (!NT_SUCCESS(Status
)) {
608 ERR("RtlUnicodeToUTF8N failed with error %08x\n", Status
);
613 ERR("RtlUnicodeToUTF8N returned a length of 0\n");
614 return STATUS_INTERNAL_ERROR
;
618 ERR("len was too long\n");
619 return STATUS_INVALID_PARAMETER
;
622 utf8
.MaximumLength
= utf8
.Length
= (USHORT
)len
;
623 utf8
.Buffer
= ExAllocatePoolWithTag(PagedPool
, utf8
.Length
, ALLOC_TAG
);
626 ERR("out of memory\n");
627 return STATUS_INSUFFICIENT_RESOURCES
;
630 Status
= RtlUnicodeToUTF8N(utf8
.Buffer
, len
, &len
, nameus
.Buffer
, nameus
.Length
);
631 if (!NT_SUCCESS(Status
)) {
632 ERR("RtlUnicodeToUTF8N failed with error %08x\n", Status
);
636 ExAcquireResourceExclusiveLite(&Vcb
->tree_lock
, TRUE
);
638 // no need for fcb_lock as we have tree_lock exclusively
639 Status
= open_fileref(fcb
->Vcb
, &fr2
, &nameus
, fileref
, FALSE
, NULL
, NULL
, PagedPool
, ccb
->case_sensitive
|| posix
, Irp
);
641 if (NT_SUCCESS(Status
)) {
643 WARN("file already exists\n");
645 Status
= STATUS_OBJECT_NAME_COLLISION
;
649 } else if (!NT_SUCCESS(Status
) && Status
!= STATUS_OBJECT_NAME_NOT_FOUND
) {
650 ERR("open_fileref returned %08x\n", Status
);
654 Status
= ObReferenceObjectByHandle(subvolh
, 0, *IoFileObjectType
, Irp
->RequestorMode
, (void**)&subvol_obj
, NULL
);
655 if (!NT_SUCCESS(Status
)) {
656 ERR("ObReferenceObjectByHandle returned %08x\n", Status
);
660 if (subvol_obj
->DeviceObject
!= FileObject
->DeviceObject
) {
661 Status
= STATUS_INVALID_PARAMETER
;
665 subvol_fcb
= subvol_obj
->FsContext
;
667 Status
= STATUS_INVALID_PARAMETER
;
671 if (subvol_fcb
->inode
!= subvol_fcb
->subvol
->root_item
.objid
) {
672 WARN("handle inode was %llx, expected %llx\n", subvol_fcb
->inode
, subvol_fcb
->subvol
->root_item
.objid
);
673 Status
= STATUS_INVALID_PARAMETER
;
677 ccb
= subvol_obj
->FsContext2
;
680 Status
= STATUS_INVALID_PARAMETER
;
684 if (!(ccb
->access
& FILE_TRAVERSE
)) {
685 WARN("insufficient privileges\n");
686 Status
= STATUS_ACCESS_DENIED
;
690 if (fcb
== Vcb
->dummy_fcb
) {
691 Status
= STATUS_ACCESS_DENIED
;
695 // clear unique flag on extents of open files in subvol
696 if (!IsListEmpty(&subvol_fcb
->subvol
->fcbs
)) {
697 LIST_ENTRY
* le
= subvol_fcb
->subvol
->fcbs
.Flink
;
699 while (le
!= &subvol_fcb
->subvol
->fcbs
) {
700 struct _fcb
* openfcb
= CONTAINING_RECORD(le
, struct _fcb
, list_entry
);
703 le2
= openfcb
->extents
.Flink
;
705 while (le2
!= &openfcb
->extents
) {
706 extent
* ext
= CONTAINING_RECORD(le2
, extent
, list_entry
);
717 Status
= do_create_snapshot(Vcb
, FileObject
, subvol_fcb
, &utf8
, &nameus
, readonly
, Irp
);
719 if (NT_SUCCESS(Status
)) {
722 Status
= open_fileref(Vcb
, &fr
, &nameus
, fileref
, FALSE
, NULL
, NULL
, PagedPool
, FALSE
, Irp
);
724 if (!NT_SUCCESS(Status
)) {
725 ERR("open_fileref returned %08x\n", Status
);
726 Status
= STATUS_SUCCESS
;
728 send_notification_fileref(fr
, FILE_NOTIFY_CHANGE_DIR_NAME
, FILE_ACTION_ADDED
, NULL
);
734 ObDereferenceObject(subvol_obj
);
737 ExReleaseResourceLite(&Vcb
->tree_lock
);
740 ExFreePool(utf8
.Buffer
);
745 static NTSTATUS
create_subvol(device_extension
* Vcb
, PFILE_OBJECT FileObject
, void* data
, ULONG datalen
, PIRP Irp
) {
746 btrfs_create_subvol
* bcs
;
747 fcb
*fcb
, *rootfcb
= NULL
;
757 UNICODE_STRING nameus
;
762 SECURITY_SUBJECT_CONTEXT subjcont
;
766 file_ref
*fr
= NULL
, *fr2
;
767 dir_child
* dc
= NULL
;
769 fcb
= FileObject
->FsContext
;
771 ERR("error - fcb was NULL\n");
772 return STATUS_INTERNAL_ERROR
;
775 ccb
= FileObject
->FsContext2
;
777 ERR("error - ccb was NULL\n");
778 return STATUS_INTERNAL_ERROR
;
781 fileref
= ccb
->fileref
;
783 if (fcb
->type
!= BTRFS_TYPE_DIRECTORY
) {
784 ERR("parent FCB was not a directory\n");
785 return STATUS_NOT_A_DIRECTORY
;
789 ERR("fileref was NULL\n");
790 return STATUS_INVALID_PARAMETER
;
793 if (fileref
->deleted
|| fcb
->deleted
) {
794 ERR("parent has been deleted\n");
795 return STATUS_FILE_DELETED
;
798 if (!(ccb
->access
& FILE_ADD_SUBDIRECTORY
)) {
799 WARN("insufficient privileges\n");
800 return STATUS_ACCESS_DENIED
;
804 return STATUS_MEDIA_WRITE_PROTECTED
;
806 if (is_subvol_readonly(fcb
->subvol
, Irp
))
807 return STATUS_ACCESS_DENIED
;
809 if (fcb
== Vcb
->dummy_fcb
)
810 return STATUS_ACCESS_DENIED
;
812 if (!data
|| datalen
< sizeof(btrfs_create_subvol
))
813 return STATUS_INVALID_PARAMETER
;
815 bcs
= (btrfs_create_subvol
*)data
;
817 if (offsetof(btrfs_create_subvol
, name
[0]) + bcs
->namelen
> datalen
)
818 return STATUS_INVALID_PARAMETER
;
820 nameus
.Length
= nameus
.MaximumLength
= bcs
->namelen
;
821 nameus
.Buffer
= bcs
->name
;
823 if (!is_file_name_valid(&nameus
, bcs
->posix
))
824 return STATUS_OBJECT_NAME_INVALID
;
828 Status
= RtlUnicodeToUTF8N(NULL
, 0, &len
, nameus
.Buffer
, nameus
.Length
);
829 if (!NT_SUCCESS(Status
)) {
830 ERR("RtlUnicodeToUTF8N failed with error %08x\n", Status
);
835 ERR("RtlUnicodeToUTF8N returned a length of 0\n");
836 return STATUS_INTERNAL_ERROR
;
840 ERR("len was too long\n");
841 return STATUS_INVALID_PARAMETER
;
844 utf8
.MaximumLength
= utf8
.Length
= (USHORT
)len
;
845 utf8
.Buffer
= ExAllocatePoolWithTag(PagedPool
, utf8
.Length
, ALLOC_TAG
);
848 ERR("out of memory\n");
849 return STATUS_INSUFFICIENT_RESOURCES
;
852 Status
= RtlUnicodeToUTF8N(utf8
.Buffer
, len
, &len
, nameus
.Buffer
, nameus
.Length
);
853 if (!NT_SUCCESS(Status
)) {
854 ERR("RtlUnicodeToUTF8N failed with error %08x\n", Status
);
858 ExAcquireResourceExclusiveLite(&Vcb
->tree_lock
, TRUE
);
860 KeQuerySystemTime(&time
);
861 win_time_to_unix(time
, &now
);
863 // no need for fcb_lock as we have tree_lock exclusively
864 Status
= open_fileref(fcb
->Vcb
, &fr2
, &nameus
, fileref
, FALSE
, NULL
, NULL
, PagedPool
, ccb
->case_sensitive
|| bcs
->posix
, Irp
);
866 if (NT_SUCCESS(Status
)) {
868 WARN("file already exists\n");
870 Status
= STATUS_OBJECT_NAME_COLLISION
;
874 } else if (!NT_SUCCESS(Status
) && Status
!= STATUS_OBJECT_NAME_NOT_FOUND
) {
875 ERR("open_fileref returned %08x\n", Status
);
879 id
= InterlockedIncrement64(&Vcb
->root_root
->lastinode
);
880 Status
= create_root(Vcb
, id
, &r
, FALSE
, 0, Irp
);
882 if (!NT_SUCCESS(Status
)) {
883 ERR("create_root returned %08x\n", Status
);
887 TRACE("created root %llx\n", id
);
889 if (!Vcb
->uuid_root
) {
892 TRACE("uuid root doesn't exist, creating it\n");
894 Status
= create_root(Vcb
, BTRFS_ROOT_UUID
, &uuid_root
, FALSE
, 0, Irp
);
896 if (!NT_SUCCESS(Status
)) {
897 ERR("create_root returned %08x\n", Status
);
901 Vcb
->uuid_root
= uuid_root
;
904 root_num
= ExAllocatePoolWithTag(PagedPool
, sizeof(UINT64
), ALLOC_TAG
);
906 ERR("out of memory\n");
907 Status
= STATUS_INSUFFICIENT_RESOURCES
;
914 get_uuid(&r
->root_item
.uuid
);
916 RtlCopyMemory(&searchkey
.obj_id
, &r
->root_item
.uuid
, sizeof(UINT64
));
917 searchkey
.obj_type
= TYPE_SUBVOL_UUID
;
918 RtlCopyMemory(&searchkey
.offset
, &r
->root_item
.uuid
.uuid
[sizeof(UINT64
)], sizeof(UINT64
));
920 Status
= find_item(Vcb
, Vcb
->uuid_root
, &tp
, &searchkey
, FALSE
, Irp
);
921 } while (NT_SUCCESS(Status
) && !keycmp(searchkey
, tp
.item
->key
));
925 Status
= insert_tree_item(Vcb
, Vcb
->uuid_root
, searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
, root_num
, sizeof(UINT64
), NULL
, Irp
);
926 if (!NT_SUCCESS(Status
)) {
927 ERR("insert_tree_item returned %08x\n", Status
);
928 ExFreePool(root_num
);
932 r
->root_item
.inode
.generation
= 1;
933 r
->root_item
.inode
.st_size
= 3;
934 r
->root_item
.inode
.st_blocks
= Vcb
->superblock
.node_size
;
935 r
->root_item
.inode
.st_nlink
= 1;
936 r
->root_item
.inode
.st_mode
= __S_IFDIR
| S_IRUSR
| S_IWUSR
| S_IXUSR
| S_IRGRP
| S_IXGRP
| S_IROTH
| S_IXOTH
; // 40755
937 r
->root_item
.inode
.flags
= 0xffffffff80000000; // FIXME - find out what these mean
940 r
->root_item
.flags
|= BTRFS_SUBVOL_READONLY
;
942 r
->root_item
.objid
= SUBVOL_ROOT_INODE
;
943 r
->root_item
.bytes_used
= Vcb
->superblock
.node_size
;
944 r
->root_item
.ctransid
= Vcb
->superblock
.generation
;
945 r
->root_item
.otransid
= Vcb
->superblock
.generation
;
946 r
->root_item
.ctime
= now
;
947 r
->root_item
.otime
= now
;
949 // add .. inode to new subvol
951 rootfcb
= create_fcb(Vcb
, PagedPool
);
953 ERR("out of memory\n");
954 Status
= STATUS_INSUFFICIENT_RESOURCES
;
961 rootfcb
->inode
= SUBVOL_ROOT_INODE
;
962 rootfcb
->type
= BTRFS_TYPE_DIRECTORY
;
964 rootfcb
->inode_item
.generation
= Vcb
->superblock
.generation
;
965 rootfcb
->inode_item
.transid
= Vcb
->superblock
.generation
;
966 rootfcb
->inode_item
.st_nlink
= 1;
967 rootfcb
->inode_item
.st_mode
= __S_IFDIR
| inherit_mode(fileref
->fcb
, TRUE
);
968 rootfcb
->inode_item
.st_atime
= rootfcb
->inode_item
.st_ctime
= rootfcb
->inode_item
.st_mtime
= rootfcb
->inode_item
.otime
= now
;
969 rootfcb
->inode_item
.st_gid
= GID_NOBODY
;
971 rootfcb
->atts
= get_file_attributes(Vcb
, rootfcb
->subvol
, rootfcb
->inode
, rootfcb
->type
, FALSE
, TRUE
, Irp
);
973 if (r
->root_item
.flags
& BTRFS_SUBVOL_READONLY
)
974 rootfcb
->atts
|= FILE_ATTRIBUTE_READONLY
;
976 SeCaptureSubjectContext(&subjcont
);
978 Status
= SeAssignSecurity(fcb
->sd
, NULL
, (void**)&rootfcb
->sd
, TRUE
, &subjcont
, IoGetFileObjectGenericMapping(), PagedPool
);
980 if (!NT_SUCCESS(Status
)) {
981 ERR("SeAssignSecurity returned %08x\n", Status
);
986 ERR("SeAssignSecurity returned NULL security descriptor\n");
987 Status
= STATUS_INTERNAL_ERROR
;
991 Status
= RtlGetOwnerSecurityDescriptor(rootfcb
->sd
, &owner
, &defaulted
);
992 if (!NT_SUCCESS(Status
)) {
993 ERR("RtlGetOwnerSecurityDescriptor returned %08x\n", Status
);
994 rootfcb
->inode_item
.st_uid
= UID_NOBODY
;
995 rootfcb
->sd_dirty
= TRUE
;
997 rootfcb
->inode_item
.st_uid
= sid_to_uid(owner
);
998 rootfcb
->sd_dirty
= rootfcb
->inode_item
.st_uid
== UID_NOBODY
;
1001 find_gid(rootfcb
, fileref
->fcb
, &subjcont
);
1003 rootfcb
->inode_item_changed
= TRUE
;
1005 acquire_fcb_lock_exclusive(Vcb
);
1006 InsertTailList(&r
->fcbs
, &rootfcb
->list_entry
);
1007 InsertTailList(&Vcb
->all_fcbs
, &rootfcb
->list_entry_all
);
1009 release_fcb_lock(Vcb
);
1011 rootfcb
->Header
.IsFastIoPossible
= fast_io_possible(rootfcb
);
1012 rootfcb
->Header
.AllocationSize
.QuadPart
= 0;
1013 rootfcb
->Header
.FileSize
.QuadPart
= 0;
1014 rootfcb
->Header
.ValidDataLength
.QuadPart
= 0;
1016 rootfcb
->created
= TRUE
;
1018 if (fileref
->fcb
->inode_item
.flags
& BTRFS_INODE_COMPRESS
)
1019 rootfcb
->inode_item
.flags
|= BTRFS_INODE_COMPRESS
;
1021 rootfcb
->prop_compression
= fileref
->fcb
->prop_compression
;
1022 rootfcb
->prop_compression_changed
= rootfcb
->prop_compression
!= PropCompression_None
;
1024 r
->lastinode
= rootfcb
->inode
;
1028 irsize
= (UINT16
)(offsetof(INODE_REF
, name
[0]) + sizeof(DOTDOT
) - 1);
1029 ir
= ExAllocatePoolWithTag(PagedPool
, irsize
, ALLOC_TAG
);
1031 ERR("out of memory\n");
1032 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1037 ir
->n
= sizeof(DOTDOT
) - 1;
1038 RtlCopyMemory(ir
->name
, DOTDOT
, ir
->n
);
1040 Status
= insert_tree_item(Vcb
, r
, r
->root_item
.objid
, TYPE_INODE_REF
, r
->root_item
.objid
, ir
, irsize
, NULL
, Irp
);
1041 if (!NT_SUCCESS(Status
)) {
1042 ERR("insert_tree_item returned %08x\n", Status
);
1047 // create fileref for entry in other subvolume
1049 fr
= create_fileref(Vcb
);
1051 ERR("out of memory\n");
1055 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1061 mark_fcb_dirty(rootfcb
);
1063 fr
->parent
= fileref
;
1065 Status
= add_dir_child(fileref
->fcb
, r
->id
, TRUE
, &utf8
, &nameus
, BTRFS_TYPE_DIRECTORY
, &dc
);
1066 if (!NT_SUCCESS(Status
))
1067 WARN("add_dir_child returned %08x\n", Status
);
1072 fr
->fcb
->hash_ptrs
= ExAllocatePoolWithTag(PagedPool
, sizeof(LIST_ENTRY
*) * 256, ALLOC_TAG
);
1073 if (!fr
->fcb
->hash_ptrs
) {
1074 ERR("out of memory\n");
1076 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1080 RtlZeroMemory(fr
->fcb
->hash_ptrs
, sizeof(LIST_ENTRY
*) * 256);
1082 fr
->fcb
->hash_ptrs_uc
= ExAllocatePoolWithTag(PagedPool
, sizeof(LIST_ENTRY
*) * 256, ALLOC_TAG
);
1083 if (!fr
->fcb
->hash_ptrs_uc
) {
1084 ERR("out of memory\n");
1086 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1090 RtlZeroMemory(fr
->fcb
->hash_ptrs_uc
, sizeof(LIST_ENTRY
*) * 256);
1092 ExAcquireResourceExclusiveLite(&fileref
->fcb
->nonpaged
->dir_children_lock
, TRUE
);
1093 InsertTailList(&fileref
->children
, &fr
->list_entry
);
1094 ExReleaseResourceLite(&fileref
->fcb
->nonpaged
->dir_children_lock
);
1096 increase_fileref_refcount(fileref
);
1098 if (fr
->fcb
->type
== BTRFS_TYPE_DIRECTORY
)
1099 fr
->fcb
->fileref
= fr
;
1102 mark_fileref_dirty(fr
);
1104 // change fcb->subvol's ROOT_ITEM
1106 fcb
->subvol
->root_item
.ctransid
= Vcb
->superblock
.generation
;
1107 fcb
->subvol
->root_item
.ctime
= now
;
1109 // change fcb's INODE_ITEM
1111 fcb
->inode_item
.transid
= Vcb
->superblock
.generation
;
1112 fcb
->inode_item
.st_size
+= utf8
.Length
* 2;
1113 fcb
->inode_item
.sequence
++;
1115 if (!ccb
->user_set_change_time
)
1116 fcb
->inode_item
.st_ctime
= now
;
1118 if (!ccb
->user_set_write_time
)
1119 fcb
->inode_item
.st_mtime
= now
;
1121 fcb
->inode_item_changed
= TRUE
;
1122 mark_fcb_dirty(fcb
);
1124 fr
->fcb
->subvol
->parent
= fcb
->subvol
->id
;
1126 Status
= STATUS_SUCCESS
;
1129 if (!NT_SUCCESS(Status
)) {
1132 mark_fileref_dirty(fr
);
1133 } else if (rootfcb
) {
1134 rootfcb
->deleted
= TRUE
;
1135 mark_fcb_dirty(rootfcb
);
1139 RemoveEntryList(&r
->list_entry
);
1140 InsertTailList(&Vcb
->drop_roots
, &r
->list_entry
);
1144 ExReleaseResourceLite(&Vcb
->tree_lock
);
1146 if (NT_SUCCESS(Status
)) {
1147 send_notification_fileref(fr
, FILE_NOTIFY_CHANGE_DIR_NAME
, FILE_ACTION_ADDED
, NULL
);
1148 send_notification_fileref(fr
->parent
, FILE_NOTIFY_CHANGE_LAST_WRITE
, FILE_ACTION_MODIFIED
, NULL
);
1158 static NTSTATUS
get_inode_info(PFILE_OBJECT FileObject
, void* data
, ULONG length
) {
1159 btrfs_inode_info2
* bii
= data
;
1164 if (length
< sizeof(btrfs_inode_info
))
1165 return STATUS_BUFFER_OVERFLOW
;
1168 return STATUS_INVALID_PARAMETER
;
1170 fcb
= FileObject
->FsContext
;
1173 return STATUS_INVALID_PARAMETER
;
1175 ccb
= FileObject
->FsContext2
;
1178 return STATUS_INVALID_PARAMETER
;
1180 if (!(ccb
->access
& FILE_READ_ATTRIBUTES
)) {
1181 WARN("insufficient privileges\n");
1182 return STATUS_ACCESS_DENIED
;
1186 fcb
= ccb
->fileref
->parent
->fcb
;
1188 old_style
= length
< sizeof(btrfs_inode_info2
);
1190 ExAcquireResourceSharedLite(fcb
->Header
.Resource
, TRUE
);
1192 bii
->subvol
= fcb
->subvol
->id
;
1193 bii
->inode
= fcb
->inode
;
1194 bii
->top
= fcb
->Vcb
->root_fileref
->fcb
== fcb
? TRUE
: FALSE
;
1195 bii
->type
= fcb
->type
;
1196 bii
->st_uid
= fcb
->inode_item
.st_uid
;
1197 bii
->st_gid
= fcb
->inode_item
.st_gid
;
1198 bii
->st_mode
= fcb
->inode_item
.st_mode
;
1200 if (fcb
->inode_item
.st_rdev
== 0)
1203 bii
->st_rdev
= makedev((fcb
->inode_item
.st_rdev
& 0xFFFFFFFFFFF) >> 20, fcb
->inode_item
.st_rdev
& 0xFFFFF);
1205 bii
->flags
= fcb
->inode_item
.flags
;
1207 bii
->inline_length
= 0;
1208 bii
->disk_size_uncompressed
= 0;
1209 bii
->disk_size_zlib
= 0;
1210 bii
->disk_size_lzo
= 0;
1213 bii
->disk_size_zstd
= 0;
1214 bii
->sparse_size
= 0;
1217 if (fcb
->type
!= BTRFS_TYPE_DIRECTORY
) {
1218 UINT64 last_end
= 0;
1220 BOOL extents_inline
= FALSE
;
1222 le
= fcb
->extents
.Flink
;
1223 while (le
!= &fcb
->extents
) {
1224 extent
* ext
= CONTAINING_RECORD(le
, extent
, list_entry
);
1227 if (!old_style
&& ext
->offset
> last_end
)
1228 bii
->sparse_size
+= ext
->offset
- last_end
;
1230 if (ext
->extent_data
.type
== EXTENT_TYPE_INLINE
) {
1231 bii
->inline_length
+= ext
->datalen
- (UINT16
)offsetof(EXTENT_DATA
, data
[0]);
1232 last_end
= ext
->offset
+ ext
->extent_data
.decoded_size
;
1233 extents_inline
= TRUE
;
1235 EXTENT_DATA2
* ed2
= (EXTENT_DATA2
*)ext
->extent_data
.data
;
1237 // FIXME - compressed extents with a hole in them are counted more than once
1238 if (ed2
->size
!= 0) {
1239 switch (ext
->extent_data
.compression
) {
1240 case BTRFS_COMPRESSION_NONE
:
1241 bii
->disk_size_uncompressed
+= ed2
->num_bytes
;
1244 case BTRFS_COMPRESSION_ZLIB
:
1245 bii
->disk_size_zlib
+= ed2
->size
;
1248 case BTRFS_COMPRESSION_LZO
:
1249 bii
->disk_size_lzo
+= ed2
->size
;
1252 case BTRFS_COMPRESSION_ZSTD
:
1254 bii
->disk_size_zstd
+= ed2
->size
;
1259 last_end
= ext
->offset
+ ed2
->num_bytes
;
1266 if (!extents_inline
&& !old_style
&& sector_align(fcb
->inode_item
.st_size
, fcb
->Vcb
->superblock
.sector_size
) > last_end
)
1267 bii
->sparse_size
+= sector_align(fcb
->inode_item
.st_size
, fcb
->Vcb
->superblock
.sector_size
) - last_end
;
1270 switch (fcb
->prop_compression
) {
1271 case PropCompression_Zlib
:
1272 bii
->compression_type
= BTRFS_COMPRESSION_ZLIB
;
1275 case PropCompression_LZO
:
1276 bii
->compression_type
= BTRFS_COMPRESSION_LZO
;
1279 case PropCompression_ZSTD
:
1280 bii
->compression_type
= BTRFS_COMPRESSION_ZSTD
;
1284 bii
->compression_type
= BTRFS_COMPRESSION_ANY
;
1288 ExReleaseResourceLite(fcb
->Header
.Resource
);
1290 return STATUS_SUCCESS
;
1293 static NTSTATUS
set_inode_info(PFILE_OBJECT FileObject
, void* data
, ULONG length
, PIRP Irp
) {
1294 btrfs_set_inode_info
* bsii
= data
;
1299 if (length
< sizeof(btrfs_set_inode_info
))
1300 return STATUS_INVALID_PARAMETER
;
1303 return STATUS_INVALID_PARAMETER
;
1305 fcb
= FileObject
->FsContext
;
1308 return STATUS_INVALID_PARAMETER
;
1310 ccb
= FileObject
->FsContext2
;
1313 return STATUS_INVALID_PARAMETER
;
1315 if (bsii
->flags_changed
&& !(ccb
->access
& FILE_WRITE_ATTRIBUTES
)) {
1316 WARN("insufficient privileges\n");
1317 return STATUS_ACCESS_DENIED
;
1320 if ((bsii
->mode_changed
|| bsii
->uid_changed
|| bsii
->gid_changed
) && !(ccb
->access
& WRITE_DAC
)) {
1321 WARN("insufficient privileges\n");
1322 return STATUS_ACCESS_DENIED
;
1325 if (bsii
->compression_type_changed
&& bsii
->compression_type
> BTRFS_COMPRESSION_ZSTD
)
1326 return STATUS_INVALID_PARAMETER
;
1329 fcb
= ccb
->fileref
->parent
->fcb
;
1331 if (is_subvol_readonly(fcb
->subvol
, Irp
)) {
1332 WARN("trying to change inode on readonly subvolume\n");
1333 return STATUS_ACCESS_DENIED
;
1336 ExAcquireResourceExclusiveLite(fcb
->Header
.Resource
, TRUE
);
1338 if (bsii
->flags_changed
) {
1339 if (fcb
->type
!= BTRFS_TYPE_DIRECTORY
&& fcb
->inode_item
.st_size
> 0 &&
1340 (bsii
->flags
& BTRFS_INODE_NODATACOW
) != (fcb
->inode_item
.flags
& BTRFS_INODE_NODATACOW
)) {
1341 WARN("trying to change nocow flag on non-empty file\n");
1342 Status
= STATUS_INVALID_PARAMETER
;
1346 fcb
->inode_item
.flags
= bsii
->flags
;
1348 if (fcb
->inode_item
.flags
& BTRFS_INODE_NODATACOW
)
1349 fcb
->inode_item
.flags
|= BTRFS_INODE_NODATASUM
;
1351 fcb
->inode_item
.flags
&= ~(UINT64
)BTRFS_INODE_NODATASUM
;
1354 if (bsii
->mode_changed
) {
1355 UINT32 allowed
= S_IRUSR
| S_IWUSR
| S_IXUSR
| S_IRGRP
| S_IWGRP
| S_IXGRP
| S_IROTH
| S_IWOTH
| S_IXOTH
|
1358 if (ccb
->access
& WRITE_OWNER
)
1361 fcb
->inode_item
.st_mode
&= ~allowed
;
1362 fcb
->inode_item
.st_mode
|= bsii
->st_mode
& allowed
;
1365 if (bsii
->uid_changed
&& fcb
->inode_item
.st_uid
!= bsii
->st_uid
) {
1366 fcb
->inode_item
.st_uid
= bsii
->st_uid
;
1368 fcb
->sd_dirty
= TRUE
;
1369 fcb
->sd_deleted
= FALSE
;
1372 if (bsii
->gid_changed
)
1373 fcb
->inode_item
.st_gid
= bsii
->st_gid
;
1375 if (bsii
->compression_type_changed
) {
1376 switch (bsii
->compression_type
) {
1377 case BTRFS_COMPRESSION_ANY
:
1378 fcb
->prop_compression
= PropCompression_None
;
1381 case BTRFS_COMPRESSION_ZLIB
:
1382 fcb
->prop_compression
= PropCompression_Zlib
;
1385 case BTRFS_COMPRESSION_LZO
:
1386 fcb
->prop_compression
= PropCompression_LZO
;
1389 case BTRFS_COMPRESSION_ZSTD
:
1390 fcb
->prop_compression
= PropCompression_ZSTD
;
1394 fcb
->prop_compression_changed
= TRUE
;
1397 if (bsii
->flags_changed
|| bsii
->mode_changed
|| bsii
->uid_changed
|| bsii
->gid_changed
|| bsii
->compression_type_changed
) {
1398 fcb
->inode_item_changed
= TRUE
;
1399 mark_fcb_dirty(fcb
);
1402 Status
= STATUS_SUCCESS
;
1405 ExReleaseResourceLite(fcb
->Header
.Resource
);
1410 static NTSTATUS
get_devices(device_extension
* Vcb
, void* data
, ULONG length
) {
1411 btrfs_device
* dev
= NULL
;
1415 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, TRUE
);
1417 le
= Vcb
->devices
.Flink
;
1418 while (le
!= &Vcb
->devices
) {
1419 device
* dev2
= CONTAINING_RECORD(le
, device
, list_entry
);
1422 if (length
< sizeof(btrfs_device
) - sizeof(WCHAR
)) {
1423 Status
= STATUS_BUFFER_OVERFLOW
;
1430 dev
->next_entry
= sizeof(btrfs_device
) - sizeof(WCHAR
) + dev
->namelen
;
1431 dev
= (btrfs_device
*)((UINT8
*)dev
+ dev
->next_entry
);
1434 structlen
= length
- offsetof(btrfs_device
, namelen
);
1437 Status
= dev_ioctl(dev2
->devobj
, IOCTL_MOUNTDEV_QUERY_DEVICE_NAME
, NULL
, 0, &dev
->namelen
, structlen
, TRUE
, NULL
);
1438 if (!NT_SUCCESS(Status
))
1441 dev
->missing
= FALSE
;
1444 dev
->missing
= TRUE
;
1447 dev
->next_entry
= 0;
1448 dev
->dev_id
= dev2
->devitem
.dev_id
;
1449 dev
->readonly
= (Vcb
->readonly
|| dev2
->readonly
) ? TRUE
: FALSE
;
1450 dev
->device_number
= dev2
->disk_num
;
1451 dev
->partition_number
= dev2
->part_num
;
1452 dev
->size
= dev2
->devitem
.num_bytes
;
1455 GET_LENGTH_INFORMATION gli
;
1457 Status
= dev_ioctl(dev2
->devobj
, IOCTL_DISK_GET_LENGTH_INFO
, NULL
, 0, &gli
, sizeof(gli
), TRUE
, NULL
);
1458 if (!NT_SUCCESS(Status
))
1461 dev
->max_size
= gli
.Length
.QuadPart
;
1463 dev
->max_size
= dev
->size
;
1465 RtlCopyMemory(dev
->stats
, dev2
->stats
, sizeof(UINT64
) * 5);
1467 length
-= sizeof(btrfs_device
) - sizeof(WCHAR
) + dev
->namelen
;
1473 ExReleaseResourceLite(&Vcb
->tree_lock
);
1478 static NTSTATUS
get_usage(device_extension
* Vcb
, void* data
, ULONG length
, PIRP Irp
) {
1479 btrfs_usage
* usage
= (btrfs_usage
*)data
;
1480 btrfs_usage
* lastbue
= NULL
;
1484 if (length
< sizeof(btrfs_usage
))
1485 return STATUS_BUFFER_OVERFLOW
;
1487 if (!Vcb
->chunk_usage_found
) {
1488 ExAcquireResourceExclusiveLite(&Vcb
->tree_lock
, TRUE
);
1490 if (!Vcb
->chunk_usage_found
)
1491 Status
= find_chunk_usage(Vcb
, Irp
);
1493 Status
= STATUS_SUCCESS
;
1495 ExReleaseResourceLite(&Vcb
->tree_lock
);
1497 if (!NT_SUCCESS(Status
)) {
1498 ERR("find_chunk_usage returned %08x\n", Status
);
1503 length
-= offsetof(btrfs_usage
, devices
);
1505 ExAcquireResourceSharedLite(&Vcb
->chunk_lock
, TRUE
);
1507 le
= Vcb
->chunks
.Flink
;
1508 while (le
!= &Vcb
->chunks
) {
1509 BOOL addnew
= FALSE
;
1511 chunk
* c
= CONTAINING_RECORD(le
, chunk
, list_entry
);
1513 if (!lastbue
) // first entry
1516 btrfs_usage
* bue
= usage
;
1521 if (bue
->type
== c
->chunk_item
->type
) {
1526 if (bue
->next_entry
== 0)
1529 bue
= (btrfs_usage
*)((UINT8
*)bue
+ bue
->next_entry
);
1541 if (length
< offsetof(btrfs_usage
, devices
)) {
1542 Status
= STATUS_BUFFER_OVERFLOW
;
1546 length
-= offsetof(btrfs_usage
, devices
);
1548 lastbue
->next_entry
= offsetof(btrfs_usage
, devices
) + (ULONG
)(lastbue
->num_devices
* sizeof(btrfs_usage_device
));
1550 bue
= (btrfs_usage
*)((UINT8
*)lastbue
+ lastbue
->next_entry
);
1553 bue
->next_entry
= 0;
1554 bue
->type
= c
->chunk_item
->type
;
1557 bue
->num_devices
= 0;
1559 if (c
->chunk_item
->type
& BLOCK_FLAG_RAID0
)
1560 factor
= c
->chunk_item
->num_stripes
;
1561 else if (c
->chunk_item
->type
& BLOCK_FLAG_RAID10
)
1562 factor
= c
->chunk_item
->num_stripes
/ c
->chunk_item
->sub_stripes
;
1563 else if (c
->chunk_item
->type
& BLOCK_FLAG_RAID5
)
1564 factor
= c
->chunk_item
->num_stripes
- 1;
1565 else if (c
->chunk_item
->type
& BLOCK_FLAG_RAID6
)
1566 factor
= c
->chunk_item
->num_stripes
- 2;
1571 while (le2
!= &Vcb
->chunks
) {
1572 chunk
* c2
= CONTAINING_RECORD(le2
, chunk
, list_entry
);
1574 if (c2
->chunk_item
->type
== c
->chunk_item
->type
) {
1576 CHUNK_ITEM_STRIPE
* cis
= (CHUNK_ITEM_STRIPE
*)&c2
->chunk_item
[1];
1579 bue
->size
+= c2
->chunk_item
->size
;
1580 bue
->used
+= c2
->used
;
1582 stripesize
= c2
->chunk_item
->size
/ factor
;
1584 for (i
= 0; i
< c2
->chunk_item
->num_stripes
; i
++) {
1588 for (j
= 0; j
< bue
->num_devices
; j
++) {
1589 if (bue
->devices
[j
].dev_id
== cis
[i
].dev_id
) {
1590 bue
->devices
[j
].alloc
+= stripesize
;
1597 if (length
< sizeof(btrfs_usage_device
)) {
1598 Status
= STATUS_BUFFER_OVERFLOW
;
1602 length
-= sizeof(btrfs_usage_device
);
1604 bue
->devices
[bue
->num_devices
].dev_id
= cis
[i
].dev_id
;
1605 bue
->devices
[bue
->num_devices
].alloc
= stripesize
;
1620 Status
= STATUS_SUCCESS
;
1623 ExReleaseResourceLite(&Vcb
->chunk_lock
);
1628 static NTSTATUS
is_volume_mounted(device_extension
* Vcb
, PIRP Irp
) {
1631 IO_STATUS_BLOCK iosb
;
1632 BOOL verify
= FALSE
;
1635 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, TRUE
);
1637 le
= Vcb
->devices
.Flink
;
1638 while (le
!= &Vcb
->devices
) {
1639 device
* dev
= CONTAINING_RECORD(le
, device
, list_entry
);
1641 if (dev
->devobj
&& dev
->removable
) {
1642 Status
= dev_ioctl(dev
->devobj
, IOCTL_STORAGE_CHECK_VERIFY
, NULL
, 0, &cc
, sizeof(ULONG
), FALSE
, &iosb
);
1644 if (iosb
.Information
!= sizeof(ULONG
))
1647 if (Status
== STATUS_VERIFY_REQUIRED
|| (NT_SUCCESS(Status
) && cc
!= dev
->change_count
)) {
1648 dev
->devobj
->Flags
|= DO_VERIFY_VOLUME
;
1652 if (NT_SUCCESS(Status
) && iosb
.Information
== sizeof(ULONG
))
1653 dev
->change_count
= cc
;
1655 if (!NT_SUCCESS(Status
) || verify
) {
1656 IoSetHardErrorOrVerifyDevice(Irp
, dev
->devobj
);
1657 ExReleaseResourceLite(&Vcb
->tree_lock
);
1659 return verify
? STATUS_VERIFY_REQUIRED
: Status
;
1666 ExReleaseResourceLite(&Vcb
->tree_lock
);
1668 return STATUS_SUCCESS
;
1671 static NTSTATUS
fs_get_statistics(void* buffer
, DWORD buflen
, ULONG_PTR
* retlen
) {
1672 FILESYSTEM_STATISTICS
* fss
;
1674 WARN("STUB: FSCTL_FILESYSTEM_GET_STATISTICS\n");
1676 // This is hideously wrong, but at least it stops SMB from breaking
1678 if (buflen
< sizeof(FILESYSTEM_STATISTICS
))
1679 return STATUS_BUFFER_TOO_SMALL
;
1682 RtlZeroMemory(fss
, sizeof(FILESYSTEM_STATISTICS
));
1685 fss
->FileSystemType
= FILESYSTEM_STATISTICS_TYPE_NTFS
;
1686 fss
->SizeOfCompleteStructure
= sizeof(FILESYSTEM_STATISTICS
);
1688 *retlen
= sizeof(FILESYSTEM_STATISTICS
);
1690 return STATUS_SUCCESS
;
1693 static NTSTATUS
set_sparse(device_extension
* Vcb
, PFILE_OBJECT FileObject
, void* data
, ULONG length
, PIRP Irp
) {
1694 FILE_SET_SPARSE_BUFFER
* fssb
= data
;
1698 ccb
* ccb
= FileObject
->FsContext2
;
1699 file_ref
* fileref
= ccb
? ccb
->fileref
: NULL
;
1701 if (data
&& length
< sizeof(FILE_SET_SPARSE_BUFFER
))
1702 return STATUS_INVALID_PARAMETER
;
1705 ERR("FileObject was NULL\n");
1706 return STATUS_INVALID_PARAMETER
;
1709 fcb
= FileObject
->FsContext
;
1712 ERR("FCB was NULL\n");
1713 return STATUS_INVALID_PARAMETER
;
1717 ERR("CCB was NULL\n");
1718 return STATUS_INVALID_PARAMETER
;
1721 if (Irp
->RequestorMode
== UserMode
&& !(ccb
->access
& FILE_WRITE_ATTRIBUTES
)) {
1722 WARN("insufficient privileges\n");
1723 return STATUS_ACCESS_DENIED
;
1727 ERR("no fileref\n");
1728 return STATUS_INVALID_PARAMETER
;
1732 fileref
= fileref
->parent
;
1736 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, TRUE
);
1737 ExAcquireResourceExclusiveLite(fcb
->Header
.Resource
, TRUE
);
1739 if (fcb
->type
!= BTRFS_TYPE_FILE
) {
1740 WARN("FileObject did not point to a file\n");
1741 Status
= STATUS_INVALID_PARAMETER
;
1746 set
= fssb
->SetSparse
;
1751 fcb
->atts
|= FILE_ATTRIBUTE_SPARSE_FILE
;
1752 fcb
->atts_changed
= TRUE
;
1756 fcb
->atts
&= ~FILE_ATTRIBUTE_SPARSE_FILE
;
1757 fcb
->atts_changed
= TRUE
;
1759 defda
= get_file_attributes(Vcb
, fcb
->subvol
, fcb
->inode
, fcb
->type
,
1760 fileref
&& fileref
->dc
&& fileref
->dc
->name
.Length
>= sizeof(WCHAR
) && fileref
->dc
->name
.Buffer
[0] == '.', TRUE
, Irp
);
1762 fcb
->atts_deleted
= defda
== fcb
->atts
;
1765 mark_fcb_dirty(fcb
);
1766 send_notification_fcb(fileref
, FILE_NOTIFY_CHANGE_ATTRIBUTES
, FILE_ACTION_MODIFIED
, NULL
);
1768 Status
= STATUS_SUCCESS
;
1771 ExReleaseResourceLite(fcb
->Header
.Resource
);
1772 ExReleaseResourceLite(&Vcb
->tree_lock
);
1777 static NTSTATUS
zero_data(device_extension
* Vcb
, fcb
* fcb
, UINT64 start
, UINT64 length
, PIRP Irp
, LIST_ENTRY
* rollback
) {
1779 BOOL make_inline
, compress
;
1780 UINT64 start_data
, end_data
;
1784 make_inline
= fcb
->inode_item
.st_size
<= Vcb
->options
.max_inline
|| fcb_is_inline(fcb
);
1787 compress
= write_fcb_compressed(fcb
);
1791 end_data
= fcb
->inode_item
.st_size
;
1792 buf_head
= (ULONG
)offsetof(EXTENT_DATA
, data
[0]);
1793 } else if (compress
) {
1794 start_data
= start
& ~(UINT64
)(COMPRESSED_EXTENT_SIZE
- 1);
1795 end_data
= min(sector_align(start
+ length
, COMPRESSED_EXTENT_SIZE
),
1796 sector_align(fcb
->inode_item
.st_size
, Vcb
->superblock
.sector_size
));
1799 start_data
= start
& ~(UINT64
)(Vcb
->superblock
.sector_size
- 1);
1800 end_data
= sector_align(start
+ length
, Vcb
->superblock
.sector_size
);
1804 data
= ExAllocatePoolWithTag(PagedPool
, (ULONG
)(buf_head
+ end_data
- start_data
), ALLOC_TAG
);
1806 ERR("out of memory\n");
1807 return STATUS_INSUFFICIENT_RESOURCES
;
1810 RtlZeroMemory(data
+ buf_head
, (ULONG
)(end_data
- start_data
));
1812 if (start
> start_data
|| start
+ length
< end_data
) {
1813 Status
= read_file(fcb
, data
+ buf_head
, start_data
, end_data
- start_data
, NULL
, Irp
);
1815 if (!NT_SUCCESS(Status
)) {
1816 ERR("read_file returned %08x\n", Status
);
1822 RtlZeroMemory(data
+ buf_head
+ start
- start_data
, (ULONG
)length
);
1826 EXTENT_DATA
* ed
= (EXTENT_DATA
*)data
;
1828 Status
= excise_extents(Vcb
, fcb
, 0, sector_align(end_data
, Vcb
->superblock
.sector_size
), Irp
, rollback
);
1829 if (!NT_SUCCESS(Status
)) {
1830 ERR("excise_extents returned %08x\n", Status
);
1835 edsize
= (UINT16
)(offsetof(EXTENT_DATA
, data
[0]) + end_data
);
1837 ed
->generation
= Vcb
->superblock
.generation
;
1838 ed
->decoded_size
= end_data
;
1839 ed
->compression
= BTRFS_COMPRESSION_NONE
;
1840 ed
->encryption
= BTRFS_ENCRYPTION_NONE
;
1841 ed
->encoding
= BTRFS_ENCODING_NONE
;
1842 ed
->type
= EXTENT_TYPE_INLINE
;
1844 Status
= add_extent_to_fcb(fcb
, 0, ed
, edsize
, FALSE
, NULL
, rollback
);
1845 if (!NT_SUCCESS(Status
)) {
1846 ERR("add_extent_to_fcb returned %08x\n", Status
);
1853 fcb
->inode_item
.st_blocks
+= end_data
;
1854 } else if (compress
) {
1855 Status
= write_compressed(fcb
, start_data
, end_data
, data
, Irp
, rollback
);
1859 if (!NT_SUCCESS(Status
)) {
1860 ERR("write_compressed returned %08x\n", Status
);
1864 Status
= do_write_file(fcb
, start_data
, end_data
, data
, Irp
, FALSE
, 0, rollback
);
1868 if (!NT_SUCCESS(Status
)) {
1869 ERR("do_write_file returned %08x\n", Status
);
1874 return STATUS_SUCCESS
;
1877 static NTSTATUS
set_zero_data(device_extension
* Vcb
, PFILE_OBJECT FileObject
, void* data
, ULONG length
, PIRP Irp
) {
1878 FILE_ZERO_DATA_INFORMATION
* fzdi
= data
;
1883 LIST_ENTRY rollback
, *le
;
1888 IO_STATUS_BLOCK iosb
;
1890 if (!data
|| length
< sizeof(FILE_ZERO_DATA_INFORMATION
))
1891 return STATUS_INVALID_PARAMETER
;
1894 ERR("FileObject was NULL\n");
1895 return STATUS_INVALID_PARAMETER
;
1898 if (fzdi
->BeyondFinalZero
.QuadPart
<= fzdi
->FileOffset
.QuadPart
) {
1899 WARN("BeyondFinalZero was less than or equal to FileOffset (%llx <= %llx)\n", fzdi
->BeyondFinalZero
.QuadPart
, fzdi
->FileOffset
.QuadPart
);
1900 return STATUS_INVALID_PARAMETER
;
1903 fcb
= FileObject
->FsContext
;
1906 ERR("FCB was NULL\n");
1907 return STATUS_INVALID_PARAMETER
;
1910 ccb
= FileObject
->FsContext2
;
1913 ERR("ccb was NULL\n");
1914 return STATUS_INVALID_PARAMETER
;
1917 if (Irp
->RequestorMode
== UserMode
&& !(ccb
->access
& FILE_WRITE_DATA
)) {
1918 WARN("insufficient privileges\n");
1919 return STATUS_ACCESS_DENIED
;
1922 fileref
= ccb
->fileref
;
1925 ERR("fileref was NULL\n");
1926 return STATUS_INVALID_PARAMETER
;
1929 InitializeListHead(&rollback
);
1931 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, TRUE
);
1932 ExAcquireResourceExclusiveLite(fcb
->Header
.Resource
, TRUE
);
1934 CcFlushCache(&fcb
->nonpaged
->segment_object
, NULL
, 0, &iosb
);
1936 if (fcb
->type
!= BTRFS_TYPE_FILE
) {
1937 WARN("FileObject did not point to a file\n");
1938 Status
= STATUS_INVALID_PARAMETER
;
1943 ERR("FileObject is stream\n");
1944 Status
= STATUS_INVALID_PARAMETER
;
1948 if ((UINT64
)fzdi
->FileOffset
.QuadPart
>= fcb
->inode_item
.st_size
) {
1949 Status
= STATUS_SUCCESS
;
1954 le
= fcb
->extents
.Flink
;
1955 while (le
!= &fcb
->extents
) {
1956 extent
* ext2
= CONTAINING_RECORD(le
, extent
, list_entry
);
1958 if (!ext2
->ignore
) {
1967 Status
= STATUS_SUCCESS
;
1971 if (ext
->extent_data
.type
== EXTENT_TYPE_INLINE
) {
1972 Status
= zero_data(Vcb
, fcb
, fzdi
->FileOffset
.QuadPart
, fzdi
->BeyondFinalZero
.QuadPart
- fzdi
->FileOffset
.QuadPart
, Irp
, &rollback
);
1973 if (!NT_SUCCESS(Status
)) {
1974 ERR("zero_data returned %08x\n", Status
);
1978 start
= sector_align(fzdi
->FileOffset
.QuadPart
, Vcb
->superblock
.sector_size
);
1980 if ((UINT64
)fzdi
->BeyondFinalZero
.QuadPart
> fcb
->inode_item
.st_size
)
1981 end
= sector_align(fcb
->inode_item
.st_size
, Vcb
->superblock
.sector_size
);
1983 end
= (fzdi
->BeyondFinalZero
.QuadPart
/ Vcb
->superblock
.sector_size
) * Vcb
->superblock
.sector_size
;
1986 Status
= zero_data(Vcb
, fcb
, fzdi
->FileOffset
.QuadPart
, fzdi
->BeyondFinalZero
.QuadPart
- fzdi
->FileOffset
.QuadPart
, Irp
, &rollback
);
1987 if (!NT_SUCCESS(Status
)) {
1988 ERR("zero_data returned %08x\n", Status
);
1992 if (start
> (UINT64
)fzdi
->FileOffset
.QuadPart
) {
1993 Status
= zero_data(Vcb
, fcb
, fzdi
->FileOffset
.QuadPart
, start
- fzdi
->FileOffset
.QuadPart
, Irp
, &rollback
);
1994 if (!NT_SUCCESS(Status
)) {
1995 ERR("zero_data returned %08x\n", Status
);
2000 if (end
< (UINT64
)fzdi
->BeyondFinalZero
.QuadPart
) {
2001 Status
= zero_data(Vcb
, fcb
, end
, fzdi
->BeyondFinalZero
.QuadPart
- end
, Irp
, &rollback
);
2002 if (!NT_SUCCESS(Status
)) {
2003 ERR("zero_data returned %08x\n", Status
);
2009 Status
= excise_extents(Vcb
, fcb
, start
, end
, Irp
, &rollback
);
2010 if (!NT_SUCCESS(Status
)) {
2011 ERR("excise_extents returned %08x\n", Status
);
2018 CcPurgeCacheSection(&fcb
->nonpaged
->segment_object
, &fzdi
->FileOffset
, (ULONG
)(fzdi
->BeyondFinalZero
.QuadPart
- fzdi
->FileOffset
.QuadPart
), FALSE
);
2020 KeQuerySystemTime(&time
);
2021 win_time_to_unix(time
, &now
);
2023 fcb
->inode_item
.transid
= Vcb
->superblock
.generation
;
2024 fcb
->inode_item
.sequence
++;
2026 if (!ccb
->user_set_change_time
)
2027 fcb
->inode_item
.st_ctime
= now
;
2029 if (!ccb
->user_set_write_time
)
2030 fcb
->inode_item
.st_mtime
= now
;
2032 fcb
->extents_changed
= TRUE
;
2033 fcb
->inode_item_changed
= TRUE
;
2034 mark_fcb_dirty(fcb
);
2036 send_notification_fcb(fileref
, FILE_NOTIFY_CHANGE_LAST_WRITE
, FILE_ACTION_MODIFIED
, NULL
);
2038 fcb
->subvol
->root_item
.ctransid
= Vcb
->superblock
.generation
;
2039 fcb
->subvol
->root_item
.ctime
= now
;
2041 Status
= STATUS_SUCCESS
;
2044 if (!NT_SUCCESS(Status
))
2045 do_rollback(Vcb
, &rollback
);
2047 clear_rollback(&rollback
);
2049 ExReleaseResourceLite(fcb
->Header
.Resource
);
2050 ExReleaseResourceLite(&Vcb
->tree_lock
);
2055 static NTSTATUS
query_ranges(PFILE_OBJECT FileObject
, FILE_ALLOCATED_RANGE_BUFFER
* inbuf
, ULONG inbuflen
, void* outbuf
, ULONG outbuflen
, ULONG_PTR
* retlen
) {
2059 FILE_ALLOCATED_RANGE_BUFFER
* ranges
= outbuf
;
2061 UINT64 last_start
, last_end
;
2063 TRACE("FSCTL_QUERY_ALLOCATED_RANGES\n");
2066 ERR("FileObject was NULL\n");
2067 return STATUS_INVALID_PARAMETER
;
2070 if (!inbuf
|| inbuflen
< sizeof(FILE_ALLOCATED_RANGE_BUFFER
) || !outbuf
)
2071 return STATUS_INVALID_PARAMETER
;
2073 fcb
= FileObject
->FsContext
;
2076 ERR("FCB was NULL\n");
2077 return STATUS_INVALID_PARAMETER
;
2080 ExAcquireResourceSharedLite(fcb
->Header
.Resource
, TRUE
);
2082 // If file is not marked as sparse, claim the whole thing as an allocated range
2084 if (!(fcb
->atts
& FILE_ATTRIBUTE_SPARSE_FILE
)) {
2085 if (fcb
->inode_item
.st_size
== 0)
2086 Status
= STATUS_SUCCESS
;
2087 else if (outbuflen
< sizeof(FILE_ALLOCATED_RANGE_BUFFER
))
2088 Status
= STATUS_BUFFER_TOO_SMALL
;
2090 ranges
[i
].FileOffset
.QuadPart
= 0;
2091 ranges
[i
].Length
.QuadPart
= fcb
->inode_item
.st_size
;
2093 Status
= STATUS_SUCCESS
;
2100 le
= fcb
->extents
.Flink
;
2105 while (le
!= &fcb
->extents
) {
2106 extent
* ext
= CONTAINING_RECORD(le
, extent
, list_entry
);
2109 EXTENT_DATA2
* ed2
= (ext
->extent_data
.type
== EXTENT_TYPE_REGULAR
|| ext
->extent_data
.type
== EXTENT_TYPE_PREALLOC
) ? (EXTENT_DATA2
*)ext
->extent_data
.data
: NULL
;
2110 UINT64 len
= ed2
? ed2
->num_bytes
: ext
->extent_data
.decoded_size
;
2112 if (ext
->offset
> last_end
) { // first extent after a hole
2113 if (last_end
> last_start
) {
2114 if ((i
+ 1) * sizeof(FILE_ALLOCATED_RANGE_BUFFER
) <= outbuflen
) {
2115 ranges
[i
].FileOffset
.QuadPart
= last_start
;
2116 ranges
[i
].Length
.QuadPart
= min(fcb
->inode_item
.st_size
, last_end
) - last_start
;
2119 Status
= STATUS_BUFFER_TOO_SMALL
;
2124 last_start
= ext
->offset
;
2127 last_end
= ext
->offset
+ len
;
2133 if (last_end
> last_start
) {
2134 if ((i
+ 1) * sizeof(FILE_ALLOCATED_RANGE_BUFFER
) <= outbuflen
) {
2135 ranges
[i
].FileOffset
.QuadPart
= last_start
;
2136 ranges
[i
].Length
.QuadPart
= min(fcb
->inode_item
.st_size
, last_end
) - last_start
;
2139 Status
= STATUS_BUFFER_TOO_SMALL
;
2144 Status
= STATUS_SUCCESS
;
2147 *retlen
= i
* sizeof(FILE_ALLOCATED_RANGE_BUFFER
);
2149 ExReleaseResourceLite(fcb
->Header
.Resource
);
2154 static NTSTATUS
get_object_id(device_extension
* Vcb
, PFILE_OBJECT FileObject
, FILE_OBJECTID_BUFFER
* buf
, ULONG buflen
, ULONG_PTR
* retlen
) {
2157 TRACE("(%p, %p, %p, %x, %p)\n", Vcb
, FileObject
, buf
, buflen
, retlen
);
2160 ERR("FileObject was NULL\n");
2161 return STATUS_INVALID_PARAMETER
;
2164 if (!buf
|| buflen
< sizeof(FILE_OBJECTID_BUFFER
))
2165 return STATUS_INVALID_PARAMETER
;
2167 fcb
= FileObject
->FsContext
;
2170 ERR("FCB was NULL\n");
2171 return STATUS_INVALID_PARAMETER
;
2174 ExAcquireResourceSharedLite(fcb
->Header
.Resource
, TRUE
);
2176 RtlCopyMemory(&buf
->ObjectId
[0], &fcb
->inode
, sizeof(UINT64
));
2177 RtlCopyMemory(&buf
->ObjectId
[sizeof(UINT64
)], &fcb
->subvol
->id
, sizeof(UINT64
));
2179 ExReleaseResourceLite(fcb
->Header
.Resource
);
2181 RtlZeroMemory(&buf
->ExtendedInfo
, sizeof(buf
->ExtendedInfo
));
2183 *retlen
= sizeof(FILE_OBJECTID_BUFFER
);
2185 return STATUS_SUCCESS
;
2188 static void flush_fcb_caches(device_extension
* Vcb
) {
2191 le
= Vcb
->all_fcbs
.Flink
;
2192 while (le
!= &Vcb
->all_fcbs
) {
2193 struct _fcb
* fcb
= CONTAINING_RECORD(le
, struct _fcb
, list_entry_all
);
2194 IO_STATUS_BLOCK iosb
;
2196 if (fcb
->type
!= BTRFS_TYPE_DIRECTORY
&& !fcb
->deleted
)
2197 CcFlushCache(&fcb
->nonpaged
->segment_object
, NULL
, 0, &iosb
);
2203 static NTSTATUS
lock_volume(device_extension
* Vcb
, PIRP Irp
) {
2204 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
2207 BOOL lock_paused_balance
= FALSE
;
2209 TRACE("FSCTL_LOCK_VOLUME\n");
2211 if (Vcb
->scrub
.thread
) {
2212 WARN("cannot lock while scrub running\n");
2213 return STATUS_DEVICE_NOT_READY
;
2216 if (Vcb
->balance
.thread
) {
2217 WARN("cannot lock while balance running\n");
2218 return STATUS_DEVICE_NOT_READY
;
2221 TRACE("locking volume\n");
2223 FsRtlNotifyVolumeEvent(IrpSp
->FileObject
, FSRTL_VOLUME_LOCK
);
2226 return STATUS_SUCCESS
;
2228 ExAcquireResourceExclusiveLite(&Vcb
->fileref_lock
, TRUE
);
2230 if (Vcb
->root_fileref
&& Vcb
->root_fileref
->fcb
&& (Vcb
->root_fileref
->open_count
> 0 || has_open_children(Vcb
->root_fileref
))) {
2231 Status
= STATUS_ACCESS_DENIED
;
2232 ExReleaseResourceLite(&Vcb
->fileref_lock
);
2236 ExReleaseResourceLite(&Vcb
->fileref_lock
);
2238 if (Vcb
->balance
.thread
&& KeReadStateEvent(&Vcb
->balance
.event
)) {
2239 ExAcquireResourceExclusiveLite(&Vcb
->tree_lock
, TRUE
);
2240 KeClearEvent(&Vcb
->balance
.event
);
2241 ExReleaseResourceLite(&Vcb
->tree_lock
);
2243 lock_paused_balance
= TRUE
;
2246 ExAcquireResourceExclusiveLite(&Vcb
->tree_lock
, TRUE
);
2248 flush_fcb_caches(Vcb
);
2250 if (Vcb
->need_write
&& !Vcb
->readonly
)
2251 Status
= do_write(Vcb
, Irp
);
2253 Status
= STATUS_SUCCESS
;
2257 ExReleaseResourceLite(&Vcb
->tree_lock
);
2259 if (!NT_SUCCESS(Status
)) {
2260 ERR("do_write returned %08x\n", Status
);
2264 IoAcquireVpbSpinLock(&irql
);
2266 if (!(Vcb
->Vpb
->Flags
& VPB_LOCKED
)) {
2267 Vcb
->Vpb
->Flags
|= VPB_LOCKED
;
2269 Vcb
->locked_fileobj
= IrpSp
->FileObject
;
2270 Vcb
->lock_paused_balance
= lock_paused_balance
;
2272 Status
= STATUS_ACCESS_DENIED
;
2273 IoReleaseVpbSpinLock(irql
);
2275 if (lock_paused_balance
)
2276 KeSetEvent(&Vcb
->balance
.event
, 0, FALSE
);
2281 IoReleaseVpbSpinLock(irql
);
2283 Status
= STATUS_SUCCESS
;
2286 if (!NT_SUCCESS(Status
))
2287 FsRtlNotifyVolumeEvent(IrpSp
->FileObject
, FSRTL_VOLUME_LOCK_FAILED
);
2292 void do_unlock_volume(device_extension
* Vcb
) {
2295 IoAcquireVpbSpinLock(&irql
);
2297 Vcb
->locked
= FALSE
;
2298 Vcb
->Vpb
->Flags
&= ~VPB_LOCKED
;
2299 Vcb
->locked_fileobj
= NULL
;
2301 IoReleaseVpbSpinLock(irql
);
2303 if (Vcb
->lock_paused_balance
)
2304 KeSetEvent(&Vcb
->balance
.event
, 0, FALSE
);
2307 static NTSTATUS
unlock_volume(device_extension
* Vcb
, PIRP Irp
) {
2308 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
2310 TRACE("FSCTL_UNLOCK_VOLUME\n");
2312 if (!Vcb
->locked
|| IrpSp
->FileObject
!= Vcb
->locked_fileobj
)
2313 return STATUS_NOT_LOCKED
;
2315 TRACE("unlocking volume\n");
2317 do_unlock_volume(Vcb
);
2319 FsRtlNotifyVolumeEvent(IrpSp
->FileObject
, FSRTL_VOLUME_UNLOCK
);
2321 return STATUS_SUCCESS
;
2324 static NTSTATUS
invalidate_volumes(PIRP Irp
) {
2325 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
2326 LUID TcbPrivilege
= {SE_TCB_PRIVILEGE
, 0};
2329 PFILE_OBJECT fileobj
;
2330 PDEVICE_OBJECT devobj
;
2333 TRACE("FSCTL_INVALIDATE_VOLUMES\n");
2335 if (!SeSinglePrivilegeCheck(TcbPrivilege
, Irp
->RequestorMode
))
2336 return STATUS_PRIVILEGE_NOT_HELD
;
2339 if (IoIs32bitProcess(Irp
)) {
2340 if (IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
!= sizeof(UINT32
))
2341 return STATUS_INVALID_PARAMETER
;
2343 h
= (HANDLE
)LongToHandle((*(PUINT32
)Irp
->AssociatedIrp
.SystemBuffer
));
2346 if (IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
!= sizeof(HANDLE
))
2347 return STATUS_INVALID_PARAMETER
;
2349 h
= *(PHANDLE
)Irp
->AssociatedIrp
.SystemBuffer
;
2354 Status
= ObReferenceObjectByHandle(h
, 0, *IoFileObjectType
, Irp
->RequestorMode
, (void**)&fileobj
, NULL
);
2356 if (!NT_SUCCESS(Status
)) {
2357 ERR("ObReferenceObjectByHandle returned %08x\n", Status
);
2361 devobj
= fileobj
->DeviceObject
;
2363 ExAcquireResourceSharedLite(&global_loading_lock
, TRUE
);
2367 while (le
!= &VcbList
) {
2368 device_extension
* Vcb
= CONTAINING_RECORD(le
, device_extension
, list_entry
);
2370 if (Vcb
->Vpb
&& Vcb
->Vpb
->RealDevice
== devobj
) {
2371 if (Vcb
->Vpb
== devobj
->Vpb
) {
2374 BOOL free_newvpb
= FALSE
;
2376 newvpb
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(VPB
), ALLOC_TAG
);
2378 ERR("out of memory\n");
2379 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2383 RtlZeroMemory(newvpb
, sizeof(VPB
));
2385 ExAcquireResourceExclusiveLite(&Vcb
->tree_lock
, TRUE
);
2387 Vcb
->removing
= TRUE
;
2389 ExReleaseResourceLite(&Vcb
->tree_lock
);
2391 CcWaitForCurrentLazyWriterActivity();
2393 ExAcquireResourceExclusiveLite(&Vcb
->tree_lock
, TRUE
);
2395 flush_fcb_caches(Vcb
);
2397 if (Vcb
->need_write
&& !Vcb
->readonly
)
2398 Status
= do_write(Vcb
, Irp
);
2400 Status
= STATUS_SUCCESS
;
2404 if (!NT_SUCCESS(Status
)) {
2405 ERR("do_write returned %08x\n", Status
);
2406 ExReleaseResourceLite(&Vcb
->tree_lock
);
2411 flush_fcb_caches(Vcb
);
2413 ExReleaseResourceLite(&Vcb
->tree_lock
);
2415 IoAcquireVpbSpinLock(&irql
);
2417 if (devobj
->Vpb
->Flags
& VPB_MOUNTED
) {
2418 newvpb
->Type
= IO_TYPE_VPB
;
2419 newvpb
->Size
= sizeof(VPB
);
2420 newvpb
->RealDevice
= devobj
;
2421 newvpb
->Flags
= devobj
->Vpb
->Flags
& VPB_REMOVE_PENDING
;
2423 devobj
->Vpb
= newvpb
;
2427 IoReleaseVpbSpinLock(irql
);
2432 if (Vcb
->open_files
== 0)
2442 Status
= STATUS_SUCCESS
;
2445 ExReleaseResourceLite(&global_loading_lock
);
2447 ObDereferenceObject(fileobj
);
2452 static NTSTATUS
is_volume_dirty(device_extension
* Vcb
, PIRP Irp
) {
2453 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
2456 if (Irp
->AssociatedIrp
.SystemBuffer
) {
2457 volstate
= Irp
->AssociatedIrp
.SystemBuffer
;
2458 } else if (Irp
->MdlAddress
!= NULL
) {
2459 volstate
= MmGetSystemAddressForMdlSafe(Irp
->MdlAddress
, LowPagePriority
);
2462 return STATUS_INSUFFICIENT_RESOURCES
;
2464 return STATUS_INVALID_USER_BUFFER
;
2466 if (IrpSp
->Parameters
.FileSystemControl
.OutputBufferLength
< sizeof(ULONG
))
2467 return STATUS_INVALID_PARAMETER
;
2471 if (IrpSp
->FileObject
->FsContext
!= Vcb
->volume_fcb
)
2472 return STATUS_INVALID_PARAMETER
;
2474 Irp
->IoStatus
.Information
= sizeof(ULONG
);
2476 return STATUS_SUCCESS
;
2479 static NTSTATUS
get_compression(PIRP Irp
) {
2480 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
2481 USHORT
* compression
;
2483 TRACE("FSCTL_GET_COMPRESSION\n");
2485 if (Irp
->AssociatedIrp
.SystemBuffer
) {
2486 compression
= Irp
->AssociatedIrp
.SystemBuffer
;
2487 } else if (Irp
->MdlAddress
!= NULL
) {
2488 compression
= MmGetSystemAddressForMdlSafe(Irp
->MdlAddress
, LowPagePriority
);
2491 return STATUS_INSUFFICIENT_RESOURCES
;
2493 return STATUS_INVALID_USER_BUFFER
;
2495 if (IrpSp
->Parameters
.FileSystemControl
.OutputBufferLength
< sizeof(USHORT
))
2496 return STATUS_INVALID_PARAMETER
;
2498 *compression
= COMPRESSION_FORMAT_NONE
;
2500 Irp
->IoStatus
.Information
= sizeof(USHORT
);
2502 return STATUS_SUCCESS
;
2505 static NTSTATUS
set_compression(PIRP Irp
) {
2506 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
2507 USHORT
* compression
;
2509 TRACE("FSCTL_SET_COMPRESSION\n");
2511 if (IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
< sizeof(USHORT
))
2512 return STATUS_INVALID_PARAMETER
;
2514 compression
= Irp
->AssociatedIrp
.SystemBuffer
;
2516 if (*compression
!= COMPRESSION_FORMAT_NONE
)
2517 return STATUS_INVALID_PARAMETER
;
2519 return STATUS_SUCCESS
;
2522 static void update_volumes(device_extension
* Vcb
) {
2524 volume_device_extension
* vde
= Vcb
->vde
;
2525 pdo_device_extension
* pdode
= vde
->pdode
;
2527 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, TRUE
);
2529 ExAcquireResourceExclusiveLite(&pdode
->child_lock
, TRUE
);
2531 le
= pdode
->children
.Flink
;
2532 while (le
!= &pdode
->children
) {
2533 volume_child
* vc
= CONTAINING_RECORD(le
, volume_child
, list_entry
);
2535 vc
->generation
= Vcb
->superblock
.generation
- 1;
2540 ExReleaseResourceLite(&pdode
->child_lock
);
2542 ExReleaseResourceLite(&Vcb
->tree_lock
);
2545 static NTSTATUS
dismount_volume(device_extension
* Vcb
, PIRP Irp
) {
2548 TRACE("FSCTL_DISMOUNT_VOLUME\n");
2550 if (!(Vcb
->Vpb
->Flags
& VPB_MOUNTED
))
2551 return STATUS_SUCCESS
;
2553 if (Vcb
->disallow_dismount
) {
2554 WARN("attempting to dismount boot volume or one containing a pagefile\n");
2555 return STATUS_ACCESS_DENIED
;
2558 Status
= FsRtlNotifyVolumeEvent(Vcb
->root_file
, FSRTL_VOLUME_DISMOUNT
);
2559 if (!NT_SUCCESS(Status
)) {
2560 WARN("FsRtlNotifyVolumeEvent returned %08x\n", Status
);
2563 ExAcquireResourceExclusiveLite(&Vcb
->tree_lock
, TRUE
);
2566 flush_fcb_caches(Vcb
);
2568 if (Vcb
->need_write
&& !Vcb
->readonly
) {
2569 Status
= do_write(Vcb
, Irp
);
2571 if (!NT_SUCCESS(Status
))
2572 ERR("do_write returned %08x\n", Status
);
2578 Vcb
->removing
= TRUE
;
2581 update_volumes(Vcb
);
2582 Vcb
->vde
->mounted_device
= NULL
;
2585 ExReleaseResourceLite(&Vcb
->tree_lock
);
2587 return STATUS_SUCCESS
;
2590 static NTSTATUS
is_device_part_of_mounted_btrfs_raid(PDEVICE_OBJECT devobj
) {
2595 BTRFS_UUID fsuuid
, devuuid
;
2598 to_read
= devobj
->SectorSize
== 0 ? sizeof(superblock
) : (ULONG
)sector_align(sizeof(superblock
), devobj
->SectorSize
);
2600 sb
= ExAllocatePoolWithTag(PagedPool
, to_read
, ALLOC_TAG
);
2602 ERR("out of memory\n");
2603 return STATUS_INSUFFICIENT_RESOURCES
;
2606 Status
= sync_read_phys(devobj
, superblock_addrs
[0], to_read
, (UINT8
*)sb
, TRUE
);
2607 if (!NT_SUCCESS(Status
)) {
2608 ERR("sync_read_phys returned %08x\n", Status
);
2613 if (sb
->magic
!= BTRFS_MAGIC
) {
2614 TRACE("device is not Btrfs\n");
2616 return STATUS_SUCCESS
;
2619 crc32
= ~calc_crc32c(0xffffffff, (UINT8
*)&sb
->uuid
, (ULONG
)sizeof(superblock
) - sizeof(sb
->checksum
));
2621 if (crc32
!= *((UINT32
*)sb
->checksum
)) {
2622 TRACE("device has Btrfs magic, but invalid superblock checksum\n");
2624 return STATUS_SUCCESS
;
2628 devuuid
= sb
->dev_item
.device_uuid
;
2632 ExAcquireResourceSharedLite(&global_loading_lock
, TRUE
);
2636 while (le
!= &VcbList
) {
2637 device_extension
* Vcb
= CONTAINING_RECORD(le
, device_extension
, list_entry
);
2639 if (RtlCompareMemory(&Vcb
->superblock
.uuid
, &fsuuid
, sizeof(BTRFS_UUID
)) == sizeof(BTRFS_UUID
)) {
2642 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, TRUE
);
2644 if (Vcb
->superblock
.num_devices
> 1) {
2645 le2
= Vcb
->devices
.Flink
;
2646 while (le2
!= &Vcb
->devices
) {
2647 device
* dev
= CONTAINING_RECORD(le2
, device
, list_entry
);
2649 if (RtlCompareMemory(&dev
->devitem
.device_uuid
, &devuuid
, sizeof(BTRFS_UUID
)) == sizeof(BTRFS_UUID
)) {
2650 ExReleaseResourceLite(&Vcb
->tree_lock
);
2651 ExReleaseResourceLite(&global_loading_lock
);
2652 return STATUS_DEVICE_NOT_READY
;
2659 ExReleaseResourceLite(&Vcb
->tree_lock
);
2660 ExReleaseResourceLite(&global_loading_lock
);
2661 return STATUS_SUCCESS
;
2667 ExReleaseResourceLite(&global_loading_lock
);
2669 return STATUS_SUCCESS
;
2672 void trim_whole_device(device
* dev
) {
2673 DEVICE_MANAGE_DATA_SET_ATTRIBUTES dmdsa
;
2676 // FIXME - avoid "bootloader area"??
2678 dmdsa
.Size
= sizeof(DEVICE_MANAGE_DATA_SET_ATTRIBUTES
);
2679 dmdsa
.Action
= DeviceDsmAction_Trim
;
2680 dmdsa
.Flags
= DEVICE_DSM_FLAG_ENTIRE_DATA_SET_RANGE
| DEVICE_DSM_FLAG_TRIM_NOT_FS_ALLOCATED
;
2681 dmdsa
.ParameterBlockOffset
= 0;
2682 dmdsa
.ParameterBlockLength
= 0;
2683 dmdsa
.DataSetRangesOffset
= 0;
2684 dmdsa
.DataSetRangesLength
= 0;
2686 Status
= dev_ioctl(dev
->devobj
, IOCTL_STORAGE_MANAGE_DATA_SET_ATTRIBUTES
, &dmdsa
, sizeof(DEVICE_MANAGE_DATA_SET_ATTRIBUTES
), NULL
, 0, TRUE
, NULL
);
2687 if (!NT_SUCCESS(Status
))
2688 WARN("IOCTL_STORAGE_MANAGE_DATA_SET_ATTRIBUTES returned %08x\n", Status
);
2691 static NTSTATUS
add_device(device_extension
* Vcb
, PIRP Irp
, KPROCESSOR_MODE processor_mode
) {
2692 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
2694 PFILE_OBJECT fileobj
, mountmgrfo
;
2695 PDEVICE_OBJECT DeviceObject
;
2700 UINT64 dev_id
, size
;
2703 UNICODE_STRING mmdevpath
, pnp_name
, pnp_name2
;
2705 PDEVICE_OBJECT mountmgr
;
2708 STORAGE_DEVICE_NUMBER sdn
;
2709 volume_device_extension
* vde
;
2710 pdo_device_extension
* pdode
;
2711 const GUID
* pnp_guid
;
2712 GET_LENGTH_INFORMATION gli
;
2714 pnp_name
.Buffer
= NULL
;
2716 if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE
), processor_mode
))
2717 return STATUS_PRIVILEGE_NOT_HELD
;
2720 WARN("not allowing second device to be added to non-PNP device\n");
2721 return STATUS_NOT_SUPPORTED
;
2724 if (Vcb
->readonly
) // FIXME - handle adding R/W device to seeding device
2725 return STATUS_MEDIA_WRITE_PROTECTED
;
2728 if (IoIs32bitProcess(Irp
)) {
2729 if (IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
!= sizeof(UINT32
))
2730 return STATUS_INVALID_PARAMETER
;
2732 h
= (HANDLE
)LongToHandle((*(PUINT32
)Irp
->AssociatedIrp
.SystemBuffer
));
2735 if (IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
!= sizeof(HANDLE
))
2736 return STATUS_INVALID_PARAMETER
;
2738 h
= *(PHANDLE
)Irp
->AssociatedIrp
.SystemBuffer
;
2743 Status
= ObReferenceObjectByHandle(h
, 0, *IoFileObjectType
, Irp
->RequestorMode
, (void**)&fileobj
, NULL
);
2745 if (!NT_SUCCESS(Status
)) {
2746 ERR("ObReferenceObjectByHandle returned %08x\n", Status
);
2750 DeviceObject
= fileobj
->DeviceObject
;
2752 Status
= get_device_pnp_name(DeviceObject
, &pnp_name
, &pnp_guid
);
2753 if (!NT_SUCCESS(Status
)) {
2754 ERR("get_device_pnp_name returned %08x\n", Status
);
2755 ObDereferenceObject(fileobj
);
2759 // If this is a disk, we have been handed the PDO, so need to go up to find something we can use
2760 if (RtlCompareMemory(pnp_guid
, &GUID_DEVINTERFACE_DISK
, sizeof(GUID
)) == sizeof(GUID
) && DeviceObject
->AttachedDevice
)
2761 DeviceObject
= DeviceObject
->AttachedDevice
;
2763 Status
= dev_ioctl(DeviceObject
, IOCTL_DISK_IS_WRITABLE
, NULL
, 0, NULL
, 0, TRUE
, NULL
);
2764 if (!NT_SUCCESS(Status
)) {
2765 ERR("IOCTL_DISK_IS_WRITABLE returned %08x\n", Status
);
2766 ObDereferenceObject(fileobj
);
2770 Status
= is_device_part_of_mounted_btrfs_raid(DeviceObject
);
2771 if (!NT_SUCCESS(Status
)) {
2772 ERR("is_device_part_of_mounted_btrfs_raid returned %08x\n", Status
);
2773 ObDereferenceObject(fileobj
);
2777 // if disk, check it has no partitions
2778 if (RtlCompareMemory(pnp_guid
, &GUID_DEVINTERFACE_DISK
, sizeof(GUID
)) == sizeof(GUID
)) {
2780 DRIVE_LAYOUT_INFORMATION_EX
* dli
= NULL
;
2790 dli
= ExAllocatePoolWithTag(PagedPool
, dlisize
, ALLOC_TAG
);
2792 ERR("out of memory\n");
2793 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2797 Status
= dev_ioctl(DeviceObject
, IOCTL_DISK_GET_DRIVE_LAYOUT_EX
, NULL
, 0, dli
, dlisize
, TRUE
, NULL
);
2798 } while (Status
== STATUS_BUFFER_TOO_SMALL
);
2800 if (NT_SUCCESS(Status
) && dli
->PartitionCount
> 0) {
2802 ERR("not adding disk which has partitions\n");
2803 Status
= STATUS_DEVICE_NOT_READY
;
2810 Status
= dev_ioctl(DeviceObject
, IOCTL_STORAGE_GET_DEVICE_NUMBER
, NULL
, 0,
2811 &sdn
, sizeof(STORAGE_DEVICE_NUMBER
), TRUE
, NULL
);
2812 if (NT_SUCCESS(Status
)) {
2813 if (sdn
.DeviceType
!= FILE_DEVICE_DISK
) { // FIXME - accept floppies and CDs?
2814 WARN("device was not disk\n");
2815 ObDereferenceObject(fileobj
);
2816 return STATUS_INVALID_PARAMETER
;
2819 sdn
.DeviceNumber
= 0xffffffff;
2820 sdn
.PartitionNumber
= 0xffffffff;
2823 Status
= dev_ioctl(DeviceObject
, IOCTL_DISK_GET_LENGTH_INFO
, NULL
, 0,
2824 &gli
, sizeof(gli
), TRUE
, NULL
);
2825 if (!NT_SUCCESS(Status
)) {
2826 ERR("error reading length information: %08x\n", Status
);
2827 ObDereferenceObject(fileobj
);
2831 size
= gli
.Length
.QuadPart
;
2833 if (size
< 0x100000) {
2834 ERR("device was not large enough to hold FS (%llx bytes, need at least 1 MB)\n", size
);
2835 ObDereferenceObject(fileobj
);
2836 return STATUS_INTERNAL_ERROR
;
2839 volume_removal(drvobj
, &pnp_name
);
2841 ExAcquireResourceExclusiveLite(&Vcb
->tree_lock
, TRUE
);
2843 if (Vcb
->need_write
)
2844 Status
= do_write(Vcb
, Irp
);
2846 Status
= STATUS_SUCCESS
;
2850 if (!NT_SUCCESS(Status
)) {
2851 ERR("do_write returned %08x\n", Status
);
2855 dev
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(device
), ALLOC_TAG
);
2857 ERR("out of memory\n");
2858 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2862 RtlZeroMemory(dev
, sizeof(device
));
2864 dev
->devobj
= DeviceObject
;
2865 dev
->seeding
= FALSE
;
2866 init_device(Vcb
, dev
, TRUE
);
2868 InitializeListHead(&dev
->space
);
2870 if (size
> 0x100000) { // add disk hole - the first MB is marked as used
2871 Status
= add_space_entry(&dev
->space
, NULL
, 0x100000, size
- 0x100000);
2872 if (!NT_SUCCESS(Status
)) {
2873 ERR("add_space_entry returned %08x\n", Status
);
2880 le
= Vcb
->devices
.Flink
;
2881 while (le
!= &Vcb
->devices
) {
2882 device
* dev2
= CONTAINING_RECORD(le
, device
, list_entry
);
2884 if (dev2
->devitem
.dev_id
> dev_id
)
2885 dev_id
= dev2
->devitem
.dev_id
;
2892 dev
->devitem
.dev_id
= dev_id
;
2893 dev
->devitem
.num_bytes
= size
;
2894 dev
->devitem
.bytes_used
= 0;
2895 dev
->devitem
.optimal_io_align
= Vcb
->superblock
.sector_size
;
2896 dev
->devitem
.optimal_io_width
= Vcb
->superblock
.sector_size
;
2897 dev
->devitem
.minimal_io_size
= Vcb
->superblock
.sector_size
;
2898 dev
->devitem
.type
= 0;
2899 dev
->devitem
.generation
= 0;
2900 dev
->devitem
.start_offset
= 0;
2901 dev
->devitem
.dev_group
= 0;
2902 dev
->devitem
.seek_speed
= 0;
2903 dev
->devitem
.bandwidth
= 0;
2904 get_uuid(&dev
->devitem
.device_uuid
);
2905 dev
->devitem
.fs_uuid
= Vcb
->superblock
.uuid
;
2907 di
= ExAllocatePoolWithTag(PagedPool
, sizeof(DEV_ITEM
), ALLOC_TAG
);
2909 ERR("out of memory\n");
2913 RtlCopyMemory(di
, &dev
->devitem
, sizeof(DEV_ITEM
));
2915 Status
= insert_tree_item(Vcb
, Vcb
->chunk_root
, 1, TYPE_DEV_ITEM
, di
->dev_id
, di
, sizeof(DEV_ITEM
), NULL
, Irp
);
2916 if (!NT_SUCCESS(Status
)) {
2917 ERR("insert_tree_item returned %08x\n", Status
);
2922 // add stats entry to dev tree
2923 stats
= ExAllocatePoolWithTag(PagedPool
, sizeof(UINT64
) * 5, ALLOC_TAG
);
2925 ERR("out of memory\n");
2926 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2930 RtlZeroMemory(stats
, sizeof(UINT64
) * 5);
2932 searchkey
.obj_id
= 0;
2933 searchkey
.obj_type
= TYPE_DEV_STATS
;
2934 searchkey
.offset
= di
->dev_id
;
2936 Status
= find_item(Vcb
, Vcb
->dev_root
, &tp
, &searchkey
, FALSE
, Irp
);
2937 if (!NT_SUCCESS(Status
)) {
2938 ERR("error - find_item returned %08x\n", Status
);
2943 if (!keycmp(tp
.item
->key
, searchkey
)) {
2944 Status
= delete_tree_item(Vcb
, &tp
);
2945 if (!NT_SUCCESS(Status
)) {
2946 ERR("delete_tree_item returned %08x\n", Status
);
2952 Status
= insert_tree_item(Vcb
, Vcb
->dev_root
, 0, TYPE_DEV_STATS
, di
->dev_id
, stats
, sizeof(UINT64
) * 5, NULL
, Irp
);
2953 if (!NT_SUCCESS(Status
)) {
2954 ERR("insert_tree_item returned %08x\n", Status
);
2959 if (dev
->trim
&& !dev
->readonly
&& !Vcb
->options
.no_trim
)
2960 trim_whole_device(dev
);
2962 // We clear the first megabyte of the device, so Windows doesn't identify it as another FS
2963 mb
= ExAllocatePoolWithTag(PagedPool
, 0x100000, ALLOC_TAG
);
2965 ERR("out of memory\n");
2966 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2970 RtlZeroMemory(mb
, 0x100000);
2972 Status
= write_data_phys(DeviceObject
, 0, mb
, 0x100000);
2973 if (!NT_SUCCESS(Status
)) {
2974 ERR("write_data_phys returned %08x\n", Status
);
2984 vc
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(volume_child
), ALLOC_TAG
);
2986 ERR("out of memory\n");
2987 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2991 vc
->uuid
= dev
->devitem
.device_uuid
;
2993 vc
->generation
= Vcb
->superblock
.generation
;
2994 vc
->devobj
= DeviceObject
;
2995 vc
->fileobj
= fileobj
;
2996 vc
->notification_entry
= NULL
;
2998 Status
= IoRegisterPlugPlayNotification(EventCategoryTargetDeviceChange
, 0, fileobj
,
2999 drvobj
, pnp_removal
, vde
->pdode
, &vc
->notification_entry
);
3000 if (!NT_SUCCESS(Status
))
3001 WARN("IoRegisterPlugPlayNotification returned %08x\n", Status
);
3003 pnp_name2
= pnp_name
;
3005 if (pnp_name
.Length
> 4 * sizeof(WCHAR
) && pnp_name
.Buffer
[0] == '\\' && (pnp_name
.Buffer
[1] == '\\' || pnp_name
.Buffer
[1] == '?') &&
3006 pnp_name
.Buffer
[2] == '?' && pnp_name
.Buffer
[3] == '\\') {
3007 pnp_name2
.Buffer
= &pnp_name2
.Buffer
[3];
3008 pnp_name2
.Length
-= 3 * sizeof(WCHAR
);
3009 pnp_name2
.MaximumLength
-= 3 * sizeof(WCHAR
);
3012 vc
->pnp_name
.Length
= vc
->pnp_name
.MaximumLength
= pnp_name2
.Length
;
3014 if (pnp_name2
.Length
== 0)
3015 vc
->pnp_name
.Buffer
= NULL
;
3017 vc
->pnp_name
.Buffer
= ExAllocatePoolWithTag(PagedPool
, pnp_name2
.Length
, ALLOC_TAG
);
3018 if (!vc
->pnp_name
.Buffer
) {
3019 ERR("out of memory\n");
3020 Status
= STATUS_INSUFFICIENT_RESOURCES
;
3024 RtlCopyMemory(vc
->pnp_name
.Buffer
, pnp_name2
.Buffer
, pnp_name2
.Length
);
3028 vc
->seeding
= FALSE
;
3029 vc
->disk_num
= sdn
.DeviceNumber
;
3030 vc
->part_num
= sdn
.PartitionNumber
;
3031 vc
->had_drive_letter
= FALSE
;
3033 ExAcquireResourceExclusiveLite(&pdode
->child_lock
, TRUE
);
3034 InsertTailList(&pdode
->children
, &vc
->list_entry
);
3035 pdode
->num_children
++;
3036 pdode
->children_loaded
++;
3037 ExReleaseResourceLite(&pdode
->child_lock
);
3039 RtlInitUnicodeString(&mmdevpath
, MOUNTMGR_DEVICE_NAME
);
3040 Status
= IoGetDeviceObjectPointer(&mmdevpath
, FILE_READ_ATTRIBUTES
, &mountmgrfo
, &mountmgr
);
3041 if (!NT_SUCCESS(Status
))
3042 ERR("IoGetDeviceObjectPointer returned %08x\n", Status
);
3044 Status
= remove_drive_letter(mountmgr
, &pnp_name
);
3045 if (!NT_SUCCESS(Status
) && Status
!= STATUS_NOT_FOUND
)
3046 WARN("remove_drive_letter returned %08x\n", Status
);
3048 vc
->had_drive_letter
= NT_SUCCESS(Status
);
3050 ObDereferenceObject(mountmgrfo
);
3053 Vcb
->superblock
.num_devices
++;
3054 Vcb
->superblock
.total_bytes
+= size
;
3055 Vcb
->devices_loaded
++;
3056 InsertTailList(&Vcb
->devices
, &dev
->list_entry
);
3058 // FIXME - send notification that volume size has increased
3060 ObReferenceObject(DeviceObject
); // for Vcb
3062 Status
= do_write(Vcb
, Irp
);
3063 if (!NT_SUCCESS(Status
))
3064 ERR("do_write returned %08x\n", Status
);
3066 ObReferenceObject(fileobj
);
3071 ExReleaseResourceLite(&Vcb
->tree_lock
);
3074 ObDereferenceObject(fileobj
);
3076 if (pnp_name
.Buffer
)
3077 ExFreePool(pnp_name
.Buffer
);
3079 if (NT_SUCCESS(Status
))
3080 FsRtlNotifyVolumeEvent(Vcb
->root_file
, FSRTL_VOLUME_CHANGE_SIZE
);
3085 static NTSTATUS
allow_extended_dasd_io(device_extension
* Vcb
, PFILE_OBJECT FileObject
) {
3089 TRACE("FSCTL_ALLOW_EXTENDED_DASD_IO\n");
3092 return STATUS_INVALID_PARAMETER
;
3094 fcb
= FileObject
->FsContext
;
3095 ccb
= FileObject
->FsContext2
;
3098 return STATUS_INVALID_PARAMETER
;
3100 if (fcb
!= Vcb
->volume_fcb
)
3101 return STATUS_INVALID_PARAMETER
;
3104 return STATUS_INVALID_PARAMETER
;
3106 ccb
->allow_extended_dasd_io
= TRUE
;
3108 return STATUS_SUCCESS
;
3111 static NTSTATUS
query_uuid(device_extension
* Vcb
, void* data
, ULONG length
) {
3112 if (length
< sizeof(BTRFS_UUID
))
3113 return STATUS_BUFFER_OVERFLOW
;
3115 RtlCopyMemory(data
, &Vcb
->superblock
.uuid
, sizeof(BTRFS_UUID
));
3117 return STATUS_SUCCESS
;
3120 static NTSTATUS
reset_stats(device_extension
* Vcb
, void* data
, ULONG length
, KPROCESSOR_MODE processor_mode
) {
3125 if (length
< sizeof(UINT64
))
3126 return STATUS_INVALID_PARAMETER
;
3129 return STATUS_MEDIA_WRITE_PROTECTED
;
3131 if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE
), processor_mode
))
3132 return STATUS_PRIVILEGE_NOT_HELD
;
3134 devid
= *((UINT64
*)data
);
3136 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, TRUE
);
3138 le
= Vcb
->devices
.Flink
;
3140 while (le
!= &Vcb
->devices
) {
3141 device
* dev
= CONTAINING_RECORD(le
, device
, list_entry
);
3143 if (dev
->devitem
.dev_id
== devid
) {
3144 RtlZeroMemory(dev
->stats
, sizeof(UINT64
) * 5);
3145 dev
->stats_changed
= TRUE
;
3146 Vcb
->stats_changed
= TRUE
;
3147 Vcb
->need_write
= TRUE
;
3148 Status
= STATUS_SUCCESS
;
3155 WARN("device %llx not found\n", devid
);
3156 Status
= STATUS_INVALID_PARAMETER
;
3159 ExReleaseResourceLite(&Vcb
->tree_lock
);
3164 static NTSTATUS
get_integrity_information(device_extension
* Vcb
, PFILE_OBJECT FileObject
, void* data
, ULONG datalen
) {
3165 FSCTL_GET_INTEGRITY_INFORMATION_BUFFER
* fgiib
= (FSCTL_GET_INTEGRITY_INFORMATION_BUFFER
*)data
;
3167 TRACE("FSCTL_GET_INTEGRITY_INFORMATION\n");
3172 return STATUS_INVALID_PARAMETER
;
3174 if (!data
|| datalen
< sizeof(FSCTL_GET_INTEGRITY_INFORMATION_BUFFER
))
3175 return STATUS_INVALID_PARAMETER
;
3177 fgiib
->ChecksumAlgorithm
= 0;
3178 fgiib
->Reserved
= 0;
3180 fgiib
->ChecksumChunkSizeInBytes
= Vcb
->superblock
.sector_size
;
3181 fgiib
->ClusterSizeInBytes
= Vcb
->superblock
.sector_size
;
3183 return STATUS_SUCCESS
;
3186 static NTSTATUS
set_integrity_information(PFILE_OBJECT FileObject
, void* data
, ULONG datalen
) {
3187 TRACE("FSCTL_SET_INTEGRITY_INFORMATION\n");
3192 return STATUS_INVALID_PARAMETER
;
3194 if (!data
|| datalen
< sizeof(FSCTL_SET_INTEGRITY_INFORMATION_BUFFER
))
3195 return STATUS_INVALID_PARAMETER
;
3197 return STATUS_SUCCESS
;
3200 BOOL
fcb_is_inline(fcb
* fcb
) {
3203 le
= fcb
->extents
.Flink
;
3204 while (le
!= &fcb
->extents
) {
3205 extent
* ext
= CONTAINING_RECORD(le
, extent
, list_entry
);
3208 return ext
->extent_data
.type
== EXTENT_TYPE_INLINE
;
3216 static NTSTATUS
duplicate_extents(device_extension
* Vcb
, PFILE_OBJECT FileObject
, void* data
, ULONG datalen
, PIRP Irp
) {
3217 DUPLICATE_EXTENTS_DATA
* ded
= (DUPLICATE_EXTENTS_DATA
*)data
;
3218 fcb
*fcb
= FileObject
? FileObject
->FsContext
: NULL
, *sourcefcb
;
3219 ccb
*ccb
= FileObject
? FileObject
->FsContext2
: NULL
, *sourceccb
;
3221 PFILE_OBJECT sourcefo
;
3222 UINT64 sourcelen
, nbytes
= 0;
3223 LIST_ENTRY rollback
, *le
, newexts
;
3228 if (!ded
|| datalen
< sizeof(DUPLICATE_EXTENTS_DATA
))
3229 return STATUS_BUFFER_TOO_SMALL
;
3232 return STATUS_MEDIA_WRITE_PROTECTED
;
3234 if (ded
->ByteCount
.QuadPart
== 0)
3235 return STATUS_SUCCESS
;
3237 if (!fcb
|| !ccb
|| fcb
== Vcb
->volume_fcb
)
3238 return STATUS_INVALID_PARAMETER
;
3240 if (is_subvol_readonly(fcb
->subvol
, Irp
))
3241 return STATUS_ACCESS_DENIED
;
3243 if (Irp
->RequestorMode
== UserMode
&& !(ccb
->access
& FILE_WRITE_DATA
)) {
3244 WARN("insufficient privileges\n");
3245 return STATUS_ACCESS_DENIED
;
3248 if (!fcb
->ads
&& fcb
->type
!= BTRFS_TYPE_FILE
&& fcb
->type
!= BTRFS_TYPE_SYMLINK
)
3249 return STATUS_INVALID_PARAMETER
;
3251 Status
= ObReferenceObjectByHandle(ded
->FileHandle
, 0, *IoFileObjectType
, Irp
->RequestorMode
, (void**)&sourcefo
, NULL
);
3252 if (!NT_SUCCESS(Status
)) {
3253 ERR("ObReferenceObjectByHandle returned %08x\n", Status
);
3257 if (sourcefo
->DeviceObject
!= FileObject
->DeviceObject
) {
3258 WARN("source and destination are on different volumes\n");
3259 ObDereferenceObject(sourcefo
);
3260 return STATUS_INVALID_PARAMETER
;
3263 sourcefcb
= sourcefo
->FsContext
;
3264 sourceccb
= sourcefo
->FsContext2
;
3266 if (!sourcefcb
|| !sourceccb
|| sourcefcb
== Vcb
->volume_fcb
) {
3267 ObDereferenceObject(sourcefo
);
3268 return STATUS_INVALID_PARAMETER
;
3271 if (!sourcefcb
->ads
&& !fcb
->ads
) {
3272 if ((ded
->SourceFileOffset
.QuadPart
& (Vcb
->superblock
.sector_size
- 1)) || (ded
->TargetFileOffset
.QuadPart
& (Vcb
->superblock
.sector_size
- 1))) {
3273 ObDereferenceObject(sourcefo
);
3274 return STATUS_INVALID_PARAMETER
;
3277 if (ded
->ByteCount
.QuadPart
& (Vcb
->superblock
.sector_size
- 1)) {
3278 ObDereferenceObject(sourcefo
);
3279 return STATUS_INVALID_PARAMETER
;
3283 if (Irp
->RequestorMode
== UserMode
&& (!(sourceccb
->access
& FILE_READ_DATA
) || !(sourceccb
->access
& FILE_READ_ATTRIBUTES
))) {
3284 WARN("insufficient privileges\n");
3285 ObDereferenceObject(sourcefo
);
3286 return STATUS_ACCESS_DENIED
;
3289 if (!sourcefcb
->ads
&& sourcefcb
->type
!= BTRFS_TYPE_FILE
&& sourcefcb
->type
!= BTRFS_TYPE_SYMLINK
) {
3290 ObDereferenceObject(sourcefo
);
3291 return STATUS_INVALID_PARAMETER
;
3294 sourcelen
= sourcefcb
->ads
? sourcefcb
->adsdata
.Length
: sourcefcb
->inode_item
.st_size
;
3296 if (sector_align(sourcelen
, Vcb
->superblock
.sector_size
) < (UINT64
)ded
->SourceFileOffset
.QuadPart
+ (UINT64
)ded
->ByteCount
.QuadPart
) {
3297 ObDereferenceObject(sourcefo
);
3298 return STATUS_NOT_SUPPORTED
;
3301 if (fcb
== sourcefcb
&&
3302 ((ded
->SourceFileOffset
.QuadPart
>= ded
->TargetFileOffset
.QuadPart
&& ded
->SourceFileOffset
.QuadPart
< ded
->TargetFileOffset
.QuadPart
+ ded
->ByteCount
.QuadPart
) ||
3303 (ded
->TargetFileOffset
.QuadPart
>= ded
->SourceFileOffset
.QuadPart
&& ded
->TargetFileOffset
.QuadPart
< ded
->SourceFileOffset
.QuadPart
+ ded
->ByteCount
.QuadPart
))) {
3304 WARN("source and destination are the same, and the ranges overlap\n");
3305 ObDereferenceObject(sourcefo
);
3306 return STATUS_INVALID_PARAMETER
;
3309 // fail if nocsum flag set on one file but not the other
3310 if (!fcb
->ads
&& !sourcefcb
->ads
&& (fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
) != (sourcefcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
)) {
3311 ObDereferenceObject(sourcefo
);
3312 return STATUS_INVALID_PARAMETER
;
3315 InitializeListHead(&rollback
);
3316 InitializeListHead(&newexts
);
3318 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, TRUE
);
3320 ExAcquireResourceExclusiveLite(fcb
->Header
.Resource
, TRUE
);
3322 if (fcb
!= sourcefcb
)
3323 ExAcquireResourceSharedLite(sourcefcb
->Header
.Resource
, TRUE
);
3325 if (!FsRtlFastCheckLockForWrite(&fcb
->lock
, &ded
->TargetFileOffset
, &ded
->ByteCount
, 0, FileObject
, PsGetCurrentProcess())) {
3326 Status
= STATUS_FILE_LOCK_CONFLICT
;
3330 if (!FsRtlFastCheckLockForRead(&sourcefcb
->lock
, &ded
->SourceFileOffset
, &ded
->ByteCount
, 0, FileObject
, PsGetCurrentProcess())) {
3331 Status
= STATUS_FILE_LOCK_CONFLICT
;
3335 make_inline
= fcb
->ads
? FALSE
: (fcb
->inode_item
.st_size
<= Vcb
->options
.max_inline
|| fcb_is_inline(fcb
));
3337 if (fcb
->ads
|| sourcefcb
->ads
|| make_inline
|| fcb_is_inline(sourcefcb
)) {
3339 ULONG bytes_read
, dataoff
, datalen2
;
3342 dataoff
= (ULONG
)ded
->TargetFileOffset
.QuadPart
;
3343 datalen2
= (ULONG
)fcb
->inode_item
.st_size
;
3344 } else if (fcb
->ads
) {
3346 datalen2
= (ULONG
)ded
->ByteCount
.QuadPart
;
3348 dataoff
= ded
->TargetFileOffset
.QuadPart
% Vcb
->superblock
.sector_size
;
3349 datalen2
= (ULONG
)sector_align(ded
->ByteCount
.QuadPart
+ dataoff
, Vcb
->superblock
.sector_size
);
3352 data2
= ExAllocatePoolWithTag(PagedPool
, datalen2
, ALLOC_TAG
);
3354 ERR("out of memory\n");
3355 Status
= STATUS_INSUFFICIENT_RESOURCES
;
3361 Status
= read_file(fcb
, data2
, 0, datalen2
, NULL
, Irp
);
3363 Status
= read_file(fcb
, data2
, ded
->TargetFileOffset
.QuadPart
- dataoff
, dataoff
, NULL
, Irp
);
3365 if (!NT_SUCCESS(Status
)) {
3366 ERR("read_file returned %08x\n", Status
);
3372 if (sourcefcb
->ads
) {
3373 Status
= read_stream(sourcefcb
, data2
+ dataoff
, ded
->SourceFileOffset
.QuadPart
, (ULONG
)ded
->ByteCount
.QuadPart
, &bytes_read
);
3374 if (!NT_SUCCESS(Status
)) {
3375 ERR("read_stream returned %08x\n", Status
);
3380 Status
= read_file(sourcefcb
, data2
+ dataoff
, ded
->SourceFileOffset
.QuadPart
, ded
->ByteCount
.QuadPart
, &bytes_read
, Irp
);
3381 if (!NT_SUCCESS(Status
)) {
3382 ERR("read_file returned %08x\n", Status
);
3388 if (dataoff
+ bytes_read
< datalen2
)
3389 RtlZeroMemory(data2
+ dataoff
+ bytes_read
, datalen2
- bytes_read
);
3392 RtlCopyMemory(&fcb
->adsdata
.Buffer
[ded
->TargetFileOffset
.QuadPart
], data2
, (USHORT
)min(ded
->ByteCount
.QuadPart
, fcb
->adsdata
.Length
- ded
->TargetFileOffset
.QuadPart
));
3393 else if (make_inline
) {
3397 Status
= excise_extents(Vcb
, fcb
, 0, sector_align(fcb
->inode_item
.st_size
, Vcb
->superblock
.sector_size
), Irp
, &rollback
);
3398 if (!NT_SUCCESS(Status
)) {
3399 ERR("excise_extents returned %08x\n", Status
);
3404 edsize
= (UINT16
)(offsetof(EXTENT_DATA
, data
[0]) + datalen2
);
3406 ed
= ExAllocatePoolWithTag(PagedPool
, edsize
, ALLOC_TAG
);
3408 ERR("out of memory\n");
3410 Status
= STATUS_INSUFFICIENT_RESOURCES
;
3414 ed
->generation
= Vcb
->superblock
.generation
;
3415 ed
->decoded_size
= fcb
->inode_item
.st_size
;
3416 ed
->compression
= BTRFS_COMPRESSION_NONE
;
3417 ed
->encryption
= BTRFS_ENCRYPTION_NONE
;
3418 ed
->encoding
= BTRFS_ENCODING_NONE
;
3419 ed
->type
= EXTENT_TYPE_INLINE
;
3421 RtlCopyMemory(ed
->data
, data2
, datalen2
);
3423 Status
= add_extent_to_fcb(fcb
, 0, ed
, edsize
, FALSE
, NULL
, &rollback
);
3424 if (!NT_SUCCESS(Status
)) {
3425 ERR("add_extent_to_fcb returned %08x\n", Status
);
3430 fcb
->inode_item
.st_blocks
+= datalen2
;
3432 UINT64 start
= ded
->TargetFileOffset
.QuadPart
- (ded
->TargetFileOffset
.QuadPart
% Vcb
->superblock
.sector_size
);
3434 Status
= do_write_file(fcb
, start
, start
+ datalen2
, data2
, Irp
, FALSE
, 0, &rollback
);
3435 if (!NT_SUCCESS(Status
)) {
3436 ERR("do_write_file returned %08x\n", Status
);
3444 LIST_ENTRY
* lastextle
;
3446 le
= sourcefcb
->extents
.Flink
;
3447 while (le
!= &sourcefcb
->extents
) {
3448 extent
* ext
= CONTAINING_RECORD(le
, extent
, list_entry
);
3451 if (ext
->offset
>= (UINT64
)ded
->SourceFileOffset
.QuadPart
+ (UINT64
)ded
->ByteCount
.QuadPart
)
3454 if (ext
->extent_data
.type
!= EXTENT_TYPE_INLINE
) {
3455 ULONG extlen
= offsetof(extent
, extent_data
) + sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
);
3457 EXTENT_DATA2
*ed2s
, *ed2d
;
3460 ed2s
= (EXTENT_DATA2
*)ext
->extent_data
.data
;
3462 if (ext
->offset
+ ed2s
->num_bytes
<= (UINT64
)ded
->SourceFileOffset
.QuadPart
) {
3467 ext2
= ExAllocatePoolWithTag(PagedPool
, extlen
, ALLOC_TAG
);
3469 ERR("out of memory\n");
3470 Status
= STATUS_INSUFFICIENT_RESOURCES
;
3474 if (ext
->offset
< (UINT64
)ded
->SourceFileOffset
.QuadPart
)
3475 ext2
->offset
= ded
->TargetFileOffset
.QuadPart
;
3477 ext2
->offset
= ext
->offset
- ded
->SourceFileOffset
.QuadPart
+ ded
->TargetFileOffset
.QuadPart
;
3479 ext2
->datalen
= sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
);
3480 ext2
->unique
= FALSE
;
3481 ext2
->ignore
= FALSE
;
3482 ext2
->inserted
= TRUE
;
3484 ext2
->extent_data
.generation
= Vcb
->superblock
.generation
;
3485 ext2
->extent_data
.decoded_size
= ext
->extent_data
.decoded_size
;
3486 ext2
->extent_data
.compression
= ext
->extent_data
.compression
;
3487 ext2
->extent_data
.encryption
= ext
->extent_data
.encryption
;
3488 ext2
->extent_data
.encoding
= ext
->extent_data
.encoding
;
3489 ext2
->extent_data
.type
= ext
->extent_data
.type
;
3491 ed2d
= (EXTENT_DATA2
*)ext2
->extent_data
.data
;
3493 ed2d
->address
= ed2s
->address
;
3494 ed2d
->size
= ed2s
->size
;
3496 if (ext
->offset
< (UINT64
)ded
->SourceFileOffset
.QuadPart
) {
3497 ed2d
->offset
= ed2s
->offset
+ ded
->SourceFileOffset
.QuadPart
- ext
->offset
;
3498 ed2d
->num_bytes
= min((UINT64
)ded
->ByteCount
.QuadPart
, ed2s
->num_bytes
+ ext
->offset
- ded
->SourceFileOffset
.QuadPart
);
3500 ed2d
->offset
= ed2s
->offset
;
3501 ed2d
->num_bytes
= min(ded
->SourceFileOffset
.QuadPart
+ ded
->ByteCount
.QuadPart
- ext
->offset
, ed2s
->num_bytes
);
3505 if (ext
->extent_data
.compression
== BTRFS_COMPRESSION_NONE
) {
3506 ext2
->csum
= ExAllocatePoolWithTag(PagedPool
, (ULONG
)(ed2d
->num_bytes
* sizeof(UINT32
) / Vcb
->superblock
.sector_size
), ALLOC_TAG
);
3508 ERR("out of memory\n");
3509 Status
= STATUS_INSUFFICIENT_RESOURCES
;
3514 RtlCopyMemory(ext2
->csum
, &ext
->csum
[(ed2d
->offset
- ed2s
->offset
) / Vcb
->superblock
.sector_size
],
3515 (ULONG
)(ed2d
->num_bytes
* sizeof(UINT32
) / Vcb
->superblock
.sector_size
));
3517 ext2
->csum
= ExAllocatePoolWithTag(PagedPool
, (ULONG
)(ed2d
->size
* sizeof(UINT32
) / Vcb
->superblock
.sector_size
), ALLOC_TAG
);
3519 ERR("out of memory\n");
3520 Status
= STATUS_INSUFFICIENT_RESOURCES
;
3525 RtlCopyMemory(ext2
->csum
, ext
->csum
, (ULONG
)(ed2s
->size
* sizeof(UINT32
) / Vcb
->superblock
.sector_size
));
3530 InsertTailList(&newexts
, &ext2
->list_entry
);
3532 c
= get_chunk_from_address(Vcb
, ed2s
->address
);
3534 ERR("get_chunk_from_address(%llx) failed\n", ed2s
->address
);
3535 Status
= STATUS_INTERNAL_ERROR
;
3539 Status
= update_changed_extent_ref(Vcb
, c
, ed2s
->address
, ed2s
->size
, fcb
->subvol
->id
, fcb
->inode
, ext2
->offset
- ed2d
->offset
,
3540 1, fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
, FALSE
, Irp
);
3541 if (!NT_SUCCESS(Status
)) {
3542 ERR("update_changed_extent_ref returned %08x\n", Status
);
3546 nbytes
+= ed2d
->num_bytes
;
3553 Status
= excise_extents(Vcb
, fcb
, ded
->TargetFileOffset
.QuadPart
, ded
->TargetFileOffset
.QuadPart
+ ded
->ByteCount
.QuadPart
, Irp
, &rollback
);
3554 if (!NT_SUCCESS(Status
)) {
3555 ERR("excise_extents returned %08x\n", Status
);
3557 while (!IsListEmpty(&newexts
)) {
3558 extent
* ext
= CONTAINING_RECORD(RemoveHeadList(&newexts
), extent
, list_entry
);
3565 // clear unique flags in source fcb
3566 le
= sourcefcb
->extents
.Flink
;
3567 while (le
!= &sourcefcb
->extents
) {
3568 extent
* ext
= CONTAINING_RECORD(le
, extent
, list_entry
);
3570 if (!ext
->ignore
&& ext
->unique
&& (ext
->extent_data
.type
== EXTENT_TYPE_REGULAR
|| ext
->extent_data
.type
== EXTENT_TYPE_PREALLOC
)) {
3571 EXTENT_DATA2
* ed2s
= (EXTENT_DATA2
*)ext
->extent_data
.data
;
3574 le2
= newexts
.Flink
;
3575 while (le2
!= &newexts
) {
3576 extent
* ext2
= CONTAINING_RECORD(le2
, extent
, list_entry
);
3578 if (ext2
->extent_data
.type
== EXTENT_TYPE_REGULAR
|| ext2
->extent_data
.type
== EXTENT_TYPE_PREALLOC
) {
3579 EXTENT_DATA2
* ed2d
= (EXTENT_DATA2
*)ext2
->extent_data
.data
;
3581 if (ed2d
->address
== ed2s
->address
&& ed2d
->size
== ed2s
->size
) {
3582 ext
->unique
= FALSE
;
3594 lastextle
= &fcb
->extents
;
3595 while (!IsListEmpty(&newexts
)) {
3596 extent
* ext
= CONTAINING_RECORD(RemoveHeadList(&newexts
), extent
, list_entry
);
3598 add_extent(fcb
, lastextle
, ext
);
3599 lastextle
= &ext
->list_entry
;
3603 KeQuerySystemTime(&time
);
3604 win_time_to_unix(time
, &now
);
3607 ccb
->fileref
->parent
->fcb
->inode_item
.sequence
++;
3609 if (!ccb
->user_set_change_time
)
3610 ccb
->fileref
->parent
->fcb
->inode_item
.st_ctime
= now
;
3612 ccb
->fileref
->parent
->fcb
->inode_item_changed
= TRUE
;
3613 mark_fcb_dirty(ccb
->fileref
->parent
->fcb
);
3615 fcb
->inode_item
.st_blocks
+= nbytes
;
3616 fcb
->inode_item
.sequence
++;
3618 if (!ccb
->user_set_change_time
)
3619 fcb
->inode_item
.st_ctime
= now
;
3621 if (!ccb
->user_set_write_time
) {
3622 fcb
->inode_item
.st_mtime
= now
;
3623 send_notification_fcb(ccb
->fileref
, FILE_NOTIFY_CHANGE_LAST_WRITE
, FILE_ACTION_MODIFIED
, NULL
);
3626 fcb
->inode_item_changed
= TRUE
;
3627 fcb
->extents_changed
= TRUE
;
3630 mark_fcb_dirty(fcb
);
3632 if (fcb
->nonpaged
->segment_object
.DataSectionObject
)
3633 CcPurgeCacheSection(&fcb
->nonpaged
->segment_object
, &ded
->TargetFileOffset
, (ULONG
)ded
->ByteCount
.QuadPart
, FALSE
);
3635 Status
= STATUS_SUCCESS
;
3638 ObDereferenceObject(sourcefo
);
3640 if (NT_SUCCESS(Status
))
3641 clear_rollback(&rollback
);
3643 do_rollback(Vcb
, &rollback
);
3645 if (fcb
!= sourcefcb
)
3646 ExReleaseResourceLite(sourcefcb
->Header
.Resource
);
3648 ExReleaseResourceLite(fcb
->Header
.Resource
);
3650 ExReleaseResourceLite(&Vcb
->tree_lock
);
3655 static NTSTATUS
mknod(device_extension
* Vcb
, PFILE_OBJECT FileObject
, void* data
, ULONG datalen
, PIRP Irp
) {
3660 file_ref
*parfileref
, *fileref
;
3661 UNICODE_STRING name
;
3670 SECURITY_SUBJECT_CONTEXT subjcont
;
3674 TRACE("(%p, %p, %p, %u)\n", Vcb
, FileObject
, data
, datalen
);
3676 if (!FileObject
|| !FileObject
->FsContext
|| !FileObject
->FsContext2
|| FileObject
->FsContext
== Vcb
->volume_fcb
)
3677 return STATUS_INVALID_PARAMETER
;
3680 return STATUS_MEDIA_WRITE_PROTECTED
;
3682 parfcb
= FileObject
->FsContext
;
3684 if (parfcb
->type
!= BTRFS_TYPE_DIRECTORY
) {
3685 WARN("trying to create file in something other than a directory\n");
3686 return STATUS_INVALID_PARAMETER
;
3689 if (is_subvol_readonly(parfcb
->subvol
, Irp
))
3690 return STATUS_ACCESS_DENIED
;
3692 parccb
= FileObject
->FsContext2
;
3693 parfileref
= parccb
->fileref
;
3696 return STATUS_INVALID_PARAMETER
;
3698 if (datalen
< sizeof(btrfs_mknod
))
3699 return STATUS_INVALID_PARAMETER
;
3701 bmn
= (btrfs_mknod
*)data
;
3703 if (datalen
< offsetof(btrfs_mknod
, name
[0]) + bmn
->namelen
|| bmn
->namelen
< sizeof(WCHAR
))
3704 return STATUS_INVALID_PARAMETER
;
3706 if (bmn
->type
== BTRFS_TYPE_UNKNOWN
|| bmn
->type
> BTRFS_TYPE_SYMLINK
)
3707 return STATUS_INVALID_PARAMETER
;
3709 if ((bmn
->type
== BTRFS_TYPE_DIRECTORY
&& !(parccb
->access
& FILE_ADD_SUBDIRECTORY
)) ||
3710 (bmn
->type
!= BTRFS_TYPE_DIRECTORY
&& !(parccb
->access
& FILE_ADD_FILE
))) {
3711 WARN("insufficient privileges\n");
3712 return STATUS_ACCESS_DENIED
;
3715 if (bmn
->inode
!= 0) {
3716 if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE
), Irp
->RequestorMode
))
3717 return STATUS_PRIVILEGE_NOT_HELD
;
3720 for (i
= 0; i
< bmn
->namelen
/ sizeof(WCHAR
); i
++) {
3721 if (bmn
->name
[i
] == 0 || bmn
->name
[i
] == '/')
3722 return STATUS_OBJECT_NAME_INVALID
;
3725 // don't allow files called . or ..
3726 if (bmn
->name
[0] == '.' && (bmn
->namelen
== sizeof(WCHAR
) || (bmn
->namelen
== 2 * sizeof(WCHAR
) && bmn
->name
[1] == '.')))
3727 return STATUS_OBJECT_NAME_INVALID
;
3729 Status
= RtlUnicodeToUTF8N(NULL
, 0, &len
, bmn
->name
, bmn
->namelen
);
3730 if (!NT_SUCCESS(Status
)) {
3731 ERR("RtlUnicodeToUTF8N return %08x\n", Status
);
3736 ERR("RtlUnicodeToUTF8N returned a length of 0\n");
3737 return STATUS_INTERNAL_ERROR
;
3741 ERR("len was too long (%x)\n", len
);
3742 return STATUS_INVALID_PARAMETER
;
3745 utf8
.MaximumLength
= utf8
.Length
= (USHORT
)len
;
3746 utf8
.Buffer
= ExAllocatePoolWithTag(PagedPool
, utf8
.MaximumLength
, ALLOC_TAG
);
3749 ERR("out of memory\n");
3750 return STATUS_INSUFFICIENT_RESOURCES
;
3753 Status
= RtlUnicodeToUTF8N(utf8
.Buffer
, len
, &len
, bmn
->name
, bmn
->namelen
);
3754 if (!NT_SUCCESS(Status
)) {
3755 ERR("RtlUnicodeToUTF8N failed with error %08x\n", Status
);
3756 ExFreePool(utf8
.Buffer
);
3760 name
.Length
= name
.MaximumLength
= bmn
->namelen
;
3761 name
.Buffer
= bmn
->name
;
3763 Status
= find_file_in_dir(&name
, parfcb
, &subvol
, &inode
, &dc
, TRUE
);
3764 if (!NT_SUCCESS(Status
) && Status
!= STATUS_OBJECT_NAME_NOT_FOUND
) {
3765 ERR("find_file_in_dir returned %08x\n", Status
);
3769 if (NT_SUCCESS(Status
)) {
3770 WARN("filename already exists\n");
3771 Status
= STATUS_OBJECT_NAME_COLLISION
;
3775 KeQuerySystemTime(&time
);
3776 win_time_to_unix(time
, &now
);
3778 fcb
= create_fcb(Vcb
, PagedPool
);
3780 ERR("out of memory\n");
3781 Status
= STATUS_INSUFFICIENT_RESOURCES
;
3787 fcb
->inode_item
.generation
= Vcb
->superblock
.generation
;
3788 fcb
->inode_item
.transid
= Vcb
->superblock
.generation
;
3789 fcb
->inode_item
.st_size
= 0;
3790 fcb
->inode_item
.st_blocks
= 0;
3791 fcb
->inode_item
.block_group
= 0;
3792 fcb
->inode_item
.st_nlink
= 1;
3793 fcb
->inode_item
.st_uid
= UID_NOBODY
;
3794 fcb
->inode_item
.st_gid
= GID_NOBODY
;
3795 fcb
->inode_item
.st_mode
= inherit_mode(parfcb
, bmn
->type
== BTRFS_TYPE_DIRECTORY
);
3797 if (bmn
->type
== BTRFS_TYPE_BLOCKDEV
|| bmn
->type
== BTRFS_TYPE_CHARDEV
)
3798 fcb
->inode_item
.st_rdev
= (minor(bmn
->st_rdev
) & 0xFFFFF) | ((major(bmn
->st_rdev
) & 0xFFFFFFFFFFF) << 20);
3800 fcb
->inode_item
.st_rdev
= 0;
3802 fcb
->inode_item
.flags
= 0;
3803 fcb
->inode_item
.sequence
= 1;
3804 fcb
->inode_item
.st_atime
= now
;
3805 fcb
->inode_item
.st_ctime
= now
;
3806 fcb
->inode_item
.st_mtime
= now
;
3807 fcb
->inode_item
.otime
= now
;
3809 if (bmn
->type
== BTRFS_TYPE_DIRECTORY
)
3810 fcb
->inode_item
.st_mode
|= __S_IFDIR
;
3811 else if (bmn
->type
== BTRFS_TYPE_CHARDEV
)
3812 fcb
->inode_item
.st_mode
|= __S_IFCHR
;
3813 else if (bmn
->type
== BTRFS_TYPE_BLOCKDEV
)
3814 fcb
->inode_item
.st_mode
|= __S_IFBLK
;
3815 else if (bmn
->type
== BTRFS_TYPE_FIFO
)
3816 fcb
->inode_item
.st_mode
|= __S_IFIFO
;
3817 else if (bmn
->type
== BTRFS_TYPE_SOCKET
)
3818 fcb
->inode_item
.st_mode
|= __S_IFSOCK
;
3819 else if (bmn
->type
== BTRFS_TYPE_SYMLINK
)
3820 fcb
->inode_item
.st_mode
|= __S_IFLNK
;
3822 fcb
->inode_item
.st_mode
|= __S_IFREG
;
3824 if (bmn
->type
!= BTRFS_TYPE_DIRECTORY
)
3825 fcb
->inode_item
.st_mode
&= ~(S_IXUSR
| S_IXGRP
| S_IXOTH
); // remove executable bit if not directory
3827 // inherit nodatacow flag from parent directory
3828 if (parfcb
->inode_item
.flags
& BTRFS_INODE_NODATACOW
) {
3829 fcb
->inode_item
.flags
|= BTRFS_INODE_NODATACOW
;
3831 if (bmn
->type
!= BTRFS_TYPE_DIRECTORY
)
3832 fcb
->inode_item
.flags
|= BTRFS_INODE_NODATASUM
;
3835 if (parfcb
->inode_item
.flags
& BTRFS_INODE_COMPRESS
)
3836 fcb
->inode_item
.flags
|= BTRFS_INODE_COMPRESS
;
3838 fcb
->prop_compression
= parfcb
->prop_compression
;
3839 fcb
->prop_compression_changed
= fcb
->prop_compression
!= PropCompression_None
;
3841 fcb
->inode_item_changed
= TRUE
;
3843 fcb
->Header
.IsFastIoPossible
= fast_io_possible(fcb
);
3844 fcb
->Header
.AllocationSize
.QuadPart
= 0;
3845 fcb
->Header
.FileSize
.QuadPart
= 0;
3846 fcb
->Header
.ValidDataLength
.QuadPart
= 0;
3850 if (bmn
->name
[0] == '.')
3851 fcb
->atts
|= FILE_ATTRIBUTE_HIDDEN
;
3853 if (bmn
->type
== BTRFS_TYPE_DIRECTORY
)
3854 fcb
->atts
|= FILE_ATTRIBUTE_DIRECTORY
;
3856 fcb
->atts_changed
= FALSE
;
3858 InterlockedIncrement(&parfcb
->refcount
);
3859 fcb
->subvol
= parfcb
->subvol
;
3861 SeCaptureSubjectContext(&subjcont
);
3863 Status
= SeAssignSecurityEx(parfileref
? parfileref
->fcb
->sd
: NULL
, NULL
, (void**)&fcb
->sd
, NULL
, fcb
->type
== BTRFS_TYPE_DIRECTORY
,
3864 SEF_SACL_AUTO_INHERIT
, &subjcont
, IoGetFileObjectGenericMapping(), PagedPool
);
3866 if (!NT_SUCCESS(Status
)) {
3867 ERR("SeAssignSecurityEx returned %08x\n", Status
);
3872 Status
= RtlGetOwnerSecurityDescriptor(fcb
->sd
, &owner
, &defaulted
);
3873 if (!NT_SUCCESS(Status
)) {
3874 WARN("RtlGetOwnerSecurityDescriptor returned %08x\n", Status
);
3875 fcb
->sd_dirty
= TRUE
;
3877 fcb
->inode_item
.st_uid
= sid_to_uid(owner
);
3878 fcb
->sd_dirty
= fcb
->inode_item
.st_uid
== UID_NOBODY
;
3881 find_gid(fcb
, parfcb
, &subjcont
);
3883 ExAcquireResourceExclusiveLite(&Vcb
->fileref_lock
, TRUE
);
3884 acquire_fcb_lock_exclusive(Vcb
);
3886 if (bmn
->inode
== 0) {
3887 inode
= InterlockedIncrement64(&parfcb
->subvol
->lastinode
);
3888 lastle
= parfcb
->subvol
->fcbs
.Blink
;
3890 if (bmn
->inode
> (UINT64
)parfcb
->subvol
->lastinode
) {
3891 inode
= parfcb
->subvol
->lastinode
= bmn
->inode
;
3892 lastle
= parfcb
->subvol
->fcbs
.Blink
;
3894 LIST_ENTRY
* le
= parfcb
->subvol
->fcbs
.Flink
;
3896 lastle
= parfcb
->subvol
->fcbs
.Blink
;;
3897 while (le
!= &parfcb
->subvol
->fcbs
) {
3898 struct _fcb
* fcb2
= CONTAINING_RECORD(le
, struct _fcb
, list_entry
);
3900 if (fcb2
->inode
== bmn
->inode
&& !fcb2
->deleted
) {
3901 release_fcb_lock(Vcb
);
3902 ExReleaseResourceLite(&Vcb
->fileref_lock
);
3904 WARN("inode collision\n");
3905 Status
= STATUS_INVALID_PARAMETER
;
3907 } else if (fcb2
->inode
> bmn
->inode
) {
3908 lastle
= fcb2
->list_entry
.Blink
;
3920 fcb
->type
= bmn
->type
;
3922 fileref
= create_fileref(Vcb
);
3924 release_fcb_lock(Vcb
);
3925 ExReleaseResourceLite(&Vcb
->fileref_lock
);
3927 ERR("out of memory\n");
3929 Status
= STATUS_INSUFFICIENT_RESOURCES
;
3935 fcb
->created
= TRUE
;
3936 fileref
->created
= TRUE
;
3938 fcb
->subvol
->root_item
.ctransid
= Vcb
->superblock
.generation
;
3939 fcb
->subvol
->root_item
.ctime
= now
;
3941 fileref
->parent
= parfileref
;
3943 mark_fcb_dirty(fcb
);
3944 mark_fileref_dirty(fileref
);
3946 Status
= add_dir_child(fileref
->parent
->fcb
, fcb
->inode
, FALSE
, &utf8
, &name
, fcb
->type
, &dc
);
3947 if (!NT_SUCCESS(Status
))
3948 WARN("add_dir_child returned %08x\n", Status
);
3951 dc
->fileref
= fileref
;
3953 ExAcquireResourceExclusiveLite(&parfileref
->fcb
->nonpaged
->dir_children_lock
, TRUE
);
3954 InsertTailList(&parfileref
->children
, &fileref
->list_entry
);
3955 ExReleaseResourceLite(&parfileref
->fcb
->nonpaged
->dir_children_lock
);
3957 increase_fileref_refcount(parfileref
);
3959 if (fcb
->type
== BTRFS_TYPE_DIRECTORY
) {
3960 fcb
->hash_ptrs
= ExAllocatePoolWithTag(PagedPool
, sizeof(LIST_ENTRY
*) * 256, ALLOC_TAG
);
3961 if (!fcb
->hash_ptrs
) {
3962 release_fcb_lock(Vcb
);
3963 ExReleaseResourceLite(&Vcb
->fileref_lock
);
3965 ERR("out of memory\n");
3966 free_fileref(fileref
);
3967 Status
= STATUS_INSUFFICIENT_RESOURCES
;
3971 RtlZeroMemory(fcb
->hash_ptrs
, sizeof(LIST_ENTRY
*) * 256);
3973 fcb
->hash_ptrs_uc
= ExAllocatePoolWithTag(PagedPool
, sizeof(LIST_ENTRY
*) * 256, ALLOC_TAG
);
3974 if (!fcb
->hash_ptrs_uc
) {
3975 release_fcb_lock(Vcb
);
3976 ExReleaseResourceLite(&Vcb
->fileref_lock
);
3978 ERR("out of memory\n");
3979 free_fileref(fileref
);
3980 Status
= STATUS_INSUFFICIENT_RESOURCES
;
3984 RtlZeroMemory(fcb
->hash_ptrs_uc
, sizeof(LIST_ENTRY
*) * 256);
3987 InsertHeadList(lastle
, &fcb
->list_entry
);
3988 InsertTailList(&Vcb
->all_fcbs
, &fcb
->list_entry_all
);
3990 if (bmn
->type
== BTRFS_TYPE_DIRECTORY
)
3991 fileref
->fcb
->fileref
= fileref
;
3993 ExAcquireResourceExclusiveLite(parfcb
->Header
.Resource
, TRUE
);
3994 parfcb
->inode_item
.st_size
+= utf8
.Length
* 2;
3995 parfcb
->inode_item
.transid
= Vcb
->superblock
.generation
;
3996 parfcb
->inode_item
.sequence
++;
3998 if (!parccb
->user_set_change_time
)
3999 parfcb
->inode_item
.st_ctime
= now
;
4001 if (!parccb
->user_set_write_time
)
4002 parfcb
->inode_item
.st_mtime
= now
;
4004 parfcb
->subvol
->fcbs_version
++;
4006 ExReleaseResourceLite(parfcb
->Header
.Resource
);
4007 release_fcb_lock(Vcb
);
4008 ExReleaseResourceLite(&Vcb
->fileref_lock
);
4010 parfcb
->inode_item_changed
= TRUE
;
4011 mark_fcb_dirty(parfcb
);
4013 send_notification_fileref(fileref
, bmn
->type
== BTRFS_TYPE_DIRECTORY
? FILE_NOTIFY_CHANGE_DIR_NAME
: FILE_NOTIFY_CHANGE_FILE_NAME
, FILE_ACTION_ADDED
, NULL
);
4015 if (!parccb
->user_set_write_time
)
4016 send_notification_fcb(parfileref
, FILE_NOTIFY_CHANGE_LAST_WRITE
, FILE_ACTION_MODIFIED
, NULL
);
4018 Status
= STATUS_SUCCESS
;
4022 ExFreePool(utf8
.Buffer
);
4027 static void mark_subvol_dirty(device_extension
* Vcb
, root
* r
) {
4031 ExAcquireResourceExclusiveLite(&Vcb
->dirty_subvols_lock
, TRUE
);
4032 InsertTailList(&Vcb
->dirty_subvols
, &r
->list_entry_dirty
);
4033 ExReleaseResourceLite(&Vcb
->dirty_subvols_lock
);
4036 Vcb
->need_write
= TRUE
;
4039 static NTSTATUS
recvd_subvol(device_extension
* Vcb
, PFILE_OBJECT FileObject
, void* data
, ULONG datalen
, KPROCESSOR_MODE processor_mode
) {
4040 btrfs_received_subvol
* brs
= (btrfs_received_subvol
*)data
;
4046 TRACE("(%p, %p, %p, %u)\n", Vcb
, FileObject
, data
, datalen
);
4048 if (!data
|| datalen
< sizeof(btrfs_received_subvol
))
4049 return STATUS_INVALID_PARAMETER
;
4051 if (!FileObject
|| !FileObject
->FsContext
|| FileObject
->FsContext
== Vcb
->volume_fcb
)
4052 return STATUS_INVALID_PARAMETER
;
4054 fcb
= FileObject
->FsContext
;
4057 return STATUS_INVALID_PARAMETER
;
4059 if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE
), processor_mode
))
4060 return STATUS_PRIVILEGE_NOT_HELD
;
4062 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, TRUE
);
4064 if (fcb
->subvol
->root_item
.rtransid
!= 0) {
4065 WARN("subvol already has received information set\n");
4066 Status
= STATUS_INVALID_PARAMETER
;
4070 KeQuerySystemTime(&time
);
4071 win_time_to_unix(time
, &now
);
4073 RtlCopyMemory(&fcb
->subvol
->root_item
.received_uuid
, &brs
->uuid
, sizeof(BTRFS_UUID
));
4074 fcb
->subvol
->root_item
.stransid
= brs
->generation
;
4075 fcb
->subvol
->root_item
.rtransid
= Vcb
->superblock
.generation
;
4076 fcb
->subvol
->root_item
.rtime
= now
;
4078 fcb
->subvol
->received
= TRUE
;
4079 mark_subvol_dirty(Vcb
, fcb
->subvol
);
4081 Status
= STATUS_SUCCESS
;
4084 ExReleaseResourceLite(&Vcb
->tree_lock
);
4089 static NTSTATUS
fsctl_get_xattrs(device_extension
* Vcb
, PFILE_OBJECT FileObject
, void* data
, ULONG datalen
, KPROCESSOR_MODE processor_mode
) {
4091 btrfs_set_xattr
* bsxa
;
4092 ULONG reqlen
= (ULONG
)offsetof(btrfs_set_xattr
, data
[0]);
4096 if (!data
|| datalen
< reqlen
)
4097 return STATUS_INVALID_PARAMETER
;
4099 if (!FileObject
|| !FileObject
->FsContext
|| !FileObject
->FsContext2
|| FileObject
->FsContext
== Vcb
->volume_fcb
)
4100 return STATUS_INVALID_PARAMETER
;
4102 fcb
= FileObject
->FsContext
;
4103 ccb
= FileObject
->FsContext2
;
4105 if (!(ccb
->access
& (FILE_READ_ATTRIBUTES
| FILE_WRITE_ATTRIBUTES
)) && processor_mode
== UserMode
) {
4106 WARN("insufficient privileges\n");
4107 return STATUS_ACCESS_DENIED
;
4110 ExAcquireResourceSharedLite(fcb
->Header
.Resource
, TRUE
);
4112 le
= fcb
->xattrs
.Flink
;
4113 while (le
!= &fcb
->xattrs
) {
4114 xattr
* xa
= CONTAINING_RECORD(le
, xattr
, list_entry
);
4116 if (xa
->valuelen
> 0)
4117 reqlen
+= (ULONG
)offsetof(btrfs_set_xattr
, data
[0]) + xa
->namelen
+ xa
->valuelen
;
4122 if (datalen
< reqlen
) {
4123 ExReleaseResourceLite(fcb
->Header
.Resource
);
4124 return STATUS_BUFFER_OVERFLOW
;
4127 bsxa
= (btrfs_set_xattr
*)data
;
4130 le
= fcb
->xattrs
.Flink
;
4131 while (le
!= &fcb
->xattrs
) {
4132 xattr
* xa
= CONTAINING_RECORD(le
, xattr
, list_entry
);
4134 if (xa
->valuelen
> 0) {
4135 bsxa
->namelen
= xa
->namelen
;
4136 bsxa
->valuelen
= xa
->valuelen
;
4137 memcpy(bsxa
->data
, xa
->data
, xa
->namelen
+ xa
->valuelen
);
4139 bsxa
= (btrfs_set_xattr
*)&bsxa
->data
[xa
->namelen
+ xa
->valuelen
];
4149 ExReleaseResourceLite(fcb
->Header
.Resource
);
4151 return STATUS_SUCCESS
;
4154 static NTSTATUS
fsctl_set_xattr(device_extension
* Vcb
, PFILE_OBJECT FileObject
, void* data
, ULONG datalen
, PIRP Irp
) {
4156 btrfs_set_xattr
* bsxa
;
4162 static const char stream_pref
[] = "user.";
4164 TRACE("(%p, %p, %p, %u)\n", Vcb
, FileObject
, data
, datalen
);
4166 if (!data
|| datalen
< sizeof(btrfs_set_xattr
))
4167 return STATUS_INVALID_PARAMETER
;
4169 bsxa
= (btrfs_set_xattr
*)data
;
4171 if (datalen
< offsetof(btrfs_set_xattr
, data
[0]) + bsxa
->namelen
+ bsxa
->valuelen
)
4172 return STATUS_INVALID_PARAMETER
;
4174 if (bsxa
->namelen
+ bsxa
->valuelen
+ sizeof(tree_header
) + sizeof(leaf_node
) + offsetof(DIR_ITEM
, name
[0]) > Vcb
->superblock
.node_size
)
4175 return STATUS_INVALID_PARAMETER
;
4177 if (!FileObject
|| !FileObject
->FsContext
|| !FileObject
->FsContext2
|| FileObject
->FsContext
== Vcb
->volume_fcb
)
4178 return STATUS_INVALID_PARAMETER
;
4181 return STATUS_MEDIA_WRITE_PROTECTED
;
4183 fcb
= FileObject
->FsContext
;
4184 ccb
= FileObject
->FsContext2
;
4186 if (is_subvol_readonly(fcb
->subvol
, Irp
))
4187 return STATUS_ACCESS_DENIED
;
4189 if (!(ccb
->access
& FILE_WRITE_ATTRIBUTES
) && Irp
->RequestorMode
== UserMode
) {
4190 WARN("insufficient privileges\n");
4191 return STATUS_ACCESS_DENIED
;
4194 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, TRUE
);
4196 ExAcquireResourceExclusiveLite(fcb
->Header
.Resource
, TRUE
);
4198 if (bsxa
->namelen
== sizeof(EA_NTACL
) - 1 && RtlCompareMemory(bsxa
->data
, EA_NTACL
, sizeof(EA_NTACL
) - 1) == sizeof(EA_NTACL
) - 1) {
4199 if ((!(ccb
->access
& WRITE_DAC
) || !(ccb
->access
& WRITE_OWNER
)) && Irp
->RequestorMode
== UserMode
) {
4200 WARN("insufficient privileges\n");
4201 Status
= STATUS_ACCESS_DENIED
;
4205 if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE
), Irp
->RequestorMode
)) {
4206 Status
= STATUS_PRIVILEGE_NOT_HELD
;
4211 ExFreePool(fcb
->sd
);
4213 if (bsxa
->valuelen
> 0 && RtlValidRelativeSecurityDescriptor(bsxa
->data
+ bsxa
->namelen
, bsxa
->valuelen
, 0)) {
4214 fcb
->sd
= ExAllocatePoolWithTag(PagedPool
, bsxa
->valuelen
, ALLOC_TAG
);
4216 ERR("out of memory\n");
4217 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4221 RtlCopyMemory(fcb
->sd
, bsxa
->data
+ bsxa
->namelen
, bsxa
->valuelen
);
4225 fcb
->sd_dirty
= TRUE
;
4228 fcb_get_sd(fcb
, ccb
->fileref
->parent
->fcb
, FALSE
, Irp
);
4229 fcb
->sd_deleted
= TRUE
;
4232 mark_fcb_dirty(fcb
);
4234 Status
= STATUS_SUCCESS
;
4236 } else if (bsxa
->namelen
== sizeof(EA_DOSATTRIB
) - 1 && RtlCompareMemory(bsxa
->data
, EA_DOSATTRIB
, sizeof(EA_DOSATTRIB
) - 1) == sizeof(EA_DOSATTRIB
) - 1) {
4239 if (bsxa
->valuelen
> 0 && get_file_attributes_from_xattr(bsxa
->data
+ bsxa
->namelen
, bsxa
->valuelen
, &atts
)) {
4242 if (fcb
->type
== BTRFS_TYPE_DIRECTORY
)
4243 fcb
->atts
|= FILE_ATTRIBUTE_DIRECTORY
;
4244 else if (fcb
->type
== BTRFS_TYPE_SYMLINK
)
4245 fcb
->atts
|= FILE_ATTRIBUTE_REPARSE_POINT
;
4247 if (fcb
->inode
== SUBVOL_ROOT_INODE
) {
4248 if (fcb
->subvol
->root_item
.flags
& BTRFS_SUBVOL_READONLY
)
4249 fcb
->atts
|= FILE_ATTRIBUTE_READONLY
;
4251 fcb
->atts
&= ~FILE_ATTRIBUTE_READONLY
;
4254 fcb
->atts_deleted
= FALSE
;
4256 BOOL hidden
= ccb
->fileref
&& ccb
->fileref
->dc
&& ccb
->fileref
->dc
->utf8
.Buffer
&& ccb
->fileref
->dc
->utf8
.Buffer
[0] == '.';
4258 fcb
->atts
= get_file_attributes(Vcb
, fcb
->subvol
, fcb
->inode
, fcb
->type
, hidden
, TRUE
, Irp
);
4259 fcb
->atts_deleted
= TRUE
;
4262 fcb
->atts_changed
= TRUE
;
4263 mark_fcb_dirty(fcb
);
4265 Status
= STATUS_SUCCESS
;
4267 } else if (bsxa
->namelen
== sizeof(EA_REPARSE
) - 1 && RtlCompareMemory(bsxa
->data
, EA_REPARSE
, sizeof(EA_REPARSE
) - 1) == sizeof(EA_REPARSE
) - 1) {
4268 if (fcb
->reparse_xattr
.Buffer
) {
4269 ExFreePool(fcb
->reparse_xattr
.Buffer
);
4270 fcb
->reparse_xattr
.Buffer
= NULL
;
4271 fcb
->reparse_xattr
.Length
= fcb
->reparse_xattr
.MaximumLength
= 0;
4274 if (bsxa
->valuelen
> 0) {
4275 fcb
->reparse_xattr
.Buffer
= ExAllocatePoolWithTag(PagedPool
, bsxa
->valuelen
, ALLOC_TAG
);
4276 if (!fcb
->reparse_xattr
.Buffer
) {
4277 ERR("out of memory\n");
4278 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4282 RtlCopyMemory(fcb
->reparse_xattr
.Buffer
, bsxa
->data
+ bsxa
->namelen
, bsxa
->valuelen
);
4283 fcb
->reparse_xattr
.Length
= fcb
->reparse_xattr
.MaximumLength
= bsxa
->valuelen
;
4286 fcb
->reparse_xattr_changed
= TRUE
;
4287 mark_fcb_dirty(fcb
);
4289 Status
= STATUS_SUCCESS
;
4291 } else if (bsxa
->namelen
== sizeof(EA_EA
) - 1 && RtlCompareMemory(bsxa
->data
, EA_EA
, sizeof(EA_EA
) - 1) == sizeof(EA_EA
) - 1) {
4292 if (!(ccb
->access
& FILE_WRITE_EA
) && Irp
->RequestorMode
== UserMode
) {
4293 WARN("insufficient privileges\n");
4294 Status
= STATUS_ACCESS_DENIED
;
4298 if (fcb
->ea_xattr
.Buffer
) {
4299 ExFreePool(fcb
->ea_xattr
.Buffer
);
4300 fcb
->ea_xattr
.Length
= fcb
->ea_xattr
.MaximumLength
= 0;
4301 fcb
->ea_xattr
.Buffer
= NULL
;
4306 if (bsxa
->valuelen
> 0) {
4309 Status
= IoCheckEaBufferValidity((FILE_FULL_EA_INFORMATION
*)(bsxa
->data
+ bsxa
->namelen
), bsxa
->valuelen
, &offset
);
4311 if (!NT_SUCCESS(Status
))
4312 WARN("IoCheckEaBufferValidity returned %08x (error at offset %u)\n", Status
, offset
);
4314 FILE_FULL_EA_INFORMATION
* eainfo
;
4316 fcb
->ea_xattr
.Buffer
= ExAllocatePoolWithTag(PagedPool
, bsxa
->valuelen
, ALLOC_TAG
);
4317 if (!fcb
->ea_xattr
.Buffer
) {
4318 ERR("out of memory\n");
4319 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4323 RtlCopyMemory(fcb
->ea_xattr
.Buffer
, bsxa
->data
+ bsxa
->namelen
, bsxa
->valuelen
);
4325 fcb
->ea_xattr
.Length
= fcb
->ea_xattr
.MaximumLength
= bsxa
->valuelen
;
4330 eainfo
= (FILE_FULL_EA_INFORMATION
*)(bsxa
->data
+ bsxa
->namelen
);
4332 fcb
->ealen
+= 5 + eainfo
->EaNameLength
+ eainfo
->EaValueLength
;
4334 if (eainfo
->NextEntryOffset
== 0)
4337 eainfo
= (FILE_FULL_EA_INFORMATION
*)(((UINT8
*)eainfo
) + eainfo
->NextEntryOffset
);
4342 fcb
->ea_changed
= TRUE
;
4343 mark_fcb_dirty(fcb
);
4345 Status
= STATUS_SUCCESS
;
4347 } 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) {
4348 static const char lzo
[] = "lzo";
4349 static const char zlib
[] = "zlib";
4350 static const char zstd
[] = "zstd";
4352 if (bsxa
->valuelen
== sizeof(zstd
) - 1 && RtlCompareMemory(bsxa
->data
+ bsxa
->namelen
, zstd
, bsxa
->valuelen
) == bsxa
->valuelen
)
4353 fcb
->prop_compression
= PropCompression_ZSTD
;
4354 else if (bsxa
->valuelen
== sizeof(lzo
) - 1 && RtlCompareMemory(bsxa
->data
+ bsxa
->namelen
, lzo
, bsxa
->valuelen
) == bsxa
->valuelen
)
4355 fcb
->prop_compression
= PropCompression_LZO
;
4356 else if (bsxa
->valuelen
== sizeof(zlib
) - 1 && RtlCompareMemory(bsxa
->data
+ bsxa
->namelen
, zlib
, bsxa
->valuelen
) == bsxa
->valuelen
)
4357 fcb
->prop_compression
= PropCompression_Zlib
;
4359 fcb
->prop_compression
= PropCompression_None
;
4361 if (fcb
->prop_compression
!= PropCompression_None
) {
4362 fcb
->inode_item
.flags
|= BTRFS_INODE_COMPRESS
;
4363 fcb
->inode_item_changed
= TRUE
;
4366 fcb
->prop_compression_changed
= TRUE
;
4367 mark_fcb_dirty(fcb
);
4369 Status
= STATUS_SUCCESS
;
4371 } else if (bsxa
->namelen
>= (sizeof(stream_pref
) - 1) && RtlCompareMemory(bsxa
->data
, stream_pref
, sizeof(stream_pref
) - 1) == sizeof(stream_pref
) - 1) {
4372 // don't allow xattrs beginning with user., as these appear as streams instead
4373 Status
= STATUS_OBJECT_NAME_INVALID
;
4377 xa
= ExAllocatePoolWithTag(PagedPool
, offsetof(xattr
, data
[0]) + bsxa
->namelen
+ bsxa
->valuelen
, ALLOC_TAG
);
4379 ERR("out of memory\n");
4380 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4384 le
= fcb
->xattrs
.Flink
;
4385 while (le
!= &fcb
->xattrs
) {
4386 xattr
* xa2
= CONTAINING_RECORD(le
, xattr
, list_entry
);
4388 if (xa2
->namelen
== bsxa
->namelen
&& RtlCompareMemory(xa2
->data
, bsxa
->data
, xa2
->namelen
) == xa2
->namelen
) {
4389 RemoveEntryList(&xa2
->list_entry
);
4397 xa
->namelen
= bsxa
->namelen
;
4398 xa
->valuelen
= bsxa
->valuelen
;
4400 RtlCopyMemory(xa
->data
, bsxa
->data
, bsxa
->namelen
+ bsxa
->valuelen
);
4402 InsertTailList(&fcb
->xattrs
, &xa
->list_entry
);
4404 fcb
->xattrs_changed
= TRUE
;
4405 mark_fcb_dirty(fcb
);
4407 Status
= STATUS_SUCCESS
;
4410 ExReleaseResourceLite(fcb
->Header
.Resource
);
4412 ExReleaseResourceLite(&Vcb
->tree_lock
);
4417 static NTSTATUS
reserve_subvol(device_extension
* Vcb
, PFILE_OBJECT FileObject
, PIRP Irp
) {
4421 TRACE("(%p, %p)\n", Vcb
, FileObject
);
4423 // "Reserving" a readonly subvol allows the calling process to write into it until the handle is closed.
4425 if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE
), Irp
->RequestorMode
))
4426 return STATUS_PRIVILEGE_NOT_HELD
;
4428 if (!FileObject
|| !FileObject
->FsContext
|| !FileObject
->FsContext2
|| FileObject
->FsContext
== Vcb
->volume_fcb
)
4429 return STATUS_INVALID_PARAMETER
;
4431 fcb
= FileObject
->FsContext
;
4432 ccb
= FileObject
->FsContext2
;
4434 if (!(fcb
->subvol
->root_item
.flags
& BTRFS_SUBVOL_READONLY
))
4435 return STATUS_INVALID_PARAMETER
;
4437 if (fcb
->subvol
->reserved
)
4438 return STATUS_INVALID_PARAMETER
;
4440 fcb
->subvol
->reserved
= PsGetCurrentProcess();
4441 ccb
->reserving
= TRUE
;
4443 return STATUS_SUCCESS
;
4446 static NTSTATUS
get_subvol_path(device_extension
* Vcb
, UINT64 id
, WCHAR
* out
, ULONG outlen
, PIRP Irp
) {
4453 le
= Vcb
->roots
.Flink
;
4454 while (le
!= &Vcb
->roots
) {
4455 root
* r2
= CONTAINING_RECORD(le
, root
, list_entry
);
4466 ERR("couldn't find subvol %llx\n", id
);
4467 return STATUS_INTERNAL_ERROR
;
4470 ExAcquireResourceExclusiveLite(&Vcb
->fileref_lock
, TRUE
);
4472 Status
= open_fileref_by_inode(Vcb
, r
, r
->root_item
.objid
, &fr
, Irp
);
4473 if (!NT_SUCCESS(Status
)) {
4474 ExReleaseResourceLite(&Vcb
->fileref_lock
);
4475 ERR("open_fileref_by_inode returned %08x\n", Status
);
4481 us
.MaximumLength
= (USHORT
)min(0xffff, outlen
) - sizeof(WCHAR
);
4483 Status
= fileref_get_filename(fr
, &us
, NULL
, NULL
);
4485 if (NT_SUCCESS(Status
) || Status
== STATUS_BUFFER_OVERFLOW
)
4486 out
[us
.Length
/ sizeof(WCHAR
)] = 0;
4488 ERR("fileref_get_filename returned %08x\n", Status
);
4492 ExReleaseResourceLite(&Vcb
->fileref_lock
);
4497 static NTSTATUS
find_subvol(device_extension
* Vcb
, void* in
, ULONG inlen
, void* out
, ULONG outlen
, PIRP Irp
) {
4498 btrfs_find_subvol
* bfs
;
4503 if (!in
|| inlen
< sizeof(btrfs_find_subvol
))
4504 return STATUS_INVALID_PARAMETER
;
4506 if (!out
|| outlen
< sizeof(WCHAR
))
4507 return STATUS_INVALID_PARAMETER
;
4509 if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE
), Irp
->RequestorMode
))
4510 return STATUS_PRIVILEGE_NOT_HELD
;
4512 bfs
= (btrfs_find_subvol
*)in
;
4514 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, TRUE
);
4516 if (!Vcb
->uuid_root
) {
4517 ERR("couldn't find uuid root\n");
4518 Status
= STATUS_NOT_FOUND
;
4522 RtlCopyMemory(&searchkey
.obj_id
, &bfs
->uuid
, sizeof(UINT64
));
4523 searchkey
.obj_type
= TYPE_SUBVOL_UUID
;
4524 RtlCopyMemory(&searchkey
.offset
, &bfs
->uuid
.uuid
[sizeof(UINT64
)], sizeof(UINT64
));
4526 Status
= find_item(Vcb
, Vcb
->uuid_root
, &tp
, &searchkey
, FALSE
, Irp
);
4528 if (!NT_SUCCESS(Status
)) {
4529 ERR("find_item returned %08x\n", Status
);
4533 if (!keycmp(searchkey
, tp
.item
->key
) && tp
.item
->size
>= sizeof(UINT64
)) {
4534 UINT64
* id
= (UINT64
*)tp
.item
->data
;
4536 if (bfs
->ctransid
!= 0) {
4540 searchkey2
.obj_id
= *id
;
4541 searchkey2
.obj_type
= TYPE_ROOT_ITEM
;
4542 searchkey2
.offset
= 0xffffffffffffffff;
4544 Status
= find_item(Vcb
, Vcb
->root_root
, &tp2
, &searchkey2
, FALSE
, Irp
);
4545 if (!NT_SUCCESS(Status
)) {
4546 ERR("find_item returned %08x\n", Status
);
4550 if (tp2
.item
->key
.obj_id
== searchkey2
.obj_id
&& tp2
.item
->key
.obj_type
== searchkey2
.obj_type
&&
4551 tp2
.item
->size
>= offsetof(ROOT_ITEM
, otransid
)) {
4552 ROOT_ITEM
* ri
= (ROOT_ITEM
*)tp2
.item
->data
;
4554 if (ri
->ctransid
== bfs
->ctransid
) {
4555 TRACE("found subvol %llx\n", *id
);
4556 Status
= get_subvol_path(Vcb
, *id
, out
, outlen
, Irp
);
4561 TRACE("found subvol %llx\n", *id
);
4562 Status
= get_subvol_path(Vcb
, *id
, out
, outlen
, Irp
);
4567 searchkey
.obj_type
= TYPE_SUBVOL_REC_UUID
;
4569 Status
= find_item(Vcb
, Vcb
->uuid_root
, &tp
, &searchkey
, FALSE
, Irp
);
4571 if (!NT_SUCCESS(Status
)) {
4572 ERR("find_item returned %08x\n", Status
);
4576 if (!keycmp(searchkey
, tp
.item
->key
) && tp
.item
->size
>= sizeof(UINT64
)) {
4577 UINT64
* ids
= (UINT64
*)tp
.item
->data
;
4580 for (i
= 0; i
< tp
.item
->size
/ sizeof(UINT64
); i
++) {
4581 if (bfs
->ctransid
!= 0) {
4585 searchkey2
.obj_id
= ids
[i
];
4586 searchkey2
.obj_type
= TYPE_ROOT_ITEM
;
4587 searchkey2
.offset
= 0xffffffffffffffff;
4589 Status
= find_item(Vcb
, Vcb
->root_root
, &tp2
, &searchkey2
, FALSE
, Irp
);
4590 if (!NT_SUCCESS(Status
)) {
4591 ERR("find_item returned %08x\n", Status
);
4595 if (tp2
.item
->key
.obj_id
== searchkey2
.obj_id
&& tp2
.item
->key
.obj_type
== searchkey2
.obj_type
&&
4596 tp2
.item
->size
>= offsetof(ROOT_ITEM
, otransid
)) {
4597 ROOT_ITEM
* ri
= (ROOT_ITEM
*)tp2
.item
->data
;
4599 if (ri
->ctransid
== bfs
->ctransid
) {
4600 TRACE("found subvol %llx\n", ids
[i
]);
4601 Status
= get_subvol_path(Vcb
, ids
[i
], out
, outlen
, Irp
);
4606 TRACE("found subvol %llx\n", ids
[i
]);
4607 Status
= get_subvol_path(Vcb
, ids
[i
], out
, outlen
, Irp
);
4613 Status
= STATUS_NOT_FOUND
;
4616 ExReleaseResourceLite(&Vcb
->tree_lock
);
4621 static NTSTATUS
resize_device(device_extension
* Vcb
, void* data
, ULONG len
, PIRP Irp
) {
4622 btrfs_resize
* br
= (btrfs_resize
*)data
;
4627 TRACE("(%p, %p, %u)\n", Vcb
, data
, len
);
4629 if (!data
|| len
< sizeof(btrfs_resize
) || (br
->size
% Vcb
->superblock
.sector_size
) != 0)
4630 return STATUS_INVALID_PARAMETER
;
4632 if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE
), Irp
->RequestorMode
))
4633 return STATUS_PRIVILEGE_NOT_HELD
;
4636 return STATUS_MEDIA_WRITE_PROTECTED
;
4638 ExAcquireResourceExclusiveLite(&Vcb
->tree_lock
, TRUE
);
4640 le
= Vcb
->devices
.Flink
;
4641 while (le
!= &Vcb
->devices
) {
4642 device
* dev2
= CONTAINING_RECORD(le
, device
, list_entry
);
4644 if (dev2
->devitem
.dev_id
== br
->device
) {
4653 ERR("could not find device %llx\n", br
->device
);
4654 Status
= STATUS_INVALID_PARAMETER
;
4659 ERR("trying to resize missing device\n");
4660 Status
= STATUS_INVALID_PARAMETER
;
4664 if (dev
->readonly
) {
4665 ERR("trying to resize readonly device\n");
4666 Status
= STATUS_INVALID_PARAMETER
;
4670 if (br
->size
> 0 && dev
->devitem
.num_bytes
== br
->size
) {
4671 TRACE("size unchanged, returning STATUS_SUCCESS\n");
4672 Status
= STATUS_SUCCESS
;
4676 if (br
->size
> 0 && dev
->devitem
.num_bytes
> br
->size
) { // shrink device
4677 BOOL need_balance
= TRUE
;
4678 UINT64 old_size
, delta
;
4680 le
= dev
->space
.Flink
;
4681 while (le
!= &dev
->space
) {
4682 space
* s
= CONTAINING_RECORD(le
, space
, list_entry
);
4684 if (s
->address
<= br
->size
&& s
->address
+ s
->size
>= dev
->devitem
.num_bytes
) {
4685 need_balance
= FALSE
;
4692 delta
= dev
->devitem
.num_bytes
- br
->size
;
4697 if (Vcb
->balance
.thread
) {
4698 WARN("balance already running\n");
4699 Status
= STATUS_DEVICE_NOT_READY
;
4703 RtlZeroMemory(Vcb
->balance
.opts
, sizeof(btrfs_balance_opts
) * 3);
4705 for (i
= 0; i
< 3; i
++) {
4706 Vcb
->balance
.opts
[i
].flags
= BTRFS_BALANCE_OPTS_ENABLED
| BTRFS_BALANCE_OPTS_DEVID
| BTRFS_BALANCE_OPTS_DRANGE
;
4707 Vcb
->balance
.opts
[i
].devid
= dev
->devitem
.dev_id
;
4708 Vcb
->balance
.opts
[i
].drange_start
= br
->size
;
4709 Vcb
->balance
.opts
[i
].drange_end
= dev
->devitem
.num_bytes
;
4712 Vcb
->balance
.paused
= FALSE
;
4713 Vcb
->balance
.shrinking
= TRUE
;
4714 Vcb
->balance
.status
= STATUS_SUCCESS
;
4715 KeInitializeEvent(&Vcb
->balance
.event
, NotificationEvent
, !Vcb
->balance
.paused
);
4717 space_list_subtract2(&dev
->space
, NULL
, br
->size
, delta
, NULL
, NULL
);
4719 Status
= PsCreateSystemThread(&Vcb
->balance
.thread
, 0, NULL
, NULL
, NULL
, balance_thread
, Vcb
);
4720 if (!NT_SUCCESS(Status
)) {
4721 ERR("PsCreateSystemThread returned %08x\n", Status
);
4725 Status
= STATUS_MORE_PROCESSING_REQUIRED
;
4730 old_size
= dev
->devitem
.num_bytes
;
4731 dev
->devitem
.num_bytes
= br
->size
;
4733 Status
= update_dev_item(Vcb
, dev
, Irp
);
4734 if (!NT_SUCCESS(Status
)) {
4735 ERR("update_dev_item returned %08x\n", Status
);
4736 dev
->devitem
.num_bytes
= old_size
;
4740 space_list_subtract2(&dev
->space
, NULL
, br
->size
, delta
, NULL
, NULL
);
4742 Vcb
->superblock
.total_bytes
-= delta
;
4743 } else { // extend device
4744 GET_LENGTH_INFORMATION gli
;
4745 UINT64 old_size
, delta
;
4747 Status
= dev_ioctl(dev
->devobj
, IOCTL_DISK_GET_LENGTH_INFO
, NULL
, 0,
4748 &gli
, sizeof(gli
), TRUE
, NULL
);
4749 if (!NT_SUCCESS(Status
)) {
4750 ERR("IOCTL_DISK_GET_LENGTH_INFO returned %08x\n", Status
);
4754 if (br
->size
== 0) {
4755 br
->size
= gli
.Length
.QuadPart
;
4757 if (dev
->devitem
.num_bytes
== br
->size
) {
4758 TRACE("size unchanged, returning STATUS_SUCCESS\n");
4759 Status
= STATUS_SUCCESS
;
4763 if (br
->size
== 0) {
4764 ERR("IOCTL_DISK_GET_LENGTH_INFO returned 0 length\n");
4765 Status
= STATUS_INTERNAL_ERROR
;
4768 } else if ((UINT64
)gli
.Length
.QuadPart
< br
->size
) {
4769 ERR("device was %llx bytes, trying to extend to %llx\n", gli
.Length
.QuadPart
, br
->size
);
4770 Status
= STATUS_INVALID_PARAMETER
;
4774 delta
= br
->size
- dev
->devitem
.num_bytes
;
4776 old_size
= dev
->devitem
.num_bytes
;
4777 dev
->devitem
.num_bytes
= br
->size
;
4779 Status
= update_dev_item(Vcb
, dev
, Irp
);
4780 if (!NT_SUCCESS(Status
)) {
4781 ERR("update_dev_item returned %08x\n", Status
);
4782 dev
->devitem
.num_bytes
= old_size
;
4786 space_list_add2(&dev
->space
, NULL
, dev
->devitem
.num_bytes
, delta
, NULL
, NULL
);
4788 Vcb
->superblock
.total_bytes
+= delta
;
4791 Status
= STATUS_SUCCESS
;
4792 Vcb
->need_write
= TRUE
;
4795 ExReleaseResourceLite(&Vcb
->tree_lock
);
4797 if (NT_SUCCESS(Status
))
4798 FsRtlNotifyVolumeEvent(Vcb
->root_file
, FSRTL_VOLUME_CHANGE_SIZE
);
4803 NTSTATUS
fsctl_request(PDEVICE_OBJECT DeviceObject
, PIRP
* Pirp
, UINT32 type
) {
4805 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
4809 #if (NTDDI_VERSION >= NTDDI_WIN7)
4810 case FSCTL_REQUEST_OPLOCK
:
4811 WARN("STUB: FSCTL_REQUEST_OPLOCK\n");
4812 Status
= STATUS_INVALID_DEVICE_REQUEST
;
4816 case FSCTL_REQUEST_OPLOCK_LEVEL_1
:
4817 WARN("STUB: FSCTL_REQUEST_OPLOCK_LEVEL_1\n");
4818 Status
= STATUS_INVALID_DEVICE_REQUEST
;
4821 case FSCTL_REQUEST_OPLOCK_LEVEL_2
:
4822 WARN("STUB: FSCTL_REQUEST_OPLOCK_LEVEL_2\n");
4823 Status
= STATUS_INVALID_DEVICE_REQUEST
;
4826 case FSCTL_REQUEST_BATCH_OPLOCK
:
4827 WARN("STUB: FSCTL_REQUEST_BATCH_OPLOCK\n");
4828 Status
= STATUS_INVALID_DEVICE_REQUEST
;
4831 case FSCTL_OPLOCK_BREAK_ACKNOWLEDGE
:
4832 WARN("STUB: FSCTL_OPLOCK_BREAK_ACKNOWLEDGE\n");
4833 Status
= STATUS_INVALID_DEVICE_REQUEST
;
4836 case FSCTL_OPLOCK_BREAK_ACK_NO_2
:
4837 WARN("STUB: FSCTL_OPLOCK_BREAK_ACK_NO_2\n");
4838 Status
= STATUS_INVALID_DEVICE_REQUEST
;
4841 case FSCTL_OPBATCH_ACK_CLOSE_PENDING
:
4842 WARN("STUB: FSCTL_OPBATCH_ACK_CLOSE_PENDING\n");
4843 Status
= STATUS_INVALID_DEVICE_REQUEST
;
4846 case FSCTL_OPLOCK_BREAK_NOTIFY
:
4847 WARN("STUB: FSCTL_OPLOCK_BREAK_NOTIFY\n");
4848 Status
= STATUS_INVALID_DEVICE_REQUEST
;
4851 case FSCTL_REQUEST_FILTER_OPLOCK
:
4852 WARN("STUB: FSCTL_REQUEST_FILTER_OPLOCK\n");
4853 Status
= STATUS_INVALID_DEVICE_REQUEST
;
4856 case FSCTL_LOCK_VOLUME
:
4857 Status
= lock_volume(DeviceObject
->DeviceExtension
, Irp
);
4860 case FSCTL_UNLOCK_VOLUME
:
4861 Status
= unlock_volume(DeviceObject
->DeviceExtension
, Irp
);
4864 case FSCTL_DISMOUNT_VOLUME
:
4865 Status
= dismount_volume(DeviceObject
->DeviceExtension
, Irp
);
4868 case FSCTL_IS_VOLUME_MOUNTED
:
4869 Status
= is_volume_mounted(DeviceObject
->DeviceExtension
, Irp
);
4872 case FSCTL_IS_PATHNAME_VALID
:
4873 WARN("STUB: FSCTL_IS_PATHNAME_VALID\n");
4874 Status
= STATUS_INVALID_DEVICE_REQUEST
;
4877 case FSCTL_MARK_VOLUME_DIRTY
:
4878 WARN("STUB: FSCTL_MARK_VOLUME_DIRTY\n");
4879 Status
= STATUS_INVALID_DEVICE_REQUEST
;
4882 case FSCTL_QUERY_RETRIEVAL_POINTERS
:
4883 WARN("STUB: FSCTL_QUERY_RETRIEVAL_POINTERS\n");
4884 Status
= STATUS_INVALID_DEVICE_REQUEST
;
4887 case FSCTL_GET_COMPRESSION
:
4888 Status
= get_compression(Irp
);
4891 case FSCTL_SET_COMPRESSION
:
4892 Status
= set_compression(Irp
);
4895 case FSCTL_SET_BOOTLOADER_ACCESSED
:
4896 WARN("STUB: FSCTL_SET_BOOTLOADER_ACCESSED\n");
4897 Status
= STATUS_INVALID_DEVICE_REQUEST
;
4900 case FSCTL_INVALIDATE_VOLUMES
:
4901 Status
= invalidate_volumes(Irp
);
4904 case FSCTL_QUERY_FAT_BPB
:
4905 WARN("STUB: FSCTL_QUERY_FAT_BPB\n");
4906 Status
= STATUS_INVALID_DEVICE_REQUEST
;
4909 case FSCTL_FILESYSTEM_GET_STATISTICS
:
4910 Status
= fs_get_statistics(Irp
->AssociatedIrp
.SystemBuffer
, IrpSp
->Parameters
.FileSystemControl
.OutputBufferLength
, &Irp
->IoStatus
.Information
);
4913 case FSCTL_GET_NTFS_VOLUME_DATA
:
4914 WARN("STUB: FSCTL_GET_NTFS_VOLUME_DATA\n");
4915 Status
= STATUS_INVALID_DEVICE_REQUEST
;
4918 case FSCTL_GET_NTFS_FILE_RECORD
:
4919 WARN("STUB: FSCTL_GET_NTFS_FILE_RECORD\n");
4920 Status
= STATUS_INVALID_DEVICE_REQUEST
;
4923 case FSCTL_GET_VOLUME_BITMAP
:
4924 WARN("STUB: FSCTL_GET_VOLUME_BITMAP\n");
4925 Status
= STATUS_INVALID_DEVICE_REQUEST
;
4928 case FSCTL_GET_RETRIEVAL_POINTERS
:
4929 WARN("STUB: FSCTL_GET_RETRIEVAL_POINTERS\n");
4930 Status
= STATUS_INVALID_DEVICE_REQUEST
;
4933 case FSCTL_MOVE_FILE
:
4934 WARN("STUB: FSCTL_MOVE_FILE\n");
4935 Status
= STATUS_INVALID_DEVICE_REQUEST
;
4938 case FSCTL_IS_VOLUME_DIRTY
:
4939 Status
= is_volume_dirty(DeviceObject
->DeviceExtension
, Irp
);
4942 case FSCTL_ALLOW_EXTENDED_DASD_IO
:
4943 Status
= allow_extended_dasd_io(DeviceObject
->DeviceExtension
, IrpSp
->FileObject
);
4946 case FSCTL_FIND_FILES_BY_SID
:
4947 WARN("STUB: FSCTL_FIND_FILES_BY_SID\n");
4948 Status
= STATUS_INVALID_DEVICE_REQUEST
;
4951 case FSCTL_SET_OBJECT_ID
:
4952 WARN("STUB: FSCTL_SET_OBJECT_ID\n");
4953 Status
= STATUS_INVALID_DEVICE_REQUEST
;
4956 case FSCTL_GET_OBJECT_ID
:
4957 Status
= get_object_id(DeviceObject
->DeviceExtension
, IrpSp
->FileObject
, Irp
->UserBuffer
,
4958 IrpSp
->Parameters
.FileSystemControl
.OutputBufferLength
, &Irp
->IoStatus
.Information
);
4961 case FSCTL_DELETE_OBJECT_ID
:
4962 WARN("STUB: FSCTL_DELETE_OBJECT_ID\n");
4963 Status
= STATUS_INVALID_DEVICE_REQUEST
;
4966 case FSCTL_SET_REPARSE_POINT
:
4967 Status
= set_reparse_point(DeviceObject
, Irp
);
4970 case FSCTL_GET_REPARSE_POINT
:
4971 Status
= get_reparse_point(DeviceObject
, IrpSp
->FileObject
, Irp
->AssociatedIrp
.SystemBuffer
,
4972 IrpSp
->Parameters
.FileSystemControl
.OutputBufferLength
, &Irp
->IoStatus
.Information
);
4975 case FSCTL_DELETE_REPARSE_POINT
:
4976 Status
= delete_reparse_point(DeviceObject
, Irp
);
4979 case FSCTL_ENUM_USN_DATA
:
4980 WARN("STUB: FSCTL_ENUM_USN_DATA\n");
4981 Status
= STATUS_INVALID_DEVICE_REQUEST
;
4984 case FSCTL_SECURITY_ID_CHECK
:
4985 WARN("STUB: FSCTL_SECURITY_ID_CHECK\n");
4986 Status
= STATUS_INVALID_DEVICE_REQUEST
;
4989 case FSCTL_READ_USN_JOURNAL
:
4990 WARN("STUB: FSCTL_READ_USN_JOURNAL\n");
4991 Status
= STATUS_INVALID_DEVICE_REQUEST
;
4994 case FSCTL_SET_OBJECT_ID_EXTENDED
:
4995 WARN("STUB: FSCTL_SET_OBJECT_ID_EXTENDED\n");
4996 Status
= STATUS_INVALID_DEVICE_REQUEST
;
4999 case FSCTL_CREATE_OR_GET_OBJECT_ID
:
5000 Status
= get_object_id(DeviceObject
->DeviceExtension
, IrpSp
->FileObject
, Irp
->UserBuffer
,
5001 IrpSp
->Parameters
.FileSystemControl
.OutputBufferLength
, &Irp
->IoStatus
.Information
);
5004 case FSCTL_SET_SPARSE
:
5005 Status
= set_sparse(DeviceObject
->DeviceExtension
, IrpSp
->FileObject
, Irp
->AssociatedIrp
.SystemBuffer
,
5006 IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
, Irp
);
5009 case FSCTL_SET_ZERO_DATA
:
5010 Status
= set_zero_data(DeviceObject
->DeviceExtension
, IrpSp
->FileObject
, Irp
->AssociatedIrp
.SystemBuffer
,
5011 IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
, Irp
);
5014 case FSCTL_QUERY_ALLOCATED_RANGES
:
5015 Status
= query_ranges(IrpSp
->FileObject
, IrpSp
->Parameters
.FileSystemControl
.Type3InputBuffer
,
5016 IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
, Irp
->UserBuffer
,
5017 IrpSp
->Parameters
.FileSystemControl
.OutputBufferLength
, &Irp
->IoStatus
.Information
);
5020 case FSCTL_ENABLE_UPGRADE
:
5021 WARN("STUB: FSCTL_ENABLE_UPGRADE\n");
5022 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5025 case FSCTL_SET_ENCRYPTION
:
5026 WARN("STUB: FSCTL_SET_ENCRYPTION\n");
5027 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5030 case FSCTL_ENCRYPTION_FSCTL_IO
:
5031 WARN("STUB: FSCTL_ENCRYPTION_FSCTL_IO\n");
5032 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5035 case FSCTL_WRITE_RAW_ENCRYPTED
:
5036 WARN("STUB: FSCTL_WRITE_RAW_ENCRYPTED\n");
5037 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5040 case FSCTL_READ_RAW_ENCRYPTED
:
5041 WARN("STUB: FSCTL_READ_RAW_ENCRYPTED\n");
5042 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5045 case FSCTL_CREATE_USN_JOURNAL
:
5046 WARN("STUB: FSCTL_CREATE_USN_JOURNAL\n");
5047 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5050 case FSCTL_READ_FILE_USN_DATA
:
5051 WARN("STUB: FSCTL_READ_FILE_USN_DATA\n");
5052 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5055 case FSCTL_WRITE_USN_CLOSE_RECORD
:
5056 WARN("STUB: FSCTL_WRITE_USN_CLOSE_RECORD\n");
5057 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5060 case FSCTL_EXTEND_VOLUME
:
5061 WARN("STUB: FSCTL_EXTEND_VOLUME\n");
5062 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5065 case FSCTL_QUERY_USN_JOURNAL
:
5066 WARN("STUB: FSCTL_QUERY_USN_JOURNAL\n");
5067 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5070 case FSCTL_DELETE_USN_JOURNAL
:
5071 WARN("STUB: FSCTL_DELETE_USN_JOURNAL\n");
5072 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5075 case FSCTL_MARK_HANDLE
:
5076 WARN("STUB: FSCTL_MARK_HANDLE\n");
5077 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5080 case FSCTL_SIS_COPYFILE
:
5081 WARN("STUB: FSCTL_SIS_COPYFILE\n");
5082 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5085 case FSCTL_SIS_LINK_FILES
:
5086 WARN("STUB: FSCTL_SIS_LINK_FILES\n");
5087 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5090 case FSCTL_RECALL_FILE
:
5091 WARN("STUB: FSCTL_RECALL_FILE\n");
5092 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5095 case FSCTL_READ_FROM_PLEX
:
5096 WARN("STUB: FSCTL_READ_FROM_PLEX\n");
5097 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5100 case FSCTL_FILE_PREFETCH
:
5101 WARN("STUB: FSCTL_FILE_PREFETCH\n");
5102 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5105 #if _WIN32_WINNT >= 0x0600
5106 case FSCTL_MAKE_MEDIA_COMPATIBLE
:
5107 WARN("STUB: FSCTL_MAKE_MEDIA_COMPATIBLE\n");
5108 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5111 case FSCTL_SET_DEFECT_MANAGEMENT
:
5112 WARN("STUB: FSCTL_SET_DEFECT_MANAGEMENT\n");
5113 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5116 case FSCTL_QUERY_SPARING_INFO
:
5117 WARN("STUB: FSCTL_QUERY_SPARING_INFO\n");
5118 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5121 case FSCTL_QUERY_ON_DISK_VOLUME_INFO
:
5122 WARN("STUB: FSCTL_QUERY_ON_DISK_VOLUME_INFO\n");
5123 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5126 case FSCTL_SET_VOLUME_COMPRESSION_STATE
:
5127 WARN("STUB: FSCTL_SET_VOLUME_COMPRESSION_STATE\n");
5128 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5131 case FSCTL_TXFS_MODIFY_RM
:
5132 WARN("STUB: FSCTL_TXFS_MODIFY_RM\n");
5133 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5136 case FSCTL_TXFS_QUERY_RM_INFORMATION
:
5137 WARN("STUB: FSCTL_TXFS_QUERY_RM_INFORMATION\n");
5138 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5141 case FSCTL_TXFS_ROLLFORWARD_REDO
:
5142 WARN("STUB: FSCTL_TXFS_ROLLFORWARD_REDO\n");
5143 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5146 case FSCTL_TXFS_ROLLFORWARD_UNDO
:
5147 WARN("STUB: FSCTL_TXFS_ROLLFORWARD_UNDO\n");
5148 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5151 case FSCTL_TXFS_START_RM
:
5152 WARN("STUB: FSCTL_TXFS_START_RM\n");
5153 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5156 case FSCTL_TXFS_SHUTDOWN_RM
:
5157 WARN("STUB: FSCTL_TXFS_SHUTDOWN_RM\n");
5158 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5161 case FSCTL_TXFS_READ_BACKUP_INFORMATION
:
5162 WARN("STUB: FSCTL_TXFS_READ_BACKUP_INFORMATION\n");
5163 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5166 case FSCTL_TXFS_WRITE_BACKUP_INFORMATION
:
5167 WARN("STUB: FSCTL_TXFS_WRITE_BACKUP_INFORMATION\n");
5168 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5171 case FSCTL_TXFS_CREATE_SECONDARY_RM
:
5172 WARN("STUB: FSCTL_TXFS_CREATE_SECONDARY_RM\n");
5173 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5176 case FSCTL_TXFS_GET_METADATA_INFO
:
5177 WARN("STUB: FSCTL_TXFS_GET_METADATA_INFO\n");
5178 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5181 case FSCTL_TXFS_GET_TRANSACTED_VERSION
:
5182 WARN("STUB: FSCTL_TXFS_GET_TRANSACTED_VERSION\n");
5183 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5186 case FSCTL_TXFS_SAVEPOINT_INFORMATION
:
5187 WARN("STUB: FSCTL_TXFS_SAVEPOINT_INFORMATION\n");
5188 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5191 case FSCTL_TXFS_CREATE_MINIVERSION
:
5192 WARN("STUB: FSCTL_TXFS_CREATE_MINIVERSION\n");
5193 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5196 case FSCTL_TXFS_TRANSACTION_ACTIVE
:
5197 WARN("STUB: FSCTL_TXFS_TRANSACTION_ACTIVE\n");
5198 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5201 case FSCTL_SET_ZERO_ON_DEALLOCATION
:
5202 WARN("STUB: FSCTL_SET_ZERO_ON_DEALLOCATION\n");
5203 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5206 case FSCTL_SET_REPAIR
:
5207 WARN("STUB: FSCTL_SET_REPAIR\n");
5208 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5211 case FSCTL_GET_REPAIR
:
5212 WARN("STUB: FSCTL_GET_REPAIR\n");
5213 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5216 case FSCTL_WAIT_FOR_REPAIR
:
5217 WARN("STUB: FSCTL_WAIT_FOR_REPAIR\n");
5218 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5221 case FSCTL_INITIATE_REPAIR
:
5222 WARN("STUB: FSCTL_INITIATE_REPAIR\n");
5223 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5226 case FSCTL_CSC_INTERNAL
:
5227 WARN("STUB: FSCTL_CSC_INTERNAL\n");
5228 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5231 case FSCTL_SHRINK_VOLUME
:
5232 WARN("STUB: FSCTL_SHRINK_VOLUME\n");
5233 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5236 case FSCTL_SET_SHORT_NAME_BEHAVIOR
:
5237 WARN("STUB: FSCTL_SET_SHORT_NAME_BEHAVIOR\n");
5238 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5241 case FSCTL_DFSR_SET_GHOST_HANDLE_STATE
:
5242 WARN("STUB: FSCTL_DFSR_SET_GHOST_HANDLE_STATE\n");
5243 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5246 case FSCTL_TXFS_LIST_TRANSACTION_LOCKED_FILES
:
5247 WARN("STUB: FSCTL_TXFS_LIST_TRANSACTION_LOCKED_FILES\n");
5248 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5251 case FSCTL_TXFS_LIST_TRANSACTIONS
:
5252 WARN("STUB: FSCTL_TXFS_LIST_TRANSACTIONS\n");
5253 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5256 case FSCTL_QUERY_PAGEFILE_ENCRYPTION
:
5257 WARN("STUB: FSCTL_QUERY_PAGEFILE_ENCRYPTION\n");
5258 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5261 case FSCTL_RESET_VOLUME_ALLOCATION_HINTS
:
5262 WARN("STUB: FSCTL_RESET_VOLUME_ALLOCATION_HINTS\n");
5263 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5266 case FSCTL_TXFS_READ_BACKUP_INFORMATION2
:
5267 WARN("STUB: FSCTL_TXFS_READ_BACKUP_INFORMATION2\n");
5268 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5271 case FSCTL_CSV_CONTROL
:
5272 WARN("STUB: FSCTL_CSV_CONTROL\n");
5273 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5276 // TRACE rather than WARN because Windows 10 spams this undocumented fsctl
5277 case FSCTL_QUERY_VOLUME_CONTAINER_STATE
:
5278 TRACE("STUB: FSCTL_QUERY_VOLUME_CONTAINER_STATE\n");
5279 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5282 case FSCTL_GET_INTEGRITY_INFORMATION
:
5283 Status
= get_integrity_information(DeviceObject
->DeviceExtension
, IrpSp
->FileObject
, map_user_buffer(Irp
, NormalPagePriority
),
5284 IrpSp
->Parameters
.FileSystemControl
.OutputBufferLength
);
5287 case FSCTL_SET_INTEGRITY_INFORMATION
:
5288 Status
= set_integrity_information(IrpSp
->FileObject
, Irp
->AssociatedIrp
.SystemBuffer
, IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
);
5291 case FSCTL_DUPLICATE_EXTENTS_TO_FILE
:
5292 Status
= duplicate_extents(DeviceObject
->DeviceExtension
, IrpSp
->FileObject
, Irp
->AssociatedIrp
.SystemBuffer
,
5293 IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
, Irp
);
5296 case FSCTL_BTRFS_GET_FILE_IDS
:
5297 Status
= get_file_ids(IrpSp
->FileObject
, map_user_buffer(Irp
, NormalPagePriority
), IrpSp
->Parameters
.FileSystemControl
.OutputBufferLength
);
5300 case FSCTL_BTRFS_CREATE_SUBVOL
:
5301 Status
= create_subvol(DeviceObject
->DeviceExtension
, IrpSp
->FileObject
, Irp
->AssociatedIrp
.SystemBuffer
, IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
, Irp
);
5304 case FSCTL_BTRFS_CREATE_SNAPSHOT
:
5305 Status
= create_snapshot(DeviceObject
->DeviceExtension
, IrpSp
->FileObject
, Irp
->AssociatedIrp
.SystemBuffer
, IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
, Irp
);
5308 case FSCTL_BTRFS_GET_INODE_INFO
:
5309 Status
= get_inode_info(IrpSp
->FileObject
, map_user_buffer(Irp
, NormalPagePriority
), IrpSp
->Parameters
.FileSystemControl
.OutputBufferLength
);
5312 case FSCTL_BTRFS_SET_INODE_INFO
:
5313 Status
= set_inode_info(IrpSp
->FileObject
, Irp
->AssociatedIrp
.SystemBuffer
, IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
, Irp
);
5316 case FSCTL_BTRFS_GET_DEVICES
:
5317 Status
= get_devices(DeviceObject
->DeviceExtension
, map_user_buffer(Irp
, NormalPagePriority
), IrpSp
->Parameters
.FileSystemControl
.OutputBufferLength
);
5320 case FSCTL_BTRFS_GET_USAGE
:
5321 Status
= get_usage(DeviceObject
->DeviceExtension
, map_user_buffer(Irp
, NormalPagePriority
), IrpSp
->Parameters
.FileSystemControl
.OutputBufferLength
, Irp
);
5324 case FSCTL_BTRFS_START_BALANCE
:
5325 Status
= start_balance(DeviceObject
->DeviceExtension
, Irp
->AssociatedIrp
.SystemBuffer
, IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
, Irp
->RequestorMode
);
5328 case FSCTL_BTRFS_QUERY_BALANCE
:
5329 Status
= query_balance(DeviceObject
->DeviceExtension
, map_user_buffer(Irp
, NormalPagePriority
), IrpSp
->Parameters
.FileSystemControl
.OutputBufferLength
);
5332 case FSCTL_BTRFS_PAUSE_BALANCE
:
5333 Status
= pause_balance(DeviceObject
->DeviceExtension
, Irp
->RequestorMode
);
5336 case FSCTL_BTRFS_RESUME_BALANCE
:
5337 Status
= resume_balance(DeviceObject
->DeviceExtension
, Irp
->RequestorMode
);
5340 case FSCTL_BTRFS_STOP_BALANCE
:
5341 Status
= stop_balance(DeviceObject
->DeviceExtension
, Irp
->RequestorMode
);
5344 case FSCTL_BTRFS_ADD_DEVICE
:
5345 Status
= add_device(DeviceObject
->DeviceExtension
, Irp
, Irp
->RequestorMode
);
5348 case FSCTL_BTRFS_REMOVE_DEVICE
:
5349 Status
= remove_device(DeviceObject
->DeviceExtension
, Irp
->AssociatedIrp
.SystemBuffer
, IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
, Irp
->RequestorMode
);
5352 case FSCTL_BTRFS_GET_UUID
:
5353 Status
= query_uuid(DeviceObject
->DeviceExtension
, map_user_buffer(Irp
, NormalPagePriority
), IrpSp
->Parameters
.FileSystemControl
.OutputBufferLength
);
5356 case FSCTL_BTRFS_START_SCRUB
:
5357 Status
= start_scrub(DeviceObject
->DeviceExtension
, Irp
->RequestorMode
);
5360 case FSCTL_BTRFS_QUERY_SCRUB
:
5361 Status
= query_scrub(DeviceObject
->DeviceExtension
, Irp
->RequestorMode
, map_user_buffer(Irp
, NormalPagePriority
), IrpSp
->Parameters
.FileSystemControl
.OutputBufferLength
);
5364 case FSCTL_BTRFS_PAUSE_SCRUB
:
5365 Status
= pause_scrub(DeviceObject
->DeviceExtension
, Irp
->RequestorMode
);
5368 case FSCTL_BTRFS_RESUME_SCRUB
:
5369 Status
= resume_scrub(DeviceObject
->DeviceExtension
, Irp
->RequestorMode
);
5372 case FSCTL_BTRFS_STOP_SCRUB
:
5373 Status
= stop_scrub(DeviceObject
->DeviceExtension
, Irp
->RequestorMode
);
5376 case FSCTL_BTRFS_RESET_STATS
:
5377 Status
= reset_stats(DeviceObject
->DeviceExtension
, Irp
->AssociatedIrp
.SystemBuffer
, IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
, Irp
->RequestorMode
);
5380 case FSCTL_BTRFS_MKNOD
:
5381 Status
= mknod(DeviceObject
->DeviceExtension
, IrpSp
->FileObject
, Irp
->AssociatedIrp
.SystemBuffer
, IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
, Irp
);
5384 case FSCTL_BTRFS_RECEIVED_SUBVOL
:
5385 Status
= recvd_subvol(DeviceObject
->DeviceExtension
, IrpSp
->FileObject
, Irp
->AssociatedIrp
.SystemBuffer
,
5386 IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
, Irp
->RequestorMode
);
5389 case FSCTL_BTRFS_GET_XATTRS
:
5390 Status
= fsctl_get_xattrs(DeviceObject
->DeviceExtension
, IrpSp
->FileObject
, Irp
->UserBuffer
, IrpSp
->Parameters
.FileSystemControl
.OutputBufferLength
, Irp
->RequestorMode
);
5393 case FSCTL_BTRFS_SET_XATTR
:
5394 Status
= fsctl_set_xattr(DeviceObject
->DeviceExtension
, IrpSp
->FileObject
, Irp
->AssociatedIrp
.SystemBuffer
,
5395 IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
, Irp
);
5398 case FSCTL_BTRFS_RESERVE_SUBVOL
:
5399 Status
= reserve_subvol(DeviceObject
->DeviceExtension
, IrpSp
->FileObject
, Irp
);
5402 case FSCTL_BTRFS_FIND_SUBVOL
:
5403 Status
= find_subvol(DeviceObject
->DeviceExtension
, Irp
->AssociatedIrp
.SystemBuffer
, IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
,
5404 Irp
->UserBuffer
, IrpSp
->Parameters
.FileSystemControl
.OutputBufferLength
, Irp
);
5407 case FSCTL_BTRFS_SEND_SUBVOL
:
5408 Status
= send_subvol(DeviceObject
->DeviceExtension
, Irp
->AssociatedIrp
.SystemBuffer
, IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
,
5409 IrpSp
->FileObject
, Irp
);
5412 case FSCTL_BTRFS_READ_SEND_BUFFER
:
5413 Status
= read_send_buffer(DeviceObject
->DeviceExtension
, IrpSp
->FileObject
, map_user_buffer(Irp
, NormalPagePriority
), IrpSp
->Parameters
.FileSystemControl
.OutputBufferLength
,
5414 &Irp
->IoStatus
.Information
, Irp
->RequestorMode
);
5417 case FSCTL_BTRFS_RESIZE
:
5418 Status
= resize_device(DeviceObject
->DeviceExtension
, Irp
->AssociatedIrp
.SystemBuffer
,
5419 IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
, Irp
);
5423 WARN("unknown control code %x (DeviceType = %x, Access = %x, Function = %x, Method = %x)\n",
5424 IrpSp
->Parameters
.FileSystemControl
.FsControlCode
, (IrpSp
->Parameters
.FileSystemControl
.FsControlCode
& 0xff0000) >> 16,
5425 (IrpSp
->Parameters
.FileSystemControl
.FsControlCode
& 0xc000) >> 14, (IrpSp
->Parameters
.FileSystemControl
.FsControlCode
& 0x3ffc) >> 2,
5426 IrpSp
->Parameters
.FileSystemControl
.FsControlCode
& 0x3);
5427 Status
= STATUS_INVALID_DEVICE_REQUEST
;