From e54cfbd253f02ea6e5af227c200486e96fa0d165 Mon Sep 17 00:00:00 2001 From: Pierre Schweitzer Date: Sat, 23 Sep 2017 20:15:16 +0000 Subject: [PATCH] [FSUTIL] Implement fsutil fsinfo statistics Note that compared to w2k3 one, ours really supports FAT volumes (even though our FAT doesn't support it!). svn path=/trunk/; revision=75934 --- .../applications/cmdutils/fsutil/fsinfo.c | 412 ++++++++++++++++++ 1 file changed, 412 insertions(+) diff --git a/reactos/base/applications/cmdutils/fsutil/fsinfo.c b/reactos/base/applications/cmdutils/fsutil/fsinfo.c index bf2aeaa1bb5..eb5e184c0fe 100644 --- a/reactos/base/applications/cmdutils/fsutil/fsinfo.c +++ b/reactos/base/applications/cmdutils/fsutil/fsinfo.c @@ -12,12 +12,14 @@ static HandlerProc DrivesMain; static HandlerProc DriveTypeMain; static HandlerProc VolumeInfoMain; +static HandlerProc StatisticsMain; static HandlerItem HandlersList[] = { /* Proc, name, help */ { DrivesMain, _T("drives"), _T("Enumerates the drives") }, { DriveTypeMain, _T("drivetype"), _T("Provides the type of a drive") }, { VolumeInfoMain, _T("volumeinfo"), _T("Provides informations about a volume") }, + { StatisticsMain, _T("statistics"), _T("Displays volume statistics") }, }; static int @@ -156,6 +158,416 @@ VolumeInfoMain(int argc, const TCHAR *argv[]) return 0; } +#define DUMP_VALUE(stats, value) fprintf(stdout, "%s: %lu\n", #value, stats->value) + +static void +DumpBase(PFILESYSTEM_STATISTICS Base, TCHAR * Name) +{ + /* Print FS name */ + _ftprintf(stdout, _T("File system type: %s\n\n"), Name); + + /* And then, dump any base stat */ + DUMP_VALUE(Base, UserFileReads); + DUMP_VALUE(Base, UserFileReadBytes); + DUMP_VALUE(Base, UserDiskReads); + DUMP_VALUE(Base, UserFileWrites); + DUMP_VALUE(Base, UserFileWriteBytes); + DUMP_VALUE(Base, UserDiskWrites); + DUMP_VALUE(Base, MetaDataReads); + DUMP_VALUE(Base, MetaDataReadBytes); + DUMP_VALUE(Base, MetaDataDiskReads); + DUMP_VALUE(Base, MetaDataWrites); + DUMP_VALUE(Base, MetaDataWriteBytes); + DUMP_VALUE(Base, MetaDataDiskWrites); + + _ftprintf(stdout, _T("\n")); +} + +static void +DumpExFat(PVOID Statistics, PVOID Specific) +{ + PEXFAT_STATISTICS ExFat; + PFILESYSTEM_STATISTICS Base; + + Base = Statistics; + ExFat = Specific; + + /* First, display the generic stats */ + DumpBase(Base, _T("EXFAT")); + + /* Then, display the EXFAT specific ones */ + DUMP_VALUE(ExFat, CreateHits); + DUMP_VALUE(ExFat, SuccessfulCreates); + DUMP_VALUE(ExFat, FailedCreates); + DUMP_VALUE(ExFat, NonCachedReads); + DUMP_VALUE(ExFat, NonCachedReadBytes); + DUMP_VALUE(ExFat, NonCachedWrites); + DUMP_VALUE(ExFat, NonCachedWriteBytes); + DUMP_VALUE(ExFat, NonCachedDiskReads); + DUMP_VALUE(ExFat, NonCachedDiskWrites); +} + +static void +DumpFat(PVOID Statistics, PVOID Specific) +{ + PFAT_STATISTICS Fat; + PFILESYSTEM_STATISTICS Base; + + Base = Statistics; + Fat = Specific; + + /* First, display the generic stats */ + DumpBase(Base, _T("FAT")); + + /* Then, display the FAT specific ones */ + DUMP_VALUE(Fat, CreateHits); + DUMP_VALUE(Fat, SuccessfulCreates); + DUMP_VALUE(Fat, FailedCreates); + DUMP_VALUE(Fat, NonCachedReads); + DUMP_VALUE(Fat, NonCachedReadBytes); + DUMP_VALUE(Fat, NonCachedWrites); + DUMP_VALUE(Fat, NonCachedWriteBytes); + DUMP_VALUE(Fat, NonCachedDiskReads); + DUMP_VALUE(Fat, NonCachedDiskWrites); +} + +static void +DumpNtfs(PVOID Statistics, PVOID Specific) +{ + PNTFS_STATISTICS Ntfs; + PFILESYSTEM_STATISTICS Base; + + Base = Statistics; + Ntfs = Specific; + + /* First, display the generic stats */ + DumpBase(Base, _T("NTFS")); + + /* Then, display the NTFS specific ones */ + DUMP_VALUE(Ntfs, MftReads); + DUMP_VALUE(Ntfs, MftReadBytes); + DUMP_VALUE(Ntfs, MftWrites); + DUMP_VALUE(Ntfs, MftWriteBytes); + DUMP_VALUE(Ntfs, Mft2Writes); + DUMP_VALUE(Ntfs, Mft2WriteBytes); + DUMP_VALUE(Ntfs, RootIndexReads); + DUMP_VALUE(Ntfs, RootIndexReadBytes); + DUMP_VALUE(Ntfs, RootIndexWrites); + DUMP_VALUE(Ntfs, RootIndexWriteBytes); + DUMP_VALUE(Ntfs, BitmapReads); + DUMP_VALUE(Ntfs, BitmapReadBytes); + DUMP_VALUE(Ntfs, BitmapWrites); + DUMP_VALUE(Ntfs, BitmapWriteBytes); + DUMP_VALUE(Ntfs, MftBitmapReads); + DUMP_VALUE(Ntfs, MftBitmapReadBytes); + DUMP_VALUE(Ntfs, MftBitmapWrites); + DUMP_VALUE(Ntfs, MftBitmapWriteBytes); + DUMP_VALUE(Ntfs, UserIndexReads); + DUMP_VALUE(Ntfs, UserIndexReadBytes); + DUMP_VALUE(Ntfs, UserIndexWrites); + DUMP_VALUE(Ntfs, UserIndexWriteBytes); + DUMP_VALUE(Ntfs, LogFileReads); + DUMP_VALUE(Ntfs, LogFileReadBytes); + DUMP_VALUE(Ntfs, LogFileWrites); + DUMP_VALUE(Ntfs, LogFileWriteBytes); +} + +#define GET_NEXT(stats, length, iter, type) (type)((ULONG_PTR)stats + (length * iter)) +#define SUM_VALUE(stats, new, value) stats->value += new->value + +inline int +ValidateSizes(ULONG Length, DWORD ProcCount, DWORD BytesRead, DWORD SpecificSize) +{ + /* Check whether we could read our base length for every processor */ + if (Length * ProcCount > BytesRead) + { + _ftprintf(stderr, _T("Only performed a partial read: %lu (expected: %lu)\n"), BytesRead, Length * ProcCount); + return 1; + } + + /* Check whether this covers a specific entry and a generic entry for every processor */ + if ((sizeof(FILESYSTEM_STATISTICS) + SpecificSize) * ProcCount > BytesRead) + { + _ftprintf(stderr, _T("Only performed a partial read: %lu (expected: %lu)\n"), BytesRead, (sizeof(FILESYSTEM_STATISTICS) + SpecificSize)); + return 1; + } + + return 0; +} + +inline void +SumBase(PFILESYSTEM_STATISTICS Base, PFILESYSTEM_STATISTICS NextBase) +{ + /* Sum any entry in the generic structures */ + SUM_VALUE(Base, NextBase, UserFileReads); + SUM_VALUE(Base, NextBase, UserFileReadBytes); + SUM_VALUE(Base, NextBase, UserDiskReads); + SUM_VALUE(Base, NextBase, UserFileWrites); + SUM_VALUE(Base, NextBase, UserFileWriteBytes); + SUM_VALUE(Base, NextBase, UserDiskWrites); + SUM_VALUE(Base, NextBase, MetaDataReads); + SUM_VALUE(Base, NextBase, MetaDataReadBytes); + SUM_VALUE(Base, NextBase, MetaDataDiskReads); + SUM_VALUE(Base, NextBase, MetaDataWrites); + SUM_VALUE(Base, NextBase, MetaDataWriteBytes); + SUM_VALUE(Base, NextBase, MetaDataDiskWrites); +} + +static int +SumExFat(PVOID Statistics, PVOID Specific, ULONG Length, DWORD ProcCount, DWORD BytesRead) +{ + DWORD i; + PEXFAT_STATISTICS ExFat; + PFILESYSTEM_STATISTICS Base; + + /* First, validate we won't read beyond allocation */ + if (ValidateSizes(Length, ProcCount, BytesRead, sizeof(EXFAT_STATISTICS))) + { + return 1; + } + + Base = Statistics; + ExFat = Specific; + + /* And for every processor, sum every relevant value in first entry */ + for (i = 1; i < ProcCount; ++i) + { + PEXFAT_STATISTICS NextExFat; + PFILESYSTEM_STATISTICS NextBase; + + NextBase = GET_NEXT(Base, Length, i, PFILESYSTEM_STATISTICS); + NextExFat = GET_NEXT(ExFat, Length, i, PEXFAT_STATISTICS); + + /* Generic first */ + SumBase(Base, NextBase); + /* Specific then */ + SUM_VALUE(ExFat, NextExFat, CreateHits); + SUM_VALUE(ExFat, NextExFat, SuccessfulCreates); + SUM_VALUE(ExFat, NextExFat, FailedCreates); + SUM_VALUE(ExFat, NextExFat, NonCachedReads); + SUM_VALUE(ExFat, NextExFat, NonCachedReadBytes); + SUM_VALUE(ExFat, NextExFat, NonCachedWrites); + SUM_VALUE(ExFat, NextExFat, NonCachedWriteBytes); + SUM_VALUE(ExFat, NextExFat, NonCachedDiskReads); + SUM_VALUE(ExFat, NextExFat, NonCachedDiskWrites); + } + + return 0; +} + +static int +SumFat(PVOID Statistics, PVOID Specific, ULONG Length, DWORD ProcCount, DWORD BytesRead) +{ + DWORD i; + PFAT_STATISTICS Fat; + PFILESYSTEM_STATISTICS Base; + + /* First, validate we won't read beyond allocation */ + if (ValidateSizes(Length, ProcCount, BytesRead, sizeof(FAT_STATISTICS))) + { + return 1; + } + + Base = Statistics; + Fat = Specific; + + /* And for every processor, sum every relevant value in first entry */ + for (i = 1; i < ProcCount; ++i) + { + PFAT_STATISTICS NextFat; + PFILESYSTEM_STATISTICS NextBase; + + NextBase = GET_NEXT(Base, Length, i, PFILESYSTEM_STATISTICS); + NextFat = GET_NEXT(Fat, Length, i, PFAT_STATISTICS); + + /* Generic first */ + SumBase(Base, NextBase); + /* Specific then */ + SUM_VALUE(Fat, NextFat, CreateHits); + SUM_VALUE(Fat, NextFat, SuccessfulCreates); + SUM_VALUE(Fat, NextFat, FailedCreates); + SUM_VALUE(Fat, NextFat, NonCachedReads); + SUM_VALUE(Fat, NextFat, NonCachedReadBytes); + SUM_VALUE(Fat, NextFat, NonCachedWrites); + SUM_VALUE(Fat, NextFat, NonCachedWriteBytes); + SUM_VALUE(Fat, NextFat, NonCachedDiskReads); + SUM_VALUE(Fat, NextFat, NonCachedDiskWrites); + } + + return 0; +} + +static int +SumNtfs(PVOID Statistics, PVOID Specific, ULONG Length, DWORD ProcCount, DWORD BytesRead) +{ + DWORD i; + PNTFS_STATISTICS Ntfs; + PFILESYSTEM_STATISTICS Base; + + /* First, validate we won't read beyond allocation */ + if (ValidateSizes(Length, ProcCount, BytesRead, sizeof(NTFS_STATISTICS))) + { + return 1; + } + + Base = Statistics; + Ntfs = Specific; + + /* And for every processor, sum every relevant value in first entry */ + for (i = 1; i < ProcCount; ++i) + { + PNTFS_STATISTICS NextNtfs; + PFILESYSTEM_STATISTICS NextBase; + + NextBase = GET_NEXT(Base, Length, i, PFILESYSTEM_STATISTICS); + NextNtfs = GET_NEXT(Ntfs, Length, i, PNTFS_STATISTICS); + + /* Generic first */ + SumBase(Base, NextBase); + /* Specific then */ + SUM_VALUE(Ntfs, NextNtfs, MftReads); + SUM_VALUE(Ntfs, NextNtfs, MftReadBytes); + SUM_VALUE(Ntfs, NextNtfs, MftWrites); + SUM_VALUE(Ntfs, NextNtfs, MftWriteBytes); + SUM_VALUE(Ntfs, NextNtfs, Mft2Writes); + SUM_VALUE(Ntfs, NextNtfs, Mft2WriteBytes); + SUM_VALUE(Ntfs, NextNtfs, RootIndexReads); + SUM_VALUE(Ntfs, NextNtfs, RootIndexReadBytes); + SUM_VALUE(Ntfs, NextNtfs, RootIndexWrites); + SUM_VALUE(Ntfs, NextNtfs, RootIndexWriteBytes); + SUM_VALUE(Ntfs, NextNtfs, BitmapReads); + SUM_VALUE(Ntfs, NextNtfs, BitmapReadBytes); + SUM_VALUE(Ntfs, NextNtfs, BitmapWrites); + SUM_VALUE(Ntfs, NextNtfs, BitmapWriteBytes); + SUM_VALUE(Ntfs, NextNtfs, MftBitmapReads); + SUM_VALUE(Ntfs, NextNtfs, MftBitmapReadBytes); + SUM_VALUE(Ntfs, NextNtfs, MftBitmapWrites); + SUM_VALUE(Ntfs, NextNtfs, MftBitmapWriteBytes); + SUM_VALUE(Ntfs, NextNtfs, UserIndexReads); + SUM_VALUE(Ntfs, NextNtfs, UserIndexReadBytes); + SUM_VALUE(Ntfs, NextNtfs, UserIndexWrites); + SUM_VALUE(Ntfs, NextNtfs, UserIndexWriteBytes); + SUM_VALUE(Ntfs, NextNtfs, LogFileReads); + SUM_VALUE(Ntfs, NextNtfs, LogFileReadBytes); + SUM_VALUE(Ntfs, NextNtfs, LogFileWrites); + SUM_VALUE(Ntfs, NextNtfs, LogFileWriteBytes); + } + + return 0; +} + +static int +StatisticsMain(int argc, const TCHAR *argv[]) +{ + HANDLE Volume; + SYSTEM_INFO SystemInfo; + FILESYSTEM_STATISTICS Buffer; + PFILESYSTEM_STATISTICS Statistics; + DWORD BytesRead, Length, ProcCount; + /* +1 because 0 isn't assigned to a filesystem */ + void (*DumpFct[FILESYSTEM_STATISTICS_TYPE_EXFAT + 1])(PVOID, PVOID) = { NULL, DumpNtfs, DumpFat, DumpExFat }; + int (*SumFct[FILESYSTEM_STATISTICS_TYPE_EXFAT + 1])(PVOID, PVOID, ULONG, DWORD, DWORD) = { NULL, SumNtfs, SumFat, SumExFat }; + + /* We need a volume (letter or GUID) */ + if (argc < 2) + { + _ftprintf(stderr, _T("Usage: fsutil fsinfo statistics \n")); + _ftprintf(stderr, _T("\tFor example: fsutil fsinfo statistics c:\n")); + return 1; + } + + /* Get a handle for the volume */ + Volume = OpenVolume(argv[1], FALSE); + if (Volume == INVALID_HANDLE_VALUE) + { + return 1; + } + + /* And query the statistics status - this call is expected to fail */ + Statistics = &Buffer; + Length = sizeof(Buffer); + /* Assume we have a single proc for now */ + ProcCount = 1; + if (DeviceIoControl(Volume, FSCTL_FILESYSTEM_GET_STATISTICS, NULL, 0, Statistics, + Length, &BytesRead, NULL) == FALSE) + { + DWORD Error; + + /* Check we failed because we provided a too small buffer */ + Error = GetLastError(); + if (Error == ERROR_MORE_DATA) + { + /* Get proc count */ + GetSystemInfo(&SystemInfo); + ProcCount = SystemInfo.dwNumberOfProcessors; + /* Get the maximum size to allocate: it's the total size (generic + specific) for every proc */ + Length = Statistics->SizeOfCompleteStructure * ProcCount; + + Statistics = LocalAlloc(LPTR, Length); + if (Statistics == NULL) + { + _ftprintf(stderr, _T("Failed to allocate memory!\n")); + CloseHandle(Volume); + return 1; + } + + /* Reissue the FSCTL, it's supposed to succeed now! */ + if (DeviceIoControl(Volume, FSCTL_FILESYSTEM_GET_STATISTICS, NULL, 0, Statistics, + Length, &BytesRead, NULL) == FALSE) + { + PrintErrorMessage(GetLastError()); + LocalFree(Statistics); + CloseHandle(Volume); + return 1; + } + } + else + { + PrintErrorMessage(GetLastError()); + CloseHandle(Volume); + return 1; + } + } + + /* No need to deal with the volume any longer */ + CloseHandle(Volume); + + /* We only support FAT, EXFAT and NTFS for now */ + if (Statistics->FileSystemType > FILESYSTEM_STATISTICS_TYPE_EXFAT || Statistics->FileSystemType < FILESYSTEM_STATISTICS_TYPE_NTFS) + { + _ftprintf(stderr, _T("Unrecognized file system type: %d\n"), Statistics->FileSystemType); + if (Statistics != &Buffer) + { + LocalFree(Statistics); + } + + return 1; + } + + /* Sum all the statistics (both generic and specific) from all the processors in the first entry */ + if (SumFct[Statistics->FileSystemType](Statistics, (PVOID)((ULONG_PTR)Statistics + sizeof(FILESYSTEM_STATISTICS)), + Statistics->SizeOfCompleteStructure, ProcCount, BytesRead)) + { + if (Statistics != &Buffer) + { + LocalFree(Statistics); + } + + return 1; + } + + /* And finally, display the statistics (both generic and specific) */ + DumpFct[Statistics->FileSystemType](Statistics, (PVOID)((ULONG_PTR)Statistics + sizeof(FILESYSTEM_STATISTICS))); + + /* If we allocated memory, release it */ + if (Statistics != &Buffer) + { + LocalFree(Statistics); + } + + return 0; +} + static void PrintUsage(const TCHAR * Command) {