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 (at) 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 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 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 */ 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 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 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 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 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 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 */ 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 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 658 char *expr_log_cmnd(int cmnd) 659 { 660 return _expr_log(NULL, cmnd); 661 } 662 663 664 char *expr_log(char *string) 665 { 666 return _expr_log(string, LOG_PRINT); 667 } 668 669 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 691 void expr_log_match(int match) 692 { 693 if(match) 694 expr_log("=True"); 695 else 696 expr_log("=False"); 697 } 698 699 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 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 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 */ 809 int parse_action_true(char *s) 810 { 811 return parse_action(s, ACTION_LOG_TRUE); 812 } 813 814 815 int parse_action_false(char *s) 816 { 817 return parse_action(s, ACTION_LOG_FALSE); 818 } 819 820 821 int parse_action_verbose(char *s) 822 { 823 return parse_action(s, ACTION_LOG_VERBOSE); 824 } 825 826 827 int parse_action_nonverbose(char *s) 828 { 829 return parse_action(s, ACTION_LOG_NONE); 830 } 831 832 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 */ 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 */ 874 int actions() 875 { 876 return other_count; 877 } 878 879 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 */ 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 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 */ 969 int exclude_actions() 970 { 971 return exclude_count; 972 } 973 974 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 */ 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 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 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 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 */ 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 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 */ 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 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 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 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 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 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 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 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 */ 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 */ 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 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 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 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 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 */ 1469 int empty_actions() 1470 { 1471 return empty_count; 1472 } 1473 1474 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 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 1568 int move_actions() 1569 { 1570 return move_count; 1571 } 1572 1573 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 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 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 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 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 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 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 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 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 */ 1980 int prune_actions() 1981 { 1982 return prune_count; 1983 } 1984 1985 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 */ 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 */ 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 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 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 */ 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 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 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 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 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 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 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 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 */ 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 */ 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 */ 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 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 */ 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 */ 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 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 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 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 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 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 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 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 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 */ 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 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 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 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 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 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