diff options
Diffstat (limited to 'win/ntfssect.c')
-rw-r--r-- | win/ntfssect.c | 355 |
1 files changed, 355 insertions, 0 deletions
diff --git a/win/ntfssect.c b/win/ntfssect.c new file mode 100644 index 00000000..8c2bccaa --- /dev/null +++ b/win/ntfssect.c @@ -0,0 +1,355 @@ +/* -------------------------------------------------------------------------- * + * + * Copyright 2011 Shao Miller - All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, Inc., 53 Temple Place Ste 330, + * Boston MA 02111-1307, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ------------------------------------------------------------------------- */ + +/**** + * ntfssect.c + * + * Fetch NTFS file cluster & sector information via Windows + * + * With special thanks to Mark Roddy for his article: + * http://www.wd-3.com/archive/luserland.htm + */ + +#include <windows.h> +#include <winioctl.h> +#include <stddef.h> +#include <string.h> + +#include "ntfssect.h" + +/*** Macros */ +#define M_ERR(msg) (NtfsSectLastErrorMessage = (msg)) + +/*** Function declarations */ +static DWORD NtfsSectGetVolumeHandle( + CHAR * VolumeName, + S_NTFSSECT_VOLINFO * VolumeInfo + ); +static DWORD NtfsSectGetVolumePartitionLba(S_NTFSSECT_VOLINFO * VolumeInfo); + +/*** Objects */ +CHAR * NtfsSectLastErrorMessage; + +/*** Function definitions */ +DWORD M_NTFSSECT_API NtfsSectGetFileVcnExtent( + HANDLE File, + LARGE_INTEGER * Vcn, + S_NTFSSECT_EXTENT * Extent + ) { + BOOL bad, ok; + DWORD output_size, rc; + STARTING_VCN_INPUT_BUFFER input; + RETRIEVAL_POINTERS_BUFFER output; + + bad = ( + File == INVALID_HANDLE_VALUE || + !Vcn || + Vcn->QuadPart < 0 || + !Extent + ); + if (bad) + return ERROR_INVALID_PARAMETER; + + input.StartingVcn = *Vcn; + ok = DeviceIoControl( + File, + FSCTL_GET_RETRIEVAL_POINTERS, + &input, + sizeof input, + &output, + sizeof output, + &output_size, + NULL + ); + rc = GetLastError(); + switch (rc) { + case NO_ERROR: + case ERROR_MORE_DATA: + Extent->FirstVcn = output.StartingVcn; + Extent->NextVcn = output.Extents[0].NextVcn; + Extent->FirstLcn = output.Extents[0].Lcn; + return ERROR_SUCCESS; + + case ERROR_HANDLE_EOF: + break; + + default: + M_ERR("NtfsSectGetFileVcnExtent(): Unknown status!"); + } + + return rc; + } + +/* Internal use only */ +static DWORD NtfsSectGetVolumeHandle( + CHAR * VolumeName, + S_NTFSSECT_VOLINFO * VolumeInfo + ) { + #define M_VOL_PREFIX "\\\\.\\" + CHAR volname[sizeof M_VOL_PREFIX - 1 + MAX_PATH + 1] = M_VOL_PREFIX; + CHAR * const volname_short = volname + sizeof M_VOL_PREFIX - 1; + CHAR * c; + DWORD rc; + + /* Prefix "\\.\" onto the passed volume name */ + strcpy(volname + sizeof M_VOL_PREFIX - 1, VolumeName); + + /* Find the last non-null character */ + for (c = volname_short; *c; ++c) + ; + + /* Remove trailing back-slash */ + if (c[-1] == '\\') + c[-1] = 0; + + /* Open the volume */ + VolumeInfo->Handle = CreateFile( + volname, + GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + OPEN_EXISTING, + 0, + NULL + ); + rc = GetLastError(); + if (VolumeInfo->Handle == INVALID_HANDLE_VALUE) { + M_ERR("Unable to open volume handle!"); + goto err_handle; + } + + return ERROR_SUCCESS; + + CloseHandle(VolumeInfo->Handle); + err_handle: + + return rc; + } + +DWORD M_NTFSSECT_API NtfsSectGetVolumeInfo( + CHAR * VolumeName, + S_NTFSSECT_VOLINFO * VolumeInfo + ) { + S_NTFSSECT_XPFUNCS xp_funcs; + DWORD rc, free_clusts, total_clusts; + BOOL ok; + + if (!VolumeName || !VolumeInfo) + return ERROR_INVALID_PARAMETER; + + rc = NtfsSectGetVolumeHandle(VolumeName, VolumeInfo); + if (rc != ERROR_SUCCESS) + goto err_handle; + + rc = NtfsSectLoadXpFuncs(&xp_funcs); + if (rc != ERROR_SUCCESS) + goto err_xp_funcs; + + ok = xp_funcs.GetDiskFreeSpace( + VolumeName, + &VolumeInfo->SectorsPerCluster, + &VolumeInfo->BytesPerSector, + &free_clusts, + &total_clusts + ); + rc = GetLastError(); + if (!ok) { + M_ERR("GetDiskFreeSpace() failed!"); + goto err_freespace; + } + + rc = NtfsSectGetVolumePartitionLba(VolumeInfo); + if (rc != ERROR_SUCCESS) + goto err_lba; + + VolumeInfo->Size = sizeof *VolumeInfo; + rc = ERROR_SUCCESS; + + err_lba: + + err_freespace: + + NtfsSectUnloadXpFuncs(&xp_funcs); + err_xp_funcs: + + if (rc != ERROR_SUCCESS) { + CloseHandle(VolumeInfo->Handle); + VolumeInfo->Handle = INVALID_HANDLE_VALUE; + } + err_handle: + + return rc; + } + +DWORD M_NTFSSECT_API NtfsSectGetVolumeInfoFromFileName( + CHAR * FileName, + S_NTFSSECT_VOLINFO * VolumeInfo + ) { + S_NTFSSECT_XPFUNCS xp_funcs; + DWORD rc; + CHAR volname[MAX_PATH + 1]; + BOOL ok; + + if (!FileName || !VolumeInfo) + return ERROR_INVALID_PARAMETER; + + rc = NtfsSectLoadXpFuncs(&xp_funcs); + if (rc != ERROR_SUCCESS) { + goto err_xp_funcs; + } + + ok = xp_funcs.GetVolumePathName( + FileName, + volname, + sizeof volname + ); + rc = GetLastError(); + if (!ok) { + M_ERR("GetVolumePathName() failed!"); + goto err_volname; + } + + rc = NtfsSectGetVolumeInfo(volname, VolumeInfo); + + err_volname: + + NtfsSectUnloadXpFuncs(&xp_funcs); + err_xp_funcs: + + return rc; + } + +/* Internal use only */ +static DWORD NtfsSectGetVolumePartitionLba(S_NTFSSECT_VOLINFO * VolumeInfo) { + BOOL ok; + VOLUME_DISK_EXTENTS vol_disk_extents; + DWORD output_size, rc; + + ok = DeviceIoControl( + VolumeInfo->Handle, + IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, + NULL, + 0, + &vol_disk_extents, + sizeof vol_disk_extents, + &output_size, + NULL + ); + rc = GetLastError(); + if (!ok) { + M_ERR("Couldn't fetch volume disk extent(s)!"); + goto err_vol_disk_extents; + } + + if (vol_disk_extents.NumberOfDiskExtents != 1) { + M_ERR("Unsupported number of volume disk extents!"); + goto err_num_of_extents; + } + + VolumeInfo->PartitionLba.QuadPart = ( + vol_disk_extents.Extents[0].StartingOffset.QuadPart / + VolumeInfo->BytesPerSector + ); + + return ERROR_SUCCESS; + + err_num_of_extents: + + err_vol_disk_extents: + + return rc; + } + +DWORD M_NTFSSECT_API NtfsSectLcnToLba( + const S_NTFSSECT_VOLINFO * VolumeInfo, + const LARGE_INTEGER * Lcn, + LARGE_INTEGER * Lba + ) { + BOOL bad; + bad = ( + !VolumeInfo || + !VolumeInfo->BytesPerSector || + !VolumeInfo->SectorsPerCluster || + !Lcn || + Lcn->QuadPart < 0 || + !Lba + ); + if (bad) + return ERROR_INVALID_PARAMETER; + + Lba->QuadPart = ( + VolumeInfo->PartitionLba.QuadPart + + Lcn->QuadPart * + VolumeInfo->SectorsPerCluster + ); + return ERROR_SUCCESS; + } + +DWORD M_NTFSSECT_API NtfsSectLoadXpFuncs(S_NTFSSECT_XPFUNCS * XpFuncs) { + DWORD rc; + + if (!XpFuncs) + return ERROR_INVALID_PARAMETER; + + XpFuncs->Size = sizeof *XpFuncs; + + XpFuncs->Kernel32 = LoadLibrary("kernel32.dll"); + rc = GetLastError(); + if (!XpFuncs->Kernel32) { + M_ERR("KERNEL32.DLL not found!"); + goto err; + } + + XpFuncs->GetVolumePathName = (F_KERNEL32_GETVOLUMEPATHNAME *) ( + GetProcAddress( + XpFuncs->Kernel32, + "GetVolumePathNameA" + ) + ); + rc = GetLastError(); + if (!XpFuncs->GetVolumePathName) { + M_ERR("GetVolumePathName() not found in KERNEL32.DLL!"); + goto err; + } + + XpFuncs->GetDiskFreeSpace = (F_KERNEL32_GETDISKFREESPACE *) ( + GetProcAddress( + XpFuncs->Kernel32, + "GetDiskFreeSpaceA" + ) + ); + rc = GetLastError(); + if (!XpFuncs->GetDiskFreeSpace) { + M_ERR("GetDiskFreeSpace() not found in KERNEL32.DLL!"); + goto err; + } + + return ERROR_SUCCESS; + + err: + NtfsSectUnloadXpFuncs(XpFuncs); + return rc; + } + +VOID M_NTFSSECT_API NtfsSectUnloadXpFuncs(S_NTFSSECT_XPFUNCS * XpFuncs) { + if (!XpFuncs) + return; + + XpFuncs->GetDiskFreeSpace = NULL; + XpFuncs->GetVolumePathName = NULL; + if (XpFuncs->Kernel32) + FreeLibrary(XpFuncs->Kernel32); + XpFuncs->Kernel32 = NULL; + XpFuncs->Size = 0; + return; + } + |