diff options
author | Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> | 2020-02-12 01:45:46 +0000 |
---|---|---|
committer | Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> | 2020-02-12 01:45:46 +0000 |
commit | fb7d69c855de3bad843788fd005d45ecb6eea74f (patch) | |
tree | 9f160822015c2b5af32dd946825ebf2bec998fee | |
parent | bd9aa03690535319bf9cf97779bf9f712b2fc3e2 (diff) | |
parent | acf30bdca25f699196efddfa2a2104c567c09c88 (diff) | |
download | toybox-fb7d69c855de3bad843788fd005d45ecb6eea74f.tar.gz |
Merge remote-tracking branch 'toybox/master' into HEAD am: d8dd46c6a4 am: 724d425513 am: acf30bdca2
Change-Id: I8af39d15f747e0537d79955df24c975777b2b4cc
-rw-r--r-- | android/device/generated/flags.h | 28 | ||||
-rw-r--r-- | android/device/generated/globals.h | 10 | ||||
-rw-r--r-- | android/device/generated/newtoys.h | 4 | ||||
-rw-r--r-- | android/linux/generated/flags.h | 28 | ||||
-rw-r--r-- | android/linux/generated/globals.h | 10 | ||||
-rw-r--r-- | android/linux/generated/newtoys.h | 4 | ||||
-rw-r--r-- | android/mac/generated/flags.h | 28 | ||||
-rw-r--r-- | android/mac/generated/globals.h | 10 | ||||
-rw-r--r-- | android/mac/generated/newtoys.h | 4 | ||||
-rw-r--r-- | lib/lib.c | 26 | ||||
-rw-r--r-- | lib/lib.h | 1 | ||||
-rw-r--r-- | main.c | 42 | ||||
-rwxr-xr-x | tests/chattr.test | 155 | ||||
-rwxr-xr-x | tests/sh.test | 45 | ||||
-rw-r--r-- | toys/other/lsattr.c | 139 | ||||
-rw-r--r-- | toys/other/stat.c | 2 | ||||
-rw-r--r-- | toys/pending/readelf.c | 12 | ||||
-rw-r--r-- | toys/pending/sh.c | 111 | ||||
-rw-r--r-- | toys/pending/vi.c | 4 | ||||
-rw-r--r-- | toys/posix/unlink.c | 2 |
20 files changed, 362 insertions, 303 deletions
diff --git a/android/device/generated/flags.h b/android/device/generated/flags.h index 3a1ff621..155cfc9f 100644 --- a/android/device/generated/flags.h +++ b/android/device/generated/flags.h @@ -218,12 +218,15 @@ #undef FLAG_L #endif -// chattr +// chattr ?p#v#R ?p#v#R #undef OPTSTR_chattr -#define OPTSTR_chattr 0 +#define OPTSTR_chattr "?p#v#R" #ifdef CLEANUP_chattr #undef CLEANUP_chattr #undef FOR_chattr +#undef FLAG_R +#undef FLAG_v +#undef FLAG_p #endif // chcon <2hvR <2hvR @@ -1645,18 +1648,18 @@ #undef FLAG_color #endif -// lsattr vpldaR vpldaR +// lsattr ldapvR ldapvR #undef OPTSTR_lsattr -#define OPTSTR_lsattr "vpldaR" +#define OPTSTR_lsattr "ldapvR" #ifdef CLEANUP_lsattr #undef CLEANUP_lsattr #undef FOR_lsattr #undef FLAG_R +#undef FLAG_v +#undef FLAG_p #undef FLAG_a #undef FLAG_d #undef FLAG_l -#undef FLAG_p -#undef FLAG_v #endif // lsmod @@ -3599,6 +3602,9 @@ #ifndef TT #define TT this.chattr #endif +#define FLAG_R (1<<0) +#define FLAG_v (1<<1) +#define FLAG_p (1<<2) #endif #ifdef FOR_chcon @@ -4807,11 +4813,11 @@ #define TT this.lsattr #endif #define FLAG_R (1<<0) -#define FLAG_a (1<<1) -#define FLAG_d (1<<2) -#define FLAG_l (1<<3) -#define FLAG_p (1<<4) -#define FLAG_v (1<<5) +#define FLAG_v (1<<1) +#define FLAG_p (1<<2) +#define FLAG_a (1<<3) +#define FLAG_d (1<<4) +#define FLAG_l (1<<5) #endif #ifdef FOR_lsmod diff --git a/android/device/generated/globals.h b/android/device/generated/globals.h index 43b5d6e8..6b93ddd9 100644 --- a/android/device/generated/globals.h +++ b/android/device/generated/globals.h @@ -307,6 +307,15 @@ struct losetup_data { char *dir; }; +// toys/other/lsattr.c + +struct lsattr_data { + long v; + long p; + + long add, rm, set; +}; + // toys/other/lspci.c struct lspci_data { @@ -1537,6 +1546,7 @@ extern union global_union { struct ionice_data ionice; struct login_data login; struct losetup_data losetup; + struct lsattr_data lsattr; struct lspci_data lspci; struct makedevs_data makedevs; struct mix_data mix; diff --git a/android/device/generated/newtoys.h b/android/device/generated/newtoys.h index 54da874f..6b93beab 100644 --- a/android/device/generated/newtoys.h +++ b/android/device/generated/newtoys.h @@ -25,7 +25,7 @@ USE_CAL(NEWTOY(cal, ">2h", TOYFLAG_USR|TOYFLAG_BIN)) USE_CAT(NEWTOY(cat, "u"USE_CAT_V("vte"), TOYFLAG_BIN)) USE_CATV(NEWTOY(catv, USE_CATV("vte"), TOYFLAG_USR|TOYFLAG_BIN)) USE_SH(NEWTOY(cd, ">1LP[-LP]", TOYFLAG_NOFORK)) -USE_CHATTR(NEWTOY(chattr, NULL, TOYFLAG_BIN)) +USE_CHATTR(NEWTOY(chattr, "?p#v#R", TOYFLAG_BIN)) USE_CHCON(NEWTOY(chcon, "<2hvR", TOYFLAG_USR|TOYFLAG_BIN)) USE_CHGRP(NEWTOY(chgrp, "<2hPLHRfv[-HLP]", TOYFLAG_BIN)) USE_CHMOD(NEWTOY(chmod, "<2?vRf[-vf]", TOYFLAG_BIN)) @@ -147,7 +147,7 @@ USE_LOGNAME(NEWTOY(logname, ">0", TOYFLAG_USR|TOYFLAG_BIN)) USE_LOGWRAPPER(NEWTOY(logwrapper, 0, TOYFLAG_NOHELP|TOYFLAG_USR|TOYFLAG_BIN)) USE_LOSETUP(NEWTOY(losetup, ">2S(sizelimit)#s(show)ro#j:fdcaD[!afj]", TOYFLAG_SBIN)) USE_LS(NEWTOY(ls, "(color):;(full-time)(show-control-chars)ZgoACFHLRSabcdfhikl@mnpqrstuw#=80<0x1[-Cxm1][-Cxml][-Cxmo][-Cxmg][-cu][-ftS][-HL][!qb]", TOYFLAG_BIN|TOYFLAG_LOCALE)) -USE_LSATTR(NEWTOY(lsattr, "vpldaR", TOYFLAG_BIN)) +USE_LSATTR(NEWTOY(lsattr, "ldapvR", TOYFLAG_BIN)) USE_LSMOD(NEWTOY(lsmod, NULL, TOYFLAG_SBIN)) USE_LSOF(NEWTOY(lsof, "lp*t", TOYFLAG_USR|TOYFLAG_BIN)) USE_LSPCI(NEWTOY(lspci, "emkn"USE_LSPCI_TEXT("@i:"), TOYFLAG_USR|TOYFLAG_BIN)) diff --git a/android/linux/generated/flags.h b/android/linux/generated/flags.h index a8b6da8e..ec5ceb0a 100644 --- a/android/linux/generated/flags.h +++ b/android/linux/generated/flags.h @@ -218,12 +218,15 @@ #undef FLAG_L #endif -// chattr +// chattr ?p#v#R #undef OPTSTR_chattr -#define OPTSTR_chattr 0 +#define OPTSTR_chattr "?p#v#R" #ifdef CLEANUP_chattr #undef CLEANUP_chattr #undef FOR_chattr +#undef FLAG_R +#undef FLAG_v +#undef FLAG_p #endif // chcon <2hvR @@ -1645,18 +1648,18 @@ #undef FLAG_color #endif -// lsattr vpldaR +// lsattr ldapvR #undef OPTSTR_lsattr -#define OPTSTR_lsattr "vpldaR" +#define OPTSTR_lsattr "ldapvR" #ifdef CLEANUP_lsattr #undef CLEANUP_lsattr #undef FOR_lsattr #undef FLAG_R +#undef FLAG_v +#undef FLAG_p #undef FLAG_a #undef FLAG_d #undef FLAG_l -#undef FLAG_p -#undef FLAG_v #endif // lsmod @@ -3599,6 +3602,9 @@ #ifndef TT #define TT this.chattr #endif +#define FLAG_R (FORCED_FLAG<<0) +#define FLAG_v (FORCED_FLAG<<1) +#define FLAG_p (FORCED_FLAG<<2) #endif #ifdef FOR_chcon @@ -4807,11 +4813,11 @@ #define TT this.lsattr #endif #define FLAG_R (FORCED_FLAG<<0) -#define FLAG_a (FORCED_FLAG<<1) -#define FLAG_d (FORCED_FLAG<<2) -#define FLAG_l (FORCED_FLAG<<3) -#define FLAG_p (FORCED_FLAG<<4) -#define FLAG_v (FORCED_FLAG<<5) +#define FLAG_v (FORCED_FLAG<<1) +#define FLAG_p (FORCED_FLAG<<2) +#define FLAG_a (FORCED_FLAG<<3) +#define FLAG_d (FORCED_FLAG<<4) +#define FLAG_l (FORCED_FLAG<<5) #endif #ifdef FOR_lsmod diff --git a/android/linux/generated/globals.h b/android/linux/generated/globals.h index 43b5d6e8..6b93ddd9 100644 --- a/android/linux/generated/globals.h +++ b/android/linux/generated/globals.h @@ -307,6 +307,15 @@ struct losetup_data { char *dir; }; +// toys/other/lsattr.c + +struct lsattr_data { + long v; + long p; + + long add, rm, set; +}; + // toys/other/lspci.c struct lspci_data { @@ -1537,6 +1546,7 @@ extern union global_union { struct ionice_data ionice; struct login_data login; struct losetup_data losetup; + struct lsattr_data lsattr; struct lspci_data lspci; struct makedevs_data makedevs; struct mix_data mix; diff --git a/android/linux/generated/newtoys.h b/android/linux/generated/newtoys.h index 54da874f..6b93beab 100644 --- a/android/linux/generated/newtoys.h +++ b/android/linux/generated/newtoys.h @@ -25,7 +25,7 @@ USE_CAL(NEWTOY(cal, ">2h", TOYFLAG_USR|TOYFLAG_BIN)) USE_CAT(NEWTOY(cat, "u"USE_CAT_V("vte"), TOYFLAG_BIN)) USE_CATV(NEWTOY(catv, USE_CATV("vte"), TOYFLAG_USR|TOYFLAG_BIN)) USE_SH(NEWTOY(cd, ">1LP[-LP]", TOYFLAG_NOFORK)) -USE_CHATTR(NEWTOY(chattr, NULL, TOYFLAG_BIN)) +USE_CHATTR(NEWTOY(chattr, "?p#v#R", TOYFLAG_BIN)) USE_CHCON(NEWTOY(chcon, "<2hvR", TOYFLAG_USR|TOYFLAG_BIN)) USE_CHGRP(NEWTOY(chgrp, "<2hPLHRfv[-HLP]", TOYFLAG_BIN)) USE_CHMOD(NEWTOY(chmod, "<2?vRf[-vf]", TOYFLAG_BIN)) @@ -147,7 +147,7 @@ USE_LOGNAME(NEWTOY(logname, ">0", TOYFLAG_USR|TOYFLAG_BIN)) USE_LOGWRAPPER(NEWTOY(logwrapper, 0, TOYFLAG_NOHELP|TOYFLAG_USR|TOYFLAG_BIN)) USE_LOSETUP(NEWTOY(losetup, ">2S(sizelimit)#s(show)ro#j:fdcaD[!afj]", TOYFLAG_SBIN)) USE_LS(NEWTOY(ls, "(color):;(full-time)(show-control-chars)ZgoACFHLRSabcdfhikl@mnpqrstuw#=80<0x1[-Cxm1][-Cxml][-Cxmo][-Cxmg][-cu][-ftS][-HL][!qb]", TOYFLAG_BIN|TOYFLAG_LOCALE)) -USE_LSATTR(NEWTOY(lsattr, "vpldaR", TOYFLAG_BIN)) +USE_LSATTR(NEWTOY(lsattr, "ldapvR", TOYFLAG_BIN)) USE_LSMOD(NEWTOY(lsmod, NULL, TOYFLAG_SBIN)) USE_LSOF(NEWTOY(lsof, "lp*t", TOYFLAG_USR|TOYFLAG_BIN)) USE_LSPCI(NEWTOY(lspci, "emkn"USE_LSPCI_TEXT("@i:"), TOYFLAG_USR|TOYFLAG_BIN)) diff --git a/android/mac/generated/flags.h b/android/mac/generated/flags.h index fc45c19b..018ba654 100644 --- a/android/mac/generated/flags.h +++ b/android/mac/generated/flags.h @@ -218,12 +218,15 @@ #undef FLAG_L #endif -// chattr +// chattr ?p#v#R #undef OPTSTR_chattr -#define OPTSTR_chattr 0 +#define OPTSTR_chattr "?p#v#R" #ifdef CLEANUP_chattr #undef CLEANUP_chattr #undef FOR_chattr +#undef FLAG_R +#undef FLAG_v +#undef FLAG_p #endif // chcon <2hvR @@ -1645,18 +1648,18 @@ #undef FLAG_color #endif -// lsattr vpldaR +// lsattr ldapvR #undef OPTSTR_lsattr -#define OPTSTR_lsattr "vpldaR" +#define OPTSTR_lsattr "ldapvR" #ifdef CLEANUP_lsattr #undef CLEANUP_lsattr #undef FOR_lsattr #undef FLAG_R +#undef FLAG_v +#undef FLAG_p #undef FLAG_a #undef FLAG_d #undef FLAG_l -#undef FLAG_p -#undef FLAG_v #endif // lsmod @@ -3599,6 +3602,9 @@ #ifndef TT #define TT this.chattr #endif +#define FLAG_R (FORCED_FLAG<<0) +#define FLAG_v (FORCED_FLAG<<1) +#define FLAG_p (FORCED_FLAG<<2) #endif #ifdef FOR_chcon @@ -4807,11 +4813,11 @@ #define TT this.lsattr #endif #define FLAG_R (FORCED_FLAG<<0) -#define FLAG_a (FORCED_FLAG<<1) -#define FLAG_d (FORCED_FLAG<<2) -#define FLAG_l (FORCED_FLAG<<3) -#define FLAG_p (FORCED_FLAG<<4) -#define FLAG_v (FORCED_FLAG<<5) +#define FLAG_v (FORCED_FLAG<<1) +#define FLAG_p (FORCED_FLAG<<2) +#define FLAG_a (FORCED_FLAG<<3) +#define FLAG_d (FORCED_FLAG<<4) +#define FLAG_l (FORCED_FLAG<<5) #endif #ifdef FOR_lsmod diff --git a/android/mac/generated/globals.h b/android/mac/generated/globals.h index 43b5d6e8..6b93ddd9 100644 --- a/android/mac/generated/globals.h +++ b/android/mac/generated/globals.h @@ -307,6 +307,15 @@ struct losetup_data { char *dir; }; +// toys/other/lsattr.c + +struct lsattr_data { + long v; + long p; + + long add, rm, set; +}; + // toys/other/lspci.c struct lspci_data { @@ -1537,6 +1546,7 @@ extern union global_union { struct ionice_data ionice; struct login_data login; struct losetup_data losetup; + struct lsattr_data lsattr; struct lspci_data lspci; struct makedevs_data makedevs; struct mix_data mix; diff --git a/android/mac/generated/newtoys.h b/android/mac/generated/newtoys.h index 54da874f..6b93beab 100644 --- a/android/mac/generated/newtoys.h +++ b/android/mac/generated/newtoys.h @@ -25,7 +25,7 @@ USE_CAL(NEWTOY(cal, ">2h", TOYFLAG_USR|TOYFLAG_BIN)) USE_CAT(NEWTOY(cat, "u"USE_CAT_V("vte"), TOYFLAG_BIN)) USE_CATV(NEWTOY(catv, USE_CATV("vte"), TOYFLAG_USR|TOYFLAG_BIN)) USE_SH(NEWTOY(cd, ">1LP[-LP]", TOYFLAG_NOFORK)) -USE_CHATTR(NEWTOY(chattr, NULL, TOYFLAG_BIN)) +USE_CHATTR(NEWTOY(chattr, "?p#v#R", TOYFLAG_BIN)) USE_CHCON(NEWTOY(chcon, "<2hvR", TOYFLAG_USR|TOYFLAG_BIN)) USE_CHGRP(NEWTOY(chgrp, "<2hPLHRfv[-HLP]", TOYFLAG_BIN)) USE_CHMOD(NEWTOY(chmod, "<2?vRf[-vf]", TOYFLAG_BIN)) @@ -147,7 +147,7 @@ USE_LOGNAME(NEWTOY(logname, ">0", TOYFLAG_USR|TOYFLAG_BIN)) USE_LOGWRAPPER(NEWTOY(logwrapper, 0, TOYFLAG_NOHELP|TOYFLAG_USR|TOYFLAG_BIN)) USE_LOSETUP(NEWTOY(losetup, ">2S(sizelimit)#s(show)ro#j:fdcaD[!afj]", TOYFLAG_SBIN)) USE_LS(NEWTOY(ls, "(color):;(full-time)(show-control-chars)ZgoACFHLRSabcdfhikl@mnpqrstuw#=80<0x1[-Cxm1][-Cxml][-Cxmo][-Cxmg][-cu][-ftS][-HL][!qb]", TOYFLAG_BIN|TOYFLAG_LOCALE)) -USE_LSATTR(NEWTOY(lsattr, "vpldaR", TOYFLAG_BIN)) +USE_LSATTR(NEWTOY(lsattr, "ldapvR", TOYFLAG_BIN)) USE_LSMOD(NEWTOY(lsmod, NULL, TOYFLAG_SBIN)) USE_LSOF(NEWTOY(lsof, "lp*t", TOYFLAG_USR|TOYFLAG_BIN)) USE_LSPCI(NEWTOY(lspci, "emkn"USE_LSPCI_TEXT("@i:"), TOYFLAG_USR|TOYFLAG_BIN)) @@ -508,21 +508,14 @@ off_t fdlength(int fd) return base; } -// Read contents of file as a single nul-terminated string. -// measure file size if !len, allocate buffer if !buf -// Existing buffers need len in *plen -// Returns amount of data read in *plen -char *readfileat(int dirfd, char *name, char *ibuf, off_t *plen) +char *readfd(int fd, char *ibuf, off_t *plen) { off_t len, rlen; - int fd; char *buf, *rbuf; // Unsafe to probe for size with a supplied buffer, don't ever do that. if (CFG_TOYBOX_DEBUG && (ibuf ? !*plen : *plen)) error_exit("bad readfileat"); - if (-1 == (fd = openat(dirfd, name, O_RDONLY))) return 0; - // If we dunno the length, probe it. If we can't probe, start with 1 page. if (!*plen) { if ((len = fdlength(fd))>0) *plen = len; @@ -543,7 +536,6 @@ char *readfileat(int dirfd, char *name, char *ibuf, off_t *plen) len -= rlen; } *plen = len = rlen+(rbuf-buf); - close(fd); if (rlen<0) { if (ibuf != buf) free(buf); @@ -553,6 +545,20 @@ char *readfileat(int dirfd, char *name, char *ibuf, off_t *plen) return buf; } +// Read contents of file as a single nul-terminated string. +// measure file size if !len, allocate buffer if !buf +// Existing buffers need len in *plen +// Returns amount of data read in *plen +char *readfileat(int dirfd, char *name, char *ibuf, off_t *plen) +{ + if (-1 == (dirfd = openat(dirfd, name, O_RDONLY))) return 0; + + ibuf = readfd(dirfd, ibuf, plen); + close(dirfd); + + return ibuf; +} + char *readfile(char *name, char *ibuf, off_t len) { return readfileat(AT_FDCWD, name, ibuf, &len); @@ -1434,4 +1440,4 @@ char *elf_arch_name(int type) } sprintf(libbuf, "unknown arch %d", type); return libbuf; -}
\ No newline at end of file +} @@ -209,6 +209,7 @@ off_t lskip(int fd, off_t offset); int mkpathat(int atfd, char *dir, mode_t lastmode, int flags); int mkpath(char *dir); struct string_list **splitpath(char *path, struct string_list **list); +char *readfd(int fd, char *ibuf, off_t *plen); char *readfileat(int dirfd, char *name, char *buf, off_t *len); char *readfile(char *name, char *buf, off_t len); void msleep(long milliseconds); @@ -29,14 +29,11 @@ struct toy_list *toy_find(char *name) if (!CFG_TOYBOX || strchr(name, '/')) return 0; - // If the name starts with "toybox" accept that as a match. Otherwise - // skip the first entry, which is out of order. - - if (!strncmp(name, "toybox", 6)) return toy_list; + // Multiplexer name works as prefix, else skip first entry (it's out of order) + if (!toys.which && strstart(&name, "toybox")) return toy_list; bottom = 1; // Binary search to find this command. - top = ARRAY_LEN(toy_list)-1; for (;;) { int result; @@ -179,7 +176,7 @@ void toy_exec(char *argv[]) // If first argument starts with - output list of command install paths. void toybox_main(void) { - static char *toy_paths[]={"usr/","bin/","sbin/",0}; + static char *toy_paths[] = {"usr/","bin/","sbin/",0}; int i, len = 0; // fast path: try to exec immediately. @@ -201,12 +198,12 @@ void toybox_main(void) if (toys.argv[1] && toys.argv[1][0] != '-') unknown(toys.argv[1]); // Output list of command. - for (i=1; i<ARRAY_LEN(toy_list); i++) { + for (i = 1; i<ARRAY_LEN(toy_list); i++) { int fl = toy_list[i].flags; if (fl & TOYMASK_LOCATION) { if (toys.argv[1]) { int j; - for (j=0; toy_paths[j]; j++) + for (j = 0; toy_paths[j]; j++) if (fl & (1<<j)) len += printf("%s", toy_paths[j]); } len += printf("%s",toy_list[i].name); @@ -219,40 +216,27 @@ void toybox_main(void) int main(int argc, char *argv[]) { + // don't segfault if our environment is crazy if (!*argv) return 127; // Snapshot stack location so we can detect recursion depth later. - // This is its own block so probe doesn't permanently consume stack. + // Nommu has special reentry path, !stacktop = "vfork/exec self happened" + if (!CFG_TOYBOX_FORK && (0x80 & **argv)) **argv &= 0x7f; else { - int stack; + int stack_start; // here so probe var won't permanently eat stack - toys.stacktop = &stack; + toys.stacktop = &stack_start; } - // Up to and including Android M, bionic's dynamic linker added a handler to - // cause a crash dump on SIGPIPE. That was removed in Android N, but adbd - // was still setting the SIGPIPE disposition to SIG_IGN, and its children - // were inheriting that. In Android O, adbd is fixed, but manually asking - // for the default disposition is harmless, and it'll be a long time before - // no one's using anything older than O! + // Android before O had non-default SIGPIPE, 7 years = remove in Sep 2024. if (CFG_TOYBOX_ON_ANDROID) signal(SIGPIPE, SIG_DFL); - // If nommu can't fork, special reentry path. - // Use !stacktop to signal "vfork happened", both before and after xexec() - if (!CFG_TOYBOX_FORK) { - if (0x80 & **argv) { - **argv &= 0x7f; - toys.stacktop = 0; - } - } - if (CFG_TOYBOX) { - // Call the multiplexer, adjusting this argv[] to be its' argv[1]. - // (It will adjust it back before calling toy_exec().) + // Call the multiplexer with argv[] as its arguments so it can toy_find() toys.argv = argv-1; toybox_main(); } else { - // a single toybox command built standalone with no multiplexer + // single command built standalone with no multiplexer is first list entry toy_singleinit(toy_list, argv); toy_list->toy_main(); } diff --git a/tests/chattr.test b/tests/chattr.test index d7dec0bc..cefc84b9 100755 --- a/tests/chattr.test +++ b/tests/chattr.test @@ -28,131 +28,56 @@ _t="abcdefghijklmnopqrstuvwxyz" _empty="--------------------" -# 'i' -- immutable +# Check +i (immutable) works by trying to write to and remove the file. _i="----i---------------" -testing "[-/+]i FILE[write]" "$IN && touch testFile && +testing "immutable" "$IN && echo "$_t" > testFile && chattr +i testFile && lsattr testFile | clean && - date > testFile 2>/dev/null; chattr -i testFile; - rm -rf testFile; $OUT " "$_i testFile\n" "" "" -testing "[-/+]i FILE[re-write]" "$IN && touch testFile && - chattr +i testFile && date > testFile 2>/dev/null || - chattr -i testFile && date > testFile 2>/dev/null && - lsattr testFile | clean; rm -rf testFile 2>/dev/null; $OUT" \ - "$_empty testFile\n" "" "" -testing "[-/+]i FILE[append]" "$IN && date > testFile && - chattr +i testFile && date >> testFile 2>/dev/null || - lsattr testFile | clean && chattr -i testFile; rm -rf testFile; $OUT" \ - "$_i testFile\n" "" "" -testing "[-/+]i FILE[move]" "$IN && date > testFile && - chattr +i testFile && mv testFile testFile1 2>/dev/null || - lsattr testFile | clean && chattr -i testFile; rm -rf testFile; $OUT" \ - "$_i testFile\n" "" "" -testing "[-/+]i FILE[delete]" "$IN && touch testFile && chattr +i testFile && - rm -f testFile 2>/dev/null || lsattr testFile | clean && - chattr -i testFile; rm -rf testFile; $OUT" "$_i testFile\n" "" "" -testing "[-/+]i FILE[read]" "$IN && echo "$_t" > testFile && - chattr +i testFile && cat testFile && lsattr testFile | clean && - chattr -i testFile; rm -rf testFile; $OUT" "$_t\n$_i testFile\n" "" "" + date > testFile 2>/dev/null; rm testFile; chattr -i testFile; + cat testFile; rm -rf testFile; $OUT " "$_i testFile\n$_t\n" "" "" -# 'a' --- append-only +# Check +a (append-only) works by failing to write and succeeding in appending. _a="-----a--------------" -testing "[-/+]a FILE[write]" "$IN && echo "$_t" > testFile && - chattr +a testFile && echo $_t > testFile || lsattr testFile | clean && - chattr -a testFile; rm -rf testFile; $OUT" "$_a testFile\n" "" "" -testing "[-/+]a FILE[re-write]" "$IN && echo "$_t" > testFile && - chattr +a testFile && echo $_t > testFile || lsattr testFile | clean && - chattr -a testFile && echo $_t > testFile && cat testFile && - lsattr testFile | clean; rm -rf testFile; - $OUT" "$_a testFile\n$_t\n$_empty testFile\n" "" "" -testing "[-/+]a FILE[append]" "$IN && echo "$_t" > testFile && - chattr +a testFile && echo $_t >> testFile && cat testFile && - lsattr testFile | clean && chattr -a testFile; rm -rf testFile; $OUT" \ - "$_t\n$_t\n$_a testFile\n" "" "" -testing "[-/+]a FILE[move]" "$IN && echo "$_t" > testFile && - chattr +a testFile && mv testFile testFile1 || - lsattr testFile | clean && chattr -a testFile; rm -rf testFile; $OUT" \ - "$_a testFile\n" "" "" -testing "[-/+]a FILE[delete]" "$IN && echo "$_t" > testFile && - chattr +a testFile && rm -f testFile || lsattr testFile | clean && - chattr -a testFile; rm -rf testFile; $OUT" "$_a testFile\n" "" "" -testing "[-/+]a FILE[read]" "$IN && echo "$_t" > testFile && - chattr +a testFile && cat testFile && lsattr testFile | clean && - chattr -a testFile; rm -rf testFile; $OUT" "$_t\n$_a testFile\n" "" "" +testing "append-only" "$IN && echo "$_t" > testFile && + chattr +a testFile && + echo $_t >> testFile && + date > testFile || lsattr testFile | clean && + chattr -a testFile; cat testFile; rm -rf testFile; $OUT" \ + "$_a testFile\n$_t\n$_t\n" "" "" -for attr in "A" "a" "c" "D" "d" "i" "j" "s" "S" "t" "T" "u" -do - testing "[-/+]$attr FILE" "$IN && echo "$_t" > testFile && - chattr +$attr testFile && cat testFile && chattr -$attr testFile && - lsattr testFile | clean; rm -rf testFile; $OUT" "$_t\n$_empty testFile\n" "" "" +# For the rest, just toggle the bits back and forth (where supported). +# Note that some file system/kernel combinations do return success but +# silently ignore your request: +T on 4.19 f2fs, or +F on 5.2i ext4, +# for example, so we're deliberately a bit selective here. +for attr in "A" "c" "d" "e" "j" "P" "S" "s" "t" "u"; do + echo "$_t" > testFile && chattr +$attr testFile 2>/dev/null || SKIPNEXT=1 + # Check that $attr is in the lsattr output, then that - turns it back off. + testing "toggle $attr" "lsattr testFile | awk '{print \$1}' > attrs; + grep -q $attr attrs || cat attrs; cat testFile && chattr -$attr testFile && + lsattr testFile | clean; rm -rf testFile" "$_t\n$_empty testFile\n" "" "" done -for attr in "A" "a" "c" "D" "d" "i" "j" "s" "S" "t" "T" "u" -do - testing "-$attr FILE" "$IN && echo "$_t" > testFile && chattr -$attr testFile && - cat testFile && lsattr testFile | clean; rm -rf testFile; $OUT" "$_t\n$_empty testFile\n" "" "" -done +_aA="-----a-A------------" +testing "multiple bits" "$IN && touch testFile && + chattr +Aa testFile && lsattr testFile | clean && + chattr -Aa testFile && lsattr testFile | clean; + rm -rf testFile; $OUT" "$_aA testFile\n$_empty testFile\n" "" "" -testing "[-/+]AacDdijsStTu FILE" "$IN && echo "$_t" > testFile && - chattr +AacDdijsStTu testFile && cat testFile && chattr -AacDdijsStTu testFile && - lsattr testFile | clean; rm -rf testFile; $OUT" "$_t\n$_empty testFile\n" "" "" -testing "[-/+]AacDdijsStTu(random) FILE" \ - "$IN && echo "$_t" > testFile && - chattr +AacDdijsStTu testFile && cat testFile && chattr -A testFile && - chattr -a testFile && chattr -c testFile && chattr -D testFile && - chattr -d testFile && chattr -i testFile && chattr -j testFile && - chattr -s testFile && chattr -S testFile && chattr -t testFile && - chattr -T testFile && chattr -u testFile && lsattr testFile | clean && - chattr -AacDdijsStTu testFile; rm -rf testFile; $OUT" \ - "$_t\n$_empty testFile\n" "" "" -testing "[-/+]AacDdijsStTu FILE*" "$IN && - echo "$_t" > testFile && echo "$_t" > testFile1 && - echo "$_t" > testFile2 && echo "$_t" > testFile3 && - echo "$_t" > testFile4 && echo "$_t" > testFile5 && - echo "$_t" > testFile6 && echo "$_t" > testFile7 && - echo "$_t" > testFile8 && echo "$_t" > testFile9 && - echo "$_t" > testFile10 && echo "$_t" > testFile11 && - chattr +AacDdijsStTu testFile* && - cat testFile9 && chattr -AacDdijsStTu testFile* && - lsattr testFile* | clean; rm -rf testFile*; $OUT" \ - "$_t\n$_empty testFile\n$_empty testFile_\n$_empty testFile_\n$_empty testFile_\n$_empty testFile_\n$_empty testFile_\n$_empty testFile_\n$_empty testFile_\n$_empty testFile_\n$_empty testFile_\n$_empty testFile_\n$_empty testFile_\n" "" "" +testing "multiple files" "$IN && touch fileA && touch fileB && + chattr +Aa fileA fileB && lsattr fileA fileB | clean && + chattr -Aa fileA fileB && lsattr fileA fileB | clean; + rm -rf testFile*; $OUT" \ + "$_aA fileA\n$_aA fileB\n$_empty fileA\n$_empty fileB\n" "" "" -# 'A' --- no-atime -_A="-------A------------" -# 's' --- secure erase -_s="s-------------------" -testing "[-/+]AacDdijsStTu(random) FILE*" \ - "$IN && echo "$_t" > testFile && - chattr +AacDdijsStTu testFile* && cat testFile && chattr -A testFile* && - chattr -a testFile* && chattr -c testFile* && chattr -D testFile* && - chattr -d testFile* && chattr -i testFile* && chattr -j testFile* && - chattr -s testFile* && chattr -S testFile* && chattr -t testFile* && - chattr -T testFile* && chattr -u testFile* && lsattr testFile | clean; - rm -rf testFile; $OUT" \ - "$_t\n$_empty testFile\n" "" "" -testing "[-/+]i FILE[write]" \ - "$IN && echo "$_t" > testFile && - chattr +i testFile && - echo \"$_t\" > testFile || lsattr testFile | clean && chattr -i testFile; - rm -rf testFile; $OUT" "$_i testFile\n" "" "" -testing "[-/+]A FILE[write]" \ - "$IN && echo "$_t" > testFile && chattr +A testFile && - echo \"$_t\" > testFile && lsattr testFile | clean && chattr -A testFile; - rm -rf testFile; $OUT" "$_A testFile\n" "" "" -testing "[-/+]s FILE[write]" \ - "$IN && echo "$_t" > testFile && chattr +s testFile && - echo \"$_t\" > testFile && lsattr testFile | clean && chattr -s testFile - rm -rf testFile; $OUT" "$_s testFile\n" "" "" -NOSPACE=1 testing "-v version FILE[write]" \ - "$IN && echo "$_t" > testFile && - chattr -v 1234 testFile && echo \"$_t\" > testFile && - lsattr -v testFile | clean; rm -rf testFile" \ - "_ $_empty testFile\n" "" "" +touch testFile; chattr -v 1234 testFile 2>/dev/null || SKIPNEXT=1 +testing "-v version" "lsattr -v testFile | awk '{print \$1}' && + chattr -v 4567 testFile && lsattr -v testFile | awk '{print \$1}'; + rm -rf testFile" "1234\n4567\n" "" "" -testing "-R [-/+]a FILE" "$IN && touch aa && chattr -R +A aa && - lsattr aa | clean && chattr -R -A aa; rm -rf aa; $OUT" "$_A aa\n" "" "" -testing "-R [-/+]a FILE.." "$IN && touch aa bb && - chattr -R +A aa bb && lsattr aa bb | clean && - chattr -R -A aa bb; rm -rf aa bb; $OUT" "$_A aa\n$_A bb\n" "" "" +mkdir -p a/b/c && touch a/b/c/fA a/b/c/fB +testing "-R" "chattr -R +a a && lsattr a/b/c/fA a/b/c/fB | clean && + chattr -R -a a && rm -rf a" \ + "$_a a/b/c/fA\n$_a a/b/c/fB\n" "" "" +rm -rf a # Clean up rm -rf testattr diff --git a/tests/sh.test b/tests/sh.test index ba469e49..ca6a2831 100755 --- a/tests/sh.test +++ b/tests/sh.test @@ -4,6 +4,8 @@ [ -f testing.sh ] && . testing.sh +# TODO make fake pty wrapper for test infrastructure + #testing "name" "command" "result" "infile" "stdin" [ -z "$SH" ] && { [ -z "$TEST_HOST" ] && SH="sh" || export SH="bash" ; } @@ -55,6 +57,13 @@ testing "leading variable assignments" \ #testing "can't have space before first : but yes around arguments" \ # 'BLAH=abcdefghi; echo ${BLAH: 1 : 3 }' "bcd\n" "" "" +NOSPACE=1 testing "curly brackets and pipe" \ + '{ echo one; echo two ; } | tee blah.txt; wc blah.txt' \ + "one\ntwo\n2 2 8 blah.txt\n" "" "" +NOSPACE=1 testing "parentheses and pipe" \ + '(echo two;echo three)|tee blah.txt;wc blah.txt' \ + "two\nthree\n2 2 10 blah.txt\n" "" "" + # texpect "name" "command" E/O/I"string" # Prompt changes for root/normal user @@ -65,3 +74,39 @@ SH="env -i PATH=${PATH@Q} PS1='\\$ ' $SH --noediting --noprofile --norc -is" txpect "prompt and exit" "$SH" "E$P" "Iexit\n" X0 txpect "prompt and echo" "$SH" "E$P" "Iecho hello\n" "Ohello"$'\n' "E$P" X0 txpect "redirect err" "$SH" "E$P" "Iecho > /dev/full\n" "E" "E$P" X0 +txpect "wait for <(exit)" "$SH" "E$P" "Icat <(echo hello 1>&2)\n" $'Ehello\n' \ + "E$P" X0 + + + +# $@ $* $# $? $- $$ $! $0 +# always exported: PWD SHLVL _ +# ./bash -c 'echo $_' prints $BASH, but PATH search shows path? Hmmm... +# ro: UID PPID EUID $ +# IFS LINENO +# PATH HOME SHELL USER LOGNAME SHLVL HOSTNAME HOSTTYPE MACHTYPE OSTYPE OLDPWD +# PS0 PS1='$ ' PS2='> ' PS3 PS4 BASH BASH_VERSION +# ENV - if [ -n "$ENV" ]; then . "$ENV"; fi # BASH_ENV - synonym for ENV +# FUNCNEST - maximum function nesting level (abort when above) +# REPLY - set by input with no args +# OPTARG OPTIND - set by getopts builtin +# OPTERR + +# maybe not: EXECIGNORE, FIGNORE, GLOBIGNORE + +#BASHPID - synonym for $$ HERE +#BASH_SUBSHELL - SHLVL synonym +#BASH_EXECUTION_STRING - -c argument +# +#automatically set: +#OPTARG - set by getopts builtin +#OPTIND - set by getopts builtin +# +#PROMPT_COMMAND PROMPT_DIRTRIM PS0 PS1 PS2 PS3 PS4 +# +#unsettable (assignments ignored before then) +#LINENO SECONDS RANDOM +#GROUPS - id -g +#HISTCMD - history number +# +#TMOUT - used by read diff --git a/toys/other/lsattr.c b/toys/other/lsattr.c index 24591d53..cd236b9e 100644 --- a/toys/other/lsattr.c +++ b/toys/other/lsattr.c @@ -7,8 +7,8 @@ * * TODO cleanup -USE_LSATTR(NEWTOY(lsattr, "vpldaR", TOYFLAG_BIN)) -USE_CHATTR(NEWTOY(chattr, NULL, TOYFLAG_BIN)) +USE_LSATTR(NEWTOY(lsattr, "ldapvR", TOYFLAG_BIN)) +USE_CHATTR(NEWTOY(chattr, "?p#v#R", TOYFLAG_BIN)) config LSATTR bool "lsattr" @@ -62,6 +62,13 @@ config CHATTR #include "toys.h" #include <linux/fs.h> +GLOBALS( + long v; + long p; + + long add, rm, set; +) + #define FS_PROJINHERT_FL 0x20000000 // Linux 4.5 #define FS_CASEFOLD_FL 0x40000000 // Linux 5.4 #define FS_VERITY_FL 0x00100000 // Linux 5.4 @@ -117,6 +124,18 @@ static int ext2_getflag(int fd, struct stat *sb, unsigned long *flag) return (ioctl(fd, FS_IOC_GETFLAGS, (void*)flag)); } +static char *attrstr(unsigned long attrs, int full) +{ + struct ext2_attr *a = e2attrs; + char *s = toybuf; + + for (; a->name; a++) + if (attrs & a->flag) *s++ = a->opt; + else if (full) *s++ = '-'; + *s = '\0'; + return toybuf; +} + static void print_file_attr(char *path) { unsigned long flag = 0, version = 0; @@ -157,14 +176,7 @@ static void print_file_attr(char *path) } if (!name_found) xprintf("---"); xputc('\n'); - } else { - int index = 0; - - for (; ptr->name; ptr++) - toybuf[index++] = (flag & ptr->flag) ? ptr->opt : '-'; - toybuf[index] = '\0'; - xprintf("%s %s\n", toybuf, path); - } + } else xprintf("%s %s\n", attrstr(flag, 1), path); } xclose(fd); return; @@ -202,7 +214,7 @@ void lsattr_main(void) { if (!*toys.optargs) dirtree_read(".", retell_dir); else - for (; *toys.optargs; toys.optargs++) { + for (; *toys.optargs; toys.optargs++) { struct stat sb; if (lstat(*toys.optargs, &sb)) perror_msg("stat '%s'", *toys.optargs); @@ -217,11 +229,6 @@ void lsattr_main(void) #define FOR_chattr #include "generated/flags.h" -static struct _chattr { - unsigned long add, rm, set, projid, version; - unsigned char pflag, vflag, recursive; -} chattr; - // Set file flags on a Linux second extended file system. static inline int ext2_setflag(int fd, struct stat *sb, unsigned long flag) { @@ -244,40 +251,21 @@ static unsigned long get_flag_val(char ch) // Parse command line argument and fill the chattr structure. static void parse_cmdline_arg(char ***argv) { - char *arg = **argv, *ptr = NULL; + char *arg = **argv, *ptr; while (arg) { switch (arg[0]) { case '-': - for (ptr = ++arg; *ptr; ptr++) { - if (*ptr == 'R') { - chattr.recursive = 1; - continue; - } else if (*ptr == 'p' || *ptr == 'v') { - unsigned val; - - arg = *(*argv += 1); - if (!arg) help_exit("missing arg to -%c", *ptr); - - val = atolx_range(arg, 0, UINT_MAX); - if (*ptr == 'v') { - chattr.version = val; - chattr.vflag = 1; - } else { - chattr.projid = val; - chattr.pflag = 1; - } - continue; - } else chattr.rm |= get_flag_val(*ptr); - } + for (ptr = ++arg; *ptr; ptr++) + TT.rm |= get_flag_val(*ptr); break; case '+': for (ptr = ++arg; *ptr; ptr++) - chattr.add |= get_flag_val(*ptr); + TT.add |= get_flag_val(*ptr); break; case '=': for (ptr = ++arg; *ptr; ptr++) - chattr.set |= get_flag_val(*ptr); + TT.set |= get_flag_val(*ptr); break; default: return; } @@ -288,9 +276,8 @@ static void parse_cmdline_arg(char ***argv) // Update attribute of given file. static int update_attr(struct dirtree *root) { - unsigned long fval = 0; char *fpath = NULL; - int fd; + int v = TT.v, fd; if (!dirtree_notdotdot(root)) return 0; @@ -298,7 +285,7 @@ static int update_attr(struct dirtree *root) * if file is a link and recursive is set or file is not regular+link+dir * (like fifo or dev file) then escape the file. */ - if ((S_ISLNK(root->st.st_mode) && chattr.recursive) + if ((S_ISLNK(root->st.st_mode) && FLAG(R)) || (!S_ISREG(root->st.st_mode) && !S_ISLNK(root->st.st_mode) && !S_ISDIR(root->st.st_mode))) return 0; @@ -308,56 +295,62 @@ static int update_attr(struct dirtree *root) free(fpath); return DIRTREE_ABORT; } - // Get current attr of file. - if (ext2_getflag(fd, &(root->st), &fval) < 0) { - perror_msg("read flags of '%s'", fpath); - free(fpath); - xclose(fd); - return DIRTREE_ABORT; - } - if (chattr.set) { // for '=' operator. - if (ext2_setflag(fd, &(root->st), chattr.set) < 0) - perror_msg("setting flags '%s'", fpath); - } else { // for '-' / '+' operator. - fval &= ~(chattr.rm); - fval |= chattr.add; - if (!S_ISDIR(root->st.st_mode)) fval &= ~FS_DIRSYNC_FL; - if (ext2_setflag(fd, &(root->st), fval) < 0) - perror_msg("setting flags '%s'", fpath); + + // Any potential flag changes? + if (TT.set | TT.add | TT.set) { + unsigned long orig, new; + + // Read current flags. + if (ext2_getflag(fd, &(root->st), &orig) < 0) { + perror_msg("read flags of '%s'", fpath); + free(fpath); + xclose(fd); + return DIRTREE_ABORT; + } + // Apply the requested changes. + if (TT.set) new = TT.set; // '='. + else { // '-' and/or '+'. + new = orig; + new &= ~(TT.rm); + new |= TT.add; + if (!S_ISDIR(root->st.st_mode)) new &= ~FS_DIRSYNC_FL; + } + // Write them back if there was any change. + if (orig != new && ext2_setflag(fd, &(root->st), new)<0) + perror_msg("%s: setting flags to =%s failed", fpath, attrstr(new, 0)); } // (FS_IOC_SETVERSION works all the way back to 2.6, but FS_IOC_FSSETXATTR // isn't available until 4.5.) - if (chattr.vflag && (ioctl(fd, FS_IOC_SETVERSION, &chattr.version)<0)) - perror_msg("while setting version on '%s'", fpath); + if (FLAG(v) && (ioctl(fd, FS_IOC_SETVERSION, &v)<0)) + perror_msg("%s: setting version to %d failed", fpath, v); - if (chattr.pflag) { + if (FLAG(p)) { struct fsxattr_4_5 fsx; + int fail = ioctl(fd, FS_IOC_FSGETXATTR_4_5, &fsx); - if (ioctl(fd, FS_IOC_FSGETXATTR_4_5, &fsx)) - perror_exit("%s: FS_IOC_FSGETXATTR failed", fpath); - fsx.fsx_projid = chattr.projid; - if (ioctl(fd, FS_IOC_FSSETXATTR_4_5, &fsx)) - perror_exit("%s: FS_IOC_FSSETXATTR failed", fpath); + fsx.fsx_projid = TT.p; + if (fail || ioctl(fd, FS_IOC_FSSETXATTR_4_5, &fsx)) + perror_msg("%s: setting projid to %u failed", fpath, fsx.fsx_projid); } free(fpath); xclose(fd); - return (S_ISDIR(root->st.st_mode) && chattr.recursive) ? DIRTREE_RECURSE : 0; + return (FLAG(R) && S_ISDIR(root->st.st_mode)) ? DIRTREE_RECURSE : 0; } void chattr_main(void) { char **argv = toys.optargs; - memset(&chattr, 0, sizeof(struct _chattr)); parse_cmdline_arg(&argv); + if (TT.p < 0 || TT.p > UINT_MAX) error_exit("bad projid %lu", TT.p); + if (TT.v < 0 || TT.v > UINT_MAX) error_exit("bad version %ld", TT.v); if (!*argv) help_exit("no file"); - if (chattr.set && (chattr.add || chattr.rm)) + if (TT.set && (TT.add || TT.rm)) error_exit("no '=' with '-' or '+'"); - if (chattr.rm & chattr.add) error_exit("set/unset same flag"); - if (!(chattr.add || chattr.rm || chattr.set || chattr.pflag || chattr.vflag)) + if (TT.rm & TT.add) error_exit("set/unset same flag"); + if (!(TT.add || TT.rm || TT.set || FLAG(p) || FLAG(v))) error_exit("need '-p', '-v', '=', '-', or '+'"); for (; *argv; argv++) dirtree_read(*argv, update_attr); - toys.exitval = 0; //always set success at this point. } diff --git a/toys/other/stat.c b/toys/other/stat.c index 08b278f8..37dd1808 100644 --- a/toys/other/stat.c +++ b/toys/other/stat.c @@ -117,7 +117,7 @@ static void print_stat(char type) printf("%s", TT.file); if (S_ISLNK(stat->st_mode)) if (readlink0(TT.file, toybuf, sizeof(toybuf))) - printf(" -> `%s'", toybuf); + printf(" -> '%s'", toybuf); } else if (type == 'o') out('u', stat->st_blksize); else if (type == 's') out('u', stat->st_size); else if (type == 't') out('x', dev_major(stat->st_rdev)); diff --git a/toys/pending/readelf.c b/toys/pending/readelf.c index a9554265..acb1ed0f 100644 --- a/toys/pending/readelf.c +++ b/toys/pending/readelf.c @@ -43,14 +43,14 @@ GLOBALS( // Section header. struct sh { int type, link, info; - long flags, addr, offset, size, addralign, entsize; + long long flags, addr, offset, size, addralign, entsize; char *name; }; // Program header. struct ph { int type, flags; - long offset, vaddr, paddr, filesz, memsz, align; + long long offset, vaddr, paddr, filesz, memsz, align; }; static void get_sh(int i, struct sh *s) @@ -396,7 +396,7 @@ static void scan_elf() char sh_flags[12] = {}, *p = sh_flags; for (j=0; j<12; j++) if (s.flags&(1<<j)) *p++="WAXxMSILOTC"[j]; - printf(" [%2d] %-20s %-14s %0*lx %06lx %06lx %02lx %3s %2d %2d %2ld\n", + printf(" [%2d] %-20s %-14s %0*llx %06llx %06llx %02llx %3s %2d %2d %2lld\n", i, s.name, sh_type(s.type), w, s.addr, s.offset, s.size, s.entsize, sh_flags, s.link, s.info, s.addralign); } @@ -423,7 +423,7 @@ static void scan_elf() "Offset", w, "VirtAddr", w, "PhysAddr", "FileSiz", "MemSiz"); for (i=0; i<phnum; i++) { get_ph(i, &ph); - printf(" %-14s 0x%06lx 0x%0*lx 0x%0*lx 0x%05lx 0x%05lx %c%c%c %#lx\n", + printf(" %-14s 0x%06llx 0x%0*llx 0x%0*llx 0x%05llx 0x%05llx %c%c%c %#llx\n", ph_type(ph.type), ph.offset, w, ph.vaddr, w, ph.paddr, ph.filesz, ph.memsz, ph.flags&4?'R':' ', ph.flags&2?'W':' ', ph.flags&1?'E':' ', ph.align); @@ -456,7 +456,7 @@ static void scan_elf() xputc('\n'); if (!dynamic.size) printf("There is no dynamic section in this file.\n"); - else printf("Dynamic section at offset 0x%lx contains %ld entries:\n" + else printf("Dynamic section at offset 0x%llx contains %lld entries:\n" " %-*s %-20s %s\n", dynamic.offset, dynamic.size/dynamic.entsize, w+2, "Tag", "Type", "Name/Value"); @@ -517,7 +517,7 @@ static void scan_elf() get_ph(i, &ph); if (ph.type == 4 /*PT_NOTE*/) { printf("\n" - "Displaying notes found at file offset 0x%lx with length 0x%lx:\n", + "Displaying notes found at file offset 0x%llx with length 0x%llx:\n", ph.offset, ph.filesz); show_notes(ph.offset, ph.filesz); } diff --git a/toys/pending/sh.c b/toys/pending/sh.c index 7e6f827d..9f686932 100644 --- a/toys/pending/sh.c +++ b/toys/pending/sh.c @@ -142,6 +142,7 @@ static void syntax_err(char *msg, ...) { va_list va; +// TODO rethink syntax errordom va_start(va, msg); verror_msg(msg, 0, va); va_end(va); @@ -579,7 +580,7 @@ int save_redirect(int **rd, int from, int to) if (hfd != dup2(to, hfd)) hfd = -1; else fcntl(hfd, F_SETFD, FD_CLOEXEC); -if (BUGBUG) dprintf(255, "redir from=%d to=%d hfd=%d\n", from, to, hfd); +if (BUGBUG) dprintf(255, "%d redir from=%d to=%d hfd=%d\n", getpid(), from, to, hfd); // dup "to" if (from != -1 && to != dup2(from, to)) { if (hfd != -1) close(hfd); @@ -626,7 +627,7 @@ static void subshell_callback(void) // TODO avoid prototype static int sh_run(char *new); -// Pass environment and command string to child shell +// Pass environment and command string to child shell, return PID of child static int run_subshell(char *str, int len) { pid_t pid; @@ -636,7 +637,7 @@ static int run_subshell(char *str, int len) char *s; if ((pid = fork())<0) perror_msg("fork"); - else if (pid>0) { + else if (!pid) { s = xstrndup(str, len); sh_run(s); free(s); @@ -667,6 +668,33 @@ static int run_subshell(char *str, int len) return pid; } +// turn a parsed pipeline back into a string. +static char *pl2str(struct sh_pipeline *pl) +{ + struct sh_pipeline *end = 0; + int level = 0, len = 0, i, j; + char *s, *ss, *sss; + + // measure, then allocate + for (j = 0; ; j++) for (end = pl; end; end = end->next) { + if (end->type == 1) level++; + else if (end->type == 3 && --level<0) break; + + for (i = 0; i<pl->arg->c; i++) + if (j) ss += sprintf(ss, "%s ", pl->arg->v[i]); + else len += strlen(pl->arg->v[i])+1; + + sss = pl->arg->v[pl->arg->c]; + if (!sss) sss = ";"; + if (j) ss = stpcpy(ss, sss); + else len += strlen(sss); + +// TODO add HERE documents back in + if (j) return s; + s = ss = xmalloc(len+1); + } +} + // Expand arguments and perform redirections. Return new process object with // expanded args. This can be called from command or block context. static struct sh_process *expand_redir(struct sh_arg *arg, int envlen, int *urd) @@ -1078,7 +1106,7 @@ struct sh_pipeline *block_end(struct sh_pipeline *pl) pl = pl->next; } - return 0; + return pl; } void free_function(struct sh_function *sp) @@ -1457,6 +1485,7 @@ void dump_filehandles(char *when) */ +// wait for every process in a pipeline to end static int wait_pipeline(struct sh_process *pp) { int rc = 0; @@ -1474,6 +1503,7 @@ static int wait_pipeline(struct sh_process *pp) return rc; } +// pipe data into and out of this segment, I.E. handle leading and trailing | static int pipe_segments(char *ctl, int *pipes, int **urd) { unredirect(*urd); @@ -1504,6 +1534,7 @@ static int pipe_segments(char *ctl, int *pipes, int **urd) return 0; } +// Handle && and || traversal in pipeline segments static struct sh_pipeline *skip_andor(int rc, struct sh_pipeline *pl) { char *ctl = pl->arg->v[pl->arg->c]; @@ -1521,9 +1552,9 @@ static struct sh_pipeline *skip_andor(int rc, struct sh_pipeline *pl) // run a parsed shell function. Handle flow control blocks and characters, // setup pipes and block redirection, break/continue, call builtins, // vfork/exec external commands. -static void run_function(struct sh_function *sp) +static void run_function(struct sh_pipeline *pl) { - struct sh_pipeline *pl = sp->pipeline, *end; + struct sh_pipeline *end; struct blockstack { struct blockstack *next; struct sh_pipeline *start, *end; @@ -1541,19 +1572,17 @@ static void run_function(struct sh_function *sp) // iterate through pipeline segments while (pl) { - char *s = *pl->arg->v, *ss = pl->arg->v[1]; -if (BUGBUG) dprintf(255, "type=%d %s %s\n", pl->type, pl->arg->v[0], pl->arg->v[pl->arg->c]); + struct sh_arg *arg = pl->arg; + char *s = *arg->v, *ss = arg->v[1], *ctl = arg->v[arg->c]; +if (BUGBUG) dprintf(255, "%d runtype=%d %s %s\n", getpid(), pl->type, s, ctl); // Is this an executable segment? if (!pl->type) { - struct sh_arg *arg = pl->arg; - char *ctl = arg->v[arg->c]; // Skip disabled block if (blk && !blk->run) { while (pl->next && !pl->next->type) pl = pl->next; continue; } - if (pipe_segments(ctl, pipes, &urd)) break; // If we just started a new pipeline, implicit parentheses (subshell) @@ -1571,7 +1600,7 @@ if (BUGBUG) dprintf(255, "type=%d %s %s\n", pl->type, pl->arg->v[0], pl->arg->v[ // How many layers to peel off? i = ss ? atol(ss) : 0; if (i<1) i = 1; - if (!blk || pl->arg->c>2 || ss[strspn(ss, "0123456789")]) { + if (!blk || arg->c>2 || ss[strspn(ss, "0123456789")]) { syntax_err("bad %s", s); break; } @@ -1605,6 +1634,7 @@ if (BUGBUG) dprintf(255, "type=%d %s %s\n", pl->type, pl->arg->v[0], pl->arg->v[ // TODO: "echo | read i" is backgroundable with ctrl-Z despite read = builtin. // probably have to inline run_command here to do that? Implicit () // also "X=42 | true; echo $X" doesn't get X. +// I.E. run_subshell() here sometimes? (But when?) dlist_add_nomalloc((void *)&pplist, (void *)run_command(arg)); } @@ -1618,10 +1648,10 @@ if (BUGBUG) dprintf(255, "type=%d %s %s\n", pl->type, pl->arg->v[0], pl->arg->v[ // Start of flow control block? } else if (pl->type == 1) { + struct sh_process *pp = 0; // are we entering this block (rather than looping back to it)? if (!blk || blk->start != pl) { - struct sh_process *pp; // If it's a nested block we're not running, skip ahead. end = block_end(pl->next); @@ -1632,7 +1662,7 @@ if (BUGBUG) dprintf(255, "type=%d %s %s\n", pl->type, pl->arg->v[0], pl->arg->v[ } // If previous piped into this block, save context until block end - if (pipe_segments(0, pipes, &urd)) break; + if (pipe_segments(end->arg->v[end->arg->c], pipes, &urd)) break; // It's a new block we're running, save context and add it to the stack. new = xzalloc(sizeof(*blk)); @@ -1649,19 +1679,20 @@ if (BUGBUG) dprintf(255, "type=%d %s %s\n", pl->type, pl->arg->v[0], pl->arg->v[ *pipes = -1; // Perform redirects listed at end of block - pp = expand_redir(blk->end->arg, 0, blk->urd); + pp = expand_redir(end->arg, 1, blk->urd); blk->urd = pp->urd; - if (pp->arg.c) perror_msg("unexpected %s", *pp->arg.v); - llist_traverse(pp->delete, free); - if (pp->arg.c) break; - free(pp); + if (pp->arg.c) { +// TODO this is a syntax_error + perror_msg("unexpected %s", *pp->arg.v); + llist_traverse(pp->delete, free); + free(pp); + break; + } } // What flow control statement is this? -// TODO ( subshell - - // if/then/elif/else/fi, while until/do/done - no special handling needed + // {/} if/then/elif/else/fi, while until/do/done - no special handling // for select/do/done if (!strcmp(s, "for") || !strcmp(s, "select")) { @@ -1677,10 +1708,33 @@ dprintf(2, "TODO skipped init for((;;)), need math parser\n"); expand_arg(&blk->farg, pl->next->arg->v[i], 0, &blk->fdelete); } else expand_arg(&blk->farg, "\"$@\"", 0, &blk->fdelete); } - pl = pl->next; - } -// TODO case/esac {/} [[/]] (/) ((/)) function/} +// TODO case/esac [[/]] (/) ((/)) function/} + +/* +TODO: a | b | c needs subshell for builtins? + - anything that can produce output + - echo declare dirs + (a; b; c) like { } but subshell + when to auto-exec? ps vs sh -c 'ps' vs sh -c '(ps)' +*/ + + // subshell + } else if (!strcmp(s, "(")) { + if (!CFG_TOYBOX_FORK) { + ss = pl2str(pl->next); + pp->pid = run_subshell(ss, strlen(ss)); + free(ss); + } else { + if (!(pp->pid = fork())) { + run_function(pl->next); + _exit(toys.exitval); + } + } + + dlist_add_nomalloc((void *)&pplist, (void *)pp); + pl = blk->end->prev; + } // gearshift from block start to block body (end of flow control test) } else if (pl->type == 2) { @@ -1705,6 +1759,9 @@ dprintf(2, "TODO skipped running for((;;)), need math parser\n"); // end of block, may have trailing redirections and/or pipe } else if (pl->type == 3) { + // if we end a block we're not in, we started in a block. + if (!blk) break; + // repeating block? if (blk->run && !strcmp(s, "done")) { pl = blk->start; @@ -1750,7 +1807,7 @@ static int sh_run(char *new) // 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); + if (!parse_line(new, &scratch)) run_function(scratch.pipeline); free_function(&scratch); rc = toys.exitval; toys.exitval = 0; @@ -1972,7 +2029,7 @@ if (BUGBUG) { int fd = open("/dev/tty", O_RDWR); dup2(fd, 255); close(fd); } if (BUGBUG) dump_state(&scratch); if (prompt != 1) { // TODO: ./blah.sh one two three: put one two three in scratch.arg - if (!prompt) run_function(&scratch); + if (!prompt) run_function(scratch.pipeline); free_function(&scratch); prompt = 0; } diff --git a/toys/pending/vi.c b/toys/pending/vi.c index 5086a0da..c6f44ef6 100644 --- a/toys/pending/vi.c +++ b/toys/pending/vi.c @@ -418,9 +418,9 @@ static int text_codepoint(char *dest, size_t offset) static size_t text_sol(size_t offset) { size_t pos; - if (!TT.filesize) return 0; + if (!TT.filesize || !offset) return 0; else if (TT.filesize <= offset) return TT.filesize-1; - else if ((pos = text_strrchr(offset, '\n')) == SIZE_MAX) return 0; + else if ((pos = text_strrchr(offset-1, '\n')) == SIZE_MAX) return 0; else if (pos < offset) return pos+1; return offset; } diff --git a/toys/posix/unlink.c b/toys/posix/unlink.c index f5faab06..017ba9d6 100644 --- a/toys/posix/unlink.c +++ b/toys/posix/unlink.c @@ -20,5 +20,5 @@ config UNLINK void unlink_main(void) { if (unlink(*toys.optargs)) - perror_exit("Couldn't unlink `%s'", *toys.optargs); + perror_exit("couldn't unlink '%s'", *toys.optargs); } |