cp,mv,touch: Set timestamps with nanosecond precision.

This uses utimensat().
This commit is contained in:
Jilles Tjoelker 2015-01-24 13:50:13 +00:00
parent f0583578a1
commit 5faf2ae160
3 changed files with 48 additions and 51 deletions

View File

@ -330,7 +330,7 @@ copy_special(struct stat *from_stat, int exists)
int
setfile(struct stat *fs, int fd)
{
static struct timeval tv[2];
static struct timespec tspec[2];
struct stat ts;
int rval, gotstat, islink, fdval;
@ -340,10 +340,11 @@ setfile(struct stat *fs, int fd)
fs->st_mode &= S_ISUID | S_ISGID | S_ISVTX |
S_IRWXU | S_IRWXG | S_IRWXO;
TIMESPEC_TO_TIMEVAL(&tv[0], &fs->st_atim);
TIMESPEC_TO_TIMEVAL(&tv[1], &fs->st_mtim);
if (islink ? lutimes(to.p_path, tv) : utimes(to.p_path, tv)) {
warn("%sutimes: %s", islink ? "l" : "", to.p_path);
tspec[0] = fs->st_atim;
tspec[1] = fs->st_mtim;
if (utimensat(AT_FDCWD, to.p_path, tspec,
islink ? AT_SYMLINK_NOFOLLOW : 0)) {
warn("utimensat: %s", to.p_path);
rval = 1;
}
if (fdval ? fstat(fd, &ts) :

View File

@ -273,7 +273,7 @@ do_move(const char *from, const char *to)
static int
fastcopy(const char *from, const char *to, struct stat *sbp)
{
struct timeval tval[2];
struct timespec ts[2];
static u_int blen = MAXPHYS;
static char *bp = NULL;
mode_t oldmode;
@ -350,10 +350,9 @@ err: if (unlink(to))
} else
warn("%s: cannot stat", to);
tval[0].tv_sec = sbp->st_atime;
tval[1].tv_sec = sbp->st_mtime;
tval[0].tv_usec = tval[1].tv_usec = 0;
if (utimes(to, tval))
ts[0] = sbp->st_atim;
ts[1] = sbp->st_mtim;
if (utimensat(AT_FDCWD, to, ts, 0))
warn("%s: set times", to);
if (close(to_fd)) {

View File

@ -56,10 +56,10 @@ static const char sccsid[] = "@(#)touch.c 8.1 (Berkeley) 6/6/93";
#include <time.h>
#include <unistd.h>
static void stime_arg1(const char *, struct timeval *);
static void stime_arg2(const char *, int, struct timeval *);
static void stime_darg(const char *, struct timeval *);
static void stime_file(const char *, struct timeval *);
static void stime_arg1(const char *, struct timespec *);
static void stime_arg2(const char *, int, struct timespec *);
static void stime_darg(const char *, struct timespec *);
static void stime_file(const char *, struct timespec *);
static int timeoffset(const char *);
static void usage(const char *);
@ -67,19 +67,17 @@ int
main(int argc, char *argv[])
{
struct stat sb;
struct timeval tv[2];
int (*stat_f)(const char *, struct stat *);
int (*utimes_f)(const char *, const struct timeval *);
struct timespec ts[2];
int atflag;
int Aflag, aflag, cflag, mflag, ch, fd, len, rval, timeset;
char *p;
char *myname;
myname = basename(argv[0]);
Aflag = aflag = cflag = mflag = timeset = 0;
stat_f = stat;
utimes_f = utimes;
if (gettimeofday(&tv[0], NULL) == -1)
err(1, "gettimeofday");
atflag = 0;
if (clock_gettime(CLOCK_REALTIME, &ts[0]) == -1)
err(1, "clock_gettime(CLOCK_REALTIME)");
while ((ch = getopt(argc, argv, "A:acd:fhmr:t:")) != -1)
switch(ch) {
@ -94,26 +92,25 @@ main(int argc, char *argv[])
break;
case 'd':
timeset = 1;
stime_darg(optarg, tv);
stime_darg(optarg, ts);
break;
case 'f':
/* No-op for compatibility. */
break;
case 'h':
cflag = 1;
stat_f = lstat;
utimes_f = lutimes;
atflag = AT_SYMLINK_NOFOLLOW;
break;
case 'm':
mflag = 1;
break;
case 'r':
timeset = 1;
stime_file(optarg, tv);
stime_file(optarg, ts);
break;
case 't':
timeset = 1;
stime_arg1(optarg, tv);
stime_arg1(optarg, ts);
break;
default:
usage(myname);
@ -132,9 +129,9 @@ main(int argc, char *argv[])
* that time once and for all here.
*/
if (aflag)
tv[0].tv_sec += Aflag;
ts[0].tv_sec += Aflag;
if (mflag)
tv[1].tv_sec += Aflag;
ts[1].tv_sec += Aflag;
Aflag = 0; /* done our job */
}
} else {
@ -148,11 +145,11 @@ main(int argc, char *argv[])
len = p - argv[0];
if (*p == '\0' && (len == 8 || len == 10)) {
timeset = 1;
stime_arg2(*argv++, len == 10, tv);
stime_arg2(*argv++, len == 10, ts);
}
}
/* Both times default to the same. */
tv[1] = tv[0];
ts[1] = ts[0];
}
if (*argv == NULL)
@ -163,7 +160,7 @@ main(int argc, char *argv[])
for (rval = 0; *argv; ++argv) {
/* See if the file exists. */
if (stat_f(*argv, &sb) != 0) {
if (fstatat(AT_FDCWD, *argv, &sb, atflag) != 0) {
if (errno != ENOENT) {
rval = 1;
warn("%s", *argv);
@ -187,9 +184,9 @@ main(int argc, char *argv[])
}
if (!aflag)
TIMESPEC_TO_TIMEVAL(&tv[0], &sb.st_atim);
ts[0] = sb.st_atim;
if (!mflag)
TIMESPEC_TO_TIMEVAL(&tv[1], &sb.st_mtim);
ts[1] = sb.st_mtim;
/*
* We're adjusting the times based on the file times, not a
@ -197,17 +194,17 @@ main(int argc, char *argv[])
*/
if (Aflag) {
if (aflag) {
TIMESPEC_TO_TIMEVAL(&tv[0], &sb.st_atim);
tv[0].tv_sec += Aflag;
ts[0] = sb.st_atim;
ts[0].tv_sec += Aflag;
}
if (mflag) {
TIMESPEC_TO_TIMEVAL(&tv[1], &sb.st_mtim);
tv[1].tv_sec += Aflag;
ts[1] = sb.st_mtim;
ts[1].tv_sec += Aflag;
}
}
/* Try utimes(2). */
if (!utimes_f(*argv, tv))
/* Try utimensat(2). */
if (!utimensat(AT_FDCWD, *argv, ts, atflag))
continue;
/* If the user specified a time, nothing else we can do. */
@ -223,7 +220,7 @@ main(int argc, char *argv[])
* The permission checks are different, too, in that the
* ability to write the file is sufficient. Take a shot.
*/
if (!utimes_f(*argv, NULL))
if (!utimensat(AT_FDCWD, *argv, NULL, atflag))
continue;
rval = 1;
@ -235,7 +232,7 @@ main(int argc, char *argv[])
#define ATOI2(ar) ((ar)[0] - '0') * 10 + ((ar)[1] - '0'); (ar) += 2;
static void
stime_arg1(const char *arg, struct timeval *tvp)
stime_arg1(const char *arg, struct timespec *tvp)
{
time_t now;
struct tm *t;
@ -291,7 +288,7 @@ stime_arg1(const char *arg, struct timeval *tvp)
if (tvp[0].tv_sec == -1)
goto terr;
tvp[0].tv_usec = tvp[1].tv_usec = 0;
tvp[0].tv_nsec = tvp[1].tv_nsec = 0;
return;
terr:
@ -299,7 +296,7 @@ stime_arg1(const char *arg, struct timeval *tvp)
}
static void
stime_arg2(const char *arg, int year, struct timeval *tvp)
stime_arg2(const char *arg, int year, struct timespec *tvp)
{
time_t now;
struct tm *t;
@ -325,18 +322,18 @@ stime_arg2(const char *arg, int year, struct timeval *tvp)
errx(1,
"out of range or illegal time specification: MMDDhhmm[yy]");
tvp[0].tv_usec = tvp[1].tv_usec = 0;
tvp[0].tv_nsec = tvp[1].tv_nsec = 0;
}
static void
stime_darg(const char *arg, struct timeval *tvp)
stime_darg(const char *arg, struct timespec *tvp)
{
struct tm t = { .tm_sec = 0 };
const char *fmt, *colon;
char *p;
int val, isutc = 0;
tvp[0].tv_usec = 0;
tvp[0].tv_nsec = 0;
t.tm_isdst = -1;
colon = strchr(arg, ':');
if (colon == NULL || strchr(colon + 1, ':') == NULL)
@ -349,9 +346,9 @@ stime_darg(const char *arg, struct timeval *tvp)
/* POSIX: must have at least one digit after dot */
if ((*p == '.' || *p == ',') && isdigit((unsigned char)p[1])) {
p++;
val = 100000;
val = 100000000;
while (isdigit((unsigned char)*p)) {
tvp[0].tv_usec += val * (*p - '0');
tvp[0].tv_nsec += val * (*p - '0');
p++;
val /= 10;
}
@ -403,14 +400,14 @@ timeoffset(const char *arg)
}
static void
stime_file(const char *fname, struct timeval *tvp)
stime_file(const char *fname, struct timespec *tsp)
{
struct stat sb;
if (stat(fname, &sb))
err(1, "%s", fname);
TIMESPEC_TO_TIMEVAL(tvp, &sb.st_atim);
TIMESPEC_TO_TIMEVAL(tvp + 1, &sb.st_mtim);
tsp[0] = sb.st_atim;
tsp[1] = sb.st_mtim;
}
static void