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