test.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565
  1. /* vi: set sw=4 ts=4: */
  2. /*
  3. * test implementation for busybox
  4. *
  5. * Copyright (c) by a whole pile of folks:
  6. *
  7. * test(1); version 7-like -- author Erik Baalbergen
  8. * modified by Eric Gisin to be used as built-in.
  9. * modified by Arnold Robbins to add SVR3 compatibility
  10. * (-x -c -b -p -u -g -k) plus Korn's -L -nt -ot -ef and new -S (socket).
  11. * modified by J.T. Conklin for NetBSD.
  12. * modified by Herbert Xu to be used as built-in in ash.
  13. * modified by Erik Andersen <andersen@codepoet.org> to be used
  14. * in busybox.
  15. *
  16. * This program is free software; you can redistribute it and/or modify
  17. * it under the terms of the GNU General Public License as published by
  18. * the Free Software Foundation; either version 2 of the License, or
  19. * (at your option) any later version.
  20. *
  21. * This program is distributed in the hope that it will be useful,
  22. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  23. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  24. * General Public License for more details.
  25. *
  26. * You should have received a copy of the GNU General Public License
  27. * along with this program; if not, write to the Free Software
  28. * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  29. *
  30. * Original copyright notice states:
  31. * "This program is in the Public Domain."
  32. */
  33. #include <sys/types.h>
  34. #include <unistd.h>
  35. #include <ctype.h>
  36. #include <errno.h>
  37. #include <stdlib.h>
  38. #include <string.h>
  39. #include "busybox.h"
  40. /* test(1) accepts the following grammar:
  41. oexpr ::= aexpr | aexpr "-o" oexpr ;
  42. aexpr ::= nexpr | nexpr "-a" aexpr ;
  43. nexpr ::= primary | "!" primary
  44. primary ::= unary-operator operand
  45. | operand binary-operator operand
  46. | operand
  47. | "(" oexpr ")"
  48. ;
  49. unary-operator ::= "-r"|"-w"|"-x"|"-f"|"-d"|"-c"|"-b"|"-p"|
  50. "-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"|"-L"|"-S";
  51. binary-operator ::= "="|"=="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"|
  52. "-nt"|"-ot"|"-ef";
  53. operand ::= <any legal UNIX file name>
  54. */
  55. enum token {
  56. EOI,
  57. FILRD,
  58. FILWR,
  59. FILEX,
  60. FILEXIST,
  61. FILREG,
  62. FILDIR,
  63. FILCDEV,
  64. FILBDEV,
  65. FILFIFO,
  66. FILSOCK,
  67. FILSYM,
  68. FILGZ,
  69. FILTT,
  70. FILSUID,
  71. FILSGID,
  72. FILSTCK,
  73. FILNT,
  74. FILOT,
  75. FILEQ,
  76. FILUID,
  77. FILGID,
  78. STREZ,
  79. STRNZ,
  80. STREQ,
  81. STRNE,
  82. STRLT,
  83. STRGT,
  84. INTEQ,
  85. INTNE,
  86. INTGE,
  87. INTGT,
  88. INTLE,
  89. INTLT,
  90. UNOT,
  91. BAND,
  92. BOR,
  93. LPAREN,
  94. RPAREN,
  95. OPERAND
  96. };
  97. enum token_types {
  98. UNOP,
  99. BINOP,
  100. BUNOP,
  101. BBINOP,
  102. PAREN
  103. };
  104. static const struct t_op {
  105. const char *op_text;
  106. short op_num, op_type;
  107. } ops[] = {
  108. {
  109. "-r", FILRD, UNOP}, {
  110. "-w", FILWR, UNOP}, {
  111. "-x", FILEX, UNOP}, {
  112. "-e", FILEXIST, UNOP}, {
  113. "-f", FILREG, UNOP}, {
  114. "-d", FILDIR, UNOP}, {
  115. "-c", FILCDEV, UNOP}, {
  116. "-b", FILBDEV, UNOP}, {
  117. "-p", FILFIFO, UNOP}, {
  118. "-u", FILSUID, UNOP}, {
  119. "-g", FILSGID, UNOP}, {
  120. "-k", FILSTCK, UNOP}, {
  121. "-s", FILGZ, UNOP}, {
  122. "-t", FILTT, UNOP}, {
  123. "-z", STREZ, UNOP}, {
  124. "-n", STRNZ, UNOP}, {
  125. "-h", FILSYM, UNOP}, /* for backwards compat */
  126. {
  127. "-O", FILUID, UNOP}, {
  128. "-G", FILGID, UNOP}, {
  129. "-L", FILSYM, UNOP}, {
  130. "-S", FILSOCK, UNOP}, {
  131. "=", STREQ, BINOP}, {
  132. "==", STREQ, BINOP}, {
  133. "!=", STRNE, BINOP}, {
  134. "<", STRLT, BINOP}, {
  135. ">", STRGT, BINOP}, {
  136. "-eq", INTEQ, BINOP}, {
  137. "-ne", INTNE, BINOP}, {
  138. "-ge", INTGE, BINOP}, {
  139. "-gt", INTGT, BINOP}, {
  140. "-le", INTLE, BINOP}, {
  141. "-lt", INTLT, BINOP}, {
  142. "-nt", FILNT, BINOP}, {
  143. "-ot", FILOT, BINOP}, {
  144. "-ef", FILEQ, BINOP}, {
  145. "!", UNOT, BUNOP}, {
  146. "-a", BAND, BBINOP}, {
  147. "-o", BOR, BBINOP}, {
  148. "(", LPAREN, PAREN}, {
  149. ")", RPAREN, PAREN}, {
  150. 0, 0, 0}
  151. };
  152. #ifdef CONFIG_FEATURE_TEST_64
  153. typedef int64_t arith_t;
  154. #else
  155. typedef int arith_t;
  156. #endif
  157. static char **t_wp;
  158. static struct t_op const *t_wp_op;
  159. static gid_t *group_array = NULL;
  160. static int ngroups;
  161. static enum token t_lex(char *s);
  162. static arith_t oexpr(enum token n);
  163. static arith_t aexpr(enum token n);
  164. static arith_t nexpr(enum token n);
  165. static int binop(void);
  166. static arith_t primary(enum token n);
  167. static int filstat(char *nm, enum token mode);
  168. static arith_t getn(const char *s);
  169. static int newerf(const char *f1, const char *f2);
  170. static int olderf(const char *f1, const char *f2);
  171. static int equalf(const char *f1, const char *f2);
  172. static void syntax(const char *op, const char *msg);
  173. static int test_eaccess(char *path, int mode);
  174. static int is_a_group_member(gid_t gid);
  175. static void initialize_group_array(void);
  176. extern int test_main(int argc, char **argv)
  177. {
  178. int res;
  179. if (strcmp(bb_applet_name, "[") == 0) {
  180. if (strcmp(argv[--argc], "]"))
  181. bb_error_msg_and_die("missing ]");
  182. argv[argc] = NULL;
  183. }
  184. if (strcmp(bb_applet_name, "[[") == 0) {
  185. if (strcmp(argv[--argc], "]]"))
  186. bb_error_msg_and_die("missing ]]");
  187. argv[argc] = NULL;
  188. }
  189. /* Implement special cases from POSIX.2, section 4.62.4 */
  190. switch (argc) {
  191. case 1:
  192. exit(1);
  193. case 2:
  194. exit(*argv[1] == '\0');
  195. case 3:
  196. if (argv[1][0] == '!' && argv[1][1] == '\0') {
  197. exit(!(*argv[2] == '\0'));
  198. }
  199. break;
  200. case 4:
  201. if (argv[1][0] != '!' || argv[1][1] != '\0') {
  202. if (t_lex(argv[2]), t_wp_op && t_wp_op->op_type == BINOP) {
  203. t_wp = &argv[1];
  204. exit(binop() == 0);
  205. }
  206. }
  207. break;
  208. case 5:
  209. if (argv[1][0] == '!' && argv[1][1] == '\0') {
  210. if (t_lex(argv[3]), t_wp_op && t_wp_op->op_type == BINOP) {
  211. t_wp = &argv[2];
  212. exit(!(binop() == 0));
  213. }
  214. }
  215. break;
  216. }
  217. t_wp = &argv[1];
  218. res = !oexpr(t_lex(*t_wp));
  219. if (*t_wp != NULL && *++t_wp != NULL)
  220. syntax(*t_wp, "unknown operand");
  221. return (res);
  222. }
  223. static void syntax(const char *op, const char *msg)
  224. {
  225. if (op && *op) {
  226. bb_error_msg_and_die("%s: %s", op, msg);
  227. } else {
  228. bb_error_msg_and_die("%s", msg);
  229. }
  230. }
  231. static arith_t oexpr(enum token n)
  232. {
  233. arith_t res;
  234. res = aexpr(n);
  235. if (t_lex(*++t_wp) == BOR) {
  236. return oexpr(t_lex(*++t_wp)) || res;
  237. }
  238. t_wp--;
  239. return res;
  240. }
  241. static arith_t aexpr(enum token n)
  242. {
  243. arith_t res;
  244. res = nexpr(n);
  245. if (t_lex(*++t_wp) == BAND)
  246. return aexpr(t_lex(*++t_wp)) && res;
  247. t_wp--;
  248. return res;
  249. }
  250. static arith_t nexpr(enum token n)
  251. {
  252. if (n == UNOT)
  253. return !nexpr(t_lex(*++t_wp));
  254. return primary(n);
  255. }
  256. static arith_t primary(enum token n)
  257. {
  258. arith_t res;
  259. if (n == EOI) {
  260. syntax(NULL, "argument expected");
  261. }
  262. if (n == LPAREN) {
  263. res = oexpr(t_lex(*++t_wp));
  264. if (t_lex(*++t_wp) != RPAREN)
  265. syntax(NULL, "closing paren expected");
  266. return res;
  267. }
  268. if (t_wp_op && t_wp_op->op_type == UNOP) {
  269. /* unary expression */
  270. if (*++t_wp == NULL)
  271. syntax(t_wp_op->op_text, "argument expected");
  272. switch (n) {
  273. case STREZ:
  274. return strlen(*t_wp) == 0;
  275. case STRNZ:
  276. return strlen(*t_wp) != 0;
  277. case FILTT:
  278. return isatty(getn(*t_wp));
  279. default:
  280. return filstat(*t_wp, n);
  281. }
  282. }
  283. if (t_lex(t_wp[1]), t_wp_op && t_wp_op->op_type == BINOP) {
  284. return binop();
  285. }
  286. return strlen(*t_wp) > 0;
  287. }
  288. static int binop(void)
  289. {
  290. const char *opnd1, *opnd2;
  291. struct t_op const *op;
  292. opnd1 = *t_wp;
  293. (void) t_lex(*++t_wp);
  294. op = t_wp_op;
  295. if ((opnd2 = *++t_wp) == (char *) 0)
  296. syntax(op->op_text, "argument expected");
  297. switch (op->op_num) {
  298. case STREQ:
  299. return strcmp(opnd1, opnd2) == 0;
  300. case STRNE:
  301. return strcmp(opnd1, opnd2) != 0;
  302. case STRLT:
  303. return strcmp(opnd1, opnd2) < 0;
  304. case STRGT:
  305. return strcmp(opnd1, opnd2) > 0;
  306. case INTEQ:
  307. return getn(opnd1) == getn(opnd2);
  308. case INTNE:
  309. return getn(opnd1) != getn(opnd2);
  310. case INTGE:
  311. return getn(opnd1) >= getn(opnd2);
  312. case INTGT:
  313. return getn(opnd1) > getn(opnd2);
  314. case INTLE:
  315. return getn(opnd1) <= getn(opnd2);
  316. case INTLT:
  317. return getn(opnd1) < getn(opnd2);
  318. case FILNT:
  319. return newerf(opnd1, opnd2);
  320. case FILOT:
  321. return olderf(opnd1, opnd2);
  322. case FILEQ:
  323. return equalf(opnd1, opnd2);
  324. }
  325. /* NOTREACHED */
  326. return 1;
  327. }
  328. static int filstat(char *nm, enum token mode)
  329. {
  330. struct stat s;
  331. unsigned int i;
  332. if (mode == FILSYM) {
  333. #ifdef S_IFLNK
  334. if (lstat(nm, &s) == 0) {
  335. i = S_IFLNK;
  336. goto filetype;
  337. }
  338. #endif
  339. return 0;
  340. }
  341. if (stat(nm, &s) != 0)
  342. return 0;
  343. switch (mode) {
  344. case FILRD:
  345. return test_eaccess(nm, R_OK) == 0;
  346. case FILWR:
  347. return test_eaccess(nm, W_OK) == 0;
  348. case FILEX:
  349. return test_eaccess(nm, X_OK) == 0;
  350. case FILEXIST:
  351. return 1;
  352. case FILREG:
  353. i = S_IFREG;
  354. goto filetype;
  355. case FILDIR:
  356. i = S_IFDIR;
  357. goto filetype;
  358. case FILCDEV:
  359. i = S_IFCHR;
  360. goto filetype;
  361. case FILBDEV:
  362. i = S_IFBLK;
  363. goto filetype;
  364. case FILFIFO:
  365. #ifdef S_IFIFO
  366. i = S_IFIFO;
  367. goto filetype;
  368. #else
  369. return 0;
  370. #endif
  371. case FILSOCK:
  372. #ifdef S_IFSOCK
  373. i = S_IFSOCK;
  374. goto filetype;
  375. #else
  376. return 0;
  377. #endif
  378. case FILSUID:
  379. i = S_ISUID;
  380. goto filebit;
  381. case FILSGID:
  382. i = S_ISGID;
  383. goto filebit;
  384. case FILSTCK:
  385. i = S_ISVTX;
  386. goto filebit;
  387. case FILGZ:
  388. return s.st_size > 0L;
  389. case FILUID:
  390. return s.st_uid == geteuid();
  391. case FILGID:
  392. return s.st_gid == getegid();
  393. default:
  394. return 1;
  395. }
  396. filetype:
  397. return ((s.st_mode & S_IFMT) == i);
  398. filebit:
  399. return ((s.st_mode & i) != 0);
  400. }
  401. static enum token t_lex(char *s)
  402. {
  403. struct t_op const *op = ops;
  404. if (s == 0) {
  405. t_wp_op = (struct t_op *) 0;
  406. return EOI;
  407. }
  408. while (op->op_text) {
  409. if (strcmp(s, op->op_text) == 0) {
  410. t_wp_op = op;
  411. return op->op_num;
  412. }
  413. op++;
  414. }
  415. t_wp_op = (struct t_op *) 0;
  416. return OPERAND;
  417. }
  418. /* atoi with error detection */
  419. static arith_t getn(const char *s)
  420. {
  421. char *p;
  422. #ifdef CONFIG_FEATURE_TEST_64
  423. long long r;
  424. #else
  425. long r;
  426. #endif
  427. errno = 0;
  428. #ifdef CONFIG_FEATURE_TEST_64
  429. r = strtoll(s, &p, 10);
  430. #else
  431. r = strtol(s, &p, 10);
  432. #endif
  433. if (errno != 0)
  434. bb_error_msg_and_die("%s: out of range", s);
  435. /* p = bb_skip_whitespace(p); avoid const warning */
  436. if (*(bb_skip_whitespace(p)))
  437. bb_error_msg_and_die("%s: bad number", s);
  438. return r;
  439. }
  440. static int newerf(const char *f1, const char *f2)
  441. {
  442. struct stat b1, b2;
  443. return (stat(f1, &b1) == 0 &&
  444. stat(f2, &b2) == 0 && b1.st_mtime > b2.st_mtime);
  445. }
  446. static int olderf(const char *f1, const char *f2)
  447. {
  448. struct stat b1, b2;
  449. return (stat(f1, &b1) == 0 &&
  450. stat(f2, &b2) == 0 && b1.st_mtime < b2.st_mtime);
  451. }
  452. static int equalf(const char *f1, const char *f2)
  453. {
  454. struct stat b1, b2;
  455. return (stat(f1, &b1) == 0 &&
  456. stat(f2, &b2) == 0 &&
  457. b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino);
  458. }
  459. /* Do the same thing access(2) does, but use the effective uid and gid,
  460. and don't make the mistake of telling root that any file is
  461. executable. */
  462. static int test_eaccess(char *path, int mode)
  463. {
  464. struct stat st;
  465. unsigned int euid = geteuid();
  466. if (stat(path, &st) < 0)
  467. return (-1);
  468. if (euid == 0) {
  469. /* Root can read or write any file. */
  470. if (mode != X_OK)
  471. return (0);
  472. /* Root can execute any file that has any one of the execute
  473. bits set. */
  474. if (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))
  475. return (0);
  476. }
  477. if (st.st_uid == euid) /* owner */
  478. mode <<= 6;
  479. else if (is_a_group_member(st.st_gid))
  480. mode <<= 3;
  481. if (st.st_mode & mode)
  482. return (0);
  483. return (-1);
  484. }
  485. static void initialize_group_array(void)
  486. {
  487. ngroups = getgroups(0, NULL);
  488. group_array = xrealloc(group_array, ngroups * sizeof(gid_t));
  489. getgroups(ngroups, group_array);
  490. }
  491. /* Return non-zero if GID is one that we have in our groups list. */
  492. static int is_a_group_member(gid_t gid)
  493. {
  494. register int i;
  495. /* Short-circuit if possible, maybe saving a call to getgroups(). */
  496. if (gid == getgid() || gid == getegid())
  497. return (1);
  498. if (ngroups == 0)
  499. initialize_group_array();
  500. /* Search through the list looking for GID. */
  501. for (i = 0; i < ngroups; i++)
  502. if (gid == group_array[i])
  503. return (1);
  504. return (0);
  505. }