1 /* Copyright (c) Mark Harmstone 2016-17
3 * This file is part of WinBtrfs.
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.
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.
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/>. */
22 #include "btrfs_drv.h"
52 #include <ntstrsafe.h>
54 NTSTATUS
RtlStringCbVPrintfA(char* pszDest
, size_t cbDest
, const char* pszFormat
, va_list argList
); // not in mingw
57 #define INCOMPAT_SUPPORTED (BTRFS_INCOMPAT_FLAGS_MIXED_BACKREF | BTRFS_INCOMPAT_FLAGS_DEFAULT_SUBVOL | BTRFS_INCOMPAT_FLAGS_MIXED_GROUPS | \
58 BTRFS_INCOMPAT_FLAGS_COMPRESS_LZO | BTRFS_INCOMPAT_FLAGS_BIG_METADATA | BTRFS_INCOMPAT_FLAGS_RAID56 | \
59 BTRFS_INCOMPAT_FLAGS_EXTENDED_IREF | BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA | BTRFS_INCOMPAT_FLAGS_NO_HOLES | \
60 BTRFS_INCOMPAT_FLAGS_COMPRESS_ZSTD | BTRFS_INCOMPAT_FLAGS_METADATA_UUID | BTRFS_INCOMPAT_FLAGS_RAID1C34)
61 #define COMPAT_RO_SUPPORTED (BTRFS_COMPAT_RO_FLAGS_FREE_SPACE_CACHE | BTRFS_COMPAT_RO_FLAGS_FREE_SPACE_CACHE_VALID)
63 static const WCHAR device_name
[] = {'\\','B','t','r','f','s',0};
64 static const WCHAR dosdevice_name
[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\','B','t','r','f','s',0};
66 DEFINE_GUID(BtrfsBusInterface
, 0x4d414874, 0x6865, 0x6761, 0x6d, 0x65, 0x83, 0x69, 0x17, 0x9a, 0x7d, 0x1d);
68 PDRIVER_OBJECT drvobj
;
69 PDEVICE_OBJECT master_devobj
, busobj
;
71 bool have_sse2
= false;
73 uint64_t num_reads
= 0;
74 LIST_ENTRY uid_map_list
, gid_map_list
;
76 ERESOURCE global_loading_lock
;
77 uint32_t debug_log_level
= 0;
78 uint32_t mount_compress
= 0;
79 uint32_t mount_compress_force
= 0;
80 uint32_t mount_compress_type
= 0;
81 uint32_t mount_zlib_level
= 3;
82 uint32_t mount_zstd_level
= 3;
83 uint32_t mount_flush_interval
= 30;
84 uint32_t mount_max_inline
= 2048;
85 uint32_t mount_skip_balance
= 0;
86 uint32_t mount_no_barrier
= 0;
87 uint32_t mount_no_trim
= 0;
88 uint32_t mount_clear_cache
= 0;
89 uint32_t mount_allow_degraded
= 0;
90 uint32_t mount_readonly
= 0;
91 uint32_t mount_no_root_dir
= 0;
93 bool log_started
= false;
94 UNICODE_STRING log_device
, log_file
, registry_path
;
95 tPsUpdateDiskCounters fPsUpdateDiskCounters
;
96 tCcCopyReadEx fCcCopyReadEx
;
97 tCcCopyWriteEx fCcCopyWriteEx
;
98 tCcSetAdditionalCacheAttributesEx fCcSetAdditionalCacheAttributesEx
;
99 tFsRtlUpdateDiskCounters fFsRtlUpdateDiskCounters
;
100 tIoUnregisterPlugPlayNotificationEx fIoUnregisterPlugPlayNotificationEx
;
101 tFsRtlGetEcpListFromIrp fFsRtlGetEcpListFromIrp
;
102 tFsRtlGetNextExtraCreateParameter fFsRtlGetNextExtraCreateParameter
;
103 tFsRtlValidateReparsePointBuffer fFsRtlValidateReparsePointBuffer
;
104 tFsRtlCheckLockForOplockRequest fFsRtlCheckLockForOplockRequest
;
105 tFsRtlAreThereCurrentOrInProgressFileLocks fFsRtlAreThereCurrentOrInProgressFileLocks
;
106 bool diskacc
= false;
107 void *notification_entry
= NULL
, *notification_entry2
= NULL
, *notification_entry3
= NULL
;
108 ERESOURCE pdo_list_lock
, mapping_lock
;
110 bool finished_probing
= false;
111 HANDLE degraded_wait_handle
= NULL
, mountmgr_thread_handle
= NULL
;
112 bool degraded_wait
= true;
113 KEVENT mountmgr_thread_event
;
114 bool shutting_down
= false;
116 extern uint64_t boot_subvol
;
119 PFILE_OBJECT comfo
= NULL
;
120 PDEVICE_OBJECT comdo
= NULL
;
121 HANDLE log_handle
= NULL
;
123 HANDLE serial_thread_handle
= NULL
;
125 static void init_serial(bool first_time
);
128 static NTSTATUS
close_file(_In_ PFILE_OBJECT FileObject
, _In_ PIRP Irp
);
132 IO_STATUS_BLOCK iosb
;
135 // no longer in Windows headers??
136 extern BOOLEAN
WdmlibRtlIsNtDdiVersionAvailable(ULONG Version
);
139 _Function_class_(IO_COMPLETION_ROUTINE
)
140 static NTSTATUS __stdcall
dbg_completion(_In_ PDEVICE_OBJECT DeviceObject
, _In_ PIRP Irp
, _In_ PVOID conptr
) {
141 read_context
* context
= conptr
;
143 UNUSED(DeviceObject
);
145 context
->iosb
= Irp
->IoStatus
;
146 KeSetEvent(&context
->Event
, 0, false);
148 return STATUS_MORE_PROCESSING_REQUIRED
;
151 #define DEBUG_MESSAGE_LEN 1024
153 #ifdef DEBUG_LONG_MESSAGES
154 void _debug_message(_In_
const char* func
, _In_
const char* file
, _In_
unsigned int line
, _In_
char* s
, ...) {
156 void _debug_message(_In_
const char* func
, _In_
char* s
, ...) {
158 LARGE_INTEGER offset
;
159 PIO_STACK_LOCATION IrpSp
;
164 read_context context
;
167 buf2
= ExAllocatePoolWithTag(NonPagedPool
, DEBUG_MESSAGE_LEN
, ALLOC_TAG
);
170 DbgPrint("Couldn't allocate buffer in debug_message\n");
174 #ifdef DEBUG_LONG_MESSAGES
175 sprintf(buf2
, "%p:%s:%s:%u:", (void*)PsGetCurrentThread(), func
, file
, line
);
177 sprintf(buf2
, "%p:%s:", (void*)PsGetCurrentThread(), func
);
179 buf
= &buf2
[strlen(buf2
)];
183 RtlStringCbVPrintfA(buf
, DEBUG_MESSAGE_LEN
- strlen(buf2
), s
, ap
);
185 ExAcquireResourceSharedLite(&log_lock
, true);
187 if (!log_started
|| (log_device
.Length
== 0 && log_file
.Length
== 0)) {
189 } else if (log_device
.Length
> 0) {
195 length
= (uint32_t)strlen(buf2
);
197 offset
.u
.LowPart
= 0;
198 offset
.u
.HighPart
= 0;
200 RtlZeroMemory(&context
, sizeof(read_context
));
202 KeInitializeEvent(&context
.Event
, NotificationEvent
, false);
204 Irp
= IoAllocateIrp(comdo
->StackSize
, false);
207 DbgPrint("IoAllocateIrp failed\n");
211 IrpSp
= IoGetNextIrpStackLocation(Irp
);
212 IrpSp
->MajorFunction
= IRP_MJ_WRITE
;
213 IrpSp
->FileObject
= comfo
;
215 if (comdo
->Flags
& DO_BUFFERED_IO
) {
216 Irp
->AssociatedIrp
.SystemBuffer
= buf2
;
218 Irp
->Flags
= IRP_BUFFERED_IO
;
219 } else if (comdo
->Flags
& DO_DIRECT_IO
) {
220 Irp
->MdlAddress
= IoAllocateMdl(buf2
, length
, false, false, NULL
);
221 if (!Irp
->MdlAddress
) {
222 DbgPrint("IoAllocateMdl failed\n");
226 MmBuildMdlForNonPagedPool(Irp
->MdlAddress
);
228 Irp
->UserBuffer
= buf2
;
231 IrpSp
->Parameters
.Write
.Length
= length
;
232 IrpSp
->Parameters
.Write
.ByteOffset
= offset
;
234 Irp
->UserIosb
= &context
.iosb
;
236 Irp
->UserEvent
= &context
.Event
;
238 IoSetCompletionRoutine(Irp
, dbg_completion
, &context
, true, true, true);
240 Status
= IoCallDriver(comdo
, Irp
);
242 if (Status
== STATUS_PENDING
) {
243 KeWaitForSingleObject(&context
.Event
, Executive
, KernelMode
, false, NULL
);
244 Status
= context
.iosb
.Status
;
247 if (comdo
->Flags
& DO_DIRECT_IO
)
248 IoFreeMdl(Irp
->MdlAddress
);
250 if (!NT_SUCCESS(Status
)) {
251 DbgPrint("failed to write to COM1 - error %08lx\n", Status
);
257 } else if (log_handle
!= NULL
) {
258 IO_STATUS_BLOCK iosb
;
260 length
= (uint32_t)strlen(buf2
);
262 Status
= ZwWriteFile(log_handle
, NULL
, NULL
, NULL
, &iosb
, buf2
, length
, NULL
, NULL
);
264 if (!NT_SUCCESS(Status
)) {
265 DbgPrint("failed to write to file - error %08lx\n", Status
);
270 ExReleaseResourceLite(&log_lock
);
279 bool is_top_level(_In_ PIRP Irp
) {
280 if (!IoGetTopLevelIrp()) {
281 IoSetTopLevelIrp(Irp
);
288 _Function_class_(DRIVER_UNLOAD
)
289 static void __stdcall
DriverUnload(_In_ PDRIVER_OBJECT DriverObject
) {
290 UNICODE_STRING dosdevice_nameW
;
292 TRACE("(%p)\n", DriverObject
);
294 dosdevice_nameW
.Buffer
= (WCHAR
*)dosdevice_name
;
295 dosdevice_nameW
.Length
= dosdevice_nameW
.MaximumLength
= sizeof(dosdevice_name
) - sizeof(WCHAR
);
297 IoDeleteSymbolicLink(&dosdevice_nameW
);
298 IoDeleteDevice(DriverObject
->DeviceObject
);
300 while (!IsListEmpty(&uid_map_list
)) {
301 LIST_ENTRY
* le
= RemoveHeadList(&uid_map_list
);
302 uid_map
* um
= CONTAINING_RECORD(le
, uid_map
, listentry
);
309 while (!IsListEmpty(&gid_map_list
)) {
310 gid_map
* gm
= CONTAINING_RECORD(RemoveHeadList(&gid_map_list
), gid_map
, listentry
);
316 // FIXME - free volumes and their devpaths
320 ObDereferenceObject(comfo
);
326 ExDeleteResourceLite(&global_loading_lock
);
327 ExDeleteResourceLite(&pdo_list_lock
);
329 if (log_device
.Buffer
)
330 ExFreePool(log_device
.Buffer
);
333 ExFreePool(log_file
.Buffer
);
335 if (registry_path
.Buffer
)
336 ExFreePool(registry_path
.Buffer
);
339 ExDeleteResourceLite(&log_lock
);
341 ExDeleteResourceLite(&mapping_lock
);
344 static bool get_last_inode(_In_
_Requires_exclusive_lock_held_(_Curr_
->tree_lock
) device_extension
* Vcb
, _In_ root
* r
, _In_opt_ PIRP Irp
) {
346 traverse_ptr tp
, prev_tp
;
350 searchkey
.obj_id
= 0xffffffffffffffff;
351 searchkey
.obj_type
= 0xff;
352 searchkey
.offset
= 0xffffffffffffffff;
354 Status
= find_item(Vcb
, r
, &tp
, &searchkey
, false, Irp
);
355 if (!NT_SUCCESS(Status
)) {
356 ERR("error - find_item returned %08lx\n", Status
);
360 if (tp
.item
->key
.obj_type
== TYPE_INODE_ITEM
|| (tp
.item
->key
.obj_type
== TYPE_ROOT_ITEM
&& !(tp
.item
->key
.obj_id
& 0x8000000000000000))) {
361 r
->lastinode
= tp
.item
->key
.obj_id
;
362 TRACE("last inode for tree %I64x is %I64x\n", r
->id
, r
->lastinode
);
366 while (find_prev_item(Vcb
, &tp
, &prev_tp
, Irp
)) {
369 TRACE("moving on to %I64x,%x,%I64x\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
371 if (tp
.item
->key
.obj_type
== TYPE_INODE_ITEM
|| (tp
.item
->key
.obj_type
== TYPE_ROOT_ITEM
&& !(tp
.item
->key
.obj_id
& 0x8000000000000000))) {
372 r
->lastinode
= tp
.item
->key
.obj_id
;
373 TRACE("last inode for tree %I64x is %I64x\n", r
->id
, r
->lastinode
);
378 r
->lastinode
= SUBVOL_ROOT_INODE
;
380 WARN("no INODE_ITEMs in tree %I64x\n", r
->id
);
386 static bool extract_xattr(_In_reads_bytes_(size
) void* item
, _In_ USHORT size
, _In_z_
char* name
, _Out_
uint8_t** data
, _Out_
uint16_t* datalen
) {
387 DIR_ITEM
* xa
= (DIR_ITEM
*)item
;
391 if (size
< sizeof(DIR_ITEM
) || size
< (sizeof(DIR_ITEM
) - 1 + xa
->m
+ xa
->n
)) {
392 WARN("DIR_ITEM is truncated\n");
396 if (xa
->n
== strlen(name
) && RtlCompareMemory(name
, xa
->name
, xa
->n
) == xa
->n
) {
397 TRACE("found xattr %s\n", name
);
402 *data
= ExAllocatePoolWithTag(PagedPool
, xa
->m
, ALLOC_TAG
);
404 ERR("out of memory\n");
408 RtlCopyMemory(*data
, &xa
->name
[xa
->n
], xa
->m
);
415 xasize
= sizeof(DIR_ITEM
) - 1 + xa
->m
+ xa
->n
;
419 xa
= (DIR_ITEM
*)&xa
->name
[xa
->m
+ xa
->n
];
424 TRACE("xattr %s not found\n", name
);
430 bool get_xattr(_In_
_Requires_lock_held_(_Curr_
->tree_lock
) device_extension
* Vcb
, _In_ root
* subvol
, _In_
uint64_t inode
, _In_z_
char* name
, _In_
uint32_t crc32
,
431 _Out_
uint8_t** data
, _Out_
uint16_t* datalen
, _In_opt_ PIRP Irp
) {
436 TRACE("(%p, %I64x, %I64x, %s, %08x, %p, %p)\n", Vcb
, subvol
->id
, inode
, name
, crc32
, data
, datalen
);
438 searchkey
.obj_id
= inode
;
439 searchkey
.obj_type
= TYPE_XATTR_ITEM
;
440 searchkey
.offset
= crc32
;
442 Status
= find_item(Vcb
, subvol
, &tp
, &searchkey
, false, Irp
);
443 if (!NT_SUCCESS(Status
)) {
444 ERR("error - find_item returned %08lx\n", Status
);
448 if (keycmp(tp
.item
->key
, searchkey
)) {
449 TRACE("could not find item (%I64x,%x,%I64x)\n", searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
);
453 if (tp
.item
->size
< sizeof(DIR_ITEM
)) {
454 ERR("(%I64x,%x,%I64x) was %u bytes, expected at least %Iu\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, tp
.item
->size
, sizeof(DIR_ITEM
));
458 return extract_xattr(tp
.item
->data
, tp
.item
->size
, name
, data
, datalen
);
461 _Dispatch_type_(IRP_MJ_CLOSE
)
462 _Function_class_(DRIVER_DISPATCH
)
463 static NTSTATUS __stdcall
drv_close(_In_ PDEVICE_OBJECT DeviceObject
, _In_ PIRP Irp
) {
465 PIO_STACK_LOCATION IrpSp
;
466 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
469 FsRtlEnterFileSystem();
473 top_level
= is_top_level(Irp
);
475 if (DeviceObject
== master_devobj
) {
476 TRACE("Closing file system\n");
477 Status
= STATUS_SUCCESS
;
479 } else if (Vcb
&& Vcb
->type
== VCB_TYPE_VOLUME
) {
480 Status
= vol_close(DeviceObject
, Irp
);
482 } else if (!Vcb
|| Vcb
->type
!= VCB_TYPE_FS
) {
483 Status
= STATUS_INVALID_PARAMETER
;
487 IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
489 // FIXME - call FsRtlNotifyUninitializeSync(&Vcb->NotifySync) if unmounting
491 Status
= close_file(IrpSp
->FileObject
, Irp
);
494 Irp
->IoStatus
.Status
= Status
;
495 Irp
->IoStatus
.Information
= 0;
497 IoCompleteRequest( Irp
, IO_DISK_INCREMENT
);
500 IoSetTopLevelIrp(NULL
);
502 TRACE("returning %08lx\n", Status
);
504 FsRtlExitFileSystem();
509 _Dispatch_type_(IRP_MJ_FLUSH_BUFFERS
)
510 _Function_class_(DRIVER_DISPATCH
)
511 static NTSTATUS __stdcall
drv_flush_buffers(_In_ PDEVICE_OBJECT DeviceObject
, _In_ PIRP Irp
) {
513 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation( Irp
);
514 PFILE_OBJECT FileObject
= IrpSp
->FileObject
;
515 fcb
* fcb
= FileObject
->FsContext
;
516 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
519 FsRtlEnterFileSystem();
521 TRACE("flush buffers\n");
523 top_level
= is_top_level(Irp
);
525 if (Vcb
&& Vcb
->type
== VCB_TYPE_VOLUME
) {
526 Status
= vol_flush_buffers(DeviceObject
, Irp
);
528 } else if (!Vcb
|| Vcb
->type
!= VCB_TYPE_FS
) {
529 Status
= STATUS_SUCCESS
;
534 ERR("fcb was NULL\n");
535 Status
= STATUS_SUCCESS
;
539 if (fcb
== Vcb
->volume_fcb
) {
540 Status
= STATUS_SUCCESS
;
544 FsRtlCheckOplock(fcb_oplock(fcb
), Irp
, NULL
, NULL
, NULL
);
546 Irp
->IoStatus
.Information
= 0;
548 fcb
->Header
.IsFastIoPossible
= fast_io_possible(fcb
);
550 Status
= STATUS_SUCCESS
;
551 Irp
->IoStatus
.Status
= Status
;
553 if (fcb
->type
!= BTRFS_TYPE_DIRECTORY
) {
554 CcFlushCache(FileObject
->SectionObjectPointer
, NULL
, 0, &Irp
->IoStatus
);
556 if (fcb
->Header
.PagingIoResource
) {
557 ExAcquireResourceExclusiveLite(fcb
->Header
.PagingIoResource
, true);
558 ExReleaseResourceLite(fcb
->Header
.PagingIoResource
);
561 Status
= Irp
->IoStatus
.Status
;
565 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
567 TRACE("returning %08lx\n", Status
);
570 IoSetTopLevelIrp(NULL
);
572 FsRtlExitFileSystem();
577 static void calculate_total_space(_In_ device_extension
* Vcb
, _Out_
uint64_t* totalsize
, _Out_
uint64_t* freespace
) {
578 uint64_t nfactor
, dfactor
, sectors_used
;
580 if (Vcb
->data_flags
& BLOCK_FLAG_DUPLICATE
|| Vcb
->data_flags
& BLOCK_FLAG_RAID1
|| Vcb
->data_flags
& BLOCK_FLAG_RAID10
) {
583 } else if (Vcb
->data_flags
& BLOCK_FLAG_RAID5
) {
584 nfactor
= Vcb
->superblock
.num_devices
- 1;
585 dfactor
= Vcb
->superblock
.num_devices
;
586 } else if (Vcb
->data_flags
& BLOCK_FLAG_RAID6
) {
587 nfactor
= Vcb
->superblock
.num_devices
- 2;
588 dfactor
= Vcb
->superblock
.num_devices
;
589 } else if (Vcb
->data_flags
& BLOCK_FLAG_RAID1C3
) {
592 } else if (Vcb
->data_flags
& BLOCK_FLAG_RAID1C4
) {
600 sectors_used
= (Vcb
->superblock
.bytes_used
/ Vcb
->superblock
.sector_size
) * nfactor
/ dfactor
;
602 *totalsize
= (Vcb
->superblock
.total_bytes
/ Vcb
->superblock
.sector_size
) * nfactor
/ dfactor
;
603 *freespace
= sectors_used
> *totalsize
? 0 : (*totalsize
- sectors_used
);
607 #define INIT_UNICODE_STRING(var, val) UNICODE_STRING us##var; us##var.Buffer = (WCHAR*)val; us##var.Length = us##var.MaximumLength = sizeof(val) - sizeof(WCHAR);
609 // This function exists because we have to lie about our FS type in certain situations.
610 // MPR!MprGetConnection queries the FS type, and compares it to a whitelist. If it doesn't match,
611 // it will return ERROR_NO_NET_OR_BAD_PATH, which prevents UAC from working.
612 // The command mklink refuses to create hard links on anything other than NTFS, so we have to
613 // blacklist cmd.exe too.
615 static bool lie_about_fs_type() {
617 PROCESS_BASIC_INFORMATION pbi
;
625 INIT_UNICODE_STRING(mpr
, L
"MPR.DLL");
626 INIT_UNICODE_STRING(cmd
, L
"CMD.EXE");
627 INIT_UNICODE_STRING(fsutil
, L
"FSUTIL.EXE");
628 INIT_UNICODE_STRING(storsvc
, L
"STORSVC.DLL");
630 if (!PsGetCurrentProcess())
634 Status
= ZwQueryInformationProcess(NtCurrentProcess(), ProcessWow64Information
, &wow64info
, sizeof(wow64info
), NULL
);
636 if (NT_SUCCESS(Status
) && wow64info
!= 0)
640 Status
= ZwQueryInformationProcess(NtCurrentProcess(), ProcessBasicInformation
, &pbi
, sizeof(pbi
), &retlen
);
642 if (!NT_SUCCESS(Status
)) {
643 ERR("ZwQueryInformationProcess returned %08lx\n", Status
);
647 if (!pbi
.PebBaseAddress
)
650 peb
= pbi
.PebBaseAddress
;
655 le
= peb
->Ldr
->InMemoryOrderModuleList
.Flink
;
656 while (le
!= &peb
->Ldr
->InMemoryOrderModuleList
) {
657 LDR_DATA_TABLE_ENTRY
* entry
= CONTAINING_RECORD(le
, LDR_DATA_TABLE_ENTRY
, InMemoryOrderLinks
);
658 bool blacklist
= false;
660 if (entry
->FullDllName
.Length
>= usmpr
.Length
) {
663 name
.Buffer
= &entry
->FullDllName
.Buffer
[(entry
->FullDllName
.Length
- usmpr
.Length
) / sizeof(WCHAR
)];
664 name
.Length
= name
.MaximumLength
= usmpr
.Length
;
666 blacklist
= FsRtlAreNamesEqual(&name
, &usmpr
, true, NULL
);
669 if (!blacklist
&& entry
->FullDllName
.Length
>= uscmd
.Length
) {
672 name
.Buffer
= &entry
->FullDllName
.Buffer
[(entry
->FullDllName
.Length
- uscmd
.Length
) / sizeof(WCHAR
)];
673 name
.Length
= name
.MaximumLength
= uscmd
.Length
;
675 blacklist
= FsRtlAreNamesEqual(&name
, &uscmd
, true, NULL
);
678 if (!blacklist
&& entry
->FullDllName
.Length
>= usfsutil
.Length
) {
681 name
.Buffer
= &entry
->FullDllName
.Buffer
[(entry
->FullDllName
.Length
- usfsutil
.Length
) / sizeof(WCHAR
)];
682 name
.Length
= name
.MaximumLength
= usfsutil
.Length
;
684 blacklist
= FsRtlAreNamesEqual(&name
, &usfsutil
, true, NULL
);
687 if (!blacklist
&& entry
->FullDllName
.Length
>= usstorsvc
.Length
) {
690 name
.Buffer
= &entry
->FullDllName
.Buffer
[(entry
->FullDllName
.Length
- usstorsvc
.Length
) / sizeof(WCHAR
)];
691 name
.Length
= name
.MaximumLength
= usstorsvc
.Length
;
693 blacklist
= FsRtlAreNamesEqual(&name
, &usstorsvc
, true, NULL
);
700 frames
= ExAllocatePoolWithTag(PagedPool
, 256 * sizeof(void*), ALLOC_TAG
);
702 ERR("out of memory\n");
706 num_frames
= RtlWalkFrameChain(frames
, 256, 1);
708 for (i
= 0; i
< num_frames
; i
++) {
709 // entry->Reserved3[1] appears to be the image size
710 if (frames
[i
] >= entry
->DllBase
&& (ULONG_PTR
)frames
[i
] <= (ULONG_PTR
)entry
->DllBase
+ (ULONG_PTR
)entry
->Reserved3
[1]) {
724 #endif // __REACTOS__
726 // version of RtlUTF8ToUnicodeN for Vista and below
727 NTSTATUS
utf8_to_utf16(WCHAR
* dest
, ULONG dest_max
, ULONG
* dest_len
, char* src
, ULONG src_len
) {
728 NTSTATUS Status
= STATUS_SUCCESS
;
729 uint8_t* in
= (uint8_t*)src
;
730 uint16_t* out
= (uint16_t*)dest
;
731 ULONG needed
= 0, left
= dest_max
/ sizeof(uint16_t);
735 for (i
= 0; i
< src_len
; ++i
) {
738 for (ULONG i
= 0; i
< src_len
; i
++) {
744 else if ((in
[i
] & 0xe0) == 0xc0) {
745 if (i
== src_len
- 1 || (in
[i
+1] & 0xc0) != 0x80) {
747 Status
= STATUS_SOME_NOT_MAPPED
;
749 cp
= ((in
[i
] & 0x1f) << 6) | (in
[i
+1] & 0x3f);
752 } else if ((in
[i
] & 0xf0) == 0xe0) {
753 if (i
>= src_len
- 2 || (in
[i
+1] & 0xc0) != 0x80 || (in
[i
+2] & 0xc0) != 0x80) {
755 Status
= STATUS_SOME_NOT_MAPPED
;
757 cp
= ((in
[i
] & 0xf) << 12) | ((in
[i
+1] & 0x3f) << 6) | (in
[i
+2] & 0x3f);
760 } else if ((in
[i
] & 0xf8) == 0xf0) {
761 if (i
>= src_len
- 3 || (in
[i
+1] & 0xc0) != 0x80 || (in
[i
+2] & 0xc0) != 0x80 || (in
[i
+3] & 0xc0) != 0x80) {
763 Status
= STATUS_SOME_NOT_MAPPED
;
765 cp
= ((in
[i
] & 0x7) << 18) | ((in
[i
+1] & 0x3f) << 12) | ((in
[i
+2] & 0x3f) << 6) | (in
[i
+3] & 0x3f);
770 Status
= STATUS_SOME_NOT_MAPPED
;
775 Status
= STATUS_SOME_NOT_MAPPED
;
781 return STATUS_BUFFER_OVERFLOW
;
789 return STATUS_BUFFER_OVERFLOW
;
793 *out
= 0xd800 | ((cp
& 0xffc00) >> 10);
796 *out
= 0xdc00 | (cp
& 0x3ff);
804 needed
+= sizeof(uint16_t);
806 needed
+= 2 * sizeof(uint16_t);
815 // version of RtlUnicodeToUTF8N for Vista and below
816 NTSTATUS
utf16_to_utf8(char* dest
, ULONG dest_max
, ULONG
* dest_len
, WCHAR
* src
, ULONG src_len
) {
817 NTSTATUS Status
= STATUS_SUCCESS
;
818 uint16_t* in
= (uint16_t*)src
;
819 uint8_t* out
= (uint8_t*)dest
;
820 ULONG in_len
= src_len
/ sizeof(uint16_t);
821 ULONG needed
= 0, left
= dest_max
;
825 for (i
= 0; i
< in_len
; i
++) {
828 for (ULONG i
= 0; i
< in_len
; i
++) {
833 if ((cp
& 0xfc00) == 0xd800) {
834 if (i
== in_len
- 1 || (*in
& 0xfc00) != 0xdc00) {
836 Status
= STATUS_SOME_NOT_MAPPED
;
838 cp
= (cp
& 0x3ff) << 10;
845 } else if ((cp
& 0xfc00) == 0xdc00) {
847 Status
= STATUS_SOME_NOT_MAPPED
;
852 Status
= STATUS_SOME_NOT_MAPPED
;
858 return STATUS_BUFFER_OVERFLOW
;
864 } else if (cp
< 0x800) {
866 return STATUS_BUFFER_OVERFLOW
;
868 *out
= 0xc0 | ((cp
& 0x7c0) >> 6);
871 *out
= 0x80 | (cp
& 0x3f);
875 } else if (cp
< 0x10000) {
877 return STATUS_BUFFER_OVERFLOW
;
879 *out
= 0xe0 | ((cp
& 0xf000) >> 12);
882 *out
= 0x80 | ((cp
& 0xfc0) >> 6);
885 *out
= 0x80 | (cp
& 0x3f);
891 return STATUS_BUFFER_OVERFLOW
;
893 *out
= 0xf0 | ((cp
& 0x1c0000) >> 18);
896 *out
= 0x80 | ((cp
& 0x3f000) >> 12);
899 *out
= 0x80 | ((cp
& 0xfc0) >> 6);
902 *out
= 0x80 | (cp
& 0x3f);
913 else if (cp
< 0x10000)
925 _Dispatch_type_(IRP_MJ_QUERY_VOLUME_INFORMATION
)
926 _Function_class_(DRIVER_DISPATCH
)
927 static NTSTATUS __stdcall
drv_query_volume_information(_In_ PDEVICE_OBJECT DeviceObject
, _In_ PIRP Irp
) {
928 PIO_STACK_LOCATION IrpSp
;
930 ULONG BytesCopied
= 0;
931 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
934 FsRtlEnterFileSystem();
936 TRACE("query volume information\n");
937 top_level
= is_top_level(Irp
);
939 if (Vcb
&& Vcb
->type
== VCB_TYPE_VOLUME
) {
940 Status
= vol_query_volume_information(DeviceObject
, Irp
);
942 } else if (!Vcb
|| Vcb
->type
!= VCB_TYPE_FS
) {
943 Status
= STATUS_INVALID_PARAMETER
;
947 IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
949 Status
= STATUS_NOT_IMPLEMENTED
;
951 switch (IrpSp
->Parameters
.QueryVolume
.FsInformationClass
) {
952 case FileFsAttributeInformation
:
954 FILE_FS_ATTRIBUTE_INFORMATION
* data
= Irp
->AssociatedIrp
.SystemBuffer
;
955 bool overflow
= false;
957 static const WCHAR ntfs
[] = L
"NTFS";
959 static const WCHAR btrfs
[] = L
"Btrfs";
960 const WCHAR
* fs_name
;
961 ULONG fs_name_len
, orig_fs_name_len
;
964 if (Irp
->RequestorMode
== UserMode
&& lie_about_fs_type()) {
966 orig_fs_name_len
= fs_name_len
= sizeof(ntfs
) - sizeof(WCHAR
);
969 orig_fs_name_len
= fs_name_len
= sizeof(btrfs
) - sizeof(WCHAR
);
973 orig_fs_name_len
= fs_name_len
= sizeof(btrfs
) - sizeof(WCHAR
);
976 TRACE("FileFsAttributeInformation\n");
978 if (IrpSp
->Parameters
.QueryVolume
.Length
< sizeof(FILE_FS_ATTRIBUTE_INFORMATION
) - sizeof(WCHAR
) + fs_name_len
) {
979 if (IrpSp
->Parameters
.QueryVolume
.Length
> sizeof(FILE_FS_ATTRIBUTE_INFORMATION
) - sizeof(WCHAR
))
980 fs_name_len
= IrpSp
->Parameters
.QueryVolume
.Length
- sizeof(FILE_FS_ATTRIBUTE_INFORMATION
) + sizeof(WCHAR
);
987 data
->FileSystemAttributes
= FILE_CASE_PRESERVED_NAMES
| FILE_CASE_SENSITIVE_SEARCH
|
988 FILE_UNICODE_ON_DISK
| FILE_NAMED_STREAMS
| FILE_SUPPORTS_HARD_LINKS
| FILE_PERSISTENT_ACLS
|
989 FILE_SUPPORTS_REPARSE_POINTS
| FILE_SUPPORTS_SPARSE_FILES
| FILE_SUPPORTS_OBJECT_IDS
|
990 FILE_SUPPORTS_OPEN_BY_FILE_ID
| FILE_SUPPORTS_EXTENDED_ATTRIBUTES
| FILE_SUPPORTS_BLOCK_REFCOUNTING
|
991 FILE_SUPPORTS_POSIX_UNLINK_RENAME
;
993 data
->FileSystemAttributes
|= FILE_READ_ONLY_VOLUME
;
995 // should also be FILE_FILE_COMPRESSION when supported
996 data
->MaximumComponentNameLength
= 255; // FIXME - check
997 data
->FileSystemNameLength
= orig_fs_name_len
;
998 RtlCopyMemory(data
->FileSystemName
, fs_name
, fs_name_len
);
1000 BytesCopied
= sizeof(FILE_FS_ATTRIBUTE_INFORMATION
) - sizeof(WCHAR
) + fs_name_len
;
1001 Status
= overflow
? STATUS_BUFFER_OVERFLOW
: STATUS_SUCCESS
;
1005 case FileFsDeviceInformation
:
1007 FILE_FS_DEVICE_INFORMATION
* ffdi
= Irp
->AssociatedIrp
.SystemBuffer
;
1009 TRACE("FileFsDeviceInformation\n");
1011 ffdi
->DeviceType
= FILE_DEVICE_DISK
;
1013 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, true);
1014 ffdi
->Characteristics
= Vcb
->Vpb
->RealDevice
->Characteristics
;
1015 ExReleaseResourceLite(&Vcb
->tree_lock
);
1018 ffdi
->Characteristics
|= FILE_READ_ONLY_DEVICE
;
1020 ffdi
->Characteristics
&= ~FILE_READ_ONLY_DEVICE
;
1022 BytesCopied
= sizeof(FILE_FS_DEVICE_INFORMATION
);
1023 Status
= STATUS_SUCCESS
;
1028 case FileFsFullSizeInformation
:
1030 FILE_FS_FULL_SIZE_INFORMATION
* ffsi
= Irp
->AssociatedIrp
.SystemBuffer
;
1032 TRACE("FileFsFullSizeInformation\n");
1034 calculate_total_space(Vcb
, (uint64_t*)&ffsi
->TotalAllocationUnits
.QuadPart
, (uint64_t*)&ffsi
->ActualAvailableAllocationUnits
.QuadPart
);
1035 ffsi
->CallerAvailableAllocationUnits
.QuadPart
= ffsi
->ActualAvailableAllocationUnits
.QuadPart
;
1036 ffsi
->SectorsPerAllocationUnit
= Vcb
->superblock
.sector_size
/ 512;
1037 ffsi
->BytesPerSector
= 512;
1039 BytesCopied
= sizeof(FILE_FS_FULL_SIZE_INFORMATION
);
1040 Status
= STATUS_SUCCESS
;
1045 case FileFsObjectIdInformation
:
1047 FILE_FS_OBJECTID_INFORMATION
* ffoi
= Irp
->AssociatedIrp
.SystemBuffer
;
1049 TRACE("FileFsObjectIdInformation\n");
1051 RtlCopyMemory(ffoi
->ObjectId
, &Vcb
->superblock
.uuid
.uuid
[0], sizeof(UCHAR
) * 16);
1052 RtlZeroMemory(ffoi
->ExtendedInfo
, sizeof(ffoi
->ExtendedInfo
));
1054 BytesCopied
= sizeof(FILE_FS_OBJECTID_INFORMATION
);
1055 Status
= STATUS_SUCCESS
;
1060 case FileFsSizeInformation
:
1062 FILE_FS_SIZE_INFORMATION
* ffsi
= Irp
->AssociatedIrp
.SystemBuffer
;
1064 TRACE("FileFsSizeInformation\n");
1066 calculate_total_space(Vcb
, (uint64_t*)&ffsi
->TotalAllocationUnits
.QuadPart
, (uint64_t*)&ffsi
->AvailableAllocationUnits
.QuadPart
);
1067 ffsi
->SectorsPerAllocationUnit
= Vcb
->superblock
.sector_size
/ 512;
1068 ffsi
->BytesPerSector
= 512;
1070 BytesCopied
= sizeof(FILE_FS_SIZE_INFORMATION
);
1071 Status
= STATUS_SUCCESS
;
1076 case FileFsVolumeInformation
:
1078 FILE_FS_VOLUME_INFORMATION
* data
= Irp
->AssociatedIrp
.SystemBuffer
;
1079 FILE_FS_VOLUME_INFORMATION ffvi
;
1080 bool overflow
= false;
1081 ULONG label_len
, orig_label_len
;
1083 TRACE("FileFsVolumeInformation\n");
1084 TRACE("max length = %lu\n", IrpSp
->Parameters
.QueryVolume
.Length
);
1086 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, true);
1088 Status
= utf8_to_utf16(NULL
, 0, &label_len
, Vcb
->superblock
.label
, (ULONG
)strlen(Vcb
->superblock
.label
));
1089 if (!NT_SUCCESS(Status
)) {
1090 ERR("utf8_to_utf16 returned %08lx\n", Status
);
1091 ExReleaseResourceLite(&Vcb
->tree_lock
);
1095 orig_label_len
= label_len
;
1097 if (IrpSp
->Parameters
.QueryVolume
.Length
< sizeof(FILE_FS_VOLUME_INFORMATION
) - sizeof(WCHAR
) + label_len
) {
1098 if (IrpSp
->Parameters
.QueryVolume
.Length
> sizeof(FILE_FS_VOLUME_INFORMATION
) - sizeof(WCHAR
))
1099 label_len
= IrpSp
->Parameters
.QueryVolume
.Length
- sizeof(FILE_FS_VOLUME_INFORMATION
) + sizeof(WCHAR
);
1106 TRACE("label_len = %lu\n", label_len
);
1108 ffvi
.VolumeCreationTime
.QuadPart
= 0; // FIXME
1109 ffvi
.VolumeSerialNumber
= Vcb
->superblock
.uuid
.uuid
[12] << 24 | Vcb
->superblock
.uuid
.uuid
[13] << 16 | Vcb
->superblock
.uuid
.uuid
[14] << 8 | Vcb
->superblock
.uuid
.uuid
[15];
1110 ffvi
.VolumeLabelLength
= orig_label_len
;
1111 ffvi
.SupportsObjects
= false;
1113 RtlCopyMemory(data
, &ffvi
, min(sizeof(FILE_FS_VOLUME_INFORMATION
) - sizeof(WCHAR
), IrpSp
->Parameters
.QueryVolume
.Length
));
1115 if (label_len
> 0) {
1118 Status
= utf8_to_utf16(&data
->VolumeLabel
[0], label_len
, &bytecount
, Vcb
->superblock
.label
, (ULONG
)strlen(Vcb
->superblock
.label
));
1119 if (!NT_SUCCESS(Status
) && Status
!= STATUS_BUFFER_TOO_SMALL
) {
1120 ERR("utf8_to_utf16 returned %08lx\n", Status
);
1121 ExReleaseResourceLite(&Vcb
->tree_lock
);
1125 TRACE("label = %.*S\n", (int)(label_len
/ sizeof(WCHAR
)), data
->VolumeLabel
);
1128 ExReleaseResourceLite(&Vcb
->tree_lock
);
1130 BytesCopied
= sizeof(FILE_FS_VOLUME_INFORMATION
) - sizeof(WCHAR
) + label_len
;
1131 Status
= overflow
? STATUS_BUFFER_OVERFLOW
: STATUS_SUCCESS
;
1136 #ifdef _MSC_VER // not in mingw yet
1137 case FileFsSectorSizeInformation
:
1139 FILE_FS_SECTOR_SIZE_INFORMATION
* data
= Irp
->AssociatedIrp
.SystemBuffer
;
1141 data
->LogicalBytesPerSector
= Vcb
->superblock
.sector_size
;
1142 data
->PhysicalBytesPerSectorForAtomicity
= Vcb
->superblock
.sector_size
;
1143 data
->PhysicalBytesPerSectorForPerformance
= Vcb
->superblock
.sector_size
;
1144 data
->FileSystemEffectivePhysicalBytesPerSectorForAtomicity
= Vcb
->superblock
.sector_size
;
1145 data
->ByteOffsetForSectorAlignment
= 0;
1146 data
->ByteOffsetForPartitionAlignment
= 0;
1148 data
->Flags
= SSINFO_FLAGS_ALIGNED_DEVICE
| SSINFO_FLAGS_PARTITION_ALIGNED_ON_DEVICE
;
1150 if (Vcb
->trim
&& !Vcb
->options
.no_trim
)
1151 data
->Flags
|= SSINFO_FLAGS_TRIM_ENABLED
;
1153 BytesCopied
= sizeof(FILE_FS_SECTOR_SIZE_INFORMATION
);
1154 Status
= STATUS_SUCCESS
;
1159 #endif /* __REACTOS__ */
1162 Status
= STATUS_INVALID_PARAMETER
;
1163 WARN("unknown FsInformationClass %u\n", IrpSp
->Parameters
.QueryVolume
.FsInformationClass
);
1167 if (!NT_SUCCESS(Status
) && Status
!= STATUS_BUFFER_OVERFLOW
)
1168 Irp
->IoStatus
.Information
= 0;
1170 Irp
->IoStatus
.Information
= BytesCopied
;
1173 Irp
->IoStatus
.Status
= Status
;
1175 IoCompleteRequest( Irp
, IO_DISK_INCREMENT
);
1178 IoSetTopLevelIrp(NULL
);
1180 TRACE("query volume information returning %08lx\n", Status
);
1182 FsRtlExitFileSystem();
1187 _Function_class_(IO_COMPLETION_ROUTINE
)
1188 static NTSTATUS __stdcall
read_completion(_In_ PDEVICE_OBJECT DeviceObject
, _In_ PIRP Irp
, _In_ PVOID conptr
) {
1189 read_context
* context
= conptr
;
1191 UNUSED(DeviceObject
);
1193 context
->iosb
= Irp
->IoStatus
;
1194 KeSetEvent(&context
->Event
, 0, false);
1196 return STATUS_MORE_PROCESSING_REQUIRED
;
1199 NTSTATUS
create_root(_In_
_Requires_exclusive_lock_held_(_Curr_
->tree_lock
) device_extension
* Vcb
, _In_
uint64_t id
,
1200 _Out_ root
** rootptr
, _In_
bool no_tree
, _In_
uint64_t offset
, _In_opt_ PIRP Irp
) {
1207 r
= ExAllocatePoolWithTag(PagedPool
, sizeof(root
), ALLOC_TAG
);
1209 ERR("out of memory\n");
1210 return STATUS_INSUFFICIENT_RESOURCES
;
1213 r
->nonpaged
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(root_nonpaged
), ALLOC_TAG
);
1215 ERR("out of memory\n");
1217 return STATUS_INSUFFICIENT_RESOURCES
;
1221 t
= ExAllocatePoolWithTag(PagedPool
, sizeof(tree
), ALLOC_TAG
);
1223 ERR("out of memory\n");
1224 ExFreePool(r
->nonpaged
);
1226 return STATUS_INSUFFICIENT_RESOURCES
;
1231 t
->is_unique
= true;
1232 t
->uniqueness_determined
= true;
1236 ri
= ExAllocatePoolWithTag(PagedPool
, sizeof(ROOT_ITEM
), ALLOC_TAG
);
1238 ERR("out of memory\n");
1243 ExFreePool(r
->nonpaged
);
1245 return STATUS_INSUFFICIENT_RESOURCES
;
1249 r
->treeholder
.address
= 0;
1250 r
->treeholder
.generation
= Vcb
->superblock
.generation
;
1251 r
->treeholder
.tree
= t
;
1254 r
->received
= false;
1258 RtlZeroMemory(&r
->root_item
, sizeof(ROOT_ITEM
));
1259 r
->root_item
.num_references
= 1;
1260 r
->fcbs_version
= 0;
1261 r
->checked_for_orphans
= true;
1263 InitializeListHead(&r
->fcbs
);
1264 RtlZeroMemory(r
->fcbs_ptrs
, sizeof(LIST_ENTRY
*) * 256);
1266 RtlCopyMemory(ri
, &r
->root_item
, sizeof(ROOT_ITEM
));
1268 // We ask here for a traverse_ptr to the item we're inserting, so we can
1269 // copy some of the tree's variables
1271 Status
= insert_tree_item(Vcb
, Vcb
->root_root
, id
, TYPE_ROOT_ITEM
, offset
, ri
, sizeof(ROOT_ITEM
), &tp
, Irp
);
1272 if (!NT_SUCCESS(Status
)) {
1273 ERR("insert_tree_item returned %08lx\n", Status
);
1279 ExFreePool(r
->nonpaged
);
1284 ExInitializeResourceLite(&r
->nonpaged
->load_tree_lock
);
1286 InsertTailList(&Vcb
->roots
, &r
->list_entry
);
1289 RtlZeroMemory(&t
->header
, sizeof(tree_header
));
1290 t
->header
.fs_uuid
= tp
.tree
->header
.fs_uuid
;
1291 t
->header
.address
= 0;
1292 t
->header
.flags
= HEADER_FLAG_MIXED_BACKREF
| 1; // 1 == "written"? Why does the Linux driver record this?
1293 t
->header
.chunk_tree_uuid
= tp
.tree
->header
.chunk_tree_uuid
;
1294 t
->header
.generation
= Vcb
->superblock
.generation
;
1295 t
->header
.tree_id
= id
;
1296 t
->header
.num_items
= 0;
1297 t
->header
.level
= 0;
1299 t
->has_address
= false;
1306 InitializeListHead(&t
->itemlist
);
1309 t
->has_new_address
= false;
1310 t
->updated_extents
= false;
1312 InsertTailList(&Vcb
->trees
, &t
->list_entry
);
1313 t
->list_entry_hash
.Flink
= NULL
;
1316 Vcb
->need_write
= true;
1321 return STATUS_SUCCESS
;
1324 static NTSTATUS
set_label(_In_ device_extension
* Vcb
, _In_ FILE_FS_LABEL_INFORMATION
* ffli
) {
1329 TRACE("label = %.*S\n", (int)(ffli
->VolumeLabelLength
/ sizeof(WCHAR
)), ffli
->VolumeLabel
);
1331 vollen
= ffli
->VolumeLabelLength
;
1333 for (i
= 0; i
< ffli
->VolumeLabelLength
/ sizeof(WCHAR
); i
++) {
1334 if (ffli
->VolumeLabel
[i
] == 0) {
1335 vollen
= i
* sizeof(WCHAR
);
1337 } else if (ffli
->VolumeLabel
[i
] == '/' || ffli
->VolumeLabel
[i
] == '\\') {
1338 Status
= STATUS_INVALID_VOLUME_LABEL
;
1346 Status
= utf16_to_utf8(NULL
, 0, &utf8len
, ffli
->VolumeLabel
, vollen
);
1347 if (!NT_SUCCESS(Status
))
1350 if (utf8len
> MAX_LABEL_SIZE
) {
1351 Status
= STATUS_INVALID_VOLUME_LABEL
;
1356 ExAcquireResourceExclusiveLite(&Vcb
->tree_lock
, true);
1359 Status
= utf16_to_utf8((PCHAR
)&Vcb
->superblock
.label
, MAX_LABEL_SIZE
, &utf8len
, ffli
->VolumeLabel
, vollen
);
1360 if (!NT_SUCCESS(Status
))
1363 Status
= STATUS_SUCCESS
;
1365 if (utf8len
< MAX_LABEL_SIZE
)
1366 RtlZeroMemory(Vcb
->superblock
.label
+ utf8len
, MAX_LABEL_SIZE
- utf8len
);
1368 Vcb
->need_write
= true;
1371 ExReleaseResourceLite(&Vcb
->tree_lock
);
1374 TRACE("returning %08lx\n", Status
);
1379 _Dispatch_type_(IRP_MJ_SET_VOLUME_INFORMATION
)
1380 _Function_class_(DRIVER_DISPATCH
)
1381 static NTSTATUS __stdcall
drv_set_volume_information(_In_ PDEVICE_OBJECT DeviceObject
, _In_ PIRP Irp
) {
1382 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
1383 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
1387 FsRtlEnterFileSystem();
1389 TRACE("set volume information\n");
1391 top_level
= is_top_level(Irp
);
1393 if (Vcb
&& Vcb
->type
== VCB_TYPE_VOLUME
) {
1394 Status
= vol_set_volume_information(DeviceObject
, Irp
);
1396 } else if (!Vcb
|| Vcb
->type
!= VCB_TYPE_FS
) {
1397 Status
= STATUS_INVALID_PARAMETER
;
1401 Status
= STATUS_NOT_IMPLEMENTED
;
1403 if (Vcb
->readonly
) {
1404 Status
= STATUS_MEDIA_WRITE_PROTECTED
;
1408 if (Vcb
->removing
|| Vcb
->locked
) {
1409 Status
= STATUS_ACCESS_DENIED
;
1413 switch (IrpSp
->Parameters
.SetVolume
.FsInformationClass
) {
1414 case FileFsControlInformation
:
1415 FIXME("STUB: FileFsControlInformation\n");
1418 case FileFsLabelInformation
:
1419 TRACE("FileFsLabelInformation\n");
1421 Status
= set_label(Vcb
, Irp
->AssociatedIrp
.SystemBuffer
);
1424 case FileFsObjectIdInformation
:
1425 FIXME("STUB: FileFsObjectIdInformation\n");
1429 WARN("Unrecognized FsInformationClass 0x%x\n", IrpSp
->Parameters
.SetVolume
.FsInformationClass
);
1434 Irp
->IoStatus
.Status
= Status
;
1435 Irp
->IoStatus
.Information
= 0;
1437 TRACE("returning %08lx\n", Status
);
1439 IoCompleteRequest( Irp
, IO_NO_INCREMENT
);
1442 IoSetTopLevelIrp(NULL
);
1444 FsRtlExitFileSystem();
1449 void send_notification_fileref(_In_ file_ref
* fileref
, _In_ ULONG filter_match
, _In_ ULONG action
, _In_opt_ PUNICODE_STRING stream
) {
1454 fcb
* fcb
= fileref
->fcb
;
1456 fn
.Length
= fn
.MaximumLength
= 0;
1457 Status
= fileref_get_filename(fileref
, &fn
, NULL
, &reqlen
);
1458 if (Status
!= STATUS_BUFFER_OVERFLOW
) {
1459 ERR("fileref_get_filename returned %08lx\n", Status
);
1463 if (reqlen
> 0xffff) {
1464 WARN("reqlen was too long for FsRtlNotifyFilterReportChange\n");
1468 fn
.Buffer
= ExAllocatePoolWithTag(PagedPool
, reqlen
, ALLOC_TAG
);
1470 ERR("out of memory\n");
1474 fn
.MaximumLength
= (USHORT
)reqlen
;
1477 Status
= fileref_get_filename(fileref
, &fn
, &name_offset
, &reqlen
);
1478 if (!NT_SUCCESS(Status
)) {
1479 ERR("fileref_get_filename returned %08lx\n", Status
);
1480 ExFreePool(fn
.Buffer
);
1484 FsRtlNotifyFilterReportChange(fcb
->Vcb
->NotifySync
, &fcb
->Vcb
->DirNotifyList
, (PSTRING
)&fn
, name_offset
,
1485 (PSTRING
)stream
, NULL
, filter_match
, action
, NULL
, NULL
);
1486 ExFreePool(fn
.Buffer
);
1489 static void send_notification_fcb(_In_ file_ref
* fileref
, _In_ ULONG filter_match
, _In_ ULONG action
, _In_opt_ PUNICODE_STRING stream
) {
1490 fcb
* fcb
= fileref
->fcb
;
1494 // no point looking for hardlinks if st_nlink == 1
1495 if (fileref
->fcb
->inode_item
.st_nlink
== 1) {
1496 send_notification_fileref(fileref
, filter_match
, action
, stream
);
1500 ExAcquireResourceExclusiveLite(&fcb
->Vcb
->fileref_lock
, true);
1502 le
= fcb
->hardlinks
.Flink
;
1503 while (le
!= &fcb
->hardlinks
) {
1504 hardlink
* hl
= CONTAINING_RECORD(le
, hardlink
, list_entry
);
1507 Status
= open_fileref_by_inode(fcb
->Vcb
, fcb
->subvol
, hl
->parent
, &parfr
, NULL
);
1509 if (!NT_SUCCESS(Status
))
1510 ERR("open_fileref_by_inode returned %08lx\n", Status
);
1511 else if (!parfr
->deleted
) {
1515 fn
.Length
= fn
.MaximumLength
= 0;
1516 Status
= fileref_get_filename(parfr
, &fn
, NULL
, &pathlen
);
1517 if (Status
!= STATUS_BUFFER_OVERFLOW
) {
1518 ERR("fileref_get_filename returned %08lx\n", Status
);
1519 free_fileref(parfr
);
1523 if (parfr
!= fcb
->Vcb
->root_fileref
)
1524 pathlen
+= sizeof(WCHAR
);
1526 if (pathlen
+ hl
->name
.Length
> 0xffff) {
1527 WARN("pathlen + hl->name.Length was too long for FsRtlNotifyFilterReportChange\n");
1528 free_fileref(parfr
);
1532 fn
.MaximumLength
= (USHORT
)(pathlen
+ hl
->name
.Length
);
1533 fn
.Buffer
= ExAllocatePoolWithTag(PagedPool
, fn
.MaximumLength
, ALLOC_TAG
);
1535 ERR("out of memory\n");
1536 free_fileref(parfr
);
1540 Status
= fileref_get_filename(parfr
, &fn
, NULL
, NULL
);
1541 if (!NT_SUCCESS(Status
)) {
1542 ERR("fileref_get_filename returned %08lx\n", Status
);
1543 free_fileref(parfr
);
1544 ExFreePool(fn
.Buffer
);
1548 if (parfr
!= fcb
->Vcb
->root_fileref
) {
1549 fn
.Buffer
[(pathlen
/ sizeof(WCHAR
)) - 1] = '\\';
1550 fn
.Length
+= sizeof(WCHAR
);
1553 RtlCopyMemory(&fn
.Buffer
[pathlen
/ sizeof(WCHAR
)], hl
->name
.Buffer
, hl
->name
.Length
);
1554 fn
.Length
+= hl
->name
.Length
;
1556 FsRtlNotifyFilterReportChange(fcb
->Vcb
->NotifySync
, &fcb
->Vcb
->DirNotifyList
, (PSTRING
)&fn
, (USHORT
)pathlen
,
1557 (PSTRING
)stream
, NULL
, filter_match
, action
, NULL
, NULL
);
1559 ExFreePool(fn
.Buffer
);
1561 free_fileref(parfr
);
1567 ExReleaseResourceLite(&fcb
->Vcb
->fileref_lock
);
1574 PUNICODE_STRING stream
;
1575 PIO_WORKITEM work_item
;
1578 _Function_class_(IO_WORKITEM_ROUTINE
)
1579 static void __stdcall
notification_work_item(PDEVICE_OBJECT DeviceObject
, PVOID con
) {
1580 notification_fcb
* nf
= con
;
1582 UNUSED(DeviceObject
);
1584 ExAcquireResourceSharedLite(&nf
->fileref
->fcb
->Vcb
->tree_lock
, TRUE
); // protect us from fileref being reaped
1586 send_notification_fcb(nf
->fileref
, nf
->filter_match
, nf
->action
, nf
->stream
);
1588 free_fileref(nf
->fileref
);
1590 ExReleaseResourceLite(&nf
->fileref
->fcb
->Vcb
->tree_lock
);
1592 IoFreeWorkItem(nf
->work_item
);
1597 void queue_notification_fcb(_In_ file_ref
* fileref
, _In_ ULONG filter_match
, _In_ ULONG action
, _In_opt_ PUNICODE_STRING stream
) {
1598 notification_fcb
* nf
;
1599 PIO_WORKITEM work_item
;
1601 nf
= ExAllocatePoolWithTag(PagedPool
, sizeof(notification_fcb
), ALLOC_TAG
);
1603 ERR("out of memory\n");
1607 work_item
= IoAllocateWorkItem(master_devobj
);
1609 ERR("out of memory\n");
1614 InterlockedIncrement(&fileref
->refcount
);
1616 nf
->fileref
= fileref
;
1617 nf
->filter_match
= filter_match
;
1618 nf
->action
= action
;
1619 nf
->stream
= stream
;
1620 nf
->work_item
= work_item
;
1622 IoQueueWorkItem(work_item
, notification_work_item
, DelayedWorkQueue
, nf
);
1625 void mark_fcb_dirty(_In_ fcb
* fcb
) {
1627 #ifdef DEBUG_FCB_REFCOUNTS
1632 #ifdef DEBUG_FCB_REFCOUNTS
1633 rc
= InterlockedIncrement(&fcb
->refcount
);
1634 WARN("fcb %p: refcount now %i\n", fcb
, rc
);
1636 InterlockedIncrement(&fcb
->refcount
);
1639 ExAcquireResourceExclusiveLite(&fcb
->Vcb
->dirty_fcbs_lock
, true);
1640 InsertTailList(&fcb
->Vcb
->dirty_fcbs
, &fcb
->list_entry_dirty
);
1641 ExReleaseResourceLite(&fcb
->Vcb
->dirty_fcbs_lock
);
1644 fcb
->Vcb
->need_write
= true;
1647 void mark_fileref_dirty(_In_ file_ref
* fileref
) {
1648 if (!fileref
->dirty
) {
1649 fileref
->dirty
= true;
1650 increase_fileref_refcount(fileref
);
1652 ExAcquireResourceExclusiveLite(&fileref
->fcb
->Vcb
->dirty_filerefs_lock
, true);
1653 InsertTailList(&fileref
->fcb
->Vcb
->dirty_filerefs
, &fileref
->list_entry_dirty
);
1654 ExReleaseResourceLite(&fileref
->fcb
->Vcb
->dirty_filerefs_lock
);
1657 fileref
->fcb
->Vcb
->need_write
= true;
1660 #ifdef DEBUG_FCB_REFCOUNTS
1661 void _free_fcb(_Inout_ fcb
* fcb
, _In_
const char* func
) {
1662 LONG rc
= InterlockedDecrement(&fcb
->refcount
);
1664 void free_fcb(_Inout_ fcb
* fcb
) {
1665 InterlockedDecrement(&fcb
->refcount
);
1668 #ifdef DEBUG_FCB_REFCOUNTS
1669 ERR("fcb %p (%s): refcount now %i (subvol %I64x, inode %I64x)\n", fcb
, func
, rc
, fcb
->subvol
? fcb
->subvol
->id
: 0, fcb
->inode
);
1673 void reap_fcb(fcb
* fcb
) {
1674 uint8_t c
= fcb
->hash
>> 24;
1676 if (fcb
->subvol
&& fcb
->subvol
->fcbs_ptrs
[c
] == &fcb
->list_entry
) {
1677 if (fcb
->list_entry
.Flink
!= &fcb
->subvol
->fcbs
&& (CONTAINING_RECORD(fcb
->list_entry
.Flink
, struct _fcb
, list_entry
)->hash
>> 24) == c
)
1678 fcb
->subvol
->fcbs_ptrs
[c
] = fcb
->list_entry
.Flink
;
1680 fcb
->subvol
->fcbs_ptrs
[c
] = NULL
;
1683 if (fcb
->list_entry
.Flink
) {
1684 RemoveEntryList(&fcb
->list_entry
);
1686 if (fcb
->subvol
&& fcb
->subvol
->dropped
&& IsListEmpty(&fcb
->subvol
->fcbs
)) {
1687 ExDeleteResourceLite(&fcb
->subvol
->nonpaged
->load_tree_lock
);
1688 ExFreePool(fcb
->subvol
->nonpaged
);
1689 ExFreePool(fcb
->subvol
);
1693 if (fcb
->list_entry_all
.Flink
)
1694 RemoveEntryList(&fcb
->list_entry_all
);
1696 ExDeleteResourceLite(&fcb
->nonpaged
->resource
);
1697 ExDeleteResourceLite(&fcb
->nonpaged
->paging_resource
);
1698 ExDeleteResourceLite(&fcb
->nonpaged
->dir_children_lock
);
1700 ExFreeToNPagedLookasideList(&fcb
->Vcb
->fcb_np_lookaside
, fcb
->nonpaged
);
1703 ExFreePool(fcb
->sd
);
1705 if (fcb
->adsxattr
.Buffer
)
1706 ExFreePool(fcb
->adsxattr
.Buffer
);
1708 if (fcb
->reparse_xattr
.Buffer
)
1709 ExFreePool(fcb
->reparse_xattr
.Buffer
);
1711 if (fcb
->ea_xattr
.Buffer
)
1712 ExFreePool(fcb
->ea_xattr
.Buffer
);
1714 if (fcb
->adsdata
.Buffer
)
1715 ExFreePool(fcb
->adsdata
.Buffer
);
1717 while (!IsListEmpty(&fcb
->extents
)) {
1718 LIST_ENTRY
* le
= RemoveHeadList(&fcb
->extents
);
1719 extent
* ext
= CONTAINING_RECORD(le
, extent
, list_entry
);
1722 ExFreePool(ext
->csum
);
1727 while (!IsListEmpty(&fcb
->hardlinks
)) {
1728 LIST_ENTRY
* le
= RemoveHeadList(&fcb
->hardlinks
);
1729 hardlink
* hl
= CONTAINING_RECORD(le
, hardlink
, list_entry
);
1731 if (hl
->name
.Buffer
)
1732 ExFreePool(hl
->name
.Buffer
);
1734 if (hl
->utf8
.Buffer
)
1735 ExFreePool(hl
->utf8
.Buffer
);
1740 while (!IsListEmpty(&fcb
->xattrs
)) {
1741 xattr
* xa
= CONTAINING_RECORD(RemoveHeadList(&fcb
->xattrs
), xattr
, list_entry
);
1746 while (!IsListEmpty(&fcb
->dir_children_index
)) {
1747 LIST_ENTRY
* le
= RemoveHeadList(&fcb
->dir_children_index
);
1748 dir_child
* dc
= CONTAINING_RECORD(le
, dir_child
, list_entry_index
);
1750 ExFreePool(dc
->utf8
.Buffer
);
1751 ExFreePool(dc
->name
.Buffer
);
1752 ExFreePool(dc
->name_uc
.Buffer
);
1757 ExFreePool(fcb
->hash_ptrs
);
1759 if (fcb
->hash_ptrs_uc
)
1760 ExFreePool(fcb
->hash_ptrs_uc
);
1762 FsRtlUninitializeFileLock(&fcb
->lock
);
1763 FsRtlUninitializeOplock(fcb_oplock(fcb
));
1765 if (fcb
->pool_type
== NonPagedPool
)
1768 ExFreeToPagedLookasideList(&fcb
->Vcb
->fcb_lookaside
, fcb
);
1771 void reap_fcbs(device_extension
* Vcb
) {
1774 le
= Vcb
->all_fcbs
.Flink
;
1775 while (le
!= &Vcb
->all_fcbs
) {
1776 fcb
* fcb
= CONTAINING_RECORD(le
, struct _fcb
, list_entry_all
);
1777 LIST_ENTRY
* le2
= le
->Flink
;
1779 if (fcb
->refcount
== 0)
1786 void free_fileref(_Inout_ file_ref
* fr
) {
1789 rc
= InterlockedDecrement(&fr
->refcount
);
1794 #ifdef DEBUG_FCB_REFCOUNTS
1795 ERR("fileref %p: refcount now %i\n", fr
, rc
);
1800 ERR("fileref %p: refcount now %li\n", fr
, rc
);
1806 void reap_fileref(device_extension
* Vcb
, file_ref
* fr
) {
1807 // FIXME - do we need a file_ref lock?
1809 // FIXME - do delete if needed
1811 ExDeleteResourceLite(&fr
->nonpaged
->fileref_lock
);
1813 ExFreeToNPagedLookasideList(&Vcb
->fileref_np_lookaside
, fr
->nonpaged
);
1815 // FIXME - throw error if children not empty
1817 if (fr
->fcb
->fileref
== fr
)
1818 fr
->fcb
->fileref
= NULL
;
1822 fr
->dc
->size
= fr
->fcb
->adsdata
.Length
;
1824 fr
->dc
->fileref
= NULL
;
1827 if (fr
->list_entry
.Flink
)
1828 RemoveEntryList(&fr
->list_entry
);
1831 free_fileref(fr
->parent
);
1835 if (fr
->oldutf8
.Buffer
)
1836 ExFreePool(fr
->oldutf8
.Buffer
);
1838 ExFreeToPagedLookasideList(&Vcb
->fileref_lookaside
, fr
);
1841 void reap_filerefs(device_extension
* Vcb
, file_ref
* fr
) {
1844 // FIXME - recursion is a bad idea in kernel mode
1846 le
= fr
->children
.Flink
;
1847 while (le
!= &fr
->children
) {
1848 file_ref
* c
= CONTAINING_RECORD(le
, file_ref
, list_entry
);
1849 LIST_ENTRY
* le2
= le
->Flink
;
1851 reap_filerefs(Vcb
, c
);
1856 if (fr
->refcount
== 0)
1857 reap_fileref(Vcb
, fr
);
1860 static NTSTATUS
close_file(_In_ PFILE_OBJECT FileObject
, _In_ PIRP Irp
) {
1863 file_ref
* fileref
= NULL
;
1868 TRACE("FileObject = %p\n", FileObject
);
1870 fcb
= FileObject
->FsContext
;
1872 TRACE("FCB was NULL, returning success\n");
1873 return STATUS_SUCCESS
;
1876 open_files
= InterlockedDecrement(&fcb
->Vcb
->open_files
);
1878 ccb
= FileObject
->FsContext2
;
1880 TRACE("close called for fcb %p)\n", fcb
);
1882 // FIXME - make sure notification gets sent if file is being deleted
1885 if (ccb
->query_string
.Buffer
)
1886 RtlFreeUnicodeString(&ccb
->query_string
);
1888 if (ccb
->filename
.Buffer
)
1889 ExFreePool(ccb
->filename
.Buffer
);
1891 // FIXME - use refcounts for fileref
1892 fileref
= ccb
->fileref
;
1894 if (fcb
->Vcb
->running_sends
> 0) {
1895 bool send_cancelled
= false;
1897 ExAcquireResourceExclusiveLite(&fcb
->Vcb
->send_load_lock
, true);
1900 ccb
->send
->cancelling
= true;
1901 send_cancelled
= true;
1902 KeSetEvent(&ccb
->send
->cleared_event
, 0, false);
1905 ExReleaseResourceLite(&fcb
->Vcb
->send_load_lock
);
1907 if (send_cancelled
) {
1909 ExAcquireResourceExclusiveLite(&fcb
->Vcb
->send_load_lock
, true);
1910 ExReleaseResourceLite(&fcb
->Vcb
->send_load_lock
);
1918 CcUninitializeCacheMap(FileObject
, NULL
, NULL
);
1920 if (open_files
== 0 && fcb
->Vcb
->removing
) {
1922 return STATUS_SUCCESS
;
1925 if (!(fcb
->Vcb
->Vpb
->Flags
& VPB_MOUNTED
))
1926 return STATUS_SUCCESS
;
1929 free_fileref(fileref
);
1933 return STATUS_SUCCESS
;
1936 void uninit(_In_ device_extension
* Vcb
) {
1943 if (!Vcb
->removing
) {
1944 ExAcquireResourceExclusiveLite(&Vcb
->tree_lock
, true);
1945 Vcb
->removing
= true;
1946 ExReleaseResourceLite(&Vcb
->tree_lock
);
1949 if (Vcb
->vde
&& Vcb
->vde
->mounted_device
== Vcb
->devobj
)
1950 Vcb
->vde
->mounted_device
= NULL
;
1952 IoAcquireVpbSpinLock(&irql
);
1953 Vcb
->Vpb
->Flags
&= ~VPB_MOUNTED
;
1954 Vcb
->Vpb
->Flags
|= VPB_DIRECT_WRITES_ALLOWED
;
1955 Vcb
->Vpb
->DeviceObject
= NULL
;
1956 IoReleaseVpbSpinLock(irql
);
1958 // FIXME - needs global_loading_lock to be held
1959 if (Vcb
->list_entry
.Flink
)
1960 RemoveEntryList(&Vcb
->list_entry
);
1962 if (Vcb
->balance
.thread
) {
1963 Vcb
->balance
.paused
= false;
1964 Vcb
->balance
.stopping
= true;
1965 KeSetEvent(&Vcb
->balance
.event
, 0, false);
1966 KeWaitForSingleObject(&Vcb
->balance
.finished
, Executive
, KernelMode
, false, NULL
);
1969 if (Vcb
->scrub
.thread
) {
1970 Vcb
->scrub
.paused
= false;
1971 Vcb
->scrub
.stopping
= true;
1972 KeSetEvent(&Vcb
->scrub
.event
, 0, false);
1973 KeWaitForSingleObject(&Vcb
->scrub
.finished
, Executive
, KernelMode
, false, NULL
);
1976 if (Vcb
->running_sends
!= 0) {
1977 bool send_cancelled
= false;
1979 ExAcquireResourceExclusiveLite(&Vcb
->send_load_lock
, true);
1981 le
= Vcb
->send_ops
.Flink
;
1982 while (le
!= &Vcb
->send_ops
) {
1983 send_info
* send
= CONTAINING_RECORD(le
, send_info
, list_entry
);
1985 if (!send
->cancelling
) {
1986 send
->cancelling
= true;
1987 send_cancelled
= true;
1989 KeSetEvent(&send
->cleared_event
, 0, false);
1995 ExReleaseResourceLite(&Vcb
->send_load_lock
);
1997 if (send_cancelled
) {
1998 while (Vcb
->running_sends
!= 0) {
1999 ExAcquireResourceExclusiveLite(&Vcb
->send_load_lock
, true);
2000 ExReleaseResourceLite(&Vcb
->send_load_lock
);
2005 Status
= registry_mark_volume_unmounted(&Vcb
->superblock
.uuid
);
2006 if (!NT_SUCCESS(Status
) && Status
!= STATUS_TOO_LATE
)
2007 WARN("registry_mark_volume_unmounted returned %08lx\n", Status
);
2009 for (i
= 0; i
< Vcb
->calcthreads
.num_threads
; i
++) {
2010 Vcb
->calcthreads
.threads
[i
].quit
= true;
2013 KeSetEvent(&Vcb
->calcthreads
.event
, 0, false);
2015 for (i
= 0; i
< Vcb
->calcthreads
.num_threads
; i
++) {
2016 KeWaitForSingleObject(&Vcb
->calcthreads
.threads
[i
].finished
, Executive
, KernelMode
, false, NULL
);
2018 ZwClose(Vcb
->calcthreads
.threads
[i
].handle
);
2021 ExFreePool(Vcb
->calcthreads
.threads
);
2024 KeSetTimer(&Vcb
->flush_thread_timer
, time
, NULL
); // trigger the timer early
2025 KeWaitForSingleObject(&Vcb
->flush_thread_finished
, Executive
, KernelMode
, false, NULL
);
2027 reap_fcb(Vcb
->volume_fcb
);
2028 reap_fcb(Vcb
->dummy_fcb
);
2031 ObDereferenceObject(Vcb
->root_file
);
2033 le
= Vcb
->chunks
.Flink
;
2034 while (le
!= &Vcb
->chunks
) {
2035 chunk
* c
= CONTAINING_RECORD(le
, chunk
, list_entry
);
2045 while (!IsListEmpty(&Vcb
->all_fcbs
)) {
2046 fcb
* fcb
= CONTAINING_RECORD(Vcb
->all_fcbs
.Flink
, struct _fcb
, list_entry_all
);
2051 while (!IsListEmpty(&Vcb
->sys_chunks
)) {
2052 sys_chunk
* sc
= CONTAINING_RECORD(RemoveHeadList(&Vcb
->sys_chunks
), sys_chunk
, list_entry
);
2055 ExFreePool(sc
->data
);
2060 while (!IsListEmpty(&Vcb
->roots
)) {
2061 root
* r
= CONTAINING_RECORD(RemoveHeadList(&Vcb
->roots
), root
, list_entry
);
2063 ExDeleteResourceLite(&r
->nonpaged
->load_tree_lock
);
2064 ExFreePool(r
->nonpaged
);
2068 while (!IsListEmpty(&Vcb
->chunks
)) {
2069 chunk
* c
= CONTAINING_RECORD(RemoveHeadList(&Vcb
->chunks
), chunk
, list_entry
);
2071 while (!IsListEmpty(&c
->space
)) {
2072 LIST_ENTRY
* le2
= RemoveHeadList(&c
->space
);
2073 space
* s
= CONTAINING_RECORD(le2
, space
, list_entry
);
2078 while (!IsListEmpty(&c
->deleting
)) {
2079 LIST_ENTRY
* le2
= RemoveHeadList(&c
->deleting
);
2080 space
* s
= CONTAINING_RECORD(le2
, space
, list_entry
);
2086 ExFreePool(c
->devices
);
2091 ExDeleteResourceLite(&c
->range_locks_lock
);
2092 ExDeleteResourceLite(&c
->partial_stripes_lock
);
2093 ExDeleteResourceLite(&c
->lock
);
2094 ExDeleteResourceLite(&c
->changed_extents_lock
);
2096 ExFreePool(c
->chunk_item
);
2100 while (!IsListEmpty(&Vcb
->devices
)) {
2101 device
* dev
= CONTAINING_RECORD(RemoveHeadList(&Vcb
->devices
), device
, list_entry
);
2103 while (!IsListEmpty(&dev
->space
)) {
2104 LIST_ENTRY
* le2
= RemoveHeadList(&dev
->space
);
2105 space
* s
= CONTAINING_RECORD(le2
, space
, list_entry
);
2113 ExAcquireResourceExclusiveLite(&Vcb
->scrub
.stats_lock
, true);
2114 while (!IsListEmpty(&Vcb
->scrub
.errors
)) {
2115 scrub_error
* err
= CONTAINING_RECORD(RemoveHeadList(&Vcb
->scrub
.errors
), scrub_error
, list_entry
);
2119 ExReleaseResourceLite(&Vcb
->scrub
.stats_lock
);
2121 ExDeleteResourceLite(&Vcb
->fcb_lock
);
2122 ExDeleteResourceLite(&Vcb
->fileref_lock
);
2123 ExDeleteResourceLite(&Vcb
->load_lock
);
2124 ExDeleteResourceLite(&Vcb
->tree_lock
);
2125 ExDeleteResourceLite(&Vcb
->chunk_lock
);
2126 ExDeleteResourceLite(&Vcb
->dirty_fcbs_lock
);
2127 ExDeleteResourceLite(&Vcb
->dirty_filerefs_lock
);
2128 ExDeleteResourceLite(&Vcb
->dirty_subvols_lock
);
2129 ExDeleteResourceLite(&Vcb
->scrub
.stats_lock
);
2130 ExDeleteResourceLite(&Vcb
->send_load_lock
);
2132 ExDeletePagedLookasideList(&Vcb
->tree_data_lookaside
);
2133 ExDeletePagedLookasideList(&Vcb
->traverse_ptr_lookaside
);
2134 ExDeletePagedLookasideList(&Vcb
->batch_item_lookaside
);
2135 ExDeletePagedLookasideList(&Vcb
->fileref_lookaside
);
2136 ExDeletePagedLookasideList(&Vcb
->fcb_lookaside
);
2137 ExDeletePagedLookasideList(&Vcb
->name_bit_lookaside
);
2138 ExDeleteNPagedLookasideList(&Vcb
->range_lock_lookaside
);
2139 ExDeleteNPagedLookasideList(&Vcb
->fileref_np_lookaside
);
2140 ExDeleteNPagedLookasideList(&Vcb
->fcb_np_lookaside
);
2142 ZwClose(Vcb
->flush_thread_handle
);
2144 if (Vcb
->devobj
->AttachedDevice
)
2145 IoDetachDevice(Vcb
->devobj
);
2147 IoDeleteDevice(Vcb
->devobj
);
2150 static NTSTATUS
delete_fileref_fcb(_In_ file_ref
* fileref
, _In_opt_ PFILE_OBJECT FileObject
, _In_opt_ PIRP Irp
, _In_ LIST_ENTRY
* rollback
) {
2156 if (fileref
->fcb
->type
!= BTRFS_TYPE_DIRECTORY
&& fileref
->fcb
->inode_item
.st_size
> 0) {
2157 Status
= excise_extents(fileref
->fcb
->Vcb
, fileref
->fcb
, 0, sector_align(fileref
->fcb
->inode_item
.st_size
, fileref
->fcb
->Vcb
->superblock
.sector_size
), Irp
, rollback
);
2158 if (!NT_SUCCESS(Status
)) {
2159 ERR("excise_extents returned %08lx\n", Status
);
2164 fileref
->fcb
->Header
.AllocationSize
.QuadPart
= 0;
2165 fileref
->fcb
->Header
.FileSize
.QuadPart
= 0;
2166 fileref
->fcb
->Header
.ValidDataLength
.QuadPart
= 0;
2171 ccfs
.AllocationSize
= fileref
->fcb
->Header
.AllocationSize
;
2172 ccfs
.FileSize
= fileref
->fcb
->Header
.FileSize
;
2173 ccfs
.ValidDataLength
= fileref
->fcb
->Header
.ValidDataLength
;
2175 Status
= STATUS_SUCCESS
;
2178 CcSetFileSizes(FileObject
, &ccfs
);
2179 } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER
) {
2180 Status
= _SEH2_GetExceptionCode();
2183 if (!NT_SUCCESS(Status
)) {
2184 ERR("CcSetFileSizes threw exception %08lx\n", Status
);
2189 fileref
->fcb
->deleted
= true;
2191 le
= fileref
->children
.Flink
;
2192 while (le
!= &fileref
->children
) {
2193 file_ref
* fr2
= CONTAINING_RECORD(le
, file_ref
, list_entry
);
2195 if (fr2
->fcb
->ads
) {
2196 fr2
->fcb
->deleted
= true;
2197 mark_fcb_dirty(fr2
->fcb
);
2203 return STATUS_SUCCESS
;
2206 NTSTATUS
delete_fileref(_In_ file_ref
* fileref
, _In_opt_ PFILE_OBJECT FileObject
, _In_
bool make_orphan
, _In_opt_ PIRP Irp
, _In_ LIST_ENTRY
* rollback
) {
2207 LARGE_INTEGER newlength
, time
;
2212 KeQuerySystemTime(&time
);
2213 win_time_to_unix(time
, &now
);
2215 ExAcquireResourceExclusiveLite(fileref
->fcb
->Header
.Resource
, true);
2217 if (fileref
->deleted
) {
2218 ExReleaseResourceLite(fileref
->fcb
->Header
.Resource
);
2219 return STATUS_SUCCESS
;
2222 if (fileref
->fcb
->subvol
->send_ops
> 0) {
2223 ExReleaseResourceLite(fileref
->fcb
->Header
.Resource
);
2224 return STATUS_ACCESS_DENIED
;
2227 fileref
->deleted
= true;
2228 mark_fileref_dirty(fileref
);
2230 // delete INODE_ITEM (0x1)
2232 TRACE("nlink = %u\n", fileref
->fcb
->inode_item
.st_nlink
);
2234 if (!fileref
->fcb
->ads
) {
2235 if (fileref
->parent
->fcb
->subvol
== fileref
->fcb
->subvol
) {
2238 mark_fcb_dirty(fileref
->fcb
);
2240 fileref
->fcb
->inode_item_changed
= true;
2242 if (fileref
->fcb
->inode_item
.st_nlink
> 1 || make_orphan
) {
2243 fileref
->fcb
->inode_item
.st_nlink
--;
2244 fileref
->fcb
->inode_item
.transid
= fileref
->fcb
->Vcb
->superblock
.generation
;
2245 fileref
->fcb
->inode_item
.sequence
++;
2246 fileref
->fcb
->inode_item
.st_ctime
= now
;
2248 Status
= delete_fileref_fcb(fileref
, FileObject
, Irp
, rollback
);
2249 if (!NT_SUCCESS(Status
)) {
2250 ERR("delete_fileref_fcb returned %08lx\n", Status
);
2251 ExReleaseResourceLite(fileref
->fcb
->Header
.Resource
);
2257 le
= fileref
->fcb
->hardlinks
.Flink
;
2258 while (le
!= &fileref
->fcb
->hardlinks
) {
2259 hardlink
* hl
= CONTAINING_RECORD(le
, hardlink
, list_entry
);
2261 if (hl
->parent
== fileref
->parent
->fcb
->inode
&& hl
->index
== fileref
->dc
->index
) {
2262 RemoveEntryList(&hl
->list_entry
);
2264 if (hl
->name
.Buffer
)
2265 ExFreePool(hl
->name
.Buffer
);
2267 if (hl
->utf8
.Buffer
)
2268 ExFreePool(hl
->utf8
.Buffer
);
2277 } else if (fileref
->fcb
->subvol
->parent
== fileref
->parent
->fcb
->subvol
->id
) { // valid subvolume
2278 if (fileref
->fcb
->subvol
->root_item
.num_references
> 1) {
2279 fileref
->fcb
->subvol
->root_item
.num_references
--;
2281 mark_fcb_dirty(fileref
->fcb
); // so ROOT_ITEM gets updated
2285 // FIXME - we need a lock here
2287 RemoveEntryList(&fileref
->fcb
->subvol
->list_entry
);
2289 InsertTailList(&fileref
->fcb
->Vcb
->drop_roots
, &fileref
->fcb
->subvol
->list_entry
);
2291 le
= fileref
->children
.Flink
;
2292 while (le
!= &fileref
->children
) {
2293 file_ref
* fr2
= CONTAINING_RECORD(le
, file_ref
, list_entry
);
2295 if (fr2
->fcb
->ads
) {
2296 fr2
->fcb
->deleted
= true;
2297 mark_fcb_dirty(fr2
->fcb
);
2305 fileref
->fcb
->deleted
= true;
2306 mark_fcb_dirty(fileref
->fcb
);
2309 // remove dir_child from parent
2312 TRACE("delete file %.*S\n", (int)(fileref
->dc
->name
.Length
/ sizeof(WCHAR
)), fileref
->dc
->name
.Buffer
);
2314 ExAcquireResourceExclusiveLite(&fileref
->parent
->fcb
->nonpaged
->dir_children_lock
, true);
2315 RemoveEntryList(&fileref
->dc
->list_entry_index
);
2317 if (!fileref
->fcb
->ads
)
2318 remove_dir_child_from_hash_lists(fileref
->parent
->fcb
, fileref
->dc
);
2320 ExReleaseResourceLite(&fileref
->parent
->fcb
->nonpaged
->dir_children_lock
);
2322 if (!fileref
->oldutf8
.Buffer
)
2323 fileref
->oldutf8
= fileref
->dc
->utf8
;
2325 ExFreePool(fileref
->dc
->utf8
.Buffer
);
2327 utf8len
= fileref
->dc
->utf8
.Length
;
2329 fileref
->oldindex
= fileref
->dc
->index
;
2331 ExFreePool(fileref
->dc
->name
.Buffer
);
2332 ExFreePool(fileref
->dc
->name_uc
.Buffer
);
2333 ExFreePool(fileref
->dc
);
2338 // update INODE_ITEM of parent
2340 ExAcquireResourceExclusiveLite(fileref
->parent
->fcb
->Header
.Resource
, true);
2342 fileref
->parent
->fcb
->inode_item
.transid
= fileref
->fcb
->Vcb
->superblock
.generation
;
2343 fileref
->parent
->fcb
->inode_item
.sequence
++;
2344 fileref
->parent
->fcb
->inode_item
.st_ctime
= now
;
2346 if (!fileref
->fcb
->ads
) {
2347 TRACE("fileref->parent->fcb->inode_item.st_size (inode %I64x) was %I64x\n", fileref
->parent
->fcb
->inode
, fileref
->parent
->fcb
->inode_item
.st_size
);
2348 fileref
->parent
->fcb
->inode_item
.st_size
-= utf8len
* 2;
2349 TRACE("fileref->parent->fcb->inode_item.st_size (inode %I64x) now %I64x\n", fileref
->parent
->fcb
->inode
, fileref
->parent
->fcb
->inode_item
.st_size
);
2350 fileref
->parent
->fcb
->inode_item
.st_mtime
= now
;
2353 fileref
->parent
->fcb
->inode_item_changed
= true;
2354 ExReleaseResourceLite(fileref
->parent
->fcb
->Header
.Resource
);
2356 if (!fileref
->fcb
->ads
&& fileref
->parent
->dc
)
2357 send_notification_fcb(fileref
->parent
, FILE_NOTIFY_CHANGE_LAST_WRITE
, FILE_ACTION_MODIFIED
, NULL
);
2359 mark_fcb_dirty(fileref
->parent
->fcb
);
2361 fileref
->fcb
->subvol
->root_item
.ctransid
= fileref
->fcb
->Vcb
->superblock
.generation
;
2362 fileref
->fcb
->subvol
->root_item
.ctime
= now
;
2364 newlength
.QuadPart
= 0;
2366 if (FileObject
&& !CcUninitializeCacheMap(FileObject
, &newlength
, NULL
))
2367 TRACE("CcUninitializeCacheMap failed\n");
2369 ExReleaseResourceLite(fileref
->fcb
->Header
.Resource
);
2371 return STATUS_SUCCESS
;
2374 _Dispatch_type_(IRP_MJ_CLEANUP
)
2375 _Function_class_(DRIVER_DISPATCH
)
2376 static NTSTATUS __stdcall
drv_cleanup(_In_ PDEVICE_OBJECT DeviceObject
, _In_ PIRP Irp
) {
2378 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
2379 PFILE_OBJECT FileObject
= IrpSp
->FileObject
;
2380 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
2381 fcb
* fcb
= FileObject
->FsContext
;
2384 FsRtlEnterFileSystem();
2388 top_level
= is_top_level(Irp
);
2390 if (Vcb
&& Vcb
->type
== VCB_TYPE_VOLUME
) {
2391 Status
= vol_cleanup(DeviceObject
, Irp
);
2393 } else if (DeviceObject
== master_devobj
) {
2394 TRACE("closing file system\n");
2395 Status
= STATUS_SUCCESS
;
2397 } else if (!Vcb
|| Vcb
->type
!= VCB_TYPE_FS
) {
2398 Status
= STATUS_INVALID_PARAMETER
;
2402 if (FileObject
->Flags
& FO_CLEANUP_COMPLETE
) {
2403 TRACE("FileObject %p already cleaned up\n", FileObject
);
2404 Status
= STATUS_SUCCESS
;
2409 ERR("fcb was NULL\n");
2410 Status
= STATUS_INVALID_PARAMETER
;
2414 FsRtlCheckOplock(fcb_oplock(fcb
), Irp
, NULL
, NULL
, NULL
);
2416 // We have to use the pointer to Vcb stored in the fcb, as we can receive cleanup
2417 // messages belonging to other devices.
2419 if (FileObject
&& FileObject
->FsContext
) {
2425 ccb
= FileObject
->FsContext2
;
2426 fileref
= ccb
? ccb
->fileref
: NULL
;
2428 TRACE("cleanup called for FileObject %p\n", FileObject
);
2429 TRACE("fileref %p, refcount = %li, open_count = %li\n", fileref
, fileref
? fileref
->refcount
: 0, fileref
? fileref
->open_count
: 0);
2431 ExAcquireResourceSharedLite(&fcb
->Vcb
->tree_lock
, true);
2433 ExAcquireResourceExclusiveLite(fcb
->Header
.Resource
, true);
2435 IoRemoveShareAccess(FileObject
, &fcb
->share_access
);
2437 FsRtlFastUnlockAll(&fcb
->lock
, FileObject
, IoGetRequestorProcess(Irp
), NULL
);
2440 FsRtlNotifyCleanup(fcb
->Vcb
->NotifySync
, &fcb
->Vcb
->DirNotifyList
, ccb
);
2443 oc
= InterlockedDecrement(&fileref
->open_count
);
2444 #ifdef DEBUG_FCB_REFCOUNTS
2445 ERR("fileref %p: open_count now %i\n", fileref
, oc
);
2449 if (ccb
&& ccb
->options
& FILE_DELETE_ON_CLOSE
&& fileref
)
2450 fileref
->delete_on_close
= true;
2452 if (fileref
&& fileref
->delete_on_close
&& fcb
->type
== BTRFS_TYPE_DIRECTORY
&& fcb
->inode_item
.st_size
> 0 && fcb
!= fcb
->Vcb
->dummy_fcb
)
2453 fileref
->delete_on_close
= false;
2455 if (fcb
->Vcb
->locked
&& fcb
->Vcb
->locked_fileobj
== FileObject
) {
2456 TRACE("unlocking volume\n");
2457 do_unlock_volume(fcb
->Vcb
);
2458 FsRtlNotifyVolumeEvent(FileObject
, FSRTL_VOLUME_UNLOCK
);
2461 if (ccb
&& ccb
->reserving
) {
2462 fcb
->subvol
->reserved
= NULL
;
2463 ccb
->reserving
= false;
2464 // FIXME - flush all of subvol's fcbs
2467 if (fileref
&& (oc
== 0 || (fileref
->delete_on_close
&& fileref
->posix_delete
))) {
2468 if (!fcb
->Vcb
->removing
) {
2469 if (oc
== 0 && fileref
->fcb
->inode_item
.st_nlink
== 0 && fileref
!= fcb
->Vcb
->root_fileref
&&
2470 fcb
!= fcb
->Vcb
->volume_fcb
&& !fcb
->ads
) { // last handle closed on POSIX-deleted file
2471 LIST_ENTRY rollback
;
2473 InitializeListHead(&rollback
);
2475 Status
= delete_fileref_fcb(fileref
, FileObject
, Irp
, &rollback
);
2476 if (!NT_SUCCESS(Status
)) {
2477 ERR("delete_fileref_fcb returned %08lx\n", Status
);
2478 do_rollback(fcb
->Vcb
, &rollback
);
2479 ExReleaseResourceLite(fileref
->fcb
->Header
.Resource
);
2480 ExReleaseResourceLite(&fcb
->Vcb
->tree_lock
);
2484 clear_rollback(&rollback
);
2486 mark_fcb_dirty(fileref
->fcb
);
2487 } else if (fileref
->delete_on_close
&& fileref
!= fcb
->Vcb
->root_fileref
&& fcb
!= fcb
->Vcb
->volume_fcb
) {
2488 LIST_ENTRY rollback
;
2490 InitializeListHead(&rollback
);
2492 if (!fileref
->fcb
->ads
|| fileref
->dc
) {
2493 if (fileref
->fcb
->ads
) {
2494 send_notification_fileref(fileref
->parent
, fcb
->type
== BTRFS_TYPE_DIRECTORY
? FILE_NOTIFY_CHANGE_DIR_NAME
: FILE_NOTIFY_CHANGE_FILE_NAME
,
2495 FILE_ACTION_REMOVED
, &fileref
->dc
->name
);
2497 send_notification_fileref(fileref
, fcb
->type
== BTRFS_TYPE_DIRECTORY
? FILE_NOTIFY_CHANGE_DIR_NAME
: FILE_NOTIFY_CHANGE_FILE_NAME
, FILE_ACTION_REMOVED
, NULL
);
2500 ExReleaseResourceLite(fcb
->Header
.Resource
);
2503 // fileref_lock needs to be acquired before fcb->Header.Resource
2504 ExAcquireResourceExclusiveLite(&fcb
->Vcb
->fileref_lock
, true);
2506 Status
= delete_fileref(fileref
, FileObject
, oc
> 0 && fileref
->posix_delete
, Irp
, &rollback
);
2507 if (!NT_SUCCESS(Status
)) {
2508 ERR("delete_fileref returned %08lx\n", Status
);
2509 do_rollback(fcb
->Vcb
, &rollback
);
2510 ExReleaseResourceLite(&fcb
->Vcb
->fileref_lock
);
2511 ExReleaseResourceLite(&fcb
->Vcb
->tree_lock
);
2515 ExReleaseResourceLite(&fcb
->Vcb
->fileref_lock
);
2517 clear_rollback(&rollback
);
2518 } else if (FileObject
->Flags
& FO_CACHE_SUPPORTED
&& FileObject
->SectionObjectPointer
->DataSectionObject
) {
2519 IO_STATUS_BLOCK iosb
;
2522 ExReleaseResourceLite(fcb
->Header
.Resource
);
2526 CcFlushCache(FileObject
->SectionObjectPointer
, NULL
, 0, &iosb
);
2528 if (!NT_SUCCESS(iosb
.Status
))
2529 ERR("CcFlushCache returned %08lx\n", iosb
.Status
);
2531 if (!ExIsResourceAcquiredSharedLite(fcb
->Header
.PagingIoResource
)) {
2532 ExAcquireResourceExclusiveLite(fcb
->Header
.PagingIoResource
, true);
2533 ExReleaseResourceLite(fcb
->Header
.PagingIoResource
);
2536 CcPurgeCacheSection(FileObject
->SectionObjectPointer
, NULL
, 0, false);
2538 TRACE("flushed cache on close (FileObject = %p, fcb = %p, AllocationSize = %I64x, FileSize = %I64x, ValidDataLength = %I64x)\n",
2539 FileObject
, fcb
, fcb
->Header
.AllocationSize
.QuadPart
, fcb
->Header
.FileSize
.QuadPart
, fcb
->Header
.ValidDataLength
.QuadPart
);
2543 if (fcb
->Vcb
&& fcb
!= fcb
->Vcb
->volume_fcb
)
2544 CcUninitializeCacheMap(FileObject
, NULL
, NULL
);
2548 ExReleaseResourceLite(fcb
->Header
.Resource
);
2550 ExReleaseResourceLite(&fcb
->Vcb
->tree_lock
);
2552 FileObject
->Flags
|= FO_CLEANUP_COMPLETE
;
2555 Status
= STATUS_SUCCESS
;
2558 TRACE("returning %08lx\n", Status
);
2560 Irp
->IoStatus
.Status
= Status
;
2561 Irp
->IoStatus
.Information
= 0;
2563 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
2566 IoSetTopLevelIrp(NULL
);
2568 FsRtlExitFileSystem();
2574 bool get_file_attributes_from_xattr(_In_reads_bytes_(len
) char* val
, _In_
uint16_t len
, _Out_ ULONG
* atts
) {
2575 if (len
> 2 && val
[0] == '0' && val
[1] == 'x') {
2579 for (i
= 2; i
< len
; i
++) {
2582 if (val
[i
] >= '0' && val
[i
] <= '9')
2583 dosnum
|= val
[i
] - '0';
2584 else if (val
[i
] >= 'a' && val
[i
] <= 'f')
2585 dosnum
|= val
[i
] + 10 - 'a';
2586 else if (val
[i
] >= 'A' && val
[i
] <= 'F')
2587 dosnum
|= val
[i
] + 10 - 'a';
2590 TRACE("DOSATTRIB: %08lx\n", dosnum
);
2600 ULONG
get_file_attributes(_In_
_Requires_lock_held_(_Curr_
->tree_lock
) device_extension
* Vcb
, _In_ root
* r
, _In_
uint64_t inode
,
2601 _In_
uint8_t type
, _In_
bool dotfile
, _In_
bool ignore_xa
, _In_opt_ PIRP Irp
) {
2606 if (!ignore_xa
&& get_xattr(Vcb
, r
, inode
, EA_DOSATTRIB
, EA_DOSATTRIB_HASH
, (uint8_t**)&eaval
, &ealen
, Irp
)) {
2609 if (get_file_attributes_from_xattr(eaval
, ealen
, &dosnum
)) {
2612 if (type
== BTRFS_TYPE_DIRECTORY
)
2613 dosnum
|= FILE_ATTRIBUTE_DIRECTORY
;
2614 else if (type
== BTRFS_TYPE_SYMLINK
)
2615 dosnum
|= FILE_ATTRIBUTE_REPARSE_POINT
;
2617 if (type
!= BTRFS_TYPE_DIRECTORY
)
2618 dosnum
&= ~FILE_ATTRIBUTE_DIRECTORY
;
2620 if (inode
== SUBVOL_ROOT_INODE
) {
2621 if (r
->root_item
.flags
& BTRFS_SUBVOL_READONLY
)
2622 dosnum
|= FILE_ATTRIBUTE_READONLY
;
2624 dosnum
&= ~FILE_ATTRIBUTE_READONLY
;
2634 case BTRFS_TYPE_DIRECTORY
:
2635 att
= FILE_ATTRIBUTE_DIRECTORY
;
2638 case BTRFS_TYPE_SYMLINK
:
2639 att
= FILE_ATTRIBUTE_REPARSE_POINT
;
2647 if (dotfile
|| (r
->id
== BTRFS_ROOT_FSTREE
&& inode
== SUBVOL_ROOT_INODE
))
2648 att
|= FILE_ATTRIBUTE_HIDDEN
;
2650 att
|= FILE_ATTRIBUTE_ARCHIVE
;
2652 if (inode
== SUBVOL_ROOT_INODE
) {
2653 if (r
->root_item
.flags
& BTRFS_SUBVOL_READONLY
)
2654 att
|= FILE_ATTRIBUTE_READONLY
;
2656 att
&= ~FILE_ATTRIBUTE_READONLY
;
2659 // FIXME - get READONLY from ii->st_mode
2660 // FIXME - return SYSTEM for block/char devices?
2663 att
= FILE_ATTRIBUTE_NORMAL
;
2668 NTSTATUS
sync_read_phys(_In_ PDEVICE_OBJECT DeviceObject
, _In_ PFILE_OBJECT FileObject
, _In_
uint64_t StartingOffset
, _In_ ULONG Length
,
2669 _Out_writes_bytes_(Length
) PUCHAR Buffer
, _In_
bool override
) {
2670 IO_STATUS_BLOCK IoStatus
;
2671 LARGE_INTEGER Offset
;
2673 PIO_STACK_LOCATION IrpSp
;
2675 read_context context
;
2679 RtlZeroMemory(&context
, sizeof(read_context
));
2680 KeInitializeEvent(&context
.Event
, NotificationEvent
, false);
2682 Offset
.QuadPart
= (LONGLONG
)StartingOffset
;
2684 Irp
= IoAllocateIrp(DeviceObject
->StackSize
, false);
2687 ERR("IoAllocateIrp failed\n");
2688 return STATUS_INSUFFICIENT_RESOURCES
;
2691 Irp
->Flags
|= IRP_NOCACHE
;
2692 IrpSp
= IoGetNextIrpStackLocation(Irp
);
2693 IrpSp
->MajorFunction
= IRP_MJ_READ
;
2694 IrpSp
->FileObject
= FileObject
;
2697 IrpSp
->Flags
|= SL_OVERRIDE_VERIFY_VOLUME
;
2699 if (DeviceObject
->Flags
& DO_BUFFERED_IO
) {
2700 Irp
->AssociatedIrp
.SystemBuffer
= ExAllocatePoolWithTag(NonPagedPool
, Length
, ALLOC_TAG
);
2701 if (!Irp
->AssociatedIrp
.SystemBuffer
) {
2702 ERR("out of memory\n");
2703 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2707 Irp
->Flags
|= IRP_BUFFERED_IO
| IRP_DEALLOCATE_BUFFER
| IRP_INPUT_OPERATION
;
2709 Irp
->UserBuffer
= Buffer
;
2710 } else if (DeviceObject
->Flags
& DO_DIRECT_IO
) {
2711 Irp
->MdlAddress
= IoAllocateMdl(Buffer
, Length
, false, false, NULL
);
2712 if (!Irp
->MdlAddress
) {
2713 ERR("IoAllocateMdl failed\n");
2714 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2718 Status
= STATUS_SUCCESS
;
2721 MmProbeAndLockPages(Irp
->MdlAddress
, KernelMode
, IoWriteAccess
);
2722 } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER
) {
2723 Status
= _SEH2_GetExceptionCode();
2726 if (!NT_SUCCESS(Status
)) {
2727 ERR("MmProbeAndLockPages threw exception %08lx\n", Status
);
2728 IoFreeMdl(Irp
->MdlAddress
);
2732 Irp
->UserBuffer
= Buffer
;
2734 IrpSp
->Parameters
.Read
.Length
= Length
;
2735 IrpSp
->Parameters
.Read
.ByteOffset
= Offset
;
2737 Irp
->UserIosb
= &IoStatus
;
2739 Irp
->UserEvent
= &context
.Event
;
2741 IoSetCompletionRoutine(Irp
, read_completion
, &context
, true, true, true);
2743 Status
= IoCallDriver(DeviceObject
, Irp
);
2745 if (Status
== STATUS_PENDING
) {
2746 KeWaitForSingleObject(&context
.Event
, Executive
, KernelMode
, false, NULL
);
2747 Status
= context
.iosb
.Status
;
2750 if (DeviceObject
->Flags
& DO_DIRECT_IO
) {
2751 MmUnlockPages(Irp
->MdlAddress
);
2752 IoFreeMdl(Irp
->MdlAddress
);
2761 bool check_superblock_checksum(superblock
* sb
) {
2762 switch (sb
->csum_type
) {
2763 case CSUM_TYPE_CRC32C
: {
2764 uint32_t crc32
= ~calc_crc32c(0xffffffff, (uint8_t*)&sb
->uuid
, (ULONG
)sizeof(superblock
) - sizeof(sb
->checksum
));
2766 if (crc32
== *((uint32_t*)sb
->checksum
))
2769 WARN("crc32 was %08x, expected %08x\n", crc32
, *((uint32_t*)sb
->checksum
));
2774 case CSUM_TYPE_XXHASH
: {
2775 uint64_t hash
= XXH64(&sb
->uuid
, sizeof(superblock
) - sizeof(sb
->checksum
), 0);
2777 if (hash
== *((uint64_t*)sb
->checksum
))
2780 WARN("superblock hash was %I64x, expected %I64x\n", hash
, *((uint64_t*)sb
->checksum
));
2785 case CSUM_TYPE_SHA256
: {
2786 uint8_t hash
[SHA256_HASH_SIZE
];
2788 calc_sha256(hash
, &sb
->uuid
, sizeof(superblock
) - sizeof(sb
->checksum
));
2790 if (RtlCompareMemory(hash
, sb
, SHA256_HASH_SIZE
) == SHA256_HASH_SIZE
)
2793 WARN("superblock hash was invalid\n");
2798 case CSUM_TYPE_BLAKE2
: {
2799 uint8_t hash
[BLAKE2_HASH_SIZE
];
2801 blake2b(hash
, sizeof(hash
), &sb
->uuid
, sizeof(superblock
) - sizeof(sb
->checksum
));
2803 if (RtlCompareMemory(hash
, sb
, BLAKE2_HASH_SIZE
) == BLAKE2_HASH_SIZE
)
2806 WARN("superblock hash was invalid\n");
2812 WARN("unrecognized csum type %x\n", sb
->csum_type
);
2818 static NTSTATUS
read_superblock(_In_ device_extension
* Vcb
, _In_ PDEVICE_OBJECT device
, _In_ PFILE_OBJECT fileobj
, _In_
uint64_t length
) {
2822 uint8_t valid_superblocks
;
2824 to_read
= device
->SectorSize
== 0 ? sizeof(superblock
) : (ULONG
)sector_align(sizeof(superblock
), device
->SectorSize
);
2826 sb
= ExAllocatePoolWithTag(NonPagedPool
, to_read
, ALLOC_TAG
);
2828 ERR("out of memory\n");
2829 return STATUS_INSUFFICIENT_RESOURCES
;
2832 if (superblock_addrs
[0] + to_read
> length
) {
2833 WARN("device was too short to have any superblock\n");
2835 return STATUS_UNRECOGNIZED_VOLUME
;
2839 valid_superblocks
= 0;
2841 while (superblock_addrs
[i
] > 0) {
2842 if (i
> 0 && superblock_addrs
[i
] + to_read
> length
)
2845 Status
= sync_read_phys(device
, fileobj
, superblock_addrs
[i
], to_read
, (PUCHAR
)sb
, false);
2846 if (!NT_SUCCESS(Status
)) {
2847 ERR("Failed to read superblock %lu: %08lx\n", i
, Status
);
2852 if (sb
->magic
!= BTRFS_MAGIC
) {
2854 TRACE("not a BTRFS volume\n");
2856 return STATUS_UNRECOGNIZED_VOLUME
;
2859 TRACE("got superblock %lu!\n", i
);
2861 if (sb
->sector_size
== 0)
2862 WARN("superblock sector size was 0\n");
2863 else if (sb
->node_size
< sizeof(tree_header
) + sizeof(internal_node
) || sb
->node_size
> 0x10000)
2864 WARN("invalid node size %x\n", sb
->node_size
);
2865 else if ((sb
->node_size
% sb
->sector_size
) != 0)
2866 WARN("node size %x was not a multiple of sector_size %x\n", sb
->node_size
, sb
->sector_size
);
2867 else if (check_superblock_checksum(sb
) && (valid_superblocks
== 0 || sb
->generation
> Vcb
->superblock
.generation
)) {
2868 RtlCopyMemory(&Vcb
->superblock
, sb
, sizeof(superblock
));
2869 valid_superblocks
++;
2878 if (valid_superblocks
== 0) {
2879 ERR("could not find any valid superblocks\n");
2880 return STATUS_INTERNAL_ERROR
;
2883 TRACE("label is %s\n", Vcb
->superblock
.label
);
2885 return STATUS_SUCCESS
;
2888 NTSTATUS
dev_ioctl(_In_ PDEVICE_OBJECT DeviceObject
, _In_ ULONG ControlCode
, _In_reads_bytes_opt_(InputBufferSize
) PVOID InputBuffer
, _In_ ULONG InputBufferSize
,
2889 _Out_writes_bytes_opt_(OutputBufferSize
) PVOID OutputBuffer
, _In_ ULONG OutputBufferSize
, _In_
bool Override
, _Out_opt_ IO_STATUS_BLOCK
* iosb
) {
2893 PIO_STACK_LOCATION IrpSp
;
2894 IO_STATUS_BLOCK IoStatus
;
2896 KeInitializeEvent(&Event
, NotificationEvent
, false);
2898 Irp
= IoBuildDeviceIoControlRequest(ControlCode
,
2908 if (!Irp
) return STATUS_INSUFFICIENT_RESOURCES
;
2911 IrpSp
= IoGetNextIrpStackLocation(Irp
);
2912 IrpSp
->Flags
|= SL_OVERRIDE_VERIFY_VOLUME
;
2915 Status
= IoCallDriver(DeviceObject
, Irp
);
2917 if (Status
== STATUS_PENDING
) {
2918 KeWaitForSingleObject(&Event
, Executive
, KernelMode
, false, NULL
);
2919 Status
= IoStatus
.Status
;
2928 _Requires_exclusive_lock_held_(Vcb
->tree_lock
)
2929 static NTSTATUS
add_root(_Inout_ device_extension
* Vcb
, _In_
uint64_t id
, _In_
uint64_t addr
,
2930 _In_
uint64_t generation
, _In_opt_ traverse_ptr
* tp
) {
2931 root
* r
= ExAllocatePoolWithTag(PagedPool
, sizeof(root
), ALLOC_TAG
);
2933 ERR("out of memory\n");
2934 return STATUS_INSUFFICIENT_RESOURCES
;
2939 r
->received
= false;
2941 r
->treeholder
.address
= addr
;
2942 r
->treeholder
.tree
= NULL
;
2943 r
->treeholder
.generation
= generation
;
2946 r
->fcbs_version
= 0;
2947 r
->checked_for_orphans
= false;
2949 InitializeListHead(&r
->fcbs
);
2950 RtlZeroMemory(r
->fcbs_ptrs
, sizeof(LIST_ENTRY
*) * 256);
2952 r
->nonpaged
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(root_nonpaged
), ALLOC_TAG
);
2954 ERR("out of memory\n");
2956 return STATUS_INSUFFICIENT_RESOURCES
;
2959 ExInitializeResourceLite(&r
->nonpaged
->load_tree_lock
);
2964 RtlCopyMemory(&r
->root_item
, tp
->item
->data
, min(sizeof(ROOT_ITEM
), tp
->item
->size
));
2965 if (tp
->item
->size
< sizeof(ROOT_ITEM
))
2966 RtlZeroMemory(((uint8_t*)&r
->root_item
) + tp
->item
->size
, sizeof(ROOT_ITEM
) - tp
->item
->size
);
2968 RtlZeroMemory(&r
->root_item
, sizeof(ROOT_ITEM
));
2970 if (!Vcb
->readonly
&& (r
->id
== BTRFS_ROOT_ROOT
|| r
->id
== BTRFS_ROOT_FSTREE
|| (r
->id
>= 0x100 && !(r
->id
& 0xf000000000000000)))) { // FS tree root
2971 // FIXME - don't call this if subvol is readonly (though we will have to if we ever toggle this flag)
2972 get_last_inode(Vcb
, r
, NULL
);
2974 if (r
->id
== BTRFS_ROOT_ROOT
&& r
->lastinode
< 0x100)
2975 r
->lastinode
= 0x100;
2978 InsertTailList(&Vcb
->roots
, &r
->list_entry
);
2981 case BTRFS_ROOT_ROOT
:
2985 case BTRFS_ROOT_EXTENT
:
2986 Vcb
->extent_root
= r
;
2989 case BTRFS_ROOT_CHUNK
:
2990 Vcb
->chunk_root
= r
;
2993 case BTRFS_ROOT_DEVTREE
:
2997 case BTRFS_ROOT_CHECKSUM
:
2998 Vcb
->checksum_root
= r
;
3001 case BTRFS_ROOT_UUID
:
3005 case BTRFS_ROOT_FREE_SPACE
:
3006 Vcb
->space_root
= r
;
3009 case BTRFS_ROOT_DATA_RELOC
:
3010 Vcb
->data_reloc_root
= r
;
3014 return STATUS_SUCCESS
;
3017 static NTSTATUS
look_for_roots(_Requires_exclusive_lock_held_(_Curr_
->tree_lock
) _In_ device_extension
* Vcb
, _In_opt_ PIRP Irp
) {
3018 traverse_ptr tp
, next_tp
;
3023 searchkey
.obj_id
= 0;
3024 searchkey
.obj_type
= 0;
3025 searchkey
.offset
= 0;
3027 Status
= find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, false, Irp
);
3028 if (!NT_SUCCESS(Status
)) {
3029 ERR("error - find_item returned %08lx\n", Status
);
3034 TRACE("(%I64x,%x,%I64x)\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
3036 if (tp
.item
->key
.obj_type
== TYPE_ROOT_ITEM
) {
3037 ROOT_ITEM
* ri
= (ROOT_ITEM
*)tp
.item
->data
;
3039 if (tp
.item
->size
< offsetof(ROOT_ITEM
, byte_limit
)) {
3040 ERR("(%I64x,%x,%I64x) was %u bytes, expected at least %Iu\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, tp
.item
->size
, offsetof(ROOT_ITEM
, byte_limit
));
3042 TRACE("root %I64x - address %I64x\n", tp
.item
->key
.obj_id
, ri
->block_number
);
3044 Status
= add_root(Vcb
, tp
.item
->key
.obj_id
, ri
->block_number
, ri
->generation
, &tp
);
3045 if (!NT_SUCCESS(Status
)) {
3046 ERR("add_root returned %08lx\n", Status
);
3050 } else if (tp
.item
->key
.obj_type
== TYPE_ROOT_BACKREF
&& !IsListEmpty(&Vcb
->roots
)) {
3051 root
* lastroot
= CONTAINING_RECORD(Vcb
->roots
.Blink
, root
, list_entry
);
3053 if (lastroot
->id
== tp
.item
->key
.obj_id
)
3054 lastroot
->parent
= tp
.item
->key
.offset
;
3057 b
= find_next_item(Vcb
, &tp
, &next_tp
, false, Irp
);
3063 if (!Vcb
->readonly
&& !Vcb
->data_reloc_root
) {
3071 WARN("data reloc root doesn't exist, creating it\n");
3073 Status
= create_root(Vcb
, BTRFS_ROOT_DATA_RELOC
, &reloc_root
, false, 0, Irp
);
3075 if (!NT_SUCCESS(Status
)) {
3076 ERR("create_root returned %08lx\n", Status
);
3080 reloc_root
->root_item
.inode
.generation
= 1;
3081 reloc_root
->root_item
.inode
.st_size
= 3;
3082 reloc_root
->root_item
.inode
.st_blocks
= Vcb
->superblock
.node_size
;
3083 reloc_root
->root_item
.inode
.st_nlink
= 1;
3084 reloc_root
->root_item
.inode
.st_mode
= 040755;
3085 reloc_root
->root_item
.inode
.flags
= 0xffffffff80000000;
3086 reloc_root
->root_item
.objid
= SUBVOL_ROOT_INODE
;
3087 reloc_root
->root_item
.bytes_used
= Vcb
->superblock
.node_size
;
3089 ii
= ExAllocatePoolWithTag(PagedPool
, sizeof(INODE_ITEM
), ALLOC_TAG
);
3091 ERR("out of memory\n");
3092 return STATUS_INSUFFICIENT_RESOURCES
;
3095 KeQuerySystemTime(&time
);
3096 win_time_to_unix(time
, &now
);
3098 RtlZeroMemory(ii
, sizeof(INODE_ITEM
));
3099 ii
->generation
= Vcb
->superblock
.generation
;
3100 ii
->st_blocks
= Vcb
->superblock
.node_size
;
3102 ii
->st_mode
= 040755;
3107 Status
= insert_tree_item(Vcb
, reloc_root
, SUBVOL_ROOT_INODE
, TYPE_INODE_ITEM
, 0, ii
, sizeof(INODE_ITEM
), NULL
, Irp
);
3108 if (!NT_SUCCESS(Status
)) {
3109 ERR("insert_tree_item returned %08lx\n", Status
);
3114 irlen
= (uint16_t)offsetof(INODE_REF
, name
[0]) + 2;
3115 ir
= ExAllocatePoolWithTag(PagedPool
, irlen
, ALLOC_TAG
);
3117 ERR("out of memory\n");
3118 return STATUS_INSUFFICIENT_RESOURCES
;
3126 Status
= insert_tree_item(Vcb
, reloc_root
, SUBVOL_ROOT_INODE
, TYPE_INODE_REF
, SUBVOL_ROOT_INODE
, ir
, irlen
, NULL
, Irp
);
3127 if (!NT_SUCCESS(Status
)) {
3128 ERR("insert_tree_item returned %08lx\n", Status
);
3133 Vcb
->data_reloc_root
= reloc_root
;
3134 Vcb
->need_write
= true;
3137 return STATUS_SUCCESS
;
3140 static NTSTATUS
find_disk_holes(_In_
_Requires_lock_held_(_Curr_
->tree_lock
) device_extension
* Vcb
, _In_ device
* dev
, _In_opt_ PIRP Irp
) {
3142 traverse_ptr tp
, next_tp
;
3147 InitializeListHead(&dev
->space
);
3149 searchkey
.obj_id
= 0;
3150 searchkey
.obj_type
= TYPE_DEV_STATS
;
3151 searchkey
.offset
= dev
->devitem
.dev_id
;
3153 Status
= find_item(Vcb
, Vcb
->dev_root
, &tp
, &searchkey
, false, Irp
);
3154 if (NT_SUCCESS(Status
) && !keycmp(tp
.item
->key
, searchkey
))
3155 RtlCopyMemory(dev
->stats
, tp
.item
->data
, min(sizeof(uint64_t) * 5, tp
.item
->size
));
3157 searchkey
.obj_id
= dev
->devitem
.dev_id
;
3158 searchkey
.obj_type
= TYPE_DEV_EXTENT
;
3159 searchkey
.offset
= 0;
3161 Status
= find_item(Vcb
, Vcb
->dev_root
, &tp
, &searchkey
, false, Irp
);
3162 if (!NT_SUCCESS(Status
)) {
3163 ERR("error - find_item returned %08lx\n", Status
);
3170 if (tp
.item
->key
.obj_id
== dev
->devitem
.dev_id
&& tp
.item
->key
.obj_type
== TYPE_DEV_EXTENT
) {
3171 if (tp
.item
->size
>= sizeof(DEV_EXTENT
)) {
3172 DEV_EXTENT
* de
= (DEV_EXTENT
*)tp
.item
->data
;
3174 if (tp
.item
->key
.offset
> lastaddr
) {
3175 Status
= add_space_entry(&dev
->space
, NULL
, lastaddr
, tp
.item
->key
.offset
- lastaddr
);
3176 if (!NT_SUCCESS(Status
)) {
3177 ERR("add_space_entry returned %08lx\n", Status
);
3182 lastaddr
= tp
.item
->key
.offset
+ de
->length
;
3184 ERR("(%I64x,%x,%I64x) was %u bytes, expected %Iu\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, tp
.item
->size
, sizeof(DEV_EXTENT
));
3188 b
= find_next_item(Vcb
, &tp
, &next_tp
, false, Irp
);
3192 if (tp
.item
->key
.obj_id
> searchkey
.obj_id
|| tp
.item
->key
.obj_type
> searchkey
.obj_type
)
3197 if (lastaddr
< dev
->devitem
.num_bytes
) {
3198 Status
= add_space_entry(&dev
->space
, NULL
, lastaddr
, dev
->devitem
.num_bytes
- lastaddr
);
3199 if (!NT_SUCCESS(Status
)) {
3200 ERR("add_space_entry returned %08lx\n", Status
);
3205 // The Linux driver doesn't like to allocate chunks within the first megabyte of a device.
3207 space_list_subtract2(&dev
->space
, NULL
, 0, 0x100000, NULL
, NULL
);
3209 return STATUS_SUCCESS
;
3212 static void add_device_to_list(_In_ device_extension
* Vcb
, _In_ device
* dev
) {
3215 le
= Vcb
->devices
.Flink
;
3217 while (le
!= &Vcb
->devices
) {
3218 device
* dev2
= CONTAINING_RECORD(le
, device
, list_entry
);
3220 if (dev2
->devitem
.dev_id
> dev
->devitem
.dev_id
) {
3221 InsertHeadList(le
->Blink
, &dev
->list_entry
);
3228 InsertTailList(&Vcb
->devices
, &dev
->list_entry
);
3232 device
* find_device_from_uuid(_In_ device_extension
* Vcb
, _In_ BTRFS_UUID
* uuid
) {
3233 volume_device_extension
* vde
;
3234 pdo_device_extension
* pdode
;
3237 le
= Vcb
->devices
.Flink
;
3238 while (le
!= &Vcb
->devices
) {
3239 device
* dev
= CONTAINING_RECORD(le
, device
, list_entry
);
3241 TRACE("device %I64x, uuid %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n", dev
->devitem
.dev_id
,
3242 dev
->devitem
.device_uuid
.uuid
[0], dev
->devitem
.device_uuid
.uuid
[1], dev
->devitem
.device_uuid
.uuid
[2], dev
->devitem
.device_uuid
.uuid
[3], dev
->devitem
.device_uuid
.uuid
[4], dev
->devitem
.device_uuid
.uuid
[5], dev
->devitem
.device_uuid
.uuid
[6], dev
->devitem
.device_uuid
.uuid
[7],
3243 dev
->devitem
.device_uuid
.uuid
[8], dev
->devitem
.device_uuid
.uuid
[9], dev
->devitem
.device_uuid
.uuid
[10], dev
->devitem
.device_uuid
.uuid
[11], dev
->devitem
.device_uuid
.uuid
[12], dev
->devitem
.device_uuid
.uuid
[13], dev
->devitem
.device_uuid
.uuid
[14], dev
->devitem
.device_uuid
.uuid
[15]);
3245 if (RtlCompareMemory(&dev
->devitem
.device_uuid
, uuid
, sizeof(BTRFS_UUID
)) == sizeof(BTRFS_UUID
)) {
3246 TRACE("returning device %I64x\n", dev
->devitem
.dev_id
);
3260 ExAcquireResourceSharedLite(&pdode
->child_lock
, true);
3262 if (Vcb
->devices_loaded
< Vcb
->superblock
.num_devices
) {
3263 le
= pdode
->children
.Flink
;
3265 while (le
!= &pdode
->children
) {
3266 volume_child
* vc
= CONTAINING_RECORD(le
, volume_child
, list_entry
);
3268 if (RtlCompareMemory(uuid
, &vc
->uuid
, sizeof(BTRFS_UUID
)) == sizeof(BTRFS_UUID
)) {
3271 dev
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(device
), ALLOC_TAG
);
3273 ExReleaseResourceLite(&pdode
->child_lock
);
3274 ERR("out of memory\n");
3278 RtlZeroMemory(dev
, sizeof(device
));
3279 dev
->devobj
= vc
->devobj
;
3280 dev
->fileobj
= vc
->fileobj
;
3281 dev
->devitem
.device_uuid
= *uuid
;
3282 dev
->devitem
.dev_id
= vc
->devid
;
3283 dev
->devitem
.num_bytes
= vc
->size
;
3284 dev
->seeding
= vc
->seeding
;
3285 dev
->readonly
= dev
->seeding
;
3287 dev
->removable
= false;
3288 dev
->disk_num
= vc
->disk_num
;
3289 dev
->part_num
= vc
->part_num
;
3290 dev
->num_trim_entries
= 0;
3291 InitializeListHead(&dev
->trim_list
);
3293 add_device_to_list(Vcb
, dev
);
3294 Vcb
->devices_loaded
++;
3296 ExReleaseResourceLite(&pdode
->child_lock
);
3305 ExReleaseResourceLite(&pdode
->child_lock
);
3308 WARN("could not find device with uuid %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n",
3309 uuid
->uuid
[0], uuid
->uuid
[1], uuid
->uuid
[2], uuid
->uuid
[3], uuid
->uuid
[4], uuid
->uuid
[5], uuid
->uuid
[6], uuid
->uuid
[7],
3310 uuid
->uuid
[8], uuid
->uuid
[9], uuid
->uuid
[10], uuid
->uuid
[11], uuid
->uuid
[12], uuid
->uuid
[13], uuid
->uuid
[14], uuid
->uuid
[15]);
3315 static bool is_device_removable(_In_ PDEVICE_OBJECT devobj
) {
3317 STORAGE_HOTPLUG_INFO shi
;
3319 Status
= dev_ioctl(devobj
, IOCTL_STORAGE_GET_HOTPLUG_INFO
, NULL
, 0, &shi
, sizeof(STORAGE_HOTPLUG_INFO
), true, NULL
);
3321 if (!NT_SUCCESS(Status
)) {
3322 ERR("dev_ioctl returned %08lx\n", Status
);
3326 return shi
.MediaRemovable
!= 0 ? true : false;
3329 static ULONG
get_device_change_count(_In_ PDEVICE_OBJECT devobj
) {
3332 IO_STATUS_BLOCK iosb
;
3334 Status
= dev_ioctl(devobj
, IOCTL_STORAGE_CHECK_VERIFY
, NULL
, 0, &cc
, sizeof(ULONG
), true, &iosb
);
3336 if (!NT_SUCCESS(Status
)) {
3337 ERR("dev_ioctl returned %08lx\n", Status
);
3341 if (iosb
.Information
< sizeof(ULONG
)) {
3342 ERR("iosb.Information was too short\n");
3349 void init_device(_In_ device_extension
* Vcb
, _Inout_ device
* dev
, _In_
bool get_nums
) {
3352 ATA_PASS_THROUGH_EX
* apte
;
3353 STORAGE_PROPERTY_QUERY spq
;
3354 DEVICE_TRIM_DESCRIPTOR dtd
;
3356 dev
->removable
= is_device_removable(dev
->devobj
);
3357 dev
->change_count
= dev
->removable
? get_device_change_count(dev
->devobj
) : 0;
3360 STORAGE_DEVICE_NUMBER sdn
;
3362 Status
= dev_ioctl(dev
->devobj
, IOCTL_STORAGE_GET_DEVICE_NUMBER
, NULL
, 0,
3363 &sdn
, sizeof(STORAGE_DEVICE_NUMBER
), true, NULL
);
3365 if (!NT_SUCCESS(Status
)) {
3366 WARN("IOCTL_STORAGE_GET_DEVICE_NUMBER returned %08lx\n", Status
);
3367 dev
->disk_num
= 0xffffffff;
3368 dev
->part_num
= 0xffffffff;
3370 dev
->disk_num
= sdn
.DeviceNumber
;
3371 dev
->part_num
= sdn
.PartitionNumber
;
3376 dev
->readonly
= dev
->seeding
;
3378 dev
->num_trim_entries
= 0;
3379 dev
->stats_changed
= false;
3380 InitializeListHead(&dev
->trim_list
);
3382 if (!dev
->readonly
) {
3383 Status
= dev_ioctl(dev
->devobj
, IOCTL_DISK_IS_WRITABLE
, NULL
, 0,
3384 NULL
, 0, true, NULL
);
3385 if (Status
== STATUS_MEDIA_WRITE_PROTECTED
)
3386 dev
->readonly
= true;
3389 aptelen
= sizeof(ATA_PASS_THROUGH_EX
) + 512;
3390 apte
= ExAllocatePoolWithTag(NonPagedPool
, aptelen
, ALLOC_TAG
);
3392 ERR("out of memory\n");
3396 RtlZeroMemory(apte
, aptelen
);
3398 apte
->Length
= sizeof(ATA_PASS_THROUGH_EX
);
3399 apte
->AtaFlags
= ATA_FLAGS_DATA_IN
;
3400 apte
->DataTransferLength
= aptelen
- sizeof(ATA_PASS_THROUGH_EX
);
3401 apte
->TimeOutValue
= 3;
3402 apte
->DataBufferOffset
= apte
->Length
;
3403 apte
->CurrentTaskFile
[6] = IDE_COMMAND_IDENTIFY
;
3405 Status
= dev_ioctl(dev
->devobj
, IOCTL_ATA_PASS_THROUGH
, apte
, aptelen
,
3406 apte
, aptelen
, true, NULL
);
3408 if (!NT_SUCCESS(Status
))
3409 TRACE("IOCTL_ATA_PASS_THROUGH returned %08lx for IDENTIFY DEVICE\n", Status
);
3411 IDENTIFY_DEVICE_DATA
* idd
= (IDENTIFY_DEVICE_DATA
*)((uint8_t*)apte
+ sizeof(ATA_PASS_THROUGH_EX
));
3413 if (idd
->CommandSetSupport
.FlushCache
) {
3414 dev
->can_flush
= true;
3415 TRACE("FLUSH CACHE supported\n");
3417 TRACE("FLUSH CACHE not supported\n");
3422 #ifdef DEBUG_TRIM_EMULATION
3426 spq
.PropertyId
= StorageDeviceTrimProperty
;
3427 spq
.QueryType
= PropertyStandardQuery
;
3428 spq
.AdditionalParameters
[0] = 0;
3430 Status
= dev_ioctl(dev
->devobj
, IOCTL_STORAGE_QUERY_PROPERTY
, &spq
, sizeof(STORAGE_PROPERTY_QUERY
),
3431 &dtd
, sizeof(DEVICE_TRIM_DESCRIPTOR
), true, NULL
);
3433 if (NT_SUCCESS(Status
)) {
3434 if (dtd
.TrimEnabled
) {
3437 TRACE("TRIM supported\n");
3439 TRACE("TRIM not supported\n");
3443 RtlZeroMemory(dev
->stats
, sizeof(uint64_t) * 5);
3446 static NTSTATUS
load_chunk_root(_In_
_Requires_lock_held_(_Curr_
->tree_lock
) device_extension
* Vcb
, _In_opt_ PIRP Irp
) {
3447 traverse_ptr tp
, next_tp
;
3453 searchkey
.obj_id
= 0;
3454 searchkey
.obj_type
= 0;
3455 searchkey
.offset
= 0;
3457 Vcb
->data_flags
= 0;
3458 Vcb
->metadata_flags
= 0;
3459 Vcb
->system_flags
= 0;
3461 Status
= find_item(Vcb
, Vcb
->chunk_root
, &tp
, &searchkey
, false, Irp
);
3462 if (!NT_SUCCESS(Status
)) {
3463 ERR("error - find_item returned %08lx\n", Status
);
3468 TRACE("(%I64x,%x,%I64x)\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
3470 if (tp
.item
->key
.obj_id
== 1 && tp
.item
->key
.obj_type
== TYPE_DEV_ITEM
) {
3471 if (tp
.item
->size
< sizeof(DEV_ITEM
)) {
3472 ERR("(%I64x,%x,%I64x) was %u bytes, expected %Iu\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, tp
.item
->size
, sizeof(DEV_ITEM
));
3474 DEV_ITEM
* di
= (DEV_ITEM
*)tp
.item
->data
;
3478 le
= Vcb
->devices
.Flink
;
3479 while (le
!= &Vcb
->devices
) {
3480 device
* dev
= CONTAINING_RECORD(le
, device
, list_entry
);
3482 if (dev
->devobj
&& RtlCompareMemory(&dev
->devitem
.device_uuid
, &di
->device_uuid
, sizeof(BTRFS_UUID
)) == sizeof(BTRFS_UUID
)) {
3483 RtlCopyMemory(&dev
->devitem
, tp
.item
->data
, min(tp
.item
->size
, sizeof(DEV_ITEM
)));
3485 if (le
!= Vcb
->devices
.Flink
)
3486 init_device(Vcb
, dev
, true);
3495 if (!done
&& Vcb
->vde
) {
3496 volume_device_extension
* vde
= Vcb
->vde
;
3497 pdo_device_extension
* pdode
= vde
->pdode
;
3499 ExAcquireResourceSharedLite(&pdode
->child_lock
, true);
3501 if (Vcb
->devices_loaded
< Vcb
->superblock
.num_devices
) {
3502 le
= pdode
->children
.Flink
;
3504 while (le
!= &pdode
->children
) {
3505 volume_child
* vc
= CONTAINING_RECORD(le
, volume_child
, list_entry
);
3507 if (RtlCompareMemory(&di
->device_uuid
, &vc
->uuid
, sizeof(BTRFS_UUID
)) == sizeof(BTRFS_UUID
)) {
3510 dev
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(device
), ALLOC_TAG
);
3512 ExReleaseResourceLite(&pdode
->child_lock
);
3513 ERR("out of memory\n");
3514 return STATUS_INSUFFICIENT_RESOURCES
;
3517 RtlZeroMemory(dev
, sizeof(device
));
3519 dev
->devobj
= vc
->devobj
;
3520 dev
->fileobj
= vc
->fileobj
;
3521 RtlCopyMemory(&dev
->devitem
, di
, min(tp
.item
->size
, sizeof(DEV_ITEM
)));
3522 dev
->seeding
= vc
->seeding
;
3523 init_device(Vcb
, dev
, false);
3525 if (dev
->devitem
.num_bytes
> vc
->size
) {
3526 WARN("device %I64x: DEV_ITEM says %I64x bytes, but Windows only reports %I64x\n", tp
.item
->key
.offset
,
3527 dev
->devitem
.num_bytes
, vc
->size
);
3529 dev
->devitem
.num_bytes
= vc
->size
;
3532 dev
->disk_num
= vc
->disk_num
;
3533 dev
->part_num
= vc
->part_num
;
3534 add_device_to_list(Vcb
, dev
);
3535 Vcb
->devices_loaded
++;
3545 if (!Vcb
->options
.allow_degraded
) {
3546 ERR("volume not found: device %I64x, uuid %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n", tp
.item
->key
.offset
,
3547 di
->device_uuid
.uuid
[0], di
->device_uuid
.uuid
[1], di
->device_uuid
.uuid
[2], di
->device_uuid
.uuid
[3], di
->device_uuid
.uuid
[4], di
->device_uuid
.uuid
[5], di
->device_uuid
.uuid
[6], di
->device_uuid
.uuid
[7],
3548 di
->device_uuid
.uuid
[8], di
->device_uuid
.uuid
[9], di
->device_uuid
.uuid
[10], di
->device_uuid
.uuid
[11], di
->device_uuid
.uuid
[12], di
->device_uuid
.uuid
[13], di
->device_uuid
.uuid
[14], di
->device_uuid
.uuid
[15]);
3552 dev
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(device
), ALLOC_TAG
);
3554 ExReleaseResourceLite(&pdode
->child_lock
);
3555 ERR("out of memory\n");
3556 return STATUS_INSUFFICIENT_RESOURCES
;
3559 RtlZeroMemory(dev
, sizeof(device
));
3561 // Missing device, so we keep dev->devobj as NULL
3562 RtlCopyMemory(&dev
->devitem
, di
, min(tp
.item
->size
, sizeof(DEV_ITEM
)));
3563 InitializeListHead(&dev
->trim_list
);
3565 add_device_to_list(Vcb
, dev
);
3566 Vcb
->devices_loaded
++;
3570 ERR("unexpected device %I64x found\n", tp
.item
->key
.offset
);
3572 ExReleaseResourceLite(&pdode
->child_lock
);
3575 } else if (tp
.item
->key
.obj_type
== TYPE_CHUNK_ITEM
) {
3576 if (tp
.item
->size
< sizeof(CHUNK_ITEM
)) {
3577 ERR("(%I64x,%x,%I64x) was %u bytes, expected at least %Iu\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, tp
.item
->size
, sizeof(CHUNK_ITEM
));
3579 c
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(chunk
), ALLOC_TAG
);
3582 ERR("out of memory\n");
3583 return STATUS_INSUFFICIENT_RESOURCES
;
3586 c
->size
= tp
.item
->size
;
3587 c
->offset
= tp
.item
->key
.offset
;
3588 c
->used
= c
->oldused
= 0;
3589 c
->cache
= c
->old_cache
= NULL
;
3591 c
->readonly
= false;
3593 c
->cache_loaded
= false;
3595 c
->space_changed
= false;
3598 c
->chunk_item
= ExAllocatePoolWithTag(NonPagedPool
, tp
.item
->size
, ALLOC_TAG
);
3600 if (!c
->chunk_item
) {
3601 ERR("out of memory\n");
3603 return STATUS_INSUFFICIENT_RESOURCES
;
3606 RtlCopyMemory(c
->chunk_item
, tp
.item
->data
, tp
.item
->size
);
3608 if (c
->chunk_item
->type
& BLOCK_FLAG_DATA
&& c
->chunk_item
->type
> Vcb
->data_flags
)
3609 Vcb
->data_flags
= c
->chunk_item
->type
;
3611 if (c
->chunk_item
->type
& BLOCK_FLAG_METADATA
&& c
->chunk_item
->type
> Vcb
->metadata_flags
)
3612 Vcb
->metadata_flags
= c
->chunk_item
->type
;
3614 if (c
->chunk_item
->type
& BLOCK_FLAG_SYSTEM
&& c
->chunk_item
->type
> Vcb
->system_flags
)
3615 Vcb
->system_flags
= c
->chunk_item
->type
;
3617 if (c
->chunk_item
->type
& BLOCK_FLAG_RAID10
) {
3618 if (c
->chunk_item
->sub_stripes
== 0 || c
->chunk_item
->sub_stripes
> c
->chunk_item
->num_stripes
) {
3619 ERR("chunk %I64x: invalid stripes (num_stripes %u, sub_stripes %u)\n", c
->offset
, c
->chunk_item
->num_stripes
, c
->chunk_item
->sub_stripes
);
3620 ExFreePool(c
->chunk_item
);
3622 return STATUS_INTERNAL_ERROR
;
3626 if (c
->chunk_item
->num_stripes
> 0) {
3627 CHUNK_ITEM_STRIPE
* cis
= (CHUNK_ITEM_STRIPE
*)&c
->chunk_item
[1];
3630 c
->devices
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(device
*) * c
->chunk_item
->num_stripes
, ALLOC_TAG
);
3633 ERR("out of memory\n");
3634 ExFreePool(c
->chunk_item
);
3636 return STATUS_INSUFFICIENT_RESOURCES
;
3639 for (i
= 0; i
< c
->chunk_item
->num_stripes
; i
++) {
3640 c
->devices
[i
] = find_device_from_uuid(Vcb
, &cis
[i
].dev_uuid
);
3641 TRACE("device %u = %p\n", i
, c
->devices
[i
]);
3643 if (!c
->devices
[i
]) {
3644 ERR("missing device\n");
3645 ExFreePool(c
->chunk_item
);
3647 return STATUS_INTERNAL_ERROR
;
3650 if (c
->devices
[i
]->readonly
)
3654 ERR("chunk %I64x: number of stripes is 0\n", c
->offset
);
3655 ExFreePool(c
->chunk_item
);
3657 return STATUS_INTERNAL_ERROR
;
3660 ExInitializeResourceLite(&c
->lock
);
3661 ExInitializeResourceLite(&c
->changed_extents_lock
);
3663 InitializeListHead(&c
->space
);
3664 InitializeListHead(&c
->space_size
);
3665 InitializeListHead(&c
->deleting
);
3666 InitializeListHead(&c
->changed_extents
);
3668 InitializeListHead(&c
->range_locks
);
3669 ExInitializeResourceLite(&c
->range_locks_lock
);
3670 KeInitializeEvent(&c
->range_locks_event
, NotificationEvent
, false);
3672 InitializeListHead(&c
->partial_stripes
);
3673 ExInitializeResourceLite(&c
->partial_stripes_lock
);
3675 c
->last_alloc_set
= false;
3679 InsertTailList(&Vcb
->chunks
, &c
->list_entry
);
3681 c
->list_entry_balance
.Flink
= NULL
;
3685 b
= find_next_item(Vcb
, &tp
, &next_tp
, false, Irp
);
3691 Vcb
->log_to_phys_loaded
= true;
3693 if (Vcb
->data_flags
== 0)
3694 Vcb
->data_flags
= BLOCK_FLAG_DATA
| (Vcb
->superblock
.num_devices
> 1 ? BLOCK_FLAG_RAID0
: 0);
3696 if (Vcb
->metadata_flags
== 0)
3697 Vcb
->metadata_flags
= BLOCK_FLAG_METADATA
| (Vcb
->superblock
.num_devices
> 1 ? BLOCK_FLAG_RAID1
: BLOCK_FLAG_DUPLICATE
);
3699 if (Vcb
->system_flags
== 0)
3700 Vcb
->system_flags
= BLOCK_FLAG_SYSTEM
| (Vcb
->superblock
.num_devices
> 1 ? BLOCK_FLAG_RAID1
: BLOCK_FLAG_DUPLICATE
);
3702 if (Vcb
->superblock
.incompat_flags
& BTRFS_INCOMPAT_FLAGS_MIXED_GROUPS
) {
3703 Vcb
->metadata_flags
|= BLOCK_FLAG_DATA
;
3704 Vcb
->data_flags
= Vcb
->metadata_flags
;
3707 return STATUS_SUCCESS
;
3710 void protect_superblocks(_Inout_ chunk
* c
) {
3712 uint64_t off_start
, off_end
;
3714 // The Linux driver also protects all the space before the first superblock.
3715 // I realize this confuses physical and logical addresses, but this is what btrfs-progs does -
3716 // evidently Linux assumes the chunk at 0 is always SINGLE.
3717 if (c
->offset
< superblock_addrs
[0])
3718 space_list_subtract(c
, false, c
->offset
, superblock_addrs
[0] - c
->offset
, NULL
);
3720 while (superblock_addrs
[i
] != 0) {
3721 CHUNK_ITEM
* ci
= c
->chunk_item
;
3722 CHUNK_ITEM_STRIPE
* cis
= (CHUNK_ITEM_STRIPE
*)&ci
[1];
3724 if (ci
->type
& BLOCK_FLAG_RAID0
|| ci
->type
& BLOCK_FLAG_RAID10
) {
3725 for (j
= 0; j
< ci
->num_stripes
; j
++) {
3726 uint16_t sub_stripes
= max(ci
->sub_stripes
, 1);
3728 if (cis
[j
].offset
+ (ci
->size
* ci
->num_stripes
/ sub_stripes
) > superblock_addrs
[i
] && cis
[j
].offset
<= superblock_addrs
[i
] + sizeof(superblock
)) {
3731 uint16_t startoffstripe
;
3734 TRACE("cut out superblock in chunk %I64x\n", c
->offset
);
3736 off_start
= superblock_addrs
[i
] - cis
[j
].offset
;
3737 off_start
-= off_start
% ci
->stripe_length
;
3738 off_start
*= ci
->num_stripes
/ sub_stripes
;
3739 off_start
+= (j
/ sub_stripes
) * ci
->stripe_length
;
3741 off_end
= off_start
+ ci
->stripe_length
;
3744 get_raid0_offset(off_start
, ci
->stripe_length
, ci
->num_stripes
/ sub_stripes
, &startoff
, &startoffstripe
);
3745 TRACE("j = %u, startoffstripe = %u\n", j
, startoffstripe
);
3746 TRACE("startoff = %I64x, superblock = %I64x\n", startoff
+ cis
[j
].offset
, superblock_addrs
[i
]);
3749 space_list_subtract(c
, false, c
->offset
+ off_start
, off_end
- off_start
, NULL
);
3752 } else if (ci
->type
& BLOCK_FLAG_RAID5
) {
3753 uint64_t stripe_size
= ci
->size
/ (ci
->num_stripes
- 1);
3755 for (j
= 0; j
< ci
->num_stripes
; j
++) {
3756 if (cis
[j
].offset
+ stripe_size
> superblock_addrs
[i
] && cis
[j
].offset
<= superblock_addrs
[i
] + sizeof(superblock
)) {
3757 TRACE("cut out superblock in chunk %I64x\n", c
->offset
);
3759 off_start
= superblock_addrs
[i
] - cis
[j
].offset
;
3760 off_start
-= off_start
% ci
->stripe_length
;
3761 off_start
*= ci
->num_stripes
- 1;
3763 off_end
= sector_align(superblock_addrs
[i
] - cis
[j
].offset
+ sizeof(superblock
), ci
->stripe_length
);
3764 off_end
*= ci
->num_stripes
- 1;
3766 TRACE("cutting out %I64x, size %I64x\n", c
->offset
+ off_start
, off_end
- off_start
);
3768 space_list_subtract(c
, false, c
->offset
+ off_start
, off_end
- off_start
, NULL
);
3771 } else if (ci
->type
& BLOCK_FLAG_RAID6
) {
3772 uint64_t stripe_size
= ci
->size
/ (ci
->num_stripes
- 2);
3774 for (j
= 0; j
< ci
->num_stripes
; j
++) {
3775 if (cis
[j
].offset
+ stripe_size
> superblock_addrs
[i
] && cis
[j
].offset
<= superblock_addrs
[i
] + sizeof(superblock
)) {
3776 TRACE("cut out superblock in chunk %I64x\n", c
->offset
);
3778 off_start
= superblock_addrs
[i
] - cis
[j
].offset
;
3779 off_start
-= off_start
% ci
->stripe_length
;
3780 off_start
*= ci
->num_stripes
- 2;
3782 off_end
= sector_align(superblock_addrs
[i
] - cis
[j
].offset
+ sizeof(superblock
), ci
->stripe_length
);
3783 off_end
*= ci
->num_stripes
- 2;
3785 TRACE("cutting out %I64x, size %I64x\n", c
->offset
+ off_start
, off_end
- off_start
);
3787 space_list_subtract(c
, false, c
->offset
+ off_start
, off_end
- off_start
, NULL
);
3790 } else { // SINGLE, DUPLICATE, RAID1, RAID1C3, RAID1C4
3791 for (j
= 0; j
< ci
->num_stripes
; j
++) {
3792 if (cis
[j
].offset
+ ci
->size
> superblock_addrs
[i
] && cis
[j
].offset
<= superblock_addrs
[i
] + sizeof(superblock
)) {
3793 TRACE("cut out superblock in chunk %I64x\n", c
->offset
);
3795 // The Linux driver protects the whole stripe in which the superblock lives
3797 off_start
= ((superblock_addrs
[i
] - cis
[j
].offset
) / c
->chunk_item
->stripe_length
) * c
->chunk_item
->stripe_length
;
3798 off_end
= sector_align(superblock_addrs
[i
] - cis
[j
].offset
+ sizeof(superblock
), c
->chunk_item
->stripe_length
);
3800 space_list_subtract(c
, false, c
->offset
+ off_start
, off_end
- off_start
, NULL
);
3809 uint64_t chunk_estimate_phys_size(device_extension
* Vcb
, chunk
* c
, uint64_t u
) {
3810 uint64_t nfactor
, dfactor
;
3812 if (c
->chunk_item
->type
& BLOCK_FLAG_DUPLICATE
|| c
->chunk_item
->type
& BLOCK_FLAG_RAID1
|| c
->chunk_item
->type
& BLOCK_FLAG_RAID10
) {
3815 } else if (c
->chunk_item
->type
& BLOCK_FLAG_RAID5
) {
3816 nfactor
= Vcb
->superblock
.num_devices
- 1;
3817 dfactor
= Vcb
->superblock
.num_devices
;
3818 } else if (c
->chunk_item
->type
& BLOCK_FLAG_RAID6
) {
3819 nfactor
= Vcb
->superblock
.num_devices
- 2;
3820 dfactor
= Vcb
->superblock
.num_devices
;
3821 } else if (c
->chunk_item
->type
& BLOCK_FLAG_RAID1C3
) {
3824 } else if (c
->chunk_item
->type
& BLOCK_FLAG_RAID1C4
) {
3832 return u
* dfactor
/ nfactor
;
3835 NTSTATUS
find_chunk_usage(_In_
_Requires_lock_held_(_Curr_
->tree_lock
) device_extension
* Vcb
, _In_opt_ PIRP Irp
) {
3836 LIST_ENTRY
* le
= Vcb
->chunks
.Flink
;
3840 BLOCK_GROUP_ITEM
* bgi
;
3843 searchkey
.obj_type
= TYPE_BLOCK_GROUP_ITEM
;
3845 Vcb
->superblock
.bytes_used
= 0;
3847 while (le
!= &Vcb
->chunks
) {
3848 c
= CONTAINING_RECORD(le
, chunk
, list_entry
);
3850 searchkey
.obj_id
= c
->offset
;
3851 searchkey
.offset
= c
->chunk_item
->size
;
3853 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, false, Irp
);
3854 if (!NT_SUCCESS(Status
)) {
3855 ERR("error - find_item returned %08lx\n", Status
);
3859 if (!keycmp(searchkey
, tp
.item
->key
)) {
3860 if (tp
.item
->size
>= sizeof(BLOCK_GROUP_ITEM
)) {
3861 bgi
= (BLOCK_GROUP_ITEM
*)tp
.item
->data
;
3863 c
->used
= c
->oldused
= bgi
->used
;
3865 TRACE("chunk %I64x has %I64x bytes used\n", c
->offset
, c
->used
);
3867 Vcb
->superblock
.bytes_used
+= chunk_estimate_phys_size(Vcb
, c
, bgi
->used
);
3869 ERR("(%I64x;%I64x,%x,%I64x) is %u bytes, expected %Iu\n",
3870 Vcb
->extent_root
->id
, tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, tp
.item
->size
, sizeof(BLOCK_GROUP_ITEM
));
3877 Vcb
->chunk_usage_found
= true;
3879 return STATUS_SUCCESS
;
3882 static NTSTATUS
load_sys_chunks(_In_ device_extension
* Vcb
) {
3884 ULONG n
= Vcb
->superblock
.n
;
3887 if (n
> sizeof(KEY
)) {
3888 RtlCopyMemory(&key
, &Vcb
->superblock
.sys_chunk_array
[Vcb
->superblock
.n
- n
], sizeof(KEY
));
3891 return STATUS_SUCCESS
;
3893 TRACE("bootstrap: %I64x,%x,%I64x\n", key
.obj_id
, key
.obj_type
, key
.offset
);
3895 if (key
.obj_type
== TYPE_CHUNK_ITEM
) {
3900 if (n
< sizeof(CHUNK_ITEM
))
3901 return STATUS_SUCCESS
;
3903 ci
= (CHUNK_ITEM
*)&Vcb
->superblock
.sys_chunk_array
[Vcb
->superblock
.n
- n
];
3904 cisize
= sizeof(CHUNK_ITEM
) + (ci
->num_stripes
* sizeof(CHUNK_ITEM_STRIPE
));
3907 return STATUS_SUCCESS
;
3909 sc
= ExAllocatePoolWithTag(PagedPool
, sizeof(sys_chunk
), ALLOC_TAG
);
3912 ERR("out of memory\n");
3913 return STATUS_INSUFFICIENT_RESOURCES
;
3918 sc
->data
= ExAllocatePoolWithTag(PagedPool
, sc
->size
, ALLOC_TAG
);
3921 ERR("out of memory\n");
3923 return STATUS_INSUFFICIENT_RESOURCES
;
3926 RtlCopyMemory(sc
->data
, ci
, sc
->size
);
3927 InsertTailList(&Vcb
->sys_chunks
, &sc
->list_entry
);
3931 ERR("unexpected item %I64x,%x,%I64x in bootstrap\n", key
.obj_id
, key
.obj_type
, key
.offset
);
3932 return STATUS_INTERNAL_ERROR
;
3936 return STATUS_SUCCESS
;
3940 root
* find_default_subvol(_In_
_Requires_lock_held_(_Curr_
->tree_lock
) device_extension
* Vcb
, _In_opt_ PIRP Irp
) {
3943 static const char fn
[] = "default";
3944 static uint32_t crc32
= 0x8dbfc2d2;
3946 if (Vcb
->options
.subvol_id
!= 0) {
3947 le
= Vcb
->roots
.Flink
;
3948 while (le
!= &Vcb
->roots
) {
3949 root
* r
= CONTAINING_RECORD(le
, root
, list_entry
);
3951 if (r
->id
== Vcb
->options
.subvol_id
)
3958 if (Vcb
->superblock
.incompat_flags
& BTRFS_INCOMPAT_FLAGS_DEFAULT_SUBVOL
) {
3964 searchkey
.obj_id
= Vcb
->superblock
.root_dir_objectid
;
3965 searchkey
.obj_type
= TYPE_DIR_ITEM
;
3966 searchkey
.offset
= crc32
;
3968 Status
= find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, false, Irp
);
3969 if (!NT_SUCCESS(Status
)) {
3970 ERR("error - find_item returned %08lx\n", Status
);
3974 if (keycmp(tp
.item
->key
, searchkey
)) {
3975 ERR("could not find (%I64x,%x,%I64x) in root tree\n", searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
);
3979 if (tp
.item
->size
< sizeof(DIR_ITEM
)) {
3980 ERR("(%I64x,%x,%I64x) was %u bytes, expected at least %Iu\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, tp
.item
->size
, sizeof(DIR_ITEM
));
3984 di
= (DIR_ITEM
*)tp
.item
->data
;
3986 if (tp
.item
->size
< sizeof(DIR_ITEM
) - 1 + di
->n
) {
3987 ERR("(%I64x,%x,%I64x) was %u bytes, expected %Iu\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, tp
.item
->size
, sizeof(DIR_ITEM
) - 1 + di
->n
);
3991 if (di
->n
!= strlen(fn
) || RtlCompareMemory(di
->name
, fn
, di
->n
) != di
->n
) {
3992 ERR("root DIR_ITEM had same CRC32, but was not \"default\"\n");
3996 if (di
->key
.obj_type
!= TYPE_ROOT_ITEM
) {
3997 ERR("default root has key (%I64x,%x,%I64x), expected subvolume\n", di
->key
.obj_id
, di
->key
.obj_type
, di
->key
.offset
);
4001 le
= Vcb
->roots
.Flink
;
4002 while (le
!= &Vcb
->roots
) {
4003 root
* r
= CONTAINING_RECORD(le
, root
, list_entry
);
4005 if (r
->id
== di
->key
.obj_id
)
4011 ERR("could not find root %I64x, using default instead\n", di
->key
.obj_id
);
4015 le
= Vcb
->roots
.Flink
;
4016 while (le
!= &Vcb
->roots
) {
4017 root
* r
= CONTAINING_RECORD(le
, root
, list_entry
);
4019 if (r
->id
== BTRFS_ROOT_FSTREE
)
4028 void init_file_cache(_In_ PFILE_OBJECT FileObject
, _In_ CC_FILE_SIZES
* ccfs
) {
4029 TRACE("(%p, %p)\n", FileObject
, ccfs
);
4031 CcInitializeCacheMap(FileObject
, ccfs
, false, &cache_callbacks
, FileObject
);
4034 fCcSetAdditionalCacheAttributesEx(FileObject
, CC_ENABLE_DISK_IO_ACCOUNTING
);
4036 CcSetReadAheadGranularity(FileObject
, READ_AHEAD_GRANULARITY
);
4039 uint32_t get_num_of_processors() {
4040 KAFFINITY p
= KeQueryActiveProcessors();
4053 static NTSTATUS
create_calc_threads(_In_ PDEVICE_OBJECT DeviceObject
) {
4054 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
4055 OBJECT_ATTRIBUTES oa
;
4058 Vcb
->calcthreads
.num_threads
= get_num_of_processors();
4060 Vcb
->calcthreads
.threads
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(drv_calc_thread
) * Vcb
->calcthreads
.num_threads
, ALLOC_TAG
);
4061 if (!Vcb
->calcthreads
.threads
) {
4062 ERR("out of memory\n");
4063 return STATUS_INSUFFICIENT_RESOURCES
;
4066 InitializeListHead(&Vcb
->calcthreads
.job_list
);
4067 KeInitializeSpinLock(&Vcb
->calcthreads
.spinlock
);
4068 KeInitializeEvent(&Vcb
->calcthreads
.event
, NotificationEvent
, false);
4070 RtlZeroMemory(Vcb
->calcthreads
.threads
, sizeof(drv_calc_thread
) * Vcb
->calcthreads
.num_threads
);
4072 InitializeObjectAttributes(&oa
, NULL
, OBJ_KERNEL_HANDLE
, NULL
, NULL
);
4074 for (i
= 0; i
< Vcb
->calcthreads
.num_threads
; i
++) {
4077 Vcb
->calcthreads
.threads
[i
].DeviceObject
= DeviceObject
;
4078 Vcb
->calcthreads
.threads
[i
].number
= i
;
4079 KeInitializeEvent(&Vcb
->calcthreads
.threads
[i
].finished
, NotificationEvent
, false);
4081 Status
= PsCreateSystemThread(&Vcb
->calcthreads
.threads
[i
].handle
, 0, &oa
, NULL
, NULL
, calc_thread
, &Vcb
->calcthreads
.threads
[i
]);
4082 if (!NT_SUCCESS(Status
)) {
4085 ERR("PsCreateSystemThread returned %08lx\n", Status
);
4087 for (j
= 0; j
< i
; j
++) {
4088 Vcb
->calcthreads
.threads
[i
].quit
= true;
4091 KeSetEvent(&Vcb
->calcthreads
.event
, 0, false);
4097 return STATUS_SUCCESS
;
4100 static bool is_btrfs_volume(_In_ PDEVICE_OBJECT DeviceObject
) {
4102 MOUNTDEV_NAME mdn
, *mdn2
;
4105 Status
= dev_ioctl(DeviceObject
, IOCTL_MOUNTDEV_QUERY_DEVICE_NAME
, NULL
, 0, &mdn
, sizeof(MOUNTDEV_NAME
), true, NULL
);
4106 if (!NT_SUCCESS(Status
) && Status
!= STATUS_BUFFER_OVERFLOW
) {
4107 ERR("IOCTL_MOUNTDEV_QUERY_DEVICE_NAME returned %08lx\n", Status
);
4111 mdnsize
= (ULONG
)offsetof(MOUNTDEV_NAME
, Name
[0]) + mdn
.NameLength
;
4113 mdn2
= ExAllocatePoolWithTag(PagedPool
, mdnsize
, ALLOC_TAG
);
4115 ERR("out of memory\n");
4119 Status
= dev_ioctl(DeviceObject
, IOCTL_MOUNTDEV_QUERY_DEVICE_NAME
, NULL
, 0, mdn2
, mdnsize
, true, NULL
);
4120 if (!NT_SUCCESS(Status
)) {
4121 ERR("IOCTL_MOUNTDEV_QUERY_DEVICE_NAME returned %08lx\n", Status
);
4126 if (mdn2
->NameLength
> (sizeof(BTRFS_VOLUME_PREFIX
) - sizeof(WCHAR
)) &&
4127 RtlCompareMemory(mdn2
->Name
, BTRFS_VOLUME_PREFIX
, sizeof(BTRFS_VOLUME_PREFIX
) - sizeof(WCHAR
)) == sizeof(BTRFS_VOLUME_PREFIX
) - sizeof(WCHAR
)) {
4137 static NTSTATUS
get_device_pnp_name_guid(_In_ PDEVICE_OBJECT DeviceObject
, _Out_ PUNICODE_STRING pnp_name
, _In_
const GUID
* guid
) {
4139 WCHAR
*list
= NULL
, *s
;
4141 Status
= IoGetDeviceInterfaces((PVOID
)guid
, NULL
, 0, &list
);
4142 if (!NT_SUCCESS(Status
)) {
4143 ERR("IoGetDeviceInterfaces returned %08lx\n", Status
);
4149 PFILE_OBJECT FileObject
;
4150 PDEVICE_OBJECT devobj
;
4151 UNICODE_STRING name
;
4153 name
.Length
= name
.MaximumLength
= (USHORT
)wcslen(s
) * sizeof(WCHAR
);
4156 if (NT_SUCCESS(IoGetDeviceObjectPointer(&name
, FILE_READ_ATTRIBUTES
, &FileObject
, &devobj
))) {
4157 if (DeviceObject
== devobj
|| DeviceObject
== FileObject
->DeviceObject
) {
4158 ObDereferenceObject(FileObject
);
4160 pnp_name
->Buffer
= ExAllocatePoolWithTag(PagedPool
, name
.Length
, ALLOC_TAG
);
4161 if (!pnp_name
->Buffer
) {
4162 ERR("out of memory\n");
4163 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4167 RtlCopyMemory(pnp_name
->Buffer
, name
.Buffer
, name
.Length
);
4168 pnp_name
->Length
= pnp_name
->MaximumLength
= name
.Length
;
4170 Status
= STATUS_SUCCESS
;
4174 ObDereferenceObject(FileObject
);
4177 s
= &s
[wcslen(s
) + 1];
4180 pnp_name
->Length
= pnp_name
->MaximumLength
= 0;
4181 pnp_name
->Buffer
= 0;
4183 Status
= STATUS_NOT_FOUND
;
4192 NTSTATUS
get_device_pnp_name(_In_ PDEVICE_OBJECT DeviceObject
, _Out_ PUNICODE_STRING pnp_name
, _Out_
const GUID
** guid
) {
4195 Status
= get_device_pnp_name_guid(DeviceObject
, pnp_name
, &GUID_DEVINTERFACE_VOLUME
);
4196 if (NT_SUCCESS(Status
)) {
4197 *guid
= &GUID_DEVINTERFACE_VOLUME
;
4201 Status
= get_device_pnp_name_guid(DeviceObject
, pnp_name
, &GUID_DEVINTERFACE_HIDDEN_VOLUME
);
4202 if (NT_SUCCESS(Status
)) {
4203 *guid
= &GUID_DEVINTERFACE_HIDDEN_VOLUME
;
4207 Status
= get_device_pnp_name_guid(DeviceObject
, pnp_name
, &GUID_DEVINTERFACE_DISK
);
4208 if (NT_SUCCESS(Status
)) {
4209 *guid
= &GUID_DEVINTERFACE_DISK
;
4213 return STATUS_NOT_FOUND
;
4216 _Success_(return>=0)
4217 static NTSTATUS
check_mount_device(_In_ PDEVICE_OBJECT DeviceObject
, _Out_
bool* pno_pnp
) {
4221 UNICODE_STRING pnp_name
;
4224 to_read
= DeviceObject
->SectorSize
== 0 ? sizeof(superblock
) : (ULONG
)sector_align(sizeof(superblock
), DeviceObject
->SectorSize
);
4226 sb
= ExAllocatePoolWithTag(NonPagedPool
, to_read
, ALLOC_TAG
);
4228 ERR("out of memory\n");
4229 return STATUS_INSUFFICIENT_RESOURCES
;
4232 Status
= sync_read_phys(DeviceObject
, NULL
, superblock_addrs
[0], to_read
, (PUCHAR
)sb
, true);
4233 if (!NT_SUCCESS(Status
)) {
4234 ERR("sync_read_phys returned %08lx\n", Status
);
4238 if (sb
->magic
!= BTRFS_MAGIC
) {
4239 Status
= STATUS_SUCCESS
;
4243 if (!check_superblock_checksum(sb
)) {
4244 Status
= STATUS_SUCCESS
;
4248 DeviceObject
->Flags
&= ~DO_VERIFY_VOLUME
;
4250 pnp_name
.Buffer
= NULL
;
4252 Status
= get_device_pnp_name(DeviceObject
, &pnp_name
, &guid
);
4253 if (!NT_SUCCESS(Status
)) {
4254 WARN("get_device_pnp_name returned %08lx\n", Status
);
4255 pnp_name
.Length
= 0;
4258 *pno_pnp
= pnp_name
.Length
== 0;
4260 if (pnp_name
.Buffer
)
4261 ExFreePool(pnp_name
.Buffer
);
4263 Status
= STATUS_SUCCESS
;
4271 static bool still_has_superblock(_In_ PDEVICE_OBJECT device
, _In_ PFILE_OBJECT fileobj
) {
4279 to_read
= device
->SectorSize
== 0 ? sizeof(superblock
) : (ULONG
)sector_align(sizeof(superblock
), device
->SectorSize
);
4281 sb
= ExAllocatePoolWithTag(NonPagedPool
, to_read
, ALLOC_TAG
);
4283 ERR("out of memory\n");
4287 Status
= sync_read_phys(device
, fileobj
, superblock_addrs
[0], to_read
, (PUCHAR
)sb
, true);
4288 if (!NT_SUCCESS(Status
)) {
4289 ERR("Failed to read superblock: %08lx\n", Status
);
4294 if (sb
->magic
!= BTRFS_MAGIC
) {
4295 TRACE("not a BTRFS volume\n");
4299 if (!check_superblock_checksum(sb
)) {
4305 ObReferenceObject(device
);
4308 PDEVICE_OBJECT device2
= IoGetLowerDeviceObject(device
);
4310 device
->Flags
&= ~DO_VERIFY_VOLUME
;
4312 ObDereferenceObject(device
);
4322 static NTSTATUS
mount_vol(_In_ PDEVICE_OBJECT DeviceObject
, _In_ PIRP Irp
) {
4323 PIO_STACK_LOCATION IrpSp
;
4324 PDEVICE_OBJECT NewDeviceObject
= NULL
;
4325 PDEVICE_OBJECT DeviceToMount
, readobj
;
4326 PFILE_OBJECT fileobj
;
4328 device_extension
* Vcb
= NULL
;
4329 LIST_ENTRY
*le
, batchlist
;
4332 fcb
* root_fcb
= NULL
;
4333 ccb
* root_ccb
= NULL
;
4334 bool init_lookaside
= false;
4336 volume_device_extension
* vde
= NULL
;
4337 pdo_device_extension
* pdode
= NULL
;
4339 uint64_t readobjsize
;
4340 OBJECT_ATTRIBUTES oa
;
4341 device_extension
* real_devext
;
4344 TRACE("(%p, %p)\n", DeviceObject
, Irp
);
4346 if (DeviceObject
!= master_devobj
) {
4347 Status
= STATUS_INVALID_DEVICE_REQUEST
;
4351 IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
4352 DeviceToMount
= IrpSp
->Parameters
.MountVolume
.DeviceObject
;
4354 real_devext
= IrpSp
->Parameters
.MountVolume
.Vpb
->RealDevice
->DeviceExtension
;
4356 // Make sure we're not trying to mount the PDO
4357 if (IrpSp
->Parameters
.MountVolume
.Vpb
->RealDevice
->DriverObject
== drvobj
&& real_devext
->type
== VCB_TYPE_PDO
)
4358 return STATUS_UNRECOGNIZED_VOLUME
;
4360 if (!is_btrfs_volume(DeviceToMount
)) {
4361 bool not_pnp
= false;
4363 Status
= check_mount_device(DeviceToMount
, ¬_pnp
);
4364 if (!NT_SUCCESS(Status
))
4365 WARN("check_mount_device returned %08lx\n", Status
);
4368 Status
= STATUS_UNRECOGNIZED_VOLUME
;
4374 pdo
= DeviceToMount
;
4376 ObReferenceObject(pdo
);
4379 PDEVICE_OBJECT pdo2
= IoGetLowerDeviceObject(pdo
);
4381 ObDereferenceObject(pdo
);
4389 ExAcquireResourceSharedLite(&pdo_list_lock
, true);
4391 le
= pdo_list
.Flink
;
4392 while (le
!= &pdo_list
) {
4393 pdo_device_extension
* pdode2
= CONTAINING_RECORD(le
, pdo_device_extension
, list_entry
);
4395 if (pdode2
->pdo
== pdo
) {
4403 ExReleaseResourceLite(&pdo_list_lock
);
4405 if (!vde
|| vde
->type
!= VCB_TYPE_VOLUME
) {
4407 Status
= STATUS_UNRECOGNIZED_VOLUME
;
4415 ExAcquireResourceExclusiveLite(&pdode
->child_lock
, true);
4417 le
= pdode
->children
.Flink
;
4418 while (le
!= &pdode
->children
) {
4419 LIST_ENTRY
* le2
= le
->Flink
;
4421 vc
= CONTAINING_RECORD(pdode
->children
.Flink
, volume_child
, list_entry
);
4423 if (!still_has_superblock(vc
->devobj
, vc
->fileobj
)) {
4424 remove_volume_child(vde
, vc
, false);
4426 if (pdode
->num_children
== 0) {
4427 ERR("error - number of devices is zero\n");
4428 Status
= STATUS_INTERNAL_ERROR
;
4432 Status
= STATUS_DEVICE_NOT_READY
;
4439 if (pdode
->num_children
== 0 || pdode
->children_loaded
== 0) {
4440 ERR("error - number of devices is zero\n");
4441 Status
= STATUS_INTERNAL_ERROR
;
4445 ExConvertExclusiveToSharedLite(&pdode
->child_lock
);
4447 vc
= CONTAINING_RECORD(pdode
->children
.Flink
, volume_child
, list_entry
);
4449 readobj
= vc
->devobj
;
4450 fileobj
= vc
->fileobj
;
4451 readobjsize
= vc
->size
;
4453 vde
->device
->Characteristics
&= ~FILE_DEVICE_SECURE_OPEN
;
4455 GET_LENGTH_INFORMATION gli
;
4458 readobj
= DeviceToMount
;
4461 Status
= dev_ioctl(readobj
, IOCTL_DISK_GET_LENGTH_INFO
, NULL
, 0,
4462 &gli
, sizeof(gli
), true, NULL
);
4464 if (!NT_SUCCESS(Status
)) {
4465 ERR("error reading length information: %08lx\n", Status
);
4469 readobjsize
= gli
.Length
.QuadPart
;
4472 Status
= IoCreateDevice(drvobj
, sizeof(device_extension
), NULL
, FILE_DEVICE_DISK_FILE_SYSTEM
, 0, false, &NewDeviceObject
);
4473 if (!NT_SUCCESS(Status
)) {
4474 ERR("IoCreateDevice returned %08lx\n", Status
);
4475 Status
= STATUS_UNRECOGNIZED_VOLUME
;
4479 NewDeviceObject
->Flags
|= DO_DIRECT_IO
;
4481 // Some programs seem to expect that the sector size will be 512, for
4482 // FILE_NO_INTERMEDIATE_BUFFERING and the like.
4483 NewDeviceObject
->SectorSize
= min(DeviceToMount
->SectorSize
, 512);
4485 Vcb
= (PVOID
)NewDeviceObject
->DeviceExtension
;
4486 RtlZeroMemory(Vcb
, sizeof(device_extension
));
4487 Vcb
->type
= VCB_TYPE_FS
;
4490 ExInitializeResourceLite(&Vcb
->tree_lock
);
4491 Vcb
->need_write
= false;
4493 ExInitializeResourceLite(&Vcb
->fcb_lock
);
4494 ExInitializeResourceLite(&Vcb
->fileref_lock
);
4495 ExInitializeResourceLite(&Vcb
->chunk_lock
);
4496 ExInitializeResourceLite(&Vcb
->dirty_fcbs_lock
);
4497 ExInitializeResourceLite(&Vcb
->dirty_filerefs_lock
);
4498 ExInitializeResourceLite(&Vcb
->dirty_subvols_lock
);
4499 ExInitializeResourceLite(&Vcb
->scrub
.stats_lock
);
4501 ExInitializeResourceLite(&Vcb
->load_lock
);
4502 ExAcquireResourceExclusiveLite(&Vcb
->load_lock
, true);
4504 ExAcquireResourceExclusiveLite(&Vcb
->tree_lock
, true);
4506 DeviceToMount
->Flags
|= DO_DIRECT_IO
;
4508 Status
= read_superblock(Vcb
, readobj
, fileobj
, readobjsize
);
4509 if (!NT_SUCCESS(Status
)) {
4510 if (!IoIsErrorUserInduced(Status
))
4511 Status
= STATUS_UNRECOGNIZED_VOLUME
;
4512 else if (Irp
->Tail
.Overlay
.Thread
)
4513 IoSetHardErrorOrVerifyDevice(Irp
, readobj
);
4518 if (!vde
&& Vcb
->superblock
.num_devices
> 1) {
4519 ERR("cannot mount multi-device FS with non-PNP device\n");
4520 Status
= STATUS_UNRECOGNIZED_VOLUME
;
4524 Status
= registry_load_volume_options(Vcb
);
4525 if (!NT_SUCCESS(Status
)) {
4526 ERR("registry_load_volume_options returned %08lx\n", Status
);
4530 if (pdode
&& RtlCompareMemory(&boot_uuid
, &pdode
->uuid
, sizeof(BTRFS_UUID
)) == sizeof(BTRFS_UUID
) && boot_subvol
!= 0)
4531 Vcb
->options
.subvol_id
= boot_subvol
;
4533 if (pdode
&& pdode
->children_loaded
< pdode
->num_children
&& (!Vcb
->options
.allow_degraded
|| !finished_probing
|| degraded_wait
)) {
4534 ERR("could not mount as %I64u device(s) missing\n", pdode
->num_children
- pdode
->children_loaded
);
4535 Status
= STATUS_DEVICE_NOT_READY
;
4539 if (Vcb
->options
.ignore
) {
4540 TRACE("ignoring volume\n");
4541 Status
= STATUS_UNRECOGNIZED_VOLUME
;
4545 if (Vcb
->superblock
.incompat_flags
& ~INCOMPAT_SUPPORTED
) {
4546 WARN("cannot mount because of unsupported incompat flags (%I64x)\n", Vcb
->superblock
.incompat_flags
& ~INCOMPAT_SUPPORTED
);
4547 Status
= STATUS_UNRECOGNIZED_VOLUME
;
4551 if (!(Vcb
->superblock
.incompat_flags
& BTRFS_INCOMPAT_FLAGS_METADATA_UUID
))
4552 Vcb
->superblock
.metadata_uuid
= Vcb
->superblock
.uuid
;
4554 Vcb
->readonly
= false;
4555 if (Vcb
->superblock
.compat_ro_flags
& ~COMPAT_RO_SUPPORTED
) {
4556 WARN("mounting read-only because of unsupported flags (%I64x)\n", Vcb
->superblock
.compat_ro_flags
& ~COMPAT_RO_SUPPORTED
);
4557 Vcb
->readonly
= true;
4560 if (Vcb
->options
.readonly
)
4561 Vcb
->readonly
= true;
4563 Vcb
->superblock
.generation
++;
4564 Vcb
->superblock
.incompat_flags
|= BTRFS_INCOMPAT_FLAGS_MIXED_BACKREF
;
4566 if (Vcb
->superblock
.log_tree_addr
!= 0) {
4567 FIXME("FIXME - replay transaction log (clearing for now)\n");
4568 Vcb
->superblock
.log_tree_addr
= 0;
4571 switch (Vcb
->superblock
.csum_type
) {
4572 case CSUM_TYPE_CRC32C
:
4573 Vcb
->csum_size
= sizeof(uint32_t);
4576 case CSUM_TYPE_XXHASH
:
4577 Vcb
->csum_size
= sizeof(uint64_t);
4580 case CSUM_TYPE_SHA256
:
4581 Vcb
->csum_size
= SHA256_HASH_SIZE
;
4584 case CSUM_TYPE_BLAKE2
:
4585 Vcb
->csum_size
= BLAKE2_HASH_SIZE
;
4589 ERR("unrecognized csum type %x\n", Vcb
->superblock
.csum_type
);
4593 InitializeListHead(&Vcb
->devices
);
4594 dev
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(device
), ALLOC_TAG
);
4596 ERR("out of memory\n");
4597 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4601 dev
->devobj
= readobj
;
4602 dev
->fileobj
= fileobj
;
4603 RtlCopyMemory(&dev
->devitem
, &Vcb
->superblock
.dev_item
, sizeof(DEV_ITEM
));
4605 if (dev
->devitem
.num_bytes
> readobjsize
) {
4606 WARN("device %I64x: DEV_ITEM says %I64x bytes, but Windows only reports %I64x\n", dev
->devitem
.dev_id
,
4607 dev
->devitem
.num_bytes
, readobjsize
);
4609 dev
->devitem
.num_bytes
= readobjsize
;
4612 dev
->seeding
= Vcb
->superblock
.flags
& BTRFS_SUPERBLOCK_FLAGS_SEEDING
? true : false;
4614 init_device(Vcb
, dev
, true);
4616 InsertTailList(&Vcb
->devices
, &dev
->list_entry
);
4617 Vcb
->devices_loaded
= 1;
4619 if (DeviceToMount
->Flags
& DO_SYSTEM_BOOT_PARTITION
)
4620 Vcb
->disallow_dismount
= true;
4622 TRACE("DeviceToMount = %p\n", DeviceToMount
);
4623 TRACE("IrpSp->Parameters.MountVolume.Vpb = %p\n", IrpSp
->Parameters
.MountVolume
.Vpb
);
4625 NewDeviceObject
->StackSize
= DeviceToMount
->StackSize
+ 1;
4626 NewDeviceObject
->Flags
&= ~DO_DEVICE_INITIALIZING
;
4628 InitializeListHead(&Vcb
->roots
);
4629 InitializeListHead(&Vcb
->drop_roots
);
4631 Vcb
->log_to_phys_loaded
= false;
4633 add_root(Vcb
, BTRFS_ROOT_CHUNK
, Vcb
->superblock
.chunk_tree_addr
, Vcb
->superblock
.chunk_root_generation
, NULL
);
4635 if (!Vcb
->chunk_root
) {
4636 ERR("Could not load chunk root.\n");
4637 Status
= STATUS_INTERNAL_ERROR
;
4641 InitializeListHead(&Vcb
->sys_chunks
);
4642 Status
= load_sys_chunks(Vcb
);
4643 if (!NT_SUCCESS(Status
)) {
4644 ERR("load_sys_chunks returned %08lx\n", Status
);
4648 InitializeListHead(&Vcb
->chunks
);
4649 InitializeListHead(&Vcb
->trees
);
4650 InitializeListHead(&Vcb
->trees_hash
);
4651 InitializeListHead(&Vcb
->all_fcbs
);
4652 InitializeListHead(&Vcb
->dirty_fcbs
);
4653 InitializeListHead(&Vcb
->dirty_filerefs
);
4654 InitializeListHead(&Vcb
->dirty_subvols
);
4655 InitializeListHead(&Vcb
->send_ops
);
4657 ExInitializeFastMutex(&Vcb
->trees_list_mutex
);
4659 InitializeListHead(&Vcb
->DirNotifyList
);
4660 InitializeListHead(&Vcb
->scrub
.errors
);
4662 FsRtlNotifyInitializeSync(&Vcb
->NotifySync
);
4664 ExInitializePagedLookasideList(&Vcb
->tree_data_lookaside
, NULL
, NULL
, 0, sizeof(tree_data
), ALLOC_TAG
, 0);
4665 ExInitializePagedLookasideList(&Vcb
->traverse_ptr_lookaside
, NULL
, NULL
, 0, sizeof(traverse_ptr
), ALLOC_TAG
, 0);
4666 ExInitializePagedLookasideList(&Vcb
->batch_item_lookaside
, NULL
, NULL
, 0, sizeof(batch_item
), ALLOC_TAG
, 0);
4667 ExInitializePagedLookasideList(&Vcb
->fileref_lookaside
, NULL
, NULL
, 0, sizeof(file_ref
), ALLOC_TAG
, 0);
4668 ExInitializePagedLookasideList(&Vcb
->fcb_lookaside
, NULL
, NULL
, 0, sizeof(fcb
), ALLOC_TAG
, 0);
4669 ExInitializePagedLookasideList(&Vcb
->name_bit_lookaside
, NULL
, NULL
, 0, sizeof(name_bit
), ALLOC_TAG
, 0);
4670 ExInitializeNPagedLookasideList(&Vcb
->range_lock_lookaside
, NULL
, NULL
, 0, sizeof(range_lock
), ALLOC_TAG
, 0);
4671 ExInitializeNPagedLookasideList(&Vcb
->fileref_np_lookaside
, NULL
, NULL
, 0, sizeof(file_ref_nonpaged
), ALLOC_TAG
, 0);
4672 ExInitializeNPagedLookasideList(&Vcb
->fcb_np_lookaside
, NULL
, NULL
, 0, sizeof(fcb_nonpaged
), ALLOC_TAG
, 0);
4673 init_lookaside
= true;
4675 Vcb
->Vpb
= IrpSp
->Parameters
.MountVolume
.Vpb
;
4677 Status
= load_chunk_root(Vcb
, Irp
);
4678 if (!NT_SUCCESS(Status
)) {
4679 ERR("load_chunk_root returned %08lx\n", Status
);
4683 if (Vcb
->superblock
.num_devices
> 1) {
4684 if (Vcb
->devices_loaded
< Vcb
->superblock
.num_devices
&& (!Vcb
->options
.allow_degraded
|| !finished_probing
)) {
4685 ERR("could not mount as %I64u device(s) missing\n", Vcb
->superblock
.num_devices
- Vcb
->devices_loaded
);
4687 IoRaiseInformationalHardError(IO_ERR_INTERNAL_ERROR
, NULL
, NULL
);
4689 Status
= STATUS_INTERNAL_ERROR
;
4693 if (dev
->readonly
&& !Vcb
->readonly
) {
4694 Vcb
->readonly
= true;
4696 le
= Vcb
->devices
.Flink
;
4697 while (le
!= &Vcb
->devices
) {
4698 device
* dev2
= CONTAINING_RECORD(le
, device
, list_entry
);
4700 if (dev2
->readonly
&& !dev2
->seeding
)
4703 if (!dev2
->readonly
) {
4704 Vcb
->readonly
= false;
4712 WARN("setting volume to readonly\n");
4715 if (dev
->readonly
) {
4716 WARN("setting volume to readonly as device is readonly\n");
4717 Vcb
->readonly
= true;
4721 add_root(Vcb
, BTRFS_ROOT_ROOT
, Vcb
->superblock
.root_tree_addr
, Vcb
->superblock
.generation
- 1, NULL
);
4723 if (!Vcb
->root_root
) {
4724 ERR("Could not load root of roots.\n");
4725 Status
= STATUS_INTERNAL_ERROR
;
4729 Status
= look_for_roots(Vcb
, Irp
);
4730 if (!NT_SUCCESS(Status
)) {
4731 ERR("look_for_roots returned %08lx\n", Status
);
4735 if (!Vcb
->readonly
) {
4736 Status
= find_chunk_usage(Vcb
, Irp
);
4737 if (!NT_SUCCESS(Status
)) {
4738 ERR("find_chunk_usage returned %08lx\n", Status
);
4743 InitializeListHead(&batchlist
);
4745 // We've already increased the generation by one
4746 if (!Vcb
->readonly
&& (
4747 Vcb
->options
.clear_cache
||
4748 (!(Vcb
->superblock
.compat_ro_flags
& BTRFS_COMPAT_RO_FLAGS_FREE_SPACE_CACHE
) && Vcb
->superblock
.generation
- 1 != Vcb
->superblock
.cache_generation
) ||
4749 (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
)))) {
4750 if (Vcb
->options
.clear_cache
)
4751 WARN("ClearCache option was set, clearing cache...\n");
4752 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
))
4753 WARN("clearing free-space tree created by buggy Linux driver\n");
4755 WARN("generation was %I64x, free-space cache generation was %I64x; clearing cache...\n", Vcb
->superblock
.generation
- 1, Vcb
->superblock
.cache_generation
);
4757 Status
= clear_free_space_cache(Vcb
, &batchlist
, Irp
);
4758 if (!NT_SUCCESS(Status
)) {
4759 ERR("clear_free_space_cache returned %08lx\n", Status
);
4760 clear_batch_list(Vcb
, &batchlist
);
4765 Status
= commit_batch_list(Vcb
, &batchlist
, Irp
);
4766 if (!NT_SUCCESS(Status
)) {
4767 ERR("commit_batch_list returned %08lx\n", Status
);
4771 Vcb
->volume_fcb
= create_fcb(Vcb
, NonPagedPool
);
4772 if (!Vcb
->volume_fcb
) {
4773 ERR("out of memory\n");
4774 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4778 Vcb
->volume_fcb
->Vcb
= Vcb
;
4779 Vcb
->volume_fcb
->sd
= NULL
;
4781 Vcb
->dummy_fcb
= create_fcb(Vcb
, NonPagedPool
);
4782 if (!Vcb
->dummy_fcb
) {
4783 ERR("out of memory\n");
4784 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4788 Vcb
->dummy_fcb
->Vcb
= Vcb
;
4789 Vcb
->dummy_fcb
->type
= BTRFS_TYPE_DIRECTORY
;
4790 Vcb
->dummy_fcb
->inode
= 2;
4791 Vcb
->dummy_fcb
->subvol
= Vcb
->root_root
;
4792 Vcb
->dummy_fcb
->atts
= FILE_ATTRIBUTE_DIRECTORY
;
4793 Vcb
->dummy_fcb
->inode_item
.st_nlink
= 1;
4794 Vcb
->dummy_fcb
->inode_item
.st_mode
= __S_IFDIR
;
4796 Vcb
->dummy_fcb
->hash_ptrs
= ExAllocatePoolWithTag(PagedPool
, sizeof(LIST_ENTRY
*) * 256, ALLOC_TAG
);
4797 if (!Vcb
->dummy_fcb
->hash_ptrs
) {
4798 ERR("out of memory\n");
4799 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4803 RtlZeroMemory(Vcb
->dummy_fcb
->hash_ptrs
, sizeof(LIST_ENTRY
*) * 256);
4805 Vcb
->dummy_fcb
->hash_ptrs_uc
= ExAllocatePoolWithTag(PagedPool
, sizeof(LIST_ENTRY
*) * 256, ALLOC_TAG
);
4806 if (!Vcb
->dummy_fcb
->hash_ptrs_uc
) {
4807 ERR("out of memory\n");
4808 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4812 RtlZeroMemory(Vcb
->dummy_fcb
->hash_ptrs_uc
, sizeof(LIST_ENTRY
*) * 256);
4814 root_fcb
= create_fcb(Vcb
, NonPagedPool
);
4816 ERR("out of memory\n");
4817 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4821 root_fcb
->Vcb
= Vcb
;
4822 root_fcb
->inode
= SUBVOL_ROOT_INODE
;
4823 root_fcb
->hash
= calc_crc32c(0xffffffff, (uint8_t*)&root_fcb
->inode
, sizeof(uint64_t));
4824 root_fcb
->type
= BTRFS_TYPE_DIRECTORY
;
4826 #ifdef DEBUG_FCB_REFCOUNTS
4827 WARN("volume FCB = %p\n", Vcb
->volume_fcb
);
4828 WARN("root FCB = %p\n", root_fcb
);
4831 root_fcb
->subvol
= find_default_subvol(Vcb
, Irp
);
4833 if (!root_fcb
->subvol
) {
4834 ERR("could not find top subvol\n");
4835 Status
= STATUS_INTERNAL_ERROR
;
4839 Status
= load_dir_children(Vcb
, root_fcb
, true, Irp
);
4840 if (!NT_SUCCESS(Status
)) {
4841 ERR("load_dir_children returned %08lx\n", Status
);
4845 searchkey
.obj_id
= root_fcb
->inode
;
4846 searchkey
.obj_type
= TYPE_INODE_ITEM
;
4847 searchkey
.offset
= 0xffffffffffffffff;
4849 Status
= find_item(Vcb
, root_fcb
->subvol
, &tp
, &searchkey
, false, Irp
);
4850 if (!NT_SUCCESS(Status
)) {
4851 ERR("error - find_item returned %08lx\n", Status
);
4855 if (tp
.item
->key
.obj_id
!= searchkey
.obj_id
|| tp
.item
->key
.obj_type
!= searchkey
.obj_type
) {
4856 ERR("couldn't find INODE_ITEM for root directory\n");
4857 Status
= STATUS_INTERNAL_ERROR
;
4861 if (tp
.item
->size
> 0)
4862 RtlCopyMemory(&root_fcb
->inode_item
, tp
.item
->data
, min(sizeof(INODE_ITEM
), tp
.item
->size
));
4864 fcb_get_sd(root_fcb
, NULL
, true, Irp
);
4866 root_fcb
->atts
= get_file_attributes(Vcb
, root_fcb
->subvol
, root_fcb
->inode
, root_fcb
->type
, false, false, Irp
);
4868 if (root_fcb
->subvol
->id
== BTRFS_ROOT_FSTREE
)
4869 root_fcb
->atts
&= ~FILE_ATTRIBUTE_HIDDEN
;
4871 Vcb
->root_fileref
= create_fileref(Vcb
);
4872 if (!Vcb
->root_fileref
) {
4873 ERR("out of memory\n");
4874 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4878 Vcb
->root_fileref
->fcb
= root_fcb
;
4879 InsertTailList(&root_fcb
->subvol
->fcbs
, &root_fcb
->list_entry
);
4880 InsertTailList(&Vcb
->all_fcbs
, &root_fcb
->list_entry_all
);
4882 root_fcb
->subvol
->fcbs_ptrs
[root_fcb
->hash
>> 24] = &root_fcb
->list_entry
;
4884 root_fcb
->fileref
= Vcb
->root_fileref
;
4886 root_ccb
= ExAllocatePoolWithTag(PagedPool
, sizeof(ccb
), ALLOC_TAG
);
4888 ERR("out of memory\n");
4889 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4893 Vcb
->root_file
= IoCreateStreamFileObject(NULL
, DeviceToMount
);
4894 Vcb
->root_file
->FsContext
= root_fcb
;
4895 Vcb
->root_file
->SectionObjectPointer
= &root_fcb
->nonpaged
->segment_object
;
4896 Vcb
->root_file
->Vpb
= DeviceObject
->Vpb
;
4898 RtlZeroMemory(root_ccb
, sizeof(ccb
));
4899 root_ccb
->NodeType
= BTRFS_NODE_TYPE_CCB
;
4900 root_ccb
->NodeSize
= sizeof(ccb
);
4902 Vcb
->root_file
->FsContext2
= root_ccb
;
4905 CcInitializeCacheMap(Vcb
->root_file
, (PCC_FILE_SIZES
)(&root_fcb
->Header
.AllocationSize
), false, &cache_callbacks
, Vcb
->root_file
);
4906 } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER
) {
4907 Status
= _SEH2_GetExceptionCode();
4911 le
= Vcb
->devices
.Flink
;
4912 while (le
!= &Vcb
->devices
) {
4913 device
* dev2
= CONTAINING_RECORD(le
, device
, list_entry
);
4915 Status
= find_disk_holes(Vcb
, dev2
, Irp
);
4916 if (!NT_SUCCESS(Status
)) {
4917 ERR("find_disk_holes returned %08lx\n", Status
);
4924 IoAcquireVpbSpinLock(&irql
);
4926 NewDeviceObject
->Vpb
= IrpSp
->Parameters
.MountVolume
.Vpb
;
4927 IrpSp
->Parameters
.MountVolume
.Vpb
->DeviceObject
= NewDeviceObject
;
4928 IrpSp
->Parameters
.MountVolume
.Vpb
->Flags
|= VPB_MOUNTED
;
4929 NewDeviceObject
->Vpb
->VolumeLabelLength
= 4; // FIXME
4930 NewDeviceObject
->Vpb
->VolumeLabel
[0] = '?';
4931 NewDeviceObject
->Vpb
->VolumeLabel
[1] = 0;
4932 NewDeviceObject
->Vpb
->ReferenceCount
++;
4934 IoReleaseVpbSpinLock(irql
);
4936 KeInitializeEvent(&Vcb
->flush_thread_finished
, NotificationEvent
, false);
4938 InitializeObjectAttributes(&oa
, NULL
, OBJ_KERNEL_HANDLE
, NULL
, NULL
);
4940 Status
= PsCreateSystemThread(&Vcb
->flush_thread_handle
, 0, &oa
, NULL
, NULL
, flush_thread
, NewDeviceObject
);
4941 if (!NT_SUCCESS(Status
)) {
4942 ERR("PsCreateSystemThread returned %08lx\n", Status
);
4946 Status
= create_calc_threads(NewDeviceObject
);
4947 if (!NT_SUCCESS(Status
)) {
4948 ERR("create_calc_threads returned %08lx\n", Status
);
4952 Status
= registry_mark_volume_mounted(&Vcb
->superblock
.uuid
);
4953 if (!NT_SUCCESS(Status
))
4954 WARN("registry_mark_volume_mounted returned %08lx\n", Status
);
4956 Status
= look_for_balance_item(Vcb
);
4957 if (!NT_SUCCESS(Status
) && Status
!= STATUS_NOT_FOUND
)
4958 WARN("look_for_balance_item returned %08lx\n", Status
);
4960 Status
= STATUS_SUCCESS
;
4963 vde
->mounted_device
= NewDeviceObject
;
4965 Vcb
->devobj
= NewDeviceObject
;
4967 ExInitializeResourceLite(&Vcb
->send_load_lock
);
4971 ExReleaseResourceLite(&pdode
->child_lock
);
4975 ExReleaseResourceLite(&Vcb
->tree_lock
);
4976 ExReleaseResourceLite(&Vcb
->load_lock
);
4979 if (!NT_SUCCESS(Status
)) {
4981 if (init_lookaside
) {
4982 ExDeletePagedLookasideList(&Vcb
->tree_data_lookaside
);
4983 ExDeletePagedLookasideList(&Vcb
->traverse_ptr_lookaside
);
4984 ExDeletePagedLookasideList(&Vcb
->batch_item_lookaside
);
4985 ExDeletePagedLookasideList(&Vcb
->fileref_lookaside
);
4986 ExDeletePagedLookasideList(&Vcb
->fcb_lookaside
);
4987 ExDeletePagedLookasideList(&Vcb
->name_bit_lookaside
);
4988 ExDeleteNPagedLookasideList(&Vcb
->range_lock_lookaside
);
4989 ExDeleteNPagedLookasideList(&Vcb
->fileref_np_lookaside
);
4990 ExDeleteNPagedLookasideList(&Vcb
->fcb_np_lookaside
);
4994 ObDereferenceObject(Vcb
->root_file
);
4995 else if (Vcb
->root_fileref
)
4996 free_fileref(Vcb
->root_fileref
);
5000 if (root_fcb
&& root_fcb
->refcount
== 0)
5003 if (Vcb
->volume_fcb
)
5004 reap_fcb(Vcb
->volume_fcb
);
5006 ExDeleteResourceLite(&Vcb
->tree_lock
);
5007 ExDeleteResourceLite(&Vcb
->load_lock
);
5008 ExDeleteResourceLite(&Vcb
->fcb_lock
);
5009 ExDeleteResourceLite(&Vcb
->fileref_lock
);
5010 ExDeleteResourceLite(&Vcb
->chunk_lock
);
5011 ExDeleteResourceLite(&Vcb
->dirty_fcbs_lock
);
5012 ExDeleteResourceLite(&Vcb
->dirty_filerefs_lock
);
5013 ExDeleteResourceLite(&Vcb
->dirty_subvols_lock
);
5014 ExDeleteResourceLite(&Vcb
->scrub
.stats_lock
);
5016 if (Vcb
->devices
.Flink
) {
5017 while (!IsListEmpty(&Vcb
->devices
)) {
5018 device
* dev2
= CONTAINING_RECORD(RemoveHeadList(&Vcb
->devices
), device
, list_entry
);
5025 if (NewDeviceObject
)
5026 IoDeleteDevice(NewDeviceObject
);
5028 ExAcquireResourceExclusiveLite(&global_loading_lock
, true);
5029 InsertTailList(&VcbList
, &Vcb
->list_entry
);
5030 ExReleaseResourceLite(&global_loading_lock
);
5032 FsRtlNotifyVolumeEvent(Vcb
->root_file
, FSRTL_VOLUME_MOUNT
);
5035 TRACE("mount_vol done (status: %lx)\n", Status
);
5040 static NTSTATUS
verify_device(_In_ device_extension
* Vcb
, _Inout_ device
* dev
) {
5046 return STATUS_WRONG_VOLUME
;
5048 if (dev
->removable
) {
5049 IO_STATUS_BLOCK iosb
;
5051 Status
= dev_ioctl(dev
->devobj
, IOCTL_STORAGE_CHECK_VERIFY
, NULL
, 0, &cc
, sizeof(ULONG
), true, &iosb
);
5053 if (IoIsErrorUserInduced(Status
)) {
5054 ERR("IOCTL_STORAGE_CHECK_VERIFY returned %08lx (user-induced)\n", Status
);
5057 pdo_device_extension
* pdode
= Vcb
->vde
->pdode
;
5059 bool changed
= false;
5061 ExAcquireResourceExclusiveLite(&pdode
->child_lock
, true);
5063 le2
= pdode
->children
.Flink
;
5064 while (le2
!= &pdode
->children
) {
5065 volume_child
* vc
= CONTAINING_RECORD(le2
, volume_child
, list_entry
);
5067 if (vc
->devobj
== dev
->devobj
) {
5068 TRACE("removing device\n");
5070 remove_volume_child(Vcb
->vde
, vc
, true);
5080 ExReleaseResourceLite(&pdode
->child_lock
);
5082 } else if (!NT_SUCCESS(Status
)) {
5083 ERR("IOCTL_STORAGE_CHECK_VERIFY returned %08lx\n", Status
);
5085 } else if (iosb
.Information
< sizeof(ULONG
)) {
5086 ERR("iosb.Information was too short\n");
5087 return STATUS_INTERNAL_ERROR
;
5090 dev
->change_count
= cc
;
5093 to_read
= dev
->devobj
->SectorSize
== 0 ? sizeof(superblock
) : (ULONG
)sector_align(sizeof(superblock
), dev
->devobj
->SectorSize
);
5095 sb
= ExAllocatePoolWithTag(NonPagedPool
, to_read
, ALLOC_TAG
);
5097 ERR("out of memory\n");
5098 return STATUS_INSUFFICIENT_RESOURCES
;
5101 Status
= sync_read_phys(dev
->devobj
, dev
->fileobj
, superblock_addrs
[0], to_read
, (PUCHAR
)sb
, true);
5102 if (!NT_SUCCESS(Status
)) {
5103 ERR("Failed to read superblock: %08lx\n", Status
);
5108 if (sb
->magic
!= BTRFS_MAGIC
) {
5109 ERR("not a BTRFS volume\n");
5111 return STATUS_WRONG_VOLUME
;
5114 if (!check_superblock_checksum(sb
)) {
5116 return STATUS_WRONG_VOLUME
;
5119 if (RtlCompareMemory(&sb
->uuid
, &Vcb
->superblock
.uuid
, sizeof(BTRFS_UUID
)) != sizeof(BTRFS_UUID
)) {
5120 ERR("different UUIDs\n");
5122 return STATUS_WRONG_VOLUME
;
5127 dev
->devobj
->Flags
&= ~DO_VERIFY_VOLUME
;
5129 return STATUS_SUCCESS
;
5132 static NTSTATUS
verify_volume(_In_ PDEVICE_OBJECT devobj
) {
5133 device_extension
* Vcb
= devobj
->DeviceExtension
;
5136 uint64_t failed_devices
= 0;
5137 bool locked
= false, remove
= false;
5139 if (!(Vcb
->Vpb
->Flags
& VPB_MOUNTED
))
5140 return STATUS_WRONG_VOLUME
;
5142 if (!ExIsResourceAcquiredExclusive(&Vcb
->tree_lock
)) {
5143 ExAcquireResourceExclusiveLite(&Vcb
->tree_lock
, true);
5147 if (Vcb
->removing
) {
5148 if (locked
) ExReleaseResourceLite(&Vcb
->tree_lock
);
5149 return STATUS_WRONG_VOLUME
;
5152 InterlockedIncrement(&Vcb
->open_files
); // so pnp_surprise_removal doesn't uninit the device while we're still using it
5154 le
= Vcb
->devices
.Flink
;
5155 while (le
!= &Vcb
->devices
) {
5156 device
* dev
= CONTAINING_RECORD(le
, device
, list_entry
);
5158 Status
= verify_device(Vcb
, dev
);
5159 if (!NT_SUCCESS(Status
)) {
5162 if (dev
->devobj
&& Vcb
->options
.allow_degraded
)
5169 InterlockedDecrement(&Vcb
->open_files
);
5171 if (Vcb
->removing
&& Vcb
->open_files
== 0)
5175 ExReleaseResourceLite(&Vcb
->tree_lock
);
5182 if (failed_devices
== 0 || (Vcb
->options
.allow_degraded
&& failed_devices
< Vcb
->superblock
.num_devices
)) {
5183 Vcb
->Vpb
->RealDevice
->Flags
&= ~DO_VERIFY_VOLUME
;
5185 return STATUS_SUCCESS
;
5191 _Dispatch_type_(IRP_MJ_FILE_SYSTEM_CONTROL
)
5192 _Function_class_(DRIVER_DISPATCH
)
5193 static NTSTATUS __stdcall
drv_file_system_control(_In_ PDEVICE_OBJECT DeviceObject
, _In_ PIRP Irp
) {
5194 PIO_STACK_LOCATION IrpSp
;
5196 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
5199 FsRtlEnterFileSystem();
5201 TRACE("file system control\n");
5203 top_level
= is_top_level(Irp
);
5205 if (Vcb
&& Vcb
->type
== VCB_TYPE_VOLUME
) {
5206 Status
= vol_file_system_control(DeviceObject
, Irp
);
5208 } else if (!Vcb
|| (Vcb
->type
!= VCB_TYPE_FS
&& Vcb
->type
!= VCB_TYPE_CONTROL
)) {
5209 Status
= STATUS_INVALID_PARAMETER
;
5213 Status
= STATUS_NOT_IMPLEMENTED
;
5215 IrpSp
= IoGetCurrentIrpStackLocation( Irp
);
5217 Irp
->IoStatus
.Information
= 0;
5219 switch (IrpSp
->MinorFunction
) {
5220 case IRP_MN_MOUNT_VOLUME
:
5221 TRACE("IRP_MN_MOUNT_VOLUME\n");
5223 Status
= mount_vol(DeviceObject
, Irp
);
5226 case IRP_MN_KERNEL_CALL
:
5227 TRACE("IRP_MN_KERNEL_CALL\n");
5229 Status
= fsctl_request(DeviceObject
, &Irp
, IrpSp
->Parameters
.FileSystemControl
.FsControlCode
);
5232 case IRP_MN_USER_FS_REQUEST
:
5233 TRACE("IRP_MN_USER_FS_REQUEST\n");
5235 Status
= fsctl_request(DeviceObject
, &Irp
, IrpSp
->Parameters
.FileSystemControl
.FsControlCode
);
5238 case IRP_MN_VERIFY_VOLUME
:
5239 TRACE("IRP_MN_VERIFY_VOLUME\n");
5241 Status
= verify_volume(DeviceObject
);
5243 if (!NT_SUCCESS(Status
) && Vcb
->Vpb
->Flags
& VPB_MOUNTED
) {
5244 ExAcquireResourceExclusiveLite(&Vcb
->tree_lock
, true);
5245 Vcb
->removing
= true;
5246 ExReleaseResourceLite(&Vcb
->tree_lock
);
5256 TRACE("returning %08lx\n", Status
);
5259 Irp
->IoStatus
.Status
= Status
;
5261 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
5265 IoSetTopLevelIrp(NULL
);
5267 FsRtlExitFileSystem();
5272 _Dispatch_type_(IRP_MJ_LOCK_CONTROL
)
5273 _Function_class_(DRIVER_DISPATCH
)
5274 static NTSTATUS __stdcall
drv_lock_control(_In_ PDEVICE_OBJECT DeviceObject
, _In_ PIRP Irp
) {
5276 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
5277 fcb
* fcb
= IrpSp
->FileObject
? IrpSp
->FileObject
->FsContext
: NULL
;
5278 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
5281 FsRtlEnterFileSystem();
5283 top_level
= is_top_level(Irp
);
5285 if (Vcb
&& Vcb
->type
== VCB_TYPE_VOLUME
) {
5286 Status
= vol_lock_control(DeviceObject
, Irp
);
5288 Irp
->IoStatus
.Status
= Status
;
5289 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
5294 TRACE("lock control\n");
5297 ERR("fcb was NULL\n");
5298 Status
= STATUS_INVALID_PARAMETER
;
5302 FsRtlCheckOplock(fcb_oplock(fcb
), Irp
, NULL
, NULL
, NULL
);
5304 Status
= FsRtlProcessFileLock(&fcb
->lock
, Irp
, NULL
);
5306 fcb
->Header
.IsFastIoPossible
= fast_io_possible(fcb
);
5309 TRACE("returning %08lx\n", Status
);
5312 IoSetTopLevelIrp(NULL
);
5314 FsRtlExitFileSystem();
5319 void do_shutdown(PIRP Irp
) {
5321 bus_device_extension
* bde
;
5323 shutting_down
= true;
5324 KeSetEvent(&mountmgr_thread_event
, 0, false);
5327 while (le
!= &VcbList
) {
5328 LIST_ENTRY
* le2
= le
->Flink
;
5330 device_extension
* Vcb
= CONTAINING_RECORD(le
, device_extension
, list_entry
);
5331 volume_device_extension
* vde
= Vcb
->vde
;
5332 PDEVICE_OBJECT devobj
= vde
? vde
->device
: NULL
;
5334 TRACE("shutting down Vcb %p\n", Vcb
);
5337 InterlockedIncrement(&vde
->open_count
);
5340 ObReferenceObject(devobj
);
5342 dismount_volume(Vcb
, true, Irp
);
5346 UNICODE_STRING mmdevpath
;
5347 PDEVICE_OBJECT mountmgr
;
5348 PFILE_OBJECT mountmgrfo
;
5352 RtlInitUnicodeString(&mmdevpath
, MOUNTMGR_DEVICE_NAME
);
5353 Status
= IoGetDeviceObjectPointer(&mmdevpath
, FILE_READ_ATTRIBUTES
, &mountmgrfo
, &mountmgr
);
5354 if (!NT_SUCCESS(Status
))
5355 ERR("IoGetDeviceObjectPointer returned %08lx\n", Status
);
5357 remove_drive_letter(mountmgr
, &vde
->name
);
5359 ObDereferenceObject(mountmgrfo
);
5362 vde
->removing
= true;
5364 newvpb
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(VPB
), ALLOC_TAG
);
5366 ERR("out of memory\n");
5370 RtlZeroMemory(newvpb
, sizeof(VPB
));
5372 newvpb
->Type
= IO_TYPE_VPB
;
5373 newvpb
->Size
= sizeof(VPB
);
5374 newvpb
->RealDevice
= newvpb
->DeviceObject
= vde
->device
;
5375 newvpb
->Flags
= VPB_DIRECT_WRITES_ALLOWED
;
5377 IoAcquireVpbSpinLock(&irql
);
5378 vde
->device
->Vpb
= newvpb
;
5379 IoReleaseVpbSpinLock(irql
);
5381 if (InterlockedDecrement(&vde
->open_count
) == 0)
5386 ObDereferenceObject(devobj
);
5393 ObDereferenceObject(comfo
);
5399 IoUnregisterFileSystem(master_devobj
);
5401 if (notification_entry2
) {
5402 if (fIoUnregisterPlugPlayNotificationEx
)
5403 fIoUnregisterPlugPlayNotificationEx(notification_entry2
);
5405 IoUnregisterPlugPlayNotification(notification_entry2
);
5407 notification_entry2
= NULL
;
5410 if (notification_entry3
) {
5411 if (fIoUnregisterPlugPlayNotificationEx
)
5412 fIoUnregisterPlugPlayNotificationEx(notification_entry3
);
5414 IoUnregisterPlugPlayNotification(notification_entry3
);
5416 notification_entry3
= NULL
;
5419 if (notification_entry
) {
5420 if (fIoUnregisterPlugPlayNotificationEx
)
5421 fIoUnregisterPlugPlayNotificationEx(notification_entry
);
5423 IoUnregisterPlugPlayNotification(notification_entry
);
5425 notification_entry
= NULL
;
5428 bde
= busobj
->DeviceExtension
;
5430 if (bde
->attached_device
)
5431 IoDetachDevice(bde
->attached_device
);
5433 IoDeleteDevice(busobj
);
5434 IoDeleteDevice(master_devobj
);
5437 _Dispatch_type_(IRP_MJ_SHUTDOWN
)
5438 _Function_class_(DRIVER_DISPATCH
)
5439 static NTSTATUS __stdcall
drv_shutdown(_In_ PDEVICE_OBJECT DeviceObject
, _In_ PIRP Irp
) {
5442 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
5444 FsRtlEnterFileSystem();
5446 TRACE("shutdown\n");
5448 top_level
= is_top_level(Irp
);
5450 if (Vcb
&& Vcb
->type
== VCB_TYPE_VOLUME
) {
5451 Status
= vol_shutdown(DeviceObject
, Irp
);
5455 Status
= STATUS_SUCCESS
;
5460 Irp
->IoStatus
.Status
= Status
;
5461 Irp
->IoStatus
.Information
= 0;
5463 IoCompleteRequest( Irp
, IO_NO_INCREMENT
);
5466 IoSetTopLevelIrp(NULL
);
5468 FsRtlExitFileSystem();
5473 static bool device_still_valid(device
* dev
, uint64_t expected_generation
) {
5475 unsigned int to_read
;
5478 to_read
= (unsigned int)(dev
->devobj
->SectorSize
== 0 ? sizeof(superblock
) : sector_align(sizeof(superblock
), dev
->devobj
->SectorSize
));
5480 sb
= ExAllocatePoolWithTag(NonPagedPool
, to_read
, ALLOC_TAG
);
5482 ERR("out of memory\n");
5486 Status
= sync_read_phys(dev
->devobj
, dev
->fileobj
, superblock_addrs
[0], to_read
, (PUCHAR
)sb
, false);
5487 if (!NT_SUCCESS(Status
)) {
5488 ERR("sync_read_phys returned %08lx\n", Status
);
5493 if (sb
->magic
!= BTRFS_MAGIC
) {
5494 ERR("magic not found\n");
5499 if (!check_superblock_checksum(sb
)) {
5504 if (sb
->generation
> expected_generation
) {
5505 ERR("generation was %I64x, expected %I64x\n", sb
->generation
, expected_generation
);
5515 _Function_class_(IO_WORKITEM_ROUTINE
)
5516 static void __stdcall
check_after_wakeup(PDEVICE_OBJECT DeviceObject
, PVOID con
) {
5517 device_extension
* Vcb
= (device_extension
*)con
;
5520 UNUSED(DeviceObject
);
5522 ExAcquireResourceExclusiveLite(&Vcb
->tree_lock
, true);
5524 le
= Vcb
->devices
.Flink
;
5526 // FIXME - do reads in parallel?
5528 while (le
!= &Vcb
->devices
) {
5529 device
* dev
= CONTAINING_RECORD(le
, device
, list_entry
);
5532 if (!device_still_valid(dev
, Vcb
->superblock
.generation
- 1)) {
5533 PDEVICE_OBJECT voldev
= Vcb
->Vpb
->RealDevice
;
5537 WARN("forcing remount\n");
5539 newvpb
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(VPB
), ALLOC_TAG
);
5541 ERR("out of memory\n");
5545 RtlZeroMemory(newvpb
, sizeof(VPB
));
5547 newvpb
->Type
= IO_TYPE_VPB
;
5548 newvpb
->Size
= sizeof(VPB
);
5549 newvpb
->RealDevice
= voldev
;
5550 newvpb
->Flags
= VPB_DIRECT_WRITES_ALLOWED
;
5552 Vcb
->removing
= true;
5554 IoAcquireVpbSpinLock(&irql
);
5555 voldev
->Vpb
= newvpb
;
5556 IoReleaseVpbSpinLock(irql
);
5560 ExReleaseResourceLite(&Vcb
->tree_lock
);
5562 if (Vcb
->open_files
== 0)
5564 else { // remove from VcbList
5565 ExAcquireResourceExclusiveLite(&global_loading_lock
, true);
5566 RemoveEntryList(&Vcb
->list_entry
);
5567 Vcb
->list_entry
.Flink
= NULL
;
5568 ExReleaseResourceLite(&global_loading_lock
);
5578 ExReleaseResourceLite(&Vcb
->tree_lock
);
5581 _Dispatch_type_(IRP_MJ_POWER
)
5582 _Function_class_(DRIVER_DISPATCH
)
5583 static NTSTATUS __stdcall
drv_power(_In_ PDEVICE_OBJECT DeviceObject
, _In_ PIRP Irp
) {
5585 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
5586 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
5589 // no need for FsRtlEnterFileSystem, as this only ever gets called in a system thread
5591 top_level
= is_top_level(Irp
);
5593 Irp
->IoStatus
.Information
= 0;
5595 if (Vcb
&& Vcb
->type
== VCB_TYPE_VOLUME
) {
5596 volume_device_extension
* vde
= DeviceObject
->DeviceExtension
;
5598 if (IrpSp
->MinorFunction
== IRP_MN_QUERY_POWER
&& IrpSp
->Parameters
.Power
.Type
== SystemPowerState
&&
5599 IrpSp
->Parameters
.Power
.State
.SystemState
!= PowerSystemWorking
&& vde
->mounted_device
) {
5600 device_extension
* Vcb2
= vde
->mounted_device
->DeviceExtension
;
5602 /* If power state is about to go to sleep or hibernate, do a flush. We do this on IRP_MJ_QUERY_POWER
5603 * rather than IRP_MJ_SET_POWER because we know that the hard disks are still awake. */
5606 ExAcquireResourceExclusiveLite(&Vcb2
->tree_lock
, true);
5608 if (Vcb2
->need_write
&& !Vcb2
->readonly
) {
5609 TRACE("doing protective flush on power state change\n");
5610 Status
= do_write(Vcb2
, NULL
);
5612 Status
= STATUS_SUCCESS
;
5616 if (!NT_SUCCESS(Status
))
5617 ERR("do_write returned %08lx\n", Status
);
5619 ExReleaseResourceLite(&Vcb2
->tree_lock
);
5621 } else if (IrpSp
->MinorFunction
== IRP_MN_SET_POWER
&& IrpSp
->Parameters
.Power
.Type
== SystemPowerState
&&
5622 IrpSp
->Parameters
.Power
.State
.SystemState
== PowerSystemWorking
&& vde
->mounted_device
) {
5623 device_extension
* Vcb2
= vde
->mounted_device
->DeviceExtension
;
5625 /* If waking up, make sure that the FS hasn't been changed while we've been out (e.g., by dual-boot Linux) */
5628 PIO_WORKITEM work_item
;
5630 work_item
= IoAllocateWorkItem(DeviceObject
);
5632 ERR("out of memory\n");
5634 IoQueueWorkItem(work_item
, check_after_wakeup
, DelayedWorkQueue
, Vcb2
);
5638 PoStartNextPowerIrp(Irp
);
5639 IoSkipCurrentIrpStackLocation(Irp
);
5640 Status
= PoCallDriver(vde
->attached_device
, Irp
);
5643 } else if (Vcb
&& Vcb
->type
== VCB_TYPE_FS
) {
5644 IoSkipCurrentIrpStackLocation(Irp
);
5646 Status
= IoCallDriver(Vcb
->Vpb
->RealDevice
, Irp
);
5649 } else if (Vcb
&& Vcb
->type
== VCB_TYPE_BUS
) {
5650 bus_device_extension
* bde
= DeviceObject
->DeviceExtension
;
5652 PoStartNextPowerIrp(Irp
);
5653 IoSkipCurrentIrpStackLocation(Irp
);
5654 Status
= PoCallDriver(bde
->attached_device
, Irp
);
5659 if (IrpSp
->MinorFunction
== IRP_MN_SET_POWER
|| IrpSp
->MinorFunction
== IRP_MN_QUERY_POWER
)
5660 Irp
->IoStatus
.Status
= STATUS_SUCCESS
;
5662 Status
= Irp
->IoStatus
.Status
;
5664 PoStartNextPowerIrp(Irp
);
5666 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
5670 IoSetTopLevelIrp(NULL
);
5675 _Dispatch_type_(IRP_MJ_SYSTEM_CONTROL
)
5676 _Function_class_(DRIVER_DISPATCH
)
5677 static NTSTATUS __stdcall
drv_system_control(_In_ PDEVICE_OBJECT DeviceObject
, _In_ PIRP Irp
) {
5679 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
5682 FsRtlEnterFileSystem();
5684 top_level
= is_top_level(Irp
);
5686 Irp
->IoStatus
.Information
= 0;
5688 if (Vcb
&& Vcb
->type
== VCB_TYPE_VOLUME
) {
5689 volume_device_extension
* vde
= DeviceObject
->DeviceExtension
;
5691 IoSkipCurrentIrpStackLocation(Irp
);
5693 Status
= IoCallDriver(vde
->attached_device
, Irp
);
5696 } else if (Vcb
&& Vcb
->type
== VCB_TYPE_FS
) {
5697 IoSkipCurrentIrpStackLocation(Irp
);
5699 Status
= IoCallDriver(Vcb
->Vpb
->RealDevice
, Irp
);
5702 } else if (Vcb
&& Vcb
->type
== VCB_TYPE_BUS
) {
5703 bus_device_extension
* bde
= DeviceObject
->DeviceExtension
;
5705 IoSkipCurrentIrpStackLocation(Irp
);
5707 Status
= IoCallDriver(bde
->attached_device
, Irp
);
5712 Status
= Irp
->IoStatus
.Status
;
5713 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
5717 IoSetTopLevelIrp(NULL
);
5719 FsRtlExitFileSystem();
5724 bool is_file_name_valid(_In_ PUNICODE_STRING us
, _In_
bool posix
, _In_
bool stream
) {
5727 if (us
->Length
< sizeof(WCHAR
))
5730 if (us
->Length
> 255 * sizeof(WCHAR
))
5733 for (i
= 0; i
< us
->Length
/ sizeof(WCHAR
); i
++) {
5734 if (us
->Buffer
[i
] == '/' || us
->Buffer
[i
] == 0 ||
5735 (!posix
&& (us
->Buffer
[i
] == '/' || us
->Buffer
[i
] == ':')) ||
5736 (!posix
&& !stream
&& (us
->Buffer
[i
] == '<' || us
->Buffer
[i
] == '>' || us
->Buffer
[i
] == '"' ||
5737 us
->Buffer
[i
] == '|' || us
->Buffer
[i
] == '?' || us
->Buffer
[i
] == '*' || (us
->Buffer
[i
] >= 1 && us
->Buffer
[i
] <= 31))))
5741 if (us
->Buffer
[0] == '.' && (us
->Length
== sizeof(WCHAR
) || (us
->Length
== 2 * sizeof(WCHAR
) && us
->Buffer
[1] == '.')))
5747 void chunk_lock_range(_In_ device_extension
* Vcb
, _In_ chunk
* c
, _In_
uint64_t start
, _In_
uint64_t length
) {
5752 rl
= ExAllocateFromNPagedLookasideList(&Vcb
->range_lock_lookaside
);
5754 ERR("out of memory\n");
5759 rl
->length
= length
;
5760 rl
->thread
= PsGetCurrentThread();
5765 ExAcquireResourceExclusiveLite(&c
->range_locks_lock
, true);
5767 le
= c
->range_locks
.Flink
;
5768 while (le
!= &c
->range_locks
) {
5769 range_lock
* rl2
= CONTAINING_RECORD(le
, range_lock
, list_entry
);
5771 if (rl2
->start
< start
+ length
&& rl2
->start
+ rl2
->length
> start
&& rl2
->thread
!= PsGetCurrentThread()) {
5780 InsertTailList(&c
->range_locks
, &rl
->list_entry
);
5782 ExReleaseResourceLite(&c
->range_locks_lock
);
5786 KeClearEvent(&c
->range_locks_event
);
5788 ExReleaseResourceLite(&c
->range_locks_lock
);
5790 KeWaitForSingleObject(&c
->range_locks_event
, UserRequest
, KernelMode
, false, NULL
);
5794 void chunk_unlock_range(_In_ device_extension
* Vcb
, _In_ chunk
* c
, _In_
uint64_t start
, _In_
uint64_t length
) {
5797 ExAcquireResourceExclusiveLite(&c
->range_locks_lock
, true);
5799 le
= c
->range_locks
.Flink
;
5800 while (le
!= &c
->range_locks
) {
5801 range_lock
* rl
= CONTAINING_RECORD(le
, range_lock
, list_entry
);
5803 if (rl
->start
== start
&& rl
->length
== length
) {
5804 RemoveEntryList(&rl
->list_entry
);
5805 ExFreeToNPagedLookasideList(&Vcb
->range_lock_lookaside
, rl
);
5812 KeSetEvent(&c
->range_locks_event
, 0, false);
5814 ExReleaseResourceLite(&c
->range_locks_lock
);
5817 void log_device_error(_In_ device_extension
* Vcb
, _Inout_ device
* dev
, _In_
int error
) {
5818 dev
->stats
[error
]++;
5819 dev
->stats_changed
= true;
5820 Vcb
->stats_changed
= true;
5824 _Function_class_(KSTART_ROUTINE
)
5825 static void __stdcall
serial_thread(void* context
) {
5826 LARGE_INTEGER due_time
;
5831 KeInitializeTimer(&timer
);
5833 due_time
.QuadPart
= (uint64_t)-10000000;
5835 KeSetTimer(&timer
, due_time
, NULL
);
5838 KeWaitForSingleObject(&timer
, Executive
, KernelMode
, false, NULL
);
5845 KeSetTimer(&timer
, due_time
, NULL
);
5848 KeCancelTimer(&timer
);
5850 PsTerminateSystemThread(STATUS_SUCCESS
);
5852 serial_thread_handle
= NULL
;
5855 static void init_serial(bool first_time
) {
5858 Status
= IoGetDeviceObjectPointer(&log_device
, FILE_WRITE_DATA
, &comfo
, &comdo
);
5859 if (!NT_SUCCESS(Status
)) {
5860 ERR("IoGetDeviceObjectPointer returned %08lx\n", Status
);
5863 OBJECT_ATTRIBUTES oa
;
5865 InitializeObjectAttributes(&oa
, NULL
, OBJ_KERNEL_HANDLE
, NULL
, NULL
);
5867 Status
= PsCreateSystemThread(&serial_thread_handle
, 0, &oa
, NULL
, NULL
, serial_thread
, NULL
);
5868 if (!NT_SUCCESS(Status
)) {
5869 ERR("PsCreateSystemThread returned %08lx\n", Status
);
5877 #if !defined(__REACTOS__) && (defined(_X86_) || defined(_AMD64_))
5878 static void check_cpu() {
5879 unsigned int cpuInfo
[4];
5883 __get_cpuid(1, &cpuInfo
[0], &cpuInfo
[1], &cpuInfo
[2], &cpuInfo
[3]);
5884 have_sse42
= cpuInfo
[2] & bit_SSE4_2
;
5885 have_sse2
= cpuInfo
[3] & bit_SSE2
;
5887 __cpuid(cpuInfo
, 1);
5888 have_sse42
= cpuInfo
[2] & (1 << 20);
5889 have_sse2
= cpuInfo
[3] & (1 << 26);
5893 TRACE("SSE4.2 is supported\n");
5894 calc_crc32c
= calc_crc32c_hw
;
5896 TRACE("SSE4.2 not supported\n");
5899 TRACE("SSE2 is supported\n");
5901 TRACE("SSE2 is not supported\n");
5906 static void init_logging() {
5907 ExAcquireResourceExclusiveLite(&log_lock
, true);
5909 if (log_device
.Length
> 0)
5911 else if (log_file
.Length
> 0) {
5913 OBJECT_ATTRIBUTES oa
;
5914 IO_STATUS_BLOCK iosb
;
5919 InitializeObjectAttributes(&oa
, &log_file
, OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
, NULL
, NULL
);
5921 Status
= ZwCreateFile(&log_handle
, FILE_WRITE_DATA
, &oa
, &iosb
, NULL
, FILE_ATTRIBUTE_NORMAL
, FILE_SHARE_READ
,
5922 FILE_OPEN_IF
, FILE_NON_DIRECTORY_FILE
| FILE_WRITE_THROUGH
| FILE_SYNCHRONOUS_IO_ALERT
, NULL
, 0);
5924 if (!NT_SUCCESS(Status
)) {
5925 ERR("ZwCreateFile returned %08lx\n", Status
);
5929 if (iosb
.Information
== FILE_OPENED
) { // already exists
5930 FILE_STANDARD_INFORMATION fsi
;
5931 FILE_POSITION_INFORMATION fpi
;
5933 static const char delim
[] = "\n---\n";
5935 // move to end of file
5937 Status
= ZwQueryInformationFile(log_handle
, &iosb
, &fsi
, sizeof(FILE_STANDARD_INFORMATION
), FileStandardInformation
);
5939 if (!NT_SUCCESS(Status
)) {
5940 ERR("ZwQueryInformationFile returned %08lx\n", Status
);
5944 fpi
.CurrentByteOffset
= fsi
.EndOfFile
;
5946 Status
= ZwSetInformationFile(log_handle
, &iosb
, &fpi
, sizeof(FILE_POSITION_INFORMATION
), FilePositionInformation
);
5948 if (!NT_SUCCESS(Status
)) {
5949 ERR("ZwSetInformationFile returned %08lx\n", Status
);
5953 Status
= ZwWriteFile(log_handle
, NULL
, NULL
, NULL
, &iosb
, (void*)delim
, sizeof(delim
) - 1, NULL
, NULL
);
5955 if (!NT_SUCCESS(Status
)) {
5956 ERR("ZwWriteFile returned %08lx\n", Status
);
5961 dateline
= ExAllocatePoolWithTag(PagedPool
, 256, ALLOC_TAG
);
5964 ERR("out of memory\n");
5968 KeQuerySystemTime(&time
);
5970 RtlTimeToTimeFields(&time
, &tf
);
5972 sprintf(dateline
, "Starting logging at %04i-%02i-%02i %02i:%02i:%02i\n", tf
.Year
, tf
.Month
, tf
.Day
, tf
.Hour
, tf
.Minute
, tf
.Second
);
5974 Status
= ZwWriteFile(log_handle
, NULL
, NULL
, NULL
, &iosb
, dateline
, (ULONG
)strlen(dateline
), NULL
, NULL
);
5976 ExFreePool(dateline
);
5978 if (!NT_SUCCESS(Status
)) {
5979 ERR("ZwWriteFile returned %08lx\n", Status
);
5985 ExReleaseResourceLite(&log_lock
);
5989 _Function_class_(KSTART_ROUTINE
)
5990 static void __stdcall
degraded_wait_thread(_In_
void* context
) {
5992 LARGE_INTEGER delay
;
5996 KeInitializeTimer(&timer
);
5998 delay
.QuadPart
= -30000000; // wait three seconds
5999 KeSetTimer(&timer
, delay
, NULL
);
6000 KeWaitForSingleObject(&timer
, Executive
, KernelMode
, false, NULL
);
6002 TRACE("timer expired\n");
6004 degraded_wait
= false;
6006 ZwClose(degraded_wait_handle
);
6007 degraded_wait_handle
= NULL
;
6009 PsTerminateSystemThread(STATUS_SUCCESS
);
6012 _Function_class_(DRIVER_ADD_DEVICE
)
6013 NTSTATUS __stdcall
AddDevice(PDRIVER_OBJECT DriverObject
, PDEVICE_OBJECT PhysicalDeviceObject
) {
6016 UNICODE_STRING volname
;
6019 pdo_device_extension
* pdode
= NULL
;
6020 PDEVICE_OBJECT voldev
;
6021 volume_device_extension
* vde
;
6022 UNICODE_STRING arc_name_us
;
6025 static const WCHAR arc_name_prefix
[] = L
"\\ArcName\\btrfs(";
6027 WCHAR arc_name
[(sizeof(arc_name_prefix
) / sizeof(WCHAR
)) - 1 + 37];
6029 TRACE("(%p, %p)\n", DriverObject
, PhysicalDeviceObject
);
6031 ExAcquireResourceSharedLite(&pdo_list_lock
, true);
6033 le
= pdo_list
.Flink
;
6034 while (le
!= &pdo_list
) {
6035 pdo_device_extension
* pdode2
= CONTAINING_RECORD(le
, pdo_device_extension
, list_entry
);
6037 if (pdode2
->pdo
== PhysicalDeviceObject
) {
6046 WARN("unrecognized PDO %p\n", PhysicalDeviceObject
);
6047 Status
= STATUS_NOT_SUPPORTED
;
6051 ExAcquireResourceExclusiveLite(&pdode
->child_lock
, true);
6053 if (pdode
->vde
) { // if already done, return success
6054 Status
= STATUS_SUCCESS
;
6058 volname
.Length
= volname
.MaximumLength
= (sizeof(BTRFS_VOLUME_PREFIX
) - sizeof(WCHAR
)) + ((36 + 1) * sizeof(WCHAR
));
6059 volname
.Buffer
= ExAllocatePoolWithTag(PagedPool
, volname
.MaximumLength
, ALLOC_TAG
); // FIXME - when do we free this?
6061 if (!volname
.Buffer
) {
6062 ERR("out of memory\n");
6063 Status
= STATUS_INSUFFICIENT_RESOURCES
;
6067 RtlCopyMemory(volname
.Buffer
, BTRFS_VOLUME_PREFIX
, sizeof(BTRFS_VOLUME_PREFIX
) - sizeof(WCHAR
));
6068 RtlCopyMemory(arc_name
, arc_name_prefix
, sizeof(arc_name_prefix
) - sizeof(WCHAR
));
6070 anp
= &arc_name
[(sizeof(arc_name_prefix
) / sizeof(WCHAR
)) - 1];
6071 s
= &volname
.Buffer
[(sizeof(BTRFS_VOLUME_PREFIX
) / sizeof(WCHAR
)) - 1];
6073 for (i
= 0; i
< 16; i
++) {
6074 *s
= *anp
= hex_digit(pdode
->uuid
.uuid
[i
] >> 4);
6078 *s
= *anp
= hex_digit(pdode
->uuid
.uuid
[i
] & 0xf);
6082 if (i
== 3 || i
== 5 || i
== 7 || i
== 9) {
6092 Status
= IoCreateDevice(drvobj
, sizeof(volume_device_extension
), &volname
, FILE_DEVICE_DISK
,
6093 WdmlibRtlIsNtDdiVersionAvailable(NTDDI_WIN8
) ? FILE_DEVICE_ALLOW_APPCONTAINER_TRAVERSAL
: 0, false, &voldev
);
6094 if (!NT_SUCCESS(Status
)) {
6095 ERR("IoCreateDevice returned %08lx\n", Status
);
6099 arc_name_us
.Buffer
= arc_name
;
6100 arc_name_us
.Length
= arc_name_us
.MaximumLength
= sizeof(arc_name
);
6102 Status
= IoCreateSymbolicLink(&arc_name_us
, &volname
);
6103 if (!NT_SUCCESS(Status
))
6104 WARN("IoCreateSymbolicLink returned %08lx\n", Status
);
6106 voldev
->SectorSize
= PhysicalDeviceObject
->SectorSize
;
6107 voldev
->Flags
|= DO_DIRECT_IO
;
6109 vde
= voldev
->DeviceExtension
;
6110 vde
->type
= VCB_TYPE_VOLUME
;
6111 vde
->name
= volname
;
6112 vde
->device
= voldev
;
6113 vde
->mounted_device
= NULL
;
6114 vde
->pdo
= PhysicalDeviceObject
;
6116 vde
->removing
= false;
6118 vde
->open_count
= 0;
6120 Status
= IoRegisterDeviceInterface(PhysicalDeviceObject
, &GUID_DEVINTERFACE_VOLUME
, NULL
, &vde
->bus_name
);
6121 if (!NT_SUCCESS(Status
))
6122 WARN("IoRegisterDeviceInterface returned %08lx\n", Status
);
6124 vde
->attached_device
= IoAttachDeviceToDeviceStack(voldev
, PhysicalDeviceObject
);
6128 if (pdode
->removable
)
6129 voldev
->Characteristics
|= FILE_REMOVABLE_MEDIA
;
6131 if (RtlCompareMemory(&boot_uuid
, &pdode
->uuid
, sizeof(BTRFS_UUID
)) == sizeof(BTRFS_UUID
)) {
6132 voldev
->Flags
|= DO_SYSTEM_BOOT_PARTITION
;
6133 PhysicalDeviceObject
->Flags
|= DO_SYSTEM_BOOT_PARTITION
;
6136 voldev
->Flags
&= ~DO_DEVICE_INITIALIZING
;
6138 Status
= IoSetDeviceInterfaceState(&vde
->bus_name
, true);
6139 if (!NT_SUCCESS(Status
))
6140 WARN("IoSetDeviceInterfaceState returned %08lx\n", Status
);
6142 Status
= STATUS_SUCCESS
;
6145 ExReleaseResourceLite(&pdode
->child_lock
);
6148 ExReleaseResourceLite(&pdo_list_lock
);
6153 _Function_class_(DRIVER_INITIALIZE
)
6154 NTSTATUS __stdcall
DriverEntry(_In_ PDRIVER_OBJECT DriverObject
, _In_ PUNICODE_STRING RegistryPath
) {
6156 PDEVICE_OBJECT DeviceObject
;
6157 UNICODE_STRING device_nameW
;
6158 UNICODE_STRING dosdevice_nameW
;
6159 control_device_extension
* cde
;
6160 bus_device_extension
* bde
;
6162 OBJECT_ATTRIBUTES oa
, system_thread_attributes
;
6165 InitializeListHead(&uid_map_list
);
6166 InitializeListHead(&gid_map_list
);
6169 ExInitializeResourceLite(&log_lock
);
6171 ExInitializeResourceLite(&mapping_lock
);
6173 log_device
.Buffer
= NULL
;
6174 log_device
.Length
= log_device
.MaximumLength
= 0;
6175 log_file
.Buffer
= NULL
;
6176 log_file
.Length
= log_file
.MaximumLength
= 0;
6178 registry_path
.Length
= registry_path
.MaximumLength
= RegistryPath
->Length
;
6179 registry_path
.Buffer
= ExAllocatePoolWithTag(PagedPool
, registry_path
.Length
, ALLOC_TAG
);
6181 if (!registry_path
.Buffer
) {
6182 ERR("out of memory\n");
6183 return STATUS_INSUFFICIENT_RESOURCES
;
6186 RtlCopyMemory(registry_path
.Buffer
, RegistryPath
->Buffer
, registry_path
.Length
);
6188 read_registry(®istry_path
, false);
6191 if (debug_log_level
> 0)
6197 TRACE("DriverEntry\n");
6199 #if !defined(__REACTOS__) && (defined(_X86_) || defined(_AMD64_))
6203 if (WdmlibRtlIsNtDdiVersionAvailable(NTDDI_WIN8
)) {
6204 UNICODE_STRING name
;
6205 tPsIsDiskCountersEnabled fPsIsDiskCountersEnabled
;
6207 RtlInitUnicodeString(&name
, L
"PsIsDiskCountersEnabled");
6208 fPsIsDiskCountersEnabled
= (tPsIsDiskCountersEnabled
)MmGetSystemRoutineAddress(&name
);
6210 if (fPsIsDiskCountersEnabled
) {
6211 diskacc
= fPsIsDiskCountersEnabled();
6213 RtlInitUnicodeString(&name
, L
"PsUpdateDiskCounters");
6214 fPsUpdateDiskCounters
= (tPsUpdateDiskCounters
)MmGetSystemRoutineAddress(&name
);
6216 if (!fPsUpdateDiskCounters
)
6219 RtlInitUnicodeString(&name
, L
"FsRtlUpdateDiskCounters");
6220 fFsRtlUpdateDiskCounters
= (tFsRtlUpdateDiskCounters
)MmGetSystemRoutineAddress(&name
);
6223 RtlInitUnicodeString(&name
, L
"CcCopyReadEx");
6224 fCcCopyReadEx
= (tCcCopyReadEx
)MmGetSystemRoutineAddress(&name
);
6226 RtlInitUnicodeString(&name
, L
"CcCopyWriteEx");
6227 fCcCopyWriteEx
= (tCcCopyWriteEx
)MmGetSystemRoutineAddress(&name
);
6229 RtlInitUnicodeString(&name
, L
"CcSetAdditionalCacheAttributesEx");
6230 fCcSetAdditionalCacheAttributesEx
= (tCcSetAdditionalCacheAttributesEx
)MmGetSystemRoutineAddress(&name
);
6232 RtlInitUnicodeString(&name
, L
"FsRtlCheckLockForOplockRequest");
6233 fFsRtlCheckLockForOplockRequest
= (tFsRtlCheckLockForOplockRequest
)MmGetSystemRoutineAddress(&name
);
6235 fPsUpdateDiskCounters
= NULL
;
6236 fCcCopyReadEx
= NULL
;
6237 fCcCopyWriteEx
= NULL
;
6238 fCcSetAdditionalCacheAttributesEx
= NULL
;
6239 fFsRtlUpdateDiskCounters
= NULL
;
6240 fFsRtlCheckLockForOplockRequest
= NULL
;
6243 if (WdmlibRtlIsNtDdiVersionAvailable(NTDDI_WIN7
)) {
6244 UNICODE_STRING name
;
6246 RtlInitUnicodeString(&name
, L
"IoUnregisterPlugPlayNotificationEx");
6247 fIoUnregisterPlugPlayNotificationEx
= (tIoUnregisterPlugPlayNotificationEx
)MmGetSystemRoutineAddress(&name
);
6249 RtlInitUnicodeString(&name
, L
"FsRtlAreThereCurrentOrInProgressFileLocks");
6250 fFsRtlAreThereCurrentOrInProgressFileLocks
= (tFsRtlAreThereCurrentOrInProgressFileLocks
)MmGetSystemRoutineAddress(&name
);
6252 fIoUnregisterPlugPlayNotificationEx
= NULL
;
6253 fFsRtlAreThereCurrentOrInProgressFileLocks
= NULL
;
6256 if (WdmlibRtlIsNtDdiVersionAvailable(NTDDI_VISTA
)) {
6257 UNICODE_STRING name
;
6259 RtlInitUnicodeString(&name
, L
"FsRtlGetEcpListFromIrp");
6260 fFsRtlGetEcpListFromIrp
= (tFsRtlGetEcpListFromIrp
)MmGetSystemRoutineAddress(&name
);
6262 RtlInitUnicodeString(&name
, L
"FsRtlGetNextExtraCreateParameter");
6263 fFsRtlGetNextExtraCreateParameter
= (tFsRtlGetNextExtraCreateParameter
)MmGetSystemRoutineAddress(&name
);
6265 RtlInitUnicodeString(&name
, L
"FsRtlValidateReparsePointBuffer");
6266 fFsRtlValidateReparsePointBuffer
= (tFsRtlValidateReparsePointBuffer
)MmGetSystemRoutineAddress(&name
);
6268 fFsRtlGetEcpListFromIrp
= NULL
;
6269 fFsRtlGetNextExtraCreateParameter
= NULL
;
6270 fFsRtlValidateReparsePointBuffer
= compat_FsRtlValidateReparsePointBuffer
;
6273 drvobj
= DriverObject
;
6275 DriverObject
->DriverUnload
= DriverUnload
;
6277 DriverObject
->DriverExtension
->AddDevice
= AddDevice
;
6279 DriverObject
->MajorFunction
[IRP_MJ_CREATE
] = drv_create
;
6280 DriverObject
->MajorFunction
[IRP_MJ_CLOSE
] = drv_close
;
6281 DriverObject
->MajorFunction
[IRP_MJ_READ
] = drv_read
;
6282 DriverObject
->MajorFunction
[IRP_MJ_WRITE
] = drv_write
;
6283 DriverObject
->MajorFunction
[IRP_MJ_QUERY_INFORMATION
] = drv_query_information
;
6284 DriverObject
->MajorFunction
[IRP_MJ_SET_INFORMATION
] = drv_set_information
;
6285 DriverObject
->MajorFunction
[IRP_MJ_QUERY_EA
] = drv_query_ea
;
6286 DriverObject
->MajorFunction
[IRP_MJ_SET_EA
] = drv_set_ea
;
6287 DriverObject
->MajorFunction
[IRP_MJ_FLUSH_BUFFERS
] = drv_flush_buffers
;
6288 DriverObject
->MajorFunction
[IRP_MJ_QUERY_VOLUME_INFORMATION
] = drv_query_volume_information
;
6289 DriverObject
->MajorFunction
[IRP_MJ_SET_VOLUME_INFORMATION
] = drv_set_volume_information
;
6290 DriverObject
->MajorFunction
[IRP_MJ_DIRECTORY_CONTROL
] = drv_directory_control
;
6291 DriverObject
->MajorFunction
[IRP_MJ_FILE_SYSTEM_CONTROL
] = drv_file_system_control
;
6292 DriverObject
->MajorFunction
[IRP_MJ_DEVICE_CONTROL
] = drv_device_control
;
6293 DriverObject
->MajorFunction
[IRP_MJ_SHUTDOWN
] = drv_shutdown
;
6294 DriverObject
->MajorFunction
[IRP_MJ_LOCK_CONTROL
] = drv_lock_control
;
6295 DriverObject
->MajorFunction
[IRP_MJ_CLEANUP
] = drv_cleanup
;
6296 DriverObject
->MajorFunction
[IRP_MJ_QUERY_SECURITY
] = drv_query_security
;
6297 DriverObject
->MajorFunction
[IRP_MJ_SET_SECURITY
] = drv_set_security
;
6298 DriverObject
->MajorFunction
[IRP_MJ_POWER
] = drv_power
;
6299 DriverObject
->MajorFunction
[IRP_MJ_SYSTEM_CONTROL
] = drv_system_control
;
6300 DriverObject
->MajorFunction
[IRP_MJ_PNP
] = drv_pnp
;
6302 init_fast_io_dispatch(&DriverObject
->FastIoDispatch
);
6304 device_nameW
.Buffer
= (WCHAR
*)device_name
;
6305 device_nameW
.Length
= device_nameW
.MaximumLength
= sizeof(device_name
) - sizeof(WCHAR
);
6306 dosdevice_nameW
.Buffer
= (WCHAR
*)dosdevice_name
;
6307 dosdevice_nameW
.Length
= dosdevice_nameW
.MaximumLength
= sizeof(dosdevice_name
) - sizeof(WCHAR
);
6309 Status
= IoCreateDevice(DriverObject
, sizeof(control_device_extension
), &device_nameW
, FILE_DEVICE_DISK_FILE_SYSTEM
,
6310 FILE_DEVICE_SECURE_OPEN
, false, &DeviceObject
);
6311 if (!NT_SUCCESS(Status
)) {
6312 ERR("IoCreateDevice returned %08lx\n", Status
);
6316 master_devobj
= DeviceObject
;
6317 cde
= (control_device_extension
*)master_devobj
->DeviceExtension
;
6319 RtlZeroMemory(cde
, sizeof(control_device_extension
));
6321 cde
->type
= VCB_TYPE_CONTROL
;
6323 DeviceObject
->Flags
&= ~DO_DEVICE_INITIALIZING
;
6325 Status
= IoCreateSymbolicLink(&dosdevice_nameW
, &device_nameW
);
6326 if (!NT_SUCCESS(Status
)) {
6327 ERR("IoCreateSymbolicLink returned %08lx\n", Status
);
6333 InitializeListHead(&VcbList
);
6334 ExInitializeResourceLite(&global_loading_lock
);
6335 ExInitializeResourceLite(&pdo_list_lock
);
6337 InitializeListHead(&pdo_list
);
6339 InitializeObjectAttributes(&oa
, RegistryPath
, OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
, NULL
, NULL
);
6340 Status
= ZwCreateKey(®h
, KEY_QUERY_VALUE
| KEY_ENUMERATE_SUB_KEYS
| KEY_NOTIFY
, &oa
, 0, NULL
, REG_OPTION_NON_VOLATILE
, &dispos
);
6341 if (!NT_SUCCESS(Status
)) {
6342 ERR("ZwCreateKey returned %08lx\n", Status
);
6346 watch_registry(regh
);
6348 Status
= IoCreateDevice(DriverObject
, sizeof(bus_device_extension
), NULL
, FILE_DEVICE_UNKNOWN
,
6349 FILE_DEVICE_SECURE_OPEN
, false, &busobj
);
6350 if (!NT_SUCCESS(Status
)) {
6351 ERR("IoCreateDevice returned %08lx\n", Status
);
6355 bde
= (bus_device_extension
*)busobj
->DeviceExtension
;
6357 RtlZeroMemory(bde
, sizeof(bus_device_extension
));
6359 bde
->type
= VCB_TYPE_BUS
;
6361 Status
= IoReportDetectedDevice(drvobj
, InterfaceTypeUndefined
, 0xFFFFFFFF, 0xFFFFFFFF,
6362 NULL
, NULL
, 0, &bde
->buspdo
);
6363 if (!NT_SUCCESS(Status
)) {
6364 ERR("IoReportDetectedDevice returned %08lx\n", Status
);
6368 Status
= IoRegisterDeviceInterface(bde
->buspdo
, &BtrfsBusInterface
, NULL
, &bde
->bus_name
);
6369 if (!NT_SUCCESS(Status
))
6370 WARN("IoRegisterDeviceInterface returned %08lx\n", Status
);
6372 bde
->attached_device
= IoAttachDeviceToDeviceStack(busobj
, bde
->buspdo
);
6374 busobj
->Flags
&= ~DO_DEVICE_INITIALIZING
;
6376 Status
= IoSetDeviceInterfaceState(&bde
->bus_name
, true);
6377 if (!NT_SUCCESS(Status
))
6378 WARN("IoSetDeviceInterfaceState returned %08lx\n", Status
);
6380 IoInvalidateDeviceRelations(bde
->buspdo
, BusRelations
);
6382 InitializeObjectAttributes(&system_thread_attributes
, NULL
, OBJ_KERNEL_HANDLE
, NULL
, NULL
);
6384 Status
= PsCreateSystemThread(°raded_wait_handle
, 0, &system_thread_attributes
, NULL
, NULL
, degraded_wait_thread
, NULL
);
6385 if (!NT_SUCCESS(Status
))
6386 WARN("PsCreateSystemThread returned %08lx\n", Status
);
6388 ExInitializeResourceLite(&boot_lock
);
6390 Status
= IoRegisterPlugPlayNotification(EventCategoryDeviceInterfaceChange
, PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES
,
6391 (PVOID
)&GUID_DEVINTERFACE_VOLUME
, DriverObject
, volume_notification
, DriverObject
, ¬ification_entry2
);
6392 if (!NT_SUCCESS(Status
))
6393 ERR("IoRegisterPlugPlayNotification returned %08lx\n", Status
);
6395 Status
= IoRegisterPlugPlayNotification(EventCategoryDeviceInterfaceChange
, PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES
,
6396 (PVOID
)&GUID_DEVINTERFACE_HIDDEN_VOLUME
, DriverObject
, volume_notification
, DriverObject
, ¬ification_entry3
);
6397 if (!NT_SUCCESS(Status
))
6398 ERR("IoRegisterPlugPlayNotification returned %08lx\n", Status
);
6400 Status
= IoRegisterPlugPlayNotification(EventCategoryDeviceInterfaceChange
, PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES
,
6401 (PVOID
)&GUID_DEVINTERFACE_DISK
, DriverObject
, pnp_notification
, DriverObject
, ¬ification_entry
);
6402 if (!NT_SUCCESS(Status
))
6403 ERR("IoRegisterPlugPlayNotification returned %08lx\n", Status
);
6405 finished_probing
= true;
6407 KeInitializeEvent(&mountmgr_thread_event
, NotificationEvent
, false);
6409 Status
= PsCreateSystemThread(&mountmgr_thread_handle
, 0, &system_thread_attributes
, NULL
, NULL
, mountmgr_thread
, NULL
);
6410 if (!NT_SUCCESS(Status
))
6411 WARN("PsCreateSystemThread returned %08lx\n", Status
);
6413 IoRegisterFileSystem(DeviceObject
);
6415 IoRegisterBootDriverReinitialization(DriverObject
, check_system_root
, NULL
);
6417 return STATUS_SUCCESS
;