#include <initguid.h>
#include <wdmguid.h>
+#include <ioevent.h>
extern ERESOURCE pdo_list_lock;
extern LIST_ENTRY pdo_list;
extern PDEVICE_OBJECT busobj;
extern tIoUnregisterPlugPlayNotificationEx fIoUnregisterPlugPlayNotificationEx;
extern ERESOURCE boot_lock;
+extern PDRIVER_OBJECT drvobj;
-typedef void (*pnp_callback)(PDRIVER_OBJECT DriverObject, PUNICODE_STRING devpath);
+typedef void (*pnp_callback)(PUNICODE_STRING devpath);
+
+#ifndef __REACTOS__
+// not in mingw yet
+#ifndef _MSC_VER
+DEFINE_GUID(GUID_IO_VOLUME_FVE_STATUS_CHANGE, 0x062998b2, 0xee1f, 0x4b6a, 0xb8, 0x57, 0xe7, 0x6c, 0xbb, 0xe9, 0xa6, 0xda);
+#endif
+#endif // __REACTOS__
extern PDEVICE_OBJECT master_devobj;
+typedef struct {
+ LIST_ENTRY list_entry;
+ PDEVICE_OBJECT devobj;
+ void* notification_entry;
+ UNICODE_STRING devpath;
+ WCHAR buf[1];
+} fve_data;
+
+static LIST_ENTRY fve_data_list = { &fve_data_list, &fve_data_list };
+KSPIN_LOCK fve_data_lock;
+
static bool fs_ignored(BTRFS_UUID* uuid) {
UNICODE_STRING path, ignoreus;
NTSTATUS Status;
return ret;
}
-static void test_vol(PDEVICE_OBJECT DeviceObject, PFILE_OBJECT FileObject,
- PUNICODE_STRING devpath, DWORD disk_num, DWORD part_num, uint64_t length) {
+typedef struct {
+ PIO_WORKITEM work_item;
+ PFILE_OBJECT fileobj;
+ PDEVICE_OBJECT devobj;
+ UNICODE_STRING devpath;
+ WCHAR buf[1];
+} fve_callback_context;
+
+_Function_class_(IO_WORKITEM_ROUTINE)
+static void __stdcall fve_callback(PDEVICE_OBJECT DeviceObject, PVOID con) {
+ fve_callback_context* ctx = con;
+
+ UNUSED(DeviceObject);
+
+ if (volume_arrival(&ctx->devpath, true)) {
+ KIRQL irql;
+ LIST_ENTRY* le;
+ fve_data* d = NULL;
+
+ // volume no longer locked - unregister notification
+
+ KeAcquireSpinLock(&fve_data_lock, &irql);
+
+ le = fve_data_list.Flink;
+ while (le != &fve_data_list) {
+ fve_data* d2 = CONTAINING_RECORD(le, fve_data, list_entry);
+
+ if (d2->devobj == ctx->devobj) {
+ RemoveEntryList(&d2->list_entry);
+ d = d2;
+ break;
+ }
+
+ le = le->Flink;
+ }
+
+ KeReleaseSpinLock(&fve_data_lock, irql);
+
+ if (d) {
+ IoUnregisterPlugPlayNotification(d->notification_entry);
+ ExFreePool(d);
+ }
+ }
+
+ IoFreeWorkItem(ctx->work_item);
+ ExFreePool(ctx);
+}
+
+static NTSTATUS __stdcall event_notification(PVOID NotificationStructure, PVOID Context) {
+ TARGET_DEVICE_REMOVAL_NOTIFICATION* tdrn = NotificationStructure;
+ PDEVICE_OBJECT devobj = Context;
+ PIO_WORKITEM work_item;
+ fve_callback_context* ctx;
+ LIST_ENTRY* le;
+ KIRQL irql;
+
+ if (RtlCompareMemory(&tdrn->Event, &GUID_IO_VOLUME_FVE_STATUS_CHANGE, sizeof(GUID)) != sizeof(GUID))
+ return STATUS_SUCCESS;
+
+ /* The FVE event has trailing data, presumably telling us whether the volume has
+ * been unlocked or whatever, but unfortunately it's undocumented! */
+
+ work_item = IoAllocateWorkItem(master_devobj);
+ if (!work_item) {
+ ERR("out of memory\n");
+ return STATUS_SUCCESS;
+ }
+
+ KeAcquireSpinLock(&fve_data_lock, &irql);
+
+ le = fve_data_list.Flink;
+ while (le != &fve_data_list) {
+ fve_data* d = CONTAINING_RECORD(le, fve_data, list_entry);
+
+ if (d->devobj == devobj) {
+ ctx = ExAllocatePoolWithTag(NonPagedPool, offsetof(fve_callback_context, buf) + d->devpath.Length,
+ ALLOC_TAG);
+
+ if (!ctx) {
+ KeReleaseSpinLock(&fve_data_lock, irql);
+ ERR("out of memory\n");
+ IoFreeWorkItem(work_item);
+ return STATUS_SUCCESS;
+ }
+
+ RtlCopyMemory(ctx->buf, d->devpath.Buffer, d->devpath.Length);
+ ctx->devpath.Length = ctx->devpath.MaximumLength = d->devpath.Length;
+
+ KeReleaseSpinLock(&fve_data_lock, irql);
+
+ ctx->devpath.Buffer = ctx->buf;
+
+ ctx->fileobj = tdrn->FileObject;
+ ctx->devobj = devobj;
+ ctx->work_item = work_item;
+
+ IoQueueWorkItem(work_item, fve_callback, DelayedWorkQueue, ctx);
+
+ return STATUS_SUCCESS;
+ }
+
+ le = le->Flink;
+ }
+
+ KeReleaseSpinLock(&fve_data_lock, irql);
+
+ IoFreeWorkItem(work_item);
+
+ return STATUS_SUCCESS;
+}
+
+static void register_fve_callback(PDEVICE_OBJECT devobj, PFILE_OBJECT fileobj,
+ PUNICODE_STRING devpath) {
+ NTSTATUS Status;
+ KIRQL irql;
+ LIST_ENTRY* le;
+
+ fve_data* d = ExAllocatePoolWithTag(NonPagedPool, offsetof(fve_data, buf) + devpath->Length, ALLOC_TAG);
+ if (!d) {
+ ERR("out of memory\n");
+ return;
+ }
+
+ d->devpath.Buffer = d->buf;
+ d->devpath.Length = d->devpath.MaximumLength = devpath->Length;
+ RtlCopyMemory(d->devpath.Buffer, devpath->Buffer, devpath->Length);
+
+ KeAcquireSpinLock(&fve_data_lock, &irql);
+
+ le = fve_data_list.Flink;
+ while (le != &fve_data_list) {
+ fve_data* d2 = CONTAINING_RECORD(le, fve_data, list_entry);
+
+ if (d2->devobj == devobj) {
+ KeReleaseSpinLock(&fve_data_lock, irql);
+ ExFreePool(d);
+ return;
+ }
+
+ le = le->Flink;
+ }
+
+ KeReleaseSpinLock(&fve_data_lock, irql);
+
+ Status = IoRegisterPlugPlayNotification(EventCategoryTargetDeviceChange, 0, fileobj, drvobj, event_notification,
+ devobj, &d->notification_entry);
+ if (!NT_SUCCESS(Status)) {
+ ERR("IoRegisterPlugPlayNotification returned %08lx\n", Status);
+ return;
+ }
+
+ KeAcquireSpinLock(&fve_data_lock, &irql);
+
+ le = fve_data_list.Flink;
+ while (le != &fve_data_list) {
+ fve_data* d2 = CONTAINING_RECORD(le, fve_data, list_entry);
+
+ if (d2->devobj == devobj) {
+ KeReleaseSpinLock(&fve_data_lock, irql);
+ IoUnregisterPlugPlayNotification(d->notification_entry);
+ ExFreePool(d);
+ return;
+ }
+
+ le = le->Flink;
+ }
+
+ d->devobj = devobj;
+ InsertTailList(&fve_data_list, &d->list_entry);
+
+ KeReleaseSpinLock(&fve_data_lock, irql);
+}
+
+static bool test_vol(PDEVICE_OBJECT DeviceObject, PFILE_OBJECT FileObject,
+ PUNICODE_STRING devpath, DWORD disk_num, DWORD part_num, uint64_t length,
+ bool fve_callback) {
NTSTATUS Status;
ULONG toread;
uint8_t* data = NULL;
uint32_t sector_size;
+ bool ret = true;
TRACE("%.*S\n", (int)(devpath->Length / sizeof(WCHAR)), devpath->Buffer);
add_volume_device(sb, devpath, length, disk_num, part_num);
}
}
+ } else if (Status == STATUS_FVE_LOCKED_VOLUME) {
+ if (fve_callback)
+ ret = false;
+ else
+ register_fve_callback(DeviceObject, FileObject, devpath);
}
deref:
if (data)
ExFreePool(data);
+
+ return ret;
}
NTSTATUS remove_drive_letter(PDEVICE_OBJECT mountmgr, PUNICODE_STRING devpath) {
return Status;
}
-void disk_arrival(PDRIVER_OBJECT DriverObject, PUNICODE_STRING devpath) {
+void disk_arrival(PUNICODE_STRING devpath) {
PFILE_OBJECT fileobj;
PDEVICE_OBJECT devobj;
NTSTATUS Status;
IO_STATUS_BLOCK iosb;
GET_LENGTH_INFORMATION gli;
- UNUSED(DriverObject);
-
ExAcquireResourceSharedLite(&boot_lock, TRUE);
Status = IoGetDeviceObjectPointer(devpath, FILE_READ_ATTRIBUTES, &fileobj, &devobj);
} else
TRACE("DeviceType = %lu, DeviceNumber = %lu, PartitionNumber = %lu\n", sdn.DeviceType, sdn.DeviceNumber, sdn.PartitionNumber);
- test_vol(devobj, fileobj, devpath, sdn.DeviceNumber, sdn.PartitionNumber, gli.Length.QuadPart);
+ test_vol(devobj, fileobj, devpath, sdn.DeviceNumber, sdn.PartitionNumber,
+ gli.Length.QuadPart, false);
end:
ObDereferenceObject(fileobj);
ExReleaseResourceLite(&pdode->child_lock);
}
-void volume_arrival(PDRIVER_OBJECT DriverObject, PUNICODE_STRING devpath) {
+bool volume_arrival(PUNICODE_STRING devpath, bool fve_callback) {
STORAGE_DEVICE_NUMBER sdn;
PFILE_OBJECT fileobj;
PDEVICE_OBJECT devobj;
GET_LENGTH_INFORMATION gli;
NTSTATUS Status;
+ bool ret = true;
TRACE("%.*S\n", (int)(devpath->Length / sizeof(WCHAR)), devpath->Buffer);
if (!NT_SUCCESS(Status)) {
ExReleaseResourceLite(&boot_lock);
ERR("IoGetDeviceObjectPointer returned %08lx\n", Status);
- return;
+ return false;
}
// make sure we're not processing devices we've created ourselves
- if (devobj->DriverObject == DriverObject)
+ if (devobj->DriverObject == drvobj)
goto end;
Status = dev_ioctl(devobj, IOCTL_VOLUME_ONLINE, NULL, 0, NULL, 0, true, NULL);
ExReleaseResourceLite(&pdo_list_lock);
}
- test_vol(devobj, fileobj, devpath, sdn.DeviceNumber, sdn.PartitionNumber, gli.Length.QuadPart);
+ ret = test_vol(devobj, fileobj, devpath, sdn.DeviceNumber, sdn.PartitionNumber,
+ gli.Length.QuadPart, fve_callback);
end:
ObDereferenceObject(fileobj);
ExReleaseResourceLite(&boot_lock);
+
+ return ret;
}
-void volume_removal(PDRIVER_OBJECT DriverObject, PUNICODE_STRING devpath) {
+static void volume_arrival2(PUNICODE_STRING devpath) {
+ volume_arrival(devpath, false);
+}
+
+void volume_removal(PUNICODE_STRING devpath) {
LIST_ENTRY* le;
UNICODE_STRING devpath2;
TRACE("%.*S\n", (int)(devpath->Length / sizeof(WCHAR)), devpath->Buffer);
- UNUSED(DriverObject);
-
devpath2 = *devpath;
if (devpath->Length > 4 * sizeof(WCHAR) && devpath->Buffer[0] == '\\' && (devpath->Buffer[1] == '\\' || devpath->Buffer[1] == '?') &&
}
typedef struct {
- PDRIVER_OBJECT DriverObject;
UNICODE_STRING name;
pnp_callback func;
PIO_WORKITEM work_item;
UNUSED(DeviceObject);
- context->func(context->DriverObject, &context->name);
+ context->func(&context->name);
if (context->name.Buffer)
ExFreePool(context->name.Buffer);
ExFreePool(context);
}
-static void enqueue_pnp_callback(PDRIVER_OBJECT DriverObject, PUNICODE_STRING name, pnp_callback func) {
+static void enqueue_pnp_callback(PUNICODE_STRING name, pnp_callback func) {
PIO_WORKITEM work_item;
pnp_callback_context* context;
return;
}
- context->DriverObject = DriverObject;
-
if (name->Length > 0) {
context->name.Buffer = ExAllocatePoolWithTag(PagedPool, name->Length, ALLOC_TAG);
if (!context->name.Buffer) {
_Function_class_(DRIVER_NOTIFICATION_CALLBACK_ROUTINE)
NTSTATUS __stdcall volume_notification(PVOID NotificationStructure, PVOID Context) {
DEVICE_INTERFACE_CHANGE_NOTIFICATION* dicn = (DEVICE_INTERFACE_CHANGE_NOTIFICATION*)NotificationStructure;
- PDRIVER_OBJECT DriverObject = (PDRIVER_OBJECT)Context;
+
+ UNUSED(Context);
if (RtlCompareMemory(&dicn->Event, &GUID_DEVICE_INTERFACE_ARRIVAL, sizeof(GUID)) == sizeof(GUID))
- enqueue_pnp_callback(DriverObject, dicn->SymbolicLinkName, volume_arrival);
+ enqueue_pnp_callback(dicn->SymbolicLinkName, volume_arrival2);
else if (RtlCompareMemory(&dicn->Event, &GUID_DEVICE_INTERFACE_REMOVAL, sizeof(GUID)) == sizeof(GUID))
- enqueue_pnp_callback(DriverObject, dicn->SymbolicLinkName, volume_removal);
+ enqueue_pnp_callback(dicn->SymbolicLinkName, volume_removal);
return STATUS_SUCCESS;
}
_Function_class_(DRIVER_NOTIFICATION_CALLBACK_ROUTINE)
NTSTATUS __stdcall pnp_notification(PVOID NotificationStructure, PVOID Context) {
DEVICE_INTERFACE_CHANGE_NOTIFICATION* dicn = (DEVICE_INTERFACE_CHANGE_NOTIFICATION*)NotificationStructure;
- PDRIVER_OBJECT DriverObject = (PDRIVER_OBJECT)Context;
+
+ UNUSED(Context);
if (RtlCompareMemory(&dicn->Event, &GUID_DEVICE_INTERFACE_ARRIVAL, sizeof(GUID)) == sizeof(GUID))
- enqueue_pnp_callback(DriverObject, dicn->SymbolicLinkName, disk_arrival);
+ enqueue_pnp_callback(dicn->SymbolicLinkName, disk_arrival);
else if (RtlCompareMemory(&dicn->Event, &GUID_DEVICE_INTERFACE_REMOVAL, sizeof(GUID)) == sizeof(GUID))
- enqueue_pnp_callback(DriverObject, dicn->SymbolicLinkName, volume_removal);
+ enqueue_pnp_callback(dicn->SymbolicLinkName, volume_removal);
return STATUS_SUCCESS;
}