diff options
Diffstat (limited to 'tests/xtouch.c')
-rw-r--r-- | tests/xtouch.c | 174 |
1 files changed, 104 insertions, 70 deletions
diff --git a/tests/xtouch.c b/tests/xtouch.c index 9a91ec7..cd41842 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 != 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, ×[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; } } |