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