Blame gnulib/tests/stat-w32.c

Packit Service a2ae7a
/* Core of implementation of fstat and stat for native Windows.
Packit Service a2ae7a
   Copyright (C) 2017-2019 Free Software Foundation, Inc.
Packit Service a2ae7a
Packit Service a2ae7a
   This program is free software: you can redistribute it and/or modify
Packit Service a2ae7a
   it under the terms of the GNU General Public License as published by
Packit Service a2ae7a
   the Free Software Foundation; either version 3 of the License, or
Packit Service a2ae7a
   (at your option) any later version.
Packit Service a2ae7a
Packit Service a2ae7a
   This program is distributed in the hope that it will be useful,
Packit Service a2ae7a
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service a2ae7a
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit Service a2ae7a
   GNU General Public License for more details.
Packit Service a2ae7a
Packit Service a2ae7a
   You should have received a copy of the GNU General Public License
Packit Service a2ae7a
   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
Packit Service a2ae7a
Packit Service a2ae7a
/* Written by Bruno Haible.  */
Packit Service a2ae7a
Packit Service a2ae7a
#include <config.h>
Packit Service a2ae7a
Packit Service a2ae7a
#if defined _WIN32 && ! defined __CYGWIN__
Packit Service a2ae7a
Packit Service a2ae7a
/* Ensure that <windows.h> defines FILE_ID_INFO.  */
Packit Service a2ae7a
#undef _WIN32_WINNT
Packit Service a2ae7a
#define _WIN32_WINNT _WIN32_WINNT_WIN8
Packit Service a2ae7a
Packit Service a2ae7a
#include <sys/types.h>
Packit Service a2ae7a
#include <sys/stat.h>
Packit Service a2ae7a
#include <errno.h>
Packit Service a2ae7a
#include <limits.h>
Packit Service a2ae7a
#include <string.h>
Packit Service a2ae7a
#include <unistd.h>
Packit Service a2ae7a
#include <windows.h>
Packit Service a2ae7a
Packit Service a2ae7a
/* Specification.  */
Packit Service a2ae7a
#include "stat-w32.h"
Packit Service a2ae7a
Packit Service a2ae7a
#include "pathmax.h"
Packit Service a2ae7a
#include "verify.h"
Packit Service a2ae7a
Packit Service a2ae7a
/* Avoid warnings from gcc -Wcast-function-type.  */
Packit Service a2ae7a
#define GetProcAddress \
Packit Service a2ae7a
  (void *) GetProcAddress
Packit Service a2ae7a
Packit Service a2ae7a
#if _GL_WINDOWS_STAT_INODES == 2
Packit Service a2ae7a
/* GetFileInformationByHandleEx was introduced only in Windows Vista.  */
Packit Service a2ae7a
typedef DWORD (WINAPI * GetFileInformationByHandleExFuncType) (HANDLE hFile,
Packit Service a2ae7a
                                                               FILE_INFO_BY_HANDLE_CLASS fiClass,
Packit Service a2ae7a
                                                               LPVOID lpBuffer,
Packit Service a2ae7a
                                                               DWORD dwBufferSize);
Packit Service a2ae7a
static GetFileInformationByHandleExFuncType GetFileInformationByHandleExFunc = NULL;
Packit Service a2ae7a
#endif
Packit Service a2ae7a
/* GetFinalPathNameByHandle was introduced only in Windows Vista.  */
Packit Service a2ae7a
typedef DWORD (WINAPI * GetFinalPathNameByHandleFuncType) (HANDLE hFile,
Packit Service a2ae7a
                                                           LPTSTR lpFilePath,
Packit Service a2ae7a
                                                           DWORD lenFilePath,
Packit Service a2ae7a
                                                           DWORD dwFlags);
