int set_utimes(const char *file, struct stat *fs) { struct timespec ts[2]; ts[0] = fs->st_atimespec; ts[1] = fs->st_mtimespec; if (lutimens(file, ts)) { warn("lutimens: %s", file); return (1); } return (0); }
void set_file_attributes (char const *to, enum file_attributes attr, char const *from, const struct stat *st, mode_t mode, struct timespec *new_time) { if (attr & FA_TIMES) { struct timespec times[2]; if (new_time) times[0] = times[1] = *new_time; else { times[0] = get_stat_atime (st); times[1] = get_stat_mtime (st); } if (lutimens (to, times) != 0) pfatal ("Failed to set the timestamps of %s %s", S_ISLNK (mode) ? "symbolic link" : "file", quotearg (to)); } if (attr & FA_IDS) { static uid_t euid = -1; static gid_t egid = -1; uid_t uid; uid_t gid; if (euid == -1) { euid = geteuid (); egid = getegid (); } uid = (euid == st->st_uid) ? -1 : st->st_uid; gid = (egid == st->st_gid) ? -1 : st->st_gid; /* May fail if we are not privileged to set the file owner, or we are not in group instat.st_gid. Ignore those errors. */ if ((uid != -1 || gid != -1) && lchown (to, uid, gid) != 0 && (errno != EPERM || (uid != -1 && lchown (to, (uid = -1), gid) != 0 && errno != EPERM))) pfatal ("Failed to set the %s of %s %s", (uid == -1) ? "owner" : "owning group", S_ISLNK (mode) ? "symbolic link" : "file", quotearg (to)); } if (attr & FA_XATTRS) if (copy_attr (from, to) != 0 && errno != ENOSYS && errno != ENOTSUP && errno != EPERM) fatal_exit (0); if (attr & FA_MODE) { #if 0 && defined HAVE_LCHMOD /* The "diff --git" format does not store the file permissions of symlinks, so don't try to set symlink file permissions even on systems where we could. */ if (lchmod (to, mode)) #else if (! S_ISLNK (mode) && chmod (to, mode) != 0) #endif pfatal ("Failed to set the permissions of %s %s", S_ISLNK (mode) ? "symbolic link" : "file", quotearg (to)); } }
int utimecmp (char const *dst_name, struct stat const *dst_stat, struct stat const *src_stat, int options) { /* Things to watch out for: The code uses a static hash table internally and is not safe in the presence of signals, multiple threads, etc. However, memory pressure that prevents use of the hash table is not fatal - we just fall back to redoing the computations on every call in that case. int and long int might be 32 bits. Many of the calculations store numbers up to 2 billion, and multiply by 10; they have to avoid multiplying 2 billion by 10, as this exceeds 32-bit capabilities. time_t might be unsigned. */ verify (TYPE_IS_INTEGER (time_t)); verify (TYPE_TWOS_COMPLEMENT (int)); /* Destination and source time stamps. */ time_t dst_s = dst_stat->st_mtime; time_t src_s = src_stat->st_mtime; int dst_ns = get_stat_mtime_ns (dst_stat); int src_ns = get_stat_mtime_ns (src_stat); if (options & UTIMECMP_TRUNCATE_SOURCE) { /* Look up the time stamp resolution for the destination device. */ /* Hash table for caching information learned about devices. */ static Hash_table *ht; /* Information about the destination file system. */ static struct fs_res *new_dst_res; struct fs_res *dst_res = NULL; struct fs_res tmp_dst_res; /* Time stamp resolution in nanoseconds. */ int res; /* Quick exit, if possible. Since the worst resolution is 2 seconds, anything that differs by more than that does not needs source truncation. */ if (dst_s == src_s && dst_ns == src_ns) return 0; if (dst_s <= src_s - 2) return -1; if (src_s <= dst_s - 2) return 1; /* Try to do a hash lookup, but fall back to stack variables and recomputation on low memory situations. */ if (! ht) ht = hash_initialize (16, NULL, dev_info_hash, dev_info_compare, free); if (ht) { if (! new_dst_res) { new_dst_res = malloc (sizeof *new_dst_res); if (!new_dst_res) goto low_memory; new_dst_res->resolution = 2 * BILLION; new_dst_res->exact = false; } new_dst_res->dev = dst_stat->st_dev; dst_res = hash_insert (ht, new_dst_res); if (! dst_res) goto low_memory; if (dst_res == new_dst_res) { /* NEW_DST_RES is now in use in the hash table, so allocate a new entry next time. */ new_dst_res = NULL; } } else { low_memory: if (ht) { tmp_dst_res.dev = dst_stat->st_dev; dst_res = hash_lookup (ht, &tmp_dst_res); } if (!dst_res) { dst_res = &tmp_dst_res; dst_res->resolution = 2 * BILLION; dst_res->exact = false; } } res = dst_res->resolution; #ifdef _PC_TIMESTAMP_RESOLUTION /* If the system will tell us the resolution, we're set! */ if (! dst_res->exact) { res = pathconf (dst_name, _PC_TIMESTAMP_RESOLUTION); if (0 < res) { dst_res->resolution = res; dst_res->exact = true; } } #endif if (! dst_res->exact) { /* This file system's resolution is not known exactly. Deduce it, and store the result in the hash table. */ time_t dst_a_s = dst_stat->st_atime; time_t dst_c_s = dst_stat->st_ctime; time_t dst_m_s = dst_s; int dst_a_ns = get_stat_atime_ns (dst_stat); int dst_c_ns = get_stat_ctime_ns (dst_stat); int dst_m_ns = dst_ns; /* Set RES to an upper bound on the file system resolution (after truncation due to SYSCALL_RESOLUTION) by inspecting the atime, ctime and mtime of the existing destination. We don't know of any file system that stores atime or ctime with a higher precision than mtime, so it's valid to look at them too. */ { bool odd_second = (dst_a_s | dst_c_s | dst_m_s) & 1; if (SYSCALL_RESOLUTION == BILLION) { if (odd_second | dst_a_ns | dst_c_ns | dst_m_ns) res = BILLION; } else { int a = dst_a_ns; int c = dst_c_ns; int m = dst_m_ns; /* Write it this way to avoid mistaken GCC warning about integer overflow in constant expression. */ int SR10 = SYSCALL_RESOLUTION; SR10 *= 10; if ((a % SR10 | c % SR10 | m % SR10) != 0) res = SYSCALL_RESOLUTION; else for (res = SR10, a /= SR10, c /= SR10, m /= SR10; (res < dst_res->resolution && (a % 10 | c % 10 | m % 10) == 0); res *= 10, a /= 10, c /= 10, m /= 10) if (res == BILLION) { if (! odd_second) res *= 2; break; } } dst_res->resolution = res; } if (SYSCALL_RESOLUTION < res) { struct timespec timespec[2]; struct stat dst_status; /* Ignore source time stamp information that must necessarily be lost when filtered through utimens. */ src_ns -= src_ns % SYSCALL_RESOLUTION; /* If the time stamps disagree widely enough, there's no need to interrogate the file system to deduce the exact time stamp resolution; return the answer directly. */ { time_t s = src_s & ~ (res == 2 * BILLION); if (src_s < dst_s || (src_s == dst_s && src_ns <= dst_ns)) return 1; if (dst_s < s || (dst_s == s && dst_ns < src_ns - src_ns % res)) return -1; } /* Determine the actual time stamp resolution for the destination file system (after truncation due to SYSCALL_RESOLUTION) by setting the access time stamp of the destination to the existing access time, except with trailing nonzero digits. */ timespec[0].tv_sec = dst_a_s; timespec[0].tv_nsec = dst_a_ns; timespec[1].tv_sec = dst_m_s | (res == 2 * BILLION); timespec[1].tv_nsec = dst_m_ns + res / 9; /* Set the modification time. But don't try to set the modification time of symbolic links; on many hosts this sets the time of the pointed-to file. */ if ((S_ISLNK (dst_stat->st_mode) ? lutimens (dst_name, timespec) : utimens (dst_name, timespec)) != 0) return -2; /* Read the modification time that was set. */ { int stat_result = (S_ISLNK (dst_stat->st_mode) ? lstat (dst_name, &dst_status) : stat (dst_name, &dst_status)); if (stat_result | (dst_status.st_mtime ^ dst_m_s) | (get_stat_mtime_ns (&dst_status) ^ dst_m_ns)) { /* The modification time changed, or we can't tell whether it changed. Change it back as best we can. */ timespec[1].tv_sec = dst_m_s; timespec[1].tv_nsec = dst_m_ns; if (S_ISLNK (dst_stat->st_mode)) lutimens (dst_name, timespec); else utimens (dst_name, timespec); } if (stat_result != 0) return -2; } /* Determine the exact resolution from the modification time that was read back. */ { int old_res = res; int a = (BILLION * (dst_status.st_mtime & 1) + get_stat_mtime_ns (&dst_status)); res = SYSCALL_RESOLUTION; for (a /= res; a % 10 == 0; a /= 10) { if (res == BILLION) { res *= 2; break; } res *= 10; if (res == old_res) break; } } } dst_res->resolution = res; dst_res->exact = true; } /* Truncate the source's time stamp according to the resolution. */ src_s &= ~ (res == 2 * BILLION); src_ns -= src_ns % res; } /* Compare the time stamps and return -1, 0, 1 accordingly. */ return (dst_s < src_s ? -1 : dst_s > src_s ? 1 : dst_ns < src_ns ? -1 : dst_ns > src_ns); }