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