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