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