1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23#include "libbb.h"
24#include <setjmp.h>
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50#define TEST_DEBUG 0
51
52enum token {
53 EOI,
54 FILRD,
55 FILWR,
56 FILEX,
57 FILEXIST,
58 FILREG,
59 FILDIR,
60 FILCDEV,
61 FILBDEV,
62 FILFIFO,
63 FILSOCK,
64 FILSYM,
65 FILGZ,
66 FILTT,
67 FILSUID,
68 FILSGID,
69 FILSTCK,
70 FILNT,
71 FILOT,
72 FILEQ,
73 FILUID,
74 FILGID,
75 STREZ,
76 STRNZ,
77 STREQ,
78 STRNE,
79 STRLT,
80 STRGT,
81 INTEQ,
82 INTNE,
83 INTGE,
84 INTGT,
85 INTLE,
86 INTLT,
87 UNOT,
88 BAND,
89 BOR,
90 LPAREN,
91 RPAREN,
92 OPERAND
93};
94#define is_int_op(a) (((unsigned char)((a) - INTEQ)) <= 5)
95#define is_str_op(a) (((unsigned char)((a) - STREZ)) <= 5)
96#define is_file_op(a) (((unsigned char)((a) - FILNT)) <= 2)
97#define is_file_access(a) (((unsigned char)((a) - FILRD)) <= 2)
98#define is_file_type(a) (((unsigned char)((a) - FILREG)) <= 5)
99#define is_file_bit(a) (((unsigned char)((a) - FILSUID)) <= 2)
100
101#if TEST_DEBUG
102int depth;
103#define nest_msg(...) do { \
104 depth++; \
105 fprintf(stderr, "%*s", depth*2, ""); \
106 fprintf(stderr, __VA_ARGS__); \
107} while (0)
108#define unnest_msg(...) do { \
109 fprintf(stderr, "%*s", depth*2, ""); \
110 fprintf(stderr, __VA_ARGS__); \
111 depth--; \
112} while (0)
113#define dbg_msg(...) do { \
114 fprintf(stderr, "%*s", depth*2, ""); \
115 fprintf(stderr, __VA_ARGS__); \
116} while (0)
117#define unnest_msg_and_return(expr, ...) do { \
118 number_t __res = (expr); \
119 fprintf(stderr, "%*s", depth*2, ""); \
120 fprintf(stderr, __VA_ARGS__, res); \
121 depth--; \
122 return __res; \
123} while (0)
124static const char *const TOKSTR[] = {
125 "EOI",
126 "FILRD",
127 "FILWR",
128 "FILEX",
129 "FILEXIST",
130 "FILREG",
131 "FILDIR",
132 "FILCDEV",
133 "FILBDEV",
134 "FILFIFO",
135 "FILSOCK",
136 "FILSYM",
137 "FILGZ",
138 "FILTT",
139 "FILSUID",
140 "FILSGID",
141 "FILSTCK",
142 "FILNT",
143 "FILOT",
144 "FILEQ",
145 "FILUID",
146 "FILGID",
147 "STREZ",
148 "STRNZ",
149 "STREQ",
150 "STRNE",
151 "STRLT",
152 "STRGT",
153 "INTEQ",
154 "INTNE",
155 "INTGE",
156 "INTGT",
157 "INTLE",
158 "INTLT",
159 "UNOT",
160 "BAND",
161 "BOR",
162 "LPAREN",
163 "RPAREN",
164 "OPERAND"
165};
166#else
167#define nest_msg(...) ((void)0)
168#define unnest_msg(...) ((void)0)
169#define dbg_msg(...) ((void)0)
170#define unnest_msg_and_return(expr, ...) return expr
171#endif
172
173enum token_types {
174 UNOP,
175 BINOP,
176 BUNOP,
177 BBINOP,
178 PAREN
179};
180
181struct operator_t {
182 char op_text[4];
183 unsigned char op_num, op_type;
184};
185
186static const struct operator_t ops[] = {
187 { "-r", FILRD , UNOP },
188 { "-w", FILWR , UNOP },
189 { "-x", FILEX , UNOP },
190 { "-e", FILEXIST, UNOP },
191 { "-f", FILREG , UNOP },
192 { "-d", FILDIR , UNOP },
193 { "-c", FILCDEV , UNOP },
194 { "-b", FILBDEV , UNOP },
195 { "-p", FILFIFO , UNOP },
196 { "-u", FILSUID , UNOP },
197 { "-g", FILSGID , UNOP },
198 { "-k", FILSTCK , UNOP },
199 { "-s", FILGZ , UNOP },
200 { "-t", FILTT , UNOP },
201 { "-z", STREZ , UNOP },
202 { "-n", STRNZ , UNOP },
203 { "-h", FILSYM , UNOP },
204
205 { "-O" , FILUID , UNOP },
206 { "-G" , FILGID , UNOP },
207 { "-L" , FILSYM , UNOP },
208 { "-S" , FILSOCK, UNOP },
209 { "=" , STREQ , BINOP },
210 { "==" , STREQ , BINOP },
211 { "!=" , STRNE , BINOP },
212 { "<" , STRLT , BINOP },
213 { ">" , STRGT , BINOP },
214 { "-eq", INTEQ , BINOP },
215 { "-ne", INTNE , BINOP },
216 { "-ge", INTGE , BINOP },
217 { "-gt", INTGT , BINOP },
218 { "-le", INTLE , BINOP },
219 { "-lt", INTLT , BINOP },
220 { "-nt", FILNT , BINOP },
221 { "-ot", FILOT , BINOP },
222 { "-ef", FILEQ , BINOP },
223 { "!" , UNOT , BUNOP },
224 { "-a" , BAND , BBINOP },
225 { "-o" , BOR , BBINOP },
226 { "(" , LPAREN , PAREN },
227 { ")" , RPAREN , PAREN },
228};
229
230
231#if ENABLE_FEATURE_TEST_64
232typedef int64_t number_t;
233#else
234typedef int number_t;
235#endif
236
237
238
239struct test_statics {
240 char **args;
241
242
243 const struct operator_t *last_operator;
244 gid_t *group_array;
245 int ngroups;
246 jmp_buf leaving;
247};
248
249
250extern struct test_statics *const test_ptr_to_statics;
251
252#define S (*test_ptr_to_statics)
253#define args (S.args )
254#define last_operator (S.last_operator)
255#define group_array (S.group_array )
256#define ngroups (S.ngroups )
257#define leaving (S.leaving )
258
259#define INIT_S() do { \
260 (*(struct test_statics**)&test_ptr_to_statics) = xzalloc(sizeof(S)); \
261 barrier(); \
262} while (0)
263#define DEINIT_S() do { \
264 free(test_ptr_to_statics); \
265} while (0)
266
267static number_t primary(enum token n);
268
269static void syntax(const char *op, const char *msg) NORETURN;
270static void syntax(const char *op, const char *msg)
271{
272 if (op && *op) {
273 bb_error_msg("%s: %s", op, msg);
274 } else {
275 bb_error_msg("%s: %s"+4, msg);
276 }
277 longjmp(leaving, 2);
278}
279
280
281
282static number_t getn(const char *s)
283{
284 char *p;
285#if ENABLE_FEATURE_TEST_64
286 long long r;
287#else
288 long r;
289#endif
290
291 errno = 0;
292#if ENABLE_FEATURE_TEST_64
293 r = strtoll(s, &p, 10);
294#else
295 r = strtol(s, &p, 10);
296#endif
297
298 if (errno != 0)
299 syntax(s, "out of range");
300
301 if (*(skip_whitespace(p)))
302 syntax(s, "bad number");
303
304 return r;
305}
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335static enum token check_operator(char *s)
336{
337 static const struct operator_t no_op = {
338 .op_num = -1,
339 .op_type = -1
340 };
341 const struct operator_t *op;
342
343 last_operator = &no_op;
344 if (s == NULL) {
345 return EOI;
346 }
347
348 op = ops;
349 do {
350 if (strcmp(s, op->op_text) == 0) {
351 last_operator = op;
352 return op->op_num;
353 }
354 op++;
355 } while (op < ops + ARRAY_SIZE(ops));
356
357 return OPERAND;
358}
359
360
361static int binop(void)
362{
363 const char *opnd1, *opnd2;
364 const struct operator_t *op;
365 number_t val1, val2;
366
367 opnd1 = *args;
368 check_operator(*++args);
369 op = last_operator;
370
371 opnd2 = *++args;
372 if (opnd2 == NULL)
373 syntax(op->op_text, "argument expected");
374
375 if (is_int_op(op->op_num)) {
376 val1 = getn(opnd1);
377 val2 = getn(opnd2);
378 if (op->op_num == INTEQ)
379 return val1 == val2;
380 if (op->op_num == INTNE)
381 return val1 != val2;
382 if (op->op_num == INTGE)
383 return val1 >= val2;
384 if (op->op_num == INTGT)
385 return val1 > val2;
386 if (op->op_num == INTLE)
387 return val1 <= val2;
388 if (op->op_num == INTLT)
389 return val1 < val2;
390 }
391 if (is_str_op(op->op_num)) {
392 val1 = strcmp(opnd1, opnd2);
393 if (op->op_num == STREQ)
394 return val1 == 0;
395 if (op->op_num == STRNE)
396 return val1 != 0;
397 if (op->op_num == STRLT)
398 return val1 < 0;
399 if (op->op_num == STRGT)
400 return val1 > 0;
401 }
402
403
404
405
406 {
407 struct stat b1, b2;
408
409 if (stat(opnd1, &b1) || stat(opnd2, &b2))
410 return 0;
411 if (op->op_num == FILNT)
412 return b1.st_mtime > b2.st_mtime;
413 if (op->op_num == FILOT)
414 return b1.st_mtime < b2.st_mtime;
415 if (op->op_num == FILEQ)
416 return b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino;
417 }
418 return 1;
419}
420
421
422static void initialize_group_array(void)
423{
424 ngroups = getgroups(0, NULL);
425 if (ngroups > 0) {
426
427
428
429
430
431 group_array = xrealloc(group_array, ngroups * sizeof(gid_t));
432 getgroups(ngroups, group_array);
433 }
434}
435
436
437
438
439
440
441static int is_a_group_member(gid_t gid)
442{
443 int i;
444
445
446 if (gid == getgid() || gid == getegid())
447 return 1;
448
449 if (ngroups == 0)
450 initialize_group_array();
451
452
453 for (i = 0; i < ngroups; i++)
454 if (gid == group_array[i])
455 return 1;
456
457 return 0;
458}
459
460
461
462
463
464static int test_eaccess(char *path, int mode)
465{
466 struct stat st;
467 unsigned int euid = geteuid();
468
469 if (stat(path, &st) < 0)
470 return -1;
471
472 if (euid == 0) {
473
474 if (mode != X_OK)
475 return 0;
476
477
478
479 if (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))
480 return 0;
481 }
482
483 if (st.st_uid == euid)
484 mode <<= 6;
485 else if (is_a_group_member(st.st_gid))
486 mode <<= 3;
487
488 if (st.st_mode & mode)
489 return 0;
490
491 return -1;
492}
493
494
495static int filstat(char *nm, enum token mode)
496{
497 struct stat s;
498 unsigned i = i;
499
500 if (mode == FILSYM) {
501#ifdef S_IFLNK
502 if (lstat(nm, &s) == 0) {
503 i = S_IFLNK;
504 goto filetype;
505 }
506#endif
507 return 0;
508 }
509
510 if (stat(nm, &s) != 0)
511 return 0;
512 if (mode == FILEXIST)
513 return 1;
514 if (is_file_access(mode)) {
515 if (mode == FILRD)
516 i = R_OK;
517 if (mode == FILWR)
518 i = W_OK;
519 if (mode == FILEX)
520 i = X_OK;
521 return test_eaccess(nm, i) == 0;
522 }
523 if (is_file_type(mode)) {
524 if (mode == FILREG)
525 i = S_IFREG;
526 if (mode == FILDIR)
527 i = S_IFDIR;
528 if (mode == FILCDEV)
529 i = S_IFCHR;
530 if (mode == FILBDEV)
531 i = S_IFBLK;
532 if (mode == FILFIFO) {
533#ifdef S_IFIFO
534 i = S_IFIFO;
535#else
536 return 0;
537#endif
538 }
539 if (mode == FILSOCK) {
540#ifdef S_IFSOCK
541 i = S_IFSOCK;
542#else
543 return 0;
544#endif
545 }
546 filetype:
547 return ((s.st_mode & S_IFMT) == i);
548 }
549 if (is_file_bit(mode)) {
550 if (mode == FILSUID)
551 i = S_ISUID;
552 if (mode == FILSGID)
553 i = S_ISGID;
554 if (mode == FILSTCK)
555 i = S_ISVTX;
556 return ((s.st_mode & i) != 0);
557 }
558 if (mode == FILGZ)
559 return s.st_size > 0L;
560 if (mode == FILUID)
561 return s.st_uid == geteuid();
562 if (mode == FILGID)
563 return s.st_gid == getegid();
564 return 1;
565}
566
567
568static number_t nexpr(enum token n)
569{
570 number_t res;
571
572 nest_msg(">nexpr(%s)\n", TOKSTR[n]);
573 if (n == UNOT) {
574 res = !nexpr(check_operator(*++args));
575 unnest_msg("<nexpr:%lld\n", res);
576 return res;
577 }
578 res = primary(n);
579 unnest_msg("<nexpr:%lld\n", res);
580 return res;
581}
582
583
584static number_t aexpr(enum token n)
585{
586 number_t res;
587
588 nest_msg(">aexpr(%s)\n", TOKSTR[n]);
589 res = nexpr(n);
590 dbg_msg("aexpr: nexpr:%lld, next args:%s\n", res, args[1]);
591 if (check_operator(*++args) == BAND) {
592 dbg_msg("aexpr: arg is AND, next args:%s\n", args[1]);
593 res = aexpr(check_operator(*++args)) && res;
594 unnest_msg("<aexpr:%lld\n", res);
595 return res;
596 }
597 args--;
598 unnest_msg("<aexpr:%lld, args:%s\n", res, args[0]);
599 return res;
600}
601
602
603static number_t oexpr(enum token n)
604{
605 number_t res;
606
607 nest_msg(">oexpr(%s)\n", TOKSTR[n]);
608 res = aexpr(n);
609 dbg_msg("oexpr: aexpr:%lld, next args:%s\n", res, args[1]);
610 if (check_operator(*++args) == BOR) {
611 dbg_msg("oexpr: next arg is OR, next args:%s\n", args[1]);
612 res = oexpr(check_operator(*++args)) || res;
613 unnest_msg("<oexpr:%lld\n", res);
614 return res;
615 }
616 args--;
617 unnest_msg("<oexpr:%lld, args:%s\n", res, args[0]);
618 return res;
619}
620
621
622static number_t primary(enum token n)
623{
624#if TEST_DEBUG
625 number_t res = res;
626#else
627 number_t res;
628#endif
629 const struct operator_t *args0_op;
630
631 nest_msg(">primary(%s)\n", TOKSTR[n]);
632 if (n == EOI) {
633 syntax(NULL, "argument expected");
634 }
635 if (n == LPAREN) {
636 res = oexpr(check_operator(*++args));
637 if (check_operator(*++args) != RPAREN)
638 syntax(NULL, "closing paren expected");
639 unnest_msg("<primary:%lld\n", res);
640 return res;
641 }
642
643
644
645 args0_op = last_operator;
646
647 if (check_operator(args[1]) != EOI) {
648 if (args[2]) {
649
650
651
652 if (last_operator->op_type == BINOP)
653 unnest_msg_and_return(binop(), "<primary: binop:%lld\n");
654 }
655 }
656
657 if (args0_op->op_type == UNOP) {
658
659 if (args[1] == NULL)
660
661 goto check_emptiness;
662 args++;
663 if (n == STREZ)
664 unnest_msg_and_return(args[0][0] == '\0', "<primary:%lld\n");
665 if (n == STRNZ)
666 unnest_msg_and_return(args[0][0] != '\0', "<primary:%lld\n");
667 if (n == FILTT)
668 unnest_msg_and_return(isatty(getn(*args)), "<primary: isatty(%s)%lld\n", *args);
669 unnest_msg_and_return(filstat(*args, n), "<primary: filstat(%s):%lld\n", *args);
670 }
671
672
673 if (last_operator->op_type == BINOP) {
674
675 unnest_msg_and_return(binop(), "<primary:%lld\n");
676 }
677 check_emptiness:
678 unnest_msg_and_return(args[0][0] != '\0', "<primary:%lld\n");
679}
680
681
682int test_main(int argc, char **argv)
683{
684 int res;
685 const char *arg0;
686
687
688 arg0 = bb_basename(argv[0]);
689 if (arg0[0] == '[') {
690 --argc;
691 if (!arg0[1]) {
692 if (NOT_LONE_CHAR(argv[argc], ']')) {
693 bb_error_msg("missing ]");
694 return 2;
695 }
696 } else {
697 if (strcmp(argv[argc], "]]") != 0) {
698 bb_error_msg("missing ]]");
699 return 2;
700 }
701 }
702 argv[argc] = NULL;
703 }
704
705
706 INIT_S();
707
708 res = setjmp(leaving);
709 if (res)
710 goto ret;
711
712
713
714
715
716
717
718
719
720 ngroups = 0;
721
722
723 argv++;
724
725
726 if (!argv[0]) {
727 res = 1;
728 goto ret;
729 }
730#if 0
731
732 if (LONE_CHAR(argv[0], '!') && argv[1]) {
733 negate = 1;
734
735 argv++;
736 }
737 if (!argv[1]) {
738 res = (*argv[0] == '\0');
739 goto ret;
740 }
741 if (argv[2] && !argv[3]) {
742 check_operator(argv[1]);
743 if (last_operator->op_type == BINOP) {
744
745 args = &argv[0];
746 res = (binop() == 0);
747 goto ret;
748 }
749 }
750
751
752 if (negate) {
753 negate = 0;
754
755 argv--;
756 }
757#endif
758 args = &argv[0];
759 res = !oexpr(check_operator(*args));
760
761 if (*args != NULL && *++args != NULL) {
762
763 bb_error_msg("%s: unknown operand", *args);
764 res = 2;
765 }
766 ret:
767 DEINIT_S();
768
769 return res;
770}
771