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