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