Packit Service a2ae7a
static GetFinalPathNameByHandleFuncType GetFinalPathNameByHandleFunc = NULL;
Packit Service a2ae7a
static BOOL initialized = FALSE;
Packit Service a2ae7a
Packit Service a2ae7a
static void
Packit Service a2ae7a
initialize (void)
Packit Service a2ae7a
{
Packit Service a2ae7a
  HMODULE kernel32 = LoadLibrary ("kernel32.dll");
Packit Service a2ae7a
  if (kernel32 != NULL)
Packit Service a2ae7a
    {
Packit Service a2ae7a
#if _GL_WINDOWS_STAT_INODES == 2
Packit Service a2ae7a
      GetFileInformationByHandleExFunc =
Packit Service a2ae7a
        (GetFileInformationByHandleExFuncType) GetProcAddress (kernel32, "GetFileInformationByHandleEx");
Packit Service a2ae7a
#endif
Packit Service a2ae7a
      GetFinalPathNameByHandleFunc =
Packit Service a2ae7a
        (GetFinalPathNameByHandleFuncType) GetProcAddress (kernel32, "GetFinalPathNameByHandleA");
Packit Service a2ae7a
    }
Packit Service a2ae7a
  initialized = TRUE;
Packit Service a2ae7a
}
Packit Service a2ae7a
Packit Service a2ae7a
/* Converts a FILETIME to GMT time since 1970-01-01 00:00:00.  */
Packit Service a2ae7a
#if _GL_WINDOWS_STAT_TIMESPEC
Packit Service a2ae7a
struct timespec
Packit Service a2ae7a
_gl_convert_FILETIME_to_timespec (const FILETIME *ft)
Packit Service a2ae7a
{
Packit Service a2ae7a
  struct timespec result;
Packit Service a2ae7a
  /* FILETIME: <https://docs.microsoft.com/en-us/windows/desktop/api/minwinbase/ns-minwinbase-filetime> */
Packit Service a2ae7a
  unsigned long long since_1601 =
Packit Service a2ae7a
    ((unsigned long long) ft->dwHighDateTime << 32)
Packit Service a2ae7a
    | (unsigned long long) ft->dwLowDateTime;
Packit Service a2ae7a
  if (since_1601 == 0)
Packit Service a2ae7a
    {
Packit Service a2ae7a
      result.tv_sec = 0;
Packit Service a2ae7a
      result.tv_nsec = 0;
Packit Service a2ae7a
    }
Packit Service a2ae7a
  else
Packit Service a2ae7a
    {
Packit Service a2ae7a
      /* Between 1601-01-01 and 1970-01-01 there were 280 normal years and 89
Packit Service a2ae7a
         leap years, in total 134774 days.  */
Packit Service a2ae7a
      unsigned long long since_1970 =
Packit Service a2ae7a
        since_1601 - (unsigned long long) 134774 * (unsigned long long) 86400 * (unsigned long long) 10000000;
Packit Service a2ae7a
      result.tv_sec = since_1970 / (unsigned long long) 10000000;
Packit Service a2ae7a
      result.tv_nsec = (unsigned long) (since_1970 % (unsigned long long) 10000000) * 100;
Packit Service a2ae7a
    }
Packit Service a2ae7a
  return result;
Packit Service a2ae7a
}
Packit Service a2ae7a
#else
Packit Service a2ae7a
time_t
Packit Service a2ae7a
_gl_convert_FILETIME_to_POSIX (const FILETIME *ft)
Packit Service a2ae7a
{
Packit Service a2ae7a
  /* FILETIME: <https://docs.microsoft.com/en-us/windows/desktop/api/minwinbase/ns-minwinbase-filetime> */
Packit Service a2ae7a
  unsigned long long since_1601 =
Packit Service a2ae7a
    ((unsigned long long) ft->dwHighDateTime << 32)
Packit Service a2ae7a
    | (unsigned long long) ft->dwLowDateTime;
Packit Service a2ae7a
  if (since_1601 == 0)
Packit Service a2ae7a
    return 0;
Packit Service a2ae7a
  else
Packit Service a2ae7a
    {
Packit Service a2ae7a
      /* Between 1601-01-01 and 1970-01-01 there were 280 normal years and 89
Packit Service a2ae7a
         leap years, in total 134774 days.  */
Packit Service a2ae7a
      unsigned long long since_1970 =
Packit Service a2ae7a
        since_1601 - (unsigned long long) 134774 * (unsigned long long) 86400 * (unsigned long long) 10000000;
Packit Service a2ae7a
      return since_1970 / (unsigned long long) 10000000;
Packit Service a2ae7a
    }
Packit Service a2ae7a
}
Packit Service a2ae7a
#endif
Packit Service a2ae7a
Packit Service a2ae7a
/* Fill *BUF with information about the file designated by H.
Packit Service a2ae7a
   PATH is the file name, if known, otherwise NULL.
Packit Service a2ae7a
   Return 0 if successful, or -1 with errno set upon failure.  */
