root/maint/gnulib/lib/stat-w32.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. initialize
  2. _gl_convert_FILETIME_to_timespec
  3. _gl_convert_FILETIME_to_POSIX
  4. _gl_fstat_by_handle

   1 /* Core of implementation of fstat and stat for native Windows.
   2    Copyright (C) 2017-2021 Free Software Foundation, Inc.
   3 
   4    This file is free software: you can redistribute it and/or modify
   5    it under the terms of the GNU Lesser General Public License as
   6    published by the Free Software Foundation; either version 2.1 of the
   7    License, or (at your option) any later version.
   8 
   9    This file is distributed in the hope that it will be useful,
  10    but WITHOUT ANY WARRANTY; without even the implied warranty of
  11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12    GNU Lesser General Public License for more details.
  13 
  14    You should have received a copy of the GNU Lesser General Public License
  15    along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
  16 
  17 /* Written by Bruno Haible.  */
  18 
  19 #include <config.h>
  20 
  21 #if defined _WIN32 && ! defined __CYGWIN__
  22 
  23 /* Attempt to make <windows.h> define FILE_ID_INFO.
  24    But ensure that the redefinition of _WIN32_WINNT does not make us assume
  25    Windows Vista or newer when building for an older version of Windows.  */
  26 #if HAVE_SDKDDKVER_H
  27 # include <sdkddkver.h>
  28 # if _WIN32_WINNT >= _WIN32_WINNT_VISTA
  29 #  define WIN32_ASSUME_VISTA 1
  30 # else
  31 #  define WIN32_ASSUME_VISTA 0
  32 # endif
  33 # if !defined _WIN32_WINNT || (_WIN32_WINNT < _WIN32_WINNT_WIN8)
  34 #  undef _WIN32_WINNT
  35 #  define _WIN32_WINNT _WIN32_WINNT_WIN8
  36 # endif
  37 #else
  38 # define WIN32_ASSUME_VISTA (_WIN32_WINNT >= _WIN32_WINNT_VISTA)
  39 #endif
  40 
  41 #include <sys/types.h>
  42 #include <sys/stat.h>
  43 #include <errno.h>
  44 #include <limits.h>
  45 #include <string.h>
  46 #include <unistd.h>
  47 #include <windows.h>
  48 
  49 /* Specification.  */
  50 #include "stat-w32.h"
  51 
  52 #include "pathmax.h"
  53 #include "verify.h"
  54 
  55 /* Don't assume that UNICODE is not defined.  */
  56 #undef LoadLibrary
  57 #define LoadLibrary LoadLibraryA
  58 #undef GetFinalPathNameByHandle
  59 #define GetFinalPathNameByHandle GetFinalPathNameByHandleA
  60 
  61 /* Older mingw headers do not define VOLUME_NAME_NONE.  */
  62 #ifndef VOLUME_NAME_NONE
  63 # define VOLUME_NAME_NONE 4
  64 #endif
  65 
  66 #if !WIN32_ASSUME_VISTA
  67 
  68 /* Avoid warnings from gcc -Wcast-function-type.  */
  69 # define GetProcAddress \
  70    (void *) GetProcAddress
  71 
  72 # if _GL_WINDOWS_STAT_INODES == 2
  73 /* GetFileInformationByHandleEx was introduced only in Windows Vista.  */
  74 typedef DWORD (WINAPI * GetFileInformationByHandleExFuncType) (HANDLE hFile,
  75                                                                FILE_INFO_BY_HANDLE_CLASS fiClass,
  76                                                                LPVOID lpBuffer,
  77                                                                DWORD dwBufferSize);
  78 static GetFileInformationByHandleExFuncType GetFileInformationByHandleExFunc = NULL;
  79 # endif
  80 /* GetFinalPathNameByHandle was introduced only in Windows Vista.  */
  81 typedef DWORD (WINAPI * GetFinalPathNameByHandleFuncType) (HANDLE hFile,
  82                                                            LPSTR lpFilePath,
  83                                                            DWORD lenFilePath,
  84                                                            DWORD dwFlags);
  85 static GetFinalPathNameByHandleFuncType GetFinalPathNameByHandleFunc = NULL;
  86 static BOOL initialized = FALSE;
  87 
  88 static void
  89 initialize (void)
     /* [previous][next][first][last][top][bottom][index][help] */
  90 {
  91   HMODULE kernel32 = LoadLibrary ("kernel32.dll");
  92   if (kernel32 != NULL)
  93     {
  94 # if _GL_WINDOWS_STAT_INODES == 2
  95       GetFileInformationByHandleExFunc =
  96         (GetFileInformationByHandleExFuncType) GetProcAddress (kernel32, "GetFileInformationByHandleEx");
  97 # endif
  98       GetFinalPathNameByHandleFunc =
  99         (GetFinalPathNameByHandleFuncType) GetProcAddress (kernel32, "GetFinalPathNameByHandleA");
 100     }
 101   initialized = TRUE;
 102 }
 103 
 104 #else
 105 
 106 # define GetFileInformationByHandleExFunc GetFileInformationByHandleEx
 107 # define GetFinalPathNameByHandleFunc GetFinalPathNameByHandle
 108 
 109 #endif
 110 
 111 /* Converts a FILETIME to GMT time since 1970-01-01 00:00:00.  */
 112 #if _GL_WINDOWS_STAT_TIMESPEC
 113 struct timespec
 114 _gl_convert_FILETIME_to_timespec (const FILETIME *ft)
     /* [previous][next][first][last][top][bottom][index][help] */
 115 {
 116   struct timespec result;
 117   /* FILETIME: <https://docs.microsoft.com/en-us/windows/desktop/api/minwinbase/ns-minwinbase-filetime> */
 118   unsigned long long since_1601 =
 119     ((unsigned long long) ft->dwHighDateTime << 32)
 120     | (unsigned long long) ft->dwLowDateTime;
 121   if (since_1601 == 0)
 122     {
 123       result.tv_sec = 0;
 124       result.tv_nsec = 0;
 125     }
 126   else
 127     {
 128       /* Between 1601-01-01 and 1970-01-01 there were 280 normal years and 89
 129          leap years, in total 134774 days.  */
 130       unsigned long long since_1970 =
 131         since_1601 - (unsigned long long) 134774 * (unsigned long long) 86400 * (unsigned long long) 10000000;
 132       result.tv_sec = since_1970 / (unsigned long long) 10000000;
 133       result.tv_nsec = (unsigned long) (since_1970 % (unsigned long long) 10000000) * 100;
 134     }
 135   return result;
 136 }
 137 #else
 138 time_t
 139 _gl_convert_FILETIME_to_POSIX (const FILETIME *ft)
     /* [previous][next][first][last][top][bottom][index][help] */
 140 {
 141   /* FILETIME: <https://docs.microsoft.com/en-us/windows/desktop/api/minwinbase/ns-minwinbase-filetime> */
 142   unsigned long long since_1601 =
 143     ((unsigned long long) ft->dwHighDateTime << 32)
 144     | (unsigned long long) ft->dwLowDateTime;
 145   if (since_1601 == 0)
 146     return 0;
 147   else
 148     {
 149       /* Between 1601-01-01 and 1970-01-01 there were 280 normal years and 89
 150          leap years, in total 134774 days.  */
 151       unsigned long long since_1970 =
 152         since_1601 - (unsigned long long) 134774 * (unsigned long long) 86400 * (unsigned long long) 10000000;
 153       return since_1970 / (unsigned long long) 10000000;
 154     }
 155 }
 156 #endif
 157 
 158 /* Fill *BUF with information about the file designated by H.
 159    PATH is the file name, if known, otherwise NULL.
 160    Return 0 if successful, or -1 with errno set upon failure.  */
 161 int
 162 _gl_fstat_by_handle (HANDLE h, const char *path, struct stat *buf)
     /* [previous][next][first][last][top][bottom][index][help] */
 163 {
 164   /* GetFileType
 165      <https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-getfiletype> */
 166   DWORD type = GetFileType (h);
 167   if (type == FILE_TYPE_DISK)
 168     {
 169 #if !WIN32_ASSUME_VISTA
 170       if (!initialized)
 171         initialize ();
 172 #endif
 173 
 174       /* st_mode can be determined through
 175          GetFileAttributesEx
 176          <https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-getfileattributesexa>
 177          <https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/ns-fileapi-_win32_file_attribute_data>
 178          or through
 179          GetFileInformationByHandle
 180          <https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-getfileinformationbyhandle>
 181          <https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/ns-fileapi-_by_handle_file_information>
 182          or through
 183          GetFileInformationByHandleEx with argument FileBasicInfo
 184          <https://docs.microsoft.com/en-us/windows/desktop/api/winbase/nf-winbase-getfileinformationbyhandleex>
 185          <https://docs.microsoft.com/en-us/windows/desktop/api/winbase/ns-winbase-_file_basic_info>
 186          The latter requires -D_WIN32_WINNT=_WIN32_WINNT_VISTA or higher.  */
 187       BY_HANDLE_FILE_INFORMATION info;
 188       if (! GetFileInformationByHandle (h, &info))
 189         goto failed;
 190 
 191       /* Test for error conditions before starting to fill *buf.  */
 192       if (sizeof (buf->st_size) <= 4 && info.nFileSizeHigh > 0)
 193         {
 194           errno = EOVERFLOW;
 195           return -1;
 196         }
 197 
 198 #if _GL_WINDOWS_STAT_INODES
 199       /* st_ino can be determined through
 200          GetFileInformationByHandle
 201          <https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-getfileinformationbyhandle>
 202          <https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/ns-fileapi-_by_handle_file_information>
 203          as 64 bits, or through
 204          GetFileInformationByHandleEx with argument FileIdInfo
 205          <https://docs.microsoft.com/en-us/windows/desktop/api/winbase/nf-winbase-getfileinformationbyhandleex>
 206          <https://docs.microsoft.com/en-us/windows/desktop/api/winbase/ns-winbase-_file_id_info>
 207          as 128 bits.
 208          The latter requires -D_WIN32_WINNT=_WIN32_WINNT_WIN8 or higher.  */
 209       /* Experiments show that GetFileInformationByHandleEx does not provide
 210          much more information than GetFileInformationByHandle:
 211            * The dwVolumeSerialNumber from GetFileInformationByHandle is equal
 212              to the low 32 bits of the 64-bit VolumeSerialNumber from
 213              GetFileInformationByHandleEx, and is apparently sufficient for
 214              identifying the device.
 215            * The nFileIndex from GetFileInformationByHandle is equal to the low
 216              64 bits of the 128-bit FileId from GetFileInformationByHandleEx,
 217              and the high 64 bits of this 128-bit FileId are zero.
 218            * On a FAT file system, GetFileInformationByHandleEx fails with error
 219              ERROR_INVALID_PARAMETER, whereas GetFileInformationByHandle
 220              succeeds.
 221            * On a CIFS/SMB file system, GetFileInformationByHandleEx fails with
 222              error ERROR_INVALID_LEVEL, whereas GetFileInformationByHandle
 223              succeeds.  */
 224 # if _GL_WINDOWS_STAT_INODES == 2
 225       if (GetFileInformationByHandleExFunc != NULL)
 226         {
 227           FILE_ID_INFO id;
 228           if (GetFileInformationByHandleExFunc (h, FileIdInfo, &id, sizeof (id)))
 229             {
 230               buf->st_dev = id.VolumeSerialNumber;
 231               verify (sizeof (ino_t) == sizeof (id.FileId));
 232               memcpy (&buf->st_ino, &id.FileId, sizeof (ino_t));
 233               goto ino_done;
 234             }
 235           else
 236             {
 237               switch (GetLastError ())
 238                 {
 239                 case ERROR_INVALID_PARAMETER: /* older Windows version, or FAT */
 240                 case ERROR_INVALID_LEVEL: /* CIFS/SMB file system */
 241                   goto fallback;
 242                 default:
 243                   goto failed;
 244                 }
 245             }
 246         }
 247      fallback: ;
 248       /* Fallback for older Windows versions.  */
 249       buf->st_dev = info.dwVolumeSerialNumber;
 250       buf->st_ino._gl_ino[0] = ((ULONGLONG) info.nFileIndexHigh << 32) | (ULONGLONG) info.nFileIndexLow;
 251       buf->st_ino._gl_ino[1] = 0;
 252      ino_done: ;
 253 # else /* _GL_WINDOWS_STAT_INODES == 1 */
 254       buf->st_dev = info.dwVolumeSerialNumber;
 255       buf->st_ino = ((ULONGLONG) info.nFileIndexHigh << 32) | (ULONGLONG) info.nFileIndexLow;
 256 # endif
 257 #else
 258       /* st_ino is not wide enough for identifying a file on a device.
 259          Without st_ino, st_dev is pointless.  */
 260       buf->st_dev = 0;
 261       buf->st_ino = 0;
 262 #endif
 263 
 264       /* st_mode.  */
 265       unsigned int mode =
 266         /* XXX How to handle FILE_ATTRIBUTE_REPARSE_POINT ?  */
 267         ((info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? _S_IFDIR | S_IEXEC_UGO : _S_IFREG)
 268         | S_IREAD_UGO
 269         | ((info.dwFileAttributes & FILE_ATTRIBUTE_READONLY) ? 0 : S_IWRITE_UGO);
 270       if (!(info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
 271         {
 272           /* Determine whether the file is executable by looking at the file
 273              name suffix.
 274              If the file name is already known, use it. Otherwise, for
 275              non-empty files, it can be determined through
 276              GetFinalPathNameByHandle
 277              <https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-getfinalpathnamebyhandlea>
 278              or through
 279              GetFileInformationByHandleEx with argument FileNameInfo
 280              <https://docs.microsoft.com/en-us/windows/desktop/api/winbase/nf-winbase-getfileinformationbyhandleex>
 281              <https://docs.microsoft.com/en-us/windows/desktop/api/winbase/ns-winbase-_file_name_info>
 282              Both require -D_WIN32_WINNT=_WIN32_WINNT_VISTA or higher.  */
 283           if (info.nFileSizeHigh > 0 || info.nFileSizeLow > 0)
 284             {
 285               char fpath[PATH_MAX];
 286               if (path != NULL
 287                   || (GetFinalPathNameByHandleFunc != NULL
 288                       && GetFinalPathNameByHandleFunc (h, fpath, sizeof (fpath), VOLUME_NAME_NONE)
 289                          < sizeof (fpath)
 290                       && (path = fpath, 1)))
 291                 {
 292                   const char *last_dot = NULL;
 293                   const char *p;
 294                   for (p = path; *p != '\0'; p++)
 295                     if (*p == '.')
 296                       last_dot = p;
 297                   if (last_dot != NULL)
 298                     {
 299                       const char *suffix = last_dot + 1;
 300                       if (_stricmp (suffix, "exe") == 0
 301                           || _stricmp (suffix, "bat") == 0
 302                           || _stricmp (suffix, "cmd") == 0
 303                           || _stricmp (suffix, "com") == 0)
 304                         mode |= S_IEXEC_UGO;
 305                     }
 306                 }
 307               else
 308                 /* Cannot determine file name.  Pretend that it is executable.  */
 309                 mode |= S_IEXEC_UGO;
 310             }
 311         }
 312       buf->st_mode = mode;
 313 
 314       /* st_nlink can be determined through
 315          GetFileInformationByHandle
 316          <https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-getfileinformationbyhandle>
 317          <https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/ns-fileapi-_by_handle_file_information>
 318          or through
 319          GetFileInformationByHandleEx with argument FileStandardInfo
 320          <https://docs.microsoft.com/en-us/windows/desktop/api/winbase/nf-winbase-getfileinformationbyhandleex>
 321          <https://docs.microsoft.com/en-us/windows/desktop/api/winbase/ns-winbase-_file_standard_info>
 322          The latter requires -D_WIN32_WINNT=_WIN32_WINNT_VISTA or higher.  */
 323       buf->st_nlink = (info.nNumberOfLinks > SHRT_MAX ? SHRT_MAX : info.nNumberOfLinks);
 324 
 325       /* There's no easy way to map the Windows SID concept to an integer.  */
 326       buf->st_uid = 0;
 327       buf->st_gid = 0;
 328 
 329       /* st_rdev is irrelevant for normal files and directories.  */
 330       buf->st_rdev = 0;
 331 
 332       /* st_size can be determined through
 333          GetFileSizeEx
 334          <https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-getfilesizeex>
 335          or through
 336          GetFileAttributesEx
 337          <https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-getfileattributesexa>
 338          <https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/ns-fileapi-_win32_file_attribute_data>
 339          or through
 340          GetFileInformationByHandle
 341          <https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-getfileinformationbyhandle>
 342          <https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/ns-fileapi-_by_handle_file_information>
 343          or through
 344          GetFileInformationByHandleEx with argument FileStandardInfo
 345          <https://docs.microsoft.com/en-us/windows/desktop/api/winbase/nf-winbase-getfileinformationbyhandleex>
 346          <https://docs.microsoft.com/en-us/windows/desktop/api/winbase/ns-winbase-_file_standard_info>
 347          The latter requires -D_WIN32_WINNT=_WIN32_WINNT_VISTA or higher.  */
 348       if (sizeof (buf->st_size) <= 4)
 349         /* Range check already done above.  */
 350         buf->st_size = info.nFileSizeLow;
 351       else
 352         buf->st_size = ((long long) info.nFileSizeHigh << 32) | (long long) info.nFileSizeLow;
 353 
 354       /* st_atime, st_mtime, st_ctime can be determined through
 355          GetFileTime
 356          <https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-getfiletime>
 357          or through
 358          GetFileAttributesEx
 359          <https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-getfileattributesexa>
 360          <https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/ns-fileapi-_win32_file_attribute_data>
 361          or through
 362          GetFileInformationByHandle
 363          <https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-getfileinformationbyhandle>
 364          <https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/ns-fileapi-_by_handle_file_information>
 365          or through
 366          GetFileInformationByHandleEx with argument FileBasicInfo
 367          <https://docs.microsoft.com/en-us/windows/desktop/api/winbase/nf-winbase-getfileinformationbyhandleex>
 368          <https://docs.microsoft.com/en-us/windows/desktop/api/winbase/ns-winbase-_file_basic_info>
 369          The latter requires -D_WIN32_WINNT=_WIN32_WINNT_VISTA or higher.  */
 370 #if _GL_WINDOWS_STAT_TIMESPEC
 371       buf->st_atim = _gl_convert_FILETIME_to_timespec (&info.ftLastAccessTime);
 372       buf->st_mtim = _gl_convert_FILETIME_to_timespec (&info.ftLastWriteTime);
 373       buf->st_ctim = _gl_convert_FILETIME_to_timespec (&info.ftCreationTime);
 374 #else
 375       buf->st_atime = _gl_convert_FILETIME_to_POSIX (&info.ftLastAccessTime);
 376       buf->st_mtime = _gl_convert_FILETIME_to_POSIX (&info.ftLastWriteTime);
 377       buf->st_ctime = _gl_convert_FILETIME_to_POSIX (&info.ftCreationTime);
 378 #endif
 379 
 380       return 0;
 381     }
 382   else if (type == FILE_TYPE_CHAR || type == FILE_TYPE_PIPE)
 383     {
 384       buf->st_dev = 0;
 385 #if _GL_WINDOWS_STAT_INODES == 2
 386       buf->st_ino._gl_ino[0] = buf->st_ino._gl_ino[1] = 0;
 387 #else
 388       buf->st_ino = 0;
 389 #endif
 390       buf->st_mode = (type == FILE_TYPE_PIPE ? _S_IFIFO : _S_IFCHR);
 391       buf->st_nlink = 1;
 392       buf->st_uid = 0;
 393       buf->st_gid = 0;
 394       buf->st_rdev = 0;
 395       if (type == FILE_TYPE_PIPE)
 396         {
 397           /* PeekNamedPipe
 398              <https://msdn.microsoft.com/en-us/library/aa365779.aspx> */
 399           DWORD bytes_available;
 400           if (PeekNamedPipe (h, NULL, 0, NULL, &bytes_available, NULL))
 401             buf->st_size = bytes_available;
 402           else
 403             buf->st_size = 0;
 404         }
 405       else
 406         buf->st_size = 0;
 407 #if _GL_WINDOWS_STAT_TIMESPEC
 408       buf->st_atim.tv_sec = 0; buf->st_atim.tv_nsec = 0;
 409       buf->st_mtim.tv_sec = 0; buf->st_mtim.tv_nsec = 0;
 410       buf->st_ctim.tv_sec = 0; buf->st_ctim.tv_nsec = 0;
 411 #else
 412       buf->st_atime = 0;
 413       buf->st_mtime = 0;
 414       buf->st_ctime = 0;
 415 #endif
 416       return 0;
 417     }
 418   else
 419     {
 420       errno = ENOENT;
 421       return -1;
 422     }
 423 
 424  failed:
 425   {
 426     DWORD error = GetLastError ();
 427     #if 0
 428     fprintf (stderr, "_gl_fstat_by_handle error 0x%x\n", (unsigned int) error);
 429     #endif
 430     switch (error)
 431       {
 432       case ERROR_ACCESS_DENIED:
 433       case ERROR_SHARING_VIOLATION:
 434         errno = EACCES;
 435         break;
 436 
 437       case ERROR_OUTOFMEMORY:
 438         errno = ENOMEM;
 439         break;
 440 
 441       case ERROR_WRITE_FAULT:
 442       case ERROR_READ_FAULT:
 443       case ERROR_GEN_FAILURE:
 444         errno = EIO;
 445         break;
 446 
 447       default:
 448         errno = EINVAL;
 449         break;
 450       }
 451     return -1;
 452   }
 453 }
 454 
 455 #else
 456 
 457 /* This declaration is solely to ensure that after preprocessing
 458    this file is never empty.  */
 459 typedef int dummy;
 460 
 461 #endif

/* [previous][next][first][last][top][bottom][index][help] */