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