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