chown: support -H -L -P if ENABLE_DESKTOP chmod: cosmetic fixes expr: smallish help for dumb compilers
No binary (build failed)
—
(log yok)
| @@ -118,10 +118,12 @@ int chmod_main(int argc, char **argv) | |
| /* | |
| Security: chmod is too important and too subtle. | |
| This is a test script (busybox chmod versus coreutils). | |
| - | Run it in empty dir. Probably requires bash. |
| + | Run it in empty directory. |
| #!/bin/sh | |
| - | function create() { |
| + | t1="/tmp/busybox chmod" |
| + | t2="/usr/bin/chmod" |
| + | create() { |
| rm -rf $1; mkdir $1 | |
| ( | |
| cd $1 || exit 1 | |
| @@ -134,17 +136,16 @@ function create() { | |
| ln -s ../up dir/up | |
| ) | |
| } | |
| - | function tst() { |
| + | tst() { |
| (cd test1; $t1 $1) | |
| (cd test2; $t2 $1) | |
| (cd test1; ls -lR) >out1 | |
| (cd test2; ls -lR) >out2 | |
| echo "chmod $1" >out.diff | |
| if ! diff -u out1 out2 >>out.diff; then exit 1; fi | |
| - | mv out.diff out1.diff |
| + | rm out.diff |
| } | |
| - | t1="/tmp/busybox chmod" |
| - | t2="/usr/bin/chmod" |
| + | echo "If script produced 'out.diff' file, then at least one testcase failed" |
| create test1; create test2 | |
| tst "a+w file" | |
| tst "a-w dir" | |
| @@ -17,25 +17,31 @@ static struct bb_uidgid_t ugid = { -1, -1 }; | |
| static int (*chown_func)(const char *, uid_t, gid_t) = chown; | |
| + | #define OPT_STR ("Rh" USE_DESKTOP("vcfLHP")) |
| + | #define BIT_RECURSE 1 |
| + | #define BIT_NODEREF 2 |
| + | #define BIT_TRAVERSE 0x20 |
| + | #define BIT_TRAVERSETOP (0x20|0x40) |
| #define OPT_RECURSE (option_mask32 & 1) | |
| #define OPT_NODEREF (option_mask32 & 2) | |
| - | #define OPT_VERBOSE (USE_DESKTOP(option_mask32 & 4) SKIP_DESKTOP(0)) |
| - | #define OPT_CHANGED (USE_DESKTOP(option_mask32 & 8) SKIP_DESKTOP(0)) |
| + | #define OPT_VERBOSE (USE_DESKTOP(option_mask32 & 0x04) SKIP_DESKTOP(0)) |
| + | #define OPT_CHANGED (USE_DESKTOP(option_mask32 & 0x08) SKIP_DESKTOP(0)) |
| #define OPT_QUIET (USE_DESKTOP(option_mask32 & 0x10) SKIP_DESKTOP(0)) | |
| - | #define OPT_STR ("Rh" USE_DESKTOP("vcf")) |
| - | |
| - | /* TODO: |
| - | * -H if a command line argument is a symbolic link to a directory, traverse it |
| + | /* POSIX options |
| * -L traverse every symbolic link to a directory encountered | |
| + | * -H if a command line argument is a symbolic link to a directory, traverse it |
| * -P do not traverse any symbolic links (default) | |
| - | */ |
| + | * We do not conform to the following: |
| + | * "Specifying more than one of -H, -L, and -P is not an error. |
| + | * The last option specified shall determine the behavior of the utility." */ |
| + | /* -L */ |
| + | #define OPT_TRAVERSE (USE_DESKTOP(option_mask32 & BIT_TRAVERSE) SKIP_DESKTOP(0)) |
| + | /* -H or -L */ |
| + | #define OPT_TRAVERSETOP (USE_DESKTOP(option_mask32 & BIT_TRAVERSETOP) SKIP_DESKTOP(0)) |
| static int fileAction(const char *fileName, struct stat *statbuf, | |
| void ATTRIBUTE_UNUSED *junk, int depth) | |
| { | |
| - | // TODO: -H/-L/-P |
| - | // if (depth ... && S_ISLNK(statbuf->st_mode)) .... |
| - | |
| if (!chown_func(fileName, | |
| (ugid.uid == (uid_t)-1) ? statbuf->st_uid : ugid.uid, | |
| (ugid.gid == (gid_t)-1) ? statbuf->st_gid : ugid.gid) | |
| @@ -62,16 +68,31 @@ int chown_main(int argc, char **argv) | |
| getopt32(argc, argv, OPT_STR); | |
| argv += optind; | |
| - | if (OPT_NODEREF) chown_func = lchown; |
| + | /* This matches coreutils behavior (almost - see below) */ |
| + | if (OPT_NODEREF |
| + | /* || (OPT_RECURSE && !OPT_TRAVERSETOP): */ |
| + | USE_DESKTOP( || (option_mask32 & (BIT_RECURSE|BIT_TRAVERSETOP)) == BIT_RECURSE) |
| + | ) { |
| + | chown_func = lchown; |
| + | } |
| parse_chown_usergroup_or_die(&ugid, argv[0]); | |
| /* Ok, ready to do the deed now */ | |
| argv++; | |
| do { | |
| - | if (!recursive_action(*argv, |
| - | OPT_RECURSE, // recurse |
| - | FALSE, // follow links: TODO: -H/-L/-P |
| + | char *arg = *argv; |
| + | |
| + | if (OPT_TRAVERSETOP) { |
| + | /* resolves symlink (even recursive) */ |
| + | arg = xmalloc_realpath(arg); |
| + | if (!arg) |
| + | continue; |
| + | } |
| + | |
| + | if (!recursive_action(arg, |
| + | OPT_RECURSE, // recurse |
| + | OPT_TRAVERSE, // follow links if -L |
| FALSE, // depth first | |
| fileAction, // file action | |
| fileAction, // dir action | |
| @@ -80,7 +101,66 @@ int chown_main(int argc, char **argv) | |
| ) { | |
| retval = EXIT_FAILURE; | |
| } | |
| + | |
| + | if (OPT_TRAVERSETOP) |
| + | free(arg); |
| } while (*++argv); | |
| return retval; | |
| } | |
| + | |
| + | /* |
| + | Testcase. Run in empty directory. |
| + | |
| + | #!/bin/sh |
| + | t1="/tmp/busybox chown" |
| + | t2="/usr/bin/chown" |
| + | create() { |
| + | rm -rf $1; mkdir $1 |
| + | ( |
| + | cd $1 || exit 1 |
| + | mkdir dir dir2 |
| + | >up |
| + | >file |
| + | >dir/file |
| + | >dir2/file |
| + | ln -s dir linkdir |
| + | ln -s file linkfile |
| + | ln -s ../up dir/linkup |
| + | ln -s ../dir2 dir/linkupdir2 |
| + | ) |
| + | chown -R 0:0 $1 |
| + | } |
| + | tst() { |
| + | create test1 |
| + | create test2 |
| + | (cd test1; $t1 $1) |
| + | (cd test2; $t2 $1) |
| + | (cd test1; ls -lnR) >out1 |
| + | (cd test2; ls -lnR) >out2 |
| + | echo "chown $1" >out.diff |
| + | if ! diff -u out1 out2 >>out.diff; then exit 1; fi |
| + | rm out.diff |
| + | } |
| + | tst_for_each() { |
| + | tst "$1 1:1 file" |
| + | tst "$1 1:1 dir" |
| + | tst "$1 1:1 linkdir" |
| + | tst "$1 1:1 linkfile" |
| + | } |
| + | echo "If script produced 'out.diff' file, then at least one testcase failed" |
| + | # These match coreutils 6.8: |
| + | tst_for_each "" |
| + | tst_for_each "-R" |
| + | tst_for_each "-RP" |
| + | tst_for_each "-RL" |
| + | tst_for_each "-RH" |
| + | tst_for_each "-h" |
| + | tst_for_each "-hR" |
| + | tst_for_each "-hRP" |
| + | # Below: with "chown linkdir" coreutils 6.8 will chown linkdir _target_, |
| + | # we lchown _the link_. I believe we are "more correct". |
| + | #tst_for_each "-hRL" |
| + | #tst_for_each "-hRH" |
| + | |
| + | */ |
| @@ -136,8 +136,8 @@ static int null(VALUE * v) | |
| { | |
| if (v->type == integer) | |
| return v->u.i == 0; | |
| - | else /* string: */ |
| - | return v->u.s[0] == '\0' || LONE_CHAR(v->u.s, '0'); |
| + | /* string: */ |
| + | return v->u.s[0] == '\0' || LONE_CHAR(v->u.s, '0'); |
| } | |
| /* Coerce V to a string value (can't fail). */ | |
| @@ -194,16 +194,16 @@ static int cmp_common(VALUE * l, VALUE * r, int op) | |
| cmpval = l->u.i - r->u.i; | |
| if (op == '<') | |
| return cmpval < 0; | |
| - | else if (op == ('L' + 'E')) |
| + | if (op == ('L' + 'E')) |
| return cmpval <= 0; | |
| - | else if (op == '=') |
| + | if (op == '=') |
| return cmpval == 0; | |
| - | else if (op == '!') |
| + | if (op == '!') |
| return cmpval != 0; | |
| - | else if (op == '>') |
| + | if (op == '>') |
| return cmpval > 0; | |
| - | else /* >= */ |
| - | return cmpval >= 0; |
| + | /* >= */ |
| + | return cmpval >= 0; |
| } | |
| /* The arithmetic operator handling functions. */ | |