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