1 /*
2 * Create a squashfs filesystem. This is a highly compressed read only
3 * filesystem.
4 *
5 * Copyright (c) 2011, 2012, 2013, 2014
6 * Phillip Lougher <phillip@squashfs.org.uk>
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2,
11 * or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21 *
22 * action.c
23 */
24
25 #include <fcntl.h>
26 #include <dirent.h>
27 #include <stddef.h>
28 #include <stdlib.h>
29 #include <stdio.h>
30 #include <string.h>
31 #include <sys/stat.h>
32 #include <sys/types.h>
33 #include <unistd.h>
34 #include <pwd.h>
35 #include <grp.h>
36 #include <sys/wait.h>
37 #include <regex.h>
38 #include <limits.h>
39 #include <errno.h>
40
41 #include "squashfs_fs.h"
42 #include "mksquashfs.h"
43 #include "action.h"
44 #include "error.h"
45 #include "fnmatch_compat.h"
46
47 #ifndef strdupa
48 #include <string.h> // required for str*()
49 #include <stdlib.h> // required for alloca
50 #define strdupa(foo) (strncpy( alloca( strlen(foo) + 1 ), foo, strlen( foo ) + 1 ))
51 #endif
52
53 /*
54 * code to parse actions
55 */
56
57 static char *cur_ptr, *source;
58 static struct action *fragment_spec = NULL;
59 static struct action *exclude_spec = NULL;
60 static struct action *empty_spec = NULL;
61 static struct action *move_spec = NULL;
62 static struct action *prune_spec = NULL;
63 static struct action *other_spec = NULL;
64 static int fragment_count = 0;
65 static int exclude_count = 0;
66 static int empty_count = 0;
67 static int move_count = 0;
68 static int prune_count = 0;
69 static int other_count = 0;
70 static struct action_entry *parsing_action;
71
72 static struct file_buffer *def_fragment = NULL;
73
74 static struct token_entry token_table[] = {
75 { "(", TOK_OPEN_BRACKET, 1, },
76 { ")", TOK_CLOSE_BRACKET, 1 },
77 { "&&", TOK_AND, 2 },
78 { "||", TOK_OR, 2 },
79 { "!", TOK_NOT, 1 },
80 { ",", TOK_COMMA, 1 },
81 { "@", TOK_AT, 1},
82 { " ", TOK_WHITE_SPACE, 1 },
83 { "\t ", TOK_WHITE_SPACE, 1 },
84 { "", -1, 0 }
85 };
86
87
88 static struct test_entry test_table[];
89
90 static struct action_entry action_table[];
91
92 static struct expr *parse_expr(int subexp);
93
94 extern char *pathname(struct dir_ent *);
95
96 extern char *subpathname(struct dir_ent *);
97
98 extern int read_file(char *filename, char *type, int (parse_line)(char *));
99
100 /*
101 * Lexical analyser
102 */
103 #define STR_SIZE 256
104
get_token(char ** string)105 static int get_token(char **string)
106 {
107 /* string buffer */
108 static char *str = NULL;
109 static int size = 0;
110
111 char *str_ptr;
112 int cur_size, i, quoted;
113
114 while (1) {
115 if (*cur_ptr == '\0')
116 return TOK_EOF;
117 for (i = 0; token_table[i].token != -1; i++)
118 if (strncmp(cur_ptr, token_table[i].string,
119 token_table[i].size) == 0)
120 break;
121 if (token_table[i].token != TOK_WHITE_SPACE)
122 break;
123 cur_ptr ++;
124 }
125
126 if (token_table[i].token != -1) {
127 cur_ptr += token_table[i].size;
128 return token_table[i].token;
129 }
130
131 /* string */
132 if(str == NULL) {
133 str = malloc(STR_SIZE);
134 if(str == NULL)
135 MEM_ERROR();
136 size = STR_SIZE;
137 }
138
139 /* Initialise string being read */
140 str_ptr = str;
141 cur_size = 0;
142 quoted = 0;
143
144 while(1) {
145 while(*cur_ptr == '"') {
146 cur_ptr ++;
147 quoted = !quoted;
148 }
149
150 if(*cur_ptr == '\0') {
151 /* inside quoted string EOF, otherwise end of string */
152 if(quoted)
153 return TOK_EOF;
154 else
155 break;
156 }
157
158 if(!quoted) {
159 for(i = 0; token_table[i].token != -1; i++)
160 if (strncmp(cur_ptr, token_table[i].string,
161 token_table[i].size) == 0)
162 break;
163 if (token_table[i].token != -1)
164 break;
165 }
166
167 if(*cur_ptr == '\\') {
168 cur_ptr ++;
169 if(*cur_ptr == '\0')
170 return TOK_EOF;
171 }
172
173 if(cur_size + 2 > size) {
174 char *tmp;
175
176 size = (cur_size + 1 + STR_SIZE) & ~(STR_SIZE - 1);
177
178 tmp = realloc(str, size);
179 if(tmp == NULL)
180 MEM_ERROR();
181
182 str_ptr = str_ptr - str + tmp;
183 str = tmp;
184 }
185
186 *str_ptr ++ = *cur_ptr ++;
187 cur_size ++;
188 }
189
190 *str_ptr = '\0';
191 *string = str;
192 return TOK_STRING;
193 }
194
195
peek_token(char ** string)196 static int peek_token(char **string)
197 {
198 char *saved = cur_ptr;
199 int token = get_token(string);
200
201 cur_ptr = saved;
202
203 return token;
204 }
205
206
207 /*
208 * Expression parser
209 */
free_parse_tree(struct expr * expr)210 static void free_parse_tree(struct expr *expr)
211 {
212 if(expr->type == ATOM_TYPE) {
213 int i;
214
215 for(i = 0; i < expr->atom.test->args; i++)
216 free(expr->atom.argv[i]);
217
218 free(expr->atom.argv);
219 } else if (expr->type == UNARY_TYPE)
220 free_parse_tree(expr->unary_op.expr);
221 else {
222 free_parse_tree(expr->expr_op.lhs);
223 free_parse_tree(expr->expr_op.rhs);
224 }
225
226 free(expr);
227 }
228
229
create_expr(struct expr * lhs,int op,struct expr * rhs)230 static struct expr *create_expr(struct expr *lhs, int op, struct expr *rhs)
231 {
232 struct expr *expr;
233
234 if (rhs == NULL) {
235 free_parse_tree(lhs);
236 return NULL;
237 }
238
239 expr = malloc(sizeof(*expr));
240 if (expr == NULL)
241 MEM_ERROR();
242
243 expr->type = OP_TYPE;
244 expr->expr_op.lhs = lhs;
245 expr->expr_op.rhs = rhs;
246 expr->expr_op.op = op;
247
248 return expr;
249 }
250
251
create_unary_op(struct expr * lhs,int op)252 static struct expr *create_unary_op(struct expr *lhs, int op)
253 {
254 struct expr *expr;
255
256 if (lhs == NULL)
257 return NULL;
258
259 expr = malloc(sizeof(*expr));
260 if (expr == NULL)
261 MEM_ERROR();
262
263 expr->type = UNARY_TYPE;
264 expr->unary_op.expr = lhs;
265 expr->unary_op.op = op;
266
267 return expr;
268 }
269
270
parse_test(char * name)271 static struct expr *parse_test(char *name)
272 {
273 char *string, **argv = NULL;
274 int token, args = 0;
275 int i;
276 struct test_entry *test;
277 struct expr *expr;
278
279 for (i = 0; test_table[i].args != -1; i++)
280 if (strcmp(name, test_table[i].name) == 0)
281 break;
282
283 test = &test_table[i];
284
285 if (test->args == -1) {
286 SYNTAX_ERROR("Non-existent test \"%s\"\n", name);
287 return NULL;
288 }
289
290 if(parsing_action->type == EXCLUDE_ACTION && !test->exclude_ok) {
291 fprintf(stderr, "Failed to parse action \"%s\"\n", source);
292 fprintf(stderr, "Test \"%s\" cannot be used in exclude "
293 "actions\n", name);
294 fprintf(stderr, "Use prune action instead ...\n");
295 return NULL;
296 }
297
298 expr = malloc(sizeof(*expr));
299 if (expr == NULL)
300 MEM_ERROR();
301
302 expr->type = ATOM_TYPE;
303
304 expr->atom.test = test;
305 expr->atom.data = NULL;
306
307 /*
308 * If the test has no arguments, then go straight to checking if there's
309 * enough arguments
310 */
311 token = peek_token(&string);
312
313 if (token != TOK_OPEN_BRACKET)
314 goto skip_args;
315
316 get_token(&string);
317
318 /*
319 * speculatively read all the arguments, and then see if the
320 * number of arguments read is the number expected, this handles
321 * tests with a variable number of arguments
322 */
323 token = get_token(&string);
324 if (token == TOK_CLOSE_BRACKET)
325 goto skip_args;
326
327 while(1) {
328 if (token != TOK_STRING) {
329 SYNTAX_ERROR("Unexpected token \"%s\", expected "
330 "argument\n", TOK_TO_STR(token, string));
331 goto failed;
332 }
333
334 argv = realloc(argv, (args + 1) * sizeof(char *));
335 if (argv == NULL)
336 MEM_ERROR();
337
338 argv[args ++ ] = strdup(string);
339
340 token = get_token(&string);
341
342 if (token == TOK_CLOSE_BRACKET)
343 break;
344
345 if (token != TOK_COMMA) {
346 SYNTAX_ERROR("Unexpected token \"%s\", expected "
347 "\",\" or \")\"\n", TOK_TO_STR(token, string));
348 goto failed;
349 }
350 token = get_token(&string);
351 }
352
353 skip_args:
354 /*
355 * expected number of arguments?
356 */
357 if(test->args != -2 && args != test->args) {
358 SYNTAX_ERROR("Unexpected number of arguments, expected %d, "
359 "got %d\n", test->args, args);
360 goto failed;
361 }
362
363 expr->atom.args = args;
364 expr->atom.argv = argv;
365
366 if (test->parse_args) {
367 int res = test->parse_args(test, &expr->atom);
368
369 if (res == 0)
370 goto failed;
371 }
372
373 return expr;
374
375 failed:
376 free(argv);
377 free(expr);
378 return NULL;
379 }
380
381
get_atom()382 static struct expr *get_atom()
383 {
384 char *string;
385 int token = get_token(&string);
386
387 switch(token) {
388 case TOK_NOT:
389 return create_unary_op(get_atom(), token);
390 case TOK_OPEN_BRACKET:
391 return parse_expr(1);
392 case TOK_STRING:
393 return parse_test(string);
394 default:
395 SYNTAX_ERROR("Unexpected token \"%s\", expected test "
396 "operation, \"!\", or \"(\"\n",
397 TOK_TO_STR(token, string));
398 return NULL;
399 }
400 }
401
402
parse_expr(int subexp)403 static struct expr *parse_expr(int subexp)
404 {
405 struct expr *expr = get_atom();
406
407 while (expr) {
408 char *string;
409 int op = get_token(&string);
410
411 if (op == TOK_EOF) {
412 if (subexp) {
413 free_parse_tree(expr);
414 SYNTAX_ERROR("Expected \"&&\", \"||\" or "
415 "\")\", got EOF\n");
416 return NULL;
417 }
418 break;
419 }
420
421 if (op == TOK_CLOSE_BRACKET) {
422 if (!subexp) {
423 free_parse_tree(expr);
424 SYNTAX_ERROR("Unexpected \")\", expected "
425 "\"&&\", \"!!\" or EOF\n");
426 return NULL;
427 }
428 break;
429 }
430
431 if (op != TOK_AND && op != TOK_OR) {
432 free_parse_tree(expr);
433 SYNTAX_ERROR("Unexpected token \"%s\", expected "
434 "\"&&\" or \"||\"\n", TOK_TO_STR(op, string));
435 return NULL;
436 }
437
438 expr = create_expr(expr, op, get_atom());
439 }
440
441 return expr;
442 }
443
444
445 /*
446 * Action parser
447 */
parse_action(char * s,int verbose)448 int parse_action(char *s, int verbose)
449 {
450 char *string, **argv = NULL;
451 int i, token, args = 0;
452 struct expr *expr;
453 struct action_entry *action;
454 void *data = NULL;
455 struct action **spec_list;
456 int spec_count;
457
458 cur_ptr = source = s;
459 token = get_token(&string);
460
461 if (token != TOK_STRING) {
462 SYNTAX_ERROR("Unexpected token \"%s\", expected name\n",
463 TOK_TO_STR(token, string));
464 return 0;
465 }
466
467 for (i = 0; action_table[i].args != -1; i++)
468 if (strcmp(string, action_table[i].name) == 0)
469 break;
470
471 if (action_table[i].args == -1) {
472 SYNTAX_ERROR("Non-existent action \"%s\"\n", string);
473 return 0;
474 }
475
476 action = &action_table[i];
477
478 token = get_token(&string);
479
480 if (token == TOK_AT)
481 goto skip_args;
482
483 if (token != TOK_OPEN_BRACKET) {
484 SYNTAX_ERROR("Unexpected token \"%s\", expected \"(\"\n",
485 TOK_TO_STR(token, string));
486 goto failed;
487 }
488
489 /*
490 * speculatively read all the arguments, and then see if the
491 * number of arguments read is the number expected, this handles
492 * actions with a variable number of arguments
493 */
494 token = get_token(&string);
495 if (token == TOK_CLOSE_BRACKET)
496 goto skip_args;
497
498 while (1) {
499 if (token != TOK_STRING) {
500 SYNTAX_ERROR("Unexpected token \"%s\", expected "
501 "argument\n", TOK_TO_STR(token, string));
502 goto failed;
503 }
504
505 argv = realloc(argv, (args + 1) * sizeof(char *));
506 if (argv == NULL)
507 MEM_ERROR();
508
509 argv[args ++] = strdup(string);
510
511 token = get_token(&string);
512
513 if (token == TOK_CLOSE_BRACKET)
514 break;
515
516 if (token != TOK_COMMA) {
517 SYNTAX_ERROR("Unexpected token \"%s\", expected "
518 "\",\" or \")\"\n", TOK_TO_STR(token, string));
519 goto failed;
520 }
521 token = get_token(&string);
522 }
523
524 skip_args:
525 /*
526 * expected number of arguments?
527 */
528 if(action->args != -2 && args != action->args) {
529 SYNTAX_ERROR("Unexpected number of arguments, expected %d, "
530 "got %d\n", action->args, args);
531 goto failed;
532 }
533
534 if (action->parse_args) {
535 int res = action->parse_args(action, args, argv, &data);
536
537 if (res == 0)
538 goto failed;
539 }
540
541 if (token == TOK_CLOSE_BRACKET)
542 token = get_token(&string);
543
544 if (token != TOK_AT) {
545 SYNTAX_ERROR("Unexpected token \"%s\", expected \"@\"\n",
546 TOK_TO_STR(token, string));
547 goto failed;
548 }
549
550 parsing_action = action;
551 expr = parse_expr(0);
552
553 if (expr == NULL)
554 goto failed;
555
556 /*
557 * choose action list and increment action counter
558 */
559 switch(action->type) {
560 case FRAGMENT_ACTION:
561 spec_count = fragment_count ++;
562 spec_list = &fragment_spec;
563 break;
564 case EXCLUDE_ACTION:
565 spec_count = exclude_count ++;
566 spec_list = &exclude_spec;
567 break;
568 case EMPTY_ACTION:
569 spec_count = empty_count ++;
570 spec_list = &empty_spec;
571 break;
572 case MOVE_ACTION:
573 spec_count = move_count ++;
574 spec_list = &move_spec;
575 break;
576 case PRUNE_ACTION:
577 spec_count = prune_count ++;
578 spec_list = &prune_spec;
579 break;
580 default:
581 spec_count = other_count ++;
582 spec_list = &other_spec;
583 }
584
585 *spec_list = realloc(*spec_list, (spec_count + 1) *
586 sizeof(struct action));
587 if (*spec_list == NULL)
588 MEM_ERROR();
589
590 (*spec_list)[spec_count].type = action->type;
591 (*spec_list)[spec_count].action = action;
592 (*spec_list)[spec_count].args = args;
593 (*spec_list)[spec_count].argv = argv;
594 (*spec_list)[spec_count].expr = expr;
595 (*spec_list)[spec_count].data = data;
596 (*spec_list)[spec_count].verbose = verbose;
597
598 return 1;
599
600 failed:
601 free(argv);
602 return 0;
603 }
604
605
606 /*
607 * Evaluate expressions
608 */
609
610 #define ALLOC_SZ 128
611
612 #define LOG_ENABLE 0
613 #define LOG_DISABLE 1
614 #define LOG_PRINT 2
615 #define LOG_ENABLED 3
616
_expr_log(char * string,int cmnd)617 char *_expr_log(char *string, int cmnd)
618 {
619 static char *expr_msg = NULL;
620 static int cur_size = 0, alloc_size = 0;
621 int size;
622
623 switch(cmnd) {
624 case LOG_ENABLE:
625 expr_msg = malloc(ALLOC_SZ);
626 alloc_size = ALLOC_SZ;
627 cur_size = 0;
628 return expr_msg;
629 case LOG_DISABLE:
630 free(expr_msg);
631 alloc_size = cur_size = 0;
632 return expr_msg = NULL;
633 case LOG_ENABLED:
634 return expr_msg;
635 default:
636 if(expr_msg == NULL)
637 return NULL;
638 break;
639 }
640
641 /* if string is empty append '\0' */
642 size = strlen(string) ? : 1;
643
644 if(alloc_size - cur_size < size) {
645 /* buffer too small, expand */
646 alloc_size = (cur_size + size + ALLOC_SZ - 1) & ~(ALLOC_SZ - 1);
647
648 expr_msg = realloc(expr_msg, alloc_size);
649 if(expr_msg == NULL)
650 MEM_ERROR();
651 }
652
653 memcpy(expr_msg + cur_size, string, size);
654 cur_size += size;
655
656 return expr_msg;
657 }
658
659
expr_log_cmnd(int cmnd)660 char *expr_log_cmnd(int cmnd)
661 {
662 return _expr_log(NULL, cmnd);
663 }
664
665
expr_log(char * string)666 char *expr_log(char *string)
667 {
668 return _expr_log(string, LOG_PRINT);
669 }
670
671
expr_log_atom(struct atom * atom)672 void expr_log_atom(struct atom *atom)
673 {
674 int i;
675
676 if(atom->test->handle_logging)
677 return;
678
679 expr_log(atom->test->name);
680
681 if(atom->args) {
682 expr_log("(");
683 for(i = 0; i < atom->args; i++) {
684 expr_log(atom->argv[i]);
685 if (i + 1 < atom->args)
686 expr_log(",");
687 }
688 expr_log(")");
689 }
690 }
691
692
expr_log_match(int match)693 void expr_log_match(int match)
694 {
695 if(match)
696 expr_log("=True");
697 else
698 expr_log("=False");
699 }
700
701
eval_expr_log(struct expr * expr,struct action_data * action_data)702 static int eval_expr_log(struct expr *expr, struct action_data *action_data)
703 {
704 int match;
705
706 switch (expr->type) {
707 case ATOM_TYPE:
708 expr_log_atom(&expr->atom);
709 match = expr->atom.test->fn(&expr->atom, action_data);
710 expr_log_match(match);
711 break;
712 case UNARY_TYPE:
713 expr_log("!");
714 match = !eval_expr_log(expr->unary_op.expr, action_data);
715 break;
716 default:
717 expr_log("(");
718 match = eval_expr_log(expr->expr_op.lhs, action_data);
719
720 if ((expr->expr_op.op == TOK_AND && match) ||
721 (expr->expr_op.op == TOK_OR && !match)) {
722 expr_log(token_table[expr->expr_op.op].string);
723 match = eval_expr_log(expr->expr_op.rhs, action_data);
724 }
725 expr_log(")");
726 break;
727 }
728
729 return match;
730 }
731
732
eval_expr(struct expr * expr,struct action_data * action_data)733 static int eval_expr(struct expr *expr, struct action_data *action_data)
734 {
735 int match;
736
737 switch (expr->type) {
738 case ATOM_TYPE:
739 match = expr->atom.test->fn(&expr->atom, action_data);
740 break;
741 case UNARY_TYPE:
742 match = !eval_expr(expr->unary_op.expr, action_data);
743 break;
744 default:
745 match = eval_expr(expr->expr_op.lhs, action_data);
746
747 if ((expr->expr_op.op == TOK_AND && match) ||
748 (expr->expr_op.op == TOK_OR && !match))
749 match = eval_expr(expr->expr_op.rhs, action_data);
750 break;
751 }
752
753 return match;
754 }
755
756
eval_expr_top(struct action * action,struct action_data * action_data)757 static int eval_expr_top(struct action *action, struct action_data *action_data)
758 {
759 if(action->verbose) {
760 int match, n;
761
762 expr_log_cmnd(LOG_ENABLE);
763
764 if(action_data->subpath)
765 expr_log(action_data->subpath);
766
767 expr_log("=");
768 expr_log(action->action->name);
769
770 if(action->args) {
771 expr_log("(");
772 for (n = 0; n < action->args; n++) {
773 expr_log(action->argv[n]);
774 if(n + 1 < action->args)
775 expr_log(",");
776 }
777 expr_log(")");
778 }
779
780 expr_log("@");
781
782 match = eval_expr_log(action->expr, action_data);
783
784 /*
785 * Print the evaluated expression log, if the
786 * result matches the logging specified
787 */
788 if((match && (action->verbose & ACTION_LOG_TRUE)) || (!match
789 && (action->verbose & ACTION_LOG_FALSE)))
790 progressbar_info("%s\n", expr_log(""));
791
792 expr_log_cmnd(LOG_DISABLE);
793
794 return match;
795 } else
796 return eval_expr(action->expr, action_data);
797 }
798
799
800 /*
801 * Read action file, passing each line to parse_action() for
802 * parsing.
803 *
804 * One action per line, of the form
805 * action(arg1,arg2)@expr(arg1,arg2)....
806 *
807 * Actions can be split across multiple lines using "\".
808 *
809 * Blank lines and comment lines indicated by # are supported.
810 */
parse_action_true(char * s)811 int parse_action_true(char *s)
812 {
813 return parse_action(s, ACTION_LOG_TRUE);
814 }
815
816
parse_action_false(char * s)817 int parse_action_false(char *s)
818 {
819 return parse_action(s, ACTION_LOG_FALSE);
820 }
821
822
parse_action_verbose(char * s)823 int parse_action_verbose(char *s)
824 {
825 return parse_action(s, ACTION_LOG_VERBOSE);
826 }
827
828
parse_action_nonverbose(char * s)829 int parse_action_nonverbose(char *s)
830 {
831 return parse_action(s, ACTION_LOG_NONE);
832 }
833
834
read_action_file(char * filename,int verbose)835 int read_action_file(char *filename, int verbose)
836 {
837 switch(verbose) {
838 case ACTION_LOG_TRUE:
839 return read_file(filename, "action", parse_action_true);
840 case ACTION_LOG_FALSE:
841 return read_file(filename, "action", parse_action_false);
842 case ACTION_LOG_VERBOSE:
843 return read_file(filename, "action", parse_action_verbose);
844 default:
845 return read_file(filename, "action", parse_action_nonverbose);
846 }
847 }
848
849
850 /*
851 * helper to evaluate whether action/test acts on this file type
852 */
file_type_match(int st_mode,int type)853 static int file_type_match(int st_mode, int type)
854 {
855 switch(type) {
856 case ACTION_DIR:
857 return S_ISDIR(st_mode);
858 case ACTION_REG:
859 return S_ISREG(st_mode);
860 case ACTION_ALL:
861 return S_ISREG(st_mode) || S_ISDIR(st_mode) ||
862 S_ISCHR(st_mode) || S_ISBLK(st_mode) ||
863 S_ISFIFO(st_mode) || S_ISSOCK(st_mode);
864 case ACTION_LNK:
865 return S_ISLNK(st_mode);
866 case ACTION_ALL_LNK:
867 default:
868 return 1;
869 }
870 }
871
872
873 /*
874 * General action evaluation code
875 */
actions()876 int actions()
877 {
878 return other_count;
879 }
880
881
eval_actions(struct dir_info * root,struct dir_ent * dir_ent)882 void eval_actions(struct dir_info *root, struct dir_ent *dir_ent)
883 {
884 int i, match;
885 struct action_data action_data;
886 int st_mode = dir_ent->inode->buf.st_mode;
887
888 action_data.name = dir_ent->name;
889 action_data.pathname = strdup(pathname(dir_ent));
890 action_data.subpath = strdup(subpathname(dir_ent));
891 action_data.buf = &dir_ent->inode->buf;
892 action_data.depth = dir_ent->our_dir->depth;
893 action_data.dir_ent = dir_ent;
894 action_data.root = root;
895
896 for (i = 0; i < other_count; i++) {
897 struct action *action = &other_spec[i];
898
899 if (!file_type_match(st_mode, action->action->file_types))
900 /* action does not operate on this file type */
901 continue;
902
903 match = eval_expr_top(action, &action_data);
904
905 if (match)
906 action->action->run_action(action, dir_ent);
907 }
908
909 free(action_data.pathname);
910 free(action_data.subpath);
911 }
912
913
914 /*
915 * Fragment specific action code
916 */
eval_frag_actions(struct dir_info * root,struct dir_ent * dir_ent)917 void *eval_frag_actions(struct dir_info *root, struct dir_ent *dir_ent)
918 {
919 int i, match;
920 struct action_data action_data;
921
922 action_data.name = dir_ent->name;
923 action_data.pathname = strdup(pathname(dir_ent));
924 action_data.subpath = strdup(subpathname(dir_ent));
925 action_data.buf = &dir_ent->inode->buf;
926 action_data.depth = dir_ent->our_dir->depth;
927 action_data.dir_ent = dir_ent;
928 action_data.root = root;
929
930 for (i = 0; i < fragment_count; i++) {
931 match = eval_expr_top(&fragment_spec[i], &action_data);
932 if (match) {
933 free(action_data.pathname);
934 free(action_data.subpath);
935 return &fragment_spec[i].data;
936 }
937 }
938
939 free(action_data.pathname);
940 free(action_data.subpath);
941 return &def_fragment;
942 }
943
944
get_frag_action(void * fragment)945 void *get_frag_action(void *fragment)
946 {
947 struct action *spec_list_end = &fragment_spec[fragment_count];
948 struct action *action;
949
950 if (fragment == NULL)
951 return &def_fragment;
952
953 if (fragment_count == 0)
954 return NULL;
955
956 if (fragment == &def_fragment)
957 action = &fragment_spec[0] - 1;
958 else
959 action = fragment - offsetof(struct action, data);
960
961 if (++action == spec_list_end)
962 return NULL;
963
964 return &action->data;
965 }
966
967
968 /*
969 * Exclude specific action code
970 */
exclude_actions()971 int exclude_actions()
972 {
973 return exclude_count;
974 }
975
976
eval_exclude_actions(char * name,char * pathname,char * subpath,struct stat * buf,int depth,struct dir_ent * dir_ent)977 int eval_exclude_actions(char *name, char *pathname, char *subpath,
978 struct stat *buf, int depth, struct dir_ent *dir_ent)
979 {
980 int i, match = 0;
981 struct action_data action_data;
982
983 action_data.name = name;
984 action_data.pathname = pathname;
985 action_data.subpath = subpath;
986 action_data.buf = buf;
987 action_data.depth = depth;
988 action_data.dir_ent = dir_ent;
989
990 for (i = 0; i < exclude_count && !match; i++)
991 match = eval_expr_top(&exclude_spec[i], &action_data);
992
993 return match;
994 }
995
996
997 /*
998 * Fragment specific action code
999 */
frag_action(struct action * action,struct dir_ent * dir_ent)1000 static void frag_action(struct action *action, struct dir_ent *dir_ent)
1001 {
1002 struct inode_info *inode = dir_ent->inode;
1003
1004 inode->no_fragments = 0;
1005 }
1006
no_frag_action(struct action * action,struct dir_ent * dir_ent)1007 static void no_frag_action(struct action *action, struct dir_ent *dir_ent)
1008 {
1009 struct inode_info *inode = dir_ent->inode;
1010
1011 inode->no_fragments = 1;
1012 }
1013
always_frag_action(struct action * action,struct dir_ent * dir_ent)1014 static void always_frag_action(struct action *action, struct dir_ent *dir_ent)
1015 {
1016 struct inode_info *inode = dir_ent->inode;
1017
1018 inode->always_use_fragments = 1;
1019 }
1020
no_always_frag_action(struct action * action,struct dir_ent * dir_ent)1021 static void no_always_frag_action(struct action *action, struct dir_ent *dir_ent)
1022 {
1023 struct inode_info *inode = dir_ent->inode;
1024
1025 inode->always_use_fragments = 0;
1026 }
1027
1028
1029 /*
1030 * Compression specific action code
1031 */
comp_action(struct action * action,struct dir_ent * dir_ent)1032 static void comp_action(struct action *action, struct dir_ent *dir_ent)
1033 {
1034 struct inode_info *inode = dir_ent->inode;
1035
1036 inode->noD = inode->noF = 0;
1037 }
1038
uncomp_action(struct action * action,struct dir_ent * dir_ent)1039 static void uncomp_action(struct action *action, struct dir_ent *dir_ent)
1040 {
1041 struct inode_info *inode = dir_ent->inode;
1042
1043 inode->noD = inode->noF = 1;
1044 }
1045
1046
1047 /*
1048 * Uid/gid specific action code
1049 */
parse_uid(char * arg)1050 static long long parse_uid(char *arg) {
1051 char *b;
1052 long long uid = strtoll(arg, &b, 10);
1053
1054 if (*b == '\0') {
1055 if (uid < 0 || uid >= (1LL << 32)) {
1056 SYNTAX_ERROR("Uid out of range\n");
1057 return -1;
1058 }
1059 } else {
1060 struct passwd *passwd = getpwnam(arg);
1061
1062 if (passwd)
1063 uid = passwd->pw_uid;
1064 else {
1065 SYNTAX_ERROR("Invalid uid or unknown user\n");
1066 return -1;
1067 }
1068 }
1069
1070 return uid;
1071 }
1072
1073
parse_gid(char * arg)1074 static long long parse_gid(char *arg) {
1075 char *b;
1076 long long gid = strtoll(arg, &b, 10);
1077
1078 if (*b == '\0') {
1079 if (gid < 0 || gid >= (1LL << 32)) {
1080 SYNTAX_ERROR("Gid out of range\n");
1081 return -1;
1082 }
1083 } else {
1084 struct group *group = getgrnam(arg);
1085
1086 if (group)
1087 gid = group->gr_gid;
1088 else {
1089 SYNTAX_ERROR("Invalid gid or unknown group\n");
1090 return -1;
1091 }
1092 }
1093
1094 return gid;
1095 }
1096
1097
parse_uid_args(struct action_entry * action,int args,char ** argv,void ** data)1098 static int parse_uid_args(struct action_entry *action, int args, char **argv,
1099 void **data)
1100 {
1101 long long uid;
1102 struct uid_info *uid_info;
1103
1104 uid = parse_uid(argv[0]);
1105 if (uid == -1)
1106 return 0;
1107
1108 uid_info = malloc(sizeof(struct uid_info));
1109 if (uid_info == NULL)
1110 MEM_ERROR();
1111
1112 uid_info->uid = uid;
1113 *data = uid_info;
1114
1115 return 1;
1116 }
1117
1118
parse_gid_args(struct action_entry * action,int args,char ** argv,void ** data)1119 static int parse_gid_args(struct action_entry *action, int args, char **argv,
1120 void **data)
1121 {
1122 long long gid;
1123 struct gid_info *gid_info;
1124
1125 gid = parse_gid(argv[0]);
1126 if (gid == -1)
1127 return 0;
1128
1129 gid_info = malloc(sizeof(struct gid_info));
1130 if (gid_info == NULL)
1131 MEM_ERROR();
1132
1133 gid_info->gid = gid;
1134 *data = gid_info;
1135
1136 return 1;
1137 }
1138
1139
parse_guid_args(struct action_entry * action,int args,char ** argv,void ** data)1140 static int parse_guid_args(struct action_entry *action, int args, char **argv,
1141 void **data)
1142 {
1143 long long uid, gid;
1144 struct guid_info *guid_info;
1145
1146 uid = parse_uid(argv[0]);
1147 if (uid == -1)
1148 return 0;
1149
1150 gid = parse_gid(argv[1]);
1151 if (gid == -1)
1152 return 0;
1153
1154 guid_info = malloc(sizeof(struct guid_info));
1155 if (guid_info == NULL)
1156 MEM_ERROR();
1157
1158 guid_info->uid = uid;
1159 guid_info->gid = gid;
1160 *data = guid_info;
1161
1162 return 1;
1163 }
1164
1165
uid_action(struct action * action,struct dir_ent * dir_ent)1166 static void uid_action(struct action *action, struct dir_ent *dir_ent)
1167 {
1168 struct inode_info *inode = dir_ent->inode;
1169 struct uid_info *uid_info = action->data;
1170
1171 inode->buf.st_uid = uid_info->uid;
1172 }
1173
gid_action(struct action * action,struct dir_ent * dir_ent)1174 static void gid_action(struct action *action, struct dir_ent *dir_ent)
1175 {
1176 struct inode_info *inode = dir_ent->inode;
1177 struct gid_info *gid_info = action->data;
1178
1179 inode->buf.st_gid = gid_info->gid;
1180 }
1181
guid_action(struct action * action,struct dir_ent * dir_ent)1182 static void guid_action(struct action *action, struct dir_ent *dir_ent)
1183 {
1184 struct inode_info *inode = dir_ent->inode;
1185 struct guid_info *guid_info = action->data;
1186
1187 inode->buf.st_uid = guid_info->uid;
1188 inode->buf.st_gid = guid_info->gid;
1189
1190 }
1191
1192
1193 /*
1194 * Mode specific action code
1195 */
parse_octal_mode_args(int args,char ** argv,void ** data)1196 static int parse_octal_mode_args(int args, char **argv,
1197 void **data)
1198 {
1199 int n, bytes;
1200 unsigned int mode;
1201 struct mode_data *mode_data;
1202
1203 /* octal mode number? */
1204 n = sscanf(argv[0], "%o%n", &mode, &bytes);
1205 if (n == 0)
1206 return -1; /* not an octal number arg */
1207
1208
1209 /* check there's no trailing junk */
1210 if (argv[0][bytes] != '\0') {
1211 SYNTAX_ERROR("Unexpected trailing bytes after octal "
1212 "mode number\n");
1213 return 0; /* bad octal number arg */
1214 }
1215
1216 /* check there's only one argument */
1217 if (args > 1) {
1218 SYNTAX_ERROR("Octal mode number is first argument, "
1219 "expected one argument, got %d\n", args);
1220 return 0; /* bad octal number arg */
1221 }
1222
1223 /* check mode is within range */
1224 if (mode > 07777) {
1225 SYNTAX_ERROR("Octal mode %o is out of range\n", mode);
1226 return 0; /* bad octal number arg */
1227 }
1228
1229 mode_data = malloc(sizeof(struct mode_data));
1230 if (mode_data == NULL)
1231 MEM_ERROR();
1232
1233 mode_data->operation = ACTION_MODE_OCT;
1234 mode_data->mode = mode;
1235 mode_data->next = NULL;
1236 *data = mode_data;
1237
1238 return 1;
1239 }
1240
1241
1242 /*
1243 * Parse symbolic mode of format [ugoa]*[[+-=]PERMS]+
1244 * PERMS = [rwxXst]+ or [ugo]
1245 */
parse_sym_mode_arg(char * arg,struct mode_data ** head,struct mode_data ** cur)1246 static int parse_sym_mode_arg(char *arg, struct mode_data **head,
1247 struct mode_data **cur)
1248 {
1249 struct mode_data *mode_data;
1250 int mode;
1251 int mask = 0;
1252 int op;
1253 char X;
1254
1255 if (arg[0] != 'u' && arg[0] != 'g' && arg[0] != 'o' && arg[0] != 'a') {
1256 /* no ownership specifiers, default to a */
1257 mask = 0777;
1258 goto parse_operation;
1259 }
1260
1261 /* parse ownership specifiers */
1262 while(1) {
1263 switch(*arg) {
1264 case 'u':
1265 mask |= 04700;
1266 break;
1267 case 'g':
1268 mask |= 02070;
1269 break;
1270 case 'o':
1271 mask |= 01007;
1272 break;
1273 case 'a':
1274 mask = 07777;
1275 break;
1276 default:
1277 goto parse_operation;
1278 }
1279 arg ++;
1280 }
1281
1282 parse_operation:
1283 /* trap a symbolic mode with just an ownership specification */
1284 if(*arg == '\0') {
1285 SYNTAX_ERROR("Expected one of '+', '-' or '=', got EOF\n");
1286 goto failed;
1287 }
1288
1289 while(*arg != '\0') {
1290 mode = 0;
1291 X = 0;
1292
1293 switch(*arg) {
1294 case '+':
1295 op = ACTION_MODE_ADD;
1296 break;
1297 case '-':
1298 op = ACTION_MODE_REM;
1299 break;
1300 case '=':
1301 op = ACTION_MODE_SET;
1302 break;
1303 default:
1304 SYNTAX_ERROR("Expected one of '+', '-' or '=', got "
1305 "'%c'\n", *arg);
1306 goto failed;
1307 }
1308
1309 arg ++;
1310
1311 /* Parse PERMS */
1312 if (*arg == 'u' || *arg == 'g' || *arg == 'o') {
1313 /* PERMS = [ugo] */
1314 mode = - *arg;
1315 arg ++;
1316 } else {
1317 /* PERMS = [rwxXst]* */
1318 while(1) {
1319 switch(*arg) {
1320 case 'r':
1321 mode |= 0444;
1322 break;
1323 case 'w':
1324 mode |= 0222;
1325 break;
1326 case 'x':
1327 mode |= 0111;
1328 break;
1329 case 's':
1330 mode |= 06000;
1331 break;
1332 case 't':
1333 mode |= 01000;
1334 break;
1335 case 'X':
1336 X = 1;
1337 break;
1338 case '+':
1339 case '-':
1340 case '=':
1341 case '\0':
1342 mode &= mask;
1343 goto perms_parsed;
1344 default:
1345 SYNTAX_ERROR("Unrecognised permission "
1346 "'%c'\n", *arg);
1347 goto failed;
1348 }
1349
1350 arg ++;
1351 }
1352 }
1353
1354 perms_parsed:
1355 mode_data = malloc(sizeof(*mode_data));
1356 if (mode_data == NULL)
1357 MEM_ERROR();
1358
1359 mode_data->operation = op;
1360 mode_data->mode = mode;
1361 mode_data->mask = mask;
1362 mode_data->X = X;
1363 mode_data->next = NULL;
1364
1365 if (*cur) {
1366 (*cur)->next = mode_data;
1367 *cur = mode_data;
1368 } else
1369 *head = *cur = mode_data;
1370 }
1371
1372 return 1;
1373
1374 failed:
1375 return 0;
1376 }
1377
1378
parse_sym_mode_args(struct action_entry * action,int args,char ** argv,void ** data)1379 static int parse_sym_mode_args(struct action_entry *action, int args,
1380 char **argv, void **data)
1381 {
1382 int i, res = 1;
1383 struct mode_data *head = NULL, *cur = NULL;
1384
1385 for (i = 0; i < args && res; i++)
1386 res = parse_sym_mode_arg(argv[i], &head, &cur);
1387
1388 *data = head;
1389
1390 return res;
1391 }
1392
1393
parse_mode_args(struct action_entry * action,int args,char ** argv,void ** data)1394 static int parse_mode_args(struct action_entry *action, int args,
1395 char **argv, void **data)
1396 {
1397 int res;
1398
1399 if (args == 0) {
1400 SYNTAX_ERROR("Mode action expects one or more arguments\n");
1401 return 0;
1402 }
1403
1404 res = parse_octal_mode_args(args, argv, data);
1405 if(res >= 0)
1406 /* Got an octal mode argument */
1407 return res;
1408 else /* not an octal mode argument */
1409 return parse_sym_mode_args(action, args, argv, data);
1410 }
1411
1412
mode_execute(struct mode_data * mode_data,int st_mode)1413 static int mode_execute(struct mode_data *mode_data, int st_mode)
1414 {
1415 int mode = 0;
1416
1417 for (;mode_data; mode_data = mode_data->next) {
1418 if (mode_data->mode < 0) {
1419 /* 'u', 'g' or 'o' */
1420 switch(-mode_data->mode) {
1421 case 'u':
1422 mode = (st_mode >> 6) & 07;
1423 break;
1424 case 'g':
1425 mode = (st_mode >> 3) & 07;
1426 break;
1427 case 'o':
1428 mode = st_mode & 07;
1429 break;
1430 }
1431 mode = ((mode << 6) | (mode << 3) | mode) &
1432 mode_data->mask;
1433 } else if (mode_data->X &&
1434 ((st_mode & S_IFMT) == S_IFDIR ||
1435 (st_mode & 0111)))
1436 /* X permission, only takes effect if inode is a
1437 * directory or x is set for some owner */
1438 mode = mode_data->mode | (0111 & mode_data->mask);
1439 else
1440 mode = mode_data->mode;
1441
1442 switch(mode_data->operation) {
1443 case ACTION_MODE_OCT:
1444 st_mode = (st_mode & S_IFMT) | mode;
1445 break;
1446 case ACTION_MODE_SET:
1447 st_mode = (st_mode & ~mode_data->mask) | mode;
1448 break;
1449 case ACTION_MODE_ADD:
1450 st_mode |= mode;
1451 break;
1452 case ACTION_MODE_REM:
1453 st_mode &= ~mode;
1454 }
1455 }
1456
1457 return st_mode;
1458 }
1459
1460
mode_action(struct action * action,struct dir_ent * dir_ent)1461 static void mode_action(struct action *action, struct dir_ent *dir_ent)
1462 {
1463 dir_ent->inode->buf.st_mode = mode_execute(action->data,
1464 dir_ent->inode->buf.st_mode);
1465 }
1466
1467
1468 /*
1469 * Empty specific action code
1470 */
empty_actions()1471 int empty_actions()
1472 {
1473 return empty_count;
1474 }
1475
1476
parse_empty_args(struct action_entry * action,int args,char ** argv,void ** data)1477 static int parse_empty_args(struct action_entry *action, int args,
1478 char **argv, void **data)
1479 {
1480 struct empty_data *empty_data;
1481 int val;
1482
1483 if (args >= 2) {
1484 SYNTAX_ERROR("Empty action expects zero or one argument\n");
1485 return 0;
1486 }
1487
1488 if (args == 0 || strcmp(argv[0], "all") == 0)
1489 val = EMPTY_ALL;
1490 else if (strcmp(argv[0], "source") == 0)
1491 val = EMPTY_SOURCE;
1492 else if (strcmp(argv[0], "excluded") == 0)
1493 val = EMPTY_EXCLUDED;
1494 else {
1495 SYNTAX_ERROR("Empty action expects zero arguments, or one"
1496 "argument containing \"all\", \"source\", or \"excluded\""
1497 "\n");
1498 return 0;
1499 }
1500
1501 empty_data = malloc(sizeof(*empty_data));
1502 if (empty_data == NULL)
1503 MEM_ERROR();
1504
1505 empty_data->val = val;
1506 *data = empty_data;
1507
1508 return 1;
1509 }
1510
1511
eval_empty_actions(struct dir_info * root,struct dir_ent * dir_ent)1512 int eval_empty_actions(struct dir_info *root, struct dir_ent *dir_ent)
1513 {
1514 int i, match = 0;
1515 struct action_data action_data;
1516 struct empty_data *data;
1517 struct dir_info *dir = dir_ent->dir;
1518
1519 /*
1520 * Empty action only works on empty directories
1521 */
1522 if (dir->count != 0)
1523 return 0;
1524
1525 action_data.name = dir_ent->name;
1526 action_data.pathname = strdup(pathname(dir_ent));
1527 action_data.subpath = strdup(subpathname(dir_ent));
1528 action_data.buf = &dir_ent->inode->buf;
1529 action_data.depth = dir_ent->our_dir->depth;
1530 action_data.dir_ent = dir_ent;
1531 action_data.root = root;
1532
1533 for (i = 0; i < empty_count && !match; i++) {
1534 data = empty_spec[i].data;
1535
1536 /*
1537 * determine the cause of the empty directory and evaluate
1538 * the empty action specified. Three empty actions:
1539 * - EMPTY_SOURCE: empty action triggers only if the directory
1540 * was originally empty, i.e directories that are empty
1541 * only due to excluding are ignored.
1542 * - EMPTY_EXCLUDED: empty action triggers only if the directory
1543 * is empty because of excluding, i.e. directories that
1544 * were originally empty are ignored.
1545 * - EMPTY_ALL (the default): empty action triggers if the
1546 * directory is empty, irrespective of the reason, i.e.
1547 * the directory could have been originally empty or could
1548 * be empty due to excluding.
1549 */
1550 if ((data->val == EMPTY_EXCLUDED && !dir->excluded) ||
1551 (data->val == EMPTY_SOURCE && dir->excluded))
1552 continue;
1553
1554 match = eval_expr_top(&empty_spec[i], &action_data);
1555 }
1556
1557 free(action_data.pathname);
1558 free(action_data.subpath);
1559
1560 return match;
1561 }
1562
1563
1564 /*
1565 * Move specific action code
1566 */
1567 static struct move_ent *move_list = NULL;
1568
1569
move_actions()1570 int move_actions()
1571 {
1572 return move_count;
1573 }
1574
1575
move_pathname(struct move_ent * move)1576 static char *move_pathname(struct move_ent *move)
1577 {
1578 struct dir_info *dest;
1579 char *name, *pathname;
1580 int res;
1581
1582 dest = (move->ops & ACTION_MOVE_MOVE) ?
1583 move->dest : move->dir_ent->our_dir;
1584 name = (move->ops & ACTION_MOVE_RENAME) ?
1585 move->name : move->dir_ent->name;
1586
1587 if(dest->subpath[0] != '\0')
1588 res = asprintf(&pathname, "%s/%s", dest->subpath, name);
1589 else
1590 res = asprintf(&pathname, "/%s", name);
1591
1592 if(res == -1)
1593 BAD_ERROR("asprintf failed in move_pathname\n");
1594
1595 return pathname;
1596 }
1597
1598
get_comp(char ** pathname)1599 static char *get_comp(char **pathname)
1600 {
1601 char *path = *pathname, *start;
1602
1603 while(*path == '/')
1604 path ++;
1605
1606 if(*path == '\0')
1607 return NULL;
1608
1609 start = path;
1610 while(*path != '/' && *path != '\0')
1611 path ++;
1612
1613 *pathname = path;
1614 return strndup(start, path - start);
1615 }
1616
1617
lookup_comp(char * comp,struct dir_info * dest)1618 static struct dir_ent *lookup_comp(char *comp, struct dir_info *dest)
1619 {
1620 struct dir_ent *dir_ent;
1621
1622 for(dir_ent = dest->list; dir_ent; dir_ent = dir_ent->next)
1623 if(strcmp(comp, dir_ent->name) == 0)
1624 break;
1625
1626 return dir_ent;
1627 }
1628
1629
eval_move(struct action_data * action_data,struct move_ent * move,struct dir_info * root,struct dir_ent * dir_ent,char * pathname)1630 void eval_move(struct action_data *action_data, struct move_ent *move,
1631 struct dir_info *root, struct dir_ent *dir_ent, char *pathname)
1632 {
1633 struct dir_info *dest, *source = dir_ent->our_dir;
1634 struct dir_ent *comp_ent;
1635 char *comp, *path = pathname;
1636
1637 /*
1638 * Walk pathname to get the destination directory
1639 *
1640 * Like the mv command, if the last component exists and it
1641 * is a directory, then move the file into that directory,
1642 * otherwise, move the file into parent directory of the last
1643 * component and rename to the last component.
1644 */
1645 if (pathname[0] == '/')
1646 /* absolute pathname, walk from root directory */
1647 dest = root;
1648 else
1649 /* relative pathname, walk from current directory */
1650 dest = source;
1651
1652 for(comp = get_comp(&pathname); comp; free(comp),
1653 comp = get_comp(&pathname)) {
1654
1655 if (strcmp(comp, ".") == 0)
1656 continue;
1657
1658 if (strcmp(comp, "..") == 0) {
1659 /* if we're in the root directory then ignore */
1660 if(dest->depth > 1)
1661 dest = dest->dir_ent->our_dir;
1662 continue;
1663 }
1664
1665 /*
1666 * Look up comp in current directory, if it exists and it is a
1667 * directory continue walking the pathname, otherwise exit,
1668 * we've walked as far as we can go, normally this is because
1669 * we've arrived at the leaf component which we are going to
1670 * rename source to
1671 */
1672 comp_ent = lookup_comp(comp, dest);
1673 if (comp_ent == NULL || (comp_ent->inode->buf.st_mode & S_IFMT)
1674 != S_IFDIR)
1675 break;
1676
1677 dest = comp_ent->dir;
1678 }
1679
1680 if(comp) {
1681 /* Leaf component? If so we're renaming to this */
1682 char *remainder = get_comp(&pathname);
1683 free(remainder);
1684
1685 if(remainder) {
1686 /*
1687 * trying to move source to a subdirectory of
1688 * comp, but comp either doesn't exist, or it isn't
1689 * a directory, which is impossible
1690 */
1691 if (comp_ent == NULL)
1692 ERROR("Move action: cannot move %s to %s, no "
1693 "such directory %s\n",
1694 action_data->subpath, path, comp);
1695 else
1696 ERROR("Move action: cannot move %s to %s, %s "
1697 "is not a directory\n",
1698 action_data->subpath, path, comp);
1699 free(comp);
1700 return;
1701 }
1702
1703 /*
1704 * Multiple move actions triggering on one file can be merged
1705 * if one is a RENAME and the other is a MOVE. Multiple RENAMEs
1706 * can only merge if they're doing the same thing
1707 */
1708 if(move->ops & ACTION_MOVE_RENAME) {
1709 if(strcmp(comp, move->name) != 0) {
1710 char *conf_path = move_pathname(move);
1711 ERROR("Move action: Cannot move %s to %s, "
1712 "conflicting move, already moving "
1713 "to %s via another move action!\n",
1714 action_data->subpath, path, conf_path);
1715 free(conf_path);
1716 free(comp);
1717 return;
1718 }
1719 free(comp);
1720 } else {
1721 move->name = comp;
1722 move->ops |= ACTION_MOVE_RENAME;
1723 }
1724 }
1725
1726 if(dest != source) {
1727 /*
1728 * Multiple move actions triggering on one file can be merged
1729 * if one is a RENAME and the other is a MOVE. Multiple MOVEs
1730 * can only merge if they're doing the same thing
1731 */
1732 if(move->ops & ACTION_MOVE_MOVE) {
1733 if(dest != move->dest) {
1734 char *conf_path = move_pathname(move);
1735 ERROR("Move action: Cannot move %s to %s, "
1736 "conflicting move, already moving "
1737 "to %s via another move action!\n",
1738 action_data->subpath, path, conf_path);
1739 free(conf_path);
1740 return;
1741 }
1742 } else {
1743 move->dest = dest;
1744 move->ops |= ACTION_MOVE_MOVE;
1745 }
1746 }
1747 }
1748
1749
subdirectory(struct dir_info * source,struct dir_info * dest)1750 static int subdirectory(struct dir_info *source, struct dir_info *dest)
1751 {
1752 if(source == NULL)
1753 return 0;
1754
1755 return strlen(source->subpath) <= strlen(dest->subpath) &&
1756 (dest->subpath[strlen(source->subpath)] == '/' ||
1757 dest->subpath[strlen(source->subpath)] == '\0') &&
1758 strncmp(source->subpath, dest->subpath,
1759 strlen(source->subpath)) == 0;
1760 }
1761
1762
eval_move_actions(struct dir_info * root,struct dir_ent * dir_ent)1763 void eval_move_actions(struct dir_info *root, struct dir_ent *dir_ent)
1764 {
1765 int i;
1766 struct action_data action_data;
1767 struct move_ent *move = NULL;
1768
1769 action_data.name = dir_ent->name;
1770 action_data.pathname = strdup(pathname(dir_ent));
1771 action_data.subpath = strdup(subpathname(dir_ent));
1772 action_data.buf = &dir_ent->inode->buf;
1773 action_data.depth = dir_ent->our_dir->depth;
1774 action_data.dir_ent = dir_ent;
1775 action_data.root = root;
1776
1777 /*
1778 * Evaluate each move action against the current file. For any
1779 * move actions that match don't actually perform the move now, but,
1780 * store it, and execute all the stored move actions together once the
1781 * directory scan is complete. This is done to ensure each separate
1782 * move action does not nondeterministically interfere with other move
1783 * actions. Each move action is considered to act independently, and
1784 * each move action sees the directory tree in the same state.
1785 */
1786 for (i = 0; i < move_count; i++) {
1787 struct action *action = &move_spec[i];
1788 int match = eval_expr_top(action, &action_data);
1789
1790 if(match) {
1791 if(move == NULL) {
1792 move = malloc(sizeof(*move));
1793 if(move == NULL)
1794 MEM_ERROR();
1795
1796 move->ops = 0;
1797 move->dir_ent = dir_ent;
1798 }
1799 eval_move(&action_data, move, root, dir_ent,
1800 action->argv[0]);
1801 }
1802 }
1803
1804 if(move) {
1805 struct dir_ent *comp_ent;
1806 struct dir_info *dest;
1807 char *name;
1808
1809 /*
1810 * Move contains the result of all triggered move actions.
1811 * Check the destination doesn't already exist
1812 */
1813 if(move->ops == 0) {
1814 free(move);
1815 goto finish;
1816 }
1817
1818 dest = (move->ops & ACTION_MOVE_MOVE) ?
1819 move->dest : dir_ent->our_dir;
1820 name = (move->ops & ACTION_MOVE_RENAME) ?
1821 move->name : dir_ent->name;
1822 comp_ent = lookup_comp(name, dest);
1823 if(comp_ent) {
1824 char *conf_path = move_pathname(move);
1825 ERROR("Move action: Cannot move %s to %s, "
1826 "destination already exists\n",
1827 action_data.subpath, conf_path);
1828 free(conf_path);
1829 free(move);
1830 goto finish;
1831 }
1832
1833 /*
1834 * If we're moving a directory, check we're not moving it to a
1835 * subdirectory of itself
1836 */
1837 if(subdirectory(dir_ent->dir, dest)) {
1838 char *conf_path = move_pathname(move);
1839 ERROR("Move action: Cannot move %s to %s, this is a "
1840 "subdirectory of itself\n",
1841 action_data.subpath, conf_path);
1842 free(conf_path);
1843 free(move);
1844 goto finish;
1845 }
1846 move->next = move_list;
1847 move_list = move;
1848 }
1849
1850 finish:
1851 free(action_data.pathname);
1852 free(action_data.subpath);
1853 }
1854
1855
move_dir(struct dir_ent * dir_ent)1856 static void move_dir(struct dir_ent *dir_ent)
1857 {
1858 struct dir_info *dir = dir_ent->dir;
1859 struct dir_ent *comp_ent;
1860
1861 /* update our directory's subpath name */
1862 free(dir->subpath);
1863 dir->subpath = strdup(subpathname(dir_ent));
1864
1865 /* recursively update the subpaths of any sub-directories */
1866 for(comp_ent = dir->list; comp_ent; comp_ent = comp_ent->next)
1867 if(comp_ent->dir)
1868 move_dir(comp_ent);
1869 }
1870
1871
move_file(struct move_ent * move_ent)1872 static void move_file(struct move_ent *move_ent)
1873 {
1874 struct dir_ent *dir_ent = move_ent->dir_ent;
1875
1876 if(move_ent->ops & ACTION_MOVE_MOVE) {
1877 struct dir_ent *comp_ent, *prev = NULL;
1878 struct dir_info *source = dir_ent->our_dir,
1879 *dest = move_ent->dest;
1880 char *filename = pathname(dir_ent);
1881
1882 /*
1883 * If we're moving a directory, check we're not moving it to a
1884 * subdirectory of itself
1885 */
1886 if(subdirectory(dir_ent->dir, dest)) {
1887 char *conf_path = move_pathname(move_ent);
1888 ERROR("Move action: Cannot move %s to %s, this is a "
1889 "subdirectory of itself\n",
1890 subpathname(dir_ent), conf_path);
1891 free(conf_path);
1892 return;
1893 }
1894
1895 /* Remove the file from source directory */
1896 for(comp_ent = source->list; comp_ent != dir_ent;
1897 prev = comp_ent, comp_ent = comp_ent->next);
1898
1899 if(prev)
1900 prev->next = comp_ent->next;
1901 else
1902 source->list = comp_ent->next;
1903
1904 source->count --;
1905 if((comp_ent->inode->buf.st_mode & S_IFMT) == S_IFDIR)
1906 source->directory_count --;
1907
1908 /* Add the file to dest directory */
1909 comp_ent->next = dest->list;
1910 dest->list = comp_ent;
1911 comp_ent->our_dir = dest;
1912
1913 dest->count ++;
1914 if((comp_ent->inode->buf.st_mode & S_IFMT) == S_IFDIR)
1915 dest->directory_count ++;
1916
1917 /*
1918 * We've moved the file, and so we can't now use the
1919 * parent directory's pathname to calculate the pathname
1920 */
1921 if(dir_ent->nonstandard_pathname == NULL) {
1922 dir_ent->nonstandard_pathname = strdup(filename);
1923 if(dir_ent->source_name) {
1924 free(dir_ent->source_name);
1925 dir_ent->source_name = NULL;
1926 }
1927 }
1928 }
1929
1930 if(move_ent->ops & ACTION_MOVE_RENAME) {
1931 /*
1932 * If we're using name in conjunction with the parent
1933 * directory's pathname to calculate the pathname, we need
1934 * to use source_name to override. Otherwise it's already being
1935 * over-ridden
1936 */
1937 if(dir_ent->nonstandard_pathname == NULL &&
1938 dir_ent->source_name == NULL)
1939 dir_ent->source_name = dir_ent->name;
1940 else
1941 free(dir_ent->name);
1942
1943 dir_ent->name = move_ent->name;
1944 }
1945
1946 if(dir_ent->dir)
1947 /*
1948 * dir_ent is a directory, and we have to recursively fix-up
1949 * its subpath, and the subpaths of all of its sub-directories
1950 */
1951 move_dir(dir_ent);
1952 }
1953
1954
do_move_actions()1955 void do_move_actions()
1956 {
1957 while(move_list) {
1958 struct move_ent *temp = move_list;
1959 struct dir_info *dest = (move_list->ops & ACTION_MOVE_MOVE) ?
1960 move_list->dest : move_list->dir_ent->our_dir;
1961 char *name = (move_list->ops & ACTION_MOVE_RENAME) ?
1962 move_list->name : move_list->dir_ent->name;
1963 struct dir_ent *comp_ent = lookup_comp(name, dest);
1964 if(comp_ent) {
1965 char *conf_path = move_pathname(move_list);
1966 ERROR("Move action: Cannot move %s to %s, "
1967 "destination already exists\n",
1968 subpathname(move_list->dir_ent), conf_path);
1969 free(conf_path);
1970 } else
1971 move_file(move_list);
1972
1973 move_list = move_list->next;
1974 free(temp);
1975 }
1976 }
1977
1978
1979 /*
1980 * Prune specific action code
1981 */
prune_actions()1982 int prune_actions()
1983 {
1984 return prune_count;
1985 }
1986
1987
eval_prune_actions(struct dir_info * root,struct dir_ent * dir_ent)1988 int eval_prune_actions(struct dir_info *root, struct dir_ent *dir_ent)
1989 {
1990 int i, match = 0;
1991 struct action_data action_data;
1992
1993 action_data.name = dir_ent->name;
1994 action_data.pathname = strdup(pathname(dir_ent));
1995 action_data.subpath = strdup(subpathname(dir_ent));
1996 action_data.buf = &dir_ent->inode->buf;
1997 action_data.depth = dir_ent->our_dir->depth;
1998 action_data.dir_ent = dir_ent;
1999 action_data.root = root;
2000
2001 for (i = 0; i < prune_count && !match; i++)
2002 match = eval_expr_top(&prune_spec[i], &action_data);
2003
2004 free(action_data.pathname);
2005 free(action_data.subpath);
2006
2007 return match;
2008 }
2009
2010
2011 /*
2012 * Noop specific action code
2013 */
noop_action(struct action * action,struct dir_ent * dir_ent)2014 static void noop_action(struct action *action, struct dir_ent *dir_ent)
2015 {
2016 }
2017
2018
2019 /*
2020 * General test evaluation code
2021 */
2022
2023 /*
2024 * A number can be of the form [range]number[size]
2025 * [range] is either:
2026 * '<' or '-', match on less than number
2027 * '>' or '+', match on greater than number
2028 * '' (nothing), match on exactly number
2029 * [size] is either:
2030 * '' (nothing), number
2031 * 'k' or 'K', number * 2^10
2032 * 'm' or 'M', number * 2^20
2033 * 'g' or 'G', number * 2^30
2034 */
parse_number(char * start,long long * size,int * range,char ** error)2035 static int parse_number(char *start, long long *size, int *range, char **error)
2036 {
2037 char *end;
2038 long long number;
2039
2040 if (*start == '>' || *start == '+') {
2041 *range = NUM_GREATER;
2042 start ++;
2043 } else if (*start == '<' || *start == '-') {
2044 *range = NUM_LESS;
2045 start ++;
2046 } else
2047 *range = NUM_EQ;
2048
2049 errno = 0; /* To enable failure after call to be determined */
2050 number = strtoll(start, &end, 10);
2051
2052 if((errno == ERANGE && (number == LLONG_MAX || number == LLONG_MIN))
2053 || (errno != 0 && number == 0)) {
2054 /* long long underflow or overflow in conversion, or other
2055 * conversion error.
2056 * Note: we don't check for LLONG_MIN and LLONG_MAX only
2057 * because strtoll can validly return that if the
2058 * user used these values
2059 */
2060 *error = "Long long underflow, overflow or other conversion "
2061 "error";
2062 return 0;
2063 }
2064
2065 if (end == start) {
2066 /* Couldn't read any number */
2067 *error = "Number expected";
2068 return 0;
2069 }
2070
2071 switch (end[0]) {
2072 case 'g':
2073 case 'G':
2074 number *= 1024;
2075 case 'm':
2076 case 'M':
2077 number *= 1024;
2078 case 'k':
2079 case 'K':
2080 number *= 1024;
2081
2082 if (end[1] != '\0') {
2083 *error = "Trailing junk after size specifier";
2084 return 0;
2085 }
2086
2087 break;
2088 case '\0':
2089 break;
2090 default:
2091 *error = "Trailing junk after number";
2092 return 0;
2093 }
2094
2095 *size = number;
2096
2097 return 1;
2098 }
2099
2100
parse_number_arg(struct test_entry * test,struct atom * atom)2101 static int parse_number_arg(struct test_entry *test, struct atom *atom)
2102 {
2103 struct test_number_arg *number;
2104 long long size;
2105 int range;
2106 char *error;
2107 int res = parse_number(atom->argv[0], &size, &range, &error);
2108
2109 if (res == 0) {
2110 TEST_SYNTAX_ERROR(test, 0, "%s\n", error);
2111 return 0;
2112 }
2113
2114 number = malloc(sizeof(*number));
2115 if (number == NULL)
2116 MEM_ERROR();
2117
2118 number->range = range;
2119 number->size = size;
2120
2121 atom->data = number;
2122
2123 return 1;
2124 }
2125
2126
parse_range_args(struct test_entry * test,struct atom * atom)2127 static int parse_range_args(struct test_entry *test, struct atom *atom)
2128 {
2129 struct test_range_args *range;
2130 long long start, end;
2131 int type;
2132 int res;
2133 char *error;
2134
2135 res = parse_number(atom->argv[0], &start, &type, &error);
2136 if (res == 0) {
2137 TEST_SYNTAX_ERROR(test, 0, "%s\n", error);
2138 return 0;
2139 }
2140
2141 if (type != NUM_EQ) {
2142 TEST_SYNTAX_ERROR(test, 0, "Range specifier (<, >, -, +) not "
2143 "expected\n");
2144 return 0;
2145 }
2146
2147 res = parse_number(atom->argv[1], &end, &type, &error);
2148 if (res == 0) {
2149 TEST_SYNTAX_ERROR(test, 1, "%s\n", error);
2150 return 0;
2151 }
2152
2153 if (type != NUM_EQ) {
2154 TEST_SYNTAX_ERROR(test, 1, "Range specifier (<, >, -, +) not "
2155 "expected\n");
2156 return 0;
2157 }
2158
2159 range = malloc(sizeof(*range));
2160 if (range == NULL)
2161 MEM_ERROR();
2162
2163 range->start = start;
2164 range->end = end;
2165
2166 atom->data = range;
2167
2168 return 1;
2169 }
2170
2171
2172 /*
2173 * Generic test code macro
2174 */
2175 #define TEST_FN(NAME, MATCH, CODE) \
2176 static int NAME##_fn(struct atom *atom, struct action_data *action_data) \
2177 { \
2178 /* test operates on MATCH file types only */ \
2179 if (!file_type_match(action_data->buf->st_mode, MATCH)) \
2180 return 0; \
2181 \
2182 CODE \
2183 }
2184
2185 /*
2186 * Generic test code macro testing VAR for size (eq, less than, greater than)
2187 */
2188 #define TEST_VAR_FN(NAME, MATCH, VAR) TEST_FN(NAME, MATCH, \
2189 { \
2190 int match = 0; \
2191 struct test_number_arg *number = atom->data; \
2192 \
2193 switch (number->range) { \
2194 case NUM_EQ: \
2195 match = VAR == number->size; \
2196 break; \
2197 case NUM_LESS: \
2198 match = VAR < number->size; \
2199 break; \
2200 case NUM_GREATER: \
2201 match = VAR > number->size; \
2202 break; \
2203 } \
2204 \
2205 return match; \
2206 })
2207
2208
2209 /*
2210 * Generic test code macro testing VAR for range [x, y] (value between x and y
2211 * inclusive).
2212 */
2213 #define TEST_VAR_RANGE_FN(NAME, MATCH, VAR) TEST_FN(NAME##_range, MATCH, \
2214 { \
2215 struct test_range_args *range = atom->data; \
2216 \
2217 return range->start <= VAR && VAR <= range->end; \
2218 })
2219
2220
2221 /*
2222 * Name, Pathname and Subpathname test specific code
2223 */
2224
2225 /*
2226 * Add a leading "/" if subpathname and pathname lacks it
2227 */
check_pathname(struct test_entry * test,struct atom * atom)2228 static int check_pathname(struct test_entry *test, struct atom *atom)
2229 {
2230 int res;
2231 char *name;
2232
2233 if(atom->argv[0][0] != '/') {
2234 res = asprintf(&name, "/%s", atom->argv[0]);
2235 if(res == -1)
2236 BAD_ERROR("asprintf failed in check_pathname\n");
2237
2238 free(atom->argv[0]);
2239 atom->argv[0] = name;
2240 }
2241
2242 return 1;
2243 }
2244
2245
2246 TEST_FN(name, ACTION_ALL_LNK, \
2247 return fnmatch(atom->argv[0], action_data->name,
2248 FNM_PATHNAME|FNM_PERIOD|FNM_EXTMATCH) == 0;)
2249
2250 TEST_FN(pathname, ACTION_ALL_LNK, \
2251 return fnmatch(atom->argv[0], action_data->subpath,
2252 FNM_PATHNAME|FNM_PERIOD|FNM_EXTMATCH) == 0;)
2253
2254
count_components(char * path)2255 static int count_components(char *path)
2256 {
2257 int count;
2258
2259 for (count = 0; *path != '\0'; count ++) {
2260 while (*path == '/')
2261 path ++;
2262
2263 while (*path != '\0' && *path != '/')
2264 path ++;
2265 }
2266
2267 return count;
2268 }
2269
2270
get_start(char * s,int n)2271 static char *get_start(char *s, int n)
2272 {
2273 int count;
2274 char *path = s;
2275
2276 for (count = 0; *path != '\0' && count < n; count ++) {
2277 while (*path == '/')
2278 path ++;
2279
2280 while (*path != '\0' && *path != '/')
2281 path ++;
2282 }
2283
2284 if (count == n)
2285 *path = '\0';
2286
2287 return s;
2288 }
2289
2290
subpathname_fn(struct atom * atom,struct action_data * action_data)2291 static int subpathname_fn(struct atom *atom, struct action_data *action_data)
2292 {
2293 return fnmatch(atom->argv[0], get_start(strdupa(action_data->subpath),
2294 count_components(atom->argv[0])),
2295 FNM_PATHNAME|FNM_PERIOD|FNM_EXTMATCH) == 0;
2296 }
2297
2298 /*
2299 * Inode attribute test operations using generic
2300 * TEST_VAR_FN(test name, file scope, attribute name) macro.
2301 * This is for tests that do not need to be specially handled in any way.
2302 * They just take a variable and compare it against a number.
2303 */
2304 TEST_VAR_FN(filesize, ACTION_REG, action_data->buf->st_size)
2305
2306 TEST_VAR_FN(dirsize, ACTION_DIR, action_data->buf->st_size)
2307
2308 TEST_VAR_FN(size, ACTION_ALL_LNK, action_data->buf->st_size)
2309
2310 TEST_VAR_FN(inode, ACTION_ALL_LNK, action_data->buf->st_ino)
2311
2312 TEST_VAR_FN(nlink, ACTION_ALL_LNK, action_data->buf->st_nlink)
2313
2314 TEST_VAR_FN(fileblocks, ACTION_REG, action_data->buf->st_blocks)
2315
2316 TEST_VAR_FN(dirblocks, ACTION_DIR, action_data->buf->st_blocks)
2317
2318 TEST_VAR_FN(blocks, ACTION_ALL_LNK, action_data->buf->st_blocks)
2319
2320 TEST_VAR_FN(dircount, ACTION_DIR, action_data->dir_ent->dir->count)
2321
2322 TEST_VAR_FN(depth, ACTION_ALL_LNK, action_data->depth)
2323
2324 TEST_VAR_RANGE_FN(filesize, ACTION_REG, action_data->buf->st_size)
2325
2326 TEST_VAR_RANGE_FN(dirsize, ACTION_DIR, action_data->buf->st_size)
2327
2328 TEST_VAR_RANGE_FN(size, ACTION_ALL_LNK, action_data->buf->st_size)
2329
2330 TEST_VAR_RANGE_FN(inode, ACTION_ALL_LNK, action_data->buf->st_ino)
2331
2332 TEST_VAR_RANGE_FN(nlink, ACTION_ALL_LNK, action_data->buf->st_nlink)
2333
2334 TEST_VAR_RANGE_FN(fileblocks, ACTION_REG, action_data->buf->st_blocks)
2335
2336 TEST_VAR_RANGE_FN(dirblocks, ACTION_DIR, action_data->buf->st_blocks)
2337
2338 TEST_VAR_RANGE_FN(blocks, ACTION_ALL_LNK, action_data->buf->st_blocks)
2339
2340 TEST_VAR_RANGE_FN(gid, ACTION_ALL_LNK, action_data->buf->st_gid)
2341
2342 TEST_VAR_RANGE_FN(uid, ACTION_ALL_LNK, action_data->buf->st_uid)
2343
2344 TEST_VAR_RANGE_FN(depth, ACTION_ALL_LNK, action_data->depth)
2345
2346 TEST_VAR_RANGE_FN(dircount, ACTION_DIR, action_data->dir_ent->dir->count)
2347
2348 /*
2349 * uid specific test code
2350 */
2351 TEST_VAR_FN(uid, ACTION_ALL_LNK, action_data->buf->st_uid)
2352
parse_uid_arg(struct test_entry * test,struct atom * atom)2353 static int parse_uid_arg(struct test_entry *test, struct atom *atom)
2354 {
2355 struct test_number_arg *number;
2356 long long size;
2357 int range;
2358 char *error;
2359
2360 if(parse_number(atom->argv[0], &size, &range, &error)) {
2361 /* managed to fully parse argument as a number */
2362 if(size < 0 || size > (((long long) 1 << 32) - 1)) {
2363 TEST_SYNTAX_ERROR(test, 1, "Numeric uid out of "
2364 "range\n");
2365 return 0;
2366 }
2367 } else {
2368 /* couldn't parse (fully) as a number, is it a user name? */
2369 struct passwd *uid = getpwnam(atom->argv[0]);
2370 if(uid) {
2371 size = uid->pw_uid;
2372 range = NUM_EQ;
2373 } else {
2374 TEST_SYNTAX_ERROR(test, 1, "Invalid uid or unknown "
2375 "user\n");
2376 return 0;
2377 }
2378 }
2379
2380 number = malloc(sizeof(*number));
2381 if(number == NULL)
2382 MEM_ERROR();
2383
2384 number->range = range;
2385 number->size= size;
2386
2387 atom->data = number;
2388
2389 return 1;
2390 }
2391
2392
2393 /*
2394 * gid specific test code
2395 */
2396 TEST_VAR_FN(gid, ACTION_ALL_LNK, action_data->buf->st_gid)
2397
parse_gid_arg(struct test_entry * test,struct atom * atom)2398 static int parse_gid_arg(struct test_entry *test, struct atom *atom)
2399 {
2400 struct test_number_arg *number;
2401 long long size;
2402 int range;
2403 char *error;
2404
2405 if(parse_number(atom->argv[0], &size, &range, &error)) {
2406 /* managed to fully parse argument as a number */
2407 if(size < 0 || size > (((long long) 1 << 32) - 1)) {
2408 TEST_SYNTAX_ERROR(test, 1, "Numeric gid out of "
2409 "range\n");
2410 return 0;
2411 }
2412 } else {
2413 /* couldn't parse (fully) as a number, is it a group name? */
2414 struct group *gid = getgrnam(atom->argv[0]);
2415 if(gid) {
2416 size = gid->gr_gid;
2417 range = NUM_EQ;
2418 } else {
2419 TEST_SYNTAX_ERROR(test, 1, "Invalid gid or unknown "
2420 "group\n");
2421 return 0;
2422 }
2423 }
2424
2425 number = malloc(sizeof(*number));
2426 if(number == NULL)
2427 MEM_ERROR();
2428
2429 number->range = range;
2430 number->size= size;
2431
2432 atom->data = number;
2433
2434 return 1;
2435 }
2436
2437
2438 /*
2439 * Type test specific code
2440 */
2441 struct type_entry type_table[] = {
2442 { S_IFSOCK, 's' },
2443 { S_IFLNK, 'l' },
2444 { S_IFREG, 'f' },
2445 { S_IFBLK, 'b' },
2446 { S_IFDIR, 'd' },
2447 { S_IFCHR, 'c' },
2448 { S_IFIFO, 'p' },
2449 { 0, 0 },
2450 };
2451
2452
parse_type_arg(struct test_entry * test,struct atom * atom)2453 static int parse_type_arg(struct test_entry *test, struct atom *atom)
2454 {
2455 int i;
2456
2457 if (strlen(atom->argv[0]) != 1)
2458 goto failed;
2459
2460 for(i = 0; type_table[i].type != 0; i++)
2461 if (type_table[i].type == atom->argv[0][0])
2462 break;
2463
2464 atom->data = &type_table[i];
2465
2466 if(type_table[i].type != 0)
2467 return 1;
2468
2469 failed:
2470 TEST_SYNTAX_ERROR(test, 0, "Unexpected file type, expected 'f', 'd', "
2471 "'c', 'b', 'l', 's' or 'p'\n");
2472 return 0;
2473 }
2474
2475
type_fn(struct atom * atom,struct action_data * action_data)2476 static int type_fn(struct atom *atom, struct action_data *action_data)
2477 {
2478 struct type_entry *type = atom->data;
2479
2480 return (action_data->buf->st_mode & S_IFMT) == type->value;
2481 }
2482
2483
2484 /*
2485 * True test specific code
2486 */
true_fn(struct atom * atom,struct action_data * action_data)2487 static int true_fn(struct atom *atom, struct action_data *action_data)
2488 {
2489 return 1;
2490 }
2491
2492
2493 /*
2494 * False test specific code
2495 */
false_fn(struct atom * atom,struct action_data * action_data)2496 static int false_fn(struct atom *atom, struct action_data *action_data)
2497 {
2498 return 0;
2499 }
2500
2501
2502 /*
2503 * File test specific code
2504 */
parse_file_arg(struct test_entry * test,struct atom * atom)2505 static int parse_file_arg(struct test_entry *test, struct atom *atom)
2506 {
2507 int res;
2508 regex_t *preg = malloc(sizeof(regex_t));
2509
2510 if (preg == NULL)
2511 MEM_ERROR();
2512
2513 res = regcomp(preg, atom->argv[0], REG_EXTENDED);
2514 if (res) {
2515 char str[1024]; /* overflow safe */
2516
2517 regerror(res, preg, str, 1024);
2518 free(preg);
2519 TEST_SYNTAX_ERROR(test, 0, "invalid regex \"%s\" because "
2520 "\"%s\"\n", atom->argv[0], str);
2521 return 0;
2522 }
2523
2524 atom->data = preg;
2525
2526 return 1;
2527 }
2528
2529
file_fn(struct atom * atom,struct action_data * action_data)2530 static int file_fn(struct atom *atom, struct action_data *action_data)
2531 {
2532 int child, res, size = 0, status;
2533 int pipefd[2];
2534 char *buffer = NULL;
2535 regex_t *preg = atom->data;
2536
2537 res = pipe(pipefd);
2538 if (res == -1)
2539 BAD_ERROR("file_fn pipe failed\n");
2540
2541 child = fork();
2542 if (child == -1)
2543 BAD_ERROR("file_fn fork_failed\n");
2544
2545 if (child == 0) {
2546 /*
2547 * Child process
2548 * Connect stdout to pipefd[1] and execute file command
2549 */
2550 close(STDOUT_FILENO);
2551 res = dup(pipefd[1]);
2552 if (res == -1)
2553 exit(EXIT_FAILURE);
2554
2555 execlp("file", "file", "-b", action_data->pathname,
2556 (char *) NULL);
2557 exit(EXIT_FAILURE);
2558 }
2559
2560 /*
2561 * Parent process. Read stdout from file command
2562 */
2563 close(pipefd[1]);
2564
2565 do {
2566 buffer = realloc(buffer, size + 512);
2567 if (buffer == NULL)
2568 MEM_ERROR();
2569
2570 res = read_bytes(pipefd[0], buffer + size, 512);
2571
2572 if (res == -1)
2573 BAD_ERROR("file_fn pipe read error\n");
2574
2575 size += 512;
2576
2577 } while (res == 512);
2578
2579 size = size + res - 512;
2580
2581 buffer[size] = '\0';
2582
2583 res = waitpid(child, &status, 0);
2584
2585 if (res == -1)
2586 BAD_ERROR("file_fn waitpid failed\n");
2587
2588 if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
2589 BAD_ERROR("file_fn file returned error\n");
2590
2591 close(pipefd[0]);
2592
2593 res = regexec(preg, buffer, (size_t) 0, NULL, 0);
2594
2595 free(buffer);
2596
2597 return res == 0;
2598 }
2599
2600
2601 /*
2602 * Exec test specific code
2603 */
exec_fn(struct atom * atom,struct action_data * action_data)2604 static int exec_fn(struct atom *atom, struct action_data *action_data)
2605 {
2606 int child, i, res, status;
2607
2608 child = fork();
2609 if (child == -1)
2610 BAD_ERROR("exec_fn fork_failed\n");
2611
2612 if (child == 0) {
2613 /*
2614 * Child process
2615 * redirect stdin, stdout & stderr to /dev/null and
2616 * execute atom->argv[0]
2617 */
2618 int fd = open("/dev/null", O_RDWR);
2619 if(fd == -1)
2620 exit(EXIT_FAILURE);
2621
2622 close(STDIN_FILENO);
2623 close(STDOUT_FILENO);
2624 close(STDERR_FILENO);
2625 for(i = 0; i < 3; i++) {
2626 res = dup(fd);
2627 if (res == -1)
2628 exit(EXIT_FAILURE);
2629 }
2630 close(fd);
2631
2632 /*
2633 * Create environment variables
2634 * NAME: name of file
2635 * PATHNAME: pathname of file relative to squashfs root
2636 * SOURCE_PATHNAME: the pathname of the file in the source
2637 * directory
2638 */
2639 res = setenv("NAME", action_data->name, 1);
2640 if(res == -1)
2641 exit(EXIT_FAILURE);
2642
2643 res = setenv("PATHNAME", action_data->subpath, 1);
2644 if(res == -1)
2645 exit(EXIT_FAILURE);
2646
2647 res = setenv("SOURCE_PATHNAME", action_data->pathname, 1);
2648 if(res == -1)
2649 exit(EXIT_FAILURE);
2650
2651 execl("/bin/sh", "sh", "-c", atom->argv[0], (char *) NULL);
2652 exit(EXIT_FAILURE);
2653 }
2654
2655 /*
2656 * Parent process.
2657 */
2658
2659 res = waitpid(child, &status, 0);
2660
2661 if (res == -1)
2662 BAD_ERROR("exec_fn waitpid failed\n");
2663
2664 return WIFEXITED(status) ? WEXITSTATUS(status) == 0 : 0;
2665 }
2666
2667
2668 /*
2669 * Symbolic link specific test code
2670 */
2671
2672 /*
2673 * Walk the supplied pathname and return the directory entry corresponding
2674 * to the pathname. If any symlinks are encountered whilst walking the
2675 * pathname, then recursively walk these, to obtain the fully
2676 * dereferenced canonicalised directory entry.
2677 *
2678 * If follow_path fails to walk a pathname either because a component
2679 * doesn't exist, it is a non directory component when a directory
2680 * component is expected, a symlink with an absolute path is encountered,
2681 * or a symlink is encountered which cannot be recursively walked due to
2682 * the above failures, then return NULL.
2683 */
follow_path(struct dir_info * dir,char * pathname)2684 static struct dir_ent *follow_path(struct dir_info *dir, char *pathname)
2685 {
2686 char *comp, *path = pathname;
2687 struct dir_ent *dir_ent = NULL;
2688
2689 /* We cannot follow absolute paths */
2690 if(pathname[0] == '/')
2691 return NULL;
2692
2693 for(comp = get_comp(&path); comp; free(comp), comp = get_comp(&path)) {
2694 if(strcmp(comp, ".") == 0)
2695 continue;
2696
2697 if(strcmp(comp, "..") == 0) {
2698 /* Move to parent if we're not in the root directory */
2699 if(dir->depth > 1) {
2700 dir = dir->dir_ent->our_dir;
2701 dir_ent = NULL; /* lazily eval at loop exit */
2702 continue;
2703 } else
2704 /* Failed to walk pathname */
2705 return NULL;
2706 }
2707
2708 /* Lookup comp in current directory */
2709 dir_ent = lookup_comp(comp, dir);
2710 if(dir_ent == NULL)
2711 /* Doesn't exist, failed to walk pathname */
2712 return NULL;
2713
2714 if((dir_ent->inode->buf.st_mode & S_IFMT) == S_IFLNK) {
2715 /* Symbolic link, try to walk it */
2716 dir_ent = follow_path(dir, dir_ent->inode->symlink);
2717 if(dir_ent == NULL)
2718 /* Failed to follow symlink */
2719 return NULL;
2720 }
2721
2722 if((dir_ent->inode->buf.st_mode & S_IFMT) != S_IFDIR)
2723 /* Cannot walk further */
2724 break;
2725
2726 dir = dir_ent->dir;
2727 }
2728
2729 /* We will have exited the loop either because we've processed
2730 * all the components, which means we've successfully walked the
2731 * pathname, or because we've hit a non-directory, in which case
2732 * it's success if this is the leaf component */
2733 if(comp) {
2734 free(comp);
2735 comp = get_comp(&path);
2736 free(comp);
2737 if(comp != NULL)
2738 /* Not a leaf component */
2739 return NULL;
2740 } else {
2741 /* Fully walked pathname, dir_ent contains correct value unless
2742 * we've walked to the parent ("..") in which case we need
2743 * to resolve it here */
2744 if(!dir_ent)
2745 dir_ent = dir->dir_ent;
2746 }
2747
2748 return dir_ent;
2749 }
2750
2751
exists_fn(struct atom * atom,struct action_data * action_data)2752 static int exists_fn(struct atom *atom, struct action_data *action_data)
2753 {
2754 /*
2755 * Test if a symlink exists within the output filesystem, that is,
2756 * the symlink has a relative path, and the relative path refers
2757 * to an entry within the output filesystem.
2758 *
2759 * This test function evaluates the path for symlinks - that is it
2760 * follows any symlinks in the path (and any symlinks that it contains
2761 * etc.), to discover the fully dereferenced canonicalised relative
2762 * path.
2763 *
2764 * If any symlinks within the path do not exist or are absolute
2765 * then the symlink is considered to not exist, as it cannot be
2766 * fully dereferenced.
2767 *
2768 * exists operates on symlinks only, other files by definition
2769 * exist
2770 */
2771 if (!file_type_match(action_data->buf->st_mode, ACTION_LNK))
2772 return 1;
2773
2774 /* dereference the symlink, and return TRUE if it exists */
2775 return follow_path(action_data->dir_ent->our_dir,
2776 action_data->dir_ent->inode->symlink) ? 1 : 0;
2777 }
2778
2779
absolute_fn(struct atom * atom,struct action_data * action_data)2780 static int absolute_fn(struct atom *atom, struct action_data *action_data)
2781 {
2782 /*
2783 * Test if a symlink has an absolute path, which by definition
2784 * means the symbolic link may be broken (even if the absolute path
2785 * does point into the filesystem being squashed, because the resultant
2786 * filesystem can be mounted/unsquashed anywhere, it is unlikely the
2787 * absolute path will still point to the right place). If you know that
2788 * an absolute symlink will point to the right place then you don't need
2789 * to use this function, and/or these symlinks can be excluded by
2790 * use of other test operators.
2791 *
2792 * absolute operates on symlinks only, other files by definition
2793 * don't have problems
2794 */
2795 if (!file_type_match(action_data->buf->st_mode, ACTION_LNK))
2796 return 0;
2797
2798 return action_data->dir_ent->inode->symlink[0] == '/';
2799 }
2800
2801
parse_expr_argX(struct test_entry * test,struct atom * atom,int argno)2802 static int parse_expr_argX(struct test_entry *test, struct atom *atom,
2803 int argno)
2804 {
2805 /* Call parse_expr to parse argument, which should be an expression */
2806
2807 /* save the current parser state */
2808 char *save_cur_ptr = cur_ptr;
2809 char *save_source = source;
2810
2811 cur_ptr = source = atom->argv[argno];
2812 atom->data = parse_expr(0);
2813
2814 cur_ptr = save_cur_ptr;
2815 source = save_source;
2816
2817 if(atom->data == NULL) {
2818 /* parse_expr(0) will have reported the exact syntax error,
2819 * but, because we recursively evaluated the expression, it
2820 * will have been reported without the context of the stat
2821 * test(). So here additionally report our failure to parse
2822 * the expression in the stat() test to give context */
2823 TEST_SYNTAX_ERROR(test, 0, "Failed to parse expression\n");
2824 return 0;
2825 }
2826
2827 return 1;
2828 }
2829
2830
parse_expr_arg0(struct test_entry * test,struct atom * atom)2831 static int parse_expr_arg0(struct test_entry *test, struct atom *atom)
2832 {
2833 return parse_expr_argX(test, atom, 0);
2834 }
2835
2836
parse_expr_arg1(struct test_entry * test,struct atom * atom)2837 static int parse_expr_arg1(struct test_entry *test, struct atom *atom)
2838 {
2839 return parse_expr_argX(test, atom, 1);
2840 }
2841
2842
stat_fn(struct atom * atom,struct action_data * action_data)2843 static int stat_fn(struct atom *atom, struct action_data *action_data)
2844 {
2845 struct stat buf;
2846 struct action_data eval_action;
2847 int match, res;
2848
2849 /* evaluate the expression using the context of the inode
2850 * pointed to by the symlink. This allows the inode attributes
2851 * of the file pointed to by the symlink to be evaluated, rather
2852 * than the symlink itself.
2853 *
2854 * Note, stat() deliberately does not evaluate the pathname, name or
2855 * depth of the symlink, these are left with the symlink values.
2856 * This allows stat() to be used on any symlink, rather than
2857 * just symlinks which are contained (if the symlink is *not*
2858 * contained then pathname, name and depth are meaningless as they
2859 * are relative to the filesystem being squashed). */
2860
2861 /* if this isn't a symlink then stat will just return the current
2862 * information, i.e. stat(expr) == expr. This is harmless and
2863 * is better than returning TRUE or FALSE in a non symlink case */
2864 res = stat(action_data->pathname, &buf);
2865 if(res == -1) {
2866 if(expr_log_cmnd(LOG_ENABLED)) {
2867 expr_log(atom->test->name);
2868 expr_log("(");
2869 expr_log_match(0);
2870 expr_log(")");
2871 }
2872 return 0;
2873 }
2874
2875 /* fill in the inode values of the file pointed to by the
2876 * symlink, but, leave everything else the same */
2877 memcpy(&eval_action, action_data, sizeof(struct action_data));
2878 eval_action.buf = &buf;
2879
2880 if(expr_log_cmnd(LOG_ENABLED)) {
2881 expr_log(atom->test->name);
2882 expr_log("(");
2883 match = eval_expr_log(atom->data, &eval_action);
2884 expr_log(")");
2885 } else
2886 match = eval_expr(atom->data, &eval_action);
2887
2888 return match;
2889 }
2890
2891
readlink_fn(struct atom * atom,struct action_data * action_data)2892 static int readlink_fn(struct atom *atom, struct action_data *action_data)
2893 {
2894 int match = 0;
2895 struct dir_ent *dir_ent;
2896 struct action_data eval_action;
2897
2898 /* Dereference the symlink and evaluate the expression in the
2899 * context of the file pointed to by the symlink.
2900 * All attributes are updated to refer to the file that is pointed to.
2901 * Thus the inode attributes, pathname, name and depth all refer to
2902 * the dereferenced file, and not the symlink.
2903 *
2904 * If the symlink cannot be dereferenced because it doesn't exist in
2905 * the output filesystem, or due to some other failure to
2906 * walk the pathname (see follow_path above), then FALSE is returned.
2907 *
2908 * If you wish to evaluate the inode attributes of symlinks which
2909 * exist in the source filestem (but not in the output filesystem then
2910 * use stat instead (see above).
2911 *
2912 * readlink operates on symlinks only */
2913 if (!file_type_match(action_data->buf->st_mode, ACTION_LNK))
2914 goto finish;
2915
2916 /* dereference the symlink, and get the directory entry it points to */
2917 dir_ent = follow_path(action_data->dir_ent->our_dir,
2918 action_data->dir_ent->inode->symlink);
2919 if(dir_ent == NULL)
2920 goto finish;
2921
2922 eval_action.name = dir_ent->name;
2923 eval_action.pathname = strdup(pathname(dir_ent));
2924 eval_action.subpath = strdup(subpathname(dir_ent));
2925 eval_action.buf = &dir_ent->inode->buf;
2926 eval_action.depth = dir_ent->our_dir->depth;
2927 eval_action.dir_ent = dir_ent;
2928 eval_action.root = action_data->root;
2929
2930 if(expr_log_cmnd(LOG_ENABLED)) {
2931 expr_log(atom->test->name);
2932 expr_log("(");
2933 match = eval_expr_log(atom->data, &eval_action);
2934 expr_log(")");
2935 } else
2936 match = eval_expr(atom->data, &eval_action);
2937
2938 free(eval_action.pathname);
2939 free(eval_action.subpath);
2940
2941 return match;
2942
2943 finish:
2944 if(expr_log_cmnd(LOG_ENABLED)) {
2945 expr_log(atom->test->name);
2946 expr_log("(");
2947 expr_log_match(0);
2948 expr_log(")");
2949 }
2950
2951 return 0;
2952 }
2953
2954
eval_fn(struct atom * atom,struct action_data * action_data)2955 static int eval_fn(struct atom *atom, struct action_data *action_data)
2956 {
2957 int match;
2958 char *path = atom->argv[0];
2959 struct dir_ent *dir_ent = action_data->dir_ent;
2960 struct stat *buf = action_data->buf;
2961 struct action_data eval_action;
2962
2963 /* Follow path (arg1) and evaluate the expression (arg2)
2964 * in the context of the file discovered. All attributes are updated
2965 * to refer to the file that is pointed to.
2966 *
2967 * This test operation allows you to add additional context to the
2968 * evaluation of the file being scanned, such as "if current file is
2969 * XXX and the parent is YYY, then ..." Often times you need or
2970 * want to test a combination of file status
2971 *
2972 * If the file referenced by the path does not exist in
2973 * the output filesystem, or some other failure is experienced in
2974 * walking the path (see follow_path above), then FALSE is returned.
2975 *
2976 * If you wish to evaluate the inode attributes of files which
2977 * exist in the source filestem (but not in the output filesystem then
2978 * use stat instead (see above). */
2979
2980 /* try to follow path, and get the directory entry it points to */
2981 if(path[0] == '/') {
2982 /* absolute, walk from root - first skip the leading / */
2983 while(path[0] == '/')
2984 path ++;
2985 if(path[0] == '\0')
2986 dir_ent = action_data->root->dir_ent;
2987 else
2988 dir_ent = follow_path(action_data->root, path);
2989 } else {
2990 /* relative, if first component is ".." walk from parent,
2991 * otherwise walk from dir_ent.
2992 * Note: this has to be handled here because follow_path
2993 * will quite correctly refuse to execute ".." on anything
2994 * which isn't a directory */
2995 if(strncmp(path, "..", 2) == 0 && (path[2] == '\0' ||
2996 path[2] == '/')) {
2997 /* walk from parent */
2998 path += 2;
2999 while(path[0] == '/')
3000 path ++;
3001 if(path[0] == '\0')
3002 dir_ent = dir_ent->our_dir->dir_ent;
3003 else
3004 dir_ent = follow_path(dir_ent->our_dir, path);
3005 } else if(!file_type_match(buf->st_mode, ACTION_DIR))
3006 dir_ent = NULL;
3007 else
3008 dir_ent = follow_path(dir_ent->dir, path);
3009 }
3010
3011 if(dir_ent == NULL) {
3012 if(expr_log_cmnd(LOG_ENABLED)) {
3013 expr_log(atom->test->name);
3014 expr_log("(");
3015 expr_log(atom->argv[0]);
3016 expr_log(",");
3017 expr_log_match(0);
3018 expr_log(")");
3019 }
3020
3021 return 0;
3022 }
3023
3024 eval_action.name = dir_ent->name;
3025 eval_action.pathname = strdup(pathname(dir_ent));
3026 eval_action.subpath = strdup(subpathname(dir_ent));
3027 eval_action.buf = &dir_ent->inode->buf;
3028 eval_action.depth = dir_ent->our_dir->depth;
3029 eval_action.dir_ent = dir_ent;
3030 eval_action.root = action_data->root;
3031
3032 if(expr_log_cmnd(LOG_ENABLED)) {
3033 expr_log(atom->test->name);
3034 expr_log("(");
3035 expr_log(eval_action.subpath);
3036 expr_log(",");
3037 match = eval_expr_log(atom->data, &eval_action);
3038 expr_log(")");
3039 } else
3040 match = eval_expr(atom->data, &eval_action);
3041
3042 free(eval_action.pathname);
3043 free(eval_action.subpath);
3044
3045 return match;
3046 }
3047
3048
3049 /*
3050 * Perm specific test code
3051 */
parse_perm_args(struct test_entry * test,struct atom * atom)3052 static int parse_perm_args(struct test_entry *test, struct atom *atom)
3053 {
3054 int res = 1, mode, op, i;
3055 char *arg;
3056 struct mode_data *head = NULL, *cur = NULL;
3057 struct perm_data *perm_data;
3058
3059 if(atom->args == 0) {
3060 TEST_SYNTAX_ERROR(test, 0, "One or more arguments expected\n");
3061 return 0;
3062 }
3063
3064 switch(atom->argv[0][0]) {
3065 case '-':
3066 op = PERM_ALL;
3067 arg = atom->argv[0] + 1;
3068 break;
3069 case '/':
3070 op = PERM_ANY;
3071 arg = atom->argv[0] + 1;
3072 break;
3073 default:
3074 op = PERM_EXACT;
3075 arg = atom->argv[0];
3076 break;
3077 }
3078
3079 /* try to parse as an octal number */
3080 res = parse_octal_mode_args(atom->args, atom->argv, (void **) &head);
3081 if(res == -1) {
3082 /* parse as sym mode argument */
3083 for(i = 0; i < atom->args && res; i++, arg = atom->argv[i])
3084 res = parse_sym_mode_arg(arg, &head, &cur);
3085 }
3086
3087 if (res == 0)
3088 goto finish;
3089
3090 /*
3091 * Evaluate the symbolic mode against a permission of 0000 octal
3092 */
3093 mode = mode_execute(head, 0);
3094
3095 perm_data = malloc(sizeof(struct perm_data));
3096 if (perm_data == NULL)
3097 MEM_ERROR();
3098
3099 perm_data->op = op;
3100 perm_data->mode = mode;
3101
3102 atom->data = perm_data;
3103
3104 finish:
3105 while(head) {
3106 struct mode_data *tmp = head;
3107 head = head->next;
3108 free(tmp);
3109 }
3110
3111 return res;
3112 }
3113
3114
perm_fn(struct atom * atom,struct action_data * action_data)3115 static int perm_fn(struct atom *atom, struct action_data *action_data)
3116 {
3117 struct perm_data *perm_data = atom->data;
3118 struct stat *buf = action_data->buf;
3119
3120 switch(perm_data->op) {
3121 case PERM_EXACT:
3122 return (buf->st_mode & ~S_IFMT) == perm_data->mode;
3123 case PERM_ALL:
3124 return (buf->st_mode & perm_data->mode) == perm_data->mode;
3125 case PERM_ANY:
3126 default:
3127 /*
3128 * if no permission bits are set in perm_data->mode match
3129 * on any file, this is to be consistent with find, which
3130 * does this to be consistent with the behaviour of
3131 * -perm -000
3132 */
3133 return perm_data->mode == 0 || (buf->st_mode & perm_data->mode);
3134 }
3135 }
3136
3137
3138 #ifdef SQUASHFS_TRACE
dump_parse_tree(struct expr * expr)3139 static void dump_parse_tree(struct expr *expr)
3140 {
3141 int i;
3142
3143 if(expr->type == ATOM_TYPE) {
3144 printf("%s", expr->atom.test->name);
3145 if(expr->atom.args) {
3146 printf("(");
3147 for(i = 0; i < expr->atom.args; i++) {
3148 printf("%s", expr->atom.argv[i]);
3149 if (i + 1 < expr->atom.args)
3150 printf(",");
3151 }
3152 printf(")");
3153 }
3154 } else if (expr->type == UNARY_TYPE) {
3155 printf("%s", token_table[expr->unary_op.op].string);
3156 dump_parse_tree(expr->unary_op.expr);
3157 } else {
3158 printf("(");
3159 dump_parse_tree(expr->expr_op.lhs);
3160 printf("%s", token_table[expr->expr_op.op].string);
3161 dump_parse_tree(expr->expr_op.rhs);
3162 printf(")");
3163 }
3164 }
3165
3166
dump_action_list(struct action * spec_list,int spec_count)3167 void dump_action_list(struct action *spec_list, int spec_count)
3168 {
3169 int i;
3170
3171 for (i = 0; i < spec_count; i++) {
3172 printf("%s", spec_list[i].action->name);
3173 if (spec_list[i].args) {
3174 int n;
3175
3176 printf("(");
3177 for (n = 0; n < spec_list[i].args; n++) {
3178 printf("%s", spec_list[i].argv[n]);
3179 if (n + 1 < spec_list[i].args)
3180 printf(",");
3181 }
3182 printf(")");
3183 }
3184 printf("=");
3185 dump_parse_tree(spec_list[i].expr);
3186 printf("\n");
3187 }
3188 }
3189
3190
dump_actions()3191 void dump_actions()
3192 {
3193 dump_action_list(exclude_spec, exclude_count);
3194 dump_action_list(fragment_spec, fragment_count);
3195 dump_action_list(other_spec, other_count);
3196 dump_action_list(move_spec, move_count);
3197 dump_action_list(empty_spec, empty_count);
3198 }
3199 #else
dump_actions()3200 void dump_actions()
3201 {
3202 }
3203 #endif
3204
3205
3206 static struct test_entry test_table[] = {
3207 { "name", 1, name_fn, NULL, 1},
3208 { "pathname", 1, pathname_fn, check_pathname, 1, 0},
3209 { "subpathname", 1, subpathname_fn, check_pathname, 1, 0},
3210 { "filesize", 1, filesize_fn, parse_number_arg, 1, 0},
3211 { "dirsize", 1, dirsize_fn, parse_number_arg, 1, 0},
3212 { "size", 1, size_fn, parse_number_arg, 1, 0},
3213 { "inode", 1, inode_fn, parse_number_arg, 1, 0},
3214 { "nlink", 1, nlink_fn, parse_number_arg, 1, 0},
3215 { "fileblocks", 1, fileblocks_fn, parse_number_arg, 1, 0},
3216 { "dirblocks", 1, dirblocks_fn, parse_number_arg, 1, 0},
3217 { "blocks", 1, blocks_fn, parse_number_arg, 1, 0},
3218 { "gid", 1, gid_fn, parse_gid_arg, 1, 0},
3219 { "uid", 1, uid_fn, parse_uid_arg, 1, 0},
3220 { "depth", 1, depth_fn, parse_number_arg, 1, 0},
3221 { "dircount", 1, dircount_fn, parse_number_arg, 0, 0},
3222 { "filesize_range", 2, filesize_range_fn, parse_range_args, 1, 0},
3223 { "dirsize_range", 2, dirsize_range_fn, parse_range_args, 1, 0},
3224 { "size_range", 2, size_range_fn, parse_range_args, 1, 0},
3225 { "inode_range", 2, inode_range_fn, parse_range_args, 1, 0},
3226 { "nlink_range", 2, nlink_range_fn, parse_range_args, 1, 0},
3227 { "fileblocks_range", 2, fileblocks_range_fn, parse_range_args, 1, 0},
3228 { "dirblocks_range", 2, dirblocks_range_fn, parse_range_args, 1, 0},
3229 { "blocks_range", 2, blocks_range_fn, parse_range_args, 1, 0},
3230 { "gid_range", 2, gid_range_fn, parse_range_args, 1, 0},
3231 { "uid_range", 2, uid_range_fn, parse_range_args, 1, 0},
3232 { "depth_range", 2, depth_range_fn, parse_range_args, 1, 0},
3233 { "dircount_range", 2, dircount_range_fn, parse_range_args, 0, 0},
3234 { "type", 1, type_fn, parse_type_arg, 1, 0},
3235 { "true", 0, true_fn, NULL, 1, 0},
3236 { "false", 0, false_fn, NULL, 1, 0},
3237 { "file", 1, file_fn, parse_file_arg, 1, 0},
3238 { "exec", 1, exec_fn, NULL, 1, 0},
3239 { "exists", 0, exists_fn, NULL, 0, 0},
3240 { "absolute", 0, absolute_fn, NULL, 0, 0},
3241 { "stat", 1, stat_fn, parse_expr_arg0, 1, 1},
3242 { "readlink", 1, readlink_fn, parse_expr_arg0, 0, 1},
3243 { "eval", 2, eval_fn, parse_expr_arg1, 0, 1},
3244 { "perm", -2, perm_fn, parse_perm_args, 1, 0},
3245 { "", -1 }
3246 };
3247
3248
3249 static struct action_entry action_table[] = {
3250 { "fragment", FRAGMENT_ACTION, 1, ACTION_REG, NULL, NULL},
3251 { "exclude", EXCLUDE_ACTION, 0, ACTION_ALL_LNK, NULL, NULL},
3252 { "fragments", FRAGMENTS_ACTION, 0, ACTION_REG, NULL, frag_action},
3253 { "no-fragments", NO_FRAGMENTS_ACTION, 0, ACTION_REG, NULL,
3254 no_frag_action},
3255 { "always-use-fragments", ALWAYS_FRAGS_ACTION, 0, ACTION_REG, NULL,
3256 always_frag_action},
3257 { "dont-always-use-fragments", NO_ALWAYS_FRAGS_ACTION, 0, ACTION_REG,
3258 NULL, no_always_frag_action},
3259 { "compressed", COMPRESSED_ACTION, 0, ACTION_REG, NULL, comp_action},
3260 { "uncompressed", UNCOMPRESSED_ACTION, 0, ACTION_REG, NULL,
3261 uncomp_action},
3262 { "uid", UID_ACTION, 1, ACTION_ALL_LNK, parse_uid_args, uid_action},
3263 { "gid", GID_ACTION, 1, ACTION_ALL_LNK, parse_gid_args, gid_action},
3264 { "guid", GUID_ACTION, 2, ACTION_ALL_LNK, parse_guid_args, guid_action},
3265 { "mode", MODE_ACTION, -2, ACTION_ALL, parse_mode_args, mode_action },
3266 { "empty", EMPTY_ACTION, -2, ACTION_DIR, parse_empty_args, NULL},
3267 { "move", MOVE_ACTION, 1, ACTION_ALL_LNK, NULL, NULL},
3268 { "prune", PRUNE_ACTION, 0, ACTION_ALL_LNK, NULL, NULL},
3269 { "chmod", MODE_ACTION, -2, ACTION_ALL, parse_mode_args, mode_action },
3270 { "noop", NOOP_ACTION, 0, ACTION_ALL, NULL, noop_action },
3271 { "", 0, -1, 0, NULL, NULL}
3272 };
3273