blob: d7bd5a0ded597da91016e8e9b970b3a794c0471f [file] [log] [blame]
Erik Andersen13456d12000-03-16 08:09:57 +00001/* vi: set sw=4 ts=4: */
2/*
Erik Andersen5e1189e2000-04-15 16:34:54 +00003 * test implementation for busybox
Erik Andersen13456d12000-03-16 08:09:57 +00004 *
Eric Andersenc7bda1c2004-03-15 08:29:22 +00005 * Copyright (c) by a whole pile of folks:
Erik Andersen13456d12000-03-16 08:09:57 +00006 *
Tim Rikerc1ef7bd2006-01-25 00:08:53 +00007 * 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.
Erik Andersen13456d12000-03-16 08:09:57 +000015 *
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:
Tim Rikerc1ef7bd2006-01-25 00:08:53 +000031 * "This program is in the Public Domain."
Erik Andersen13456d12000-03-16 08:09:57 +000032 */
33
Erik Andersen13456d12000-03-16 08:09:57 +000034#include <sys/types.h>
Erik Andersen13456d12000-03-16 08:09:57 +000035#include <unistd.h>
36#include <ctype.h>
37#include <errno.h>
38#include <stdlib.h>
39#include <string.h>
Eric Andersencbe31da2001-02-20 06:14:08 +000040#include "busybox.h"
Erik Andersen13456d12000-03-16 08:09:57 +000041
42/* test(1) accepts the following grammar:
43 oexpr ::= aexpr | aexpr "-o" oexpr ;
44 aexpr ::= nexpr | nexpr "-a" aexpr ;
45 nexpr ::= primary | "!" primary
46 primary ::= unary-operator operand
47 | operand binary-operator operand
48 | operand
49 | "(" oexpr ")"
50 ;
51 unary-operator ::= "-r"|"-w"|"-x"|"-f"|"-d"|"-c"|"-b"|"-p"|
52 "-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"|"-L"|"-S";
53
Mike Frysinger75ac42b2005-04-14 02:49:22 +000054 binary-operator ::= "="|"=="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"|
Erik Andersen13456d12000-03-16 08:09:57 +000055 "-nt"|"-ot"|"-ef";
56 operand ::= <any legal UNIX file name>
57*/
58
59enum token {
60 EOI,
61 FILRD,
62 FILWR,
63 FILEX,
64 FILEXIST,
65 FILREG,
66 FILDIR,
67 FILCDEV,
68 FILBDEV,
69 FILFIFO,
70 FILSOCK,
71 FILSYM,
72 FILGZ,
73 FILTT,
74 FILSUID,
75 FILSGID,
76 FILSTCK,
77 FILNT,
78 FILOT,
79 FILEQ,
80 FILUID,
81 FILGID,
82 STREZ,
83 STRNZ,
84 STREQ,
85 STRNE,
86 STRLT,
87 STRGT,
88 INTEQ,
89 INTNE,
90 INTGE,
91 INTGT,
92 INTLE,
93 INTLT,
94 UNOT,
95 BAND,
96 BOR,
97 LPAREN,
98 RPAREN,
99 OPERAND
100};
101
102enum token_types {
103 UNOP,
104 BINOP,
105 BUNOP,
106 BBINOP,
107 PAREN
108};
109
Eric Andersen92d23242001-03-19 23:49:41 +0000110static const struct t_op {
Erik Andersen13456d12000-03-16 08:09:57 +0000111 const char *op_text;
112 short op_num, op_type;
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000113} ops[] = {
114 {
115 "-r", FILRD, UNOP}, {
116 "-w", FILWR, UNOP}, {
117 "-x", FILEX, UNOP}, {
118 "-e", FILEXIST, UNOP}, {
119 "-f", FILREG, UNOP}, {
120 "-d", FILDIR, UNOP}, {
121 "-c", FILCDEV, UNOP}, {
122 "-b", FILBDEV, UNOP}, {
123 "-p", FILFIFO, UNOP}, {
124 "-u", FILSUID, UNOP}, {
125 "-g", FILSGID, UNOP}, {
126 "-k", FILSTCK, UNOP}, {
127 "-s", FILGZ, UNOP}, {
128 "-t", FILTT, UNOP}, {
129 "-z", STREZ, UNOP}, {
130 "-n", STRNZ, UNOP}, {
131 "-h", FILSYM, UNOP}, /* for backwards compat */
132 {
133 "-O", FILUID, UNOP}, {
134 "-G", FILGID, UNOP}, {
135 "-L", FILSYM, UNOP}, {
136 "-S", FILSOCK, UNOP}, {
137 "=", STREQ, BINOP}, {
Mike Frysinger75ac42b2005-04-14 02:49:22 +0000138 "==", STREQ, BINOP}, {
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000139 "!=", STRNE, BINOP}, {
140 "<", STRLT, BINOP}, {
141 ">", STRGT, BINOP}, {
142 "-eq", INTEQ, BINOP}, {
143 "-ne", INTNE, BINOP}, {
144 "-ge", INTGE, BINOP}, {
145 "-gt", INTGT, BINOP}, {
146 "-le", INTLE, BINOP}, {
147 "-lt", INTLT, BINOP}, {
148 "-nt", FILNT, BINOP}, {
149 "-ot", FILOT, BINOP}, {
150 "-ef", FILEQ, BINOP}, {
151 "!", UNOT, BUNOP}, {
152 "-a", BAND, BBINOP}, {
153 "-o", BOR, BBINOP}, {
154 "(", LPAREN, PAREN}, {
155 ")", RPAREN, PAREN}, {
156 0, 0, 0}
Erik Andersen13456d12000-03-16 08:09:57 +0000157};
158
Glenn L McGrath73db8be2004-08-11 02:45:47 +0000159#ifdef CONFIG_FEATURE_TEST_64
160typedef int64_t arith_t;
161#else
162typedef int arith_t;
163#endif
164
Matt Kraaie6e81832000-12-30 07:46:23 +0000165static char **t_wp;
166static struct t_op const *t_wp_op;
Erik Andersen13456d12000-03-16 08:09:57 +0000167static gid_t *group_array = NULL;
168static int ngroups;
169
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000170static enum token t_lex(char *s);
Glenn L McGrath73db8be2004-08-11 02:45:47 +0000171static arith_t oexpr(enum token n);
172static arith_t aexpr(enum token n);
173static arith_t nexpr(enum token n);
Eric Andersen74400cc2001-10-18 04:11:39 +0000174static int binop(void);
Glenn L McGrath73db8be2004-08-11 02:45:47 +0000175static arith_t primary(enum token n);
Eric Andersen74400cc2001-10-18 04:11:39 +0000176static int filstat(char *nm, enum token mode);
Glenn L McGrath73db8be2004-08-11 02:45:47 +0000177static arith_t getn(const char *s);
Eric Andersen74400cc2001-10-18 04:11:39 +0000178static int newerf(const char *f1, const char *f2);
179static int olderf(const char *f1, const char *f2);
180static int equalf(const char *f1, const char *f2);
181static void syntax(const char *op, const char *msg);
182static int test_eaccess(char *path, int mode);
183static int is_a_group_member(gid_t gid);
184static void initialize_group_array(void);
Erik Andersen13456d12000-03-16 08:09:57 +0000185
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000186extern int test_main(int argc, char **argv)
Erik Andersen13456d12000-03-16 08:09:57 +0000187{
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000188 int res;
Erik Andersen13456d12000-03-16 08:09:57 +0000189
Manuel Novoa III cad53642003-03-19 09:13:01 +0000190 if (strcmp(bb_applet_name, "[") == 0) {
Erik Andersen13456d12000-03-16 08:09:57 +0000191 if (strcmp(argv[--argc], "]"))
Manuel Novoa III cad53642003-03-19 09:13:01 +0000192 bb_error_msg_and_die("missing ]");
Erik Andersen13456d12000-03-16 08:09:57 +0000193 argv[argc] = NULL;
194 }
Mike Frysinger75ac42b2005-04-14 02:49:22 +0000195 if (strcmp(bb_applet_name, "[[") == 0) {
196 if (strcmp(argv[--argc], "]]"))
197 bb_error_msg_and_die("missing ]]");
198 argv[argc] = NULL;
199 }
Erik Andersen13456d12000-03-16 08:09:57 +0000200 /* Implement special cases from POSIX.2, section 4.62.4 */
201 switch (argc) {
202 case 1:
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000203 exit(1);
Erik Andersen13456d12000-03-16 08:09:57 +0000204 case 2:
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000205 exit(*argv[1] == '\0');
Erik Andersen13456d12000-03-16 08:09:57 +0000206 case 3:
207 if (argv[1][0] == '!' && argv[1][1] == '\0') {
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000208 exit(!(*argv[2] == '\0'));
Erik Andersen13456d12000-03-16 08:09:57 +0000209 }
210 break;
211 case 4:
212 if (argv[1][0] != '!' || argv[1][1] != '\0') {
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000213 if (t_lex(argv[2]), t_wp_op && t_wp_op->op_type == BINOP) {
Erik Andersen13456d12000-03-16 08:09:57 +0000214 t_wp = &argv[1];
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000215 exit(binop() == 0);
Erik Andersen13456d12000-03-16 08:09:57 +0000216 }
217 }
218 break;
219 case 5:
220 if (argv[1][0] == '!' && argv[1][1] == '\0') {
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000221 if (t_lex(argv[3]), t_wp_op && t_wp_op->op_type == BINOP) {
Erik Andersen13456d12000-03-16 08:09:57 +0000222 t_wp = &argv[2];
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000223 exit(!(binop() == 0));
Erik Andersen13456d12000-03-16 08:09:57 +0000224 }
225 }
226 break;
227 }
228
229 t_wp = &argv[1];
230 res = !oexpr(t_lex(*t_wp));
231
232 if (*t_wp != NULL && *++t_wp != NULL)
233 syntax(*t_wp, "unknown operand");
234
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000235 return (res);
Erik Andersen13456d12000-03-16 08:09:57 +0000236}
237
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000238static void syntax(const char *op, const char *msg)
Erik Andersen13456d12000-03-16 08:09:57 +0000239{
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000240 if (op && *op) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000241 bb_error_msg_and_die("%s: %s", op, msg);
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000242 } else {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000243 bb_error_msg_and_die("%s", msg);
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000244 }
Erik Andersen13456d12000-03-16 08:09:57 +0000245}
246
Glenn L McGrath73db8be2004-08-11 02:45:47 +0000247static arith_t oexpr(enum token n)
Erik Andersen13456d12000-03-16 08:09:57 +0000248{
Glenn L McGrath73db8be2004-08-11 02:45:47 +0000249 arith_t res;
Erik Andersen13456d12000-03-16 08:09:57 +0000250
251 res = aexpr(n);
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000252 if (t_lex(*++t_wp) == BOR) {
Erik Andersen13456d12000-03-16 08:09:57 +0000253 return oexpr(t_lex(*++t_wp)) || res;
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000254 }
Erik Andersen13456d12000-03-16 08:09:57 +0000255 t_wp--;
256 return res;
257}
258
Glenn L McGrath73db8be2004-08-11 02:45:47 +0000259static arith_t aexpr(enum token n)
Erik Andersen13456d12000-03-16 08:09:57 +0000260{
Glenn L McGrath73db8be2004-08-11 02:45:47 +0000261 arith_t res;
Erik Andersen13456d12000-03-16 08:09:57 +0000262
263 res = nexpr(n);
264 if (t_lex(*++t_wp) == BAND)
265 return aexpr(t_lex(*++t_wp)) && res;
266 t_wp--;
267 return res;
268}
269
Glenn L McGrath73db8be2004-08-11 02:45:47 +0000270static arith_t nexpr(enum token n)
Erik Andersen13456d12000-03-16 08:09:57 +0000271{
272 if (n == UNOT)
273 return !nexpr(t_lex(*++t_wp));
274 return primary(n);
275}
276
Glenn L McGrath73db8be2004-08-11 02:45:47 +0000277static arith_t primary(enum token n)
Erik Andersen13456d12000-03-16 08:09:57 +0000278{
Glenn L McGrath73db8be2004-08-11 02:45:47 +0000279 arith_t res;
Erik Andersen13456d12000-03-16 08:09:57 +0000280
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000281 if (n == EOI) {
Erik Andersen13456d12000-03-16 08:09:57 +0000282 syntax(NULL, "argument expected");
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000283 }
Erik Andersen13456d12000-03-16 08:09:57 +0000284 if (n == LPAREN) {
285 res = oexpr(t_lex(*++t_wp));
286 if (t_lex(*++t_wp) != RPAREN)
287 syntax(NULL, "closing paren expected");
288 return res;
289 }
290 if (t_wp_op && t_wp_op->op_type == UNOP) {
291 /* unary expression */
292 if (*++t_wp == NULL)
293 syntax(t_wp_op->op_text, "argument expected");
294 switch (n) {
295 case STREZ:
296 return strlen(*t_wp) == 0;
297 case STRNZ:
298 return strlen(*t_wp) != 0;
299 case FILTT:
300 return isatty(getn(*t_wp));
301 default:
302 return filstat(*t_wp, n);
303 }
304 }
305
306 if (t_lex(t_wp[1]), t_wp_op && t_wp_op->op_type == BINOP) {
307 return binop();
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000308 }
Erik Andersen13456d12000-03-16 08:09:57 +0000309
310 return strlen(*t_wp) > 0;
311}
312
Mike Frysinger4e5936e2005-04-16 04:30:38 +0000313static int binop(void)
Erik Andersen13456d12000-03-16 08:09:57 +0000314{
315 const char *opnd1, *opnd2;
316 struct t_op const *op;
317
318 opnd1 = *t_wp;
319 (void) t_lex(*++t_wp);
320 op = t_wp_op;
321
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000322 if ((opnd2 = *++t_wp) == (char *) 0)
Erik Andersen13456d12000-03-16 08:09:57 +0000323 syntax(op->op_text, "argument expected");
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000324
Erik Andersen13456d12000-03-16 08:09:57 +0000325 switch (op->op_num) {
326 case STREQ:
327 return strcmp(opnd1, opnd2) == 0;
328 case STRNE:
329 return strcmp(opnd1, opnd2) != 0;
330 case STRLT:
331 return strcmp(opnd1, opnd2) < 0;
332 case STRGT:
333 return strcmp(opnd1, opnd2) > 0;
334 case INTEQ:
335 return getn(opnd1) == getn(opnd2);
336 case INTNE:
337 return getn(opnd1) != getn(opnd2);
338 case INTGE:
339 return getn(opnd1) >= getn(opnd2);
340 case INTGT:
341 return getn(opnd1) > getn(opnd2);
342 case INTLE:
343 return getn(opnd1) <= getn(opnd2);
344 case INTLT:
345 return getn(opnd1) < getn(opnd2);
346 case FILNT:
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000347 return newerf(opnd1, opnd2);
Erik Andersen13456d12000-03-16 08:09:57 +0000348 case FILOT:
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000349 return olderf(opnd1, opnd2);
Erik Andersen13456d12000-03-16 08:09:57 +0000350 case FILEQ:
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000351 return equalf(opnd1, opnd2);
Erik Andersen13456d12000-03-16 08:09:57 +0000352 }
353 /* NOTREACHED */
354 return 1;
355}
356
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000357static int filstat(char *nm, enum token mode)
Erik Andersen13456d12000-03-16 08:09:57 +0000358{
359 struct stat s;
Eric Andersenfad04fd2000-07-14 06:49:52 +0000360 unsigned int i;
Erik Andersen13456d12000-03-16 08:09:57 +0000361
362 if (mode == FILSYM) {
363#ifdef S_IFLNK
364 if (lstat(nm, &s) == 0) {
365 i = S_IFLNK;
366 goto filetype;
367 }
368#endif
369 return 0;
370 }
371
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000372 if (stat(nm, &s) != 0)
Erik Andersen13456d12000-03-16 08:09:57 +0000373 return 0;
374
375 switch (mode) {
376 case FILRD:
377 return test_eaccess(nm, R_OK) == 0;
378 case FILWR:
379 return test_eaccess(nm, W_OK) == 0;
380 case FILEX:
381 return test_eaccess(nm, X_OK) == 0;
382 case FILEXIST:
383 return 1;
384 case FILREG:
385 i = S_IFREG;
386 goto filetype;
387 case FILDIR:
388 i = S_IFDIR;
389 goto filetype;
390 case FILCDEV:
391 i = S_IFCHR;
392 goto filetype;
393 case FILBDEV:
394 i = S_IFBLK;
395 goto filetype;
396 case FILFIFO:
397#ifdef S_IFIFO
398 i = S_IFIFO;
399 goto filetype;
400#else
401 return 0;
402#endif
403 case FILSOCK:
404#ifdef S_IFSOCK
405 i = S_IFSOCK;
406 goto filetype;
407#else
408 return 0;
409#endif
410 case FILSUID:
411 i = S_ISUID;
412 goto filebit;
413 case FILSGID:
414 i = S_ISGID;
415 goto filebit;
416 case FILSTCK:
417 i = S_ISVTX;
418 goto filebit;
419 case FILGZ:
420 return s.st_size > 0L;
421 case FILUID:
422 return s.st_uid == geteuid();
423 case FILGID:
424 return s.st_gid == getegid();
425 default:
426 return 1;
427 }
428
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000429 filetype:
Erik Andersen13456d12000-03-16 08:09:57 +0000430 return ((s.st_mode & S_IFMT) == i);
431
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000432 filebit:
Erik Andersen13456d12000-03-16 08:09:57 +0000433 return ((s.st_mode & i) != 0);
434}
435
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000436static enum token t_lex(char *s)
Erik Andersen13456d12000-03-16 08:09:57 +0000437{
438 struct t_op const *op = ops;
439
440 if (s == 0) {
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000441 t_wp_op = (struct t_op *) 0;
Erik Andersen13456d12000-03-16 08:09:57 +0000442 return EOI;
443 }
444 while (op->op_text) {
445 if (strcmp(s, op->op_text) == 0) {
446 t_wp_op = op;
447 return op->op_num;
448 }
449 op++;
450 }
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000451 t_wp_op = (struct t_op *) 0;
Erik Andersen13456d12000-03-16 08:09:57 +0000452 return OPERAND;
453}
454
455/* atoi with error detection */
Glenn L McGrath73db8be2004-08-11 02:45:47 +0000456static arith_t getn(const char *s)
Erik Andersen13456d12000-03-16 08:09:57 +0000457{
458 char *p;
Glenn L McGrath73db8be2004-08-11 02:45:47 +0000459#ifdef CONFIG_FEATURE_TEST_64
460 long long r;
461#else
Erik Andersen13456d12000-03-16 08:09:57 +0000462 long r;
Glenn L McGrath73db8be2004-08-11 02:45:47 +0000463#endif
Erik Andersen13456d12000-03-16 08:09:57 +0000464
465 errno = 0;
Glenn L McGrath73db8be2004-08-11 02:45:47 +0000466#ifdef CONFIG_FEATURE_TEST_64
467 r = strtoll(s, &p, 10);
468#else
Erik Andersen13456d12000-03-16 08:09:57 +0000469 r = strtol(s, &p, 10);
Glenn L McGrath73db8be2004-08-11 02:45:47 +0000470#endif
Erik Andersen13456d12000-03-16 08:09:57 +0000471
472 if (errno != 0)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000473 bb_error_msg_and_die("%s: out of range", s);
Erik Andersen13456d12000-03-16 08:09:57 +0000474
Eric Andersen8876fb22003-06-20 09:01:58 +0000475 /* p = bb_skip_whitespace(p); avoid const warning */
476 if (*(bb_skip_whitespace(p)))
Manuel Novoa III cad53642003-03-19 09:13:01 +0000477 bb_error_msg_and_die("%s: bad number", s);
Erik Andersen13456d12000-03-16 08:09:57 +0000478
Glenn L McGrath73db8be2004-08-11 02:45:47 +0000479 return r;
Erik Andersen13456d12000-03-16 08:09:57 +0000480}
481
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000482static int newerf(const char *f1, const char *f2)
Erik Andersen13456d12000-03-16 08:09:57 +0000483{
484 struct stat b1, b2;
485
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000486 return (stat(f1, &b1) == 0 &&
487 stat(f2, &b2) == 0 && b1.st_mtime > b2.st_mtime);
Erik Andersen13456d12000-03-16 08:09:57 +0000488}
489
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000490static int olderf(const char *f1, const char *f2)
Erik Andersen13456d12000-03-16 08:09:57 +0000491{
492 struct stat b1, b2;
493
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000494 return (stat(f1, &b1) == 0 &&
495 stat(f2, &b2) == 0 && b1.st_mtime < b2.st_mtime);
Erik Andersen13456d12000-03-16 08:09:57 +0000496}
497
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000498static int equalf(const char *f1, const char *f2)
Erik Andersen13456d12000-03-16 08:09:57 +0000499{
500 struct stat b1, b2;
501
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000502 return (stat(f1, &b1) == 0 &&
503 stat(f2, &b2) == 0 &&
504 b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino);
Erik Andersen13456d12000-03-16 08:09:57 +0000505}
506
507/* Do the same thing access(2) does, but use the effective uid and gid,
508 and don't make the mistake of telling root that any file is
509 executable. */
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000510static int test_eaccess(char *path, int mode)
Erik Andersen13456d12000-03-16 08:09:57 +0000511{
512 struct stat st;
Eric Andersenfad04fd2000-07-14 06:49:52 +0000513 unsigned int euid = geteuid();
Erik Andersen13456d12000-03-16 08:09:57 +0000514
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000515 if (stat(path, &st) < 0)
Erik Andersen13456d12000-03-16 08:09:57 +0000516 return (-1);
517
518 if (euid == 0) {
519 /* Root can read or write any file. */
520 if (mode != X_OK)
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000521 return (0);
Erik Andersen13456d12000-03-16 08:09:57 +0000522
523 /* Root can execute any file that has any one of the execute
524 bits set. */
525 if (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))
526 return (0);
527 }
528
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000529 if (st.st_uid == euid) /* owner */
Erik Andersen13456d12000-03-16 08:09:57 +0000530 mode <<= 6;
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000531 else if (is_a_group_member(st.st_gid))
Erik Andersen13456d12000-03-16 08:09:57 +0000532 mode <<= 3;
533
534 if (st.st_mode & mode)
535 return (0);
536
537 return (-1);
538}
539
Mike Frysinger4e5936e2005-04-16 04:30:38 +0000540static void initialize_group_array(void)
Erik Andersen13456d12000-03-16 08:09:57 +0000541{
542 ngroups = getgroups(0, NULL);
Matt Kraai322ae932000-09-13 02:46:14 +0000543 group_array = xrealloc(group_array, ngroups * sizeof(gid_t));
Erik Andersen13456d12000-03-16 08:09:57 +0000544 getgroups(ngroups, group_array);
545}
546
547/* Return non-zero if GID is one that we have in our groups list. */
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000548static int is_a_group_member(gid_t gid)
Erik Andersen13456d12000-03-16 08:09:57 +0000549{
550 register int i;
551
552 /* Short-circuit if possible, maybe saving a call to getgroups(). */
553 if (gid == getgid() || gid == getegid())
554 return (1);
555
556 if (ngroups == 0)
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000557 initialize_group_array();
Erik Andersen13456d12000-03-16 08:09:57 +0000558
559 /* Search through the list looking for GID. */
560 for (i = 0; i < ngroups; i++)
561 if (gid == group_array[i])
562 return (1);
563
564 return (0);
565}