diff --git a/build/VStudio/winfsp_sys.vcxproj b/build/VStudio/winfsp_sys.vcxproj
index f581e221..21747b51 100644
--- a/build/VStudio/winfsp_sys.vcxproj
+++ b/build/VStudio/winfsp_sys.vcxproj
@@ -177,6 +177,7 @@
+
diff --git a/build/VStudio/winfsp_sys.vcxproj.filters b/build/VStudio/winfsp_sys.vcxproj.filters
index f13f62ca..cbe042c7 100644
--- a/build/VStudio/winfsp_sys.vcxproj.filters
+++ b/build/VStudio/winfsp_sys.vcxproj.filters
@@ -95,6 +95,9 @@
Source
+
+ Source
+
diff --git a/src/sys/device.c b/src/sys/device.c
index f71c9678..4dfef85a 100644
--- a/src/sys/device.c
+++ b/src/sys/device.c
@@ -364,6 +364,12 @@ static NTSTATUS FspFsvolDeviceInit(PDEVICE_OBJECT DeviceObject)
InitializeListHead(&FsvolDeviceExtension->NotifyList);
FsvolDeviceExtension->InitDoneNotify = 1;
+ /* create file system statistics */
+ Result = FspStatisticsCreate(&FsvolDeviceExtension->Statistics);
+ if (!NT_SUCCESS(Result))
+ return Result;
+ FsvolDeviceExtension->InitDoneStat = 1;
+
/* initialize our context table */
ExInitializeResourceLite(&FsvolDeviceExtension->FileRenameResource);
ExInitializeResourceLite(&FsvolDeviceExtension->ContextTableResource);
@@ -408,6 +414,10 @@ static VOID FspFsvolDeviceFini(PDEVICE_OBJECT DeviceObject)
if (FsvolDeviceExtension->InitDoneTimer)
IoStopTimer(DeviceObject);
+ /* delete the file system statistics */
+ if (FsvolDeviceExtension->InitDoneStat)
+ FspStatisticsDelete(FsvolDeviceExtension->Statistics);
+
/* uninitialize the FSRTL Notify mechanism */
if (FsvolDeviceExtension->InitDoneNotify)
{
diff --git a/src/sys/driver.c b/src/sys/driver.c
index 12fffa15..41ee0b6a 100644
--- a/src/sys/driver.c
+++ b/src/sys/driver.c
@@ -176,6 +176,8 @@ NTSTATUS DriverEntry(
static VOID FspDriverMultiVersionInitialize(VOID)
{
+ FspProcessorCount = KeQueryActiveProcessorCount(0);
+
ExInitializeDriverRuntime(DrvRtPoolNxOptIn);
if (RtlIsNtDdiVersionAvailable(NTDDI_WIN7))
@@ -216,5 +218,6 @@ PDEVICE_OBJECT FspFsctlNetDeviceObject;
FAST_IO_DISPATCH FspFastIoDispatch;
CACHE_MANAGER_CALLBACKS FspCacheManagerCallbacks;
+ULONG FspProcessorCount;
FSP_MV_CcCoherencyFlushAndPurgeCache *FspMvCcCoherencyFlushAndPurgeCache;
ULONG FspMvMdlMappingNoWrite = 0;
diff --git a/src/sys/driver.h b/src/sys/driver.h
index 062c3e6d..c1d49319 100644
--- a/src/sys/driver.h
+++ b/src/sys/driver.h
@@ -949,6 +949,21 @@ VOID FspWqPostIrpWorkItem(PIRP Irp);
#define FspWqRepostIrpWorkItem(I, RW, RF)\
FspWqCreateAndPostIrpWorkItem(I, RW, RF, TRUE)
+/* file system statistics */
+typedef struct
+{
+ FILESYSTEM_STATISTICS Base;
+ FAT_STATISTICS Specific; /* pretend that we are FAT when it comes to stats */
+ /* align to 64 bytes */
+ __declspec(align(64)) UINT8 EndOfStruct[];
+} FSP_STATISTICS;
+NTSTATUS FspStatisticsCreate(FSP_STATISTICS **PStatistics);
+VOID FspStatisticsDelete(FSP_STATISTICS *Statistics);
+NTSTATUS FspStatisticsCopy(FSP_STATISTICS *Statistics, PVOID Buffer, PULONG PLength);
+#define FspStatistics(S) (&(S)[KeGetCurrentProcessorNumber() % FspProcessorCount])
+#define FspStatisticsInc(S,F) ((S)->F++)
+#define FspStatisticsAdd(S,F,V) ((S)->F += (V))
+
/* device management */
enum
{
@@ -990,7 +1005,7 @@ typedef struct
{
FSP_DEVICE_EXTENSION Base;
UINT32 InitDoneFsvrt:1, InitDoneIoq:1, InitDoneSec:1, InitDoneDir:1, InitDoneStrm:1,
- InitDoneCtxTab:1, InitDoneTimer:1, InitDoneInfo:1, InitDoneNotify:1;
+ InitDoneCtxTab:1, InitDoneTimer:1, InitDoneInfo:1, InitDoneNotify:1, InitDoneStat:1;
PDEVICE_OBJECT FsctlDeviceObject;
PDEVICE_OBJECT FsvrtDeviceObject;
HANDLE MupHandle;
@@ -1016,6 +1031,7 @@ typedef struct
FSP_FSCTL_VOLUME_INFO VolumeInfo;
PNOTIFY_SYNC NotifySync;
LIST_ENTRY NotifyList;
+ FSP_STATISTICS *Statistics;
} FSP_FSVOL_DEVICE_EXTENSION;
static inline
FSP_DEVICE_EXTENSION *FspDeviceExtension(PDEVICE_OBJECT DeviceObject)
@@ -1083,6 +1099,8 @@ VOID FspDeviceGlobalUnlock(VOID)
extern ERESOURCE FspDeviceGlobalResource;
ExReleaseResourceLite(&FspDeviceGlobalResource);
}
+#define FspFsvolDeviceStatistics(DeviceObject)\
+ FspStatistics(FspFsvolDeviceExtension(DeviceObject)->Statistics)
#define FspFsvolDeviceStoppedStatus(DeviceObject)\
STATUS_VOLUME_DISMOUNTED
//(FILE_DEVICE_DISK_FILE_SYSTEM == (DeviceObject)->DeviceType ?\
@@ -1486,6 +1504,7 @@ extern FSP_IOCMPL_DISPATCH *FspIopCompleteFunction[];
extern ERESOURCE FspDeviceGlobalResource;
extern WCHAR FspFileDescDirectoryPatternMatchAll[];
extern const GUID FspMainFileOpenEcpGuid;
+extern ULONG FspProcessorCount;
extern FSP_MV_CcCoherencyFlushAndPurgeCache *FspMvCcCoherencyFlushAndPurgeCache;
extern ULONG FspMvMdlMappingNoWrite;
diff --git a/src/sys/fsctl.c b/src/sys/fsctl.c
index 6aaf6df3..701156b7 100644
--- a/src/sys/fsctl.c
+++ b/src/sys/fsctl.c
@@ -31,6 +31,8 @@ static IO_COMPLETION_ROUTINE FspFsvolFileSystemControlOplockCompletion;
static WORKER_THREAD_ROUTINE FspFsvolFileSystemControlOplockCompletionWork;
static NTSTATUS FspFsvolFileSystemControlQueryPersistentVolumeState(
PDEVICE_OBJECT FsvolDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp);
+static NTSTATUS FspFsvolFileSystemControlGetStatistics(
+ PDEVICE_OBJECT FsvolDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp);
static NTSTATUS FspFsvolFileSystemControl(
PDEVICE_OBJECT FsvolDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp);
FSP_IOCMPL_DISPATCH FspFsvolFileSystemControlComplete;
@@ -45,6 +47,7 @@ FSP_DRIVER_DISPATCH FspFileSystemControl;
// !#pragma alloc_text(PAGE, FspFsvolFileSystemControlOplockCompletion)
#pragma alloc_text(PAGE, FspFsvolFileSystemControlOplockCompletionWork)
#pragma alloc_text(PAGE, FspFsvolFileSystemControlQueryPersistentVolumeState)
+#pragma alloc_text(PAGE, FspFsvolFileSystemControlGetStatistics)
#pragma alloc_text(PAGE, FspFsvolFileSystemControl)
#pragma alloc_text(PAGE, FspFsvolFileSystemControlComplete)
#pragma alloc_text(PAGE, FspFsvolFileSystemControlRequestFini)
@@ -513,6 +516,23 @@ static NTSTATUS FspFsvolFileSystemControlQueryPersistentVolumeState(
return STATUS_SUCCESS;
}
+static NTSTATUS FspFsvolFileSystemControlGetStatistics(
+ PDEVICE_OBJECT FsvolDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp)
+{
+ PAGED_CODE();
+
+ NTSTATUS Result;
+ FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(FsvolDeviceObject);
+ PVOID Buffer = Irp->AssociatedIrp.SystemBuffer;
+ ULONG Length = IrpSp->Parameters.FileSystemControl.OutputBufferLength;
+
+ Result = FspStatisticsCopy(FsvolDeviceExtension->Statistics, Buffer, &Length);
+
+ Irp->IoStatus.Information = Length;
+
+ return Result;
+}
+
static NTSTATUS FspFsvolFileSystemControl(
PDEVICE_OBJECT FsvolDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp)
{
@@ -549,6 +569,9 @@ static NTSTATUS FspFsvolFileSystemControl(
case FSCTL_QUERY_PERSISTENT_VOLUME_STATE:
Result = FspFsvolFileSystemControlQueryPersistentVolumeState(FsvolDeviceObject, Irp, IrpSp);
break;
+ case FSCTL_FILESYSTEM_GET_STATISTICS:
+ Result = FspFsvolFileSystemControlGetStatistics(FsvolDeviceObject, Irp, IrpSp);
+ break;
}
break;
}
diff --git a/src/sys/iop.c b/src/sys/iop.c
index 3dec1fe5..d0d837a1 100644
--- a/src/sys/iop.c
+++ b/src/sys/iop.c
@@ -281,6 +281,28 @@ VOID FspIopCompleteIrpEx(PIRP Irp, NTSTATUS Result, BOOLEAN DeviceDereference)
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
PDEVICE_OBJECT DeviceObject = IrpSp->DeviceObject;
+ /*
+ * HACK:
+ *
+ * We update the Create statistics here to avoid doing it in multiple places.
+ */
+ if (IRP_MJ_CREATE == IrpSp->MajorFunction)
+ {
+ FSP_DEVICE_EXTENSION *DeviceExtension = FspDeviceExtension(DeviceObject);
+
+ if (FspFsvolDeviceExtensionKind == FspDeviceExtension(DeviceObject)->Kind)
+ {
+ FSP_STATISTICS *Statistics = FspStatistics(
+ ((FSP_FSVOL_DEVICE_EXTENSION *)DeviceExtension)->Statistics);
+
+ FspStatisticsInc(Statistics, Specific.CreateHits);
+ if (STATUS_SUCCESS == Result)
+ FspStatisticsInc(Statistics, Specific.SuccessfulCreates);
+ else
+ FspStatisticsInc(Statistics, Specific.FailedCreates);
+ }
+ }
+ else
/*
* HACK:
*
diff --git a/src/sys/read.c b/src/sys/read.c
index d20227a8..176967d8 100644
--- a/src/sys/read.c
+++ b/src/sys/read.c
@@ -324,6 +324,20 @@ static NTSTATUS FspFsvolReadNonCached(
FspFileNodeSetOwner(FileNode, Full, Request);
FspIopRequestContext(Request, RequestIrp) = Irp;
+ FSP_STATISTICS *Statistics = FspFsvolDeviceStatistics(FsvolDeviceObject);
+ if (PagingIo)
+ {
+ FspStatisticsInc(Statistics, Base.UserFileReads);
+ FspStatisticsAdd(Statistics, Base.UserFileReadBytes, ReadLength);
+ FspStatisticsInc(Statistics, Base.UserDiskReads);
+ }
+ else
+ {
+ FspStatisticsInc(Statistics, Specific.NonCachedReads);
+ FspStatisticsAdd(Statistics, Specific.NonCachedReadBytes, ReadLength);
+ FspStatisticsInc(Statistics, Specific.NonCachedDiskReads);
+ }
+
return FSP_STATUS_IOQ_POST;
}
diff --git a/src/sys/statistics.c b/src/sys/statistics.c
new file mode 100644
index 00000000..94194b08
--- /dev/null
+++ b/src/sys/statistics.c
@@ -0,0 +1,84 @@
+/**
+ * @file sys/statistics.c
+ *
+ * @copyright 2015-2016 Bill Zissimopoulos
+ */
+/*
+ * This file is part of WinFsp.
+ *
+ * You can redistribute it and/or modify it under the terms of the GNU
+ * General Public License version 3 as published by the Free Software
+ * Foundation.
+ *
+ * Licensees holding a valid commercial license may use this file in
+ * accordance with the commercial license agreement provided with the
+ * software.
+ */
+
+#include
+
+NTSTATUS FspStatisticsCreate(FSP_STATISTICS **PStatistics);
+VOID FspStatisticsDelete(FSP_STATISTICS *Statistics);
+NTSTATUS FspStatisticsCopy(FSP_STATISTICS *Statistics, PVOID Buffer, PULONG PLength);
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(PAGE, FspStatisticsCreate)
+#pragma alloc_text(PAGE, FspStatisticsDelete)
+#pragma alloc_text(PAGE, FspStatisticsCopy)
+#endif
+
+NTSTATUS FspStatisticsCreate(FSP_STATISTICS **PStatistics)
+{
+ *PStatistics = FspAllocNonPaged(sizeof(FSP_STATISTICS) * FspProcessorCount);
+ if (0 == *PStatistics)
+ return STATUS_INSUFFICIENT_RESOURCES;
+
+ RtlZeroMemory(*PStatistics, sizeof(FSP_STATISTICS) * FspProcessorCount);
+ for (ULONG Index = 0; FspProcessorCount > Index; Index++)
+ {
+ FSP_STATISTICS *Statistics = PStatistics[Index];
+
+ /* pretend that we are FAT when it comes to stats */
+ Statistics->Base.FileSystemType = FILESYSTEM_STATISTICS_TYPE_FAT;
+ Statistics->Base.Version = 1;
+ Statistics->Base.SizeOfCompleteStructure = sizeof(FSP_STATISTICS);
+ }
+
+ return STATUS_SUCCESS;
+}
+
+VOID FspStatisticsDelete(FSP_STATISTICS *Statistics)
+{
+ FspFree(Statistics);
+}
+
+NTSTATUS FspStatisticsCopy(FSP_STATISTICS *Statistics, PVOID Buffer, PULONG PLength)
+{
+ NTSTATUS Result;
+ ULONG StatLength;
+
+ if (0 == Buffer)
+ {
+ *PLength = 0;
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ if (sizeof(FILESYSTEM_STATISTICS) > *PLength)
+ {
+ *PLength = 0;
+ return STATUS_BUFFER_TOO_SMALL;
+ }
+
+ StatLength = sizeof(FSP_STATISTICS) * FspProcessorCount;
+ if (*PLength >= StatLength)
+ {
+ *PLength = StatLength;
+ Result = STATUS_SUCCESS;
+ }
+ else
+ Result = STATUS_BUFFER_OVERFLOW;
+
+ RtlCopyMemory(Buffer, Statistics, *PLength);
+
+ return Result;
+}
diff --git a/src/sys/write.c b/src/sys/write.c
index ef4d192e..fa79fc28 100644
--- a/src/sys/write.c
+++ b/src/sys/write.c
@@ -394,6 +394,20 @@ static NTSTATUS FspFsvolWriteNonCached(
FspFileNodeSetOwner(FileNode, Full, Request);
FspIopRequestContext(Request, RequestIrp) = Irp;
+ FSP_STATISTICS *Statistics = FspFsvolDeviceStatistics(FsvolDeviceObject);
+ if (PagingIo)
+ {
+ FspStatisticsInc(Statistics, Base.UserFileWrites);
+ FspStatisticsAdd(Statistics, Base.UserFileWriteBytes, WriteLength);
+ FspStatisticsInc(Statistics, Base.UserDiskWrites);
+ }
+ else
+ {
+ FspStatisticsInc(Statistics, Specific.NonCachedWrites);
+ FspStatisticsAdd(Statistics, Specific.NonCachedWriteBytes, WriteLength);
+ FspStatisticsInc(Statistics, Specific.NonCachedDiskWrites);
+ }
+
return FSP_STATUS_IOQ_POST;
}
diff --git a/tools/run-tests.bat b/tools/run-tests.bat
index bdc0d0fb..29f0ec70 100755
--- a/tools/run-tests.bat
+++ b/tools/run-tests.bat
@@ -379,8 +379,8 @@ rem call :__ifstest %1 /d %2 /g ChangeNotification /z /v
rem if !ERRORLEVEL! neq 0 set IfsTestMemfsExit=1
call :__ifstest %1 /d %2 /g ReadWrite /z /v
if !ERRORLEVEL! neq 0 set IfsTestMemfsExit=1
-rem call :__ifstest %1 /d %2 /g SectionsCaching /z /v
-rem if !ERRORLEVEL! neq 0 set IfsTestMemfsExit=1
+call :__ifstest %1 /d %2 /g SectionsCaching /z /v
+if !ERRORLEVEL! neq 0 set IfsTestMemfsExit=1
rem call :__ifstest %1 /d %2 /g ReparsePoints /z /v
rem if !ERRORLEVEL! neq 0 set IfsTestMemfsExit=1
rem call :__ifstest %1 /d %2 /g StreamEnhancements /z /v