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"
49 #define INCOMPAT_SUPPORTED (BTRFS_INCOMPAT_FLAGS_MIXED_BACKREF | BTRFS_INCOMPAT_FLAGS_DEFAULT_SUBVOL | BTRFS_INCOMPAT_FLAGS_MIXED_GROUPS | \
50 BTRFS_INCOMPAT_FLAGS_COMPRESS_LZO | BTRFS_INCOMPAT_FLAGS_BIG_METADATA | BTRFS_INCOMPAT_FLAGS_RAID56 | \
51 BTRFS_INCOMPAT_FLAGS_EXTENDED_IREF | BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA | BTRFS_INCOMPAT_FLAGS_NO_HOLES | \
52 BTRFS_INCOMPAT_FLAGS_COMPRESS_ZSTD)
53 #define COMPAT_RO_SUPPORTED (BTRFS_COMPAT_RO_FLAGS_FREE_SPACE_CACHE | BTRFS_COMPAT_RO_FLAGS_FREE_SPACE_CACHE_VALID)
55 static const WCHAR device_name
[] = {'\\','B','t','r','f','s',0};
56 static const WCHAR dosdevice_name
[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\','B','t','r','f','s',0};
58 DEFINE_GUID(BtrfsBusInterface
, 0x4d414874, 0x6865, 0x6761, 0x6d, 0x65, 0x83, 0x69, 0x17, 0x9a, 0x7d, 0x1d);
60 PDRIVER_OBJECT drvobj
;
61 PDEVICE_OBJECT master_devobj
;
63 BOOL have_sse42
= FALSE
, have_sse2
= FALSE
;
66 LIST_ENTRY uid_map_list
, gid_map_list
;
68 ERESOURCE global_loading_lock
;
69 UINT32 debug_log_level
= 0;
70 UINT32 mount_compress
= 0;
71 UINT32 mount_compress_force
= 0;
72 UINT32 mount_compress_type
= 0;
73 UINT32 mount_zlib_level
= 3;
74 UINT32 mount_zstd_level
= 3;
75 UINT32 mount_flush_interval
= 30;
76 UINT32 mount_max_inline
= 2048;
77 UINT32 mount_skip_balance
= 0;
78 UINT32 mount_no_barrier
= 0;
79 UINT32 mount_no_trim
= 0;
80 UINT32 mount_clear_cache
= 0;
81 UINT32 mount_allow_degraded
= 0;
82 UINT32 mount_readonly
= 0;
84 BOOL log_started
= FALSE
;
85 UNICODE_STRING log_device
, log_file
, registry_path
;
86 tPsUpdateDiskCounters fPsUpdateDiskCounters
;
87 tCcCopyReadEx fCcCopyReadEx
;
88 tCcCopyWriteEx fCcCopyWriteEx
;
89 tCcSetAdditionalCacheAttributesEx fCcSetAdditionalCacheAttributesEx
;
90 tFsRtlUpdateDiskCounters fFsRtlUpdateDiskCounters
;
92 void *notification_entry
= NULL
, *notification_entry2
= NULL
, *notification_entry3
= NULL
;
93 ERESOURCE pdo_list_lock
, mapping_lock
;
95 BOOL finished_probing
= FALSE
;
96 HANDLE degraded_wait_handle
= NULL
, mountmgr_thread_handle
= NULL
;
97 BOOL degraded_wait
= TRUE
;
98 KEVENT mountmgr_thread_event
;
99 BOOL shutting_down
= FALSE
;
102 PFILE_OBJECT comfo
= NULL
;
103 PDEVICE_OBJECT comdo
= NULL
;
104 HANDLE log_handle
= NULL
;
106 HANDLE serial_thread_handle
= NULL
;
108 static void init_serial(BOOL first_time
);
111 static NTSTATUS
close_file(_In_ PFILE_OBJECT FileObject
, _In_ PIRP Irp
);
115 IO_STATUS_BLOCK iosb
;
119 _Function_class_(IO_COMPLETION_ROUTINE
)
120 static NTSTATUS
dbg_completion(_In_ PDEVICE_OBJECT DeviceObject
, _In_ PIRP Irp
, _In_ PVOID conptr
) {
121 read_context
* context
= conptr
;
123 UNUSED(DeviceObject
);
125 context
->iosb
= Irp
->IoStatus
;
126 KeSetEvent(&context
->Event
, 0, FALSE
);
128 return STATUS_MORE_PROCESSING_REQUIRED
;
131 #ifdef DEBUG_LONG_MESSAGES
132 void _debug_message(_In_
const char* func
, _In_
const char* file
, _In_
unsigned int line
, _In_
char* s
, ...) {
134 void _debug_message(_In_
const char* func
, _In_
char* s
, ...) {
136 LARGE_INTEGER offset
;
137 PIO_STACK_LOCATION IrpSp
;
142 read_context context
;
145 buf2
= ExAllocatePoolWithTag(NonPagedPool
, 1024, ALLOC_TAG
);
148 DbgPrint("Couldn't allocate buffer in debug_message\n");
152 #ifdef DEBUG_LONG_MESSAGES
153 sprintf(buf2
, "%p:%s:%s:%u:", PsGetCurrentThread(), func
, file
, line
);
155 sprintf(buf2
, "%p:%s:", PsGetCurrentThread(), func
);
157 buf
= &buf2
[strlen(buf2
)];
160 vsprintf(buf
, s
, ap
);
162 ExAcquireResourceSharedLite(&log_lock
, TRUE
);
164 if (!log_started
|| (log_device
.Length
== 0 && log_file
.Length
== 0)) {
166 } else if (log_device
.Length
> 0) {
168 DbgPrint("comdo is NULL :-(\n");
173 length
= (UINT32
)strlen(buf2
);
175 offset
.u
.LowPart
= 0;
176 offset
.u
.HighPart
= 0;
178 RtlZeroMemory(&context
, sizeof(read_context
));
180 KeInitializeEvent(&context
.Event
, NotificationEvent
, FALSE
);
182 Irp
= IoAllocateIrp(comdo
->StackSize
, FALSE
);
185 DbgPrint("IoAllocateIrp failed\n");
189 IrpSp
= IoGetNextIrpStackLocation(Irp
);
190 IrpSp
->MajorFunction
= IRP_MJ_WRITE
;
192 if (comdo
->Flags
& DO_BUFFERED_IO
) {
193 Irp
->AssociatedIrp
.SystemBuffer
= buf2
;
195 Irp
->Flags
= IRP_BUFFERED_IO
;
196 } else if (comdo
->Flags
& DO_DIRECT_IO
) {
197 Irp
->MdlAddress
= IoAllocateMdl(buf2
, length
, FALSE
, FALSE
, NULL
);
198 if (!Irp
->MdlAddress
) {
199 DbgPrint("IoAllocateMdl failed\n");
203 MmBuildMdlForNonPagedPool(Irp
->MdlAddress
);
205 Irp
->UserBuffer
= buf2
;
208 IrpSp
->Parameters
.Write
.Length
= length
;
209 IrpSp
->Parameters
.Write
.ByteOffset
= offset
;
211 Irp
->UserIosb
= &context
.iosb
;
213 Irp
->UserEvent
= &context
.Event
;
215 IoSetCompletionRoutine(Irp
, dbg_completion
, &context
, TRUE
, TRUE
, TRUE
);
217 Status
= IoCallDriver(comdo
, Irp
);
219 if (Status
== STATUS_PENDING
) {
220 KeWaitForSingleObject(&context
.Event
, Executive
, KernelMode
, FALSE
, NULL
);
221 Status
= context
.iosb
.Status
;
224 if (comdo
->Flags
& DO_DIRECT_IO
)
225 IoFreeMdl(Irp
->MdlAddress
);
227 if (!NT_SUCCESS(Status
)) {
228 DbgPrint("failed to write to COM1 - error %08x\n", Status
);
234 } else if (log_handle
!= NULL
) {
235 IO_STATUS_BLOCK iosb
;
237 length
= (UINT32
)strlen(buf2
);
239 Status
= ZwWriteFile(log_handle
, NULL
, NULL
, NULL
, &iosb
, buf2
, length
, NULL
, NULL
);
241 if (!NT_SUCCESS(Status
)) {
242 DbgPrint("failed to write to file - error %08x\n", Status
);
247 ExReleaseResourceLite(&log_lock
);
256 BOOL
is_top_level(_In_ PIRP Irp
) {
257 if (!IoGetTopLevelIrp()) {
258 IoSetTopLevelIrp(Irp
);
265 _Function_class_(DRIVER_UNLOAD
)
267 static void NTAPI
DriverUnload(_In_ PDRIVER_OBJECT DriverObject
) {
269 static void DriverUnload(_In_ PDRIVER_OBJECT DriverObject
) {
271 UNICODE_STRING dosdevice_nameW
;
277 IoUnregisterFileSystem(DriverObject
->DeviceObject
);
279 if (notification_entry2
)
281 IoUnregisterPlugPlayNotification(notification_entry2
);
283 IoUnregisterPlugPlayNotificationEx(notification_entry2
);
286 if (notification_entry3
)
288 IoUnregisterPlugPlayNotification(notification_entry3
);
290 IoUnregisterPlugPlayNotificationEx(notification_entry3
);
293 if (notification_entry
)
295 IoUnregisterPlugPlayNotification(notification_entry
);
297 IoUnregisterPlugPlayNotificationEx(notification_entry
);
300 dosdevice_nameW
.Buffer
= (WCHAR
*)dosdevice_name
;
301 dosdevice_nameW
.Length
= dosdevice_nameW
.MaximumLength
= sizeof(dosdevice_name
) - sizeof(WCHAR
);
303 IoDeleteSymbolicLink(&dosdevice_nameW
);
304 IoDeleteDevice(DriverObject
->DeviceObject
);
306 while (!IsListEmpty(&uid_map_list
)) {
307 LIST_ENTRY
* le
= RemoveHeadList(&uid_map_list
);
308 uid_map
* um
= CONTAINING_RECORD(le
, uid_map
, listentry
);
315 while (!IsListEmpty(&gid_map_list
)) {
316 gid_map
* gm
= CONTAINING_RECORD(RemoveHeadList(&gid_map_list
), gid_map
, listentry
);
322 // FIXME - free volumes and their devpaths
326 ObDereferenceObject(comfo
);
332 ExDeleteResourceLite(&global_loading_lock
);
333 ExDeleteResourceLite(&pdo_list_lock
);
335 if (log_device
.Buffer
)
336 ExFreePool(log_device
.Buffer
);
339 ExFreePool(log_file
.Buffer
);
341 if (registry_path
.Buffer
)
342 ExFreePool(registry_path
.Buffer
);
345 ExDeleteResourceLite(&log_lock
);
347 ExDeleteResourceLite(&mapping_lock
);
350 static BOOL
get_last_inode(_In_
_Requires_exclusive_lock_held_(_Curr_
->tree_lock
) device_extension
* Vcb
, _In_ root
* r
, _In_opt_ PIRP Irp
) {
352 traverse_ptr tp
, prev_tp
;
356 searchkey
.obj_id
= 0xffffffffffffffff;
357 searchkey
.obj_type
= 0xff;
358 searchkey
.offset
= 0xffffffffffffffff;
360 Status
= find_item(Vcb
, r
, &tp
, &searchkey
, FALSE
, Irp
);
361 if (!NT_SUCCESS(Status
)) {
362 ERR("error - find_item returned %08x\n", Status
);
366 if (tp
.item
->key
.obj_type
== TYPE_INODE_ITEM
|| (tp
.item
->key
.obj_type
== TYPE_ROOT_ITEM
&& !(tp
.item
->key
.obj_id
& 0x8000000000000000))) {
367 r
->lastinode
= tp
.item
->key
.obj_id
;
368 TRACE("last inode for tree %llx is %llx\n", r
->id
, r
->lastinode
);
372 while (find_prev_item(Vcb
, &tp
, &prev_tp
, Irp
)) {
375 TRACE("moving on to %llx,%x,%llx\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
377 if (tp
.item
->key
.obj_type
== TYPE_INODE_ITEM
|| (tp
.item
->key
.obj_type
== TYPE_ROOT_ITEM
&& !(tp
.item
->key
.obj_id
& 0x8000000000000000))) {
378 r
->lastinode
= tp
.item
->key
.obj_id
;
379 TRACE("last inode for tree %llx is %llx\n", r
->id
, r
->lastinode
);
384 r
->lastinode
= SUBVOL_ROOT_INODE
;
386 WARN("no INODE_ITEMs in tree %llx\n", r
->id
);
392 static BOOL
extract_xattr(_In_reads_bytes_(size
) void* item
, _In_ USHORT size
, _In_z_
char* name
, _Out_ UINT8
** data
, _Out_ UINT16
* datalen
) {
393 DIR_ITEM
* xa
= (DIR_ITEM
*)item
;
397 if (size
< sizeof(DIR_ITEM
) || size
< (sizeof(DIR_ITEM
) - 1 + xa
->m
+ xa
->n
)) {
398 WARN("DIR_ITEM is truncated\n");
402 if (xa
->n
== strlen(name
) && RtlCompareMemory(name
, xa
->name
, xa
->n
) == xa
->n
) {
403 TRACE("found xattr %s\n", name
);
408 *data
= ExAllocatePoolWithTag(PagedPool
, xa
->m
, ALLOC_TAG
);
410 ERR("out of memory\n");
414 RtlCopyMemory(*data
, &xa
->name
[xa
->n
], xa
->m
);
421 xasize
= sizeof(DIR_ITEM
) - 1 + xa
->m
+ xa
->n
;
425 xa
= (DIR_ITEM
*)&xa
->name
[xa
->m
+ xa
->n
];
430 TRACE("xattr %s not found\n", name
);
436 BOOL
get_xattr(_In_
_Requires_lock_held_(_Curr_
->tree_lock
) device_extension
* Vcb
, _In_ root
* subvol
, _In_ UINT64 inode
, _In_z_
char* name
, _In_ UINT32 crc32
,
437 _Out_ UINT8
** data
, _Out_ UINT16
* datalen
, _In_opt_ PIRP Irp
) {
442 TRACE("(%p, %llx, %llx, %s, %08x, %p, %p)\n", Vcb
, subvol
->id
, inode
, name
, crc32
, data
, datalen
);
444 searchkey
.obj_id
= inode
;
445 searchkey
.obj_type
= TYPE_XATTR_ITEM
;
446 searchkey
.offset
= crc32
;
448 Status
= find_item(Vcb
, subvol
, &tp
, &searchkey
, FALSE
, Irp
);
449 if (!NT_SUCCESS(Status
)) {
450 ERR("error - find_item returned %08x\n", Status
);
454 if (keycmp(tp
.item
->key
, searchkey
)) {
455 TRACE("could not find item (%llx,%x,%llx)\n", searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
);
459 if (tp
.item
->size
< sizeof(DIR_ITEM
)) {
460 ERR("(%llx,%x,%llx) 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
));
464 return extract_xattr(tp
.item
->data
, tp
.item
->size
, name
, data
, datalen
);
467 _Dispatch_type_(IRP_MJ_CLOSE
)
468 _Function_class_(DRIVER_DISPATCH
)
470 static NTSTATUS NTAPI
drv_close(_In_ PDEVICE_OBJECT DeviceObject
, _In_ PIRP Irp
) {
472 static NTSTATUS
drv_close(_In_ PDEVICE_OBJECT DeviceObject
, _In_ PIRP Irp
) {
475 PIO_STACK_LOCATION IrpSp
;
476 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
479 FsRtlEnterFileSystem();
483 top_level
= is_top_level(Irp
);
485 if (DeviceObject
== master_devobj
) {
486 TRACE("Closing file system\n");
487 Status
= STATUS_SUCCESS
;
489 } else if (Vcb
&& Vcb
->type
== VCB_TYPE_VOLUME
) {
490 Status
= vol_close(DeviceObject
, Irp
);
492 } else if (!Vcb
|| Vcb
->type
!= VCB_TYPE_FS
) {
493 Status
= STATUS_INVALID_PARAMETER
;
497 IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
499 // FIXME - unmount if called for volume
500 // FIXME - call FsRtlNotifyUninitializeSync(&Vcb->NotifySync) if unmounting
502 Status
= close_file(IrpSp
->FileObject
, Irp
);
505 Irp
->IoStatus
.Status
= Status
;
506 Irp
->IoStatus
.Information
= 0;
508 IoCompleteRequest( Irp
, IO_DISK_INCREMENT
);
511 IoSetTopLevelIrp(NULL
);
513 TRACE("returning %08x\n", Status
);
515 FsRtlExitFileSystem();
520 _Dispatch_type_(IRP_MJ_FLUSH_BUFFERS
)
521 _Function_class_(DRIVER_DISPATCH
)
523 static NTSTATUS NTAPI
drv_flush_buffers(_In_ PDEVICE_OBJECT DeviceObject
, _In_ PIRP Irp
) {
525 static NTSTATUS
drv_flush_buffers(_In_ PDEVICE_OBJECT DeviceObject
, _In_ PIRP Irp
) {
528 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation( Irp
);
529 PFILE_OBJECT FileObject
= IrpSp
->FileObject
;
530 fcb
* fcb
= FileObject
->FsContext
;
531 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
534 FsRtlEnterFileSystem();
536 TRACE("flush buffers\n");
538 top_level
= is_top_level(Irp
);
540 if (Vcb
&& Vcb
->type
== VCB_TYPE_VOLUME
) {
541 Status
= vol_flush_buffers(DeviceObject
, Irp
);
543 } else if (!Vcb
|| Vcb
->type
!= VCB_TYPE_FS
) {
544 Status
= STATUS_INVALID_PARAMETER
;
549 ERR("fcb was NULL\n");
550 Status
= STATUS_INVALID_PARAMETER
;
554 if (fcb
== Vcb
->volume_fcb
) {
555 Status
= STATUS_INVALID_PARAMETER
;
559 Irp
->IoStatus
.Information
= 0;
561 fcb
->Header
.IsFastIoPossible
= fast_io_possible(fcb
);
563 Status
= STATUS_SUCCESS
;
564 Irp
->IoStatus
.Status
= Status
;
566 if (fcb
->type
!= BTRFS_TYPE_DIRECTORY
) {
567 CcFlushCache(&fcb
->nonpaged
->segment_object
, NULL
, 0, &Irp
->IoStatus
);
569 if (fcb
->Header
.PagingIoResource
) {
570 ExAcquireResourceExclusiveLite(fcb
->Header
.PagingIoResource
, TRUE
);
571 ExReleaseResourceLite(fcb
->Header
.PagingIoResource
);
574 Status
= Irp
->IoStatus
.Status
;
578 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
580 TRACE("returning %08x\n", Status
);
583 IoSetTopLevelIrp(NULL
);
585 FsRtlExitFileSystem();
590 static void calculate_total_space(_In_ device_extension
* Vcb
, _Out_ UINT64
* totalsize
, _Out_ UINT64
* freespace
) {
591 UINT64 nfactor
, dfactor
, sectors_used
;
593 if (Vcb
->data_flags
& BLOCK_FLAG_DUPLICATE
|| Vcb
->data_flags
& BLOCK_FLAG_RAID1
|| Vcb
->data_flags
& BLOCK_FLAG_RAID10
) {
596 } else if (Vcb
->data_flags
& BLOCK_FLAG_RAID5
) {
597 nfactor
= Vcb
->superblock
.num_devices
- 1;
598 dfactor
= Vcb
->superblock
.num_devices
;
599 } else if (Vcb
->data_flags
& BLOCK_FLAG_RAID6
) {
600 nfactor
= Vcb
->superblock
.num_devices
- 2;
601 dfactor
= Vcb
->superblock
.num_devices
;
607 sectors_used
= Vcb
->superblock
.bytes_used
/ Vcb
->superblock
.sector_size
;
609 *totalsize
= (Vcb
->superblock
.total_bytes
/ Vcb
->superblock
.sector_size
) * nfactor
/ dfactor
;
610 *freespace
= sectors_used
> *totalsize
? 0 : (*totalsize
- sectors_used
);
614 // This function exists because we have to lie about our FS type in certain situations.
615 // MPR!MprGetConnection queries the FS type, and compares it to a whitelist. If it doesn't match,
616 // it will return ERROR_NO_NET_OR_BAD_PATH, which prevents UAC from working.
617 // The command mklink refuses to create hard links on anything other than NTFS, so we have to
618 // blacklist cmd.exe too.
620 static BOOL
lie_about_fs_type() {
622 PROCESS_BASIC_INFORMATION pbi
;
630 static const WCHAR mpr
[] = L
"MPR.DLL";
631 static const WCHAR cmd
[] = L
"CMD.EXE";
632 static const WCHAR fsutil
[] = L
"FSUTIL.EXE";
633 UNICODE_STRING mprus
, cmdus
, fsutilus
;
635 mprus
.Buffer
= (WCHAR
*)mpr
;
636 mprus
.Length
= mprus
.MaximumLength
= sizeof(mpr
) - sizeof(WCHAR
);
637 cmdus
.Buffer
= (WCHAR
*)cmd
;
638 cmdus
.Length
= cmdus
.MaximumLength
= sizeof(cmd
) - sizeof(WCHAR
);
639 fsutilus
.Buffer
= (WCHAR
*)fsutil
;
640 fsutilus
.Length
= fsutilus
.MaximumLength
= sizeof(fsutil
) - sizeof(WCHAR
);
642 if (!PsGetCurrentProcess())
646 Status
= ZwQueryInformationProcess(NtCurrentProcess(), ProcessWow64Information
, &wow64info
, sizeof(wow64info
), NULL
);
648 if (NT_SUCCESS(Status
) && wow64info
!= 0)
652 Status
= ZwQueryInformationProcess(NtCurrentProcess(), ProcessBasicInformation
, &pbi
, sizeof(pbi
), &retlen
);
654 if (!NT_SUCCESS(Status
)) {
655 ERR("ZwQueryInformationProcess returned %08x\n", Status
);
659 if (!pbi
.PebBaseAddress
)
662 peb
= pbi
.PebBaseAddress
;
667 le
= peb
->Ldr
->InMemoryOrderModuleList
.Flink
;
668 while (le
!= &peb
->Ldr
->InMemoryOrderModuleList
) {
669 LDR_DATA_TABLE_ENTRY
* entry
= CONTAINING_RECORD(le
, LDR_DATA_TABLE_ENTRY
, InMemoryOrderLinks
);
670 BOOL blacklist
= FALSE
;
672 if (entry
->FullDllName
.Length
>= mprus
.Length
) {
675 name
.Buffer
= &entry
->FullDllName
.Buffer
[(entry
->FullDllName
.Length
- mprus
.Length
) / sizeof(WCHAR
)];
676 name
.Length
= name
.MaximumLength
= mprus
.Length
;
678 blacklist
= FsRtlAreNamesEqual(&name
, &mprus
, TRUE
, NULL
);
681 if (!blacklist
&& entry
->FullDllName
.Length
>= cmdus
.Length
) {
684 name
.Buffer
= &entry
->FullDllName
.Buffer
[(entry
->FullDllName
.Length
- cmdus
.Length
) / sizeof(WCHAR
)];
685 name
.Length
= name
.MaximumLength
= cmdus
.Length
;
687 blacklist
= FsRtlAreNamesEqual(&name
, &cmdus
, TRUE
, NULL
);
690 if (!blacklist
&& entry
->FullDllName
.Length
>= fsutilus
.Length
) {
693 name
.Buffer
= &entry
->FullDllName
.Buffer
[(entry
->FullDllName
.Length
- fsutilus
.Length
) / sizeof(WCHAR
)];
694 name
.Length
= name
.MaximumLength
= fsutilus
.Length
;
696 blacklist
= FsRtlAreNamesEqual(&name
, &fsutilus
, TRUE
, NULL
);
703 frames
= ExAllocatePoolWithTag(PagedPool
, 256 * sizeof(void*), ALLOC_TAG
);
705 ERR("out of memory\n");
709 num_frames
= RtlWalkFrameChain(frames
, 256, 1);
711 for (i
= 0; i
< num_frames
; i
++) {
712 // entry->Reserved3[1] appears to be the image size
713 if (frames
[i
] >= entry
->DllBase
&& (ULONG_PTR
)frames
[i
] <= (ULONG_PTR
)entry
->DllBase
+ (ULONG_PTR
)entry
->Reserved3
[1]) {
729 _Dispatch_type_(IRP_MJ_QUERY_VOLUME_INFORMATION
)
730 _Function_class_(DRIVER_DISPATCH
)
732 static NTSTATUS NTAPI
drv_query_volume_information(_In_ PDEVICE_OBJECT DeviceObject
, _In_ PIRP Irp
) {
734 static NTSTATUS
drv_query_volume_information(_In_ PDEVICE_OBJECT DeviceObject
, _In_ PIRP Irp
) {
736 PIO_STACK_LOCATION IrpSp
;
738 ULONG BytesCopied
= 0;
739 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
742 FsRtlEnterFileSystem();
744 TRACE("query volume information\n");
745 top_level
= is_top_level(Irp
);
747 if (Vcb
&& Vcb
->type
== VCB_TYPE_VOLUME
) {
748 Status
= vol_query_volume_information(DeviceObject
, Irp
);
750 } else if (!Vcb
|| Vcb
->type
!= VCB_TYPE_FS
) {
751 Status
= STATUS_INVALID_PARAMETER
;
755 IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
757 Status
= STATUS_NOT_IMPLEMENTED
;
759 switch (IrpSp
->Parameters
.QueryVolume
.FsInformationClass
) {
760 case FileFsAttributeInformation
:
762 FILE_FS_ATTRIBUTE_INFORMATION
* data
= Irp
->AssociatedIrp
.SystemBuffer
;
763 BOOL overflow
= FALSE
;
765 static const WCHAR ntfs
[] = L
"NTFS";
767 static const WCHAR btrfs
[] = L
"Btrfs";
768 const WCHAR
* fs_name
;
769 ULONG fs_name_len
, orig_fs_name_len
;
772 if (Irp
->RequestorMode
== UserMode
&& lie_about_fs_type()) {
774 orig_fs_name_len
= fs_name_len
= sizeof(ntfs
) - sizeof(WCHAR
);
777 orig_fs_name_len
= fs_name_len
= sizeof(btrfs
) - sizeof(WCHAR
);
781 orig_fs_name_len
= fs_name_len
= sizeof(btrfs
) - sizeof(WCHAR
);
784 TRACE("FileFsAttributeInformation\n");
786 if (IrpSp
->Parameters
.QueryVolume
.Length
< sizeof(FILE_FS_ATTRIBUTE_INFORMATION
) - sizeof(WCHAR
) + fs_name_len
) {
787 if (IrpSp
->Parameters
.QueryVolume
.Length
> sizeof(FILE_FS_ATTRIBUTE_INFORMATION
) - sizeof(WCHAR
))
788 fs_name_len
= IrpSp
->Parameters
.QueryVolume
.Length
- sizeof(FILE_FS_ATTRIBUTE_INFORMATION
) + sizeof(WCHAR
);
795 data
->FileSystemAttributes
= FILE_CASE_PRESERVED_NAMES
| FILE_CASE_SENSITIVE_SEARCH
|
796 FILE_UNICODE_ON_DISK
| FILE_NAMED_STREAMS
| FILE_SUPPORTS_HARD_LINKS
| FILE_PERSISTENT_ACLS
|
797 FILE_SUPPORTS_REPARSE_POINTS
| FILE_SUPPORTS_SPARSE_FILES
| FILE_SUPPORTS_OBJECT_IDS
|
798 FILE_SUPPORTS_OPEN_BY_FILE_ID
| FILE_SUPPORTS_EXTENDED_ATTRIBUTES
| FILE_SUPPORTS_BLOCK_REFCOUNTING
|
799 FILE_SUPPORTS_POSIX_UNLINK_RENAME
;
801 data
->FileSystemAttributes
|= FILE_READ_ONLY_VOLUME
;
803 // should also be FILE_FILE_COMPRESSION when supported
804 data
->MaximumComponentNameLength
= 255; // FIXME - check
805 data
->FileSystemNameLength
= orig_fs_name_len
;
806 RtlCopyMemory(data
->FileSystemName
, fs_name
, fs_name_len
);
808 BytesCopied
= sizeof(FILE_FS_ATTRIBUTE_INFORMATION
) - sizeof(WCHAR
) + fs_name_len
;
809 Status
= overflow
? STATUS_BUFFER_OVERFLOW
: STATUS_SUCCESS
;
813 case FileFsDeviceInformation
:
815 FILE_FS_DEVICE_INFORMATION
* ffdi
= Irp
->AssociatedIrp
.SystemBuffer
;
817 TRACE("FileFsDeviceInformation\n");
819 ffdi
->DeviceType
= FILE_DEVICE_DISK
;
821 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, TRUE
);
822 ffdi
->Characteristics
= Vcb
->Vpb
->RealDevice
->Characteristics
;
823 ExReleaseResourceLite(&Vcb
->tree_lock
);
826 ffdi
->Characteristics
|= FILE_READ_ONLY_DEVICE
;
828 ffdi
->Characteristics
&= ~FILE_READ_ONLY_DEVICE
;
830 BytesCopied
= sizeof(FILE_FS_DEVICE_INFORMATION
);
831 Status
= STATUS_SUCCESS
;
836 case FileFsFullSizeInformation
:
838 FILE_FS_FULL_SIZE_INFORMATION
* ffsi
= Irp
->AssociatedIrp
.SystemBuffer
;
840 TRACE("FileFsFullSizeInformation\n");
842 calculate_total_space(Vcb
, (UINT64
*)&ffsi
->TotalAllocationUnits
.QuadPart
, (UINT64
*)&ffsi
->ActualAvailableAllocationUnits
.QuadPart
);
843 ffsi
->CallerAvailableAllocationUnits
.QuadPart
= ffsi
->ActualAvailableAllocationUnits
.QuadPart
;
844 ffsi
->SectorsPerAllocationUnit
= 1;
845 ffsi
->BytesPerSector
= Vcb
->superblock
.sector_size
;
847 BytesCopied
= sizeof(FILE_FS_FULL_SIZE_INFORMATION
);
848 Status
= STATUS_SUCCESS
;
853 case FileFsObjectIdInformation
:
855 FILE_FS_OBJECTID_INFORMATION
* ffoi
= Irp
->AssociatedIrp
.SystemBuffer
;
857 TRACE("FileFsObjectIdInformation\n");
859 RtlCopyMemory(ffoi
->ObjectId
, &Vcb
->superblock
.uuid
.uuid
[0], sizeof(UCHAR
) * 16);
860 RtlZeroMemory(ffoi
->ExtendedInfo
, sizeof(ffoi
->ExtendedInfo
));
862 BytesCopied
= sizeof(FILE_FS_OBJECTID_INFORMATION
);
863 Status
= STATUS_SUCCESS
;
868 case FileFsSizeInformation
:
870 FILE_FS_SIZE_INFORMATION
* ffsi
= Irp
->AssociatedIrp
.SystemBuffer
;
872 TRACE("FileFsSizeInformation\n");
874 calculate_total_space(Vcb
, (UINT64
*)&ffsi
->TotalAllocationUnits
.QuadPart
, (UINT64
*)&ffsi
->AvailableAllocationUnits
.QuadPart
);
875 ffsi
->SectorsPerAllocationUnit
= 1;
876 ffsi
->BytesPerSector
= Vcb
->superblock
.sector_size
;
878 BytesCopied
= sizeof(FILE_FS_SIZE_INFORMATION
);
879 Status
= STATUS_SUCCESS
;
884 case FileFsVolumeInformation
:
886 FILE_FS_VOLUME_INFORMATION
* data
= Irp
->AssociatedIrp
.SystemBuffer
;
887 FILE_FS_VOLUME_INFORMATION ffvi
;
888 BOOL overflow
= FALSE
;
889 ULONG label_len
, orig_label_len
;
891 TRACE("FileFsVolumeInformation\n");
892 TRACE("max length = %u\n", IrpSp
->Parameters
.QueryVolume
.Length
);
894 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, TRUE
);
896 Status
= RtlUTF8ToUnicodeN(NULL
, 0, &label_len
, Vcb
->superblock
.label
, (ULONG
)strlen(Vcb
->superblock
.label
));
897 if (!NT_SUCCESS(Status
)) {
898 ERR("RtlUTF8ToUnicodeN returned %08x\n", Status
);
899 ExReleaseResourceLite(&Vcb
->tree_lock
);
903 orig_label_len
= label_len
;
905 if (IrpSp
->Parameters
.QueryVolume
.Length
< sizeof(FILE_FS_VOLUME_INFORMATION
) - sizeof(WCHAR
) + label_len
) {
906 if (IrpSp
->Parameters
.QueryVolume
.Length
> sizeof(FILE_FS_VOLUME_INFORMATION
) - sizeof(WCHAR
))
907 label_len
= IrpSp
->Parameters
.QueryVolume
.Length
- sizeof(FILE_FS_VOLUME_INFORMATION
) + sizeof(WCHAR
);
914 TRACE("label_len = %u\n", label_len
);
916 ffvi
.VolumeCreationTime
.QuadPart
= 0; // FIXME
917 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];
918 ffvi
.VolumeLabelLength
= orig_label_len
;
919 ffvi
.SupportsObjects
= FALSE
;
921 RtlCopyMemory(data
, &ffvi
, min(sizeof(FILE_FS_VOLUME_INFORMATION
) - sizeof(WCHAR
), IrpSp
->Parameters
.QueryVolume
.Length
));
926 Status
= RtlUTF8ToUnicodeN(&data
->VolumeLabel
[0], label_len
, &bytecount
, Vcb
->superblock
.label
, (ULONG
)strlen(Vcb
->superblock
.label
));
927 if (!NT_SUCCESS(Status
) && Status
!= STATUS_BUFFER_TOO_SMALL
) {
928 ERR("RtlUTF8ToUnicodeN returned %08x\n", Status
);
929 ExReleaseResourceLite(&Vcb
->tree_lock
);
933 TRACE("label = %.*S\n", label_len
/ sizeof(WCHAR
), data
->VolumeLabel
);
936 ExReleaseResourceLite(&Vcb
->tree_lock
);
938 BytesCopied
= sizeof(FILE_FS_VOLUME_INFORMATION
) - sizeof(WCHAR
) + label_len
;
939 Status
= overflow
? STATUS_BUFFER_OVERFLOW
: STATUS_SUCCESS
;
944 #ifdef _MSC_VER // not in mingw yet
945 case FileFsSectorSizeInformation
:
947 FILE_FS_SECTOR_SIZE_INFORMATION
* data
= Irp
->AssociatedIrp
.SystemBuffer
;
949 data
->LogicalBytesPerSector
= Vcb
->superblock
.sector_size
;
950 data
->PhysicalBytesPerSectorForAtomicity
= Vcb
->superblock
.sector_size
;
951 data
->PhysicalBytesPerSectorForPerformance
= Vcb
->superblock
.sector_size
;
952 data
->FileSystemEffectivePhysicalBytesPerSectorForAtomicity
= Vcb
->superblock
.sector_size
;
953 data
->ByteOffsetForSectorAlignment
= 0;
954 data
->ByteOffsetForPartitionAlignment
= 0;
956 data
->Flags
= SSINFO_FLAGS_ALIGNED_DEVICE
| SSINFO_FLAGS_PARTITION_ALIGNED_ON_DEVICE
;
958 if (Vcb
->trim
&& !Vcb
->options
.no_trim
)
959 data
->Flags
|= SSINFO_FLAGS_TRIM_ENABLED
;
961 BytesCopied
= sizeof(FILE_FS_SECTOR_SIZE_INFORMATION
);
966 #endif /* __REACTOS__ */
969 Status
= STATUS_INVALID_PARAMETER
;
970 WARN("unknown FsInformationClass %u\n", IrpSp
->Parameters
.QueryVolume
.FsInformationClass
);
974 if (!NT_SUCCESS(Status
) && Status
!= STATUS_BUFFER_OVERFLOW
)
975 Irp
->IoStatus
.Information
= 0;
977 Irp
->IoStatus
.Information
= BytesCopied
;
980 Irp
->IoStatus
.Status
= Status
;
982 IoCompleteRequest( Irp
, IO_DISK_INCREMENT
);
985 IoSetTopLevelIrp(NULL
);
987 TRACE("query volume information returning %08x\n", Status
);
989 FsRtlExitFileSystem();
994 _Function_class_(IO_COMPLETION_ROUTINE
)
996 static NTSTATUS NTAPI
read_completion(_In_ PDEVICE_OBJECT DeviceObject
, _In_ PIRP Irp
, _In_ PVOID conptr
) {
998 static NTSTATUS
read_completion(_In_ PDEVICE_OBJECT DeviceObject
, _In_ PIRP Irp
, _In_ PVOID conptr
) {
1000 read_context
* context
= conptr
;
1002 UNUSED(DeviceObject
);
1004 context
->iosb
= Irp
->IoStatus
;
1005 KeSetEvent(&context
->Event
, 0, FALSE
);
1007 return STATUS_MORE_PROCESSING_REQUIRED
;
1010 NTSTATUS
create_root(_In_
_Requires_exclusive_lock_held_(_Curr_
->tree_lock
) device_extension
* Vcb
, _In_ UINT64 id
,
1011 _Out_ root
** rootptr
, _In_ BOOL no_tree
, _In_ UINT64 offset
, _In_opt_ PIRP Irp
) {
1018 r
= ExAllocatePoolWithTag(PagedPool
, sizeof(root
), ALLOC_TAG
);
1020 ERR("out of memory\n");
1021 return STATUS_INSUFFICIENT_RESOURCES
;
1024 r
->nonpaged
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(root_nonpaged
), ALLOC_TAG
);
1026 ERR("out of memory\n");
1028 return STATUS_INSUFFICIENT_RESOURCES
;
1032 t
= ExAllocatePoolWithTag(PagedPool
, sizeof(tree
), ALLOC_TAG
);
1034 ERR("out of memory\n");
1035 ExFreePool(r
->nonpaged
);
1037 return STATUS_INSUFFICIENT_RESOURCES
;
1042 t
->is_unique
= TRUE
;
1043 t
->uniqueness_determined
= TRUE
;
1047 ri
= ExAllocatePoolWithTag(PagedPool
, sizeof(ROOT_ITEM
), ALLOC_TAG
);
1049 ERR("out of memory\n");
1054 ExFreePool(r
->nonpaged
);
1056 return STATUS_INSUFFICIENT_RESOURCES
;
1060 r
->treeholder
.address
= 0;
1061 r
->treeholder
.generation
= Vcb
->superblock
.generation
;
1062 r
->treeholder
.tree
= t
;
1065 r
->received
= FALSE
;
1069 RtlZeroMemory(&r
->root_item
, sizeof(ROOT_ITEM
));
1070 r
->root_item
.num_references
= 1;
1071 r
->fcbs_version
= 0;
1072 r
->checked_for_orphans
= TRUE
;
1073 InitializeListHead(&r
->fcbs
);
1074 RtlZeroMemory(r
->fcbs_ptrs
, sizeof(LIST_ENTRY
*) * 256);
1076 RtlCopyMemory(ri
, &r
->root_item
, sizeof(ROOT_ITEM
));
1078 // We ask here for a traverse_ptr to the item we're inserting, so we can
1079 // copy some of the tree's variables
1081 Status
= insert_tree_item(Vcb
, Vcb
->root_root
, id
, TYPE_ROOT_ITEM
, offset
, ri
, sizeof(ROOT_ITEM
), &tp
, Irp
);
1082 if (!NT_SUCCESS(Status
)) {
1083 ERR("insert_tree_item returned %08x\n", Status
);
1089 ExFreePool(r
->nonpaged
);
1094 ExInitializeResourceLite(&r
->nonpaged
->load_tree_lock
);
1096 InsertTailList(&Vcb
->roots
, &r
->list_entry
);
1099 RtlZeroMemory(&t
->header
, sizeof(tree_header
));
1100 t
->header
.fs_uuid
= tp
.tree
->header
.fs_uuid
;
1101 t
->header
.address
= 0;
1102 t
->header
.flags
= HEADER_FLAG_MIXED_BACKREF
| 1; // 1 == "written"? Why does the Linux driver record this?
1103 t
->header
.chunk_tree_uuid
= tp
.tree
->header
.chunk_tree_uuid
;
1104 t
->header
.generation
= Vcb
->superblock
.generation
;
1105 t
->header
.tree_id
= id
;
1106 t
->header
.num_items
= 0;
1107 t
->header
.level
= 0;
1109 t
->has_address
= FALSE
;
1116 InitializeListHead(&t
->itemlist
);
1119 t
->has_new_address
= FALSE
;
1120 t
->updated_extents
= FALSE
;
1122 InsertTailList(&Vcb
->trees
, &t
->list_entry
);
1123 t
->list_entry_hash
.Flink
= NULL
;
1126 Vcb
->need_write
= TRUE
;
1131 return STATUS_SUCCESS
;
1134 static NTSTATUS
set_label(_In_ device_extension
* Vcb
, _In_ FILE_FS_LABEL_INFORMATION
* ffli
) {
1139 TRACE("label = %.*S\n", ffli
->VolumeLabelLength
/ sizeof(WCHAR
), ffli
->VolumeLabel
);
1141 vollen
= ffli
->VolumeLabelLength
;
1143 for (i
= 0; i
< ffli
->VolumeLabelLength
/ sizeof(WCHAR
); i
++) {
1144 if (ffli
->VolumeLabel
[i
] == 0) {
1145 vollen
= i
* sizeof(WCHAR
);
1147 } else if (ffli
->VolumeLabel
[i
] == '/' || ffli
->VolumeLabel
[i
] == '\\') {
1148 Status
= STATUS_INVALID_VOLUME_LABEL
;
1156 Status
= RtlUnicodeToUTF8N(NULL
, 0, &utf8len
, ffli
->VolumeLabel
, vollen
);
1157 if (!NT_SUCCESS(Status
))
1160 if (utf8len
> MAX_LABEL_SIZE
) {
1161 Status
= STATUS_INVALID_VOLUME_LABEL
;
1166 ExAcquireResourceExclusiveLite(&Vcb
->tree_lock
, TRUE
);
1169 Status
= RtlUnicodeToUTF8N((PCHAR
)&Vcb
->superblock
.label
, MAX_LABEL_SIZE
, &utf8len
, ffli
->VolumeLabel
, vollen
);
1170 if (!NT_SUCCESS(Status
))
1173 Status
= STATUS_SUCCESS
;
1175 if (utf8len
< MAX_LABEL_SIZE
)
1176 RtlZeroMemory(Vcb
->superblock
.label
+ utf8len
, MAX_LABEL_SIZE
- utf8len
);
1178 Vcb
->need_write
= TRUE
;
1181 ExReleaseResourceLite(&Vcb
->tree_lock
);
1184 TRACE("returning %08x\n", Status
);
1189 _Dispatch_type_(IRP_MJ_SET_VOLUME_INFORMATION
)
1190 _Function_class_(DRIVER_DISPATCH
)
1192 static NTSTATUS NTAPI
drv_set_volume_information(_In_ PDEVICE_OBJECT DeviceObject
, _In_ PIRP Irp
) {
1194 static NTSTATUS
drv_set_volume_information(_In_ PDEVICE_OBJECT DeviceObject
, _In_ PIRP Irp
) {
1196 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
1197 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
1201 FsRtlEnterFileSystem();
1203 TRACE("set volume information\n");
1205 top_level
= is_top_level(Irp
);
1207 if (Vcb
&& Vcb
->type
== VCB_TYPE_VOLUME
) {
1208 Status
= vol_set_volume_information(DeviceObject
, Irp
);
1210 } else if (!Vcb
|| Vcb
->type
!= VCB_TYPE_FS
) {
1211 Status
= STATUS_INVALID_PARAMETER
;
1215 Status
= STATUS_NOT_IMPLEMENTED
;
1217 if (Vcb
->readonly
) {
1218 Status
= STATUS_MEDIA_WRITE_PROTECTED
;
1222 if (Vcb
->removing
|| Vcb
->locked
) {
1223 Status
= STATUS_ACCESS_DENIED
;
1227 switch (IrpSp
->Parameters
.SetVolume
.FsInformationClass
) {
1228 case FileFsControlInformation
:
1229 FIXME("STUB: FileFsControlInformation\n");
1232 case FileFsLabelInformation
:
1233 TRACE("FileFsLabelInformation\n");
1235 Status
= set_label(Vcb
, Irp
->AssociatedIrp
.SystemBuffer
);
1238 case FileFsObjectIdInformation
:
1239 FIXME("STUB: FileFsObjectIdInformation\n");
1243 WARN("Unrecognized FsInformationClass 0x%x\n", IrpSp
->Parameters
.SetVolume
.FsInformationClass
);
1248 Irp
->IoStatus
.Status
= Status
;
1249 Irp
->IoStatus
.Information
= 0;
1251 TRACE("returning %08x\n", Status
);
1253 IoCompleteRequest( Irp
, IO_NO_INCREMENT
);
1256 IoSetTopLevelIrp(NULL
);
1258 FsRtlExitFileSystem();
1263 static WCHAR
* file_desc_fcb(_In_ fcb
* fcb
) {
1269 if (fcb
->debug_desc
)
1270 return fcb
->debug_desc
;
1272 if (fcb
== fcb
->Vcb
->volume_fcb
)
1273 return L
"volume FCB";
1275 fcb
->debug_desc
= ExAllocatePoolWithTag(PagedPool
, 60 * sizeof(WCHAR
), ALLOC_TAG
);
1276 if (!fcb
->debug_desc
)
1277 return L
"(memory error)";
1279 // I know this is pretty hackish...
1280 // GCC doesn't like %llx in sprintf, and MSVC won't let us use swprintf
1281 // without the CRT, which breaks drivers.
1283 sprintf(s
, "subvol %x, inode %x", (UINT32
)fcb
->subvol
->id
, (UINT32
)fcb
->inode
);
1286 as
.Length
= as
.MaximumLength
= (USHORT
)strlen(s
);
1288 us
.Buffer
= fcb
->debug_desc
;
1289 us
.MaximumLength
= 60 * sizeof(WCHAR
);
1292 Status
= RtlAnsiStringToUnicodeString(&us
, &as
, FALSE
);
1293 if (!NT_SUCCESS(Status
))
1294 return L
"(RtlAnsiStringToUnicodeString error)";
1296 us
.Buffer
[us
.Length
/ sizeof(WCHAR
)] = 0;
1298 return fcb
->debug_desc
;
1301 WCHAR
* file_desc_fileref(_In_ file_ref
* fileref
) {
1306 if (fileref
->debug_desc
)
1307 return fileref
->debug_desc
;
1309 fn
.Length
= fn
.MaximumLength
= 0;
1310 Status
= fileref_get_filename(fileref
, &fn
, NULL
, &reqlen
);
1311 if (Status
!= STATUS_BUFFER_OVERFLOW
)
1314 if (reqlen
> 0xffff - sizeof(WCHAR
))
1315 return L
"(too long)";
1317 fileref
->debug_desc
= ExAllocatePoolWithTag(PagedPool
, reqlen
+ sizeof(WCHAR
), ALLOC_TAG
);
1318 if (!fileref
->debug_desc
)
1319 return L
"(memory error)";
1321 fn
.Buffer
= fileref
->debug_desc
;
1323 fn
.MaximumLength
= (USHORT
)(reqlen
+ sizeof(WCHAR
));
1325 Status
= fileref_get_filename(fileref
, &fn
, NULL
, &reqlen
);
1326 if (!NT_SUCCESS(Status
)) {
1327 ExFreePool(fileref
->debug_desc
);
1328 fileref
->debug_desc
= NULL
;
1332 fileref
->debug_desc
[fn
.Length
/ sizeof(WCHAR
)] = 0;
1334 return fileref
->debug_desc
;
1338 WCHAR
* file_desc(_In_ PFILE_OBJECT FileObject
) {
1339 fcb
* fcb
= FileObject
->FsContext
;
1340 ccb
* ccb
= FileObject
->FsContext2
;
1341 file_ref
* fileref
= ccb
? ccb
->fileref
: NULL
;
1344 return file_desc_fileref(fileref
);
1346 return file_desc_fcb(fcb
);
1349 void send_notification_fileref(_In_ file_ref
* fileref
, _In_ ULONG filter_match
, _In_ ULONG action
, _In_opt_ PUNICODE_STRING stream
) {
1354 fcb
* fcb
= fileref
->fcb
;
1356 fn
.Length
= fn
.MaximumLength
= 0;
1357 Status
= fileref_get_filename(fileref
, &fn
, NULL
, &reqlen
);
1358 if (Status
!= STATUS_BUFFER_OVERFLOW
) {
1359 ERR("fileref_get_filename returned %08x\n", Status
);
1363 if (reqlen
> 0xffff) {
1364 WARN("reqlen was too long for FsRtlNotifyFilterReportChange\n");
1368 fn
.Buffer
= ExAllocatePoolWithTag(PagedPool
, reqlen
, ALLOC_TAG
);
1370 ERR("out of memory\n");
1374 fn
.MaximumLength
= (USHORT
)reqlen
;
1377 Status
= fileref_get_filename(fileref
, &fn
, &name_offset
, &reqlen
);
1378 if (!NT_SUCCESS(Status
)) {
1379 ERR("fileref_get_filename returned %08x\n", Status
);
1380 ExFreePool(fn
.Buffer
);
1384 FsRtlNotifyFilterReportChange(fcb
->Vcb
->NotifySync
, &fcb
->Vcb
->DirNotifyList
, (PSTRING
)&fn
, name_offset
,
1385 (PSTRING
)stream
, NULL
, filter_match
, action
, NULL
, NULL
);
1386 ExFreePool(fn
.Buffer
);
1389 void send_notification_fcb(_In_ file_ref
* fileref
, _In_ ULONG filter_match
, _In_ ULONG action
, _In_opt_ PUNICODE_STRING stream
) {
1390 fcb
* fcb
= fileref
->fcb
;
1394 // no point looking for hardlinks if st_nlink == 1
1395 if (fileref
->fcb
->inode_item
.st_nlink
== 1) {
1396 send_notification_fileref(fileref
, filter_match
, action
, stream
);
1400 ExAcquireResourceExclusiveLite(&fcb
->Vcb
->fileref_lock
, TRUE
);
1402 le
= fcb
->hardlinks
.Flink
;
1403 while (le
!= &fcb
->hardlinks
) {
1404 hardlink
* hl
= CONTAINING_RECORD(le
, hardlink
, list_entry
);
1407 Status
= open_fileref_by_inode(fcb
->Vcb
, fcb
->subvol
, hl
->parent
, &parfr
, NULL
);
1409 if (!NT_SUCCESS(Status
))
1410 ERR("open_fileref_by_inode returned %08x\n", Status
);
1411 else if (!parfr
->deleted
) {
1415 fn
.Length
= fn
.MaximumLength
= 0;
1416 Status
= fileref_get_filename(parfr
, &fn
, NULL
, &pathlen
);
1417 if (Status
!= STATUS_BUFFER_OVERFLOW
) {
1418 ERR("fileref_get_filename returned %08x\n", Status
);
1419 free_fileref(parfr
);
1423 if (parfr
!= fcb
->Vcb
->root_fileref
)
1424 pathlen
+= sizeof(WCHAR
);
1426 if (pathlen
+ hl
->name
.Length
> 0xffff) {
1427 WARN("pathlen + hl->name.Length was too long for FsRtlNotifyFilterReportChange\n");
1428 free_fileref(parfr
);
1432 fn
.MaximumLength
= (USHORT
)(pathlen
+ hl
->name
.Length
);
1433 fn
.Buffer
= ExAllocatePoolWithTag(PagedPool
, fn
.MaximumLength
, ALLOC_TAG
);
1435 ERR("out of memory\n");
1436 free_fileref(parfr
);
1440 Status
= fileref_get_filename(parfr
, &fn
, NULL
, NULL
);
1441 if (!NT_SUCCESS(Status
)) {
1442 ERR("fileref_get_filename returned %08x\n", Status
);
1443 free_fileref(parfr
);
1444 ExFreePool(fn
.Buffer
);
1448 if (parfr
!= fcb
->Vcb
->root_fileref
) {
1449 fn
.Buffer
[(pathlen
/ sizeof(WCHAR
)) - 1] = '\\';
1450 fn
.Length
+= sizeof(WCHAR
);
1453 RtlCopyMemory(&fn
.Buffer
[pathlen
/ sizeof(WCHAR
)], hl
->name
.Buffer
, hl
->name
.Length
);
1454 fn
.Length
+= hl
->name
.Length
;
1456 FsRtlNotifyFilterReportChange(fcb
->Vcb
->NotifySync
, &fcb
->Vcb
->DirNotifyList
, (PSTRING
)&fn
, (USHORT
)pathlen
,
1457 (PSTRING
)stream
, NULL
, filter_match
, action
, NULL
, NULL
);
1459 ExFreePool(fn
.Buffer
);
1461 free_fileref(parfr
);
1467 ExReleaseResourceLite(&fcb
->Vcb
->fileref_lock
);
1470 void mark_fcb_dirty(_In_ fcb
* fcb
) {
1472 #ifdef DEBUG_FCB_REFCOUNTS
1477 #ifdef DEBUG_FCB_REFCOUNTS
1478 rc
= InterlockedIncrement(&fcb
->refcount
);
1479 WARN("fcb %p: refcount now %i\n", fcb
, rc
);
1481 InterlockedIncrement(&fcb
->refcount
);
1484 ExAcquireResourceExclusiveLite(&fcb
->Vcb
->dirty_fcbs_lock
, TRUE
);
1485 InsertTailList(&fcb
->Vcb
->dirty_fcbs
, &fcb
->list_entry_dirty
);
1486 ExReleaseResourceLite(&fcb
->Vcb
->dirty_fcbs_lock
);
1489 fcb
->Vcb
->need_write
= TRUE
;
1492 void mark_fileref_dirty(_In_ file_ref
* fileref
) {
1493 if (!fileref
->dirty
) {
1494 fileref
->dirty
= TRUE
;
1495 increase_fileref_refcount(fileref
);
1497 ExAcquireResourceExclusiveLite(&fileref
->fcb
->Vcb
->dirty_filerefs_lock
, TRUE
);
1498 InsertTailList(&fileref
->fcb
->Vcb
->dirty_filerefs
, &fileref
->list_entry_dirty
);
1499 ExReleaseResourceLite(&fileref
->fcb
->Vcb
->dirty_filerefs_lock
);
1502 fileref
->fcb
->Vcb
->need_write
= TRUE
;
1505 #ifdef DEBUG_FCB_REFCOUNTS
1506 void _free_fcb(_Inout_ fcb
* fcb
, _In_
const char* func
) {
1507 LONG rc
= InterlockedDecrement(&fcb
->refcount
);
1509 void free_fcb(_Inout_ fcb
* fcb
) {
1510 InterlockedDecrement(&fcb
->refcount
);
1513 #ifdef DEBUG_FCB_REFCOUNTS
1514 #ifdef DEBUG_LONG_MESSAGES
1515 ERR("fcb %p (%s): refcount now %i (subvol %llx, inode %llx)\n", fcb
, func
, rc
, fcb
->subvol
? fcb
->subvol
->id
: 0, fcb
->inode
);
1517 ERR("fcb %p (%s): refcount now %i (subvol %llx, inode %llx)\n", fcb
, func
, rc
, fcb
->subvol
? fcb
->subvol
->id
: 0, fcb
->inode
);
1522 void reap_fcb(fcb
* fcb
) {
1523 UINT8 c
= fcb
->hash
>> 24;
1525 if (fcb
->subvol
&& fcb
->subvol
->fcbs_ptrs
[c
] == &fcb
->list_entry
) {
1526 if (fcb
->list_entry
.Flink
!= &fcb
->subvol
->fcbs
&& (CONTAINING_RECORD(fcb
->list_entry
.Flink
, struct _fcb
, list_entry
)->hash
>> 24) == c
)
1527 fcb
->subvol
->fcbs_ptrs
[c
] = fcb
->list_entry
.Flink
;
1529 fcb
->subvol
->fcbs_ptrs
[c
] = NULL
;
1532 if (fcb
->list_entry
.Flink
)
1533 RemoveEntryList(&fcb
->list_entry
);
1535 if (fcb
->list_entry_all
.Flink
)
1536 RemoveEntryList(&fcb
->list_entry_all
);
1538 ExDeleteResourceLite(&fcb
->nonpaged
->resource
);
1539 ExDeleteResourceLite(&fcb
->nonpaged
->paging_resource
);
1540 ExDeleteResourceLite(&fcb
->nonpaged
->dir_children_lock
);
1542 ExFreeToNPagedLookasideList(&fcb
->Vcb
->fcb_np_lookaside
, fcb
->nonpaged
);
1545 ExFreePool(fcb
->sd
);
1547 if (fcb
->adsxattr
.Buffer
)
1548 ExFreePool(fcb
->adsxattr
.Buffer
);
1550 if (fcb
->reparse_xattr
.Buffer
)
1551 ExFreePool(fcb
->reparse_xattr
.Buffer
);
1553 if (fcb
->ea_xattr
.Buffer
)
1554 ExFreePool(fcb
->ea_xattr
.Buffer
);
1556 if (fcb
->adsdata
.Buffer
)
1557 ExFreePool(fcb
->adsdata
.Buffer
);
1559 if (fcb
->debug_desc
)
1560 ExFreePool(fcb
->debug_desc
);
1562 while (!IsListEmpty(&fcb
->extents
)) {
1563 LIST_ENTRY
* le
= RemoveHeadList(&fcb
->extents
);
1564 extent
* ext
= CONTAINING_RECORD(le
, extent
, list_entry
);
1567 ExFreePool(ext
->csum
);
1572 while (!IsListEmpty(&fcb
->hardlinks
)) {
1573 LIST_ENTRY
* le
= RemoveHeadList(&fcb
->hardlinks
);
1574 hardlink
* hl
= CONTAINING_RECORD(le
, hardlink
, list_entry
);
1576 if (hl
->name
.Buffer
)
1577 ExFreePool(hl
->name
.Buffer
);
1579 if (hl
->utf8
.Buffer
)
1580 ExFreePool(hl
->utf8
.Buffer
);
1585 while (!IsListEmpty(&fcb
->xattrs
)) {
1586 xattr
* xa
= CONTAINING_RECORD(RemoveHeadList(&fcb
->xattrs
), xattr
, list_entry
);
1591 while (!IsListEmpty(&fcb
->dir_children_index
)) {
1592 LIST_ENTRY
* le
= RemoveHeadList(&fcb
->dir_children_index
);
1593 dir_child
* dc
= CONTAINING_RECORD(le
, dir_child
, list_entry_index
);
1595 ExFreePool(dc
->utf8
.Buffer
);
1596 ExFreePool(dc
->name
.Buffer
);
1597 ExFreePool(dc
->name_uc
.Buffer
);
1602 ExFreePool(fcb
->hash_ptrs
);
1604 if (fcb
->hash_ptrs_uc
)
1605 ExFreePool(fcb
->hash_ptrs_uc
);
1607 FsRtlUninitializeFileLock(&fcb
->lock
);
1609 if (fcb
->pool_type
== NonPagedPool
)
1612 ExFreeToPagedLookasideList(&fcb
->Vcb
->fcb_lookaside
, fcb
);
1615 void reap_fcbs(device_extension
* Vcb
) {
1618 le
= Vcb
->all_fcbs
.Flink
;
1619 while (le
!= &Vcb
->all_fcbs
) {
1620 fcb
* fcb
= CONTAINING_RECORD(le
, struct _fcb
, list_entry_all
);
1621 LIST_ENTRY
* le2
= le
->Flink
;
1623 if (fcb
->refcount
== 0)
1630 void free_fileref(_Inout_ file_ref
* fr
) {
1633 rc
= InterlockedDecrement(&fr
->refcount
);
1638 #ifdef DEBUG_FCB_REFCOUNTS
1639 ERR("fileref %p: refcount now %i\n", fr
, rc
);
1644 ERR("fileref %p: refcount now %i\n", fr
, rc
);
1650 void reap_fileref(device_extension
* Vcb
, file_ref
* fr
) {
1651 // FIXME - do we need a file_ref lock?
1653 // FIXME - do delete if needed
1656 ExFreePool(fr
->debug_desc
);
1658 ExDeleteResourceLite(&fr
->nonpaged
->fileref_lock
);
1660 ExFreeToNPagedLookasideList(&Vcb
->fileref_np_lookaside
, fr
->nonpaged
);
1662 // FIXME - throw error if children not empty
1664 if (fr
->fcb
->fileref
== fr
)
1665 fr
->fcb
->fileref
= NULL
;
1669 fr
->dc
->size
= fr
->fcb
->adsdata
.Length
;
1671 fr
->dc
->fileref
= NULL
;
1674 if (fr
->list_entry
.Flink
)
1675 RemoveEntryList(&fr
->list_entry
);
1678 free_fileref(fr
->parent
);
1682 ExFreeToPagedLookasideList(&Vcb
->fileref_lookaside
, fr
);
1685 void reap_filerefs(device_extension
* Vcb
, file_ref
* fr
) {
1688 // FIXME - recursion is a bad idea in kernel mode
1690 le
= fr
->children
.Flink
;
1691 while (le
!= &fr
->children
) {
1692 file_ref
* c
= CONTAINING_RECORD(le
, file_ref
, list_entry
);
1693 LIST_ENTRY
* le2
= le
->Flink
;
1695 reap_filerefs(Vcb
, c
);
1700 if (fr
->refcount
== 0)
1701 reap_fileref(Vcb
, fr
);
1704 static NTSTATUS
close_file(_In_ PFILE_OBJECT FileObject
, _In_ PIRP Irp
) {
1707 file_ref
* fileref
= NULL
;
1712 TRACE("FileObject = %p\n", FileObject
);
1714 fcb
= FileObject
->FsContext
;
1716 TRACE("FCB was NULL, returning success\n");
1717 return STATUS_SUCCESS
;
1720 open_files
= InterlockedDecrement(&fcb
->Vcb
->open_files
);
1722 ccb
= FileObject
->FsContext2
;
1724 TRACE("close called for %S (fcb == %p)\n", file_desc(FileObject
), fcb
);
1726 // FIXME - make sure notification gets sent if file is being deleted
1729 if (ccb
->query_string
.Buffer
)
1730 RtlFreeUnicodeString(&ccb
->query_string
);
1732 if (ccb
->filename
.Buffer
)
1733 ExFreePool(ccb
->filename
.Buffer
);
1735 // FIXME - use refcounts for fileref
1736 fileref
= ccb
->fileref
;
1738 if (fcb
->Vcb
->running_sends
> 0) {
1739 BOOL send_cancelled
= FALSE
;
1741 ExAcquireResourceExclusiveLite(&fcb
->Vcb
->send_load_lock
, TRUE
);
1744 ccb
->send
->cancelling
= TRUE
;
1745 send_cancelled
= TRUE
;
1746 KeSetEvent(&ccb
->send
->cleared_event
, 0, FALSE
);
1749 ExReleaseResourceLite(&fcb
->Vcb
->send_load_lock
);
1751 if (send_cancelled
) {
1753 ExAcquireResourceExclusiveLite(&fcb
->Vcb
->send_load_lock
, TRUE
);
1754 ExReleaseResourceLite(&fcb
->Vcb
->send_load_lock
);
1762 CcUninitializeCacheMap(FileObject
, NULL
, NULL
);
1764 if (open_files
== 0 && fcb
->Vcb
->removing
) {
1766 return STATUS_SUCCESS
;
1769 if (!(fcb
->Vcb
->Vpb
->Flags
& VPB_MOUNTED
))
1770 return STATUS_SUCCESS
;
1773 free_fileref(fileref
);
1777 return STATUS_SUCCESS
;
1780 void uninit(_In_ device_extension
* Vcb
) {
1787 if (!Vcb
->removing
) {
1788 ExAcquireResourceExclusiveLite(&Vcb
->tree_lock
, TRUE
);
1789 Vcb
->removing
= TRUE
;
1790 ExReleaseResourceLite(&Vcb
->tree_lock
);
1793 IoAcquireVpbSpinLock(&irql
);
1794 Vcb
->Vpb
->Flags
&= ~VPB_MOUNTED
;
1795 Vcb
->Vpb
->Flags
|= VPB_DIRECT_WRITES_ALLOWED
;
1796 IoReleaseVpbSpinLock(irql
);
1798 RemoveEntryList(&Vcb
->list_entry
);
1800 if (Vcb
->balance
.thread
) {
1801 Vcb
->balance
.paused
= FALSE
;
1802 Vcb
->balance
.stopping
= TRUE
;
1803 KeSetEvent(&Vcb
->balance
.event
, 0, FALSE
);
1804 KeWaitForSingleObject(&Vcb
->balance
.finished
, Executive
, KernelMode
, FALSE
, NULL
);
1807 if (Vcb
->scrub
.thread
) {
1808 Vcb
->scrub
.paused
= FALSE
;
1809 Vcb
->scrub
.stopping
= TRUE
;
1810 KeSetEvent(&Vcb
->scrub
.event
, 0, FALSE
);
1811 KeWaitForSingleObject(&Vcb
->scrub
.finished
, Executive
, KernelMode
, FALSE
, NULL
);
1814 if (Vcb
->running_sends
!= 0) {
1815 BOOL send_cancelled
= FALSE
;
1817 ExAcquireResourceExclusiveLite(&Vcb
->send_load_lock
, TRUE
);
1819 le
= Vcb
->send_ops
.Flink
;
1820 while (le
!= &Vcb
->send_ops
) {
1821 send_info
* send
= CONTAINING_RECORD(le
, send_info
, list_entry
);
1823 if (!send
->cancelling
) {
1824 send
->cancelling
= TRUE
;
1825 send_cancelled
= TRUE
;
1827 KeSetEvent(&send
->cleared_event
, 0, FALSE
);
1833 ExReleaseResourceLite(&Vcb
->send_load_lock
);
1835 if (send_cancelled
) {
1836 while (Vcb
->running_sends
!= 0) {
1837 ExAcquireResourceExclusiveLite(&Vcb
->send_load_lock
, TRUE
);
1838 ExReleaseResourceLite(&Vcb
->send_load_lock
);
1843 Status
= registry_mark_volume_unmounted(&Vcb
->superblock
.uuid
);
1844 if (!NT_SUCCESS(Status
) && Status
!= STATUS_TOO_LATE
)
1845 WARN("registry_mark_volume_unmounted returned %08x\n", Status
);
1847 for (i
= 0; i
< Vcb
->calcthreads
.num_threads
; i
++) {
1848 Vcb
->calcthreads
.threads
[i
].quit
= TRUE
;
1851 KeSetEvent(&Vcb
->calcthreads
.event
, 0, FALSE
);
1853 for (i
= 0; i
< Vcb
->calcthreads
.num_threads
; i
++) {
1854 KeWaitForSingleObject(&Vcb
->calcthreads
.threads
[i
].finished
, Executive
, KernelMode
, FALSE
, NULL
);
1856 ZwClose(Vcb
->calcthreads
.threads
[i
].handle
);
1859 ExDeleteResourceLite(&Vcb
->calcthreads
.lock
);
1860 ExFreePool(Vcb
->calcthreads
.threads
);
1863 KeSetTimer(&Vcb
->flush_thread_timer
, time
, NULL
); // trigger the timer early
1864 KeWaitForSingleObject(&Vcb
->flush_thread_finished
, Executive
, KernelMode
, FALSE
, NULL
);
1866 reap_fcb(Vcb
->volume_fcb
);
1867 reap_fcb(Vcb
->dummy_fcb
);
1870 ObDereferenceObject(Vcb
->root_file
);
1872 le
= Vcb
->chunks
.Flink
;
1873 while (le
!= &Vcb
->chunks
) {
1874 chunk
* c
= CONTAINING_RECORD(le
, chunk
, list_entry
);
1884 while (!IsListEmpty(&Vcb
->roots
)) {
1885 root
* r
= CONTAINING_RECORD(RemoveHeadList(&Vcb
->roots
), root
, list_entry
);
1887 ExDeleteResourceLite(&r
->nonpaged
->load_tree_lock
);
1888 ExFreePool(r
->nonpaged
);
1892 while (!IsListEmpty(&Vcb
->chunks
)) {
1893 chunk
* c
= CONTAINING_RECORD(RemoveHeadList(&Vcb
->chunks
), chunk
, list_entry
);
1895 while (!IsListEmpty(&c
->space
)) {
1896 LIST_ENTRY
* le2
= RemoveHeadList(&c
->space
);
1897 space
* s
= CONTAINING_RECORD(le2
, space
, list_entry
);
1902 while (!IsListEmpty(&c
->deleting
)) {
1903 LIST_ENTRY
* le2
= RemoveHeadList(&c
->deleting
);
1904 space
* s
= CONTAINING_RECORD(le2
, space
, list_entry
);
1910 ExFreePool(c
->devices
);
1915 ExDeleteResourceLite(&c
->range_locks_lock
);
1916 ExDeleteResourceLite(&c
->partial_stripes_lock
);
1917 ExDeleteResourceLite(&c
->lock
);
1918 ExDeleteResourceLite(&c
->changed_extents_lock
);
1920 ExFreePool(c
->chunk_item
);
1924 // FIXME - free any open fcbs?
1926 while (!IsListEmpty(&Vcb
->devices
)) {
1927 device
* dev
= CONTAINING_RECORD(RemoveHeadList(&Vcb
->devices
), device
, list_entry
);
1929 while (!IsListEmpty(&dev
->space
)) {
1930 LIST_ENTRY
* le2
= RemoveHeadList(&dev
->space
);
1931 space
* s
= CONTAINING_RECORD(le2
, space
, list_entry
);
1939 ExAcquireResourceExclusiveLite(&Vcb
->scrub
.stats_lock
, TRUE
);
1940 while (!IsListEmpty(&Vcb
->scrub
.errors
)) {
1941 scrub_error
* err
= CONTAINING_RECORD(RemoveHeadList(&Vcb
->scrub
.errors
), scrub_error
, list_entry
);
1945 ExReleaseResourceLite(&Vcb
->scrub
.stats_lock
);
1947 ExDeleteResourceLite(&Vcb
->fcb_lock
);
1948 ExDeleteResourceLite(&Vcb
->fileref_lock
);
1949 ExDeleteResourceLite(&Vcb
->load_lock
);
1950 ExDeleteResourceLite(&Vcb
->tree_lock
);
1951 ExDeleteResourceLite(&Vcb
->chunk_lock
);
1952 ExDeleteResourceLite(&Vcb
->dirty_fcbs_lock
);
1953 ExDeleteResourceLite(&Vcb
->dirty_filerefs_lock
);
1954 ExDeleteResourceLite(&Vcb
->dirty_subvols_lock
);
1955 ExDeleteResourceLite(&Vcb
->scrub
.stats_lock
);
1956 ExDeleteResourceLite(&Vcb
->send_load_lock
);
1958 ExDeletePagedLookasideList(&Vcb
->tree_data_lookaside
);
1959 ExDeletePagedLookasideList(&Vcb
->traverse_ptr_lookaside
);
1960 ExDeletePagedLookasideList(&Vcb
->batch_item_lookaside
);
1961 ExDeletePagedLookasideList(&Vcb
->fileref_lookaside
);
1962 ExDeletePagedLookasideList(&Vcb
->fcb_lookaside
);
1963 ExDeletePagedLookasideList(&Vcb
->name_bit_lookaside
);
1964 ExDeleteNPagedLookasideList(&Vcb
->range_lock_lookaside
);
1965 ExDeleteNPagedLookasideList(&Vcb
->fileref_np_lookaside
);
1966 ExDeleteNPagedLookasideList(&Vcb
->fcb_np_lookaside
);
1968 ZwClose(Vcb
->flush_thread_handle
);
1971 static NTSTATUS
delete_fileref_fcb(_In_ file_ref
* fileref
, _In_opt_ PFILE_OBJECT FileObject
, _In_opt_ PIRP Irp
, _In_ LIST_ENTRY
* rollback
) {
1977 if (fileref
->fcb
->type
!= BTRFS_TYPE_DIRECTORY
&& fileref
->fcb
->inode_item
.st_size
> 0) {
1978 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
);
1979 if (!NT_SUCCESS(Status
)) {
1980 ERR("excise_extents returned %08x\n", Status
);
1985 fileref
->fcb
->Header
.AllocationSize
.QuadPart
= 0;
1986 fileref
->fcb
->Header
.FileSize
.QuadPart
= 0;
1987 fileref
->fcb
->Header
.ValidDataLength
.QuadPart
= 0;
1992 ccfs
.AllocationSize
= fileref
->fcb
->Header
.AllocationSize
;
1993 ccfs
.FileSize
= fileref
->fcb
->Header
.FileSize
;
1994 ccfs
.ValidDataLength
= fileref
->fcb
->Header
.ValidDataLength
;
1996 Status
= STATUS_SUCCESS
;
1999 CcSetFileSizes(FileObject
, &ccfs
);
2000 } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER
) {
2001 Status
= _SEH2_GetExceptionCode();
2004 if (!NT_SUCCESS(Status
)) {
2005 ERR("CcSetFileSizes threw exception %08x\n", Status
);
2010 fileref
->fcb
->deleted
= TRUE
;
2012 le
= fileref
->children
.Flink
;
2013 while (le
!= &fileref
->children
) {
2014 file_ref
* fr2
= CONTAINING_RECORD(le
, file_ref
, list_entry
);
2016 if (fr2
->fcb
->ads
) {
2017 fr2
->fcb
->deleted
= TRUE
;
2018 mark_fcb_dirty(fr2
->fcb
);
2024 return STATUS_SUCCESS
;
2027 NTSTATUS
delete_fileref(_In_ file_ref
* fileref
, _In_opt_ PFILE_OBJECT FileObject
, _In_ BOOL make_orphan
, _In_opt_ PIRP Irp
, _In_ LIST_ENTRY
* rollback
) {
2028 LARGE_INTEGER newlength
, time
;
2033 KeQuerySystemTime(&time
);
2034 win_time_to_unix(time
, &now
);
2036 ExAcquireResourceExclusiveLite(fileref
->fcb
->Header
.Resource
, TRUE
);
2038 if (fileref
->deleted
) {
2039 ExReleaseResourceLite(fileref
->fcb
->Header
.Resource
);
2040 return STATUS_SUCCESS
;
2043 if (fileref
->fcb
->subvol
->send_ops
> 0) {
2044 ExReleaseResourceLite(fileref
->fcb
->Header
.Resource
);
2045 return STATUS_ACCESS_DENIED
;
2048 fileref
->deleted
= TRUE
;
2049 mark_fileref_dirty(fileref
);
2051 // delete INODE_ITEM (0x1)
2053 TRACE("nlink = %u\n", fileref
->fcb
->inode_item
.st_nlink
);
2055 if (!fileref
->fcb
->ads
) {
2056 if (fileref
->parent
->fcb
->subvol
== fileref
->fcb
->subvol
) {
2059 mark_fcb_dirty(fileref
->fcb
);
2061 fileref
->fcb
->inode_item_changed
= TRUE
;
2063 if (fileref
->fcb
->inode_item
.st_nlink
> 1 || make_orphan
) {
2064 fileref
->fcb
->inode_item
.st_nlink
--;
2065 fileref
->fcb
->inode_item
.transid
= fileref
->fcb
->Vcb
->superblock
.generation
;
2066 fileref
->fcb
->inode_item
.sequence
++;
2067 fileref
->fcb
->inode_item
.st_ctime
= now
;
2069 Status
= delete_fileref_fcb(fileref
, FileObject
, Irp
, rollback
);
2070 if (!NT_SUCCESS(Status
)) {
2071 ERR("delete_fileref_fcb returned %08x\n", Status
);
2072 ExReleaseResourceLite(fileref
->fcb
->Header
.Resource
);
2078 le
= fileref
->fcb
->hardlinks
.Flink
;
2079 while (le
!= &fileref
->fcb
->hardlinks
) {
2080 hardlink
* hl
= CONTAINING_RECORD(le
, hardlink
, list_entry
);
2082 if (hl
->parent
== fileref
->parent
->fcb
->inode
&& hl
->index
== fileref
->dc
->index
) {
2083 RemoveEntryList(&hl
->list_entry
);
2085 if (hl
->name
.Buffer
)
2086 ExFreePool(hl
->name
.Buffer
);
2088 if (hl
->utf8
.Buffer
)
2089 ExFreePool(hl
->utf8
.Buffer
);
2098 } else if (fileref
->fcb
->subvol
->parent
== fileref
->parent
->fcb
->subvol
->id
) { // valid subvolume
2099 if (fileref
->fcb
->subvol
->root_item
.num_references
> 1) {
2100 fileref
->fcb
->subvol
->root_item
.num_references
--;
2102 mark_fcb_dirty(fileref
->fcb
); // so ROOT_ITEM gets updated
2106 // FIXME - we need a lock here
2108 RemoveEntryList(&fileref
->fcb
->subvol
->list_entry
);
2110 InsertTailList(&fileref
->fcb
->Vcb
->drop_roots
, &fileref
->fcb
->subvol
->list_entry
);
2112 le
= fileref
->children
.Flink
;
2113 while (le
!= &fileref
->children
) {
2114 file_ref
* fr2
= CONTAINING_RECORD(le
, file_ref
, list_entry
);
2116 if (fr2
->fcb
->ads
) {
2117 fr2
->fcb
->deleted
= TRUE
;
2118 mark_fcb_dirty(fr2
->fcb
);
2126 fileref
->fcb
->deleted
= TRUE
;
2127 mark_fcb_dirty(fileref
->fcb
);
2130 // remove dir_child from parent
2133 TRACE("delete file %.*S\n", fileref
->dc
->name
.Length
/ sizeof(WCHAR
), fileref
->dc
->name
.Buffer
);
2135 ExAcquireResourceExclusiveLite(&fileref
->parent
->fcb
->nonpaged
->dir_children_lock
, TRUE
);
2136 RemoveEntryList(&fileref
->dc
->list_entry_index
);
2138 if (!fileref
->fcb
->ads
)
2139 remove_dir_child_from_hash_lists(fileref
->parent
->fcb
, fileref
->dc
);
2141 ExReleaseResourceLite(&fileref
->parent
->fcb
->nonpaged
->dir_children_lock
);
2143 if (!fileref
->oldutf8
.Buffer
)
2144 fileref
->oldutf8
= fileref
->dc
->utf8
;
2146 ExFreePool(fileref
->dc
->utf8
.Buffer
);
2148 utf8len
= fileref
->dc
->utf8
.Length
;
2150 fileref
->oldindex
= fileref
->dc
->index
;
2152 ExFreePool(fileref
->dc
->name
.Buffer
);
2153 ExFreePool(fileref
->dc
->name_uc
.Buffer
);
2154 ExFreePool(fileref
->dc
);
2159 // update INODE_ITEM of parent
2161 ExAcquireResourceExclusiveLite(fileref
->parent
->fcb
->Header
.Resource
, TRUE
);
2163 fileref
->parent
->fcb
->inode_item
.transid
= fileref
->fcb
->Vcb
->superblock
.generation
;
2164 fileref
->parent
->fcb
->inode_item
.sequence
++;
2165 fileref
->parent
->fcb
->inode_item
.st_ctime
= now
;
2167 if (!fileref
->fcb
->ads
) {
2168 TRACE("fileref->parent->fcb->inode_item.st_size (inode %llx) was %llx\n", fileref
->parent
->fcb
->inode
, fileref
->parent
->fcb
->inode_item
.st_size
);
2169 fileref
->parent
->fcb
->inode_item
.st_size
-= utf8len
* 2;
2170 TRACE("fileref->parent->fcb->inode_item.st_size (inode %llx) now %llx\n", fileref
->parent
->fcb
->inode
, fileref
->parent
->fcb
->inode_item
.st_size
);
2171 fileref
->parent
->fcb
->inode_item
.st_mtime
= now
;
2174 fileref
->parent
->fcb
->inode_item_changed
= TRUE
;
2175 ExReleaseResourceLite(fileref
->parent
->fcb
->Header
.Resource
);
2177 if (!fileref
->fcb
->ads
&& fileref
->parent
->dc
)
2178 send_notification_fcb(fileref
->parent
, FILE_NOTIFY_CHANGE_LAST_WRITE
, FILE_ACTION_MODIFIED
, NULL
);
2180 mark_fcb_dirty(fileref
->parent
->fcb
);
2182 fileref
->fcb
->subvol
->root_item
.ctransid
= fileref
->fcb
->Vcb
->superblock
.generation
;
2183 fileref
->fcb
->subvol
->root_item
.ctime
= now
;
2185 newlength
.QuadPart
= 0;
2187 if (FileObject
&& !CcUninitializeCacheMap(FileObject
, &newlength
, NULL
))
2188 TRACE("CcUninitializeCacheMap failed\n");
2190 ExReleaseResourceLite(fileref
->fcb
->Header
.Resource
);
2192 return STATUS_SUCCESS
;
2195 _Dispatch_type_(IRP_MJ_CLEANUP
)
2196 _Function_class_(DRIVER_DISPATCH
)
2198 static NTSTATUS NTAPI
drv_cleanup(_In_ PDEVICE_OBJECT DeviceObject
, _In_ PIRP Irp
) {
2200 static NTSTATUS
drv_cleanup(_In_ PDEVICE_OBJECT DeviceObject
, _In_ PIRP Irp
) {
2203 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
2204 PFILE_OBJECT FileObject
= IrpSp
->FileObject
;
2205 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
2206 fcb
* fcb
= FileObject
->FsContext
;
2209 FsRtlEnterFileSystem();
2213 top_level
= is_top_level(Irp
);
2215 if (Vcb
&& Vcb
->type
== VCB_TYPE_VOLUME
) {
2216 Status
= vol_cleanup(DeviceObject
, Irp
);
2218 } else if (DeviceObject
== master_devobj
) {
2219 TRACE("closing file system\n");
2220 Status
= STATUS_SUCCESS
;
2222 } else if (!Vcb
|| Vcb
->type
!= VCB_TYPE_FS
) {
2223 Status
= STATUS_INVALID_PARAMETER
;
2227 if (FileObject
->Flags
& FO_CLEANUP_COMPLETE
) {
2228 TRACE("FileObject %p already cleaned up\n", FileObject
);
2229 Status
= STATUS_SUCCESS
;
2234 ERR("fcb was NULL\n");
2235 Status
= STATUS_INVALID_PARAMETER
;
2239 // We have to use the pointer to Vcb stored in the fcb, as we can receive cleanup
2240 // messages belonging to other devices.
2242 if (FileObject
&& FileObject
->FsContext
) {
2248 ccb
= FileObject
->FsContext2
;
2249 fileref
= ccb
? ccb
->fileref
: NULL
;
2251 TRACE("cleanup called for FileObject %p\n", FileObject
);
2252 TRACE("fileref %p (%S), refcount = %u, open_count = %u\n", fileref
, file_desc(FileObject
), fileref
? fileref
->refcount
: 0, fileref
? fileref
->open_count
: 0);
2254 ExAcquireResourceSharedLite(&fcb
->Vcb
->tree_lock
, TRUE
);
2256 ExAcquireResourceExclusiveLite(fcb
->Header
.Resource
, TRUE
);
2258 IoRemoveShareAccess(FileObject
, &fcb
->share_access
);
2261 FsRtlNotifyCleanup(fcb
->Vcb
->NotifySync
, &fcb
->Vcb
->DirNotifyList
, ccb
);
2264 oc
= InterlockedDecrement(&fileref
->open_count
);
2265 #ifdef DEBUG_FCB_REFCOUNTS
2266 ERR("fileref %p: open_count now %i\n", fileref
, oc
);
2270 if (ccb
&& ccb
->options
& FILE_DELETE_ON_CLOSE
&& fileref
)
2271 fileref
->delete_on_close
= TRUE
;
2273 if (fileref
&& fileref
->delete_on_close
&& fcb
->type
== BTRFS_TYPE_DIRECTORY
&& fcb
->inode_item
.st_size
> 0 && fcb
!= fcb
->Vcb
->dummy_fcb
)
2274 fileref
->delete_on_close
= FALSE
;
2276 if (fcb
->Vcb
->locked
&& fcb
->Vcb
->locked_fileobj
== FileObject
) {
2277 TRACE("unlocking volume\n");
2278 do_unlock_volume(fcb
->Vcb
);
2279 FsRtlNotifyVolumeEvent(FileObject
, FSRTL_VOLUME_UNLOCK
);
2282 if (ccb
&& ccb
->reserving
) {
2283 fcb
->subvol
->reserved
= NULL
;
2284 ccb
->reserving
= FALSE
;
2285 // FIXME - flush all of subvol's fcbs
2288 if (fileref
&& (oc
== 0 || (fileref
->delete_on_close
&& fileref
->posix_delete
))) {
2289 if (!fcb
->Vcb
->removing
) {
2290 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
2291 LIST_ENTRY rollback
;
2293 InitializeListHead(&rollback
);
2295 Status
= delete_fileref_fcb(fileref
, FileObject
, Irp
, &rollback
);
2296 if (!NT_SUCCESS(Status
)) {
2297 ERR("delete_fileref_fcb returned %08x\n", Status
);
2298 do_rollback(fcb
->Vcb
, &rollback
);
2299 ExReleaseResourceLite(fileref
->fcb
->Header
.Resource
);
2300 ExReleaseResourceLite(&fcb
->Vcb
->tree_lock
);
2304 clear_rollback(&rollback
);
2306 mark_fcb_dirty(fileref
->fcb
);
2307 } else if (fileref
->delete_on_close
&& fileref
!= fcb
->Vcb
->root_fileref
&& fcb
!= fcb
->Vcb
->volume_fcb
) {
2308 LIST_ENTRY rollback
;
2310 InitializeListHead(&rollback
);
2312 if (!fileref
->fcb
->ads
|| fileref
->dc
) {
2313 if (fileref
->fcb
->ads
) {
2314 send_notification_fileref(fileref
->parent
, fcb
->type
== BTRFS_TYPE_DIRECTORY
? FILE_NOTIFY_CHANGE_DIR_NAME
: FILE_NOTIFY_CHANGE_FILE_NAME
,
2315 FILE_ACTION_REMOVED
, &fileref
->dc
->name
);
2317 send_notification_fileref(fileref
, fcb
->type
== BTRFS_TYPE_DIRECTORY
? FILE_NOTIFY_CHANGE_DIR_NAME
: FILE_NOTIFY_CHANGE_FILE_NAME
, FILE_ACTION_REMOVED
, NULL
);
2320 ExReleaseResourceLite(fcb
->Header
.Resource
);
2323 // fileref_lock needs to be acquired before fcb->Header.Resource
2324 ExAcquireResourceExclusiveLite(&fcb
->Vcb
->fileref_lock
, TRUE
);
2326 Status
= delete_fileref(fileref
, FileObject
, oc
> 0 && fileref
->posix_delete
, Irp
, &rollback
);
2327 if (!NT_SUCCESS(Status
)) {
2328 ERR("delete_fileref returned %08x\n", Status
);
2329 do_rollback(fcb
->Vcb
, &rollback
);
2330 ExReleaseResourceLite(&fcb
->Vcb
->fileref_lock
);
2331 ExReleaseResourceLite(&fcb
->Vcb
->tree_lock
);
2335 ExReleaseResourceLite(&fcb
->Vcb
->fileref_lock
);
2337 clear_rollback(&rollback
);
2338 } else if (FileObject
->Flags
& FO_CACHE_SUPPORTED
&& fcb
->nonpaged
->segment_object
.DataSectionObject
) {
2339 IO_STATUS_BLOCK iosb
;
2340 CcFlushCache(FileObject
->SectionObjectPointer
, NULL
, 0, &iosb
);
2342 if (!NT_SUCCESS(iosb
.Status
)) {
2343 ERR("CcFlushCache returned %08x\n", iosb
.Status
);
2346 if (!ExIsResourceAcquiredSharedLite(fcb
->Header
.PagingIoResource
)) {
2347 ExAcquireResourceExclusiveLite(fcb
->Header
.PagingIoResource
, TRUE
);
2348 ExReleaseResourceLite(fcb
->Header
.PagingIoResource
);
2351 CcPurgeCacheSection(&fcb
->nonpaged
->segment_object
, NULL
, 0, FALSE
);
2353 TRACE("flushed cache on close (FileObject = %p, fcb = %p, AllocationSize = %llx, FileSize = %llx, ValidDataLength = %llx)\n",
2354 FileObject
, fcb
, fcb
->Header
.AllocationSize
.QuadPart
, fcb
->Header
.FileSize
.QuadPart
, fcb
->Header
.ValidDataLength
.QuadPart
);
2358 if (fcb
->Vcb
&& fcb
!= fcb
->Vcb
->volume_fcb
)
2359 CcUninitializeCacheMap(FileObject
, NULL
, NULL
);
2363 ExReleaseResourceLite(fcb
->Header
.Resource
);
2365 ExReleaseResourceLite(&fcb
->Vcb
->tree_lock
);
2367 FileObject
->Flags
|= FO_CLEANUP_COMPLETE
;
2370 Status
= STATUS_SUCCESS
;
2373 TRACE("returning %08x\n", Status
);
2375 Irp
->IoStatus
.Status
= Status
;
2376 Irp
->IoStatus
.Information
= 0;
2378 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
2381 IoSetTopLevelIrp(NULL
);
2383 FsRtlExitFileSystem();
2389 BOOL
get_file_attributes_from_xattr(_In_reads_bytes_(len
) char* val
, _In_ UINT16 len
, _Out_ ULONG
* atts
) {
2390 if (len
> 2 && val
[0] == '0' && val
[1] == 'x') {
2394 for (i
= 2; i
< len
; i
++) {
2397 if (val
[i
] >= '0' && val
[i
] <= '9')
2398 dosnum
|= val
[i
] - '0';
2399 else if (val
[i
] >= 'a' && val
[i
] <= 'f')
2400 dosnum
|= val
[i
] + 10 - 'a';
2401 else if (val
[i
] >= 'A' && val
[i
] <= 'F')
2402 dosnum
|= val
[i
] + 10 - 'a';
2405 TRACE("DOSATTRIB: %08x\n", dosnum
);
2415 ULONG
get_file_attributes(_In_
_Requires_lock_held_(_Curr_
->tree_lock
) device_extension
* Vcb
, _In_ root
* r
, _In_ UINT64 inode
,
2416 _In_ UINT8 type
, _In_ BOOL dotfile
, _In_ BOOL ignore_xa
, _In_opt_ PIRP Irp
) {
2421 if (!ignore_xa
&& get_xattr(Vcb
, r
, inode
, EA_DOSATTRIB
, EA_DOSATTRIB_HASH
, (UINT8
**)&eaval
, &ealen
, Irp
)) {
2424 if (get_file_attributes_from_xattr(eaval
, ealen
, &dosnum
)) {
2427 if (type
== BTRFS_TYPE_DIRECTORY
)
2428 dosnum
|= FILE_ATTRIBUTE_DIRECTORY
;
2429 else if (type
== BTRFS_TYPE_SYMLINK
)
2430 dosnum
|= FILE_ATTRIBUTE_REPARSE_POINT
;
2432 if (type
!= BTRFS_TYPE_DIRECTORY
)
2433 dosnum
&= ~FILE_ATTRIBUTE_DIRECTORY
;
2435 if (inode
== SUBVOL_ROOT_INODE
) {
2436 if (r
->root_item
.flags
& BTRFS_SUBVOL_READONLY
)
2437 dosnum
|= FILE_ATTRIBUTE_READONLY
;
2439 dosnum
&= ~FILE_ATTRIBUTE_READONLY
;
2449 case BTRFS_TYPE_DIRECTORY
:
2450 att
= FILE_ATTRIBUTE_DIRECTORY
;
2453 case BTRFS_TYPE_SYMLINK
:
2454 att
= FILE_ATTRIBUTE_REPARSE_POINT
;
2463 att
|= FILE_ATTRIBUTE_HIDDEN
;
2466 att
|= FILE_ATTRIBUTE_ARCHIVE
;
2468 if (inode
== SUBVOL_ROOT_INODE
) {
2469 if (r
->root_item
.flags
& BTRFS_SUBVOL_READONLY
)
2470 att
|= FILE_ATTRIBUTE_READONLY
;
2472 att
&= ~FILE_ATTRIBUTE_READONLY
;
2475 // FIXME - get READONLY from ii->st_mode
2476 // FIXME - return SYSTEM for block/char devices?
2479 att
= FILE_ATTRIBUTE_NORMAL
;
2484 NTSTATUS
sync_read_phys(_In_ PDEVICE_OBJECT DeviceObject
, _In_ UINT64 StartingOffset
, _In_ ULONG Length
,
2485 _Out_writes_bytes_(Length
) PUCHAR Buffer
, _In_ BOOL override
) {
2486 IO_STATUS_BLOCK IoStatus
;
2487 LARGE_INTEGER Offset
;
2489 PIO_STACK_LOCATION IrpSp
;
2491 read_context context
;
2495 RtlZeroMemory(&context
, sizeof(read_context
));
2496 KeInitializeEvent(&context
.Event
, NotificationEvent
, FALSE
);
2498 Offset
.QuadPart
= (LONGLONG
)StartingOffset
;
2500 Irp
= IoAllocateIrp(DeviceObject
->StackSize
, FALSE
);
2503 ERR("IoAllocateIrp failed\n");
2504 return STATUS_INSUFFICIENT_RESOURCES
;
2507 Irp
->Flags
|= IRP_NOCACHE
;
2508 IrpSp
= IoGetNextIrpStackLocation(Irp
);
2509 IrpSp
->MajorFunction
= IRP_MJ_READ
;
2512 IrpSp
->Flags
|= SL_OVERRIDE_VERIFY_VOLUME
;
2514 if (DeviceObject
->Flags
& DO_BUFFERED_IO
) {
2515 Irp
->AssociatedIrp
.SystemBuffer
= ExAllocatePoolWithTag(NonPagedPool
, Length
, ALLOC_TAG
);
2516 if (!Irp
->AssociatedIrp
.SystemBuffer
) {
2517 ERR("out of memory\n");
2518 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2522 Irp
->Flags
|= IRP_BUFFERED_IO
| IRP_DEALLOCATE_BUFFER
| IRP_INPUT_OPERATION
;
2524 Irp
->UserBuffer
= Buffer
;
2525 } else if (DeviceObject
->Flags
& DO_DIRECT_IO
) {
2526 Irp
->MdlAddress
= IoAllocateMdl(Buffer
, Length
, FALSE
, FALSE
, NULL
);
2527 if (!Irp
->MdlAddress
) {
2528 ERR("IoAllocateMdl failed\n");
2529 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2533 Status
= STATUS_SUCCESS
;
2536 MmProbeAndLockPages(Irp
->MdlAddress
, KernelMode
, IoWriteAccess
);
2537 } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER
) {
2538 Status
= _SEH2_GetExceptionCode();
2541 if (!NT_SUCCESS(Status
)) {
2542 ERR("MmProbeAndLockPages threw exception %08x\n", Status
);
2543 IoFreeMdl(Irp
->MdlAddress
);
2547 Irp
->UserBuffer
= Buffer
;
2549 IrpSp
->Parameters
.Read
.Length
= Length
;
2550 IrpSp
->Parameters
.Read
.ByteOffset
= Offset
;
2552 Irp
->UserIosb
= &IoStatus
;
2554 Irp
->UserEvent
= &context
.Event
;
2556 IoSetCompletionRoutine(Irp
, read_completion
, &context
, TRUE
, TRUE
, TRUE
);
2558 Status
= IoCallDriver(DeviceObject
, Irp
);
2560 if (Status
== STATUS_PENDING
) {
2561 KeWaitForSingleObject(&context
.Event
, Executive
, KernelMode
, FALSE
, NULL
);
2562 Status
= context
.iosb
.Status
;
2565 if (DeviceObject
->Flags
& DO_DIRECT_IO
) {
2566 MmUnlockPages(Irp
->MdlAddress
);
2567 IoFreeMdl(Irp
->MdlAddress
);
2576 static NTSTATUS
read_superblock(_In_ device_extension
* Vcb
, _In_ PDEVICE_OBJECT device
, _In_ UINT64 length
) {
2580 UINT8 valid_superblocks
;
2582 to_read
= device
->SectorSize
== 0 ? sizeof(superblock
) : (ULONG
)sector_align(sizeof(superblock
), device
->SectorSize
);
2584 sb
= ExAllocatePoolWithTag(NonPagedPool
, to_read
, ALLOC_TAG
);
2586 ERR("out of memory\n");
2587 return STATUS_INSUFFICIENT_RESOURCES
;
2590 if (superblock_addrs
[0] + to_read
> length
) {
2591 WARN("device was too short to have any superblock\n");
2593 return STATUS_UNRECOGNIZED_VOLUME
;
2597 valid_superblocks
= 0;
2599 while (superblock_addrs
[i
] > 0) {
2602 if (i
> 0 && superblock_addrs
[i
] + to_read
> length
)
2605 Status
= sync_read_phys(device
, superblock_addrs
[i
], to_read
, (PUCHAR
)sb
, FALSE
);
2606 if (!NT_SUCCESS(Status
)) {
2607 ERR("Failed to read superblock %u: %08x\n", i
, Status
);
2612 if (sb
->magic
!= BTRFS_MAGIC
) {
2614 TRACE("not a BTRFS volume\n");
2616 return STATUS_UNRECOGNIZED_VOLUME
;
2619 TRACE("got superblock %u!\n", i
);
2621 crc32
= ~calc_crc32c(0xffffffff, (UINT8
*)&sb
->uuid
, (ULONG
)sizeof(superblock
) - sizeof(sb
->checksum
));
2623 if (crc32
!= *((UINT32
*)sb
->checksum
))
2624 WARN("crc32 was %08x, expected %08x\n", crc32
, *((UINT32
*)sb
->checksum
));
2625 else if (sb
->sector_size
== 0)
2626 WARN("superblock sector size was 0\n");
2627 else if (sb
->node_size
< sizeof(tree_header
) + sizeof(internal_node
) || sb
->node_size
> 0x10000)
2628 WARN("invalid node size %x\n", sb
->node_size
);
2629 else if ((sb
->node_size
% sb
->sector_size
) != 0)
2630 WARN("node size %x was not a multiple of sector_size %x\n", sb
->node_size
, sb
->sector_size
);
2631 else if (valid_superblocks
== 0 || sb
->generation
> Vcb
->superblock
.generation
) {
2632 RtlCopyMemory(&Vcb
->superblock
, sb
, sizeof(superblock
));
2633 valid_superblocks
++;
2642 if (valid_superblocks
== 0) {
2643 ERR("could not find any valid superblocks\n");
2644 return STATUS_INTERNAL_ERROR
;
2647 TRACE("label is %s\n", Vcb
->superblock
.label
);
2649 return STATUS_SUCCESS
;
2652 NTSTATUS
dev_ioctl(_In_ PDEVICE_OBJECT DeviceObject
, _In_ ULONG ControlCode
, _In_reads_bytes_opt_(InputBufferSize
) PVOID InputBuffer
, _In_ ULONG InputBufferSize
,
2653 _Out_writes_bytes_opt_(OutputBufferSize
) PVOID OutputBuffer
, _In_ ULONG OutputBufferSize
, _In_ BOOLEAN Override
, _Out_opt_ IO_STATUS_BLOCK
* iosb
) {
2657 PIO_STACK_LOCATION IrpSp
;
2658 IO_STATUS_BLOCK IoStatus
;
2660 KeInitializeEvent(&Event
, NotificationEvent
, FALSE
);
2662 Irp
= IoBuildDeviceIoControlRequest(ControlCode
,
2672 if (!Irp
) return STATUS_INSUFFICIENT_RESOURCES
;
2675 IrpSp
= IoGetNextIrpStackLocation(Irp
);
2676 IrpSp
->Flags
|= SL_OVERRIDE_VERIFY_VOLUME
;
2679 Status
= IoCallDriver(DeviceObject
, Irp
);
2681 if (Status
== STATUS_PENDING
) {
2682 KeWaitForSingleObject(&Event
, Executive
, KernelMode
, FALSE
, NULL
);
2683 Status
= IoStatus
.Status
;
2692 _Requires_exclusive_lock_held_(Vcb
->tree_lock
)
2693 static NTSTATUS
add_root(_Inout_ device_extension
* Vcb
, _In_ UINT64 id
, _In_ UINT64 addr
,
2694 _In_ UINT64 generation
, _In_opt_ traverse_ptr
* tp
) {
2695 root
* r
= ExAllocatePoolWithTag(PagedPool
, sizeof(root
), ALLOC_TAG
);
2697 ERR("out of memory\n");
2698 return STATUS_INSUFFICIENT_RESOURCES
;
2703 r
->received
= FALSE
;
2705 r
->treeholder
.address
= addr
;
2706 r
->treeholder
.tree
= NULL
;
2707 r
->treeholder
.generation
= generation
;
2710 r
->fcbs_version
= 0;
2711 r
->checked_for_orphans
= FALSE
;
2712 InitializeListHead(&r
->fcbs
);
2713 RtlZeroMemory(r
->fcbs_ptrs
, sizeof(LIST_ENTRY
*) * 256);
2715 r
->nonpaged
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(root_nonpaged
), ALLOC_TAG
);
2717 ERR("out of memory\n");
2719 return STATUS_INSUFFICIENT_RESOURCES
;
2722 ExInitializeResourceLite(&r
->nonpaged
->load_tree_lock
);
2727 RtlCopyMemory(&r
->root_item
, tp
->item
->data
, min(sizeof(ROOT_ITEM
), tp
->item
->size
));
2728 if (tp
->item
->size
< sizeof(ROOT_ITEM
))
2729 RtlZeroMemory(((UINT8
*)&r
->root_item
) + tp
->item
->size
, sizeof(ROOT_ITEM
) - tp
->item
->size
);
2731 RtlZeroMemory(&r
->root_item
, sizeof(ROOT_ITEM
));
2733 if (!Vcb
->readonly
&& (r
->id
== BTRFS_ROOT_ROOT
|| r
->id
== BTRFS_ROOT_FSTREE
|| (r
->id
>= 0x100 && !(r
->id
& 0xf000000000000000)))) { // FS tree root
2734 // FIXME - don't call this if subvol is readonly (though we will have to if we ever toggle this flag)
2735 get_last_inode(Vcb
, r
, NULL
);
2737 if (r
->id
== BTRFS_ROOT_ROOT
&& r
->lastinode
< 0x100)
2738 r
->lastinode
= 0x100;
2741 InsertTailList(&Vcb
->roots
, &r
->list_entry
);
2744 case BTRFS_ROOT_ROOT
:
2748 case BTRFS_ROOT_EXTENT
:
2749 Vcb
->extent_root
= r
;
2752 case BTRFS_ROOT_CHUNK
:
2753 Vcb
->chunk_root
= r
;
2756 case BTRFS_ROOT_DEVTREE
:
2760 case BTRFS_ROOT_CHECKSUM
:
2761 Vcb
->checksum_root
= r
;
2764 case BTRFS_ROOT_UUID
:
2768 case BTRFS_ROOT_FREE_SPACE
:
2769 Vcb
->space_root
= r
;
2772 case BTRFS_ROOT_DATA_RELOC
:
2773 Vcb
->data_reloc_root
= r
;
2777 return STATUS_SUCCESS
;
2780 static NTSTATUS
look_for_roots(_Requires_exclusive_lock_held_(_Curr_
->tree_lock
) _In_ device_extension
* Vcb
, _In_opt_ PIRP Irp
) {
2781 traverse_ptr tp
, next_tp
;
2786 searchkey
.obj_id
= 0;
2787 searchkey
.obj_type
= 0;
2788 searchkey
.offset
= 0;
2790 Status
= find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, FALSE
, Irp
);
2791 if (!NT_SUCCESS(Status
)) {
2792 ERR("error - find_item returned %08x\n", Status
);
2797 TRACE("(%llx,%x,%llx)\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
2799 if (tp
.item
->key
.obj_type
== TYPE_ROOT_ITEM
) {
2800 ROOT_ITEM
* ri
= (ROOT_ITEM
*)tp
.item
->data
;
2802 if (tp
.item
->size
< offsetof(ROOT_ITEM
, byte_limit
)) {
2803 ERR("(%llx,%x,%llx) 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
));
2805 TRACE("root %llx - address %llx\n", tp
.item
->key
.obj_id
, ri
->block_number
);
2807 Status
= add_root(Vcb
, tp
.item
->key
.obj_id
, ri
->block_number
, ri
->generation
, &tp
);
2808 if (!NT_SUCCESS(Status
)) {
2809 ERR("add_root returned %08x\n", Status
);
2813 } else if (tp
.item
->key
.obj_type
== TYPE_ROOT_BACKREF
&& !IsListEmpty(&Vcb
->roots
)) {
2814 root
* lastroot
= CONTAINING_RECORD(Vcb
->roots
.Blink
, root
, list_entry
);
2816 if (lastroot
->id
== tp
.item
->key
.obj_id
)
2817 lastroot
->parent
= tp
.item
->key
.offset
;
2820 b
= find_next_item(Vcb
, &tp
, &next_tp
, FALSE
, Irp
);
2826 if (!Vcb
->readonly
&& !Vcb
->data_reloc_root
) {
2834 WARN("data reloc root doesn't exist, creating it\n");
2836 Status
= create_root(Vcb
, BTRFS_ROOT_DATA_RELOC
, &reloc_root
, FALSE
, 0, Irp
);
2838 if (!NT_SUCCESS(Status
)) {
2839 ERR("create_root returned %08x\n", Status
);
2843 reloc_root
->root_item
.inode
.generation
= 1;
2844 reloc_root
->root_item
.inode
.st_size
= 3;
2845 reloc_root
->root_item
.inode
.st_blocks
= Vcb
->superblock
.node_size
;
2846 reloc_root
->root_item
.inode
.st_nlink
= 1;
2847 reloc_root
->root_item
.inode
.st_mode
= 040755;
2848 reloc_root
->root_item
.inode
.flags
= 0xffffffff80000000;
2849 reloc_root
->root_item
.objid
= SUBVOL_ROOT_INODE
;
2850 reloc_root
->root_item
.bytes_used
= Vcb
->superblock
.node_size
;
2852 ii
= ExAllocatePoolWithTag(PagedPool
, sizeof(INODE_ITEM
), ALLOC_TAG
);
2854 ERR("out of memory\n");
2855 return STATUS_INSUFFICIENT_RESOURCES
;
2858 KeQuerySystemTime(&time
);
2859 win_time_to_unix(time
, &now
);
2861 RtlZeroMemory(ii
, sizeof(INODE_ITEM
));
2862 ii
->generation
= Vcb
->superblock
.generation
;
2863 ii
->st_blocks
= Vcb
->superblock
.node_size
;
2865 ii
->st_mode
= 040755;
2870 Status
= insert_tree_item(Vcb
, reloc_root
, SUBVOL_ROOT_INODE
, TYPE_INODE_ITEM
, 0, ii
, sizeof(INODE_ITEM
), NULL
, Irp
);
2871 if (!NT_SUCCESS(Status
)) {
2872 ERR("insert_tree_item returned %08x\n", Status
);
2877 irlen
= (UINT16
)offsetof(INODE_REF
, name
[0]) + 2;
2878 ir
= ExAllocatePoolWithTag(PagedPool
, irlen
, ALLOC_TAG
);
2880 ERR("out of memory\n");
2881 return STATUS_INSUFFICIENT_RESOURCES
;
2889 Status
= insert_tree_item(Vcb
, reloc_root
, SUBVOL_ROOT_INODE
, TYPE_INODE_REF
, SUBVOL_ROOT_INODE
, ir
, irlen
, NULL
, Irp
);
2890 if (!NT_SUCCESS(Status
)) {
2891 ERR("insert_tree_item returned %08x\n", Status
);
2896 Vcb
->data_reloc_root
= reloc_root
;
2897 Vcb
->need_write
= TRUE
;
2900 return STATUS_SUCCESS
;
2903 static NTSTATUS
find_disk_holes(_In_
_Requires_lock_held_(_Curr_
->tree_lock
) device_extension
* Vcb
, _In_ device
* dev
, _In_opt_ PIRP Irp
) {
2905 traverse_ptr tp
, next_tp
;
2910 InitializeListHead(&dev
->space
);
2912 searchkey
.obj_id
= 0;
2913 searchkey
.obj_type
= TYPE_DEV_STATS
;
2914 searchkey
.offset
= dev
->devitem
.dev_id
;
2916 Status
= find_item(Vcb
, Vcb
->dev_root
, &tp
, &searchkey
, FALSE
, Irp
);
2917 if (NT_SUCCESS(Status
) && !keycmp(tp
.item
->key
, searchkey
))
2918 RtlCopyMemory(dev
->stats
, tp
.item
->data
, min(sizeof(UINT64
) * 5, tp
.item
->size
));
2920 searchkey
.obj_id
= dev
->devitem
.dev_id
;
2921 searchkey
.obj_type
= TYPE_DEV_EXTENT
;
2922 searchkey
.offset
= 0;
2924 Status
= find_item(Vcb
, Vcb
->dev_root
, &tp
, &searchkey
, FALSE
, Irp
);
2925 if (!NT_SUCCESS(Status
)) {
2926 ERR("error - find_item returned %08x\n", Status
);
2933 if (tp
.item
->key
.obj_id
== dev
->devitem
.dev_id
&& tp
.item
->key
.obj_type
== TYPE_DEV_EXTENT
) {
2934 if (tp
.item
->size
>= sizeof(DEV_EXTENT
)) {
2935 DEV_EXTENT
* de
= (DEV_EXTENT
*)tp
.item
->data
;
2937 if (tp
.item
->key
.offset
> lastaddr
) {
2938 Status
= add_space_entry(&dev
->space
, NULL
, lastaddr
, tp
.item
->key
.offset
- lastaddr
);
2939 if (!NT_SUCCESS(Status
)) {
2940 ERR("add_space_entry returned %08x\n", Status
);
2945 lastaddr
= tp
.item
->key
.offset
+ de
->length
;
2947 ERR("(%llx,%x,%llx) 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
));
2951 b
= find_next_item(Vcb
, &tp
, &next_tp
, FALSE
, Irp
);
2955 if (tp
.item
->key
.obj_id
> searchkey
.obj_id
|| tp
.item
->key
.obj_type
> searchkey
.obj_type
)
2960 if (lastaddr
< dev
->devitem
.num_bytes
) {
2961 Status
= add_space_entry(&dev
->space
, NULL
, lastaddr
, dev
->devitem
.num_bytes
- lastaddr
);
2962 if (!NT_SUCCESS(Status
)) {
2963 ERR("add_space_entry returned %08x\n", Status
);
2968 // The Linux driver doesn't like to allocate chunks within the first megabyte of a device.
2970 space_list_subtract2(&dev
->space
, NULL
, 0, 0x100000, NULL
, NULL
);
2972 return STATUS_SUCCESS
;
2975 static void add_device_to_list(_In_ device_extension
* Vcb
, _In_ device
* dev
) {
2978 le
= Vcb
->devices
.Flink
;
2980 while (le
!= &Vcb
->devices
) {
2981 device
* dev2
= CONTAINING_RECORD(le
, device
, list_entry
);
2983 if (dev2
->devitem
.dev_id
> dev
->devitem
.dev_id
) {
2984 InsertHeadList(le
->Blink
, &dev
->list_entry
);
2991 InsertTailList(&Vcb
->devices
, &dev
->list_entry
);
2995 device
* find_device_from_uuid(_In_ device_extension
* Vcb
, _In_ BTRFS_UUID
* uuid
) {
2996 volume_device_extension
* vde
;
2997 pdo_device_extension
* pdode
;
3000 le
= Vcb
->devices
.Flink
;
3001 while (le
!= &Vcb
->devices
) {
3002 device
* dev
= CONTAINING_RECORD(le
, device
, list_entry
);
3004 TRACE("device %llx, uuid %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n", dev
->devitem
.dev_id
,
3005 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],
3006 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]);
3008 if (RtlCompareMemory(&dev
->devitem
.device_uuid
, uuid
, sizeof(BTRFS_UUID
)) == sizeof(BTRFS_UUID
)) {
3009 TRACE("returning device %llx\n", dev
->devitem
.dev_id
);
3023 ExAcquireResourceSharedLite(&pdode
->child_lock
, TRUE
);
3025 if (Vcb
->devices_loaded
< Vcb
->superblock
.num_devices
) {
3026 le
= pdode
->children
.Flink
;
3028 while (le
!= &pdode
->children
) {
3029 volume_child
* vc
= CONTAINING_RECORD(le
, volume_child
, list_entry
);
3031 if (RtlCompareMemory(uuid
, &vc
->uuid
, sizeof(BTRFS_UUID
)) == sizeof(BTRFS_UUID
)) {
3034 dev
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(device
), ALLOC_TAG
);
3036 ExReleaseResourceLite(&pdode
->child_lock
);
3037 ERR("out of memory\n");
3041 RtlZeroMemory(dev
, sizeof(device
));
3042 dev
->devobj
= vc
->devobj
;
3043 dev
->devitem
.device_uuid
= *uuid
;
3044 dev
->devitem
.dev_id
= vc
->devid
;
3045 dev
->devitem
.num_bytes
= vc
->size
;
3046 dev
->seeding
= vc
->seeding
;
3047 dev
->readonly
= dev
->seeding
;
3049 dev
->removable
= FALSE
;
3050 dev
->disk_num
= vc
->disk_num
;
3051 dev
->part_num
= vc
->part_num
;
3052 dev
->num_trim_entries
= 0;
3053 InitializeListHead(&dev
->trim_list
);
3055 add_device_to_list(Vcb
, dev
);
3056 Vcb
->devices_loaded
++;
3058 ExReleaseResourceLite(&pdode
->child_lock
);
3067 ExReleaseResourceLite(&pdode
->child_lock
);
3070 WARN("could not find device with uuid %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n",
3071 uuid
->uuid
[0], uuid
->uuid
[1], uuid
->uuid
[2], uuid
->uuid
[3], uuid
->uuid
[4], uuid
->uuid
[5], uuid
->uuid
[6], uuid
->uuid
[7],
3072 uuid
->uuid
[8], uuid
->uuid
[9], uuid
->uuid
[10], uuid
->uuid
[11], uuid
->uuid
[12], uuid
->uuid
[13], uuid
->uuid
[14], uuid
->uuid
[15]);
3077 static BOOL
is_device_removable(_In_ PDEVICE_OBJECT devobj
) {
3079 STORAGE_HOTPLUG_INFO shi
;
3081 Status
= dev_ioctl(devobj
, IOCTL_STORAGE_GET_HOTPLUG_INFO
, NULL
, 0, &shi
, sizeof(STORAGE_HOTPLUG_INFO
), TRUE
, NULL
);
3083 if (!NT_SUCCESS(Status
)) {
3084 ERR("dev_ioctl returned %08x\n", Status
);
3088 return shi
.MediaRemovable
!= 0 ? TRUE
: FALSE
;
3091 static ULONG
get_device_change_count(_In_ PDEVICE_OBJECT devobj
) {
3094 IO_STATUS_BLOCK iosb
;
3096 Status
= dev_ioctl(devobj
, IOCTL_STORAGE_CHECK_VERIFY
, NULL
, 0, &cc
, sizeof(ULONG
), TRUE
, &iosb
);
3098 if (!NT_SUCCESS(Status
)) {
3099 ERR("dev_ioctl returned %08x\n", Status
);
3103 if (iosb
.Information
< sizeof(ULONG
)) {
3104 ERR("iosb.Information was too short\n");
3111 void init_device(_In_ device_extension
* Vcb
, _Inout_ device
* dev
, _In_ BOOL get_nums
) {
3114 ATA_PASS_THROUGH_EX
* apte
;
3115 STORAGE_PROPERTY_QUERY spq
;
3116 DEVICE_TRIM_DESCRIPTOR dtd
;
3118 dev
->removable
= is_device_removable(dev
->devobj
);
3119 dev
->change_count
= dev
->removable
? get_device_change_count(dev
->devobj
) : 0;
3122 STORAGE_DEVICE_NUMBER sdn
;
3124 Status
= dev_ioctl(dev
->devobj
, IOCTL_STORAGE_GET_DEVICE_NUMBER
, NULL
, 0,
3125 &sdn
, sizeof(STORAGE_DEVICE_NUMBER
), TRUE
, NULL
);
3127 if (!NT_SUCCESS(Status
)) {
3128 WARN("IOCTL_STORAGE_GET_DEVICE_NUMBER returned %08x\n", Status
);
3129 dev
->disk_num
= 0xffffffff;
3130 dev
->part_num
= 0xffffffff;
3132 dev
->disk_num
= sdn
.DeviceNumber
;
3133 dev
->part_num
= sdn
.PartitionNumber
;
3138 dev
->readonly
= dev
->seeding
;
3140 dev
->num_trim_entries
= 0;
3141 dev
->stats_changed
= FALSE
;
3142 InitializeListHead(&dev
->trim_list
);
3144 if (!dev
->readonly
) {
3145 Status
= dev_ioctl(dev
->devobj
, IOCTL_DISK_IS_WRITABLE
, NULL
, 0,
3146 NULL
, 0, TRUE
, NULL
);
3147 if (Status
== STATUS_MEDIA_WRITE_PROTECTED
)
3148 dev
->readonly
= TRUE
;
3151 aptelen
= sizeof(ATA_PASS_THROUGH_EX
) + 512;
3152 apte
= ExAllocatePoolWithTag(NonPagedPool
, aptelen
, ALLOC_TAG
);
3154 ERR("out of memory\n");
3158 RtlZeroMemory(apte
, aptelen
);
3160 apte
->Length
= sizeof(ATA_PASS_THROUGH_EX
);
3161 apte
->AtaFlags
= ATA_FLAGS_DATA_IN
;
3162 apte
->DataTransferLength
= aptelen
- sizeof(ATA_PASS_THROUGH_EX
);
3163 apte
->TimeOutValue
= 3;
3164 apte
->DataBufferOffset
= apte
->Length
;
3165 apte
->CurrentTaskFile
[6] = IDE_COMMAND_IDENTIFY
;
3167 Status
= dev_ioctl(dev
->devobj
, IOCTL_ATA_PASS_THROUGH
, apte
, aptelen
,
3168 apte
, aptelen
, TRUE
, NULL
);
3170 if (!NT_SUCCESS(Status
))
3171 TRACE("IOCTL_ATA_PASS_THROUGH returned %08x for IDENTIFY DEVICE\n", Status
);
3173 IDENTIFY_DEVICE_DATA
* idd
= (IDENTIFY_DEVICE_DATA
*)((UINT8
*)apte
+ sizeof(ATA_PASS_THROUGH_EX
));
3175 if (idd
->CommandSetSupport
.FlushCache
) {
3176 dev
->can_flush
= TRUE
;
3177 TRACE("FLUSH CACHE supported\n");
3179 TRACE("FLUSH CACHE not supported\n");
3184 spq
.PropertyId
= StorageDeviceTrimProperty
;
3185 spq
.QueryType
= PropertyStandardQuery
;
3186 spq
.AdditionalParameters
[0] = 0;
3188 Status
= dev_ioctl(dev
->devobj
, IOCTL_STORAGE_QUERY_PROPERTY
, &spq
, sizeof(STORAGE_PROPERTY_QUERY
),
3189 &dtd
, sizeof(DEVICE_TRIM_DESCRIPTOR
), TRUE
, NULL
);
3191 if (NT_SUCCESS(Status
)) {
3192 if (dtd
.TrimEnabled
) {
3195 TRACE("TRIM supported\n");
3197 TRACE("TRIM not supported\n");
3200 RtlZeroMemory(dev
->stats
, sizeof(UINT64
) * 5);
3203 static NTSTATUS
load_chunk_root(_In_
_Requires_lock_held_(_Curr_
->tree_lock
) device_extension
* Vcb
, _In_opt_ PIRP Irp
) {
3204 traverse_ptr tp
, next_tp
;
3210 searchkey
.obj_id
= 0;
3211 searchkey
.obj_type
= 0;
3212 searchkey
.offset
= 0;
3214 Vcb
->data_flags
= 0;
3215 Vcb
->metadata_flags
= 0;
3216 Vcb
->system_flags
= 0;
3218 Status
= find_item(Vcb
, Vcb
->chunk_root
, &tp
, &searchkey
, FALSE
, Irp
);
3219 if (!NT_SUCCESS(Status
)) {
3220 ERR("error - find_item returned %08x\n", Status
);
3225 TRACE("(%llx,%x,%llx)\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
3227 if (tp
.item
->key
.obj_id
== 1 && tp
.item
->key
.obj_type
== TYPE_DEV_ITEM
) {
3228 if (tp
.item
->size
< sizeof(DEV_ITEM
)) {
3229 ERR("(%llx,%x,%llx) 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
));
3231 DEV_ITEM
* di
= (DEV_ITEM
*)tp
.item
->data
;
3235 le
= Vcb
->devices
.Flink
;
3236 while (le
!= &Vcb
->devices
) {
3237 device
* dev
= CONTAINING_RECORD(le
, device
, list_entry
);
3239 if (dev
->devobj
&& RtlCompareMemory(&dev
->devitem
.device_uuid
, &di
->device_uuid
, sizeof(BTRFS_UUID
)) == sizeof(BTRFS_UUID
)) {
3240 RtlCopyMemory(&dev
->devitem
, tp
.item
->data
, min(tp
.item
->size
, sizeof(DEV_ITEM
)));
3242 if (le
!= Vcb
->devices
.Flink
)
3243 init_device(Vcb
, dev
, TRUE
);
3252 if (!done
&& Vcb
->vde
) {
3253 volume_device_extension
* vde
= Vcb
->vde
;
3254 pdo_device_extension
* pdode
= vde
->pdode
;
3256 ExAcquireResourceSharedLite(&pdode
->child_lock
, TRUE
);
3258 if (Vcb
->devices_loaded
< Vcb
->superblock
.num_devices
) {
3259 le
= pdode
->children
.Flink
;
3261 while (le
!= &pdode
->children
) {
3262 volume_child
* vc
= CONTAINING_RECORD(le
, volume_child
, list_entry
);
3264 if (RtlCompareMemory(&di
->device_uuid
, &vc
->uuid
, sizeof(BTRFS_UUID
)) == sizeof(BTRFS_UUID
)) {
3267 dev
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(device
), ALLOC_TAG
);
3269 ExReleaseResourceLite(&pdode
->child_lock
);
3270 ERR("out of memory\n");
3271 return STATUS_INSUFFICIENT_RESOURCES
;
3274 RtlZeroMemory(dev
, sizeof(device
));
3276 dev
->devobj
= vc
->devobj
;
3277 RtlCopyMemory(&dev
->devitem
, di
, min(tp
.item
->size
, sizeof(DEV_ITEM
)));
3278 dev
->seeding
= vc
->seeding
;
3279 init_device(Vcb
, dev
, FALSE
);
3281 if (dev
->devitem
.num_bytes
> vc
->size
) {
3282 WARN("device %llx: DEV_ITEM says %llx bytes, but Windows only reports %llx\n", tp
.item
->key
.offset
,
3283 dev
->devitem
.num_bytes
, vc
->size
);
3285 dev
->devitem
.num_bytes
= vc
->size
;
3288 dev
->disk_num
= vc
->disk_num
;
3289 dev
->part_num
= vc
->part_num
;
3290 add_device_to_list(Vcb
, dev
);
3291 Vcb
->devices_loaded
++;
3301 if (!Vcb
->options
.allow_degraded
) {
3302 ERR("volume not found: device %llx, uuid %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n", tp
.item
->key
.offset
,
3303 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],
3304 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]);
3308 dev
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(device
), ALLOC_TAG
);
3310 ExReleaseResourceLite(&pdode
->child_lock
);
3311 ERR("out of memory\n");
3312 return STATUS_INSUFFICIENT_RESOURCES
;
3315 RtlZeroMemory(dev
, sizeof(device
));
3317 // Missing device, so we keep dev->devobj as NULL
3318 RtlCopyMemory(&dev
->devitem
, di
, min(tp
.item
->size
, sizeof(DEV_ITEM
)));
3319 InitializeListHead(&dev
->trim_list
);
3321 add_device_to_list(Vcb
, dev
);
3322 Vcb
->devices_loaded
++;
3326 ERR("unexpected device %llx found\n", tp
.item
->key
.offset
);
3328 ExReleaseResourceLite(&pdode
->child_lock
);
3331 } else if (tp
.item
->key
.obj_type
== TYPE_CHUNK_ITEM
) {
3332 if (tp
.item
->size
< sizeof(CHUNK_ITEM
)) {
3333 ERR("(%llx,%x,%llx) 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
));
3335 c
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(chunk
), ALLOC_TAG
);
3338 ERR("out of memory\n");
3339 return STATUS_INSUFFICIENT_RESOURCES
;
3342 c
->size
= tp
.item
->size
;
3343 c
->offset
= tp
.item
->key
.offset
;
3344 c
->used
= c
->oldused
= 0;
3345 c
->cache
= c
->old_cache
= NULL
;
3347 c
->readonly
= FALSE
;
3349 c
->cache_loaded
= FALSE
;
3351 c
->space_changed
= FALSE
;
3354 c
->chunk_item
= ExAllocatePoolWithTag(NonPagedPool
, tp
.item
->size
, ALLOC_TAG
);
3356 if (!c
->chunk_item
) {
3357 ERR("out of memory\n");
3359 return STATUS_INSUFFICIENT_RESOURCES
;
3362 RtlCopyMemory(c
->chunk_item
, tp
.item
->data
, tp
.item
->size
);
3364 if (c
->chunk_item
->type
& BLOCK_FLAG_DATA
&& c
->chunk_item
->type
> Vcb
->data_flags
)
3365 Vcb
->data_flags
= c
->chunk_item
->type
;
3367 if (c
->chunk_item
->type
& BLOCK_FLAG_METADATA
&& c
->chunk_item
->type
> Vcb
->metadata_flags
)
3368 Vcb
->metadata_flags
= c
->chunk_item
->type
;
3370 if (c
->chunk_item
->type
& BLOCK_FLAG_SYSTEM
&& c
->chunk_item
->type
> Vcb
->system_flags
)
3371 Vcb
->system_flags
= c
->chunk_item
->type
;
3373 if (c
->chunk_item
->type
& BLOCK_FLAG_RAID10
) {
3374 if (c
->chunk_item
->sub_stripes
== 0 || c
->chunk_item
->sub_stripes
> c
->chunk_item
->num_stripes
) {
3375 ERR("chunk %llx: invalid stripes (num_stripes %u, sub_stripes %u)\n", c
->offset
, c
->chunk_item
->num_stripes
, c
->chunk_item
->sub_stripes
);
3376 ExFreePool(c
->chunk_item
);
3378 return STATUS_INTERNAL_ERROR
;
3382 if (c
->chunk_item
->num_stripes
> 0) {
3383 CHUNK_ITEM_STRIPE
* cis
= (CHUNK_ITEM_STRIPE
*)&c
->chunk_item
[1];
3386 c
->devices
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(device
*) * c
->chunk_item
->num_stripes
, ALLOC_TAG
);
3389 ERR("out of memory\n");
3390 ExFreePool(c
->chunk_item
);
3392 return STATUS_INSUFFICIENT_RESOURCES
;
3395 for (i
= 0; i
< c
->chunk_item
->num_stripes
; i
++) {
3396 c
->devices
[i
] = find_device_from_uuid(Vcb
, &cis
[i
].dev_uuid
);
3397 TRACE("device %llu = %p\n", i
, c
->devices
[i
]);
3399 if (!c
->devices
[i
]) {
3400 ERR("missing device\n");
3401 ExFreePool(c
->chunk_item
);
3403 return STATUS_INTERNAL_ERROR
;
3406 if (c
->devices
[i
]->readonly
)
3410 ERR("chunk %llx: number of stripes is 0\n", c
->offset
);
3411 ExFreePool(c
->chunk_item
);
3413 return STATUS_INTERNAL_ERROR
;
3416 ExInitializeResourceLite(&c
->lock
);
3417 ExInitializeResourceLite(&c
->changed_extents_lock
);
3419 InitializeListHead(&c
->space
);
3420 InitializeListHead(&c
->space_size
);
3421 InitializeListHead(&c
->deleting
);
3422 InitializeListHead(&c
->changed_extents
);
3424 InitializeListHead(&c
->range_locks
);
3425 ExInitializeResourceLite(&c
->range_locks_lock
);
3426 KeInitializeEvent(&c
->range_locks_event
, NotificationEvent
, FALSE
);
3428 InitializeListHead(&c
->partial_stripes
);
3429 ExInitializeResourceLite(&c
->partial_stripes_lock
);
3431 c
->last_alloc_set
= FALSE
;
3435 InsertTailList(&Vcb
->chunks
, &c
->list_entry
);
3437 c
->list_entry_balance
.Flink
= NULL
;
3441 b
= find_next_item(Vcb
, &tp
, &next_tp
, FALSE
, Irp
);
3447 Vcb
->log_to_phys_loaded
= TRUE
;
3449 if (Vcb
->data_flags
== 0)
3450 Vcb
->data_flags
= BLOCK_FLAG_DATA
| (Vcb
->superblock
.num_devices
> 1 ? BLOCK_FLAG_RAID0
: 0);
3452 if (Vcb
->metadata_flags
== 0)
3453 Vcb
->metadata_flags
= BLOCK_FLAG_METADATA
| (Vcb
->superblock
.num_devices
> 1 ? BLOCK_FLAG_RAID1
: BLOCK_FLAG_DUPLICATE
);
3455 if (Vcb
->system_flags
== 0)
3456 Vcb
->system_flags
= BLOCK_FLAG_SYSTEM
| (Vcb
->superblock
.num_devices
> 1 ? BLOCK_FLAG_RAID1
: BLOCK_FLAG_DUPLICATE
);
3458 if (Vcb
->superblock
.incompat_flags
& BTRFS_INCOMPAT_FLAGS_MIXED_GROUPS
) {
3459 Vcb
->metadata_flags
|= BLOCK_FLAG_DATA
;
3460 Vcb
->data_flags
= Vcb
->metadata_flags
;
3463 return STATUS_SUCCESS
;
3466 void protect_superblocks(_Inout_ chunk
* c
) {
3468 UINT64 off_start
, off_end
;
3470 // The Linux driver also protects all the space before the first superblock.
3471 // I realize this confuses physical and logical addresses, but this is what btrfs-progs does -
3472 // evidently Linux assumes the chunk at 0 is always SINGLE.
3473 if (c
->offset
< superblock_addrs
[0])
3474 space_list_subtract(c
, FALSE
, c
->offset
, superblock_addrs
[0] - c
->offset
, NULL
);
3476 while (superblock_addrs
[i
] != 0) {
3477 CHUNK_ITEM
* ci
= c
->chunk_item
;
3478 CHUNK_ITEM_STRIPE
* cis
= (CHUNK_ITEM_STRIPE
*)&ci
[1];
3480 if (ci
->type
& BLOCK_FLAG_RAID0
|| ci
->type
& BLOCK_FLAG_RAID10
) {
3481 for (j
= 0; j
< ci
->num_stripes
; j
++) {
3482 UINT16 sub_stripes
= max(ci
->sub_stripes
, 1);
3484 if (cis
[j
].offset
+ (ci
->size
* ci
->num_stripes
/ sub_stripes
) > superblock_addrs
[i
] && cis
[j
].offset
<= superblock_addrs
[i
] + sizeof(superblock
)) {
3487 UINT16 startoffstripe
;
3490 TRACE("cut out superblock in chunk %llx\n", c
->offset
);
3492 off_start
= superblock_addrs
[i
] - cis
[j
].offset
;
3493 off_start
-= off_start
% ci
->stripe_length
;
3494 off_start
*= ci
->num_stripes
/ sub_stripes
;
3495 off_start
+= (j
/ sub_stripes
) * ci
->stripe_length
;
3497 off_end
= off_start
+ ci
->stripe_length
;
3500 get_raid0_offset(off_start
, ci
->stripe_length
, ci
->num_stripes
/ sub_stripes
, &startoff
, &startoffstripe
);
3501 TRACE("j = %u, startoffstripe = %u\n", j
, startoffstripe
);
3502 TRACE("startoff = %llx, superblock = %llx\n", startoff
+ cis
[j
].offset
, superblock_addrs
[i
]);
3505 space_list_subtract(c
, FALSE
, c
->offset
+ off_start
, off_end
- off_start
, NULL
);
3508 } else if (ci
->type
& BLOCK_FLAG_RAID5
) {
3509 UINT64 stripe_size
= ci
->size
/ (ci
->num_stripes
- 1);
3511 for (j
= 0; j
< ci
->num_stripes
; j
++) {
3512 if (cis
[j
].offset
+ stripe_size
> superblock_addrs
[i
] && cis
[j
].offset
<= superblock_addrs
[i
] + sizeof(superblock
)) {
3513 TRACE("cut out superblock in chunk %llx\n", c
->offset
);
3515 off_start
= superblock_addrs
[i
] - cis
[j
].offset
;
3516 off_start
-= off_start
% ci
->stripe_length
;
3517 off_start
*= ci
->num_stripes
- 1;
3519 off_end
= sector_align(superblock_addrs
[i
] - cis
[j
].offset
+ sizeof(superblock
), ci
->stripe_length
);
3520 off_end
*= ci
->num_stripes
- 1;
3522 TRACE("cutting out %llx, size %llx\n", c
->offset
+ off_start
, off_end
- off_start
);
3524 space_list_subtract(c
, FALSE
, c
->offset
+ off_start
, off_end
- off_start
, NULL
);
3527 } else if (ci
->type
& BLOCK_FLAG_RAID6
) {
3528 UINT64 stripe_size
= ci
->size
/ (ci
->num_stripes
- 2);
3530 for (j
= 0; j
< ci
->num_stripes
; j
++) {
3531 if (cis
[j
].offset
+ stripe_size
> superblock_addrs
[i
] && cis
[j
].offset
<= superblock_addrs
[i
] + sizeof(superblock
)) {
3532 TRACE("cut out superblock in chunk %llx\n", c
->offset
);
3534 off_start
= superblock_addrs
[i
] - cis
[j
].offset
;
3535 off_start
-= off_start
% ci
->stripe_length
;
3536 off_start
*= ci
->num_stripes
- 2;
3538 off_end
= sector_align(superblock_addrs
[i
] - cis
[j
].offset
+ sizeof(superblock
), ci
->stripe_length
);
3539 off_end
*= ci
->num_stripes
- 2;
3541 TRACE("cutting out %llx, size %llx\n", c
->offset
+ off_start
, off_end
- off_start
);
3543 space_list_subtract(c
, FALSE
, c
->offset
+ off_start
, off_end
- off_start
, NULL
);
3546 } else { // SINGLE, DUPLICATE, RAID1
3547 for (j
= 0; j
< ci
->num_stripes
; j
++) {
3548 if (cis
[j
].offset
+ ci
->size
> superblock_addrs
[i
] && cis
[j
].offset
<= superblock_addrs
[i
] + sizeof(superblock
)) {
3549 TRACE("cut out superblock in chunk %llx\n", c
->offset
);
3551 // The Linux driver protects the whole stripe in which the superblock lives
3553 off_start
= ((superblock_addrs
[i
] - cis
[j
].offset
) / c
->chunk_item
->stripe_length
) * c
->chunk_item
->stripe_length
;
3554 off_end
= sector_align(superblock_addrs
[i
] - cis
[j
].offset
+ sizeof(superblock
), c
->chunk_item
->stripe_length
);
3556 space_list_subtract(c
, FALSE
, c
->offset
+ off_start
, off_end
- off_start
, NULL
);
3565 UINT64
chunk_estimate_phys_size(device_extension
* Vcb
, chunk
* c
, UINT64 u
) {
3566 UINT64 nfactor
, dfactor
;
3568 if (c
->chunk_item
->type
& BLOCK_FLAG_DUPLICATE
|| c
->chunk_item
->type
& BLOCK_FLAG_RAID1
|| c
->chunk_item
->type
& BLOCK_FLAG_RAID10
) {
3571 } else if (c
->chunk_item
->type
& BLOCK_FLAG_RAID5
) {
3572 nfactor
= Vcb
->superblock
.num_devices
- 1;
3573 dfactor
= Vcb
->superblock
.num_devices
;
3574 } else if (c
->chunk_item
->type
& BLOCK_FLAG_RAID6
) {
3575 nfactor
= Vcb
->superblock
.num_devices
- 2;
3576 dfactor
= Vcb
->superblock
.num_devices
;
3582 return u
* dfactor
/ nfactor
;
3585 NTSTATUS
find_chunk_usage(_In_
_Requires_lock_held_(_Curr_
->tree_lock
) device_extension
* Vcb
, _In_opt_ PIRP Irp
) {
3586 LIST_ENTRY
* le
= Vcb
->chunks
.Flink
;
3590 BLOCK_GROUP_ITEM
* bgi
;
3593 searchkey
.obj_type
= TYPE_BLOCK_GROUP_ITEM
;
3595 Vcb
->superblock
.bytes_used
= 0;
3597 while (le
!= &Vcb
->chunks
) {
3598 c
= CONTAINING_RECORD(le
, chunk
, list_entry
);
3600 searchkey
.obj_id
= c
->offset
;
3601 searchkey
.offset
= c
->chunk_item
->size
;
3603 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
, Irp
);
3604 if (!NT_SUCCESS(Status
)) {
3605 ERR("error - find_item returned %08x\n", Status
);
3609 if (!keycmp(searchkey
, tp
.item
->key
)) {
3610 if (tp
.item
->size
>= sizeof(BLOCK_GROUP_ITEM
)) {
3611 bgi
= (BLOCK_GROUP_ITEM
*)tp
.item
->data
;
3613 c
->used
= c
->oldused
= bgi
->used
;
3615 TRACE("chunk %llx has %llx bytes used\n", c
->offset
, c
->used
);
3617 Vcb
->superblock
.bytes_used
+= chunk_estimate_phys_size(Vcb
, c
, bgi
->used
);
3619 ERR("(%llx;%llx,%x,%llx) is %u bytes, expected %u\n",
3620 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
));
3627 Vcb
->chunk_usage_found
= TRUE
;
3629 return STATUS_SUCCESS
;
3632 static NTSTATUS
load_sys_chunks(_In_ device_extension
* Vcb
) {
3634 ULONG n
= Vcb
->superblock
.n
;
3637 if (n
> sizeof(KEY
)) {
3638 RtlCopyMemory(&key
, &Vcb
->superblock
.sys_chunk_array
[Vcb
->superblock
.n
- n
], sizeof(KEY
));
3641 return STATUS_SUCCESS
;
3643 TRACE("bootstrap: %llx,%x,%llx\n", key
.obj_id
, key
.obj_type
, key
.offset
);
3645 if (key
.obj_type
== TYPE_CHUNK_ITEM
) {
3650 if (n
< sizeof(CHUNK_ITEM
))
3651 return STATUS_SUCCESS
;
3653 ci
= (CHUNK_ITEM
*)&Vcb
->superblock
.sys_chunk_array
[Vcb
->superblock
.n
- n
];
3654 cisize
= sizeof(CHUNK_ITEM
) + (ci
->num_stripes
* sizeof(CHUNK_ITEM_STRIPE
));
3657 return STATUS_SUCCESS
;
3659 sc
= ExAllocatePoolWithTag(PagedPool
, sizeof(sys_chunk
), ALLOC_TAG
);
3662 ERR("out of memory\n");
3663 return STATUS_INSUFFICIENT_RESOURCES
;
3668 sc
->data
= ExAllocatePoolWithTag(PagedPool
, sc
->size
, ALLOC_TAG
);
3671 ERR("out of memory\n");
3673 return STATUS_INSUFFICIENT_RESOURCES
;
3676 RtlCopyMemory(sc
->data
, ci
, sc
->size
);
3677 InsertTailList(&Vcb
->sys_chunks
, &sc
->list_entry
);
3681 ERR("unexpected item %llx,%x,%llx in bootstrap\n", key
.obj_id
, key
.obj_type
, key
.offset
);
3682 return STATUS_INTERNAL_ERROR
;
3686 return STATUS_SUCCESS
;
3690 static root
* find_default_subvol(_In_
_Requires_lock_held_(_Curr_
->tree_lock
) device_extension
* Vcb
, _In_opt_ PIRP Irp
) {
3693 static const char fn
[] = "default";
3694 static UINT32 crc32
= 0x8dbfc2d2;
3696 if (Vcb
->options
.subvol_id
!= 0) {
3697 le
= Vcb
->roots
.Flink
;
3698 while (le
!= &Vcb
->roots
) {
3699 root
* r
= CONTAINING_RECORD(le
, root
, list_entry
);
3701 if (r
->id
== Vcb
->options
.subvol_id
)
3708 if (Vcb
->superblock
.incompat_flags
& BTRFS_INCOMPAT_FLAGS_DEFAULT_SUBVOL
) {
3714 searchkey
.obj_id
= Vcb
->superblock
.root_dir_objectid
;
3715 searchkey
.obj_type
= TYPE_DIR_ITEM
;
3716 searchkey
.offset
= crc32
;
3718 Status
= find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, FALSE
, Irp
);
3719 if (!NT_SUCCESS(Status
)) {
3720 ERR("error - find_item returned %08x\n", Status
);
3724 if (keycmp(tp
.item
->key
, searchkey
)) {
3725 ERR("could not find (%llx,%x,%llx) in root tree\n", searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
);
3729 if (tp
.item
->size
< sizeof(DIR_ITEM
)) {
3730 ERR("(%llx,%x,%llx) 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
));
3734 di
= (DIR_ITEM
*)tp
.item
->data
;
3736 if (tp
.item
->size
< sizeof(DIR_ITEM
) - 1 + di
->n
) {
3737 ERR("(%llx,%x,%llx) 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
);
3741 if (di
->n
!= strlen(fn
) || RtlCompareMemory(di
->name
, fn
, di
->n
) != di
->n
) {
3742 ERR("root DIR_ITEM had same CRC32, but was not \"default\"\n");
3746 if (di
->key
.obj_type
!= TYPE_ROOT_ITEM
) {
3747 ERR("default root has key (%llx,%x,%llx), expected subvolume\n", di
->key
.obj_id
, di
->key
.obj_type
, di
->key
.offset
);
3751 le
= Vcb
->roots
.Flink
;
3752 while (le
!= &Vcb
->roots
) {
3753 root
* r
= CONTAINING_RECORD(le
, root
, list_entry
);
3755 if (r
->id
== di
->key
.obj_id
)
3761 ERR("could not find root %llx, using default instead\n", di
->key
.obj_id
);
3765 le
= Vcb
->roots
.Flink
;
3766 while (le
!= &Vcb
->roots
) {
3767 root
* r
= CONTAINING_RECORD(le
, root
, list_entry
);
3769 if (r
->id
== BTRFS_ROOT_FSTREE
)
3778 void init_file_cache(_In_ PFILE_OBJECT FileObject
, _In_ CC_FILE_SIZES
* ccfs
) {
3779 TRACE("(%p, %p)\n", FileObject
, ccfs
);
3781 CcInitializeCacheMap(FileObject
, ccfs
, FALSE
, cache_callbacks
, FileObject
);
3784 fCcSetAdditionalCacheAttributesEx(FileObject
, CC_ENABLE_DISK_IO_ACCOUNTING
);
3786 CcSetReadAheadGranularity(FileObject
, READ_AHEAD_GRANULARITY
);
3789 static NTSTATUS
create_calc_threads(_In_ PDEVICE_OBJECT DeviceObject
) {
3790 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
3793 Vcb
->calcthreads
.num_threads
= KeQueryActiveProcessorCount(NULL
);
3795 Vcb
->calcthreads
.threads
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(drv_calc_thread
) * Vcb
->calcthreads
.num_threads
, ALLOC_TAG
);
3796 if (!Vcb
->calcthreads
.threads
) {
3797 ERR("out of memory\n");
3798 return STATUS_INSUFFICIENT_RESOURCES
;
3801 InitializeListHead(&Vcb
->calcthreads
.job_list
);
3802 ExInitializeResourceLite(&Vcb
->calcthreads
.lock
);
3803 KeInitializeEvent(&Vcb
->calcthreads
.event
, NotificationEvent
, FALSE
);
3805 RtlZeroMemory(Vcb
->calcthreads
.threads
, sizeof(drv_calc_thread
) * Vcb
->calcthreads
.num_threads
);
3807 for (i
= 0; i
< Vcb
->calcthreads
.num_threads
; i
++) {
3810 Vcb
->calcthreads
.threads
[i
].DeviceObject
= DeviceObject
;
3811 KeInitializeEvent(&Vcb
->calcthreads
.threads
[i
].finished
, NotificationEvent
, FALSE
);
3813 Status
= PsCreateSystemThread(&Vcb
->calcthreads
.threads
[i
].handle
, 0, NULL
, NULL
, NULL
, calc_thread
, &Vcb
->calcthreads
.threads
[i
]);
3814 if (!NT_SUCCESS(Status
)) {
3817 ERR("PsCreateSystemThread returned %08x\n", Status
);
3819 for (j
= 0; j
< i
; j
++) {
3820 Vcb
->calcthreads
.threads
[i
].quit
= TRUE
;
3823 KeSetEvent(&Vcb
->calcthreads
.event
, 0, FALSE
);
3829 return STATUS_SUCCESS
;
3832 static BOOL
is_btrfs_volume(_In_ PDEVICE_OBJECT DeviceObject
) {
3834 MOUNTDEV_NAME mdn
, *mdn2
;
3837 Status
= dev_ioctl(DeviceObject
, IOCTL_MOUNTDEV_QUERY_DEVICE_NAME
, NULL
, 0, &mdn
, sizeof(MOUNTDEV_NAME
), TRUE
, NULL
);
3838 if (!NT_SUCCESS(Status
) && Status
!= STATUS_BUFFER_OVERFLOW
) {
3839 ERR("IOCTL_MOUNTDEV_QUERY_DEVICE_NAME returned %08x\n", Status
);
3843 mdnsize
= (ULONG
)offsetof(MOUNTDEV_NAME
, Name
[0]) + mdn
.NameLength
;
3845 mdn2
= ExAllocatePoolWithTag(PagedPool
, mdnsize
, ALLOC_TAG
);
3847 ERR("out of memory\n");
3851 Status
= dev_ioctl(DeviceObject
, IOCTL_MOUNTDEV_QUERY_DEVICE_NAME
, NULL
, 0, mdn2
, mdnsize
, TRUE
, NULL
);
3852 if (!NT_SUCCESS(Status
)) {
3853 ERR("IOCTL_MOUNTDEV_QUERY_DEVICE_NAME returned %08x\n", Status
);
3858 if (mdn2
->NameLength
> (sizeof(BTRFS_VOLUME_PREFIX
) - sizeof(WCHAR
)) &&
3859 RtlCompareMemory(mdn2
->Name
, BTRFS_VOLUME_PREFIX
, sizeof(BTRFS_VOLUME_PREFIX
) - sizeof(WCHAR
)) == sizeof(BTRFS_VOLUME_PREFIX
) - sizeof(WCHAR
)) {
3869 static NTSTATUS
get_device_pnp_name_guid(_In_ PDEVICE_OBJECT DeviceObject
, _Out_ PUNICODE_STRING pnp_name
, _In_
const GUID
* guid
) {
3871 WCHAR
*list
= NULL
, *s
;
3873 Status
= IoGetDeviceInterfaces((PVOID
)guid
, NULL
, 0, &list
);
3874 if (!NT_SUCCESS(Status
)) {
3875 ERR("IoGetDeviceInterfaces returned %08x\n", Status
);
3881 PFILE_OBJECT FileObject
;
3882 PDEVICE_OBJECT devobj
;
3883 UNICODE_STRING name
;
3885 name
.Length
= name
.MaximumLength
= (USHORT
)wcslen(s
) * sizeof(WCHAR
);
3888 if (NT_SUCCESS(IoGetDeviceObjectPointer(&name
, FILE_READ_ATTRIBUTES
, &FileObject
, &devobj
))) {
3889 if (DeviceObject
== devobj
|| DeviceObject
== FileObject
->DeviceObject
) {
3890 ObDereferenceObject(FileObject
);
3892 pnp_name
->Buffer
= ExAllocatePoolWithTag(PagedPool
, name
.Length
, ALLOC_TAG
);
3893 if (!pnp_name
->Buffer
) {
3894 ERR("out of memory\n");
3895 Status
= STATUS_INSUFFICIENT_RESOURCES
;
3899 RtlCopyMemory(pnp_name
->Buffer
, name
.Buffer
, name
.Length
);
3900 pnp_name
->Length
= pnp_name
->MaximumLength
= name
.Length
;
3902 Status
= STATUS_SUCCESS
;
3906 ObDereferenceObject(FileObject
);
3909 s
= &s
[wcslen(s
) + 1];
3912 pnp_name
->Length
= pnp_name
->MaximumLength
= 0;
3913 pnp_name
->Buffer
= 0;
3915 Status
= STATUS_NOT_FOUND
;
3924 NTSTATUS
get_device_pnp_name(_In_ PDEVICE_OBJECT DeviceObject
, _Out_ PUNICODE_STRING pnp_name
, _Out_
const GUID
** guid
) {
3927 Status
= get_device_pnp_name_guid(DeviceObject
, pnp_name
, &GUID_DEVINTERFACE_VOLUME
);
3928 if (NT_SUCCESS(Status
)) {
3929 *guid
= &GUID_DEVINTERFACE_VOLUME
;
3933 Status
= get_device_pnp_name_guid(DeviceObject
, pnp_name
, &GUID_DEVINTERFACE_HIDDEN_VOLUME
);
3934 if (NT_SUCCESS(Status
)) {
3935 *guid
= &GUID_DEVINTERFACE_HIDDEN_VOLUME
;
3939 Status
= get_device_pnp_name_guid(DeviceObject
, pnp_name
, &GUID_DEVINTERFACE_DISK
);
3940 if (NT_SUCCESS(Status
)) {
3941 *guid
= &GUID_DEVINTERFACE_DISK
;
3945 return STATUS_NOT_FOUND
;
3948 _Success_(return>=0)
3949 static NTSTATUS
check_mount_device(_In_ PDEVICE_OBJECT DeviceObject
, _Out_ BOOL
* no_pnp
) {
3954 UNICODE_STRING pnp_name
;
3957 to_read
= DeviceObject
->SectorSize
== 0 ? sizeof(superblock
) : (ULONG
)sector_align(sizeof(superblock
), DeviceObject
->SectorSize
);
3959 sb
= ExAllocatePoolWithTag(NonPagedPool
, to_read
, ALLOC_TAG
);
3961 ERR("out of memory\n");
3962 return STATUS_INSUFFICIENT_RESOURCES
;
3965 Status
= sync_read_phys(DeviceObject
, superblock_addrs
[0], to_read
, (PUCHAR
)sb
, TRUE
);
3966 if (!NT_SUCCESS(Status
)) {
3967 ERR("sync_read_phys returned %08x\n", Status
);
3971 if (sb
->magic
!= BTRFS_MAGIC
) {
3972 Status
= STATUS_SUCCESS
;
3976 crc32
= ~calc_crc32c(0xffffffff, (UINT8
*)&sb
->uuid
, (ULONG
)sizeof(superblock
) - sizeof(sb
->checksum
));
3978 if (crc32
!= *((UINT32
*)sb
->checksum
)) {
3979 WARN("crc32 was %08x, expected %08x\n", crc32
, *((UINT32
*)sb
->checksum
));
3980 Status
= STATUS_SUCCESS
;
3984 DeviceObject
->Flags
&= ~DO_VERIFY_VOLUME
;
3986 pnp_name
.Buffer
= NULL
;
3988 Status
= get_device_pnp_name(DeviceObject
, &pnp_name
, &guid
);
3989 if (!NT_SUCCESS(Status
)) {
3990 WARN("get_device_pnp_name returned %08x\n", Status
);
3991 pnp_name
.Length
= 0;
3994 if (pnp_name
.Length
== 0)
3998 volume_arrival(drvobj
, &pnp_name
);
4001 if (pnp_name
.Buffer
)
4002 ExFreePool(pnp_name
.Buffer
);
4004 Status
= STATUS_SUCCESS
;
4012 static BOOL
still_has_superblock(_In_ PDEVICE_OBJECT device
) {
4016 PDEVICE_OBJECT device2
;
4021 to_read
= device
->SectorSize
== 0 ? sizeof(superblock
) : (ULONG
)sector_align(sizeof(superblock
), device
->SectorSize
);
4023 sb
= ExAllocatePoolWithTag(NonPagedPool
, to_read
, ALLOC_TAG
);
4025 ERR("out of memory\n");
4029 Status
= sync_read_phys(device
, superblock_addrs
[0], to_read
, (PUCHAR
)sb
, TRUE
);
4030 if (!NT_SUCCESS(Status
)) {
4031 ERR("Failed to read superblock: %08x\n", Status
);
4036 if (sb
->magic
!= BTRFS_MAGIC
) {
4037 TRACE("not a BTRFS volume\n");
4041 UINT32 crc32
= ~calc_crc32c(0xffffffff, (UINT8
*)&sb
->uuid
, (ULONG
)sizeof(superblock
) - sizeof(sb
->checksum
));
4043 if (crc32
!= *((UINT32
*)sb
->checksum
)) {
4044 WARN("crc32 was %08x, expected %08x\n", crc32
, *((UINT32
*)sb
->checksum
));
4053 device2
->Flags
&= ~DO_VERIFY_VOLUME
;
4054 device2
= IoGetLowerDeviceObject(device2
);
4061 static NTSTATUS
mount_vol(_In_ PDEVICE_OBJECT DeviceObject
, _In_ PIRP Irp
) {
4062 PIO_STACK_LOCATION IrpSp
;
4063 PDEVICE_OBJECT NewDeviceObject
= NULL
;
4064 PDEVICE_OBJECT DeviceToMount
, readobj
;
4066 device_extension
* Vcb
= NULL
;
4067 LIST_ENTRY
*le
, batchlist
;
4070 fcb
* root_fcb
= NULL
;
4071 ccb
* root_ccb
= NULL
;
4072 BOOL init_lookaside
= FALSE
;
4074 volume_device_extension
* vde
= NULL
;
4075 pdo_device_extension
* pdode
= NULL
;
4077 BOOL no_pnp
= FALSE
;
4080 TRACE("(%p, %p)\n", DeviceObject
, Irp
);
4082 if (DeviceObject
!= master_devobj
) {
4083 Status
= STATUS_INVALID_DEVICE_REQUEST
;
4087 IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
4088 DeviceToMount
= IrpSp
->Parameters
.MountVolume
.DeviceObject
;
4090 if (!is_btrfs_volume(DeviceToMount
)) {
4091 Status
= check_mount_device(DeviceToMount
, &no_pnp
);
4092 if (!NT_SUCCESS(Status
))
4093 WARN("check_mount_device returned %08x\n", Status
);
4096 Status
= STATUS_UNRECOGNIZED_VOLUME
;
4102 pdo
= DeviceToMount
;
4104 while (IoGetLowerDeviceObject(pdo
)) {
4105 pdo
= IoGetLowerDeviceObject(pdo
);
4108 ExAcquireResourceSharedLite(&pdo_list_lock
, TRUE
);
4110 le
= pdo_list
.Flink
;
4111 while (le
!= &pdo_list
) {
4112 pdo_device_extension
* pdode
= CONTAINING_RECORD(le
, pdo_device_extension
, list_entry
);
4114 if (pdode
->pdo
== pdo
) {
4122 ExReleaseResourceLite(&pdo_list_lock
);
4124 if (!vde
|| vde
->type
!= VCB_TYPE_VOLUME
) {
4126 Status
= STATUS_UNRECOGNIZED_VOLUME
;
4134 ExAcquireResourceExclusiveLite(&pdode
->child_lock
, TRUE
);
4136 le
= pdode
->children
.Flink
;
4137 while (le
!= &pdode
->children
) {
4138 LIST_ENTRY
* le2
= le
->Flink
;
4140 vc
= CONTAINING_RECORD(pdode
->children
.Flink
, volume_child
, list_entry
);
4142 if (!still_has_superblock(vc
->devobj
)) {
4143 remove_volume_child(vde
, vc
, FALSE
);
4145 if (pdode
->num_children
== 0) {
4146 ERR("error - number of devices is zero\n");
4147 Status
= STATUS_INTERNAL_ERROR
;
4151 Status
= STATUS_DEVICE_NOT_READY
;
4158 if (pdode
->num_children
== 0 || pdode
->children_loaded
== 0) {
4159 ERR("error - number of devices is zero\n");
4160 Status
= STATUS_INTERNAL_ERROR
;
4164 ExConvertExclusiveToSharedLite(&pdode
->child_lock
);
4166 vc
= CONTAINING_RECORD(pdode
->children
.Flink
, volume_child
, list_entry
);
4168 readobj
= vc
->devobj
;
4169 readobjsize
= vc
->size
;
4171 vde
->device
->Characteristics
&= ~FILE_DEVICE_SECURE_OPEN
;
4173 GET_LENGTH_INFORMATION gli
;
4176 readobj
= DeviceToMount
;
4178 Status
= dev_ioctl(readobj
, IOCTL_DISK_GET_LENGTH_INFO
, NULL
, 0,
4179 &gli
, sizeof(gli
), TRUE
, NULL
);
4181 if (!NT_SUCCESS(Status
)) {
4182 ERR("error reading length information: %08x\n", Status
);
4186 readobjsize
= gli
.Length
.QuadPart
;
4189 Status
= IoCreateDevice(drvobj
, sizeof(device_extension
), NULL
, FILE_DEVICE_DISK_FILE_SYSTEM
, 0, FALSE
, &NewDeviceObject
);
4190 if (!NT_SUCCESS(Status
)) {
4191 ERR("IoCreateDevice returned %08x\n", Status
);
4192 Status
= STATUS_UNRECOGNIZED_VOLUME
;
4196 NewDeviceObject
->Flags
|= DO_DIRECT_IO
;
4198 // Some programs seem to expect that the sector size will be 512, for
4199 // FILE_NO_INTERMEDIATE_BUFFERING and the like.
4200 NewDeviceObject
->SectorSize
= min(DeviceToMount
->SectorSize
, 512);
4202 Vcb
= (PVOID
)NewDeviceObject
->DeviceExtension
;
4203 RtlZeroMemory(Vcb
, sizeof(device_extension
));
4204 Vcb
->type
= VCB_TYPE_FS
;
4207 ExInitializeResourceLite(&Vcb
->tree_lock
);
4208 Vcb
->need_write
= FALSE
;
4210 ExInitializeResourceLite(&Vcb
->fcb_lock
);
4211 ExInitializeResourceLite(&Vcb
->fileref_lock
);
4212 ExInitializeResourceLite(&Vcb
->chunk_lock
);
4213 ExInitializeResourceLite(&Vcb
->dirty_fcbs_lock
);
4214 ExInitializeResourceLite(&Vcb
->dirty_filerefs_lock
);
4215 ExInitializeResourceLite(&Vcb
->dirty_subvols_lock
);
4216 ExInitializeResourceLite(&Vcb
->scrub
.stats_lock
);
4218 ExInitializeResourceLite(&Vcb
->load_lock
);
4219 ExAcquireResourceExclusiveLite(&Vcb
->load_lock
, TRUE
);
4221 ExAcquireResourceExclusiveLite(&Vcb
->tree_lock
, TRUE
);
4223 DeviceToMount
->Flags
|= DO_DIRECT_IO
;
4225 Status
= read_superblock(Vcb
, readobj
, readobjsize
);
4226 if (!NT_SUCCESS(Status
)) {
4227 if (!IoIsErrorUserInduced(Status
))
4228 Status
= STATUS_UNRECOGNIZED_VOLUME
;
4229 else if (Irp
->Tail
.Overlay
.Thread
)
4230 IoSetHardErrorOrVerifyDevice(Irp
, readobj
);
4235 if (!vde
&& Vcb
->superblock
.num_devices
> 1) {
4236 ERR("cannot mount multi-device FS with non-PNP device\n");
4237 Status
= STATUS_UNRECOGNIZED_VOLUME
;
4241 Status
= registry_load_volume_options(Vcb
);
4242 if (!NT_SUCCESS(Status
)) {
4243 ERR("registry_load_volume_options returned %08x\n", Status
);
4247 if (pdode
&& pdode
->children_loaded
< pdode
->num_children
&& (!Vcb
->options
.allow_degraded
|| !finished_probing
|| degraded_wait
)) {
4248 ERR("could not mount as %u device(s) missing\n", pdode
->num_children
- pdode
->children_loaded
);
4249 Status
= STATUS_DEVICE_NOT_READY
;
4253 if (Vcb
->options
.ignore
) {
4254 TRACE("ignoring volume\n");
4255 Status
= STATUS_UNRECOGNIZED_VOLUME
;
4259 if (Vcb
->superblock
.incompat_flags
& ~INCOMPAT_SUPPORTED
) {
4260 WARN("cannot mount because of unsupported incompat flags (%llx)\n", Vcb
->superblock
.incompat_flags
& ~INCOMPAT_SUPPORTED
);
4261 Status
= STATUS_UNRECOGNIZED_VOLUME
;
4265 Vcb
->readonly
= FALSE
;
4266 if (Vcb
->superblock
.compat_ro_flags
& ~COMPAT_RO_SUPPORTED
) {
4267 WARN("mounting read-only because of unsupported flags (%llx)\n", Vcb
->superblock
.compat_ro_flags
& ~COMPAT_RO_SUPPORTED
);
4268 Vcb
->readonly
= TRUE
;
4271 if (Vcb
->options
.readonly
)
4272 Vcb
->readonly
= TRUE
;
4274 Vcb
->superblock
.generation
++;
4275 Vcb
->superblock
.incompat_flags
|= BTRFS_INCOMPAT_FLAGS_MIXED_BACKREF
;
4277 InitializeListHead(&Vcb
->devices
);
4278 dev
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(device
), ALLOC_TAG
);
4280 ERR("out of memory\n");
4281 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4285 dev
->devobj
= readobj
;
4286 RtlCopyMemory(&dev
->devitem
, &Vcb
->superblock
.dev_item
, sizeof(DEV_ITEM
));
4288 if (dev
->devitem
.num_bytes
> readobjsize
) {
4289 WARN("device %llx: DEV_ITEM says %llx bytes, but Windows only reports %llx\n", dev
->devitem
.dev_id
,
4290 dev
->devitem
.num_bytes
, readobjsize
);
4292 dev
->devitem
.num_bytes
= readobjsize
;
4295 dev
->seeding
= Vcb
->superblock
.flags
& BTRFS_SUPERBLOCK_FLAGS_SEEDING
? TRUE
: FALSE
;
4297 init_device(Vcb
, dev
, TRUE
);
4299 InsertTailList(&Vcb
->devices
, &dev
->list_entry
);
4300 Vcb
->devices_loaded
= 1;
4302 if (DeviceToMount
->Flags
& DO_SYSTEM_BOOT_PARTITION
)
4303 Vcb
->disallow_dismount
= TRUE
;
4305 TRACE("DeviceToMount = %p\n", DeviceToMount
);
4306 TRACE("IrpSp->Parameters.MountVolume.Vpb = %p\n", IrpSp
->Parameters
.MountVolume
.Vpb
);
4308 NewDeviceObject
->StackSize
= DeviceToMount
->StackSize
+ 1;
4309 NewDeviceObject
->Flags
&= ~DO_DEVICE_INITIALIZING
;
4311 InitializeListHead(&Vcb
->roots
);
4312 InitializeListHead(&Vcb
->drop_roots
);
4314 Vcb
->log_to_phys_loaded
= FALSE
;
4316 add_root(Vcb
, BTRFS_ROOT_CHUNK
, Vcb
->superblock
.chunk_tree_addr
, Vcb
->superblock
.chunk_root_generation
, NULL
);
4318 if (!Vcb
->chunk_root
) {
4319 ERR("Could not load chunk root.\n");
4320 Status
= STATUS_INTERNAL_ERROR
;
4324 InitializeListHead(&Vcb
->sys_chunks
);
4325 Status
= load_sys_chunks(Vcb
);
4326 if (!NT_SUCCESS(Status
)) {
4327 ERR("load_sys_chunks returned %08x\n", Status
);
4331 InitializeListHead(&Vcb
->chunks
);
4332 InitializeListHead(&Vcb
->trees
);
4333 InitializeListHead(&Vcb
->trees_hash
);
4334 InitializeListHead(&Vcb
->all_fcbs
);
4335 InitializeListHead(&Vcb
->dirty_fcbs
);
4336 InitializeListHead(&Vcb
->dirty_filerefs
);
4337 InitializeListHead(&Vcb
->dirty_subvols
);
4338 InitializeListHead(&Vcb
->send_ops
);
4340 ExInitializeFastMutex(&Vcb
->trees_list_mutex
);
4342 InitializeListHead(&Vcb
->DirNotifyList
);
4343 InitializeListHead(&Vcb
->scrub
.errors
);
4345 FsRtlNotifyInitializeSync(&Vcb
->NotifySync
);
4347 ExInitializePagedLookasideList(&Vcb
->tree_data_lookaside
, NULL
, NULL
, 0, sizeof(tree_data
), ALLOC_TAG
, 0);
4348 ExInitializePagedLookasideList(&Vcb
->traverse_ptr_lookaside
, NULL
, NULL
, 0, sizeof(traverse_ptr
), ALLOC_TAG
, 0);
4349 ExInitializePagedLookasideList(&Vcb
->batch_item_lookaside
, NULL
, NULL
, 0, sizeof(batch_item
), ALLOC_TAG
, 0);
4350 ExInitializePagedLookasideList(&Vcb
->fileref_lookaside
, NULL
, NULL
, 0, sizeof(file_ref
), ALLOC_TAG
, 0);
4351 ExInitializePagedLookasideList(&Vcb
->fcb_lookaside
, NULL
, NULL
, 0, sizeof(fcb
), ALLOC_TAG
, 0);
4352 ExInitializePagedLookasideList(&Vcb
->name_bit_lookaside
, NULL
, NULL
, 0, sizeof(name_bit
), ALLOC_TAG
, 0);
4353 ExInitializeNPagedLookasideList(&Vcb
->range_lock_lookaside
, NULL
, NULL
, 0, sizeof(range_lock
), ALLOC_TAG
, 0);
4354 ExInitializeNPagedLookasideList(&Vcb
->fileref_np_lookaside
, NULL
, NULL
, 0, sizeof(file_ref_nonpaged
), ALLOC_TAG
, 0);
4355 ExInitializeNPagedLookasideList(&Vcb
->fcb_np_lookaside
, NULL
, NULL
, 0, sizeof(fcb_nonpaged
), ALLOC_TAG
, 0);
4356 init_lookaside
= TRUE
;
4358 Vcb
->Vpb
= IrpSp
->Parameters
.MountVolume
.Vpb
;
4360 Status
= load_chunk_root(Vcb
, Irp
);
4361 if (!NT_SUCCESS(Status
)) {
4362 ERR("load_chunk_root returned %08x\n", Status
);
4366 if (Vcb
->superblock
.num_devices
> 1) {
4367 if (Vcb
->devices_loaded
< Vcb
->superblock
.num_devices
&& (!Vcb
->options
.allow_degraded
|| !finished_probing
)) {
4368 ERR("could not mount as %u device(s) missing\n", Vcb
->superblock
.num_devices
- Vcb
->devices_loaded
);
4370 IoRaiseInformationalHardError(IO_ERR_INTERNAL_ERROR
, NULL
, NULL
);
4372 Status
= STATUS_INTERNAL_ERROR
;
4376 if (dev
->readonly
&& !Vcb
->readonly
) {
4377 Vcb
->readonly
= TRUE
;
4379 le
= Vcb
->devices
.Flink
;
4380 while (le
!= &Vcb
->devices
) {
4381 device
* dev2
= CONTAINING_RECORD(le
, device
, list_entry
);
4383 if (dev2
->readonly
&& !dev2
->seeding
)
4386 if (!dev2
->readonly
) {
4387 Vcb
->readonly
= FALSE
;
4395 WARN("setting volume to readonly\n");
4398 if (dev
->readonly
) {
4399 WARN("setting volume to readonly as device is readonly\n");
4400 Vcb
->readonly
= TRUE
;
4404 add_root(Vcb
, BTRFS_ROOT_ROOT
, Vcb
->superblock
.root_tree_addr
, Vcb
->superblock
.generation
- 1, NULL
);
4406 if (!Vcb
->root_root
) {
4407 ERR("Could not load root of roots.\n");
4408 Status
= STATUS_INTERNAL_ERROR
;
4412 Status
= look_for_roots(Vcb
, Irp
);
4413 if (!NT_SUCCESS(Status
)) {
4414 ERR("look_for_roots returned %08x\n", Status
);
4418 if (!Vcb
->readonly
) {
4419 Status
= find_chunk_usage(Vcb
, Irp
);
4420 if (!NT_SUCCESS(Status
)) {
4421 ERR("find_chunk_usage returned %08x\n", Status
);
4426 InitializeListHead(&batchlist
);
4428 // We've already increased the generation by one
4429 if (!Vcb
->readonly
&& (
4430 Vcb
->options
.clear_cache
||
4431 (!(Vcb
->superblock
.compat_ro_flags
& BTRFS_COMPAT_RO_FLAGS_FREE_SPACE_CACHE
) && Vcb
->superblock
.generation
- 1 != Vcb
->superblock
.cache_generation
) ||
4432 (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
)))) {
4433 if (Vcb
->options
.clear_cache
)
4434 WARN("ClearCache option was set, clearing cache...\n");
4435 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
))
4436 WARN("clearing free-space tree created by buggy Linux driver\n");
4438 WARN("generation was %llx, free-space cache generation was %llx; clearing cache...\n", Vcb
->superblock
.generation
- 1, Vcb
->superblock
.cache_generation
);
4440 Status
= clear_free_space_cache(Vcb
, &batchlist
, Irp
);
4441 if (!NT_SUCCESS(Status
)) {
4442 ERR("clear_free_space_cache returned %08x\n", Status
);
4443 clear_batch_list(Vcb
, &batchlist
);
4448 Status
= commit_batch_list(Vcb
, &batchlist
, Irp
);
4449 if (!NT_SUCCESS(Status
)) {
4450 ERR("commit_batch_list returned %08x\n", Status
);
4454 Vcb
->volume_fcb
= create_fcb(Vcb
, NonPagedPool
);
4455 if (!Vcb
->volume_fcb
) {
4456 ERR("out of memory\n");
4457 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4461 Vcb
->volume_fcb
->Vcb
= Vcb
;
4462 Vcb
->volume_fcb
->sd
= NULL
;
4464 Vcb
->dummy_fcb
= create_fcb(Vcb
, NonPagedPool
);
4465 if (!Vcb
->dummy_fcb
) {
4466 ERR("out of memory\n");
4467 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4471 Vcb
->dummy_fcb
->Vcb
= Vcb
;
4472 Vcb
->dummy_fcb
->type
= BTRFS_TYPE_DIRECTORY
;
4473 Vcb
->dummy_fcb
->inode
= 2;
4474 Vcb
->dummy_fcb
->subvol
= Vcb
->root_root
;
4475 Vcb
->dummy_fcb
->atts
= FILE_ATTRIBUTE_DIRECTORY
;
4476 Vcb
->dummy_fcb
->inode_item
.st_nlink
= 1;
4477 Vcb
->dummy_fcb
->inode_item
.st_mode
= __S_IFDIR
;
4479 Vcb
->dummy_fcb
->hash_ptrs
= ExAllocatePoolWithTag(PagedPool
, sizeof(LIST_ENTRY
*) * 256, ALLOC_TAG
);
4480 if (!Vcb
->dummy_fcb
->hash_ptrs
) {
4481 ERR("out of memory\n");
4482 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4486 RtlZeroMemory(Vcb
->dummy_fcb
->hash_ptrs
, sizeof(LIST_ENTRY
*) * 256);
4488 Vcb
->dummy_fcb
->hash_ptrs_uc
= ExAllocatePoolWithTag(PagedPool
, sizeof(LIST_ENTRY
*) * 256, ALLOC_TAG
);
4489 if (!Vcb
->dummy_fcb
->hash_ptrs_uc
) {
4490 ERR("out of memory\n");
4491 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4495 RtlZeroMemory(Vcb
->dummy_fcb
->hash_ptrs_uc
, sizeof(LIST_ENTRY
*) * 256);
4497 root_fcb
= create_fcb(Vcb
, NonPagedPool
);
4499 ERR("out of memory\n");
4500 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4504 root_fcb
->Vcb
= Vcb
;
4505 root_fcb
->inode
= SUBVOL_ROOT_INODE
;
4506 root_fcb
->hash
= calc_crc32c(0xffffffff, (UINT8
*)&root_fcb
->inode
, sizeof(UINT64
));
4507 root_fcb
->type
= BTRFS_TYPE_DIRECTORY
;
4509 #ifdef DEBUG_FCB_REFCOUNTS
4510 WARN("volume FCB = %p\n", Vcb
->volume_fcb
);
4511 WARN("root FCB = %p\n", root_fcb
);
4514 root_fcb
->subvol
= find_default_subvol(Vcb
, Irp
);
4516 if (!root_fcb
->subvol
) {
4517 ERR("could not find top subvol\n");
4518 Status
= STATUS_INTERNAL_ERROR
;
4522 Status
= load_dir_children(Vcb
, root_fcb
, TRUE
, Irp
);
4523 if (!NT_SUCCESS(Status
)) {
4524 ERR("load_dir_children returned %08x\n", Status
);
4528 searchkey
.obj_id
= root_fcb
->inode
;
4529 searchkey
.obj_type
= TYPE_INODE_ITEM
;
4530 searchkey
.offset
= 0xffffffffffffffff;
4532 Status
= find_item(Vcb
, root_fcb
->subvol
, &tp
, &searchkey
, FALSE
, Irp
);
4533 if (!NT_SUCCESS(Status
)) {
4534 ERR("error - find_item returned %08x\n", Status
);
4538 if (tp
.item
->key
.obj_id
!= searchkey
.obj_id
|| tp
.item
->key
.obj_type
!= searchkey
.obj_type
) {
4539 ERR("couldn't find INODE_ITEM for root directory\n");
4540 Status
= STATUS_INTERNAL_ERROR
;
4544 if (tp
.item
->size
> 0)
4545 RtlCopyMemory(&root_fcb
->inode_item
, tp
.item
->data
, min(sizeof(INODE_ITEM
), tp
.item
->size
));
4547 fcb_get_sd(root_fcb
, NULL
, TRUE
, Irp
);
4549 root_fcb
->atts
= get_file_attributes(Vcb
, root_fcb
->subvol
, root_fcb
->inode
, root_fcb
->type
, FALSE
, FALSE
, Irp
);
4551 Vcb
->root_fileref
= create_fileref(Vcb
);
4552 if (!Vcb
->root_fileref
) {
4553 ERR("out of memory\n");
4554 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4558 Vcb
->root_fileref
->fcb
= root_fcb
;
4559 InsertTailList(&root_fcb
->subvol
->fcbs
, &root_fcb
->list_entry
);
4560 InsertTailList(&Vcb
->all_fcbs
, &root_fcb
->list_entry_all
);
4562 root_fcb
->subvol
->fcbs_ptrs
[root_fcb
->hash
>> 24] = &root_fcb
->list_entry
;
4564 root_fcb
->fileref
= Vcb
->root_fileref
;
4566 root_ccb
= ExAllocatePoolWithTag(PagedPool
, sizeof(ccb
), ALLOC_TAG
);
4568 ERR("out of memory\n");
4569 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4573 /* HACK: stream file object seems to get deleted at some point
4574 * leading to use after free when installing ReactOS on
4576 * Workaround: leak a handle to the fileobject
4577 * XXX: Could be improved by storing it somewhere and releasing it
4578 * on dismount. Or even by referencing again the file object.
4581 Vcb
->root_file
= IoCreateStreamFileObject(NULL
, DeviceToMount
);
4585 Vcb
->root_file
= IoCreateStreamFileObjectEx(NULL
, DeviceToMount
, &Dummy
);
4588 Vcb
->root_file
->FsContext
= root_fcb
;
4589 Vcb
->root_file
->SectionObjectPointer
= &root_fcb
->nonpaged
->segment_object
;
4590 Vcb
->root_file
->Vpb
= DeviceObject
->Vpb
;
4592 RtlZeroMemory(root_ccb
, sizeof(ccb
));
4593 root_ccb
->NodeType
= BTRFS_NODE_TYPE_CCB
;
4594 root_ccb
->NodeSize
= sizeof(ccb
);
4596 Vcb
->root_file
->FsContext2
= root_ccb
;
4599 CcInitializeCacheMap(Vcb
->root_file
, (PCC_FILE_SIZES
)(&root_fcb
->Header
.AllocationSize
), FALSE
, cache_callbacks
, Vcb
->root_file
);
4600 } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER
) {
4601 Status
= _SEH2_GetExceptionCode();
4605 le
= Vcb
->devices
.Flink
;
4606 while (le
!= &Vcb
->devices
) {
4607 device
* dev2
= CONTAINING_RECORD(le
, device
, list_entry
);
4609 Status
= find_disk_holes(Vcb
, dev2
, Irp
);
4610 if (!NT_SUCCESS(Status
)) {
4611 ERR("find_disk_holes returned %08x\n", Status
);
4618 NewDeviceObject
->Vpb
= IrpSp
->Parameters
.MountVolume
.Vpb
;
4619 IrpSp
->Parameters
.MountVolume
.Vpb
->DeviceObject
= NewDeviceObject
;
4620 IrpSp
->Parameters
.MountVolume
.Vpb
->Flags
|= VPB_MOUNTED
;
4621 NewDeviceObject
->Vpb
->VolumeLabelLength
= 4; // FIXME
4622 NewDeviceObject
->Vpb
->VolumeLabel
[0] = '?';
4623 NewDeviceObject
->Vpb
->VolumeLabel
[1] = 0;
4624 NewDeviceObject
->Vpb
->ReferenceCount
++; // FIXME - should we deref this at any point?
4626 KeInitializeEvent(&Vcb
->flush_thread_finished
, NotificationEvent
, FALSE
);
4628 Status
= PsCreateSystemThread(&Vcb
->flush_thread_handle
, 0, NULL
, NULL
, NULL
, flush_thread
, NewDeviceObject
);
4629 if (!NT_SUCCESS(Status
)) {
4630 ERR("PsCreateSystemThread returned %08x\n", Status
);
4634 Status
= create_calc_threads(NewDeviceObject
);
4635 if (!NT_SUCCESS(Status
)) {
4636 ERR("create_calc_threads returned %08x\n", Status
);
4640 Status
= registry_mark_volume_mounted(&Vcb
->superblock
.uuid
);
4641 if (!NT_SUCCESS(Status
))
4642 WARN("registry_mark_volume_mounted returned %08x\n", Status
);
4644 Status
= look_for_balance_item(Vcb
);
4645 if (!NT_SUCCESS(Status
) && Status
!= STATUS_NOT_FOUND
)
4646 WARN("look_for_balance_item returned %08x\n", Status
);
4648 Status
= STATUS_SUCCESS
;
4651 vde
->mounted_device
= NewDeviceObject
;
4653 ExInitializeResourceLite(&Vcb
->send_load_lock
);
4657 ExReleaseResourceLite(&pdode
->child_lock
);
4661 ExReleaseResourceLite(&Vcb
->tree_lock
);
4662 ExReleaseResourceLite(&Vcb
->load_lock
);
4665 if (!NT_SUCCESS(Status
)) {
4667 if (init_lookaside
) {
4668 ExDeletePagedLookasideList(&Vcb
->tree_data_lookaside
);
4669 ExDeletePagedLookasideList(&Vcb
->traverse_ptr_lookaside
);
4670 ExDeletePagedLookasideList(&Vcb
->batch_item_lookaside
);
4671 ExDeletePagedLookasideList(&Vcb
->fileref_lookaside
);
4672 ExDeletePagedLookasideList(&Vcb
->fcb_lookaside
);
4673 ExDeletePagedLookasideList(&Vcb
->name_bit_lookaside
);
4674 ExDeleteNPagedLookasideList(&Vcb
->range_lock_lookaside
);
4675 ExDeleteNPagedLookasideList(&Vcb
->fileref_np_lookaside
);
4676 ExDeleteNPagedLookasideList(&Vcb
->fcb_np_lookaside
);
4680 ObDereferenceObject(Vcb
->root_file
);
4681 else if (Vcb
->root_fileref
)
4682 free_fileref(Vcb
->root_fileref
);
4686 if (root_fcb
&& root_fcb
->refcount
== 0)
4689 if (Vcb
->volume_fcb
)
4690 reap_fcb(Vcb
->volume_fcb
);
4692 ExDeleteResourceLite(&Vcb
->tree_lock
);
4693 ExDeleteResourceLite(&Vcb
->load_lock
);
4694 ExDeleteResourceLite(&Vcb
->fcb_lock
);
4695 ExDeleteResourceLite(&Vcb
->fileref_lock
);
4696 ExDeleteResourceLite(&Vcb
->chunk_lock
);
4697 ExDeleteResourceLite(&Vcb
->dirty_fcbs_lock
);
4698 ExDeleteResourceLite(&Vcb
->dirty_filerefs_lock
);
4699 ExDeleteResourceLite(&Vcb
->dirty_subvols_lock
);
4700 ExDeleteResourceLite(&Vcb
->scrub
.stats_lock
);
4702 if (Vcb
->devices
.Flink
) {
4703 while (!IsListEmpty(&Vcb
->devices
)) {
4704 device
* dev2
= CONTAINING_RECORD(RemoveHeadList(&Vcb
->devices
), device
, list_entry
);
4711 if (NewDeviceObject
)
4712 IoDeleteDevice(NewDeviceObject
);
4714 ExAcquireResourceExclusiveLite(&global_loading_lock
, TRUE
);
4715 InsertTailList(&VcbList
, &Vcb
->list_entry
);
4716 ExReleaseResourceLite(&global_loading_lock
);
4718 FsRtlNotifyVolumeEvent(Vcb
->root_file
, FSRTL_VOLUME_MOUNT
);
4721 TRACE("mount_vol done (status: %lx)\n", Status
);
4726 static NTSTATUS
verify_device(_In_ device_extension
* Vcb
, _Inout_ device
* dev
) {
4733 return STATUS_WRONG_VOLUME
;
4735 if (dev
->removable
) {
4736 IO_STATUS_BLOCK iosb
;
4738 Status
= dev_ioctl(dev
->devobj
, IOCTL_STORAGE_CHECK_VERIFY
, NULL
, 0, &cc
, sizeof(ULONG
), TRUE
, &iosb
);
4740 if (IoIsErrorUserInduced(Status
)) {
4741 ERR("IOCTL_STORAGE_CHECK_VERIFY returned %08x (user-induced)\n", Status
);
4744 pdo_device_extension
* pdode
= Vcb
->vde
->pdode
;
4746 BOOL changed
= FALSE
;
4748 ExAcquireResourceExclusiveLite(&pdode
->child_lock
, TRUE
);
4750 le2
= pdode
->children
.Flink
;
4751 while (le2
!= &pdode
->children
) {
4752 volume_child
* vc
= CONTAINING_RECORD(le2
, volume_child
, list_entry
);
4754 if (vc
->devobj
== dev
->devobj
) {
4755 TRACE("removing device\n");
4757 remove_volume_child(Vcb
->vde
, vc
, TRUE
);
4767 ExReleaseResourceLite(&pdode
->child_lock
);
4769 } else if (!NT_SUCCESS(Status
)) {
4770 ERR("IOCTL_STORAGE_CHECK_VERIFY returned %08x\n", Status
);
4772 } else if (iosb
.Information
< sizeof(ULONG
)) {
4773 ERR("iosb.Information was too short\n");
4774 return STATUS_INTERNAL_ERROR
;
4777 dev
->change_count
= cc
;
4780 to_read
= dev
->devobj
->SectorSize
== 0 ? sizeof(superblock
) : (ULONG
)sector_align(sizeof(superblock
), dev
->devobj
->SectorSize
);
4782 sb
= ExAllocatePoolWithTag(NonPagedPool
, to_read
, ALLOC_TAG
);
4784 ERR("out of memory\n");
4785 return STATUS_INSUFFICIENT_RESOURCES
;
4788 Status
= sync_read_phys(dev
->devobj
, superblock_addrs
[0], to_read
, (PUCHAR
)sb
, TRUE
);
4789 if (!NT_SUCCESS(Status
)) {
4790 ERR("Failed to read superblock: %08x\n", Status
);
4795 if (sb
->magic
!= BTRFS_MAGIC
) {
4796 ERR("not a BTRFS volume\n");
4798 return STATUS_WRONG_VOLUME
;
4801 crc32
= ~calc_crc32c(0xffffffff, (UINT8
*)&sb
->uuid
, (ULONG
)sizeof(superblock
) - sizeof(sb
->checksum
));
4802 TRACE("crc32 was %08x, expected %08x\n", crc32
, *((UINT32
*)sb
->checksum
));
4804 if (crc32
!= *((UINT32
*)sb
->checksum
)) {
4805 ERR("checksum error\n");
4807 return STATUS_WRONG_VOLUME
;
4810 if (RtlCompareMemory(&sb
->uuid
, &Vcb
->superblock
.uuid
, sizeof(BTRFS_UUID
)) != sizeof(BTRFS_UUID
)) {
4811 ERR("different UUIDs\n");
4813 return STATUS_WRONG_VOLUME
;
4818 dev
->devobj
->Flags
&= ~DO_VERIFY_VOLUME
;
4820 return STATUS_SUCCESS
;
4823 static NTSTATUS
verify_volume(_In_ PDEVICE_OBJECT devobj
) {
4824 device_extension
* Vcb
= devobj
->DeviceExtension
;
4827 UINT64 failed_devices
= 0;
4828 BOOL locked
= FALSE
, remove
= FALSE
;
4830 if (!(Vcb
->Vpb
->Flags
& VPB_MOUNTED
))
4831 return STATUS_WRONG_VOLUME
;
4833 if (!ExIsResourceAcquiredExclusive(&Vcb
->tree_lock
)) {
4834 ExAcquireResourceExclusiveLite(&Vcb
->tree_lock
, TRUE
);
4838 if (Vcb
->removing
) {
4839 if (locked
) ExReleaseResourceLite(&Vcb
->tree_lock
);
4840 return STATUS_WRONG_VOLUME
;
4843 InterlockedIncrement(&Vcb
->open_files
); // so pnp_surprise_removal doesn't uninit the device while we're still using it
4845 le
= Vcb
->devices
.Flink
;
4846 while (le
!= &Vcb
->devices
) {
4847 device
* dev
= CONTAINING_RECORD(le
, device
, list_entry
);
4849 Status
= verify_device(Vcb
, dev
);
4850 if (!NT_SUCCESS(Status
)) {
4853 if (dev
->devobj
&& Vcb
->options
.allow_degraded
)
4860 InterlockedDecrement(&Vcb
->open_files
);
4862 if (Vcb
->removing
&& Vcb
->open_files
== 0)
4866 ExReleaseResourceLite(&Vcb
->tree_lock
);
4873 if (failed_devices
== 0 || (Vcb
->options
.allow_degraded
&& failed_devices
< Vcb
->superblock
.num_devices
)) {
4874 Vcb
->Vpb
->RealDevice
->Flags
&= ~DO_VERIFY_VOLUME
;
4876 return STATUS_SUCCESS
;
4882 _Dispatch_type_(IRP_MJ_FILE_SYSTEM_CONTROL
)
4883 _Function_class_(DRIVER_DISPATCH
)
4885 static NTSTATUS NTAPI
drv_file_system_control(_In_ PDEVICE_OBJECT DeviceObject
, _In_ PIRP Irp
) {
4887 static NTSTATUS
drv_file_system_control(_In_ PDEVICE_OBJECT DeviceObject
, _In_ PIRP Irp
) {
4889 PIO_STACK_LOCATION IrpSp
;
4891 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
4894 FsRtlEnterFileSystem();
4896 TRACE("file system control\n");
4898 top_level
= is_top_level(Irp
);
4900 if (Vcb
&& Vcb
->type
== VCB_TYPE_VOLUME
) {
4901 Status
= vol_file_system_control(DeviceObject
, Irp
);
4903 } else if (!Vcb
|| (Vcb
->type
!= VCB_TYPE_FS
&& Vcb
->type
!= VCB_TYPE_CONTROL
)) {
4904 Status
= STATUS_INVALID_PARAMETER
;
4908 Status
= STATUS_NOT_IMPLEMENTED
;
4910 IrpSp
= IoGetCurrentIrpStackLocation( Irp
);
4912 Irp
->IoStatus
.Information
= 0;
4914 switch (IrpSp
->MinorFunction
) {
4915 case IRP_MN_MOUNT_VOLUME
:
4916 TRACE("IRP_MN_MOUNT_VOLUME\n");
4918 Status
= mount_vol(DeviceObject
, Irp
);
4921 case IRP_MN_KERNEL_CALL
:
4922 TRACE("IRP_MN_KERNEL_CALL\n");
4924 Status
= fsctl_request(DeviceObject
, &Irp
, IrpSp
->Parameters
.FileSystemControl
.FsControlCode
);
4927 case IRP_MN_USER_FS_REQUEST
:
4928 TRACE("IRP_MN_USER_FS_REQUEST\n");
4930 Status
= fsctl_request(DeviceObject
, &Irp
, IrpSp
->Parameters
.FileSystemControl
.FsControlCode
);
4933 case IRP_MN_VERIFY_VOLUME
:
4934 TRACE("IRP_MN_VERIFY_VOLUME\n");
4936 Status
= verify_volume(DeviceObject
);
4938 if (!NT_SUCCESS(Status
) && Vcb
->Vpb
->Flags
& VPB_MOUNTED
) {
4939 ExAcquireResourceExclusiveLite(&Vcb
->tree_lock
, TRUE
);
4940 Vcb
->removing
= TRUE
;
4941 ExReleaseResourceLite(&Vcb
->tree_lock
);
4951 TRACE("returning %08x\n", Status
);
4954 Irp
->IoStatus
.Status
= Status
;
4956 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
4960 IoSetTopLevelIrp(NULL
);
4962 FsRtlExitFileSystem();
4967 _Dispatch_type_(IRP_MJ_LOCK_CONTROL
)
4968 _Function_class_(DRIVER_DISPATCH
)
4970 static NTSTATUS NTAPI
drv_lock_control(_In_ PDEVICE_OBJECT DeviceObject
, _In_ PIRP Irp
) {
4972 static NTSTATUS
drv_lock_control(_In_ PDEVICE_OBJECT DeviceObject
, _In_ PIRP Irp
) {
4975 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
4976 fcb
* fcb
= IrpSp
->FileObject
->FsContext
;
4977 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
4980 FsRtlEnterFileSystem();
4982 top_level
= is_top_level(Irp
);
4984 if (Vcb
&& Vcb
->type
== VCB_TYPE_VOLUME
) {
4985 Status
= vol_lock_control(DeviceObject
, Irp
);
4987 Irp
->IoStatus
.Status
= Status
;
4988 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
4993 TRACE("lock control\n");
4995 Status
= FsRtlProcessFileLock(&fcb
->lock
, Irp
, NULL
);
4997 fcb
->Header
.IsFastIoPossible
= fast_io_possible(fcb
);
5000 TRACE("returning %08x\n", Status
);
5003 IoSetTopLevelIrp(NULL
);
5005 FsRtlExitFileSystem();
5010 _Dispatch_type_(IRP_MJ_SHUTDOWN
)
5011 _Function_class_(DRIVER_DISPATCH
)
5013 static NTSTATUS NTAPI
drv_shutdown(_In_ PDEVICE_OBJECT DeviceObject
, _In_ PIRP Irp
) {
5015 static NTSTATUS
drv_shutdown(_In_ PDEVICE_OBJECT DeviceObject
, _In_ PIRP Irp
) {
5019 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
5022 FsRtlEnterFileSystem();
5024 TRACE("shutdown\n");
5026 top_level
= is_top_level(Irp
);
5028 if (Vcb
&& Vcb
->type
== VCB_TYPE_VOLUME
) {
5029 Status
= vol_shutdown(DeviceObject
, Irp
);
5033 Status
= STATUS_SUCCESS
;
5035 shutting_down
= TRUE
;
5036 KeSetEvent(&mountmgr_thread_event
, 0, FALSE
);
5039 while (le
!= &VcbList
) {
5041 LIST_ENTRY
* le2
= le
->Flink
;
5043 Vcb
= CONTAINING_RECORD(le
, device_extension
, list_entry
);
5045 TRACE("shutting down Vcb %p\n", Vcb
);
5047 ExAcquireResourceExclusiveLite(&Vcb
->tree_lock
, TRUE
);
5048 Vcb
->removing
= TRUE
;
5049 open_files
= Vcb
->open_files
> 0;
5051 if (Vcb
->need_write
&& !Vcb
->readonly
) {
5052 Status
= do_write(Vcb
, Irp
);
5053 if (!NT_SUCCESS(Status
))
5054 ERR("do_write returned %08x\n", Status
);
5059 ExReleaseResourceLite(&Vcb
->tree_lock
);
5069 ObDereferenceObject(comfo
);
5076 Irp
->IoStatus
.Status
= Status
;
5077 Irp
->IoStatus
.Information
= 0;
5079 IoCompleteRequest( Irp
, IO_NO_INCREMENT
);
5082 IoSetTopLevelIrp(NULL
);
5084 FsRtlExitFileSystem();
5089 _Dispatch_type_(IRP_MJ_POWER
)
5090 _Function_class_(DRIVER_DISPATCH
)
5092 static NTSTATUS NTAPI
drv_power(_In_ PDEVICE_OBJECT DeviceObject
, _In_ PIRP Irp
) {
5094 static NTSTATUS
drv_power(_In_ PDEVICE_OBJECT DeviceObject
, _In_ PIRP Irp
) {
5097 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
5100 FsRtlEnterFileSystem();
5102 top_level
= is_top_level(Irp
);
5104 Irp
->IoStatus
.Information
= 0;
5106 if (Vcb
&& Vcb
->type
== VCB_TYPE_VOLUME
) {
5107 Status
= vol_power(DeviceObject
, Irp
);
5109 Irp
->IoStatus
.Status
= Status
;
5110 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
5113 } else if (Vcb
&& Vcb
->type
== VCB_TYPE_FS
) {
5114 IoSkipCurrentIrpStackLocation(Irp
);
5116 Status
= IoCallDriver(Vcb
->Vpb
->RealDevice
, Irp
);
5121 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5122 Irp
->IoStatus
.Status
= Status
;
5123 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
5127 IoSetTopLevelIrp(NULL
);
5129 FsRtlExitFileSystem();
5134 _Dispatch_type_(IRP_MJ_SYSTEM_CONTROL
)
5135 _Function_class_(DRIVER_DISPATCH
)
5137 static NTSTATUS NTAPI
drv_system_control(_In_ PDEVICE_OBJECT DeviceObject
, _In_ PIRP Irp
) {
5139 static NTSTATUS
drv_system_control(_In_ PDEVICE_OBJECT DeviceObject
, _In_ PIRP Irp
) {
5142 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
5145 FsRtlEnterFileSystem();
5147 top_level
= is_top_level(Irp
);
5149 Irp
->IoStatus
.Information
= 0;
5151 if (Vcb
&& Vcb
->type
== VCB_TYPE_VOLUME
) {
5152 volume_device_extension
* vde
= DeviceObject
->DeviceExtension
;
5154 IoSkipCurrentIrpStackLocation(Irp
);
5156 Status
= IoCallDriver(vde
->pdo
, Irp
);
5159 } else if (Vcb
&& Vcb
->type
== VCB_TYPE_FS
) {
5160 IoSkipCurrentIrpStackLocation(Irp
);
5162 Status
= IoCallDriver(Vcb
->Vpb
->RealDevice
, Irp
);
5167 Status
= Irp
->IoStatus
.Status
;
5168 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
5172 IoSetTopLevelIrp(NULL
);
5174 FsRtlExitFileSystem();
5179 BOOL
is_file_name_valid(_In_ PUNICODE_STRING us
, _In_ BOOL posix
) {
5182 if (us
->Length
< sizeof(WCHAR
))
5185 if (us
->Length
> 255 * sizeof(WCHAR
))
5188 for (i
= 0; i
< us
->Length
/ sizeof(WCHAR
); i
++) {
5189 if (us
->Buffer
[i
] == '/' || us
->Buffer
[i
] == 0 ||
5190 (!posix
&& (us
->Buffer
[i
] == '<' || us
->Buffer
[i
] == '>' || us
->Buffer
[i
] == ':' || us
->Buffer
[i
] == '"' ||
5191 us
->Buffer
[i
] == '|' || us
->Buffer
[i
] == '?' || us
->Buffer
[i
] == '*' || (us
->Buffer
[i
] >= 1 && us
->Buffer
[i
] <= 31))))
5195 if (us
->Buffer
[0] == '.' && (us
->Length
== sizeof(WCHAR
) || (us
->Length
== 2 * sizeof(WCHAR
) && us
->Buffer
[1] == '.')))
5201 void chunk_lock_range(_In_ device_extension
* Vcb
, _In_ chunk
* c
, _In_ UINT64 start
, _In_ UINT64 length
) {
5206 rl
= ExAllocateFromNPagedLookasideList(&Vcb
->range_lock_lookaside
);
5208 ERR("out of memory\n");
5213 rl
->length
= length
;
5214 rl
->thread
= PsGetCurrentThread();
5219 ExAcquireResourceExclusiveLite(&c
->range_locks_lock
, TRUE
);
5221 le
= c
->range_locks
.Flink
;
5222 while (le
!= &c
->range_locks
) {
5223 range_lock
* rl2
= CONTAINING_RECORD(le
, range_lock
, list_entry
);
5225 if (rl2
->start
< start
+ length
&& rl2
->start
+ rl2
->length
> start
&& rl2
->thread
!= PsGetCurrentThread()) {
5234 InsertTailList(&c
->range_locks
, &rl
->list_entry
);
5236 ExReleaseResourceLite(&c
->range_locks_lock
);
5240 KeClearEvent(&c
->range_locks_event
);
5242 ExReleaseResourceLite(&c
->range_locks_lock
);
5244 KeWaitForSingleObject(&c
->range_locks_event
, UserRequest
, KernelMode
, FALSE
, NULL
);
5248 void chunk_unlock_range(_In_ device_extension
* Vcb
, _In_ chunk
* c
, _In_ UINT64 start
, _In_ UINT64 length
) {
5251 ExAcquireResourceExclusiveLite(&c
->range_locks_lock
, TRUE
);
5253 le
= c
->range_locks
.Flink
;
5254 while (le
!= &c
->range_locks
) {
5255 range_lock
* rl
= CONTAINING_RECORD(le
, range_lock
, list_entry
);
5257 if (rl
->start
== start
&& rl
->length
== length
) {
5258 RemoveEntryList(&rl
->list_entry
);
5259 ExFreeToNPagedLookasideList(&Vcb
->range_lock_lookaside
, rl
);
5266 KeSetEvent(&c
->range_locks_event
, 0, FALSE
);
5268 ExReleaseResourceLite(&c
->range_locks_lock
);
5271 void log_device_error(_In_ device_extension
* Vcb
, _Inout_ device
* dev
, _In_
int error
) {
5272 dev
->stats
[error
]++;
5273 dev
->stats_changed
= TRUE
;
5274 Vcb
->stats_changed
= TRUE
;
5278 _Function_class_(KSTART_ROUTINE
)
5279 static void serial_thread(void* context
) {
5280 LARGE_INTEGER due_time
;
5285 KeInitializeTimer(&timer
);
5287 due_time
.QuadPart
= (UINT64
)-10000000;
5289 KeSetTimer(&timer
, due_time
, NULL
);
5292 KeWaitForSingleObject(&timer
, Executive
, KernelMode
, FALSE
, NULL
);
5299 KeSetTimer(&timer
, due_time
, NULL
);
5302 KeCancelTimer(&timer
);
5304 PsTerminateSystemThread(STATUS_SUCCESS
);
5306 serial_thread_handle
= NULL
;
5309 static void init_serial(BOOL first_time
) {
5312 Status
= IoGetDeviceObjectPointer(&log_device
, FILE_WRITE_DATA
, &comfo
, &comdo
);
5313 if (!NT_SUCCESS(Status
)) {
5314 ERR("IoGetDeviceObjectPointer returned %08x\n", Status
);
5319 Status
= PsCreateSystemThread(&serial_thread_handle
, 0, NULL
, NULL
, NULL
, serial_thread
, NULL
);
5320 if (!NT_SUCCESS(Status
)) {
5321 ERR("PsCreateSystemThread returned %08x\n", Status
);
5330 static void check_cpu() {
5331 unsigned int cpuInfo
[4];
5333 __get_cpuid(1, &cpuInfo
[0], &cpuInfo
[1], &cpuInfo
[2], &cpuInfo
[3]);
5334 have_sse42
= cpuInfo
[2] & bit_SSE4_2
;
5335 have_sse2
= cpuInfo
[3] & bit_SSE2
;
5337 __cpuid(cpuInfo
, 1);
5338 have_sse42
= cpuInfo
[2] & (1 << 20);
5339 have_sse2
= cpuInfo
[3] & (1 << 26);
5343 TRACE("SSE4.2 is supported\n");
5345 TRACE("SSE4.2 not supported\n");
5348 TRACE("SSE2 is supported\n");
5350 TRACE("SSE2 is not supported\n");
5355 static void init_logging() {
5356 ExAcquireResourceExclusiveLite(&log_lock
, TRUE
);
5358 if (log_device
.Length
> 0)
5360 else if (log_file
.Length
> 0) {
5362 OBJECT_ATTRIBUTES oa
;
5363 IO_STATUS_BLOCK iosb
;
5368 InitializeObjectAttributes(&oa
, &log_file
, OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
, NULL
, NULL
);
5370 Status
= ZwCreateFile(&log_handle
, FILE_WRITE_DATA
, &oa
, &iosb
, NULL
, FILE_ATTRIBUTE_NORMAL
, FILE_SHARE_READ
,
5371 FILE_OPEN_IF
, FILE_NON_DIRECTORY_FILE
| FILE_WRITE_THROUGH
| FILE_SYNCHRONOUS_IO_ALERT
, NULL
, 0);
5373 if (!NT_SUCCESS(Status
)) {
5374 ERR("ZwCreateFile returned %08x\n", Status
);
5378 if (iosb
.Information
== FILE_OPENED
) { // already exists
5379 FILE_STANDARD_INFORMATION fsi
;
5380 FILE_POSITION_INFORMATION fpi
;
5382 static const char delim
[] = "\n---\n";
5384 // move to end of file
5386 Status
= ZwQueryInformationFile(log_handle
, &iosb
, &fsi
, sizeof(FILE_STANDARD_INFORMATION
), FileStandardInformation
);
5388 if (!NT_SUCCESS(Status
)) {
5389 ERR("ZwQueryInformationFile returned %08x\n", Status
);
5393 fpi
.CurrentByteOffset
= fsi
.EndOfFile
;
5395 Status
= ZwSetInformationFile(log_handle
, &iosb
, &fpi
, sizeof(FILE_POSITION_INFORMATION
), FilePositionInformation
);
5397 if (!NT_SUCCESS(Status
)) {
5398 ERR("ZwSetInformationFile returned %08x\n", Status
);
5402 Status
= ZwWriteFile(log_handle
, NULL
, NULL
, NULL
, &iosb
, (void*)delim
, sizeof(delim
) - 1, NULL
, NULL
);
5404 if (!NT_SUCCESS(Status
)) {
5405 ERR("ZwWriteFile returned %08x\n", Status
);
5410 dateline
= ExAllocatePoolWithTag(PagedPool
, 256, ALLOC_TAG
);
5413 ERR("out of memory\n");
5417 KeQuerySystemTime(&time
);
5419 RtlTimeToTimeFields(&time
, &tf
);
5421 sprintf(dateline
, "Starting logging at %04i-%02i-%02i %02i:%02i:%02i\n", tf
.Year
, tf
.Month
, tf
.Day
, tf
.Hour
, tf
.Minute
, tf
.Second
);
5423 Status
= ZwWriteFile(log_handle
, NULL
, NULL
, NULL
, &iosb
, dateline
, (ULONG
)strlen(dateline
), NULL
, NULL
);
5425 ExFreePool(dateline
);
5427 if (!NT_SUCCESS(Status
)) {
5428 ERR("ZwWriteFile returned %08x\n", Status
);
5434 ExReleaseResourceLite(&log_lock
);
5438 _Function_class_(KSTART_ROUTINE
)
5440 static void NTAPI
degraded_wait_thread(_In_
void* context
) {
5442 static void degraded_wait_thread(_In_
void* context
) {
5445 LARGE_INTEGER delay
;
5449 KeInitializeTimer(&timer
);
5451 delay
.QuadPart
= -30000000; // wait three seconds
5452 KeSetTimer(&timer
, delay
, NULL
);
5453 KeWaitForSingleObject(&timer
, Executive
, KernelMode
, FALSE
, NULL
);
5455 TRACE("timer expired\n");
5457 degraded_wait
= FALSE
;
5459 ZwClose(degraded_wait_handle
);
5460 degraded_wait_handle
= NULL
;
5462 PsTerminateSystemThread(STATUS_SUCCESS
);
5466 NTSTATUS NTAPI
AddDevice(PDRIVER_OBJECT DriverObject
, PDEVICE_OBJECT PhysicalDeviceObject
) {
5468 NTSTATUS
AddDevice(PDRIVER_OBJECT DriverObject
, PDEVICE_OBJECT PhysicalDeviceObject
) {
5472 UNICODE_STRING volname
;
5474 pdo_device_extension
* pdode
= NULL
;
5475 PDEVICE_OBJECT voldev
;
5476 volume_device_extension
* vde
;
5478 TRACE("(%p, %p)\n", DriverObject
, PhysicalDeviceObject
);
5480 ExAcquireResourceSharedLite(&pdo_list_lock
, TRUE
);
5482 le
= pdo_list
.Flink
;
5483 while (le
!= &pdo_list
) {
5484 pdo_device_extension
* pdode2
= CONTAINING_RECORD(le
, pdo_device_extension
, list_entry
);
5486 if (pdode2
->pdo
== PhysicalDeviceObject
) {
5495 WARN("unrecognized PDO %p\n", PhysicalDeviceObject
);
5496 Status
= STATUS_NOT_SUPPORTED
;
5500 ExAcquireResourceSharedLite(&pdode
->child_lock
, TRUE
);
5502 volname
.Length
= volname
.MaximumLength
= (sizeof(BTRFS_VOLUME_PREFIX
) - sizeof(WCHAR
)) + ((36 + 1) * sizeof(WCHAR
));
5503 volname
.Buffer
= ExAllocatePoolWithTag(PagedPool
, volname
.MaximumLength
, ALLOC_TAG
); // FIXME - when do we free this?
5505 if (!volname
.Buffer
) {
5506 ERR("out of memory\n");
5507 Status
= STATUS_INSUFFICIENT_RESOURCES
;
5511 RtlCopyMemory(volname
.Buffer
, BTRFS_VOLUME_PREFIX
, sizeof(BTRFS_VOLUME_PREFIX
) - sizeof(WCHAR
));
5513 j
= (sizeof(BTRFS_VOLUME_PREFIX
) / sizeof(WCHAR
)) - 1;
5514 for (i
= 0; i
< 16; i
++) {
5515 volname
.Buffer
[j
] = hex_digit(pdode
->uuid
.uuid
[i
] >> 4); j
++;
5516 volname
.Buffer
[j
] = hex_digit(pdode
->uuid
.uuid
[i
] & 0xf); j
++;
5518 if (i
== 3 || i
== 5 || i
== 7 || i
== 9) {
5519 volname
.Buffer
[j
] = '-';
5524 volname
.Buffer
[j
] = '}';
5526 Status
= IoCreateDevice(drvobj
, sizeof(volume_device_extension
), &volname
, FILE_DEVICE_DISK
,
5527 RtlIsNtDdiVersionAvailable(NTDDI_WIN8
) ? FILE_DEVICE_ALLOW_APPCONTAINER_TRAVERSAL
: 0, FALSE
, &voldev
);
5528 if (!NT_SUCCESS(Status
)) {
5529 ERR("IoCreateDevice returned %08x\n", Status
);
5533 voldev
->SectorSize
= PhysicalDeviceObject
->SectorSize
;
5534 voldev
->Flags
|= DO_DIRECT_IO
;
5536 vde
= voldev
->DeviceExtension
;
5537 vde
->type
= VCB_TYPE_VOLUME
;
5538 vde
->name
= volname
;
5539 vde
->device
= voldev
;
5540 vde
->mounted_device
= NULL
;
5541 vde
->pdo
= PhysicalDeviceObject
;
5543 vde
->removing
= FALSE
;
5544 vde
->open_count
= 0;
5546 Status
= IoRegisterDeviceInterface(PhysicalDeviceObject
, &GUID_DEVINTERFACE_VOLUME
, NULL
, &vde
->bus_name
);
5547 if (!NT_SUCCESS(Status
))
5548 WARN("IoRegisterDeviceInterface returned %08x\n", Status
);
5550 vde
->attached_device
= IoAttachDeviceToDeviceStack(voldev
, PhysicalDeviceObject
);
5554 if (pdode
->removable
)
5555 voldev
->Characteristics
|= FILE_REMOVABLE_MEDIA
;
5557 voldev
->Flags
&= ~DO_DEVICE_INITIALIZING
;
5559 Status
= IoSetDeviceInterfaceState(&vde
->bus_name
, TRUE
);
5560 if (!NT_SUCCESS(Status
))
5561 WARN("IoSetDeviceInterfaceState returned %08x\n", Status
);
5563 Status
= STATUS_SUCCESS
;
5566 ExReleaseResourceLite(&pdode
->child_lock
);
5569 ExReleaseResourceLite(&pdo_list_lock
);
5574 _Function_class_(DRIVER_INITIALIZE
)
5576 NTSTATUS NTAPI
DriverEntry(_In_ PDRIVER_OBJECT DriverObject
, _In_ PUNICODE_STRING RegistryPath
) {
5578 NTSTATUS
DriverEntry(_In_ PDRIVER_OBJECT DriverObject
, _In_ PUNICODE_STRING RegistryPath
) {
5581 PDEVICE_OBJECT DeviceObject
;
5582 UNICODE_STRING device_nameW
;
5583 UNICODE_STRING dosdevice_nameW
;
5584 control_device_extension
* cde
;
5586 OBJECT_ATTRIBUTES oa
;
5589 InitializeListHead(&uid_map_list
);
5590 InitializeListHead(&gid_map_list
);
5593 ExInitializeResourceLite(&log_lock
);
5595 ExInitializeResourceLite(&mapping_lock
);
5597 log_device
.Buffer
= NULL
;
5598 log_device
.Length
= log_device
.MaximumLength
= 0;
5599 log_file
.Buffer
= NULL
;
5600 log_file
.Length
= log_file
.MaximumLength
= 0;
5602 registry_path
.Length
= registry_path
.MaximumLength
= RegistryPath
->Length
;
5603 registry_path
.Buffer
= ExAllocatePoolWithTag(PagedPool
, registry_path
.Length
, ALLOC_TAG
);
5605 if (!registry_path
.Buffer
) {
5606 ERR("out of memory\n");
5607 return STATUS_INSUFFICIENT_RESOURCES
;
5610 RtlCopyMemory(registry_path
.Buffer
, RegistryPath
->Buffer
, registry_path
.Length
);
5612 read_registry(®istry_path
, FALSE
);
5615 if (debug_log_level
> 0)
5621 TRACE("DriverEntry\n");
5627 if (RtlIsNtDdiVersionAvailable(NTDDI_WIN8
)) {
5628 UNICODE_STRING name
;
5629 tPsIsDiskCountersEnabled fPsIsDiskCountersEnabled
;
5631 RtlInitUnicodeString(&name
, L
"PsIsDiskCountersEnabled");
5632 fPsIsDiskCountersEnabled
= (tPsIsDiskCountersEnabled
)MmGetSystemRoutineAddress(&name
);
5634 if (fPsIsDiskCountersEnabled
) {
5635 diskacc
= fPsIsDiskCountersEnabled();
5637 RtlInitUnicodeString(&name
, L
"PsUpdateDiskCounters");
5638 fPsUpdateDiskCounters
= (tPsUpdateDiskCounters
)MmGetSystemRoutineAddress(&name
);
5640 if (!fPsUpdateDiskCounters
)
5643 RtlInitUnicodeString(&name
, L
"FsRtlUpdateDiskCounters");
5644 fFsRtlUpdateDiskCounters
= (tFsRtlUpdateDiskCounters
)MmGetSystemRoutineAddress(&name
);
5647 RtlInitUnicodeString(&name
, L
"CcCopyReadEx");
5648 fCcCopyReadEx
= (tCcCopyReadEx
)MmGetSystemRoutineAddress(&name
);
5650 RtlInitUnicodeString(&name
, L
"CcCopyWriteEx");
5651 fCcCopyWriteEx
= (tCcCopyWriteEx
)MmGetSystemRoutineAddress(&name
);
5653 RtlInitUnicodeString(&name
, L
"CcSetAdditionalCacheAttributesEx");
5654 fCcSetAdditionalCacheAttributesEx
= (tCcSetAdditionalCacheAttributesEx
)MmGetSystemRoutineAddress(&name
);
5656 fPsUpdateDiskCounters
= NULL
;
5657 fCcCopyReadEx
= NULL
;
5658 fCcCopyWriteEx
= NULL
;
5659 fCcSetAdditionalCacheAttributesEx
= NULL
;
5660 fFsRtlUpdateDiskCounters
= NULL
;
5663 drvobj
= DriverObject
;
5665 DriverObject
->DriverUnload
= DriverUnload
;
5667 DriverObject
->DriverExtension
->AddDevice
= AddDevice
;
5670 DriverObject
->MajorFunction
[IRP_MJ_CREATE
] = drv_create
;
5671 DriverObject
->MajorFunction
[IRP_MJ_CLOSE
] = drv_close
;
5672 DriverObject
->MajorFunction
[IRP_MJ_READ
] = drv_read
;
5673 DriverObject
->MajorFunction
[IRP_MJ_WRITE
] = drv_write
;
5674 DriverObject
->MajorFunction
[IRP_MJ_QUERY_INFORMATION
] = drv_query_information
;
5675 DriverObject
->MajorFunction
[IRP_MJ_SET_INFORMATION
] = drv_set_information
;
5676 DriverObject
->MajorFunction
[IRP_MJ_QUERY_EA
] = drv_query_ea
;
5677 DriverObject
->MajorFunction
[IRP_MJ_SET_EA
] = drv_set_ea
;
5678 DriverObject
->MajorFunction
[IRP_MJ_FLUSH_BUFFERS
] = drv_flush_buffers
;
5679 DriverObject
->MajorFunction
[IRP_MJ_QUERY_VOLUME_INFORMATION
] = drv_query_volume_information
;
5680 DriverObject
->MajorFunction
[IRP_MJ_SET_VOLUME_INFORMATION
] = drv_set_volume_information
;
5681 DriverObject
->MajorFunction
[IRP_MJ_DIRECTORY_CONTROL
] = drv_directory_control
;
5682 DriverObject
->MajorFunction
[IRP_MJ_FILE_SYSTEM_CONTROL
] = drv_file_system_control
;
5683 DriverObject
->MajorFunction
[IRP_MJ_DEVICE_CONTROL
] = drv_device_control
;
5684 DriverObject
->MajorFunction
[IRP_MJ_SHUTDOWN
] = drv_shutdown
;
5685 DriverObject
->MajorFunction
[IRP_MJ_LOCK_CONTROL
] = drv_lock_control
;
5686 DriverObject
->MajorFunction
[IRP_MJ_CLEANUP
] = drv_cleanup
;
5687 DriverObject
->MajorFunction
[IRP_MJ_QUERY_SECURITY
] = drv_query_security
;
5688 DriverObject
->MajorFunction
[IRP_MJ_SET_SECURITY
] = drv_set_security
;
5689 DriverObject
->MajorFunction
[IRP_MJ_POWER
] = drv_power
;
5690 DriverObject
->MajorFunction
[IRP_MJ_SYSTEM_CONTROL
] = drv_system_control
;
5691 DriverObject
->MajorFunction
[IRP_MJ_PNP
] = drv_pnp
;
5693 DriverObject
->MajorFunction
[IRP_MJ_CREATE
] = (PDRIVER_DISPATCH
)drv_create
;
5694 DriverObject
->MajorFunction
[IRP_MJ_CLOSE
] = (PDRIVER_DISPATCH
)drv_close
;
5695 DriverObject
->MajorFunction
[IRP_MJ_READ
] = (PDRIVER_DISPATCH
)drv_read
;
5696 DriverObject
->MajorFunction
[IRP_MJ_WRITE
] = (PDRIVER_DISPATCH
)drv_write
;
5697 DriverObject
->MajorFunction
[IRP_MJ_QUERY_INFORMATION
] = (PDRIVER_DISPATCH
)drv_query_information
;
5698 DriverObject
->MajorFunction
[IRP_MJ_SET_INFORMATION
] = (PDRIVER_DISPATCH
)drv_set_information
;
5699 DriverObject
->MajorFunction
[IRP_MJ_QUERY_EA
] = (PDRIVER_DISPATCH
)drv_query_ea
;
5700 DriverObject
->MajorFunction
[IRP_MJ_SET_EA
] = (PDRIVER_DISPATCH
)drv_set_ea
;
5701 DriverObject
->MajorFunction
[IRP_MJ_FLUSH_BUFFERS
] = (PDRIVER_DISPATCH
)drv_flush_buffers
;
5702 DriverObject
->MajorFunction
[IRP_MJ_QUERY_VOLUME_INFORMATION
] = (PDRIVER_DISPATCH
)drv_query_volume_information
;
5703 DriverObject
->MajorFunction
[IRP_MJ_SET_VOLUME_INFORMATION
] = (PDRIVER_DISPATCH
)drv_set_volume_information
;
5704 DriverObject
->MajorFunction
[IRP_MJ_DIRECTORY_CONTROL
] = (PDRIVER_DISPATCH
)drv_directory_control
;
5705 DriverObject
->MajorFunction
[IRP_MJ_FILE_SYSTEM_CONTROL
] = (PDRIVER_DISPATCH
)drv_file_system_control
;
5706 DriverObject
->MajorFunction
[IRP_MJ_DEVICE_CONTROL
] = (PDRIVER_DISPATCH
)drv_device_control
;
5707 DriverObject
->MajorFunction
[IRP_MJ_SHUTDOWN
] = (PDRIVER_DISPATCH
)drv_shutdown
;
5708 DriverObject
->MajorFunction
[IRP_MJ_LOCK_CONTROL
] = (PDRIVER_DISPATCH
)drv_lock_control
;
5709 DriverObject
->MajorFunction
[IRP_MJ_CLEANUP
] = (PDRIVER_DISPATCH
)drv_cleanup
;
5710 DriverObject
->MajorFunction
[IRP_MJ_QUERY_SECURITY
] = (PDRIVER_DISPATCH
)drv_query_security
;
5711 DriverObject
->MajorFunction
[IRP_MJ_SET_SECURITY
] = (PDRIVER_DISPATCH
)drv_set_security
;
5712 DriverObject
->MajorFunction
[IRP_MJ_POWER
] = (PDRIVER_DISPATCH
)drv_power
;
5713 DriverObject
->MajorFunction
[IRP_MJ_SYSTEM_CONTROL
] = (PDRIVER_DISPATCH
)drv_system_control
;
5714 DriverObject
->MajorFunction
[IRP_MJ_PNP
] = (PDRIVER_DISPATCH
)drv_pnp
;
5717 init_fast_io_dispatch(&DriverObject
->FastIoDispatch
);
5719 device_nameW
.Buffer
= (WCHAR
*)device_name
;
5720 device_nameW
.Length
= device_nameW
.MaximumLength
= sizeof(device_name
) - sizeof(WCHAR
);
5721 dosdevice_nameW
.Buffer
= (WCHAR
*)dosdevice_name
;
5722 dosdevice_nameW
.Length
= dosdevice_nameW
.MaximumLength
= sizeof(dosdevice_name
) - sizeof(WCHAR
);
5724 Status
= IoCreateDevice(DriverObject
, sizeof(control_device_extension
), &device_nameW
, FILE_DEVICE_DISK_FILE_SYSTEM
,
5725 FILE_DEVICE_SECURE_OPEN
, FALSE
, &DeviceObject
);
5726 if (!NT_SUCCESS(Status
)) {
5727 ERR("IoCreateDevice returned %08x\n", Status
);
5731 master_devobj
= DeviceObject
;
5732 cde
= (control_device_extension
*)master_devobj
->DeviceExtension
;
5734 RtlZeroMemory(cde
, sizeof(control_device_extension
));
5736 cde
->type
= VCB_TYPE_CONTROL
;
5738 DeviceObject
->Flags
&= ~DO_DEVICE_INITIALIZING
;
5740 Status
= IoCreateSymbolicLink(&dosdevice_nameW
, &device_nameW
);
5741 if (!NT_SUCCESS(Status
)) {
5742 ERR("IoCreateSymbolicLink returned %08x\n", Status
);
5746 Status
= init_cache();
5747 if (!NT_SUCCESS(Status
)) {
5748 ERR("init_cache returned %08x\n", Status
);
5752 InitializeListHead(&VcbList
);
5753 ExInitializeResourceLite(&global_loading_lock
);
5754 ExInitializeResourceLite(&pdo_list_lock
);
5756 InitializeListHead(&pdo_list
);
5758 InitializeObjectAttributes(&oa
, RegistryPath
, OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
, NULL
, NULL
);
5759 Status
= ZwCreateKey(®h
, KEY_QUERY_VALUE
| KEY_ENUMERATE_SUB_KEYS
| KEY_NOTIFY
, &oa
, 0, NULL
, REG_OPTION_NON_VOLATILE
, &dispos
);
5760 /* ReactOS specific hack: allow BtrFS driver to start in 1st stage with no hive */
5762 if (!NT_SUCCESS(Status
)) {
5763 ERR("ZwCreateKey returned %08x\n", Status
);
5767 watch_registry(regh
);
5769 if (NT_SUCCESS(Status
)) {
5770 watch_registry(regh
);
5774 Status
= IoReportDetectedDevice(drvobj
, InterfaceTypeUndefined
, 0xFFFFFFFF, 0xFFFFFFFF,
5775 NULL
, NULL
, 0, &cde
->buspdo
);
5776 if (!NT_SUCCESS(Status
)) {
5777 ERR("IoReportDetectedDevice returned %08x\n", Status
);
5781 Status
= IoRegisterDeviceInterface(cde
->buspdo
, &BtrfsBusInterface
, NULL
, &cde
->bus_name
);
5782 if (!NT_SUCCESS(Status
))
5783 WARN("IoRegisterDeviceInterface returned %08x\n", Status
);
5785 cde
->attached_device
= IoAttachDeviceToDeviceStack(DeviceObject
, cde
->buspdo
);
5787 Status
= IoSetDeviceInterfaceState(&cde
->bus_name
, TRUE
);
5788 if (!NT_SUCCESS(Status
))
5789 WARN("IoSetDeviceInterfaceState returned %08x\n", Status
);
5791 DeviceObject
->Flags
&= ~DO_DEVICE_INITIALIZING
;
5793 IoInvalidateDeviceRelations(cde
->buspdo
, BusRelations
);
5795 Status
= PsCreateSystemThread(°raded_wait_handle
, 0, NULL
, NULL
, NULL
, degraded_wait_thread
, NULL
);
5796 if (!NT_SUCCESS(Status
))
5797 WARN("PsCreateSystemThread returned %08x\n", Status
);
5799 Status
= IoRegisterPlugPlayNotification(EventCategoryDeviceInterfaceChange
, PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES
,
5800 (PVOID
)&GUID_DEVINTERFACE_VOLUME
, DriverObject
, volume_notification
, DriverObject
, ¬ification_entry2
);
5801 if (!NT_SUCCESS(Status
))
5802 ERR("IoRegisterPlugPlayNotification returned %08x\n", Status
);
5804 Status
= IoRegisterPlugPlayNotification(EventCategoryDeviceInterfaceChange
, PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES
,
5805 (PVOID
)&GUID_DEVINTERFACE_HIDDEN_VOLUME
, DriverObject
, volume_notification
, DriverObject
, ¬ification_entry3
);
5806 if (!NT_SUCCESS(Status
))
5807 ERR("IoRegisterPlugPlayNotification returned %08x\n", Status
);
5809 Status
= IoRegisterPlugPlayNotification(EventCategoryDeviceInterfaceChange
, PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES
,
5810 (PVOID
)&GUID_DEVINTERFACE_DISK
, DriverObject
, pnp_notification
, DriverObject
, ¬ification_entry
);
5811 if (!NT_SUCCESS(Status
))
5812 ERR("IoRegisterPlugPlayNotification returned %08x\n", Status
);
5814 finished_probing
= TRUE
;
5816 KeInitializeEvent(&mountmgr_thread_event
, NotificationEvent
, FALSE
);
5819 Status
= PsCreateSystemThread(&mountmgr_thread_handle
, 0, NULL
, NULL
, NULL
, mountmgr_thread
, NULL
);
5820 if (!NT_SUCCESS(Status
))
5821 WARN("PsCreateSystemThread returned %08x\n", Status
);
5824 IoRegisterFileSystem(DeviceObject
);
5826 return STATUS_SUCCESS
;