diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/alloc.c | 8 | ||||
-rw-r--r-- | src/alloc.h | 2 | ||||
-rw-r--r-- | src/bar.c | 135 | ||||
-rw-r--r-- | src/bfstd.c | 146 | ||||
-rw-r--r-- | src/bfstd.h | 22 | ||||
-rw-r--r-- | src/bftw.c | 2 | ||||
-rw-r--r-- | src/bftw.h | 4 | ||||
-rw-r--r-- | src/bit.h | 2 | ||||
-rw-r--r-- | src/color.c | 2 | ||||
-rw-r--r-- | src/color.h | 2 | ||||
-rw-r--r-- | src/ctx.c | 2 | ||||
-rw-r--r-- | src/ctx.h | 2 | ||||
-rw-r--r-- | src/diag.c | 2 | ||||
-rw-r--r-- | src/diag.h | 2 | ||||
-rw-r--r-- | src/dir.c | 18 | ||||
-rw-r--r-- | src/dir.h | 7 | ||||
-rw-r--r-- | src/dstring.c | 7 | ||||
-rw-r--r-- | src/dstring.h | 6 | ||||
-rw-r--r-- | src/eval.c | 85 | ||||
-rw-r--r-- | src/eval.h | 3 | ||||
-rw-r--r-- | src/exec.c | 2 | ||||
-rw-r--r-- | src/expr.h | 2 | ||||
-rw-r--r-- | src/fsade.c | 164 | ||||
-rw-r--r-- | src/fsade.h | 28 | ||||
-rw-r--r-- | src/ioq.c | 7 | ||||
-rw-r--r-- | src/ioq.h | 2 | ||||
-rw-r--r-- | src/main.c | 4 | ||||
-rw-r--r-- | src/mtab.c | 54 | ||||
-rw-r--r-- | src/mtab.h | 2 | ||||
-rw-r--r-- | src/opt.c | 8 | ||||
-rw-r--r-- | src/parse.c | 120 | ||||
-rw-r--r-- | src/prelude.h (renamed from src/config.h) | 76 | ||||
-rw-r--r-- | src/printf.c | 141 | ||||
-rw-r--r-- | src/pwcache.c | 2 | ||||
-rw-r--r-- | src/sanity.h | 2 | ||||
-rw-r--r-- | src/stat.c | 53 | ||||
-rw-r--r-- | src/stat.h | 10 | ||||
-rw-r--r-- | src/thread.c | 2 | ||||
-rw-r--r-- | src/thread.h | 2 | ||||
-rw-r--r-- | src/trie.c | 2 | ||||
-rw-r--r-- | src/xregex.c | 2 | ||||
-rw-r--r-- | src/xspawn.c | 30 | ||||
-rw-r--r-- | src/xspawn.h | 2 | ||||
-rw-r--r-- | src/xtime.c | 40 |
44 files changed, 738 insertions, 478 deletions
diff --git a/src/alloc.c b/src/alloc.c index b65d0c5..ebaff38 100644 --- a/src/alloc.c +++ b/src/alloc.c @@ -1,9 +1,9 @@ // Copyright © Tavian Barnes <tavianator@tavianator.com> // SPDX-License-Identifier: 0BSD +#include "prelude.h" #include "alloc.h" #include "bit.h" -#include "config.h" #include "diag.h" #include "sanity.h" #include <errno.h> @@ -24,12 +24,12 @@ static void *xmemalign(size_t align, size_t size) { bfs_assert(align >= sizeof(void *)); bfs_assert(is_aligned(align, size)); -#if __APPLE__ +#if BFS_HAS_ALIGNED_ALLOC + return aligned_alloc(align, size); +#else void *ptr = NULL; errno = posix_memalign(&ptr, align, size); return ptr; -#else - return aligned_alloc(align, size); #endif } diff --git a/src/alloc.h b/src/alloc.h index ae055bc..095134a 100644 --- a/src/alloc.h +++ b/src/alloc.h @@ -8,7 +8,7 @@ #ifndef BFS_ALLOC_H #define BFS_ALLOC_H -#include "config.h" +#include "prelude.h" #include <errno.h> #include <stddef.h> #include <stdlib.h> @@ -1,11 +1,12 @@ // Copyright © Tavian Barnes <tavianator@tavianator.com> // SPDX-License-Identifier: 0BSD +#include "prelude.h" #include "bar.h" #include "atomic.h" #include "bfstd.h" #include "bit.h" -#include "config.h" +#include "diag.h" #include "dstring.h" #include <errno.h> #include <fcntl.h> @@ -15,9 +16,30 @@ #include <string.h> #include <sys/ioctl.h> +/** + * Sentinel values for bfs_bar::refcount. + */ +enum { + /** The bar is currently uninitialized. */ + BFS_BAR_UNINIT = 0, + /** The bar is being initialized/destroyed. */ + BFS_BAR_BUSY = 1, + /** Values >= this are valid refcounts. */ + BFS_BAR_READY = 2, +}; + struct bfs_bar { - int fd; + /** + * A file descriptor to the TTY. Must be atomic because signal handlers + * can race with bfs_bar_hide(). + */ + atomic int fd; + /** A reference count protecting `fd`. */ + atomic unsigned int refcount; + + /** The width of the TTY. */ atomic unsigned int width; + /** The height of the TTY. */ atomic unsigned int height; }; @@ -26,27 +48,71 @@ static struct bfs_bar the_bar = { .fd = -1, }; +/** Try to acquire a reference to bar->fd. */ +static int bfs_bar_getfd(struct bfs_bar *bar) { + unsigned int refs = load(&bar->refcount, relaxed); + do { + if (refs < BFS_BAR_READY) { + errno = EAGAIN; + return -1; + } + } while (!compare_exchange_weak(&bar->refcount, &refs, refs + 1, acquire, relaxed)); + + return load(&bar->fd, relaxed); +} + +/** Release a reference to bar->fd. */ +static void bfs_bar_putfd(struct bfs_bar *bar) { + int fd = load(&bar->fd, relaxed); + + unsigned int refs = fetch_sub(&bar->refcount, 1, release); + bfs_assert(refs >= BFS_BAR_READY); + + if (refs == 2) { + close_quietly(fd); + store(&bar->fd, -1, relaxed); + store(&bar->refcount, BFS_BAR_UNINIT, release); + } +} + /** Get the terminal size, if possible. */ static int bfs_bar_getsize(struct bfs_bar *bar) { #ifdef TIOCGWINSZ - struct winsize ws; - if (ioctl(bar->fd, TIOCGWINSZ, &ws) != 0) { + int fd = bfs_bar_getfd(bar); + if (fd < 0) { return -1; } - store(&bar->width, ws.ws_col, relaxed); - store(&bar->height, ws.ws_row, relaxed); - return 0; + struct winsize ws; + int ret = ioctl(fd, TIOCGWINSZ, &ws); + if (ret == 0) { + store(&bar->width, ws.ws_col, relaxed); + store(&bar->height, ws.ws_row, relaxed); + } + + bfs_bar_putfd(bar); + return ret; #else errno = ENOTSUP; return -1; #endif } -/** Async Signal Safe puts(). */ -static int ass_puts(int fd, const char *str) { - size_t len = strlen(str); - return xwrite(fd, str, len) == len ? 0 : -1; +/** Write a string to the status bar (async-signal-safe). */ +static int bfs_bar_write(struct bfs_bar *bar, const char *str, size_t len) { + int fd = bfs_bar_getfd(bar); + if (fd < 0) { + return -1; + } + + int ret = xwrite(fd, str, len) == len ? 0 : -1; + bfs_bar_putfd(bar); + return ret; +} + +/** Write a string to the status bar (async-signal-safe). */ +static int bfs_bar_puts(struct bfs_bar *bar, const char *str) { + return bfs_bar_write(bar, str, strlen(str)); } /** Number of decimal digits needed for terminal sizes. */ @@ -70,22 +136,24 @@ static char *ass_itoa(char *str, unsigned int n) { /** Update the size of the scrollable region. */ static int bfs_bar_resize(struct bfs_bar *bar) { - char esc_seq[12 + ITOA_DIGITS] = - "\0337" // DECSC: Save cursor - "\033[;"; // DECSTBM: Set scrollable region + char esc_seq[12 + ITOA_DIGITS]; + + char *cur = stpcpy(esc_seq, + "\0337" // DECSC: Save cursor + "\033[;" // DECSTBM: Set scrollable region + ); // DECSTBM takes the height as the second argument unsigned int height = load(&bar->height, relaxed); - char *ptr = esc_seq + strlen(esc_seq); - ptr = ass_itoa(ptr, height - 1); + cur = ass_itoa(cur, height - 1); - strcpy(ptr, + cur = stpcpy(cur, "r" // DECSTBM "\0338" // DECRC: Restore the cursor "\033[J" // ED: Erase display from cursor to end ); - return ass_puts(bar->fd, esc_seq); + return bfs_bar_write(bar, esc_seq, cur - esc_seq); } #ifdef SIGWINCH @@ -102,7 +170,7 @@ static void sighand_winch(int sig) { /** Reset the scrollable region and hide the bar. */ static int bfs_bar_reset(struct bfs_bar *bar) { - return ass_puts(bar->fd, + return bfs_bar_puts(bar, "\0337" // DECSC: Save cursor "\033[r" // DECSTBM: Reset scrollable region "\0338" // DECRC: Restore cursor @@ -138,13 +206,14 @@ static int bfs_bar_printf(struct bfs_bar *bar, const char *format, ...) { return -1; } - int ret = ass_puts(bar->fd, str); + int ret = bfs_bar_write(bar, str, dstrlen(str)); dstrfree(str); return ret; } struct bfs_bar *bfs_bar_show(void) { - if (the_bar.fd >= 0) { + unsigned int refs = BFS_BAR_UNINIT; + if (!compare_exchange_strong(&the_bar.refcount, &refs, BFS_BAR_BUSY, acq_rel, relaxed)) { errno = EBUSY; goto fail; } @@ -153,16 +222,18 @@ struct bfs_bar *bfs_bar_show(void) { ctermid(term); if (strlen(term) == 0) { errno = ENOTTY; - goto fail; + goto unref; } - the_bar.fd = open(term, O_RDWR | O_CLOEXEC); - if (the_bar.fd < 0) { - goto fail; + int fd = open(term, O_RDWR | O_CLOEXEC); + if (fd < 0) { + goto unref; } + store(&the_bar.fd, fd, relaxed); + store(&the_bar.refcount, BFS_BAR_READY, release); if (bfs_bar_getsize(&the_bar) != 0) { - goto fail_close; + goto put; } reset_before_death_by(SIGABRT); @@ -192,9 +263,11 @@ struct bfs_bar *bfs_bar_show(void) { return &the_bar; -fail_close: - close_quietly(the_bar.fd); - the_bar.fd = -1; +put: + bfs_bar_putfd(&the_bar); + return NULL; +unref: + store(&the_bar.refcount, 0, release); fail: return NULL; } @@ -233,7 +306,5 @@ void bfs_bar_hide(struct bfs_bar *bar) { #endif bfs_bar_reset(bar); - - xclose(bar->fd); - bar->fd = -1; + bfs_bar_putfd(bar); } diff --git a/src/bfstd.c b/src/bfstd.c index 2499f00..0ac3a72 100644 --- a/src/bfstd.c +++ b/src/bfstd.c @@ -1,9 +1,9 @@ // Copyright © Tavian Barnes <tavianator@tavianator.com> // SPDX-License-Identifier: 0BSD +#include "prelude.h" #include "bfstd.h" #include "bit.h" -#include "config.h" #include "diag.h" #include "sanity.h" #include "thread.h" @@ -186,10 +186,10 @@ char *xgetdelim(FILE *file, char delim) { const char *xgetprogname(void) { const char *cmd = NULL; -#if __GLIBC__ - cmd = program_invocation_short_name; -#elif BSD +#if BFS_HAS_GETPROGNAME cmd = getprogname(); +#elif BFS_HAS_GETPROGNAME_GNU + cmd = program_invocation_short_name; #endif if (!cmd) { @@ -252,40 +252,6 @@ int ynprompt(void) { return ret; } -/** Get the single character describing the given file type. */ -static char type_char(mode_t mode) { - switch (mode & S_IFMT) { - case S_IFREG: - return '-'; - case S_IFBLK: - return 'b'; - case S_IFCHR: - return 'c'; - case S_IFDIR: - return 'd'; - case S_IFLNK: - return 'l'; - case S_IFIFO: - return 'p'; - case S_IFSOCK: - return 's'; -#ifdef S_IFDOOR - case S_IFDOOR: - return 'D'; -#endif -#ifdef S_IFPORT - case S_IFPORT: - return 'P'; -#endif -#ifdef S_IFWHT - case S_IFWHT: - return 'w'; -#endif - } - - return '?'; -} - void *xmemdup(const void *src, size_t size) { void *ret = malloc(size); if (ret) { @@ -317,35 +283,33 @@ const char *xstrerror(int errnum) { const char *ret = NULL; static thread_local char buf[256]; - // - __APPLE__ - // - __COSMOPOLITAN__ - // - No strerror_l() - // - __FreeBSD__ && SANITIZE_MEMORY - // - duplocale() triggers https://github.com/llvm/llvm-project/issues/65532 -#if __APPLE__ || __COSMOPOLITAN__ || (__FreeBSD__ && SANITIZE_MEMORY) - if (strerror_r(errnum, buf, sizeof(buf)) == 0) { - ret = buf; - } -#else -# if __NetBSD__ - // NetBSD has no thread-specific locales - locale_t loc = LC_GLOBAL_LOCALE; -# else + // On FreeBSD with MemorySanitizer, duplocale() triggers + // https://github.com/llvm/llvm-project/issues/65532 +#if BFS_HAS_STRERROR_L && !(__FreeBSD__ && SANITIZE_MEMORY) +# if BFS_HAS_USELOCALE locale_t loc = uselocale((locale_t)0); +# else + locale_t loc = LC_GLOBAL_LOCALE; # endif - locale_t copy = loc; - if (copy == LC_GLOBAL_LOCALE) { - copy = duplocale(copy); + bool free_loc = false; + if (loc == LC_GLOBAL_LOCALE) { + loc = duplocale(loc); + free_loc = true; } - if (copy != (locale_t)0) { - ret = strerror_l(errnum, copy); - - if (loc == LC_GLOBAL_LOCALE) { - freelocale(copy); + if (loc != (locale_t)0) { + ret = strerror_l(errnum, loc); + if (free_loc) { + freelocale(loc); } } +#elif BFS_HAS_STRERROR_R_POSIX + if (strerror_r(errnum, buf, sizeof(buf)) == 0) { + ret = buf; + } +#elif BFS_HAS_STRERROR_R_GNU + ret = strerror_r(errnum, buf, sizeof(buf)); #endif if (!ret) { @@ -358,6 +322,40 @@ const char *xstrerror(int errnum) { return ret; } +/** Get the single character describing the given file type. */ +static char type_char(mode_t mode) { + switch (mode & S_IFMT) { + case S_IFREG: + return '-'; + case S_IFBLK: + return 'b'; + case S_IFCHR: + return 'c'; + case S_IFDIR: + return 'd'; + case S_IFLNK: + return 'l'; + case S_IFIFO: + return 'p'; + case S_IFSOCK: + return 's'; +#ifdef S_IFDOOR + case S_IFDOOR: + return 'D'; +#endif +#ifdef S_IFPORT + case S_IFPORT: + return 'P'; +#endif +#ifdef S_IFWHT + case S_IFWHT: + return 'w'; +#endif + } + + return '?'; +} + void xstrmode(mode_t mode, char str[11]) { strcpy(str, "----------"); @@ -489,7 +487,7 @@ int dup_cloexec(int fd) { } int pipe_cloexec(int pipefd[2]) { -#if __linux__ || (BSD && !__APPLE__) +#if BFS_HAS_PIPE2 return pipe2(pipefd, O_CLOEXEC); #else if (pipe(pipefd) != 0) { @@ -581,10 +579,7 @@ int xfaccessat(int fd, const char *path, int amode) { } char *xconfstr(int name) { -#if __ANDROID__ - errno = ENOTSUP; - return NULL; -#else +#if BFS_HAS_CONFSTR size_t len = confstr(name, NULL, 0); if (len == 0) { return NULL; @@ -601,7 +596,10 @@ char *xconfstr(int name) { } return str; -#endif // !__ANDROID__ +#else + errno = ENOTSUP; + return NULL; +#endif } char *xreadlinkat(int fd, const char *path, size_t size) { @@ -639,8 +637,14 @@ error: return NULL; } +#if BFS_HAS_STRTOFFLAGS +# define BFS_STRTOFFLAGS strtofflags +#elif BFS_HAS_STRING_TO_FLAGS +# define BFS_STRTOFFLAGS string_to_flags +#endif + int xstrtofflags(const char **str, unsigned long long *set, unsigned long long *clear) { -#if BSD && !__GNU__ +#ifdef BFS_STRTOFFLAGS char *str_arg = (char *)*str; #if __OpenBSD__ @@ -651,11 +655,7 @@ int xstrtofflags(const char **str, unsigned long long *set, unsigned long long * bfs_fflags_t set_arg = 0; bfs_fflags_t clear_arg = 0; -#if __NetBSD__ - int ret = string_to_flags(&str_arg, &set_arg, &clear_arg); -#else - int ret = strtofflags(&str_arg, &set_arg, &clear_arg); -#endif + int ret = BFS_STRTOFFLAGS(&str_arg, &set_arg, &clear_arg); *str = str_arg; *set = set_arg; @@ -665,7 +665,7 @@ int xstrtofflags(const char **str, unsigned long long *set, unsigned long long * errno = EINVAL; } return ret; -#else // !BSD +#else // !BFS_STRTOFFLAGS errno = ENOTSUP; return -1; #endif diff --git a/src/bfstd.h b/src/bfstd.h index fc22971..f91e380 100644 --- a/src/bfstd.h +++ b/src/bfstd.h @@ -8,7 +8,7 @@ #ifndef BFS_BFSTD_H #define BFS_BFSTD_H -#include "config.h" +#include "prelude.h" #include "sanity.h" #include <stddef.h> @@ -283,11 +283,21 @@ 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 +/** + * Get the access/change/modification time from a struct stat. + */ +#if BFS_HAS_ST_ACMTIM +# define ST_ATIM(sb) (sb).st_atim +# define ST_CTIM(sb) (sb).st_ctim +# define ST_MTIM(sb) (sb).st_mtim +#elif BFS_HAS_ST_ACMTIMESPEC +# define ST_ATIM(sb) (sb).st_atimespec +# define ST_CTIM(sb) (sb).st_ctimespec +# define ST_MTIM(sb) (sb).st_mtimespec +#else +# define ST_ATIM(sb) ((struct timespec) { .tv_sec = (sb).st_atime }) +# define ST_CTIM(sb) ((struct timespec) { .tv_sec = (sb).st_ctime }) +# define ST_MTIM(sb) ((struct timespec) { .tv_sec = (sb).st_mtime }) #endif // #include <sys/wait.h> @@ -18,10 +18,10 @@ * various helper functions to take fewer parameters. */ +#include "prelude.h" #include "bftw.h" #include "alloc.h" #include "bfstd.h" -#include "config.h" #include "diag.h" #include "dir.h" #include "dstring.h" @@ -54,7 +54,7 @@ struct BFTW { /** The file type. */ enum bfs_type type; - /** The errno that occurred, if type == BFTW_ERROR. */ + /** The errno that occurred, if type == BFS_ERROR. */ int error; /** A parent file descriptor for the *at() family of calls. */ @@ -104,7 +104,7 @@ const struct bfs_stat *bftw_cached_stat(const struct BFTW *ftwbuf, enum bfs_stat * @param flags * flags for bfs_stat(). Pass ftwbuf->stat_flags for the default flags. * @return - * The type of the file, or BFTW_ERROR if an error occurred. + * The type of the file, or BFS_ERROR if an error occurred. */ enum bfs_type bftw_type(const struct BFTW *ftwbuf, enum bfs_stat_flags flags); @@ -8,7 +8,7 @@ #ifndef BFS_BIT_H #define BFS_BIT_H -#include "config.h" +#include "prelude.h" #include <limits.h> #include <stdint.h> diff --git a/src/color.c b/src/color.c index 8c32a68..f004bf2 100644 --- a/src/color.c +++ b/src/color.c @@ -1,11 +1,11 @@ // Copyright © Tavian Barnes <tavianator@tavianator.com> // SPDX-License-Identifier: 0BSD +#include "prelude.h" #include "color.h" #include "alloc.h" #include "bfstd.h" #include "bftw.h" -#include "config.h" #include "diag.h" #include "dir.h" #include "dstring.h" diff --git a/src/color.h b/src/color.h index e3e7973..3278cd6 100644 --- a/src/color.h +++ b/src/color.h @@ -8,7 +8,7 @@ #ifndef BFS_COLOR_H #define BFS_COLOR_H -#include "config.h" +#include "prelude.h" #include "dstring.h" #include <stdio.h> @@ -237,7 +237,7 @@ int bfs_ctx_free(struct bfs_ctx *ctx) { if (bfs_ctx_fclose(ctx, ctx_file) != 0) { if (cerr) { - bfs_error(ctx, "'%s': %m.\n", ctx_file->path); + bfs_error(ctx, "%pq: %m.\n", ctx_file->path); } ret = -1; } @@ -8,9 +8,9 @@ #ifndef BFS_CTX_H #define BFS_CTX_H +#include "prelude.h" #include "alloc.h" #include "bftw.h" -#include "config.h" #include "diag.h" #include "expr.h" #include "trie.h" @@ -1,11 +1,11 @@ // Copyright © Tavian Barnes <tavianator@tavianator.com> // SPDX-License-Identifier: 0BSD +#include "prelude.h" #include "diag.h" #include "alloc.h" #include "bfstd.h" #include "color.h" -#include "config.h" #include "ctx.h" #include "dstring.h" #include "expr.h" @@ -8,7 +8,7 @@ #ifndef BFS_DIAG_H #define BFS_DIAG_H -#include "config.h" +#include "prelude.h" #include <stdarg.h> /** @@ -1,10 +1,10 @@ // Copyright © Tavian Barnes <tavianator@tavianator.com> // SPDX-License-Identifier: 0BSD +#include "prelude.h" #include "dir.h" #include "alloc.h" #include "bfstd.h" -#include "config.h" #include "diag.h" #include "sanity.h" #include "trie.h" @@ -17,7 +17,7 @@ #include <unistd.h> #if BFS_USE_GETDENTS -# if __linux__ +# if BFS_HAS_GETDENTS64_SYSCALL # include <sys/syscall.h> # endif @@ -25,12 +25,14 @@ static ssize_t bfs_getdents(int fd, void *buf, size_t size) { sanitize_uninit(buf, size); -#if (__linux__ && __GLIBC__ && !__GLIBC_PREREQ(2, 30)) || __ANDROID__ - ssize_t ret = syscall(SYS_getdents64, fd, buf, size); -#elif __linux__ +#if BFS_HAS_GETDENTS + ssize_t ret = getdents(fd, buf, size); +#elif BFS_HAS_GETDENTS64 ssize_t ret = getdents64(fd, buf, size); +#elif BFS_HAS_GETDENTS64_SYSCALL + ssize_t ret = syscall(SYS_getdents64, fd, buf, size); #else - ssize_t ret = getdents(fd, buf, size); +# error "No getdents() implementation" #endif if (ret > 0) { @@ -42,7 +44,7 @@ static ssize_t bfs_getdents(int fd, void *buf, size_t size) { #endif // BFS_USE_GETDENTS -#if BFS_USE_GETDENTS && __linux__ +#if BFS_USE_GETDENTS && !BFS_HAS_GETDENTS /** Directory entry type for bfs_getdents() */ typedef struct dirent64 sys_dirent; #else @@ -351,7 +353,7 @@ int bfs_closedir(struct bfs_dir *dir) { int bfs_unwrapdir(struct bfs_dir *dir) { #if BFS_USE_GETDENTS int ret = dir->fd; -#elif __FreeBSD__ +#elif BFS_HAS_FDCLOSEDIR int ret = fdclosedir(dir->dir); #endif @@ -8,14 +8,15 @@ #ifndef BFS_DIR_H #define BFS_DIR_H +#include "prelude.h" #include <sys/types.h> /** * Whether the implementation uses the getdents() syscall directly, rather than * libc's readdir(). */ -#ifndef BFS_USE_GETDENTS -# define BFS_USE_GETDENTS (__linux__ || __FreeBSD__) +#if !defined(BFS_USE_GETDENTS) && (__linux__ || __FreeBSD__) +# define BFS_USE_GETDENTS (BFS_HAS_GETDENTS || BFS_HAS_GETDENTS64 | BFS_HAS_GETDENTS64_SYSCALL) #endif /** @@ -152,7 +153,7 @@ int bfs_closedir(struct bfs_dir *dir); * Whether the bfs_unwrapdir() function is supported. */ #ifndef BFS_USE_UNWRAPDIR -# define BFS_USE_UNWRAPDIR (BFS_USE_GETDENTS || __FreeBSD__) +# define BFS_USE_UNWRAPDIR (BFS_USE_GETDENTS || BFS_HAS_FDCLOSEDIR) #endif #if BFS_USE_UNWRAPDIR diff --git a/src/dstring.c b/src/dstring.c index 10b0fad..86ab646 100644 --- a/src/dstring.c +++ b/src/dstring.c @@ -1,10 +1,10 @@ // Copyright © Tavian Barnes <tavianator@tavianator.com> // SPDX-License-Identifier: 0BSD +#include "prelude.h" #include "dstring.h" #include "alloc.h" #include "bit.h" -#include "config.h" #include "diag.h" #include <stdarg.h> #include <stddef.h> @@ -174,7 +174,7 @@ int dstrxcpy(dchar **dest, const char *src, size_t len) { return 0; } -char *dstrprintf(const char *format, ...) { +dchar *dstrprintf(const char *format, ...) { va_list args; va_start(args, format); @@ -184,7 +184,7 @@ char *dstrprintf(const char *format, ...) { return str; } -char *dstrvprintf(const char *format, va_list args) { +dchar *dstrvprintf(const char *format, va_list args) { // Guess a capacity to try to avoid reallocating dchar *str = dstralloc(2 * strlen(format)); if (!str) { @@ -245,6 +245,7 @@ int dstrvcatf(dchar **str, const char *format, va_list args) { return 0; fail: + va_end(copy); *tail = '\0'; return -1; } diff --git a/src/dstring.h b/src/dstring.h index 6006199..14e1d3e 100644 --- a/src/dstring.h +++ b/src/dstring.h @@ -8,8 +8,8 @@ #ifndef BFS_DSTRING_H #define BFS_DSTRING_H +#include "prelude.h" #include "bfstd.h" -#include "config.h" #include <stdarg.h> #include <stddef.h> @@ -244,7 +244,7 @@ int dstrxcpy(dchar **dest, const char *str, size_t len); * The created string, or NULL on failure. */ attr(printf(1, 2)) -char *dstrprintf(const char *format, ...); +dchar *dstrprintf(const char *format, ...); /** * Create a dynamic string from a format string and a va_list. @@ -257,7 +257,7 @@ char *dstrprintf(const char *format, ...); * The created string, or NULL on failure. */ attr(printf(1, 0)) -char *dstrvprintf(const char *format, va_list args); +dchar *dstrvprintf(const char *format, va_list args); /** * Format some text onto the end of a dynamic string. @@ -5,12 +5,12 @@ * Implementation of all the primary expressions. */ +#include "prelude.h" #include "eval.h" #include "bar.h" #include "bfstd.h" #include "bftw.h" #include "color.h" -#include "config.h" #include "ctx.h" #include "diag.h" #include "dir.h" @@ -145,6 +145,20 @@ bool bfs_expr_cmp(const struct bfs_expr *expr, long long n) { return false; } +/** Common code for fnmatch() tests. */ +static bool eval_fnmatch(const struct bfs_expr *expr, const char *str) { + if (expr->literal) { +#ifdef FNM_CASEFOLD + if (expr->fnm_flags & FNM_CASEFOLD) { + return strcasecmp(expr->pattern, str) == 0; + } +#endif + return strcmp(expr->pattern, str) == 0; + } else { + return fnmatch(expr->pattern, str, expr->fnm_flags) == 0; + } +} + /** * -true test. */ @@ -194,6 +208,21 @@ bool eval_capable(const struct bfs_expr *expr, struct bfs_eval *state) { } /** + * -context test. + */ +bool eval_context(const struct bfs_expr *expr, struct bfs_eval *state) { + char *con = bfs_getfilecon(state->ftwbuf); + if (!con) { + eval_report_error(state); + return false; + } + + bool ret = eval_fnmatch(expr, con); + bfs_freecon(con); + return ret; +} + +/** * Get the given timespec field out of a stat buffer. */ static const struct timespec *eval_stat_time(const struct bfs_stat *statbuf, enum bfs_stat_field field, struct bfs_eval *state) { @@ -416,38 +445,42 @@ bool eval_depth(const struct bfs_expr *expr, struct bfs_eval *state) { * -empty test. */ bool eval_empty(const struct bfs_expr *expr, struct bfs_eval *state) { - bool ret = false; const struct BFTW *ftwbuf = state->ftwbuf; + const struct bfs_stat *statbuf; + struct bfs_dir *dir; - if (ftwbuf->type == BFS_DIR) { - struct bfs_dir *dir = bfs_allocdir(); + switch (ftwbuf->type) { + case BFS_REG: + statbuf = eval_stat(state); + return statbuf && statbuf->size == 0; + + case BFS_DIR: + dir = bfs_allocdir(); if (!dir) { - eval_report_error(state); - return ret; + goto error; } if (bfs_opendir(dir, ftwbuf->at_fd, ftwbuf->at_path, 0) != 0) { - eval_report_error(state); - return ret; + goto error; } int did_read = bfs_readdir(dir, NULL); + bfs_closedir(dir); + if (did_read < 0) { - eval_report_error(state); - } else { - ret = !did_read; + goto error; } - bfs_closedir(dir); free(dir); - } else if (ftwbuf->type == BFS_REG) { - const struct bfs_stat *statbuf = eval_stat(state); - if (statbuf) { - ret = statbuf->size == 0; - } - } + return did_read == 0; + error: + eval_report_error(state); + free(dir); + return false; - return ret; + default: + return false; + } } /** @@ -546,20 +579,6 @@ bool eval_links(const struct bfs_expr *expr, struct bfs_eval *state) { return bfs_expr_cmp(expr, statbuf->nlink); } -/** Common code for fnmatch() tests. */ -static bool eval_fnmatch(const struct bfs_expr *expr, const char *str) { - if (expr->literal) { -#ifdef FNM_CASEFOLD - if (expr->fnm_flags & FNM_CASEFOLD) { - return strcasecmp(expr->pattern, str) == 0; - } -#endif - return strcmp(expr->pattern, str) == 0; - } else { - return fnmatch(expr->pattern, str, expr->fnm_flags) == 0; - } -} - /** * -i?lname test. */ @@ -9,7 +9,7 @@ #ifndef BFS_EVAL_H #define BFS_EVAL_H -#include "config.h" +#include "prelude.h" struct bfs_ctx; struct bfs_expr; @@ -49,6 +49,7 @@ bool eval_false(const struct bfs_expr *expr, struct bfs_eval *state); bool eval_access(const struct bfs_expr *expr, struct bfs_eval *state); bool eval_acl(const struct bfs_expr *expr, struct bfs_eval *state); bool eval_capable(const struct bfs_expr *expr, struct bfs_eval *state); +bool eval_context(const struct bfs_expr *expr, struct bfs_eval *state); bool eval_perm(const struct bfs_expr *expr, struct bfs_eval *state); bool eval_xattr(const struct bfs_expr *expr, struct bfs_eval *state); bool eval_xattrname(const struct bfs_expr *expr, struct bfs_eval *state); @@ -1,12 +1,12 @@ // Copyright © Tavian Barnes <tavianator@tavianator.com> // SPDX-License-Identifier: 0BSD +#include "prelude.h" #include "exec.h" #include "alloc.h" #include "bfstd.h" #include "bftw.h" #include "color.h" -#include "config.h" #include "ctx.h" #include "diag.h" #include "dstring.h" @@ -8,8 +8,8 @@ #ifndef BFS_EXPR_H #define BFS_EXPR_H +#include "prelude.h" #include "color.h" -#include "config.h" #include "eval.h" #include "stat.h" #include <sys/types.h> diff --git a/src/fsade.c b/src/fsade.c index ee17416..d56fb07 100644 --- a/src/fsade.c +++ b/src/fsade.c @@ -1,11 +1,11 @@ // Copyright © Tavian Barnes <tavianator@tavianator.com> // SPDX-License-Identifier: 0BSD +#include "prelude.h" #include "fsade.h" #include "atomic.h" #include "bfstd.h" #include "bftw.h" -#include "config.h" #include "dir.h" #include "dstring.h" #include "sanity.h" @@ -22,6 +22,10 @@ # include <sys/capability.h> #endif +#if BFS_CAN_CHECK_CONTEXT +# include <selinux/selinux.h> +#endif + #if BFS_USE_SYS_EXTATTR_H # include <sys/extattr.h> #elif BFS_USE_SYS_XATTR_H @@ -117,11 +121,23 @@ static bool is_absence_error(int error) { #if BFS_CAN_CHECK_ACL +#if BFS_HAS_ACL_GET_FILE + /** Unified interface for incompatible acl_get_entry() implementations. */ static int bfs_acl_entry(acl_t acl, int which, acl_entry_t *entry) { -#if __DragonFly__ && !defined(ACL_FIRST_ENTRY) && !defined(ACL_NEXT_ENTRY) -# define ACL_FIRST_ENTRY 0 -# define ACL_NEXT_ENTRY 1 +#if BFS_HAS_ACL_GET_ENTRY + int ret = acl_get_entry(acl, which, entry); +# if __APPLE__ + // POSIX.1e specifies a return value of 1 for success, but macOS returns 0 instead + return !ret; +# else + return ret; +# endif +#elif __DragonFly__ +# if !defined(ACL_FIRST_ENTRY) && !defined(ACL_NEXT_ENTRY) +# define ACL_FIRST_ENTRY 0 +# define ACL_NEXT_ENTRY 1 +# endif switch (which) { case ACL_FIRST_ENTRY: @@ -138,24 +154,22 @@ static int bfs_acl_entry(acl_t acl, int which, acl_entry_t *entry) { acl_entry_t last = &acl->acl_entry[acl->acl_cnt]; return *entry == last; #else - int ret = acl_get_entry(acl, which, entry); -# if __APPLE__ - // POSIX.1e specifies a return value of 1 for success, but macOS returns 0 instead - return !ret; -# else - return ret; -# endif + errno = ENOTSUP; + return -1; #endif } /** Unified interface for acl_get_tag_type(). */ attr(maybe_unused) static int bfs_acl_tag_type(acl_entry_t entry, acl_tag_t *tag) { -#if __DragonFly__ +#if BFS_HAS_ACL_GET_TAG_TYPE + return acl_get_tag_type(entry, tag); +#elif __DragonFly__ *tag = entry->ae_tag; return 0; #else - return acl_get_tag_type(entry, tag); + errno = ENOTSUP; + return -1; #endif } @@ -195,7 +209,7 @@ static int bfs_check_acl_type(acl_t acl, acl_type_t type) { return bfs_check_posix1e_acl(acl, false); } -#if __FreeBSD__ +#if BFS_HAS_ACL_IS_TRIVIAL_NP int trivial; int ret = acl_is_trivial_np(acl, &trivial); @@ -209,34 +223,40 @@ static int bfs_check_acl_type(acl_t acl, acl_type_t type) { } else { return 1; } -#else // !__FreeBSD__ +#else return bfs_check_posix1e_acl(acl, true); #endif } +#endif // BFS_HAS_ACL_GET_FILE + int bfs_check_acl(const struct BFTW *ftwbuf) { + if (ftwbuf->type == BFS_LNK) { + return 0; + } + + const char *path = fake_at(ftwbuf); + +#if BFS_HAS_ACL_TRIVIAL + int ret = acl_trivial(path); + int error = errno; +#elif BFS_HAS_ACL_GET_FILE static const acl_type_t acl_types[] = { -#if __APPLE__ +# if __APPLE__ // macOS gives EINVAL for either of the two standard ACL types, // supporting only ACL_TYPE_EXTENDED ACL_TYPE_EXTENDED, -#else +# else // The two standard POSIX.1e ACL types ACL_TYPE_ACCESS, ACL_TYPE_DEFAULT, -#endif +# endif -#ifdef ACL_TYPE_NFS4 +# ifdef ACL_TYPE_NFS4 ACL_TYPE_NFS4, -#endif +# endif }; - if (ftwbuf->type == BFS_LNK) { - return 0; - } - - const char *path = fake_at(ftwbuf); - int ret = -1, error = 0; for (size_t i = 0; i < countof(acl_types) && ret <= 0; ++i) { acl_type_t type = acl_types[i]; @@ -260,6 +280,7 @@ int bfs_check_acl(const struct BFTW *ftwbuf) { error = errno; acl_free(acl); } +#endif free_fake_at(ftwbuf, path); errno = error; @@ -323,17 +344,62 @@ int bfs_check_capabilities(const struct BFTW *ftwbuf) { #if BFS_CAN_CHECK_XATTRS +#if BFS_USE_SYS_EXTATTR_H + +/** Wrapper for extattr_list_{file,link}. */ +static ssize_t bfs_extattr_list(const char *path, enum bfs_type type, int namespace) { + if (type == BFS_LNK) { +#if BFS_HAS_EXTATTR_LIST_LINK + return extattr_list_link(path, namespace, NULL, 0); +#elif BFS_HAS_EXTATTR_GET_LINK + return extattr_get_link(path, namespace, "", NULL, 0); +#else + return 0; +#endif + } + +#if BFS_HAS_EXTATTR_LIST_FILE + return extattr_list_file(path, namespace, NULL, 0); +#elif BFS_HAS_EXTATTR_GET_FILE + // From man extattr(2): + // + // In earlier versions of this API, passing an empty string for the + // attribute name to extattr_get_file() would return the list of attributes + // defined for the target object. This interface has been deprecated in + // preference to using the explicit list API, and should not be used. + return extattr_get_file(path, namespace, "", NULL, 0); +#else + return 0; +#endif +} + +/** Wrapper for extattr_get_{file,link}. */ +static ssize_t bfs_extattr_get(const char *path, enum bfs_type type, int namespace, const char *name) { + if (type == BFS_LNK) { +#if BFS_HAS_EXTATTR_GET_LINK + return extattr_get_link(path, namespace, name, NULL, 0); +#else + return 0; +#endif + } + +#if BFS_HAS_EXTATTR_GET_FILE + return extattr_get_file(path, namespace, name, NULL, 0); +#else + return 0; +#endif +} + +#endif // BFS_USE_SYS_EXTATTR_H + int bfs_check_xattrs(const struct BFTW *ftwbuf) { const char *path = fake_at(ftwbuf); ssize_t len; #if BFS_USE_SYS_EXTATTR_H - ssize_t (*extattr_list)(const char *, int, void *, size_t) = - ftwbuf->type == BFS_LNK ? extattr_list_link : extattr_list_file; - - len = extattr_list(path, EXTATTR_NAMESPACE_SYSTEM, NULL, 0); + len = bfs_extattr_list(path, ftwbuf->type, EXTATTR_NAMESPACE_SYSTEM); if (len <= 0) { - len = extattr_list(path, EXTATTR_NAMESPACE_USER, NULL, 0); + len = bfs_extattr_list(path, ftwbuf->type, EXTATTR_NAMESPACE_USER); } #elif __APPLE__ int options = ftwbuf->type == BFS_LNK ? XATTR_NOFOLLOW : 0; @@ -367,12 +433,9 @@ int bfs_check_xattr_named(const struct BFTW *ftwbuf, const char *name) { ssize_t len; #if BFS_USE_SYS_EXTATTR_H - ssize_t (*extattr_get)(const char *, int, const char *, void *, size_t) = - ftwbuf->type == BFS_LNK ? extattr_get_link : extattr_get_file; - - len = extattr_get(path, EXTATTR_NAMESPACE_SYSTEM, name, NULL, 0); + len = bfs_extattr_get(path, ftwbuf->type, EXTATTR_NAMESPACE_SYSTEM, name); if (len < 0) { - len = extattr_get(path, EXTATTR_NAMESPACE_USER, name, NULL, 0); + len = bfs_extattr_get(path, ftwbuf->type, EXTATTR_NAMESPACE_USER, name); } #elif __APPLE__ int options = ftwbuf->type == BFS_LNK ? XATTR_NOFOLLOW : 0; @@ -414,3 +477,32 @@ int bfs_check_xattr_named(const struct BFTW *ftwbuf, const char *name) { } #endif + +char *bfs_getfilecon(const struct BFTW *ftwbuf) { +#if BFS_CAN_CHECK_CONTEXT + const char *path = fake_at(ftwbuf); + + char *con; + int ret; + if (ftwbuf->type == BFS_LNK) { + ret = lgetfilecon(path, &con); + } else { + ret = getfilecon(path, &con); + } + + if (ret >= 0) { + return con; + } else { + return NULL; + } +#else + errno = ENOTSUP; + return NULL; +#endif +} + +void bfs_freecon(char *con) { +#if BFS_CAN_CHECK_CONTEXT + freecon(con); +#endif +} diff --git a/src/fsade.h b/src/fsade.h index 413938d..eefef9f 100644 --- a/src/fsade.h +++ b/src/fsade.h @@ -9,16 +9,13 @@ #ifndef BFS_FSADE_H #define BFS_FSADE_H -#include "config.h" +#include "prelude.h" -#define BFS_CAN_CHECK_ACL BFS_USE_SYS_ACL_H +#define BFS_CAN_CHECK_ACL (BFS_HAS_ACL_GET_FILE || BFS_HAS_ACL_TRIVIAL) -#if !defined(BFS_CAN_CHECK_CAPABILITIES) && BFS_USE_SYS_CAPABILITY_H -# include <sys/capability.h> -# ifdef CAP_CHOWN -# define BFS_CAN_CHECK_CAPABILITIES true -# endif -#endif +#define BFS_CAN_CHECK_CAPABILITIES BFS_USE_LIBCAP + +#define BFS_CAN_CHECK_CONTEXT BFS_USE_LIBSELINUX #define BFS_CAN_CHECK_XATTRS (BFS_USE_SYS_EXTATTR_H || BFS_USE_SYS_XATTR_H) @@ -66,4 +63,19 @@ int bfs_check_xattrs(const struct BFTW *ftwbuf); */ int bfs_check_xattr_named(const struct BFTW *ftwbuf, const char *name); +/** + * Get a file's SELinux context + * + * @param ftwbuf + * The file to check. + * @return + * The file's SELinux context, or NULL on failure. + */ +char *bfs_getfilecon(const struct BFTW *ftwbuf); + +/** + * Free a bfs_getfilecon() result. + */ +void bfs_freecon(char *con); + #endif // BFS_FSADE_H @@ -118,12 +118,12 @@ * [1]: https://arxiv.org/abs/2201.02179 */ +#include "prelude.h" #include "ioq.h" #include "alloc.h" #include "atomic.h" #include "bfstd.h" #include "bit.h" -#include "config.h" #include "diag.h" #include "dir.h" #include "stat.h" @@ -918,17 +918,14 @@ static void ioq_thread_join(struct ioq_thread *thread) { } struct ioq *ioq_create(size_t depth, size_t nthreads) { - struct ioq *ioq = ALLOC_FLEX(struct ioq, threads, nthreads); + struct ioq *ioq = ZALLOC_FLEX(struct ioq, threads, nthreads); if (!ioq) { goto fail; } ioq->depth = depth; - ioq->size = 0; - ioq->cancel = false; ARENA_INIT(&ioq->ents, struct ioq_ent); - #if BFS_USE_LIBURING && BFS_USE_STATX ARENA_INIT(&ioq->xbufs, struct statx); #endif @@ -8,7 +8,7 @@ #ifndef BFS_IOQ_H #define BFS_IOQ_H -#include "config.h" +#include "prelude.h" #include "dir.h" #include "stat.h" #include <stddef.h> @@ -26,7 +26,7 @@ * - bit.h (bit manipulation) * - bfstd.[ch] (standard library wrappers/polyfills) * - color.[ch] (for pretty terminal colors) - * - config.h (configuration and feature/platform detection) + * - prelude.h (configuration and feature/platform detection) * - diag.[ch] (formats diagnostic messages) * - dir.[ch] (a directory API facade) * - dstring.[ch] (a dynamic string library) @@ -45,8 +45,8 @@ * - xtime.[ch] (date/time handling utilities) */ +#include "prelude.h" #include "bfstd.h" -#include "config.h" #include "ctx.h" #include "diag.h" #include "eval.h" @@ -1,10 +1,10 @@ // Copyright © Tavian Barnes <tavianator@tavianator.com> // SPDX-License-Identifier: 0BSD +#include "prelude.h" #include "mtab.h" #include "alloc.h" #include "bfstd.h" -#include "config.h" #include "stat.h" #include "trie.h" #include <errno.h> @@ -13,11 +13,11 @@ #include <string.h> #include <sys/types.h> -#if !defined(BFS_USE_MNTENT) && BFS_USE_MNTENT_H +#if !defined(BFS_USE_MNTENT) && BFS_HAS_GETMNTENT_1 # define BFS_USE_MNTENT true -#elif !defined(BFS_USE_MNTINFO) && BSD +#elif !defined(BFS_USE_MNTINFO) && BFS_HAS_GETMNTINFO # define BFS_USE_MNTINFO true -#elif !defined(BFS_USE_MNTTAB) && __SVR4 +#elif !defined(BFS_USE_MNTTAB) && BFS_HAS_GETMNTENT_2 # define BFS_USE_MNTTAB true #endif @@ -27,7 +27,6 @@ # include <stdio.h> #elif BFS_USE_MNTINFO # include <sys/mount.h> -# include <sys/ucred.h> #elif BFS_USE_MNTTAB # include <stdio.h> # include <sys/mnttab.h> @@ -41,11 +40,16 @@ struct bfs_mount { char *path; /** The filesystem type. */ char *type; + /** Buffer for the strings. */ + char buf[]; }; struct bfs_mtab { + /** Mount point arena. */ + struct varena varena; + /** The array of mount points. */ - struct bfs_mount *mounts; + struct bfs_mount **mounts; /** The number of mount points. */ size_t nmounts; @@ -63,28 +67,37 @@ struct bfs_mtab { */ attr(maybe_unused) static int bfs_mtab_add(struct bfs_mtab *mtab, const char *path, const char *type) { - struct bfs_mount *mount = RESERVE(struct bfs_mount, &mtab->mounts, &mtab->nmounts); + size_t path_size = strlen(path) + 1; + size_t type_size = strlen(type) + 1; + size_t size = path_size + type_size; + struct bfs_mount *mount = varena_alloc(&mtab->varena, size); if (!mount) { return -1; } - mount->path = strdup(path); - mount->type = strdup(type); - if (!mount->path || !mount->type) { - goto fail; + struct bfs_mount **ptr = RESERVE(struct bfs_mount *, &mtab->mounts, &mtab->nmounts); + if (!ptr) { + goto free; } + *ptr = mount; + + mount->path = mount->buf; + memcpy(mount->path, path, path_size); + + mount->type = mount->buf + path_size; + memcpy(mount->type, type, type_size); const char *name = path + xbaseoff(path); if (!trie_insert_str(&mtab->names, name)) { - goto fail; + goto shrink; } return 0; -fail: - free(mount->type); - free(mount->path); +shrink: --mtab->nmounts; +free: + varena_free(&mtab->varena, mount, size); return -1; } @@ -94,6 +107,8 @@ struct bfs_mtab *bfs_mtab_parse(void) { return NULL; } + VARENA_INIT(&mtab->varena, struct bfs_mount, buf); + trie_init(&mtab->names); trie_init(&mtab->types); @@ -132,7 +147,7 @@ struct bfs_mtab *bfs_mtab_parse(void) { bfs_statfs *mntbuf; int size = getmntinfo(&mntbuf, MNT_WAIT); - if (size < 0) { + if (size <= 0) { error = errno; goto fail; } @@ -195,7 +210,7 @@ static int bfs_mtab_fill_types(struct bfs_mtab *mtab) { struct bfs_stat parent_stat; for (size_t i = 0; i < mtab->nmounts; ++i) { - struct bfs_mount *mount = &mtab->mounts[i]; + struct bfs_mount *mount = mtab->mounts[i]; const char *path = mount->path; int fd = AT_FDCWD; @@ -280,11 +295,8 @@ void bfs_mtab_free(struct bfs_mtab *mtab) { trie_destroy(&mtab->types); trie_destroy(&mtab->names); - for (size_t i = 0; i < mtab->nmounts; ++i) { - free(mtab->mounts[i].type); - free(mtab->mounts[i].path); - } free(mtab->mounts); + varena_destroy(&mtab->varena); free(mtab); } @@ -8,7 +8,7 @@ #ifndef BFS_MTAB_H #define BFS_MTAB_H -#include "config.h" +#include "prelude.h" struct bfs_stat; @@ -25,11 +25,11 @@ * effects are reachable at all, skipping the traversal if not. */ +#include "prelude.h" #include "opt.h" #include "bftw.h" #include "bit.h" #include "color.h" -#include "config.h" #include "ctx.h" #include "diag.h" #include "dir.h" @@ -947,8 +947,12 @@ static struct bfs_expr *visit_shallow(struct bfs_opt *opt, struct bfs_expr *expr expr = general(opt, expr, visitor); } + if (!expr) { + return NULL; + } + visit_fn *specific = look_up_visitor(expr, visitor->table); - if (expr && specific) { + if (specific) { expr = specific(opt, expr, visitor); } diff --git a/src/parse.c b/src/parse.c index 2dfcab2..a1155c0 100644 --- a/src/parse.c +++ b/src/parse.c @@ -8,12 +8,12 @@ * flags like always-true options, and skipping over paths wherever they appear. */ +#include "prelude.h" #include "parse.h" #include "alloc.h" #include "bfstd.h" #include "bftw.h" #include "color.h" -#include "config.h" #include "ctx.h" #include "diag.h" #include "dir.h" @@ -1076,6 +1076,67 @@ static struct bfs_expr *parse_color(struct bfs_parser *parser, int color, int ar } /** + * Common code for fnmatch() tests. + */ +static struct bfs_expr *parse_fnmatch(const struct bfs_parser *parser, struct bfs_expr *expr, bool casefold) { + if (!expr) { + return NULL; + } + + expr->pattern = expr->argv[1]; + + if (casefold) { +#ifdef FNM_CASEFOLD + expr->fnm_flags = FNM_CASEFOLD; +#else + parse_expr_error(parser, expr, "Missing platform support.\n"); + return NULL; +#endif + } else { + expr->fnm_flags = 0; + } + + // POSIX says, about fnmatch(): + // + // If pattern ends with an unescaped <backslash>, fnmatch() shall + // return a non-zero value (indicating either no match or an error). + // + // But not all implementations obey this, so check for it ourselves. + size_t i, len = strlen(expr->pattern); + for (i = 0; i < len; ++i) { + if (expr->pattern[len - i - 1] != '\\') { + break; + } + } + if (i % 2 != 0) { + parse_expr_warning(parser, expr, "Unescaped trailing backslash.\n\n"); + expr->eval_fn = eval_false; + return expr; + } + + // strcmp() can be much faster than fnmatch() since it doesn't have to + // parse the pattern, so special-case patterns with no wildcards. + // + // https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_13_01 + expr->literal = strcspn(expr->pattern, "?*\\[") == len; + + return expr; +} + +/** + * Parse -context. + */ +static struct bfs_expr *parse_context(struct bfs_parser *parser, int flag, int arg2) { +#if BFS_CAN_CHECK_CONTEXT + struct bfs_expr *expr = parse_unary_test(parser, eval_context); + return parse_fnmatch(parser, expr, false); +#else + parse_error(parser, "Missing platform support.\n"); + return NULL; +#endif +} + +/** * Parse -{false,true}. */ static struct bfs_expr *parse_const(struct bfs_parser *parser, int value, int arg2) { @@ -1632,54 +1693,6 @@ static struct bfs_expr *parse_mount(struct bfs_parser *parser, int arg1, int arg } /** - * Common code for fnmatch() tests. - */ -static struct bfs_expr *parse_fnmatch(const struct bfs_parser *parser, struct bfs_expr *expr, bool casefold) { - if (!expr) { - return NULL; - } - - expr->pattern = expr->argv[1]; - - if (casefold) { -#ifdef FNM_CASEFOLD - expr->fnm_flags = FNM_CASEFOLD; -#else - parse_expr_error(parser, expr, "Missing platform support.\n"); - return NULL; -#endif - } else { - expr->fnm_flags = 0; - } - - // POSIX says, about fnmatch(): - // - // If pattern ends with an unescaped <backslash>, fnmatch() shall - // return a non-zero value (indicating either no match or an error). - // - // But not all implementations obey this, so check for it ourselves. - size_t i, len = strlen(expr->pattern); - for (i = 0; i < len; ++i) { - if (expr->pattern[len - i - 1] != '\\') { - break; - } - } - if (i % 2 != 0) { - parse_expr_warning(parser, expr, "Unescaped trailing backslash.\n\n"); - expr->eval_fn = eval_false; - return expr; - } - - // strcmp() can be much faster than fnmatch() since it doesn't have to - // parse the pattern, so special-case patterns with no wildcards. - // - // https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_13_01 - expr->literal = strcspn(expr->pattern, "?*\\[") == len; - - return expr; -} - -/** * Parse -i?name. */ static struct bfs_expr *parse_name(struct bfs_parser *parser, int casefold, int arg2) { @@ -1742,7 +1755,7 @@ static int parse_reftime(const struct bfs_parser *parser, struct bfs_expr *expr) fprintf(stderr, " - %04d-%02d-%02d\n", year, month, tm.tm_mday); fprintf(stderr, " - %04d-%02d-%02dT%02d:%02d:%02d\n", year, month, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); -#if __FreeBSD__ +#if BFS_HAS_TM_GMTOFF int gmtoff = tm.tm_gmtoff; #else int gmtoff = -timezone; @@ -2768,6 +2781,10 @@ static struct bfs_expr *parse_help(struct bfs_parser *parser, int arg1, int arg2 cfprintf(cout, " ${blu}-capable${rs}\n"); cfprintf(cout, " Find files with POSIX.1e capabilities set\n"); #endif +#if BFS_CAN_CHECK_CONTEXT + cfprintf(cout, " ${blu}-context${rs} ${bld}GLOB${rs}\n"); + cfprintf(cout, " Find files with SELinux context matching a glob pattern\n"); +#endif cfprintf(cout, " ${blu}-depth${rs} ${bld}[-+]N${rs}\n"); cfprintf(cout, " Find files with depth ${bld}N${rs}\n"); cfprintf(cout, " ${blu}-empty${rs}\n"); @@ -2909,7 +2926,7 @@ static struct bfs_expr *parse_help(struct bfs_parser *parser, int arg1, int arg2 * "Parse" -version. */ static struct bfs_expr *parse_version(struct bfs_parser *parser, int arg1, int arg2) { - cfprintf(parser->ctx->cout, "${ex}%s${rs} ${bld}%s${rs}\n\n", BFS_COMMAND, BFS_VERSION); + cfprintf(parser->ctx->cout, "${ex}%s${rs} ${bld}%s${rs}\n\n", BFS_COMMAND, bfs_version); printf("%s\n", BFS_HOMEPAGE); @@ -2961,6 +2978,7 @@ static const struct table_entry parse_table[] = { {"-cmin", T_TEST, parse_min, BFS_STAT_CTIME}, {"-cnewer", T_TEST, parse_newer, BFS_STAT_CTIME}, {"-color", T_OPTION, parse_color, true}, + {"-context", T_TEST, parse_context, true}, {"-csince", T_TEST, parse_since, BFS_STAT_CTIME}, {"-ctime", T_TEST, parse_time, BFS_STAT_CTIME}, {"-d", T_FLAG, parse_depth}, diff --git a/src/config.h b/src/prelude.h index 892f24c..72f88b0 100644 --- a/src/config.h +++ b/src/prelude.h @@ -5,8 +5,8 @@ * Configuration and feature/platform detection. */ -#ifndef BFS_CONFIG_H -#define BFS_CONFIG_H +#ifndef BFS_PRELUDE_H +#define BFS_PRELUDE_H // Possible __STDC_VERSION__ values @@ -26,16 +26,19 @@ // bfs packaging configuration +#include "config.h" + #ifndef BFS_COMMAND # define BFS_COMMAND "bfs" #endif -#ifndef BFS_VERSION -# define BFS_VERSION "3.1.3" -#endif #ifndef BFS_HOMEPAGE # define BFS_HOMEPAGE "https://tavianator.com/projects/bfs.html" #endif +// This is a symbol instead of a literal so we don't have to rebuild everything +// when the version number changes +extern const char bfs_version[]; + // Check for system headers #ifdef __has_include @@ -46,12 +49,6 @@ #if __has_include(<paths.h>) # define BFS_HAS_PATHS_H true #endif -#if __has_include(<sys/acl.h>) -# define BFS_HAS_SYS_ACL_H true -#endif -#if __has_include(<sys/capability.h>) -# define BFS_HAS_SYS_CAPABILITY_H true -#endif #if __has_include(<sys/extattr.h>) # define BFS_HAS_SYS_EXTATTR_H true #endif @@ -78,8 +75,6 @@ #define BFS_HAS_MNTENT_H __GLIBC__ #define BFS_HAS_PATHS_H true -#define BFS_HAS_SYS_ACL_H true -#define BFS_HAS_SYS_CAPABILITY_H __linux__ #define BFS_HAS_SYS_EXTATTR_H __FreeBSD__ #define BFS_HAS_SYS_MKDEV_H false #define BFS_HAS_SYS_PARAM_H true @@ -96,14 +91,8 @@ #ifndef BFS_USE_PATHS_H # define BFS_USE_PATHS_H BFS_HAS_PATHS_H #endif -#ifndef BFS_USE_SYS_ACL_H -# define BFS_USE_SYS_ACL_H (BFS_HAS_SYS_ACL_H && !__illumos__) -#endif -#ifndef BFS_USE_SYS_CAPABILITY_H -# define BFS_USE_SYS_CAPABILITY_H (BFS_HAS_SYS_CAPABILITY_H && !__FreeBSD__) -#endif #ifndef BFS_USE_SYS_EXTATTR_H -# define BFS_USE_SYS_EXTATTR_H (BFS_HAS_SYS_EXTATTR_H && !__DragonFly__) +# define BFS_USE_SYS_EXTATTR_H BFS_HAS_SYS_EXTATTR_H #endif #ifndef BFS_USE_SYS_MKDEV_H # define BFS_USE_SYS_MKDEV_H BFS_HAS_SYS_MKDEV_H @@ -138,21 +127,6 @@ # define __has_attribute(attr) false #endif -// Platform detection - -// Get the definition of BSD if available -#if BFS_USE_SYS_PARAM_H -# include <sys/param.h> -#endif - -#ifndef __GLIBC_PREREQ -# define __GLIBC_PREREQ(maj, min) false -#endif - -#ifndef __NetBSD_Prereq__ -# define __NetBSD_Prereq__(maj, min, patch) false -#endif - // Fundamental utilities /** @@ -179,14 +153,25 @@ #endif /** + * Polyfill max_align_t if we don't already have it. + */ +#if !BFS_HAS_MAX_ALIGN_T +typedef union { +# ifdef __BIGGEST_ALIGNMENT__ + alignas(__BIGGEST_ALIGNMENT__) char c; +# else + long double ld; + long long ll; + void *ptr; +# endif +} max_align_t; +#endif + +/** * Alignment specifier that avoids false sharing. */ #define cache_align alignas(FALSE_SHARING_SIZE) -#if __COSMOPOLITAN__ -typedef long double max_align_t; -#endif - // Wrappers for attributes /** @@ -244,10 +229,19 @@ typedef long double max_align_t; #endif /** + * Annotates functions that potentially modify and return format strings. + */ +#if __has_attribute(format_arg) +# define attr_format_arg(arg) __attribute__((format_arg(arg))) +#else +# define attr_format_arg(args) +#endif + +/** * Annotates allocator-like functions. */ #if __has_attribute(malloc) -# if __GNUC__ >= 11 +# if __GNUC__ >= 11 && !__OPTIMIZE__ // malloc(deallocator) disables inlining on GCC # define attr_malloc(...) attr_nodiscard __attribute__((malloc(__VA_ARGS__))) # else # define attr_malloc(...) attr_nodiscard __attribute__((malloc)) @@ -373,4 +367,4 @@ typedef long double max_align_t; // Only trigger an error on more than 9 arguments #define attr_too_many_none(...) -#endif // BFS_CONFIG_H +#endif // BFS_PRELUDE_H diff --git a/src/printf.c b/src/printf.c index 487f039..f8428f7 100644 --- a/src/printf.c +++ b/src/printf.c @@ -1,17 +1,18 @@ // Copyright © Tavian Barnes <tavianator@tavianator.com> // SPDX-License-Identifier: 0BSD +#include "prelude.h" #include "printf.h" #include "alloc.h" #include "bfstd.h" #include "bftw.h" #include "color.h" -#include "config.h" #include "ctx.h" #include "diag.h" #include "dir.h" #include "dstring.h" #include "expr.h" +#include "fsade.h" #include "mtab.h" #include "pwcache.h" #include "stat.h" @@ -87,22 +88,20 @@ static bool should_color(CFILE *cfile, const struct bfs_fmt *fmt) { bfs_assert(ret >= 0 && (size_t)ret < sizeof(buf)); \ (void)ret -/** - * Common entry point for fprintf() with a dynamic format string. - */ -static int dyn_fprintf(FILE *file, const struct bfs_fmt *fmt, ...) { - va_list args; - va_start(args, fmt); - -#if __GNUC__ -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wformat-nonliteral" -#endif - int ret = vfprintf(file, fmt->str, args); -#if __GNUC__ -# pragma GCC diagnostic pop -#endif +/** Return a dynamic format string. */ +attr(format_arg(2)) +static const char *dyn_fmt(const char *str, const char *fake) { + bfs_assert(strcmp(str + strlen(str) - strlen(fake) + 1, fake + 1) == 0, + "Mismatched format specifiers: '%s' vs. '%s'", str, fake); + return str; +} +/** Wrapper for fprintf(). */ +attr(printf(3, 4)) +static int bfs_fprintf(CFILE *cfile, const struct bfs_fmt *fmt, const char *fake, ...) { + va_list args; + va_start(args, fake); + int ret = vfprintf(cfile->file, dyn_fmt(fmt->str, fake), args); va_end(args); return ret; } @@ -138,7 +137,7 @@ static int bfs_printf_ctime(CFILE *cfile, const struct bfs_fmt *fmt, const struc (long)ts->tv_nsec, 1900 + tm.tm_year); - return dyn_fprintf(cfile->file, fmt, buf); + return bfs_fprintf(cfile, fmt, "%s", buf); } /** %A, %B/%W, %C, %T: strftime() */ @@ -213,7 +212,7 @@ static int bfs_printf_strftime(CFILE *cfile, const struct bfs_fmt *fmt, const st bfs_assert(ret >= 0 && (size_t)ret < sizeof(buf)); (void)ret; - return dyn_fprintf(cfile->file, fmt, buf); + return bfs_fprintf(cfile, fmt, "%s", buf); } /** %b: blocks */ @@ -225,12 +224,12 @@ static int bfs_printf_b(CFILE *cfile, const struct bfs_fmt *fmt, const struct BF uintmax_t blocks = ((uintmax_t)statbuf->blocks * BFS_STAT_BLKSIZE + 511) / 512; BFS_PRINTF_BUF(buf, "%ju", blocks); - return dyn_fprintf(cfile->file, fmt, buf); + return bfs_fprintf(cfile, fmt, "%s", buf); } /** %d: depth */ static int bfs_printf_d(CFILE *cfile, const struct bfs_fmt *fmt, const struct BFTW *ftwbuf) { - return dyn_fprintf(cfile->file, fmt, (intmax_t)ftwbuf->depth); + return bfs_fprintf(cfile, fmt, "%jd", (intmax_t)ftwbuf->depth); } /** %D: device */ @@ -241,7 +240,7 @@ static int bfs_printf_D(CFILE *cfile, const struct bfs_fmt *fmt, const struct BF } BFS_PRINTF_BUF(buf, "%ju", (uintmax_t)statbuf->dev); - return dyn_fprintf(cfile->file, fmt, buf); + return bfs_fprintf(cfile, fmt, "%s", buf); } /** %f: file name */ @@ -249,7 +248,7 @@ static int bfs_printf_f(CFILE *cfile, const struct bfs_fmt *fmt, const struct BF if (should_color(cfile, fmt)) { return cfprintf(cfile, "%pF", ftwbuf); } else { - return dyn_fprintf(cfile->file, fmt, ftwbuf->path + ftwbuf->nameoff); + return bfs_fprintf(cfile, fmt, "%s", ftwbuf->path + ftwbuf->nameoff); } } @@ -265,7 +264,7 @@ static int bfs_printf_F(CFILE *cfile, const struct bfs_fmt *fmt, const struct BF return -1; } - return dyn_fprintf(cfile->file, fmt, type); + return bfs_fprintf(cfile, fmt, "%s", type); } /** %G: gid */ @@ -276,7 +275,7 @@ static int bfs_printf_G(CFILE *cfile, const struct bfs_fmt *fmt, const struct BF } BFS_PRINTF_BUF(buf, "%ju", (uintmax_t)statbuf->gid); - return dyn_fprintf(cfile->file, fmt, buf); + return bfs_fprintf(cfile, fmt, "%s", buf); } /** %g: group name */ @@ -292,7 +291,7 @@ static int bfs_printf_g(CFILE *cfile, const struct bfs_fmt *fmt, const struct BF return bfs_printf_G(cfile, fmt, ftwbuf); } - return dyn_fprintf(cfile->file, fmt, grp->gr_name); + return bfs_fprintf(cfile, fmt, "%s", grp->gr_name); } /** %h: leading directories */ @@ -321,7 +320,7 @@ static int bfs_printf_h(CFILE *cfile, const struct bfs_fmt *fmt, const struct BF if (should_color(cfile, fmt)) { ret = cfprintf(cfile, "${di}%pQ${rs}", buf); } else { - ret = dyn_fprintf(cfile->file, fmt, buf); + ret = bfs_fprintf(cfile, fmt, "%s", buf); } free(copy); @@ -337,7 +336,7 @@ static int bfs_printf_H(CFILE *cfile, const struct bfs_fmt *fmt, const struct BF return cfprintf(cfile, "${di}%pQ${rs}", ftwbuf->root); } } else { - return dyn_fprintf(cfile->file, fmt, ftwbuf->root); + return bfs_fprintf(cfile, fmt, "%s", ftwbuf->root); } } @@ -349,7 +348,7 @@ static int bfs_printf_i(CFILE *cfile, const struct bfs_fmt *fmt, const struct BF } BFS_PRINTF_BUF(buf, "%ju", (uintmax_t)statbuf->ino); - return dyn_fprintf(cfile->file, fmt, buf); + return bfs_fprintf(cfile, fmt, "%s", buf); } /** %k: 1K blocks */ @@ -361,7 +360,7 @@ static int bfs_printf_k(CFILE *cfile, const struct bfs_fmt *fmt, const struct BF uintmax_t blocks = ((uintmax_t)statbuf->blocks * BFS_STAT_BLKSIZE + 1023) / 1024; BFS_PRINTF_BUF(buf, "%ju", blocks); - return dyn_fprintf(cfile->file, fmt, buf); + return bfs_fprintf(cfile, fmt, "%s", buf); } /** %l: link target */ @@ -383,7 +382,7 @@ static int bfs_printf_l(CFILE *cfile, const struct bfs_fmt *fmt, const struct BF } } - int ret = dyn_fprintf(cfile->file, fmt, target); + int ret = bfs_fprintf(cfile, fmt, "%s", target); free(buf); return ret; } @@ -395,7 +394,7 @@ static int bfs_printf_m(CFILE *cfile, const struct bfs_fmt *fmt, const struct BF return -1; } - return dyn_fprintf(cfile->file, fmt, (unsigned int)(statbuf->mode & 07777)); + return bfs_fprintf(cfile, fmt, "%o", (unsigned int)(statbuf->mode & 07777)); } /** %M: symbolic mode */ @@ -407,7 +406,7 @@ static int bfs_printf_M(CFILE *cfile, const struct bfs_fmt *fmt, const struct BF char buf[11]; xstrmode(statbuf->mode, buf); - return dyn_fprintf(cfile->file, fmt, buf); + return bfs_fprintf(cfile, fmt, "%s", buf); } /** %n: link count */ @@ -418,7 +417,7 @@ static int bfs_printf_n(CFILE *cfile, const struct bfs_fmt *fmt, const struct BF } BFS_PRINTF_BUF(buf, "%ju", (uintmax_t)statbuf->nlink); - return dyn_fprintf(cfile->file, fmt, buf); + return bfs_fprintf(cfile, fmt, "%s", buf); } /** %p: full path */ @@ -426,7 +425,7 @@ static int bfs_printf_p(CFILE *cfile, const struct bfs_fmt *fmt, const struct BF if (should_color(cfile, fmt)) { return cfprintf(cfile, "%pP", ftwbuf); } else { - return dyn_fprintf(cfile->file, fmt, ftwbuf->path); + return bfs_fprintf(cfile, fmt, "%s", ftwbuf->path); } } @@ -447,7 +446,7 @@ static int bfs_printf_P(CFILE *cfile, const struct bfs_fmt *fmt, const struct BF copybuf.nameoff -= offset; return cfprintf(cfile, "%pP", ©buf); } else { - return dyn_fprintf(cfile->file, fmt, ftwbuf->path + offset); + return bfs_fprintf(cfile, fmt, "%s", ftwbuf->path + offset); } } @@ -459,7 +458,7 @@ static int bfs_printf_s(CFILE *cfile, const struct bfs_fmt *fmt, const struct BF } BFS_PRINTF_BUF(buf, "%ju", (uintmax_t)statbuf->size); - return dyn_fprintf(cfile->file, fmt, buf); + return bfs_fprintf(cfile, fmt, "%s", buf); } /** %S: sparseness */ @@ -475,7 +474,7 @@ static int bfs_printf_S(CFILE *cfile, const struct bfs_fmt *fmt, const struct BF } else { sparsity = (double)BFS_STAT_BLKSIZE * statbuf->blocks / statbuf->size; } - return dyn_fprintf(cfile->file, fmt, sparsity); + return bfs_fprintf(cfile, fmt, "%g", sparsity); } /** %U: uid */ @@ -486,7 +485,7 @@ static int bfs_printf_U(CFILE *cfile, const struct bfs_fmt *fmt, const struct BF } BFS_PRINTF_BUF(buf, "%ju", (uintmax_t)statbuf->uid); - return dyn_fprintf(cfile->file, fmt, buf); + return bfs_fprintf(cfile, fmt, "%s", buf); } /** %u: user name */ @@ -502,7 +501,7 @@ static int bfs_printf_u(CFILE *cfile, const struct bfs_fmt *fmt, const struct BF return bfs_printf_U(cfile, fmt, ftwbuf); } - return dyn_fprintf(cfile->file, fmt, pwd->pw_name); + return bfs_fprintf(cfile, fmt, "%s", pwd->pw_name); } static const char *bfs_printf_type(enum bfs_type type) { @@ -519,10 +518,14 @@ static const char *bfs_printf_type(enum bfs_type type) { return "p"; case BFS_LNK: return "l"; + case BFS_PORT: + return "P"; case BFS_REG: return "f"; case BFS_SOCK: return "s"; + case BFS_WHT: + return "w"; default: return "U"; } @@ -531,39 +534,29 @@ static const char *bfs_printf_type(enum bfs_type type) { /** %y: type */ static int bfs_printf_y(CFILE *cfile, const struct bfs_fmt *fmt, const struct BFTW *ftwbuf) { const char *type = bfs_printf_type(ftwbuf->type); - return dyn_fprintf(cfile->file, fmt, type); + return bfs_fprintf(cfile, fmt, "%s", type); } /** %Y: target type */ static int bfs_printf_Y(CFILE *cfile, const struct bfs_fmt *fmt, const struct BFTW *ftwbuf) { - int error = 0; - - if (ftwbuf->type != BFS_LNK) { - return bfs_printf_y(cfile, fmt, ftwbuf); - } + enum bfs_type type = bftw_type(ftwbuf, BFS_STAT_FOLLOW); + const char *str; - const char *type = "U"; - - const struct bfs_stat *statbuf = bftw_stat(ftwbuf, BFS_STAT_FOLLOW); - if (statbuf) { - type = bfs_printf_type(bfs_mode_to_type(statbuf->mode)); - } else { - switch (errno) { - case ELOOP: - type = "L"; - break; - case ENOENT: - case ENOTDIR: - type = "N"; - break; - default: - type = "?"; + int error = 0; + if (type == BFS_ERROR) { + if (errno_is_like(ELOOP)) { + str = "L"; + } else if (errno_is_like(ENOENT)) { + str = "N"; + } else { + str = "?"; error = errno; - break; } + } else { + str = bfs_printf_type(type); } - int ret = dyn_fprintf(cfile->file, fmt, type); + int ret = bfs_fprintf(cfile, fmt, "%s", str); if (error != 0) { ret = -1; errno = error; @@ -571,6 +564,19 @@ static int bfs_printf_Y(CFILE *cfile, const struct bfs_fmt *fmt, const struct BF return ret; } +/** %Z: SELinux context */ +attr(maybe_unused) +static int bfs_printf_Z(CFILE *cfile, const struct bfs_fmt *fmt, const struct BFTW *ftwbuf) { + char *con = bfs_getfilecon(ftwbuf); + if (!con) { + return -1; + } + + int ret = bfs_fprintf(cfile, fmt, "%s", con); + bfs_freecon(con); + return ret; +} + /** * Append a literal string to the chain. */ @@ -840,6 +846,15 @@ int bfs_printf_parse(const struct bfs_ctx *ctx, struct bfs_expr *expr, const cha case 'Y': fmt.fn = bfs_printf_Y; break; + case 'Z': +#if BFS_CAN_CHECK_CONTEXT + fmt.fn = bfs_printf_Z; + break; +#else + bfs_expr_error(ctx, expr); + bfs_error(ctx, "Missing platform support for '%%%c'.\n", c); + goto fmt_error; +#endif case 'A': fmt.stat_field = BFS_STAT_ATIME; diff --git a/src/pwcache.c b/src/pwcache.c index 79437d8..af8c237 100644 --- a/src/pwcache.c +++ b/src/pwcache.c @@ -1,9 +1,9 @@ // Copyright © Tavian Barnes <tavianator@tavianator.com> // SPDX-License-Identifier: 0BSD +#include "prelude.h" #include "pwcache.h" #include "alloc.h" -#include "config.h" #include "trie.h" #include <errno.h> #include <grp.h> diff --git a/src/sanity.h b/src/sanity.h index 423e6ff..e168b8f 100644 --- a/src/sanity.h +++ b/src/sanity.h @@ -8,7 +8,7 @@ #ifndef BFS_SANITY_H #define BFS_SANITY_H -#include "config.h" +#include "prelude.h" #include <stddef.h> #if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__) @@ -1,10 +1,10 @@ // Copyright © Tavian Barnes <tavianator@tavianator.com> // SPDX-License-Identifier: 0BSD +#include "prelude.h" #include "stat.h" #include "atomic.h" #include "bfstd.h" -#include "config.h" #include "diag.h" #include "sanity.h" #include <errno.h> @@ -13,7 +13,7 @@ #include <sys/stat.h> #include <sys/types.h> -#if BFS_USE_STATX && !BFS_HAS_LIBC_STATX +#if BFS_USE_STATX && !BFS_HAS_STATX # include <linux/stat.h> # include <sys/syscall.h> # include <unistd.h> @@ -62,7 +62,7 @@ int bfs_fstatat_flags(enum bfs_stat_flags flags) { ret |= AT_SYMLINK_NOFOLLOW; } -#if defined(AT_NO_AUTOMOUNT) && (!__GNU__ || __GLIBC_PREREQ(2, 35)) +#ifdef AT_NO_AUTOMOUNT ret |= AT_NO_AUTOMOUNT; #endif @@ -99,23 +99,26 @@ void bfs_stat_convert(struct bfs_stat *dest, const struct stat *src) { dest->rdev = src->st_rdev; dest->mask |= BFS_STAT_RDEV; -#if BSD +#if BFS_HAS_ST_FLAGS dest->attrs = src->st_flags; dest->mask |= BFS_STAT_ATTRS; #endif - dest->atime = src->st_atim; + dest->atime = ST_ATIM(*src); dest->mask |= BFS_STAT_ATIME; - dest->ctime = src->st_ctim; + dest->ctime = ST_CTIM(*src); dest->mask |= BFS_STAT_CTIME; - dest->mtime = src->st_mtim; + dest->mtime = ST_MTIM(*src); dest->mask |= BFS_STAT_MTIME; -#if __APPLE__ || __FreeBSD__ || __NetBSD__ +#if BFS_HAS_ST_BIRTHTIM dest->btime = src->st_birthtim; dest->mask |= BFS_STAT_BTIME; +#elif BFS_HAS_ST_BIRTHTIMESPEC + dest->btime = src->st_birthtimespec; + dest->mask |= BFS_STAT_BTIME; #endif } @@ -137,7 +140,7 @@ static int bfs_stat_impl(int at_fd, const char *at_path, int at_flags, struct bf * Wrapper for the statx() system call, which had no glibc wrapper prior to 2.28. */ static int bfs_statx(int at_fd, const char *at_path, int at_flags, unsigned int mask, struct statx *buf) { -#if BFS_HAS_LIBC_STATX +#if BFS_HAS_STATX int ret = statx(at_fd, at_path, at_flags, mask, buf); #else int ret = syscall(SYS_statx, at_fd, at_path, at_flags, mask, buf); @@ -294,27 +297,21 @@ int bfs_stat(int at_fd, const char *at_path, enum bfs_stat_flags flags, struct b return bfs_stat_tryfollow(at_fd, at_path, at_flags, flags, buf); } - // Check __GNU__ to work around https://lists.gnu.org/archive/html/bug-hurd/2021-12/msg00001.html -#if defined(AT_EMPTY_PATH) && !__GNU__ - static atomic bool has_at_ep = true; - if (load(&has_at_ep, relaxed)) { - at_flags |= AT_EMPTY_PATH; - int ret = bfs_stat_explicit(at_fd, "", at_flags, buf); - if (ret != 0 && errno == EINVAL) { - store(&has_at_ep, false, relaxed); - } else { - return ret; - } - } -#endif - - struct stat statbuf; - if (fstat(at_fd, &statbuf) == 0) { - bfs_stat_convert(buf, &statbuf); - return 0; - } else { +#if BFS_USE_STATX + // If we have statx(), use it with AT_EMPTY_PATH for its extra features + at_flags |= AT_EMPTY_PATH; + return bfs_stat_explicit(at_fd, "", at_flags, buf); +#else + // Otherwise, just use fstat() rather than fstatat(at_fd, ""), to save + // the kernel the trouble of copying in the empty string + struct stat sb; + if (fstat(at_fd, &sb) != 0) { return -1; } + + bfs_stat_convert(buf, &sb); + return 0; +#endif } const struct timespec *bfs_stat_time(const struct bfs_stat *buf, enum bfs_stat_field field) { @@ -12,21 +12,17 @@ #ifndef BFS_STAT_H #define BFS_STAT_H -#include "config.h" +#include "prelude.h" #include <sys/stat.h> #include <sys/types.h> #include <time.h> -#if defined(STATX_BASIC_STATS) && (!__ANDROID__ || __ANDROID_API__ >= 30) -# define BFS_HAS_LIBC_STATX true -#elif __linux__ +#if !BFS_HAS_STATX && BFS_HAS_STATX_SYSCALL # include <linux/stat.h> #endif #ifndef BFS_USE_STATX -# ifdef STATX_BASIC_STATS -# define BFS_USE_STATX true -# endif +# define BFS_USE_STATX (BFS_HAS_STATX || BFS_HAS_STATX_SYSCALL) #endif #if BFS_USE_SYS_PARAM_H diff --git a/src/thread.c b/src/thread.c index 200d8c3..3793896 100644 --- a/src/thread.c +++ b/src/thread.c @@ -1,9 +1,9 @@ // Copyright © Tavian Barnes <tavianator@tavianator.com> // SPDX-License-Identifier: 0BSD +#include "prelude.h" #include "thread.h" #include "bfstd.h" -#include "config.h" #include "diag.h" #include <errno.h> #include <pthread.h> diff --git a/src/thread.h b/src/thread.h index 8174fe4..db11bd8 100644 --- a/src/thread.h +++ b/src/thread.h @@ -8,7 +8,7 @@ #ifndef BFS_THREAD_H #define BFS_THREAD_H -#include "config.h" +#include "prelude.h" #include <pthread.h> #if __STDC_VERSION__ < C23 && !defined(thread_local) @@ -81,10 +81,10 @@ * and insert intermediate singleton "jump" nodes when necessary. */ +#include "prelude.h" #include "trie.h" #include "alloc.h" #include "bit.h" -#include "config.h" #include "diag.h" #include "list.h" #include <stdint.h> diff --git a/src/xregex.c b/src/xregex.c index 3df27f0..c2711bc 100644 --- a/src/xregex.c +++ b/src/xregex.c @@ -1,10 +1,10 @@ // Copyright © Tavian Barnes <tavianator@tavianator.com> // SPDX-License-Identifier: 0BSD +#include "prelude.h" #include "xregex.h" #include "alloc.h" #include "bfstd.h" -#include "config.h" #include "diag.h" #include "sanity.h" #include "thread.h" diff --git a/src/xspawn.c b/src/xspawn.c index 065fbae..0b0cea4 100644 --- a/src/xspawn.c +++ b/src/xspawn.c @@ -1,10 +1,10 @@ // Copyright © Tavian Barnes <tavianator@tavianator.com> // SPDX-License-Identifier: 0BSD +#include "prelude.h" #include "xspawn.h" #include "alloc.h" #include "bfstd.h" -#include "config.h" #include "list.h" #include <errno.h> #include <fcntl.h> @@ -209,10 +209,10 @@ int bfs_spawn_adddup2(struct bfs_spawn *ctx, int oldfd, int newfd) { * PATH ..., using the working directory of the child process after all * file_actions have been performed. * - * but macOS resolves the PATH *before* file_actions (because there + * but macOS and NetBSD resolve the PATH *before* file_actions (because there * posix_spawn() is its own syscall). */ -#define BFS_POSIX_SPAWNP_AFTER_FCHDIR !__APPLE__ +#define BFS_POSIX_SPAWNP_AFTER_FCHDIR !(__APPLE__ || __NetBSD__) int bfs_spawn_addfchdir(struct bfs_spawn *ctx, int fd) { struct bfs_spawn_action *action = bfs_spawn_action(BFS_SPAWN_FCHDIR); @@ -220,29 +220,15 @@ int bfs_spawn_addfchdir(struct bfs_spawn *ctx, int fd) { return -1; } -#ifndef BFS_HAS_POSIX_SPAWN_FCHDIR -# define BFS_HAS_POSIX_SPAWN_FCHDIR __NetBSD_Prereq__(10, 0, 0) -#endif - -#ifndef BFS_HAS_POSIX_SPAWN_FCHDIR_NP -# if __GLIBC__ -# define BFS_HAS_POSIX_SPAWN_FCHDIR_NP __GLIBC_PREREQ(2, 29) -# elif __ANDROID__ -# define BFS_HAS_POSIX_SPAWN_FCHDIR_NP (__ANDROID_API__ >= 34) -# else -# define BFS_HAS_POSIX_SPAWN_FCHDIR_NP (__linux__ || __FreeBSD__ || __APPLE__) -# endif -#endif - -#if BFS_HAS_POSIX_SPAWN_FCHDIR -# define BFS_POSIX_SPAWN_FCHDIR posix_spawn_file_actions_addfchdir -#elif BFS_HAS_POSIX_SPAWN_FCHDIR_NP -# define BFS_POSIX_SPAWN_FCHDIR posix_spawn_file_actions_addfchdir_np +#if BFS_HAS_POSIX_SPAWN_ADDFCHDIR +# define BFS_POSIX_SPAWN_ADDFCHDIR posix_spawn_file_actions_addfchdir +#elif BFS_HAS_POSIX_SPAWN_ADDFCHDIR_NP +# define BFS_POSIX_SPAWN_ADDFCHDIR posix_spawn_file_actions_addfchdir_np #endif #if _POSIX_SPAWN > 0 && defined(BFS_POSIX_SPAWN_FCHDIR) if (ctx->flags & BFS_SPAWN_USE_POSIX) { - errno = BFS_POSIX_SPAWN_FCHDIR(&ctx->actions, fd); + errno = BFS_POSIX_SPAWN_ADDFCHDIR(&ctx->actions, fd); if (errno != 0) { free(action); return -1; diff --git a/src/xspawn.h b/src/xspawn.h index a20cbd0..6a8f54a 100644 --- a/src/xspawn.h +++ b/src/xspawn.h @@ -8,7 +8,7 @@ #ifndef BFS_XSPAWN_H #define BFS_XSPAWN_H -#include "config.h" +#include "prelude.h" #include <sys/resource.h> #include <sys/types.h> #include <unistd.h> diff --git a/src/xtime.c b/src/xtime.c index bcf6dd3..eb11afa 100644 --- a/src/xtime.c +++ b/src/xtime.c @@ -1,10 +1,11 @@ // Copyright © Tavian Barnes <tavianator@tavianator.com> // SPDX-License-Identifier: 0BSD +#include "prelude.h" #include "xtime.h" #include "bfstd.h" -#include "config.h" #include "diag.h" +#include "sanity.h" #include <errno.h> #include <limits.h> #include <sys/time.h> @@ -12,13 +13,13 @@ #include <unistd.h> int xmktime(struct tm *tm, time_t *timep) { - *timep = mktime(tm); + time_t time = mktime(tm); - if (*timep == -1) { + if (time == -1) { int error = errno; struct tm tmp; - if (!localtime_r(timep, &tmp)) { + if (!localtime_r(&time, &tmp)) { bfs_bug("localtime_r(-1): %s", xstrerror(errno)); return -1; } @@ -30,9 +31,38 @@ int xmktime(struct tm *tm, time_t *timep) { } } + *timep = time; + return 0; +} + +// FreeBSD is missing an interceptor +#if BFS_HAS_TIMEGM && !(__FreeBSD__ && SANITIZE_MEMORY) + +int xtimegm(struct tm *tm, time_t *timep) { + time_t time = timegm(tm); + + if (time == -1) { + int error = errno; + + struct tm tmp; + if (!gmtime_r(&time, &tmp)) { + bfs_bug("gmtime_r(-1): %s", xstrerror(errno)); + return -1; + } + + if (tm->tm_year != tmp.tm_year || tm->tm_yday != tmp.tm_yday + || tm->tm_hour != tmp.tm_hour || tm->tm_min != tmp.tm_min || tm->tm_sec != tmp.tm_sec) { + errno = error; + return -1; + } + } + + *timep = time; return 0; } +#else + static int safe_add(int *value, int delta) { if (*value >= 0) { if (delta > INT_MAX - *value) { @@ -147,6 +177,8 @@ overflow: return -1; } +#endif // !BFS_HAS_TIMEGM + /** Parse a decimal digit. */ static int xgetdigit(char c) { int ret = c - '0'; |