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