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