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