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