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