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