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 #define COMPAT_RO_SUPPORTED (BTRFS_COMPAT_RO_FLAGS_FREE_SPACE_CACHE | BTRFS_COMPAT_RO_FLAGS_FREE_SPACE_CACHE_VALID)
54 static WCHAR device_name
[] = {'\\','B','t','r','f','s',0};
55 static WCHAR dosdevice_name
[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\','B','t','r','f','s',0};
57 DEFINE_GUID(BtrfsBusInterface
, 0x4d414874, 0x6865, 0x6761, 0x6d, 0x65, 0x83, 0x69, 0x17, 0x9a, 0x7d, 0x1d);
59 PDRIVER_OBJECT drvobj
;
60 PDEVICE_OBJECT master_devobj
;
62 BOOL have_sse42
= FALSE
, have_sse2
= FALSE
;
65 LIST_ENTRY uid_map_list
, gid_map_list
;
67 ERESOURCE global_loading_lock
;
68 UINT32 debug_log_level
= 0;
69 UINT32 mount_compress
= 0;
70 UINT32 mount_compress_force
= 0;
71 UINT32 mount_compress_type
= 0;
72 UINT32 mount_zlib_level
= 3;
73 UINT32 mount_flush_interval
= 30;
74 UINT32 mount_max_inline
= 2048;
75 UINT32 mount_skip_balance
= 0;
76 UINT32 mount_no_barrier
= 0;
77 UINT32 mount_no_trim
= 0;
78 UINT32 mount_clear_cache
= 0;
79 UINT32 mount_allow_degraded
= 0;
80 UINT32 mount_readonly
= 0;
82 BOOL log_started
= FALSE
;
83 UNICODE_STRING log_device
, log_file
, registry_path
;
84 tPsUpdateDiskCounters fPsUpdateDiskCounters
;
85 tCcCopyReadEx fCcCopyReadEx
;
86 tCcCopyWriteEx fCcCopyWriteEx
;
87 tCcSetAdditionalCacheAttributesEx fCcSetAdditionalCacheAttributesEx
;
88 tFsRtlUpdateDiskCounters fFsRtlUpdateDiskCounters
;
90 void *notification_entry
= NULL
, *notification_entry2
= NULL
, *notification_entry3
= NULL
;
91 ERESOURCE pdo_list_lock
, mapping_lock
;
93 BOOL finished_probing
= FALSE
;
94 HANDLE degraded_wait_handle
= NULL
, mountmgr_thread_handle
= NULL
;
95 BOOL degraded_wait
= TRUE
;
96 KEVENT mountmgr_thread_event
;
97 BOOL shutting_down
= FALSE
;
100 PFILE_OBJECT comfo
= NULL
;
101 PDEVICE_OBJECT comdo
= NULL
;
102 HANDLE log_handle
= NULL
;
104 HANDLE serial_thread_handle
= NULL
;
106 static void init_serial(BOOL first_time
);
109 static NTSTATUS
close_file(_In_ PFILE_OBJECT FileObject
, _In_ PIRP Irp
);
113 IO_STATUS_BLOCK iosb
;
117 _Function_class_(IO_COMPLETION_ROUTINE
)
118 static NTSTATUS
dbg_completion(_In_ PDEVICE_OBJECT DeviceObject
, _In_ PIRP Irp
, _In_ PVOID conptr
) {
119 read_context
* context
= conptr
;
121 UNUSED(DeviceObject
);
123 context
->iosb
= Irp
->IoStatus
;
124 KeSetEvent(&context
->Event
, 0, FALSE
);
126 return STATUS_MORE_PROCESSING_REQUIRED
;
129 #ifdef DEBUG_LONG_MESSAGES
130 void _debug_message(_In_
const char* func
, _In_
const char* file
, _In_
unsigned int line
, _In_
char* s
, ...) {
132 void _debug_message(_In_
const char* func
, _In_
char* s
, ...) {
134 LARGE_INTEGER offset
;
135 PIO_STACK_LOCATION IrpSp
;
140 read_context context
;
143 buf2
= ExAllocatePoolWithTag(NonPagedPool
, 1024, ALLOC_TAG
);
146 DbgPrint("Couldn't allocate buffer in debug_message\n");
150 #ifdef DEBUG_LONG_MESSAGES
151 sprintf(buf2
, "%p:%s:%s:%u:", PsGetCurrentThread(), func
, file
, line
);
153 sprintf(buf2
, "%p:%s:", PsGetCurrentThread(), func
);
155 buf
= &buf2
[strlen(buf2
)];
158 vsprintf(buf
, s
, ap
);
160 ExAcquireResourceSharedLite(&log_lock
, TRUE
);
162 if (!log_started
|| (log_device
.Length
== 0 && log_file
.Length
== 0)) {
164 } else if (log_device
.Length
> 0) {
166 DbgPrint("comdo is NULL :-(\n");
171 length
= (UINT32
)strlen(buf2
);
173 offset
.u
.LowPart
= 0;
174 offset
.u
.HighPart
= 0;
176 RtlZeroMemory(&context
, sizeof(read_context
));
178 KeInitializeEvent(&context
.Event
, NotificationEvent
, FALSE
);
180 Irp
= IoAllocateIrp(comdo
->StackSize
, FALSE
);
183 DbgPrint("IoAllocateIrp failed\n");
187 IrpSp
= IoGetNextIrpStackLocation(Irp
);
188 IrpSp
->MajorFunction
= IRP_MJ_WRITE
;
190 if (comdo
->Flags
& DO_BUFFERED_IO
) {
191 Irp
->AssociatedIrp
.SystemBuffer
= buf2
;
193 Irp
->Flags
= IRP_BUFFERED_IO
;
194 } else if (comdo
->Flags
& DO_DIRECT_IO
) {
195 Irp
->MdlAddress
= IoAllocateMdl(buf2
, length
, FALSE
, FALSE
, NULL
);
196 if (!Irp
->MdlAddress
) {
197 DbgPrint("IoAllocateMdl failed\n");
201 MmBuildMdlForNonPagedPool(Irp
->MdlAddress
);
203 Irp
->UserBuffer
= buf2
;
206 IrpSp
->Parameters
.Write
.Length
= length
;
207 IrpSp
->Parameters
.Write
.ByteOffset
= offset
;
209 Irp
->UserIosb
= &context
.iosb
;
211 Irp
->UserEvent
= &context
.Event
;
213 IoSetCompletionRoutine(Irp
, dbg_completion
, &context
, TRUE
, TRUE
, TRUE
);
215 Status
= IoCallDriver(comdo
, Irp
);
217 if (Status
== STATUS_PENDING
) {
218 KeWaitForSingleObject(&context
.Event
, Executive
, KernelMode
, FALSE
, NULL
);
219 Status
= context
.iosb
.Status
;
222 if (comdo
->Flags
& DO_DIRECT_IO
)
223 IoFreeMdl(Irp
->MdlAddress
);
225 if (!NT_SUCCESS(Status
)) {
226 DbgPrint("failed to write to COM1 - error %08x\n", Status
);
232 } else if (log_handle
!= NULL
) {
233 IO_STATUS_BLOCK iosb
;
235 length
= (UINT32
)strlen(buf2
);
237 Status
= ZwWriteFile(log_handle
, NULL
, NULL
, NULL
, &iosb
, buf2
, length
, NULL
, NULL
);
239 if (!NT_SUCCESS(Status
)) {
240 DbgPrint("failed to write to file - error %08x\n", Status
);
245 ExReleaseResourceLite(&log_lock
);
254 BOOL
is_top_level(_In_ PIRP Irp
) {
255 if (!IoGetTopLevelIrp()) {
256 IoSetTopLevelIrp(Irp
);
263 _Function_class_(DRIVER_UNLOAD
)
265 static void NTAPI
DriverUnload(_In_ PDRIVER_OBJECT DriverObject
) {
267 static void DriverUnload(_In_ PDRIVER_OBJECT DriverObject
) {
269 UNICODE_STRING dosdevice_nameW
;
271 ERR("DriverUnload\n");
275 IoUnregisterFileSystem(DriverObject
->DeviceObject
);
277 if (notification_entry2
)
279 IoUnregisterPlugPlayNotification(notification_entry2
);
281 IoUnregisterPlugPlayNotificationEx(notification_entry2
);
284 if (notification_entry3
)
286 IoUnregisterPlugPlayNotification(notification_entry3
);
288 IoUnregisterPlugPlayNotificationEx(notification_entry3
);
291 if (notification_entry
)
293 IoUnregisterPlugPlayNotification(notification_entry
);
295 IoUnregisterPlugPlayNotificationEx(notification_entry
);
298 dosdevice_nameW
.Buffer
= dosdevice_name
;
299 dosdevice_nameW
.Length
= dosdevice_nameW
.MaximumLength
= (USHORT
)wcslen(dosdevice_name
) * sizeof(WCHAR
);
301 IoDeleteSymbolicLink(&dosdevice_nameW
);
302 IoDeleteDevice(DriverObject
->DeviceObject
);
304 while (!IsListEmpty(&uid_map_list
)) {
305 LIST_ENTRY
* le
= RemoveHeadList(&uid_map_list
);
306 uid_map
* um
= CONTAINING_RECORD(le
, uid_map
, listentry
);
313 while (!IsListEmpty(&gid_map_list
)) {
314 gid_map
* gm
= CONTAINING_RECORD(RemoveHeadList(&gid_map_list
), gid_map
, listentry
);
320 // FIXME - free volumes and their devpaths
324 ObDereferenceObject(comfo
);
330 ExDeleteResourceLite(&global_loading_lock
);
331 ExDeleteResourceLite(&pdo_list_lock
);
333 if (log_device
.Buffer
)
334 ExFreePool(log_device
.Buffer
);
337 ExFreePool(log_file
.Buffer
);
339 if (registry_path
.Buffer
)
340 ExFreePool(registry_path
.Buffer
);
343 ExDeleteResourceLite(&log_lock
);
345 ExDeleteResourceLite(&mapping_lock
);
348 static BOOL
get_last_inode(_In_
_Requires_exclusive_lock_held_(_Curr_
->tree_lock
) device_extension
* Vcb
, _In_ root
* r
, _In_opt_ PIRP Irp
) {
350 traverse_ptr tp
, prev_tp
;
354 searchkey
.obj_id
= 0xffffffffffffffff;
355 searchkey
.obj_type
= 0xff;
356 searchkey
.offset
= 0xffffffffffffffff;
358 Status
= find_item(Vcb
, r
, &tp
, &searchkey
, FALSE
, Irp
);
359 if (!NT_SUCCESS(Status
)) {
360 ERR("error - find_item returned %08x\n", Status
);
364 if (tp
.item
->key
.obj_type
== TYPE_INODE_ITEM
|| (tp
.item
->key
.obj_type
== TYPE_ROOT_ITEM
&& !(tp
.item
->key
.obj_id
& 0x8000000000000000))) {
365 r
->lastinode
= tp
.item
->key
.obj_id
;
366 TRACE("last inode for tree %llx is %llx\n", r
->id
, r
->lastinode
);
370 while (find_prev_item(Vcb
, &tp
, &prev_tp
, Irp
)) {
373 TRACE("moving on to %llx,%x,%llx\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
375 if (tp
.item
->key
.obj_type
== TYPE_INODE_ITEM
|| (tp
.item
->key
.obj_type
== TYPE_ROOT_ITEM
&& !(tp
.item
->key
.obj_id
& 0x8000000000000000))) {
376 r
->lastinode
= tp
.item
->key
.obj_id
;
377 TRACE("last inode for tree %llx is %llx\n", r
->id
, r
->lastinode
);
382 r
->lastinode
= SUBVOL_ROOT_INODE
;
384 WARN("no INODE_ITEMs in tree %llx\n", r
->id
);
390 static BOOL
extract_xattr(_In_reads_bytes_(size
) void* item
, _In_ USHORT size
, _In_z_
char* name
, _Out_ UINT8
** data
, _Out_ UINT16
* datalen
) {
391 DIR_ITEM
* xa
= (DIR_ITEM
*)item
;
395 if (size
< sizeof(DIR_ITEM
) || size
< (sizeof(DIR_ITEM
) - 1 + xa
->m
+ xa
->n
)) {
396 WARN("DIR_ITEM is truncated\n");
400 if (xa
->n
== strlen(name
) && RtlCompareMemory(name
, xa
->name
, xa
->n
) == xa
->n
) {
401 TRACE("found xattr %s\n", name
);
406 *data
= ExAllocatePoolWithTag(PagedPool
, xa
->m
, ALLOC_TAG
);
408 ERR("out of memory\n");
412 RtlCopyMemory(*data
, &xa
->name
[xa
->n
], xa
->m
);
419 xasize
= sizeof(DIR_ITEM
) - 1 + xa
->m
+ xa
->n
;
423 xa
= (DIR_ITEM
*)&xa
->name
[xa
->m
+ xa
->n
];
428 TRACE("xattr %s not found\n", name
);
434 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
,
435 _Out_ UINT8
** data
, _Out_ UINT16
* datalen
, _In_opt_ PIRP Irp
) {
440 TRACE("(%p, %llx, %llx, %s, %08x, %p, %p)\n", Vcb
, subvol
->id
, inode
, name
, crc32
, data
, datalen
);
442 searchkey
.obj_id
= inode
;
443 searchkey
.obj_type
= TYPE_XATTR_ITEM
;
444 searchkey
.offset
= crc32
;
446 Status
= find_item(Vcb
, subvol
, &tp
, &searchkey
, FALSE
, Irp
);
447 if (!NT_SUCCESS(Status
)) {
448 ERR("error - find_item returned %08x\n", Status
);
452 if (keycmp(tp
.item
->key
, searchkey
)) {
453 TRACE("could not find item (%llx,%x,%llx)\n", searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
);
457 if (tp
.item
->size
< sizeof(DIR_ITEM
)) {
458 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
));
462 return extract_xattr(tp
.item
->data
, tp
.item
->size
, name
, data
, datalen
);
465 _Dispatch_type_(IRP_MJ_CLOSE
)
466 _Function_class_(DRIVER_DISPATCH
)
467 static NTSTATUS
drv_close(_In_ PDEVICE_OBJECT DeviceObject
, _In_ PIRP Irp
) {
469 PIO_STACK_LOCATION IrpSp
;
470 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
473 FsRtlEnterFileSystem();
477 top_level
= is_top_level(Irp
);
479 if (DeviceObject
== master_devobj
) {
480 TRACE("Closing file system\n");
481 Status
= STATUS_SUCCESS
;
483 } else if (Vcb
&& Vcb
->type
== VCB_TYPE_VOLUME
) {
484 Status
= vol_close(DeviceObject
, Irp
);
486 } else if (!Vcb
|| Vcb
->type
!= VCB_TYPE_FS
) {
487 Status
= STATUS_INVALID_PARAMETER
;
491 IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
493 // FIXME - unmount if called for volume
494 // FIXME - call FsRtlNotifyUninitializeSync(&Vcb->NotifySync) if unmounting
496 Status
= close_file(IrpSp
->FileObject
, Irp
);
499 Irp
->IoStatus
.Status
= Status
;
500 Irp
->IoStatus
.Information
= 0;
502 IoCompleteRequest( Irp
, IO_DISK_INCREMENT
);
505 IoSetTopLevelIrp(NULL
);
507 TRACE("returning %08x\n", Status
);
509 FsRtlExitFileSystem();
514 _Dispatch_type_(IRP_MJ_FLUSH_BUFFERS
)
515 _Function_class_(DRIVER_DISPATCH
)
516 static NTSTATUS
drv_flush_buffers(_In_ PDEVICE_OBJECT DeviceObject
, _In_ PIRP Irp
) {
518 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation( Irp
);
519 PFILE_OBJECT FileObject
= IrpSp
->FileObject
;
520 fcb
* fcb
= FileObject
->FsContext
;
521 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
524 FsRtlEnterFileSystem();
526 TRACE("flush buffers\n");
528 top_level
= is_top_level(Irp
);
530 if (Vcb
&& Vcb
->type
== VCB_TYPE_VOLUME
) {
531 Status
= vol_flush_buffers(DeviceObject
, Irp
);
533 } else if (!Vcb
|| Vcb
->type
!= VCB_TYPE_FS
) {
534 Status
= STATUS_INVALID_PARAMETER
;
539 ERR("fcb was NULL\n");
540 Status
= STATUS_INVALID_PARAMETER
;
544 if (fcb
== Vcb
->volume_fcb
) {
545 Status
= STATUS_INVALID_PARAMETER
;
549 Irp
->IoStatus
.Information
= 0;
551 fcb
->Header
.IsFastIoPossible
= fast_io_possible(fcb
);
553 Status
= STATUS_SUCCESS
;
554 Irp
->IoStatus
.Status
= Status
;
556 if (fcb
->type
!= BTRFS_TYPE_DIRECTORY
) {
557 CcFlushCache(&fcb
->nonpaged
->segment_object
, NULL
, 0, &Irp
->IoStatus
);
559 if (fcb
->Header
.PagingIoResource
) {
560 ExAcquireResourceExclusiveLite(fcb
->Header
.PagingIoResource
, TRUE
);
561 ExReleaseResourceLite(fcb
->Header
.PagingIoResource
);
564 Status
= Irp
->IoStatus
.Status
;
568 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
570 TRACE("returning %08x\n", Status
);
573 IoSetTopLevelIrp(NULL
);
575 FsRtlExitFileSystem();
580 static void calculate_total_space(_In_ device_extension
* Vcb
, _Out_ UINT64
* totalsize
, _Out_ UINT64
* freespace
) {
581 UINT64 nfactor
, dfactor
, sectors_used
;
583 if (Vcb
->data_flags
& BLOCK_FLAG_DUPLICATE
|| Vcb
->data_flags
& BLOCK_FLAG_RAID1
|| Vcb
->data_flags
& BLOCK_FLAG_RAID10
) {
586 } else if (Vcb
->data_flags
& BLOCK_FLAG_RAID5
) {
587 nfactor
= Vcb
->superblock
.num_devices
- 1;
588 dfactor
= Vcb
->superblock
.num_devices
;
589 } else if (Vcb
->data_flags
& BLOCK_FLAG_RAID6
) {
590 nfactor
= Vcb
->superblock
.num_devices
- 2;
591 dfactor
= Vcb
->superblock
.num_devices
;
597 sectors_used
= Vcb
->superblock
.bytes_used
/ Vcb
->superblock
.sector_size
;
599 *totalsize
= (Vcb
->superblock
.total_bytes
/ Vcb
->superblock
.sector_size
) * nfactor
/ dfactor
;
600 *freespace
= sectors_used
> *totalsize
? 0 : (*totalsize
- sectors_used
);
604 // This function exists because we have to lie about our FS type in certain situations.
605 // MPR!MprGetConnection queries the FS type, and compares it to a whitelist. If it doesn't match,
606 // it will return ERROR_NO_NET_OR_BAD_PATH, which prevents UAC from working.
607 // The command mklink refuses to create hard links on anything other than NTFS, so we have to
608 // blacklist cmd.exe too.
610 static BOOL
lie_about_fs_type() {
612 PROCESS_BASIC_INFORMATION pbi
;
617 static WCHAR mpr
[] = L
"MPR.DLL";
618 static WCHAR cmd
[] = L
"CMD.EXE";
619 static WCHAR fsutil
[] = L
"FSUTIL.EXE";
620 UNICODE_STRING mprus
, cmdus
, fsutilus
;
623 mprus
.Length
= mprus
.MaximumLength
= (USHORT
)(wcslen(mpr
) * sizeof(WCHAR
));
625 cmdus
.Length
= cmdus
.MaximumLength
= (USHORT
)(wcslen(cmd
) * sizeof(WCHAR
));
626 fsutilus
.Buffer
= fsutil
;
627 fsutilus
.Length
= fsutilus
.MaximumLength
= (USHORT
)(wcslen(fsutil
) * sizeof(WCHAR
));
629 if (!PsGetCurrentProcess())
632 Status
= ZwQueryInformationProcess(NtCurrentProcess(), ProcessBasicInformation
, &pbi
, sizeof(pbi
), &retlen
);
634 if (!NT_SUCCESS(Status
)) {
635 ERR("ZwQueryInformationProcess returned %08x\n", Status
);
639 if (!pbi
.PebBaseAddress
)
642 peb
= pbi
.PebBaseAddress
;
647 le
= peb
->Ldr
->InMemoryOrderModuleList
.Flink
;
648 while (le
!= &peb
->Ldr
->InMemoryOrderModuleList
) {
649 LDR_DATA_TABLE_ENTRY
* entry
= CONTAINING_RECORD(le
, LDR_DATA_TABLE_ENTRY
, InMemoryOrderLinks
);
650 BOOL blacklist
= FALSE
;
652 if (entry
->FullDllName
.Length
>= mprus
.Length
) {
655 name
.Buffer
= &entry
->FullDllName
.Buffer
[(entry
->FullDllName
.Length
- mprus
.Length
) / sizeof(WCHAR
)];
656 name
.Length
= name
.MaximumLength
= mprus
.Length
;
658 blacklist
= FsRtlAreNamesEqual(&name
, &mprus
, TRUE
, NULL
);
661 if (!blacklist
&& entry
->FullDllName
.Length
>= cmdus
.Length
) {
664 name
.Buffer
= &entry
->FullDllName
.Buffer
[(entry
->FullDllName
.Length
- cmdus
.Length
) / sizeof(WCHAR
)];
665 name
.Length
= name
.MaximumLength
= cmdus
.Length
;
667 blacklist
= FsRtlAreNamesEqual(&name
, &cmdus
, TRUE
, NULL
);
670 if (!blacklist
&& entry
->FullDllName
.Length
>= fsutilus
.Length
) {
673 name
.Buffer
= &entry
->FullDllName
.Buffer
[(entry
->FullDllName
.Length
- fsutilus
.Length
) / sizeof(WCHAR
)];
674 name
.Length
= name
.MaximumLength
= fsutilus
.Length
;
676 blacklist
= FsRtlAreNamesEqual(&name
, &fsutilus
, TRUE
, NULL
);
683 frames
= ExAllocatePoolWithTag(PagedPool
, 256 * sizeof(void*), ALLOC_TAG
);
685 ERR("out of memory\n");
689 num_frames
= RtlWalkFrameChain(frames
, 256, 1);
691 for (i
= 0; i
< num_frames
; i
++) {
692 // entry->Reserved3[1] appears to be the image size
693 if (frames
[i
] >= entry
->DllBase
&& (ULONG_PTR
)frames
[i
] <= (ULONG_PTR
)entry
->DllBase
+ (ULONG_PTR
)entry
->Reserved3
[1]) {
709 _Dispatch_type_(IRP_MJ_QUERY_VOLUME_INFORMATION
)
710 _Function_class_(DRIVER_DISPATCH
)
711 static NTSTATUS
drv_query_volume_information(_In_ PDEVICE_OBJECT DeviceObject
, _In_ PIRP Irp
) {
712 PIO_STACK_LOCATION IrpSp
;
714 ULONG BytesCopied
= 0;
715 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
718 FsRtlEnterFileSystem();
720 TRACE("query volume information\n");
721 top_level
= is_top_level(Irp
);
723 if (Vcb
&& Vcb
->type
== VCB_TYPE_VOLUME
) {
724 Status
= vol_query_volume_information(DeviceObject
, Irp
);
726 } else if (!Vcb
|| Vcb
->type
!= VCB_TYPE_FS
) {
727 Status
= STATUS_INVALID_PARAMETER
;
731 IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
733 Status
= STATUS_NOT_IMPLEMENTED
;
735 switch (IrpSp
->Parameters
.QueryVolume
.FsInformationClass
) {
736 case FileFsAttributeInformation
:
738 FILE_FS_ATTRIBUTE_INFORMATION
* data
= Irp
->AssociatedIrp
.SystemBuffer
;
739 BOOL overflow
= FALSE
;
741 WCHAR
* fs_name
= (Irp
->RequestorMode
== UserMode
&& lie_about_fs_type()) ? L
"NTFS" : L
"Btrfs";
742 ULONG fs_name_len
= (ULONG
)wcslen(fs_name
) * sizeof(WCHAR
);
744 WCHAR
* fs_name
= L
"Btrfs";
745 ULONG fs_name_len
= 5 * sizeof(WCHAR
);
747 ULONG orig_fs_name_len
= fs_name_len
;
749 TRACE("FileFsAttributeInformation\n");
751 if (IrpSp
->Parameters
.QueryVolume
.Length
< sizeof(FILE_FS_ATTRIBUTE_INFORMATION
) - sizeof(WCHAR
) + fs_name_len
) {
752 if (IrpSp
->Parameters
.QueryVolume
.Length
> sizeof(FILE_FS_ATTRIBUTE_INFORMATION
) - sizeof(WCHAR
))
753 fs_name_len
= IrpSp
->Parameters
.QueryVolume
.Length
- sizeof(FILE_FS_ATTRIBUTE_INFORMATION
) + sizeof(WCHAR
);
760 data
->FileSystemAttributes
= FILE_CASE_PRESERVED_NAMES
| FILE_CASE_SENSITIVE_SEARCH
|
761 FILE_UNICODE_ON_DISK
| FILE_NAMED_STREAMS
| FILE_SUPPORTS_HARD_LINKS
| FILE_PERSISTENT_ACLS
|
762 FILE_SUPPORTS_REPARSE_POINTS
| FILE_SUPPORTS_SPARSE_FILES
| FILE_SUPPORTS_OBJECT_IDS
|
763 FILE_SUPPORTS_OPEN_BY_FILE_ID
| FILE_SUPPORTS_EXTENDED_ATTRIBUTES
| FILE_SUPPORTS_BLOCK_REFCOUNTING
;
765 data
->FileSystemAttributes
|= FILE_READ_ONLY_VOLUME
;
767 // should also be FILE_FILE_COMPRESSION when supported
768 data
->MaximumComponentNameLength
= 255; // FIXME - check
769 data
->FileSystemNameLength
= orig_fs_name_len
;
770 RtlCopyMemory(data
->FileSystemName
, fs_name
, fs_name_len
);
772 BytesCopied
= sizeof(FILE_FS_ATTRIBUTE_INFORMATION
) - sizeof(WCHAR
) + fs_name_len
;
773 Status
= overflow
? STATUS_BUFFER_OVERFLOW
: STATUS_SUCCESS
;
777 case FileFsDeviceInformation
:
779 FILE_FS_DEVICE_INFORMATION
* ffdi
= Irp
->AssociatedIrp
.SystemBuffer
;
781 TRACE("FileFsDeviceInformation\n");
783 ffdi
->DeviceType
= FILE_DEVICE_DISK
;
785 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, TRUE
);
786 ffdi
->Characteristics
= Vcb
->Vpb
->RealDevice
->Characteristics
;
787 ExReleaseResourceLite(&Vcb
->tree_lock
);
790 ffdi
->Characteristics
|= FILE_READ_ONLY_DEVICE
;
792 ffdi
->Characteristics
&= ~FILE_READ_ONLY_DEVICE
;
794 BytesCopied
= sizeof(FILE_FS_DEVICE_INFORMATION
);
795 Status
= STATUS_SUCCESS
;
800 case FileFsFullSizeInformation
:
802 FILE_FS_FULL_SIZE_INFORMATION
* ffsi
= Irp
->AssociatedIrp
.SystemBuffer
;
804 TRACE("FileFsFullSizeInformation\n");
806 calculate_total_space(Vcb
, (UINT64
*)&ffsi
->TotalAllocationUnits
.QuadPart
, (UINT64
*)&ffsi
->ActualAvailableAllocationUnits
.QuadPart
);
807 ffsi
->CallerAvailableAllocationUnits
.QuadPart
= ffsi
->ActualAvailableAllocationUnits
.QuadPart
;
808 ffsi
->SectorsPerAllocationUnit
= 1;
809 ffsi
->BytesPerSector
= Vcb
->superblock
.sector_size
;
811 BytesCopied
= sizeof(FILE_FS_FULL_SIZE_INFORMATION
);
812 Status
= STATUS_SUCCESS
;
817 case FileFsObjectIdInformation
:
819 FILE_FS_OBJECTID_INFORMATION
* ffoi
= Irp
->AssociatedIrp
.SystemBuffer
;
821 TRACE("FileFsObjectIdInformation\n");
823 RtlCopyMemory(ffoi
->ObjectId
, &Vcb
->superblock
.uuid
.uuid
[0], sizeof(UCHAR
) * 16);
824 RtlZeroMemory(ffoi
->ExtendedInfo
, sizeof(ffoi
->ExtendedInfo
));
826 BytesCopied
= sizeof(FILE_FS_OBJECTID_INFORMATION
);
827 Status
= STATUS_SUCCESS
;
832 case FileFsSizeInformation
:
834 FILE_FS_SIZE_INFORMATION
* ffsi
= Irp
->AssociatedIrp
.SystemBuffer
;
836 TRACE("FileFsSizeInformation\n");
838 calculate_total_space(Vcb
, (UINT64
*)&ffsi
->TotalAllocationUnits
.QuadPart
, (UINT64
*)&ffsi
->AvailableAllocationUnits
.QuadPart
);
839 ffsi
->SectorsPerAllocationUnit
= 1;
840 ffsi
->BytesPerSector
= Vcb
->superblock
.sector_size
;
842 BytesCopied
= sizeof(FILE_FS_SIZE_INFORMATION
);
843 Status
= STATUS_SUCCESS
;
848 case FileFsVolumeInformation
:
850 FILE_FS_VOLUME_INFORMATION
* data
= Irp
->AssociatedIrp
.SystemBuffer
;
851 FILE_FS_VOLUME_INFORMATION ffvi
;
852 BOOL overflow
= FALSE
;
853 ULONG label_len
, orig_label_len
;
855 TRACE("FileFsVolumeInformation\n");
856 TRACE("max length = %u\n", IrpSp
->Parameters
.QueryVolume
.Length
);
858 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, TRUE
);
860 Status
= RtlUTF8ToUnicodeN(NULL
, 0, &label_len
, Vcb
->superblock
.label
, (ULONG
)strlen(Vcb
->superblock
.label
));
861 if (!NT_SUCCESS(Status
)) {
862 ERR("RtlUTF8ToUnicodeN returned %08x\n", Status
);
863 ExReleaseResourceLite(&Vcb
->tree_lock
);
867 orig_label_len
= label_len
;
869 if (IrpSp
->Parameters
.QueryVolume
.Length
< sizeof(FILE_FS_VOLUME_INFORMATION
) - sizeof(WCHAR
) + label_len
) {
870 if (IrpSp
->Parameters
.QueryVolume
.Length
> sizeof(FILE_FS_VOLUME_INFORMATION
) - sizeof(WCHAR
))
871 label_len
= IrpSp
->Parameters
.QueryVolume
.Length
- sizeof(FILE_FS_VOLUME_INFORMATION
) + sizeof(WCHAR
);
878 TRACE("label_len = %u\n", label_len
);
880 ffvi
.VolumeCreationTime
.QuadPart
= 0; // FIXME
881 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];
882 ffvi
.VolumeLabelLength
= orig_label_len
;
883 ffvi
.SupportsObjects
= FALSE
;
885 RtlCopyMemory(data
, &ffvi
, min(sizeof(FILE_FS_VOLUME_INFORMATION
) - sizeof(WCHAR
), IrpSp
->Parameters
.QueryVolume
.Length
));
890 Status
= RtlUTF8ToUnicodeN(&data
->VolumeLabel
[0], label_len
, &bytecount
, Vcb
->superblock
.label
, (ULONG
)strlen(Vcb
->superblock
.label
));
891 if (!NT_SUCCESS(Status
) && Status
!= STATUS_BUFFER_TOO_SMALL
) {
892 ERR("RtlUTF8ToUnicodeN returned %08x\n", Status
);
893 ExReleaseResourceLite(&Vcb
->tree_lock
);
897 TRACE("label = %.*S\n", label_len
/ sizeof(WCHAR
), data
->VolumeLabel
);
900 ExReleaseResourceLite(&Vcb
->tree_lock
);
902 BytesCopied
= sizeof(FILE_FS_VOLUME_INFORMATION
) - sizeof(WCHAR
) + label_len
;
903 Status
= overflow
? STATUS_BUFFER_OVERFLOW
: STATUS_SUCCESS
;
908 #ifdef _MSC_VER // not in mingw yet
909 case FileFsSectorSizeInformation
:
911 FILE_FS_SECTOR_SIZE_INFORMATION
* data
= Irp
->AssociatedIrp
.SystemBuffer
;
913 data
->LogicalBytesPerSector
= Vcb
->superblock
.sector_size
;
914 data
->PhysicalBytesPerSectorForAtomicity
= Vcb
->superblock
.sector_size
;
915 data
->PhysicalBytesPerSectorForPerformance
= Vcb
->superblock
.sector_size
;
916 data
->FileSystemEffectivePhysicalBytesPerSectorForAtomicity
= Vcb
->superblock
.sector_size
;
917 data
->ByteOffsetForSectorAlignment
= 0;
918 data
->ByteOffsetForPartitionAlignment
= 0;
920 data
->Flags
= SSINFO_FLAGS_ALIGNED_DEVICE
| SSINFO_FLAGS_PARTITION_ALIGNED_ON_DEVICE
;
922 if (Vcb
->trim
&& !Vcb
->options
.no_trim
)
923 data
->Flags
|= SSINFO_FLAGS_TRIM_ENABLED
;
925 BytesCopied
= sizeof(FILE_FS_SECTOR_SIZE_INFORMATION
);
930 #endif /* __REACTOS__ */
933 Status
= STATUS_INVALID_PARAMETER
;
934 WARN("unknown FsInformationClass %u\n", IrpSp
->Parameters
.QueryVolume
.FsInformationClass
);
938 if (!NT_SUCCESS(Status
) && Status
!= STATUS_BUFFER_OVERFLOW
)
939 Irp
->IoStatus
.Information
= 0;
941 Irp
->IoStatus
.Information
= BytesCopied
;
944 Irp
->IoStatus
.Status
= Status
;
946 IoCompleteRequest( Irp
, IO_DISK_INCREMENT
);
949 IoSetTopLevelIrp(NULL
);
951 TRACE("query volume information returning %08x\n", Status
);
953 FsRtlExitFileSystem();
958 _Function_class_(IO_COMPLETION_ROUTINE
)
960 static NTSTATUS NTAPI
read_completion(_In_ PDEVICE_OBJECT DeviceObject
, _In_ PIRP Irp
, _In_ PVOID conptr
) {
962 static NTSTATUS
read_completion(_In_ PDEVICE_OBJECT DeviceObject
, _In_ PIRP Irp
, _In_ PVOID conptr
) {
964 read_context
* context
= conptr
;
966 UNUSED(DeviceObject
);
968 context
->iosb
= Irp
->IoStatus
;
969 KeSetEvent(&context
->Event
, 0, FALSE
);
971 return STATUS_MORE_PROCESSING_REQUIRED
;
974 NTSTATUS
create_root(_In_
_Requires_exclusive_lock_held_(_Curr_
->tree_lock
) device_extension
* Vcb
, _In_ UINT64 id
,
975 _Out_ root
** rootptr
, _In_ BOOL no_tree
, _In_ UINT64 offset
, _In_opt_ PIRP Irp
) {
982 r
= ExAllocatePoolWithTag(PagedPool
, sizeof(root
), ALLOC_TAG
);
984 ERR("out of memory\n");
985 return STATUS_INSUFFICIENT_RESOURCES
;
988 r
->nonpaged
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(root_nonpaged
), ALLOC_TAG
);
990 ERR("out of memory\n");
992 return STATUS_INSUFFICIENT_RESOURCES
;
996 t
= ExAllocatePoolWithTag(PagedPool
, sizeof(tree
), ALLOC_TAG
);
998 ERR("out of memory\n");
999 ExFreePool(r
->nonpaged
);
1001 return STATUS_INSUFFICIENT_RESOURCES
;
1004 t
->is_unique
= TRUE
;
1005 t
->uniqueness_determined
= TRUE
;
1009 ri
= ExAllocatePoolWithTag(PagedPool
, sizeof(ROOT_ITEM
), ALLOC_TAG
);
1011 ERR("out of memory\n");
1016 ExFreePool(r
->nonpaged
);
1018 return STATUS_INSUFFICIENT_RESOURCES
;
1022 r
->treeholder
.address
= 0;
1023 r
->treeholder
.generation
= Vcb
->superblock
.generation
;
1024 r
->treeholder
.tree
= t
;
1027 r
->received
= FALSE
;
1031 RtlZeroMemory(&r
->root_item
, sizeof(ROOT_ITEM
));
1032 r
->root_item
.num_references
= 1;
1033 InitializeListHead(&r
->fcbs
);
1035 RtlCopyMemory(ri
, &r
->root_item
, sizeof(ROOT_ITEM
));
1037 // We ask here for a traverse_ptr to the item we're inserting, so we can
1038 // copy some of the tree's variables
1040 Status
= insert_tree_item(Vcb
, Vcb
->root_root
, id
, TYPE_ROOT_ITEM
, offset
, ri
, sizeof(ROOT_ITEM
), &tp
, Irp
);
1041 if (!NT_SUCCESS(Status
)) {
1042 ERR("insert_tree_item returned %08x\n", Status
);
1048 ExFreePool(r
->nonpaged
);
1053 ExInitializeResourceLite(&r
->nonpaged
->load_tree_lock
);
1055 InsertTailList(&Vcb
->roots
, &r
->list_entry
);
1058 RtlZeroMemory(&t
->header
, sizeof(tree_header
));
1059 t
->header
.fs_uuid
= tp
.tree
->header
.fs_uuid
;
1060 t
->header
.address
= 0;
1061 t
->header
.flags
= HEADER_FLAG_MIXED_BACKREF
| 1; // 1 == "written"? Why does the Linux driver record this?
1062 t
->header
.chunk_tree_uuid
= tp
.tree
->header
.chunk_tree_uuid
;
1063 t
->header
.generation
= Vcb
->superblock
.generation
;
1064 t
->header
.tree_id
= id
;
1065 t
->header
.num_items
= 0;
1066 t
->header
.level
= 0;
1068 t
->has_address
= FALSE
;
1075 InitializeListHead(&t
->itemlist
);
1078 t
->has_new_address
= FALSE
;
1079 t
->updated_extents
= FALSE
;
1081 InsertTailList(&Vcb
->trees
, &t
->list_entry
);
1082 t
->list_entry_hash
.Flink
= NULL
;
1085 Vcb
->need_write
= TRUE
;
1090 return STATUS_SUCCESS
;
1093 static NTSTATUS
set_label(_In_ device_extension
* Vcb
, _In_ FILE_FS_LABEL_INFORMATION
* ffli
) {
1098 TRACE("label = %.*S\n", ffli
->VolumeLabelLength
/ sizeof(WCHAR
), ffli
->VolumeLabel
);
1100 vollen
= ffli
->VolumeLabelLength
;
1102 for (i
= 0; i
< ffli
->VolumeLabelLength
/ sizeof(WCHAR
); i
++) {
1103 if (ffli
->VolumeLabel
[i
] == 0) {
1104 vollen
= i
* sizeof(WCHAR
);
1106 } else if (ffli
->VolumeLabel
[i
] == '/' || ffli
->VolumeLabel
[i
] == '\\') {
1107 Status
= STATUS_INVALID_VOLUME_LABEL
;
1115 Status
= RtlUnicodeToUTF8N(NULL
, 0, &utf8len
, ffli
->VolumeLabel
, vollen
);
1116 if (!NT_SUCCESS(Status
))
1119 if (utf8len
> MAX_LABEL_SIZE
) {
1120 Status
= STATUS_INVALID_VOLUME_LABEL
;
1125 ExAcquireResourceExclusiveLite(&Vcb
->tree_lock
, TRUE
);
1128 Status
= RtlUnicodeToUTF8N((PCHAR
)&Vcb
->superblock
.label
, MAX_LABEL_SIZE
, &utf8len
, ffli
->VolumeLabel
, vollen
);
1129 if (!NT_SUCCESS(Status
))
1132 Status
= STATUS_SUCCESS
;
1134 if (utf8len
< MAX_LABEL_SIZE
)
1135 RtlZeroMemory(Vcb
->superblock
.label
+ utf8len
, MAX_LABEL_SIZE
- utf8len
);
1137 Vcb
->need_write
= TRUE
;
1140 ExReleaseResourceLite(&Vcb
->tree_lock
);
1143 TRACE("returning %08x\n", Status
);
1148 _Dispatch_type_(IRP_MJ_SET_VOLUME_INFORMATION
)
1149 _Function_class_(DRIVER_DISPATCH
)
1150 static NTSTATUS
drv_set_volume_information(_In_ PDEVICE_OBJECT DeviceObject
, _In_ PIRP Irp
) {
1151 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
1152 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
1156 FsRtlEnterFileSystem();
1158 TRACE("set volume information\n");
1160 top_level
= is_top_level(Irp
);
1162 if (Vcb
&& Vcb
->type
== VCB_TYPE_VOLUME
) {
1163 Status
= vol_set_volume_information(DeviceObject
, Irp
);
1165 } else if (!Vcb
|| Vcb
->type
!= VCB_TYPE_FS
) {
1166 Status
= STATUS_INVALID_PARAMETER
;
1170 Status
= STATUS_NOT_IMPLEMENTED
;
1172 if (Vcb
->readonly
) {
1173 Status
= STATUS_MEDIA_WRITE_PROTECTED
;
1177 if (Vcb
->removing
|| Vcb
->locked
) {
1178 Status
= STATUS_ACCESS_DENIED
;
1182 switch (IrpSp
->Parameters
.SetVolume
.FsInformationClass
) {
1183 case FileFsControlInformation
:
1184 FIXME("STUB: FileFsControlInformation\n");
1187 case FileFsLabelInformation
:
1188 TRACE("FileFsLabelInformation\n");
1190 Status
= set_label(Vcb
, Irp
->AssociatedIrp
.SystemBuffer
);
1193 case FileFsObjectIdInformation
:
1194 FIXME("STUB: FileFsObjectIdInformation\n");
1198 WARN("Unrecognized FsInformationClass 0x%x\n", IrpSp
->Parameters
.SetVolume
.FsInformationClass
);
1203 Irp
->IoStatus
.Status
= Status
;
1204 Irp
->IoStatus
.Information
= 0;
1206 TRACE("returning %08x\n", Status
);
1208 IoCompleteRequest( Irp
, IO_NO_INCREMENT
);
1211 IoSetTopLevelIrp(NULL
);
1213 FsRtlExitFileSystem();
1218 static WCHAR
* file_desc_fcb(_In_ fcb
* fcb
) {
1224 if (fcb
->debug_desc
)
1225 return fcb
->debug_desc
;
1227 if (fcb
== fcb
->Vcb
->volume_fcb
)
1228 return L
"volume FCB";
1230 fcb
->debug_desc
= ExAllocatePoolWithTag(PagedPool
, 60 * sizeof(WCHAR
), ALLOC_TAG
);
1231 if (!fcb
->debug_desc
)
1232 return L
"(memory error)";
1234 // I know this is pretty hackish...
1235 // GCC doesn't like %llx in sprintf, and MSVC won't let us use swprintf
1236 // without the CRT, which breaks drivers.
1238 sprintf(s
, "subvol %x, inode %x", (UINT32
)fcb
->subvol
->id
, (UINT32
)fcb
->inode
);
1241 as
.Length
= as
.MaximumLength
= (USHORT
)strlen(s
);
1243 us
.Buffer
= fcb
->debug_desc
;
1244 us
.MaximumLength
= 60 * sizeof(WCHAR
);
1247 Status
= RtlAnsiStringToUnicodeString(&us
, &as
, FALSE
);
1248 if (!NT_SUCCESS(Status
))
1249 return L
"(RtlAnsiStringToUnicodeString error)";
1251 us
.Buffer
[us
.Length
/ sizeof(WCHAR
)] = 0;
1253 return fcb
->debug_desc
;
1256 WCHAR
* file_desc_fileref(_In_ file_ref
* fileref
) {
1261 if (fileref
->debug_desc
)
1262 return fileref
->debug_desc
;
1264 fn
.Length
= fn
.MaximumLength
= 0;
1265 Status
= fileref_get_filename(fileref
, &fn
, NULL
, &reqlen
);
1266 if (Status
!= STATUS_BUFFER_OVERFLOW
)
1269 if (reqlen
> 0xffff - sizeof(WCHAR
))
1270 return L
"(too long)";
1272 fileref
->debug_desc
= ExAllocatePoolWithTag(PagedPool
, reqlen
+ sizeof(WCHAR
), ALLOC_TAG
);
1273 if (!fileref
->debug_desc
)
1274 return L
"(memory error)";
1276 fn
.Buffer
= fileref
->debug_desc
;
1278 fn
.MaximumLength
= (USHORT
)(reqlen
+ sizeof(WCHAR
));
1280 Status
= fileref_get_filename(fileref
, &fn
, NULL
, &reqlen
);
1281 if (!NT_SUCCESS(Status
)) {
1282 ExFreePool(fileref
->debug_desc
);
1283 fileref
->debug_desc
= NULL
;
1287 fileref
->debug_desc
[fn
.Length
/ sizeof(WCHAR
)] = 0;
1289 return fileref
->debug_desc
;
1293 WCHAR
* file_desc(_In_ PFILE_OBJECT FileObject
) {
1294 fcb
* fcb
= FileObject
->FsContext
;
1295 ccb
* ccb
= FileObject
->FsContext2
;
1296 file_ref
* fileref
= ccb
? ccb
->fileref
: NULL
;
1299 return file_desc_fileref(fileref
);
1301 return file_desc_fcb(fcb
);
1304 void send_notification_fileref(_In_ file_ref
* fileref
, _In_ ULONG filter_match
, _In_ ULONG action
, _In_opt_ PUNICODE_STRING stream
) {
1309 fcb
* fcb
= fileref
->fcb
;
1311 fn
.Length
= fn
.MaximumLength
= 0;
1312 Status
= fileref_get_filename(fileref
, &fn
, NULL
, &reqlen
);
1313 if (Status
!= STATUS_BUFFER_OVERFLOW
) {
1314 ERR("fileref_get_filename returned %08x\n", Status
);
1318 if (reqlen
> 0xffff) {
1319 WARN("reqlen was too long for FsRtlNotifyFilterReportChange\n");
1323 fn
.Buffer
= ExAllocatePoolWithTag(PagedPool
, reqlen
, ALLOC_TAG
);
1325 ERR("out of memory\n");
1329 fn
.MaximumLength
= (USHORT
)reqlen
;
1332 Status
= fileref_get_filename(fileref
, &fn
, &name_offset
, &reqlen
);
1333 if (!NT_SUCCESS(Status
)) {
1334 ERR("fileref_get_filename returned %08x\n", Status
);
1335 ExFreePool(fn
.Buffer
);
1339 FsRtlNotifyFilterReportChange(fcb
->Vcb
->NotifySync
, &fcb
->Vcb
->DirNotifyList
, (PSTRING
)&fn
, name_offset
,
1340 (PSTRING
)stream
, NULL
, filter_match
, action
, NULL
, NULL
);
1341 ExFreePool(fn
.Buffer
);
1344 void send_notification_fcb(_In_ file_ref
* fileref
, _In_ ULONG filter_match
, _In_ ULONG action
, _In_opt_ PUNICODE_STRING stream
) {
1345 fcb
* fcb
= fileref
->fcb
;
1349 // no point looking for hardlinks if st_nlink == 1
1350 if (fileref
->fcb
->inode_item
.st_nlink
== 1) {
1351 send_notification_fileref(fileref
, filter_match
, action
, stream
);
1355 ExAcquireResourceExclusiveLite(&fcb
->Vcb
->fcb_lock
, TRUE
);
1357 le
= fcb
->hardlinks
.Flink
;
1358 while (le
!= &fcb
->hardlinks
) {
1359 hardlink
* hl
= CONTAINING_RECORD(le
, hardlink
, list_entry
);
1362 Status
= open_fileref_by_inode(fcb
->Vcb
, fcb
->subvol
, hl
->parent
, &parfr
, NULL
);
1364 if (!NT_SUCCESS(Status
))
1365 ERR("open_fileref_by_inode returned %08x\n", Status
);
1366 else if (!parfr
->deleted
) {
1370 fn
.Length
= fn
.MaximumLength
= 0;
1371 Status
= fileref_get_filename(parfr
, &fn
, NULL
, &pathlen
);
1372 if (Status
!= STATUS_BUFFER_OVERFLOW
) {
1373 ERR("fileref_get_filename returned %08x\n", Status
);
1374 free_fileref(fcb
->Vcb
, parfr
);
1378 if (parfr
!= fcb
->Vcb
->root_fileref
)
1379 pathlen
+= sizeof(WCHAR
);
1381 if (pathlen
+ hl
->name
.Length
> 0xffff) {
1382 WARN("pathlen + hl->name.Length was too long for FsRtlNotifyFilterReportChange\n");
1383 free_fileref(fcb
->Vcb
, parfr
);
1387 fn
.MaximumLength
= (USHORT
)(pathlen
+ hl
->name
.Length
);
1388 fn
.Buffer
= ExAllocatePoolWithTag(PagedPool
, fn
.MaximumLength
, ALLOC_TAG
);
1390 ERR("out of memory\n");
1391 free_fileref(fcb
->Vcb
, parfr
);
1395 Status
= fileref_get_filename(parfr
, &fn
, NULL
, NULL
);
1396 if (!NT_SUCCESS(Status
)) {
1397 ERR("fileref_get_filename returned %08x\n", Status
);
1398 free_fileref(fcb
->Vcb
, parfr
);
1399 ExFreePool(fn
.Buffer
);
1403 if (parfr
!= fcb
->Vcb
->root_fileref
) {
1404 fn
.Buffer
[(pathlen
/ sizeof(WCHAR
)) - 1] = '\\';
1405 fn
.Length
+= sizeof(WCHAR
);
1408 RtlCopyMemory(&fn
.Buffer
[pathlen
/ sizeof(WCHAR
)], hl
->name
.Buffer
, hl
->name
.Length
);
1409 fn
.Length
+= hl
->name
.Length
;
1411 FsRtlNotifyFilterReportChange(fcb
->Vcb
->NotifySync
, &fcb
->Vcb
->DirNotifyList
, (PSTRING
)&fn
, (USHORT
)pathlen
,
1412 (PSTRING
)stream
, NULL
, filter_match
, action
, NULL
, NULL
);
1414 ExFreePool(fn
.Buffer
);
1416 free_fileref(fcb
->Vcb
, parfr
);
1422 ExReleaseResourceLite(&fcb
->Vcb
->fcb_lock
);
1425 void mark_fcb_dirty(_In_ fcb
* fcb
) {
1427 #ifdef DEBUG_FCB_REFCOUNTS
1432 #ifdef DEBUG_FCB_REFCOUNTS
1433 rc
= InterlockedIncrement(&fcb
->refcount
);
1434 WARN("fcb %p: refcount now %i\n", fcb
, rc
);
1436 InterlockedIncrement(&fcb
->refcount
);
1439 ExAcquireResourceExclusiveLite(&fcb
->Vcb
->dirty_fcbs_lock
, TRUE
);
1440 InsertTailList(&fcb
->Vcb
->dirty_fcbs
, &fcb
->list_entry_dirty
);
1441 ExReleaseResourceLite(&fcb
->Vcb
->dirty_fcbs_lock
);
1444 fcb
->Vcb
->need_write
= TRUE
;
1447 void mark_fileref_dirty(_In_ file_ref
* fileref
) {
1448 if (!fileref
->dirty
) {
1449 fileref
->dirty
= TRUE
;
1450 increase_fileref_refcount(fileref
);
1452 ExAcquireResourceExclusiveLite(&fileref
->fcb
->Vcb
->dirty_filerefs_lock
, TRUE
);
1453 InsertTailList(&fileref
->fcb
->Vcb
->dirty_filerefs
, &fileref
->list_entry_dirty
);
1454 ExReleaseResourceLite(&fileref
->fcb
->Vcb
->dirty_filerefs_lock
);
1457 fileref
->fcb
->Vcb
->need_write
= TRUE
;
1460 #ifdef DEBUG_FCB_REFCOUNTS
1461 void _free_fcb(_Requires_exclusive_lock_held_(_Curr_
->fcb_lock
) _In_ device_extension
* Vcb
, _Inout_ fcb
* fcb
, _In_
const char* func
) {
1463 void free_fcb(_Requires_exclusive_lock_held_(_Curr_
->fcb_lock
) _In_ device_extension
* Vcb
, _Inout_ fcb
* fcb
) {
1467 rc
= InterlockedDecrement(&fcb
->refcount
);
1469 #ifdef DEBUG_FCB_REFCOUNTS
1470 #ifdef DEBUG_LONG_MESSAGES
1471 ERR("fcb %p: refcount now %i (subvol %llx, inode %llx)\n", fcb
, rc
, fcb
->subvol
? fcb
->subvol
->id
: 0, fcb
->inode
);
1473 ERR("fcb %p: refcount now %i (subvol %llx, inode %llx)\n", fcb
, rc
, fcb
->subvol
? fcb
->subvol
->id
: 0, fcb
->inode
);
1480 if (fcb
->list_entry
.Flink
)
1481 RemoveEntryList(&fcb
->list_entry
);
1483 if (fcb
->list_entry_all
.Flink
)
1484 RemoveEntryList(&fcb
->list_entry_all
);
1486 ExDeleteResourceLite(&fcb
->nonpaged
->resource
);
1487 ExDeleteResourceLite(&fcb
->nonpaged
->paging_resource
);
1488 ExDeleteResourceLite(&fcb
->nonpaged
->dir_children_lock
);
1490 ExFreeToNPagedLookasideList(&Vcb
->fcb_np_lookaside
, fcb
->nonpaged
);
1493 ExFreePool(fcb
->sd
);
1495 if (fcb
->adsxattr
.Buffer
)
1496 ExFreePool(fcb
->adsxattr
.Buffer
);
1498 if (fcb
->reparse_xattr
.Buffer
)
1499 ExFreePool(fcb
->reparse_xattr
.Buffer
);
1501 if (fcb
->ea_xattr
.Buffer
)
1502 ExFreePool(fcb
->ea_xattr
.Buffer
);
1504 if (fcb
->adsdata
.Buffer
)
1505 ExFreePool(fcb
->adsdata
.Buffer
);
1507 if (fcb
->debug_desc
)
1508 ExFreePool(fcb
->debug_desc
);
1510 while (!IsListEmpty(&fcb
->extents
)) {
1511 LIST_ENTRY
* le
= RemoveHeadList(&fcb
->extents
);
1512 extent
* ext
= CONTAINING_RECORD(le
, extent
, list_entry
);
1515 ExFreePool(ext
->csum
);
1520 while (!IsListEmpty(&fcb
->hardlinks
)) {
1521 LIST_ENTRY
* le
= RemoveHeadList(&fcb
->hardlinks
);
1522 hardlink
* hl
= CONTAINING_RECORD(le
, hardlink
, list_entry
);
1524 if (hl
->name
.Buffer
)
1525 ExFreePool(hl
->name
.Buffer
);
1527 if (hl
->utf8
.Buffer
)
1528 ExFreePool(hl
->utf8
.Buffer
);
1533 while (!IsListEmpty(&fcb
->xattrs
)) {
1534 xattr
* xa
= CONTAINING_RECORD(RemoveHeadList(&fcb
->xattrs
), xattr
, list_entry
);
1539 while (!IsListEmpty(&fcb
->dir_children_index
)) {
1540 LIST_ENTRY
* le
= RemoveHeadList(&fcb
->dir_children_index
);
1541 dir_child
* dc
= CONTAINING_RECORD(le
, dir_child
, list_entry_index
);
1543 ExFreePool(dc
->utf8
.Buffer
);
1544 ExFreePool(dc
->name
.Buffer
);
1545 ExFreePool(dc
->name_uc
.Buffer
);
1550 ExFreePool(fcb
->hash_ptrs
);
1552 if (fcb
->hash_ptrs_uc
)
1553 ExFreePool(fcb
->hash_ptrs_uc
);
1555 FsRtlUninitializeFileLock(&fcb
->lock
);
1557 if (fcb
->pool_type
== NonPagedPool
)
1560 ExFreeToPagedLookasideList(&Vcb
->fcb_lookaside
, fcb
);
1562 #ifdef DEBUG_FCB_REFCOUNTS
1563 #ifdef DEBUG_LONG_MESSAGES
1564 _debug_message(func
, file
, line
, "freeing fcb %p\n", fcb
);
1566 _debug_message(func
, "freeing fcb %p\n", fcb
);
1571 void free_fileref(_Requires_exclusive_lock_held_(_Curr_
->fcb_lock
) _In_ device_extension
* Vcb
, _Inout_ file_ref
* fr
) {
1574 rc
= InterlockedDecrement(&fr
->refcount
);
1576 #ifdef DEBUG_FCB_REFCOUNTS
1577 ERR("fileref %p: refcount now %i\n", fr
, rc
);
1582 ERR("fileref %p: refcount now %i\n", fr
, rc
);
1591 ExAcquireResourceExclusiveLite(&fr
->parent
->nonpaged
->children_lock
, TRUE
);
1593 // FIXME - do we need a file_ref lock?
1595 // FIXME - do delete if needed
1598 ExFreePool(fr
->debug_desc
);
1600 ExDeleteResourceLite(&fr
->nonpaged
->children_lock
);
1601 ExDeleteResourceLite(&fr
->nonpaged
->fileref_lock
);
1603 ExFreeToNPagedLookasideList(&Vcb
->fileref_np_lookaside
, fr
->nonpaged
);
1605 // FIXME - throw error if children not empty
1607 if (fr
->fcb
->fileref
== fr
)
1608 fr
->fcb
->fileref
= NULL
;
1612 fr
->dc
->size
= fr
->fcb
->adsdata
.Length
;
1614 fr
->dc
->fileref
= NULL
;
1617 if (fr
->list_entry
.Flink
)
1618 RemoveEntryList(&fr
->list_entry
);
1621 ExReleaseResourceLite(&fr
->parent
->nonpaged
->children_lock
);
1622 free_fileref(Vcb
, fr
->parent
);
1625 free_fcb(Vcb
, fr
->fcb
);
1627 ExFreeToPagedLookasideList(&Vcb
->fileref_lookaside
, fr
);
1630 static NTSTATUS
close_file(_In_ PFILE_OBJECT FileObject
, _In_ PIRP Irp
) {
1633 file_ref
* fileref
= NULL
;
1635 device_extension
* Vcb
;
1639 TRACE("FileObject = %p\n", FileObject
);
1641 fcb
= FileObject
->FsContext
;
1643 TRACE("FCB was NULL, returning success\n");
1644 return STATUS_SUCCESS
;
1647 open_files
= InterlockedDecrement(&fcb
->Vcb
->open_files
);
1649 ccb
= FileObject
->FsContext2
;
1651 TRACE("close called for %S (fcb == %p)\n", file_desc(FileObject
), fcb
);
1653 // FIXME - make sure notification gets sent if file is being deleted
1656 if (ccb
->query_string
.Buffer
)
1657 RtlFreeUnicodeString(&ccb
->query_string
);
1659 if (ccb
->filename
.Buffer
)
1660 ExFreePool(ccb
->filename
.Buffer
);
1662 // FIXME - use refcounts for fileref
1663 fileref
= ccb
->fileref
;
1665 if (fcb
->Vcb
->running_sends
> 0) {
1666 BOOL send_cancelled
= FALSE
;
1668 ExAcquireResourceExclusiveLite(&fcb
->Vcb
->send_load_lock
, TRUE
);
1671 ccb
->send
->cancelling
= TRUE
;
1672 send_cancelled
= TRUE
;
1673 KeSetEvent(&ccb
->send
->cleared_event
, 0, FALSE
);
1676 ExReleaseResourceLite(&fcb
->Vcb
->send_load_lock
);
1678 if (send_cancelled
) {
1680 ExAcquireResourceExclusiveLite(&fcb
->Vcb
->send_load_lock
, TRUE
);
1681 ExReleaseResourceLite(&fcb
->Vcb
->send_load_lock
);
1689 CcUninitializeCacheMap(FileObject
, NULL
, NULL
);
1691 if (open_files
== 0 && fcb
->Vcb
->removing
) {
1692 uninit(fcb
->Vcb
, FALSE
);
1693 return STATUS_SUCCESS
;
1696 if (!(fcb
->Vcb
->Vpb
->Flags
& VPB_MOUNTED
))
1697 return STATUS_SUCCESS
;
1701 ExAcquireResourceExclusiveLite(&Vcb
->fcb_lock
, TRUE
);
1704 free_fileref(fcb
->Vcb
, fileref
);
1708 ExReleaseResourceLite(&Vcb
->fcb_lock
);
1710 return STATUS_SUCCESS
;
1713 void uninit(_In_ device_extension
* Vcb
, _In_ BOOL flush
) {
1719 if (!Vcb
->removing
) {
1720 ExAcquireResourceExclusiveLite(&Vcb
->tree_lock
, TRUE
);
1721 Vcb
->removing
= TRUE
;
1722 ExReleaseResourceLite(&Vcb
->tree_lock
);
1725 RemoveEntryList(&Vcb
->list_entry
);
1727 if (Vcb
->balance
.thread
) {
1728 Vcb
->balance
.paused
= FALSE
;
1729 Vcb
->balance
.stopping
= TRUE
;
1730 KeSetEvent(&Vcb
->balance
.event
, 0, FALSE
);
1731 KeWaitForSingleObject(&Vcb
->balance
.finished
, Executive
, KernelMode
, FALSE
, NULL
);
1734 if (Vcb
->scrub
.thread
) {
1735 Vcb
->scrub
.paused
= FALSE
;
1736 Vcb
->scrub
.stopping
= TRUE
;
1737 KeSetEvent(&Vcb
->scrub
.event
, 0, FALSE
);
1738 KeWaitForSingleObject(&Vcb
->scrub
.finished
, Executive
, KernelMode
, FALSE
, NULL
);
1741 if (Vcb
->running_sends
!= 0) {
1742 BOOL send_cancelled
= FALSE
;
1744 ExAcquireResourceExclusiveLite(&Vcb
->send_load_lock
, TRUE
);
1746 le
= Vcb
->send_ops
.Flink
;
1747 while (le
!= &Vcb
->send_ops
) {
1748 send_info
* send
= CONTAINING_RECORD(le
, send_info
, list_entry
);
1750 if (!send
->cancelling
) {
1751 send
->cancelling
= TRUE
;
1752 send_cancelled
= TRUE
;
1754 KeSetEvent(&send
->cleared_event
, 0, FALSE
);
1760 ExReleaseResourceLite(&Vcb
->send_load_lock
);
1762 if (send_cancelled
) {
1763 while (Vcb
->running_sends
!= 0) {
1764 ExAcquireResourceExclusiveLite(&Vcb
->send_load_lock
, TRUE
);
1765 ExReleaseResourceLite(&Vcb
->send_load_lock
);
1770 Status
= registry_mark_volume_unmounted(&Vcb
->superblock
.uuid
);
1771 if (!NT_SUCCESS(Status
) && Status
!= STATUS_TOO_LATE
)
1772 WARN("registry_mark_volume_unmounted returned %08x\n", Status
);
1775 ExAcquireResourceExclusiveLite(&Vcb
->tree_lock
, TRUE
);
1777 if (Vcb
->need_write
&& !Vcb
->readonly
) {
1778 Status
= do_write(Vcb
, NULL
);
1779 if (!NT_SUCCESS(Status
))
1780 ERR("do_write returned %08x\n", Status
);
1785 ExReleaseResourceLite(&Vcb
->tree_lock
);
1788 for (i
= 0; i
< Vcb
->calcthreads
.num_threads
; i
++) {
1789 Vcb
->calcthreads
.threads
[i
].quit
= TRUE
;
1792 KeSetEvent(&Vcb
->calcthreads
.event
, 0, FALSE
);
1794 for (i
= 0; i
< Vcb
->calcthreads
.num_threads
; i
++) {
1795 KeWaitForSingleObject(&Vcb
->calcthreads
.threads
[i
].finished
, Executive
, KernelMode
, FALSE
, NULL
);
1797 ZwClose(Vcb
->calcthreads
.threads
[i
].handle
);
1800 ExDeleteResourceLite(&Vcb
->calcthreads
.lock
);
1801 ExFreePool(Vcb
->calcthreads
.threads
);
1804 KeSetTimer(&Vcb
->flush_thread_timer
, time
, NULL
); // trigger the timer early
1805 KeWaitForSingleObject(&Vcb
->flush_thread_finished
, Executive
, KernelMode
, FALSE
, NULL
);
1807 ExAcquireResourceExclusiveLite(&Vcb
->fcb_lock
, TRUE
);
1808 free_fcb(Vcb
, Vcb
->volume_fcb
);
1809 free_fcb(Vcb
, Vcb
->dummy_fcb
);
1810 ExReleaseResourceLite(&Vcb
->fcb_lock
);
1813 ObDereferenceObject(Vcb
->root_file
);
1815 le
= Vcb
->chunks
.Flink
;
1816 while (le
!= &Vcb
->chunks
) {
1817 chunk
* c
= CONTAINING_RECORD(le
, chunk
, list_entry
);
1820 ExAcquireResourceExclusiveLite(&Vcb
->fcb_lock
, TRUE
);
1821 free_fcb(Vcb
, c
->cache
);
1822 ExReleaseResourceLite(&Vcb
->fcb_lock
);
1829 while (!IsListEmpty(&Vcb
->roots
)) {
1830 root
* r
= CONTAINING_RECORD(RemoveHeadList(&Vcb
->roots
), root
, list_entry
);
1832 ExDeleteResourceLite(&r
->nonpaged
->load_tree_lock
);
1833 ExFreePool(r
->nonpaged
);
1837 while (!IsListEmpty(&Vcb
->chunks
)) {
1838 chunk
* c
= CONTAINING_RECORD(RemoveHeadList(&Vcb
->chunks
), chunk
, list_entry
);
1840 while (!IsListEmpty(&c
->space
)) {
1841 LIST_ENTRY
* le2
= RemoveHeadList(&c
->space
);
1842 space
* s
= CONTAINING_RECORD(le2
, space
, list_entry
);
1847 while (!IsListEmpty(&c
->deleting
)) {
1848 LIST_ENTRY
* le2
= RemoveHeadList(&c
->deleting
);
1849 space
* s
= CONTAINING_RECORD(le2
, space
, list_entry
);
1855 ExFreePool(c
->devices
);
1858 ExAcquireResourceExclusiveLite(&Vcb
->fcb_lock
, TRUE
);
1859 free_fcb(Vcb
, c
->cache
);
1860 ExReleaseResourceLite(&Vcb
->fcb_lock
);
1863 ExDeleteResourceLite(&c
->range_locks_lock
);
1864 ExDeleteResourceLite(&c
->partial_stripes_lock
);
1865 ExDeleteResourceLite(&c
->lock
);
1866 ExDeleteResourceLite(&c
->changed_extents_lock
);
1868 ExFreePool(c
->chunk_item
);
1872 // FIXME - free any open fcbs?
1874 while (!IsListEmpty(&Vcb
->devices
)) {
1875 device
* dev
= CONTAINING_RECORD(RemoveHeadList(&Vcb
->devices
), device
, list_entry
);
1877 while (!IsListEmpty(&dev
->space
)) {
1878 LIST_ENTRY
* le2
= RemoveHeadList(&dev
->space
);
1879 space
* s
= CONTAINING_RECORD(le2
, space
, list_entry
);
1887 ExAcquireResourceExclusiveLite(&Vcb
->scrub
.stats_lock
, TRUE
);
1888 while (!IsListEmpty(&Vcb
->scrub
.errors
)) {
1889 scrub_error
* err
= CONTAINING_RECORD(RemoveHeadList(&Vcb
->scrub
.errors
), scrub_error
, list_entry
);
1893 ExReleaseResourceLite(&Vcb
->scrub
.stats_lock
);
1895 ExDeleteResourceLite(&Vcb
->fcb_lock
);
1896 ExDeleteResourceLite(&Vcb
->load_lock
);
1897 ExDeleteResourceLite(&Vcb
->tree_lock
);
1898 ExDeleteResourceLite(&Vcb
->chunk_lock
);
1899 ExDeleteResourceLite(&Vcb
->dirty_fcbs_lock
);
1900 ExDeleteResourceLite(&Vcb
->dirty_filerefs_lock
);
1901 ExDeleteResourceLite(&Vcb
->dirty_subvols_lock
);
1902 ExDeleteResourceLite(&Vcb
->scrub
.stats_lock
);
1903 ExDeleteResourceLite(&Vcb
->send_load_lock
);
1905 ExDeletePagedLookasideList(&Vcb
->tree_data_lookaside
);
1906 ExDeletePagedLookasideList(&Vcb
->traverse_ptr_lookaside
);
1907 ExDeletePagedLookasideList(&Vcb
->batch_item_lookaside
);
1908 ExDeletePagedLookasideList(&Vcb
->fileref_lookaside
);
1909 ExDeletePagedLookasideList(&Vcb
->fcb_lookaside
);
1910 ExDeletePagedLookasideList(&Vcb
->name_bit_lookaside
);
1911 ExDeleteNPagedLookasideList(&Vcb
->range_lock_lookaside
);
1912 ExDeleteNPagedLookasideList(&Vcb
->fileref_np_lookaside
);
1913 ExDeleteNPagedLookasideList(&Vcb
->fcb_np_lookaside
);
1915 ZwClose(Vcb
->flush_thread_handle
);
1918 NTSTATUS
delete_fileref(_In_ file_ref
* fileref
, _In_ PFILE_OBJECT FileObject
, _In_opt_ PIRP Irp
, _In_ LIST_ENTRY
* rollback
) {
1919 LARGE_INTEGER newlength
, time
;
1924 KeQuerySystemTime(&time
);
1925 win_time_to_unix(time
, &now
);
1927 ExAcquireResourceExclusiveLite(fileref
->fcb
->Header
.Resource
, TRUE
);
1929 if (fileref
->deleted
) {
1930 ExReleaseResourceLite(fileref
->fcb
->Header
.Resource
);
1931 return STATUS_SUCCESS
;
1934 if (fileref
->fcb
->subvol
->send_ops
> 0) {
1935 ExReleaseResourceLite(fileref
->fcb
->Header
.Resource
);
1936 return STATUS_ACCESS_DENIED
;
1939 fileref
->deleted
= TRUE
;
1940 mark_fileref_dirty(fileref
);
1942 // delete INODE_ITEM (0x1)
1944 TRACE("nlink = %u\n", fileref
->fcb
->inode_item
.st_nlink
);
1946 if (!fileref
->fcb
->ads
) {
1947 if (fileref
->parent
->fcb
->subvol
== fileref
->fcb
->subvol
) {
1950 mark_fcb_dirty(fileref
->fcb
);
1952 fileref
->fcb
->inode_item_changed
= TRUE
;
1954 if (fileref
->fcb
->inode_item
.st_nlink
> 1) {
1955 fileref
->fcb
->inode_item
.st_nlink
--;
1956 fileref
->fcb
->inode_item
.transid
= fileref
->fcb
->Vcb
->superblock
.generation
;
1957 fileref
->fcb
->inode_item
.sequence
++;
1958 fileref
->fcb
->inode_item
.st_ctime
= now
;
1962 if (fileref
->fcb
->type
!= BTRFS_TYPE_DIRECTORY
&& fileref
->fcb
->inode_item
.st_size
> 0) {
1963 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
);
1964 if (!NT_SUCCESS(Status
)) {
1965 ERR("excise_extents returned %08x\n", Status
);
1966 ExReleaseResourceLite(fileref
->fcb
->Header
.Resource
);
1971 fileref
->fcb
->Header
.AllocationSize
.QuadPart
= 0;
1972 fileref
->fcb
->Header
.FileSize
.QuadPart
= 0;
1973 fileref
->fcb
->Header
.ValidDataLength
.QuadPart
= 0;
1978 ccfs
.AllocationSize
= fileref
->fcb
->Header
.AllocationSize
;
1979 ccfs
.FileSize
= fileref
->fcb
->Header
.FileSize
;
1980 ccfs
.ValidDataLength
= fileref
->fcb
->Header
.ValidDataLength
;
1982 Status
= STATUS_SUCCESS
;
1985 CcSetFileSizes(FileObject
, &ccfs
);
1986 } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER
) {
1987 Status
= _SEH2_GetExceptionCode();
1990 if (!NT_SUCCESS(Status
)) {
1991 ERR("CcSetFileSizes threw exception %08x\n", Status
);
1992 ExReleaseResourceLite(fileref
->fcb
->Header
.Resource
);
1997 fileref
->fcb
->deleted
= TRUE
;
2001 le
= fileref
->fcb
->hardlinks
.Flink
;
2002 while (le
!= &fileref
->fcb
->hardlinks
) {
2003 hardlink
* hl
= CONTAINING_RECORD(le
, hardlink
, list_entry
);
2005 if (hl
->parent
== fileref
->parent
->fcb
->inode
&& hl
->index
== fileref
->dc
->index
) {
2006 RemoveEntryList(&hl
->list_entry
);
2008 if (hl
->name
.Buffer
)
2009 ExFreePool(hl
->name
.Buffer
);
2011 if (hl
->utf8
.Buffer
)
2012 ExFreePool(hl
->utf8
.Buffer
);
2021 } else if (fileref
->fcb
->subvol
->parent
== fileref
->parent
->fcb
->subvol
->id
) { // valid subvolume
2022 if (fileref
->fcb
->subvol
->root_item
.num_references
> 1) {
2023 fileref
->fcb
->subvol
->root_item
.num_references
--;
2025 mark_fcb_dirty(fileref
->fcb
); // so ROOT_ITEM gets updated
2027 // FIXME - we need a lock here
2029 RemoveEntryList(&fileref
->fcb
->subvol
->list_entry
);
2031 InsertTailList(&fileref
->fcb
->Vcb
->drop_roots
, &fileref
->fcb
->subvol
->list_entry
);
2035 fileref
->fcb
->deleted
= TRUE
;
2036 mark_fcb_dirty(fileref
->fcb
);
2039 // remove dir_child from parent
2042 TRACE("delete file %.*S\n", fileref
->dc
->name
.Length
/ sizeof(WCHAR
), fileref
->dc
->name
.Buffer
);
2044 ExAcquireResourceExclusiveLite(&fileref
->parent
->fcb
->nonpaged
->dir_children_lock
, TRUE
);
2045 RemoveEntryList(&fileref
->dc
->list_entry_index
);
2047 if (!fileref
->fcb
->ads
)
2048 remove_dir_child_from_hash_lists(fileref
->parent
->fcb
, fileref
->dc
);
2050 ExReleaseResourceLite(&fileref
->parent
->fcb
->nonpaged
->dir_children_lock
);
2052 if (!fileref
->oldutf8
.Buffer
)
2053 fileref
->oldutf8
= fileref
->dc
->utf8
;
2055 ExFreePool(fileref
->dc
->utf8
.Buffer
);
2057 utf8len
= fileref
->dc
->utf8
.Length
;
2059 fileref
->oldindex
= fileref
->dc
->index
;
2061 ExFreePool(fileref
->dc
->name
.Buffer
);
2062 ExFreePool(fileref
->dc
->name_uc
.Buffer
);
2063 ExFreePool(fileref
->dc
);
2068 // update INODE_ITEM of parent
2070 ExAcquireResourceExclusiveLite(fileref
->parent
->fcb
->Header
.Resource
, TRUE
);
2072 fileref
->parent
->fcb
->inode_item
.transid
= fileref
->fcb
->Vcb
->superblock
.generation
;
2073 fileref
->parent
->fcb
->inode_item
.sequence
++;
2074 fileref
->parent
->fcb
->inode_item
.st_ctime
= now
;
2076 if (!fileref
->fcb
->ads
) {
2077 TRACE("fileref->parent->fcb->inode_item.st_size (inode %llx) was %llx\n", fileref
->parent
->fcb
->inode
, fileref
->parent
->fcb
->inode_item
.st_size
);
2078 fileref
->parent
->fcb
->inode_item
.st_size
-= utf8len
* 2;
2079 TRACE("fileref->parent->fcb->inode_item.st_size (inode %llx) now %llx\n", fileref
->parent
->fcb
->inode
, fileref
->parent
->fcb
->inode_item
.st_size
);
2080 fileref
->parent
->fcb
->inode_item
.st_mtime
= now
;
2083 fileref
->parent
->fcb
->inode_item_changed
= TRUE
;
2084 ExReleaseResourceLite(fileref
->parent
->fcb
->Header
.Resource
);
2086 if (!fileref
->fcb
->ads
&& fileref
->parent
->dc
)
2087 send_notification_fcb(fileref
->parent
, FILE_NOTIFY_CHANGE_LAST_WRITE
, FILE_ACTION_MODIFIED
, NULL
);
2089 mark_fcb_dirty(fileref
->parent
->fcb
);
2091 fileref
->fcb
->subvol
->root_item
.ctransid
= fileref
->fcb
->Vcb
->superblock
.generation
;
2092 fileref
->fcb
->subvol
->root_item
.ctime
= now
;
2094 newlength
.QuadPart
= 0;
2096 if (FileObject
&& !CcUninitializeCacheMap(FileObject
, &newlength
, NULL
))
2097 TRACE("CcUninitializeCacheMap failed\n");
2099 ExReleaseResourceLite(fileref
->fcb
->Header
.Resource
);
2101 return STATUS_SUCCESS
;
2104 _Dispatch_type_(IRP_MJ_CLEANUP
)
2105 _Function_class_(DRIVER_DISPATCH
)
2106 static NTSTATUS
drv_cleanup(_In_ PDEVICE_OBJECT DeviceObject
, _In_ PIRP Irp
) {
2108 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
2109 PFILE_OBJECT FileObject
= IrpSp
->FileObject
;
2110 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
2111 fcb
* fcb
= FileObject
->FsContext
;
2114 FsRtlEnterFileSystem();
2118 top_level
= is_top_level(Irp
);
2120 if (Vcb
&& Vcb
->type
== VCB_TYPE_VOLUME
) {
2121 Status
= vol_cleanup(DeviceObject
, Irp
);
2123 } else if (DeviceObject
== master_devobj
) {
2124 TRACE("closing file system\n");
2125 Status
= STATUS_SUCCESS
;
2127 } else if (!Vcb
|| Vcb
->type
!= VCB_TYPE_FS
) {
2128 Status
= STATUS_INVALID_PARAMETER
;
2132 if (FileObject
->Flags
& FO_CLEANUP_COMPLETE
) {
2133 TRACE("FileObject %p already cleaned up\n", FileObject
);
2134 Status
= STATUS_SUCCESS
;
2139 ERR("fcb was NULL\n");
2140 Status
= STATUS_INVALID_PARAMETER
;
2144 // We have to use the pointer to Vcb stored in the fcb, as we can receive cleanup
2145 // messages belonging to other devices.
2147 if (FileObject
&& FileObject
->FsContext
) {
2153 ccb
= FileObject
->FsContext2
;
2154 fileref
= ccb
? ccb
->fileref
: NULL
;
2156 TRACE("cleanup called for FileObject %p\n", FileObject
);
2157 TRACE("fileref %p (%S), refcount = %u, open_count = %u\n", fileref
, file_desc(FileObject
), fileref
? fileref
->refcount
: 0, fileref
? fileref
->open_count
: 0);
2159 ExAcquireResourceSharedLite(&fcb
->Vcb
->tree_lock
, TRUE
);
2161 ExAcquireResourceExclusiveLite(fcb
->Header
.Resource
, TRUE
);
2163 IoRemoveShareAccess(FileObject
, &fcb
->share_access
);
2166 FsRtlNotifyCleanup(fcb
->Vcb
->NotifySync
, &fcb
->Vcb
->DirNotifyList
, ccb
);
2169 oc
= InterlockedDecrement(&fileref
->open_count
);
2170 #ifdef DEBUG_FCB_REFCOUNTS
2171 ERR("fileref %p: open_count now %i\n", fileref
, oc
);
2175 if (ccb
&& ccb
->options
& FILE_DELETE_ON_CLOSE
&& fileref
)
2176 fileref
->delete_on_close
= TRUE
;
2178 if (fileref
&& fileref
->delete_on_close
&& fcb
->type
== BTRFS_TYPE_DIRECTORY
&& fcb
->inode_item
.st_size
> 0 && fcb
!= fcb
->Vcb
->dummy_fcb
)
2179 fileref
->delete_on_close
= FALSE
;
2181 if (fcb
->Vcb
->locked
&& fcb
->Vcb
->locked_fileobj
== FileObject
) {
2182 TRACE("unlocking volume\n");
2183 do_unlock_volume(fcb
->Vcb
);
2184 FsRtlNotifyVolumeEvent(FileObject
, FSRTL_VOLUME_UNLOCK
);
2187 if (ccb
&& ccb
->reserving
) {
2188 fcb
->subvol
->reserved
= NULL
;
2189 ccb
->reserving
= FALSE
;
2190 // FIXME - flush all of subvol's fcbs
2193 if (fileref
&& oc
== 0) {
2194 if (!fcb
->Vcb
->removing
) {
2195 if (fileref
&& fileref
->delete_on_close
&& fileref
!= fcb
->Vcb
->root_fileref
&& fcb
!= fcb
->Vcb
->volume_fcb
) {
2196 LIST_ENTRY rollback
;
2198 InitializeListHead(&rollback
);
2200 if (!fileref
->fcb
->ads
|| fileref
->dc
) {
2201 if (fileref
->fcb
->ads
) {
2202 send_notification_fileref(fileref
->parent
, fcb
->type
== BTRFS_TYPE_DIRECTORY
? FILE_NOTIFY_CHANGE_DIR_NAME
: FILE_NOTIFY_CHANGE_FILE_NAME
,
2203 FILE_ACTION_REMOVED
, &fileref
->dc
->name
);
2205 send_notification_fileref(fileref
, fcb
->type
== BTRFS_TYPE_DIRECTORY
? FILE_NOTIFY_CHANGE_DIR_NAME
: FILE_NOTIFY_CHANGE_FILE_NAME
, FILE_ACTION_REMOVED
, NULL
);
2208 ExReleaseResourceLite(fcb
->Header
.Resource
);
2211 // fcb_lock needs to be acquired before fcb->Header.Resource
2212 ExAcquireResourceExclusiveLite(&fcb
->Vcb
->fcb_lock
, TRUE
);
2214 Status
= delete_fileref(fileref
, FileObject
, Irp
, &rollback
);
2215 if (!NT_SUCCESS(Status
)) {
2216 ERR("delete_fileref returned %08x\n", Status
);
2217 do_rollback(fcb
->Vcb
, &rollback
);
2218 ExReleaseResourceLite(&fcb
->Vcb
->fcb_lock
);
2219 ExReleaseResourceLite(&fcb
->Vcb
->tree_lock
);
2223 ExReleaseResourceLite(&fcb
->Vcb
->fcb_lock
);
2227 clear_rollback(&rollback
);
2228 } else if (FileObject
->Flags
& FO_CACHE_SUPPORTED
&& fcb
->nonpaged
->segment_object
.DataSectionObject
) {
2229 IO_STATUS_BLOCK iosb
;
2230 CcFlushCache(FileObject
->SectionObjectPointer
, NULL
, 0, &iosb
);
2232 if (!NT_SUCCESS(iosb
.Status
)) {
2233 ERR("CcFlushCache returned %08x\n", iosb
.Status
);
2236 if (!ExIsResourceAcquiredSharedLite(fcb
->Header
.PagingIoResource
)) {
2237 ExAcquireResourceExclusiveLite(fcb
->Header
.PagingIoResource
, TRUE
);
2238 ExReleaseResourceLite(fcb
->Header
.PagingIoResource
);
2241 CcPurgeCacheSection(&fcb
->nonpaged
->segment_object
, NULL
, 0, FALSE
);
2243 TRACE("flushed cache on close (FileObject = %p, fcb = %p, AllocationSize = %llx, FileSize = %llx, ValidDataLength = %llx)\n",
2244 FileObject
, fcb
, fcb
->Header
.AllocationSize
.QuadPart
, fcb
->Header
.FileSize
.QuadPart
, fcb
->Header
.ValidDataLength
.QuadPart
);
2248 if (fcb
->Vcb
&& fcb
!= fcb
->Vcb
->volume_fcb
)
2249 CcUninitializeCacheMap(FileObject
, NULL
, NULL
);
2253 ExReleaseResourceLite(fcb
->Header
.Resource
);
2255 ExReleaseResourceLite(&fcb
->Vcb
->tree_lock
);
2257 FileObject
->Flags
|= FO_CLEANUP_COMPLETE
;
2260 Status
= STATUS_SUCCESS
;
2263 TRACE("returning %08x\n", Status
);
2265 Irp
->IoStatus
.Status
= Status
;
2266 Irp
->IoStatus
.Information
= 0;
2268 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
2271 IoSetTopLevelIrp(NULL
);
2273 FsRtlExitFileSystem();
2279 BOOL
get_file_attributes_from_xattr(_In_reads_bytes_(len
) char* val
, _In_ UINT16 len
, _Out_ ULONG
* atts
) {
2280 if (len
> 2 && val
[0] == '0' && val
[1] == 'x') {
2284 for (i
= 2; i
< len
; i
++) {
2287 if (val
[i
] >= '0' && val
[i
] <= '9')
2288 dosnum
|= val
[i
] - '0';
2289 else if (val
[i
] >= 'a' && val
[i
] <= 'f')
2290 dosnum
|= val
[i
] + 10 - 'a';
2291 else if (val
[i
] >= 'A' && val
[i
] <= 'F')
2292 dosnum
|= val
[i
] + 10 - 'a';
2295 TRACE("DOSATTRIB: %08x\n", dosnum
);
2305 ULONG
get_file_attributes(_In_
_Requires_lock_held_(_Curr_
->tree_lock
) device_extension
* Vcb
, _In_ root
* r
, _In_ UINT64 inode
,
2306 _In_ UINT8 type
, _In_ BOOL dotfile
, _In_ BOOL ignore_xa
, _In_opt_ PIRP Irp
) {
2311 if (!ignore_xa
&& get_xattr(Vcb
, r
, inode
, EA_DOSATTRIB
, EA_DOSATTRIB_HASH
, (UINT8
**)&eaval
, &ealen
, Irp
)) {
2314 if (get_file_attributes_from_xattr(eaval
, ealen
, &dosnum
)) {
2317 if (type
== BTRFS_TYPE_DIRECTORY
)
2318 dosnum
|= FILE_ATTRIBUTE_DIRECTORY
;
2319 else if (type
== BTRFS_TYPE_SYMLINK
)
2320 dosnum
|= FILE_ATTRIBUTE_REPARSE_POINT
;
2322 if (type
!= BTRFS_TYPE_DIRECTORY
)
2323 dosnum
&= ~FILE_ATTRIBUTE_DIRECTORY
;
2325 if (inode
== SUBVOL_ROOT_INODE
) {
2326 if (r
->root_item
.flags
& BTRFS_SUBVOL_READONLY
)
2327 dosnum
|= FILE_ATTRIBUTE_READONLY
;
2329 dosnum
&= ~FILE_ATTRIBUTE_READONLY
;
2339 case BTRFS_TYPE_DIRECTORY
:
2340 att
= FILE_ATTRIBUTE_DIRECTORY
;
2343 case BTRFS_TYPE_SYMLINK
:
2344 att
= FILE_ATTRIBUTE_REPARSE_POINT
;
2353 att
|= FILE_ATTRIBUTE_HIDDEN
;
2356 att
|= FILE_ATTRIBUTE_ARCHIVE
;
2358 if (inode
== SUBVOL_ROOT_INODE
) {
2359 if (r
->root_item
.flags
& BTRFS_SUBVOL_READONLY
)
2360 att
|= FILE_ATTRIBUTE_READONLY
;
2362 att
&= ~FILE_ATTRIBUTE_READONLY
;
2365 // FIXME - get READONLY from ii->st_mode
2366 // FIXME - return SYSTEM for block/char devices?
2369 att
= FILE_ATTRIBUTE_NORMAL
;
2374 NTSTATUS
sync_read_phys(_In_ PDEVICE_OBJECT DeviceObject
, _In_ UINT64 StartingOffset
, _In_ ULONG Length
,
2375 _Out_writes_bytes_(Length
) PUCHAR Buffer
, _In_ BOOL override
) {
2376 IO_STATUS_BLOCK IoStatus
;
2377 LARGE_INTEGER Offset
;
2379 PIO_STACK_LOCATION IrpSp
;
2381 read_context context
;
2385 RtlZeroMemory(&context
, sizeof(read_context
));
2386 KeInitializeEvent(&context
.Event
, NotificationEvent
, FALSE
);
2388 Offset
.QuadPart
= (LONGLONG
)StartingOffset
;
2390 Irp
= IoAllocateIrp(DeviceObject
->StackSize
, FALSE
);
2393 ERR("IoAllocateIrp failed\n");
2394 return STATUS_INSUFFICIENT_RESOURCES
;
2397 Irp
->Flags
|= IRP_NOCACHE
;
2398 IrpSp
= IoGetNextIrpStackLocation(Irp
);
2399 IrpSp
->MajorFunction
= IRP_MJ_READ
;
2402 IrpSp
->Flags
|= SL_OVERRIDE_VERIFY_VOLUME
;
2404 if (DeviceObject
->Flags
& DO_BUFFERED_IO
) {
2405 Irp
->AssociatedIrp
.SystemBuffer
= ExAllocatePoolWithTag(NonPagedPool
, Length
, ALLOC_TAG
);
2406 if (!Irp
->AssociatedIrp
.SystemBuffer
) {
2407 ERR("out of memory\n");
2408 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2412 Irp
->Flags
|= IRP_BUFFERED_IO
| IRP_DEALLOCATE_BUFFER
| IRP_INPUT_OPERATION
;
2414 Irp
->UserBuffer
= Buffer
;
2415 } else if (DeviceObject
->Flags
& DO_DIRECT_IO
) {
2416 Irp
->MdlAddress
= IoAllocateMdl(Buffer
, Length
, FALSE
, FALSE
, NULL
);
2417 if (!Irp
->MdlAddress
) {
2418 ERR("IoAllocateMdl failed\n");
2419 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2423 Status
= STATUS_SUCCESS
;
2426 MmProbeAndLockPages(Irp
->MdlAddress
, KernelMode
, IoWriteAccess
);
2427 } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER
) {
2428 Status
= _SEH2_GetExceptionCode();
2431 if (!NT_SUCCESS(Status
)) {
2432 ERR("MmProbeAndLockPages threw exception %08x\n", Status
);
2433 IoFreeMdl(Irp
->MdlAddress
);
2437 Irp
->UserBuffer
= Buffer
;
2439 IrpSp
->Parameters
.Read
.Length
= Length
;
2440 IrpSp
->Parameters
.Read
.ByteOffset
= Offset
;
2442 Irp
->UserIosb
= &IoStatus
;
2444 Irp
->UserEvent
= &context
.Event
;
2446 IoSetCompletionRoutine(Irp
, read_completion
, &context
, TRUE
, TRUE
, TRUE
);
2448 Status
= IoCallDriver(DeviceObject
, Irp
);
2450 if (Status
== STATUS_PENDING
) {
2451 KeWaitForSingleObject(&context
.Event
, Executive
, KernelMode
, FALSE
, NULL
);
2452 Status
= context
.iosb
.Status
;
2455 if (DeviceObject
->Flags
& DO_DIRECT_IO
) {
2456 MmUnlockPages(Irp
->MdlAddress
);
2457 IoFreeMdl(Irp
->MdlAddress
);
2466 static NTSTATUS
read_superblock(_In_ device_extension
* Vcb
, _In_ PDEVICE_OBJECT device
, _In_ UINT64 length
) {
2470 UINT8 valid_superblocks
;
2472 to_read
= device
->SectorSize
== 0 ? sizeof(superblock
) : (ULONG
)sector_align(sizeof(superblock
), device
->SectorSize
);
2474 sb
= ExAllocatePoolWithTag(NonPagedPool
, to_read
, ALLOC_TAG
);
2476 ERR("out of memory\n");
2477 return STATUS_INSUFFICIENT_RESOURCES
;
2480 if (superblock_addrs
[0] + to_read
> length
) {
2481 WARN("device was too short to have any superblock\n");
2483 return STATUS_UNRECOGNIZED_VOLUME
;
2487 valid_superblocks
= 0;
2489 while (superblock_addrs
[i
] > 0) {
2492 if (i
> 0 && superblock_addrs
[i
] + to_read
> length
)
2495 Status
= sync_read_phys(device
, superblock_addrs
[i
], to_read
, (PUCHAR
)sb
, FALSE
);
2496 if (!NT_SUCCESS(Status
)) {
2497 ERR("Failed to read superblock %u: %08x\n", i
, Status
);
2502 if (sb
->magic
!= BTRFS_MAGIC
) {
2504 TRACE("not a BTRFS volume\n");
2506 return STATUS_UNRECOGNIZED_VOLUME
;
2509 TRACE("got superblock %u!\n", i
);
2511 crc32
= ~calc_crc32c(0xffffffff, (UINT8
*)&sb
->uuid
, (ULONG
)sizeof(superblock
) - sizeof(sb
->checksum
));
2513 if (crc32
!= *((UINT32
*)sb
->checksum
))
2514 WARN("crc32 was %08x, expected %08x\n", crc32
, *((UINT32
*)sb
->checksum
));
2515 else if (sb
->sector_size
== 0)
2516 WARN("superblock sector size was 0\n");
2517 else if (sb
->node_size
< sizeof(tree_header
) + sizeof(internal_node
) || sb
->node_size
> 0x10000)
2518 WARN("invalid node size %x\n", sb
->node_size
);
2519 else if ((sb
->node_size
% sb
->sector_size
) != 0)
2520 WARN("node size %x was not a multiple of sector_size %x\n", sb
->node_size
, sb
->sector_size
);
2521 else if (valid_superblocks
== 0 || sb
->generation
> Vcb
->superblock
.generation
) {
2522 RtlCopyMemory(&Vcb
->superblock
, sb
, sizeof(superblock
));
2523 valid_superblocks
++;
2532 if (valid_superblocks
== 0) {
2533 ERR("could not find any valid superblocks\n");
2534 return STATUS_INTERNAL_ERROR
;
2537 TRACE("label is %s\n", Vcb
->superblock
.label
);
2539 return STATUS_SUCCESS
;
2542 NTSTATUS
dev_ioctl(_In_ PDEVICE_OBJECT DeviceObject
, _In_ ULONG ControlCode
, _In_reads_bytes_opt_(InputBufferSize
) PVOID InputBuffer
, _In_ ULONG InputBufferSize
,
2543 _Out_writes_bytes_opt_(OutputBufferSize
) PVOID OutputBuffer
, _In_ ULONG OutputBufferSize
, _In_ BOOLEAN Override
, _Out_opt_ IO_STATUS_BLOCK
* iosb
) {
2547 PIO_STACK_LOCATION IrpSp
;
2548 IO_STATUS_BLOCK IoStatus
;
2550 KeInitializeEvent(&Event
, NotificationEvent
, FALSE
);
2552 Irp
= IoBuildDeviceIoControlRequest(ControlCode
,
2562 if (!Irp
) return STATUS_INSUFFICIENT_RESOURCES
;
2565 IrpSp
= IoGetNextIrpStackLocation(Irp
);
2566 IrpSp
->Flags
|= SL_OVERRIDE_VERIFY_VOLUME
;
2569 Status
= IoCallDriver(DeviceObject
, Irp
);
2571 if (Status
== STATUS_PENDING
) {
2572 KeWaitForSingleObject(&Event
, Executive
, KernelMode
, FALSE
, NULL
);
2573 Status
= IoStatus
.Status
;
2582 _Requires_exclusive_lock_held_(Vcb
->tree_lock
)
2583 static NTSTATUS
add_root(_Inout_ device_extension
* Vcb
, _In_ UINT64 id
, _In_ UINT64 addr
,
2584 _In_ UINT64 generation
, _In_opt_ traverse_ptr
* tp
) {
2585 root
* r
= ExAllocatePoolWithTag(PagedPool
, sizeof(root
), ALLOC_TAG
);
2587 ERR("out of memory\n");
2588 return STATUS_INSUFFICIENT_RESOURCES
;
2593 r
->received
= FALSE
;
2595 r
->treeholder
.address
= addr
;
2596 r
->treeholder
.tree
= NULL
;
2597 r
->treeholder
.generation
= generation
;
2600 InitializeListHead(&r
->fcbs
);
2602 r
->nonpaged
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(root_nonpaged
), ALLOC_TAG
);
2604 ERR("out of memory\n");
2606 return STATUS_INSUFFICIENT_RESOURCES
;
2609 ExInitializeResourceLite(&r
->nonpaged
->load_tree_lock
);
2614 RtlCopyMemory(&r
->root_item
, tp
->item
->data
, min(sizeof(ROOT_ITEM
), tp
->item
->size
));
2615 if (tp
->item
->size
< sizeof(ROOT_ITEM
))
2616 RtlZeroMemory(((UINT8
*)&r
->root_item
) + tp
->item
->size
, sizeof(ROOT_ITEM
) - tp
->item
->size
);
2618 RtlZeroMemory(&r
->root_item
, sizeof(ROOT_ITEM
));
2620 if (!Vcb
->readonly
&& (r
->id
== BTRFS_ROOT_ROOT
|| r
->id
== BTRFS_ROOT_FSTREE
|| (r
->id
>= 0x100 && !(r
->id
& 0xf000000000000000)))) { // FS tree root
2621 // FIXME - don't call this if subvol is readonly (though we will have to if we ever toggle this flag)
2622 get_last_inode(Vcb
, r
, NULL
);
2624 if (r
->id
== BTRFS_ROOT_ROOT
&& r
->lastinode
< 0x100)
2625 r
->lastinode
= 0x100;
2628 InsertTailList(&Vcb
->roots
, &r
->list_entry
);
2631 case BTRFS_ROOT_ROOT
:
2635 case BTRFS_ROOT_EXTENT
:
2636 Vcb
->extent_root
= r
;
2639 case BTRFS_ROOT_CHUNK
:
2640 Vcb
->chunk_root
= r
;
2643 case BTRFS_ROOT_DEVTREE
:
2647 case BTRFS_ROOT_CHECKSUM
:
2648 Vcb
->checksum_root
= r
;
2651 case BTRFS_ROOT_UUID
:
2655 case BTRFS_ROOT_FREE_SPACE
:
2656 Vcb
->space_root
= r
;
2659 case BTRFS_ROOT_DATA_RELOC
:
2660 Vcb
->data_reloc_root
= r
;
2664 return STATUS_SUCCESS
;
2667 static NTSTATUS
look_for_roots(_Requires_exclusive_lock_held_(_Curr_
->tree_lock
) _In_ device_extension
* Vcb
, _In_opt_ PIRP Irp
) {
2668 traverse_ptr tp
, next_tp
;
2673 searchkey
.obj_id
= 0;
2674 searchkey
.obj_type
= 0;
2675 searchkey
.offset
= 0;
2677 Status
= find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, FALSE
, Irp
);
2678 if (!NT_SUCCESS(Status
)) {
2679 ERR("error - find_item returned %08x\n", Status
);
2684 TRACE("(%llx,%x,%llx)\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
2686 if (tp
.item
->key
.obj_type
== TYPE_ROOT_ITEM
) {
2687 ROOT_ITEM
* ri
= (ROOT_ITEM
*)tp
.item
->data
;
2689 if (tp
.item
->size
< offsetof(ROOT_ITEM
, byte_limit
)) {
2690 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
));
2692 TRACE("root %llx - address %llx\n", tp
.item
->key
.obj_id
, ri
->block_number
);
2694 Status
= add_root(Vcb
, tp
.item
->key
.obj_id
, ri
->block_number
, ri
->generation
, &tp
);
2695 if (!NT_SUCCESS(Status
)) {
2696 ERR("add_root returned %08x\n", Status
);
2700 } else if (tp
.item
->key
.obj_type
== TYPE_ROOT_BACKREF
&& !IsListEmpty(&Vcb
->roots
)) {
2701 root
* lastroot
= CONTAINING_RECORD(Vcb
->roots
.Blink
, root
, list_entry
);
2703 if (lastroot
->id
== tp
.item
->key
.obj_id
)
2704 lastroot
->parent
= tp
.item
->key
.offset
;
2707 b
= find_next_item(Vcb
, &tp
, &next_tp
, FALSE
, Irp
);
2713 if (!Vcb
->readonly
&& !Vcb
->data_reloc_root
) {
2721 WARN("data reloc root doesn't exist, creating it\n");
2723 Status
= create_root(Vcb
, BTRFS_ROOT_DATA_RELOC
, &reloc_root
, FALSE
, 0, Irp
);
2725 if (!NT_SUCCESS(Status
)) {
2726 ERR("create_root returned %08x\n", Status
);
2730 reloc_root
->root_item
.inode
.generation
= 1;
2731 reloc_root
->root_item
.inode
.st_size
= 3;
2732 reloc_root
->root_item
.inode
.st_blocks
= Vcb
->superblock
.node_size
;
2733 reloc_root
->root_item
.inode
.st_nlink
= 1;
2734 reloc_root
->root_item
.inode
.st_mode
= 040755;
2735 reloc_root
->root_item
.inode
.flags
= 0xffffffff80000000;
2736 reloc_root
->root_item
.objid
= SUBVOL_ROOT_INODE
;
2737 reloc_root
->root_item
.bytes_used
= Vcb
->superblock
.node_size
;
2739 ii
= ExAllocatePoolWithTag(PagedPool
, sizeof(INODE_ITEM
), ALLOC_TAG
);
2741 ERR("out of memory\n");
2742 return STATUS_INSUFFICIENT_RESOURCES
;
2745 KeQuerySystemTime(&time
);
2746 win_time_to_unix(time
, &now
);
2748 RtlZeroMemory(ii
, sizeof(INODE_ITEM
));
2749 ii
->generation
= Vcb
->superblock
.generation
;
2750 ii
->st_blocks
= Vcb
->superblock
.node_size
;
2752 ii
->st_mode
= 040755;
2757 Status
= insert_tree_item(Vcb
, reloc_root
, SUBVOL_ROOT_INODE
, TYPE_INODE_ITEM
, 0, ii
, sizeof(INODE_ITEM
), NULL
, Irp
);
2758 if (!NT_SUCCESS(Status
)) {
2759 ERR("insert_tree_item returned %08x\n", Status
);
2764 irlen
= (UINT16
)offsetof(INODE_REF
, name
[0]) + 2;
2765 ir
= ExAllocatePoolWithTag(PagedPool
, irlen
, ALLOC_TAG
);
2767 ERR("out of memory\n");
2768 return STATUS_INSUFFICIENT_RESOURCES
;
2776 Status
= insert_tree_item(Vcb
, reloc_root
, SUBVOL_ROOT_INODE
, TYPE_INODE_REF
, SUBVOL_ROOT_INODE
, ir
, irlen
, NULL
, Irp
);
2777 if (!NT_SUCCESS(Status
)) {
2778 ERR("insert_tree_item returned %08x\n", Status
);
2783 Vcb
->data_reloc_root
= reloc_root
;
2784 Vcb
->need_write
= TRUE
;
2787 return STATUS_SUCCESS
;
2790 static NTSTATUS
find_disk_holes(_In_
_Requires_lock_held_(_Curr_
->tree_lock
) device_extension
* Vcb
, _In_ device
* dev
, _In_opt_ PIRP Irp
) {
2792 traverse_ptr tp
, next_tp
;
2797 InitializeListHead(&dev
->space
);
2799 searchkey
.obj_id
= 0;
2800 searchkey
.obj_type
= TYPE_DEV_STATS
;
2801 searchkey
.offset
= dev
->devitem
.dev_id
;
2803 Status
= find_item(Vcb
, Vcb
->dev_root
, &tp
, &searchkey
, FALSE
, Irp
);
2804 if (NT_SUCCESS(Status
) && !keycmp(tp
.item
->key
, searchkey
))
2805 RtlCopyMemory(dev
->stats
, tp
.item
->data
, min(sizeof(UINT64
) * 5, tp
.item
->size
));
2807 searchkey
.obj_id
= dev
->devitem
.dev_id
;
2808 searchkey
.obj_type
= TYPE_DEV_EXTENT
;
2809 searchkey
.offset
= 0;
2811 Status
= find_item(Vcb
, Vcb
->dev_root
, &tp
, &searchkey
, FALSE
, Irp
);
2812 if (!NT_SUCCESS(Status
)) {
2813 ERR("error - find_item returned %08x\n", Status
);
2820 if (tp
.item
->key
.obj_id
== dev
->devitem
.dev_id
&& tp
.item
->key
.obj_type
== TYPE_DEV_EXTENT
) {
2821 if (tp
.item
->size
>= sizeof(DEV_EXTENT
)) {
2822 DEV_EXTENT
* de
= (DEV_EXTENT
*)tp
.item
->data
;
2824 if (tp
.item
->key
.offset
> lastaddr
) {
2825 Status
= add_space_entry(&dev
->space
, NULL
, lastaddr
, tp
.item
->key
.offset
- lastaddr
);
2826 if (!NT_SUCCESS(Status
)) {
2827 ERR("add_space_entry returned %08x\n", Status
);
2832 lastaddr
= tp
.item
->key
.offset
+ de
->length
;
2834 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
));
2838 b
= find_next_item(Vcb
, &tp
, &next_tp
, FALSE
, Irp
);
2842 if (tp
.item
->key
.obj_id
> searchkey
.obj_id
|| tp
.item
->key
.obj_type
> searchkey
.obj_type
)
2847 if (lastaddr
< dev
->devitem
.num_bytes
) {
2848 Status
= add_space_entry(&dev
->space
, NULL
, lastaddr
, dev
->devitem
.num_bytes
- lastaddr
);
2849 if (!NT_SUCCESS(Status
)) {
2850 ERR("add_space_entry returned %08x\n", Status
);
2855 // The Linux driver doesn't like to allocate chunks within the first megabyte of a device.
2857 space_list_subtract2(&dev
->space
, NULL
, 0, 0x100000, NULL
, NULL
);
2859 return STATUS_SUCCESS
;
2862 static void add_device_to_list(_In_ device_extension
* Vcb
, _In_ device
* dev
) {
2865 le
= Vcb
->devices
.Flink
;
2867 while (le
!= &Vcb
->devices
) {
2868 device
* dev2
= CONTAINING_RECORD(le
, device
, list_entry
);
2870 if (dev2
->devitem
.dev_id
> dev
->devitem
.dev_id
) {
2871 InsertHeadList(le
->Blink
, &dev
->list_entry
);
2878 InsertTailList(&Vcb
->devices
, &dev
->list_entry
);
2882 device
* find_device_from_uuid(_In_ device_extension
* Vcb
, _In_ BTRFS_UUID
* uuid
) {
2883 volume_device_extension
* vde
;
2884 pdo_device_extension
* pdode
;
2887 le
= Vcb
->devices
.Flink
;
2888 while (le
!= &Vcb
->devices
) {
2889 device
* dev
= CONTAINING_RECORD(le
, device
, list_entry
);
2891 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
,
2892 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],
2893 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]);
2895 if (RtlCompareMemory(&dev
->devitem
.device_uuid
, uuid
, sizeof(BTRFS_UUID
)) == sizeof(BTRFS_UUID
)) {
2896 TRACE("returning device %llx\n", dev
->devitem
.dev_id
);
2910 ExAcquireResourceSharedLite(&pdode
->child_lock
, TRUE
);
2912 if (Vcb
->devices_loaded
< Vcb
->superblock
.num_devices
) {
2913 le
= pdode
->children
.Flink
;
2915 while (le
!= &pdode
->children
) {
2916 volume_child
* vc
= CONTAINING_RECORD(le
, volume_child
, list_entry
);
2918 if (RtlCompareMemory(uuid
, &vc
->uuid
, sizeof(BTRFS_UUID
)) == sizeof(BTRFS_UUID
)) {
2921 dev
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(device
), ALLOC_TAG
);
2923 ExReleaseResourceLite(&pdode
->child_lock
);
2924 ERR("out of memory\n");
2928 RtlZeroMemory(dev
, sizeof(device
));
2929 dev
->devobj
= vc
->devobj
;
2930 dev
->devitem
.device_uuid
= *uuid
;
2931 dev
->devitem
.dev_id
= vc
->devid
;
2932 dev
->devitem
.num_bytes
= vc
->size
;
2933 dev
->seeding
= vc
->seeding
;
2934 dev
->readonly
= dev
->seeding
;
2936 dev
->removable
= FALSE
;
2937 dev
->disk_num
= vc
->disk_num
;
2938 dev
->part_num
= vc
->part_num
;
2939 dev
->num_trim_entries
= 0;
2940 InitializeListHead(&dev
->trim_list
);
2942 add_device_to_list(Vcb
, dev
);
2943 Vcb
->devices_loaded
++;
2945 ExReleaseResourceLite(&pdode
->child_lock
);
2954 ExReleaseResourceLite(&pdode
->child_lock
);
2957 WARN("could not find device with uuid %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n",
2958 uuid
->uuid
[0], uuid
->uuid
[1], uuid
->uuid
[2], uuid
->uuid
[3], uuid
->uuid
[4], uuid
->uuid
[5], uuid
->uuid
[6], uuid
->uuid
[7],
2959 uuid
->uuid
[8], uuid
->uuid
[9], uuid
->uuid
[10], uuid
->uuid
[11], uuid
->uuid
[12], uuid
->uuid
[13], uuid
->uuid
[14], uuid
->uuid
[15]);
2964 static BOOL
is_device_removable(_In_ PDEVICE_OBJECT devobj
) {
2966 STORAGE_HOTPLUG_INFO shi
;
2968 Status
= dev_ioctl(devobj
, IOCTL_STORAGE_GET_HOTPLUG_INFO
, NULL
, 0, &shi
, sizeof(STORAGE_HOTPLUG_INFO
), TRUE
, NULL
);
2970 if (!NT_SUCCESS(Status
)) {
2971 ERR("dev_ioctl returned %08x\n", Status
);
2975 return shi
.MediaRemovable
!= 0 ? TRUE
: FALSE
;
2978 static ULONG
get_device_change_count(_In_ PDEVICE_OBJECT devobj
) {
2981 IO_STATUS_BLOCK iosb
;
2983 Status
= dev_ioctl(devobj
, IOCTL_STORAGE_CHECK_VERIFY
, NULL
, 0, &cc
, sizeof(ULONG
), TRUE
, &iosb
);
2985 if (!NT_SUCCESS(Status
)) {
2986 ERR("dev_ioctl returned %08x\n", Status
);
2990 if (iosb
.Information
< sizeof(ULONG
)) {
2991 ERR("iosb.Information was too short\n");
2998 void init_device(_In_ device_extension
* Vcb
, _Inout_ device
* dev
, _In_ BOOL get_nums
) {
3001 ATA_PASS_THROUGH_EX
* apte
;
3002 STORAGE_PROPERTY_QUERY spq
;
3003 DEVICE_TRIM_DESCRIPTOR dtd
;
3005 dev
->removable
= is_device_removable(dev
->devobj
);
3006 dev
->change_count
= dev
->removable
? get_device_change_count(dev
->devobj
) : 0;
3009 STORAGE_DEVICE_NUMBER sdn
;
3011 Status
= dev_ioctl(dev
->devobj
, IOCTL_STORAGE_GET_DEVICE_NUMBER
, NULL
, 0,
3012 &sdn
, sizeof(STORAGE_DEVICE_NUMBER
), TRUE
, NULL
);
3014 if (!NT_SUCCESS(Status
)) {
3015 WARN("IOCTL_STORAGE_GET_DEVICE_NUMBER returned %08x\n", Status
);
3016 dev
->disk_num
= 0xffffffff;
3017 dev
->part_num
= 0xffffffff;
3019 dev
->disk_num
= sdn
.DeviceNumber
;
3020 dev
->part_num
= sdn
.PartitionNumber
;
3025 dev
->readonly
= dev
->seeding
;
3027 dev
->num_trim_entries
= 0;
3028 dev
->stats_changed
= FALSE
;
3029 InitializeListHead(&dev
->trim_list
);
3031 if (!dev
->readonly
) {
3032 Status
= dev_ioctl(dev
->devobj
, IOCTL_DISK_IS_WRITABLE
, NULL
, 0,
3033 NULL
, 0, TRUE
, NULL
);
3034 if (Status
== STATUS_MEDIA_WRITE_PROTECTED
)
3035 dev
->readonly
= TRUE
;
3038 aptelen
= sizeof(ATA_PASS_THROUGH_EX
) + 512;
3039 apte
= ExAllocatePoolWithTag(NonPagedPool
, aptelen
, ALLOC_TAG
);
3041 ERR("out of memory\n");
3045 RtlZeroMemory(apte
, aptelen
);
3047 apte
->Length
= sizeof(ATA_PASS_THROUGH_EX
);
3048 apte
->AtaFlags
= ATA_FLAGS_DATA_IN
;
3049 apte
->DataTransferLength
= aptelen
- sizeof(ATA_PASS_THROUGH_EX
);
3050 apte
->TimeOutValue
= 3;
3051 apte
->DataBufferOffset
= apte
->Length
;
3052 apte
->CurrentTaskFile
[6] = IDE_COMMAND_IDENTIFY
;
3054 Status
= dev_ioctl(dev
->devobj
, IOCTL_ATA_PASS_THROUGH
, apte
, aptelen
,
3055 apte
, aptelen
, TRUE
, NULL
);
3057 if (!NT_SUCCESS(Status
))
3058 TRACE("IOCTL_ATA_PASS_THROUGH returned %08x for IDENTIFY DEVICE\n", Status
);
3060 IDENTIFY_DEVICE_DATA
* idd
= (IDENTIFY_DEVICE_DATA
*)((UINT8
*)apte
+ sizeof(ATA_PASS_THROUGH_EX
));
3062 if (idd
->CommandSetSupport
.FlushCache
) {
3063 dev
->can_flush
= TRUE
;
3064 TRACE("FLUSH CACHE supported\n");
3066 TRACE("FLUSH CACHE not supported\n");
3071 spq
.PropertyId
= StorageDeviceTrimProperty
;
3072 spq
.QueryType
= PropertyStandardQuery
;
3073 spq
.AdditionalParameters
[0] = 0;
3075 Status
= dev_ioctl(dev
->devobj
, IOCTL_STORAGE_QUERY_PROPERTY
, &spq
, sizeof(STORAGE_PROPERTY_QUERY
),
3076 &dtd
, sizeof(DEVICE_TRIM_DESCRIPTOR
), TRUE
, NULL
);
3078 if (NT_SUCCESS(Status
)) {
3079 if (dtd
.TrimEnabled
) {
3082 TRACE("TRIM supported\n");
3084 TRACE("TRIM not supported\n");
3087 RtlZeroMemory(dev
->stats
, sizeof(UINT64
) * 5);
3090 static NTSTATUS
load_chunk_root(_In_
_Requires_lock_held_(_Curr_
->tree_lock
) device_extension
* Vcb
, _In_opt_ PIRP Irp
) {
3091 traverse_ptr tp
, next_tp
;
3097 searchkey
.obj_id
= 0;
3098 searchkey
.obj_type
= 0;
3099 searchkey
.offset
= 0;
3101 Vcb
->data_flags
= 0;
3102 Vcb
->metadata_flags
= 0;
3103 Vcb
->system_flags
= 0;
3105 Status
= find_item(Vcb
, Vcb
->chunk_root
, &tp
, &searchkey
, FALSE
, Irp
);
3106 if (!NT_SUCCESS(Status
)) {
3107 ERR("error - find_item returned %08x\n", Status
);
3112 TRACE("(%llx,%x,%llx)\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
3114 if (tp
.item
->key
.obj_id
== 1 && tp
.item
->key
.obj_type
== TYPE_DEV_ITEM
) {
3115 if (tp
.item
->size
< sizeof(DEV_ITEM
)) {
3116 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
));
3118 DEV_ITEM
* di
= (DEV_ITEM
*)tp
.item
->data
;
3122 le
= Vcb
->devices
.Flink
;
3123 while (le
!= &Vcb
->devices
) {
3124 device
* dev
= CONTAINING_RECORD(le
, device
, list_entry
);
3126 if (dev
->devobj
&& RtlCompareMemory(&dev
->devitem
.device_uuid
, &di
->device_uuid
, sizeof(BTRFS_UUID
)) == sizeof(BTRFS_UUID
)) {
3127 RtlCopyMemory(&dev
->devitem
, tp
.item
->data
, min(tp
.item
->size
, sizeof(DEV_ITEM
)));
3129 if (le
!= Vcb
->devices
.Flink
)
3130 init_device(Vcb
, dev
, TRUE
);
3139 if (!done
&& Vcb
->vde
) {
3140 volume_device_extension
* vde
= Vcb
->vde
;
3141 pdo_device_extension
* pdode
= vde
->pdode
;
3143 ExAcquireResourceSharedLite(&pdode
->child_lock
, TRUE
);
3145 if (Vcb
->devices_loaded
< Vcb
->superblock
.num_devices
) {
3146 le
= pdode
->children
.Flink
;
3148 while (le
!= &pdode
->children
) {
3149 volume_child
* vc
= CONTAINING_RECORD(le
, volume_child
, list_entry
);
3151 if (RtlCompareMemory(&di
->device_uuid
, &vc
->uuid
, sizeof(BTRFS_UUID
)) == sizeof(BTRFS_UUID
)) {
3154 dev
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(device
), ALLOC_TAG
);
3156 ExReleaseResourceLite(&pdode
->child_lock
);
3157 ERR("out of memory\n");
3158 return STATUS_INSUFFICIENT_RESOURCES
;
3161 RtlZeroMemory(dev
, sizeof(device
));
3163 dev
->devobj
= vc
->devobj
;
3164 RtlCopyMemory(&dev
->devitem
, di
, min(tp
.item
->size
, sizeof(DEV_ITEM
)));
3165 dev
->seeding
= vc
->seeding
;
3166 init_device(Vcb
, dev
, FALSE
);
3168 if (dev
->devitem
.num_bytes
> vc
->size
) {
3169 WARN("device %llx: DEV_ITEM says %llx bytes, but Windows only reports %llx\n", tp
.item
->key
.offset
,
3170 dev
->devitem
.num_bytes
, vc
->size
);
3172 dev
->devitem
.num_bytes
= vc
->size
;
3175 dev
->disk_num
= vc
->disk_num
;
3176 dev
->part_num
= vc
->part_num
;
3177 add_device_to_list(Vcb
, dev
);
3178 Vcb
->devices_loaded
++;
3188 if (!Vcb
->options
.allow_degraded
) {
3189 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
,
3190 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],
3191 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]);
3195 dev
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(device
), ALLOC_TAG
);
3197 ExReleaseResourceLite(&pdode
->child_lock
);
3198 ERR("out of memory\n");
3199 return STATUS_INSUFFICIENT_RESOURCES
;
3202 RtlZeroMemory(dev
, sizeof(device
));
3204 // Missing device, so we keep dev->devobj as NULL
3205 RtlCopyMemory(&dev
->devitem
, di
, min(tp
.item
->size
, sizeof(DEV_ITEM
)));
3206 InitializeListHead(&dev
->trim_list
);
3208 add_device_to_list(Vcb
, dev
);
3209 Vcb
->devices_loaded
++;
3213 ERR("unexpected device %llx found\n", tp
.item
->key
.offset
);
3215 ExReleaseResourceLite(&pdode
->child_lock
);
3218 } else if (tp
.item
->key
.obj_type
== TYPE_CHUNK_ITEM
) {
3219 if (tp
.item
->size
< sizeof(CHUNK_ITEM
)) {
3220 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
));
3222 c
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(chunk
), ALLOC_TAG
);
3225 ERR("out of memory\n");
3226 return STATUS_INSUFFICIENT_RESOURCES
;
3229 c
->size
= tp
.item
->size
;
3230 c
->offset
= tp
.item
->key
.offset
;
3231 c
->used
= c
->oldused
= 0;
3232 c
->cache
= c
->old_cache
= NULL
;
3234 c
->readonly
= FALSE
;
3236 c
->cache_loaded
= FALSE
;
3238 c
->space_changed
= FALSE
;
3241 c
->chunk_item
= ExAllocatePoolWithTag(NonPagedPool
, tp
.item
->size
, ALLOC_TAG
);
3243 if (!c
->chunk_item
) {
3244 ERR("out of memory\n");
3246 return STATUS_INSUFFICIENT_RESOURCES
;
3249 RtlCopyMemory(c
->chunk_item
, tp
.item
->data
, tp
.item
->size
);
3251 if (c
->chunk_item
->type
& BLOCK_FLAG_DATA
&& c
->chunk_item
->type
> Vcb
->data_flags
)
3252 Vcb
->data_flags
= c
->chunk_item
->type
;
3254 if (c
->chunk_item
->type
& BLOCK_FLAG_METADATA
&& c
->chunk_item
->type
> Vcb
->metadata_flags
)
3255 Vcb
->metadata_flags
= c
->chunk_item
->type
;
3257 if (c
->chunk_item
->type
& BLOCK_FLAG_SYSTEM
&& c
->chunk_item
->type
> Vcb
->system_flags
)
3258 Vcb
->system_flags
= c
->chunk_item
->type
;
3260 if (c
->chunk_item
->type
& BLOCK_FLAG_RAID10
) {
3261 if (c
->chunk_item
->sub_stripes
== 0 || c
->chunk_item
->sub_stripes
> c
->chunk_item
->num_stripes
) {
3262 ERR("chunk %llx: invalid stripes (num_stripes %u, sub_stripes %u)\n", c
->offset
, c
->chunk_item
->num_stripes
, c
->chunk_item
->sub_stripes
);
3263 ExFreePool(c
->chunk_item
);
3265 return STATUS_INTERNAL_ERROR
;
3269 if (c
->chunk_item
->num_stripes
> 0) {
3270 CHUNK_ITEM_STRIPE
* cis
= (CHUNK_ITEM_STRIPE
*)&c
->chunk_item
[1];
3273 c
->devices
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(device
*) * c
->chunk_item
->num_stripes
, ALLOC_TAG
);
3276 ERR("out of memory\n");
3277 ExFreePool(c
->chunk_item
);
3279 return STATUS_INSUFFICIENT_RESOURCES
;
3282 for (i
= 0; i
< c
->chunk_item
->num_stripes
; i
++) {
3283 c
->devices
[i
] = find_device_from_uuid(Vcb
, &cis
[i
].dev_uuid
);
3284 TRACE("device %llu = %p\n", i
, c
->devices
[i
]);
3286 if (!c
->devices
[i
]) {
3287 ERR("missing device\n");
3288 ExFreePool(c
->chunk_item
);
3290 return STATUS_INTERNAL_ERROR
;
3293 if (c
->devices
[i
]->readonly
)
3297 ERR("chunk %llx: number of stripes is 0\n", c
->offset
);
3298 ExFreePool(c
->chunk_item
);
3300 return STATUS_INTERNAL_ERROR
;
3303 ExInitializeResourceLite(&c
->lock
);
3304 ExInitializeResourceLite(&c
->changed_extents_lock
);
3306 InitializeListHead(&c
->space
);
3307 InitializeListHead(&c
->space_size
);
3308 InitializeListHead(&c
->deleting
);
3309 InitializeListHead(&c
->changed_extents
);
3311 InitializeListHead(&c
->range_locks
);
3312 ExInitializeResourceLite(&c
->range_locks_lock
);
3313 KeInitializeEvent(&c
->range_locks_event
, NotificationEvent
, FALSE
);
3315 InitializeListHead(&c
->partial_stripes
);
3316 ExInitializeResourceLite(&c
->partial_stripes_lock
);
3318 c
->last_alloc_set
= FALSE
;
3322 InsertTailList(&Vcb
->chunks
, &c
->list_entry
);
3324 c
->list_entry_balance
.Flink
= NULL
;
3328 b
= find_next_item(Vcb
, &tp
, &next_tp
, FALSE
, Irp
);
3334 Vcb
->log_to_phys_loaded
= TRUE
;
3336 if (Vcb
->data_flags
== 0)
3337 Vcb
->data_flags
= BLOCK_FLAG_DATA
| (Vcb
->superblock
.num_devices
> 1 ? BLOCK_FLAG_RAID0
: 0);
3339 if (Vcb
->metadata_flags
== 0)
3340 Vcb
->metadata_flags
= BLOCK_FLAG_METADATA
| (Vcb
->superblock
.num_devices
> 1 ? BLOCK_FLAG_RAID1
: BLOCK_FLAG_DUPLICATE
);
3342 if (Vcb
->system_flags
== 0)
3343 Vcb
->system_flags
= BLOCK_FLAG_SYSTEM
| (Vcb
->superblock
.num_devices
> 1 ? BLOCK_FLAG_RAID1
: BLOCK_FLAG_DUPLICATE
);
3345 if (Vcb
->superblock
.incompat_flags
& BTRFS_INCOMPAT_FLAGS_MIXED_GROUPS
) {
3346 Vcb
->metadata_flags
|= BLOCK_FLAG_DATA
;
3347 Vcb
->data_flags
= Vcb
->metadata_flags
;
3350 return STATUS_SUCCESS
;
3353 void protect_superblocks(_Inout_ chunk
* c
) {
3355 UINT64 off_start
, off_end
;
3357 // The Linux driver also protects all the space before the first superblock.
3358 // I realize this confuses physical and logical addresses, but this is what btrfs-progs does -
3359 // evidently Linux assumes the chunk at 0 is always SINGLE.
3360 if (c
->offset
< superblock_addrs
[0])
3361 space_list_subtract(c
, FALSE
, c
->offset
, superblock_addrs
[0] - c
->offset
, NULL
);
3363 while (superblock_addrs
[i
] != 0) {
3364 CHUNK_ITEM
* ci
= c
->chunk_item
;
3365 CHUNK_ITEM_STRIPE
* cis
= (CHUNK_ITEM_STRIPE
*)&ci
[1];
3367 if (ci
->type
& BLOCK_FLAG_RAID0
|| ci
->type
& BLOCK_FLAG_RAID10
) {
3368 for (j
= 0; j
< ci
->num_stripes
; j
++) {
3369 UINT16 sub_stripes
= max(ci
->sub_stripes
, 1);
3371 if (cis
[j
].offset
+ (ci
->size
* ci
->num_stripes
/ sub_stripes
) > superblock_addrs
[i
] && cis
[j
].offset
<= superblock_addrs
[i
] + sizeof(superblock
)) {
3374 UINT16 startoffstripe
;
3377 TRACE("cut out superblock in chunk %llx\n", c
->offset
);
3379 off_start
= superblock_addrs
[i
] - cis
[j
].offset
;
3380 off_start
-= off_start
% ci
->stripe_length
;
3381 off_start
*= ci
->num_stripes
/ sub_stripes
;
3382 off_start
+= (j
/ sub_stripes
) * ci
->stripe_length
;
3384 off_end
= off_start
+ ci
->stripe_length
;
3387 get_raid0_offset(off_start
, ci
->stripe_length
, ci
->num_stripes
/ sub_stripes
, &startoff
, &startoffstripe
);
3388 TRACE("j = %u, startoffstripe = %u\n", j
, startoffstripe
);
3389 TRACE("startoff = %llx, superblock = %llx\n", startoff
+ cis
[j
].offset
, superblock_addrs
[i
]);
3392 space_list_subtract(c
, FALSE
, c
->offset
+ off_start
, off_end
- off_start
, NULL
);
3395 } else if (ci
->type
& BLOCK_FLAG_RAID5
) {
3396 UINT64 stripe_size
= ci
->size
/ (ci
->num_stripes
- 1);
3398 for (j
= 0; j
< ci
->num_stripes
; j
++) {
3399 if (cis
[j
].offset
+ stripe_size
> superblock_addrs
[i
] && cis
[j
].offset
<= superblock_addrs
[i
] + sizeof(superblock
)) {
3400 TRACE("cut out superblock in chunk %llx\n", c
->offset
);
3402 off_start
= superblock_addrs
[i
] - cis
[j
].offset
;
3403 off_start
-= off_start
% ci
->stripe_length
;
3404 off_start
*= ci
->num_stripes
- 1;
3406 off_end
= sector_align(superblock_addrs
[i
] - cis
[j
].offset
+ sizeof(superblock
), ci
->stripe_length
);
3407 off_end
*= ci
->num_stripes
- 1;
3409 TRACE("cutting out %llx, size %llx\n", c
->offset
+ off_start
, off_end
- off_start
);
3411 space_list_subtract(c
, FALSE
, c
->offset
+ off_start
, off_end
- off_start
, NULL
);
3414 } else if (ci
->type
& BLOCK_FLAG_RAID6
) {
3415 UINT64 stripe_size
= ci
->size
/ (ci
->num_stripes
- 2);
3417 for (j
= 0; j
< ci
->num_stripes
; j
++) {
3418 if (cis
[j
].offset
+ stripe_size
> superblock_addrs
[i
] && cis
[j
].offset
<= superblock_addrs
[i
] + sizeof(superblock
)) {
3419 TRACE("cut out superblock in chunk %llx\n", c
->offset
);
3421 off_start
= superblock_addrs
[i
] - cis
[j
].offset
;
3422 off_start
-= off_start
% ci
->stripe_length
;
3423 off_start
*= ci
->num_stripes
- 2;
3425 off_end
= sector_align(superblock_addrs
[i
] - cis
[j
].offset
+ sizeof(superblock
), ci
->stripe_length
);
3426 off_end
*= ci
->num_stripes
- 2;
3428 TRACE("cutting out %llx, size %llx\n", c
->offset
+ off_start
, off_end
- off_start
);
3430 space_list_subtract(c
, FALSE
, c
->offset
+ off_start
, off_end
- off_start
, NULL
);
3433 } else { // SINGLE, DUPLICATE, RAID1
3434 for (j
= 0; j
< ci
->num_stripes
; j
++) {
3435 if (cis
[j
].offset
+ ci
->size
> superblock_addrs
[i
] && cis
[j
].offset
<= superblock_addrs
[i
] + sizeof(superblock
)) {
3436 TRACE("cut out superblock in chunk %llx\n", c
->offset
);
3438 // The Linux driver protects the whole stripe in which the superblock lives
3440 off_start
= ((superblock_addrs
[i
] - cis
[j
].offset
) / c
->chunk_item
->stripe_length
) * c
->chunk_item
->stripe_length
;
3441 off_end
= sector_align(superblock_addrs
[i
] - cis
[j
].offset
+ sizeof(superblock
), c
->chunk_item
->stripe_length
);
3443 space_list_subtract(c
, FALSE
, c
->offset
+ off_start
, off_end
- off_start
, NULL
);
3452 NTSTATUS
find_chunk_usage(_In_
_Requires_lock_held_(_Curr_
->tree_lock
) device_extension
* Vcb
, _In_opt_ PIRP Irp
) {
3453 LIST_ENTRY
* le
= Vcb
->chunks
.Flink
;
3457 BLOCK_GROUP_ITEM
* bgi
;
3460 searchkey
.obj_type
= TYPE_BLOCK_GROUP_ITEM
;
3462 while (le
!= &Vcb
->chunks
) {
3463 c
= CONTAINING_RECORD(le
, chunk
, list_entry
);
3465 searchkey
.obj_id
= c
->offset
;
3466 searchkey
.offset
= c
->chunk_item
->size
;
3468 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
, Irp
);
3469 if (!NT_SUCCESS(Status
)) {
3470 ERR("error - find_item returned %08x\n", Status
);
3474 if (!keycmp(searchkey
, tp
.item
->key
)) {
3475 if (tp
.item
->size
>= sizeof(BLOCK_GROUP_ITEM
)) {
3476 bgi
= (BLOCK_GROUP_ITEM
*)tp
.item
->data
;
3478 c
->used
= c
->oldused
= bgi
->used
;
3480 TRACE("chunk %llx has %llx bytes used\n", c
->offset
, c
->used
);
3482 ERR("(%llx;%llx,%x,%llx) is %u bytes, expected %u\n",
3483 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
));
3490 Vcb
->chunk_usage_found
= TRUE
;
3492 return STATUS_SUCCESS
;
3495 static NTSTATUS
load_sys_chunks(_In_ device_extension
* Vcb
) {
3497 ULONG n
= Vcb
->superblock
.n
;
3500 if (n
> sizeof(KEY
)) {
3501 RtlCopyMemory(&key
, &Vcb
->superblock
.sys_chunk_array
[Vcb
->superblock
.n
- n
], sizeof(KEY
));
3504 return STATUS_SUCCESS
;
3506 TRACE("bootstrap: %llx,%x,%llx\n", key
.obj_id
, key
.obj_type
, key
.offset
);
3508 if (key
.obj_type
== TYPE_CHUNK_ITEM
) {
3513 if (n
< sizeof(CHUNK_ITEM
))
3514 return STATUS_SUCCESS
;
3516 ci
= (CHUNK_ITEM
*)&Vcb
->superblock
.sys_chunk_array
[Vcb
->superblock
.n
- n
];
3517 cisize
= sizeof(CHUNK_ITEM
) + (ci
->num_stripes
* sizeof(CHUNK_ITEM_STRIPE
));
3520 return STATUS_SUCCESS
;
3522 sc
= ExAllocatePoolWithTag(PagedPool
, sizeof(sys_chunk
), ALLOC_TAG
);
3525 ERR("out of memory\n");
3526 return STATUS_INSUFFICIENT_RESOURCES
;
3531 sc
->data
= ExAllocatePoolWithTag(PagedPool
, sc
->size
, ALLOC_TAG
);
3534 ERR("out of memory\n");
3536 return STATUS_INSUFFICIENT_RESOURCES
;
3539 RtlCopyMemory(sc
->data
, ci
, sc
->size
);
3540 InsertTailList(&Vcb
->sys_chunks
, &sc
->list_entry
);
3544 ERR("unexpected item %llx,%x,%llx in bootstrap\n", key
.obj_id
, key
.obj_type
, key
.offset
);
3545 return STATUS_INTERNAL_ERROR
;
3549 return STATUS_SUCCESS
;
3553 static root
* find_default_subvol(_In_
_Requires_lock_held_(_Curr_
->tree_lock
) device_extension
* Vcb
, _In_opt_ PIRP Irp
) {
3556 static char fn
[] = "default";
3557 static UINT32 crc32
= 0x8dbfc2d2;
3559 if (Vcb
->options
.subvol_id
!= 0) {
3560 le
= Vcb
->roots
.Flink
;
3561 while (le
!= &Vcb
->roots
) {
3562 root
* r
= CONTAINING_RECORD(le
, root
, list_entry
);
3564 if (r
->id
== Vcb
->options
.subvol_id
)
3571 if (Vcb
->superblock
.incompat_flags
& BTRFS_INCOMPAT_FLAGS_DEFAULT_SUBVOL
) {
3577 searchkey
.obj_id
= Vcb
->superblock
.root_dir_objectid
;
3578 searchkey
.obj_type
= TYPE_DIR_ITEM
;
3579 searchkey
.offset
= crc32
;
3581 Status
= find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, FALSE
, Irp
);
3582 if (!NT_SUCCESS(Status
)) {
3583 ERR("error - find_item returned %08x\n", Status
);
3587 if (keycmp(tp
.item
->key
, searchkey
)) {
3588 ERR("could not find (%llx,%x,%llx) in root tree\n", searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
);
3592 if (tp
.item
->size
< sizeof(DIR_ITEM
)) {
3593 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
));
3597 di
= (DIR_ITEM
*)tp
.item
->data
;
3599 if (tp
.item
->size
< sizeof(DIR_ITEM
) - 1 + di
->n
) {
3600 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
);
3604 if (di
->n
!= strlen(fn
) || RtlCompareMemory(di
->name
, fn
, di
->n
) != di
->n
) {
3605 ERR("root DIR_ITEM had same CRC32, but was not \"default\"\n");
3609 if (di
->key
.obj_type
!= TYPE_ROOT_ITEM
) {
3610 ERR("default root has key (%llx,%x,%llx), expected subvolume\n", di
->key
.obj_id
, di
->key
.obj_type
, di
->key
.offset
);
3614 le
= Vcb
->roots
.Flink
;
3615 while (le
!= &Vcb
->roots
) {
3616 root
* r
= CONTAINING_RECORD(le
, root
, list_entry
);
3618 if (r
->id
== di
->key
.obj_id
)
3624 ERR("could not find root %llx, using default instead\n", di
->key
.obj_id
);
3628 le
= Vcb
->roots
.Flink
;
3629 while (le
!= &Vcb
->roots
) {
3630 root
* r
= CONTAINING_RECORD(le
, root
, list_entry
);
3632 if (r
->id
== BTRFS_ROOT_FSTREE
)
3641 void init_file_cache(_In_ PFILE_OBJECT FileObject
, _In_ CC_FILE_SIZES
* ccfs
) {
3642 TRACE("(%p, %p)\n", FileObject
, ccfs
);
3644 CcInitializeCacheMap(FileObject
, ccfs
, FALSE
, cache_callbacks
, FileObject
);
3647 fCcSetAdditionalCacheAttributesEx(FileObject
, CC_ENABLE_DISK_IO_ACCOUNTING
);
3649 CcSetReadAheadGranularity(FileObject
, READ_AHEAD_GRANULARITY
);
3652 static NTSTATUS
create_calc_threads(_In_ PDEVICE_OBJECT DeviceObject
) {
3653 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
3656 Vcb
->calcthreads
.num_threads
= KeQueryActiveProcessorCount(NULL
);
3658 Vcb
->calcthreads
.threads
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(drv_calc_thread
) * Vcb
->calcthreads
.num_threads
, ALLOC_TAG
);
3659 if (!Vcb
->calcthreads
.threads
) {
3660 ERR("out of memory\n");
3661 return STATUS_INSUFFICIENT_RESOURCES
;
3664 InitializeListHead(&Vcb
->calcthreads
.job_list
);
3665 ExInitializeResourceLite(&Vcb
->calcthreads
.lock
);
3666 KeInitializeEvent(&Vcb
->calcthreads
.event
, NotificationEvent
, FALSE
);
3668 RtlZeroMemory(Vcb
->calcthreads
.threads
, sizeof(drv_calc_thread
) * Vcb
->calcthreads
.num_threads
);
3670 for (i
= 0; i
< Vcb
->calcthreads
.num_threads
; i
++) {
3673 Vcb
->calcthreads
.threads
[i
].DeviceObject
= DeviceObject
;
3674 KeInitializeEvent(&Vcb
->calcthreads
.threads
[i
].finished
, NotificationEvent
, FALSE
);
3676 Status
= PsCreateSystemThread(&Vcb
->calcthreads
.threads
[i
].handle
, 0, NULL
, NULL
, NULL
, calc_thread
, &Vcb
->calcthreads
.threads
[i
]);
3677 if (!NT_SUCCESS(Status
)) {
3680 ERR("PsCreateSystemThread returned %08x\n", Status
);
3682 for (j
= 0; j
< i
; j
++) {
3683 Vcb
->calcthreads
.threads
[i
].quit
= TRUE
;
3686 KeSetEvent(&Vcb
->calcthreads
.event
, 0, FALSE
);
3692 return STATUS_SUCCESS
;
3695 static BOOL
is_btrfs_volume(_In_ PDEVICE_OBJECT DeviceObject
) {
3697 MOUNTDEV_NAME mdn
, *mdn2
;
3700 Status
= dev_ioctl(DeviceObject
, IOCTL_MOUNTDEV_QUERY_DEVICE_NAME
, NULL
, 0, &mdn
, sizeof(MOUNTDEV_NAME
), TRUE
, NULL
);
3701 if (!NT_SUCCESS(Status
) && Status
!= STATUS_BUFFER_OVERFLOW
) {
3702 ERR("IOCTL_MOUNTDEV_QUERY_DEVICE_NAME returned %08x\n", Status
);
3706 mdnsize
= (ULONG
)offsetof(MOUNTDEV_NAME
, Name
[0]) + mdn
.NameLength
;
3708 mdn2
= ExAllocatePoolWithTag(PagedPool
, mdnsize
, ALLOC_TAG
);
3710 ERR("out of memory\n");
3714 Status
= dev_ioctl(DeviceObject
, IOCTL_MOUNTDEV_QUERY_DEVICE_NAME
, NULL
, 0, mdn2
, mdnsize
, TRUE
, NULL
);
3715 if (!NT_SUCCESS(Status
)) {
3716 ERR("IOCTL_MOUNTDEV_QUERY_DEVICE_NAME returned %08x\n", Status
);
3721 if (mdn2
->NameLength
> wcslen(BTRFS_VOLUME_PREFIX
) * sizeof(WCHAR
) &&
3722 RtlCompareMemory(mdn2
->Name
, BTRFS_VOLUME_PREFIX
, wcslen(BTRFS_VOLUME_PREFIX
) * sizeof(WCHAR
)) == wcslen(BTRFS_VOLUME_PREFIX
) * sizeof(WCHAR
)) {
3732 static NTSTATUS
get_device_pnp_name_guid(_In_ PDEVICE_OBJECT DeviceObject
, _Out_ PUNICODE_STRING pnp_name
, _In_
const GUID
* guid
) {
3734 WCHAR
*list
= NULL
, *s
;
3736 Status
= IoGetDeviceInterfaces((PVOID
)guid
, NULL
, 0, &list
);
3737 if (!NT_SUCCESS(Status
)) {
3738 ERR("IoGetDeviceInterfaces returned %08x\n", Status
);
3744 PFILE_OBJECT FileObject
;
3745 PDEVICE_OBJECT devobj
;
3746 UNICODE_STRING name
;
3748 name
.Length
= name
.MaximumLength
= (USHORT
)wcslen(s
) * sizeof(WCHAR
);
3751 if (NT_SUCCESS(IoGetDeviceObjectPointer(&name
, FILE_READ_ATTRIBUTES
, &FileObject
, &devobj
))) {
3752 if (DeviceObject
== devobj
|| DeviceObject
== FileObject
->DeviceObject
) {
3753 ObDereferenceObject(FileObject
);
3755 pnp_name
->Buffer
= ExAllocatePoolWithTag(PagedPool
, name
.Length
, ALLOC_TAG
);
3756 if (!pnp_name
->Buffer
) {
3757 ERR("out of memory\n");
3758 Status
= STATUS_INSUFFICIENT_RESOURCES
;
3762 RtlCopyMemory(pnp_name
->Buffer
, name
.Buffer
, name
.Length
);
3763 pnp_name
->Length
= pnp_name
->MaximumLength
= name
.Length
;
3765 Status
= STATUS_SUCCESS
;
3769 ObDereferenceObject(FileObject
);
3772 s
= &s
[wcslen(s
) + 1];
3775 pnp_name
->Length
= pnp_name
->MaximumLength
= 0;
3776 pnp_name
->Buffer
= 0;
3778 Status
= STATUS_NOT_FOUND
;
3787 NTSTATUS
get_device_pnp_name(_In_ PDEVICE_OBJECT DeviceObject
, _Out_ PUNICODE_STRING pnp_name
, _Out_
const GUID
** guid
) {
3790 Status
= get_device_pnp_name_guid(DeviceObject
, pnp_name
, &GUID_DEVINTERFACE_VOLUME
);
3791 if (NT_SUCCESS(Status
)) {
3792 *guid
= &GUID_DEVINTERFACE_VOLUME
;
3796 Status
= get_device_pnp_name_guid(DeviceObject
, pnp_name
, &GUID_DEVINTERFACE_HIDDEN_VOLUME
);
3797 if (NT_SUCCESS(Status
)) {
3798 *guid
= &GUID_DEVINTERFACE_HIDDEN_VOLUME
;
3802 Status
= get_device_pnp_name_guid(DeviceObject
, pnp_name
, &GUID_DEVINTERFACE_DISK
);
3803 if (NT_SUCCESS(Status
)) {
3804 *guid
= &GUID_DEVINTERFACE_DISK
;
3808 return STATUS_NOT_FOUND
;
3811 _Success_(return>=0)
3812 static NTSTATUS
check_mount_device(_In_ PDEVICE_OBJECT DeviceObject
, _Out_ BOOL
* no_pnp
) {
3817 UNICODE_STRING pnp_name
;
3820 to_read
= DeviceObject
->SectorSize
== 0 ? sizeof(superblock
) : (ULONG
)sector_align(sizeof(superblock
), DeviceObject
->SectorSize
);
3822 sb
= ExAllocatePoolWithTag(NonPagedPool
, to_read
, ALLOC_TAG
);
3824 ERR("out of memory\n");
3825 return STATUS_INSUFFICIENT_RESOURCES
;
3828 Status
= sync_read_phys(DeviceObject
, superblock_addrs
[0], to_read
, (PUCHAR
)sb
, TRUE
);
3829 if (!NT_SUCCESS(Status
)) {
3830 ERR("sync_read_phys returned %08x\n", Status
);
3834 if (sb
->magic
!= BTRFS_MAGIC
) {
3835 Status
= STATUS_SUCCESS
;
3839 crc32
= ~calc_crc32c(0xffffffff, (UINT8
*)&sb
->uuid
, (ULONG
)sizeof(superblock
) - sizeof(sb
->checksum
));
3841 if (crc32
!= *((UINT32
*)sb
->checksum
)) {
3842 WARN("crc32 was %08x, expected %08x\n", crc32
, *((UINT32
*)sb
->checksum
));
3843 Status
= STATUS_SUCCESS
;
3847 DeviceObject
->Flags
&= ~DO_VERIFY_VOLUME
;
3849 pnp_name
.Buffer
= NULL
;
3851 Status
= get_device_pnp_name(DeviceObject
, &pnp_name
, &guid
);
3852 if (!NT_SUCCESS(Status
)) {
3853 WARN("get_device_pnp_name returned %08x\n", Status
);
3854 pnp_name
.Length
= 0;
3857 if (pnp_name
.Length
== 0)
3861 volume_arrival(drvobj
, &pnp_name
);
3864 if (pnp_name
.Buffer
)
3865 ExFreePool(pnp_name
.Buffer
);
3867 Status
= STATUS_SUCCESS
;
3875 static BOOL
still_has_superblock(_In_ PDEVICE_OBJECT device
) {
3879 PDEVICE_OBJECT device2
;
3884 to_read
= device
->SectorSize
== 0 ? sizeof(superblock
) : (ULONG
)sector_align(sizeof(superblock
), device
->SectorSize
);
3886 sb
= ExAllocatePoolWithTag(NonPagedPool
, to_read
, ALLOC_TAG
);
3888 ERR("out of memory\n");
3892 Status
= sync_read_phys(device
, superblock_addrs
[0], to_read
, (PUCHAR
)sb
, TRUE
);
3893 if (!NT_SUCCESS(Status
)) {
3894 ERR("Failed to read superblock: %08x\n", Status
);
3899 if (sb
->magic
!= BTRFS_MAGIC
) {
3900 TRACE("not a BTRFS volume\n");
3904 UINT32 crc32
= ~calc_crc32c(0xffffffff, (UINT8
*)&sb
->uuid
, (ULONG
)sizeof(superblock
) - sizeof(sb
->checksum
));
3906 if (crc32
!= *((UINT32
*)sb
->checksum
)) {
3907 WARN("crc32 was %08x, expected %08x\n", crc32
, *((UINT32
*)sb
->checksum
));
3916 device2
->Flags
&= ~DO_VERIFY_VOLUME
;
3917 device2
= IoGetLowerDeviceObject(device2
);
3924 static NTSTATUS
mount_vol(_In_ PDEVICE_OBJECT DeviceObject
, _In_ PIRP Irp
) {
3925 PIO_STACK_LOCATION IrpSp
;
3926 PDEVICE_OBJECT NewDeviceObject
= NULL
;
3927 PDEVICE_OBJECT DeviceToMount
, readobj
;
3929 device_extension
* Vcb
= NULL
;
3930 LIST_ENTRY
*le
, batchlist
;
3933 fcb
* root_fcb
= NULL
;
3934 ccb
* root_ccb
= NULL
;
3935 BOOL init_lookaside
= FALSE
;
3937 volume_device_extension
* vde
= NULL
;
3938 pdo_device_extension
* pdode
= NULL
;
3940 BOOL no_pnp
= FALSE
;
3943 TRACE("(%p, %p)\n", DeviceObject
, Irp
);
3945 if (DeviceObject
!= master_devobj
) {
3946 Status
= STATUS_INVALID_DEVICE_REQUEST
;
3950 IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
3951 DeviceToMount
= IrpSp
->Parameters
.MountVolume
.DeviceObject
;
3953 if (!is_btrfs_volume(DeviceToMount
)) {
3954 Status
= check_mount_device(DeviceToMount
, &no_pnp
);
3955 if (!NT_SUCCESS(Status
))
3956 WARN("check_mount_device returned %08x\n", Status
);
3959 Status
= STATUS_UNRECOGNIZED_VOLUME
;
3965 pdo
= DeviceToMount
;
3967 while (IoGetLowerDeviceObject(pdo
)) {
3968 pdo
= IoGetLowerDeviceObject(pdo
);
3971 ExAcquireResourceSharedLite(&pdo_list_lock
, TRUE
);
3973 le
= pdo_list
.Flink
;
3974 while (le
!= &pdo_list
) {
3975 pdo_device_extension
* pdode
= CONTAINING_RECORD(le
, pdo_device_extension
, list_entry
);
3977 if (pdode
->pdo
== pdo
) {
3985 ExReleaseResourceLite(&pdo_list_lock
);
3987 if (!vde
|| vde
->type
!= VCB_TYPE_VOLUME
) {
3989 Status
= STATUS_UNRECOGNIZED_VOLUME
;
3997 ExAcquireResourceExclusiveLite(&pdode
->child_lock
, TRUE
);
3999 le
= pdode
->children
.Flink
;
4000 while (le
!= &pdode
->children
) {
4001 LIST_ENTRY
* le2
= le
->Flink
;
4003 vc
= CONTAINING_RECORD(pdode
->children
.Flink
, volume_child
, list_entry
);
4005 if (!still_has_superblock(vc
->devobj
)) {
4006 remove_volume_child(vde
, vc
, FALSE
);
4008 if (pdode
->num_children
== 0) {
4009 ERR("error - number of devices is zero\n");
4010 Status
= STATUS_INTERNAL_ERROR
;
4014 Status
= STATUS_DEVICE_NOT_READY
;
4021 if (pdode
->num_children
== 0 || pdode
->children_loaded
== 0) {
4022 ERR("error - number of devices is zero\n");
4023 Status
= STATUS_INTERNAL_ERROR
;
4027 ExConvertExclusiveToSharedLite(&pdode
->child_lock
);
4029 vc
= CONTAINING_RECORD(pdode
->children
.Flink
, volume_child
, list_entry
);
4031 readobj
= vc
->devobj
;
4032 readobjsize
= vc
->size
;
4034 vde
->device
->Characteristics
&= ~FILE_DEVICE_SECURE_OPEN
;
4036 GET_LENGTH_INFORMATION gli
;
4039 readobj
= DeviceToMount
;
4041 Status
= dev_ioctl(readobj
, IOCTL_DISK_GET_LENGTH_INFO
, NULL
, 0,
4042 &gli
, sizeof(gli
), TRUE
, NULL
);
4044 if (!NT_SUCCESS(Status
)) {
4045 ERR("error reading length information: %08x\n", Status
);
4049 readobjsize
= gli
.Length
.QuadPart
;
4052 Status
= IoCreateDevice(drvobj
, sizeof(device_extension
), NULL
, FILE_DEVICE_DISK_FILE_SYSTEM
, 0, FALSE
, &NewDeviceObject
);
4053 if (!NT_SUCCESS(Status
)) {
4054 ERR("IoCreateDevice returned %08x\n", Status
);
4055 Status
= STATUS_UNRECOGNIZED_VOLUME
;
4059 NewDeviceObject
->Flags
|= DO_DIRECT_IO
;
4061 // Some programs seem to expect that the sector size will be 512, for
4062 // FILE_NO_INTERMEDIATE_BUFFERING and the like.
4063 NewDeviceObject
->SectorSize
= min(DeviceToMount
->SectorSize
, 512);
4065 Vcb
= (PVOID
)NewDeviceObject
->DeviceExtension
;
4066 RtlZeroMemory(Vcb
, sizeof(device_extension
));
4067 Vcb
->type
= VCB_TYPE_FS
;
4070 ExInitializeResourceLite(&Vcb
->tree_lock
);
4071 Vcb
->need_write
= FALSE
;
4073 ExInitializeResourceLite(&Vcb
->fcb_lock
);
4074 ExInitializeResourceLite(&Vcb
->chunk_lock
);
4075 ExInitializeResourceLite(&Vcb
->dirty_fcbs_lock
);
4076 ExInitializeResourceLite(&Vcb
->dirty_filerefs_lock
);
4077 ExInitializeResourceLite(&Vcb
->dirty_subvols_lock
);
4078 ExInitializeResourceLite(&Vcb
->scrub
.stats_lock
);
4080 ExInitializeResourceLite(&Vcb
->load_lock
);
4081 ExAcquireResourceExclusiveLite(&Vcb
->load_lock
, TRUE
);
4083 ExAcquireResourceExclusiveLite(&Vcb
->tree_lock
, TRUE
);
4085 DeviceToMount
->Flags
|= DO_DIRECT_IO
;
4087 Status
= read_superblock(Vcb
, readobj
, readobjsize
);
4088 if (!NT_SUCCESS(Status
)) {
4089 if (!IoIsErrorUserInduced(Status
))
4090 Status
= STATUS_UNRECOGNIZED_VOLUME
;
4091 else if (Irp
->Tail
.Overlay
.Thread
)
4092 IoSetHardErrorOrVerifyDevice(Irp
, readobj
);
4097 if (!vde
&& Vcb
->superblock
.num_devices
> 1) {
4098 ERR("cannot mount multi-device FS with non-PNP device\n");
4099 Status
= STATUS_UNRECOGNIZED_VOLUME
;
4103 Status
= registry_load_volume_options(Vcb
);
4104 if (!NT_SUCCESS(Status
)) {
4105 ERR("registry_load_volume_options returned %08x\n", Status
);
4109 if (pdode
&& pdode
->children_loaded
< pdode
->num_children
&& (!Vcb
->options
.allow_degraded
|| !finished_probing
|| degraded_wait
)) {
4110 ERR("could not mount as %u device(s) missing\n", pdode
->num_children
- pdode
->children_loaded
);
4111 Status
= STATUS_DEVICE_NOT_READY
;
4115 if (Vcb
->options
.ignore
) {
4116 TRACE("ignoring volume\n");
4117 Status
= STATUS_UNRECOGNIZED_VOLUME
;
4121 if (Vcb
->superblock
.incompat_flags
& ~INCOMPAT_SUPPORTED
) {
4122 WARN("cannot mount because of unsupported incompat flags (%llx)\n", Vcb
->superblock
.incompat_flags
& ~INCOMPAT_SUPPORTED
);
4123 Status
= STATUS_UNRECOGNIZED_VOLUME
;
4127 Vcb
->readonly
= FALSE
;
4128 if (Vcb
->superblock
.compat_ro_flags
& ~COMPAT_RO_SUPPORTED
) {
4129 WARN("mounting read-only because of unsupported flags (%llx)\n", Vcb
->superblock
.compat_ro_flags
& ~COMPAT_RO_SUPPORTED
);
4130 Vcb
->readonly
= TRUE
;
4133 if (Vcb
->options
.readonly
)
4134 Vcb
->readonly
= TRUE
;
4136 Vcb
->superblock
.generation
++;
4137 Vcb
->superblock
.incompat_flags
|= BTRFS_INCOMPAT_FLAGS_MIXED_BACKREF
;
4139 InitializeListHead(&Vcb
->devices
);
4140 dev
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(device
), ALLOC_TAG
);
4142 ERR("out of memory\n");
4143 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4147 dev
->devobj
= readobj
;
4148 RtlCopyMemory(&dev
->devitem
, &Vcb
->superblock
.dev_item
, sizeof(DEV_ITEM
));
4150 if (dev
->devitem
.num_bytes
> readobjsize
) {
4151 WARN("device %llx: DEV_ITEM says %llx bytes, but Windows only reports %llx\n", dev
->devitem
.dev_id
,
4152 dev
->devitem
.num_bytes
, readobjsize
);
4154 dev
->devitem
.num_bytes
= readobjsize
;
4157 dev
->seeding
= Vcb
->superblock
.flags
& BTRFS_SUPERBLOCK_FLAGS_SEEDING
? TRUE
: FALSE
;
4159 init_device(Vcb
, dev
, TRUE
);
4161 InsertTailList(&Vcb
->devices
, &dev
->list_entry
);
4162 Vcb
->devices_loaded
= 1;
4164 if (DeviceToMount
->Flags
& DO_SYSTEM_BOOT_PARTITION
)
4165 Vcb
->disallow_dismount
= TRUE
;
4167 TRACE("DeviceToMount = %p\n", DeviceToMount
);
4168 TRACE("IrpSp->Parameters.MountVolume.Vpb = %p\n", IrpSp
->Parameters
.MountVolume
.Vpb
);
4170 NewDeviceObject
->StackSize
= DeviceToMount
->StackSize
+ 1;
4171 NewDeviceObject
->Flags
&= ~DO_DEVICE_INITIALIZING
;
4173 InitializeListHead(&Vcb
->roots
);
4174 InitializeListHead(&Vcb
->drop_roots
);
4176 Vcb
->log_to_phys_loaded
= FALSE
;
4178 add_root(Vcb
, BTRFS_ROOT_CHUNK
, Vcb
->superblock
.chunk_tree_addr
, Vcb
->superblock
.chunk_root_generation
, NULL
);
4180 if (!Vcb
->chunk_root
) {
4181 ERR("Could not load chunk root.\n");
4182 Status
= STATUS_INTERNAL_ERROR
;
4186 InitializeListHead(&Vcb
->sys_chunks
);
4187 Status
= load_sys_chunks(Vcb
);
4188 if (!NT_SUCCESS(Status
)) {
4189 ERR("load_sys_chunks returned %08x\n", Status
);
4193 InitializeListHead(&Vcb
->chunks
);
4194 InitializeListHead(&Vcb
->trees
);
4195 InitializeListHead(&Vcb
->trees_hash
);
4196 InitializeListHead(&Vcb
->all_fcbs
);
4197 InitializeListHead(&Vcb
->dirty_fcbs
);
4198 InitializeListHead(&Vcb
->dirty_filerefs
);
4199 InitializeListHead(&Vcb
->dirty_subvols
);
4200 InitializeListHead(&Vcb
->send_ops
);
4202 InitializeListHead(&Vcb
->DirNotifyList
);
4203 InitializeListHead(&Vcb
->scrub
.errors
);
4205 FsRtlNotifyInitializeSync(&Vcb
->NotifySync
);
4207 ExInitializePagedLookasideList(&Vcb
->tree_data_lookaside
, NULL
, NULL
, 0, sizeof(tree_data
), ALLOC_TAG
, 0);
4208 ExInitializePagedLookasideList(&Vcb
->traverse_ptr_lookaside
, NULL
, NULL
, 0, sizeof(traverse_ptr
), ALLOC_TAG
, 0);
4209 ExInitializePagedLookasideList(&Vcb
->batch_item_lookaside
, NULL
, NULL
, 0, sizeof(batch_item
), ALLOC_TAG
, 0);
4210 ExInitializePagedLookasideList(&Vcb
->fileref_lookaside
, NULL
, NULL
, 0, sizeof(file_ref
), ALLOC_TAG
, 0);
4211 ExInitializePagedLookasideList(&Vcb
->fcb_lookaside
, NULL
, NULL
, 0, sizeof(fcb
), ALLOC_TAG
, 0);
4212 ExInitializePagedLookasideList(&Vcb
->name_bit_lookaside
, NULL
, NULL
, 0, sizeof(name_bit
), ALLOC_TAG
, 0);
4213 ExInitializeNPagedLookasideList(&Vcb
->range_lock_lookaside
, NULL
, NULL
, 0, sizeof(range_lock
), ALLOC_TAG
, 0);
4214 ExInitializeNPagedLookasideList(&Vcb
->fileref_np_lookaside
, NULL
, NULL
, 0, sizeof(file_ref_nonpaged
), ALLOC_TAG
, 0);
4215 ExInitializeNPagedLookasideList(&Vcb
->fcb_np_lookaside
, NULL
, NULL
, 0, sizeof(fcb_nonpaged
), ALLOC_TAG
, 0);
4216 init_lookaside
= TRUE
;
4218 Vcb
->Vpb
= IrpSp
->Parameters
.MountVolume
.Vpb
;
4220 Status
= load_chunk_root(Vcb
, Irp
);
4221 if (!NT_SUCCESS(Status
)) {
4222 ERR("load_chunk_root returned %08x\n", Status
);
4226 if (Vcb
->superblock
.num_devices
> 1) {
4227 if (Vcb
->devices_loaded
< Vcb
->superblock
.num_devices
&& (!Vcb
->options
.allow_degraded
|| !finished_probing
)) {
4228 ERR("could not mount as %u device(s) missing\n", Vcb
->superblock
.num_devices
- Vcb
->devices_loaded
);
4230 IoRaiseInformationalHardError(IO_ERR_INTERNAL_ERROR
, NULL
, NULL
);
4232 Status
= STATUS_INTERNAL_ERROR
;
4236 if (dev
->readonly
&& !Vcb
->readonly
) {
4237 Vcb
->readonly
= TRUE
;
4239 le
= Vcb
->devices
.Flink
;
4240 while (le
!= &Vcb
->devices
) {
4241 device
* dev2
= CONTAINING_RECORD(le
, device
, list_entry
);
4243 if (dev2
->readonly
&& !dev2
->seeding
)
4246 if (!dev2
->readonly
) {
4247 Vcb
->readonly
= FALSE
;
4255 WARN("setting volume to readonly\n");
4258 if (dev
->readonly
) {
4259 WARN("setting volume to readonly as device is readonly\n");
4260 Vcb
->readonly
= TRUE
;
4264 add_root(Vcb
, BTRFS_ROOT_ROOT
, Vcb
->superblock
.root_tree_addr
, Vcb
->superblock
.generation
- 1, NULL
);
4266 if (!Vcb
->root_root
) {
4267 ERR("Could not load root of roots.\n");
4268 Status
= STATUS_INTERNAL_ERROR
;
4272 Status
= look_for_roots(Vcb
, Irp
);
4273 if (!NT_SUCCESS(Status
)) {
4274 ERR("look_for_roots returned %08x\n", Status
);
4278 if (!Vcb
->readonly
) {
4279 Status
= find_chunk_usage(Vcb
, Irp
);
4280 if (!NT_SUCCESS(Status
)) {
4281 ERR("find_chunk_usage returned %08x\n", Status
);
4286 InitializeListHead(&batchlist
);
4288 // We've already increased the generation by one
4289 if (!Vcb
->readonly
&& (
4290 Vcb
->options
.clear_cache
||
4291 (!(Vcb
->superblock
.compat_ro_flags
& BTRFS_COMPAT_RO_FLAGS_FREE_SPACE_CACHE
) && Vcb
->superblock
.generation
- 1 != Vcb
->superblock
.cache_generation
) ||
4292 (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
)))) {
4293 if (Vcb
->options
.clear_cache
)
4294 WARN("ClearCache option was set, clearing cache...\n");
4295 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
))
4296 WARN("clearing free-space tree created by buggy Linux driver\n");
4298 WARN("generation was %llx, free-space cache generation was %llx; clearing cache...\n", Vcb
->superblock
.generation
- 1, Vcb
->superblock
.cache_generation
);
4300 Status
= clear_free_space_cache(Vcb
, &batchlist
, Irp
);
4301 if (!NT_SUCCESS(Status
)) {
4302 ERR("clear_free_space_cache returned %08x\n", Status
);
4303 clear_batch_list(Vcb
, &batchlist
);
4308 Status
= commit_batch_list(Vcb
, &batchlist
, Irp
);
4309 if (!NT_SUCCESS(Status
)) {
4310 ERR("commit_batch_list returned %08x\n", Status
);
4314 Vcb
->volume_fcb
= create_fcb(Vcb
, NonPagedPool
);
4315 if (!Vcb
->volume_fcb
) {
4316 ERR("out of memory\n");
4317 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4321 Vcb
->volume_fcb
->Vcb
= Vcb
;
4322 Vcb
->volume_fcb
->sd
= NULL
;
4324 Vcb
->dummy_fcb
= create_fcb(Vcb
, NonPagedPool
);
4325 if (!Vcb
->dummy_fcb
) {
4326 ERR("out of memory\n");
4327 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4331 Vcb
->dummy_fcb
->Vcb
= Vcb
;
4332 Vcb
->dummy_fcb
->type
= BTRFS_TYPE_DIRECTORY
;
4333 Vcb
->dummy_fcb
->inode
= 2;
4334 Vcb
->dummy_fcb
->subvol
= Vcb
->root_root
;
4335 Vcb
->dummy_fcb
->atts
= FILE_ATTRIBUTE_DIRECTORY
;
4336 Vcb
->dummy_fcb
->inode_item
.st_nlink
= 1;
4337 Vcb
->dummy_fcb
->inode_item
.st_mode
= __S_IFDIR
;
4339 Vcb
->dummy_fcb
->hash_ptrs
= ExAllocatePoolWithTag(PagedPool
, sizeof(LIST_ENTRY
*) * 256, ALLOC_TAG
);
4340 if (!Vcb
->dummy_fcb
->hash_ptrs
) {
4341 ERR("out of memory\n");
4342 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4346 RtlZeroMemory(Vcb
->dummy_fcb
->hash_ptrs
, sizeof(LIST_ENTRY
*) * 256);
4348 Vcb
->dummy_fcb
->hash_ptrs_uc
= ExAllocatePoolWithTag(PagedPool
, sizeof(LIST_ENTRY
*) * 256, ALLOC_TAG
);
4349 if (!Vcb
->dummy_fcb
->hash_ptrs_uc
) {
4350 ERR("out of memory\n");
4351 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4355 RtlZeroMemory(Vcb
->dummy_fcb
->hash_ptrs_uc
, sizeof(LIST_ENTRY
*) * 256);
4357 root_fcb
= create_fcb(Vcb
, NonPagedPool
);
4359 ERR("out of memory\n");
4360 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4364 root_fcb
->Vcb
= Vcb
;
4365 root_fcb
->inode
= SUBVOL_ROOT_INODE
;
4366 root_fcb
->type
= BTRFS_TYPE_DIRECTORY
;
4368 #ifdef DEBUG_FCB_REFCOUNTS
4369 WARN("volume FCB = %p\n", Vcb
->volume_fcb
);
4370 WARN("root FCB = %p\n", root_fcb
);
4373 root_fcb
->subvol
= find_default_subvol(Vcb
, Irp
);
4375 if (!root_fcb
->subvol
) {
4376 ERR("could not find top subvol\n");
4377 Status
= STATUS_INTERNAL_ERROR
;
4381 Status
= load_dir_children(Vcb
, root_fcb
, TRUE
, Irp
);
4382 if (!NT_SUCCESS(Status
)) {
4383 ERR("load_dir_children returned %08x\n", Status
);
4387 searchkey
.obj_id
= root_fcb
->inode
;
4388 searchkey
.obj_type
= TYPE_INODE_ITEM
;
4389 searchkey
.offset
= 0xffffffffffffffff;
4391 Status
= find_item(Vcb
, root_fcb
->subvol
, &tp
, &searchkey
, FALSE
, Irp
);
4392 if (!NT_SUCCESS(Status
)) {
4393 ERR("error - find_item returned %08x\n", Status
);
4397 if (tp
.item
->key
.obj_id
!= searchkey
.obj_id
|| tp
.item
->key
.obj_type
!= searchkey
.obj_type
) {
4398 ERR("couldn't find INODE_ITEM for root directory\n");
4399 Status
= STATUS_INTERNAL_ERROR
;
4403 if (tp
.item
->size
> 0)
4404 RtlCopyMemory(&root_fcb
->inode_item
, tp
.item
->data
, min(sizeof(INODE_ITEM
), tp
.item
->size
));
4406 fcb_get_sd(root_fcb
, NULL
, TRUE
, Irp
);
4408 root_fcb
->atts
= get_file_attributes(Vcb
, root_fcb
->subvol
, root_fcb
->inode
, root_fcb
->type
, FALSE
, FALSE
, Irp
);
4410 Vcb
->root_fileref
= create_fileref(Vcb
);
4411 if (!Vcb
->root_fileref
) {
4412 ERR("out of memory\n");
4413 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4417 Vcb
->root_fileref
->fcb
= root_fcb
;
4418 InsertTailList(&root_fcb
->subvol
->fcbs
, &root_fcb
->list_entry
);
4419 InsertTailList(&Vcb
->all_fcbs
, &root_fcb
->list_entry_all
);
4421 root_fcb
->fileref
= Vcb
->root_fileref
;
4423 root_ccb
= ExAllocatePoolWithTag(PagedPool
, sizeof(ccb
), ALLOC_TAG
);
4425 ERR("out of memory\n");
4426 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4430 Vcb
->root_file
= IoCreateStreamFileObject(NULL
, DeviceToMount
);
4431 Vcb
->root_file
->FsContext
= root_fcb
;
4432 Vcb
->root_file
->SectionObjectPointer
= &root_fcb
->nonpaged
->segment_object
;
4433 Vcb
->root_file
->Vpb
= DeviceObject
->Vpb
;
4435 RtlZeroMemory(root_ccb
, sizeof(ccb
));
4436 root_ccb
->NodeType
= BTRFS_NODE_TYPE_CCB
;
4437 root_ccb
->NodeSize
= sizeof(ccb
);
4439 Vcb
->root_file
->FsContext2
= root_ccb
;
4442 CcInitializeCacheMap(Vcb
->root_file
, (PCC_FILE_SIZES
)(&root_fcb
->Header
.AllocationSize
), FALSE
, cache_callbacks
, Vcb
->root_file
);
4443 } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER
) {
4444 Status
= _SEH2_GetExceptionCode();
4448 le
= Vcb
->devices
.Flink
;
4449 while (le
!= &Vcb
->devices
) {
4450 device
* dev2
= CONTAINING_RECORD(le
, device
, list_entry
);
4452 Status
= find_disk_holes(Vcb
, dev2
, Irp
);
4453 if (!NT_SUCCESS(Status
)) {
4454 ERR("find_disk_holes returned %08x\n", Status
);
4461 NewDeviceObject
->Vpb
= IrpSp
->Parameters
.MountVolume
.Vpb
;
4462 IrpSp
->Parameters
.MountVolume
.Vpb
->DeviceObject
= NewDeviceObject
;
4463 IrpSp
->Parameters
.MountVolume
.Vpb
->Flags
|= VPB_MOUNTED
;
4464 NewDeviceObject
->Vpb
->VolumeLabelLength
= 4; // FIXME
4465 NewDeviceObject
->Vpb
->VolumeLabel
[0] = '?';
4466 NewDeviceObject
->Vpb
->VolumeLabel
[1] = 0;
4467 NewDeviceObject
->Vpb
->ReferenceCount
++; // FIXME - should we deref this at any point?
4469 KeInitializeEvent(&Vcb
->flush_thread_finished
, NotificationEvent
, FALSE
);
4471 Status
= PsCreateSystemThread(&Vcb
->flush_thread_handle
, 0, NULL
, NULL
, NULL
, flush_thread
, NewDeviceObject
);
4472 if (!NT_SUCCESS(Status
)) {
4473 ERR("PsCreateSystemThread returned %08x\n", Status
);
4477 Status
= create_calc_threads(NewDeviceObject
);
4478 if (!NT_SUCCESS(Status
)) {
4479 ERR("create_calc_threads returned %08x\n", Status
);
4483 Status
= registry_mark_volume_mounted(&Vcb
->superblock
.uuid
);
4484 if (!NT_SUCCESS(Status
))
4485 WARN("registry_mark_volume_mounted returned %08x\n", Status
);
4487 Status
= look_for_balance_item(Vcb
);
4488 if (!NT_SUCCESS(Status
) && Status
!= STATUS_NOT_FOUND
)
4489 WARN("look_for_balance_item returned %08x\n", Status
);
4491 Status
= STATUS_SUCCESS
;
4494 vde
->mounted_device
= NewDeviceObject
;
4496 ExInitializeResourceLite(&Vcb
->send_load_lock
);
4500 ExReleaseResourceLite(&pdode
->child_lock
);
4504 ExReleaseResourceLite(&Vcb
->tree_lock
);
4505 ExReleaseResourceLite(&Vcb
->load_lock
);
4508 if (!NT_SUCCESS(Status
)) {
4510 if (init_lookaside
) {
4511 ExDeletePagedLookasideList(&Vcb
->tree_data_lookaside
);
4512 ExDeletePagedLookasideList(&Vcb
->traverse_ptr_lookaside
);
4513 ExDeletePagedLookasideList(&Vcb
->batch_item_lookaside
);
4514 ExDeletePagedLookasideList(&Vcb
->fileref_lookaside
);
4515 ExDeletePagedLookasideList(&Vcb
->fcb_lookaside
);
4516 ExDeletePagedLookasideList(&Vcb
->name_bit_lookaside
);
4517 ExDeleteNPagedLookasideList(&Vcb
->range_lock_lookaside
);
4518 ExDeleteNPagedLookasideList(&Vcb
->fileref_np_lookaside
);
4519 ExDeleteNPagedLookasideList(&Vcb
->fcb_np_lookaside
);
4523 ObDereferenceObject(Vcb
->root_file
);
4524 else if (Vcb
->root_fileref
) {
4525 ExAcquireResourceExclusiveLite(&Vcb
->fcb_lock
, TRUE
);
4526 free_fileref(Vcb
, Vcb
->root_fileref
);
4527 ExReleaseResourceLite(&Vcb
->fcb_lock
);
4528 } else if (root_fcb
) {
4529 ExAcquireResourceExclusiveLite(&Vcb
->fcb_lock
, TRUE
);
4530 free_fcb(Vcb
, root_fcb
);
4531 ExReleaseResourceLite(&Vcb
->fcb_lock
);
4534 if (Vcb
->volume_fcb
) {
4535 ExAcquireResourceExclusiveLite(&Vcb
->fcb_lock
, TRUE
);
4536 free_fcb(Vcb
, Vcb
->volume_fcb
);
4537 ExReleaseResourceLite(&Vcb
->fcb_lock
);
4540 ExDeleteResourceLite(&Vcb
->tree_lock
);
4541 ExDeleteResourceLite(&Vcb
->load_lock
);
4542 ExDeleteResourceLite(&Vcb
->fcb_lock
);
4543 ExDeleteResourceLite(&Vcb
->chunk_lock
);
4544 ExDeleteResourceLite(&Vcb
->dirty_fcbs_lock
);
4545 ExDeleteResourceLite(&Vcb
->dirty_filerefs_lock
);
4546 ExDeleteResourceLite(&Vcb
->dirty_subvols_lock
);
4547 ExDeleteResourceLite(&Vcb
->scrub
.stats_lock
);
4549 if (Vcb
->devices
.Flink
) {
4550 while (!IsListEmpty(&Vcb
->devices
)) {
4551 device
* dev2
= CONTAINING_RECORD(RemoveHeadList(&Vcb
->devices
), device
, list_entry
);
4558 if (NewDeviceObject
)
4559 IoDeleteDevice(NewDeviceObject
);
4561 ExAcquireResourceExclusiveLite(&global_loading_lock
, TRUE
);
4562 InsertTailList(&VcbList
, &Vcb
->list_entry
);
4563 ExReleaseResourceLite(&global_loading_lock
);
4565 FsRtlNotifyVolumeEvent(Vcb
->root_file
, FSRTL_VOLUME_MOUNT
);
4568 TRACE("mount_vol done (status: %lx)\n", Status
);
4573 static NTSTATUS
verify_device(_In_ device_extension
* Vcb
, _Inout_ device
* dev
) {
4580 return STATUS_WRONG_VOLUME
;
4582 if (dev
->removable
) {
4583 IO_STATUS_BLOCK iosb
;
4585 Status
= dev_ioctl(dev
->devobj
, IOCTL_STORAGE_CHECK_VERIFY
, NULL
, 0, &cc
, sizeof(ULONG
), TRUE
, &iosb
);
4587 if (IoIsErrorUserInduced(Status
)) {
4588 ERR("IOCTL_STORAGE_CHECK_VERIFY returned %08x (user-induced)\n", Status
);
4591 pdo_device_extension
* pdode
= Vcb
->vde
->pdode
;
4593 BOOL changed
= FALSE
;
4595 ExAcquireResourceExclusiveLite(&pdode
->child_lock
, TRUE
);
4597 le2
= pdode
->children
.Flink
;
4598 while (le2
!= &pdode
->children
) {
4599 volume_child
* vc
= CONTAINING_RECORD(le2
, volume_child
, list_entry
);
4601 if (vc
->devobj
== dev
->devobj
) {
4602 TRACE("removing device\n");
4604 remove_volume_child(Vcb
->vde
, vc
, TRUE
);
4614 ExReleaseResourceLite(&pdode
->child_lock
);
4616 } else if (!NT_SUCCESS(Status
)) {
4617 ERR("IOCTL_STORAGE_CHECK_VERIFY returned %08x\n", Status
);
4619 } else if (iosb
.Information
< sizeof(ULONG
)) {
4620 ERR("iosb.Information was too short\n");
4621 return STATUS_INTERNAL_ERROR
;
4624 dev
->change_count
= cc
;
4627 to_read
= dev
->devobj
->SectorSize
== 0 ? sizeof(superblock
) : (ULONG
)sector_align(sizeof(superblock
), dev
->devobj
->SectorSize
);
4629 sb
= ExAllocatePoolWithTag(NonPagedPool
, to_read
, ALLOC_TAG
);
4631 ERR("out of memory\n");
4632 return STATUS_INSUFFICIENT_RESOURCES
;
4635 Status
= sync_read_phys(dev
->devobj
, superblock_addrs
[0], to_read
, (PUCHAR
)sb
, TRUE
);
4636 if (!NT_SUCCESS(Status
)) {
4637 ERR("Failed to read superblock: %08x\n", Status
);
4642 if (sb
->magic
!= BTRFS_MAGIC
) {
4643 ERR("not a BTRFS volume\n");
4645 return STATUS_WRONG_VOLUME
;
4648 crc32
= ~calc_crc32c(0xffffffff, (UINT8
*)&sb
->uuid
, (ULONG
)sizeof(superblock
) - sizeof(sb
->checksum
));
4649 TRACE("crc32 was %08x, expected %08x\n", crc32
, *((UINT32
*)sb
->checksum
));
4651 if (crc32
!= *((UINT32
*)sb
->checksum
)) {
4652 ERR("checksum error\n");
4654 return STATUS_WRONG_VOLUME
;
4657 if (RtlCompareMemory(&sb
->uuid
, &Vcb
->superblock
.uuid
, sizeof(BTRFS_UUID
)) != sizeof(BTRFS_UUID
)) {
4658 ERR("different UUIDs\n");
4660 return STATUS_WRONG_VOLUME
;
4665 dev
->devobj
->Flags
&= ~DO_VERIFY_VOLUME
;
4667 return STATUS_SUCCESS
;
4670 static NTSTATUS
verify_volume(_In_ PDEVICE_OBJECT devobj
) {
4671 device_extension
* Vcb
= devobj
->DeviceExtension
;
4674 UINT64 failed_devices
= 0;
4675 BOOL locked
= FALSE
, remove
= FALSE
;
4677 if (!(Vcb
->Vpb
->Flags
& VPB_MOUNTED
))
4678 return STATUS_WRONG_VOLUME
;
4680 if (!ExIsResourceAcquiredExclusive(&Vcb
->tree_lock
)) {
4681 ExAcquireResourceExclusiveLite(&Vcb
->tree_lock
, TRUE
);
4685 if (Vcb
->removing
) {
4686 if (locked
) ExReleaseResourceLite(&Vcb
->tree_lock
);
4687 return STATUS_WRONG_VOLUME
;
4690 InterlockedIncrement(&Vcb
->open_files
); // so pnp_surprise_removal doesn't uninit the device while we're still using it
4692 le
= Vcb
->devices
.Flink
;
4693 while (le
!= &Vcb
->devices
) {
4694 device
* dev
= CONTAINING_RECORD(le
, device
, list_entry
);
4696 Status
= verify_device(Vcb
, dev
);
4697 if (!NT_SUCCESS(Status
)) {
4700 if (dev
->devobj
&& Vcb
->options
.allow_degraded
)
4707 InterlockedDecrement(&Vcb
->open_files
);
4709 if (Vcb
->removing
&& Vcb
->open_files
== 0)
4713 ExReleaseResourceLite(&Vcb
->tree_lock
);
4720 if (failed_devices
== 0 || (Vcb
->options
.allow_degraded
&& failed_devices
< Vcb
->superblock
.num_devices
)) {
4721 Vcb
->Vpb
->RealDevice
->Flags
&= ~DO_VERIFY_VOLUME
;
4723 return STATUS_SUCCESS
;
4729 _Dispatch_type_(IRP_MJ_FILE_SYSTEM_CONTROL
)
4730 _Function_class_(DRIVER_DISPATCH
)
4731 static NTSTATUS
drv_file_system_control(_In_ PDEVICE_OBJECT DeviceObject
, _In_ PIRP Irp
) {
4732 PIO_STACK_LOCATION IrpSp
;
4734 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
4737 FsRtlEnterFileSystem();
4739 TRACE("file system control\n");
4741 top_level
= is_top_level(Irp
);
4743 if (Vcb
&& Vcb
->type
== VCB_TYPE_VOLUME
) {
4744 Status
= vol_file_system_control(DeviceObject
, Irp
);
4746 } else if (!Vcb
|| (Vcb
->type
!= VCB_TYPE_FS
&& Vcb
->type
!= VCB_TYPE_CONTROL
)) {
4747 Status
= STATUS_INVALID_PARAMETER
;
4751 Status
= STATUS_NOT_IMPLEMENTED
;
4753 IrpSp
= IoGetCurrentIrpStackLocation( Irp
);
4755 Irp
->IoStatus
.Information
= 0;
4757 switch (IrpSp
->MinorFunction
) {
4758 case IRP_MN_MOUNT_VOLUME
:
4759 TRACE("IRP_MN_MOUNT_VOLUME\n");
4761 Status
= mount_vol(DeviceObject
, Irp
);
4764 case IRP_MN_KERNEL_CALL
:
4765 TRACE("IRP_MN_KERNEL_CALL\n");
4767 Status
= fsctl_request(DeviceObject
, &Irp
, IrpSp
->Parameters
.FileSystemControl
.FsControlCode
);
4770 case IRP_MN_USER_FS_REQUEST
:
4771 TRACE("IRP_MN_USER_FS_REQUEST\n");
4773 Status
= fsctl_request(DeviceObject
, &Irp
, IrpSp
->Parameters
.FileSystemControl
.FsControlCode
);
4776 case IRP_MN_VERIFY_VOLUME
:
4777 TRACE("IRP_MN_VERIFY_VOLUME\n");
4779 Status
= verify_volume(DeviceObject
);
4781 if (!NT_SUCCESS(Status
) && Vcb
->Vpb
->Flags
& VPB_MOUNTED
) {
4782 ExAcquireResourceExclusiveLite(&Vcb
->tree_lock
, TRUE
);
4783 Vcb
->removing
= TRUE
;
4784 ExReleaseResourceLite(&Vcb
->tree_lock
);
4794 TRACE("returning %08x\n", Status
);
4797 Irp
->IoStatus
.Status
= Status
;
4799 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
4803 IoSetTopLevelIrp(NULL
);
4805 FsRtlExitFileSystem();
4810 _Dispatch_type_(IRP_MJ_LOCK_CONTROL
)
4811 _Function_class_(DRIVER_DISPATCH
)
4812 static NTSTATUS
drv_lock_control(_In_ PDEVICE_OBJECT DeviceObject
, _In_ PIRP Irp
) {
4814 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
4815 fcb
* fcb
= IrpSp
->FileObject
->FsContext
;
4816 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
4819 FsRtlEnterFileSystem();
4821 top_level
= is_top_level(Irp
);
4823 if (Vcb
&& Vcb
->type
== VCB_TYPE_VOLUME
) {
4824 Status
= vol_lock_control(DeviceObject
, Irp
);
4826 Irp
->IoStatus
.Status
= Status
;
4827 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
4832 TRACE("lock control\n");
4834 Status
= FsRtlProcessFileLock(&fcb
->lock
, Irp
, NULL
);
4836 fcb
->Header
.IsFastIoPossible
= fast_io_possible(fcb
);
4839 TRACE("returning %08x\n", Status
);
4842 IoSetTopLevelIrp(NULL
);
4844 FsRtlExitFileSystem();
4849 _Dispatch_type_(IRP_MJ_SHUTDOWN
)
4850 _Function_class_(DRIVER_DISPATCH
)
4851 static NTSTATUS
drv_shutdown(_In_ PDEVICE_OBJECT DeviceObject
, _In_ PIRP Irp
) {
4854 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
4856 FsRtlEnterFileSystem();
4858 TRACE("shutdown\n");
4860 top_level
= is_top_level(Irp
);
4862 if (Vcb
&& Vcb
->type
== VCB_TYPE_VOLUME
) {
4863 Status
= vol_shutdown(DeviceObject
, Irp
);
4867 Status
= STATUS_SUCCESS
;
4869 shutting_down
= TRUE
;
4870 KeSetEvent(&mountmgr_thread_event
, 0, FALSE
);
4872 while (!IsListEmpty(&VcbList
)) {
4873 Vcb
= CONTAINING_RECORD(VcbList
.Flink
, device_extension
, list_entry
);
4875 TRACE("shutting down Vcb %p\n", Vcb
);
4882 ObDereferenceObject(comfo
);
4889 Irp
->IoStatus
.Status
= Status
;
4890 Irp
->IoStatus
.Information
= 0;
4892 IoCompleteRequest( Irp
, IO_NO_INCREMENT
);
4895 IoSetTopLevelIrp(NULL
);
4897 FsRtlExitFileSystem();
4902 _Dispatch_type_(IRP_MJ_POWER
)
4903 _Function_class_(DRIVER_DISPATCH
)
4904 static NTSTATUS
drv_power(_In_ PDEVICE_OBJECT DeviceObject
, _In_ PIRP Irp
) {
4906 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
4909 FsRtlEnterFileSystem();
4911 top_level
= is_top_level(Irp
);
4913 Irp
->IoStatus
.Information
= 0;
4915 if (Vcb
&& Vcb
->type
== VCB_TYPE_VOLUME
) {
4916 Status
= vol_power(DeviceObject
, Irp
);
4918 Irp
->IoStatus
.Status
= Status
;
4919 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
4922 } else if (Vcb
&& Vcb
->type
== VCB_TYPE_FS
) {
4923 IoSkipCurrentIrpStackLocation(Irp
);
4925 Status
= IoCallDriver(Vcb
->Vpb
->RealDevice
, Irp
);
4930 Status
= STATUS_INVALID_DEVICE_REQUEST
;
4931 Irp
->IoStatus
.Status
= Status
;
4932 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
4936 IoSetTopLevelIrp(NULL
);
4938 FsRtlExitFileSystem();
4943 _Dispatch_type_(IRP_MJ_SYSTEM_CONTROL
)
4944 _Function_class_(DRIVER_DISPATCH
)
4945 static NTSTATUS
drv_system_control(_In_ PDEVICE_OBJECT DeviceObject
, _In_ PIRP Irp
) {
4947 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
4950 FsRtlEnterFileSystem();
4952 top_level
= is_top_level(Irp
);
4954 Irp
->IoStatus
.Information
= 0;
4956 if (Vcb
&& Vcb
->type
== VCB_TYPE_VOLUME
) {
4957 volume_device_extension
* vde
= DeviceObject
->DeviceExtension
;
4959 IoSkipCurrentIrpStackLocation(Irp
);
4961 Status
= IoCallDriver(vde
->pdo
, Irp
);
4964 } else if (Vcb
&& Vcb
->type
== VCB_TYPE_FS
) {
4965 IoSkipCurrentIrpStackLocation(Irp
);
4967 Status
= IoCallDriver(Vcb
->Vpb
->RealDevice
, Irp
);
4972 Status
= Irp
->IoStatus
.Status
;
4973 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
4977 IoSetTopLevelIrp(NULL
);
4979 FsRtlExitFileSystem();
4984 BOOL
is_file_name_valid(_In_ PUNICODE_STRING us
, _In_ BOOL posix
) {
4987 if (us
->Length
< sizeof(WCHAR
))
4990 if (us
->Length
> 255 * sizeof(WCHAR
))
4993 for (i
= 0; i
< us
->Length
/ sizeof(WCHAR
); i
++) {
4994 if (us
->Buffer
[i
] == '/' || us
->Buffer
[i
] == 0 ||
4995 (!posix
&& (us
->Buffer
[i
] == '<' || us
->Buffer
[i
] == '>' || us
->Buffer
[i
] == ':' || us
->Buffer
[i
] == '"' ||
4996 us
->Buffer
[i
] == '|' || us
->Buffer
[i
] == '?' || us
->Buffer
[i
] == '*' || (us
->Buffer
[i
] >= 1 && us
->Buffer
[i
] <= 31))))
5000 if (us
->Buffer
[0] == '.' && (us
->Length
== sizeof(WCHAR
) || (us
->Length
== 2 * sizeof(WCHAR
) && us
->Buffer
[1] == '.')))
5006 void chunk_lock_range(_In_ device_extension
* Vcb
, _In_ chunk
* c
, _In_ UINT64 start
, _In_ UINT64 length
) {
5011 rl
= ExAllocateFromNPagedLookasideList(&Vcb
->range_lock_lookaside
);
5013 ERR("out of memory\n");
5018 rl
->length
= length
;
5019 rl
->thread
= PsGetCurrentThread();
5024 ExAcquireResourceExclusiveLite(&c
->range_locks_lock
, TRUE
);
5026 le
= c
->range_locks
.Flink
;
5027 while (le
!= &c
->range_locks
) {
5028 range_lock
* rl2
= CONTAINING_RECORD(le
, range_lock
, list_entry
);
5030 if (rl2
->start
< start
+ length
&& rl2
->start
+ rl2
->length
> start
&& rl2
->thread
!= PsGetCurrentThread()) {
5039 InsertTailList(&c
->range_locks
, &rl
->list_entry
);
5041 ExReleaseResourceLite(&c
->range_locks_lock
);
5045 KeClearEvent(&c
->range_locks_event
);
5047 ExReleaseResourceLite(&c
->range_locks_lock
);
5049 KeWaitForSingleObject(&c
->range_locks_event
, UserRequest
, KernelMode
, FALSE
, NULL
);
5053 void chunk_unlock_range(_In_ device_extension
* Vcb
, _In_ chunk
* c
, _In_ UINT64 start
, _In_ UINT64 length
) {
5056 ExAcquireResourceExclusiveLite(&c
->range_locks_lock
, TRUE
);
5058 le
= c
->range_locks
.Flink
;
5059 while (le
!= &c
->range_locks
) {
5060 range_lock
* rl
= CONTAINING_RECORD(le
, range_lock
, list_entry
);
5062 if (rl
->start
== start
&& rl
->length
== length
) {
5063 RemoveEntryList(&rl
->list_entry
);
5064 ExFreeToNPagedLookasideList(&Vcb
->range_lock_lookaside
, rl
);
5071 KeSetEvent(&c
->range_locks_event
, 0, FALSE
);
5073 ExReleaseResourceLite(&c
->range_locks_lock
);
5076 void log_device_error(_In_ device_extension
* Vcb
, _Inout_ device
* dev
, _In_
int error
) {
5077 dev
->stats
[error
]++;
5078 dev
->stats_changed
= TRUE
;
5079 Vcb
->stats_changed
= TRUE
;
5083 _Function_class_(KSTART_ROUTINE
)
5084 static void serial_thread(void* context
) {
5085 LARGE_INTEGER due_time
;
5090 KeInitializeTimer(&timer
);
5092 due_time
.QuadPart
= (UINT64
)-10000000;
5094 KeSetTimer(&timer
, due_time
, NULL
);
5097 KeWaitForSingleObject(&timer
, Executive
, KernelMode
, FALSE
, NULL
);
5104 KeSetTimer(&timer
, due_time
, NULL
);
5107 KeCancelTimer(&timer
);
5109 PsTerminateSystemThread(STATUS_SUCCESS
);
5111 serial_thread_handle
= NULL
;
5114 static void init_serial(BOOL first_time
) {
5117 Status
= IoGetDeviceObjectPointer(&log_device
, FILE_WRITE_DATA
, &comfo
, &comdo
);
5118 if (!NT_SUCCESS(Status
)) {
5119 ERR("IoGetDeviceObjectPointer returned %08x\n", Status
);
5124 Status
= PsCreateSystemThread(&serial_thread_handle
, 0, NULL
, NULL
, NULL
, serial_thread
, NULL
);
5125 if (!NT_SUCCESS(Status
)) {
5126 ERR("PsCreateSystemThread returned %08x\n", Status
);
5135 static void check_cpu() {
5136 unsigned int cpuInfo
[4];
5138 __get_cpuid(1, &cpuInfo
[0], &cpuInfo
[1], &cpuInfo
[2], &cpuInfo
[3]);
5139 have_sse42
= cpuInfo
[2] & bit_SSE4_2
;
5140 have_sse2
= cpuInfo
[3] & bit_SSE2
;
5142 __cpuid(cpuInfo
, 1);
5143 have_sse42
= cpuInfo
[2] & (1 << 20);
5144 have_sse2
= cpuInfo
[3] & (1 << 26);
5148 TRACE("SSE4.2 is supported\n");
5150 TRACE("SSE4.2 not supported\n");
5153 TRACE("SSE2 is supported\n");
5155 TRACE("SSE2 is not supported\n");
5160 static void init_logging() {
5161 ExAcquireResourceExclusiveLite(&log_lock
, TRUE
);
5163 if (log_device
.Length
> 0)
5165 else if (log_file
.Length
> 0) {
5167 OBJECT_ATTRIBUTES oa
;
5168 IO_STATUS_BLOCK iosb
;
5173 InitializeObjectAttributes(&oa
, &log_file
, OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
, NULL
, NULL
);
5175 Status
= ZwCreateFile(&log_handle
, FILE_WRITE_DATA
, &oa
, &iosb
, NULL
, FILE_ATTRIBUTE_NORMAL
, FILE_SHARE_READ
,
5176 FILE_OPEN_IF
, FILE_NON_DIRECTORY_FILE
| FILE_WRITE_THROUGH
| FILE_SYNCHRONOUS_IO_ALERT
, NULL
, 0);
5178 if (!NT_SUCCESS(Status
)) {
5179 ERR("ZwCreateFile returned %08x\n", Status
);
5183 if (iosb
.Information
== FILE_OPENED
) { // already exists
5184 FILE_STANDARD_INFORMATION fsi
;
5185 FILE_POSITION_INFORMATION fpi
;
5187 static char delim
[] = "\n---\n";
5189 // move to end of file
5191 Status
= ZwQueryInformationFile(log_handle
, &iosb
, &fsi
, sizeof(FILE_STANDARD_INFORMATION
), FileStandardInformation
);
5193 if (!NT_SUCCESS(Status
)) {
5194 ERR("ZwQueryInformationFile returned %08x\n", Status
);
5198 fpi
.CurrentByteOffset
= fsi
.EndOfFile
;
5200 Status
= ZwSetInformationFile(log_handle
, &iosb
, &fpi
, sizeof(FILE_POSITION_INFORMATION
), FilePositionInformation
);
5202 if (!NT_SUCCESS(Status
)) {
5203 ERR("ZwSetInformationFile returned %08x\n", Status
);
5207 Status
= ZwWriteFile(log_handle
, NULL
, NULL
, NULL
, &iosb
, delim
, (ULONG
)strlen(delim
), NULL
, NULL
);
5209 if (!NT_SUCCESS(Status
)) {
5210 ERR("ZwWriteFile returned %08x\n", Status
);
5215 dateline
= ExAllocatePoolWithTag(PagedPool
, 256, ALLOC_TAG
);
5218 ERR("out of memory\n");
5222 KeQuerySystemTime(&time
);
5224 RtlTimeToTimeFields(&time
, &tf
);
5226 sprintf(dateline
, "Starting logging at %04i-%02i-%02i %02i:%02i:%02i\n", tf
.Year
, tf
.Month
, tf
.Day
, tf
.Hour
, tf
.Minute
, tf
.Second
);
5228 Status
= ZwWriteFile(log_handle
, NULL
, NULL
, NULL
, &iosb
, dateline
, (ULONG
)strlen(dateline
), NULL
, NULL
);
5230 ExFreePool(dateline
);
5232 if (!NT_SUCCESS(Status
)) {
5233 ERR("ZwWriteFile returned %08x\n", Status
);
5239 ExReleaseResourceLite(&log_lock
);
5243 _Function_class_(KSTART_ROUTINE
)
5245 static void NTAPI
degraded_wait_thread(_In_
void* context
) {
5247 static void degraded_wait_thread(_In_
void* context
) {
5250 LARGE_INTEGER delay
;
5254 KeInitializeTimer(&timer
);
5256 delay
.QuadPart
= -30000000; // wait three seconds
5257 KeSetTimer(&timer
, delay
, NULL
);
5258 KeWaitForSingleObject(&timer
, Executive
, KernelMode
, FALSE
, NULL
);
5260 TRACE("timer expired\n");
5262 degraded_wait
= FALSE
;
5264 ZwClose(degraded_wait_handle
);
5265 degraded_wait_handle
= NULL
;
5267 PsTerminateSystemThread(STATUS_SUCCESS
);
5271 NTSTATUS NTAPI
AddDevice(PDRIVER_OBJECT DriverObject
, PDEVICE_OBJECT PhysicalDeviceObject
) {
5273 NTSTATUS
AddDevice(PDRIVER_OBJECT DriverObject
, PDEVICE_OBJECT PhysicalDeviceObject
) {
5277 UNICODE_STRING volname
;
5279 pdo_device_extension
* pdode
= NULL
;
5280 PDEVICE_OBJECT voldev
;
5281 volume_device_extension
* vde
;
5283 TRACE("(%p, %p)\n", DriverObject
, PhysicalDeviceObject
);
5285 ExAcquireResourceSharedLite(&pdo_list_lock
, TRUE
);
5287 le
= pdo_list
.Flink
;
5288 while (le
!= &pdo_list
) {
5289 pdo_device_extension
* pdode2
= CONTAINING_RECORD(le
, pdo_device_extension
, list_entry
);
5291 if (pdode2
->pdo
== PhysicalDeviceObject
) {
5300 WARN("unrecognized PDO %p\n", PhysicalDeviceObject
);
5301 Status
= STATUS_NOT_SUPPORTED
;
5305 ExAcquireResourceSharedLite(&pdode
->child_lock
, TRUE
);
5307 volname
.Length
= volname
.MaximumLength
= (USHORT
)((wcslen(BTRFS_VOLUME_PREFIX
) + 36 + 1) * sizeof(WCHAR
));
5308 volname
.Buffer
= ExAllocatePoolWithTag(PagedPool
, volname
.MaximumLength
, ALLOC_TAG
); // FIXME - when do we free this?
5310 if (!volname
.Buffer
) {
5311 ERR("out of memory\n");
5312 Status
= STATUS_INSUFFICIENT_RESOURCES
;
5316 RtlCopyMemory(volname
.Buffer
, BTRFS_VOLUME_PREFIX
, wcslen(BTRFS_VOLUME_PREFIX
) * sizeof(WCHAR
));
5318 j
= (ULONG
)wcslen(BTRFS_VOLUME_PREFIX
);
5319 for (i
= 0; i
< 16; i
++) {
5320 volname
.Buffer
[j
] = hex_digit(pdode
->uuid
.uuid
[i
] >> 4); j
++;
5321 volname
.Buffer
[j
] = hex_digit(pdode
->uuid
.uuid
[i
] & 0xf); j
++;
5323 if (i
== 3 || i
== 5 || i
== 7 || i
== 9) {
5324 volname
.Buffer
[j
] = '-';
5329 volname
.Buffer
[j
] = '}';
5331 Status
= IoCreateDevice(drvobj
, sizeof(volume_device_extension
), &volname
, FILE_DEVICE_DISK
,
5332 RtlIsNtDdiVersionAvailable(NTDDI_WIN8
) ? FILE_DEVICE_ALLOW_APPCONTAINER_TRAVERSAL
: 0, FALSE
, &voldev
);
5333 if (!NT_SUCCESS(Status
)) {
5334 ERR("IoCreateDevice returned %08x\n", Status
);
5338 voldev
->SectorSize
= PhysicalDeviceObject
->SectorSize
;
5339 voldev
->Flags
|= DO_DIRECT_IO
;
5341 vde
= voldev
->DeviceExtension
;
5342 vde
->type
= VCB_TYPE_VOLUME
;
5343 vde
->name
= volname
;
5344 vde
->device
= voldev
;
5345 vde
->mounted_device
= NULL
;
5346 vde
->pdo
= PhysicalDeviceObject
;
5348 vde
->removing
= FALSE
;
5349 vde
->open_count
= 0;
5351 Status
= IoRegisterDeviceInterface(PhysicalDeviceObject
, &GUID_DEVINTERFACE_VOLUME
, NULL
, &vde
->bus_name
);
5352 if (!NT_SUCCESS(Status
))
5353 WARN("IoRegisterDeviceInterface returned %08x\n", Status
);
5355 vde
->attached_device
= IoAttachDeviceToDeviceStack(voldev
, PhysicalDeviceObject
);
5359 if (pdode
->removable
)
5360 voldev
->Characteristics
|= FILE_REMOVABLE_MEDIA
;
5362 voldev
->Flags
&= ~DO_DEVICE_INITIALIZING
;
5364 Status
= IoSetDeviceInterfaceState(&vde
->bus_name
, TRUE
);
5365 if (!NT_SUCCESS(Status
))
5366 WARN("IoSetDeviceInterfaceState returned %08x\n", Status
);
5368 Status
= STATUS_SUCCESS
;
5371 ExReleaseResourceLite(&pdode
->child_lock
);
5374 ExReleaseResourceLite(&pdo_list_lock
);
5379 _Function_class_(DRIVER_INITIALIZE
)
5381 NTSTATUS NTAPI
DriverEntry(_In_ PDRIVER_OBJECT DriverObject
, _In_ PUNICODE_STRING RegistryPath
) {
5383 NTSTATUS
DriverEntry(_In_ PDRIVER_OBJECT DriverObject
, _In_ PUNICODE_STRING RegistryPath
) {
5386 PDEVICE_OBJECT DeviceObject
;
5387 UNICODE_STRING device_nameW
;
5388 UNICODE_STRING dosdevice_nameW
;
5389 control_device_extension
* cde
;
5391 OBJECT_ATTRIBUTES oa
;
5394 InitializeListHead(&uid_map_list
);
5395 InitializeListHead(&gid_map_list
);
5398 ExInitializeResourceLite(&log_lock
);
5400 ExInitializeResourceLite(&mapping_lock
);
5402 log_device
.Buffer
= NULL
;
5403 log_device
.Length
= log_device
.MaximumLength
= 0;
5404 log_file
.Buffer
= NULL
;
5405 log_file
.Length
= log_file
.MaximumLength
= 0;
5407 registry_path
.Length
= registry_path
.MaximumLength
= RegistryPath
->Length
;
5408 registry_path
.Buffer
= ExAllocatePoolWithTag(PagedPool
, registry_path
.Length
, ALLOC_TAG
);
5410 if (!registry_path
.Buffer
) {
5411 ERR("out of memory\n");
5412 return STATUS_INSUFFICIENT_RESOURCES
;
5415 RtlCopyMemory(registry_path
.Buffer
, RegistryPath
->Buffer
, registry_path
.Length
);
5417 read_registry(®istry_path
, FALSE
);
5420 if (debug_log_level
> 0)
5426 TRACE("DriverEntry\n");
5432 if (RtlIsNtDdiVersionAvailable(NTDDI_WIN8
)) {
5433 UNICODE_STRING name
;
5434 tPsIsDiskCountersEnabled fPsIsDiskCountersEnabled
;
5436 RtlInitUnicodeString(&name
, L
"PsIsDiskCountersEnabled");
5437 fPsIsDiskCountersEnabled
= (tPsIsDiskCountersEnabled
)MmGetSystemRoutineAddress(&name
);
5439 if (fPsIsDiskCountersEnabled
) {
5440 diskacc
= fPsIsDiskCountersEnabled();
5442 RtlInitUnicodeString(&name
, L
"PsUpdateDiskCounters");
5443 fPsUpdateDiskCounters
= (tPsUpdateDiskCounters
)MmGetSystemRoutineAddress(&name
);
5445 if (!fPsUpdateDiskCounters
)
5448 RtlInitUnicodeString(&name
, L
"FsRtlUpdateDiskCounters");
5449 fFsRtlUpdateDiskCounters
= (tFsRtlUpdateDiskCounters
)MmGetSystemRoutineAddress(&name
);
5452 RtlInitUnicodeString(&name
, L
"CcCopyReadEx");
5453 fCcCopyReadEx
= (tCcCopyReadEx
)MmGetSystemRoutineAddress(&name
);
5455 RtlInitUnicodeString(&name
, L
"CcCopyWriteEx");
5456 fCcCopyWriteEx
= (tCcCopyWriteEx
)MmGetSystemRoutineAddress(&name
);
5458 RtlInitUnicodeString(&name
, L
"CcSetAdditionalCacheAttributesEx");
5459 fCcSetAdditionalCacheAttributesEx
= (tCcSetAdditionalCacheAttributesEx
)MmGetSystemRoutineAddress(&name
);
5461 fPsUpdateDiskCounters
= NULL
;
5462 fCcCopyReadEx
= NULL
;
5463 fCcCopyWriteEx
= NULL
;
5464 fCcSetAdditionalCacheAttributesEx
= NULL
;
5465 fFsRtlUpdateDiskCounters
= NULL
;
5468 drvobj
= DriverObject
;
5470 DriverObject
->DriverUnload
= DriverUnload
;
5472 DriverObject
->DriverExtension
->AddDevice
= AddDevice
;
5474 DriverObject
->MajorFunction
[IRP_MJ_CREATE
] = (PDRIVER_DISPATCH
)drv_create
;
5475 DriverObject
->MajorFunction
[IRP_MJ_CLOSE
] = (PDRIVER_DISPATCH
)drv_close
;
5476 DriverObject
->MajorFunction
[IRP_MJ_READ
] = (PDRIVER_DISPATCH
)drv_read
;
5477 DriverObject
->MajorFunction
[IRP_MJ_WRITE
] = (PDRIVER_DISPATCH
)drv_write
;
5478 DriverObject
->MajorFunction
[IRP_MJ_QUERY_INFORMATION
] = (PDRIVER_DISPATCH
)drv_query_information
;
5479 DriverObject
->MajorFunction
[IRP_MJ_SET_INFORMATION
] = (PDRIVER_DISPATCH
)drv_set_information
;
5480 DriverObject
->MajorFunction
[IRP_MJ_QUERY_EA
] = (PDRIVER_DISPATCH
)drv_query_ea
;
5481 DriverObject
->MajorFunction
[IRP_MJ_SET_EA
] = (PDRIVER_DISPATCH
)drv_set_ea
;
5482 DriverObject
->MajorFunction
[IRP_MJ_FLUSH_BUFFERS
] = (PDRIVER_DISPATCH
)drv_flush_buffers
;
5483 DriverObject
->MajorFunction
[IRP_MJ_QUERY_VOLUME_INFORMATION
] = (PDRIVER_DISPATCH
)drv_query_volume_information
;
5484 DriverObject
->MajorFunction
[IRP_MJ_SET_VOLUME_INFORMATION
] = (PDRIVER_DISPATCH
)drv_set_volume_information
;
5485 DriverObject
->MajorFunction
[IRP_MJ_DIRECTORY_CONTROL
] = (PDRIVER_DISPATCH
)drv_directory_control
;
5486 DriverObject
->MajorFunction
[IRP_MJ_FILE_SYSTEM_CONTROL
] = (PDRIVER_DISPATCH
)drv_file_system_control
;
5487 DriverObject
->MajorFunction
[IRP_MJ_DEVICE_CONTROL
] = (PDRIVER_DISPATCH
)drv_device_control
;
5488 DriverObject
->MajorFunction
[IRP_MJ_SHUTDOWN
] = (PDRIVER_DISPATCH
)drv_shutdown
;
5489 DriverObject
->MajorFunction
[IRP_MJ_LOCK_CONTROL
] = (PDRIVER_DISPATCH
)drv_lock_control
;
5490 DriverObject
->MajorFunction
[IRP_MJ_CLEANUP
] = (PDRIVER_DISPATCH
)drv_cleanup
;
5491 DriverObject
->MajorFunction
[IRP_MJ_QUERY_SECURITY
] = (PDRIVER_DISPATCH
)drv_query_security
;
5492 DriverObject
->MajorFunction
[IRP_MJ_SET_SECURITY
] = (PDRIVER_DISPATCH
)drv_set_security
;
5493 DriverObject
->MajorFunction
[IRP_MJ_POWER
] = (PDRIVER_DISPATCH
)drv_power
;
5494 DriverObject
->MajorFunction
[IRP_MJ_SYSTEM_CONTROL
] = (PDRIVER_DISPATCH
)drv_system_control
;
5495 DriverObject
->MajorFunction
[IRP_MJ_PNP
] = (PDRIVER_DISPATCH
)drv_pnp
;
5497 init_fast_io_dispatch(&DriverObject
->FastIoDispatch
);
5499 device_nameW
.Buffer
= device_name
;
5500 device_nameW
.Length
= device_nameW
.MaximumLength
= (USHORT
)wcslen(device_name
) * sizeof(WCHAR
);
5501 dosdevice_nameW
.Buffer
= dosdevice_name
;
5502 dosdevice_nameW
.Length
= dosdevice_nameW
.MaximumLength
= (USHORT
)wcslen(dosdevice_name
) * sizeof(WCHAR
);
5504 Status
= IoCreateDevice(DriverObject
, sizeof(control_device_extension
), &device_nameW
, FILE_DEVICE_DISK_FILE_SYSTEM
,
5505 FILE_DEVICE_SECURE_OPEN
, FALSE
, &DeviceObject
);
5506 if (!NT_SUCCESS(Status
)) {
5507 ERR("IoCreateDevice returned %08x\n", Status
);
5511 master_devobj
= DeviceObject
;
5512 cde
= (control_device_extension
*)master_devobj
->DeviceExtension
;
5514 RtlZeroMemory(cde
, sizeof(control_device_extension
));
5516 cde
->type
= VCB_TYPE_CONTROL
;
5518 DeviceObject
->Flags
&= ~DO_DEVICE_INITIALIZING
;
5520 Status
= IoCreateSymbolicLink(&dosdevice_nameW
, &device_nameW
);
5521 if (!NT_SUCCESS(Status
)) {
5522 ERR("IoCreateSymbolicLink returned %08x\n", Status
);
5526 Status
= init_cache();
5527 if (!NT_SUCCESS(Status
)) {
5528 ERR("init_cache returned %08x\n", Status
);
5532 InitializeListHead(&VcbList
);
5533 ExInitializeResourceLite(&global_loading_lock
);
5534 ExInitializeResourceLite(&pdo_list_lock
);
5536 InitializeListHead(&pdo_list
);
5538 InitializeObjectAttributes(&oa
, RegistryPath
, OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
, NULL
, NULL
);
5539 Status
= ZwCreateKey(®h
, KEY_QUERY_VALUE
| KEY_ENUMERATE_SUB_KEYS
| KEY_NOTIFY
, &oa
, 0, NULL
, REG_OPTION_NON_VOLATILE
, &dispos
);
5540 if (!NT_SUCCESS(Status
)) {
5541 ERR("ZwCreateKey returned %08x\n", Status
);
5545 watch_registry(regh
);
5547 Status
= IoReportDetectedDevice(drvobj
, InterfaceTypeUndefined
, 0xFFFFFFFF, 0xFFFFFFFF,
5548 NULL
, NULL
, 0, &cde
->buspdo
);
5549 if (!NT_SUCCESS(Status
)) {
5550 ERR("IoReportDetectedDevice returned %08x\n", Status
);
5554 Status
= IoRegisterDeviceInterface(cde
->buspdo
, &BtrfsBusInterface
, NULL
, &cde
->bus_name
);
5555 if (!NT_SUCCESS(Status
))
5556 WARN("IoRegisterDeviceInterface returned %08x\n", Status
);
5558 cde
->attached_device
= IoAttachDeviceToDeviceStack(DeviceObject
, cde
->buspdo
);
5560 Status
= IoSetDeviceInterfaceState(&cde
->bus_name
, TRUE
);
5561 if (!NT_SUCCESS(Status
))
5562 WARN("IoSetDeviceInterfaceState returned %08x\n", Status
);
5564 DeviceObject
->Flags
&= ~DO_DEVICE_INITIALIZING
;
5566 IoInvalidateDeviceRelations(cde
->buspdo
, BusRelations
);
5568 Status
= PsCreateSystemThread(°raded_wait_handle
, 0, NULL
, NULL
, NULL
, degraded_wait_thread
, NULL
);
5569 if (!NT_SUCCESS(Status
))
5570 WARN("PsCreateSystemThread returned %08x\n", Status
);
5572 Status
= IoRegisterPlugPlayNotification(EventCategoryDeviceInterfaceChange
, PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES
,
5573 (PVOID
)&GUID_DEVINTERFACE_VOLUME
, DriverObject
, volume_notification
, DriverObject
, ¬ification_entry2
);
5574 if (!NT_SUCCESS(Status
))
5575 ERR("IoRegisterPlugPlayNotification returned %08x\n", Status
);
5577 Status
= IoRegisterPlugPlayNotification(EventCategoryDeviceInterfaceChange
, PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES
,
5578 (PVOID
)&GUID_DEVINTERFACE_HIDDEN_VOLUME
, DriverObject
, volume_notification
, DriverObject
, ¬ification_entry3
);
5579 if (!NT_SUCCESS(Status
))
5580 ERR("IoRegisterPlugPlayNotification returned %08x\n", Status
);
5582 Status
= IoRegisterPlugPlayNotification(EventCategoryDeviceInterfaceChange
, PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES
,
5583 (PVOID
)&GUID_DEVINTERFACE_DISK
, DriverObject
, pnp_notification
, DriverObject
, ¬ification_entry
);
5584 if (!NT_SUCCESS(Status
))
5585 ERR("IoRegisterPlugPlayNotification returned %08x\n", Status
);
5587 finished_probing
= TRUE
;
5589 KeInitializeEvent(&mountmgr_thread_event
, NotificationEvent
, FALSE
);
5591 Status
= PsCreateSystemThread(&mountmgr_thread_handle
, 0, NULL
, NULL
, NULL
, mountmgr_thread
, NULL
);
5592 if (!NT_SUCCESS(Status
))
5593 WARN("PsCreateSystemThread returned %08x\n", Status
);
5595 IoRegisterFileSystem(DeviceObject
);
5597 return STATUS_SUCCESS
;