1 /* Copyright (c) Mark Harmstone 2016-17
3 * This file is part of WinBtrfs.
5 * WinBtrfs is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public Licence as published by
7 * the Free Software Foundation, either version 3 of the Licence, or
8 * (at your option) any later version.
10 * WinBtrfs is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Lesser General Public Licence for more details.
15 * You should have received a copy of the GNU Lesser General Public Licence
16 * along with WinBtrfs. If not, see <http://www.gnu.org/licenses/>. */
18 #include "btrfs_drv.h"
19 #include "btrfsioctl.h"
26 #ifndef FSCTL_CSV_CONTROL
27 #define FSCTL_CSV_CONTROL CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 181, METHOD_BUFFERED, FILE_ANY_ACCESS)
30 #ifndef FSCTL_QUERY_VOLUME_CONTAINER_STATE
31 #define FSCTL_QUERY_VOLUME_CONTAINER_STATE CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 228, METHOD_BUFFERED, FILE_ANY_ACCESS)
36 #define SEF_AVOID_PRIVILEGE_CHECK 0x08 // on MSDN but not in any header files(?)
38 #ifndef _MSC_VER // not in mingw yet
39 #define DEVICE_DSM_FLAG_TRIM_NOT_FS_ALLOCATED 0x80000000
42 #define SEF_SACL_AUTO_INHERIT 0x02
44 extern LIST_ENTRY VcbList
;
45 extern ERESOURCE global_loading_lock
;
46 extern PDRIVER_OBJECT drvobj
;
48 static void mark_subvol_dirty(device_extension
* Vcb
, root
* r
);
50 static NTSTATUS
get_file_ids(PFILE_OBJECT FileObject
, void* data
, ULONG length
) {
51 btrfs_get_file_ids
* bgfi
;
54 if (length
< sizeof(btrfs_get_file_ids
))
55 return STATUS_BUFFER_OVERFLOW
;
58 return STATUS_INVALID_PARAMETER
;
60 fcb
= FileObject
->FsContext
;
63 return STATUS_INVALID_PARAMETER
;
67 bgfi
->subvol
= fcb
->subvol
->id
;
68 bgfi
->inode
= fcb
->inode
;
69 bgfi
->top
= fcb
->Vcb
->root_fileref
->fcb
== fcb
? TRUE
: FALSE
;
71 return STATUS_SUCCESS
;
74 static void get_uuid(BTRFS_UUID
* uuid
) {
78 seed
= KeQueryPerformanceCounter(NULL
);
80 for (i
= 0; i
< 16; i
+=2) {
81 ULONG rand
= RtlRandomEx(&seed
.LowPart
);
83 uuid
->uuid
[i
] = (rand
& 0xff00) >> 8;
84 uuid
->uuid
[i
+1] = rand
& 0xff;
88 static NTSTATUS
snapshot_tree_copy(device_extension
* Vcb
, UINT64 addr
, root
* subvol
, UINT64
* newaddr
, PIRP Irp
, LIST_ENTRY
* rollback
) {
91 write_data_context wtc
;
97 buf
= ExAllocatePoolWithTag(NonPagedPool
, Vcb
->superblock
.node_size
, ALLOC_TAG
);
99 ERR("out of memory\n");
100 return STATUS_INSUFFICIENT_RESOURCES
;
103 wtc
.parity1
= wtc
.parity2
= wtc
.scratch
= NULL
;
104 wtc
.mdl
= wtc
.parity1_mdl
= wtc
.parity2_mdl
= NULL
;
106 Status
= read_data(Vcb
, addr
, Vcb
->superblock
.node_size
, NULL
, TRUE
, buf
, NULL
, NULL
, Irp
, 0, FALSE
, NormalPagePriority
);
107 if (!NT_SUCCESS(Status
)) {
108 ERR("read_data returned %08x\n", Status
);
112 th
= (tree_header
*)buf
;
114 RtlZeroMemory(&t
, sizeof(tree
));
116 t
.header
.level
= th
->level
;
117 t
.header
.tree_id
= t
.root
->id
;
119 Status
= get_tree_new_address(Vcb
, &t
, Irp
, rollback
);
120 if (!NT_SUCCESS(Status
)) {
121 ERR("get_tree_new_address returned %08x\n", Status
);
125 if (!t
.has_new_address
) {
126 ERR("tree new address not set\n");
127 Status
= STATUS_INTERNAL_ERROR
;
131 c
= get_chunk_from_address(Vcb
, t
.new_address
);
134 c
->used
+= Vcb
->superblock
.node_size
;
136 ERR("could not find chunk for address %llx\n", t
.new_address
);
137 Status
= STATUS_INTERNAL_ERROR
;
141 th
->address
= t
.new_address
;
142 th
->tree_id
= subvol
->id
;
143 th
->generation
= Vcb
->superblock
.generation
;
144 th
->fs_uuid
= Vcb
->superblock
.uuid
;
146 if (th
->level
== 0) {
148 leaf_node
* ln
= (leaf_node
*)&th
[1];
150 for (i
= 0; i
< th
->num_items
; i
++) {
151 if (ln
[i
].key
.obj_type
== TYPE_EXTENT_DATA
&& ln
[i
].size
>= sizeof(EXTENT_DATA
) && ln
[i
].offset
+ ln
[i
].size
<= Vcb
->superblock
.node_size
- sizeof(tree_header
)) {
152 EXTENT_DATA
* ed
= (EXTENT_DATA
*)(((UINT8
*)&th
[1]) + ln
[i
].offset
);
154 if ((ed
->type
== EXTENT_TYPE_REGULAR
|| ed
->type
== EXTENT_TYPE_PREALLOC
) && ln
[i
].size
>= sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
)) {
155 EXTENT_DATA2
* ed2
= (EXTENT_DATA2
*)&ed
->data
[0];
157 if (ed2
->size
!= 0) { // not sparse
158 Status
= increase_extent_refcount_data(Vcb
, ed2
->address
, ed2
->size
, subvol
->id
, ln
[i
].key
.obj_id
, ln
[i
].key
.offset
- ed2
->offset
, 1, Irp
);
160 if (!NT_SUCCESS(Status
)) {
161 ERR("increase_extent_refcount_data returned %08x\n", Status
);
170 internal_node
* in
= (internal_node
*)&th
[1];
172 for (i
= 0; i
< th
->num_items
; i
++) {
175 tbr
.offset
= subvol
->id
;
177 Status
= increase_extent_refcount(Vcb
, in
[i
].address
, Vcb
->superblock
.node_size
, TYPE_TREE_BLOCK_REF
, &tbr
, NULL
, th
->level
- 1, Irp
);
178 if (!NT_SUCCESS(Status
)) {
179 ERR("increase_extent_refcount returned %08x\n", Status
);
185 *((UINT32
*)buf
) = ~calc_crc32c(0xffffffff, (UINT8
*)&th
->fs_uuid
, Vcb
->superblock
.node_size
- sizeof(th
->csum
));
187 KeInitializeEvent(&wtc
.Event
, NotificationEvent
, FALSE
);
188 InitializeListHead(&wtc
.stripes
);
189 wtc
.stripes_left
= 0;
191 Status
= write_data(Vcb
, t
.new_address
, buf
, Vcb
->superblock
.node_size
, &wtc
, NULL
, NULL
, FALSE
, 0, NormalPagePriority
);
192 if (!NT_SUCCESS(Status
)) {
193 ERR("write_data returned %08x\n", Status
);
197 if (wtc
.stripes
.Flink
!= &wtc
.stripes
) {
198 BOOL need_wait
= FALSE
;
200 // launch writes and wait
201 le
= wtc
.stripes
.Flink
;
202 while (le
!= &wtc
.stripes
) {
203 write_data_stripe
* stripe
= CONTAINING_RECORD(le
, write_data_stripe
, list_entry
);
205 if (stripe
->status
!= WriteDataStatus_Ignore
) {
207 IoCallDriver(stripe
->device
->devobj
, stripe
->Irp
);
214 KeWaitForSingleObject(&wtc
.Event
, Executive
, KernelMode
, FALSE
, NULL
);
216 le
= wtc
.stripes
.Flink
;
217 while (le
!= &wtc
.stripes
) {
218 write_data_stripe
* stripe
= CONTAINING_RECORD(le
, write_data_stripe
, list_entry
);
220 if (stripe
->status
!= WriteDataStatus_Ignore
&& !NT_SUCCESS(stripe
->iosb
.Status
)) {
221 Status
= stripe
->iosb
.Status
;
222 log_device_error(Vcb
, stripe
->device
, BTRFS_DEV_STAT_WRITE_ERRORS
);
229 free_write_data_stripes(&wtc
);
233 if (NT_SUCCESS(Status
))
234 *newaddr
= t
.new_address
;
244 void flush_subvol_fcbs(root
* subvol
) {
245 LIST_ENTRY
* le
= subvol
->fcbs
.Flink
;
247 if (IsListEmpty(&subvol
->fcbs
))
250 while (le
!= &subvol
->fcbs
) {
251 struct _fcb
* fcb
= CONTAINING_RECORD(le
, struct _fcb
, list_entry
);
252 IO_STATUS_BLOCK iosb
;
254 if (fcb
->type
!= BTRFS_TYPE_DIRECTORY
&& !fcb
->deleted
)
255 CcFlushCache(&fcb
->nonpaged
->segment_object
, NULL
, 0, &iosb
);
261 static NTSTATUS
do_create_snapshot(device_extension
* Vcb
, PFILE_OBJECT parent
, fcb
* subvol_fcb
, PANSI_STRING utf8
, PUNICODE_STRING name
, BOOL readonly
, PIRP Irp
) {
265 root
*r
, *subvol
= subvol_fcb
->subvol
;
268 UINT64 address
, *root_num
;
271 fcb
* fcb
= parent
->FsContext
;
272 ccb
* ccb
= parent
->FsContext2
;
274 file_ref
*fileref
, *fr
;
275 dir_child
* dc
= NULL
;
278 ERR("error - ccb was NULL\n");
279 return STATUS_INTERNAL_ERROR
;
282 if (!(ccb
->access
& FILE_ADD_SUBDIRECTORY
)) {
283 WARN("insufficient privileges\n");
284 return STATUS_ACCESS_DENIED
;
287 fileref
= ccb
->fileref
;
289 if (fileref
->fcb
== Vcb
->dummy_fcb
)
290 return STATUS_ACCESS_DENIED
;
292 // flush open files on this subvol
294 flush_subvol_fcbs(subvol
);
299 Status
= do_write(Vcb
, Irp
);
301 Status
= STATUS_SUCCESS
;
305 if (!NT_SUCCESS(Status
)) {
306 ERR("do_write returned %08x\n", Status
);
310 InitializeListHead(&rollback
);
314 id
= InterlockedIncrement64(&Vcb
->root_root
->lastinode
);
315 Status
= create_root(Vcb
, id
, &r
, TRUE
, Vcb
->superblock
.generation
, Irp
);
317 if (!NT_SUCCESS(Status
)) {
318 ERR("create_root returned %08x\n", Status
);
322 r
->lastinode
= subvol
->lastinode
;
324 if (!Vcb
->uuid_root
) {
327 TRACE("uuid root doesn't exist, creating it\n");
329 Status
= create_root(Vcb
, BTRFS_ROOT_UUID
, &uuid_root
, FALSE
, 0, Irp
);
331 if (!NT_SUCCESS(Status
)) {
332 ERR("create_root returned %08x\n", Status
);
336 Vcb
->uuid_root
= uuid_root
;
339 root_num
= ExAllocatePoolWithTag(PagedPool
, sizeof(UINT64
), ALLOC_TAG
);
341 ERR("out of memory\n");
342 Status
= STATUS_INSUFFICIENT_RESOURCES
;
349 get_uuid(&r
->root_item
.uuid
);
351 RtlCopyMemory(&searchkey
.obj_id
, &r
->root_item
.uuid
, sizeof(UINT64
));
352 searchkey
.obj_type
= TYPE_SUBVOL_UUID
;
353 RtlCopyMemory(&searchkey
.offset
, &r
->root_item
.uuid
.uuid
[sizeof(UINT64
)], sizeof(UINT64
));
355 Status
= find_item(Vcb
, Vcb
->uuid_root
, &tp
, &searchkey
, FALSE
, Irp
);
356 } while (NT_SUCCESS(Status
) && !keycmp(searchkey
, tp
.item
->key
));
360 Status
= insert_tree_item(Vcb
, Vcb
->uuid_root
, searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
, root_num
, sizeof(UINT64
), NULL
, Irp
);
361 if (!NT_SUCCESS(Status
)) {
362 ERR("insert_tree_item returned %08x\n", Status
);
363 ExFreePool(root_num
);
367 searchkey
.obj_id
= r
->id
;
368 searchkey
.obj_type
= TYPE_ROOT_ITEM
;
369 searchkey
.offset
= 0xffffffffffffffff;
371 Status
= find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, FALSE
, Irp
);
372 if (!NT_SUCCESS(Status
)) {
373 ERR("error - find_item returned %08x\n", Status
);
377 Status
= snapshot_tree_copy(Vcb
, subvol
->root_item
.block_number
, r
, &address
, Irp
, &rollback
);
378 if (!NT_SUCCESS(Status
)) {
379 ERR("snapshot_tree_copy returned %08x\n", Status
);
383 KeQuerySystemTime(&time
);
384 win_time_to_unix(time
, &now
);
386 r
->root_item
.inode
.generation
= 1;
387 r
->root_item
.inode
.st_size
= 3;
388 r
->root_item
.inode
.st_blocks
= subvol
->root_item
.inode
.st_blocks
;
389 r
->root_item
.inode
.st_nlink
= 1;
390 r
->root_item
.inode
.st_mode
= __S_IFDIR
| S_IRUSR
| S_IWUSR
| S_IXUSR
| S_IRGRP
| S_IXGRP
| S_IROTH
| S_IXOTH
; // 40755
391 r
->root_item
.inode
.flags
= 0xffffffff80000000; // FIXME - find out what these mean
392 r
->root_item
.generation
= Vcb
->superblock
.generation
;
393 r
->root_item
.objid
= subvol
->root_item
.objid
;
394 r
->root_item
.block_number
= address
;
395 r
->root_item
.bytes_used
= subvol
->root_item
.bytes_used
;
396 r
->root_item
.last_snapshot_generation
= Vcb
->superblock
.generation
;
397 r
->root_item
.root_level
= subvol
->root_item
.root_level
;
398 r
->root_item
.generation2
= Vcb
->superblock
.generation
;
399 r
->root_item
.parent_uuid
= subvol
->root_item
.uuid
;
400 r
->root_item
.ctransid
= subvol
->root_item
.ctransid
;
401 r
->root_item
.otransid
= Vcb
->superblock
.generation
;
402 r
->root_item
.ctime
= subvol
->root_item
.ctime
;
403 r
->root_item
.otime
= now
;
406 r
->root_item
.flags
|= BTRFS_SUBVOL_READONLY
;
408 r
->treeholder
.address
= address
;
410 // FIXME - do we need to copy over the send and receive fields too?
412 if (tp
.item
->key
.obj_id
!= searchkey
.obj_id
|| tp
.item
->key
.obj_type
!= searchkey
.obj_type
) {
413 ERR("error - could not find ROOT_ITEM for subvol %llx\n", r
->id
);
414 Status
= STATUS_INTERNAL_ERROR
;
418 RtlCopyMemory(tp
.item
->data
, &r
->root_item
, sizeof(ROOT_ITEM
));
420 // update ROOT_ITEM of original subvol
422 subvol
->root_item
.last_snapshot_generation
= Vcb
->superblock
.generation
;
424 mark_subvol_dirty(Vcb
, subvol
);
426 // create fileref for entry in other subvolume
428 fr
= create_fileref(Vcb
);
430 ERR("out of memory\n");
431 Status
= STATUS_INSUFFICIENT_RESOURCES
;
435 Status
= open_fcb(Vcb
, r
, r
->root_item
.objid
, BTRFS_TYPE_DIRECTORY
, utf8
, fcb
, &fr
->fcb
, PagedPool
, Irp
);
436 if (!NT_SUCCESS(Status
)) {
437 ERR("open_fcb returned %08x\n", Status
);
438 free_fileref(Vcb
, fr
);
442 fr
->parent
= fileref
;
444 Status
= add_dir_child(fileref
->fcb
, r
->id
, TRUE
, utf8
, name
, BTRFS_TYPE_DIRECTORY
, &dc
);
445 if (!NT_SUCCESS(Status
))
446 WARN("add_dir_child returned %08x\n", Status
);
451 ExAcquireResourceExclusiveLite(&fileref
->nonpaged
->children_lock
, TRUE
);
452 InsertTailList(&fileref
->children
, &fr
->list_entry
);
453 ExReleaseResourceLite(&fileref
->nonpaged
->children_lock
);
455 increase_fileref_refcount(fileref
);
458 mark_fileref_dirty(fr
);
460 if (fr
->fcb
->type
== BTRFS_TYPE_DIRECTORY
)
461 fr
->fcb
->fileref
= fr
;
463 fr
->fcb
->subvol
->parent
= fileref
->fcb
->subvol
->id
;
465 free_fileref(Vcb
, fr
);
467 // change fcb's INODE_ITEM
469 fcb
->inode_item
.transid
= Vcb
->superblock
.generation
;
470 fcb
->inode_item
.sequence
++;
471 fcb
->inode_item
.st_size
+= utf8
->Length
* 2;
473 if (!ccb
->user_set_change_time
)
474 fcb
->inode_item
.st_ctime
= now
;
476 if (!ccb
->user_set_write_time
)
477 fcb
->inode_item
.st_mtime
= now
;
479 fcb
->inode_item_changed
= TRUE
;
482 fcb
->subvol
->root_item
.ctime
= now
;
483 fcb
->subvol
->root_item
.ctransid
= Vcb
->superblock
.generation
;
485 send_notification_fileref(fr
, FILE_NOTIFY_CHANGE_DIR_NAME
, FILE_ACTION_ADDED
, NULL
);
486 send_notification_fileref(fr
->parent
, FILE_NOTIFY_CHANGE_LAST_WRITE
, FILE_ACTION_MODIFIED
, NULL
);
488 le
= subvol
->fcbs
.Flink
;
489 while (le
!= &subvol
->fcbs
) {
490 struct _fcb
* fcb2
= CONTAINING_RECORD(le
, struct _fcb
, list_entry
);
491 LIST_ENTRY
* le2
= fcb2
->extents
.Flink
;
493 while (le2
!= &fcb2
->extents
) {
494 extent
* ext
= CONTAINING_RECORD(le2
, extent
, list_entry
);
505 Status
= do_write(Vcb
, Irp
);
509 if (!NT_SUCCESS(Status
))
510 ERR("do_write returned %08x\n", Status
);
513 if (NT_SUCCESS(Status
))
514 clear_rollback(&rollback
);
516 do_rollback(Vcb
, &rollback
);
521 static NTSTATUS
create_snapshot(device_extension
* Vcb
, PFILE_OBJECT FileObject
, void* data
, ULONG length
, PIRP Irp
) {
522 PFILE_OBJECT subvol_obj
;
524 btrfs_create_snapshot
* bcs
= data
;
527 BOOL readonly
, posix
;
529 UNICODE_STRING nameus
;
533 file_ref
*fileref
, *fr2
;
536 if (IoIs32bitProcess(Irp
)) {
537 btrfs_create_snapshot32
* bcs32
= data
;
539 if (length
< offsetof(btrfs_create_snapshot32
, name
))
540 return STATUS_INVALID_PARAMETER
;
542 if (length
< offsetof(btrfs_create_snapshot32
, name
) + bcs32
->namelen
)
543 return STATUS_INVALID_PARAMETER
;
545 subvolh
= Handle32ToHandle(bcs32
->subvol
);
547 nameus
.Buffer
= bcs32
->name
;
548 nameus
.Length
= nameus
.MaximumLength
= bcs32
->namelen
;
550 readonly
= bcs32
->readonly
;
551 posix
= bcs32
->posix
;
554 if (length
< offsetof(btrfs_create_snapshot
, name
))
555 return STATUS_INVALID_PARAMETER
;
557 if (length
< offsetof(btrfs_create_snapshot
, name
) + bcs
->namelen
)
558 return STATUS_INVALID_PARAMETER
;
560 subvolh
= bcs
->subvol
;
562 nameus
.Buffer
= bcs
->name
;
563 nameus
.Length
= nameus
.MaximumLength
= bcs
->namelen
;
565 readonly
= bcs
->readonly
;
572 return STATUS_INVALID_PARAMETER
;
574 if (!FileObject
|| !FileObject
->FsContext
)
575 return STATUS_INVALID_PARAMETER
;
577 fcb
= FileObject
->FsContext
;
578 ccb
= FileObject
->FsContext2
;
580 if (!fcb
|| !ccb
|| fcb
->type
!= BTRFS_TYPE_DIRECTORY
)
581 return STATUS_INVALID_PARAMETER
;
583 fileref
= ccb
->fileref
;
586 ERR("fileref was NULL\n");
587 return STATUS_INVALID_PARAMETER
;
590 if (!(ccb
->access
& FILE_ADD_SUBDIRECTORY
)) {
591 WARN("insufficient privileges\n");
592 return STATUS_ACCESS_DENIED
;
596 return STATUS_MEDIA_WRITE_PROTECTED
;
598 if (is_subvol_readonly(fcb
->subvol
, Irp
))
599 return STATUS_ACCESS_DENIED
;
601 if (!is_file_name_valid(&nameus
, posix
))
602 return STATUS_OBJECT_NAME_INVALID
;
606 Status
= RtlUnicodeToUTF8N(NULL
, 0, &len
, nameus
.Buffer
, nameus
.Length
);
607 if (!NT_SUCCESS(Status
)) {
608 ERR("RtlUnicodeToUTF8N failed with error %08x\n", Status
);
613 ERR("RtlUnicodeToUTF8N returned a length of 0\n");
614 return STATUS_INTERNAL_ERROR
;
618 ERR("len was too long\n");
619 return STATUS_INVALID_PARAMETER
;
622 utf8
.MaximumLength
= utf8
.Length
= (USHORT
)len
;
623 utf8
.Buffer
= ExAllocatePoolWithTag(PagedPool
, utf8
.Length
, ALLOC_TAG
);
626 ERR("out of memory\n");
627 return STATUS_INSUFFICIENT_RESOURCES
;
630 Status
= RtlUnicodeToUTF8N(utf8
.Buffer
, len
, &len
, nameus
.Buffer
, nameus
.Length
);
631 if (!NT_SUCCESS(Status
)) {
632 ERR("RtlUnicodeToUTF8N failed with error %08x\n", Status
);
636 ExAcquireResourceExclusiveLite(&Vcb
->tree_lock
, TRUE
);
638 // no need for fcb_lock as we have tree_lock exclusively
639 Status
= open_fileref(fcb
->Vcb
, &fr2
, &nameus
, fileref
, FALSE
, NULL
, NULL
, PagedPool
, ccb
->case_sensitive
|| posix
, Irp
);
641 if (NT_SUCCESS(Status
)) {
643 WARN("file already exists\n");
644 free_fileref(Vcb
, fr2
);
645 Status
= STATUS_OBJECT_NAME_COLLISION
;
648 free_fileref(Vcb
, fr2
);
649 } else if (!NT_SUCCESS(Status
) && Status
!= STATUS_OBJECT_NAME_NOT_FOUND
) {
650 ERR("open_fileref returned %08x\n", Status
);
654 Status
= ObReferenceObjectByHandle(subvolh
, 0, *IoFileObjectType
, Irp
->RequestorMode
, (void**)&subvol_obj
, NULL
);
655 if (!NT_SUCCESS(Status
)) {
656 ERR("ObReferenceObjectByHandle returned %08x\n", Status
);
660 if (subvol_obj
->DeviceObject
!= FileObject
->DeviceObject
) {
661 Status
= STATUS_INVALID_PARAMETER
;
665 subvol_fcb
= subvol_obj
->FsContext
;
667 Status
= STATUS_INVALID_PARAMETER
;
671 if (subvol_fcb
->inode
!= subvol_fcb
->subvol
->root_item
.objid
) {
672 WARN("handle inode was %llx, expected %llx\n", subvol_fcb
->inode
, subvol_fcb
->subvol
->root_item
.objid
);
673 Status
= STATUS_INVALID_PARAMETER
;
677 ccb
= subvol_obj
->FsContext2
;
680 Status
= STATUS_INVALID_PARAMETER
;
684 if (!(ccb
->access
& FILE_TRAVERSE
)) {
685 WARN("insufficient privileges\n");
686 Status
= STATUS_ACCESS_DENIED
;
690 if (fcb
== Vcb
->dummy_fcb
) {
691 Status
= STATUS_ACCESS_DENIED
;
695 // clear unique flag on extents of open files in subvol
696 if (!IsListEmpty(&subvol_fcb
->subvol
->fcbs
)) {
697 LIST_ENTRY
* le
= subvol_fcb
->subvol
->fcbs
.Flink
;
699 while (le
!= &subvol_fcb
->subvol
->fcbs
) {
700 struct _fcb
* openfcb
= CONTAINING_RECORD(le
, struct _fcb
, list_entry
);
703 le2
= openfcb
->extents
.Flink
;
705 while (le2
!= &openfcb
->extents
) {
706 extent
* ext
= CONTAINING_RECORD(le2
, extent
, list_entry
);
717 Status
= do_create_snapshot(Vcb
, FileObject
, subvol_fcb
, &utf8
, &nameus
, readonly
, Irp
);
719 if (NT_SUCCESS(Status
)) {
722 Status
= open_fileref(Vcb
, &fr
, &nameus
, fileref
, FALSE
, NULL
, NULL
, PagedPool
, FALSE
, Irp
);
724 if (!NT_SUCCESS(Status
)) {
725 ERR("open_fileref returned %08x\n", Status
);
726 Status
= STATUS_SUCCESS
;
728 send_notification_fileref(fr
, FILE_NOTIFY_CHANGE_DIR_NAME
, FILE_ACTION_ADDED
, NULL
);
729 free_fileref(Vcb
, fr
);
734 ObDereferenceObject(subvol_obj
);
737 ExReleaseResourceLite(&Vcb
->tree_lock
);
740 ExFreePool(utf8
.Buffer
);
745 static NTSTATUS
create_subvol(device_extension
* Vcb
, PFILE_OBJECT FileObject
, void* data
, ULONG datalen
, PIRP Irp
) {
746 btrfs_create_subvol
* bcs
;
747 fcb
*fcb
, *rootfcb
= NULL
;
757 UNICODE_STRING nameus
;
762 SECURITY_SUBJECT_CONTEXT subjcont
;
766 file_ref
*fr
= NULL
, *fr2
;
767 dir_child
* dc
= NULL
;
769 fcb
= FileObject
->FsContext
;
771 ERR("error - fcb was NULL\n");
772 return STATUS_INTERNAL_ERROR
;
775 ccb
= FileObject
->FsContext2
;
777 ERR("error - ccb was NULL\n");
778 return STATUS_INTERNAL_ERROR
;
781 fileref
= ccb
->fileref
;
783 if (fcb
->type
!= BTRFS_TYPE_DIRECTORY
) {
784 ERR("parent FCB was not a directory\n");
785 return STATUS_NOT_A_DIRECTORY
;
789 ERR("fileref was NULL\n");
790 return STATUS_INVALID_PARAMETER
;
793 if (fileref
->deleted
|| fcb
->deleted
) {
794 ERR("parent has been deleted\n");
795 return STATUS_FILE_DELETED
;
798 if (!(ccb
->access
& FILE_ADD_SUBDIRECTORY
)) {
799 WARN("insufficient privileges\n");
800 return STATUS_ACCESS_DENIED
;
804 return STATUS_MEDIA_WRITE_PROTECTED
;
806 if (is_subvol_readonly(fcb
->subvol
, Irp
))
807 return STATUS_ACCESS_DENIED
;
809 if (fcb
== Vcb
->dummy_fcb
)
810 return STATUS_ACCESS_DENIED
;
812 if (!data
|| datalen
< sizeof(btrfs_create_subvol
))
813 return STATUS_INVALID_PARAMETER
;
815 bcs
= (btrfs_create_subvol
*)data
;
817 if (offsetof(btrfs_create_subvol
, name
[0]) + bcs
->namelen
> datalen
)
818 return STATUS_INVALID_PARAMETER
;
820 nameus
.Length
= nameus
.MaximumLength
= bcs
->namelen
;
821 nameus
.Buffer
= bcs
->name
;
823 if (!is_file_name_valid(&nameus
, bcs
->posix
))
824 return STATUS_OBJECT_NAME_INVALID
;
828 Status
= RtlUnicodeToUTF8N(NULL
, 0, &len
, nameus
.Buffer
, nameus
.Length
);
829 if (!NT_SUCCESS(Status
)) {
830 ERR("RtlUnicodeToUTF8N failed with error %08x\n", Status
);
835 ERR("RtlUnicodeToUTF8N returned a length of 0\n");
836 return STATUS_INTERNAL_ERROR
;
840 ERR("len was too long\n");
841 return STATUS_INVALID_PARAMETER
;
844 utf8
.MaximumLength
= utf8
.Length
= (USHORT
)len
;
845 utf8
.Buffer
= ExAllocatePoolWithTag(PagedPool
, utf8
.Length
, ALLOC_TAG
);
848 ERR("out of memory\n");
849 return STATUS_INSUFFICIENT_RESOURCES
;
852 Status
= RtlUnicodeToUTF8N(utf8
.Buffer
, len
, &len
, nameus
.Buffer
, nameus
.Length
);
853 if (!NT_SUCCESS(Status
)) {
854 ERR("RtlUnicodeToUTF8N failed with error %08x\n", Status
);
858 ExAcquireResourceExclusiveLite(&Vcb
->tree_lock
, TRUE
);
860 KeQuerySystemTime(&time
);
861 win_time_to_unix(time
, &now
);
863 // no need for fcb_lock as we have tree_lock exclusively
864 Status
= open_fileref(fcb
->Vcb
, &fr2
, &nameus
, fileref
, FALSE
, NULL
, NULL
, PagedPool
, ccb
->case_sensitive
|| bcs
->posix
, Irp
);
866 if (NT_SUCCESS(Status
)) {
868 WARN("file already exists\n");
869 free_fileref(Vcb
, fr2
);
870 Status
= STATUS_OBJECT_NAME_COLLISION
;
873 free_fileref(Vcb
, fr2
);
874 } else if (!NT_SUCCESS(Status
) && Status
!= STATUS_OBJECT_NAME_NOT_FOUND
) {
875 ERR("open_fileref returned %08x\n", Status
);
879 id
= InterlockedIncrement64(&Vcb
->root_root
->lastinode
);
880 Status
= create_root(Vcb
, id
, &r
, FALSE
, 0, Irp
);
882 if (!NT_SUCCESS(Status
)) {
883 ERR("create_root returned %08x\n", Status
);
887 TRACE("created root %llx\n", id
);
889 if (!Vcb
->uuid_root
) {
892 TRACE("uuid root doesn't exist, creating it\n");
894 Status
= create_root(Vcb
, BTRFS_ROOT_UUID
, &uuid_root
, FALSE
, 0, Irp
);
896 if (!NT_SUCCESS(Status
)) {
897 ERR("create_root returned %08x\n", Status
);
901 Vcb
->uuid_root
= uuid_root
;
904 root_num
= ExAllocatePoolWithTag(PagedPool
, sizeof(UINT64
), ALLOC_TAG
);
906 ERR("out of memory\n");
907 Status
= STATUS_INSUFFICIENT_RESOURCES
;
914 get_uuid(&r
->root_item
.uuid
);
916 RtlCopyMemory(&searchkey
.obj_id
, &r
->root_item
.uuid
, sizeof(UINT64
));
917 searchkey
.obj_type
= TYPE_SUBVOL_UUID
;
918 RtlCopyMemory(&searchkey
.offset
, &r
->root_item
.uuid
.uuid
[sizeof(UINT64
)], sizeof(UINT64
));
920 Status
= find_item(Vcb
, Vcb
->uuid_root
, &tp
, &searchkey
, FALSE
, Irp
);
921 } while (NT_SUCCESS(Status
) && !keycmp(searchkey
, tp
.item
->key
));
925 Status
= insert_tree_item(Vcb
, Vcb
->uuid_root
, searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
, root_num
, sizeof(UINT64
), NULL
, Irp
);
926 if (!NT_SUCCESS(Status
)) {
927 ERR("insert_tree_item returned %08x\n", Status
);
928 ExFreePool(root_num
);
932 r
->root_item
.inode
.generation
= 1;
933 r
->root_item
.inode
.st_size
= 3;
934 r
->root_item
.inode
.st_blocks
= Vcb
->superblock
.node_size
;
935 r
->root_item
.inode
.st_nlink
= 1;
936 r
->root_item
.inode
.st_mode
= __S_IFDIR
| S_IRUSR
| S_IWUSR
| S_IXUSR
| S_IRGRP
| S_IXGRP
| S_IROTH
| S_IXOTH
; // 40755
937 r
->root_item
.inode
.flags
= 0xffffffff80000000; // FIXME - find out what these mean
940 r
->root_item
.flags
|= BTRFS_SUBVOL_READONLY
;
942 r
->root_item
.objid
= SUBVOL_ROOT_INODE
;
943 r
->root_item
.bytes_used
= Vcb
->superblock
.node_size
;
944 r
->root_item
.ctransid
= Vcb
->superblock
.generation
;
945 r
->root_item
.otransid
= Vcb
->superblock
.generation
;
946 r
->root_item
.ctime
= now
;
947 r
->root_item
.otime
= now
;
949 // add .. inode to new subvol
951 rootfcb
= create_fcb(Vcb
, PagedPool
);
953 ERR("out of memory\n");
954 Status
= STATUS_INSUFFICIENT_RESOURCES
;
961 rootfcb
->inode
= SUBVOL_ROOT_INODE
;
962 rootfcb
->type
= BTRFS_TYPE_DIRECTORY
;
964 rootfcb
->inode_item
.generation
= Vcb
->superblock
.generation
;
965 rootfcb
->inode_item
.transid
= Vcb
->superblock
.generation
;
966 rootfcb
->inode_item
.st_nlink
= 1;
967 rootfcb
->inode_item
.st_mode
= __S_IFDIR
| inherit_mode(fileref
->fcb
, TRUE
);
968 rootfcb
->inode_item
.st_atime
= rootfcb
->inode_item
.st_ctime
= rootfcb
->inode_item
.st_mtime
= rootfcb
->inode_item
.otime
= now
;
969 rootfcb
->inode_item
.st_gid
= GID_NOBODY
;
971 rootfcb
->atts
= get_file_attributes(Vcb
, rootfcb
->subvol
, rootfcb
->inode
, rootfcb
->type
, FALSE
, TRUE
, Irp
);
973 if (r
->root_item
.flags
& BTRFS_SUBVOL_READONLY
)
974 rootfcb
->atts
|= FILE_ATTRIBUTE_READONLY
;
976 SeCaptureSubjectContext(&subjcont
);
978 Status
= SeAssignSecurity(fcb
->sd
, NULL
, (void**)&rootfcb
->sd
, TRUE
, &subjcont
, IoGetFileObjectGenericMapping(), PagedPool
);
980 if (!NT_SUCCESS(Status
)) {
981 ERR("SeAssignSecurity returned %08x\n", Status
);
986 ERR("SeAssignSecurity returned NULL security descriptor\n");
987 Status
= STATUS_INTERNAL_ERROR
;
991 Status
= RtlGetOwnerSecurityDescriptor(rootfcb
->sd
, &owner
, &defaulted
);
992 if (!NT_SUCCESS(Status
)) {
993 ERR("RtlGetOwnerSecurityDescriptor returned %08x\n", Status
);
994 rootfcb
->inode_item
.st_uid
= UID_NOBODY
;
995 rootfcb
->sd_dirty
= TRUE
;
997 rootfcb
->inode_item
.st_uid
= sid_to_uid(owner
);
998 rootfcb
->sd_dirty
= rootfcb
->inode_item
.st_uid
== UID_NOBODY
;
1001 find_gid(rootfcb
, fileref
->fcb
, &subjcont
);
1003 rootfcb
->inode_item_changed
= TRUE
;
1005 acquire_fcb_lock_exclusive(Vcb
);
1006 InsertTailList(&r
->fcbs
, &rootfcb
->list_entry
);
1007 InsertTailList(&Vcb
->all_fcbs
, &rootfcb
->list_entry_all
);
1008 release_fcb_lock(Vcb
);
1010 rootfcb
->Header
.IsFastIoPossible
= fast_io_possible(rootfcb
);
1011 rootfcb
->Header
.AllocationSize
.QuadPart
= 0;
1012 rootfcb
->Header
.FileSize
.QuadPart
= 0;
1013 rootfcb
->Header
.ValidDataLength
.QuadPart
= 0;
1015 rootfcb
->created
= TRUE
;
1017 if (fileref
->fcb
->inode_item
.flags
& BTRFS_INODE_COMPRESS
)
1018 rootfcb
->inode_item
.flags
|= BTRFS_INODE_COMPRESS
;
1020 rootfcb
->prop_compression
= fileref
->fcb
->prop_compression
;
1021 rootfcb
->prop_compression_changed
= rootfcb
->prop_compression
!= PropCompression_None
;
1023 r
->lastinode
= rootfcb
->inode
;
1027 irsize
= (UINT16
)(offsetof(INODE_REF
, name
[0]) + strlen(DOTDOT
));
1028 ir
= ExAllocatePoolWithTag(PagedPool
, irsize
, ALLOC_TAG
);
1030 ERR("out of memory\n");
1031 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1036 ir
->n
= (USHORT
)strlen(DOTDOT
);
1037 RtlCopyMemory(ir
->name
, DOTDOT
, ir
->n
);
1039 Status
= insert_tree_item(Vcb
, r
, r
->root_item
.objid
, TYPE_INODE_REF
, r
->root_item
.objid
, ir
, irsize
, NULL
, Irp
);
1040 if (!NT_SUCCESS(Status
)) {
1041 ERR("insert_tree_item returned %08x\n", Status
);
1046 // create fileref for entry in other subvolume
1048 fr
= create_fileref(Vcb
);
1050 ERR("out of memory\n");
1052 acquire_fcb_lock_exclusive(Vcb
);
1053 free_fcb(Vcb
, rootfcb
);
1054 release_fcb_lock(Vcb
);
1056 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1062 mark_fcb_dirty(rootfcb
);
1064 fr
->parent
= fileref
;
1066 Status
= add_dir_child(fileref
->fcb
, r
->id
, TRUE
, &utf8
, &nameus
, BTRFS_TYPE_DIRECTORY
, &dc
);
1067 if (!NT_SUCCESS(Status
))
1068 WARN("add_dir_child returned %08x\n", Status
);
1073 fr
->fcb
->hash_ptrs
= ExAllocatePoolWithTag(PagedPool
, sizeof(LIST_ENTRY
*) * 256, ALLOC_TAG
);
1074 if (!fr
->fcb
->hash_ptrs
) {
1075 ERR("out of memory\n");
1076 acquire_fcb_lock_exclusive(Vcb
);
1077 free_fileref(Vcb
, fr
);
1078 release_fcb_lock(Vcb
);
1079 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1083 RtlZeroMemory(fr
->fcb
->hash_ptrs
, sizeof(LIST_ENTRY
*) * 256);
1085 fr
->fcb
->hash_ptrs_uc
= ExAllocatePoolWithTag(PagedPool
, sizeof(LIST_ENTRY
*) * 256, ALLOC_TAG
);
1086 if (!fr
->fcb
->hash_ptrs_uc
) {
1087 ERR("out of memory\n");
1088 acquire_fcb_lock_exclusive(Vcb
);
1089 free_fileref(Vcb
, fr
);
1090 release_fcb_lock(Vcb
);
1091 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1095 RtlZeroMemory(fr
->fcb
->hash_ptrs_uc
, sizeof(LIST_ENTRY
*) * 256);
1097 ExAcquireResourceExclusiveLite(&fileref
->nonpaged
->children_lock
, TRUE
);
1098 InsertTailList(&fileref
->children
, &fr
->list_entry
);
1099 ExReleaseResourceLite(&fileref
->nonpaged
->children_lock
);
1101 increase_fileref_refcount(fileref
);
1103 if (fr
->fcb
->type
== BTRFS_TYPE_DIRECTORY
)
1104 fr
->fcb
->fileref
= fr
;
1107 mark_fileref_dirty(fr
);
1109 // change fcb->subvol's ROOT_ITEM
1111 fcb
->subvol
->root_item
.ctransid
= Vcb
->superblock
.generation
;
1112 fcb
->subvol
->root_item
.ctime
= now
;
1114 // change fcb's INODE_ITEM
1116 fcb
->inode_item
.transid
= Vcb
->superblock
.generation
;
1117 fcb
->inode_item
.st_size
+= utf8
.Length
* 2;
1118 fcb
->inode_item
.sequence
++;
1120 if (!ccb
->user_set_change_time
)
1121 fcb
->inode_item
.st_ctime
= now
;
1123 if (!ccb
->user_set_write_time
)
1124 fcb
->inode_item
.st_mtime
= now
;
1126 fcb
->inode_item_changed
= TRUE
;
1127 mark_fcb_dirty(fcb
);
1129 fr
->fcb
->subvol
->parent
= fcb
->subvol
->id
;
1131 Status
= STATUS_SUCCESS
;
1134 if (!NT_SUCCESS(Status
)) {
1137 mark_fileref_dirty(fr
);
1138 } else if (rootfcb
) {
1139 rootfcb
->deleted
= TRUE
;
1140 mark_fcb_dirty(rootfcb
);
1144 RemoveEntryList(&r
->list_entry
);
1145 InsertTailList(&Vcb
->drop_roots
, &r
->list_entry
);
1149 ExReleaseResourceLite(&Vcb
->tree_lock
);
1151 if (NT_SUCCESS(Status
)) {
1152 send_notification_fileref(fr
, FILE_NOTIFY_CHANGE_DIR_NAME
, FILE_ACTION_ADDED
, NULL
);
1153 send_notification_fileref(fr
->parent
, FILE_NOTIFY_CHANGE_LAST_WRITE
, FILE_ACTION_MODIFIED
, NULL
);
1158 acquire_fcb_lock_exclusive(Vcb
);
1159 free_fileref(Vcb
, fr
);
1160 release_fcb_lock(Vcb
);
1166 static NTSTATUS
get_inode_info(PFILE_OBJECT FileObject
, void* data
, ULONG length
) {
1167 btrfs_inode_info
* bii
= data
;
1171 if (length
< sizeof(btrfs_inode_info
))
1172 return STATUS_BUFFER_OVERFLOW
;
1175 return STATUS_INVALID_PARAMETER
;
1177 fcb
= FileObject
->FsContext
;
1180 return STATUS_INVALID_PARAMETER
;
1182 ccb
= FileObject
->FsContext2
;
1185 return STATUS_INVALID_PARAMETER
;
1187 if (!(ccb
->access
& FILE_READ_ATTRIBUTES
)) {
1188 WARN("insufficient privileges\n");
1189 return STATUS_ACCESS_DENIED
;
1193 fcb
= ccb
->fileref
->parent
->fcb
;
1195 ExAcquireResourceSharedLite(fcb
->Header
.Resource
, TRUE
);
1197 bii
->subvol
= fcb
->subvol
->id
;
1198 bii
->inode
= fcb
->inode
;
1199 bii
->top
= fcb
->Vcb
->root_fileref
->fcb
== fcb
? TRUE
: FALSE
;
1200 bii
->type
= fcb
->type
;
1201 bii
->st_uid
= fcb
->inode_item
.st_uid
;
1202 bii
->st_gid
= fcb
->inode_item
.st_gid
;
1203 bii
->st_mode
= fcb
->inode_item
.st_mode
;
1205 if (fcb
->inode_item
.st_rdev
== 0)
1208 bii
->st_rdev
= makedev((fcb
->inode_item
.st_rdev
& 0xFFFFFFFFFFF) >> 20, fcb
->inode_item
.st_rdev
& 0xFFFFF);
1210 bii
->flags
= fcb
->inode_item
.flags
;
1212 bii
->inline_length
= 0;
1213 bii
->disk_size
[0] = 0;
1214 bii
->disk_size
[1] = 0;
1215 bii
->disk_size
[2] = 0;
1217 if (fcb
->type
!= BTRFS_TYPE_DIRECTORY
) {
1220 le
= fcb
->extents
.Flink
;
1221 while (le
!= &fcb
->extents
) {
1222 extent
* ext
= CONTAINING_RECORD(le
, extent
, list_entry
);
1225 if (ext
->extent_data
.type
== EXTENT_TYPE_INLINE
) {
1226 bii
->inline_length
+= ext
->datalen
- (UINT16
)offsetof(EXTENT_DATA
, data
[0]);
1228 EXTENT_DATA2
* ed2
= (EXTENT_DATA2
*)ext
->extent_data
.data
;
1230 // FIXME - compressed extents with a hole in them are counted more than once
1231 if (ed2
->size
!= 0) {
1232 if (ext
->extent_data
.compression
== BTRFS_COMPRESSION_NONE
) {
1233 bii
->disk_size
[0] += ed2
->num_bytes
;
1234 } else if (ext
->extent_data
.compression
== BTRFS_COMPRESSION_ZLIB
) {
1235 bii
->disk_size
[1] += ed2
->size
;
1236 } else if (ext
->extent_data
.compression
== BTRFS_COMPRESSION_LZO
) {
1237 bii
->disk_size
[2] += ed2
->size
;
1247 switch (fcb
->prop_compression
) {
1248 case PropCompression_Zlib
:
1249 bii
->compression_type
= BTRFS_COMPRESSION_ZLIB
;
1252 case PropCompression_LZO
:
1253 bii
->compression_type
= BTRFS_COMPRESSION_LZO
;
1257 bii
->compression_type
= BTRFS_COMPRESSION_ANY
;
1261 ExReleaseResourceLite(fcb
->Header
.Resource
);
1263 return STATUS_SUCCESS
;
1266 static NTSTATUS
set_inode_info(PFILE_OBJECT FileObject
, void* data
, ULONG length
, PIRP Irp
) {
1267 btrfs_set_inode_info
* bsii
= data
;
1272 if (length
< sizeof(btrfs_set_inode_info
))
1273 return STATUS_INVALID_PARAMETER
;
1276 return STATUS_INVALID_PARAMETER
;
1278 fcb
= FileObject
->FsContext
;
1281 return STATUS_INVALID_PARAMETER
;
1283 ccb
= FileObject
->FsContext2
;
1286 return STATUS_INVALID_PARAMETER
;
1288 if (bsii
->flags_changed
&& !(ccb
->access
& FILE_WRITE_ATTRIBUTES
)) {
1289 WARN("insufficient privileges\n");
1290 return STATUS_ACCESS_DENIED
;
1293 if ((bsii
->mode_changed
|| bsii
->uid_changed
|| bsii
->gid_changed
) && !(ccb
->access
& WRITE_DAC
)) {
1294 WARN("insufficient privileges\n");
1295 return STATUS_ACCESS_DENIED
;
1298 if (bsii
->compression_type_changed
&& bsii
->compression_type
> BTRFS_COMPRESSION_LZO
)
1299 return STATUS_INVALID_PARAMETER
;
1302 fcb
= ccb
->fileref
->parent
->fcb
;
1304 if (is_subvol_readonly(fcb
->subvol
, Irp
)) {
1305 WARN("trying to change inode on readonly subvolume\n");
1306 return STATUS_ACCESS_DENIED
;
1309 ExAcquireResourceExclusiveLite(fcb
->Header
.Resource
, TRUE
);
1311 if (bsii
->flags_changed
) {
1312 if (fcb
->type
!= BTRFS_TYPE_DIRECTORY
&& fcb
->inode_item
.st_size
> 0 &&
1313 (bsii
->flags
& BTRFS_INODE_NODATACOW
) != (fcb
->inode_item
.flags
& BTRFS_INODE_NODATACOW
)) {
1314 WARN("trying to change nocow flag on non-empty file\n");
1315 Status
= STATUS_INVALID_PARAMETER
;
1319 fcb
->inode_item
.flags
= bsii
->flags
;
1321 if (fcb
->inode_item
.flags
& BTRFS_INODE_NODATACOW
)
1322 fcb
->inode_item
.flags
|= BTRFS_INODE_NODATASUM
;
1324 fcb
->inode_item
.flags
&= ~(UINT64
)BTRFS_INODE_NODATASUM
;
1327 if (bsii
->mode_changed
) {
1328 UINT32 allowed
= S_IRUSR
| S_IWUSR
| S_IXUSR
| S_IRGRP
| S_IWGRP
| S_IXGRP
| S_IROTH
| S_IWOTH
| S_IXOTH
|
1331 if (ccb
->access
& WRITE_OWNER
)
1334 fcb
->inode_item
.st_mode
&= ~allowed
;
1335 fcb
->inode_item
.st_mode
|= bsii
->st_mode
& allowed
;
1338 if (bsii
->uid_changed
&& fcb
->inode_item
.st_uid
!= bsii
->st_uid
) {
1339 fcb
->inode_item
.st_uid
= bsii
->st_uid
;
1341 fcb
->sd_dirty
= TRUE
;
1342 fcb
->sd_deleted
= FALSE
;
1345 if (bsii
->gid_changed
)
1346 fcb
->inode_item
.st_gid
= bsii
->st_gid
;
1348 if (bsii
->compression_type_changed
) {
1349 switch (bsii
->compression_type
) {
1350 case BTRFS_COMPRESSION_ANY
:
1351 fcb
->prop_compression
= PropCompression_None
;
1354 case BTRFS_COMPRESSION_ZLIB
:
1355 fcb
->prop_compression
= PropCompression_Zlib
;
1358 case BTRFS_COMPRESSION_LZO
:
1359 fcb
->prop_compression
= PropCompression_LZO
;
1363 fcb
->prop_compression_changed
= TRUE
;
1366 if (bsii
->flags_changed
|| bsii
->mode_changed
|| bsii
->uid_changed
|| bsii
->gid_changed
|| bsii
->compression_type_changed
) {
1367 fcb
->inode_item_changed
= TRUE
;
1368 mark_fcb_dirty(fcb
);
1371 Status
= STATUS_SUCCESS
;
1374 ExReleaseResourceLite(fcb
->Header
.Resource
);
1379 static NTSTATUS
get_devices(device_extension
* Vcb
, void* data
, ULONG length
) {
1380 btrfs_device
* dev
= NULL
;
1384 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, TRUE
);
1386 le
= Vcb
->devices
.Flink
;
1387 while (le
!= &Vcb
->devices
) {
1388 device
* dev2
= CONTAINING_RECORD(le
, device
, list_entry
);
1391 if (length
< sizeof(btrfs_device
) - sizeof(WCHAR
)) {
1392 Status
= STATUS_BUFFER_OVERFLOW
;
1399 dev
->next_entry
= sizeof(btrfs_device
) - sizeof(WCHAR
) + dev
->namelen
;
1400 dev
= (btrfs_device
*)((UINT8
*)dev
+ dev
->next_entry
);
1403 structlen
= length
- offsetof(btrfs_device
, namelen
);
1406 Status
= dev_ioctl(dev2
->devobj
, IOCTL_MOUNTDEV_QUERY_DEVICE_NAME
, NULL
, 0, &dev
->namelen
, structlen
, TRUE
, NULL
);
1407 if (!NT_SUCCESS(Status
))
1410 dev
->missing
= FALSE
;
1413 dev
->missing
= TRUE
;
1416 dev
->next_entry
= 0;
1417 dev
->dev_id
= dev2
->devitem
.dev_id
;
1418 dev
->readonly
= (Vcb
->readonly
|| dev2
->readonly
) ? TRUE
: FALSE
;
1419 dev
->device_number
= dev2
->disk_num
;
1420 dev
->partition_number
= dev2
->part_num
;
1421 dev
->size
= dev2
->devitem
.num_bytes
;
1424 GET_LENGTH_INFORMATION gli
;
1426 Status
= dev_ioctl(dev2
->devobj
, IOCTL_DISK_GET_LENGTH_INFO
, NULL
, 0, &gli
, sizeof(gli
), TRUE
, NULL
);
1427 if (!NT_SUCCESS(Status
))
1430 dev
->max_size
= gli
.Length
.QuadPart
;
1432 dev
->max_size
= dev
->size
;
1434 RtlCopyMemory(dev
->stats
, dev2
->stats
, sizeof(UINT64
) * 5);
1436 length
-= sizeof(btrfs_device
) - sizeof(WCHAR
) + dev
->namelen
;
1442 ExReleaseResourceLite(&Vcb
->tree_lock
);
1447 static NTSTATUS
get_usage(device_extension
* Vcb
, void* data
, ULONG length
, PIRP Irp
) {
1448 btrfs_usage
* usage
= (btrfs_usage
*)data
;
1449 btrfs_usage
* lastbue
= NULL
;
1453 if (length
< sizeof(btrfs_usage
))
1454 return STATUS_BUFFER_OVERFLOW
;
1456 if (!Vcb
->chunk_usage_found
) {
1457 ExAcquireResourceExclusiveLite(&Vcb
->tree_lock
, TRUE
);
1459 if (!Vcb
->chunk_usage_found
)
1460 Status
= find_chunk_usage(Vcb
, Irp
);
1462 Status
= STATUS_SUCCESS
;
1464 ExReleaseResourceLite(&Vcb
->tree_lock
);
1466 if (!NT_SUCCESS(Status
)) {
1467 ERR("find_chunk_usage returned %08x\n", Status
);
1472 length
-= offsetof(btrfs_usage
, devices
);
1474 ExAcquireResourceSharedLite(&Vcb
->chunk_lock
, TRUE
);
1476 le
= Vcb
->chunks
.Flink
;
1477 while (le
!= &Vcb
->chunks
) {
1478 BOOL addnew
= FALSE
;
1480 chunk
* c
= CONTAINING_RECORD(le
, chunk
, list_entry
);
1482 if (!lastbue
) // first entry
1485 btrfs_usage
* bue
= usage
;
1490 if (bue
->type
== c
->chunk_item
->type
) {
1495 if (bue
->next_entry
== 0)
1498 bue
= (btrfs_usage
*)((UINT8
*)bue
+ bue
->next_entry
);
1510 if (length
< offsetof(btrfs_usage
, devices
)) {
1511 Status
= STATUS_BUFFER_OVERFLOW
;
1515 length
-= offsetof(btrfs_usage
, devices
);
1517 lastbue
->next_entry
= offsetof(btrfs_usage
, devices
) + (ULONG
)(lastbue
->num_devices
* sizeof(btrfs_usage_device
));
1519 bue
= (btrfs_usage
*)((UINT8
*)lastbue
+ lastbue
->next_entry
);
1522 bue
->next_entry
= 0;
1523 bue
->type
= c
->chunk_item
->type
;
1526 bue
->num_devices
= 0;
1528 if (c
->chunk_item
->type
& BLOCK_FLAG_RAID0
)
1529 factor
= c
->chunk_item
->num_stripes
;
1530 else if (c
->chunk_item
->type
& BLOCK_FLAG_RAID10
)
1531 factor
= c
->chunk_item
->num_stripes
/ c
->chunk_item
->sub_stripes
;
1532 else if (c
->chunk_item
->type
& BLOCK_FLAG_RAID5
)
1533 factor
= c
->chunk_item
->num_stripes
- 1;
1534 else if (c
->chunk_item
->type
& BLOCK_FLAG_RAID6
)
1535 factor
= c
->chunk_item
->num_stripes
- 2;
1540 while (le2
!= &Vcb
->chunks
) {
1541 chunk
* c2
= CONTAINING_RECORD(le2
, chunk
, list_entry
);
1543 if (c2
->chunk_item
->type
== c
->chunk_item
->type
) {
1545 CHUNK_ITEM_STRIPE
* cis
= (CHUNK_ITEM_STRIPE
*)&c2
->chunk_item
[1];
1548 bue
->size
+= c2
->chunk_item
->size
;
1549 bue
->used
+= c2
->used
;
1551 stripesize
= c2
->chunk_item
->size
/ factor
;
1553 for (i
= 0; i
< c2
->chunk_item
->num_stripes
; i
++) {
1557 for (j
= 0; j
< bue
->num_devices
; j
++) {
1558 if (bue
->devices
[j
].dev_id
== cis
[i
].dev_id
) {
1559 bue
->devices
[j
].alloc
+= stripesize
;
1566 if (length
< sizeof(btrfs_usage_device
)) {
1567 Status
= STATUS_BUFFER_OVERFLOW
;
1571 length
-= sizeof(btrfs_usage_device
);
1573 bue
->devices
[bue
->num_devices
].dev_id
= cis
[i
].dev_id
;
1574 bue
->devices
[bue
->num_devices
].alloc
= stripesize
;
1589 Status
= STATUS_SUCCESS
;
1592 ExReleaseResourceLite(&Vcb
->chunk_lock
);
1597 static NTSTATUS
is_volume_mounted(device_extension
* Vcb
, PIRP Irp
) {
1600 IO_STATUS_BLOCK iosb
;
1601 BOOL verify
= FALSE
;
1604 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, TRUE
);
1606 le
= Vcb
->devices
.Flink
;
1607 while (le
!= &Vcb
->devices
) {
1608 device
* dev
= CONTAINING_RECORD(le
, device
, list_entry
);
1610 if (dev
->devobj
&& dev
->removable
) {
1611 Status
= dev_ioctl(dev
->devobj
, IOCTL_STORAGE_CHECK_VERIFY
, NULL
, 0, &cc
, sizeof(ULONG
), FALSE
, &iosb
);
1613 if (iosb
.Information
!= sizeof(ULONG
))
1616 if (Status
== STATUS_VERIFY_REQUIRED
|| (NT_SUCCESS(Status
) && cc
!= dev
->change_count
)) {
1617 dev
->devobj
->Flags
|= DO_VERIFY_VOLUME
;
1621 if (NT_SUCCESS(Status
) && iosb
.Information
== sizeof(ULONG
))
1622 dev
->change_count
= cc
;
1624 if (!NT_SUCCESS(Status
) || verify
) {
1625 IoSetHardErrorOrVerifyDevice(Irp
, dev
->devobj
);
1626 ExReleaseResourceLite(&Vcb
->tree_lock
);
1628 return verify
? STATUS_VERIFY_REQUIRED
: Status
;
1635 ExReleaseResourceLite(&Vcb
->tree_lock
);
1637 return STATUS_SUCCESS
;
1640 static NTSTATUS
fs_get_statistics(void* buffer
, DWORD buflen
, ULONG_PTR
* retlen
) {
1641 FILESYSTEM_STATISTICS
* fss
;
1643 WARN("STUB: FSCTL_FILESYSTEM_GET_STATISTICS\n");
1645 // This is hideously wrong, but at least it stops SMB from breaking
1647 if (buflen
< sizeof(FILESYSTEM_STATISTICS
))
1648 return STATUS_BUFFER_TOO_SMALL
;
1651 RtlZeroMemory(fss
, sizeof(FILESYSTEM_STATISTICS
));
1654 fss
->FileSystemType
= FILESYSTEM_STATISTICS_TYPE_NTFS
;
1655 fss
->SizeOfCompleteStructure
= sizeof(FILESYSTEM_STATISTICS
);
1657 *retlen
= sizeof(FILESYSTEM_STATISTICS
);
1659 return STATUS_SUCCESS
;
1662 static NTSTATUS
set_sparse(device_extension
* Vcb
, PFILE_OBJECT FileObject
, void* data
, ULONG length
, PIRP Irp
) {
1663 FILE_SET_SPARSE_BUFFER
* fssb
= data
;
1667 ccb
* ccb
= FileObject
->FsContext2
;
1668 file_ref
* fileref
= ccb
? ccb
->fileref
: NULL
;
1670 if (data
&& length
< sizeof(FILE_SET_SPARSE_BUFFER
))
1671 return STATUS_INVALID_PARAMETER
;
1674 ERR("FileObject was NULL\n");
1675 return STATUS_INVALID_PARAMETER
;
1678 fcb
= FileObject
->FsContext
;
1681 ERR("FCB was NULL\n");
1682 return STATUS_INVALID_PARAMETER
;
1686 ERR("CCB was NULL\n");
1687 return STATUS_INVALID_PARAMETER
;
1690 if (Irp
->RequestorMode
== UserMode
&& !(ccb
->access
& FILE_WRITE_ATTRIBUTES
)) {
1691 WARN("insufficient privileges\n");
1692 return STATUS_ACCESS_DENIED
;
1696 ERR("no fileref\n");
1697 return STATUS_INVALID_PARAMETER
;
1701 fileref
= fileref
->parent
;
1705 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, TRUE
);
1706 ExAcquireResourceExclusiveLite(fcb
->Header
.Resource
, TRUE
);
1708 if (fcb
->type
!= BTRFS_TYPE_FILE
) {
1709 WARN("FileObject did not point to a file\n");
1710 Status
= STATUS_INVALID_PARAMETER
;
1715 set
= fssb
->SetSparse
;
1720 fcb
->atts
|= FILE_ATTRIBUTE_SPARSE_FILE
;
1721 fcb
->atts_changed
= TRUE
;
1725 fcb
->atts
&= ~FILE_ATTRIBUTE_SPARSE_FILE
;
1726 fcb
->atts_changed
= TRUE
;
1728 defda
= get_file_attributes(Vcb
, fcb
->subvol
, fcb
->inode
, fcb
->type
,
1729 fileref
&& fileref
->dc
&& fileref
->dc
->name
.Length
>= sizeof(WCHAR
) && fileref
->dc
->name
.Buffer
[0] == '.', TRUE
, Irp
);
1731 fcb
->atts_deleted
= defda
== fcb
->atts
;
1734 mark_fcb_dirty(fcb
);
1735 send_notification_fcb(fileref
, FILE_NOTIFY_CHANGE_ATTRIBUTES
, FILE_ACTION_MODIFIED
, NULL
);
1737 Status
= STATUS_SUCCESS
;
1740 ExReleaseResourceLite(fcb
->Header
.Resource
);
1741 ExReleaseResourceLite(&Vcb
->tree_lock
);
1746 static NTSTATUS
zero_data(device_extension
* Vcb
, fcb
* fcb
, UINT64 start
, UINT64 length
, PIRP Irp
, LIST_ENTRY
* rollback
) {
1748 BOOL make_inline
, compress
;
1749 UINT64 start_data
, end_data
;
1753 make_inline
= fcb
->inode_item
.st_size
<= Vcb
->options
.max_inline
|| fcb_is_inline(fcb
);
1756 compress
= write_fcb_compressed(fcb
);
1760 end_data
= fcb
->inode_item
.st_size
;
1761 buf_head
= (ULONG
)offsetof(EXTENT_DATA
, data
[0]);
1762 } else if (compress
) {
1763 start_data
= start
& ~(UINT64
)(COMPRESSED_EXTENT_SIZE
- 1);
1764 end_data
= min(sector_align(start
+ length
, COMPRESSED_EXTENT_SIZE
),
1765 sector_align(fcb
->inode_item
.st_size
, Vcb
->superblock
.sector_size
));
1768 start_data
= start
& ~(UINT64
)(Vcb
->superblock
.sector_size
- 1);
1769 end_data
= sector_align(start
+ length
, Vcb
->superblock
.sector_size
);
1773 data
= ExAllocatePoolWithTag(PagedPool
, (ULONG
)(buf_head
+ end_data
- start_data
), ALLOC_TAG
);
1775 ERR("out of memory\n");
1776 return STATUS_INSUFFICIENT_RESOURCES
;
1779 RtlZeroMemory(data
+ buf_head
, (ULONG
)(end_data
- start_data
));
1781 if (start
> start_data
|| start
+ length
< end_data
) {
1782 Status
= read_file(fcb
, data
+ buf_head
, start_data
, end_data
- start_data
, NULL
, Irp
);
1784 if (!NT_SUCCESS(Status
)) {
1785 ERR("read_file returned %08x\n", Status
);
1791 RtlZeroMemory(data
+ buf_head
+ start
- start_data
, (ULONG
)length
);
1795 EXTENT_DATA
* ed
= (EXTENT_DATA
*)data
;
1797 Status
= excise_extents(Vcb
, fcb
, 0, sector_align(end_data
, Vcb
->superblock
.sector_size
), Irp
, rollback
);
1798 if (!NT_SUCCESS(Status
)) {
1799 ERR("excise_extents returned %08x\n", Status
);
1804 edsize
= (UINT16
)(offsetof(EXTENT_DATA
, data
[0]) + end_data
);
1806 ed
->generation
= Vcb
->superblock
.generation
;
1807 ed
->decoded_size
= end_data
;
1808 ed
->compression
= BTRFS_COMPRESSION_NONE
;
1809 ed
->encryption
= BTRFS_ENCRYPTION_NONE
;
1810 ed
->encoding
= BTRFS_ENCODING_NONE
;
1811 ed
->type
= EXTENT_TYPE_INLINE
;
1813 Status
= add_extent_to_fcb(fcb
, 0, ed
, edsize
, FALSE
, NULL
, rollback
);
1814 if (!NT_SUCCESS(Status
)) {
1815 ERR("add_extent_to_fcb returned %08x\n", Status
);
1822 fcb
->inode_item
.st_blocks
+= end_data
;
1823 } else if (compress
) {
1824 Status
= write_compressed(fcb
, start_data
, end_data
, data
, Irp
, rollback
);
1828 if (!NT_SUCCESS(Status
)) {
1829 ERR("write_compressed returned %08x\n", Status
);
1833 Status
= do_write_file(fcb
, start_data
, end_data
, data
, Irp
, FALSE
, 0, rollback
);
1837 if (!NT_SUCCESS(Status
)) {
1838 ERR("do_write_file returned %08x\n", Status
);
1843 return STATUS_SUCCESS
;
1846 static NTSTATUS
set_zero_data(device_extension
* Vcb
, PFILE_OBJECT FileObject
, void* data
, ULONG length
, PIRP Irp
) {
1847 FILE_ZERO_DATA_INFORMATION
* fzdi
= data
;
1852 LIST_ENTRY rollback
, *le
;
1857 IO_STATUS_BLOCK iosb
;
1859 if (!data
|| length
< sizeof(FILE_ZERO_DATA_INFORMATION
))
1860 return STATUS_INVALID_PARAMETER
;
1863 ERR("FileObject was NULL\n");
1864 return STATUS_INVALID_PARAMETER
;
1867 if (fzdi
->BeyondFinalZero
.QuadPart
<= fzdi
->FileOffset
.QuadPart
) {
1868 WARN("BeyondFinalZero was less than or equal to FileOffset (%llx <= %llx)\n", fzdi
->BeyondFinalZero
.QuadPart
, fzdi
->FileOffset
.QuadPart
);
1869 return STATUS_INVALID_PARAMETER
;
1872 fcb
= FileObject
->FsContext
;
1875 ERR("FCB was NULL\n");
1876 return STATUS_INVALID_PARAMETER
;
1879 ccb
= FileObject
->FsContext2
;
1882 ERR("ccb was NULL\n");
1883 return STATUS_INVALID_PARAMETER
;
1886 if (Irp
->RequestorMode
== UserMode
&& !(ccb
->access
& FILE_WRITE_DATA
)) {
1887 WARN("insufficient privileges\n");
1888 return STATUS_ACCESS_DENIED
;
1891 fileref
= ccb
->fileref
;
1894 ERR("fileref was NULL\n");
1895 return STATUS_INVALID_PARAMETER
;
1898 InitializeListHead(&rollback
);
1900 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, TRUE
);
1901 ExAcquireResourceExclusiveLite(fcb
->Header
.Resource
, TRUE
);
1903 CcFlushCache(&fcb
->nonpaged
->segment_object
, NULL
, 0, &iosb
);
1905 if (fcb
->type
!= BTRFS_TYPE_FILE
) {
1906 WARN("FileObject did not point to a file\n");
1907 Status
= STATUS_INVALID_PARAMETER
;
1912 ERR("FileObject is stream\n");
1913 Status
= STATUS_INVALID_PARAMETER
;
1917 if ((UINT64
)fzdi
->FileOffset
.QuadPart
>= fcb
->inode_item
.st_size
) {
1918 Status
= STATUS_SUCCESS
;
1923 le
= fcb
->extents
.Flink
;
1924 while (le
!= &fcb
->extents
) {
1925 extent
* ext2
= CONTAINING_RECORD(le
, extent
, list_entry
);
1927 if (!ext2
->ignore
) {
1936 Status
= STATUS_SUCCESS
;
1940 if (ext
->extent_data
.type
== EXTENT_TYPE_INLINE
) {
1941 Status
= zero_data(Vcb
, fcb
, fzdi
->FileOffset
.QuadPart
, fzdi
->BeyondFinalZero
.QuadPart
- fzdi
->FileOffset
.QuadPart
, Irp
, &rollback
);
1942 if (!NT_SUCCESS(Status
)) {
1943 ERR("zero_data returned %08x\n", Status
);
1947 start
= sector_align(fzdi
->FileOffset
.QuadPart
, Vcb
->superblock
.sector_size
);
1949 if ((UINT64
)fzdi
->BeyondFinalZero
.QuadPart
> fcb
->inode_item
.st_size
)
1950 end
= sector_align(fcb
->inode_item
.st_size
, Vcb
->superblock
.sector_size
);
1952 end
= (fzdi
->BeyondFinalZero
.QuadPart
/ Vcb
->superblock
.sector_size
) * Vcb
->superblock
.sector_size
;
1955 Status
= zero_data(Vcb
, fcb
, fzdi
->FileOffset
.QuadPart
, fzdi
->BeyondFinalZero
.QuadPart
- fzdi
->FileOffset
.QuadPart
, Irp
, &rollback
);
1956 if (!NT_SUCCESS(Status
)) {
1957 ERR("zero_data returned %08x\n", Status
);
1961 if (start
> (UINT64
)fzdi
->FileOffset
.QuadPart
) {
1962 Status
= zero_data(Vcb
, fcb
, fzdi
->FileOffset
.QuadPart
, start
- fzdi
->FileOffset
.QuadPart
, Irp
, &rollback
);
1963 if (!NT_SUCCESS(Status
)) {
1964 ERR("zero_data returned %08x\n", Status
);
1969 if (end
< (UINT64
)fzdi
->BeyondFinalZero
.QuadPart
) {
1970 Status
= zero_data(Vcb
, fcb
, end
, fzdi
->BeyondFinalZero
.QuadPart
- end
, Irp
, &rollback
);
1971 if (!NT_SUCCESS(Status
)) {
1972 ERR("zero_data returned %08x\n", Status
);
1978 Status
= excise_extents(Vcb
, fcb
, start
, end
, Irp
, &rollback
);
1979 if (!NT_SUCCESS(Status
)) {
1980 ERR("excise_extents returned %08x\n", Status
);
1987 CcPurgeCacheSection(&fcb
->nonpaged
->segment_object
, &fzdi
->FileOffset
, (ULONG
)(fzdi
->BeyondFinalZero
.QuadPart
- fzdi
->FileOffset
.QuadPart
), FALSE
);
1989 KeQuerySystemTime(&time
);
1990 win_time_to_unix(time
, &now
);
1992 fcb
->inode_item
.transid
= Vcb
->superblock
.generation
;
1993 fcb
->inode_item
.sequence
++;
1995 if (!ccb
->user_set_change_time
)
1996 fcb
->inode_item
.st_ctime
= now
;
1998 if (!ccb
->user_set_write_time
)
1999 fcb
->inode_item
.st_mtime
= now
;
2001 fcb
->extents_changed
= TRUE
;
2002 fcb
->inode_item_changed
= TRUE
;
2003 mark_fcb_dirty(fcb
);
2005 send_notification_fcb(fileref
, FILE_NOTIFY_CHANGE_LAST_WRITE
, FILE_ACTION_MODIFIED
, NULL
);
2007 fcb
->subvol
->root_item
.ctransid
= Vcb
->superblock
.generation
;
2008 fcb
->subvol
->root_item
.ctime
= now
;
2010 Status
= STATUS_SUCCESS
;
2013 if (!NT_SUCCESS(Status
))
2014 do_rollback(Vcb
, &rollback
);
2016 clear_rollback(&rollback
);
2018 ExReleaseResourceLite(fcb
->Header
.Resource
);
2019 ExReleaseResourceLite(&Vcb
->tree_lock
);
2024 static NTSTATUS
query_ranges(PFILE_OBJECT FileObject
, FILE_ALLOCATED_RANGE_BUFFER
* inbuf
, ULONG inbuflen
, void* outbuf
, ULONG outbuflen
, ULONG_PTR
* retlen
) {
2028 FILE_ALLOCATED_RANGE_BUFFER
* ranges
= outbuf
;
2030 UINT64 last_start
, last_end
;
2032 TRACE("FSCTL_QUERY_ALLOCATED_RANGES\n");
2035 ERR("FileObject was NULL\n");
2036 return STATUS_INVALID_PARAMETER
;
2039 if (!inbuf
|| inbuflen
< sizeof(FILE_ALLOCATED_RANGE_BUFFER
) || !outbuf
)
2040 return STATUS_INVALID_PARAMETER
;
2042 fcb
= FileObject
->FsContext
;
2045 ERR("FCB was NULL\n");
2046 return STATUS_INVALID_PARAMETER
;
2049 ExAcquireResourceSharedLite(fcb
->Header
.Resource
, TRUE
);
2051 // If file is not marked as sparse, claim the whole thing as an allocated range
2053 if (!(fcb
->atts
& FILE_ATTRIBUTE_SPARSE_FILE
)) {
2054 if (fcb
->inode_item
.st_size
== 0)
2055 Status
= STATUS_SUCCESS
;
2056 else if (outbuflen
< sizeof(FILE_ALLOCATED_RANGE_BUFFER
))
2057 Status
= STATUS_BUFFER_TOO_SMALL
;
2059 ranges
[i
].FileOffset
.QuadPart
= 0;
2060 ranges
[i
].Length
.QuadPart
= fcb
->inode_item
.st_size
;
2062 Status
= STATUS_SUCCESS
;
2069 le
= fcb
->extents
.Flink
;
2074 while (le
!= &fcb
->extents
) {
2075 extent
* ext
= CONTAINING_RECORD(le
, extent
, list_entry
);
2078 EXTENT_DATA2
* ed2
= (ext
->extent_data
.type
== EXTENT_TYPE_REGULAR
|| ext
->extent_data
.type
== EXTENT_TYPE_PREALLOC
) ? (EXTENT_DATA2
*)ext
->extent_data
.data
: NULL
;
2079 UINT64 len
= ed2
? ed2
->num_bytes
: ext
->extent_data
.decoded_size
;
2081 if (ext
->offset
> last_end
) { // first extent after a hole
2082 if (last_end
> last_start
) {
2083 if ((i
+ 1) * sizeof(FILE_ALLOCATED_RANGE_BUFFER
) <= outbuflen
) {
2084 ranges
[i
].FileOffset
.QuadPart
= last_start
;
2085 ranges
[i
].Length
.QuadPart
= min(fcb
->inode_item
.st_size
, last_end
) - last_start
;
2088 Status
= STATUS_BUFFER_TOO_SMALL
;
2093 last_start
= ext
->offset
;
2096 last_end
= ext
->offset
+ len
;
2102 if (last_end
> last_start
) {
2103 if ((i
+ 1) * sizeof(FILE_ALLOCATED_RANGE_BUFFER
) <= outbuflen
) {
2104 ranges
[i
].FileOffset
.QuadPart
= last_start
;
2105 ranges
[i
].Length
.QuadPart
= min(fcb
->inode_item
.st_size
, last_end
) - last_start
;
2108 Status
= STATUS_BUFFER_TOO_SMALL
;
2113 Status
= STATUS_SUCCESS
;
2116 *retlen
= i
* sizeof(FILE_ALLOCATED_RANGE_BUFFER
);
2118 ExReleaseResourceLite(fcb
->Header
.Resource
);
2123 static NTSTATUS
get_object_id(device_extension
* Vcb
, PFILE_OBJECT FileObject
, FILE_OBJECTID_BUFFER
* buf
, ULONG buflen
, ULONG_PTR
* retlen
) {
2126 TRACE("(%p, %p, %p, %x, %p)\n", Vcb
, FileObject
, buf
, buflen
, retlen
);
2129 ERR("FileObject was NULL\n");
2130 return STATUS_INVALID_PARAMETER
;
2133 if (!buf
|| buflen
< sizeof(FILE_OBJECTID_BUFFER
))
2134 return STATUS_INVALID_PARAMETER
;
2136 fcb
= FileObject
->FsContext
;
2139 ERR("FCB was NULL\n");
2140 return STATUS_INVALID_PARAMETER
;
2143 ExAcquireResourceSharedLite(fcb
->Header
.Resource
, TRUE
);
2145 RtlCopyMemory(&buf
->ObjectId
[0], &fcb
->inode
, sizeof(UINT64
));
2146 RtlCopyMemory(&buf
->ObjectId
[sizeof(UINT64
)], &fcb
->subvol
->id
, sizeof(UINT64
));
2148 ExReleaseResourceLite(fcb
->Header
.Resource
);
2150 RtlZeroMemory(&buf
->ExtendedInfo
, sizeof(buf
->ExtendedInfo
));
2152 *retlen
= sizeof(FILE_OBJECTID_BUFFER
);
2154 return STATUS_SUCCESS
;
2157 static void flush_fcb_caches(device_extension
* Vcb
) {
2160 le
= Vcb
->all_fcbs
.Flink
;
2161 while (le
!= &Vcb
->all_fcbs
) {
2162 struct _fcb
* fcb
= CONTAINING_RECORD(le
, struct _fcb
, list_entry_all
);
2163 IO_STATUS_BLOCK iosb
;
2165 if (fcb
->type
!= BTRFS_TYPE_DIRECTORY
&& !fcb
->deleted
)
2166 CcFlushCache(&fcb
->nonpaged
->segment_object
, NULL
, 0, &iosb
);
2172 static NTSTATUS
lock_volume(device_extension
* Vcb
, PIRP Irp
) {
2173 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
2176 BOOL lock_paused_balance
= FALSE
;
2178 TRACE("FSCTL_LOCK_VOLUME\n");
2180 if (Vcb
->scrub
.thread
) {
2181 WARN("cannot lock while scrub running\n");
2182 return STATUS_DEVICE_NOT_READY
;
2185 if (Vcb
->balance
.thread
) {
2186 WARN("cannot lock while balance running\n");
2187 return STATUS_DEVICE_NOT_READY
;
2190 TRACE("locking volume\n");
2192 FsRtlNotifyVolumeEvent(IrpSp
->FileObject
, FSRTL_VOLUME_LOCK
);
2195 return STATUS_SUCCESS
;
2197 acquire_fcb_lock_exclusive(Vcb
);
2199 if (Vcb
->root_fileref
&& Vcb
->root_fileref
->fcb
&& (Vcb
->root_fileref
->open_count
> 0 || has_open_children(Vcb
->root_fileref
))) {
2200 Status
= STATUS_ACCESS_DENIED
;
2201 release_fcb_lock(Vcb
);
2205 release_fcb_lock(Vcb
);
2207 if (Vcb
->balance
.thread
&& KeReadStateEvent(&Vcb
->balance
.event
)) {
2208 ExAcquireResourceExclusiveLite(&Vcb
->tree_lock
, TRUE
);
2209 KeClearEvent(&Vcb
->balance
.event
);
2210 ExReleaseResourceLite(&Vcb
->tree_lock
);
2212 lock_paused_balance
= TRUE
;
2215 ExAcquireResourceExclusiveLite(&Vcb
->tree_lock
, TRUE
);
2217 flush_fcb_caches(Vcb
);
2219 if (Vcb
->need_write
&& !Vcb
->readonly
)
2220 Status
= do_write(Vcb
, Irp
);
2222 Status
= STATUS_SUCCESS
;
2226 ExReleaseResourceLite(&Vcb
->tree_lock
);
2228 if (!NT_SUCCESS(Status
)) {
2229 ERR("do_write returned %08x\n", Status
);
2233 IoAcquireVpbSpinLock(&irql
);
2235 if (!(Vcb
->Vpb
->Flags
& VPB_LOCKED
)) {
2236 Vcb
->Vpb
->Flags
|= VPB_LOCKED
;
2238 Vcb
->locked_fileobj
= IrpSp
->FileObject
;
2239 Vcb
->lock_paused_balance
= lock_paused_balance
;
2241 Status
= STATUS_ACCESS_DENIED
;
2242 IoReleaseVpbSpinLock(irql
);
2244 if (lock_paused_balance
)
2245 KeSetEvent(&Vcb
->balance
.event
, 0, FALSE
);
2250 IoReleaseVpbSpinLock(irql
);
2252 Status
= STATUS_SUCCESS
;
2255 if (!NT_SUCCESS(Status
))
2256 FsRtlNotifyVolumeEvent(IrpSp
->FileObject
, FSRTL_VOLUME_LOCK_FAILED
);
2261 void do_unlock_volume(device_extension
* Vcb
) {
2264 IoAcquireVpbSpinLock(&irql
);
2266 Vcb
->locked
= FALSE
;
2267 Vcb
->Vpb
->Flags
&= ~VPB_LOCKED
;
2268 Vcb
->locked_fileobj
= NULL
;
2270 IoReleaseVpbSpinLock(irql
);
2272 if (Vcb
->lock_paused_balance
)
2273 KeSetEvent(&Vcb
->balance
.event
, 0, FALSE
);
2276 static NTSTATUS
unlock_volume(device_extension
* Vcb
, PIRP Irp
) {
2277 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
2279 TRACE("FSCTL_UNLOCK_VOLUME\n");
2281 if (!Vcb
->locked
|| IrpSp
->FileObject
!= Vcb
->locked_fileobj
)
2282 return STATUS_NOT_LOCKED
;
2284 TRACE("unlocking volume\n");
2286 do_unlock_volume(Vcb
);
2288 FsRtlNotifyVolumeEvent(IrpSp
->FileObject
, FSRTL_VOLUME_UNLOCK
);
2290 return STATUS_SUCCESS
;
2293 static NTSTATUS
invalidate_volumes(PIRP Irp
) {
2294 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
2295 LUID TcbPrivilege
= {SE_TCB_PRIVILEGE
, 0};
2298 PFILE_OBJECT fileobj
;
2299 PDEVICE_OBJECT devobj
;
2302 TRACE("FSCTL_INVALIDATE_VOLUMES\n");
2304 if (!SeSinglePrivilegeCheck(TcbPrivilege
, Irp
->RequestorMode
))
2305 return STATUS_PRIVILEGE_NOT_HELD
;
2308 if (IoIs32bitProcess(Irp
)) {
2309 if (IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
!= sizeof(UINT32
))
2310 return STATUS_INVALID_PARAMETER
;
2312 h
= (HANDLE
)LongToHandle((*(PUINT32
)Irp
->AssociatedIrp
.SystemBuffer
));
2315 if (IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
!= sizeof(HANDLE
))
2316 return STATUS_INVALID_PARAMETER
;
2318 h
= *(PHANDLE
)Irp
->AssociatedIrp
.SystemBuffer
;
2323 Status
= ObReferenceObjectByHandle(h
, 0, *IoFileObjectType
, Irp
->RequestorMode
, (void**)&fileobj
, NULL
);
2325 if (!NT_SUCCESS(Status
)) {
2326 ERR("ObReferenceObjectByHandle returned %08x\n", Status
);
2330 devobj
= fileobj
->DeviceObject
;
2332 ExAcquireResourceSharedLite(&global_loading_lock
, TRUE
);
2336 while (le
!= &VcbList
) {
2337 device_extension
* Vcb
= CONTAINING_RECORD(le
, device_extension
, list_entry
);
2339 if (Vcb
->Vpb
&& Vcb
->Vpb
->RealDevice
== devobj
) {
2340 if (Vcb
->Vpb
== devobj
->Vpb
) {
2343 BOOL free_newvpb
= FALSE
;
2345 newvpb
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(VPB
), ALLOC_TAG
);
2347 ERR("out of memory\n");
2348 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2352 RtlZeroMemory(newvpb
, sizeof(VPB
));
2354 IoAcquireVpbSpinLock(&irql
);
2355 devobj
->Vpb
->Flags
&= ~VPB_MOUNTED
;
2356 IoReleaseVpbSpinLock(irql
);
2358 ExAcquireResourceExclusiveLite(&Vcb
->tree_lock
, TRUE
);
2360 Vcb
->removing
= TRUE
;
2362 ExReleaseResourceLite(&Vcb
->tree_lock
);
2364 CcWaitForCurrentLazyWriterActivity();
2366 ExAcquireResourceExclusiveLite(&Vcb
->tree_lock
, TRUE
);
2368 flush_fcb_caches(Vcb
);
2370 if (Vcb
->need_write
&& !Vcb
->readonly
)
2371 Status
= do_write(Vcb
, Irp
);
2373 Status
= STATUS_SUCCESS
;
2377 if (!NT_SUCCESS(Status
)) {
2378 ERR("do_write returned %08x\n", Status
);
2379 ExReleaseResourceLite(&Vcb
->tree_lock
);
2384 flush_fcb_caches(Vcb
);
2386 ExReleaseResourceLite(&Vcb
->tree_lock
);
2388 IoAcquireVpbSpinLock(&irql
);
2390 if (devobj
->Vpb
->Flags
& VPB_MOUNTED
) {
2391 newvpb
->Type
= IO_TYPE_VPB
;
2392 newvpb
->Size
= sizeof(VPB
);
2393 newvpb
->RealDevice
= devobj
;
2394 newvpb
->Flags
= devobj
->Vpb
->Flags
& VPB_REMOVE_PENDING
;
2396 devobj
->Vpb
= newvpb
;
2400 IoReleaseVpbSpinLock(irql
);
2405 if (Vcb
->open_files
== 0)
2415 Status
= STATUS_SUCCESS
;
2418 ExReleaseResourceLite(&global_loading_lock
);
2420 ObDereferenceObject(fileobj
);
2425 static NTSTATUS
is_volume_dirty(device_extension
* Vcb
, PIRP Irp
) {
2426 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
2429 if (Irp
->AssociatedIrp
.SystemBuffer
) {
2430 volstate
= Irp
->AssociatedIrp
.SystemBuffer
;
2431 } else if (Irp
->MdlAddress
!= NULL
) {
2432 volstate
= MmGetSystemAddressForMdlSafe(Irp
->MdlAddress
, LowPagePriority
);
2435 return STATUS_INSUFFICIENT_RESOURCES
;
2437 return STATUS_INVALID_USER_BUFFER
;
2439 if (IrpSp
->Parameters
.FileSystemControl
.OutputBufferLength
< sizeof(ULONG
))
2440 return STATUS_INVALID_PARAMETER
;
2444 if (IrpSp
->FileObject
->FsContext
!= Vcb
->volume_fcb
)
2445 return STATUS_INVALID_PARAMETER
;
2447 Irp
->IoStatus
.Information
= sizeof(ULONG
);
2449 return STATUS_SUCCESS
;
2452 static NTSTATUS
get_compression(PIRP Irp
) {
2453 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
2454 USHORT
* compression
;
2456 TRACE("FSCTL_GET_COMPRESSION\n");
2458 if (Irp
->AssociatedIrp
.SystemBuffer
) {
2459 compression
= Irp
->AssociatedIrp
.SystemBuffer
;
2460 } else if (Irp
->MdlAddress
!= NULL
) {
2461 compression
= MmGetSystemAddressForMdlSafe(Irp
->MdlAddress
, LowPagePriority
);
2464 return STATUS_INSUFFICIENT_RESOURCES
;
2466 return STATUS_INVALID_USER_BUFFER
;
2468 if (IrpSp
->Parameters
.FileSystemControl
.OutputBufferLength
< sizeof(USHORT
))
2469 return STATUS_INVALID_PARAMETER
;
2471 *compression
= COMPRESSION_FORMAT_NONE
;
2473 Irp
->IoStatus
.Information
= sizeof(USHORT
);
2475 return STATUS_SUCCESS
;
2478 static NTSTATUS
set_compression(PIRP Irp
) {
2479 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
2480 USHORT
* compression
;
2482 TRACE("FSCTL_SET_COMPRESSION\n");
2484 if (IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
< sizeof(USHORT
))
2485 return STATUS_INVALID_PARAMETER
;
2487 compression
= Irp
->AssociatedIrp
.SystemBuffer
;
2489 if (*compression
!= COMPRESSION_FORMAT_NONE
)
2490 return STATUS_INVALID_PARAMETER
;
2492 return STATUS_SUCCESS
;
2495 static void update_volumes(device_extension
* Vcb
) {
2497 volume_device_extension
* vde
= Vcb
->vde
;
2498 pdo_device_extension
* pdode
= vde
->pdode
;
2500 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, TRUE
);
2502 ExAcquireResourceExclusiveLite(&pdode
->child_lock
, TRUE
);
2504 le
= pdode
->children
.Flink
;
2505 while (le
!= &pdode
->children
) {
2506 volume_child
* vc
= CONTAINING_RECORD(le
, volume_child
, list_entry
);
2508 vc
->generation
= Vcb
->superblock
.generation
- 1;
2513 ExReleaseResourceLite(&pdode
->child_lock
);
2515 ExReleaseResourceLite(&Vcb
->tree_lock
);
2518 static NTSTATUS
dismount_volume(device_extension
* Vcb
, PIRP Irp
) {
2522 TRACE("FSCTL_DISMOUNT_VOLUME\n");
2524 if (!(Vcb
->Vpb
->Flags
& VPB_MOUNTED
))
2525 return STATUS_SUCCESS
;
2527 if (Vcb
->disallow_dismount
) {
2528 WARN("attempting to dismount boot volume or one containing a pagefile\n");
2529 return STATUS_ACCESS_DENIED
;
2532 Status
= FsRtlNotifyVolumeEvent(Vcb
->root_file
, FSRTL_VOLUME_DISMOUNT
);
2533 if (!NT_SUCCESS(Status
)) {
2534 WARN("FsRtlNotifyVolumeEvent returned %08x\n", Status
);
2537 ExAcquireResourceExclusiveLite(&Vcb
->tree_lock
, TRUE
);
2540 flush_fcb_caches(Vcb
);
2542 if (Vcb
->need_write
&& !Vcb
->readonly
) {
2543 Status
= do_write(Vcb
, Irp
);
2545 if (!NT_SUCCESS(Status
))
2546 ERR("do_write returned %08x\n", Status
);
2552 Vcb
->removing
= TRUE
;
2555 update_volumes(Vcb
);
2556 Vcb
->vde
->mounted_device
= NULL
;
2559 ExReleaseResourceLite(&Vcb
->tree_lock
);
2561 IoAcquireVpbSpinLock(&irql
);
2562 Vcb
->Vpb
->Flags
&= ~VPB_MOUNTED
;
2563 Vcb
->Vpb
->Flags
|= VPB_DIRECT_WRITES_ALLOWED
;
2564 IoReleaseVpbSpinLock(irql
);
2566 return STATUS_SUCCESS
;
2569 static NTSTATUS
is_device_part_of_mounted_btrfs_raid(PDEVICE_OBJECT devobj
) {
2574 BTRFS_UUID fsuuid
, devuuid
;
2577 to_read
= devobj
->SectorSize
== 0 ? sizeof(superblock
) : (ULONG
)sector_align(sizeof(superblock
), devobj
->SectorSize
);
2579 sb
= ExAllocatePoolWithTag(PagedPool
, to_read
, ALLOC_TAG
);
2581 ERR("out of memory\n");
2582 return STATUS_INSUFFICIENT_RESOURCES
;
2585 Status
= sync_read_phys(devobj
, superblock_addrs
[0], to_read
, (UINT8
*)sb
, TRUE
);
2586 if (!NT_SUCCESS(Status
)) {
2587 ERR("sync_read_phys returned %08x\n", Status
);
2592 if (sb
->magic
!= BTRFS_MAGIC
) {
2593 TRACE("device is not Btrfs\n");
2595 return STATUS_SUCCESS
;
2598 crc32
= ~calc_crc32c(0xffffffff, (UINT8
*)&sb
->uuid
, (ULONG
)sizeof(superblock
) - sizeof(sb
->checksum
));
2600 if (crc32
!= *((UINT32
*)sb
->checksum
)) {
2601 TRACE("device has Btrfs magic, but invalid superblock checksum\n");
2603 return STATUS_SUCCESS
;
2607 devuuid
= sb
->dev_item
.device_uuid
;
2611 ExAcquireResourceSharedLite(&global_loading_lock
, TRUE
);
2615 while (le
!= &VcbList
) {
2616 device_extension
* Vcb
= CONTAINING_RECORD(le
, device_extension
, list_entry
);
2618 if (RtlCompareMemory(&Vcb
->superblock
.uuid
, &fsuuid
, sizeof(BTRFS_UUID
)) == sizeof(BTRFS_UUID
)) {
2621 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, TRUE
);
2623 if (Vcb
->superblock
.num_devices
> 1) {
2624 le2
= Vcb
->devices
.Flink
;
2625 while (le2
!= &Vcb
->devices
) {
2626 device
* dev
= CONTAINING_RECORD(le2
, device
, list_entry
);
2628 if (RtlCompareMemory(&dev
->devitem
.device_uuid
, &devuuid
, sizeof(BTRFS_UUID
)) == sizeof(BTRFS_UUID
)) {
2629 ExReleaseResourceLite(&Vcb
->tree_lock
);
2630 ExReleaseResourceLite(&global_loading_lock
);
2631 return STATUS_DEVICE_NOT_READY
;
2638 ExReleaseResourceLite(&Vcb
->tree_lock
);
2639 ExReleaseResourceLite(&global_loading_lock
);
2640 return STATUS_SUCCESS
;
2646 ExReleaseResourceLite(&global_loading_lock
);
2648 return STATUS_SUCCESS
;
2651 void trim_whole_device(device
* dev
) {
2652 DEVICE_MANAGE_DATA_SET_ATTRIBUTES dmdsa
;
2655 // FIXME - avoid "bootloader area"??
2657 dmdsa
.Size
= sizeof(DEVICE_MANAGE_DATA_SET_ATTRIBUTES
);
2658 dmdsa
.Action
= DeviceDsmAction_Trim
;
2659 dmdsa
.Flags
= DEVICE_DSM_FLAG_ENTIRE_DATA_SET_RANGE
| DEVICE_DSM_FLAG_TRIM_NOT_FS_ALLOCATED
;
2660 dmdsa
.ParameterBlockOffset
= 0;
2661 dmdsa
.ParameterBlockLength
= 0;
2662 dmdsa
.DataSetRangesOffset
= 0;
2663 dmdsa
.DataSetRangesLength
= 0;
2665 Status
= dev_ioctl(dev
->devobj
, IOCTL_STORAGE_MANAGE_DATA_SET_ATTRIBUTES
, &dmdsa
, sizeof(DEVICE_MANAGE_DATA_SET_ATTRIBUTES
), NULL
, 0, TRUE
, NULL
);
2666 if (!NT_SUCCESS(Status
))
2667 WARN("IOCTL_STORAGE_MANAGE_DATA_SET_ATTRIBUTES returned %08x\n", Status
);
2670 static NTSTATUS
add_device(device_extension
* Vcb
, PIRP Irp
, KPROCESSOR_MODE processor_mode
) {
2671 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
2673 PFILE_OBJECT fileobj
, mountmgrfo
;
2674 PDEVICE_OBJECT DeviceObject
;
2679 UINT64 dev_id
, size
;
2682 UNICODE_STRING mmdevpath
, pnp_name
, pnp_name2
;
2684 PDEVICE_OBJECT mountmgr
;
2687 STORAGE_DEVICE_NUMBER sdn
;
2688 volume_device_extension
* vde
;
2689 pdo_device_extension
* pdode
;
2690 const GUID
* pnp_guid
;
2691 GET_LENGTH_INFORMATION gli
;
2693 pnp_name
.Buffer
= NULL
;
2695 if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE
), processor_mode
))
2696 return STATUS_PRIVILEGE_NOT_HELD
;
2699 WARN("not allowing second device to be added to non-PNP device\n");
2700 return STATUS_NOT_SUPPORTED
;
2703 if (Vcb
->readonly
) // FIXME - handle adding R/W device to seeding device
2704 return STATUS_MEDIA_WRITE_PROTECTED
;
2707 if (IoIs32bitProcess(Irp
)) {
2708 if (IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
!= sizeof(UINT32
))
2709 return STATUS_INVALID_PARAMETER
;
2711 h
= (HANDLE
)LongToHandle((*(PUINT32
)Irp
->AssociatedIrp
.SystemBuffer
));
2714 if (IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
!= sizeof(HANDLE
))
2715 return STATUS_INVALID_PARAMETER
;
2717 h
= *(PHANDLE
)Irp
->AssociatedIrp
.SystemBuffer
;
2722 Status
= ObReferenceObjectByHandle(h
, 0, *IoFileObjectType
, Irp
->RequestorMode
, (void**)&fileobj
, NULL
);
2724 if (!NT_SUCCESS(Status
)) {
2725 ERR("ObReferenceObjectByHandle returned %08x\n", Status
);
2729 DeviceObject
= fileobj
->DeviceObject
;
2731 Status
= get_device_pnp_name(DeviceObject
, &pnp_name
, &pnp_guid
);
2732 if (!NT_SUCCESS(Status
)) {
2733 ERR("get_device_pnp_name returned %08x\n", Status
);
2734 ObDereferenceObject(fileobj
);
2738 // If this is a disk, we have been handed the PDO, so need to go up to find something we can use
2739 if (RtlCompareMemory(pnp_guid
, &GUID_DEVINTERFACE_DISK
, sizeof(GUID
)) == sizeof(GUID
) && DeviceObject
->AttachedDevice
)
2740 DeviceObject
= DeviceObject
->AttachedDevice
;
2742 Status
= dev_ioctl(DeviceObject
, IOCTL_DISK_IS_WRITABLE
, NULL
, 0, NULL
, 0, TRUE
, NULL
);
2743 if (!NT_SUCCESS(Status
)) {
2744 ERR("IOCTL_DISK_IS_WRITABLE returned %08x\n", Status
);
2745 ObDereferenceObject(fileobj
);
2749 Status
= is_device_part_of_mounted_btrfs_raid(DeviceObject
);
2750 if (!NT_SUCCESS(Status
)) {
2751 ERR("is_device_part_of_mounted_btrfs_raid returned %08x\n", Status
);
2752 ObDereferenceObject(fileobj
);
2756 // if disk, check it has no partitions
2757 if (RtlCompareMemory(pnp_guid
, &GUID_DEVINTERFACE_DISK
, sizeof(GUID
)) == sizeof(GUID
)) {
2759 DRIVE_LAYOUT_INFORMATION_EX
* dli
= NULL
;
2769 dli
= ExAllocatePoolWithTag(PagedPool
, dlisize
, ALLOC_TAG
);
2771 ERR("out of memory\n");
2772 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2776 Status
= dev_ioctl(DeviceObject
, IOCTL_DISK_GET_DRIVE_LAYOUT_EX
, NULL
, 0, dli
, dlisize
, TRUE
, NULL
);
2777 } while (Status
== STATUS_BUFFER_TOO_SMALL
);
2779 if (NT_SUCCESS(Status
) && dli
->PartitionCount
> 0) {
2781 ERR("not adding disk which has partitions\n");
2782 Status
= STATUS_DEVICE_NOT_READY
;
2789 Status
= dev_ioctl(DeviceObject
, IOCTL_STORAGE_GET_DEVICE_NUMBER
, NULL
, 0,
2790 &sdn
, sizeof(STORAGE_DEVICE_NUMBER
), TRUE
, NULL
);
2791 if (NT_SUCCESS(Status
)) {
2792 if (sdn
.DeviceType
!= FILE_DEVICE_DISK
) { // FIXME - accept floppies and CDs?
2793 WARN("device was not disk\n");
2794 ObDereferenceObject(fileobj
);
2795 return STATUS_INVALID_PARAMETER
;
2798 sdn
.DeviceNumber
= 0xffffffff;
2799 sdn
.PartitionNumber
= 0xffffffff;
2802 Status
= dev_ioctl(DeviceObject
, IOCTL_DISK_GET_LENGTH_INFO
, NULL
, 0,
2803 &gli
, sizeof(gli
), TRUE
, NULL
);
2804 if (!NT_SUCCESS(Status
)) {
2805 ERR("error reading length information: %08x\n", Status
);
2806 ObDereferenceObject(fileobj
);
2810 size
= gli
.Length
.QuadPart
;
2812 if (size
< 0x100000) {
2813 ERR("device was not large enough to hold FS (%llx bytes, need at least 1 MB)\n", size
);
2814 ObDereferenceObject(fileobj
);
2815 return STATUS_INTERNAL_ERROR
;
2818 volume_removal(drvobj
, &pnp_name
);
2820 ExAcquireResourceExclusiveLite(&Vcb
->tree_lock
, TRUE
);
2822 if (Vcb
->need_write
)
2823 Status
= do_write(Vcb
, Irp
);
2825 Status
= STATUS_SUCCESS
;
2829 if (!NT_SUCCESS(Status
)) {
2830 ERR("do_write returned %08x\n", Status
);
2834 dev
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(device
), ALLOC_TAG
);
2836 ERR("out of memory\n");
2837 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2841 RtlZeroMemory(dev
, sizeof(device
));
2843 dev
->devobj
= DeviceObject
;
2844 dev
->seeding
= FALSE
;
2845 init_device(Vcb
, dev
, TRUE
);
2847 InitializeListHead(&dev
->space
);
2849 if (size
> 0x100000) { // add disk hole - the first MB is marked as used
2850 Status
= add_space_entry(&dev
->space
, NULL
, 0x100000, size
- 0x100000);
2851 if (!NT_SUCCESS(Status
)) {
2852 ERR("add_space_entry returned %08x\n", Status
);
2859 le
= Vcb
->devices
.Flink
;
2860 while (le
!= &Vcb
->devices
) {
2861 device
* dev2
= CONTAINING_RECORD(le
, device
, list_entry
);
2863 if (dev2
->devitem
.dev_id
> dev_id
)
2864 dev_id
= dev2
->devitem
.dev_id
;
2871 dev
->devitem
.dev_id
= dev_id
;
2872 dev
->devitem
.num_bytes
= size
;
2873 dev
->devitem
.bytes_used
= 0;
2874 dev
->devitem
.optimal_io_align
= Vcb
->superblock
.sector_size
;
2875 dev
->devitem
.optimal_io_width
= Vcb
->superblock
.sector_size
;
2876 dev
->devitem
.minimal_io_size
= Vcb
->superblock
.sector_size
;
2877 dev
->devitem
.type
= 0;
2878 dev
->devitem
.generation
= 0;
2879 dev
->devitem
.start_offset
= 0;
2880 dev
->devitem
.dev_group
= 0;
2881 dev
->devitem
.seek_speed
= 0;
2882 dev
->devitem
.bandwidth
= 0;
2883 get_uuid(&dev
->devitem
.device_uuid
);
2884 dev
->devitem
.fs_uuid
= Vcb
->superblock
.uuid
;
2886 di
= ExAllocatePoolWithTag(PagedPool
, sizeof(DEV_ITEM
), ALLOC_TAG
);
2888 ERR("out of memory\n");
2892 RtlCopyMemory(di
, &dev
->devitem
, sizeof(DEV_ITEM
));
2894 Status
= insert_tree_item(Vcb
, Vcb
->chunk_root
, 1, TYPE_DEV_ITEM
, di
->dev_id
, di
, sizeof(DEV_ITEM
), NULL
, Irp
);
2895 if (!NT_SUCCESS(Status
)) {
2896 ERR("insert_tree_item returned %08x\n", Status
);
2901 // add stats entry to dev tree
2902 stats
= ExAllocatePoolWithTag(PagedPool
, sizeof(UINT64
) * 5, ALLOC_TAG
);
2904 ERR("out of memory\n");
2905 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2909 RtlZeroMemory(stats
, sizeof(UINT64
) * 5);
2911 searchkey
.obj_id
= 0;
2912 searchkey
.obj_type
= TYPE_DEV_STATS
;
2913 searchkey
.offset
= di
->dev_id
;
2915 Status
= find_item(Vcb
, Vcb
->dev_root
, &tp
, &searchkey
, FALSE
, Irp
);
2916 if (!NT_SUCCESS(Status
)) {
2917 ERR("error - find_item returned %08x\n", Status
);
2922 if (!keycmp(tp
.item
->key
, searchkey
)) {
2923 Status
= delete_tree_item(Vcb
, &tp
);
2924 if (!NT_SUCCESS(Status
)) {
2925 ERR("delete_tree_item returned %08x\n", Status
);
2931 Status
= insert_tree_item(Vcb
, Vcb
->dev_root
, 0, TYPE_DEV_STATS
, di
->dev_id
, stats
, sizeof(UINT64
) * 5, NULL
, Irp
);
2932 if (!NT_SUCCESS(Status
)) {
2933 ERR("insert_tree_item returned %08x\n", Status
);
2938 if (dev
->trim
&& !dev
->readonly
&& !Vcb
->options
.no_trim
)
2939 trim_whole_device(dev
);
2941 // We clear the first megabyte of the device, so Windows doesn't identify it as another FS
2942 mb
= ExAllocatePoolWithTag(PagedPool
, 0x100000, ALLOC_TAG
);
2944 ERR("out of memory\n");
2945 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2949 RtlZeroMemory(mb
, 0x100000);
2951 Status
= write_data_phys(DeviceObject
, 0, mb
, 0x100000);
2952 if (!NT_SUCCESS(Status
)) {
2953 ERR("write_data_phys returned %08x\n", Status
);
2963 vc
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(volume_child
), ALLOC_TAG
);
2965 ERR("out of memory\n");
2966 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2970 vc
->uuid
= dev
->devitem
.device_uuid
;
2972 vc
->generation
= Vcb
->superblock
.generation
;
2973 vc
->devobj
= DeviceObject
;
2974 vc
->fileobj
= fileobj
;
2975 vc
->notification_entry
= NULL
;
2977 Status
= IoRegisterPlugPlayNotification(EventCategoryTargetDeviceChange
, 0, fileobj
,
2978 drvobj
, pnp_removal
, vde
->pdode
, &vc
->notification_entry
);
2979 if (!NT_SUCCESS(Status
))
2980 WARN("IoRegisterPlugPlayNotification returned %08x\n", Status
);
2982 pnp_name2
= pnp_name
;
2984 if (pnp_name
.Length
> 4 * sizeof(WCHAR
) && pnp_name
.Buffer
[0] == '\\' && (pnp_name
.Buffer
[1] == '\\' || pnp_name
.Buffer
[1] == '?') &&
2985 pnp_name
.Buffer
[2] == '?' && pnp_name
.Buffer
[3] == '\\') {
2986 pnp_name2
.Buffer
= &pnp_name2
.Buffer
[3];
2987 pnp_name2
.Length
-= 3 * sizeof(WCHAR
);
2988 pnp_name2
.MaximumLength
-= 3 * sizeof(WCHAR
);
2991 vc
->pnp_name
.Length
= vc
->pnp_name
.MaximumLength
= pnp_name2
.Length
;
2993 if (pnp_name2
.Length
== 0)
2994 vc
->pnp_name
.Buffer
= NULL
;
2996 vc
->pnp_name
.Buffer
= ExAllocatePoolWithTag(PagedPool
, pnp_name2
.Length
, ALLOC_TAG
);
2997 if (!vc
->pnp_name
.Buffer
) {
2998 ERR("out of memory\n");
2999 Status
= STATUS_INSUFFICIENT_RESOURCES
;
3003 RtlCopyMemory(vc
->pnp_name
.Buffer
, pnp_name2
.Buffer
, pnp_name2
.Length
);
3007 vc
->seeding
= FALSE
;
3008 vc
->disk_num
= sdn
.DeviceNumber
;
3009 vc
->part_num
= sdn
.PartitionNumber
;
3010 vc
->had_drive_letter
= FALSE
;
3012 ExAcquireResourceExclusiveLite(&pdode
->child_lock
, TRUE
);
3013 InsertTailList(&pdode
->children
, &vc
->list_entry
);
3014 pdode
->num_children
++;
3015 pdode
->children_loaded
++;
3016 ExReleaseResourceLite(&pdode
->child_lock
);
3018 RtlInitUnicodeString(&mmdevpath
, MOUNTMGR_DEVICE_NAME
);
3019 Status
= IoGetDeviceObjectPointer(&mmdevpath
, FILE_READ_ATTRIBUTES
, &mountmgrfo
, &mountmgr
);
3020 if (!NT_SUCCESS(Status
))
3021 ERR("IoGetDeviceObjectPointer returned %08x\n", Status
);
3023 Status
= remove_drive_letter(mountmgr
, &pnp_name
);
3024 if (!NT_SUCCESS(Status
) && Status
!= STATUS_NOT_FOUND
)
3025 WARN("remove_drive_letter returned %08x\n", Status
);
3027 vc
->had_drive_letter
= NT_SUCCESS(Status
);
3029 ObDereferenceObject(mountmgrfo
);
3032 Vcb
->superblock
.num_devices
++;
3033 Vcb
->superblock
.total_bytes
+= size
;
3034 Vcb
->devices_loaded
++;
3035 InsertTailList(&Vcb
->devices
, &dev
->list_entry
);
3037 // FIXME - send notification that volume size has increased
3039 ObReferenceObject(DeviceObject
); // for Vcb
3041 Status
= do_write(Vcb
, Irp
);
3042 if (!NT_SUCCESS(Status
))
3043 ERR("do_write returned %08x\n", Status
);
3045 ObReferenceObject(fileobj
);
3050 ExReleaseResourceLite(&Vcb
->tree_lock
);
3053 ObDereferenceObject(fileobj
);
3055 if (pnp_name
.Buffer
)
3056 ExFreePool(pnp_name
.Buffer
);
3058 if (NT_SUCCESS(Status
))
3059 FsRtlNotifyVolumeEvent(Vcb
->root_file
, FSRTL_VOLUME_CHANGE_SIZE
);
3064 static NTSTATUS
allow_extended_dasd_io(device_extension
* Vcb
, PFILE_OBJECT FileObject
) {
3068 TRACE("FSCTL_ALLOW_EXTENDED_DASD_IO\n");
3071 return STATUS_INVALID_PARAMETER
;
3073 fcb
= FileObject
->FsContext
;
3074 ccb
= FileObject
->FsContext2
;
3077 return STATUS_INVALID_PARAMETER
;
3079 if (fcb
!= Vcb
->volume_fcb
)
3080 return STATUS_INVALID_PARAMETER
;
3083 return STATUS_INVALID_PARAMETER
;
3085 ccb
->allow_extended_dasd_io
= TRUE
;
3087 return STATUS_SUCCESS
;
3090 static NTSTATUS
query_uuid(device_extension
* Vcb
, void* data
, ULONG length
) {
3091 if (length
< sizeof(BTRFS_UUID
))
3092 return STATUS_BUFFER_OVERFLOW
;
3094 RtlCopyMemory(data
, &Vcb
->superblock
.uuid
, sizeof(BTRFS_UUID
));
3096 return STATUS_SUCCESS
;
3099 static NTSTATUS
reset_stats(device_extension
* Vcb
, void* data
, ULONG length
, KPROCESSOR_MODE processor_mode
) {
3104 if (length
< sizeof(UINT64
))
3105 return STATUS_INVALID_PARAMETER
;
3108 return STATUS_MEDIA_WRITE_PROTECTED
;
3110 if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE
), processor_mode
))
3111 return STATUS_PRIVILEGE_NOT_HELD
;
3113 devid
= *((UINT64
*)data
);
3115 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, TRUE
);
3117 le
= Vcb
->devices
.Flink
;
3119 while (le
!= &Vcb
->devices
) {
3120 device
* dev
= CONTAINING_RECORD(le
, device
, list_entry
);
3122 if (dev
->devitem
.dev_id
== devid
) {
3123 RtlZeroMemory(dev
->stats
, sizeof(UINT64
) * 5);
3124 dev
->stats_changed
= TRUE
;
3125 Vcb
->stats_changed
= TRUE
;
3126 Vcb
->need_write
= TRUE
;
3127 Status
= STATUS_SUCCESS
;
3134 WARN("device %llx not found\n", devid
);
3135 Status
= STATUS_INVALID_PARAMETER
;
3138 ExReleaseResourceLite(&Vcb
->tree_lock
);
3143 static NTSTATUS
get_integrity_information(device_extension
* Vcb
, PFILE_OBJECT FileObject
, void* data
, ULONG datalen
) {
3144 FSCTL_GET_INTEGRITY_INFORMATION_BUFFER
* fgiib
= (FSCTL_GET_INTEGRITY_INFORMATION_BUFFER
*)data
;
3146 TRACE("FSCTL_GET_INTEGRITY_INFORMATION\n");
3151 return STATUS_INVALID_PARAMETER
;
3153 if (!data
|| datalen
< sizeof(FSCTL_GET_INTEGRITY_INFORMATION_BUFFER
))
3154 return STATUS_INVALID_PARAMETER
;
3156 fgiib
->ChecksumAlgorithm
= 0;
3157 fgiib
->Reserved
= 0;
3159 fgiib
->ChecksumChunkSizeInBytes
= Vcb
->superblock
.sector_size
;
3160 fgiib
->ClusterSizeInBytes
= Vcb
->superblock
.sector_size
;
3162 return STATUS_SUCCESS
;
3165 static NTSTATUS
set_integrity_information(PFILE_OBJECT FileObject
, void* data
, ULONG datalen
) {
3166 TRACE("FSCTL_SET_INTEGRITY_INFORMATION\n");
3171 return STATUS_INVALID_PARAMETER
;
3173 if (!data
|| datalen
< sizeof(FSCTL_SET_INTEGRITY_INFORMATION_BUFFER
))
3174 return STATUS_INVALID_PARAMETER
;
3176 return STATUS_SUCCESS
;
3179 BOOL
fcb_is_inline(fcb
* fcb
) {
3182 le
= fcb
->extents
.Flink
;
3183 while (le
!= &fcb
->extents
) {
3184 extent
* ext
= CONTAINING_RECORD(le
, extent
, list_entry
);
3187 return ext
->extent_data
.type
== EXTENT_TYPE_INLINE
;
3195 static NTSTATUS
duplicate_extents(device_extension
* Vcb
, PFILE_OBJECT FileObject
, void* data
, ULONG datalen
, PIRP Irp
) {
3196 DUPLICATE_EXTENTS_DATA
* ded
= (DUPLICATE_EXTENTS_DATA
*)data
;
3197 fcb
*fcb
= FileObject
? FileObject
->FsContext
: NULL
, *sourcefcb
;
3198 ccb
*ccb
= FileObject
? FileObject
->FsContext2
: NULL
, *sourceccb
;
3200 PFILE_OBJECT sourcefo
;
3201 UINT64 sourcelen
, nbytes
= 0;
3202 LIST_ENTRY rollback
, *le
, newexts
;
3207 if (!ded
|| datalen
< sizeof(DUPLICATE_EXTENTS_DATA
))
3208 return STATUS_BUFFER_TOO_SMALL
;
3211 return STATUS_MEDIA_WRITE_PROTECTED
;
3213 if (ded
->ByteCount
.QuadPart
== 0)
3214 return STATUS_SUCCESS
;
3216 if (!fcb
|| !ccb
|| fcb
== Vcb
->volume_fcb
)
3217 return STATUS_INVALID_PARAMETER
;
3219 if (is_subvol_readonly(fcb
->subvol
, Irp
))
3220 return STATUS_ACCESS_DENIED
;
3222 if (Irp
->RequestorMode
== UserMode
&& !(ccb
->access
& FILE_WRITE_DATA
)) {
3223 WARN("insufficient privileges\n");
3224 return STATUS_ACCESS_DENIED
;
3227 if (!fcb
->ads
&& fcb
->type
!= BTRFS_TYPE_FILE
&& fcb
->type
!= BTRFS_TYPE_SYMLINK
)
3228 return STATUS_INVALID_PARAMETER
;
3230 Status
= ObReferenceObjectByHandle(ded
->FileHandle
, 0, *IoFileObjectType
, Irp
->RequestorMode
, (void**)&sourcefo
, NULL
);
3231 if (!NT_SUCCESS(Status
)) {
3232 ERR("ObReferenceObjectByHandle returned %08x\n", Status
);
3236 if (sourcefo
->DeviceObject
!= FileObject
->DeviceObject
) {
3237 WARN("source and destination are on different volumes\n");
3238 ObDereferenceObject(sourcefo
);
3239 return STATUS_INVALID_PARAMETER
;
3242 sourcefcb
= sourcefo
->FsContext
;
3243 sourceccb
= sourcefo
->FsContext2
;
3245 if (!sourcefcb
|| !sourceccb
|| sourcefcb
== Vcb
->volume_fcb
) {
3246 ObDereferenceObject(sourcefo
);
3247 return STATUS_INVALID_PARAMETER
;
3250 if (!sourcefcb
->ads
&& !fcb
->ads
) {
3251 if ((ded
->SourceFileOffset
.QuadPart
& (Vcb
->superblock
.sector_size
- 1)) || (ded
->TargetFileOffset
.QuadPart
& (Vcb
->superblock
.sector_size
- 1))) {
3252 ObDereferenceObject(sourcefo
);
3253 return STATUS_INVALID_PARAMETER
;
3256 if (ded
->ByteCount
.QuadPart
& (Vcb
->superblock
.sector_size
- 1)) {
3257 ObDereferenceObject(sourcefo
);
3258 return STATUS_INVALID_PARAMETER
;
3262 if (Irp
->RequestorMode
== UserMode
&& (!(sourceccb
->access
& FILE_READ_DATA
) || !(sourceccb
->access
& FILE_READ_ATTRIBUTES
))) {
3263 WARN("insufficient privileges\n");
3264 ObDereferenceObject(sourcefo
);
3265 return STATUS_ACCESS_DENIED
;
3268 if (!sourcefcb
->ads
&& sourcefcb
->type
!= BTRFS_TYPE_FILE
&& sourcefcb
->type
!= BTRFS_TYPE_SYMLINK
) {
3269 ObDereferenceObject(sourcefo
);
3270 return STATUS_INVALID_PARAMETER
;
3273 sourcelen
= sourcefcb
->ads
? sourcefcb
->adsdata
.Length
: sourcefcb
->inode_item
.st_size
;
3275 if (sector_align(sourcelen
, Vcb
->superblock
.sector_size
) < (UINT64
)ded
->SourceFileOffset
.QuadPart
+ (UINT64
)ded
->ByteCount
.QuadPart
) {
3276 ObDereferenceObject(sourcefo
);
3277 return STATUS_NOT_SUPPORTED
;
3280 if (fcb
== sourcefcb
&&
3281 ((ded
->SourceFileOffset
.QuadPart
>= ded
->TargetFileOffset
.QuadPart
&& ded
->SourceFileOffset
.QuadPart
< ded
->TargetFileOffset
.QuadPart
+ ded
->ByteCount
.QuadPart
) ||
3282 (ded
->TargetFileOffset
.QuadPart
>= ded
->SourceFileOffset
.QuadPart
&& ded
->TargetFileOffset
.QuadPart
< ded
->SourceFileOffset
.QuadPart
+ ded
->ByteCount
.QuadPart
))) {
3283 WARN("source and destination are the same, and the ranges overlap\n");
3284 ObDereferenceObject(sourcefo
);
3285 return STATUS_INVALID_PARAMETER
;
3288 // fail if nocsum flag set on one file but not the other
3289 if (!fcb
->ads
&& !sourcefcb
->ads
&& (fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
) != (sourcefcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
)) {
3290 ObDereferenceObject(sourcefo
);
3291 return STATUS_INVALID_PARAMETER
;
3294 InitializeListHead(&rollback
);
3295 InitializeListHead(&newexts
);
3297 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, TRUE
);
3299 ExAcquireResourceExclusiveLite(fcb
->Header
.Resource
, TRUE
);
3301 if (fcb
!= sourcefcb
)
3302 ExAcquireResourceSharedLite(sourcefcb
->Header
.Resource
, TRUE
);
3304 if (!FsRtlFastCheckLockForWrite(&fcb
->lock
, &ded
->TargetFileOffset
, &ded
->ByteCount
, 0, FileObject
, PsGetCurrentProcess())) {
3305 Status
= STATUS_FILE_LOCK_CONFLICT
;
3309 if (!FsRtlFastCheckLockForRead(&sourcefcb
->lock
, &ded
->SourceFileOffset
, &ded
->ByteCount
, 0, FileObject
, PsGetCurrentProcess())) {
3310 Status
= STATUS_FILE_LOCK_CONFLICT
;
3314 make_inline
= fcb
->ads
? FALSE
: (fcb
->inode_item
.st_size
<= Vcb
->options
.max_inline
|| fcb_is_inline(fcb
));
3316 if (fcb
->ads
|| sourcefcb
->ads
|| make_inline
|| fcb_is_inline(sourcefcb
)) {
3318 ULONG bytes_read
, dataoff
, datalen2
;
3321 dataoff
= (ULONG
)ded
->TargetFileOffset
.QuadPart
;
3322 datalen2
= (ULONG
)fcb
->inode_item
.st_size
;
3323 } else if (fcb
->ads
) {
3325 datalen2
= (ULONG
)ded
->ByteCount
.QuadPart
;
3327 dataoff
= ded
->TargetFileOffset
.QuadPart
% Vcb
->superblock
.sector_size
;
3328 datalen2
= (ULONG
)sector_align(ded
->ByteCount
.QuadPart
+ dataoff
, Vcb
->superblock
.sector_size
);
3331 data2
= ExAllocatePoolWithTag(PagedPool
, datalen2
, ALLOC_TAG
);
3333 ERR("out of memory\n");
3334 Status
= STATUS_INSUFFICIENT_RESOURCES
;
3340 Status
= read_file(fcb
, data2
, 0, datalen2
, NULL
, Irp
);
3342 Status
= read_file(fcb
, data2
, ded
->TargetFileOffset
.QuadPart
- dataoff
, dataoff
, NULL
, Irp
);
3344 if (!NT_SUCCESS(Status
)) {
3345 ERR("read_file returned %08x\n", Status
);
3351 if (sourcefcb
->ads
) {
3352 Status
= read_stream(sourcefcb
, data2
+ dataoff
, ded
->SourceFileOffset
.QuadPart
, (ULONG
)ded
->ByteCount
.QuadPart
, &bytes_read
);
3353 if (!NT_SUCCESS(Status
)) {
3354 ERR("read_stream returned %08x\n", Status
);
3359 Status
= read_file(sourcefcb
, data2
+ dataoff
, ded
->SourceFileOffset
.QuadPart
, ded
->ByteCount
.QuadPart
, &bytes_read
, Irp
);
3360 if (!NT_SUCCESS(Status
)) {
3361 ERR("read_file returned %08x\n", Status
);
3367 if (dataoff
+ bytes_read
< datalen2
)
3368 RtlZeroMemory(data2
+ dataoff
+ bytes_read
, datalen2
- bytes_read
);
3371 RtlCopyMemory(&fcb
->adsdata
.Buffer
[ded
->TargetFileOffset
.QuadPart
], data2
, (USHORT
)min(ded
->ByteCount
.QuadPart
, fcb
->adsdata
.Length
- ded
->TargetFileOffset
.QuadPart
));
3372 else if (make_inline
) {
3376 Status
= excise_extents(Vcb
, fcb
, 0, sector_align(fcb
->inode_item
.st_size
, Vcb
->superblock
.sector_size
), Irp
, &rollback
);
3377 if (!NT_SUCCESS(Status
)) {
3378 ERR("excise_extents returned %08x\n", Status
);
3383 edsize
= (UINT16
)(offsetof(EXTENT_DATA
, data
[0]) + datalen2
);
3385 ed
= ExAllocatePoolWithTag(PagedPool
, edsize
, ALLOC_TAG
);
3387 ERR("out of memory\n");
3389 Status
= STATUS_INSUFFICIENT_RESOURCES
;
3393 ed
->generation
= Vcb
->superblock
.generation
;
3394 ed
->decoded_size
= fcb
->inode_item
.st_size
;
3395 ed
->compression
= BTRFS_COMPRESSION_NONE
;
3396 ed
->encryption
= BTRFS_ENCRYPTION_NONE
;
3397 ed
->encoding
= BTRFS_ENCODING_NONE
;
3398 ed
->type
= EXTENT_TYPE_INLINE
;
3400 RtlCopyMemory(ed
->data
, data2
, datalen2
);
3402 Status
= add_extent_to_fcb(fcb
, 0, ed
, edsize
, FALSE
, NULL
, &rollback
);
3403 if (!NT_SUCCESS(Status
)) {
3404 ERR("add_extent_to_fcb returned %08x\n", Status
);
3409 fcb
->inode_item
.st_blocks
+= datalen2
;
3411 UINT64 start
= ded
->TargetFileOffset
.QuadPart
- (ded
->TargetFileOffset
.QuadPart
% Vcb
->superblock
.sector_size
);
3413 Status
= do_write_file(fcb
, start
, start
+ datalen2
, data2
, Irp
, FALSE
, 0, &rollback
);
3414 if (!NT_SUCCESS(Status
)) {
3415 ERR("do_write_file returned %08x\n", Status
);
3423 LIST_ENTRY
* lastextle
;
3425 le
= sourcefcb
->extents
.Flink
;
3426 while (le
!= &sourcefcb
->extents
) {
3427 extent
* ext
= CONTAINING_RECORD(le
, extent
, list_entry
);
3430 if (ext
->offset
>= (UINT64
)ded
->SourceFileOffset
.QuadPart
+ (UINT64
)ded
->ByteCount
.QuadPart
)
3433 if (ext
->extent_data
.type
!= EXTENT_TYPE_INLINE
) {
3434 ULONG extlen
= offsetof(extent
, extent_data
) + sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
);
3436 EXTENT_DATA2
*ed2s
, *ed2d
;
3439 ed2s
= (EXTENT_DATA2
*)ext
->extent_data
.data
;
3441 if (ext
->offset
+ ed2s
->num_bytes
<= (UINT64
)ded
->SourceFileOffset
.QuadPart
) {
3446 ext2
= ExAllocatePoolWithTag(PagedPool
, extlen
, ALLOC_TAG
);
3448 ERR("out of memory\n");
3449 Status
= STATUS_INSUFFICIENT_RESOURCES
;
3453 if (ext
->offset
< (UINT64
)ded
->SourceFileOffset
.QuadPart
)
3454 ext2
->offset
= ded
->TargetFileOffset
.QuadPart
;
3456 ext2
->offset
= ext
->offset
- ded
->SourceFileOffset
.QuadPart
+ ded
->TargetFileOffset
.QuadPart
;
3458 ext2
->datalen
= sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
);
3459 ext2
->unique
= FALSE
;
3460 ext2
->ignore
= FALSE
;
3461 ext2
->inserted
= TRUE
;
3463 ext2
->extent_data
.generation
= Vcb
->superblock
.generation
;
3464 ext2
->extent_data
.decoded_size
= ext
->extent_data
.decoded_size
;
3465 ext2
->extent_data
.compression
= ext
->extent_data
.compression
;
3466 ext2
->extent_data
.encryption
= ext
->extent_data
.encryption
;
3467 ext2
->extent_data
.encoding
= ext
->extent_data
.encoding
;
3468 ext2
->extent_data
.type
= ext
->extent_data
.type
;
3470 ed2d
= (EXTENT_DATA2
*)ext2
->extent_data
.data
;
3472 ed2d
->address
= ed2s
->address
;
3473 ed2d
->size
= ed2s
->size
;
3475 if (ext
->offset
< (UINT64
)ded
->SourceFileOffset
.QuadPart
) {
3476 ed2d
->offset
= ed2s
->offset
+ ded
->SourceFileOffset
.QuadPart
- ext
->offset
;
3477 ed2d
->num_bytes
= min((UINT64
)ded
->ByteCount
.QuadPart
, ed2s
->num_bytes
+ ext
->offset
- ded
->SourceFileOffset
.QuadPart
);
3479 ed2d
->offset
= ed2s
->offset
;
3480 ed2d
->num_bytes
= min(ded
->SourceFileOffset
.QuadPart
+ ded
->ByteCount
.QuadPart
- ext
->offset
, ed2s
->num_bytes
);
3484 if (ext
->extent_data
.compression
== BTRFS_COMPRESSION_NONE
) {
3485 ext2
->csum
= ExAllocatePoolWithTag(PagedPool
, (ULONG
)(ed2d
->num_bytes
* sizeof(UINT32
) / Vcb
->superblock
.sector_size
), ALLOC_TAG
);
3487 ERR("out of memory\n");
3488 Status
= STATUS_INSUFFICIENT_RESOURCES
;
3493 RtlCopyMemory(ext2
->csum
, &ext
->csum
[(ed2d
->offset
- ed2s
->offset
) / Vcb
->superblock
.sector_size
],
3494 (ULONG
)(ed2d
->num_bytes
* sizeof(UINT32
) / Vcb
->superblock
.sector_size
));
3496 ext2
->csum
= ExAllocatePoolWithTag(PagedPool
, (ULONG
)(ed2d
->size
* sizeof(UINT32
) / Vcb
->superblock
.sector_size
), ALLOC_TAG
);
3498 ERR("out of memory\n");
3499 Status
= STATUS_INSUFFICIENT_RESOURCES
;
3504 RtlCopyMemory(ext2
->csum
, ext
->csum
, (ULONG
)(ed2s
->size
* sizeof(UINT32
) / Vcb
->superblock
.sector_size
));
3509 InsertTailList(&newexts
, &ext2
->list_entry
);
3511 c
= get_chunk_from_address(Vcb
, ed2s
->address
);
3513 ERR("get_chunk_from_address(%llx) failed\n", ed2s
->address
);
3514 Status
= STATUS_INTERNAL_ERROR
;
3518 Status
= update_changed_extent_ref(Vcb
, c
, ed2s
->address
, ed2s
->size
, fcb
->subvol
->id
, fcb
->inode
, ext2
->offset
- ed2d
->offset
,
3519 1, fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
, FALSE
, Irp
);
3520 if (!NT_SUCCESS(Status
)) {
3521 ERR("update_changed_extent_ref returned %08x\n", Status
);
3525 nbytes
+= ed2d
->num_bytes
;
3532 Status
= excise_extents(Vcb
, fcb
, ded
->TargetFileOffset
.QuadPart
, ded
->TargetFileOffset
.QuadPart
+ ded
->ByteCount
.QuadPart
, Irp
, &rollback
);
3533 if (!NT_SUCCESS(Status
)) {
3534 ERR("excise_extents returned %08x\n", Status
);
3536 while (!IsListEmpty(&newexts
)) {
3537 extent
* ext
= CONTAINING_RECORD(RemoveHeadList(&newexts
), extent
, list_entry
);
3544 // clear unique flags in source fcb
3545 le
= sourcefcb
->extents
.Flink
;
3546 while (le
!= &sourcefcb
->extents
) {
3547 extent
* ext
= CONTAINING_RECORD(le
, extent
, list_entry
);
3549 if (!ext
->ignore
&& ext
->unique
&& (ext
->extent_data
.type
== EXTENT_TYPE_REGULAR
|| ext
->extent_data
.type
== EXTENT_TYPE_PREALLOC
)) {
3550 EXTENT_DATA2
* ed2s
= (EXTENT_DATA2
*)ext
->extent_data
.data
;
3553 le2
= newexts
.Flink
;
3554 while (le2
!= &newexts
) {
3555 extent
* ext2
= CONTAINING_RECORD(le2
, extent
, list_entry
);
3557 if (ext2
->extent_data
.type
== EXTENT_TYPE_REGULAR
|| ext2
->extent_data
.type
== EXTENT_TYPE_PREALLOC
) {
3558 EXTENT_DATA2
* ed2d
= (EXTENT_DATA2
*)ext2
->extent_data
.data
;
3560 if (ed2d
->address
== ed2s
->address
&& ed2d
->size
== ed2s
->size
) {
3561 ext
->unique
= FALSE
;
3573 lastextle
= &fcb
->extents
;
3574 while (!IsListEmpty(&newexts
)) {
3575 extent
* ext
= CONTAINING_RECORD(RemoveHeadList(&newexts
), extent
, list_entry
);
3577 add_extent(fcb
, lastextle
, ext
);
3578 lastextle
= &ext
->list_entry
;
3582 KeQuerySystemTime(&time
);
3583 win_time_to_unix(time
, &now
);
3586 ccb
->fileref
->parent
->fcb
->inode_item
.sequence
++;
3588 if (!ccb
->user_set_change_time
)
3589 ccb
->fileref
->parent
->fcb
->inode_item
.st_ctime
= now
;
3591 ccb
->fileref
->parent
->fcb
->inode_item_changed
= TRUE
;
3592 mark_fcb_dirty(ccb
->fileref
->parent
->fcb
);
3594 fcb
->inode_item
.st_blocks
+= nbytes
;
3595 fcb
->inode_item
.sequence
++;
3597 if (!ccb
->user_set_change_time
)
3598 fcb
->inode_item
.st_ctime
= now
;
3600 if (!ccb
->user_set_write_time
) {
3601 fcb
->inode_item
.st_mtime
= now
;
3602 send_notification_fcb(ccb
->fileref
, FILE_NOTIFY_CHANGE_LAST_WRITE
, FILE_ACTION_MODIFIED
, NULL
);
3605 fcb
->inode_item_changed
= TRUE
;
3606 fcb
->extents_changed
= TRUE
;
3609 mark_fcb_dirty(fcb
);
3611 if (fcb
->nonpaged
->segment_object
.DataSectionObject
)
3612 CcPurgeCacheSection(&fcb
->nonpaged
->segment_object
, &ded
->TargetFileOffset
, (ULONG
)ded
->ByteCount
.QuadPart
, FALSE
);
3614 Status
= STATUS_SUCCESS
;
3617 ObDereferenceObject(sourcefo
);
3619 if (NT_SUCCESS(Status
))
3620 clear_rollback(&rollback
);
3622 do_rollback(Vcb
, &rollback
);
3624 if (fcb
!= sourcefcb
)
3625 ExReleaseResourceLite(sourcefcb
->Header
.Resource
);
3627 ExReleaseResourceLite(fcb
->Header
.Resource
);
3629 ExReleaseResourceLite(&Vcb
->tree_lock
);
3634 // based on functions in sys/sysmacros.h
3635 #define major(rdev) ((((rdev) >> 8) & 0xFFF) | ((UINT32)((rdev) >> 32) & ~0xFFF))
3636 #define minor(rdev) (((rdev) & 0xFF) | ((UINT32)((rdev) >> 12) & ~0xFF))
3638 static NTSTATUS
mknod(device_extension
* Vcb
, PFILE_OBJECT FileObject
, void* data
, ULONG datalen
, PIRP Irp
) {
3643 file_ref
*parfileref
, *fileref
;
3644 UNICODE_STRING name
;
3653 SECURITY_SUBJECT_CONTEXT subjcont
;
3657 TRACE("(%p, %p, %p, %u)\n", Vcb
, FileObject
, data
, datalen
);
3659 if (!FileObject
|| !FileObject
->FsContext
|| !FileObject
->FsContext2
|| FileObject
->FsContext
== Vcb
->volume_fcb
)
3660 return STATUS_INVALID_PARAMETER
;
3663 return STATUS_MEDIA_WRITE_PROTECTED
;
3665 parfcb
= FileObject
->FsContext
;
3667 if (parfcb
->type
!= BTRFS_TYPE_DIRECTORY
) {
3668 WARN("trying to create file in something other than a directory\n");
3669 return STATUS_INVALID_PARAMETER
;
3672 if (is_subvol_readonly(parfcb
->subvol
, Irp
))
3673 return STATUS_ACCESS_DENIED
;
3675 parccb
= FileObject
->FsContext2
;
3676 parfileref
= parccb
->fileref
;
3679 return STATUS_INVALID_PARAMETER
;
3681 if (datalen
< sizeof(btrfs_mknod
))
3682 return STATUS_INVALID_PARAMETER
;
3684 bmn
= (btrfs_mknod
*)data
;
3686 if (datalen
< offsetof(btrfs_mknod
, name
[0]) + bmn
->namelen
|| bmn
->namelen
< sizeof(WCHAR
))
3687 return STATUS_INVALID_PARAMETER
;
3689 if (bmn
->type
== BTRFS_TYPE_UNKNOWN
|| bmn
->type
> BTRFS_TYPE_SYMLINK
)
3690 return STATUS_INVALID_PARAMETER
;
3692 if ((bmn
->type
== BTRFS_TYPE_DIRECTORY
&& !(parccb
->access
& FILE_ADD_SUBDIRECTORY
)) ||
3693 (bmn
->type
!= BTRFS_TYPE_DIRECTORY
&& !(parccb
->access
& FILE_ADD_FILE
))) {
3694 WARN("insufficient privileges\n");
3695 return STATUS_ACCESS_DENIED
;
3698 if (bmn
->inode
!= 0) {
3699 if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE
), Irp
->RequestorMode
))
3700 return STATUS_PRIVILEGE_NOT_HELD
;
3703 for (i
= 0; i
< bmn
->namelen
/ sizeof(WCHAR
); i
++) {
3704 if (bmn
->name
[i
] == 0 || bmn
->name
[i
] == '/')
3705 return STATUS_OBJECT_NAME_INVALID
;
3708 // don't allow files called . or ..
3709 if (bmn
->name
[0] == '.' && (bmn
->namelen
== sizeof(WCHAR
) || (bmn
->namelen
== 2 * sizeof(WCHAR
) && bmn
->name
[1] == '.')))
3710 return STATUS_OBJECT_NAME_INVALID
;
3712 Status
= RtlUnicodeToUTF8N(NULL
, 0, &len
, bmn
->name
, bmn
->namelen
);
3713 if (!NT_SUCCESS(Status
)) {
3714 ERR("RtlUnicodeToUTF8N return %08x\n", Status
);
3719 ERR("RtlUnicodeToUTF8N returned a length of 0\n");
3720 return STATUS_INTERNAL_ERROR
;
3724 ERR("len was too long (%x)\n", len
);
3725 return STATUS_INVALID_PARAMETER
;
3728 utf8
.MaximumLength
= utf8
.Length
= (USHORT
)len
;
3729 utf8
.Buffer
= ExAllocatePoolWithTag(PagedPool
, utf8
.MaximumLength
, ALLOC_TAG
);
3732 ERR("out of memory\n");
3733 return STATUS_INSUFFICIENT_RESOURCES
;
3736 Status
= RtlUnicodeToUTF8N(utf8
.Buffer
, len
, &len
, bmn
->name
, bmn
->namelen
);
3737 if (!NT_SUCCESS(Status
)) {
3738 ERR("RtlUnicodeToUTF8N failed with error %08x\n", Status
);
3739 ExFreePool(utf8
.Buffer
);
3743 name
.Length
= name
.MaximumLength
= bmn
->namelen
;
3744 name
.Buffer
= bmn
->name
;
3746 acquire_fcb_lock_exclusive(Vcb
);
3748 Status
= find_file_in_dir(&name
, parfcb
, &subvol
, &inode
, &dc
, TRUE
);
3749 if (!NT_SUCCESS(Status
) && Status
!= STATUS_OBJECT_NAME_NOT_FOUND
) {
3750 ERR("find_file_in_dir returned %08x\n", Status
);
3754 if (NT_SUCCESS(Status
)) {
3755 WARN("filename already exists\n");
3756 Status
= STATUS_OBJECT_NAME_COLLISION
;
3760 if (bmn
->inode
== 0) {
3761 inode
= InterlockedIncrement64(&parfcb
->subvol
->lastinode
);
3762 lastle
= parfcb
->subvol
->fcbs
.Blink
;
3764 if (bmn
->inode
> (UINT64
)parfcb
->subvol
->lastinode
) {
3765 inode
= parfcb
->subvol
->lastinode
= bmn
->inode
;
3766 lastle
= parfcb
->subvol
->fcbs
.Blink
;
3768 LIST_ENTRY
* le
= parfcb
->subvol
->fcbs
.Flink
;
3770 lastle
= parfcb
->subvol
->fcbs
.Blink
;;
3771 while (le
!= &parfcb
->subvol
->fcbs
) {
3772 struct _fcb
* fcb2
= CONTAINING_RECORD(le
, struct _fcb
, list_entry
);
3774 if (fcb2
->inode
== bmn
->inode
&& !fcb2
->deleted
) {
3775 WARN("inode collision\n");
3776 Status
= STATUS_INVALID_PARAMETER
;
3778 } else if (fcb2
->inode
> bmn
->inode
) {
3779 lastle
= fcb2
->list_entry
.Blink
;
3790 KeQuerySystemTime(&time
);
3791 win_time_to_unix(time
, &now
);
3793 fcb
= create_fcb(Vcb
, PagedPool
);
3795 ERR("out of memory\n");
3796 Status
= STATUS_INSUFFICIENT_RESOURCES
;
3802 fcb
->inode_item
.generation
= Vcb
->superblock
.generation
;
3803 fcb
->inode_item
.transid
= Vcb
->superblock
.generation
;
3804 fcb
->inode_item
.st_size
= 0;
3805 fcb
->inode_item
.st_blocks
= 0;
3806 fcb
->inode_item
.block_group
= 0;
3807 fcb
->inode_item
.st_nlink
= 1;
3808 fcb
->inode_item
.st_uid
= UID_NOBODY
;
3809 fcb
->inode_item
.st_gid
= GID_NOBODY
;
3810 fcb
->inode_item
.st_mode
= inherit_mode(parfcb
, bmn
->type
== BTRFS_TYPE_DIRECTORY
);
3812 if (bmn
->type
== BTRFS_TYPE_BLOCKDEV
|| bmn
->type
== BTRFS_TYPE_CHARDEV
)
3813 fcb
->inode_item
.st_rdev
= (minor(bmn
->st_rdev
) & 0xFFFFF) | ((major(bmn
->st_rdev
) & 0xFFFFFFFFFFF) << 20);
3815 fcb
->inode_item
.st_rdev
= 0;
3817 fcb
->inode_item
.flags
= 0;
3818 fcb
->inode_item
.sequence
= 1;
3819 fcb
->inode_item
.st_atime
= now
;
3820 fcb
->inode_item
.st_ctime
= now
;
3821 fcb
->inode_item
.st_mtime
= now
;
3822 fcb
->inode_item
.otime
= now
;
3824 if (bmn
->type
== BTRFS_TYPE_DIRECTORY
)
3825 fcb
->inode_item
.st_mode
|= __S_IFDIR
;
3826 else if (bmn
->type
== BTRFS_TYPE_CHARDEV
)
3827 fcb
->inode_item
.st_mode
|= __S_IFCHR
;
3828 else if (bmn
->type
== BTRFS_TYPE_BLOCKDEV
)
3829 fcb
->inode_item
.st_mode
|= __S_IFBLK
;
3830 else if (bmn
->type
== BTRFS_TYPE_FIFO
)
3831 fcb
->inode_item
.st_mode
|= __S_IFIFO
;
3832 else if (bmn
->type
== BTRFS_TYPE_SOCKET
)
3833 fcb
->inode_item
.st_mode
|= __S_IFSOCK
;
3834 else if (bmn
->type
== BTRFS_TYPE_SYMLINK
)
3835 fcb
->inode_item
.st_mode
|= __S_IFLNK
;
3837 fcb
->inode_item
.st_mode
|= __S_IFREG
;
3839 if (bmn
->type
!= BTRFS_TYPE_DIRECTORY
)
3840 fcb
->inode_item
.st_mode
&= ~(S_IXUSR
| S_IXGRP
| S_IXOTH
); // remove executable bit if not directory
3842 // inherit nodatacow flag from parent directory
3843 if (parfcb
->inode_item
.flags
& BTRFS_INODE_NODATACOW
) {
3844 fcb
->inode_item
.flags
|= BTRFS_INODE_NODATACOW
;
3846 if (bmn
->type
!= BTRFS_TYPE_DIRECTORY
)
3847 fcb
->inode_item
.flags
|= BTRFS_INODE_NODATASUM
;
3850 if (parfcb
->inode_item
.flags
& BTRFS_INODE_COMPRESS
)
3851 fcb
->inode_item
.flags
|= BTRFS_INODE_COMPRESS
;
3853 fcb
->prop_compression
= parfcb
->prop_compression
;
3854 fcb
->prop_compression_changed
= fcb
->prop_compression
!= PropCompression_None
;
3856 fcb
->inode_item_changed
= TRUE
;
3858 fcb
->Header
.IsFastIoPossible
= fast_io_possible(fcb
);
3859 fcb
->Header
.AllocationSize
.QuadPart
= 0;
3860 fcb
->Header
.FileSize
.QuadPart
= 0;
3861 fcb
->Header
.ValidDataLength
.QuadPart
= 0;
3865 if (bmn
->name
[0] == '.')
3866 fcb
->atts
|= FILE_ATTRIBUTE_HIDDEN
;
3868 if (bmn
->type
== BTRFS_TYPE_DIRECTORY
)
3869 fcb
->atts
|= FILE_ATTRIBUTE_DIRECTORY
;
3871 fcb
->atts_changed
= FALSE
;
3873 InterlockedIncrement(&parfcb
->refcount
);
3874 fcb
->subvol
= parfcb
->subvol
;
3876 fcb
->type
= bmn
->type
;
3878 SeCaptureSubjectContext(&subjcont
);
3880 Status
= SeAssignSecurityEx(parfileref
? parfileref
->fcb
->sd
: NULL
, NULL
, (void**)&fcb
->sd
, NULL
, fcb
->type
== BTRFS_TYPE_DIRECTORY
,
3881 SEF_SACL_AUTO_INHERIT
, &subjcont
, IoGetFileObjectGenericMapping(), PagedPool
);
3883 if (!NT_SUCCESS(Status
)) {
3884 ERR("SeAssignSecurityEx returned %08x\n", Status
);
3889 Status
= RtlGetOwnerSecurityDescriptor(fcb
->sd
, &owner
, &defaulted
);
3890 if (!NT_SUCCESS(Status
)) {
3891 WARN("RtlGetOwnerSecurityDescriptor returned %08x\n", Status
);
3892 fcb
->sd_dirty
= TRUE
;
3894 fcb
->inode_item
.st_uid
= sid_to_uid(owner
);
3895 fcb
->sd_dirty
= fcb
->inode_item
.st_uid
== UID_NOBODY
;
3898 find_gid(fcb
, parfcb
, &subjcont
);
3900 fileref
= create_fileref(Vcb
);
3902 ERR("out of memory\n");
3904 Status
= STATUS_INSUFFICIENT_RESOURCES
;
3910 fcb
->created
= TRUE
;
3911 mark_fcb_dirty(fcb
);
3913 fileref
->created
= TRUE
;
3914 mark_fileref_dirty(fileref
);
3916 fcb
->subvol
->root_item
.ctransid
= Vcb
->superblock
.generation
;
3917 fcb
->subvol
->root_item
.ctime
= now
;
3919 fileref
->parent
= parfileref
;
3921 Status
= add_dir_child(fileref
->parent
->fcb
, fcb
->inode
, FALSE
, &utf8
, &name
, fcb
->type
, &dc
);
3922 if (!NT_SUCCESS(Status
))
3923 WARN("add_dir_child returned %08x\n", Status
);
3926 dc
->fileref
= fileref
;
3928 ExAcquireResourceExclusiveLite(&parfileref
->nonpaged
->children_lock
, TRUE
);
3929 InsertTailList(&parfileref
->children
, &fileref
->list_entry
);
3930 ExReleaseResourceLite(&parfileref
->nonpaged
->children_lock
);
3932 increase_fileref_refcount(parfileref
);
3934 if (fcb
->type
== BTRFS_TYPE_DIRECTORY
) {
3935 fcb
->hash_ptrs
= ExAllocatePoolWithTag(PagedPool
, sizeof(LIST_ENTRY
*) * 256, ALLOC_TAG
);
3936 if (!fcb
->hash_ptrs
) {
3937 ERR("out of memory\n");
3938 free_fileref(Vcb
, fileref
);
3939 Status
= STATUS_INSUFFICIENT_RESOURCES
;
3943 RtlZeroMemory(fcb
->hash_ptrs
, sizeof(LIST_ENTRY
*) * 256);
3945 fcb
->hash_ptrs_uc
= ExAllocatePoolWithTag(PagedPool
, sizeof(LIST_ENTRY
*) * 256, ALLOC_TAG
);
3946 if (!fcb
->hash_ptrs_uc
) {
3947 ERR("out of memory\n");
3948 free_fileref(Vcb
, fileref
);
3949 Status
= STATUS_INSUFFICIENT_RESOURCES
;
3953 RtlZeroMemory(fcb
->hash_ptrs_uc
, sizeof(LIST_ENTRY
*) * 256);
3956 InsertHeadList(lastle
, &fcb
->list_entry
);
3957 InsertTailList(&Vcb
->all_fcbs
, &fcb
->list_entry_all
);
3959 if (bmn
->type
== BTRFS_TYPE_DIRECTORY
)
3960 fileref
->fcb
->fileref
= fileref
;
3962 ExAcquireResourceExclusiveLite(parfcb
->Header
.Resource
, TRUE
);
3963 parfcb
->inode_item
.st_size
+= utf8
.Length
* 2;
3964 parfcb
->inode_item
.transid
= Vcb
->superblock
.generation
;
3965 parfcb
->inode_item
.sequence
++;
3967 if (!parccb
->user_set_change_time
)
3968 parfcb
->inode_item
.st_ctime
= now
;
3970 if (!parccb
->user_set_write_time
)
3971 parfcb
->inode_item
.st_mtime
= now
;
3973 ExReleaseResourceLite(parfcb
->Header
.Resource
);
3975 parfcb
->inode_item_changed
= TRUE
;
3976 mark_fcb_dirty(parfcb
);
3978 send_notification_fileref(fileref
, bmn
->type
== BTRFS_TYPE_DIRECTORY
? FILE_NOTIFY_CHANGE_DIR_NAME
: FILE_NOTIFY_CHANGE_FILE_NAME
, FILE_ACTION_ADDED
, NULL
);
3980 if (!parccb
->user_set_write_time
)
3981 send_notification_fcb(parfileref
, FILE_NOTIFY_CHANGE_LAST_WRITE
, FILE_ACTION_MODIFIED
, NULL
);
3983 Status
= STATUS_SUCCESS
;
3986 release_fcb_lock(Vcb
);
3988 ExFreePool(utf8
.Buffer
);
3993 static void mark_subvol_dirty(device_extension
* Vcb
, root
* r
) {
3997 ExAcquireResourceExclusiveLite(&Vcb
->dirty_subvols_lock
, TRUE
);
3998 InsertTailList(&Vcb
->dirty_subvols
, &r
->list_entry_dirty
);
3999 ExReleaseResourceLite(&Vcb
->dirty_subvols_lock
);
4002 Vcb
->need_write
= TRUE
;
4005 static NTSTATUS
recvd_subvol(device_extension
* Vcb
, PFILE_OBJECT FileObject
, void* data
, ULONG datalen
, KPROCESSOR_MODE processor_mode
) {
4006 btrfs_received_subvol
* brs
= (btrfs_received_subvol
*)data
;
4012 TRACE("(%p, %p, %p, %u)\n", Vcb
, FileObject
, data
, datalen
);
4014 if (!data
|| datalen
< sizeof(btrfs_received_subvol
))
4015 return STATUS_INVALID_PARAMETER
;
4017 if (!FileObject
|| !FileObject
->FsContext
|| FileObject
->FsContext
== Vcb
->volume_fcb
)
4018 return STATUS_INVALID_PARAMETER
;
4020 fcb
= FileObject
->FsContext
;
4023 return STATUS_INVALID_PARAMETER
;
4025 if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE
), processor_mode
))
4026 return STATUS_PRIVILEGE_NOT_HELD
;
4028 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, TRUE
);
4030 if (fcb
->subvol
->root_item
.rtransid
!= 0) {
4031 WARN("subvol already has received information set\n");
4032 Status
= STATUS_INVALID_PARAMETER
;
4036 KeQuerySystemTime(&time
);
4037 win_time_to_unix(time
, &now
);
4039 RtlCopyMemory(&fcb
->subvol
->root_item
.received_uuid
, &brs
->uuid
, sizeof(BTRFS_UUID
));
4040 fcb
->subvol
->root_item
.stransid
= brs
->generation
;
4041 fcb
->subvol
->root_item
.rtransid
= Vcb
->superblock
.generation
;
4042 fcb
->subvol
->root_item
.rtime
= now
;
4044 fcb
->subvol
->received
= TRUE
;
4045 mark_subvol_dirty(Vcb
, fcb
->subvol
);
4047 Status
= STATUS_SUCCESS
;
4050 ExReleaseResourceLite(&Vcb
->tree_lock
);
4055 static NTSTATUS
fsctl_get_xattrs(device_extension
* Vcb
, PFILE_OBJECT FileObject
, void* data
, ULONG datalen
, KPROCESSOR_MODE processor_mode
) {
4057 btrfs_set_xattr
* bsxa
;
4058 ULONG reqlen
= (ULONG
)offsetof(btrfs_set_xattr
, data
[0]);
4062 if (!data
|| datalen
< reqlen
)
4063 return STATUS_INVALID_PARAMETER
;
4065 if (!FileObject
|| !FileObject
->FsContext
|| !FileObject
->FsContext2
|| FileObject
->FsContext
== Vcb
->volume_fcb
)
4066 return STATUS_INVALID_PARAMETER
;
4068 fcb
= FileObject
->FsContext
;
4069 ccb
= FileObject
->FsContext2
;
4071 if (!(ccb
->access
& (FILE_READ_ATTRIBUTES
| FILE_WRITE_ATTRIBUTES
)) && processor_mode
== UserMode
) {
4072 WARN("insufficient privileges\n");
4073 return STATUS_ACCESS_DENIED
;
4076 ExAcquireResourceSharedLite(fcb
->Header
.Resource
, TRUE
);
4078 le
= fcb
->xattrs
.Flink
;
4079 while (le
!= &fcb
->xattrs
) {
4080 xattr
* xa
= CONTAINING_RECORD(le
, xattr
, list_entry
);
4082 if (xa
->valuelen
> 0)
4083 reqlen
+= (ULONG
)offsetof(btrfs_set_xattr
, data
[0]) + xa
->namelen
+ xa
->valuelen
;
4088 if (datalen
< reqlen
) {
4089 ExReleaseResourceLite(fcb
->Header
.Resource
);
4090 return STATUS_BUFFER_OVERFLOW
;
4093 bsxa
= (btrfs_set_xattr
*)data
;
4096 le
= fcb
->xattrs
.Flink
;
4097 while (le
!= &fcb
->xattrs
) {
4098 xattr
* xa
= CONTAINING_RECORD(le
, xattr
, list_entry
);
4100 if (xa
->valuelen
> 0) {
4101 bsxa
->namelen
= xa
->namelen
;
4102 bsxa
->valuelen
= xa
->valuelen
;
4103 memcpy(bsxa
->data
, xa
->data
, xa
->namelen
+ xa
->valuelen
);
4105 bsxa
= (btrfs_set_xattr
*)&bsxa
->data
[xa
->namelen
+ xa
->valuelen
];
4115 ExReleaseResourceLite(fcb
->Header
.Resource
);
4117 return STATUS_SUCCESS
;
4120 static NTSTATUS
fsctl_set_xattr(device_extension
* Vcb
, PFILE_OBJECT FileObject
, void* data
, ULONG datalen
, PIRP Irp
) {
4122 btrfs_set_xattr
* bsxa
;
4128 static const char stream_pref
[] = "user.";
4130 TRACE("(%p, %p, %p, %u)\n", Vcb
, FileObject
, data
, datalen
);
4132 if (!data
|| datalen
< sizeof(btrfs_set_xattr
))
4133 return STATUS_INVALID_PARAMETER
;
4135 bsxa
= (btrfs_set_xattr
*)data
;
4137 if (datalen
< offsetof(btrfs_set_xattr
, data
[0]) + bsxa
->namelen
+ bsxa
->valuelen
)
4138 return STATUS_INVALID_PARAMETER
;
4140 if (bsxa
->namelen
+ bsxa
->valuelen
+ sizeof(tree_header
) + sizeof(leaf_node
) + offsetof(DIR_ITEM
, name
[0]) > Vcb
->superblock
.node_size
)
4141 return STATUS_INVALID_PARAMETER
;
4143 if (!FileObject
|| !FileObject
->FsContext
|| !FileObject
->FsContext2
|| FileObject
->FsContext
== Vcb
->volume_fcb
)
4144 return STATUS_INVALID_PARAMETER
;
4147 return STATUS_MEDIA_WRITE_PROTECTED
;
4149 fcb
= FileObject
->FsContext
;
4150 ccb
= FileObject
->FsContext2
;
4152 if (is_subvol_readonly(fcb
->subvol
, Irp
))
4153 return STATUS_ACCESS_DENIED
;
4155 if (!(ccb
->access
& FILE_WRITE_ATTRIBUTES
) && Irp
->RequestorMode
== UserMode
) {
4156 WARN("insufficient privileges\n");
4157 return STATUS_ACCESS_DENIED
;
4160 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, TRUE
);
4162 ExAcquireResourceExclusiveLite(fcb
->Header
.Resource
, TRUE
);
4164 if (bsxa
->namelen
== strlen(EA_NTACL
) && RtlCompareMemory(bsxa
->data
, EA_NTACL
, strlen(EA_NTACL
)) == strlen(EA_NTACL
)) {
4165 if ((!(ccb
->access
& WRITE_DAC
) || !(ccb
->access
& WRITE_OWNER
)) && Irp
->RequestorMode
== UserMode
) {
4166 WARN("insufficient privileges\n");
4167 Status
= STATUS_ACCESS_DENIED
;
4171 if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE
), Irp
->RequestorMode
)) {
4172 Status
= STATUS_PRIVILEGE_NOT_HELD
;
4177 ExFreePool(fcb
->sd
);
4179 if (bsxa
->valuelen
> 0 && RtlValidRelativeSecurityDescriptor(bsxa
->data
+ bsxa
->namelen
, bsxa
->valuelen
, 0)) {
4180 fcb
->sd
= ExAllocatePoolWithTag(PagedPool
, bsxa
->valuelen
, ALLOC_TAG
);
4182 ERR("out of memory\n");
4183 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4187 RtlCopyMemory(fcb
->sd
, bsxa
->data
+ bsxa
->namelen
, bsxa
->valuelen
);
4191 fcb
->sd_dirty
= TRUE
;
4194 fcb_get_sd(fcb
, ccb
->fileref
->parent
->fcb
, FALSE
, Irp
);
4195 fcb
->sd_deleted
= TRUE
;
4198 mark_fcb_dirty(fcb
);
4200 Status
= STATUS_SUCCESS
;
4202 } else if (bsxa
->namelen
== strlen(EA_DOSATTRIB
) && RtlCompareMemory(bsxa
->data
, EA_DOSATTRIB
, strlen(EA_DOSATTRIB
)) == strlen(EA_DOSATTRIB
)) {
4205 if (bsxa
->valuelen
> 0 && get_file_attributes_from_xattr(bsxa
->data
+ bsxa
->namelen
, bsxa
->valuelen
, &atts
)) {
4208 if (fcb
->type
== BTRFS_TYPE_DIRECTORY
)
4209 fcb
->atts
|= FILE_ATTRIBUTE_DIRECTORY
;
4210 else if (fcb
->type
== BTRFS_TYPE_SYMLINK
)
4211 fcb
->atts
|= FILE_ATTRIBUTE_REPARSE_POINT
;
4213 if (fcb
->inode
== SUBVOL_ROOT_INODE
) {
4214 if (fcb
->subvol
->root_item
.flags
& BTRFS_SUBVOL_READONLY
)
4215 fcb
->atts
|= FILE_ATTRIBUTE_READONLY
;
4217 fcb
->atts
&= ~FILE_ATTRIBUTE_READONLY
;
4220 fcb
->atts_deleted
= FALSE
;
4222 BOOL hidden
= ccb
->fileref
&& ccb
->fileref
->dc
&& ccb
->fileref
->dc
->utf8
.Buffer
&& ccb
->fileref
->dc
->utf8
.Buffer
[0] == '.';
4224 fcb
->atts
= get_file_attributes(Vcb
, fcb
->subvol
, fcb
->inode
, fcb
->type
, hidden
, TRUE
, Irp
);
4225 fcb
->atts_deleted
= TRUE
;
4228 fcb
->atts_changed
= TRUE
;
4229 mark_fcb_dirty(fcb
);
4231 Status
= STATUS_SUCCESS
;
4233 } else if (bsxa
->namelen
== strlen(EA_REPARSE
) && RtlCompareMemory(bsxa
->data
, EA_REPARSE
, strlen(EA_REPARSE
)) == strlen(EA_REPARSE
)) {
4234 if (fcb
->reparse_xattr
.Buffer
) {
4235 ExFreePool(fcb
->reparse_xattr
.Buffer
);
4236 fcb
->reparse_xattr
.Buffer
= NULL
;
4237 fcb
->reparse_xattr
.Length
= fcb
->reparse_xattr
.MaximumLength
= 0;
4240 if (bsxa
->valuelen
> 0) {
4241 fcb
->reparse_xattr
.Buffer
= ExAllocatePoolWithTag(PagedPool
, bsxa
->valuelen
, ALLOC_TAG
);
4242 if (!fcb
->reparse_xattr
.Buffer
) {
4243 ERR("out of memory\n");
4244 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4248 RtlCopyMemory(fcb
->reparse_xattr
.Buffer
, bsxa
->data
+ bsxa
->namelen
, bsxa
->valuelen
);
4249 fcb
->reparse_xattr
.Length
= fcb
->reparse_xattr
.MaximumLength
= bsxa
->valuelen
;
4252 fcb
->reparse_xattr_changed
= TRUE
;
4253 mark_fcb_dirty(fcb
);
4255 Status
= STATUS_SUCCESS
;
4257 } else if (bsxa
->namelen
== strlen(EA_EA
) && RtlCompareMemory(bsxa
->data
, EA_EA
, strlen(EA_EA
)) == strlen(EA_EA
)) {
4258 if (!(ccb
->access
& FILE_WRITE_EA
) && Irp
->RequestorMode
== UserMode
) {
4259 WARN("insufficient privileges\n");
4260 Status
= STATUS_ACCESS_DENIED
;
4264 if (fcb
->ea_xattr
.Buffer
) {
4265 ExFreePool(fcb
->ea_xattr
.Buffer
);
4266 fcb
->ea_xattr
.Length
= fcb
->ea_xattr
.MaximumLength
= 0;
4267 fcb
->ea_xattr
.Buffer
= NULL
;
4272 if (bsxa
->valuelen
> 0) {
4275 Status
= IoCheckEaBufferValidity((FILE_FULL_EA_INFORMATION
*)(bsxa
->data
+ bsxa
->namelen
), bsxa
->valuelen
, &offset
);
4277 if (!NT_SUCCESS(Status
))
4278 WARN("IoCheckEaBufferValidity returned %08x (error at offset %u)\n", Status
, offset
);
4280 FILE_FULL_EA_INFORMATION
* eainfo
;
4282 fcb
->ea_xattr
.Buffer
= ExAllocatePoolWithTag(PagedPool
, bsxa
->valuelen
, ALLOC_TAG
);
4283 if (!fcb
->ea_xattr
.Buffer
) {
4284 ERR("out of memory\n");
4285 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4289 RtlCopyMemory(fcb
->ea_xattr
.Buffer
, bsxa
->data
+ bsxa
->namelen
, bsxa
->valuelen
);
4291 fcb
->ea_xattr
.Length
= fcb
->ea_xattr
.MaximumLength
= bsxa
->valuelen
;
4296 eainfo
= (FILE_FULL_EA_INFORMATION
*)(bsxa
->data
+ bsxa
->namelen
);
4298 fcb
->ealen
+= 5 + eainfo
->EaNameLength
+ eainfo
->EaValueLength
;
4300 if (eainfo
->NextEntryOffset
== 0)
4303 eainfo
= (FILE_FULL_EA_INFORMATION
*)(((UINT8
*)eainfo
) + eainfo
->NextEntryOffset
);
4308 fcb
->ea_changed
= TRUE
;
4309 mark_fcb_dirty(fcb
);
4311 Status
= STATUS_SUCCESS
;
4313 } else if (bsxa
->namelen
== strlen(EA_PROP_COMPRESSION
) && RtlCompareMemory(bsxa
->data
, EA_PROP_COMPRESSION
, strlen(EA_PROP_COMPRESSION
)) == strlen(EA_PROP_COMPRESSION
)) {
4314 const char lzo
[] = "lzo";
4315 const char zlib
[] = "zlib";
4317 if (bsxa
->valuelen
== strlen(lzo
) && RtlCompareMemory(bsxa
->data
+ bsxa
->namelen
, lzo
, bsxa
->valuelen
) == bsxa
->valuelen
)
4318 fcb
->prop_compression
= PropCompression_LZO
;
4319 else if (bsxa
->valuelen
== strlen(zlib
) && RtlCompareMemory(bsxa
->data
+ bsxa
->namelen
, zlib
, bsxa
->valuelen
) == bsxa
->valuelen
)
4320 fcb
->prop_compression
= PropCompression_Zlib
;
4322 fcb
->prop_compression
= PropCompression_None
;
4324 if (fcb
->prop_compression
!= PropCompression_None
) {
4325 fcb
->inode_item
.flags
|= BTRFS_INODE_COMPRESS
;
4326 fcb
->inode_item_changed
= TRUE
;
4329 fcb
->prop_compression_changed
= TRUE
;
4330 mark_fcb_dirty(fcb
);
4332 Status
= STATUS_SUCCESS
;
4334 } else if (bsxa
->namelen
>= strlen(stream_pref
) && RtlCompareMemory(bsxa
->data
, stream_pref
, strlen(stream_pref
)) == strlen(stream_pref
)) {
4335 // don't allow xattrs beginning with user., as these appear as streams instead
4336 Status
= STATUS_OBJECT_NAME_INVALID
;
4340 xa
= ExAllocatePoolWithTag(PagedPool
, offsetof(xattr
, data
[0]) + bsxa
->namelen
+ bsxa
->valuelen
, ALLOC_TAG
);
4342 ERR("out of memory\n");
4343 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4347 le
= fcb
->xattrs
.Flink
;
4348 while (le
!= &fcb
->xattrs
) {
4349 xattr
* xa2
= CONTAINING_RECORD(le
, xattr
, list_entry
);
4351 if (xa2
->namelen
== bsxa
->namelen
&& RtlCompareMemory(xa2
->data
, bsxa
->data
, xa2
->namelen
) == xa2
->namelen
) {
4352 RemoveEntryList(&xa2
->list_entry
);
4360 xa
->namelen
= bsxa
->namelen
;
4361 xa
->valuelen
= bsxa
->valuelen
;
4363 RtlCopyMemory(xa
->data
, bsxa
->data
, bsxa
->namelen
+ bsxa
->valuelen
);
4365 InsertTailList(&fcb
->xattrs
, &xa
->list_entry
);
4367 fcb
->xattrs_changed
= TRUE
;
4368 mark_fcb_dirty(fcb
);
4370 Status
= STATUS_SUCCESS
;
4373 ExReleaseResourceLite(fcb
->Header
.Resource
);
4375 ExReleaseResourceLite(&Vcb
->tree_lock
);
4380 static NTSTATUS
reserve_subvol(device_extension
* Vcb
, PFILE_OBJECT FileObject
, PIRP Irp
) {
4384 TRACE("(%p, %p)\n", Vcb
, FileObject
);
4386 // "Reserving" a readonly subvol allows the calling process to write into it until the handle is closed.
4388 if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE
), Irp
->RequestorMode
))
4389 return STATUS_PRIVILEGE_NOT_HELD
;
4391 if (!FileObject
|| !FileObject
->FsContext
|| !FileObject
->FsContext2
|| FileObject
->FsContext
== Vcb
->volume_fcb
)
4392 return STATUS_INVALID_PARAMETER
;
4394 fcb
= FileObject
->FsContext
;
4395 ccb
= FileObject
->FsContext2
;
4397 if (!(fcb
->subvol
->root_item
.flags
& BTRFS_SUBVOL_READONLY
))
4398 return STATUS_INVALID_PARAMETER
;
4400 if (fcb
->subvol
->reserved
)
4401 return STATUS_INVALID_PARAMETER
;
4403 fcb
->subvol
->reserved
= PsGetCurrentProcess();
4404 ccb
->reserving
= TRUE
;
4406 return STATUS_SUCCESS
;
4409 static NTSTATUS
get_subvol_path(device_extension
* Vcb
, UINT64 id
, WCHAR
* out
, ULONG outlen
, PIRP Irp
) {
4416 le
= Vcb
->roots
.Flink
;
4417 while (le
!= &Vcb
->roots
) {
4418 root
* r2
= CONTAINING_RECORD(le
, root
, list_entry
);
4429 ERR("couldn't find subvol %llx\n", id
);
4430 return STATUS_INTERNAL_ERROR
;
4433 acquire_fcb_lock_shared(Vcb
);
4435 Status
= open_fileref_by_inode(Vcb
, r
, r
->root_item
.objid
, &fr
, Irp
);
4436 if (!NT_SUCCESS(Status
)) {
4437 release_fcb_lock(Vcb
);
4438 ERR("open_fileref_by_inode returned %08x\n", Status
);
4444 us
.MaximumLength
= (USHORT
)min(0xffff, outlen
) - sizeof(WCHAR
);
4446 Status
= fileref_get_filename(fr
, &us
, NULL
, NULL
);
4448 if (NT_SUCCESS(Status
) || Status
== STATUS_BUFFER_OVERFLOW
)
4449 out
[us
.Length
/ sizeof(WCHAR
)] = 0;
4451 ERR("fileref_get_filename returned %08x\n", Status
);
4453 free_fileref(Vcb
, fr
);
4455 release_fcb_lock(Vcb
);
4460 static NTSTATUS
find_subvol(device_extension
* Vcb
, void* in
, ULONG inlen
, void* out
, ULONG outlen
, PIRP Irp
) {
4461 btrfs_find_subvol
* bfs
;
4466 if (!in
|| inlen
< sizeof(btrfs_find_subvol
))
4467 return STATUS_INVALID_PARAMETER
;
4469 if (!out
|| outlen
< sizeof(WCHAR
))
4470 return STATUS_INVALID_PARAMETER
;
4472 if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE
), Irp
->RequestorMode
))
4473 return STATUS_PRIVILEGE_NOT_HELD
;
4475 bfs
= (btrfs_find_subvol
*)in
;
4477 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, TRUE
);
4479 if (!Vcb
->uuid_root
) {
4480 ERR("couldn't find uuid root\n");
4481 Status
= STATUS_NOT_FOUND
;
4485 RtlCopyMemory(&searchkey
.obj_id
, &bfs
->uuid
, sizeof(UINT64
));
4486 searchkey
.obj_type
= TYPE_SUBVOL_UUID
;
4487 RtlCopyMemory(&searchkey
.offset
, &bfs
->uuid
.uuid
[sizeof(UINT64
)], sizeof(UINT64
));
4489 Status
= find_item(Vcb
, Vcb
->uuid_root
, &tp
, &searchkey
, FALSE
, Irp
);
4491 if (!NT_SUCCESS(Status
)) {
4492 ERR("find_item returned %08x\n", Status
);
4496 if (!keycmp(searchkey
, tp
.item
->key
) && tp
.item
->size
>= sizeof(UINT64
)) {
4497 UINT64
* id
= (UINT64
*)tp
.item
->data
;
4499 if (bfs
->ctransid
!= 0) {
4503 searchkey2
.obj_id
= *id
;
4504 searchkey2
.obj_type
= TYPE_ROOT_ITEM
;
4505 searchkey2
.offset
= 0xffffffffffffffff;
4507 Status
= find_item(Vcb
, Vcb
->root_root
, &tp2
, &searchkey2
, FALSE
, Irp
);
4508 if (!NT_SUCCESS(Status
)) {
4509 ERR("find_item returned %08x\n", Status
);
4513 if (tp2
.item
->key
.obj_id
== searchkey2
.obj_id
&& tp2
.item
->key
.obj_type
== searchkey2
.obj_type
&&
4514 tp2
.item
->size
>= offsetof(ROOT_ITEM
, otransid
)) {
4515 ROOT_ITEM
* ri
= (ROOT_ITEM
*)tp2
.item
->data
;
4517 if (ri
->ctransid
== bfs
->ctransid
) {
4518 TRACE("found subvol %llx\n", *id
);
4519 Status
= get_subvol_path(Vcb
, *id
, out
, outlen
, Irp
);
4524 TRACE("found subvol %llx\n", *id
);
4525 Status
= get_subvol_path(Vcb
, *id
, out
, outlen
, Irp
);
4530 searchkey
.obj_type
= TYPE_SUBVOL_REC_UUID
;
4532 Status
= find_item(Vcb
, Vcb
->uuid_root
, &tp
, &searchkey
, FALSE
, Irp
);
4534 if (!NT_SUCCESS(Status
)) {
4535 ERR("find_item returned %08x\n", Status
);
4539 if (!keycmp(searchkey
, tp
.item
->key
) && tp
.item
->size
>= sizeof(UINT64
)) {
4540 UINT64
* ids
= (UINT64
*)tp
.item
->data
;
4543 for (i
= 0; i
< tp
.item
->size
/ sizeof(UINT64
); i
++) {
4544 if (bfs
->ctransid
!= 0) {
4548 searchkey2
.obj_id
= ids
[i
];
4549 searchkey2
.obj_type
= TYPE_ROOT_ITEM
;
4550 searchkey2
.offset
= 0xffffffffffffffff;
4552 Status
= find_item(Vcb
, Vcb
->root_root
, &tp2
, &searchkey2
, FALSE
, Irp
);
4553 if (!NT_SUCCESS(Status
)) {
4554 ERR("find_item returned %08x\n", Status
);
4558 if (tp2
.item
->key
.obj_id
== searchkey2
.obj_id
&& tp2
.item
->key
.obj_type
== searchkey2
.obj_type
&&
4559 tp2
.item
->size
>= offsetof(ROOT_ITEM
, otransid
)) {
4560 ROOT_ITEM
* ri
= (ROOT_ITEM
*)tp2
.item
->data
;
4562 if (ri
->ctransid
== bfs
->ctransid
) {
4563 TRACE("found subvol %llx\n", ids
[i
]);
4564 Status
= get_subvol_path(Vcb
, ids
[i
], out
, outlen
, Irp
);
4569 TRACE("found subvol %llx\n", ids
[i
]);
4570 Status
= get_subvol_path(Vcb
, ids
[i
], out
, outlen
, Irp
);
4576 Status
= STATUS_NOT_FOUND
;
4579 ExReleaseResourceLite(&Vcb
->tree_lock
);
4584 static NTSTATUS
resize_device(device_extension
* Vcb
, void* data
, ULONG len
, PIRP Irp
) {
4585 btrfs_resize
* br
= (btrfs_resize
*)data
;
4590 TRACE("(%p, %p, %u)\n", Vcb
, data
, len
);
4592 if (!data
|| len
< sizeof(btrfs_resize
) || (br
->size
% Vcb
->superblock
.sector_size
) != 0)
4593 return STATUS_INVALID_PARAMETER
;
4595 if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE
), Irp
->RequestorMode
))
4596 return STATUS_PRIVILEGE_NOT_HELD
;
4599 return STATUS_MEDIA_WRITE_PROTECTED
;
4601 ExAcquireResourceExclusiveLite(&Vcb
->tree_lock
, TRUE
);
4603 le
= Vcb
->devices
.Flink
;
4604 while (le
!= &Vcb
->devices
) {
4605 device
* dev2
= CONTAINING_RECORD(le
, device
, list_entry
);
4607 if (dev2
->devitem
.dev_id
== br
->device
) {
4616 ERR("could not find device %llx\n", br
->device
);
4617 Status
= STATUS_INVALID_PARAMETER
;
4622 ERR("trying to resize missing device\n");
4623 Status
= STATUS_INVALID_PARAMETER
;
4627 if (dev
->readonly
) {
4628 ERR("trying to resize readonly device\n");
4629 Status
= STATUS_INVALID_PARAMETER
;
4633 if (br
->size
> 0 && dev
->devitem
.num_bytes
== br
->size
) {
4634 TRACE("size unchanged, returning STATUS_SUCCESS\n");
4635 Status
= STATUS_SUCCESS
;
4639 if (br
->size
> 0 && dev
->devitem
.num_bytes
> br
->size
) { // shrink device
4640 BOOL need_balance
= TRUE
;
4641 UINT64 old_size
, delta
;
4643 le
= dev
->space
.Flink
;
4644 while (le
!= &dev
->space
) {
4645 space
* s
= CONTAINING_RECORD(le
, space
, list_entry
);
4647 if (s
->address
<= br
->size
&& s
->address
+ s
->size
>= dev
->devitem
.num_bytes
) {
4648 need_balance
= FALSE
;
4655 delta
= dev
->devitem
.num_bytes
- br
->size
;
4660 if (Vcb
->balance
.thread
) {
4661 WARN("balance already running\n");
4662 Status
= STATUS_DEVICE_NOT_READY
;
4666 RtlZeroMemory(Vcb
->balance
.opts
, sizeof(btrfs_balance_opts
) * 3);
4668 for (i
= 0; i
< 3; i
++) {
4669 Vcb
->balance
.opts
[i
].flags
= BTRFS_BALANCE_OPTS_ENABLED
| BTRFS_BALANCE_OPTS_DEVID
| BTRFS_BALANCE_OPTS_DRANGE
;
4670 Vcb
->balance
.opts
[i
].devid
= dev
->devitem
.dev_id
;
4671 Vcb
->balance
.opts
[i
].drange_start
= br
->size
;
4672 Vcb
->balance
.opts
[i
].drange_end
= dev
->devitem
.num_bytes
;
4675 Vcb
->balance
.paused
= FALSE
;
4676 Vcb
->balance
.shrinking
= TRUE
;
4677 Vcb
->balance
.status
= STATUS_SUCCESS
;
4678 KeInitializeEvent(&Vcb
->balance
.event
, NotificationEvent
, !Vcb
->balance
.paused
);
4680 space_list_subtract2(&dev
->space
, NULL
, br
->size
, delta
, NULL
, NULL
);
4682 Status
= PsCreateSystemThread(&Vcb
->balance
.thread
, 0, NULL
, NULL
, NULL
, balance_thread
, Vcb
);
4683 if (!NT_SUCCESS(Status
)) {
4684 ERR("PsCreateSystemThread returned %08x\n", Status
);
4688 Status
= STATUS_MORE_PROCESSING_REQUIRED
;
4693 old_size
= dev
->devitem
.num_bytes
;
4694 dev
->devitem
.num_bytes
= br
->size
;
4696 Status
= update_dev_item(Vcb
, dev
, Irp
);
4697 if (!NT_SUCCESS(Status
)) {
4698 ERR("update_dev_item returned %08x\n", Status
);
4699 dev
->devitem
.num_bytes
= old_size
;
4703 space_list_subtract2(&dev
->space
, NULL
, br
->size
, delta
, NULL
, NULL
);
4705 Vcb
->superblock
.total_bytes
-= delta
;
4706 } else { // extend device
4707 GET_LENGTH_INFORMATION gli
;
4708 UINT64 old_size
, delta
;
4710 Status
= dev_ioctl(dev
->devobj
, IOCTL_DISK_GET_LENGTH_INFO
, NULL
, 0,
4711 &gli
, sizeof(gli
), TRUE
, NULL
);
4712 if (!NT_SUCCESS(Status
)) {
4713 ERR("IOCTL_DISK_GET_LENGTH_INFO returned %08x\n", Status
);
4717 if (br
->size
== 0) {
4718 br
->size
= gli
.Length
.QuadPart
;
4720 if (dev
->devitem
.num_bytes
== br
->size
) {
4721 TRACE("size unchanged, returning STATUS_SUCCESS\n");
4722 Status
= STATUS_SUCCESS
;
4726 if (br
->size
== 0) {
4727 ERR("IOCTL_DISK_GET_LENGTH_INFO returned 0 length\n");
4728 Status
= STATUS_INTERNAL_ERROR
;
4731 } else if ((UINT64
)gli
.Length
.QuadPart
< br
->size
) {
4732 ERR("device was %llx bytes, trying to extend to %llx\n", gli
.Length
.QuadPart
, br
->size
);
4733 Status
= STATUS_INVALID_PARAMETER
;
4737 delta
= br
->size
- dev
->devitem
.num_bytes
;
4739 old_size
= dev
->devitem
.num_bytes
;
4740 dev
->devitem
.num_bytes
= br
->size
;
4742 Status
= update_dev_item(Vcb
, dev
, Irp
);
4743 if (!NT_SUCCESS(Status
)) {
4744 ERR("update_dev_item returned %08x\n", Status
);
4745 dev
->devitem
.num_bytes
= old_size
;
4749 space_list_add2(&dev
->space
, NULL
, dev
->devitem
.num_bytes
, delta
, NULL
, NULL
);
4751 Vcb
->superblock
.total_bytes
+= delta
;
4754 Status
= STATUS_SUCCESS
;
4755 Vcb
->need_write
= TRUE
;
4758 ExReleaseResourceLite(&Vcb
->tree_lock
);
4760 if (NT_SUCCESS(Status
))
4761 FsRtlNotifyVolumeEvent(Vcb
->root_file
, FSRTL_VOLUME_CHANGE_SIZE
);
4766 NTSTATUS
fsctl_request(PDEVICE_OBJECT DeviceObject
, PIRP
* Pirp
, UINT32 type
) {
4768 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
4772 #if (NTDDI_VERSION >= NTDDI_WIN7)
4773 case FSCTL_REQUEST_OPLOCK
:
4774 WARN("STUB: FSCTL_REQUEST_OPLOCK\n");
4775 Status
= STATUS_INVALID_DEVICE_REQUEST
;
4779 case FSCTL_REQUEST_OPLOCK_LEVEL_1
:
4780 WARN("STUB: FSCTL_REQUEST_OPLOCK_LEVEL_1\n");
4781 Status
= STATUS_INVALID_DEVICE_REQUEST
;
4784 case FSCTL_REQUEST_OPLOCK_LEVEL_2
:
4785 WARN("STUB: FSCTL_REQUEST_OPLOCK_LEVEL_2\n");
4786 Status
= STATUS_INVALID_DEVICE_REQUEST
;
4789 case FSCTL_REQUEST_BATCH_OPLOCK
:
4790 WARN("STUB: FSCTL_REQUEST_BATCH_OPLOCK\n");
4791 Status
= STATUS_INVALID_DEVICE_REQUEST
;
4794 case FSCTL_OPLOCK_BREAK_ACKNOWLEDGE
:
4795 WARN("STUB: FSCTL_OPLOCK_BREAK_ACKNOWLEDGE\n");
4796 Status
= STATUS_INVALID_DEVICE_REQUEST
;
4799 case FSCTL_OPLOCK_BREAK_ACK_NO_2
:
4800 WARN("STUB: FSCTL_OPLOCK_BREAK_ACK_NO_2\n");
4801 Status
= STATUS_INVALID_DEVICE_REQUEST
;
4804 case FSCTL_OPBATCH_ACK_CLOSE_PENDING
:
4805 WARN("STUB: FSCTL_OPBATCH_ACK_CLOSE_PENDING\n");
4806 Status
= STATUS_INVALID_DEVICE_REQUEST
;
4809 case FSCTL_OPLOCK_BREAK_NOTIFY
:
4810 WARN("STUB: FSCTL_OPLOCK_BREAK_NOTIFY\n");
4811 Status
= STATUS_INVALID_DEVICE_REQUEST
;
4814 case FSCTL_REQUEST_FILTER_OPLOCK
:
4815 WARN("STUB: FSCTL_REQUEST_FILTER_OPLOCK\n");
4816 Status
= STATUS_INVALID_DEVICE_REQUEST
;
4819 case FSCTL_LOCK_VOLUME
:
4820 Status
= lock_volume(DeviceObject
->DeviceExtension
, Irp
);
4823 case FSCTL_UNLOCK_VOLUME
:
4824 Status
= unlock_volume(DeviceObject
->DeviceExtension
, Irp
);
4827 case FSCTL_DISMOUNT_VOLUME
:
4828 Status
= dismount_volume(DeviceObject
->DeviceExtension
, Irp
);
4831 case FSCTL_IS_VOLUME_MOUNTED
:
4832 Status
= is_volume_mounted(DeviceObject
->DeviceExtension
, Irp
);
4835 case FSCTL_IS_PATHNAME_VALID
:
4836 WARN("STUB: FSCTL_IS_PATHNAME_VALID\n");
4837 Status
= STATUS_INVALID_DEVICE_REQUEST
;
4840 case FSCTL_MARK_VOLUME_DIRTY
:
4841 WARN("STUB: FSCTL_MARK_VOLUME_DIRTY\n");
4842 Status
= STATUS_INVALID_DEVICE_REQUEST
;
4845 case FSCTL_QUERY_RETRIEVAL_POINTERS
:
4846 WARN("STUB: FSCTL_QUERY_RETRIEVAL_POINTERS\n");
4847 Status
= STATUS_INVALID_DEVICE_REQUEST
;
4850 case FSCTL_GET_COMPRESSION
:
4851 Status
= get_compression(Irp
);
4854 case FSCTL_SET_COMPRESSION
:
4855 Status
= set_compression(Irp
);
4858 case FSCTL_SET_BOOTLOADER_ACCESSED
:
4859 WARN("STUB: FSCTL_SET_BOOTLOADER_ACCESSED\n");
4860 Status
= STATUS_INVALID_DEVICE_REQUEST
;
4863 case FSCTL_INVALIDATE_VOLUMES
:
4864 Status
= invalidate_volumes(Irp
);
4867 case FSCTL_QUERY_FAT_BPB
:
4868 WARN("STUB: FSCTL_QUERY_FAT_BPB\n");
4869 Status
= STATUS_INVALID_DEVICE_REQUEST
;
4872 case FSCTL_FILESYSTEM_GET_STATISTICS
:
4873 Status
= fs_get_statistics(Irp
->AssociatedIrp
.SystemBuffer
, IrpSp
->Parameters
.FileSystemControl
.OutputBufferLength
, &Irp
->IoStatus
.Information
);
4876 case FSCTL_GET_NTFS_VOLUME_DATA
:
4877 WARN("STUB: FSCTL_GET_NTFS_VOLUME_DATA\n");
4878 Status
= STATUS_INVALID_DEVICE_REQUEST
;
4881 case FSCTL_GET_NTFS_FILE_RECORD
:
4882 WARN("STUB: FSCTL_GET_NTFS_FILE_RECORD\n");
4883 Status
= STATUS_INVALID_DEVICE_REQUEST
;
4886 case FSCTL_GET_VOLUME_BITMAP
:
4887 WARN("STUB: FSCTL_GET_VOLUME_BITMAP\n");
4888 Status
= STATUS_INVALID_DEVICE_REQUEST
;
4891 case FSCTL_GET_RETRIEVAL_POINTERS
:
4892 WARN("STUB: FSCTL_GET_RETRIEVAL_POINTERS\n");
4893 Status
= STATUS_INVALID_DEVICE_REQUEST
;
4896 case FSCTL_MOVE_FILE
:
4897 WARN("STUB: FSCTL_MOVE_FILE\n");
4898 Status
= STATUS_INVALID_DEVICE_REQUEST
;
4901 case FSCTL_IS_VOLUME_DIRTY
:
4902 Status
= is_volume_dirty(DeviceObject
->DeviceExtension
, Irp
);
4905 case FSCTL_ALLOW_EXTENDED_DASD_IO
:
4906 Status
= allow_extended_dasd_io(DeviceObject
->DeviceExtension
, IrpSp
->FileObject
);
4909 case FSCTL_FIND_FILES_BY_SID
:
4910 WARN("STUB: FSCTL_FIND_FILES_BY_SID\n");
4911 Status
= STATUS_INVALID_DEVICE_REQUEST
;
4914 case FSCTL_SET_OBJECT_ID
:
4915 WARN("STUB: FSCTL_SET_OBJECT_ID\n");
4916 Status
= STATUS_INVALID_DEVICE_REQUEST
;
4919 case FSCTL_GET_OBJECT_ID
:
4920 Status
= get_object_id(DeviceObject
->DeviceExtension
, IrpSp
->FileObject
, Irp
->UserBuffer
,
4921 IrpSp
->Parameters
.FileSystemControl
.OutputBufferLength
, &Irp
->IoStatus
.Information
);
4924 case FSCTL_DELETE_OBJECT_ID
:
4925 WARN("STUB: FSCTL_DELETE_OBJECT_ID\n");
4926 Status
= STATUS_INVALID_DEVICE_REQUEST
;
4929 case FSCTL_SET_REPARSE_POINT
:
4930 Status
= set_reparse_point(DeviceObject
, Irp
);
4933 case FSCTL_GET_REPARSE_POINT
:
4934 Status
= get_reparse_point(DeviceObject
, IrpSp
->FileObject
, Irp
->AssociatedIrp
.SystemBuffer
,
4935 IrpSp
->Parameters
.FileSystemControl
.OutputBufferLength
, &Irp
->IoStatus
.Information
);
4938 case FSCTL_DELETE_REPARSE_POINT
:
4939 Status
= delete_reparse_point(DeviceObject
, Irp
);
4942 case FSCTL_ENUM_USN_DATA
:
4943 WARN("STUB: FSCTL_ENUM_USN_DATA\n");
4944 Status
= STATUS_INVALID_DEVICE_REQUEST
;
4947 case FSCTL_SECURITY_ID_CHECK
:
4948 WARN("STUB: FSCTL_SECURITY_ID_CHECK\n");
4949 Status
= STATUS_INVALID_DEVICE_REQUEST
;
4952 case FSCTL_READ_USN_JOURNAL
:
4953 WARN("STUB: FSCTL_READ_USN_JOURNAL\n");
4954 Status
= STATUS_INVALID_DEVICE_REQUEST
;
4957 case FSCTL_SET_OBJECT_ID_EXTENDED
:
4958 WARN("STUB: FSCTL_SET_OBJECT_ID_EXTENDED\n");
4959 Status
= STATUS_INVALID_DEVICE_REQUEST
;
4962 case FSCTL_CREATE_OR_GET_OBJECT_ID
:
4963 Status
= get_object_id(DeviceObject
->DeviceExtension
, IrpSp
->FileObject
, Irp
->UserBuffer
,
4964 IrpSp
->Parameters
.FileSystemControl
.OutputBufferLength
, &Irp
->IoStatus
.Information
);
4967 case FSCTL_SET_SPARSE
:
4968 Status
= set_sparse(DeviceObject
->DeviceExtension
, IrpSp
->FileObject
, Irp
->AssociatedIrp
.SystemBuffer
,
4969 IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
, Irp
);
4972 case FSCTL_SET_ZERO_DATA
:
4973 Status
= set_zero_data(DeviceObject
->DeviceExtension
, IrpSp
->FileObject
, Irp
->AssociatedIrp
.SystemBuffer
,
4974 IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
, Irp
);
4977 case FSCTL_QUERY_ALLOCATED_RANGES
:
4978 Status
= query_ranges(IrpSp
->FileObject
, IrpSp
->Parameters
.FileSystemControl
.Type3InputBuffer
,
4979 IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
, Irp
->UserBuffer
,
4980 IrpSp
->Parameters
.FileSystemControl
.OutputBufferLength
, &Irp
->IoStatus
.Information
);
4983 case FSCTL_ENABLE_UPGRADE
:
4984 WARN("STUB: FSCTL_ENABLE_UPGRADE\n");
4985 Status
= STATUS_INVALID_DEVICE_REQUEST
;
4988 case FSCTL_SET_ENCRYPTION
:
4989 WARN("STUB: FSCTL_SET_ENCRYPTION\n");
4990 Status
= STATUS_INVALID_DEVICE_REQUEST
;
4993 case FSCTL_ENCRYPTION_FSCTL_IO
:
4994 WARN("STUB: FSCTL_ENCRYPTION_FSCTL_IO\n");
4995 Status
= STATUS_INVALID_DEVICE_REQUEST
;
4998 case FSCTL_WRITE_RAW_ENCRYPTED
:
4999 WARN("STUB: FSCTL_WRITE_RAW_ENCRYPTED\n");
5000 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5003 case FSCTL_READ_RAW_ENCRYPTED
:
5004 WARN("STUB: FSCTL_READ_RAW_ENCRYPTED\n");
5005 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5008 case FSCTL_CREATE_USN_JOURNAL
:
5009 WARN("STUB: FSCTL_CREATE_USN_JOURNAL\n");
5010 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5013 case FSCTL_READ_FILE_USN_DATA
:
5014 WARN("STUB: FSCTL_READ_FILE_USN_DATA\n");
5015 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5018 case FSCTL_WRITE_USN_CLOSE_RECORD
:
5019 WARN("STUB: FSCTL_WRITE_USN_CLOSE_RECORD\n");
5020 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5023 case FSCTL_EXTEND_VOLUME
:
5024 WARN("STUB: FSCTL_EXTEND_VOLUME\n");
5025 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5028 case FSCTL_QUERY_USN_JOURNAL
:
5029 WARN("STUB: FSCTL_QUERY_USN_JOURNAL\n");
5030 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5033 case FSCTL_DELETE_USN_JOURNAL
:
5034 WARN("STUB: FSCTL_DELETE_USN_JOURNAL\n");
5035 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5038 case FSCTL_MARK_HANDLE
:
5039 WARN("STUB: FSCTL_MARK_HANDLE\n");
5040 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5043 case FSCTL_SIS_COPYFILE
:
5044 WARN("STUB: FSCTL_SIS_COPYFILE\n");
5045 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5048 case FSCTL_SIS_LINK_FILES
:
5049 WARN("STUB: FSCTL_SIS_LINK_FILES\n");
5050 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5053 case FSCTL_RECALL_FILE
:
5054 WARN("STUB: FSCTL_RECALL_FILE\n");
5055 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5058 case FSCTL_READ_FROM_PLEX
:
5059 WARN("STUB: FSCTL_READ_FROM_PLEX\n");
5060 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5063 case FSCTL_FILE_PREFETCH
:
5064 WARN("STUB: FSCTL_FILE_PREFETCH\n");
5065 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5068 #if _WIN32_WINNT >= 0x0600
5069 case FSCTL_MAKE_MEDIA_COMPATIBLE
:
5070 WARN("STUB: FSCTL_MAKE_MEDIA_COMPATIBLE\n");
5071 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5074 case FSCTL_SET_DEFECT_MANAGEMENT
:
5075 WARN("STUB: FSCTL_SET_DEFECT_MANAGEMENT\n");
5076 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5079 case FSCTL_QUERY_SPARING_INFO
:
5080 WARN("STUB: FSCTL_QUERY_SPARING_INFO\n");
5081 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5084 case FSCTL_QUERY_ON_DISK_VOLUME_INFO
:
5085 WARN("STUB: FSCTL_QUERY_ON_DISK_VOLUME_INFO\n");
5086 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5089 case FSCTL_SET_VOLUME_COMPRESSION_STATE
:
5090 WARN("STUB: FSCTL_SET_VOLUME_COMPRESSION_STATE\n");
5091 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5094 case FSCTL_TXFS_MODIFY_RM
:
5095 WARN("STUB: FSCTL_TXFS_MODIFY_RM\n");
5096 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5099 case FSCTL_TXFS_QUERY_RM_INFORMATION
:
5100 WARN("STUB: FSCTL_TXFS_QUERY_RM_INFORMATION\n");
5101 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5104 case FSCTL_TXFS_ROLLFORWARD_REDO
:
5105 WARN("STUB: FSCTL_TXFS_ROLLFORWARD_REDO\n");
5106 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5109 case FSCTL_TXFS_ROLLFORWARD_UNDO
:
5110 WARN("STUB: FSCTL_TXFS_ROLLFORWARD_UNDO\n");
5111 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5114 case FSCTL_TXFS_START_RM
:
5115 WARN("STUB: FSCTL_TXFS_START_RM\n");
5116 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5119 case FSCTL_TXFS_SHUTDOWN_RM
:
5120 WARN("STUB: FSCTL_TXFS_SHUTDOWN_RM\n");
5121 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5124 case FSCTL_TXFS_READ_BACKUP_INFORMATION
:
5125 WARN("STUB: FSCTL_TXFS_READ_BACKUP_INFORMATION\n");
5126 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5129 case FSCTL_TXFS_WRITE_BACKUP_INFORMATION
:
5130 WARN("STUB: FSCTL_TXFS_WRITE_BACKUP_INFORMATION\n");
5131 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5134 case FSCTL_TXFS_CREATE_SECONDARY_RM
:
5135 WARN("STUB: FSCTL_TXFS_CREATE_SECONDARY_RM\n");
5136 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5139 case FSCTL_TXFS_GET_METADATA_INFO
:
5140 WARN("STUB: FSCTL_TXFS_GET_METADATA_INFO\n");
5141 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5144 case FSCTL_TXFS_GET_TRANSACTED_VERSION
:
5145 WARN("STUB: FSCTL_TXFS_GET_TRANSACTED_VERSION\n");
5146 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5149 case FSCTL_TXFS_SAVEPOINT_INFORMATION
:
5150 WARN("STUB: FSCTL_TXFS_SAVEPOINT_INFORMATION\n");
5151 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5154 case FSCTL_TXFS_CREATE_MINIVERSION
:
5155 WARN("STUB: FSCTL_TXFS_CREATE_MINIVERSION\n");
5156 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5159 case FSCTL_TXFS_TRANSACTION_ACTIVE
:
5160 WARN("STUB: FSCTL_TXFS_TRANSACTION_ACTIVE\n");
5161 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5164 case FSCTL_SET_ZERO_ON_DEALLOCATION
:
5165 WARN("STUB: FSCTL_SET_ZERO_ON_DEALLOCATION\n");
5166 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5169 case FSCTL_SET_REPAIR
:
5170 WARN("STUB: FSCTL_SET_REPAIR\n");
5171 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5174 case FSCTL_GET_REPAIR
:
5175 WARN("STUB: FSCTL_GET_REPAIR\n");
5176 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5179 case FSCTL_WAIT_FOR_REPAIR
:
5180 WARN("STUB: FSCTL_WAIT_FOR_REPAIR\n");
5181 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5184 case FSCTL_INITIATE_REPAIR
:
5185 WARN("STUB: FSCTL_INITIATE_REPAIR\n");
5186 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5189 case FSCTL_CSC_INTERNAL
:
5190 WARN("STUB: FSCTL_CSC_INTERNAL\n");
5191 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5194 case FSCTL_SHRINK_VOLUME
:
5195 WARN("STUB: FSCTL_SHRINK_VOLUME\n");
5196 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5199 case FSCTL_SET_SHORT_NAME_BEHAVIOR
:
5200 WARN("STUB: FSCTL_SET_SHORT_NAME_BEHAVIOR\n");
5201 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5204 case FSCTL_DFSR_SET_GHOST_HANDLE_STATE
:
5205 WARN("STUB: FSCTL_DFSR_SET_GHOST_HANDLE_STATE\n");
5206 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5209 case FSCTL_TXFS_LIST_TRANSACTION_LOCKED_FILES
:
5210 WARN("STUB: FSCTL_TXFS_LIST_TRANSACTION_LOCKED_FILES\n");
5211 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5214 case FSCTL_TXFS_LIST_TRANSACTIONS
:
5215 WARN("STUB: FSCTL_TXFS_LIST_TRANSACTIONS\n");
5216 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5219 case FSCTL_QUERY_PAGEFILE_ENCRYPTION
:
5220 WARN("STUB: FSCTL_QUERY_PAGEFILE_ENCRYPTION\n");
5221 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5224 case FSCTL_RESET_VOLUME_ALLOCATION_HINTS
:
5225 WARN("STUB: FSCTL_RESET_VOLUME_ALLOCATION_HINTS\n");
5226 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5229 case FSCTL_TXFS_READ_BACKUP_INFORMATION2
:
5230 WARN("STUB: FSCTL_TXFS_READ_BACKUP_INFORMATION2\n");
5231 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5234 case FSCTL_CSV_CONTROL
:
5235 WARN("STUB: FSCTL_CSV_CONTROL\n");
5236 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5239 // TRACE rather than WARN because Windows 10 spams this undocumented fsctl
5240 case FSCTL_QUERY_VOLUME_CONTAINER_STATE
:
5241 TRACE("STUB: FSCTL_QUERY_VOLUME_CONTAINER_STATE\n");
5242 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5245 case FSCTL_GET_INTEGRITY_INFORMATION
:
5246 Status
= get_integrity_information(DeviceObject
->DeviceExtension
, IrpSp
->FileObject
, map_user_buffer(Irp
, NormalPagePriority
),
5247 IrpSp
->Parameters
.FileSystemControl
.OutputBufferLength
);
5250 case FSCTL_SET_INTEGRITY_INFORMATION
:
5251 Status
= set_integrity_information(IrpSp
->FileObject
, Irp
->AssociatedIrp
.SystemBuffer
, IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
);
5254 case FSCTL_DUPLICATE_EXTENTS_TO_FILE
:
5255 Status
= duplicate_extents(DeviceObject
->DeviceExtension
, IrpSp
->FileObject
, Irp
->AssociatedIrp
.SystemBuffer
,
5256 IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
, Irp
);
5259 case FSCTL_BTRFS_GET_FILE_IDS
:
5260 Status
= get_file_ids(IrpSp
->FileObject
, map_user_buffer(Irp
, NormalPagePriority
), IrpSp
->Parameters
.FileSystemControl
.OutputBufferLength
);
5263 case FSCTL_BTRFS_CREATE_SUBVOL
:
5264 Status
= create_subvol(DeviceObject
->DeviceExtension
, IrpSp
->FileObject
, Irp
->AssociatedIrp
.SystemBuffer
, IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
, Irp
);
5267 case FSCTL_BTRFS_CREATE_SNAPSHOT
:
5268 Status
= create_snapshot(DeviceObject
->DeviceExtension
, IrpSp
->FileObject
, Irp
->AssociatedIrp
.SystemBuffer
, IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
, Irp
);
5271 case FSCTL_BTRFS_GET_INODE_INFO
:
5272 Status
= get_inode_info(IrpSp
->FileObject
, map_user_buffer(Irp
, NormalPagePriority
), IrpSp
->Parameters
.FileSystemControl
.OutputBufferLength
);
5275 case FSCTL_BTRFS_SET_INODE_INFO
:
5276 Status
= set_inode_info(IrpSp
->FileObject
, Irp
->AssociatedIrp
.SystemBuffer
, IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
, Irp
);
5279 case FSCTL_BTRFS_GET_DEVICES
:
5280 Status
= get_devices(DeviceObject
->DeviceExtension
, map_user_buffer(Irp
, NormalPagePriority
), IrpSp
->Parameters
.FileSystemControl
.OutputBufferLength
);
5283 case FSCTL_BTRFS_GET_USAGE
:
5284 Status
= get_usage(DeviceObject
->DeviceExtension
, map_user_buffer(Irp
, NormalPagePriority
), IrpSp
->Parameters
.FileSystemControl
.OutputBufferLength
, Irp
);
5287 case FSCTL_BTRFS_START_BALANCE
:
5288 Status
= start_balance(DeviceObject
->DeviceExtension
, Irp
->AssociatedIrp
.SystemBuffer
, IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
, Irp
->RequestorMode
);
5291 case FSCTL_BTRFS_QUERY_BALANCE
:
5292 Status
= query_balance(DeviceObject
->DeviceExtension
, map_user_buffer(Irp
, NormalPagePriority
), IrpSp
->Parameters
.FileSystemControl
.OutputBufferLength
);
5295 case FSCTL_BTRFS_PAUSE_BALANCE
:
5296 Status
= pause_balance(DeviceObject
->DeviceExtension
, Irp
->RequestorMode
);
5299 case FSCTL_BTRFS_RESUME_BALANCE
:
5300 Status
= resume_balance(DeviceObject
->DeviceExtension
, Irp
->RequestorMode
);
5303 case FSCTL_BTRFS_STOP_BALANCE
:
5304 Status
= stop_balance(DeviceObject
->DeviceExtension
, Irp
->RequestorMode
);
5307 case FSCTL_BTRFS_ADD_DEVICE
:
5308 Status
= add_device(DeviceObject
->DeviceExtension
, Irp
, Irp
->RequestorMode
);
5311 case FSCTL_BTRFS_REMOVE_DEVICE
:
5312 Status
= remove_device(DeviceObject
->DeviceExtension
, Irp
->AssociatedIrp
.SystemBuffer
, IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
, Irp
->RequestorMode
);
5315 case FSCTL_BTRFS_GET_UUID
:
5316 Status
= query_uuid(DeviceObject
->DeviceExtension
, map_user_buffer(Irp
, NormalPagePriority
), IrpSp
->Parameters
.FileSystemControl
.OutputBufferLength
);
5319 case FSCTL_BTRFS_START_SCRUB
:
5320 Status
= start_scrub(DeviceObject
->DeviceExtension
, Irp
->RequestorMode
);
5323 case FSCTL_BTRFS_QUERY_SCRUB
:
5324 Status
= query_scrub(DeviceObject
->DeviceExtension
, Irp
->RequestorMode
, map_user_buffer(Irp
, NormalPagePriority
), IrpSp
->Parameters
.FileSystemControl
.OutputBufferLength
);
5327 case FSCTL_BTRFS_PAUSE_SCRUB
:
5328 Status
= pause_scrub(DeviceObject
->DeviceExtension
, Irp
->RequestorMode
);
5331 case FSCTL_BTRFS_RESUME_SCRUB
:
5332 Status
= resume_scrub(DeviceObject
->DeviceExtension
, Irp
->RequestorMode
);
5335 case FSCTL_BTRFS_STOP_SCRUB
:
5336 Status
= stop_scrub(DeviceObject
->DeviceExtension
, Irp
->RequestorMode
);
5339 case FSCTL_BTRFS_RESET_STATS
:
5340 Status
= reset_stats(DeviceObject
->DeviceExtension
, Irp
->AssociatedIrp
.SystemBuffer
, IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
, Irp
->RequestorMode
);
5343 case FSCTL_BTRFS_MKNOD
:
5344 Status
= mknod(DeviceObject
->DeviceExtension
, IrpSp
->FileObject
, Irp
->AssociatedIrp
.SystemBuffer
, IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
, Irp
);
5347 case FSCTL_BTRFS_RECEIVED_SUBVOL
:
5348 Status
= recvd_subvol(DeviceObject
->DeviceExtension
, IrpSp
->FileObject
, Irp
->AssociatedIrp
.SystemBuffer
,
5349 IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
, Irp
->RequestorMode
);
5352 case FSCTL_BTRFS_GET_XATTRS
:
5353 Status
= fsctl_get_xattrs(DeviceObject
->DeviceExtension
, IrpSp
->FileObject
, Irp
->UserBuffer
, IrpSp
->Parameters
.FileSystemControl
.OutputBufferLength
, Irp
->RequestorMode
);
5356 case FSCTL_BTRFS_SET_XATTR
:
5357 Status
= fsctl_set_xattr(DeviceObject
->DeviceExtension
, IrpSp
->FileObject
, Irp
->AssociatedIrp
.SystemBuffer
,
5358 IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
, Irp
);
5361 case FSCTL_BTRFS_RESERVE_SUBVOL
:
5362 Status
= reserve_subvol(DeviceObject
->DeviceExtension
, IrpSp
->FileObject
, Irp
);
5365 case FSCTL_BTRFS_FIND_SUBVOL
:
5366 Status
= find_subvol(DeviceObject
->DeviceExtension
, Irp
->AssociatedIrp
.SystemBuffer
, IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
,
5367 Irp
->UserBuffer
, IrpSp
->Parameters
.FileSystemControl
.OutputBufferLength
, Irp
);
5370 case FSCTL_BTRFS_SEND_SUBVOL
:
5371 Status
= send_subvol(DeviceObject
->DeviceExtension
, Irp
->AssociatedIrp
.SystemBuffer
, IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
,
5372 IrpSp
->FileObject
, Irp
);
5375 case FSCTL_BTRFS_READ_SEND_BUFFER
:
5376 Status
= read_send_buffer(DeviceObject
->DeviceExtension
, IrpSp
->FileObject
, map_user_buffer(Irp
, NormalPagePriority
), IrpSp
->Parameters
.FileSystemControl
.OutputBufferLength
,
5377 &Irp
->IoStatus
.Information
, Irp
->RequestorMode
);
5380 case FSCTL_BTRFS_RESIZE
:
5381 Status
= resize_device(DeviceObject
->DeviceExtension
, Irp
->AssociatedIrp
.SystemBuffer
,
5382 IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
, Irp
);
5386 WARN("unknown control code %x (DeviceType = %x, Access = %x, Function = %x, Method = %x)\n",
5387 IrpSp
->Parameters
.FileSystemControl
.FsControlCode
, (IrpSp
->Parameters
.FileSystemControl
.FsControlCode
& 0xff0000) >> 16,
5388 (IrpSp
->Parameters
.FileSystemControl
.FsControlCode
& 0xc000) >> 14, (IrpSp
->Parameters
.FileSystemControl
.FsControlCode
& 0x3ffc) >> 2,
5389 IrpSp
->Parameters
.FileSystemControl
.FsControlCode
& 0x3);
5390 Status
= STATUS_INVALID_DEVICE_REQUEST
;