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