syslinux/win/ntfssect.c
<<
>>
Prefs
   1/* -------------------------------------------------------------------------- *
   2 *
   3 *   Copyright 2011 Shao Miller - All Rights Reserved
   4 *
   5 *   This program is free software; you can redistribute it and/or modify
   6 *   it under the terms of the GNU General Public License as published by
   7 *   the Free Software Foundation, Inc., 53 Temple Place Ste 330,
   8 *   Boston MA 02111-1307, USA; either version 2 of the License, or
   9 *   (at your option) any later version; incorporated herein by reference.
  10 *
  11 * ------------------------------------------------------------------------- */
  12
  13/****
  14 * ntfssect.c
  15 *
  16 * Fetch NTFS file cluster & sector information via Windows
  17 *
  18 * With special thanks to Mark Roddy for his article:
  19 *   http://www.wd-3.com/archive/luserland.htm
  20 */
  21
  22#include <windows.h>
  23#include <winioctl.h>
  24#include <stddef.h>
  25#include <string.h>
  26
  27#include "ntfssect.h"
  28
  29/*** Macros */
  30#define M_ERR(msg) (NtfsSectLastErrorMessage = (msg))
  31
  32/*** Function declarations */
  33static DWORD NtfsSectGetVolumeHandle(
  34    CHAR * VolumeName,
  35    S_NTFSSECT_VOLINFO * VolumeInfo
  36  );
  37static DWORD NtfsSectGetVolumePartitionLba(S_NTFSSECT_VOLINFO * VolumeInfo);
  38
  39/*** Objects */
  40CHAR * NtfsSectLastErrorMessage;
  41
  42/*** Function definitions */
  43DWORD M_NTFSSECT_API NtfsSectGetFileVcnExtent(
  44    HANDLE File,
  45    LARGE_INTEGER * Vcn,
  46    S_NTFSSECT_EXTENT * Extent
  47  ) {
  48    BOOL bad, ok;
  49    DWORD output_size, rc;
  50    STARTING_VCN_INPUT_BUFFER input;
  51    RETRIEVAL_POINTERS_BUFFER output;
  52
  53    bad = (
  54        File == INVALID_HANDLE_VALUE ||
  55        !Vcn ||
  56        Vcn->QuadPart < 0 ||
  57        !Extent
  58      );
  59    if (bad)
  60      return ERROR_INVALID_PARAMETER;
  61
  62    input.StartingVcn = *Vcn;
  63    ok = DeviceIoControl(
  64        File,
  65        FSCTL_GET_RETRIEVAL_POINTERS,
  66        &input,
  67        sizeof input,
  68        &output,
  69        sizeof output,
  70        &output_size,
  71        NULL
  72      );
  73    rc = GetLastError();
  74    switch (rc) {
  75        case NO_ERROR:
  76        case ERROR_MORE_DATA:
  77          Extent->FirstVcn = output.StartingVcn;
  78          Extent->NextVcn = output.Extents[0].NextVcn;
  79          Extent->FirstLcn = output.Extents[0].Lcn;
  80          return ERROR_SUCCESS;
  81
  82        case ERROR_HANDLE_EOF:
  83          break;
  84
  85        default:
  86          M_ERR("NtfsSectGetFileVcnExtent(): Unknown status!");
  87      }
  88
  89    return rc;
  90  }
  91
  92/* Internal use only */
  93static DWORD NtfsSectGetVolumeHandle(
  94    CHAR * VolumeName,
  95    S_NTFSSECT_VOLINFO * VolumeInfo
  96  ) {
  97    #define M_VOL_PREFIX "\\\\.\\"
  98    CHAR volname[sizeof M_VOL_PREFIX - 1 + MAX_PATH + 1] = M_VOL_PREFIX;
  99    CHAR * const volname_short = volname + sizeof M_VOL_PREFIX - 1;
 100    CHAR * c;
 101    DWORD rc;
 102
 103    /* Prefix "\\.\" onto the passed volume name */
 104    strcpy(volname + sizeof M_VOL_PREFIX - 1, VolumeName);
 105
 106    /* Find the last non-null character */
 107    for (c = volname_short; *c; ++c)
 108      ;
 109
 110    /* Remove trailing back-slash */
 111    if (c[-1] == '\\')
 112      c[-1] = 0;
 113
 114    /* Open the volume */
 115    VolumeInfo->Handle = CreateFile(
 116        volname,
 117        GENERIC_READ,
 118        FILE_SHARE_READ | FILE_SHARE_WRITE,
 119        NULL,
 120        OPEN_EXISTING,
 121        0,
 122        NULL
 123      );
 124    rc = GetLastError();
 125    if (VolumeInfo->Handle == INVALID_HANDLE_VALUE) {
 126        M_ERR("Unable to open volume handle!");
 127        goto err_handle;
 128      }
 129
 130    return ERROR_SUCCESS;
 131
 132    CloseHandle(VolumeInfo->Handle);
 133    err_handle:
 134
 135    return rc;
 136  }
 137
 138DWORD M_NTFSSECT_API NtfsSectGetVolumeInfo(
 139    CHAR * VolumeName,
 140    S_NTFSSECT_VOLINFO * VolumeInfo
 141  ) {
 142    S_NTFSSECT_XPFUNCS xp_funcs;
 143    DWORD rc, free_clusts, total_clusts;
 144    BOOL ok;
 145
 146    if (!VolumeName || !VolumeInfo)
 147      return ERROR_INVALID_PARAMETER;
 148
 149    rc = NtfsSectGetVolumeHandle(VolumeName, VolumeInfo);
 150    if (rc != ERROR_SUCCESS)
 151      goto err_handle;
 152
 153    rc = NtfsSectLoadXpFuncs(&xp_funcs);
 154    if (rc != ERROR_SUCCESS)
 155      goto err_xp_funcs;
 156
 157    ok = xp_funcs.GetDiskFreeSpace(
 158        VolumeName,
 159        &VolumeInfo->SectorsPerCluster,
 160        &VolumeInfo->BytesPerSector,
 161        &free_clusts,
 162        &total_clusts
 163      );
 164    rc = GetLastError();
 165    if (!ok) {
 166        M_ERR("GetDiskFreeSpace() failed!");
 167        goto err_freespace;
 168      }
 169
 170    rc = NtfsSectGetVolumePartitionLba(VolumeInfo);
 171    if (rc != ERROR_SUCCESS)
 172      goto err_lba;
 173
 174    VolumeInfo->Size = sizeof *VolumeInfo;
 175    rc = ERROR_SUCCESS;
 176
 177    err_lba:
 178
 179    err_freespace:
 180
 181    NtfsSectUnloadXpFuncs(&xp_funcs);
 182    err_xp_funcs:
 183
 184    if (rc != ERROR_SUCCESS) {
 185        CloseHandle(VolumeInfo->Handle);
 186        VolumeInfo->Handle = INVALID_HANDLE_VALUE;
 187      }
 188    err_handle:
 189
 190    return rc;
 191  }
 192
 193DWORD M_NTFSSECT_API NtfsSectGetVolumeInfoFromFileName(
 194    CHAR * FileName,
 195    S_NTFSSECT_VOLINFO * VolumeInfo
 196  ) {
 197    S_NTFSSECT_XPFUNCS xp_funcs;
 198    DWORD rc;
 199    CHAR volname[MAX_PATH + 1];
 200    BOOL ok;
 201
 202    if (!FileName || !VolumeInfo)
 203      return ERROR_INVALID_PARAMETER;
 204
 205    rc = NtfsSectLoadXpFuncs(&xp_funcs);
 206    if (rc != ERROR_SUCCESS) {
 207        goto err_xp_funcs;
 208      }
 209
 210    ok = xp_funcs.GetVolumePathName(
 211        FileName,
 212        volname,
 213        sizeof volname
 214      );
 215    rc = GetLastError();
 216    if (!ok) {
 217        M_ERR("GetVolumePathName() failed!");
 218        goto err_volname;
 219      }
 220
 221    rc = NtfsSectGetVolumeInfo(volname, VolumeInfo);
 222
 223    err_volname:
 224
 225    NtfsSectUnloadXpFuncs(&xp_funcs);
 226    err_xp_funcs:
 227
 228    return rc;
 229  }
 230
 231/* Internal use only */
 232static DWORD NtfsSectGetVolumePartitionLba(S_NTFSSECT_VOLINFO * VolumeInfo) {
 233    BOOL ok;
 234    VOLUME_DISK_EXTENTS vol_disk_extents;
 235    DWORD output_size, rc;
 236
 237    ok = DeviceIoControl(
 238        VolumeInfo->Handle,
 239        IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS,
 240        NULL,
 241        0,
 242        &vol_disk_extents,
 243        sizeof vol_disk_extents,
 244        &output_size,
 245        NULL
 246      );
 247    rc = GetLastError();
 248    if (!ok) {
 249        M_ERR("Couldn't fetch volume disk extent(s)!");
 250        goto err_vol_disk_extents;
 251      }
 252
 253    if (vol_disk_extents.NumberOfDiskExtents != 1) {
 254        M_ERR("Unsupported number of volume disk extents!");
 255        goto err_num_of_extents;
 256      }
 257
 258    VolumeInfo->PartitionLba.QuadPart = (
 259        vol_disk_extents.Extents[0].StartingOffset.QuadPart /
 260        VolumeInfo->BytesPerSector
 261      );
 262
 263    return ERROR_SUCCESS;
 264
 265    err_num_of_extents:
 266
 267    err_vol_disk_extents:
 268
 269    return rc;
 270  }
 271
 272DWORD M_NTFSSECT_API NtfsSectLcnToLba(
 273    const S_NTFSSECT_VOLINFO * VolumeInfo,
 274    const LARGE_INTEGER * Lcn,
 275    LARGE_INTEGER * Lba
 276  ) {
 277    BOOL bad;
 278    bad = (
 279        !VolumeInfo ||
 280        !VolumeInfo->BytesPerSector ||
 281        !VolumeInfo->SectorsPerCluster ||
 282        !Lcn ||
 283        Lcn->QuadPart < 0 ||
 284        !Lba
 285      );
 286    if (bad)
 287      return ERROR_INVALID_PARAMETER;
 288
 289    Lba->QuadPart = (
 290        VolumeInfo->PartitionLba.QuadPart +
 291        Lcn->QuadPart *
 292        VolumeInfo->SectorsPerCluster
 293      );
 294    return ERROR_SUCCESS;
 295  }
 296
 297DWORD M_NTFSSECT_API NtfsSectLoadXpFuncs(S_NTFSSECT_XPFUNCS * XpFuncs) {
 298    DWORD rc;
 299
 300    if (!XpFuncs)
 301      return ERROR_INVALID_PARAMETER;
 302
 303    XpFuncs->Size = sizeof *XpFuncs;
 304
 305    XpFuncs->Kernel32 = LoadLibrary("kernel32.dll");
 306    rc = GetLastError();
 307    if (!XpFuncs->Kernel32) {
 308        M_ERR("KERNEL32.DLL not found!");
 309        goto err;
 310      }
 311
 312    XpFuncs->GetVolumePathName = (F_KERNEL32_GETVOLUMEPATHNAME *) (
 313        GetProcAddress(
 314            XpFuncs->Kernel32,
 315            "GetVolumePathNameA"
 316          )
 317      );
 318    rc = GetLastError();
 319    if (!XpFuncs->GetVolumePathName) {
 320        M_ERR("GetVolumePathName() not found in KERNEL32.DLL!");
 321        goto err;
 322      }
 323
 324    XpFuncs->GetDiskFreeSpace = (F_KERNEL32_GETDISKFREESPACE *) (
 325        GetProcAddress(
 326            XpFuncs->Kernel32,
 327            "GetDiskFreeSpaceA"
 328          )
 329      );
 330    rc = GetLastError();
 331    if (!XpFuncs->GetDiskFreeSpace) {
 332        M_ERR("GetDiskFreeSpace() not found in KERNEL32.DLL!");
 333        goto err;
 334      }
 335
 336    return ERROR_SUCCESS;
 337
 338    err:
 339    NtfsSectUnloadXpFuncs(XpFuncs);
 340    return rc;
 341  }
 342
 343VOID M_NTFSSECT_API NtfsSectUnloadXpFuncs(S_NTFSSECT_XPFUNCS * XpFuncs) {
 344    if (!XpFuncs)
 345      return;
 346
 347    XpFuncs->GetDiskFreeSpace = NULL;
 348    XpFuncs->GetVolumePathName = NULL;
 349    if (XpFuncs->Kernel32)
 350      FreeLibrary(XpFuncs->Kernel32);
 351    XpFuncs->Kernel32 = NULL;
 352    XpFuncs->Size = 0;
 353    return;
 354  }
 355
 356