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