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