Home | History | Annotate | Download | only in win
      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 */
     33 static DWORD NtfsSectGetVolumeHandle(
     34     CHAR * VolumeName,
     35     S_NTFSSECT_VOLINFO * VolumeInfo
     36   );
     37 static DWORD NtfsSectGetVolumePartitionLba(S_NTFSSECT_VOLINFO * VolumeInfo);
     38 
     39 /*** Objects */
     40 CHAR * NtfsSectLastErrorMessage;
     41 
     42 /*** Function definitions */
     43 DWORD 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 */
     93 static 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 
    138 DWORD 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 
    193 DWORD 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 */
    232 static 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 
    272 DWORD 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 
    297 DWORD 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 
    343 VOID 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