LCOV - code coverage report
Current view: top level - lib - utimecmp.c (source / functions) Hit Total Coverage
Test: coreutils.info Lines: 7 91 7.7 %
Date: 2018-01-30 Functions: 1 3 33.3 %

          Line data    Source code
       1             : /* utimecmp.c -- compare file time stamps
       2             : 
       3             :    Copyright (C) 2004, 2005, 2006, 2007 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 <http://www.gnu.org/licenses/>.  */
      17             : 
      18             : /* Written by Paul Eggert.  */
      19             : 
      20             : #include <config.h>
      21             : 
      22             : #include "utimecmp.h"
      23             : 
      24             : #include <limits.h>
      25             : #include <stdbool.h>
      26             : #include <stdint.h>
      27             : #include <stdlib.h>
      28             : #include <time.h>
      29             : #include "hash.h"
      30             : #include "intprops.h"
      31             : #include "stat-time.h"
      32             : #include "utimens.h"
      33             : #include "verify.h"
      34             : #include "xalloc.h"
      35             : 
      36             : #ifndef MAX
      37             : # define MAX(a, b) ((a) > (b) ? (a) : (b))
      38             : #endif
      39             : 
      40             : enum { BILLION = 1000 * 1000 * 1000 };
      41             : 
      42             : /* Best possible resolution that utimens can set and stat can return,
      43             :    due to system-call limitations.  It must be a power of 10 that is
      44             :    no greater than 1 billion.  */
      45             : #if (HAVE_WORKING_UTIMES                                        \
      46             :      && (defined HAVE_STRUCT_STAT_ST_ATIM_TV_NSEC               \
      47             :          || defined HAVE_STRUCT_STAT_ST_ATIMESPEC_TV_NSEC       \
      48             :          || defined HAVE_STRUCT_STAT_ST_ATIMENSEC               \
      49             :          || defined HAVE_STRUCT_STAT_ST_ATIM_ST__TIM_TV_NSEC    \
      50             :          || defined HAVE_STRUCT_STAT_ST_SPARE1))
      51             : enum { SYSCALL_RESOLUTION = 1000 };
      52             : #else
      53             : enum { SYSCALL_RESOLUTION = BILLION };
      54             : #endif
      55             : 
      56             : /* Describe a file system and its time stamp resolution in nanoseconds.  */
      57             : struct fs_res
      58             : {
      59             :   /* Device number of file system.  */
      60             :   dev_t dev;
      61             : 
      62             :   /* An upper bound on the time stamp resolution of this file system,
      63             :      ignoring any resolution that cannot be set via utimens.  It is
      64             :      represented by an integer count of nanoseconds.  It must be
      65             :      either 2 billion, or a power of 10 that is no greater than a
      66             :      billion and is no less than SYSCALL_RESOLUTION.  */
      67             :   int resolution;
      68             : 
      69             :   /* True if RESOLUTION is known to be exact, and is not merely an
      70             :      upper bound on the true resolution.  */
      71             :   bool exact;
      72             : };
      73             : 
      74             : /* Hash some device info.  */
      75             : static size_t
      76           0 : dev_info_hash (void const *x, size_t table_size)
      77             : {
      78           0 :   struct fs_res const *p = x;
      79             : 
      80             :   /* Beware signed arithmetic gotchas.  */
      81             :   if (TYPE_SIGNED (dev_t) && SIZE_MAX < MAX (INT_MAX, TYPE_MAXIMUM (dev_t)))
      82             :     {
      83             :       uintmax_t dev = p->dev;
      84             :       return dev % table_size;
      85             :     }
      86             : 
      87           0 :   return p->dev % table_size;
      88             : }
      89             : 
      90             : /* Compare two dev_info structs.  */
      91             : static bool
      92           0 : dev_info_compare (void const *x, void const *y)
      93             : {
      94           0 :   struct fs_res const *a = x;
      95           0 :   struct fs_res const *b = y;
      96           0 :   return a->dev == b->dev;
      97             : }
      98             : 
      99             : /* Return -1, 0, 1 based on whether the destination file (with name
     100             :    DST_NAME and status DST_STAT) is older than SRC_STAT, the same age
     101             :    as SRC_STAT, or newer than SRC_STAT, respectively.
     102             : 
     103             :    If OPTIONS & UTIMECMP_TRUNCATE_SOURCE, do the comparison after SRC is
     104             :    converted to the destination's timestamp resolution as filtered through
     105             :    utimens.  In this case, return -2 if the exact answer cannot be
     106             :    determined; this can happen only if the time stamps are very close and
     107             :    there is some trouble accessing the file system (e.g., the user does not
     108             :    have permission to futz with the destination's time stamps).  */
     109             : 
     110             : int
     111           5 : utimecmp (char const *dst_name,
     112             :           struct stat const *dst_stat,
     113             :           struct stat const *src_stat,
     114             :           int options)
     115             : {
     116             :   /* Things to watch out for:
     117             : 
     118             :      The code uses a static hash table internally and is not safe in the
     119             :      presence of signals, multiple threads, etc.
     120             : 
     121             :      int and long int might be 32 bits.  Many of the calculations store
     122             :      numbers up to 2 billion, and multiply by 10; they have to avoid
     123             :      multiplying 2 billion by 10, as this exceeds 32-bit capabilities.
     124             : 
     125             :      time_t might be unsigned.  */
     126             : 
     127             :   verify (TYPE_IS_INTEGER (time_t));
     128             :   verify (TYPE_TWOS_COMPLEMENT (int));
     129             : 
     130             :   /* Destination and source time stamps.  */
     131           5 :   time_t dst_s = dst_stat->st_mtime;
     132           5 :   time_t src_s = src_stat->st_mtime;
     133           5 :   int dst_ns = get_stat_mtime_ns (dst_stat);
     134           5 :   int src_ns = get_stat_mtime_ns (src_stat);
     135             : 
     136           5 :   if (options & UTIMECMP_TRUNCATE_SOURCE)
     137             :     {
     138             :       /* Look up the time stamp resolution for the destination device.  */
     139             : 
     140             :       /* Hash table for devices.  */
     141             :       static Hash_table *ht;
     142             : 
     143             :       /* Information about the destination file system.  */
     144             :       static struct fs_res *new_dst_res;
     145             :       struct fs_res *dst_res;
     146             : 
     147             :       /* Time stamp resolution in nanoseconds.  */
     148             :       int res;
     149             : 
     150           0 :       if (! ht)
     151           0 :         ht = hash_initialize (16, NULL, dev_info_hash, dev_info_compare, free);
     152           0 :       if (! new_dst_res)
     153             :         {
     154           0 :           new_dst_res = xmalloc (sizeof *new_dst_res);
     155           0 :           new_dst_res->resolution = 2 * BILLION;
     156           0 :           new_dst_res->exact = false;
     157             :         }
     158           0 :       new_dst_res->dev = dst_stat->st_dev;
     159           0 :       dst_res = hash_insert (ht, new_dst_res);
     160           0 :       if (! dst_res)
     161           0 :         xalloc_die ();
     162             : 
     163           0 :       if (dst_res == new_dst_res)
     164             :         {
     165             :           /* NEW_DST_RES is now in use in the hash table, so allocate a
     166             :              new entry next time.  */
     167           0 :           new_dst_res = NULL;
     168             :         }
     169             : 
     170           0 :       res = dst_res->resolution;
     171             : 
     172           0 :       if (! dst_res->exact)
     173             :         {
     174             :           /* This file system's resolution is not known exactly.
     175             :              Deduce it, and store the result in the hash table.  */
     176             : 
     177           0 :           time_t dst_a_s = dst_stat->st_atime;
     178           0 :           time_t dst_c_s = dst_stat->st_ctime;
     179           0 :           time_t dst_m_s = dst_s;
     180           0 :           int dst_a_ns = get_stat_atime_ns (dst_stat);
     181           0 :           int dst_c_ns = get_stat_ctime_ns (dst_stat);
     182           0 :           int dst_m_ns = dst_ns;
     183             : 
     184             :           /* Set RES to an upper bound on the file system resolution
     185             :              (after truncation due to SYSCALL_RESOLUTION) by inspecting
     186             :              the atime, ctime and mtime of the existing destination.
     187             :              We don't know of any file system that stores atime or
     188             :              ctime with a higher precision than mtime, so it's valid to
     189             :              look at them too.  */
     190             :           {
     191           0 :             bool odd_second = (dst_a_s | dst_c_s | dst_m_s) & 1;
     192             : 
     193             :             if (SYSCALL_RESOLUTION == BILLION)
     194             :               {
     195             :                 if (odd_second | dst_a_ns | dst_c_ns | dst_m_ns)
     196             :                   res = BILLION;
     197             :               }
     198             :             else
     199             :               {
     200           0 :                 int a = dst_a_ns;
     201           0 :                 int c = dst_c_ns;
     202           0 :                 int m = dst_m_ns;
     203             : 
     204             :                 /* Write it this way to avoid mistaken GCC warning
     205             :                    about integer overflow in constant expression.  */
     206           0 :                 int SR10 = SYSCALL_RESOLUTION;  SR10 *= 10;
     207             : 
     208           0 :                 if ((a % SR10 | c % SR10 | m % SR10) != 0)
     209           0 :                   res = SYSCALL_RESOLUTION;
     210             :                 else
     211           0 :                   for (res = SR10, a /= SR10, c /= SR10, m /= SR10;
     212           0 :                        (res < dst_res->resolution
     213           0 :                         && (a % 10 | c % 10 | m % 10) == 0);
     214           0 :                        res *= 10, a /= 10, c /= 10, m /= 10)
     215           0 :                     if (res == BILLION)
     216             :                       {
     217           0 :                         if (! odd_second)
     218           0 :                           res *= 2;
     219           0 :                         break;
     220             :                       }
     221             :               }
     222             : 
     223           0 :             dst_res->resolution = res;
     224             :           }
     225             : 
     226           0 :           if (SYSCALL_RESOLUTION < res)
     227             :             {
     228             :               struct timespec timespec[2];
     229             :               struct stat dst_status;
     230             : 
     231             :               /* Ignore source time stamp information that must necessarily
     232             :                  be lost when filtered through utimens.  */
     233           0 :               src_ns -= src_ns % SYSCALL_RESOLUTION;
     234             : 
     235             :               /* If the time stamps disagree widely enough, there's no need
     236             :                  to interrogate the file system to deduce the exact time
     237             :                  stamp resolution; return the answer directly.  */
     238             :               {
     239           0 :                 time_t s = src_s & ~ (res == 2 * BILLION);
     240           0 :                 if (src_s < dst_s || (src_s == dst_s && src_ns <= dst_ns))
     241           0 :                   return 1;
     242           0 :                 if (dst_s < s
     243           0 :                     || (dst_s == s && dst_ns < src_ns - src_ns % res))
     244           0 :                   return -1;
     245             :               }
     246             : 
     247             :               /* Determine the actual time stamp resolution for the
     248             :                  destination file system (after truncation due to
     249             :                  SYSCALL_RESOLUTION) by setting the access time stamp of the
     250             :                  destination to the existing access time, except with
     251             :                  trailing nonzero digits.  */
     252             : 
     253           0 :               timespec[0].tv_sec = dst_a_s;
     254           0 :               timespec[0].tv_nsec = dst_a_ns;
     255           0 :               timespec[1].tv_sec = dst_m_s | (res == 2 * BILLION);
     256           0 :               timespec[1].tv_nsec = dst_m_ns + res / 9;
     257             : 
     258             :               /* Set the modification time.  But don't try to set the
     259             :                  modification time of symbolic links; on many hosts this sets
     260             :                  the time of the pointed-to file.  */
     261           0 :               if (S_ISLNK (dst_stat->st_mode)
     262           0 :                   || utimens (dst_name, timespec) != 0)
     263           0 :                 return -2;
     264             : 
     265             :               /* Read the modification time that was set.  It's safe to call
     266             :                  'stat' here instead of worrying about 'lstat'; either the
     267             :                  caller used 'stat', or the caller used 'lstat' and found
     268             :                  something other than a symbolic link.  */
     269             :               {
     270           0 :                 int stat_result = stat (dst_name, &dst_status);
     271             : 
     272           0 :                 if (stat_result
     273           0 :                     | (dst_status.st_mtime ^ dst_m_s)
     274           0 :                     | (get_stat_mtime_ns (&dst_status) ^ dst_m_ns))
     275             :                   {
     276             :                     /* The modification time changed, or we can't tell whether
     277             :                        it changed.  Change it back as best we can.  */
     278           0 :                     timespec[1].tv_sec = dst_m_s;
     279           0 :                     timespec[1].tv_nsec = dst_m_ns;
     280           0 :                     utimens (dst_name, timespec);
     281             :                   }
     282             : 
     283           0 :                 if (stat_result != 0)
     284           0 :                   return -2;
     285             :               }
     286             : 
     287             :               /* Determine the exact resolution from the modification time
     288             :                  that was read back.  */
     289             :               {
     290           0 :                 int old_res = res;
     291           0 :                 int a = (BILLION * (dst_status.st_mtime & 1)
     292           0 :                          + get_stat_mtime_ns (&dst_status));
     293             : 
     294           0 :                 res = SYSCALL_RESOLUTION;
     295             : 
     296           0 :                 for (a /= res; a % 10 != 0; a /= 10)
     297             :                   {
     298           0 :                     if (res == BILLION)
     299             :                       {
     300           0 :                         res *= 2;
     301           0 :                         break;
     302             :                       }
     303           0 :                     res *= 10;
     304           0 :                     if (res == old_res)
     305           0 :                       break;
     306             :                   }
     307             :               }
     308             :             }
     309             : 
     310           0 :           dst_res->resolution = res;
     311           0 :           dst_res->exact = true;
     312             :         }
     313             : 
     314             :       /* Truncate the source's time stamp according to the resolution.  */
     315           0 :       src_s &= ~ (res == 2 * BILLION);
     316           0 :       src_ns -= src_ns % res;
     317             :     }
     318             : 
     319             :   /* Compare the time stamps and return -1, 0, 1 accordingly.  */
     320             :   return (dst_s < src_s ? -1
     321           5 :           : dst_s > src_s ? 1
     322           0 :           : dst_ns < src_ns ? -1
     323           0 :           : dst_ns > src_ns);
     324             : }

Generated by: LCOV version 1.10