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
;
800 data
->FileSystemAttributes
|= FILE_READ_ONLY_VOLUME
;
802 // should also be FILE_FILE_COMPRESSION when supported
803 data
->MaximumComponentNameLength
= 255; // FIXME - check
804 data
->FileSystemNameLength
= orig_fs_name_len
;
805 RtlCopyMemory(data
->FileSystemName
, fs_name
, fs_name_len
);
807 BytesCopied
= sizeof(FILE_FS_ATTRIBUTE_INFORMATION
) - sizeof(WCHAR
) + fs_name_len
;
808 Status
= overflow
? STATUS_BUFFER_OVERFLOW
: STATUS_SUCCESS
;
812 case FileFsDeviceInformation
:
814 FILE_FS_DEVICE_INFORMATION
* ffdi
= Irp
->AssociatedIrp
.SystemBuffer
;
816 TRACE("FileFsDeviceInformation\n");
818 ffdi
->DeviceType
= FILE_DEVICE_DISK
;
820 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, TRUE
);
821 ffdi
->Characteristics
= Vcb
->Vpb
->RealDevice
->Characteristics
;
822 ExReleaseResourceLite(&Vcb
->tree_lock
);
825 ffdi
->Characteristics
|= FILE_READ_ONLY_DEVICE
;
827 ffdi
->Characteristics
&= ~FILE_READ_ONLY_DEVICE
;
829 BytesCopied
= sizeof(FILE_FS_DEVICE_INFORMATION
);
830 Status
= STATUS_SUCCESS
;
835 case FileFsFullSizeInformation
:
837 FILE_FS_FULL_SIZE_INFORMATION
* ffsi
= Irp
->AssociatedIrp
.SystemBuffer
;
839 TRACE("FileFsFullSizeInformation\n");
841 calculate_total_space(Vcb
, (UINT64
*)&ffsi
->TotalAllocationUnits
.QuadPart
, (UINT64
*)&ffsi
->ActualAvailableAllocationUnits
.QuadPart
);
842 ffsi
->CallerAvailableAllocationUnits
.QuadPart
= ffsi
->ActualAvailableAllocationUnits
.QuadPart
;
843 ffsi
->SectorsPerAllocationUnit
= 1;
844 ffsi
->BytesPerSector
= Vcb
->superblock
.sector_size
;
846 BytesCopied
= sizeof(FILE_FS_FULL_SIZE_INFORMATION
);
847 Status
= STATUS_SUCCESS
;
852 case FileFsObjectIdInformation
:
854 FILE_FS_OBJECTID_INFORMATION
* ffoi
= Irp
->AssociatedIrp
.SystemBuffer
;
856 TRACE("FileFsObjectIdInformation\n");
858 RtlCopyMemory(ffoi
->ObjectId
, &Vcb
->superblock
.uuid
.uuid
[0], sizeof(UCHAR
) * 16);
859 RtlZeroMemory(ffoi
->ExtendedInfo
, sizeof(ffoi
->ExtendedInfo
));
861 BytesCopied
= sizeof(FILE_FS_OBJECTID_INFORMATION
);
862 Status
= STATUS_SUCCESS
;
867 case FileFsSizeInformation
:
869 FILE_FS_SIZE_INFORMATION
* ffsi
= Irp
->AssociatedIrp
.SystemBuffer
;
871 TRACE("FileFsSizeInformation\n");
873 calculate_total_space(Vcb
, (UINT64
*)&ffsi
->TotalAllocationUnits
.QuadPart
, (UINT64
*)&ffsi
->AvailableAllocationUnits
.QuadPart
);
874 ffsi
->SectorsPerAllocationUnit
= 1;
875 ffsi
->BytesPerSector
= Vcb
->superblock
.sector_size
;
877 BytesCopied
= sizeof(FILE_FS_SIZE_INFORMATION
);
878 Status
= STATUS_SUCCESS
;
883 case FileFsVolumeInformation
:
885 FILE_FS_VOLUME_INFORMATION
* data
= Irp
->AssociatedIrp
.SystemBuffer
;
886 FILE_FS_VOLUME_INFORMATION ffvi
;
887 BOOL overflow
= FALSE
;
888 ULONG label_len
, orig_label_len
;
890 TRACE("FileFsVolumeInformation\n");
891 TRACE("max length = %u\n", IrpSp
->Parameters
.QueryVolume
.Length
);
893 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, TRUE
);
895 Status
= RtlUTF8ToUnicodeN(NULL
, 0, &label_len
, Vcb
->superblock
.label
, (ULONG
)strlen(Vcb
->superblock
.label
));
896 if (!NT_SUCCESS(Status
)) {
897 ERR("RtlUTF8ToUnicodeN returned %08x\n", Status
);
898 ExReleaseResourceLite(&Vcb
->tree_lock
);
902 orig_label_len
= label_len
;
904 if (IrpSp
->Parameters
.QueryVolume
.Length
< sizeof(FILE_FS_VOLUME_INFORMATION
) - sizeof(WCHAR
) + label_len
) {
905 if (IrpSp
->Parameters
.QueryVolume
.Length
> sizeof(FILE_FS_VOLUME_INFORMATION
) - sizeof(WCHAR
))
906 label_len
= IrpSp
->Parameters
.QueryVolume
.Length
- sizeof(FILE_FS_VOLUME_INFORMATION
) + sizeof(WCHAR
);
913 TRACE("label_len = %u\n", label_len
);
915 ffvi
.VolumeCreationTime
.QuadPart
= 0; // FIXME
916 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];
917 ffvi
.VolumeLabelLength
= orig_label_len
;
918 ffvi
.SupportsObjects
= FALSE
;
920 RtlCopyMemory(data
, &ffvi
, min(sizeof(FILE_FS_VOLUME_INFORMATION
) - sizeof(WCHAR
), IrpSp
->Parameters
.QueryVolume
.Length
));
925 Status
= RtlUTF8ToUnicodeN(&data
->VolumeLabel
[0], label_len
, &bytecount
, Vcb
->superblock
.label
, (ULONG
)strlen(Vcb
->superblock
.label
));
926 if (!NT_SUCCESS(Status
) && Status
!= STATUS_BUFFER_TOO_SMALL
) {
927 ERR("RtlUTF8ToUnicodeN returned %08x\n", Status
);
928 ExReleaseResourceLite(&Vcb
->tree_lock
);
932 TRACE("label = %.*S\n", label_len
/ sizeof(WCHAR
), data
->VolumeLabel
);
935 ExReleaseResourceLite(&Vcb
->tree_lock
);
937 BytesCopied
= sizeof(FILE_FS_VOLUME_INFORMATION
) - sizeof(WCHAR
) + label_len
;
938 Status
= overflow
? STATUS_BUFFER_OVERFLOW
: STATUS_SUCCESS
;
943 #ifdef _MSC_VER // not in mingw yet
944 case FileFsSectorSizeInformation
:
946 FILE_FS_SECTOR_SIZE_INFORMATION
* data
= Irp
->AssociatedIrp
.SystemBuffer
;
948 data
->LogicalBytesPerSector
= Vcb
->superblock
.sector_size
;
949 data
->PhysicalBytesPerSectorForAtomicity
= Vcb
->superblock
.sector_size
;
950 data
->PhysicalBytesPerSectorForPerformance
= Vcb
->superblock
.sector_size
;
951 data
->FileSystemEffectivePhysicalBytesPerSectorForAtomicity
= Vcb
->superblock
.sector_size
;
952 data
->ByteOffsetForSectorAlignment
= 0;
953 data
->ByteOffsetForPartitionAlignment
= 0;
955 data
->Flags
= SSINFO_FLAGS_ALIGNED_DEVICE
| SSINFO_FLAGS_PARTITION_ALIGNED_ON_DEVICE
;
957 if (Vcb
->trim
&& !Vcb
->options
.no_trim
)
958 data
->Flags
|= SSINFO_FLAGS_TRIM_ENABLED
;
960 BytesCopied
= sizeof(FILE_FS_SECTOR_SIZE_INFORMATION
);
965 #endif /* __REACTOS__ */
968 Status
= STATUS_INVALID_PARAMETER
;
969 WARN("unknown FsInformationClass %u\n", IrpSp
->Parameters
.QueryVolume
.FsInformationClass
);
973 if (!NT_SUCCESS(Status
) && Status
!= STATUS_BUFFER_OVERFLOW
)
974 Irp
->IoStatus
.Information
= 0;
976 Irp
->IoStatus
.Information
= BytesCopied
;
979 Irp
->IoStatus
.Status
= Status
;
981 IoCompleteRequest( Irp
, IO_DISK_INCREMENT
);
984 IoSetTopLevelIrp(NULL
);
986 TRACE("query volume information returning %08x\n", Status
);
988 FsRtlExitFileSystem();
993 _Function_class_(IO_COMPLETION_ROUTINE
)
995 static NTSTATUS NTAPI
read_completion(_In_ PDEVICE_OBJECT DeviceObject
, _In_ PIRP Irp
, _In_ PVOID conptr
) {
997 static NTSTATUS
read_completion(_In_ PDEVICE_OBJECT DeviceObject
, _In_ PIRP Irp
, _In_ PVOID conptr
) {
999 read_context
* context
= conptr
;
1001 UNUSED(DeviceObject
);
1003 context
->iosb
= Irp
->IoStatus
;
1004 KeSetEvent(&context
->Event
, 0, FALSE
);
1006 return STATUS_MORE_PROCESSING_REQUIRED
;
1009 NTSTATUS
create_root(_In_
_Requires_exclusive_lock_held_(_Curr_
->tree_lock
) device_extension
* Vcb
, _In_ UINT64 id
,
1010 _Out_ root
** rootptr
, _In_ BOOL no_tree
, _In_ UINT64 offset
, _In_opt_ PIRP Irp
) {
1017 r
= ExAllocatePoolWithTag(PagedPool
, sizeof(root
), ALLOC_TAG
);
1019 ERR("out of memory\n");
1020 return STATUS_INSUFFICIENT_RESOURCES
;
1023 r
->nonpaged
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(root_nonpaged
), ALLOC_TAG
);
1025 ERR("out of memory\n");
1027 return STATUS_INSUFFICIENT_RESOURCES
;
1031 t
= ExAllocatePoolWithTag(PagedPool
, sizeof(tree
), ALLOC_TAG
);
1033 ERR("out of memory\n");
1034 ExFreePool(r
->nonpaged
);
1036 return STATUS_INSUFFICIENT_RESOURCES
;
1041 t
->is_unique
= TRUE
;
1042 t
->uniqueness_determined
= TRUE
;
1046 ri
= ExAllocatePoolWithTag(PagedPool
, sizeof(ROOT_ITEM
), ALLOC_TAG
);
1048 ERR("out of memory\n");
1053 ExFreePool(r
->nonpaged
);
1055 return STATUS_INSUFFICIENT_RESOURCES
;
1059 r
->treeholder
.address
= 0;
1060 r
->treeholder
.generation
= Vcb
->superblock
.generation
;
1061 r
->treeholder
.tree
= t
;
1064 r
->received
= FALSE
;
1068 RtlZeroMemory(&r
->root_item
, sizeof(ROOT_ITEM
));
1069 r
->root_item
.num_references
= 1;
1070 r
->fcbs_version
= 0;
1071 InitializeListHead(&r
->fcbs
);
1072 RtlZeroMemory(r
->fcbs_ptrs
, sizeof(LIST_ENTRY
*) * 256);
1074 RtlCopyMemory(ri
, &r
->root_item
, sizeof(ROOT_ITEM
));
1076 // We ask here for a traverse_ptr to the item we're inserting, so we can
1077 // copy some of the tree's variables
1079 Status
= insert_tree_item(Vcb
, Vcb
->root_root
, id
, TYPE_ROOT_ITEM
, offset
, ri
, sizeof(ROOT_ITEM
), &tp
, Irp
);
1080 if (!NT_SUCCESS(Status
)) {
1081 ERR("insert_tree_item returned %08x\n", Status
);
1087 ExFreePool(r
->nonpaged
);
1092 ExInitializeResourceLite(&r
->nonpaged
->load_tree_lock
);
1094 InsertTailList(&Vcb
->roots
, &r
->list_entry
);
1097 RtlZeroMemory(&t
->header
, sizeof(tree_header
));
1098 t
->header
.fs_uuid
= tp
.tree
->header
.fs_uuid
;
1099 t
->header
.address
= 0;
1100 t
->header
.flags
= HEADER_FLAG_MIXED_BACKREF
| 1; // 1 == "written"? Why does the Linux driver record this?
1101 t
->header
.chunk_tree_uuid
= tp
.tree
->header
.chunk_tree_uuid
;
1102 t
->header
.generation
= Vcb
->superblock
.generation
;
1103 t
->header
.tree_id
= id
;
1104 t
->header
.num_items
= 0;
1105 t
->header
.level
= 0;
1107 t
->has_address
= FALSE
;
1114 InitializeListHead(&t
->itemlist
);
1117 t
->has_new_address
= FALSE
;
1118 t
->updated_extents
= FALSE
;
1120 InsertTailList(&Vcb
->trees
, &t
->list_entry
);
1121 t
->list_entry_hash
.Flink
= NULL
;
1124 Vcb
->need_write
= TRUE
;
1129 return STATUS_SUCCESS
;
1132 static NTSTATUS
set_label(_In_ device_extension
* Vcb
, _In_ FILE_FS_LABEL_INFORMATION
* ffli
) {
1137 TRACE("label = %.*S\n", ffli
->VolumeLabelLength
/ sizeof(WCHAR
), ffli
->VolumeLabel
);
1139 vollen
= ffli
->VolumeLabelLength
;
1141 for (i
= 0; i
< ffli
->VolumeLabelLength
/ sizeof(WCHAR
); i
++) {
1142 if (ffli
->VolumeLabel
[i
] == 0) {
1143 vollen
= i
* sizeof(WCHAR
);
1145 } else if (ffli
->VolumeLabel
[i
] == '/' || ffli
->VolumeLabel
[i
] == '\\') {
1146 Status
= STATUS_INVALID_VOLUME_LABEL
;
1154 Status
= RtlUnicodeToUTF8N(NULL
, 0, &utf8len
, ffli
->VolumeLabel
, vollen
);
1155 if (!NT_SUCCESS(Status
))
1158 if (utf8len
> MAX_LABEL_SIZE
) {
1159 Status
= STATUS_INVALID_VOLUME_LABEL
;
1164 ExAcquireResourceExclusiveLite(&Vcb
->tree_lock
, TRUE
);
1167 Status
= RtlUnicodeToUTF8N((PCHAR
)&Vcb
->superblock
.label
, MAX_LABEL_SIZE
, &utf8len
, ffli
->VolumeLabel
, vollen
);
1168 if (!NT_SUCCESS(Status
))
1171 Status
= STATUS_SUCCESS
;
1173 if (utf8len
< MAX_LABEL_SIZE
)
1174 RtlZeroMemory(Vcb
->superblock
.label
+ utf8len
, MAX_LABEL_SIZE
- utf8len
);
1176 Vcb
->need_write
= TRUE
;
1179 ExReleaseResourceLite(&Vcb
->tree_lock
);
1182 TRACE("returning %08x\n", Status
);
1187 _Dispatch_type_(IRP_MJ_SET_VOLUME_INFORMATION
)
1188 _Function_class_(DRIVER_DISPATCH
)
1190 static NTSTATUS NTAPI
drv_set_volume_information(_In_ PDEVICE_OBJECT DeviceObject
, _In_ PIRP Irp
) {
1192 static NTSTATUS
drv_set_volume_information(_In_ PDEVICE_OBJECT DeviceObject
, _In_ PIRP Irp
) {
1194 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
1195 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
1199 FsRtlEnterFileSystem();
1201 TRACE("set volume information\n");
1203 top_level
= is_top_level(Irp
);
1205 if (Vcb
&& Vcb
->type
== VCB_TYPE_VOLUME
) {
1206 Status
= vol_set_volume_information(DeviceObject
, Irp
);
1208 } else if (!Vcb
|| Vcb
->type
!= VCB_TYPE_FS
) {
1209 Status
= STATUS_INVALID_PARAMETER
;
1213 Status
= STATUS_NOT_IMPLEMENTED
;
1215 if (Vcb
->readonly
) {
1216 Status
= STATUS_MEDIA_WRITE_PROTECTED
;
1220 if (Vcb
->removing
|| Vcb
->locked
) {
1221 Status
= STATUS_ACCESS_DENIED
;
1225 switch (IrpSp
->Parameters
.SetVolume
.FsInformationClass
) {
1226 case FileFsControlInformation
:
1227 FIXME("STUB: FileFsControlInformation\n");
1230 case FileFsLabelInformation
:
1231 TRACE("FileFsLabelInformation\n");
1233 Status
= set_label(Vcb
, Irp
->AssociatedIrp
.SystemBuffer
);
1236 case FileFsObjectIdInformation
:
1237 FIXME("STUB: FileFsObjectIdInformation\n");
1241 WARN("Unrecognized FsInformationClass 0x%x\n", IrpSp
->Parameters
.SetVolume
.FsInformationClass
);
1246 Irp
->IoStatus
.Status
= Status
;
1247 Irp
->IoStatus
.Information
= 0;
1249 TRACE("returning %08x\n", Status
);
1251 IoCompleteRequest( Irp
, IO_NO_INCREMENT
);
1254 IoSetTopLevelIrp(NULL
);
1256 FsRtlExitFileSystem();
1261 static WCHAR
* file_desc_fcb(_In_ fcb
* fcb
) {
1267 if (fcb
->debug_desc
)
1268 return fcb
->debug_desc
;
1270 if (fcb
== fcb
->Vcb
->volume_fcb
)
1271 return L
"volume FCB";
1273 fcb
->debug_desc
= ExAllocatePoolWithTag(PagedPool
, 60 * sizeof(WCHAR
), ALLOC_TAG
);
1274 if (!fcb
->debug_desc
)
1275 return L
"(memory error)";
1277 // I know this is pretty hackish...
1278 // GCC doesn't like %llx in sprintf, and MSVC won't let us use swprintf
1279 // without the CRT, which breaks drivers.
1281 sprintf(s
, "subvol %x, inode %x", (UINT32
)fcb
->subvol
->id
, (UINT32
)fcb
->inode
);
1284 as
.Length
= as
.MaximumLength
= (USHORT
)strlen(s
);
1286 us
.Buffer
= fcb
->debug_desc
;
1287 us
.MaximumLength
= 60 * sizeof(WCHAR
);
1290 Status
= RtlAnsiStringToUnicodeString(&us
, &as
, FALSE
);
1291 if (!NT_SUCCESS(Status
))
1292 return L
"(RtlAnsiStringToUnicodeString error)";
1294 us
.Buffer
[us
.Length
/ sizeof(WCHAR
)] = 0;
1296 return fcb
->debug_desc
;
1299 WCHAR
* file_desc_fileref(_In_ file_ref
* fileref
) {
1304 if (fileref
->debug_desc
)
1305 return fileref
->debug_desc
;
1307 fn
.Length
= fn
.MaximumLength
= 0;
1308 Status
= fileref_get_filename(fileref
, &fn
, NULL
, &reqlen
);
1309 if (Status
!= STATUS_BUFFER_OVERFLOW
)
1312 if (reqlen
> 0xffff - sizeof(WCHAR
))
1313 return L
"(too long)";
1315 fileref
->debug_desc
= ExAllocatePoolWithTag(PagedPool
, reqlen
+ sizeof(WCHAR
), ALLOC_TAG
);
1316 if (!fileref
->debug_desc
)
1317 return L
"(memory error)";
1319 fn
.Buffer
= fileref
->debug_desc
;
1321 fn
.MaximumLength
= (USHORT
)(reqlen
+ sizeof(WCHAR
));
1323 Status
= fileref_get_filename(fileref
, &fn
, NULL
, &reqlen
);
1324 if (!NT_SUCCESS(Status
)) {
1325 ExFreePool(fileref
->debug_desc
);
1326 fileref
->debug_desc
= NULL
;
1330 fileref
->debug_desc
[fn
.Length
/ sizeof(WCHAR
)] = 0;
1332 return fileref
->debug_desc
;
1336 WCHAR
* file_desc(_In_ PFILE_OBJECT FileObject
) {
1337 fcb
* fcb
= FileObject
->FsContext
;
1338 ccb
* ccb
= FileObject
->FsContext2
;
1339 file_ref
* fileref
= ccb
? ccb
->fileref
: NULL
;
1342 return file_desc_fileref(fileref
);
1344 return file_desc_fcb(fcb
);
1347 void send_notification_fileref(_In_ file_ref
* fileref
, _In_ ULONG filter_match
, _In_ ULONG action
, _In_opt_ PUNICODE_STRING stream
) {
1352 fcb
* fcb
= fileref
->fcb
;
1354 fn
.Length
= fn
.MaximumLength
= 0;
1355 Status
= fileref_get_filename(fileref
, &fn
, NULL
, &reqlen
);
1356 if (Status
!= STATUS_BUFFER_OVERFLOW
) {
1357 ERR("fileref_get_filename returned %08x\n", Status
);
1361 if (reqlen
> 0xffff) {
1362 WARN("reqlen was too long for FsRtlNotifyFilterReportChange\n");
1366 fn
.Buffer
= ExAllocatePoolWithTag(PagedPool
, reqlen
, ALLOC_TAG
);
1368 ERR("out of memory\n");
1372 fn
.MaximumLength
= (USHORT
)reqlen
;
1375 Status
= fileref_get_filename(fileref
, &fn
, &name_offset
, &reqlen
);
1376 if (!NT_SUCCESS(Status
)) {
1377 ERR("fileref_get_filename returned %08x\n", Status
);
1378 ExFreePool(fn
.Buffer
);
1382 FsRtlNotifyFilterReportChange(fcb
->Vcb
->NotifySync
, &fcb
->Vcb
->DirNotifyList
, (PSTRING
)&fn
, name_offset
,
1383 (PSTRING
)stream
, NULL
, filter_match
, action
, NULL
, NULL
);
1384 ExFreePool(fn
.Buffer
);
1387 void send_notification_fcb(_In_ file_ref
* fileref
, _In_ ULONG filter_match
, _In_ ULONG action
, _In_opt_ PUNICODE_STRING stream
) {
1388 fcb
* fcb
= fileref
->fcb
;
1392 // no point looking for hardlinks if st_nlink == 1
1393 if (fileref
->fcb
->inode_item
.st_nlink
== 1) {
1394 send_notification_fileref(fileref
, filter_match
, action
, stream
);
1398 ExAcquireResourceExclusiveLite(&fcb
->Vcb
->fileref_lock
, TRUE
);
1400 le
= fcb
->hardlinks
.Flink
;
1401 while (le
!= &fcb
->hardlinks
) {
1402 hardlink
* hl
= CONTAINING_RECORD(le
, hardlink
, list_entry
);
1405 Status
= open_fileref_by_inode(fcb
->Vcb
, fcb
->subvol
, hl
->parent
, &parfr
, NULL
);
1407 if (!NT_SUCCESS(Status
))
1408 ERR("open_fileref_by_inode returned %08x\n", Status
);
1409 else if (!parfr
->deleted
) {
1413 fn
.Length
= fn
.MaximumLength
= 0;
1414 Status
= fileref_get_filename(parfr
, &fn
, NULL
, &pathlen
);
1415 if (Status
!= STATUS_BUFFER_OVERFLOW
) {
1416 ERR("fileref_get_filename returned %08x\n", Status
);
1417 free_fileref(parfr
);
1421 if (parfr
!= fcb
->Vcb
->root_fileref
)
1422 pathlen
+= sizeof(WCHAR
);
1424 if (pathlen
+ hl
->name
.Length
> 0xffff) {
1425 WARN("pathlen + hl->name.Length was too long for FsRtlNotifyFilterReportChange\n");
1426 free_fileref(parfr
);
1430 fn
.MaximumLength
= (USHORT
)(pathlen
+ hl
->name
.Length
);
1431 fn
.Buffer
= ExAllocatePoolWithTag(PagedPool
, fn
.MaximumLength
, ALLOC_TAG
);
1433 ERR("out of memory\n");
1434 free_fileref(parfr
);
1438 Status
= fileref_get_filename(parfr
, &fn
, NULL
, NULL
);
1439 if (!NT_SUCCESS(Status
)) {
1440 ERR("fileref_get_filename returned %08x\n", Status
);
1441 free_fileref(parfr
);
1442 ExFreePool(fn
.Buffer
);
1446 if (parfr
!= fcb
->Vcb
->root_fileref
) {
1447 fn
.Buffer
[(pathlen
/ sizeof(WCHAR
)) - 1] = '\\';
1448 fn
.Length
+= sizeof(WCHAR
);
1451 RtlCopyMemory(&fn
.Buffer
[pathlen
/ sizeof(WCHAR
)], hl
->name
.Buffer
, hl
->name
.Length
);
1452 fn
.Length
+= hl
->name
.Length
;
1454 FsRtlNotifyFilterReportChange(fcb
->Vcb
->NotifySync
, &fcb
->Vcb
->DirNotifyList
, (PSTRING
)&fn
, (USHORT
)pathlen
,
1455 (PSTRING
)stream
, NULL
, filter_match
, action
, NULL
, NULL
);
1457 ExFreePool(fn
.Buffer
);
1459 free_fileref(parfr
);
1465 ExReleaseResourceLite(&fcb
->Vcb
->fileref_lock
);
1468 void mark_fcb_dirty(_In_ fcb
* fcb
) {
1470 #ifdef DEBUG_FCB_REFCOUNTS
1475 #ifdef DEBUG_FCB_REFCOUNTS
1476 rc
= InterlockedIncrement(&fcb
->refcount
);
1477 WARN("fcb %p: refcount now %i\n", fcb
, rc
);
1479 InterlockedIncrement(&fcb
->refcount
);
1482 ExAcquireResourceExclusiveLite(&fcb
->Vcb
->dirty_fcbs_lock
, TRUE
);
1483 InsertTailList(&fcb
->Vcb
->dirty_fcbs
, &fcb
->list_entry_dirty
);
1484 ExReleaseResourceLite(&fcb
->Vcb
->dirty_fcbs_lock
);
1487 fcb
->Vcb
->need_write
= TRUE
;
1490 void mark_fileref_dirty(_In_ file_ref
* fileref
) {
1491 if (!fileref
->dirty
) {
1492 fileref
->dirty
= TRUE
;
1493 increase_fileref_refcount(fileref
);
1495 ExAcquireResourceExclusiveLite(&fileref
->fcb
->Vcb
->dirty_filerefs_lock
, TRUE
);
1496 InsertTailList(&fileref
->fcb
->Vcb
->dirty_filerefs
, &fileref
->list_entry_dirty
);
1497 ExReleaseResourceLite(&fileref
->fcb
->Vcb
->dirty_filerefs_lock
);
1500 fileref
->fcb
->Vcb
->need_write
= TRUE
;
1503 #ifdef DEBUG_FCB_REFCOUNTS
1504 void _free_fcb(_Inout_ fcb
* fcb
, _In_
const char* func
) {
1505 LONG rc
= InterlockedDecrement(&fcb
->refcount
);
1507 void free_fcb(_Inout_ fcb
* fcb
) {
1508 InterlockedDecrement(&fcb
->refcount
);
1511 #ifdef DEBUG_FCB_REFCOUNTS
1512 #ifdef DEBUG_LONG_MESSAGES
1513 ERR("fcb %p (%s): refcount now %i (subvol %llx, inode %llx)\n", fcb
, func
, rc
, fcb
->subvol
? fcb
->subvol
->id
: 0, fcb
->inode
);
1515 ERR("fcb %p (%s): refcount now %i (subvol %llx, inode %llx)\n", fcb
, func
, rc
, fcb
->subvol
? fcb
->subvol
->id
: 0, fcb
->inode
);
1520 void reap_fcb(fcb
* fcb
) {
1521 UINT8 c
= fcb
->hash
>> 24;
1523 if (fcb
->subvol
&& fcb
->subvol
->fcbs_ptrs
[c
] == &fcb
->list_entry
) {
1524 if (fcb
->list_entry
.Flink
!= &fcb
->subvol
->fcbs
&& (CONTAINING_RECORD(fcb
->list_entry
.Flink
, struct _fcb
, list_entry
)->hash
>> 24) == c
)
1525 fcb
->subvol
->fcbs_ptrs
[c
] = fcb
->list_entry
.Flink
;
1527 fcb
->subvol
->fcbs_ptrs
[c
] = NULL
;
1530 if (fcb
->list_entry
.Flink
)
1531 RemoveEntryList(&fcb
->list_entry
);
1533 if (fcb
->list_entry_all
.Flink
)
1534 RemoveEntryList(&fcb
->list_entry_all
);
1536 ExDeleteResourceLite(&fcb
->nonpaged
->resource
);
1537 ExDeleteResourceLite(&fcb
->nonpaged
->paging_resource
);
1538 ExDeleteResourceLite(&fcb
->nonpaged
->dir_children_lock
);
1540 ExFreeToNPagedLookasideList(&fcb
->Vcb
->fcb_np_lookaside
, fcb
->nonpaged
);
1543 ExFreePool(fcb
->sd
);
1545 if (fcb
->adsxattr
.Buffer
)
1546 ExFreePool(fcb
->adsxattr
.Buffer
);
1548 if (fcb
->reparse_xattr
.Buffer
)
1549 ExFreePool(fcb
->reparse_xattr
.Buffer
);
1551 if (fcb
->ea_xattr
.Buffer
)
1552 ExFreePool(fcb
->ea_xattr
.Buffer
);
1554 if (fcb
->adsdata
.Buffer
)
1555 ExFreePool(fcb
->adsdata
.Buffer
);
1557 if (fcb
->debug_desc
)
1558 ExFreePool(fcb
->debug_desc
);
1560 while (!IsListEmpty(&fcb
->extents
)) {
1561 LIST_ENTRY
* le
= RemoveHeadList(&fcb
->extents
);
1562 extent
* ext
= CONTAINING_RECORD(le
, extent
, list_entry
);
1565 ExFreePool(ext
->csum
);
1570 while (!IsListEmpty(&fcb
->hardlinks
)) {
1571 LIST_ENTRY
* le
= RemoveHeadList(&fcb
->hardlinks
);
1572 hardlink
* hl
= CONTAINING_RECORD(le
, hardlink
, list_entry
);
1574 if (hl
->name
.Buffer
)
1575 ExFreePool(hl
->name
.Buffer
);
1577 if (hl
->utf8
.Buffer
)
1578 ExFreePool(hl
->utf8
.Buffer
);
1583 while (!IsListEmpty(&fcb
->xattrs
)) {
1584 xattr
* xa
= CONTAINING_RECORD(RemoveHeadList(&fcb
->xattrs
), xattr
, list_entry
);
1589 while (!IsListEmpty(&fcb
->dir_children_index
)) {
1590 LIST_ENTRY
* le
= RemoveHeadList(&fcb
->dir_children_index
);
1591 dir_child
* dc
= CONTAINING_RECORD(le
, dir_child
, list_entry_index
);
1593 ExFreePool(dc
->utf8
.Buffer
);
1594 ExFreePool(dc
->name
.Buffer
);
1595 ExFreePool(dc
->name_uc
.Buffer
);
1600 ExFreePool(fcb
->hash_ptrs
);
1602 if (fcb
->hash_ptrs_uc
)
1603 ExFreePool(fcb
->hash_ptrs_uc
);
1605 FsRtlUninitializeFileLock(&fcb
->lock
);
1607 if (fcb
->pool_type
== NonPagedPool
)
1610 ExFreeToPagedLookasideList(&fcb
->Vcb
->fcb_lookaside
, fcb
);
1613 void reap_fcbs(device_extension
* Vcb
) {
1616 le
= Vcb
->all_fcbs
.Flink
;
1617 while (le
!= &Vcb
->all_fcbs
) {
1618 fcb
* fcb
= CONTAINING_RECORD(le
, struct _fcb
, list_entry_all
);
1619 LIST_ENTRY
* le2
= le
->Flink
;
1621 if (fcb
->refcount
== 0)
1628 void free_fileref(_Inout_ file_ref
* fr
) {
1631 rc
= InterlockedDecrement(&fr
->refcount
);
1636 #ifdef DEBUG_FCB_REFCOUNTS
1637 ERR("fileref %p: refcount now %i\n", fr
, rc
);
1642 ERR("fileref %p: refcount now %i\n", fr
, rc
);
1648 void reap_fileref(device_extension
* Vcb
, file_ref
* fr
) {
1649 // FIXME - do we need a file_ref lock?
1651 // FIXME - do delete if needed
1654 ExFreePool(fr
->debug_desc
);
1656 ExDeleteResourceLite(&fr
->nonpaged
->fileref_lock
);
1658 ExFreeToNPagedLookasideList(&Vcb
->fileref_np_lookaside
, fr
->nonpaged
);
1660 // FIXME - throw error if children not empty
1662 if (fr
->fcb
->fileref
== fr
)
1663 fr
->fcb
->fileref
= NULL
;
1667 fr
->dc
->size
= fr
->fcb
->adsdata
.Length
;
1669 fr
->dc
->fileref
= NULL
;
1672 if (fr
->list_entry
.Flink
)
1673 RemoveEntryList(&fr
->list_entry
);
1676 free_fileref(fr
->parent
);
1680 ExFreeToPagedLookasideList(&Vcb
->fileref_lookaside
, fr
);
1683 void reap_filerefs(device_extension
* Vcb
, file_ref
* fr
) {
1686 // FIXME - recursion is a bad idea in kernel mode
1688 le
= fr
->children
.Flink
;
1689 while (le
!= &fr
->children
) {
1690 file_ref
* c
= CONTAINING_RECORD(le
, file_ref
, list_entry
);
1691 LIST_ENTRY
* le2
= le
->Flink
;
1693 reap_filerefs(Vcb
, c
);
1698 if (fr
->refcount
== 0)
1699 reap_fileref(Vcb
, fr
);
1702 static NTSTATUS
close_file(_In_ PFILE_OBJECT FileObject
, _In_ PIRP Irp
) {
1705 file_ref
* fileref
= NULL
;
1710 TRACE("FileObject = %p\n", FileObject
);
1712 fcb
= FileObject
->FsContext
;
1714 TRACE("FCB was NULL, returning success\n");
1715 return STATUS_SUCCESS
;
1718 open_files
= InterlockedDecrement(&fcb
->Vcb
->open_files
);
1720 ccb
= FileObject
->FsContext2
;
1722 TRACE("close called for %S (fcb == %p)\n", file_desc(FileObject
), fcb
);
1724 // FIXME - make sure notification gets sent if file is being deleted
1727 if (ccb
->query_string
.Buffer
)
1728 RtlFreeUnicodeString(&ccb
->query_string
);
1730 if (ccb
->filename
.Buffer
)
1731 ExFreePool(ccb
->filename
.Buffer
);
1733 // FIXME - use refcounts for fileref
1734 fileref
= ccb
->fileref
;
1736 if (fcb
->Vcb
->running_sends
> 0) {
1737 BOOL send_cancelled
= FALSE
;
1739 ExAcquireResourceExclusiveLite(&fcb
->Vcb
->send_load_lock
, TRUE
);
1742 ccb
->send
->cancelling
= TRUE
;
1743 send_cancelled
= TRUE
;
1744 KeSetEvent(&ccb
->send
->cleared_event
, 0, FALSE
);
1747 ExReleaseResourceLite(&fcb
->Vcb
->send_load_lock
);
1749 if (send_cancelled
) {
1751 ExAcquireResourceExclusiveLite(&fcb
->Vcb
->send_load_lock
, TRUE
);
1752 ExReleaseResourceLite(&fcb
->Vcb
->send_load_lock
);
1760 CcUninitializeCacheMap(FileObject
, NULL
, NULL
);
1762 if (open_files
== 0 && fcb
->Vcb
->removing
) {
1764 return STATUS_SUCCESS
;
1767 if (!(fcb
->Vcb
->Vpb
->Flags
& VPB_MOUNTED
))
1768 return STATUS_SUCCESS
;
1771 free_fileref(fileref
);
1775 return STATUS_SUCCESS
;
1778 void uninit(_In_ device_extension
* Vcb
) {
1785 if (!Vcb
->removing
) {
1786 ExAcquireResourceExclusiveLite(&Vcb
->tree_lock
, TRUE
);
1787 Vcb
->removing
= TRUE
;
1788 ExReleaseResourceLite(&Vcb
->tree_lock
);
1791 IoAcquireVpbSpinLock(&irql
);
1792 Vcb
->Vpb
->Flags
&= ~VPB_MOUNTED
;
1793 Vcb
->Vpb
->Flags
|= VPB_DIRECT_WRITES_ALLOWED
;
1794 IoReleaseVpbSpinLock(irql
);
1796 RemoveEntryList(&Vcb
->list_entry
);
1798 if (Vcb
->balance
.thread
) {
1799 Vcb
->balance
.paused
= FALSE
;
1800 Vcb
->balance
.stopping
= TRUE
;
1801 KeSetEvent(&Vcb
->balance
.event
, 0, FALSE
);
1802 KeWaitForSingleObject(&Vcb
->balance
.finished
, Executive
, KernelMode
, FALSE
, NULL
);
1805 if (Vcb
->scrub
.thread
) {
1806 Vcb
->scrub
.paused
= FALSE
;
1807 Vcb
->scrub
.stopping
= TRUE
;
1808 KeSetEvent(&Vcb
->scrub
.event
, 0, FALSE
);
1809 KeWaitForSingleObject(&Vcb
->scrub
.finished
, Executive
, KernelMode
, FALSE
, NULL
);
1812 if (Vcb
->running_sends
!= 0) {
1813 BOOL send_cancelled
= FALSE
;
1815 ExAcquireResourceExclusiveLite(&Vcb
->send_load_lock
, TRUE
);
1817 le
= Vcb
->send_ops
.Flink
;
1818 while (le
!= &Vcb
->send_ops
) {
1819 send_info
* send
= CONTAINING_RECORD(le
, send_info
, list_entry
);
1821 if (!send
->cancelling
) {
1822 send
->cancelling
= TRUE
;
1823 send_cancelled
= TRUE
;
1825 KeSetEvent(&send
->cleared_event
, 0, FALSE
);
1831 ExReleaseResourceLite(&Vcb
->send_load_lock
);
1833 if (send_cancelled
) {
1834 while (Vcb
->running_sends
!= 0) {
1835 ExAcquireResourceExclusiveLite(&Vcb
->send_load_lock
, TRUE
);
1836 ExReleaseResourceLite(&Vcb
->send_load_lock
);
1841 Status
= registry_mark_volume_unmounted(&Vcb
->superblock
.uuid
);
1842 if (!NT_SUCCESS(Status
) && Status
!= STATUS_TOO_LATE
)
1843 WARN("registry_mark_volume_unmounted returned %08x\n", Status
);
1845 for (i
= 0; i
< Vcb
->calcthreads
.num_threads
; i
++) {
1846 Vcb
->calcthreads
.threads
[i
].quit
= TRUE
;
1849 KeSetEvent(&Vcb
->calcthreads
.event
, 0, FALSE
);
1851 for (i
= 0; i
< Vcb
->calcthreads
.num_threads
; i
++) {
1852 KeWaitForSingleObject(&Vcb
->calcthreads
.threads
[i
].finished
, Executive
, KernelMode
, FALSE
, NULL
);
1854 ZwClose(Vcb
->calcthreads
.threads
[i
].handle
);
1857 ExDeleteResourceLite(&Vcb
->calcthreads
.lock
);
1858 ExFreePool(Vcb
->calcthreads
.threads
);
1861 KeSetTimer(&Vcb
->flush_thread_timer
, time
, NULL
); // trigger the timer early
1862 KeWaitForSingleObject(&Vcb
->flush_thread_finished
, Executive
, KernelMode
, FALSE
, NULL
);
1864 reap_fcb(Vcb
->volume_fcb
);
1865 reap_fcb(Vcb
->dummy_fcb
);
1868 ObDereferenceObject(Vcb
->root_file
);
1870 le
= Vcb
->chunks
.Flink
;
1871 while (le
!= &Vcb
->chunks
) {
1872 chunk
* c
= CONTAINING_RECORD(le
, chunk
, list_entry
);
1882 while (!IsListEmpty(&Vcb
->roots
)) {
1883 root
* r
= CONTAINING_RECORD(RemoveHeadList(&Vcb
->roots
), root
, list_entry
);
1885 ExDeleteResourceLite(&r
->nonpaged
->load_tree_lock
);
1886 ExFreePool(r
->nonpaged
);
1890 while (!IsListEmpty(&Vcb
->chunks
)) {
1891 chunk
* c
= CONTAINING_RECORD(RemoveHeadList(&Vcb
->chunks
), chunk
, list_entry
);
1893 while (!IsListEmpty(&c
->space
)) {
1894 LIST_ENTRY
* le2
= RemoveHeadList(&c
->space
);
1895 space
* s
= CONTAINING_RECORD(le2
, space
, list_entry
);
1900 while (!IsListEmpty(&c
->deleting
)) {
1901 LIST_ENTRY
* le2
= RemoveHeadList(&c
->deleting
);
1902 space
* s
= CONTAINING_RECORD(le2
, space
, list_entry
);
1908 ExFreePool(c
->devices
);
1913 ExDeleteResourceLite(&c
->range_locks_lock
);
1914 ExDeleteResourceLite(&c
->partial_stripes_lock
);
1915 ExDeleteResourceLite(&c
->lock
);
1916 ExDeleteResourceLite(&c
->changed_extents_lock
);
1918 ExFreePool(c
->chunk_item
);
1922 // FIXME - free any open fcbs?
1924 while (!IsListEmpty(&Vcb
->devices
)) {
1925 device
* dev
= CONTAINING_RECORD(RemoveHeadList(&Vcb
->devices
), device
, list_entry
);
1927 while (!IsListEmpty(&dev
->space
)) {
1928 LIST_ENTRY
* le2
= RemoveHeadList(&dev
->space
);
1929 space
* s
= CONTAINING_RECORD(le2
, space
, list_entry
);
1937 ExAcquireResourceExclusiveLite(&Vcb
->scrub
.stats_lock
, TRUE
);
1938 while (!IsListEmpty(&Vcb
->scrub
.errors
)) {
1939 scrub_error
* err
= CONTAINING_RECORD(RemoveHeadList(&Vcb
->scrub
.errors
), scrub_error
, list_entry
);
1943 ExReleaseResourceLite(&Vcb
->scrub
.stats_lock
);
1945 ExDeleteResourceLite(&Vcb
->fcb_lock
);
1946 ExDeleteResourceLite(&Vcb
->fileref_lock
);
1947 ExDeleteResourceLite(&Vcb
->load_lock
);
1948 ExDeleteResourceLite(&Vcb
->tree_lock
);
1949 ExDeleteResourceLite(&Vcb
->chunk_lock
);
1950 ExDeleteResourceLite(&Vcb
->dirty_fcbs_lock
);
1951 ExDeleteResourceLite(&Vcb
->dirty_filerefs_lock
);
1952 ExDeleteResourceLite(&Vcb
->dirty_subvols_lock
);
1953 ExDeleteResourceLite(&Vcb
->scrub
.stats_lock
);
1954 ExDeleteResourceLite(&Vcb
->send_load_lock
);
1956 ExDeletePagedLookasideList(&Vcb
->tree_data_lookaside
);
1957 ExDeletePagedLookasideList(&Vcb
->traverse_ptr_lookaside
);
1958 ExDeletePagedLookasideList(&Vcb
->batch_item_lookaside
);
1959 ExDeletePagedLookasideList(&Vcb
->fileref_lookaside
);
1960 ExDeletePagedLookasideList(&Vcb
->fcb_lookaside
);
1961 ExDeletePagedLookasideList(&Vcb
->name_bit_lookaside
);
1962 ExDeleteNPagedLookasideList(&Vcb
->range_lock_lookaside
);
1963 ExDeleteNPagedLookasideList(&Vcb
->fileref_np_lookaside
);
1964 ExDeleteNPagedLookasideList(&Vcb
->fcb_np_lookaside
);
1966 ZwClose(Vcb
->flush_thread_handle
);
1969 NTSTATUS
delete_fileref(_In_ file_ref
* fileref
, _In_opt_ PFILE_OBJECT FileObject
, _In_opt_ PIRP Irp
, _In_ LIST_ENTRY
* rollback
) {
1970 LARGE_INTEGER newlength
, time
;
1975 KeQuerySystemTime(&time
);
1976 win_time_to_unix(time
, &now
);
1978 ExAcquireResourceExclusiveLite(fileref
->fcb
->Header
.Resource
, TRUE
);
1980 if (fileref
->deleted
) {
1981 ExReleaseResourceLite(fileref
->fcb
->Header
.Resource
);
1982 return STATUS_SUCCESS
;
1985 if (fileref
->fcb
->subvol
->send_ops
> 0) {
1986 ExReleaseResourceLite(fileref
->fcb
->Header
.Resource
);
1987 return STATUS_ACCESS_DENIED
;
1990 fileref
->deleted
= TRUE
;
1991 mark_fileref_dirty(fileref
);
1993 // delete INODE_ITEM (0x1)
1995 TRACE("nlink = %u\n", fileref
->fcb
->inode_item
.st_nlink
);
1997 if (!fileref
->fcb
->ads
) {
1998 if (fileref
->parent
->fcb
->subvol
== fileref
->fcb
->subvol
) {
2001 mark_fcb_dirty(fileref
->fcb
);
2003 fileref
->fcb
->inode_item_changed
= TRUE
;
2005 if (fileref
->fcb
->inode_item
.st_nlink
> 1) {
2006 fileref
->fcb
->inode_item
.st_nlink
--;
2007 fileref
->fcb
->inode_item
.transid
= fileref
->fcb
->Vcb
->superblock
.generation
;
2008 fileref
->fcb
->inode_item
.sequence
++;
2009 fileref
->fcb
->inode_item
.st_ctime
= now
;
2013 if (fileref
->fcb
->type
!= BTRFS_TYPE_DIRECTORY
&& fileref
->fcb
->inode_item
.st_size
> 0) {
2014 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
);
2015 if (!NT_SUCCESS(Status
)) {
2016 ERR("excise_extents returned %08x\n", Status
);
2017 ExReleaseResourceLite(fileref
->fcb
->Header
.Resource
);
2022 fileref
->fcb
->Header
.AllocationSize
.QuadPart
= 0;
2023 fileref
->fcb
->Header
.FileSize
.QuadPart
= 0;
2024 fileref
->fcb
->Header
.ValidDataLength
.QuadPart
= 0;
2029 ccfs
.AllocationSize
= fileref
->fcb
->Header
.AllocationSize
;
2030 ccfs
.FileSize
= fileref
->fcb
->Header
.FileSize
;
2031 ccfs
.ValidDataLength
= fileref
->fcb
->Header
.ValidDataLength
;
2033 Status
= STATUS_SUCCESS
;
2036 CcSetFileSizes(FileObject
, &ccfs
);
2037 } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER
) {
2038 Status
= _SEH2_GetExceptionCode();
2041 if (!NT_SUCCESS(Status
)) {
2042 ERR("CcSetFileSizes threw exception %08x\n", Status
);
2043 ExReleaseResourceLite(fileref
->fcb
->Header
.Resource
);
2048 fileref
->fcb
->deleted
= TRUE
;
2050 le
= fileref
->children
.Flink
;
2051 while (le
!= &fileref
->children
) {
2052 file_ref
* fr2
= CONTAINING_RECORD(le
, file_ref
, list_entry
);
2054 if (fr2
->fcb
->ads
) {
2055 fr2
->fcb
->deleted
= TRUE
;
2056 mark_fcb_dirty(fr2
->fcb
);
2064 le
= fileref
->fcb
->hardlinks
.Flink
;
2065 while (le
!= &fileref
->fcb
->hardlinks
) {
2066 hardlink
* hl
= CONTAINING_RECORD(le
, hardlink
, list_entry
);
2068 if (hl
->parent
== fileref
->parent
->fcb
->inode
&& hl
->index
== fileref
->dc
->index
) {
2069 RemoveEntryList(&hl
->list_entry
);
2071 if (hl
->name
.Buffer
)
2072 ExFreePool(hl
->name
.Buffer
);
2074 if (hl
->utf8
.Buffer
)
2075 ExFreePool(hl
->utf8
.Buffer
);
2084 } else if (fileref
->fcb
->subvol
->parent
== fileref
->parent
->fcb
->subvol
->id
) { // valid subvolume
2085 if (fileref
->fcb
->subvol
->root_item
.num_references
> 1) {
2086 fileref
->fcb
->subvol
->root_item
.num_references
--;
2088 mark_fcb_dirty(fileref
->fcb
); // so ROOT_ITEM gets updated
2092 // FIXME - we need a lock here
2094 RemoveEntryList(&fileref
->fcb
->subvol
->list_entry
);
2096 InsertTailList(&fileref
->fcb
->Vcb
->drop_roots
, &fileref
->fcb
->subvol
->list_entry
);
2098 le
= fileref
->children
.Flink
;
2099 while (le
!= &fileref
->children
) {
2100 file_ref
* fr2
= CONTAINING_RECORD(le
, file_ref
, list_entry
);
2102 if (fr2
->fcb
->ads
) {
2103 fr2
->fcb
->deleted
= TRUE
;
2104 mark_fcb_dirty(fr2
->fcb
);
2112 fileref
->fcb
->deleted
= TRUE
;
2113 mark_fcb_dirty(fileref
->fcb
);
2116 // remove dir_child from parent
2119 TRACE("delete file %.*S\n", fileref
->dc
->name
.Length
/ sizeof(WCHAR
), fileref
->dc
->name
.Buffer
);
2121 ExAcquireResourceExclusiveLite(&fileref
->parent
->fcb
->nonpaged
->dir_children_lock
, TRUE
);
2122 RemoveEntryList(&fileref
->dc
->list_entry_index
);
2124 if (!fileref
->fcb
->ads
)
2125 remove_dir_child_from_hash_lists(fileref
->parent
->fcb
, fileref
->dc
);
2127 ExReleaseResourceLite(&fileref
->parent
->fcb
->nonpaged
->dir_children_lock
);
2129 if (!fileref
->oldutf8
.Buffer
)
2130 fileref
->oldutf8
= fileref
->dc
->utf8
;
2132 ExFreePool(fileref
->dc
->utf8
.Buffer
);
2134 utf8len
= fileref
->dc
->utf8
.Length
;
2136 fileref
->oldindex
= fileref
->dc
->index
;
2138 ExFreePool(fileref
->dc
->name
.Buffer
);
2139 ExFreePool(fileref
->dc
->name_uc
.Buffer
);
2140 ExFreePool(fileref
->dc
);
2145 // update INODE_ITEM of parent
2147 ExAcquireResourceExclusiveLite(fileref
->parent
->fcb
->Header
.Resource
, TRUE
);
2149 fileref
->parent
->fcb
->inode_item
.transid
= fileref
->fcb
->Vcb
->superblock
.generation
;
2150 fileref
->parent
->fcb
->inode_item
.sequence
++;
2151 fileref
->parent
->fcb
->inode_item
.st_ctime
= now
;
2153 if (!fileref
->fcb
->ads
) {
2154 TRACE("fileref->parent->fcb->inode_item.st_size (inode %llx) was %llx\n", fileref
->parent
->fcb
->inode
, fileref
->parent
->fcb
->inode_item
.st_size
);
2155 fileref
->parent
->fcb
->inode_item
.st_size
-= utf8len
* 2;
2156 TRACE("fileref->parent->fcb->inode_item.st_size (inode %llx) now %llx\n", fileref
->parent
->fcb
->inode
, fileref
->parent
->fcb
->inode_item
.st_size
);
2157 fileref
->parent
->fcb
->inode_item
.st_mtime
= now
;
2160 fileref
->parent
->fcb
->inode_item_changed
= TRUE
;
2161 ExReleaseResourceLite(fileref
->parent
->fcb
->Header
.Resource
);
2163 if (!fileref
->fcb
->ads
&& fileref
->parent
->dc
)
2164 send_notification_fcb(fileref
->parent
, FILE_NOTIFY_CHANGE_LAST_WRITE
, FILE_ACTION_MODIFIED
, NULL
);
2166 mark_fcb_dirty(fileref
->parent
->fcb
);
2168 fileref
->fcb
->subvol
->root_item
.ctransid
= fileref
->fcb
->Vcb
->superblock
.generation
;
2169 fileref
->fcb
->subvol
->root_item
.ctime
= now
;
2171 newlength
.QuadPart
= 0;
2173 if (FileObject
&& !CcUninitializeCacheMap(FileObject
, &newlength
, NULL
))
2174 TRACE("CcUninitializeCacheMap failed\n");
2176 ExReleaseResourceLite(fileref
->fcb
->Header
.Resource
);
2178 return STATUS_SUCCESS
;
2181 _Dispatch_type_(IRP_MJ_CLEANUP
)
2182 _Function_class_(DRIVER_DISPATCH
)
2184 static NTSTATUS NTAPI
drv_cleanup(_In_ PDEVICE_OBJECT DeviceObject
, _In_ PIRP Irp
) {
2186 static NTSTATUS
drv_cleanup(_In_ PDEVICE_OBJECT DeviceObject
, _In_ PIRP Irp
) {
2189 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
2190 PFILE_OBJECT FileObject
= IrpSp
->FileObject
;
2191 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
2192 fcb
* fcb
= FileObject
->FsContext
;
2195 FsRtlEnterFileSystem();
2199 top_level
= is_top_level(Irp
);
2201 if (Vcb
&& Vcb
->type
== VCB_TYPE_VOLUME
) {
2202 Status
= vol_cleanup(DeviceObject
, Irp
);
2204 } else if (DeviceObject
== master_devobj
) {
2205 TRACE("closing file system\n");
2206 Status
= STATUS_SUCCESS
;
2208 } else if (!Vcb
|| Vcb
->type
!= VCB_TYPE_FS
) {
2209 Status
= STATUS_INVALID_PARAMETER
;
2213 if (FileObject
->Flags
& FO_CLEANUP_COMPLETE
) {
2214 TRACE("FileObject %p already cleaned up\n", FileObject
);
2215 Status
= STATUS_SUCCESS
;
2220 ERR("fcb was NULL\n");
2221 Status
= STATUS_INVALID_PARAMETER
;
2225 // We have to use the pointer to Vcb stored in the fcb, as we can receive cleanup
2226 // messages belonging to other devices.
2228 if (FileObject
&& FileObject
->FsContext
) {
2234 ccb
= FileObject
->FsContext2
;
2235 fileref
= ccb
? ccb
->fileref
: NULL
;
2237 TRACE("cleanup called for FileObject %p\n", FileObject
);
2238 TRACE("fileref %p (%S), refcount = %u, open_count = %u\n", fileref
, file_desc(FileObject
), fileref
? fileref
->refcount
: 0, fileref
? fileref
->open_count
: 0);
2240 ExAcquireResourceSharedLite(&fcb
->Vcb
->tree_lock
, TRUE
);
2242 ExAcquireResourceExclusiveLite(fcb
->Header
.Resource
, TRUE
);
2244 IoRemoveShareAccess(FileObject
, &fcb
->share_access
);
2247 FsRtlNotifyCleanup(fcb
->Vcb
->NotifySync
, &fcb
->Vcb
->DirNotifyList
, ccb
);
2250 oc
= InterlockedDecrement(&fileref
->open_count
);
2251 #ifdef DEBUG_FCB_REFCOUNTS
2252 ERR("fileref %p: open_count now %i\n", fileref
, oc
);
2256 if (ccb
&& ccb
->options
& FILE_DELETE_ON_CLOSE
&& fileref
)
2257 fileref
->delete_on_close
= TRUE
;
2259 if (fileref
&& fileref
->delete_on_close
&& fcb
->type
== BTRFS_TYPE_DIRECTORY
&& fcb
->inode_item
.st_size
> 0 && fcb
!= fcb
->Vcb
->dummy_fcb
)
2260 fileref
->delete_on_close
= FALSE
;
2262 if (fcb
->Vcb
->locked
&& fcb
->Vcb
->locked_fileobj
== FileObject
) {
2263 TRACE("unlocking volume\n");
2264 do_unlock_volume(fcb
->Vcb
);
2265 FsRtlNotifyVolumeEvent(FileObject
, FSRTL_VOLUME_UNLOCK
);
2268 if (ccb
&& ccb
->reserving
) {
2269 fcb
->subvol
->reserved
= NULL
;
2270 ccb
->reserving
= FALSE
;
2271 // FIXME - flush all of subvol's fcbs
2274 if (fileref
&& oc
== 0) {
2275 if (!fcb
->Vcb
->removing
) {
2276 if (fileref
&& fileref
->delete_on_close
&& fileref
!= fcb
->Vcb
->root_fileref
&& fcb
!= fcb
->Vcb
->volume_fcb
) {
2277 LIST_ENTRY rollback
;
2279 InitializeListHead(&rollback
);
2281 if (!fileref
->fcb
->ads
|| fileref
->dc
) {
2282 if (fileref
->fcb
->ads
) {
2283 send_notification_fileref(fileref
->parent
, fcb
->type
== BTRFS_TYPE_DIRECTORY
? FILE_NOTIFY_CHANGE_DIR_NAME
: FILE_NOTIFY_CHANGE_FILE_NAME
,
2284 FILE_ACTION_REMOVED
, &fileref
->dc
->name
);
2286 send_notification_fileref(fileref
, fcb
->type
== BTRFS_TYPE_DIRECTORY
? FILE_NOTIFY_CHANGE_DIR_NAME
: FILE_NOTIFY_CHANGE_FILE_NAME
, FILE_ACTION_REMOVED
, NULL
);
2289 ExReleaseResourceLite(fcb
->Header
.Resource
);
2292 // fileref_lock needs to be acquired before fcb->Header.Resource
2293 ExAcquireResourceExclusiveLite(&fcb
->Vcb
->fileref_lock
, TRUE
);
2295 Status
= delete_fileref(fileref
, FileObject
, Irp
, &rollback
);
2296 if (!NT_SUCCESS(Status
)) {
2297 ERR("delete_fileref returned %08x\n", Status
);
2298 do_rollback(fcb
->Vcb
, &rollback
);
2299 ExReleaseResourceLite(&fcb
->Vcb
->fileref_lock
);
2300 ExReleaseResourceLite(&fcb
->Vcb
->tree_lock
);
2304 ExReleaseResourceLite(&fcb
->Vcb
->fileref_lock
);
2306 clear_rollback(&rollback
);
2307 } else if (FileObject
->Flags
& FO_CACHE_SUPPORTED
&& fcb
->nonpaged
->segment_object
.DataSectionObject
) {
2308 IO_STATUS_BLOCK iosb
;
2309 CcFlushCache(FileObject
->SectionObjectPointer
, NULL
, 0, &iosb
);
2311 if (!NT_SUCCESS(iosb
.Status
)) {
2312 ERR("CcFlushCache returned %08x\n", iosb
.Status
);
2315 if (!ExIsResourceAcquiredSharedLite(fcb
->Header
.PagingIoResource
)) {
2316 ExAcquireResourceExclusiveLite(fcb
->Header
.PagingIoResource
, TRUE
);
2317 ExReleaseResourceLite(fcb
->Header
.PagingIoResource
);
2320 CcPurgeCacheSection(&fcb
->nonpaged
->segment_object
, NULL
, 0, FALSE
);
2322 TRACE("flushed cache on close (FileObject = %p, fcb = %p, AllocationSize = %llx, FileSize = %llx, ValidDataLength = %llx)\n",
2323 FileObject
, fcb
, fcb
->Header
.AllocationSize
.QuadPart
, fcb
->Header
.FileSize
.QuadPart
, fcb
->Header
.ValidDataLength
.QuadPart
);
2327 if (fcb
->Vcb
&& fcb
!= fcb
->Vcb
->volume_fcb
)
2328 CcUninitializeCacheMap(FileObject
, NULL
, NULL
);
2332 ExReleaseResourceLite(fcb
->Header
.Resource
);
2334 ExReleaseResourceLite(&fcb
->Vcb
->tree_lock
);
2336 FileObject
->Flags
|= FO_CLEANUP_COMPLETE
;
2339 Status
= STATUS_SUCCESS
;
2342 TRACE("returning %08x\n", Status
);
2344 Irp
->IoStatus
.Status
= Status
;
2345 Irp
->IoStatus
.Information
= 0;
2347 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
2350 IoSetTopLevelIrp(NULL
);
2352 FsRtlExitFileSystem();
2358 BOOL
get_file_attributes_from_xattr(_In_reads_bytes_(len
) char* val
, _In_ UINT16 len
, _Out_ ULONG
* atts
) {
2359 if (len
> 2 && val
[0] == '0' && val
[1] == 'x') {
2363 for (i
= 2; i
< len
; i
++) {
2366 if (val
[i
] >= '0' && val
[i
] <= '9')
2367 dosnum
|= val
[i
] - '0';
2368 else if (val
[i
] >= 'a' && val
[i
] <= 'f')
2369 dosnum
|= val
[i
] + 10 - 'a';
2370 else if (val
[i
] >= 'A' && val
[i
] <= 'F')
2371 dosnum
|= val
[i
] + 10 - 'a';
2374 TRACE("DOSATTRIB: %08x\n", dosnum
);
2384 ULONG
get_file_attributes(_In_
_Requires_lock_held_(_Curr_
->tree_lock
) device_extension
* Vcb
, _In_ root
* r
, _In_ UINT64 inode
,
2385 _In_ UINT8 type
, _In_ BOOL dotfile
, _In_ BOOL ignore_xa
, _In_opt_ PIRP Irp
) {
2390 if (!ignore_xa
&& get_xattr(Vcb
, r
, inode
, EA_DOSATTRIB
, EA_DOSATTRIB_HASH
, (UINT8
**)&eaval
, &ealen
, Irp
)) {
2393 if (get_file_attributes_from_xattr(eaval
, ealen
, &dosnum
)) {
2396 if (type
== BTRFS_TYPE_DIRECTORY
)
2397 dosnum
|= FILE_ATTRIBUTE_DIRECTORY
;
2398 else if (type
== BTRFS_TYPE_SYMLINK
)
2399 dosnum
|= FILE_ATTRIBUTE_REPARSE_POINT
;
2401 if (type
!= BTRFS_TYPE_DIRECTORY
)
2402 dosnum
&= ~FILE_ATTRIBUTE_DIRECTORY
;
2404 if (inode
== SUBVOL_ROOT_INODE
) {
2405 if (r
->root_item
.flags
& BTRFS_SUBVOL_READONLY
)
2406 dosnum
|= FILE_ATTRIBUTE_READONLY
;
2408 dosnum
&= ~FILE_ATTRIBUTE_READONLY
;
2418 case BTRFS_TYPE_DIRECTORY
:
2419 att
= FILE_ATTRIBUTE_DIRECTORY
;
2422 case BTRFS_TYPE_SYMLINK
:
2423 att
= FILE_ATTRIBUTE_REPARSE_POINT
;
2432 att
|= FILE_ATTRIBUTE_HIDDEN
;
2435 att
|= FILE_ATTRIBUTE_ARCHIVE
;
2437 if (inode
== SUBVOL_ROOT_INODE
) {
2438 if (r
->root_item
.flags
& BTRFS_SUBVOL_READONLY
)
2439 att
|= FILE_ATTRIBUTE_READONLY
;
2441 att
&= ~FILE_ATTRIBUTE_READONLY
;
2444 // FIXME - get READONLY from ii->st_mode
2445 // FIXME - return SYSTEM for block/char devices?
2448 att
= FILE_ATTRIBUTE_NORMAL
;
2453 NTSTATUS
sync_read_phys(_In_ PDEVICE_OBJECT DeviceObject
, _In_ UINT64 StartingOffset
, _In_ ULONG Length
,
2454 _Out_writes_bytes_(Length
) PUCHAR Buffer
, _In_ BOOL override
) {
2455 IO_STATUS_BLOCK IoStatus
;
2456 LARGE_INTEGER Offset
;
2458 PIO_STACK_LOCATION IrpSp
;
2460 read_context context
;
2464 RtlZeroMemory(&context
, sizeof(read_context
));
2465 KeInitializeEvent(&context
.Event
, NotificationEvent
, FALSE
);
2467 Offset
.QuadPart
= (LONGLONG
)StartingOffset
;
2469 Irp
= IoAllocateIrp(DeviceObject
->StackSize
, FALSE
);
2472 ERR("IoAllocateIrp failed\n");
2473 return STATUS_INSUFFICIENT_RESOURCES
;
2476 Irp
->Flags
|= IRP_NOCACHE
;
2477 IrpSp
= IoGetNextIrpStackLocation(Irp
);
2478 IrpSp
->MajorFunction
= IRP_MJ_READ
;
2481 IrpSp
->Flags
|= SL_OVERRIDE_VERIFY_VOLUME
;
2483 if (DeviceObject
->Flags
& DO_BUFFERED_IO
) {
2484 Irp
->AssociatedIrp
.SystemBuffer
= ExAllocatePoolWithTag(NonPagedPool
, Length
, ALLOC_TAG
);
2485 if (!Irp
->AssociatedIrp
.SystemBuffer
) {
2486 ERR("out of memory\n");
2487 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2491 Irp
->Flags
|= IRP_BUFFERED_IO
| IRP_DEALLOCATE_BUFFER
| IRP_INPUT_OPERATION
;
2493 Irp
->UserBuffer
= Buffer
;
2494 } else if (DeviceObject
->Flags
& DO_DIRECT_IO
) {
2495 Irp
->MdlAddress
= IoAllocateMdl(Buffer
, Length
, FALSE
, FALSE
, NULL
);
2496 if (!Irp
->MdlAddress
) {
2497 ERR("IoAllocateMdl failed\n");
2498 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2502 Status
= STATUS_SUCCESS
;
2505 MmProbeAndLockPages(Irp
->MdlAddress
, KernelMode
, IoWriteAccess
);
2506 } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER
) {
2507 Status
= _SEH2_GetExceptionCode();
2510 if (!NT_SUCCESS(Status
)) {
2511 ERR("MmProbeAndLockPages threw exception %08x\n", Status
);
2512 IoFreeMdl(Irp
->MdlAddress
);
2516 Irp
->UserBuffer
= Buffer
;
2518 IrpSp
->Parameters
.Read
.Length
= Length
;
2519 IrpSp
->Parameters
.Read
.ByteOffset
= Offset
;
2521 Irp
->UserIosb
= &IoStatus
;
2523 Irp
->UserEvent
= &context
.Event
;
2525 IoSetCompletionRoutine(Irp
, read_completion
, &context
, TRUE
, TRUE
, TRUE
);
2527 Status
= IoCallDriver(DeviceObject
, Irp
);
2529 if (Status
== STATUS_PENDING
) {
2530 KeWaitForSingleObject(&context
.Event
, Executive
, KernelMode
, FALSE
, NULL
);
2531 Status
= context
.iosb
.Status
;
2534 if (DeviceObject
->Flags
& DO_DIRECT_IO
) {
2535 MmUnlockPages(Irp
->MdlAddress
);
2536 IoFreeMdl(Irp
->MdlAddress
);
2545 static NTSTATUS
read_superblock(_In_ device_extension
* Vcb
, _In_ PDEVICE_OBJECT device
, _In_ UINT64 length
) {
2549 UINT8 valid_superblocks
;
2551 to_read
= device
->SectorSize
== 0 ? sizeof(superblock
) : (ULONG
)sector_align(sizeof(superblock
), device
->SectorSize
);
2553 sb
= ExAllocatePoolWithTag(NonPagedPool
, to_read
, ALLOC_TAG
);
2555 ERR("out of memory\n");
2556 return STATUS_INSUFFICIENT_RESOURCES
;
2559 if (superblock_addrs
[0] + to_read
> length
) {
2560 WARN("device was too short to have any superblock\n");
2562 return STATUS_UNRECOGNIZED_VOLUME
;
2566 valid_superblocks
= 0;
2568 while (superblock_addrs
[i
] > 0) {
2571 if (i
> 0 && superblock_addrs
[i
] + to_read
> length
)
2574 Status
= sync_read_phys(device
, superblock_addrs
[i
], to_read
, (PUCHAR
)sb
, FALSE
);
2575 if (!NT_SUCCESS(Status
)) {
2576 ERR("Failed to read superblock %u: %08x\n", i
, Status
);
2581 if (sb
->magic
!= BTRFS_MAGIC
) {
2583 TRACE("not a BTRFS volume\n");
2585 return STATUS_UNRECOGNIZED_VOLUME
;
2588 TRACE("got superblock %u!\n", i
);
2590 crc32
= ~calc_crc32c(0xffffffff, (UINT8
*)&sb
->uuid
, (ULONG
)sizeof(superblock
) - sizeof(sb
->checksum
));
2592 if (crc32
!= *((UINT32
*)sb
->checksum
))
2593 WARN("crc32 was %08x, expected %08x\n", crc32
, *((UINT32
*)sb
->checksum
));
2594 else if (sb
->sector_size
== 0)
2595 WARN("superblock sector size was 0\n");
2596 else if (sb
->node_size
< sizeof(tree_header
) + sizeof(internal_node
) || sb
->node_size
> 0x10000)
2597 WARN("invalid node size %x\n", sb
->node_size
);
2598 else if ((sb
->node_size
% sb
->sector_size
) != 0)
2599 WARN("node size %x was not a multiple of sector_size %x\n", sb
->node_size
, sb
->sector_size
);
2600 else if (valid_superblocks
== 0 || sb
->generation
> Vcb
->superblock
.generation
) {
2601 RtlCopyMemory(&Vcb
->superblock
, sb
, sizeof(superblock
));
2602 valid_superblocks
++;
2611 if (valid_superblocks
== 0) {
2612 ERR("could not find any valid superblocks\n");
2613 return STATUS_INTERNAL_ERROR
;
2616 TRACE("label is %s\n", Vcb
->superblock
.label
);
2618 return STATUS_SUCCESS
;
2621 NTSTATUS
dev_ioctl(_In_ PDEVICE_OBJECT DeviceObject
, _In_ ULONG ControlCode
, _In_reads_bytes_opt_(InputBufferSize
) PVOID InputBuffer
, _In_ ULONG InputBufferSize
,
2622 _Out_writes_bytes_opt_(OutputBufferSize
) PVOID OutputBuffer
, _In_ ULONG OutputBufferSize
, _In_ BOOLEAN Override
, _Out_opt_ IO_STATUS_BLOCK
* iosb
) {
2626 PIO_STACK_LOCATION IrpSp
;
2627 IO_STATUS_BLOCK IoStatus
;
2629 KeInitializeEvent(&Event
, NotificationEvent
, FALSE
);
2631 Irp
= IoBuildDeviceIoControlRequest(ControlCode
,
2641 if (!Irp
) return STATUS_INSUFFICIENT_RESOURCES
;
2644 IrpSp
= IoGetNextIrpStackLocation(Irp
);
2645 IrpSp
->Flags
|= SL_OVERRIDE_VERIFY_VOLUME
;
2648 Status
= IoCallDriver(DeviceObject
, Irp
);
2650 if (Status
== STATUS_PENDING
) {
2651 KeWaitForSingleObject(&Event
, Executive
, KernelMode
, FALSE
, NULL
);
2652 Status
= IoStatus
.Status
;
2661 _Requires_exclusive_lock_held_(Vcb
->tree_lock
)
2662 static NTSTATUS
add_root(_Inout_ device_extension
* Vcb
, _In_ UINT64 id
, _In_ UINT64 addr
,
2663 _In_ UINT64 generation
, _In_opt_ traverse_ptr
* tp
) {
2664 root
* r
= ExAllocatePoolWithTag(PagedPool
, sizeof(root
), ALLOC_TAG
);
2666 ERR("out of memory\n");
2667 return STATUS_INSUFFICIENT_RESOURCES
;
2672 r
->received
= FALSE
;
2674 r
->treeholder
.address
= addr
;
2675 r
->treeholder
.tree
= NULL
;
2676 r
->treeholder
.generation
= generation
;
2679 r
->fcbs_version
= 0;
2680 InitializeListHead(&r
->fcbs
);
2681 RtlZeroMemory(r
->fcbs_ptrs
, sizeof(LIST_ENTRY
*) * 256);
2683 r
->nonpaged
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(root_nonpaged
), ALLOC_TAG
);
2685 ERR("out of memory\n");
2687 return STATUS_INSUFFICIENT_RESOURCES
;
2690 ExInitializeResourceLite(&r
->nonpaged
->load_tree_lock
);
2695 RtlCopyMemory(&r
->root_item
, tp
->item
->data
, min(sizeof(ROOT_ITEM
), tp
->item
->size
));
2696 if (tp
->item
->size
< sizeof(ROOT_ITEM
))
2697 RtlZeroMemory(((UINT8
*)&r
->root_item
) + tp
->item
->size
, sizeof(ROOT_ITEM
) - tp
->item
->size
);
2699 RtlZeroMemory(&r
->root_item
, sizeof(ROOT_ITEM
));
2701 if (!Vcb
->readonly
&& (r
->id
== BTRFS_ROOT_ROOT
|| r
->id
== BTRFS_ROOT_FSTREE
|| (r
->id
>= 0x100 && !(r
->id
& 0xf000000000000000)))) { // FS tree root
2702 // FIXME - don't call this if subvol is readonly (though we will have to if we ever toggle this flag)
2703 get_last_inode(Vcb
, r
, NULL
);
2705 if (r
->id
== BTRFS_ROOT_ROOT
&& r
->lastinode
< 0x100)
2706 r
->lastinode
= 0x100;
2709 InsertTailList(&Vcb
->roots
, &r
->list_entry
);
2712 case BTRFS_ROOT_ROOT
:
2716 case BTRFS_ROOT_EXTENT
:
2717 Vcb
->extent_root
= r
;
2720 case BTRFS_ROOT_CHUNK
:
2721 Vcb
->chunk_root
= r
;
2724 case BTRFS_ROOT_DEVTREE
:
2728 case BTRFS_ROOT_CHECKSUM
:
2729 Vcb
->checksum_root
= r
;
2732 case BTRFS_ROOT_UUID
:
2736 case BTRFS_ROOT_FREE_SPACE
:
2737 Vcb
->space_root
= r
;
2740 case BTRFS_ROOT_DATA_RELOC
:
2741 Vcb
->data_reloc_root
= r
;
2745 return STATUS_SUCCESS
;
2748 static NTSTATUS
look_for_roots(_Requires_exclusive_lock_held_(_Curr_
->tree_lock
) _In_ device_extension
* Vcb
, _In_opt_ PIRP Irp
) {
2749 traverse_ptr tp
, next_tp
;
2754 searchkey
.obj_id
= 0;
2755 searchkey
.obj_type
= 0;
2756 searchkey
.offset
= 0;
2758 Status
= find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, FALSE
, Irp
);
2759 if (!NT_SUCCESS(Status
)) {
2760 ERR("error - find_item returned %08x\n", Status
);
2765 TRACE("(%llx,%x,%llx)\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
2767 if (tp
.item
->key
.obj_type
== TYPE_ROOT_ITEM
) {
2768 ROOT_ITEM
* ri
= (ROOT_ITEM
*)tp
.item
->data
;
2770 if (tp
.item
->size
< offsetof(ROOT_ITEM
, byte_limit
)) {
2771 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
));
2773 TRACE("root %llx - address %llx\n", tp
.item
->key
.obj_id
, ri
->block_number
);
2775 Status
= add_root(Vcb
, tp
.item
->key
.obj_id
, ri
->block_number
, ri
->generation
, &tp
);
2776 if (!NT_SUCCESS(Status
)) {
2777 ERR("add_root returned %08x\n", Status
);
2781 } else if (tp
.item
->key
.obj_type
== TYPE_ROOT_BACKREF
&& !IsListEmpty(&Vcb
->roots
)) {
2782 root
* lastroot
= CONTAINING_RECORD(Vcb
->roots
.Blink
, root
, list_entry
);
2784 if (lastroot
->id
== tp
.item
->key
.obj_id
)
2785 lastroot
->parent
= tp
.item
->key
.offset
;
2788 b
= find_next_item(Vcb
, &tp
, &next_tp
, FALSE
, Irp
);
2794 if (!Vcb
->readonly
&& !Vcb
->data_reloc_root
) {
2802 WARN("data reloc root doesn't exist, creating it\n");
2804 Status
= create_root(Vcb
, BTRFS_ROOT_DATA_RELOC
, &reloc_root
, FALSE
, 0, Irp
);
2806 if (!NT_SUCCESS(Status
)) {
2807 ERR("create_root returned %08x\n", Status
);
2811 reloc_root
->root_item
.inode
.generation
= 1;
2812 reloc_root
->root_item
.inode
.st_size
= 3;
2813 reloc_root
->root_item
.inode
.st_blocks
= Vcb
->superblock
.node_size
;
2814 reloc_root
->root_item
.inode
.st_nlink
= 1;
2815 reloc_root
->root_item
.inode
.st_mode
= 040755;
2816 reloc_root
->root_item
.inode
.flags
= 0xffffffff80000000;
2817 reloc_root
->root_item
.objid
= SUBVOL_ROOT_INODE
;
2818 reloc_root
->root_item
.bytes_used
= Vcb
->superblock
.node_size
;
2820 ii
= ExAllocatePoolWithTag(PagedPool
, sizeof(INODE_ITEM
), ALLOC_TAG
);
2822 ERR("out of memory\n");
2823 return STATUS_INSUFFICIENT_RESOURCES
;
2826 KeQuerySystemTime(&time
);
2827 win_time_to_unix(time
, &now
);
2829 RtlZeroMemory(ii
, sizeof(INODE_ITEM
));
2830 ii
->generation
= Vcb
->superblock
.generation
;
2831 ii
->st_blocks
= Vcb
->superblock
.node_size
;
2833 ii
->st_mode
= 040755;
2838 Status
= insert_tree_item(Vcb
, reloc_root
, SUBVOL_ROOT_INODE
, TYPE_INODE_ITEM
, 0, ii
, sizeof(INODE_ITEM
), NULL
, Irp
);
2839 if (!NT_SUCCESS(Status
)) {
2840 ERR("insert_tree_item returned %08x\n", Status
);
2845 irlen
= (UINT16
)offsetof(INODE_REF
, name
[0]) + 2;
2846 ir
= ExAllocatePoolWithTag(PagedPool
, irlen
, ALLOC_TAG
);
2848 ERR("out of memory\n");
2849 return STATUS_INSUFFICIENT_RESOURCES
;
2857 Status
= insert_tree_item(Vcb
, reloc_root
, SUBVOL_ROOT_INODE
, TYPE_INODE_REF
, SUBVOL_ROOT_INODE
, ir
, irlen
, NULL
, Irp
);
2858 if (!NT_SUCCESS(Status
)) {
2859 ERR("insert_tree_item returned %08x\n", Status
);
2864 Vcb
->data_reloc_root
= reloc_root
;
2865 Vcb
->need_write
= TRUE
;
2868 return STATUS_SUCCESS
;
2871 static NTSTATUS
find_disk_holes(_In_
_Requires_lock_held_(_Curr_
->tree_lock
) device_extension
* Vcb
, _In_ device
* dev
, _In_opt_ PIRP Irp
) {
2873 traverse_ptr tp
, next_tp
;
2878 InitializeListHead(&dev
->space
);
2880 searchkey
.obj_id
= 0;
2881 searchkey
.obj_type
= TYPE_DEV_STATS
;
2882 searchkey
.offset
= dev
->devitem
.dev_id
;
2884 Status
= find_item(Vcb
, Vcb
->dev_root
, &tp
, &searchkey
, FALSE
, Irp
);
2885 if (NT_SUCCESS(Status
) && !keycmp(tp
.item
->key
, searchkey
))
2886 RtlCopyMemory(dev
->stats
, tp
.item
->data
, min(sizeof(UINT64
) * 5, tp
.item
->size
));
2888 searchkey
.obj_id
= dev
->devitem
.dev_id
;
2889 searchkey
.obj_type
= TYPE_DEV_EXTENT
;
2890 searchkey
.offset
= 0;
2892 Status
= find_item(Vcb
, Vcb
->dev_root
, &tp
, &searchkey
, FALSE
, Irp
);
2893 if (!NT_SUCCESS(Status
)) {
2894 ERR("error - find_item returned %08x\n", Status
);
2901 if (tp
.item
->key
.obj_id
== dev
->devitem
.dev_id
&& tp
.item
->key
.obj_type
== TYPE_DEV_EXTENT
) {
2902 if (tp
.item
->size
>= sizeof(DEV_EXTENT
)) {
2903 DEV_EXTENT
* de
= (DEV_EXTENT
*)tp
.item
->data
;
2905 if (tp
.item
->key
.offset
> lastaddr
) {
2906 Status
= add_space_entry(&dev
->space
, NULL
, lastaddr
, tp
.item
->key
.offset
- lastaddr
);
2907 if (!NT_SUCCESS(Status
)) {
2908 ERR("add_space_entry returned %08x\n", Status
);
2913 lastaddr
= tp
.item
->key
.offset
+ de
->length
;
2915 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
));
2919 b
= find_next_item(Vcb
, &tp
, &next_tp
, FALSE
, Irp
);
2923 if (tp
.item
->key
.obj_id
> searchkey
.obj_id
|| tp
.item
->key
.obj_type
> searchkey
.obj_type
)
2928 if (lastaddr
< dev
->devitem
.num_bytes
) {
2929 Status
= add_space_entry(&dev
->space
, NULL
, lastaddr
, dev
->devitem
.num_bytes
- lastaddr
);
2930 if (!NT_SUCCESS(Status
)) {
2931 ERR("add_space_entry returned %08x\n", Status
);
2936 // The Linux driver doesn't like to allocate chunks within the first megabyte of a device.
2938 space_list_subtract2(&dev
->space
, NULL
, 0, 0x100000, NULL
, NULL
);
2940 return STATUS_SUCCESS
;
2943 static void add_device_to_list(_In_ device_extension
* Vcb
, _In_ device
* dev
) {
2946 le
= Vcb
->devices
.Flink
;
2948 while (le
!= &Vcb
->devices
) {
2949 device
* dev2
= CONTAINING_RECORD(le
, device
, list_entry
);
2951 if (dev2
->devitem
.dev_id
> dev
->devitem
.dev_id
) {
2952 InsertHeadList(le
->Blink
, &dev
->list_entry
);
2959 InsertTailList(&Vcb
->devices
, &dev
->list_entry
);
2963 device
* find_device_from_uuid(_In_ device_extension
* Vcb
, _In_ BTRFS_UUID
* uuid
) {
2964 volume_device_extension
* vde
;
2965 pdo_device_extension
* pdode
;
2968 le
= Vcb
->devices
.Flink
;
2969 while (le
!= &Vcb
->devices
) {
2970 device
* dev
= CONTAINING_RECORD(le
, device
, list_entry
);
2972 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
,
2973 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],
2974 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]);
2976 if (RtlCompareMemory(&dev
->devitem
.device_uuid
, uuid
, sizeof(BTRFS_UUID
)) == sizeof(BTRFS_UUID
)) {
2977 TRACE("returning device %llx\n", dev
->devitem
.dev_id
);
2991 ExAcquireResourceSharedLite(&pdode
->child_lock
, TRUE
);
2993 if (Vcb
->devices_loaded
< Vcb
->superblock
.num_devices
) {
2994 le
= pdode
->children
.Flink
;
2996 while (le
!= &pdode
->children
) {
2997 volume_child
* vc
= CONTAINING_RECORD(le
, volume_child
, list_entry
);
2999 if (RtlCompareMemory(uuid
, &vc
->uuid
, sizeof(BTRFS_UUID
)) == sizeof(BTRFS_UUID
)) {
3002 dev
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(device
), ALLOC_TAG
);
3004 ExReleaseResourceLite(&pdode
->child_lock
);
3005 ERR("out of memory\n");
3009 RtlZeroMemory(dev
, sizeof(device
));
3010 dev
->devobj
= vc
->devobj
;
3011 dev
->devitem
.device_uuid
= *uuid
;
3012 dev
->devitem
.dev_id
= vc
->devid
;
3013 dev
->devitem
.num_bytes
= vc
->size
;
3014 dev
->seeding
= vc
->seeding
;
3015 dev
->readonly
= dev
->seeding
;
3017 dev
->removable
= FALSE
;
3018 dev
->disk_num
= vc
->disk_num
;
3019 dev
->part_num
= vc
->part_num
;
3020 dev
->num_trim_entries
= 0;
3021 InitializeListHead(&dev
->trim_list
);
3023 add_device_to_list(Vcb
, dev
);
3024 Vcb
->devices_loaded
++;
3026 ExReleaseResourceLite(&pdode
->child_lock
);
3035 ExReleaseResourceLite(&pdode
->child_lock
);
3038 WARN("could not find device with uuid %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n",
3039 uuid
->uuid
[0], uuid
->uuid
[1], uuid
->uuid
[2], uuid
->uuid
[3], uuid
->uuid
[4], uuid
->uuid
[5], uuid
->uuid
[6], uuid
->uuid
[7],
3040 uuid
->uuid
[8], uuid
->uuid
[9], uuid
->uuid
[10], uuid
->uuid
[11], uuid
->uuid
[12], uuid
->uuid
[13], uuid
->uuid
[14], uuid
->uuid
[15]);
3045 static BOOL
is_device_removable(_In_ PDEVICE_OBJECT devobj
) {
3047 STORAGE_HOTPLUG_INFO shi
;
3049 Status
= dev_ioctl(devobj
, IOCTL_STORAGE_GET_HOTPLUG_INFO
, NULL
, 0, &shi
, sizeof(STORAGE_HOTPLUG_INFO
), TRUE
, NULL
);
3051 if (!NT_SUCCESS(Status
)) {
3052 ERR("dev_ioctl returned %08x\n", Status
);
3056 return shi
.MediaRemovable
!= 0 ? TRUE
: FALSE
;
3059 static ULONG
get_device_change_count(_In_ PDEVICE_OBJECT devobj
) {
3062 IO_STATUS_BLOCK iosb
;
3064 Status
= dev_ioctl(devobj
, IOCTL_STORAGE_CHECK_VERIFY
, NULL
, 0, &cc
, sizeof(ULONG
), TRUE
, &iosb
);
3066 if (!NT_SUCCESS(Status
)) {
3067 ERR("dev_ioctl returned %08x\n", Status
);
3071 if (iosb
.Information
< sizeof(ULONG
)) {
3072 ERR("iosb.Information was too short\n");
3079 void init_device(_In_ device_extension
* Vcb
, _Inout_ device
* dev
, _In_ BOOL get_nums
) {
3082 ATA_PASS_THROUGH_EX
* apte
;
3083 STORAGE_PROPERTY_QUERY spq
;
3084 DEVICE_TRIM_DESCRIPTOR dtd
;
3086 dev
->removable
= is_device_removable(dev
->devobj
);
3087 dev
->change_count
= dev
->removable
? get_device_change_count(dev
->devobj
) : 0;
3090 STORAGE_DEVICE_NUMBER sdn
;
3092 Status
= dev_ioctl(dev
->devobj
, IOCTL_STORAGE_GET_DEVICE_NUMBER
, NULL
, 0,
3093 &sdn
, sizeof(STORAGE_DEVICE_NUMBER
), TRUE
, NULL
);
3095 if (!NT_SUCCESS(Status
)) {
3096 WARN("IOCTL_STORAGE_GET_DEVICE_NUMBER returned %08x\n", Status
);
3097 dev
->disk_num
= 0xffffffff;
3098 dev
->part_num
= 0xffffffff;
3100 dev
->disk_num
= sdn
.DeviceNumber
;
3101 dev
->part_num
= sdn
.PartitionNumber
;
3106 dev
->readonly
= dev
->seeding
;
3108 dev
->num_trim_entries
= 0;
3109 dev
->stats_changed
= FALSE
;
3110 InitializeListHead(&dev
->trim_list
);
3112 if (!dev
->readonly
) {
3113 Status
= dev_ioctl(dev
->devobj
, IOCTL_DISK_IS_WRITABLE
, NULL
, 0,
3114 NULL
, 0, TRUE
, NULL
);
3115 if (Status
== STATUS_MEDIA_WRITE_PROTECTED
)
3116 dev
->readonly
= TRUE
;
3119 aptelen
= sizeof(ATA_PASS_THROUGH_EX
) + 512;
3120 apte
= ExAllocatePoolWithTag(NonPagedPool
, aptelen
, ALLOC_TAG
);
3122 ERR("out of memory\n");
3126 RtlZeroMemory(apte
, aptelen
);
3128 apte
->Length
= sizeof(ATA_PASS_THROUGH_EX
);
3129 apte
->AtaFlags
= ATA_FLAGS_DATA_IN
;
3130 apte
->DataTransferLength
= aptelen
- sizeof(ATA_PASS_THROUGH_EX
);
3131 apte
->TimeOutValue
= 3;
3132 apte
->DataBufferOffset
= apte
->Length
;
3133 apte
->CurrentTaskFile
[6] = IDE_COMMAND_IDENTIFY
;
3135 Status
= dev_ioctl(dev
->devobj
, IOCTL_ATA_PASS_THROUGH
, apte
, aptelen
,
3136 apte
, aptelen
, TRUE
, NULL
);
3138 if (!NT_SUCCESS(Status
))
3139 TRACE("IOCTL_ATA_PASS_THROUGH returned %08x for IDENTIFY DEVICE\n", Status
);
3141 IDENTIFY_DEVICE_DATA
* idd
= (IDENTIFY_DEVICE_DATA
*)((UINT8
*)apte
+ sizeof(ATA_PASS_THROUGH_EX
));
3143 if (idd
->CommandSetSupport
.FlushCache
) {
3144 dev
->can_flush
= TRUE
;
3145 TRACE("FLUSH CACHE supported\n");
3147 TRACE("FLUSH CACHE not supported\n");
3152 spq
.PropertyId
= StorageDeviceTrimProperty
;
3153 spq
.QueryType
= PropertyStandardQuery
;
3154 spq
.AdditionalParameters
[0] = 0;
3156 Status
= dev_ioctl(dev
->devobj
, IOCTL_STORAGE_QUERY_PROPERTY
, &spq
, sizeof(STORAGE_PROPERTY_QUERY
),
3157 &dtd
, sizeof(DEVICE_TRIM_DESCRIPTOR
), TRUE
, NULL
);
3159 if (NT_SUCCESS(Status
)) {
3160 if (dtd
.TrimEnabled
) {
3163 TRACE("TRIM supported\n");
3165 TRACE("TRIM not supported\n");
3168 RtlZeroMemory(dev
->stats
, sizeof(UINT64
) * 5);
3171 static NTSTATUS
load_chunk_root(_In_
_Requires_lock_held_(_Curr_
->tree_lock
) device_extension
* Vcb
, _In_opt_ PIRP Irp
) {
3172 traverse_ptr tp
, next_tp
;
3178 searchkey
.obj_id
= 0;
3179 searchkey
.obj_type
= 0;
3180 searchkey
.offset
= 0;
3182 Vcb
->data_flags
= 0;
3183 Vcb
->metadata_flags
= 0;
3184 Vcb
->system_flags
= 0;
3186 Status
= find_item(Vcb
, Vcb
->chunk_root
, &tp
, &searchkey
, FALSE
, Irp
);
3187 if (!NT_SUCCESS(Status
)) {
3188 ERR("error - find_item returned %08x\n", Status
);
3193 TRACE("(%llx,%x,%llx)\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
3195 if (tp
.item
->key
.obj_id
== 1 && tp
.item
->key
.obj_type
== TYPE_DEV_ITEM
) {
3196 if (tp
.item
->size
< sizeof(DEV_ITEM
)) {
3197 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
));
3199 DEV_ITEM
* di
= (DEV_ITEM
*)tp
.item
->data
;
3203 le
= Vcb
->devices
.Flink
;
3204 while (le
!= &Vcb
->devices
) {
3205 device
* dev
= CONTAINING_RECORD(le
, device
, list_entry
);
3207 if (dev
->devobj
&& RtlCompareMemory(&dev
->devitem
.device_uuid
, &di
->device_uuid
, sizeof(BTRFS_UUID
)) == sizeof(BTRFS_UUID
)) {
3208 RtlCopyMemory(&dev
->devitem
, tp
.item
->data
, min(tp
.item
->size
, sizeof(DEV_ITEM
)));
3210 if (le
!= Vcb
->devices
.Flink
)
3211 init_device(Vcb
, dev
, TRUE
);
3220 if (!done
&& Vcb
->vde
) {
3221 volume_device_extension
* vde
= Vcb
->vde
;
3222 pdo_device_extension
* pdode
= vde
->pdode
;
3224 ExAcquireResourceSharedLite(&pdode
->child_lock
, TRUE
);
3226 if (Vcb
->devices_loaded
< Vcb
->superblock
.num_devices
) {
3227 le
= pdode
->children
.Flink
;
3229 while (le
!= &pdode
->children
) {
3230 volume_child
* vc
= CONTAINING_RECORD(le
, volume_child
, list_entry
);
3232 if (RtlCompareMemory(&di
->device_uuid
, &vc
->uuid
, sizeof(BTRFS_UUID
)) == sizeof(BTRFS_UUID
)) {
3235 dev
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(device
), ALLOC_TAG
);
3237 ExReleaseResourceLite(&pdode
->child_lock
);
3238 ERR("out of memory\n");
3239 return STATUS_INSUFFICIENT_RESOURCES
;
3242 RtlZeroMemory(dev
, sizeof(device
));
3244 dev
->devobj
= vc
->devobj
;
3245 RtlCopyMemory(&dev
->devitem
, di
, min(tp
.item
->size
, sizeof(DEV_ITEM
)));
3246 dev
->seeding
= vc
->seeding
;
3247 init_device(Vcb
, dev
, FALSE
);
3249 if (dev
->devitem
.num_bytes
> vc
->size
) {
3250 WARN("device %llx: DEV_ITEM says %llx bytes, but Windows only reports %llx\n", tp
.item
->key
.offset
,
3251 dev
->devitem
.num_bytes
, vc
->size
);
3253 dev
->devitem
.num_bytes
= vc
->size
;
3256 dev
->disk_num
= vc
->disk_num
;
3257 dev
->part_num
= vc
->part_num
;
3258 add_device_to_list(Vcb
, dev
);
3259 Vcb
->devices_loaded
++;
3269 if (!Vcb
->options
.allow_degraded
) {
3270 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
,
3271 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],
3272 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]);
3276 dev
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(device
), ALLOC_TAG
);
3278 ExReleaseResourceLite(&pdode
->child_lock
);
3279 ERR("out of memory\n");
3280 return STATUS_INSUFFICIENT_RESOURCES
;
3283 RtlZeroMemory(dev
, sizeof(device
));
3285 // Missing device, so we keep dev->devobj as NULL
3286 RtlCopyMemory(&dev
->devitem
, di
, min(tp
.item
->size
, sizeof(DEV_ITEM
)));
3287 InitializeListHead(&dev
->trim_list
);
3289 add_device_to_list(Vcb
, dev
);
3290 Vcb
->devices_loaded
++;
3294 ERR("unexpected device %llx found\n", tp
.item
->key
.offset
);
3296 ExReleaseResourceLite(&pdode
->child_lock
);
3299 } else if (tp
.item
->key
.obj_type
== TYPE_CHUNK_ITEM
) {
3300 if (tp
.item
->size
< sizeof(CHUNK_ITEM
)) {
3301 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
));
3303 c
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(chunk
), ALLOC_TAG
);
3306 ERR("out of memory\n");
3307 return STATUS_INSUFFICIENT_RESOURCES
;
3310 c
->size
= tp
.item
->size
;
3311 c
->offset
= tp
.item
->key
.offset
;
3312 c
->used
= c
->oldused
= 0;
3313 c
->cache
= c
->old_cache
= NULL
;
3315 c
->readonly
= FALSE
;
3317 c
->cache_loaded
= FALSE
;
3319 c
->space_changed
= FALSE
;
3322 c
->chunk_item
= ExAllocatePoolWithTag(NonPagedPool
, tp
.item
->size
, ALLOC_TAG
);
3324 if (!c
->chunk_item
) {
3325 ERR("out of memory\n");
3327 return STATUS_INSUFFICIENT_RESOURCES
;
3330 RtlCopyMemory(c
->chunk_item
, tp
.item
->data
, tp
.item
->size
);
3332 if (c
->chunk_item
->type
& BLOCK_FLAG_DATA
&& c
->chunk_item
->type
> Vcb
->data_flags
)
3333 Vcb
->data_flags
= c
->chunk_item
->type
;
3335 if (c
->chunk_item
->type
& BLOCK_FLAG_METADATA
&& c
->chunk_item
->type
> Vcb
->metadata_flags
)
3336 Vcb
->metadata_flags
= c
->chunk_item
->type
;
3338 if (c
->chunk_item
->type
& BLOCK_FLAG_SYSTEM
&& c
->chunk_item
->type
> Vcb
->system_flags
)
3339 Vcb
->system_flags
= c
->chunk_item
->type
;
3341 if (c
->chunk_item
->type
& BLOCK_FLAG_RAID10
) {
3342 if (c
->chunk_item
->sub_stripes
== 0 || c
->chunk_item
->sub_stripes
> c
->chunk_item
->num_stripes
) {
3343 ERR("chunk %llx: invalid stripes (num_stripes %u, sub_stripes %u)\n", c
->offset
, c
->chunk_item
->num_stripes
, c
->chunk_item
->sub_stripes
);
3344 ExFreePool(c
->chunk_item
);
3346 return STATUS_INTERNAL_ERROR
;
3350 if (c
->chunk_item
->num_stripes
> 0) {
3351 CHUNK_ITEM_STRIPE
* cis
= (CHUNK_ITEM_STRIPE
*)&c
->chunk_item
[1];
3354 c
->devices
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(device
*) * c
->chunk_item
->num_stripes
, ALLOC_TAG
);
3357 ERR("out of memory\n");
3358 ExFreePool(c
->chunk_item
);
3360 return STATUS_INSUFFICIENT_RESOURCES
;
3363 for (i
= 0; i
< c
->chunk_item
->num_stripes
; i
++) {
3364 c
->devices
[i
] = find_device_from_uuid(Vcb
, &cis
[i
].dev_uuid
);
3365 TRACE("device %llu = %p\n", i
, c
->devices
[i
]);
3367 if (!c
->devices
[i
]) {
3368 ERR("missing device\n");
3369 ExFreePool(c
->chunk_item
);
3371 return STATUS_INTERNAL_ERROR
;
3374 if (c
->devices
[i
]->readonly
)
3378 ERR("chunk %llx: number of stripes is 0\n", c
->offset
);
3379 ExFreePool(c
->chunk_item
);
3381 return STATUS_INTERNAL_ERROR
;
3384 ExInitializeResourceLite(&c
->lock
);
3385 ExInitializeResourceLite(&c
->changed_extents_lock
);
3387 InitializeListHead(&c
->space
);
3388 InitializeListHead(&c
->space_size
);
3389 InitializeListHead(&c
->deleting
);
3390 InitializeListHead(&c
->changed_extents
);
3392 InitializeListHead(&c
->range_locks
);
3393 ExInitializeResourceLite(&c
->range_locks_lock
);
3394 KeInitializeEvent(&c
->range_locks_event
, NotificationEvent
, FALSE
);
3396 InitializeListHead(&c
->partial_stripes
);
3397 ExInitializeResourceLite(&c
->partial_stripes_lock
);
3399 c
->last_alloc_set
= FALSE
;
3403 InsertTailList(&Vcb
->chunks
, &c
->list_entry
);
3405 c
->list_entry_balance
.Flink
= NULL
;
3409 b
= find_next_item(Vcb
, &tp
, &next_tp
, FALSE
, Irp
);
3415 Vcb
->log_to_phys_loaded
= TRUE
;
3417 if (Vcb
->data_flags
== 0)
3418 Vcb
->data_flags
= BLOCK_FLAG_DATA
| (Vcb
->superblock
.num_devices
> 1 ? BLOCK_FLAG_RAID0
: 0);
3420 if (Vcb
->metadata_flags
== 0)
3421 Vcb
->metadata_flags
= BLOCK_FLAG_METADATA
| (Vcb
->superblock
.num_devices
> 1 ? BLOCK_FLAG_RAID1
: BLOCK_FLAG_DUPLICATE
);
3423 if (Vcb
->system_flags
== 0)
3424 Vcb
->system_flags
= BLOCK_FLAG_SYSTEM
| (Vcb
->superblock
.num_devices
> 1 ? BLOCK_FLAG_RAID1
: BLOCK_FLAG_DUPLICATE
);
3426 if (Vcb
->superblock
.incompat_flags
& BTRFS_INCOMPAT_FLAGS_MIXED_GROUPS
) {
3427 Vcb
->metadata_flags
|= BLOCK_FLAG_DATA
;
3428 Vcb
->data_flags
= Vcb
->metadata_flags
;
3431 return STATUS_SUCCESS
;
3434 void protect_superblocks(_Inout_ chunk
* c
) {
3436 UINT64 off_start
, off_end
;
3438 // The Linux driver also protects all the space before the first superblock.
3439 // I realize this confuses physical and logical addresses, but this is what btrfs-progs does -
3440 // evidently Linux assumes the chunk at 0 is always SINGLE.
3441 if (c
->offset
< superblock_addrs
[0])
3442 space_list_subtract(c
, FALSE
, c
->offset
, superblock_addrs
[0] - c
->offset
, NULL
);
3444 while (superblock_addrs
[i
] != 0) {
3445 CHUNK_ITEM
* ci
= c
->chunk_item
;
3446 CHUNK_ITEM_STRIPE
* cis
= (CHUNK_ITEM_STRIPE
*)&ci
[1];
3448 if (ci
->type
& BLOCK_FLAG_RAID0
|| ci
->type
& BLOCK_FLAG_RAID10
) {
3449 for (j
= 0; j
< ci
->num_stripes
; j
++) {
3450 UINT16 sub_stripes
= max(ci
->sub_stripes
, 1);
3452 if (cis
[j
].offset
+ (ci
->size
* ci
->num_stripes
/ sub_stripes
) > superblock_addrs
[i
] && cis
[j
].offset
<= superblock_addrs
[i
] + sizeof(superblock
)) {
3455 UINT16 startoffstripe
;
3458 TRACE("cut out superblock in chunk %llx\n", c
->offset
);
3460 off_start
= superblock_addrs
[i
] - cis
[j
].offset
;
3461 off_start
-= off_start
% ci
->stripe_length
;
3462 off_start
*= ci
->num_stripes
/ sub_stripes
;
3463 off_start
+= (j
/ sub_stripes
) * ci
->stripe_length
;
3465 off_end
= off_start
+ ci
->stripe_length
;
3468 get_raid0_offset(off_start
, ci
->stripe_length
, ci
->num_stripes
/ sub_stripes
, &startoff
, &startoffstripe
);
3469 TRACE("j = %u, startoffstripe = %u\n", j
, startoffstripe
);
3470 TRACE("startoff = %llx, superblock = %llx\n", startoff
+ cis
[j
].offset
, superblock_addrs
[i
]);
3473 space_list_subtract(c
, FALSE
, c
->offset
+ off_start
, off_end
- off_start
, NULL
);
3476 } else if (ci
->type
& BLOCK_FLAG_RAID5
) {
3477 UINT64 stripe_size
= ci
->size
/ (ci
->num_stripes
- 1);
3479 for (j
= 0; j
< ci
->num_stripes
; j
++) {
3480 if (cis
[j
].offset
+ stripe_size
> superblock_addrs
[i
] && cis
[j
].offset
<= superblock_addrs
[i
] + sizeof(superblock
)) {
3481 TRACE("cut out superblock in chunk %llx\n", c
->offset
);
3483 off_start
= superblock_addrs
[i
] - cis
[j
].offset
;
3484 off_start
-= off_start
% ci
->stripe_length
;
3485 off_start
*= ci
->num_stripes
- 1;
3487 off_end
= sector_align(superblock_addrs
[i
] - cis
[j
].offset
+ sizeof(superblock
), ci
->stripe_length
);
3488 off_end
*= ci
->num_stripes
- 1;
3490 TRACE("cutting out %llx, size %llx\n", c
->offset
+ off_start
, off_end
- off_start
);
3492 space_list_subtract(c
, FALSE
, c
->offset
+ off_start
, off_end
- off_start
, NULL
);
3495 } else if (ci
->type
& BLOCK_FLAG_RAID6
) {
3496 UINT64 stripe_size
= ci
->size
/ (ci
->num_stripes
- 2);
3498 for (j
= 0; j
< ci
->num_stripes
; j
++) {
3499 if (cis
[j
].offset
+ stripe_size
> superblock_addrs
[i
] && cis
[j
].offset
<= superblock_addrs
[i
] + sizeof(superblock
)) {
3500 TRACE("cut out superblock in chunk %llx\n", c
->offset
);
3502 off_start
= superblock_addrs
[i
] - cis
[j
].offset
;
3503 off_start
-= off_start
% ci
->stripe_length
;
3504 off_start
*= ci
->num_stripes
- 2;
3506 off_end
= sector_align(superblock_addrs
[i
] - cis
[j
].offset
+ sizeof(superblock
), ci
->stripe_length
);
3507 off_end
*= ci
->num_stripes
- 2;
3509 TRACE("cutting out %llx, size %llx\n", c
->offset
+ off_start
, off_end
- off_start
);
3511 space_list_subtract(c
, FALSE
, c
->offset
+ off_start
, off_end
- off_start
, NULL
);
3514 } else { // SINGLE, DUPLICATE, RAID1
3515 for (j
= 0; j
< ci
->num_stripes
; j
++) {
3516 if (cis
[j
].offset
+ ci
->size
> superblock_addrs
[i
] && cis
[j
].offset
<= superblock_addrs
[i
] + sizeof(superblock
)) {
3517 TRACE("cut out superblock in chunk %llx\n", c
->offset
);
3519 // The Linux driver protects the whole stripe in which the superblock lives
3521 off_start
= ((superblock_addrs
[i
] - cis
[j
].offset
) / c
->chunk_item
->stripe_length
) * c
->chunk_item
->stripe_length
;
3522 off_end
= sector_align(superblock_addrs
[i
] - cis
[j
].offset
+ sizeof(superblock
), c
->chunk_item
->stripe_length
);
3524 space_list_subtract(c
, FALSE
, c
->offset
+ off_start
, off_end
- off_start
, NULL
);
3533 NTSTATUS
find_chunk_usage(_In_
_Requires_lock_held_(_Curr_
->tree_lock
) device_extension
* Vcb
, _In_opt_ PIRP Irp
) {
3534 LIST_ENTRY
* le
= Vcb
->chunks
.Flink
;
3538 BLOCK_GROUP_ITEM
* bgi
;
3541 searchkey
.obj_type
= TYPE_BLOCK_GROUP_ITEM
;
3543 while (le
!= &Vcb
->chunks
) {
3544 c
= CONTAINING_RECORD(le
, chunk
, list_entry
);
3546 searchkey
.obj_id
= c
->offset
;
3547 searchkey
.offset
= c
->chunk_item
->size
;
3549 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
, Irp
);
3550 if (!NT_SUCCESS(Status
)) {
3551 ERR("error - find_item returned %08x\n", Status
);
3555 if (!keycmp(searchkey
, tp
.item
->key
)) {
3556 if (tp
.item
->size
>= sizeof(BLOCK_GROUP_ITEM
)) {
3557 bgi
= (BLOCK_GROUP_ITEM
*)tp
.item
->data
;
3559 c
->used
= c
->oldused
= bgi
->used
;
3561 TRACE("chunk %llx has %llx bytes used\n", c
->offset
, c
->used
);
3563 ERR("(%llx;%llx,%x,%llx) is %u bytes, expected %u\n",
3564 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
));
3571 Vcb
->chunk_usage_found
= TRUE
;
3573 return STATUS_SUCCESS
;
3576 static NTSTATUS
load_sys_chunks(_In_ device_extension
* Vcb
) {
3578 ULONG n
= Vcb
->superblock
.n
;
3581 if (n
> sizeof(KEY
)) {
3582 RtlCopyMemory(&key
, &Vcb
->superblock
.sys_chunk_array
[Vcb
->superblock
.n
- n
], sizeof(KEY
));
3585 return STATUS_SUCCESS
;
3587 TRACE("bootstrap: %llx,%x,%llx\n", key
.obj_id
, key
.obj_type
, key
.offset
);
3589 if (key
.obj_type
== TYPE_CHUNK_ITEM
) {
3594 if (n
< sizeof(CHUNK_ITEM
))
3595 return STATUS_SUCCESS
;
3597 ci
= (CHUNK_ITEM
*)&Vcb
->superblock
.sys_chunk_array
[Vcb
->superblock
.n
- n
];
3598 cisize
= sizeof(CHUNK_ITEM
) + (ci
->num_stripes
* sizeof(CHUNK_ITEM_STRIPE
));
3601 return STATUS_SUCCESS
;
3603 sc
= ExAllocatePoolWithTag(PagedPool
, sizeof(sys_chunk
), ALLOC_TAG
);
3606 ERR("out of memory\n");
3607 return STATUS_INSUFFICIENT_RESOURCES
;
3612 sc
->data
= ExAllocatePoolWithTag(PagedPool
, sc
->size
, ALLOC_TAG
);
3615 ERR("out of memory\n");
3617 return STATUS_INSUFFICIENT_RESOURCES
;
3620 RtlCopyMemory(sc
->data
, ci
, sc
->size
);
3621 InsertTailList(&Vcb
->sys_chunks
, &sc
->list_entry
);
3625 ERR("unexpected item %llx,%x,%llx in bootstrap\n", key
.obj_id
, key
.obj_type
, key
.offset
);
3626 return STATUS_INTERNAL_ERROR
;
3630 return STATUS_SUCCESS
;
3634 static root
* find_default_subvol(_In_
_Requires_lock_held_(_Curr_
->tree_lock
) device_extension
* Vcb
, _In_opt_ PIRP Irp
) {
3637 static const char fn
[] = "default";
3638 static UINT32 crc32
= 0x8dbfc2d2;
3640 if (Vcb
->options
.subvol_id
!= 0) {
3641 le
= Vcb
->roots
.Flink
;
3642 while (le
!= &Vcb
->roots
) {
3643 root
* r
= CONTAINING_RECORD(le
, root
, list_entry
);
3645 if (r
->id
== Vcb
->options
.subvol_id
)
3652 if (Vcb
->superblock
.incompat_flags
& BTRFS_INCOMPAT_FLAGS_DEFAULT_SUBVOL
) {
3658 searchkey
.obj_id
= Vcb
->superblock
.root_dir_objectid
;
3659 searchkey
.obj_type
= TYPE_DIR_ITEM
;
3660 searchkey
.offset
= crc32
;
3662 Status
= find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, FALSE
, Irp
);
3663 if (!NT_SUCCESS(Status
)) {
3664 ERR("error - find_item returned %08x\n", Status
);
3668 if (keycmp(tp
.item
->key
, searchkey
)) {
3669 ERR("could not find (%llx,%x,%llx) in root tree\n", searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
);
3673 if (tp
.item
->size
< sizeof(DIR_ITEM
)) {
3674 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
));
3678 di
= (DIR_ITEM
*)tp
.item
->data
;
3680 if (tp
.item
->size
< sizeof(DIR_ITEM
) - 1 + di
->n
) {
3681 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
);
3685 if (di
->n
!= strlen(fn
) || RtlCompareMemory(di
->name
, fn
, di
->n
) != di
->n
) {
3686 ERR("root DIR_ITEM had same CRC32, but was not \"default\"\n");
3690 if (di
->key
.obj_type
!= TYPE_ROOT_ITEM
) {
3691 ERR("default root has key (%llx,%x,%llx), expected subvolume\n", di
->key
.obj_id
, di
->key
.obj_type
, di
->key
.offset
);
3695 le
= Vcb
->roots
.Flink
;
3696 while (le
!= &Vcb
->roots
) {
3697 root
* r
= CONTAINING_RECORD(le
, root
, list_entry
);
3699 if (r
->id
== di
->key
.obj_id
)
3705 ERR("could not find root %llx, using default instead\n", di
->key
.obj_id
);
3709 le
= Vcb
->roots
.Flink
;
3710 while (le
!= &Vcb
->roots
) {
3711 root
* r
= CONTAINING_RECORD(le
, root
, list_entry
);
3713 if (r
->id
== BTRFS_ROOT_FSTREE
)
3722 void init_file_cache(_In_ PFILE_OBJECT FileObject
, _In_ CC_FILE_SIZES
* ccfs
) {
3723 TRACE("(%p, %p)\n", FileObject
, ccfs
);
3725 CcInitializeCacheMap(FileObject
, ccfs
, FALSE
, cache_callbacks
, FileObject
);
3728 fCcSetAdditionalCacheAttributesEx(FileObject
, CC_ENABLE_DISK_IO_ACCOUNTING
);
3730 CcSetReadAheadGranularity(FileObject
, READ_AHEAD_GRANULARITY
);
3733 static NTSTATUS
create_calc_threads(_In_ PDEVICE_OBJECT DeviceObject
) {
3734 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
3737 Vcb
->calcthreads
.num_threads
= KeQueryActiveProcessorCount(NULL
);
3739 Vcb
->calcthreads
.threads
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(drv_calc_thread
) * Vcb
->calcthreads
.num_threads
, ALLOC_TAG
);
3740 if (!Vcb
->calcthreads
.threads
) {
3741 ERR("out of memory\n");
3742 return STATUS_INSUFFICIENT_RESOURCES
;
3745 InitializeListHead(&Vcb
->calcthreads
.job_list
);
3746 ExInitializeResourceLite(&Vcb
->calcthreads
.lock
);
3747 KeInitializeEvent(&Vcb
->calcthreads
.event
, NotificationEvent
, FALSE
);
3749 RtlZeroMemory(Vcb
->calcthreads
.threads
, sizeof(drv_calc_thread
) * Vcb
->calcthreads
.num_threads
);
3751 for (i
= 0; i
< Vcb
->calcthreads
.num_threads
; i
++) {
3754 Vcb
->calcthreads
.threads
[i
].DeviceObject
= DeviceObject
;
3755 KeInitializeEvent(&Vcb
->calcthreads
.threads
[i
].finished
, NotificationEvent
, FALSE
);
3757 Status
= PsCreateSystemThread(&Vcb
->calcthreads
.threads
[i
].handle
, 0, NULL
, NULL
, NULL
, calc_thread
, &Vcb
->calcthreads
.threads
[i
]);
3758 if (!NT_SUCCESS(Status
)) {
3761 ERR("PsCreateSystemThread returned %08x\n", Status
);
3763 for (j
= 0; j
< i
; j
++) {
3764 Vcb
->calcthreads
.threads
[i
].quit
= TRUE
;
3767 KeSetEvent(&Vcb
->calcthreads
.event
, 0, FALSE
);
3773 return STATUS_SUCCESS
;
3776 static BOOL
is_btrfs_volume(_In_ PDEVICE_OBJECT DeviceObject
) {
3778 MOUNTDEV_NAME mdn
, *mdn2
;
3781 Status
= dev_ioctl(DeviceObject
, IOCTL_MOUNTDEV_QUERY_DEVICE_NAME
, NULL
, 0, &mdn
, sizeof(MOUNTDEV_NAME
), TRUE
, NULL
);
3782 if (!NT_SUCCESS(Status
) && Status
!= STATUS_BUFFER_OVERFLOW
) {
3783 ERR("IOCTL_MOUNTDEV_QUERY_DEVICE_NAME returned %08x\n", Status
);
3787 mdnsize
= (ULONG
)offsetof(MOUNTDEV_NAME
, Name
[0]) + mdn
.NameLength
;
3789 mdn2
= ExAllocatePoolWithTag(PagedPool
, mdnsize
, ALLOC_TAG
);
3791 ERR("out of memory\n");
3795 Status
= dev_ioctl(DeviceObject
, IOCTL_MOUNTDEV_QUERY_DEVICE_NAME
, NULL
, 0, mdn2
, mdnsize
, TRUE
, NULL
);
3796 if (!NT_SUCCESS(Status
)) {
3797 ERR("IOCTL_MOUNTDEV_QUERY_DEVICE_NAME returned %08x\n", Status
);
3802 if (mdn2
->NameLength
> (sizeof(BTRFS_VOLUME_PREFIX
) - sizeof(WCHAR
)) &&
3803 RtlCompareMemory(mdn2
->Name
, BTRFS_VOLUME_PREFIX
, sizeof(BTRFS_VOLUME_PREFIX
) - sizeof(WCHAR
)) == sizeof(BTRFS_VOLUME_PREFIX
) - sizeof(WCHAR
)) {
3813 static NTSTATUS
get_device_pnp_name_guid(_In_ PDEVICE_OBJECT DeviceObject
, _Out_ PUNICODE_STRING pnp_name
, _In_
const GUID
* guid
) {
3815 WCHAR
*list
= NULL
, *s
;
3817 Status
= IoGetDeviceInterfaces((PVOID
)guid
, NULL
, 0, &list
);
3818 if (!NT_SUCCESS(Status
)) {
3819 ERR("IoGetDeviceInterfaces returned %08x\n", Status
);
3825 PFILE_OBJECT FileObject
;
3826 PDEVICE_OBJECT devobj
;
3827 UNICODE_STRING name
;
3829 name
.Length
= name
.MaximumLength
= (USHORT
)wcslen(s
) * sizeof(WCHAR
);
3832 if (NT_SUCCESS(IoGetDeviceObjectPointer(&name
, FILE_READ_ATTRIBUTES
, &FileObject
, &devobj
))) {
3833 if (DeviceObject
== devobj
|| DeviceObject
== FileObject
->DeviceObject
) {
3834 ObDereferenceObject(FileObject
);
3836 pnp_name
->Buffer
= ExAllocatePoolWithTag(PagedPool
, name
.Length
, ALLOC_TAG
);
3837 if (!pnp_name
->Buffer
) {
3838 ERR("out of memory\n");
3839 Status
= STATUS_INSUFFICIENT_RESOURCES
;
3843 RtlCopyMemory(pnp_name
->Buffer
, name
.Buffer
, name
.Length
);
3844 pnp_name
->Length
= pnp_name
->MaximumLength
= name
.Length
;
3846 Status
= STATUS_SUCCESS
;
3850 ObDereferenceObject(FileObject
);
3853 s
= &s
[wcslen(s
) + 1];
3856 pnp_name
->Length
= pnp_name
->MaximumLength
= 0;
3857 pnp_name
->Buffer
= 0;
3859 Status
= STATUS_NOT_FOUND
;
3868 NTSTATUS
get_device_pnp_name(_In_ PDEVICE_OBJECT DeviceObject
, _Out_ PUNICODE_STRING pnp_name
, _Out_
const GUID
** guid
) {
3871 Status
= get_device_pnp_name_guid(DeviceObject
, pnp_name
, &GUID_DEVINTERFACE_VOLUME
);
3872 if (NT_SUCCESS(Status
)) {
3873 *guid
= &GUID_DEVINTERFACE_VOLUME
;
3877 Status
= get_device_pnp_name_guid(DeviceObject
, pnp_name
, &GUID_DEVINTERFACE_HIDDEN_VOLUME
);
3878 if (NT_SUCCESS(Status
)) {
3879 *guid
= &GUID_DEVINTERFACE_HIDDEN_VOLUME
;
3883 Status
= get_device_pnp_name_guid(DeviceObject
, pnp_name
, &GUID_DEVINTERFACE_DISK
);
3884 if (NT_SUCCESS(Status
)) {
3885 *guid
= &GUID_DEVINTERFACE_DISK
;
3889 return STATUS_NOT_FOUND
;
3892 _Success_(return>=0)
3893 static NTSTATUS
check_mount_device(_In_ PDEVICE_OBJECT DeviceObject
, _Out_ BOOL
* no_pnp
) {
3898 UNICODE_STRING pnp_name
;
3901 to_read
= DeviceObject
->SectorSize
== 0 ? sizeof(superblock
) : (ULONG
)sector_align(sizeof(superblock
), DeviceObject
->SectorSize
);
3903 sb
= ExAllocatePoolWithTag(NonPagedPool
, to_read
, ALLOC_TAG
);
3905 ERR("out of memory\n");
3906 return STATUS_INSUFFICIENT_RESOURCES
;
3909 Status
= sync_read_phys(DeviceObject
, superblock_addrs
[0], to_read
, (PUCHAR
)sb
, TRUE
);
3910 if (!NT_SUCCESS(Status
)) {
3911 ERR("sync_read_phys returned %08x\n", Status
);
3915 if (sb
->magic
!= BTRFS_MAGIC
) {
3916 Status
= STATUS_SUCCESS
;
3920 crc32
= ~calc_crc32c(0xffffffff, (UINT8
*)&sb
->uuid
, (ULONG
)sizeof(superblock
) - sizeof(sb
->checksum
));
3922 if (crc32
!= *((UINT32
*)sb
->checksum
)) {
3923 WARN("crc32 was %08x, expected %08x\n", crc32
, *((UINT32
*)sb
->checksum
));
3924 Status
= STATUS_SUCCESS
;
3928 DeviceObject
->Flags
&= ~DO_VERIFY_VOLUME
;
3930 pnp_name
.Buffer
= NULL
;
3932 Status
= get_device_pnp_name(DeviceObject
, &pnp_name
, &guid
);
3933 if (!NT_SUCCESS(Status
)) {
3934 WARN("get_device_pnp_name returned %08x\n", Status
);
3935 pnp_name
.Length
= 0;
3938 if (pnp_name
.Length
== 0)
3942 volume_arrival(drvobj
, &pnp_name
);
3945 if (pnp_name
.Buffer
)
3946 ExFreePool(pnp_name
.Buffer
);
3948 Status
= STATUS_SUCCESS
;
3956 static BOOL
still_has_superblock(_In_ PDEVICE_OBJECT device
) {
3960 PDEVICE_OBJECT device2
;
3965 to_read
= device
->SectorSize
== 0 ? sizeof(superblock
) : (ULONG
)sector_align(sizeof(superblock
), device
->SectorSize
);
3967 sb
= ExAllocatePoolWithTag(NonPagedPool
, to_read
, ALLOC_TAG
);
3969 ERR("out of memory\n");
3973 Status
= sync_read_phys(device
, superblock_addrs
[0], to_read
, (PUCHAR
)sb
, TRUE
);
3974 if (!NT_SUCCESS(Status
)) {
3975 ERR("Failed to read superblock: %08x\n", Status
);
3980 if (sb
->magic
!= BTRFS_MAGIC
) {
3981 TRACE("not a BTRFS volume\n");
3985 UINT32 crc32
= ~calc_crc32c(0xffffffff, (UINT8
*)&sb
->uuid
, (ULONG
)sizeof(superblock
) - sizeof(sb
->checksum
));
3987 if (crc32
!= *((UINT32
*)sb
->checksum
)) {
3988 WARN("crc32 was %08x, expected %08x\n", crc32
, *((UINT32
*)sb
->checksum
));
3997 device2
->Flags
&= ~DO_VERIFY_VOLUME
;
3998 device2
= IoGetLowerDeviceObject(device2
);
4005 static NTSTATUS
mount_vol(_In_ PDEVICE_OBJECT DeviceObject
, _In_ PIRP Irp
) {
4006 PIO_STACK_LOCATION IrpSp
;
4007 PDEVICE_OBJECT NewDeviceObject
= NULL
;
4008 PDEVICE_OBJECT DeviceToMount
, readobj
;
4010 device_extension
* Vcb
= NULL
;
4011 LIST_ENTRY
*le
, batchlist
;
4014 fcb
* root_fcb
= NULL
;
4015 ccb
* root_ccb
= NULL
;
4016 BOOL init_lookaside
= FALSE
;
4018 volume_device_extension
* vde
= NULL
;
4019 pdo_device_extension
* pdode
= NULL
;
4021 BOOL no_pnp
= FALSE
;
4024 TRACE("(%p, %p)\n", DeviceObject
, Irp
);
4026 if (DeviceObject
!= master_devobj
) {
4027 Status
= STATUS_INVALID_DEVICE_REQUEST
;
4031 IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
4032 DeviceToMount
= IrpSp
->Parameters
.MountVolume
.DeviceObject
;
4034 if (!is_btrfs_volume(DeviceToMount
)) {
4035 Status
= check_mount_device(DeviceToMount
, &no_pnp
);
4036 if (!NT_SUCCESS(Status
))
4037 WARN("check_mount_device returned %08x\n", Status
);
4040 Status
= STATUS_UNRECOGNIZED_VOLUME
;
4046 pdo
= DeviceToMount
;
4048 while (IoGetLowerDeviceObject(pdo
)) {
4049 pdo
= IoGetLowerDeviceObject(pdo
);
4052 ExAcquireResourceSharedLite(&pdo_list_lock
, TRUE
);
4054 le
= pdo_list
.Flink
;
4055 while (le
!= &pdo_list
) {
4056 pdo_device_extension
* pdode
= CONTAINING_RECORD(le
, pdo_device_extension
, list_entry
);
4058 if (pdode
->pdo
== pdo
) {
4066 ExReleaseResourceLite(&pdo_list_lock
);
4068 if (!vde
|| vde
->type
!= VCB_TYPE_VOLUME
) {
4070 Status
= STATUS_UNRECOGNIZED_VOLUME
;
4078 ExAcquireResourceExclusiveLite(&pdode
->child_lock
, TRUE
);
4080 le
= pdode
->children
.Flink
;
4081 while (le
!= &pdode
->children
) {
4082 LIST_ENTRY
* le2
= le
->Flink
;
4084 vc
= CONTAINING_RECORD(pdode
->children
.Flink
, volume_child
, list_entry
);
4086 if (!still_has_superblock(vc
->devobj
)) {
4087 remove_volume_child(vde
, vc
, FALSE
);
4089 if (pdode
->num_children
== 0) {
4090 ERR("error - number of devices is zero\n");
4091 Status
= STATUS_INTERNAL_ERROR
;
4095 Status
= STATUS_DEVICE_NOT_READY
;
4102 if (pdode
->num_children
== 0 || pdode
->children_loaded
== 0) {
4103 ERR("error - number of devices is zero\n");
4104 Status
= STATUS_INTERNAL_ERROR
;
4108 ExConvertExclusiveToSharedLite(&pdode
->child_lock
);
4110 vc
= CONTAINING_RECORD(pdode
->children
.Flink
, volume_child
, list_entry
);
4112 readobj
= vc
->devobj
;
4113 readobjsize
= vc
->size
;
4115 vde
->device
->Characteristics
&= ~FILE_DEVICE_SECURE_OPEN
;
4117 GET_LENGTH_INFORMATION gli
;
4120 readobj
= DeviceToMount
;
4122 Status
= dev_ioctl(readobj
, IOCTL_DISK_GET_LENGTH_INFO
, NULL
, 0,
4123 &gli
, sizeof(gli
), TRUE
, NULL
);
4125 if (!NT_SUCCESS(Status
)) {
4126 ERR("error reading length information: %08x\n", Status
);
4130 readobjsize
= gli
.Length
.QuadPart
;
4133 Status
= IoCreateDevice(drvobj
, sizeof(device_extension
), NULL
, FILE_DEVICE_DISK_FILE_SYSTEM
, 0, FALSE
, &NewDeviceObject
);
4134 if (!NT_SUCCESS(Status
)) {
4135 ERR("IoCreateDevice returned %08x\n", Status
);
4136 Status
= STATUS_UNRECOGNIZED_VOLUME
;
4140 NewDeviceObject
->Flags
|= DO_DIRECT_IO
;
4142 // Some programs seem to expect that the sector size will be 512, for
4143 // FILE_NO_INTERMEDIATE_BUFFERING and the like.
4144 NewDeviceObject
->SectorSize
= min(DeviceToMount
->SectorSize
, 512);
4146 Vcb
= (PVOID
)NewDeviceObject
->DeviceExtension
;
4147 RtlZeroMemory(Vcb
, sizeof(device_extension
));
4148 Vcb
->type
= VCB_TYPE_FS
;
4151 ExInitializeResourceLite(&Vcb
->tree_lock
);
4152 Vcb
->need_write
= FALSE
;
4154 ExInitializeResourceLite(&Vcb
->fcb_lock
);
4155 ExInitializeResourceLite(&Vcb
->fileref_lock
);
4156 ExInitializeResourceLite(&Vcb
->chunk_lock
);
4157 ExInitializeResourceLite(&Vcb
->dirty_fcbs_lock
);
4158 ExInitializeResourceLite(&Vcb
->dirty_filerefs_lock
);
4159 ExInitializeResourceLite(&Vcb
->dirty_subvols_lock
);
4160 ExInitializeResourceLite(&Vcb
->scrub
.stats_lock
);
4162 ExInitializeResourceLite(&Vcb
->load_lock
);
4163 ExAcquireResourceExclusiveLite(&Vcb
->load_lock
, TRUE
);
4165 ExAcquireResourceExclusiveLite(&Vcb
->tree_lock
, TRUE
);
4167 DeviceToMount
->Flags
|= DO_DIRECT_IO
;
4169 Status
= read_superblock(Vcb
, readobj
, readobjsize
);
4170 if (!NT_SUCCESS(Status
)) {
4171 if (!IoIsErrorUserInduced(Status
))
4172 Status
= STATUS_UNRECOGNIZED_VOLUME
;
4173 else if (Irp
->Tail
.Overlay
.Thread
)
4174 IoSetHardErrorOrVerifyDevice(Irp
, readobj
);
4179 if (!vde
&& Vcb
->superblock
.num_devices
> 1) {
4180 ERR("cannot mount multi-device FS with non-PNP device\n");
4181 Status
= STATUS_UNRECOGNIZED_VOLUME
;
4185 Status
= registry_load_volume_options(Vcb
);
4186 if (!NT_SUCCESS(Status
)) {
4187 ERR("registry_load_volume_options returned %08x\n", Status
);
4191 if (pdode
&& pdode
->children_loaded
< pdode
->num_children
&& (!Vcb
->options
.allow_degraded
|| !finished_probing
|| degraded_wait
)) {
4192 ERR("could not mount as %u device(s) missing\n", pdode
->num_children
- pdode
->children_loaded
);
4193 Status
= STATUS_DEVICE_NOT_READY
;
4197 if (Vcb
->options
.ignore
) {
4198 TRACE("ignoring volume\n");
4199 Status
= STATUS_UNRECOGNIZED_VOLUME
;
4203 if (Vcb
->superblock
.incompat_flags
& ~INCOMPAT_SUPPORTED
) {
4204 WARN("cannot mount because of unsupported incompat flags (%llx)\n", Vcb
->superblock
.incompat_flags
& ~INCOMPAT_SUPPORTED
);
4205 Status
= STATUS_UNRECOGNIZED_VOLUME
;
4209 Vcb
->readonly
= FALSE
;
4210 if (Vcb
->superblock
.compat_ro_flags
& ~COMPAT_RO_SUPPORTED
) {
4211 WARN("mounting read-only because of unsupported flags (%llx)\n", Vcb
->superblock
.compat_ro_flags
& ~COMPAT_RO_SUPPORTED
);
4212 Vcb
->readonly
= TRUE
;
4215 if (Vcb
->options
.readonly
)
4216 Vcb
->readonly
= TRUE
;
4218 Vcb
->superblock
.generation
++;
4219 Vcb
->superblock
.incompat_flags
|= BTRFS_INCOMPAT_FLAGS_MIXED_BACKREF
;
4221 InitializeListHead(&Vcb
->devices
);
4222 dev
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(device
), ALLOC_TAG
);
4224 ERR("out of memory\n");
4225 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4229 dev
->devobj
= readobj
;
4230 RtlCopyMemory(&dev
->devitem
, &Vcb
->superblock
.dev_item
, sizeof(DEV_ITEM
));
4232 if (dev
->devitem
.num_bytes
> readobjsize
) {
4233 WARN("device %llx: DEV_ITEM says %llx bytes, but Windows only reports %llx\n", dev
->devitem
.dev_id
,
4234 dev
->devitem
.num_bytes
, readobjsize
);
4236 dev
->devitem
.num_bytes
= readobjsize
;
4239 dev
->seeding
= Vcb
->superblock
.flags
& BTRFS_SUPERBLOCK_FLAGS_SEEDING
? TRUE
: FALSE
;
4241 init_device(Vcb
, dev
, TRUE
);
4243 InsertTailList(&Vcb
->devices
, &dev
->list_entry
);
4244 Vcb
->devices_loaded
= 1;
4246 if (DeviceToMount
->Flags
& DO_SYSTEM_BOOT_PARTITION
)
4247 Vcb
->disallow_dismount
= TRUE
;
4249 TRACE("DeviceToMount = %p\n", DeviceToMount
);
4250 TRACE("IrpSp->Parameters.MountVolume.Vpb = %p\n", IrpSp
->Parameters
.MountVolume
.Vpb
);
4252 NewDeviceObject
->StackSize
= DeviceToMount
->StackSize
+ 1;
4253 NewDeviceObject
->Flags
&= ~DO_DEVICE_INITIALIZING
;
4255 InitializeListHead(&Vcb
->roots
);
4256 InitializeListHead(&Vcb
->drop_roots
);
4258 Vcb
->log_to_phys_loaded
= FALSE
;
4260 add_root(Vcb
, BTRFS_ROOT_CHUNK
, Vcb
->superblock
.chunk_tree_addr
, Vcb
->superblock
.chunk_root_generation
, NULL
);
4262 if (!Vcb
->chunk_root
) {
4263 ERR("Could not load chunk root.\n");
4264 Status
= STATUS_INTERNAL_ERROR
;
4268 InitializeListHead(&Vcb
->sys_chunks
);
4269 Status
= load_sys_chunks(Vcb
);
4270 if (!NT_SUCCESS(Status
)) {
4271 ERR("load_sys_chunks returned %08x\n", Status
);
4275 InitializeListHead(&Vcb
->chunks
);
4276 InitializeListHead(&Vcb
->trees
);
4277 InitializeListHead(&Vcb
->trees_hash
);
4278 InitializeListHead(&Vcb
->all_fcbs
);
4279 InitializeListHead(&Vcb
->dirty_fcbs
);
4280 InitializeListHead(&Vcb
->dirty_filerefs
);
4281 InitializeListHead(&Vcb
->dirty_subvols
);
4282 InitializeListHead(&Vcb
->send_ops
);
4284 ExInitializeFastMutex(&Vcb
->trees_list_mutex
);
4286 InitializeListHead(&Vcb
->DirNotifyList
);
4287 InitializeListHead(&Vcb
->scrub
.errors
);
4289 FsRtlNotifyInitializeSync(&Vcb
->NotifySync
);
4291 ExInitializePagedLookasideList(&Vcb
->tree_data_lookaside
, NULL
, NULL
, 0, sizeof(tree_data
), ALLOC_TAG
, 0);
4292 ExInitializePagedLookasideList(&Vcb
->traverse_ptr_lookaside
, NULL
, NULL
, 0, sizeof(traverse_ptr
), ALLOC_TAG
, 0);
4293 ExInitializePagedLookasideList(&Vcb
->batch_item_lookaside
, NULL
, NULL
, 0, sizeof(batch_item
), ALLOC_TAG
, 0);
4294 ExInitializePagedLookasideList(&Vcb
->fileref_lookaside
, NULL
, NULL
, 0, sizeof(file_ref
), ALLOC_TAG
, 0);
4295 ExInitializePagedLookasideList(&Vcb
->fcb_lookaside
, NULL
, NULL
, 0, sizeof(fcb
), ALLOC_TAG
, 0);
4296 ExInitializePagedLookasideList(&Vcb
->name_bit_lookaside
, NULL
, NULL
, 0, sizeof(name_bit
), ALLOC_TAG
, 0);
4297 ExInitializeNPagedLookasideList(&Vcb
->range_lock_lookaside
, NULL
, NULL
, 0, sizeof(range_lock
), ALLOC_TAG
, 0);
4298 ExInitializeNPagedLookasideList(&Vcb
->fileref_np_lookaside
, NULL
, NULL
, 0, sizeof(file_ref_nonpaged
), ALLOC_TAG
, 0);
4299 ExInitializeNPagedLookasideList(&Vcb
->fcb_np_lookaside
, NULL
, NULL
, 0, sizeof(fcb_nonpaged
), ALLOC_TAG
, 0);
4300 init_lookaside
= TRUE
;
4302 Vcb
->Vpb
= IrpSp
->Parameters
.MountVolume
.Vpb
;
4304 Status
= load_chunk_root(Vcb
, Irp
);
4305 if (!NT_SUCCESS(Status
)) {
4306 ERR("load_chunk_root returned %08x\n", Status
);
4310 if (Vcb
->superblock
.num_devices
> 1) {
4311 if (Vcb
->devices_loaded
< Vcb
->superblock
.num_devices
&& (!Vcb
->options
.allow_degraded
|| !finished_probing
)) {
4312 ERR("could not mount as %u device(s) missing\n", Vcb
->superblock
.num_devices
- Vcb
->devices_loaded
);
4314 IoRaiseInformationalHardError(IO_ERR_INTERNAL_ERROR
, NULL
, NULL
);
4316 Status
= STATUS_INTERNAL_ERROR
;
4320 if (dev
->readonly
&& !Vcb
->readonly
) {
4321 Vcb
->readonly
= TRUE
;
4323 le
= Vcb
->devices
.Flink
;
4324 while (le
!= &Vcb
->devices
) {
4325 device
* dev2
= CONTAINING_RECORD(le
, device
, list_entry
);
4327 if (dev2
->readonly
&& !dev2
->seeding
)
4330 if (!dev2
->readonly
) {
4331 Vcb
->readonly
= FALSE
;
4339 WARN("setting volume to readonly\n");
4342 if (dev
->readonly
) {
4343 WARN("setting volume to readonly as device is readonly\n");
4344 Vcb
->readonly
= TRUE
;
4348 add_root(Vcb
, BTRFS_ROOT_ROOT
, Vcb
->superblock
.root_tree_addr
, Vcb
->superblock
.generation
- 1, NULL
);
4350 if (!Vcb
->root_root
) {
4351 ERR("Could not load root of roots.\n");
4352 Status
= STATUS_INTERNAL_ERROR
;
4356 Status
= look_for_roots(Vcb
, Irp
);
4357 if (!NT_SUCCESS(Status
)) {
4358 ERR("look_for_roots returned %08x\n", Status
);
4362 if (!Vcb
->readonly
) {
4363 Status
= find_chunk_usage(Vcb
, Irp
);
4364 if (!NT_SUCCESS(Status
)) {
4365 ERR("find_chunk_usage returned %08x\n", Status
);
4370 InitializeListHead(&batchlist
);
4372 // We've already increased the generation by one
4373 if (!Vcb
->readonly
&& (
4374 Vcb
->options
.clear_cache
||
4375 (!(Vcb
->superblock
.compat_ro_flags
& BTRFS_COMPAT_RO_FLAGS_FREE_SPACE_CACHE
) && Vcb
->superblock
.generation
- 1 != Vcb
->superblock
.cache_generation
) ||
4376 (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
)))) {
4377 if (Vcb
->options
.clear_cache
)
4378 WARN("ClearCache option was set, clearing cache...\n");
4379 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
))
4380 WARN("clearing free-space tree created by buggy Linux driver\n");
4382 WARN("generation was %llx, free-space cache generation was %llx; clearing cache...\n", Vcb
->superblock
.generation
- 1, Vcb
->superblock
.cache_generation
);
4384 Status
= clear_free_space_cache(Vcb
, &batchlist
, Irp
);
4385 if (!NT_SUCCESS(Status
)) {
4386 ERR("clear_free_space_cache returned %08x\n", Status
);
4387 clear_batch_list(Vcb
, &batchlist
);
4392 Status
= commit_batch_list(Vcb
, &batchlist
, Irp
);
4393 if (!NT_SUCCESS(Status
)) {
4394 ERR("commit_batch_list returned %08x\n", Status
);
4398 Vcb
->volume_fcb
= create_fcb(Vcb
, NonPagedPool
);
4399 if (!Vcb
->volume_fcb
) {
4400 ERR("out of memory\n");
4401 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4405 Vcb
->volume_fcb
->Vcb
= Vcb
;
4406 Vcb
->volume_fcb
->sd
= NULL
;
4408 Vcb
->dummy_fcb
= create_fcb(Vcb
, NonPagedPool
);
4409 if (!Vcb
->dummy_fcb
) {
4410 ERR("out of memory\n");
4411 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4415 Vcb
->dummy_fcb
->Vcb
= Vcb
;
4416 Vcb
->dummy_fcb
->type
= BTRFS_TYPE_DIRECTORY
;
4417 Vcb
->dummy_fcb
->inode
= 2;
4418 Vcb
->dummy_fcb
->subvol
= Vcb
->root_root
;
4419 Vcb
->dummy_fcb
->atts
= FILE_ATTRIBUTE_DIRECTORY
;
4420 Vcb
->dummy_fcb
->inode_item
.st_nlink
= 1;
4421 Vcb
->dummy_fcb
->inode_item
.st_mode
= __S_IFDIR
;
4423 Vcb
->dummy_fcb
->hash_ptrs
= ExAllocatePoolWithTag(PagedPool
, sizeof(LIST_ENTRY
*) * 256, ALLOC_TAG
);
4424 if (!Vcb
->dummy_fcb
->hash_ptrs
) {
4425 ERR("out of memory\n");
4426 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4430 RtlZeroMemory(Vcb
->dummy_fcb
->hash_ptrs
, sizeof(LIST_ENTRY
*) * 256);
4432 Vcb
->dummy_fcb
->hash_ptrs_uc
= ExAllocatePoolWithTag(PagedPool
, sizeof(LIST_ENTRY
*) * 256, ALLOC_TAG
);
4433 if (!Vcb
->dummy_fcb
->hash_ptrs_uc
) {
4434 ERR("out of memory\n");
4435 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4439 RtlZeroMemory(Vcb
->dummy_fcb
->hash_ptrs_uc
, sizeof(LIST_ENTRY
*) * 256);
4441 root_fcb
= create_fcb(Vcb
, NonPagedPool
);
4443 ERR("out of memory\n");
4444 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4448 root_fcb
->Vcb
= Vcb
;
4449 root_fcb
->inode
= SUBVOL_ROOT_INODE
;
4450 root_fcb
->hash
= calc_crc32c(0xffffffff, (UINT8
*)&root_fcb
->inode
, sizeof(UINT64
));
4451 root_fcb
->type
= BTRFS_TYPE_DIRECTORY
;
4453 #ifdef DEBUG_FCB_REFCOUNTS
4454 WARN("volume FCB = %p\n", Vcb
->volume_fcb
);
4455 WARN("root FCB = %p\n", root_fcb
);
4458 root_fcb
->subvol
= find_default_subvol(Vcb
, Irp
);
4460 if (!root_fcb
->subvol
) {
4461 ERR("could not find top subvol\n");
4462 Status
= STATUS_INTERNAL_ERROR
;
4466 Status
= load_dir_children(Vcb
, root_fcb
, TRUE
, Irp
);
4467 if (!NT_SUCCESS(Status
)) {
4468 ERR("load_dir_children returned %08x\n", Status
);
4472 searchkey
.obj_id
= root_fcb
->inode
;
4473 searchkey
.obj_type
= TYPE_INODE_ITEM
;
4474 searchkey
.offset
= 0xffffffffffffffff;
4476 Status
= find_item(Vcb
, root_fcb
->subvol
, &tp
, &searchkey
, FALSE
, Irp
);
4477 if (!NT_SUCCESS(Status
)) {
4478 ERR("error - find_item returned %08x\n", Status
);
4482 if (tp
.item
->key
.obj_id
!= searchkey
.obj_id
|| tp
.item
->key
.obj_type
!= searchkey
.obj_type
) {
4483 ERR("couldn't find INODE_ITEM for root directory\n");
4484 Status
= STATUS_INTERNAL_ERROR
;
4488 if (tp
.item
->size
> 0)
4489 RtlCopyMemory(&root_fcb
->inode_item
, tp
.item
->data
, min(sizeof(INODE_ITEM
), tp
.item
->size
));
4491 fcb_get_sd(root_fcb
, NULL
, TRUE
, Irp
);
4493 root_fcb
->atts
= get_file_attributes(Vcb
, root_fcb
->subvol
, root_fcb
->inode
, root_fcb
->type
, FALSE
, FALSE
, Irp
);
4495 Vcb
->root_fileref
= create_fileref(Vcb
);
4496 if (!Vcb
->root_fileref
) {
4497 ERR("out of memory\n");
4498 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4502 Vcb
->root_fileref
->fcb
= root_fcb
;
4503 InsertTailList(&root_fcb
->subvol
->fcbs
, &root_fcb
->list_entry
);
4504 InsertTailList(&Vcb
->all_fcbs
, &root_fcb
->list_entry_all
);
4506 root_fcb
->subvol
->fcbs_ptrs
[root_fcb
->hash
>> 24] = &root_fcb
->list_entry
;
4508 root_fcb
->fileref
= Vcb
->root_fileref
;
4510 root_ccb
= ExAllocatePoolWithTag(PagedPool
, sizeof(ccb
), ALLOC_TAG
);
4512 ERR("out of memory\n");
4513 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4517 /* HACK: stream file object seems to get deleted at some point
4518 * leading to use after free when installing ReactOS on
4520 * Workaround: leak a handle to the fileobject
4521 * XXX: Could be improved by storing it somewhere and releasing it
4522 * on dismount. Or even by referencing again the file object.
4525 Vcb
->root_file
= IoCreateStreamFileObject(NULL
, DeviceToMount
);
4529 Vcb
->root_file
= IoCreateStreamFileObjectEx(NULL
, DeviceToMount
, &Dummy
);
4532 Vcb
->root_file
->FsContext
= root_fcb
;
4533 Vcb
->root_file
->SectionObjectPointer
= &root_fcb
->nonpaged
->segment_object
;
4534 Vcb
->root_file
->Vpb
= DeviceObject
->Vpb
;
4536 RtlZeroMemory(root_ccb
, sizeof(ccb
));
4537 root_ccb
->NodeType
= BTRFS_NODE_TYPE_CCB
;
4538 root_ccb
->NodeSize
= sizeof(ccb
);
4540 Vcb
->root_file
->FsContext2
= root_ccb
;
4543 CcInitializeCacheMap(Vcb
->root_file
, (PCC_FILE_SIZES
)(&root_fcb
->Header
.AllocationSize
), FALSE
, cache_callbacks
, Vcb
->root_file
);
4544 } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER
) {
4545 Status
= _SEH2_GetExceptionCode();
4549 le
= Vcb
->devices
.Flink
;
4550 while (le
!= &Vcb
->devices
) {
4551 device
* dev2
= CONTAINING_RECORD(le
, device
, list_entry
);
4553 Status
= find_disk_holes(Vcb
, dev2
, Irp
);
4554 if (!NT_SUCCESS(Status
)) {
4555 ERR("find_disk_holes returned %08x\n", Status
);
4562 NewDeviceObject
->Vpb
= IrpSp
->Parameters
.MountVolume
.Vpb
;
4563 IrpSp
->Parameters
.MountVolume
.Vpb
->DeviceObject
= NewDeviceObject
;
4564 IrpSp
->Parameters
.MountVolume
.Vpb
->Flags
|= VPB_MOUNTED
;
4565 NewDeviceObject
->Vpb
->VolumeLabelLength
= 4; // FIXME
4566 NewDeviceObject
->Vpb
->VolumeLabel
[0] = '?';
4567 NewDeviceObject
->Vpb
->VolumeLabel
[1] = 0;
4568 NewDeviceObject
->Vpb
->ReferenceCount
++; // FIXME - should we deref this at any point?
4570 KeInitializeEvent(&Vcb
->flush_thread_finished
, NotificationEvent
, FALSE
);
4572 Status
= PsCreateSystemThread(&Vcb
->flush_thread_handle
, 0, NULL
, NULL
, NULL
, flush_thread
, NewDeviceObject
);
4573 if (!NT_SUCCESS(Status
)) {
4574 ERR("PsCreateSystemThread returned %08x\n", Status
);
4578 Status
= create_calc_threads(NewDeviceObject
);
4579 if (!NT_SUCCESS(Status
)) {
4580 ERR("create_calc_threads returned %08x\n", Status
);
4584 Status
= registry_mark_volume_mounted(&Vcb
->superblock
.uuid
);
4585 if (!NT_SUCCESS(Status
))
4586 WARN("registry_mark_volume_mounted returned %08x\n", Status
);
4588 Status
= look_for_balance_item(Vcb
);
4589 if (!NT_SUCCESS(Status
) && Status
!= STATUS_NOT_FOUND
)
4590 WARN("look_for_balance_item returned %08x\n", Status
);
4592 Status
= STATUS_SUCCESS
;
4595 vde
->mounted_device
= NewDeviceObject
;
4597 ExInitializeResourceLite(&Vcb
->send_load_lock
);
4601 ExReleaseResourceLite(&pdode
->child_lock
);
4605 ExReleaseResourceLite(&Vcb
->tree_lock
);
4606 ExReleaseResourceLite(&Vcb
->load_lock
);
4609 if (!NT_SUCCESS(Status
)) {
4611 if (init_lookaside
) {
4612 ExDeletePagedLookasideList(&Vcb
->tree_data_lookaside
);
4613 ExDeletePagedLookasideList(&Vcb
->traverse_ptr_lookaside
);
4614 ExDeletePagedLookasideList(&Vcb
->batch_item_lookaside
);
4615 ExDeletePagedLookasideList(&Vcb
->fileref_lookaside
);
4616 ExDeletePagedLookasideList(&Vcb
->fcb_lookaside
);
4617 ExDeletePagedLookasideList(&Vcb
->name_bit_lookaside
);
4618 ExDeleteNPagedLookasideList(&Vcb
->range_lock_lookaside
);
4619 ExDeleteNPagedLookasideList(&Vcb
->fileref_np_lookaside
);
4620 ExDeleteNPagedLookasideList(&Vcb
->fcb_np_lookaside
);
4624 ObDereferenceObject(Vcb
->root_file
);
4625 else if (Vcb
->root_fileref
)
4626 free_fileref(Vcb
->root_fileref
);
4630 if (root_fcb
&& root_fcb
->refcount
== 0)
4633 if (Vcb
->volume_fcb
)
4634 reap_fcb(Vcb
->volume_fcb
);
4636 ExDeleteResourceLite(&Vcb
->tree_lock
);
4637 ExDeleteResourceLite(&Vcb
->load_lock
);
4638 ExDeleteResourceLite(&Vcb
->fcb_lock
);
4639 ExDeleteResourceLite(&Vcb
->fileref_lock
);
4640 ExDeleteResourceLite(&Vcb
->chunk_lock
);
4641 ExDeleteResourceLite(&Vcb
->dirty_fcbs_lock
);
4642 ExDeleteResourceLite(&Vcb
->dirty_filerefs_lock
);
4643 ExDeleteResourceLite(&Vcb
->dirty_subvols_lock
);
4644 ExDeleteResourceLite(&Vcb
->scrub
.stats_lock
);
4646 if (Vcb
->devices
.Flink
) {
4647 while (!IsListEmpty(&Vcb
->devices
)) {
4648 device
* dev2
= CONTAINING_RECORD(RemoveHeadList(&Vcb
->devices
), device
, list_entry
);
4655 if (NewDeviceObject
)
4656 IoDeleteDevice(NewDeviceObject
);
4658 ExAcquireResourceExclusiveLite(&global_loading_lock
, TRUE
);
4659 InsertTailList(&VcbList
, &Vcb
->list_entry
);
4660 ExReleaseResourceLite(&global_loading_lock
);
4662 FsRtlNotifyVolumeEvent(Vcb
->root_file
, FSRTL_VOLUME_MOUNT
);
4665 TRACE("mount_vol done (status: %lx)\n", Status
);
4670 static NTSTATUS
verify_device(_In_ device_extension
* Vcb
, _Inout_ device
* dev
) {
4677 return STATUS_WRONG_VOLUME
;
4679 if (dev
->removable
) {
4680 IO_STATUS_BLOCK iosb
;
4682 Status
= dev_ioctl(dev
->devobj
, IOCTL_STORAGE_CHECK_VERIFY
, NULL
, 0, &cc
, sizeof(ULONG
), TRUE
, &iosb
);
4684 if (IoIsErrorUserInduced(Status
)) {
4685 ERR("IOCTL_STORAGE_CHECK_VERIFY returned %08x (user-induced)\n", Status
);
4688 pdo_device_extension
* pdode
= Vcb
->vde
->pdode
;
4690 BOOL changed
= FALSE
;
4692 ExAcquireResourceExclusiveLite(&pdode
->child_lock
, TRUE
);
4694 le2
= pdode
->children
.Flink
;
4695 while (le2
!= &pdode
->children
) {
4696 volume_child
* vc
= CONTAINING_RECORD(le2
, volume_child
, list_entry
);
4698 if (vc
->devobj
== dev
->devobj
) {
4699 TRACE("removing device\n");
4701 remove_volume_child(Vcb
->vde
, vc
, TRUE
);
4711 ExReleaseResourceLite(&pdode
->child_lock
);
4713 } else if (!NT_SUCCESS(Status
)) {
4714 ERR("IOCTL_STORAGE_CHECK_VERIFY returned %08x\n", Status
);
4716 } else if (iosb
.Information
< sizeof(ULONG
)) {
4717 ERR("iosb.Information was too short\n");
4718 return STATUS_INTERNAL_ERROR
;
4721 dev
->change_count
= cc
;
4724 to_read
= dev
->devobj
->SectorSize
== 0 ? sizeof(superblock
) : (ULONG
)sector_align(sizeof(superblock
), dev
->devobj
->SectorSize
);
4726 sb
= ExAllocatePoolWithTag(NonPagedPool
, to_read
, ALLOC_TAG
);
4728 ERR("out of memory\n");
4729 return STATUS_INSUFFICIENT_RESOURCES
;
4732 Status
= sync_read_phys(dev
->devobj
, superblock_addrs
[0], to_read
, (PUCHAR
)sb
, TRUE
);
4733 if (!NT_SUCCESS(Status
)) {
4734 ERR("Failed to read superblock: %08x\n", Status
);
4739 if (sb
->magic
!= BTRFS_MAGIC
) {
4740 ERR("not a BTRFS volume\n");
4742 return STATUS_WRONG_VOLUME
;
4745 crc32
= ~calc_crc32c(0xffffffff, (UINT8
*)&sb
->uuid
, (ULONG
)sizeof(superblock
) - sizeof(sb
->checksum
));
4746 TRACE("crc32 was %08x, expected %08x\n", crc32
, *((UINT32
*)sb
->checksum
));
4748 if (crc32
!= *((UINT32
*)sb
->checksum
)) {
4749 ERR("checksum error\n");
4751 return STATUS_WRONG_VOLUME
;
4754 if (RtlCompareMemory(&sb
->uuid
, &Vcb
->superblock
.uuid
, sizeof(BTRFS_UUID
)) != sizeof(BTRFS_UUID
)) {
4755 ERR("different UUIDs\n");
4757 return STATUS_WRONG_VOLUME
;
4762 dev
->devobj
->Flags
&= ~DO_VERIFY_VOLUME
;
4764 return STATUS_SUCCESS
;
4767 static NTSTATUS
verify_volume(_In_ PDEVICE_OBJECT devobj
) {
4768 device_extension
* Vcb
= devobj
->DeviceExtension
;
4771 UINT64 failed_devices
= 0;
4772 BOOL locked
= FALSE
, remove
= FALSE
;
4774 if (!(Vcb
->Vpb
->Flags
& VPB_MOUNTED
))
4775 return STATUS_WRONG_VOLUME
;
4777 if (!ExIsResourceAcquiredExclusive(&Vcb
->tree_lock
)) {
4778 ExAcquireResourceExclusiveLite(&Vcb
->tree_lock
, TRUE
);
4782 if (Vcb
->removing
) {
4783 if (locked
) ExReleaseResourceLite(&Vcb
->tree_lock
);
4784 return STATUS_WRONG_VOLUME
;
4787 InterlockedIncrement(&Vcb
->open_files
); // so pnp_surprise_removal doesn't uninit the device while we're still using it
4789 le
= Vcb
->devices
.Flink
;
4790 while (le
!= &Vcb
->devices
) {
4791 device
* dev
= CONTAINING_RECORD(le
, device
, list_entry
);
4793 Status
= verify_device(Vcb
, dev
);
4794 if (!NT_SUCCESS(Status
)) {
4797 if (dev
->devobj
&& Vcb
->options
.allow_degraded
)
4804 InterlockedDecrement(&Vcb
->open_files
);
4806 if (Vcb
->removing
&& Vcb
->open_files
== 0)
4810 ExReleaseResourceLite(&Vcb
->tree_lock
);
4817 if (failed_devices
== 0 || (Vcb
->options
.allow_degraded
&& failed_devices
< Vcb
->superblock
.num_devices
)) {
4818 Vcb
->Vpb
->RealDevice
->Flags
&= ~DO_VERIFY_VOLUME
;
4820 return STATUS_SUCCESS
;
4826 _Dispatch_type_(IRP_MJ_FILE_SYSTEM_CONTROL
)
4827 _Function_class_(DRIVER_DISPATCH
)
4829 static NTSTATUS NTAPI
drv_file_system_control(_In_ PDEVICE_OBJECT DeviceObject
, _In_ PIRP Irp
) {
4831 static NTSTATUS
drv_file_system_control(_In_ PDEVICE_OBJECT DeviceObject
, _In_ PIRP Irp
) {
4833 PIO_STACK_LOCATION IrpSp
;
4835 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
4838 FsRtlEnterFileSystem();
4840 TRACE("file system control\n");
4842 top_level
= is_top_level(Irp
);
4844 if (Vcb
&& Vcb
->type
== VCB_TYPE_VOLUME
) {
4845 Status
= vol_file_system_control(DeviceObject
, Irp
);
4847 } else if (!Vcb
|| (Vcb
->type
!= VCB_TYPE_FS
&& Vcb
->type
!= VCB_TYPE_CONTROL
)) {
4848 Status
= STATUS_INVALID_PARAMETER
;
4852 Status
= STATUS_NOT_IMPLEMENTED
;
4854 IrpSp
= IoGetCurrentIrpStackLocation( Irp
);
4856 Irp
->IoStatus
.Information
= 0;
4858 switch (IrpSp
->MinorFunction
) {
4859 case IRP_MN_MOUNT_VOLUME
:
4860 TRACE("IRP_MN_MOUNT_VOLUME\n");
4862 Status
= mount_vol(DeviceObject
, Irp
);
4865 case IRP_MN_KERNEL_CALL
:
4866 TRACE("IRP_MN_KERNEL_CALL\n");
4868 Status
= fsctl_request(DeviceObject
, &Irp
, IrpSp
->Parameters
.FileSystemControl
.FsControlCode
);
4871 case IRP_MN_USER_FS_REQUEST
:
4872 TRACE("IRP_MN_USER_FS_REQUEST\n");
4874 Status
= fsctl_request(DeviceObject
, &Irp
, IrpSp
->Parameters
.FileSystemControl
.FsControlCode
);
4877 case IRP_MN_VERIFY_VOLUME
:
4878 TRACE("IRP_MN_VERIFY_VOLUME\n");
4880 Status
= verify_volume(DeviceObject
);
4882 if (!NT_SUCCESS(Status
) && Vcb
->Vpb
->Flags
& VPB_MOUNTED
) {
4883 ExAcquireResourceExclusiveLite(&Vcb
->tree_lock
, TRUE
);
4884 Vcb
->removing
= TRUE
;
4885 ExReleaseResourceLite(&Vcb
->tree_lock
);
4895 TRACE("returning %08x\n", Status
);
4898 Irp
->IoStatus
.Status
= Status
;
4900 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
4904 IoSetTopLevelIrp(NULL
);
4906 FsRtlExitFileSystem();
4911 _Dispatch_type_(IRP_MJ_LOCK_CONTROL
)
4912 _Function_class_(DRIVER_DISPATCH
)
4914 static NTSTATUS NTAPI
drv_lock_control(_In_ PDEVICE_OBJECT DeviceObject
, _In_ PIRP Irp
) {
4916 static NTSTATUS
drv_lock_control(_In_ PDEVICE_OBJECT DeviceObject
, _In_ PIRP Irp
) {
4919 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
4920 fcb
* fcb
= IrpSp
->FileObject
->FsContext
;
4921 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
4924 FsRtlEnterFileSystem();
4926 top_level
= is_top_level(Irp
);
4928 if (Vcb
&& Vcb
->type
== VCB_TYPE_VOLUME
) {
4929 Status
= vol_lock_control(DeviceObject
, Irp
);
4931 Irp
->IoStatus
.Status
= Status
;
4932 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
4937 TRACE("lock control\n");
4939 Status
= FsRtlProcessFileLock(&fcb
->lock
, Irp
, NULL
);
4941 fcb
->Header
.IsFastIoPossible
= fast_io_possible(fcb
);
4944 TRACE("returning %08x\n", Status
);
4947 IoSetTopLevelIrp(NULL
);
4949 FsRtlExitFileSystem();
4954 _Dispatch_type_(IRP_MJ_SHUTDOWN
)
4955 _Function_class_(DRIVER_DISPATCH
)
4957 static NTSTATUS NTAPI
drv_shutdown(_In_ PDEVICE_OBJECT DeviceObject
, _In_ PIRP Irp
) {
4959 static NTSTATUS
drv_shutdown(_In_ PDEVICE_OBJECT DeviceObject
, _In_ PIRP Irp
) {
4963 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
4966 FsRtlEnterFileSystem();
4968 TRACE("shutdown\n");
4970 top_level
= is_top_level(Irp
);
4972 if (Vcb
&& Vcb
->type
== VCB_TYPE_VOLUME
) {
4973 Status
= vol_shutdown(DeviceObject
, Irp
);
4977 Status
= STATUS_SUCCESS
;
4979 shutting_down
= TRUE
;
4980 KeSetEvent(&mountmgr_thread_event
, 0, FALSE
);
4983 while (le
!= &VcbList
) {
4985 LIST_ENTRY
* le2
= le
->Flink
;
4987 Vcb
= CONTAINING_RECORD(le
, device_extension
, list_entry
);
4989 TRACE("shutting down Vcb %p\n", Vcb
);
4991 ExAcquireResourceExclusiveLite(&Vcb
->tree_lock
, TRUE
);
4992 Vcb
->removing
= TRUE
;
4993 open_files
= Vcb
->open_files
> 0;
4995 if (Vcb
->need_write
&& !Vcb
->readonly
) {
4996 Status
= do_write(Vcb
, Irp
);
4997 if (!NT_SUCCESS(Status
))
4998 ERR("do_write returned %08x\n", Status
);
5003 ExReleaseResourceLite(&Vcb
->tree_lock
);
5013 ObDereferenceObject(comfo
);
5020 Irp
->IoStatus
.Status
= Status
;
5021 Irp
->IoStatus
.Information
= 0;
5023 IoCompleteRequest( Irp
, IO_NO_INCREMENT
);
5026 IoSetTopLevelIrp(NULL
);
5028 FsRtlExitFileSystem();
5033 _Dispatch_type_(IRP_MJ_POWER
)
5034 _Function_class_(DRIVER_DISPATCH
)
5036 static NTSTATUS NTAPI
drv_power(_In_ PDEVICE_OBJECT DeviceObject
, _In_ PIRP Irp
) {
5038 static NTSTATUS
drv_power(_In_ PDEVICE_OBJECT DeviceObject
, _In_ PIRP Irp
) {
5041 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
5044 FsRtlEnterFileSystem();
5046 top_level
= is_top_level(Irp
);
5048 Irp
->IoStatus
.Information
= 0;
5050 if (Vcb
&& Vcb
->type
== VCB_TYPE_VOLUME
) {
5051 Status
= vol_power(DeviceObject
, Irp
);
5053 Irp
->IoStatus
.Status
= Status
;
5054 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
5057 } else if (Vcb
&& Vcb
->type
== VCB_TYPE_FS
) {
5058 IoSkipCurrentIrpStackLocation(Irp
);
5060 Status
= IoCallDriver(Vcb
->Vpb
->RealDevice
, Irp
);
5065 Status
= STATUS_INVALID_DEVICE_REQUEST
;
5066 Irp
->IoStatus
.Status
= Status
;
5067 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
5071 IoSetTopLevelIrp(NULL
);
5073 FsRtlExitFileSystem();
5078 _Dispatch_type_(IRP_MJ_SYSTEM_CONTROL
)
5079 _Function_class_(DRIVER_DISPATCH
)
5081 static NTSTATUS NTAPI
drv_system_control(_In_ PDEVICE_OBJECT DeviceObject
, _In_ PIRP Irp
) {
5083 static NTSTATUS
drv_system_control(_In_ PDEVICE_OBJECT DeviceObject
, _In_ PIRP Irp
) {
5086 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
5089 FsRtlEnterFileSystem();
5091 top_level
= is_top_level(Irp
);
5093 Irp
->IoStatus
.Information
= 0;
5095 if (Vcb
&& Vcb
->type
== VCB_TYPE_VOLUME
) {
5096 volume_device_extension
* vde
= DeviceObject
->DeviceExtension
;
5098 IoSkipCurrentIrpStackLocation(Irp
);
5100 Status
= IoCallDriver(vde
->pdo
, Irp
);
5103 } else if (Vcb
&& Vcb
->type
== VCB_TYPE_FS
) {
5104 IoSkipCurrentIrpStackLocation(Irp
);
5106 Status
= IoCallDriver(Vcb
->Vpb
->RealDevice
, Irp
);
5111 Status
= Irp
->IoStatus
.Status
;
5112 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
5116 IoSetTopLevelIrp(NULL
);
5118 FsRtlExitFileSystem();
5123 BOOL
is_file_name_valid(_In_ PUNICODE_STRING us
, _In_ BOOL posix
) {
5126 if (us
->Length
< sizeof(WCHAR
))
5129 if (us
->Length
> 255 * sizeof(WCHAR
))
5132 for (i
= 0; i
< us
->Length
/ sizeof(WCHAR
); i
++) {
5133 if (us
->Buffer
[i
] == '/' || us
->Buffer
[i
] == 0 ||
5134 (!posix
&& (us
->Buffer
[i
] == '<' || us
->Buffer
[i
] == '>' || us
->Buffer
[i
] == ':' || us
->Buffer
[i
] == '"' ||
5135 us
->Buffer
[i
] == '|' || us
->Buffer
[i
] == '?' || us
->Buffer
[i
] == '*' || (us
->Buffer
[i
] >= 1 && us
->Buffer
[i
] <= 31))))
5139 if (us
->Buffer
[0] == '.' && (us
->Length
== sizeof(WCHAR
) || (us
->Length
== 2 * sizeof(WCHAR
) && us
->Buffer
[1] == '.')))
5145 void chunk_lock_range(_In_ device_extension
* Vcb
, _In_ chunk
* c
, _In_ UINT64 start
, _In_ UINT64 length
) {
5150 rl
= ExAllocateFromNPagedLookasideList(&Vcb
->range_lock_lookaside
);
5152 ERR("out of memory\n");
5157 rl
->length
= length
;
5158 rl
->thread
= PsGetCurrentThread();
5163 ExAcquireResourceExclusiveLite(&c
->range_locks_lock
, TRUE
);
5165 le
= c
->range_locks
.Flink
;
5166 while (le
!= &c
->range_locks
) {
5167 range_lock
* rl2
= CONTAINING_RECORD(le
, range_lock
, list_entry
);
5169 if (rl2
->start
< start
+ length
&& rl2
->start
+ rl2
->length
> start
&& rl2
->thread
!= PsGetCurrentThread()) {
5178 InsertTailList(&c
->range_locks
, &rl
->list_entry
);
5180 ExReleaseResourceLite(&c
->range_locks_lock
);
5184 KeClearEvent(&c
->range_locks_event
);
5186 ExReleaseResourceLite(&c
->range_locks_lock
);
5188 KeWaitForSingleObject(&c
->range_locks_event
, UserRequest
, KernelMode
, FALSE
, NULL
);
5192 void chunk_unlock_range(_In_ device_extension
* Vcb
, _In_ chunk
* c
, _In_ UINT64 start
, _In_ UINT64 length
) {
5195 ExAcquireResourceExclusiveLite(&c
->range_locks_lock
, TRUE
);
5197 le
= c
->range_locks
.Flink
;
5198 while (le
!= &c
->range_locks
) {
5199 range_lock
* rl
= CONTAINING_RECORD(le
, range_lock
, list_entry
);
5201 if (rl
->start
== start
&& rl
->length
== length
) {
5202 RemoveEntryList(&rl
->list_entry
);
5203 ExFreeToNPagedLookasideList(&Vcb
->range_lock_lookaside
, rl
);
5210 KeSetEvent(&c
->range_locks_event
, 0, FALSE
);
5212 ExReleaseResourceLite(&c
->range_locks_lock
);
5215 void log_device_error(_In_ device_extension
* Vcb
, _Inout_ device
* dev
, _In_
int error
) {
5216 dev
->stats
[error
]++;
5217 dev
->stats_changed
= TRUE
;
5218 Vcb
->stats_changed
= TRUE
;
5222 _Function_class_(KSTART_ROUTINE
)
5223 static void serial_thread(void* context
) {
5224 LARGE_INTEGER due_time
;
5229 KeInitializeTimer(&timer
);
5231 due_time
.QuadPart
= (UINT64
)-10000000;
5233 KeSetTimer(&timer
, due_time
, NULL
);
5236 KeWaitForSingleObject(&timer
, Executive
, KernelMode
, FALSE
, NULL
);
5243 KeSetTimer(&timer
, due_time
, NULL
);
5246 KeCancelTimer(&timer
);
5248 PsTerminateSystemThread(STATUS_SUCCESS
);
5250 serial_thread_handle
= NULL
;
5253 static void init_serial(BOOL first_time
) {
5256 Status
= IoGetDeviceObjectPointer(&log_device
, FILE_WRITE_DATA
, &comfo
, &comdo
);
5257 if (!NT_SUCCESS(Status
)) {
5258 ERR("IoGetDeviceObjectPointer returned %08x\n", Status
);
5263 Status
= PsCreateSystemThread(&serial_thread_handle
, 0, NULL
, NULL
, NULL
, serial_thread
, NULL
);
5264 if (!NT_SUCCESS(Status
)) {
5265 ERR("PsCreateSystemThread returned %08x\n", Status
);
5274 static void check_cpu() {
5275 unsigned int cpuInfo
[4];
5277 __get_cpuid(1, &cpuInfo
[0], &cpuInfo
[1], &cpuInfo
[2], &cpuInfo
[3]);
5278 have_sse42
= cpuInfo
[2] & bit_SSE4_2
;
5279 have_sse2
= cpuInfo
[3] & bit_SSE2
;
5281 __cpuid(cpuInfo
, 1);
5282 have_sse42
= cpuInfo
[2] & (1 << 20);
5283 have_sse2
= cpuInfo
[3] & (1 << 26);
5287 TRACE("SSE4.2 is supported\n");
5289 TRACE("SSE4.2 not supported\n");
5292 TRACE("SSE2 is supported\n");
5294 TRACE("SSE2 is not supported\n");
5299 static void init_logging() {
5300 ExAcquireResourceExclusiveLite(&log_lock
, TRUE
);
5302 if (log_device
.Length
> 0)
5304 else if (log_file
.Length
> 0) {
5306 OBJECT_ATTRIBUTES oa
;
5307 IO_STATUS_BLOCK iosb
;
5312 InitializeObjectAttributes(&oa
, &log_file
, OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
, NULL
, NULL
);
5314 Status
= ZwCreateFile(&log_handle
, FILE_WRITE_DATA
, &oa
, &iosb
, NULL
, FILE_ATTRIBUTE_NORMAL
, FILE_SHARE_READ
,
5315 FILE_OPEN_IF
, FILE_NON_DIRECTORY_FILE
| FILE_WRITE_THROUGH
| FILE_SYNCHRONOUS_IO_ALERT
, NULL
, 0);
5317 if (!NT_SUCCESS(Status
)) {
5318 ERR("ZwCreateFile returned %08x\n", Status
);
5322 if (iosb
.Information
== FILE_OPENED
) { // already exists
5323 FILE_STANDARD_INFORMATION fsi
;
5324 FILE_POSITION_INFORMATION fpi
;
5326 static const char delim
[] = "\n---\n";
5328 // move to end of file
5330 Status
= ZwQueryInformationFile(log_handle
, &iosb
, &fsi
, sizeof(FILE_STANDARD_INFORMATION
), FileStandardInformation
);
5332 if (!NT_SUCCESS(Status
)) {
5333 ERR("ZwQueryInformationFile returned %08x\n", Status
);
5337 fpi
.CurrentByteOffset
= fsi
.EndOfFile
;
5339 Status
= ZwSetInformationFile(log_handle
, &iosb
, &fpi
, sizeof(FILE_POSITION_INFORMATION
), FilePositionInformation
);
5341 if (!NT_SUCCESS(Status
)) {
5342 ERR("ZwSetInformationFile returned %08x\n", Status
);
5346 Status
= ZwWriteFile(log_handle
, NULL
, NULL
, NULL
, &iosb
, (void*)delim
, sizeof(delim
) - 1, NULL
, NULL
);
5348 if (!NT_SUCCESS(Status
)) {
5349 ERR("ZwWriteFile returned %08x\n", Status
);
5354 dateline
= ExAllocatePoolWithTag(PagedPool
, 256, ALLOC_TAG
);
5357 ERR("out of memory\n");
5361 KeQuerySystemTime(&time
);
5363 RtlTimeToTimeFields(&time
, &tf
);
5365 sprintf(dateline
, "Starting logging at %04i-%02i-%02i %02i:%02i:%02i\n", tf
.Year
, tf
.Month
, tf
.Day
, tf
.Hour
, tf
.Minute
, tf
.Second
);
5367 Status
= ZwWriteFile(log_handle
, NULL
, NULL
, NULL
, &iosb
, dateline
, (ULONG
)strlen(dateline
), NULL
, NULL
);
5369 ExFreePool(dateline
);
5371 if (!NT_SUCCESS(Status
)) {
5372 ERR("ZwWriteFile returned %08x\n", Status
);
5378 ExReleaseResourceLite(&log_lock
);
5382 _Function_class_(KSTART_ROUTINE
)
5384 static void NTAPI
degraded_wait_thread(_In_
void* context
) {
5386 static void degraded_wait_thread(_In_
void* context
) {
5389 LARGE_INTEGER delay
;
5393 KeInitializeTimer(&timer
);
5395 delay
.QuadPart
= -30000000; // wait three seconds
5396 KeSetTimer(&timer
, delay
, NULL
);
5397 KeWaitForSingleObject(&timer
, Executive
, KernelMode
, FALSE
, NULL
);
5399 TRACE("timer expired\n");
5401 degraded_wait
= FALSE
;
5403 ZwClose(degraded_wait_handle
);
5404 degraded_wait_handle
= NULL
;
5406 PsTerminateSystemThread(STATUS_SUCCESS
);
5410 NTSTATUS NTAPI
AddDevice(PDRIVER_OBJECT DriverObject
, PDEVICE_OBJECT PhysicalDeviceObject
) {
5412 NTSTATUS
AddDevice(PDRIVER_OBJECT DriverObject
, PDEVICE_OBJECT PhysicalDeviceObject
) {
5416 UNICODE_STRING volname
;
5418 pdo_device_extension
* pdode
= NULL
;
5419 PDEVICE_OBJECT voldev
;
5420 volume_device_extension
* vde
;
5422 TRACE("(%p, %p)\n", DriverObject
, PhysicalDeviceObject
);
5424 ExAcquireResourceSharedLite(&pdo_list_lock
, TRUE
);
5426 le
= pdo_list
.Flink
;
5427 while (le
!= &pdo_list
) {
5428 pdo_device_extension
* pdode2
= CONTAINING_RECORD(le
, pdo_device_extension
, list_entry
);
5430 if (pdode2
->pdo
== PhysicalDeviceObject
) {
5439 WARN("unrecognized PDO %p\n", PhysicalDeviceObject
);
5440 Status
= STATUS_NOT_SUPPORTED
;
5444 ExAcquireResourceSharedLite(&pdode
->child_lock
, TRUE
);
5446 volname
.Length
= volname
.MaximumLength
= (sizeof(BTRFS_VOLUME_PREFIX
) - sizeof(WCHAR
)) + ((36 + 1) * sizeof(WCHAR
));
5447 volname
.Buffer
= ExAllocatePoolWithTag(PagedPool
, volname
.MaximumLength
, ALLOC_TAG
); // FIXME - when do we free this?
5449 if (!volname
.Buffer
) {
5450 ERR("out of memory\n");
5451 Status
= STATUS_INSUFFICIENT_RESOURCES
;
5455 RtlCopyMemory(volname
.Buffer
, BTRFS_VOLUME_PREFIX
, sizeof(BTRFS_VOLUME_PREFIX
) - sizeof(WCHAR
));
5457 j
= (sizeof(BTRFS_VOLUME_PREFIX
) / sizeof(WCHAR
)) - 1;
5458 for (i
= 0; i
< 16; i
++) {
5459 volname
.Buffer
[j
] = hex_digit(pdode
->uuid
.uuid
[i
] >> 4); j
++;
5460 volname
.Buffer
[j
] = hex_digit(pdode
->uuid
.uuid
[i
] & 0xf); j
++;
5462 if (i
== 3 || i
== 5 || i
== 7 || i
== 9) {
5463 volname
.Buffer
[j
] = '-';
5468 volname
.Buffer
[j
] = '}';
5470 Status
= IoCreateDevice(drvobj
, sizeof(volume_device_extension
), &volname
, FILE_DEVICE_DISK
,
5471 RtlIsNtDdiVersionAvailable(NTDDI_WIN8
) ? FILE_DEVICE_ALLOW_APPCONTAINER_TRAVERSAL
: 0, FALSE
, &voldev
);
5472 if (!NT_SUCCESS(Status
)) {
5473 ERR("IoCreateDevice returned %08x\n", Status
);
5477 voldev
->SectorSize
= PhysicalDeviceObject
->SectorSize
;
5478 voldev
->Flags
|= DO_DIRECT_IO
;
5480 vde
= voldev
->DeviceExtension
;
5481 vde
->type
= VCB_TYPE_VOLUME
;
5482 vde
->name
= volname
;
5483 vde
->device
= voldev
;
5484 vde
->mounted_device
= NULL
;
5485 vde
->pdo
= PhysicalDeviceObject
;
5487 vde
->removing
= FALSE
;
5488 vde
->open_count
= 0;
5490 Status
= IoRegisterDeviceInterface(PhysicalDeviceObject
, &GUID_DEVINTERFACE_VOLUME
, NULL
, &vde
->bus_name
);
5491 if (!NT_SUCCESS(Status
))
5492 WARN("IoRegisterDeviceInterface returned %08x\n", Status
);
5494 vde
->attached_device
= IoAttachDeviceToDeviceStack(voldev
, PhysicalDeviceObject
);
5498 if (pdode
->removable
)
5499 voldev
->Characteristics
|= FILE_REMOVABLE_MEDIA
;
5501 voldev
->Flags
&= ~DO_DEVICE_INITIALIZING
;
5503 Status
= IoSetDeviceInterfaceState(&vde
->bus_name
, TRUE
);
5504 if (!NT_SUCCESS(Status
))
5505 WARN("IoSetDeviceInterfaceState returned %08x\n", Status
);
5507 Status
= STATUS_SUCCESS
;
5510 ExReleaseResourceLite(&pdode
->child_lock
);
5513 ExReleaseResourceLite(&pdo_list_lock
);
5518 _Function_class_(DRIVER_INITIALIZE
)
5520 NTSTATUS NTAPI
DriverEntry(_In_ PDRIVER_OBJECT DriverObject
, _In_ PUNICODE_STRING RegistryPath
) {
5522 NTSTATUS
DriverEntry(_In_ PDRIVER_OBJECT DriverObject
, _In_ PUNICODE_STRING RegistryPath
) {
5525 PDEVICE_OBJECT DeviceObject
;
5526 UNICODE_STRING device_nameW
;
5527 UNICODE_STRING dosdevice_nameW
;
5528 control_device_extension
* cde
;
5530 OBJECT_ATTRIBUTES oa
;
5533 InitializeListHead(&uid_map_list
);
5534 InitializeListHead(&gid_map_list
);
5537 ExInitializeResourceLite(&log_lock
);
5539 ExInitializeResourceLite(&mapping_lock
);
5541 log_device
.Buffer
= NULL
;
5542 log_device
.Length
= log_device
.MaximumLength
= 0;
5543 log_file
.Buffer
= NULL
;
5544 log_file
.Length
= log_file
.MaximumLength
= 0;
5546 registry_path
.Length
= registry_path
.MaximumLength
= RegistryPath
->Length
;
5547 registry_path
.Buffer
= ExAllocatePoolWithTag(PagedPool
, registry_path
.Length
, ALLOC_TAG
);
5549 if (!registry_path
.Buffer
) {
5550 ERR("out of memory\n");
5551 return STATUS_INSUFFICIENT_RESOURCES
;
5554 RtlCopyMemory(registry_path
.Buffer
, RegistryPath
->Buffer
, registry_path
.Length
);
5556 read_registry(®istry_path
, FALSE
);
5559 if (debug_log_level
> 0)
5565 TRACE("DriverEntry\n");
5571 if (RtlIsNtDdiVersionAvailable(NTDDI_WIN8
)) {
5572 UNICODE_STRING name
;
5573 tPsIsDiskCountersEnabled fPsIsDiskCountersEnabled
;
5575 RtlInitUnicodeString(&name
, L
"PsIsDiskCountersEnabled");
5576 fPsIsDiskCountersEnabled
= (tPsIsDiskCountersEnabled
)MmGetSystemRoutineAddress(&name
);
5578 if (fPsIsDiskCountersEnabled
) {
5579 diskacc
= fPsIsDiskCountersEnabled();
5581 RtlInitUnicodeString(&name
, L
"PsUpdateDiskCounters");
5582 fPsUpdateDiskCounters
= (tPsUpdateDiskCounters
)MmGetSystemRoutineAddress(&name
);
5584 if (!fPsUpdateDiskCounters
)
5587 RtlInitUnicodeString(&name
, L
"FsRtlUpdateDiskCounters");
5588 fFsRtlUpdateDiskCounters
= (tFsRtlUpdateDiskCounters
)MmGetSystemRoutineAddress(&name
);
5591 RtlInitUnicodeString(&name
, L
"CcCopyReadEx");
5592 fCcCopyReadEx
= (tCcCopyReadEx
)MmGetSystemRoutineAddress(&name
);
5594 RtlInitUnicodeString(&name
, L
"CcCopyWriteEx");
5595 fCcCopyWriteEx
= (tCcCopyWriteEx
)MmGetSystemRoutineAddress(&name
);
5597 RtlInitUnicodeString(&name
, L
"CcSetAdditionalCacheAttributesEx");
5598 fCcSetAdditionalCacheAttributesEx
= (tCcSetAdditionalCacheAttributesEx
)MmGetSystemRoutineAddress(&name
);
5600 fPsUpdateDiskCounters
= NULL
;
5601 fCcCopyReadEx
= NULL
;
5602 fCcCopyWriteEx
= NULL
;
5603 fCcSetAdditionalCacheAttributesEx
= NULL
;
5604 fFsRtlUpdateDiskCounters
= NULL
;
5607 drvobj
= DriverObject
;
5609 DriverObject
->DriverUnload
= DriverUnload
;
5611 DriverObject
->DriverExtension
->AddDevice
= AddDevice
;
5614 DriverObject
->MajorFunction
[IRP_MJ_CREATE
] = drv_create
;
5615 DriverObject
->MajorFunction
[IRP_MJ_CLOSE
] = drv_close
;
5616 DriverObject
->MajorFunction
[IRP_MJ_READ
] = drv_read
;
5617 DriverObject
->MajorFunction
[IRP_MJ_WRITE
] = drv_write
;
5618 DriverObject
->MajorFunction
[IRP_MJ_QUERY_INFORMATION
] = drv_query_information
;
5619 DriverObject
->MajorFunction
[IRP_MJ_SET_INFORMATION
] = drv_set_information
;
5620 DriverObject
->MajorFunction
[IRP_MJ_QUERY_EA
] = drv_query_ea
;
5621 DriverObject
->MajorFunction
[IRP_MJ_SET_EA
] = drv_set_ea
;
5622 DriverObject
->MajorFunction
[IRP_MJ_FLUSH_BUFFERS
] = drv_flush_buffers
;
5623 DriverObject
->MajorFunction
[IRP_MJ_QUERY_VOLUME_INFORMATION
] = drv_query_volume_information
;
5624 DriverObject
->MajorFunction
[IRP_MJ_SET_VOLUME_INFORMATION
] = drv_set_volume_information
;
5625 DriverObject
->MajorFunction
[IRP_MJ_DIRECTORY_CONTROL
] = drv_directory_control
;
5626 DriverObject
->MajorFunction
[IRP_MJ_FILE_SYSTEM_CONTROL
] = drv_file_system_control
;
5627 DriverObject
->MajorFunction
[IRP_MJ_DEVICE_CONTROL
] = drv_device_control
;
5628 DriverObject
->MajorFunction
[IRP_MJ_SHUTDOWN
] = drv_shutdown
;
5629 DriverObject
->MajorFunction
[IRP_MJ_LOCK_CONTROL
] = drv_lock_control
;
5630 DriverObject
->MajorFunction
[IRP_MJ_CLEANUP
] = drv_cleanup
;
5631 DriverObject
->MajorFunction
[IRP_MJ_QUERY_SECURITY
] = drv_query_security
;
5632 DriverObject
->MajorFunction
[IRP_MJ_SET_SECURITY
] = drv_set_security
;
5633 DriverObject
->MajorFunction
[IRP_MJ_POWER
] = drv_power
;
5634 DriverObject
->MajorFunction
[IRP_MJ_SYSTEM_CONTROL
] = drv_system_control
;
5635 DriverObject
->MajorFunction
[IRP_MJ_PNP
] = drv_pnp
;
5637 DriverObject
->MajorFunction
[IRP_MJ_CREATE
] = (PDRIVER_DISPATCH
)drv_create
;
5638 DriverObject
->MajorFunction
[IRP_MJ_CLOSE
] = (PDRIVER_DISPATCH
)drv_close
;
5639 DriverObject
->MajorFunction
[IRP_MJ_READ
] = (PDRIVER_DISPATCH
)drv_read
;
5640 DriverObject
->MajorFunction
[IRP_MJ_WRITE
] = (PDRIVER_DISPATCH
)drv_write
;
5641 DriverObject
->MajorFunction
[IRP_MJ_QUERY_INFORMATION
] = (PDRIVER_DISPATCH
)drv_query_information
;
5642 DriverObject
->MajorFunction
[IRP_MJ_SET_INFORMATION
] = (PDRIVER_DISPATCH
)drv_set_information
;
5643 DriverObject
->MajorFunction
[IRP_MJ_QUERY_EA
] = (PDRIVER_DISPATCH
)drv_query_ea
;
5644 DriverObject
->MajorFunction
[IRP_MJ_SET_EA
] = (PDRIVER_DISPATCH
)drv_set_ea
;
5645 DriverObject
->MajorFunction
[IRP_MJ_FLUSH_BUFFERS
] = (PDRIVER_DISPATCH
)drv_flush_buffers
;
5646 DriverObject
->MajorFunction
[IRP_MJ_QUERY_VOLUME_INFORMATION
] = (PDRIVER_DISPATCH
)drv_query_volume_information
;
5647 DriverObject
->MajorFunction
[IRP_MJ_SET_VOLUME_INFORMATION
] = (PDRIVER_DISPATCH
)drv_set_volume_information
;
5648 DriverObject
->MajorFunction
[IRP_MJ_DIRECTORY_CONTROL
] = (PDRIVER_DISPATCH
)drv_directory_control
;
5649 DriverObject
->MajorFunction
[IRP_MJ_FILE_SYSTEM_CONTROL
] = (PDRIVER_DISPATCH
)drv_file_system_control
;
5650 DriverObject
->MajorFunction
[IRP_MJ_DEVICE_CONTROL
] = (PDRIVER_DISPATCH
)drv_device_control
;
5651 DriverObject
->MajorFunction
[IRP_MJ_SHUTDOWN
] = (PDRIVER_DISPATCH
)drv_shutdown
;
5652 DriverObject
->MajorFunction
[IRP_MJ_LOCK_CONTROL
] = (PDRIVER_DISPATCH
)drv_lock_control
;
5653 DriverObject
->MajorFunction
[IRP_MJ_CLEANUP
] = (PDRIVER_DISPATCH
)drv_cleanup
;
5654 DriverObject
->MajorFunction
[IRP_MJ_QUERY_SECURITY
] = (PDRIVER_DISPATCH
)drv_query_security
;
5655 DriverObject
->MajorFunction
[IRP_MJ_SET_SECURITY
] = (PDRIVER_DISPATCH
)drv_set_security
;
5656 DriverObject
->MajorFunction
[IRP_MJ_POWER
] = (PDRIVER_DISPATCH
)drv_power
;
5657 DriverObject
->MajorFunction
[IRP_MJ_SYSTEM_CONTROL
] = (PDRIVER_DISPATCH
)drv_system_control
;
5658 DriverObject
->MajorFunction
[IRP_MJ_PNP
] = (PDRIVER_DISPATCH
)drv_pnp
;
5661 init_fast_io_dispatch(&DriverObject
->FastIoDispatch
);
5663 device_nameW
.Buffer
= (WCHAR
*)device_name
;
5664 device_nameW
.Length
= device_nameW
.MaximumLength
= sizeof(device_name
) - sizeof(WCHAR
);
5665 dosdevice_nameW
.Buffer
= (WCHAR
*)dosdevice_name
;
5666 dosdevice_nameW
.Length
= dosdevice_nameW
.MaximumLength
= sizeof(dosdevice_name
) - sizeof(WCHAR
);
5668 Status
= IoCreateDevice(DriverObject
, sizeof(control_device_extension
), &device_nameW
, FILE_DEVICE_DISK_FILE_SYSTEM
,
5669 FILE_DEVICE_SECURE_OPEN
, FALSE
, &DeviceObject
);
5670 if (!NT_SUCCESS(Status
)) {
5671 ERR("IoCreateDevice returned %08x\n", Status
);
5675 master_devobj
= DeviceObject
;
5676 cde
= (control_device_extension
*)master_devobj
->DeviceExtension
;
5678 RtlZeroMemory(cde
, sizeof(control_device_extension
));
5680 cde
->type
= VCB_TYPE_CONTROL
;
5682 DeviceObject
->Flags
&= ~DO_DEVICE_INITIALIZING
;
5684 Status
= IoCreateSymbolicLink(&dosdevice_nameW
, &device_nameW
);
5685 if (!NT_SUCCESS(Status
)) {
5686 ERR("IoCreateSymbolicLink returned %08x\n", Status
);
5690 Status
= init_cache();
5691 if (!NT_SUCCESS(Status
)) {
5692 ERR("init_cache returned %08x\n", Status
);
5696 InitializeListHead(&VcbList
);
5697 ExInitializeResourceLite(&global_loading_lock
);
5698 ExInitializeResourceLite(&pdo_list_lock
);
5700 InitializeListHead(&pdo_list
);
5702 InitializeObjectAttributes(&oa
, RegistryPath
, OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
, NULL
, NULL
);
5703 Status
= ZwCreateKey(®h
, KEY_QUERY_VALUE
| KEY_ENUMERATE_SUB_KEYS
| KEY_NOTIFY
, &oa
, 0, NULL
, REG_OPTION_NON_VOLATILE
, &dispos
);
5704 /* ReactOS specific hack: allow BtrFS driver to start in 1st stage with no hive */
5706 if (!NT_SUCCESS(Status
)) {
5707 ERR("ZwCreateKey returned %08x\n", Status
);
5711 watch_registry(regh
);
5713 if (NT_SUCCESS(Status
)) {
5714 watch_registry(regh
);
5718 Status
= IoReportDetectedDevice(drvobj
, InterfaceTypeUndefined
, 0xFFFFFFFF, 0xFFFFFFFF,
5719 NULL
, NULL
, 0, &cde
->buspdo
);
5720 if (!NT_SUCCESS(Status
)) {
5721 ERR("IoReportDetectedDevice returned %08x\n", Status
);
5725 Status
= IoRegisterDeviceInterface(cde
->buspdo
, &BtrfsBusInterface
, NULL
, &cde
->bus_name
);
5726 if (!NT_SUCCESS(Status
))
5727 WARN("IoRegisterDeviceInterface returned %08x\n", Status
);
5729 cde
->attached_device
= IoAttachDeviceToDeviceStack(DeviceObject
, cde
->buspdo
);
5731 Status
= IoSetDeviceInterfaceState(&cde
->bus_name
, TRUE
);
5732 if (!NT_SUCCESS(Status
))
5733 WARN("IoSetDeviceInterfaceState returned %08x\n", Status
);
5735 DeviceObject
->Flags
&= ~DO_DEVICE_INITIALIZING
;
5737 IoInvalidateDeviceRelations(cde
->buspdo
, BusRelations
);
5739 Status
= PsCreateSystemThread(°raded_wait_handle
, 0, NULL
, NULL
, NULL
, degraded_wait_thread
, NULL
);
5740 if (!NT_SUCCESS(Status
))
5741 WARN("PsCreateSystemThread returned %08x\n", Status
);
5743 Status
= IoRegisterPlugPlayNotification(EventCategoryDeviceInterfaceChange
, PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES
,
5744 (PVOID
)&GUID_DEVINTERFACE_VOLUME
, DriverObject
, volume_notification
, DriverObject
, ¬ification_entry2
);
5745 if (!NT_SUCCESS(Status
))
5746 ERR("IoRegisterPlugPlayNotification returned %08x\n", Status
);
5748 Status
= IoRegisterPlugPlayNotification(EventCategoryDeviceInterfaceChange
, PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES
,
5749 (PVOID
)&GUID_DEVINTERFACE_HIDDEN_VOLUME
, DriverObject
, volume_notification
, DriverObject
, ¬ification_entry3
);
5750 if (!NT_SUCCESS(Status
))
5751 ERR("IoRegisterPlugPlayNotification returned %08x\n", Status
);
5753 Status
= IoRegisterPlugPlayNotification(EventCategoryDeviceInterfaceChange
, PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES
,
5754 (PVOID
)&GUID_DEVINTERFACE_DISK
, DriverObject
, pnp_notification
, DriverObject
, ¬ification_entry
);
5755 if (!NT_SUCCESS(Status
))
5756 ERR("IoRegisterPlugPlayNotification returned %08x\n", Status
);
5758 finished_probing
= TRUE
;
5760 KeInitializeEvent(&mountmgr_thread_event
, NotificationEvent
, FALSE
);
5763 Status
= PsCreateSystemThread(&mountmgr_thread_handle
, 0, NULL
, NULL
, NULL
, mountmgr_thread
, NULL
);
5764 if (!NT_SUCCESS(Status
))
5765 WARN("PsCreateSystemThread returned %08x\n", Status
);
5768 IoRegisterFileSystem(DeviceObject
);
5770 return STATUS_SUCCESS
;