maint: update copyright
[gnulib.git] / lib / utimecmp.c
index de18944..49ea5f8 100644 (file)
@@ -1,11 +1,11 @@
 /* utimecmp.c -- compare file time stamps
 
-   Copyright (C) 2004, 2005 Free Software Foundation, Inc.
+   Copyright (C) 2004-2007, 2009-2014 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
-   the Free Software Foundation; either version 2, or (at your option)
-   any later version.
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
 
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    GNU General Public License for more details.
 
    You should have received a copy of the GNU General Public License
-   along with this program; if not, write to the Free Software Foundation,
-   Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
 /* Written by Paul Eggert.  */
 
-#if HAVE_CONFIG_H
-# include <config.h>
-#endif
+#include <config.h>
 
 #include "utimecmp.h"
 
-#if HAVE_INTTYPES_H
-# include <inttypes.h>
-#endif
-#if HAVE_STDINT_H
-# include <stdint.h>
-#endif
-
 #include <limits.h>
 #include <stdbool.h>
+#include <stdint.h>
 #include <stdlib.h>
+#include <time.h>
+#include <unistd.h>
+
 #include "hash.h"
 #include "intprops.h"
-#include "timespec.h"
+#include "stat-time.h"
 #include "utimens.h"
-#include "xalloc.h"
-
-/* Verify a requirement at compile-time (unlike assert, which is runtime).  */
-#define verify(name, assertion) struct name { char a[(assertion) ? 1 : -1]; }
+#include "verify.h"
 
 #ifndef MAX
 # define MAX(a, b) ((a) > (b) ? (a) : (b))
 #endif
 
-#ifndef SIZE_MAX
-# define SIZE_MAX ((size_t) -1)
-#endif
-
-enum { BILLION = 1000 * 1000 * 1000 };
+#define BILLION (1000 * 1000 * 1000)
 
 /* Best possible resolution that utimens can set and stat can return,
    due to system-call limitations.  It must be a power of 10 that is
    no greater than 1 billion.  */
-#if HAVE_WORKING_UTIMES && defined ST_MTIM_NSEC
+#if HAVE_UTIMENSAT
+enum { SYSCALL_RESOLUTION = 1 };
+#elif ((HAVE_FUTIMESAT || HAVE_WORKING_UTIMES)                  \
+       && (defined HAVE_STRUCT_STAT_ST_ATIM_TV_NSEC             \
+           || defined HAVE_STRUCT_STAT_ST_ATIMESPEC_TV_NSEC     \
+           || defined HAVE_STRUCT_STAT_ST_ATIMENSEC             \
+           || defined HAVE_STRUCT_STAT_ST_ATIM_ST__TIM_TV_NSEC  \
+           || defined HAVE_STRUCT_STAT_ST_SPARE1))
 enum { SYSCALL_RESOLUTION = 1000 };
 #else
 enum { SYSCALL_RESOLUTION = BILLION };
@@ -109,6 +103,8 @@ dev_info_compare (void const *x, void const *y)
    DST_NAME and status DST_STAT) is older than SRC_STAT, the same age
    as SRC_STAT, or newer than SRC_STAT, respectively.
 
+   DST_NAME may be NULL if OPTIONS is 0.
+
    If OPTIONS & UTIMECMP_TRUNCATE_SOURCE, do the comparison after SRC is
    converted to the destination's timestamp resolution as filtered through
    utimens.  In this case, return -2 if the exact answer cannot be
