Diff for /embedaddon/libiconv/srclib/stat.c between versions 1.1.1.1 and 1.1.1.2

version 1.1.1.1, 2012/05/29 09:29:43 version 1.1.1.2, 2021/03/17 13:38:46
Line 1 Line 1
 /* Work around platform bugs in stat.  /* Work around platform bugs in stat.
   Copyright (C) 2009-2011 Free Software Foundation, Inc.   Copyright (C) 2009-2019 Free Software Foundation, Inc.
   
    This program is free software: you can redistribute it and/or modify     This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by     it under the terms of the GNU General Public License as published by
Line 12 Line 12
    GNU General Public License for more details.     GNU General Public License for more details.
   
    You should have received a copy of the GNU General Public License     You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
   
/* written by Eric Blake *//* Written by Eric Blake and Bruno Haible.  */
   
   /* If the user's config.h happens to include <sys/stat.h>, let it include only
      the system's <sys/stat.h> here, so that orig_stat doesn't recurse to
      rpl_stat.  */
   #define __need_system_sys_stat_h
 #include <config.h>  #include <config.h>
   
 /* Get the original definition of stat.  It might be defined as a macro.  */  /* Get the original definition of stat.  It might be defined as a macro.  */
 #define __need_system_sys_stat_h  
 #include <sys/types.h>  #include <sys/types.h>
 #include <sys/stat.h>  #include <sys/stat.h>
 #undef __need_system_sys_stat_h  #undef __need_system_sys_stat_h
   
static inline int#if defined _WIN32 && ! defined __CYGWIN__
 # define WINDOWS_NATIVE
 #endif
 
 #if !defined WINDOWS_NATIVE
 
 static int
 orig_stat (const char *filename, struct stat *buf)  orig_stat (const char *filename, struct stat *buf)
 {  {
   return stat (filename, buf);    return stat (filename, buf);
 }  }
   
   #endif
   
 /* Specification.  */  /* Specification.  */
#include <sys/stat.h>#ifdef __osf__
 /* Write "sys/stat.h" here, not <sys/stat.h>, otherwise OSF/1 5.1 DTK cc
    eliminates this include because of the preliminary #include <sys/stat.h>
    above.  */
 # include "sys/stat.h"
 #else
 # include <sys/stat.h>
 #endif
   
   #include "stat-time.h"
   
 #include <errno.h>  #include <errno.h>
 #include <limits.h>  #include <limits.h>
 #include <stdbool.h>  #include <stdbool.h>
 #include <string.h>  #include <string.h>
#include "dosname.h"#include "filename.h"
 #include "malloca.h"
 #include "verify.h"  #include "verify.h"
   
   #ifdef WINDOWS_NATIVE
   # define WIN32_LEAN_AND_MEAN
   # include <windows.h>
   # include "stat-w32.h"
   #endif
   
   #ifdef WINDOWS_NATIVE
   /* Return TRUE if the given file name denotes an UNC root.  */
   static BOOL
   is_unc_root (const char *rname)
   {
     /* Test whether it has the syntax '\\server\share'.  */
     if (ISSLASH (rname[0]) && ISSLASH (rname[1]))
       {
         /* It starts with two slashes.  Find the next slash.  */
         const char *p = rname + 2;
         const char *q = p;
         while (*q != '\0' && !ISSLASH (*q))
           q++;
         if (q > p && *q != '\0')
           {
             /* Found the next slash at q.  */
             q++;
             const char *r = q;
             while (*r != '\0' && !ISSLASH (*r))
               r++;
             if (r > q && *r == '\0')
               return TRUE;
           }
       }
     return FALSE;
   }
   #endif
   
 /* Store information about NAME into ST.  Work around bugs with  /* Store information about NAME into ST.  Work around bugs with
    trailing slashes.  Mingw has other bugs (such as st_ino always     trailing slashes.  Mingw has other bugs (such as st_ino always
    being 0 on success) which this wrapper does not work around.  But     being 0 on success) which this wrapper does not work around.  But
Line 47  orig_stat (const char *filename, struct stat *buf) Line 102  orig_stat (const char *filename, struct stat *buf)
    correctly.  */     correctly.  */
   
 int  int
