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