* _fpending.c: Include <config.h> unconditionally, since we no
[pspp] / lib / utimecmp.c
1 /* utimecmp.c -- compare file time stamps
2
3    Copyright (C) 2004, 2005, 2006 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 #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 "hash.h"
30 #include "intprops.h"
31 #include "stat-time.h"
32 #include "timespec.h"
33 #include "utimens.h"
34 #include "verify.h"
35 #include "xalloc.h"
36
37 #ifndef MAX
38 # define MAX(a, b) ((a) > (b) ? (a) : (b))
39 #endif
40
41 #ifndef SIZE_MAX
42 # define SIZE_MAX ((size_t) -1)
43 #endif
44
45 enum { BILLION = 1000 * 1000 * 1000 };
46
47 /* Best possible resolution that utimens can set and stat can return,
48    due to system-call limitations.  It must be a power of 10 that is
49    no greater than 1 billion.  */
50 #if (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    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.
125
126      int and long int might be 32 bits.  Many of the calculations store
127      numbers up to 2 billion, and multiply by 10; they have to avoid
128      multiplying 2 billion by 10, as this exceeds 32-bit capabilities.
129
130      time_t might be unsigned.  */
131
132   verify (TYPE_IS_INTEGER (time_t));
133   verify (TYPE_TWOS_COMPLEMENT (int));
134
135   /* Destination and source time stamps.  */
136   time_t dst_s = dst_stat->st_mtime;
137   time_t src_s = src_stat->st_mtime;
138   int dst_ns = get_stat_mtime_ns (dst_stat);
139   int src_ns = get_stat_mtime_ns (src_stat);
140
141   if (options & UTIMECMP_TRUNCATE_SOURCE)
142     {
143       /* Look up the time stamp resolution for the destination device.  */
144
145       /* Hash table for devices.  */
146       static Hash_table *ht;
147
148       /* Information about the destination file system.  */
149       static struct fs_res *new_dst_res;
150       struct fs_res *dst_res;
151
152       /* Time stamp resolution in nanoseconds.  */
153       int res;
154
155       if (! ht)
156         ht = hash_initialize (16, NULL, dev_info_hash, dev_info_compare, free);
157       if (! new_dst_res)
158         {
159           new_dst_res = xmalloc (sizeof *new_dst_res);
160           new_dst_res->resolution = 2 * BILLION;
161           new_dst_res->exact = false;
162         }
163       new_dst_res->dev = dst_stat->st_dev;
164       dst_res = hash_insert (ht, new_dst_res);
165       if (! dst_res)
166         xalloc_die ();
167
168       if (dst_res == new_dst_res)
169         {
170           /* NEW_DST_RES is now in use in the hash table, so allocate a
171              new entry next time.  */
172           new_dst_res = NULL;
173         }
174
175       res = dst_res->resolution;
176
177       if (! dst_res->exact)
178         {
179           /* This file system's resolution is not known exactly.
180              Deduce it, and store the result in the hash table.  */
181
182           time_t dst_a_s = dst_stat->st_atime;
183           time_t dst_c_s = dst_stat->st_ctime;
184           time_t dst_m_s = dst_s;
185           int dst_a_ns = get_stat_atime_ns (dst_stat);
186           int dst_c_ns = get_stat_ctime_ns (dst_stat);
187           int dst_m_ns = dst_ns;
188
189           /* Set RES to an upper bound on the file system resolution
190              (after truncation due to SYSCALL_RESOLUTION) by inspecting
191              the atime, ctime and mtime of the existing destination.
192              We don't know of any file system that stores atime or
193              ctime with a higher precision than mtime, so it's valid to
194              look at them too.  */
195           {
196             bool odd_second = (dst_a_s | dst_c_s | dst_m_s) & 1;
197
198             if (SYSCALL_RESOLUTION == BILLION)
199               {
200                 if (odd_second | dst_a_ns | dst_c_ns | dst_m_ns)
201                   res = BILLION;
202               }
203             else
204               {
205                 int a = dst_a_ns;
206                 int c = dst_c_ns;
207                 int m = dst_m_ns;
208
209                 /* Write it this way to avoid mistaken GCC warning
210                    about integer overflow in constant expression.  */
211                 int SR10 = SYSCALL_RESOLUTION;  SR10 *= 10;
212
213                 if ((a % SR10 | c % SR10 | m % SR10) != 0)
214                   res = SYSCALL_RESOLUTION;
215                 else
216                   for (res = SR10, a /= SR10, c /= SR10, m /= SR10;
217                        (res < dst_res->resolution
218                         && (a % 10 | c % 10 | m % 10) == 0);
219                        res *= 10, a /= 10, c /= 10, m /= 10)
220                     if (res == BILLION)
221                       {
222                         if (! odd_second)
223                           res *= 2;
224                         break;
225                       }
226               }
227
228             dst_res->resolution = res;
229           }
230
231           if (SYSCALL_RESOLUTION < res)
232             {
233               struct timespec timespec[2];
234               struct stat dst_status;
235
236               /* Ignore source time stamp information that must necessarily
237                  be lost when filtered through utimens.  */
238               src_ns -= src_ns % SYSCALL_RESOLUTION;
239
240               /* If the time stamps disagree widely enough, there's no need
241                  to interrogate the file system to deduce the exact time
242                  stamp resolution; return the answer directly.  */
243               {
244                 time_t s = src_s & ~ (res == 2 * BILLION);
245                 if (src_s < dst_s || (src_s == dst_s && src_ns <= dst_ns))
246                   return 1;
247                 if (dst_s < s
248                     || (dst_s == s && dst_ns < src_ns - src_ns % res))
249                   return -1;
250               }
251
252               /* Determine the actual time stamp resolution for the
253                  destination file system (after truncation due to
254                  SYSCALL_RESOLUTION) by setting the access time stamp of the
255                  destination to the existing access time, except with
256                  trailing nonzero digits.  */
257
258               timespec[0].tv_sec = dst_a_s;
259               timespec[0].tv_nsec = dst_a_ns;
260               timespec[1].tv_sec = dst_m_s | (res == 2 * BILLION);
261               timespec[1].tv_nsec = dst_m_ns + res / 9;
262
263               /* Set the modification time.  But don't try to set the
264                  modification time of symbolic links; on many hosts this sets
265                  the time of the pointed-to file.  */
266               if (S_ISLNK (dst_stat->st_mode)
267                   || utimens (dst_name, timespec) != 0)
268                 return -2;
269
270               /* Read the modification time that was set.  It's safe to call
271                  'stat' here instead of worrying about 'lstat'; either the
272                  caller used 'stat', or the caller used 'lstat' and found
273                  something other than a symbolic link.  */
274               {
275                 int stat_result = stat (dst_name, &dst_status);
276
277                 if (stat_result
278                     | (dst_status.st_mtime ^ dst_m_s)
279                     | (get_stat_mtime_ns (&dst_status) ^ dst_m_ns))
280                   {
281                     /* The modification time changed, or we can't tell whether
282                        it changed.  Change it back as best we can.  */
283                     timespec[1].tv_sec = dst_m_s;
284                     timespec[1].tv_nsec = dst_m_ns;
285                     utimens (dst_name, timespec);
286                   }
287
288                 if (stat_result != 0)
289                   return -2;
290               }
291
292               /* Determine the exact resolution from the modification time
293                  that was read back.  */
294               {
295                 int old_res = res;
296                 int a = (BILLION * (dst_status.st_mtime & 1)
297                          + get_stat_mtime_ns (&dst_status));
298
299                 res = SYSCALL_RESOLUTION;
300
301                 for (a /= res; a % 10 != 0; a /= 10)
302                   {
303                     if (res == BILLION)
304                       {
305                         res *= 2;
306                         break;
307                       }
308                     res *= 10;
309                     if (res == old_res)
310                       break;
311                   }
312               }
313             }
314
315           dst_res->resolution = res;
316           dst_res->exact = true;
317         }
318
319       /* Truncate the source's time stamp according to the resolution.  */
320       src_s &= ~ (res == 2 * BILLION);
321       src_ns -= src_ns % res;
322     }
323
324   /* Compare the time stamps and return -1, 0, 1 accordingly.  */
325   return (dst_s < src_s ? -1
326           : dst_s > src_s ? 1
327           : dst_ns < src_ns ? -1
328           : dst_ns > src_ns);
329 }