root/maint/gnulib/lib/utimecmp.c

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

DEFINITIONS

This source file includes following definitions.
  1. dev_info_hash
  2. dev_info_compare
  3. utimecmp
  4. utimecmpat

   1 /* utimecmp.c -- compare file timestamps
   2 
   3    Copyright (C) 2004-2007, 2009-2021 Free Software Foundation, Inc.
   4 
   5    This program is free software: you can redistribute it and/or modify
   6    it under the terms of the GNU General Public License as published by
   7    the Free Software Foundation; either version 3 of the License, or
   8    (at your option) any later version.
   9 
  10    This program is distributed in the hope that it will be useful,
  11    but WITHOUT ANY WARRANTY; without even the implied warranty of
  12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13    GNU General Public License for more details.
  14 
  15    You should have received a copy of the GNU General Public License
  16    along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
  17 
  18 /* Written by Paul Eggert.  */
  19 
  20 #include <config.h>
  21 
  22 #include "utimecmp.h"
  23 
  24 #include <fcntl.h>
  25 #include <limits.h>
  26 #include <stdbool.h>
  27 #include <stdint.h>
  28 #include <stdlib.h>
  29 #include <sys/stat.h>
  30 #include <time.h>
  31 #include <unistd.h>
  32 
  33 #include "dirname.h"
  34 #include "hash.h"
  35 #include "intprops.h"
  36 #include "stat-time.h"
  37 #include "verify.h"
  38 
  39 #ifndef MAX
  40 # define MAX(a, b) ((a) > (b) ? (a) : (b))
  41 #endif
  42 
  43 #define BILLION (1000 * 1000 * 1000)
  44 
  45 /* Best possible resolution that utimens can set and stat can return,
  46    due to system-call limitations.  It must be a power of 10 that is
  47    no greater than 1 billion.  */
  48 #if HAVE_UTIMENSAT
  49 enum { SYSCALL_RESOLUTION = 1 };
  50 #elif defined _WIN32 && ! defined __CYGWIN__
  51 /* On native Windows, file times have 100 ns resolution. See
  52    <https://docs.microsoft.com/en-us/windows/desktop/api/minwinbase/ns-minwinbase-filetime>  */
  53 enum { SYSCALL_RESOLUTION = 100 };
  54 #elif ((HAVE_FUTIMESAT || HAVE_WORKING_UTIMES)                  \
  55        && (defined HAVE_STRUCT_STAT_ST_ATIM_TV_NSEC             \
  56            || defined HAVE_STRUCT_STAT_ST_ATIMESPEC_TV_NSEC     \
  57            || defined HAVE_STRUCT_STAT_ST_ATIMENSEC             \
  58            || defined HAVE_STRUCT_STAT_ST_ATIM_ST__TIM_TV_NSEC  \
  59            || defined HAVE_STRUCT_STAT_ST_SPARE1))
  60 enum { SYSCALL_RESOLUTION = 1000 };
  61 #else
  62 enum { SYSCALL_RESOLUTION = BILLION };
  63 #endif
  64 
  65 /* Describe a file system and its timestamp resolution in nanoseconds.  */
  66 struct fs_res
  67 {
  68   /* Device number of file system.  */
  69   dev_t dev;
  70 
  71   /* An upper bound on the timestamp resolution of this file system,
  72      ignoring any resolution that cannot be set via utimens.  It is
  73      represented by an integer count of nanoseconds.  It must be
  74      either 2 billion, or a power of 10 that is no greater than a
  75      billion and is no less than SYSCALL_RESOLUTION.  */
  76   int resolution;
  77 
  78   /* True if RESOLUTION is known to be exact, and is not merely an
  79      upper bound on the true resolution.  */
  80   bool exact;
  81 };
  82 
  83 /* Hash some device info.  */
  84 static size_t
  85 dev_info_hash (void const *x, size_t table_size)
     /* [previous][next][first][last][top][bottom][index][help] */
  86 {
  87   struct fs_res const *p = x;
  88 
  89   /* Beware signed arithmetic gotchas.  */
  90   if (TYPE_SIGNED (dev_t) && SIZE_MAX < MAX (INT_MAX, TYPE_MAXIMUM (dev_t)))
  91     {
  92       uintmax_t dev = p->dev;
  93       return dev % table_size;
  94     }
  95 
  96   return p->dev % table_size;
  97 }
  98 
  99 /* Compare two dev_info structs.  */
 100 static bool
 101 dev_info_compare (void const *x, void const *y)
     /* [previous][next][first][last][top][bottom][index][help] */
 102 {
 103   struct fs_res const *a = x;
 104   struct fs_res const *b = y;
 105   return a->dev == b->dev;
 106 }
 107 
 108 /* Return -1, 0, 1 based on whether the destination file (relative
 109    to openat-like directory file descriptor DFD with name
 110    DST_NAME and status DST_STAT) is older than SRC_STAT, the same age
 111    as SRC_STAT, or newer than SRC_STAT, respectively.
 112 
 113    DST_NAME may be NULL if OPTIONS is 0.
 114 
 115    If OPTIONS & UTIMECMP_TRUNCATE_SOURCE, do the comparison after SRC is
 116    converted to the destination's timestamp resolution as filtered through
 117    utimens.  In this case, return -2 if the exact answer cannot be
 118    determined; this can happen only if the timestamps are very close and
 119    there is some trouble accessing the file system (e.g., the user does not
 120    have permission to futz with the destination's timestamps).  */
 121 
 122 int
 123 utimecmp (char const *dst_name,
     /* [previous][next][first][last][top][bottom][index][help] */
 124           struct stat const *dst_stat,
 125           struct stat const *src_stat,
 126           int options)
 127 {
 128   return utimecmpat (AT_FDCWD, dst_name, dst_stat, src_stat, options);
 129 }
 130 
 131 int
 132 utimecmpat (int dfd, char const *dst_name,
     /* [previous][next][first][last][top][bottom][index][help] */
 133             struct stat const *dst_stat,
 134             struct stat const *src_stat,
 135             int options)
 136 {
 137   /* Things to watch out for:
 138 
 139      The code uses a static hash table internally and is not safe in the
 140      presence of signals, multiple threads, etc.  However, memory pressure
 141      that prevents use of the hash table is not fatal - we just fall back
 142      to redoing the computations on every call in that case.
 143 
 144      int and long int might be 32 bits.  Many of the calculations store
 145      numbers up to 2 billion, and multiply by 10; they have to avoid
 146      multiplying 2 billion by 10, as this exceeds 32-bit capabilities.
 147 
 148      time_t might be unsigned.  */
 149 
 150   verify (TYPE_IS_INTEGER (time_t));
 151 
 152   /* Destination and source timestamps.  */
 153   time_t dst_s = dst_stat->st_mtime;
 154   time_t src_s = src_stat->st_mtime;
 155   int dst_ns = get_stat_mtime_ns (dst_stat);
 156   int src_ns = get_stat_mtime_ns (src_stat);
 157 
 158   if (options & UTIMECMP_TRUNCATE_SOURCE)
 159     {
 160 #if defined _AIX
 161       /* On AIX 7.2, on a jfs2 file system, the times may differ by up to
 162          0.01 seconds in either direction.  But it does not seem to come
 163          from clock ticks of 0.01 seconds each.  */
 164       long long difference =
 165         ((long long) dst_s - (long long) src_s) * BILLION
 166         + ((long long) dst_ns - (long long) src_ns);
 167       if (difference < 10000000 && difference > -10000000)
 168         return 0;
 169 #endif
 170 
 171       /* Look up the timestamp resolution for the destination device.  */
 172 
 173       /* Hash table for caching information learned about devices.  */
 174       static Hash_table *ht;
 175 
 176       /* Information about the destination file system.  */
 177       static struct fs_res *new_dst_res;
 178       struct fs_res *dst_res = NULL;
 179       struct fs_res tmp_dst_res;
 180 
 181       /* timestamp resolution in nanoseconds.  */
 182       int res;
 183 
 184       /* Quick exit, if possible.  Since the worst resolution is 2
 185          seconds, anything that differs by more than that does not
 186          needs source truncation.  */
 187       if (dst_s == src_s && dst_ns == src_ns)
 188         return 0;
 189       if (dst_s <= src_s - 2)
 190         return -1;
 191       if (src_s <= dst_s - 2)
 192         return 1;
 193 
 194       /* Try to do a hash lookup, but fall back to stack variables and
 195          recomputation on low memory situations.  */
 196       if (! ht)
 197         ht = hash_initialize (16, NULL, dev_info_hash, dev_info_compare, free);
 198       if (ht)
 199         {
 200           if (! new_dst_res)
 201             {
 202               new_dst_res = malloc (sizeof *new_dst_res);
 203               if (!new_dst_res)
 204                 goto low_memory;
 205               new_dst_res->resolution = 2 * BILLION;
 206               new_dst_res->exact = false;
 207             }
 208           new_dst_res->dev = dst_stat->st_dev;
 209           dst_res = hash_insert (ht, new_dst_res);
 210           if (! dst_res)
 211             goto low_memory;
 212 
 213           if (dst_res == new_dst_res)
 214             {
 215               /* NEW_DST_RES is now in use in the hash table, so allocate a
 216                  new entry next time.  */
 217               new_dst_res = NULL;
 218             }
 219         }
 220       else
 221         {
 222         low_memory:
 223           if (ht)
 224             {
 225               tmp_dst_res.dev = dst_stat->st_dev;
 226               dst_res = hash_lookup (ht, &tmp_dst_res);
 227             }
 228           if (!dst_res)
 229             {
 230               dst_res = &tmp_dst_res;
 231               dst_res->resolution = 2 * BILLION;
 232               dst_res->exact = false;
 233             }
 234         }
 235 
 236       res = dst_res->resolution;
 237 
 238 #ifdef _PC_TIMESTAMP_RESOLUTION
 239       /* If the system will tell us the resolution, we're set!  */
 240       if (! dst_res->exact)
 241         {
 242           res = -1;
 243           if (dfd == AT_FDCWD)
 244             res = pathconf (dst_name, _PC_TIMESTAMP_RESOLUTION);
 245           else
 246             {
 247               char *dstdir = mdir_name (dst_name);
 248               if (dstdir)
 249                 {
 250                   int destdirfd = openat (dfd, dstdir,
 251                                           O_SEARCH | O_CLOEXEC | O_DIRECTORY);
 252                   if (0 <= destdirfd)
 253                     {
 254                       res = fpathconf (destdirfd, _PC_TIMESTAMP_RESOLUTION);
 255                       close (destdirfd);
 256                     }
 257                   free (dstdir);
 258                 }
 259             }
 260           if (0 < res)
 261             {
 262               dst_res->resolution = res;
 263               dst_res->exact = true;
 264             }
 265         }
 266 #endif
 267 
 268       if (! dst_res->exact)
 269         {
 270           /* This file system's resolution is not known exactly.
 271              Deduce it, and store the result in the hash table.  */
 272 
 273           time_t dst_a_s = dst_stat->st_atime;
 274           time_t dst_c_s = dst_stat->st_ctime;
 275           time_t dst_m_s = dst_s;
 276           int dst_a_ns = get_stat_atime_ns (dst_stat);
 277           int dst_c_ns = get_stat_ctime_ns (dst_stat);
 278           int dst_m_ns = dst_ns;
 279 
 280           /* Set RES to an upper bound on the file system resolution
 281              (after truncation due to SYSCALL_RESOLUTION) by inspecting
 282              the atime, ctime and mtime of the existing destination.
 283              We don't know of any file system that stores atime or
 284              ctime with a higher precision than mtime, so it's valid to
 285              look at them too.  */
 286           {
 287             bool odd_second = (dst_a_s | dst_c_s | dst_m_s) & 1;
 288 
 289             if (SYSCALL_RESOLUTION == BILLION)
 290               {
 291                 if (odd_second | dst_a_ns | dst_c_ns | dst_m_ns)
 292                   res = BILLION;
 293               }
 294             else
 295               {
 296                 int a = dst_a_ns;
 297                 int c = dst_c_ns;
 298                 int m = dst_m_ns;
 299 
 300                 /* Write it this way to avoid mistaken GCC warning
 301                    about integer overflow in constant expression.  */
 302                 int SR10 = SYSCALL_RESOLUTION;  SR10 *= 10;
 303 
 304                 if ((a % SR10 | c % SR10 | m % SR10) != 0)
 305                   res = SYSCALL_RESOLUTION;
 306                 else
 307                   for (res = SR10, a /= SR10, c /= SR10, m /= SR10;
 308                        (res < dst_res->resolution
 309                         && (a % 10 | c % 10 | m % 10) == 0);
 310                        res *= 10, a /= 10, c /= 10, m /= 10)
 311                     if (res == BILLION)
 312                       {
 313                         if (! odd_second)
 314                           res *= 2;
 315                         break;
 316                       }
 317               }
 318 
 319             dst_res->resolution = res;
 320           }
 321 
 322           if (SYSCALL_RESOLUTION < res)
 323             {
 324               struct timespec timespec[2];
 325               struct stat dst_status;
 326 
 327               /* Ignore source timestamp information that must necessarily
 328                  be lost when filtered through utimens.  */
 329               src_ns -= src_ns % SYSCALL_RESOLUTION;
 330 
 331               /* If the timestamps disagree widely enough, there's no need
 332                  to interrogate the file system to deduce the exact
 333                  timestamp resolution; return the answer directly.  */
 334               {
 335                 time_t s = src_s & ~ (res == 2 * BILLION ? 1 : 0);
 336                 if (src_s < dst_s || (src_s == dst_s && src_ns <= dst_ns))
 337                   return 1;
 338                 if (dst_s < s
 339                     || (dst_s == s && dst_ns < src_ns - src_ns % res))
 340                   return -1;
 341               }
 342 
 343               /* Determine the actual timestamp resolution for the
 344                  destination file system (after truncation due to
 345                  SYSCALL_RESOLUTION) by setting the access timestamp of the
 346                  destination to the existing access time, except with
 347                  trailing nonzero digits.  */
 348 
 349               timespec[0].tv_sec = dst_a_s;
 350               timespec[0].tv_nsec = dst_a_ns;
 351               timespec[1].tv_sec = dst_m_s | (res == 2 * BILLION);
 352               timespec[1].tv_nsec = dst_m_ns + res / 9;
 353 
 354               if (utimensat (dfd, dst_name, timespec, AT_SYMLINK_NOFOLLOW))
 355                 return -2;
 356 
 357               /* Read the modification time that was set.  */
 358               {
 359                 int stat_result
 360                   = fstatat (dfd, dst_name, &dst_status, AT_SYMLINK_NOFOLLOW);
 361 
 362                 if (stat_result
 363                     | (dst_status.st_mtime ^ dst_m_s)
 364                     | (get_stat_mtime_ns (&dst_status) ^ dst_m_ns))
 365                   {
 366                     /* The modification time changed, or we can't tell whether
 367                        it changed.  Change it back as best we can.  */
 368                     timespec[1].tv_sec = dst_m_s;
 369                     timespec[1].tv_nsec = dst_m_ns;
 370                     utimensat (dfd, dst_name, timespec, AT_SYMLINK_NOFOLLOW);
 371                   }
 372 
 373                 if (stat_result != 0)
 374                   return -2;
 375               }
 376 
 377               /* Determine the exact resolution from the modification time
 378                  that was read back.  */
 379               {
 380                 int old_res = res;
 381                 int a = (BILLION * (dst_status.st_mtime & 1)
 382                          + get_stat_mtime_ns (&dst_status));
 383 
 384                 res = SYSCALL_RESOLUTION;
 385 
 386                 for (a /= res; a % 10 == 0; a /= 10)
 387                   {
 388                     if (res == BILLION)
 389                       {
 390                         res *= 2;
 391                         break;
 392                       }
 393                     res *= 10;
 394                     if (res == old_res)
 395                       break;
 396                   }
 397               }
 398             }
 399 
 400           dst_res->resolution = res;
 401           dst_res->exact = true;
 402         }
 403 
 404       /* Truncate the source's timestamp according to the resolution.  */
 405       src_s &= ~ (res == 2 * BILLION ? 1 : 0);
 406       src_ns -= src_ns % res;
 407     }
 408 
 409   /* Compare the timestamps and return -1, 0, 1 accordingly.  */
 410   return (_GL_CMP (dst_s, src_s)
 411           + ((dst_s == src_s ? ~0 : 0) & _GL_CMP (dst_ns, src_ns)));
 412 }

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