1 /*
2  * COPYRIGHT:       See COPYING in the top level directory
3  * PROJECT:         ReactOS NTFS Information tool
4  * FILE:            cmdutils/ntfsinfo/ntfsinfo.c
5  * PURPOSE:         Query information from NTFS volume using FSCTL
6  * PROGRAMMERS:     Pierre Schweitzer <pierre@reactos.org>
7  */
8 
9 #include <windows.h>
10 #include <tchar.h>
11 #include <stdio.h>
12 
13 typedef struct
14 {
15     ULONG Type;
16     USHORT UsaOffset;
17     USHORT UsaCount;
18     ULONGLONG Lsn;
19 } NTFS_RECORD_HEADER, *PNTFS_RECORD_HEADER;
20 
21 #define NRH_FILE_TYPE  0x454C4946
22 #define ATTRIBUTE_TYPE_DATA 0x80
23 #define ATTRIBUTE_TYPE_END 0xFFFFFFFF
24 
25 typedef struct _FILE_RECORD_HEADER
26 {
27     NTFS_RECORD_HEADER Ntfs;
28     USHORT SequenceNumber;
29     USHORT LinkCount;
30     USHORT AttributeOffset;
31     USHORT Flags;
32     ULONG BytesInUse;
33     ULONG BytesAllocated;
34     ULONGLONG BaseFileRecord;
35     USHORT NextAttributeNumber;
36 } FILE_RECORD_HEADER, *PFILE_RECORD_HEADER;
37 
38 typedef struct
39 {
40     ULONG Type;
41     ULONG Length;
42     UCHAR IsNonResident;
43     UCHAR NameLength;
44     USHORT NameOffset;
45     USHORT Flags;
46     USHORT Instance;
47     union
48     {
49         struct
50         {
51             ULONG ValueLength;
52             USHORT ValueOffset;
53             UCHAR Flags;
54             UCHAR Reserved;
55         } Resident;
56         struct
57         {
58             ULONGLONG LowestVCN;
59             ULONGLONG HighestVCN;
60             USHORT MappingPairsOffset;
61             USHORT CompressionUnit;
62             UCHAR Reserved[4];
63             LONGLONG AllocatedSize;
64             LONGLONG DataSize;
65             LONGLONG InitializedSize;
66             LONGLONG CompressedSize;
67         } NonResident;
68     };
69 } NTFS_ATTR_RECORD, *PNTFS_ATTR_RECORD;
70 
71 static TCHAR * MetaDataFiles[] = {
72     _T("$MFT\t\t"),
73     _T("$MFTMirr\t"),
74     _T("$LogFile\t"),
75     _T("$Volume\t\t"),
76     _T("$AttrDef\t"),
77     _T("."),
78     _T("$Bitmap\t\t"),
79     _T("$Boot\t\t"),
80     _T("$BadClus\t"),
81     _T("$Quota\t\t"),
82     _T("$UpCase\t\t"),
83     _T("$Extended\t"),
84     NULL,
85 };
86 
87 int
88 __cdecl
89 _tmain(int argc, const TCHAR *argv[])
90 {
91     TCHAR VolumeName[] = _T("\\\\.\\C:");
92     HANDLE VolumeHandle;
93     NTFS_VOLUME_DATA_BUFFER VolumeInfo;
94     DWORD LengthReturned;
95     ULONGLONG VolumeSize;
96     ULONGLONG MftClusters;
97     UINT File = 0;
98     PNTFS_FILE_RECORD_OUTPUT_BUFFER OutputBuffer;
99 
100     if (argc > 1)
101     {
102         TCHAR Letter = argv[1][0];
103 
104         if ((Letter >= 'A' && Letter <= 'Z') ||
105             (Letter >= 'a' && Letter <= 'z'))
106         {
107             VolumeName[4] = Letter;
108         }
109     }
110 
111     VolumeHandle = CreateFile(VolumeName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0 );
112     if (VolumeHandle == INVALID_HANDLE_VALUE)
113     {
114         _ftprintf(stderr, _T("Failed opening the volume '%s' (%lx)\n"), VolumeName, GetLastError());
115         return 1;
116     }
117 
118     if (!DeviceIoControl(VolumeHandle, FSCTL_GET_NTFS_VOLUME_DATA, NULL, 0, &VolumeInfo, sizeof(VolumeInfo), &LengthReturned, NULL))
119     {
120         _ftprintf(stderr, _T("Failed requesting volume '%s' data (%lx)\n"), VolumeName, GetLastError());
121         CloseHandle(VolumeHandle);
122         return 1;
123     }
124 
125     if (LengthReturned < sizeof(VolumeInfo))
126     {
127         _ftprintf(stderr, _T("Failed reading volume '%s' data (%lx)\n"), VolumeName, GetLastError());
128         CloseHandle(VolumeHandle);
129         return 1;
130     }
131 
132     _tprintf(_T("Volume Size\n-----------\n"));
133     VolumeSize = VolumeInfo.TotalClusters.QuadPart * VolumeInfo.BytesPerCluster;
134     _tprintf(_T("Volume size\t\t: %I64u MB\n"), VolumeSize >> 20);
135     _tprintf(_T("Total sectors\t\t: %I64u\n"), VolumeInfo.NumberSectors.QuadPart);
136     _tprintf(_T("Total clusters\t\t: %I64u\n"), VolumeInfo.TotalClusters.QuadPart);
137     _tprintf(_T("Free clusters\t\t: %I64u\n"), VolumeInfo.FreeClusters.QuadPart);
138     _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);
139 
140     _tprintf(_T("\nAllocation Size\n---------------\n"));
141     _tprintf(_T("Bytes per sector\t: %lu\n"), VolumeInfo.BytesPerSector);
142     _tprintf(_T("Bytes per cluster\t: %lu\n"), VolumeInfo.BytesPerCluster);
143     _tprintf(_T("Bytes per MFT record:\t: %lu\n"), VolumeInfo.BytesPerFileRecordSegment);
144     _tprintf(_T("Clusters per MFT record\t: %lu\n"), VolumeInfo.ClustersPerFileRecordSegment);
145 
146     _tprintf(_T("\nMFT Information\n---------------\n"));
147     _tprintf(_T("MFT size\t\t: %I64u MB (%u%% of drive)\n"), VolumeInfo.MftValidDataLength.QuadPart >> 20, (VolumeInfo.MftValidDataLength.QuadPart * 100) / VolumeSize);
148     _tprintf(_T("MFT start cluster\t: %I64u\n"), VolumeInfo.MftStartLcn.QuadPart);
149     _tprintf(_T("MFT zone clusters\t: %I64u - %I64u\n"), VolumeInfo.MftZoneStart.QuadPart, VolumeInfo.MftZoneEnd.QuadPart);
150     MftClusters = VolumeInfo.MftZoneEnd.QuadPart - VolumeInfo.MftZoneStart.QuadPart;
151     _tprintf(_T("MFT zone size\t\t: %I64u MB (%u%% of drive)\n"), (MftClusters * VolumeInfo.BytesPerCluster) >> 20, (MftClusters * 100) / VolumeInfo.TotalClusters.QuadPart);
152     _tprintf(_T("MFT mirror start\t: %I64u\n"), VolumeInfo.Mft2StartLcn.QuadPart);
153 
154     _tprintf(_T("\nMeta-Data files\n---------------\n"));
155     OutputBuffer = HeapAlloc(GetProcessHeap(), 0, VolumeInfo.BytesPerFileRecordSegment + sizeof(NTFS_FILE_RECORD_OUTPUT_BUFFER));
156     while (MetaDataFiles[File] != NULL)
157     {
158         NTFS_FILE_RECORD_INPUT_BUFFER InputBuffer;
159         PFILE_RECORD_HEADER FileRecord;
160         PNTFS_ATTR_RECORD Attribute;
161         ULONGLONG Size = 0;
162 
163         if (File == 5)
164         {
165             ++File;
166             continue;
167         }
168 
169         InputBuffer.FileReferenceNumber.QuadPart = File;
170         if (!DeviceIoControl(VolumeHandle, FSCTL_GET_NTFS_FILE_RECORD, &InputBuffer, sizeof(InputBuffer),
171                              OutputBuffer, VolumeInfo.BytesPerFileRecordSegment  + sizeof(NTFS_FILE_RECORD_OUTPUT_BUFFER),
172                              &LengthReturned, NULL))
173         {
174             ++File;
175             continue;
176         }
177 
178         if (OutputBuffer->FileReferenceNumber.QuadPart != File)
179         {
180             ++File;
181             continue;
182         }
183 
184         FileRecord = (PFILE_RECORD_HEADER)OutputBuffer->FileRecordBuffer;
185         if (FileRecord->Ntfs.Type != NRH_FILE_TYPE)
186         {
187             ++File;
188             continue;
189         }
190 
191         Attribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + FileRecord->AttributeOffset);
192         while (Attribute < (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + FileRecord->BytesInUse) &&
193                Attribute->Type != ATTRIBUTE_TYPE_END)
194         {
195             if (Attribute->Type == ATTRIBUTE_TYPE_DATA)
196             {
197                 Size = (Attribute->IsNonResident) ? Attribute->NonResident.AllocatedSize : Attribute->Resident.ValueLength;
198                 break;
199             }
200 
201             Attribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)Attribute + Attribute->Length);
202         }
203 
204         _tprintf(_T("%s%I64u bytes\n"), MetaDataFiles[File], Size);
205 
206         ++File;
207     }
208 
209     HeapFree(GetProcessHeap(), 0, OutputBuffer);
210     CloseHandle(VolumeHandle);
211     return 0;
212 }
213