xref: /reactos/drivers/filesystems/btrfs/fsctl.c (revision 7b1049c8)
1 /* Copyright (c) Mark Harmstone 2016-17
2  *
3  * This file is part of WinBtrfs.
4  *
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.
9  *
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.
14  *
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/>. */
17 
18 #include "btrfs_drv.h"
19 #include "btrfsioctl.h"
20 #include <ntddstor.h>
21 #include <ntdddisk.h>
22 #ifndef __REACTOS__
23 #include <sys/stat.h>
24 #endif
25 
26 #ifndef FSCTL_CSV_CONTROL
27 #define FSCTL_CSV_CONTROL CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 181, METHOD_BUFFERED, FILE_ANY_ACCESS)
28 #endif
29 
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)
32 #endif
33 
34 #define DOTDOT ".."
35 
36 #define SEF_AVOID_PRIVILEGE_CHECK 0x08 // on MSDN but not in any header files(?)
37 
38 #ifndef _MSC_VER // not in mingw yet
39 #define DEVICE_DSM_FLAG_TRIM_NOT_FS_ALLOCATED 0x80000000
40 #endif
41 
42 #define SEF_SACL_AUTO_INHERIT 0x02
43 
44 extern LIST_ENTRY VcbList;
45 extern ERESOURCE global_loading_lock;
46 extern PDRIVER_OBJECT drvobj;
47 extern tFsRtlCheckLockForOplockRequest fFsRtlCheckLockForOplockRequest;
48 extern tFsRtlAreThereCurrentOrInProgressFileLocks fFsRtlAreThereCurrentOrInProgressFileLocks;
49 
50 static void mark_subvol_dirty(device_extension* Vcb, root* r);
51 
52 static NTSTATUS get_file_ids(PFILE_OBJECT FileObject, void* data, ULONG length) {
53     btrfs_get_file_ids* bgfi;
54     fcb* fcb;
55 
56     if (length < sizeof(btrfs_get_file_ids))
57         return STATUS_BUFFER_OVERFLOW;
58 
59     if (!FileObject)
60         return STATUS_INVALID_PARAMETER;
61 
62     fcb = FileObject->FsContext;
63 
64     if (!fcb)
65         return STATUS_INVALID_PARAMETER;
66 
67     bgfi = data;
68 
69     bgfi->subvol = fcb->subvol->id;
70     bgfi->inode = fcb->inode;
71     bgfi->top = fcb->Vcb->root_fileref->fcb == fcb ? true : false;
72 
73     return STATUS_SUCCESS;
74 }
75 
76 static void get_uuid(BTRFS_UUID* uuid) {
77     LARGE_INTEGER seed;
78     uint8_t i;
79 
80     seed = KeQueryPerformanceCounter(NULL);
81 
82     for (i = 0; i < 16; i+=2) {
83         ULONG rand = RtlRandomEx(&seed.LowPart);
84 
85         uuid->uuid[i] = (rand & 0xff00) >> 8;
86         uuid->uuid[i+1] = rand & 0xff;
87     }
88 }
89 
90 static NTSTATUS snapshot_tree_copy(device_extension* Vcb, uint64_t addr, root* subvol, uint64_t* newaddr, PIRP Irp, LIST_ENTRY* rollback) {
91     uint8_t* buf;
92     NTSTATUS Status;
93     write_data_context wtc;
94     LIST_ENTRY* le;
95     tree t;
96     tree_header* th;
97     chunk* c;
98 
99     buf = ExAllocatePoolWithTag(NonPagedPool, Vcb->superblock.node_size, ALLOC_TAG);
100     if (!buf) {
101         ERR("out of memory\n");
102         return STATUS_INSUFFICIENT_RESOURCES;
103     }
104 
105     wtc.parity1 = wtc.parity2 = wtc.scratch = NULL;
106     wtc.mdl = wtc.parity1_mdl = wtc.parity2_mdl = NULL;
107 
108     Status = read_data(Vcb, addr, Vcb->superblock.node_size, NULL, true, buf, NULL, NULL, Irp, 0, false, NormalPagePriority);
109     if (!NT_SUCCESS(Status)) {
110         ERR("read_data returned %08lx\n", Status);
111         goto end;
112     }
113 
114     th = (tree_header*)buf;
115 
116     RtlZeroMemory(&t, sizeof(tree));
117     t.root = subvol;
118     t.header.level = th->level;
119     t.header.tree_id = t.root->id;
120 
121     Status = get_tree_new_address(Vcb, &t, Irp, rollback);
122     if (!NT_SUCCESS(Status)) {
123         ERR("get_tree_new_address returned %08lx\n", Status);
124         goto end;
125     }
126 
127     if (!t.has_new_address) {
128         ERR("tree new address not set\n");
129         Status = STATUS_INTERNAL_ERROR;
130         goto end;
131     }
132 
133     c = get_chunk_from_address(Vcb, t.new_address);
134 
135     if (c)
136         c->used += Vcb->superblock.node_size;
137     else {
138         ERR("could not find chunk for address %I64x\n", t.new_address);
139         Status = STATUS_INTERNAL_ERROR;
140         goto end;
141     }
142 
143     th->address = t.new_address;
144     th->tree_id = subvol->id;
145     th->generation = Vcb->superblock.generation;
146     th->fs_uuid = Vcb->superblock.metadata_uuid;
147 
148     if (th->level == 0) {
149         uint32_t i;
150         leaf_node* ln = (leaf_node*)&th[1];
151 
152         for (i = 0; i < th->num_items; i++) {
153             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)) {
154                 EXTENT_DATA* ed = (EXTENT_DATA*)(((uint8_t*)&th[1]) + ln[i].offset);
155 
156                 if ((ed->type == EXTENT_TYPE_REGULAR || ed->type == EXTENT_TYPE_PREALLOC) && ln[i].size >= sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2)) {
157                     EXTENT_DATA2* ed2 = (EXTENT_DATA2*)&ed->data[0];
158 
159                     if (ed2->size != 0) { // not sparse
160                         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);
161 
162                         if (!NT_SUCCESS(Status)) {
163                             ERR("increase_extent_refcount_data returned %08lx\n", Status);
164                             goto end;
165                         }
166                     }
167                 }
168             }
169         }
170     } else {
171         uint32_t i;
172         internal_node* in = (internal_node*)&th[1];
173 
174         for (i = 0; i < th->num_items; i++) {
175             TREE_BLOCK_REF tbr;
176 
177             tbr.offset = subvol->id;
178 
179             Status = increase_extent_refcount(Vcb, in[i].address, Vcb->superblock.node_size, TYPE_TREE_BLOCK_REF, &tbr, NULL, th->level - 1, Irp);
180             if (!NT_SUCCESS(Status)) {
181                 ERR("increase_extent_refcount returned %08lx\n", Status);
182                 goto end;
183             }
184         }
185     }
186 
187     calc_tree_checksum(Vcb, th);
188 
189     KeInitializeEvent(&wtc.Event, NotificationEvent, false);
190     InitializeListHead(&wtc.stripes);
191     wtc.stripes_left = 0;
192 
193     Status = write_data(Vcb, t.new_address, buf, Vcb->superblock.node_size, &wtc, NULL, NULL, false, 0, NormalPagePriority);
194     if (!NT_SUCCESS(Status)) {
195         ERR("write_data returned %08lx\n", Status);
196         goto end;
197     }
198 
199     if (wtc.stripes.Flink != &wtc.stripes) {
200         bool need_wait = false;
201 
202         // launch writes and wait
203         le = wtc.stripes.Flink;
204         while (le != &wtc.stripes) {
205             write_data_stripe* stripe = CONTAINING_RECORD(le, write_data_stripe, list_entry);
206 
207             if (stripe->status != WriteDataStatus_Ignore) {
208                 need_wait = true;
209                 IoCallDriver(stripe->device->devobj, stripe->Irp);
210             }
211 
212             le = le->Flink;
213         }
214 
215         if (need_wait)
216             KeWaitForSingleObject(&wtc.Event, Executive, KernelMode, false, NULL);
217 
218         le = wtc.stripes.Flink;
219         while (le != &wtc.stripes) {
220             write_data_stripe* stripe = CONTAINING_RECORD(le, write_data_stripe, list_entry);
221 
222             if (stripe->status != WriteDataStatus_Ignore && !NT_SUCCESS(stripe->iosb.Status)) {
223                 Status = stripe->iosb.Status;
224                 log_device_error(Vcb, stripe->device, BTRFS_DEV_STAT_WRITE_ERRORS);
225                 break;
226             }
227 
228             le = le->Flink;
229         }
230 
231         free_write_data_stripes(&wtc);
232         buf = NULL;
233     }
234 
235     if (NT_SUCCESS(Status))
236         *newaddr = t.new_address;
237 
238 end:
239 
240     if (buf)
241         ExFreePool(buf);
242 
243     return Status;
244 }
245 
246 void flush_subvol_fcbs(root* subvol) {
247     LIST_ENTRY* le = subvol->fcbs.Flink;
248 
249     if (IsListEmpty(&subvol->fcbs))
250         return;
251 
252     while (le != &subvol->fcbs) {
253         struct _fcb* fcb = CONTAINING_RECORD(le, struct _fcb, list_entry);
254         IO_STATUS_BLOCK iosb;
255 
256         if (fcb->type != BTRFS_TYPE_DIRECTORY && !fcb->deleted)
257             CcFlushCache(&fcb->nonpaged->segment_object, NULL, 0, &iosb);
258 
259         le = le->Flink;
260     }
261 }
262 
263 static NTSTATUS do_create_snapshot(device_extension* Vcb, PFILE_OBJECT parent, fcb* subvol_fcb, PANSI_STRING utf8, PUNICODE_STRING name, bool readonly, PIRP Irp) {
264     LIST_ENTRY rollback;
265     uint64_t id;
266     NTSTATUS Status;
267     root *r, *subvol = subvol_fcb->subvol;
268     KEY searchkey;
269     traverse_ptr tp;
270     uint64_t address, *root_num;
271     LARGE_INTEGER time;
272     BTRFS_TIME now;
273     fcb* fcb = parent->FsContext;
274     ccb* ccb = parent->FsContext2;
275     LIST_ENTRY* le;
276     file_ref *fileref, *fr;
277     dir_child* dc = NULL;
278 
279     if (!ccb) {
280         ERR("error - ccb was NULL\n");
281         return STATUS_INTERNAL_ERROR;
282     }
283 
284     if (!(ccb->access & FILE_ADD_SUBDIRECTORY)) {
285         WARN("insufficient privileges\n");
286         return STATUS_ACCESS_DENIED;
287     }
288 
289     fileref = ccb->fileref;
290 
291     if (fileref->fcb == Vcb->dummy_fcb)
292         return STATUS_ACCESS_DENIED;
293 
294     // flush open files on this subvol
295 
296     flush_subvol_fcbs(subvol);
297 
298     // flush metadata
299 
300     if (Vcb->need_write)
301         Status = do_write(Vcb, Irp);
302     else
303         Status = STATUS_SUCCESS;
304 
305     free_trees(Vcb);
306 
307     if (!NT_SUCCESS(Status)) {
308         ERR("do_write returned %08lx\n", Status);
309         return Status;
310     }
311 
312     InitializeListHead(&rollback);
313 
314     // create new root
315 
316     id = InterlockedIncrement64(&Vcb->root_root->lastinode);
317     Status = create_root(Vcb, id, &r, true, Vcb->superblock.generation, Irp);
318 
319     if (!NT_SUCCESS(Status)) {
320         ERR("create_root returned %08lx\n", Status);
321         goto end;
322     }
323 
324     r->lastinode = subvol->lastinode;
325 
326     if (!Vcb->uuid_root) {
327         root* uuid_root;
328 
329         TRACE("uuid root doesn't exist, creating it\n");
330 
331         Status = create_root(Vcb, BTRFS_ROOT_UUID, &uuid_root, false, 0, Irp);
332 
333         if (!NT_SUCCESS(Status)) {
334             ERR("create_root returned %08lx\n", Status);
335             goto end;
336         }
337 
338         Vcb->uuid_root = uuid_root;
339     }
340 
341     root_num = ExAllocatePoolWithTag(PagedPool, sizeof(uint64_t), ALLOC_TAG);
342     if (!root_num) {
343         ERR("out of memory\n");
344         Status = STATUS_INSUFFICIENT_RESOURCES;
345         goto end;
346     }
347 
348     tp.tree = NULL;
349 
350     do {
351         get_uuid(&r->root_item.uuid);
352 
353         RtlCopyMemory(&searchkey.obj_id, &r->root_item.uuid, sizeof(uint64_t));
354         searchkey.obj_type = TYPE_SUBVOL_UUID;
355         RtlCopyMemory(&searchkey.offset, &r->root_item.uuid.uuid[sizeof(uint64_t)], sizeof(uint64_t));
356 
357         Status = find_item(Vcb, Vcb->uuid_root, &tp, &searchkey, false, Irp);
358     } while (NT_SUCCESS(Status) && !keycmp(searchkey, tp.item->key));
359 
360     *root_num = r->id;
361 
362     Status = insert_tree_item(Vcb, Vcb->uuid_root, searchkey.obj_id, searchkey.obj_type, searchkey.offset, root_num, sizeof(uint64_t), NULL, Irp);
363     if (!NT_SUCCESS(Status)) {
364         ERR("insert_tree_item returned %08lx\n", Status);
365         ExFreePool(root_num);
366         goto end;
367     }
368 
369     searchkey.obj_id = r->id;
370     searchkey.obj_type = TYPE_ROOT_ITEM;
371     searchkey.offset = 0xffffffffffffffff;
372 
373     Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, false, Irp);
374     if (!NT_SUCCESS(Status)) {
375         ERR("error - find_item returned %08lx\n", Status);
376         goto end;
377     }
378 
379     Status = snapshot_tree_copy(Vcb, subvol->root_item.block_number, r, &address, Irp, &rollback);
380     if (!NT_SUCCESS(Status)) {
381         ERR("snapshot_tree_copy returned %08lx\n", Status);
382         goto end;
383     }
384 
385     KeQuerySystemTime(&time);
386     win_time_to_unix(time, &now);
387 
388     r->root_item.inode.generation = 1;
389     r->root_item.inode.st_size = 3;
390     r->root_item.inode.st_blocks = subvol->root_item.inode.st_blocks;
391     r->root_item.inode.st_nlink = 1;
392     r->root_item.inode.st_mode = __S_IFDIR | S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; // 40755
393     r->root_item.inode.flags = 0xffffffff80000000; // FIXME - find out what these mean
394     r->root_item.generation = Vcb->superblock.generation;
395     r->root_item.objid = subvol->root_item.objid;
396     r->root_item.block_number = address;
397     r->root_item.bytes_used = subvol->root_item.bytes_used;
398     r->root_item.last_snapshot_generation = Vcb->superblock.generation;
399     r->root_item.root_level = subvol->root_item.root_level;
400     r->root_item.generation2 = Vcb->superblock.generation;
401     r->root_item.parent_uuid = subvol->root_item.uuid;
402     r->root_item.ctransid = subvol->root_item.ctransid;
403     r->root_item.otransid = Vcb->superblock.generation;
404     r->root_item.ctime = subvol->root_item.ctime;
405     r->root_item.otime = now;
406 
407     if (readonly)
408         r->root_item.flags |= BTRFS_SUBVOL_READONLY;
409 
410     r->treeholder.address = address;
411 
412     // FIXME - do we need to copy over the send and receive fields too?
413 
414     if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) {
415         ERR("error - could not find ROOT_ITEM for subvol %I64x\n", r->id);
416         Status = STATUS_INTERNAL_ERROR;
417         goto end;
418     }
419 
420     RtlCopyMemory(tp.item->data, &r->root_item, sizeof(ROOT_ITEM));
421 
422     // update ROOT_ITEM of original subvol
423 
424     subvol->root_item.last_snapshot_generation = Vcb->superblock.generation;
425 
426     mark_subvol_dirty(Vcb, subvol);
427 
428     // create fileref for entry in other subvolume
429 
430     fr = create_fileref(Vcb);
431     if (!fr) {
432         ERR("out of memory\n");
433         Status = STATUS_INSUFFICIENT_RESOURCES;
434         goto end;
435     }
436 
437     Status = open_fcb(Vcb, r, r->root_item.objid, BTRFS_TYPE_DIRECTORY, utf8, false, fcb, &fr->fcb, PagedPool, Irp);
438     if (!NT_SUCCESS(Status)) {
439         ERR("open_fcb returned %08lx\n", Status);
440         free_fileref(fr);
441         goto end;
442     }
443 
444     fr->parent = fileref;
445 
446     Status = add_dir_child(fileref->fcb, r->id, true, utf8, name, BTRFS_TYPE_DIRECTORY, &dc);
447     if (!NT_SUCCESS(Status))
448         WARN("add_dir_child returned %08lx\n", Status);
449 
450     fr->dc = dc;
451     dc->fileref = fr;
452 
453     ExAcquireResourceExclusiveLite(&fileref->fcb->nonpaged->dir_children_lock, true);
454     InsertTailList(&fileref->children, &fr->list_entry);
455     ExReleaseResourceLite(&fileref->fcb->nonpaged->dir_children_lock);
456 
457     increase_fileref_refcount(fileref);
458 
459     fr->created = true;
460     mark_fileref_dirty(fr);
461 
462     if (fr->fcb->type == BTRFS_TYPE_DIRECTORY)
463         fr->fcb->fileref = fr;
464 
465     fr->fcb->subvol->parent = fileref->fcb->subvol->id;
466 
467     free_fileref(fr);
468 
469     // change fcb's INODE_ITEM
470 
471     fcb->inode_item.transid = Vcb->superblock.generation;
472     fcb->inode_item.sequence++;
473     fcb->inode_item.st_size += utf8->Length * 2;
474 
475     if (!ccb->user_set_change_time)
476         fcb->inode_item.st_ctime = now;
477 
478     if (!ccb->user_set_write_time)
479         fcb->inode_item.st_mtime = now;
480 
481     fcb->inode_item_changed = true;
482     mark_fcb_dirty(fcb);
483 
484     fcb->subvol->root_item.ctime = now;
485     fcb->subvol->root_item.ctransid = Vcb->superblock.generation;
486 
487     send_notification_fileref(fr, FILE_NOTIFY_CHANGE_DIR_NAME, FILE_ACTION_ADDED, NULL);
488     send_notification_fileref(fr->parent, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED, NULL);
489 
490     le = subvol->fcbs.Flink;
491     while (le != &subvol->fcbs) {
492         struct _fcb* fcb2 = CONTAINING_RECORD(le, struct _fcb, list_entry);
493         LIST_ENTRY* le2 = fcb2->extents.Flink;
494 
495         while (le2 != &fcb2->extents) {
496             extent* ext = CONTAINING_RECORD(le2, extent, list_entry);
497 
498             if (!ext->ignore)
499                 ext->unique = false;
500 
501             le2 = le2->Flink;
502         }
503 
504         le = le->Flink;
505     }
506 
507     Status = do_write(Vcb, Irp);
508 
509     free_trees(Vcb);
510 
511     if (!NT_SUCCESS(Status))
512         ERR("do_write returned %08lx\n", Status);
513 
514 end:
515     if (NT_SUCCESS(Status))
516         clear_rollback(&rollback);
517     else
518         do_rollback(Vcb, &rollback);
519 
520     return Status;
521 }
522 
523 static NTSTATUS create_snapshot(device_extension* Vcb, PFILE_OBJECT FileObject, void* data, ULONG length, PIRP Irp) {
524     PFILE_OBJECT subvol_obj;
525     NTSTATUS Status;
526     btrfs_create_snapshot* bcs = data;
527     fcb* subvol_fcb;
528     HANDLE subvolh;
529     bool readonly, posix;
530     ANSI_STRING utf8;
531     UNICODE_STRING nameus;
532     ULONG len;
533     fcb* fcb;
534     ccb* ccb;
535     file_ref *fileref, *fr2;
536 
537 #if defined(_WIN64)
538     if (IoIs32bitProcess(Irp)) {
539         btrfs_create_snapshot32* bcs32 = data;
540 
541         if (length < offsetof(btrfs_create_snapshot32, name))
542             return STATUS_INVALID_PARAMETER;
543 
544         if (length < offsetof(btrfs_create_snapshot32, name) + bcs32->namelen)
545             return STATUS_INVALID_PARAMETER;
546 
547         subvolh = Handle32ToHandle(bcs32->subvol);
548 
549         nameus.Buffer = bcs32->name;
550         nameus.Length = nameus.MaximumLength = bcs32->namelen;
551 
552         readonly = bcs32->readonly;
553         posix = bcs32->posix;
554     } else {
555 #endif
556         if (length < offsetof(btrfs_create_snapshot, name))
557             return STATUS_INVALID_PARAMETER;
558 
559         if (length < offsetof(btrfs_create_snapshot, name) + bcs->namelen)
560             return STATUS_INVALID_PARAMETER;
561 
562         subvolh = bcs->subvol;
563 
564         nameus.Buffer = bcs->name;
565         nameus.Length = nameus.MaximumLength = bcs->namelen;
566 
567         readonly = bcs->readonly;
568         posix = bcs->posix;
569 #if defined(_WIN64)
570     }
571 #endif
572 
573     if (!subvolh)
574         return STATUS_INVALID_PARAMETER;
575 
576     if (!FileObject || !FileObject->FsContext)
577         return STATUS_INVALID_PARAMETER;
578 
579     fcb = FileObject->FsContext;
580     ccb = FileObject->FsContext2;
581 
582     if (!fcb || !ccb || fcb->type != BTRFS_TYPE_DIRECTORY)
583         return STATUS_INVALID_PARAMETER;
584 
585     fileref = ccb->fileref;
586 
587     if (!fileref) {
588         ERR("fileref was NULL\n");
589         return STATUS_INVALID_PARAMETER;
590     }
591 
592     if (!(ccb->access & FILE_ADD_SUBDIRECTORY)) {
593         WARN("insufficient privileges\n");
594         return STATUS_ACCESS_DENIED;
595     }
596 
597     if (Vcb->readonly)
598         return STATUS_MEDIA_WRITE_PROTECTED;
599 
600     if (is_subvol_readonly(fcb->subvol, Irp))
601         return STATUS_ACCESS_DENIED;
602 
603     if (!is_file_name_valid(&nameus, posix, false))
604         return STATUS_OBJECT_NAME_INVALID;
605 
606     utf8.Buffer = NULL;
607 
608     Status = utf16_to_utf8(NULL, 0, &len, nameus.Buffer, nameus.Length);
609     if (!NT_SUCCESS(Status)) {
610         ERR("utf16_to_utf8 failed with error %08lx\n", Status);
611         return Status;
612     }
613 
614     if (len == 0) {
615         ERR("utf16_to_utf8 returned a length of 0\n");
616         return STATUS_INTERNAL_ERROR;
617     }
618 
619     if (len > 0xffff) {
620         ERR("len was too long\n");
621         return STATUS_INVALID_PARAMETER;
622     }
623 
624     utf8.MaximumLength = utf8.Length = (USHORT)len;
625     utf8.Buffer = ExAllocatePoolWithTag(PagedPool, utf8.Length, ALLOC_TAG);
626 
627     if (!utf8.Buffer) {
628         ERR("out of memory\n");
629         return STATUS_INSUFFICIENT_RESOURCES;
630     }
631 
632     Status = utf16_to_utf8(utf8.Buffer, len, &len, nameus.Buffer, nameus.Length);
633     if (!NT_SUCCESS(Status)) {
634         ERR("utf16_to_utf8 failed with error %08lx\n", Status);
635         goto end2;
636     }
637 
638     ExAcquireResourceExclusiveLite(&Vcb->tree_lock, true);
639 
640     // no need for fcb_lock as we have tree_lock exclusively
641     Status = open_fileref(fcb->Vcb, &fr2, &nameus, fileref, false, NULL, NULL, PagedPool, ccb->case_sensitive || posix, Irp);
642 
643     if (NT_SUCCESS(Status)) {
644         if (!fr2->deleted) {
645             WARN("file already exists\n");
646             free_fileref(fr2);
647             Status = STATUS_OBJECT_NAME_COLLISION;
648             goto end3;
649         } else
650             free_fileref(fr2);
651     } else if (!NT_SUCCESS(Status) && Status != STATUS_OBJECT_NAME_NOT_FOUND) {
652         ERR("open_fileref returned %08lx\n", Status);
653         goto end3;
654     }
655 
656     Status = ObReferenceObjectByHandle(subvolh, 0, *IoFileObjectType, Irp->RequestorMode, (void**)&subvol_obj, NULL);
657     if (!NT_SUCCESS(Status)) {
658         ERR("ObReferenceObjectByHandle returned %08lx\n", Status);
659         goto end3;
660     }
661 
662     if (subvol_obj->DeviceObject != FileObject->DeviceObject) {
663         Status = STATUS_INVALID_PARAMETER;
664         goto end;
665     }
666 
667     subvol_fcb = subvol_obj->FsContext;
668     if (!subvol_fcb) {
669         Status = STATUS_INVALID_PARAMETER;
670         goto end;
671     }
672 
673     if (subvol_fcb->inode != subvol_fcb->subvol->root_item.objid) {
674         WARN("handle inode was %I64x, expected %I64x\n", subvol_fcb->inode, subvol_fcb->subvol->root_item.objid);
675         Status = STATUS_INVALID_PARAMETER;
676         goto end;
677     }
678 
679     ccb = subvol_obj->FsContext2;
680 
681     if (!ccb) {
682         Status = STATUS_INVALID_PARAMETER;
683         goto end;
684     }
685 
686     if (!(ccb->access & FILE_TRAVERSE)) {
687         WARN("insufficient privileges\n");
688         Status = STATUS_ACCESS_DENIED;
689         goto end;
690     }
691 
692     if (fcb == Vcb->dummy_fcb) {
693         Status = STATUS_ACCESS_DENIED;
694         goto end;
695     }
696 
697     // clear unique flag on extents of open files in subvol
698     if (!IsListEmpty(&subvol_fcb->subvol->fcbs)) {
699         LIST_ENTRY* le = subvol_fcb->subvol->fcbs.Flink;
700 
701         while (le != &subvol_fcb->subvol->fcbs) {
702             struct _fcb* openfcb = CONTAINING_RECORD(le, struct _fcb, list_entry);
703             LIST_ENTRY* le2;
704 
705             le2 = openfcb->extents.Flink;
706 
707             while (le2 != &openfcb->extents) {
708                 extent* ext = CONTAINING_RECORD(le2, extent, list_entry);
709 
710                 ext->unique = false;
711 
712                 le2 = le2->Flink;
713             }
714 
715             le = le->Flink;
716         }
717     }
718 
719     Status = do_create_snapshot(Vcb, FileObject, subvol_fcb, &utf8, &nameus, readonly, Irp);
720 
721     if (NT_SUCCESS(Status)) {
722         file_ref* fr;
723 
724         Status = open_fileref(Vcb, &fr, &nameus, fileref, false, NULL, NULL, PagedPool, false, Irp);
725 
726         if (!NT_SUCCESS(Status)) {
727             ERR("open_fileref returned %08lx\n", Status);
728             Status = STATUS_SUCCESS;
729         } else {
730             send_notification_fileref(fr, FILE_NOTIFY_CHANGE_DIR_NAME, FILE_ACTION_ADDED, NULL);
731             free_fileref(fr);
732         }
733     }
734 
735 end:
736     ObDereferenceObject(subvol_obj);
737 
738 end3:
739     ExReleaseResourceLite(&Vcb->tree_lock);
740 
741 end2:
742     ExFreePool(utf8.Buffer);
743 
744     return Status;
745 }
746 
747 static NTSTATUS create_subvol(device_extension* Vcb, PFILE_OBJECT FileObject, void* data, ULONG datalen, PIRP Irp) {
748     btrfs_create_subvol* bcs;
749     fcb *fcb, *rootfcb = NULL;
750     ccb* ccb;
751     file_ref* fileref;
752     NTSTATUS Status;
753     uint64_t id;
754     root* r = NULL;
755     LARGE_INTEGER time;
756     BTRFS_TIME now;
757     ULONG len;
758     uint16_t irsize;
759     UNICODE_STRING nameus;
760     ANSI_STRING utf8;
761     INODE_REF* ir;
762     KEY searchkey;
763     traverse_ptr tp;
764     SECURITY_SUBJECT_CONTEXT subjcont;
765     PSID owner;
766     BOOLEAN defaulted;
767     uint64_t* root_num;
768     file_ref *fr = NULL, *fr2;
769     dir_child* dc = NULL;
770 
771     fcb = FileObject->FsContext;
772     if (!fcb) {
773         ERR("error - fcb was NULL\n");
774         return STATUS_INTERNAL_ERROR;
775     }
776 
777     ccb = FileObject->FsContext2;
778     if (!ccb) {
779         ERR("error - ccb was NULL\n");
780         return STATUS_INTERNAL_ERROR;
781     }
782 
783     fileref = ccb->fileref;
784 
785     if (fcb->type != BTRFS_TYPE_DIRECTORY) {
786         ERR("parent FCB was not a directory\n");
787         return STATUS_NOT_A_DIRECTORY;
788     }
789 
790     if (!fileref) {
791         ERR("fileref was NULL\n");
792         return STATUS_INVALID_PARAMETER;
793     }
794 
795     if (fileref->deleted || fcb->deleted) {
796         ERR("parent has been deleted\n");
797         return STATUS_FILE_DELETED;
798     }
799 
800     if (!(ccb->access & FILE_ADD_SUBDIRECTORY)) {
801         WARN("insufficient privileges\n");
802         return STATUS_ACCESS_DENIED;
803     }
804 
805     if (Vcb->readonly)
806         return STATUS_MEDIA_WRITE_PROTECTED;
807 
808     if (is_subvol_readonly(fcb->subvol, Irp))
809         return STATUS_ACCESS_DENIED;
810 
811     if (fcb == Vcb->dummy_fcb)
812         return STATUS_ACCESS_DENIED;
813 
814     if (!data || datalen < sizeof(btrfs_create_subvol))
815         return STATUS_INVALID_PARAMETER;
816 
817     bcs = (btrfs_create_subvol*)data;
818 
819     if (offsetof(btrfs_create_subvol, name[0]) + bcs->namelen > datalen)
820         return STATUS_INVALID_PARAMETER;
821 
822     nameus.Length = nameus.MaximumLength = bcs->namelen;
823     nameus.Buffer = bcs->name;
824 
825     if (!is_file_name_valid(&nameus, bcs->posix, false))
826         return STATUS_OBJECT_NAME_INVALID;
827 
828     utf8.Buffer = NULL;
829 
830     Status = utf16_to_utf8(NULL, 0, &len, nameus.Buffer, nameus.Length);
831     if (!NT_SUCCESS(Status)) {
832         ERR("utf16_to_utf8 failed with error %08lx\n", Status);
833         return Status;
834     }
835 
836     if (len == 0) {
837         ERR("utf16_to_utf8 returned a length of 0\n");
838         return STATUS_INTERNAL_ERROR;
839     }
840 
841     if (len > 0xffff) {
842         ERR("len was too long\n");
843         return STATUS_INVALID_PARAMETER;
844     }
845 
846     utf8.MaximumLength = utf8.Length = (USHORT)len;
847     utf8.Buffer = ExAllocatePoolWithTag(PagedPool, utf8.Length, ALLOC_TAG);
848 
849     if (!utf8.Buffer) {
850         ERR("out of memory\n");
851         return STATUS_INSUFFICIENT_RESOURCES;
852     }
853 
854     Status = utf16_to_utf8(utf8.Buffer, len, &len, nameus.Buffer, nameus.Length);
855     if (!NT_SUCCESS(Status)) {
856         ERR("utf16_to_utf8 failed with error %08lx\n", Status);
857         goto end2;
858     }
859 
860     ExAcquireResourceExclusiveLite(&Vcb->tree_lock, true);
861 
862     KeQuerySystemTime(&time);
863     win_time_to_unix(time, &now);
864 
865     // no need for fcb_lock as we have tree_lock exclusively
866     Status = open_fileref(fcb->Vcb, &fr2, &nameus, fileref, false, NULL, NULL, PagedPool, ccb->case_sensitive || bcs->posix, Irp);
867 
868     if (NT_SUCCESS(Status)) {
869         if (!fr2->deleted) {
870             WARN("file already exists\n");
871             free_fileref(fr2);
872             Status = STATUS_OBJECT_NAME_COLLISION;
873             goto end;
874         } else
875             free_fileref(fr2);
876     } else if (!NT_SUCCESS(Status) && Status != STATUS_OBJECT_NAME_NOT_FOUND) {
877         ERR("open_fileref returned %08lx\n", Status);
878         goto end;
879     }
880 
881     id = InterlockedIncrement64(&Vcb->root_root->lastinode);
882     Status = create_root(Vcb, id, &r, false, 0, Irp);
883 
884     if (!NT_SUCCESS(Status)) {
885         ERR("create_root returned %08lx\n", Status);
886         goto end;
887     }
888 
889     TRACE("created root %I64x\n", id);
890 
891     if (!Vcb->uuid_root) {
892         root* uuid_root;
893 
894         TRACE("uuid root doesn't exist, creating it\n");
895 
896         Status = create_root(Vcb, BTRFS_ROOT_UUID, &uuid_root, false, 0, Irp);
897 
898         if (!NT_SUCCESS(Status)) {
899             ERR("create_root returned %08lx\n", Status);
900             goto end;
901         }
902 
903         Vcb->uuid_root = uuid_root;
904     }
905 
906     root_num = ExAllocatePoolWithTag(PagedPool, sizeof(uint64_t), ALLOC_TAG);
907     if (!root_num) {
908         ERR("out of memory\n");
909         Status = STATUS_INSUFFICIENT_RESOURCES;
910         goto end;
911     }
912 
913     tp.tree = NULL;
914 
915     do {
916         get_uuid(&r->root_item.uuid);
917 
918         RtlCopyMemory(&searchkey.obj_id, &r->root_item.uuid, sizeof(uint64_t));
919         searchkey.obj_type = TYPE_SUBVOL_UUID;
920         RtlCopyMemory(&searchkey.offset, &r->root_item.uuid.uuid[sizeof(uint64_t)], sizeof(uint64_t));
921 
922         Status = find_item(Vcb, Vcb->uuid_root, &tp, &searchkey, false, Irp);
923     } while (NT_SUCCESS(Status) && !keycmp(searchkey, tp.item->key));
924 
925     *root_num = r->id;
926 
927     Status = insert_tree_item(Vcb, Vcb->uuid_root, searchkey.obj_id, searchkey.obj_type, searchkey.offset, root_num, sizeof(uint64_t), NULL, Irp);
928     if (!NT_SUCCESS(Status)) {
929         ERR("insert_tree_item returned %08lx\n", Status);
930         ExFreePool(root_num);
931         goto end;
932     }
933 
934     r->root_item.inode.generation = 1;
935     r->root_item.inode.st_size = 3;
936     r->root_item.inode.st_blocks = Vcb->superblock.node_size;
937     r->root_item.inode.st_nlink = 1;
938     r->root_item.inode.st_mode = __S_IFDIR | S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; // 40755
939     r->root_item.inode.flags = 0xffffffff80000000; // FIXME - find out what these mean
940 
941     if (bcs->readonly)
942         r->root_item.flags |= BTRFS_SUBVOL_READONLY;
943 
944     r->root_item.objid = SUBVOL_ROOT_INODE;
945     r->root_item.bytes_used = Vcb->superblock.node_size;
946     r->root_item.ctransid = Vcb->superblock.generation;
947     r->root_item.otransid = Vcb->superblock.generation;
948     r->root_item.ctime = now;
949     r->root_item.otime = now;
950 
951     // add .. inode to new subvol
952 
953     rootfcb = create_fcb(Vcb, PagedPool);
954     if (!rootfcb) {
955         ERR("out of memory\n");
956         Status = STATUS_INSUFFICIENT_RESOURCES;
957         goto end;
958     }
959 
960     rootfcb->Vcb = Vcb;
961 
962     rootfcb->subvol = r;
963     rootfcb->inode = SUBVOL_ROOT_INODE;
964     rootfcb->type = BTRFS_TYPE_DIRECTORY;
965 
966     rootfcb->inode_item.generation = Vcb->superblock.generation;
967     rootfcb->inode_item.transid = Vcb->superblock.generation;
968     rootfcb->inode_item.st_nlink = 1;
969     rootfcb->inode_item.st_mode = __S_IFDIR | inherit_mode(fileref->fcb, true);
970     rootfcb->inode_item.st_atime = rootfcb->inode_item.st_ctime = rootfcb->inode_item.st_mtime = rootfcb->inode_item.otime = now;
971     rootfcb->inode_item.st_gid = GID_NOBODY;
972 
973     rootfcb->atts = get_file_attributes(Vcb, rootfcb->subvol, rootfcb->inode, rootfcb->type, false, true, Irp);
974 
975     if (r->root_item.flags & BTRFS_SUBVOL_READONLY)
976         rootfcb->atts |= FILE_ATTRIBUTE_READONLY;
977 
978     SeCaptureSubjectContext(&subjcont);
979 
980     Status = SeAssignSecurity(fcb->sd, NULL, (void**)&rootfcb->sd, true, &subjcont, IoGetFileObjectGenericMapping(), PagedPool);
981 
982     if (!NT_SUCCESS(Status)) {
983         ERR("SeAssignSecurity returned %08lx\n", Status);
984         goto end;
985     }
986 
987     if (!rootfcb->sd) {
988         ERR("SeAssignSecurity returned NULL security descriptor\n");
989         Status = STATUS_INTERNAL_ERROR;
990         goto end;
991     }
992 
993     Status = RtlGetOwnerSecurityDescriptor(rootfcb->sd, &owner, &defaulted);
994     if (!NT_SUCCESS(Status)) {
995         ERR("RtlGetOwnerSecurityDescriptor returned %08lx\n", Status);
996         rootfcb->inode_item.st_uid = UID_NOBODY;
997         rootfcb->sd_dirty = true;
998     } else {
999         rootfcb->inode_item.st_uid = sid_to_uid(owner);
1000         rootfcb->sd_dirty = rootfcb->inode_item.st_uid == UID_NOBODY;
1001     }
1002 
1003     find_gid(rootfcb, fileref->fcb, &subjcont);
1004 
1005     rootfcb->inode_item_changed = true;
1006 
1007     acquire_fcb_lock_exclusive(Vcb);
1008     InsertTailList(&r->fcbs, &rootfcb->list_entry);
1009     InsertTailList(&Vcb->all_fcbs, &rootfcb->list_entry_all);
1010     r->fcbs_version++;
1011     release_fcb_lock(Vcb);
1012 
1013     rootfcb->Header.IsFastIoPossible = fast_io_possible(rootfcb);
1014     rootfcb->Header.AllocationSize.QuadPart = 0;
1015     rootfcb->Header.FileSize.QuadPart = 0;
1016     rootfcb->Header.ValidDataLength.QuadPart = 0;
1017 
1018     rootfcb->created = true;
1019 
1020     if (fileref->fcb->inode_item.flags & BTRFS_INODE_COMPRESS)
1021         rootfcb->inode_item.flags |= BTRFS_INODE_COMPRESS;
1022 
1023     rootfcb->prop_compression = fileref->fcb->prop_compression;
1024     rootfcb->prop_compression_changed = rootfcb->prop_compression != PropCompression_None;
1025 
1026     r->lastinode = rootfcb->inode;
1027 
1028     // add INODE_REF
1029 
1030     irsize = (uint16_t)(offsetof(INODE_REF, name[0]) + sizeof(DOTDOT) - 1);
1031     ir = ExAllocatePoolWithTag(PagedPool, irsize, ALLOC_TAG);
1032     if (!ir) {
1033         ERR("out of memory\n");
1034         Status = STATUS_INSUFFICIENT_RESOURCES;
1035         goto end;
1036     }
1037 
1038     ir->index = 0;
1039     ir->n = sizeof(DOTDOT) - 1;
1040     RtlCopyMemory(ir->name, DOTDOT, ir->n);
1041 
1042     Status = insert_tree_item(Vcb, r, r->root_item.objid, TYPE_INODE_REF, r->root_item.objid, ir, irsize, NULL, Irp);
1043     if (!NT_SUCCESS(Status)) {
1044         ERR("insert_tree_item returned %08lx\n", Status);
1045         ExFreePool(ir);
1046         goto end;
1047     }
1048 
1049     // create fileref for entry in other subvolume
1050 
1051     fr = create_fileref(Vcb);
1052     if (!fr) {
1053         ERR("out of memory\n");
1054 
1055         reap_fcb(rootfcb);
1056 
1057         Status = STATUS_INSUFFICIENT_RESOURCES;
1058         goto end;
1059     }
1060 
1061     fr->fcb = rootfcb;
1062 
1063     mark_fcb_dirty(rootfcb);
1064 
1065     fr->parent = fileref;
1066 
1067     Status = add_dir_child(fileref->fcb, r->id, true, &utf8, &nameus, BTRFS_TYPE_DIRECTORY, &dc);
1068     if (!NT_SUCCESS(Status))
1069         WARN("add_dir_child returned %08lx\n", Status);
1070 
1071     fr->dc = dc;
1072     dc->fileref = fr;
1073 
1074     fr->fcb->hash_ptrs = ExAllocatePoolWithTag(PagedPool, sizeof(LIST_ENTRY*) * 256, ALLOC_TAG);
1075     if (!fr->fcb->hash_ptrs) {
1076         ERR("out of memory\n");
1077         free_fileref(fr);
1078         Status = STATUS_INSUFFICIENT_RESOURCES;
1079         goto end;
1080     }
1081 
1082     RtlZeroMemory(fr->fcb->hash_ptrs, sizeof(LIST_ENTRY*) * 256);
1083 
1084     fr->fcb->hash_ptrs_uc = ExAllocatePoolWithTag(PagedPool, sizeof(LIST_ENTRY*) * 256, ALLOC_TAG);
1085     if (!fr->fcb->hash_ptrs_uc) {
1086         ERR("out of memory\n");
1087         free_fileref(fr);
1088         Status = STATUS_INSUFFICIENT_RESOURCES;
1089         goto end;
1090     }
1091 
1092     RtlZeroMemory(fr->fcb->hash_ptrs_uc, sizeof(LIST_ENTRY*) * 256);
1093 
1094     ExAcquireResourceExclusiveLite(&fileref->fcb->nonpaged->dir_children_lock, true);
1095     InsertTailList(&fileref->children, &fr->list_entry);
1096     ExReleaseResourceLite(&fileref->fcb->nonpaged->dir_children_lock);
1097 
1098     increase_fileref_refcount(fileref);
1099 
1100     if (fr->fcb->type == BTRFS_TYPE_DIRECTORY)
1101         fr->fcb->fileref = fr;
1102 
1103     fr->created = true;
1104     mark_fileref_dirty(fr);
1105 
1106     // change fcb->subvol's ROOT_ITEM
1107 
1108     fcb->subvol->root_item.ctransid = Vcb->superblock.generation;
1109     fcb->subvol->root_item.ctime = now;
1110 
1111     // change fcb's INODE_ITEM
1112 
1113     fcb->inode_item.transid = Vcb->superblock.generation;
1114     fcb->inode_item.st_size += utf8.Length * 2;
1115     fcb->inode_item.sequence++;
1116 
1117     if (!ccb->user_set_change_time)
1118         fcb->inode_item.st_ctime = now;
1119 
1120     if (!ccb->user_set_write_time)
1121         fcb->inode_item.st_mtime = now;
1122 
1123     fcb->inode_item_changed = true;
1124     mark_fcb_dirty(fcb);
1125 
1126     fr->fcb->subvol->parent = fcb->subvol->id;
1127 
1128     Status = STATUS_SUCCESS;
1129 
1130 end:
1131     if (!NT_SUCCESS(Status)) {
1132         if (fr) {
1133             fr->deleted = true;
1134             mark_fileref_dirty(fr);
1135         } else if (rootfcb) {
1136             rootfcb->deleted = true;
1137             mark_fcb_dirty(rootfcb);
1138         }
1139 
1140         if (r) {
1141             RemoveEntryList(&r->list_entry);
1142             InsertTailList(&Vcb->drop_roots, &r->list_entry);
1143         }
1144     }
1145 
1146     ExReleaseResourceLite(&Vcb->tree_lock);
1147 
1148     if (NT_SUCCESS(Status)) {
1149         send_notification_fileref(fr, FILE_NOTIFY_CHANGE_DIR_NAME, FILE_ACTION_ADDED, NULL);
1150         send_notification_fileref(fr->parent, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED, NULL);
1151     }
1152 
1153 end2:
1154     if (fr)
1155         free_fileref(fr);
1156 
1157     return Status;
1158 }
1159 
1160 static NTSTATUS get_inode_info(PFILE_OBJECT FileObject, void* data, ULONG length) {
1161     btrfs_inode_info* bii = data;
1162     fcb* fcb;
1163     ccb* ccb;
1164     bool old_style;
1165 
1166     if (length < offsetof(btrfs_inode_info, disk_size_zstd))
1167         return STATUS_BUFFER_OVERFLOW;
1168 
1169     if (!FileObject)
1170         return STATUS_INVALID_PARAMETER;
1171 
1172     fcb = FileObject->FsContext;
1173 
1174     if (!fcb)
1175         return STATUS_INVALID_PARAMETER;
1176 
1177     ccb = FileObject->FsContext2;
1178 
1179     if (!ccb)
1180         return STATUS_INVALID_PARAMETER;
1181 
1182     if (!(ccb->access & FILE_READ_ATTRIBUTES)) {
1183         WARN("insufficient privileges\n");
1184         return STATUS_ACCESS_DENIED;
1185     }
1186 
1187     if (fcb->ads)
1188         fcb = ccb->fileref->parent->fcb;
1189 
1190     old_style = length < offsetof(btrfs_inode_info, sparse_size) + sizeof(((btrfs_inode_info*)NULL)->sparse_size);
1191 
1192     ExAcquireResourceSharedLite(fcb->Header.Resource, true);
1193 
1194     bii->subvol = fcb->subvol->id;
1195     bii->inode = fcb->inode;
1196     bii->top = fcb->Vcb->root_fileref->fcb == fcb ? true : false;
1197     bii->type = fcb->type;
1198     bii->st_uid = fcb->inode_item.st_uid;
1199     bii->st_gid = fcb->inode_item.st_gid;
1200     bii->st_mode = fcb->inode_item.st_mode;
1201 
1202     if (fcb->inode_item.st_rdev == 0)
1203         bii->st_rdev = 0;
1204     else
1205         bii->st_rdev = makedev((fcb->inode_item.st_rdev & 0xFFFFFFFFFFF) >> 20, fcb->inode_item.st_rdev & 0xFFFFF);
1206 
1207     bii->flags = fcb->inode_item.flags;
1208 
1209     bii->inline_length = 0;
1210     bii->disk_size_uncompressed = 0;
1211     bii->disk_size_zlib = 0;
1212     bii->disk_size_lzo = 0;
1213 
1214     if (!old_style) {
1215         bii->disk_size_zstd = 0;
1216         bii->sparse_size = 0;
1217     }
1218 
1219     if (fcb->type != BTRFS_TYPE_DIRECTORY) {
1220         uint64_t last_end = 0;
1221         LIST_ENTRY* le;
1222         bool extents_inline = false;
1223 
1224         le = fcb->extents.Flink;
1225         while (le != &fcb->extents) {
1226             extent* ext = CONTAINING_RECORD(le, extent, list_entry);
1227 
1228             if (!ext->ignore) {
1229                 if (!old_style && ext->offset > last_end)
1230                     bii->sparse_size += ext->offset - last_end;
1231 
1232                 if (ext->extent_data.type == EXTENT_TYPE_INLINE) {
1233                     bii->inline_length += ext->datalen - (uint16_t)offsetof(EXTENT_DATA, data[0]);
1234                     last_end = ext->offset + ext->extent_data.decoded_size;
1235                     extents_inline = true;
1236                 } else {
1237                     EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ext->extent_data.data;
1238 
1239                     // FIXME - compressed extents with a hole in them are counted more than once
1240                     if (ed2->size != 0) {
1241                         switch (ext->extent_data.compression) {
1242                             case BTRFS_COMPRESSION_NONE:
1243                                 bii->disk_size_uncompressed += ed2->num_bytes;
1244                                 break;
1245 
1246                             case BTRFS_COMPRESSION_ZLIB:
1247                                 bii->disk_size_zlib += ed2->size;
1248                                 break;
1249 
1250                             case BTRFS_COMPRESSION_LZO:
1251                                 bii->disk_size_lzo += ed2->size;
1252                                 break;
1253 
1254                             case BTRFS_COMPRESSION_ZSTD:
1255                                 if (!old_style)
1256                                     bii->disk_size_zstd += ed2->size;
1257                                 break;
1258                         }
1259                     }
1260 
1261                     last_end = ext->offset + ed2->num_bytes;
1262                 }
1263             }
1264 
1265             le = le->Flink;
1266         }
1267 
1268         if (!extents_inline && !old_style && sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size) > last_end)
1269             bii->sparse_size += sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size) - last_end;
1270 
1271         if (length >= offsetof(btrfs_inode_info, num_extents) + sizeof(((btrfs_inode_info*)NULL)->num_extents)) {
1272             EXTENT_DATA2* last_ed2 = NULL;
1273 
1274             le = fcb->extents.Flink;
1275 
1276             bii->num_extents = 0;
1277 
1278             while (le != &fcb->extents) {
1279                 extent* ext = CONTAINING_RECORD(le, extent, list_entry);
1280 
1281                 if (!ext->ignore && ext->extent_data.type != EXTENT_TYPE_INLINE) {
1282                     EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ext->extent_data.data;
1283 
1284                     if (ed2->size != 0) {
1285                         if (!last_ed2 || ed2->offset != last_ed2->offset + last_ed2->num_bytes)
1286                             bii->num_extents++;
1287 
1288                         last_ed2 = ed2;
1289                     } else
1290                         last_ed2 = NULL;
1291                 }
1292 
1293                 le = le->Flink;
1294             }
1295         }
1296     }
1297 
1298     switch (fcb->prop_compression) {
1299         case PropCompression_Zlib:
1300             bii->compression_type = BTRFS_COMPRESSION_ZLIB;
1301             break;
1302 
1303         case PropCompression_LZO:
1304             bii->compression_type = BTRFS_COMPRESSION_LZO;
1305             break;
1306 
1307         case PropCompression_ZSTD:
1308             bii->compression_type = BTRFS_COMPRESSION_ZSTD;
1309             break;
1310 
1311         default:
1312             bii->compression_type = BTRFS_COMPRESSION_ANY;
1313             break;
1314     }
1315 
1316     ExReleaseResourceLite(fcb->Header.Resource);
1317 
1318     return STATUS_SUCCESS;
1319 }
1320 
1321 static NTSTATUS set_inode_info(PFILE_OBJECT FileObject, void* data, ULONG length, PIRP Irp) {
1322     btrfs_set_inode_info* bsii = data;
1323     NTSTATUS Status;
1324     fcb* fcb;
1325     ccb* ccb;
1326 
1327     if (length < sizeof(btrfs_set_inode_info))
1328         return STATUS_INVALID_PARAMETER;
1329 
1330     if (!FileObject)
1331         return STATUS_INVALID_PARAMETER;
1332 
1333     fcb = FileObject->FsContext;
1334 
1335     if (!fcb)
1336         return STATUS_INVALID_PARAMETER;
1337 
1338     ccb = FileObject->FsContext2;
1339 
1340     if (!ccb)
1341         return STATUS_INVALID_PARAMETER;
1342 
1343     if (bsii->flags_changed && !(ccb->access & FILE_WRITE_ATTRIBUTES)) {
1344         WARN("insufficient privileges\n");
1345         return STATUS_ACCESS_DENIED;
1346     }
1347 
1348     if ((bsii->mode_changed || bsii->uid_changed || bsii->gid_changed) && !(ccb->access & WRITE_DAC)) {
1349         WARN("insufficient privileges\n");
1350         return STATUS_ACCESS_DENIED;
1351     }
1352 
1353     if (bsii->compression_type_changed && bsii->compression_type > BTRFS_COMPRESSION_ZSTD)
1354         return STATUS_INVALID_PARAMETER;
1355 
1356     if (fcb->ads)
1357         fcb = ccb->fileref->parent->fcb;
1358 
1359     if (is_subvol_readonly(fcb->subvol, Irp)) {
1360         WARN("trying to change inode on readonly subvolume\n");
1361         return STATUS_ACCESS_DENIED;
1362     }
1363 
1364     ExAcquireResourceExclusiveLite(fcb->Header.Resource, true);
1365 
1366     if (bsii->flags_changed) {
1367         if (fcb->type != BTRFS_TYPE_DIRECTORY && fcb->inode_item.st_size > 0 &&
1368             (bsii->flags & BTRFS_INODE_NODATACOW) != (fcb->inode_item.flags & BTRFS_INODE_NODATACOW)) {
1369             WARN("trying to change nocow flag on non-empty file\n");
1370             Status = STATUS_INVALID_PARAMETER;
1371             goto end;
1372         }
1373 
1374         fcb->inode_item.flags = bsii->flags;
1375 
1376         if (fcb->inode_item.flags & BTRFS_INODE_NODATACOW)
1377             fcb->inode_item.flags |= BTRFS_INODE_NODATASUM;
1378         else
1379             fcb->inode_item.flags &= ~(uint64_t)BTRFS_INODE_NODATASUM;
1380     }
1381 
1382     if (bsii->mode_changed) {
1383         uint32_t allowed = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IWOTH | S_IXOTH |
1384                          S_ISGID | S_ISVTX;
1385 
1386         if (ccb->access & WRITE_OWNER)
1387             allowed |= S_ISUID;
1388 
1389         fcb->inode_item.st_mode &= ~allowed;
1390         fcb->inode_item.st_mode |= bsii->st_mode & allowed;
1391     }
1392 
1393     if (bsii->uid_changed && fcb->inode_item.st_uid != bsii->st_uid) {
1394         fcb->inode_item.st_uid = bsii->st_uid;
1395 
1396         fcb->sd_dirty = true;
1397         fcb->sd_deleted = false;
1398     }
1399 
1400     if (bsii->gid_changed)
1401         fcb->inode_item.st_gid = bsii->st_gid;
1402 
1403     if (bsii->compression_type_changed) {
1404         switch (bsii->compression_type) {
1405             case BTRFS_COMPRESSION_ANY:
1406                 fcb->prop_compression = PropCompression_None;
1407             break;
1408 
1409             case BTRFS_COMPRESSION_ZLIB:
1410                 fcb->prop_compression = PropCompression_Zlib;
1411             break;
1412 
1413             case BTRFS_COMPRESSION_LZO:
1414                 fcb->prop_compression = PropCompression_LZO;
1415             break;
1416 
1417             case BTRFS_COMPRESSION_ZSTD:
1418                 fcb->prop_compression = PropCompression_ZSTD;
1419             break;
1420         }
1421 
1422         fcb->prop_compression_changed = true;
1423     }
1424 
1425     if (bsii->flags_changed || bsii->mode_changed || bsii->uid_changed || bsii->gid_changed || bsii->compression_type_changed) {
1426         fcb->inode_item_changed = true;
1427         mark_fcb_dirty(fcb);
1428     }
1429 
1430     Status = STATUS_SUCCESS;
1431 
1432 end:
1433     ExReleaseResourceLite(fcb->Header.Resource);
1434 
1435     return Status;
1436 }
1437 
1438 static NTSTATUS get_devices(device_extension* Vcb, void* data, ULONG length) {
1439     btrfs_device* dev = NULL;
1440     NTSTATUS Status;
1441     LIST_ENTRY* le;
1442 
1443     ExAcquireResourceSharedLite(&Vcb->tree_lock, true);
1444 
1445     le = Vcb->devices.Flink;
1446     while (le != &Vcb->devices) {
1447         device* dev2 = CONTAINING_RECORD(le, device, list_entry);
1448         ULONG structlen;
1449 
1450         if (length < sizeof(btrfs_device) - sizeof(WCHAR)) {
1451             Status = STATUS_BUFFER_OVERFLOW;
1452             goto end;
1453         }
1454 
1455         if (!dev)
1456             dev = data;
1457         else {
1458             dev->next_entry = sizeof(btrfs_device) - sizeof(WCHAR) + dev->namelen;
1459             dev = (btrfs_device*)((uint8_t*)dev + dev->next_entry);
1460         }
1461 
1462         structlen = length - offsetof(btrfs_device, namelen);
1463 
1464         if (dev2->devobj) {
1465             Status = dev_ioctl(dev2->devobj, IOCTL_MOUNTDEV_QUERY_DEVICE_NAME, NULL, 0, &dev->namelen, structlen, true, NULL);
1466             if (!NT_SUCCESS(Status))
1467                 goto end;
1468 
1469             dev->missing = false;
1470         } else {
1471             dev->namelen = 0;
1472             dev->missing = true;
1473         }
1474 
1475         dev->next_entry = 0;
1476         dev->dev_id = dev2->devitem.dev_id;
1477         dev->readonly = (Vcb->readonly || dev2->readonly) ? true : false;
1478         dev->device_number = dev2->disk_num;
1479         dev->partition_number = dev2->part_num;
1480         dev->size = dev2->devitem.num_bytes;
1481 
1482         if (dev2->devobj) {
1483             GET_LENGTH_INFORMATION gli;
1484 
1485             Status = dev_ioctl(dev2->devobj, IOCTL_DISK_GET_LENGTH_INFO, NULL, 0, &gli, sizeof(gli), true, NULL);
1486             if (!NT_SUCCESS(Status))
1487                 goto end;
1488 
1489             dev->max_size = gli.Length.QuadPart;
1490         } else
1491             dev->max_size = dev->size;
1492 
1493         RtlCopyMemory(dev->stats, dev2->stats, sizeof(uint64_t) * 5);
1494 
1495         length -= sizeof(btrfs_device) - sizeof(WCHAR) + dev->namelen;
1496 
1497         le = le->Flink;
1498     }
1499 
1500 end:
1501     ExReleaseResourceLite(&Vcb->tree_lock);
1502 
1503     return Status;
1504 }
1505 
1506 static NTSTATUS get_usage(device_extension* Vcb, void* data, ULONG length, PIRP Irp) {
1507     btrfs_usage* usage = (btrfs_usage*)data;
1508     btrfs_usage* lastbue = NULL;
1509     NTSTATUS Status;
1510     LIST_ENTRY* le;
1511 
1512     if (length < sizeof(btrfs_usage))
1513         return STATUS_BUFFER_OVERFLOW;
1514 
1515     if (!Vcb->chunk_usage_found) {
1516         ExAcquireResourceExclusiveLite(&Vcb->tree_lock, true);
1517 
1518         if (!Vcb->chunk_usage_found)
1519             Status = find_chunk_usage(Vcb, Irp);
1520         else
1521             Status = STATUS_SUCCESS;
1522 
1523         ExReleaseResourceLite(&Vcb->tree_lock);
1524 
1525         if (!NT_SUCCESS(Status)) {
1526             ERR("find_chunk_usage returned %08lx\n", Status);
1527             return Status;
1528         }
1529     }
1530 
1531     length -= offsetof(btrfs_usage, devices);
1532 
1533     ExAcquireResourceSharedLite(&Vcb->chunk_lock, true);
1534 
1535     le = Vcb->chunks.Flink;
1536     while (le != &Vcb->chunks) {
1537         bool addnew = false;
1538 
1539         chunk* c = CONTAINING_RECORD(le, chunk, list_entry);
1540 
1541         if (!lastbue) // first entry
1542             addnew = true;
1543         else {
1544             btrfs_usage* bue = usage;
1545 
1546             addnew = true;
1547 
1548             while (true) {
1549                 if (bue->type == c->chunk_item->type) {
1550                     addnew = false;
1551                     break;
1552                 }
1553 
1554                 if (bue->next_entry == 0)
1555                     break;
1556                 else
1557                     bue = (btrfs_usage*)((uint8_t*)bue + bue->next_entry);
1558             }
1559         }
1560 
1561         if (addnew) {
1562             btrfs_usage* bue;
1563             LIST_ENTRY* le2;
1564             uint64_t factor;
1565 
1566             if (!lastbue) {
1567                 bue = usage;
1568             } else {
1569                 if (length < offsetof(btrfs_usage, devices)) {
1570                     Status = STATUS_BUFFER_OVERFLOW;
1571                     goto end;
1572                 }
1573 
1574                 length -= offsetof(btrfs_usage, devices);
1575 
1576                 lastbue->next_entry = offsetof(btrfs_usage, devices) + (ULONG)(lastbue->num_devices * sizeof(btrfs_usage_device));
1577 
1578                 bue = (btrfs_usage*)((uint8_t*)lastbue + lastbue->next_entry);
1579             }
1580 
1581             bue->next_entry = 0;
1582             bue->type = c->chunk_item->type;
1583             bue->size = 0;
1584             bue->used = 0;
1585             bue->num_devices = 0;
1586 
1587             if (c->chunk_item->type & BLOCK_FLAG_RAID0)
1588                 factor = c->chunk_item->num_stripes;
1589             else if (c->chunk_item->type & BLOCK_FLAG_RAID10)
1590                 factor = c->chunk_item->num_stripes / c->chunk_item->sub_stripes;
1591             else if (c->chunk_item->type & BLOCK_FLAG_RAID5)
1592                 factor = c->chunk_item->num_stripes - 1;
1593             else if (c->chunk_item->type & BLOCK_FLAG_RAID6)
1594                 factor = c->chunk_item->num_stripes - 2;
1595             else
1596                 factor = 1;
1597 
1598             le2 = le;
1599             while (le2 != &Vcb->chunks) {
1600                 chunk* c2 = CONTAINING_RECORD(le2, chunk, list_entry);
1601 
1602                 if (c2->chunk_item->type == c->chunk_item->type) {
1603                     uint16_t i;
1604                     CHUNK_ITEM_STRIPE* cis = (CHUNK_ITEM_STRIPE*)&c2->chunk_item[1];
1605                     uint64_t stripesize;
1606 
1607                     bue->size += c2->chunk_item->size;
1608                     bue->used += c2->used;
1609 
1610                     stripesize = c2->chunk_item->size / factor;
1611 
1612                     for (i = 0; i < c2->chunk_item->num_stripes; i++) {
1613                         uint64_t j;
1614                         bool found = false;
1615 
1616                         for (j = 0; j < bue->num_devices; j++) {
1617                             if (bue->devices[j].dev_id == cis[i].dev_id) {
1618                                 bue->devices[j].alloc += stripesize;
1619                                 found = true;
1620                                 break;
1621                             }
1622                         }
1623 
1624                         if (!found) {
1625                             if (length < sizeof(btrfs_usage_device)) {
1626                                 Status = STATUS_BUFFER_OVERFLOW;
1627                                 goto end;
1628                             }
1629 
1630                             length -= sizeof(btrfs_usage_device);
1631 
1632                             bue->devices[bue->num_devices].dev_id = cis[i].dev_id;
1633                             bue->devices[bue->num_devices].alloc = stripesize;
1634                             bue->num_devices++;
1635                         }
1636                     }
1637                 }
1638 
1639                 le2 = le2->Flink;
1640             }
1641 
1642             lastbue = bue;
1643         }
1644 
1645         le = le->Flink;
1646     }
1647 
1648     Status = STATUS_SUCCESS;
1649 
1650 end:
1651     ExReleaseResourceLite(&Vcb->chunk_lock);
1652 
1653     return Status;
1654 }
1655 
1656 static NTSTATUS is_volume_mounted(device_extension* Vcb, PIRP Irp) {
1657     NTSTATUS Status;
1658     ULONG cc;
1659     IO_STATUS_BLOCK iosb;
1660     bool verify = false;
1661     LIST_ENTRY* le;
1662 
1663     ExAcquireResourceSharedLite(&Vcb->tree_lock, true);
1664 
1665     le = Vcb->devices.Flink;
1666     while (le != &Vcb->devices) {
1667         device* dev = CONTAINING_RECORD(le, device, list_entry);
1668 
1669         if (dev->devobj && dev->removable) {
1670             Status = dev_ioctl(dev->devobj, IOCTL_STORAGE_CHECK_VERIFY, NULL, 0, &cc, sizeof(ULONG), false, &iosb);
1671 
1672             if (iosb.Information != sizeof(ULONG))
1673                 cc = 0;
1674 
1675             if (Status == STATUS_VERIFY_REQUIRED || (NT_SUCCESS(Status) && cc != dev->change_count)) {
1676                 dev->devobj->Flags |= DO_VERIFY_VOLUME;
1677                 verify = true;
1678             }
1679 
1680             if (NT_SUCCESS(Status) && iosb.Information == sizeof(ULONG))
1681                 dev->change_count = cc;
1682 
1683             if (!NT_SUCCESS(Status) || verify) {
1684                 IoSetHardErrorOrVerifyDevice(Irp, dev->devobj);
1685                 ExReleaseResourceLite(&Vcb->tree_lock);
1686 
1687                 return verify ? STATUS_VERIFY_REQUIRED : Status;
1688             }
1689         }
1690 
1691         le = le->Flink;
1692     }
1693 
1694     ExReleaseResourceLite(&Vcb->tree_lock);
1695 
1696     return STATUS_SUCCESS;
1697 }
1698 
1699 static NTSTATUS fs_get_statistics(void* buffer, DWORD buflen, ULONG_PTR* retlen) {
1700     FILESYSTEM_STATISTICS* fss;
1701 
1702     WARN("STUB: FSCTL_FILESYSTEM_GET_STATISTICS\n");
1703 
1704     // This is hideously wrong, but at least it stops SMB from breaking
1705 
1706     if (buflen < sizeof(FILESYSTEM_STATISTICS))
1707         return STATUS_BUFFER_TOO_SMALL;
1708 
1709     fss = buffer;
1710     RtlZeroMemory(fss, sizeof(FILESYSTEM_STATISTICS));
1711 
1712     fss->Version = 1;
1713     fss->FileSystemType = FILESYSTEM_STATISTICS_TYPE_NTFS;
1714     fss->SizeOfCompleteStructure = sizeof(FILESYSTEM_STATISTICS);
1715 
1716     *retlen = sizeof(FILESYSTEM_STATISTICS);
1717 
1718     return STATUS_SUCCESS;
1719 }
1720 
1721 static NTSTATUS set_sparse(device_extension* Vcb, PFILE_OBJECT FileObject, void* data, ULONG length, PIRP Irp) {
1722     FILE_SET_SPARSE_BUFFER* fssb = data;
1723     NTSTATUS Status;
1724     bool set;
1725     fcb* fcb;
1726     ccb* ccb = FileObject->FsContext2;
1727     file_ref* fileref = ccb ? ccb->fileref : NULL;
1728 
1729     if (data && length < sizeof(FILE_SET_SPARSE_BUFFER))
1730         return STATUS_INVALID_PARAMETER;
1731 
1732     if (!FileObject) {
1733         ERR("FileObject was NULL\n");
1734         return STATUS_INVALID_PARAMETER;
1735     }
1736 
1737     fcb = FileObject->FsContext;
1738 
1739     if (!fcb) {
1740         ERR("FCB was NULL\n");
1741         return STATUS_INVALID_PARAMETER;
1742     }
1743 
1744     if (!ccb) {
1745         ERR("CCB was NULL\n");
1746         return STATUS_INVALID_PARAMETER;
1747     }
1748 
1749     if (Irp->RequestorMode == UserMode && !(ccb->access & FILE_WRITE_ATTRIBUTES)) {
1750         WARN("insufficient privileges\n");
1751         return STATUS_ACCESS_DENIED;
1752     }
1753 
1754     if (!fileref) {
1755         ERR("no fileref\n");
1756         return STATUS_INVALID_PARAMETER;
1757     }
1758 
1759     if (fcb->ads) {
1760         fileref = fileref->parent;
1761         fcb = fileref->fcb;
1762     }
1763 
1764     ExAcquireResourceSharedLite(&Vcb->tree_lock, true);
1765     ExAcquireResourceExclusiveLite(fcb->Header.Resource, true);
1766 
1767     if (fcb->type != BTRFS_TYPE_FILE) {
1768         WARN("FileObject did not point to a file\n");
1769         Status = STATUS_INVALID_PARAMETER;
1770         goto end;
1771     }
1772 
1773     if (fssb)
1774         set = fssb->SetSparse;
1775     else
1776         set = true;
1777 
1778     if (set) {
1779         fcb->atts |= FILE_ATTRIBUTE_SPARSE_FILE;
1780         fcb->atts_changed = true;
1781     } else {
1782         ULONG defda;
1783 
1784         fcb->atts &= ~FILE_ATTRIBUTE_SPARSE_FILE;
1785         fcb->atts_changed = true;
1786 
1787         defda = get_file_attributes(Vcb, fcb->subvol, fcb->inode, fcb->type,
1788                                     fileref && fileref->dc && fileref->dc->name.Length >= sizeof(WCHAR) && fileref->dc->name.Buffer[0] == '.', true, Irp);
1789 
1790         fcb->atts_deleted = defda == fcb->atts;
1791     }
1792 
1793     mark_fcb_dirty(fcb);
1794     queue_notification_fcb(fileref, FILE_NOTIFY_CHANGE_ATTRIBUTES, FILE_ACTION_MODIFIED, NULL);
1795 
1796     Status = STATUS_SUCCESS;
1797 
1798 end:
1799     ExReleaseResourceLite(fcb->Header.Resource);
1800     ExReleaseResourceLite(&Vcb->tree_lock);
1801 
1802     return Status;
1803 }
1804 
1805 static NTSTATUS zero_data(device_extension* Vcb, fcb* fcb, uint64_t start, uint64_t length, PIRP Irp, LIST_ENTRY* rollback) {
1806     NTSTATUS Status;
1807     bool make_inline, compress;
1808     uint64_t start_data, end_data;
1809     ULONG buf_head;
1810     uint8_t* data;
1811 
1812     make_inline = fcb->inode_item.st_size <= Vcb->options.max_inline || fcb_is_inline(fcb);
1813 
1814     if (!make_inline)
1815         compress = write_fcb_compressed(fcb);
1816 
1817     if (make_inline) {
1818         start_data = 0;
1819         end_data = fcb->inode_item.st_size;
1820         buf_head = (ULONG)offsetof(EXTENT_DATA, data[0]);
1821     } else if (compress) {
1822         start_data = start & ~(uint64_t)(COMPRESSED_EXTENT_SIZE - 1);
1823         end_data = min(sector_align(start + length, COMPRESSED_EXTENT_SIZE),
1824                        sector_align(fcb->inode_item.st_size, Vcb->superblock.sector_size));
1825         buf_head = 0;
1826     } else {
1827         start_data = start & ~(uint64_t)(Vcb->superblock.sector_size - 1);
1828         end_data = sector_align(start + length, Vcb->superblock.sector_size);
1829         buf_head = 0;
1830     }
1831 
1832     data = ExAllocatePoolWithTag(PagedPool, (ULONG)(buf_head + end_data - start_data), ALLOC_TAG);
1833     if (!data) {
1834         ERR("out of memory\n");
1835         return STATUS_INSUFFICIENT_RESOURCES;
1836     }
1837 
1838     RtlZeroMemory(data + buf_head, (ULONG)(end_data - start_data));
1839 
1840     if (start > start_data || start + length < end_data) {
1841         Status = read_file(fcb, data + buf_head, start_data, end_data - start_data, NULL, Irp);
1842 
1843         if (!NT_SUCCESS(Status)) {
1844             ERR("read_file returned %08lx\n", Status);
1845             ExFreePool(data);
1846             return Status;
1847         }
1848     }
1849 
1850     RtlZeroMemory(data + buf_head + start - start_data, (ULONG)length);
1851 
1852     if (make_inline) {
1853         uint16_t edsize;
1854         EXTENT_DATA* ed = (EXTENT_DATA*)data;
1855 
1856         Status = excise_extents(Vcb, fcb, 0, sector_align(end_data, Vcb->superblock.sector_size), Irp, rollback);
1857         if (!NT_SUCCESS(Status)) {
1858             ERR("excise_extents returned %08lx\n", Status);
1859             ExFreePool(data);
1860             return Status;
1861         }
1862 
1863         edsize = (uint16_t)(offsetof(EXTENT_DATA, data[0]) + end_data);
1864 
1865         ed->generation = Vcb->superblock.generation;
1866         ed->decoded_size = end_data;
1867         ed->compression = BTRFS_COMPRESSION_NONE;
1868         ed->encryption = BTRFS_ENCRYPTION_NONE;
1869         ed->encoding = BTRFS_ENCODING_NONE;
1870         ed->type = EXTENT_TYPE_INLINE;
1871 
1872         Status = add_extent_to_fcb(fcb, 0, ed, edsize, false, NULL, rollback);
1873         if (!NT_SUCCESS(Status)) {
1874             ERR("add_extent_to_fcb returned %08lx\n", Status);
1875             ExFreePool(data);
1876             return Status;
1877         }
1878 
1879         ExFreePool(data);
1880 
1881         fcb->inode_item.st_blocks += end_data;
1882     } else if (compress) {
1883         Status = write_compressed(fcb, start_data, end_data, data, Irp, rollback);
1884 
1885         ExFreePool(data);
1886 
1887         if (!NT_SUCCESS(Status)) {
1888             ERR("write_compressed returned %08lx\n", Status);
1889             return Status;
1890         }
1891     } else {
1892         Status = do_write_file(fcb, start_data, end_data, data, Irp, false, 0, rollback);
1893 
1894         ExFreePool(data);
1895 
1896         if (!NT_SUCCESS(Status)) {
1897             ERR("do_write_file returned %08lx\n", Status);
1898             return Status;
1899         }
1900     }
1901 
1902     return STATUS_SUCCESS;
1903 }
1904 
1905 static NTSTATUS set_zero_data(device_extension* Vcb, PFILE_OBJECT FileObject, void* data, ULONG length, PIRP Irp) {
1906     FILE_ZERO_DATA_INFORMATION* fzdi = data;
1907     NTSTATUS Status;
1908     fcb* fcb;
1909     ccb* ccb;
1910     file_ref* fileref;
1911     LIST_ENTRY rollback, *le;
1912     LARGE_INTEGER time;
1913     BTRFS_TIME now;
1914     uint64_t start, end;
1915     extent* ext;
1916     IO_STATUS_BLOCK iosb;
1917 
1918     if (!data || length < sizeof(FILE_ZERO_DATA_INFORMATION))
1919         return STATUS_INVALID_PARAMETER;
1920 
1921     if (!FileObject) {
1922         ERR("FileObject was NULL\n");
1923         return STATUS_INVALID_PARAMETER;
1924     }
1925 
1926     if (fzdi->BeyondFinalZero.QuadPart <= fzdi->FileOffset.QuadPart) {
1927         WARN("BeyondFinalZero was less than or equal to FileOffset (%I64x <= %I64x)\n", fzdi->BeyondFinalZero.QuadPart, fzdi->FileOffset.QuadPart);
1928         return STATUS_INVALID_PARAMETER;
1929     }
1930 
1931     fcb = FileObject->FsContext;
1932 
1933     if (!fcb) {
1934         ERR("FCB was NULL\n");
1935         return STATUS_INVALID_PARAMETER;
1936     }
1937 
1938     ccb = FileObject->FsContext2;
1939 
1940     if (!ccb) {
1941         ERR("ccb was NULL\n");
1942         return STATUS_INVALID_PARAMETER;
1943     }
1944 
1945     if (Irp->RequestorMode == UserMode && !(ccb->access & FILE_WRITE_DATA)) {
1946         WARN("insufficient privileges\n");
1947         return STATUS_ACCESS_DENIED;
1948     }
1949 
1950     fileref = ccb->fileref;
1951 
1952     if (!fileref) {
1953         ERR("fileref was NULL\n");
1954         return STATUS_INVALID_PARAMETER;
1955     }
1956 
1957     InitializeListHead(&rollback);
1958 
1959     ExAcquireResourceSharedLite(&Vcb->tree_lock, true);
1960     ExAcquireResourceExclusiveLite(fcb->Header.Resource, true);
1961 
1962     CcFlushCache(FileObject->SectionObjectPointer, NULL, 0, &iosb);
1963 
1964     if (fcb->type != BTRFS_TYPE_FILE) {
1965         WARN("FileObject did not point to a file\n");
1966         Status = STATUS_INVALID_PARAMETER;
1967         goto end;
1968     }
1969 
1970     if (fcb->ads) {
1971         ERR("FileObject is stream\n");
1972         Status = STATUS_INVALID_PARAMETER;
1973         goto end;
1974     }
1975 
1976     if ((uint64_t)fzdi->FileOffset.QuadPart >= fcb->inode_item.st_size) {
1977         Status = STATUS_SUCCESS;
1978         goto end;
1979     }
1980 
1981     ext = NULL;
1982     le = fcb->extents.Flink;
1983     while (le != &fcb->extents) {
1984         extent* ext2 = CONTAINING_RECORD(le, extent, list_entry);
1985 
1986         if (!ext2->ignore) {
1987             ext = ext2;
1988             break;
1989         }
1990 
1991         le = le->Flink;
1992     }
1993 
1994     if (!ext) {
1995         Status = STATUS_SUCCESS;
1996         goto end;
1997     }
1998 
1999     if (ext->extent_data.type == EXTENT_TYPE_INLINE) {
2000         Status = zero_data(Vcb, fcb, fzdi->FileOffset.QuadPart, fzdi->BeyondFinalZero.QuadPart - fzdi->FileOffset.QuadPart, Irp, &rollback);
2001         if (!NT_SUCCESS(Status)) {
2002             ERR("zero_data returned %08lx\n", Status);
2003             goto end;
2004         }
2005     } else {
2006         start = sector_align(fzdi->FileOffset.QuadPart, Vcb->superblock.sector_size);
2007 
2008         if ((uint64_t)fzdi->BeyondFinalZero.QuadPart > fcb->inode_item.st_size)
2009             end = sector_align(fcb->inode_item.st_size, Vcb->superblock.sector_size);
2010         else
2011             end = (fzdi->BeyondFinalZero.QuadPart / Vcb->superblock.sector_size) * Vcb->superblock.sector_size;
2012 
2013         if (end <= start) {
2014             Status = zero_data(Vcb, fcb, fzdi->FileOffset.QuadPart, fzdi->BeyondFinalZero.QuadPart - fzdi->FileOffset.QuadPart, Irp, &rollback);
2015             if (!NT_SUCCESS(Status)) {
2016                 ERR("zero_data returned %08lx\n", Status);
2017                 goto end;
2018             }
2019         } else {
2020             if (start > (uint64_t)fzdi->FileOffset.QuadPart) {
2021                 Status = zero_data(Vcb, fcb, fzdi->FileOffset.QuadPart, start - fzdi->FileOffset.QuadPart, Irp, &rollback);
2022                 if (!NT_SUCCESS(Status)) {
2023                     ERR("zero_data returned %08lx\n", Status);
2024                     goto end;
2025                 }
2026             }
2027 
2028             if (end < (uint64_t)fzdi->BeyondFinalZero.QuadPart) {
2029                 Status = zero_data(Vcb, fcb, end, fzdi->BeyondFinalZero.QuadPart - end, Irp, &rollback);
2030                 if (!NT_SUCCESS(Status)) {
2031                     ERR("zero_data returned %08lx\n", Status);
2032                     goto end;
2033                 }
2034             }
2035 
2036             if (end > start) {
2037                 Status = excise_extents(Vcb, fcb, start, end, Irp, &rollback);
2038                 if (!NT_SUCCESS(Status)) {
2039                     ERR("excise_extents returned %08lx\n", Status);
2040                     goto end;
2041                 }
2042             }
2043         }
2044     }
2045 
2046     CcPurgeCacheSection(FileObject->SectionObjectPointer, &fzdi->FileOffset, (ULONG)(fzdi->BeyondFinalZero.QuadPart - fzdi->FileOffset.QuadPart), false);
2047 
2048     KeQuerySystemTime(&time);
2049     win_time_to_unix(time, &now);
2050 
2051     fcb->inode_item.transid = Vcb->superblock.generation;
2052     fcb->inode_item.sequence++;
2053 
2054     if (!ccb->user_set_change_time)
2055         fcb->inode_item.st_ctime = now;
2056 
2057     if (!ccb->user_set_write_time)
2058         fcb->inode_item.st_mtime = now;
2059 
2060     fcb->extents_changed = true;
2061     fcb->inode_item_changed = true;
2062     mark_fcb_dirty(fcb);
2063 
2064     queue_notification_fcb(fileref, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED, NULL);
2065 
2066     fcb->subvol->root_item.ctransid = Vcb->superblock.generation;
2067     fcb->subvol->root_item.ctime = now;
2068 
2069     Status = STATUS_SUCCESS;
2070 
2071 end:
2072     if (!NT_SUCCESS(Status))
2073         do_rollback(Vcb, &rollback);
2074     else
2075         clear_rollback(&rollback);
2076 
2077     ExReleaseResourceLite(fcb->Header.Resource);
2078     ExReleaseResourceLite(&Vcb->tree_lock);
2079 
2080     return Status;
2081 }
2082 
2083 static NTSTATUS query_ranges(PFILE_OBJECT FileObject, FILE_ALLOCATED_RANGE_BUFFER* inbuf, ULONG inbuflen, void* outbuf, ULONG outbuflen, ULONG_PTR* retlen) {
2084     NTSTATUS Status;
2085     fcb* fcb;
2086     LIST_ENTRY* le;
2087     FILE_ALLOCATED_RANGE_BUFFER* ranges = outbuf;
2088     ULONG i = 0;
2089     uint64_t last_start, last_end;
2090 
2091     TRACE("FSCTL_QUERY_ALLOCATED_RANGES\n");
2092 
2093     if (!FileObject) {
2094         ERR("FileObject was NULL\n");
2095         return STATUS_INVALID_PARAMETER;
2096     }
2097 
2098     if (!inbuf || inbuflen < sizeof(FILE_ALLOCATED_RANGE_BUFFER) || !outbuf)
2099         return STATUS_INVALID_PARAMETER;
2100 
2101     fcb = FileObject->FsContext;
2102 
2103     if (!fcb) {
2104         ERR("FCB was NULL\n");
2105         return STATUS_INVALID_PARAMETER;
2106     }
2107 
2108     ExAcquireResourceSharedLite(fcb->Header.Resource, true);
2109 
2110     // If file is not marked as sparse, claim the whole thing as an allocated range
2111 
2112     if (!(fcb->atts & FILE_ATTRIBUTE_SPARSE_FILE)) {
2113         if (fcb->inode_item.st_size == 0)
2114             Status = STATUS_SUCCESS;
2115         else if (outbuflen < sizeof(FILE_ALLOCATED_RANGE_BUFFER))
2116             Status = STATUS_BUFFER_TOO_SMALL;
2117         else {
2118             ranges[i].FileOffset.QuadPart = 0;
2119             ranges[i].Length.QuadPart = fcb->inode_item.st_size;
2120             i++;
2121             Status = STATUS_SUCCESS;
2122         }
2123 
2124         goto end;
2125 
2126     }
2127 
2128     le = fcb->extents.Flink;
2129 
2130     last_start = 0;
2131     last_end = 0;
2132 
2133     while (le != &fcb->extents) {
2134         extent* ext = CONTAINING_RECORD(le, extent, list_entry);
2135 
2136         if (!ext->ignore) {
2137             EXTENT_DATA2* ed2 = (ext->extent_data.type == EXTENT_TYPE_REGULAR || ext->extent_data.type == EXTENT_TYPE_PREALLOC) ? (EXTENT_DATA2*)ext->extent_data.data : NULL;
2138             uint64_t len = ed2 ? ed2->num_bytes : ext->extent_data.decoded_size;
2139 
2140             if (ext->offset > last_end) { // first extent after a hole
2141                 if (last_end > last_start) {
2142                     if ((i + 1) * sizeof(FILE_ALLOCATED_RANGE_BUFFER) <= outbuflen) {
2143                         ranges[i].FileOffset.QuadPart = last_start;
2144                         ranges[i].Length.QuadPart = min(fcb->inode_item.st_size, last_end) - last_start;
2145                         i++;
2146                     } else {
2147                         Status = STATUS_BUFFER_TOO_SMALL;
2148                         goto end;
2149                     }
2150                 }
2151 
2152                 last_start = ext->offset;
2153             }
2154 
2155             last_end = ext->offset + len;
2156         }
2157 
2158         le = le->Flink;
2159     }
2160 
2161     if (last_end > last_start) {
2162         if ((i + 1) * sizeof(FILE_ALLOCATED_RANGE_BUFFER) <= outbuflen) {
2163             ranges[i].FileOffset.QuadPart = last_start;
2164             ranges[i].Length.QuadPart = min(fcb->inode_item.st_size, last_end) - last_start;
2165             i++;
2166         } else {
2167             Status = STATUS_BUFFER_TOO_SMALL;
2168             goto end;
2169         }
2170     }
2171 
2172     Status = STATUS_SUCCESS;
2173 
2174 end:
2175     *retlen = i * sizeof(FILE_ALLOCATED_RANGE_BUFFER);
2176 
2177     ExReleaseResourceLite(fcb->Header.Resource);
2178 
2179     return Status;
2180 }
2181 
2182 static NTSTATUS get_object_id(device_extension* Vcb, PFILE_OBJECT FileObject, FILE_OBJECTID_BUFFER* buf, ULONG buflen, ULONG_PTR* retlen) {
2183     fcb* fcb;
2184 
2185     TRACE("(%p, %p, %p, %lx, %p)\n", Vcb, FileObject, buf, buflen, retlen);
2186 
2187     if (!FileObject) {
2188         ERR("FileObject was NULL\n");
2189         return STATUS_INVALID_PARAMETER;
2190     }
2191 
2192     if (!buf || buflen < sizeof(FILE_OBJECTID_BUFFER))
2193         return STATUS_INVALID_PARAMETER;
2194 
2195     fcb = FileObject->FsContext;
2196 
2197     if (!fcb) {
2198         ERR("FCB was NULL\n");
2199         return STATUS_INVALID_PARAMETER;
2200     }
2201 
2202     ExAcquireResourceSharedLite(fcb->Header.Resource, true);
2203 
2204     RtlCopyMemory(&buf->ObjectId[0], &fcb->inode, sizeof(uint64_t));
2205     RtlCopyMemory(&buf->ObjectId[sizeof(uint64_t)], &fcb->subvol->id, sizeof(uint64_t));
2206 
2207     ExReleaseResourceLite(fcb->Header.Resource);
2208 
2209     RtlZeroMemory(&buf->ExtendedInfo, sizeof(buf->ExtendedInfo));
2210 
2211     *retlen = sizeof(FILE_OBJECTID_BUFFER);
2212 
2213     return STATUS_SUCCESS;
2214 }
2215 
2216 static void flush_fcb_caches(device_extension* Vcb) {
2217     LIST_ENTRY* le;
2218 
2219     le = Vcb->all_fcbs.Flink;
2220     while (le != &Vcb->all_fcbs) {
2221         struct _fcb* fcb = CONTAINING_RECORD(le, struct _fcb, list_entry_all);
2222         IO_STATUS_BLOCK iosb;
2223 
2224         if (fcb->type != BTRFS_TYPE_DIRECTORY && !fcb->deleted)
2225             CcFlushCache(&fcb->nonpaged->segment_object, NULL, 0, &iosb);
2226 
2227         le = le->Flink;
2228     }
2229 }
2230 
2231 static NTSTATUS lock_volume(device_extension* Vcb, PIRP Irp) {
2232     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
2233     NTSTATUS Status;
2234     KIRQL irql;
2235     bool lock_paused_balance = false;
2236 
2237     TRACE("FSCTL_LOCK_VOLUME\n");
2238 
2239     if (Vcb->scrub.thread) {
2240         WARN("cannot lock while scrub running\n");
2241         return STATUS_DEVICE_NOT_READY;
2242     }
2243 
2244     if (Vcb->balance.thread) {
2245         WARN("cannot lock while balance running\n");
2246         return STATUS_DEVICE_NOT_READY;
2247     }
2248 
2249     TRACE("locking volume\n");
2250 
2251     FsRtlNotifyVolumeEvent(IrpSp->FileObject, FSRTL_VOLUME_LOCK);
2252 
2253     if (Vcb->locked)
2254         return STATUS_SUCCESS;
2255 
2256     ExAcquireResourceExclusiveLite(&Vcb->fileref_lock, true);
2257 
2258     if (Vcb->root_fileref && Vcb->root_fileref->fcb && (Vcb->root_fileref->open_count > 0 || has_open_children(Vcb->root_fileref))) {
2259         Status = STATUS_ACCESS_DENIED;
2260         ExReleaseResourceLite(&Vcb->fileref_lock);
2261         goto end;
2262     }
2263 
2264     ExReleaseResourceLite(&Vcb->fileref_lock);
2265 
2266     if (Vcb->balance.thread && KeReadStateEvent(&Vcb->balance.event)) {
2267         ExAcquireResourceExclusiveLite(&Vcb->tree_lock, true);
2268         KeClearEvent(&Vcb->balance.event);
2269         ExReleaseResourceLite(&Vcb->tree_lock);
2270 
2271         lock_paused_balance = true;
2272     }
2273 
2274     ExAcquireResourceExclusiveLite(&Vcb->tree_lock, true);
2275 
2276     flush_fcb_caches(Vcb);
2277 
2278     if (Vcb->need_write && !Vcb->readonly)
2279         Status = do_write(Vcb, Irp);
2280     else
2281         Status = STATUS_SUCCESS;
2282 
2283     free_trees(Vcb);
2284 
2285     ExReleaseResourceLite(&Vcb->tree_lock);
2286 
2287     if (!NT_SUCCESS(Status)) {
2288         ERR("do_write returned %08lx\n", Status);
2289         goto end;
2290     }
2291 
2292     IoAcquireVpbSpinLock(&irql);
2293 
2294     if (!(Vcb->Vpb->Flags & VPB_LOCKED)) {
2295         Vcb->Vpb->Flags |= VPB_LOCKED;
2296         Vcb->locked = true;
2297         Vcb->locked_fileobj = IrpSp->FileObject;
2298         Vcb->lock_paused_balance = lock_paused_balance;
2299     } else {
2300         Status = STATUS_ACCESS_DENIED;
2301         IoReleaseVpbSpinLock(irql);
2302 
2303         if (lock_paused_balance)
2304             KeSetEvent(&Vcb->balance.event, 0, false);
2305 
2306         goto end;
2307     }
2308 
2309     IoReleaseVpbSpinLock(irql);
2310 
2311     Status = STATUS_SUCCESS;
2312 
2313 end:
2314     if (!NT_SUCCESS(Status))
2315         FsRtlNotifyVolumeEvent(IrpSp->FileObject, FSRTL_VOLUME_LOCK_FAILED);
2316 
2317     return Status;
2318 }
2319 
2320 void do_unlock_volume(device_extension* Vcb) {
2321     KIRQL irql;
2322 
2323     IoAcquireVpbSpinLock(&irql);
2324 
2325     Vcb->locked = false;
2326     Vcb->Vpb->Flags &= ~VPB_LOCKED;
2327     Vcb->locked_fileobj = NULL;
2328 
2329     IoReleaseVpbSpinLock(irql);
2330 
2331     if (Vcb->lock_paused_balance)
2332         KeSetEvent(&Vcb->balance.event, 0, false);
2333 }
2334 
2335 static NTSTATUS unlock_volume(device_extension* Vcb, PIRP Irp) {
2336     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
2337 
2338     TRACE("FSCTL_UNLOCK_VOLUME\n");
2339 
2340     if (!Vcb->locked || IrpSp->FileObject != Vcb->locked_fileobj)
2341         return STATUS_NOT_LOCKED;
2342 
2343     TRACE("unlocking volume\n");
2344 
2345     do_unlock_volume(Vcb);
2346 
2347     FsRtlNotifyVolumeEvent(IrpSp->FileObject, FSRTL_VOLUME_UNLOCK);
2348 
2349     return STATUS_SUCCESS;
2350 }
2351 
2352 static NTSTATUS invalidate_volumes(PIRP Irp) {
2353     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
2354     LUID TcbPrivilege = {SE_TCB_PRIVILEGE, 0};
2355     NTSTATUS Status;
2356     HANDLE h;
2357     PFILE_OBJECT fileobj;
2358     PDEVICE_OBJECT devobj;
2359     LIST_ENTRY* le;
2360 
2361     TRACE("FSCTL_INVALIDATE_VOLUMES\n");
2362 
2363     if (!SeSinglePrivilegeCheck(TcbPrivilege, Irp->RequestorMode))
2364         return STATUS_PRIVILEGE_NOT_HELD;
2365 
2366 #if defined(_WIN64)
2367     if (IoIs32bitProcess(Irp)) {
2368         if (IrpSp->Parameters.FileSystemControl.InputBufferLength != sizeof(uint32_t))
2369             return STATUS_INVALID_PARAMETER;
2370 
2371         h = (HANDLE)LongToHandle((*(uint32_t*)Irp->AssociatedIrp.SystemBuffer));
2372     } else {
2373 #endif
2374         if (IrpSp->Parameters.FileSystemControl.InputBufferLength != sizeof(HANDLE))
2375             return STATUS_INVALID_PARAMETER;
2376 
2377         h = *(PHANDLE)Irp->AssociatedIrp.SystemBuffer;
2378 #if defined(_WIN64)
2379     }
2380 #endif
2381 
2382     Status = ObReferenceObjectByHandle(h, 0, *IoFileObjectType, Irp->RequestorMode, (void**)&fileobj, NULL);
2383 
2384     if (!NT_SUCCESS(Status)) {
2385         ERR("ObReferenceObjectByHandle returned %08lx\n", Status);
2386         return Status;
2387     }
2388 
2389     devobj = fileobj->DeviceObject;
2390 
2391     ExAcquireResourceSharedLite(&global_loading_lock, true);
2392 
2393     le = VcbList.Flink;
2394 
2395     while (le != &VcbList) {
2396         device_extension* Vcb = CONTAINING_RECORD(le, device_extension, list_entry);
2397 
2398         if (Vcb->Vpb && Vcb->Vpb->RealDevice == devobj) {
2399             if (Vcb->Vpb == devobj->Vpb) {
2400                 KIRQL irql;
2401                 PVPB newvpb;
2402                 bool free_newvpb = false;
2403 
2404                 newvpb = ExAllocatePoolWithTag(NonPagedPool, sizeof(VPB), ALLOC_TAG);
2405                 if (!newvpb) {
2406                     ERR("out of memory\n");
2407                     Status = STATUS_INSUFFICIENT_RESOURCES;
2408                     goto end;
2409                 }
2410 
2411                 RtlZeroMemory(newvpb, sizeof(VPB));
2412 
2413                 ObReferenceObject(devobj);
2414 
2415                 ExAcquireResourceExclusiveLite(&Vcb->tree_lock, true);
2416 
2417                 Vcb->removing = true;
2418 
2419                 ExReleaseResourceLite(&Vcb->tree_lock);
2420 
2421                 CcWaitForCurrentLazyWriterActivity();
2422 
2423                 ExAcquireResourceExclusiveLite(&Vcb->tree_lock, true);
2424 
2425                 flush_fcb_caches(Vcb);
2426 
2427                 if (Vcb->need_write && !Vcb->readonly)
2428                     Status = do_write(Vcb, Irp);
2429                 else
2430                     Status = STATUS_SUCCESS;
2431 
2432                 free_trees(Vcb);
2433 
2434                 if (!NT_SUCCESS(Status)) {
2435                     ERR("do_write returned %08lx\n", Status);
2436                     ExReleaseResourceLite(&Vcb->tree_lock);
2437                     ExFreePool(newvpb);
2438                     ObDereferenceObject(devobj);
2439                     goto end;
2440                 }
2441 
2442                 flush_fcb_caches(Vcb);
2443 
2444                 ExReleaseResourceLite(&Vcb->tree_lock);
2445 
2446                 IoAcquireVpbSpinLock(&irql);
2447 
2448                 if (devobj->Vpb->Flags & VPB_MOUNTED) {
2449                     newvpb->Type = IO_TYPE_VPB;
2450                     newvpb->Size = sizeof(VPB);
2451                     newvpb->RealDevice = devobj;
2452                     newvpb->Flags = devobj->Vpb->Flags & VPB_REMOVE_PENDING;
2453 
2454                     devobj->Vpb = newvpb;
2455                 } else
2456                     free_newvpb = true;
2457 
2458                 IoReleaseVpbSpinLock(irql);
2459 
2460                 if (free_newvpb)
2461                     ExFreePool(newvpb);
2462 
2463                 if (Vcb->open_files == 0)
2464                     uninit(Vcb);
2465 
2466                 ObDereferenceObject(devobj);
2467             }
2468 
2469             break;
2470         }
2471 
2472         le = le->Flink;
2473     }
2474 
2475     Status = STATUS_SUCCESS;
2476 
2477 end:
2478     ExReleaseResourceLite(&global_loading_lock);
2479 
2480     ObDereferenceObject(fileobj);
2481 
2482     return Status;
2483 }
2484 
2485 static NTSTATUS is_volume_dirty(device_extension* Vcb, PIRP Irp) {
2486     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
2487     ULONG* volstate;
2488 
2489     if (Irp->AssociatedIrp.SystemBuffer) {
2490         volstate = Irp->AssociatedIrp.SystemBuffer;
2491     } else if (Irp->MdlAddress != NULL) {
2492         volstate = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, LowPagePriority);
2493 
2494         if (!volstate)
2495             return STATUS_INSUFFICIENT_RESOURCES;
2496     } else
2497         return STATUS_INVALID_USER_BUFFER;
2498 
2499     if (IrpSp->Parameters.FileSystemControl.OutputBufferLength < sizeof(ULONG))
2500         return STATUS_INVALID_PARAMETER;
2501 
2502     *volstate = 0;
2503 
2504     if (IrpSp->FileObject->FsContext != Vcb->volume_fcb)
2505         return STATUS_INVALID_PARAMETER;
2506 
2507     Irp->IoStatus.Information = sizeof(ULONG);
2508 
2509     return STATUS_SUCCESS;
2510 }
2511 
2512 static NTSTATUS get_compression(PIRP Irp) {
2513     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
2514     USHORT* compression;
2515 
2516     TRACE("FSCTL_GET_COMPRESSION\n");
2517 
2518     if (Irp->AssociatedIrp.SystemBuffer) {
2519         compression = Irp->AssociatedIrp.SystemBuffer;
2520     } else if (Irp->MdlAddress != NULL) {
2521         compression = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, LowPagePriority);
2522 
2523         if (!compression)
2524             return STATUS_INSUFFICIENT_RESOURCES;
2525     } else
2526         return STATUS_INVALID_USER_BUFFER;
2527 
2528     if (IrpSp->Parameters.FileSystemControl.OutputBufferLength < sizeof(USHORT))
2529         return STATUS_INVALID_PARAMETER;
2530 
2531     *compression = COMPRESSION_FORMAT_NONE;
2532 
2533     Irp->IoStatus.Information = sizeof(USHORT);
2534 
2535     return STATUS_SUCCESS;
2536 }
2537 
2538 static NTSTATUS set_compression(PIRP Irp) {
2539     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
2540     USHORT* compression;
2541 
2542     TRACE("FSCTL_SET_COMPRESSION\n");
2543 
2544     if (IrpSp->Parameters.FileSystemControl.InputBufferLength < sizeof(USHORT))
2545         return STATUS_INVALID_PARAMETER;
2546 
2547     compression = Irp->AssociatedIrp.SystemBuffer;
2548 
2549     if (*compression != COMPRESSION_FORMAT_NONE)
2550         return STATUS_INVALID_PARAMETER;
2551 
2552     return STATUS_SUCCESS;
2553 }
2554 
2555 static void update_volumes(device_extension* Vcb) {
2556     LIST_ENTRY* le;
2557     volume_device_extension* vde = Vcb->vde;
2558     pdo_device_extension* pdode = vde->pdode;
2559 
2560     ExAcquireResourceSharedLite(&Vcb->tree_lock, true);
2561 
2562     ExAcquireResourceExclusiveLite(&pdode->child_lock, true);
2563 
2564     le = pdode->children.Flink;
2565     while (le != &pdode->children) {
2566         volume_child* vc = CONTAINING_RECORD(le, volume_child, list_entry);
2567 
2568         vc->generation = Vcb->superblock.generation - 1;
2569 
2570         le = le->Flink;
2571     }
2572 
2573     ExReleaseResourceLite(&pdode->child_lock);
2574 
2575     ExReleaseResourceLite(&Vcb->tree_lock);
2576 }
2577 
2578 NTSTATUS dismount_volume(device_extension* Vcb, bool shutdown, PIRP Irp) {
2579     NTSTATUS Status;
2580     bool open_files;
2581 
2582     TRACE("FSCTL_DISMOUNT_VOLUME\n");
2583 
2584     if (!(Vcb->Vpb->Flags & VPB_MOUNTED))
2585         return STATUS_SUCCESS;
2586 
2587     if (!shutdown) {
2588         if (Vcb->disallow_dismount || Vcb->page_file_count != 0) {
2589             WARN("attempting to dismount boot volume or one containing a pagefile\n");
2590             return STATUS_ACCESS_DENIED;
2591         }
2592 
2593         Status = FsRtlNotifyVolumeEvent(Vcb->root_file, FSRTL_VOLUME_DISMOUNT);
2594         if (!NT_SUCCESS(Status)) {
2595             WARN("FsRtlNotifyVolumeEvent returned %08lx\n", Status);
2596         }
2597     }
2598 
2599     ExAcquireResourceExclusiveLite(&Vcb->tree_lock, true);
2600 
2601     if (!Vcb->locked) {
2602         flush_fcb_caches(Vcb);
2603 
2604         if (Vcb->need_write && !Vcb->readonly) {
2605             Status = do_write(Vcb, Irp);
2606 
2607             if (!NT_SUCCESS(Status))
2608                 ERR("do_write returned %08lx\n", Status);
2609         }
2610     }
2611 
2612     free_trees(Vcb);
2613 
2614     Vcb->removing = true;
2615 
2616     open_files = Vcb->open_files > 0;
2617 
2618     if (Vcb->vde) {
2619         update_volumes(Vcb);
2620         Vcb->vde->mounted_device = NULL;
2621     }
2622 
2623     ExReleaseResourceLite(&Vcb->tree_lock);
2624 
2625     if (!open_files)
2626         uninit(Vcb);
2627 
2628     return STATUS_SUCCESS;
2629 }
2630 
2631 static NTSTATUS is_device_part_of_mounted_btrfs_raid(PDEVICE_OBJECT devobj, PFILE_OBJECT fileobj) {
2632     NTSTATUS Status;
2633     ULONG to_read;
2634     superblock* sb;
2635     BTRFS_UUID fsuuid, devuuid;
2636     LIST_ENTRY* le;
2637 
2638     to_read = devobj->SectorSize == 0 ? sizeof(superblock) : (ULONG)sector_align(sizeof(superblock), devobj->SectorSize);
2639 
2640     sb = ExAllocatePoolWithTag(PagedPool, to_read, ALLOC_TAG);
2641     if (!sb) {
2642         ERR("out of memory\n");
2643         return STATUS_INSUFFICIENT_RESOURCES;
2644     }
2645 
2646     Status = sync_read_phys(devobj, fileobj, superblock_addrs[0], to_read, (uint8_t*)sb, true);
2647     if (!NT_SUCCESS(Status)) {
2648         ERR("sync_read_phys returned %08lx\n", Status);
2649         ExFreePool(sb);
2650         return Status;
2651     }
2652 
2653     if (sb->magic != BTRFS_MAGIC) {
2654         TRACE("device is not Btrfs\n");
2655         ExFreePool(sb);
2656         return STATUS_SUCCESS;
2657     }
2658 
2659     if (!check_superblock_checksum(sb)) {
2660         TRACE("device has Btrfs magic, but invalid superblock checksum\n");
2661         ExFreePool(sb);
2662         return STATUS_SUCCESS;
2663     }
2664 
2665     fsuuid = sb->uuid;
2666     devuuid = sb->dev_item.device_uuid;
2667 
2668     ExFreePool(sb);
2669 
2670     ExAcquireResourceSharedLite(&global_loading_lock, true);
2671 
2672     le = VcbList.Flink;
2673 
2674     while (le != &VcbList) {
2675         device_extension* Vcb = CONTAINING_RECORD(le, device_extension, list_entry);
2676 
2677         if (RtlCompareMemory(&Vcb->superblock.uuid, &fsuuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
2678             LIST_ENTRY* le2;
2679 
2680             ExAcquireResourceSharedLite(&Vcb->tree_lock, true);
2681 
2682             if (Vcb->superblock.num_devices > 1) {
2683                 le2 = Vcb->devices.Flink;
2684                 while (le2 != &Vcb->devices) {
2685                     device* dev = CONTAINING_RECORD(le2, device, list_entry);
2686 
2687                     if (RtlCompareMemory(&dev->devitem.device_uuid, &devuuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
2688                         ExReleaseResourceLite(&Vcb->tree_lock);
2689                         ExReleaseResourceLite(&global_loading_lock);
2690                         return STATUS_DEVICE_NOT_READY;
2691                     }
2692 
2693                     le2 = le2->Flink;
2694                 }
2695             }
2696 
2697             ExReleaseResourceLite(&Vcb->tree_lock);
2698             ExReleaseResourceLite(&global_loading_lock);
2699             return STATUS_SUCCESS;
2700         }
2701 
2702         le = le->Flink;
2703     }
2704 
2705     ExReleaseResourceLite(&global_loading_lock);
2706 
2707     return STATUS_SUCCESS;
2708 }
2709 
2710 void trim_whole_device(device* dev) {
2711     DEVICE_MANAGE_DATA_SET_ATTRIBUTES dmdsa;
2712     NTSTATUS Status;
2713 
2714     // FIXME - avoid "bootloader area"??
2715 
2716     dmdsa.Size = sizeof(DEVICE_MANAGE_DATA_SET_ATTRIBUTES);
2717     dmdsa.Action = DeviceDsmAction_Trim;
2718     dmdsa.Flags = DEVICE_DSM_FLAG_ENTIRE_DATA_SET_RANGE | DEVICE_DSM_FLAG_TRIM_NOT_FS_ALLOCATED;
2719     dmdsa.ParameterBlockOffset = 0;
2720     dmdsa.ParameterBlockLength = 0;
2721     dmdsa.DataSetRangesOffset = 0;
2722     dmdsa.DataSetRangesLength = 0;
2723 
2724     Status = dev_ioctl(dev->devobj, IOCTL_STORAGE_MANAGE_DATA_SET_ATTRIBUTES, &dmdsa, sizeof(DEVICE_MANAGE_DATA_SET_ATTRIBUTES), NULL, 0, true, NULL);
2725     if (!NT_SUCCESS(Status))
2726         WARN("IOCTL_STORAGE_MANAGE_DATA_SET_ATTRIBUTES returned %08lx\n", Status);
2727 }
2728 
2729 static NTSTATUS add_device(device_extension* Vcb, PIRP Irp, KPROCESSOR_MODE processor_mode) {
2730     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
2731     NTSTATUS Status;
2732     PFILE_OBJECT fileobj, mountmgrfo;
2733     PDEVICE_OBJECT DeviceObject;
2734     HANDLE h;
2735     LIST_ENTRY* le;
2736     device* dev;
2737     DEV_ITEM* di;
2738     uint64_t dev_id, size;
2739     uint8_t* mb;
2740     uint64_t* stats;
2741     UNICODE_STRING mmdevpath, pnp_name, pnp_name2;
2742     volume_child* vc;
2743     PDEVICE_OBJECT mountmgr;
2744     KEY searchkey;
2745     traverse_ptr tp;
2746     STORAGE_DEVICE_NUMBER sdn;
2747     volume_device_extension* vde;
2748     pdo_device_extension* pdode;
2749     const GUID* pnp_guid;
2750     GET_LENGTH_INFORMATION gli;
2751 
2752     pnp_name.Buffer = NULL;
2753 
2754     if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE), processor_mode))
2755         return STATUS_PRIVILEGE_NOT_HELD;
2756 
2757     if (!Vcb->vde) {
2758         WARN("not allowing second device to be added to non-PNP device\n");
2759         return STATUS_NOT_SUPPORTED;
2760     }
2761 
2762     if (Vcb->readonly) // FIXME - handle adding R/W device to seeding device
2763         return STATUS_MEDIA_WRITE_PROTECTED;
2764 
2765 #if defined(_WIN64)
2766     if (IoIs32bitProcess(Irp)) {
2767         if (IrpSp->Parameters.FileSystemControl.InputBufferLength != sizeof(uint32_t))
2768             return STATUS_INVALID_PARAMETER;
2769 
2770         h = (HANDLE)LongToHandle((*(uint32_t*)Irp->AssociatedIrp.SystemBuffer));
2771     } else {
2772 #endif
2773         if (IrpSp->Parameters.FileSystemControl.InputBufferLength != sizeof(HANDLE))
2774             return STATUS_INVALID_PARAMETER;
2775 
2776         h = *(PHANDLE)Irp->AssociatedIrp.SystemBuffer;
2777 #if defined(_WIN64)
2778     }
2779 #endif
2780 
2781     Status = ObReferenceObjectByHandle(h, 0, *IoFileObjectType, Irp->RequestorMode, (void**)&fileobj, NULL);
2782 
2783     if (!NT_SUCCESS(Status)) {
2784         ERR("ObReferenceObjectByHandle returned %08lx\n", Status);
2785         return Status;
2786     }
2787 
2788     DeviceObject = fileobj->DeviceObject;
2789 
2790     Status = get_device_pnp_name(DeviceObject, &pnp_name, &pnp_guid);
2791     if (!NT_SUCCESS(Status)) {
2792         ERR("get_device_pnp_name returned %08lx\n", Status);
2793         ObDereferenceObject(fileobj);
2794         return Status;
2795     }
2796 
2797     // If this is a disk, we have been handed the PDO, so need to go up to find something we can use
2798     if (RtlCompareMemory(pnp_guid, &GUID_DEVINTERFACE_DISK, sizeof(GUID)) == sizeof(GUID) && DeviceObject->AttachedDevice)
2799         DeviceObject = DeviceObject->AttachedDevice;
2800 
2801     Status = dev_ioctl(DeviceObject, IOCTL_DISK_IS_WRITABLE, NULL, 0, NULL, 0, true, NULL);
2802     if (!NT_SUCCESS(Status)) {
2803         ERR("IOCTL_DISK_IS_WRITABLE returned %08lx\n", Status);
2804         ObDereferenceObject(fileobj);
2805         return Status;
2806     }
2807 
2808     Status = is_device_part_of_mounted_btrfs_raid(DeviceObject, fileobj);
2809     if (!NT_SUCCESS(Status)) {
2810         ERR("is_device_part_of_mounted_btrfs_raid returned %08lx\n", Status);
2811         ObDereferenceObject(fileobj);
2812         return Status;
2813     }
2814 
2815     // if disk, check it has no partitions
2816     if (RtlCompareMemory(pnp_guid, &GUID_DEVINTERFACE_DISK, sizeof(GUID)) == sizeof(GUID)) {
2817         ULONG dlisize;
2818         DRIVE_LAYOUT_INFORMATION_EX* dli = NULL;
2819 
2820         dlisize = 0;
2821 
2822         do {
2823             dlisize += 1024;
2824 
2825             if (dli)
2826                 ExFreePool(dli);
2827 
2828             dli = ExAllocatePoolWithTag(PagedPool, dlisize, ALLOC_TAG);
2829             if (!dli) {
2830                 ERR("out of memory\n");
2831                 Status = STATUS_INSUFFICIENT_RESOURCES;
2832                 goto end2;
2833             }
2834 
2835             Status = dev_ioctl(DeviceObject, IOCTL_DISK_GET_DRIVE_LAYOUT_EX, NULL, 0, dli, dlisize, true, NULL);
2836         } while (Status == STATUS_BUFFER_TOO_SMALL);
2837 
2838         if (NT_SUCCESS(Status) && dli->PartitionCount > 0) {
2839             ExFreePool(dli);
2840             ERR("not adding disk which has partitions\n");
2841             Status = STATUS_DEVICE_NOT_READY;
2842             goto end2;
2843         }
2844 
2845         ExFreePool(dli);
2846     }
2847 
2848     Status = dev_ioctl(DeviceObject, IOCTL_STORAGE_GET_DEVICE_NUMBER, NULL, 0,
2849                        &sdn, sizeof(STORAGE_DEVICE_NUMBER), true, NULL);
2850     if (NT_SUCCESS(Status)) {
2851         if (sdn.DeviceType != FILE_DEVICE_DISK) { // FIXME - accept floppies and CDs?
2852             WARN("device was not disk\n");
2853             ObDereferenceObject(fileobj);
2854             return STATUS_INVALID_PARAMETER;
2855         }
2856     } else {
2857         sdn.DeviceNumber = 0xffffffff;
2858         sdn.PartitionNumber = 0xffffffff;
2859     }
2860 
2861     Status = dev_ioctl(DeviceObject, IOCTL_DISK_GET_LENGTH_INFO, NULL, 0,
2862                         &gli, sizeof(gli), true, NULL);
2863     if (!NT_SUCCESS(Status)) {
2864         ERR("error reading length information: %08lx\n", Status);
2865         ObDereferenceObject(fileobj);
2866         return Status;
2867     }
2868 
2869     size = gli.Length.QuadPart;
2870 
2871     if (size < 0x100000) {
2872         ERR("device was not large enough to hold FS (%I64x bytes, need at least 1 MB)\n", size);
2873         ObDereferenceObject(fileobj);
2874         return STATUS_INTERNAL_ERROR;
2875     }
2876 
2877     volume_removal(drvobj, &pnp_name);
2878 
2879     ExAcquireResourceExclusiveLite(&Vcb->tree_lock, true);
2880 
2881     if (Vcb->need_write)
2882         Status = do_write(Vcb, Irp);
2883     else
2884         Status = STATUS_SUCCESS;
2885 
2886     free_trees(Vcb);
2887 
2888     if (!NT_SUCCESS(Status)) {
2889         ERR("do_write returned %08lx\n", Status);
2890         goto end;
2891     }
2892 
2893     dev = ExAllocatePoolWithTag(NonPagedPool, sizeof(device), ALLOC_TAG);
2894     if (!dev) {
2895         ERR("out of memory\n");
2896         Status = STATUS_INSUFFICIENT_RESOURCES;
2897         goto end;
2898     }
2899 
2900     RtlZeroMemory(dev, sizeof(device));
2901 
2902     dev->devobj = DeviceObject;
2903     dev->fileobj = fileobj;
2904     dev->seeding = false;
2905     init_device(Vcb, dev, true);
2906 
2907     InitializeListHead(&dev->space);
2908 
2909     if (size > 0x100000) { // add disk hole - the first MB is marked as used
2910         Status = add_space_entry(&dev->space, NULL, 0x100000, size - 0x100000);
2911         if (!NT_SUCCESS(Status)) {
2912             ERR("add_space_entry returned %08lx\n", Status);
2913             goto end;
2914         }
2915     }
2916 
2917     dev_id = 0;
2918 
2919     le = Vcb->devices.Flink;
2920     while (le != &Vcb->devices) {
2921         device* dev2 = CONTAINING_RECORD(le, device, list_entry);
2922 
2923         if (dev2->devitem.dev_id > dev_id)
2924             dev_id = dev2->devitem.dev_id;
2925 
2926         le = le->Flink;
2927     }
2928 
2929     dev_id++;
2930 
2931     dev->devitem.dev_id = dev_id;
2932     dev->devitem.num_bytes = size;
2933     dev->devitem.bytes_used = 0;
2934     dev->devitem.optimal_io_align = Vcb->superblock.sector_size;
2935     dev->devitem.optimal_io_width = Vcb->superblock.sector_size;
2936     dev->devitem.minimal_io_size = Vcb->superblock.sector_size;
2937     dev->devitem.type = 0;
2938     dev->devitem.generation = 0;
2939     dev->devitem.start_offset = 0;
2940     dev->devitem.dev_group = 0;
2941     dev->devitem.seek_speed = 0;
2942     dev->devitem.bandwidth = 0;
2943     get_uuid(&dev->devitem.device_uuid);
2944     dev->devitem.fs_uuid = Vcb->superblock.uuid;
2945 
2946     di = ExAllocatePoolWithTag(PagedPool, sizeof(DEV_ITEM), ALLOC_TAG);
2947     if (!di) {
2948         ERR("out of memory\n");
2949         goto end;
2950     }
2951 
2952     RtlCopyMemory(di, &dev->devitem, sizeof(DEV_ITEM));
2953 
2954     Status = insert_tree_item(Vcb, Vcb->chunk_root, 1, TYPE_DEV_ITEM, di->dev_id, di, sizeof(DEV_ITEM), NULL, Irp);
2955     if (!NT_SUCCESS(Status)) {
2956         ERR("insert_tree_item returned %08lx\n", Status);
2957         ExFreePool(di);
2958         goto end;
2959     }
2960 
2961     // add stats entry to dev tree
2962     stats = ExAllocatePoolWithTag(PagedPool, sizeof(uint64_t) * 5, ALLOC_TAG);
2963     if (!stats) {
2964         ERR("out of memory\n");
2965         Status = STATUS_INSUFFICIENT_RESOURCES;
2966         goto end;
2967     }
2968 
2969     RtlZeroMemory(stats, sizeof(uint64_t) * 5);
2970 
2971     searchkey.obj_id = 0;
2972     searchkey.obj_type = TYPE_DEV_STATS;
2973     searchkey.offset = di->dev_id;
2974 
2975     Status = find_item(Vcb, Vcb->dev_root, &tp, &searchkey, false, Irp);
2976     if (!NT_SUCCESS(Status)) {
2977         ERR("error - find_item returned %08lx\n", Status);
2978         ExFreePool(stats);
2979         goto end;
2980     }
2981 
2982     if (!keycmp(tp.item->key, searchkey)) {
2983         Status = delete_tree_item(Vcb, &tp);
2984         if (!NT_SUCCESS(Status)) {
2985             ERR("delete_tree_item returned %08lx\n", Status);
2986             ExFreePool(stats);
2987             goto end;
2988         }
2989     }
2990 
2991     Status = insert_tree_item(Vcb, Vcb->dev_root, 0, TYPE_DEV_STATS, di->dev_id, stats, sizeof(uint64_t) * 5, NULL, Irp);
2992     if (!NT_SUCCESS(Status)) {
2993         ERR("insert_tree_item returned %08lx\n", Status);
2994         ExFreePool(stats);
2995         goto end;
2996     }
2997 
2998     if (dev->trim && !dev->readonly && !Vcb->options.no_trim)
2999         trim_whole_device(dev);
3000 
3001     // We clear the first megabyte of the device, so Windows doesn't identify it as another FS
3002     mb = ExAllocatePoolWithTag(PagedPool, 0x100000, ALLOC_TAG);
3003     if (!mb) {
3004         ERR("out of memory\n");
3005         Status = STATUS_INSUFFICIENT_RESOURCES;
3006         goto end;
3007     }
3008 
3009     RtlZeroMemory(mb, 0x100000);
3010 
3011     Status = write_data_phys(DeviceObject, fileobj, 0, mb, 0x100000);
3012     if (!NT_SUCCESS(Status)) {
3013         ERR("write_data_phys returned %08lx\n", Status);
3014         ExFreePool(mb);
3015         goto end;
3016     }
3017 
3018     ExFreePool(mb);
3019 
3020     vde = Vcb->vde;
3021     pdode = vde->pdode;
3022 
3023     vc = ExAllocatePoolWithTag(NonPagedPool, sizeof(volume_child), ALLOC_TAG);
3024     if (!vc) {
3025         ERR("out of memory\n");
3026         Status = STATUS_INSUFFICIENT_RESOURCES;
3027         goto end;
3028     }
3029 
3030     vc->uuid = dev->devitem.device_uuid;
3031     vc->devid = dev_id;
3032     vc->generation = Vcb->superblock.generation;
3033     vc->devobj = DeviceObject;
3034     vc->fileobj = fileobj;
3035     vc->notification_entry = NULL;
3036     vc->boot_volume = false;
3037 
3038     Status = IoRegisterPlugPlayNotification(EventCategoryTargetDeviceChange, 0, fileobj,
3039                                             drvobj, pnp_removal, vde->pdode, &vc->notification_entry);
3040     if (!NT_SUCCESS(Status))
3041         WARN("IoRegisterPlugPlayNotification returned %08lx\n", Status);
3042 
3043     pnp_name2 = pnp_name;
3044 
3045     if (pnp_name.Length > 4 * sizeof(WCHAR) && pnp_name.Buffer[0] == '\\' && (pnp_name.Buffer[1] == '\\' || pnp_name.Buffer[1] == '?') &&
3046         pnp_name.Buffer[2] == '?' && pnp_name.Buffer[3] == '\\') {
3047         pnp_name2.Buffer = &pnp_name2.Buffer[3];
3048         pnp_name2.Length -= 3 * sizeof(WCHAR);
3049         pnp_name2.MaximumLength -= 3 * sizeof(WCHAR);
3050     }
3051 
3052     vc->pnp_name.Length = vc->pnp_name.MaximumLength = pnp_name2.Length;
3053 
3054     if (pnp_name2.Length == 0)
3055         vc->pnp_name.Buffer = NULL;
3056     else {
3057         vc->pnp_name.Buffer = ExAllocatePoolWithTag(PagedPool, pnp_name2.Length, ALLOC_TAG);
3058         if (!vc->pnp_name.Buffer) {
3059             ERR("out of memory\n");
3060             Status = STATUS_INSUFFICIENT_RESOURCES;
3061             goto end;
3062         }
3063 
3064         RtlCopyMemory(vc->pnp_name.Buffer, pnp_name2.Buffer, pnp_name2.Length);
3065     }
3066 
3067     vc->size = size;
3068     vc->seeding = false;
3069     vc->disk_num = sdn.DeviceNumber;
3070     vc->part_num = sdn.PartitionNumber;
3071     vc->had_drive_letter = false;
3072 
3073     ExAcquireResourceExclusiveLite(&pdode->child_lock, true);
3074     InsertTailList(&pdode->children, &vc->list_entry);
3075     pdode->num_children++;
3076     pdode->children_loaded++;
3077     ExReleaseResourceLite(&pdode->child_lock);
3078 
3079     RtlInitUnicodeString(&mmdevpath, MOUNTMGR_DEVICE_NAME);
3080     Status = IoGetDeviceObjectPointer(&mmdevpath, FILE_READ_ATTRIBUTES, &mountmgrfo, &mountmgr);
3081     if (!NT_SUCCESS(Status))
3082         ERR("IoGetDeviceObjectPointer returned %08lx\n", Status);
3083     else {
3084         Status = remove_drive_letter(mountmgr, &pnp_name);
3085         if (!NT_SUCCESS(Status) && Status != STATUS_NOT_FOUND)
3086             WARN("remove_drive_letter returned %08lx\n", Status);
3087 
3088         vc->had_drive_letter = NT_SUCCESS(Status);
3089 
3090         ObDereferenceObject(mountmgrfo);
3091     }
3092 
3093     Vcb->superblock.num_devices++;
3094     Vcb->superblock.total_bytes += size;
3095     Vcb->devices_loaded++;
3096     InsertTailList(&Vcb->devices, &dev->list_entry);
3097 
3098     // FIXME - send notification that volume size has increased
3099 
3100     ObReferenceObject(DeviceObject); // for Vcb
3101 
3102     Status = do_write(Vcb, Irp);
3103     if (!NT_SUCCESS(Status))
3104         ERR("do_write returned %08lx\n", Status);
3105 
3106     ObReferenceObject(fileobj);
3107 
3108 end:
3109     free_trees(Vcb);
3110 
3111     ExReleaseResourceLite(&Vcb->tree_lock);
3112 
3113 end2:
3114     ObDereferenceObject(fileobj);
3115 
3116     if (pnp_name.Buffer)
3117         ExFreePool(pnp_name.Buffer);
3118 
3119     if (NT_SUCCESS(Status))
3120         FsRtlNotifyVolumeEvent(Vcb->root_file, FSRTL_VOLUME_CHANGE_SIZE);
3121 
3122     return Status;
3123 }
3124 
3125 static NTSTATUS allow_extended_dasd_io(device_extension* Vcb, PFILE_OBJECT FileObject) {
3126     fcb* fcb;
3127     ccb* ccb;
3128 
3129     TRACE("FSCTL_ALLOW_EXTENDED_DASD_IO\n");
3130 
3131     if (!FileObject)
3132         return STATUS_INVALID_PARAMETER;
3133 
3134     fcb = FileObject->FsContext;
3135     ccb = FileObject->FsContext2;
3136 
3137     if (!fcb)
3138         return STATUS_INVALID_PARAMETER;
3139 
3140     if (fcb != Vcb->volume_fcb)
3141         return STATUS_INVALID_PARAMETER;
3142 
3143     if (!ccb)
3144         return STATUS_INVALID_PARAMETER;
3145 
3146     ccb->allow_extended_dasd_io = true;
3147 
3148     return STATUS_SUCCESS;
3149 }
3150 
3151 static NTSTATUS query_uuid(device_extension* Vcb, void* data, ULONG length) {
3152     if (length < sizeof(BTRFS_UUID))
3153         return STATUS_BUFFER_OVERFLOW;
3154 
3155     RtlCopyMemory(data, &Vcb->superblock.uuid, sizeof(BTRFS_UUID));
3156 
3157     return STATUS_SUCCESS;
3158 }
3159 
3160 static NTSTATUS reset_stats(device_extension* Vcb, void* data, ULONG length, KPROCESSOR_MODE processor_mode) {
3161     uint64_t devid;
3162     NTSTATUS Status;
3163     LIST_ENTRY* le;
3164 
3165     if (length < sizeof(uint64_t))
3166         return STATUS_INVALID_PARAMETER;
3167 
3168     if (Vcb->readonly)
3169         return STATUS_MEDIA_WRITE_PROTECTED;
3170 
3171     if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE), processor_mode))
3172         return STATUS_PRIVILEGE_NOT_HELD;
3173 
3174     devid = *((uint64_t*)data);
3175 
3176     ExAcquireResourceSharedLite(&Vcb->tree_lock, true);
3177 
3178     le = Vcb->devices.Flink;
3179 
3180     while (le != &Vcb->devices) {
3181         device* dev = CONTAINING_RECORD(le, device, list_entry);
3182 
3183         if (dev->devitem.dev_id == devid) {
3184             RtlZeroMemory(dev->stats, sizeof(uint64_t) * 5);
3185             dev->stats_changed = true;
3186             Vcb->stats_changed = true;
3187             Vcb->need_write = true;
3188             Status = STATUS_SUCCESS;
3189             goto end;
3190         }
3191 
3192         le = le->Flink;
3193     }
3194 
3195     WARN("device %I64x not found\n", devid);
3196     Status = STATUS_INVALID_PARAMETER;
3197 
3198 end:
3199     ExReleaseResourceLite(&Vcb->tree_lock);
3200 
3201     return Status;
3202 }
3203 
3204 static NTSTATUS get_integrity_information(device_extension* Vcb, PFILE_OBJECT FileObject, void* data, ULONG datalen) {
3205     FSCTL_GET_INTEGRITY_INFORMATION_BUFFER* fgiib = (FSCTL_GET_INTEGRITY_INFORMATION_BUFFER*)data;
3206 
3207     TRACE("FSCTL_GET_INTEGRITY_INFORMATION\n");
3208 
3209     // STUB
3210 
3211     if (!FileObject)
3212         return STATUS_INVALID_PARAMETER;
3213 
3214     if (!data || datalen < sizeof(FSCTL_GET_INTEGRITY_INFORMATION_BUFFER))
3215         return STATUS_INVALID_PARAMETER;
3216 
3217     fgiib->ChecksumAlgorithm = 0;
3218     fgiib->Reserved = 0;
3219     fgiib->Flags = 0;
3220     fgiib->ChecksumChunkSizeInBytes = Vcb->superblock.sector_size;
3221     fgiib->ClusterSizeInBytes = Vcb->superblock.sector_size;
3222 
3223     return STATUS_SUCCESS;
3224 }
3225 
3226 static NTSTATUS set_integrity_information(PFILE_OBJECT FileObject, void* data, ULONG datalen) {
3227     TRACE("FSCTL_SET_INTEGRITY_INFORMATION\n");
3228 
3229     // STUB
3230 
3231     if (!FileObject)
3232         return STATUS_INVALID_PARAMETER;
3233 
3234     if (!data || datalen < sizeof(FSCTL_SET_INTEGRITY_INFORMATION_BUFFER))
3235         return STATUS_INVALID_PARAMETER;
3236 
3237     return STATUS_SUCCESS;
3238 }
3239 
3240 bool fcb_is_inline(fcb* fcb) {
3241     LIST_ENTRY* le;
3242 
3243     le = fcb->extents.Flink;
3244     while (le != &fcb->extents) {
3245         extent* ext = CONTAINING_RECORD(le, extent, list_entry);
3246 
3247         if (!ext->ignore)
3248             return ext->extent_data.type == EXTENT_TYPE_INLINE;
3249 
3250         le = le->Flink;
3251     }
3252 
3253     return false;
3254 }
3255 
3256 static NTSTATUS duplicate_extents(device_extension* Vcb, PFILE_OBJECT FileObject, void* data, ULONG datalen, PIRP Irp) {
3257     DUPLICATE_EXTENTS_DATA* ded = (DUPLICATE_EXTENTS_DATA*)data;
3258     fcb *fcb = FileObject ? FileObject->FsContext : NULL, *sourcefcb;
3259     ccb *ccb = FileObject ? FileObject->FsContext2 : NULL, *sourceccb;
3260     NTSTATUS Status;
3261     PFILE_OBJECT sourcefo;
3262     uint64_t sourcelen, nbytes = 0;
3263     LIST_ENTRY rollback, *le, newexts;
3264     LARGE_INTEGER time;
3265     BTRFS_TIME now;
3266     bool make_inline;
3267 
3268     if (!ded || datalen < sizeof(DUPLICATE_EXTENTS_DATA))
3269         return STATUS_BUFFER_TOO_SMALL;
3270 
3271     if (Vcb->readonly)
3272         return STATUS_MEDIA_WRITE_PROTECTED;
3273 
3274     if (ded->ByteCount.QuadPart == 0)
3275         return STATUS_SUCCESS;
3276 
3277     if (!fcb || !ccb || fcb == Vcb->volume_fcb)
3278         return STATUS_INVALID_PARAMETER;
3279 
3280     if (is_subvol_readonly(fcb->subvol, Irp))
3281         return STATUS_ACCESS_DENIED;
3282 
3283     if (Irp->RequestorMode == UserMode && !(ccb->access & FILE_WRITE_DATA)) {
3284         WARN("insufficient privileges\n");
3285         return STATUS_ACCESS_DENIED;
3286     }
3287 
3288     if (!fcb->ads && fcb->type != BTRFS_TYPE_FILE && fcb->type != BTRFS_TYPE_SYMLINK)
3289         return STATUS_INVALID_PARAMETER;
3290 
3291     Status = ObReferenceObjectByHandle(ded->FileHandle, 0, *IoFileObjectType, Irp->RequestorMode, (void**)&sourcefo, NULL);
3292     if (!NT_SUCCESS(Status)) {
3293         ERR("ObReferenceObjectByHandle returned %08lx\n", Status);
3294         return Status;
3295     }
3296 
3297     if (sourcefo->DeviceObject != FileObject->DeviceObject) {
3298         WARN("source and destination are on different volumes\n");
3299         ObDereferenceObject(sourcefo);
3300         return STATUS_INVALID_PARAMETER;
3301     }
3302 
3303     sourcefcb = sourcefo->FsContext;
3304     sourceccb = sourcefo->FsContext2;
3305 
3306     if (!sourcefcb || !sourceccb || sourcefcb == Vcb->volume_fcb) {
3307         ObDereferenceObject(sourcefo);
3308         return STATUS_INVALID_PARAMETER;
3309     }
3310 
3311     if (!sourcefcb->ads && !fcb->ads) {
3312         if ((ded->SourceFileOffset.QuadPart & (Vcb->superblock.sector_size - 1)) || (ded->TargetFileOffset.QuadPart & (Vcb->superblock.sector_size - 1))) {
3313             ObDereferenceObject(sourcefo);
3314             return STATUS_INVALID_PARAMETER;
3315         }
3316 
3317         if (ded->ByteCount.QuadPart & (Vcb->superblock.sector_size - 1)) {
3318             ObDereferenceObject(sourcefo);
3319             return STATUS_INVALID_PARAMETER;
3320         }
3321     }
3322 
3323     if (Irp->RequestorMode == UserMode && (!(sourceccb->access & FILE_READ_DATA) || !(sourceccb->access & FILE_READ_ATTRIBUTES))) {
3324         WARN("insufficient privileges\n");
3325         ObDereferenceObject(sourcefo);
3326         return STATUS_ACCESS_DENIED;
3327     }
3328 
3329     if (!sourcefcb->ads && sourcefcb->type != BTRFS_TYPE_FILE && sourcefcb->type != BTRFS_TYPE_SYMLINK) {
3330         ObDereferenceObject(sourcefo);
3331         return STATUS_INVALID_PARAMETER;
3332     }
3333 
3334     sourcelen = sourcefcb->ads ? sourcefcb->adsdata.Length : sourcefcb->inode_item.st_size;
3335 
3336     if (sector_align(sourcelen, Vcb->superblock.sector_size) < (uint64_t)ded->SourceFileOffset.QuadPart + (uint64_t)ded->ByteCount.QuadPart) {
3337         ObDereferenceObject(sourcefo);
3338         return STATUS_NOT_SUPPORTED;
3339     }
3340 
3341     if (fcb == sourcefcb &&
3342         ((ded->SourceFileOffset.QuadPart >= ded->TargetFileOffset.QuadPart && ded->SourceFileOffset.QuadPart < ded->TargetFileOffset.QuadPart + ded->ByteCount.QuadPart) ||
3343         (ded->TargetFileOffset.QuadPart >= ded->SourceFileOffset.QuadPart && ded->TargetFileOffset.QuadPart < ded->SourceFileOffset.QuadPart + ded->ByteCount.QuadPart))) {
3344         WARN("source and destination are the same, and the ranges overlap\n");
3345         ObDereferenceObject(sourcefo);
3346         return STATUS_INVALID_PARAMETER;
3347     }
3348 
3349     // fail if nocsum flag set on one file but not the other
3350     if (!fcb->ads && !sourcefcb->ads && (fcb->inode_item.flags & BTRFS_INODE_NODATASUM) != (sourcefcb->inode_item.flags & BTRFS_INODE_NODATASUM)) {
3351         ObDereferenceObject(sourcefo);
3352         return STATUS_INVALID_PARAMETER;
3353     }
3354 
3355     InitializeListHead(&rollback);
3356     InitializeListHead(&newexts);
3357 
3358     ExAcquireResourceSharedLite(&Vcb->tree_lock, true);
3359 
3360     ExAcquireResourceExclusiveLite(fcb->Header.Resource, true);
3361 
3362     if (fcb != sourcefcb)
3363         ExAcquireResourceSharedLite(sourcefcb->Header.Resource, true);
3364 
3365     if (!FsRtlFastCheckLockForWrite(&fcb->lock, &ded->TargetFileOffset, &ded->ByteCount, 0, FileObject, PsGetCurrentProcess())) {
3366         Status = STATUS_FILE_LOCK_CONFLICT;
3367         goto end;
3368     }
3369 
3370     if (!FsRtlFastCheckLockForRead(&sourcefcb->lock, &ded->SourceFileOffset, &ded->ByteCount, 0, FileObject, PsGetCurrentProcess())) {
3371         Status = STATUS_FILE_LOCK_CONFLICT;
3372         goto end;
3373     }
3374 
3375     make_inline = fcb->ads ? false : (fcb->inode_item.st_size <= Vcb->options.max_inline || fcb_is_inline(fcb));
3376 
3377     if (fcb->ads || sourcefcb->ads || make_inline || fcb_is_inline(sourcefcb)) {
3378         uint8_t* data2;
3379         ULONG bytes_read, dataoff, datalen2;
3380 
3381         if (make_inline) {
3382             dataoff = (ULONG)ded->TargetFileOffset.QuadPart;
3383             datalen2 = (ULONG)fcb->inode_item.st_size;
3384         } else if (fcb->ads) {
3385             dataoff = 0;
3386             datalen2 = (ULONG)ded->ByteCount.QuadPart;
3387         } else {
3388             dataoff = ded->TargetFileOffset.QuadPart % Vcb->superblock.sector_size;
3389             datalen2 = (ULONG)sector_align(ded->ByteCount.QuadPart + dataoff, Vcb->superblock.sector_size);
3390         }
3391 
3392         data2 = ExAllocatePoolWithTag(PagedPool, datalen2, ALLOC_TAG);
3393         if (!data2) {
3394             ERR("out of memory\n");
3395             Status = STATUS_INSUFFICIENT_RESOURCES;
3396             goto end;
3397         }
3398 
3399         if (dataoff > 0) {
3400             if (make_inline)
3401                 Status = read_file(fcb, data2, 0, datalen2, NULL, Irp);
3402             else
3403                 Status = read_file(fcb, data2, ded->TargetFileOffset.QuadPart - dataoff, dataoff, NULL, Irp);
3404 
3405             if (!NT_SUCCESS(Status)) {
3406                 ERR("read_file returned %08lx\n", Status);
3407                 ExFreePool(data2);
3408                 goto end;
3409             }
3410         }
3411 
3412         if (sourcefcb->ads) {
3413             Status = read_stream(sourcefcb, data2 + dataoff, ded->SourceFileOffset.QuadPart, (ULONG)ded->ByteCount.QuadPart, &bytes_read);
3414             if (!NT_SUCCESS(Status)) {
3415                 ERR("read_stream returned %08lx\n", Status);
3416                 ExFreePool(data2);
3417                 goto end;
3418             }
3419         } else {
3420             Status = read_file(sourcefcb, data2 + dataoff, ded->SourceFileOffset.QuadPart, ded->ByteCount.QuadPart, &bytes_read, Irp);
3421             if (!NT_SUCCESS(Status)) {
3422                 ERR("read_file returned %08lx\n", Status);
3423                 ExFreePool(data2);
3424                 goto end;
3425             }
3426         }
3427 
3428         if (dataoff + bytes_read < datalen2)
3429             RtlZeroMemory(data2 + dataoff + bytes_read, datalen2 - bytes_read);
3430 
3431         if (fcb->ads)
3432             RtlCopyMemory(&fcb->adsdata.Buffer[ded->TargetFileOffset.QuadPart], data2, (USHORT)min(ded->ByteCount.QuadPart, fcb->adsdata.Length - ded->TargetFileOffset.QuadPart));
3433         else if (make_inline) {
3434             uint16_t edsize;
3435             EXTENT_DATA* ed;
3436 
3437             Status = excise_extents(Vcb, fcb, 0, sector_align(fcb->inode_item.st_size, Vcb->superblock.sector_size), Irp, &rollback);
3438             if (!NT_SUCCESS(Status)) {
3439                 ERR("excise_extents returned %08lx\n", Status);
3440                 ExFreePool(data2);
3441                 goto end;
3442             }
3443 
3444             edsize = (uint16_t)(offsetof(EXTENT_DATA, data[0]) + datalen2);
3445 
3446             ed = ExAllocatePoolWithTag(PagedPool, edsize, ALLOC_TAG);
3447             if (!ed) {
3448                 ERR("out of memory\n");
3449                 ExFreePool(data2);
3450                 Status = STATUS_INSUFFICIENT_RESOURCES;
3451                 goto end;
3452             }
3453 
3454             ed->generation = Vcb->superblock.generation;
3455             ed->decoded_size = fcb->inode_item.st_size;
3456             ed->compression = BTRFS_COMPRESSION_NONE;
3457             ed->encryption = BTRFS_ENCRYPTION_NONE;
3458             ed->encoding = BTRFS_ENCODING_NONE;
3459             ed->type = EXTENT_TYPE_INLINE;
3460 
3461             RtlCopyMemory(ed->data, data2, datalen2);
3462 
3463             Status = add_extent_to_fcb(fcb, 0, ed, edsize, false, NULL, &rollback);
3464             if (!NT_SUCCESS(Status)) {
3465                 ERR("add_extent_to_fcb returned %08lx\n", Status);
3466                 ExFreePool(data2);
3467                 goto end;
3468             }
3469 
3470             fcb->inode_item.st_blocks += datalen2;
3471         } else {
3472             uint64_t start = ded->TargetFileOffset.QuadPart - (ded->TargetFileOffset.QuadPart % Vcb->superblock.sector_size);
3473 
3474             Status = do_write_file(fcb, start, start + datalen2, data2, Irp, false, 0, &rollback);
3475             if (!NT_SUCCESS(Status)) {
3476                 ERR("do_write_file returned %08lx\n", Status);
3477                 ExFreePool(data2);
3478                 goto end;
3479             }
3480         }
3481 
3482         ExFreePool(data2);
3483     } else {
3484         LIST_ENTRY* lastextle;
3485 
3486         le = sourcefcb->extents.Flink;
3487         while (le != &sourcefcb->extents) {
3488             extent* ext = CONTAINING_RECORD(le, extent, list_entry);
3489 
3490             if (!ext->ignore) {
3491                 if (ext->offset >= (uint64_t)ded->SourceFileOffset.QuadPart + (uint64_t)ded->ByteCount.QuadPart)
3492                     break;
3493 
3494                 if (ext->extent_data.type != EXTENT_TYPE_INLINE) {
3495                     ULONG extlen = offsetof(extent, extent_data) + sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2);
3496                     extent* ext2;
3497                     EXTENT_DATA2 *ed2s, *ed2d;
3498                     chunk* c;
3499 
3500                     ed2s = (EXTENT_DATA2*)ext->extent_data.data;
3501 
3502                     if (ext->offset + ed2s->num_bytes <= (uint64_t)ded->SourceFileOffset.QuadPart) {
3503                         le = le->Flink;
3504                         continue;
3505                     }
3506 
3507                     ext2 = ExAllocatePoolWithTag(PagedPool, extlen, ALLOC_TAG);
3508                     if (!ext2) {
3509                         ERR("out of memory\n");
3510                         Status = STATUS_INSUFFICIENT_RESOURCES;
3511                         goto end;
3512                     }
3513 
3514                     if (ext->offset < (uint64_t)ded->SourceFileOffset.QuadPart)
3515                         ext2->offset = ded->TargetFileOffset.QuadPart;
3516                     else
3517                         ext2->offset = ext->offset - ded->SourceFileOffset.QuadPart + ded->TargetFileOffset.QuadPart;
3518 
3519                     ext2->datalen = sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2);
3520                     ext2->unique = false;
3521                     ext2->ignore = false;
3522                     ext2->inserted = true;
3523 
3524                     ext2->extent_data.generation = Vcb->superblock.generation;
3525                     ext2->extent_data.decoded_size = ext->extent_data.decoded_size;
3526                     ext2->extent_data.compression = ext->extent_data.compression;
3527                     ext2->extent_data.encryption = ext->extent_data.encryption;
3528                     ext2->extent_data.encoding = ext->extent_data.encoding;
3529                     ext2->extent_data.type = ext->extent_data.type;
3530 
3531                     ed2d = (EXTENT_DATA2*)ext2->extent_data.data;
3532 
3533                     ed2d->address = ed2s->address;
3534                     ed2d->size = ed2s->size;
3535 
3536                     if (ext->offset < (uint64_t)ded->SourceFileOffset.QuadPart) {
3537                         ed2d->offset = ed2s->offset + ded->SourceFileOffset.QuadPart - ext->offset;
3538                         ed2d->num_bytes = min((uint64_t)ded->ByteCount.QuadPart, ed2s->num_bytes + ext->offset - ded->SourceFileOffset.QuadPart);
3539                     } else {
3540                         ed2d->offset = ed2s->offset;
3541                         ed2d->num_bytes = min(ded->SourceFileOffset.QuadPart + ded->ByteCount.QuadPart - ext->offset, ed2s->num_bytes);
3542                     }
3543 
3544                     if (ext->csum) {
3545                         if (ext->extent_data.compression == BTRFS_COMPRESSION_NONE) {
3546                             ext2->csum = ExAllocatePoolWithTag(PagedPool, (ULONG)(ed2d->num_bytes * Vcb->csum_size / Vcb->superblock.sector_size), ALLOC_TAG);
3547                             if (!ext2->csum) {
3548                                 ERR("out of memory\n");
3549                                 Status = STATUS_INSUFFICIENT_RESOURCES;
3550                                 ExFreePool(ext2);
3551                                 goto end;
3552                             }
3553 
3554                             RtlCopyMemory(ext2->csum, (uint8_t*)ext->csum + ((ed2d->offset - ed2s->offset) * Vcb->csum_size / Vcb->superblock.sector_size),
3555                                           (ULONG)(ed2d->num_bytes * Vcb->csum_size / Vcb->superblock.sector_size));
3556                         } else {
3557                             ext2->csum = ExAllocatePoolWithTag(PagedPool, (ULONG)(ed2d->size * Vcb->csum_size / Vcb->superblock.sector_size), ALLOC_TAG);
3558                             if (!ext2->csum) {
3559                                 ERR("out of memory\n");
3560                                 Status = STATUS_INSUFFICIENT_RESOURCES;
3561                                 ExFreePool(ext2);
3562                                 goto end;
3563                             }
3564 
3565                             RtlCopyMemory(ext2->csum, ext->csum, (ULONG)(ed2s->size * Vcb->csum_size / Vcb->superblock.sector_size));
3566                         }
3567                     } else
3568                         ext2->csum = NULL;
3569 
3570                     InsertTailList(&newexts, &ext2->list_entry);
3571 
3572                     c = get_chunk_from_address(Vcb, ed2s->address);
3573                     if (!c) {
3574                         ERR("get_chunk_from_address(%I64x) failed\n", ed2s->address);
3575                         Status = STATUS_INTERNAL_ERROR;
3576                         goto end;
3577                     }
3578 
3579                     Status = update_changed_extent_ref(Vcb, c, ed2s->address, ed2s->size, fcb->subvol->id, fcb->inode, ext2->offset - ed2d->offset,
3580                                                     1, fcb->inode_item.flags & BTRFS_INODE_NODATASUM, false, Irp);
3581                     if (!NT_SUCCESS(Status)) {
3582                         ERR("update_changed_extent_ref returned %08lx\n", Status);
3583                         goto end;
3584                     }
3585 
3586                     nbytes += ed2d->num_bytes;
3587                 }
3588             }
3589 
3590             le = le->Flink;
3591         }
3592 
3593         Status = excise_extents(Vcb, fcb, ded->TargetFileOffset.QuadPart, ded->TargetFileOffset.QuadPart + ded->ByteCount.QuadPart, Irp, &rollback);
3594         if (!NT_SUCCESS(Status)) {
3595             ERR("excise_extents returned %08lx\n", Status);
3596 
3597             while (!IsListEmpty(&newexts)) {
3598                 extent* ext = CONTAINING_RECORD(RemoveHeadList(&newexts), extent, list_entry);
3599                 ExFreePool(ext);
3600             }
3601 
3602             goto end;
3603         }
3604 
3605         // clear unique flags in source fcb
3606         le = sourcefcb->extents.Flink;
3607         while (le != &sourcefcb->extents) {
3608             extent* ext = CONTAINING_RECORD(le, extent, list_entry);
3609 
3610             if (!ext->ignore && ext->unique && (ext->extent_data.type == EXTENT_TYPE_REGULAR || ext->extent_data.type == EXTENT_TYPE_PREALLOC)) {
3611                 EXTENT_DATA2* ed2s = (EXTENT_DATA2*)ext->extent_data.data;
3612                 LIST_ENTRY* le2;
3613 
3614                 le2 = newexts.Flink;
3615                 while (le2 != &newexts) {
3616                     extent* ext2 = CONTAINING_RECORD(le2, extent, list_entry);
3617 
3618                     if (ext2->extent_data.type == EXTENT_TYPE_REGULAR || ext2->extent_data.type == EXTENT_TYPE_PREALLOC) {
3619                         EXTENT_DATA2* ed2d = (EXTENT_DATA2*)ext2->extent_data.data;
3620 
3621                         if (ed2d->address == ed2s->address && ed2d->size == ed2s->size) {
3622                             ext->unique = false;
3623                             break;
3624                         }
3625                     }
3626 
3627                     le2 = le2->Flink;
3628                 }
3629             }
3630 
3631             le = le->Flink;
3632         }
3633 
3634         lastextle = &fcb->extents;
3635         while (!IsListEmpty(&newexts)) {
3636             extent* ext = CONTAINING_RECORD(RemoveHeadList(&newexts), extent, list_entry);
3637 
3638             add_extent(fcb, lastextle, ext);
3639             lastextle = &ext->list_entry;
3640         }
3641     }
3642 
3643     KeQuerySystemTime(&time);
3644     win_time_to_unix(time, &now);
3645 
3646     if (fcb->ads) {
3647         ccb->fileref->parent->fcb->inode_item.sequence++;
3648 
3649         if (!ccb->user_set_change_time)
3650             ccb->fileref->parent->fcb->inode_item.st_ctime = now;
3651 
3652         ccb->fileref->parent->fcb->inode_item_changed = true;
3653         mark_fcb_dirty(ccb->fileref->parent->fcb);
3654     } else {
3655         fcb->inode_item.st_blocks += nbytes;
3656         fcb->inode_item.sequence++;
3657 
3658         if (!ccb->user_set_change_time)
3659             fcb->inode_item.st_ctime = now;
3660 
3661         if (!ccb->user_set_write_time) {
3662             fcb->inode_item.st_mtime = now;
3663             queue_notification_fcb(ccb->fileref, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED, NULL);
3664         }
3665 
3666         fcb->inode_item_changed = true;
3667         fcb->extents_changed = true;
3668     }
3669 
3670     mark_fcb_dirty(fcb);
3671 
3672     if (FileObject->SectionObjectPointer->DataSectionObject)
3673         CcPurgeCacheSection(FileObject->SectionObjectPointer, &ded->TargetFileOffset, (ULONG)ded->ByteCount.QuadPart, false);
3674 
3675     Status = STATUS_SUCCESS;
3676 
3677 end:
3678     ObDereferenceObject(sourcefo);
3679 
3680     if (NT_SUCCESS(Status))
3681         clear_rollback(&rollback);
3682     else
3683         do_rollback(Vcb, &rollback);
3684 
3685     if (fcb != sourcefcb)
3686         ExReleaseResourceLite(sourcefcb->Header.Resource);
3687 
3688     ExReleaseResourceLite(fcb->Header.Resource);
3689 
3690     ExReleaseResourceLite(&Vcb->tree_lock);
3691 
3692     return Status;
3693 }
3694 
3695 static NTSTATUS mknod(device_extension* Vcb, PFILE_OBJECT FileObject, void* data, ULONG datalen, PIRP Irp) {
3696     NTSTATUS Status;
3697     btrfs_mknod* bmn;
3698     fcb *parfcb, *fcb;
3699     ccb* parccb;
3700     file_ref *parfileref, *fileref;
3701     UNICODE_STRING name;
3702     root* subvol;
3703     uint64_t inode;
3704     dir_child* dc;
3705     LARGE_INTEGER time;
3706     BTRFS_TIME now;
3707     LIST_ENTRY* lastle;
3708     ANSI_STRING utf8;
3709     ULONG len, i;
3710     SECURITY_SUBJECT_CONTEXT subjcont;
3711     PSID owner;
3712     BOOLEAN defaulted;
3713 
3714     TRACE("(%p, %p, %p, %lu)\n", Vcb, FileObject, data, datalen);
3715 
3716     if (!FileObject || !FileObject->FsContext || !FileObject->FsContext2 || FileObject->FsContext == Vcb->volume_fcb)
3717         return STATUS_INVALID_PARAMETER;
3718 
3719     if (Vcb->readonly)
3720         return STATUS_MEDIA_WRITE_PROTECTED;
3721 
3722     parfcb = FileObject->FsContext;
3723 
3724     if (parfcb->type != BTRFS_TYPE_DIRECTORY) {
3725         WARN("trying to create file in something other than a directory\n");
3726         return STATUS_INVALID_PARAMETER;
3727     }
3728 
3729     if (is_subvol_readonly(parfcb->subvol, Irp))
3730         return STATUS_ACCESS_DENIED;
3731 
3732     parccb = FileObject->FsContext2;
3733     parfileref = parccb->fileref;
3734 
3735     if (!parfileref)
3736         return STATUS_INVALID_PARAMETER;
3737 
3738     if (datalen < sizeof(btrfs_mknod))
3739         return STATUS_INVALID_PARAMETER;
3740 
3741     bmn = (btrfs_mknod*)data;
3742 
3743     if (datalen < offsetof(btrfs_mknod, name[0]) + bmn->namelen || bmn->namelen < sizeof(WCHAR))
3744         return STATUS_INVALID_PARAMETER;
3745 
3746     if (bmn->type == BTRFS_TYPE_UNKNOWN || bmn->type > BTRFS_TYPE_SYMLINK)
3747         return STATUS_INVALID_PARAMETER;
3748 
3749     if ((bmn->type == BTRFS_TYPE_DIRECTORY && !(parccb->access & FILE_ADD_SUBDIRECTORY)) ||
3750         (bmn->type != BTRFS_TYPE_DIRECTORY && !(parccb->access & FILE_ADD_FILE))) {
3751         WARN("insufficient privileges\n");
3752         return STATUS_ACCESS_DENIED;
3753     }
3754 
3755     if (bmn->inode != 0) {
3756         if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE), Irp->RequestorMode))
3757             return STATUS_PRIVILEGE_NOT_HELD;
3758     }
3759 
3760     for (i = 0; i < bmn->namelen / sizeof(WCHAR); i++) {
3761         if (bmn->name[i] == 0 || bmn->name[i] == '/')
3762             return STATUS_OBJECT_NAME_INVALID;
3763     }
3764 
3765     // don't allow files called . or ..
3766     if (bmn->name[0] == '.' && (bmn->namelen == sizeof(WCHAR) || (bmn->namelen == 2 * sizeof(WCHAR) && bmn->name[1] == '.')))
3767         return STATUS_OBJECT_NAME_INVALID;
3768 
3769     Status = utf16_to_utf8(NULL, 0, &len, bmn->name, bmn->namelen);
3770     if (!NT_SUCCESS(Status)) {
3771         ERR("utf16_to_utf8 returned %08lx\n", Status);
3772         return Status;
3773     }
3774 
3775     if (len == 0) {
3776         ERR("utf16_to_utf8 returned a length of 0\n");
3777         return STATUS_INTERNAL_ERROR;
3778     }
3779 
3780     if (len > 0xffff) {
3781         ERR("len was too long (%lx)\n", len);
3782         return STATUS_INVALID_PARAMETER;
3783     }
3784 
3785     utf8.MaximumLength = utf8.Length = (USHORT)len;
3786     utf8.Buffer = ExAllocatePoolWithTag(PagedPool, utf8.MaximumLength, ALLOC_TAG);
3787 
3788     if (!utf8.Buffer) {
3789         ERR("out of memory\n");
3790         return STATUS_INSUFFICIENT_RESOURCES;
3791     }
3792 
3793     Status = utf16_to_utf8(utf8.Buffer, len, &len, bmn->name, bmn->namelen);
3794     if (!NT_SUCCESS(Status)) {
3795         ERR("utf16_to_utf8 failed with error %08lx\n", Status);
3796         ExFreePool(utf8.Buffer);
3797         return Status;
3798     }
3799 
3800     name.Length = name.MaximumLength = bmn->namelen;
3801     name.Buffer = bmn->name;
3802 
3803     Status = find_file_in_dir(&name, parfcb, &subvol, &inode, &dc, true);
3804     if (!NT_SUCCESS(Status) && Status != STATUS_OBJECT_NAME_NOT_FOUND) {
3805         ERR("find_file_in_dir returned %08lx\n", Status);
3806         goto end;
3807     }
3808 
3809     if (NT_SUCCESS(Status)) {
3810         WARN("filename already exists\n");
3811         Status = STATUS_OBJECT_NAME_COLLISION;
3812         goto end;
3813     }
3814 
3815     KeQuerySystemTime(&time);
3816     win_time_to_unix(time, &now);
3817 
3818     fcb = create_fcb(Vcb, PagedPool);
3819     if (!fcb) {
3820         ERR("out of memory\n");
3821         Status = STATUS_INSUFFICIENT_RESOURCES;
3822         goto end;
3823     }
3824 
3825     fcb->Vcb = Vcb;
3826 
3827     fcb->inode_item.generation = Vcb->superblock.generation;
3828     fcb->inode_item.transid = Vcb->superblock.generation;
3829     fcb->inode_item.st_size = 0;
3830     fcb->inode_item.st_blocks = 0;
3831     fcb->inode_item.block_group = 0;
3832     fcb->inode_item.st_nlink = 1;
3833     fcb->inode_item.st_uid = UID_NOBODY;
3834     fcb->inode_item.st_gid = GID_NOBODY;
3835     fcb->inode_item.st_mode = inherit_mode(parfcb, bmn->type == BTRFS_TYPE_DIRECTORY);
3836 
3837     if (bmn->type == BTRFS_TYPE_BLOCKDEV || bmn->type == BTRFS_TYPE_CHARDEV)
3838         fcb->inode_item.st_rdev = (minor(bmn->st_rdev) & 0xFFFFF) | ((major(bmn->st_rdev) & 0xFFFFFFFFFFF) << 20);
3839     else
3840         fcb->inode_item.st_rdev = 0;
3841 
3842     fcb->inode_item.flags = 0;
3843     fcb->inode_item.sequence = 1;
3844     fcb->inode_item.st_atime = now;
3845     fcb->inode_item.st_ctime = now;
3846     fcb->inode_item.st_mtime = now;
3847     fcb->inode_item.otime = now;
3848 
3849     if (bmn->type == BTRFS_TYPE_DIRECTORY)
3850         fcb->inode_item.st_mode |= __S_IFDIR;
3851     else if (bmn->type == BTRFS_TYPE_CHARDEV)
3852         fcb->inode_item.st_mode |= __S_IFCHR;
3853     else if (bmn->type == BTRFS_TYPE_BLOCKDEV)
3854         fcb->inode_item.st_mode |= __S_IFBLK;
3855     else if (bmn->type == BTRFS_TYPE_FIFO)
3856         fcb->inode_item.st_mode |= __S_IFIFO;
3857     else if (bmn->type == BTRFS_TYPE_SOCKET)
3858         fcb->inode_item.st_mode |= __S_IFSOCK;
3859     else if (bmn->type == BTRFS_TYPE_SYMLINK)
3860         fcb->inode_item.st_mode |= __S_IFLNK;
3861     else
3862         fcb->inode_item.st_mode |= __S_IFREG;
3863 
3864     if (bmn->type != BTRFS_TYPE_DIRECTORY)
3865         fcb->inode_item.st_mode &= ~(S_IXUSR | S_IXGRP | S_IXOTH); // remove executable bit if not directory
3866 
3867     // inherit nodatacow flag from parent directory
3868     if (parfcb->inode_item.flags & BTRFS_INODE_NODATACOW) {
3869         fcb->inode_item.flags |= BTRFS_INODE_NODATACOW;
3870 
3871         if (bmn->type != BTRFS_TYPE_DIRECTORY)
3872             fcb->inode_item.flags |= BTRFS_INODE_NODATASUM;
3873     }
3874 
3875     if (parfcb->inode_item.flags & BTRFS_INODE_COMPRESS)
3876         fcb->inode_item.flags |= BTRFS_INODE_COMPRESS;
3877 
3878     fcb->prop_compression = parfcb->prop_compression;
3879     fcb->prop_compression_changed = fcb->prop_compression != PropCompression_None;
3880 
3881     fcb->inode_item_changed = true;
3882 
3883     fcb->Header.IsFastIoPossible = fast_io_possible(fcb);
3884     fcb->Header.AllocationSize.QuadPart = 0;
3885     fcb->Header.FileSize.QuadPart = 0;
3886     fcb->Header.ValidDataLength.QuadPart = 0;
3887 
3888     fcb->atts = 0;
3889 
3890     if (bmn->name[0] == '.')
3891         fcb->atts |= FILE_ATTRIBUTE_HIDDEN;
3892 
3893     if (bmn->type == BTRFS_TYPE_DIRECTORY)
3894         fcb->atts |= FILE_ATTRIBUTE_DIRECTORY;
3895 
3896     fcb->atts_changed = false;
3897 
3898     InterlockedIncrement(&parfcb->refcount);
3899     fcb->subvol = parfcb->subvol;
3900 
3901     SeCaptureSubjectContext(&subjcont);
3902 
3903     Status = SeAssignSecurityEx(parfileref ? parfileref->fcb->sd : NULL, NULL, (void**)&fcb->sd, NULL, fcb->type == BTRFS_TYPE_DIRECTORY,
3904                                 SEF_SACL_AUTO_INHERIT, &subjcont, IoGetFileObjectGenericMapping(), PagedPool);
3905 
3906     if (!NT_SUCCESS(Status)) {
3907         ERR("SeAssignSecurityEx returned %08lx\n", Status);
3908         reap_fcb(fcb);
3909         goto end;
3910     }
3911 
3912     Status = RtlGetOwnerSecurityDescriptor(fcb->sd, &owner, &defaulted);
3913     if (!NT_SUCCESS(Status)) {
3914         WARN("RtlGetOwnerSecurityDescriptor returned %08lx\n", Status);
3915         fcb->sd_dirty = true;
3916     } else {
3917         fcb->inode_item.st_uid = sid_to_uid(owner);
3918         fcb->sd_dirty = fcb->inode_item.st_uid == UID_NOBODY;
3919     }
3920 
3921     find_gid(fcb, parfcb, &subjcont);
3922 
3923     ExAcquireResourceExclusiveLite(&Vcb->fileref_lock, true);
3924     acquire_fcb_lock_exclusive(Vcb);
3925 
3926     if (bmn->inode == 0) {
3927         inode = InterlockedIncrement64(&parfcb->subvol->lastinode);
3928         lastle = parfcb->subvol->fcbs.Blink;
3929     } else {
3930         if (bmn->inode > (uint64_t)parfcb->subvol->lastinode) {
3931             inode = parfcb->subvol->lastinode = bmn->inode;
3932             lastle = parfcb->subvol->fcbs.Blink;
3933         } else {
3934             LIST_ENTRY* le = parfcb->subvol->fcbs.Flink;
3935 
3936             lastle = parfcb->subvol->fcbs.Blink;;
3937             while (le != &parfcb->subvol->fcbs) {
3938                 struct _fcb* fcb2 = CONTAINING_RECORD(le, struct _fcb, list_entry);
3939 
3940                 if (fcb2->inode == bmn->inode && !fcb2->deleted) {
3941                     release_fcb_lock(Vcb);
3942                     ExReleaseResourceLite(&Vcb->fileref_lock);
3943 
3944                     WARN("inode collision\n");
3945                     Status = STATUS_INVALID_PARAMETER;
3946                     goto end;
3947                 } else if (fcb2->inode > bmn->inode) {
3948                     lastle = fcb2->list_entry.Blink;
3949                     break;
3950                 }
3951 
3952                 le = le->Flink;
3953             }
3954 
3955             inode = bmn->inode;
3956         }
3957     }
3958 
3959     fcb->inode = inode;
3960     fcb->type = bmn->type;
3961 
3962     fileref = create_fileref(Vcb);
3963     if (!fileref) {
3964         release_fcb_lock(Vcb);
3965         ExReleaseResourceLite(&Vcb->fileref_lock);
3966 
3967         ERR("out of memory\n");
3968         reap_fcb(fcb);
3969         Status = STATUS_INSUFFICIENT_RESOURCES;
3970         goto end;
3971     }
3972 
3973     fileref->fcb = fcb;
3974 
3975     fcb->created = true;
3976     fileref->created = true;
3977 
3978     fcb->subvol->root_item.ctransid = Vcb->superblock.generation;
3979     fcb->subvol->root_item.ctime = now;
3980 
3981     fileref->parent = parfileref;
3982 
3983     mark_fcb_dirty(fcb);
3984     mark_fileref_dirty(fileref);
3985 
3986     Status = add_dir_child(fileref->parent->fcb, fcb->inode, false, &utf8, &name, fcb->type, &dc);
3987     if (!NT_SUCCESS(Status))
3988         WARN("add_dir_child returned %08lx\n", Status);
3989 
3990     fileref->dc = dc;
3991     dc->fileref = fileref;
3992 
3993     ExAcquireResourceExclusiveLite(&parfileref->fcb->nonpaged->dir_children_lock, true);
3994     InsertTailList(&parfileref->children, &fileref->list_entry);
3995     ExReleaseResourceLite(&parfileref->fcb->nonpaged->dir_children_lock);
3996 
3997     increase_fileref_refcount(parfileref);
3998 
3999     if (fcb->type == BTRFS_TYPE_DIRECTORY) {
4000         fcb->hash_ptrs = ExAllocatePoolWithTag(PagedPool, sizeof(LIST_ENTRY*) * 256, ALLOC_TAG);
4001         if (!fcb->hash_ptrs) {
4002             release_fcb_lock(Vcb);
4003             ExReleaseResourceLite(&Vcb->fileref_lock);
4004 
4005             ERR("out of memory\n");
4006             free_fileref(fileref);
4007             Status = STATUS_INSUFFICIENT_RESOURCES;
4008             goto end;
4009         }
4010 
4011         RtlZeroMemory(fcb->hash_ptrs, sizeof(LIST_ENTRY*) * 256);
4012 
4013         fcb->hash_ptrs_uc = ExAllocatePoolWithTag(PagedPool, sizeof(LIST_ENTRY*) * 256, ALLOC_TAG);
4014         if (!fcb->hash_ptrs_uc) {
4015             release_fcb_lock(Vcb);
4016             ExReleaseResourceLite(&Vcb->fileref_lock);
4017 
4018             ERR("out of memory\n");
4019             free_fileref(fileref);
4020             Status = STATUS_INSUFFICIENT_RESOURCES;
4021             goto end;
4022         }
4023 
4024         RtlZeroMemory(fcb->hash_ptrs_uc, sizeof(LIST_ENTRY*) * 256);
4025     }
4026 
4027     InsertHeadList(lastle, &fcb->list_entry);
4028     InsertTailList(&Vcb->all_fcbs, &fcb->list_entry_all);
4029 
4030     if (bmn->type == BTRFS_TYPE_DIRECTORY)
4031         fileref->fcb->fileref = fileref;
4032 
4033     ExAcquireResourceExclusiveLite(parfcb->Header.Resource, true);
4034     parfcb->inode_item.st_size += utf8.Length * 2;
4035     parfcb->inode_item.transid = Vcb->superblock.generation;
4036     parfcb->inode_item.sequence++;
4037 
4038     if (!parccb->user_set_change_time)
4039         parfcb->inode_item.st_ctime = now;
4040 
4041     if (!parccb->user_set_write_time)
4042         parfcb->inode_item.st_mtime = now;
4043 
4044     parfcb->subvol->fcbs_version++;
4045 
4046     ExReleaseResourceLite(parfcb->Header.Resource);
4047     release_fcb_lock(Vcb);
4048     ExReleaseResourceLite(&Vcb->fileref_lock);
4049 
4050     parfcb->inode_item_changed = true;
4051     mark_fcb_dirty(parfcb);
4052 
4053     send_notification_fileref(fileref, bmn->type == BTRFS_TYPE_DIRECTORY ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_ADDED, NULL);
4054 
4055     if (!parccb->user_set_write_time)
4056         queue_notification_fcb(parfileref, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED, NULL);
4057 
4058     Status = STATUS_SUCCESS;
4059 
4060 end:
4061 
4062     ExFreePool(utf8.Buffer);
4063 
4064     return Status;
4065 }
4066 
4067 static void mark_subvol_dirty(device_extension* Vcb, root* r) {
4068     if (!r->dirty) {
4069         r->dirty = true;
4070 
4071         ExAcquireResourceExclusiveLite(&Vcb->dirty_subvols_lock, true);
4072         InsertTailList(&Vcb->dirty_subvols, &r->list_entry_dirty);
4073         ExReleaseResourceLite(&Vcb->dirty_subvols_lock);
4074     }
4075 
4076     Vcb->need_write = true;
4077 }
4078 
4079 static NTSTATUS recvd_subvol(device_extension* Vcb, PFILE_OBJECT FileObject, void* data, ULONG datalen, KPROCESSOR_MODE processor_mode) {
4080     btrfs_received_subvol* brs = (btrfs_received_subvol*)data;
4081     fcb* fcb;
4082     NTSTATUS Status;
4083     LARGE_INTEGER time;
4084     BTRFS_TIME now;
4085 
4086     TRACE("(%p, %p, %p, %lu)\n", Vcb, FileObject, data, datalen);
4087 
4088     if (!data || datalen < sizeof(btrfs_received_subvol))
4089         return STATUS_INVALID_PARAMETER;
4090 
4091     if (!FileObject || !FileObject->FsContext || FileObject->FsContext == Vcb->volume_fcb)
4092         return STATUS_INVALID_PARAMETER;
4093 
4094     fcb = FileObject->FsContext;
4095 
4096     if (!fcb->subvol)
4097         return STATUS_INVALID_PARAMETER;
4098 
4099     if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE), processor_mode))
4100         return STATUS_PRIVILEGE_NOT_HELD;
4101 
4102     ExAcquireResourceSharedLite(&Vcb->tree_lock, true);
4103 
4104     if (fcb->subvol->root_item.rtransid != 0) {
4105         WARN("subvol already has received information set\n");
4106         Status = STATUS_INVALID_PARAMETER;
4107         goto end;
4108     }
4109 
4110     KeQuerySystemTime(&time);
4111     win_time_to_unix(time, &now);
4112 
4113     RtlCopyMemory(&fcb->subvol->root_item.received_uuid, &brs->uuid, sizeof(BTRFS_UUID));
4114     fcb->subvol->root_item.stransid = brs->generation;
4115     fcb->subvol->root_item.rtransid = Vcb->superblock.generation;
4116     fcb->subvol->root_item.rtime = now;
4117 
4118     fcb->subvol->received = true;
4119     mark_subvol_dirty(Vcb, fcb->subvol);
4120 
4121     Status = STATUS_SUCCESS;
4122 
4123 end:
4124     ExReleaseResourceLite(&Vcb->tree_lock);
4125 
4126     return Status;
4127 }
4128 
4129 static NTSTATUS fsctl_get_xattrs(device_extension* Vcb, PFILE_OBJECT FileObject, void* data, ULONG datalen, KPROCESSOR_MODE processor_mode) {
4130     LIST_ENTRY* le;
4131     btrfs_set_xattr* bsxa;
4132     ULONG reqlen = (ULONG)offsetof(btrfs_set_xattr, data[0]);
4133     fcb* fcb;
4134     ccb* ccb;
4135 
4136     if (!data || datalen < reqlen)
4137         return STATUS_INVALID_PARAMETER;
4138 
4139     if (!FileObject || !FileObject->FsContext || !FileObject->FsContext2 || FileObject->FsContext == Vcb->volume_fcb)
4140         return STATUS_INVALID_PARAMETER;
4141 
4142     fcb = FileObject->FsContext;
4143     ccb = FileObject->FsContext2;
4144 
4145     if (!(ccb->access & (FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES)) && processor_mode == UserMode) {
4146         WARN("insufficient privileges\n");
4147         return STATUS_ACCESS_DENIED;
4148     }
4149 
4150     ExAcquireResourceSharedLite(fcb->Header.Resource, true);
4151 
4152     le = fcb->xattrs.Flink;
4153     while (le != &fcb->xattrs) {
4154         xattr* xa = CONTAINING_RECORD(le, xattr, list_entry);
4155 
4156         if (xa->valuelen > 0)
4157             reqlen += (ULONG)offsetof(btrfs_set_xattr, data[0]) + xa->namelen + xa->valuelen;
4158 
4159         le = le->Flink;
4160     }
4161 
4162     if (datalen < reqlen) {
4163         ExReleaseResourceLite(fcb->Header.Resource);
4164         return STATUS_BUFFER_OVERFLOW;
4165     }
4166 
4167     bsxa = (btrfs_set_xattr*)data;
4168 
4169     if (reqlen > 0) {
4170         le = fcb->xattrs.Flink;
4171         while (le != &fcb->xattrs) {
4172             xattr* xa = CONTAINING_RECORD(le, xattr, list_entry);
4173 
4174             if (xa->valuelen > 0) {
4175                 bsxa->namelen = xa->namelen;
4176                 bsxa->valuelen = xa->valuelen;
4177                 memcpy(bsxa->data, xa->data, xa->namelen + xa->valuelen);
4178 
4179                 bsxa = (btrfs_set_xattr*)&bsxa->data[xa->namelen + xa->valuelen];
4180             }
4181 
4182             le = le->Flink;
4183         }
4184     }
4185 
4186     bsxa->namelen = 0;
4187     bsxa->valuelen = 0;
4188 
4189     ExReleaseResourceLite(fcb->Header.Resource);
4190 
4191     return STATUS_SUCCESS;
4192 }
4193 
4194 static NTSTATUS fsctl_set_xattr(device_extension* Vcb, PFILE_OBJECT FileObject, void* data, ULONG datalen, PIRP Irp) {
4195     NTSTATUS Status;
4196     btrfs_set_xattr* bsxa;
4197     xattr* xa;
4198     fcb* fcb;
4199     ccb* ccb;
4200     LIST_ENTRY* le;
4201 
4202     static const char stream_pref[] = "user.";
4203 
4204     TRACE("(%p, %p, %p, %lu)\n", Vcb, FileObject, data, datalen);
4205 
4206     if (!data || datalen < sizeof(btrfs_set_xattr))
4207         return STATUS_INVALID_PARAMETER;
4208 
4209     bsxa = (btrfs_set_xattr*)data;
4210 
4211     if (datalen < offsetof(btrfs_set_xattr, data[0]) + bsxa->namelen + bsxa->valuelen)
4212         return STATUS_INVALID_PARAMETER;
4213 
4214     if (bsxa->namelen + bsxa->valuelen + sizeof(tree_header) + sizeof(leaf_node) + offsetof(DIR_ITEM, name[0]) > Vcb->superblock.node_size)
4215         return STATUS_INVALID_PARAMETER;
4216 
4217     if (!FileObject || !FileObject->FsContext || !FileObject->FsContext2 || FileObject->FsContext == Vcb->volume_fcb)
4218         return STATUS_INVALID_PARAMETER;
4219 
4220     if (Vcb->readonly)
4221         return STATUS_MEDIA_WRITE_PROTECTED;
4222 
4223     fcb = FileObject->FsContext;
4224     ccb = FileObject->FsContext2;
4225 
4226     if (is_subvol_readonly(fcb->subvol, Irp))
4227         return STATUS_ACCESS_DENIED;
4228 
4229     if (!(ccb->access & FILE_WRITE_ATTRIBUTES) && Irp->RequestorMode == UserMode) {
4230         WARN("insufficient privileges\n");
4231         return STATUS_ACCESS_DENIED;
4232     }
4233 
4234     ExAcquireResourceSharedLite(&Vcb->tree_lock, true);
4235 
4236     ExAcquireResourceExclusiveLite(fcb->Header.Resource, true);
4237 
4238     if (bsxa->namelen == sizeof(EA_NTACL) - 1 && RtlCompareMemory(bsxa->data, EA_NTACL, sizeof(EA_NTACL) - 1) == sizeof(EA_NTACL) - 1) {
4239         if ((!(ccb->access & WRITE_DAC) || !(ccb->access & WRITE_OWNER)) && Irp->RequestorMode == UserMode) {
4240             WARN("insufficient privileges\n");
4241             Status = STATUS_ACCESS_DENIED;
4242             goto end;
4243         }
4244 
4245         if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE), Irp->RequestorMode)) {
4246             Status = STATUS_PRIVILEGE_NOT_HELD;
4247             goto end;
4248         }
4249 
4250         if (fcb->sd)
4251             ExFreePool(fcb->sd);
4252 
4253         if (bsxa->valuelen > 0 && RtlValidRelativeSecurityDescriptor(bsxa->data + bsxa->namelen, bsxa->valuelen, 0)) {
4254             fcb->sd = ExAllocatePoolWithTag(PagedPool, bsxa->valuelen, ALLOC_TAG);
4255             if (!fcb->sd) {
4256                 ERR("out of memory\n");
4257                 Status = STATUS_INSUFFICIENT_RESOURCES;
4258                 goto end;
4259             }
4260 
4261             RtlCopyMemory(fcb->sd, bsxa->data + bsxa->namelen, bsxa->valuelen);
4262         } else if (fcb->sd)
4263             fcb->sd = NULL;
4264 
4265         fcb->sd_dirty = true;
4266 
4267         if (!fcb->sd) {
4268             fcb_get_sd(fcb, ccb->fileref->parent->fcb, false, Irp);
4269             fcb->sd_deleted = true;
4270         }
4271 
4272         mark_fcb_dirty(fcb);
4273 
4274         Status = STATUS_SUCCESS;
4275         goto end;
4276     } else if (bsxa->namelen == sizeof(EA_DOSATTRIB) - 1 && RtlCompareMemory(bsxa->data, EA_DOSATTRIB, sizeof(EA_DOSATTRIB) - 1) == sizeof(EA_DOSATTRIB) - 1) {
4277         ULONG atts;
4278 
4279         if (bsxa->valuelen > 0 && get_file_attributes_from_xattr(bsxa->data + bsxa->namelen, bsxa->valuelen, &atts)) {
4280             fcb->atts = atts;
4281 
4282             if (fcb->type == BTRFS_TYPE_DIRECTORY)
4283                 fcb->atts |= FILE_ATTRIBUTE_DIRECTORY;
4284             else if (fcb->type == BTRFS_TYPE_SYMLINK)
4285                 fcb->atts |= FILE_ATTRIBUTE_REPARSE_POINT;
4286 
4287             if (fcb->inode == SUBVOL_ROOT_INODE) {
4288                 if (fcb->subvol->root_item.flags & BTRFS_SUBVOL_READONLY)
4289                     fcb->atts |= FILE_ATTRIBUTE_READONLY;
4290                 else
4291                     fcb->atts &= ~FILE_ATTRIBUTE_READONLY;
4292             }
4293 
4294             fcb->atts_deleted = false;
4295         } else {
4296             bool hidden = ccb->fileref && ccb->fileref->dc && ccb->fileref->dc->utf8.Buffer && ccb->fileref->dc->utf8.Buffer[0] == '.';
4297 
4298             fcb->atts = get_file_attributes(Vcb, fcb->subvol, fcb->inode, fcb->type, hidden, true, Irp);
4299             fcb->atts_deleted = true;
4300         }
4301 
4302         fcb->atts_changed = true;
4303         mark_fcb_dirty(fcb);
4304 
4305         Status = STATUS_SUCCESS;
4306         goto end;
4307     } else if (bsxa->namelen == sizeof(EA_REPARSE) - 1 && RtlCompareMemory(bsxa->data, EA_REPARSE, sizeof(EA_REPARSE) - 1) == sizeof(EA_REPARSE) - 1) {
4308         if (fcb->reparse_xattr.Buffer) {
4309             ExFreePool(fcb->reparse_xattr.Buffer);
4310             fcb->reparse_xattr.Buffer = NULL;
4311             fcb->reparse_xattr.Length = fcb->reparse_xattr.MaximumLength = 0;
4312         }
4313 
4314         if (bsxa->valuelen > 0) {
4315             fcb->reparse_xattr.Buffer = ExAllocatePoolWithTag(PagedPool, bsxa->valuelen, ALLOC_TAG);
4316             if (!fcb->reparse_xattr.Buffer) {
4317                 ERR("out of memory\n");
4318                 Status = STATUS_INSUFFICIENT_RESOURCES;
4319                 goto end;
4320             }
4321 
4322             RtlCopyMemory(fcb->reparse_xattr.Buffer, bsxa->data + bsxa->namelen, bsxa->valuelen);
4323             fcb->reparse_xattr.Length = fcb->reparse_xattr.MaximumLength = bsxa->valuelen;
4324         }
4325 
4326         fcb->reparse_xattr_changed = true;
4327         mark_fcb_dirty(fcb);
4328 
4329         Status = STATUS_SUCCESS;
4330         goto end;
4331     } else if (bsxa->namelen == sizeof(EA_EA) - 1 && RtlCompareMemory(bsxa->data, EA_EA, sizeof(EA_EA) - 1) == sizeof(EA_EA) - 1) {
4332         if (!(ccb->access & FILE_WRITE_EA) && Irp->RequestorMode == UserMode) {
4333             WARN("insufficient privileges\n");
4334             Status = STATUS_ACCESS_DENIED;
4335             goto end;
4336         }
4337 
4338         if (fcb->ea_xattr.Buffer) {
4339             ExFreePool(fcb->ea_xattr.Buffer);
4340             fcb->ea_xattr.Length = fcb->ea_xattr.MaximumLength = 0;
4341             fcb->ea_xattr.Buffer = NULL;
4342         }
4343 
4344         fcb->ealen = 0;
4345 
4346         if (bsxa->valuelen > 0) {
4347             ULONG offset;
4348 
4349             Status = IoCheckEaBufferValidity((FILE_FULL_EA_INFORMATION*)(bsxa->data + bsxa->namelen), bsxa->valuelen, &offset);
4350 
4351             if (!NT_SUCCESS(Status))
4352                 WARN("IoCheckEaBufferValidity returned %08lx (error at offset %lu)\n", Status, offset);
4353             else {
4354                 FILE_FULL_EA_INFORMATION* eainfo;
4355 
4356                 fcb->ea_xattr.Buffer = ExAllocatePoolWithTag(PagedPool, bsxa->valuelen, ALLOC_TAG);
4357                 if (!fcb->ea_xattr.Buffer) {
4358                     ERR("out of memory\n");
4359                     Status = STATUS_INSUFFICIENT_RESOURCES;
4360                     goto end;
4361                 }
4362 
4363                 RtlCopyMemory(fcb->ea_xattr.Buffer, bsxa->data + bsxa->namelen, bsxa->valuelen);
4364 
4365                 fcb->ea_xattr.Length = fcb->ea_xattr.MaximumLength = bsxa->valuelen;
4366 
4367                 fcb->ealen = 4;
4368 
4369                 // calculate ealen
4370                 eainfo = (FILE_FULL_EA_INFORMATION*)(bsxa->data + bsxa->namelen);
4371                 do {
4372                     fcb->ealen += 5 + eainfo->EaNameLength + eainfo->EaValueLength;
4373 
4374                     if (eainfo->NextEntryOffset == 0)
4375                         break;
4376 
4377                     eainfo = (FILE_FULL_EA_INFORMATION*)(((uint8_t*)eainfo) + eainfo->NextEntryOffset);
4378                 } while (true);
4379             }
4380         }
4381 
4382         fcb->ea_changed = true;
4383         mark_fcb_dirty(fcb);
4384 
4385         Status = STATUS_SUCCESS;
4386         goto end;
4387     } else if (bsxa->namelen == sizeof(EA_CASE_SENSITIVE) - 1 && RtlCompareMemory(bsxa->data, EA_CASE_SENSITIVE, sizeof(EA_CASE_SENSITIVE) - 1) == sizeof(EA_CASE_SENSITIVE) - 1) {
4388         if (bsxa->valuelen > 0 && bsxa->data[bsxa->namelen] == '1') {
4389             fcb->case_sensitive = true;
4390             mark_fcb_dirty(fcb);
4391         }
4392 
4393         Status = STATUS_SUCCESS;
4394         goto end;
4395     } else if (bsxa->namelen == sizeof(EA_PROP_COMPRESSION) - 1 && RtlCompareMemory(bsxa->data, EA_PROP_COMPRESSION, sizeof(EA_PROP_COMPRESSION) - 1) == sizeof(EA_PROP_COMPRESSION) - 1) {
4396         static const char lzo[] = "lzo";
4397         static const char zlib[] = "zlib";
4398         static const char zstd[] = "zstd";
4399 
4400         if (bsxa->valuelen == sizeof(zstd) - 1 && RtlCompareMemory(bsxa->data + bsxa->namelen, zstd, bsxa->valuelen) == bsxa->valuelen)
4401             fcb->prop_compression = PropCompression_ZSTD;
4402         else if (bsxa->valuelen == sizeof(lzo) - 1 && RtlCompareMemory(bsxa->data + bsxa->namelen, lzo, bsxa->valuelen) == bsxa->valuelen)
4403             fcb->prop_compression = PropCompression_LZO;
4404         else if (bsxa->valuelen == sizeof(zlib) - 1 && RtlCompareMemory(bsxa->data + bsxa->namelen, zlib, bsxa->valuelen) == bsxa->valuelen)
4405             fcb->prop_compression = PropCompression_Zlib;
4406         else
4407             fcb->prop_compression = PropCompression_None;
4408 
4409         if (fcb->prop_compression != PropCompression_None) {
4410             fcb->inode_item.flags |= BTRFS_INODE_COMPRESS;
4411             fcb->inode_item_changed = true;
4412         }
4413 
4414         fcb->prop_compression_changed = true;
4415         mark_fcb_dirty(fcb);
4416 
4417         Status = STATUS_SUCCESS;
4418         goto end;
4419     } else if (bsxa->namelen >= (sizeof(stream_pref) - 1) && RtlCompareMemory(bsxa->data, stream_pref, sizeof(stream_pref) - 1) == sizeof(stream_pref) - 1) {
4420         // don't allow xattrs beginning with user., as these appear as streams instead
4421         Status = STATUS_OBJECT_NAME_INVALID;
4422         goto end;
4423     }
4424 
4425     xa = ExAllocatePoolWithTag(PagedPool, offsetof(xattr, data[0]) + bsxa->namelen + bsxa->valuelen, ALLOC_TAG);
4426     if (!xa) {
4427         ERR("out of memory\n");
4428         Status = STATUS_INSUFFICIENT_RESOURCES;
4429         goto end;
4430     }
4431 
4432     le = fcb->xattrs.Flink;
4433     while (le != &fcb->xattrs) {
4434         xattr* xa2 = CONTAINING_RECORD(le, xattr, list_entry);
4435 
4436         if (xa2->namelen == bsxa->namelen && RtlCompareMemory(xa2->data, bsxa->data, xa2->namelen) == xa2->namelen) {
4437             RemoveEntryList(&xa2->list_entry);
4438             ExFreePool(xa2);
4439             break;
4440         }
4441 
4442         le = le->Flink;
4443     }
4444 
4445     xa->namelen = bsxa->namelen;
4446     xa->valuelen = bsxa->valuelen;
4447     xa->dirty = true;
4448     RtlCopyMemory(xa->data, bsxa->data, bsxa->namelen + bsxa->valuelen);
4449 
4450     InsertTailList(&fcb->xattrs, &xa->list_entry);
4451 
4452     fcb->xattrs_changed = true;
4453     mark_fcb_dirty(fcb);
4454 
4455     Status = STATUS_SUCCESS;
4456 
4457 end:
4458     ExReleaseResourceLite(fcb->Header.Resource);
4459 
4460     ExReleaseResourceLite(&Vcb->tree_lock);
4461 
4462     return Status;
4463 }
4464 
4465 static NTSTATUS reserve_subvol(device_extension* Vcb, PFILE_OBJECT FileObject, PIRP Irp) {
4466     fcb* fcb;
4467     ccb* ccb;
4468 
4469     TRACE("(%p, %p)\n", Vcb, FileObject);
4470 
4471     // "Reserving" a readonly subvol allows the calling process to write into it until the handle is closed.
4472 
4473     if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE), Irp->RequestorMode))
4474         return STATUS_PRIVILEGE_NOT_HELD;
4475 
4476     if (!FileObject || !FileObject->FsContext || !FileObject->FsContext2 || FileObject->FsContext == Vcb->volume_fcb)
4477         return STATUS_INVALID_PARAMETER;
4478 
4479     fcb = FileObject->FsContext;
4480     ccb = FileObject->FsContext2;
4481 
4482     if (!(fcb->subvol->root_item.flags & BTRFS_SUBVOL_READONLY))
4483         return STATUS_INVALID_PARAMETER;
4484 
4485     if (fcb->subvol->reserved)
4486         return STATUS_INVALID_PARAMETER;
4487 
4488     fcb->subvol->reserved = PsGetCurrentProcess();
4489     ccb->reserving = true;
4490 
4491     return STATUS_SUCCESS;
4492 }
4493 
4494 static NTSTATUS get_subvol_path(device_extension* Vcb, uint64_t id, WCHAR* out, ULONG outlen, PIRP Irp) {
4495     LIST_ENTRY* le;
4496     root* r = NULL;
4497     NTSTATUS Status;
4498     file_ref* fr;
4499     UNICODE_STRING us;
4500 
4501     le = Vcb->roots.Flink;
4502     while (le != &Vcb->roots) {
4503         root* r2 = CONTAINING_RECORD(le, root, list_entry);
4504 
4505         if (r2->id == id) {
4506             r = r2;
4507             break;
4508         }
4509 
4510         le = le->Flink;
4511     }
4512 
4513     if (!r) {
4514         ERR("couldn't find subvol %I64x\n", id);
4515         return STATUS_INTERNAL_ERROR;
4516     }
4517 
4518     ExAcquireResourceExclusiveLite(&Vcb->fileref_lock, true);
4519 
4520     Status = open_fileref_by_inode(Vcb, r, r->root_item.objid, &fr, Irp);
4521     if (!NT_SUCCESS(Status)) {
4522         ExReleaseResourceLite(&Vcb->fileref_lock);
4523         ERR("open_fileref_by_inode returned %08lx\n", Status);
4524         return Status;
4525     }
4526 
4527     us.Buffer = out;
4528     us.Length = 0;
4529     us.MaximumLength = (USHORT)min(0xffff, outlen) - sizeof(WCHAR);
4530 
4531     Status = fileref_get_filename(fr, &us, NULL, NULL);
4532 
4533     if (NT_SUCCESS(Status) || Status == STATUS_BUFFER_OVERFLOW)
4534         out[us.Length / sizeof(WCHAR)] = 0;
4535     else
4536         ERR("fileref_get_filename returned %08lx\n", Status);
4537 
4538     free_fileref(fr);
4539 
4540     ExReleaseResourceLite(&Vcb->fileref_lock);
4541 
4542     return Status;
4543 }
4544 
4545 static NTSTATUS find_subvol(device_extension* Vcb, void* in, ULONG inlen, void* out, ULONG outlen, PIRP Irp) {
4546     btrfs_find_subvol* bfs;
4547     NTSTATUS Status;
4548     traverse_ptr tp;
4549     KEY searchkey;
4550 
4551     if (!in || inlen < sizeof(btrfs_find_subvol))
4552         return STATUS_INVALID_PARAMETER;
4553 
4554     if (!out || outlen < sizeof(WCHAR))
4555         return STATUS_INVALID_PARAMETER;
4556 
4557     if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE), Irp->RequestorMode))
4558         return STATUS_PRIVILEGE_NOT_HELD;
4559 
4560     bfs = (btrfs_find_subvol*)in;
4561 
4562     ExAcquireResourceSharedLite(&Vcb->tree_lock, true);
4563 
4564     if (!Vcb->uuid_root) {
4565         ERR("couldn't find uuid root\n");
4566         Status = STATUS_NOT_FOUND;
4567         goto end;
4568     }
4569 
4570     RtlCopyMemory(&searchkey.obj_id, &bfs->uuid, sizeof(uint64_t));
4571     searchkey.obj_type = TYPE_SUBVOL_UUID;
4572     RtlCopyMemory(&searchkey.offset, &bfs->uuid.uuid[sizeof(uint64_t)], sizeof(uint64_t));
4573 
4574     Status = find_item(Vcb, Vcb->uuid_root, &tp, &searchkey, false, Irp);
4575 
4576     if (!NT_SUCCESS(Status)) {
4577         ERR("find_item returned %08lx\n", Status);
4578         goto end;
4579     }
4580 
4581     if (!keycmp(searchkey, tp.item->key) && tp.item->size >= sizeof(uint64_t)) {
4582         uint64_t* id = (uint64_t*)tp.item->data;
4583 
4584         if (bfs->ctransid != 0) {
4585             KEY searchkey2;
4586             traverse_ptr tp2;
4587 
4588             searchkey2.obj_id = *id;
4589             searchkey2.obj_type = TYPE_ROOT_ITEM;
4590             searchkey2.offset = 0xffffffffffffffff;
4591 
4592             Status = find_item(Vcb, Vcb->root_root, &tp2, &searchkey2, false, Irp);
4593             if (!NT_SUCCESS(Status)) {
4594                 ERR("find_item returned %08lx\n", Status);
4595                 goto end;
4596             }
4597 
4598             if (tp2.item->key.obj_id == searchkey2.obj_id && tp2.item->key.obj_type == searchkey2.obj_type &&
4599                 tp2.item->size >= offsetof(ROOT_ITEM, otransid)) {
4600                 ROOT_ITEM* ri = (ROOT_ITEM*)tp2.item->data;
4601 
4602                 if (ri->ctransid == bfs->ctransid) {
4603                     TRACE("found subvol %I64x\n", *id);
4604                     Status = get_subvol_path(Vcb, *id, out, outlen, Irp);
4605                     goto end;
4606                 }
4607             }
4608         } else {
4609             TRACE("found subvol %I64x\n", *id);
4610             Status = get_subvol_path(Vcb, *id, out, outlen, Irp);
4611             goto end;
4612         }
4613     }
4614 
4615     searchkey.obj_type = TYPE_SUBVOL_REC_UUID;
4616 
4617     Status = find_item(Vcb, Vcb->uuid_root, &tp, &searchkey, false, Irp);
4618 
4619     if (!NT_SUCCESS(Status)) {
4620         ERR("find_item returned %08lx\n", Status);
4621         goto end;
4622     }
4623 
4624     if (!keycmp(searchkey, tp.item->key) && tp.item->size >= sizeof(uint64_t)) {
4625         uint64_t* ids = (uint64_t*)tp.item->data;
4626         ULONG i;
4627 
4628         for (i = 0; i < tp.item->size / sizeof(uint64_t); i++) {
4629             if (bfs->ctransid != 0) {
4630                 KEY searchkey2;
4631                 traverse_ptr tp2;
4632 
4633                 searchkey2.obj_id = ids[i];
4634                 searchkey2.obj_type = TYPE_ROOT_ITEM;
4635                 searchkey2.offset = 0xffffffffffffffff;
4636 
4637                 Status = find_item(Vcb, Vcb->root_root, &tp2, &searchkey2, false, Irp);
4638                 if (!NT_SUCCESS(Status)) {
4639                     ERR("find_item returned %08lx\n", Status);
4640                     goto end;
4641                 }
4642 
4643                 if (tp2.item->key.obj_id == searchkey2.obj_id && tp2.item->key.obj_type == searchkey2.obj_type &&
4644                     tp2.item->size >= offsetof(ROOT_ITEM, otransid)) {
4645                     ROOT_ITEM* ri = (ROOT_ITEM*)tp2.item->data;
4646 
4647                     if (ri->ctransid == bfs->ctransid) {
4648                         TRACE("found subvol %I64x\n", ids[i]);
4649                         Status = get_subvol_path(Vcb, ids[i], out, outlen, Irp);
4650                         goto end;
4651                     }
4652                 }
4653             } else {
4654                 TRACE("found subvol %I64x\n", ids[i]);
4655                 Status = get_subvol_path(Vcb, ids[i], out, outlen, Irp);
4656                 goto end;
4657             }
4658         }
4659     }
4660 
4661     Status = STATUS_NOT_FOUND;
4662 
4663 end:
4664     ExReleaseResourceLite(&Vcb->tree_lock);
4665 
4666     return Status;
4667 }
4668 
4669 static NTSTATUS resize_device(device_extension* Vcb, void* data, ULONG len, PIRP Irp) {
4670     btrfs_resize* br = (btrfs_resize*)data;
4671     NTSTATUS Status;
4672     LIST_ENTRY* le;
4673     device* dev = NULL;
4674 
4675     TRACE("(%p, %p, %lu)\n", Vcb, data, len);
4676 
4677     if (!data || len < sizeof(btrfs_resize) || (br->size % Vcb->superblock.sector_size) != 0)
4678         return STATUS_INVALID_PARAMETER;
4679 
4680     if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE), Irp->RequestorMode))
4681         return STATUS_PRIVILEGE_NOT_HELD;
4682 
4683     if (Vcb->readonly)
4684         return STATUS_MEDIA_WRITE_PROTECTED;
4685 
4686     ExAcquireResourceExclusiveLite(&Vcb->tree_lock, true);
4687 
4688     le = Vcb->devices.Flink;
4689     while (le != &Vcb->devices) {
4690         device* dev2 = CONTAINING_RECORD(le, device, list_entry);
4691 
4692         if (dev2->devitem.dev_id == br->device) {
4693             dev = dev2;
4694             break;
4695         }
4696 
4697         le = le->Flink;
4698     }
4699 
4700     if (!dev) {
4701         ERR("could not find device %I64x\n", br->device);
4702         Status = STATUS_INVALID_PARAMETER;
4703         goto end;
4704     }
4705 
4706     if (!dev->devobj) {
4707         ERR("trying to resize missing device\n");
4708         Status = STATUS_INVALID_PARAMETER;
4709         goto end;
4710     }
4711 
4712     if (dev->readonly) {
4713         ERR("trying to resize readonly device\n");
4714         Status = STATUS_INVALID_PARAMETER;
4715         goto end;
4716     }
4717 
4718     if (br->size > 0 && dev->devitem.num_bytes == br->size) {
4719         TRACE("size unchanged, returning STATUS_SUCCESS\n");
4720         Status = STATUS_SUCCESS;
4721         goto end;
4722     }
4723 
4724     if (br->size > 0 && dev->devitem.num_bytes > br->size) { // shrink device
4725         bool need_balance = true;
4726         uint64_t old_size, delta;
4727 
4728         le = dev->space.Flink;
4729         while (le != &dev->space) {
4730             space* s = CONTAINING_RECORD(le, space, list_entry);
4731 
4732             if (s->address <= br->size && s->address + s->size >= dev->devitem.num_bytes) {
4733                 need_balance = false;
4734                 break;
4735             }
4736 
4737             le = le->Flink;
4738         }
4739 
4740         delta = dev->devitem.num_bytes - br->size;
4741 
4742         if (need_balance) {
4743             OBJECT_ATTRIBUTES oa;
4744             int i;
4745 
4746             if (Vcb->balance.thread) {
4747                 WARN("balance already running\n");
4748                 Status = STATUS_DEVICE_NOT_READY;
4749                 goto end;
4750             }
4751 
4752             RtlZeroMemory(Vcb->balance.opts, sizeof(btrfs_balance_opts) * 3);
4753 
4754             for (i = 0; i < 3; i++) {
4755                 Vcb->balance.opts[i].flags = BTRFS_BALANCE_OPTS_ENABLED | BTRFS_BALANCE_OPTS_DEVID | BTRFS_BALANCE_OPTS_DRANGE;
4756                 Vcb->balance.opts[i].devid = dev->devitem.dev_id;
4757                 Vcb->balance.opts[i].drange_start = br->size;
4758                 Vcb->balance.opts[i].drange_end = dev->devitem.num_bytes;
4759             }
4760 
4761             Vcb->balance.paused = false;
4762             Vcb->balance.shrinking = true;
4763             Vcb->balance.status = STATUS_SUCCESS;
4764             KeInitializeEvent(&Vcb->balance.event, NotificationEvent, !Vcb->balance.paused);
4765 
4766             space_list_subtract2(&dev->space, NULL, br->size, delta, NULL, NULL);
4767 
4768             InitializeObjectAttributes(&oa, NULL, OBJ_KERNEL_HANDLE, NULL, NULL);
4769 
4770             Status = PsCreateSystemThread(&Vcb->balance.thread, 0, &oa, NULL, NULL, balance_thread, Vcb);
4771             if (!NT_SUCCESS(Status)) {
4772                 ERR("PsCreateSystemThread returned %08lx\n", Status);
4773                 goto end;
4774             }
4775 
4776             Status = STATUS_MORE_PROCESSING_REQUIRED;
4777 
4778             goto end;
4779         }
4780 
4781         old_size = dev->devitem.num_bytes;
4782         dev->devitem.num_bytes = br->size;
4783 
4784         Status = update_dev_item(Vcb, dev, Irp);
4785         if (!NT_SUCCESS(Status)) {
4786             ERR("update_dev_item returned %08lx\n", Status);
4787             dev->devitem.num_bytes = old_size;
4788             goto end;
4789         }
4790 
4791         space_list_subtract2(&dev->space, NULL, br->size, delta, NULL, NULL);
4792 
4793         Vcb->superblock.total_bytes -= delta;
4794     } else { // extend device
4795         GET_LENGTH_INFORMATION gli;
4796         uint64_t old_size, delta;
4797 
4798         Status = dev_ioctl(dev->devobj, IOCTL_DISK_GET_LENGTH_INFO, NULL, 0,
4799                            &gli, sizeof(gli), true, NULL);
4800         if (!NT_SUCCESS(Status)) {
4801             ERR("IOCTL_DISK_GET_LENGTH_INFO returned %08lx\n", Status);
4802             goto end;
4803         }
4804 
4805         if (br->size == 0) {
4806             br->size = gli.Length.QuadPart;
4807 
4808             if (dev->devitem.num_bytes == br->size) {
4809                 TRACE("size unchanged, returning STATUS_SUCCESS\n");
4810                 Status = STATUS_SUCCESS;
4811                 goto end;
4812             }
4813 
4814             if (br->size == 0) {
4815                 ERR("IOCTL_DISK_GET_LENGTH_INFO returned 0 length\n");
4816                 Status = STATUS_INTERNAL_ERROR;
4817                 goto end;
4818             }
4819         } else if ((uint64_t)gli.Length.QuadPart < br->size) {
4820             ERR("device was %I64x bytes, trying to extend to %I64x\n", gli.Length.QuadPart, br->size);
4821             Status = STATUS_INVALID_PARAMETER;
4822             goto end;
4823         }
4824 
4825         delta = br->size - dev->devitem.num_bytes;
4826 
4827         old_size = dev->devitem.num_bytes;
4828         dev->devitem.num_bytes = br->size;
4829 
4830         Status = update_dev_item(Vcb, dev, Irp);
4831         if (!NT_SUCCESS(Status)) {
4832             ERR("update_dev_item returned %08lx\n", Status);
4833             dev->devitem.num_bytes = old_size;
4834             goto end;
4835         }
4836 
4837         space_list_add2(&dev->space, NULL, dev->devitem.num_bytes, delta, NULL, NULL);
4838 
4839         Vcb->superblock.total_bytes += delta;
4840     }
4841 
4842     Status = STATUS_SUCCESS;
4843     Vcb->need_write = true;
4844 
4845 end:
4846     ExReleaseResourceLite(&Vcb->tree_lock);
4847 
4848     if (NT_SUCCESS(Status))
4849         FsRtlNotifyVolumeEvent(Vcb->root_file, FSRTL_VOLUME_CHANGE_SIZE);
4850 
4851     return Status;
4852 }
4853 
4854 static NTSTATUS fsctl_oplock(device_extension* Vcb, PIRP* Pirp) {
4855     NTSTATUS Status;
4856     PIRP Irp = *Pirp;
4857     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
4858     uint32_t fsctl = IrpSp->Parameters.FileSystemControl.FsControlCode;
4859     PFILE_OBJECT FileObject = IrpSp->FileObject;
4860     fcb* fcb = FileObject ? FileObject->FsContext : NULL;
4861     ccb* ccb = FileObject ? FileObject->FsContext2 : NULL;
4862     file_ref* fileref = ccb ? ccb->fileref : NULL;
4863 #if (NTDDI_VERSION >= NTDDI_WIN7)
4864     PREQUEST_OPLOCK_INPUT_BUFFER buf = NULL;
4865     bool oplock_request = false, oplock_ack = false;
4866 #else
4867     bool oplock_request = false;
4868 #endif
4869     ULONG oplock_count = 0;
4870 #ifdef __REACTOS__
4871     bool shared_request;
4872 #endif
4873 
4874     if (!fcb) {
4875         ERR("fcb was NULL\n");
4876         return STATUS_INVALID_PARAMETER;
4877     }
4878 
4879     if (!fileref) {
4880         ERR("fileref was NULL\n");
4881         return STATUS_INVALID_PARAMETER;
4882     }
4883 
4884     if (fcb->type != BTRFS_TYPE_FILE && fcb->type != BTRFS_TYPE_DIRECTORY)
4885         return STATUS_INVALID_PARAMETER;
4886 
4887 #if (NTDDI_VERSION >= NTDDI_WIN7)
4888     if (fsctl == FSCTL_REQUEST_OPLOCK) {
4889         if (IrpSp->Parameters.FileSystemControl.InputBufferLength < sizeof(REQUEST_OPLOCK_INPUT_BUFFER))
4890             return STATUS_BUFFER_TOO_SMALL;
4891 
4892         if (IrpSp->Parameters.FileSystemControl.OutputBufferLength < sizeof(REQUEST_OPLOCK_OUTPUT_BUFFER))
4893             return STATUS_BUFFER_TOO_SMALL;
4894 
4895         buf = Irp->AssociatedIrp.SystemBuffer;
4896 
4897         // flags are mutually exclusive
4898         if (buf->Flags & REQUEST_OPLOCK_INPUT_FLAG_REQUEST && buf->Flags & REQUEST_OPLOCK_INPUT_FLAG_ACK)
4899             return STATUS_INVALID_PARAMETER;
4900 
4901         oplock_request = buf->Flags & REQUEST_OPLOCK_INPUT_FLAG_REQUEST;
4902         oplock_ack = buf->Flags & REQUEST_OPLOCK_INPUT_FLAG_ACK;
4903 
4904         if (!oplock_request && !oplock_ack)
4905             return STATUS_INVALID_PARAMETER;
4906     }
4907 #endif
4908 
4909 #if (NTDDI_VERSION >= NTDDI_WIN7)
4910     bool shared_request = (fsctl == FSCTL_REQUEST_OPLOCK_LEVEL_2) || (fsctl == FSCTL_REQUEST_OPLOCK && !(buf->RequestedOplockLevel & OPLOCK_LEVEL_CACHE_WRITE));
4911 #else
4912     shared_request = (fsctl == FSCTL_REQUEST_OPLOCK_LEVEL_2);
4913 #endif
4914 
4915 #if (NTDDI_VERSION >= NTDDI_WIN7)
4916     if (fcb->type == BTRFS_TYPE_DIRECTORY && (fsctl != FSCTL_REQUEST_OPLOCK || !shared_request)) {
4917 #else
4918     if (fcb->type == BTRFS_TYPE_DIRECTORY && !shared_request) {
4919 #endif
4920         WARN("oplock requests on directories can only be for read or read-handle oplocks\n");
4921         return STATUS_INVALID_PARAMETER;
4922     }
4923 
4924     ExAcquireResourceSharedLite(&Vcb->tree_lock, true);
4925 
4926     if (fsctl == FSCTL_REQUEST_OPLOCK_LEVEL_1 || fsctl == FSCTL_REQUEST_BATCH_OPLOCK || fsctl == FSCTL_REQUEST_FILTER_OPLOCK ||
4927         fsctl == FSCTL_REQUEST_OPLOCK_LEVEL_2 || oplock_request) {
4928         ExAcquireResourceExclusiveLite(fcb->Header.Resource, true);
4929 
4930         if (shared_request) {
4931             if (fcb->type == BTRFS_TYPE_FILE) {
4932                 if (fFsRtlCheckLockForOplockRequest)
4933                     oplock_count = !fFsRtlCheckLockForOplockRequest(&fcb->lock, &fcb->Header.AllocationSize);
4934                 else if (fFsRtlAreThereCurrentOrInProgressFileLocks)
4935                     oplock_count = fFsRtlAreThereCurrentOrInProgressFileLocks(&fcb->lock);
4936                 else
4937                     oplock_count = FsRtlAreThereCurrentFileLocks(&fcb->lock);
4938             }
4939         } else
4940             oplock_count = fileref->open_count;
4941     } else
4942         ExAcquireResourceSharedLite(fcb->Header.Resource, true);
4943 
4944 #if (NTDDI_VERSION >= NTDDI_WIN7)
4945     if ((fsctl == FSCTL_REQUEST_FILTER_OPLOCK || fsctl == FSCTL_REQUEST_BATCH_OPLOCK ||
4946         (fsctl == FSCTL_REQUEST_OPLOCK && buf->RequestedOplockLevel & OPLOCK_LEVEL_CACHE_HANDLE)) &&
4947 #else
4948     if ((fsctl == FSCTL_REQUEST_FILTER_OPLOCK || fsctl == FSCTL_REQUEST_BATCH_OPLOCK) &&
4949 #endif
4950         fileref->delete_on_close) {
4951         ExReleaseResourceLite(fcb->Header.Resource);
4952         ExReleaseResourceLite(&Vcb->tree_lock);
4953         return STATUS_DELETE_PENDING;
4954     }
4955 
4956     Status = FsRtlOplockFsctrl(fcb_oplock(fcb), Irp, oplock_count);
4957 
4958     *Pirp = NULL;
4959 
4960     fcb->Header.IsFastIoPossible = fast_io_possible(fcb);
4961 
4962     ExReleaseResourceLite(fcb->Header.Resource);
4963     ExReleaseResourceLite(&Vcb->tree_lock);
4964 
4965     return Status;
4966 }
4967 
4968 NTSTATUS fsctl_request(PDEVICE_OBJECT DeviceObject, PIRP* Pirp, uint32_t type) {
4969     PIRP Irp = *Pirp;
4970     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
4971     NTSTATUS Status;
4972 
4973     if (IrpSp->FileObject && IrpSp->FileObject->FsContext) {
4974         device_extension* Vcb = DeviceObject->DeviceExtension;
4975 
4976         if (Vcb->type == VCB_TYPE_FS)
4977             FsRtlCheckOplock(fcb_oplock(IrpSp->FileObject->FsContext), Irp, NULL, NULL, NULL);
4978     }
4979 
4980     switch (type) {
4981         case FSCTL_REQUEST_OPLOCK_LEVEL_1:
4982         case FSCTL_REQUEST_OPLOCK_LEVEL_2:
4983         case FSCTL_REQUEST_BATCH_OPLOCK:
4984         case FSCTL_OPLOCK_BREAK_ACKNOWLEDGE:
4985         case FSCTL_OPBATCH_ACK_CLOSE_PENDING:
4986         case FSCTL_OPLOCK_BREAK_NOTIFY:
4987         case FSCTL_OPLOCK_BREAK_ACK_NO_2:
4988         case FSCTL_REQUEST_FILTER_OPLOCK:
4989 #if (NTDDI_VERSION >= NTDDI_WIN7)
4990         case FSCTL_REQUEST_OPLOCK:
4991 #endif
4992             Status = fsctl_oplock(DeviceObject->DeviceExtension, Pirp);
4993             break;
4994 
4995         case FSCTL_LOCK_VOLUME:
4996             Status = lock_volume(DeviceObject->DeviceExtension, Irp);
4997             break;
4998 
4999         case FSCTL_UNLOCK_VOLUME:
5000             Status = unlock_volume(DeviceObject->DeviceExtension, Irp);
5001             break;
5002 
5003         case FSCTL_DISMOUNT_VOLUME:
5004             Status = dismount_volume(DeviceObject->DeviceExtension, false, Irp);
5005             break;
5006 
5007         case FSCTL_IS_VOLUME_MOUNTED:
5008             Status = is_volume_mounted(DeviceObject->DeviceExtension, Irp);
5009             break;
5010 
5011         case FSCTL_IS_PATHNAME_VALID:
5012             WARN("STUB: FSCTL_IS_PATHNAME_VALID\n");
5013             Status = STATUS_INVALID_DEVICE_REQUEST;
5014             break;
5015 
5016         case FSCTL_MARK_VOLUME_DIRTY:
5017             WARN("STUB: FSCTL_MARK_VOLUME_DIRTY\n");
5018             Status = STATUS_INVALID_DEVICE_REQUEST;
5019             break;
5020 
5021         case FSCTL_QUERY_RETRIEVAL_POINTERS:
5022             WARN("STUB: FSCTL_QUERY_RETRIEVAL_POINTERS\n");
5023             Status = STATUS_INVALID_DEVICE_REQUEST;
5024             break;
5025 
5026         case FSCTL_GET_COMPRESSION:
5027             Status = get_compression(Irp);
5028             break;
5029 
5030         case FSCTL_SET_COMPRESSION:
5031             Status = set_compression(Irp);
5032             break;
5033 
5034         case FSCTL_SET_BOOTLOADER_ACCESSED:
5035             WARN("STUB: FSCTL_SET_BOOTLOADER_ACCESSED\n");
5036             Status = STATUS_INVALID_DEVICE_REQUEST;
5037             break;
5038 
5039         case FSCTL_INVALIDATE_VOLUMES:
5040             Status = invalidate_volumes(Irp);
5041             break;
5042 
5043         case FSCTL_QUERY_FAT_BPB:
5044             WARN("STUB: FSCTL_QUERY_FAT_BPB\n");
5045             Status = STATUS_INVALID_DEVICE_REQUEST;
5046             break;
5047 
5048         case FSCTL_FILESYSTEM_GET_STATISTICS:
5049             Status = fs_get_statistics(Irp->AssociatedIrp.SystemBuffer, IrpSp->Parameters.FileSystemControl.OutputBufferLength, &Irp->IoStatus.Information);
5050             break;
5051 
5052         case FSCTL_GET_NTFS_VOLUME_DATA:
5053             WARN("STUB: FSCTL_GET_NTFS_VOLUME_DATA\n");
5054             Status = STATUS_INVALID_DEVICE_REQUEST;
5055             break;
5056 
5057         case FSCTL_GET_NTFS_FILE_RECORD:
5058             WARN("STUB: FSCTL_GET_NTFS_FILE_RECORD\n");
5059             Status = STATUS_INVALID_DEVICE_REQUEST;
5060             break;
5061 
5062         case FSCTL_GET_VOLUME_BITMAP:
5063             WARN("STUB: FSCTL_GET_VOLUME_BITMAP\n");
5064             Status = STATUS_INVALID_DEVICE_REQUEST;
5065             break;
5066 
5067         case FSCTL_GET_RETRIEVAL_POINTERS:
5068             WARN("STUB: FSCTL_GET_RETRIEVAL_POINTERS\n");
5069             Status = STATUS_INVALID_DEVICE_REQUEST;
5070             break;
5071 
5072         case FSCTL_MOVE_FILE:
5073             WARN("STUB: FSCTL_MOVE_FILE\n");
5074             Status = STATUS_INVALID_DEVICE_REQUEST;
5075             break;
5076 
5077         case FSCTL_IS_VOLUME_DIRTY:
5078             Status = is_volume_dirty(DeviceObject->DeviceExtension, Irp);
5079             break;
5080 
5081         case FSCTL_ALLOW_EXTENDED_DASD_IO:
5082             Status = allow_extended_dasd_io(DeviceObject->DeviceExtension, IrpSp->FileObject);
5083             break;
5084 
5085         case FSCTL_FIND_FILES_BY_SID:
5086             WARN("STUB: FSCTL_FIND_FILES_BY_SID\n");
5087             Status = STATUS_INVALID_DEVICE_REQUEST;
5088             break;
5089 
5090         case FSCTL_SET_OBJECT_ID:
5091             WARN("STUB: FSCTL_SET_OBJECT_ID\n");
5092             Status = STATUS_INVALID_DEVICE_REQUEST;
5093             break;
5094 
5095         case FSCTL_GET_OBJECT_ID:
5096             Status = get_object_id(DeviceObject->DeviceExtension, IrpSp->FileObject, Irp->UserBuffer,
5097                                    IrpSp->Parameters.FileSystemControl.OutputBufferLength, &Irp->IoStatus.Information);
5098             break;
5099 
5100         case FSCTL_DELETE_OBJECT_ID:
5101             WARN("STUB: FSCTL_DELETE_OBJECT_ID\n");
5102             Status = STATUS_INVALID_DEVICE_REQUEST;
5103             break;
5104 
5105         case FSCTL_SET_REPARSE_POINT:
5106             Status = set_reparse_point(DeviceObject, Irp);
5107             break;
5108 
5109         case FSCTL_GET_REPARSE_POINT:
5110             Status = get_reparse_point(DeviceObject, IrpSp->FileObject, Irp->AssociatedIrp.SystemBuffer,
5111                                        IrpSp->Parameters.FileSystemControl.OutputBufferLength, &Irp->IoStatus.Information);
5112             break;
5113 
5114         case FSCTL_DELETE_REPARSE_POINT:
5115             Status = delete_reparse_point(DeviceObject, Irp);
5116             break;
5117 
5118         case FSCTL_ENUM_USN_DATA:
5119             WARN("STUB: FSCTL_ENUM_USN_DATA\n");
5120             Status = STATUS_INVALID_DEVICE_REQUEST;
5121             break;
5122 
5123         case FSCTL_SECURITY_ID_CHECK:
5124             WARN("STUB: FSCTL_SECURITY_ID_CHECK\n");
5125             Status = STATUS_INVALID_DEVICE_REQUEST;
5126             break;
5127 
5128         case FSCTL_READ_USN_JOURNAL:
5129             WARN("STUB: FSCTL_READ_USN_JOURNAL\n");
5130             Status = STATUS_INVALID_DEVICE_REQUEST;
5131             break;
5132 
5133         case FSCTL_SET_OBJECT_ID_EXTENDED:
5134             WARN("STUB: FSCTL_SET_OBJECT_ID_EXTENDED\n");
5135             Status = STATUS_INVALID_DEVICE_REQUEST;
5136             break;
5137 
5138         case FSCTL_CREATE_OR_GET_OBJECT_ID:
5139             Status = get_object_id(DeviceObject->DeviceExtension, IrpSp->FileObject, Irp->UserBuffer,
5140                                    IrpSp->Parameters.FileSystemControl.OutputBufferLength, &Irp->IoStatus.Information);
5141             break;
5142 
5143         case FSCTL_SET_SPARSE:
5144             Status = set_sparse(DeviceObject->DeviceExtension, IrpSp->FileObject, Irp->AssociatedIrp.SystemBuffer,
5145                                 IrpSp->Parameters.FileSystemControl.InputBufferLength, Irp);
5146             break;
5147 
5148         case FSCTL_SET_ZERO_DATA:
5149             Status = set_zero_data(DeviceObject->DeviceExtension, IrpSp->FileObject, Irp->AssociatedIrp.SystemBuffer,
5150                                    IrpSp->Parameters.FileSystemControl.InputBufferLength, Irp);
5151             break;
5152 
5153         case FSCTL_QUERY_ALLOCATED_RANGES:
5154             Status = query_ranges(IrpSp->FileObject, IrpSp->Parameters.FileSystemControl.Type3InputBuffer,
5155                                   IrpSp->Parameters.FileSystemControl.InputBufferLength, Irp->UserBuffer,
5156                                   IrpSp->Parameters.FileSystemControl.OutputBufferLength, &Irp->IoStatus.Information);
5157             break;
5158 
5159         case FSCTL_ENABLE_UPGRADE:
5160             WARN("STUB: FSCTL_ENABLE_UPGRADE\n");
5161             Status = STATUS_INVALID_DEVICE_REQUEST;
5162             break;
5163 
5164         case FSCTL_SET_ENCRYPTION:
5165             WARN("STUB: FSCTL_SET_ENCRYPTION\n");
5166             Status = STATUS_INVALID_DEVICE_REQUEST;
5167             break;
5168 
5169         case FSCTL_ENCRYPTION_FSCTL_IO:
5170             WARN("STUB: FSCTL_ENCRYPTION_FSCTL_IO\n");
5171             Status = STATUS_INVALID_DEVICE_REQUEST;
5172             break;
5173 
5174         case FSCTL_WRITE_RAW_ENCRYPTED:
5175             WARN("STUB: FSCTL_WRITE_RAW_ENCRYPTED\n");
5176             Status = STATUS_INVALID_DEVICE_REQUEST;
5177             break;
5178 
5179         case FSCTL_READ_RAW_ENCRYPTED:
5180             WARN("STUB: FSCTL_READ_RAW_ENCRYPTED\n");
5181             Status = STATUS_INVALID_DEVICE_REQUEST;
5182             break;
5183 
5184         case FSCTL_CREATE_USN_JOURNAL:
5185             WARN("STUB: FSCTL_CREATE_USN_JOURNAL\n");
5186             Status = STATUS_INVALID_DEVICE_REQUEST;
5187             break;
5188 
5189         case FSCTL_READ_FILE_USN_DATA:
5190             WARN("STUB: FSCTL_READ_FILE_USN_DATA\n");
5191             Status = STATUS_INVALID_DEVICE_REQUEST;
5192             break;
5193 
5194         case FSCTL_WRITE_USN_CLOSE_RECORD:
5195             WARN("STUB: FSCTL_WRITE_USN_CLOSE_RECORD\n");
5196             Status = STATUS_INVALID_DEVICE_REQUEST;
5197             break;
5198 
5199         case FSCTL_EXTEND_VOLUME:
5200             WARN("STUB: FSCTL_EXTEND_VOLUME\n");
5201             Status = STATUS_INVALID_DEVICE_REQUEST;
5202             break;
5203 
5204         case FSCTL_QUERY_USN_JOURNAL:
5205             WARN("STUB: FSCTL_QUERY_USN_JOURNAL\n");
5206             Status = STATUS_INVALID_DEVICE_REQUEST;
5207             break;
5208 
5209         case FSCTL_DELETE_USN_JOURNAL:
5210             WARN("STUB: FSCTL_DELETE_USN_JOURNAL\n");
5211             Status = STATUS_INVALID_DEVICE_REQUEST;
5212             break;
5213 
5214         case FSCTL_MARK_HANDLE:
5215             WARN("STUB: FSCTL_MARK_HANDLE\n");
5216             Status = STATUS_INVALID_DEVICE_REQUEST;
5217             break;
5218 
5219         case FSCTL_SIS_COPYFILE:
5220             WARN("STUB: FSCTL_SIS_COPYFILE\n");
5221             Status = STATUS_INVALID_DEVICE_REQUEST;
5222             break;
5223 
5224         case FSCTL_SIS_LINK_FILES:
5225             WARN("STUB: FSCTL_SIS_LINK_FILES\n");
5226             Status = STATUS_INVALID_DEVICE_REQUEST;
5227             break;
5228 
5229         case FSCTL_RECALL_FILE:
5230             WARN("STUB: FSCTL_RECALL_FILE\n");
5231             Status = STATUS_INVALID_DEVICE_REQUEST;
5232             break;
5233 
5234         case FSCTL_READ_FROM_PLEX:
5235             WARN("STUB: FSCTL_READ_FROM_PLEX\n");
5236             Status = STATUS_INVALID_DEVICE_REQUEST;
5237             break;
5238 
5239         case FSCTL_FILE_PREFETCH:
5240             WARN("STUB: FSCTL_FILE_PREFETCH\n");
5241             Status = STATUS_INVALID_DEVICE_REQUEST;
5242             break;
5243 
5244 #if _WIN32_WINNT >= 0x0600
5245         case FSCTL_MAKE_MEDIA_COMPATIBLE:
5246             WARN("STUB: FSCTL_MAKE_MEDIA_COMPATIBLE\n");
5247             Status = STATUS_INVALID_DEVICE_REQUEST;
5248             break;
5249 
5250         case FSCTL_SET_DEFECT_MANAGEMENT:
5251             WARN("STUB: FSCTL_SET_DEFECT_MANAGEMENT\n");
5252             Status = STATUS_INVALID_DEVICE_REQUEST;
5253             break;
5254 
5255         case FSCTL_QUERY_SPARING_INFO:
5256             WARN("STUB: FSCTL_QUERY_SPARING_INFO\n");
5257             Status = STATUS_INVALID_DEVICE_REQUEST;
5258             break;
5259 
5260         case FSCTL_QUERY_ON_DISK_VOLUME_INFO:
5261             WARN("STUB: FSCTL_QUERY_ON_DISK_VOLUME_INFO\n");
5262             Status = STATUS_INVALID_DEVICE_REQUEST;
5263             break;
5264 
5265         case FSCTL_SET_VOLUME_COMPRESSION_STATE:
5266             WARN("STUB: FSCTL_SET_VOLUME_COMPRESSION_STATE\n");
5267             Status = STATUS_INVALID_DEVICE_REQUEST;
5268             break;
5269 
5270         case FSCTL_TXFS_MODIFY_RM:
5271             WARN("STUB: FSCTL_TXFS_MODIFY_RM\n");
5272             Status = STATUS_INVALID_DEVICE_REQUEST;
5273             break;
5274 
5275         case FSCTL_TXFS_QUERY_RM_INFORMATION:
5276             WARN("STUB: FSCTL_TXFS_QUERY_RM_INFORMATION\n");
5277             Status = STATUS_INVALID_DEVICE_REQUEST;
5278             break;
5279 
5280         case FSCTL_TXFS_ROLLFORWARD_REDO:
5281             WARN("STUB: FSCTL_TXFS_ROLLFORWARD_REDO\n");
5282             Status = STATUS_INVALID_DEVICE_REQUEST;
5283             break;
5284 
5285         case FSCTL_TXFS_ROLLFORWARD_UNDO:
5286             WARN("STUB: FSCTL_TXFS_ROLLFORWARD_UNDO\n");
5287             Status = STATUS_INVALID_DEVICE_REQUEST;
5288             break;
5289 
5290         case FSCTL_TXFS_START_RM:
5291             WARN("STUB: FSCTL_TXFS_START_RM\n");
5292             Status = STATUS_INVALID_DEVICE_REQUEST;
5293             break;
5294 
5295         case FSCTL_TXFS_SHUTDOWN_RM:
5296             WARN("STUB: FSCTL_TXFS_SHUTDOWN_RM\n");
5297             Status = STATUS_INVALID_DEVICE_REQUEST;
5298             break;
5299 
5300         case FSCTL_TXFS_READ_BACKUP_INFORMATION:
5301             WARN("STUB: FSCTL_TXFS_READ_BACKUP_INFORMATION\n");
5302             Status = STATUS_INVALID_DEVICE_REQUEST;
5303             break;
5304 
5305         case FSCTL_TXFS_WRITE_BACKUP_INFORMATION:
5306             WARN("STUB: FSCTL_TXFS_WRITE_BACKUP_INFORMATION\n");
5307             Status = STATUS_INVALID_DEVICE_REQUEST;
5308             break;
5309 
5310         case FSCTL_TXFS_CREATE_SECONDARY_RM:
5311             WARN("STUB: FSCTL_TXFS_CREATE_SECONDARY_RM\n");
5312             Status = STATUS_INVALID_DEVICE_REQUEST;
5313             break;
5314 
5315         case FSCTL_TXFS_GET_METADATA_INFO:
5316             WARN("STUB: FSCTL_TXFS_GET_METADATA_INFO\n");
5317             Status = STATUS_INVALID_DEVICE_REQUEST;
5318             break;
5319 
5320         case FSCTL_TXFS_GET_TRANSACTED_VERSION:
5321             WARN("STUB: FSCTL_TXFS_GET_TRANSACTED_VERSION\n");
5322             Status = STATUS_INVALID_DEVICE_REQUEST;
5323             break;
5324 
5325         case FSCTL_TXFS_SAVEPOINT_INFORMATION:
5326             WARN("STUB: FSCTL_TXFS_SAVEPOINT_INFORMATION\n");
5327             Status = STATUS_INVALID_DEVICE_REQUEST;
5328             break;
5329 
5330         case FSCTL_TXFS_CREATE_MINIVERSION:
5331             WARN("STUB: FSCTL_TXFS_CREATE_MINIVERSION\n");
5332             Status = STATUS_INVALID_DEVICE_REQUEST;
5333             break;
5334 
5335         case FSCTL_TXFS_TRANSACTION_ACTIVE:
5336             WARN("STUB: FSCTL_TXFS_TRANSACTION_ACTIVE\n");
5337             Status = STATUS_INVALID_DEVICE_REQUEST;
5338             break;
5339 
5340         case FSCTL_SET_ZERO_ON_DEALLOCATION:
5341             WARN("STUB: FSCTL_SET_ZERO_ON_DEALLOCATION\n");
5342             Status = STATUS_INVALID_DEVICE_REQUEST;
5343             break;
5344 
5345         case FSCTL_SET_REPAIR:
5346             WARN("STUB: FSCTL_SET_REPAIR\n");
5347             Status = STATUS_INVALID_DEVICE_REQUEST;
5348             break;
5349 
5350         case FSCTL_GET_REPAIR:
5351             WARN("STUB: FSCTL_GET_REPAIR\n");
5352             Status = STATUS_INVALID_DEVICE_REQUEST;
5353             break;
5354 
5355         case FSCTL_WAIT_FOR_REPAIR:
5356             WARN("STUB: FSCTL_WAIT_FOR_REPAIR\n");
5357             Status = STATUS_INVALID_DEVICE_REQUEST;
5358             break;
5359 
5360         case FSCTL_INITIATE_REPAIR:
5361             WARN("STUB: FSCTL_INITIATE_REPAIR\n");
5362             Status = STATUS_INVALID_DEVICE_REQUEST;
5363             break;
5364 
5365         case FSCTL_CSC_INTERNAL:
5366             WARN("STUB: FSCTL_CSC_INTERNAL\n");
5367             Status = STATUS_INVALID_DEVICE_REQUEST;
5368             break;
5369 
5370         case FSCTL_SHRINK_VOLUME:
5371             WARN("STUB: FSCTL_SHRINK_VOLUME\n");
5372             Status = STATUS_INVALID_DEVICE_REQUEST;
5373             break;
5374 
5375         case FSCTL_SET_SHORT_NAME_BEHAVIOR:
5376             WARN("STUB: FSCTL_SET_SHORT_NAME_BEHAVIOR\n");
5377             Status = STATUS_INVALID_DEVICE_REQUEST;
5378             break;
5379 
5380         case FSCTL_DFSR_SET_GHOST_HANDLE_STATE:
5381             WARN("STUB: FSCTL_DFSR_SET_GHOST_HANDLE_STATE\n");
5382             Status = STATUS_INVALID_DEVICE_REQUEST;
5383             break;
5384 
5385         case FSCTL_TXFS_LIST_TRANSACTION_LOCKED_FILES:
5386             WARN("STUB: FSCTL_TXFS_LIST_TRANSACTION_LOCKED_FILES\n");
5387             Status = STATUS_INVALID_DEVICE_REQUEST;
5388             break;
5389 
5390         case FSCTL_TXFS_LIST_TRANSACTIONS:
5391             WARN("STUB: FSCTL_TXFS_LIST_TRANSACTIONS\n");
5392             Status = STATUS_INVALID_DEVICE_REQUEST;
5393             break;
5394 
5395         case FSCTL_QUERY_PAGEFILE_ENCRYPTION:
5396             WARN("STUB: FSCTL_QUERY_PAGEFILE_ENCRYPTION\n");
5397             Status = STATUS_INVALID_DEVICE_REQUEST;
5398             break;
5399 
5400         case FSCTL_RESET_VOLUME_ALLOCATION_HINTS:
5401             WARN("STUB: FSCTL_RESET_VOLUME_ALLOCATION_HINTS\n");
5402             Status = STATUS_INVALID_DEVICE_REQUEST;
5403             break;
5404 
5405         case FSCTL_TXFS_READ_BACKUP_INFORMATION2:
5406             WARN("STUB: FSCTL_TXFS_READ_BACKUP_INFORMATION2\n");
5407             Status = STATUS_INVALID_DEVICE_REQUEST;
5408             break;
5409 
5410         case FSCTL_CSV_CONTROL:
5411             WARN("STUB: FSCTL_CSV_CONTROL\n");
5412             Status = STATUS_INVALID_DEVICE_REQUEST;
5413             break;
5414 #endif
5415         // TRACE rather than WARN because Windows 10 spams this undocumented fsctl
5416         case FSCTL_QUERY_VOLUME_CONTAINER_STATE:
5417             TRACE("STUB: FSCTL_QUERY_VOLUME_CONTAINER_STATE\n");
5418             Status = STATUS_INVALID_DEVICE_REQUEST;
5419             break;
5420 
5421         case FSCTL_GET_INTEGRITY_INFORMATION:
5422             Status = get_integrity_information(DeviceObject->DeviceExtension, IrpSp->FileObject, map_user_buffer(Irp, NormalPagePriority),
5423                                                IrpSp->Parameters.FileSystemControl.OutputBufferLength);
5424             break;
5425 
5426         case FSCTL_SET_INTEGRITY_INFORMATION:
5427             Status = set_integrity_information(IrpSp->FileObject, Irp->AssociatedIrp.SystemBuffer, IrpSp->Parameters.FileSystemControl.InputBufferLength);
5428             break;
5429 
5430         case FSCTL_DUPLICATE_EXTENTS_TO_FILE:
5431             Status = duplicate_extents(DeviceObject->DeviceExtension, IrpSp->FileObject, Irp->AssociatedIrp.SystemBuffer,
5432                                        IrpSp->Parameters.FileSystemControl.InputBufferLength, Irp);
5433             break;
5434 
5435         case FSCTL_BTRFS_GET_FILE_IDS:
5436             Status = get_file_ids(IrpSp->FileObject, map_user_buffer(Irp, NormalPagePriority), IrpSp->Parameters.FileSystemControl.OutputBufferLength);
5437             break;
5438 
5439         case FSCTL_BTRFS_CREATE_SUBVOL:
5440             Status = create_subvol(DeviceObject->DeviceExtension, IrpSp->FileObject, Irp->AssociatedIrp.SystemBuffer, IrpSp->Parameters.FileSystemControl.InputBufferLength, Irp);
5441             break;
5442 
5443         case FSCTL_BTRFS_CREATE_SNAPSHOT:
5444             Status = create_snapshot(DeviceObject->DeviceExtension, IrpSp->FileObject, Irp->AssociatedIrp.SystemBuffer, IrpSp->Parameters.FileSystemControl.InputBufferLength, Irp);
5445             break;
5446 
5447         case FSCTL_BTRFS_GET_INODE_INFO:
5448             Status = get_inode_info(IrpSp->FileObject, map_user_buffer(Irp, NormalPagePriority), IrpSp->Parameters.FileSystemControl.OutputBufferLength);
5449             break;
5450 
5451         case FSCTL_BTRFS_SET_INODE_INFO:
5452             Status = set_inode_info(IrpSp->FileObject, Irp->AssociatedIrp.SystemBuffer, IrpSp->Parameters.FileSystemControl.InputBufferLength, Irp);
5453             break;
5454 
5455         case FSCTL_BTRFS_GET_DEVICES:
5456             Status = get_devices(DeviceObject->DeviceExtension, map_user_buffer(Irp, NormalPagePriority), IrpSp->Parameters.FileSystemControl.OutputBufferLength);
5457             break;
5458 
5459         case FSCTL_BTRFS_GET_USAGE:
5460             Status = get_usage(DeviceObject->DeviceExtension, map_user_buffer(Irp, NormalPagePriority), IrpSp->Parameters.FileSystemControl.OutputBufferLength, Irp);
5461             break;
5462 
5463         case FSCTL_BTRFS_START_BALANCE:
5464             Status = start_balance(DeviceObject->DeviceExtension, Irp->AssociatedIrp.SystemBuffer, IrpSp->Parameters.FileSystemControl.InputBufferLength, Irp->RequestorMode);
5465             break;
5466 
5467         case FSCTL_BTRFS_QUERY_BALANCE:
5468             Status = query_balance(DeviceObject->DeviceExtension, map_user_buffer(Irp, NormalPagePriority), IrpSp->Parameters.FileSystemControl.OutputBufferLength);
5469             break;
5470 
5471         case FSCTL_BTRFS_PAUSE_BALANCE:
5472             Status = pause_balance(DeviceObject->DeviceExtension, Irp->RequestorMode);
5473             break;
5474 
5475         case FSCTL_BTRFS_RESUME_BALANCE:
5476             Status = resume_balance(DeviceObject->DeviceExtension, Irp->RequestorMode);
5477             break;
5478 
5479         case FSCTL_BTRFS_STOP_BALANCE:
5480             Status = stop_balance(DeviceObject->DeviceExtension, Irp->RequestorMode);
5481             break;
5482 
5483         case FSCTL_BTRFS_ADD_DEVICE:
5484             Status = add_device(DeviceObject->DeviceExtension, Irp, Irp->RequestorMode);
5485             break;
5486 
5487         case FSCTL_BTRFS_REMOVE_DEVICE:
5488             Status = remove_device(DeviceObject->DeviceExtension, Irp->AssociatedIrp.SystemBuffer, IrpSp->Parameters.FileSystemControl.InputBufferLength, Irp->RequestorMode);
5489             break;
5490 
5491         case FSCTL_BTRFS_GET_UUID:
5492             Status = query_uuid(DeviceObject->DeviceExtension, map_user_buffer(Irp, NormalPagePriority), IrpSp->Parameters.FileSystemControl.OutputBufferLength);
5493             break;
5494 
5495         case FSCTL_BTRFS_START_SCRUB:
5496             Status = start_scrub(DeviceObject->DeviceExtension, Irp->RequestorMode);
5497             break;
5498 
5499         case FSCTL_BTRFS_QUERY_SCRUB:
5500             Status = query_scrub(DeviceObject->DeviceExtension, Irp->RequestorMode, map_user_buffer(Irp, NormalPagePriority), IrpSp->Parameters.FileSystemControl.OutputBufferLength);
5501             break;
5502 
5503         case FSCTL_BTRFS_PAUSE_SCRUB:
5504             Status = pause_scrub(DeviceObject->DeviceExtension, Irp->RequestorMode);
5505             break;
5506 
5507         case FSCTL_BTRFS_RESUME_SCRUB:
5508             Status = resume_scrub(DeviceObject->DeviceExtension, Irp->RequestorMode);
5509             break;
5510 
5511         case FSCTL_BTRFS_STOP_SCRUB:
5512             Status = stop_scrub(DeviceObject->DeviceExtension, Irp->RequestorMode);
5513             break;
5514 
5515         case FSCTL_BTRFS_RESET_STATS:
5516             Status = reset_stats(DeviceObject->DeviceExtension, Irp->AssociatedIrp.SystemBuffer, IrpSp->Parameters.FileSystemControl.InputBufferLength, Irp->RequestorMode);
5517             break;
5518 
5519         case FSCTL_BTRFS_MKNOD:
5520             Status = mknod(DeviceObject->DeviceExtension, IrpSp->FileObject, Irp->AssociatedIrp.SystemBuffer, IrpSp->Parameters.FileSystemControl.InputBufferLength, Irp);
5521             break;
5522 
5523         case FSCTL_BTRFS_RECEIVED_SUBVOL:
5524             Status = recvd_subvol(DeviceObject->DeviceExtension, IrpSp->FileObject, Irp->AssociatedIrp.SystemBuffer,
5525                                   IrpSp->Parameters.FileSystemControl.InputBufferLength, Irp->RequestorMode);
5526             break;
5527 
5528         case FSCTL_BTRFS_GET_XATTRS:
5529             Status = fsctl_get_xattrs(DeviceObject->DeviceExtension, IrpSp->FileObject, Irp->UserBuffer, IrpSp->Parameters.FileSystemControl.OutputBufferLength, Irp->RequestorMode);
5530             break;
5531 
5532         case FSCTL_BTRFS_SET_XATTR:
5533             Status = fsctl_set_xattr(DeviceObject->DeviceExtension, IrpSp->FileObject, Irp->AssociatedIrp.SystemBuffer,
5534                                      IrpSp->Parameters.FileSystemControl.InputBufferLength, Irp);
5535             break;
5536 
5537         case FSCTL_BTRFS_RESERVE_SUBVOL:
5538             Status = reserve_subvol(DeviceObject->DeviceExtension, IrpSp->FileObject, Irp);
5539             break;
5540 
5541         case FSCTL_BTRFS_FIND_SUBVOL:
5542             Status = find_subvol(DeviceObject->DeviceExtension, Irp->AssociatedIrp.SystemBuffer, IrpSp->Parameters.FileSystemControl.InputBufferLength,
5543                                  Irp->UserBuffer, IrpSp->Parameters.FileSystemControl.OutputBufferLength, Irp);
5544             break;
5545 
5546         case FSCTL_BTRFS_SEND_SUBVOL:
5547             Status = send_subvol(DeviceObject->DeviceExtension, Irp->AssociatedIrp.SystemBuffer, IrpSp->Parameters.FileSystemControl.InputBufferLength,
5548                                  IrpSp->FileObject, Irp);
5549             break;
5550 
5551         case FSCTL_BTRFS_READ_SEND_BUFFER:
5552             Status = read_send_buffer(DeviceObject->DeviceExtension, IrpSp->FileObject, map_user_buffer(Irp, NormalPagePriority), IrpSp->Parameters.FileSystemControl.OutputBufferLength,
5553                                       &Irp->IoStatus.Information, Irp->RequestorMode);
5554             break;
5555 
5556         case FSCTL_BTRFS_RESIZE:
5557             Status = resize_device(DeviceObject->DeviceExtension, Irp->AssociatedIrp.SystemBuffer,
5558                                    IrpSp->Parameters.FileSystemControl.InputBufferLength, Irp);
5559             break;
5560 
5561         default:
5562             WARN("unknown control code %lx (DeviceType = %lx, Access = %lx, Function = %lx, Method = %lx)\n",
5563                           IrpSp->Parameters.FileSystemControl.FsControlCode, (IrpSp->Parameters.FileSystemControl.FsControlCode & 0xff0000) >> 16,
5564                           (IrpSp->Parameters.FileSystemControl.FsControlCode & 0xc000) >> 14, (IrpSp->Parameters.FileSystemControl.FsControlCode & 0x3ffc) >> 2,
5565                           IrpSp->Parameters.FileSystemControl.FsControlCode & 0x3);
5566             Status = STATUS_INVALID_DEVICE_REQUEST;
5567             break;
5568     }
5569 
5570     return Status;
5571 }
5572