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