(My apologies for the late reply. Your mail never arrived, and I only
discovered it on the WWW interface yesterday.)
Thorsten Glaser:
I’ve noticed an error (in the native BSD environment) which I believe
to be related, if not the same, but haven’t gotten around to debugging
yet.
I’ll have to find a way to do this on systems without the |*at()|
syscalls, however. Since you already looked into this, perhaps you
have an idea for me ;)
I have. I've tested this on Debian Linux. It highlights that there's a
further bug somewhere else, with the datestamps that are being supplied
for symbolic links to these functions; but it addresses /this/ problem
of symbolic links being erroneously followed.
The FreeBSD |pax| code in comparison uses |lutimes()|, but using
|utimensat()| eliminates the need for an extra |lstat()| call because of
the availablility of the |UTIME_OMIT| mechanism. Both OpenBSD and
FreeBSD have a |utimensat()| function, and neither of their |pax|
implementations have this problem with following symbolic links. I'll
leave you with what if anything to do about MirOS BSD. (-:
In |file_subs.c| add
#if defined(__LINUX__) || defined(__linux__) || defined(__FreeBSD__) || defined
(__DragonFlyBSD__) || defined(__OpenBSD__)
#define PAX_UTIMENSAT
#define PAX_FUTIMENS
#elif !defined(__INTERIX)
#define PAX_UTIMES
#endif
And rewrite these two functions:
void
set_ftime(char *fnm, time_t mtime, time_t atime, int frc)
{
#if defined(PAX_UTIMENSAT)
static struct timespec ts[2] = {{0L, 0L}, {0L, 0L}};
#elif defined(PAX_UTIMES)
static struct timeval tv[2] = {{0L, 0L}, {0L, 0L}};
struct stat sb;
#else
struct utimbuf u;
struct stat sb;
#endif
if (!frc && (!patime || !pmtime)) {
/*
* If we are not forcing, only set those times the user wants set.
* We get the current values of the times if we need them.
*/
#if defined(PAX_UTIMENSAT)
if (!patime)
ts[0].tv_nsec = UTIME_OMIT;
if (!pmtime)
ts[1].tv_nsec = UTIME_OMIT;
#else
if (lstat(fnm, &sb) == 0) {
if (!patime)
atime = sb.st_atime;
if (!pmtime)
mtime = sb.st_mtime;
} else
syswarn(0,errno,"Unable to obtain file stats %s", fnm);
#endif
}
/*
* set the times
*/
#if defined(PAX_UTIMENSAT)
ts[0].tv_sec = atime;
ts[1].tv_sec = mtime;
if (utimensat(AT_FDCWD, fnm, ts, AT_SYMLINK_NOFOLLOW) < 0)
#elif defined(PAX_UTIMES)
tv[0].tv_sec = (long)atime;
tv[1].tv_sec = (long)mtime;
if (utimes(fnm, tv) < 0)
#else
u.actime = atime;
u.modtime = mtime;
if (utime(fnm, &u) < 0)
#endif
syswarn(1, errno, "Access/modification time set failed on: %s",
fnm);
}
#if defined(PAX_FUTIMES)
static void
fset_ftime(char *fnm, int fd, time_t mtime, time_t atime, int frc)
{
#if defined(PAX_FUTIMENS)
static struct timespec ts[2] = {{0L, 0L}, {0L, 0L}};
#else
static struct timeval tv[2] = {{0L, 0L}, {0L, 0L}};
struct stat sb;
#endif
if (!frc && (!patime || !pmtime)) {
/*
* If we are not forcing, only set those times the user wants set.
* We get the current values of the times if we need them.
*/
#if defined(PAX_FUTIMENS)
if (!patime)
ts[0].tv_nsec = UTIME_OMIT;
if (!pmtime)
ts[1].tv_nsec = UTIME_OMIT;
#else
if (fstat(fd, &sb) == 0) {
if (!patime)
atime = sb.st_atime;
if (!pmtime)
mtime = sb.st_mtime;
} else
syswarn(0,errno,"Unable to obtain file stats %s", fnm);
#endif
}
/*
* set the times
*/
#if defined(PAX_FUTIMENS)
ts[0].tv_sec = atime;
ts[1].tv_sec = mtime;
if (futimens(fd, ts) < 0)
#else
tv[0].tv_sec = (long)atime;
tv[1].tv_sec = (long)mtime;
if (futimes(fd, tv) < 0)
#endif
syswarn(1, errno, "Access/modification time set failed on: %s",
fnm);
}
#endif
Here's an alternative rendition that makes the individual
implementations clearer, but makes the code prone to maintenance errors
where only one of the alternatives gets updated:
void
set_ftime(char *fnm, time_t mtime, time_t atime, int frc)
{
#if defined(PAX_UTIMENSAT)
static struct timespec ts[2] = {{0L, 0L}, {0L, 0L}};
if (!frc && (!patime || !pmtime)) {
/*
* If we are not forcing, only set those times the user wants set.
*/
if (!patime)
ts[0].tv_nsec = UTIME_OMIT;
if (!pmtime)
ts[1].tv_nsec = UTIME_OMIT;
}
/*
* set the times
*/
ts[0].tv_sec = atime;
ts[1].tv_sec = mtime;
if (utimensat(AT_FDCWD, fnm, ts, AT_SYMLINK_NOFOLLOW) < 0)
syswarn(1, errno, "Access/modification time set failed on: %s",
fnm);
#elif defined(PAX_UTIMES)
static struct timeval tv[2] = {{0L, 0L}, {0L, 0L}};
struct stat sb;
if (!frc && (!patime || !pmtime)) {
/*
* If we are not forcing, only set those times the user wants set.
* We get the current values of the times if we need them.
*/
if (lstat(fnm, &sb) == 0) {
if (!patime)
atime = sb.st_atime;
if (!pmtime)
mtime = sb.st_mtime;
} else
syswarn(0,errno,"Unable to obtain file stats %s", fnm);
}
/*
* set the times
*/
tv[0].tv_sec = (long)atime;
tv[1].tv_sec = (long)mtime;
if (utimes(fnm, tv) < 0)
syswarn(1, errno, "Access/modification time set failed on: %s",
fnm);
#else
struct utimbuf u;
struct stat sb;
if (!frc && (!patime || !pmtime)) {
/*
* If we are not forcing, only set those times the user wants set.
* We get the current values of the times if we need them.
*/
if (lstat(fnm, &sb) == 0) {
if (!patime)
atime = sb.st_atime;
if (!pmtime)
mtime = sb.st_mtime;
} else
syswarn(0,errno,"Unable to obtain file stats %s", fnm);
}
/*
* set the times
*/
u.actime = atime;
u.modtime = mtime;
if (utime(fnm, &u) < 0)
syswarn(1, errno, "Access/modification time set failed on: %s",
fnm);
}
#endif
#if defined(PAX_FUTIMES)
static void
fset_ftime(char *fnm, int fd, time_t mtime, time_t atime, int frc)
{
#if defined(PAX_FUTIMENS)
static struct timespec ts[2] = {{0L, 0L}, {0L, 0L}};
if (!frc && (!patime || !pmtime)) {
/*
* If we are not forcing, only set those times the user wants set.
*/
if (!patime)
ts[0].tv_nsec = UTIME_OMIT;
if (!pmtime)
ts[1].tv_nsec = UTIME_OMIT;
}
/*
* set the times
*/
ts[0].tv_sec = atime;
ts[1].tv_sec = mtime;
if (futimens(fd, ts) < 0)
syswarn(1, errno, "Access/modification time set failed on: %s",
fnm);
#else
static struct timeval tv[2] = {{0L, 0L}, {0L, 0L}};
struct stat sb;
if (!frc && (!patime || !pmtime)) {
/*
* If we are not forcing, only set those times the user wants set.
* We get the current values of the times if we need them.
*/
if (fstat(fd, &sb) == 0) {
if (!patime)
atime = sb.st_atime;
if (!pmtime)
mtime = sb.st_mtime;
} else
syswarn(0,errno,"Unable to obtain file stats %s", fnm);
}
/*
* set the times
*/
tv[0].tv_sec = (long)atime;
tv[1].tv_sec = (long)mtime;
if (futimes(fd, tv) < 0)
syswarn(1, errno, "Access/modification time set failed on: %s",
fnm);
#endif
}
#endif