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
DrivesMain(int argc,const TCHAR * argv[])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
DriveTypeMain(int argc,const TCHAR * argv[])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
VolumeInfoMain(int argc,const TCHAR * argv[])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
NtfsInfoMain(int argc,const TCHAR * argv[])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
DumpBase(PFILESYSTEM_STATISTICS Base,TCHAR * Name)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
DumpExFat(PVOID Statistics,PVOID Specific)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
DumpFat(PVOID Statistics,PVOID Specific)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
DumpNtfs(PVOID Statistics,PVOID Specific)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
ValidateSizes(ULONG Length,DWORD ProcCount,DWORD BytesRead,DWORD SpecificSize)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
SumBase(PFILESYSTEM_STATISTICS Base,PFILESYSTEM_STATISTICS NextBase)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
SumExFat(PVOID Statistics,PVOID Specific,ULONG Length,DWORD ProcCount,DWORD BytesRead)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
SumFat(PVOID Statistics,PVOID Specific,ULONG Length,DWORD ProcCount,DWORD BytesRead)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
SumNtfs(PVOID Statistics,PVOID Specific,ULONG Length,DWORD ProcCount,DWORD BytesRead)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
StatisticsMain(int argc,const TCHAR * argv[])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
PrintUsage(const TCHAR * Command)636 PrintUsage(const TCHAR * Command)
637 {
638 PrintDefaultUsage(_T(" FSINFO "), Command, (HandlerItem *)&HandlersList,
639 (sizeof(HandlersList) / sizeof(HandlersList[0])));
640 }
641
642 int
FsInfoMain(int argc,const TCHAR * argv[])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