summaryrefslogtreecommitdiff
path: root/win/ntfssect.c
diff options
context:
space:
mode:
Diffstat (limited to 'win/ntfssect.c')
-rw-r--r--win/ntfssect.c355
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;
+ }
+