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