From 1c3c66afa43a8b8edb729d02fdb015f77fac2376 Mon Sep 17 00:00:00 2001 From: Pierre Schweitzer Date: Tue, 30 Jun 2015 21:03:55 +0000 Subject: [PATCH] [NTFSINFO] Time to free some Sysinternals tool: ntfsinfo. To make it short, this tool dumps various information about a NTFS volume and its reserved meta-data files Our version comes with three advantages compared to the Russinovich's tool: - It's FLOSS - It works properly on NT5+ (developed with W2K3 & W7) whereas R's cannot display meta-data files information - It will open a volume by default if none provided One issue so far: it doesn't work properly on ReactOS! CORE-8725 svn path=/trunk/; revision=68326 --- rosapps/applications/cmdutils/CMakeLists.txt | 1 + .../cmdutils/ntfsinfo/CMakeLists.txt | 5 + .../applications/cmdutils/ntfsinfo/ntfsinfo.c | 212 ++++++++++++++++++ .../cmdutils/ntfsinfo/ntfsinfo.rc | 4 + 4 files changed, 222 insertions(+) create mode 100644 rosapps/applications/cmdutils/ntfsinfo/CMakeLists.txt create mode 100644 rosapps/applications/cmdutils/ntfsinfo/ntfsinfo.c create mode 100644 rosapps/applications/cmdutils/ntfsinfo/ntfsinfo.rc diff --git a/rosapps/applications/cmdutils/CMakeLists.txt b/rosapps/applications/cmdutils/CMakeLists.txt index b30ee491457..374d3b87634 100644 --- a/rosapps/applications/cmdutils/CMakeLists.txt +++ b/rosapps/applications/cmdutils/CMakeLists.txt @@ -1,5 +1,6 @@ add_subdirectory(appwiz) add_subdirectory(cat) +add_subdirectory(ntfsinfo) add_subdirectory(tee) add_subdirectory(touch) add_subdirectory(uptime) diff --git a/rosapps/applications/cmdutils/ntfsinfo/CMakeLists.txt b/rosapps/applications/cmdutils/ntfsinfo/CMakeLists.txt new file mode 100644 index 00000000000..69db6f991ce --- /dev/null +++ b/rosapps/applications/cmdutils/ntfsinfo/CMakeLists.txt @@ -0,0 +1,5 @@ +list(APPEND SOURCE ntfsinfo.c ntfsinfo.rc) +add_executable(ntfsinfo ${SOURCE}) +set_module_type(ntfsinfo win32cui UNICODE) +add_importlibs(ntfsinfo msvcrt kernel32 ntdll) +add_cd_file(TARGET ntfsinfo DESTINATION reactos/system32 FOR all) diff --git a/rosapps/applications/cmdutils/ntfsinfo/ntfsinfo.c b/rosapps/applications/cmdutils/ntfsinfo/ntfsinfo.c new file mode 100644 index 00000000000..c7022505c89 --- /dev/null +++ b/rosapps/applications/cmdutils/ntfsinfo/ntfsinfo.c @@ -0,0 +1,212 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS NTFS Information tool + * FILE: cmdutils/ntfsinfo/ntfsinfo.c + * PURPOSE: Query information from NTFS volume using FSCTL + * PROGRAMMERS: Pierre Schweitzer + */ + +#include +#include +#include + +typedef struct +{ + ULONG Type; + USHORT UsaOffset; + USHORT UsaCount; + ULONGLONG Lsn; +} NTFS_RECORD_HEADER, *PNTFS_RECORD_HEADER; + +#define NRH_FILE_TYPE 0x454C4946 +#define ATTRIBUTE_TYPE_DATA 0x80 +#define ATTRIBUTE_TYPE_END 0xFFFFFFFF + +typedef struct _FILE_RECORD_HEADER +{ + NTFS_RECORD_HEADER Ntfs; + USHORT SequenceNumber; + USHORT LinkCount; + USHORT AttributeOffset; + USHORT Flags; + ULONG BytesInUse; + ULONG BytesAllocated; + ULONGLONG BaseFileRecord; + USHORT NextAttributeNumber; +} FILE_RECORD_HEADER, *PFILE_RECORD_HEADER; + +typedef struct +{ + ULONG Type; + ULONG Length; + UCHAR IsNonResident; + UCHAR NameLength; + USHORT NameOffset; + USHORT Flags; + USHORT Instance; + union + { + struct + { + ULONG ValueLength; + USHORT ValueOffset; + UCHAR Flags; + UCHAR Reserved; + } Resident; + struct + { + ULONGLONG LowestVCN; + ULONGLONG HighestVCN; + USHORT MappingPairsOffset; + USHORT CompressionUnit; + UCHAR Reserved[4]; + LONGLONG AllocatedSize; + LONGLONG DataSize; + LONGLONG InitializedSize; + LONGLONG CompressedSize; + } NonResident; + }; +} NTFS_ATTR_RECORD, *PNTFS_ATTR_RECORD; + +static TCHAR * MetaDataFiles[] = { + _T("$MFT\t\t"), + _T("$MFTMirr\t"), + _T("$LogFile\t"), + _T("$Volume\t\t"), + _T("$AttrDef\t"), + _T("."), + _T("$Bitmap\t\t"), + _T("$Boot\t\t"), + _T("$BadClus\t"), + _T("$Quota\t\t"), + _T("$UpCase\t\t"), + _T("$Extended\t"), + NULL, +}; + +int +__cdecl +_tmain(int argc, const TCHAR *argv[]) +{ + TCHAR VolumeName[] = _T("\\\\.\\C:"); + HANDLE VolumeHandle; + NTFS_VOLUME_DATA_BUFFER VolumeInfo; + DWORD LengthReturned; + ULONGLONG VolumeSize; + ULONGLONG MftClusters; + UINT File = 0; + PNTFS_FILE_RECORD_OUTPUT_BUFFER OutputBuffer; + + if (argc > 1) + { + TCHAR Letter = argv[1][0]; + + if ((Letter >= 'A' && Letter <= 'Z') || + (Letter >= 'a' && Letter <= 'z')) + { + VolumeName[4] = Letter; + } + } + + VolumeHandle = CreateFile(VolumeName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0 ); + if (VolumeHandle == INVALID_HANDLE_VALUE) + { + _ftprintf(stderr, _T("Failed opening the volume '%s' (%lx)\n"), VolumeName, GetLastError()); + return 1; + } + + if (!DeviceIoControl(VolumeHandle, FSCTL_GET_NTFS_VOLUME_DATA, NULL, 0, &VolumeInfo, sizeof(VolumeInfo), &LengthReturned, NULL)) + { + _ftprintf(stderr, _T("Failed requesting volume '%s' data (%lx)\n"), VolumeName, GetLastError()); + CloseHandle(VolumeHandle); + return 1; + } + + if (LengthReturned < sizeof(VolumeInfo)) + { + _ftprintf(stderr, _T("Failed reading volume '%s' data (%lx)\n"), VolumeName, GetLastError()); + CloseHandle(VolumeHandle); + return 1; + } + + _tprintf(_T("Volume Size\n-----------\n")); + VolumeSize = VolumeInfo.TotalClusters.QuadPart * VolumeInfo.BytesPerCluster; + _tprintf(_T("Volume size\t\t: %I64u MB\n"), VolumeSize >> 20); + _tprintf(_T("Total sectors\t\t: %I64u\n"), VolumeInfo.NumberSectors.QuadPart); + _tprintf(_T("Total clusters\t\t: %I64u\n"), VolumeInfo.TotalClusters.QuadPart); + _tprintf(_T("Free clusters\t\t: %I64u\n"), VolumeInfo.FreeClusters.QuadPart); + _tprintf(_T("Free space\t\t: %I64u MB (%u%% of drive)\n"), (VolumeInfo.FreeClusters.QuadPart * VolumeInfo.BytesPerCluster) >> 20, (VolumeInfo.FreeClusters.QuadPart * 100) / VolumeInfo.TotalClusters.QuadPart); + + _tprintf(_T("\nAllocation Size\n---------------\n")); + _tprintf(_T("Bytes per sector\t: %lu\n"), VolumeInfo.BytesPerSector); + _tprintf(_T("Bytes per cluster\t: %lu\n"), VolumeInfo.BytesPerCluster); + _tprintf(_T("Bytes per MFT record:\t: %lu\n"), VolumeInfo.BytesPerFileRecordSegment); + _tprintf(_T("Clusters per MFT record\t: %lu\n"), VolumeInfo.ClustersPerFileRecordSegment); + + _tprintf(_T("\nMFT Information\n---------------\n")); + _tprintf(_T("MFT size\t\t: %I64u MB (%u%% of drive)\n"), VolumeInfo.MftValidDataLength.QuadPart >> 20, (VolumeInfo.MftValidDataLength.QuadPart * 100) / VolumeSize); + _tprintf(_T("MFT start cluster\t: %I64u\n"), VolumeInfo.MftStartLcn.QuadPart); + _tprintf(_T("MFT zone clusters\t: %I64u - %I64u\n"), VolumeInfo.MftZoneStart.QuadPart, VolumeInfo.MftZoneEnd.QuadPart); + MftClusters = VolumeInfo.MftZoneEnd.QuadPart - VolumeInfo.MftZoneStart.QuadPart; + _tprintf(_T("MFT zone size\t\t: %I64u MB (%u%% of drive)\n"), (MftClusters * VolumeInfo.BytesPerCluster) >> 20, (MftClusters * 100) / VolumeInfo.TotalClusters.QuadPart); + _tprintf(_T("MFT mirror start\t: %I64u\n"), VolumeInfo.Mft2StartLcn.QuadPart); + + _tprintf(_T("\nMeta-Data files\n---------------\n")); + OutputBuffer = HeapAlloc(GetProcessHeap(), 0, VolumeInfo.BytesPerFileRecordSegment + sizeof(NTFS_FILE_RECORD_OUTPUT_BUFFER)); + while (MetaDataFiles[File] != NULL) + { + NTFS_FILE_RECORD_INPUT_BUFFER InputBuffer; + PFILE_RECORD_HEADER FileRecord; + PNTFS_ATTR_RECORD Attribute; + ULONGLONG Size = 0; + + if (File == 5) + { + ++File; + continue; + } + + InputBuffer.FileReferenceNumber.QuadPart = File; + if (!DeviceIoControl(VolumeHandle, FSCTL_GET_NTFS_FILE_RECORD, &InputBuffer, sizeof(InputBuffer), + OutputBuffer, VolumeInfo.BytesPerFileRecordSegment + sizeof(NTFS_FILE_RECORD_OUTPUT_BUFFER), + &LengthReturned, NULL)) + { + ++File; + continue; + } + + if (OutputBuffer->FileReferenceNumber.QuadPart != File) + { + ++File; + continue; + } + + FileRecord = (PFILE_RECORD_HEADER)OutputBuffer->FileRecordBuffer; + if (FileRecord->Ntfs.Type != NRH_FILE_TYPE) + { + ++File; + continue; + } + + Attribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + FileRecord->AttributeOffset); + while (Attribute < (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + FileRecord->BytesInUse) && + Attribute->Type != ATTRIBUTE_TYPE_END) + { + if (Attribute->Type == ATTRIBUTE_TYPE_DATA) + { + Size = (Attribute->IsNonResident) ? Attribute->NonResident.AllocatedSize : Attribute->Resident.ValueLength; + break; + } + + Attribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)Attribute + Attribute->Length); + } + + _tprintf(_T("%s%I64u bytes\n"), MetaDataFiles[File], Size); + + ++File; + } + + HeapFree(GetProcessHeap(), 0, OutputBuffer); + CloseHandle(VolumeHandle); + return 0; +} diff --git a/rosapps/applications/cmdutils/ntfsinfo/ntfsinfo.rc b/rosapps/applications/cmdutils/ntfsinfo/ntfsinfo.rc new file mode 100644 index 00000000000..f0440d3fdcf --- /dev/null +++ b/rosapps/applications/cmdutils/ntfsinfo/ntfsinfo.rc @@ -0,0 +1,4 @@ +#define REACTOS_STR_FILE_DESCRIPTION "NTFS Information\0" +#define REACTOS_STR_INTERNAL_NAME "ntfsinfo\0" +#define REACTOS_STR_ORIGINAL_FILENAME "ntfsinfo.exe\0" +#include -- 2.17.1