test.c revision 1.42 1 1.42 kre /* $NetBSD: test.c,v 1.42 2018/09/12 23:33:31 kre Exp $ */
2 1.15 cgd
3 1.13 jtc /*
4 1.13 jtc * test(1); version 7-like -- author Erik Baalbergen
5 1.13 jtc * modified by Eric Gisin to be used as built-in.
6 1.13 jtc * modified by Arnold Robbins to add SVR3 compatibility
7 1.13 jtc * (-x -c -b -p -u -g -k) plus Korn's -L -nt -ot -ef and new -S (socket).
8 1.13 jtc * modified by J.T. Conklin for NetBSD.
9 1.1 glass *
10 1.13 jtc * This program is in the Public Domain.
11 1.1 glass */
12 1.1 glass
13 1.17 christos #include <sys/cdefs.h>
14 1.1 glass #ifndef lint
15 1.42 kre __RCSID("$NetBSD: test.c,v 1.42 2018/09/12 23:33:31 kre Exp $");
16 1.13 jtc #endif
17 1.1 glass
18 1.23 wiz #include <sys/stat.h>
19 1.1 glass #include <sys/types.h>
20 1.23 wiz
21 1.13 jtc #include <ctype.h>
22 1.23 wiz #include <err.h>
23 1.1 glass #include <errno.h>
24 1.33 christos #include <limits.h>
25 1.35 christos #include <locale.h>
26 1.13 jtc #include <stdio.h>
27 1.1 glass #include <stdlib.h>
28 1.1 glass #include <string.h>
29 1.23 wiz #include <unistd.h>
30 1.22 christos #include <stdarg.h>
31 1.1 glass
32 1.13 jtc /* test(1) accepts the following grammar:
33 1.13 jtc oexpr ::= aexpr | aexpr "-o" oexpr ;
34 1.13 jtc aexpr ::= nexpr | nexpr "-a" aexpr ;
35 1.14 cgd nexpr ::= primary | "!" primary
36 1.13 jtc primary ::= unary-operator operand
37 1.13 jtc | operand binary-operator operand
38 1.13 jtc | operand
39 1.13 jtc | "(" oexpr ")"
40 1.13 jtc ;
41 1.13 jtc unary-operator ::= "-r"|"-w"|"-x"|"-f"|"-d"|"-c"|"-b"|"-p"|
42 1.13 jtc "-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"|"-L"|"-S";
43 1.13 jtc
44 1.30 hubertf binary-operator ::= "="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"|
45 1.30 hubertf "-nt"|"-ot"|"-ef";
46 1.13 jtc operand ::= <any legal UNIX file name>
47 1.13 jtc */
48 1.13 jtc
49 1.13 jtc enum token {
50 1.13 jtc EOI,
51 1.13 jtc FILRD,
52 1.13 jtc FILWR,
53 1.13 jtc FILEX,
54 1.13 jtc FILEXIST,
55 1.13 jtc FILREG,
56 1.13 jtc FILDIR,
57 1.13 jtc FILCDEV,
58 1.13 jtc FILBDEV,
59 1.13 jtc FILFIFO,
60 1.13 jtc FILSOCK,
61 1.13 jtc FILSYM,
62 1.13 jtc FILGZ,
63 1.13 jtc FILTT,
64 1.13 jtc FILSUID,
65 1.13 jtc FILSGID,
66 1.13 jtc FILSTCK,
67 1.13 jtc FILNT,
68 1.13 jtc FILOT,
69 1.13 jtc FILEQ,
70 1.13 jtc FILUID,
71 1.13 jtc FILGID,
72 1.13 jtc STREZ,
73 1.13 jtc STRNZ,
74 1.13 jtc STREQ,
75 1.13 jtc STRNE,
76 1.13 jtc STRLT,
77 1.13 jtc STRGT,
78 1.13 jtc INTEQ,
79 1.13 jtc INTNE,
80 1.13 jtc INTGE,
81 1.13 jtc INTGT,
82 1.13 jtc INTLE,
83 1.13 jtc INTLT,
84 1.13 jtc UNOT,
85 1.13 jtc BAND,
86 1.13 jtc BOR,
87 1.13 jtc LPAREN,
88 1.13 jtc RPAREN,
89 1.13 jtc OPERAND
90 1.13 jtc };
91 1.1 glass
92 1.13 jtc enum token_types {
93 1.13 jtc UNOP,
94 1.13 jtc BINOP,
95 1.13 jtc BUNOP,
96 1.13 jtc BBINOP,
97 1.13 jtc PAREN
98 1.1 glass };
99 1.1 glass
100 1.31 christos struct t_op {
101 1.13 jtc const char *op_text;
102 1.13 jtc short op_num, op_type;
103 1.31 christos };
104 1.31 christos
105 1.31 christos static const struct t_op cop[] = {
106 1.13 jtc {"!", UNOT, BUNOP},
107 1.13 jtc {"(", LPAREN, PAREN},
108 1.13 jtc {")", RPAREN, PAREN},
109 1.31 christos {"<", STRLT, BINOP},
110 1.31 christos {"=", STREQ, BINOP},
111 1.31 christos {">", STRGT, BINOP},
112 1.31 christos };
113 1.31 christos
114 1.31 christos static const struct t_op cop2[] = {
115 1.31 christos {"!=", STRNE, BINOP},
116 1.31 christos };
117 1.31 christos
118 1.31 christos static const struct t_op mop3[] = {
119 1.31 christos {"ef", FILEQ, BINOP},
120 1.31 christos {"eq", INTEQ, BINOP},
121 1.31 christos {"ge", INTGE, BINOP},
122 1.31 christos {"gt", INTGT, BINOP},
123 1.31 christos {"le", INTLE, BINOP},
124 1.31 christos {"lt", INTLT, BINOP},
125 1.31 christos {"ne", INTNE, BINOP},
126 1.31 christos {"nt", FILNT, BINOP},
127 1.31 christos {"ot", FILOT, BINOP},
128 1.31 christos };
129 1.31 christos
130 1.31 christos static const struct t_op mop2[] = {
131 1.31 christos {"G", FILGID, UNOP},
132 1.31 christos {"L", FILSYM, UNOP},
133 1.31 christos {"O", FILUID, UNOP},
134 1.31 christos {"S", FILSOCK,UNOP},
135 1.31 christos {"a", BAND, BBINOP},
136 1.31 christos {"b", FILBDEV,UNOP},
137 1.31 christos {"c", FILCDEV,UNOP},
138 1.31 christos {"d", FILDIR, UNOP},
139 1.31 christos {"e", FILEXIST,UNOP},
140 1.31 christos {"f", FILREG, UNOP},
141 1.31 christos {"g", FILSGID,UNOP},
142 1.31 christos {"h", FILSYM, UNOP}, /* for backwards compat */
143 1.31 christos {"k", FILSTCK,UNOP},
144 1.31 christos {"n", STRNZ, UNOP},
145 1.31 christos {"o", BOR, BBINOP},
146 1.31 christos {"p", FILFIFO,UNOP},
147 1.31 christos {"r", FILRD, UNOP},
148 1.31 christos {"s", FILGZ, UNOP},
149 1.31 christos {"t", FILTT, UNOP},
150 1.31 christos {"u", FILSUID,UNOP},
151 1.31 christos {"w", FILWR, UNOP},
152 1.31 christos {"x", FILEX, UNOP},
153 1.31 christos {"z", STREZ, UNOP},
154 1.1 glass };
155 1.1 glass
156 1.22 christos static char **t_wp;
157 1.22 christos static struct t_op const *t_wp_op;
158 1.1 glass
159 1.38 joerg __dead static void syntax(const char *, const char *);
160 1.23 wiz static int oexpr(enum token);
161 1.23 wiz static int aexpr(enum token);
162 1.23 wiz static int nexpr(enum token);
163 1.42 kre static struct t_op const *findop(const char *);
164 1.23 wiz static int primary(enum token);
165 1.23 wiz static int binop(void);
166 1.42 kre static int perform_unop(enum token, const char *);
167 1.42 kre static int perform_binop(enum token, const char *, const char *);
168 1.33 christos static int test_access(struct stat *, mode_t);
169 1.42 kre static int filstat(const char *, enum token);
170 1.23 wiz static enum token t_lex(char *);
171 1.23 wiz static int isoperand(void);
172 1.36 christos static long long getn(const char *);
173 1.23 wiz static int newerf(const char *, const char *);
174 1.23 wiz static int olderf(const char *, const char *);
175 1.23 wiz static int equalf(const char *, const char *);
176 1.17 christos
177 1.42 kre static int one_arg(const char *);
178 1.42 kre static int two_arg(const char *, const char *);
179 1.42 kre static int three_arg(const char *, const char *, const char *);
180 1.42 kre static int four_arg(const char *, const char *, const char *, const char *);
181 1.42 kre
182 1.22 christos #if defined(SHELL)
183 1.39 joerg extern void error(const char *, ...) __dead __printflike(1, 2);
184 1.33 christos extern void *ckmalloc(size_t);
185 1.22 christos #else
186 1.39 joerg static void error(const char *, ...) __dead __printflike(1, 2);
187 1.22 christos
188 1.22 christos static void
189 1.22 christos error(const char *msg, ...)
190 1.22 christos {
191 1.22 christos va_list ap;
192 1.22 christos
193 1.22 christos va_start(ap, msg);
194 1.22 christos verrx(2, msg, ap);
195 1.22 christos /*NOTREACHED*/
196 1.22 christos va_end(ap);
197 1.22 christos }
198 1.33 christos
199 1.33 christos static void *ckmalloc(size_t);
200 1.33 christos static void *
201 1.33 christos ckmalloc(size_t nbytes)
202 1.33 christos {
203 1.33 christos void *p = malloc(nbytes);
204 1.33 christos
205 1.33 christos if (!p)
206 1.33 christos error("Not enough memory!");
207 1.33 christos return p;
208 1.33 christos }
209 1.22 christos #endif
210 1.22 christos
211 1.22 christos #ifdef SHELL
212 1.23 wiz int testcmd(int, char **);
213 1.22 christos
214 1.22 christos int
215 1.23 wiz testcmd(int argc, char **argv)
216 1.22 christos #else
217 1.1 glass int
218 1.24 wiz main(int argc, char *argv[])
219 1.22 christos #endif
220 1.1 glass {
221 1.24 wiz int res;
222 1.28 christos const char *argv0;
223 1.22 christos
224 1.28 christos #ifdef SHELL
225 1.28 christos argv0 = argv[0];
226 1.28 christos #else
227 1.24 wiz setprogname(argv[0]);
228 1.35 christos (void)setlocale(LC_ALL, "");
229 1.28 christos argv0 = getprogname();
230 1.28 christos #endif
231 1.28 christos if (strcmp(argv0, "[") == 0) {
232 1.1 glass if (strcmp(argv[--argc], "]"))
233 1.22 christos error("missing ]");
234 1.1 glass argv[argc] = NULL;
235 1.1 glass }
236 1.1 glass
237 1.42 kre /*
238 1.42 kre * POSIX defines operations of test for up to 4 args
239 1.42 kre * (depending upon what the args are in some cases)
240 1.42 kre *
241 1.42 kre * arg count does not include the command name, (but argc does)
242 1.42 kre * nor the closing ']' when the command was '[' (removed above)
243 1.42 kre *
244 1.42 kre * None of the following allow -a or -o as an operator (those
245 1.42 kre * only apply in the evaluation of unspeicified expressions)
246 1.42 kre *
247 1.42 kre * Note that the xxx_arg() functions return "shell" true/false
248 1.42 kre * (0 == true, 1 == false) or -1 for "unspecified case"
249 1.42 kre *
250 1.42 kre * Other functions return C true/false (1 == true, 0 == false)
251 1.42 kre *
252 1.42 kre * Hence we simply return the result from xxx_arg(), but
253 1.42 kre * invert the result of oexpr() below before returning it.
254 1.42 kre */
255 1.42 kre switch (argc - 1) {
256 1.42 kre case -1: /* impossible, but never mind */
257 1.42 kre case 0: /* test $a where a='' false */
258 1.22 christos return 1;
259 1.22 christos
260 1.42 kre case 1: /* test "$a" */
261 1.42 kre return one_arg(argv[1]); /* always works */
262 1.42 kre
263 1.42 kre case 2: /* test op "$a" */
264 1.42 kre res = two_arg(argv[1], argv[2]);
265 1.42 kre if (res >= 0)
266 1.42 kre return res;
267 1.42 kre break;
268 1.42 kre
269 1.42 kre case 3: /* test "$a" op "$b" or test ! op "$a" */
270 1.42 kre res = three_arg(argv[1], argv[2], argv[3]);
271 1.42 kre if (res >= 0)
272 1.42 kre return res;
273 1.42 kre break;
274 1.42 kre
275 1.42 kre case 4: /* test ! "$a" op "$b" or test ( op "$a" ) */
276 1.42 kre res = four_arg(argv[1], argv[2], argv[3], argv[4]);
277 1.42 kre if (res >= 0)
278 1.42 kre return res;
279 1.42 kre break;
280 1.42 kre
281 1.42 kre default:
282 1.42 kre break;
283 1.42 kre }
284 1.42 kre
285 1.42 kre /*
286 1.42 kre * All other cases produce unspecified results
287 1.42 kre * (including cases above with small arg counts where the
288 1.42 kre * args are not what was expected to be seen)
289 1.42 kre *
290 1.42 kre * We fall back to the old method, of attempting to parse
291 1.42 kre * the expr (highly ambiguous as there is no distinction between
292 1.42 kre * operators and operands that happen to look like operators)
293 1.42 kre */
294 1.42 kre
295 1.14 cgd t_wp = &argv[1];
296 1.13 jtc res = !oexpr(t_lex(*t_wp));
297 1.13 jtc
298 1.13 jtc if (*t_wp != NULL && *++t_wp != NULL)
299 1.21 kleink syntax(*t_wp, "unexpected operator");
300 1.13 jtc
301 1.13 jtc return res;
302 1.13 jtc }
303 1.13 jtc
304 1.13 jtc static void
305 1.23 wiz syntax(const char *op, const char *msg)
306 1.13 jtc {
307 1.26 simonb
308 1.13 jtc if (op && *op)
309 1.22 christos error("%s: %s", op, msg);
310 1.13 jtc else
311 1.22 christos error("%s", msg);
312 1.42 kre }
313 1.42 kre
314 1.42 kre static int
315 1.42 kre one_arg(const char *arg)
316 1.42 kre {
317 1.42 kre /*
318 1.42 kre * True (exit 0, so false...) if arg is not a null string
319 1.42 kre * False (so exit 1, so true) if it is.
320 1.42 kre */
321 1.42 kre return *arg == '\0';
322 1.42 kre }
323 1.42 kre
324 1.42 kre static int
325 1.42 kre two_arg(const char *a1, const char *a2)
326 1.42 kre {
327 1.42 kre static struct t_op const *op;
328 1.42 kre
329 1.42 kre if (a1[0] == '!' && a1[1] == 0)
330 1.42 kre return !one_arg(a2);
331 1.42 kre
332 1.42 kre op = findop(a1);
333 1.42 kre if (op != NULL && op->op_type == UNOP)
334 1.42 kre return !perform_unop(op->op_num, a2);
335 1.42 kre
336 1.42 kre /*
337 1.42 kre * an extension, but as we've entered the realm of the unspecified
338 1.42 kre * we're allowed... test ( $a ) where a=''
339 1.42 kre */
340 1.42 kre if (a1[0] == '(' && a2[0] == ')' && (a1[1] | a2[1]) == 0)
341 1.42 kre return 1;
342 1.42 kre
343 1.42 kre return -1;
344 1.42 kre }
345 1.42 kre
346 1.42 kre static int
347 1.42 kre three_arg(const char *a1, const char *a2, const char *a3)
348 1.42 kre {
349 1.42 kre static struct t_op const *op;
350 1.42 kre int res;
351 1.42 kre
352 1.42 kre op = findop(a2);
353 1.42 kre if (op != NULL && op->op_type == BINOP)
354 1.42 kre return !perform_binop(op->op_num, a1, a3);
355 1.42 kre
356 1.42 kre if (a1[1] != '\0')
357 1.42 kre return -1;
358 1.42 kre
359 1.42 kre if (a1[0] == '!') {
360 1.42 kre res = two_arg(a2, a3);
361 1.42 kre if (res >= 0)
362 1.42 kre res = !res;
363 1.42 kre return res;
364 1.42 kre }
365 1.42 kre
366 1.42 kre if (a1[0] == '(' && a3[0] == ')' && a3[1] == '\0')
367 1.42 kre return one_arg(a2);
368 1.42 kre
369 1.42 kre return -1;
370 1.42 kre }
371 1.42 kre
372 1.42 kre static int
373 1.42 kre four_arg(const char *a1, const char *a2, const char *a3, const char *a4)
374 1.42 kre {
375 1.42 kre int res;
376 1.42 kre
377 1.42 kre if (a1[1] != '\0')
378 1.42 kre return -1;
379 1.42 kre
380 1.42 kre if (a1[0] == '!') {
381 1.42 kre res = three_arg(a2, a3, a4);
382 1.42 kre if (res >= 0)
383 1.42 kre res = !res;
384 1.42 kre return res;
385 1.42 kre }
386 1.42 kre
387 1.42 kre if (a1[0] == '(' && a4[0] == ')' && a4[1] == '\0')
388 1.42 kre return two_arg(a2, a3);
389 1.42 kre
390 1.42 kre return -1;
391 1.13 jtc }
392 1.13 jtc
393 1.13 jtc static int
394 1.23 wiz oexpr(enum token n)
395 1.13 jtc {
396 1.13 jtc int res;
397 1.13 jtc
398 1.13 jtc res = aexpr(n);
399 1.32 christos if (*t_wp == NULL)
400 1.32 christos return res;
401 1.13 jtc if (t_lex(*++t_wp) == BOR)
402 1.13 jtc return oexpr(t_lex(*++t_wp)) || res;
403 1.13 jtc t_wp--;
404 1.13 jtc return res;
405 1.13 jtc }
406 1.13 jtc
407 1.13 jtc static int
408 1.23 wiz aexpr(enum token n)
409 1.13 jtc {
410 1.13 jtc int res;
411 1.13 jtc
412 1.13 jtc res = nexpr(n);
413 1.32 christos if (*t_wp == NULL)
414 1.32 christos return res;
415 1.13 jtc if (t_lex(*++t_wp) == BAND)
416 1.13 jtc return aexpr(t_lex(*++t_wp)) && res;
417 1.13 jtc t_wp--;
418 1.13 jtc return res;
419 1.13 jtc }
420 1.13 jtc
421 1.13 jtc static int
422 1.23 wiz nexpr(enum token n)
423 1.13 jtc {
424 1.26 simonb
425 1.13 jtc if (n == UNOT)
426 1.13 jtc return !nexpr(t_lex(*++t_wp));
427 1.13 jtc return primary(n);
428 1.13 jtc }
429 1.13 jtc
430 1.13 jtc static int
431 1.23 wiz primary(enum token n)
432 1.13 jtc {
433 1.21 kleink enum token nn;
434 1.13 jtc int res;
435 1.13 jtc
436 1.13 jtc if (n == EOI)
437 1.21 kleink return 0; /* missing expression */
438 1.13 jtc if (n == LPAREN) {
439 1.21 kleink if ((nn = t_lex(*++t_wp)) == RPAREN)
440 1.21 kleink return 0; /* missing expression */
441 1.21 kleink res = oexpr(nn);
442 1.13 jtc if (t_lex(*++t_wp) != RPAREN)
443 1.13 jtc syntax(NULL, "closing paren expected");
444 1.13 jtc return res;
445 1.1 glass }
446 1.13 jtc if (t_wp_op && t_wp_op->op_type == UNOP) {
447 1.13 jtc /* unary expression */
448 1.13 jtc if (*++t_wp == NULL)
449 1.13 jtc syntax(t_wp_op->op_text, "argument expected");
450 1.42 kre return perform_unop(n, *t_wp);
451 1.13 jtc }
452 1.14 cgd
453 1.14 cgd if (t_lex(t_wp[1]), t_wp_op && t_wp_op->op_type == BINOP) {
454 1.14 cgd return binop();
455 1.14 cgd }
456 1.14 cgd
457 1.14 cgd return strlen(*t_wp) > 0;
458 1.14 cgd }
459 1.14 cgd
460 1.14 cgd static int
461 1.42 kre perform_unop(enum token n, const char *opnd)
462 1.42 kre {
463 1.42 kre switch (n) {
464 1.42 kre case STREZ:
465 1.42 kre return strlen(opnd) == 0;
466 1.42 kre case STRNZ:
467 1.42 kre return strlen(opnd) != 0;
468 1.42 kre case FILTT:
469 1.42 kre return isatty((int)getn(opnd));
470 1.42 kre default:
471 1.42 kre return filstat(opnd, n);
472 1.42 kre }
473 1.42 kre }
474 1.42 kre
475 1.42 kre static int
476 1.23 wiz binop(void)
477 1.14 cgd {
478 1.16 tls const char *opnd1, *opnd2;
479 1.14 cgd struct t_op const *op;
480 1.14 cgd
481 1.13 jtc opnd1 = *t_wp;
482 1.13 jtc (void) t_lex(*++t_wp);
483 1.14 cgd op = t_wp_op;
484 1.13 jtc
485 1.26 simonb if ((opnd2 = *++t_wp) == NULL)
486 1.14 cgd syntax(op->op_text, "argument expected");
487 1.13 jtc
488 1.42 kre return perform_binop(op->op_num, opnd1, opnd2);
489 1.42 kre }
490 1.42 kre
491 1.42 kre static int
492 1.42 kre perform_binop(enum token op_num, const char *opnd1, const char *opnd2)
493 1.42 kre {
494 1.42 kre switch (op_num) {
495 1.14 cgd case STREQ:
496 1.14 cgd return strcmp(opnd1, opnd2) == 0;
497 1.14 cgd case STRNE:
498 1.14 cgd return strcmp(opnd1, opnd2) != 0;
499 1.14 cgd case STRLT:
500 1.14 cgd return strcmp(opnd1, opnd2) < 0;
501 1.14 cgd case STRGT:
502 1.14 cgd return strcmp(opnd1, opnd2) > 0;
503 1.14 cgd case INTEQ:
504 1.14 cgd return getn(opnd1) == getn(opnd2);
505 1.14 cgd case INTNE:
506 1.14 cgd return getn(opnd1) != getn(opnd2);
507 1.14 cgd case INTGE:
508 1.14 cgd return getn(opnd1) >= getn(opnd2);
509 1.14 cgd case INTGT:
510 1.14 cgd return getn(opnd1) > getn(opnd2);
511 1.14 cgd case INTLE:
512 1.14 cgd return getn(opnd1) <= getn(opnd2);
513 1.14 cgd case INTLT:
514 1.14 cgd return getn(opnd1) < getn(opnd2);
515 1.14 cgd case FILNT:
516 1.26 simonb return newerf(opnd1, opnd2);
517 1.14 cgd case FILOT:
518 1.26 simonb return olderf(opnd1, opnd2);
519 1.14 cgd case FILEQ:
520 1.26 simonb return equalf(opnd1, opnd2);
521 1.17 christos default:
522 1.19 mycroft abort();
523 1.17 christos /* NOTREACHED */
524 1.1 glass }
525 1.1 glass }
526 1.1 glass
527 1.33 christos /*
528 1.33 christos * The manual, and IEEE POSIX 1003.2, suggests this should check the mode bits,
529 1.33 christos * not use access():
530 1.33 christos *
531 1.33 christos * True shall indicate only that the write flag is on. The file is not
532 1.33 christos * writable on a read-only file system even if this test indicates true.
533 1.33 christos *
534 1.33 christos * Unfortunately IEEE POSIX 1003.1-2001, as quoted in SuSv3, says only:
535 1.33 christos *
536 1.33 christos * True shall indicate that permission to read from file will be granted,
537 1.33 christos * as defined in "File Read, Write, and Creation".
538 1.33 christos *
539 1.33 christos * and that section says:
540 1.33 christos *
541 1.33 christos * When a file is to be read or written, the file shall be opened with an
542 1.33 christos * access mode corresponding to the operation to be performed. If file
543 1.33 christos * access permissions deny access, the requested operation shall fail.
544 1.33 christos *
545 1.33 christos * and of course access permissions are described as one might expect:
546 1.33 christos *
547 1.33 christos * * If a process has the appropriate privilege:
548 1.33 christos *
549 1.33 christos * * If read, write, or directory search permission is requested,
550 1.33 christos * access shall be granted.
551 1.33 christos *
552 1.33 christos * * If execute permission is requested, access shall be granted if
553 1.33 christos * execute permission is granted to at least one user by the file
554 1.33 christos * permission bits or by an alternate access control mechanism;
555 1.33 christos * otherwise, access shall be denied.
556 1.33 christos *
557 1.33 christos * * Otherwise:
558 1.33 christos *
559 1.33 christos * * The file permission bits of a file contain read, write, and
560 1.33 christos * execute/search permissions for the file owner class, file group
561 1.33 christos * class, and file other class.
562 1.33 christos *
563 1.33 christos * * Access shall be granted if an alternate access control mechanism
564 1.33 christos * is not enabled and the requested access permission bit is set for
565 1.33 christos * the class (file owner class, file group class, or file other class)
566 1.33 christos * to which the process belongs, or if an alternate access control
567 1.33 christos * mechanism is enabled and it allows the requested access; otherwise,
568 1.33 christos * access shall be denied.
569 1.33 christos *
570 1.33 christos * and when I first read this I thought: surely we can't go about using
571 1.33 christos * open(O_WRONLY) to try this test! However the POSIX 1003.1-2001 Rationale
572 1.33 christos * section for test does in fact say:
573 1.33 christos *
574 1.33 christos * On historical BSD systems, test -w directory always returned false
575 1.33 christos * because test tried to open the directory for writing, which always
576 1.33 christos * fails.
577 1.33 christos *
578 1.33 christos * and indeed this is in fact true for Seventh Edition UNIX, UNIX 32V, and UNIX
579 1.33 christos * System III, and thus presumably also for BSD up to and including 4.3.
580 1.33 christos *
581 1.33 christos * Secondly I remembered why using open() and/or access() are bogus. They
582 1.33 christos * don't work right for detecting read and write permissions bits when called
583 1.33 christos * by root.
584 1.33 christos *
585 1.33 christos * Interestingly the 'test' in 4.4BSD was closer to correct (as per
586 1.33 christos * 1003.2-1992) and it was implemented efficiently with stat() instead of
587 1.33 christos * open().
588 1.33 christos *
589 1.33 christos * This was apparently broken in NetBSD around about 1994/06/30 when the old
590 1.33 christos * 4.4BSD implementation was replaced with a (arguably much better coded)
591 1.33 christos * implementation derived from pdksh.
592 1.33 christos *
593 1.33 christos * Note that modern pdksh is yet different again, but still not correct, at
594 1.33 christos * least not w.r.t. 1003.2-1992.
595 1.33 christos *
596 1.33 christos * As I think more about it and read more of the related IEEE docs I don't like
597 1.33 christos * that wording about 'test -r' and 'test -w' in 1003.1-2001 at all. I very
598 1.33 christos * much prefer the original wording in 1003.2-1992. It is much more useful,
599 1.33 christos * and so that's what I've implemented.
600 1.33 christos *
601 1.33 christos * (Note that a strictly conforming implementation of 1003.1-2001 is in fact
602 1.33 christos * totally useless for the case in question since its 'test -w' and 'test -r'
603 1.33 christos * can never fail for root for any existing files, i.e. files for which 'test
604 1.33 christos * -e' succeeds.)
605 1.33 christos *
606 1.33 christos * The rationale for 1003.1-2001 suggests that the wording was "clarified" in
607 1.33 christos * 1003.1-2001 to align with the 1003.2b draft. 1003.2b Draft 12 (July 1999),
608 1.33 christos * which is the latest copy I have, does carry the same suggested wording as is
609 1.33 christos * in 1003.1-2001, with its rationale saying:
610 1.33 christos *
611 1.33 christos * This change is a clarification and is the result of interpretation
612 1.33 christos * request PASC 1003.2-92 #23 submitted for IEEE Std 1003.2-1992.
613 1.33 christos *
614 1.33 christos * That interpretation can be found here:
615 1.33 christos *
616 1.33 christos * http://www.pasc.org/interps/unofficial/db/p1003.2/pasc-1003.2-23.html
617 1.33 christos *
618 1.33 christos * Not terribly helpful, unfortunately. I wonder who that fence sitter was.
619 1.33 christos *
620 1.33 christos * Worse, IMVNSHO, I think the authors of 1003.2b-D12 have mis-interpreted the
621 1.33 christos * PASC interpretation and appear to be gone against at least one widely used
622 1.33 christos * implementation (namely 4.4BSD). The problem is that for file access by root
623 1.33 christos * this means that if test '-r' and '-w' are to behave as if open() were called
624 1.33 christos * then there's no way for a shell script running as root to check if a file
625 1.33 christos * has certain access bits set other than by the grotty means of interpreting
626 1.33 christos * the output of 'ls -l'. This was widely considered to be a bug in V7's
627 1.33 christos * "test" and is, I believe, one of the reasons why direct use of access() was
628 1.33 christos * avoided in some more recent implementations!
629 1.33 christos *
630 1.33 christos * I have always interpreted '-r' to match '-w' and '-x' as per the original
631 1.33 christos * wording in 1003.2-1992, not the other way around. I think 1003.2b goes much
632 1.33 christos * too far the wrong way without any valid rationale and that it's best if we
633 1.33 christos * stick with 1003.2-1992 and test the flags, and not mimic the behaviour of
634 1.33 christos * open() since we already know very well how it will work -- existance of the
635 1.33 christos * file is all that matters to open() for root.
636 1.33 christos *
637 1.33 christos * Unfortunately the SVID is no help at all (which is, I guess, partly why
638 1.33 christos * we're in this mess in the first place :-).
639 1.33 christos *
640 1.33 christos * The SysV implementation (at least in the 'test' builtin in /bin/sh) does use
641 1.33 christos * access(name, 2) even though it also goes to much greater lengths for '-x'
642 1.33 christos * matching the 1003.2-1992 definition (which is no doubt where that definition
643 1.33 christos * came from).
644 1.33 christos *
645 1.33 christos * The ksh93 implementation uses access() for '-r' and '-w' if
646 1.33 christos * (euid==uid&&egid==gid), but uses st_mode for '-x' iff running as root.
647 1.33 christos * i.e. it does strictly conform to 1003.1-2001 (and presumably 1003.2b).
648 1.33 christos */
649 1.33 christos static int
650 1.33 christos test_access(struct stat *sp, mode_t stmode)
651 1.33 christos {
652 1.33 christos gid_t *groups;
653 1.33 christos register int n;
654 1.33 christos uid_t euid;
655 1.33 christos int maxgroups;
656 1.33 christos
657 1.33 christos /*
658 1.33 christos * I suppose we could use access() if not running as root and if we are
659 1.33 christos * running with ((euid == uid) && (egid == gid)), but we've already
660 1.33 christos * done the stat() so we might as well just test the permissions
661 1.33 christos * directly instead of asking the kernel to do it....
662 1.33 christos */
663 1.33 christos euid = geteuid();
664 1.33 christos if (euid == 0) /* any bit is good enough */
665 1.33 christos stmode = (stmode << 6) | (stmode << 3) | stmode;
666 1.33 christos else if (sp->st_uid == euid)
667 1.33 christos stmode <<= 6;
668 1.33 christos else if (sp->st_gid == getegid())
669 1.33 christos stmode <<= 3;
670 1.33 christos else {
671 1.33 christos /* XXX stolen almost verbatim from ksh93.... */
672 1.33 christos /* on some systems you can be in several groups */
673 1.33 christos if ((maxgroups = getgroups(0, NULL)) <= 0)
674 1.33 christos maxgroups = NGROUPS_MAX; /* pre-POSIX system? */
675 1.33 christos groups = ckmalloc((maxgroups + 1) * sizeof(gid_t));
676 1.33 christos n = getgroups(maxgroups, groups);
677 1.33 christos while (--n >= 0) {
678 1.33 christos if (groups[n] == sp->st_gid) {
679 1.33 christos stmode <<= 3;
680 1.33 christos break;
681 1.33 christos }
682 1.33 christos }
683 1.33 christos free(groups);
684 1.33 christos }
685 1.33 christos
686 1.33 christos return sp->st_mode & stmode;
687 1.33 christos }
688 1.33 christos
689 1.1 glass static int
690 1.42 kre filstat(const char *nm, enum token mode)
691 1.1 glass {
692 1.13 jtc struct stat s;
693 1.13 jtc
694 1.18 mycroft if (mode == FILSYM ? lstat(nm, &s) : stat(nm, &s))
695 1.13 jtc return 0;
696 1.1 glass
697 1.13 jtc switch (mode) {
698 1.13 jtc case FILRD:
699 1.33 christos return test_access(&s, S_IROTH);
700 1.13 jtc case FILWR:
701 1.33 christos return test_access(&s, S_IWOTH);
702 1.13 jtc case FILEX:
703 1.33 christos return test_access(&s, S_IXOTH);
704 1.13 jtc case FILEXIST:
705 1.33 christos return 1; /* the successful lstat()/stat() is good enough */
706 1.13 jtc case FILREG:
707 1.18 mycroft return S_ISREG(s.st_mode);
708 1.13 jtc case FILDIR:
709 1.18 mycroft return S_ISDIR(s.st_mode);
710 1.13 jtc case FILCDEV:
711 1.18 mycroft return S_ISCHR(s.st_mode);
712 1.13 jtc case FILBDEV:
713 1.18 mycroft return S_ISBLK(s.st_mode);
714 1.13 jtc case FILFIFO:
715 1.18 mycroft return S_ISFIFO(s.st_mode);
716 1.13 jtc case FILSOCK:
717 1.18 mycroft return S_ISSOCK(s.st_mode);
718 1.18 mycroft case FILSYM:
719 1.18 mycroft return S_ISLNK(s.st_mode);
720 1.13 jtc case FILSUID:
721 1.18 mycroft return (s.st_mode & S_ISUID) != 0;
722 1.13 jtc case FILSGID:
723 1.18 mycroft return (s.st_mode & S_ISGID) != 0;
724 1.13 jtc case FILSTCK:
725 1.18 mycroft return (s.st_mode & S_ISVTX) != 0;
726 1.13 jtc case FILGZ:
727 1.18 mycroft return s.st_size > (off_t)0;
728 1.13 jtc case FILUID:
729 1.13 jtc return s.st_uid == geteuid();
730 1.13 jtc case FILGID:
731 1.13 jtc return s.st_gid == getegid();
732 1.13 jtc default:
733 1.13 jtc return 1;
734 1.13 jtc }
735 1.1 glass }
736 1.1 glass
737 1.31 christos #define VTOC(x) (const unsigned char *)((const struct t_op *)x)->op_text
738 1.31 christos
739 1.31 christos static int
740 1.31 christos compare1(const void *va, const void *vb)
741 1.31 christos {
742 1.31 christos const unsigned char *a = va;
743 1.31 christos const unsigned char *b = VTOC(vb);
744 1.31 christos
745 1.31 christos return a[0] - b[0];
746 1.31 christos }
747 1.31 christos
748 1.31 christos static int
749 1.31 christos compare2(const void *va, const void *vb)
750 1.31 christos {
751 1.31 christos const unsigned char *a = va;
752 1.31 christos const unsigned char *b = VTOC(vb);
753 1.31 christos int z = a[0] - b[0];
754 1.31 christos
755 1.31 christos return z ? z : (a[1] - b[1]);
756 1.31 christos }
757 1.31 christos
758 1.31 christos static struct t_op const *
759 1.31 christos findop(const char *s)
760 1.31 christos {
761 1.31 christos if (s[0] == '-') {
762 1.31 christos if (s[1] == '\0')
763 1.31 christos return NULL;
764 1.31 christos if (s[2] == '\0')
765 1.31 christos return bsearch(s + 1, mop2, __arraycount(mop2),
766 1.31 christos sizeof(*mop2), compare1);
767 1.31 christos else if (s[3] != '\0')
768 1.31 christos return NULL;
769 1.31 christos else
770 1.31 christos return bsearch(s + 1, mop3, __arraycount(mop3),
771 1.31 christos sizeof(*mop3), compare2);
772 1.31 christos } else {
773 1.31 christos if (s[1] == '\0')
774 1.31 christos return bsearch(s, cop, __arraycount(cop), sizeof(*cop),
775 1.31 christos compare1);
776 1.31 christos else if (strcmp(s, cop2[0].op_text) == 0)
777 1.31 christos return cop2;
778 1.31 christos else
779 1.31 christos return NULL;
780 1.31 christos }
781 1.31 christos }
782 1.31 christos
783 1.13 jtc static enum token
784 1.23 wiz t_lex(char *s)
785 1.1 glass {
786 1.24 wiz struct t_op const *op;
787 1.24 wiz
788 1.31 christos if (s == NULL) {
789 1.26 simonb t_wp_op = NULL;
790 1.13 jtc return EOI;
791 1.13 jtc }
792 1.31 christos
793 1.31 christos if ((op = findop(s)) != NULL) {
794 1.31 christos if (!((op->op_type == UNOP && isoperand()) ||
795 1.31 christos (op->op_num == LPAREN && *(t_wp+1) == 0))) {
796 1.13 jtc t_wp_op = op;
797 1.13 jtc return op->op_num;
798 1.13 jtc }
799 1.13 jtc }
800 1.26 simonb t_wp_op = NULL;
801 1.13 jtc return OPERAND;
802 1.21 kleink }
803 1.21 kleink
804 1.21 kleink static int
805 1.23 wiz isoperand(void)
806 1.21 kleink {
807 1.24 wiz struct t_op const *op;
808 1.24 wiz char *s, *t;
809 1.21 kleink
810 1.21 kleink if ((s = *(t_wp+1)) == 0)
811 1.21 kleink return 1;
812 1.21 kleink if ((t = *(t_wp+2)) == 0)
813 1.21 kleink return 0;
814 1.31 christos if ((op = findop(s)) != NULL)
815 1.31 christos return op->op_type == BINOP && (t[0] != ')' || t[1] != '\0');
816 1.21 kleink return 0;
817 1.1 glass }
818 1.1 glass
819 1.13 jtc /* atoi with error detection */
820 1.36 christos static long long
821 1.23 wiz getn(const char *s)
822 1.1 glass {
823 1.6 alm char *p;
824 1.36 christos long long r;
825 1.1 glass
826 1.6 alm errno = 0;
827 1.36 christos r = strtoll(s, &p, 10);
828 1.13 jtc
829 1.6 alm if (errno != 0)
830 1.36 christos if (errno == ERANGE && (r == LLONG_MAX || r == LLONG_MIN))
831 1.22 christos error("%s: out of range", s);
832 1.13 jtc
833 1.42 kre if (p != s)
834 1.42 kre while (isspace((unsigned char)*p))
835 1.42 kre p++;
836 1.13 jtc
837 1.37 christos if (*p || p == s)
838 1.42 kre error("'%s': bad number", s);
839 1.13 jtc
840 1.36 christos return r;
841 1.1 glass }
842 1.1 glass
843 1.13 jtc static int
844 1.26 simonb newerf(const char *f1, const char *f2)
845 1.1 glass {
846 1.13 jtc struct stat b1, b2;
847 1.13 jtc
848 1.26 simonb return (stat(f1, &b1) == 0 &&
849 1.26 simonb stat(f2, &b2) == 0 &&
850 1.40 uebayasi timespeccmp(&b1.st_mtim, &b2.st_mtim, >));
851 1.1 glass }
852 1.1 glass
853 1.13 jtc static int
854 1.26 simonb olderf(const char *f1, const char *f2)
855 1.1 glass {
856 1.13 jtc struct stat b1, b2;
857 1.13 jtc
858 1.26 simonb return (stat(f1, &b1) == 0 &&
859 1.26 simonb stat(f2, &b2) == 0 &&
860 1.40 uebayasi timespeccmp(&b1.st_mtim, &b2.st_mtim, <));
861 1.1 glass }
862 1.1 glass
863 1.13 jtc static int
864 1.26 simonb equalf(const char *f1, const char *f2)
865 1.13 jtc {
866 1.13 jtc struct stat b1, b2;
867 1.1 glass
868 1.26 simonb return (stat(f1, &b1) == 0 &&
869 1.26 simonb stat(f2, &b2) == 0 &&
870 1.13 jtc b1.st_dev == b2.st_dev &&
871 1.13 jtc b1.st_ino == b2.st_ino);
872 1.1 glass }
873