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