From baa49bdc1b7b992bfb8298589cd33c04353c6b57 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Mon, 7 Oct 2024 15:18:45 +0200 Subject: hush: make "test -x" use cached groupinfo While at it, correct "type" to skip non-executable files in PATH function old new delta builtin_source 211 316 +105 builtin_test 10 32 +22 hush_main 1150 1170 +20 builtin_type 122 137 +15 if_command_vV_print_and_exit 120 114 -6 find_in_path 131 - -131 ------------------------------------------------------------------------------ (add/remove: 0/1 grow/shrink: 4/1 up/down: 162/-137) Total: 25 bytes Signed-off-by: Denys Vlasenko --- shell/hush.c | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/shell/hush.c b/shell/hush.c index 6b6ec7c6b..074f35f2b 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -1021,6 +1021,13 @@ struct globals { #endif #if HUSH_DEBUG >= 2 int debug_indent; +#endif +#if ENABLE_HUSH_TEST || BASH_TEST2 + /* Cached supplementary group array (for testing executable'ity of files) */ + struct cached_groupinfo groupinfo; +# define GROUPINFO_INIT { G.groupinfo.euid = -1; G.groupinfo.egid = -1; } +#else +# define GROUPINFO_INIT /* nothing */ #endif struct sigaction sa; char optstring_buf[sizeof("eixcs")]; @@ -1040,6 +1047,7 @@ struct globals { /* memset(&G.sa, 0, sizeof(G.sa)); */ \ sigfillset(&G.sa.sa_mask); \ G.sa.sa_flags = SA_RESTART; \ + GROUPINFO_INIT; \ } while (0) /* Function prototypes for builtins */ @@ -8193,6 +8201,8 @@ static int setup_redirects(struct command *prog, struct squirrel **sqp) return 0; } +/* Find a file in PATH, not necessarily executable */ +//TODO: shares code with find_executable() in libbb, factor out? static char *find_in_path(const char *arg) { char *ret = NULL; @@ -8635,7 +8645,7 @@ static void if_command_vV_print_and_exit(char opt_vV, char *cmd, const char *exp to_free = NULL; if (!explanation) { - char *path = getenv("PATH"); + char *path = (char*)get_local_var_value("PATH"); explanation = to_free = find_executable(cmd, &path); /* path == NULL is ok */ if (!explanation) _exit(1); /* PROG was not found */ @@ -10870,7 +10880,8 @@ static NOINLINE int run_applet_main(char **argv, int (*applet_main_func)(int arg #if ENABLE_HUSH_TEST || BASH_TEST2 static int FAST_FUNC builtin_test(char **argv) { - return run_applet_main(argv, test_main); + int argc = string_array_len(argv); + return test_main2(&G.groupinfo, argc, argv); } #endif #if ENABLE_HUSH_ECHO @@ -11063,12 +11074,16 @@ static int FAST_FUNC builtin_type(char **argv) # endif else if (find_builtin(*argv)) type = "a shell builtin"; - else if ((path = find_in_path(*argv)) != NULL) - type = path; else { - bb_error_msg("type: %s: not found", *argv); - ret = EXIT_FAILURE; - continue; + char *pathvar = (char*)get_local_var_value("PATH"); + path = find_executable(*argv, &pathvar); + if (path) + type = path; + else { + bb_error_msg("type: %s: not found", *argv); + ret = EXIT_FAILURE; + continue; + } } printf("%s is %s\n", *argv, type); -- cgit v1.2.3-55-g6feb From 8c4bccb83e5e2594f16310b7cbe07bf05fc9f13a Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Mon, 7 Oct 2024 22:00:16 +0200 Subject: hush: fix "type ./cat" and "command -v ./cat" to not scan PATH function old new delta find_executable_in_PATH - 67 +67 if_command_vV_print_and_exit 114 116 +2 .rodata 105712 105710 -2 builtin_type 137 128 -9 ------------------------------------------------------------------------------ (add/remove: 1/0 grow/shrink: 1/2 up/down: 69/-11) Total: 58 bytes Signed-off-by: Denys Vlasenko --- shell/hush.c | 46 +++++++++++++++++++++++++++++++++------------- 1 file changed, 33 insertions(+), 13 deletions(-) diff --git a/shell/hush.c b/shell/hush.c index 074f35f2b..4e477d05a 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -8201,27 +8201,29 @@ static int setup_redirects(struct command *prog, struct squirrel **sqp) return 0; } -/* Find a file in PATH, not necessarily executable */ +/* Find a file in PATH, not necessarily executable + * Name is known to not contain '/'. + */ //TODO: shares code with find_executable() in libbb, factor out? -static char *find_in_path(const char *arg) +static char *find_in_PATH(const char *name) { - char *ret = NULL; + char *ret; const char *PATH = get_local_var_value("PATH"); if (!PATH) return NULL; - + ret = NULL; while (1) { const char *end = strchrnul(PATH, ':'); int sz = end - PATH; /* must be int! */ free(ret); if (sz != 0) { - ret = xasprintf("%.*s/%s", sz, PATH, arg); + ret = xasprintf("%.*s/%s", sz, PATH, name); } else { /* We have xxx::yyyy in $PATH, * it means "use current dir" */ - ret = xstrdup(arg); + ret = xstrdup(name); } if (access(ret, F_OK) == 0) break; @@ -8236,6 +8238,24 @@ static char *find_in_path(const char *arg) return ret; } +#if ENABLE_HUSH_TYPE || ENABLE_HUSH_COMMAND +static char *find_executable_in_PATH(const char *name) +{ + char *PATH; + if (strchr(name, '/')) { + /* Name with '/' is tested verbatim, with no PATH traversal: + * "cd /bin; type ./cat" should print "./cat is ./cat", + * NOT "./cat is /bin/./cat" + */ + if (file_is_executable(name)) + return xstrdup(name); + return NULL; + } + PATH = (char*)get_local_var_value("PATH"); + return find_executable(name, &PATH); /* path == NULL is ok */ +} +#endif + static const struct built_in_command *find_builtin_helper(const char *name, const struct built_in_command *x, const struct built_in_command *end) @@ -8645,10 +8665,11 @@ static void if_command_vV_print_and_exit(char opt_vV, char *cmd, const char *exp to_free = NULL; if (!explanation) { - char *path = (char*)get_local_var_value("PATH"); - explanation = to_free = find_executable(cmd, &path); /* path == NULL is ok */ - if (!explanation) + explanation = to_free = find_executable_in_PATH(cmd); + if (!explanation) { + bb_error_msg("%s: %s: not found", "command", cmd); _exit(1); /* PROG was not found */ + } if (opt_vV != 'V') cmd = to_free; /* -v PROG prints "/path/to/PROG" */ } @@ -11075,12 +11096,11 @@ static int FAST_FUNC builtin_type(char **argv) else if (find_builtin(*argv)) type = "a shell builtin"; else { - char *pathvar = (char*)get_local_var_value("PATH"); - path = find_executable(*argv, &pathvar); + path = find_executable_in_PATH(*argv); if (path) type = path; else { - bb_error_msg("type: %s: not found", *argv); + bb_error_msg("%s: %s: not found", "type", *argv); ret = EXIT_FAILURE; continue; } @@ -11710,7 +11730,7 @@ static int FAST_FUNC builtin_source(char **argv) } arg_path = NULL; if (!strchr(filename, '/')) { - arg_path = find_in_path(filename); + arg_path = find_in_PATH(filename); if (arg_path) filename = arg_path; else if (!ENABLE_HUSH_BASH_SOURCE_CURDIR) { -- cgit v1.2.3-55-g6feb From 49d9e06fbab0b02a71deed57610edb0c8f4fb20c Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Tue, 8 Oct 2024 04:03:17 +0200 Subject: libbb: modify find_executable() to not temporarily write to PATH This allows to simplify "which" applet code function old new delta find_executable 93 111 +18 which_main 191 177 -14 builtin_source 316 294 -22 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 1/2 up/down: 18/-36) Total: -18 bytes Signed-off-by: Denys Vlasenko --- debianutils/which.c | 9 +++------ include/libbb.h | 2 +- libbb/executable.c | 56 ++++++++++++++++++++++++++++------------------------- shell/hush.c | 35 ++++++++++++++------------------- 4 files changed, 48 insertions(+), 54 deletions(-) diff --git a/debianutils/which.c b/debianutils/which.c index 1f547919f..a7d55a215 100644 --- a/debianutils/which.c +++ b/debianutils/which.c @@ -31,15 +31,12 @@ int which_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int which_main(int argc UNUSED_PARAM, char **argv) { - char *env_path; + const char *env_path; int status = 0; - /* This sizeof(): bb_default_root_path is shorter than BB_PATH_ROOT_PATH */ - char buf[sizeof(BB_PATH_ROOT_PATH)]; env_path = getenv("PATH"); if (!env_path) - /* env_path must be writable, and must not alloc, so... */ - env_path = strcpy(buf, bb_default_root_path); + env_path = bb_default_root_path; getopt32(argv, "^" "a" "\0" "-1"/*at least one arg*/); argv += optind; @@ -54,7 +51,7 @@ int which_main(int argc UNUSED_PARAM, char **argv) puts(*argv); } } else { - char *path; + const char *path; char *p; path = env_path; diff --git a/include/libbb.h b/include/libbb.h index f5f8e1635..4d6193795 100644 --- a/include/libbb.h +++ b/include/libbb.h @@ -1227,7 +1227,7 @@ void FAST_FUNC update_utmp_DEAD_PROCESS(pid_t pid); int file_is_executable(const char *name) FAST_FUNC; -char *find_executable(const char *filename, char **PATHp) FAST_FUNC; +char *find_executable(const char *filename, const char **PATHp) FAST_FUNC; int executable_exists(const char *filename) FAST_FUNC; /* BB_EXECxx always execs (it's not doing NOFORK/NOEXEC stuff), diff --git a/libbb/executable.c b/libbb/executable.c index a033b74d9..09bed1eaf 100644 --- a/libbb/executable.c +++ b/libbb/executable.c @@ -21,14 +21,11 @@ int FAST_FUNC file_is_executable(const char *name) /* search (*PATHp) for an executable file; * return allocated string containing full path if found; * PATHp points to the component after the one where it was found - * (or NULL), + * (or NULL if found in last component), * you may call find_executable again with this PATHp to continue - * (if it's not NULL). - * return NULL otherwise; (PATHp is undefined) - * in all cases (*PATHp) contents are temporarily modified - * but are restored on return (s/:/NUL/ and back). + * return NULL otherwise (PATHp is undefined) */ -char* FAST_FUNC find_executable(const char *filename, char **PATHp) +char* FAST_FUNC find_executable(const char *name, const char **PATHp) { /* About empty components in $PATH: * http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap08.html @@ -38,38 +35,45 @@ char* FAST_FUNC find_executable(const char *filename, char **PATHp) * initial colon preceding the rest of the list, or as a trailing colon * following the rest of the list. */ - char *p, *n; + char *p = (char*) *PATHp; - p = *PATHp; - while (p) { - int ex; + if (!p) + return NULL; + while (1) { + const char *end = strchrnul(p, ':'); + int sz = end - p; - n = strchr(p, ':'); - if (n) *n = '\0'; - p = concat_path_file( - p[0] ? p : ".", /* handle "::" case */ - filename - ); - ex = file_is_executable(p); - if (n) *n++ = ':'; - if (ex) { - *PATHp = n; + if (sz != 0) { + p = xasprintf("%.*s/%s", sz, p, name); + } else { + /* We have xxx::yyy in $PATH, + * it means "use current dir" */ + p = xstrdup(name); +// A bit of discrepancy wrt the path used if file is found here. +// bash 5.2.15 "type" returns "./NAME". +// GNU which v2.21 returns "/CUR/DIR/NAME". +// With -a, both skip over all colons: xxx::::yyy is the same as xxx::yyy, +// current dir is not tried the second time. + } + if (file_is_executable(p)) { + *PATHp = (*end ? end+1 : NULL); return p; } free(p); - p = n; - } /* on loop exit p == NULL */ - return p; + if (*end == '\0') + return NULL; + p = (char *) end + 1; + } } /* search $PATH for an executable file; * return 1 if found; * return 0 otherwise; */ -int FAST_FUNC executable_exists(const char *filename) +int FAST_FUNC executable_exists(const char *name) { - char *path = getenv("PATH"); - char *ret = find_executable(filename, &path); + const char *path = getenv("PATH"); + char *ret = find_executable(name, &path); free(ret); return ret != NULL; } diff --git a/shell/hush.c b/shell/hush.c index 4e477d05a..04dda0734 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -8207,41 +8207,34 @@ static int setup_redirects(struct command *prog, struct squirrel **sqp) //TODO: shares code with find_executable() in libbb, factor out? static char *find_in_PATH(const char *name) { - char *ret; - const char *PATH = get_local_var_value("PATH"); + char *p = (char*) get_local_var_value("PATH"); - if (!PATH) + if (!p) return NULL; - ret = NULL; while (1) { - const char *end = strchrnul(PATH, ':'); - int sz = end - PATH; /* must be int! */ + const char *end = strchrnul(p, ':'); + int sz = end - p; /* must be int! */ - free(ret); if (sz != 0) { - ret = xasprintf("%.*s/%s", sz, PATH, name); + p = xasprintf("%.*s/%s", sz, p, name); } else { - /* We have xxx::yyyy in $PATH, + /* We have xxx::yyy in $PATH, * it means "use current dir" */ - ret = xstrdup(name); + p = xstrdup(name); } - if (access(ret, F_OK) == 0) - break; - - if (*end == '\0') { - free(ret); + if (access(p, F_OK) == 0) + return p; + free(p); + if (*end == '\0') return NULL; - } - PATH = end + 1; + p = (char *) end + 1; } - - return ret; } #if ENABLE_HUSH_TYPE || ENABLE_HUSH_COMMAND static char *find_executable_in_PATH(const char *name) { - char *PATH; + const char *PATH; if (strchr(name, '/')) { /* Name with '/' is tested verbatim, with no PATH traversal: * "cd /bin; type ./cat" should print "./cat is ./cat", @@ -8251,7 +8244,7 @@ static char *find_executable_in_PATH(const char *name) return xstrdup(name); return NULL; } - PATH = (char*)get_local_var_value("PATH"); + PATH = get_local_var_value("PATH"); return find_executable(name, &PATH); /* path == NULL is ok */ } #endif -- cgit v1.2.3-55-g6feb From 75ca8d074bacb6896d770993b93161c40aa31b9f Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Tue, 8 Oct 2024 04:23:29 +0200 Subject: hexdump: accept hex numbers in -n, closes 16195 function old new delta hexdump_main 366 383 +17 Signed-off-by: Denys Vlasenko --- util-linux/hexdump.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/util-linux/hexdump.c b/util-linux/hexdump.c index c46ffeca7..5a938e783 100644 --- a/util-linux/hexdump.c +++ b/util-linux/hexdump.c @@ -127,15 +127,20 @@ int hexdump_main(int argc, char **argv) if (ch == 'f') { bb_dump_addfile(dumper, optarg); } /* else */ - if (ch == 'n') { - dumper->dump_length = xatoi_positive(optarg); + if (ch == 'n') { /* compat: -n accepts hex numbers too */ + dumper->dump_length = xstrtou_range_sfx( + optarg, + /*base:*/ 0, + /*lo:*/ 0, /*hi:*/ INT_MAX, + kmg_i_suffixes + ); } /* else */ if (ch == 's') { /* compat: -s accepts hex numbers too */ dumper->dump_skip = xstrtoull_range_sfx( optarg, /*base:*/ 0, /*lo:*/ 0, /*hi:*/ OFF_T_MAX, - bkm_suffixes + kmg_i_suffixes ); } /* else */ if (ch == 'v') { -- cgit v1.2.3-55-g6feb