summaryrefslogtreecommitdiffstats
path: root/tests/xtouch.c
diff options
context:
space:
mode:
Diffstat (limited to 'tests/xtouch.c')
-rw-r--r--tests/xtouch.c174
1 files changed, 104 insertions, 70 deletions
diff --git a/tests/xtouch.c b/tests/xtouch.c
index 9a91ec7..427e3e0 100644
--- a/tests/xtouch.c
+++ b/tests/xtouch.c
@@ -1,28 +1,17 @@
-/****************************************************************************
- * bfs *
- * Copyright (C) 2022 Tavian Barnes <tavianator@tavianator.com> *
- * *
- * Permission to use, copy, modify, and/or distribute this software for any *
- * purpose with or without fee is hereby granted. *
- * *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES *
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF *
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR *
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES *
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN *
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF *
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. *
- ****************************************************************************/
-
-#include "../src/bfstd.h"
-#include "../src/xtime.h"
+// Copyright © Tavian Barnes <tavianator@tavianator.com>
+// SPDX-License-Identifier: 0BSD
+
+#include "prelude.h"
+#include "bfstd.h"
+#include "sanity.h"
+#include "xtime.h"
#include <errno.h>
#include <fcntl.h>
-#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
+#include <sys/types.h>
#include <time.h>
#include <unistd.h>
@@ -49,85 +38,128 @@ struct args {
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;
+/** Open (and maybe create) a single directory. */
+static int open_dir(const struct args *args, int dfd, const char *path) {
+ int ret = openat(dfd, path, O_SEARCH | O_DIRECTORY);
+
+ if (ret < 0 && errno == ENOENT && (args->flags & CREATE_PARENTS)) {
+ if (mkdirat(dfd, path, args->pmode) == 0 || errno == EEXIST) {
+ ret = openat(dfd, path, O_SEARCH | O_DIRECTORY);
+ }
}
+
+ return ret;
}
-/** Create any parent directories of the given path. */
-static int mkdirs(const char *path, mode_t mode) {
- char *copy = strdup(path);
- if (!copy) {
+/** Open (and maybe create) the parent directory of the path. */
+static int open_parent(const struct args *args, const char **path) {
+ size_t max = xbaseoff(*path);
+ if (max == 0) {
+ return AT_FDCWD;
+ }
+
+ char *dir = strndup(*path, max);
+ if (!dir) {
return -1;
}
- int ret = -1;
- char *cur = copy + strspn(copy, "/");
- while (true) {
- cur += strcspn(cur, "/");
+ // Optimistically try the whole path first
+ int dfd = open_dir(args, AT_FDCWD, dir);
+ if (dfd >= 0) {
+ goto done;
+ }
- char *next = cur + strspn(cur, "/");
- if (!*next) {
- ret = 0;
- break;
+ if (errno == ENOENT) {
+ if (!(args->flags & CREATE_PARENTS)) {
+ goto err;
}
+ } else if (!errno_is_like(ENAMETOOLONG)) {
+ goto err;
+ }
- *cur = '\0';
- if (mkdir(copy, mode) != 0 && errno != EEXIST) {
- break;
+ // Open the parents one-at-a-time
+ dfd = AT_FDCWD;
+ char *cur = dir;
+ while (*cur) {
+ char *next = cur;
+ next += strcspn(next, "/");
+ next += strspn(next, "/");
+
+ char c = *next;
+ *next = '\0';
+
+ int parent = dfd;
+ dfd = open_dir(args, parent, cur);
+ if (parent >= 0) {
+ close_quietly(parent);
}
- *cur = '/';
+ if (dfd < 0) {
+ goto err;
+ }
+
+ *next = c;
cur = next;
}
- free(copy);
- return ret;
+done:
+ *path += max;
+err:
+ free(dir);
+ return dfd;
+}
+
+/** 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;
+ }
}
/** Touch one path. */
static int xtouch(const struct args *args, const char *path) {
- int ret = utimensat(AT_FDCWD, path, args->times, at_flags(args));
+ int dfd = open_parent(args, &path);
+ if (dfd < 0 && dfd != (int)AT_FDCWD) {
+ return -1;
+ }
+
+ int ret = utimensat(dfd, path, args->times, at_flags(args));
if (ret == 0 || errno != ENOENT) {
- return ret;
+ goto done;
}
if (args->flags & NO_CREATE) {
- return 0;
- } else if (args->flags & CREATE_PARENTS) {
- if (mkdirs(path, args->pmode) != 0) {
- return -1;
- }
+ ret = 0;
+ goto done;
}
size_t len = strlen(path);
if (len > 0 && path[len - 1] == '/') {
- if (mkdir(path, args->dmode) != 0) {
- return -1;
+ if (mkdirat(dfd, path, args->dmode) == 0) {
+ ret = utimensat(dfd, path, args->times, at_flags(args));
}
-
- return utimensat(AT_FDCWD, path, args->times, at_flags(args));
} else {
- int fd = open(path, O_WRONLY | O_CREAT, args->fmode);
- if (fd < 0) {
- return -1;
- }
-
- if (futimens(fd, args->times) != 0) {
- int error = errno;
- close(fd);
- errno = error;
- return -1;
+ int fd = openat(dfd, path, O_WRONLY | O_CREAT, args->fmode);
+ if (fd >= 0) {
+ if (futimens(fd, args->times) == 0) {
+ ret = xclose(fd);
+ } else {
+ close_quietly(fd);
+ }
}
+ }
- return close(fd);
+done:
+ if (dfd >= 0) {
+ close_quietly(dfd);
}
+ return ret;
}
int main(int argc, char *argv[]) {
+ tzset();
+
mode_t mask = umask(0);
struct args args = {
@@ -187,6 +219,8 @@ int main(int argc, char *argv[]) {
if (marg) {
char *end;
long mode = strtol(marg, &end, 8);
+ // https://github.com/llvm/llvm-project/issues/64946
+ sanitize_init(&end);
if (*marg && !*end && mode >= 0 && mode < 01000) {
args.fmode = args.dmode = mode;
} else {
@@ -200,14 +234,14 @@ int main(int argc, char *argv[]) {
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));
+ fprintf(stderr, "%s: '%s': %s\n", cmd, rarg, xstrerror(errno));
return EXIT_FAILURE;
}
- times[0] = buf.st_atim;
- times[1] = buf.st_mtim;
+ times[0] = ST_ATIM(buf);
+ times[1] = ST_MTIM(buf);
} else if (darg) {
if (xgetdate(darg, &times[0]) != 0) {
- fprintf(stderr, "%s: Parsing time '%s' failed: %s\n", cmd, darg, strerror(errno));
+ fprintf(stderr, "%s: Parsing time '%s' failed: %s\n", cmd, darg, xstrerror(errno));
return EXIT_FAILURE;
}
times[1] = times[0];
@@ -240,7 +274,7 @@ int main(int argc, char *argv[]) {
for (; optind < argc; ++optind) {
const char *path = argv[optind];
if (xtouch(&args, path) != 0) {
- fprintf(stderr, "%s: '%s': %s\n", cmd, path, strerror(errno));
+ fprintf(stderr, "%s: '%s': %s\n", cmd, path, xstrerror(errno));
ret = EXIT_FAILURE;
}
}