summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTavian Barnes <tavianator@tavianator.com>2022-11-08 11:53:19 -0500
committerTavian Barnes <tavianator@tavianator.com>2022-11-08 11:53:19 -0500
commit8917a48c31540dd7a99f95dd317d6cf1ac2f6803 (patch)
tree673d875cc007d504c6170a9cc19e06e88e5690d1
parent2d5edb37b924715b4fbee4d917ac334c773fca61 (diff)
downloadbfs-8917a48c31540dd7a99f95dd317d6cf1ac2f6803.tar.xz
tests/xtouch: Add some missing POSIX touch features
-rw-r--r--src/bfstd.h9
-rw-r--r--src/stat.c7
-rw-r--r--src/xtime.c2
-rwxr-xr-xtests/tests.sh10
-rw-r--r--tests/xtouch.c145
5 files changed, 114 insertions, 59 deletions
diff --git a/src/bfstd.h b/src/bfstd.h
index 9a06ac1..6bf6ec8 100644
--- a/src/bfstd.h
+++ b/src/bfstd.h
@@ -153,6 +153,15 @@ int xmajor(dev_t dev);
*/
int xminor(dev_t dev);
+// #include <sys/stat.h>
+
+#if __APPLE__
+# define st_atim st_atimespec
+# define st_ctim st_ctimespec
+# define st_mtim st_mtimespec
+# define st_birthtim st_birthtimespec
+#endif
+
// #include <unistd.h>
/**
diff --git a/src/stat.c b/src/stat.c
index b59e9b0..94dedef 100644
--- a/src/stat.c
+++ b/src/stat.c
@@ -37,13 +37,6 @@
# define BFS_STATX true
#endif
-#if __APPLE__
-# define st_atim st_atimespec
-# define st_ctim st_ctimespec
-# define st_mtim st_mtimespec
-# define st_birthtim st_birthtimespec
-#endif
-
const char *bfs_stat_field_name(enum bfs_stat_field field) {
switch (field) {
case BFS_STAT_DEV:
diff --git a/src/xtime.c b/src/xtime.c
index 153b267..079d42a 100644
--- a/src/xtime.c
+++ b/src/xtime.c
@@ -236,7 +236,7 @@ int xgetdate(const char *str, struct timespec *result) {
if (!*str) {
goto end;
- } else if (*str == 'T') {
+ } else if (*str == 'T' || *str == ' ') {
++str;
}
diff --git a/tests/tests.sh b/tests/tests.sh
index 6a32f62..8d13aca 100755
--- a/tests/tests.sh
+++ b/tests/tests.sh
@@ -362,12 +362,12 @@ make_loops "$TMP/loops"
# Creates a file+directory structure with varying timestamps
function make_times() {
- "$XTOUCH" -p -t 199112140000 "$1/a"
- "$XTOUCH" -p -t 199112140001 "$1/b"
- "$XTOUCH" -p -t 199112140002 "$1/c"
+ "$XTOUCH" -p -t "1991-12-14 00:00" "$1/a"
+ "$XTOUCH" -p -t "1991-12-14 00:01" "$1/b"
+ "$XTOUCH" -p -t "1991-12-14 00:02" "$1/c"
ln -s a "$1/l"
- "$XTOUCH" -p -h -t 199112140003 "$1/l"
- "$XTOUCH" -p -t 199112140004 "$1"
+ "$XTOUCH" -p -h -t "1991-12-14 00:03" "$1/l"
+ "$XTOUCH" -p -t "1991-12-14 00:04" "$1"
}
make_times "$TMP/times"
diff --git a/tests/xtouch.c b/tests/xtouch.c
index 7a248eb..9a91ec7 100644
--- a/tests/xtouch.c
+++ b/tests/xtouch.c
@@ -14,6 +14,7 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. *
****************************************************************************/
+#include "../src/bfstd.h"
#include "../src/xtime.h"
#include <errno.h>
#include <fcntl.h>
@@ -25,10 +26,36 @@
#include <time.h>
#include <unistd.h>
-/** Check if a path has a trailing slash. */
-static bool trailing_slash(const char *path) {
- size_t len = strlen(path);
- return len > 0 && path[len - 1] == '/';
+/** Parsed xtouch arguments. */
+struct args {
+ /** Simple flags. */
+ enum {
+ /** Don't create nonexistent files (-c). */
+ NO_CREATE = 1 << 0,
+ /** Don't follow symlinks (-h). */
+ NO_FOLLOW = 1 << 1,
+ /** Create any missing parent directories (-p). */
+ CREATE_PARENTS = 1 << 2,
+ } flags;
+
+ /** Timestamps (-r|-t|-d). */
+ struct timespec times[2];
+
+ /** File creation mode (-M; default 0666 & ~umask). */
+ mode_t fmode;
+ /** Directory creation mode (-M; default 0777 & ~umask). */
+ mode_t dmode;
+ /** Parent directory creation mode (0777 & ~umask). */
+ mode_t pmode;
+};
+
+/** Compute flags for fstatat()/utimensat(). */
+static int at_flags(const struct args *args) {
+ if (args->flags & NO_FOLLOW) {
+ return AT_SYMLINK_NOFOLLOW;
+ } else {
+ return 0;
+ }
}
/** Create any parent directories of the given path. */
@@ -62,29 +89,34 @@ static int mkdirs(const char *path, mode_t mode) {
}
/** Touch one path. */
-static int xtouch(const char *path, const struct timespec times[2], int flags, mode_t mode, bool parents, mode_t pmode) {
- int ret = utimensat(AT_FDCWD, path, times, flags);
+static int xtouch(const struct args *args, const char *path) {
+ int ret = utimensat(AT_FDCWD, path, args->times, at_flags(args));
if (ret == 0 || errno != ENOENT) {
return ret;
}
- if (parents && mkdirs(path, pmode) != 0) {
- return -1;
+ if (args->flags & NO_CREATE) {
+ return 0;
+ } else if (args->flags & CREATE_PARENTS) {
+ if (mkdirs(path, args->pmode) != 0) {
+ return -1;
+ }
}
- if (trailing_slash(path)) {
- if (mkdir(path, mode) != 0) {
+ size_t len = strlen(path);
+ if (len > 0 && path[len - 1] == '/') {
+ if (mkdir(path, args->dmode) != 0) {
return -1;
}
- return utimensat(AT_FDCWD, path, times, flags);
+ return utimensat(AT_FDCWD, path, args->times, at_flags(args));
} else {
- int fd = open(path, O_WRONLY | O_CREAT, mode);
+ int fd = open(path, O_WRONLY | O_CREAT, args->fmode);
if (fd < 0) {
return -1;
}
- if (futimens(fd, times) != 0) {
+ if (futimens(fd, args->times) != 0) {
int error = errno;
close(fd);
errno = error;
@@ -96,33 +128,52 @@ static int xtouch(const char *path, const struct timespec times[2], int flags, m
}
int main(int argc, char *argv[]) {
- int flags = 0;
+ mode_t mask = umask(0);
+
+ struct args args = {
+ .flags = 0,
+ .times = {
+ { .tv_nsec = UTIME_OMIT },
+ { .tv_nsec = UTIME_OMIT },
+ },
+ .fmode = 0666 & ~mask,
+ .dmode = 0777 & ~mask,
+ .pmode = 0777 & ~mask,
+ };
+
bool atime = false, mtime = false;
- bool parents = false;
- const char *mstr = NULL;
- const char *tstr = NULL;
+ const char *darg = NULL;
+ const char *marg = NULL;
+ const char *rarg = NULL;
const char *cmd = argc > 0 ? argv[0] : "xtouch";
int c;
- while (c = getopt(argc, argv, ":M:ahmpt:"), c != -1) {
+ while (c = getopt(argc, argv, ":M:acd:hmpr:t:"), c != -1) {
switch (c) {
case 'M':
- mstr = optarg;
+ marg = optarg;
break;
case 'a':
atime = true;
break;
+ case 'c':
+ args.flags |= NO_CREATE;
+ break;
+ case 'd':
+ case 't':
+ darg = optarg;
+ break;
case 'h':
- flags = AT_SYMLINK_NOFOLLOW;
+ args.flags |= NO_FOLLOW;
break;
case 'm':
mtime = true;
break;
case 'p':
- parents = true;
+ args.flags |= CREATE_PARENTS;
break;
- case 't':
- tstr = optarg;
+ case 'r':
+ rarg = optarg;
break;
case ':':
fprintf(stderr, "%s: Missing argument to -%c\n", cmd, optopt);
@@ -133,48 +184,51 @@ int main(int argc, char *argv[]) {
}
}
- mode_t mask = umask(0);
- mode_t fmode = 0666 & ~mask;
- mode_t dmode = 0777 & ~mask;
- mode_t pmode = 0777 & ~mask;
- if (mstr) {
+ if (marg) {
char *end;
- long mode = strtol(mstr, &end, 8);
- if (*mstr && !*end && mode >= 0 && mode < 01000) {
- fmode = dmode = mode;
+ long mode = strtol(marg, &end, 8);
+ if (*marg && !*end && mode >= 0 && mode < 01000) {
+ args.fmode = args.dmode = mode;
} else {
- fprintf(stderr, "%s: Invalid mode '%s'\n", cmd, mstr);
+ fprintf(stderr, "%s: Invalid mode '%s'\n", cmd, marg);
return EXIT_FAILURE;
}
}
- struct timespec ts;
- if (tstr) {
- if (xgetdate(tstr, &ts) != 0) {
- fprintf(stderr, "%s: Parsing time '%s' failed: %s\n", cmd, tstr, strerror(errno));
+ struct timespec times[2];
+
+ if (rarg) {
+ struct stat buf;
+ if (fstatat(AT_FDCWD, rarg, &buf, at_flags(&args)) != 0) {
+ fprintf(stderr, "%s: '%s': %s\n", cmd, rarg, strerror(errno));
return EXIT_FAILURE;
}
+ times[0] = buf.st_atim;
+ times[1] = buf.st_mtim;
+ } else if (darg) {
+ if (xgetdate(darg, &times[0]) != 0) {
+ fprintf(stderr, "%s: Parsing time '%s' failed: %s\n", cmd, darg, strerror(errno));
+ return EXIT_FAILURE;
+ }
+ times[1] = times[0];
} else {
// Don't use UTIME_NOW, so that multiple paths all get the same timestamp
- if (xgettime(&ts) != 0) {
+ if (xgettime(&times[0]) != 0) {
perror("xgettime()");
return EXIT_FAILURE;
}
+ times[1] = times[0];
}
- struct timespec times[2] = {
- { .tv_nsec = UTIME_OMIT },
- { .tv_nsec = UTIME_OMIT },
- };
if (!atime && !mtime) {
atime = true;
mtime = true;
}
if (atime) {
- times[0] = ts;
+ args.times[0] = times[0];
}
if (mtime) {
- times[1] = ts;
+ args.times[1] = times[1];
}
if (optind >= argc) {
@@ -185,9 +239,8 @@ int main(int argc, char *argv[]) {
int ret = EXIT_SUCCESS;
for (; optind < argc; ++optind) {
const char *path = argv[optind];
- bool isdir = trailing_slash(path);
- if (xtouch(path, times, flags, isdir ? dmode : fmode, parents, pmode) != 0) {
- fprintf(stderr, "%s: Touching '%s' failed: %s\n", cmd, path, strerror(errno));
+ if (xtouch(&args, path) != 0) {
+ fprintf(stderr, "%s: '%s': %s\n", cmd, path, strerror(errno));
ret = EXIT_FAILURE;
}
}