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