Packit Service a2ae7a
int
Packit Service a2ae7a
_gl_fstat_by_handle (HANDLE h, const char *path, struct stat *buf)
Packit Service a2ae7a
{
Packit Service a2ae7a
  /* GetFileType
Packit Service a2ae7a
     <https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-getfiletype> */
Packit Service a2ae7a
  DWORD type = GetFileType (h);
Packit Service a2ae7a
  if (type == FILE_TYPE_DISK)
Packit Service a2ae7a
    {
Packit Service a2ae7a
      if (!initialized)
Packit Service a2ae7a
        initialize ();
Packit Service a2ae7a
Packit Service a2ae7a
      /* st_mode can be determined through
Packit Service a2ae7a
         GetFileAttributesEx
Packit Service a2ae7a
         <https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-getfileattributesexa>
Packit Service a2ae7a
         <https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/ns-fileapi-_win32_file_attribute_data>
Packit Service a2ae7a
         or through
Packit Service a2ae7a
         GetFileInformationByHandle
Packit Service a2ae7a
         <https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-getfileinformationbyhandle>
Packit Service a2ae7a
         <https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/ns-fileapi-_by_handle_file_information>
Packit Service a2ae7a
         or through
Packit Service a2ae7a
         GetFileInformationByHandleEx with argument FileBasicInfo
Packit Service a2ae7a
         <https://docs.microsoft.com/en-us/windows/desktop/api/winbase/nf-winbase-getfileinformationbyhandleex>
Packit Service a2ae7a
         <https://docs.microsoft.com/en-us/windows/desktop/api/winbase/ns-winbase-_file_basic_info>
Packit Service a2ae7a
         The latter requires -D_WIN32_WINNT=_WIN32_WINNT_VISTA or higher.  */
Packit Service a2ae7a
      BY_HANDLE_FILE_INFORMATION info;
Packit Service a2ae7a
      if (! GetFileInformationByHandle (h, &info))
Packit Service a2ae7a
        goto failed;
Packit Service a2ae7a
Packit Service a2ae7a
      /* Test for error conditions before starting to fill *buf.  */
Packit Service a2ae7a
      if (sizeof (buf->st_size) <= 4 && info.nFileSizeHigh > 0)
Packit Service a2ae7a
        {
Packit Service a2ae7a
          errno = EOVERFLOW;
Packit Service a2ae7a
          return -1;
Packit Service a2ae7a
        }
Packit Service a2ae7a
Packit Service a2ae7a
#if _GL_WINDOWS_STAT_INODES
Packit Service a2ae7a
      /* st_ino can be determined through
Packit Service a2ae7a
         GetFileInformationByHandle
Packit Service a2ae7a
         <https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-getfileinformationbyhandle>
Packit Service a2ae7a
         <https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/ns-fileapi-_by_handle_file_information>
Packit Service a2ae7a
         as 64 bits, or through
Packit Service a2ae7a
         GetFileInformationByHandleEx with argument FileIdInfo
Packit Service a2ae7a
         <https://docs.microsoft.com/en-us/windows/desktop/api/winbase/nf-winbase-getfileinformationbyhandleex>
Packit Service a2ae7a
         <https://docs.microsoft.com/en-us/windows/desktop/api/winbase/ns-winbase-_file_id_info>
Packit Service a2ae7a
         as 128 bits.
Packit Service a2ae7a
         The latter requires -D_WIN32_WINNT=_WIN32_WINNT_WIN8 or higher.  */
Packit Service a2ae7a
      /* Experiments show that GetFileInformationByHandleEx does not provide
Packit Service a2ae7a
         much more information than GetFileInformationByHandle:
Packit Service a2ae7a
           * The dwVolumeSerialNumber from GetFileInformationByHandle is equal
Packit Service a2ae7a
             to the low 32 bits of the 64-bit VolumeSerialNumber from
Packit Service a2ae7a
             GetFileInformationByHandleEx, and is apparently sufficient for
Packit Service a2ae7a
             identifying the device.
Packit Service a2ae7a
           * The nFileIndex from GetFileInformationByHandle is equal to the low
Packit Service a2ae7a
             64 bits of the 128-bit FileId from GetFileInformationByHandleEx,
Packit Service a2ae7a
             and the high 64 bits of this 128-bit FileId are zero.
Packit Service a2ae7a
           * On a FAT file system, GetFileInformationByHandleEx fails with error
Packit Service a2ae7a
             ERROR_INVALID_PARAMETER, whereas GetFileInformationByHandle
Packit Service a2ae7a
             succeeds.
Packit Service a2ae7a
           * On a CIFS/SMB file system, GetFileInformationByHandleEx fails with
Packit Service a2ae7a
             error ERROR_INVALID_LEVEL, whereas GetFileInformationByHandle
Packit Service a2ae7a
             succeeds.  */
Packit Service a2ae7a
# if _GL_WINDOWS_STAT_INODES == 2
Packit Service a2ae7a
      if (GetFileInformationByHandleExFunc != NULL)
Packit Service a2ae7a
        {
Packit Service a2ae7a
          FILE_ID_INFO id;
Packit Service a2ae7a
          if (GetFileInformationByHandleExFunc (h, FileIdInfo, &id, sizeof (id)))
Packit Service a2ae7a
            {
Packit Service a2ae7a
              buf->st_dev = id.VolumeSerialNumber;
Packit Service a2ae7a
              verify (sizeof (ino_t) == sizeof (id.FileId));
Packit Service a2ae7a
              memcpy (&buf->st_ino, &id.FileId, sizeof (ino_t));
Packit Service a2ae7a
              goto ino_done;
Packit Service a2ae7a
            }
Packit Service a2ae7a
          else
Packit Service a2ae7a
            {
Packit Service a2ae7a
              switch (GetLastError ())
Packit Service a2ae7a
                {
Packit Service a2ae7a
                case ERROR_INVALID_PARAMETER: /* older Windows version, or FAT */
Packit Service a2ae7a
                case ERROR_INVALID_LEVEL: /* CIFS/SMB file system */
Packit Service a2ae7a
                  goto fallback;
Packit Service a2ae7a
                default:
Packit Service a2ae7a
                  goto failed;
Packit Service a2ae7a
                }
Packit Service a2ae7a
            }
Packit Service a2ae7a
        }
Packit Service a2ae7a
     fallback: ;
Packit Service a2ae7a
      /* Fallback for older Windows versions.  */
Packit Service a2ae7a
      buf->st_dev = info.dwVolumeSerialNumber;
Packit Service a2ae7a
      buf->st_ino._gl_ino[0] = ((ULONGLONG) info.nFileIndexHigh << 32) | (ULONGLONG) info.nFileIndexLow;
Packit Service a2ae7a
      buf->st_ino._gl_ino[1] = 0;
Packit Service a2ae7a
     ino_done: ;
Packit Service a2ae7a
# else /* _GL_WINDOWS_STAT_INODES == 1 */
Packit Service a2ae7a
      buf->st_dev = info.dwVolumeSerialNumber;
Packit Service a2ae7a
      buf->st_ino = ((ULONGLONG) info.nFileIndexHigh << 32) | (ULONGLONG) info.nFileIndexLow;
Packit Service a2ae7a
# endif
Packit Service a2ae7a
#else
Packit Service a2ae7a
      /* st_ino is not wide enough for identifying a file on a device.
Packit Service a2ae7a
         Without st_ino, st_dev is pointless.  */
Packit Service a2ae7a
      buf->st_dev = 0;
Packit Service a2ae7a
      buf->st_ino = 0;
Packit Service a2ae7a
#endif
Packit Service a2ae7a
Packit Service a2ae7a
      /* st_mode.  */
Packit Service a2ae7a
      unsigned int mode =
Packit Service a2ae7a
        /* XXX How to handle FILE_ATTRIBUTE_REPARSE_POINT ?  */
Packit Service a2ae7a
        ((info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? _S_IFDIR | S_IEXEC_UGO : _S_IFREG)
Packit Service a2ae7a
        | S_IREAD_UGO
Packit Service a2ae7a
        | ((info.dwFileAttributes & FILE_ATTRIBUTE_READONLY) ? 0 : S_IWRITE_UGO);
Packit Service a2ae7a
      if (!(info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
Packit Service a2ae7a
        {
Packit Service a2ae7a
          /* Determine whether the file is executable by looking at the file
Packit Service a2ae7a
             name suffix.
Packit Service a2ae7a
             If the file name is already known, use it. Otherwise, for
Packit Service a2ae7a
             non-empty files, it can be determined through
Packit Service a2ae7a
             GetFinalPathNameByHandle
Packit Service a2ae7a
             <https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-getfinalpathnamebyhandlea>
Packit Service a2ae7a
             or through
Packit Service a2ae7a
             GetFileInformationByHandleEx with argument FileNameInfo
Packit Service a2ae7a
             <https://docs.microsoft.com/en-us/windows/desktop/api/winbase/nf-winbase-getfileinformationbyhandleex>
Packit Service a2ae7a
             <https://docs.microsoft.com/en-us/windows/desktop/api/winbase/ns-winbase-_file_name_info>
Packit Service a2ae7a
             Both require -D_WIN32_WINNT=_WIN32_WINNT_VISTA or higher.  */
Packit Service a2ae7a
          if (info.nFileSizeHigh > 0 || info.nFileSizeLow > 0)
Packit Service a2ae7a
            {
Packit Service a2ae7a
              char fpath[PATH_MAX];
Packit Service a2ae7a
              if (path != NULL
Packit Service a2ae7a
                  || (GetFinalPathNameByHandleFunc != NULL
Packit Service a2ae7a
                      && GetFinalPathNameByHandleFunc (h, fpath, sizeof (fpath), VOLUME_NAME_NONE)
Packit Service a2ae7a
                         < sizeof (fpath)
Packit Service a2ae7a
                      && (path = fpath, 1)))
Packit Service a2ae7a
                {
Packit Service a2ae7a
                  const char *last_dot = NULL;
Packit Service a2ae7a
                  const char *p;
Packit Service a2ae7a
                  for (p = path; *p != '\0'; p++)
Packit Service a2ae7a
                    if (*p == '.')
Packit Service a2ae7a
                      last_dot = p;
Packit Service a2ae7a
                  if (last_dot != NULL)
Packit Service a2ae7a
                    {
Packit Service a2ae7a
                      const char *suffix = last_dot + 1;
Packit Service a2ae7a
                      if (_stricmp (suffix, "exe") == 0
Packit Service a2ae7a
                          || _stricmp (suffix, "bat") == 0
Packit Service a2ae7a
                          || _stricmp (suffix, "cmd") == 0
Packit Service a2ae7a
                          || _stricmp (suffix, "com") == 0)
Packit Service a2ae7a
                        mode |= S_IEXEC_UGO;
Packit Service a2ae7a
                    }
Packit Service a2ae7a
                }
Packit Service a2ae7a
              else
Packit Service a2ae7a
                /* Cannot determine file name.  Pretend that it is executable.  */
Packit Service a2ae7a
                mode |= S_IEXEC_UGO;
Packit Service a2ae7a
            }
Packit Service a2ae7a
        }
Packit Service a2ae7a
      buf->st_mode = mode;
Packit Service a2ae7a
Packit Service a2ae7a
      /* st_nlink can be determined through
Packit Service a2ae7a
         GetFileInformationByHandle
Packit Service a2ae7a
         <https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-getfileinformationbyhandle>
Packit Service a2ae7a
         <https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/ns-fileapi-_by_handle_file_information>
Packit Service a2ae7a
         or through
Packit Service a2ae7a
         GetFileInformationByHandleEx with argument FileStandardInfo
Packit Service a2ae7a
         <https://docs.microsoft.com/en-us/windows/desktop/api/winbase/nf-winbase-getfileinformationbyhandleex>
Packit Service a2ae7a
         <https://docs.microsoft.com/en-us/windows/desktop/api/winbase/ns-winbase-_file_standard_info>
Packit Service a2ae7a
         The latter requires -D_WIN32_WINNT=_WIN32_WINNT_VISTA or higher.  */
Packit Service a2ae7a
      buf->st_nlink = (info.nNumberOfLinks > SHRT_MAX ? SHRT_MAX : info.nNumberOfLinks);
Packit Service a2ae7a
Packit Service a2ae7a
      /* There's no easy way to map the Windows SID concept to an integer.  */
Packit Service a2ae7a
      buf->st_uid = 0;
Packit Service a2ae7a
      buf->st_gid = 0;
Packit Service a2ae7a
Packit Service a2ae7a
      /* st_rdev is irrelevant for normal files and directories.  */
Packit Service a2ae7a
      buf->st_rdev = 0;
Packit Service a2ae7a
Packit Service a2ae7a
      /* st_size can be determined through
Packit Service a2ae7a
         GetFileSizeEx
Packit Service a2ae7a
         <https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-getfilesizeex>
Packit Service a2ae7a
         or through
Packit Service a2ae7a
         GetFileAttributesEx
Packit Service a2ae7a
         <https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-getfileattributesexa>
Packit Service a2ae7a
         <https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/ns-fileapi-_win32_file_attribute_data>
Packit Service a2ae7a
         or through
Packit Service a2ae7a
         GetFileInformationByHandle
Packit Service a2ae7a
         <https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-getfileinformationbyhandle>
Packit Service a2ae7a
         <https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/ns-fileapi-_by_handle_file_information>
Packit Service a2ae7a
         or through
Packit Service a2ae7a
         GetFileInformationByHandleEx with argument FileStandardInfo
Packit Service a2ae7a
         <https://docs.microsoft.com/en-us/windows/desktop/api/winbase/nf-winbase-getfileinformationbyhandleex>
Packit Service a2ae7a
         <https://docs.microsoft.com/en-us/windows/desktop/api/winbase/ns-winbase-_file_standard_info>
Packit Service a2ae7a
         The latter requires -D_WIN32_WINNT=_WIN32_WINNT_VISTA or higher.  */
Packit Service a2ae7a
      if (sizeof (buf->st_size) <= 4)
Packit Service a2ae7a
        /* Range check already done above.  */
Packit Service a2ae7a
        buf->st_size = info.nFileSizeLow;
Packit Service a2ae7a
      else
Packit Service a2ae7a
        buf->st_size = ((long long) info.nFileSizeHigh << 32) | (long long) info.nFileSizeLow;
Packit Service a2ae7a
Packit Service a2ae7a
      /* st_atime, st_mtime, st_ctime can be determined through
Packit Service a2ae7a
         GetFileTime
Packit Service a2ae7a
         <https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-getfiletime>
Packit Service a2ae7a
         or through
Packit Service a2ae7a
         GetFileAttributesEx
Packit Service a2ae7a
         <https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-getfileattributesexa>
Packit Service a2ae7a
         <https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/ns-fileapi-_win32_file_attribute_data>
Packit Service a2ae7a
         or through
Packit Service a2ae7a
         GetFileInformationByHandle
Packit Service a2ae7a
         <https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-getfileinformationbyhandle>
Packit Service a2ae7a
         <https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/ns-fileapi-_by_handle_file_information>
Packit Service a2ae7a
         or through
Packit Service a2ae7a
         GetFileInformationByHandleEx with argument FileBasicInfo
Packit Service a2ae7a
         <https://docs.microsoft.com/en-us/windows/desktop/api/winbase/nf-winbase-getfileinformationbyhandleex>
Packit Service a2ae7a
         <https://docs.microsoft.com/en-us/windows/desktop/api/winbase/ns-winbase-_file_basic_info>
Packit Service a2ae7a
         The latter requires -D_WIN32_WINNT=_WIN32_WINNT_VISTA or higher.  */
Packit Service a2ae7a
#if _GL_WINDOWS_STAT_TIMESPEC
Packit Service a2ae7a
      buf->st_atim = _gl_convert_FILETIME_to_timespec (&info.ftLastAccessTime);
Packit Service a2ae7a
      buf->st_mtim = _gl_convert_FILETIME_to_timespec (&info.ftLastWriteTime);
Packit Service a2ae7a
      buf->st_ctim = _gl_convert_FILETIME_to_timespec (&info.ftCreationTime);
Packit Service a2ae7a
#else
Packit Service a2ae7a
      buf->st_atime = _gl_convert_FILETIME_to_POSIX (&info.ftLastAccessTime);
Packit Service a2ae7a
      buf->st_mtime = _gl_convert_FILETIME_to_POSIX (&info.ftLastWriteTime);
Packit Service a2ae7a
      buf->st_ctime = _gl_convert_FILETIME_to_POSIX (&info.ftCreationTime);
Packit Service a2ae7a
#endif
Packit Service a2ae7a
Packit Service a2ae7a
      return 0;
Packit Service a2ae7a
    }
Packit Service a2ae7a
  else if (type == FILE_TYPE_CHAR || type == FILE_TYPE_PIPE)
Packit Service a2ae7a
    {
Packit Service a2ae7a
      buf->st_dev = 0;
Packit Service a2ae7a
#if _GL_WINDOWS_STAT_INODES == 2
Packit Service a2ae7a
      buf->st_ino._gl_ino[0] = buf->st_ino._gl_ino[1] = 0;
Packit Service a2ae7a
#else
Packit Service a2ae7a
      buf->st_ino = 0;
Packit Service a2ae7a
#endif
Packit Service a2ae7a
      buf->st_mode = (type == FILE_TYPE_PIPE ? _S_IFIFO : _S_IFCHR);
Packit Service a2ae7a
      buf->st_nlink = 1;
Packit Service a2ae7a
      buf->st_uid = 0;
Packit Service a2ae7a
      buf->st_gid = 0;
Packit Service a2ae7a
      buf->st_rdev = 0;
Packit Service a2ae7a
      if (type == FILE_TYPE_PIPE)
Packit Service a2ae7a
        {
Packit Service a2ae7a
          /* PeekNamedPipe
Packit Service a2ae7a
             <https://msdn.microsoft.com/en-us/library/aa365779.aspx> */
Packit Service a2ae7a
          DWORD bytes_available;
Packit Service a2ae7a
          if (PeekNamedPipe (h, NULL, 0, NULL, &bytes_available, NULL))
Packit Service a2ae7a
            buf->st_size = bytes_available;
Packit Service a2ae7a
          else
Packit Service a2ae7a
            buf->st_size = 0;
Packit Service a2ae7a
        }
Packit Service a2ae7a
      else
Packit Service a2ae7a
        buf->st_size = 0;
Packit Service a2ae7a
#if _GL_WINDOWS_STAT_TIMESPEC
Packit Service a2ae7a
      buf->st_atim.tv_sec = 0; buf->st_atim.tv_nsec = 0;
Packit Service a2ae7a
      buf->st_mtim.tv_sec = 0; buf->st_mtim.tv_nsec = 0;
Packit Service a2ae7a
      buf->st_ctim.tv_sec = 0; buf->st_ctim.tv_nsec = 0;
Packit Service a2ae7a
#else
Packit Service a2ae7a
      buf->st_atime = 0;
Packit Service a2ae7a
      buf->st_mtime = 0;
Packit Service a2ae7a
      buf->st_ctime = 0;
Packit Service a2ae7a
#endif
Packit Service a2ae7a
      return 0;
Packit Service a2ae7a
    }
Packit Service a2ae7a
  else
Packit Service a2ae7a
    {
Packit Service a2ae7a
      errno = ENOENT;
Packit Service a2ae7a
      return -1;
Packit Service a2ae7a
    }
Packit Service a2ae7a
Packit Service a2ae7a
 failed:
Packit Service a2ae7a
  {
Packit Service a2ae7a
    DWORD error = GetLastError ();
Packit Service a2ae7a
    #if 0
Packit Service a2ae7a
    fprintf (stderr, "_gl_fstat_by_handle error 0x%x\n", (unsigned int) error);
Packit Service a2ae7a
    #endif
Packit Service a2ae7a
    switch (error)
Packit Service a2ae7a
      {
Packit Service a2ae7a
      case ERROR_ACCESS_DENIED:
Packit Service a2ae7a
      case ERROR_SHARING_VIOLATION:
Packit Service a2ae7a
        errno = EACCES;
Packit Service a2ae7a
        break;
Packit Service a2ae7a
Packit Service a2ae7a
      case ERROR_OUTOFMEMORY:
Packit Service a2ae7a
        errno = ENOMEM;
Packit Service a2ae7a
        break;
Packit Service a2ae7a
Packit Service a2ae7a
      case ERROR_WRITE_FAULT:
Packit Service a2ae7a
      case ERROR_READ_FAULT:
Packit Service a2ae7a
      case ERROR_GEN_FAILURE:
Packit Service a2ae7a
        errno = EIO;
Packit Service a2ae7a
        break;
Packit Service a2ae7a
Packit Service a2ae7a
      default:
Packit Service a2ae7a
        errno = EINVAL;
Packit Service a2ae7a
        break;
Packit Service a2ae7a
      }
Packit Service a2ae7a
    return -1;
Packit Service a2ae7a
  }
Packit Service a2ae7a
}
Packit Service a2ae7a
Packit Service a2ae7a
#else
Packit Service a2ae7a
Packit Service a2ae7a
/* This declaration is solely to ensure that after preprocessing
Packit Service a2ae7a
   this file is never empty.  */
Packit Service a2ae7a
typedef int dummy;
Packit Service a2ae7a
Packit Service a2ae7a
#endif