File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / libiconv / srclib / stat-w32.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Wed Mar 17 13:38:46 2021 UTC (3 years, 3 months ago) by misho
Branches: libiconv, MAIN
CVS tags: v1_16p0, HEAD
libiconv 1.16

    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>