Annotation of embedaddon/libiconv/srclib/stat-w32.c, revision 1.1.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>