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