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