• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* -------------------------------------------------------------------------- *
2  *
3  *   Copyright 2011 Shao Miller - All Rights Reserved
4  *
5  *   This program is free software; you can redistribute it and/or modify
6  *   it under the terms of the GNU General Public License as published by
7  *   the Free Software Foundation, Inc., 53 Temple Place Ste 330,
8  *   Boston MA 02111-1307, USA; either version 2 of the License, or
9  *   (at your option) any later version; incorporated herein by reference.
10  *
11  * ------------------------------------------------------------------------- */
12 
13 /****
14  * ntfssect.c
15  *
16  * Fetch NTFS file cluster & sector information via Windows
17  *
18  * With special thanks to Mark Roddy for his article:
19  *   http://www.wd-3.com/archive/luserland.htm
20  */
21 
22 #include <windows.h>
23 #include <winioctl.h>
24 #include <stddef.h>
25 #include <string.h>
26 
27 #include "ntfssect.h"
28 
29 /*** Macros */
30 #define M_ERR(msg) (NtfsSectLastErrorMessage = (msg))
31 
32 /*** Function declarations */
33 static DWORD NtfsSectGetVolumeHandle(
34     CHAR * VolumeName,
35     S_NTFSSECT_VOLINFO * VolumeInfo
36   );
37 static DWORD NtfsSectGetVolumePartitionLba(S_NTFSSECT_VOLINFO * VolumeInfo);
38 
39 /*** Objects */
40 CHAR * NtfsSectLastErrorMessage;
41 
42 /*** Function definitions */
NtfsSectGetFileVcnExtent(HANDLE File,LARGE_INTEGER * Vcn,S_NTFSSECT_EXTENT * Extent)43 DWORD M_NTFSSECT_API NtfsSectGetFileVcnExtent(
44     HANDLE File,
45     LARGE_INTEGER * Vcn,
46     S_NTFSSECT_EXTENT * Extent
47   ) {
48     BOOL bad, ok;
49     DWORD output_size, rc;
50     STARTING_VCN_INPUT_BUFFER input;
51     RETRIEVAL_POINTERS_BUFFER output;
52 
53     bad = (
54         File == INVALID_HANDLE_VALUE ||
55         !Vcn ||
56         Vcn->QuadPart < 0 ||
57         !Extent
58       );
59     if (bad)
60       return ERROR_INVALID_PARAMETER;
61 
62     input.StartingVcn = *Vcn;
63     ok = DeviceIoControl(
64         File,
65         FSCTL_GET_RETRIEVAL_POINTERS,
66         &input,
67         sizeof input,
68         &output,
69         sizeof output,
70         &output_size,
71         NULL
72       );
73     rc = GetLastError();
74     switch (rc) {
75         case NO_ERROR:
76         case ERROR_MORE_DATA:
77           Extent->FirstVcn = output.StartingVcn;
78           Extent->NextVcn = output.Extents[0].NextVcn;
79           Extent->FirstLcn = output.Extents[0].Lcn;
80           return ERROR_SUCCESS;
81 
82         case ERROR_HANDLE_EOF:
83           break;
84 
85         default:
86           M_ERR("NtfsSectGetFileVcnExtent(): Unknown status!");
87       }
88 
89     return rc;
90   }
91 
92 /* Internal use only */
NtfsSectGetVolumeHandle(CHAR * VolumeName,S_NTFSSECT_VOLINFO * VolumeInfo)93 static DWORD NtfsSectGetVolumeHandle(
94     CHAR * VolumeName,
95     S_NTFSSECT_VOLINFO * VolumeInfo
96   ) {
97     #define M_VOL_PREFIX "\\\\.\\"
98     CHAR volname[sizeof M_VOL_PREFIX - 1 + MAX_PATH + 1] = M_VOL_PREFIX;
99     CHAR * const volname_short = volname + sizeof M_VOL_PREFIX - 1;
100     CHAR * c;
101     DWORD rc;
102 
103     /* Prefix "\\.\" onto the passed volume name */
104     strcpy(volname + sizeof M_VOL_PREFIX - 1, VolumeName);
105 
106     /* Find the last non-null character */
107     for (c = volname_short; *c; ++c)
108       ;
109 
110     /* Remove trailing back-slash */
111     if (c[-1] == '\\')
112       c[-1] = 0;
113 
114     /* Open the volume */
115     VolumeInfo->Handle = CreateFile(
116         volname,
117         GENERIC_READ,
118         FILE_SHARE_READ | FILE_SHARE_WRITE,
119         NULL,
120         OPEN_EXISTING,
121         0,
122         NULL
123       );
124     rc = GetLastError();
125     if (VolumeInfo->Handle == INVALID_HANDLE_VALUE) {
126         M_ERR("Unable to open volume handle!");
127         goto err_handle;
128       }
129 
130     return ERROR_SUCCESS;
131 
132     CloseHandle(VolumeInfo->Handle);
133     err_handle:
134 
135     return rc;
136   }
137 
NtfsSectGetVolumeInfo(CHAR * VolumeName,S_NTFSSECT_VOLINFO * VolumeInfo)138 DWORD M_NTFSSECT_API NtfsSectGetVolumeInfo(
139     CHAR * VolumeName,
140     S_NTFSSECT_VOLINFO * VolumeInfo
141   ) {
142     S_NTFSSECT_XPFUNCS xp_funcs;
143     DWORD rc, free_clusts, total_clusts;
144     BOOL ok;
145 
146     if (!VolumeName || !VolumeInfo)
147       return ERROR_INVALID_PARAMETER;
148 
149     rc = NtfsSectGetVolumeHandle(VolumeName, VolumeInfo);
150     if (rc != ERROR_SUCCESS)
151       goto err_handle;
152 
153     rc = NtfsSectLoadXpFuncs(&xp_funcs);
154     if (rc != ERROR_SUCCESS)
155       goto err_xp_funcs;
156 
157     ok = xp_funcs.GetDiskFreeSpace(
158         VolumeName,
159         &VolumeInfo->SectorsPerCluster,
160         &VolumeInfo->BytesPerSector,
161         &free_clusts,
162         &total_clusts
163       );
164     rc = GetLastError();
165     if (!ok) {
166         M_ERR("GetDiskFreeSpace() failed!");
167         goto err_freespace;
168       }
169 
170     rc = NtfsSectGetVolumePartitionLba(VolumeInfo);
171     if (rc != ERROR_SUCCESS)
172       goto err_lba;
173 
174     VolumeInfo->Size = sizeof *VolumeInfo;
175     rc = ERROR_SUCCESS;
176 
177     err_lba:
178 
179     err_freespace:
180 
181     NtfsSectUnloadXpFuncs(&xp_funcs);
182     err_xp_funcs:
183 
184     if (rc != ERROR_SUCCESS) {
185         CloseHandle(VolumeInfo->Handle);
186         VolumeInfo->Handle = INVALID_HANDLE_VALUE;
187       }
188     err_handle:
189 
190     return rc;
191   }
192 
NtfsSectGetVolumeInfoFromFileName(CHAR * FileName,S_NTFSSECT_VOLINFO * VolumeInfo)193 DWORD M_NTFSSECT_API NtfsSectGetVolumeInfoFromFileName(
194     CHAR * FileName,
195     S_NTFSSECT_VOLINFO * VolumeInfo
196   ) {
197     S_NTFSSECT_XPFUNCS xp_funcs;
198     DWORD rc;
199     CHAR volname[MAX_PATH + 1];
200     BOOL ok;
201 
202     if (!FileName || !VolumeInfo)
203       return ERROR_INVALID_PARAMETER;
204 
205     rc = NtfsSectLoadXpFuncs(&xp_funcs);
206     if (rc != ERROR_SUCCESS) {
207         goto err_xp_funcs;
208       }
209 
210     ok = xp_funcs.GetVolumePathName(
211         FileName,
212         volname,
213         sizeof volname
214       );
215     rc = GetLastError();
216     if (!ok) {
217         M_ERR("GetVolumePathName() failed!");
218         goto err_volname;
219       }
220 
221     rc = NtfsSectGetVolumeInfo(volname, VolumeInfo);
222 
223     err_volname:
224 
225     NtfsSectUnloadXpFuncs(&xp_funcs);
226     err_xp_funcs:
227 
228     return rc;
229   }
230 
231 /* Internal use only */
NtfsSectGetVolumePartitionLba(S_NTFSSECT_VOLINFO * VolumeInfo)232 static DWORD NtfsSectGetVolumePartitionLba(S_NTFSSECT_VOLINFO * VolumeInfo) {
233     BOOL ok;
234     VOLUME_DISK_EXTENTS vol_disk_extents;
235     DWORD output_size, rc;
236 
237     ok = DeviceIoControl(
238         VolumeInfo->Handle,
239         IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS,
240         NULL,
241         0,
242         &vol_disk_extents,
243         sizeof vol_disk_extents,
244         &output_size,
245         NULL
246       );
247     rc = GetLastError();
248     if (!ok) {
249         M_ERR("Couldn't fetch volume disk extent(s)!");
250         goto err_vol_disk_extents;
251       }
252 
253     if (vol_disk_extents.NumberOfDiskExtents != 1) {
254         M_ERR("Unsupported number of volume disk extents!");
255         goto err_num_of_extents;
256       }
257 
258     VolumeInfo->PartitionLba.QuadPart = (
259         vol_disk_extents.Extents[0].StartingOffset.QuadPart /
260         VolumeInfo->BytesPerSector
261       );
262 
263     return ERROR_SUCCESS;
264 
265     err_num_of_extents:
266 
267     err_vol_disk_extents:
268 
269     return rc;
270   }
271 
NtfsSectLcnToLba(const S_NTFSSECT_VOLINFO * VolumeInfo,const LARGE_INTEGER * Lcn,LARGE_INTEGER * Lba)272 DWORD M_NTFSSECT_API NtfsSectLcnToLba(
273     const S_NTFSSECT_VOLINFO * VolumeInfo,
274     const LARGE_INTEGER * Lcn,
275     LARGE_INTEGER * Lba
276   ) {
277     BOOL bad;
278     bad = (
279         !VolumeInfo ||
280         !VolumeInfo->BytesPerSector ||
281         !VolumeInfo->SectorsPerCluster ||
282         !Lcn ||
283         Lcn->QuadPart < 0 ||
284         !Lba
285       );
286     if (bad)
287       return ERROR_INVALID_PARAMETER;
288 
289     Lba->QuadPart = (
290         VolumeInfo->PartitionLba.QuadPart +
291         Lcn->QuadPart *
292         VolumeInfo->SectorsPerCluster
293       );
294     return ERROR_SUCCESS;
295   }
296 
NtfsSectLoadXpFuncs(S_NTFSSECT_XPFUNCS * XpFuncs)297 DWORD M_NTFSSECT_API NtfsSectLoadXpFuncs(S_NTFSSECT_XPFUNCS * XpFuncs) {
298     DWORD rc;
299 
300     if (!XpFuncs)
301       return ERROR_INVALID_PARAMETER;
302 
303     XpFuncs->Size = sizeof *XpFuncs;
304 
305     XpFuncs->Kernel32 = LoadLibrary("kernel32.dll");
306     rc = GetLastError();
307     if (!XpFuncs->Kernel32) {
308         M_ERR("KERNEL32.DLL not found!");
309         goto err;
310       }
311 
312     XpFuncs->GetVolumePathName = (F_KERNEL32_GETVOLUMEPATHNAME *) (
313         GetProcAddress(
314             XpFuncs->Kernel32,
315             "GetVolumePathNameA"
316           )
317       );
318     rc = GetLastError();
319     if (!XpFuncs->GetVolumePathName) {
320         M_ERR("GetVolumePathName() not found in KERNEL32.DLL!");
321         goto err;
322       }
323 
324     XpFuncs->GetDiskFreeSpace = (F_KERNEL32_GETDISKFREESPACE *) (
325         GetProcAddress(
326             XpFuncs->Kernel32,
327             "GetDiskFreeSpaceA"
328           )
329       );
330     rc = GetLastError();
331     if (!XpFuncs->GetDiskFreeSpace) {
332         M_ERR("GetDiskFreeSpace() not found in KERNEL32.DLL!");
333         goto err;
334       }
335 
336     return ERROR_SUCCESS;
337 
338     err:
339     NtfsSectUnloadXpFuncs(XpFuncs);
340     return rc;
341   }
342 
NtfsSectUnloadXpFuncs(S_NTFSSECT_XPFUNCS * XpFuncs)343 VOID M_NTFSSECT_API NtfsSectUnloadXpFuncs(S_NTFSSECT_XPFUNCS * XpFuncs) {
344     if (!XpFuncs)
345       return;
346 
347     XpFuncs->GetDiskFreeSpace = NULL;
348     XpFuncs->GetVolumePathName = NULL;
349     if (XpFuncs->Kernel32)
350       FreeLibrary(XpFuncs->Kernel32);
351     XpFuncs->Kernel32 = NULL;
352     XpFuncs->Size = 0;
353     return;
354   }
355 
356