xref: /reactos/drivers/filesystems/btrfs/btrfs.c (revision 80774a2f)
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 #ifdef _DEBUG
19 #define DEBUG
20 #endif
21 
22 #include "btrfs_drv.h"
23 #ifndef __REACTOS__
24 #ifndef _MSC_VER
25 #include <cpuid.h>
26 #else
27 #include <intrin.h>
28 #endif
29 #endif
30 #include <ntddscsi.h>
31 #include "btrfs.h"
32 #include <ata.h>
33 
34 #ifndef _MSC_VER
35 #include <initguid.h>
36 #include <ntddstor.h>
37 #undef INITGUID
38 #endif
39 
40 #include <ntdddisk.h>
41 #include <ntddvol.h>
42 
43 #ifdef _MSC_VER
44 #include <initguid.h>
45 #include <ntddstor.h>
46 #undef INITGUID
47 #endif
48 
49 #define INCOMPAT_SUPPORTED (BTRFS_INCOMPAT_FLAGS_MIXED_BACKREF | BTRFS_INCOMPAT_FLAGS_DEFAULT_SUBVOL | BTRFS_INCOMPAT_FLAGS_MIXED_GROUPS | \
50                             BTRFS_INCOMPAT_FLAGS_COMPRESS_LZO | BTRFS_INCOMPAT_FLAGS_BIG_METADATA | BTRFS_INCOMPAT_FLAGS_RAID56 | \
51                             BTRFS_INCOMPAT_FLAGS_EXTENDED_IREF | BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA | BTRFS_INCOMPAT_FLAGS_NO_HOLES)
52 #define COMPAT_RO_SUPPORTED (BTRFS_COMPAT_RO_FLAGS_FREE_SPACE_CACHE | BTRFS_COMPAT_RO_FLAGS_FREE_SPACE_CACHE_VALID)
53 
54 static WCHAR device_name[] = {'\\','B','t','r','f','s',0};
55 static WCHAR dosdevice_name[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\','B','t','r','f','s',0};
56 
57 DEFINE_GUID(BtrfsBusInterface, 0x4d414874, 0x6865, 0x6761, 0x6d, 0x65, 0x83, 0x69, 0x17, 0x9a, 0x7d, 0x1d);
58 
59 PDRIVER_OBJECT drvobj;
60 PDEVICE_OBJECT master_devobj;
61 #ifndef __REACTOS__
62 BOOL have_sse42 = FALSE, have_sse2 = FALSE;
63 #endif
64 UINT64 num_reads = 0;
65 LIST_ENTRY uid_map_list, gid_map_list;
66 LIST_ENTRY VcbList;
67 ERESOURCE global_loading_lock;
68 UINT32 debug_log_level = 0;
69 UINT32 mount_compress = 0;
70 UINT32 mount_compress_force = 0;
71 UINT32 mount_compress_type = 0;
72 UINT32 mount_zlib_level = 3;
73 UINT32 mount_flush_interval = 30;
74 UINT32 mount_max_inline = 2048;
75 UINT32 mount_skip_balance = 0;
76 UINT32 mount_no_barrier = 0;
77 UINT32 mount_no_trim = 0;
78 UINT32 mount_clear_cache = 0;
79 UINT32 mount_allow_degraded = 0;
80 UINT32 mount_readonly = 0;
81 UINT32 no_pnp = 0;
82 BOOL log_started = FALSE;
83 UNICODE_STRING log_device, log_file, registry_path;
84 tPsUpdateDiskCounters fPsUpdateDiskCounters;
85 tCcCopyReadEx fCcCopyReadEx;
86 tCcCopyWriteEx fCcCopyWriteEx;
87 tCcSetAdditionalCacheAttributesEx fCcSetAdditionalCacheAttributesEx;
88 tFsRtlUpdateDiskCounters fFsRtlUpdateDiskCounters;
89 BOOL diskacc = FALSE;
90 void *notification_entry = NULL, *notification_entry2 = NULL, *notification_entry3 = NULL;
91 ERESOURCE pdo_list_lock, mapping_lock;
92 LIST_ENTRY pdo_list;
93 BOOL finished_probing = FALSE;
94 HANDLE degraded_wait_handle = NULL, mountmgr_thread_handle = NULL;
95 BOOL degraded_wait = TRUE;
96 KEVENT mountmgr_thread_event;
97 BOOL shutting_down = FALSE;
98 
99 #ifdef _DEBUG
100 PFILE_OBJECT comfo = NULL;
101 PDEVICE_OBJECT comdo = NULL;
102 HANDLE log_handle = NULL;
103 ERESOURCE log_lock;
104 HANDLE serial_thread_handle = NULL;
105 
106 static void init_serial(BOOL first_time);
107 #endif
108 
109 static NTSTATUS close_file(_In_ PFILE_OBJECT FileObject, _In_ PIRP Irp);
110 
111 typedef struct {
112     KEVENT Event;
113     IO_STATUS_BLOCK iosb;
114 } read_context;
115 
116 #ifdef _DEBUG
117 _Function_class_(IO_COMPLETION_ROUTINE)
118 static NTSTATUS dbg_completion(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp, _In_ PVOID conptr) {
119     read_context* context = conptr;
120 
121     UNUSED(DeviceObject);
122 
123     context->iosb = Irp->IoStatus;
124     KeSetEvent(&context->Event, 0, FALSE);
125 
126     return STATUS_MORE_PROCESSING_REQUIRED;
127 }
128 
129 #ifdef DEBUG_LONG_MESSAGES
130 void _debug_message(_In_ const char* func, _In_ const char* file, _In_ unsigned int line, _In_ char* s, ...) {
131 #else
132 void _debug_message(_In_ const char* func, _In_ char* s, ...) {
133 #endif
134     LARGE_INTEGER offset;
135     PIO_STACK_LOCATION IrpSp;
136     NTSTATUS Status;
137     PIRP Irp;
138     va_list ap;
139     char *buf2, *buf;
140     read_context context;
141     UINT32 length;
142 
143     buf2 = ExAllocatePoolWithTag(NonPagedPool, 1024, ALLOC_TAG);
144 
145     if (!buf2) {
146         DbgPrint("Couldn't allocate buffer in debug_message\n");
147         return;
148     }
149 
150 #ifdef DEBUG_LONG_MESSAGES
151     sprintf(buf2, "%p:%s:%s:%u:", PsGetCurrentThread(), func, file, line);
152 #else
153     sprintf(buf2, "%p:%s:", PsGetCurrentThread(), func);
154 #endif
155     buf = &buf2[strlen(buf2)];
156 
157     va_start(ap, s);
158     vsprintf(buf, s, ap);
159 
160     ExAcquireResourceSharedLite(&log_lock, TRUE);
161 
162     if (!log_started || (log_device.Length == 0 && log_file.Length == 0)) {
163         DbgPrint(buf2);
164     } else if (log_device.Length > 0) {
165         if (!comdo) {
166             DbgPrint("comdo is NULL :-(\n");
167             DbgPrint(buf2);
168             goto exit2;
169         }
170 
171         length = (UINT32)strlen(buf2);
172 
173         offset.u.LowPart = 0;
174         offset.u.HighPart = 0;
175 
176         RtlZeroMemory(&context, sizeof(read_context));
177 
178         KeInitializeEvent(&context.Event, NotificationEvent, FALSE);
179 
180         Irp = IoAllocateIrp(comdo->StackSize, FALSE);
181 
182         if (!Irp) {
183             DbgPrint("IoAllocateIrp failed\n");
184             goto exit2;
185         }
186 
187         IrpSp = IoGetNextIrpStackLocation(Irp);
188         IrpSp->MajorFunction = IRP_MJ_WRITE;
189 
190         if (comdo->Flags & DO_BUFFERED_IO) {
191             Irp->AssociatedIrp.SystemBuffer = buf2;
192 
193             Irp->Flags = IRP_BUFFERED_IO;
194         } else if (comdo->Flags & DO_DIRECT_IO) {
195             Irp->MdlAddress = IoAllocateMdl(buf2, length, FALSE, FALSE, NULL);
196             if (!Irp->MdlAddress) {
197                 DbgPrint("IoAllocateMdl failed\n");
198                 goto exit;
199             }
200 
201             MmBuildMdlForNonPagedPool(Irp->MdlAddress);
202         } else {
203             Irp->UserBuffer = buf2;
204         }
205 
206         IrpSp->Parameters.Write.Length = length;
207         IrpSp->Parameters.Write.ByteOffset = offset;
208 
209         Irp->UserIosb = &context.iosb;
210 
211         Irp->UserEvent = &context.Event;
212 
213         IoSetCompletionRoutine(Irp, dbg_completion, &context, TRUE, TRUE, TRUE);
214 
215         Status = IoCallDriver(comdo, Irp);
216 
217         if (Status == STATUS_PENDING) {
218             KeWaitForSingleObject(&context.Event, Executive, KernelMode, FALSE, NULL);
219             Status = context.iosb.Status;
220         }
221 
222         if (comdo->Flags & DO_DIRECT_IO)
223             IoFreeMdl(Irp->MdlAddress);
224 
225         if (!NT_SUCCESS(Status)) {
226             DbgPrint("failed to write to COM1 - error %08x\n", Status);
227             goto exit;
228         }
229 
230 exit:
231         IoFreeIrp(Irp);
232     } else if (log_handle != NULL) {
233         IO_STATUS_BLOCK iosb;
234 
235         length = (UINT32)strlen(buf2);
236 
237         Status = ZwWriteFile(log_handle, NULL, NULL, NULL, &iosb, buf2, length, NULL, NULL);
238 
239         if (!NT_SUCCESS(Status)) {
240             DbgPrint("failed to write to file - error %08x\n", Status);
241         }
242     }
243 
244 exit2:
245     ExReleaseResourceLite(&log_lock);
246 
247     va_end(ap);
248 
249     if (buf2)
250         ExFreePool(buf2);
251 }
252 #endif
253 
254 BOOL is_top_level(_In_ PIRP Irp) {
255     if (!IoGetTopLevelIrp()) {
256         IoSetTopLevelIrp(Irp);
257         return TRUE;
258     }
259 
260     return FALSE;
261 }
262 
263 _Function_class_(DRIVER_UNLOAD)
264 #ifdef __REACTOS__
265 static void NTAPI DriverUnload(_In_ PDRIVER_OBJECT DriverObject) {
266 #else
267 static void DriverUnload(_In_ PDRIVER_OBJECT DriverObject) {
268 #endif
269     UNICODE_STRING dosdevice_nameW;
270 
271     ERR("DriverUnload\n");
272 
273     free_cache();
274 
275     IoUnregisterFileSystem(DriverObject->DeviceObject);
276 
277     if (notification_entry2)
278 #ifdef __REACTOS__
279         IoUnregisterPlugPlayNotification(notification_entry2);
280 #else
281         IoUnregisterPlugPlayNotificationEx(notification_entry2);
282 #endif
283 
284     if (notification_entry3)
285 #ifdef __REACTOS__
286         IoUnregisterPlugPlayNotification(notification_entry3);
287 #else
288         IoUnregisterPlugPlayNotificationEx(notification_entry3);
289 #endif
290 
291     if (notification_entry)
292 #ifdef __REACTOS__
293         IoUnregisterPlugPlayNotification(notification_entry);
294 #else
295         IoUnregisterPlugPlayNotificationEx(notification_entry);
296 #endif
297 
298     dosdevice_nameW.Buffer = dosdevice_name;
299     dosdevice_nameW.Length = dosdevice_nameW.MaximumLength = (USHORT)wcslen(dosdevice_name) * sizeof(WCHAR);
300 
301     IoDeleteSymbolicLink(&dosdevice_nameW);
302     IoDeleteDevice(DriverObject->DeviceObject);
303 
304     while (!IsListEmpty(&uid_map_list)) {
305         LIST_ENTRY* le = RemoveHeadList(&uid_map_list);
306         uid_map* um = CONTAINING_RECORD(le, uid_map, listentry);
307 
308         ExFreePool(um->sid);
309 
310         ExFreePool(um);
311     }
312 
313     while (!IsListEmpty(&gid_map_list)) {
314         gid_map* gm = CONTAINING_RECORD(RemoveHeadList(&gid_map_list), gid_map, listentry);
315 
316         ExFreePool(gm->sid);
317         ExFreePool(gm);
318     }
319 
320     // FIXME - free volumes and their devpaths
321 
322 #ifdef _DEBUG
323     if (comfo)
324         ObDereferenceObject(comfo);
325 
326     if (log_handle)
327         ZwClose(log_handle);
328 #endif
329 
330     ExDeleteResourceLite(&global_loading_lock);
331     ExDeleteResourceLite(&pdo_list_lock);
332 
333     if (log_device.Buffer)
334         ExFreePool(log_device.Buffer);
335 
336     if (log_file.Buffer)
337         ExFreePool(log_file.Buffer);
338 
339     if (registry_path.Buffer)
340         ExFreePool(registry_path.Buffer);
341 
342 #ifdef _DEBUG
343     ExDeleteResourceLite(&log_lock);
344 #endif
345     ExDeleteResourceLite(&mapping_lock);
346 }
347 
348 static BOOL get_last_inode(_In_ _Requires_exclusive_lock_held_(_Curr_->tree_lock) device_extension* Vcb, _In_ root* r, _In_opt_ PIRP Irp) {
349     KEY searchkey;
350     traverse_ptr tp, prev_tp;
351     NTSTATUS Status;
352 
353     // get last entry
354     searchkey.obj_id = 0xffffffffffffffff;
355     searchkey.obj_type = 0xff;
356     searchkey.offset = 0xffffffffffffffff;
357 
358     Status = find_item(Vcb, r, &tp, &searchkey, FALSE, Irp);
359     if (!NT_SUCCESS(Status)) {
360         ERR("error - find_item returned %08x\n", Status);
361         return FALSE;
362     }
363 
364     if (tp.item->key.obj_type == TYPE_INODE_ITEM || (tp.item->key.obj_type == TYPE_ROOT_ITEM && !(tp.item->key.obj_id & 0x8000000000000000))) {
365         r->lastinode = tp.item->key.obj_id;
366         TRACE("last inode for tree %llx is %llx\n", r->id, r->lastinode);
367         return TRUE;
368     }
369 
370     while (find_prev_item(Vcb, &tp, &prev_tp, Irp)) {
371         tp = prev_tp;
372 
373         TRACE("moving on to %llx,%x,%llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
374 
375         if (tp.item->key.obj_type == TYPE_INODE_ITEM || (tp.item->key.obj_type == TYPE_ROOT_ITEM && !(tp.item->key.obj_id & 0x8000000000000000))) {
376             r->lastinode = tp.item->key.obj_id;
377             TRACE("last inode for tree %llx is %llx\n", r->id, r->lastinode);
378             return TRUE;
379         }
380     }
381 
382     r->lastinode = SUBVOL_ROOT_INODE;
383 
384     WARN("no INODE_ITEMs in tree %llx\n", r->id);
385 
386     return TRUE;
387 }
388 
389 _Success_(return)
390 static BOOL extract_xattr(_In_reads_bytes_(size) void* item, _In_ USHORT size, _In_z_ char* name, _Out_ UINT8** data, _Out_ UINT16* datalen) {
391     DIR_ITEM* xa = (DIR_ITEM*)item;
392     USHORT xasize;
393 
394     while (TRUE) {
395         if (size < sizeof(DIR_ITEM) || size < (sizeof(DIR_ITEM) - 1 + xa->m + xa->n)) {
396             WARN("DIR_ITEM is truncated\n");
397             return FALSE;
398         }
399 
400         if (xa->n == strlen(name) && RtlCompareMemory(name, xa->name, xa->n) == xa->n) {
401             TRACE("found xattr %s\n", name);
402 
403             *datalen = xa->m;
404 
405             if (xa->m > 0) {
406                 *data = ExAllocatePoolWithTag(PagedPool, xa->m, ALLOC_TAG);
407                 if (!*data) {
408                     ERR("out of memory\n");
409                     return FALSE;
410                 }
411 
412                 RtlCopyMemory(*data, &xa->name[xa->n], xa->m);
413             } else
414                 *data = NULL;
415 
416             return TRUE;
417         }
418 
419         xasize = sizeof(DIR_ITEM) - 1 + xa->m + xa->n;
420 
421         if (size > xasize) {
422             size -= xasize;
423             xa = (DIR_ITEM*)&xa->name[xa->m + xa->n];
424         } else
425             break;
426     }
427 
428     TRACE("xattr %s not found\n", name);
429 
430     return FALSE;
431 }
432 
433 _Success_(return)
434 BOOL get_xattr(_In_ _Requires_lock_held_(_Curr_->tree_lock) device_extension* Vcb, _In_ root* subvol, _In_ UINT64 inode, _In_z_ char* name, _In_ UINT32 crc32,
435                _Out_ UINT8** data, _Out_ UINT16* datalen, _In_opt_ PIRP Irp) {
436     KEY searchkey;
437     traverse_ptr tp;
438     NTSTATUS Status;
439 
440     TRACE("(%p, %llx, %llx, %s, %08x, %p, %p)\n", Vcb, subvol->id, inode, name, crc32, data, datalen);
441 
442     searchkey.obj_id = inode;
443     searchkey.obj_type = TYPE_XATTR_ITEM;
444     searchkey.offset = crc32;
445 
446     Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE, Irp);
447     if (!NT_SUCCESS(Status)) {
448         ERR("error - find_item returned %08x\n", Status);
449         return FALSE;
450     }
451 
452     if (keycmp(tp.item->key, searchkey)) {
453         TRACE("could not find item (%llx,%x,%llx)\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset);
454         return FALSE;
455     }
456 
457     if (tp.item->size < sizeof(DIR_ITEM)) {
458         ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(DIR_ITEM));
459         return FALSE;
460     }
461 
462     return extract_xattr(tp.item->data, tp.item->size, name, data, datalen);
463 }
464 
465 _Dispatch_type_(IRP_MJ_CLOSE)
466 _Function_class_(DRIVER_DISPATCH)
467 static NTSTATUS drv_close(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) {
468     NTSTATUS Status;
469     PIO_STACK_LOCATION IrpSp;
470     device_extension* Vcb = DeviceObject->DeviceExtension;
471     BOOL top_level;
472 
473     FsRtlEnterFileSystem();
474 
475     TRACE("close\n");
476 
477     top_level = is_top_level(Irp);
478 
479     if (DeviceObject == master_devobj) {
480         TRACE("Closing file system\n");
481         Status = STATUS_SUCCESS;
482         goto end;
483     } else if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
484         Status = vol_close(DeviceObject, Irp);
485         goto end;
486     } else if (!Vcb || Vcb->type != VCB_TYPE_FS) {
487         Status = STATUS_INVALID_PARAMETER;
488         goto end;
489     }
490 
491     IrpSp = IoGetCurrentIrpStackLocation(Irp);
492 
493     // FIXME - unmount if called for volume
494     // FIXME - call FsRtlNotifyUninitializeSync(&Vcb->NotifySync) if unmounting
495 
496     Status = close_file(IrpSp->FileObject, Irp);
497 
498 end:
499     Irp->IoStatus.Status = Status;
500     Irp->IoStatus.Information = 0;
501 
502     IoCompleteRequest( Irp, IO_DISK_INCREMENT );
503 
504     if (top_level)
505         IoSetTopLevelIrp(NULL);
506 
507     TRACE("returning %08x\n", Status);
508 
509     FsRtlExitFileSystem();
510 
511     return Status;
512 }
513 
514 _Dispatch_type_(IRP_MJ_FLUSH_BUFFERS)
515 _Function_class_(DRIVER_DISPATCH)
516 static NTSTATUS drv_flush_buffers(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) {
517     NTSTATUS Status;
518     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp );
519     PFILE_OBJECT FileObject = IrpSp->FileObject;
520     fcb* fcb = FileObject->FsContext;
521     device_extension* Vcb = DeviceObject->DeviceExtension;
522     BOOL top_level;
523 
524     FsRtlEnterFileSystem();
525 
526     TRACE("flush buffers\n");
527 
528     top_level = is_top_level(Irp);
529 
530     if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
531         Status = vol_flush_buffers(DeviceObject, Irp);
532         goto end;
533     } else if (!Vcb || Vcb->type != VCB_TYPE_FS) {
534         Status = STATUS_INVALID_PARAMETER;
535         goto end;
536     }
537 
538     if (!fcb) {
539         ERR("fcb was NULL\n");
540         Status = STATUS_INVALID_PARAMETER;
541         goto end;
542     }
543 
544     if (fcb == Vcb->volume_fcb) {
545         Status = STATUS_INVALID_PARAMETER;
546         goto end;
547     }
548 
549     Irp->IoStatus.Information = 0;
550 
551     fcb->Header.IsFastIoPossible = fast_io_possible(fcb);
552 
553     Status = STATUS_SUCCESS;
554     Irp->IoStatus.Status = Status;
555 
556     if (fcb->type != BTRFS_TYPE_DIRECTORY) {
557         CcFlushCache(&fcb->nonpaged->segment_object, NULL, 0, &Irp->IoStatus);
558 
559         if (fcb->Header.PagingIoResource) {
560             ExAcquireResourceExclusiveLite(fcb->Header.PagingIoResource, TRUE);
561             ExReleaseResourceLite(fcb->Header.PagingIoResource);
562         }
563 
564         Status = Irp->IoStatus.Status;
565     }
566 
567 end:
568     IoCompleteRequest(Irp, IO_NO_INCREMENT);
569 
570     TRACE("returning %08x\n", Status);
571 
572     if (top_level)
573         IoSetTopLevelIrp(NULL);
574 
575     FsRtlExitFileSystem();
576 
577     return Status;
578 }
579 
580 static void calculate_total_space(_In_ device_extension* Vcb, _Out_ UINT64* totalsize, _Out_ UINT64* freespace) {
581     UINT64 nfactor, dfactor, sectors_used;
582 
583     if (Vcb->data_flags & BLOCK_FLAG_DUPLICATE || Vcb->data_flags & BLOCK_FLAG_RAID1 || Vcb->data_flags & BLOCK_FLAG_RAID10) {
584         nfactor = 1;
585         dfactor = 2;
586     } else if (Vcb->data_flags & BLOCK_FLAG_RAID5) {
587         nfactor = Vcb->superblock.num_devices - 1;
588         dfactor = Vcb->superblock.num_devices;
589     } else if (Vcb->data_flags & BLOCK_FLAG_RAID6) {
590         nfactor = Vcb->superblock.num_devices - 2;
591         dfactor = Vcb->superblock.num_devices;
592     } else {
593         nfactor = 1;
594         dfactor = 1;
595     }
596 
597     sectors_used = Vcb->superblock.bytes_used / Vcb->superblock.sector_size;
598 
599     *totalsize = (Vcb->superblock.total_bytes / Vcb->superblock.sector_size) * nfactor / dfactor;
600     *freespace = sectors_used > *totalsize ? 0 : (*totalsize - sectors_used);
601 }
602 
603 #ifndef __REACTOS__
604 // This function exists because we have to lie about our FS type in certain situations.
605 // MPR!MprGetConnection queries the FS type, and compares it to a whitelist. If it doesn't match,
606 // it will return ERROR_NO_NET_OR_BAD_PATH, which prevents UAC from working.
607 // The command mklink refuses to create hard links on anything other than NTFS, so we have to
608 // blacklist cmd.exe too.
609 
610 static BOOL lie_about_fs_type() {
611     NTSTATUS Status;
612     PROCESS_BASIC_INFORMATION pbi;
613     PPEB peb;
614     LIST_ENTRY* le;
615     ULONG retlen;
616 
617     static WCHAR mpr[] = L"MPR.DLL";
618     static WCHAR cmd[] = L"CMD.EXE";
619     static WCHAR fsutil[] = L"FSUTIL.EXE";
620     UNICODE_STRING mprus, cmdus, fsutilus;
621 
622     mprus.Buffer = mpr;
623     mprus.Length = mprus.MaximumLength = (USHORT)(wcslen(mpr) * sizeof(WCHAR));
624     cmdus.Buffer = cmd;
625     cmdus.Length = cmdus.MaximumLength = (USHORT)(wcslen(cmd) * sizeof(WCHAR));
626     fsutilus.Buffer = fsutil;
627     fsutilus.Length = fsutilus.MaximumLength = (USHORT)(wcslen(fsutil) * sizeof(WCHAR));
628 
629     if (!PsGetCurrentProcess())
630         return FALSE;
631 
632     Status = ZwQueryInformationProcess(NtCurrentProcess(), ProcessBasicInformation, &pbi, sizeof(pbi), &retlen);
633 
634     if (!NT_SUCCESS(Status)) {
635         ERR("ZwQueryInformationProcess returned %08x\n", Status);
636         return FALSE;
637     }
638 
639     if (!pbi.PebBaseAddress)
640         return FALSE;
641 
642     peb = pbi.PebBaseAddress;
643 
644     if (!peb->Ldr)
645         return FALSE;
646 
647     le = peb->Ldr->InMemoryOrderModuleList.Flink;
648     while (le != &peb->Ldr->InMemoryOrderModuleList) {
649         LDR_DATA_TABLE_ENTRY* entry = CONTAINING_RECORD(le, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);
650         BOOL blacklist = FALSE;
651 
652         if (entry->FullDllName.Length >= mprus.Length) {
653             UNICODE_STRING name;
654 
655             name.Buffer = &entry->FullDllName.Buffer[(entry->FullDllName.Length - mprus.Length) / sizeof(WCHAR)];
656             name.Length = name.MaximumLength = mprus.Length;
657 
658             blacklist = FsRtlAreNamesEqual(&name, &mprus, TRUE, NULL);
659         }
660 
661         if (!blacklist && entry->FullDllName.Length >= cmdus.Length) {
662             UNICODE_STRING name;
663 
664             name.Buffer = &entry->FullDllName.Buffer[(entry->FullDllName.Length - cmdus.Length) / sizeof(WCHAR)];
665             name.Length = name.MaximumLength = cmdus.Length;
666 
667             blacklist = FsRtlAreNamesEqual(&name, &cmdus, TRUE, NULL);
668         }
669 
670         if (!blacklist && entry->FullDllName.Length >= fsutilus.Length) {
671             UNICODE_STRING name;
672 
673             name.Buffer = &entry->FullDllName.Buffer[(entry->FullDllName.Length - fsutilus.Length) / sizeof(WCHAR)];
674             name.Length = name.MaximumLength = fsutilus.Length;
675 
676             blacklist = FsRtlAreNamesEqual(&name, &fsutilus, TRUE, NULL);
677         }
678 
679         if (blacklist) {
680             void** frames;
681             ULONG i, num_frames;
682 
683             frames = ExAllocatePoolWithTag(PagedPool, 256 * sizeof(void*), ALLOC_TAG);
684             if (!frames) {
685                 ERR("out of memory\n");
686                 return FALSE;
687             }
688 
689             num_frames = RtlWalkFrameChain(frames, 256, 1);
690 
691             for (i = 0; i < num_frames; i++) {
692                 // entry->Reserved3[1] appears to be the image size
693                 if (frames[i] >= entry->DllBase && (ULONG_PTR)frames[i] <= (ULONG_PTR)entry->DllBase + (ULONG_PTR)entry->Reserved3[1]) {
694                     ExFreePool(frames);
695                     return TRUE;
696                 }
697             }
698 
699             ExFreePool(frames);
700         }
701 
702         le = le->Flink;
703     }
704 
705     return FALSE;
706 }
707 #endif
708 
709 _Dispatch_type_(IRP_MJ_QUERY_VOLUME_INFORMATION)
710 _Function_class_(DRIVER_DISPATCH)
711 static NTSTATUS drv_query_volume_information(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) {
712     PIO_STACK_LOCATION IrpSp;
713     NTSTATUS Status;
714     ULONG BytesCopied = 0;
715     device_extension* Vcb = DeviceObject->DeviceExtension;
716     BOOL top_level;
717 
718     FsRtlEnterFileSystem();
719 
720     TRACE("query volume information\n");
721     top_level = is_top_level(Irp);
722 
723     if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
724         Status = vol_query_volume_information(DeviceObject, Irp);
725         goto end;
726     } else if (!Vcb || Vcb->type != VCB_TYPE_FS) {
727         Status = STATUS_INVALID_PARAMETER;
728         goto end;
729     }
730 
731     IrpSp = IoGetCurrentIrpStackLocation(Irp);
732 
733     Status = STATUS_NOT_IMPLEMENTED;
734 
735     switch (IrpSp->Parameters.QueryVolume.FsInformationClass) {
736         case FileFsAttributeInformation:
737         {
738             FILE_FS_ATTRIBUTE_INFORMATION* data = Irp->AssociatedIrp.SystemBuffer;
739             BOOL overflow = FALSE;
740 #ifndef __REACTOS__
741             WCHAR* fs_name = (Irp->RequestorMode == UserMode && lie_about_fs_type()) ? L"NTFS" : L"Btrfs";
742             ULONG fs_name_len = (ULONG)wcslen(fs_name) * sizeof(WCHAR);
743 #else
744             WCHAR* fs_name = L"Btrfs";
745             ULONG fs_name_len = 5 * sizeof(WCHAR);
746 #endif
747             ULONG orig_fs_name_len = fs_name_len;
748 
749             TRACE("FileFsAttributeInformation\n");
750 
751             if (IrpSp->Parameters.QueryVolume.Length < sizeof(FILE_FS_ATTRIBUTE_INFORMATION) - sizeof(WCHAR) + fs_name_len) {
752                 if (IrpSp->Parameters.QueryVolume.Length > sizeof(FILE_FS_ATTRIBUTE_INFORMATION) - sizeof(WCHAR))
753                     fs_name_len = IrpSp->Parameters.QueryVolume.Length - sizeof(FILE_FS_ATTRIBUTE_INFORMATION) + sizeof(WCHAR);
754                 else
755                     fs_name_len = 0;
756 
757                 overflow = TRUE;
758             }
759 
760             data->FileSystemAttributes = FILE_CASE_PRESERVED_NAMES | FILE_CASE_SENSITIVE_SEARCH |
761                                          FILE_UNICODE_ON_DISK | FILE_NAMED_STREAMS | FILE_SUPPORTS_HARD_LINKS | FILE_PERSISTENT_ACLS |
762                                          FILE_SUPPORTS_REPARSE_POINTS | FILE_SUPPORTS_SPARSE_FILES | FILE_SUPPORTS_OBJECT_IDS |
763                                          FILE_SUPPORTS_OPEN_BY_FILE_ID | FILE_SUPPORTS_EXTENDED_ATTRIBUTES | FILE_SUPPORTS_BLOCK_REFCOUNTING;
764             if (Vcb->readonly)
765                 data->FileSystemAttributes |= FILE_READ_ONLY_VOLUME;
766 
767             // should also be FILE_FILE_COMPRESSION when supported
768             data->MaximumComponentNameLength = 255; // FIXME - check
769             data->FileSystemNameLength = orig_fs_name_len;
770             RtlCopyMemory(data->FileSystemName, fs_name, fs_name_len);
771 
772             BytesCopied = sizeof(FILE_FS_ATTRIBUTE_INFORMATION) - sizeof(WCHAR) + fs_name_len;
773             Status = overflow ? STATUS_BUFFER_OVERFLOW : STATUS_SUCCESS;
774             break;
775         }
776 
777         case FileFsDeviceInformation:
778         {
779             FILE_FS_DEVICE_INFORMATION* ffdi = Irp->AssociatedIrp.SystemBuffer;
780 
781             TRACE("FileFsDeviceInformation\n");
782 
783             ffdi->DeviceType = FILE_DEVICE_DISK;
784 
785             ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE);
786             ffdi->Characteristics = Vcb->Vpb->RealDevice->Characteristics;
787             ExReleaseResourceLite(&Vcb->tree_lock);
788 
789             if (Vcb->readonly)
790                 ffdi->Characteristics |= FILE_READ_ONLY_DEVICE;
791             else
792                 ffdi->Characteristics &= ~FILE_READ_ONLY_DEVICE;
793 
794             BytesCopied = sizeof(FILE_FS_DEVICE_INFORMATION);
795             Status = STATUS_SUCCESS;
796 
797             break;
798         }
799 
800         case FileFsFullSizeInformation:
801         {
802             FILE_FS_FULL_SIZE_INFORMATION* ffsi = Irp->AssociatedIrp.SystemBuffer;
803 
804             TRACE("FileFsFullSizeInformation\n");
805 
806             calculate_total_space(Vcb, (UINT64*)&ffsi->TotalAllocationUnits.QuadPart, (UINT64*)&ffsi->ActualAvailableAllocationUnits.QuadPart);
807             ffsi->CallerAvailableAllocationUnits.QuadPart = ffsi->ActualAvailableAllocationUnits.QuadPart;
808             ffsi->SectorsPerAllocationUnit = 1;
809             ffsi->BytesPerSector = Vcb->superblock.sector_size;
810 
811             BytesCopied = sizeof(FILE_FS_FULL_SIZE_INFORMATION);
812             Status = STATUS_SUCCESS;
813 
814             break;
815         }
816 
817         case FileFsObjectIdInformation:
818         {
819             FILE_FS_OBJECTID_INFORMATION* ffoi = Irp->AssociatedIrp.SystemBuffer;
820 
821             TRACE("FileFsObjectIdInformation\n");
822 
823             RtlCopyMemory(ffoi->ObjectId, &Vcb->superblock.uuid.uuid[0], sizeof(UCHAR) * 16);
824             RtlZeroMemory(ffoi->ExtendedInfo, sizeof(ffoi->ExtendedInfo));
825 
826             BytesCopied = sizeof(FILE_FS_OBJECTID_INFORMATION);
827             Status = STATUS_SUCCESS;
828 
829             break;
830         }
831 
832         case FileFsSizeInformation:
833         {
834             FILE_FS_SIZE_INFORMATION* ffsi = Irp->AssociatedIrp.SystemBuffer;
835 
836             TRACE("FileFsSizeInformation\n");
837 
838             calculate_total_space(Vcb, (UINT64*)&ffsi->TotalAllocationUnits.QuadPart, (UINT64*)&ffsi->AvailableAllocationUnits.QuadPart);
839             ffsi->SectorsPerAllocationUnit = 1;
840             ffsi->BytesPerSector = Vcb->superblock.sector_size;
841 
842             BytesCopied = sizeof(FILE_FS_SIZE_INFORMATION);
843             Status = STATUS_SUCCESS;
844 
845             break;
846         }
847 
848         case FileFsVolumeInformation:
849         {
850             FILE_FS_VOLUME_INFORMATION* data = Irp->AssociatedIrp.SystemBuffer;
851             FILE_FS_VOLUME_INFORMATION ffvi;
852             BOOL overflow = FALSE;
853             ULONG label_len, orig_label_len;
854 
855             TRACE("FileFsVolumeInformation\n");
856             TRACE("max length = %u\n", IrpSp->Parameters.QueryVolume.Length);
857 
858             ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE);
859 
860             Status = RtlUTF8ToUnicodeN(NULL, 0, &label_len, Vcb->superblock.label, (ULONG)strlen(Vcb->superblock.label));
861             if (!NT_SUCCESS(Status)) {
862                 ERR("RtlUTF8ToUnicodeN returned %08x\n", Status);
863                 ExReleaseResourceLite(&Vcb->tree_lock);
864                 break;
865             }
866 
867             orig_label_len = label_len;
868 
869             if (IrpSp->Parameters.QueryVolume.Length < sizeof(FILE_FS_VOLUME_INFORMATION) - sizeof(WCHAR) + label_len) {
870                 if (IrpSp->Parameters.QueryVolume.Length > sizeof(FILE_FS_VOLUME_INFORMATION) - sizeof(WCHAR))
871                     label_len = IrpSp->Parameters.QueryVolume.Length - sizeof(FILE_FS_VOLUME_INFORMATION) + sizeof(WCHAR);
872                 else
873                     label_len = 0;
874 
875                 overflow = TRUE;
876             }
877 
878             TRACE("label_len = %u\n", label_len);
879 
880             ffvi.VolumeCreationTime.QuadPart = 0; // FIXME
881             ffvi.VolumeSerialNumber = Vcb->superblock.uuid.uuid[12] << 24 | Vcb->superblock.uuid.uuid[13] << 16 | Vcb->superblock.uuid.uuid[14] << 8 | Vcb->superblock.uuid.uuid[15];
882             ffvi.VolumeLabelLength = orig_label_len;
883             ffvi.SupportsObjects = FALSE;
884 
885             RtlCopyMemory(data, &ffvi, min(sizeof(FILE_FS_VOLUME_INFORMATION) - sizeof(WCHAR), IrpSp->Parameters.QueryVolume.Length));
886 
887             if (label_len > 0) {
888                 ULONG bytecount;
889 
890                 Status = RtlUTF8ToUnicodeN(&data->VolumeLabel[0], label_len, &bytecount, Vcb->superblock.label, (ULONG)strlen(Vcb->superblock.label));
891                 if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_TOO_SMALL) {
892                     ERR("RtlUTF8ToUnicodeN returned %08x\n", Status);
893                     ExReleaseResourceLite(&Vcb->tree_lock);
894                     break;
895                 }
896 
897                 TRACE("label = %.*S\n", label_len / sizeof(WCHAR), data->VolumeLabel);
898             }
899 
900             ExReleaseResourceLite(&Vcb->tree_lock);
901 
902             BytesCopied = sizeof(FILE_FS_VOLUME_INFORMATION) - sizeof(WCHAR) + label_len;
903             Status = overflow ? STATUS_BUFFER_OVERFLOW : STATUS_SUCCESS;
904             break;
905         }
906 
907 #ifndef __REACTOS__
908 #ifdef _MSC_VER // not in mingw yet
909         case FileFsSectorSizeInformation:
910         {
911             FILE_FS_SECTOR_SIZE_INFORMATION* data = Irp->AssociatedIrp.SystemBuffer;
912 
913             data->LogicalBytesPerSector = Vcb->superblock.sector_size;
914             data->PhysicalBytesPerSectorForAtomicity = Vcb->superblock.sector_size;
915             data->PhysicalBytesPerSectorForPerformance = Vcb->superblock.sector_size;
916             data->FileSystemEffectivePhysicalBytesPerSectorForAtomicity = Vcb->superblock.sector_size;
917             data->ByteOffsetForSectorAlignment = 0;
918             data->ByteOffsetForPartitionAlignment = 0;
919 
920             data->Flags = SSINFO_FLAGS_ALIGNED_DEVICE | SSINFO_FLAGS_PARTITION_ALIGNED_ON_DEVICE;
921 
922             if (Vcb->trim && !Vcb->options.no_trim)
923                 data->Flags |= SSINFO_FLAGS_TRIM_ENABLED;
924 
925             BytesCopied = sizeof(FILE_FS_SECTOR_SIZE_INFORMATION);
926 
927             break;
928         }
929 #endif
930 #endif /* __REACTOS__ */
931 
932         default:
933             Status = STATUS_INVALID_PARAMETER;
934             WARN("unknown FsInformationClass %u\n", IrpSp->Parameters.QueryVolume.FsInformationClass);
935             break;
936     }
937 
938     if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW)
939         Irp->IoStatus.Information = 0;
940     else
941         Irp->IoStatus.Information = BytesCopied;
942 
943 end:
944     Irp->IoStatus.Status = Status;
945 
946     IoCompleteRequest( Irp, IO_DISK_INCREMENT );
947 
948     if (top_level)
949         IoSetTopLevelIrp(NULL);
950 
951     TRACE("query volume information returning %08x\n", Status);
952 
953     FsRtlExitFileSystem();
954 
955     return Status;
956 }
957 
958 _Function_class_(IO_COMPLETION_ROUTINE)
959 #ifdef __REACTOS__
960 static NTSTATUS NTAPI read_completion(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp, _In_ PVOID conptr) {
961 #else
962 static NTSTATUS read_completion(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp, _In_ PVOID conptr) {
963 #endif
964     read_context* context = conptr;
965 
966     UNUSED(DeviceObject);
967 
968     context->iosb = Irp->IoStatus;
969     KeSetEvent(&context->Event, 0, FALSE);
970 
971     return STATUS_MORE_PROCESSING_REQUIRED;
972 }
973 
974 NTSTATUS create_root(_In_ _Requires_exclusive_lock_held_(_Curr_->tree_lock) device_extension* Vcb, _In_ UINT64 id,
975                      _Out_ root** rootptr, _In_ BOOL no_tree, _In_ UINT64 offset, _In_opt_ PIRP Irp) {
976     NTSTATUS Status;
977     root* r;
978     tree* t = NULL;
979     ROOT_ITEM* ri;
980     traverse_ptr tp;
981 
982     r = ExAllocatePoolWithTag(PagedPool, sizeof(root), ALLOC_TAG);
983     if (!r) {
984         ERR("out of memory\n");
985         return STATUS_INSUFFICIENT_RESOURCES;
986     }
987 
988     r->nonpaged = ExAllocatePoolWithTag(NonPagedPool, sizeof(root_nonpaged), ALLOC_TAG);
989     if (!r->nonpaged) {
990         ERR("out of memory\n");
991         ExFreePool(r);
992         return STATUS_INSUFFICIENT_RESOURCES;
993     }
994 
995     if (!no_tree) {
996         t = ExAllocatePoolWithTag(PagedPool, sizeof(tree), ALLOC_TAG);
997         if (!t) {
998             ERR("out of memory\n");
999             ExFreePool(r->nonpaged);
1000             ExFreePool(r);
1001             return STATUS_INSUFFICIENT_RESOURCES;
1002         }
1003 
1004         t->is_unique = TRUE;
1005         t->uniqueness_determined = TRUE;
1006         t->buf = NULL;
1007     }
1008 
1009     ri = ExAllocatePoolWithTag(PagedPool, sizeof(ROOT_ITEM), ALLOC_TAG);
1010     if (!ri) {
1011         ERR("out of memory\n");
1012 
1013         if (t)
1014             ExFreePool(t);
1015 
1016         ExFreePool(r->nonpaged);
1017         ExFreePool(r);
1018         return STATUS_INSUFFICIENT_RESOURCES;
1019     }
1020 
1021     r->id = id;
1022     r->treeholder.address = 0;
1023     r->treeholder.generation = Vcb->superblock.generation;
1024     r->treeholder.tree = t;
1025     r->lastinode = 0;
1026     r->dirty = FALSE;
1027     r->received = FALSE;
1028     r->reserved = NULL;
1029     r->parent = 0;
1030     r->send_ops = 0;
1031     RtlZeroMemory(&r->root_item, sizeof(ROOT_ITEM));
1032     r->root_item.num_references = 1;
1033     InitializeListHead(&r->fcbs);
1034 
1035     RtlCopyMemory(ri, &r->root_item, sizeof(ROOT_ITEM));
1036 
1037     // We ask here for a traverse_ptr to the item we're inserting, so we can
1038     // copy some of the tree's variables
1039 
1040     Status = insert_tree_item(Vcb, Vcb->root_root, id, TYPE_ROOT_ITEM, offset, ri, sizeof(ROOT_ITEM), &tp, Irp);
1041     if (!NT_SUCCESS(Status)) {
1042         ERR("insert_tree_item returned %08x\n", Status);
1043         ExFreePool(ri);
1044 
1045         if (t)
1046             ExFreePool(t);
1047 
1048         ExFreePool(r->nonpaged);
1049         ExFreePool(r);
1050         return Status;
1051     }
1052 
1053     ExInitializeResourceLite(&r->nonpaged->load_tree_lock);
1054 
1055     InsertTailList(&Vcb->roots, &r->list_entry);
1056 
1057     if (!no_tree) {
1058         RtlZeroMemory(&t->header, sizeof(tree_header));
1059         t->header.fs_uuid = tp.tree->header.fs_uuid;
1060         t->header.address = 0;
1061         t->header.flags = HEADER_FLAG_MIXED_BACKREF | 1; // 1 == "written"? Why does the Linux driver record this?
1062         t->header.chunk_tree_uuid = tp.tree->header.chunk_tree_uuid;
1063         t->header.generation = Vcb->superblock.generation;
1064         t->header.tree_id = id;
1065         t->header.num_items = 0;
1066         t->header.level = 0;
1067 
1068         t->has_address = FALSE;
1069         t->size = 0;
1070         t->Vcb = Vcb;
1071         t->parent = NULL;
1072         t->paritem = NULL;
1073         t->root = r;
1074 
1075         InitializeListHead(&t->itemlist);
1076 
1077         t->new_address = 0;
1078         t->has_new_address = FALSE;
1079         t->updated_extents = FALSE;
1080 
1081         InsertTailList(&Vcb->trees, &t->list_entry);
1082         t->list_entry_hash.Flink = NULL;
1083 
1084         t->write = TRUE;
1085         Vcb->need_write = TRUE;
1086     }
1087 
1088     *rootptr = r;
1089 
1090     return STATUS_SUCCESS;
1091 }
1092 
1093 static NTSTATUS set_label(_In_ device_extension* Vcb, _In_ FILE_FS_LABEL_INFORMATION* ffli) {
1094     ULONG utf8len;
1095     NTSTATUS Status;
1096     ULONG vollen, i;
1097 
1098     TRACE("label = %.*S\n", ffli->VolumeLabelLength / sizeof(WCHAR), ffli->VolumeLabel);
1099 
1100     vollen = ffli->VolumeLabelLength;
1101 
1102     for (i = 0; i < ffli->VolumeLabelLength / sizeof(WCHAR); i++) {
1103         if (ffli->VolumeLabel[i] == 0) {
1104             vollen = i * sizeof(WCHAR);
1105             break;
1106         } else if (ffli->VolumeLabel[i] == '/' || ffli->VolumeLabel[i] == '\\') {
1107             Status = STATUS_INVALID_VOLUME_LABEL;
1108             goto end;
1109         }
1110     }
1111 
1112     if (vollen == 0) {
1113         utf8len = 0;
1114     } else {
1115         Status = RtlUnicodeToUTF8N(NULL, 0, &utf8len, ffli->VolumeLabel, vollen);
1116         if (!NT_SUCCESS(Status))
1117             goto end;
1118 
1119         if (utf8len > MAX_LABEL_SIZE) {
1120             Status = STATUS_INVALID_VOLUME_LABEL;
1121             goto end;
1122         }
1123     }
1124 
1125     ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE);
1126 
1127     if (utf8len > 0) {
1128         Status = RtlUnicodeToUTF8N((PCHAR)&Vcb->superblock.label, MAX_LABEL_SIZE, &utf8len, ffli->VolumeLabel, vollen);
1129         if (!NT_SUCCESS(Status))
1130             goto release;
1131     } else
1132         Status = STATUS_SUCCESS;
1133 
1134     if (utf8len < MAX_LABEL_SIZE)
1135         RtlZeroMemory(Vcb->superblock.label + utf8len, MAX_LABEL_SIZE - utf8len);
1136 
1137     Vcb->need_write = TRUE;
1138 
1139 release:
1140     ExReleaseResourceLite(&Vcb->tree_lock);
1141 
1142 end:
1143     TRACE("returning %08x\n", Status);
1144 
1145     return Status;
1146 }
1147 
1148 _Dispatch_type_(IRP_MJ_SET_VOLUME_INFORMATION)
1149 _Function_class_(DRIVER_DISPATCH)
1150 static NTSTATUS drv_set_volume_information(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) {
1151     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
1152     device_extension* Vcb = DeviceObject->DeviceExtension;
1153     NTSTATUS Status;
1154     BOOL top_level;
1155 
1156     FsRtlEnterFileSystem();
1157 
1158     TRACE("set volume information\n");
1159 
1160     top_level = is_top_level(Irp);
1161 
1162     if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
1163         Status = vol_set_volume_information(DeviceObject, Irp);
1164         goto end;
1165     } else if (!Vcb || Vcb->type != VCB_TYPE_FS) {
1166         Status = STATUS_INVALID_PARAMETER;
1167         goto end;
1168     }
1169 
1170     Status = STATUS_NOT_IMPLEMENTED;
1171 
1172     if (Vcb->readonly) {
1173         Status = STATUS_MEDIA_WRITE_PROTECTED;
1174         goto end;
1175     }
1176 
1177     if (Vcb->removing || Vcb->locked) {
1178         Status = STATUS_ACCESS_DENIED;
1179         goto end;
1180     }
1181 
1182     switch (IrpSp->Parameters.SetVolume.FsInformationClass) {
1183         case FileFsControlInformation:
1184             FIXME("STUB: FileFsControlInformation\n");
1185             break;
1186 
1187         case FileFsLabelInformation:
1188             TRACE("FileFsLabelInformation\n");
1189 
1190             Status = set_label(Vcb, Irp->AssociatedIrp.SystemBuffer);
1191             break;
1192 
1193         case FileFsObjectIdInformation:
1194             FIXME("STUB: FileFsObjectIdInformation\n");
1195             break;
1196 
1197         default:
1198             WARN("Unrecognized FsInformationClass 0x%x\n", IrpSp->Parameters.SetVolume.FsInformationClass);
1199             break;
1200     }
1201 
1202 end:
1203     Irp->IoStatus.Status = Status;
1204     Irp->IoStatus.Information = 0;
1205 
1206     TRACE("returning %08x\n", Status);
1207 
1208     IoCompleteRequest( Irp, IO_NO_INCREMENT );
1209 
1210     if (top_level)
1211         IoSetTopLevelIrp(NULL);
1212 
1213     FsRtlExitFileSystem();
1214 
1215     return Status;
1216 }
1217 
1218 static WCHAR* file_desc_fcb(_In_ fcb* fcb) {
1219     char s[60];
1220     NTSTATUS Status;
1221     UNICODE_STRING us;
1222     ANSI_STRING as;
1223 
1224     if (fcb->debug_desc)
1225         return fcb->debug_desc;
1226 
1227     if (fcb == fcb->Vcb->volume_fcb)
1228         return L"volume FCB";
1229 
1230     fcb->debug_desc = ExAllocatePoolWithTag(PagedPool, 60 * sizeof(WCHAR), ALLOC_TAG);
1231     if (!fcb->debug_desc)
1232         return L"(memory error)";
1233 
1234     // I know this is pretty hackish...
1235     // GCC doesn't like %llx in sprintf, and MSVC won't let us use swprintf
1236     // without the CRT, which breaks drivers.
1237 
1238     sprintf(s, "subvol %x, inode %x", (UINT32)fcb->subvol->id, (UINT32)fcb->inode);
1239 
1240     as.Buffer = s;
1241     as.Length = as.MaximumLength = (USHORT)strlen(s);
1242 
1243     us.Buffer = fcb->debug_desc;
1244     us.MaximumLength = 60 * sizeof(WCHAR);
1245     us.Length = 0;
1246 
1247     Status = RtlAnsiStringToUnicodeString(&us, &as, FALSE);
1248     if (!NT_SUCCESS(Status))
1249         return L"(RtlAnsiStringToUnicodeString error)";
1250 
1251     us.Buffer[us.Length / sizeof(WCHAR)] = 0;
1252 
1253     return fcb->debug_desc;
1254 }
1255 
1256 WCHAR* file_desc_fileref(_In_ file_ref* fileref) {
1257     NTSTATUS Status;
1258     UNICODE_STRING fn;
1259     ULONG reqlen;
1260 
1261     if (fileref->debug_desc)
1262         return fileref->debug_desc;
1263 
1264     fn.Length = fn.MaximumLength = 0;
1265     Status = fileref_get_filename(fileref, &fn, NULL, &reqlen);
1266     if (Status != STATUS_BUFFER_OVERFLOW)
1267         return L"ERROR";
1268 
1269     if (reqlen > 0xffff - sizeof(WCHAR))
1270         return L"(too long)";
1271 
1272     fileref->debug_desc = ExAllocatePoolWithTag(PagedPool, reqlen + sizeof(WCHAR), ALLOC_TAG);
1273     if (!fileref->debug_desc)
1274         return L"(memory error)";
1275 
1276     fn.Buffer = fileref->debug_desc;
1277     fn.Length = 0;
1278     fn.MaximumLength = (USHORT)(reqlen + sizeof(WCHAR));
1279 
1280     Status = fileref_get_filename(fileref, &fn, NULL, &reqlen);
1281     if (!NT_SUCCESS(Status)) {
1282         ExFreePool(fileref->debug_desc);
1283         fileref->debug_desc = NULL;
1284         return L"ERROR";
1285     }
1286 
1287     fileref->debug_desc[fn.Length / sizeof(WCHAR)] = 0;
1288 
1289     return fileref->debug_desc;
1290 }
1291 
1292 _Ret_z_
1293 WCHAR* file_desc(_In_ PFILE_OBJECT FileObject) {
1294     fcb* fcb = FileObject->FsContext;
1295     ccb* ccb = FileObject->FsContext2;
1296     file_ref* fileref = ccb ? ccb->fileref : NULL;
1297 
1298     if (fileref)
1299         return file_desc_fileref(fileref);
1300     else
1301         return file_desc_fcb(fcb);
1302 }
1303 
1304 void send_notification_fileref(_In_ file_ref* fileref, _In_ ULONG filter_match, _In_ ULONG action, _In_opt_ PUNICODE_STRING stream) {
1305     UNICODE_STRING fn;
1306     NTSTATUS Status;
1307     ULONG reqlen;
1308     USHORT name_offset;
1309     fcb* fcb = fileref->fcb;
1310 
1311     fn.Length = fn.MaximumLength = 0;
1312     Status = fileref_get_filename(fileref, &fn, NULL, &reqlen);
1313     if (Status != STATUS_BUFFER_OVERFLOW) {
1314         ERR("fileref_get_filename returned %08x\n", Status);
1315         return;
1316     }
1317 
1318     if (reqlen > 0xffff) {
1319         WARN("reqlen was too long for FsRtlNotifyFilterReportChange\n");
1320         return;
1321     }
1322 
1323     fn.Buffer = ExAllocatePoolWithTag(PagedPool, reqlen, ALLOC_TAG);
1324     if (!fn.Buffer) {
1325         ERR("out of memory\n");
1326         return;
1327     }
1328 
1329     fn.MaximumLength = (USHORT)reqlen;
1330     fn.Length = 0;
1331 
1332     Status = fileref_get_filename(fileref, &fn, &name_offset, &reqlen);
1333     if (!NT_SUCCESS(Status)) {
1334         ERR("fileref_get_filename returned %08x\n", Status);
1335         ExFreePool(fn.Buffer);
1336         return;
1337     }
1338 
1339     FsRtlNotifyFilterReportChange(fcb->Vcb->NotifySync, &fcb->Vcb->DirNotifyList, (PSTRING)&fn, name_offset,
1340                                   (PSTRING)stream, NULL, filter_match, action, NULL, NULL);
1341     ExFreePool(fn.Buffer);
1342 }
1343 
1344 void send_notification_fcb(_In_ file_ref* fileref, _In_ ULONG filter_match, _In_ ULONG action, _In_opt_ PUNICODE_STRING stream) {
1345     fcb* fcb = fileref->fcb;
1346     LIST_ENTRY* le;
1347     NTSTATUS Status;
1348 
1349     // no point looking for hardlinks if st_nlink == 1
1350     if (fileref->fcb->inode_item.st_nlink == 1) {
1351         send_notification_fileref(fileref, filter_match, action, stream);
1352         return;
1353     }
1354 
1355     acquire_fcb_lock_exclusive(fcb->Vcb);
1356 
1357     le = fcb->hardlinks.Flink;
1358     while (le != &fcb->hardlinks) {
1359         hardlink* hl = CONTAINING_RECORD(le, hardlink, list_entry);
1360         file_ref* parfr;
1361 
1362         Status = open_fileref_by_inode(fcb->Vcb, fcb->subvol, hl->parent, &parfr, NULL);
1363 
1364         if (!NT_SUCCESS(Status))
1365             ERR("open_fileref_by_inode returned %08x\n", Status);
1366         else if (!parfr->deleted) {
1367             UNICODE_STRING fn;
1368             ULONG pathlen;
1369 
1370             fn.Length = fn.MaximumLength = 0;
1371             Status = fileref_get_filename(parfr, &fn, NULL, &pathlen);
1372             if (Status != STATUS_BUFFER_OVERFLOW) {
1373                 ERR("fileref_get_filename returned %08x\n", Status);
1374                 free_fileref(fcb->Vcb, parfr);
1375                 break;
1376             }
1377 
1378             if (parfr != fcb->Vcb->root_fileref)
1379                 pathlen += sizeof(WCHAR);
1380 
1381             if (pathlen + hl->name.Length > 0xffff) {
1382                 WARN("pathlen + hl->name.Length was too long for FsRtlNotifyFilterReportChange\n");
1383                 free_fileref(fcb->Vcb, parfr);
1384                 break;
1385             }
1386 
1387             fn.MaximumLength = (USHORT)(pathlen + hl->name.Length);
1388             fn.Buffer = ExAllocatePoolWithTag(PagedPool, fn.MaximumLength, ALLOC_TAG);
1389             if (!fn.Buffer) {
1390                 ERR("out of memory\n");
1391                 free_fileref(fcb->Vcb, parfr);
1392                 break;
1393             }
1394 
1395             Status = fileref_get_filename(parfr, &fn, NULL, NULL);
1396             if (!NT_SUCCESS(Status)) {
1397                 ERR("fileref_get_filename returned %08x\n", Status);
1398                 free_fileref(fcb->Vcb, parfr);
1399                 ExFreePool(fn.Buffer);
1400                 break;
1401             }
1402 
1403             if (parfr != fcb->Vcb->root_fileref) {
1404                 fn.Buffer[(pathlen / sizeof(WCHAR)) - 1] = '\\';
1405                 fn.Length += sizeof(WCHAR);
1406             }
1407 
1408             RtlCopyMemory(&fn.Buffer[pathlen / sizeof(WCHAR)], hl->name.Buffer, hl->name.Length);
1409             fn.Length += hl->name.Length;
1410 
1411             FsRtlNotifyFilterReportChange(fcb->Vcb->NotifySync, &fcb->Vcb->DirNotifyList, (PSTRING)&fn, (USHORT)pathlen,
1412                                           (PSTRING)stream, NULL, filter_match, action, NULL, NULL);
1413 
1414             ExFreePool(fn.Buffer);
1415 
1416             free_fileref(fcb->Vcb, parfr);
1417         }
1418 
1419         le = le->Flink;
1420     }
1421 
1422     release_fcb_lock(fcb->Vcb);
1423 }
1424 
1425 void mark_fcb_dirty(_In_ fcb* fcb) {
1426     if (!fcb->dirty) {
1427 #ifdef DEBUG_FCB_REFCOUNTS
1428         LONG rc;
1429 #endif
1430         fcb->dirty = TRUE;
1431 
1432 #ifdef DEBUG_FCB_REFCOUNTS
1433         rc = InterlockedIncrement(&fcb->refcount);
1434         WARN("fcb %p: refcount now %i\n", fcb, rc);
1435 #else
1436         InterlockedIncrement(&fcb->refcount);
1437 #endif
1438 
1439         ExAcquireResourceExclusiveLite(&fcb->Vcb->dirty_fcbs_lock, TRUE);
1440         InsertTailList(&fcb->Vcb->dirty_fcbs, &fcb->list_entry_dirty);
1441         ExReleaseResourceLite(&fcb->Vcb->dirty_fcbs_lock);
1442     }
1443 
1444     fcb->Vcb->need_write = TRUE;
1445 }
1446 
1447 void mark_fileref_dirty(_In_ file_ref* fileref) {
1448     if (!fileref->dirty) {
1449         fileref->dirty = TRUE;
1450         increase_fileref_refcount(fileref);
1451 
1452         ExAcquireResourceExclusiveLite(&fileref->fcb->Vcb->dirty_filerefs_lock, TRUE);
1453         InsertTailList(&fileref->fcb->Vcb->dirty_filerefs, &fileref->list_entry_dirty);
1454         ExReleaseResourceLite(&fileref->fcb->Vcb->dirty_filerefs_lock);
1455     }
1456 
1457     fileref->fcb->Vcb->need_write = TRUE;
1458 }
1459 
1460 #ifdef DEBUG_FCB_REFCOUNTS
1461 void _free_fcb(_Requires_exclusive_lock_held_(_Curr_->fcb_lock) _In_ device_extension* Vcb, _Inout_ fcb* fcb, _In_ const char* func) {
1462 #else
1463 void free_fcb(_Requires_exclusive_lock_held_(_Curr_->fcb_lock) _In_ device_extension* Vcb, _Inout_ fcb* fcb) {
1464 #endif
1465     LONG rc;
1466 
1467     rc = InterlockedDecrement(&fcb->refcount);
1468 
1469 #ifdef DEBUG_FCB_REFCOUNTS
1470 #ifdef DEBUG_LONG_MESSAGES
1471     ERR("fcb %p: refcount now %i (subvol %llx, inode %llx)\n", fcb, rc, fcb->subvol ? fcb->subvol->id : 0, fcb->inode);
1472 #else
1473     ERR("fcb %p: refcount now %i (subvol %llx, inode %llx)\n", fcb, rc, fcb->subvol ? fcb->subvol->id : 0, fcb->inode);
1474 #endif
1475 #endif
1476 
1477     if (rc > 0)
1478         return;
1479 
1480     if (fcb->list_entry.Flink)
1481         RemoveEntryList(&fcb->list_entry);
1482 
1483     if (fcb->list_entry_all.Flink)
1484         RemoveEntryList(&fcb->list_entry_all);
1485 
1486     ExDeleteResourceLite(&fcb->nonpaged->resource);
1487     ExDeleteResourceLite(&fcb->nonpaged->paging_resource);
1488     ExDeleteResourceLite(&fcb->nonpaged->dir_children_lock);
1489 
1490     ExFreeToNPagedLookasideList(&Vcb->fcb_np_lookaside, fcb->nonpaged);
1491 
1492     if (fcb->sd)
1493         ExFreePool(fcb->sd);
1494 
1495     if (fcb->adsxattr.Buffer)
1496         ExFreePool(fcb->adsxattr.Buffer);
1497 
1498     if (fcb->reparse_xattr.Buffer)
1499         ExFreePool(fcb->reparse_xattr.Buffer);
1500 
1501     if (fcb->ea_xattr.Buffer)
1502         ExFreePool(fcb->ea_xattr.Buffer);
1503 
1504     if (fcb->adsdata.Buffer)
1505         ExFreePool(fcb->adsdata.Buffer);
1506 
1507     if (fcb->debug_desc)
1508         ExFreePool(fcb->debug_desc);
1509 
1510     while (!IsListEmpty(&fcb->extents)) {
1511         LIST_ENTRY* le = RemoveHeadList(&fcb->extents);
1512         extent* ext = CONTAINING_RECORD(le, extent, list_entry);
1513 
1514         if (ext->csum)
1515             ExFreePool(ext->csum);
1516 
1517         ExFreePool(ext);
1518     }
1519 
1520     while (!IsListEmpty(&fcb->hardlinks)) {
1521         LIST_ENTRY* le = RemoveHeadList(&fcb->hardlinks);
1522         hardlink* hl = CONTAINING_RECORD(le, hardlink, list_entry);
1523 
1524         if (hl->name.Buffer)
1525             ExFreePool(hl->name.Buffer);
1526 
1527         if (hl->utf8.Buffer)
1528             ExFreePool(hl->utf8.Buffer);
1529 
1530         ExFreePool(hl);
1531     }
1532 
1533     while (!IsListEmpty(&fcb->xattrs)) {
1534         xattr* xa = CONTAINING_RECORD(RemoveHeadList(&fcb->xattrs), xattr, list_entry);
1535 
1536         ExFreePool(xa);
1537     }
1538 
1539     while (!IsListEmpty(&fcb->dir_children_index)) {
1540         LIST_ENTRY* le = RemoveHeadList(&fcb->dir_children_index);
1541         dir_child* dc = CONTAINING_RECORD(le, dir_child, list_entry_index);
1542 
1543         ExFreePool(dc->utf8.Buffer);
1544         ExFreePool(dc->name.Buffer);
1545         ExFreePool(dc->name_uc.Buffer);
1546         ExFreePool(dc);
1547     }
1548 
1549     if (fcb->hash_ptrs)
1550         ExFreePool(fcb->hash_ptrs);
1551 
1552     if (fcb->hash_ptrs_uc)
1553         ExFreePool(fcb->hash_ptrs_uc);
1554 
1555     FsRtlUninitializeFileLock(&fcb->lock);
1556 
1557     if (fcb->pool_type == NonPagedPool)
1558         ExFreePool(fcb);
1559     else
1560         ExFreeToPagedLookasideList(&Vcb->fcb_lookaside, fcb);
1561 
1562 #ifdef DEBUG_FCB_REFCOUNTS
1563 #ifdef DEBUG_LONG_MESSAGES
1564     _debug_message(func, file, line, "freeing fcb %p\n", fcb);
1565 #else
1566     _debug_message(func, "freeing fcb %p\n", fcb);
1567 #endif
1568 #endif
1569 }
1570 
1571 void free_fileref(_Requires_exclusive_lock_held_(_Curr_->fcb_lock) _In_ device_extension* Vcb, _Inout_ file_ref* fr) {
1572     LONG rc;
1573 
1574     rc = InterlockedDecrement(&fr->refcount);
1575 
1576 #ifdef DEBUG_FCB_REFCOUNTS
1577     ERR("fileref %p: refcount now %i\n", fr, rc);
1578 #endif
1579 
1580 #ifdef _DEBUG
1581     if (rc < 0) {
1582         ERR("fileref %p: refcount now %i\n", fr, rc);
1583         int3;
1584     }
1585 #endif
1586 
1587     if (rc > 0)
1588         return;
1589 
1590     if (fr->parent)
1591         ExAcquireResourceExclusiveLite(&fr->parent->nonpaged->children_lock, TRUE);
1592 
1593     // FIXME - do we need a file_ref lock?
1594 
1595     // FIXME - do delete if needed
1596 
1597     if (fr->debug_desc)
1598         ExFreePool(fr->debug_desc);
1599 
1600     ExDeleteResourceLite(&fr->nonpaged->children_lock);
1601     ExDeleteResourceLite(&fr->nonpaged->fileref_lock);
1602 
1603     ExFreeToNPagedLookasideList(&Vcb->fileref_np_lookaside, fr->nonpaged);
1604 
1605     // FIXME - throw error if children not empty
1606 
1607     if (fr->fcb->fileref == fr)
1608         fr->fcb->fileref = NULL;
1609 
1610     if (fr->dc) {
1611         if (fr->fcb->ads)
1612             fr->dc->size = fr->fcb->adsdata.Length;
1613 
1614         fr->dc->fileref = NULL;
1615     }
1616 
1617     if (fr->list_entry.Flink)
1618         RemoveEntryList(&fr->list_entry);
1619 
1620     if (fr->parent) {
1621         ExReleaseResourceLite(&fr->parent->nonpaged->children_lock);
1622         free_fileref(Vcb, fr->parent);
1623     }
1624 
1625     free_fcb(Vcb, fr->fcb);
1626 
1627     ExFreeToPagedLookasideList(&Vcb->fileref_lookaside, fr);
1628 }
1629 
1630 static NTSTATUS close_file(_In_ PFILE_OBJECT FileObject, _In_ PIRP Irp) {
1631     fcb* fcb;
1632     ccb* ccb;
1633     file_ref* fileref = NULL;
1634     LONG open_files;
1635     device_extension* Vcb;
1636 
1637     UNUSED(Irp);
1638 
1639     TRACE("FileObject = %p\n", FileObject);
1640 
1641     fcb = FileObject->FsContext;
1642     if (!fcb) {
1643         TRACE("FCB was NULL, returning success\n");
1644         return STATUS_SUCCESS;
1645     }
1646 
1647     open_files = InterlockedDecrement(&fcb->Vcb->open_files);
1648 
1649     ccb = FileObject->FsContext2;
1650 
1651     TRACE("close called for %S (fcb == %p)\n", file_desc(FileObject), fcb);
1652 
1653     // FIXME - make sure notification gets sent if file is being deleted
1654 
1655     if (ccb) {
1656         if (ccb->query_string.Buffer)
1657             RtlFreeUnicodeString(&ccb->query_string);
1658 
1659         if (ccb->filename.Buffer)
1660             ExFreePool(ccb->filename.Buffer);
1661 
1662         // FIXME - use refcounts for fileref
1663         fileref = ccb->fileref;
1664 
1665         if (fcb->Vcb->running_sends > 0) {
1666             BOOL send_cancelled = FALSE;
1667 
1668             ExAcquireResourceExclusiveLite(&fcb->Vcb->send_load_lock, TRUE);
1669 
1670             if (ccb->send) {
1671                 ccb->send->cancelling = TRUE;
1672                 send_cancelled = TRUE;
1673                 KeSetEvent(&ccb->send->cleared_event, 0, FALSE);
1674             }
1675 
1676             ExReleaseResourceLite(&fcb->Vcb->send_load_lock);
1677 
1678             if (send_cancelled) {
1679                 while (ccb->send) {
1680                     ExAcquireResourceExclusiveLite(&fcb->Vcb->send_load_lock, TRUE);
1681                     ExReleaseResourceLite(&fcb->Vcb->send_load_lock);
1682                 }
1683             }
1684         }
1685 
1686         ExFreePool(ccb);
1687     }
1688 
1689     CcUninitializeCacheMap(FileObject, NULL, NULL);
1690 
1691     if (open_files == 0 && fcb->Vcb->removing) {
1692         uninit(fcb->Vcb, FALSE);
1693         return STATUS_SUCCESS;
1694     }
1695 
1696     if (!(fcb->Vcb->Vpb->Flags & VPB_MOUNTED))
1697         return STATUS_SUCCESS;
1698 
1699     Vcb = fcb->Vcb;
1700 
1701     acquire_fcb_lock_exclusive(Vcb);
1702 
1703     if (fileref)
1704         free_fileref(fcb->Vcb, fileref);
1705     else
1706         free_fcb(Vcb, fcb);
1707 
1708     release_fcb_lock(Vcb);
1709 
1710     return STATUS_SUCCESS;
1711 }
1712 
1713 void uninit(_In_ device_extension* Vcb, _In_ BOOL flush) {
1714     UINT64 i;
1715     NTSTATUS Status;
1716     LIST_ENTRY* le;
1717     LARGE_INTEGER time;
1718 
1719     if (!Vcb->removing) {
1720         ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE);
1721         Vcb->removing = TRUE;
1722         ExReleaseResourceLite(&Vcb->tree_lock);
1723     }
1724 
1725     RemoveEntryList(&Vcb->list_entry);
1726 
1727     if (Vcb->balance.thread) {
1728         Vcb->balance.paused = FALSE;
1729         Vcb->balance.stopping = TRUE;
1730         KeSetEvent(&Vcb->balance.event, 0, FALSE);
1731         KeWaitForSingleObject(&Vcb->balance.finished, Executive, KernelMode, FALSE, NULL);
1732     }
1733 
1734     if (Vcb->scrub.thread) {
1735         Vcb->scrub.paused = FALSE;
1736         Vcb->scrub.stopping = TRUE;
1737         KeSetEvent(&Vcb->scrub.event, 0, FALSE);
1738         KeWaitForSingleObject(&Vcb->scrub.finished, Executive, KernelMode, FALSE, NULL);
1739     }
1740 
1741     if (Vcb->running_sends != 0) {
1742         BOOL send_cancelled = FALSE;
1743 
1744         ExAcquireResourceExclusiveLite(&Vcb->send_load_lock, TRUE);
1745 
1746         le = Vcb->send_ops.Flink;
1747         while (le != &Vcb->send_ops) {
1748             send_info* send = CONTAINING_RECORD(le, send_info, list_entry);
1749 
1750             if (!send->cancelling) {
1751                 send->cancelling = TRUE;
1752                 send_cancelled = TRUE;
1753                 send->ccb = NULL;
1754                 KeSetEvent(&send->cleared_event, 0, FALSE);
1755             }
1756 
1757             le = le->Flink;
1758         }
1759 
1760         ExReleaseResourceLite(&Vcb->send_load_lock);
1761 
1762         if (send_cancelled) {
1763             while (Vcb->running_sends != 0) {
1764                 ExAcquireResourceExclusiveLite(&Vcb->send_load_lock, TRUE);
1765                 ExReleaseResourceLite(&Vcb->send_load_lock);
1766             }
1767         }
1768     }
1769 
1770     Status = registry_mark_volume_unmounted(&Vcb->superblock.uuid);
1771     if (!NT_SUCCESS(Status) && Status != STATUS_TOO_LATE)
1772         WARN("registry_mark_volume_unmounted returned %08x\n", Status);
1773 
1774     if (flush) {
1775         ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE);
1776 
1777         if (Vcb->need_write && !Vcb->readonly) {
1778             Status = do_write(Vcb, NULL);
1779             if (!NT_SUCCESS(Status))
1780                 ERR("do_write returned %08x\n", Status);
1781         }
1782 
1783         free_trees(Vcb);
1784 
1785         ExReleaseResourceLite(&Vcb->tree_lock);
1786     }
1787 
1788     for (i = 0; i < Vcb->calcthreads.num_threads; i++) {
1789         Vcb->calcthreads.threads[i].quit = TRUE;
1790     }
1791 
1792     KeSetEvent(&Vcb->calcthreads.event, 0, FALSE);
1793 
1794     for (i = 0; i < Vcb->calcthreads.num_threads; i++) {
1795         KeWaitForSingleObject(&Vcb->calcthreads.threads[i].finished, Executive, KernelMode, FALSE, NULL);
1796 
1797         ZwClose(Vcb->calcthreads.threads[i].handle);
1798     }
1799 
1800     ExDeleteResourceLite(&Vcb->calcthreads.lock);
1801     ExFreePool(Vcb->calcthreads.threads);
1802 
1803     time.QuadPart = 0;
1804     KeSetTimer(&Vcb->flush_thread_timer, time, NULL); // trigger the timer early
1805     KeWaitForSingleObject(&Vcb->flush_thread_finished, Executive, KernelMode, FALSE, NULL);
1806 
1807     acquire_fcb_lock_exclusive(Vcb);
1808     free_fcb(Vcb, Vcb->volume_fcb);
1809     free_fcb(Vcb, Vcb->dummy_fcb);
1810     release_fcb_lock(Vcb);
1811 
1812     if (Vcb->root_file)
1813         ObDereferenceObject(Vcb->root_file);
1814 
1815     le = Vcb->chunks.Flink;
1816     while (le != &Vcb->chunks) {
1817         chunk* c = CONTAINING_RECORD(le, chunk, list_entry);
1818 
1819         if (c->cache) {
1820             acquire_fcb_lock_exclusive(Vcb);
1821             free_fcb(Vcb, c->cache);
1822             release_fcb_lock(Vcb);
1823             c->cache = NULL;
1824         }
1825 
1826         le = le->Flink;
1827     }
1828 
1829     while (!IsListEmpty(&Vcb->roots)) {
1830         root* r = CONTAINING_RECORD(RemoveHeadList(&Vcb->roots), root, list_entry);
1831 
1832         ExDeleteResourceLite(&r->nonpaged->load_tree_lock);
1833         ExFreePool(r->nonpaged);
1834         ExFreePool(r);
1835     }
1836 
1837     while (!IsListEmpty(&Vcb->chunks)) {
1838         chunk* c = CONTAINING_RECORD(RemoveHeadList(&Vcb->chunks), chunk, list_entry);
1839 
1840         while (!IsListEmpty(&c->space)) {
1841             LIST_ENTRY* le2 = RemoveHeadList(&c->space);
1842             space* s = CONTAINING_RECORD(le2, space, list_entry);
1843 
1844             ExFreePool(s);
1845         }
1846 
1847         while (!IsListEmpty(&c->deleting)) {
1848             LIST_ENTRY* le2 = RemoveHeadList(&c->deleting);
1849             space* s = CONTAINING_RECORD(le2, space, list_entry);
1850 
1851             ExFreePool(s);
1852         }
1853 
1854         if (c->devices)
1855             ExFreePool(c->devices);
1856 
1857         if (c->cache) {
1858             acquire_fcb_lock_exclusive(Vcb);
1859             free_fcb(Vcb, c->cache);
1860             release_fcb_lock(Vcb);
1861         }
1862 
1863         ExDeleteResourceLite(&c->range_locks_lock);
1864         ExDeleteResourceLite(&c->partial_stripes_lock);
1865         ExDeleteResourceLite(&c->lock);
1866         ExDeleteResourceLite(&c->changed_extents_lock);
1867 
1868         ExFreePool(c->chunk_item);
1869         ExFreePool(c);
1870     }
1871 
1872     // FIXME - free any open fcbs?
1873 
1874     while (!IsListEmpty(&Vcb->devices)) {
1875         device* dev = CONTAINING_RECORD(RemoveHeadList(&Vcb->devices), device, list_entry);
1876 
1877         while (!IsListEmpty(&dev->space)) {
1878             LIST_ENTRY* le2 = RemoveHeadList(&dev->space);
1879             space* s = CONTAINING_RECORD(le2, space, list_entry);
1880 
1881             ExFreePool(s);
1882         }
1883 
1884         ExFreePool(dev);
1885     }
1886 
1887     ExAcquireResourceExclusiveLite(&Vcb->scrub.stats_lock, TRUE);
1888     while (!IsListEmpty(&Vcb->scrub.errors)) {
1889         scrub_error* err = CONTAINING_RECORD(RemoveHeadList(&Vcb->scrub.errors), scrub_error, list_entry);
1890 
1891         ExFreePool(err);
1892     }
1893     ExReleaseResourceLite(&Vcb->scrub.stats_lock);
1894 
1895     ExDeleteResourceLite(&Vcb->fcb_lock);
1896     ExDeleteResourceLite(&Vcb->load_lock);
1897     ExDeleteResourceLite(&Vcb->tree_lock);
1898     ExDeleteResourceLite(&Vcb->chunk_lock);
1899     ExDeleteResourceLite(&Vcb->dirty_fcbs_lock);
1900     ExDeleteResourceLite(&Vcb->dirty_filerefs_lock);
1901     ExDeleteResourceLite(&Vcb->dirty_subvols_lock);
1902     ExDeleteResourceLite(&Vcb->scrub.stats_lock);
1903     ExDeleteResourceLite(&Vcb->send_load_lock);
1904 
1905     ExDeletePagedLookasideList(&Vcb->tree_data_lookaside);
1906     ExDeletePagedLookasideList(&Vcb->traverse_ptr_lookaside);
1907     ExDeletePagedLookasideList(&Vcb->batch_item_lookaside);
1908     ExDeletePagedLookasideList(&Vcb->fileref_lookaside);
1909     ExDeletePagedLookasideList(&Vcb->fcb_lookaside);
1910     ExDeletePagedLookasideList(&Vcb->name_bit_lookaside);
1911     ExDeleteNPagedLookasideList(&Vcb->range_lock_lookaside);
1912     ExDeleteNPagedLookasideList(&Vcb->fileref_np_lookaside);
1913     ExDeleteNPagedLookasideList(&Vcb->fcb_np_lookaside);
1914 
1915     ZwClose(Vcb->flush_thread_handle);
1916 }
1917 
1918 NTSTATUS delete_fileref(_In_ file_ref* fileref, _In_opt_ PFILE_OBJECT FileObject, _In_opt_ PIRP Irp, _In_ LIST_ENTRY* rollback) {
1919     LARGE_INTEGER newlength, time;
1920     BTRFS_TIME now;
1921     NTSTATUS Status;
1922     ULONG utf8len = 0;
1923 
1924     KeQuerySystemTime(&time);
1925     win_time_to_unix(time, &now);
1926 
1927     ExAcquireResourceExclusiveLite(fileref->fcb->Header.Resource, TRUE);
1928 
1929     if (fileref->deleted) {
1930         ExReleaseResourceLite(fileref->fcb->Header.Resource);
1931         return STATUS_SUCCESS;
1932     }
1933 
1934     if (fileref->fcb->subvol->send_ops > 0) {
1935         ExReleaseResourceLite(fileref->fcb->Header.Resource);
1936         return STATUS_ACCESS_DENIED;
1937     }
1938 
1939     fileref->deleted = TRUE;
1940     mark_fileref_dirty(fileref);
1941 
1942     // delete INODE_ITEM (0x1)
1943 
1944     TRACE("nlink = %u\n", fileref->fcb->inode_item.st_nlink);
1945 
1946     if (!fileref->fcb->ads) {
1947         if (fileref->parent->fcb->subvol == fileref->fcb->subvol) {
1948             LIST_ENTRY* le;
1949 
1950             mark_fcb_dirty(fileref->fcb);
1951 
1952             fileref->fcb->inode_item_changed = TRUE;
1953 
1954             if (fileref->fcb->inode_item.st_nlink > 1) {
1955                 fileref->fcb->inode_item.st_nlink--;
1956                 fileref->fcb->inode_item.transid = fileref->fcb->Vcb->superblock.generation;
1957                 fileref->fcb->inode_item.sequence++;
1958                 fileref->fcb->inode_item.st_ctime = now;
1959             } else {
1960                 // excise extents
1961 
1962                 if (fileref->fcb->type != BTRFS_TYPE_DIRECTORY && fileref->fcb->inode_item.st_size > 0) {
1963                     Status = excise_extents(fileref->fcb->Vcb, fileref->fcb, 0, sector_align(fileref->fcb->inode_item.st_size, fileref->fcb->Vcb->superblock.sector_size), Irp, rollback);
1964                     if (!NT_SUCCESS(Status)) {
1965                         ERR("excise_extents returned %08x\n", Status);
1966                         ExReleaseResourceLite(fileref->fcb->Header.Resource);
1967                         return Status;
1968                     }
1969                 }
1970 
1971                 fileref->fcb->Header.AllocationSize.QuadPart = 0;
1972                 fileref->fcb->Header.FileSize.QuadPart = 0;
1973                 fileref->fcb->Header.ValidDataLength.QuadPart = 0;
1974 
1975                 if (FileObject) {
1976                     CC_FILE_SIZES ccfs;
1977 
1978                     ccfs.AllocationSize = fileref->fcb->Header.AllocationSize;
1979                     ccfs.FileSize = fileref->fcb->Header.FileSize;
1980                     ccfs.ValidDataLength = fileref->fcb->Header.ValidDataLength;
1981 
1982                     Status = STATUS_SUCCESS;
1983 
1984                     _SEH2_TRY {
1985                         CcSetFileSizes(FileObject, &ccfs);
1986                     } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
1987                         Status = _SEH2_GetExceptionCode();
1988                     } _SEH2_END;
1989 
1990                     if (!NT_SUCCESS(Status)) {
1991                         ERR("CcSetFileSizes threw exception %08x\n", Status);
1992                         ExReleaseResourceLite(fileref->fcb->Header.Resource);
1993                         return Status;
1994                     }
1995                 }
1996 
1997                 fileref->fcb->deleted = TRUE;
1998 
1999                 le = fileref->children.Flink;
2000                 while (le != &fileref->children) {
2001                     file_ref* fr2 = CONTAINING_RECORD(le, file_ref, list_entry);
2002 
2003                     if (fr2->fcb->ads) {
2004                         fr2->fcb->deleted = TRUE;
2005                         mark_fcb_dirty(fr2->fcb);
2006                     }
2007 
2008                     le = le->Flink;
2009                 }
2010             }
2011 
2012             if (fileref->dc) {
2013                 le = fileref->fcb->hardlinks.Flink;
2014                 while (le != &fileref->fcb->hardlinks) {
2015                     hardlink* hl = CONTAINING_RECORD(le, hardlink, list_entry);
2016 
2017                     if (hl->parent == fileref->parent->fcb->inode && hl->index == fileref->dc->index) {
2018                         RemoveEntryList(&hl->list_entry);
2019 
2020                         if (hl->name.Buffer)
2021                             ExFreePool(hl->name.Buffer);
2022 
2023                         if (hl->utf8.Buffer)
2024                             ExFreePool(hl->utf8.Buffer);
2025 
2026                         ExFreePool(hl);
2027                         break;
2028                     }
2029 
2030                     le = le->Flink;
2031                 }
2032             }
2033         } else if (fileref->fcb->subvol->parent == fileref->parent->fcb->subvol->id) { // valid subvolume
2034             if (fileref->fcb->subvol->root_item.num_references > 1) {
2035                 fileref->fcb->subvol->root_item.num_references--;
2036 
2037                 mark_fcb_dirty(fileref->fcb); // so ROOT_ITEM gets updated
2038             } else {
2039                 LIST_ENTRY* le;
2040 
2041                 // FIXME - we need a lock here
2042 
2043                 RemoveEntryList(&fileref->fcb->subvol->list_entry);
2044 
2045                 InsertTailList(&fileref->fcb->Vcb->drop_roots, &fileref->fcb->subvol->list_entry);
2046 
2047                 le = fileref->children.Flink;
2048                 while (le != &fileref->children) {
2049                     file_ref* fr2 = CONTAINING_RECORD(le, file_ref, list_entry);
2050 
2051                     if (fr2->fcb->ads) {
2052                         fr2->fcb->deleted = TRUE;
2053                         mark_fcb_dirty(fr2->fcb);
2054                     }
2055 
2056                     le = le->Flink;
2057                 }
2058             }
2059         }
2060     } else {
2061         fileref->fcb->deleted = TRUE;
2062         mark_fcb_dirty(fileref->fcb);
2063     }
2064 
2065     // remove dir_child from parent
2066 
2067     if (fileref->dc) {
2068         TRACE("delete file %.*S\n", fileref->dc->name.Length / sizeof(WCHAR), fileref->dc->name.Buffer);
2069 
2070         ExAcquireResourceExclusiveLite(&fileref->parent->fcb->nonpaged->dir_children_lock, TRUE);
2071         RemoveEntryList(&fileref->dc->list_entry_index);
2072 
2073         if (!fileref->fcb->ads)
2074             remove_dir_child_from_hash_lists(fileref->parent->fcb, fileref->dc);
2075 
2076         ExReleaseResourceLite(&fileref->parent->fcb->nonpaged->dir_children_lock);
2077 
2078         if (!fileref->oldutf8.Buffer)
2079             fileref->oldutf8 = fileref->dc->utf8;
2080         else
2081             ExFreePool(fileref->dc->utf8.Buffer);
2082 
2083         utf8len = fileref->dc->utf8.Length;
2084 
2085         fileref->oldindex = fileref->dc->index;
2086 
2087         ExFreePool(fileref->dc->name.Buffer);
2088         ExFreePool(fileref->dc->name_uc.Buffer);
2089         ExFreePool(fileref->dc);
2090 
2091         fileref->dc = NULL;
2092     }
2093 
2094     // update INODE_ITEM of parent
2095 
2096     ExAcquireResourceExclusiveLite(fileref->parent->fcb->Header.Resource, TRUE);
2097 
2098     fileref->parent->fcb->inode_item.transid = fileref->fcb->Vcb->superblock.generation;
2099     fileref->parent->fcb->inode_item.sequence++;
2100     fileref->parent->fcb->inode_item.st_ctime = now;
2101 
2102     if (!fileref->fcb->ads) {
2103         TRACE("fileref->parent->fcb->inode_item.st_size (inode %llx) was %llx\n", fileref->parent->fcb->inode, fileref->parent->fcb->inode_item.st_size);
2104         fileref->parent->fcb->inode_item.st_size -= utf8len * 2;
2105         TRACE("fileref->parent->fcb->inode_item.st_size (inode %llx) now %llx\n", fileref->parent->fcb->inode, fileref->parent->fcb->inode_item.st_size);
2106         fileref->parent->fcb->inode_item.st_mtime = now;
2107     }
2108 
2109     fileref->parent->fcb->inode_item_changed = TRUE;
2110     ExReleaseResourceLite(fileref->parent->fcb->Header.Resource);
2111 
2112     if (!fileref->fcb->ads && fileref->parent->dc)
2113         send_notification_fcb(fileref->parent, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED, NULL);
2114 
2115     mark_fcb_dirty(fileref->parent->fcb);
2116 
2117     fileref->fcb->subvol->root_item.ctransid = fileref->fcb->Vcb->superblock.generation;
2118     fileref->fcb->subvol->root_item.ctime = now;
2119 
2120     newlength.QuadPart = 0;
2121 
2122     if (FileObject && !CcUninitializeCacheMap(FileObject, &newlength, NULL))
2123         TRACE("CcUninitializeCacheMap failed\n");
2124 
2125     ExReleaseResourceLite(fileref->fcb->Header.Resource);
2126 
2127     return STATUS_SUCCESS;
2128 }
2129 
2130 _Dispatch_type_(IRP_MJ_CLEANUP)
2131 _Function_class_(DRIVER_DISPATCH)
2132 static NTSTATUS drv_cleanup(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) {
2133     NTSTATUS Status;
2134     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
2135     PFILE_OBJECT FileObject = IrpSp->FileObject;
2136     device_extension* Vcb = DeviceObject->DeviceExtension;
2137     fcb* fcb = FileObject->FsContext;
2138     BOOL top_level;
2139 
2140     FsRtlEnterFileSystem();
2141 
2142     TRACE("cleanup\n");
2143 
2144     top_level = is_top_level(Irp);
2145 
2146     if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
2147         Status = vol_cleanup(DeviceObject, Irp);
2148         goto exit;
2149     } else if (DeviceObject == master_devobj) {
2150         TRACE("closing file system\n");
2151         Status = STATUS_SUCCESS;
2152         goto exit;
2153     } else if (!Vcb || Vcb->type != VCB_TYPE_FS) {
2154         Status = STATUS_INVALID_PARAMETER;
2155         goto exit;
2156     }
2157 
2158     if (FileObject->Flags & FO_CLEANUP_COMPLETE) {
2159         TRACE("FileObject %p already cleaned up\n", FileObject);
2160         Status = STATUS_SUCCESS;
2161         goto exit;
2162     }
2163 
2164     if (!fcb) {
2165         ERR("fcb was NULL\n");
2166         Status = STATUS_INVALID_PARAMETER;
2167         goto exit;
2168     }
2169 
2170     // We have to use the pointer to Vcb stored in the fcb, as we can receive cleanup
2171     // messages belonging to other devices.
2172 
2173     if (FileObject && FileObject->FsContext) {
2174         LONG oc;
2175         ccb* ccb;
2176         file_ref* fileref;
2177         BOOL locked = TRUE;
2178 
2179         ccb = FileObject->FsContext2;
2180         fileref = ccb ? ccb->fileref : NULL;
2181 
2182         TRACE("cleanup called for FileObject %p\n", FileObject);
2183         TRACE("fileref %p (%S), refcount = %u, open_count = %u\n", fileref, file_desc(FileObject), fileref ? fileref->refcount : 0, fileref ? fileref->open_count : 0);
2184 
2185         ExAcquireResourceSharedLite(&fcb->Vcb->tree_lock, TRUE);
2186 
2187         ExAcquireResourceExclusiveLite(fcb->Header.Resource, TRUE);
2188 
2189         IoRemoveShareAccess(FileObject, &fcb->share_access);
2190 
2191         if (ccb)
2192             FsRtlNotifyCleanup(fcb->Vcb->NotifySync, &fcb->Vcb->DirNotifyList, ccb);
2193 
2194         if (fileref) {
2195             oc = InterlockedDecrement(&fileref->open_count);
2196 #ifdef DEBUG_FCB_REFCOUNTS
2197             ERR("fileref %p: open_count now %i\n", fileref, oc);
2198 #endif
2199         }
2200 
2201         if (ccb && ccb->options & FILE_DELETE_ON_CLOSE && fileref)
2202             fileref->delete_on_close = TRUE;
2203 
2204         if (fileref && fileref->delete_on_close && fcb->type == BTRFS_TYPE_DIRECTORY && fcb->inode_item.st_size > 0 && fcb != fcb->Vcb->dummy_fcb)
2205             fileref->delete_on_close = FALSE;
2206 
2207         if (fcb->Vcb->locked && fcb->Vcb->locked_fileobj == FileObject) {
2208             TRACE("unlocking volume\n");
2209             do_unlock_volume(fcb->Vcb);
2210             FsRtlNotifyVolumeEvent(FileObject, FSRTL_VOLUME_UNLOCK);
2211         }
2212 
2213         if (ccb && ccb->reserving) {
2214             fcb->subvol->reserved = NULL;
2215             ccb->reserving = FALSE;
2216             // FIXME - flush all of subvol's fcbs
2217         }
2218 
2219         if (fileref && oc == 0) {
2220             if (!fcb->Vcb->removing) {
2221                 if (fileref && fileref->delete_on_close && fileref != fcb->Vcb->root_fileref && fcb != fcb->Vcb->volume_fcb) {
2222                     LIST_ENTRY rollback;
2223 
2224                     InitializeListHead(&rollback);
2225 
2226                     if (!fileref->fcb->ads || fileref->dc) {
2227                         if (fileref->fcb->ads) {
2228                             send_notification_fileref(fileref->parent, fcb->type == BTRFS_TYPE_DIRECTORY ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME,
2229                                                       FILE_ACTION_REMOVED, &fileref->dc->name);
2230                         } else
2231                             send_notification_fileref(fileref, fcb->type == BTRFS_TYPE_DIRECTORY ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_REMOVED, NULL);
2232                     }
2233 
2234                     ExReleaseResourceLite(fcb->Header.Resource);
2235                     locked = FALSE;
2236 
2237                     // fcb_lock needs to be acquired before fcb->Header.Resource
2238                     acquire_fcb_lock_exclusive(fcb->Vcb);
2239 
2240                     Status = delete_fileref(fileref, FileObject, Irp, &rollback);
2241                     if (!NT_SUCCESS(Status)) {
2242                         ERR("delete_fileref returned %08x\n", Status);
2243                         do_rollback(fcb->Vcb, &rollback);
2244                         release_fcb_lock(fcb->Vcb);
2245                         ExReleaseResourceLite(&fcb->Vcb->tree_lock);
2246                         goto exit;
2247                     }
2248 
2249                     release_fcb_lock(fcb->Vcb);
2250 
2251                     locked = FALSE;
2252 
2253                     clear_rollback(&rollback);
2254                 } else if (FileObject->Flags & FO_CACHE_SUPPORTED && fcb->nonpaged->segment_object.DataSectionObject) {
2255                     IO_STATUS_BLOCK iosb;
2256                     CcFlushCache(FileObject->SectionObjectPointer, NULL, 0, &iosb);
2257 
2258                     if (!NT_SUCCESS(iosb.Status)) {
2259                         ERR("CcFlushCache returned %08x\n", iosb.Status);
2260                     }
2261 
2262                     if (!ExIsResourceAcquiredSharedLite(fcb->Header.PagingIoResource)) {
2263                         ExAcquireResourceExclusiveLite(fcb->Header.PagingIoResource, TRUE);
2264                         ExReleaseResourceLite(fcb->Header.PagingIoResource);
2265                     }
2266 
2267                     CcPurgeCacheSection(&fcb->nonpaged->segment_object, NULL, 0, FALSE);
2268 
2269                     TRACE("flushed cache on close (FileObject = %p, fcb = %p, AllocationSize = %llx, FileSize = %llx, ValidDataLength = %llx)\n",
2270                         FileObject, fcb, fcb->Header.AllocationSize.QuadPart, fcb->Header.FileSize.QuadPart, fcb->Header.ValidDataLength.QuadPart);
2271                 }
2272             }
2273 
2274             if (fcb->Vcb && fcb != fcb->Vcb->volume_fcb)
2275                 CcUninitializeCacheMap(FileObject, NULL, NULL);
2276         }
2277 
2278         if (locked)
2279             ExReleaseResourceLite(fcb->Header.Resource);
2280 
2281         ExReleaseResourceLite(&fcb->Vcb->tree_lock);
2282 
2283         FileObject->Flags |= FO_CLEANUP_COMPLETE;
2284     }
2285 
2286     Status = STATUS_SUCCESS;
2287 
2288 exit:
2289     TRACE("returning %08x\n", Status);
2290 
2291     Irp->IoStatus.Status = Status;
2292     Irp->IoStatus.Information = 0;
2293 
2294     IoCompleteRequest(Irp, IO_NO_INCREMENT);
2295 
2296     if (top_level)
2297         IoSetTopLevelIrp(NULL);
2298 
2299     FsRtlExitFileSystem();
2300 
2301     return Status;
2302 }
2303 
2304 _Success_(return)
2305 BOOL get_file_attributes_from_xattr(_In_reads_bytes_(len) char* val, _In_ UINT16 len, _Out_ ULONG* atts) {
2306     if (len > 2 && val[0] == '0' && val[1] == 'x') {
2307         int i;
2308         ULONG dosnum = 0;
2309 
2310         for (i = 2; i < len; i++) {
2311             dosnum *= 0x10;
2312 
2313             if (val[i] >= '0' && val[i] <= '9')
2314                 dosnum |= val[i] - '0';
2315             else if (val[i] >= 'a' && val[i] <= 'f')
2316                 dosnum |= val[i] + 10 - 'a';
2317             else if (val[i] >= 'A' && val[i] <= 'F')
2318                 dosnum |= val[i] + 10 - 'a';
2319         }
2320 
2321         TRACE("DOSATTRIB: %08x\n", dosnum);
2322 
2323         *atts = dosnum;
2324 
2325         return TRUE;
2326     }
2327 
2328     return FALSE;
2329 }
2330 
2331 ULONG get_file_attributes(_In_ _Requires_lock_held_(_Curr_->tree_lock) device_extension* Vcb, _In_ root* r, _In_ UINT64 inode,
2332                           _In_ UINT8 type, _In_ BOOL dotfile, _In_ BOOL ignore_xa, _In_opt_ PIRP Irp) {
2333     ULONG att;
2334     char* eaval;
2335     UINT16 ealen;
2336 
2337     if (!ignore_xa && get_xattr(Vcb, r, inode, EA_DOSATTRIB, EA_DOSATTRIB_HASH, (UINT8**)&eaval, &ealen, Irp)) {
2338         ULONG dosnum = 0;
2339 
2340         if (get_file_attributes_from_xattr(eaval, ealen, &dosnum)) {
2341             ExFreePool(eaval);
2342 
2343             if (type == BTRFS_TYPE_DIRECTORY)
2344                 dosnum |= FILE_ATTRIBUTE_DIRECTORY;
2345             else if (type == BTRFS_TYPE_SYMLINK)
2346                 dosnum |= FILE_ATTRIBUTE_REPARSE_POINT;
2347 
2348             if (type != BTRFS_TYPE_DIRECTORY)
2349                 dosnum &= ~FILE_ATTRIBUTE_DIRECTORY;
2350 
2351             if (inode == SUBVOL_ROOT_INODE) {
2352                 if (r->root_item.flags & BTRFS_SUBVOL_READONLY)
2353                     dosnum |= FILE_ATTRIBUTE_READONLY;
2354                 else
2355                     dosnum &= ~FILE_ATTRIBUTE_READONLY;
2356             }
2357 
2358             return dosnum;
2359         }
2360 
2361         ExFreePool(eaval);
2362     }
2363 
2364     switch (type) {
2365         case BTRFS_TYPE_DIRECTORY:
2366             att = FILE_ATTRIBUTE_DIRECTORY;
2367             break;
2368 
2369         case BTRFS_TYPE_SYMLINK:
2370             att = FILE_ATTRIBUTE_REPARSE_POINT;
2371             break;
2372 
2373         default:
2374             att = 0;
2375             break;
2376     }
2377 
2378     if (dotfile) {
2379         att |= FILE_ATTRIBUTE_HIDDEN;
2380     }
2381 
2382     att |= FILE_ATTRIBUTE_ARCHIVE;
2383 
2384     if (inode == SUBVOL_ROOT_INODE) {
2385         if (r->root_item.flags & BTRFS_SUBVOL_READONLY)
2386             att |= FILE_ATTRIBUTE_READONLY;
2387         else
2388             att &= ~FILE_ATTRIBUTE_READONLY;
2389     }
2390 
2391     // FIXME - get READONLY from ii->st_mode
2392     // FIXME - return SYSTEM for block/char devices?
2393 
2394     if (att == 0)
2395         att = FILE_ATTRIBUTE_NORMAL;
2396 
2397     return att;
2398 }
2399 
2400 NTSTATUS sync_read_phys(_In_ PDEVICE_OBJECT DeviceObject, _In_ UINT64 StartingOffset, _In_ ULONG Length,
2401                         _Out_writes_bytes_(Length) PUCHAR Buffer, _In_ BOOL override) {
2402     IO_STATUS_BLOCK IoStatus;
2403     LARGE_INTEGER Offset;
2404     PIRP Irp;
2405     PIO_STACK_LOCATION IrpSp;
2406     NTSTATUS Status;
2407     read_context context;
2408 
2409     num_reads++;
2410 
2411     RtlZeroMemory(&context, sizeof(read_context));
2412     KeInitializeEvent(&context.Event, NotificationEvent, FALSE);
2413 
2414     Offset.QuadPart = (LONGLONG)StartingOffset;
2415 
2416     Irp = IoAllocateIrp(DeviceObject->StackSize, FALSE);
2417 
2418     if (!Irp) {
2419         ERR("IoAllocateIrp failed\n");
2420         return STATUS_INSUFFICIENT_RESOURCES;
2421     }
2422 
2423     Irp->Flags |= IRP_NOCACHE;
2424     IrpSp = IoGetNextIrpStackLocation(Irp);
2425     IrpSp->MajorFunction = IRP_MJ_READ;
2426 
2427     if (override)
2428         IrpSp->Flags |= SL_OVERRIDE_VERIFY_VOLUME;
2429 
2430     if (DeviceObject->Flags & DO_BUFFERED_IO) {
2431         Irp->AssociatedIrp.SystemBuffer = ExAllocatePoolWithTag(NonPagedPool, Length, ALLOC_TAG);
2432         if (!Irp->AssociatedIrp.SystemBuffer) {
2433             ERR("out of memory\n");
2434             Status = STATUS_INSUFFICIENT_RESOURCES;
2435             goto exit;
2436         }
2437 
2438         Irp->Flags |= IRP_BUFFERED_IO | IRP_DEALLOCATE_BUFFER | IRP_INPUT_OPERATION;
2439 
2440         Irp->UserBuffer = Buffer;
2441     } else if (DeviceObject->Flags & DO_DIRECT_IO) {
2442         Irp->MdlAddress = IoAllocateMdl(Buffer, Length, FALSE, FALSE, NULL);
2443         if (!Irp->MdlAddress) {
2444             ERR("IoAllocateMdl failed\n");
2445             Status = STATUS_INSUFFICIENT_RESOURCES;
2446             goto exit;
2447         }
2448 
2449         Status = STATUS_SUCCESS;
2450 
2451         _SEH2_TRY {
2452             MmProbeAndLockPages(Irp->MdlAddress, KernelMode, IoWriteAccess);
2453         } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
2454             Status = _SEH2_GetExceptionCode();
2455         } _SEH2_END;
2456 
2457         if (!NT_SUCCESS(Status)) {
2458             ERR("MmProbeAndLockPages threw exception %08x\n", Status);
2459             IoFreeMdl(Irp->MdlAddress);
2460             goto exit;
2461         }
2462     } else
2463         Irp->UserBuffer = Buffer;
2464 
2465     IrpSp->Parameters.Read.Length = Length;
2466     IrpSp->Parameters.Read.ByteOffset = Offset;
2467 
2468     Irp->UserIosb = &IoStatus;
2469 
2470     Irp->UserEvent = &context.Event;
2471 
2472     IoSetCompletionRoutine(Irp, read_completion, &context, TRUE, TRUE, TRUE);
2473 
2474     Status = IoCallDriver(DeviceObject, Irp);
2475 
2476     if (Status == STATUS_PENDING) {
2477         KeWaitForSingleObject(&context.Event, Executive, KernelMode, FALSE, NULL);
2478         Status = context.iosb.Status;
2479     }
2480 
2481     if (DeviceObject->Flags & DO_DIRECT_IO) {
2482         MmUnlockPages(Irp->MdlAddress);
2483         IoFreeMdl(Irp->MdlAddress);
2484     }
2485 
2486 exit:
2487     IoFreeIrp(Irp);
2488 
2489     return Status;
2490 }
2491 
2492 static NTSTATUS read_superblock(_In_ device_extension* Vcb, _In_ PDEVICE_OBJECT device, _In_ UINT64 length) {
2493     NTSTATUS Status;
2494     superblock* sb;
2495     ULONG i, to_read;
2496     UINT8 valid_superblocks;
2497 
2498     to_read = device->SectorSize == 0 ? sizeof(superblock) : (ULONG)sector_align(sizeof(superblock), device->SectorSize);
2499 
2500     sb = ExAllocatePoolWithTag(NonPagedPool, to_read, ALLOC_TAG);
2501     if (!sb) {
2502         ERR("out of memory\n");
2503         return STATUS_INSUFFICIENT_RESOURCES;
2504     }
2505 
2506     if (superblock_addrs[0] + to_read > length) {
2507         WARN("device was too short to have any superblock\n");
2508         ExFreePool(sb);
2509         return STATUS_UNRECOGNIZED_VOLUME;
2510     }
2511 
2512     i = 0;
2513     valid_superblocks = 0;
2514 
2515     while (superblock_addrs[i] > 0) {
2516         UINT32 crc32;
2517 
2518         if (i > 0 && superblock_addrs[i] + to_read > length)
2519             break;
2520 
2521         Status = sync_read_phys(device, superblock_addrs[i], to_read, (PUCHAR)sb, FALSE);
2522         if (!NT_SUCCESS(Status)) {
2523             ERR("Failed to read superblock %u: %08x\n", i, Status);
2524             ExFreePool(sb);
2525             return Status;
2526         }
2527 
2528         if (sb->magic != BTRFS_MAGIC) {
2529             if (i == 0) {
2530                 TRACE("not a BTRFS volume\n");
2531                 ExFreePool(sb);
2532                 return STATUS_UNRECOGNIZED_VOLUME;
2533             }
2534         } else {
2535             TRACE("got superblock %u!\n", i);
2536 
2537             crc32 = ~calc_crc32c(0xffffffff, (UINT8*)&sb->uuid, (ULONG)sizeof(superblock) - sizeof(sb->checksum));
2538 
2539             if (crc32 != *((UINT32*)sb->checksum))
2540                 WARN("crc32 was %08x, expected %08x\n", crc32, *((UINT32*)sb->checksum));
2541             else if (sb->sector_size == 0)
2542                 WARN("superblock sector size was 0\n");
2543             else if (sb->node_size < sizeof(tree_header) + sizeof(internal_node) || sb->node_size > 0x10000)
2544                 WARN("invalid node size %x\n", sb->node_size);
2545             else if ((sb->node_size % sb->sector_size) != 0)
2546                 WARN("node size %x was not a multiple of sector_size %x\n", sb->node_size, sb->sector_size);
2547             else if (valid_superblocks == 0 || sb->generation > Vcb->superblock.generation) {
2548                 RtlCopyMemory(&Vcb->superblock, sb, sizeof(superblock));
2549                 valid_superblocks++;
2550             }
2551         }
2552 
2553         i++;
2554     }
2555 
2556     ExFreePool(sb);
2557 
2558     if (valid_superblocks == 0) {
2559         ERR("could not find any valid superblocks\n");
2560         return STATUS_INTERNAL_ERROR;
2561     }
2562 
2563     TRACE("label is %s\n", Vcb->superblock.label);
2564 
2565     return STATUS_SUCCESS;
2566 }
2567 
2568 NTSTATUS dev_ioctl(_In_ PDEVICE_OBJECT DeviceObject, _In_ ULONG ControlCode, _In_reads_bytes_opt_(InputBufferSize) PVOID InputBuffer, _In_ ULONG InputBufferSize,
2569                    _Out_writes_bytes_opt_(OutputBufferSize) PVOID OutputBuffer, _In_ ULONG OutputBufferSize, _In_ BOOLEAN Override, _Out_opt_ IO_STATUS_BLOCK* iosb) {
2570     PIRP Irp;
2571     KEVENT Event;
2572     NTSTATUS Status;
2573     PIO_STACK_LOCATION IrpSp;
2574     IO_STATUS_BLOCK IoStatus;
2575 
2576     KeInitializeEvent(&Event, NotificationEvent, FALSE);
2577 
2578     Irp = IoBuildDeviceIoControlRequest(ControlCode,
2579                                         DeviceObject,
2580                                         InputBuffer,
2581                                         InputBufferSize,
2582                                         OutputBuffer,
2583                                         OutputBufferSize,
2584                                         FALSE,
2585                                         &Event,
2586                                         &IoStatus);
2587 
2588     if (!Irp) return STATUS_INSUFFICIENT_RESOURCES;
2589 
2590     if (Override) {
2591         IrpSp = IoGetNextIrpStackLocation(Irp);
2592         IrpSp->Flags |= SL_OVERRIDE_VERIFY_VOLUME;
2593     }
2594 
2595     Status = IoCallDriver(DeviceObject, Irp);
2596 
2597     if (Status == STATUS_PENDING) {
2598         KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
2599         Status = IoStatus.Status;
2600     }
2601 
2602     if (iosb)
2603         *iosb = IoStatus;
2604 
2605     return Status;
2606 }
2607 
2608 _Requires_exclusive_lock_held_(Vcb->tree_lock)
2609 static NTSTATUS add_root(_Inout_ device_extension* Vcb, _In_ UINT64 id, _In_ UINT64 addr,
2610                          _In_ UINT64 generation, _In_opt_ traverse_ptr* tp) {
2611     root* r = ExAllocatePoolWithTag(PagedPool, sizeof(root), ALLOC_TAG);
2612     if (!r) {
2613         ERR("out of memory\n");
2614         return STATUS_INSUFFICIENT_RESOURCES;
2615     }
2616 
2617     r->id = id;
2618     r->dirty = FALSE;
2619     r->received = FALSE;
2620     r->reserved = NULL;
2621     r->treeholder.address = addr;
2622     r->treeholder.tree = NULL;
2623     r->treeholder.generation = generation;
2624     r->parent = 0;
2625     r->send_ops = 0;
2626     InitializeListHead(&r->fcbs);
2627 
2628     r->nonpaged = ExAllocatePoolWithTag(NonPagedPool, sizeof(root_nonpaged), ALLOC_TAG);
2629     if (!r->nonpaged) {
2630         ERR("out of memory\n");
2631         ExFreePool(r);
2632         return STATUS_INSUFFICIENT_RESOURCES;
2633     }
2634 
2635     ExInitializeResourceLite(&r->nonpaged->load_tree_lock);
2636 
2637     r->lastinode = 0;
2638 
2639     if (tp) {
2640         RtlCopyMemory(&r->root_item, tp->item->data, min(sizeof(ROOT_ITEM), tp->item->size));
2641         if (tp->item->size < sizeof(ROOT_ITEM))
2642             RtlZeroMemory(((UINT8*)&r->root_item) + tp->item->size, sizeof(ROOT_ITEM) - tp->item->size);
2643     } else
2644         RtlZeroMemory(&r->root_item, sizeof(ROOT_ITEM));
2645 
2646     if (!Vcb->readonly && (r->id == BTRFS_ROOT_ROOT || r->id == BTRFS_ROOT_FSTREE || (r->id >= 0x100 && !(r->id & 0xf000000000000000)))) { // FS tree root
2647         // FIXME - don't call this if subvol is readonly (though we will have to if we ever toggle this flag)
2648         get_last_inode(Vcb, r, NULL);
2649 
2650         if (r->id == BTRFS_ROOT_ROOT && r->lastinode < 0x100)
2651             r->lastinode = 0x100;
2652     }
2653 
2654     InsertTailList(&Vcb->roots, &r->list_entry);
2655 
2656     switch (r->id) {
2657         case BTRFS_ROOT_ROOT:
2658             Vcb->root_root = r;
2659             break;
2660 
2661         case BTRFS_ROOT_EXTENT:
2662             Vcb->extent_root = r;
2663             break;
2664 
2665         case BTRFS_ROOT_CHUNK:
2666             Vcb->chunk_root = r;
2667             break;
2668 
2669         case BTRFS_ROOT_DEVTREE:
2670             Vcb->dev_root = r;
2671             break;
2672 
2673         case BTRFS_ROOT_CHECKSUM:
2674             Vcb->checksum_root = r;
2675             break;
2676 
2677         case BTRFS_ROOT_UUID:
2678             Vcb->uuid_root = r;
2679             break;
2680 
2681         case BTRFS_ROOT_FREE_SPACE:
2682             Vcb->space_root = r;
2683             break;
2684 
2685         case BTRFS_ROOT_DATA_RELOC:
2686             Vcb->data_reloc_root = r;
2687             break;
2688     }
2689 
2690     return STATUS_SUCCESS;
2691 }
2692 
2693 static NTSTATUS look_for_roots(_Requires_exclusive_lock_held_(_Curr_->tree_lock) _In_ device_extension* Vcb, _In_opt_ PIRP Irp) {
2694     traverse_ptr tp, next_tp;
2695     KEY searchkey;
2696     BOOL b;
2697     NTSTATUS Status;
2698 
2699     searchkey.obj_id = 0;
2700     searchkey.obj_type = 0;
2701     searchkey.offset = 0;
2702 
2703     Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp);
2704     if (!NT_SUCCESS(Status)) {
2705         ERR("error - find_item returned %08x\n", Status);
2706         return Status;
2707     }
2708 
2709     do {
2710         TRACE("(%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
2711 
2712         if (tp.item->key.obj_type == TYPE_ROOT_ITEM) {
2713             ROOT_ITEM* ri = (ROOT_ITEM*)tp.item->data;
2714 
2715             if (tp.item->size < offsetof(ROOT_ITEM, byte_limit)) {
2716                 ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, offsetof(ROOT_ITEM, byte_limit));
2717             } else {
2718                 TRACE("root %llx - address %llx\n", tp.item->key.obj_id, ri->block_number);
2719 
2720                 Status = add_root(Vcb, tp.item->key.obj_id, ri->block_number, ri->generation, &tp);
2721                 if (!NT_SUCCESS(Status)) {
2722                     ERR("add_root returned %08x\n", Status);
2723                     return Status;
2724                 }
2725             }
2726         } else if (tp.item->key.obj_type == TYPE_ROOT_BACKREF && !IsListEmpty(&Vcb->roots)) {
2727             root* lastroot = CONTAINING_RECORD(Vcb->roots.Blink, root, list_entry);
2728 
2729             if (lastroot->id == tp.item->key.obj_id)
2730                 lastroot->parent = tp.item->key.offset;
2731         }
2732 
2733         b = find_next_item(Vcb, &tp, &next_tp, FALSE, Irp);
2734 
2735         if (b)
2736             tp = next_tp;
2737     } while (b);
2738 
2739     if (!Vcb->readonly && !Vcb->data_reloc_root) {
2740         root* reloc_root;
2741         INODE_ITEM* ii;
2742         UINT16 irlen;
2743         INODE_REF* ir;
2744         LARGE_INTEGER time;
2745         BTRFS_TIME now;
2746 
2747         WARN("data reloc root doesn't exist, creating it\n");
2748 
2749         Status = create_root(Vcb, BTRFS_ROOT_DATA_RELOC, &reloc_root, FALSE, 0, Irp);
2750 
2751         if (!NT_SUCCESS(Status)) {
2752             ERR("create_root returned %08x\n", Status);
2753             return Status;
2754         }
2755 
2756         reloc_root->root_item.inode.generation = 1;
2757         reloc_root->root_item.inode.st_size = 3;
2758         reloc_root->root_item.inode.st_blocks = Vcb->superblock.node_size;
2759         reloc_root->root_item.inode.st_nlink = 1;
2760         reloc_root->root_item.inode.st_mode = 040755;
2761         reloc_root->root_item.inode.flags = 0xffffffff80000000;
2762         reloc_root->root_item.objid = SUBVOL_ROOT_INODE;
2763         reloc_root->root_item.bytes_used = Vcb->superblock.node_size;
2764 
2765         ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
2766         if (!ii) {
2767             ERR("out of memory\n");
2768             return STATUS_INSUFFICIENT_RESOURCES;
2769         }
2770 
2771         KeQuerySystemTime(&time);
2772         win_time_to_unix(time, &now);
2773 
2774         RtlZeroMemory(ii, sizeof(INODE_ITEM));
2775         ii->generation = Vcb->superblock.generation;
2776         ii->st_blocks = Vcb->superblock.node_size;
2777         ii->st_nlink = 1;
2778         ii->st_mode = 040755;
2779         ii->st_atime = now;
2780         ii->st_ctime = now;
2781         ii->st_mtime = now;
2782 
2783         Status = insert_tree_item(Vcb, reloc_root, SUBVOL_ROOT_INODE, TYPE_INODE_ITEM, 0, ii, sizeof(INODE_ITEM), NULL, Irp);
2784         if (!NT_SUCCESS(Status)) {
2785             ERR("insert_tree_item returned %08x\n", Status);
2786             ExFreePool(ii);
2787             return Status;
2788         }
2789 
2790         irlen = (UINT16)offsetof(INODE_REF, name[0]) + 2;
2791         ir = ExAllocatePoolWithTag(PagedPool, irlen, ALLOC_TAG);
2792         if (!ir) {
2793             ERR("out of memory\n");
2794             return STATUS_INSUFFICIENT_RESOURCES;
2795         }
2796 
2797         ir->index = 0;
2798         ir->n = 2;
2799         ir->name[0] = '.';
2800         ir->name[1] = '.';
2801 
2802         Status = insert_tree_item(Vcb, reloc_root, SUBVOL_ROOT_INODE, TYPE_INODE_REF, SUBVOL_ROOT_INODE, ir, irlen, NULL, Irp);
2803         if (!NT_SUCCESS(Status)) {
2804             ERR("insert_tree_item returned %08x\n", Status);
2805             ExFreePool(ir);
2806             return Status;
2807         }
2808 
2809         Vcb->data_reloc_root = reloc_root;
2810         Vcb->need_write = TRUE;
2811     }
2812 
2813     return STATUS_SUCCESS;
2814 }
2815 
2816 static NTSTATUS find_disk_holes(_In_ _Requires_lock_held_(_Curr_->tree_lock) device_extension* Vcb, _In_ device* dev, _In_opt_ PIRP Irp) {
2817     KEY searchkey;
2818     traverse_ptr tp, next_tp;
2819     BOOL b;
2820     UINT64 lastaddr;
2821     NTSTATUS Status;
2822 
2823     InitializeListHead(&dev->space);
2824 
2825     searchkey.obj_id = 0;
2826     searchkey.obj_type = TYPE_DEV_STATS;
2827     searchkey.offset = dev->devitem.dev_id;
2828 
2829     Status = find_item(Vcb, Vcb->dev_root, &tp, &searchkey, FALSE, Irp);
2830     if (NT_SUCCESS(Status) && !keycmp(tp.item->key, searchkey))
2831         RtlCopyMemory(dev->stats, tp.item->data, min(sizeof(UINT64) * 5, tp.item->size));
2832 
2833     searchkey.obj_id = dev->devitem.dev_id;
2834     searchkey.obj_type = TYPE_DEV_EXTENT;
2835     searchkey.offset = 0;
2836 
2837     Status = find_item(Vcb, Vcb->dev_root, &tp, &searchkey, FALSE, Irp);
2838     if (!NT_SUCCESS(Status)) {
2839         ERR("error - find_item returned %08x\n", Status);
2840         return Status;
2841     }
2842 
2843     lastaddr = 0;
2844 
2845     do {
2846         if (tp.item->key.obj_id == dev->devitem.dev_id && tp.item->key.obj_type == TYPE_DEV_EXTENT) {
2847             if (tp.item->size >= sizeof(DEV_EXTENT)) {
2848                 DEV_EXTENT* de = (DEV_EXTENT*)tp.item->data;
2849 
2850                 if (tp.item->key.offset > lastaddr) {
2851                     Status = add_space_entry(&dev->space, NULL, lastaddr, tp.item->key.offset - lastaddr);
2852                     if (!NT_SUCCESS(Status)) {
2853                         ERR("add_space_entry returned %08x\n", Status);
2854                         return Status;
2855                     }
2856                 }
2857 
2858                 lastaddr = tp.item->key.offset + de->length;
2859             } else {
2860                 ERR("(%llx,%x,%llx) was %u bytes, expected %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(DEV_EXTENT));
2861             }
2862         }
2863 
2864         b = find_next_item(Vcb, &tp, &next_tp, FALSE, Irp);
2865 
2866         if (b) {
2867             tp = next_tp;
2868             if (tp.item->key.obj_id > searchkey.obj_id || tp.item->key.obj_type > searchkey.obj_type)
2869                 break;
2870         }
2871     } while (b);
2872 
2873     if (lastaddr < dev->devitem.num_bytes) {
2874         Status = add_space_entry(&dev->space, NULL, lastaddr, dev->devitem.num_bytes - lastaddr);
2875         if (!NT_SUCCESS(Status)) {
2876             ERR("add_space_entry returned %08x\n", Status);
2877             return Status;
2878         }
2879     }
2880 
2881     // The Linux driver doesn't like to allocate chunks within the first megabyte of a device.
2882 
2883     space_list_subtract2(&dev->space, NULL, 0, 0x100000, NULL, NULL);
2884 
2885     return STATUS_SUCCESS;
2886 }
2887 
2888 static void add_device_to_list(_In_ device_extension* Vcb, _In_ device* dev) {
2889     LIST_ENTRY* le;
2890 
2891     le = Vcb->devices.Flink;
2892 
2893     while (le != &Vcb->devices) {
2894         device* dev2 = CONTAINING_RECORD(le, device, list_entry);
2895 
2896         if (dev2->devitem.dev_id > dev->devitem.dev_id) {
2897             InsertHeadList(le->Blink, &dev->list_entry);
2898             return;
2899         }
2900 
2901         le = le->Flink;
2902     }
2903 
2904     InsertTailList(&Vcb->devices, &dev->list_entry);
2905 }
2906 
2907 _Ret_maybenull_
2908 device* find_device_from_uuid(_In_ device_extension* Vcb, _In_ BTRFS_UUID* uuid) {
2909     volume_device_extension* vde;
2910     pdo_device_extension* pdode;
2911     LIST_ENTRY* le;
2912 
2913     le = Vcb->devices.Flink;
2914     while (le != &Vcb->devices) {
2915         device* dev = CONTAINING_RECORD(le, device, list_entry);
2916 
2917         TRACE("device %llx, uuid %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n", dev->devitem.dev_id,
2918             dev->devitem.device_uuid.uuid[0], dev->devitem.device_uuid.uuid[1], dev->devitem.device_uuid.uuid[2], dev->devitem.device_uuid.uuid[3], dev->devitem.device_uuid.uuid[4], dev->devitem.device_uuid.uuid[5], dev->devitem.device_uuid.uuid[6], dev->devitem.device_uuid.uuid[7],
2919             dev->devitem.device_uuid.uuid[8], dev->devitem.device_uuid.uuid[9], dev->devitem.device_uuid.uuid[10], dev->devitem.device_uuid.uuid[11], dev->devitem.device_uuid.uuid[12], dev->devitem.device_uuid.uuid[13], dev->devitem.device_uuid.uuid[14], dev->devitem.device_uuid.uuid[15]);
2920 
2921         if (RtlCompareMemory(&dev->devitem.device_uuid, uuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
2922             TRACE("returning device %llx\n", dev->devitem.dev_id);
2923             return dev;
2924         }
2925 
2926         le = le->Flink;
2927     }
2928 
2929     vde = Vcb->vde;
2930 
2931     if (!vde)
2932         goto end;
2933 
2934     pdode = vde->pdode;
2935 
2936     ExAcquireResourceSharedLite(&pdode->child_lock, TRUE);
2937 
2938     if (Vcb->devices_loaded < Vcb->superblock.num_devices) {
2939         le = pdode->children.Flink;
2940 
2941         while (le != &pdode->children) {
2942             volume_child* vc = CONTAINING_RECORD(le, volume_child, list_entry);
2943 
2944             if (RtlCompareMemory(uuid, &vc->uuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
2945                 device* dev;
2946 
2947                 dev = ExAllocatePoolWithTag(NonPagedPool, sizeof(device), ALLOC_TAG);
2948                 if (!dev) {
2949                     ExReleaseResourceLite(&pdode->child_lock);
2950                     ERR("out of memory\n");
2951                     return NULL;
2952                 }
2953 
2954                 RtlZeroMemory(dev, sizeof(device));
2955                 dev->devobj = vc->devobj;
2956                 dev->devitem.device_uuid = *uuid;
2957                 dev->devitem.dev_id = vc->devid;
2958                 dev->devitem.num_bytes = vc->size;
2959                 dev->seeding = vc->seeding;
2960                 dev->readonly = dev->seeding;
2961                 dev->reloc = FALSE;
2962                 dev->removable = FALSE;
2963                 dev->disk_num = vc->disk_num;
2964                 dev->part_num = vc->part_num;
2965                 dev->num_trim_entries = 0;
2966                 InitializeListHead(&dev->trim_list);
2967 
2968                 add_device_to_list(Vcb, dev);
2969                 Vcb->devices_loaded++;
2970 
2971                 ExReleaseResourceLite(&pdode->child_lock);
2972 
2973                 return dev;
2974             }
2975 
2976             le = le->Flink;
2977         }
2978     }
2979 
2980     ExReleaseResourceLite(&pdode->child_lock);
2981 
2982 end:
2983     WARN("could not find device with uuid %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n",
2984          uuid->uuid[0], uuid->uuid[1], uuid->uuid[2], uuid->uuid[3], uuid->uuid[4], uuid->uuid[5], uuid->uuid[6], uuid->uuid[7],
2985          uuid->uuid[8], uuid->uuid[9], uuid->uuid[10], uuid->uuid[11], uuid->uuid[12], uuid->uuid[13], uuid->uuid[14], uuid->uuid[15]);
2986 
2987     return NULL;
2988 }
2989 
2990 static BOOL is_device_removable(_In_ PDEVICE_OBJECT devobj) {
2991     NTSTATUS Status;
2992     STORAGE_HOTPLUG_INFO shi;
2993 
2994     Status = dev_ioctl(devobj, IOCTL_STORAGE_GET_HOTPLUG_INFO, NULL, 0, &shi, sizeof(STORAGE_HOTPLUG_INFO), TRUE, NULL);
2995 
2996     if (!NT_SUCCESS(Status)) {
2997         ERR("dev_ioctl returned %08x\n", Status);
2998         return FALSE;
2999     }
3000 
3001     return shi.MediaRemovable != 0 ? TRUE : FALSE;
3002 }
3003 
3004 static ULONG get_device_change_count(_In_ PDEVICE_OBJECT devobj) {
3005     NTSTATUS Status;
3006     ULONG cc;
3007     IO_STATUS_BLOCK iosb;
3008 
3009     Status = dev_ioctl(devobj, IOCTL_STORAGE_CHECK_VERIFY, NULL, 0, &cc, sizeof(ULONG), TRUE, &iosb);
3010 
3011     if (!NT_SUCCESS(Status)) {
3012         ERR("dev_ioctl returned %08x\n", Status);
3013         return 0;
3014     }
3015 
3016     if (iosb.Information < sizeof(ULONG)) {
3017         ERR("iosb.Information was too short\n");
3018         return 0;
3019     }
3020 
3021     return cc;
3022 }
3023 
3024 void init_device(_In_ device_extension* Vcb, _Inout_ device* dev, _In_ BOOL get_nums) {
3025     NTSTATUS Status;
3026     ULONG aptelen;
3027     ATA_PASS_THROUGH_EX* apte;
3028     STORAGE_PROPERTY_QUERY spq;
3029     DEVICE_TRIM_DESCRIPTOR dtd;
3030 
3031     dev->removable = is_device_removable(dev->devobj);
3032     dev->change_count = dev->removable ? get_device_change_count(dev->devobj) : 0;
3033 
3034     if (get_nums) {
3035         STORAGE_DEVICE_NUMBER sdn;
3036 
3037         Status = dev_ioctl(dev->devobj, IOCTL_STORAGE_GET_DEVICE_NUMBER, NULL, 0,
3038                            &sdn, sizeof(STORAGE_DEVICE_NUMBER), TRUE, NULL);
3039 
3040         if (!NT_SUCCESS(Status)) {
3041             WARN("IOCTL_STORAGE_GET_DEVICE_NUMBER returned %08x\n", Status);
3042             dev->disk_num = 0xffffffff;
3043             dev->part_num = 0xffffffff;
3044         } else {
3045             dev->disk_num = sdn.DeviceNumber;
3046             dev->part_num = sdn.PartitionNumber;
3047         }
3048     }
3049 
3050     dev->trim = FALSE;
3051     dev->readonly = dev->seeding;
3052     dev->reloc = FALSE;
3053     dev->num_trim_entries = 0;
3054     dev->stats_changed = FALSE;
3055     InitializeListHead(&dev->trim_list);
3056 
3057     if (!dev->readonly) {
3058         Status = dev_ioctl(dev->devobj, IOCTL_DISK_IS_WRITABLE, NULL, 0,
3059                         NULL, 0, TRUE, NULL);
3060         if (Status == STATUS_MEDIA_WRITE_PROTECTED)
3061             dev->readonly = TRUE;
3062     }
3063 
3064     aptelen = sizeof(ATA_PASS_THROUGH_EX) + 512;
3065     apte = ExAllocatePoolWithTag(NonPagedPool, aptelen, ALLOC_TAG);
3066     if (!apte) {
3067         ERR("out of memory\n");
3068         return;
3069     }
3070 
3071     RtlZeroMemory(apte, aptelen);
3072 
3073     apte->Length = sizeof(ATA_PASS_THROUGH_EX);
3074     apte->AtaFlags = ATA_FLAGS_DATA_IN;
3075     apte->DataTransferLength = aptelen - sizeof(ATA_PASS_THROUGH_EX);
3076     apte->TimeOutValue = 3;
3077     apte->DataBufferOffset = apte->Length;
3078     apte->CurrentTaskFile[6] = IDE_COMMAND_IDENTIFY;
3079 
3080     Status = dev_ioctl(dev->devobj, IOCTL_ATA_PASS_THROUGH, apte, aptelen,
3081                        apte, aptelen, TRUE, NULL);
3082 
3083     if (!NT_SUCCESS(Status))
3084         TRACE("IOCTL_ATA_PASS_THROUGH returned %08x for IDENTIFY DEVICE\n", Status);
3085     else {
3086         IDENTIFY_DEVICE_DATA* idd = (IDENTIFY_DEVICE_DATA*)((UINT8*)apte + sizeof(ATA_PASS_THROUGH_EX));
3087 
3088         if (idd->CommandSetSupport.FlushCache) {
3089             dev->can_flush = TRUE;
3090             TRACE("FLUSH CACHE supported\n");
3091         } else
3092             TRACE("FLUSH CACHE not supported\n");
3093     }
3094 
3095     ExFreePool(apte);
3096 
3097     spq.PropertyId = StorageDeviceTrimProperty;
3098     spq.QueryType = PropertyStandardQuery;
3099     spq.AdditionalParameters[0] = 0;
3100 
3101     Status = dev_ioctl(dev->devobj, IOCTL_STORAGE_QUERY_PROPERTY, &spq, sizeof(STORAGE_PROPERTY_QUERY),
3102                        &dtd, sizeof(DEVICE_TRIM_DESCRIPTOR), TRUE, NULL);
3103 
3104     if (NT_SUCCESS(Status)) {
3105         if (dtd.TrimEnabled) {
3106             dev->trim = TRUE;
3107             Vcb->trim = TRUE;
3108             TRACE("TRIM supported\n");
3109         } else
3110             TRACE("TRIM not supported\n");
3111     }
3112 
3113     RtlZeroMemory(dev->stats, sizeof(UINT64) * 5);
3114 }
3115 
3116 static NTSTATUS load_chunk_root(_In_ _Requires_lock_held_(_Curr_->tree_lock) device_extension* Vcb, _In_opt_ PIRP Irp) {
3117     traverse_ptr tp, next_tp;
3118     KEY searchkey;
3119     BOOL b;
3120     chunk* c;
3121     NTSTATUS Status;
3122 
3123     searchkey.obj_id = 0;
3124     searchkey.obj_type = 0;
3125     searchkey.offset = 0;
3126 
3127     Vcb->data_flags = 0;
3128     Vcb->metadata_flags = 0;
3129     Vcb->system_flags = 0;
3130 
3131     Status = find_item(Vcb, Vcb->chunk_root, &tp, &searchkey, FALSE, Irp);
3132     if (!NT_SUCCESS(Status)) {
3133         ERR("error - find_item returned %08x\n", Status);
3134         return Status;
3135     }
3136 
3137     do {
3138         TRACE("(%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
3139 
3140         if (tp.item->key.obj_id == 1 && tp.item->key.obj_type == TYPE_DEV_ITEM) {
3141             if (tp.item->size < sizeof(DEV_ITEM)) {
3142                 ERR("(%llx,%x,%llx) was %u bytes, expected %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(DEV_ITEM));
3143             } else {
3144                 DEV_ITEM* di = (DEV_ITEM*)tp.item->data;
3145                 LIST_ENTRY* le;
3146                 BOOL done = FALSE;
3147 
3148                 le = Vcb->devices.Flink;
3149                 while (le != &Vcb->devices) {
3150                     device* dev = CONTAINING_RECORD(le, device, list_entry);
3151 
3152                     if (dev->devobj && RtlCompareMemory(&dev->devitem.device_uuid, &di->device_uuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
3153                         RtlCopyMemory(&dev->devitem, tp.item->data, min(tp.item->size, sizeof(DEV_ITEM)));
3154 
3155                         if (le != Vcb->devices.Flink)
3156                             init_device(Vcb, dev, TRUE);
3157 
3158                         done = TRUE;
3159                         break;
3160                     }
3161 
3162                     le = le->Flink;
3163                 }
3164 
3165                 if (!done && Vcb->vde) {
3166                     volume_device_extension* vde = Vcb->vde;
3167                     pdo_device_extension* pdode = vde->pdode;
3168 
3169                     ExAcquireResourceSharedLite(&pdode->child_lock, TRUE);
3170 
3171                     if (Vcb->devices_loaded < Vcb->superblock.num_devices) {
3172                         le = pdode->children.Flink;
3173 
3174                         while (le != &pdode->children) {
3175                             volume_child* vc = CONTAINING_RECORD(le, volume_child, list_entry);
3176 
3177                             if (RtlCompareMemory(&di->device_uuid, &vc->uuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
3178                                 device* dev;
3179 
3180                                 dev = ExAllocatePoolWithTag(NonPagedPool, sizeof(device), ALLOC_TAG);
3181                                 if (!dev) {
3182                                     ExReleaseResourceLite(&pdode->child_lock);
3183                                     ERR("out of memory\n");
3184                                     return STATUS_INSUFFICIENT_RESOURCES;
3185                                 }
3186 
3187                                 RtlZeroMemory(dev, sizeof(device));
3188 
3189                                 dev->devobj = vc->devobj;
3190                                 RtlCopyMemory(&dev->devitem, di, min(tp.item->size, sizeof(DEV_ITEM)));
3191                                 dev->seeding = vc->seeding;
3192                                 init_device(Vcb, dev, FALSE);
3193 
3194                                 if (dev->devitem.num_bytes > vc->size) {
3195                                     WARN("device %llx: DEV_ITEM says %llx bytes, but Windows only reports %llx\n", tp.item->key.offset,
3196                                          dev->devitem.num_bytes, vc->size);
3197 
3198                                     dev->devitem.num_bytes = vc->size;
3199                                 }
3200 
3201                                 dev->disk_num = vc->disk_num;
3202                                 dev->part_num = vc->part_num;
3203                                 add_device_to_list(Vcb, dev);
3204                                 Vcb->devices_loaded++;
3205 
3206                                 done = TRUE;
3207                                 break;
3208                             }
3209 
3210                             le = le->Flink;
3211                         }
3212 
3213                         if (!done) {
3214                             if (!Vcb->options.allow_degraded) {
3215                                 ERR("volume not found: device %llx, uuid %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n", tp.item->key.offset,
3216                                     di->device_uuid.uuid[0], di->device_uuid.uuid[1], di->device_uuid.uuid[2], di->device_uuid.uuid[3], di->device_uuid.uuid[4], di->device_uuid.uuid[5], di->device_uuid.uuid[6], di->device_uuid.uuid[7],
3217                                     di->device_uuid.uuid[8], di->device_uuid.uuid[9], di->device_uuid.uuid[10], di->device_uuid.uuid[11], di->device_uuid.uuid[12], di->device_uuid.uuid[13], di->device_uuid.uuid[14], di->device_uuid.uuid[15]);
3218                             } else {
3219                                 device* dev;
3220 
3221                                 dev = ExAllocatePoolWithTag(NonPagedPool, sizeof(device), ALLOC_TAG);
3222                                 if (!dev) {
3223                                     ExReleaseResourceLite(&pdode->child_lock);
3224                                     ERR("out of memory\n");
3225                                     return STATUS_INSUFFICIENT_RESOURCES;
3226                                 }
3227 
3228                                 RtlZeroMemory(dev, sizeof(device));
3229 
3230                                 // Missing device, so we keep dev->devobj as NULL
3231                                 RtlCopyMemory(&dev->devitem, di, min(tp.item->size, sizeof(DEV_ITEM)));
3232                                 InitializeListHead(&dev->trim_list);
3233 
3234                                 add_device_to_list(Vcb, dev);
3235                                 Vcb->devices_loaded++;
3236                             }
3237                         }
3238                     } else
3239                         ERR("unexpected device %llx found\n", tp.item->key.offset);
3240 
3241                     ExReleaseResourceLite(&pdode->child_lock);
3242                 }
3243             }
3244         } else if (tp.item->key.obj_type == TYPE_CHUNK_ITEM) {
3245             if (tp.item->size < sizeof(CHUNK_ITEM)) {
3246                 ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(CHUNK_ITEM));
3247             } else {
3248                 c = ExAllocatePoolWithTag(NonPagedPool, sizeof(chunk), ALLOC_TAG);
3249 
3250                 if (!c) {
3251                     ERR("out of memory\n");
3252                     return STATUS_INSUFFICIENT_RESOURCES;
3253                 }
3254 
3255                 c->size = tp.item->size;
3256                 c->offset = tp.item->key.offset;
3257                 c->used = c->oldused = 0;
3258                 c->cache = c->old_cache = NULL;
3259                 c->created = FALSE;
3260                 c->readonly = FALSE;
3261                 c->reloc = FALSE;
3262                 c->cache_loaded = FALSE;
3263                 c->changed = FALSE;
3264                 c->space_changed = FALSE;
3265                 c->balance_num = 0;
3266 
3267                 c->chunk_item = ExAllocatePoolWithTag(NonPagedPool, tp.item->size, ALLOC_TAG);
3268 
3269                 if (!c->chunk_item) {
3270                     ERR("out of memory\n");
3271                     ExFreePool(c);
3272                     return STATUS_INSUFFICIENT_RESOURCES;
3273                 }
3274 
3275                 RtlCopyMemory(c->chunk_item, tp.item->data, tp.item->size);
3276 
3277                 if (c->chunk_item->type & BLOCK_FLAG_DATA && c->chunk_item->type > Vcb->data_flags)
3278                     Vcb->data_flags = c->chunk_item->type;
3279 
3280                 if (c->chunk_item->type & BLOCK_FLAG_METADATA && c->chunk_item->type > Vcb->metadata_flags)
3281                     Vcb->metadata_flags = c->chunk_item->type;
3282 
3283                 if (c->chunk_item->type & BLOCK_FLAG_SYSTEM && c->chunk_item->type > Vcb->system_flags)
3284                     Vcb->system_flags = c->chunk_item->type;
3285 
3286                 if (c->chunk_item->type & BLOCK_FLAG_RAID10) {
3287                     if (c->chunk_item->sub_stripes == 0 || c->chunk_item->sub_stripes > c->chunk_item->num_stripes) {
3288                         ERR("chunk %llx: invalid stripes (num_stripes %u, sub_stripes %u)\n", c->offset, c->chunk_item->num_stripes, c->chunk_item->sub_stripes);
3289                         ExFreePool(c->chunk_item);
3290                         ExFreePool(c);
3291                         return STATUS_INTERNAL_ERROR;
3292                     }
3293                 }
3294 
3295                 if (c->chunk_item->num_stripes > 0) {
3296                     CHUNK_ITEM_STRIPE* cis = (CHUNK_ITEM_STRIPE*)&c->chunk_item[1];
3297                     UINT16 i;
3298 
3299                     c->devices = ExAllocatePoolWithTag(NonPagedPool, sizeof(device*) * c->chunk_item->num_stripes, ALLOC_TAG);
3300 
3301                     if (!c->devices) {
3302                         ERR("out of memory\n");
3303                         ExFreePool(c->chunk_item);
3304                         ExFreePool(c);
3305                         return STATUS_INSUFFICIENT_RESOURCES;
3306                     }
3307 
3308                     for (i = 0; i < c->chunk_item->num_stripes; i++) {
3309                         c->devices[i] = find_device_from_uuid(Vcb, &cis[i].dev_uuid);
3310                         TRACE("device %llu = %p\n", i, c->devices[i]);
3311 
3312                         if (!c->devices[i]) {
3313                             ERR("missing device\n");
3314                             ExFreePool(c->chunk_item);
3315                             ExFreePool(c);
3316                             return STATUS_INTERNAL_ERROR;
3317                         }
3318 
3319                         if (c->devices[i]->readonly)
3320                             c->readonly = TRUE;
3321                     }
3322                 } else {
3323                     ERR("chunk %llx: number of stripes is 0\n", c->offset);
3324                     ExFreePool(c->chunk_item);
3325                     ExFreePool(c);
3326                     return STATUS_INTERNAL_ERROR;
3327                 }
3328 
3329                 ExInitializeResourceLite(&c->lock);
3330                 ExInitializeResourceLite(&c->changed_extents_lock);
3331 
3332                 InitializeListHead(&c->space);
3333                 InitializeListHead(&c->space_size);
3334                 InitializeListHead(&c->deleting);
3335                 InitializeListHead(&c->changed_extents);
3336 
3337                 InitializeListHead(&c->range_locks);
3338                 ExInitializeResourceLite(&c->range_locks_lock);
3339                 KeInitializeEvent(&c->range_locks_event, NotificationEvent, FALSE);
3340 
3341                 InitializeListHead(&c->partial_stripes);
3342                 ExInitializeResourceLite(&c->partial_stripes_lock);
3343 
3344                 c->last_alloc_set = FALSE;
3345 
3346                 c->last_stripe = 0;
3347 
3348                 InsertTailList(&Vcb->chunks, &c->list_entry);
3349 
3350                 c->list_entry_balance.Flink = NULL;
3351             }
3352         }
3353 
3354         b = find_next_item(Vcb, &tp, &next_tp, FALSE, Irp);
3355 
3356         if (b)
3357             tp = next_tp;
3358     } while (b);
3359 
3360     Vcb->log_to_phys_loaded = TRUE;
3361 
3362     if (Vcb->data_flags == 0)
3363         Vcb->data_flags = BLOCK_FLAG_DATA | (Vcb->superblock.num_devices > 1 ? BLOCK_FLAG_RAID0 : 0);
3364 
3365     if (Vcb->metadata_flags == 0)
3366         Vcb->metadata_flags = BLOCK_FLAG_METADATA | (Vcb->superblock.num_devices > 1 ? BLOCK_FLAG_RAID1 : BLOCK_FLAG_DUPLICATE);
3367 
3368     if (Vcb->system_flags == 0)
3369         Vcb->system_flags = BLOCK_FLAG_SYSTEM | (Vcb->superblock.num_devices > 1 ? BLOCK_FLAG_RAID1 : BLOCK_FLAG_DUPLICATE);
3370 
3371     if (Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_MIXED_GROUPS) {
3372         Vcb->metadata_flags |= BLOCK_FLAG_DATA;
3373         Vcb->data_flags = Vcb->metadata_flags;
3374     }
3375 
3376     return STATUS_SUCCESS;
3377 }
3378 
3379 void protect_superblocks(_Inout_ chunk* c) {
3380     UINT16 i = 0, j;
3381     UINT64 off_start, off_end;
3382 
3383     // The Linux driver also protects all the space before the first superblock.
3384     // I realize this confuses physical and logical addresses, but this is what btrfs-progs does -
3385     // evidently Linux assumes the chunk at 0 is always SINGLE.
3386     if (c->offset < superblock_addrs[0])
3387         space_list_subtract(c, FALSE, c->offset, superblock_addrs[0] - c->offset, NULL);
3388 
3389     while (superblock_addrs[i] != 0) {
3390         CHUNK_ITEM* ci = c->chunk_item;
3391         CHUNK_ITEM_STRIPE* cis = (CHUNK_ITEM_STRIPE*)&ci[1];
3392 
3393         if (ci->type & BLOCK_FLAG_RAID0 || ci->type & BLOCK_FLAG_RAID10) {
3394             for (j = 0; j < ci->num_stripes; j++) {
3395                 UINT16 sub_stripes = max(ci->sub_stripes, 1);
3396 
3397                 if (cis[j].offset + (ci->size * ci->num_stripes / sub_stripes) > superblock_addrs[i] && cis[j].offset <= superblock_addrs[i] + sizeof(superblock)) {
3398 #ifdef _DEBUG
3399                     UINT64 startoff;
3400                     UINT16 startoffstripe;
3401 #endif
3402 
3403                     TRACE("cut out superblock in chunk %llx\n", c->offset);
3404 
3405                     off_start = superblock_addrs[i] - cis[j].offset;
3406                     off_start -= off_start % ci->stripe_length;
3407                     off_start *= ci->num_stripes / sub_stripes;
3408                     off_start += (j / sub_stripes) * ci->stripe_length;
3409 
3410                     off_end = off_start + ci->stripe_length;
3411 
3412 #ifdef _DEBUG
3413                     get_raid0_offset(off_start, ci->stripe_length, ci->num_stripes / sub_stripes, &startoff, &startoffstripe);
3414                     TRACE("j = %u, startoffstripe = %u\n", j, startoffstripe);
3415                     TRACE("startoff = %llx, superblock = %llx\n", startoff + cis[j].offset, superblock_addrs[i]);
3416 #endif
3417 
3418                     space_list_subtract(c, FALSE, c->offset + off_start, off_end - off_start, NULL);
3419                 }
3420             }
3421         } else if (ci->type & BLOCK_FLAG_RAID5) {
3422             UINT64 stripe_size = ci->size / (ci->num_stripes - 1);
3423 
3424             for (j = 0; j < ci->num_stripes; j++) {
3425                 if (cis[j].offset + stripe_size > superblock_addrs[i] && cis[j].offset <= superblock_addrs[i] + sizeof(superblock)) {
3426                     TRACE("cut out superblock in chunk %llx\n", c->offset);
3427 
3428                     off_start = superblock_addrs[i] - cis[j].offset;
3429                     off_start -= off_start % ci->stripe_length;
3430                     off_start *= ci->num_stripes - 1;
3431 
3432                     off_end = sector_align(superblock_addrs[i] - cis[j].offset + sizeof(superblock), ci->stripe_length);
3433                     off_end *= ci->num_stripes - 1;
3434 
3435                     TRACE("cutting out %llx, size %llx\n", c->offset + off_start, off_end - off_start);
3436 
3437                     space_list_subtract(c, FALSE, c->offset + off_start, off_end - off_start, NULL);
3438                 }
3439             }
3440         } else if (ci->type & BLOCK_FLAG_RAID6) {
3441             UINT64 stripe_size = ci->size / (ci->num_stripes - 2);
3442 
3443             for (j = 0; j < ci->num_stripes; j++) {
3444                 if (cis[j].offset + stripe_size > superblock_addrs[i] && cis[j].offset <= superblock_addrs[i] + sizeof(superblock)) {
3445                     TRACE("cut out superblock in chunk %llx\n", c->offset);
3446 
3447                     off_start = superblock_addrs[i] - cis[j].offset;
3448                     off_start -= off_start % ci->stripe_length;
3449                     off_start *= ci->num_stripes - 2;
3450 
3451                     off_end = sector_align(superblock_addrs[i] - cis[j].offset + sizeof(superblock), ci->stripe_length);
3452                     off_end *= ci->num_stripes - 2;
3453 
3454                     TRACE("cutting out %llx, size %llx\n", c->offset + off_start, off_end - off_start);
3455 
3456                     space_list_subtract(c, FALSE, c->offset + off_start, off_end - off_start, NULL);
3457                 }
3458             }
3459         } else { // SINGLE, DUPLICATE, RAID1
3460             for (j = 0; j < ci->num_stripes; j++) {
3461                 if (cis[j].offset + ci->size > superblock_addrs[i] && cis[j].offset <= superblock_addrs[i] + sizeof(superblock)) {
3462                     TRACE("cut out superblock in chunk %llx\n", c->offset);
3463 
3464                     // The Linux driver protects the whole stripe in which the superblock lives
3465 
3466                     off_start = ((superblock_addrs[i] - cis[j].offset) / c->chunk_item->stripe_length) * c->chunk_item->stripe_length;
3467                     off_end = sector_align(superblock_addrs[i] - cis[j].offset + sizeof(superblock), c->chunk_item->stripe_length);
3468 
3469                     space_list_subtract(c, FALSE, c->offset + off_start, off_end - off_start, NULL);
3470                 }
3471             }
3472         }
3473 
3474         i++;
3475     }
3476 }
3477 
3478 NTSTATUS find_chunk_usage(_In_ _Requires_lock_held_(_Curr_->tree_lock) device_extension* Vcb, _In_opt_ PIRP Irp) {
3479     LIST_ENTRY* le = Vcb->chunks.Flink;
3480     chunk* c;
3481     KEY searchkey;
3482     traverse_ptr tp;
3483     BLOCK_GROUP_ITEM* bgi;
3484     NTSTATUS Status;
3485 
3486     searchkey.obj_type = TYPE_BLOCK_GROUP_ITEM;
3487 
3488     while (le != &Vcb->chunks) {
3489         c = CONTAINING_RECORD(le, chunk, list_entry);
3490 
3491         searchkey.obj_id = c->offset;
3492         searchkey.offset = c->chunk_item->size;
3493 
3494         Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp);
3495         if (!NT_SUCCESS(Status)) {
3496             ERR("error - find_item returned %08x\n", Status);
3497             return Status;
3498         }
3499 
3500         if (!keycmp(searchkey, tp.item->key)) {
3501             if (tp.item->size >= sizeof(BLOCK_GROUP_ITEM)) {
3502                 bgi = (BLOCK_GROUP_ITEM*)tp.item->data;
3503 
3504                 c->used = c->oldused = bgi->used;
3505 
3506                 TRACE("chunk %llx has %llx bytes used\n", c->offset, c->used);
3507             } else {
3508                 ERR("(%llx;%llx,%x,%llx) is %u bytes, expected %u\n",
3509                     Vcb->extent_root->id, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(BLOCK_GROUP_ITEM));
3510             }
3511         }
3512 
3513         le = le->Flink;
3514     }
3515 
3516     Vcb->chunk_usage_found = TRUE;
3517 
3518     return STATUS_SUCCESS;
3519 }
3520 
3521 static NTSTATUS load_sys_chunks(_In_ device_extension* Vcb) {
3522     KEY key;
3523     ULONG n = Vcb->superblock.n;
3524 
3525     while (n > 0) {
3526         if (n > sizeof(KEY)) {
3527             RtlCopyMemory(&key, &Vcb->superblock.sys_chunk_array[Vcb->superblock.n - n], sizeof(KEY));
3528             n -= sizeof(KEY);
3529         } else
3530             return STATUS_SUCCESS;
3531 
3532         TRACE("bootstrap: %llx,%x,%llx\n", key.obj_id, key.obj_type, key.offset);
3533 
3534         if (key.obj_type == TYPE_CHUNK_ITEM) {
3535             CHUNK_ITEM* ci;
3536             USHORT cisize;
3537             sys_chunk* sc;
3538 
3539             if (n < sizeof(CHUNK_ITEM))
3540                 return STATUS_SUCCESS;
3541 
3542             ci = (CHUNK_ITEM*)&Vcb->superblock.sys_chunk_array[Vcb->superblock.n - n];
3543             cisize = sizeof(CHUNK_ITEM) + (ci->num_stripes * sizeof(CHUNK_ITEM_STRIPE));
3544 
3545             if (n < cisize)
3546                 return STATUS_SUCCESS;
3547 
3548             sc = ExAllocatePoolWithTag(PagedPool, sizeof(sys_chunk), ALLOC_TAG);
3549 
3550             if (!sc) {
3551                 ERR("out of memory\n");
3552                 return STATUS_INSUFFICIENT_RESOURCES;
3553             }
3554 
3555             sc->key = key;
3556             sc->size = cisize;
3557             sc->data = ExAllocatePoolWithTag(PagedPool, sc->size, ALLOC_TAG);
3558 
3559             if (!sc->data) {
3560                 ERR("out of memory\n");
3561                 ExFreePool(sc);
3562                 return STATUS_INSUFFICIENT_RESOURCES;
3563             }
3564 
3565             RtlCopyMemory(sc->data, ci, sc->size);
3566             InsertTailList(&Vcb->sys_chunks, &sc->list_entry);
3567 
3568             n -= cisize;
3569         } else {
3570             ERR("unexpected item %llx,%x,%llx in bootstrap\n", key.obj_id, key.obj_type, key.offset);
3571             return STATUS_INTERNAL_ERROR;
3572         }
3573     }
3574 
3575     return STATUS_SUCCESS;
3576 }
3577 
3578 _Ret_maybenull_
3579 static root* find_default_subvol(_In_ _Requires_lock_held_(_Curr_->tree_lock) device_extension* Vcb, _In_opt_ PIRP Irp) {
3580     LIST_ENTRY* le;
3581 
3582     static char fn[] = "default";
3583     static UINT32 crc32 = 0x8dbfc2d2;
3584 
3585     if (Vcb->options.subvol_id != 0) {
3586         le = Vcb->roots.Flink;
3587         while (le != &Vcb->roots) {
3588             root* r = CONTAINING_RECORD(le, root, list_entry);
3589 
3590             if (r->id == Vcb->options.subvol_id)
3591                 return r;
3592 
3593             le = le->Flink;
3594         }
3595     }
3596 
3597     if (Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_DEFAULT_SUBVOL) {
3598         NTSTATUS Status;
3599         KEY searchkey;
3600         traverse_ptr tp;
3601         DIR_ITEM* di;
3602 
3603         searchkey.obj_id = Vcb->superblock.root_dir_objectid;
3604         searchkey.obj_type = TYPE_DIR_ITEM;
3605         searchkey.offset = crc32;
3606 
3607         Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp);
3608         if (!NT_SUCCESS(Status)) {
3609             ERR("error - find_item returned %08x\n", Status);
3610             goto end;
3611         }
3612 
3613         if (keycmp(tp.item->key, searchkey)) {
3614             ERR("could not find (%llx,%x,%llx) in root tree\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset);
3615             goto end;
3616         }
3617 
3618         if (tp.item->size < sizeof(DIR_ITEM)) {
3619             ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(DIR_ITEM));
3620             goto end;
3621         }
3622 
3623         di = (DIR_ITEM*)tp.item->data;
3624 
3625         if (tp.item->size < sizeof(DIR_ITEM) - 1 + di->n) {
3626             ERR("(%llx,%x,%llx) was %u bytes, expected %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(DIR_ITEM) - 1 + di->n);
3627             goto end;
3628         }
3629 
3630         if (di->n != strlen(fn) || RtlCompareMemory(di->name, fn, di->n) != di->n) {
3631             ERR("root DIR_ITEM had same CRC32, but was not \"default\"\n");
3632             goto end;
3633         }
3634 
3635         if (di->key.obj_type != TYPE_ROOT_ITEM) {
3636             ERR("default root has key (%llx,%x,%llx), expected subvolume\n", di->key.obj_id, di->key.obj_type, di->key.offset);
3637             goto end;
3638         }
3639 
3640         le = Vcb->roots.Flink;
3641         while (le != &Vcb->roots) {
3642             root* r = CONTAINING_RECORD(le, root, list_entry);
3643 
3644             if (r->id == di->key.obj_id)
3645                 return r;
3646 
3647             le = le->Flink;
3648         }
3649 
3650         ERR("could not find root %llx, using default instead\n", di->key.obj_id);
3651     }
3652 
3653 end:
3654     le = Vcb->roots.Flink;
3655     while (le != &Vcb->roots) {
3656         root* r = CONTAINING_RECORD(le, root, list_entry);
3657 
3658         if (r->id == BTRFS_ROOT_FSTREE)
3659             return r;
3660 
3661         le = le->Flink;
3662     }
3663 
3664     return NULL;
3665 }
3666 
3667 void init_file_cache(_In_ PFILE_OBJECT FileObject, _In_ CC_FILE_SIZES* ccfs) {
3668     TRACE("(%p, %p)\n", FileObject, ccfs);
3669 
3670     CcInitializeCacheMap(FileObject, ccfs, FALSE, cache_callbacks, FileObject);
3671 
3672     if (diskacc)
3673         fCcSetAdditionalCacheAttributesEx(FileObject, CC_ENABLE_DISK_IO_ACCOUNTING);
3674 
3675     CcSetReadAheadGranularity(FileObject, READ_AHEAD_GRANULARITY);
3676 }
3677 
3678 static NTSTATUS create_calc_threads(_In_ PDEVICE_OBJECT DeviceObject) {
3679     device_extension* Vcb = DeviceObject->DeviceExtension;
3680     ULONG i;
3681 
3682     Vcb->calcthreads.num_threads = KeQueryActiveProcessorCount(NULL);
3683 
3684     Vcb->calcthreads.threads = ExAllocatePoolWithTag(NonPagedPool, sizeof(drv_calc_thread) * Vcb->calcthreads.num_threads, ALLOC_TAG);
3685     if (!Vcb->calcthreads.threads) {
3686         ERR("out of memory\n");
3687         return STATUS_INSUFFICIENT_RESOURCES;
3688     }
3689 
3690     InitializeListHead(&Vcb->calcthreads.job_list);
3691     ExInitializeResourceLite(&Vcb->calcthreads.lock);
3692     KeInitializeEvent(&Vcb->calcthreads.event, NotificationEvent, FALSE);
3693 
3694     RtlZeroMemory(Vcb->calcthreads.threads, sizeof(drv_calc_thread) * Vcb->calcthreads.num_threads);
3695 
3696     for (i = 0; i < Vcb->calcthreads.num_threads; i++) {
3697         NTSTATUS Status;
3698 
3699         Vcb->calcthreads.threads[i].DeviceObject = DeviceObject;
3700         KeInitializeEvent(&Vcb->calcthreads.threads[i].finished, NotificationEvent, FALSE);
3701 
3702         Status = PsCreateSystemThread(&Vcb->calcthreads.threads[i].handle, 0, NULL, NULL, NULL, calc_thread, &Vcb->calcthreads.threads[i]);
3703         if (!NT_SUCCESS(Status)) {
3704             ULONG j;
3705 
3706             ERR("PsCreateSystemThread returned %08x\n", Status);
3707 
3708             for (j = 0; j < i; j++) {
3709                 Vcb->calcthreads.threads[i].quit = TRUE;
3710             }
3711 
3712             KeSetEvent(&Vcb->calcthreads.event, 0, FALSE);
3713 
3714             return Status;
3715         }
3716     }
3717 
3718     return STATUS_SUCCESS;
3719 }
3720 
3721 static BOOL is_btrfs_volume(_In_ PDEVICE_OBJECT DeviceObject) {
3722     NTSTATUS Status;
3723     MOUNTDEV_NAME mdn, *mdn2;
3724     ULONG mdnsize;
3725 
3726     Status = dev_ioctl(DeviceObject, IOCTL_MOUNTDEV_QUERY_DEVICE_NAME, NULL, 0, &mdn, sizeof(MOUNTDEV_NAME), TRUE, NULL);
3727     if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW) {
3728         ERR("IOCTL_MOUNTDEV_QUERY_DEVICE_NAME returned %08x\n", Status);
3729         return FALSE;
3730     }
3731 
3732     mdnsize = (ULONG)offsetof(MOUNTDEV_NAME, Name[0]) + mdn.NameLength;
3733 
3734     mdn2 = ExAllocatePoolWithTag(PagedPool, mdnsize, ALLOC_TAG);
3735     if (!mdn2) {
3736         ERR("out of memory\n");
3737         return FALSE;
3738     }
3739 
3740     Status = dev_ioctl(DeviceObject, IOCTL_MOUNTDEV_QUERY_DEVICE_NAME, NULL, 0, mdn2, mdnsize, TRUE, NULL);
3741     if (!NT_SUCCESS(Status)) {
3742         ERR("IOCTL_MOUNTDEV_QUERY_DEVICE_NAME returned %08x\n", Status);
3743         ExFreePool(mdn2);
3744         return FALSE;
3745     }
3746 
3747     if (mdn2->NameLength > wcslen(BTRFS_VOLUME_PREFIX) * sizeof(WCHAR) &&
3748         RtlCompareMemory(mdn2->Name, BTRFS_VOLUME_PREFIX, wcslen(BTRFS_VOLUME_PREFIX) * sizeof(WCHAR)) == wcslen(BTRFS_VOLUME_PREFIX) * sizeof(WCHAR)) {
3749         ExFreePool(mdn2);
3750         return TRUE;
3751     }
3752 
3753     ExFreePool(mdn2);
3754 
3755     return FALSE;
3756 }
3757 
3758 static NTSTATUS get_device_pnp_name_guid(_In_ PDEVICE_OBJECT DeviceObject, _Out_ PUNICODE_STRING pnp_name, _In_ const GUID* guid) {
3759     NTSTATUS Status;
3760     WCHAR *list = NULL, *s;
3761 
3762     Status = IoGetDeviceInterfaces((PVOID)guid, NULL, 0, &list);
3763     if (!NT_SUCCESS(Status)) {
3764         ERR("IoGetDeviceInterfaces returned %08x\n", Status);
3765         return Status;
3766     }
3767 
3768     s = list;
3769     while (s[0] != 0) {
3770         PFILE_OBJECT FileObject;
3771         PDEVICE_OBJECT devobj;
3772         UNICODE_STRING name;
3773 
3774         name.Length = name.MaximumLength = (USHORT)wcslen(s) * sizeof(WCHAR);
3775         name.Buffer = s;
3776 
3777         if (NT_SUCCESS(IoGetDeviceObjectPointer(&name, FILE_READ_ATTRIBUTES, &FileObject, &devobj))) {
3778             if (DeviceObject == devobj || DeviceObject == FileObject->DeviceObject) {
3779                 ObDereferenceObject(FileObject);
3780 
3781                 pnp_name->Buffer = ExAllocatePoolWithTag(PagedPool, name.Length, ALLOC_TAG);
3782                 if (!pnp_name->Buffer) {
3783                     ERR("out of memory\n");
3784                     Status = STATUS_INSUFFICIENT_RESOURCES;
3785                     goto end;
3786                 }
3787 
3788                 RtlCopyMemory(pnp_name->Buffer, name.Buffer, name.Length);
3789                 pnp_name->Length = pnp_name->MaximumLength = name.Length;
3790 
3791                 Status = STATUS_SUCCESS;
3792                 goto end;
3793             }
3794 
3795             ObDereferenceObject(FileObject);
3796         }
3797 
3798         s = &s[wcslen(s) + 1];
3799     }
3800 
3801     pnp_name->Length = pnp_name->MaximumLength = 0;
3802     pnp_name->Buffer = 0;
3803 
3804     Status = STATUS_NOT_FOUND;
3805 
3806 end:
3807     if (list)
3808         ExFreePool(list);
3809 
3810     return Status;
3811 }
3812 
3813 NTSTATUS get_device_pnp_name(_In_ PDEVICE_OBJECT DeviceObject, _Out_ PUNICODE_STRING pnp_name, _Out_ const GUID** guid) {
3814     NTSTATUS Status;
3815 
3816     Status = get_device_pnp_name_guid(DeviceObject, pnp_name, &GUID_DEVINTERFACE_VOLUME);
3817     if (NT_SUCCESS(Status)) {
3818         *guid = &GUID_DEVINTERFACE_VOLUME;
3819         return Status;
3820     }
3821 
3822     Status = get_device_pnp_name_guid(DeviceObject, pnp_name, &GUID_DEVINTERFACE_HIDDEN_VOLUME);
3823     if (NT_SUCCESS(Status)) {
3824         *guid = &GUID_DEVINTERFACE_HIDDEN_VOLUME;
3825         return Status;
3826     }
3827 
3828     Status = get_device_pnp_name_guid(DeviceObject, pnp_name, &GUID_DEVINTERFACE_DISK);
3829     if (NT_SUCCESS(Status)) {
3830         *guid = &GUID_DEVINTERFACE_DISK;
3831         return Status;
3832     }
3833 
3834     return STATUS_NOT_FOUND;
3835 }
3836 
3837 _Success_(return>=0)
3838 static NTSTATUS check_mount_device(_In_ PDEVICE_OBJECT DeviceObject, _Out_ BOOL* no_pnp) {
3839     NTSTATUS Status;
3840     ULONG to_read;
3841     superblock* sb;
3842     UINT32 crc32;
3843     UNICODE_STRING pnp_name;
3844     const GUID* guid;
3845 
3846     to_read = DeviceObject->SectorSize == 0 ? sizeof(superblock) : (ULONG)sector_align(sizeof(superblock), DeviceObject->SectorSize);
3847 
3848     sb = ExAllocatePoolWithTag(NonPagedPool, to_read, ALLOC_TAG);
3849     if (!sb) {
3850         ERR("out of memory\n");
3851         return STATUS_INSUFFICIENT_RESOURCES;
3852     }
3853 
3854     Status = sync_read_phys(DeviceObject, superblock_addrs[0], to_read, (PUCHAR)sb, TRUE);
3855     if (!NT_SUCCESS(Status)) {
3856         ERR("sync_read_phys returned %08x\n", Status);
3857         goto end;
3858     }
3859 
3860     if (sb->magic != BTRFS_MAGIC) {
3861         Status = STATUS_SUCCESS;
3862         goto end;
3863     }
3864 
3865     crc32 = ~calc_crc32c(0xffffffff, (UINT8*)&sb->uuid, (ULONG)sizeof(superblock) - sizeof(sb->checksum));
3866 
3867     if (crc32 != *((UINT32*)sb->checksum)) {
3868         WARN("crc32 was %08x, expected %08x\n", crc32, *((UINT32*)sb->checksum));
3869         Status = STATUS_SUCCESS;
3870         goto end;
3871     }
3872 
3873     DeviceObject->Flags &= ~DO_VERIFY_VOLUME;
3874 
3875     pnp_name.Buffer = NULL;
3876 
3877     Status = get_device_pnp_name(DeviceObject, &pnp_name, &guid);
3878     if (!NT_SUCCESS(Status)) {
3879         WARN("get_device_pnp_name returned %08x\n", Status);
3880         pnp_name.Length = 0;
3881     }
3882 
3883     if (pnp_name.Length == 0)
3884         *no_pnp = TRUE;
3885     else {
3886         *no_pnp = FALSE;
3887         volume_arrival(drvobj, &pnp_name);
3888     }
3889 
3890     if (pnp_name.Buffer)
3891         ExFreePool(pnp_name.Buffer);
3892 
3893     Status = STATUS_SUCCESS;
3894 
3895 end:
3896     ExFreePool(sb);
3897 
3898     return Status;
3899 }
3900 
3901 static BOOL still_has_superblock(_In_ PDEVICE_OBJECT device) {
3902     NTSTATUS Status;
3903     ULONG to_read;
3904     superblock* sb;
3905     PDEVICE_OBJECT device2;
3906 
3907     if (!device)
3908         return FALSE;
3909 
3910     to_read = device->SectorSize == 0 ? sizeof(superblock) : (ULONG)sector_align(sizeof(superblock), device->SectorSize);
3911 
3912     sb = ExAllocatePoolWithTag(NonPagedPool, to_read, ALLOC_TAG);
3913     if (!sb) {
3914         ERR("out of memory\n");
3915         return FALSE;
3916     }
3917 
3918     Status = sync_read_phys(device, superblock_addrs[0], to_read, (PUCHAR)sb, TRUE);
3919     if (!NT_SUCCESS(Status)) {
3920         ERR("Failed to read superblock: %08x\n", Status);
3921         ExFreePool(sb);
3922         return FALSE;
3923     }
3924 
3925     if (sb->magic != BTRFS_MAGIC) {
3926         TRACE("not a BTRFS volume\n");
3927         ExFreePool(sb);
3928         return FALSE;
3929     } else {
3930         UINT32 crc32 = ~calc_crc32c(0xffffffff, (UINT8*)&sb->uuid, (ULONG)sizeof(superblock) - sizeof(sb->checksum));
3931 
3932         if (crc32 != *((UINT32*)sb->checksum)) {
3933             WARN("crc32 was %08x, expected %08x\n", crc32, *((UINT32*)sb->checksum));
3934             ExFreePool(sb);
3935             return FALSE;
3936         }
3937     }
3938 
3939     device2 = device;
3940 
3941     do {
3942         device2->Flags &= ~DO_VERIFY_VOLUME;
3943         device2 = IoGetLowerDeviceObject(device2);
3944     } while (device2);
3945 
3946     ExFreePool(sb);
3947     return TRUE;
3948 }
3949 
3950 static NTSTATUS mount_vol(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) {
3951     PIO_STACK_LOCATION IrpSp;
3952     PDEVICE_OBJECT NewDeviceObject = NULL;
3953     PDEVICE_OBJECT DeviceToMount, readobj;
3954     NTSTATUS Status;
3955     device_extension* Vcb = NULL;
3956     LIST_ENTRY *le, batchlist;
3957     KEY searchkey;
3958     traverse_ptr tp;
3959     fcb* root_fcb = NULL;
3960     ccb* root_ccb = NULL;
3961     BOOL init_lookaside = FALSE;
3962     device* dev;
3963     volume_device_extension* vde = NULL;
3964     pdo_device_extension* pdode = NULL;
3965     volume_child* vc;
3966     BOOL no_pnp = FALSE;
3967     UINT64 readobjsize;
3968 
3969     TRACE("(%p, %p)\n", DeviceObject, Irp);
3970 
3971     if (DeviceObject != master_devobj) {
3972         Status = STATUS_INVALID_DEVICE_REQUEST;
3973         goto exit;
3974     }
3975 
3976     IrpSp = IoGetCurrentIrpStackLocation(Irp);
3977     DeviceToMount = IrpSp->Parameters.MountVolume.DeviceObject;
3978 
3979     if (!is_btrfs_volume(DeviceToMount)) {
3980         Status = check_mount_device(DeviceToMount, &no_pnp);
3981         if (!NT_SUCCESS(Status))
3982             WARN("check_mount_device returned %08x\n", Status);
3983 
3984         if (!no_pnp) {
3985             Status = STATUS_UNRECOGNIZED_VOLUME;
3986             goto exit2;
3987         }
3988     } else {
3989         PDEVICE_OBJECT pdo;
3990 
3991         pdo = DeviceToMount;
3992 
3993         while (IoGetLowerDeviceObject(pdo)) {
3994             pdo = IoGetLowerDeviceObject(pdo);
3995         }
3996 
3997         ExAcquireResourceSharedLite(&pdo_list_lock, TRUE);
3998 
3999         le = pdo_list.Flink;
4000         while (le != &pdo_list) {
4001             pdo_device_extension* pdode = CONTAINING_RECORD(le, pdo_device_extension, list_entry);
4002 
4003             if (pdode->pdo == pdo) {
4004                 vde = pdode->vde;
4005                 break;
4006             }
4007 
4008             le = le->Flink;
4009         }
4010 
4011         ExReleaseResourceLite(&pdo_list_lock);
4012 
4013         if (!vde || vde->type != VCB_TYPE_VOLUME) {
4014             vde = NULL;
4015             Status = STATUS_UNRECOGNIZED_VOLUME;
4016             goto exit2;
4017         }
4018     }
4019 
4020     if (vde) {
4021         pdode = vde->pdode;
4022 
4023         ExAcquireResourceExclusiveLite(&pdode->child_lock, TRUE);
4024 
4025         le = pdode->children.Flink;
4026         while (le != &pdode->children) {
4027             LIST_ENTRY* le2 = le->Flink;
4028 
4029             vc = CONTAINING_RECORD(pdode->children.Flink, volume_child, list_entry);
4030 
4031             if (!still_has_superblock(vc->devobj)) {
4032                 remove_volume_child(vde, vc, FALSE);
4033 
4034                 if (pdode->num_children == 0) {
4035                     ERR("error - number of devices is zero\n");
4036                     Status = STATUS_INTERNAL_ERROR;
4037                     goto exit2;
4038                 }
4039 
4040                 Status = STATUS_DEVICE_NOT_READY;
4041                 goto exit2;
4042             }
4043 
4044             le = le2;
4045         }
4046 
4047         if (pdode->num_children == 0 || pdode->children_loaded == 0) {
4048             ERR("error - number of devices is zero\n");
4049             Status = STATUS_INTERNAL_ERROR;
4050             goto exit;
4051         }
4052 
4053         ExConvertExclusiveToSharedLite(&pdode->child_lock);
4054 
4055         vc = CONTAINING_RECORD(pdode->children.Flink, volume_child, list_entry);
4056 
4057         readobj = vc->devobj;
4058         readobjsize = vc->size;
4059 
4060         vde->device->Characteristics &= ~FILE_DEVICE_SECURE_OPEN;
4061     } else {
4062         GET_LENGTH_INFORMATION gli;
4063 
4064         vc = NULL;
4065         readobj = DeviceToMount;
4066 
4067         Status = dev_ioctl(readobj, IOCTL_DISK_GET_LENGTH_INFO, NULL, 0,
4068                            &gli, sizeof(gli), TRUE, NULL);
4069 
4070         if (!NT_SUCCESS(Status)) {
4071             ERR("error reading length information: %08x\n", Status);
4072             goto exit;
4073         }
4074 
4075         readobjsize = gli.Length.QuadPart;
4076     }
4077 
4078     Status = IoCreateDevice(drvobj, sizeof(device_extension), NULL, FILE_DEVICE_DISK_FILE_SYSTEM, 0, FALSE, &NewDeviceObject);
4079     if (!NT_SUCCESS(Status)) {
4080         ERR("IoCreateDevice returned %08x\n", Status);
4081         Status = STATUS_UNRECOGNIZED_VOLUME;
4082         goto exit;
4083     }
4084 
4085     NewDeviceObject->Flags |= DO_DIRECT_IO;
4086 
4087     // Some programs seem to expect that the sector size will be 512, for
4088     // FILE_NO_INTERMEDIATE_BUFFERING and the like.
4089     NewDeviceObject->SectorSize = min(DeviceToMount->SectorSize, 512);
4090 
4091     Vcb = (PVOID)NewDeviceObject->DeviceExtension;
4092     RtlZeroMemory(Vcb, sizeof(device_extension));
4093     Vcb->type = VCB_TYPE_FS;
4094     Vcb->vde = vde;
4095 
4096     ExInitializeResourceLite(&Vcb->tree_lock);
4097     Vcb->need_write = FALSE;
4098 
4099     ExInitializeResourceLite(&Vcb->fcb_lock);
4100     ExInitializeResourceLite(&Vcb->chunk_lock);
4101     ExInitializeResourceLite(&Vcb->dirty_fcbs_lock);
4102     ExInitializeResourceLite(&Vcb->dirty_filerefs_lock);
4103     ExInitializeResourceLite(&Vcb->dirty_subvols_lock);
4104     ExInitializeResourceLite(&Vcb->scrub.stats_lock);
4105 
4106     ExInitializeResourceLite(&Vcb->load_lock);
4107     ExAcquireResourceExclusiveLite(&Vcb->load_lock, TRUE);
4108 
4109     ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE);
4110 
4111     DeviceToMount->Flags |= DO_DIRECT_IO;
4112 
4113     Status = read_superblock(Vcb, readobj, readobjsize);
4114     if (!NT_SUCCESS(Status)) {
4115         if (!IoIsErrorUserInduced(Status))
4116             Status = STATUS_UNRECOGNIZED_VOLUME;
4117         else if (Irp->Tail.Overlay.Thread)
4118             IoSetHardErrorOrVerifyDevice(Irp, readobj);
4119 
4120         goto exit;
4121     }
4122 
4123     if (!vde && Vcb->superblock.num_devices > 1) {
4124         ERR("cannot mount multi-device FS with non-PNP device\n");
4125         Status = STATUS_UNRECOGNIZED_VOLUME;
4126         goto exit;
4127     }
4128 
4129     Status = registry_load_volume_options(Vcb);
4130     if (!NT_SUCCESS(Status)) {
4131         ERR("registry_load_volume_options returned %08x\n", Status);
4132         goto exit;
4133     }
4134 
4135     if (pdode && pdode->children_loaded < pdode->num_children && (!Vcb->options.allow_degraded || !finished_probing || degraded_wait)) {
4136         ERR("could not mount as %u device(s) missing\n", pdode->num_children - pdode->children_loaded);
4137         Status = STATUS_DEVICE_NOT_READY;
4138         goto exit;
4139     }
4140 
4141     if (Vcb->options.ignore) {
4142         TRACE("ignoring volume\n");
4143         Status = STATUS_UNRECOGNIZED_VOLUME;
4144         goto exit;
4145     }
4146 
4147     if (Vcb->superblock.incompat_flags & ~INCOMPAT_SUPPORTED) {
4148         WARN("cannot mount because of unsupported incompat flags (%llx)\n", Vcb->superblock.incompat_flags & ~INCOMPAT_SUPPORTED);
4149         Status = STATUS_UNRECOGNIZED_VOLUME;
4150         goto exit;
4151     }
4152 
4153     Vcb->readonly = FALSE;
4154     if (Vcb->superblock.compat_ro_flags & ~COMPAT_RO_SUPPORTED) {
4155         WARN("mounting read-only because of unsupported flags (%llx)\n", Vcb->superblock.compat_ro_flags & ~COMPAT_RO_SUPPORTED);
4156         Vcb->readonly = TRUE;
4157     }
4158 
4159     if (Vcb->options.readonly)
4160         Vcb->readonly = TRUE;
4161 
4162     Vcb->superblock.generation++;
4163     Vcb->superblock.incompat_flags |= BTRFS_INCOMPAT_FLAGS_MIXED_BACKREF;
4164 
4165     InitializeListHead(&Vcb->devices);
4166     dev = ExAllocatePoolWithTag(NonPagedPool, sizeof(device), ALLOC_TAG);
4167     if (!dev) {
4168         ERR("out of memory\n");
4169         Status = STATUS_INSUFFICIENT_RESOURCES;
4170         goto exit;
4171     }
4172 
4173     dev->devobj = readobj;
4174     RtlCopyMemory(&dev->devitem, &Vcb->superblock.dev_item, sizeof(DEV_ITEM));
4175 
4176     if (dev->devitem.num_bytes > readobjsize) {
4177         WARN("device %llx: DEV_ITEM says %llx bytes, but Windows only reports %llx\n", dev->devitem.dev_id,
4178                 dev->devitem.num_bytes, readobjsize);
4179 
4180         dev->devitem.num_bytes = readobjsize;
4181     }
4182 
4183     dev->seeding = Vcb->superblock.flags & BTRFS_SUPERBLOCK_FLAGS_SEEDING ? TRUE : FALSE;
4184 
4185     init_device(Vcb, dev, TRUE);
4186 
4187     InsertTailList(&Vcb->devices, &dev->list_entry);
4188     Vcb->devices_loaded = 1;
4189 
4190     if (DeviceToMount->Flags & DO_SYSTEM_BOOT_PARTITION)
4191         Vcb->disallow_dismount = TRUE;
4192 
4193     TRACE("DeviceToMount = %p\n", DeviceToMount);
4194     TRACE("IrpSp->Parameters.MountVolume.Vpb = %p\n", IrpSp->Parameters.MountVolume.Vpb);
4195 
4196     NewDeviceObject->StackSize = DeviceToMount->StackSize + 1;
4197     NewDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
4198 
4199     InitializeListHead(&Vcb->roots);
4200     InitializeListHead(&Vcb->drop_roots);
4201 
4202     Vcb->log_to_phys_loaded = FALSE;
4203 
4204     add_root(Vcb, BTRFS_ROOT_CHUNK, Vcb->superblock.chunk_tree_addr, Vcb->superblock.chunk_root_generation, NULL);
4205 
4206     if (!Vcb->chunk_root) {
4207         ERR("Could not load chunk root.\n");
4208         Status = STATUS_INTERNAL_ERROR;
4209         goto exit;
4210     }
4211 
4212     InitializeListHead(&Vcb->sys_chunks);
4213     Status = load_sys_chunks(Vcb);
4214     if (!NT_SUCCESS(Status)) {
4215         ERR("load_sys_chunks returned %08x\n", Status);
4216         goto exit;
4217     }
4218 
4219     InitializeListHead(&Vcb->chunks);
4220     InitializeListHead(&Vcb->trees);
4221     InitializeListHead(&Vcb->trees_hash);
4222     InitializeListHead(&Vcb->all_fcbs);
4223     InitializeListHead(&Vcb->dirty_fcbs);
4224     InitializeListHead(&Vcb->dirty_filerefs);
4225     InitializeListHead(&Vcb->dirty_subvols);
4226     InitializeListHead(&Vcb->send_ops);
4227 
4228     InitializeListHead(&Vcb->DirNotifyList);
4229     InitializeListHead(&Vcb->scrub.errors);
4230 
4231     FsRtlNotifyInitializeSync(&Vcb->NotifySync);
4232 
4233     ExInitializePagedLookasideList(&Vcb->tree_data_lookaside, NULL, NULL, 0, sizeof(tree_data), ALLOC_TAG, 0);
4234     ExInitializePagedLookasideList(&Vcb->traverse_ptr_lookaside, NULL, NULL, 0, sizeof(traverse_ptr), ALLOC_TAG, 0);
4235     ExInitializePagedLookasideList(&Vcb->batch_item_lookaside, NULL, NULL, 0, sizeof(batch_item), ALLOC_TAG, 0);
4236     ExInitializePagedLookasideList(&Vcb->fileref_lookaside, NULL, NULL, 0, sizeof(file_ref), ALLOC_TAG, 0);
4237     ExInitializePagedLookasideList(&Vcb->fcb_lookaside, NULL, NULL, 0, sizeof(fcb), ALLOC_TAG, 0);
4238     ExInitializePagedLookasideList(&Vcb->name_bit_lookaside, NULL, NULL, 0, sizeof(name_bit), ALLOC_TAG, 0);
4239     ExInitializeNPagedLookasideList(&Vcb->range_lock_lookaside, NULL, NULL, 0, sizeof(range_lock), ALLOC_TAG, 0);
4240     ExInitializeNPagedLookasideList(&Vcb->fileref_np_lookaside, NULL, NULL, 0, sizeof(file_ref_nonpaged), ALLOC_TAG, 0);
4241     ExInitializeNPagedLookasideList(&Vcb->fcb_np_lookaside, NULL, NULL, 0, sizeof(fcb_nonpaged), ALLOC_TAG, 0);
4242     init_lookaside = TRUE;
4243 
4244     Vcb->Vpb = IrpSp->Parameters.MountVolume.Vpb;
4245 
4246     Status = load_chunk_root(Vcb, Irp);
4247     if (!NT_SUCCESS(Status)) {
4248         ERR("load_chunk_root returned %08x\n", Status);
4249         goto exit;
4250     }
4251 
4252     if (Vcb->superblock.num_devices > 1) {
4253         if (Vcb->devices_loaded < Vcb->superblock.num_devices && (!Vcb->options.allow_degraded || !finished_probing)) {
4254             ERR("could not mount as %u device(s) missing\n", Vcb->superblock.num_devices - Vcb->devices_loaded);
4255 
4256             IoRaiseInformationalHardError(IO_ERR_INTERNAL_ERROR, NULL, NULL);
4257 
4258             Status = STATUS_INTERNAL_ERROR;
4259             goto exit;
4260         }
4261 
4262         if (dev->readonly && !Vcb->readonly) {
4263             Vcb->readonly = TRUE;
4264 
4265             le = Vcb->devices.Flink;
4266             while (le != &Vcb->devices) {
4267                 device* dev2 = CONTAINING_RECORD(le, device, list_entry);
4268 
4269                 if (dev2->readonly && !dev2->seeding)
4270                     break;
4271 
4272                 if (!dev2->readonly) {
4273                     Vcb->readonly = FALSE;
4274                     break;
4275                 }
4276 
4277                 le = le->Flink;
4278             }
4279 
4280             if (Vcb->readonly)
4281                 WARN("setting volume to readonly\n");
4282         }
4283     } else {
4284         if (dev->readonly) {
4285             WARN("setting volume to readonly as device is readonly\n");
4286             Vcb->readonly = TRUE;
4287         }
4288     }
4289 
4290     add_root(Vcb, BTRFS_ROOT_ROOT, Vcb->superblock.root_tree_addr, Vcb->superblock.generation - 1, NULL);
4291 
4292     if (!Vcb->root_root) {
4293         ERR("Could not load root of roots.\n");
4294         Status = STATUS_INTERNAL_ERROR;
4295         goto exit;
4296     }
4297 
4298     Status = look_for_roots(Vcb, Irp);
4299     if (!NT_SUCCESS(Status)) {
4300         ERR("look_for_roots returned %08x\n", Status);
4301         goto exit;
4302     }
4303 
4304     if (!Vcb->readonly) {
4305         Status = find_chunk_usage(Vcb, Irp);
4306         if (!NT_SUCCESS(Status)) {
4307             ERR("find_chunk_usage returned %08x\n", Status);
4308             goto exit;
4309         }
4310     }
4311 
4312     InitializeListHead(&batchlist);
4313 
4314     // We've already increased the generation by one
4315     if (!Vcb->readonly && (
4316         Vcb->options.clear_cache ||
4317         (!(Vcb->superblock.compat_ro_flags & BTRFS_COMPAT_RO_FLAGS_FREE_SPACE_CACHE) && Vcb->superblock.generation - 1 != Vcb->superblock.cache_generation) ||
4318         (Vcb->superblock.compat_ro_flags & BTRFS_COMPAT_RO_FLAGS_FREE_SPACE_CACHE && !(Vcb->superblock.compat_ro_flags & BTRFS_COMPAT_RO_FLAGS_FREE_SPACE_CACHE_VALID)))) {
4319         if (Vcb->options.clear_cache)
4320             WARN("ClearCache option was set, clearing cache...\n");
4321         else if (Vcb->superblock.compat_ro_flags & BTRFS_COMPAT_RO_FLAGS_FREE_SPACE_CACHE && !(Vcb->superblock.compat_ro_flags & BTRFS_COMPAT_RO_FLAGS_FREE_SPACE_CACHE_VALID))
4322             WARN("clearing free-space tree created by buggy Linux driver\n");
4323         else
4324             WARN("generation was %llx, free-space cache generation was %llx; clearing cache...\n", Vcb->superblock.generation - 1, Vcb->superblock.cache_generation);
4325 
4326         Status = clear_free_space_cache(Vcb, &batchlist, Irp);
4327         if (!NT_SUCCESS(Status)) {
4328             ERR("clear_free_space_cache returned %08x\n", Status);
4329             clear_batch_list(Vcb, &batchlist);
4330             goto exit;
4331         }
4332     }
4333 
4334     Status = commit_batch_list(Vcb, &batchlist, Irp);
4335     if (!NT_SUCCESS(Status)) {
4336         ERR("commit_batch_list returned %08x\n", Status);
4337         goto exit;
4338     }
4339 
4340     Vcb->volume_fcb = create_fcb(Vcb, NonPagedPool);
4341     if (!Vcb->volume_fcb) {
4342         ERR("out of memory\n");
4343         Status = STATUS_INSUFFICIENT_RESOURCES;
4344         goto exit;
4345     }
4346 
4347     Vcb->volume_fcb->Vcb = Vcb;
4348     Vcb->volume_fcb->sd = NULL;
4349 
4350     Vcb->dummy_fcb = create_fcb(Vcb, NonPagedPool);
4351     if (!Vcb->dummy_fcb) {
4352         ERR("out of memory\n");
4353         Status = STATUS_INSUFFICIENT_RESOURCES;
4354         goto exit;
4355     }
4356 
4357     Vcb->dummy_fcb->Vcb = Vcb;
4358     Vcb->dummy_fcb->type = BTRFS_TYPE_DIRECTORY;
4359     Vcb->dummy_fcb->inode = 2;
4360     Vcb->dummy_fcb->subvol = Vcb->root_root;
4361     Vcb->dummy_fcb->atts = FILE_ATTRIBUTE_DIRECTORY;
4362     Vcb->dummy_fcb->inode_item.st_nlink = 1;
4363     Vcb->dummy_fcb->inode_item.st_mode = __S_IFDIR;
4364 
4365     Vcb->dummy_fcb->hash_ptrs = ExAllocatePoolWithTag(PagedPool, sizeof(LIST_ENTRY*) * 256, ALLOC_TAG);
4366     if (!Vcb->dummy_fcb->hash_ptrs) {
4367         ERR("out of memory\n");
4368         Status = STATUS_INSUFFICIENT_RESOURCES;
4369         goto exit;
4370     }
4371 
4372     RtlZeroMemory(Vcb->dummy_fcb->hash_ptrs, sizeof(LIST_ENTRY*) * 256);
4373 
4374     Vcb->dummy_fcb->hash_ptrs_uc = ExAllocatePoolWithTag(PagedPool, sizeof(LIST_ENTRY*) * 256, ALLOC_TAG);
4375     if (!Vcb->dummy_fcb->hash_ptrs_uc) {
4376         ERR("out of memory\n");
4377         Status = STATUS_INSUFFICIENT_RESOURCES;
4378         goto exit;
4379     }
4380 
4381     RtlZeroMemory(Vcb->dummy_fcb->hash_ptrs_uc, sizeof(LIST_ENTRY*) * 256);
4382 
4383     root_fcb = create_fcb(Vcb, NonPagedPool);
4384     if (!root_fcb) {
4385         ERR("out of memory\n");
4386         Status = STATUS_INSUFFICIENT_RESOURCES;
4387         goto exit;
4388     }
4389 
4390     root_fcb->Vcb = Vcb;
4391     root_fcb->inode = SUBVOL_ROOT_INODE;
4392     root_fcb->type = BTRFS_TYPE_DIRECTORY;
4393 
4394 #ifdef DEBUG_FCB_REFCOUNTS
4395     WARN("volume FCB = %p\n", Vcb->volume_fcb);
4396     WARN("root FCB = %p\n", root_fcb);
4397 #endif
4398 
4399     root_fcb->subvol = find_default_subvol(Vcb, Irp);
4400 
4401     if (!root_fcb->subvol) {
4402         ERR("could not find top subvol\n");
4403         Status = STATUS_INTERNAL_ERROR;
4404         goto exit;
4405     }
4406 
4407     Status = load_dir_children(Vcb, root_fcb, TRUE, Irp);
4408     if (!NT_SUCCESS(Status)) {
4409         ERR("load_dir_children returned %08x\n", Status);
4410         goto exit;
4411     }
4412 
4413     searchkey.obj_id = root_fcb->inode;
4414     searchkey.obj_type = TYPE_INODE_ITEM;
4415     searchkey.offset = 0xffffffffffffffff;
4416 
4417     Status = find_item(Vcb, root_fcb->subvol, &tp, &searchkey, FALSE, Irp);
4418     if (!NT_SUCCESS(Status)) {
4419         ERR("error - find_item returned %08x\n", Status);
4420         goto exit;
4421     }
4422 
4423     if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) {
4424         ERR("couldn't find INODE_ITEM for root directory\n");
4425         Status = STATUS_INTERNAL_ERROR;
4426         goto exit;
4427     }
4428 
4429     if (tp.item->size > 0)
4430         RtlCopyMemory(&root_fcb->inode_item, tp.item->data, min(sizeof(INODE_ITEM), tp.item->size));
4431 
4432     fcb_get_sd(root_fcb, NULL, TRUE, Irp);
4433 
4434     root_fcb->atts = get_file_attributes(Vcb, root_fcb->subvol, root_fcb->inode, root_fcb->type, FALSE, FALSE, Irp);
4435 
4436     Vcb->root_fileref = create_fileref(Vcb);
4437     if (!Vcb->root_fileref) {
4438         ERR("out of memory\n");
4439         Status = STATUS_INSUFFICIENT_RESOURCES;
4440         goto exit;
4441     }
4442 
4443     Vcb->root_fileref->fcb = root_fcb;
4444     InsertTailList(&root_fcb->subvol->fcbs, &root_fcb->list_entry);
4445     InsertTailList(&Vcb->all_fcbs, &root_fcb->list_entry_all);
4446 
4447     root_fcb->fileref = Vcb->root_fileref;
4448 
4449     root_ccb = ExAllocatePoolWithTag(PagedPool, sizeof(ccb), ALLOC_TAG);
4450     if (!root_ccb) {
4451         ERR("out of memory\n");
4452         Status = STATUS_INSUFFICIENT_RESOURCES;
4453         goto exit;
4454     }
4455 
4456     Vcb->root_file = IoCreateStreamFileObject(NULL, DeviceToMount);
4457     Vcb->root_file->FsContext = root_fcb;
4458     Vcb->root_file->SectionObjectPointer = &root_fcb->nonpaged->segment_object;
4459     Vcb->root_file->Vpb = DeviceObject->Vpb;
4460 
4461     RtlZeroMemory(root_ccb, sizeof(ccb));
4462     root_ccb->NodeType = BTRFS_NODE_TYPE_CCB;
4463     root_ccb->NodeSize = sizeof(ccb);
4464 
4465     Vcb->root_file->FsContext2 = root_ccb;
4466 
4467     _SEH2_TRY {
4468         CcInitializeCacheMap(Vcb->root_file, (PCC_FILE_SIZES)(&root_fcb->Header.AllocationSize), FALSE, cache_callbacks, Vcb->root_file);
4469     } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
4470         Status = _SEH2_GetExceptionCode();
4471         goto exit;
4472     } _SEH2_END;
4473 
4474     le = Vcb->devices.Flink;
4475     while (le != &Vcb->devices) {
4476         device* dev2 = CONTAINING_RECORD(le, device, list_entry);
4477 
4478         Status = find_disk_holes(Vcb, dev2, Irp);
4479         if (!NT_SUCCESS(Status)) {
4480             ERR("find_disk_holes returned %08x\n", Status);
4481             goto exit;
4482         }
4483 
4484         le = le->Flink;
4485     }
4486 
4487     NewDeviceObject->Vpb = IrpSp->Parameters.MountVolume.Vpb;
4488     IrpSp->Parameters.MountVolume.Vpb->DeviceObject = NewDeviceObject;
4489     IrpSp->Parameters.MountVolume.Vpb->Flags |= VPB_MOUNTED;
4490     NewDeviceObject->Vpb->VolumeLabelLength = 4; // FIXME
4491     NewDeviceObject->Vpb->VolumeLabel[0] = '?';
4492     NewDeviceObject->Vpb->VolumeLabel[1] = 0;
4493     NewDeviceObject->Vpb->ReferenceCount++; // FIXME - should we deref this at any point?
4494 
4495     KeInitializeEvent(&Vcb->flush_thread_finished, NotificationEvent, FALSE);
4496 
4497     Status = PsCreateSystemThread(&Vcb->flush_thread_handle, 0, NULL, NULL, NULL, flush_thread, NewDeviceObject);
4498     if (!NT_SUCCESS(Status)) {
4499         ERR("PsCreateSystemThread returned %08x\n", Status);
4500         goto exit;
4501     }
4502 
4503     Status = create_calc_threads(NewDeviceObject);
4504     if (!NT_SUCCESS(Status)) {
4505         ERR("create_calc_threads returned %08x\n", Status);
4506         goto exit;
4507     }
4508 
4509     Status = registry_mark_volume_mounted(&Vcb->superblock.uuid);
4510     if (!NT_SUCCESS(Status))
4511         WARN("registry_mark_volume_mounted returned %08x\n", Status);
4512 
4513     Status = look_for_balance_item(Vcb);
4514     if (!NT_SUCCESS(Status) && Status != STATUS_NOT_FOUND)
4515         WARN("look_for_balance_item returned %08x\n", Status);
4516 
4517     Status = STATUS_SUCCESS;
4518 
4519     if (vde)
4520         vde->mounted_device = NewDeviceObject;
4521 
4522     ExInitializeResourceLite(&Vcb->send_load_lock);
4523 
4524 exit:
4525     if (pdode)
4526         ExReleaseResourceLite(&pdode->child_lock);
4527 
4528 exit2:
4529     if (Vcb) {
4530         ExReleaseResourceLite(&Vcb->tree_lock);
4531         ExReleaseResourceLite(&Vcb->load_lock);
4532     }
4533 
4534     if (!NT_SUCCESS(Status)) {
4535         if (Vcb) {
4536             if (init_lookaside) {
4537                 ExDeletePagedLookasideList(&Vcb->tree_data_lookaside);
4538                 ExDeletePagedLookasideList(&Vcb->traverse_ptr_lookaside);
4539                 ExDeletePagedLookasideList(&Vcb->batch_item_lookaside);
4540                 ExDeletePagedLookasideList(&Vcb->fileref_lookaside);
4541                 ExDeletePagedLookasideList(&Vcb->fcb_lookaside);
4542                 ExDeletePagedLookasideList(&Vcb->name_bit_lookaside);
4543                 ExDeleteNPagedLookasideList(&Vcb->range_lock_lookaside);
4544                 ExDeleteNPagedLookasideList(&Vcb->fileref_np_lookaside);
4545                 ExDeleteNPagedLookasideList(&Vcb->fcb_np_lookaside);
4546             }
4547 
4548             if (Vcb->root_file)
4549                 ObDereferenceObject(Vcb->root_file);
4550             else if (Vcb->root_fileref) {
4551                 acquire_fcb_lock_exclusive(Vcb);
4552                 free_fileref(Vcb, Vcb->root_fileref);
4553                 release_fcb_lock(Vcb);
4554             } else if (root_fcb) {
4555                 acquire_fcb_lock_exclusive(Vcb);
4556                 free_fcb(Vcb, root_fcb);
4557                 release_fcb_lock(Vcb);
4558             }
4559 
4560             if (Vcb->volume_fcb) {
4561                 acquire_fcb_lock_exclusive(Vcb);
4562                 free_fcb(Vcb, Vcb->volume_fcb);
4563                 release_fcb_lock(Vcb);
4564             }
4565 
4566             ExDeleteResourceLite(&Vcb->tree_lock);
4567             ExDeleteResourceLite(&Vcb->load_lock);
4568             ExDeleteResourceLite(&Vcb->fcb_lock);
4569             ExDeleteResourceLite(&Vcb->chunk_lock);
4570             ExDeleteResourceLite(&Vcb->dirty_fcbs_lock);
4571             ExDeleteResourceLite(&Vcb->dirty_filerefs_lock);
4572             ExDeleteResourceLite(&Vcb->dirty_subvols_lock);
4573             ExDeleteResourceLite(&Vcb->scrub.stats_lock);
4574 
4575             if (Vcb->devices.Flink) {
4576                 while (!IsListEmpty(&Vcb->devices)) {
4577                     device* dev2 = CONTAINING_RECORD(RemoveHeadList(&Vcb->devices), device, list_entry);
4578 
4579                     ExFreePool(dev2);
4580                 }
4581             }
4582         }
4583 
4584         if (NewDeviceObject)
4585             IoDeleteDevice(NewDeviceObject);
4586     } else {
4587         ExAcquireResourceExclusiveLite(&global_loading_lock, TRUE);
4588         InsertTailList(&VcbList, &Vcb->list_entry);
4589         ExReleaseResourceLite(&global_loading_lock);
4590 
4591         FsRtlNotifyVolumeEvent(Vcb->root_file, FSRTL_VOLUME_MOUNT);
4592     }
4593 
4594     TRACE("mount_vol done (status: %lx)\n", Status);
4595 
4596     return Status;
4597 }
4598 
4599 static NTSTATUS verify_device(_In_ device_extension* Vcb, _Inout_ device* dev) {
4600     NTSTATUS Status;
4601     superblock* sb;
4602     UINT32 crc32;
4603     ULONG to_read, cc;
4604 
4605     if (!dev->devobj)
4606         return STATUS_WRONG_VOLUME;
4607 
4608     if (dev->removable) {
4609         IO_STATUS_BLOCK iosb;
4610 
4611         Status = dev_ioctl(dev->devobj, IOCTL_STORAGE_CHECK_VERIFY, NULL, 0, &cc, sizeof(ULONG), TRUE, &iosb);
4612 
4613         if (IoIsErrorUserInduced(Status)) {
4614             ERR("IOCTL_STORAGE_CHECK_VERIFY returned %08x (user-induced)\n", Status);
4615 
4616             if (Vcb->vde) {
4617                 pdo_device_extension* pdode = Vcb->vde->pdode;
4618                 LIST_ENTRY* le2;
4619                 BOOL changed = FALSE;
4620 
4621                 ExAcquireResourceExclusiveLite(&pdode->child_lock, TRUE);
4622 
4623                 le2 = pdode->children.Flink;
4624                 while (le2 != &pdode->children) {
4625                     volume_child* vc = CONTAINING_RECORD(le2, volume_child, list_entry);
4626 
4627                     if (vc->devobj == dev->devobj) {
4628                         TRACE("removing device\n");
4629 
4630                         remove_volume_child(Vcb->vde, vc, TRUE);
4631                         changed = TRUE;
4632 
4633                         break;
4634                     }
4635 
4636                     le2 = le2->Flink;
4637                 }
4638 
4639                 if (!changed)
4640                     ExReleaseResourceLite(&pdode->child_lock);
4641             }
4642         } else if (!NT_SUCCESS(Status)) {
4643             ERR("IOCTL_STORAGE_CHECK_VERIFY returned %08x\n", Status);
4644             return Status;
4645         } else if (iosb.Information < sizeof(ULONG)) {
4646             ERR("iosb.Information was too short\n");
4647             return STATUS_INTERNAL_ERROR;
4648         }
4649 
4650         dev->change_count = cc;
4651     }
4652 
4653     to_read = dev->devobj->SectorSize == 0 ? sizeof(superblock) : (ULONG)sector_align(sizeof(superblock), dev->devobj->SectorSize);
4654 
4655     sb = ExAllocatePoolWithTag(NonPagedPool, to_read, ALLOC_TAG);
4656     if (!sb) {
4657         ERR("out of memory\n");
4658         return STATUS_INSUFFICIENT_RESOURCES;
4659     }
4660 
4661     Status = sync_read_phys(dev->devobj, superblock_addrs[0], to_read, (PUCHAR)sb, TRUE);
4662     if (!NT_SUCCESS(Status)) {
4663         ERR("Failed to read superblock: %08x\n", Status);
4664         ExFreePool(sb);
4665         return Status;
4666     }
4667 
4668     if (sb->magic != BTRFS_MAGIC) {
4669         ERR("not a BTRFS volume\n");
4670         ExFreePool(sb);
4671         return STATUS_WRONG_VOLUME;
4672     }
4673 
4674     crc32 = ~calc_crc32c(0xffffffff, (UINT8*)&sb->uuid, (ULONG)sizeof(superblock) - sizeof(sb->checksum));
4675     TRACE("crc32 was %08x, expected %08x\n", crc32, *((UINT32*)sb->checksum));
4676 
4677     if (crc32 != *((UINT32*)sb->checksum)) {
4678         ERR("checksum error\n");
4679         ExFreePool(sb);
4680         return STATUS_WRONG_VOLUME;
4681     }
4682 
4683     if (RtlCompareMemory(&sb->uuid, &Vcb->superblock.uuid, sizeof(BTRFS_UUID)) != sizeof(BTRFS_UUID)) {
4684         ERR("different UUIDs\n");
4685         ExFreePool(sb);
4686         return STATUS_WRONG_VOLUME;
4687     }
4688 
4689     ExFreePool(sb);
4690 
4691     dev->devobj->Flags &= ~DO_VERIFY_VOLUME;
4692 
4693     return STATUS_SUCCESS;
4694 }
4695 
4696 static NTSTATUS verify_volume(_In_ PDEVICE_OBJECT devobj) {
4697     device_extension* Vcb = devobj->DeviceExtension;
4698     NTSTATUS Status;
4699     LIST_ENTRY* le;
4700     UINT64 failed_devices = 0;
4701     BOOL locked = FALSE, remove = FALSE;
4702 
4703     if (!(Vcb->Vpb->Flags & VPB_MOUNTED))
4704         return STATUS_WRONG_VOLUME;
4705 
4706     if (!ExIsResourceAcquiredExclusive(&Vcb->tree_lock)) {
4707         ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE);
4708         locked = TRUE;
4709     }
4710 
4711     if (Vcb->removing) {
4712         if (locked) ExReleaseResourceLite(&Vcb->tree_lock);
4713         return STATUS_WRONG_VOLUME;
4714     }
4715 
4716     InterlockedIncrement(&Vcb->open_files); // so pnp_surprise_removal doesn't uninit the device while we're still using it
4717 
4718     le = Vcb->devices.Flink;
4719     while (le != &Vcb->devices) {
4720         device* dev = CONTAINING_RECORD(le, device, list_entry);
4721 
4722         Status = verify_device(Vcb, dev);
4723         if (!NT_SUCCESS(Status)) {
4724             failed_devices++;
4725 
4726             if (dev->devobj && Vcb->options.allow_degraded)
4727                 dev->devobj = NULL;
4728         }
4729 
4730         le = le->Flink;
4731     }
4732 
4733     InterlockedDecrement(&Vcb->open_files);
4734 
4735     if (Vcb->removing && Vcb->open_files == 0)
4736         remove = TRUE;
4737 
4738     if (locked)
4739         ExReleaseResourceLite(&Vcb->tree_lock);
4740 
4741     if (remove) {
4742         uninit(Vcb, FALSE);
4743         return Status;
4744     }
4745 
4746     if (failed_devices == 0 || (Vcb->options.allow_degraded && failed_devices < Vcb->superblock.num_devices)) {
4747         Vcb->Vpb->RealDevice->Flags &= ~DO_VERIFY_VOLUME;
4748 
4749         return STATUS_SUCCESS;
4750     }
4751 
4752     return Status;
4753 }
4754 
4755 _Dispatch_type_(IRP_MJ_FILE_SYSTEM_CONTROL)
4756 _Function_class_(DRIVER_DISPATCH)
4757 static NTSTATUS drv_file_system_control(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) {
4758     PIO_STACK_LOCATION IrpSp;
4759     NTSTATUS Status;
4760     device_extension* Vcb = DeviceObject->DeviceExtension;
4761     BOOL top_level;
4762 
4763     FsRtlEnterFileSystem();
4764 
4765     TRACE("file system control\n");
4766 
4767     top_level = is_top_level(Irp);
4768 
4769     if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
4770         Status = vol_file_system_control(DeviceObject, Irp);
4771         goto end;
4772     } else if (!Vcb || (Vcb->type != VCB_TYPE_FS && Vcb->type != VCB_TYPE_CONTROL)) {
4773         Status = STATUS_INVALID_PARAMETER;
4774         goto end;
4775     }
4776 
4777     Status = STATUS_NOT_IMPLEMENTED;
4778 
4779     IrpSp = IoGetCurrentIrpStackLocation( Irp );
4780 
4781     Irp->IoStatus.Information = 0;
4782 
4783     switch (IrpSp->MinorFunction) {
4784         case IRP_MN_MOUNT_VOLUME:
4785             TRACE("IRP_MN_MOUNT_VOLUME\n");
4786 
4787             Status = mount_vol(DeviceObject, Irp);
4788             break;
4789 
4790         case IRP_MN_KERNEL_CALL:
4791             TRACE("IRP_MN_KERNEL_CALL\n");
4792 
4793             Status = fsctl_request(DeviceObject, &Irp, IrpSp->Parameters.FileSystemControl.FsControlCode);
4794             break;
4795 
4796         case IRP_MN_USER_FS_REQUEST:
4797             TRACE("IRP_MN_USER_FS_REQUEST\n");
4798 
4799             Status = fsctl_request(DeviceObject, &Irp, IrpSp->Parameters.FileSystemControl.FsControlCode);
4800             break;
4801 
4802         case IRP_MN_VERIFY_VOLUME:
4803             TRACE("IRP_MN_VERIFY_VOLUME\n");
4804 
4805             Status = verify_volume(DeviceObject);
4806 
4807             if (!NT_SUCCESS(Status) && Vcb->Vpb->Flags & VPB_MOUNTED) {
4808                 ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE);
4809                 Vcb->removing = TRUE;
4810                 ExReleaseResourceLite(&Vcb->tree_lock);
4811             }
4812 
4813             break;
4814 
4815         default:
4816             break;
4817     }
4818 
4819 end:
4820     TRACE("returning %08x\n", Status);
4821 
4822     if (Irp) {
4823         Irp->IoStatus.Status = Status;
4824 
4825         IoCompleteRequest(Irp, IO_NO_INCREMENT);
4826     }
4827 
4828     if (top_level)
4829         IoSetTopLevelIrp(NULL);
4830 
4831     FsRtlExitFileSystem();
4832 
4833     return Status;
4834 }
4835 
4836 _Dispatch_type_(IRP_MJ_LOCK_CONTROL)
4837 _Function_class_(DRIVER_DISPATCH)
4838 static NTSTATUS drv_lock_control(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) {
4839     NTSTATUS Status;
4840     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
4841     fcb* fcb = IrpSp->FileObject->FsContext;
4842     device_extension* Vcb = DeviceObject->DeviceExtension;
4843     BOOL top_level;
4844 
4845     FsRtlEnterFileSystem();
4846 
4847     top_level = is_top_level(Irp);
4848 
4849     if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
4850         Status = vol_lock_control(DeviceObject, Irp);
4851 
4852         Irp->IoStatus.Status = Status;
4853         IoCompleteRequest(Irp, IO_NO_INCREMENT);
4854 
4855         goto exit;
4856     }
4857 
4858     TRACE("lock control\n");
4859 
4860     Status = FsRtlProcessFileLock(&fcb->lock, Irp, NULL);
4861 
4862     fcb->Header.IsFastIoPossible = fast_io_possible(fcb);
4863 
4864 exit:
4865     TRACE("returning %08x\n", Status);
4866 
4867     if (top_level)
4868         IoSetTopLevelIrp(NULL);
4869 
4870     FsRtlExitFileSystem();
4871 
4872     return Status;
4873 }
4874 
4875 _Dispatch_type_(IRP_MJ_SHUTDOWN)
4876 _Function_class_(DRIVER_DISPATCH)
4877 static NTSTATUS drv_shutdown(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) {
4878     NTSTATUS Status;
4879     BOOL top_level;
4880     device_extension* Vcb = DeviceObject->DeviceExtension;
4881 
4882     FsRtlEnterFileSystem();
4883 
4884     TRACE("shutdown\n");
4885 
4886     top_level = is_top_level(Irp);
4887 
4888     if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
4889         Status = vol_shutdown(DeviceObject, Irp);
4890         goto end;
4891     }
4892 
4893     Status = STATUS_SUCCESS;
4894 
4895     shutting_down = TRUE;
4896     KeSetEvent(&mountmgr_thread_event, 0, FALSE);
4897 
4898     while (!IsListEmpty(&VcbList)) {
4899         Vcb = CONTAINING_RECORD(VcbList.Flink, device_extension, list_entry);
4900 
4901         TRACE("shutting down Vcb %p\n", Vcb);
4902 
4903         uninit(Vcb, TRUE);
4904     }
4905 
4906 #ifdef _DEBUG
4907     if (comfo) {
4908         ObDereferenceObject(comfo);
4909         comdo = NULL;
4910         comfo = NULL;
4911     }
4912 #endif
4913 
4914 end:
4915     Irp->IoStatus.Status = Status;
4916     Irp->IoStatus.Information = 0;
4917 
4918     IoCompleteRequest( Irp, IO_NO_INCREMENT );
4919 
4920     if (top_level)
4921         IoSetTopLevelIrp(NULL);
4922 
4923     FsRtlExitFileSystem();
4924 
4925     return Status;
4926 }
4927 
4928 _Dispatch_type_(IRP_MJ_POWER)
4929 _Function_class_(DRIVER_DISPATCH)
4930 static NTSTATUS drv_power(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) {
4931     NTSTATUS Status;
4932     device_extension* Vcb = DeviceObject->DeviceExtension;
4933     BOOL top_level;
4934 
4935     FsRtlEnterFileSystem();
4936 
4937     top_level = is_top_level(Irp);
4938 
4939     Irp->IoStatus.Information = 0;
4940 
4941     if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
4942         Status = vol_power(DeviceObject, Irp);
4943 
4944         Irp->IoStatus.Status = Status;
4945         IoCompleteRequest(Irp, IO_NO_INCREMENT);
4946 
4947         goto exit;
4948     } else if (Vcb && Vcb->type == VCB_TYPE_FS) {
4949         IoSkipCurrentIrpStackLocation(Irp);
4950 
4951         Status = IoCallDriver(Vcb->Vpb->RealDevice, Irp);
4952 
4953         goto exit;
4954     }
4955 
4956     Status = STATUS_INVALID_DEVICE_REQUEST;
4957     Irp->IoStatus.Status = Status;
4958     IoCompleteRequest(Irp, IO_NO_INCREMENT);
4959 
4960 exit:
4961     if (top_level)
4962         IoSetTopLevelIrp(NULL);
4963 
4964     FsRtlExitFileSystem();
4965 
4966     return Status;
4967 }
4968 
4969 _Dispatch_type_(IRP_MJ_SYSTEM_CONTROL)
4970 _Function_class_(DRIVER_DISPATCH)
4971 static NTSTATUS drv_system_control(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) {
4972     NTSTATUS Status;
4973     device_extension* Vcb = DeviceObject->DeviceExtension;
4974     BOOL top_level;
4975 
4976     FsRtlEnterFileSystem();
4977 
4978     top_level = is_top_level(Irp);
4979 
4980     Irp->IoStatus.Information = 0;
4981 
4982     if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
4983         volume_device_extension* vde = DeviceObject->DeviceExtension;
4984 
4985         IoSkipCurrentIrpStackLocation(Irp);
4986 
4987         Status = IoCallDriver(vde->pdo, Irp);
4988 
4989         goto exit;
4990     } else if (Vcb && Vcb->type == VCB_TYPE_FS) {
4991         IoSkipCurrentIrpStackLocation(Irp);
4992 
4993         Status = IoCallDriver(Vcb->Vpb->RealDevice, Irp);
4994 
4995         goto exit;
4996     }
4997 
4998     Status = Irp->IoStatus.Status;
4999     IoCompleteRequest(Irp, IO_NO_INCREMENT);
5000 
5001 exit:
5002     if (top_level)
5003         IoSetTopLevelIrp(NULL);
5004 
5005     FsRtlExitFileSystem();
5006 
5007     return Status;
5008 }
5009 
5010 BOOL is_file_name_valid(_In_ PUNICODE_STRING us, _In_ BOOL posix) {
5011     ULONG i;
5012 
5013     if (us->Length < sizeof(WCHAR))
5014         return FALSE;
5015 
5016     if (us->Length > 255 * sizeof(WCHAR))
5017         return FALSE;
5018 
5019     for (i = 0; i < us->Length / sizeof(WCHAR); i++) {
5020         if (us->Buffer[i] == '/' || us->Buffer[i] == 0 ||
5021             (!posix && (us->Buffer[i] == '<' || us->Buffer[i] == '>' || us->Buffer[i] == ':' || us->Buffer[i] == '"' ||
5022             us->Buffer[i] == '|' || us->Buffer[i] == '?' || us->Buffer[i] == '*' || (us->Buffer[i] >= 1 && us->Buffer[i] <= 31))))
5023             return FALSE;
5024     }
5025 
5026     if (us->Buffer[0] == '.' && (us->Length == sizeof(WCHAR) || (us->Length == 2 * sizeof(WCHAR) && us->Buffer[1] == '.')))
5027         return FALSE;
5028 
5029     return TRUE;
5030 }
5031 
5032 void chunk_lock_range(_In_ device_extension* Vcb, _In_ chunk* c, _In_ UINT64 start, _In_ UINT64 length) {
5033     LIST_ENTRY* le;
5034     BOOL locked;
5035     range_lock* rl;
5036 
5037     rl = ExAllocateFromNPagedLookasideList(&Vcb->range_lock_lookaside);
5038     if (!rl) {
5039         ERR("out of memory\n");
5040         return;
5041     }
5042 
5043     rl->start = start;
5044     rl->length = length;
5045     rl->thread = PsGetCurrentThread();
5046 
5047     while (TRUE) {
5048         locked = FALSE;
5049 
5050         ExAcquireResourceExclusiveLite(&c->range_locks_lock, TRUE);
5051 
5052         le = c->range_locks.Flink;
5053         while (le != &c->range_locks) {
5054             range_lock* rl2 = CONTAINING_RECORD(le, range_lock, list_entry);
5055 
5056             if (rl2->start < start + length && rl2->start + rl2->length > start && rl2->thread != PsGetCurrentThread()) {
5057                 locked = TRUE;
5058                 break;
5059             }
5060 
5061             le = le->Flink;
5062         }
5063 
5064         if (!locked) {
5065             InsertTailList(&c->range_locks, &rl->list_entry);
5066 
5067             ExReleaseResourceLite(&c->range_locks_lock);
5068             return;
5069         }
5070 
5071         KeClearEvent(&c->range_locks_event);
5072 
5073         ExReleaseResourceLite(&c->range_locks_lock);
5074 
5075         KeWaitForSingleObject(&c->range_locks_event, UserRequest, KernelMode, FALSE, NULL);
5076     }
5077 }
5078 
5079 void chunk_unlock_range(_In_ device_extension* Vcb, _In_ chunk* c, _In_ UINT64 start, _In_ UINT64 length) {
5080     LIST_ENTRY* le;
5081 
5082     ExAcquireResourceExclusiveLite(&c->range_locks_lock, TRUE);
5083 
5084     le = c->range_locks.Flink;
5085     while (le != &c->range_locks) {
5086         range_lock* rl = CONTAINING_RECORD(le, range_lock, list_entry);
5087 
5088         if (rl->start == start && rl->length == length) {
5089             RemoveEntryList(&rl->list_entry);
5090             ExFreeToNPagedLookasideList(&Vcb->range_lock_lookaside, rl);
5091             break;
5092         }
5093 
5094         le = le->Flink;
5095     }
5096 
5097     KeSetEvent(&c->range_locks_event, 0, FALSE);
5098 
5099     ExReleaseResourceLite(&c->range_locks_lock);
5100 }
5101 
5102 void log_device_error(_In_ device_extension* Vcb, _Inout_ device* dev, _In_ int error) {
5103     dev->stats[error]++;
5104     dev->stats_changed = TRUE;
5105     Vcb->stats_changed = TRUE;
5106 }
5107 
5108 #ifdef _DEBUG
5109 _Function_class_(KSTART_ROUTINE)
5110 static void serial_thread(void* context) {
5111     LARGE_INTEGER due_time;
5112     KTIMER timer;
5113 
5114     UNUSED(context);
5115 
5116     KeInitializeTimer(&timer);
5117 
5118     due_time.QuadPart = (UINT64)-10000000;
5119 
5120     KeSetTimer(&timer, due_time, NULL);
5121 
5122     while (TRUE) {
5123         KeWaitForSingleObject(&timer, Executive, KernelMode, FALSE, NULL);
5124 
5125         init_serial(FALSE);
5126 
5127         if (comdo)
5128             break;
5129 
5130         KeSetTimer(&timer, due_time, NULL);
5131     }
5132 
5133     KeCancelTimer(&timer);
5134 
5135     PsTerminateSystemThread(STATUS_SUCCESS);
5136 
5137     serial_thread_handle = NULL;
5138 }
5139 
5140 static void init_serial(BOOL first_time) {
5141     NTSTATUS Status;
5142 
5143     Status = IoGetDeviceObjectPointer(&log_device, FILE_WRITE_DATA, &comfo, &comdo);
5144     if (!NT_SUCCESS(Status)) {
5145         ERR("IoGetDeviceObjectPointer returned %08x\n", Status);
5146 
5147         if (first_time) {
5148             NTSTATUS Status;
5149 
5150             Status = PsCreateSystemThread(&serial_thread_handle, 0, NULL, NULL, NULL, serial_thread, NULL);
5151             if (!NT_SUCCESS(Status)) {
5152                 ERR("PsCreateSystemThread returned %08x\n", Status);
5153                 return;
5154             }
5155         }
5156     }
5157 }
5158 #endif
5159 
5160 #ifndef __REACTOS__
5161 static void check_cpu() {
5162     unsigned int cpuInfo[4];
5163 #ifndef _MSC_VER
5164     __get_cpuid(1, &cpuInfo[0], &cpuInfo[1], &cpuInfo[2], &cpuInfo[3]);
5165     have_sse42 = cpuInfo[2] & bit_SSE4_2;
5166     have_sse2 = cpuInfo[3] & bit_SSE2;
5167 #else
5168    __cpuid(cpuInfo, 1);
5169    have_sse42 = cpuInfo[2] & (1 << 20);
5170    have_sse2 = cpuInfo[3] & (1 << 26);
5171 #endif
5172 
5173     if (have_sse42)
5174         TRACE("SSE4.2 is supported\n");
5175     else
5176         TRACE("SSE4.2 not supported\n");
5177 
5178     if (have_sse2)
5179         TRACE("SSE2 is supported\n");
5180     else
5181         TRACE("SSE2 is not supported\n");
5182 }
5183 #endif
5184 
5185 #ifdef _DEBUG
5186 static void init_logging() {
5187     ExAcquireResourceExclusiveLite(&log_lock, TRUE);
5188 
5189     if (log_device.Length > 0)
5190         init_serial(TRUE);
5191     else if (log_file.Length > 0) {
5192         NTSTATUS Status;
5193         OBJECT_ATTRIBUTES oa;
5194         IO_STATUS_BLOCK iosb;
5195         char* dateline;
5196         LARGE_INTEGER time;
5197         TIME_FIELDS tf;
5198 
5199         InitializeObjectAttributes(&oa, &log_file, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
5200 
5201         Status = ZwCreateFile(&log_handle, FILE_WRITE_DATA, &oa, &iosb, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ,
5202                               FILE_OPEN_IF, FILE_NON_DIRECTORY_FILE | FILE_WRITE_THROUGH | FILE_SYNCHRONOUS_IO_ALERT, NULL, 0);
5203 
5204         if (!NT_SUCCESS(Status)) {
5205             ERR("ZwCreateFile returned %08x\n", Status);
5206             goto end;
5207         }
5208 
5209         if (iosb.Information == FILE_OPENED) { // already exists
5210             FILE_STANDARD_INFORMATION fsi;
5211             FILE_POSITION_INFORMATION fpi;
5212 
5213             static char delim[] = "\n---\n";
5214 
5215             // move to end of file
5216 
5217             Status = ZwQueryInformationFile(log_handle, &iosb, &fsi, sizeof(FILE_STANDARD_INFORMATION), FileStandardInformation);
5218 
5219             if (!NT_SUCCESS(Status)) {
5220                 ERR("ZwQueryInformationFile returned %08x\n", Status);
5221                 goto end;
5222             }
5223 
5224             fpi.CurrentByteOffset = fsi.EndOfFile;
5225 
5226             Status = ZwSetInformationFile(log_handle, &iosb, &fpi, sizeof(FILE_POSITION_INFORMATION), FilePositionInformation);
5227 
5228             if (!NT_SUCCESS(Status)) {
5229                 ERR("ZwSetInformationFile returned %08x\n", Status);
5230                 goto end;
5231             }
5232 
5233             Status = ZwWriteFile(log_handle, NULL, NULL, NULL, &iosb, delim, (ULONG)strlen(delim), NULL, NULL);
5234 
5235             if (!NT_SUCCESS(Status)) {
5236                 ERR("ZwWriteFile returned %08x\n", Status);
5237                 goto end;
5238             }
5239         }
5240 
5241         dateline = ExAllocatePoolWithTag(PagedPool, 256, ALLOC_TAG);
5242 
5243         if (!dateline) {
5244             ERR("out of memory\n");
5245             goto end;
5246         }
5247 
5248         KeQuerySystemTime(&time);
5249 
5250         RtlTimeToTimeFields(&time, &tf);
5251 
5252         sprintf(dateline, "Starting logging at %04i-%02i-%02i %02i:%02i:%02i\n", tf.Year, tf.Month, tf.Day, tf.Hour, tf.Minute, tf.Second);
5253 
5254         Status = ZwWriteFile(log_handle, NULL, NULL, NULL, &iosb, dateline, (ULONG)strlen(dateline), NULL, NULL);
5255 
5256         ExFreePool(dateline);
5257 
5258         if (!NT_SUCCESS(Status)) {
5259             ERR("ZwWriteFile returned %08x\n", Status);
5260             goto end;
5261         }
5262     }
5263 
5264 end:
5265     ExReleaseResourceLite(&log_lock);
5266 }
5267 #endif
5268 
5269 _Function_class_(KSTART_ROUTINE)
5270 #ifdef __REACTOS__
5271 static void NTAPI degraded_wait_thread(_In_ void* context) {
5272 #else
5273 static void degraded_wait_thread(_In_ void* context) {
5274 #endif
5275     KTIMER timer;
5276     LARGE_INTEGER delay;
5277 
5278     UNUSED(context);
5279 
5280     KeInitializeTimer(&timer);
5281 
5282     delay.QuadPart = -30000000; // wait three seconds
5283     KeSetTimer(&timer, delay, NULL);
5284     KeWaitForSingleObject(&timer, Executive, KernelMode, FALSE, NULL);
5285 
5286     TRACE("timer expired\n");
5287 
5288     degraded_wait = FALSE;
5289 
5290     ZwClose(degraded_wait_handle);
5291     degraded_wait_handle = NULL;
5292 
5293     PsTerminateSystemThread(STATUS_SUCCESS);
5294 }
5295 
5296 #ifdef __REACTOS__
5297 NTSTATUS NTAPI AddDevice(PDRIVER_OBJECT DriverObject, PDEVICE_OBJECT PhysicalDeviceObject) {
5298 #else
5299 NTSTATUS AddDevice(PDRIVER_OBJECT DriverObject, PDEVICE_OBJECT PhysicalDeviceObject) {
5300 #endif
5301     LIST_ENTRY* le;
5302     NTSTATUS Status;
5303     UNICODE_STRING volname;
5304     ULONG i, j;
5305     pdo_device_extension* pdode = NULL;
5306     PDEVICE_OBJECT voldev;
5307     volume_device_extension* vde;
5308 
5309     TRACE("(%p, %p)\n", DriverObject, PhysicalDeviceObject);
5310 
5311     ExAcquireResourceSharedLite(&pdo_list_lock, TRUE);
5312 
5313     le = pdo_list.Flink;
5314     while (le != &pdo_list) {
5315         pdo_device_extension* pdode2 = CONTAINING_RECORD(le, pdo_device_extension, list_entry);
5316 
5317         if (pdode2->pdo == PhysicalDeviceObject) {
5318             pdode = pdode2;
5319             break;
5320         }
5321 
5322         le = le->Flink;
5323     }
5324 
5325     if (!pdode) {
5326         WARN("unrecognized PDO %p\n", PhysicalDeviceObject);
5327         Status = STATUS_NOT_SUPPORTED;
5328         goto end;
5329     }
5330 
5331     ExAcquireResourceSharedLite(&pdode->child_lock, TRUE);
5332 
5333     volname.Length = volname.MaximumLength = (USHORT)((wcslen(BTRFS_VOLUME_PREFIX) + 36 + 1) * sizeof(WCHAR));
5334     volname.Buffer = ExAllocatePoolWithTag(PagedPool, volname.MaximumLength, ALLOC_TAG); // FIXME - when do we free this?
5335 
5336     if (!volname.Buffer) {
5337         ERR("out of memory\n");
5338         Status = STATUS_INSUFFICIENT_RESOURCES;
5339         goto end2;
5340     }
5341 
5342     RtlCopyMemory(volname.Buffer, BTRFS_VOLUME_PREFIX, wcslen(BTRFS_VOLUME_PREFIX) * sizeof(WCHAR));
5343 
5344     j = (ULONG)wcslen(BTRFS_VOLUME_PREFIX);
5345     for (i = 0; i < 16; i++) {
5346         volname.Buffer[j] = hex_digit(pdode->uuid.uuid[i] >> 4); j++;
5347         volname.Buffer[j] = hex_digit(pdode->uuid.uuid[i] & 0xf); j++;
5348 
5349         if (i == 3 || i == 5 || i == 7 || i == 9) {
5350             volname.Buffer[j] = '-';
5351             j++;
5352         }
5353     }
5354 
5355     volname.Buffer[j] = '}';
5356 
5357     Status = IoCreateDevice(drvobj, sizeof(volume_device_extension), &volname, FILE_DEVICE_DISK,
5358                             RtlIsNtDdiVersionAvailable(NTDDI_WIN8) ? FILE_DEVICE_ALLOW_APPCONTAINER_TRAVERSAL : 0, FALSE, &voldev);
5359     if (!NT_SUCCESS(Status)) {
5360         ERR("IoCreateDevice returned %08x\n", Status);
5361         goto end2;
5362     }
5363 
5364     voldev->SectorSize = PhysicalDeviceObject->SectorSize;
5365     voldev->Flags |= DO_DIRECT_IO;
5366 
5367     vde = voldev->DeviceExtension;
5368     vde->type = VCB_TYPE_VOLUME;
5369     vde->name = volname;
5370     vde->device = voldev;
5371     vde->mounted_device = NULL;
5372     vde->pdo = PhysicalDeviceObject;
5373     vde->pdode = pdode;
5374     vde->removing = FALSE;
5375     vde->open_count = 0;
5376 
5377     Status = IoRegisterDeviceInterface(PhysicalDeviceObject, &GUID_DEVINTERFACE_VOLUME, NULL, &vde->bus_name);
5378     if (!NT_SUCCESS(Status))
5379         WARN("IoRegisterDeviceInterface returned %08x\n", Status);
5380 
5381     vde->attached_device = IoAttachDeviceToDeviceStack(voldev, PhysicalDeviceObject);
5382 
5383     pdode->vde = vde;
5384 
5385     if (pdode->removable)
5386         voldev->Characteristics |= FILE_REMOVABLE_MEDIA;
5387 
5388     voldev->Flags &= ~DO_DEVICE_INITIALIZING;
5389 
5390     Status = IoSetDeviceInterfaceState(&vde->bus_name, TRUE);
5391     if (!NT_SUCCESS(Status))
5392         WARN("IoSetDeviceInterfaceState returned %08x\n", Status);
5393 
5394     Status = STATUS_SUCCESS;
5395 
5396 end2:
5397     ExReleaseResourceLite(&pdode->child_lock);
5398 
5399 end:
5400     ExReleaseResourceLite(&pdo_list_lock);
5401 
5402     return Status;
5403 }
5404 
5405 _Function_class_(DRIVER_INITIALIZE)
5406 #ifdef __REACTOS__
5407 NTSTATUS NTAPI DriverEntry(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath) {
5408 #else
5409 NTSTATUS DriverEntry(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath) {
5410 #endif
5411     NTSTATUS Status;
5412     PDEVICE_OBJECT DeviceObject;
5413     UNICODE_STRING device_nameW;
5414     UNICODE_STRING dosdevice_nameW;
5415     control_device_extension* cde;
5416     HANDLE regh;
5417     OBJECT_ATTRIBUTES oa;
5418     ULONG dispos;
5419 
5420     InitializeListHead(&uid_map_list);
5421     InitializeListHead(&gid_map_list);
5422 
5423 #ifdef _DEBUG
5424     ExInitializeResourceLite(&log_lock);
5425 #endif
5426     ExInitializeResourceLite(&mapping_lock);
5427 
5428     log_device.Buffer = NULL;
5429     log_device.Length = log_device.MaximumLength = 0;
5430     log_file.Buffer = NULL;
5431     log_file.Length = log_file.MaximumLength = 0;
5432 
5433     registry_path.Length = registry_path.MaximumLength = RegistryPath->Length;
5434     registry_path.Buffer = ExAllocatePoolWithTag(PagedPool, registry_path.Length, ALLOC_TAG);
5435 
5436     if (!registry_path.Buffer) {
5437         ERR("out of memory\n");
5438         return STATUS_INSUFFICIENT_RESOURCES;
5439     }
5440 
5441     RtlCopyMemory(registry_path.Buffer, RegistryPath->Buffer, registry_path.Length);
5442 
5443     read_registry(&registry_path, FALSE);
5444 
5445 #ifdef _DEBUG
5446     if (debug_log_level > 0)
5447         init_logging();
5448 
5449     log_started = TRUE;
5450 #endif
5451 
5452     TRACE("DriverEntry\n");
5453 
5454 #ifndef __REACTOS__
5455     check_cpu();
5456 #endif
5457 
5458     if (RtlIsNtDdiVersionAvailable(NTDDI_WIN8)) {
5459         UNICODE_STRING name;
5460         tPsIsDiskCountersEnabled fPsIsDiskCountersEnabled;
5461 
5462         RtlInitUnicodeString(&name, L"PsIsDiskCountersEnabled");
5463         fPsIsDiskCountersEnabled = (tPsIsDiskCountersEnabled)MmGetSystemRoutineAddress(&name);
5464 
5465         if (fPsIsDiskCountersEnabled) {
5466             diskacc = fPsIsDiskCountersEnabled();
5467 
5468             RtlInitUnicodeString(&name, L"PsUpdateDiskCounters");
5469             fPsUpdateDiskCounters = (tPsUpdateDiskCounters)MmGetSystemRoutineAddress(&name);
5470 
5471             if (!fPsUpdateDiskCounters)
5472                 diskacc = FALSE;
5473 
5474             RtlInitUnicodeString(&name, L"FsRtlUpdateDiskCounters");
5475             fFsRtlUpdateDiskCounters = (tFsRtlUpdateDiskCounters)MmGetSystemRoutineAddress(&name);
5476         }
5477 
5478         RtlInitUnicodeString(&name, L"CcCopyReadEx");
5479         fCcCopyReadEx = (tCcCopyReadEx)MmGetSystemRoutineAddress(&name);
5480 
5481         RtlInitUnicodeString(&name, L"CcCopyWriteEx");
5482         fCcCopyWriteEx = (tCcCopyWriteEx)MmGetSystemRoutineAddress(&name);
5483 
5484         RtlInitUnicodeString(&name, L"CcSetAdditionalCacheAttributesEx");
5485         fCcSetAdditionalCacheAttributesEx = (tCcSetAdditionalCacheAttributesEx)MmGetSystemRoutineAddress(&name);
5486     } else {
5487         fPsUpdateDiskCounters = NULL;
5488         fCcCopyReadEx = NULL;
5489         fCcCopyWriteEx = NULL;
5490         fCcSetAdditionalCacheAttributesEx = NULL;
5491         fFsRtlUpdateDiskCounters = NULL;
5492     }
5493 
5494     drvobj = DriverObject;
5495 
5496     DriverObject->DriverUnload = DriverUnload;
5497 
5498     DriverObject->DriverExtension->AddDevice = AddDevice;
5499 
5500     DriverObject->MajorFunction[IRP_MJ_CREATE]                   = (PDRIVER_DISPATCH)drv_create;
5501     DriverObject->MajorFunction[IRP_MJ_CLOSE]                    = (PDRIVER_DISPATCH)drv_close;
5502     DriverObject->MajorFunction[IRP_MJ_READ]                     = (PDRIVER_DISPATCH)drv_read;
5503     DriverObject->MajorFunction[IRP_MJ_WRITE]                    = (PDRIVER_DISPATCH)drv_write;
5504     DriverObject->MajorFunction[IRP_MJ_QUERY_INFORMATION]        = (PDRIVER_DISPATCH)drv_query_information;
5505     DriverObject->MajorFunction[IRP_MJ_SET_INFORMATION]          = (PDRIVER_DISPATCH)drv_set_information;
5506     DriverObject->MajorFunction[IRP_MJ_QUERY_EA]                 = (PDRIVER_DISPATCH)drv_query_ea;
5507     DriverObject->MajorFunction[IRP_MJ_SET_EA]                   = (PDRIVER_DISPATCH)drv_set_ea;
5508     DriverObject->MajorFunction[IRP_MJ_FLUSH_BUFFERS]            = (PDRIVER_DISPATCH)drv_flush_buffers;
5509     DriverObject->MajorFunction[IRP_MJ_QUERY_VOLUME_INFORMATION] = (PDRIVER_DISPATCH)drv_query_volume_information;
5510     DriverObject->MajorFunction[IRP_MJ_SET_VOLUME_INFORMATION]   = (PDRIVER_DISPATCH)drv_set_volume_information;
5511     DriverObject->MajorFunction[IRP_MJ_DIRECTORY_CONTROL]        = (PDRIVER_DISPATCH)drv_directory_control;
5512     DriverObject->MajorFunction[IRP_MJ_FILE_SYSTEM_CONTROL]      = (PDRIVER_DISPATCH)drv_file_system_control;
5513     DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL]           = (PDRIVER_DISPATCH)drv_device_control;
5514     DriverObject->MajorFunction[IRP_MJ_SHUTDOWN]                 = (PDRIVER_DISPATCH)drv_shutdown;
5515     DriverObject->MajorFunction[IRP_MJ_LOCK_CONTROL]             = (PDRIVER_DISPATCH)drv_lock_control;
5516     DriverObject->MajorFunction[IRP_MJ_CLEANUP]                  = (PDRIVER_DISPATCH)drv_cleanup;
5517     DriverObject->MajorFunction[IRP_MJ_QUERY_SECURITY]           = (PDRIVER_DISPATCH)drv_query_security;
5518     DriverObject->MajorFunction[IRP_MJ_SET_SECURITY]             = (PDRIVER_DISPATCH)drv_set_security;
5519     DriverObject->MajorFunction[IRP_MJ_POWER]                    = (PDRIVER_DISPATCH)drv_power;
5520     DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL]           = (PDRIVER_DISPATCH)drv_system_control;
5521     DriverObject->MajorFunction[IRP_MJ_PNP]                      = (PDRIVER_DISPATCH)drv_pnp;
5522 
5523     init_fast_io_dispatch(&DriverObject->FastIoDispatch);
5524 
5525     device_nameW.Buffer = device_name;
5526     device_nameW.Length = device_nameW.MaximumLength = (USHORT)wcslen(device_name) * sizeof(WCHAR);
5527     dosdevice_nameW.Buffer = dosdevice_name;
5528     dosdevice_nameW.Length = dosdevice_nameW.MaximumLength = (USHORT)wcslen(dosdevice_name) * sizeof(WCHAR);
5529 
5530     Status = IoCreateDevice(DriverObject, sizeof(control_device_extension), &device_nameW, FILE_DEVICE_DISK_FILE_SYSTEM,
5531                             FILE_DEVICE_SECURE_OPEN, FALSE, &DeviceObject);
5532     if (!NT_SUCCESS(Status)) {
5533         ERR("IoCreateDevice returned %08x\n", Status);
5534         return Status;
5535     }
5536 
5537     master_devobj = DeviceObject;
5538     cde = (control_device_extension*)master_devobj->DeviceExtension;
5539 
5540     RtlZeroMemory(cde, sizeof(control_device_extension));
5541 
5542     cde->type = VCB_TYPE_CONTROL;
5543 
5544     DeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
5545 
5546     Status = IoCreateSymbolicLink(&dosdevice_nameW, &device_nameW);
5547     if (!NT_SUCCESS(Status)) {
5548         ERR("IoCreateSymbolicLink returned %08x\n", Status);
5549         return Status;
5550     }
5551 
5552     Status = init_cache();
5553     if (!NT_SUCCESS(Status)) {
5554         ERR("init_cache returned %08x\n", Status);
5555         return Status;
5556     }
5557 
5558     InitializeListHead(&VcbList);
5559     ExInitializeResourceLite(&global_loading_lock);
5560     ExInitializeResourceLite(&pdo_list_lock);
5561 
5562     InitializeListHead(&pdo_list);
5563 
5564     InitializeObjectAttributes(&oa, RegistryPath, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
5565     Status = ZwCreateKey(&regh, KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS | KEY_NOTIFY, &oa, 0, NULL, REG_OPTION_NON_VOLATILE, &dispos);
5566     if (!NT_SUCCESS(Status)) {
5567         ERR("ZwCreateKey returned %08x\n", Status);
5568         return Status;
5569     }
5570 
5571     watch_registry(regh);
5572 
5573     Status = IoReportDetectedDevice(drvobj, InterfaceTypeUndefined, 0xFFFFFFFF, 0xFFFFFFFF,
5574                                     NULL, NULL, 0, &cde->buspdo);
5575     if (!NT_SUCCESS(Status)) {
5576         ERR("IoReportDetectedDevice returned %08x\n", Status);
5577         return Status;
5578     }
5579 
5580     Status = IoRegisterDeviceInterface(cde->buspdo, &BtrfsBusInterface, NULL, &cde->bus_name);
5581     if (!NT_SUCCESS(Status))
5582         WARN("IoRegisterDeviceInterface returned %08x\n", Status);
5583 
5584     cde->attached_device = IoAttachDeviceToDeviceStack(DeviceObject, cde->buspdo);
5585 
5586     Status = IoSetDeviceInterfaceState(&cde->bus_name, TRUE);
5587     if (!NT_SUCCESS(Status))
5588         WARN("IoSetDeviceInterfaceState returned %08x\n", Status);
5589 
5590     DeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
5591 
5592     IoInvalidateDeviceRelations(cde->buspdo, BusRelations);
5593 
5594     Status = PsCreateSystemThread(&degraded_wait_handle, 0, NULL, NULL, NULL, degraded_wait_thread, NULL);
5595     if (!NT_SUCCESS(Status))
5596         WARN("PsCreateSystemThread returned %08x\n", Status);
5597 
5598     Status = IoRegisterPlugPlayNotification(EventCategoryDeviceInterfaceChange, PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES,
5599                                             (PVOID)&GUID_DEVINTERFACE_VOLUME, DriverObject, volume_notification, DriverObject, &notification_entry2);
5600     if (!NT_SUCCESS(Status))
5601         ERR("IoRegisterPlugPlayNotification returned %08x\n", Status);
5602 
5603     Status = IoRegisterPlugPlayNotification(EventCategoryDeviceInterfaceChange, PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES,
5604                                             (PVOID)&GUID_DEVINTERFACE_HIDDEN_VOLUME, DriverObject, volume_notification, DriverObject, &notification_entry3);
5605     if (!NT_SUCCESS(Status))
5606         ERR("IoRegisterPlugPlayNotification returned %08x\n", Status);
5607 
5608     Status = IoRegisterPlugPlayNotification(EventCategoryDeviceInterfaceChange, PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES,
5609                                             (PVOID)&GUID_DEVINTERFACE_DISK, DriverObject, pnp_notification, DriverObject, &notification_entry);
5610     if (!NT_SUCCESS(Status))
5611         ERR("IoRegisterPlugPlayNotification returned %08x\n", Status);
5612 
5613     finished_probing = TRUE;
5614 
5615     KeInitializeEvent(&mountmgr_thread_event, NotificationEvent, FALSE);
5616 
5617 #ifndef __REACTOS__
5618     Status = PsCreateSystemThread(&mountmgr_thread_handle, 0, NULL, NULL, NULL, mountmgr_thread, NULL);
5619     if (!NT_SUCCESS(Status))
5620         WARN("PsCreateSystemThread returned %08x\n", Status);
5621 #endif
5622 
5623     IoRegisterFileSystem(DeviceObject);
5624 
5625     return STATUS_SUCCESS;
5626 }
5627