356 lines
8.2 KiB
C
356 lines
8.2 KiB
C
/* -------------------------------------------------------------------------- *
|
|
*
|
|
* 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;
|
|
}
|
|
|