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