1 /*
2  * COPYRIGHT:       See COPYING in the top level directory
3  * PROJECT:         ReactOS FS utility tool
4  * FILE:            base/applications/cmdutils/fsinfo.c
5  * PURPOSE:         FSutil file systems information
6  * PROGRAMMERS:     Pierre Schweitzer <pierre@reactos.org>
7  */
8 
9 #include "fsutil.h"
10 
11 /* Add handlers here for subcommands */
12 static HandlerProc DrivesMain;
13 static HandlerProc DriveTypeMain;
14 static HandlerProc VolumeInfoMain;
15 static HandlerProc NtfsInfoMain;
16 static HandlerProc StatisticsMain;
17 static HandlerItem HandlersList[] =
18 {
19     /* Proc, name, help */
20     { DrivesMain, _T("drives"), _T("Enumerates the drives") },
21     { DriveTypeMain, _T("drivetype"), _T("Provides the type of a drive") },
22     { VolumeInfoMain, _T("volumeinfo"), _T("Provides informations about a volume") },
23     { NtfsInfoMain, _T("ntfsinfo"), _T("Displays informations about a NTFS volume") },
24     { StatisticsMain, _T("statistics"), _T("Displays volume statistics") },
25 };
26 
27 static int
28 DrivesMain(int argc, const TCHAR *argv[])
29 {
30     UINT i;
31     DWORD Drives;
32 
33     /* Get the drives bitmap */
34     Drives = GetLogicalDrives();
35     if (Drives == 0)
36     {
37         PrintErrorMessage(GetLastError());
38         return 1;
39     }
40 
41     /* And output any found drive */
42     _ftprintf(stdout, _T("Drives:"));
43     for (i = 0; i < 26; i++)
44     {
45         if (Drives & (1 << i))
46         {
47             _ftprintf(stdout, _T(" %c:\\"), 'A' + i);
48         }
49     }
50     _ftprintf(stdout, _T("\n"));
51 
52     return 0;
53 }
54 
55 static int
56 DriveTypeMain(int argc, const TCHAR *argv[])
57 {
58     UINT Type;
59 
60     /* We need a volume (letter) */
61     if (argc < 2)
62     {
63         _ftprintf(stderr, _T("Usage: fsutil fsinfo drivetype <volume>\n"));
64         _ftprintf(stderr, _T("\tFor example: fsutil fsinfo drivetype c:\n"));
65         return 1;
66     }
67 
68     /* Get its drive type and make it readable */
69     Type = GetDriveType(argv[1]);
70     switch (Type)
71     {
72         case DRIVE_UNKNOWN:
73             _ftprintf(stdout, _T("%s - unknown drive type\n"), argv[1]);
74             break;
75 
76         case DRIVE_NO_ROOT_DIR:
77             _ftprintf(stdout, _T("%s - not a root directory\n"), argv[1]);
78             break;
79 
80         case DRIVE_REMOVABLE:
81             _ftprintf(stdout, _T("%s - removable drive\n"), argv[1]);
82             break;
83 
84         case DRIVE_FIXED:
85             _ftprintf(stdout, _T("%s - fixed drive\n"), argv[1]);
86             break;
87 
88         case DRIVE_REMOTE:
89             _ftprintf(stdout, _T("%s - remote or network drive\n"), argv[1]);
90             break;
91 
92         case DRIVE_CDROM:
93             _ftprintf(stdout, _T("%s - CD-ROM drive\n"), argv[1]);
94             break;
95 
96         case DRIVE_RAMDISK:
97             _ftprintf(stdout, _T("%s - RAM disk drive\n"), argv[1]);
98             break;
99     }
100 
101     return 0;
102 }
103 
104 static int
105 VolumeInfoMain(int argc, const TCHAR *argv[])
106 {
107     DWORD Serial, MaxComponentLen, Flags;
108     TCHAR VolumeName[MAX_PATH + 1], FileSystem[MAX_PATH + 1];
109 
110 #define HANDLE_FLAG(Flags, Flag, Desc) \
111     if (Flags & Flag)                  \
112         _ftprintf(stdout, Desc)
113 
114     /* We need a volume (path) */
115     if (argc < 2)
116     {
117         _ftprintf(stderr, _T("Usage: fsutil fsinfo volumeinfo <volume_path>\n"));
118         _ftprintf(stderr, _T("\tFor example: fsutil fsinfo volumeinfo c:\\\n"));
119         return 1;
120     }
121 
122     /* Gather information */
123     if (!GetVolumeInformation(argv[1], VolumeName,  MAX_PATH + 1, &Serial,
124                               &MaxComponentLen, &Flags, FileSystem, MAX_PATH + 1))
125     {
126         PrintErrorMessage(GetLastError());
127         return 1;
128     }
129 
130     /* Display general information */
131     _ftprintf(stdout, _T("Volume name: %s\n"), VolumeName);
132     _ftprintf(stdout, _T("Volume serial number: 0x%x\n"), Serial);
133     _ftprintf(stdout, _T("Maximum component length: %u\n"), MaxComponentLen);
134     _ftprintf(stdout, _T("File system name: %s\n"), FileSystem);
135 
136     /* Display specific flags */
137     HANDLE_FLAG(Flags, FILE_CASE_SENSITIVE_SEARCH, _T("Supports case-sensitive file names\n"));
138     HANDLE_FLAG(Flags, FILE_CASE_PRESERVED_NAMES, _T("Supports preserved case of file names\n"));
139     HANDLE_FLAG(Flags, FILE_UNICODE_ON_DISK, _T("Supports unicode file names\n"));
140     HANDLE_FLAG(Flags, FILE_PERSISTENT_ACLS, _T("Preserves and applies ACLs\n"));
141     HANDLE_FLAG(Flags, FILE_FILE_COMPRESSION, _T("Supports compression per file\n"));
142     HANDLE_FLAG(Flags, FILE_VOLUME_QUOTAS, _T("Supports disk quotas\n"));
143     HANDLE_FLAG(Flags, FILE_SUPPORTS_SPARSE_FILES, _T("Supports sparse files\n"));
144     HANDLE_FLAG(Flags, FILE_SUPPORTS_REPARSE_POINTS, _T("Supports reparse points\n"));
145     HANDLE_FLAG(Flags, FILE_VOLUME_IS_COMPRESSED, _T("Is a compressed volume\n"));
146     HANDLE_FLAG(Flags, FILE_SUPPORTS_OBJECT_IDS, _T("Supports object identifiers\n"));
147     HANDLE_FLAG(Flags, FILE_SUPPORTS_ENCRYPTION, _T("Supports the Encrypted File System (EFS)\n"));
148     HANDLE_FLAG(Flags, FILE_NAMED_STREAMS, _T("Supports named streams\n"));
149     HANDLE_FLAG(Flags, FILE_READ_ONLY_VOLUME, _T("Is a read-only volume\n"));
150     HANDLE_FLAG(Flags, FILE_SEQUENTIAL_WRITE_ONCE, _T("Supports a single sequential write\n"));
151     HANDLE_FLAG(Flags, FILE_SUPPORTS_TRANSACTIONS, _T("Supports transactions\n"));
152     HANDLE_FLAG(Flags, FILE_SUPPORTS_HARD_LINKS, _T("Supports hard links\n"));
153     HANDLE_FLAG(Flags, FILE_SUPPORTS_EXTENDED_ATTRIBUTES, _T("Supports extended attributes\n"));
154     HANDLE_FLAG(Flags, FILE_SUPPORTS_OPEN_BY_FILE_ID, _T("Supports opening files per file identifier\n"));
155     HANDLE_FLAG(Flags, FILE_SUPPORTS_USN_JOURNAL, _T("Supports Update Sequence Number (USN) journals\n"));
156     HANDLE_FLAG(Flags, FILE_DAX_VOLUME, _T("Is a direct access volume\n"));
157 
158 #undef HANDLE_FLAGS
159 
160     return 0;
161 }
162 
163 static int
164 NtfsInfoMain(int argc, const TCHAR *argv[])
165 {
166     HANDLE Volume;
167     DWORD BytesRead;
168     struct
169     {
170         NTFS_VOLUME_DATA_BUFFER;
171         NTFS_EXTENDED_VOLUME_DATA;
172     } Data;
173 
174     /* We need a volume (letter or GUID) */
175     if (argc < 2)
176     {
177         _ftprintf(stderr, _T("Usage: fsutil fsinfo ntfsinfo <volume>\n"));
178         _ftprintf(stderr, _T("\tFor example: fsutil fsinfo ntfsinfo c:\n"));
179         return 1;
180     }
181 
182     /* Get a handle for the volume */
183     Volume = OpenVolume(argv[1], FALSE, TRUE);
184     if (Volume == INVALID_HANDLE_VALUE)
185     {
186         return 1;
187     }
188 
189     /* And query the NTFS data */
190     if (DeviceIoControl(Volume, FSCTL_GET_NTFS_VOLUME_DATA, NULL, 0, &Data,
191                         sizeof(Data), &BytesRead, NULL) == FALSE)
192     {
193         PrintErrorMessage(GetLastError());
194         CloseHandle(Volume);
195         return 1;
196     }
197 
198     /* We no longer need the volume */
199     CloseHandle(Volume);
200 
201     /* Dump data */
202     _ftprintf(stdout, _T("NTFS volume serial number:\t\t0x%0.16I64x\n"), Data.VolumeSerialNumber.QuadPart);
203     /* Only print version if extended structure was returned */
204     if (BytesRead > sizeof(NTFS_VOLUME_DATA_BUFFER))
205     {
206         _ftprintf(stdout, _T("Version:\t\t\t\t%u.%u\n"), Data.MajorVersion, Data.MinorVersion);
207     }
208     _ftprintf(stdout, _T("Number of sectors:\t\t\t0x%0.16I64x\n"), Data.NumberSectors.QuadPart);
209     _ftprintf(stdout, _T("Total number of clusters:\t\t0x%0.16I64x\n"), Data.TotalClusters.QuadPart);
210     _ftprintf(stdout, _T("Free clusters:\t\t\t\t0x%0.16I64x\n"), Data.FreeClusters.QuadPart);
211     _ftprintf(stdout, _T("Total number of reserved clusters:\t0x%0.16I64x\n"), Data.TotalReserved.QuadPart);
212     _ftprintf(stdout, _T("Bytes per sector:\t\t\t%d\n"), Data.BytesPerSector);
213     _ftprintf(stdout, _T("Bytes per cluster:\t\t\t%d\n"), Data.BytesPerCluster);
214     _ftprintf(stdout, _T("Bytes per file record segment:\t\t%d\n"), Data.BytesPerFileRecordSegment);
215     _ftprintf(stdout, _T("Clusters per file record segment:\t%d\n"), Data.ClustersPerFileRecordSegment);
216     _ftprintf(stdout, _T("MFT valid data length:\t\t\t0x%0.16I64x\n"), Data.MftValidDataLength.QuadPart);
217     _ftprintf(stdout, _T("MFT start LCN:\t\t\t\t0x%0.16I64x\n"), Data.MftStartLcn.QuadPart);
218     _ftprintf(stdout, _T("MFT2 start LCN:\t\t\t\t0x%0.16I64x\n"), Data.Mft2StartLcn.QuadPart);
219     _ftprintf(stdout, _T("MFT zone start:\t\t\t\t0x%0.16I64x\n"), Data.MftZoneStart.QuadPart);
220     _ftprintf(stdout, _T("MFT zone end:\t\t\t\t0x%0.16I64x\n"), Data.MftZoneEnd.QuadPart);
221 
222     return 0;
223 }
224 
225 #define DUMP_VALUE(stats, value) fprintf(stdout, "%s: %lu\n", #value, stats->value)
226 
227 static void
228 DumpBase(PFILESYSTEM_STATISTICS Base, TCHAR * Name)
229 {
230     /* Print FS name */
231     _ftprintf(stdout, _T("File system type: %s\n\n"), Name);
232 
233     /* And then, dump any base stat */
234     DUMP_VALUE(Base, UserFileReads);
235     DUMP_VALUE(Base, UserFileReadBytes);
236     DUMP_VALUE(Base, UserDiskReads);
237     DUMP_VALUE(Base, UserFileWrites);
238     DUMP_VALUE(Base, UserFileWriteBytes);
239     DUMP_VALUE(Base, UserDiskWrites);
240     DUMP_VALUE(Base, MetaDataReads);
241     DUMP_VALUE(Base, MetaDataReadBytes);
242     DUMP_VALUE(Base, MetaDataDiskReads);
243     DUMP_VALUE(Base, MetaDataWrites);
244     DUMP_VALUE(Base, MetaDataWriteBytes);
245     DUMP_VALUE(Base, MetaDataDiskWrites);
246 
247     _ftprintf(stdout, _T("\n"));
248 }
249 
250 static void
251 DumpExFat(PVOID Statistics, PVOID Specific)
252 {
253     PEXFAT_STATISTICS ExFat;
254     PFILESYSTEM_STATISTICS Base;
255 
256     Base = Statistics;
257     ExFat = Specific;
258 
259     /* First, display the generic stats */
260     DumpBase(Base, _T("EXFAT"));
261 
262     /* Then, display the EXFAT specific ones */
263     DUMP_VALUE(ExFat, CreateHits);
264     DUMP_VALUE(ExFat, SuccessfulCreates);
265     DUMP_VALUE(ExFat, FailedCreates);
266     DUMP_VALUE(ExFat, NonCachedReads);
267     DUMP_VALUE(ExFat, NonCachedReadBytes);
268     DUMP_VALUE(ExFat, NonCachedWrites);
269     DUMP_VALUE(ExFat, NonCachedWriteBytes);
270     DUMP_VALUE(ExFat, NonCachedDiskReads);
271     DUMP_VALUE(ExFat, NonCachedDiskWrites);
272 }
273 
274 static void
275 DumpFat(PVOID Statistics, PVOID Specific)
276 {
277     PFAT_STATISTICS Fat;
278     PFILESYSTEM_STATISTICS Base;
279 
280     Base = Statistics;
281     Fat = Specific;
282 
283     /* First, display the generic stats */
284     DumpBase(Base, _T("FAT"));
285 
286     /* Then, display the FAT specific ones */
287     DUMP_VALUE(Fat, CreateHits);
288     DUMP_VALUE(Fat, SuccessfulCreates);
289     DUMP_VALUE(Fat, FailedCreates);
290     DUMP_VALUE(Fat, NonCachedReads);
291     DUMP_VALUE(Fat, NonCachedReadBytes);
292     DUMP_VALUE(Fat, NonCachedWrites);
293     DUMP_VALUE(Fat, NonCachedWriteBytes);
294     DUMP_VALUE(Fat, NonCachedDiskReads);
295     DUMP_VALUE(Fat, NonCachedDiskWrites);
296 }
297 
298 static void
299 DumpNtfs(PVOID Statistics, PVOID Specific)
300 {
301     PNTFS_STATISTICS Ntfs;
302     PFILESYSTEM_STATISTICS Base;
303 
304     Base = Statistics;
305     Ntfs = Specific;
306 
307     /* First, display the generic stats */
308     DumpBase(Base, _T("NTFS"));
309 
310     /* Then, display the NTFS specific ones */
311     DUMP_VALUE(Ntfs, MftReads);
312     DUMP_VALUE(Ntfs, MftReadBytes);
313     DUMP_VALUE(Ntfs, MftWrites);
314     DUMP_VALUE(Ntfs, MftWriteBytes);
315     DUMP_VALUE(Ntfs, Mft2Writes);
316     DUMP_VALUE(Ntfs, Mft2WriteBytes);
317     DUMP_VALUE(Ntfs, RootIndexReads);
318     DUMP_VALUE(Ntfs, RootIndexReadBytes);
319     DUMP_VALUE(Ntfs, RootIndexWrites);
320     DUMP_VALUE(Ntfs, RootIndexWriteBytes);
321     DUMP_VALUE(Ntfs, BitmapReads);
322     DUMP_VALUE(Ntfs, BitmapReadBytes);
323     DUMP_VALUE(Ntfs, BitmapWrites);
324     DUMP_VALUE(Ntfs, BitmapWriteBytes);
325     DUMP_VALUE(Ntfs, MftBitmapReads);
326     DUMP_VALUE(Ntfs, MftBitmapReadBytes);
327     DUMP_VALUE(Ntfs, MftBitmapWrites);
328     DUMP_VALUE(Ntfs, MftBitmapWriteBytes);
329     DUMP_VALUE(Ntfs, UserIndexReads);
330     DUMP_VALUE(Ntfs, UserIndexReadBytes);
331     DUMP_VALUE(Ntfs, UserIndexWrites);
332     DUMP_VALUE(Ntfs, UserIndexWriteBytes);
333     DUMP_VALUE(Ntfs, LogFileReads);
334     DUMP_VALUE(Ntfs, LogFileReadBytes);
335     DUMP_VALUE(Ntfs, LogFileWrites);
336     DUMP_VALUE(Ntfs, LogFileWriteBytes);
337 }
338 
339 #define GET_NEXT(stats, length, iter, type) (type)((ULONG_PTR)stats + (length * iter))
340 #define SUM_VALUE(stats, new, value) stats->value += new->value
341 
342 static inline int
343 ValidateSizes(ULONG Length, DWORD ProcCount, DWORD BytesRead, DWORD SpecificSize)
344 {
345     /* Check whether we could read our base length for every processor */
346     if (Length * ProcCount > BytesRead)
347     {
348         _ftprintf(stderr, _T("Only performed a partial read: %lu (expected: %lu)\n"), BytesRead, Length * ProcCount);
349         return 1;
350     }
351 
352     /* Check whether this covers a specific entry and a generic entry for every processor */
353     if ((sizeof(FILESYSTEM_STATISTICS) + SpecificSize) * ProcCount > BytesRead)
354     {
355         _ftprintf(stderr, _T("Only performed a partial read: %lu (expected: %Iu)\n"), BytesRead, (sizeof(FILESYSTEM_STATISTICS) + SpecificSize));
356         return 1;
357     }
358 
359     return 0;
360 }
361 
362 static inline void
363 SumBase(PFILESYSTEM_STATISTICS Base, PFILESYSTEM_STATISTICS NextBase)
364 {
365     /* Sum any entry in the generic structures */
366     SUM_VALUE(Base, NextBase, UserFileReads);
367     SUM_VALUE(Base, NextBase, UserFileReadBytes);
368     SUM_VALUE(Base, NextBase, UserDiskReads);
369     SUM_VALUE(Base, NextBase, UserFileWrites);
370     SUM_VALUE(Base, NextBase, UserFileWriteBytes);
371     SUM_VALUE(Base, NextBase, UserDiskWrites);
372     SUM_VALUE(Base, NextBase, MetaDataReads);
373     SUM_VALUE(Base, NextBase, MetaDataReadBytes);
374     SUM_VALUE(Base, NextBase, MetaDataDiskReads);
375     SUM_VALUE(Base, NextBase, MetaDataWrites);
376     SUM_VALUE(Base, NextBase, MetaDataWriteBytes);
377     SUM_VALUE(Base, NextBase, MetaDataDiskWrites);
378 }
379 
380 static int
381 SumExFat(PVOID Statistics, PVOID Specific, ULONG Length, DWORD ProcCount, DWORD BytesRead)
382 {
383     DWORD i;
384     PEXFAT_STATISTICS ExFat;
385     PFILESYSTEM_STATISTICS Base;
386 
387     /* First, validate we won't read beyond allocation */
388     if (ValidateSizes(Length, ProcCount, BytesRead, sizeof(EXFAT_STATISTICS)))
389     {
390         return 1;
391     }
392 
393     Base = Statistics;
394     ExFat = Specific;
395 
396     /* And for every processor, sum every relevant value in first entry */
397     for (i = 1; i < ProcCount; ++i)
398     {
399         PEXFAT_STATISTICS NextExFat;
400         PFILESYSTEM_STATISTICS NextBase;
401 
402         NextBase = GET_NEXT(Base, Length, i, PFILESYSTEM_STATISTICS);
403         NextExFat = GET_NEXT(ExFat, Length, i, PEXFAT_STATISTICS);
404 
405         /* Generic first */
406         SumBase(Base, NextBase);
407         /* Specific then */
408         SUM_VALUE(ExFat, NextExFat, CreateHits);
409         SUM_VALUE(ExFat, NextExFat, SuccessfulCreates);
410         SUM_VALUE(ExFat, NextExFat, FailedCreates);
411         SUM_VALUE(ExFat, NextExFat, NonCachedReads);
412         SUM_VALUE(ExFat, NextExFat, NonCachedReadBytes);
413         SUM_VALUE(ExFat, NextExFat, NonCachedWrites);
414         SUM_VALUE(ExFat, NextExFat, NonCachedWriteBytes);
415         SUM_VALUE(ExFat, NextExFat, NonCachedDiskReads);
416         SUM_VALUE(ExFat, NextExFat, NonCachedDiskWrites);
417     }
418 
419     return 0;
420 }
421 
422 static int
423 SumFat(PVOID Statistics, PVOID Specific, ULONG Length, DWORD ProcCount, DWORD BytesRead)
424 {
425     DWORD i;
426     PFAT_STATISTICS Fat;
427     PFILESYSTEM_STATISTICS Base;
428 
429     /* First, validate we won't read beyond allocation */
430     if (ValidateSizes(Length, ProcCount, BytesRead, sizeof(FAT_STATISTICS)))
431     {
432         return 1;
433     }
434 
435     Base = Statistics;
436     Fat = Specific;
437 
438     /* And for every processor, sum every relevant value in first entry */
439     for (i = 1; i < ProcCount; ++i)
440     {
441         PFAT_STATISTICS NextFat;
442         PFILESYSTEM_STATISTICS NextBase;
443 
444         NextBase = GET_NEXT(Base, Length, i, PFILESYSTEM_STATISTICS);
445         NextFat = GET_NEXT(Fat, Length, i, PFAT_STATISTICS);
446 
447         /* Generic first */
448         SumBase(Base, NextBase);
449         /* Specific then */
450         SUM_VALUE(Fat, NextFat, CreateHits);
451         SUM_VALUE(Fat, NextFat, SuccessfulCreates);
452         SUM_VALUE(Fat, NextFat, FailedCreates);
453         SUM_VALUE(Fat, NextFat, NonCachedReads);
454         SUM_VALUE(Fat, NextFat, NonCachedReadBytes);
455         SUM_VALUE(Fat, NextFat, NonCachedWrites);
456         SUM_VALUE(Fat, NextFat, NonCachedWriteBytes);
457         SUM_VALUE(Fat, NextFat, NonCachedDiskReads);
458         SUM_VALUE(Fat, NextFat, NonCachedDiskWrites);
459     }
460 
461     return 0;
462 }
463 
464 static int
465 SumNtfs(PVOID Statistics, PVOID Specific, ULONG Length, DWORD ProcCount, DWORD BytesRead)
466 {
467     DWORD i;
468     PNTFS_STATISTICS Ntfs;
469     PFILESYSTEM_STATISTICS Base;
470 
471     /* First, validate we won't read beyond allocation */
472     if (ValidateSizes(Length, ProcCount, BytesRead, sizeof(NTFS_STATISTICS)))
473     {
474         return 1;
475     }
476 
477     Base = Statistics;
478     Ntfs = Specific;
479 
480     /* And for every processor, sum every relevant value in first entry */
481     for (i = 1; i < ProcCount; ++i)
482     {
483         PNTFS_STATISTICS NextNtfs;
484         PFILESYSTEM_STATISTICS NextBase;
485 
486         NextBase = GET_NEXT(Base, Length, i, PFILESYSTEM_STATISTICS);
487         NextNtfs = GET_NEXT(Ntfs, Length, i, PNTFS_STATISTICS);
488 
489         /* Generic first */
490         SumBase(Base, NextBase);
491         /* Specific then */
492         SUM_VALUE(Ntfs, NextNtfs, MftReads);
493         SUM_VALUE(Ntfs, NextNtfs, MftReadBytes);
494         SUM_VALUE(Ntfs, NextNtfs, MftWrites);
495         SUM_VALUE(Ntfs, NextNtfs, MftWriteBytes);
496         SUM_VALUE(Ntfs, NextNtfs, Mft2Writes);
497         SUM_VALUE(Ntfs, NextNtfs, Mft2WriteBytes);
498         SUM_VALUE(Ntfs, NextNtfs, RootIndexReads);
499         SUM_VALUE(Ntfs, NextNtfs, RootIndexReadBytes);
500         SUM_VALUE(Ntfs, NextNtfs, RootIndexWrites);
501         SUM_VALUE(Ntfs, NextNtfs, RootIndexWriteBytes);
502         SUM_VALUE(Ntfs, NextNtfs, BitmapReads);
503         SUM_VALUE(Ntfs, NextNtfs, BitmapReadBytes);
504         SUM_VALUE(Ntfs, NextNtfs, BitmapWrites);
505         SUM_VALUE(Ntfs, NextNtfs, BitmapWriteBytes);
506         SUM_VALUE(Ntfs, NextNtfs, MftBitmapReads);
507         SUM_VALUE(Ntfs, NextNtfs, MftBitmapReadBytes);
508         SUM_VALUE(Ntfs, NextNtfs, MftBitmapWrites);
509         SUM_VALUE(Ntfs, NextNtfs, MftBitmapWriteBytes);
510         SUM_VALUE(Ntfs, NextNtfs, UserIndexReads);
511         SUM_VALUE(Ntfs, NextNtfs, UserIndexReadBytes);
512         SUM_VALUE(Ntfs, NextNtfs, UserIndexWrites);
513         SUM_VALUE(Ntfs, NextNtfs, UserIndexWriteBytes);
514         SUM_VALUE(Ntfs, NextNtfs, LogFileReads);
515         SUM_VALUE(Ntfs, NextNtfs, LogFileReadBytes);
516         SUM_VALUE(Ntfs, NextNtfs, LogFileWrites);
517         SUM_VALUE(Ntfs, NextNtfs, LogFileWriteBytes);
518     }
519 
520     return 0;
521 }
522 
523 static int
524 StatisticsMain(int argc, const TCHAR *argv[])
525 {
526     HANDLE Volume;
527     SYSTEM_INFO SystemInfo;
528     FILESYSTEM_STATISTICS Buffer;
529     PFILESYSTEM_STATISTICS Statistics;
530     DWORD BytesRead, Length, ProcCount;
531     /* +1 because 0 isn't assigned to a filesystem */
532     void (*DumpFct[FILESYSTEM_STATISTICS_TYPE_EXFAT + 1])(PVOID, PVOID) = { NULL, DumpNtfs, DumpFat, DumpExFat };
533     int (*SumFct[FILESYSTEM_STATISTICS_TYPE_EXFAT + 1])(PVOID, PVOID, ULONG, DWORD, DWORD) = { NULL, SumNtfs, SumFat, SumExFat };
534 
535     /* We need a volume (letter or GUID) */
536     if (argc < 2)
537     {
538         _ftprintf(stderr, _T("Usage: fsutil fsinfo statistics <volume>\n"));
539         _ftprintf(stderr, _T("\tFor example: fsutil fsinfo statistics c:\n"));
540         return 1;
541     }
542 
543     /* Get a handle for the volume */
544     Volume = OpenVolume(argv[1], FALSE, FALSE);
545     if (Volume == INVALID_HANDLE_VALUE)
546     {
547         return 1;
548     }
549 
550     /* And query the statistics status - this call is expected to fail */
551     Statistics = &Buffer;
552     Length = sizeof(Buffer);
553     /* Assume we have a single proc for now */
554     ProcCount = 1;
555     if (DeviceIoControl(Volume, FSCTL_FILESYSTEM_GET_STATISTICS, NULL, 0, Statistics,
556                         Length, &BytesRead, NULL) == FALSE)
557     {
558         DWORD Error;
559 
560         /* Check we failed because we provided a too small buffer */
561         Error = GetLastError();
562         if (Error == ERROR_MORE_DATA)
563         {
564             /* Get proc count */
565             GetSystemInfo(&SystemInfo);
566             ProcCount = SystemInfo.dwNumberOfProcessors;
567             /* Get the maximum size to allocate: it's the total size (generic + specific) for every proc */
568             Length = Statistics->SizeOfCompleteStructure * ProcCount;
569 
570             Statistics = LocalAlloc(LPTR, Length);
571             if (Statistics == NULL)
572             {
573                 _ftprintf(stderr, _T("Failed to allocate memory!\n"));
574                 CloseHandle(Volume);
575                 return 1;
576             }
577 
578             /* Reissue the FSCTL, it's supposed to succeed now! */
579             if (DeviceIoControl(Volume, FSCTL_FILESYSTEM_GET_STATISTICS, NULL, 0, Statistics,
580                                 Length, &BytesRead, NULL) == FALSE)
581             {
582                 PrintErrorMessage(GetLastError());
583                 LocalFree(Statistics);
584                 CloseHandle(Volume);
585                 return 1;
586             }
587         }
588         else
589         {
590             PrintErrorMessage(Error);
591             CloseHandle(Volume);
592             return 1;
593         }
594     }
595 
596     /* No need to deal with the volume any longer */
597     CloseHandle(Volume);
598 
599     /* We only support FAT, EXFAT and NTFS for now */
600     if (Statistics->FileSystemType > FILESYSTEM_STATISTICS_TYPE_EXFAT || Statistics->FileSystemType < FILESYSTEM_STATISTICS_TYPE_NTFS)
601     {
602         _ftprintf(stderr, _T("Unrecognized file system type: %d\n"), Statistics->FileSystemType);
603         if (Statistics != &Buffer)
604         {
605             LocalFree(Statistics);
606         }
607 
608         return 1;
609     }
610 
611     /* Sum all the statistics (both generic and specific) from all the processors in the first entry */
612     if (SumFct[Statistics->FileSystemType](Statistics, (PVOID)((ULONG_PTR)Statistics + sizeof(FILESYSTEM_STATISTICS)),
613                                            Statistics->SizeOfCompleteStructure, ProcCount, BytesRead))
614     {
615         if (Statistics != &Buffer)
616         {
617             LocalFree(Statistics);
618         }
619 
620         return 1;
621     }
622 
623     /* And finally, display the statistics (both generic and specific) */
624     DumpFct[Statistics->FileSystemType](Statistics, (PVOID)((ULONG_PTR)Statistics + sizeof(FILESYSTEM_STATISTICS)));
625 
626     /* If we allocated memory, release it */
627     if (Statistics != &Buffer)
628     {
629         LocalFree(Statistics);
630     }
631 
632     return 0;
633 }
634 
635 static void
636 PrintUsage(const TCHAR * Command)
637 {
638     PrintDefaultUsage(_T(" FSINFO "), Command, (HandlerItem *)&HandlersList,
639                       (sizeof(HandlersList) / sizeof(HandlersList[0])));
640 }
641 
642 int
643 FsInfoMain(int argc, const TCHAR *argv[])
644 {
645     return FindHandler(argc, argv, (HandlerItem *)&HandlersList,
646                        (sizeof(HandlersList) / sizeof(HandlersList[0])),
647                        PrintUsage);
648 }
649