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"
50 #include <ntstrsafe.h>
52 NTSTATUS
RtlStringCbVPrintfA(char* pszDest
, size_t cbDest
, const char* pszFormat
, va_list argList
); // not in mingw
55 #define INCOMPAT_SUPPORTED (BTRFS_INCOMPAT_FLAGS_MIXED_BACKREF | BTRFS_INCOMPAT_FLAGS_DEFAULT_SUBVOL | BTRFS_INCOMPAT_FLAGS_MIXED_GROUPS | \
56 BTRFS_INCOMPAT_FLAGS_COMPRESS_LZO | BTRFS_INCOMPAT_FLAGS_BIG_METADATA | BTRFS_INCOMPAT_FLAGS_RAID56 | \
57 BTRFS_INCOMPAT_FLAGS_EXTENDED_IREF | BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA | BTRFS_INCOMPAT_FLAGS_NO_HOLES | \
58 BTRFS_INCOMPAT_FLAGS_COMPRESS_ZSTD)
59 #define COMPAT_RO_SUPPORTED (BTRFS_COMPAT_RO_FLAGS_FREE_SPACE_CACHE | BTRFS_COMPAT_RO_FLAGS_FREE_SPACE_CACHE_VALID)
61 static const WCHAR device_name
[] = {'\\','B','t','r','f','s',0};
62 static const WCHAR dosdevice_name
[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\','B','t','r','f','s',0};
64 DEFINE_GUID(BtrfsBusInterface
, 0x4d414874, 0x6865, 0x6761, 0x6d, 0x65, 0x83, 0x69, 0x17, 0x9a, 0x7d, 0x1d);
66 PDRIVER_OBJECT drvobj
;
67 PDEVICE_OBJECT master_devobj
, busobj
;
69 bool have_sse42
= false, have_sse2
= false;
71 uint64_t num_reads
= 0;
72 LIST_ENTRY uid_map_list
, gid_map_list
;
74 ERESOURCE global_loading_lock
;
75 uint32_t debug_log_level
= 0;
76 uint32_t mount_compress
= 0;
77 uint32_t mount_compress_force
= 0;
78 uint32_t mount_compress_type
= 0;
79 uint32_t mount_zlib_level
= 3;
80 uint32_t mount_zstd_level
= 3;
81 uint32_t mount_flush_interval
= 30;
82 uint32_t mount_max_inline
= 2048;
83 uint32_t mount_skip_balance
= 0;
84 uint32_t mount_no_barrier
= 0;
85 uint32_t mount_no_trim
= 0;
86 uint32_t mount_clear_cache
= 0;
87 uint32_t mount_allow_degraded
= 0;
88 uint32_t mount_readonly
= 0;
90 bool log_started
= false;
91 UNICODE_STRING log_device
, log_file
, registry_path
;
92 tPsUpdateDiskCounters fPsUpdateDiskCounters
;
93 tCcCopyReadEx fCcCopyReadEx
;
94 tCcCopyWriteEx fCcCopyWriteEx
;
95 tCcSetAdditionalCacheAttributesEx fCcSetAdditionalCacheAttributesEx
;
96 tFsRtlUpdateDiskCounters fFsRtlUpdateDiskCounters
;
97 tIoUnregisterPlugPlayNotificationEx fIoUnregisterPlugPlayNotificationEx
;
98 tFsRtlGetEcpListFromIrp fFsRtlGetEcpListFromIrp
;
99 tFsRtlGetNextExtraCreateParameter fFsRtlGetNextExtraCreateParameter
;
100 tFsRtlValidateReparsePointBuffer fFsRtlValidateReparsePointBuffer
;
101 bool diskacc
= false;
102 void *notification_entry
= NULL
, *notification_entry2
= NULL
, *notification_entry3
= NULL
;
103 ERESOURCE pdo_list_lock
, mapping_lock
;
105 bool finished_probing
= false;
106 HANDLE degraded_wait_handle
= NULL
, mountmgr_thread_handle
= NULL
;
107 bool degraded_wait
= true;
108 KEVENT mountmgr_thread_event
;
109 bool shutting_down
= false;
112 PFILE_OBJECT comfo
= NULL
;
113 PDEVICE_OBJECT comdo
= NULL
;
114 HANDLE log_handle
= NULL
;
116 HANDLE serial_thread_handle
= NULL
;
118 static void init_serial(bool first_time
);
121 static NTSTATUS
close_file(_In_ PFILE_OBJECT FileObject
, _In_ PIRP Irp
);
125 IO_STATUS_BLOCK iosb
;
128 // no longer in Windows headers??
129 extern BOOLEAN
WdmlibRtlIsNtDdiVersionAvailable(ULONG Version
);
132 _Function_class_(IO_COMPLETION_ROUTINE
)
133 static NTSTATUS __stdcall
dbg_completion(_In_ PDEVICE_OBJECT DeviceObject
, _In_ PIRP Irp
, _In_ PVOID conptr
) {
134 read_context
* context
= conptr
;
136 UNUSED(DeviceObject
);
138 context
->iosb
= Irp
->IoStatus
;
139 KeSetEvent(&context
->Event
, 0, false);
141 return STATUS_MORE_PROCESSING_REQUIRED
;
144 #define DEBUG_MESSAGE_LEN 1024
146 #ifdef DEBUG_LONG_MESSAGES
147 void _debug_message(_In_
const char* func
, _In_
const char* file
, _In_
unsigned int line
, _In_
char* s
, ...) {
149 void _debug_message(_In_
const char* func
, _In_
char* s
, ...) {
151 LARGE_INTEGER offset
;
152 PIO_STACK_LOCATION IrpSp
;
157 read_context context
;
160 buf2
= ExAllocatePoolWithTag(NonPagedPool
, DEBUG_MESSAGE_LEN
, ALLOC_TAG
);
163 DbgPrint("Couldn't allocate buffer in debug_message\n");
167 #ifdef DEBUG_LONG_MESSAGES
168 sprintf(buf2
, "%p:%s:%s:%u:", PsGetCurrentThread(), func
, file
, line
);
170 sprintf(buf2
, "%p:%s:", PsGetCurrentThread(), func
);
172 buf
= &buf2
[strlen(buf2
)];
176 RtlStringCbVPrintfA(buf
, DEBUG_MESSAGE_LEN
- strlen(buf2
), s
, ap
);
178 ExAcquireResourceSharedLite(&log_lock
, true);
180 if (!log_started
|| (log_device
.Length
== 0 && log_file
.Length
== 0)) {
182 } else if (log_device
.Length
> 0) {
188 length
= (uint32_t)strlen(buf2
);
190 offset
.u
.LowPart
= 0;
191 offset
.u
.HighPart
= 0;
193 RtlZeroMemory(&context
, sizeof(read_context
));
195 KeInitializeEvent(&context
.Event
, NotificationEvent
, false);
197 Irp
= IoAllocateIrp(comdo
->StackSize
, false);
200 DbgPrint("IoAllocateIrp failed\n");
204 IrpSp
= IoGetNextIrpStackLocation(Irp
);
205 IrpSp
->MajorFunction
= IRP_MJ_WRITE
;
206 IrpSp
->FileObject
= comfo
;
208 if (comdo
->Flags
& DO_BUFFERED_IO
) {
209 Irp
->AssociatedIrp
.SystemBuffer
= buf2
;
211 Irp
->Flags
= IRP_BUFFERED_IO
;
212 } else if (comdo
->Flags
& DO_DIRECT_IO
) {
213 Irp
->MdlAddress
= IoAllocateMdl(buf2
, length
, false, false, NULL
);
214 if (!Irp
->MdlAddress
) {
215 DbgPrint("IoAllocateMdl failed\n");
219 MmBuildMdlForNonPagedPool(Irp
->MdlAddress
);
221 Irp
->UserBuffer
= buf2
;
224 IrpSp
->Parameters
.Write
.Length
= length
;
225 IrpSp
->Parameters
.Write
.ByteOffset
= offset
;
227 Irp
->UserIosb
= &context
.iosb
;
229 Irp
->UserEvent
= &context
.Event
;
231 IoSetCompletionRoutine(Irp
, dbg_completion
, &context
, true, true, true);
233 Status
= IoCallDriver(comdo
, Irp
);
235 if (Status
== STATUS_PENDING
) {
236 KeWaitForSingleObject(&context
.Event
, Executive
, KernelMode
, false, NULL
);
237 Status
= context
.iosb
.Status
;
240 if (comdo
->Flags
& DO_DIRECT_IO
)
241 IoFreeMdl(Irp
->MdlAddress
);
243 if (!NT_SUCCESS(Status
)) {
244 DbgPrint("failed to write to COM1 - error %08x\n", Status
);
250 } else if (log_handle
!= NULL
) {
251 IO_STATUS_BLOCK iosb
;
253 length
= (uint32_t)strlen(buf2
);
255 Status
= ZwWriteFile(log_handle
, NULL
, NULL
, NULL
, &iosb
, buf2
, length
, NULL
, NULL
);
257 if (!NT_SUCCESS(Status
)) {
258 DbgPrint("failed to write to file - error %08x\n", Status
);
263 ExReleaseResourceLite(&log_lock
);
272 bool is_top_level(_In_ PIRP Irp
) {
273 if (!IoGetTopLevelIrp()) {
274 IoSetTopLevelIrp(Irp
);
281 _Function_class_(DRIVER_UNLOAD
)
282 static void __stdcall
DriverUnload(_In_ PDRIVER_OBJECT DriverObject
) {
283 UNICODE_STRING dosdevice_nameW
;
285 TRACE("(%p)\n", DriverObject
);
289 IoUnregisterFileSystem(DriverObject
->DeviceObject
);
291 if (notification_entry2
) {
292 if (fIoUnregisterPlugPlayNotificationEx
)
293 fIoUnregisterPlugPlayNotificationEx(notification_entry2
);
295 IoUnregisterPlugPlayNotification(notification_entry2
);
298 if (notification_entry3
) {
299 if (fIoUnregisterPlugPlayNotificationEx
)
300 fIoUnregisterPlugPlayNotificationEx(notification_entry3
);
302 IoUnregisterPlugPlayNotification(notification_entry3
);
305 if (notification_entry
) {
306 if (fIoUnregisterPlugPlayNotificationEx
)
307 fIoUnregisterPlugPlayNotificationEx(notification_entry
);
309 IoUnregisterPlugPlayNotification(notification_entry
);
312 dosdevice_nameW
.Buffer
= (WCHAR
*)dosdevice_name
;
313 dosdevice_nameW
.Length
= dosdevice_nameW
.MaximumLength
= sizeof(dosdevice_name
) - sizeof(WCHAR
);
315 IoDeleteSymbolicLink(&dosdevice_nameW
);
316 IoDeleteDevice(DriverObject
->DeviceObject
);
318 while (!IsListEmpty(&uid_map_list
)) {
319 LIST_ENTRY
* le
= RemoveHeadList(&uid_map_list
);
320 uid_map
* um
= CONTAINING_RECORD(le
, uid_map
, listentry
);
327 while (!IsListEmpty(&gid_map_list
)) {
328 gid_map
* gm
= CONTAINING_RECORD(RemoveHeadList(&gid_map_list
), gid_map
, listentry
);
334 // FIXME - free volumes and their devpaths
338 ObDereferenceObject(comfo
);
344 ExDeleteResourceLite(&global_loading_lock
);
345 ExDeleteResourceLite(&pdo_list_lock
);
347 if (log_device
.Buffer
)
348 ExFreePool(log_device
.Buffer
);
351 ExFreePool(log_file
.Buffer
);
353 if (registry_path
.Buffer
)
354 ExFreePool(registry_path
.Buffer
);
357 ExDeleteResourceLite(&log_lock
);
359 ExDeleteResourceLite(&mapping_lock
);
362 static bool get_last_inode(_In_
_Requires_exclusive_lock_held_(_Curr_
->tree_lock
) device_extension
* Vcb
, _In_ root
* r
, _In_opt_ PIRP Irp
) {
364 traverse_ptr tp
, prev_tp
;
368 searchkey
.obj_id
= 0xffffffffffffffff;
369 searchkey
.obj_type
= 0xff;
370 searchkey
.offset
= 0xffffffffffffffff;
372 Status
= find_item(Vcb
, r
, &tp
, &searchkey
, false, Irp
);
373 if (!NT_SUCCESS(Status
)) {
374 ERR("error - find_item returned %08x\n", Status
);
378 if (tp
.item
->key
.obj_type
== TYPE_INODE_ITEM
|| (tp
.item
->key
.obj_type
== TYPE_ROOT_ITEM
&& !(tp
.item
->key
.obj_id
& 0x8000000000000000))) {
379 r
->lastinode
= tp
.item
->key
.obj_id
;
380 TRACE("last inode for tree %I64x is %I64x\n", r
->id
, r
->lastinode
);
384 while (find_prev_item(Vcb
, &tp
, &prev_tp
, Irp
)) {
387 TRACE("moving on to %I64x,%x,%I64x\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
389 if (tp
.item
->key
.obj_type
== TYPE_INODE_ITEM
|| (tp
.item
->key
.obj_type
== TYPE_ROOT_ITEM
&& !(tp
.item
->key
.obj_id
& 0x8000000000000000))) {
390 r
->lastinode
= tp
.item
->key
.obj_id
;
391 TRACE("last inode for tree %I64x is %I64x\n", r
->id
, r
->lastinode
);
396 r
->lastinode
= SUBVOL_ROOT_INODE
;
398 WARN("no INODE_ITEMs in tree %I64x\n", r
->id
);
404 static bool extract_xattr(_In_reads_bytes_(size
) void* item
, _In_ USHORT size
, _In_z_
char* name
, _Out_
uint8_t** data
, _Out_
uint16_t* datalen
) {
405 DIR_ITEM
* xa
= (DIR_ITEM
*)item
;
409 if (size
< sizeof(DIR_ITEM
) || size
< (sizeof(DIR_ITEM
) - 1 + xa
->m
+ xa
->n
)) {
410 WARN("DIR_ITEM is truncated\n");
414 if (xa
->n
== strlen(name
) && RtlCompareMemory(name
, xa
->name
, xa
->n
) == xa
->n
) {
415 TRACE("found xattr %s\n", name
);
420 *data
= ExAllocatePoolWithTag(PagedPool
, xa
->m
, ALLOC_TAG
);
422 ERR("out of memory\n");
426 RtlCopyMemory(*data
, &xa
->name
[xa
->n
], xa
->m
);
433 xasize
= sizeof(DIR_ITEM
) - 1 + xa
->m
+ xa
->n
;
437 xa
= (DIR_ITEM
*)&xa
->name
[xa
->m
+ xa
->n
];
442 TRACE("xattr %s not found\n", name
);
448 bool get_xattr(_In_
_Requires_lock_held_(_Curr_
->tree_lock
) device_extension
* Vcb
, _In_ root
* subvol
, _In_
uint64_t inode
, _In_z_
char* name
, _In_
uint32_t crc32
,
449 _Out_
uint8_t** data
, _Out_
uint16_t* datalen
, _In_opt_ PIRP Irp
) {
454 TRACE("(%p, %I64x, %I64x, %s, %08x, %p, %p)\n", Vcb
, subvol
->id
, inode
, name
, crc32
, data
, datalen
);
456 searchkey
.obj_id
= inode
;
457 searchkey
.obj_type
= TYPE_XATTR_ITEM
;
458 searchkey
.offset
= crc32
;
460 Status
= find_item(Vcb
, subvol
, &tp
, &searchkey
, false, Irp
);
461 if (!NT_SUCCESS(Status
)) {
462 ERR("error - find_item returned %08x\n", Status
);
466 if (keycmp(tp
.item
->key
, searchkey
)) {
467 TRACE("could not find item (%I64x,%x,%I64x)\n", searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
);
471 if (tp
.item
->size
< sizeof(DIR_ITEM
)) {
472 ERR("(%I64x,%x,%I64x) was %u bytes, expected at least %u\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, tp
.item
->size
, sizeof(DIR_ITEM
));
476 return extract_xattr(tp
.item
->data
, tp
.item
->size
, name
, data
, datalen
);
479 _Dispatch_type_(IRP_MJ_CLOSE
)
480 _Function_class_(DRIVER_DISPATCH
)
481 static NTSTATUS __stdcall
drv_close(_In_ PDEVICE_OBJECT DeviceObject
, _In_ PIRP Irp
) {
483 PIO_STACK_LOCATION IrpSp
;
484 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
487 FsRtlEnterFileSystem();
491 top_level
= is_top_level(Irp
);
493 if (DeviceObject
== master_devobj
) {
494 TRACE("Closing file system\n");
495 Status
= STATUS_SUCCESS
;
497 } else if (Vcb
&& Vcb
->type
== VCB_TYPE_VOLUME
) {
498 Status
= vol_close(DeviceObject
, Irp
);
500 } else if (!Vcb
|| Vcb
->type
!= VCB_TYPE_FS
) {
501 Status
= STATUS_INVALID_PARAMETER
;
505 IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
507 // FIXME - unmount if called for volume
508 // FIXME - call FsRtlNotifyUninitializeSync(&Vcb->NotifySync) if unmounting
510 Status
= close_file(IrpSp
->FileObject
, Irp
);
513 Irp
->IoStatus
.Status
= Status
;
514 Irp
->IoStatus
.Information
= 0;
516 IoCompleteRequest( Irp
, IO_DISK_INCREMENT
);
519 IoSetTopLevelIrp(NULL
);
521 TRACE("returning %08x\n", Status
);
523 FsRtlExitFileSystem();
528 _Dispatch_type_(IRP_MJ_FLUSH_BUFFERS
)
529 _Function_class_(DRIVER_DISPATCH
)
530 static NTSTATUS __stdcall
drv_flush_buffers(_In_ PDEVICE_OBJECT DeviceObject
, _In_ PIRP Irp
) {
532 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation( Irp
);
533 PFILE_OBJECT FileObject
= IrpSp
->FileObject
;
534 fcb
* fcb
= FileObject
->FsContext
;
535 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
538 FsRtlEnterFileSystem();
540 TRACE("flush buffers\n");
542 top_level
= is_top_level(Irp
);
544 if (Vcb
&& Vcb
->type
== VCB_TYPE_VOLUME
) {
545 Status
= vol_flush_buffers(DeviceObject
, Irp
);
547 } else if (!Vcb
|| Vcb
->type
!= VCB_TYPE_FS
) {
548 Status
= STATUS_SUCCESS
;
553 ERR("fcb was NULL\n");
554 Status
= STATUS_SUCCESS
;
558 if (fcb
== Vcb
->volume_fcb
) {
559 Status
= STATUS_SUCCESS
;
563 Irp
->IoStatus
.Information
= 0;
565 fcb
->Header
.IsFastIoPossible
= fast_io_possible(fcb
);
567 Status
= STATUS_SUCCESS
;
568 Irp
->IoStatus
.Status
= Status
;
570 if (fcb
->type
!= BTRFS_TYPE_DIRECTORY
) {
571 CcFlushCache(FileObject
->SectionObjectPointer
, NULL
, 0, &Irp
->IoStatus
);
573 if (fcb
->Header
.PagingIoResource
) {
574 ExAcquireResourceExclusiveLite(fcb
->Header
.PagingIoResource
, true);
575 ExReleaseResourceLite(fcb
->Header
.PagingIoResource
);
578 Status
= Irp
->IoStatus
.Status
;
582 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
584 TRACE("returning %08x\n", Status
);
587 IoSetTopLevelIrp(NULL
);
589 FsRtlExitFileSystem();
594 static void calculate_total_space(_In_ device_extension
* Vcb
, _Out_
uint64_t* totalsize
, _Out_
uint64_t* freespace
) {
595 uint64_t nfactor
, dfactor
, sectors_used
;
597 if (Vcb
->data_flags
& BLOCK_FLAG_DUPLICATE
|| Vcb
->data_flags
& BLOCK_FLAG_RAID1
|| Vcb
->data_flags
& BLOCK_FLAG_RAID10
) {
600 } else if (Vcb
->data_flags
& BLOCK_FLAG_RAID5
) {
601 nfactor
= Vcb
->superblock
.num_devices
- 1;
602 dfactor
= Vcb
->superblock
.num_devices
;
603 } else if (Vcb
->data_flags
& BLOCK_FLAG_RAID6
) {
604 nfactor
= Vcb
->superblock
.num_devices
- 2;
605 dfactor
= Vcb
->superblock
.num_devices
;
611 sectors_used
= Vcb
->superblock
.bytes_used
/ Vcb
->superblock
.sector_size
;
613 *totalsize
= (Vcb
->superblock
.total_bytes
/ Vcb
->superblock
.sector_size
) * nfactor
/ dfactor
;
614 *freespace
= sectors_used
> *totalsize
? 0 : (*totalsize
- sectors_used
);
618 // This function exists because we have to lie about our FS type in certain situations.
619 // MPR!MprGetConnection queries the FS type, and compares it to a whitelist. If it doesn't match,
620 // it will return ERROR_NO_NET_OR_BAD_PATH, which prevents UAC from working.
621 // The command mklink refuses to create hard links on anything other than NTFS, so we have to
622 // blacklist cmd.exe too.
624 static bool lie_about_fs_type() {
626 PROCESS_BASIC_INFORMATION pbi
;
634 static const WCHAR mpr
[] = L
"MPR.DLL";
635 static const WCHAR cmd
[] = L
"CMD.EXE";
636 static const WCHAR fsutil
[] = L
"FSUTIL.EXE";
637 UNICODE_STRING mprus
, cmdus
, fsutilus
;
639 mprus
.Buffer
= (WCHAR
*)mpr
;
640 mprus
.Length
= mprus
.MaximumLength
= sizeof(mpr
) - sizeof(WCHAR
);
641 cmdus
.Buffer
= (WCHAR
*)cmd
;
642 cmdus
.Length
= cmdus
.MaximumLength
= sizeof(cmd
) - sizeof(WCHAR
);
643 fsutilus
.Buffer
= (WCHAR
*)fsutil
;
644 fsutilus
.Length
= fsutilus
.MaximumLength
= sizeof(fsutil
) - sizeof(WCHAR
);
646 if (!PsGetCurrentProcess())
650 Status
= ZwQueryInformationProcess(NtCurrentProcess(), ProcessWow64Information
, &wow64info
, sizeof(wow64info
), NULL
);
652 if (NT_SUCCESS(Status
) && wow64info
!= 0)
656 Status
= ZwQueryInformationProcess(NtCurrentProcess(), ProcessBasicInformation
, &pbi
, sizeof(pbi
), &retlen
);
658 if (!NT_SUCCESS(Status
)) {
659 ERR("ZwQueryInformationProcess returned %08x\n", Status
);
663 if (!pbi
.PebBaseAddress
)
666 peb
= pbi
.PebBaseAddress
;
671 le
= peb
->Ldr
->InMemoryOrderModuleList
.Flink
;
672 while (le
!= &peb
->Ldr
->InMemoryOrderModuleList
) {
673 LDR_DATA_TABLE_ENTRY
* entry
= CONTAINING_RECORD(le
, LDR_DATA_TABLE_ENTRY
, InMemoryOrderLinks
);
674 bool blacklist
= false;
676 if (entry
->FullDllName
.Length
>= mprus
.Length
) {
679 name
.Buffer
= &entry
->FullDllName
.Buffer
[(entry
->FullDllName
.Length
- mprus
.Length
) / sizeof(WCHAR
)];
680 name
.Length
= name
.MaximumLength
= mprus
.Length
;
682 blacklist
= FsRtlAreNamesEqual(&name
, &mprus
, true, NULL
);
685 if (!blacklist
&& entry
->FullDllName
.Length
>= cmdus
.Length
) {
688 name
.Buffer
= &entry
->FullDllName
.Buffer
[(entry
->FullDllName
.Length
- cmdus
.Length
) / sizeof(WCHAR
)];
689 name
.Length
= name
.MaximumLength
= cmdus
.Length
;
691 blacklist
= FsRtlAreNamesEqual(&name
, &cmdus
, true, NULL
);
694 if (!blacklist
&& entry
->FullDllName
.Length
>= fsutilus
.Length
) {
697 name
.Buffer
= &entry
->FullDllName
.Buffer
[(entry
->FullDllName
.Length
- fsutilus
.Length
) / sizeof(WCHAR
)];
698 name
.Length
= name
.MaximumLength
= fsutilus
.Length
;
700 blacklist
= FsRtlAreNamesEqual(&name
, &fsutilus
, true, NULL
);
707 frames
= ExAllocatePoolWithTag(PagedPool
, 256 * sizeof(void*), ALLOC_TAG
);
709 ERR("out of memory\n");
713 num_frames
= RtlWalkFrameChain(frames
, 256, 1);
715 for (i
= 0; i
< num_frames
; i
++) {
716 // entry->Reserved3[1] appears to be the image size
717 if (frames
[i
] >= entry
->DllBase
&& (ULONG_PTR
)frames
[i
] <= (ULONG_PTR
)entry
->DllBase
+ (ULONG_PTR
)entry
->Reserved3
[1]) {
733 // version of RtlUTF8ToUnicodeN for Vista and below
734 NTSTATUS
utf8_to_utf16(WCHAR
* dest
, ULONG dest_max
, ULONG
* dest_len
, char* src
, ULONG src_len
) {
735 NTSTATUS Status
= STATUS_SUCCESS
;
736 uint8_t* in
= (uint8_t*)src
;
737 uint16_t* out
= (uint16_t*)dest
;
738 ULONG needed
= 0, left
= dest_max
/ sizeof(uint16_t);
742 for (i
= 0; i
< src_len
; ++i
) {
745 for (ULONG i
= 0; i
< src_len
; i
++) {
751 else if ((in
[i
] & 0xe0) == 0xc0) {
752 if (i
== src_len
- 1 || (in
[i
+1] & 0xc0) != 0x80) {
754 Status
= STATUS_SOME_NOT_MAPPED
;
756 cp
= ((in
[i
] & 0x1f) << 6) | (in
[i
+1] & 0x3f);
759 } else if ((in
[i
] & 0xf0) == 0xe0) {
760 if (i
>= src_len
- 2 || (in
[i
+1] & 0xc0) != 0x80 || (in
[i
+2] & 0xc0) != 0x80) {
762 Status
= STATUS_SOME_NOT_MAPPED
;
764 cp
= ((in
[i
] & 0xf) << 12) | ((in
[i
+1] & 0x3f) << 6) | (in
[i
+2] & 0x3f);
767 } else if ((in
[i
] & 0xf8) == 0xf0) {
768 if (i
>= src_len
- 3 || (in
[i
+1] & 0xc0) != 0x80 || (in
[i
+2] & 0xc0) != 0x80 || (in
[i
+3] & 0xc0) != 0x80) {
770 Status
= STATUS_SOME_NOT_MAPPED
;
772 cp
= ((in
[i
] & 0x7) << 18) | ((in
[i
+1] & 0x3f) << 12) | ((in
[i
+2] & 0x3f) << 6) | (in
[i
+3] & 0x3f);
777 Status
= STATUS_SOME_NOT_MAPPED
;
782 Status
= STATUS_SOME_NOT_MAPPED
;
788 return STATUS_BUFFER_OVERFLOW
;
796 return STATUS_BUFFER_OVERFLOW
;
800 *out
= 0xd800 | ((cp
& 0xffc00) >> 10);
803 *out
= 0xdc00 | (cp
& 0x3ff);
811 needed
+= sizeof(uint16_t);
813 needed
+= 2 * sizeof(uint16_t);
822 // version of RtlUnicodeToUTF8N for Vista and below
823 NTSTATUS
utf16_to_utf8(char* dest
, ULONG dest_max
, ULONG
* dest_len
, WCHAR
* src
, ULONG src_len
) {
824 NTSTATUS Status
= STATUS_SUCCESS
;
825 uint16_t* in
= (uint16_t*)src
;
826 uint8_t* out
= (uint8_t*)dest
;
827 ULONG in_len
= src_len
/ sizeof(uint16_t);
828 ULONG needed
= 0, left
= dest_max
;
832 for (i
= 0; i
< in_len
; i
++) {
835 for (ULONG i
= 0; i
< in_len
; i
++) {
840 if ((cp
& 0xfc00) == 0xd800) {
841 if (i
== in_len
- 1 || (*in
& 0xfc00) != 0xdc00) {
843 Status
= STATUS_SOME_NOT_MAPPED
;
845 cp
= (cp
& 0x3ff) << 10;
852 } else if ((cp
& 0xfc00) == 0xdc00) {
854 Status
= STATUS_SOME_NOT_MAPPED
;
859 Status
= STATUS_SOME_NOT_MAPPED
;
865 return STATUS_BUFFER_OVERFLOW
;
871 } else if (cp
< 0x800) {
873 return STATUS_BUFFER_OVERFLOW
;
875 *out
= 0xc0 | ((cp
& 0x7c0) >> 6);
878 *out
= 0x80 | (cp
& 0x3f);
882 } else if (cp
< 0x10000) {
884 return STATUS_BUFFER_OVERFLOW
;
886 *out
= 0xe0 | ((cp
& 0xf000) >> 12);
889 *out
= 0x80 | ((cp
& 0xfc0) >> 6);
892 *out
= 0x80 | (cp
& 0x3f);
898 return STATUS_BUFFER_OVERFLOW
;
900 *out
= 0xf0 | ((cp
& 0x1c0000) >> 18);
903 *out
= 0x80 | ((cp
& 0x3f000) >> 12);
906 *out
= 0x80 | ((cp
& 0xfc0) >> 6);
909 *out
= 0x80 | (cp
& 0x3f);
920 else if (cp
< 0x10000)
932 _Dispatch_type_(IRP_MJ_QUERY_VOLUME_INFORMATION
)
933 _Function_class_(DRIVER_DISPATCH
)
934 static NTSTATUS __stdcall
drv_query_volume_information(_In_ PDEVICE_OBJECT DeviceObject
, _In_ PIRP Irp
) {
935 PIO_STACK_LOCATION IrpSp
;
937 ULONG BytesCopied
= 0;
938 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
941 FsRtlEnterFileSystem();
943 TRACE("query volume information\n");
944 top_level
= is_top_level(Irp
);
946 if (Vcb
&& Vcb
->type
== VCB_TYPE_VOLUME
) {
947 Status
= vol_query_volume_information(DeviceObject
, Irp
);
949 } else if (!Vcb
|| Vcb
->type
!= VCB_TYPE_FS
) {
950 Status
= STATUS_INVALID_PARAMETER
;
954 IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
956 Status
= STATUS_NOT_IMPLEMENTED
;
958 switch (IrpSp
->Parameters
.QueryVolume
.FsInformationClass
) {
959 case FileFsAttributeInformation
:
961 FILE_FS_ATTRIBUTE_INFORMATION
* data
= Irp
->AssociatedIrp
.SystemBuffer
;
962 bool overflow
= false;
964 static const WCHAR ntfs
[] = L
"NTFS";
966 static const WCHAR btrfs
[] = L
"Btrfs";
967 const WCHAR
* fs_name
;
968 ULONG fs_name_len
, orig_fs_name_len
;
971 if (Irp
->RequestorMode
== UserMode
&& lie_about_fs_type()) {
973 orig_fs_name_len
= fs_name_len
= sizeof(ntfs
) - sizeof(WCHAR
);
976 orig_fs_name_len
= fs_name_len
= sizeof(btrfs
) - sizeof(WCHAR
);
980 orig_fs_name_len
= fs_name_len
= sizeof(btrfs
) - sizeof(WCHAR
);
983 TRACE("FileFsAttributeInformation\n");
985 if (IrpSp
->Parameters
.QueryVolume
.Length
< sizeof(FILE_FS_ATTRIBUTE_INFORMATION
) - sizeof(WCHAR
) + fs_name_len
) {
986 if (IrpSp
->Parameters
.QueryVolume
.Length
> sizeof(FILE_FS_ATTRIBUTE_INFORMATION
) - sizeof(WCHAR
))
987 fs_name_len
= IrpSp
->Parameters
.QueryVolume
.Length
- sizeof(FILE_FS_ATTRIBUTE_INFORMATION
) + sizeof(WCHAR
);
994 data
->FileSystemAttributes
= FILE_CASE_PRESERVED_NAMES
| FILE_CASE_SENSITIVE_SEARCH
|
995 FILE_UNICODE_ON_DISK
| FILE_NAMED_STREAMS
| FILE_SUPPORTS_HARD_LINKS
| FILE_PERSISTENT_ACLS
|
996 FILE_SUPPORTS_REPARSE_POINTS
| FILE_SUPPORTS_SPARSE_FILES
| FILE_SUPPORTS_OBJECT_IDS
|
997 FILE_SUPPORTS_OPEN_BY_FILE_ID
| FILE_SUPPORTS_EXTENDED_ATTRIBUTES
| FILE_SUPPORTS_BLOCK_REFCOUNTING
|
998 FILE_SUPPORTS_POSIX_UNLINK_RENAME
;
1000 data
->FileSystemAttributes
|= FILE_READ_ONLY_VOLUME
;
1002 // should also be FILE_FILE_COMPRESSION when supported
1003 data
->MaximumComponentNameLength
= 255; // FIXME - check
1004 data
->FileSystemNameLength
= orig_fs_name_len
;
1005 RtlCopyMemory(data
->FileSystemName
, fs_name
, fs_name_len
);
1007 BytesCopied
= sizeof(FILE_FS_ATTRIBUTE_INFORMATION
) - sizeof(WCHAR
) + fs_name_len
;
1008 Status
= overflow
? STATUS_BUFFER_OVERFLOW
: STATUS_SUCCESS
;
1012 case FileFsDeviceInformation
:
1014 FILE_FS_DEVICE_INFORMATION
* ffdi
= Irp
->AssociatedIrp
.SystemBuffer
;
1016 TRACE("FileFsDeviceInformation\n");
1018 ffdi
->DeviceType
= FILE_DEVICE_DISK
;
1020 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, true);
1021 ffdi
->Characteristics
= Vcb
->Vpb
->RealDevice
->Characteristics
;
1022 ExReleaseResourceLite(&Vcb
->tree_lock
);
1025 ffdi
->Characteristics
|= FILE_READ_ONLY_DEVICE
;
1027 ffdi
->Characteristics
&= ~FILE_READ_ONLY_DEVICE
;
1029 BytesCopied
= sizeof(FILE_FS_DEVICE_INFORMATION
);
1030 Status
= STATUS_SUCCESS
;
1035 case FileFsFullSizeInformation
:
1037 FILE_FS_FULL_SIZE_INFORMATION
* ffsi
= Irp
->AssociatedIrp
.SystemBuffer
;
1039 TRACE("FileFsFullSizeInformation\n");
1041 calculate_total_space(Vcb
, (uint64_t*)&ffsi
->TotalAllocationUnits
.QuadPart
, (uint64_t*)&ffsi
->ActualAvailableAllocationUnits
.QuadPart
);
1042 ffsi
->CallerAvailableAllocationUnits
.QuadPart
= ffsi
->ActualAvailableAllocationUnits
.QuadPart
;
1043 ffsi
->SectorsPerAllocationUnit
= Vcb
->superblock
.sector_size
/ 512;
1044 ffsi
->BytesPerSector
= 512;
1046 BytesCopied
= sizeof(FILE_FS_FULL_SIZE_INFORMATION
);
1047 Status
= STATUS_SUCCESS
;
1052 case FileFsObjectIdInformation
:
1054 FILE_FS_OBJECTID_INFORMATION
* ffoi
= Irp
->AssociatedIrp
.SystemBuffer
;
1056 TRACE("FileFsObjectIdInformation\n");
1058 RtlCopyMemory(ffoi
->ObjectId
, &Vcb
->superblock
.uuid
.uuid
[0], sizeof(UCHAR
) * 16);
1059 RtlZeroMemory(ffoi
->ExtendedInfo
, sizeof(ffoi
->ExtendedInfo
));
1061 BytesCopied
= sizeof(FILE_FS_OBJECTID_INFORMATION
);
1062 Status
= STATUS_SUCCESS
;
1067 case FileFsSizeInformation
:
1069 FILE_FS_SIZE_INFORMATION
* ffsi
= Irp
->AssociatedIrp
.SystemBuffer
;
1071 TRACE("FileFsSizeInformation\n");
1073 calculate_total_space(Vcb
, (uint64_t*)&ffsi
->TotalAllocationUnits
.QuadPart
, (uint64_t*)&ffsi
->AvailableAllocationUnits
.QuadPart
);
1074 ffsi
->SectorsPerAllocationUnit
= Vcb
->superblock
.sector_size
/ 512;
1075 ffsi
->BytesPerSector
= 512;
1077 BytesCopied
= sizeof(FILE_FS_SIZE_INFORMATION
);
1078 Status
= STATUS_SUCCESS
;
1083 case FileFsVolumeInformation
:
1085 FILE_FS_VOLUME_INFORMATION
* data
= Irp
->AssociatedIrp
.SystemBuffer
;
1086 FILE_FS_VOLUME_INFORMATION ffvi
;
1087 bool overflow
= false;
1088 ULONG label_len
, orig_label_len
;
1090 TRACE("FileFsVolumeInformation\n");
1091 TRACE("max length = %u\n", IrpSp
->Parameters
.QueryVolume
.Length
);
1093 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, true);
1095 Status
= utf8_to_utf16(NULL
, 0, &label_len
, Vcb
->superblock
.label
, (ULONG
)strlen(Vcb
->superblock
.label
));
1096 if (!NT_SUCCESS(Status
)) {
1097 ERR("utf8_to_utf16 returned %08x\n", Status
);
1098 ExReleaseResourceLite(&Vcb
->tree_lock
);
1102 orig_label_len
= label_len
;
1104 if (IrpSp
->Parameters
.QueryVolume
.Length
< sizeof(FILE_FS_VOLUME_INFORMATION
) - sizeof(WCHAR
) + label_len
) {
1105 if (IrpSp
->Parameters
.QueryVolume
.Length
> sizeof(FILE_FS_VOLUME_INFORMATION
) - sizeof(WCHAR
))
1106 label_len
= IrpSp
->Parameters
.QueryVolume
.Length
- sizeof(FILE_FS_VOLUME_INFORMATION
) + sizeof(WCHAR
);
1113 TRACE("label_len = %u\n", label_len
);
1115 ffvi
.VolumeCreationTime
.QuadPart
= 0; // FIXME
1116 ffvi
.VolumeSerialNumber
= Vcb
->superblock
.uuid
.uuid
[12] << 24 | Vcb
->superblock
.uuid
.uuid
[13] << 16 | Vcb
->superblock
.uuid
.uuid
[14] << 8 | Vcb
->superblock
.uuid
.uuid
[15];
1117 ffvi
.VolumeLabelLength
= orig_label_len
;
1118 ffvi
.SupportsObjects
= false;
1120 RtlCopyMemory(data
, &ffvi
, min(sizeof(FILE_FS_VOLUME_INFORMATION
) - sizeof(WCHAR
), IrpSp
->Parameters
.QueryVolume
.Length
));
1122 if (label_len
> 0) {
1125 Status
= utf8_to_utf16(&data
->VolumeLabel
[0], label_len
, &bytecount
, Vcb
->superblock
.label
, (ULONG
)strlen(Vcb
->superblock
.label
));
1126 if (!NT_SUCCESS(Status
) && Status
!= STATUS_BUFFER_TOO_SMALL
) {
1127 ERR("utf8_to_utf16 returned %08x\n", Status
);
1128 ExReleaseResourceLite(&Vcb
->tree_lock
);
1132 TRACE("label = %.*S\n", label_len
/ sizeof(WCHAR
), data
->VolumeLabel
);
1135 ExReleaseResourceLite(&Vcb
->tree_lock
);
1137 BytesCopied
= sizeof(FILE_FS_VOLUME_INFORMATION
) - sizeof(WCHAR
) + label_len
;
1138 Status
= overflow
? STATUS_BUFFER_OVERFLOW
: STATUS_SUCCESS
;
1143 #ifdef _MSC_VER // not in mingw yet
1144 case FileFsSectorSizeInformation
:
1146 FILE_FS_SECTOR_SIZE_INFORMATION
* data
= Irp
->AssociatedIrp
.SystemBuffer
;
1148 data
->LogicalBytesPerSector
= Vcb
->superblock
.sector_size
;
1149 data
->PhysicalBytesPerSectorForAtomicity
= Vcb
->superblock
.sector_size
;
1150 data
->PhysicalBytesPerSectorForPerformance
= Vcb
->superblock
.sector_size
;
1151 data
->FileSystemEffectivePhysicalBytesPerSectorForAtomicity
= Vcb
->superblock
.sector_size
;
1152 data
->ByteOffsetForSectorAlignment
= 0;
1153 data
->ByteOffsetForPartitionAlignment
= 0;
1155 data
->Flags
= SSINFO_FLAGS_ALIGNED_DEVICE
| SSINFO_FLAGS_PARTITION_ALIGNED_ON_DEVICE
;
1157 if (Vcb
->trim
&& !Vcb
->options
.no_trim
)
1158 data
->Flags
|= SSINFO_FLAGS_TRIM_ENABLED
;
1160 BytesCopied
= sizeof(FILE_FS_SECTOR_SIZE_INFORMATION
);
1165 #endif /* __REACTOS__ */
1168 Status
= STATUS_INVALID_PARAMETER
;
1169 WARN("unknown FsInformationClass %u\n", IrpSp
->Parameters
.QueryVolume
.FsInformationClass
);
1173 if (!NT_SUCCESS(Status
) && Status
!= STATUS_BUFFER_OVERFLOW
)
1174 Irp
->IoStatus
.Information
= 0;
1176 Irp
->IoStatus
.Information
= BytesCopied
;
1179 Irp
->IoStatus
.Status
= Status
;
1181 IoCompleteRequest( Irp
, IO_DISK_INCREMENT
);
1184 IoSetTopLevelIrp(NULL
);
1186 TRACE("query volume information returning %08x\n", Status
);
1188 FsRtlExitFileSystem();
1193 _Function_class_(IO_COMPLETION_ROUTINE
)
1194 static NTSTATUS __stdcall
read_completion(_In_ PDEVICE_OBJECT DeviceObject
, _In_ PIRP Irp
, _In_ PVOID conptr
) {
1195 read_context
* context
= conptr
;
1197 UNUSED(DeviceObject
);
1199 context
->iosb
= Irp
->IoStatus
;
1200 KeSetEvent(&context
->Event
, 0, false);
1202 return STATUS_MORE_PROCESSING_REQUIRED
;
1205 NTSTATUS
create_root(_In_
_Requires_exclusive_lock_held_(_Curr_
->tree_lock
) device_extension
* Vcb
, _In_
uint64_t id
,
1206 _Out_ root
** rootptr
, _In_
bool no_tree
, _In_
uint64_t offset
, _In_opt_ PIRP Irp
) {
1213 r
= ExAllocatePoolWithTag(PagedPool
, sizeof(root
), ALLOC_TAG
);
1215 ERR("out of memory\n");
1216 return STATUS_INSUFFICIENT_RESOURCES
;
1219 r
->nonpaged
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(root_nonpaged
), ALLOC_TAG
);
1221 ERR("out of memory\n");
1223 return STATUS_INSUFFICIENT_RESOURCES
;
1227 t
= ExAllocatePoolWithTag(PagedPool
, sizeof(tree
), ALLOC_TAG
);
1229 ERR("out of memory\n");
1230 ExFreePool(r
->nonpaged
);
1232 return STATUS_INSUFFICIENT_RESOURCES
;
1237 t
->is_unique
= true;
1238 t
->uniqueness_determined
= true;
1242 ri
= ExAllocatePoolWithTag(PagedPool
, sizeof(ROOT_ITEM
), ALLOC_TAG
);
1244 ERR("out of memory\n");
1249 ExFreePool(r
->nonpaged
);
1251 return STATUS_INSUFFICIENT_RESOURCES
;
1255 r
->treeholder
.address
= 0;
1256 r
->treeholder
.generation
= Vcb
->superblock
.generation
;
1257 r
->treeholder
.tree
= t
;
1260 r
->received
= false;
1264 RtlZeroMemory(&r
->root_item
, sizeof(ROOT_ITEM
));
1265 r
->root_item
.num_references
= 1;
1266 r
->fcbs_version
= 0;
1267 r
->checked_for_orphans
= true;
1268 InitializeListHead(&r
->fcbs
);
1269 RtlZeroMemory(r
->fcbs_ptrs
, sizeof(LIST_ENTRY
*) * 256);
1271 RtlCopyMemory(ri
, &r
->root_item
, sizeof(ROOT_ITEM
));
1273 // We ask here for a traverse_ptr to the item we're inserting, so we can
1274 // copy some of the tree's variables
1276 Status
= insert_tree_item(Vcb
, Vcb
->root_root
, id
, TYPE_ROOT_ITEM
, offset
, ri
, sizeof(ROOT_ITEM
), &tp
, Irp
);
1277 if (!NT_SUCCESS(Status
)) {
1278 ERR("insert_tree_item returned %08x\n", Status
);
1284 ExFreePool(r
->nonpaged
);
1289 ExInitializeResourceLite(&r
->nonpaged
->load_tree_lock
);
1291 InsertTailList(&Vcb
->roots
, &r
->list_entry
);
1294 RtlZeroMemory(&t
->header
, sizeof(tree_header
));
1295 t
->header
.fs_uuid
= tp
.tree
->header
.fs_uuid
;
1296 t
->header
.address
= 0;
1297 t
->header
.flags
= HEADER_FLAG_MIXED_BACKREF
| 1; // 1 == "written"? Why does the Linux driver record this?
1298 t
->header
.chunk_tree_uuid
= tp
.tree
->header
.chunk_tree_uuid
;
1299 t
->header
.generation
= Vcb
->superblock
.generation
;
1300 t
->header
.tree_id
= id
;
1301 t
->header
.num_items
= 0;
1302 t
->header
.level
= 0;
1304 t
->has_address
= false;
1311 InitializeListHead(&t
->itemlist
);
1314 t
->has_new_address
= false;
1315 t
->updated_extents
= false;
1317 InsertTailList(&Vcb
->trees
, &t
->list_entry
);
1318 t
->list_entry_hash
.Flink
= NULL
;
1321 Vcb
->need_write
= true;
1326 return STATUS_SUCCESS
;
1329 static NTSTATUS
set_label(_In_ device_extension
* Vcb
, _In_ FILE_FS_LABEL_INFORMATION
* ffli
) {
1334 TRACE("label = %.*S\n", ffli
->VolumeLabelLength
/ sizeof(WCHAR
), ffli
->VolumeLabel
);
1336 vollen
= ffli
->VolumeLabelLength
;
1338 for (i
= 0; i
< ffli
->VolumeLabelLength
/ sizeof(WCHAR
); i
++) {
1339 if (ffli
->VolumeLabel
[i
] == 0) {
1340 vollen
= i
* sizeof(WCHAR
);
1342 } else if (ffli
->VolumeLabel
[i
] == '/' || ffli
->VolumeLabel
[i
] == '\\') {
1343 Status
= STATUS_INVALID_VOLUME_LABEL
;
1351 Status
= utf16_to_utf8(NULL
, 0, &utf8len
, ffli
->VolumeLabel
, vollen
);
1352 if (!NT_SUCCESS(Status
))
1355 if (utf8len
> MAX_LABEL_SIZE
) {
1356 Status
= STATUS_INVALID_VOLUME_LABEL
;
1361 ExAcquireResourceExclusiveLite(&Vcb
->tree_lock
, true);
1364 Status
= utf16_to_utf8((PCHAR
)&Vcb
->superblock
.label
, MAX_LABEL_SIZE
, &utf8len
, ffli
->VolumeLabel
, vollen
);
1365 if (!NT_SUCCESS(Status
))
1368 Status
= STATUS_SUCCESS
;
1370 if (utf8len
< MAX_LABEL_SIZE
)
1371 RtlZeroMemory(Vcb
->superblock
.label
+ utf8len
, MAX_LABEL_SIZE
- utf8len
);
1373 Vcb
->need_write
= true;
1376 ExReleaseResourceLite(&Vcb
->tree_lock
);
1379 TRACE("returning %08x\n", Status
);
1384 _Dispatch_type_(IRP_MJ_SET_VOLUME_INFORMATION
)
1385 _Function_class_(DRIVER_DISPATCH
)
1386 static NTSTATUS __stdcall
drv_set_volume_information(_In_ PDEVICE_OBJECT DeviceObject
, _In_ PIRP Irp
) {
1387 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
1388 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
1392 FsRtlEnterFileSystem();
1394 TRACE("set volume information\n");
1396 top_level
= is_top_level(Irp
);
1398 if (Vcb
&& Vcb
->type
== VCB_TYPE_VOLUME
) {
1399 Status
= vol_set_volume_information(DeviceObject
, Irp
);
1401 } else if (!Vcb
|| Vcb
->type
!= VCB_TYPE_FS
) {
1402 Status
= STATUS_INVALID_PARAMETER
;
1406 Status
= STATUS_NOT_IMPLEMENTED
;
1408 if (Vcb
->readonly
) {
1409 Status
= STATUS_MEDIA_WRITE_PROTECTED
;
1413 if (Vcb
->removing
|| Vcb
->locked
) {
1414 Status
= STATUS_ACCESS_DENIED
;
1418 switch (IrpSp
->Parameters
.SetVolume
.FsInformationClass
) {
1419 case FileFsControlInformation
:
1420 FIXME("STUB: FileFsControlInformation\n");
1423 case FileFsLabelInformation
:
1424 TRACE("FileFsLabelInformation\n");
1426 Status
= set_label(Vcb
, Irp
->AssociatedIrp
.SystemBuffer
);
1429 case FileFsObjectIdInformation
:
1430 FIXME("STUB: FileFsObjectIdInformation\n");
1434 WARN("Unrecognized FsInformationClass 0x%x\n", IrpSp
->Parameters
.SetVolume
.FsInformationClass
);
1439 Irp
->IoStatus
.Status
= Status
;
1440 Irp
->IoStatus
.Information
= 0;
1442 TRACE("returning %08x\n", Status
);
1444 IoCompleteRequest( Irp
, IO_NO_INCREMENT
);
1447 IoSetTopLevelIrp(NULL
);
1449 FsRtlExitFileSystem();
1454 static WCHAR
* file_desc_fcb(_In_ fcb
* fcb
) {
1460 if (fcb
->debug_desc
)
1461 return fcb
->debug_desc
;
1463 if (fcb
== fcb
->Vcb
->volume_fcb
)
1464 return L
"volume FCB";
1466 fcb
->debug_desc
= ExAllocatePoolWithTag(PagedPool
, 60 * sizeof(WCHAR
), ALLOC_TAG
);
1467 if (!fcb
->debug_desc
)
1468 return L
"(memory error)";
1470 // I know this is pretty hackish...
1471 // GCC doesn't like %llx in sprintf, and MSVC won't let us use swprintf
1472 // without the CRT, which breaks drivers.
1474 sprintf(s
, "subvol %x, inode %x", (uint32_t)fcb
->subvol
->id
, (uint32_t)fcb
->inode
);
1477 as
.Length
= as
.MaximumLength
= (USHORT
)strlen(s
);
1479 us
.Buffer
= fcb
->debug_desc
;
1480 us
.MaximumLength
= 60 * sizeof(WCHAR
);
1483 Status
= RtlAnsiStringToUnicodeString(&us
, &as
, false);
1484 if (!NT_SUCCESS(Status
))
1485 return L
"(RtlAnsiStringToUnicodeString error)";
1487 us
.Buffer
[us
.Length
/ sizeof(WCHAR
)] = 0;
1489 return fcb
->debug_desc
;
1492 WCHAR
* file_desc_fileref(_In_ file_ref
* fileref
) {
1497 if (fileref
->debug_desc
)
1498 return fileref
->debug_desc
;
1500 fn
.Length
= fn
.MaximumLength
= 0;
1501 Status
= fileref_get_filename(fileref
, &fn
, NULL
, &reqlen
);
1502 if (Status
!= STATUS_BUFFER_OVERFLOW
)
1505 if (reqlen
> 0xffff - sizeof(WCHAR
))
1506 return L
"(too long)";
1508 fileref
->debug_desc
= ExAllocatePoolWithTag(PagedPool
, reqlen
+ sizeof(WCHAR
), ALLOC_TAG
);
1509 if (!fileref
->debug_desc
)
1510 return L
"(memory error)";
1512 fn
.Buffer
= fileref
->debug_desc
;
1514 fn
.MaximumLength
= (USHORT
)(reqlen
+ sizeof(WCHAR
));
1516 Status
= fileref_get_filename(fileref
, &fn
, NULL
, &reqlen
);
1517 if (!NT_SUCCESS(Status
)) {
1518 ExFreePool(fileref
->debug_desc
);
1519 fileref
->debug_desc
= NULL
;
1523 fileref
->debug_desc
[fn
.Length
/ sizeof(WCHAR
)] = 0;
1525 return fileref
->debug_desc
;
1529 WCHAR
* file_desc(_In_ PFILE_OBJECT FileObject
) {
1530 fcb
* fcb
= FileObject
->FsContext
;
1531 ccb
* ccb
= FileObject
->FsContext2
;
1532 file_ref
* fileref
= ccb
? ccb
->fileref
: NULL
;
1534 if (fcb
->Header
.Flags2
& FSRTL_FLAG2_IS_PAGING_FILE
)
1535 return L
"(paging file)";
1538 return file_desc_fileref(fileref
);
1540 return file_desc_fcb(fcb
);
1543 void send_notification_fileref(_In_ file_ref
* fileref
, _In_ ULONG filter_match
, _In_ ULONG action
, _In_opt_ PUNICODE_STRING stream
) {
1548 fcb
* fcb
= fileref
->fcb
;
1550 fn
.Length
= fn
.MaximumLength
= 0;
1551 Status
= fileref_get_filename(fileref
, &fn
, NULL
, &reqlen
);
1552 if (Status
!= STATUS_BUFFER_OVERFLOW
) {
1553 ERR("fileref_get_filename returned %08x\n", Status
);
1557 if (reqlen
> 0xffff) {
1558 WARN("reqlen was too long for FsRtlNotifyFilterReportChange\n");
1562 fn
.Buffer
= ExAllocatePoolWithTag(PagedPool
, reqlen
, ALLOC_TAG
);
1564 ERR("out of memory\n");
1568 fn
.MaximumLength
= (USHORT
)reqlen
;
1571 Status
= fileref_get_filename(fileref
, &fn
, &name_offset
, &reqlen
);
1572 if (!NT_SUCCESS(Status
)) {
1573 ERR("fileref_get_filename returned %08x\n", Status
);
1574 ExFreePool(fn
.Buffer
);
1578 FsRtlNotifyFilterReportChange(fcb
->Vcb
->NotifySync
, &fcb
->Vcb
->DirNotifyList
, (PSTRING
)&fn
, name_offset
,
1579 (PSTRING
)stream
, NULL
, filter_match
, action
, NULL
, NULL
);
1580 ExFreePool(fn
.Buffer
);
1583 void send_notification_fcb(_In_ file_ref
* fileref
, _In_ ULONG filter_match
, _In_ ULONG action
, _In_opt_ PUNICODE_STRING stream
) {
1584 fcb
* fcb
= fileref
->fcb
;
1588 // no point looking for hardlinks if st_nlink == 1
1589 if (fileref
->fcb
->inode_item
.st_nlink
== 1) {
1590 send_notification_fileref(fileref
, filter_match
, action
, stream
);
1594 ExAcquireResourceExclusiveLite(&fcb
->Vcb
->fileref_lock
, true);
1596 le
= fcb
->hardlinks
.Flink
;
1597 while (le
!= &fcb
->hardlinks
) {
1598 hardlink
* hl
= CONTAINING_RECORD(le
, hardlink
, list_entry
);
1601 Status
= open_fileref_by_inode(fcb
->Vcb
, fcb
->subvol
, hl
->parent
, &parfr
, NULL
);
1603 if (!NT_SUCCESS(Status
))
1604 ERR("open_fileref_by_inode returned %08x\n", Status
);
1605 else if (!parfr
->deleted
) {
1609 fn
.Length
= fn
.MaximumLength
= 0;
1610 Status
= fileref_get_filename(parfr
, &fn
, NULL
, &pathlen
);
1611 if (Status
!= STATUS_BUFFER_OVERFLOW
) {
1612 ERR("fileref_get_filename returned %08x\n", Status
);
1613 free_fileref(parfr
);
1617 if (parfr
!= fcb
->Vcb
->root_fileref
)
1618 pathlen
+= sizeof(WCHAR
);
1620 if (pathlen
+ hl
->name
.Length
> 0xffff) {
1621 WARN("pathlen + hl->name.Length was too long for FsRtlNotifyFilterReportChange\n");
1622 free_fileref(parfr
);
1626 fn
.MaximumLength
= (USHORT
)(pathlen
+ hl
->name
.Length
);
1627 fn
.Buffer
= ExAllocatePoolWithTag(PagedPool
, fn
.MaximumLength
, ALLOC_TAG
);
1629 ERR("out of memory\n");
1630 free_fileref(parfr
);
1634 Status
= fileref_get_filename(parfr
, &fn
, NULL
, NULL
);
1635 if (!NT_SUCCESS(Status
)) {
1636 ERR("fileref_get_filename returned %08x\n", Status
);
1637 free_fileref(parfr
);
1638 ExFreePool(fn
.Buffer
);
1642 if (parfr
!= fcb
->Vcb
->root_fileref
) {
1643 fn
.Buffer
[(pathlen
/ sizeof(WCHAR
)) - 1] = '\\';
1644 fn
.Length
+= sizeof(WCHAR
);
1647 RtlCopyMemory(&fn
.Buffer
[pathlen
/ sizeof(WCHAR
)], hl
->name
.Buffer
, hl
->name
.Length
);
1648 fn
.Length
+= hl
->name
.Length
;
1650 FsRtlNotifyFilterReportChange(fcb
->Vcb
->NotifySync
, &fcb
->Vcb
->DirNotifyList
, (PSTRING
)&fn
, (USHORT
)pathlen
,
1651 (PSTRING
)stream
, NULL
, filter_match
, action
, NULL
, NULL
);
1653 ExFreePool(fn
.Buffer
);
1655 free_fileref(parfr
);
1661 ExReleaseResourceLite(&fcb
->Vcb
->fileref_lock
);
1664 void mark_fcb_dirty(_In_ fcb
* fcb
) {
1666 #ifdef DEBUG_FCB_REFCOUNTS
1671 #ifdef DEBUG_FCB_REFCOUNTS
1672 rc
= InterlockedIncrement(&fcb
->refcount
);
1673 WARN("fcb %p: refcount now %i\n", fcb
, rc
);
1675 InterlockedIncrement(&fcb
->refcount
);
1678 ExAcquireResourceExclusiveLite(&fcb
->Vcb
->dirty_fcbs_lock
, true);
1679 InsertTailList(&fcb
->Vcb
->dirty_fcbs
, &fcb
->list_entry_dirty
);
1680 ExReleaseResourceLite(&fcb
->Vcb
->dirty_fcbs_lock
);
1683 fcb
->Vcb
->need_write
= true;
1686 void mark_fileref_dirty(_In_ file_ref
* fileref
) {
1687 if (!fileref
->dirty
) {
1688 fileref
->dirty
= true;
1689 increase_fileref_refcount(fileref
);
1691 ExAcquireResourceExclusiveLite(&fileref
->fcb
->Vcb
->dirty_filerefs_lock
, true);
1692 InsertTailList(&fileref
->fcb
->Vcb
->dirty_filerefs
, &fileref
->list_entry_dirty
);
1693 ExReleaseResourceLite(&fileref
->fcb
->Vcb
->dirty_filerefs_lock
);
1696 fileref
->fcb
->Vcb
->need_write
= true;
1699 #ifdef DEBUG_FCB_REFCOUNTS
1700 void _free_fcb(_Inout_ fcb
* fcb
, _In_
const char* func
) {
1701 LONG rc
= InterlockedDecrement(&fcb
->refcount
);
1703 void free_fcb(_Inout_ fcb
* fcb
) {
1704 InterlockedDecrement(&fcb
->refcount
);
1707 #ifdef DEBUG_FCB_REFCOUNTS
1708 ERR("fcb %p (%s): refcount now %i (subvol %I64x, inode %I64x)\n", fcb
, func
, rc
, fcb
->subvol
? fcb
->subvol
->id
: 0, fcb
->inode
);
1712 void reap_fcb(fcb
* fcb
) {
1713 uint8_t c
= fcb
->hash
>> 24;
1715 if (fcb
->subvol
&& fcb
->subvol
->fcbs_ptrs
[c
] == &fcb
->list_entry
) {
1716 if (fcb
->list_entry
.Flink
!= &fcb
->subvol
->fcbs
&& (CONTAINING_RECORD(fcb
->list_entry
.Flink
, struct _fcb
, list_entry
)->hash
>> 24) == c
)
1717 fcb
->subvol
->fcbs_ptrs
[c
] = fcb
->list_entry
.Flink
;
1719 fcb
->subvol
->fcbs_ptrs
[c
] = NULL
;
1722 if (fcb
->list_entry
.Flink
)
1723 RemoveEntryList(&fcb
->list_entry
);
1725 if (fcb
->list_entry_all
.Flink
)
1726 RemoveEntryList(&fcb
->list_entry_all
);
1728 ExDeleteResourceLite(&fcb
->nonpaged
->resource
);
1729 ExDeleteResourceLite(&fcb
->nonpaged
->paging_resource
);
1730 ExDeleteResourceLite(&fcb
->nonpaged
->dir_children_lock
);
1732 ExFreeToNPagedLookasideList(&fcb
->Vcb
->fcb_np_lookaside
, fcb
->nonpaged
);
1735 ExFreePool(fcb
->sd
);
1737 if (fcb
->adsxattr
.Buffer
)
1738 ExFreePool(fcb
->adsxattr
.Buffer
);
1740 if (fcb
->reparse_xattr
.Buffer
)
1741 ExFreePool(fcb
->reparse_xattr
.Buffer
);
1743 if (fcb
->ea_xattr
.Buffer
)
1744 ExFreePool(fcb
->ea_xattr
.Buffer
);
1746 if (fcb
->adsdata
.Buffer
)
1747 ExFreePool(fcb
->adsdata
.Buffer
);
1749 if (fcb
->debug_desc
)
1750 ExFreePool(fcb
->debug_desc
);
1752 while (!IsListEmpty(&fcb
->extents
)) {
1753 LIST_ENTRY
* le
= RemoveHeadList(&fcb
->extents
);
1754 extent
* ext
= CONTAINING_RECORD(le
, extent
, list_entry
);
1757 ExFreePool(ext
->csum
);
1762 while (!IsListEmpty(&fcb
->hardlinks
)) {
1763 LIST_ENTRY
* le
= RemoveHeadList(&fcb
->hardlinks
);
1764 hardlink
* hl
= CONTAINING_RECORD(le
, hardlink
, list_entry
);
1766 if (hl
->name
.Buffer
)
1767 ExFreePool(hl
->name
.Buffer
);
1769 if (hl
->utf8
.Buffer
)
1770 ExFreePool(hl
->utf8
.Buffer
);
1775 while (!IsListEmpty(&fcb
->xattrs
)) {
1776 xattr
* xa
= CONTAINING_RECORD(RemoveHeadList(&fcb
->xattrs
), xattr
, list_entry
);
1781 while (!IsListEmpty(&fcb
->dir_children_index
)) {
1782 LIST_ENTRY
* le
= RemoveHeadList(&fcb
->dir_children_index
);
1783 dir_child
* dc
= CONTAINING_RECORD(le
, dir_child
, list_entry_index
);
1785 ExFreePool(dc
->utf8
.Buffer
);
1786 ExFreePool(dc
->name
.Buffer
);
1787 ExFreePool(dc
->name_uc
.Buffer
);
1792 ExFreePool(fcb
->hash_ptrs
);
1794 if (fcb
->hash_ptrs_uc
)
1795 ExFreePool(fcb
->hash_ptrs_uc
);
1797 FsRtlUninitializeFileLock(&fcb
->lock
);
1799 if (fcb
->pool_type
== NonPagedPool
)
1802 ExFreeToPagedLookasideList(&fcb
->Vcb
->fcb_lookaside
, fcb
);
1805 void reap_fcbs(device_extension
* Vcb
) {
1808 le
= Vcb
->all_fcbs
.Flink
;
1809 while (le
!= &Vcb
->all_fcbs
) {
1810 fcb
* fcb
= CONTAINING_RECORD(le
, struct _fcb
, list_entry_all
);
1811 LIST_ENTRY
* le2
= le
->Flink
;
1813 if (fcb
->refcount
== 0)
1820 void free_fileref(_Inout_ file_ref
* fr
) {
1823 rc
= InterlockedDecrement(&fr
->refcount
);
1828 #ifdef DEBUG_FCB_REFCOUNTS
1829 ERR("fileref %p: refcount now %i\n", fr
, rc
);
1834 ERR("fileref %p: refcount now %i\n", fr
, rc
);
1840 void reap_fileref(device_extension
* Vcb
, file_ref
* fr
) {
1841 // FIXME - do we need a file_ref lock?
1843 // FIXME - do delete if needed
1846 ExFreePool(fr
->debug_desc
);
1848 ExDeleteResourceLite(&fr
->nonpaged
->fileref_lock
);
1850 ExFreeToNPagedLookasideList(&Vcb
->fileref_np_lookaside
, fr
->nonpaged
);
1852 // FIXME - throw error if children not empty
1854 if (fr
->fcb
->fileref
== fr
)
1855 fr
->fcb
->fileref
= NULL
;
1859 fr
->dc
->size
= fr
->fcb
->adsdata
.Length
;
1861 fr
->dc
->fileref
= NULL
;
1864 if (fr
->list_entry
.Flink
)
1865 RemoveEntryList(&fr
->list_entry
);
1868 free_fileref(fr
->parent
);
1872 ExFreeToPagedLookasideList(&Vcb
->fileref_lookaside
, fr
);
1875 void reap_filerefs(device_extension
* Vcb
, file_ref
* fr
) {
1878 // FIXME - recursion is a bad idea in kernel mode
1880 le
= fr
->children
.Flink
;
1881 while (le
!= &fr
->children
) {
1882 file_ref
* c
= CONTAINING_RECORD(le
, file_ref
, list_entry
);
1883 LIST_ENTRY
* le2
= le
->Flink
;
1885 reap_filerefs(Vcb
, c
);
1890 if (fr
->refcount
== 0)
1891 reap_fileref(Vcb
, fr
);
1894 static NTSTATUS
close_file(_In_ PFILE_OBJECT FileObject
, _In_ PIRP Irp
) {
1897 file_ref
* fileref
= NULL
;
1902 TRACE("FileObject = %p\n", FileObject
);
1904 fcb
= FileObject
->FsContext
;
1906 TRACE("FCB was NULL, returning success\n");
1907 return STATUS_SUCCESS
;
1910 open_files
= InterlockedDecrement(&fcb
->Vcb
->open_files
);
1912 ccb
= FileObject
->FsContext2
;
1914 TRACE("close called for %S (fcb == %p)\n", file_desc(FileObject
), fcb
);
1916 // FIXME - make sure notification gets sent if file is being deleted
1919 if (ccb
->query_string
.Buffer
)
1920 RtlFreeUnicodeString(&ccb
->query_string
);
1922 if (ccb
->filename
.Buffer
)
1923 ExFreePool(ccb
->filename
.Buffer
);
1925 // FIXME - use refcounts for fileref
1926 fileref
= ccb
->fileref
;
1928 if (fcb
->Vcb
->running_sends
> 0) {
1929 bool send_cancelled
= false;
1931 ExAcquireResourceExclusiveLite(&fcb
->Vcb
->send_load_lock
, true);
1934 ccb
->send
->cancelling
= true;
1935 send_cancelled
= true;
1936 KeSetEvent(&ccb
->send
->cleared_event
, 0, false);
1939 ExReleaseResourceLite(&fcb
->Vcb
->send_load_lock
);
1941 if (send_cancelled
) {
1943 ExAcquireResourceExclusiveLite(&fcb
->Vcb
->send_load_lock
, true);
1944 ExReleaseResourceLite(&fcb
->Vcb
->send_load_lock
);
1952 CcUninitializeCacheMap(FileObject
, NULL
, NULL
);
1954 if (open_files
== 0 && fcb
->Vcb
->removing
) {
1956 return STATUS_SUCCESS
;
1959 if (!(fcb
->Vcb
->Vpb
->Flags
& VPB_MOUNTED
))
1960 return STATUS_SUCCESS
;
1963 free_fileref(fileref
);
1967 return STATUS_SUCCESS
;
1970 void uninit(_In_ device_extension
* Vcb
) {
1977 if (!Vcb
->removing
) {
1978 ExAcquireResourceExclusiveLite(&Vcb
->tree_lock
, true);
1979 Vcb
->removing
= true;
1980 ExReleaseResourceLite(&Vcb
->tree_lock
);
1983 IoAcquireVpbSpinLock(&irql
);
1984 Vcb
->Vpb
->Flags
&= ~VPB_MOUNTED
;
1985 Vcb
->Vpb
->Flags
|= VPB_DIRECT_WRITES_ALLOWED
;
1986 IoReleaseVpbSpinLock(irql
);
1988 RemoveEntryList(&Vcb
->list_entry
);
1990 if (Vcb
->balance
.thread
) {
1991 Vcb
->balance
.paused
= false;
1992 Vcb
->balance
.stopping
= true;
1993 KeSetEvent(&Vcb
->balance
.event
, 0, false);
1994 KeWaitForSingleObject(&Vcb
->balance
.finished
, Executive
, KernelMode
, false, NULL
);
1997 if (Vcb
->scrub
.thread
) {
1998 Vcb
->scrub
.paused
= false;
1999 Vcb
->scrub
.stopping
= true;
2000 KeSetEvent(&Vcb
->scrub
.event
, 0, false);
2001 KeWaitForSingleObject(&Vcb
->scrub
.finished
, Executive
, KernelMode
, false, NULL
);
2004 if (Vcb
->running_sends
!= 0) {
2005 bool send_cancelled
= false;
2007 ExAcquireResourceExclusiveLite(&Vcb
->send_load_lock
, true);
2009 le
= Vcb
->send_ops
.Flink
;
2010 while (le
!= &Vcb
->send_ops
) {
2011 send_info
* send
= CONTAINING_RECORD(le
, send_info
, list_entry
);
2013 if (!send
->cancelling
) {
2014 send
->cancelling
= true;
2015 send_cancelled
= true;
2017 KeSetEvent(&send
->cleared_event
, 0, false);
2023 ExReleaseResourceLite(&Vcb
->send_load_lock
);
2025 if (send_cancelled
) {
2026 while (Vcb
->running_sends
!= 0) {
2027 ExAcquireResourceExclusiveLite(&Vcb
->send_load_lock
, true);
2028 ExReleaseResourceLite(&Vcb
->send_load_lock
);
2033 Status
= registry_mark_volume_unmounted(&Vcb
->superblock
.uuid
);
2034 if (!NT_SUCCESS(Status
) && Status
!= STATUS_TOO_LATE
)
2035 WARN("registry_mark_volume_unmounted returned %08x\n", Status
);
2037 for (i
= 0; i
< Vcb
->calcthreads
.num_threads
; i
++) {
2038 Vcb
->calcthreads
.threads
[i
].quit
= true;
2041 KeSetEvent(&Vcb
->calcthreads
.event
, 0, false);
2043 for (i
= 0; i
< Vcb
->calcthreads
.num_threads
; i
++) {
2044 KeWaitForSingleObject(&Vcb
->calcthreads
.threads
[i
].finished
, Executive
, KernelMode
, false, NULL
);
2046 ZwClose(Vcb
->calcthreads
.threads
[i
].handle
);
2049 ExDeleteResourceLite(&Vcb
->calcthreads
.lock
);
2050 ExFreePool(Vcb
->calcthreads
.threads
);
2053 KeSetTimer(&Vcb
->flush_thread_timer
, time
, NULL
); // trigger the timer early
2054 KeWaitForSingleObject(&Vcb
->flush_thread_finished
, Executive
, KernelMode
, false, NULL
);
2056 reap_fcb(Vcb
->volume_fcb
);
2057 reap_fcb(Vcb
->dummy_fcb
);
2060 ObDereferenceObject(Vcb
->root_file
);
2062 le
= Vcb
->chunks
.Flink
;
2063 while (le
!= &Vcb
->chunks
) {
2064 chunk
* c
= CONTAINING_RECORD(le
, chunk
, list_entry
);
2074 while (!IsListEmpty(&Vcb
->roots
)) {
2075 root
* r
= CONTAINING_RECORD(RemoveHeadList(&Vcb
->roots
), root
, list_entry
);
2077 ExDeleteResourceLite(&r
->nonpaged
->load_tree_lock
);
2078 ExFreePool(r
->nonpaged
);
2082 while (!IsListEmpty(&Vcb
->chunks
)) {
2083 chunk
* c
= CONTAINING_RECORD(RemoveHeadList(&Vcb
->chunks
), chunk
, list_entry
);
2085 while (!IsListEmpty(&c
->space
)) {
2086 LIST_ENTRY
* le2
= RemoveHeadList(&c
->space
);
2087 space
* s
= CONTAINING_RECORD(le2
, space
, list_entry
);
2092 while (!IsListEmpty(&c
->deleting
)) {
2093 LIST_ENTRY
* le2
= RemoveHeadList(&c
->deleting
);
2094 space
* s
= CONTAINING_RECORD(le2
, space
, list_entry
);
2100 ExFreePool(c
->devices
);
2105 ExDeleteResourceLite(&c
->range_locks_lock
);
2106 ExDeleteResourceLite(&c
->partial_stripes_lock
);
2107 ExDeleteResourceLite(&c
->lock
);
2108 ExDeleteResourceLite(&c
->changed_extents_lock
);
2110 ExFreePool(c
->chunk_item
);
2114 // FIXME - free any open fcbs?
2116 while (!IsListEmpty(&Vcb
->devices
)) {
2117 device
* dev
= CONTAINING_RECORD(RemoveHeadList(&Vcb
->devices
), device
, list_entry
);
2119 while (!IsListEmpty(&dev
->space
)) {
2120 LIST_ENTRY
* le2
= RemoveHeadList(&dev
->space
);
2121 space
* s
= CONTAINING_RECORD(le2
, space
, list_entry
);
2129 ExAcquireResourceExclusiveLite(&Vcb
->scrub
.stats_lock
, true);
2130 while (!IsListEmpty(&Vcb
->scrub
.errors
)) {
2131 scrub_error
* err
= CONTAINING_RECORD(RemoveHeadList(&Vcb
->scrub
.errors
), scrub_error
, list_entry
);
2135 ExReleaseResourceLite(&Vcb
->scrub
.stats_lock
);
2137 ExDeleteResourceLite(&Vcb
->fcb_lock
);
2138 ExDeleteResourceLite(&Vcb
->fileref_lock
);
2139 ExDeleteResourceLite(&Vcb
->load_lock
);
2140 ExDeleteResourceLite(&Vcb
->tree_lock
);
2141 ExDeleteResourceLite(&Vcb
->chunk_lock
);
2142 ExDeleteResourceLite(&Vcb
->dirty_fcbs_lock
);
2143 ExDeleteResourceLite(&Vcb
->dirty_filerefs_lock
);
2144 ExDeleteResourceLite(&Vcb
->dirty_subvols_lock
);
2145 ExDeleteResourceLite(&Vcb
->scrub
.stats_lock
);
2146 ExDeleteResourceLite(&Vcb
->send_load_lock
);
2148 ExDeletePagedLookasideList(&Vcb
->tree_data_lookaside
);
2149 ExDeletePagedLookasideList(&Vcb
->traverse_ptr_lookaside
);
2150 ExDeletePagedLookasideList(&Vcb
->batch_item_lookaside
);
2151 ExDeletePagedLookasideList(&Vcb
->fileref_lookaside
);
2152 ExDeletePagedLookasideList(&Vcb
->fcb_lookaside
);
2153 ExDeletePagedLookasideList(&Vcb
->name_bit_lookaside
);
2154 ExDeleteNPagedLookasideList(&Vcb
->range_lock_lookaside
);
2155 ExDeleteNPagedLookasideList(&Vcb
->fileref_np_lookaside
);
2156 ExDeleteNPagedLookasideList(&Vcb
->fcb_np_lookaside
);
2158 ZwClose(Vcb
->flush_thread_handle
);
2161 static NTSTATUS
delete_fileref_fcb(_In_ file_ref
* fileref
, _In_opt_ PFILE_OBJECT FileObject
, _In_opt_ PIRP Irp
, _In_ LIST_ENTRY
* rollback
) {
2167 if (fileref
->fcb
->type
!= BTRFS_TYPE_DIRECTORY
&& fileref
->fcb
->inode_item
.st_size
> 0) {
2168 Status
= excise_extents(fileref
->fcb
->Vcb
, fileref
->fcb
, 0, sector_align(fileref
->fcb
->inode_item
.st_size
, fileref
->fcb
->Vcb
->superblock
.sector_size
), Irp
, rollback
);
2169 if (!NT_SUCCESS(Status
)) {
2170 ERR("excise_extents returned %08x\n", Status
);
2175 fileref
->fcb
->Header
.AllocationSize
.QuadPart
= 0;
2176 fileref
->fcb
->Header
.FileSize
.QuadPart
= 0;
2177 fileref
->fcb
->Header
.ValidDataLength
.QuadPart
= 0;
2182 ccfs
.AllocationSize
= fileref
->fcb
->Header
.AllocationSize
;
2183 ccfs
.FileSize
= fileref
->fcb
->Header
.FileSize
;
2184 ccfs
.ValidDataLength
= fileref
->fcb
->Header
.ValidDataLength
;
2186 Status
= STATUS_SUCCESS
;
2189 CcSetFileSizes(FileObject
, &ccfs
);
2190 } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER
) {
2191 Status
= _SEH2_GetExceptionCode();
2194 if (!NT_SUCCESS(Status
)) {
2195 ERR("CcSetFileSizes threw exception %08x\n", Status
);
2200 fileref
->fcb
->deleted
= true;
2202 le
= fileref
->children
.Flink
;
2203 while (le
!= &fileref
->children
) {
2204 file_ref
* fr2
= CONTAINING_RECORD(le
, file_ref
, list_entry
);
2206 if (fr2
->fcb
->ads
) {
2207 fr2
->fcb
->deleted
= true;
2208 mark_fcb_dirty(fr2
->fcb
);
2214 return STATUS_SUCCESS
;
2217 NTSTATUS
delete_fileref(_In_ file_ref
* fileref
, _In_opt_ PFILE_OBJECT FileObject
, _In_
bool make_orphan
, _In_opt_ PIRP Irp
, _In_ LIST_ENTRY
* rollback
) {
2218 LARGE_INTEGER newlength
, time
;
2223 KeQuerySystemTime(&time
);
2224 win_time_to_unix(time
, &now
);
2226 ExAcquireResourceExclusiveLite(fileref
->fcb
->Header
.Resource
, true);
2228 if (fileref
->deleted
) {
2229 ExReleaseResourceLite(fileref
->fcb
->Header
.Resource
);
2230 return STATUS_SUCCESS
;
2233 if (fileref
->fcb
->subvol
->send_ops
> 0) {
2234 ExReleaseResourceLite(fileref
->fcb
->Header
.Resource
);
2235 return STATUS_ACCESS_DENIED
;
2238 fileref
->deleted
= true;
2239 mark_fileref_dirty(fileref
);
2241 // delete INODE_ITEM (0x1)
2243 TRACE("nlink = %u\n", fileref
->fcb
->inode_item
.st_nlink
);
2245 if (!fileref
->fcb
->ads
) {
2246 if (fileref
->parent
->fcb
->subvol
== fileref
->fcb
->subvol
) {
2249 mark_fcb_dirty(fileref
->fcb
);
2251 fileref
->fcb
->inode_item_changed
= true;
2253 if (fileref
->fcb
->inode_item
.st_nlink
> 1 || make_orphan
) {
2254 fileref
->fcb
->inode_item
.st_nlink
--;
2255 fileref
->fcb
->inode_item
.transid
= fileref
->fcb
->Vcb
->superblock
.generation
;
2256 fileref
->fcb
->inode_item
.sequence
++;
2257 fileref
->fcb
->inode_item
.st_ctime
= now
;
2259 Status
= delete_fileref_fcb(fileref
, FileObject
, Irp
, rollback
);
2260 if (!NT_SUCCESS(Status
)) {
2261 ERR("delete_fileref_fcb returned %08x\n", Status
);
2262 ExReleaseResourceLite(fileref
->fcb
->Header
.Resource
);
2268 le
= fileref
->fcb
->hardlinks
.Flink
;
2269 while (le
!= &fileref
->fcb
->hardlinks
) {
2270 hardlink
* hl
= CONTAINING_RECORD(le
, hardlink
, list_entry
);
2272 if (hl
->parent
== fileref
->parent
->fcb
->inode
&& hl
->index
== fileref
->dc
->index
) {
2273 RemoveEntryList(&hl
->list_entry
);
2275 if (hl
->name
.Buffer
)
2276 ExFreePool(hl
->name
.Buffer
);
2278 if (hl
->utf8
.Buffer
)
2279 ExFreePool(hl
->utf8
.Buffer
);
2288 } else if (fileref
->fcb
->subvol
->parent
== fileref
->parent
->fcb
->subvol
->id
) { // valid subvolume
2289 if (fileref
->fcb
->subvol
->root_item
.num_references
> 1) {
2290 fileref
->fcb
->subvol
->root_item
.num_references
--;
2292 mark_fcb_dirty(fileref
->fcb
); // so ROOT_ITEM gets updated
2296 // FIXME - we need a lock here
2298 RemoveEntryList(&fileref
->fcb
->subvol
->list_entry
);
2300 InsertTailList(&fileref
->fcb
->Vcb
->drop_roots
, &fileref
->fcb
->subvol
->list_entry
);
2302 le
= fileref
->children
.Flink
;
2303 while (le
!= &fileref
->children
) {
2304 file_ref
* fr2
= CONTAINING_RECORD(le
, file_ref
, list_entry
);
2306 if (fr2
->fcb
->ads
) {
2307 fr2
->fcb
->deleted
= true;
2308 mark_fcb_dirty(fr2
->fcb
);
2316 fileref
->fcb
->deleted
= true;
2317 mark_fcb_dirty(fileref
->fcb
);
2320 // remove dir_child from parent
2323 TRACE("delete file %.*S\n", fileref
->dc
->name
.Length
/ sizeof(WCHAR
), fileref
->dc
->name
.Buffer
);
2325 ExAcquireResourceExclusiveLite(&fileref
->parent
->fcb
->nonpaged
->dir_children_lock
, true);
2326 RemoveEntryList(&fileref
->dc
->list_entry_index
);
2328 if (!fileref
->fcb
->ads
)
2329 remove_dir_child_from_hash_lists(fileref
->parent
->fcb
, fileref
->dc
);
2331 ExReleaseResourceLite(&fileref
->parent
->fcb
->nonpaged
->dir_children_lock
);
2333 if (!fileref
->oldutf8
.Buffer
)
2334 fileref
->oldutf8
= fileref
->dc
->utf8
;
2336 ExFreePool(fileref
->dc
->utf8
.Buffer
);
2338 utf8len
= fileref
->dc
->utf8
.Length
;
2340 fileref
->oldindex
= fileref
->dc
->index
;
2342 ExFreePool(fileref
->dc
->name
.Buffer
);
2343 ExFreePool(fileref
->dc
->name_uc
.Buffer
);
2344 ExFreePool(fileref
->dc
);
2349 // update INODE_ITEM of parent
2351 ExAcquireResourceExclusiveLite(fileref
->parent
->fcb
->Header
.Resource
, true);
2353 fileref
->parent
->fcb
->inode_item
.transid
= fileref
->fcb
->Vcb
->superblock
.generation
;
2354 fileref
->parent
->fcb
->inode_item
.sequence
++;
2355 fileref
->parent
->fcb
->inode_item
.st_ctime
= now
;
2357 if (!fileref
->fcb
->ads
) {
2358 TRACE("fileref->parent->fcb->inode_item.st_size (inode %I64x) was %I64x\n", fileref
->parent
->fcb
->inode
, fileref
->parent
->fcb
->inode_item
.st_size
);
2359 fileref
->parent
->fcb
->inode_item
.st_size
-= utf8len
* 2;
2360 TRACE("fileref->parent->fcb->inode_item.st_size (inode %I64x) now %I64x\n", fileref
->parent
->fcb
->inode
, fileref
->parent
->fcb
->inode_item
.st_size
);
2361 fileref
->parent
->fcb
->inode_item
.st_mtime
= now
;
2364 fileref
->parent
->fcb
->inode_item_changed
= true;
2365 ExReleaseResourceLite(fileref
->parent
->fcb
->Header
.Resource
);
2367 if (!fileref
->fcb
->ads
&& fileref
->parent
->dc
)
2368 send_notification_fcb(fileref
->parent
, FILE_NOTIFY_CHANGE_LAST_WRITE
, FILE_ACTION_MODIFIED
, NULL
);
2370 mark_fcb_dirty(fileref
->parent
->fcb
);
2372 fileref
->fcb
->subvol
->root_item
.ctransid
= fileref
->fcb
->Vcb
->superblock
.generation
;
2373 fileref
->fcb
->subvol
->root_item
.ctime
= now
;
2375 newlength
.QuadPart
= 0;
2377 if (FileObject
&& !CcUninitializeCacheMap(FileObject
, &newlength
, NULL
))
2378 TRACE("CcUninitializeCacheMap failed\n");
2380 ExReleaseResourceLite(fileref
->fcb
->Header
.Resource
);
2382 return STATUS_SUCCESS
;
2385 _Dispatch_type_(IRP_MJ_CLEANUP
)
2386 _Function_class_(DRIVER_DISPATCH
)
2387 static NTSTATUS __stdcall
drv_cleanup(_In_ PDEVICE_OBJECT DeviceObject
, _In_ PIRP Irp
) {
2389 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
2390 PFILE_OBJECT FileObject
= IrpSp
->FileObject
;
2391 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
2392 fcb
* fcb
= FileObject
->FsContext
;
2395 FsRtlEnterFileSystem();
2399 top_level
= is_top_level(Irp
);
2401 if (Vcb
&& Vcb
->type
== VCB_TYPE_VOLUME
) {
2402 Status
= vol_cleanup(DeviceObject
, Irp
);
2404 } else if (DeviceObject
== master_devobj
) {
2405 TRACE("closing file system\n");
2406 Status
= STATUS_SUCCESS
;
2408 } else if (!Vcb
|| Vcb
->type
!= VCB_TYPE_FS
) {
2409 Status
= STATUS_INVALID_PARAMETER
;
2413 if (FileObject
->Flags
& FO_CLEANUP_COMPLETE
) {
2414 TRACE("FileObject %p already cleaned up\n", FileObject
);
2415 Status
= STATUS_SUCCESS
;
2420 ERR("fcb was NULL\n");
2421 Status
= STATUS_INVALID_PARAMETER
;
2425 // We have to use the pointer to Vcb stored in the fcb, as we can receive cleanup
2426 // messages belonging to other devices.
2428 if (FileObject
&& FileObject
->FsContext
) {
2434 ccb
= FileObject
->FsContext2
;
2435 fileref
= ccb
? ccb
->fileref
: NULL
;
2437 TRACE("cleanup called for FileObject %p\n", FileObject
);
2438 TRACE("fileref %p (%S), refcount = %u, open_count = %u\n", fileref
, file_desc(FileObject
), fileref
? fileref
->refcount
: 0, fileref
? fileref
->open_count
: 0);
2440 ExAcquireResourceSharedLite(&fcb
->Vcb
->tree_lock
, true);
2442 ExAcquireResourceExclusiveLite(fcb
->Header
.Resource
, true);
2444 IoRemoveShareAccess(FileObject
, &fcb
->share_access
);
2446 FsRtlFastUnlockAll(&fcb
->lock
, FileObject
, IoGetRequestorProcess(Irp
), NULL
);
2449 FsRtlNotifyCleanup(fcb
->Vcb
->NotifySync
, &fcb
->Vcb
->DirNotifyList
, ccb
);
2452 oc
= InterlockedDecrement(&fileref
->open_count
);
2453 #ifdef DEBUG_FCB_REFCOUNTS
2454 ERR("fileref %p: open_count now %i\n", fileref
, oc
);
2458 if (ccb
&& ccb
->options
& FILE_DELETE_ON_CLOSE
&& fileref
)
2459 fileref
->delete_on_close
= true;
2461 if (fileref
&& fileref
->delete_on_close
&& fcb
->type
== BTRFS_TYPE_DIRECTORY
&& fcb
->inode_item
.st_size
> 0 && fcb
!= fcb
->Vcb
->dummy_fcb
)
2462 fileref
->delete_on_close
= false;
2464 if (fcb
->Vcb
->locked
&& fcb
->Vcb
->locked_fileobj
== FileObject
) {
2465 TRACE("unlocking volume\n");
2466 do_unlock_volume(fcb
->Vcb
);
2467 FsRtlNotifyVolumeEvent(FileObject
, FSRTL_VOLUME_UNLOCK
);
2470 if (ccb
&& ccb
->reserving
) {
2471 fcb
->subvol
->reserved
= NULL
;
2472 ccb
->reserving
= false;
2473 // FIXME - flush all of subvol's fcbs
2476 if (fileref
&& (oc
== 0 || (fileref
->delete_on_close
&& fileref
->posix_delete
))) {
2477 if (!fcb
->Vcb
->removing
) {
2478 if (oc
== 0 && fileref
->fcb
->inode_item
.st_nlink
== 0 && fileref
!= fcb
->Vcb
->root_fileref
&& fcb
!= fcb
->Vcb
->volume_fcb
) { // last handle closed on POSIX-deleted file
2479 LIST_ENTRY rollback
;
2481 InitializeListHead(&rollback
);
2483 Status
= delete_fileref_fcb(fileref
, FileObject
, Irp
, &rollback
);
2484 if (!NT_SUCCESS(Status
)) {
2485 ERR("delete_fileref_fcb returned %08x\n", Status
);
2486 do_rollback(fcb
->Vcb
, &rollback
);
2487 ExReleaseResourceLite(fileref
->fcb
->Header
.Resource
);
2488 ExReleaseResourceLite(&fcb
->Vcb
->tree_lock
);
2492 clear_rollback(&rollback
);
2494 mark_fcb_dirty(fileref
->fcb
);
2495 } else if (fileref
->delete_on_close
&& fileref
!= fcb
->Vcb
->root_fileref
&& fcb
!= fcb
->Vcb
->volume_fcb
) {
2496 LIST_ENTRY rollback
;
2498 InitializeListHead(&rollback
);
2500 if (!fileref
->fcb
->ads
|| fileref
->dc
) {
2501 if (fileref
->fcb
->ads
) {
2502 send_notification_fileref(fileref
->parent
, fcb
->type
== BTRFS_TYPE_DIRECTORY
? FILE_NOTIFY_CHANGE_DIR_NAME
: FILE_NOTIFY_CHANGE_FILE_NAME
,
2503 FILE_ACTION_REMOVED
, &fileref
->dc
->name
);
2505 send_notification_fileref(fileref
, fcb
->type
== BTRFS_TYPE_DIRECTORY
? FILE_NOTIFY_CHANGE_DIR_NAME
: FILE_NOTIFY_CHANGE_FILE_NAME
, FILE_ACTION_REMOVED
, NULL
);
2508 ExReleaseResourceLite(fcb
->Header
.Resource
);
2511 // fileref_lock needs to be acquired before fcb->Header.Resource
2512 ExAcquireResourceExclusiveLite(&fcb
->Vcb
->fileref_lock
, true);
2514 Status
= delete_fileref(fileref
, FileObject
, oc
> 0 && fileref
->posix_delete
, Irp
, &rollback
);
2515 if (!NT_SUCCESS(Status
)) {
2516 ERR("delete_fileref returned %08x\n", Status
);
2517 do_rollback(fcb
->Vcb
, &rollback
);
2518 ExReleaseResourceLite(&fcb
->Vcb
->fileref_lock
);
2519 ExReleaseResourceLite(&fcb
->Vcb
->tree_lock
);
2523 ExReleaseResourceLite(&fcb
->Vcb
->fileref_lock
);
2525 clear_rollback(&rollback
);
2526 } else if (FileObject
->Flags
& FO_CACHE_SUPPORTED
&& FileObject
->SectionObjectPointer
->DataSectionObject
) {
2527 IO_STATUS_BLOCK iosb
;
2528 CcFlushCache(FileObject
->SectionObjectPointer
, NULL
, 0, &iosb
);
2530 if (!NT_SUCCESS(iosb
.Status
)) {
2531 ERR("CcFlushCache returned %08x\n", iosb
.Status
);
2534 if (!ExIsResourceAcquiredSharedLite(fcb
->Header
.PagingIoResource
)) {
2535 ExAcquireResourceExclusiveLite(fcb
->Header
.PagingIoResource
, true);
2536 ExReleaseResourceLite(fcb
->Header
.PagingIoResource
);
2539 CcPurgeCacheSection(FileObject
->SectionObjectPointer
, NULL
, 0, false);
2541 TRACE("flushed cache on close (FileObject = %p, fcb = %p, AllocationSize = %I64x, FileSize = %I64x, ValidDataLength = %I64x)\n",
2542 FileObject
, fcb
, fcb
->Header
.AllocationSize
.QuadPart
, fcb
->Header
.FileSize
.QuadPart
, fcb
->Header
.ValidDataLength
.QuadPart
);
2546 if (fcb
->Vcb
&& fcb
!= fcb
->Vcb
->volume_fcb
)
2547 CcUninitializeCacheMap(FileObject
, NULL
, NULL
);
2551 ExReleaseResourceLite(fcb
->Header
.Resource
);
2553 ExReleaseResourceLite(&fcb
->Vcb
->tree_lock
);
2555 FileObject
->Flags
|= FO_CLEANUP_COMPLETE
;
2558 Status
= STATUS_SUCCESS
;
2561 TRACE("returning %08x\n", Status
);
2563 Irp
->IoStatus
.Status
= Status
;
2564 Irp
->IoStatus
.Information
= 0;
2566 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
2569 IoSetTopLevelIrp(NULL
);
2571 FsRtlExitFileSystem();
2577 bool get_file_attributes_from_xattr(_In_reads_bytes_(len
) char* val
, _In_
uint16_t len
, _Out_ ULONG
* atts
) {
2578 if (len
> 2 && val
[0] == '0' && val
[1] == 'x') {
2582 for (i
= 2; i
< len
; i
++) {
2585 if (val
[i
] >= '0' && val
[i
] <= '9')
2586 dosnum
|= val
[i
] - '0';
2587 else if (val
[i
] >= 'a' && val
[i
] <= 'f')
2588 dosnum
|= val
[i
] + 10 - 'a';
2589 else if (val
[i
] >= 'A' && val
[i
] <= 'F')
2590 dosnum
|= val
[i
] + 10 - 'a';
2593 TRACE("DOSATTRIB: %08x\n", dosnum
);
2603 ULONG
get_file_attributes(_In_
_Requires_lock_held_(_Curr_
->tree_lock
) device_extension
* Vcb
, _In_ root
* r
, _In_
uint64_t inode
,
2604 _In_
uint8_t type
, _In_
bool dotfile
, _In_
bool ignore_xa
, _In_opt_ PIRP Irp
) {
2609 if (!ignore_xa
&& get_xattr(Vcb
, r
, inode
, EA_DOSATTRIB
, EA_DOSATTRIB_HASH
, (uint8_t**)&eaval
, &ealen
, Irp
)) {
2612 if (get_file_attributes_from_xattr(eaval
, ealen
, &dosnum
)) {
2615 if (type
== BTRFS_TYPE_DIRECTORY
)
2616 dosnum
|= FILE_ATTRIBUTE_DIRECTORY
;
2617 else if (type
== BTRFS_TYPE_SYMLINK
)
2618 dosnum
|= FILE_ATTRIBUTE_REPARSE_POINT
;
2620 if (type
!= BTRFS_TYPE_DIRECTORY
)
2621 dosnum
&= ~FILE_ATTRIBUTE_DIRECTORY
;
2623 if (inode
== SUBVOL_ROOT_INODE
) {
2624 if (r
->root_item
.flags
& BTRFS_SUBVOL_READONLY
)
2625 dosnum
|= FILE_ATTRIBUTE_READONLY
;
2627 dosnum
&= ~FILE_ATTRIBUTE_READONLY
;
2637 case BTRFS_TYPE_DIRECTORY
:
2638 att
= FILE_ATTRIBUTE_DIRECTORY
;
2641 case BTRFS_TYPE_SYMLINK
:
2642 att
= FILE_ATTRIBUTE_REPARSE_POINT
;
2651 att
|= FILE_ATTRIBUTE_HIDDEN
;
2654 att
|= FILE_ATTRIBUTE_ARCHIVE
;
2656 if (inode
== SUBVOL_ROOT_INODE
) {
2657 if (r
->root_item
.flags
& BTRFS_SUBVOL_READONLY
)
2658 att
|= FILE_ATTRIBUTE_READONLY
;
2660 att
&= ~FILE_ATTRIBUTE_READONLY
;
2663 // FIXME - get READONLY from ii->st_mode
2664 // FIXME - return SYSTEM for block/char devices?
2667 att
= FILE_ATTRIBUTE_NORMAL
;
2672 NTSTATUS
sync_read_phys(_In_ PDEVICE_OBJECT DeviceObject
, _In_ PFILE_OBJECT FileObject
, _In_
uint64_t StartingOffset
, _In_ ULONG Length
,
2673 _Out_writes_bytes_(Length
) PUCHAR Buffer
, _In_
bool override
) {
2674 IO_STATUS_BLOCK IoStatus
;
2675 LARGE_INTEGER Offset
;
2677 PIO_STACK_LOCATION IrpSp
;
2679 read_context context
;
2683 RtlZeroMemory(&context
, sizeof(read_context
));
2684 KeInitializeEvent(&context
.Event
, NotificationEvent
, false);
2686 Offset
.QuadPart
= (LONGLONG
)StartingOffset
;
2688 Irp
= IoAllocateIrp(DeviceObject
->StackSize
, false);
2691 ERR("IoAllocateIrp failed\n");
2692 return STATUS_INSUFFICIENT_RESOURCES
;
2695 Irp
->Flags
|= IRP_NOCACHE
;
2696 IrpSp
= IoGetNextIrpStackLocation(Irp
);
2697 IrpSp
->MajorFunction
= IRP_MJ_READ
;
2698 IrpSp
->FileObject
= FileObject
;
2701 IrpSp
->Flags
|= SL_OVERRIDE_VERIFY_VOLUME
;
2703 if (DeviceObject
->Flags
& DO_BUFFERED_IO
) {
2704 Irp
->AssociatedIrp
.SystemBuffer
= ExAllocatePoolWithTag(NonPagedPool
, Length
, ALLOC_TAG
);
2705 if (!Irp
->AssociatedIrp
.SystemBuffer
) {
2706 ERR("out of memory\n");
2707 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2711 Irp
->Flags
|= IRP_BUFFERED_IO
| IRP_DEALLOCATE_BUFFER
| IRP_INPUT_OPERATION
;
2713 Irp
->UserBuffer
= Buffer
;
2714 } else if (DeviceObject
->Flags
& DO_DIRECT_IO
) {
2715 Irp
->MdlAddress
= IoAllocateMdl(Buffer
, Length
, false, false, NULL
);
2716 if (!Irp
->MdlAddress
) {
2717 ERR("IoAllocateMdl failed\n");
2718 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2722 Status
= STATUS_SUCCESS
;
2725 MmProbeAndLockPages(Irp
->MdlAddress
, KernelMode
, IoWriteAccess
);
2726 } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER
) {
2727 Status
= _SEH2_GetExceptionCode();
2730 if (!NT_SUCCESS(Status
)) {
2731 ERR("MmProbeAndLockPages threw exception %08x\n", Status
);
2732 IoFreeMdl(Irp
->MdlAddress
);
2736 Irp
->UserBuffer
= Buffer
;
2738 IrpSp
->Parameters
.Read
.Length
= Length
;
2739 IrpSp
->Parameters
.Read
.ByteOffset
= Offset
;
2741 Irp
->UserIosb
= &IoStatus
;
2743 Irp
->UserEvent
= &context
.Event
;
2745 IoSetCompletionRoutine(Irp
, read_completion
, &context
, true, true, true);
2747 Status
= IoCallDriver(DeviceObject
, Irp
);
2749 if (Status
== STATUS_PENDING
) {
2750 KeWaitForSingleObject(&context
.Event
, Executive
, KernelMode
, false, NULL
);
2751 Status
= context
.iosb
.Status
;
2754 if (DeviceObject
->Flags
& DO_DIRECT_IO
) {
2755 MmUnlockPages(Irp
->MdlAddress
);
2756 IoFreeMdl(Irp
->MdlAddress
);
2765 static NTSTATUS
read_superblock(_In_ device_extension
* Vcb
, _In_ PDEVICE_OBJECT device
, _In_ PFILE_OBJECT fileobj
, _In_
uint64_t length
) {
2769 uint8_t valid_superblocks
;
2771 to_read
= device
->SectorSize
== 0 ? sizeof(superblock
) : (ULONG
)sector_align(sizeof(superblock
), device
->SectorSize
);
2773 sb
= ExAllocatePoolWithTag(NonPagedPool
, to_read
, ALLOC_TAG
);
2775 ERR("out of memory\n");
2776 return STATUS_INSUFFICIENT_RESOURCES
;
2779 if (superblock_addrs
[0] + to_read
> length
) {
2780 WARN("device was too short to have any superblock\n");
2782 return STATUS_UNRECOGNIZED_VOLUME
;
2786 valid_superblocks
= 0;
2788 while (superblock_addrs
[i
] > 0) {
2791 if (i
> 0 && superblock_addrs
[i
] + to_read
> length
)
2794 Status
= sync_read_phys(device
, fileobj
, superblock_addrs
[i
], to_read
, (PUCHAR
)sb
, false);
2795 if (!NT_SUCCESS(Status
)) {
2796 ERR("Failed to read superblock %u: %08x\n", i
, Status
);
2801 if (sb
->magic
!= BTRFS_MAGIC
) {
2803 TRACE("not a BTRFS volume\n");
2805 return STATUS_UNRECOGNIZED_VOLUME
;
2808 TRACE("got superblock %u!\n", i
);
2810 crc32
= ~calc_crc32c(0xffffffff, (uint8_t*)&sb
->uuid
, (ULONG
)sizeof(superblock
) - sizeof(sb
->checksum
));
2812 if (crc32
!= *((uint32_t*)sb
->checksum
))
2813 WARN("crc32 was %08x, expected %08x\n", crc32
, *((uint32_t*)sb
->checksum
));
2814 else if (sb
->sector_size
== 0)
2815 WARN("superblock sector size was 0\n");
2816 else if (sb
->node_size
< sizeof(tree_header
) + sizeof(internal_node
) || sb
->node_size
> 0x10000)
2817 WARN("invalid node size %x\n", sb
->node_size
);
2818 else if ((sb
->node_size
% sb
->sector_size
) != 0)
2819 WARN("node size %x was not a multiple of sector_size %x\n", sb
->node_size
, sb
->sector_size
);
2820 else if (valid_superblocks
== 0 || sb
->generation
> Vcb
->superblock
.generation
) {
2821 RtlCopyMemory(&Vcb
->superblock
, sb
, sizeof(superblock
));
2822 valid_superblocks
++;
2831 if (valid_superblocks
== 0) {
2832 ERR("could not find any valid superblocks\n");
2833 return STATUS_INTERNAL_ERROR
;
2836 TRACE("label is %s\n", Vcb
->superblock
.label
);
2838 return STATUS_SUCCESS
;
2841 NTSTATUS
dev_ioctl(_In_ PDEVICE_OBJECT DeviceObject
, _In_ ULONG ControlCode
, _In_reads_bytes_opt_(InputBufferSize
) PVOID InputBuffer
, _In_ ULONG InputBufferSize
,
2842 _Out_writes_bytes_opt_(OutputBufferSize
) PVOID OutputBuffer
, _In_ ULONG OutputBufferSize
, _In_
bool Override
, _Out_opt_ IO_STATUS_BLOCK
* iosb
) {
2846 PIO_STACK_LOCATION IrpSp
;
2847 IO_STATUS_BLOCK IoStatus
;
2849 KeInitializeEvent(&Event
, NotificationEvent
, false);
2851 Irp
= IoBuildDeviceIoControlRequest(ControlCode
,
2861 if (!Irp
) return STATUS_INSUFFICIENT_RESOURCES
;
2864 IrpSp
= IoGetNextIrpStackLocation(Irp
);
2865 IrpSp
->Flags
|= SL_OVERRIDE_VERIFY_VOLUME
;
2868 Status
= IoCallDriver(DeviceObject
, Irp
);
2870 if (Status
== STATUS_PENDING
) {
2871 KeWaitForSingleObject(&Event
, Executive
, KernelMode
, false, NULL
);
2872 Status
= IoStatus
.Status
;
2881 _Requires_exclusive_lock_held_(Vcb
->tree_lock
)
2882 static NTSTATUS
add_root(_Inout_ device_extension
* Vcb
, _In_
uint64_t id
, _In_
uint64_t addr
,
2883 _In_
uint64_t generation
, _In_opt_ traverse_ptr
* tp
) {
2884 root
* r
= ExAllocatePoolWithTag(PagedPool
, sizeof(root
), ALLOC_TAG
);
2886 ERR("out of memory\n");
2887 return STATUS_INSUFFICIENT_RESOURCES
;
2892 r
->received
= false;
2894 r
->treeholder
.address
= addr
;
2895 r
->treeholder
.tree
= NULL
;
2896 r
->treeholder
.generation
= generation
;
2899 r
->fcbs_version
= 0;
2900 r
->checked_for_orphans
= false;
2901 InitializeListHead(&r
->fcbs
);
2902 RtlZeroMemory(r
->fcbs_ptrs
, sizeof(LIST_ENTRY
*) * 256);
2904 r
->nonpaged
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(root_nonpaged
), ALLOC_TAG
);
2906 ERR("out of memory\n");
2908 return STATUS_INSUFFICIENT_RESOURCES
;
2911 ExInitializeResourceLite(&r
->nonpaged
->load_tree_lock
);
2916 RtlCopyMemory(&r
->root_item
, tp
->item
->data
, min(sizeof(ROOT_ITEM
), tp
->item
->size
));
2917 if (tp
->item
->size
< sizeof(ROOT_ITEM
))
2918 RtlZeroMemory(((uint8_t*)&r
->root_item
) + tp
->item
->size
, sizeof(ROOT_ITEM
) - tp
->item
->size
);
2920 RtlZeroMemory(&r
->root_item
, sizeof(ROOT_ITEM
));
2922 if (!Vcb
->readonly
&& (r
->id
== BTRFS_ROOT_ROOT
|| r
->id
== BTRFS_ROOT_FSTREE
|| (r
->id
>= 0x100 && !(r
->id
& 0xf000000000000000)))) { // FS tree root
2923 // FIXME - don't call this if subvol is readonly (though we will have to if we ever toggle this flag)
2924 get_last_inode(Vcb
, r
, NULL
);
2926 if (r
->id
== BTRFS_ROOT_ROOT
&& r
->lastinode
< 0x100)
2927 r
->lastinode
= 0x100;
2930 InsertTailList(&Vcb
->roots
, &r
->list_entry
);
2933 case BTRFS_ROOT_ROOT
:
2937 case BTRFS_ROOT_EXTENT
:
2938 Vcb
->extent_root
= r
;
2941 case BTRFS_ROOT_CHUNK
:
2942 Vcb
->chunk_root
= r
;
2945 case BTRFS_ROOT_DEVTREE
:
2949 case BTRFS_ROOT_CHECKSUM
:
2950 Vcb
->checksum_root
= r
;
2953 case BTRFS_ROOT_UUID
:
2957 case BTRFS_ROOT_FREE_SPACE
:
2958 Vcb
->space_root
= r
;
2961 case BTRFS_ROOT_DATA_RELOC
:
2962 Vcb
->data_reloc_root
= r
;
2966 return STATUS_SUCCESS
;
2969 static NTSTATUS
look_for_roots(_Requires_exclusive_lock_held_(_Curr_
->tree_lock
) _In_ device_extension
* Vcb
, _In_opt_ PIRP Irp
) {
2970 traverse_ptr tp
, next_tp
;
2975 searchkey
.obj_id
= 0;
2976 searchkey
.obj_type
= 0;
2977 searchkey
.offset
= 0;
2979 Status
= find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, false, Irp
);
2980 if (!NT_SUCCESS(Status
)) {
2981 ERR("error - find_item returned %08x\n", Status
);
2986 TRACE("(%I64x,%x,%I64x)\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
2988 if (tp
.item
->key
.obj_type
== TYPE_ROOT_ITEM
) {
2989 ROOT_ITEM
* ri
= (ROOT_ITEM
*)tp
.item
->data
;
2991 if (tp
.item
->size
< offsetof(ROOT_ITEM
, byte_limit
)) {
2992 ERR("(%I64x,%x,%I64x) was %u bytes, expected at least %u\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, tp
.item
->size
, offsetof(ROOT_ITEM
, byte_limit
));
2994 TRACE("root %I64x - address %I64x\n", tp
.item
->key
.obj_id
, ri
->block_number
);
2996 Status
= add_root(Vcb
, tp
.item
->key
.obj_id
, ri
->block_number
, ri
->generation
, &tp
);
2997 if (!NT_SUCCESS(Status
)) {
2998 ERR("add_root returned %08x\n", Status
);
3002 } else if (tp
.item
->key
.obj_type
== TYPE_ROOT_BACKREF
&& !IsListEmpty(&Vcb
->roots
)) {
3003 root
* lastroot
= CONTAINING_RECORD(Vcb
->roots
.Blink
, root
, list_entry
);
3005 if (lastroot
->id
== tp
.item
->key
.obj_id
)
3006 lastroot
->parent
= tp
.item
->key
.offset
;
3009 b
= find_next_item(Vcb
, &tp
, &next_tp
, false, Irp
);
3015 if (!Vcb
->readonly
&& !Vcb
->data_reloc_root
) {
3023 WARN("data reloc root doesn't exist, creating it\n");
3025 Status
= create_root(Vcb
, BTRFS_ROOT_DATA_RELOC
, &reloc_root
, false, 0, Irp
);
3027 if (!NT_SUCCESS(Status
)) {
3028 ERR("create_root returned %08x\n", Status
);
3032 reloc_root
->root_item
.inode
.generation
= 1;
3033 reloc_root
->root_item
.inode
.st_size
= 3;
3034 reloc_root
->root_item
.inode
.st_blocks
= Vcb
->superblock
.node_size
;
3035 reloc_root
->root_item
.inode
.st_nlink
= 1;
3036 reloc_root
->root_item
.inode
.st_mode
= 040755;
3037 reloc_root
->root_item
.inode
.flags
= 0xffffffff80000000;
3038 reloc_root
->root_item
.objid
= SUBVOL_ROOT_INODE
;
3039 reloc_root
->root_item
.bytes_used
= Vcb
->superblock
.node_size
;
3041 ii
= ExAllocatePoolWithTag(PagedPool
, sizeof(INODE_ITEM
), ALLOC_TAG
);
3043 ERR("out of memory\n");
3044 return STATUS_INSUFFICIENT_RESOURCES
;
3047 KeQuerySystemTime(&time
);
3048 win_time_to_unix(time
, &now
);
3050 RtlZeroMemory(ii
, sizeof(INODE_ITEM
));
3051 ii
->generation
= Vcb
->superblock
.generation
;
3052 ii
->st_blocks
= Vcb
->superblock
.node_size
;
3054 ii
->st_mode
= 040755;
3059 Status
= insert_tree_item(Vcb
, reloc_root
, SUBVOL_ROOT_INODE
, TYPE_INODE_ITEM
, 0, ii
, sizeof(INODE_ITEM
), NULL
, Irp
);
3060 if (!NT_SUCCESS(Status
)) {
3061 ERR("insert_tree_item returned %08x\n", Status
);
3066 irlen
= (uint16_t)offsetof(INODE_REF
, name
[0]) + 2;
3067 ir
= ExAllocatePoolWithTag(PagedPool
, irlen
, ALLOC_TAG
);
3069 ERR("out of memory\n");
3070 return STATUS_INSUFFICIENT_RESOURCES
;
3078 Status
= insert_tree_item(Vcb
, reloc_root
, SUBVOL_ROOT_INODE
, TYPE_INODE_REF
, SUBVOL_ROOT_INODE
, ir
, irlen
, NULL
, Irp
);
3079 if (!NT_SUCCESS(Status
)) {
3080 ERR("insert_tree_item returned %08x\n", Status
);
3085 Vcb
->data_reloc_root
= reloc_root
;
3086 Vcb
->need_write
= true;
3089 return STATUS_SUCCESS
;
3092 static NTSTATUS
find_disk_holes(_In_
_Requires_lock_held_(_Curr_
->tree_lock
) device_extension
* Vcb
, _In_ device
* dev
, _In_opt_ PIRP Irp
) {
3094 traverse_ptr tp
, next_tp
;
3099 InitializeListHead(&dev
->space
);
3101 searchkey
.obj_id
= 0;
3102 searchkey
.obj_type
= TYPE_DEV_STATS
;
3103 searchkey
.offset
= dev
->devitem
.dev_id
;
3105 Status
= find_item(Vcb
, Vcb
->dev_root
, &tp
, &searchkey
, false, Irp
);
3106 if (NT_SUCCESS(Status
) && !keycmp(tp
.item
->key
, searchkey
))
3107 RtlCopyMemory(dev
->stats
, tp
.item
->data
, min(sizeof(uint64_t) * 5, tp
.item
->size
));
3109 searchkey
.obj_id
= dev
->devitem
.dev_id
;
3110 searchkey
.obj_type
= TYPE_DEV_EXTENT
;
3111 searchkey
.offset
= 0;
3113 Status
= find_item(Vcb
, Vcb
->dev_root
, &tp
, &searchkey
, false, Irp
);
3114 if (!NT_SUCCESS(Status
)) {
3115 ERR("error - find_item returned %08x\n", Status
);
3122 if (tp
.item
->key
.obj_id
== dev
->devitem
.dev_id
&& tp
.item
->key
.obj_type
== TYPE_DEV_EXTENT
) {
3123 if (tp
.item
->size
>= sizeof(DEV_EXTENT
)) {
3124 DEV_EXTENT
* de
= (DEV_EXTENT
*)tp
.item
->data
;
3126 if (tp
.item
->key
.offset
> lastaddr
) {
3127 Status
= add_space_entry(&dev
->space
, NULL
, lastaddr
, tp
.item
->key
.offset
- lastaddr
);
3128 if (!NT_SUCCESS(Status
)) {
3129 ERR("add_space_entry returned %08x\n", Status
);
3134 lastaddr
= tp
.item
->key
.offset
+ de
->length
;
3136 ERR("(%I64x,%x,%I64x) was %u bytes, expected %u\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, tp
.item
->size
, sizeof(DEV_EXTENT
));
3140 b
= find_next_item(Vcb
, &tp
, &next_tp
, false, Irp
);
3144 if (tp
.item
->key
.obj_id
> searchkey
.obj_id
|| tp
.item
->key
.obj_type
> searchkey
.obj_type
)
3149 if (lastaddr
< dev
->devitem
.num_bytes
) {
3150 Status
= add_space_entry(&dev
->space
, NULL
, lastaddr
, dev
->devitem
.num_bytes
- lastaddr
);
3151 if (!NT_SUCCESS(Status
)) {
3152 ERR("add_space_entry returned %08x\n", Status
);
3157 // The Linux driver doesn't like to allocate chunks within the first megabyte of a device.
3159 space_list_subtract2(&dev
->space
, NULL
, 0, 0x100000, NULL
, NULL
);
3161 return STATUS_SUCCESS
;
3164 static void add_device_to_list(_In_ device_extension
* Vcb
, _In_ device
* dev
) {
3167 le
= Vcb
->devices
.Flink
;
3169 while (le
!= &Vcb
->devices
) {
3170 device
* dev2
= CONTAINING_RECORD(le
, device
, list_entry
);
3172 if (dev2
->devitem
.dev_id
> dev
->devitem
.dev_id
) {
3173 InsertHeadList(le
->Blink
, &dev
->list_entry
);
3180 InsertTailList(&Vcb
->devices
, &dev
->list_entry
);
3184 device
* find_device_from_uuid(_In_ device_extension
* Vcb
, _In_ BTRFS_UUID
* uuid
) {
3185 volume_device_extension
* vde
;
3186 pdo_device_extension
* pdode
;
3189 le
= Vcb
->devices
.Flink
;
3190 while (le
!= &Vcb
->devices
) {
3191 device
* dev
= CONTAINING_RECORD(le
, device
, list_entry
);
3193 TRACE("device %I64x, uuid %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n", dev
->devitem
.dev_id
,
3194 dev
->devitem
.device_uuid
.uuid
[0], dev
->devitem
.device_uuid
.uuid
[1], dev
->devitem
.device_uuid
.uuid
[2], dev
->devitem
.device_uuid
.uuid
[3], dev
->devitem
.device_uuid
.uuid
[4], dev
->devitem
.device_uuid
.uuid
[5], dev
->devitem
.device_uuid
.uuid
[6], dev
->devitem
.device_uuid
.uuid
[7],
3195 dev
->devitem
.device_uuid
.uuid
[8], dev
->devitem
.device_uuid
.uuid
[9], dev
->devitem
.device_uuid
.uuid
[10], dev
->devitem
.device_uuid
.uuid
[11], dev
->devitem
.device_uuid
.uuid
[12], dev
->devitem
.device_uuid
.uuid
[13], dev
->devitem
.device_uuid
.uuid
[14], dev
->devitem
.device_uuid
.uuid
[15]);
3197 if (RtlCompareMemory(&dev
->devitem
.device_uuid
, uuid
, sizeof(BTRFS_UUID
)) == sizeof(BTRFS_UUID
)) {
3198 TRACE("returning device %I64x\n", dev
->devitem
.dev_id
);
3212 ExAcquireResourceSharedLite(&pdode
->child_lock
, true);
3214 if (Vcb
->devices_loaded
< Vcb
->superblock
.num_devices
) {
3215 le
= pdode
->children
.Flink
;
3217 while (le
!= &pdode
->children
) {
3218 volume_child
* vc
= CONTAINING_RECORD(le
, volume_child
, list_entry
);
3220 if (RtlCompareMemory(uuid
, &vc
->uuid
, sizeof(BTRFS_UUID
)) == sizeof(BTRFS_UUID
)) {
3223 dev
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(device
), ALLOC_TAG
);
3225 ExReleaseResourceLite(&pdode
->child_lock
);
3226 ERR("out of memory\n");
3230 RtlZeroMemory(dev
, sizeof(device
));
3231 dev
->devobj
= vc
->devobj
;
3232 dev
->fileobj
= vc
->fileobj
;
3233 dev
->devitem
.device_uuid
= *uuid
;
3234 dev
->devitem
.dev_id
= vc
->devid
;
3235 dev
->devitem
.num_bytes
= vc
->size
;
3236 dev
->seeding
= vc
->seeding
;
3237 dev
->readonly
= dev
->seeding
;
3239 dev
->removable
= false;
3240 dev
->disk_num
= vc
->disk_num
;
3241 dev
->part_num
= vc
->part_num
;
3242 dev
->num_trim_entries
= 0;
3243 InitializeListHead(&dev
->trim_list
);
3245 add_device_to_list(Vcb
, dev
);
3246 Vcb
->devices_loaded
++;
3248 ExReleaseResourceLite(&pdode
->child_lock
);
3257 ExReleaseResourceLite(&pdode
->child_lock
);
3260 WARN("could not find device with uuid %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n",
3261 uuid
->uuid
[0], uuid
->uuid
[1], uuid
->uuid
[2], uuid
->uuid
[3], uuid
->uuid
[4], uuid
->uuid
[5], uuid
->uuid
[6], uuid
->uuid
[7],
3262 uuid
->uuid
[8], uuid
->uuid
[9], uuid
->uuid
[10], uuid
->uuid
[11], uuid
->uuid
[12], uuid
->uuid
[13], uuid
->uuid
[14], uuid
->uuid
[15]);
3267 static bool is_device_removable(_In_ PDEVICE_OBJECT devobj
) {
3269 STORAGE_HOTPLUG_INFO shi
;
3271 Status
= dev_ioctl(devobj
, IOCTL_STORAGE_GET_HOTPLUG_INFO
, NULL
, 0, &shi
, sizeof(STORAGE_HOTPLUG_INFO
), true, NULL
);
3273 if (!NT_SUCCESS(Status
)) {
3274 ERR("dev_ioctl returned %08x\n", Status
);
3278 return shi
.MediaRemovable
!= 0 ? true : false;
3281 static ULONG
get_device_change_count(_In_ PDEVICE_OBJECT devobj
) {
3284 IO_STATUS_BLOCK iosb
;
3286 Status
= dev_ioctl(devobj
, IOCTL_STORAGE_CHECK_VERIFY
, NULL
, 0, &cc
, sizeof(ULONG
), true, &iosb
);
3288 if (!NT_SUCCESS(Status
)) {
3289 ERR("dev_ioctl returned %08x\n", Status
);
3293 if (iosb
.Information
< sizeof(ULONG
)) {
3294 ERR("iosb.Information was too short\n");
3301 void init_device(_In_ device_extension
* Vcb
, _Inout_ device
* dev
, _In_
bool get_nums
) {
3304 ATA_PASS_THROUGH_EX
* apte
;
3305 STORAGE_PROPERTY_QUERY spq
;
3306 DEVICE_TRIM_DESCRIPTOR dtd
;
3308 dev
->removable
= is_device_removable(dev
->devobj
);
3309 dev
->change_count
= dev
->removable
? get_device_change_count(dev
->devobj
) : 0;
3312 STORAGE_DEVICE_NUMBER sdn
;
3314 Status
= dev_ioctl(dev
->devobj
, IOCTL_STORAGE_GET_DEVICE_NUMBER
, NULL
, 0,
3315 &sdn
, sizeof(STORAGE_DEVICE_NUMBER
), true, NULL
);
3317 if (!NT_SUCCESS(Status
)) {
3318 WARN("IOCTL_STORAGE_GET_DEVICE_NUMBER returned %08x\n", Status
);
3319 dev
->disk_num
= 0xffffffff;
3320 dev
->part_num
= 0xffffffff;
3322 dev
->disk_num
= sdn
.DeviceNumber
;
3323 dev
->part_num
= sdn
.PartitionNumber
;
3328 dev
->readonly
= dev
->seeding
;
3330 dev
->num_trim_entries
= 0;
3331 dev
->stats_changed
= false;
3332 InitializeListHead(&dev
->trim_list
);
3334 if (!dev
->readonly
) {
3335 Status
= dev_ioctl(dev
->devobj
, IOCTL_DISK_IS_WRITABLE
, NULL
, 0,
3336 NULL
, 0, true, NULL
);
3337 if (Status
== STATUS_MEDIA_WRITE_PROTECTED
)
3338 dev
->readonly
= true;
3341 aptelen
= sizeof(ATA_PASS_THROUGH_EX
) + 512;
3342 apte
= ExAllocatePoolWithTag(NonPagedPool
, aptelen
, ALLOC_TAG
);
3344 ERR("out of memory\n");
3348 RtlZeroMemory(apte
, aptelen
);
3350 apte
->Length
= sizeof(ATA_PASS_THROUGH_EX
);
3351 apte
->AtaFlags
= ATA_FLAGS_DATA_IN
;
3352 apte
->DataTransferLength
= aptelen
- sizeof(ATA_PASS_THROUGH_EX
);
3353 apte
->TimeOutValue
= 3;
3354 apte
->DataBufferOffset
= apte
->Length
;
3355 apte
->CurrentTaskFile
[6] = IDE_COMMAND_IDENTIFY
;
3357 Status
= dev_ioctl(dev
->devobj
, IOCTL_ATA_PASS_THROUGH
, apte
, aptelen
,
3358 apte
, aptelen
, true, NULL
);
3360 if (!NT_SUCCESS(Status
))
3361 TRACE("IOCTL_ATA_PASS_THROUGH returned %08x for IDENTIFY DEVICE\n", Status
);
3363 IDENTIFY_DEVICE_DATA
* idd
= (IDENTIFY_DEVICE_DATA
*)((uint8_t*)apte
+ sizeof(ATA_PASS_THROUGH_EX
));
3365 if (idd
->CommandSetSupport
.FlushCache
) {
3366 dev
->can_flush
= true;
3367 TRACE("FLUSH CACHE supported\n");
3369 TRACE("FLUSH CACHE not supported\n");
3374 #ifdef DEBUG_TRIM_EMULATION
3378 spq
.PropertyId
= StorageDeviceTrimProperty
;
3379 spq
.QueryType
= PropertyStandardQuery
;
3380 spq
.AdditionalParameters
[0] = 0;
3382 Status
= dev_ioctl(dev
->devobj
, IOCTL_STORAGE_QUERY_PROPERTY
, &spq
, sizeof(STORAGE_PROPERTY_QUERY
),
3383 &dtd
, sizeof(DEVICE_TRIM_DESCRIPTOR
), true, NULL
);
3385 if (NT_SUCCESS(Status
)) {
3386 if (dtd
.TrimEnabled
) {
3389 TRACE("TRIM supported\n");
3391 TRACE("TRIM not supported\n");
3395 RtlZeroMemory(dev
->stats
, sizeof(uint64_t) * 5);
3398 static NTSTATUS
load_chunk_root(_In_
_Requires_lock_held_(_Curr_
->tree_lock
) device_extension
* Vcb
, _In_opt_ PIRP Irp
) {
3399 traverse_ptr tp
, next_tp
;
3405 searchkey
.obj_id
= 0;
3406 searchkey
.obj_type
= 0;
3407 searchkey
.offset
= 0;
3409 Vcb
->data_flags
= 0;
3410 Vcb
->metadata_flags
= 0;
3411 Vcb
->system_flags
= 0;
3413 Status
= find_item(Vcb
, Vcb
->chunk_root
, &tp
, &searchkey
, false, Irp
);
3414 if (!NT_SUCCESS(Status
)) {
3415 ERR("error - find_item returned %08x\n", Status
);
3420 TRACE("(%I64x,%x,%I64x)\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
3422 if (tp
.item
->key
.obj_id
== 1 && tp
.item
->key
.obj_type
== TYPE_DEV_ITEM
) {
3423 if (tp
.item
->size
< sizeof(DEV_ITEM
)) {
3424 ERR("(%I64x,%x,%I64x) was %u bytes, expected %u\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, tp
.item
->size
, sizeof(DEV_ITEM
));
3426 DEV_ITEM
* di
= (DEV_ITEM
*)tp
.item
->data
;
3430 le
= Vcb
->devices
.Flink
;
3431 while (le
!= &Vcb
->devices
) {
3432 device
* dev
= CONTAINING_RECORD(le
, device
, list_entry
);
3434 if (dev
->devobj
&& RtlCompareMemory(&dev
->devitem
.device_uuid
, &di
->device_uuid
, sizeof(BTRFS_UUID
)) == sizeof(BTRFS_UUID
)) {
3435 RtlCopyMemory(&dev
->devitem
, tp
.item
->data
, min(tp
.item
->size
, sizeof(DEV_ITEM
)));
3437 if (le
!= Vcb
->devices
.Flink
)
3438 init_device(Vcb
, dev
, true);
3447 if (!done
&& Vcb
->vde
) {
3448 volume_device_extension
* vde
= Vcb
->vde
;
3449 pdo_device_extension
* pdode
= vde
->pdode
;
3451 ExAcquireResourceSharedLite(&pdode
->child_lock
, true);
3453 if (Vcb
->devices_loaded
< Vcb
->superblock
.num_devices
) {
3454 le
= pdode
->children
.Flink
;
3456 while (le
!= &pdode
->children
) {
3457 volume_child
* vc
= CONTAINING_RECORD(le
, volume_child
, list_entry
);
3459 if (RtlCompareMemory(&di
->device_uuid
, &vc
->uuid
, sizeof(BTRFS_UUID
)) == sizeof(BTRFS_UUID
)) {
3462 dev
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(device
), ALLOC_TAG
);
3464 ExReleaseResourceLite(&pdode
->child_lock
);
3465 ERR("out of memory\n");
3466 return STATUS_INSUFFICIENT_RESOURCES
;
3469 RtlZeroMemory(dev
, sizeof(device
));
3471 dev
->devobj
= vc
->devobj
;
3472 dev
->fileobj
= vc
->fileobj
;
3473 RtlCopyMemory(&dev
->devitem
, di
, min(tp
.item
->size
, sizeof(DEV_ITEM
)));
3474 dev
->seeding
= vc
->seeding
;
3475 init_device(Vcb
, dev
, false);
3477 if (dev
->devitem
.num_bytes
> vc
->size
) {
3478 WARN("device %I64x: DEV_ITEM says %I64x bytes, but Windows only reports %I64x\n", tp
.item
->key
.offset
,
3479 dev
->devitem
.num_bytes
, vc
->size
);
3481 dev
->devitem
.num_bytes
= vc
->size
;
3484 dev
->disk_num
= vc
->disk_num
;
3485 dev
->part_num
= vc
->part_num
;
3486 add_device_to_list(Vcb
, dev
);
3487 Vcb
->devices_loaded
++;
3497 if (!Vcb
->options
.allow_degraded
) {
3498 ERR("volume not found: device %I64x, uuid %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n", tp
.item
->key
.offset
,
3499 di
->device_uuid
.uuid
[0], di
->device_uuid
.uuid
[1], di
->device_uuid
.uuid
[2], di
->device_uuid
.uuid
[3], di
->device_uuid
.uuid
[4], di
->device_uuid
.uuid
[5], di
->device_uuid
.uuid
[6], di
->device_uuid
.uuid
[7],
3500 di
->device_uuid
.uuid
[8], di
->device_uuid
.uuid
[9], di
->device_uuid
.uuid
[10], di
->device_uuid
.uuid
[11], di
->device_uuid
.uuid
[12], di
->device_uuid
.uuid
[13], di
->device_uuid
.uuid
[14], di
->device_uuid
.uuid
[15]);
3504 dev
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(device
), ALLOC_TAG
);
3506 ExReleaseResourceLite(&pdode
->child_lock
);
3507 ERR("out of memory\n");
3508 return STATUS_INSUFFICIENT_RESOURCES
;
3511 RtlZeroMemory(dev
, sizeof(device
));
3513 // Missing device, so we keep dev->devobj as NULL
3514 RtlCopyMemory(&dev
->devitem
, di
, min(tp
.item
->size
, sizeof(DEV_ITEM
)));
3515 InitializeListHead(&dev
->trim_list
);
3517 add_device_to_list(Vcb
, dev
);
3518 Vcb
->devices_loaded
++;
3522 ERR("unexpected device %I64x found\n", tp
.item
->key
.offset
);
3524 ExReleaseResourceLite(&pdode
->child_lock
);
3527 } else if (tp
.item
->key
.obj_type
== TYPE_CHUNK_ITEM
) {
3528 if (tp
.item
->size
< sizeof(CHUNK_ITEM
)) {
3529 ERR("(%I64x,%x,%I64x) was %u bytes, expected at least %u\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, tp
.item
->size
, sizeof(CHUNK_ITEM
));
3531 c
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(chunk
), ALLOC_TAG
);
3534 ERR("out of memory\n");
3535 return STATUS_INSUFFICIENT_RESOURCES
;
3538 c
->size
= tp
.item
->size
;
3539 c
->offset
= tp
.item
->key
.offset
;
3540 c
->used
= c
->oldused
= 0;
3541 c
->cache
= c
->old_cache
= NULL
;
3543 c
->readonly
= false;
3545 c
->cache_loaded
= false;
3547 c
->space_changed
= false;
3550 c
->chunk_item
= ExAllocatePoolWithTag(NonPagedPool
, tp
.item
->size
, ALLOC_TAG
);
3552 if (!c
->chunk_item
) {
3553 ERR("out of memory\n");
3555 return STATUS_INSUFFICIENT_RESOURCES
;
3558 RtlCopyMemory(c
->chunk_item
, tp
.item
->data
, tp
.item
->size
);
3560 if (c
->chunk_item
->type
& BLOCK_FLAG_DATA
&& c
->chunk_item
->type
> Vcb
->data_flags
)
3561 Vcb
->data_flags
= c
->chunk_item
->type
;
3563 if (c
->chunk_item
->type
& BLOCK_FLAG_METADATA
&& c
->chunk_item
->type
> Vcb
->metadata_flags
)
3564 Vcb
->metadata_flags
= c
->chunk_item
->type
;
3566 if (c
->chunk_item
->type
& BLOCK_FLAG_SYSTEM
&& c
->chunk_item
->type
> Vcb
->system_flags
)
3567 Vcb
->system_flags
= c
->chunk_item
->type
;
3569 if (c
->chunk_item
->type
& BLOCK_FLAG_RAID10
) {
3570 if (c
->chunk_item
->sub_stripes
== 0 || c
->chunk_item
->sub_stripes
> c
->chunk_item
->num_stripes
) {
3571 ERR("chunk %I64x: invalid stripes (num_stripes %u, sub_stripes %u)\n", c
->offset
, c
->chunk_item
->num_stripes
, c
->chunk_item
->sub_stripes
);
3572 ExFreePool(c
->chunk_item
);
3574 return STATUS_INTERNAL_ERROR
;
3578 if (c
->chunk_item
->num_stripes
> 0) {
3579 CHUNK_ITEM_STRIPE
* cis
= (CHUNK_ITEM_STRIPE
*)&c
->chunk_item
[1];
3582 c
->devices
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(device
*) * c
->chunk_item
->num_stripes
, ALLOC_TAG
);
3585 ERR("out of memory\n");
3586 ExFreePool(c
->chunk_item
);
3588 return STATUS_INSUFFICIENT_RESOURCES
;
3591 for (i
= 0; i
< c
->chunk_item
->num_stripes
; i
++) {
3592 c
->devices
[i
] = find_device_from_uuid(Vcb
, &cis
[i
].dev_uuid
);
3593 TRACE("device %I64u = %p\n", i
, c
->devices
[i
]);
3595 if (!c
->devices
[i
]) {
3596 ERR("missing device\n");
3597 ExFreePool(c
->chunk_item
);
3599 return STATUS_INTERNAL_ERROR
;
3602 if (c
->devices
[i
]->readonly
)
3606 ERR("chunk %I64x: number of stripes is 0\n", c
->offset
);
3607 ExFreePool(c
->chunk_item
);
3609 return STATUS_INTERNAL_ERROR
;
3612 ExInitializeResourceLite(&c
->lock
);
3613 ExInitializeResourceLite(&c
->changed_extents_lock
);
3615 InitializeListHead(&c
->space
);
3616 InitializeListHead(&c
->space_size
);
3617 InitializeListHead(&c
->deleting
);
3618 InitializeListHead(&c
->changed_extents
);
3620 InitializeListHead(&c
->range_locks
);
3621 ExInitializeResourceLite(&c
->range_locks_lock
);
3622 KeInitializeEvent(&c
->range_locks_event
, NotificationEvent
, false);
3624 InitializeListHead(&c
->partial_stripes
);
3625 ExInitializeResourceLite(&c
->partial_stripes_lock
);
3627 c
->last_alloc_set
= false;
3631 InsertTailList(&Vcb
->chunks
, &c
->list_entry
);
3633 c
->list_entry_balance
.Flink
= NULL
;
3637 b
= find_next_item(Vcb
, &tp
, &next_tp
, false, Irp
);
3643 Vcb
->log_to_phys_loaded
= true;
3645 if (Vcb
->data_flags
== 0)
3646 Vcb
->data_flags
= BLOCK_FLAG_DATA
| (Vcb
->superblock
.num_devices
> 1 ? BLOCK_FLAG_RAID0
: 0);
3648 if (Vcb
->metadata_flags
== 0)
3649 Vcb
->metadata_flags
= BLOCK_FLAG_METADATA
| (Vcb
->superblock
.num_devices
> 1 ? BLOCK_FLAG_RAID1
: BLOCK_FLAG_DUPLICATE
);
3651 if (Vcb
->system_flags
== 0)
3652 Vcb
->system_flags
= BLOCK_FLAG_SYSTEM
| (Vcb
->superblock
.num_devices
> 1 ? BLOCK_FLAG_RAID1
: BLOCK_FLAG_DUPLICATE
);
3654 if (Vcb
->superblock
.incompat_flags
& BTRFS_INCOMPAT_FLAGS_MIXED_GROUPS
) {
3655 Vcb
->metadata_flags
|= BLOCK_FLAG_DATA
;
3656 Vcb
->data_flags
= Vcb
->metadata_flags
;
3659 return STATUS_SUCCESS
;
3662 void protect_superblocks(_Inout_ chunk
* c
) {
3664 uint64_t off_start
, off_end
;
3666 // The Linux driver also protects all the space before the first superblock.
3667 // I realize this confuses physical and logical addresses, but this is what btrfs-progs does -
3668 // evidently Linux assumes the chunk at 0 is always SINGLE.
3669 if (c
->offset
< superblock_addrs
[0])
3670 space_list_subtract(c
, false, c
->offset
, superblock_addrs
[0] - c
->offset
, NULL
);
3672 while (superblock_addrs
[i
] != 0) {
3673 CHUNK_ITEM
* ci
= c
->chunk_item
;
3674 CHUNK_ITEM_STRIPE
* cis
= (CHUNK_ITEM_STRIPE
*)&ci
[1];
3676 if (ci
->type
& BLOCK_FLAG_RAID0
|| ci
->type
& BLOCK_FLAG_RAID10
) {
3677 for (j
= 0; j
< ci
->num_stripes
; j
++) {
3678 uint16_t sub_stripes
= max(ci
->sub_stripes
, 1);
3680 if (cis
[j
].offset
+ (ci
->size
* ci
->num_stripes
/ sub_stripes
) > superblock_addrs
[i
] && cis
[j
].offset
<= superblock_addrs
[i
] + sizeof(superblock
)) {
3683 uint16_t startoffstripe
;
3686 TRACE("cut out superblock in chunk %I64x\n", c
->offset
);
3688 off_start
= superblock_addrs
[i
] - cis
[j
].offset
;
3689 off_start
-= off_start
% ci
->stripe_length
;
3690 off_start
*= ci
->num_stripes
/ sub_stripes
;
3691 off_start
+= (j
/ sub_stripes
) * ci
->stripe_length
;
3693 off_end
= off_start
+ ci
->stripe_length
;
3696 get_raid0_offset(off_start
, ci
->stripe_length
, ci
->num_stripes
/ sub_stripes
, &startoff
, &startoffstripe
);
3697 TRACE("j = %u, startoffstripe = %u\n", j
, startoffstripe
);
3698 TRACE("startoff = %I64x, superblock = %I64x\n", startoff
+ cis
[j
].offset
, superblock_addrs
[i
]);
3701 space_list_subtract(c
, false, c
->offset
+ off_start
, off_end
- off_start
, NULL
);
3704 } else if (ci
->type
& BLOCK_FLAG_RAID5
) {
3705 uint64_t stripe_size
= ci
->size
/ (ci
->num_stripes
- 1);
3707 for (j
= 0; j
< ci
->num_stripes
; j
++) {
3708 if (cis
[j
].offset
+ stripe_size
> superblock_addrs
[i
] && cis
[j
].offset
<= superblock_addrs
[i
] + sizeof(superblock
)) {
3709 TRACE("cut out superblock in chunk %I64x\n", c
->offset
);
3711 off_start
= superblock_addrs
[i
] - cis
[j
].offset
;
3712 off_start
-= off_start
% ci
->stripe_length
;
3713 off_start
*= ci
->num_stripes
- 1;
3715 off_end
= sector_align(superblock_addrs
[i
] - cis
[j
].offset
+ sizeof(superblock
), ci
->stripe_length
);
3716 off_end
*= ci
->num_stripes
- 1;
3718 TRACE("cutting out %I64x, size %I64x\n", c
->offset
+ off_start
, off_end
- off_start
);
3720 space_list_subtract(c
, false, c
->offset
+ off_start
, off_end
- off_start
, NULL
);
3723 } else if (ci
->type
& BLOCK_FLAG_RAID6
) {
3724 uint64_t stripe_size
= ci
->size
/ (ci
->num_stripes
- 2);
3726 for (j
= 0; j
< ci
->num_stripes
; j
++) {
3727 if (cis
[j
].offset
+ stripe_size
> superblock_addrs
[i
] && cis
[j
].offset
<= superblock_addrs
[i
] + sizeof(superblock
)) {
3728 TRACE("cut out superblock in chunk %I64x\n", c
->offset
);
3730 off_start
= superblock_addrs
[i
] - cis
[j
].offset
;
3731 off_start
-= off_start
% ci
->stripe_length
;
3732 off_start
*= ci
->num_stripes
- 2;
3734 off_end
= sector_align(superblock_addrs
[i
] - cis
[j
].offset
+ sizeof(superblock
), ci
->stripe_length
);
3735 off_end
*= ci
->num_stripes
- 2;
3737 TRACE("cutting out %I64x, size %I64x\n", c
->offset
+ off_start
, off_end
- off_start
);
3739 space_list_subtract(c
, false, c
->offset
+ off_start
, off_end
- off_start
, NULL
);
3742 } else { // SINGLE, DUPLICATE, RAID1
3743 for (j
= 0; j
< ci
->num_stripes
; j
++) {
3744 if (cis
[j
].offset
+ ci
->size
> superblock_addrs
[i
] && cis
[j
].offset
<= superblock_addrs
[i
] + sizeof(superblock
)) {
3745 TRACE("cut out superblock in chunk %I64x\n", c
->offset
);
3747 // The Linux driver protects the whole stripe in which the superblock lives
3749 off_start
= ((superblock_addrs
[i
] - cis
[j
].offset
) / c
->chunk_item
->stripe_length
) * c
->chunk_item
->stripe_length
;
3750 off_end
= sector_align(superblock_addrs
[i
] - cis
[j
].offset
+ sizeof(superblock
), c
->chunk_item
->stripe_length
);
3752 space_list_subtract(c
, false, c
->offset
+ off_start
, off_end
- off_start
, NULL
);
3761 uint64_t chunk_estimate_phys_size(device_extension
* Vcb
, chunk
* c
, uint64_t u
) {
3762 uint64_t nfactor
, dfactor
;
3764 if (c
->chunk_item
->type
& BLOCK_FLAG_DUPLICATE
|| c
->chunk_item
->type
& BLOCK_FLAG_RAID1
|| c
->chunk_item
->type
& BLOCK_FLAG_RAID10
) {
3767 } else if (c
->chunk_item
->type
& BLOCK_FLAG_RAID5
) {
3768 nfactor
= Vcb
->superblock
.num_devices
- 1;
3769 dfactor
= Vcb
->superblock
.num_devices
;
3770 } else if (c
->chunk_item
->type
& BLOCK_FLAG_RAID6
) {
3771 nfactor
= Vcb
->superblock
.num_devices
- 2;
3772 dfactor
= Vcb
->superblock
.num_devices
;
3778 return u
* dfactor
/ nfactor
;
3781 NTSTATUS
find_chunk_usage(_In_
_Requires_lock_held_(_Curr_
->tree_lock
) device_extension
* Vcb
, _In_opt_ PIRP Irp
) {
3782 LIST_ENTRY
* le
= Vcb
->chunks
.Flink
;
3786 BLOCK_GROUP_ITEM
* bgi
;
3789 searchkey
.obj_type
= TYPE_BLOCK_GROUP_ITEM
;
3791 Vcb
->superblock
.bytes_used
= 0;
3793 while (le
!= &Vcb
->chunks
) {
3794 c
= CONTAINING_RECORD(le
, chunk
, list_entry
);
3796 searchkey
.obj_id
= c
->offset
;
3797 searchkey
.offset
= c
->chunk_item
->size
;
3799 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, false, Irp
);
3800 if (!NT_SUCCESS(Status
)) {
3801 ERR("error - find_item returned %08x\n", Status
);
3805 if (!keycmp(searchkey
, tp
.item
->key
)) {
3806 if (tp
.item
->size
>= sizeof(BLOCK_GROUP_ITEM
)) {
3807 bgi
= (BLOCK_GROUP_ITEM
*)tp
.item
->data
;
3809 c
->used
= c
->oldused
= bgi
->used
;
3811 TRACE("chunk %I64x has %I64x bytes used\n", c
->offset
, c
->used
);
3813 Vcb
->superblock
.bytes_used
+= chunk_estimate_phys_size(Vcb
, c
, bgi
->used
);
3815 ERR("(%I64x;%I64x,%x,%I64x) is %u bytes, expected %u\n",
3816 Vcb
->extent_root
->id
, tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, tp
.item
->size
, sizeof(BLOCK_GROUP_ITEM
));
3823 Vcb
->chunk_usage_found
= true;
3825 return STATUS_SUCCESS
;
3828 static NTSTATUS
load_sys_chunks(_In_ device_extension
* Vcb
) {
3830 ULONG n
= Vcb
->superblock
.n
;
3833 if (n
> sizeof(KEY
)) {
3834 RtlCopyMemory(&key
, &Vcb
->superblock
.sys_chunk_array
[Vcb
->superblock
.n
- n
], sizeof(KEY
));
3837 return STATUS_SUCCESS
;
3839 TRACE("bootstrap: %I64x,%x,%I64x\n", key
.obj_id
, key
.obj_type
, key
.offset
);
3841 if (key
.obj_type
== TYPE_CHUNK_ITEM
) {
3846 if (n
< sizeof(CHUNK_ITEM
))
3847 return STATUS_SUCCESS
;
3849 ci
= (CHUNK_ITEM
*)&Vcb
->superblock
.sys_chunk_array
[Vcb
->superblock
.n
- n
];
3850 cisize
= sizeof(CHUNK_ITEM
) + (ci
->num_stripes
* sizeof(CHUNK_ITEM_STRIPE
));
3853 return STATUS_SUCCESS
;
3855 sc
= ExAllocatePoolWithTag(PagedPool
, sizeof(sys_chunk
), ALLOC_TAG
);
3858 ERR("out of memory\n");
3859 return STATUS_INSUFFICIENT_RESOURCES
;
3864 sc
->data
= ExAllocatePoolWithTag(PagedPool
, sc
->size
, ALLOC_TAG
);
3867 ERR("out of memory\n");
3869 return STATUS_INSUFFICIENT_RESOURCES
;
3872 RtlCopyMemory(sc
->data
, ci
, sc
->size
);
3873 InsertTailList(&Vcb
->sys_chunks
, &sc
->list_entry
);
3877 ERR("unexpected item %I64x,%x,%I64x in bootstrap\n", key
.obj_id
, key
.obj_type
, key
.offset
);
3878 return STATUS_INTERNAL_ERROR
;
3882 return STATUS_SUCCESS
;
3886 static root
* find_default_subvol(_In_
_Requires_lock_held_(_Curr_
->tree_lock
) device_extension
* Vcb
, _In_opt_ PIRP Irp
) {
3889 static const char fn
[] = "default";
3890 static uint32_t crc32
= 0x8dbfc2d2;
3892 if (Vcb
->options
.subvol_id
!= 0) {
3893 le
= Vcb
->roots
.Flink
;
3894 while (le
!= &Vcb
->roots
) {
3895 root
* r
= CONTAINING_RECORD(le
, root
, list_entry
);
3897 if (r
->id
== Vcb
->options
.subvol_id
)
3904 if (Vcb
->superblock
.incompat_flags
& BTRFS_INCOMPAT_FLAGS_DEFAULT_SUBVOL
) {
3910 searchkey
.obj_id
= Vcb
->superblock
.root_dir_objectid
;
3911 searchkey
.obj_type
= TYPE_DIR_ITEM
;
3912 searchkey
.offset
= crc32
;
3914 Status
= find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, false, Irp
);
3915 if (!NT_SUCCESS(Status
)) {
3916 ERR("error - find_item returned %08x\n", Status
);
3920 if (keycmp(tp
.item
->key
, searchkey
)) {
3921 ERR("could not find (%I64x,%x,%I64x) in root tree\n", searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
);
3925 if (tp
.item
->size
< sizeof(DIR_ITEM
)) {
3926 ERR("(%I64x,%x,%I64x) was %u bytes, expected at least %u\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, tp
.item
->size
, sizeof(DIR_ITEM
));
3930 di
= (DIR_ITEM
*)tp
.item
->data
;
3932 if (tp
.item
->size
< sizeof(DIR_ITEM
) - 1 + di
->n
) {
3933 ERR("(%I64x,%x,%I64x) was %u bytes, expected %u\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, tp
.item
->size
, sizeof(DIR_ITEM
) - 1 + di
->n
);
3937 if (di
->n
!= strlen(fn
) || RtlCompareMemory(di
->name
, fn
, di
->n
) != di
->n
) {
3938 ERR("root DIR_ITEM had same CRC32, but was not \"default\"\n");
3942 if (di
->key
.obj_type
!= TYPE_ROOT_ITEM
) {
3943 ERR("default root has key (%I64x,%x,%I64x), expected subvolume\n", di
->key
.obj_id
, di
->key
.obj_type
, di
->key
.offset
);
3947 le
= Vcb
->roots
.Flink
;
3948 while (le
!= &Vcb
->roots
) {
3949 root
* r
= CONTAINING_RECORD(le
, root
, list_entry
);
3951 if (r
->id
== di
->key
.obj_id
)
3957 ERR("could not find root %I64x, using default instead\n", di
->key
.obj_id
);
3961 le
= Vcb
->roots
.Flink
;
3962 while (le
!= &Vcb
->roots
) {
3963 root
* r
= CONTAINING_RECORD(le
, root
, list_entry
);
3965 if (r
->id
== BTRFS_ROOT_FSTREE
)
3974 void init_file_cache(_In_ PFILE_OBJECT FileObject
, _In_ CC_FILE_SIZES
* ccfs
) {
3975 TRACE("(%p, %p)\n", FileObject
, ccfs
);
3977 CcInitializeCacheMap(FileObject
, ccfs
, false, cache_callbacks
, FileObject
);
3980 fCcSetAdditionalCacheAttributesEx(FileObject
, CC_ENABLE_DISK_IO_ACCOUNTING
);
3982 CcSetReadAheadGranularity(FileObject
, READ_AHEAD_GRANULARITY
);
3985 uint32_t get_num_of_processors() {
3986 KAFFINITY p
= KeQueryActiveProcessors();
3999 static NTSTATUS
create_calc_threads(_In_ PDEVICE_OBJECT DeviceObject
) {
4000 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
4003 Vcb
->calcthreads
.num_threads
= get_num_of_processors();
4005 Vcb
->calcthreads
.threads
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(drv_calc_thread
) * Vcb
->calcthreads
.num_threads
, ALLOC_TAG
);
4006 if (!Vcb
->calcthreads
.threads
) {
4007 ERR("out of memory\n");
4008 return STATUS_INSUFFICIENT_RESOURCES
;
4011 InitializeListHead(&Vcb
->calcthreads
.job_list
);
4012 ExInitializeResourceLite(&Vcb
->calcthreads
.lock
);
4013 KeInitializeEvent(&Vcb
->calcthreads
.event
, NotificationEvent
, false);
4015 RtlZeroMemory(Vcb
->calcthreads
.threads
, sizeof(drv_calc_thread
) * Vcb
->calcthreads
.num_threads
);
4017 for (i
= 0; i
< Vcb
->calcthreads
.num_threads
; i
++) {
4020 Vcb
->calcthreads
.threads
[i
].DeviceObject
= DeviceObject
;
4021 KeInitializeEvent(&Vcb
->calcthreads
.threads
[i
].finished
, NotificationEvent
, false);
4023 Status
= PsCreateSystemThread(&Vcb
->calcthreads
.threads
[i
].handle
, 0, NULL
, NULL
, NULL
, calc_thread
, &Vcb
->calcthreads
.threads
[i
]);
4024 if (!NT_SUCCESS(Status
)) {
4027 ERR("PsCreateSystemThread returned %08x\n", Status
);
4029 for (j
= 0; j
< i
; j
++) {
4030 Vcb
->calcthreads
.threads
[i
].quit
= true;
4033 KeSetEvent(&Vcb
->calcthreads
.event
, 0, false);
4039 return STATUS_SUCCESS
;
4042 static bool is_btrfs_volume(_In_ PDEVICE_OBJECT DeviceObject
) {
4044 MOUNTDEV_NAME mdn
, *mdn2
;
4047 Status
= dev_ioctl(DeviceObject
, IOCTL_MOUNTDEV_QUERY_DEVICE_NAME
, NULL
, 0, &mdn
, sizeof(MOUNTDEV_NAME
), true, NULL
);
4048 if (!NT_SUCCESS(Status
) && Status
!= STATUS_BUFFER_OVERFLOW
) {
4049 ERR("IOCTL_MOUNTDEV_QUERY_DEVICE_NAME returned %08x\n", Status
);
4053 mdnsize
= (ULONG
)offsetof(MOUNTDEV_NAME
, Name
[0]) + mdn
.NameLength
;
4055 mdn2
= ExAllocatePoolWithTag(PagedPool
, mdnsize
, ALLOC_TAG
);
4057 ERR("out of memory\n");
4061 Status
= dev_ioctl(DeviceObject
, IOCTL_MOUNTDEV_QUERY_DEVICE_NAME
, NULL
, 0, mdn2
, mdnsize
, true, NULL
);
4062 if (!NT_SUCCESS(Status
)) {
4063 ERR("IOCTL_MOUNTDEV_QUERY_DEVICE_NAME returned %08x\n", Status
);
4068 if (mdn2
->NameLength
> (sizeof(BTRFS_VOLUME_PREFIX
) - sizeof(WCHAR
)) &&
4069 RtlCompareMemory(mdn2
->Name
, BTRFS_VOLUME_PREFIX
, sizeof(BTRFS_VOLUME_PREFIX
) - sizeof(WCHAR
)) == sizeof(BTRFS_VOLUME_PREFIX
) - sizeof(WCHAR
)) {
4079 static NTSTATUS
get_device_pnp_name_guid(_In_ PDEVICE_OBJECT DeviceObject
, _Out_ PUNICODE_STRING pnp_name
, _In_
const GUID
* guid
) {
4081 WCHAR
*list
= NULL
, *s
;
4083 Status
= IoGetDeviceInterfaces((PVOID
)guid
, NULL
, 0, &list
);
4084 if (!NT_SUCCESS(Status
)) {
4085 ERR("IoGetDeviceInterfaces returned %08x\n", Status
);
4091 PFILE_OBJECT FileObject
;
4092 PDEVICE_OBJECT devobj
;
4093 UNICODE_STRING name
;
4095 name
.Length
= name
.MaximumLength
= (USHORT
)wcslen(s
) * sizeof(WCHAR
);
4098 if (NT_SUCCESS(IoGetDeviceObjectPointer(&name
, FILE_READ_ATTRIBUTES
, &FileObject
, &devobj
))) {
4099 if (DeviceObject
== devobj
|| DeviceObject
== FileObject
->DeviceObject
) {
4100 ObDereferenceObject(FileObject
);
4102 pnp_name
->Buffer
= ExAllocatePoolWithTag(PagedPool
, name
.Length
, ALLOC_TAG
);
4103 if (!pnp_name
->Buffer
) {
4104 ERR("out of memory\n");
4105 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4109 RtlCopyMemory(pnp_name
->Buffer
, name
.Buffer
, name
.Length
);
4110 pnp_name
->Length
= pnp_name
->MaximumLength
= name
.Length
;
4112 Status
= STATUS_SUCCESS
;
4116 ObDereferenceObject(FileObject
);
4119 s
= &s
[wcslen(s
) + 1];
4122 pnp_name
->Length
= pnp_name
->MaximumLength
= 0;
4123 pnp_name
->Buffer
= 0;
4125 Status
= STATUS_NOT_FOUND
;
4134 NTSTATUS
get_device_pnp_name(_In_ PDEVICE_OBJECT DeviceObject
, _Out_ PUNICODE_STRING pnp_name
, _Out_
const GUID
** guid
) {
4137 Status
= get_device_pnp_name_guid(DeviceObject
, pnp_name
, &GUID_DEVINTERFACE_VOLUME
);
4138 if (NT_SUCCESS(Status
)) {
4139 *guid
= &GUID_DEVINTERFACE_VOLUME
;
4143 Status
= get_device_pnp_name_guid(DeviceObject
, pnp_name
, &GUID_DEVINTERFACE_HIDDEN_VOLUME
);
4144 if (NT_SUCCESS(Status
)) {
4145 *guid
= &GUID_DEVINTERFACE_HIDDEN_VOLUME
;
4149 Status
= get_device_pnp_name_guid(DeviceObject
, pnp_name
, &GUID_DEVINTERFACE_DISK
);
4150 if (NT_SUCCESS(Status
)) {
4151 *guid
= &GUID_DEVINTERFACE_DISK
;
4155 return STATUS_NOT_FOUND
;
4158 _Success_(return>=0)
4159 static NTSTATUS
check_mount_device(_In_ PDEVICE_OBJECT DeviceObject
, _Out_
bool* pno_pnp
) {
4164 UNICODE_STRING pnp_name
;
4167 to_read
= DeviceObject
->SectorSize
== 0 ? sizeof(superblock
) : (ULONG
)sector_align(sizeof(superblock
), DeviceObject
->SectorSize
);
4169 sb
= ExAllocatePoolWithTag(NonPagedPool
, to_read
, ALLOC_TAG
);
4171 ERR("out of memory\n");
4172 return STATUS_INSUFFICIENT_RESOURCES
;
4175 Status
= sync_read_phys(DeviceObject
, NULL
, superblock_addrs
[0], to_read
, (PUCHAR
)sb
, true);
4176 if (!NT_SUCCESS(Status
)) {
4177 ERR("sync_read_phys returned %08x\n", Status
);
4181 if (sb
->magic
!= BTRFS_MAGIC
) {
4182 Status
= STATUS_SUCCESS
;
4186 crc32
= ~calc_crc32c(0xffffffff, (uint8_t*)&sb
->uuid
, (ULONG
)sizeof(superblock
) - sizeof(sb
->checksum
));
4188 if (crc32
!= *((uint32_t*)sb
->checksum
)) {
4189 WARN("crc32 was %08x, expected %08x\n", crc32
, *((uint32_t*)sb
->checksum
));
4190 Status
= STATUS_SUCCESS
;
4194 DeviceObject
->Flags
&= ~DO_VERIFY_VOLUME
;
4196 pnp_name
.Buffer
= NULL
;
4198 Status
= get_device_pnp_name(DeviceObject
, &pnp_name
, &guid
);
4199 if (!NT_SUCCESS(Status
)) {
4200 WARN("get_device_pnp_name returned %08x\n", Status
);
4201 pnp_name
.Length
= 0;
4204 if (pnp_name
.Length
== 0)
4208 volume_arrival(drvobj
, &pnp_name
);
4211 if (pnp_name
.Buffer
)
4212 ExFreePool(pnp_name
.Buffer
);
4214 Status
= STATUS_SUCCESS
;
4222 static bool still_has_superblock(_In_ PDEVICE_OBJECT device
, _In_ PFILE_OBJECT fileobj
) {
4226 PDEVICE_OBJECT device2
;
4231 to_read
= device
->SectorSize
== 0 ? sizeof(superblock
) : (ULONG
)sector_align(sizeof(superblock
), device
->SectorSize
);
4233 sb
= ExAllocatePoolWithTag(NonPagedPool
, to_read
, ALLOC_TAG
);
4235 ERR("out of memory\n");
4239 Status
= sync_read_phys(device
, fileobj
, superblock_addrs
[0], to_read
, (PUCHAR
)sb
, true);
4240 if (!NT_SUCCESS(Status
)) {
4241 ERR("Failed to read superblock: %08x\n", Status
);
4246 if (sb
->magic
!= BTRFS_MAGIC
) {
4247 TRACE("not a BTRFS volume\n");
4251 uint32_t crc32
= ~calc_crc32c(0xffffffff, (uint8_t*)&sb
->uuid
, (ULONG
)sizeof(superblock
) - sizeof(sb
->checksum
));
4253 if (crc32
!= *((uint32_t*)sb
->checksum
)) {
4254 WARN("crc32 was %08x, expected %08x\n", crc32
, *((uint32_t*)sb
->checksum
));
4263 device2
->Flags
&= ~DO_VERIFY_VOLUME
;
4264 device2
= IoGetLowerDeviceObject(device2
);
4271 static NTSTATUS
mount_vol(_In_ PDEVICE_OBJECT DeviceObject
, _In_ PIRP Irp
) {
4272 PIO_STACK_LOCATION IrpSp
;
4273 PDEVICE_OBJECT NewDeviceObject
= NULL
;
4274 PDEVICE_OBJECT DeviceToMount
, readobj
;
4275 PFILE_OBJECT fileobj
;
4277 device_extension
* Vcb
= NULL
;
4278 LIST_ENTRY
*le
, batchlist
;
4281 fcb
* root_fcb
= NULL
;
4282 ccb
* root_ccb
= NULL
;
4283 bool init_lookaside
= false;
4285 volume_device_extension
* vde
= NULL
;
4286 pdo_device_extension
* pdode
= NULL
;
4288 uint64_t readobjsize
;
4290 TRACE("(%p, %p)\n", DeviceObject
, Irp
);
4292 if (DeviceObject
!= master_devobj
) {
4293 Status
= STATUS_INVALID_DEVICE_REQUEST
;
4297 IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
4298 DeviceToMount
= IrpSp
->Parameters
.MountVolume
.DeviceObject
;
4300 if (!is_btrfs_volume(DeviceToMount
)) {
4301 bool not_pnp
= false;
4303 Status
= check_mount_device(DeviceToMount
, ¬_pnp
);
4304 if (!NT_SUCCESS(Status
))
4305 WARN("check_mount_device returned %08x\n", Status
);
4308 Status
= STATUS_UNRECOGNIZED_VOLUME
;
4314 pdo
= DeviceToMount
;
4316 while (IoGetLowerDeviceObject(pdo
)) {
4317 pdo
= IoGetLowerDeviceObject(pdo
);
4320 ExAcquireResourceSharedLite(&pdo_list_lock
, true);
4322 le
= pdo_list
.Flink
;
4323 while (le
!= &pdo_list
) {
4324 pdo_device_extension
* pdode2
= CONTAINING_RECORD(le
, pdo_device_extension
, list_entry
);
4326 if (pdode2
->pdo
== pdo
) {
4334 ExReleaseResourceLite(&pdo_list_lock
);
4336 if (!vde
|| vde
->type
!= VCB_TYPE_VOLUME
) {
4338 Status
= STATUS_UNRECOGNIZED_VOLUME
;
4346 ExAcquireResourceExclusiveLite(&pdode
->child_lock
, true);
4348 le
= pdode
->children
.Flink
;
4349 while (le
!= &pdode
->children
) {
4350 LIST_ENTRY
* le2
= le
->Flink
;
4352 vc
= CONTAINING_RECORD(pdode
->children
.Flink
, volume_child
, list_entry
);
4354 if (!still_has_superblock(vc
->devobj
, vc
->fileobj
)) {
4355 remove_volume_child(vde
, vc
, false);
4357 if (pdode
->num_children
== 0) {
4358 ERR("error - number of devices is zero\n");
4359 Status
= STATUS_INTERNAL_ERROR
;
4363 Status
= STATUS_DEVICE_NOT_READY
;
4370 if (pdode
->num_children
== 0 || pdode
->children_loaded
== 0) {
4371 ERR("error - number of devices is zero\n");
4372 Status
= STATUS_INTERNAL_ERROR
;
4376 ExConvertExclusiveToSharedLite(&pdode
->child_lock
);
4378 vc
= CONTAINING_RECORD(pdode
->children
.Flink
, volume_child
, list_entry
);
4380 readobj
= vc
->devobj
;
4381 fileobj
= vc
->fileobj
;
4382 readobjsize
= vc
->size
;
4384 vde
->device
->Characteristics
&= ~FILE_DEVICE_SECURE_OPEN
;
4386 GET_LENGTH_INFORMATION gli
;
4389 readobj
= DeviceToMount
;
4392 Status
= dev_ioctl(readobj
, IOCTL_DISK_GET_LENGTH_INFO
, NULL
, 0,
4393 &gli
, sizeof(gli
), true, NULL
);
4395 if (!NT_SUCCESS(Status
)) {
4396 ERR("error reading length information: %08x\n", Status
);
4400 readobjsize
= gli
.Length
.QuadPart
;
4403 Status
= IoCreateDevice(drvobj
, sizeof(device_extension
), NULL
, FILE_DEVICE_DISK_FILE_SYSTEM
, 0, false, &NewDeviceObject
);
4404 if (!NT_SUCCESS(Status
)) {
4405 ERR("IoCreateDevice returned %08x\n", Status
);
4406 Status
= STATUS_UNRECOGNIZED_VOLUME
;
4410 NewDeviceObject
->Flags
|= DO_DIRECT_IO
;
4412 // Some programs seem to expect that the sector size will be 512, for
4413 // FILE_NO_INTERMEDIATE_BUFFERING and the like.
4414 NewDeviceObject
->SectorSize
= min(DeviceToMount
->SectorSize
, 512);
4416 Vcb
= (PVOID
)NewDeviceObject
->DeviceExtension
;
4417 RtlZeroMemory(Vcb
, sizeof(device_extension
));
4418 Vcb
->type
= VCB_TYPE_FS
;
4421 ExInitializeResourceLite(&Vcb
->tree_lock
);
4422 Vcb
->need_write
= false;
4424 ExInitializeResourceLite(&Vcb
->fcb_lock
);
4425 ExInitializeResourceLite(&Vcb
->fileref_lock
);
4426 ExInitializeResourceLite(&Vcb
->chunk_lock
);
4427 ExInitializeResourceLite(&Vcb
->dirty_fcbs_lock
);
4428 ExInitializeResourceLite(&Vcb
->dirty_filerefs_lock
);
4429 ExInitializeResourceLite(&Vcb
->dirty_subvols_lock
);
4430 ExInitializeResourceLite(&Vcb
->scrub
.stats_lock
);
4432 ExInitializeResourceLite(&Vcb
->load_lock
);
4433 ExAcquireResourceExclusiveLite(&Vcb
->load_lock
, true);
4435 ExAcquireResourceExclusiveLite(&Vcb
->tree_lock
, true);
4437 DeviceToMount
->Flags
|= DO_DIRECT_IO
;
4439 Status
= read_superblock(Vcb
, readobj
, fileobj
, readobjsize
);
4440 if (!NT_SUCCESS(Status
)) {
4441 if (!IoIsErrorUserInduced(Status
))
4442 Status
= STATUS_UNRECOGNIZED_VOLUME
;
4443 else if (Irp
->Tail
.Overlay
.Thread
)
4444 IoSetHardErrorOrVerifyDevice(Irp
, readobj
);
4449 if (!vde
&& Vcb
->superblock
.num_devices
> 1) {
4450 ERR("cannot mount multi-device FS with non-PNP device\n");
4451 Status
= STATUS_UNRECOGNIZED_VOLUME
;
4455 Status
= registry_load_volume_options(Vcb
);
4456 if (!NT_SUCCESS(Status
)) {
4457 ERR("registry_load_volume_options returned %08x\n", Status
);
4461 if (pdode
&& pdode
->children_loaded
< pdode
->num_children
&& (!Vcb
->options
.allow_degraded
|| !finished_probing
|| degraded_wait
)) {
4462 ERR("could not mount as %u device(s) missing\n", pdode
->num_children
- pdode
->children_loaded
);
4463 Status
= STATUS_DEVICE_NOT_READY
;
4467 if (Vcb
->options
.ignore
) {
4468 TRACE("ignoring volume\n");
4469 Status
= STATUS_UNRECOGNIZED_VOLUME
;
4473 if (Vcb
->superblock
.incompat_flags
& ~INCOMPAT_SUPPORTED
) {
4474 WARN("cannot mount because of unsupported incompat flags (%I64x)\n", Vcb
->superblock
.incompat_flags
& ~INCOMPAT_SUPPORTED
);
4475 Status
= STATUS_UNRECOGNIZED_VOLUME
;
4479 Vcb
->readonly
= false;
4480 if (Vcb
->superblock
.compat_ro_flags
& ~COMPAT_RO_SUPPORTED
) {
4481 WARN("mounting read-only because of unsupported flags (%I64x)\n", Vcb
->superblock
.compat_ro_flags
& ~COMPAT_RO_SUPPORTED
);
4482 Vcb
->readonly
= true;
4485 if (Vcb
->options
.readonly
)
4486 Vcb
->readonly
= true;
4488 Vcb
->superblock
.generation
++;
4489 Vcb
->superblock
.incompat_flags
|= BTRFS_INCOMPAT_FLAGS_MIXED_BACKREF
;
4491 InitializeListHead(&Vcb
->devices
);
4492 dev
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(device
), ALLOC_TAG
);
4494 ERR("out of memory\n");
4495 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4499 dev
->devobj
= readobj
;
4500 dev
->fileobj
= fileobj
;
4501 RtlCopyMemory(&dev
->devitem
, &Vcb
->superblock
.dev_item
, sizeof(DEV_ITEM
));
4503 if (dev
->devitem
.num_bytes
> readobjsize
) {
4504 WARN("device %I64x: DEV_ITEM says %I64x bytes, but Windows only reports %I64x\n", dev
->devitem
.dev_id
,
4505 dev
->devitem
.num_bytes
, readobjsize
);
4507 dev
->devitem
.num_bytes
= readobjsize
;
4510 dev
->seeding
= Vcb
->superblock
.flags
& BTRFS_SUPERBLOCK_FLAGS_SEEDING
? true : false;
4512 init_device(Vcb
, dev
, true);
4514 InsertTailList(&Vcb
->devices
, &dev
->list_entry
);
4515 Vcb
->devices_loaded
= 1;
4517 if (DeviceToMount
->Flags
& DO_SYSTEM_BOOT_PARTITION
)
4518 Vcb
->disallow_dismount
= true;
4520 TRACE("DeviceToMount = %p\n", DeviceToMount
);
4521 TRACE("IrpSp->Parameters.MountVolume.Vpb = %p\n", IrpSp
->Parameters
.MountVolume
.Vpb
);
4523 NewDeviceObject
->StackSize
= DeviceToMount
->StackSize
+ 1;
4524 NewDeviceObject
->Flags
&= ~DO_DEVICE_INITIALIZING
;
4526 InitializeListHead(&Vcb
->roots
);
4527 InitializeListHead(&Vcb
->drop_roots
);
4529 Vcb
->log_to_phys_loaded
= false;
4531 add_root(Vcb
, BTRFS_ROOT_CHUNK
, Vcb
->superblock
.chunk_tree_addr
, Vcb
->superblock
.chunk_root_generation
, NULL
);
4533 if (!Vcb
->chunk_root
) {
4534 ERR("Could not load chunk root.\n");
4535 Status
= STATUS_INTERNAL_ERROR
;
4539 InitializeListHead(&Vcb
->sys_chunks
);
4540 Status
= load_sys_chunks(Vcb
);
4541 if (!NT_SUCCESS(Status
)) {
4542 ERR("load_sys_chunks returned %08x\n", Status
);
4546 InitializeListHead(&Vcb
->chunks
);
4547 InitializeListHead(&Vcb
->trees
);
4548 InitializeListHead(&Vcb
->trees_hash
);
4549 InitializeListHead(&Vcb
->all_fcbs
);
4550 InitializeListHead(&Vcb
->dirty_fcbs
);
4551 InitializeListHead(&Vcb
->dirty_filerefs
);
4552 InitializeListHead(&Vcb
->dirty_subvols
);
4553 InitializeListHead(&Vcb
->send_ops
);
4555 ExInitializeFastMutex(&Vcb
->trees_list_mutex
);
4557 InitializeListHead(&Vcb
->DirNotifyList
);
4558 InitializeListHead(&Vcb
->scrub
.errors
);
4560 FsRtlNotifyInitializeSync(&Vcb
->NotifySync
);
4562 ExInitializePagedLookasideList(&Vcb
->tree_data_lookaside
, NULL
, NULL
, 0, sizeof(tree_data
), ALLOC_TAG
, 0);
4563 ExInitializePagedLookasideList(&Vcb
->traverse_ptr_lookaside
, NULL
, NULL
, 0, sizeof(traverse_ptr
), ALLOC_TAG
, 0);
4564 ExInitializePagedLookasideList(&Vcb
->batch_item_lookaside
, NULL
, NULL
, 0, sizeof(batch_item
), ALLOC_TAG
, 0);
4565 ExInitializePagedLookasideList(&Vcb
->fileref_lookaside
, NULL
, NULL
, 0, sizeof(file_ref
), ALLOC_TAG
, 0);
4566 ExInitializePagedLookasideList(&Vcb
->fcb_lookaside
, NULL
, NULL
, 0, sizeof(fcb
), ALLOC_TAG
, 0);
4567 ExInitializePagedLookasideList(&Vcb
->name_bit_lookaside
, NULL
, NULL
, 0, sizeof(name_bit
), ALLOC_TAG
, 0);
4568 ExInitializeNPagedLookasideList(&Vcb
->range_lock_lookaside
, NULL
, NULL
, 0, sizeof(range_lock
), ALLOC_TAG
, 0);
4569 ExInitializeNPagedLookasideList(&Vcb
->fileref_np_lookaside
, NULL
, NULL
, 0, sizeof(file_ref_nonpaged
), ALLOC_TAG
, 0);
4570 ExInitializeNPagedLookasideList(&Vcb
->fcb_np_lookaside
, NULL
, NULL
, 0, sizeof(fcb_nonpaged
), ALLOC_TAG
, 0);
4571 init_lookaside
= true;
4573 Vcb
->Vpb
= IrpSp
->Parameters
.MountVolume
.Vpb
;
4575 Status
= load_chunk_root(Vcb
, Irp
);
4576 if (!NT_SUCCESS(Status
)) {
4577 ERR("load_chunk_root returned %08x\n", Status
);
4581 if (Vcb
->superblock
.num_devices
> 1) {
4582 if (Vcb
->devices_loaded
< Vcb
->superblock
.num_devices
&& (!Vcb
->options
.allow_degraded
|| !finished_probing
)) {
4583 ERR("could not mount as %u device(s) missing\n", Vcb
->superblock
.num_devices
- Vcb
->devices_loaded
);
4585 IoRaiseInformationalHardError(IO_ERR_INTERNAL_ERROR
, NULL
, NULL
);
4587 Status
= STATUS_INTERNAL_ERROR
;
4591 if (dev
->readonly
&& !Vcb
->readonly
) {
4592 Vcb
->readonly
= true;
4594 le
= Vcb
->devices
.Flink
;
4595 while (le
!= &Vcb
->devices
) {
4596 device
* dev2
= CONTAINING_RECORD(le
, device
, list_entry
);
4598 if (dev2
->readonly
&& !dev2
->seeding
)
4601 if (!dev2
->readonly
) {
4602 Vcb
->readonly
= false;
4610 WARN("setting volume to readonly\n");
4613 if (dev
->readonly
) {
4614 WARN("setting volume to readonly as device is readonly\n");
4615 Vcb
->readonly
= true;
4619 add_root(Vcb
, BTRFS_ROOT_ROOT
, Vcb
->superblock
.root_tree_addr
, Vcb
->superblock
.generation
- 1, NULL
);
4621 if (!Vcb
->root_root
) {
4622 ERR("Could not load root of roots.\n");
4623 Status
= STATUS_INTERNAL_ERROR
;
4627 Status
= look_for_roots(Vcb
, Irp
);
4628 if (!NT_SUCCESS(Status
)) {
4629 ERR("look_for_roots returned %08x\n", Status
);
4633 if (!Vcb
->readonly
) {
4634 Status
= find_chunk_usage(Vcb
, Irp
);
4635 if (!NT_SUCCESS(Status
)) {
4636 ERR("find_chunk_usage returned %08x\n", Status
);
4641 InitializeListHead(&batchlist
);
4643 // We've already increased the generation by one
4644 if (!Vcb
->readonly
&& (
4645 Vcb
->options
.clear_cache
||
4646 (!(Vcb
->superblock
.compat_ro_flags
& BTRFS_COMPAT_RO_FLAGS_FREE_SPACE_CACHE
) && Vcb
->superblock
.generation
- 1 != Vcb
->superblock
.cache_generation
) ||
4647 (Vcb
->superblock
.compat_ro_flags
& BTRFS_COMPAT_RO_FLAGS_FREE_SPACE_CACHE
&& !(Vcb
->superblock
.compat_ro_flags
& BTRFS_COMPAT_RO_FLAGS_FREE_SPACE_CACHE_VALID
)))) {
4648 if (Vcb
->options
.clear_cache
)
4649 WARN("ClearCache option was set, clearing cache...\n");
4650 else if (Vcb
->superblock
.compat_ro_flags
& BTRFS_COMPAT_RO_FLAGS_FREE_SPACE_CACHE
&& !(Vcb
->superblock
.compat_ro_flags
& BTRFS_COMPAT_RO_FLAGS_FREE_SPACE_CACHE_VALID
))
4651 WARN("clearing free-space tree created by buggy Linux driver\n");
4653 WARN("generation was %I64x, free-space cache generation was %I64x; clearing cache...\n", Vcb
->superblock
.generation
- 1, Vcb
->superblock
.cache_generation
);
4655 Status
= clear_free_space_cache(Vcb
, &batchlist
, Irp
);
4656 if (!NT_SUCCESS(Status
)) {
4657 ERR("clear_free_space_cache returned %08x\n", Status
);
4658 clear_batch_list(Vcb
, &batchlist
);
4663 Status
= commit_batch_list(Vcb
, &batchlist
, Irp
);
4664 if (!NT_SUCCESS(Status
)) {
4665 ERR("commit_batch_list returned %08x\n", Status
);
4669 Vcb
->volume_fcb
= create_fcb(Vcb
, NonPagedPool
);
4670 if (!Vcb
->volume_fcb
) {
4671 ERR("out of memory\n");
4672 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4676 Vcb
->volume_fcb
->Vcb
= Vcb
;
4677 Vcb
->volume_fcb
->sd
= NULL
;
4679 Vcb
->dummy_fcb
= create_fcb(Vcb
, NonPagedPool
);
4680 if (!Vcb
->dummy_fcb
) {
4681 ERR("out of memory\n");
4682 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4686 Vcb
->dummy_fcb
->Vcb
= Vcb
;
4687 Vcb
->dummy_fcb
->type
= BTRFS_TYPE_DIRECTORY
;
4688 Vcb
->dummy_fcb
->inode
= 2;
4689 Vcb
->dummy_fcb
->subvol
= Vcb
->root_root
;
4690 Vcb
->dummy_fcb
->atts
= FILE_ATTRIBUTE_DIRECTORY
;
4691 Vcb
->dummy_fcb
->inode_item
.st_nlink
= 1;
4692 Vcb
->dummy_fcb
->inode_item
.st_mode
= __S_IFDIR
;
4694 Vcb
->dummy_fcb
->hash_ptrs
= ExAllocatePoolWithTag(PagedPool
, sizeof(LIST_ENTRY
*) * 256, ALLOC_TAG
);
4695 if (!Vcb
->dummy_fcb
->hash_ptrs
) {
4696 ERR("out of memory\n");
4697 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4701 RtlZeroMemory(Vcb
->dummy_fcb
->hash_ptrs
, sizeof(LIST_ENTRY
*) * 256);
4703 Vcb
->dummy_fcb
->hash_ptrs_uc
= ExAllocatePoolWithTag(PagedPool
, sizeof(LIST_ENTRY
*) * 256, ALLOC_TAG
);
4704 if (!Vcb
->dummy_fcb
->hash_ptrs_uc
) {
4705 ERR("out of memory\n");
4706 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4710 RtlZeroMemory(Vcb
->dummy_fcb
->hash_ptrs_uc
, sizeof(LIST_ENTRY
*) * 256);
4712 root_fcb
= create_fcb(Vcb
, NonPagedPool
);
4714 ERR("out of memory\n");
4715 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4719 root_fcb
->Vcb
= Vcb
;
4720 root_fcb
->inode
= SUBVOL_ROOT_INODE
;
4721 root_fcb
->hash
= calc_crc32c(0xffffffff, (uint8_t*)&root_fcb
->inode
, sizeof(uint64_t));
4722 root_fcb
->type
= BTRFS_TYPE_DIRECTORY
;
4724 #ifdef DEBUG_FCB_REFCOUNTS
4725 WARN("volume FCB = %p\n", Vcb
->volume_fcb
);
4726 WARN("root FCB = %p\n", root_fcb
);
4729 root_fcb
->subvol
= find_default_subvol(Vcb
, Irp
);
4731 if (!root_fcb
->subvol
) {
4732 ERR("could not find top subvol\n");
4733 Status
= STATUS_INTERNAL_ERROR
;
4737 Status
= load_dir_children(Vcb
, root_fcb
, true, Irp
);
4738 if (!NT_SUCCESS(Status
)) {
4739 ERR("load_dir_children returned %08x\n", Status
);
4743 searchkey
.obj_id
= root_fcb
->inode
;
4744 searchkey
.obj_type
= TYPE_INODE_ITEM
;
4745 searchkey
.offset
= 0xffffffffffffffff;
4747 Status
= find_item(Vcb
, root_fcb
->subvol
, &tp
, &searchkey
, false, Irp
);
4748 if (!NT_SUCCESS(Status
)) {
4749 ERR("error - find_item returned %08x\n", Status
);
4753 if (tp
.item
->key
.obj_id
!= searchkey
.obj_id
|| tp
.item
->key
.obj_type
!= searchkey
.obj_type
) {
4754 ERR("couldn't find INODE_ITEM for root directory\n");
4755 Status
= STATUS_INTERNAL_ERROR
;
4759 if (tp
.item
->size
> 0)
4760 RtlCopyMemory(&root_fcb
->inode_item
, tp
.item
->data
, min(sizeof(INODE_ITEM
), tp
.item
->size
));
4762 fcb_get_sd(root_fcb
, NULL
, true, Irp
);
4764 root_fcb
->atts
= get_file_attributes(Vcb
, root_fcb
->subvol
, root_fcb
->inode
, root_fcb
->type
, false, false, Irp
);
4766 Vcb
->root_fileref
= create_fileref(Vcb
);
4767 if (!Vcb
->root_fileref
) {
4768 ERR("out of memory\n");
4769 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4773 Vcb
->root_fileref
->fcb
= root_fcb
;
4774 InsertTailList(&root_fcb
->subvol
->fcbs
, &root_fcb
->list_entry
);
4775 InsertTailList(&Vcb
->all_fcbs
, &root_fcb
->list_entry_all
);
4777 root_fcb
->subvol
->fcbs_ptrs
[root_fcb
->hash
>> 24] = &root_fcb
->list_entry
;
4779 root_fcb
->fileref
= Vcb
->root_fileref
;
4781 root_ccb
= ExAllocatePoolWithTag(PagedPool
, sizeof(ccb
), ALLOC_TAG
);
4783 ERR("out of memory\n");
4784 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4788 /* HACK: stream file object seems to get deleted at some point
4789 * leading to use after free when installing ReactOS on
4791 * Workaround: leak a handle to the fileobject
4792 * XXX: Could be improved by storing it somewhere and releasing it
4793 * on dismount. Or even by referencing again the file object.
4796 Vcb
->root_file
= IoCreateStreamFileObject(NULL
, DeviceToMount
);
4800 Vcb
->root_file
= IoCreateStreamFileObjectEx(NULL
, DeviceToMount
, &Dummy
);
4803 Vcb
->root_file
->FsContext
= root_fcb
;
4804 Vcb
->root_file
->SectionObjectPointer
= &root_fcb
->nonpaged
->segment_object
;
4805 Vcb
->root_file
->Vpb
= DeviceObject
->Vpb
;
4807 RtlZeroMemory(root_ccb
, sizeof(ccb
));
4808 root_ccb
->NodeType
= BTRFS_NODE_TYPE_CCB
;
4809 root_ccb
->NodeSize
= sizeof(ccb
);
4811 Vcb
->root_file
->FsContext2
= root_ccb
;
4814 CcInitializeCacheMap(Vcb
->root_file
, (PCC_FILE_SIZES
)(&root_fcb
->Header
.AllocationSize
), false, cache_callbacks
, Vcb
->root_file
);
4815 } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER
) {
4816 Status
= _SEH2_GetExceptionCode();
4820 le
= Vcb
->devices
.Flink
;
4821 while (le
!= &Vcb
->devices
) {
4822 device
* dev2
= CONTAINING_RECORD(le
, device
, list_entry
);
4824 Status
= find_disk_holes(Vcb
, dev2
, Irp
);
4825 if (!NT_SUCCESS(Status
)) {
4826 ERR("find_disk_holes returned %08x\n", Status
);
4833 NewDeviceObject
->Vpb
= IrpSp
->Parameters
.MountVolume
.Vpb
;
4834 IrpSp
->Parameters
.MountVolume
.Vpb
->DeviceObject
= NewDeviceObject
;
4835 IrpSp
->Parameters
.MountVolume
.Vpb
->Flags
|= VPB_MOUNTED
;
4836 NewDeviceObject
->Vpb
->VolumeLabelLength
= 4; // FIXME
4837 NewDeviceObject
->Vpb
->VolumeLabel
[0] = '?';
4838 NewDeviceObject
->Vpb
->VolumeLabel
[1] = 0;
4839 NewDeviceObject
->Vpb
->ReferenceCount
++; // FIXME - should we deref this at any point?
4841 KeInitializeEvent(&Vcb
->flush_thread_finished
, NotificationEvent
, false);
4843 Status
= PsCreateSystemThread(&Vcb
->flush_thread_handle
, 0, NULL
, NULL
, NULL
, flush_thread
, NewDeviceObject
);
4844 if (!NT_SUCCESS(Status
)) {
4845 ERR("PsCreateSystemThread returned %08x\n", Status
);
4849 Status
= create_calc_threads(NewDeviceObject
);
4850 if (!NT_SUCCESS(Status
)) {
4851 ERR("create_calc_threads returned %08x\n", Status
);
4855 Status
= registry_mark_volume_mounted(&Vcb
->superblock
.uuid
);
4856 if (!NT_SUCCESS(Status
))
4857 WARN("registry_mark_volume_mounted returned %08x\n", Status
);
4859 Status
= look_for_balance_item(Vcb
);
4860 if (!NT_SUCCESS(Status
) && Status
!= STATUS_NOT_FOUND
)
4861 WARN("look_for_balance_item returned %08x\n", Status
);
4863 Status
= STATUS_SUCCESS
;
4866 vde
->mounted_device
= NewDeviceObject
;
4868 ExInitializeResourceLite(&Vcb
->send_load_lock
);
4872 ExReleaseResourceLite(&pdode
->child_lock
);
4876 ExReleaseResourceLite(&Vcb
->tree_lock
);
4877 ExReleaseResourceLite(&Vcb
->load_lock
);
4880 if (!NT_SUCCESS(Status
)) {
4882 if (init_lookaside
) {
4883 ExDeletePagedLookasideList(&Vcb
->tree_data_lookaside
);
4884 ExDeletePagedLookasideList(&Vcb
->traverse_ptr_lookaside
);
4885 ExDeletePagedLookasideList(&Vcb
->batch_item_lookaside
);
4886 ExDeletePagedLookasideList(&Vcb
->fileref_lookaside
);
4887 ExDeletePagedLookasideList(&Vcb
->fcb_lookaside
);
4888 ExDeletePagedLookasideList(&Vcb
->name_bit_lookaside
);
4889 ExDeleteNPagedLookasideList(&Vcb
->range_lock_lookaside
);
4890 ExDeleteNPagedLookasideList(&Vcb
->fileref_np_lookaside
);
4891 ExDeleteNPagedLookasideList(&Vcb
->fcb_np_lookaside
);
4895 ObDereferenceObject(Vcb
->root_file
);
4896 else if (Vcb
->root_fileref
)
4897 free_fileref(Vcb
->root_fileref
);
4901 if (root_fcb
&& root_fcb
->refcount
== 0)
4904 if (Vcb
->volume_fcb
)
4905 reap_fcb(Vcb
->volume_fcb
);
4907 ExDeleteResourceLite(&Vcb
->tree_lock
);
4908 ExDeleteResourceLite(&Vcb
->load_lock
);
4909 ExDeleteResourceLite(&Vcb
->fcb_lock
);
4910 ExDeleteResourceLite(&Vcb
->fileref_lock
);
4911 ExDeleteResourceLite(&Vcb
->chunk_lock
);
4912 ExDeleteResourceLite(&Vcb
->dirty_fcbs_lock
);
4913 ExDeleteResourceLite(&Vcb
->dirty_filerefs_lock
);
4914 ExDeleteResourceLite(&Vcb
->dirty_subvols_lock
);
4915 ExDeleteResourceLite(&Vcb
->scrub
.stats_lock
);
4917 if (Vcb
->devices
.Flink
) {
4918 while (!IsListEmpty(&Vcb
->devices
)) {
4919 device
* dev2
= CONTAINING_RECORD(RemoveHeadList(&Vcb
->devices
), device
, list_entry
);
4926 if (NewDeviceObject
)
4927 IoDeleteDevice(NewDeviceObject
);
4929 ExAcquireResourceExclusiveLite(&global_loading_lock
, true);
4930 InsertTailList(&VcbList
, &Vcb
->list_entry
);
4931 ExReleaseResourceLite(&global_loading_lock
);
4933 FsRtlNotifyVolumeEvent(Vcb
->root_file
, FSRTL_VOLUME_MOUNT
);
4936 TRACE("mount_vol done (status: %lx)\n", Status
);
4941 static NTSTATUS
verify_device(_In_ device_extension
* Vcb
, _Inout_ device
* dev
) {
4948 return STATUS_WRONG_VOLUME
;
4950 if (dev
->removable
) {
4951 IO_STATUS_BLOCK iosb
;
4953 Status
= dev_ioctl(dev
->devobj
, IOCTL_STORAGE_CHECK_VERIFY
, NULL
, 0, &cc
, sizeof(ULONG
), true, &iosb
);
4955 if (IoIsErrorUserInduced(Status
)) {
4956 ERR("IOCTL_STORAGE_CHECK_VERIFY returned %08x (user-induced)\n", Status
);
4959 pdo_device_extension
* pdode
= Vcb
->vde
->pdode
;
4961 bool changed
= false;
4963 ExAcquireResourceExclusiveLite(&pdode
->child_lock
, true);
4965 le2
= pdode
->children
.Flink
;
4966 while (le2
!= &pdode
->children
) {
4967 volume_child
* vc
= CONTAINING_RECORD(le2
, volume_child
, list_entry
);
4969 if (vc
->devobj
== dev
->devobj
) {
4970 TRACE("removing device\n");
4972 remove_volume_child(Vcb
->vde
, vc
, true);
4982 ExReleaseResourceLite(&pdode
->child_lock
);
4984 } else if (!NT_SUCCESS(Status
)) {
4985 ERR("IOCTL_STORAGE_CHECK_VERIFY returned %08x\n", Status
);
4987 } else if (iosb
.Information
< sizeof(ULONG
)) {
4988 ERR("iosb.Information was too short\n");
4989 return STATUS_INTERNAL_ERROR
;
4992 dev
->change_count
= cc
;
4995 to_read
= dev
->devobj
->SectorSize
== 0 ? sizeof(superblock
) : (ULONG
)sector_align(sizeof(superblock
), dev
->devobj
->SectorSize
);
4997 sb
= ExAllocatePoolWithTag(NonPagedPool
, to_read
, ALLOC_TAG
);
4999 ERR("out of memory\n");
5000 return STATUS_INSUFFICIENT_RESOURCES
;
5003 Status
= sync_read_phys(dev
->devobj
, dev
->fileobj
, superblock_addrs
[0], to_read
, (PUCHAR
)sb
, true);
5004 if (!NT_SUCCESS(Status
)) {
5005 ERR("Failed to read superblock: %08x\n", Status
);
5010 if (sb
->magic
!= BTRFS_MAGIC
) {
5011 ERR("not a BTRFS volume\n");
5013 return STATUS_WRONG_VOLUME
;
5016 crc32
= ~calc_crc32c(0xffffffff, (uint8_t*)&sb
->uuid
, (ULONG
)sizeof(superblock
) - sizeof(sb
->checksum
));
5017 TRACE("crc32 was %08x, expected %08x\n", crc32
, *((uint32_t*)sb
->checksum
));
5019 if (crc32
!= *((uint32_t*)sb
->checksum
)) {
5020 ERR("checksum error\n");
5022 return STATUS_WRONG_VOLUME
;
5025 if (RtlCompareMemory(&sb
->uuid
, &Vcb
->superblock
.uuid
, sizeof(BTRFS_UUID
)) != sizeof(BTRFS_UUID
)) {
5026 ERR("different UUIDs\n");
5028 return STATUS_WRONG_VOLUME
;
5033 dev
->devobj
->Flags
&= ~DO_VERIFY_VOLUME
;
5035 return STATUS_SUCCESS
;
5038 static NTSTATUS
verify_volume(_In_ PDEVICE_OBJECT devobj
) {
5039 device_extension
* Vcb
= devobj
->DeviceExtension
;
5042 uint64_t failed_devices
= 0;
5043 bool locked
= false, remove
= false;
5045 if (!(Vcb
->Vpb
->Flags
& VPB_MOUNTED
))
5046 return STATUS_WRONG_VOLUME
;
5048 if (!ExIsResourceAcquiredExclusive(&Vcb
->tree_lock
)) {
5049 ExAcquireResourceExclusiveLite(&Vcb
->tree_lock
, true);
5053 if (Vcb
->removing
) {
5054 if (locked
) ExReleaseResourceLite(&Vcb
->tree_lock
);
5055 return STATUS_WRONG_VOLUME
;
5058 InterlockedIncrement(&Vcb
->open_files
); // so pnp_surprise_removal doesn't uninit the device while we're still using it
5060 le
= Vcb
->devices
.Flink
;
5061 while (le
!= &Vcb
->devices
) {
5062 device
* dev
= CONTAINING_RECORD(le
, device
, list_entry
);
5064 Status
= verify_device(Vcb
, dev
);
5065 if (!NT_SUCCESS(Status
)) {
5068 if (dev
->devobj
&& Vcb
->options
.allow_degraded
)
5075 InterlockedDecrement(&Vcb
->open_files
);
5077 if (Vcb
->removing
&& Vcb
->open_files
== 0)
5081 ExReleaseResourceLite(&Vcb
->tree_lock
);
5088 if (failed_devices
== 0 || (Vcb
->options
.allow_degraded
&& failed_devices
< Vcb
->superblock
.num_devices
)) {
5089 Vcb
->Vpb
->RealDevice
->Flags
&= ~DO_VERIFY_VOLUME
;
5091 return STATUS_SUCCESS
;
5097 _Dispatch_type_(IRP_MJ_FILE_SYSTEM_CONTROL
)
5098 _Function_class_(DRIVER_DISPATCH
)
5099 static NTSTATUS __stdcall
drv_file_system_control(_In_ PDEVICE_OBJECT DeviceObject
, _In_ PIRP Irp
) {
5100 PIO_STACK_LOCATION IrpSp
;
5102 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
5105 FsRtlEnterFileSystem();
5107 TRACE("file system control\n");
5109 top_level
= is_top_level(Irp
);
5111 if (Vcb
&& Vcb
->type
== VCB_TYPE_VOLUME
) {
5112 Status
= vol_file_system_control(DeviceObject
, Irp
);
5114 } else if (!Vcb
|| (Vcb
->type
!= VCB_TYPE_FS
&& Vcb
->type
!= VCB_TYPE_CONTROL
)) {
5115 Status
= STATUS_INVALID_PARAMETER
;
5119 Status
= STATUS_NOT_IMPLEMENTED
;
5121 IrpSp
= IoGetCurrentIrpStackLocation( Irp
);
5123 Irp
->IoStatus
.Information
= 0;
5125 switch (IrpSp
->MinorFunction
) {
5126 case IRP_MN_MOUNT_VOLUME
:
5127 TRACE("IRP_MN_MOUNT_VOLUME\n");
5129 Status
= mount_vol(DeviceObject
, Irp
);
5132 case IRP_MN_KERNEL_CALL
:
5133 TRACE("IRP_MN_KERNEL_CALL\n");
5135 Status
= fsctl_request(DeviceObject
, &Irp
, IrpSp
->Parameters
.FileSystemControl
.FsControlCode
);
5138 case IRP_MN_USER_FS_REQUEST
:
5139 TRACE("IRP_MN_USER_FS_REQUEST\n");
5141 Status
= fsctl_request(DeviceObject
, &Irp
, IrpSp
->Parameters
.FileSystemControl
.FsControlCode
);
5144 case IRP_MN_VERIFY_VOLUME
:
5145 TRACE("IRP_MN_VERIFY_VOLUME\n");
5147 Status
= verify_volume(DeviceObject
);
5149 if (!NT_SUCCESS(Status
) && Vcb
->Vpb
->Flags
& VPB_MOUNTED
) {
5150 ExAcquireResourceExclusiveLite(&Vcb
->tree_lock
, true);
5151 Vcb
->removing
= true;
5152 ExReleaseResourceLite(&Vcb
->tree_lock
);
5162 TRACE("returning %08x\n", Status
);
5165 Irp
->IoStatus
.Status
= Status
;
5167 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
5171 IoSetTopLevelIrp(NULL
);
5173 FsRtlExitFileSystem();
5178 _Dispatch_type_(IRP_MJ_LOCK_CONTROL
)
5179 _Function_class_(DRIVER_DISPATCH
)
5180 static NTSTATUS __stdcall
drv_lock_control(_In_ PDEVICE_OBJECT DeviceObject
, _In_ PIRP Irp
) {
5182 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
5183 fcb
* fcb
= IrpSp
->FileObject
->FsContext
;
5184 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
5187 FsRtlEnterFileSystem();
5189 top_level
= is_top_level(Irp
);
5191 if (Vcb
&& Vcb
->type
== VCB_TYPE_VOLUME
) {
5192 Status
= vol_lock_control(DeviceObject
, Irp
);
5194 Irp
->IoStatus
.Status
= Status
;
5195 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
5200 TRACE("lock control\n");
5202 Status
= FsRtlProcessFileLock(&fcb
->lock
, Irp
, NULL
);
5204 fcb
->Header
.IsFastIoPossible
= fast_io_possible(fcb
);
5207 TRACE("returning %08x\n", Status
);
5210 IoSetTopLevelIrp(NULL
);
5212 FsRtlExitFileSystem();
5217 _Dispatch_type_(IRP_MJ_SHUTDOWN
)
5218 _Function_class_(DRIVER_DISPATCH
)
5219 static NTSTATUS __stdcall
drv_shutdown(_In_ PDEVICE_OBJECT DeviceObject
, _In_ PIRP Irp
) {
5222 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
5225 FsRtlEnterFileSystem();
5227 TRACE("shutdown\n");
5229 top_level
= is_top_level(Irp
);
5231 if (Vcb
&& Vcb
->type
== VCB_TYPE_VOLUME
) {
5232 Status
= vol_shutdown(DeviceObject
, Irp
);
5236 Status
= STATUS_SUCCESS
;
5238 shutting_down
= true;
5239 KeSetEvent(&mountmgr_thread_event
, 0, false);
5242 while (le
!= &VcbList
) {
5244 LIST_ENTRY
* le2
= le
->Flink
;
5246 Vcb
= CONTAINING_RECORD(le
, device_extension
, list_entry
);
5248 TRACE("shutting down Vcb %p\n", Vcb
);
5250 ExAcquireResourceExclusiveLite(&Vcb
->tree_lock
, true);
5251 Vcb
->removing
= true;
5252 open_files
= Vcb
->open_files
> 0;
5254 if (Vcb
->need_write
&& !Vcb
->readonly
) {
5255 Status
= do_write(Vcb
, Irp
);
5256 if (!NT_SUCCESS(Status
))
5257 ERR("do_write returned %08x\n", Status
);
5262 ExReleaseResourceLite(&Vcb
->tree_lock
);
5272 ObDereferenceObject(comfo
);
5279 Irp
->IoStatus
.Status
= Status
;
5280 Irp
->IoStatus
.Information
= 0;
5282 IoCompleteRequest( Irp
, IO_NO_INCREMENT
);
5285 IoSetTopLevelIrp(NULL
);
5287 FsRtlExitFileSystem();
5292 _Dispatch_type_(IRP_MJ_POWER
)
5293 _Function_class_(DRIVER_DISPATCH
)
5294 static NTSTATUS __stdcall
drv_power(_In_ PDEVICE_OBJECT DeviceObject
, _In_ PIRP Irp
) {
5296 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
5297 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
5300 FsRtlEnterFileSystem();
5302 top_level
= is_top_level(Irp
);
5304 Irp
->IoStatus
.Information
= 0;
5306 if (Vcb
&& Vcb
->type
== VCB_TYPE_VOLUME
) {
5307 volume_device_extension
* vde
= DeviceObject
->DeviceExtension
;
5309 PoStartNextPowerIrp(Irp
);
5310 IoSkipCurrentIrpStackLocation(Irp
);
5311 Status
= PoCallDriver(vde
->attached_device
, Irp
);
5314 } else if (Vcb
&& Vcb
->type
== VCB_TYPE_FS
) {
5315 IoSkipCurrentIrpStackLocation(Irp
);
5317 Status
= IoCallDriver(Vcb
->Vpb
->RealDevice
, Irp
);
5320 } else if (Vcb
&& Vcb
->type
== VCB_TYPE_BUS
) {
5321 bus_device_extension
* bde
= DeviceObject
->DeviceExtension
;
5323 PoStartNextPowerIrp(Irp
);
5324 IoSkipCurrentIrpStackLocation(Irp
);
5325 Status
= PoCallDriver(bde
->attached_device
, Irp
);
5330 if (IrpSp
->MinorFunction
== IRP_MN_SET_POWER
|| IrpSp
->MinorFunction
== IRP_MN_QUERY_POWER
)
5331 Irp
->IoStatus
.Status
= STATUS_SUCCESS
;
5333 Status
= Irp
->IoStatus
.Status
;
5335 PoStartNextPowerIrp(Irp
);
5337 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
5341 IoSetTopLevelIrp(NULL
);
5343 FsRtlExitFileSystem();
5348 _Dispatch_type_(IRP_MJ_SYSTEM_CONTROL
)
5349 _Function_class_(DRIVER_DISPATCH
)
5350 static NTSTATUS __stdcall
drv_system_control(_In_ PDEVICE_OBJECT DeviceObject
, _In_ PIRP Irp
) {
5352 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
5355 FsRtlEnterFileSystem();
5357 top_level
= is_top_level(Irp
);
5359 Irp
->IoStatus
.Information
= 0;
5361 if (Vcb
&& Vcb
->type
== VCB_TYPE_VOLUME
) {
5362 volume_device_extension
* vde
= DeviceObject
->DeviceExtension
;
5364 IoSkipCurrentIrpStackLocation(Irp
);
5366 Status
= IoCallDriver(vde
->attached_device
, Irp
);
5369 } else if (Vcb
&& Vcb
->type
== VCB_TYPE_FS
) {
5370 IoSkipCurrentIrpStackLocation(Irp
);
5372 Status
= IoCallDriver(Vcb
->Vpb
->RealDevice
, Irp
);
5375 } else if (Vcb
&& Vcb
->type
== VCB_TYPE_BUS
) {
5376 bus_device_extension
* bde
= DeviceObject
->DeviceExtension
;
5378 IoSkipCurrentIrpStackLocation(Irp
);
5380 Status
= IoCallDriver(bde
->attached_device
, Irp
);
5385 Status
= Irp
->IoStatus
.Status
;
5386 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
5390 IoSetTopLevelIrp(NULL
);
5392 FsRtlExitFileSystem();
5397 bool is_file_name_valid(_In_ PUNICODE_STRING us
, _In_
bool posix
) {
5400 if (us
->Length
< sizeof(WCHAR
))
5403 if (us
->Length
> 255 * sizeof(WCHAR
))
5406 for (i
= 0; i
< us
->Length
/ sizeof(WCHAR
); i
++) {
5407 if (us
->Buffer
[i
] == '/' || us
->Buffer
[i
] == 0 ||
5408 (!posix
&& (us
->Buffer
[i
] == '<' || us
->Buffer
[i
] == '>' || us
->Buffer
[i
] == ':' || us
->Buffer
[i
] == '"' ||
5409 us
->Buffer
[i
] == '|' || us
->Buffer
[i
] == '?' || us
->Buffer
[i
] == '*' || (us
->Buffer
[i
] >= 1 && us
->Buffer
[i
] <= 31))))
5413 if (us
->Buffer
[0] == '.' && (us
->Length
== sizeof(WCHAR
) || (us
->Length
== 2 * sizeof(WCHAR
) && us
->Buffer
[1] == '.')))
5419 void chunk_lock_range(_In_ device_extension
* Vcb
, _In_ chunk
* c
, _In_
uint64_t start
, _In_
uint64_t length
) {
5424 rl
= ExAllocateFromNPagedLookasideList(&Vcb
->range_lock_lookaside
);
5426 ERR("out of memory\n");
5431 rl
->length
= length
;
5432 rl
->thread
= PsGetCurrentThread();
5437 ExAcquireResourceExclusiveLite(&c
->range_locks_lock
, true);
5439 le
= c
->range_locks
.Flink
;
5440 while (le
!= &c
->range_locks
) {
5441 range_lock
* rl2
= CONTAINING_RECORD(le
, range_lock
, list_entry
);
5443 if (rl2
->start
< start
+ length
&& rl2
->start
+ rl2
->length
> start
&& rl2
->thread
!= PsGetCurrentThread()) {
5452 InsertTailList(&c
->range_locks
, &rl
->list_entry
);
5454 ExReleaseResourceLite(&c
->range_locks_lock
);
5458 KeClearEvent(&c
->range_locks_event
);
5460 ExReleaseResourceLite(&c
->range_locks_lock
);
5462 KeWaitForSingleObject(&c
->range_locks_event
, UserRequest
, KernelMode
, false, NULL
);
5466 void chunk_unlock_range(_In_ device_extension
* Vcb
, _In_ chunk
* c
, _In_
uint64_t start
, _In_
uint64_t length
) {
5469 ExAcquireResourceExclusiveLite(&c
->range_locks_lock
, true);
5471 le
= c
->range_locks
.Flink
;
5472 while (le
!= &c
->range_locks
) {
5473 range_lock
* rl
= CONTAINING_RECORD(le
, range_lock
, list_entry
);
5475 if (rl
->start
== start
&& rl
->length
== length
) {
5476 RemoveEntryList(&rl
->list_entry
);
5477 ExFreeToNPagedLookasideList(&Vcb
->range_lock_lookaside
, rl
);
5484 KeSetEvent(&c
->range_locks_event
, 0, false);
5486 ExReleaseResourceLite(&c
->range_locks_lock
);
5489 void log_device_error(_In_ device_extension
* Vcb
, _Inout_ device
* dev
, _In_
int error
) {
5490 dev
->stats
[error
]++;
5491 dev
->stats_changed
= true;
5492 Vcb
->stats_changed
= true;
5496 _Function_class_(KSTART_ROUTINE
)
5497 static void __stdcall
serial_thread(void* context
) {
5498 LARGE_INTEGER due_time
;
5503 KeInitializeTimer(&timer
);
5505 due_time
.QuadPart
= (uint64_t)-10000000;
5507 KeSetTimer(&timer
, due_time
, NULL
);
5510 KeWaitForSingleObject(&timer
, Executive
, KernelMode
, false, NULL
);
5517 KeSetTimer(&timer
, due_time
, NULL
);
5520 KeCancelTimer(&timer
);
5522 PsTerminateSystemThread(STATUS_SUCCESS
);
5524 serial_thread_handle
= NULL
;
5527 static void init_serial(bool first_time
) {
5530 Status
= IoGetDeviceObjectPointer(&log_device
, FILE_WRITE_DATA
, &comfo
, &comdo
);
5531 if (!NT_SUCCESS(Status
)) {
5532 ERR("IoGetDeviceObjectPointer returned %08x\n", Status
);
5535 Status
= PsCreateSystemThread(&serial_thread_handle
, 0, NULL
, NULL
, NULL
, serial_thread
, NULL
);
5536 if (!NT_SUCCESS(Status
)) {
5537 ERR("PsCreateSystemThread returned %08x\n", Status
);
5546 static void check_cpu() {
5547 unsigned int cpuInfo
[4];
5549 __get_cpuid(1, &cpuInfo
[0], &cpuInfo
[1], &cpuInfo
[2], &cpuInfo
[3]);
5550 have_sse42
= cpuInfo
[2] & bit_SSE4_2
;
5551 have_sse2
= cpuInfo
[3] & bit_SSE2
;
5553 __cpuid(cpuInfo
, 1);
5554 have_sse42
= cpuInfo
[2] & (1 << 20);
5555 have_sse2
= cpuInfo
[3] & (1 << 26);
5559 TRACE("SSE4.2 is supported\n");
5561 TRACE("SSE4.2 not supported\n");
5564 TRACE("SSE2 is supported\n");
5566 TRACE("SSE2 is not supported\n");
5571 static void init_logging() {
5572 ExAcquireResourceExclusiveLite(&log_lock
, true);
5574 if (log_device
.Length
> 0)
5576 else if (log_file
.Length
> 0) {
5578 OBJECT_ATTRIBUTES oa
;
5579 IO_STATUS_BLOCK iosb
;
5584 InitializeObjectAttributes(&oa
, &log_file
, OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
, NULL
, NULL
);
5586 Status
= ZwCreateFile(&log_handle
, FILE_WRITE_DATA
, &oa
, &iosb
, NULL
, FILE_ATTRIBUTE_NORMAL
, FILE_SHARE_READ
,
5587 FILE_OPEN_IF
, FILE_NON_DIRECTORY_FILE
| FILE_WRITE_THROUGH
| FILE_SYNCHRONOUS_IO_ALERT
, NULL
, 0);
5589 if (!NT_SUCCESS(Status
)) {
5590 ERR("ZwCreateFile returned %08x\n", Status
);
5594 if (iosb
.Information
== FILE_OPENED
) { // already exists
5595 FILE_STANDARD_INFORMATION fsi
;
5596 FILE_POSITION_INFORMATION fpi
;
5598 static const char delim
[] = "\n---\n";
5600 // move to end of file
5602 Status
= ZwQueryInformationFile(log_handle
, &iosb
, &fsi
, sizeof(FILE_STANDARD_INFORMATION
), FileStandardInformation
);
5604 if (!NT_SUCCESS(Status
)) {
5605 ERR("ZwQueryInformationFile returned %08x\n", Status
);
5609 fpi
.CurrentByteOffset
= fsi
.EndOfFile
;
5611 Status
= ZwSetInformationFile(log_handle
, &iosb
, &fpi
, sizeof(FILE_POSITION_INFORMATION
), FilePositionInformation
);
5613 if (!NT_SUCCESS(Status
)) {
5614 ERR("ZwSetInformationFile returned %08x\n", Status
);
5618 Status
= ZwWriteFile(log_handle
, NULL
, NULL
, NULL
, &iosb
, (void*)delim
, sizeof(delim
) - 1, NULL
, NULL
);
5620 if (!NT_SUCCESS(Status
)) {
5621 ERR("ZwWriteFile returned %08x\n", Status
);
5626 dateline
= ExAllocatePoolWithTag(PagedPool
, 256, ALLOC_TAG
);
5629 ERR("out of memory\n");
5633 KeQuerySystemTime(&time
);
5635 RtlTimeToTimeFields(&time
, &tf
);
5637 sprintf(dateline
, "Starting logging at %04i-%02i-%02i %02i:%02i:%02i\n", tf
.Year
, tf
.Month
, tf
.Day
, tf
.Hour
, tf
.Minute
, tf
.Second
);
5639 Status
= ZwWriteFile(log_handle
, NULL
, NULL
, NULL
, &iosb
, dateline
, (ULONG
)strlen(dateline
), NULL
, NULL
);
5641 ExFreePool(dateline
);
5643 if (!NT_SUCCESS(Status
)) {
5644 ERR("ZwWriteFile returned %08x\n", Status
);
5650 ExReleaseResourceLite(&log_lock
);
5654 _Function_class_(KSTART_ROUTINE
)
5655 static void __stdcall
degraded_wait_thread(_In_
void* context
) {
5657 LARGE_INTEGER delay
;
5661 KeInitializeTimer(&timer
);
5663 delay
.QuadPart
= -30000000; // wait three seconds
5664 KeSetTimer(&timer
, delay
, NULL
);
5665 KeWaitForSingleObject(&timer
, Executive
, KernelMode
, false, NULL
);
5667 TRACE("timer expired\n");
5669 degraded_wait
= false;
5671 ZwClose(degraded_wait_handle
);
5672 degraded_wait_handle
= NULL
;
5674 PsTerminateSystemThread(STATUS_SUCCESS
);
5677 _Function_class_(DRIVER_ADD_DEVICE
)
5678 NTSTATUS __stdcall
AddDevice(PDRIVER_OBJECT DriverObject
, PDEVICE_OBJECT PhysicalDeviceObject
) {
5681 UNICODE_STRING volname
;
5683 pdo_device_extension
* pdode
= NULL
;
5684 PDEVICE_OBJECT voldev
;
5685 volume_device_extension
* vde
;
5687 TRACE("(%p, %p)\n", DriverObject
, PhysicalDeviceObject
);
5689 ExAcquireResourceSharedLite(&pdo_list_lock
, true);
5691 le
= pdo_list
.Flink
;
5692 while (le
!= &pdo_list
) {
5693 pdo_device_extension
* pdode2
= CONTAINING_RECORD(le
, pdo_device_extension
, list_entry
);
5695 if (pdode2
->pdo
== PhysicalDeviceObject
) {
5704 WARN("unrecognized PDO %p\n", PhysicalDeviceObject
);
5705 Status
= STATUS_NOT_SUPPORTED
;
5709 ExAcquireResourceSharedLite(&pdode
->child_lock
, true);
5711 volname
.Length
= volname
.MaximumLength
= (sizeof(BTRFS_VOLUME_PREFIX
) - sizeof(WCHAR
)) + ((36 + 1) * sizeof(WCHAR
));
5712 volname
.Buffer
= ExAllocatePoolWithTag(PagedPool
, volname
.MaximumLength
, ALLOC_TAG
); // FIXME - when do we free this?
5714 if (!volname
.Buffer
) {
5715 ERR("out of memory\n");
5716 Status
= STATUS_INSUFFICIENT_RESOURCES
;
5720 RtlCopyMemory(volname
.Buffer
, BTRFS_VOLUME_PREFIX
, sizeof(BTRFS_VOLUME_PREFIX
) - sizeof(WCHAR
));
5722 j
= (sizeof(BTRFS_VOLUME_PREFIX
) / sizeof(WCHAR
)) - 1;
5723 for (i
= 0; i
< 16; i
++) {
5724 volname
.Buffer
[j
] = hex_digit(pdode
->uuid
.uuid
[i
] >> 4); j
++;
5725 volname
.Buffer
[j
] = hex_digit(pdode
->uuid
.uuid
[i
] & 0xf); j
++;
5727 if (i
== 3 || i
== 5 || i
== 7 || i
== 9) {
5728 volname
.Buffer
[j
] = '-';
5733 volname
.Buffer
[j
] = '}';
5735 Status
= IoCreateDevice(drvobj
, sizeof(volume_device_extension
), &volname
, FILE_DEVICE_DISK
,
5736 WdmlibRtlIsNtDdiVersionAvailable(NTDDI_WIN8
) ? FILE_DEVICE_ALLOW_APPCONTAINER_TRAVERSAL
: 0, false, &voldev
);
5737 if (!NT_SUCCESS(Status
)) {
5738 ERR("IoCreateDevice returned %08x\n", Status
);
5742 voldev
->SectorSize
= PhysicalDeviceObject
->SectorSize
;
5743 voldev
->Flags
|= DO_DIRECT_IO
;
5745 vde
= voldev
->DeviceExtension
;
5746 vde
->type
= VCB_TYPE_VOLUME
;
5747 vde
->name
= volname
;
5748 vde
->device
= voldev
;
5749 vde
->mounted_device
= NULL
;
5750 vde
->pdo
= PhysicalDeviceObject
;
5752 vde
->removing
= false;
5753 vde
->open_count
= 0;
5755 Status
= IoRegisterDeviceInterface(PhysicalDeviceObject
, &GUID_DEVINTERFACE_VOLUME
, NULL
, &vde
->bus_name
);
5756 if (!NT_SUCCESS(Status
))
5757 WARN("IoRegisterDeviceInterface returned %08x\n", Status
);
5759 vde
->attached_device
= IoAttachDeviceToDeviceStack(voldev
, PhysicalDeviceObject
);
5763 if (pdode
->removable
)
5764 voldev
->Characteristics
|= FILE_REMOVABLE_MEDIA
;
5766 voldev
->Flags
&= ~DO_DEVICE_INITIALIZING
;
5768 Status
= IoSetDeviceInterfaceState(&vde
->bus_name
, true);
5769 if (!NT_SUCCESS(Status
))
5770 WARN("IoSetDeviceInterfaceState returned %08x\n", Status
);
5772 Status
= STATUS_SUCCESS
;
5775 ExReleaseResourceLite(&pdode
->child_lock
);
5778 ExReleaseResourceLite(&pdo_list_lock
);
5783 _Function_class_(DRIVER_INITIALIZE
)
5784 NTSTATUS __stdcall
DriverEntry(_In_ PDRIVER_OBJECT DriverObject
, _In_ PUNICODE_STRING RegistryPath
) {
5786 PDEVICE_OBJECT DeviceObject
;
5787 UNICODE_STRING device_nameW
;
5788 UNICODE_STRING dosdevice_nameW
;
5789 control_device_extension
* cde
;
5790 bus_device_extension
* bde
;
5792 OBJECT_ATTRIBUTES oa
;
5795 InitializeListHead(&uid_map_list
);
5796 InitializeListHead(&gid_map_list
);
5799 ExInitializeResourceLite(&log_lock
);
5801 ExInitializeResourceLite(&mapping_lock
);
5803 log_device
.Buffer
= NULL
;
5804 log_device
.Length
= log_device
.MaximumLength
= 0;
5805 log_file
.Buffer
= NULL
;
5806 log_file
.Length
= log_file
.MaximumLength
= 0;
5808 registry_path
.Length
= registry_path
.MaximumLength
= RegistryPath
->Length
;
5809 registry_path
.Buffer
= ExAllocatePoolWithTag(PagedPool
, registry_path
.Length
, ALLOC_TAG
);
5811 if (!registry_path
.Buffer
) {
5812 ERR("out of memory\n");
5813 return STATUS_INSUFFICIENT_RESOURCES
;
5816 RtlCopyMemory(registry_path
.Buffer
, RegistryPath
->Buffer
, registry_path
.Length
);
5818 read_registry(®istry_path
, false);
5821 if (debug_log_level
> 0)
5827 TRACE("DriverEntry\n");
5833 if (WdmlibRtlIsNtDdiVersionAvailable(NTDDI_WIN8
)) {
5834 UNICODE_STRING name
;
5835 tPsIsDiskCountersEnabled fPsIsDiskCountersEnabled
;
5837 RtlInitUnicodeString(&name
, L
"PsIsDiskCountersEnabled");
5838 fPsIsDiskCountersEnabled
= (tPsIsDiskCountersEnabled
)MmGetSystemRoutineAddress(&name
);
5840 if (fPsIsDiskCountersEnabled
) {
5841 diskacc
= fPsIsDiskCountersEnabled();
5843 RtlInitUnicodeString(&name
, L
"PsUpdateDiskCounters");
5844 fPsUpdateDiskCounters
= (tPsUpdateDiskCounters
)MmGetSystemRoutineAddress(&name
);
5846 if (!fPsUpdateDiskCounters
)
5849 RtlInitUnicodeString(&name
, L
"FsRtlUpdateDiskCounters");
5850 fFsRtlUpdateDiskCounters
= (tFsRtlUpdateDiskCounters
)MmGetSystemRoutineAddress(&name
);
5853 RtlInitUnicodeString(&name
, L
"CcCopyReadEx");
5854 fCcCopyReadEx
= (tCcCopyReadEx
)MmGetSystemRoutineAddress(&name
);
5856 RtlInitUnicodeString(&name
, L
"CcCopyWriteEx");
5857 fCcCopyWriteEx
= (tCcCopyWriteEx
)MmGetSystemRoutineAddress(&name
);
5859 RtlInitUnicodeString(&name
, L
"CcSetAdditionalCacheAttributesEx");
5860 fCcSetAdditionalCacheAttributesEx
= (tCcSetAdditionalCacheAttributesEx
)MmGetSystemRoutineAddress(&name
);
5862 fPsUpdateDiskCounters
= NULL
;
5863 fCcCopyReadEx
= NULL
;
5864 fCcCopyWriteEx
= NULL
;
5865 fCcSetAdditionalCacheAttributesEx
= NULL
;
5866 fFsRtlUpdateDiskCounters
= NULL
;
5869 if (WdmlibRtlIsNtDdiVersionAvailable(NTDDI_WIN7
)) {
5870 UNICODE_STRING name
;
5872 RtlInitUnicodeString(&name
, L
"IoUnregisterPlugPlayNotificationEx");
5873 fIoUnregisterPlugPlayNotificationEx
= (tIoUnregisterPlugPlayNotificationEx
)MmGetSystemRoutineAddress(&name
);
5875 fIoUnregisterPlugPlayNotificationEx
= NULL
;
5877 if (WdmlibRtlIsNtDdiVersionAvailable(NTDDI_VISTA
)) {
5878 UNICODE_STRING name
;
5880 RtlInitUnicodeString(&name
, L
"FsRtlGetEcpListFromIrp");
5881 fFsRtlGetEcpListFromIrp
= (tFsRtlGetEcpListFromIrp
)MmGetSystemRoutineAddress(&name
);
5883 RtlInitUnicodeString(&name
, L
"FsRtlGetNextExtraCreateParameter");
5884 fFsRtlGetNextExtraCreateParameter
= (tFsRtlGetNextExtraCreateParameter
)MmGetSystemRoutineAddress(&name
);
5886 RtlInitUnicodeString(&name
, L
"FsRtlValidateReparsePointBuffer");
5887 fFsRtlValidateReparsePointBuffer
= (tFsRtlValidateReparsePointBuffer
)MmGetSystemRoutineAddress(&name
);
5889 fFsRtlGetEcpListFromIrp
= NULL
;
5890 fFsRtlGetNextExtraCreateParameter
= NULL
;
5891 fFsRtlValidateReparsePointBuffer
= compat_FsRtlValidateReparsePointBuffer
;
5894 drvobj
= DriverObject
;
5896 DriverObject
->DriverUnload
= DriverUnload
;
5898 DriverObject
->DriverExtension
->AddDevice
= AddDevice
;
5900 DriverObject
->MajorFunction
[IRP_MJ_CREATE
] = drv_create
;
5901 DriverObject
->MajorFunction
[IRP_MJ_CLOSE
] = drv_close
;
5902 DriverObject
->MajorFunction
[IRP_MJ_READ
] = drv_read
;
5903 DriverObject
->MajorFunction
[IRP_MJ_WRITE
] = drv_write
;
5904 DriverObject
->MajorFunction
[IRP_MJ_QUERY_INFORMATION
] = drv_query_information
;
5905 DriverObject
->MajorFunction
[IRP_MJ_SET_INFORMATION
] = drv_set_information
;
5906 DriverObject
->MajorFunction
[IRP_MJ_QUERY_EA
] = drv_query_ea
;
5907 DriverObject
->MajorFunction
[IRP_MJ_SET_EA
] = drv_set_ea
;
5908 DriverObject
->MajorFunction
[IRP_MJ_FLUSH_BUFFERS
] = drv_flush_buffers
;
5909 DriverObject
->MajorFunction
[IRP_MJ_QUERY_VOLUME_INFORMATION
] = drv_query_volume_information
;
5910 DriverObject
->MajorFunction
[IRP_MJ_SET_VOLUME_INFORMATION
] = drv_set_volume_information
;
5911 DriverObject
->MajorFunction
[IRP_MJ_DIRECTORY_CONTROL
] = drv_directory_control
;
5912 DriverObject
->MajorFunction
[IRP_MJ_FILE_SYSTEM_CONTROL
] = drv_file_system_control
;
5913 DriverObject
->MajorFunction
[IRP_MJ_DEVICE_CONTROL
] = drv_device_control
;
5914 DriverObject
->MajorFunction
[IRP_MJ_SHUTDOWN
] = drv_shutdown
;
5915 DriverObject
->MajorFunction
[IRP_MJ_LOCK_CONTROL
] = drv_lock_control
;
5916 DriverObject
->MajorFunction
[IRP_MJ_CLEANUP
] = drv_cleanup
;
5917 DriverObject
->MajorFunction
[IRP_MJ_QUERY_SECURITY
] = drv_query_security
;
5918 DriverObject
->MajorFunction
[IRP_MJ_SET_SECURITY
] = drv_set_security
;
5919 DriverObject
->MajorFunction
[IRP_MJ_POWER
] = drv_power
;
5920 DriverObject
->MajorFunction
[IRP_MJ_SYSTEM_CONTROL
] = drv_system_control
;
5921 DriverObject
->MajorFunction
[IRP_MJ_PNP
] = drv_pnp
;
5923 init_fast_io_dispatch(&DriverObject
->FastIoDispatch
);
5925 device_nameW
.Buffer
= (WCHAR
*)device_name
;
5926 device_nameW
.Length
= device_nameW
.MaximumLength
= sizeof(device_name
) - sizeof(WCHAR
);
5927 dosdevice_nameW
.Buffer
= (WCHAR
*)dosdevice_name
;
5928 dosdevice_nameW
.Length
= dosdevice_nameW
.MaximumLength
= sizeof(dosdevice_name
) - sizeof(WCHAR
);
5930 Status
= IoCreateDevice(DriverObject
, sizeof(control_device_extension
), &device_nameW
, FILE_DEVICE_DISK_FILE_SYSTEM
,
5931 FILE_DEVICE_SECURE_OPEN
, false, &DeviceObject
);
5932 if (!NT_SUCCESS(Status
)) {
5933 ERR("IoCreateDevice returned %08x\n", Status
);
5937 master_devobj
= DeviceObject
;
5938 cde
= (control_device_extension
*)master_devobj
->DeviceExtension
;
5940 RtlZeroMemory(cde
, sizeof(control_device_extension
));
5942 cde
->type
= VCB_TYPE_CONTROL
;
5944 DeviceObject
->Flags
&= ~DO_DEVICE_INITIALIZING
;
5946 Status
= IoCreateSymbolicLink(&dosdevice_nameW
, &device_nameW
);
5947 if (!NT_SUCCESS(Status
)) {
5948 ERR("IoCreateSymbolicLink returned %08x\n", Status
);
5952 Status
= init_cache();
5953 if (!NT_SUCCESS(Status
)) {
5954 ERR("init_cache returned %08x\n", Status
);
5958 InitializeListHead(&VcbList
);
5959 ExInitializeResourceLite(&global_loading_lock
);
5960 ExInitializeResourceLite(&pdo_list_lock
);
5962 InitializeListHead(&pdo_list
);
5964 InitializeObjectAttributes(&oa
, RegistryPath
, OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
, NULL
, NULL
);
5965 Status
= ZwCreateKey(®h
, KEY_QUERY_VALUE
| KEY_ENUMERATE_SUB_KEYS
| KEY_NOTIFY
, &oa
, 0, NULL
, REG_OPTION_NON_VOLATILE
, &dispos
);
5966 /* ReactOS specific hack: allow BtrFS driver to start in 1st stage with no hive */
5968 if (!NT_SUCCESS(Status
)) {
5969 ERR("ZwCreateKey returned %08x\n", Status
);
5973 watch_registry(regh
);
5975 if (NT_SUCCESS(Status
)) {
5976 watch_registry(regh
);
5980 Status
= IoCreateDevice(DriverObject
, sizeof(bus_device_extension
), NULL
, FILE_DEVICE_UNKNOWN
,
5981 FILE_DEVICE_SECURE_OPEN
, false, &busobj
);
5982 if (!NT_SUCCESS(Status
)) {
5983 ERR("IoCreateDevice returned %08x\n", Status
);
5987 bde
= (bus_device_extension
*)busobj
->DeviceExtension
;
5989 RtlZeroMemory(bde
, sizeof(bus_device_extension
));
5991 bde
->type
= VCB_TYPE_BUS
;
5993 Status
= IoReportDetectedDevice(drvobj
, InterfaceTypeUndefined
, 0xFFFFFFFF, 0xFFFFFFFF,
5994 NULL
, NULL
, 0, &bde
->buspdo
);
5995 if (!NT_SUCCESS(Status
)) {
5996 ERR("IoReportDetectedDevice returned %08x\n", Status
);
6000 Status
= IoRegisterDeviceInterface(bde
->buspdo
, &BtrfsBusInterface
, NULL
, &bde
->bus_name
);
6001 if (!NT_SUCCESS(Status
))
6002 WARN("IoRegisterDeviceInterface returned %08x\n", Status
);
6004 bde
->attached_device
= IoAttachDeviceToDeviceStack(busobj
, bde
->buspdo
);
6006 busobj
->Flags
&= ~DO_DEVICE_INITIALIZING
;
6008 Status
= IoSetDeviceInterfaceState(&bde
->bus_name
, true);
6009 if (!NT_SUCCESS(Status
))
6010 WARN("IoSetDeviceInterfaceState returned %08x\n", Status
);
6012 IoInvalidateDeviceRelations(bde
->buspdo
, BusRelations
);
6014 Status
= PsCreateSystemThread(°raded_wait_handle
, 0, NULL
, NULL
, NULL
, degraded_wait_thread
, NULL
);
6015 if (!NT_SUCCESS(Status
))
6016 WARN("PsCreateSystemThread returned %08x\n", Status
);
6018 Status
= IoRegisterPlugPlayNotification(EventCategoryDeviceInterfaceChange
, PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES
,
6019 (PVOID
)&GUID_DEVINTERFACE_VOLUME
, DriverObject
, volume_notification
, DriverObject
, ¬ification_entry2
);
6020 if (!NT_SUCCESS(Status
))
6021 ERR("IoRegisterPlugPlayNotification returned %08x\n", Status
);
6023 Status
= IoRegisterPlugPlayNotification(EventCategoryDeviceInterfaceChange
, PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES
,
6024 (PVOID
)&GUID_DEVINTERFACE_HIDDEN_VOLUME
, DriverObject
, volume_notification
, DriverObject
, ¬ification_entry3
);
6025 if (!NT_SUCCESS(Status
))
6026 ERR("IoRegisterPlugPlayNotification returned %08x\n", Status
);
6028 Status
= IoRegisterPlugPlayNotification(EventCategoryDeviceInterfaceChange
, PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES
,
6029 (PVOID
)&GUID_DEVINTERFACE_DISK
, DriverObject
, pnp_notification
, DriverObject
, ¬ification_entry
);
6030 if (!NT_SUCCESS(Status
))
6031 ERR("IoRegisterPlugPlayNotification returned %08x\n", Status
);
6033 finished_probing
= true;
6035 KeInitializeEvent(&mountmgr_thread_event
, NotificationEvent
, false);
6038 Status
= PsCreateSystemThread(&mountmgr_thread_handle
, 0, NULL
, NULL
, NULL
, mountmgr_thread
, NULL
);
6039 if (!NT_SUCCESS(Status
))
6040 WARN("PsCreateSystemThread returned %08x\n", Status
);
6043 IoRegisterFileSystem(DeviceObject
);
6045 IoRegisterBootDriverReinitialization(DriverObject
, check_system_root
, NULL
);
6047 return STATUS_SUCCESS
;