Bump to 1.14.1
[platform/upstream/augeas.git] / lib / utimecmp.c
1 /* utimecmp.c -- compare file time stamps
2
3    Copyright (C) 2004-2007, 2009-2016 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 <unistd.h>
30
31 #include "hash.h"
32 #include "intprops.h"
33 #include "stat-time.h"
34 #include "utimens.h"
35 #include "verify.h"
36
37 #ifndef MAX
38 # define MAX(a, b) ((a) > (b) ? (a) : (b))
39 #endif
40
41 #define BILLION (1000 * 1000 * 1000)
42
43 /* Best possible resolution that utimens can set and stat can return,
44    due to system-call limitations.  It must be a power of 10 that is
45    no greater than 1 billion.  */
46 #if HAVE_UTIMENSAT
47 enum { SYSCALL_RESOLUTION = 1 };
48 #elif ((HAVE_FUTIMESAT || HAVE_WORKING_UTIMES)                  \
49        && (defined HAVE_STRUCT_STAT_ST_ATIM_TV_NSEC             \
50            || defined HAVE_STRUCT_STAT_ST_ATIMESPEC_TV_NSEC     \
51            || defined HAVE_STRUCT_STAT_ST_ATIMENSEC             \
52            || defined HAVE_STRUCT_STAT_ST_ATIM_ST__TIM_TV_NSEC  \
53            || defined HAVE_STRUCT_STAT_ST_SPARE1))
54 enum { SYSCALL_RESOLUTION = 1000 };
55 #else
56 enum { SYSCALL_RESOLUTION = BILLION };
57 #endif
58
59 /* Describe a file system and its time stamp resolution in nanoseconds.  */
60 struct fs_res
61 {
62   /* Device number of file system.  */
63   dev_t dev;
64
65   /* An upper bound on the time stamp resolution of this file system,
66      ignoring any resolution that cannot be set via utimens.  It is
67      represented by an integer count of nanoseconds.  It must be
68      either 2 billion, or a power of 10 that is no greater than a
69      billion and is no less than SYSCALL_RESOLUTION.  */
70   int resolution;
71
72   /* True if RESOLUTION is known to be exact, and is not merely an
73      upper bound on the true resolution.  */
74   bool exact;
75 };
76
77 /* Hash some device info.  */
78 static size_t
79 dev_info_hash (void const *x, size_t table_size)
80 {
81   struct fs_res const *p = x;
82
83   /* Beware signed arithmetic gotchas.  */
84   if (TYPE_SIGNED (dev_t) && SIZE_MAX < MAX (INT_MAX, TYPE_MAXIMUM (dev_t)))
85     {
86       uintmax_t dev = p->dev;
87       return dev % table_size;
88     }
89
90   return p->dev % table_size;
91 }
92
93 /* Compare two dev_info structs.  */
94 static bool
95 dev_info_compare (void const *x, void const *y)
96 {
97   struct fs_res const *a = x;
98   struct fs_res const *b = y;
99   return a->dev == b->dev;
100 }
101
102 /* Return -1, 0, 1 based on whether the destination file (with name
103    DST_NAME and status DST_STAT) is older than SRC_STAT, the same age
104    as SRC_STAT, or newer than SRC_STAT, respectively.
105
106    DST_NAME may be NULL if OPTIONS is 0.
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.  However, memory pressure
125      that prevents use of the hash table is not fatal - we just fall back
126      to redoing the computations on every call in that case.
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
136   /* Destination and source time stamps.  */
137   time_t dst_s = dst_stat->st_mtime;
138   time_t src_s = src_stat->st_mtime;
139   int dst_ns = get_stat_mtime_ns (dst_stat);
140   int src_ns = get_stat_mtime_ns (src_stat);
141
142   if (options & UTIMECMP_TRUNCATE_SOURCE)
143     {
144       /* Look up the time stamp resolution for the destination device.  */
145
146       /* Hash table for caching information learned about devices.  */
147       static Hash_table *ht;
148
149       /* Information about the destination file system.  */
150       static struct fs_res *new_dst_res;
151       struct fs_res *dst_res = NULL;
152       struct fs_res tmp_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       /* Try to do a hash lookup, but fall back to stack variables and
168          recomputation on low memory situations.  */
169       if (! ht)
170         ht = hash_initialize (16, NULL, dev_info_hash, dev_info_compare, free);
171       if (ht)
172         {
173           if (! new_dst_res)
174             {
175               new_dst_res = malloc (sizeof *new_dst_res);
176               if (!new_dst_res)
177                 goto low_memory;
178               new_dst_res->resolution = 2 * BILLION;
179               new_dst_res->exact = false;
180             }
181           new_dst_res->dev = dst_stat->st_dev;
182           dst_res = hash_insert (ht, new_dst_res);
183           if (! dst_res)
184             goto low_memory;
185
186           if (dst_res == new_dst_res)
187             {
188               /* NEW_DST_RES is now in use in the hash table, so allocate a
189                  new entry next time.  */
190               new_dst_res = NULL;
191             }
192         }
193       else
194         {
195         low_memory:
196           if (ht)
197             {
198               tmp_dst_res.dev = dst_stat->st_dev;
199               dst_res = hash_lookup (ht, &tmp_dst_res);
200             }
201           if (!dst_res)
202             {
203               dst_res = &tmp_dst_res;
204               dst_res->resolution = 2 * BILLION;
205               dst_res->exact = false;
206             }
207         }
208
209       res = dst_res->resolution;
210
211 #ifdef _PC_TIMESTAMP_RESOLUTION
212       /* If the system will tell us the resolution, we're set!  */
213       if (! dst_res->exact)
214         {
215           res = pathconf (dst_name, _PC_TIMESTAMP_RESOLUTION);
216           if (0 < res)
217             {
218               dst_res->resolution = res;
219               dst_res->exact = true;
220             }
221         }
222 #endif
223
224       if (! dst_res->exact)
225         {
226           /* This file system's resolution is not known exactly.
227              Deduce it, and store the result in the hash table.  */
228
229           time_t dst_a_s = dst_stat->st_atime;
230           time_t dst_c_s = dst_stat->st_ctime;
231           time_t dst_m_s = dst_s;
232           int dst_a_ns = get_stat_atime_ns (dst_stat);
233           int dst_c_ns = get_stat_ctime_ns (dst_stat);
234           int dst_m_ns = dst_ns;
235
236           /* Set RES to an upper bound on the file system resolution
237              (after truncation due to SYSCALL_RESOLUTION) by inspecting
238              the atime, ctime and mtime of the existing destination.
239              We don't know of any file system that stores atime or
240              ctime with a higher precision than mtime, so it's valid to
241              look at them too.  */
242           {
243             bool odd_second = (dst_a_s | dst_c_s | dst_m_s) & 1;
244
245             if (SYSCALL_RESOLUTION == BILLION)
246               {
247                 if (odd_second | dst_a_ns | dst_c_ns | dst_m_ns)
248                   res = BILLION;
249               }
250             else
251               {
252                 int a = dst_a_ns;
253                 int c = dst_c_ns;
254                 int m = dst_m_ns;
255
256                 /* Write it this way to avoid mistaken GCC warning
257                    about integer overflow in constant expression.  */
258                 int SR10 = SYSCALL_RESOLUTION;  SR10 *= 10;
259
260                 if ((a % SR10 | c % SR10 | m % SR10) != 0)
261                   res = SYSCALL_RESOLUTION;
262                 else
263                   for (res = SR10, a /= SR10, c /= SR10, m /= SR10;
264                        (res < dst_res->resolution
265                         && (a % 10 | c % 10 | m % 10) == 0);
266                        res *= 10, a /= 10, c /= 10, m /= 10)
267                     if (res == BILLION)
268                       {
269                         if (! odd_second)
270                           res *= 2;
271                         break;
272                       }
273               }
274
275             dst_res->resolution = res;
276           }
277
278           if (SYSCALL_RESOLUTION < res)
279             {
280               struct timespec timespec[2];
281               struct stat dst_status;
282
283               /* Ignore source time stamp information that must necessarily
284                  be lost when filtered through utimens.  */
285               src_ns -= src_ns % SYSCALL_RESOLUTION;
286
287               /* If the time stamps disagree widely enough, there's no need
288                  to interrogate the file system to deduce the exact time
289                  stamp resolution; return the answer directly.  */
290               {
291                 time_t s = src_s & ~ (res == 2 * BILLION ? 1 : 0);
292                 if (src_s < dst_s || (src_s == dst_s && src_ns <= dst_ns))
293                   return 1;
294                 if (dst_s < s
295                     || (dst_s == s && dst_ns < src_ns - src_ns % res))
296                   return -1;
297               }
298
299               /* Determine the actual time stamp resolution for the
300                  destination file system (after truncation due to
301                  SYSCALL_RESOLUTION) by setting the access time stamp of the
302                  destination to the existing access time, except with
303                  trailing nonzero digits.  */
304
305               timespec[0].tv_sec = dst_a_s;
306               timespec[0].tv_nsec = dst_a_ns;
307               timespec[1].tv_sec = dst_m_s | (res == 2 * BILLION);
308               timespec[1].tv_nsec = dst_m_ns + res / 9;
309
310               /* Set the modification time.  But don't try to set the
311                  modification time of symbolic links; on many hosts this sets
312                  the time of the pointed-to file.  */
313               if ((S_ISLNK (dst_stat->st_mode)
314                    ? lutimens (dst_name, timespec)
315                    : utimens (dst_name, timespec)) != 0)
316                 return -2;
317
318               /* Read the modification time that was set.  */
319               {
320                 int stat_result = (S_ISLNK (dst_stat->st_mode)
321                                    ? lstat (dst_name, &dst_status)
322                                    : stat (dst_name, &dst_status));
323
324                 if (stat_result
325                     | (dst_status.st_mtime ^ dst_m_s)
326                     | (get_stat_mtime_ns (&dst_status) ^ dst_m_ns))
327                   {
328                     /* The modification time changed, or we can't tell whether
329                        it changed.  Change it back as best we can.  */
330                     timespec[1].tv_sec = dst_m_s;
331                     timespec[1].tv_nsec = dst_m_ns;
332                     if (S_ISLNK (dst_stat->st_mode))
333                       lutimens (dst_name, timespec);
334                     else
335                       utimens (dst_name, timespec);
336                   }
337
338                 if (stat_result != 0)
339                   return -2;
340               }
341
342               /* Determine the exact resolution from the modification time
343                  that was read back.  */
344               {
345                 int old_res = res;
346                 int a = (BILLION * (dst_status.st_mtime & 1)
347                          + get_stat_mtime_ns (&dst_status));
348
349                 res = SYSCALL_RESOLUTION;
350
351                 for (a /= res; a % 10 == 0; a /= 10)
352                   {
353                     if (res == BILLION)
354                       {
355                         res *= 2;
356                         break;
357                       }
358                     res *= 10;
359                     if (res == old_res)
360                       break;
361                   }
362               }
363             }
364
365           dst_res->resolution = res;
366           dst_res->exact = true;
367         }
368
369       /* Truncate the source's time stamp according to the resolution.  */
370       src_s &= ~ (res == 2 * BILLION ? 1 : 0);
371       src_ns -= src_ns % res;
372     }
373
374   /* Compare the time stamps and return -1, 0, 1 accordingly.  */
375   return (dst_s < src_s ? -1
376           : dst_s > src_s ? 1
377           : dst_ns < src_ns ? -1
378           : dst_ns > src_ns);
379 }