diff options
author | Elliott Hughes <enh@google.com> | 2020-02-01 14:50:22 -0800 |
---|---|---|
committer | Elliott Hughes <enh@google.com> | 2020-02-01 14:51:10 -0800 |
commit | 6e03079ea1203ad861e8f28e6587c0dba49be41e (patch) | |
tree | d01535751eff03c7159b5d08dfda2657eae6e436 /toys | |
parent | bcdcee27084b9659950148235cdabf4f90bd4893 (diff) | |
parent | ae626295bad3e20a9c457f5f616cd92299f5258c (diff) | |
download | toybox-6e03079ea1203ad861e8f28e6587c0dba49be41e.tar.gz |
Merge remote-tracking branch 'toybox/master' into HEAD
Change-Id: I630a9c162cab4b53e0c9fc0f8b32e5cbc2fad3ea
Diffstat (limited to 'toys')
-rw-r--r-- | toys/other/lsattr.c | 34 | ||||
-rw-r--r-- | toys/pending/sh.c | 301 | ||||
-rw-r--r-- | toys/pending/wget.c | 6 | ||||
-rw-r--r-- | toys/posix/rm.c | 5 |
4 files changed, 206 insertions, 140 deletions
diff --git a/toys/other/lsattr.c b/toys/other/lsattr.c index 76db649f..24591d53 100644 --- a/toys/other/lsattr.c +++ b/toys/other/lsattr.c @@ -14,9 +14,9 @@ config LSATTR bool "lsattr" default y help - usage: lsattr [-Radlpv] [file...] + usage: lsattr [-Radlpv] [FILE...] - List file attributes on a Linux second extended file system. + List file attributes on a Linux file system. Flag letters are defined in chattr help. -R Recursively list attributes of directories and their contents @@ -30,9 +30,9 @@ config CHATTR bool "chattr" default y help - usage: chattr [-R] [-+=AacDdijsStTu] [-p projid] [-v version] [file...] + usage: chattr [-R] [-+=AacDdijsStTu] [-p PROJID] [-v VERSION] [FILE...] - Change file attributes on a Linux second extended file system. + Change file attributes on a Linux file system. -R Recurse -p Set the file's project number @@ -44,21 +44,19 @@ config CHATTR '=' Set attributes Attributes: - A Don't track atime - a Append mode only - C No copy on write - c Enable compress - D Write dir contents synchronously - d Don't backup with dump - F Case-insensitive directory lookups (casefold) - i Cannot be modified (immutable) - j Write all data to journal first + A No atime a Append only + C No COW c Compression + D Synchronous dir updates d No dump + E Encrypted e Extents + F Case-insensitive (casefold) + I Indexed directory i Immutable + j Journal data + N Inline data in inode P Project hierarchy - S Write file contents synchronously - s Zero disk storage when deleted - T Top of directory hierarchy - t Disable tail-merging of partial blocks with other files - u Allow file to be undeleted + S Synchronous file updates s Secure delete + T Top of dir hierarchy t No tail-merging + u Allow undelete + V Verity */ #define FOR_lsattr #include "toys.h" diff --git a/toys/pending/sh.c b/toys/pending/sh.c index 98f5a17e..7e6f827d 100644 --- a/toys/pending/sh.c +++ b/toys/pending/sh.c @@ -19,19 +19,23 @@ * * Things like the bash man page are good to read too. * + * deviations from posix: don't care about $LANG or $LC_ALL + * TODO: test that $PS1 color changes work without stupid \[ \] hack - * TODO: make fake pty wrapper for test infrastructure - * TODO: // Handle embedded NUL bytes in the command line. - * existing but considered builtins: false kill pwd true time + * TODO: Handle embedded NUL bytes in the command line? (When/how?) + * TODO: replace getenv() with faster func: sort env and binary search + * buitins: alias bg command fc fg getopts jobs newgrp read umask unalias wait - * "special" builtins: break continue : . eval exec export readonly return set - * shift times trap unset + * disown umask suspend source pushd popd dirs logout times trap + * unset local export readonly set : . let history declare + * "special" builtins: break continue eval exec return shift + * builtins with extra shell behavior: kill pwd time test + * | & ; < > ( ) $ ` \ " ' <space> <tab> <newline> * * ? [ # ~ = % * ! { } case do done elif else esac fi for if in then until while * [[ ]] function select - * $@ $* $# $? $- $$ $! $0 - * ENV HOME IFS LANG LC_ALL LINENO PATH PPID PS1 PS2 PS4 PWD + * label: * TODO: test exit from "trap EXIT" doesn't recurse * TODO: ! history expansion @@ -44,7 +48,7 @@ * then until while { } time [[ ]] USE_SH(NEWTOY(cd, ">1LP[-LP]", TOYFLAG_NOFORK)) -USE_SH(NEWTOY(exit, NULL, TOYFLAG_NOFORK)) +USE_SH(NEWTOY(exit, 0, TOYFLAG_NOFORK)) USE_SH(NEWTOY(sh, "(noediting)(noprofile)(norc)sc:i", TOYFLAG_BIN)) USE_SH(OLDTOY(toysh, sh, TOYFLAG_BIN)) @@ -94,12 +98,12 @@ config EXIT #include "toys.h" GLOBALS( - char *command; + char *c; long lineno; char **locals, *subshell_env; struct double_list functions; - unsigned options, jobcnt; + unsigned options, jobcnt, loc_ro, loc_magic; int hfd; // next high filehandle (>= 10) // Running jobs. @@ -145,6 +149,7 @@ static void syntax_err(char *msg, ...) if (*toys.optargs) xexit(); } +// append to array with null terminator and realloc as necessary void array_add(char ***list, unsigned count, char *data) { if (!(count&31)) *list = xrealloc(*list, sizeof(char *)*(count+33)); @@ -152,8 +157,6 @@ void array_add(char ***list, unsigned count, char *data) (*list)[count+1] = 0; } -// TODO local variables - // Return index of variable within this list static unsigned findvar(char **list, char *name, int len) { @@ -176,10 +179,7 @@ static void setvar(char *s, unsigned type) unsigned uu; int len = stridx(s, '='); - if (len == -1) { - error_msg("no = in setvar %s\n", s); - return; - } + if (len == -1) return error_msg("no = in setvar %s\n", s); if (type&TAKE_MEM) type ^= TAKE_MEM; else s = xstrdup(s); @@ -190,13 +190,11 @@ static void setvar(char *s, unsigned type) if (environ && environ[uu = findvar(environ, s, len)]) { if (uu>=toys.envc) free(environ[uu]); environ[uu] = s; - } else { - uu = 0; - if (TT.locals && TT.locals[uu = findvar(TT.locals, s, len)]) { - free(TT.locals[uu]); - TT.locals[uu] = s; - } else array_add(&TT.locals, uu, s); - } + } else if (TT.locals[uu = findvar(TT.locals, s, len)]) { + if (uu<TT.loc_ro) return error_msg("%.*s: readonly variable", len, s); + free(TT.locals[uu]); + TT.locals[uu] = s; + } else array_add(&TT.locals, uu, s); } // get variable of length len starting at s. @@ -217,10 +215,8 @@ static char *getvar(char *s) return getvarlen(s, strlen(s)); } - - -// returns pointer to next unquoted (or double quoted if dquot) char. -// handle \ '' "" `` $() +// returns offset of next unquoted (or double quoted if dquot) char. +// handles \ '' "" `` $() int skip_quote(char *s, int dquot, int *depth) { int i, q = dquot ? *depth : 0; @@ -230,12 +226,13 @@ int skip_quote(char *s, int dquot, int *depth) char c = s[i], qq = q ? toybuf[q-1] : 0; if (c == '\\') i++; + else if (dquot && q==1 && qq=='"' && c!='"') break; else if (qq!='\'' && c=='$' && s[1]=='(') { toybuf[q++] = ')'; i++; } else if (q && qq==c) q--; else if ((!q || qq==')') && (c=='"' || c=='\'' || c=='`')) toybuf[q++] = c; - else if (!q || (dquot && q==1 && qq=='"')) break; + else if (!q) break; } if (dquot) *depth = q; @@ -255,15 +252,14 @@ void add_arg(struct arg_list **list, char *arg) *list = al; } -// quote removal, brace, tilde, parameter/variable, $(command), -// $((arithmetic)), split, path -#define NO_PATH (1<<0) -#define NO_SPLIT (1<<1) -#define NO_BRACE (1<<2) -#define NO_TILDE (1<<3) -#define NO_QUOTE (1<<4) -#define FORCE_COPY (1<<31) -#define FORCE_KEEP (1<<30) +#define NO_PATH (1<<0) // path expansion (wildcards) +#define NO_SPLIT (1<<1) // word splitting +#define NO_BRACE (1<<2) // {brace,expansion} +#define NO_TILDE (1<<3) // ~username/path +#define NO_QUOTE (1<<4) // quote removal +#define FORCE_COPY (1<<31) // don't keep original, copy even if not modified +#define FORCE_KEEP (1<<30) // this is a copy, free if not appended to delete +// TODO: parameter/variable $(command) $((math)) split pathglob // TODO: ${name:?error} causes an error/abort here (syntax_err longjmp?) // TODO: $1 $@ $* need args marshalled down here: function+structure? // arg = append to this @@ -274,7 +270,7 @@ void add_arg(struct arg_list **list, char *arg) static void expand_arg_nobrace(struct sh_arg *arg, char *old, unsigned flags, struct arg_list **delete) { - char *new = old; + char *new = old, *s, *ss, *sss; if (flags&FORCE_KEEP) old = 0; @@ -283,7 +279,6 @@ static void expand_arg_nobrace(struct sh_arg *arg, char *old, unsigned flags, // Tilde expansion if (!(flags&NO_TILDE) && *new == '~') { struct passwd *pw = 0; - char *s, *ss, *sss; // first expansion so don't need to free previous new ss = 0; @@ -302,22 +297,28 @@ static void expand_arg_nobrace(struct sh_arg *arg, char *old, unsigned flags, new = s; } -// ${ $(( $( $[ $' ` " ' + // parameter/variable expansion + +// TODO this is wrong + if (*new == '$') { + char *s = getvar(new+1); + + if (new != old) free(new); + if (!s) return; + new = xstrdup(s); + } /* + for (s = new; *(s += skip_quote(s, 1, &depth));) { + if (*s == '`') { + +// ${ $(( $( $[ $' ` " ' + while (*s) { - if (!quote && !(flags&NO_BRACE) && *s == '{') { -TODO this recurses - } else if (quote != '*s == '$') { + if (quote != '*s == '$') { // *@#?-$!_0 "Special Paremeters" ($0 not affected by shift) // 0-9 positional parameters if (s[1] == '$' - -// EUID GROUPS HOSTNAME HOSTTYPE=$(uname -m) MACHTYPE=$HOSTTYPE-unknown-linux -// OLDPWD OSTYPE=linux/android PIPESTATUS PPID PWD RANDOM REPLY SECONDS UID -// COLUMNS LINES HOME SHELL -// IFS PATH -// PS0 PS1='$ ' PS2='> ' PS3 } } @@ -333,8 +334,9 @@ TODO this recurses } */ +// TODO not else? // quote removal - if (!(flags&NO_QUOTE)) { + else if (!(flags&NO_QUOTE)) { int to = 0, from = 0; for (;;) { @@ -481,7 +483,6 @@ static void expand_arg(struct sh_arg *arg, char *old, unsigned flags, } } - // Expand exactly one arg, returning NULL if it split. static char *expand_one_arg(char *new, unsigned flags, struct arg_list **del) { @@ -613,10 +614,6 @@ struct sh_function { char *end; }; -// TODO: try to avoid prototype. -static int parse_line(char *line, struct sh_function *sp); -void free_function(struct sh_function *sp); - // TODO: waitpid(WNOHANG) to clean up zombies and catch background& ending static void subshell_callback(void) @@ -626,23 +623,46 @@ static void subshell_callback(void) TT.subshell_env[strlen(TT.subshell_env)-1] = 0; } +// TODO avoid prototype +static int sh_run(char *new); + // Pass environment and command string to child shell static int run_subshell(char *str, int len) { - int pipes[2], pid, i; + pid_t pid; - if (pipe(pipes) || 254 != dup2(pipes[0], 254)) return 1; - close(pipes[0]); + // The with-mmu path is significantly faster. + if (CFG_TOYBOX_FORK) { + char *s; - fcntl(pipes[1], F_SETFD, FD_CLOEXEC); + if ((pid = fork())<0) perror_msg("fork"); + else if (pid>0) { + s = xstrndup(str, len); + sh_run(s); + free(s); - pid = xpopen_setup(0, 0, subshell_callback); - close(254); + _exit(toys.exitval); + } - if (TT.locals) - for (i = 0; TT.locals[i]; i++) dprintf(pipes[1], "%s\n", TT.locals[i]); - dprintf(pipes[1], "%.*s\n", len, str); - close(pipes[1]); + // On nommu vfork, exec /proc/self/exe, and pipe state data to ourselves. + } else { + int pipes[2], i; + + // open pipe to child + if (pipe(pipes) || 254 != dup2(pipes[0], 254)) return 1; + close(pipes[0]); + fcntl(pipes[1], F_SETFD, FD_CLOEXEC); + + // vfork child + pid = xpopen_setup(0, 0, subshell_callback); + + // marshall data to child + close(254); + if (TT.locals) + for (i = 0; TT.locals[i]; i++) dprintf(pipes[1], "%s\n", TT.locals[i]); + dprintf(pipes[1], "%.*s\n", len, str); + close(pipes[1]); + } return pid; } @@ -873,12 +893,11 @@ if (BUGBUG) { int i; dprintf(255, "envlen=%d arg->c=%d run=", envlen, arg->c); f // Do nothing if nothing to do } else if (pp->exit || !pp->arg.v); - else if (!strcmp(*pp->arg.v, "((")) { - printf("Math!\n"); -// TODO: handle ((math)) +// else if (!strcmp(*pp->arg.v, "((")) +// TODO: handle ((math)) currently totally broken // TODO: call functions() // Is this command a builtin that should run in this process? - } else if ((tl = toy_find(*pp->arg.v)) + else if ((tl = toy_find(*pp->arg.v)) && (tl->flags & (TOYFLAG_NOFORK|TOYFLAG_MAYFORK))) { struct toy_context temp; @@ -900,25 +919,30 @@ if (BUGBUG) { int i; dprintf(255, "envlen=%d arg->c=%d run=", envlen, arg->c); f memcpy(&toys, &temp, sizeof(struct toy_context)); } else { char **env = 0, **old = environ, *ss, *sss; - int kk, ll; - - // Assign leading environment variables - if (envlen) { - kk = 0; - if (environ) while (environ[kk]) kk++; - if (kk) env = xmemdup(environ, sizeof(char *)*(kk+33)); - for (j = 0; j<envlen; j++) { - sss = expand_one_arg(arg->v[j], NO_PATH|NO_SPLIT, &pp->delete); - for (ll = 0; ll<kk; ll++) { - for (s = sss, ss = env[ll]; *s == *ss && *s != '='; s++, ss++); - if (*s != '=') continue; - env[ll] = sss; - break; - } - if (ll == kk) array_add(&env, kk, sss); - } + int kk = 0, ll; + + // We don't allocate/free any array members, just the array + if (environ) while (environ[kk]) kk++; + if (kk) { + env = xmalloc(sizeof(char *)*(kk+33)); + memcpy(env, environ, sizeof(char *)*(kk+1)); environ = env; } + // assign leading environment variables + for (j = 0; j<envlen; j++) { + sss = expand_one_arg(arg->v[j], NO_PATH|NO_SPLIT, &pp->delete); + for (ll = 0; ll<kk; ll++) { + for (s = sss, ss = env[ll]; *s == *ss && *s != '='; s++, ss++); + if (*s != '=') continue; + env[ll] = sss; + break; + } + if (ll == kk) array_add(&environ, kk++, sss); + } + ss = getvar("SHLVL"); + sprintf(toybuf, "%d", atoi(ss ? ss : "")+1); + xsetenv("SHLVL", toybuf); + if (-1 == (pp->pid = xpopen_both(pp->arg.v, 0))) perror_msg("%s: vfork", *pp->arg.v); @@ -1656,14 +1680,7 @@ dprintf(2, "TODO skipped init for((;;)), need math parser\n"); pl = pl->next; } -/* TODO -case/esac -{/} -[[/]] -(/) -((/)) -function/} -*/ +// TODO case/esac {/} [[/]] (/) ((/)) function/} // gearshift from block start to block body (end of flow control test) } else if (pl->type == 2) { @@ -1730,6 +1747,8 @@ static int sh_run(char *new) struct sh_function scratch; int rc; +// TODO switch the fmemopen for -c to use this? Error checking? $(blah) + memset(&scratch, 0, sizeof(struct sh_function)); if (!parse_line(new, &scratch)) run_function(&scratch); free_function(&scratch); @@ -1799,13 +1818,52 @@ static void do_prompt(char *prompt) writeall(2, toybuf, len); } -// sanitize environment and handle nommu subshell handoff -void subshell_imports(void) +// only set local variable when global not present +static void setonlylocal(char ***to, char *name, char *val) { - int to, from, pid = 0, ppid = 0, len; + if (getenv(name)) return; + *(*to)++ = xmprintf("%s=%s", name, val ? val : ""); +} + +// init locals, sanitize environment, handle nommu subshell handoff +void subshell_setup(void) +{ + struct passwd *pw = getpwuid(getuid()); + int to, from, pid = 0, ppid = 0, mypid, myppid, len; + char *s, *ss, **ll, *locals[] = {"GROUPS=", "SECONDS=", "RANDOM=", "LINENO=", + xmprintf("PPID=%d", myppid = getppid()), xmprintf("EUID=%d", geteuid()), + xmprintf("$=%d", mypid = getpid()), xmprintf("UID=%d", getuid())}; struct stat st; + struct utsname uu; FILE *fp; - char *s; + + // Initialize read only local variables + TT.locals = xmalloc(32*sizeof(char *)); + memcpy(TT.locals, locals, sizeof(locals)); + ll = TT.locals+(TT.loc_ro = ARRAY_LEN(locals)); + TT.loc_magic = 4; + + // Add local variables that can be overwritten + setonlylocal(&ll, "PATH", _PATH_DEFPATH); + if (!pw) pw = (void *)toybuf; // first use, so still zeroed + setonlylocal(&ll, "HOME", *pw->pw_dir ? pw->pw_dir : "/"); + setonlylocal(&ll, "SHELL", pw->pw_shell); + setonlylocal(&ll, "USER", pw->pw_name); + setonlylocal(&ll, "LOGNAME", pw->pw_name); + gethostname(toybuf, sizeof(toybuf)-1); + *ll++ = xmprintf("HOSTNAME=%s", toybuf); + uname(&uu); + setonlylocal(&ll, "HOSTTYPE", uu.machine); + sprintf(toybuf, "%s-unknown-linux", uu.machine); + setonlylocal(&ll, "MACHTYPE", toybuf); + setonlylocal(&ll, "OSTYPE", uu.sysname); + // sprintf(toybuf, "%s-toybox", TOYBOX_VERSION); + // setonlylocal(&ll, "BASH_VERSION", toybuf); + *ll++ = xstrdup("OPTERR=1"); + *toybuf = 0; + if (readlink0("/proc/self/exe", toybuf, sizeof(toybuf))) + setonlylocal(&ll, "BASH", toybuf); + *ll = 0; // Ensure environ copied and toys.envc set, and clean out illegal entries xunsetenv(""); @@ -1824,12 +1882,30 @@ void subshell_imports(void) } environ[toys.optc = to] = 0; + // set/update PWD + sh_run("cd ."); + + // set _ to path to this shell + s = toys.argv[0]; + ss = 0; + if (!strchr(s, '/')) { + if (!(ss = getcwd(0, 0))) { + if (*toybuf) s = toybuf; + } else { + s = xmprintf("%s/%s", ss, s); + free(ss); + ss = s; + } + } + xsetenv("_", s); + free(ss); + if (!getvar("SHLVL")) xsetenv("SHLVL", "1"); + //TODO indexed array,associative array,integer,local,nameref,readonly,uppercase // if (s+1<ss && strchr("aAilnru", *s)) { // sanity check: magic env variable, pipe status - if (CFG_TOYBOX_FORK || toys.stacktop || pid != getpid() || ppid != getppid()) - return; + if (CFG_TOYBOX_FORK || toys.stacktop || pid!=mypid || ppid!=myppid) return; if (fstat(254, &st) || !S_ISFIFO(st.st_mode)) error_exit(0); fcntl(254, F_SETFD, FD_CLOEXEC); fp = fdopen(254, "r"); @@ -1853,25 +1929,12 @@ void sh_main(void) TT.hfd = 10; signal(SIGPIPE, SIG_IGN); - // Ensure environ copied and toys.envc set - xunsetenv(""); - - // TODO: traverse and unset illegal environment variables named "$" and such - // TODO euid stuff? - + // TODO login shell? // TODO read profile, read rc // if (!FLAG(noprofile)) { } - // Set local variable $HOME to user's login path - if (!(new = getenv("HOME"))) { - struct passwd *pw = getpwuid(getuid()); - - setvar(xmprintf("HOME=%s", (pw && *pw->pw_dir)?pw->pw_dir:"/"), TAKE_MEM); - } - sh_run("cd ."); - if (BUGBUG) { int fd = open("/dev/tty", O_RDWR); dup2(fd, 255); close(fd); } // Is this an interactive shell? // if (FLAG(i) || (!FLAG(c)&&(FLAG(S)||!toys.optc) && isatty(0) && isatty(1))) @@ -1879,10 +1942,12 @@ if (BUGBUG) { int fd = open("/dev/tty", O_RDWR); dup2(fd, 255); close(fd); } // Set up signal handlers and grab control of this tty. // Read environment for exports from parent shell - subshell_imports(); + subshell_setup(); memset(&scratch, 0, sizeof(scratch)); - if (TT.command) f = fmemopen(TT.command, strlen(TT.command), "r"); + +// TODO unify fmemopen() here with sh_run + if (TT.c) f = fmemopen(TT.c, strlen(TT.c), "r"); else if (*toys.optargs) f = xfopen(*toys.optargs, "r"); else { f = stdin; @@ -1898,7 +1963,7 @@ if (BUGBUG) { int fd = open("/dev/tty", O_RDWR); dup2(fd, 255); close(fd); } if (!s) s = prompt ? "> " : (getpid() ? "\\$ " : "# "); do_prompt(s); } else TT.lineno++; -// TODO line editing/history +// TODO line editing/history, should set $COLUMNS $LINES and sigwinch update if (!(new = xgetline(f ? f : stdin, 0))) break; // TODO if (!isspace(*new)) add_to_history(line); @@ -1984,7 +2049,7 @@ void cd_main(void) if (bad || chdir(dd)) perror_msg("chdir '%s'", dd); else { - if (pwd) xsetenv("OLD", pwd); + if (pwd) xsetenv("OLDPWD", pwd); xsetenv("PWD", dd); } free(dd); diff --git a/toys/pending/wget.c b/toys/pending/wget.c index 405ed942..16672a24 100644 --- a/toys/pending/wget.c +++ b/toys/pending/wget.c @@ -135,7 +135,7 @@ void wget_main(void) FILE *fp; ssize_t len, body_len; char *body, *result, *rc, *r_str, *redir_loc = 0; - char ua[18] = "toybox wget", ver[6], hostname[1024], port[6], path[1024]; + char ua[18] = "toybox wget", hostname[1024], port[6], path[1024]; // TODO extract filename to be saved from URL if (!(toys.optflags & FLAG_O)) help_exit("no filename"); @@ -145,9 +145,7 @@ void wget_main(void) get_info(toys.optargs[0], hostname, port, path); -#ifdef TOYBOX_VERSION - strcat(ua, "/"), strncpy(ver, TOYBOX_VERSION, 5), strcat(ua, ver); -#endif + sprintf("/%s", TOYBOX_VERSION); for (;; redirects--) { sock = conn_svr(hostname, port); // compose HTTP request diff --git a/toys/posix/rm.c b/toys/posix/rm.c index 2a6bc282..eec586c5 100644 --- a/toys/posix/rm.c +++ b/toys/posix/rm.c @@ -99,6 +99,11 @@ void rm_main(void) error_msg("rm /. if you mean it"); continue; } + // "rm dir/.*" can expand to include .. which generally isn't what you want + if (!strcmp("..", basename(*s))) { + error_msg("bad path %s", *s); + continue; + } // Files that already don't exist aren't errors for -f, so try a quick // unlink now to see if it succeeds or reports that it didn't exist. |