diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/bfs.h | 14 | ||||
-rw-r--r-- | src/bfstd.c | 94 | ||||
-rw-r--r-- | src/bfstd.h | 30 | ||||
-rw-r--r-- | src/bftw.c | 5 | ||||
-rw-r--r-- | src/color.c | 216 | ||||
-rw-r--r-- | src/diag.h | 4 | ||||
-rw-r--r-- | src/dstring.c | 2 | ||||
-rw-r--r-- | src/ioq.c | 4 | ||||
-rw-r--r-- | src/opt.c | 19 | ||||
-rw-r--r-- | src/parse.c | 69 | ||||
-rw-r--r-- | src/prelude.h | 3 | ||||
-rw-r--r-- | src/trie.c | 2 | ||||
-rw-r--r-- | src/trie.h | 2 | ||||
-rw-r--r-- | src/xspawn.c | 78 |
14 files changed, 455 insertions, 87 deletions
@@ -202,7 +202,10 @@ extern const char bfs_ldlibs[]; * Disabled on TSan due to https://github.com/google/sanitizers/issues/342. */ #ifndef BFS_USE_TARGET_CLONES -# if __has_attribute(target_clones) && (__GLIBC__ || __FreeBSD__) && !__SANITIZE_THREAD__ +# if __has_attribute(target_clones) \ + && (__GLIBC__ || __FreeBSD__) \ + && !__SANITIZE_THREAD__ \ + && !__SANITIZE_TYPE__ # define BFS_USE_TARGET_CLONES true # else # define BFS_USE_TARGET_CLONES false @@ -219,6 +222,15 @@ extern const char bfs_ldlibs[]; #endif /** + * Mark the size of a flexible array member. + */ +#if __has_attribute(counted_by) +# define _counted_by(...) __attribute__((counted_by(__VA_ARGS__))) +#else +# define _counted_by(...) +#endif + +/** * Optimization hint to not unroll a loop. */ #if BFS_HAS_PRAGMA_NOUNROLL diff --git a/src/bfstd.c b/src/bfstd.c index 4269d55..b78af7a 100644 --- a/src/bfstd.c +++ b/src/bfstd.c @@ -235,6 +235,36 @@ static int xstrtox_epilogue(const char *str, char **end, char *endp) { return 0; } +int xstrtos(const char *str, char **end, int base, short *value) { + long n; + if (xstrtol(str, end, base, &n) != 0) { + return -1; + } + + if (n < SHRT_MIN || n > SHRT_MAX) { + errno = ERANGE; + return -1; + } + + *value = n; + return 0; +} + +int xstrtoi(const char *str, char **end, int base, int *value) { + long n; + if (xstrtol(str, end, base, &n) != 0) { + return -1; + } + + if (n < INT_MIN || n > INT_MAX) { + errno = ERANGE; + return -1; + } + + *value = n; + return 0; +} + int xstrtol(const char *str, char **end, int base, long *value) { if (xstrtox_prologue(str) != 0) { return -1; @@ -275,6 +305,70 @@ int xstrtod(const char *str, char **end, double *value) { return xstrtox_epilogue(str, end, endp); } +int xstrtous(const char *str, char **end, int base, unsigned short *value) { + unsigned long n; + if (xstrtoul(str, end, base, &n) != 0) { + return -1; + } + + if (n > USHRT_MAX) { + errno = ERANGE; + return -1; + } + + *value = n; + return 0; +} + +int xstrtoui(const char *str, char **end, int base, unsigned int *value) { + unsigned long n; + if (xstrtoul(str, end, base, &n) != 0) { + return -1; + } + + if (n > UINT_MAX) { + errno = ERANGE; + return -1; + } + + *value = n; + return 0; +} + +/** Common epilogue for xstrtou*() wrappers. */ +static int xstrtoux_epilogue(const char *str, char **end, char *endp) { + if (xstrtox_epilogue(str, end, endp) != 0) { + return -1; + } + + if (str[0] == '-') { + errno = ERANGE; + return -1; + } + + return 0; +} + +int xstrtoul(const char *str, char **end, int base, unsigned long *value) { + if (xstrtox_prologue(str) != 0) { + return -1; + } + + char *endp; + *value = strtoul(str, &endp, base); + return xstrtoux_epilogue(str, end, endp); +} + +int xstrtoull(const char *str, char **end, int base, unsigned long long *value) { + if (xstrtox_prologue(str) != 0) { + return -1; + } + + char *endp; + *value = strtoull(str, &endp, base); + return xstrtoux_epilogue(str, end, endp); +} + /** Compile and execute a regular expression for xrpmatch(). */ static int xrpregex(nl_item item, const char *response) { const char *pattern = nl_langinfo(item); diff --git a/src/bfstd.h b/src/bfstd.h index 51920c9..15dd949 100644 --- a/src/bfstd.h +++ b/src/bfstd.h @@ -169,6 +169,16 @@ char *xgetdelim(FILE *file, char delim); const char *xgetprogname(void); /** + * Like xstrtol(), but for short. + */ +int xstrtos(const char *str, char **end, int base, short *value); + +/** + * Like xstrtol(), but for int. + */ +int xstrtoi(const char *str, char **end, int base, int *value); + +/** * Wrapper for strtol() that forbids leading spaces. */ int xstrtol(const char *str, char **end, int base, long *value); @@ -179,6 +189,26 @@ int xstrtol(const char *str, char **end, int base, long *value); int xstrtoll(const char *str, char **end, int base, long long *value); /** + * Like xstrtoul(), but for unsigned short. + */ +int xstrtous(const char *str, char **end, int base, unsigned short *value); + +/** + * Like xstrtoul(), but for unsigned int. + */ +int xstrtoui(const char *str, char **end, int base, unsigned int *value); + +/** + * Wrapper for strtoul() that forbids leading spaces, negatives. + */ +int xstrtoul(const char *str, char **end, int base, unsigned long *value); + +/** + * Wrapper for strtoull() that forbids leading spaces, negatives. + */ +int xstrtoull(const char *str, char **end, int base, unsigned long long *value); + +/** * Wrapper for strtof() that forbids leading spaces. */ int xstrtof(const char *str, char **end, float *value); @@ -253,7 +253,7 @@ struct bftw_file { /** The length of the file's name. */ size_t namelen; /** The file's name. */ - char name[]; + char name[]; // _counted_by(namelen + 1) }; /** @@ -1485,7 +1485,8 @@ fail: /** Check if we should stat() a file asynchronously. */ static bool bftw_should_ioq_stat(struct bftw_state *state, struct bftw_file *file) { - // To avoid surprising users too much, process the roots in order + // POSIX wants the root paths to be processed in order + // See https://www.austingroupbugs.net/view.php?id=1859 if (file->depth == 0) { return false; } diff --git a/src/color.c b/src/color.c index 588dbac..926cf2b 100644 --- a/src/color.c +++ b/src/color.c @@ -32,7 +32,7 @@ struct esc_seq { /** The length of the escape sequence. */ size_t len; /** The escape sequence itself, without a terminating NUL. */ - char seq[]; + char seq[] _counted_by(len); }; /** @@ -48,7 +48,7 @@ struct ext_color { /** Whether the comparison should be case-sensitive. */ bool case_sensitive; /** The extension to match (NUL-terminated). */ - char ext[]; + char ext[]; // _counted_by(len + 1); }; struct colors { @@ -103,6 +103,8 @@ struct colors { struct esc_seq *pipe; struct esc_seq *socket; + struct esc_seq *dataless; + /** A mapping from color names (fi, di, ln, etc.) to struct fields. */ struct trie names; @@ -161,26 +163,32 @@ static int cat_esc(dchar **dstr, const struct esc_seq *seq) { return dstrxcat(dstr, seq->seq, seq->len); } -/** Set a named escape sequence. */ -static int set_esc(struct colors *colors, const char *name, dchar *value) { - struct esc_seq **field = get_esc(colors, name); - if (!field) { - return 0; +/** Set an escape sequence field. */ +static int set_esc_field(struct colors *colors, struct esc_seq **field, const dchar *value) { + struct esc_seq *seq = NULL; + if (value) { + seq = new_esc(colors, value, dstrlen(value)); + if (!seq) { + return -1; + } } if (*field) { free_esc(colors, *field); - *field = NULL; } + *field = seq; - if (value) { - *field = new_esc(colors, value, dstrlen(value)); - if (!*field) { - return -1; - } + return 0; +} + +/** Set a named escape sequence. */ +static int set_esc(struct colors *colors, const char *name, const dchar *value) { + struct esc_seq **field = get_esc(colors, name); + if (!field) { + return 0; } - return 0; + return set_esc_field(colors, field, value); } /** Reverse a string, to turn suffix matches into prefix matches. */ @@ -607,6 +615,109 @@ fail: return ret; } +/** Parse the FreeBSD $LSCOLORS format. */ +static int parse_bsd_ls_colors(struct colors *colors, const char *lscolors) { + static const char *fg_codes[256] = { + // 0-7: deprecated aliases for a-h + ['0'] = "30", ['1'] = "31", ['2'] = "32", ['3'] = "33", + ['4'] = "34", ['5'] = "35", ['6'] = "36", ['7'] = "37", + // a-h: first 8 ANSI foreground colors + ['a'] = "30", ['b'] = "31", ['c'] = "32", ['d'] = "33", + ['e'] = "34", ['f'] = "35", ['g'] = "36", ['h'] = "37", + // x: default foreground + ['x'] = "39", + // A-H: bold foreground colors + ['A'] = "1;30", ['B'] = "1;31", ['C'] = "1;32", ['D'] = "1;33", + ['E'] = "1;34", ['F'] = "1;35", ['G'] = "1;36", ['H'] = "1;37", + // X: bold default foreground + ['X'] = "1;39", + }; + + static const char *bg_codes[256] = { + // 0-7: deprecated aliases for a-h + ['0'] = "40", ['1'] = "41", ['2'] = "42", ['3'] = "43", + ['4'] = "44", ['5'] = "45", ['6'] = "46", ['7'] = "47", + // a-h: first 8 ANSI background colors + ['a'] = "40", ['b'] = "41", ['c'] = "42", ['d'] = "43", + ['e'] = "44", ['f'] = "45", ['g'] = "46", ['h'] = "47", + // x: default background + ['x'] = "49", + // A-H: background colors + underline + ['A'] = "4;40", ['B'] = "4;41", ['C'] = "4;42", ['D'] = "4;43", + ['E'] = "4;44", ['F'] = "4;45", ['G'] = "4;46", ['H'] = "4;47", + // X: default background + underline + ['X'] = "4;49", + }; + + // Please refer to https://man.freebsd.org/cgi/man.cgi?ls(1)#ENVIRONMENT + char complete_colors[] = "exfxcxdxbxegedabagacadah"; + + // For short $LSCOLORS, use the default colors for the rest + size_t max = strlen(complete_colors); + size_t len = strnlen(lscolors, max); + memcpy(complete_colors, lscolors, len); + + struct esc_seq **keys[] = { + &colors->directory, + &colors->link, + &colors->socket, + &colors->pipe, + &colors->executable, + &colors->blockdev, + &colors->chardev, + &colors->setuid, + &colors->setgid, + &colors->sticky_other_writable, + &colors->other_writable, + &colors->dataless, + }; + + dchar *buf = dstralloc(8); + if (!buf) { + return -1; + } + + int ret = -1; + for (size_t i = 0; i < countof(keys); ++i) { + uint8_t fg = complete_colors[2 * i]; + uint8_t bg = complete_colors[2 * i + 1]; + + const char *fg_code = fg_codes[fg]; + const char *bg_code = bg_codes[bg]; + + dstrshrink(buf, 0); + if (fg_code) { + if (dstrcat(&buf, fg_code) != 0) { + goto fail; + } + } + if (fg_code && bg_code) { + if (dstrcat(&buf, ";") != 0) { + goto fail; + } + } + if (bg_code) { + if (dstrcat(&buf, bg_code) != 0) { + goto fail; + } + } + + const dchar *value = dstrlen(buf) > 0 ? buf : NULL; + if (set_esc_field(colors, keys[i], value) != 0) { + goto fail; + } + } + + ret = 0; +fail: + dstrfree(buf); + return ret; +} + +static bool str_isset(const char *str) { + return str && *str; +} + struct colors *parse_colors(void) { struct colors *colors = ALLOC(struct colors); if (!colors) { @@ -672,16 +783,28 @@ struct colors *parse_colors(void) { fail = fail || init_esc(colors, "pi", "33", &colors->pipe); fail = fail || init_esc(colors, "so", "01;35", &colors->socket); + colors->dataless = NULL; + if (fail) { goto fail; } - if (parse_gnu_ls_colors(colors, getenv("LS_COLORS")) != 0) { - goto fail; - } - if (parse_gnu_ls_colors(colors, getenv("BFS_COLORS")) != 0) { - goto fail; + const char *gnu_colors = getenv("LS_COLORS"); + const char *bfs_colors = getenv("BFS_COLORS"); + const char *bsd_colors = getenv("LSCOLORS"); + if (str_isset(gnu_colors) || str_isset(bfs_colors)) { + if (parse_gnu_ls_colors(colors, gnu_colors) != 0) { + goto fail; + } + if (parse_gnu_ls_colors(colors, bfs_colors) != 0) { + goto fail; + } + } else if (str_isset(bsd_colors)) { + if (parse_bsd_ls_colors(colors, bsd_colors) != 0) { + goto fail; + } } + if (build_iext_trie(colors) != 0) { goto fail; } @@ -949,6 +1072,34 @@ static bool cpath_is_broken(const struct cpath *cpath) { } } +/** Check if we need a statbuf to colorize a file. */ +static bool must_stat(const struct colors *colors, enum bfs_type type) { + switch (type) { + case BFS_REG: + if (colors->setuid || colors->setgid || colors->executable || colors->multi_hard) { + return true; + } + +#ifdef ST_DATALESS + if (colors->dataless) { + return true; + } +#endif + + return false; + + case BFS_DIR: + if (colors->sticky_other_writable || colors->other_writable || colors->sticky) { + return true; + } + + return false; + + default: + return false; + } +} + /** Get the color for a file. */ static const struct esc_seq *file_color(const struct colors *colors, const struct cpath *cpath) { enum bfs_type type; @@ -963,17 +1114,17 @@ static const struct esc_seq *file_color(const struct colors *colors, const struc } const struct bfs_stat *statbuf = NULL; + if (must_stat(colors, type)) { + statbuf = cpath_stat(cpath); + if (!statbuf) { + goto error; + } + } + const struct esc_seq *color = NULL; switch (type) { case BFS_REG: - if (colors->setuid || colors->setgid || colors->executable || colors->multi_hard) { - statbuf = cpath_stat(cpath); - if (!statbuf) { - goto error; - } - } - if (colors->setuid && (statbuf->mode & 04000)) { color = colors->setuid; } else if (colors->setgid && (statbuf->mode & 02000)) { @@ -986,6 +1137,12 @@ static const struct esc_seq *file_color(const struct colors *colors, const struc color = colors->multi_hard; } +#ifdef SF_DATALESS + if (!color && colors->dataless && (statbuf->attrs & SF_DATALESS)) { + color = colors->dataless; + } +#endif + if (!color) { const char *name = cpath->path + cpath->nameoff; size_t namelen = cpath->valid - cpath->nameoff; @@ -999,13 +1156,6 @@ static const struct esc_seq *file_color(const struct colors *colors, const struc break; case BFS_DIR: - if (colors->sticky_other_writable || colors->other_writable || colors->sticky) { - statbuf = cpath_stat(cpath); - if (!statbuf) { - goto error; - } - } - if (colors->sticky_other_writable && (statbuf->mode & 01002) == 01002) { color = colors->sticky_other_writable; } else if (colors->other_writable && (statbuf->mode & 00002)) { @@ -18,6 +18,7 @@ * * bfs: func@src/file.c:0: Message */ +// Use (format) ? "..." : "" so the format string is required #define BFS_DIAG_FORMAT_(format) \ ((format) ? "%s: %s@%s:%d: " format "%s" : "") @@ -75,7 +76,7 @@ void bfs_abortf(const char *format, ...); bfs_eabort_(__VA_ARGS__, ) #define bfs_eabort_(format, ...) \ - ((format) ? bfs_abort_(format ": %s", __VA_ARGS__ errstr(), ) : (void)0) + bfs_abort_(format "%s%s", __VA_ARGS__ (sizeof("" format) > 1 ? ": " : ""), errstr(), ) /** * Abort in debug builds; no-op in release builds. @@ -116,7 +117,6 @@ void bfs_abortf(const char *format, ...); #define bfs_everify(...) \ bfs_everify_(#__VA_ARGS__, __VA_ARGS__, "", ) - #define bfs_everify_(str, cond, format, ...) \ ((cond) ? (void)0 : bfs_everify__(format, BFS_DIAG_MSG_(format, str), __VA_ARGS__)) diff --git a/src/dstring.c b/src/dstring.c index 0f08679..678d685 100644 --- a/src/dstring.c +++ b/src/dstring.c @@ -23,7 +23,7 @@ struct dstring { /** Length of the string, *excluding* the terminating NUL. */ size_t len; /** The string itself. */ - alignas(dchar) char str[]; + alignas(dchar) char str[] _counted_by(cap); }; #define DSTR_OFFSET offsetof(struct dstring, str) @@ -203,7 +203,7 @@ struct ioqq { cache_align atomic size_t tail; /** The circular buffer itself. */ - cache_align ioq_slot slots[]; + cache_align ioq_slot slots[]; // _counted_by(slot_mask + 1) }; /** Destroy an I/O command queue. */ @@ -593,7 +593,7 @@ struct ioq { /** The number of background threads. */ size_t nthreads; /** The background threads themselves. */ - struct ioq_thread threads[]; + struct ioq_thread threads[] _counted_by(nthreads); }; /** Cancel a request if we need to. */ @@ -1623,14 +1623,19 @@ static void data_flow_icmp(struct bfs_opt *opt, const struct bfs_expr *expr, enu /** Transfer function for -{execut,read,writ}able. */ static struct bfs_expr *data_flow_access(struct bfs_opt *opt, struct bfs_expr *expr, const struct visitor *visitor) { - if (expr->num & R_OK) { + switch (expr->num) { + case R_OK: data_flow_pred(opt, READABLE_PRED, true); - } - if (expr->num & W_OK) { + break; + case W_OK: data_flow_pred(opt, WRITABLE_PRED, true); - } - if (expr->num & X_OK) { + break; + case X_OK: data_flow_pred(opt, EXECUTABLE_PRED, true); + break; + default: + bfs_bug("Unknown access() mode %lld", expr->num); + break; } return expr; @@ -1655,7 +1660,7 @@ static struct bfs_expr *data_flow_gid(struct bfs_opt *opt, struct bfs_expr *expr gid_t gid = range->min; bool nogroup = !bfs_getgrgid(opt->ctx->groups, gid); if (errno == 0) { - data_flow_pred(opt, NOGROUP_PRED, nogroup); + constrain_pred(&opt->after_true.preds[NOGROUP_PRED], nogroup); } } @@ -1729,7 +1734,7 @@ static struct bfs_expr *data_flow_uid(struct bfs_opt *opt, struct bfs_expr *expr uid_t uid = range->min; bool nouser = !bfs_getpwuid(opt->ctx->users, uid); if (errno == 0) { - data_flow_pred(opt, NOUSER_PRED, nouser); + constrain_pred(&opt->after_true.preds[NOUSER_PRED], nouser); } } diff --git a/src/parse.c b/src/parse.c index 9c39d6b..5ec4c0e 100644 --- a/src/parse.c +++ b/src/parse.c @@ -1247,6 +1247,41 @@ static struct bfs_expr *parse_empty(struct bfs_parser *parser, int arg1, int arg return expr; } +/** Check for unsafe relative paths in $PATH. */ +static const char *unsafe_path(const struct bfs_exec *execbuf) { + if (!(execbuf->flags & BFS_EXEC_CHDIR)) { + // Not -execdir or -okdir + return NULL; + } + + const char *exe = execbuf->tmpl_argv[0]; + if (strchr(exe, '/')) { + // No $PATH lookups for /foo or foo/bar + return NULL; + } + + if (strstr(exe, "{}")) { + // Substituted paths always contain a / + return NULL; + } + + const char *path = getenv("PATH"); + while (path) { + if (path[0] != '/') { + // Relative $PATH component! + return path; + } + + path = strchr(path, ':'); + if (path) { + ++path; + } + } + + // No relative components in $PATH + return NULL; +} + /** * Parse -exec(dir)?/-ok(dir)?. */ @@ -1269,29 +1304,21 @@ static struct bfs_expr *parse_exec(struct bfs_parser *parser, int flags, int arg // For pipe() in bfs_spawn() expr->ephemeral_fds = 2; - if (execbuf->flags & BFS_EXEC_CHDIR) { - // Check for relative paths in $PATH - const char *path = getenv("PATH"); - while (path) { - if (*path != '/') { - size_t len = strcspn(path, ":"); - char *comp = strndup(path, len); - if (comp) { - parse_expr_error(parser, expr, - "This action would be unsafe, since ${bld}$$PATH${rs} contains the relative path ${bld}%pq${rs}\n", comp); - free(comp); - } else { - parse_perror(parser, "strndup()"); - } - return NULL; - } - - path = strchr(path, ':'); - if (path) { - ++path; - } + const char *unsafe = unsafe_path(execbuf); + if (unsafe) { + size_t len = strcspn(unsafe, ":"); + char *comp = strndup(unsafe, len); + if (comp) { + parse_expr_error(parser, expr, + "This action would be unsafe, since ${bld}$$PATH${rs} contains the relative path ${bld}%pq${rs}\n", comp); + free(comp); + } else { + parse_perror(parser, "strndup()"); } + return NULL; + } + if (execbuf->flags & BFS_EXEC_CHDIR) { // To dup() the parent directory if (execbuf->flags & BFS_EXEC_MULTI) { ++expr->persistent_fds; diff --git a/src/prelude.h b/src/prelude.h index de89a6c..3b1c4e5 100644 --- a/src/prelude.h +++ b/src/prelude.h @@ -126,5 +126,8 @@ #if __has_feature(thread_sanitizer) && !defined(__SANITIZE_THREAD__) # define __SANITIZE_THREAD__ true #endif +#if __has_feature(type_sanitizer) && !defined(__SANITIZE_TYPE__) +# define __SANITIZE_TYPE__ true +#endif #endif // BFS_PRELUDE_H @@ -129,7 +129,7 @@ struct trie_node { * tag to distinguish internal nodes from leaves. This is safe as long * as all dynamic allocations are aligned to more than a single byte. */ - uintptr_t children[]; + uintptr_t children[]; // _counted_by(count_ones(bitmap)) }; /** Check if an encoded pointer is to an internal node. */ @@ -21,7 +21,7 @@ struct trie_leaf { /** The length of the key in bytes. */ size_t length; /** The key itself, stored inline. */ - char key[]; + char key[] _counted_by(length); }; /** diff --git a/src/xspawn.c b/src/xspawn.c index 3fa4e60..ee62c05 100644 --- a/src/xspawn.c +++ b/src/xspawn.c @@ -232,12 +232,28 @@ int bfs_spawn_adddup2(struct bfs_spawn *ctx, int oldfd, int newfd) { */ #define BFS_POSIX_SPAWNP_AFTER_FCHDIR !(__APPLE__ || __NetBSD__) +/** + * NetBSD even resolves the executable before file actions with posix_spawn()! + */ +#define BFS_POSIX_SPAWN_AFTER_FCHDIR !__NetBSD__ + int bfs_spawn_addfchdir(struct bfs_spawn *ctx, int fd) { struct bfs_spawn_action *action = bfs_spawn_action(BFS_SPAWN_FCHDIR); if (!action) { return -1; } +#if __APPLE__ + // macOS has a bug that causes EBADF when an fchdir() action refers to a + // file opened by the file actions + for_slist (struct bfs_spawn_action, prev, ctx) { + if (fd == prev->out_fd) { + bfs_spawn_clear_posix(ctx); + break; + } + } +#endif + #if BFS_HAS_POSIX_SPAWN_ADDFCHDIR # define BFS_POSIX_SPAWN_ADDFCHDIR posix_spawn_file_actions_addfchdir #elif BFS_HAS_POSIX_SPAWN_ADDFCHDIR_NP @@ -401,18 +417,40 @@ static bool bfs_resolve_relative(const struct bfs_resolver *res) { return false; } +/** Check if the actions include fchdir(). */ +static bool bfs_spawn_will_chdir(const struct bfs_spawn *ctx) { + if (ctx) { + for_slist (const struct bfs_spawn_action, action, ctx) { + if (action->op == BFS_SPAWN_FCHDIR) { + return true; + } + } + } + + return false; +} + +/** Check if we can call xfaccessat() before file actions. */ +static bool bfs_can_access_early(const struct bfs_resolver *res, const struct bfs_spawn *ctx) { + if (res->exe[0] == '/') { + return true; + } + + if (bfs_spawn_will_chdir(ctx)) { + return false; + } + + return true; +} + /** Check if we can resolve the executable before file actions. */ static bool bfs_can_resolve_early(const struct bfs_resolver *res, const struct bfs_spawn *ctx) { if (!bfs_resolve_relative(res)) { return true; } - if (ctx) { - for_slist (const struct bfs_spawn_action, action, ctx) { - if (action->op == BFS_SPAWN_FCHDIR) { - return false; - } - } + if (bfs_spawn_will_chdir(ctx)) { + return false; } return true; @@ -442,17 +480,19 @@ static int bfs_resolve_early(struct bfs_resolver *res, const char *exe, const st }; if (bfs_can_skip_resolve(res, ctx)) { - // Do this check eagerly, even though posix_spawn()/execv() also - // would, because: - // - // - faccessat() is faster than fork()/clone() + execv() - // - posix_spawn() is not guaranteed to report ENOENT - if (xfaccessat(AT_FDCWD, exe, X_OK) == 0) { - res->done = true; - return 0; - } else { - return -1; + if (bfs_can_access_early(res, ctx)) { + // Do this check eagerly, even though posix_spawn()/execv() also + // would, because: + // + // - faccessat() is faster than fork()/clone() + execv() + // - posix_spawn() is not guaranteed to report ENOENT + if (xfaccessat(AT_FDCWD, exe, X_OK) != 0) { + return -1; + } } + + res->done = true; + return 0; } res->path = getenv("PATH"); @@ -529,6 +569,12 @@ static bool bfs_use_posix_spawn(const struct bfs_resolver *res, const struct bfs } #endif +#if !BFS_POSIX_SPAWN_AFTER_FCHDIR + if (res->exe[0] != '/' && bfs_spawn_will_chdir(ctx)) { + return false; + } +#endif + return true; } |