Changeset 1770 in MondoRescue for branches/stable/mindi-busybox/coreutils/test.c
- Timestamp:
- Nov 6, 2007, 11:01:53 AM (17 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
branches/stable/mindi-busybox/coreutils/test.c
r902 r1770 13 13 * modified by Erik Andersen <andersen@codepoet.org> to be used 14 14 * in busybox. 15 * modified by Bernhard Fischer to be useable (i.e. a bit less bloaty). 15 16 * 16 17 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. … … 20 21 */ 21 22 22 #include "busybox.h" 23 #include <unistd.h> 24 #include <ctype.h> 25 #include <errno.h> 26 #include <string.h> 23 #include "libbb.h" 27 24 #include <setjmp.h> 25 26 /* This is a NOEXEC applet. Be very careful! */ 27 28 28 29 29 /* test(1) accepts the following grammar: … … 86 86 OPERAND 87 87 }; 88 88 #define is_int_op(a) (((unsigned char)((a) - INTEQ)) <= 5) 89 #define is_str_op(a) (((unsigned char)((a) - STREZ)) <= 5) 90 #define is_file_op(a) (((unsigned char)((a) - FILNT)) <= 2) 91 #define is_file_access(a) (((unsigned char)((a) - FILRD)) <= 2) 92 #define is_file_type(a) (((unsigned char)((a) - FILREG)) <= 5) 93 #define is_file_bit(a) (((unsigned char)((a) - FILSUID)) <= 2) 89 94 enum token_types { 90 95 UNOP, … … 96 101 97 102 static const struct t_op { 98 c onst char *op_text;99 shortop_num, op_type;103 char op_text[4]; 104 unsigned char op_num, op_type; 100 105 } ops[] = { 101 { 102 "-r", FILRD, UNOP}, { 103 "-w", FILWR, UNOP}, { 104 "-x", FILEX, UNOP}, { 105 "-e", FILEXIST, UNOP}, { 106 "-f", FILREG, UNOP}, { 107 "-d", FILDIR, UNOP}, { 108 "-c", FILCDEV, UNOP}, { 109 "-b", FILBDEV, UNOP}, { 110 "-p", FILFIFO, UNOP}, { 111 "-u", FILSUID, UNOP}, { 112 "-g", FILSGID, UNOP}, { 113 "-k", FILSTCK, UNOP}, { 114 "-s", FILGZ, UNOP}, { 115 "-t", FILTT, UNOP}, { 116 "-z", STREZ, UNOP}, { 117 "-n", STRNZ, UNOP}, { 118 "-h", FILSYM, UNOP}, /* for backwards compat */ 119 { 120 "-O", FILUID, UNOP}, { 121 "-G", FILGID, UNOP}, { 122 "-L", FILSYM, UNOP}, { 123 "-S", FILSOCK, UNOP}, { 124 "=", STREQ, BINOP}, { 125 "==", STREQ, BINOP}, { 126 "!=", STRNE, BINOP}, { 127 "<", STRLT, BINOP}, { 128 ">", STRGT, BINOP}, { 129 "-eq", INTEQ, BINOP}, { 130 "-ne", INTNE, BINOP}, { 131 "-ge", INTGE, BINOP}, { 132 "-gt", INTGT, BINOP}, { 133 "-le", INTLE, BINOP}, { 134 "-lt", INTLT, BINOP}, { 135 "-nt", FILNT, BINOP}, { 136 "-ot", FILOT, BINOP}, { 137 "-ef", FILEQ, BINOP}, { 138 "!", UNOT, BUNOP}, { 139 "-a", BAND, BBINOP}, { 140 "-o", BOR, BBINOP}, { 141 "(", LPAREN, PAREN}, { 142 ")", RPAREN, PAREN}, { 143 0, 0, 0} 106 { "-r", FILRD , UNOP }, 107 { "-w", FILWR , UNOP }, 108 { "-x", FILEX , UNOP }, 109 { "-e", FILEXIST, UNOP }, 110 { "-f", FILREG , UNOP }, 111 { "-d", FILDIR , UNOP }, 112 { "-c", FILCDEV , UNOP }, 113 { "-b", FILBDEV , UNOP }, 114 { "-p", FILFIFO , UNOP }, 115 { "-u", FILSUID , UNOP }, 116 { "-g", FILSGID , UNOP }, 117 { "-k", FILSTCK , UNOP }, 118 { "-s", FILGZ , UNOP }, 119 { "-t", FILTT , UNOP }, 120 { "-z", STREZ , UNOP }, 121 { "-n", STRNZ , UNOP }, 122 { "-h", FILSYM , UNOP }, /* for backwards compat */ 123 124 { "-O" , FILUID , UNOP }, 125 { "-G" , FILGID , UNOP }, 126 { "-L" , FILSYM , UNOP }, 127 { "-S" , FILSOCK, UNOP }, 128 { "=" , STREQ , BINOP }, 129 { "==" , STREQ , BINOP }, 130 { "!=" , STRNE , BINOP }, 131 { "<" , STRLT , BINOP }, 132 { ">" , STRGT , BINOP }, 133 { "-eq", INTEQ , BINOP }, 134 { "-ne", INTNE , BINOP }, 135 { "-ge", INTGE , BINOP }, 136 { "-gt", INTGT , BINOP }, 137 { "-le", INTLE , BINOP }, 138 { "-lt", INTLT , BINOP }, 139 { "-nt", FILNT , BINOP }, 140 { "-ot", FILOT , BINOP }, 141 { "-ef", FILEQ , BINOP }, 142 { "!" , UNOT , BUNOP }, 143 { "-a" , BAND , BBINOP }, 144 { "-o" , BOR , BBINOP }, 145 { "(" , LPAREN , PAREN }, 146 { ")" , RPAREN , PAREN }, 144 147 }; 145 148 146 #ifdef CONFIG_FEATURE_TEST_64 149 150 #if ENABLE_FEATURE_TEST_64 147 151 typedef int64_t arith_t; 148 152 #else … … 150 154 #endif 151 155 156 /* Cannot eliminate these static data (do the G trick) 157 * because of test_main usage from other applets */ 152 158 static char **t_wp; 153 159 static struct t_op const *t_wp_op; 154 160 static gid_t *group_array; 155 161 static int ngroups; 162 static jmp_buf leaving; 156 163 157 164 static enum token t_lex(char *s); … … 163 170 static int filstat(char *nm, enum token mode); 164 171 static arith_t getn(const char *s); 172 /* UNUSED 165 173 static int newerf(const char *f1, const char *f2); 166 174 static int olderf(const char *f1, const char *f2); 167 175 static int equalf(const char *f1, const char *f2); 176 */ 168 177 static int test_eaccess(char *path, int mode); 169 178 static int is_a_group_member(gid_t gid); 170 179 static void initialize_group_array(void); 171 180 172 static jmp_buf leaving; 173 174 int bb_test(int argc, char **argv) 181 int test_main(int argc, char **argv) 175 182 { 176 183 int res; 177 178 if (strcmp(argv[0], "[") == 0) { 179 if (strcmp(argv[--argc], "]")) { 180 bb_error_msg("missing ]"); 181 return 2; 182 } 183 argv[argc] = NULL; 184 } else if (strcmp(argv[0], "[[") == 0) { 185 if (strcmp(argv[--argc], "]]")) { 186 bb_error_msg("missing ]]"); 187 return 2; 184 const char *arg0; 185 bool _off; 186 187 arg0 = bb_basename(argv[0]); 188 if (arg0[0] == '[') { 189 --argc; 190 if (!arg0[1]) { /* "[" ? */ 191 if (NOT_LONE_CHAR(argv[argc], ']')) { 192 bb_error_msg("missing ]"); 193 return 2; 194 } 195 } else { /* assuming "[[" */ 196 if (strcmp(argv[argc], "]]") != 0) { 197 bb_error_msg("missing ]]"); 198 return 2; 199 } 188 200 } 189 201 argv[argc] = NULL; … … 202 214 * prevails... 203 215 */ 204 216 ngroups = 0; 205 217 206 218 /* Implement special cases from POSIX.2, section 4.62.4 */ 207 switch (argc) { 208 case 1: 219 if (argc == 1) 209 220 return 1; 210 case 2:221 if (argc == 2) 211 222 return *argv[1] == '\0'; 212 case 3: 213 if (argv[1][0] == '!' && argv[1][1] == '\0') { 223 //assert(argc); 224 /* remember if we saw argc==4 which wants *no* '!' test */ 225 _off = argc - 4; 226 if (_off ? 227 (LONE_CHAR(argv[1], '!')) 228 : (argv[1][0] != '!' || argv[1][1] != '\0')) 229 { 230 if (argc == 3) 214 231 return *argv[2] != '\0'; 232 233 t_lex(argv[2 + _off]); 234 if (t_wp_op && t_wp_op->op_type == BINOP) { 235 t_wp = &argv[1 + _off]; 236 return binop() == _off; 215 237 } 216 break; 217 case 4: 218 if (argv[1][0] != '!' || argv[1][1] != '\0') { 219 if (t_lex(argv[2]), t_wp_op && t_wp_op->op_type == BINOP) { 220 t_wp = &argv[1]; 221 return binop() == 0; 222 } 223 } 224 break; 225 case 5: 226 if (argv[1][0] == '!' && argv[1][1] == '\0') { 227 if (t_lex(argv[3]), t_wp_op && t_wp_op->op_type == BINOP) { 228 t_wp = &argv[2]; 229 return binop() != 0; 230 } 231 } 232 break; 233 } 234 238 } 235 239 t_wp = &argv[1]; 236 240 res = !oexpr(t_lex(*t_wp)); … … 243 247 } 244 248 249 static void syntax(const char *op, const char *msg) ATTRIBUTE_NORETURN; 245 250 static void syntax(const char *op, const char *msg) 246 251 { … … 248 253 bb_error_msg("%s: %s", op, msg); 249 254 } else { 250 bb_error_msg("%s ", msg);255 bb_error_msg("%s: %s"+4, msg); 251 256 } 252 257 longjmp(leaving, 2); … … 300 305 if (*++t_wp == NULL) 301 306 syntax(t_wp_op->op_text, "argument expected"); 302 switch (n) { 303 case STREZ: 304 return strlen(*t_wp) == 0; 305 case STRNZ: 306 return strlen(*t_wp) != 0; 307 case FILTT: 307 if (n == STREZ) 308 return t_wp[0][0] == '\0'; 309 if (n == STRNZ) 310 return t_wp[0][0] != '\0'; 311 if (n == FILTT) 308 312 return isatty(getn(*t_wp)); 309 default: 310 return filstat(*t_wp, n); 311 } 312 } 313 314 if (t_lex(t_wp[1]), t_wp_op && t_wp_op->op_type == BINOP) { 313 return filstat(*t_wp, n); 314 } 315 316 t_lex(t_wp[1]); 317 if (t_wp_op && t_wp_op->op_type == BINOP) { 315 318 return binop(); 316 319 } 317 320 318 return strlen(*t_wp) > 0;321 return t_wp[0][0] != '\0'; 319 322 } 320 323 … … 323 326 const char *opnd1, *opnd2; 324 327 struct t_op const *op; 328 arith_t val1, val2; 325 329 326 330 opnd1 = *t_wp; … … 328 332 op = t_wp_op; 329 333 330 if ((opnd2 = *++t_wp) == (char *) 0) 334 opnd2 = *++t_wp; 335 if (opnd2 == NULL) 331 336 syntax(op->op_text, "argument expected"); 332 337 333 switch (op->op_num) { 334 case STREQ: 335 return strcmp(opnd1, opnd2) == 0; 336 case STRNE: 337 return strcmp(opnd1, opnd2) != 0; 338 case STRLT: 339 return strcmp(opnd1, opnd2) < 0; 340 case STRGT: 341 return strcmp(opnd1, opnd2) > 0; 342 case INTEQ: 343 return getn(opnd1) == getn(opnd2); 344 case INTNE: 345 return getn(opnd1) != getn(opnd2); 346 case INTGE: 347 return getn(opnd1) >= getn(opnd2); 348 case INTGT: 349 return getn(opnd1) > getn(opnd2); 350 case INTLE: 351 return getn(opnd1) <= getn(opnd2); 352 case INTLT: 353 return getn(opnd1) < getn(opnd2); 354 case FILNT: 355 return newerf(opnd1, opnd2); 356 case FILOT: 357 return olderf(opnd1, opnd2); 358 case FILEQ: 359 return equalf(opnd1, opnd2); 360 } 361 /* NOTREACHED */ 362 return 1; 338 if (is_int_op(op->op_num)) { 339 val1 = getn(opnd1); 340 val2 = getn(opnd2); 341 if (op->op_num == INTEQ) 342 return val1 == val2; 343 if (op->op_num == INTNE) 344 return val1 != val2; 345 if (op->op_num == INTGE) 346 return val1 >= val2; 347 if (op->op_num == INTGT) 348 return val1 > val2; 349 if (op->op_num == INTLE) 350 return val1 <= val2; 351 if (op->op_num == INTLT) 352 return val1 < val2; 353 } 354 if (is_str_op(op->op_num)) { 355 val1 = strcmp(opnd1, opnd2); 356 if (op->op_num == STREQ) 357 return val1 == 0; 358 if (op->op_num == STRNE) 359 return val1 != 0; 360 if (op->op_num == STRLT) 361 return val1 < 0; 362 if (op->op_num == STRGT) 363 return val1 > 0; 364 } 365 /* We are sure that these three are by now the only binops we didn't check 366 * yet, so we do not check if the class is correct: 367 */ 368 /* if (is_file_op(op->op_num)) */ 369 { 370 struct stat b1, b2; 371 372 if (stat(opnd1, &b1) || stat(opnd2, &b2)) 373 return 0; /* false, since at least one stat failed */ 374 if (op->op_num == FILNT) 375 return b1.st_mtime > b2.st_mtime; 376 if (op->op_num == FILOT) 377 return b1.st_mtime < b2.st_mtime; 378 if (op->op_num == FILEQ) 379 return b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino; 380 } 381 return 1; /* NOTREACHED */ 363 382 } 364 383 … … 366 385 { 367 386 struct stat s; 368 unsigned int i;387 int i = i; /* gcc 3.x thinks it can be used uninitialized */ 369 388 370 389 if (mode == FILSYM) { … … 380 399 if (stat(nm, &s) != 0) 381 400 return 0; 382 383 switch (mode) { 384 case FILRD: 385 return test_eaccess(nm, R_OK) == 0; 386 case FILWR: 387 return test_eaccess(nm, W_OK) == 0; 388 case FILEX: 389 return test_eaccess(nm, X_OK) == 0; 390 case FILEXIST: 401 if (mode == FILEXIST) 391 402 return 1; 392 case FILREG: 393 i = S_IFREG; 394 goto filetype; 395 case FILDIR: 396 i = S_IFDIR; 397 goto filetype; 398 case FILCDEV: 399 i = S_IFCHR; 400 goto filetype; 401 case FILBDEV: 402 i = S_IFBLK; 403 goto filetype; 404 case FILFIFO: 403 if (is_file_access(mode)) { 404 if (mode == FILRD) 405 i = R_OK; 406 if (mode == FILWR) 407 i = W_OK; 408 if (mode == FILEX) 409 i = X_OK; 410 return test_eaccess(nm, i) == 0; 411 } 412 if (is_file_type(mode)) { 413 if (mode == FILREG) 414 i = S_IFREG; 415 if (mode == FILDIR) 416 i = S_IFDIR; 417 if (mode == FILCDEV) 418 i = S_IFCHR; 419 if (mode == FILBDEV) 420 i = S_IFBLK; 421 if (mode == FILFIFO) { 405 422 #ifdef S_IFIFO 406 i = S_IFIFO; 407 goto filetype; 423 i = S_IFIFO; 408 424 #else 409 return 0;425 return 0; 410 426 #endif 411 case FILSOCK: 427 } 428 if (mode == FILSOCK) { 412 429 #ifdef S_IFSOCK 413 i = S_IFSOCK; 414 goto filetype; 430 i = S_IFSOCK; 415 431 #else 416 return 0;432 return 0; 417 433 #endif 418 case FILSUID: 419 i = S_ISUID; 420 goto filebit; 421 case FILSGID: 422 i = S_ISGID; 423 goto filebit; 424 case FILSTCK: 425 i = S_ISVTX; 426 goto filebit; 427 case FILGZ: 434 } 435 filetype: 436 return ((s.st_mode & S_IFMT) == i); 437 } 438 if (is_file_bit(mode)) { 439 if (mode == FILSUID) 440 i = S_ISUID; 441 if (mode == FILSGID) 442 i = S_ISGID; 443 if (mode == FILSTCK) 444 i = S_ISVTX; 445 return ((s.st_mode & i) != 0); 446 } 447 if (mode == FILGZ) 428 448 return s.st_size > 0L; 429 case FILUID:449 if (mode == FILUID) 430 450 return s.st_uid == geteuid(); 431 case FILGID:451 if (mode == FILGID) 432 452 return s.st_gid == getegid(); 433 default: 434 return 1; 435 } 436 437 filetype: 438 return ((s.st_mode & S_IFMT) == i); 439 440 filebit: 441 return ((s.st_mode & i) != 0); 453 return 1; /* NOTREACHED */ 442 454 } 443 455 444 456 static enum token t_lex(char *s) 445 457 { 446 struct t_op const *op = ops;447 448 if (s == 0) {449 t_wp_op = (struct t_op *) 0;458 const struct t_op *op; 459 460 t_wp_op = NULL; 461 if (s == NULL) { 450 462 return EOI; 451 463 } 452 while (op->op_text) { 464 465 op = ops; 466 do { 453 467 if (strcmp(s, op->op_text) == 0) { 454 468 t_wp_op = op; … … 456 470 } 457 471 op++; 458 } 459 t_wp_op = (struct t_op *) 0; 472 } while (op < ops + ARRAY_SIZE(ops)); 473 460 474 return OPERAND; 461 475 } 462 476 463 477 /* atoi with error detection */ 478 //XXX: FIXME: duplicate of existing libbb function? 464 479 static arith_t getn(const char *s) 465 480 { 466 481 char *p; 467 #if def CONFIG_FEATURE_TEST_64482 #if ENABLE_FEATURE_TEST_64 468 483 long long r; 469 484 #else … … 472 487 473 488 errno = 0; 474 #if def CONFIG_FEATURE_TEST_64489 #if ENABLE_FEATURE_TEST_64 475 490 r = strtoll(s, &p, 10); 476 491 #else … … 487 502 } 488 503 504 /* UNUSED 489 505 static int newerf(const char *f1, const char *f2) 490 506 { … … 511 527 b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino); 512 528 } 529 */ 513 530 514 531 /* Do the same thing access(2) does, but use the effective uid and gid, … … 521 538 522 539 if (stat(path, &st) < 0) 523 return (-1);540 return -1; 524 541 525 542 if (euid == 0) { 526 543 /* Root can read or write any file. */ 527 544 if (mode != X_OK) 528 return (0);545 return 0; 529 546 530 547 /* Root can execute any file that has any one of the execute 531 548 bits set. */ 532 549 if (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) 533 return (0);550 return 0; 534 551 } 535 552 … … 540 557 541 558 if (st.st_mode & mode) 542 return (0);543 544 return (-1);559 return 0; 560 561 return -1; 545 562 } 546 563 … … 549 566 ngroups = getgroups(0, NULL); 550 567 if (ngroups > 0) { 551 group_array = xmalloc(ngroups * sizeof(gid_t)); 568 /* FIXME: ash tries so hard to not die on OOM, 569 * and we spoil it with just one xrealloc here */ 570 /* We realloc, because test_main can be entered repeatedly by shell. 571 * Testcase (ash): 'while true; do test -x some_file; done' 572 * and watch top. (some_file must have owner != you) */ 573 group_array = xrealloc(group_array, ngroups * sizeof(gid_t)); 552 574 getgroups(ngroups, group_array); 553 575 } … … 555 577 556 578 /* Return non-zero if GID is one that we have in our groups list. */ 579 //XXX: FIXME: duplicate of existing libbb function? 580 // see toplevel TODO file: 581 // possible code duplication ingroup() and is_a_group_member() 557 582 static int is_a_group_member(gid_t gid) 558 583 { 559 registerint i;584 int i; 560 585 561 586 /* Short-circuit if possible, maybe saving a call to getgroups(). */ 562 587 if (gid == getgid() || gid == getegid()) 563 return (1);588 return 1; 564 589 565 590 if (ngroups == 0) … … 569 594 for (i = 0; i < ngroups; i++) 570 595 if (gid == group_array[i]) 571 return (1); 572 573 return (0); 574 } 575 576 577 /* applet entry point */ 578 579 int test_main(int argc, char **argv) 580 { 581 exit(bb_test(argc, argv)); 582 } 583 596 return 1; 597 598 return 0; 599 }
Note:
See TracChangeset
for help on using the changeset viewer.