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