root/maint/gnulib/lib/utime.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. _gl_utimens_windows
  2. utime
  3. utime

   1 /* Work around platform bugs in utime.
   2    Copyright (C) 2017-2021 Free Software Foundation, Inc.
   3 
   4    This file is free software: you can redistribute it and/or modify
   5    it under the terms of the GNU Lesser General Public License as
   6    published by the Free Software Foundation; either version 3 of the
   7    License, or (at your option) any later version.
   8 
   9    This file 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 Lesser General Public License for more details.
  13 
  14    You should have received a copy of the GNU Lesser 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 /* Specification.  */
  22 #include <utime.h>
  23 
  24 #if defined _WIN32 && ! defined __CYGWIN__
  25 
  26 # include <errno.h>
  27 # include <stdbool.h>
  28 # include <windows.h>
  29 # include "filename.h"
  30 # include "malloca.h"
  31 
  32 /* Don't assume that UNICODE is not defined.  */
  33 # undef CreateFile
  34 # define CreateFile CreateFileA
  35 # undef GetFileAttributes
  36 # define GetFileAttributes GetFileAttributesA
  37 
  38 int
  39 _gl_utimens_windows (const char *name, struct timespec ts[2])
     /* [previous][next][first][last][top][bottom][index][help] */
  40 {
  41   /* POSIX <https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_13>
  42      specifies: "More than two leading <slash> characters shall be treated as
  43      a single <slash> character."  */
  44   if (ISSLASH (name[0]) && ISSLASH (name[1]) && ISSLASH (name[2]))
  45     {
  46       name += 2;
  47       while (ISSLASH (name[1]))
  48         name++;
  49     }
  50 
  51   size_t len = strlen (name);
  52   size_t drive_prefix_len = (HAS_DEVICE (name) ? 2 : 0);
  53 
  54   /* Remove trailing slashes (except the very first one, at position
  55      drive_prefix_len), but remember their presence.  */
  56   size_t rlen;
  57   bool check_dir = false;
  58 
  59   rlen = len;
  60   while (rlen > drive_prefix_len && ISSLASH (name[rlen-1]))
  61     {
  62       check_dir = true;
  63       if (rlen == drive_prefix_len + 1)
  64         break;
  65       rlen--;
  66     }
  67 
  68   const char *rname;
  69   char *malloca_rname;
  70   if (rlen == len)
  71     {
  72       rname = name;
  73       malloca_rname = NULL;
  74     }
  75   else
  76     {
  77       malloca_rname = malloca (rlen + 1);
  78       if (malloca_rname == NULL)
  79         {
  80           errno = ENOMEM;
  81           return -1;
  82         }
  83       memcpy (malloca_rname, name, rlen);
  84       malloca_rname[rlen] = '\0';
  85       rname = malloca_rname;
  86     }
  87 
  88   DWORD error;
  89 
  90   /* Open a handle to the file.
  91      CreateFile
  92      <https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-createfilea>
  93      <https://docs.microsoft.com/en-us/windows/desktop/FileIO/creating-and-opening-files>  */
  94   HANDLE handle =
  95     CreateFile (rname,
  96                 FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES,
  97                 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
  98                 NULL,
  99                 OPEN_EXISTING,
 100                 /* FILE_FLAG_POSIX_SEMANTICS (treat file names that differ only
 101                    in case as different) makes sense only when applied to *all*
 102                    filesystem operations.  */
 103                 FILE_FLAG_BACKUP_SEMANTICS /* | FILE_FLAG_POSIX_SEMANTICS */,
 104                 NULL);
 105   if (handle == INVALID_HANDLE_VALUE)
 106     {
 107       error = GetLastError ();
 108       goto failed;
 109     }
 110 
 111   if (check_dir)
 112     {
 113       /* GetFileAttributes
 114          <https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-getfileattributesa>  */
 115       DWORD attributes = GetFileAttributes (rname);
 116       if (attributes == INVALID_FILE_ATTRIBUTES)
 117         {
 118           error = GetLastError ();
 119           CloseHandle (handle);
 120           goto failed;
 121         }
 122       if ((attributes & FILE_ATTRIBUTE_DIRECTORY) == 0)
 123         {
 124           CloseHandle (handle);
 125           if (malloca_rname != NULL)
 126             freea (malloca_rname);
 127           errno = ENOTDIR;
 128           return -1;
 129         }
 130     }
 131 
 132   {
 133     /* Use SetFileTime(). See
 134        <https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-setfiletime>
 135        <https://docs.microsoft.com/en-us/windows/desktop/api/minwinbase/ns-minwinbase-filetime>  */
 136     FILETIME last_access_time;
 137     FILETIME last_write_time;
 138     if (ts == NULL)
 139       {
 140         /* GetSystemTimeAsFileTime is the same as
 141            GetSystemTime followed by SystemTimeToFileTime.
 142            <https://docs.microsoft.com/en-us/windows/desktop/api/sysinfoapi/nf-sysinfoapi-getsystemtimeasfiletime>.
 143            It would be overkill to use
 144            GetSystemTimePreciseAsFileTime
 145            <https://docs.microsoft.com/en-us/windows/desktop/api/sysinfoapi/nf-sysinfoapi-getsystemtimepreciseasfiletime>.  */
 146         FILETIME current_time;
 147         GetSystemTimeAsFileTime (&current_time);
 148         last_access_time = current_time;
 149         last_write_time = current_time;
 150       }
 151     else
 152       {
 153         {
 154           ULONGLONG time_since_16010101 =
 155             (ULONGLONG) ts[0].tv_sec * 10000000 + ts[0].tv_nsec / 100 + 116444736000000000LL;
 156           last_access_time.dwLowDateTime = (DWORD) time_since_16010101;
 157           last_access_time.dwHighDateTime = time_since_16010101 >> 32;
 158         }
 159         {
 160           ULONGLONG time_since_16010101 =
 161             (ULONGLONG) ts[1].tv_sec * 10000000 + ts[1].tv_nsec / 100 + 116444736000000000LL;
 162           last_write_time.dwLowDateTime = (DWORD) time_since_16010101;
 163           last_write_time.dwHighDateTime = time_since_16010101 >> 32;
 164         }
 165       }
 166     if (SetFileTime (handle, NULL, &last_access_time, &last_write_time))
 167       {
 168         CloseHandle (handle);
 169         if (malloca_rname != NULL)
 170           freea (malloca_rname);
 171         return 0;
 172       }
 173     else
 174       {
 175         #if 0
 176         DWORD sft_error = GetLastError ();
 177         fprintf (stderr, "utimens SetFileTime error 0x%x\n", (unsigned int) sft_error);
 178         #endif
 179         CloseHandle (handle);
 180         if (malloca_rname != NULL)
 181           freea (malloca_rname);
 182         errno = EINVAL;
 183         return -1;
 184       }
 185   }
 186 
 187  failed:
 188   {
 189     #if 0
 190     fprintf (stderr, "utimens CreateFile/GetFileAttributes error 0x%x\n", (unsigned int) error);
 191     #endif
 192     if (malloca_rname != NULL)
 193       freea (malloca_rname);
 194 
 195     switch (error)
 196       {
 197       /* Some of these errors probably cannot happen with the specific flags
 198          that we pass to CreateFile.  But who knows...  */
 199       case ERROR_FILE_NOT_FOUND: /* The last component of rname does not exist.  */
 200       case ERROR_PATH_NOT_FOUND: /* Some directory component in rname does not exist.  */
 201       case ERROR_BAD_PATHNAME:   /* rname is such as '\\server'.  */
 202       case ERROR_BAD_NETPATH:    /* rname is such as '\\nonexistentserver\share'.  */
 203       case ERROR_BAD_NET_NAME:   /* rname is such as '\\server\nonexistentshare'.  */
 204       case ERROR_INVALID_NAME:   /* rname contains wildcards, misplaced colon, etc.  */
 205       case ERROR_DIRECTORY:
 206         errno = ENOENT;
 207         break;
 208 
 209       case ERROR_ACCESS_DENIED:  /* rname is such as 'C:\System Volume Information\foo'.  */
 210       case ERROR_SHARING_VIOLATION: /* rname is such as 'C:\pagefile.sys'.  */
 211         errno = (ts != NULL ? EPERM : EACCES);
 212         break;
 213 
 214       case ERROR_OUTOFMEMORY:
 215         errno = ENOMEM;
 216         break;
 217 
 218       case ERROR_WRITE_PROTECT:
 219         errno = EROFS;
 220         break;
 221 
 222       case ERROR_WRITE_FAULT:
 223       case ERROR_READ_FAULT:
 224       case ERROR_GEN_FAILURE:
 225         errno = EIO;
 226         break;
 227 
 228       case ERROR_BUFFER_OVERFLOW:
 229       case ERROR_FILENAME_EXCED_RANGE:
 230         errno = ENAMETOOLONG;
 231         break;
 232 
 233       case ERROR_DELETE_PENDING: /* XXX map to EACCES or EPERM? */
 234         errno = EPERM;
 235         break;
 236 
 237       default:
 238         errno = EINVAL;
 239         break;
 240       }
 241 
 242     return -1;
 243   }
 244 }
 245 
 246 int
 247 utime (const char *name, const struct utimbuf *ts)
     /* [previous][next][first][last][top][bottom][index][help] */
 248 {
 249   if (ts == NULL)
 250     return _gl_utimens_windows (name, NULL);
 251   else
 252     {
 253       struct timespec ts_with_nanoseconds[2];
 254       ts_with_nanoseconds[0].tv_sec = ts->actime;
 255       ts_with_nanoseconds[0].tv_nsec = 0;
 256       ts_with_nanoseconds[1].tv_sec = ts->modtime;
 257       ts_with_nanoseconds[1].tv_nsec = 0;
 258       return _gl_utimens_windows (name, ts_with_nanoseconds);
 259     }
 260 }
 261 
 262 #else
 263 
 264 # include <errno.h>
 265 # include <sys/stat.h>
 266 # include "filename.h"
 267 
 268 int
 269 utime (const char *name, const struct utimbuf *ts)
     /* [previous][next][first][last][top][bottom][index][help] */
 270 #undef utime
 271 {
 272 # if REPLACE_FUNC_UTIME_FILE
 273   /* macOS 10.13 mistakenly succeeds when given a symbolic link to a
 274      non-directory with a trailing slash.  */
 275   size_t len = strlen (name);
 276   if (len > 0 && ISSLASH (name[len - 1]))
 277     {
 278       struct stat buf;
 279 
 280       if (stat (name, &buf) == -1 && errno != EOVERFLOW)
 281         return -1;
 282     }
 283 # endif /* REPLACE_FUNC_UTIME_FILE */
 284 
 285   return utime (name, ts);
 286 }
 287 
 288 #endif

/* [previous][next][first][last][top][bottom][index][help] */