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