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