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