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 */ 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 */ 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 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 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 */ 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 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 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 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