aboutsummaryrefslogtreecommitdiff
path: root/coreutils
diff options
context:
space:
mode:
Diffstat (limited to 'coreutils')
-rw-r--r--coreutils/test.c91
1 files changed, 47 insertions, 44 deletions
diff --git a/coreutils/test.c b/coreutils/test.c
index 008d90b25..b63e33cc0 100644
--- a/coreutils/test.c
+++ b/coreutils/test.c
@@ -426,8 +426,7 @@ struct test_statics {
426 /* set only by check_operator(), either to bogus struct 426 /* set only by check_operator(), either to bogus struct
427 * or points to matching operator_t struct. Never NULL. */ 427 * or points to matching operator_t struct. Never NULL. */
428 const struct operator_t *last_operator; 428 const struct operator_t *last_operator;
429 gid_t *group_array; 429 struct cached_groupinfo *groupinfo;
430 int ngroups;
431#if BASH_TEST2 430#if BASH_TEST2
432 bool bash_test2; 431 bool bash_test2;
433#endif 432#endif
@@ -440,8 +439,7 @@ extern struct test_statics *BB_GLOBAL_CONST test_ptr_to_statics;
440#define S (*test_ptr_to_statics) 439#define S (*test_ptr_to_statics)
441#define args (S.args ) 440#define args (S.args )
442#define last_operator (S.last_operator) 441#define last_operator (S.last_operator)
443#define group_array (S.group_array ) 442#define groupinfo (S.groupinfo )
444#define ngroups (S.ngroups )
445#define bash_test2 (S.bash_test2 ) 443#define bash_test2 (S.bash_test2 )
446#define leaving (S.leaving ) 444#define leaving (S.leaving )
447 445
@@ -449,7 +447,6 @@ extern struct test_statics *BB_GLOBAL_CONST test_ptr_to_statics;
449 XZALLOC_CONST_PTR(&test_ptr_to_statics, sizeof(S)); \ 447 XZALLOC_CONST_PTR(&test_ptr_to_statics, sizeof(S)); \
450} while (0) 448} while (0)
451#define DEINIT_S() do { \ 449#define DEINIT_S() do { \
452 free(group_array); \
453 free(test_ptr_to_statics); \ 450 free(test_ptr_to_statics); \
454} while (0) 451} while (0)
455 452
@@ -637,62 +634,52 @@ static int binop(void)
637 /*return 1; - NOTREACHED */ 634 /*return 1; - NOTREACHED */
638} 635}
639 636
640static void initialize_group_array(void)
641{
642 group_array = bb_getgroups(&ngroups, NULL);
643}
644
645/* Return non-zero if GID is one that we have in our groups list. */ 637/* Return non-zero if GID is one that we have in our groups list. */
646//XXX: FIXME: duplicate of existing libbb function?
647// see toplevel TODO file:
648// possible code duplication ingroup() and is_a_group_member()
649static int is_a_group_member(gid_t gid) 638static int is_a_group_member(gid_t gid)
650{ 639{
651 int i;
652
653 /* Short-circuit if possible, maybe saving a call to getgroups(). */ 640 /* Short-circuit if possible, maybe saving a call to getgroups(). */
654 if (gid == getgid() || gid == getegid()) 641 if (gid == get_cached_egid(&groupinfo->egid))
655 return 1; 642 return 1;
656 643
657 if (ngroups == 0) 644 return is_in_supplementary_groups(groupinfo, gid);
658 initialize_group_array();
659
660 /* Search through the list looking for GID. */
661 for (i = 0; i < ngroups; i++)
662 if (gid == group_array[i])
663 return 1;
664
665 return 0;
666} 645}
667 646
668 647/*
669/* Do the same thing access(2) does, but use the effective uid and gid, 648 * Similar to what access(2) does, but uses the effective uid and gid.
670 and don't make the mistake of telling root that any file is 649 * Doesn't make the mistake of telling root that any file is executable.
671 executable. */ 650 * Returns non-zero if the file is accessible.
672static int test_eaccess(struct stat *st, int mode) 651 */
652static int test_st_mode(struct stat *st, int mode)
673{ 653{
674 unsigned int euid = geteuid(); 654 enum { ANY_IX = S_IXUSR | S_IXGRP | S_IXOTH };
655 unsigned euid;
656
657 if (mode == X_OK) {
658 /* Do we already know with no extra syscalls? */
659 //if (!S_ISREG(st->st_mode))
660 // return 0; /* not a regular file */
661 // ^^^ bash 5.2.15 "test -x" does not check this!
662 if ((st->st_mode & ANY_IX) == 0)
663 return 0; /* no one can execute */
664 if ((st->st_mode & ANY_IX) == ANY_IX)
665 return 1; /* anyone can execute */
666 }
675 667
668 euid = get_cached_euid(&groupinfo->euid);
676 if (euid == 0) { 669 if (euid == 0) {
677 /* Root can read or write any file. */ 670 /* Root can read or write any file. */
678 if (mode != X_OK) 671 if (mode != X_OK)
679 return 0; 672 return 1;
680 673
681 /* Root can execute any file that has any one of the execute 674 /* Root can execute any file that has any one of the execute
682 * bits set. */ 675 * bits set. */
683 if (st->st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) 676 mode = S_IXUSR | S_IXGRP | S_IXOTH;
684 return 0; 677 } else if (st->st_uid == euid) /* owner */
685 }
686
687 if (st->st_uid == euid) /* owner */
688 mode <<= 6; 678 mode <<= 6;
689 else if (is_a_group_member(st->st_gid)) 679 else if (is_a_group_member(st->st_gid))
690 mode <<= 3; 680 mode <<= 3;
691 681
692 if (st->st_mode & mode) 682 return st->st_mode & mode;
693 return 0;
694
695 return -1;
696} 683}
697 684
698 685
@@ -722,7 +709,7 @@ static int filstat(char *nm, enum token mode)
722 i = W_OK; 709 i = W_OK;
723 if (mode == FILEX) 710 if (mode == FILEX)
724 i = X_OK; 711 i = X_OK;
725 return test_eaccess(&s, i) == 0; 712 return test_st_mode(&s, i);
726 } 713 }
727 if (is_file_type(mode)) { 714 if (is_file_type(mode)) {
728 if (mode == FILREG) 715 if (mode == FILREG)
@@ -760,7 +747,7 @@ static int filstat(char *nm, enum token mode)
760 return ((s.st_mode & i) != 0); 747 return ((s.st_mode & i) != 0);
761 } 748 }
762 if (mode == FILGZ) 749 if (mode == FILGZ)
763 return s.st_size > 0L; 750 return s.st_size != 0L; /* shorter than "> 0" test */
764 if (mode == FILUID) 751 if (mode == FILUID)
765 return s.st_uid == geteuid(); 752 return s.st_uid == geteuid();
766 if (mode == FILGID) 753 if (mode == FILGID)
@@ -891,7 +878,7 @@ static number_t primary(enum token n)
891} 878}
892 879
893 880
894int test_main(int argc, char **argv) 881int FAST_FUNC test_main2(struct cached_groupinfo *pgroupinfo, int argc, char **argv)
895{ 882{
896 int res; 883 int res;
897 const char *arg0; 884 const char *arg0;
@@ -924,6 +911,7 @@ int test_main(int argc, char **argv)
924 911
925 /* We must do DEINIT_S() prior to returning */ 912 /* We must do DEINIT_S() prior to returning */
926 INIT_S(); 913 INIT_S();
914 groupinfo = pgroupinfo;
927 915
928#if BASH_TEST2 916#if BASH_TEST2
929 bash_test2 = bt2; 917 bash_test2 = bt2;
@@ -1026,3 +1014,18 @@ int test_main(int argc, char **argv)
1026 DEINIT_S(); 1014 DEINIT_S();
1027 return res; 1015 return res;
1028} 1016}
1017
1018int test_main(int argc, char **argv)
1019{
1020 struct cached_groupinfo info;
1021 int r;
1022
1023 info.euid = -1;
1024 info.egid = -1;
1025 info.ngroups = 0;
1026 info.supplementary_array = NULL;
1027 r = test_main2(&info, argc, argv);
1028 free(info.supplementary_array);
1029
1030 return r;
1031}