@@ -118,14 +114,16 @@ dev_info_compare (void const *x, void const *y)
 
 int
 utimecmp (char const *dst_name,
-         struct stat const *dst_stat,
-         struct stat const *src_stat,
-         int options)
+          struct stat const *dst_stat,
+          struct stat const *src_stat,
+          int options)
 {
   /* Things to watch out for:
 
      The code uses a static hash table internally and is not safe in the
-     presence of signals, multiple threads, etc.
+     presence of signals, multiple threads, etc.  However, memory pressure
+     that prevents use of the hash table is not fatal - we just fall back
+     to redoing the computations on every call in that case.
 
      int and long int might be 32 bits.  Many of the calculations store
      numbers up to 2 billion, and multiply by 10; they have to avoid
@@ -133,192 +131,241 @@ utimecmp (char const *dst_name,
 
      time_t might be unsigned.  */
 
-  verify (time_t_is_integer, TYPE_IS_INTEGER (time_t));
-  verify (twos_complement_arithmetic, TYPE_TWOS_COMPLEMENT (int));
+  verify (TYPE_IS_INTEGER (time_t));
+  verify (TYPE_TWOS_COMPLEMENT (int));
 
   /* Destination and source time stamps.  */
   time_t dst_s = dst_stat->st_mtime;
   time_t src_s = src_stat->st_mtime;
-  int dst_ns = TIMESPEC_NS (dst_stat->st_mtim);
-  int src_ns = TIMESPEC_NS (src_stat->st_mtim);
+  int dst_ns = get_stat_mtime_ns (dst_stat);
+  int src_ns = get_stat_mtime_ns (src_stat);
 
   if (options & UTIMECMP_TRUNCATE_SOURCE)
     {
       /* Look up the time stamp resolution for the destination device.  */
 
-      /* Hash table for devices.  */
+      /* Hash table for caching information learned about devices.  */
       static Hash_table *ht;
 
       /* Information about the destination file system.  */
       static struct fs_res *new_dst_res;
-      struct fs_res *dst_res;
+      struct fs_res *dst_res = NULL;
+      struct fs_res tmp_dst_res;
 
       /* Time stamp resolution in nanoseconds.  */
       int res;
 
+      /* Quick exit, if possible.  Since the worst resolution is 2
+         seconds, anything that differs by more than that does not
+         needs source truncation.  */
+      if (dst_s == src_s && dst_ns == src_ns)
+        return 0;
+      if (dst_s <= src_s - 2)
+        return -1;
+      if (src_s <= dst_s - 2)
+        return 1;
+
+      /* Try to do a hash lookup, but fall back to stack variables and
+         recomputation on low memory situations.  */
       if (! ht)
-       ht = hash_initialize (16, NULL, dev_info_hash, dev_info_compare, free);
-      if (! new_dst_res)
-       {
-         new_dst_res = xmalloc (sizeof *new_dst_res);
-         new_dst_res->resolution = 2 * BILLION;
-         new_dst_res->exact = false;
-       }
-      new_dst_res->dev = dst_stat->st_dev;
-      dst_res = hash_insert (ht, new_dst_res);
-      if (! dst_res)
-       xalloc_die ();
-
-      if (dst_res == new_dst_res)
-       {
-         /* NEW_DST_RES is now in use in the hash table, so allocate a
-            new entry next time.  */
-         new_dst_res = NULL;
-       }
+        ht = hash_initialize (16, NULL, dev_info_hash, dev_info_compare, free);
+      if (ht)
+        {
+          if (! new_dst_res)
+            {
+              new_dst_res = malloc (sizeof *new_dst_res);
+              if (!new_dst_res)
+                goto low_memory;
+              new_dst_res->resolution = 2 * BILLION;
+              new_dst_res->exact = false;
+            }
+          new_dst_res->dev = dst_stat->st_dev;
+          dst_res = hash_insert (ht, new_dst_res);
+          if (! dst_res)
+            goto low_memory;
+
+          if (dst_res == new_dst_res)
+            {
+              /* NEW_DST_RES is now in use in the hash table, so allocate a
+                 new entry next time.  */
+              new_dst_res = NULL;
+            }
+        }
+      else
+        {
+        low_memory:
+          if (ht)
+            {
+              tmp_dst_res.dev = dst_stat->st_dev;
+              dst_res = hash_lookup (ht, &tmp_dst_res);
+            }
+          if (!dst_res)
+            {
+              dst_res = &tmp_dst_res;
+              dst_res->resolution = 2 * BILLION;
+              dst_res->exact = false;
+            }
+        }
 
       res = dst_res->resolution;
 
+#ifdef _PC_TIMESTAMP_RESOLUTION
+      /* If the system will tell us the resolution, we're set!  */
+      if (! dst_res->exact)
+        {
+          res = pathconf (dst_name, _PC_TIMESTAMP_RESOLUTION);
+          if (0 < res)
+            {
+              dst_res->resolution = res;
+              dst_res->exact = true;
+            }
+        }
+#endif
+
       if (! dst_res->exact)
-       {
-         /* This file system's resolution is not known exactly.
-            Deduce it, and store the result in the hash table.  */
-
-         time_t dst_a_s = dst_stat->st_atime;
-         time_t dst_c_s = dst_stat->st_ctime;
-         time_t dst_m_s = dst_s;
-         int dst_a_ns = TIMESPEC_NS (dst_stat->st_atim);
-         int dst_c_ns = TIMESPEC_NS (dst_stat->st_ctim);
-         int dst_m_ns = dst_ns;
-
-         /* Set RES to an upper bound on the file system resolution
-            (after truncation due to SYSCALL_RESOLUTION) by inspecting
-            the atime, ctime and mtime of the existing destination.
-            We don't know of any file system that stores atime or
-            ctime with a higher precision than mtime, so it's valid to
-            look at them too.  */
-         {
-           bool odd_second = (dst_a_s | dst_c_s | dst_m_s) & 1;
-
-           if (SYSCALL_RESOLUTION == BILLION)
-             {
-               if (odd_second | dst_a_ns | dst_c_ns | dst_m_ns)
-                 res = BILLION;
-             }
-           else
-             {
-               int a = dst_a_ns;
-               int c = dst_c_ns;
-               int m = dst_m_ns;
-
-               /* Write it this way to avoid mistaken GCC warning
-                  about integer overflow in constant expression.  */
-               int SR10 = SYSCALL_RESOLUTION;  SR10 *= 10;
-
-               if ((a % SR10 | c % SR10 | m % SR10) != 0)
-                 res = SYSCALL_RESOLUTION;
-               else
-                 for (res = SR10, a /= SR10, c /= SR10, m /= SR10;
-                      (res < dst_res->resolution
-                       && (a % 10 | c % 10 | m % 10) == 0);
-                      res *= 10, a /= 10, c /= 10, m /= 10)
-                   if (res == BILLION)
-                     {
-                       if (! odd_second)
-                         res *= 2;
-                       break;
-                     }
-             }
-
-           dst_res->resolution = res;
-         }
-
-         if (SYSCALL_RESOLUTION < res)
-           {
-             struct timespec timespec[2];
-             struct stat dst_status;
-
-             /* Ignore source time stamp information that must necessarily
-                be lost when filtered through utimens.  */
-             src_ns -= src_ns % SYSCALL_RESOLUTION;
-
-             /* If the time stamps disagree widely enough, there's no need
-                to interrogate the file system to deduce the exact time
-                stamp resolution; return the answer directly.  */
-             {
-               time_t s = src_s & ~ (res == 2 * BILLION);
-               if (src_s < dst_s || (src_s == dst_s && src_ns <= dst_ns))
-                 return 1;
-               if (dst_s < s
-                   || (dst_s == s && dst_ns < src_ns - src_ns % res))
-                 return -1;
-             }
-
-             /* Determine the actual time stamp resolution for the
-                destination file system (after truncation due to
-                SYSCALL_RESOLUTION) by setting the access time stamp of the
-                destination to the existing access time, except with
-                trailing nonzero digits.  */
-
-             timespec[0].tv_sec = dst_a_s;
-             timespec[0].tv_nsec = dst_a_ns;
-             timespec[1].tv_sec = dst_m_s | (res == 2 * BILLION);
-             timespec[1].tv_nsec = dst_m_ns + res / 9;
-
-             /* Set the modification time.  But don't try to set the
-                modification time of symbolic links; on many hosts this sets
-                the time of the pointed-to file.  */
-             if (S_ISLNK (dst_stat->st_mode)
-                 || utimens (dst_name, timespec) != 0)
-               return -2;
-
-             /* Read the modification time that was set.  It's safe to call
-                'stat' here instead of worrying about 'lstat'; either the
-                caller used 'stat', or the caller used 'lstat' and found
-                something other than a symbolic link.  */
-             {
-               int stat_result = stat (dst_name, &dst_status);
-
-               if (stat_result
-                   | (dst_status.st_mtime ^ dst_m_s)
-                   | (TIMESPEC_NS (dst_status.st_mtim) ^ dst_m_ns))
-                 {
-                   /* The modification time changed, or we can't tell whether
-                      it changed.  Change it back as best we can.  */
-                   timespec[1].tv_sec = dst_m_s;
-                   timespec[1].tv_nsec = dst_m_ns;
-                   utimens (dst_name, timespec);
-                 }
-
-               if (stat_result != 0)
-                 return -2;
-             }
-
-             /* Determine the exact resolution from the modification time
-                that was read back.  */
-             {
-               int old_res = res;
-               int a = (BILLION * (dst_status.st_mtime & 1)
-                        + TIMESPEC_NS (dst_status.st_mtim));
-
-               res = SYSCALL_RESOLUTION;
-
-               for (a /= res; a % 10 != 0; a /= 10)
-                 {
-                   if (res == BILLION)
-                     {
-                       res *= 2;
-                       break;
-                     }
-                   res *= 10;
-                   if (res == old_res)
-                     break;
-                 }
-             }
-           }
-
-         dst_res->resolution = res;
-         dst_res->exact = true;
-       }
+        {
+          /* This file system's resolution is not known exactly.
+             Deduce it, and store the result in the hash table.  */
+
+          time_t dst_a_s = dst_stat->st_atime;
+          time_t dst_c_s = dst_stat->st_ctime;
+          time_t dst_m_s = dst_s;
+          int dst_a_ns = get_stat_atime_ns (dst_stat);
+          int dst_c_ns = get_stat_ctime_ns (dst_stat);
+          int dst_m_ns = dst_ns;
+
+          /* Set RES to an upper bound on the file system resolution
+             (after truncation due to SYSCALL_RESOLUTION) by inspecting
+             the atime, ctime and mtime of the existing destination.
+             We don't know of any file system that stores atime or
+             ctime with a higher precision than mtime, so it's valid to
+             look at them too.  */
+          {
+            bool odd_second = (dst_a_s | dst_c_s | dst_m_s) & 1;
+
+            if (SYSCALL_RESOLUTION == BILLION)
+              {
+                if (odd_second | dst_a_ns | dst_c_ns | dst_m_ns)
+                  res = BILLION;
+              }
+            else
+              {
+                int a = dst_a_ns;
+                int c = dst_c_ns;
+                int m = dst_m_ns;
+
+                /* Write it this way to avoid mistaken GCC warning
+                   about integer overflow in constant expression.  */
+                int SR10 = SYSCALL_RESOLUTION;  SR10 *= 10;
+
+                if ((a % SR10 | c % SR10 | m % SR10) != 0)
+                  res = SYSCALL_RESOLUTION;
+                else
+                  for (res = SR10, a /= SR10, c /= SR10, m /= SR10;
+                       (res < dst_res->resolution
+                        && (a % 10 | c % 10 | m % 10) == 0);
+                       res *= 10, a /= 10, c /= 10, m /= 10)
+                    if (res == BILLION)
+                      {
+                        if (! odd_second)
+                          res *= 2;
+                        break;
+                      }
+              }
+
+            dst_res->resolution = res;
+          }
+
+          if (SYSCALL_RESOLUTION < res)
+            {
+              struct timespec timespec[2];
+              struct stat dst_status;
+
+              /* Ignore source time stamp information that must necessarily
+                 be lost when filtered through utimens.  */
+              src_ns -= src_ns % SYSCALL_RESOLUTION;
+
+              /* If the time stamps disagree widely enough, there's no need
+                 to interrogate the file system to deduce the exact time
+                 stamp resolution; return the answer directly.  */
+              {
+                time_t s = src_s & ~ (res == 2 * BILLION);
+                if (src_s < dst_s || (src_s == dst_s && src_ns <= dst_ns))
+                  return 1;
+                if (dst_s < s
+                    || (dst_s == s && dst_ns < src_ns - src_ns % res))
+                  return -1;
+              }
+
+              /* Determine the actual time stamp resolution for the
+                 destination file system (after truncation due to
+                 SYSCALL_RESOLUTION) by setting the access time stamp of the
+                 destination to the existing access time, except with
+                 trailing nonzero digits.  */
+
+              timespec[0].tv_sec = dst_a_s;
+              timespec[0].tv_nsec = dst_a_ns;
+              timespec[1].tv_sec = dst_m_s | (res == 2 * BILLION);
+              timespec[1].tv_nsec = dst_m_ns + res / 9;
+
+              /* Set the modification time.  But don't try to set the
+                 modification time of symbolic links; on many hosts this sets
+                 the time of the pointed-to file.  */
+              if ((S_ISLNK (dst_stat->st_mode)
+                   ? lutimens (dst_name, timespec)
+                   : utimens (dst_name, timespec)) != 0)
+                return -2;
+
+              /* Read the modification time that was set.  */
+              {
+                int stat_result = (S_ISLNK (dst_stat->st_mode)
+                                   ? lstat (dst_name, &dst_status)
+                                   : stat (dst_name, &dst_status));
+
+                if (stat_result
+                    | (dst_status.st_mtime ^ dst_m_s)
+                    | (get_stat_mtime_ns (&dst_status) ^ dst_m_ns))
+                  {
+                    /* The modification time changed, or we can't tell whether
+                       it changed.  Change it back as best we can.  */
+                    timespec[1].tv_sec = dst_m_s;
+                    timespec[1].tv_nsec = dst_m_ns;
+                    if (S_ISLNK (dst_stat->st_mode))
+                      lutimens (dst_name, timespec);
+                    else
+                      utimens (dst_name, timespec);
+                  }
+
+                if (stat_result != 0)
+                  return -2;
+              }
+
+              /* Determine the exact resolution from the modification time
+                 that was read back.  */
+              {
+                int old_res = res;
+                int a = (BILLION * (dst_status.st_mtime & 1)
+                         + get_stat_mtime_ns (&dst_status));
+
+                res = SYSCALL_RESOLUTION;
+
+                for (a /= res; a % 10 == 0; a /= 10)
+                  {
+                    if (res == BILLION)
+                      {
+                        res *= 2;
+                        break;
+                      }
+                    res *= 10;
+                    if (res == old_res)
+                      break;
+                  }
+              }
+            }
+
+          dst_res->resolution = res;
+          dst_res->exact = true;
+        }
 
       /* Truncate the source's time stamp according to the resolution.  */
       src_s &= ~ (res == 2 * BILLION);
@@ -327,7 +374,7 @@ utimecmp (char const *dst_name,
 
   /* Compare the time stamps and return -1, 0, 1 accordingly.  */
   return (dst_s < src_s ? -1
-         : dst_s > src_s ? 1
-         : dst_ns < src_ns ? -1
-         : dst_ns > src_ns);
+          : dst_s > src_s ? 1
+          : dst_ns < src_ns ? -1
+          : dst_ns > src_ns);
 }