rpl_stat (char const *name, struct stat *st)rpl_stat (char const *name, struct stat *buf)
 {  {
  int result = orig_stat (name, st);#ifdef WINDOWS_NATIVE
#if REPLACE_FUNC_STAT_FILE  /* Fill the fields ourselves, because the original stat function returns
  /* Solaris 9 mistakenly succeeds when given a non-directory with a     values for st_atime, st_mtime, st_ctime that depend on the current time
     trailing slash.  */     zone.  See
  if (result == 0 && !S_ISDIR (st->st_mode))     <https://lists.gnu.org/r/bug-gnulib/2017-04/msg00134.html>  */
   /* XXX Should we convert to wchar_t* and prepend '\\?\', in order to work
      around length limitations
      <https://docs.microsoft.com/en-us/windows/desktop/FileIO/naming-a-file> ?  */
 
   /* POSIX <http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_13>
      specifies: "More than two leading <slash> characters shall be treated as
      a single <slash> character."  */
   if (ISSLASH (name[0]) && ISSLASH (name[1]) && ISSLASH (name[2]))
     {      {
      size_t len = strlen (name);      name += 2;
      if (ISSLASH (name[len - 1]))      while (ISSLASH (name[1]))
         name++;
     }
 
   size_t len = strlen (name);
   size_t drive_prefix_len = (HAS_DEVICE (name) ? 2 : 0);
 
   /* Remove trailing slashes (except the very first one, at position
      drive_prefix_len), but remember their presence.  */
   size_t rlen;
   bool check_dir = false;
 
   rlen = len;
   while (rlen > drive_prefix_len && ISSLASH (name[rlen-1]))
     {
       check_dir = true;
       if (rlen == drive_prefix_len + 1)
         break;
       rlen--;
     }
 
   /* Handle '' and 'C:'.  */
   if (!check_dir && rlen == drive_prefix_len)
     {
       errno = ENOENT;
       return -1;
     }
 
   /* Handle '\\'.  */
   if (rlen == 1 && ISSLASH (name[0]) && len >= 2)
     {
       errno = ENOENT;
       return -1;
     }
 
   const char *rname;
   char *malloca_rname;
   if (rlen == len)
     {
       rname = name;
       malloca_rname = NULL;
     }
   else
     {
       malloca_rname = malloca (rlen + 1);
       if (malloca_rname == NULL)
         {          {
          errno = ENOTDIR;          errno = ENOMEM;
           return -1;            return -1;
         }          }
         memcpy (malloca_rname, name, rlen);
         malloca_rname[rlen] = '\0';
         rname = malloca_rname;
     }      }
 #endif /* REPLACE_FUNC_STAT_FILE */  
 #if REPLACE_FUNC_STAT_DIR  
   /* The only known systems where REPLACE_FUNC_STAT_DIR is needed also  
      have a constant PATH_MAX.  */  
 # ifndef PATH_MAX  
 #  error "Please port this replacement to your platform"  
 # endif  
   
  if (result == -1 && errno == ENOENT)  /* There are two ways to get at the requested information:
        - by scanning the parent directory and examining the relevant
          directory entry,
        - by opening the file directly.
      The first approach fails for root directories (e.g. 'C:\') and
      UNC root directories (e.g. '\\server\share').
      The second approach fails for some system files (e.g. 'C:\pagefile.sys'
      and 'C:\hiberfil.sys'): ERROR_SHARING_VIOLATION.
      The second approach gives more information (in particular, correct
      st_dev, st_ino, st_nlink fields).
      So we use the second approach and, as a fallback except for root and
      UNC root directories, also the first approach.  */
   {
     int ret;
 
     {      {
      /* Due to mingw's oddities, there are some directories (like      /* Approach based on the file.  */
         c:\) where stat() only succeeds with a trailing slash, and
         other directories (like c:\windows) where stat() only      /* Open a handle to the file.
         succeeds without a trailing slash.  But we want the two to be         CreateFile
         synonymous, since chdir() manages either style.  Likewise, Mingw also         <https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-createfilea>
         reports ENOENT for names longer than PATH_MAX, when we want         <https://docs.microsoft.com/en-us/windows/desktop/FileIO/creating-and-opening-files>  */
         ENAMETOOLONG, and for stat("file/"), when we want ENOTDIR.      HANDLE h =
         Fortunately, mingw PATH_MAX is small enough for stack        CreateFile (rname,
         allocation.  */                    FILE_READ_ATTRIBUTES,
      char fixed_name[PATH_MAX + 1] = {0};                    FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
      size_t len = strlen (name);                    NULL,
      bool check_dir = false;                    OPEN_EXISTING,
      verify (PATH_MAX <= 4096);                    /* FILE_FLAG_POSIX_SEMANTICS (treat file names that differ only
      if (PATH_MAX <= len)                       in case as different) makes sense only when applied to *all*
        errno = ENAMETOOLONG;                       filesystem operations.  */
      else if (len)                    FILE_FLAG_BACKUP_SEMANTICS /* | FILE_FLAG_POSIX_SEMANTICS */,
                     NULL);
       if (h != INVALID_HANDLE_VALUE)
         {          {
          strcpy (fixed_name, name);          ret = _gl_fstat_by_handle (h, rname, buf);
          if (ISSLASH (fixed_name[len - 1]))          CloseHandle (h);
           goto done;
         }
     }
 
     /* Test for root and UNC root directories.  */
     if ((rlen == drive_prefix_len + 1 && ISSLASH (rname[drive_prefix_len]))
         || is_unc_root (rname))
       goto failed;
 
     /* Fallback.  */
     {
       /* Approach based on the directory entry.  */
 
       if (strchr (rname, '?') != NULL || strchr (rname, '*') != NULL)
         {
           /* Other Windows API functions would fail with error
              ERROR_INVALID_NAME.  */
           if (malloca_rname != NULL)
             freea (malloca_rname);
           errno = ENOENT;
           return -1;
         }
 
       /* Get the details about the directory entry.  This can be done through
          FindFirstFile
          <https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-findfirstfilea>
          <https://docs.microsoft.com/en-us/windows/desktop/api/minwinbase/ns-minwinbase-_win32_find_dataa>
          or through
          FindFirstFileEx with argument FindExInfoBasic
          <https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-findfirstfileexa>
          <https://docs.microsoft.com/en-us/windows/desktop/api/minwinbase/ne-minwinbase-findex_info_levels>
          <https://docs.microsoft.com/en-us/windows/desktop/api/minwinbase/ns-minwinbase-_win32_find_dataa>  */
       WIN32_FIND_DATA info;
       HANDLE h = FindFirstFile (rname, &info);
       if (h == INVALID_HANDLE_VALUE)
         goto failed;
 
       /* Test for error conditions before starting to fill *buf.  */
       if (sizeof (buf->st_size) <= 4 && info.nFileSizeHigh > 0)
         {
           FindClose (h);
           if (malloca_rname != NULL)
             freea (malloca_rname);
           errno = EOVERFLOW;
           return -1;
         }
 
 # if _GL_WINDOWS_STAT_INODES
       buf->st_dev = 0;
 #  if _GL_WINDOWS_STAT_INODES == 2
       buf->st_ino._gl_ino[0] = buf->st_ino._gl_ino[1] = 0;
 #  else /* _GL_WINDOWS_STAT_INODES == 1 */
       buf->st_ino = 0;
 #  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 (info.nFileSizeHigh > 0 || info.nFileSizeLow > 0)
             {              {
              check_dir = true;              const char *last_dot = NULL;
              while (len && ISSLASH (fixed_name[len - 1]))              const char *p;
                fixed_name[--len] = '\0';              for (p = info.cFileName; *p != '\0'; p++)
              if (!len)                if (*p == '.')
                fixed_name[0] = '/';                  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        }
            fixed_name[len++] = '/';      buf->st_mode = mode;
          result = orig_stat (fixed_name, st);
          if (result == 0 && check_dir && !S_ISDIR (st->st_mode))      /* st_nlink.  Ignore hard links here.  */
       buf->st_nlink = 1;
 
       /* 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.  */
       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.  */
 # 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
 
       FindClose (h);
 
       ret = 0;
     }
 
    done:
     if (ret >= 0 && check_dir && !S_ISDIR (buf->st_mode))
       {
         errno = ENOTDIR;
         ret = -1;
       }
     if (malloca_rname != NULL)
       {
         int saved_errno = errno;
         freea (malloca_rname);
         errno = saved_errno;
       }
     return ret;
   }
 
  failed:
   {
     DWORD error = GetLastError ();
     #if 0
     fprintf (stderr, "rpl_stat error 0x%x\n", (unsigned int) error);
     #endif
 
     if (malloca_rname != NULL)
       freea (malloca_rname);
 
     switch (error)
       {
       /* Some of these errors probably cannot happen with the specific flags
          that we pass to CreateFile.  But who knows...  */
       case ERROR_FILE_NOT_FOUND: /* The last component of rname does not exist.  */
       case ERROR_PATH_NOT_FOUND: /* Some directory component in rname does not exist.  */
       case ERROR_BAD_PATHNAME:   /* rname is such as '\\server'.  */
       case ERROR_BAD_NET_NAME:   /* rname is such as '\\server\nonexistentshare'.  */
       case ERROR_INVALID_NAME:   /* rname contains wildcards, misplaced colon, etc.  */
       case ERROR_DIRECTORY:
         errno = ENOENT;
         break;
 
       case ERROR_ACCESS_DENIED:  /* rname is such as 'C:\System Volume Information\foo'.  */
       case ERROR_SHARING_VIOLATION: /* rname is such as 'C:\pagefile.sys' (second approach only).  */
                                     /* XXX map to EACCESS or EPERM? */
         errno = EACCES;
         break;
 
       case ERROR_OUTOFMEMORY:
         errno = ENOMEM;
         break;
 
       case ERROR_WRITE_PROTECT:
         errno = EROFS;
         break;
 
       case ERROR_WRITE_FAULT:
       case ERROR_READ_FAULT:
       case ERROR_GEN_FAILURE:
         errno = EIO;
         break;
 
       case ERROR_BUFFER_OVERFLOW:
       case ERROR_FILENAME_EXCED_RANGE:
         errno = ENAMETOOLONG;
         break;
 
       case ERROR_DELETE_PENDING: /* XXX map to EACCESS or EPERM? */
         errno = EPERM;
         break;
 
       default:
         errno = EINVAL;
         break;
       }
 
     return -1;
   }
 #else
   int result = orig_stat (name, buf);
   if (result == 0)
     {
 # if REPLACE_FUNC_STAT_FILE
       /* Solaris 9 mistakenly succeeds when given a non-directory with a
          trailing slash.  */
       if (!S_ISDIR (buf->st_mode))
         {
           size_t len = strlen (name);
           if (ISSLASH (name[len - 1]))
             {              {
               result = -1;  
               errno = ENOTDIR;                errno = ENOTDIR;
                 return -1;
             }              }
         }          }
   # endif /* REPLACE_FUNC_STAT_FILE */
         result = stat_time_normalize (result, buf);
     }      }
 #endif /* REPLACE_FUNC_STAT_DIR */  
   return result;    return result;
   #endif
 }  }

Removed from v.1.1.1.1  
changed lines
  Added in v.1.1.1.2


FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>