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