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