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