diff --git a/build/VStudio/winfsp.vcxproj b/build/VStudio/winfsp.vcxproj
index 4a4418c9..f7d94004 100644
--- a/build/VStudio/winfsp.vcxproj
+++ b/build/VStudio/winfsp.vcxproj
@@ -142,6 +142,7 @@
+
diff --git a/build/VStudio/winfsp.vcxproj.filters b/build/VStudio/winfsp.vcxproj.filters
index f49c593f..eb72665c 100644
--- a/build/VStudio/winfsp.vcxproj.filters
+++ b/build/VStudio/winfsp.vcxproj.filters
@@ -74,6 +74,9 @@
Source
+
+ Source
+
diff --git a/src/sys/driver.h b/src/sys/driver.h
index d6027dc5..90a147c6 100644
--- a/src/sys/driver.h
+++ b/src/sys/driver.h
@@ -113,51 +113,6 @@
#pragma warning(disable:4100) /* unreferenced formal parameter */
#pragma warning(disable:4200) /* zero-sized array in struct/union */
-/* types */
-enum
-{
- FspFsctlDeviceExtensionKind = 'C', /* file system control device (e.g. \Device\WinFsp.Disk) */
- FspFsvrtDeviceExtensionKind = 'V', /* virtual volume device (e.g. \Device\Volume{GUID}) */
- FspFsvolDeviceExtensionKind = 'F', /* file system volume device (unnamed) */
-};
-typedef struct
-{
- UINT8 Kind;
-} FSP_DEVICE_EXTENSION;
-typedef struct
-{
- FSP_DEVICE_EXTENSION Base;
-} FSP_FSCTL_DEVICE_EXTENSION;
-typedef struct
-{
- FSP_DEVICE_EXTENSION Base;
- UINT8 SecurityDescriptorBuf[];
-} FSP_FSVRT_DEVICE_EXTENSION;
-typedef struct
-{
- FSP_DEVICE_EXTENSION Base;
-} FSP_FSVOL_DEVICE_EXTENSION;
-static inline
-FSP_DEVICE_EXTENSION *FspDeviceExtension(PDEVICE_OBJECT DeviceObject)
-{
- return DeviceObject->DeviceExtension;
-}
-static inline
-FSP_FSCTL_DEVICE_EXTENSION *FspFsctlDeviceExtension(PDEVICE_OBJECT DeviceObject)
-{
- return DeviceObject->DeviceExtension;
-}
-static inline
-FSP_FSVRT_DEVICE_EXTENSION *FspFsvrtDeviceExtension(PDEVICE_OBJECT DeviceObject)
-{
- return DeviceObject->DeviceExtension;
-}
-static inline
-FSP_FSVOL_DEVICE_EXTENSION *FspFsvolDeviceExtension(PDEVICE_OBJECT DeviceObject)
-{
- return DeviceObject->DeviceExtension;
-}
-
/* driver major functions */
DRIVER_DISPATCH FspCleanup;
DRIVER_DISPATCH FspClose;
@@ -190,6 +145,68 @@ FAST_IO_RELEASE_FOR_MOD_WRITE FspReleaseForModWrite;
FAST_IO_ACQUIRE_FOR_CCFLUSH FspAcquireForCcFlush;
FAST_IO_RELEASE_FOR_CCFLUSH FspReleaseForCcFlush;
+/* I/O queue */
+typedef struct
+{
+ KSPIN_LOCK SpinLock;
+ int Enabled;
+ LIST_ENTRY PendingIrpList, ProcessIrpList;
+ IO_CSQ PendingIoCsq, ProcessIoCsq;
+} FSP_IOQ;
+VOID FspIoqInitialize(FSP_IOQ *Ioq);
+VOID FspIoqEnable(FSP_IOQ *Ioq, int Delta);
+BOOLEAN FspIoqPostIrp(FSP_IOQ *Ioq, PIRP Irp);
+PIRP FspIoqNextPendingIrp(FSP_IOQ *Ioq);
+BOOLEAN FspIoqProcessIrp(FSP_IOQ *Ioq, PIRP Irp);
+PIRP FspIoqRemoveProcessIrp(FSP_IOQ *Ioq, UINT_PTR IrpHint);
+VOID FspIoqCancelAll(FSP_IOQ *Ioq);
+
+/* device extensions */
+enum
+{
+ FspFsctlDeviceExtensionKind = 'C', /* file system control device (e.g. \Device\WinFsp.Disk) */
+ FspFsvrtDeviceExtensionKind = 'V', /* virtual volume device (e.g. \Device\Volume{GUID}) */
+ FspFsvolDeviceExtensionKind = 'F', /* file system volume device (unnamed) */
+};
+typedef struct
+{
+ UINT8 Kind;
+} FSP_DEVICE_EXTENSION;
+typedef struct
+{
+ FSP_DEVICE_EXTENSION Base;
+} FSP_FSCTL_DEVICE_EXTENSION;
+typedef struct
+{
+ FSP_DEVICE_EXTENSION Base;
+ FSP_IOQ Ioq;
+ UINT8 SecurityDescriptorBuf[];
+} FSP_FSVRT_DEVICE_EXTENSION;
+typedef struct
+{
+ FSP_DEVICE_EXTENSION Base;
+} FSP_FSVOL_DEVICE_EXTENSION;
+static inline
+FSP_DEVICE_EXTENSION *FspDeviceExtension(PDEVICE_OBJECT DeviceObject)
+{
+ return DeviceObject->DeviceExtension;
+}
+static inline
+FSP_FSCTL_DEVICE_EXTENSION *FspFsctlDeviceExtension(PDEVICE_OBJECT DeviceObject)
+{
+ return DeviceObject->DeviceExtension;
+}
+static inline
+FSP_FSVRT_DEVICE_EXTENSION *FspFsvrtDeviceExtension(PDEVICE_OBJECT DeviceObject)
+{
+ return DeviceObject->DeviceExtension;
+}
+static inline
+FSP_FSVOL_DEVICE_EXTENSION *FspFsvolDeviceExtension(PDEVICE_OBJECT DeviceObject)
+{
+ return DeviceObject->DeviceExtension;
+}
+
/* misc */
NTSTATUS CreateGuid(GUID *Guid);
NTSTATUS SecuritySubjectContextAccessCheck(
diff --git a/src/sys/ioq.c b/src/sys/ioq.c
new file mode 100644
index 00000000..7371a2a2
--- /dev/null
+++ b/src/sys/ioq.c
@@ -0,0 +1,209 @@
+/**
+ * @file sys/ioq.c
+ *
+ * @copyright 2015 Bill Zissimopoulos
+ */
+
+#include
+
+/*
+ * An FSP_IOQ encapsulates the main FSP mechanism for handling IRP's.
+ * It has two queues: a "pending" queue for managing newly arrived IRP's
+ * and a "processing" queue for managing IRP's currently being processed
+ * (i.e. sent to the user-mode file system for further processing).
+ *
+ * IRP's arrive at a MajorFunction (MJ) and are then posted to the device's
+ * FSP_IOQ and marked pending. When the user-mode file system performs
+ * FSP_FSCTL_TRANSACT, the IRP's are removed from the pending queue and
+ * are then marshalled to the user process; prior to that they are added
+ * to the processing queue. At a later time the user-mode will perform
+ * another FSP_FSCTL_TRANSACT at which time any processed IRP's will be
+ * marshalled back to us and will be then removed from the processing queue
+ * and completed.
+ *
+ * State diagram:
+ * +--------------------+
+ * | | | ProcessIrp
+ * v | v
+ * +------------+ | +------------+
+ * | MJ | | | Processing |
+ * +------------+ | +------------+
+ * | | |
+ * | PostIrp | | RemoveProcessIrp
+ * v | v
+ * +------------+ | +------------+
+ * | Pending | | | TRANSACT |
+ * +------------+ | | IN |
+ * | | +------------+
+ * | NextPendingIrp | |
+ * v | | CompleteIrp
+ * +------------+ | v
+ * | TRANSACT | | +------------+
+ * | OUT | | | Completed |
+ * +------------+ | +------------+
+ * | |
+ * +---------------------+
+ */
+
+static NTSTATUS FspIoqPendingInsertIrpEx(PIO_CSQ IoCsq, PIRP Irp, PVOID InsertContext)
+{
+ FSP_IOQ *Ioq = CONTAINING_RECORD(IoCsq, FSP_IOQ, PendingIoCsq);
+
+ if (0 > Ioq->Enabled)
+ return STATUS_ACCESS_DENIED;
+
+ InsertTailList(&Ioq->PendingIrpList, &Irp->Tail.Overlay.ListEntry);
+ return STATUS_SUCCESS;
+}
+
+static VOID FspIoqPendingRemoveIrp(PIO_CSQ IoCsq, PIRP Irp)
+{
+ RemoveEntryList(&Irp->Tail.Overlay.ListEntry);
+}
+
+static PIRP FspIoqPendingPeekNextIrp(PIO_CSQ IoCsq, PIRP Irp, PVOID PeekContext)
+{
+ FSP_IOQ *Ioq = CONTAINING_RECORD(IoCsq, FSP_IOQ, PendingIoCsq);
+ PLIST_ENTRY Head, Entry;
+
+ if (!PeekContext && 0 > Ioq->Enabled)
+ return 0;
+
+ Head = &Ioq->PendingIrpList;
+ if (0 == Irp)
+ Entry = Head->Flink;
+ else
+ Entry = Irp->Tail.Overlay.ListEntry.Flink;
+
+ return Head != Entry ? CONTAINING_RECORD(Entry, IRP, Tail.Overlay.ListEntry) : 0;
+}
+
+static VOID FspIoqPendingAcquireLock(PIO_CSQ IoCsq, PKIRQL Irql)
+{
+ FSP_IOQ *Ioq = CONTAINING_RECORD(IoCsq, FSP_IOQ, PendingIoCsq);
+ KeAcquireSpinLock(&Ioq->SpinLock, Irql);
+}
+
+static VOID FspIoqPendingReleaseLock(PIO_CSQ IoCsq, KIRQL Irql)
+{
+ FSP_IOQ *Ioq = CONTAINING_RECORD(IoCsq, FSP_IOQ, PendingIoCsq);
+ KeReleaseSpinLock(&Ioq->SpinLock, Irql);
+}
+
+static NTSTATUS FspIoqProcessInsertIrpEx(PIO_CSQ IoCsq, PIRP Irp, PVOID InsertContext)
+{
+ FSP_IOQ *Ioq = CONTAINING_RECORD(IoCsq, FSP_IOQ, ProcessIoCsq);
+
+ if (0 > Ioq->Enabled)
+ return STATUS_ACCESS_DENIED;
+
+ InsertTailList(&Ioq->ProcessIrpList, &Irp->Tail.Overlay.ListEntry);
+ return STATUS_SUCCESS;
+}
+
+static VOID FspIoqProcessRemoveIrp(PIO_CSQ IoCsq, PIRP Irp)
+{
+ RemoveEntryList(&Irp->Tail.Overlay.ListEntry);
+}
+
+static PIRP FspIoqProcessPeekNextIrp(PIO_CSQ IoCsq, PIRP Irp, PVOID PeekContext)
+{
+ FSP_IOQ *Ioq = CONTAINING_RECORD(IoCsq, FSP_IOQ, ProcessIoCsq);
+ PLIST_ENTRY Head, Entry;
+
+ if (!PeekContext && 0 > Ioq->Enabled)
+ return 0;
+
+ Head = &Ioq->ProcessIrpList;
+ if (0 == Irp)
+ Entry = Head->Flink;
+ else
+ Entry = Irp->Tail.Overlay.ListEntry.Flink;
+
+ for (; Head != Entry; Entry = Entry->Flink)
+ {
+ Irp = CONTAINING_RECORD(Entry, IRP, Tail.Overlay.ListEntry);
+ if (Irp == PeekContext)
+ return Irp;
+ }
+
+ return 0;
+}
+
+static VOID FspIoqProcessAcquireLock(PIO_CSQ IoCsq, PKIRQL Irql)
+{
+ FSP_IOQ *Ioq = CONTAINING_RECORD(IoCsq, FSP_IOQ, ProcessIoCsq);
+ KeAcquireSpinLock(&Ioq->SpinLock, Irql);
+}
+
+static VOID FspIoqProcessReleaseLock(PIO_CSQ IoCsq, KIRQL Irql)
+{
+ FSP_IOQ *Ioq = CONTAINING_RECORD(IoCsq, FSP_IOQ, ProcessIoCsq);
+ KeReleaseSpinLock(&Ioq->SpinLock, Irql);
+}
+
+static VOID FspIoqCompleteCanceledIrp(PIO_CSQ IoCsq, PIRP Irp)
+{
+ Irp->IoStatus.Status = STATUS_CANCELLED;
+ Irp->IoStatus.Information = 0;
+ IoCompleteRequest(Irp, FSP_IO_INCREMENT);
+}
+
+VOID FspIoqInitialize(FSP_IOQ *Ioq)
+{
+ RtlZeroMemory(Ioq, sizeof *Ioq);
+ KeInitializeSpinLock(&Ioq->SpinLock);
+ InitializeListHead(&Ioq->PendingIrpList);
+ InitializeListHead(&Ioq->ProcessIrpList);
+ IoCsqInitializeEx(&Ioq->PendingIoCsq,
+ FspIoqPendingInsertIrpEx,
+ FspIoqPendingRemoveIrp,
+ FspIoqPendingPeekNextIrp,
+ FspIoqPendingAcquireLock,
+ FspIoqPendingReleaseLock,
+ FspIoqCompleteCanceledIrp);
+ IoCsqInitializeEx(&Ioq->ProcessIoCsq,
+ FspIoqProcessInsertIrpEx,
+ FspIoqProcessRemoveIrp,
+ FspIoqProcessPeekNextIrp,
+ FspIoqProcessAcquireLock,
+ FspIoqProcessReleaseLock,
+ FspIoqCompleteCanceledIrp);
+}
+
+VOID FspIoqEnable(FSP_IOQ *Ioq, int Delta)
+{
+ KIRQL Irql;
+ KeAcquireSpinLock(&Ioq->SpinLock, &Irql);
+ Ioq->Enabled += Delta;
+ KeReleaseSpinLock(&Ioq->SpinLock, Irql);
+}
+
+BOOLEAN FspIoqPostIrp(FSP_IOQ *Ioq, PIRP Irp)
+{
+ return STATUS_SUCCESS == IoCsqInsertIrpEx(&Ioq->PendingIoCsq, Irp, 0, 0);
+}
+
+PIRP FspIoqNextPendingIrp(FSP_IOQ *Ioq)
+{
+ return IoCsqRemoveNextIrp(&Ioq->PendingIoCsq, (PVOID)1);
+}
+
+BOOLEAN FspIoqProcessIrp(FSP_IOQ *Ioq, PIRP Irp)
+{
+ return STATUS_SUCCESS == IoCsqInsertIrpEx(&Ioq->ProcessIoCsq, Irp, 0, 0);
+}
+
+PIRP FspIoqRemoveProcessIrp(FSP_IOQ *Ioq, UINT_PTR IrpHint)
+{
+ return IoCsqRemoveNextIrp(&Ioq->PendingIoCsq, (PVOID)IrpHint);
+}
+
+VOID FspIoqCancelAll(FSP_IOQ *Ioq)
+{
+ PIRP Irp;
+ while (0 != (Irp = IoCsqRemoveNextIrp(&Ioq->PendingIoCsq, 0)))
+ FspIoqCompleteCanceledIrp(&Ioq->PendingIoCsq, Irp);
+ while (0 != (Irp = IoCsqRemoveNextIrp(&Ioq->ProcessIoCsq, 0)))
+ FspIoqCompleteCanceledIrp(&Ioq->ProcessIoCsq, Irp);
+}