diff -Nru squashfs-tools-4.2+20130409/action.c squashfs-tools-4.3+20140919/action.c --- squashfs-tools-4.2+20130409/action.c 2013-05-09 10:39:11.000000000 +0200 +++ squashfs-tools-4.3+20140919/action.c 2015-07-20 21:03:05.000000000 +0200 @@ -2,7 +2,7 @@ * Create a squashfs filesystem. This is a highly compressed read only * filesystem. * - * Copyright (c) 2011, 2012, 2013 + * Copyright (c) 2011, 2012, 2013, 2014 * Phillip Lougher * * This program is free software; you can redistribute it and/or @@ -53,12 +53,15 @@ static struct action *exclude_spec = NULL; static struct action *empty_spec = NULL; static struct action *move_spec = NULL; +static struct action *prune_spec = NULL; static struct action *other_spec = NULL; static int fragment_count = 0; static int exclude_count = 0; static int empty_count = 0; static int move_count = 0; +static int prune_count = 0; static int other_count = 0; +static struct action_entry *parsing_action; static struct file_buffer *def_fragment = NULL; @@ -198,12 +201,34 @@ /* * Expression parser */ +static void free_parse_tree(struct expr *expr) +{ + if(expr->type == ATOM_TYPE) { + int i; + + for(i = 0; i < expr->atom.test->args; i++) + free(expr->atom.argv[i]); + + free(expr->atom.argv); + } else if (expr->type == UNARY_TYPE) + free_parse_tree(expr->unary_op.expr); + else { + free_parse_tree(expr->expr_op.lhs); + free_parse_tree(expr->expr_op.rhs); + } + + free(expr); +} + + static struct expr *create_expr(struct expr *lhs, int op, struct expr *rhs) { struct expr *expr; - if (rhs == NULL) + if (rhs == NULL) { + free_parse_tree(lhs); return NULL; + } expr = malloc(sizeof(*expr)); if (expr == NULL) @@ -239,8 +264,8 @@ static struct expr *parse_test(char *name) { - char *string; - int token; + char *string, **argv = NULL; + int token, args = 0; int i; struct test_entry *test; struct expr *expr; @@ -249,12 +274,20 @@ if (strcmp(name, test_table[i].name) == 0) break; - if (test_table[i].args == -1) { + test = &test_table[i]; + + if (test->args == -1) { SYNTAX_ERROR("Non-existent test \"%s\"\n", name); return NULL; } - test = &test_table[i]; + if(parsing_action->type == EXCLUDE_ACTION && !test->exclude_ok) { + fprintf(stderr, "Failed to parse action \"%s\"\n", source); + fprintf(stderr, "Test \"%s\" cannot be used in exclude " + "actions\n", name); + fprintf(stderr, "Use prune action instead ...\n"); + return NULL; + } expr = malloc(sizeof(*expr)); if (expr == NULL) @@ -262,55 +295,68 @@ expr->type = ATOM_TYPE; - expr->atom.argv = malloc(test->args * sizeof(char *)); - if (expr->atom.argv == NULL) - MEM_ERROR(); - expr->atom.test = test; expr->atom.data = NULL; /* - * If the test has no arguments, allow it to be typed - * without brackets + * If the test has no arguments, then go straight to checking if there's + * enough arguments */ - if (test->args == 0) { - token = peek_token(&string); + token = peek_token(&string); - if (token != TOK_OPEN_BRACKET) + if (token != TOK_OPEN_BRACKET) goto skip_args; - } - - token = get_token(&string); - if (token != TOK_OPEN_BRACKET) { - SYNTAX_ERROR("Unexpected token \"%s\", expected \"(\"\n", - TOK_TO_STR(token, string)); - goto failed; - } + get_token(&string); - for (i = 0; i < test->args; i++) { - token = get_token(&string); + /* + * speculatively read all the arguments, and then see if the + * number of arguments read is the number expected, this handles + * tests with a variable number of arguments + */ + token = get_token(&string); + if (token == TOK_CLOSE_BRACKET) + goto skip_args; + while(1) { if (token != TOK_STRING) { SYNTAX_ERROR("Unexpected token \"%s\", expected " "argument\n", TOK_TO_STR(token, string)); goto failed; } - expr->atom.argv[i] = strdup(string); + argv = realloc(argv, (args + 1) * sizeof(char *)); + if (argv == NULL) + MEM_ERROR(); + + argv[args ++ ] = strdup(string); + + token = get_token(&string); - if (i + 1 < test->args) { - token = get_token(&string); + if (token == TOK_CLOSE_BRACKET) + break; - if (token != TOK_COMMA) { - SYNTAX_ERROR("Unexpected token \"%s\", " - "expected \",\"\n", - TOK_TO_STR(token, string)); + if (token != TOK_COMMA) { + SYNTAX_ERROR("Unexpected token \"%s\", expected " + "\",\" or \")\"\n", TOK_TO_STR(token, string)); goto failed; - } } + token = get_token(&string); + } + +skip_args: + /* + * expected number of arguments? + */ + if(test->args != -2 && args != test->args) { + SYNTAX_ERROR("Unexpected number of arguments, expected %d, " + "got %d\n", test->args, args); + goto failed; } + expr->atom.args = args; + expr->atom.argv = argv; + if (test->parse_args) { int res = test->parse_args(test, &expr->atom); @@ -318,19 +364,10 @@ goto failed; } - token = get_token(&string); - - if (token != TOK_CLOSE_BRACKET) { - SYNTAX_ERROR("Unexpected token \"%s\", expected \")\"\n", - TOK_TO_STR(token, string)); - goto failed; - } - -skip_args: return expr; failed: - free(expr->atom.argv); + free(argv); free(expr); return NULL; } @@ -367,6 +404,7 @@ if (op == TOK_EOF) { if (subexp) { + free_parse_tree(expr); SYNTAX_ERROR("Expected \"&&\", \"||\" or " "\")\", got EOF\n"); return NULL; @@ -376,6 +414,7 @@ if (op == TOK_CLOSE_BRACKET) { if (!subexp) { + free_parse_tree(expr); SYNTAX_ERROR("Unexpected \")\", expected " "\"&&\", \"!!\" or EOF\n"); return NULL; @@ -384,6 +423,7 @@ } if (op != TOK_AND && op != TOK_OR) { + free_parse_tree(expr); SYNTAX_ERROR("Unexpected token \"%s\", expected " "\"&&\" or \"||\"\n", TOK_TO_STR(op, string)); return NULL; @@ -399,7 +439,7 @@ /* * Action parser */ -int parse_action(char *s) +int parse_action(char *s, int verbose) { char *string, **argv = NULL; int i, token, args = 0; @@ -501,6 +541,7 @@ goto failed; } + parsing_action = action; expr = parse_expr(0); if (expr == NULL) @@ -526,6 +567,10 @@ spec_count = move_count ++; spec_list = &move_spec; break; + case PRUNE_ACTION: + spec_count = prune_count ++; + spec_list = &prune_spec; + break; default: spec_count = other_count ++; spec_list = &other_spec; @@ -542,6 +587,7 @@ (*spec_list)[spec_count].argv = argv; (*spec_list)[spec_count].expr = expr; (*spec_list)[spec_count].data = data; + (*spec_list)[spec_count].verbose = verbose; return 1; @@ -551,68 +597,133 @@ } -static void dump_parse_tree(struct expr *expr) -{ - if(expr->type == ATOM_TYPE) { - int i; +/* + * Evaluate expressions + */ - printf("%s(", expr->atom.test->name); - for(i = 0; i < expr->atom.test->args; i++) { - printf("%s", expr->atom.argv[i]); - if (i + 1 < expr->atom.test->args) - printf(","); - } - printf(")"); - } else if (expr->type == UNARY_TYPE) { - printf("%s", token_table[expr->unary_op.op].string); - dump_parse_tree(expr->unary_op.expr); - } else { - printf("("); - dump_parse_tree(expr->expr_op.lhs); - printf("%s", token_table[expr->expr_op.op].string); - dump_parse_tree(expr->expr_op.rhs); - printf(")"); +#define ALLOC_SZ 128 + +#define LOG_ENABLE 0 +#define LOG_DISABLE 1 +#define LOG_PRINT 2 +#define LOG_ENABLED 3 + +char *_expr_log(char *string, int cmnd) +{ + static char *expr_msg = NULL; + static int cur_size = 0, alloc_size = 0; + int size; + + switch(cmnd) { + case LOG_ENABLE: + expr_msg = malloc(ALLOC_SZ); + alloc_size = ALLOC_SZ; + cur_size = 0; + return expr_msg; + case LOG_DISABLE: + free(expr_msg); + alloc_size = cur_size = 0; + return expr_msg = NULL; + case LOG_ENABLED: + return expr_msg; + default: + if(expr_msg == NULL) + return NULL; + break; + } + + /* if string is empty append '\0' */ + size = strlen(string) ? : 1; + + if(alloc_size - cur_size < size) { + /* buffer too small, expand */ + alloc_size = (cur_size + size + ALLOC_SZ - 1) & ~(ALLOC_SZ - 1); + + expr_msg = realloc(expr_msg, alloc_size); + if(expr_msg == NULL) + MEM_ERROR(); } + + memcpy(expr_msg + cur_size, string, size); + cur_size += size; + + return expr_msg; } -void dump_action_list(struct action *spec_list, int spec_count) +char *expr_log_cmnd(int cmnd) +{ + return _expr_log(NULL, cmnd); +} + + +char *expr_log(char *string) +{ + return _expr_log(string, LOG_PRINT); +} + + +void expr_log_atom(struct atom *atom) { int i; - for (i = 0; i < spec_count; i++) { - printf("%s", spec_list[i].action->name); - if (spec_list[i].action->args) { - int n; + if(atom->test->handle_logging) + return; - printf("("); - for (n = 0; n < spec_list[i].action->args; n++) { - printf("%s", spec_list[i].argv[n]); - if (n + 1 < spec_list[i].action->args) - printf(","); - } - printf(")"); + expr_log(atom->test->name); + + if(atom->args) { + expr_log("("); + for(i = 0; i < atom->args; i++) { + expr_log(atom->argv[i]); + if (i + 1 < atom->args) + expr_log(","); } - printf("="); - dump_parse_tree(spec_list[i].expr); - printf("\n"); + expr_log(")"); } } -void dump_actions() +void expr_log_match(int match) { - dump_action_list(exclude_spec, exclude_count); - dump_action_list(fragment_spec, fragment_count); - dump_action_list(other_spec, other_count); - dump_action_list(move_spec, move_count); - dump_action_list(empty_spec, empty_count); + if(match) + expr_log("=True"); + else + expr_log("=False"); +} + + +static int eval_expr_log(struct expr *expr, struct action_data *action_data) +{ + int match; + + switch (expr->type) { + case ATOM_TYPE: + expr_log_atom(&expr->atom); + match = expr->atom.test->fn(&expr->atom, action_data); + expr_log_match(match); + break; + case UNARY_TYPE: + expr_log("!"); + match = !eval_expr_log(expr->unary_op.expr, action_data); + break; + default: + expr_log("("); + match = eval_expr_log(expr->expr_op.lhs, action_data); + + if ((expr->expr_op.op == TOK_AND && match) || + (expr->expr_op.op == TOK_OR && !match)) { + expr_log(token_table[expr->expr_op.op].string); + match = eval_expr_log(expr->expr_op.rhs, action_data); + } + expr_log(")"); + break; + } + + return match; } -/* - * Evaluate expressions - */ static int eval_expr(struct expr *expr, struct action_data *action_data) { int match; @@ -637,6 +748,49 @@ } +static int eval_expr_top(struct action *action, struct action_data *action_data) +{ + if(action->verbose) { + int match, n; + + expr_log_cmnd(LOG_ENABLE); + + if(action_data->subpath) + expr_log(action_data->subpath); + + expr_log("="); + expr_log(action->action->name); + + if(action->args) { + expr_log("("); + for (n = 0; n < action->args; n++) { + expr_log(action->argv[n]); + if(n + 1 < action->args) + expr_log(","); + } + expr_log(")"); + } + + expr_log("@"); + + match = eval_expr_log(action->expr, action_data); + + /* + * Print the evaluated expression log, if the + * result matches the logging specified + */ + if((match && (action->verbose & ACTION_LOG_TRUE)) || (!match + && (action->verbose & ACTION_LOG_FALSE))) + progressbar_info("%s\n", expr_log("")); + + expr_log_cmnd(LOG_DISABLE); + + return match; + } else + return eval_expr(action->expr, action_data); +} + + /* * Read action file, passing each line to parse_action() for * parsing. @@ -648,9 +802,65 @@ * * Blank lines and comment lines indicated by # are supported. */ -int read_action_file(char *filename) +int parse_action_true(char *s) +{ + return parse_action(s, ACTION_LOG_TRUE); +} + + +int parse_action_false(char *s) +{ + return parse_action(s, ACTION_LOG_FALSE); +} + + +int parse_action_verbose(char *s) +{ + return parse_action(s, ACTION_LOG_VERBOSE); +} + + +int parse_action_nonverbose(char *s) +{ + return parse_action(s, ACTION_LOG_NONE); +} + + +int read_action_file(char *filename, int verbose) +{ + switch(verbose) { + case ACTION_LOG_TRUE: + return read_file(filename, "action", parse_action_true); + case ACTION_LOG_FALSE: + return read_file(filename, "action", parse_action_false); + case ACTION_LOG_VERBOSE: + return read_file(filename, "action", parse_action_verbose); + default: + return read_file(filename, "action", parse_action_nonverbose); + } +} + + +/* + * helper to evaluate whether action/test acts on this file type + */ +static int file_type_match(int st_mode, int type) { - return read_file(filename, "action", parse_action); + switch(type) { + case ACTION_DIR: + return S_ISDIR(st_mode); + case ACTION_REG: + return S_ISREG(st_mode); + case ACTION_ALL: + return S_ISREG(st_mode) || S_ISDIR(st_mode) || + S_ISCHR(st_mode) || S_ISBLK(st_mode) || + S_ISFIFO(st_mode) || S_ISSOCK(st_mode); + case ACTION_LNK: + return S_ISLNK(st_mode); + case ACTION_ALL_LNK: + default: + return 1; + } } @@ -663,53 +873,65 @@ } -void eval_actions(struct dir_ent *dir_ent) +void eval_actions(struct dir_info *root, struct dir_ent *dir_ent) { int i, match; struct action_data action_data; - int file_type = dir_ent->inode->buf.st_mode & S_IFMT; + int st_mode = dir_ent->inode->buf.st_mode; action_data.name = dir_ent->name; - action_data.pathname = pathname(dir_ent); - action_data.subpath = subpathname(dir_ent); + action_data.pathname = strdup(pathname(dir_ent)); + action_data.subpath = strdup(subpathname(dir_ent)); action_data.buf = &dir_ent->inode->buf; action_data.depth = dir_ent->our_dir->depth; + action_data.dir_ent = dir_ent; + action_data.root = root; for (i = 0; i < other_count; i++) { struct action *action = &other_spec[i]; - if ((action->action->file_types & file_type) == 0) + if (!file_type_match(st_mode, action->action->file_types)) /* action does not operate on this file type */ continue; - match = eval_expr(action->expr, &action_data); + match = eval_expr_top(action, &action_data); if (match) action->action->run_action(action, dir_ent); } + + free(action_data.pathname); + free(action_data.subpath); } /* * Fragment specific action code */ -void *eval_frag_actions(struct dir_ent *dir_ent) +void *eval_frag_actions(struct dir_info *root, struct dir_ent *dir_ent) { int i, match; struct action_data action_data; action_data.name = dir_ent->name; - action_data.pathname = pathname(dir_ent); - action_data.subpath = subpathname(dir_ent); + action_data.pathname = strdup(pathname(dir_ent)); + action_data.subpath = strdup(subpathname(dir_ent)); action_data.buf = &dir_ent->inode->buf; action_data.depth = dir_ent->our_dir->depth; + action_data.dir_ent = dir_ent; + action_data.root = root; for (i = 0; i < fragment_count; i++) { - match = eval_expr(fragment_spec[i].expr, &action_data); - if (match) + match = eval_expr_top(&fragment_spec[i], &action_data); + if (match) { + free(action_data.pathname); + free(action_data.subpath); return &fragment_spec[i].data; + } } + free(action_data.pathname); + free(action_data.subpath); return &def_fragment; } @@ -747,7 +969,7 @@ int eval_exclude_actions(char *name, char *pathname, char *subpath, - struct stat *buf, int depth) + struct stat *buf, int depth, struct dir_ent *dir_ent) { int i, match = 0; struct action_data action_data; @@ -757,9 +979,10 @@ action_data.subpath = subpath; action_data.buf = buf; action_data.depth = depth; + action_data.dir_ent = dir_ent; for (i = 0; i < exclude_count && !match; i++) - match = eval_expr(exclude_spec[i].expr, &action_data); + match = eval_expr_top(&exclude_spec[i], &action_data); return match; } @@ -823,8 +1046,8 @@ long long uid = strtoll(arg, &b, 10); if (*b == '\0') { - if (uid < 0 || uid >= (1LL < 32)) { - SYNTAX_ERROR("action: uid out of range\n"); + if (uid < 0 || uid >= (1LL << 32)) { + SYNTAX_ERROR("Uid out of range\n"); return -1; } } else { @@ -833,7 +1056,7 @@ if (passwd) uid = passwd->pw_uid; else { - SYNTAX_ERROR("action: invalid uid or unknown user\n"); + SYNTAX_ERROR("Invalid uid or unknown user\n"); return -1; } } @@ -847,8 +1070,8 @@ long long gid = strtoll(arg, &b, 10); if (*b == '\0') { - if (gid < 0 || gid >= (1LL < 32)) { - SYNTAX_ERROR("action: gid out of range\n"); + if (gid < 0 || gid >= (1LL << 32)) { + SYNTAX_ERROR("Gid out of range\n"); return -1; } } else { @@ -857,7 +1080,7 @@ if (group) gid = group->gr_gid; else { - SYNTAX_ERROR("action: invalid gid or unknown user\n"); + SYNTAX_ERROR("Invalid gid or unknown group\n"); return -1; } } @@ -964,29 +1187,37 @@ /* * Mode specific action code */ -static int parse_octal_mode_args(unsigned int mode, int bytes, int args, - char **argv, void **data) +static int parse_octal_mode_args(int args, char **argv, + void **data) { + int n, bytes; + unsigned int mode; struct mode_data *mode_data; + /* octal mode number? */ + n = sscanf(argv[0], "%o%n", &mode, &bytes); + if (n == 0) + return -1; /* not an octal number arg */ + + /* check there's no trailing junk */ if (argv[0][bytes] != '\0') { SYNTAX_ERROR("Unexpected trailing bytes after octal " "mode number\n"); - return 0; + return 0; /* bad octal number arg */ } /* check there's only one argument */ if (args > 1) { SYNTAX_ERROR("Octal mode number is first argument, " "expected one argument, got %d\n", args); - return 0; + return 0; /* bad octal number arg */ } /* check mode is within range */ if (mode > 07777) { SYNTAX_ERROR("Octal mode %o is out of range\n", mode); - return 0; + return 0; /* bad octal number arg */ } mode_data = malloc(sizeof(struct mode_data)); @@ -1003,19 +1234,17 @@ /* - * Parse symbolic mode of format [ugoa]+[+-=]PERMS + * Parse symbolic mode of format [ugoa]*[[+-=]PERMS]+ * PERMS = [rwxXst]+ or [ugo] */ -static struct mode_data *parse_sym_mode_arg(char *arg) +static int parse_sym_mode_arg(char *arg, struct mode_data **head, + struct mode_data **cur) { - struct mode_data *mode_data = malloc(sizeof(*mode_data)); - int mode = 0; + struct mode_data *mode_data; + int mode; int mask = 0; int op; - char X = 0; - - if (mode_data == NULL) - MEM_ERROR(); + char X; if (arg[0] != 'u' && arg[0] != 'g' && arg[0] != 'o' && arg[0] != 'a') { /* no ownership specifiers, default to a */ @@ -1045,130 +1274,138 @@ } parse_operation: - switch(*arg) { - case '+': - op = ACTION_MODE_ADD; - break; - case '-': - op = ACTION_MODE_REM; - break; - case '=': - op = ACTION_MODE_SET; - break; - default: - SYNTAX_ERROR("Action mode: Expected one of '+', '-' or '=', " - "got '%c'\n", *arg); + /* trap a symbolic mode with just an ownership specification */ + if(*arg == '\0') { + SYNTAX_ERROR("Expected one of '+', '-' or '=', got EOF\n"); goto failed; } - arg ++; + while(*arg != '\0') { + mode = 0; + X = 0; - /* Parse PERMS */ - if (*arg == 'u' || *arg == 'g' || *arg == 'o') { - /* PERMS = [ugo] */ - mode = - *arg; - if (*++arg != '\0') { - SYNTAX_ERROR("Action mode: permission 'u', 'g' or 'o' " - "has trailing characters\n"); + switch(*arg) { + case '+': + op = ACTION_MODE_ADD; + break; + case '-': + op = ACTION_MODE_REM; + break; + case '=': + op = ACTION_MODE_SET; + break; + default: + SYNTAX_ERROR("Expected one of '+', '-' or '=', got " + "'%c'\n", *arg); goto failed; } - } else { - /* PERMS = [rwxXst]+ */ - while(*arg != '\0') { - switch(*arg) { - case 'r': - mode |= 0444; - break; - case 'w': - mode |= 0222; - break; - case 'x': - mode |= 0111; - break; - case 's': - mode |= 06000; - break; - case 't': - mode |= 01000; - break; - case 'X': - X = 1; - break; - default: - SYNTAX_ERROR("Action mode: unrecognised " - "permission '%c'\n", *arg); - goto failed; - } - + + arg ++; + + /* Parse PERMS */ + if (*arg == 'u' || *arg == 'g' || *arg == 'o') { + /* PERMS = [ugo] */ + mode = - *arg; arg ++; + } else { + /* PERMS = [rwxXst]* */ + while(1) { + switch(*arg) { + case 'r': + mode |= 0444; + break; + case 'w': + mode |= 0222; + break; + case 'x': + mode |= 0111; + break; + case 's': + mode |= 06000; + break; + case 't': + mode |= 01000; + break; + case 'X': + X = 1; + break; + case '+': + case '-': + case '=': + case '\0': + mode &= mask; + goto perms_parsed; + default: + SYNTAX_ERROR("Unrecognised permission " + "'%c'\n", *arg); + goto failed; + } + + arg ++; + } } - mode &= mask; - } + +perms_parsed: + mode_data = malloc(sizeof(*mode_data)); + if (mode_data == NULL) + MEM_ERROR(); - mode_data->operation = op; - mode_data->mode = mode; - mode_data->mask = mask; - mode_data->X = X; - mode_data->next = NULL; + mode_data->operation = op; + mode_data->mode = mode; + mode_data->mask = mask; + mode_data->X = X; + mode_data->next = NULL; + + if (*cur) { + (*cur)->next = mode_data; + *cur = mode_data; + } else + *head = *cur = mode_data; + } - return mode_data; + return 1; failed: - free(mode_data); - return NULL; + return 0; } static int parse_sym_mode_args(struct action_entry *action, int args, char **argv, void **data) { - int i; + int i, res = 1; struct mode_data *head = NULL, *cur = NULL; - for (i = 0; i < args; i++) { - struct mode_data *entry = parse_sym_mode_arg(argv[i]); - - if (entry == NULL) - return 0; - - if (cur) { - cur->next = entry; - cur = entry; - } else - head = cur = entry; - } + for (i = 0; i < args && res; i++) + res = parse_sym_mode_arg(argv[i], &head, &cur); *data = head; - return 1; + return res; } static int parse_mode_args(struct action_entry *action, int args, char **argv, void **data) { - int n, bytes; - unsigned int mode; + int res; if (args == 0) { SYNTAX_ERROR("Mode action expects one or more arguments\n"); return 0; } - /* octal mode number? */ - n = sscanf(argv[0], "%o%n", &mode, &bytes); - - if(n >= 1) - return parse_octal_mode_args(mode, bytes, args, argv, data); - else + res = parse_octal_mode_args(args, argv, data); + if(res >= 0) + /* Got an octal mode argument */ + return res; + else /* not an octal mode argument */ return parse_sym_mode_args(action, args, argv, data); } -static void mode_action(struct action *action, struct dir_ent *dir_ent) +static int mode_execute(struct mode_data *mode_data, int st_mode) { - struct stat *buf = &dir_ent->inode->buf; - struct mode_data *mode_data = action->data; int mode = 0; for (;mode_data; mode_data = mode_data->next) { @@ -1176,20 +1413,20 @@ /* 'u', 'g' or 'o' */ switch(-mode_data->mode) { case 'u': - mode = (buf->st_mode >> 6) & 07; + mode = (st_mode >> 6) & 07; break; case 'g': - mode = (buf->st_mode >> 3) & 07; + mode = (st_mode >> 3) & 07; break; case 'o': - mode = buf->st_mode & 07; + mode = st_mode & 07; break; } mode = ((mode << 6) | (mode << 3) | mode) & mode_data->mask; } else if (mode_data->X && - ((buf->st_mode & S_IFMT) == S_IFDIR || - (buf->st_mode & 0111))) + ((st_mode & S_IFMT) == S_IFDIR || + (st_mode & 0111))) /* X permission, only takes effect if inode is a * directory or x is set for some owner */ mode = mode_data->mode | (0111 & mode_data->mask); @@ -1198,18 +1435,27 @@ switch(mode_data->operation) { case ACTION_MODE_OCT: - buf->st_mode = (buf->st_mode & ~S_IFMT) | mode; + st_mode = (st_mode & S_IFMT) | mode; break; case ACTION_MODE_SET: - buf->st_mode = (buf->st_mode & ~mode_data->mask) | mode; + st_mode = (st_mode & ~mode_data->mask) | mode; break; case ACTION_MODE_ADD: - buf->st_mode |= mode; + st_mode |= mode; break; case ACTION_MODE_REM: - buf->st_mode &= ~mode; + st_mode &= ~mode; } } + + return st_mode; +} + + +static void mode_action(struct action *action, struct dir_ent *dir_ent) +{ + dir_ent->inode->buf.st_mode = mode_execute(action->data, + dir_ent->inode->buf.st_mode); } @@ -1257,7 +1503,7 @@ } -int eval_empty_actions(struct dir_ent *dir_ent) +int eval_empty_actions(struct dir_info *root, struct dir_ent *dir_ent) { int i, match = 0; struct action_data action_data; @@ -1271,10 +1517,12 @@ return 0; action_data.name = dir_ent->name; - action_data.pathname = pathname(dir_ent); - action_data.subpath = subpathname(dir_ent); + action_data.pathname = strdup(pathname(dir_ent)); + action_data.subpath = strdup(subpathname(dir_ent)); action_data.buf = &dir_ent->inode->buf; action_data.depth = dir_ent->our_dir->depth; + action_data.dir_ent = dir_ent; + action_data.root = root; for (i = 0; i < empty_count && !match; i++) { data = empty_spec[i].data; @@ -1297,9 +1545,12 @@ (data->val == EMPTY_SOURCE && dir->excluded)) continue; - match = eval_expr(empty_spec[i].expr, &action_data); + match = eval_expr_top(&empty_spec[i], &action_data); } + free(action_data.pathname); + free(action_data.subpath); + return match; } @@ -1510,10 +1761,12 @@ struct move_ent *move = NULL; action_data.name = dir_ent->name; - action_data.pathname = pathname(dir_ent); - action_data.subpath = subpathname(dir_ent); + action_data.pathname = strdup(pathname(dir_ent)); + action_data.subpath = strdup(subpathname(dir_ent)); action_data.buf = &dir_ent->inode->buf; action_data.depth = dir_ent->our_dir->depth; + action_data.dir_ent = dir_ent; + action_data.root = root; /* * Evaluate each move action against the current file. For any @@ -1526,7 +1779,7 @@ */ for (i = 0; i < move_count; i++) { struct action *action = &move_spec[i]; - int match = eval_expr(action->expr, &action_data); + int match = eval_expr_top(action, &action_data); if(match) { if(move == NULL) { @@ -1553,7 +1806,7 @@ */ if(move->ops == 0) { free(move); - return; + goto finish; } dest = (move->ops & ACTION_MOVE_MOVE) ? @@ -1568,7 +1821,7 @@ action_data.subpath, conf_path); free(conf_path); free(move); - return; + goto finish; } /* @@ -1582,11 +1835,15 @@ action_data.subpath, conf_path); free(conf_path); free(move); - return; + goto finish; } move->next = move_list; move_list = move; } + +finish: + free(action_data.pathname); + free(action_data.subpath); } @@ -1714,6 +1971,46 @@ /* + * Prune specific action code + */ +int prune_actions() +{ + return prune_count; +} + + +int eval_prune_actions(struct dir_info *root, struct dir_ent *dir_ent) +{ + int i, match = 0; + struct action_data action_data; + + action_data.name = dir_ent->name; + action_data.pathname = strdup(pathname(dir_ent)); + action_data.subpath = strdup(subpathname(dir_ent)); + action_data.buf = &dir_ent->inode->buf; + action_data.depth = dir_ent->our_dir->depth; + action_data.dir_ent = dir_ent; + action_data.root = root; + + for (i = 0; i < prune_count && !match; i++) + match = eval_expr_top(&prune_spec[i], &action_data); + + free(action_data.pathname); + free(action_data.subpath); + + return match; +} + + +/* + * Noop specific action code + */ +static void noop_action(struct action *action, struct dir_ent *dir_ent) +{ +} + + +/* * General test evaluation code */ @@ -1768,13 +2065,13 @@ switch (end[0]) { case 'g': case 'G': - *size *= 1024; + number *= 1024; case 'm': case 'M': - *size *= 1024; + number *= 1024; case 'k': case 'K': - *size *= 1024; + number *= 1024; if (end[1] != '\0') { *error = "Trailing junk after size specifier"; @@ -1873,7 +2170,7 @@ static int NAME##_fn(struct atom *atom, struct action_data *action_data) \ { \ /* test operates on MATCH file types only */ \ - if (!(action_data->buf->st_mode & MATCH)) \ + if (!file_type_match(action_data->buf->st_mode, MATCH)) \ return 0; \ \ CODE \ @@ -1992,6 +2289,12 @@ FNM_PATHNAME|FNM_PERIOD|FNM_EXTMATCH) == 0; } +/* + * Inode attribute test operations using generic + * TEST_VAR_FN(test name, file scope, attribute name) macro. + * This is for tests that do not need to be specially handled in any way. + * They just take a variable and compare it against a number. + */ TEST_VAR_FN(filesize, ACTION_REG, action_data->buf->st_size) TEST_VAR_FN(dirsize, ACTION_DIR, action_data->buf->st_size) @@ -2008,9 +2311,7 @@ TEST_VAR_FN(blocks, ACTION_ALL_LNK, action_data->buf->st_blocks) -TEST_VAR_FN(gid, ACTION_ALL_LNK, action_data->buf->st_gid) - -TEST_VAR_FN(uid, ACTION_ALL_LNK, action_data->buf->st_uid) +TEST_VAR_FN(dircount, ACTION_DIR, action_data->dir_ent->dir->count) TEST_VAR_FN(depth, ACTION_ALL_LNK, action_data->depth) @@ -2036,6 +2337,98 @@ TEST_VAR_RANGE_FN(depth, ACTION_ALL_LNK, action_data->depth) +TEST_VAR_RANGE_FN(dircount, ACTION_DIR, action_data->dir_ent->dir->count) + +/* + * uid specific test code + */ +TEST_VAR_FN(uid, ACTION_ALL_LNK, action_data->buf->st_uid) + +static int parse_uid_arg(struct test_entry *test, struct atom *atom) +{ + struct test_number_arg *number; + long long size; + int range; + char *error; + + if(parse_number(atom->argv[0], &size, &range, &error)) { + /* managed to fully parse argument as a number */ + if(size < 0 || size > (((long long) 1 << 32) - 1)) { + TEST_SYNTAX_ERROR(test, 1, "Numeric uid out of " + "range\n"); + return 0; + } + } else { + /* couldn't parse (fully) as a number, is it a user name? */ + struct passwd *uid = getpwnam(atom->argv[0]); + if(uid) { + size = uid->pw_uid; + range = NUM_EQ; + } else { + TEST_SYNTAX_ERROR(test, 1, "Invalid uid or unknown " + "user\n"); + return 0; + } + } + + number = malloc(sizeof(*number)); + if(number == NULL) + MEM_ERROR(); + + number->range = range; + number->size= size; + + atom->data = number; + + return 1; +} + + +/* + * gid specific test code + */ +TEST_VAR_FN(gid, ACTION_ALL_LNK, action_data->buf->st_gid) + +static int parse_gid_arg(struct test_entry *test, struct atom *atom) +{ + struct test_number_arg *number; + long long size; + int range; + char *error; + + if(parse_number(atom->argv[0], &size, &range, &error)) { + /* managed to fully parse argument as a number */ + if(size < 0 || size > (((long long) 1 << 32) - 1)) { + TEST_SYNTAX_ERROR(test, 1, "Numeric gid out of " + "range\n"); + return 0; + } + } else { + /* couldn't parse (fully) as a number, is it a group name? */ + struct group *gid = getgrnam(atom->argv[0]); + if(gid) { + size = gid->gr_gid; + range = NUM_EQ; + } else { + TEST_SYNTAX_ERROR(test, 1, "Invalid gid or unknown " + "group\n"); + return 0; + } + } + + number = malloc(sizeof(*number)); + if(number == NULL) + MEM_ERROR(); + + number->range = range; + number->size= size; + + atom->data = number; + + return 1; +} + + /* * Type test specific code */ @@ -2116,6 +2509,7 @@ char str[1024]; /* overflow safe */ regerror(res, preg, str, 1024); + free(preg); TEST_SYNTAX_ERROR(test, 0, "invalid regex \"%s\" because " "\"%s\"\n", atom->argv[0], str); return 0; @@ -2265,37 +2659,583 @@ } +/* + * Symbolic link specific test code + */ + +/* + * Walk the supplied pathname and return the directory entry corresponding + * to the pathname. If any symlinks are encountered whilst walking the + * pathname, then recursively walk these, to obtain the fully + * dereferenced canonicalised directory entry. + * + * If follow_path fails to walk a pathname either because a component + * doesn't exist, it is a non directory component when a directory + * component is expected, a symlink with an absolute path is encountered, + * or a symlink is encountered which cannot be recursively walked due to + * the above failures, then return NULL. + */ +static struct dir_ent *follow_path(struct dir_info *dir, char *pathname) +{ + char *comp, *path = pathname; + struct dir_ent *dir_ent = NULL; + + /* We cannot follow absolute paths */ + if(pathname[0] == '/') + return NULL; + + for(comp = get_comp(&path); comp; free(comp), comp = get_comp(&path)) { + if(strcmp(comp, ".") == 0) + continue; + + if(strcmp(comp, "..") == 0) { + /* Move to parent if we're not in the root directory */ + if(dir->depth > 1) { + dir = dir->dir_ent->our_dir; + dir_ent = NULL; /* lazily eval at loop exit */ + continue; + } else + /* Failed to walk pathname */ + return NULL; + } + + /* Lookup comp in current directory */ + dir_ent = lookup_comp(comp, dir); + if(dir_ent == NULL) + /* Doesn't exist, failed to walk pathname */ + return NULL; + + if((dir_ent->inode->buf.st_mode & S_IFMT) == S_IFLNK) { + /* Symbolic link, try to walk it */ + dir_ent = follow_path(dir, dir_ent->inode->symlink); + if(dir_ent == NULL) + /* Failed to follow symlink */ + return NULL; + } + + if((dir_ent->inode->buf.st_mode & S_IFMT) != S_IFDIR) + /* Cannot walk further */ + break; + + dir = dir_ent->dir; + } + + /* We will have exited the loop either because we've processed + * all the components, which means we've successfully walked the + * pathname, or because we've hit a non-directory, in which case + * it's success if this is the leaf component */ + if(comp) { + free(comp); + comp = get_comp(&path); + free(comp); + if(comp != NULL) + /* Not a leaf component */ + return NULL; + } else { + /* Fully walked pathname, dir_ent contains correct value unless + * we've walked to the parent ("..") in which case we need + * to resolve it here */ + if(!dir_ent) + dir_ent = dir->dir_ent; + } + + return dir_ent; +} + + +static int exists_fn(struct atom *atom, struct action_data *action_data) +{ + /* + * Test if a symlink exists within the output filesystem, that is, + * the symlink has a relative path, and the relative path refers + * to an entry within the output filesystem. + * + * This test function evaluates the path for symlinks - that is it + * follows any symlinks in the path (and any symlinks that it contains + * etc.), to discover the fully dereferenced canonicalised relative + * path. + * + * If any symlinks within the path do not exist or are absolute + * then the symlink is considered to not exist, as it cannot be + * fully dereferenced. + * + * exists operates on symlinks only, other files by definition + * exist + */ + if (!file_type_match(action_data->buf->st_mode, ACTION_LNK)) + return 1; + + /* dereference the symlink, and return TRUE if it exists */ + return follow_path(action_data->dir_ent->our_dir, + action_data->dir_ent->inode->symlink) ? 1 : 0; +} + + +static int absolute_fn(struct atom *atom, struct action_data *action_data) +{ + /* + * Test if a symlink has an absolute path, which by definition + * means the symbolic link may be broken (even if the absolute path + * does point into the filesystem being squashed, because the resultant + * filesystem can be mounted/unsquashed anywhere, it is unlikely the + * absolute path will still point to the right place). If you know that + * an absolute symlink will point to the right place then you don't need + * to use this function, and/or these symlinks can be excluded by + * use of other test operators. + * + * absolute operates on symlinks only, other files by definition + * don't have problems + */ + if (!file_type_match(action_data->buf->st_mode, ACTION_LNK)) + return 0; + + return action_data->dir_ent->inode->symlink[0] == '/'; +} + + +static int parse_expr_argX(struct test_entry *test, struct atom *atom, + int argno) +{ + /* Call parse_expr to parse argument, which should be an expression */ + + /* save the current parser state */ + char *save_cur_ptr = cur_ptr; + char *save_source = source; + + cur_ptr = source = atom->argv[argno]; + atom->data = parse_expr(0); + + cur_ptr = save_cur_ptr; + source = save_source; + + if(atom->data == NULL) { + /* parse_expr(0) will have reported the exact syntax error, + * but, because we recursively evaluated the expression, it + * will have been reported without the context of the stat + * test(). So here additionally report our failure to parse + * the expression in the stat() test to give context */ + TEST_SYNTAX_ERROR(test, 0, "Failed to parse expression\n"); + return 0; + } + + return 1; +} + + +static int parse_expr_arg0(struct test_entry *test, struct atom *atom) +{ + return parse_expr_argX(test, atom, 0); +} + + +static int parse_expr_arg1(struct test_entry *test, struct atom *atom) +{ + return parse_expr_argX(test, atom, 1); +} + + +static int stat_fn(struct atom *atom, struct action_data *action_data) +{ + struct stat buf; + struct action_data eval_action; + int match, res; + + /* evaluate the expression using the context of the inode + * pointed to by the symlink. This allows the inode attributes + * of the file pointed to by the symlink to be evaluated, rather + * than the symlink itself. + * + * Note, stat() deliberately does not evaluate the pathname, name or + * depth of the symlink, these are left with the symlink values. + * This allows stat() to be used on any symlink, rather than + * just symlinks which are contained (if the symlink is *not* + * contained then pathname, name and depth are meaningless as they + * are relative to the filesystem being squashed). */ + + /* if this isn't a symlink then stat will just return the current + * information, i.e. stat(expr) == expr. This is harmless and + * is better than returning TRUE or FALSE in a non symlink case */ + res = stat(action_data->pathname, &buf); + if(res == -1) { + if(expr_log_cmnd(LOG_ENABLED)) { + expr_log(atom->test->name); + expr_log("("); + expr_log_match(0); + expr_log(")"); + } + return 0; + } + + /* fill in the inode values of the file pointed to by the + * symlink, but, leave everything else the same */ + memcpy(&eval_action, action_data, sizeof(struct action_data)); + eval_action.buf = &buf; + + if(expr_log_cmnd(LOG_ENABLED)) { + expr_log(atom->test->name); + expr_log("("); + match = eval_expr_log(atom->data, &eval_action); + expr_log(")"); + } else + match = eval_expr(atom->data, &eval_action); + + return match; +} + + +static int readlink_fn(struct atom *atom, struct action_data *action_data) +{ + int match = 0; + struct dir_ent *dir_ent; + struct action_data eval_action; + + /* Dereference the symlink and evaluate the expression in the + * context of the file pointed to by the symlink. + * All attributes are updated to refer to the file that is pointed to. + * Thus the inode attributes, pathname, name and depth all refer to + * the dereferenced file, and not the symlink. + * + * If the symlink cannot be dereferenced because it doesn't exist in + * the output filesystem, or due to some other failure to + * walk the pathname (see follow_path above), then FALSE is returned. + * + * If you wish to evaluate the inode attributes of symlinks which + * exist in the source filestem (but not in the output filesystem then + * use stat instead (see above). + * + * readlink operates on symlinks only */ + if (!file_type_match(action_data->buf->st_mode, ACTION_LNK)) + goto finish; + + /* dereference the symlink, and get the directory entry it points to */ + dir_ent = follow_path(action_data->dir_ent->our_dir, + action_data->dir_ent->inode->symlink); + if(dir_ent == NULL) + goto finish; + + eval_action.name = dir_ent->name; + eval_action.pathname = strdup(pathname(dir_ent)); + eval_action.subpath = strdup(subpathname(dir_ent)); + eval_action.buf = &dir_ent->inode->buf; + eval_action.depth = dir_ent->our_dir->depth; + eval_action.dir_ent = dir_ent; + eval_action.root = action_data->root; + + if(expr_log_cmnd(LOG_ENABLED)) { + expr_log(atom->test->name); + expr_log("("); + match = eval_expr_log(atom->data, &eval_action); + expr_log(")"); + } else + match = eval_expr(atom->data, &eval_action); + + free(eval_action.pathname); + free(eval_action.subpath); + + return match; + +finish: + if(expr_log_cmnd(LOG_ENABLED)) { + expr_log(atom->test->name); + expr_log("("); + expr_log_match(0); + expr_log(")"); + } + + return 0; +} + + +static int eval_fn(struct atom *atom, struct action_data *action_data) +{ + int match; + char *path = atom->argv[0]; + struct dir_ent *dir_ent = action_data->dir_ent; + struct stat *buf = action_data->buf; + struct action_data eval_action; + + /* Follow path (arg1) and evaluate the expression (arg2) + * in the context of the file discovered. All attributes are updated + * to refer to the file that is pointed to. + * + * This test operation allows you to add additional context to the + * evaluation of the file being scanned, such as "if current file is + * XXX and the parent is YYY, then ..." Often times you need or + * want to test a combination of file status + * + * If the file referenced by the path does not exist in + * the output filesystem, or some other failure is experienced in + * walking the path (see follow_path above), then FALSE is returned. + * + * If you wish to evaluate the inode attributes of files which + * exist in the source filestem (but not in the output filesystem then + * use stat instead (see above). */ + + /* try to follow path, and get the directory entry it points to */ + if(path[0] == '/') { + /* absolute, walk from root - first skip the leading / */ + while(path[0] == '/') + path ++; + if(path[0] == '\0') + dir_ent = action_data->root->dir_ent; + else + dir_ent = follow_path(action_data->root, path); + } else { + /* relative, if first component is ".." walk from parent, + * otherwise walk from dir_ent. + * Note: this has to be handled here because follow_path + * will quite correctly refuse to execute ".." on anything + * which isn't a directory */ + if(strncmp(path, "..", 2) == 0 && (path[2] == '\0' || + path[2] == '/')) { + /* walk from parent */ + path += 2; + while(path[0] == '/') + path ++; + if(path[0] == '\0') + dir_ent = dir_ent->our_dir->dir_ent; + else + dir_ent = follow_path(dir_ent->our_dir, path); + } else if(!file_type_match(buf->st_mode, ACTION_DIR)) + dir_ent = NULL; + else + dir_ent = follow_path(dir_ent->dir, path); + } + + if(dir_ent == NULL) { + if(expr_log_cmnd(LOG_ENABLED)) { + expr_log(atom->test->name); + expr_log("("); + expr_log(atom->argv[0]); + expr_log(","); + expr_log_match(0); + expr_log(")"); + } + + return 0; + } + + eval_action.name = dir_ent->name; + eval_action.pathname = strdup(pathname(dir_ent)); + eval_action.subpath = strdup(subpathname(dir_ent)); + eval_action.buf = &dir_ent->inode->buf; + eval_action.depth = dir_ent->our_dir->depth; + eval_action.dir_ent = dir_ent; + eval_action.root = action_data->root; + + if(expr_log_cmnd(LOG_ENABLED)) { + expr_log(atom->test->name); + expr_log("("); + expr_log(eval_action.subpath); + expr_log(","); + match = eval_expr_log(atom->data, &eval_action); + expr_log(")"); + } else + match = eval_expr(atom->data, &eval_action); + + free(eval_action.pathname); + free(eval_action.subpath); + + return match; +} + + +/* + * Perm specific test code + */ +static int parse_perm_args(struct test_entry *test, struct atom *atom) +{ + int res = 1, mode, op, i; + char *arg; + struct mode_data *head = NULL, *cur = NULL; + struct perm_data *perm_data; + + if(atom->args == 0) { + TEST_SYNTAX_ERROR(test, 0, "One or more arguments expected\n"); + return 0; + } + + switch(atom->argv[0][0]) { + case '-': + op = PERM_ALL; + arg = atom->argv[0] + 1; + break; + case '/': + op = PERM_ANY; + arg = atom->argv[0] + 1; + break; + default: + op = PERM_EXACT; + arg = atom->argv[0]; + break; + } + + /* try to parse as an octal number */ + res = parse_octal_mode_args(atom->args, atom->argv, (void **) &head); + if(res == -1) { + /* parse as sym mode argument */ + for(i = 0; i < atom->args && res; i++, arg = atom->argv[i]) + res = parse_sym_mode_arg(arg, &head, &cur); + } + + if (res == 0) + goto finish; + + /* + * Evaluate the symbolic mode against a permission of 0000 octal + */ + mode = mode_execute(head, 0); + + perm_data = malloc(sizeof(struct perm_data)); + if (perm_data == NULL) + MEM_ERROR(); + + perm_data->op = op; + perm_data->mode = mode; + + atom->data = perm_data; + +finish: + while(head) { + struct mode_data *tmp = head; + head = head->next; + free(tmp); + } + + return res; +} + + +static int perm_fn(struct atom *atom, struct action_data *action_data) +{ + struct perm_data *perm_data = atom->data; + struct stat *buf = action_data->buf; + + switch(perm_data->op) { + case PERM_EXACT: + return (buf->st_mode & ~S_IFMT) == perm_data->mode; + case PERM_ALL: + return (buf->st_mode & perm_data->mode) == perm_data->mode; + case PERM_ANY: + default: + /* + * if no permission bits are set in perm_data->mode match + * on any file, this is to be consistent with find, which + * does this to be consistent with the behaviour of + * -perm -000 + */ + return perm_data->mode == 0 || (buf->st_mode & perm_data->mode); + } +} + + +#ifdef SQUASHFS_TRACE +static void dump_parse_tree(struct expr *expr) +{ + int i; + + if(expr->type == ATOM_TYPE) { + printf("%s", expr->atom.test->name); + if(expr->atom.args) { + printf("("); + for(i = 0; i < expr->atom.args; i++) { + printf("%s", expr->atom.argv[i]); + if (i + 1 < expr->atom.args) + printf(","); + } + printf(")"); + } + } else if (expr->type == UNARY_TYPE) { + printf("%s", token_table[expr->unary_op.op].string); + dump_parse_tree(expr->unary_op.expr); + } else { + printf("("); + dump_parse_tree(expr->expr_op.lhs); + printf("%s", token_table[expr->expr_op.op].string); + dump_parse_tree(expr->expr_op.rhs); + printf(")"); + } +} + + +void dump_action_list(struct action *spec_list, int spec_count) +{ + int i; + + for (i = 0; i < spec_count; i++) { + printf("%s", spec_list[i].action->name); + if (spec_list[i].args) { + int n; + + printf("("); + for (n = 0; n < spec_list[i].args; n++) { + printf("%s", spec_list[i].argv[n]); + if (n + 1 < spec_list[i].args) + printf(","); + } + printf(")"); + } + printf("="); + dump_parse_tree(spec_list[i].expr); + printf("\n"); + } +} + + +void dump_actions() +{ + dump_action_list(exclude_spec, exclude_count); + dump_action_list(fragment_spec, fragment_count); + dump_action_list(other_spec, other_count); + dump_action_list(move_spec, move_count); + dump_action_list(empty_spec, empty_count); +} +#else +void dump_actions() +{ +} +#endif + + static struct test_entry test_table[] = { - { "name", 1, name_fn}, - { "pathname", 1, pathname_fn, check_pathname}, - { "subpathname", 1, subpathname_fn, check_pathname}, - { "filesize", 1, filesize_fn, parse_number_arg}, - { "dirsize", 1, dirsize_fn, parse_number_arg}, - { "size", 1, size_fn, parse_number_arg}, - { "inode", 1, inode_fn, parse_number_arg}, - { "nlink", 1, nlink_fn, parse_number_arg}, - { "fileblocks", 1, fileblocks_fn, parse_number_arg}, - { "dirblocks", 1, dirblocks_fn, parse_number_arg}, - { "blocks", 1, blocks_fn, parse_number_arg}, - { "gid", 1, gid_fn, parse_number_arg}, - { "uid", 1, uid_fn, parse_number_arg}, - { "depth", 1, depth_fn, parse_number_arg}, - { "filesize_range", 2, filesize_range_fn, parse_range_args}, - { "dirsize_range", 2, dirsize_range_fn, parse_range_args}, - { "size_range", 2, size_range_fn, parse_range_args}, - { "inode_range", 2, inode_range_fn, parse_range_args}, - { "nlink_range", 2, nlink_range_fn, parse_range_args}, - { "fileblocks_range", 2, fileblocks_range_fn, parse_range_args}, - { "dirblocks_range", 2, dirblocks_range_fn, parse_range_args}, - { "blocks_range", 2, blocks_range_fn, parse_range_args}, - { "gid_range", 2, gid_range_fn, parse_range_args}, - { "uid_range", 2, uid_range_fn, parse_range_args}, - { "depth_range", 2, depth_range_fn, parse_range_args}, - { "type", 1, type_fn, parse_type_arg}, - { "true", 0, true_fn, NULL}, - { "false", 0, false_fn, NULL}, - { "file", 1, file_fn, parse_file_arg}, - { "exec", 1, exec_fn, NULL}, + { "name", 1, name_fn, NULL, 1}, + { "pathname", 1, pathname_fn, check_pathname, 1, 0}, + { "subpathname", 1, subpathname_fn, check_pathname, 1, 0}, + { "filesize", 1, filesize_fn, parse_number_arg, 1, 0}, + { "dirsize", 1, dirsize_fn, parse_number_arg, 1, 0}, + { "size", 1, size_fn, parse_number_arg, 1, 0}, + { "inode", 1, inode_fn, parse_number_arg, 1, 0}, + { "nlink", 1, nlink_fn, parse_number_arg, 1, 0}, + { "fileblocks", 1, fileblocks_fn, parse_number_arg, 1, 0}, + { "dirblocks", 1, dirblocks_fn, parse_number_arg, 1, 0}, + { "blocks", 1, blocks_fn, parse_number_arg, 1, 0}, + { "gid", 1, gid_fn, parse_gid_arg, 1, 0}, + { "uid", 1, uid_fn, parse_uid_arg, 1, 0}, + { "depth", 1, depth_fn, parse_number_arg, 1, 0}, + { "dircount", 1, dircount_fn, parse_number_arg, 0, 0}, + { "filesize_range", 2, filesize_range_fn, parse_range_args, 1, 0}, + { "dirsize_range", 2, dirsize_range_fn, parse_range_args, 1, 0}, + { "size_range", 2, size_range_fn, parse_range_args, 1, 0}, + { "inode_range", 2, inode_range_fn, parse_range_args, 1, 0}, + { "nlink_range", 2, nlink_range_fn, parse_range_args, 1, 0}, + { "fileblocks_range", 2, fileblocks_range_fn, parse_range_args, 1, 0}, + { "dirblocks_range", 2, dirblocks_range_fn, parse_range_args, 1, 0}, + { "blocks_range", 2, blocks_range_fn, parse_range_args, 1, 0}, + { "gid_range", 2, gid_range_fn, parse_range_args, 1, 0}, + { "uid_range", 2, uid_range_fn, parse_range_args, 1, 0}, + { "depth_range", 2, depth_range_fn, parse_range_args, 1, 0}, + { "dircount_range", 2, dircount_range_fn, parse_range_args, 0, 0}, + { "type", 1, type_fn, parse_type_arg, 1, 0}, + { "true", 0, true_fn, NULL, 1, 0}, + { "false", 0, false_fn, NULL, 1, 0}, + { "file", 1, file_fn, parse_file_arg, 1, 0}, + { "exec", 1, exec_fn, NULL, 1, 0}, + { "exists", 0, exists_fn, NULL, 0, 0}, + { "absolute", 0, absolute_fn, NULL, 0, 0}, + { "stat", 1, stat_fn, parse_expr_arg0, 1, 1}, + { "readlink", 1, readlink_fn, parse_expr_arg0, 0, 1}, + { "eval", 2, eval_fn, parse_expr_arg1, 0, 1}, + { "perm", -2, perm_fn, parse_perm_args, 1, 0}, { "", -1 } }; @@ -2318,6 +3258,9 @@ { "guid", GUID_ACTION, 2, ACTION_ALL_LNK, parse_guid_args, guid_action}, { "mode", MODE_ACTION, -2, ACTION_ALL, parse_mode_args, mode_action }, { "empty", EMPTY_ACTION, -2, ACTION_DIR, parse_empty_args, NULL}, - { "move", MOVE_ACTION, -2, ACTION_ALL_LNK, NULL, NULL}, + { "move", MOVE_ACTION, 1, ACTION_ALL_LNK, NULL, NULL}, + { "prune", PRUNE_ACTION, 0, ACTION_ALL_LNK, NULL, NULL}, + { "chmod", MODE_ACTION, -2, ACTION_ALL, parse_mode_args, mode_action }, + { "noop", NOOP_ACTION, 0, ACTION_ALL, NULL, noop_action }, { "", 0, -1, 0, NULL, NULL} }; diff -Nru squashfs-tools-4.2+20130409/action.h squashfs-tools-4.3+20140919/action.h --- squashfs-tools-4.2+20130409/action.h 2013-05-09 10:39:11.000000000 +0200 +++ squashfs-tools-4.3+20140919/action.h 2015-07-20 21:03:05.000000000 +0200 @@ -1,8 +1,10 @@ +#ifndef ACTION_H +#define ACTION_H /* * Create a squashfs filesystem. This is a highly compressed read only * filesystem. * - * Copyright (c) 2011, 2012, 2013 + * Copyright (c) 2011, 2012, 2013, 2014 * Phillip Lougher * * This program is free software; you can redistribute it and/or @@ -72,6 +74,7 @@ fprintf(stderr, "Failed to parse action \"%s\"\n", source); \ fprintf(stderr, "Syntax error: "S, ##ARGS); \ fprintf(stderr, "Got here \"%s\"\n", src); \ + free(src); \ } #define TEST_SYNTAX_ERROR(TEST, ARG, S, ARGS...) { \ @@ -81,6 +84,7 @@ fprintf(stderr, "Syntax error in \"%s()\", arg %d: "S, TEST->name, \ ARG, ##ARGS); \ fprintf(stderr, "Got here \"%s\"\n", src); \ + free(src); \ } struct expr; @@ -94,6 +98,7 @@ struct atom { struct test_entry *test; + int args; char **argv; void *data; }; @@ -139,6 +144,8 @@ int args; int (*fn)(struct atom *, struct action_data *); int (*parse_args)(struct test_entry *, struct atom *); + int exclude_ok; + int handle_logging; }; @@ -168,15 +175,28 @@ #define MODE_ACTION 11 #define EMPTY_ACTION 12 #define MOVE_ACTION 13 +#define PRUNE_ACTION 14 +#define NOOP_ACTION 15 /* * Define what file types each action operates over */ -#define ACTION_DIR S_IFDIR -#define ACTION_REG S_IFREG -#define ACTION_ALL_LNK (S_IFDIR | S_IFREG | S_IFBLK | S_IFCHR | S_IFSOCK | \ - S_IFIFO | S_IFLNK) -#define ACTION_ALL (S_IFDIR | S_IFREG | S_IFBLK | S_IFCHR | S_IFSOCK | S_IFIFO) +#define ACTION_DIR 1 +#define ACTION_REG 2 +#define ACTION_ALL_LNK 3 +#define ACTION_ALL 4 +#define ACTION_LNK 5 + + +/* + * Action logging requested, specified by the various + * -action, -true-action, -false-action and -verbose-action + * options + */ +#define ACTION_LOG_NONE 0 +#define ACTION_LOG_TRUE 1 +#define ACTION_LOG_FALSE 2 +#define ACTION_LOG_VERBOSE ACTION_LOG_TRUE | ACTION_LOG_FALSE struct action_entry { char *name; @@ -194,6 +214,8 @@ char *pathname; char *subpath; struct stat *buf; + struct dir_ent *dir_ent; + struct dir_info *root; }; @@ -204,6 +226,7 @@ char **argv; struct expr *expr; void *data; + int verbose; }; @@ -269,20 +292,37 @@ /* + * Perm test function specific definitions + */ +#define PERM_ALL 1 +#define PERM_ANY 2 +#define PERM_EXACT 3 + +struct perm_data { + int op; + int mode; +}; + + +/* * External function definitions */ -extern int parse_action(char *); +extern int parse_action(char *, int verbose); extern void dump_actions(); -extern void *eval_frag_actions(struct dir_ent *); +extern void *eval_frag_actions(struct dir_info *, struct dir_ent *); extern void *get_frag_action(void *); -extern int eval_exclude_actions(char *, char *, char *, struct stat *, int); -extern void eval_actions(struct dir_ent *); -extern int eval_empty_actions(struct dir_ent *dir_ent); +extern int eval_exclude_actions(char *, char *, char *, struct stat *, int, + struct dir_ent *); +extern void eval_actions(struct dir_info *, struct dir_ent *); +extern int eval_empty_actions(struct dir_info *, struct dir_ent *dir_ent); extern void eval_move_actions(struct dir_info *, struct dir_ent *); +extern int eval_prune_actions(struct dir_info *, struct dir_ent *); extern void do_move_actions(); extern int read_bytes(int, void *, int); extern int actions(); extern int move_actions(); extern int empty_actions(); -extern int read_action_file(char *); +extern int read_action_file(char *, int); extern int exclude_actions(); +extern int prune_actions(); +#endif diff -Nru squashfs-tools-4.2+20130409/caches-queues-lists.c squashfs-tools-4.3+20140919/caches-queues-lists.c --- squashfs-tools-4.2+20130409/caches-queues-lists.c 2013-05-09 10:39:11.000000000 +0200 +++ squashfs-tools-4.3+20140919/caches-queues-lists.c 2015-07-20 21:03:05.000000000 +0200 @@ -2,7 +2,7 @@ * Create a squashfs filesystem. This is a highly compressed read only * filesystem. * - * Copyright (c) 2013 + * Copyright (c) 2013, 2014 * Phillip Lougher * * This program is free software; you can redistribute it and/or @@ -25,12 +25,11 @@ #include #include #include +#include #include "error.h" #include "caches-queues-lists.h" -int first_freelist = 1; - extern int add_overflow(int, int); extern int multiply_overflow(int, int); @@ -98,37 +97,182 @@ } -#define CALCULATE_HASH(start) (start & 0xffff) \ +int queue_empty(struct queue *queue) +{ + int empty; + pthread_cleanup_push((void *) pthread_mutex_unlock, &queue->mutex); + pthread_mutex_lock(&queue->mutex); -/* Called with the cache mutex held */ -void insert_hash_table(struct cache *cache, struct file_buffer *entry) + empty = queue->readp == queue->writep; + + pthread_cleanup_pop(1); + + return empty; +} + + +void queue_flush(struct queue *queue) +{ + pthread_cleanup_push((void *) pthread_mutex_unlock, &queue->mutex); + pthread_mutex_lock(&queue->mutex); + + queue->readp = queue->writep; + + pthread_cleanup_pop(1); +} + + +void dump_queue(struct queue *queue) { - int hash = CALCULATE_HASH(entry->index); + pthread_cleanup_push((void *) pthread_mutex_unlock, &queue->mutex); + pthread_mutex_lock(&queue->mutex); + + printf("\tMax size %d, size %d%s\n", queue->size - 1, + queue->readp <= queue->writep ? queue->writep - queue->readp : + queue->size - queue->readp + queue->writep, + queue->readp == queue->writep ? " (EMPTY)" : + ((queue->writep + 1) % queue->size) == queue->readp ? + " (FULL)" : ""); - entry->hash_next = cache->hash_table[hash]; - cache->hash_table[hash] = entry; - entry->hash_prev = NULL; - if(entry->hash_next) - entry->hash_next->hash_prev = entry; + pthread_cleanup_pop(1); } +/* define seq queue hash tables */ +#define CALCULATE_SEQ_HASH(N) CALCULATE_HASH(N) + +/* Called with the seq queue mutex held */ +INSERT_HASH_TABLE(seq, struct seq_queue, CALCULATE_SEQ_HASH, sequence, seq) + /* Called with the cache mutex held */ -void remove_hash_table(struct cache *cache, struct file_buffer *entry) +REMOVE_HASH_TABLE(seq, struct seq_queue, CALCULATE_SEQ_HASH, sequence, seq); + +static unsigned int sequence = 0; + + +struct seq_queue *seq_queue_init() +{ + struct seq_queue *queue = malloc(sizeof(struct seq_queue)); + if(queue == NULL) + MEM_ERROR(); + + memset(queue, 0, sizeof(struct seq_queue)); + + pthread_mutex_init(&queue->mutex, NULL); + pthread_cond_init(&queue->wait, NULL); + + return queue; +} + + +void seq_queue_put(struct seq_queue *queue, struct file_buffer *entry) { - if(entry->hash_prev) - entry->hash_prev->hash_next = entry->hash_next; + pthread_cleanup_push((void *) pthread_mutex_unlock, &queue->mutex); + pthread_mutex_lock(&queue->mutex); + + insert_seq_hash_table(queue, entry); + + if(entry->fragment) + queue->fragment_count ++; else - cache->hash_table[CALCULATE_HASH(entry->index)] = - entry->hash_next; - if(entry->hash_next) - entry->hash_next->hash_prev = entry->hash_prev; + queue->block_count ++; + + if(entry->sequence == sequence) + pthread_cond_signal(&queue->wait); - entry->hash_prev = entry->hash_next = NULL; + pthread_cleanup_pop(1); } +struct file_buffer *seq_queue_get(struct seq_queue *queue) +{ + /* + * Look-up buffer matching sequence in the queue, if found return + * it, otherwise wait until it arrives + */ + int hash = CALCULATE_SEQ_HASH(sequence); + struct file_buffer *entry; + + pthread_cleanup_push((void *) pthread_mutex_unlock, &queue->mutex); + pthread_mutex_lock(&queue->mutex); + + while(1) { + for(entry = queue->hash_table[hash]; entry; + entry = entry->seq_next) + if(entry->sequence == sequence) + break; + + if(entry) { + /* + * found the buffer in the queue, decrement the + * appropriate count, and remove from hash list + */ + if(entry->fragment) + queue->fragment_count --; + else + queue->block_count --; + + remove_seq_hash_table(queue, entry); + + sequence ++; + + break; + } + + /* entry not found, wait for it to arrive */ + pthread_cond_wait(&queue->wait, &queue->mutex); + } + + pthread_cleanup_pop(1); + + return entry; +} + + +void seq_queue_flush(struct seq_queue *queue) +{ + int i; + + pthread_cleanup_push((void *) pthread_mutex_unlock, &queue->mutex); + pthread_mutex_lock(&queue->mutex); + + for(i = 0; i < HASH_SIZE; i++) + queue->hash_table[i] = NULL; + + queue->fragment_count = queue->block_count = 0; + + pthread_cleanup_pop(1); +} + + +void dump_seq_queue(struct seq_queue *queue, int fragment_queue) +{ + int size; + + pthread_cleanup_push((void *) pthread_mutex_unlock, &queue->mutex); + pthread_mutex_lock(&queue->mutex); + + size = fragment_queue ? queue->fragment_count : queue->block_count; + + printf("\tMax size unlimited, size %d%s\n", size, + size == 0 ? " (EMPTY)" : ""); + + pthread_cleanup_pop(1); +} + + +/* define cache hash tables */ +#define CALCULATE_CACHE_HASH(N) CALCULATE_HASH(llabs(N)) + +/* Called with the cache mutex held */ +INSERT_HASH_TABLE(cache, struct cache, CALCULATE_CACHE_HASH, index, hash) + +/* Called with the cache mutex held */ +REMOVE_HASH_TABLE(cache, struct cache, CALCULATE_CACHE_HASH, index, hash); + +/* define cache free list */ + /* Called with the cache mutex held */ INSERT_LIST(free, struct file_buffer) @@ -136,7 +280,8 @@ REMOVE_LIST(free, struct file_buffer) -struct cache *cache_init(int buffer_size, int max_buffers) +struct cache *cache_init(int buffer_size, int max_buffers, int noshrink_lookup, + int first_freelist) { struct cache *cache = malloc(sizeof(struct cache)); @@ -146,10 +291,38 @@ cache->max_buffers = max_buffers; cache->buffer_size = buffer_size; cache->count = 0; + cache->used = 0; cache->free_list = NULL; + + /* + * The cache will grow up to max_buffers in size in response to + * an increase in readhead/number of buffers in flight. But + * once the outstanding buffers gets returned, we can either elect + * to shrink the cache, or to put the freed blocks onto a free list. + * + * For the caches where we want to do lookup (fragment/writer), + * a don't shrink policy is best, for the reader cache it + * makes no sense to keep buffers around longer than necessary as + * we don't do any lookup on those blocks. + */ + cache->noshrink_lookup = noshrink_lookup; + + /* + * The default use freelist before growing cache policy behaves + * poorly with appending - with many duplicates the caches + * do not grow due to the fact that large queues of outstanding + * fragments/writer blocks do not occur, leading to small caches + * and un-uncessary performance loss to frequent cache + * replacement in the small caches. Therefore with appending + * change the policy to grow the caches before reusing blocks + * from the freelist + */ + cache->first_freelist = first_freelist; + memset(cache->hash_table, 0, sizeof(struct file_buffer *) * 65536); pthread_mutex_init(&cache->mutex, NULL); pthread_cond_init(&cache->wait_for_free, NULL); + pthread_cond_init(&cache->wait_for_unlock, NULL); return cache; } @@ -159,7 +332,7 @@ { /* Lookup block in the cache, if found return with usage count * incremented, if not found return NULL */ - int hash = CALCULATE_HASH(index); + int hash = CALCULATE_CACHE_HASH(index); struct file_buffer *entry; pthread_cleanup_push((void *) pthread_mutex_unlock, &cache->mutex); @@ -173,8 +346,11 @@ /* found the block in the cache, increment used count and * if necessary remove from free list so it won't disappear */ + if(entry->used == 0) { + remove_free_list(&cache->free_list, entry); + cache->used ++; + } entry->used ++; - remove_free_list(&cache->free_list, entry); } pthread_cleanup_pop(1); @@ -183,50 +359,76 @@ } -struct file_buffer *cache_get(struct cache *cache, long long index, int keep) +static struct file_buffer *cache_freelist(struct cache *cache) { - /* Get a free block out of the cache indexed on index. */ - struct file_buffer *entry; + struct file_buffer *entry = cache->free_list; + + remove_free_list(&cache->free_list, entry); + + /* a block on the free_list is hashed */ + remove_cache_hash_table(cache, entry); + cache->used ++; + return entry; +} + + +static struct file_buffer *cache_alloc(struct cache *cache) +{ + struct file_buffer *entry = malloc(sizeof(struct file_buffer) + + cache->buffer_size); + if(entry == NULL) + MEM_ERROR(); + + entry->cache = cache; + entry->free_prev = entry->free_next = NULL; + cache->count ++; + return entry; +} + + +static struct file_buffer *_cache_get(struct cache *cache, long long index, + int hash) +{ + /* Get a free block out of the cache indexed on index. */ + struct file_buffer *entry = NULL; + pthread_cleanup_push((void *) pthread_mutex_unlock, &cache->mutex); pthread_mutex_lock(&cache->mutex); while(1) { - /* first try to get a block from the free list */ - if(first_freelist && cache->free_list) { - /* a block on the free_list is a "keep" block */ - entry = cache->free_list; - remove_free_list(&cache->free_list, entry); - remove_hash_table(cache, entry); - break; - } else if(cache->count < cache->max_buffers) { - /* next try to allocate new block */ - entry = malloc(sizeof(struct file_buffer) + - cache->buffer_size); - if(entry == NULL) - MEM_ERROR(); - entry->cache = cache; - entry->free_prev = entry->free_next = NULL; - cache->count ++; - break; - } else if(!first_freelist && cache->free_list) { - /* a block on the free_list is a "keep" block */ - entry = cache->free_list; - remove_free_list(&cache->free_list, entry); - remove_hash_table(cache, entry); + if(cache->noshrink_lookup) { + /* first try to get a block from the free list */ + if(cache->first_freelist && cache->free_list) + entry = cache_freelist(cache); + else if(cache->count < cache->max_buffers) { + entry = cache_alloc(cache); + cache->used ++; + } else if(!cache->first_freelist && cache->free_list) + entry = cache_freelist(cache); + } else { /* shrinking non-lookup cache */ + if(cache->count < cache->max_buffers) { + entry = cache_alloc(cache); + if(cache->count > cache->max_count) + cache->max_count = cache->count; + } + } + + if(entry) break; - } else - /* wait for a block */ - pthread_cond_wait(&cache->wait_for_free, &cache->mutex); + + /* wait for a block */ + pthread_cond_wait(&cache->wait_for_free, &cache->mutex); } - /* initialise block and if a keep block insert into the hash table */ + /* initialise block and if hash is set insert into the hash table */ entry->used = 1; + entry->locked = FALSE; + entry->wait_on_unlock = FALSE; entry->error = FALSE; - entry->keep = keep; - if(keep) { + if(hash) { entry->index = index; - insert_hash_table(cache, entry); + insert_cache_hash_table(cache, entry); } pthread_cleanup_pop(1); @@ -235,17 +437,28 @@ } -void cache_rehash(struct file_buffer *entry, long long index) +struct file_buffer *cache_get(struct cache *cache, long long index) +{ + return _cache_get(cache, index, 1); +} + + +struct file_buffer *cache_get_nohash(struct cache *cache) +{ + return _cache_get(cache, 0, 0); +} + + +void cache_hash(struct file_buffer *entry, long long index) { struct cache *cache = entry->cache; pthread_cleanup_push((void *) pthread_mutex_unlock, &cache->mutex); pthread_mutex_lock(&cache->mutex); - if(entry->keep) - remove_hash_table(cache, entry); - entry->keep = TRUE; + entry->index = index; - insert_hash_table(cache, entry); + insert_cache_hash_table(cache, entry); + pthread_cleanup_pop(1); } @@ -254,10 +467,16 @@ { struct cache *cache; - /* finished with this cache entry, once the usage count reaches zero it - * can be reused and if a keep block put onto the free list. As keep - * blocks remain accessible via the hash table they can be found - * getting a new lease of life before they are reused. */ + /* + * Finished with this cache entry, once the usage count reaches zero it + * can be reused. + * + * If noshrink_lookup is set, put the block onto the free list. + * As blocks remain accessible via the hash table they can be found + * getting a new lease of life before they are reused. + * + * if noshrink_lookup is not set then shrink the cache. + */ if(entry == NULL) return; @@ -269,9 +488,10 @@ entry->used --; if(entry->used == 0) { - if(entry->keep) + if(cache->noshrink_lookup) { insert_free_list(&cache->free_list, entry); - else { + cache->used --; + } else { free(entry); cache->count --; } @@ -281,4 +501,144 @@ } pthread_cleanup_pop(1); +} + + +void dump_cache(struct cache *cache) +{ + pthread_cleanup_push((void *) pthread_mutex_unlock, &cache->mutex); + pthread_mutex_lock(&cache->mutex); + + if(cache->noshrink_lookup) + printf("\tMax buffers %d, Current size %d, Used %d, %s\n", + cache->max_buffers, cache->count, cache->used, + cache->free_list ? "Free buffers" : "No free buffers"); + else + printf("\tMax buffers %d, Current size %d, Maximum historical " + "size %d\n", cache->max_buffers, cache->count, + cache->max_count); + + pthread_cleanup_pop(1); +} + + +struct file_buffer *cache_get_nowait(struct cache *cache, long long index) +{ + struct file_buffer *entry = NULL; + /* + * block doesn't exist, create it, but return it with the + * locked flag set, so nothing tries to use it while it doesn't + * contain data. + * + * If there's no space in the cache then return NULL. + */ + + pthread_cleanup_push((void *) pthread_mutex_unlock, &cache->mutex); + pthread_mutex_lock(&cache->mutex); + + /* first try to get a block from the free list */ + if(cache->first_freelist && cache->free_list) + entry = cache_freelist(cache); + else if(cache->count < cache->max_buffers) { + entry = cache_alloc(cache); + cache->used ++; + } else if(!cache->first_freelist && cache->free_list) + entry = cache_freelist(cache); + + if(entry) { + /* initialise block and insert into the hash table */ + entry->used = 1; + entry->locked = TRUE; + entry->wait_on_unlock = FALSE; + entry->error = FALSE; + entry->index = index; + insert_cache_hash_table(cache, entry); + } + + pthread_cleanup_pop(1); + + return entry; +} + + +struct file_buffer *cache_lookup_nowait(struct cache *cache, long long index, + char *locked) +{ + /* + * Lookup block in the cache, if found return it with the locked flag + * indicating whether it is currently locked. In both cases increment + * the used count. + * + * If it doesn't exist in the cache return NULL; + */ + int hash = CALCULATE_CACHE_HASH(index); + struct file_buffer *entry; + + pthread_cleanup_push((void *) pthread_mutex_unlock, &cache->mutex); + pthread_mutex_lock(&cache->mutex); + + /* first check if the entry already exists */ + for(entry = cache->hash_table[hash]; entry; entry = entry->hash_next) + if(entry->index == index) + break; + + if(entry) { + if(entry->used == 0) { + remove_free_list(&cache->free_list, entry); + cache->used ++; + } + entry->used ++; + *locked = entry->locked; + } + + pthread_cleanup_pop(1); + + return entry; +} + + +void cache_wait_unlock(struct file_buffer *buffer) +{ + struct cache *cache = buffer->cache; + + pthread_cleanup_push((void *) pthread_mutex_unlock, &cache->mutex); + pthread_mutex_lock(&cache->mutex); + + while(buffer->locked) { + /* + * another thread is filling this in, wait until it + * becomes unlocked. Used has been incremented to ensure it + * doesn't get reused. By definition a block can't be + * locked and unused, and so we don't need to worry + * about it being on the freelist now, but, it may + * become unused when unlocked unless used is + * incremented + */ + buffer->wait_on_unlock = TRUE; + pthread_cond_wait(&cache->wait_for_unlock, &cache->mutex); + } + + pthread_cleanup_pop(1); +} + + +void cache_unlock(struct file_buffer *entry) +{ + struct cache *cache = entry->cache; + + /* + * Unlock this locked cache entry. If anything is waiting for this + * to become unlocked, wake it up. + */ + pthread_cleanup_push((void *) pthread_mutex_unlock, &cache->mutex); + pthread_mutex_lock(&cache->mutex); + + entry->locked = FALSE; + + if(entry->wait_on_unlock) { + entry->wait_on_unlock = FALSE; + pthread_cond_broadcast(&cache->wait_for_unlock); + } + + pthread_cleanup_pop(1); } diff -Nru squashfs-tools-4.2+20130409/caches-queues-lists.h squashfs-tools-4.3+20140919/caches-queues-lists.h --- squashfs-tools-4.2+20130409/caches-queues-lists.h 2013-05-09 10:39:11.000000000 +0200 +++ squashfs-tools-4.3+20140919/caches-queues-lists.h 2015-07-20 21:03:05.000000000 +0200 @@ -1,8 +1,10 @@ +#ifndef CACHES_QUEUES_LISTS_H +#define CACHES_QUEUES_LISTS_H /* * Create a squashfs filesystem. This is a highly compressed read only * filesystem. * - * Copyright (c) 2013 + * Copyright (c) 2013, 2014 * Phillip Lougher * * This program is free software; you can redistribute it and/or @@ -22,24 +24,104 @@ * caches-queues-lists.h */ +#define INSERT_LIST(NAME, TYPE) \ +void insert_##NAME##_list(TYPE **list, TYPE *entry) { \ + if(*list) { \ + entry->NAME##_next = *list; \ + entry->NAME##_prev = (*list)->NAME##_prev; \ + (*list)->NAME##_prev->NAME##_next = entry; \ + (*list)->NAME##_prev = entry; \ + } else { \ + *list = entry; \ + entry->NAME##_prev = entry->NAME##_next = entry; \ + } \ +} + + +#define REMOVE_LIST(NAME, TYPE) \ +void remove_##NAME##_list(TYPE **list, TYPE *entry) { \ + if(entry->NAME##_prev == entry && entry->NAME##_next == entry) { \ + /* only this entry in the list */ \ + *list = NULL; \ + } else if(entry->NAME##_prev != NULL && entry->NAME##_next != NULL) { \ + /* more than one entry in the list */ \ + entry->NAME##_next->NAME##_prev = entry->NAME##_prev; \ + entry->NAME##_prev->NAME##_next = entry->NAME##_next; \ + if(*list == entry) \ + *list = entry->NAME##_next; \ + } \ + entry->NAME##_prev = entry->NAME##_next = NULL; \ +} + + +#define INSERT_HASH_TABLE(NAME, TYPE, HASH_FUNCTION, FIELD, LINK) \ +void insert_##NAME##_hash_table(TYPE *container, struct file_buffer *entry) \ +{ \ + int hash = HASH_FUNCTION(entry->FIELD); \ +\ + entry->LINK##_next = container->hash_table[hash]; \ + container->hash_table[hash] = entry; \ + entry->LINK##_prev = NULL; \ + if(entry->LINK##_next) \ + entry->LINK##_next->LINK##_prev = entry; \ +} + + +#define REMOVE_HASH_TABLE(NAME, TYPE, HASH_FUNCTION, FIELD, LINK) \ +void remove_##NAME##_hash_table(TYPE *container, struct file_buffer *entry) \ +{ \ + if(entry->LINK##_prev) \ + entry->LINK##_prev->LINK##_next = entry->LINK##_next; \ + else \ + container->hash_table[HASH_FUNCTION(entry->FIELD)] = \ + entry->LINK##_next; \ + if(entry->LINK##_next) \ + entry->LINK##_next->LINK##_prev = entry->LINK##_prev; \ +\ + entry->LINK##_prev = entry->LINK##_next = NULL; \ +} + +#define HASH_SIZE 65536 +#define CALCULATE_HASH(n) ((n) & 0xffff) + + /* struct describing a cache entry passed between threads */ struct file_buffer { - struct cache *cache; - struct file_buffer *hash_next; - struct file_buffer *hash_prev; - struct file_buffer *free_next; - struct file_buffer *free_prev; - struct file_buffer *next; + union { + long long index; + long long sequence; + }; long long file_size; - long long index; - long long block; - long long sequence; + union { + long long block; + unsigned short checksum; + }; + struct cache *cache; + union { + struct file_info *dupl_start; + struct file_buffer *hash_next; + }; + union { + int duplicate; + struct file_buffer *hash_prev; + }; + union { + struct { + struct file_buffer *free_next; + struct file_buffer *free_prev; + }; + struct { + struct file_buffer *seq_next; + struct file_buffer *seq_prev; + }; + }; int size; int c_byte; - char keep; char used; char fragment; char error; + char locked; + char wait_on_unlock; char noD; char data[0]; }; @@ -57,59 +139,62 @@ }; +/* + * struct describing seq_queues used to pass data between the read + * thread and the deflate and main threads + */ +struct seq_queue { + int fragment_count; + int block_count; + struct file_buffer *hash_table[HASH_SIZE]; + pthread_mutex_t mutex; + pthread_cond_t wait; +}; + + /* Cache status struct. Caches are used to keep track of memory buffers passed between different threads */ struct cache { int max_buffers; int count; int buffer_size; + int noshrink_lookup; + int first_freelist; + union { + int used; + int max_count; + }; pthread_mutex_t mutex; pthread_cond_t wait_for_free; + pthread_cond_t wait_for_unlock; struct file_buffer *free_list; - struct file_buffer *hash_table[65536]; + struct file_buffer *hash_table[HASH_SIZE]; }; -#define INSERT_LIST(NAME, TYPE) \ -void insert_##NAME##_list(TYPE **list, TYPE *entry) { \ - if(*list) { \ - entry->NAME##_next = *list; \ - entry->NAME##_prev = (*list)->NAME##_prev; \ - (*list)->NAME##_prev->NAME##_next = entry; \ - (*list)->NAME##_prev = entry; \ - } else { \ - *list = entry; \ - entry->NAME##_prev = entry->NAME##_next = entry; \ - } \ -} - - -#define REMOVE_LIST(NAME, TYPE) \ -void remove_##NAME##_list(TYPE **list, TYPE *entry) { \ - if(entry->NAME##_prev == entry && entry->NAME##_next == entry) { \ - /* only this entry in the list */ \ - *list = NULL; \ - } else if(entry->NAME##_prev != NULL && entry->NAME##_next != NULL) { \ - /* more than one entry in the list */ \ - entry->NAME##_next->NAME##_prev = entry->NAME##_prev; \ - entry->NAME##_prev->NAME##_next = entry->NAME##_next; \ - if(*list == entry) \ - *list = entry->NAME##_next; \ - } \ - entry->NAME##_prev = entry->NAME##_next = NULL; \ -} - extern struct queue *queue_init(int); extern void queue_put(struct queue *, void *); extern void *queue_get(struct queue *); -extern struct cache *cache_init(int, int); +extern int queue_empty(struct queue *); +extern void queue_flush(struct queue *); +extern void dump_queue(struct queue *); +extern struct seq_queue *seq_queue_init(); +extern void seq_queue_put(struct seq_queue *, struct file_buffer *); +extern void dump_seq_queue(struct seq_queue *, int); +extern struct file_buffer *seq_queue_get(struct seq_queue *); +extern void seq_queue_flush(struct seq_queue *); +extern struct cache *cache_init(int, int, int, int); extern struct file_buffer *cache_lookup(struct cache *, long long); -extern struct file_buffer *cache_get(struct cache *, long long, int); -extern void cache_rehash(struct file_buffer *, long long); +extern struct file_buffer *cache_get(struct cache *, long long); +extern struct file_buffer *cache_get_nohash(struct cache *); +extern void cache_hash(struct file_buffer *, long long); extern void cache_block_put(struct file_buffer *); - -//extern void insert_free_list(struct file_buffer **, struct file_buffer *); -//extern void remove_free_list(struct file_buffer **, struct file_buffer *); +extern void dump_cache(struct cache *); +extern struct file_buffer *cache_get_nowait(struct cache *, long long); +extern struct file_buffer *cache_lookup_nowait(struct cache *, long long, + char *); +extern void cache_wait_unlock(struct file_buffer *); +extern void cache_unlock(struct file_buffer *); extern int first_freelist; - +#endif diff -Nru squashfs-tools-4.2+20130409/compressor.c squashfs-tools-4.3+20140919/compressor.c --- squashfs-tools-4.2+20130409/compressor.c 2013-05-09 10:39:11.000000000 +0200 +++ squashfs-tools-4.3+20140919/compressor.c 2015-07-20 21:03:05.000000000 +0200 @@ -49,6 +49,14 @@ extern struct compressor lzo_comp_ops; #endif +#ifndef LZ4_SUPPORT +static struct compressor lz4_comp_ops = { + LZ4_COMPRESSION, "lz4" +}; +#else +extern struct compressor lz4_comp_ops; +#endif + #ifndef XZ_SUPPORT static struct compressor xz_comp_ops = { XZ_COMPRESSION, "xz" @@ -67,6 +75,7 @@ &gzip_comp_ops, &lzma_comp_ops, &lzo_comp_ops, + &lz4_comp_ops, &xz_comp_ops, &unknown_comp_ops }; diff -Nru squashfs-tools-4.2+20130409/compressor.h squashfs-tools-4.3+20140919/compressor.h --- squashfs-tools-4.2+20130409/compressor.h 2013-05-09 10:39:11.000000000 +0200 +++ squashfs-tools-4.3+20140919/compressor.h 2015-07-20 21:03:05.000000000 +0200 @@ -1,6 +1,8 @@ +#ifndef COMPRESSOR_H +#define COMPRESSOR_H /* * - * Copyright (c) 2009, 2010, 2011, 2012, 2013 + * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014 * Phillip Lougher * * This program is free software; you can redistribute it and/or @@ -31,6 +33,7 @@ int (*options_post)(int); void *(*dump_options)(int, int *); int (*extract_options)(int, void *, int); + int (*check_options)(int, void *, int); void (*display_options)(void *, int); void (*usage)(); }; @@ -64,9 +67,8 @@ /* - * For the following functions, please see xz_wrapper.c for - * commented examples of how they are used (xz is currently - * the only compressor that uses compression options). + * For the following functions please see the lzo, lz4 or xz + * compressors for commented examples of how they are used. */ static inline int compressor_options(struct compressor *comp, char *argv[], int argc) @@ -104,9 +106,19 @@ } +static inline int compressor_check_options(struct compressor *comp, + int block_size, void *buffer, int size) +{ + if(comp->check_options == NULL) + return 0; + return comp->check_options(block_size, buffer, size); +} + + static inline void compressor_display_options(struct compressor *comp, void *buffer, int size) { if(comp->display_options != NULL) comp->display_options(buffer, size); } +#endif diff -Nru squashfs-tools-4.2+20130409/debian/changelog squashfs-tools-4.3+20140919/debian/changelog --- squashfs-tools-4.2+20130409/debian/changelog 2013-09-18 10:22:27.000000000 +0200 +++ squashfs-tools-4.3+20140919/debian/changelog 2015-07-20 22:44:05.000000000 +0200 @@ -1,3 +1,11 @@ +squashfs-tools (1:4.3+20140919-0) unstable; urgency=low + + * Merging upstream version 4.3+20140919. + * Fix for CVE 2015-4645/4646 + * Updates man pages + + -- Romeo Papa Mon, 20 Jul 2015 21:28:12 +0200 + squashfs-tools (1:4.2+20130409-2) unstable; urgency=low * New maintainer (closes: #723600). diff -Nru squashfs-tools-4.2+20130409/debian/manpages/mksquashfs.1 squashfs-tools-4.3+20140919/debian/manpages/mksquashfs.1 --- squashfs-tools-4.2+20130409/debian/manpages/mksquashfs.1 2013-05-09 22:22:49.000000000 +0200 +++ squashfs-tools-4.3+20140919/debian/manpages/mksquashfs.1 2015-07-20 22:44:05.000000000 +0200 @@ -1,4 +1,4 @@ -.TH MKSQUASHFS 1 "2012\-06\-30" "4.2" "create and append squashfs filesystems" +.TH MKSQUASHFS 1 "2014\-05\-13" "4.3" "create and append squashfs filesystems" .SH NAME mksquashfs \- tool to create and append to squashfs filesystems @@ -15,9 +15,9 @@ .SS Filesystem build options .IP "\-comp \fICOMPRESSION\fR" 4 -select \fICOMPRESSION\fR compression. Compressors available: gzip (default), lzo, xz. +select \fICOMPRESSION\fR compression. Compressors available: gzip (default), lzma (no kernel support), lzo, lz4 and xz. .IP "\-b \fIBLOCK_SIZE\fR" -set data block to \fIBLOCK_SIZE\fR. Default 131072 bytes. +set data block to \fIBLOCK_SIZE\fR. Default 131072 bytes. Optionally K or M can be used as a suffix to specify kilobytes or megabytes, respectively. .IP "\-no\-exports" 4 don't make the filesystem exportable via NFS. .IP "\-no\-sparse" 4 @@ -74,6 +74,8 @@ .SS Mksquashfs runtime options: .IP "\-version" 4 print version, licence and copyright message. +.IP "\-exit\-on\-error" 4 +treat normally ignored errors as fatal. .IP "\-recover \fINAME\fR" 4 recover filesystem data using recovery file \fINAME\fR. .IP "\-no\-recovery" 4 @@ -82,14 +84,18 @@ print files written to filesystem. .IP "\-no\-progress" 4 don't display the progress bar. +.IP "\-progress" 4 +display progress bar when using the \-info option. .IP "\-processors \fINUMBER\fR" 4 Use \fINUMBER\fR processors. By default will use number of processors available. +.IP "\-mem \fISIZE\fR" 4 +Use \fISIZE\fR physical memory. Optionally K or M can be used as a suffix for kilobytes or megabytes, respectively. Default 25% of memory. .IP "\-read\-queue \fISIZE\fR" 4 -Set input queue to \fISIZE\fR Mbytes. Default 64 Mbytes. +Deprecated. Use \-mem instead. .IP "\-write\-queue \fISIZE\fR" 4 -Set output queue to \fISIZE\fR Mbytes. Default 512 Mbytes. +Deprecated. Use \-mem instead. .IP "\-fragment\-queue \fISIZE\fR" 4 -Set fragment queue to \fISIZE\fR Mbytes. Default 64 Mbytes. +Deprecated. Use \-mem instead. .SS Miscellaneous options .IP "\-root\-owned" 4 @@ -102,11 +108,27 @@ alternative name for \-noF. .IP "\-noXattrCompression" 4 alternative name for \-noX. +.IP "\-Xhelp" 4 +print compressor options for selected compressor .SS Compressors available and compressor specific options -.IP "gzip (no options) (default)" -.IP "lzo (no options)" -.IP "xz" +.IP "gzip (default)" +.IP "\-Xcompression-level \fIcompression\-level\fR" 4 +\fIcompression\-level\fR should be 1 .. 9 (default 9) +.IP "\-Xwindow\-size \fIwindow\-size\fR" 4 +\fIwindow\-size\fR should be 8 .. 15 (default 15) +.IP "\-Xstrategy strategy1,strategy2,...,strategyN" 4 +Compress using strategy1,strategy2,...,strategyN in turn and choose the best compression. Available strategies: default, filtered, huffman_only, run_length_encoded and fixed +.IP "lzmz (no options) (no kernel support)" 4 +.IP "lzo" 4 +.IP "\-Xalgorithm \fIalgorithm\fR" 4 +Where \fIalgorithm\fR is one of: lzo1x_1, lzo1x_1_11, lzo1x_1_12, lzo1x_1_15 or lzo1x_999. (default lzo1x_999) +.IP "\-Xcompression\-level \fIcompression\-level\fR" 4 +\fIcompression\-level\fR should be 1 .. 9 (default 8) +.IP "lz4" 4 +.IP "\-Xhc" +Compress using LZ4 High Compression +.IP "xz" 4 .IP "\-Xbcj filter1,filter2,...,filterN" 4 Compress using filter1,filter2,...,filterN in turn (in addition to no filter), and choose the best compression. Available filters: x86, arm, armthumb, powerpc, sparc, ia64. .IP "\-Xdict\-size \fIDICT_SIZE\fR" 4 @@ -119,6 +141,6 @@ More information about mksquashfs and the squashfs filesystem can be found at <\fIhttp://squashfs.sourceforge.net/\fR>. .SH AUTHOR -squashfs was written by Phillip Lougher <\fIphillip@squashfs.org.uk\fR>. +squashfs was written by Phillip Lougher <\fIplougher@users.sourceforge.net\fR>. .PP -This manual page was written by Daniel Baumann <\fImail@daniel\-baumann.ch\fR>. +This manual page was written by Daniel Baumann <\fIdaniel.baumann@progress\-technologies.net\fR>. With some updates for 4.3 for use with Fedora and Debian. diff -Nru squashfs-tools-4.2+20130409/debian/manpages/unsquashfs.1 squashfs-tools-4.3+20140919/debian/manpages/unsquashfs.1 --- squashfs-tools-4.2+20130409/debian/manpages/unsquashfs.1 2013-05-09 22:22:49.000000000 +0200 +++ squashfs-tools-4.3+20140919/debian/manpages/unsquashfs.1 2015-07-20 22:44:05.000000000 +0200 @@ -1,4 +1,4 @@ -.TH UNSQUASHFS 1 "2012\-06\-30" "4.2" "uncompress squashfs filesystems" +.TH UNSQUASHFS 1 "2014\-05\-13" "4.3" "uncompress squashfs filesystems" .SH NAME mksquashfs \- tool to uncompress squashfs filesystems @@ -22,6 +22,8 @@ don't extract xattrs in file system. .IP "\-x, \-xattrs" 4 extract xattrs in file system (default). +.IP "\-u, \-user\-xattrs" 4 +only extract user xattrs in file system. Enables extracting xattrs. .IP "\-p \fINUMBER\fR, \-processors \fINUMBER\fR" 4 use \fINUMBER\fR processors. By default will use number of processors available. .IP "\-i, \-info" 4 @@ -38,7 +40,7 @@ display filesystem superblock information. .IP "\-e \fIEXTRACT_FILE\fR, \-ef \fIEXTRACT_FILE\fR" 4 list of directories or files to extract. One per line. -.IP "\-da \fISIZE\fR, \-data-queue \fISIZE\fR" 4 +.IP "\-da \fISIZE\fR, \-data\-queue \fISIZE\fR" 4 Set data queue to \fISIZE\fR Mbytes. Default 256 Mbytes. .IP "\-fr \fISIZE\fR, \-frag\-queue \fISIZE\fR" 4 Set fragment queue to \fISIZE\fR Mbytes. Default 256 Mbytes. @@ -47,7 +49,9 @@ .SS Decompressors available .IP "gzip" 4 +.IP "lzma" 4 .IP "lzo" 4 +.IP "lz4" 4 .IP "xz" 4 .SH SEE ALSO @@ -57,6 +61,6 @@ More information about unsquashfs and the squashfs filesystem can be found at <\fIhttp://squashfs.sourceforge.net/\fR>. .SH AUTHOR -squashfs was written by Phillip Lougher <\fIphillip@squashfs.org.uk\fR>. +squashfs was written by Phillip Lougher <\fIplougher@users.sourceforge.net\fR>. .PP -This manual page was written by Daniel Baumann <\fImail@daniel\-baumann.ch\fR>. +This manual page was written by Daniel Baumann <\fIdaniel.baumann@progress\-technologies.net\fR>. With some updates for 4.3 for use with Fedora and Debian. diff -Nru squashfs-tools-4.2+20130409/debian/patches/0001-kfreebsd.patch squashfs-tools-4.3+20140919/debian/patches/0001-kfreebsd.patch --- squashfs-tools-4.2+20130409/debian/patches/0001-kfreebsd.patch 2013-05-09 22:25:25.000000000 +0200 +++ squashfs-tools-4.3+20140919/debian/patches/0001-kfreebsd.patch 1970-01-01 01:00:00.000000000 +0100 @@ -1,103 +0,0 @@ -Author: Cyril Brulebois -Description: Fixes FTBFS on kfreebsd (Closes: #557174). - -Index: squashfs-tools/mksquashfs.c -=================================================================== ---- squashfs-tools.orig/mksquashfs.c 2013-05-09 20:25:19.000000000 +0000 -+++ squashfs-tools/mksquashfs.c 2013-05-09 20:25:19.000000000 +0000 -@@ -51,7 +51,7 @@ - #include - #include - --#ifndef linux -+#if !defined(linux) && !defined(__GLIBC__) - #define __BYTE_ORDER BYTE_ORDER - #define __BIG_ENDIAN BIG_ENDIAN - #define __LITTLE_ENDIAN LITTLE_ENDIAN -@@ -4038,7 +4038,7 @@ - BAD_ERROR("Failed to set signal mask in intialise_threads\n"); - - if(processors == -1) { --#ifndef linux -+#if !defined(linux) && !defined(__GLIBC__) - int mib[2]; - size_t len = sizeof(processors); - -Index: squashfs-tools/read_fs.c -=================================================================== ---- squashfs-tools.orig/read_fs.c 2013-05-09 20:25:19.000000000 +0000 -+++ squashfs-tools/read_fs.c 2013-05-09 20:25:19.000000000 +0000 -@@ -34,7 +34,7 @@ - #include - #include - --#ifndef linux -+#if !defined(linux) && !defined(__GLIBC__) - #define __BYTE_ORDER BYTE_ORDER - #define __BIG_ENDIAN BIG_ENDIAN - #define __LITTLE_ENDIAN LITTLE_ENDIAN -Index: squashfs-tools/read_xattrs.c -=================================================================== ---- squashfs-tools.orig/read_xattrs.c 2013-05-09 20:25:19.000000000 +0000 -+++ squashfs-tools/read_xattrs.c 2013-05-09 20:25:19.000000000 +0000 -@@ -31,7 +31,7 @@ - #include - #include - --#ifndef linux -+#if !defined(linux) && !defined(__GLIBC__) - #define __BYTE_ORDER BYTE_ORDER - #define __BIG_ENDIAN BIG_ENDIAN - #define __LITTLE_ENDIAN LITTLE_ENDIAN -Index: squashfs-tools/swap.c -=================================================================== ---- squashfs-tools.orig/swap.c 2013-05-09 20:25:19.000000000 +0000 -+++ squashfs-tools/swap.c 2013-05-09 20:25:19.000000000 +0000 -@@ -19,7 +19,7 @@ - * swap.c - */ - --#ifndef linux -+#if !defined(linux) && !defined(__GLIBC__) - #define __BYTE_ORDER BYTE_ORDER - #define __BIG_ENDIAN BIG_ENDIAN - #define __LITTLE_ENDIAN LITTLE_ENDIAN -Index: squashfs-tools/unsquashfs.c -=================================================================== ---- squashfs-tools.orig/unsquashfs.c 2013-05-09 20:25:19.000000000 +0000 -+++ squashfs-tools/unsquashfs.c 2013-05-09 20:25:19.000000000 +0000 -@@ -2104,7 +2104,7 @@ - "\n"); - - if(processors == -1) { --#ifndef linux -+#if !defined(linux) && !defined(__GLIBC__) - int mib[2]; - size_t len = sizeof(processors); - -Index: squashfs-tools/unsquashfs.h -=================================================================== ---- squashfs-tools.orig/unsquashfs.h 2013-05-09 20:25:19.000000000 +0000 -+++ squashfs-tools/unsquashfs.h 2013-05-09 20:25:19.000000000 +0000 -@@ -45,7 +45,7 @@ - #include - #include - --#ifndef linux -+#if !defined(linux) && !defined(__GLIBC__) - #define __BYTE_ORDER BYTE_ORDER - #define __BIG_ENDIAN BIG_ENDIAN - #define __LITTLE_ENDIAN LITTLE_ENDIAN -Index: squashfs-tools/xz_wrapper.h -=================================================================== ---- squashfs-tools.orig/xz_wrapper.h 2013-05-09 20:25:19.000000000 +0000 -+++ squashfs-tools/xz_wrapper.h 2013-05-09 20:25:19.000000000 +0000 -@@ -24,7 +24,7 @@ - * - */ - --#ifndef linux -+#if !defined(linux) && !defined(__GLIBC__) - #define __BYTE_ORDER BYTE_ORDER - #define __BIG_ENDIAN BIG_ENDIAN - #define __LITTLE_ENDIAN LITTLE_ENDIAN diff -Nru squashfs-tools-4.2+20130409/debian/patches/cve-2015-4646 squashfs-tools-4.3+20140919/debian/patches/cve-2015-4646 --- squashfs-tools-4.2+20130409/debian/patches/cve-2015-4646 1970-01-01 01:00:00.000000000 +0100 +++ squashfs-tools-4.3+20140919/debian/patches/cve-2015-4646 2015-07-20 22:29:13.000000000 +0200 @@ -0,0 +1,47 @@ +Description: CVE-2015-4645 integer overflow in read_fragment_table_4 + Integer overflow issue was reported in squashfs-tools. + Following code in unsquash-4.c overflows the bytes variable, so that the allocation of fragments_bytes[] has an erroneous size. + + int bytes = SQUASHFS_FRAGMENT_BYTES(sBlk.s.fragments); + ... + fragment_table = malloc(bytes) + . + squashfs-tools (1:4.3+20140919-0) unstable; urgency=low + . + * Fixes https://security-tracker.debian.org/tracker/CVE-2015-4645 + * Fixes https://security-tracker.debian.org/tracker/CVE-2015-4646 + +Author: Romeo Papa +Origin: upstream, https://github.com/gcanalesb/sasquatch/commit/6777e08cc38bc780d27c69c1d8c272867b74524f +Bug: https://github.com/devttys0/sasquatch/pull/5 +Bug-Debian: https://security-tracker.debian.org/tracker/CVE-2015-4646 +Bug-Fedora: http://people.canonical.com/~ubuntu-security/cve/2015/CVE-2015-4646.html +Last-Update: 2015-06-17 + +--- squashfs-tools-4.3+20140919.orig/unsquash-4.c ++++ squashfs-tools-4.3+20140919/unsquash-4.c +@@ -31,9 +31,9 @@ static unsigned int *id_table; + int read_fragment_table_4(long long *directory_table_end) + { + int res, i; +- int bytes = SQUASHFS_FRAGMENT_BYTES(sBlk.s.fragments); +- int indexes = SQUASHFS_FRAGMENT_INDEXES(sBlk.s.fragments); +- long long fragment_table_index[indexes]; ++ size_t bytes = SQUASHFS_FRAGMENT_BYTES(sBlk.s.fragments); ++ size_t indexes = SQUASHFS_FRAGMENT_INDEXES(sBlk.s.fragments); ++ long long *fragment_table_index; + + TRACE("read_fragment_table: %d fragments, reading %d fragment indexes " + "from 0x%llx\n", sBlk.s.fragments, indexes, +@@ -44,6 +44,11 @@ int read_fragment_table_4(long long *dir + return TRUE; + } + ++ fragment_table_index = malloc(indexes*sizeof(long long)); ++ if(fragment_table_index == NULL) ++ EXIT_UNSQUASH("read_fragment_table: failed to allocate " ++ "fragment table index\n"); ++ + fragment_table = malloc(bytes); + if(fragment_table == NULL) + EXIT_UNSQUASH("read_fragment_table: failed to allocate " diff -Nru squashfs-tools-4.2+20130409/debian/patches/series squashfs-tools-4.3+20140919/debian/patches/series --- squashfs-tools-4.2+20130409/debian/patches/series 2013-05-09 22:22:49.000000000 +0200 +++ squashfs-tools-4.3+20140919/debian/patches/series 2015-07-20 22:30:50.000000000 +0200 @@ -1 +1 @@ -0001-kfreebsd.patch +cve-2015-4646 diff -Nru squashfs-tools-4.2+20130409/error.h squashfs-tools-4.3+20140919/error.h --- squashfs-tools-4.2+20130409/error.h 2013-05-09 10:39:11.000000000 +0200 +++ squashfs-tools-4.3+20140919/error.h 2015-07-20 21:03:05.000000000 +0200 @@ -1,8 +1,10 @@ +#ifndef ERROR_H +#define ERROR_H /* * Create a squashfs filesystem. This is a highly compressed read only * filesystem. * - * Copyright (c) 2012, 2013 + * Copyright (c) 2012, 2013, 2014 * Phillip Lougher * * This program is free software; you can redistribute it and/or @@ -22,6 +24,8 @@ * error.h */ +extern int exit_on_error; + extern void prep_exit(); extern void progressbar_error(char *fmt, ...); extern void progressbar_info(char *fmt, ...); @@ -46,6 +50,23 @@ progressbar_error(s, ## args); \ } while(0) +#define ERROR_START(s, args...) \ + do { \ + disable_progress_bar(); \ + fprintf(stderr, s, ## args); \ + } while(0) + +#define ERROR_EXIT(s, args...) \ + do {\ + if (exit_on_error) { \ + fprintf(stderr, "\n"); \ + EXIT_MKSQUASHFS(); \ + } else { \ + fprintf(stderr, s, ## args); \ + enable_progress_bar(); \ + } \ + } while(0) + #define EXIT_MKSQUASHFS() \ do {\ prep_exit();\ @@ -66,3 +87,4 @@ __func__); \ EXIT_MKSQUASHFS();\ } while(0) +#endif diff -Nru squashfs-tools-4.2+20130409/gzip_wrapper.c squashfs-tools-4.3+20140919/gzip_wrapper.c --- squashfs-tools-4.2+20130409/gzip_wrapper.c 2013-05-09 10:39:11.000000000 +0200 +++ squashfs-tools-4.3+20140919/gzip_wrapper.c 2015-07-20 21:03:05.000000000 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2010, 2013 + * Copyright (c) 2009, 2010, 2013, 2014 * Phillip Lougher * * This program is free software; you can redistribute it and/or @@ -17,34 +17,376 @@ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * gzip_wrapper.c + * + * Support for ZLIB compression http://www.zlib.net */ +#include +#include #include #include #include "squashfs_fs.h" +#include "gzip_wrapper.h" #include "compressor.h" -static int gzip_init(void **strm, int block_size, int flags) +static struct strategy strategy[] = { + { "default", Z_DEFAULT_STRATEGY, 0 }, + { "filtered", Z_FILTERED, 0 }, + { "huffman_only", Z_HUFFMAN_ONLY, 0 }, + { "run_length_encoded", Z_RLE, 0 }, + { "fixed", Z_FIXED, 0 }, + { NULL, 0, 0 } +}; + +static int strategy_count = 0; + +/* default compression level */ +static int compression_level = GZIP_DEFAULT_COMPRESSION_LEVEL; + +/* default window size */ +static int window_size = GZIP_DEFAULT_WINDOW_SIZE; + +/* + * This function is called by the options parsing code in mksquashfs.c + * to parse any -X compressor option. + * + * This function returns: + * >=0 (number of additional args parsed) on success + * -1 if the option was unrecognised, or + * -2 if the option was recognised, but otherwise bad in + * some way (e.g. invalid parameter) + * + * Note: this function sets internal compressor state, but does not + * pass back the results of the parsing other than success/failure. + * The gzip_dump_options() function is called later to get the options in + * a format suitable for writing to the filesystem. + */ +static int gzip_options(char *argv[], int argc) { - int res; - z_stream *stream; + if(strcmp(argv[0], "-Xcompression-level") == 0) { + if(argc < 2) { + fprintf(stderr, "gzip: -Xcompression-level missing " + "compression level\n"); + fprintf(stderr, "gzip: -Xcompression-level it " + "should be 1 >= n <= 9\n"); + goto failed; + } + + compression_level = atoi(argv[1]); + if(compression_level < 1 || compression_level > 9) { + fprintf(stderr, "gzip: -Xcompression-level invalid, it " + "should be 1 >= n <= 9\n"); + goto failed; + } + + return 1; + } else if(strcmp(argv[0], "-Xwindow-size") == 0) { + if(argc < 2) { + fprintf(stderr, "gzip: -Xwindow-size missing window " + " size\n"); + fprintf(stderr, "gzip: -Xwindow-size \n"); + goto failed; + } + + window_size = atoi(argv[1]); + if(window_size < 8 || window_size > 15) { + fprintf(stderr, "gzip: -Xwindow-size invalid, it " + "should be 8 >= n <= 15\n"); + goto failed; + } + + return 1; + } else if(strcmp(argv[0], "-Xstrategy") == 0) { + char *name; + int i; + + if(argc < 2) { + fprintf(stderr, "gzip: -Xstrategy missing " + "strategies\n"); + goto failed; + } + + name = argv[1]; + while(name[0] != '\0') { + for(i = 0; strategy[i].name; i++) { + int n = strlen(strategy[i].name); + if((strncmp(name, strategy[i].name, n) == 0) && + (name[n] == '\0' || + name[n] == ',')) { + if(strategy[i].selected == 0) { + strategy[i].selected = 1; + strategy_count++; + } + name += name[n] == ',' ? n + 1 : n; + break; + } + } + if(strategy[i].name == NULL) { + fprintf(stderr, "gzip: -Xstrategy unrecognised " + "strategy\n"); + goto failed; + } + } + + return 1; + } + + return -1; + +failed: + return -2; +} + + +/* + * This function is called after all options have been parsed. + * It is used to do post-processing on the compressor options using + * values that were not expected to be known at option parse time. + * + * This function returns 0 on successful post processing, or + * -1 on error + */ +static int gzip_options_post(int block_size) +{ + if(strategy_count == 1 && strategy[0].selected) { + strategy_count = 0; + strategy[0].selected = 0; + } + + return 0; +} + + +/* + * This function is called by mksquashfs to dump the parsed + * compressor options in a format suitable for writing to the + * compressor options field in the filesystem (stored immediately + * after the superblock). + * + * This function returns a pointer to the compression options structure + * to be stored (and the size), or NULL if there are no compression + * options + * + */ +static void *gzip_dump_options(int block_size, int *size) +{ + static struct gzip_comp_opts comp_opts; + int i, strategies = 0; + + /* + * If default compression options of: + * compression-level: 8 and + * window-size: 15 and + * strategy_count == 0 then + * don't store a compression options structure (this is compatible + * with the legacy implementation of GZIP for Squashfs) + */ + if(compression_level == GZIP_DEFAULT_COMPRESSION_LEVEL && + window_size == GZIP_DEFAULT_WINDOW_SIZE && + strategy_count == 0) + return NULL; + + for(i = 0; strategy[i].name; i++) + strategies |= strategy[i].selected << i; + + comp_opts.compression_level = compression_level; + comp_opts.window_size = window_size; + comp_opts.strategy = strategies; + + SQUASHFS_INSWAP_COMP_OPTS(&comp_opts); + + *size = sizeof(comp_opts); + return &comp_opts; +} + + +/* + * This function is a helper specifically for the append mode of + * mksquashfs. Its purpose is to set the internal compressor state + * to the stored compressor options in the passed compressor options + * structure. + * + * In effect this function sets up the compressor options + * to the same state they were when the filesystem was originally + * generated, this is to ensure on appending, the compressor uses + * the same compression options that were used to generate the + * original filesystem. + * + * Note, even if there are no compressor options, this function is still + * called with an empty compressor structure (size == 0), to explicitly + * set the default options, this is to ensure any user supplied + * -X options on the appending mksquashfs command line are over-ridden + * + * This function returns 0 on sucessful extraction of options, and + * -1 on error + */ +static int gzip_extract_options(int block_size, void *buffer, int size) +{ + struct gzip_comp_opts *comp_opts = buffer; + int i; + + if(size == 0) { + /* Set default values */ + compression_level = GZIP_DEFAULT_COMPRESSION_LEVEL; + window_size = GZIP_DEFAULT_WINDOW_SIZE; + strategy_count = 0; + return 0; + } + + /* we expect a comp_opts structure of sufficient size to be present */ + if(size < sizeof(*comp_opts)) + goto failed; + + SQUASHFS_INSWAP_COMP_OPTS(comp_opts); + + /* Check comp_opts structure for correctness */ + if(comp_opts->compression_level < 1 || + comp_opts->compression_level > 9) { + fprintf(stderr, "gzip: bad compression level in " + "compression options structure\n"); + goto failed; + } + compression_level = comp_opts->compression_level; - stream = *strm = malloc(sizeof(z_stream)); - if(stream == NULL) + if(comp_opts->window_size < 8 || + comp_opts->window_size > 15) { + fprintf(stderr, "gzip: bad window size in " + "compression options structure\n"); goto failed; + } + window_size = comp_opts->window_size; + + strategy_count = 0; + for(i = 0; strategy[i].name; i++) { + if((comp_opts->strategy >> i) & 1) { + strategy[i].selected = 1; + strategy_count ++; + } else + strategy[i].selected = 0; + } + + return 0; + +failed: + fprintf(stderr, "gzip: error reading stored compressor options from " + "filesystem!\n"); + + return -1; +} + + +void gzip_display_options(void *buffer, int size) +{ + struct gzip_comp_opts *comp_opts = buffer; + int i, printed; + + /* we expect a comp_opts structure of sufficient size to be present */ + if(size < sizeof(*comp_opts)) + goto failed; + + SQUASHFS_INSWAP_COMP_OPTS(comp_opts); + + /* Check comp_opts structure for correctness */ + if(comp_opts->compression_level < 1 || + comp_opts->compression_level > 9) { + fprintf(stderr, "gzip: bad compression level in " + "compression options structure\n"); + goto failed; + } + printf("\tcompression-level %d\n", comp_opts->compression_level); + + if(comp_opts->window_size < 8 || + comp_opts->window_size > 15) { + fprintf(stderr, "gzip: bad window size in " + "compression options structure\n"); + goto failed; + } + printf("\twindow-size %d\n", comp_opts->window_size); + + for(i = 0, printed = 0; strategy[i].name; i++) { + if((comp_opts->strategy >> i) & 1) { + if(printed) + printf(", "); + else + printf("\tStrategies selected: "); + printf("%s", strategy[i].name); + printed = 1; + } + } + + if(!printed) + printf("\tStrategies selected: default\n"); + else + printf("\n"); + + return; + +failed: + fprintf(stderr, "gzip: error reading stored compressor options from " + "filesystem!\n"); +} - stream->zalloc = Z_NULL; - stream->zfree = Z_NULL; - stream->opaque = 0; - res = deflateInit(stream, 9); +/* + * This function is called by mksquashfs to initialise the + * compressor, before compress() is called. + * + * This function returns 0 on success, and + * -1 on error + */ +static int gzip_init(void **strm, int block_size, int datablock) +{ + int i, j, res; + struct gzip_stream *stream; + + if(!datablock || !strategy_count) { + stream = malloc(sizeof(*stream) + sizeof(struct gzip_strategy)); + if(stream == NULL) + goto failed; + + stream->strategies = 1; + stream->strategy[0].strategy = Z_DEFAULT_STRATEGY; + } else { + stream = malloc(sizeof(*stream) + + sizeof(struct gzip_strategy) * strategy_count); + if(stream == NULL) + goto failed; + + memset(stream->strategy, 0, sizeof(struct gzip_strategy) * + strategy_count); + + stream->strategies = strategy_count; + + for(i = 0, j = 0; strategy[i].name; i++) { + if(!strategy[i].selected) + continue; + + stream->strategy[j].strategy = strategy[i].strategy; + if(j) { + stream->strategy[j].buffer = malloc(block_size); + if(stream->strategy[j].buffer == NULL) + goto failed2; + } + j++; + } + } + + stream->stream.zalloc = Z_NULL; + stream->stream.zfree = Z_NULL; + stream->stream.opaque = 0; + + res = deflateInit2(&stream->stream, compression_level, Z_DEFLATED, + window_size, 8, stream->strategy[0].strategy); if(res != Z_OK) goto failed2; + *strm = stream; return 0; failed2: + for(i = 1; i < stream->strategies; i++) + free(stream->strategy[i].buffer); free(stream); failed: return -1; @@ -54,29 +396,51 @@ static int gzip_compress(void *strm, void *d, void *s, int size, int block_size, int *error) { - int res; - z_stream *stream = strm; - - res = deflateReset(stream); - if(res != Z_OK) - goto failed; - - stream->next_in = s; - stream->avail_in = size; - stream->next_out = d; - stream->avail_out = block_size; + int i, res; + struct gzip_stream *stream = strm; + struct gzip_strategy *selected = NULL; + + stream->strategy[0].buffer = d; + + for(i = 0; i < stream->strategies; i++) { + struct gzip_strategy *strategy = &stream->strategy[i]; + + res = deflateReset(&stream->stream); + if(res != Z_OK) + goto failed; + + stream->stream.next_in = s; + stream->stream.avail_in = size; + stream->stream.next_out = strategy->buffer; + stream->stream.avail_out = block_size; + + if(stream->strategies > 1) { + res = deflateParams(&stream->stream, + compression_level, strategy->strategy); + if(res != Z_OK) + goto failed; + } + + res = deflate(&stream->stream, Z_FINISH); + strategy->length = stream->stream.total_out; + if(res == Z_STREAM_END) { + if(!selected || selected->length > strategy->length) + selected = strategy; + } else if(res != Z_OK) + goto failed; + } - res = deflate(stream, Z_FINISH); - if(res == Z_STREAM_END) - /* - * Success, return the compressed size. - */ - return (int) stream->total_out; - if(res == Z_OK) + if(!selected) /* * Output buffer overflow. Return out of buffer space */ return 0; + + if(selected->buffer != d) + memcpy(d, selected->buffer, selected->length); + + return (int) selected->length; + failed: /* * All other errors return failure, with the compressor @@ -94,8 +458,29 @@ res = uncompress(d, &bytes, s, size); - *error = res; - return res == Z_OK ? (int) bytes : -1; + if(res == Z_OK) + return (int) bytes; + else { + *error = res; + return -1; + } +} + + +void gzip_usage() +{ + fprintf(stderr, "\t -Xcompression-level \n"); + fprintf(stderr, "\t\t should be 1 .. 9 (default " + "%d)\n", GZIP_DEFAULT_COMPRESSION_LEVEL); + fprintf(stderr, "\t -Xwindow-size \n"); + fprintf(stderr, "\t\t should be 8 .. 15 (default " + "%d)\n", GZIP_DEFAULT_WINDOW_SIZE); + fprintf(stderr, "\t -Xstrategy strategy1,strategy2,...,strategyN\n"); + fprintf(stderr, "\t\tCompress using strategy1,strategy2,...,strategyN" + " in turn\n"); + fprintf(stderr, "\t\tand choose the best compression.\n"); + fprintf(stderr, "\t\tAvailable strategies: default, filtered, " + "huffman_only,\n\t\trun_length_encoded and fixed\n"); } @@ -103,10 +488,13 @@ .init = gzip_init, .compress = gzip_compress, .uncompress = gzip_uncompress, - .options = NULL, - .usage = NULL, + .options = gzip_options, + .options_post = gzip_options_post, + .dump_options = gzip_dump_options, + .extract_options = gzip_extract_options, + .display_options = gzip_display_options, + .usage = gzip_usage, .id = ZLIB_COMPRESSION, .name = "gzip", .supported = 1 }; - diff -Nru squashfs-tools-4.2+20130409/gzip_wrapper.h squashfs-tools-4.3+20140919/gzip_wrapper.h --- squashfs-tools-4.2+20130409/gzip_wrapper.h 1970-01-01 01:00:00.000000000 +0100 +++ squashfs-tools-4.3+20140919/gzip_wrapper.h 2015-07-20 21:03:05.000000000 +0200 @@ -0,0 +1,75 @@ +#ifndef GZIP_WRAPPER_H +#define GZIP_WRAPPER_H +/* + * Squashfs + * + * Copyright (c) 2014 + * Phillip Lougher + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2, + * or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * gzip_wrapper.h + * + */ + +#ifndef linux +#define __BYTE_ORDER BYTE_ORDER +#define __BIG_ENDIAN BIG_ENDIAN +#define __LITTLE_ENDIAN LITTLE_ENDIAN +#else +#include +#endif + +#if __BYTE_ORDER == __BIG_ENDIAN +extern unsigned int inswap_le16(unsigned short); +extern unsigned int inswap_le32(unsigned int); + +#define SQUASHFS_INSWAP_COMP_OPTS(s) { \ + (s)->compression_level = inswap_le32((s)->compression_level); \ + (s)->window_size = inswap_le16((s)->window_size); \ + (s)->strategy = inswap_le16((s)->strategy); \ +} +#else +#define SQUASHFS_INSWAP_COMP_OPTS(s) +#endif + +/* Default compression */ +#define GZIP_DEFAULT_COMPRESSION_LEVEL 9 +#define GZIP_DEFAULT_WINDOW_SIZE 15 + +struct gzip_comp_opts { + int compression_level; + short window_size; + short strategy; +}; + +struct strategy { + char *name; + int strategy; + int selected; +}; + +struct gzip_strategy { + int strategy; + int length; + void *buffer; +}; + +struct gzip_stream { + z_stream stream; + int strategies; + struct gzip_strategy strategy[0]; +}; +#endif diff -Nru squashfs-tools-4.2+20130409/info.c squashfs-tools-4.3+20140919/info.c --- squashfs-tools-4.2+20130409/info.c 2013-05-09 10:39:11.000000000 +0200 +++ squashfs-tools-4.3+20140919/info.c 2015-07-20 21:03:05.000000000 +0200 @@ -2,7 +2,7 @@ * Create a squashfs filesystem. This is a highly compressed read only * filesystem. * - * Copyright (c) 2013 + * Copyright (c) 2013, 2014 * Phillip Lougher * * This program is free software; you can redistribute it and/or @@ -35,55 +35,142 @@ #include #include #include +#include #include "squashfs_fs.h" #include "mksquashfs.h" #include "error.h" +#include "progressbar.h" +#include "caches-queues-lists.h" static int silent = 0; -static struct dir_ent *dir_ent = NULL; +static struct dir_ent *ent = NULL; pthread_t info_thread; void disable_info() { - dir_ent = NULL; + ent = NULL; } -void update_info(struct dir_ent *ent) +void update_info(struct dir_ent *dir_ent) { - dir_ent = ent; + ent = dir_ent; +} + + +void print_filename() +{ + struct dir_ent *dir_ent = ent; + + if(dir_ent == NULL) + return; + + if(dir_ent->our_dir->subpath[0] != '\0') + INFO("%s/%s\n", dir_ent->our_dir->subpath, dir_ent->name); + else + INFO("/%s\n", dir_ent->name); +} + + +void dump_state() +{ + disable_progress_bar(); + + printf("Queue and Cache status dump\n"); + printf("===========================\n"); + + printf("file buffer queue (reader thread -> deflate thread(s))\n"); + dump_queue(to_deflate); + + printf("uncompressed fragment queue (reader thread -> fragment" + " thread(s))\n"); + dump_queue(to_process_frag); + + printf("processed fragment queue (fragment thread(s) -> main" + " thread)\n"); + dump_seq_queue(to_main, 1); + + printf("compressed block queue (deflate thread(s) -> main thread)\n"); + dump_seq_queue(to_main, 0); + + printf("uncompressed packed fragment queue (main thread -> fragment" + " deflate thread(s))\n"); + dump_queue(to_frag); + + + printf("locked frag queue (compressed frags waiting while multi-block" + " file is written)\n"); + dump_queue(locked_fragment); + + printf("compressed block queue (main & fragment deflate threads(s) ->" + " writer thread)\n"); + dump_queue(to_writer); + + printf("read cache (uncompressed blocks read by reader thread)\n"); + dump_cache(reader_buffer); + + printf("block write cache (compressed blocks waiting for the writer" + " thread)\n"); + dump_cache(bwriter_buffer); + printf("fragment write cache (compressed fragments waiting for the" + " writer thread)\n"); + dump_cache(fwriter_buffer); + + printf("fragment cache (frags waiting to be compressed by fragment" + " deflate thread(s))\n"); + dump_cache(fragment_buffer); + + printf("fragment reserve cache (avoids pipeline stall if frag cache" + " full in dup check)\n"); + dump_cache(reserve_cache); + + enable_progress_bar(); } void *info_thrd(void *arg) { sigset_t sigmask; - int sig, res; - char *subpath; + struct timespec timespec = { .tv_sec = 1, .tv_nsec = 0 }; + int sig, waiting = 0; sigemptyset(&sigmask); sigaddset(&sigmask, SIGQUIT); + sigaddset(&sigmask, SIGHUP); while(1) { - sigwait(&sigmask, &sig); - - if(dir_ent == NULL) - continue; - - if(dir_ent->our_dir->subpath[0] != '\0') - res = asprintf(&subpath, "%s/%s", - dir_ent->our_dir->subpath, dir_ent->name); + if(waiting) + sig = sigtimedwait(&sigmask, NULL, ×pec); else - res = asprintf(&subpath, "/%s", dir_ent->name); - - if(res < 0) - printf("asprintf failed in info_thrd\n"); + sig = sigwaitinfo(&sigmask, NULL); - INFO("%s\n", subpath); - free(subpath); + if(sig == -1) { + switch(errno) { + case EAGAIN: + /* interval timed out */ + waiting = 0; + /* FALLTHROUGH */ + case EINTR: + /* if waiting, the wait will be longer, but + that's OK */ + continue; + default: + BAD_ERROR("sigtimedwait/sigwaitinfo failed " + "because %s\n", strerror(errno)); + } + } + + if(sig == SIGQUIT && !waiting) { + print_filename(); + + /* set one second interval period, if ^\ received + within then, dump queue and cache status */ + waiting = 1; + } else + dump_state(); } } diff -Nru squashfs-tools-4.2+20130409/info.h squashfs-tools-4.3+20140919/info.h --- squashfs-tools-4.2+20130409/info.h 2013-05-09 10:39:11.000000000 +0200 +++ squashfs-tools-4.3+20140919/info.h 2015-07-20 21:03:05.000000000 +0200 @@ -1,8 +1,10 @@ +#ifndef INFO_H +#define INFO_H /* * Create a squashfs filesystem. This is a highly compressed read only * filesystem. * - * Copyright (c) 2013 + * Copyright (c) 2013, 2014 * Phillip Lougher * * This program is free software; you can redistribute it and/or @@ -23,5 +25,6 @@ */ extern void disable_info(); -extern void update_info(); +extern void update_info(struct dir_ent *); extern void init_info(); +#endif diff -Nru squashfs-tools-4.2+20130409/lz4_wrapper.c squashfs-tools-4.3+20140919/lz4_wrapper.c --- squashfs-tools-4.2+20130409/lz4_wrapper.c 1970-01-01 01:00:00.000000000 +0100 +++ squashfs-tools-4.3+20140919/lz4_wrapper.c 2015-07-20 21:03:05.000000000 +0200 @@ -0,0 +1,283 @@ +/* + * Copyright (c) 2013 + * Phillip Lougher + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2, + * or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * lz4_wrapper.c + * + * Support for LZ4 compression http://fastcompression.blogspot.com/p/lz4.html + */ + +#include +#include +#include +#include +#include + +#include "squashfs_fs.h" +#include "lz4_wrapper.h" +#include "compressor.h" + +static int hc = 0; + +/* + * This function is called by the options parsing code in mksquashfs.c + * to parse any -X compressor option. + * + * This function returns: + * >=0 (number of additional args parsed) on success + * -1 if the option was unrecognised, or + * -2 if the option was recognised, but otherwise bad in + * some way (e.g. invalid parameter) + * + * Note: this function sets internal compressor state, but does not + * pass back the results of the parsing other than success/failure. + * The lz4_dump_options() function is called later to get the options in + * a format suitable for writing to the filesystem. + */ +static int lz4_options(char *argv[], int argc) +{ + if(strcmp(argv[0], "-Xhc") == 0) { + hc = 1; + return 0; + } + + return -1; +} + + +/* + * This function is called by mksquashfs to dump the parsed + * compressor options in a format suitable for writing to the + * compressor options field in the filesystem (stored immediately + * after the superblock). + * + * This function returns a pointer to the compression options structure + * to be stored (and the size), or NULL if there are no compression + * options + * + * Currently LZ4 always returns a comp_opts structure, with + * the version indicating LZ4_LEGACY stream fomat. This is to + * easily accomodate changes in the kernel code to different + * stream formats + */ +static void *lz4_dump_options(int block_size, int *size) +{ + static struct lz4_comp_opts comp_opts; + + comp_opts.version = LZ4_LEGACY; + comp_opts.flags = hc ? LZ4_HC : 0; + SQUASHFS_INSWAP_COMP_OPTS(&comp_opts); + + *size = sizeof(comp_opts); + return &comp_opts; +} + + +/* + * This function is a helper specifically for the append mode of + * mksquashfs. Its purpose is to set the internal compressor state + * to the stored compressor options in the passed compressor options + * structure. + * + * In effect this function sets up the compressor options + * to the same state they were when the filesystem was originally + * generated, this is to ensure on appending, the compressor uses + * the same compression options that were used to generate the + * original filesystem. + * + * Note, even if there are no compressor options, this function is still + * called with an empty compressor structure (size == 0), to explicitly + * set the default options, this is to ensure any user supplied + * -X options on the appending mksquashfs command line are over-ridden + * + * This function returns 0 on sucessful extraction of options, and + * -1 on error + */ +static int lz4_extract_options(int block_size, void *buffer, int size) +{ + struct lz4_comp_opts *comp_opts = buffer; + + /* we expect a comp_opts structure to be present */ + if(size < sizeof(*comp_opts)) + goto failed; + + SQUASHFS_INSWAP_COMP_OPTS(comp_opts); + + /* we expect the stream format to be LZ4_LEGACY */ + if(comp_opts->version != LZ4_LEGACY) { + fprintf(stderr, "lz4: unknown LZ4 version\n"); + goto failed; + } + + /* + * Check compression flags, currently only LZ4_HC ("high compression") + * can be set. + */ + if(comp_opts->flags == LZ4_HC) + hc = 1; + else if(comp_opts->flags != 0) { + fprintf(stderr, "lz4: unknown LZ4 flags\n"); + goto failed; + } + + return 0; + +failed: + fprintf(stderr, "lz4: error reading stored compressor options from " + "filesystem!\n"); + + return -1; +} + + +/* + * This function is a helper specifically for unsquashfs. + * Its purpose is to check that the compression options are + * understood by this version of LZ4. + * + * This is important for LZ4 because the format understood by the + * Linux kernel may change from the already obsolete legacy format + * currently supported. + * + * If this does happen, then this version of LZ4 will not be able to decode + * the newer format. So we need to check for this. + * + * This function returns 0 on sucessful checking of options, and + * -1 on error + */ +static int lz4_check_options(int block_size, void *buffer, int size) +{ + struct lz4_comp_opts *comp_opts = buffer; + + /* we expect a comp_opts structure to be present */ + if(size < sizeof(*comp_opts)) + goto failed; + + SQUASHFS_INSWAP_COMP_OPTS(comp_opts); + + /* we expect the stream format to be LZ4_LEGACY */ + if(comp_opts->version != LZ4_LEGACY) { + fprintf(stderr, "lz4: unknown LZ4 version\n"); + goto failed; + } + + return 0; + +failed: + fprintf(stderr, "lz4: error reading stored compressor options from " + "filesystem!\n"); + return -1; +} + + +void lz4_display_options(void *buffer, int size) +{ + struct lz4_comp_opts *comp_opts = buffer; + + /* check passed comp opts struct is of the correct length */ + if(size < sizeof(*comp_opts)) + goto failed; + + SQUASHFS_INSWAP_COMP_OPTS(comp_opts); + + /* we expect the stream format to be LZ4_LEGACY */ + if(comp_opts->version != LZ4_LEGACY) { + fprintf(stderr, "lz4: unknown LZ4 version\n"); + goto failed; + } + + /* + * Check compression flags, currently only LZ4_HC ("high compression") + * can be set. + */ + if(comp_opts->flags & ~LZ4_FLAGS_MASK) { + fprintf(stderr, "lz4: unknown LZ4 flags\n"); + goto failed; + } + + if(comp_opts->flags & LZ4_HC) + printf("\tHigh Compression option specified (-Xhc)\n"); + + return; + +failed: + fprintf(stderr, "lz4: error reading stored compressor options from " + "filesystem!\n"); +} + + +static int lz4_compress(void *strm, void *dest, void *src, int size, + int block_size, int *error) +{ + int res; + + if(hc) + res = LZ4_compressHC_limitedOutput(src, dest, size, block_size); + else + res = LZ4_compress_limitedOutput(src, dest, size, block_size); + + if(res == 0) { + /* + * Output buffer overflow. Return out of buffer space + */ + return 0; + } else if(res < 0) { + /* + * All other errors return failure, with the compressor + * specific error code in *error + */ + *error = res; + return -1; + } + + return res; +} + + +static int lz4_uncompress(void *dest, void *src, int size, int outsize, + int *error) +{ + int res = LZ4_decompress_safe(src, dest, size, outsize); + if(res < 0) { + *error = res; + return -1; + } + + return res; +} + + +void lz4_usage() +{ + fprintf(stderr, "\t -Xhc\n"); + fprintf(stderr, "\t\tCompress using LZ4 High Compression\n"); +} + + +struct compressor lz4_comp_ops = { + .compress = lz4_compress, + .uncompress = lz4_uncompress, + .options = lz4_options, + .dump_options = lz4_dump_options, + .extract_options = lz4_extract_options, + .check_options = lz4_check_options, + .display_options = lz4_display_options, + .usage = lz4_usage, + .id = LZ4_COMPRESSION, + .name = "lz4", + .supported = 1 +}; diff -Nru squashfs-tools-4.2+20130409/lz4_wrapper.h squashfs-tools-4.3+20140919/lz4_wrapper.h --- squashfs-tools-4.2+20130409/lz4_wrapper.h 1970-01-01 01:00:00.000000000 +0100 +++ squashfs-tools-4.3+20140919/lz4_wrapper.h 2015-07-20 21:03:05.000000000 +0200 @@ -0,0 +1,61 @@ +#ifndef LZ4_WRAPPER_H +#define LZ4_WRAPPER_H +/* + * Squashfs + * + * Copyright (c) 2013 + * Phillip Lougher + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2, + * or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * lz4_wrapper.h + * + */ + +#ifndef linux +#define __BYTE_ORDER BYTE_ORDER +#define __BIG_ENDIAN BIG_ENDIAN +#define __LITTLE_ENDIAN LITTLE_ENDIAN +#else +#include +#endif + +#if __BYTE_ORDER == __BIG_ENDIAN +extern unsigned int inswap_le32(unsigned int); + +#define SQUASHFS_INSWAP_COMP_OPTS(s) { \ + (s)->version = inswap_le32((s)->version); \ + (s)->flags = inswap_le32((s)->flags); \ +} +#else +#define SQUASHFS_INSWAP_COMP_OPTS(s) +#endif + +/* + * Define the various stream formats recognised. + * Currently omly legacy stream format is supported by the + * kernel + */ +#define LZ4_LEGACY 1 +#define LZ4_FLAGS_MASK 1 + +/* Define the compression flags recognised. */ +#define LZ4_HC 1 + +struct lz4_comp_opts { + int version; + int flags; +}; +#endif diff -Nru squashfs-tools-4.2+20130409/lzma_wrapper.c squashfs-tools-4.3+20140919/lzma_wrapper.c --- squashfs-tools-4.2+20130409/lzma_wrapper.c 2013-05-09 10:39:11.000000000 +0200 +++ squashfs-tools-4.3+20140919/lzma_wrapper.c 2015-07-20 21:03:05.000000000 +0200 @@ -99,8 +99,12 @@ res = LzmaUncompress(dest, &outlen, src + LZMA_HEADER_SIZE, &inlen, src, LZMA_PROPS_SIZE); - *error = res; - return res == SZ_OK ? outlen : -1; + if(res == SZ_OK) + return outlen; + else { + *error = res; + return -1; + } } diff -Nru squashfs-tools-4.2+20130409/lzo_wrapper.c squashfs-tools-4.3+20140919/lzo_wrapper.c --- squashfs-tools-4.2+20130409/lzo_wrapper.c 2013-05-09 10:39:11.000000000 +0200 +++ squashfs-tools-4.3+20140919/lzo_wrapper.c 2015-07-20 21:03:05.000000000 +0200 @@ -1,8 +1,5 @@ /* - * Copyright (c) 2010 LG Electronics - * Chan Jeong - * - * All modifications Copyright (c) 2010, 2013 + * Copyright (c) 2013, 2014 * Phillip Lougher * * This program is free software; you can redistribute it and/or @@ -20,44 +17,319 @@ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * lzo_wrapper.c + * + * Support for LZO compression http://www.oberhumer.com/opensource/lzo */ -#include +#include #include - +#include #include #include #include "squashfs_fs.h" +#include "lzo_wrapper.h" #include "compressor.h" -/* worst-case expansion calculation during compression, - see LZO FAQ for more information */ -#define LZO_OUTPUT_BUFFER_SIZE(size) (size + (size/16) + 64 + 3) - -struct lzo_stream { - lzo_voidp wrkmem; - lzo_bytep out; +static struct lzo_algorithm lzo[] = { + { "lzo1x_1", LZO1X_1_MEM_COMPRESS, lzo1x_1_compress }, + { "lzo1x_1_11", LZO1X_1_11_MEM_COMPRESS, lzo1x_1_11_compress }, + { "lzo1x_1_12", LZO1X_1_12_MEM_COMPRESS, lzo1x_1_12_compress }, + { "lzo1x_1_15", LZO1X_1_15_MEM_COMPRESS, lzo1x_1_15_compress }, + { "lzo1x_999", LZO1X_999_MEM_COMPRESS, lzo1x_999_wrapper }, + { NULL, 0, NULL } }; +/* default LZO compression algorithm and compression level */ +static int algorithm = SQUASHFS_LZO1X_999; +static int compression_level = SQUASHFS_LZO1X_999_COMP_DEFAULT; + +/* user specified compression level */ +static int user_comp_level = -1; + + +/* + * This function is called by the options parsing code in mksquashfs.c + * to parse any -X compressor option. + * + * This function returns: + * >=0 (number of additional args parsed) on success + * -1 if the option was unrecognised, or + * -2 if the option was recognised, but otherwise bad in + * some way (e.g. invalid parameter) + * + * Note: this function sets internal compressor state, but does not + * pass back the results of the parsing other than success/failure. + * The lzo_dump_options() function is called later to get the options in + * a format suitable for writing to the filesystem. + */ +static int lzo_options(char *argv[], int argc) +{ + int i; + + if(strcmp(argv[0], "-Xalgorithm") == 0) { + if(argc < 2) { + fprintf(stderr, "lzo: -Xalgorithm missing algorithm\n"); + fprintf(stderr, "lzo: -Xalgorithm \n"); + goto failed2; + } + + for(i = 0; lzo[i].name; i++) { + if(strcmp(argv[1], lzo[i].name) == 0) { + algorithm = i; + return 1; + } + } + + fprintf(stderr, "lzo: -Xalgorithm unrecognised algorithm\n"); + goto failed2; + } else if(strcmp(argv[0], "-Xcompression-level") == 0) { + if(argc < 2) { + fprintf(stderr, "lzo: -Xcompression-level missing " + "compression level\n"); + fprintf(stderr, "lzo: -Xcompression-level it " + "should be 1 >= n <= 9\n"); + goto failed; + } + + user_comp_level = atoi(argv[1]); + if(user_comp_level < 1 || user_comp_level > 9) { + fprintf(stderr, "lzo: -Xcompression-level invalid, it " + "should be 1 >= n <= 9\n"); + goto failed; + } + + return 1; + } + + return -1; -static int squashfs_lzo_init(void **strm, int block_size, int flags) +failed: + return -2; + +failed2: + fprintf(stderr, "lzo: compression algorithm should be one of:\n"); + for(i = 0; lzo[i].name; i++) + fprintf(stderr, "\t%s\n", lzo[i].name); + return -2; +} + + +/* + * This function is called after all options have been parsed. + * It is used to do post-processing on the compressor options using + * values that were not expected to be known at option parse time. + * + * In this case the LZO algorithm may not be known until after the + * compression level has been set (-Xalgorithm used after -Xcompression-level) + * + * This function returns 0 on successful post processing, or + * -1 on error + */ +static int lzo_options_post(int block_size) +{ + /* + * Use of compression level only makes sense for + * LZO1X_999 algorithm + */ + if(user_comp_level != -1) { + if(algorithm != SQUASHFS_LZO1X_999) { + fprintf(stderr, "lzo: -Xcompression-level not " + "supported by selected %s algorithm\n", + lzo[algorithm].name); + fprintf(stderr, "lzo: -Xcompression-level is only " + "applicable for the lzo1x_999 algorithm\n"); + goto failed; + } + compression_level = user_comp_level; + } + + return 0; + +failed: + return -1; +} + + +/* + * This function is called by mksquashfs to dump the parsed + * compressor options in a format suitable for writing to the + * compressor options field in the filesystem (stored immediately + * after the superblock). + * + * This function returns a pointer to the compression options structure + * to be stored (and the size), or NULL if there are no compression + * options + * + */ +static void *lzo_dump_options(int block_size, int *size) +{ + static struct lzo_comp_opts comp_opts; + + /* + * If default compression options of SQUASHFS_LZO1X_999 and + * compression level of SQUASHFS_LZO1X_999_COMP_DEFAULT then + * don't store a compression options structure (this is compatible + * with the legacy implementation of LZO for Squashfs) + */ + if(algorithm == SQUASHFS_LZO1X_999 && + compression_level == SQUASHFS_LZO1X_999_COMP_DEFAULT) + return NULL; + + comp_opts.algorithm = algorithm; + comp_opts.compression_level = algorithm == SQUASHFS_LZO1X_999 ? + compression_level : 0; + + SQUASHFS_INSWAP_COMP_OPTS(&comp_opts); + + *size = sizeof(comp_opts); + return &comp_opts; +} + + +/* + * This function is a helper specifically for the append mode of + * mksquashfs. Its purpose is to set the internal compressor state + * to the stored compressor options in the passed compressor options + * structure. + * + * In effect this function sets up the compressor options + * to the same state they were when the filesystem was originally + * generated, this is to ensure on appending, the compressor uses + * the same compression options that were used to generate the + * original filesystem. + * + * Note, even if there are no compressor options, this function is still + * called with an empty compressor structure (size == 0), to explicitly + * set the default options, this is to ensure any user supplied + * -X options on the appending mksquashfs command line are over-ridden + * + * This function returns 0 on sucessful extraction of options, and + * -1 on error + */ +static int lzo_extract_options(int block_size, void *buffer, int size) +{ + struct lzo_comp_opts *comp_opts = buffer; + + if(size == 0) { + /* Set default values */ + algorithm = SQUASHFS_LZO1X_999; + compression_level = SQUASHFS_LZO1X_999_COMP_DEFAULT; + return 0; + } + + /* we expect a comp_opts structure of sufficient size to be present */ + if(size < sizeof(*comp_opts)) + goto failed; + + SQUASHFS_INSWAP_COMP_OPTS(comp_opts); + + /* Check comp_opts structure for correctness */ + switch(comp_opts->algorithm) { + case SQUASHFS_LZO1X_1: + case SQUASHFS_LZO1X_1_11: + case SQUASHFS_LZO1X_1_12: + case SQUASHFS_LZO1X_1_15: + if(comp_opts->compression_level != 0) { + fprintf(stderr, "lzo: bad compression level in " + "compression options structure\n"); + goto failed; + } + break; + case SQUASHFS_LZO1X_999: + if(comp_opts->compression_level < 1 || + comp_opts->compression_level > 9) { + fprintf(stderr, "lzo: bad compression level in " + "compression options structure\n"); + goto failed; + } + compression_level = comp_opts->compression_level; + break; + default: + fprintf(stderr, "lzo: bad algorithm in compression options " + "structure\n"); + goto failed; + } + + algorithm = comp_opts->algorithm; + + return 0; + +failed: + fprintf(stderr, "lzo: error reading stored compressor options from " + "filesystem!\n"); + + return -1; +} + + +void lzo_display_options(void *buffer, int size) +{ + struct lzo_comp_opts *comp_opts = buffer; + + /* we expect a comp_opts structure of sufficient size to be present */ + if(size < sizeof(*comp_opts)) + goto failed; + + SQUASHFS_INSWAP_COMP_OPTS(comp_opts); + + /* Check comp_opts structure for correctness */ + switch(comp_opts->algorithm) { + case SQUASHFS_LZO1X_1: + case SQUASHFS_LZO1X_1_11: + case SQUASHFS_LZO1X_1_12: + case SQUASHFS_LZO1X_1_15: + printf("\talgorithm %s\n", lzo[comp_opts->algorithm].name); + break; + case SQUASHFS_LZO1X_999: + if(comp_opts->compression_level < 1 || + comp_opts->compression_level > 9) { + fprintf(stderr, "lzo: bad compression level in " + "compression options structure\n"); + goto failed; + } + printf("\talgorithm %s\n", lzo[comp_opts->algorithm].name); + printf("\tcompression level %d\n", + comp_opts->compression_level); + break; + default: + fprintf(stderr, "lzo: bad algorithm in compression options " + "structure\n"); + goto failed; + } + + return; + +failed: + fprintf(stderr, "lzo: error reading stored compressor options from " + "filesystem!\n"); +} + + +/* + * This function is called by mksquashfs to initialise the + * compressor, before compress() is called. + * + * This function returns 0 on success, and + * -1 on error + */ +static int squashfs_lzo_init(void **strm, int block_size, int datablock) { struct lzo_stream *stream; - if((stream = *strm = malloc(sizeof(struct lzo_stream))) == NULL) + stream = *strm = malloc(sizeof(struct lzo_stream)); + if(stream == NULL) goto failed; - /* work memory for compression */ - if((stream->wrkmem = malloc(LZO1X_999_MEM_COMPRESS)) == NULL) + + stream->workspace = malloc(lzo[algorithm].size); + if(stream->workspace == NULL) goto failed2; - /* temporal output buffer */ - if((stream->out = malloc(LZO_OUTPUT_BUFFER_SIZE(block_size))) == NULL) - goto failed3; - return 0; + stream->buffer = malloc(LZO_MAX_EXPANSION(block_size)); + if(stream->buffer != NULL) + return 0; -failed3: - free(stream->wrkmem); + free(stream->workspace); failed2: free(stream); failed: @@ -65,47 +337,89 @@ } -static int lzo_compress(void *strm, void *d, void *s, int size, int block_size, - int *error) +static int lzo_compress(void *strm, void *dest, void *src, int size, + int block_size, int *error) { int res; - lzo_uint outlen; + lzo_uint compsize, orig_size = size; struct lzo_stream *stream = strm; - res = lzo1x_999_compress(s, size, stream->out, &outlen, stream->wrkmem); + res = lzo[algorithm].compress(src, size, stream->buffer, &compsize, + stream->workspace); if(res != LZO_E_OK) - goto failed; - if(outlen >= size) - /* - * Output buffer overflow. Return out of buffer space - */ - return 0; + goto failed; - /* - * Success, return the compressed size. + /* Successful compression, however, we need to check that + * the compressed size is not larger than the available + * buffer space. Normally in other compressor APIs they take + * a destination buffer size, and overflows return an error. + * With LZO it lacks a destination size and so we must output + * to a temporary buffer large enough to accomodate any + * result, and explictly check here for overflow */ - memcpy(d, stream->out, outlen); - return outlen; + if(compsize > block_size) + return 0; + + res = lzo1x_optimize(stream->buffer, compsize, src, &orig_size, NULL); + + if (res != LZO_E_OK || orig_size != size) + goto failed; + + memcpy(dest, stream->buffer, compsize); + return compsize; failed: - /* - * All other errors return failure, with the compressor - * specific error code in *error - */ + /* fail, compressor specific error code returned in error */ *error = res; return -1; } -static int lzo_uncompress(void *d, void *s, int size, int outsize, int *error) +static int lzo_uncompress(void *dest, void *src, int size, int outsize, + int *error) { int res; - lzo_uint bytes = outsize; + lzo_uint outlen = outsize; - res = lzo1x_decompress_safe(s, size, d, &bytes, NULL); + res = lzo1x_decompress_safe(src, size, dest, &outlen, NULL); + if(res != LZO_E_OK) { + *error = res; + return -1; + } - *error = res; - return res == LZO_E_OK ? bytes : -1; + return outlen; +} + + +void lzo_usage() +{ + int i; + + fprintf(stderr, "\t -Xalgorithm \n"); + fprintf(stderr, "\t\tWhere is one of:\n"); + + for(i = 0; lzo[i].name; i++) + fprintf(stderr, "\t\t\t%s%s\n", lzo[i].name, + i == SQUASHFS_LZO1X_999 ? " (default)" : ""); + + fprintf(stderr, "\t -Xcompression-level \n"); + fprintf(stderr, "\t\t should be 1 .. 9 (default " + "%d)\n", SQUASHFS_LZO1X_999_COMP_DEFAULT); + fprintf(stderr, "\t\tOnly applies to lzo1x_999 algorithm\n"); +} + + +/* + * Helper function for lzo1x_999 compression algorithm. + * All other lzo1x_xxx compressors do not take a compression level, + * so we need to wrap lzo1x_999 to pass the compression level which + * is applicable to it + */ +int lzo1x_999_wrapper(const lzo_bytep src, lzo_uint src_len, lzo_bytep dst, + lzo_uintp compsize, lzo_voidp workspace) +{ + return lzo1x_999_compress_level(src, src_len, dst, compsize, + workspace, NULL, 0, 0, compression_level); } @@ -113,10 +427,13 @@ .init = squashfs_lzo_init, .compress = lzo_compress, .uncompress = lzo_uncompress, - .options = NULL, - .usage = NULL, + .options = lzo_options, + .options_post = lzo_options_post, + .dump_options = lzo_dump_options, + .extract_options = lzo_extract_options, + .display_options = lzo_display_options, + .usage = lzo_usage, .id = LZO_COMPRESSION, .name = "lzo", .supported = 1 }; - diff -Nru squashfs-tools-4.2+20130409/lzo_wrapper.h squashfs-tools-4.3+20140919/lzo_wrapper.h --- squashfs-tools-4.2+20130409/lzo_wrapper.h 1970-01-01 01:00:00.000000000 +0100 +++ squashfs-tools-4.3+20140919/lzo_wrapper.h 2015-07-20 21:03:05.000000000 +0200 @@ -0,0 +1,78 @@ +#ifndef LZO_WRAPPER_H +#define LZO_WRAPPER_H +/* + * Squashfs + * + * Copyright (c) 2013 + * Phillip Lougher + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2, + * or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * lzo_wrapper.h + * + */ + +#ifndef linux +#define __BYTE_ORDER BYTE_ORDER +#define __BIG_ENDIAN BIG_ENDIAN +#define __LITTLE_ENDIAN LITTLE_ENDIAN +#else +#include +#endif + +#if __BYTE_ORDER == __BIG_ENDIAN +extern unsigned int inswap_le32(unsigned int); + +#define SQUASHFS_INSWAP_COMP_OPTS(s) { \ + (s)->algorithm = inswap_le32((s)->algorithm); \ + (s)->compression_level = inswap_le32((s)->compression_level); \ +} +#else +#define SQUASHFS_INSWAP_COMP_OPTS(s) +#endif + +/* Define the compression flags recognised. */ +#define SQUASHFS_LZO1X_1 0 +#define SQUASHFS_LZO1X_1_11 1 +#define SQUASHFS_LZO1X_1_12 2 +#define SQUASHFS_LZO1X_1_15 3 +#define SQUASHFS_LZO1X_999 4 + +/* Default compression level used by SQUASHFS_LZO1X_999 */ +#define SQUASHFS_LZO1X_999_COMP_DEFAULT 8 + +struct lzo_comp_opts { + int algorithm; + int compression_level; +}; + +struct lzo_algorithm { + char *name; + int size; + int (*compress) (const lzo_bytep, lzo_uint, lzo_bytep, lzo_uintp, + lzo_voidp); +}; + +struct lzo_stream { + void *workspace; + void *buffer; +}; + +#define LZO_MAX_EXPANSION(size) (size + (size / 16) + 64 + 3) + +int lzo1x_999_wrapper(const lzo_bytep, lzo_uint, lzo_bytep, lzo_uintp, + lzo_voidp); + +#endif diff -Nru squashfs-tools-4.2+20130409/Makefile squashfs-tools-4.3+20140919/Makefile --- squashfs-tools-4.2+20130409/Makefile 2013-05-09 10:39:11.000000000 +0200 +++ squashfs-tools-4.3+20140919/Makefile 2015-07-20 21:03:05.000000000 +0200 @@ -40,6 +40,20 @@ #LZO_SUPPORT = 1 #LZO_DIR = /usr/local + +########### Building LZ4 support ############# +# +# Yann Collet's LZ4 tools are supported +# LZ4 homepage: http://fastcompression.blogspot.com/p/lz4.html +# LZ4 source repository: http://code.google.com/p/lz4 +# +# To build configure the tools using cmake to build shared libraries, +# install and uncomment +# the LZ4_SUPPORT line below. +# +#LZ4_SUPPORT = 1 + + ########### Building LZMA support ############# # # LZMA1 compression. @@ -96,12 +110,12 @@ INCLUDEDIR = -I. INSTALL_DIR = /usr/local/bin -MKSQUASHFS_OBJS = mksquashfs.o read_fs.o sort.o swap.o pseudo.o compressor.o \ - action.o progressbar.o read_file.o info.o restore.o \ +MKSQUASHFS_OBJS = mksquashfs.o read_fs.o action.o swap.o pseudo.o compressor.o \ + sort.o progressbar.o read_file.o info.o restore.o process_fragments.o \ caches-queues-lists.o UNSQUASHFS_OBJS = unsquashfs.o unsquash-1.o unsquash-2.o unsquash-3.o \ - unsquash-4.o swap.o compressor.o + unsquash-4.o swap.o compressor.o unsquashfs_info.o CFLAGS ?= -O2 CFLAGS += $(EXTRA_CFLAGS) $(INCLUDEDIR) -D_FILE_OFFSET_BITS=64 \ @@ -155,6 +169,14 @@ COMPRESSORS += lzo endif +ifeq ($(LZ4_SUPPORT),1) +CFLAGS += -DLZ4_SUPPORT +MKSQUASHFS_OBJS += lz4_wrapper.o +UNSQUASHFS_OBJS += lz4_wrapper.o +LIBS += -llz4 +COMPRESSORS += lz4 +endif + ifeq ($(XATTR_SUPPORT),1) ifeq ($(XATTR_DEFAULT),1) CFLAGS += -DXATTR_SUPPORT -DXATTR_DEFAULT @@ -187,14 +209,16 @@ # At least one compressor must have been selected # ifndef COMPRESSORS -$(error "No compressor selected! Select one or more of GZIP, LZMA, XZ or LZO!") +$(error "No compressor selected! Select one or more of GZIP, LZMA, XZ, LZO or \ + LZ4!") endif # # COMP_DEFAULT must be a selected compressor # ifeq (, $(findstring $(COMP_DEFAULT), $(COMPRESSORS))) -$(error "COMP_DEFAULT isn't selected to be built!") +$(error "COMP_DEFAULT is set to ${COMP_DEFAULT}, which isn't selected to be \ + built!") endif .PHONY: all @@ -203,45 +227,53 @@ mksquashfs: $(MKSQUASHFS_OBJS) $(CC) $(LDFLAGS) $(EXTRA_LDFLAGS) $(MKSQUASHFS_OBJS) $(LIBS) -o $@ -mksquashfs.o: mksquashfs.c squashfs_fs.h mksquashfs.h sort.h squashfs_swap.h \ - xattr.h pseudo.h compressor.h action.h progressbar.h +mksquashfs.o: Makefile mksquashfs.c squashfs_fs.h squashfs_swap.h mksquashfs.h \ + sort.h pseudo.h compressor.h xattr.h action.h error.h progressbar.h \ + info.h caches-queues-lists.h read_fs.h restore.h process_fragments.h -read_fs.o: read_fs.c squashfs_fs.h read_fs.h squashfs_swap.h compressor.h \ - xattr.h +read_fs.o: read_fs.c squashfs_fs.h squashfs_swap.h compressor.h xattr.h \ + error.h mksquashfs.h -sort.o: sort.c squashfs_fs.h sort.h mksquashfs.h +sort.o: sort.c squashfs_fs.h mksquashfs.h sort.h error.h progressbar.h swap.o: swap.c -pseudo.o: pseudo.c pseudo.h +pseudo.o: pseudo.c pseudo.h error.h progressbar.h -compressor.o: compressor.c compressor.h squashfs_fs.h +compressor.o: Makefile compressor.c compressor.h squashfs_fs.h -xattr.o: xattr.c xattr.h squashfs_fs.h squashfs_swap.h mksquashfs.h +xattr.o: xattr.c squashfs_fs.h squashfs_swap.h mksquashfs.h xattr.h error.h \ + progressbar.h -read_xattrs.o: read_xattrs.c xattr.h squashfs_fs.h squashfs_swap.h read_fs.h +read_xattrs.o: read_xattrs.c squashfs_fs.h squashfs_swap.h xattr.h error.h -action.o: action.h squashfs_fs.h mksquashfs.h +action.o: action.c squashfs_fs.h mksquashfs.h action.h error.h -progressbar.o: progressbar.c +progressbar.o: progressbar.c error.h -read_file.o: read_file.c +read_file.o: read_file.c error.h -info.o: info.c +info.o: info.c squashfs_fs.h mksquashfs.h error.h progressbar.h \ + caches-queues-lists.h -restore.o: restore.c +restore.o: restore.c caches-queues-lists.h squashfs_fs.h mksquashfs.h error.h \ + progressbar.h info.h -caches-queues-lists.o: caches-queues-lists.c +process_fragments.o: process_fragments.c process_fragments.h -gzip_wrapper.o: gzip_wrapper.c compressor.h squashfs_fs.h +caches-queues-lists.o: caches-queues-lists.c error.h caches-queues-lists.h + +gzip_wrapper.o: gzip_wrapper.c squashfs_fs.h gzip_wrapper.h compressor.h lzma_wrapper.o: lzma_wrapper.c compressor.h squashfs_fs.h lzma_xz_wrapper.o: lzma_xz_wrapper.c compressor.h squashfs_fs.h -lzo_wrapper.o: lzo_wrapper.c compressor.h squashfs_fs.h +lzo_wrapper.o: lzo_wrapper.c squashfs_fs.h lzo_wrapper.h compressor.h + +lz4_wrapper.o: lz4_wrapper.c squashfs_fs.h lz4_wrapper.h compressor.h -xz_wrapper.o: xz_wrapper.c compressor.h squashfs_fs.h +xz_wrapper.o: xz_wrapper.c squashfs_fs.h xz_wrapper.h compressor.h unsquashfs: $(UNSQUASHFS_OBJS) $(CC) $(LDFLAGS) $(EXTRA_LDFLAGS) $(UNSQUASHFS_OBJS) $(LIBS) -o $@ @@ -260,6 +292,7 @@ unsquashfs_xattr.o: unsquashfs_xattr.c unsquashfs.h squashfs_fs.h xattr.h +unsquashfs_info.o: unsquashfs.h squashfs_fs.h .PHONY: clean clean: diff -Nru squashfs-tools-4.2+20130409/mksquashfs.c squashfs-tools-4.3+20140919/mksquashfs.c --- squashfs-tools-4.2+20130409/mksquashfs.c 2013-05-09 10:39:11.000000000 +0200 +++ squashfs-tools-4.3+20140919/mksquashfs.c 2015-07-20 21:03:05.000000000 +0200 @@ -3,7 +3,7 @@ * filesystem. * * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, - * 2012, 2013 + * 2012, 2013, 2014 * Phillip Lougher * * This program is free software; you can redistribute it and/or @@ -73,6 +73,9 @@ #include "progressbar.h" #include "info.h" #include "caches-queues-lists.h" +#include "read_fs.h" +#include "restore.h" +#include "process_fragments.h" int delete = FALSE; int fd; @@ -80,17 +83,23 @@ /* filesystem flags for building */ int comp_opts = FALSE; -int no_xattrs = XATTR_DEF, noX = 0; -int duplicate_checking = 1, noF = 0, no_fragments = 0, always_use_fragments = 0; -int noI = 0, noD = 0; +int no_xattrs = XATTR_DEF; +int noX = FALSE; +int duplicate_checking = TRUE; +int noF = FALSE; +int no_fragments = FALSE; +int always_use_fragments = FALSE; +int noI = FALSE; +int noD = FALSE; int silent = TRUE; -long long global_uid = -1, global_gid = -1; int exportable = TRUE; -int progress = TRUE; int sparse_files = TRUE; int old_exclude = TRUE; int use_regex = FALSE; int nopad = FALSE; +int exit_on_error = FALSE; + +long long global_uid = -1, global_gid = -1; /* superblock attributes */ int block_size = SQUASHFS_FILE_SIZE, block_log; @@ -186,21 +195,15 @@ struct pathname *stickypath = NULL; int excluded(char *name, struct pathnames *paths, struct pathnames **new); -/* fragment block data structures */ int fragments = 0; -struct fragment { - unsigned int index; - int offset; - int size; -}; - #define FRAG_SIZE 32768 -#define FRAG_INDEX (1LL << 32) struct squashfs_fragment_entry *fragment_table = NULL; int fragments_outstanding = 0; +int fragments_locked = FALSE; + /* current inode number for directories and non directories */ unsigned int inode_no = 1; unsigned int root_inode_number = 0; @@ -217,21 +220,9 @@ }; struct old_root_entry_info *old_root_entry; -/* in memory file info */ -struct file_info { - long long file_size; - long long bytes; - unsigned short checksum; - unsigned short fragment_checksum; - long long start; - unsigned int *block_list; - struct file_info *next; - struct fragment *fragment; - char checksum_flag; -}; - /* restore orignal filesystem state if appending to existing filesystem is * cancelled */ +int appending = FALSE; char *sdata_cache, *sdirectory_data_cache, *sdirectory_compressed; long long sbytes, stotal_bytes; @@ -245,10 +236,10 @@ int threads; /* flag whether destination file is a block device */ -int block_device = 0; +int block_device = FALSE; /* flag indicating whether files are sorted using sort list(s) */ -int sorted = 0; +int sorted = FALSE; /* save destination file name for deleting on error */ char *destination_file = NULL; @@ -257,93 +248,67 @@ char *recovery_file = NULL; int recover = TRUE; -/* in memory uid tables */ -#define ID_ENTRIES 256 -#define ID_HASH(id) (id & (ID_ENTRIES - 1)) -#define ISA_UID 1 -#define ISA_GID 2 -struct id { - unsigned int id; - int index; - char flags; - struct id *next; -}; struct id *id_hash_table[ID_ENTRIES]; struct id *id_table[SQUASHFS_IDS], *sid_table[SQUASHFS_IDS]; unsigned int uid_count = 0, guid_count = 0; unsigned int sid_count = 0, suid_count = 0, sguid_count = 0; -struct cache *reader_buffer, *writer_buffer, *fragment_buffer; -struct queue *to_reader, *from_reader, *to_writer, *from_writer, *from_deflate, - *to_frag; -pthread_t *thread, *deflator_thread, *frag_deflator_thread; +struct cache *reader_buffer, *fragment_buffer, *reserve_cache; +struct cache *bwriter_buffer, *fwriter_buffer; +struct queue *to_reader, *to_deflate, *to_writer, *from_writer, + *to_frag, *locked_fragment, *to_process_frag; +struct seq_queue *to_main; +pthread_t reader_thread, writer_thread, main_thread; +pthread_t *deflator_thread, *frag_deflator_thread, *frag_thread; pthread_t *restore_thread = NULL; -pthread_mutex_t fragment_mutex; -pthread_cond_t fragment_waiting; -pthread_mutex_t pos_mutex; +pthread_mutex_t fragment_mutex = PTHREAD_MUTEX_INITIALIZER; +pthread_mutex_t pos_mutex = PTHREAD_MUTEX_INITIALIZER; +pthread_mutex_t dup_mutex = PTHREAD_MUTEX_INITIALIZER; /* user options that control parallelisation */ int processors = -1; -/* default size of output buffer in Mbytes */ -#define WRITER_BUFFER_DEFAULT 512 -/* default size of input buffer in Mbytes */ -#define READER_BUFFER_DEFAULT 64 -/* default size of fragment buffer in Mbytes */ -#define FRAGMENT_BUFFER_DEFAULT 64 -int writer_buffer_size; +int bwriter_size; /* compression operations */ -static struct compressor *comp; -int compressor_opts_parsed = 0; +struct compressor *comp = NULL; +int compressor_opt_parsed = FALSE; void *stream = NULL; /* xattr stats */ unsigned int xattr_bytes = 0, total_xattr_bytes = 0; -char *read_from_disk(long long start, unsigned int avail_bytes); +/* fragment to file mapping used when appending */ +int append_fragments = 0; +struct append_file **file_mapping; + +/* root of the in-core directory structure */ +struct dir_info *root_dir; + +static char *read_from_disk(long long start, unsigned int avail_bytes); void add_old_root_entry(char *name, squashfs_inode inode, int inode_number, int type); -extern struct compressor *read_super(int fd, struct squashfs_super_block *sBlk, - char *source); -extern long long read_filesystem(char *root_name, int fd, - struct squashfs_super_block *sBlk, char **cinode_table, char **data_cache, - char **cdirectory_table, char **directory_data_cache, - unsigned int *last_directory_block, unsigned int *inode_dir_offset, - unsigned int *inode_dir_file_size, unsigned int *root_inode_size, - unsigned int *inode_dir_start_block, int *file_count, int *sym_count, - int *dev_count, int *dir_count, int *fifo_count, int *sock_count, - long long *uncompressed_file, unsigned int *uncompressed_inode, - unsigned int *uncompressed_directory, - unsigned int *inode_dir_inode_number, - unsigned int *inode_dir_parent_inode, - void (push_directory_entry)(char *, squashfs_inode, int, int), - struct squashfs_fragment_entry **fragment_table, - squashfs_inode **inode_lookup_table); -extern int read_sort_file(char *filename, int source, char *source_path[]); -extern void sort_files_and_write(struct dir_info *dir); struct file_info *duplicate(long long file_size, long long bytes, unsigned int **block_list, long long *start, struct fragment **fragment, struct file_buffer *file_buffer, int blocks, unsigned short checksum, - unsigned short fragment_checksum, int checksum_flag); + int checksum_flag); struct dir_info *dir_scan1(char *, char *, struct pathnames *, struct dir_ent *(_readdir)(struct dir_info *), int); void dir_scan2(struct dir_info *dir, struct pseudo *pseudo); -void dir_scan3(struct dir_info *root, struct dir_info *dir); +void dir_scan3(struct dir_info *dir); void dir_scan4(struct dir_info *dir); void dir_scan5(struct dir_info *dir); -void dir_scan6(squashfs_inode *inode, struct dir_info *dir_info); +void dir_scan6(struct dir_info *dir); +void dir_scan7(squashfs_inode *inode, struct dir_info *dir_info); struct file_info *add_non_dup(long long file_size, long long bytes, unsigned int *block_list, long long start, struct fragment *fragment, unsigned short checksum, unsigned short fragment_checksum, - int checksum_flag); -extern void generate_file_priorities(struct dir_info *dir, int priority, - struct stat *buf); -extern struct priority_entry *priority_list[65536]; + int checksum_flag, int checksum_frag_flag); long long generic_write_table(int, void *, int, void *, int); void restorefs(); struct dir_info *scan1_opendir(char *pathname, char *subpath, int depth); -extern pthread_t *init_restore_thread(pthread_t); void write_filesystem_tables(struct squashfs_super_block *sBlk, int nopad); +unsigned short get_checksum_mem(char *buff, int bytes); +void check_usable_phys_mem(int total_mem); void prep_exit() @@ -360,12 +325,14 @@ exit(1); } else { /* signal the restore thread to restore */ - kill(getpid(), SIGUSR1); + pthread_kill(*restore_thread, SIGUSR1); pthread_exit(NULL); } - } - if(delete && destination_file && !block_device) - unlink(destination_file); + } else if(delete) { + if(destination_file && !block_device) + unlink(destination_file); + } else if(recovery_file) + unlink(recovery_file); } @@ -387,22 +354,20 @@ } +int multiply_overflowll(long long a, int multiplier) +{ + return (LLONG_MAX / multiplier) < a; +} + + #define MKINODE(A) ((squashfs_inode)(((squashfs_inode) inode_bytes << 16) \ + (((char *)A) - data_cache))) void restorefs() { - int i; - ERROR("Exiting - restoring original filesystem!\n\n"); - for(i = 0; i < 2 + processors * 2; i++) - pthread_cancel(thread[i]); - for(i = 0; i < 2 + processors * 2; i++) - pthread_join(thread[i], NULL); - - TRACE("All threads in signal handler\n"); bytes = sbytes; memcpy(data_cache, sdata_cache, cache_bytes = scache_bytes); memcpy(directory_data_cache, sdirectory_data_cache, @@ -555,15 +520,11 @@ ERROR("read_fs_bytes: Lseek on destination failed because %s, " "offset=0x%llx\n", strerror(errno), off); res = 0; - goto mutex_unlock; - } - - if(read_bytes(fd, buff, bytes) < bytes) { + } else if(read_bytes(fd, buff, bytes) < bytes) { ERROR("Read on destination failed\n"); res = 0; } -mutex_unlock: pthread_cleanup_pop(1); return res; } @@ -1076,56 +1037,28 @@ } else if(type == SQUASHFS_SYMLINK_TYPE) { struct squashfs_symlink_inode_header *symlink = &inode_header.symlink; - int byte; - char buff[65536]; /* overflow safe */ + int byte = strlen(dir_ent->inode->symlink); size_t off = offsetof(struct squashfs_symlink_inode_header, symlink); - byte = readlink(filename, buff, 65536); - if(byte == -1) { - ERROR("Failed to read symlink %s, creating empty " - "symlink\n", filename); - byte = 0; - } - - if(byte == 65536) { - ERROR("Symlink %s is greater than 65536 bytes! " - "Creating empty symlink\n", filename); - byte = 0; - } - inode = get_inode(sizeof(*symlink) + byte); symlink->nlink = nlink; symlink->symlink_size = byte; SQUASHFS_SWAP_SYMLINK_INODE_HEADER(symlink, inode); - strncpy(inode + off, buff, byte); + strncpy(inode + off, dir_ent->inode->symlink, byte); TRACE("Symbolic link inode, symlink_size %d, nlink %d\n", byte, nlink); } else if(type == SQUASHFS_LSYMLINK_TYPE) { struct squashfs_symlink_inode_header *symlink = &inode_header.symlink; - int byte; - char buff[65536]; /* overflow safe */ + int byte = strlen(dir_ent->inode->symlink); size_t off = offsetof(struct squashfs_symlink_inode_header, symlink); - byte = readlink(filename, buff, 65536); - if(byte == -1) { - ERROR("Failed to read symlink %s, creating empty " - "symlink\n", filename); - byte = 0; - } - - if(byte == 65536) { - ERROR("Symlink %s is greater than 65536 bytes! " - "Creating empty symlink\n", filename); - byte = 0; - } - inode = get_inode(sizeof(*symlink) + byte + sizeof(unsigned int)); symlink->nlink = nlink; symlink->symlink_size = byte; SQUASHFS_SWAP_SYMLINK_INODE_HEADER(symlink, inode); - strncpy(inode + off, buff, byte); + strncpy(inode + off, dir_ent->inode->symlink, byte); SQUASHFS_SWAP_INTS(&xattr, inode + off + byte, 1); TRACE("Symbolic link inode, symlink_size %d, nlink %d\n", byte, nlink); @@ -1363,28 +1296,82 @@ } -struct file_buffer *get_fragment(struct fragment *fragment) +static struct file_buffer *get_fragment(struct fragment *fragment) { struct squashfs_fragment_entry *disk_fragment; - int res, size; - long long start_block; struct file_buffer *buffer, *compressed_buffer; + long long start_block; + int res, size, index = fragment->index; + char locked; + + /* + * Lookup fragment block in cache. + * If the fragment block doesn't exist, then get the compressed version + * from the writer cache or off disk, and decompress it. + * + * This routine has two things which complicate the code: + * + * 1. Multiple threads can simultaneously lookup/create the + * same buffer. This means a buffer needs to be "locked" + * when it is being filled in, to prevent other threads from + * using it when it is not ready. This is because we now do + * fragment duplicate checking in parallel. + * 2. We have two caches which need to be checked for the + * presence of fragment blocks: the normal fragment cache + * and a "reserve" cache. The reserve cache is used to + * prevent an unnecessary pipeline stall when the fragment cache + * is full of fragments waiting to be compressed. + */ if(fragment->index == SQUASHFS_INVALID_FRAG) return NULL; - buffer = cache_lookup(fragment_buffer, fragment->index); - if(buffer) - return buffer; + pthread_cleanup_push((void *) pthread_mutex_unlock, &dup_mutex); + pthread_mutex_lock(&dup_mutex); - compressed_buffer = cache_lookup(writer_buffer, fragment->index + - FRAG_INDEX); +again: + buffer = cache_lookup_nowait(fragment_buffer, index, &locked); + if(buffer) { + pthread_mutex_unlock(&dup_mutex); + if(locked) + /* got a buffer being filled in. Wait for it */ + cache_wait_unlock(buffer); + goto finished; + } + + /* not in fragment cache, is it in the reserve cache? */ + buffer = cache_lookup_nowait(reserve_cache, index, &locked); + if(buffer) { + pthread_mutex_unlock(&dup_mutex); + if(locked) + /* got a buffer being filled in. Wait for it */ + cache_wait_unlock(buffer); + goto finished; + } + + /* in neither cache, try to get it from the fragment cache */ + buffer = cache_get_nowait(fragment_buffer, index); + if(!buffer) { + /* + * no room, get it from the reserve cache, this is + * dimensioned so it will always have space (no more than + * processors + 1 can have an outstanding reserve buffer) + */ + buffer = cache_get_nowait(reserve_cache, index); + if(!buffer) { + /* failsafe */ + ERROR("no space in reserve cache\n"); + goto again; + } + } + + pthread_mutex_unlock(&dup_mutex); - buffer = cache_get(fragment_buffer, fragment->index, 1); + compressed_buffer = cache_lookup(fwriter_buffer, index); pthread_cleanup_push((void *) pthread_mutex_unlock, &fragment_mutex); pthread_mutex_lock(&fragment_mutex); - disk_fragment = &fragment_table[fragment->index]; + disk_fragment = &fragment_table[index]; size = SQUASHFS_COMPRESSED_SIZE_BLOCK(disk_fragment->size); start_block = disk_fragment->start_block; pthread_cleanup_pop(1); @@ -1395,8 +1382,14 @@ if(compressed_buffer) data = compressed_buffer->data; - else + else { data = read_from_disk(start_block, size); + if(data == NULL) { + ERROR("Failed to read fragment from output" + " filesystem\n"); + BAD_ERROR("Output filesystem corrupted?\n"); + } + } res = compressor_uncompress(comp, buffer->data, data, size, block_size, &error); @@ -1414,58 +1407,95 @@ } } + cache_unlock(buffer); cache_block_put(compressed_buffer); +finished: + pthread_cleanup_pop(0); + return buffer; } -struct frag_locked { - struct file_buffer *buffer; - int c_byte; - int fragment; - struct frag_locked *fragment_prev; - struct frag_locked *fragment_next; -}; +unsigned short get_fragment_checksum(struct file_info *file) +{ + struct file_buffer *frag_buffer; + struct append_file *append; + int res, index = file->fragment->index; + unsigned short checksum; -int fragments_locked = FALSE; -struct frag_locked *frag_locked_list = NULL; + if(index == SQUASHFS_INVALID_FRAG) + return 0; + + pthread_cleanup_push((void *) pthread_mutex_unlock, &dup_mutex); + pthread_mutex_lock(&dup_mutex); + res = file->have_frag_checksum; + checksum = file->fragment_checksum; + pthread_cleanup_pop(1); + + if(res) + return checksum; + + frag_buffer = get_fragment(file->fragment); + + pthread_cleanup_push((void *) pthread_mutex_unlock, &dup_mutex); + + for(append = file_mapping[index]; append; append = append->next) { + int offset = append->file->fragment->offset; + int size = append->file->fragment->size; + unsigned short cksum = + get_checksum_mem(frag_buffer->data + offset, size); -INSERT_LIST(fragment, struct frag_locked) -REMOVE_LIST(fragment, struct frag_locked) + if(file == append->file) + checksum = cksum; -int lock_fragments() + pthread_mutex_lock(&dup_mutex); + append->file->fragment_checksum = cksum; + append->file->have_frag_checksum = TRUE; + pthread_mutex_unlock(&dup_mutex); + } + + cache_block_put(frag_buffer); + pthread_cleanup_pop(0); + + return checksum; +} + + +void lock_fragments() { - int count; pthread_cleanup_push((void *) pthread_mutex_unlock, &fragment_mutex); pthread_mutex_lock(&fragment_mutex); fragments_locked = TRUE; - count = fragments_outstanding; pthread_cleanup_pop(1); - return count; } void unlock_fragments() { - struct frag_locked *entry; - int compressed_size; + int frg, size; + struct file_buffer *write_buffer; pthread_cleanup_push((void *) pthread_mutex_unlock, &fragment_mutex); pthread_mutex_lock(&fragment_mutex); - while(frag_locked_list) { - entry = frag_locked_list; - remove_fragment_list(&frag_locked_list, entry); - compressed_size = SQUASHFS_COMPRESSED_SIZE_BLOCK(entry->c_byte); - fragment_table[entry->fragment].size = entry->c_byte; - fragment_table[entry->fragment].start_block = bytes; - entry->buffer->block = bytes; - bytes += compressed_size; + + /* + * Note queue_empty() is inherently racy with respect to concurrent + * queue get and pushes. We avoid this because we're holding the + * fragment_mutex which ensures no other threads can be using the + * queue at this time. + */ + while(!queue_empty(locked_fragment)) { + write_buffer = queue_get(locked_fragment); + frg = write_buffer->block; + size = SQUASHFS_COMPRESSED_SIZE_BLOCK(fragment_table[frg].size); + fragment_table[frg].start_block = bytes; + write_buffer->block = bytes; + bytes += size; fragments_outstanding --; - queue_put(to_writer, entry->buffer); + queue_put(to_writer, write_buffer); TRACE("fragment_locked writing fragment %d, compressed size %d" - "\n", entry->fragment, compressed_size); - free(entry); + "\n", frg, size); } fragments_locked = FALSE; pthread_cleanup_pop(1); @@ -1475,14 +1505,10 @@ void add_pending_fragment(struct file_buffer *write_buffer, int c_byte, int fragment) { - struct frag_locked *entry = malloc(sizeof(struct frag_locked)); - if(entry == NULL) - MEM_ERROR(); - entry->buffer = write_buffer; - entry->c_byte = c_byte; - entry->fragment = fragment; - entry->fragment_prev = entry->fragment_next = NULL; - insert_fragment_list(&frag_locked_list, entry); + fragment_table[fragment].size = c_byte; + write_buffer->block = fragment; + + queue_put(locked_fragment, write_buffer); } @@ -1502,7 +1528,7 @@ struct file_buffer *allocate_fragment() { - struct file_buffer *fragment = cache_get(fragment_buffer, fragments, 1); + struct file_buffer *fragment = cache_get(fragment_buffer, fragments); pthread_cleanup_push((void *) pthread_mutex_unlock, &fragment_mutex); pthread_mutex_lock(&fragment_mutex); @@ -1543,7 +1569,7 @@ if(file_buffer == NULL || file_buffer->size == 0) return &empty_fragment; - fragment = eval_frag_actions(dir_ent); + fragment = eval_frag_actions(root_dir, dir_ent); if((*fragment) && (*fragment)->size + file_buffer->size > block_size) { write_fragment(*fragment); @@ -1645,15 +1671,13 @@ char read_from_file_buffer[SQUASHFS_FILE_MAX_SIZE]; -char *read_from_disk(long long start, unsigned int avail_bytes) +static char *read_from_disk(long long start, unsigned int avail_bytes) { int res; res = read_fs_bytes(fd, start, avail_bytes, read_from_file_buffer); - if(res == 0) { - ERROR("Failed to read data from output filesystem\n"); - BAD_ERROR("Output filesystem corrupted?\n"); - } + if(res == 0) + return NULL; return read_from_file_buffer; } @@ -1665,10 +1689,8 @@ int res; res = read_fs_bytes(fd, start, avail_bytes, read_from_file_buffer2); - if(res == 0) { - ERROR("Failed to read data from output filesystem\n"); - BAD_ERROR("Output filesystem corrupted?\n"); - } + if(res == 0) + return NULL; return read_from_file_buffer2; } @@ -1702,14 +1724,22 @@ bytes = SQUASHFS_COMPRESSED_SIZE_BLOCK(blocks[i]); if(bytes == 0) /* sparse block */ continue; - write_buffer = cache_lookup(writer_buffer, start); + write_buffer = cache_lookup(bwriter_buffer, start); if(write_buffer) { chksum = get_checksum(write_buffer->data, bytes, chksum); cache_block_put(write_buffer); - } else - chksum = get_checksum(read_from_disk(start, bytes), - bytes, chksum); + } else { + void *data = read_from_disk(start, bytes); + if(data == NULL) { + ERROR("Failed to checksum data from output" + " filesystem\n"); + BAD_ERROR("Output filesystem corrupted?\n"); + } + + chksum = get_checksum(data, bytes, chksum); + } + l -= bytes; start += bytes; } @@ -1741,6 +1771,8 @@ struct fragment *frg; unsigned int *block_list = block_listp; struct file_info *dupl_ptr = dupl[DUP_HASH(file_size)]; + struct append_file *append_file; + struct file_info *file; if(!duplicate_checking || file_size == 0) return; @@ -1767,44 +1799,29 @@ frg->offset = offset; frg->size = bytes; - add_non_dup(file_size, file_bytes, block_list, start, frg, 0, 0, FALSE); -} - + file = add_non_dup(file_size, file_bytes, block_list, start, frg, 0, 0, + FALSE, FALSE); -int pre_duplicate(long long file_size) -{ - struct file_info *dupl_ptr = dupl[DUP_HASH(file_size)]; + if(fragment == SQUASHFS_INVALID_FRAG) + return; - for(; dupl_ptr; dupl_ptr = dupl_ptr->next) - if(dupl_ptr->file_size == file_size) - return TRUE; + append_file = malloc(sizeof(struct append_file)); + if(append_file == NULL) + MEM_ERROR(); - return FALSE; + append_file->file = file; + append_file->next = file_mapping[fragment]; + file_mapping[fragment] = append_file; } -int pre_duplicate_frag(long long file_size, unsigned short checksum) +int pre_duplicate(long long file_size) { struct file_info *dupl_ptr = dupl[DUP_HASH(file_size)]; for(; dupl_ptr; dupl_ptr = dupl_ptr->next) - if(file_size == dupl_ptr->file_size && file_size == - dupl_ptr->fragment->size) { - if(dupl_ptr->checksum_flag == FALSE) { - struct file_buffer *frag_buffer = - get_fragment(dupl_ptr->fragment); - dupl_ptr->checksum = - get_checksum_disk(dupl_ptr->start, - dupl_ptr->bytes, dupl_ptr->block_list); - dupl_ptr->fragment_checksum = - get_checksum_mem(frag_buffer->data + - dupl_ptr->fragment->offset, file_size); - cache_block_put(frag_buffer); - dupl_ptr->checksum_flag = TRUE; - } - if(dupl_ptr->fragment_checksum == checksum) - return TRUE; - } + if(dupl_ptr->file_size == file_size) + return TRUE; return FALSE; } @@ -1813,7 +1830,7 @@ struct file_info *add_non_dup(long long file_size, long long bytes, unsigned int *block_list, long long start, struct fragment *fragment, unsigned short checksum, unsigned short fragment_checksum, - int checksum_flag) + int checksum_flag, int checksum_frag_flag) { struct file_info *dupl_ptr = malloc(sizeof(struct file_info)); @@ -1827,22 +1844,74 @@ dupl_ptr->fragment = fragment; dupl_ptr->checksum = checksum; dupl_ptr->fragment_checksum = fragment_checksum; - dupl_ptr->checksum_flag = checksum_flag; + dupl_ptr->have_frag_checksum = checksum_frag_flag; + dupl_ptr->have_checksum = checksum_flag; + + pthread_cleanup_push((void *) pthread_mutex_unlock, &dup_mutex); + pthread_mutex_lock(&dup_mutex); dupl_ptr->next = dupl[DUP_HASH(file_size)]; dupl[DUP_HASH(file_size)] = dupl_ptr; dup_files ++; + pthread_cleanup_pop(1); return dupl_ptr; } +struct fragment *frag_duplicate(struct file_buffer *file_buffer, char *dont_put) +{ + struct file_info *dupl_ptr; + struct file_buffer *buffer; + struct file_info *dupl_start = file_buffer->dupl_start; + long long file_size = file_buffer->file_size; + unsigned short checksum = file_buffer->checksum; + int res; + + if(file_buffer->duplicate) { + TRACE("Found duplicate file, fragment %d, size %d, offset %d, " + "checksum 0x%x\n", dupl_start->fragment->index, + file_size, dupl_start->fragment->offset, checksum); + *dont_put = TRUE; + return dupl_start->fragment; + } else { + *dont_put = FALSE; + dupl_ptr = dupl[DUP_HASH(file_size)]; + } + + for(; dupl_ptr && dupl_ptr != dupl_start; dupl_ptr = dupl_ptr->next) { + if(file_size == dupl_ptr->file_size && file_size == + dupl_ptr->fragment->size) { + if(get_fragment_checksum(dupl_ptr) == checksum) { + buffer = get_fragment(dupl_ptr->fragment); + res = memcmp(file_buffer->data, buffer->data + + dupl_ptr->fragment->offset, file_size); + cache_block_put(buffer); + if(res == 0) + break; + } + } + } + + if(!dupl_ptr || dupl_ptr == dupl_start) + return NULL; + + TRACE("Found duplicate file, fragment %d, size %d, offset %d, " + "checksum 0x%x\n", dupl_ptr->fragment->index, file_size, + dupl_ptr->fragment->offset, checksum); + + return dupl_ptr->fragment; +} + + struct file_info *duplicate(long long file_size, long long bytes, unsigned int **block_list, long long *start, struct fragment **fragment, struct file_buffer *file_buffer, int blocks, unsigned short checksum, - unsigned short fragment_checksum, int checksum_flag) + int checksum_flag) { struct file_info *dupl_ptr = dupl[DUP_HASH(file_size)]; int frag_bytes = file_buffer ? file_buffer->size : 0; + unsigned short fragment_checksum = file_buffer ? + file_buffer->checksum : 0; for(; dupl_ptr; dupl_ptr = dupl_ptr->next) if(file_size == dupl_ptr->file_size && bytes == dupl_ptr->bytes @@ -1857,27 +1926,19 @@ if(checksum_flag == FALSE) { checksum = get_checksum_disk(*start, bytes, *block_list); - fragment_checksum = - get_checksum_mem_buffer(file_buffer); checksum_flag = TRUE; } - if(dupl_ptr->checksum_flag == FALSE) { - struct file_buffer *frag_buffer = - get_fragment(dupl_ptr->fragment); + if(!dupl_ptr->have_checksum) { dupl_ptr->checksum = get_checksum_disk(dupl_ptr->start, dupl_ptr->bytes, dupl_ptr->block_list); - dupl_ptr->fragment_checksum = - get_checksum_mem(frag_buffer->data + - dupl_ptr->fragment->offset, frag_bytes); - cache_block_put(frag_buffer); - dupl_ptr->checksum_flag = TRUE; + dupl_ptr->have_checksum = TRUE; } if(checksum != dupl_ptr->checksum || fragment_checksum != - dupl_ptr->fragment_checksum) + get_fragment_checksum(dupl_ptr)) continue; target_start = *start; @@ -1891,22 +1952,36 @@ if(size == 0) continue; - target_buffer = cache_lookup(writer_buffer, + target_buffer = cache_lookup(bwriter_buffer, target_start); if(target_buffer) target_data = target_buffer->data; - else + else { target_data = read_from_disk(target_start, size); + if(target_data == NULL) { + ERROR("Failed to read data from" + " output filesystem\n"); + BAD_ERROR("Output filesystem" + " corrupted?\n"); + } + } - dup_buffer = cache_lookup(writer_buffer, + dup_buffer = cache_lookup(bwriter_buffer, dup_start); if(dup_buffer) dup_data = dup_buffer->data; - else + else { dup_data = read_from_disk2(dup_start, size); + if(dup_data == NULL) { + ERROR("Failed to read data from" + " output filesystem\n"); + BAD_ERROR("Output filesystem" + " corrupted?\n"); + } + } res = memcmp(target_data, dup_data, size); cache_block_put(target_buffer); @@ -1948,13 +2023,13 @@ return add_non_dup(file_size, bytes, *block_list, *start, *fragment, - checksum, fragment_checksum, checksum_flag); + checksum, fragment_checksum, checksum_flag, TRUE); } inline int is_fragment(struct inode_info *inode) { - int file_size = inode->buf.st_size; + off_t file_size = inode->buf.st_size; /* * If this block is to be compressed differently to the @@ -1963,33 +2038,57 @@ if(inode->noF != noF) return FALSE; - return !inode->no_fragments && (file_size < block_size || + return !inode->no_fragments && file_size && (file_size < block_size || (inode->always_use_fragments && file_size & (block_size - 1))); } +void put_file_buffer(struct file_buffer *file_buffer) +{ + /* + * Decide where to send the file buffer: + * - compressible non-fragment blocks go to the deflate threads, + * - fragments go to the process fragment threads, + * - all others go directly to the main thread + */ + if(file_buffer->error) { + file_buffer->fragment = 0; + seq_queue_put(to_main, file_buffer); + } else if (file_buffer->file_size == 0) + seq_queue_put(to_main, file_buffer); + else if(file_buffer->fragment) + queue_put(to_process_frag, file_buffer); + else + queue_put(to_deflate, file_buffer); +} + + static int seq = 0; void reader_read_process(struct dir_ent *dir_ent) { + long long bytes = 0; struct inode_info *inode = dir_ent->inode; struct file_buffer *prev_buffer = NULL, *file_buffer; - int status, res, byte, count = 0; - int file = get_pseudo_file(inode->pseudo_id)->fd; - int child = get_pseudo_file(inode->pseudo_id)->child; - long long bytes = 0; + int status, byte, res, child; + int file = pseudo_exec_file(get_pseudo_file(inode->pseudo_id), &child); + + if(!file) { + file_buffer = cache_get_nohash(reader_buffer); + file_buffer->sequence = seq ++; + goto read_err; + } while(1) { - file_buffer = cache_get(reader_buffer, 0, 0); + file_buffer = cache_get_nohash(reader_buffer); file_buffer->sequence = seq ++; file_buffer->noD = inode->noD; byte = read_bytes(file, file_buffer->data, block_size); if(byte == -1) - goto read_err; + goto read_err2; file_buffer->size = byte; file_buffer->file_size = -1; - file_buffer->block = count ++; file_buffer->error = FALSE; file_buffer->fragment = FALSE; bytes += byte; @@ -2007,7 +2106,7 @@ progress_bar_size(1); if(prev_buffer) - queue_put(from_reader, prev_buffer); + put_file_buffer(prev_buffer); prev_buffer = file_buffer; } @@ -2018,6 +2117,8 @@ inode->buf.st_size = bytes; res = waitpid(child, &status, 0); + close(file); + if(res == -1 || !WIFEXITED(status) || WEXITSTATUS(status) != 0) goto read_err; @@ -2029,10 +2130,12 @@ } prev_buffer->file_size = bytes; prev_buffer->fragment = is_fragment(inode); - queue_put(from_reader, prev_buffer); + put_file_buffer(prev_buffer); return; +read_err2: + close(file); read_err: if(prev_buffer) { cache_block_put(file_buffer); @@ -2040,7 +2143,7 @@ file_buffer = prev_buffer; } file_buffer->error = TRUE; - queue_put(from_deflate, file_buffer); + put_file_buffer(file_buffer); } @@ -2048,7 +2151,7 @@ { struct stat *buf = &dir_ent->inode->buf, buf2; struct file_buffer *file_buffer; - int blocks, byte, count, expected, file, res; + int blocks, file, res; long long bytes, read_size; struct inode_info *inode = dir_ent->inode; @@ -2058,28 +2161,22 @@ inode->read = TRUE; again: bytes = 0; - count = 0; - file_buffer = NULL; read_size = buf->st_size; blocks = (read_size + block_size - 1) >> block_log; file = open(pathname_reader(dir_ent), O_RDONLY); if(file == -1) { - file_buffer = cache_get(reader_buffer, 0, 0); + file_buffer = cache_get_nohash(reader_buffer); file_buffer->sequence = seq ++; goto read_err2; } do { - expected = read_size - ((long long) count * block_size) > - block_size ? block_size : - read_size - ((long long) count * block_size); - - if(file_buffer) - queue_put(from_reader, file_buffer); - file_buffer = cache_get(reader_buffer, 0, 0); + file_buffer = cache_get_nohash(reader_buffer); + file_buffer->file_size = read_size; file_buffer->sequence = seq ++; file_buffer->noD = inode->noD; + file_buffer->error = FALSE; /* * Always try to read block_size bytes from the file rather @@ -2090,29 +2187,28 @@ * case where the file is an exact multiple of the block_size * is dealt with later. */ - byte = file_buffer->size = read_bytes(file, file_buffer->data, + file_buffer->size = read_bytes(file, file_buffer->data, block_size); - - file_buffer->file_size = read_size; - - if(byte == -1) + if(file_buffer->size == -1) goto read_err; - if(byte != expected) - goto restat; + bytes += file_buffer->size; - file_buffer->block = count; - file_buffer->error = FALSE; - file_buffer->fragment = FALSE; + if(blocks > 1) { + /* non-tail block should be exactly block_size */ + if(file_buffer->size < block_size) + goto restat; - bytes += byte; - count ++; - } while(count < blocks); + file_buffer->fragment = FALSE; + put_file_buffer(file_buffer); + } + } while(-- blocks > 0); + /* Overall size including tail should match */ if(read_size != bytes) goto restat; - if(expected == block_size) { + if(read_size && read_size % block_size == 0) { /* * Special case where we've not tried to read past the end of * the file. We expect to get EOF, i.e. the file isn't larger @@ -2130,7 +2226,7 @@ } file_buffer->fragment = is_fragment(inode); - queue_put(from_reader, file_buffer); + put_file_buffer(file_buffer); close(file); @@ -2139,7 +2235,7 @@ restat: res = fstat(file, &buf2); if(res == -1) { - ERROR("Cannot stat dir/file %s because %s, ignoring\n", + ERROR("Cannot stat dir/file %s because %s\n", pathname_reader(dir_ent), strerror(errno)); goto read_err; } @@ -2148,14 +2244,14 @@ close(file); memcpy(buf, &buf2, sizeof(struct stat)); file_buffer->error = 2; - queue_put(from_deflate, file_buffer); + put_file_buffer(file_buffer); goto again; } read_err: close(file); read_err2: file_buffer->error = TRUE; - queue_put(from_deflate, file_buffer); + put_file_buffer(file_buffer); } @@ -2261,6 +2357,7 @@ void *deflator(void *arg) { + struct file_buffer *write_buffer = cache_get_nohash(bwriter_buffer); void *stream = NULL; int res; @@ -2269,20 +2366,12 @@ BAD_ERROR("deflator:: compressor_init failed\n"); while(1) { - struct file_buffer *file_buffer = queue_get(from_reader); - struct file_buffer *write_buffer; + struct file_buffer *file_buffer = queue_get(to_deflate); - if(file_buffer->file_size == 0) { + if(sparse_files && all_zero(file_buffer)) { file_buffer->c_byte = 0; - queue_put(from_deflate, file_buffer); - } else if(sparse_files && all_zero(file_buffer)) { - file_buffer->c_byte = 0; - queue_put(from_deflate, file_buffer); - } else if(file_buffer->fragment) { - file_buffer->c_byte = file_buffer->size; - queue_put(from_deflate, file_buffer); + seq_queue_put(to_main, file_buffer); } else { - write_buffer = cache_get(writer_buffer, 0, 0); write_buffer->c_byte = mangle2(stream, write_buffer->data, file_buffer->data, file_buffer->size, block_size, @@ -2295,7 +2384,8 @@ write_buffer->fragment = FALSE; write_buffer->error = FALSE; cache_block_put(file_buffer); - queue_put(from_deflate, write_buffer); + seq_queue_put(to_main, write_buffer); + write_buffer = cache_get_nohash(bwriter_buffer); } } } @@ -2310,18 +2400,18 @@ if(res) BAD_ERROR("frag_deflator:: compressor_init failed\n"); + pthread_cleanup_push((void *) pthread_mutex_unlock, &fragment_mutex); + while(1) { int c_byte, compressed_size; struct file_buffer *file_buffer = queue_get(to_frag); struct file_buffer *write_buffer = - cache_get(writer_buffer, file_buffer->block + - FRAG_INDEX, 1); + cache_get(fwriter_buffer, file_buffer->block); c_byte = mangle2(stream, write_buffer->data, file_buffer->data, file_buffer->size, block_size, noF, 1); compressed_size = SQUASHFS_COMPRESSED_SIZE_BLOCK(c_byte); write_buffer->size = compressed_size; - pthread_cleanup_push((void *) pthread_mutex_unlock, &fragment_mutex); pthread_mutex_lock(&fragment_mutex); if(fragments_locked == FALSE) { fragment_table[file_buffer->block].size = c_byte; @@ -2330,137 +2420,73 @@ bytes += compressed_size; fragments_outstanding --; queue_put(to_writer, write_buffer); + pthread_mutex_unlock(&fragment_mutex); TRACE("Writing fragment %lld, uncompressed size %d, " "compressed size %d\n", file_buffer->block, file_buffer->size, compressed_size); - } else + } else { add_pending_fragment(write_buffer, c_byte, file_buffer->block); - pthread_cleanup_pop(1); + pthread_mutex_unlock(&fragment_mutex); + } cache_block_put(file_buffer); } -} - -#define HASH_ENTRIES 256 -#define BLOCK_HASH(a) (a % HASH_ENTRIES) -struct file_buffer *block_hash[HASH_ENTRIES]; - -void push_buffer(struct file_buffer *file_buffer) -{ - int hash = BLOCK_HASH(file_buffer->sequence); - - file_buffer->next = block_hash[hash]; - block_hash[hash] = file_buffer; + pthread_cleanup_pop(0); } -struct file_buffer *get_file_buffer(struct queue *queue) +struct file_buffer *get_file_buffer() { - static unsigned int sequence = 0; - int hash = BLOCK_HASH(sequence); - struct file_buffer *file_buffer = block_hash[hash], *prev = NULL; - - for(;file_buffer; prev = file_buffer, file_buffer = file_buffer->next) - if(file_buffer->sequence == sequence) - break; - - if(file_buffer) { - if(prev) - prev->next = file_buffer->next; - else - block_hash[hash] = file_buffer->next; - } else { - while(1) { - file_buffer = queue_get(queue); - if(file_buffer->sequence == sequence) - break; - push_buffer(file_buffer); - } - } - - sequence ++; + struct file_buffer *file_buffer = seq_queue_get(to_main); return file_buffer; } void write_file_empty(squashfs_inode *inode, struct dir_ent *dir_ent, - int *duplicate_file) + struct file_buffer *file_buffer, int *duplicate_file) { file_count ++; *duplicate_file = FALSE; + cache_block_put(file_buffer); create_inode(inode, NULL, dir_ent, SQUASHFS_FILE_TYPE, 0, 0, 0, NULL, &empty_fragment, NULL, 0); } -void write_file_frag_dup(squashfs_inode *inode, struct dir_ent *dir_ent, - int size, int *duplicate_file, struct file_buffer *file_buffer, - unsigned short checksum) -{ - struct file_info *dupl_ptr; - struct fragment *fragment; - unsigned int *block_listp = NULL; - long long start = 0; - - dupl_ptr = duplicate(size, 0, &block_listp, &start, &fragment, - file_buffer, 0, 0, checksum, TRUE); - - if(dupl_ptr) { - *duplicate_file = FALSE; - fragment = get_and_fill_fragment(file_buffer, dir_ent); - dupl_ptr->fragment = fragment; - } else - *duplicate_file = TRUE; - - cache_block_put(file_buffer); - - total_bytes += size; - file_count ++; - - inc_progress_bar(); - - create_inode(inode, NULL, dir_ent, SQUASHFS_FILE_TYPE, size, 0, - 0, NULL, fragment, NULL, 0); -} - - -void write_file_frag(squashfs_inode *inode, struct dir_ent *dir_ent, int size, +void write_file_frag(squashfs_inode *inode, struct dir_ent *dir_ent, struct file_buffer *file_buffer, int *duplicate_file) { + int size = file_buffer->file_size; struct fragment *fragment; - unsigned short checksum; - - checksum = get_checksum_mem_buffer(file_buffer); + unsigned short checksum = file_buffer->checksum; + char dont_put; - if(pre_duplicate_frag(size, checksum)) { - write_file_frag_dup(inode, dir_ent, size, duplicate_file, - file_buffer, checksum); - return; + fragment = frag_duplicate(file_buffer, &dont_put); + *duplicate_file = !fragment; + if(!fragment) { + fragment = get_and_fill_fragment(file_buffer, dir_ent); + if(duplicate_checking) + add_non_dup(size, 0, NULL, 0, fragment, 0, checksum, + TRUE, TRUE); } - - fragment = get_and_fill_fragment(file_buffer, dir_ent); - - cache_block_put(file_buffer); - if(duplicate_checking) - add_non_dup(size, 0, NULL, 0, fragment, 0, checksum, TRUE); + if(dont_put) + free(file_buffer); + else + cache_block_put(file_buffer); total_bytes += size; file_count ++; - *duplicate_file = FALSE; - inc_progress_bar(); create_inode(inode, NULL, dir_ent, SQUASHFS_FILE_TYPE, size, 0, 0, NULL, fragment, NULL, 0); - if(duplicate_checking == FALSE) + if(!duplicate_checking) free_fragment(fragment); - - return; } @@ -2482,7 +2508,7 @@ start = bytes; while (1) { read_size = read_buffer->file_size; - if(read_buffer->fragment && read_buffer->c_byte) + if(read_buffer->fragment) fragment_buffer = read_buffer; else { block_list = realloc(block_list, (block + 1) * @@ -2493,7 +2519,7 @@ if(read_buffer->c_byte) { read_buffer->block = bytes; bytes += read_buffer->size; - cache_rehash(read_buffer, read_buffer->block); + cache_hash(read_buffer, read_buffer->block); file_bytes += read_buffer->size; queue_put(to_writer, read_buffer); } else { @@ -2506,18 +2532,19 @@ if(read_size != -1) break; - read_buffer = get_file_buffer(from_deflate); + read_buffer = get_file_buffer(); if(read_buffer->error) goto read_err; } unlock_fragments(); fragment = get_and_fill_fragment(fragment_buffer, dir_ent); - cache_block_put(fragment_buffer); if(duplicate_checking) add_non_dup(read_size, file_bytes, block_list, start, fragment, - 0, 0, FALSE); + 0, fragment_buffer ? fragment_buffer->checksum : 0, + FALSE, TRUE); + cache_block_put(fragment_buffer); file_count ++; total_bytes += read_size; @@ -2553,42 +2580,57 @@ } -int write_file_blocks(squashfs_inode *inode, struct dir_ent *dir_ent, - long long read_size, struct file_buffer *read_buffer, - int *duplicate_file) +int write_file_blocks_dup(squashfs_inode *inode, struct dir_ent *dir_ent, + struct file_buffer *read_buffer, int *duplicate_file) { - long long file_bytes, start; + int block, thresh; + long long read_size = read_buffer->file_size; + long long file_bytes, dup_start, start; struct fragment *fragment; - unsigned int *block_list; - int block, status; + struct file_info *dupl_ptr; int blocks = (read_size + block_size - 1) >> block_log; - long long sparse = 0; + unsigned int *block_list, *block_listp; + struct file_buffer **buffer_list; + int status; + long long sparse = 0; struct file_buffer *fragment_buffer = NULL; - *duplicate_file = FALSE; - block_list = malloc(blocks * sizeof(unsigned int)); if(block_list == NULL) MEM_ERROR(); + block_listp = block_list; + + buffer_list = malloc(blocks * sizeof(struct file_buffer *)); + if(buffer_list == NULL) + MEM_ERROR(); lock_fragments(); file_bytes = 0; - start = bytes; + start = dup_start = bytes; + thresh = blocks > bwriter_size ? blocks - bwriter_size : 0; + for(block = 0; block < blocks;) { - if(read_buffer->fragment && read_buffer->c_byte) { + if(read_buffer->fragment) { block_list[block] = 0; + buffer_list[block] = NULL; fragment_buffer = read_buffer; blocks = read_size >> block_log; } else { block_list[block] = read_buffer->c_byte; + if(read_buffer->c_byte) { read_buffer->block = bytes; bytes += read_buffer->size; - cache_rehash(read_buffer, read_buffer->block); file_bytes += read_buffer->size; - queue_put(to_writer, read_buffer); + cache_hash(read_buffer, read_buffer->block); + if(block < thresh) { + buffer_list[block] = NULL; + queue_put(to_writer, read_buffer); + } else + buffer_list[block] = read_buffer; } else { + buffer_list[block] = NULL; sparse += read_buffer->size; cache_block_put(read_buffer); } @@ -2596,19 +2638,43 @@ inc_progress_bar(); if(++block < blocks) { - read_buffer = get_file_buffer(from_deflate); + read_buffer = get_file_buffer(); if(read_buffer->error) goto read_err; } } + dupl_ptr = duplicate(read_size, file_bytes, &block_listp, &dup_start, + &fragment, fragment_buffer, blocks, 0, FALSE); + + if(dupl_ptr) { + *duplicate_file = FALSE; + for(block = thresh; block < blocks; block ++) + if(buffer_list[block]) + queue_put(to_writer, buffer_list[block]); + fragment = get_and_fill_fragment(fragment_buffer, dir_ent); + dupl_ptr->fragment = fragment; + } else { + *duplicate_file = TRUE; + for(block = thresh; block < blocks; block ++) + cache_block_put(buffer_list[block]); + bytes = start; + if(thresh && !block_device) { + int res; + + queue_put(to_writer, NULL); + if(queue_get(from_writer) != 0) + EXIT_MKSQUASHFS(); + res = ftruncate(fd, bytes); + if(res != 0) + BAD_ERROR("Failed to truncate dest file because" + " %s\n", strerror(errno)); + } + } + unlock_fragments(); - fragment = get_and_fill_fragment(fragment_buffer, dir_ent); cache_block_put(fragment_buffer); - - if(duplicate_checking) - add_non_dup(read_size, file_bytes, block_list, start, fragment, - 0, 0, FALSE); + free(buffer_list); file_count ++; total_bytes += read_size; @@ -2623,13 +2689,11 @@ if(sparse && (dir_ent->inode->buf.st_blocks << 9) >= read_size) sparse = 0; - create_inode(inode, NULL, dir_ent, SQUASHFS_FILE_TYPE, read_size, start, - blocks, block_list, fragment, NULL, sparse); + create_inode(inode, NULL, dir_ent, SQUASHFS_FILE_TYPE, read_size, + dup_start, blocks, block_listp, fragment, NULL, sparse); - if(duplicate_checking == FALSE) { + if(*duplicate_file == TRUE) free(block_list); - free_fragment(fragment); - } return 0; @@ -2637,7 +2701,7 @@ dec_progress_bar(block); status = read_buffer->error; bytes = start; - if(!block_device) { + if(thresh && !block_device) { int res; queue_put(to_writer, NULL); @@ -2649,64 +2713,54 @@ strerror(errno)); } unlock_fragments(); + for(blocks = thresh; blocks < block; blocks ++) + cache_block_put(buffer_list[blocks]); + free(buffer_list); free(block_list); cache_block_put(read_buffer); return status; } -int write_file_blocks_dup(squashfs_inode *inode, struct dir_ent *dir_ent, - long long read_size, struct file_buffer *read_buffer, - int *duplicate_file) +int write_file_blocks(squashfs_inode *inode, struct dir_ent *dir_ent, + struct file_buffer *read_buffer, int *dup) { - int block, thresh; - long long file_bytes, dup_start, start; + long long read_size = read_buffer->file_size; + long long file_bytes, start; struct fragment *fragment; - struct file_info *dupl_ptr; + unsigned int *block_list; + int block, status; int blocks = (read_size + block_size - 1) >> block_log; - unsigned int *block_list, *block_listp; - struct file_buffer **buffer_list; - int status, num_locked_fragments; long long sparse = 0; struct file_buffer *fragment_buffer = NULL; + if(pre_duplicate(read_size)) + return write_file_blocks_dup(inode, dir_ent, read_buffer, dup); + + *dup = FALSE; + block_list = malloc(blocks * sizeof(unsigned int)); if(block_list == NULL) MEM_ERROR(); - block_listp = block_list; - buffer_list = malloc(blocks * sizeof(struct file_buffer *)); - if(buffer_list == NULL) - MEM_ERROR(); - - num_locked_fragments = lock_fragments(); + lock_fragments(); file_bytes = 0; - start = dup_start = bytes; - thresh = blocks > (writer_buffer_size - num_locked_fragments) ? - blocks - (writer_buffer_size - num_locked_fragments): 0; - + start = bytes; for(block = 0; block < blocks;) { - if(read_buffer->fragment && read_buffer->c_byte) { + if(read_buffer->fragment) { block_list[block] = 0; - buffer_list[block] = NULL; fragment_buffer = read_buffer; blocks = read_size >> block_log; } else { block_list[block] = read_buffer->c_byte; - if(read_buffer->c_byte) { read_buffer->block = bytes; bytes += read_buffer->size; + cache_hash(read_buffer, read_buffer->block); file_bytes += read_buffer->size; - cache_rehash(read_buffer, read_buffer->block); - if(block < thresh) { - buffer_list[block] = NULL; - queue_put(to_writer, read_buffer); - } else - buffer_list[block] = read_buffer; + queue_put(to_writer, read_buffer); } else { - buffer_list[block] = NULL; sparse += read_buffer->size; cache_block_put(read_buffer); } @@ -2714,43 +2768,20 @@ inc_progress_bar(); if(++block < blocks) { - read_buffer = get_file_buffer(from_deflate); + read_buffer = get_file_buffer(); if(read_buffer->error) goto read_err; } } - dupl_ptr = duplicate(read_size, file_bytes, &block_listp, &dup_start, - &fragment, fragment_buffer, blocks, 0, 0, FALSE); - - if(dupl_ptr) { - *duplicate_file = FALSE; - for(block = thresh; block < blocks; block ++) - if(buffer_list[block]) - queue_put(to_writer, buffer_list[block]); - fragment = get_and_fill_fragment(fragment_buffer, dir_ent); - dupl_ptr->fragment = fragment; - } else { - *duplicate_file = TRUE; - for(block = thresh; block < blocks; block ++) - cache_block_put(buffer_list[block]); - bytes = start; - if(thresh && !block_device) { - int res; - - queue_put(to_writer, NULL); - if(queue_get(from_writer) != 0) - EXIT_MKSQUASHFS(); - res = ftruncate(fd, bytes); - if(res != 0) - BAD_ERROR("Failed to truncate dest file because" - " %s\n", strerror(errno)); - } - } - unlock_fragments(); + fragment = get_and_fill_fragment(fragment_buffer, dir_ent); + + if(duplicate_checking) + add_non_dup(read_size, file_bytes, block_list, start, fragment, + 0, fragment_buffer ? fragment_buffer->checksum : 0, + FALSE, TRUE); cache_block_put(fragment_buffer); - free(buffer_list); file_count ++; total_bytes += read_size; @@ -2765,11 +2796,13 @@ if(sparse && (dir_ent->inode->buf.st_blocks << 9) >= read_size) sparse = 0; - create_inode(inode, NULL, dir_ent, SQUASHFS_FILE_TYPE, read_size, - dup_start, blocks, block_listp, fragment, NULL, sparse); + create_inode(inode, NULL, dir_ent, SQUASHFS_FILE_TYPE, read_size, start, + blocks, block_list, fragment, NULL, sparse); - if(*duplicate_file == TRUE) + if(duplicate_checking == FALSE) { free(block_list); + free_fragment(fragment); + } return 0; @@ -2777,7 +2810,7 @@ dec_progress_bar(block); status = read_buffer->error; bytes = start; - if(thresh && !block_device) { + if(!block_device) { int res; queue_put(to_writer, NULL); @@ -2789,58 +2822,40 @@ strerror(errno)); } unlock_fragments(); - for(blocks = thresh; blocks < block; blocks ++) - cache_block_put(buffer_list[blocks]); - free(buffer_list); free(block_list); cache_block_put(read_buffer); return status; } -void write_file(squashfs_inode *inode, struct dir_ent *dir_ent, - int *duplicate_file) +void write_file(squashfs_inode *inode, struct dir_ent *dir, int *dup) { int status; struct file_buffer *read_buffer; - long long read_size; again: - read_buffer = get_file_buffer(from_deflate); - + read_buffer = get_file_buffer(); status = read_buffer->error; - if(status) { - cache_block_put(read_buffer); - goto file_err; - } - - read_size = read_buffer->file_size; - if(read_size == -1) - status = write_file_process(inode, dir_ent, read_buffer, - duplicate_file); - else if(read_size == 0) { - write_file_empty(inode, dir_ent, duplicate_file); + if(status) cache_block_put(read_buffer); - } else if(read_buffer->fragment && read_buffer->c_byte) - write_file_frag(inode, dir_ent, read_size, read_buffer, - duplicate_file); - else if(pre_duplicate(read_size)) - status = write_file_blocks_dup(inode, dir_ent, read_size, - read_buffer, duplicate_file); + else if(read_buffer->file_size == -1) + status = write_file_process(inode, dir, read_buffer, dup); + else if(read_buffer->file_size == 0) + write_file_empty(inode, dir, read_buffer, dup); + else if(read_buffer->fragment && read_buffer->c_byte) + write_file_frag(inode, dir, read_buffer, dup); else - status = write_file_blocks(inode, dir_ent, read_size, - read_buffer, duplicate_file); + status = write_file_blocks(inode, dir, read_buffer, dup); -file_err: if(status == 2) { ERROR("File %s changed size while reading filesystem, " - "attempting to re-read\n", pathname(dir_ent)); + "attempting to re-read\n", pathname(dir)); goto again; } else if(status == 1) { - ERROR("Failed to read file %s, creating empty file\n", - pathname(dir_ent)); - write_file_empty(inode, dir_ent, duplicate_file); + ERROR_START("Failed to read file %s", pathname(dir)); + ERROR_EXIT(", creating empty file\n"); + write_file_empty(inode, dir, NULL, dup); } } @@ -2921,7 +2936,8 @@ } -struct inode_info *lookup_inode2(struct stat *buf, int pseudo, int id) +struct inode_info *lookup_inode3(struct stat *buf, int pseudo, int id, + char *symlink, int bytes) { int ino_hash = INODE_HASH(buf->st_dev, buf->st_ino); struct inode_info *inode; @@ -2941,10 +2957,12 @@ } } - inode = malloc(sizeof(struct inode_info)); + inode = malloc(sizeof(struct inode_info) + bytes); if(inode == NULL) MEM_ERROR(); + if(bytes) + memcpy(&inode->symlink, symlink, bytes); memcpy(&inode->buf, buf, sizeof(struct stat)); inode->read = FALSE; inode->root_entry = FALSE; @@ -2965,9 +2983,6 @@ inode->noD = noD; inode->noF = noF; - if((buf->st_mode & S_IFMT) == S_IFREG) - progress_bar_size((buf->st_size + block_size - 1) >> block_log); - inode->next = inode_info[ino_hash]; inode_info[ino_hash] = inode; @@ -2975,6 +2990,12 @@ } +struct inode_info *lookup_inode2(struct stat *buf, int pseudo, int id) +{ + return lookup_inode3(buf, pseudo, id, NULL, 0); +} + + inline struct inode_info *lookup_inode(struct stat *buf) { return lookup_inode2(buf, 0, 0); @@ -2983,8 +3004,12 @@ inline void alloc_inode_no(struct inode_info *inode, unsigned int use_this) { - if (inode->inode_number == 0) + if (inode->inode_number == 0) { inode->inode_number = use_this ? : inode_no ++; + if((inode->buf.st_mode & S_IFMT) == S_IFREG) + progress_bar_size((inode->buf.st_size + block_size - 1) + >> block_log); + } } @@ -2999,6 +3024,7 @@ dir_ent->source_name = source_name; dir_ent->nonstandard_pathname = nonstandard_pathname; dir_ent->our_dir = dir; + dir_ent->inode = NULL; dir_ent->next = NULL; return dir_ent; @@ -3041,6 +3067,15 @@ if(dir_ent->source_name) free(dir_ent->source_name); + if(dir_ent->nonstandard_pathname) + free(dir_ent->nonstandard_pathname); + + /* if this entry has been associated with an inode, then we need + * to update the inode nlink count. Orphaned inodes are harmless, and + * is easier to leave them than go to the bother of deleting them */ + if(dir_ent->inode && !dir_ent->inode->root_entry) + dir_ent->inode->nlink --; + free(dir_ent); } @@ -3051,42 +3086,18 @@ } - void dir_scan(squashfs_inode *inode, char *pathname, - struct dir_ent *(_readdir)(struct dir_info *)) + struct dir_ent *(_readdir)(struct dir_info *), int progress) { struct stat buf; - struct dir_info *dir_info = dir_scan1(pathname, "", paths, _readdir, 1); struct dir_ent *dir_ent; - if(dir_info == NULL) + root_dir = dir_scan1(pathname, "", paths, _readdir, 1); + if(root_dir == NULL) return; - /* - * Process most actions and any pseudo files - */ - if(actions() || get_pseudo()) - dir_scan2(dir_info, get_pseudo()); - - /* - * Process move actions - */ - if(move_actions()) { - dir_scan3(dir_info, dir_info); - do_move_actions(); - } - - /* - * Process empty actions - */ - if(empty_actions()) - dir_scan4(dir_info); - - /* - * Sort directories and compute the inode numbers - */ - dir_scan5(dir_info); - + /* Create root directory dir_ent and associated inode, and connect + * it to the root directory dir_info structure */ dir_ent = create_dir_entry("", NULL, pathname, scan1_opendir("", "", 0)); @@ -3111,21 +3122,69 @@ dir_ent->inode = lookup_inode(&buf); } + dir_ent->dir = root_dir; + root_dir->dir_ent = dir_ent; + + /* + * Process most actions and any pseudo files + */ + if(actions() || get_pseudo()) + dir_scan2(root_dir, get_pseudo()); + + /* + * Process move actions + */ + if(move_actions()) { + dir_scan3(root_dir); + do_move_actions(); + } + + /* + * Process prune actions + */ + if(prune_actions()) + dir_scan4(root_dir); + + /* + * Process empty actions + */ + if(empty_actions()) + dir_scan5(root_dir); + + /* + * Sort directories and compute the inode numbers + */ + dir_scan6(root_dir); + alloc_inode_no(dir_ent->inode, root_inode_number); - dir_ent->dir = dir_info; - dir_info->dir_ent = dir_ent; - eval_actions(dir_ent); + eval_actions(root_dir, dir_ent); if(sorted) - generate_file_priorities(dir_info, 0, - &dir_info->dir_ent->inode->buf); - queue_put(to_reader, dir_info); + generate_file_priorities(root_dir, 0, + &root_dir->dir_ent->inode->buf); + + if(appending) { + sigset_t sigmask; + + restore_thread = init_restore_thread(); + sigemptyset(&sigmask); + sigaddset(&sigmask, SIGINT); + sigaddset(&sigmask, SIGTERM); + sigaddset(&sigmask, SIGUSR1); + if(pthread_sigmask(SIG_BLOCK, &sigmask, NULL) == -1) + BAD_ERROR("Failed to set signal mask\n"); + write_destination(fd, SQUASHFS_START, 4, "\0\0\0\0"); + } + + queue_put(to_reader, root_dir); + + set_progressbar_state(progress); + if(sorted) - sort_files_and_write(dir_info); - if(progress) - enable_progress_bar(); - dir_scan6(inode, dir_info); + sort_files_and_write(root_dir); + + dir_scan7(inode, root_dir); dir_ent->inode->inode = *inode; dir_ent->inode->type = SQUASHFS_DIR_TYPE; } @@ -3187,8 +3246,9 @@ int pass = 1, res; if(dir_name == NULL) { - ERROR("Bad source directory %s - skipping ...\n", + ERROR_START("Bad source directory %s", source_path[index]); + ERROR_EXIT(" - skipping ...\n"); index ++; continue; } @@ -3213,7 +3273,7 @@ ERROR("%s\n", dir_name); } return create_dir_entry(dir_name, basename, - source_path[index ++], dir); + strdup(source_path[index ++]), dir); } return NULL; } @@ -3289,7 +3349,8 @@ struct dir_ent *dir_ent; if(dir == NULL) { - ERROR("Could not open %s, skipping...\n", filename); + ERROR_START("Could not open %s", filename); + ERROR_EXIT(", skipping...\n"); return NULL; } @@ -3307,8 +3368,9 @@ } if(lstat(filename, &buf) == -1) { - ERROR("Cannot stat dir/file %s because %s, ignoring\n", + ERROR_START("Cannot stat dir/file %s because %s", filename, strerror(errno)); + ERROR_EXIT(", ignoring\n"); free_dir_entry(dir_ent); continue; } @@ -3320,8 +3382,9 @@ (buf.st_mode & S_IFMT) != S_IFBLK && (buf.st_mode & S_IFMT) != S_IFIFO && (buf.st_mode & S_IFMT) != S_IFSOCK) { - ERROR("File %s has unrecognised filetype %d, ignoring" - "\n", filename, buf.st_mode & S_IFMT); + ERROR_START("File %s has unrecognised filetype %d", + filename, buf.st_mode & S_IFMT); + ERROR_EXIT(", ignoring\n"); free_dir_entry(dir_ent); continue; } @@ -3337,30 +3400,53 @@ subpath = subpathname(dir_ent); if(eval_exclude_actions(dir_name, filename, subpath, - &buf, depth)) { + &buf, depth, dir_ent)) { add_excluded(dir); free_dir_entry(dir_ent); continue; } } - if((buf.st_mode & S_IFMT) == S_IFDIR) { + switch(buf.st_mode & S_IFMT) { + case S_IFDIR: if(subpath == NULL) subpath = subpathname(dir_ent); sub_dir = dir_scan1(filename, subpath, new, scan1_readdir, depth + 1); - if(sub_dir == NULL) { + if(sub_dir) { + dir->directory_count ++; + add_dir_entry(dir_ent, sub_dir, + lookup_inode(&buf)); + } else free_dir_entry(dir_ent); - free(new); - continue; + break; + case S_IFLNK: { + int byte; + static char buff[65536]; /* overflow safe */ + + byte = readlink(filename, buff, 65536); + if(byte == -1) { + ERROR_START("Failed to read symlink %s", + filename); + ERROR_EXIT(", ignoring\n"); + } else if(byte == 65536) { + ERROR_START("Symlink %s is greater than 65536 " + "bytes!", filename); + ERROR_EXIT(", ignoring\n"); + } else { + /* readlink doesn't 0 terminate the returned + * path */ + buff[byte] = '\0'; + add_dir_entry(dir_ent, NULL, lookup_inode3(&buf, + 0, 0, buff, byte + 1)); } + break; + } + default: + add_dir_entry(dir_ent, NULL, lookup_inode(&buf)); + } - dir->directory_count ++; - } else - sub_dir = NULL; - - add_dir_entry(dir_ent, sub_dir, lookup_inode(&buf)); free(new); } @@ -3410,7 +3496,7 @@ struct stat *buf = &inode_info->buf; char *name = dir_ent->name; - eval_actions(dir_ent); + eval_actions(root_dir, dir_ent); if((buf->st_mode & S_IFMT) == S_IFDIR) dir_scan2(dir_ent->dir, pseudo_subdir(name, pseudo)); @@ -3421,16 +3507,18 @@ if(pseudo_ent->dev->type == 'm') { struct stat *buf; if(dir_ent == NULL) { - ERROR("Pseudo modify file \"%s\" does not exist " - "in source filesystem. Ignoring.\n", + ERROR_START("Pseudo modify file \"%s\" does " + "not exist in source filesystem.", pseudo_ent->pathname); + ERROR_EXIT(" Ignoring.\n"); continue; } if(dir_ent->inode->root_entry) { - ERROR("Pseudo modify file \"%s\" is a pre-existing" - " file in the filesystem being appended" - " to. It cannot be modified. " - "Ignoring.\n", pseudo_ent->pathname); + ERROR_START("Pseudo modify file \"%s\" is a " + "pre-existing file in the filesystem " + "being appended to. It cannot be "\ + "modified.", pseudo_ent->pathname); + ERROR_EXIT(" Ignoring.\n"); continue; } buf = &dir_ent->inode->buf; @@ -3442,17 +3530,20 @@ } if(dir_ent) { - if(dir_ent->inode->root_entry) - ERROR("Pseudo file \"%s\" is a pre-existing" - " file in the filesystem being appended" - " to. Ignoring.\n", + if(dir_ent->inode->root_entry) { + ERROR_START("Pseudo file \"%s\" is a " + "pre-existing file in the filesystem " + "being appended to.", pseudo_ent->pathname); - else - ERROR("Pseudo file \"%s\" exists in source " - "filesystem \"%s\".\nIgnoring, " - "exclude it (-e/-ef) to override.\n", + ERROR_EXIT(" Ignoring.\n"); + } else { + ERROR_START("Pseudo file \"%s\" exists in " + "source filesystem \"%s\".", pseudo_ent->pathname, pathname(dir_ent)); + ERROR_EXIT("\nIgnoring, exclude it (-e/-ef) to " + "override.\n"); + } continue; } @@ -3473,9 +3564,9 @@ struct dir_info *sub_dir = scan1_opendir("", subpath, dir->depth + 1); if(sub_dir == NULL) { - ERROR("Could not create pseudo directory \"%s\"" - ", skipping...\n", - pseudo_ent->pathname); + ERROR_START("Could not create pseudo directory " + "\"%s\"", pseudo_ent->pathname); + ERROR_EXIT(", skipping...\n"); free(subpath); pseudo_ino --; continue; @@ -3485,25 +3576,10 @@ add_dir_entry(dir_ent, sub_dir, lookup_inode2(&buf, PSEUDO_FILE_OTHER, 0)); } else if(pseudo_ent->dev->type == 'f') { -#ifdef USE_TMP_FILE - struct stat buf2; - int res = stat(pseudo_ent->dev->filename, &buf2); - if(res == -1) { - ERROR("Stat on pseudo file \"%s\" failed, " - "skipping...\n", pseudo_ent->pathname); - pseudo_ino --; - continue; - } - buf.st_size = buf2.st_size; - add_dir_entry2(pseudo_ent->name, NULL, - pseudo_ent->dev->filename, NULL, - lookup_inode2(&buf, PSEUDO_FILE_OTHER, 0), dir); -#else add_dir_entry2(pseudo_ent->name, NULL, pseudo_ent->pathname, NULL, lookup_inode2(&buf, PSEUDO_FILE_PROCESS, pseudo_ent->dev->pseudo_id), dir); -#endif } else { add_dir_entry2(pseudo_ent->name, NULL, pseudo_ent->pathname, NULL, @@ -3517,35 +3593,113 @@ * dir_scan3 routines... * This processes the move action */ -void dir_scan3(struct dir_info *root, struct dir_info *dir) +void dir_scan3(struct dir_info *dir) { struct dir_ent *dir_ent = NULL; while((dir_ent = scan2_readdir(dir, dir_ent)) != NULL) { - eval_move_actions(root, dir_ent); + eval_move_actions(root_dir, dir_ent); if((dir_ent->inode->buf.st_mode & S_IFMT) == S_IFDIR) - dir_scan3(root, dir_ent->dir); + dir_scan3(dir_ent->dir); } } /* * dir_scan4 routines... + * This processes the prune action. This action is designed to do fine + * grained tuning of the in-core directory structure after the exclude, + * move and pseudo actions have been performed. This allows complex + * tests to be performed which are impossible at exclude time (i.e. + * tests which rely on the in-core directory structure) + */ +void free_dir(struct dir_info *dir) +{ + struct dir_ent *dir_ent = dir->list; + + while(dir_ent) { + struct dir_ent *tmp = dir_ent; + + if((dir_ent->inode->buf.st_mode & S_IFMT) == S_IFDIR) + free_dir(dir_ent->dir); + + dir_ent = dir_ent->next; + free_dir_entry(tmp); + } + + free(dir->pathname); + free(dir->subpath); + free(dir); +} + + +void dir_scan4(struct dir_info *dir) +{ + struct dir_ent *dir_ent = dir->list, *prev = NULL; + + while(dir_ent) { + if(dir_ent->inode->root_entry) { + prev = dir_ent; + dir_ent = dir_ent->next; + continue; + } + + if((dir_ent->inode->buf.st_mode & S_IFMT) == S_IFDIR) + dir_scan4(dir_ent->dir); + + if(eval_prune_actions(root_dir, dir_ent)) { + struct dir_ent *tmp = dir_ent; + + if((dir_ent->inode->buf.st_mode & S_IFMT) == S_IFDIR) { + free_dir(dir_ent->dir); + dir->directory_count --; + } + + dir->count --; + + /* remove dir_ent from list */ + dir_ent = dir_ent->next; + if(prev) + prev->next = dir_ent; + else + dir->list = dir_ent; + + /* free it */ + free_dir_entry(tmp); + + add_excluded(dir); + continue; + } + + prev = dir_ent; + dir_ent = dir_ent->next; + } +} + + +/* + * dir_scan5 routines... * This processes the empty action. This action has to be processed after * all other actions because the previous exclude and move actions and the * pseudo actions affect whether a directory is empty */ -void dir_scan4(struct dir_info *dir) +void dir_scan5(struct dir_info *dir) { struct dir_ent *dir_ent = dir->list, *prev = NULL; while(dir_ent) { + if(dir_ent->inode->root_entry) { + prev = dir_ent; + dir_ent = dir_ent->next; + continue; + } + if((dir_ent->inode->buf.st_mode & S_IFMT) == S_IFDIR) { - dir_scan4(dir_ent->dir); + dir_scan5(dir_ent->dir); - if(eval_empty_actions(dir_ent)) { + if(eval_empty_actions(root_dir, dir_ent)) { struct dir_ent *tmp = dir_ent; /* @@ -3581,7 +3735,7 @@ /* - * dir_scan5 routines... + * dir_scan6 routines... * This sorts every directory and computes the inode numbers */ @@ -3599,7 +3753,7 @@ struct dir_ent *cur, *l1, *l2, *next; int len1, len2, stride = 1; - if(dir->count < 2) + if(dir->list == NULL || dir->count < 2) return; /* @@ -3674,7 +3828,7 @@ } -void dir_scan5(struct dir_info *dir) +void dir_scan6(struct dir_info *dir) { struct dir_ent *dir_ent; unsigned int byte_count = 0; @@ -3691,7 +3845,7 @@ alloc_inode_no(dir_ent->inode, 0); if((dir_ent->inode->buf.st_mode & S_IFMT) == S_IFDIR) - dir_scan5(dir_ent->dir); + dir_scan6(dir_ent->dir); } if((dir->count < 257 && byte_count < SQUASHFS_METADATA_SIZE)) @@ -3703,7 +3857,7 @@ * dir_scan6 routines... * This generates the filesystem metadata and writes it out to the destination */ -void scan6_init_dir(struct directory *dir) +void scan7_init_dir(struct directory *dir) { dir->buff = malloc(SQUASHFS_METADATA_SIZE); if(dir->buff == NULL) @@ -3718,7 +3872,7 @@ } -struct dir_ent *scan6_readdir(struct directory *dir, struct dir_info *dir_info, +struct dir_ent *scan7_readdir(struct directory *dir, struct dir_info *dir_info, struct dir_ent *dir_ent) { if (dir_ent == NULL) @@ -3734,7 +3888,7 @@ } -void scan6_freedir(struct directory *dir) +void scan7_freedir(struct directory *dir) { if(dir->index) free(dir->index); @@ -3742,16 +3896,16 @@ } -void dir_scan6(squashfs_inode *inode, struct dir_info *dir_info) +void dir_scan7(squashfs_inode *inode, struct dir_info *dir_info) { int squashfs_type; int duplicate_file; struct directory dir; struct dir_ent *dir_ent = NULL; - scan6_init_dir(&dir); + scan7_init_dir(&dir); - while((dir_ent = scan6_readdir(&dir, dir_info, dir_ent)) != NULL) { + while((dir_ent = scan7_readdir(&dir, dir_info, dir_ent)) != NULL) { struct stat *buf = &dir_ent->inode->buf; update_info(dir_ent); @@ -3772,7 +3926,7 @@ case S_IFDIR: squashfs_type = SQUASHFS_DIR_TYPE; - dir_scan6(inode, dir_ent->dir); + dir_scan7(inode, dir_ent->dir); break; case S_IFLNK: @@ -3883,7 +4037,7 @@ INFO("directory %s inode 0x%llx\n", subpathname(dir_info->dir_ent), *inode); - scan6_freedir(&dir); + scan7_freedir(&dir); } @@ -3928,8 +4082,9 @@ if(path[0] == '/' || strncmp(path, "./", 2) == 0 || strncmp(path, "../", 3) == 0) { if(lstat(path, &buf) == -1) { - ERROR("Cannot stat exclude dir/file %s because %s, " - "ignoring\n", path, strerror(errno)); + ERROR_START("Cannot stat exclude dir/file %s because " + "%s", path, strerror(errno)); + ERROR_EXIT(", ignoring\n"); return TRUE; } ADD_ENTRY(buf); @@ -3941,10 +4096,11 @@ if(res == -1) BAD_ERROR("asprintf failed in old_add_exclude\n"); if(lstat(filename, &buf) == -1) { - if(!(errno == ENOENT || errno == ENOTDIR)) - ERROR("Cannot stat exclude dir/file %s because " - "%s, ignoring\n", filename, - strerror(errno)); + if(!(errno == ENOENT || errno == ENOTDIR)) { + ERROR_START("Cannot stat exclude dir/file %s " + "because %s", filename, strerror(errno)); + ERROR_EXIT(", ignoring\n"); + } free(filename); continue; } @@ -3971,39 +4127,50 @@ } -void initialise_threads(int readb_mbytes, int writeb_mbytes, - int fragmentb_mbytes) +void initialise_threads(int readq, int fragq, int bwriteq, int fwriteq, + int freelst, char *destination_file) { int i; sigset_t sigmask, old_mask; - int reader_buffer_size; - int fragment_buffer_size; + int total_mem = readq; + int reader_size; + int fragment_size; + int fwriter_size; /* - * writer_buffer_size is global because it is needed in + * bwriter_size is global because it is needed in * write_file_blocks_dup() */ /* + * Never allow the total size of the queues to be larger than + * physical memory + * + * When adding together the possibly user supplied values, make + * sure they've not been deliberately contrived to overflow an int + */ + if(add_overflow(total_mem, fragq)) + BAD_ERROR("Queue sizes rediculously too large\n"); + total_mem += fragq; + if(add_overflow(total_mem, bwriteq)) + BAD_ERROR("Queue sizes rediculously too large\n"); + total_mem += bwriteq; + if(add_overflow(total_mem, fwriteq)) + BAD_ERROR("Queue sizes rediculously too large\n"); + total_mem += fwriteq; + + check_usable_phys_mem(total_mem); + + /* * convert from queue size in Mbytes to queue size in * blocks. * - * In doing so, check that the user supplied values do not - * overflow a signed int + * This isn't going to overflow an int unless there exists + * systems with more than 8 Petabytes of RAM! */ - if(shift_overflow(readb_mbytes, 20 - block_log)) - BAD_ERROR("Read queue is too large\n"); - else - reader_buffer_size = readb_mbytes << (20 - block_log); - - if(shift_overflow(fragmentb_mbytes, 20 - block_log)) - BAD_ERROR("Fragment queue is too large\n"); - else - fragment_buffer_size = fragmentb_mbytes << (20 - block_log); - - if(shift_overflow(writeb_mbytes, 20 - block_log)) - BAD_ERROR("Write queue is too large\n"); - else - writer_buffer_size = writeb_mbytes << (20 - block_log); + reader_size = readq << (20 - block_log); + fragment_size = fragq << (20 - block_log); + bwriter_size = bwriteq << (20 - block_log); + fwriter_size = fwriteq << (20 - block_log); /* * setup signal handlers for the main thread, these cleanup @@ -4020,10 +4187,11 @@ signal(SIGINT, sighandler); signal(SIGUSR1, sighandler); - /* block SIGQUIT this is handled by the info thread */ + /* block SIGQUIT and SIGHUP, these are handled by the info thread */ sigemptyset(&sigmask); sigaddset(&sigmask, SIGQUIT); - if(pthread_sigmask(SIG_BLOCK, &sigmask, &old_mask) == -1) + sigaddset(&sigmask, SIGHUP); + if(pthread_sigmask(SIG_BLOCK, &sigmask, NULL) == -1) BAD_ERROR("Failed to set signal mask in intialise_threads\n"); /* @@ -4050,8 +4218,9 @@ #endif if(sysctl(mib, 2, &processors, &len, NULL, 0) == -1) { - ERROR("Failed to get number of available processors. " - "Defaulting to 1\n"); + ERROR_START("Failed to get number of available " + "processors."); + ERROR_EXIT(" Defaulting to 1\n"); processors = 1; } #else @@ -4059,44 +4228,48 @@ #endif } - if(multiply_overflow(processors, 2) || - add_overflow(processors * 2, 2) || - multiply_overflow(processors * 2 + 2, - sizeof(pthread_t))) + if(multiply_overflow(processors, 3) || + multiply_overflow(processors * 3, sizeof(pthread_t))) BAD_ERROR("Processors too large\n"); - thread = malloc((2 + processors * 2) * sizeof(pthread_t)); - if(thread == NULL) + deflator_thread = malloc(processors * 3 * sizeof(pthread_t)); + if(deflator_thread == NULL) MEM_ERROR(); - deflator_thread = &thread[2]; frag_deflator_thread = &deflator_thread[processors]; + frag_thread = &frag_deflator_thread[processors]; to_reader = queue_init(1); - from_reader = queue_init(reader_buffer_size); - to_writer = queue_init(writer_buffer_size); + to_deflate = queue_init(reader_size); + to_process_frag = queue_init(reader_size); + to_writer = queue_init(bwriter_size + fwriter_size); from_writer = queue_init(1); - from_deflate = queue_init(reader_buffer_size); - to_frag = queue_init(fragment_buffer_size); - reader_buffer = cache_init(block_size, reader_buffer_size); - writer_buffer = cache_init(block_size, writer_buffer_size); - fragment_buffer = cache_init(block_size, fragment_buffer_size); - pthread_create(&thread[0], NULL, reader, NULL); - pthread_create(&thread[1], NULL, writer, NULL); + to_frag = queue_init(fragment_size); + locked_fragment = queue_init(fragment_size); + to_main = seq_queue_init(); + reader_buffer = cache_init(block_size, reader_size, 0, 0); + bwriter_buffer = cache_init(block_size, bwriter_size, 1, freelst); + fwriter_buffer = cache_init(block_size, fwriter_size, 1, freelst); + fragment_buffer = cache_init(block_size, fragment_size, 1, 0); + reserve_cache = cache_init(block_size, processors + 1, 1, 0); + pthread_create(&reader_thread, NULL, reader, NULL); + pthread_create(&writer_thread, NULL, writer, NULL); init_progress_bar(); init_info(); - pthread_mutex_init(&fragment_mutex, NULL); - pthread_cond_init(&fragment_waiting, NULL); for(i = 0; i < processors; i++) { - if(pthread_create(&deflator_thread[i], NULL, deflator, NULL) != - 0) + if(pthread_create(&deflator_thread[i], NULL, deflator, NULL)) BAD_ERROR("Failed to create thread\n"); if(pthread_create(&frag_deflator_thread[i], NULL, frag_deflator, NULL) != 0) BAD_ERROR("Failed to create thread\n"); + if(pthread_create(&frag_thread[i], NULL, frag_thrd, + (void *) destination_file) != 0) + BAD_ERROR("Failed to create thread\n"); } + main_thread = pthread_self(); + printf("Parallel mksquashfs: Using %d processor%s\n", processors, processors == 1 ? "" : "s"); @@ -4120,12 +4293,21 @@ inode_lookup_table = it; for(i = 0; i < INODE_HASH_SIZE; i ++) { - struct inode_info *inode = inode_info[i]; + struct inode_info *inode; for(inode = inode_info[i]; inode; inode = inode->next) { inode_number = get_inode_no(inode); + /* The empty action will produce orphaned inode + * entries in the inode_info[] table. These + * entries because they are orphaned will not be + * allocated an inode number in dir_scan5(), so + * skip any entries with the default dummy inode + * number of 0 */ + if(inode_number == 0) + continue; + SQUASHFS_SWAP_LONG_LONGS(&inode->inode, &inode_lookup_table[inode_number - 1], 1); @@ -4146,11 +4328,14 @@ target ++; start = target; - while(*target != '/' && *target!= '\0') + while(*target != '/' && *target != '\0') target ++; *targname = strndup(start, target - start); + while(*target == '/') + target ++; + return target; } @@ -4550,91 +4735,23 @@ MEM_ERROR(); res = read_bytes(recoverfd, metadata, bytes); - if(res == -1) - BAD_ERROR("Failed to read recovery file, because %s\n", - strerror(errno)); - if(res < bytes) - BAD_ERROR("Recovery file appears to be truncated\n"); - - write_destination(fd, 0, sizeof(struct squashfs_super_block), &sBlk); - - write_destination(fd, sBlk.inode_table_start, bytes, metadata); - - close(recoverfd); - close(fd); - - printf("Successfully wrote recovery file \"%s\". Exiting\n", - recovery_file); - - exit(0); -} - - -int parse_number(char *start, int *res, int size) -{ - char *end; - long number = strtol(start, &end, 10); - - /* - * check for strtol underflow or overflow in conversion. - * Note: strtol can validly return LONG_MIN and LONG_MAX - * if the user entered these values, but, additional code - * to distinguish this scenario is unnecessary, because for - * our purposes LONG_MIN and LONG_MAX are too large anyway - */ - if(number == LONG_MIN || number == LONG_MAX) - return 0; - - /* reject negative numbers as invalid */ - if(number < 0) - return 0; - - /* check if long result will overflow signed int */ - if(number > INT_MAX) - return 0; - - if(size) { - /* - * Check for multiplier and trailing junk. - * But first check that a number exists before the - * multiplier - */ - if(end == start) - return 0; - - switch(end[0]) { - case 'm': - case 'M': - if(multiply_overflow((int) number, 1048576)) - return 0; - number *= 1048576; + if(res == -1) + BAD_ERROR("Failed to read recovery file, because %s\n", + strerror(errno)); + if(res < bytes) + BAD_ERROR("Recovery file appears to be truncated\n"); - if(end[1] != '\0') - /* trailing junk after number */ - return 0; + write_destination(fd, 0, sizeof(struct squashfs_super_block), &sBlk); - break; - case 'k': - case 'K': - if(multiply_overflow((int) number, 1024)) - return 0; - number *= 1024; + write_destination(fd, sBlk.inode_table_start, bytes, metadata); - if(end[1] != '\0') - /* trailing junk after number */ - return 0; - case '\0': - break; - default: - /* trailing junk after number */ - return 0; - } - } else if(end[0] != '\0') - /* trailing junk after number */ - return 0; + close(recoverfd); + close(fd); - *res = number; - return 1; + printf("Successfully wrote recovery file \"%s\". Exiting\n", + recovery_file); + + exit(0); } @@ -4674,8 +4791,6 @@ close(fd); - delete_pseudo_files(); - if(recovery_file) unlink(recovery_file); @@ -4750,15 +4865,233 @@ } +int parse_numberll(char *start, long long *res, int size) +{ + char *end; + long long number; + + errno = 0; /* To distinguish success/failure after call */ + + number = strtoll(start, &end, 10); + + /* + * check for strtoll underflow or overflow in conversion, and other + * errors. + */ + if((errno == ERANGE && (number == LLONG_MIN || number == LLONG_MAX)) || + (errno != 0 && number == 0)) + return 0; + + /* reject negative numbers as invalid */ + if(number < 0) + return 0; + + if(size) { + /* + * Check for multiplier and trailing junk. + * But first check that a number exists before the + * multiplier + */ + if(end == start) + return 0; + + switch(end[0]) { + case 'g': + case 'G': + if(multiply_overflowll(number, 1073741824)) + return 0; + number *= 1073741824; + + if(end[1] != '\0') + /* trailing junk after multiplier, but + * allow it to be "bytes" */ + if(strcmp(end + 1, "bytes")) + return 0; + + break; + case 'm': + case 'M': + if(multiply_overflowll(number, 1048576)) + return 0; + number *= 1048576; + + if(end[1] != '\0') + /* trailing junk after multiplier, but + * allow it to be "bytes" */ + if(strcmp(end + 1, "bytes")) + return 0; + + break; + case 'k': + case 'K': + if(multiply_overflowll(number, 1024)) + return 0; + number *= 1024; + + if(end[1] != '\0') + /* trailing junk after multiplier, but + * allow it to be "bytes" */ + if(strcmp(end + 1, "bytes")) + return 0; + + break; + case '\0': + break; + default: + /* trailing junk after number */ + return 0; + } + } else if(end[0] != '\0') + /* trailing junk after number */ + return 0; + + *res = number; + return 1; +} + + +int parse_number(char *start, int *res, int size) +{ + long long number; + + if(!parse_numberll(start, &number, size)) + return 0; + + /* check if long result will overflow signed int */ + if(number > INT_MAX) + return 0; + + *res = (int) number; + return 1; +} + + int parse_num(char *arg, int *res) { return parse_number(arg, res, 0); } +int get_physical_memory() +{ + /* + * Long longs are used here because with PAE, a 32-bit + * machine can have more than 4GB of physical memory + * + * sysconf(_SC_PHYS_PAGES) relies on /proc being mounted. + * If it isn't fail. + */ + long long num_pages = sysconf(_SC_PHYS_PAGES); + long long page_size = sysconf(_SC_PAGESIZE); + int phys_mem = num_pages * page_size >> 20; + + if(num_pages == -1 || page_size == -1) + return 0; + + if(phys_mem < SQUASHFS_LOWMEM) + BAD_ERROR("Mksquashfs requires more physical memory than is " + "available!\n"); + + return phys_mem; +} + + +void check_usable_phys_mem(int total_mem) +{ + /* + * We want to allow users to use as much of their physical + * memory as they wish. However, for practical reasons there are + * limits which need to be imposed, to protect users from themselves + * and to prevent people from using Mksquashfs as a DOS attack by using + * all physical memory. Mksquashfs uses memory to cache data from disk + * to optimise performance. It is pointless to ask it to use more + * than 75% of physical memory, as this causes thrashing and it is thus + * self-defeating. + */ + int mem = get_physical_memory(); + + mem = (mem >> 1) + (mem >> 2); /* 75% */ + + if(total_mem > mem && mem) { + ERROR("Total memory requested is more than 75%% of physical " + "memory.\n"); + ERROR("Mksquashfs uses memory to cache data from disk to " + "optimise performance.\n"); + ERROR("It is pointless to ask it to use more than this amount " + "of memory, as this\n"); + ERROR("causes thrashing and it is thus self-defeating.\n"); + BAD_ERROR("Requested memory size too large\n"); + } + + if(sizeof(void *) == 4 && total_mem > 2048) { + /* + * If we're running on a kernel with PAE or on a 64-bit kernel, + * then the 75% physical memory limit can still easily exceed + * the addressable memory by this process. + * + * Due to the typical kernel/user-space split (1GB/3GB, or + * 2GB/2GB), we have to conservatively assume the 32-bit + * processes can only address 2-3GB. So refuse if the user + * tries to allocate more than 2GB. + */ + ERROR("Total memory requested may exceed maximum " + "addressable memory by this process\n"); + BAD_ERROR("Requested memory size too large\n"); + } +} + + +int get_default_phys_mem() +{ + /* + * get_physical_memory() relies on /proc being mounted. + * If it fails, issue a warning, and use + * SQUASHFS_LOWMEM / SQUASHFS_TAKE as default, + * and allow a larger value to be set with -mem. + */ + int mem = get_physical_memory(); + + if(mem == 0) { + mem = SQUASHFS_LOWMEM / SQUASHFS_TAKE; + + ERROR("Warning: Cannot get size of physical memory, probably " + "because /proc is missing.\n"); + ERROR("Warning: Defaulting to minimal use of %d Mbytes, use " + "-mem to set a better value,\n", mem); + ERROR("Warning: or fix /proc.\n"); + } else + mem /= SQUASHFS_TAKE; + + if(sizeof(void *) == 4 && mem > 640) { + /* + * If we're running on a kernel with PAE or on a 64-bit kernel, + * the default memory usage can exceed the addressable + * memory by this process. + * Due to the typical kernel/user-space split (1GB/3GB, or + * 2GB/2GB), we have to conservatively assume the 32-bit + * processes can only address 2-3GB. So limit the default + * usage to 640M, which gives room for other data. + */ + mem = 640; + } + + return mem; +} + + +void calculate_queue_sizes(int mem, int *readq, int *fragq, int *bwriteq, + int *fwriteq) +{ + *readq = mem / SQUASHFS_READQ_MEM; + *bwriteq = mem / SQUASHFS_BWRITEQ_MEM; + *fwriteq = mem / SQUASHFS_FWRITEQ_MEM; + *fragq = mem - *readq - *bwriteq - *fwriteq; +} + + #define VERSION() \ - printf("mksquashfs version 4.2-git (2013/04/07)\n");\ - printf("copyright (C) 2013 Phillip Lougher "\ + printf("mksquashfs version 4.3-git (2014/09/12)\n");\ + printf("copyright (C) 2014 Phillip Lougher "\ "\n\n"); \ printf("This program is free software; you can redistribute it and/or"\ "\n");\ @@ -4781,29 +5114,79 @@ char *b, *root_name = NULL; int keep_as_directory = FALSE; squashfs_inode inode; - int readb_mbytes = READER_BUFFER_DEFAULT, - writeb_mbytes = WRITER_BUFFER_DEFAULT, - fragmentb_mbytes = FRAGMENT_BUFFER_DEFAULT; + int readq; + int fragq; + int bwriteq; + int fwriteq; + int total_mem = get_default_phys_mem(); + int progress = TRUE; int force_progress = FALSE; struct file_buffer **fragment = NULL; - block_log = slog(block_size); if(argc > 1 && strcmp(argv[1], "-version") == 0) { VERSION(); exit(0); } + + block_log = slog(block_size); + calculate_queue_sizes(total_mem, &readq, &fragq, &bwriteq, &fwriteq); + for(i = 1; i < argc && argv[i][0] != '-'; i++); if(i < 3) goto printOptions; source_path = argv + 1; source = i - 2; + /* - * lookup default compressor. Note the Makefile ensures the default - * compressor has been built, and so we don't need to to check - * for failure here + * Scan the command line for -comp xxx option, this is to ensure + * any -X compressor specific options are passed to the + * correct compressor */ - comp = lookup_compressor(COMP_DEFAULT); for(; i < argc; i++) { + struct compressor *prev_comp = comp; + + if(strcmp(argv[i], "-comp") == 0) { + if(++i == argc) { + ERROR("%s: -comp missing compression type\n", + argv[0]); + exit(1); + } + comp = lookup_compressor(argv[i]); + if(!comp->supported) { + ERROR("%s: Compressor \"%s\" is not supported!" + "\n", argv[0], argv[i]); + ERROR("%s: Compressors available:\n", argv[0]); + display_compressors("", COMP_DEFAULT); + exit(1); + } + if(prev_comp != NULL && prev_comp != comp) { + ERROR("%s: -comp multiple conflicting -comp" + " options specified on command line" + ", previously %s, now %s\n", argv[0], + prev_comp->name, comp->name); + exit(1); + } + compressor_opt_parsed = 1; + + } else if(strcmp(argv[i], "-e") == 0) + break; + else if(strcmp(argv[i], "-root-becomes") == 0 || + strcmp(argv[i], "-ef") == 0 || + strcmp(argv[i], "-pf") == 0 || + strcmp(argv[i], "-vaf") == 0 || + strcmp(argv[i], "-comp") == 0) + i++; + } + + /* + * if no -comp option specified lookup default compressor. Note the + * Makefile ensures the default compressor has been built, and so we + * don't need to to check for failure here + */ + if(comp == NULL) + comp = lookup_compressor(COMP_DEFAULT); + + for(i = source + 2; i < argc; i++) { if(strcmp(argv[i], "-action") == 0 || strcmp(argv[i], "-a") ==0) { if(++i == argc) { @@ -4811,54 +5194,114 @@ argv[0], argv[i - 1]); exit(1); } - res = parse_action(argv[i]); + res = parse_action(argv[i], ACTION_LOG_NONE); + if(res == 0) + exit(1); + + } else if(strcmp(argv[i], "-verbose-action") == 0 || + strcmp(argv[i], "-va") ==0) { + if(++i == argc) { + ERROR("%s: %s missing action\n", + argv[0], argv[i - 1]); + exit(1); + } + res = parse_action(argv[i], ACTION_LOG_VERBOSE); if(res == 0) exit(1); - } else if(strcmp(argv[i], "-af") == 0) { + } else if(strcmp(argv[i], "-true-action") == 0 || + strcmp(argv[i], "-ta") ==0) { if(++i == argc) { - ERROR("%s: -af missing filename\n", argv[0]); + ERROR("%s: %s missing action\n", + argv[0], argv[i - 1]); exit(1); } - if(read_action_file(argv[i]) == FALSE) + res = parse_action(argv[i], ACTION_LOG_TRUE); + if(res == 0) exit(1); - } else if(strcmp(argv[i], "-comp") == 0) { - if(compressor_opts_parsed) { - ERROR("%s: -comp must appear before -X options" - "\n", argv[0]); + } else if(strcmp(argv[i], "-false-action") == 0 || + strcmp(argv[i], "-fa") ==0) { + if(++i == argc) { + ERROR("%s: %s missing action\n", + argv[0], argv[i - 1]); exit(1); } + res = parse_action(argv[i], ACTION_LOG_FALSE); + if(res == 0) + exit(1); + + } else if(strcmp(argv[i], "-action-file") == 0 || + strcmp(argv[i], "-af") ==0) { if(++i == argc) { - ERROR("%s: -comp missing compression type\n", - argv[0]); + ERROR("%s: %s missing filename\n", argv[0], + argv[i - 1]); exit(1); } - comp = lookup_compressor(argv[i]); - if(!comp->supported) { - ERROR("%s: Compressor \"%s\" is not supported!" - "\n", argv[0], argv[i]); - ERROR("%s: Compressors available:\n", argv[0]); - display_compressors("", COMP_DEFAULT); + if(read_action_file(argv[i], ACTION_LOG_NONE) == FALSE) + exit(1); + + } else if(strcmp(argv[i], "-verbose-action-file") == 0 || + strcmp(argv[i], "-vaf") ==0) { + if(++i == argc) { + ERROR("%s: %s missing filename\n", argv[0], + argv[i - 1]); + exit(1); + } + if(read_action_file(argv[i], ACTION_LOG_VERBOSE) == FALSE) + exit(1); + + } else if(strcmp(argv[i], "-true-action-file") == 0 || + strcmp(argv[i], "-taf") ==0) { + if(++i == argc) { + ERROR("%s: %s missing filename\n", argv[0], + argv[i - 1]); + exit(1); + } + if(read_action_file(argv[i], ACTION_LOG_TRUE) == FALSE) + exit(1); + + } else if(strcmp(argv[i], "-false-action-file") == 0 || + strcmp(argv[i], "-faf") ==0) { + if(++i == argc) { + ERROR("%s: %s missing filename\n", argv[0], + argv[i - 1]); exit(1); } + if(read_action_file(argv[i], ACTION_LOG_FALSE) == FALSE) + exit(1); + + } else if(strcmp(argv[i], "-comp") == 0) + /* parsed previously */ + i++; + + else if(strncmp(argv[i], "-X", 2) == 0) { + int args; - } else if(strncmp(argv[i], "-X", 2) == 0) { - int args = compressor_options(comp, argv + i, argc - i); + if(strcmp(argv[i] + 2, "help") == 0) + goto print_compressor_options; + + args = compressor_options(comp, argv + i, argc - i); if(args < 0) { if(args == -1) { ERROR("%s: Unrecognised compressor" " option %s\n", argv[0], argv[i]); - ERROR("%s: Did you forget to specify" - " -comp, or specify it after" - " the compressor specific" - " option?\n", argv[0]); - } + if(!compressor_opt_parsed) + ERROR("%s: Did you forget to" + " specify -comp?\n", + argv[0]); +print_compressor_options: + ERROR("%s: selected compressor \"%s\"" + ". Options supported: %s\n", + argv[0], comp->name, + comp->usage ? "" : "none"); + if(comp->usage) + comp->usage(); + } exit(1); } i += args; - compressor_opts_parsed = 1; } else if(strcmp(argv[i], "-pf") == 0) { if(++i == argc) { @@ -4910,42 +5353,68 @@ exit(1); } } else if(strcmp(argv[i], "-read-queue") == 0) { - if((++i == argc) || - !parse_num(argv[i], &readb_mbytes)) { + if((++i == argc) || !parse_num(argv[i], &readq)) { ERROR("%s: -read-queue missing or invalid " "queue size\n", argv[0]); exit(1); } - if(readb_mbytes < 1) { + if(readq < 1) { ERROR("%s: -read-queue should be 1 megabyte or " "larger\n", argv[0]); exit(1); } } else if(strcmp(argv[i], "-write-queue") == 0) { - if((++i == argc) || - !parse_num(argv[i], &writeb_mbytes)) { + if((++i == argc) || !parse_num(argv[i], &bwriteq)) { ERROR("%s: -write-queue missing or invalid " "queue size\n", argv[0]); exit(1); } - if(writeb_mbytes < 1) { - ERROR("%s: -write-queue should be 1 megabyte " + if(bwriteq < 2) { + ERROR("%s: -write-queue should be 2 megabytes " "or larger\n", argv[0]); exit(1); } + fwriteq = bwriteq >> 1; + bwriteq -= fwriteq; } else if(strcmp(argv[i], "-fragment-queue") == 0) { - if((++i == argc) || - !parse_num(argv[i], - &fragmentb_mbytes)) { + if((++i == argc) || !parse_num(argv[i], &fragq)) { ERROR("%s: -fragment-queue missing or invalid " "queue size\n", argv[0]); exit(1); } - if(fragmentb_mbytes < 1) { + if(fragq < 1) { ERROR("%s: -fragment-queue should be 1 " "megabyte or larger\n", argv[0]); exit(1); } + } else if(strcmp(argv[i], "-mem") == 0) { + long long number; + + if((++i == argc) || + !parse_numberll(argv[i], &number, 1)) { + ERROR("%s: -mem missing or invalid mem size\n", + argv[0]); + exit(1); + } + + /* + * convert from bytes to Mbytes, ensuring the value + * does not overflow a signed int + */ + if(number >= (1LL << 51)) { + ERROR("%s: -mem invalid mem size\n", argv[0]); + exit(1); + } + + total_mem = number / 1048576; + if(total_mem < (SQUASHFS_LOWMEM / SQUASHFS_TAKE)) { + ERROR("%s: -mem should be %d Mbytes or " + "larger\n", argv[0], + SQUASHFS_LOWMEM / SQUASHFS_TAKE); + exit(1); + } + calculate_queue_sizes(total_mem, &readq, &fragq, + &bwriteq, &fwriteq); } else if(strcmp(argv[i], "-b") == 0) { if(++i == argc) { ERROR("%s: -b missing block size\n", argv[0]); @@ -5067,6 +5536,9 @@ else if(strcmp(argv[i], "-keep-as-directory") == 0) keep_as_directory = TRUE; + else if(strcmp(argv[i], "-exit-on-error") == 0) + exit_on_error = TRUE; + else if(strcmp(argv[i], "-root-becomes") == 0) { if(++i == argc) { ERROR("%s: -root-becomes: missing name\n", @@ -5086,8 +5558,10 @@ ERROR("\t\t\tCompressors available:\n"); display_compressors("\t\t\t", COMP_DEFAULT); ERROR("-b \t\tset data block to " - ". Default %d bytes\n", - SQUASHFS_FILE_SIZE); + ". Default 128 Kbytes\n"); + ERROR("\t\t\tOptionally a suffix of K or M can be" + " given to specify\n\t\t\tKbytes or Mbytes" + " respectively\n"); ERROR("-no-exports\t\tdon't make the filesystem " "exportable via NFS\n"); ERROR("-no-sparse\t\tdon't detect sparse files\n"); @@ -5145,6 +5619,8 @@ ERROR("\nMksquashfs runtime options:\n"); ERROR("-version\t\tprint version, licence and " "copyright message\n"); + ERROR("-exit-on-error\t\ttreat normally ignored errors " + "as fatal\n"); ERROR("-recover \t\trecover filesystem data " "using recovery file \n"); ERROR("-no-recovery\t\tdon't generate a recovery " @@ -5157,15 +5633,11 @@ ERROR("-processors \tUse processors." " By default will use number of\n"); ERROR("\t\t\tprocessors available\n"); - ERROR("-read-queue \tSet input queue to " - "Mbytes. Default %d Mbytes\n", - READER_BUFFER_DEFAULT); - ERROR("-write-queue \tSet output queue to " - "Mbytes. Default %d Mbytes\n", - WRITER_BUFFER_DEFAULT); - ERROR("-fragment-queue \tSet fragment queue to " - " Mbytes. Default %d Mbytes\n", - FRAGMENT_BUFFER_DEFAULT); + ERROR("-mem \t\tUse physical memory. " + "Currently set to %dM\n", total_mem); + ERROR("\t\t\tOptionally a suffix of K, M or G can be" + " given to specify\n\t\t\tKbytes, Mbytes or" + " Gbytes respectively\n"); ERROR("\nMiscellaneous options:\n"); ERROR("-root-owned\t\talternative name for -all-root" "\n"); @@ -5177,6 +5649,8 @@ "-noF\n"); ERROR("-noXattrCompression\talternative name for " "-noX\n"); + ERROR("\n-Xhelp\t\t\tprint compressor options for" + " selected compressor\n"); ERROR("\nCompressors available and compressor specific " "options:\n"); display_compressor_usage(COMP_DEFAULT); @@ -5201,6 +5675,11 @@ progress = force_progress; #ifdef SQUASHFS_TRACE + /* + * Disable progress bar if full debug tracing is enabled. + * The progress bar in this case just gets in the way of the + * debug trace output + */ progress = FALSE; #endif @@ -5269,6 +5748,7 @@ strcmp(argv[i], "-sort") == 0 || strcmp(argv[i], "-pf") == 0 || strcmp(argv[i], "-af") == 0 || + strcmp(argv[i], "-vaf") == 0 || strcmp(argv[i], "-comp") == 0) i++; @@ -5298,6 +5778,7 @@ strcmp(argv[i], "-ef") == 0 || strcmp(argv[i], "-pf") == 0 || strcmp(argv[i], "-af") == 0 || + strcmp(argv[i], "-vaf") == 0 || strcmp(argv[i], "-comp") == 0) i++; @@ -5324,7 +5805,8 @@ comp_opts = SQUASHFS_COMP_OPTS(sBlk.flags); } - initialise_threads(readb_mbytes, writeb_mbytes, fragmentb_mbytes); + initialise_threads(readq, fragq, bwriteq, fwriteq, delete, + destination_file); res = compressor_init(comp, &stream, SQUASHFS_METADATA_SIZE, 0); if(res) @@ -5366,7 +5848,6 @@ SQUASHFS_INODE_BLK(sBlk.root_inode), root_inode_offset = SQUASHFS_INODE_OFFSET(sBlk.root_inode); - sigset_t sigmask, old_mask; if((bytes = read_filesystem(root_name, fd, &sBlk, &inode_table, &data_cache, &directory_table, @@ -5385,7 +5866,7 @@ "device or file use -noappend\n"); EXIT_MKSQUASHFS(); } - if((fragments = sBlk.fragments)) { + if((append_fragments = fragments = sBlk.fragments)) { fragment_table = realloc((char *) fragment_table, ((fragments + FRAG_SIZE - 1) & ~(FRAG_SIZE - 1)) * sizeof(struct squashfs_fragment_entry)); @@ -5437,14 +5918,7 @@ sid_count = id_count; write_recovery_data(&sBlk); save_xattrs(); - restore_thread = init_restore_thread(pthread_self()); - sigemptyset(&sigmask); - sigaddset(&sigmask, SIGINT); - sigaddset(&sigmask, SIGTERM); - sigaddset(&sigmask, SIGUSR1); - if(pthread_sigmask(SIG_BLOCK, &sigmask, &old_mask) == -1) - BAD_ERROR("Failed to set signal mask\n"); - write_destination(fd, SQUASHFS_START, 4, "\0\0\0\0"); + appending = TRUE; /* * set the filesystem state up to be able to append to the @@ -5493,33 +5967,22 @@ inode_count = file_count + dir_count + sym_count + dev_count + fifo_count + sock_count; - - /* - * The default use freelist before growing cache policy behaves - * poorly with appending - with many deplicates the caches - * do not grow due to the fact that large queues of outstanding - * fragments/writer blocks do not occur, leading to small caches - * and un-uncessary performance loss to frequent cache - * replacement in the small caches. Therefore with appending - * change the policy to grow the caches before reusing blocks - * from the freelist - */ - first_freelist = FALSE; } if(path) paths = add_subdir(paths, path); dump_actions(); + dump_pseudos(); if(delete && !keep_as_directory && source == 1 && S_ISDIR(source_buf.st_mode)) - dir_scan(&inode, source_path[0], scan1_readdir); + dir_scan(&inode, source_path[0], scan1_readdir, progress); else if(!keep_as_directory && source == 1 && S_ISDIR(source_buf.st_mode)) - dir_scan(&inode, source_path[0], scan1_single_readdir); + dir_scan(&inode, source_path[0], scan1_single_readdir, progress); else - dir_scan(&inode, "", scan1_encomp_readdir); + dir_scan(&inode, "", scan1_encomp_readdir, progress); sBlk.root_inode = inode; sBlk.inodes = inode_count; sBlk.s_magic = SQUASHFS_MAGIC; @@ -5532,7 +5995,6 @@ no_xattrs, comp_opts); sBlk.mkfs_time = time(NULL); - disable_progress_bar(); disable_info(); while((fragment = get_frag_action(fragment))) @@ -5551,6 +6013,7 @@ if(queue_get(from_writer) != 0) EXIT_MKSQUASHFS(); + set_progressbar_state(FALSE); write_filesystem_tables(&sBlk, nopad); return 0; diff -Nru squashfs-tools-4.2+20130409/mksquashfs.h squashfs-tools-4.3+20140919/mksquashfs.h --- squashfs-tools-4.2+20130409/mksquashfs.h 2013-05-09 10:39:11.000000000 +0200 +++ squashfs-tools-4.3+20140919/mksquashfs.h 2015-07-20 21:03:05.000000000 +0200 @@ -4,8 +4,8 @@ * Squashfs * * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 - * 2012 - * Phillip Lougher + * 2012, 2013, 2014 + * Phillip Lougher * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -25,22 +25,6 @@ * */ -#if __BYTE_ORDER == __BIG_ENDIAN -#define SQUASHFS_SWAP_SHORTS(s, d, n) swap_le16_num(s, d, n) -#define SQUASHFS_SWAP_INTS(s, d, n) swap_le32_num(s, d, n) -#define SQUASHFS_SWAP_LONG_LONGS(s, d, n) swap_le64_num(s, d, n) - -#define SWAP_LE16(s, d) swap_le16(s, d) -#define SWAP_LE32(s, d) swap_le32(s, d) -#define SWAP_LE64(s, d) swap_le64(s, d) -#else -#define SQUASHFS_MEMCPY(s, d, n) memcpy(d, s, n) -#define SQUASHFS_SWAP_SHORTS(s, d, n) memcpy(d, s, n * sizeof(short)) -#define SQUASHFS_SWAP_INTS(s, d, n) memcpy(d, s, n * sizeof(int)) -#define SQUASHFS_SWAP_LONG_LONGS(s, d, n) \ - memcpy(d, s, n * sizeof(long long)) -#endif - struct dir_info { char *pathname; char *subpath; @@ -79,6 +63,47 @@ char always_use_fragments; char noD; char noF; + char symlink[0]; +}; + +/* in memory file info */ +struct file_info { + long long file_size; + long long bytes; + long long start; + unsigned int *block_list; + struct file_info *next; + struct fragment *fragment; + unsigned short checksum; + unsigned short fragment_checksum; + char have_frag_checksum; + char have_checksum; +}; + +/* fragment block data structures */ +struct fragment { + unsigned int index; + int offset; + int size; +}; + +/* in memory uid tables */ +#define ID_ENTRIES 256 +#define ID_HASH(id) (id & (ID_ENTRIES - 1)) +#define ISA_UID 1 +#define ISA_GID 2 + +struct id { + unsigned int id; + int index; + char flags; + struct id *next; +}; + +/* fragment to file mapping used when appending */ +struct append_file { + struct file_info *file; + struct append_file *next; }; #define PSEUDO_FILE_OTHER 1 @@ -88,7 +113,42 @@ #define IS_PSEUDO_PROCESS(a) ((a)->pseudo_file & PSEUDO_FILE_PROCESS) #define IS_PSEUDO_OTHER(a) ((a)->pseudo_file & PSEUDO_FILE_OTHER) +/* + * Amount of physical memory to use by default, and the default queue + * ratios + */ +#define SQUASHFS_TAKE 4 +#define SQUASHFS_READQ_MEM 4 +#define SQUASHFS_BWRITEQ_MEM 4 +#define SQUASHFS_FWRITEQ_MEM 4 + +/* + * Lowest amount of physical memory considered viable for Mksquashfs + * to run in Mbytes + */ +#define SQUASHFS_LOWMEM 64 + /* offset of data in compressed metadata blocks (allowing room for * compressed size */ #define BLOCK_OFFSET 2 + +extern struct cache *reader_buffer, *fragment_buffer, *reserve_cache; +struct cache *bwriter_buffer, *fwriter_buffer; +extern struct queue *to_reader, *to_deflate, *to_writer, *from_writer, + *to_frag, *locked_fragment, *to_process_frag; +extern struct append_file **file_mapping; +extern struct seq_queue *to_main; +extern pthread_mutex_t fragment_mutex, dup_mutex; +extern struct squashfs_fragment_entry *fragment_table; +extern struct compressor *comp; +extern int block_size; +extern struct file_info *dupl[]; +extern int read_fs_bytes(int, long long, int, void *); +extern void add_file(long long, long long, long long, unsigned int *, int, + unsigned int, int, int); +extern struct id *create_id(unsigned int); +extern unsigned int get_uid(unsigned int); +extern unsigned int get_guid(unsigned int); +extern int read_bytes(int, void *, int); +extern unsigned short get_checksum_mem(char *, int); #endif diff -Nru squashfs-tools-4.2+20130409/par_mksquashfs/README squashfs-tools-4.3+20140919/par_mksquashfs/README --- squashfs-tools-4.2+20130409/par_mksquashfs/README 2013-05-09 10:39:11.000000000 +0200 +++ squashfs-tools-4.3+20140919/par_mksquashfs/README 1970-01-01 01:00:00.000000000 +0100 @@ -1,2 +0,0 @@ -par_mksquashfs is now the standard mksquashfs, and so this directory is now empty. - diff -Nru squashfs-tools-4.2+20130409/process_fragments.c squashfs-tools-4.3+20140919/process_fragments.c --- squashfs-tools-4.2+20130409/process_fragments.c 1970-01-01 01:00:00.000000000 +0100 +++ squashfs-tools-4.3+20140919/process_fragments.c 2015-07-20 21:03:05.000000000 +0200 @@ -0,0 +1,370 @@ +/* + * Create a squashfs filesystem. This is a highly compressed read only + * filesystem. + * + * Copyright (c) 2014 + * Phillip Lougher + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2, + * or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * process_fragments.c + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "caches-queues-lists.h" +#include "squashfs_fs.h" +#include "mksquashfs.h" +#include "error.h" +#include "progressbar.h" +#include "info.h" +#include "compressor.h" +#include "process_fragments.h" + +#define FALSE 0 +#define TRUE 1 + +extern struct queue *to_process_frag; +extern struct seq_queue *to_main; +extern int sparse_files; + +/* + * Compute 16 bit BSD checksum over the data, and check for sparseness + */ +static int checksum_sparse(struct file_buffer *file_buffer) +{ + unsigned char *b = (unsigned char *) file_buffer->data; + unsigned short chksum = 0; + int bytes = file_buffer->size, sparse = TRUE, value; + + while(bytes --) { + chksum = (chksum & 1) ? (chksum >> 1) | 0x8000 : chksum >> 1; + value = *b++; + if(value) { + sparse = FALSE; + chksum += value; + } + } + + file_buffer->checksum = chksum; + return sparse; +} + + +static int read_filesystem(int fd, long long byte, int bytes, void *buff) +{ + off_t off = byte; + + TRACE("read_filesystem: reading from position 0x%llx, bytes %d\n", + byte, bytes); + + if(lseek(fd, off, SEEK_SET) == -1) { + ERROR("read_filesystem: Lseek on destination failed because %s, " + "offset=0x%llx\n", strerror(errno), off); + return 0; + } else if(read_bytes(fd, buff, bytes) < bytes) { + ERROR("Read on destination failed\n"); + return 0; + } + + return 1; +} + + +static struct file_buffer *get_fragment(struct fragment *fragment, + char *data_buffer, int fd) +{ + struct squashfs_fragment_entry *disk_fragment; + struct file_buffer *buffer, *compressed_buffer; + long long start_block; + int res, size, index = fragment->index; + char locked; + + /* + * Lookup fragment block in cache. + * If the fragment block doesn't exist, then get the compressed version + * from the writer cache or off disk, and decompress it. + * + * This routine has two things which complicate the code: + * + * 1. Multiple threads can simultaneously lookup/create the + * same buffer. This means a buffer needs to be "locked" + * when it is being filled in, to prevent other threads from + * using it when it is not ready. This is because we now do + * fragment duplicate checking in parallel. + * 2. We have two caches which need to be checked for the + * presence of fragment blocks: the normal fragment cache + * and a "reserve" cache. The reserve cache is used to + * prevent an unnecessary pipeline stall when the fragment cache + * is full of fragments waiting to be compressed. + */ + pthread_cleanup_push((void *) pthread_mutex_unlock, &dup_mutex); + pthread_mutex_lock(&dup_mutex); + +again: + buffer = cache_lookup_nowait(fragment_buffer, index, &locked); + if(buffer) { + pthread_mutex_unlock(&dup_mutex); + if(locked) + /* got a buffer being filled in. Wait for it */ + cache_wait_unlock(buffer); + goto finished; + } + + /* not in fragment cache, is it in the reserve cache? */ + buffer = cache_lookup_nowait(reserve_cache, index, &locked); + if(buffer) { + pthread_mutex_unlock(&dup_mutex); + if(locked) + /* got a buffer being filled in. Wait for it */ + cache_wait_unlock(buffer); + goto finished; + } + + /* in neither cache, try to get it from the fragment cache */ + buffer = cache_get_nowait(fragment_buffer, index); + if(!buffer) { + /* + * no room, get it from the reserve cache, this is + * dimensioned so it will always have space (no more than + * processors + 1 can have an outstanding reserve buffer) + */ + buffer = cache_get_nowait(reserve_cache, index); + if(!buffer) { + /* failsafe */ + ERROR("no space in reserve cache\n"); + goto again; + } + } + + pthread_mutex_unlock(&dup_mutex); + + compressed_buffer = cache_lookup(fwriter_buffer, index); + + pthread_cleanup_push((void *) pthread_mutex_unlock, &fragment_mutex); + pthread_mutex_lock(&fragment_mutex); + disk_fragment = &fragment_table[index]; + size = SQUASHFS_COMPRESSED_SIZE_BLOCK(disk_fragment->size); + start_block = disk_fragment->start_block; + pthread_cleanup_pop(1); + + if(SQUASHFS_COMPRESSED_BLOCK(disk_fragment->size)) { + int error; + char *data; + + if(compressed_buffer) + data = compressed_buffer->data; + else { + res = read_filesystem(fd, start_block, size, data_buffer); + if(res == 0) { + ERROR("Failed to read fragment from output" + " filesystem\n"); + BAD_ERROR("Output filesystem corrupted?\n"); + } + data = data_buffer; + } + + res = compressor_uncompress(comp, buffer->data, data, size, + block_size, &error); + if(res == -1) + BAD_ERROR("%s uncompress failed with error code %d\n", + comp->name, error); + } else if(compressed_buffer) + memcpy(buffer->data, compressed_buffer->data, size); + else { + res = read_filesystem(fd, start_block, size, buffer->data); + if(res == 0) { + ERROR("Failed to read fragment from output " + "filesystem\n"); + BAD_ERROR("Output filesystem corrupted?\n"); + } + } + + cache_unlock(buffer); + cache_block_put(compressed_buffer); + +finished: + pthread_cleanup_pop(0); + + return buffer; +} + + +struct file_buffer *get_fragment_cksum(struct file_info *file, + char *data_buffer, int fd, unsigned short *checksum) +{ + struct file_buffer *frag_buffer; + struct append_file *append; + int index = file->fragment->index; + + frag_buffer = get_fragment(file->fragment, data_buffer, fd); + + pthread_cleanup_push((void *) pthread_mutex_unlock, &dup_mutex); + + for(append = file_mapping[index]; append; append = append->next) { + int offset = append->file->fragment->offset; + int size = append->file->fragment->size; + char *data = frag_buffer->data + offset; + unsigned short cksum = get_checksum_mem(data, size); + + if(file == append->file) + *checksum = cksum; + + pthread_mutex_lock(&dup_mutex); + append->file->fragment_checksum = cksum; + append->file->have_frag_checksum = TRUE; + pthread_mutex_unlock(&dup_mutex); + } + + pthread_cleanup_pop(0); + + return frag_buffer; +} + + +void *frag_thrd(void *destination_file) +{ + sigset_t sigmask, old_mask; + char *data_buffer; + int fd; + + sigemptyset(&sigmask); + sigaddset(&sigmask, SIGINT); + sigaddset(&sigmask, SIGTERM); + sigaddset(&sigmask, SIGUSR1); + pthread_sigmask(SIG_BLOCK, &sigmask, &old_mask); + + fd = open(destination_file, O_RDONLY); + if(fd == -1) + BAD_ERROR("frag_thrd: can't open destination for reading\n"); + + data_buffer = malloc(SQUASHFS_FILE_MAX_SIZE); + if(data_buffer == NULL) + MEM_ERROR(); + + pthread_cleanup_push((void *) pthread_mutex_unlock, &dup_mutex); + + while(1) { + struct file_buffer *file_buffer = queue_get(to_process_frag); + struct file_buffer *buffer; + int sparse = checksum_sparse(file_buffer); + struct file_info *dupl_ptr; + long long file_size; + unsigned short checksum; + char flag; + int res; + + if(sparse_files && sparse) { + file_buffer->c_byte = 0; + file_buffer->fragment = FALSE; + } else + file_buffer->c_byte = file_buffer->size; + + /* + * Specutively pull into the fragment cache any fragment blocks + * which contain fragments which *this* fragment may be + * be a duplicate. + * + * By ensuring the fragment block is in cache ahead of time + * should eliminate the parallelisation stall when the + * main thread needs to read the fragment block to do a + * duplicate check on it. + * + * If this is a fragment belonging to a larger file + * (with additional blocks) then ignore it. Here we're + * interested in the "low hanging fruit" of files which + * consist of only a fragment + */ + if(file_buffer->file_size != file_buffer->size) { + seq_queue_put(to_main, file_buffer); + continue; + } + + file_size = file_buffer->file_size; + + pthread_mutex_lock(&dup_mutex); + dupl_ptr = dupl[DUP_HASH(file_size)]; + pthread_mutex_unlock(&dup_mutex); + + file_buffer->dupl_start = dupl_ptr; + file_buffer->duplicate = FALSE; + + for(; dupl_ptr; dupl_ptr = dupl_ptr->next) { + if(file_size != dupl_ptr->file_size || + file_size != dupl_ptr->fragment->size) + continue; + + pthread_mutex_lock(&dup_mutex); + flag = dupl_ptr->have_frag_checksum; + checksum = dupl_ptr->fragment_checksum; + pthread_mutex_unlock(&dup_mutex); + + /* + * If we have the checksum and it matches then + * read in the fragment block. + * + * If we *don't* have the checksum, then we are + * appending, and the fragment block is on the + * "old" filesystem. Read it in and checksum + * the entire fragment buffer + */ + if(!flag) { + buffer = get_fragment_cksum(dupl_ptr, + data_buffer, fd, &checksum); + if(checksum != file_buffer->checksum) { + cache_block_put(buffer); + continue; + } + } else if(checksum == file_buffer->checksum) + buffer = get_fragment(dupl_ptr->fragment, + data_buffer, fd); + else + continue; + + res = memcmp(file_buffer->data, buffer->data + + dupl_ptr->fragment->offset, file_size); + cache_block_put(buffer); + if(res == 0) { + struct file_buffer *dup = malloc(sizeof(*dup)); + if(dup == NULL) + MEM_ERROR(); + memcpy(dup, file_buffer, sizeof(*dup)); + cache_block_put(file_buffer); + dup->dupl_start = dupl_ptr; + dup->duplicate = TRUE; + file_buffer = dup; + break; + } + } + + seq_queue_put(to_main, file_buffer); + } + + pthread_cleanup_pop(0); +} diff -Nru squashfs-tools-4.2+20130409/process_fragments.h squashfs-tools-4.3+20140919/process_fragments.h --- squashfs-tools-4.2+20130409/process_fragments.h 1970-01-01 01:00:00.000000000 +0100 +++ squashfs-tools-4.3+20140919/process_fragments.h 2015-07-20 21:03:05.000000000 +0200 @@ -0,0 +1,30 @@ +#ifndef PROCESS_FRAGMENTS_H +#define PROCESS_FRAGMENTS_H +/* + * Create a squashfs filesystem. This is a highly compressed read only + * filesystem. + * + * Copyright (c) 2014 + * Phillip Lougher + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2, + * or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * process_fragments.h + */ + +#define DUP_HASH(a) (a & 0xffff) + +extern void *frag_thrd(void *); +#endif diff -Nru squashfs-tools-4.2+20130409/progressbar.c squashfs-tools-4.3+20140919/progressbar.c --- squashfs-tools-4.2+20130409/progressbar.c 2013-05-09 10:39:11.000000000 +0200 +++ squashfs-tools-4.3+20140919/progressbar.c 2015-07-20 21:03:05.000000000 +0200 @@ -2,7 +2,7 @@ * Create a squashfs filesystem. This is a highly compressed read only * filesystem. * - * Copyright (c) 2012, 2013 + * Copyright (c) 2012, 2013, 2014 * Phillip Lougher * * This program is free software; you can redistribute it and/or @@ -35,7 +35,15 @@ #include "error.h" -int progress_enabled = 0; +#define FALSE 0 +#define TRUE 1 + +/* flag whether progressbar display is enabled or not */ +int display_progress_bar = FALSE; + +/* flag whether the progress bar is temporarily disbled */ +int temp_disabled = FALSE; + int rotate = 0; int cur_uncompressed = 0, estimated_uncompressed = 0; int columns; @@ -50,7 +58,7 @@ if(ioctl(1, TIOCGWINSZ, &winsize) == -1) { if(isatty(STDOUT_FILENO)) - printf("TIOCGWINSZ ioctl failed, defaulting to 80 " + ERROR("TIOCGWINSZ ioctl failed, defaulting to 80 " "columns\n"); columns = 80; } else @@ -132,21 +140,39 @@ void enable_progress_bar() { + pthread_cleanup_push((void *) pthread_mutex_unlock, &progress_mutex); pthread_mutex_lock(&progress_mutex); - progress_enabled = 1; - pthread_mutex_unlock(&progress_mutex); + if(display_progress_bar) + progress_bar(cur_uncompressed, estimated_uncompressed, columns); + temp_disabled = FALSE; + pthread_cleanup_pop(1); } void disable_progress_bar() { + pthread_cleanup_push((void *) pthread_mutex_unlock, &progress_mutex); pthread_mutex_lock(&progress_mutex); - if(progress_enabled) { - progress_bar(cur_uncompressed, estimated_uncompressed, columns); + if(display_progress_bar) printf("\n"); + temp_disabled = TRUE; + pthread_cleanup_pop(1); +} + + +void set_progressbar_state(int state) +{ + pthread_cleanup_push((void *) pthread_mutex_unlock, &progress_mutex); + pthread_mutex_lock(&progress_mutex); + if(display_progress_bar != state) { + if(display_progress_bar && !temp_disabled) { + progress_bar(cur_uncompressed, estimated_uncompressed, + columns); + printf("\n"); + } + display_progress_bar = state; } - progress_enabled = 0; - pthread_mutex_unlock(&progress_mutex); + pthread_cleanup_pop(1); } @@ -158,7 +184,7 @@ if(ioctl(1, TIOCGWINSZ, &winsize) == -1) { if(isatty(STDOUT_FILENO)) - printf("TIOCGWINSZ ioctl failed, defaulting to 80 " + ERROR("TIOCGWINSZ ioctl failed, defaulting to 80 " "columns\n"); columns = 80; } else @@ -181,12 +207,11 @@ if(res == -1 && errno != EINTR) BAD_ERROR("nanosleep failed in progress thread\n"); - if(progress_enabled) { - pthread_mutex_lock(&progress_mutex); + pthread_mutex_lock(&progress_mutex); + if(display_progress_bar && !temp_disabled) progress_bar(cur_uncompressed, estimated_uncompressed, columns); - pthread_mutex_unlock(&progress_mutex); - } + pthread_mutex_unlock(&progress_mutex); } } @@ -201,16 +226,17 @@ { va_list ap; + pthread_cleanup_push((void *) pthread_mutex_unlock, &progress_mutex); pthread_mutex_lock(&progress_mutex); - if(progress_enabled) + if(display_progress_bar && !temp_disabled) fprintf(stderr, "\n"); va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); - pthread_mutex_unlock(&progress_mutex); + pthread_cleanup_pop(1); } @@ -218,15 +244,16 @@ { va_list ap; + pthread_cleanup_push((void *) pthread_mutex_unlock, &progress_mutex); pthread_mutex_lock(&progress_mutex); - if(progress_enabled) + if(display_progress_bar && !temp_disabled) printf("\n"); va_start(ap, fmt); vprintf(fmt, ap); va_end(ap); - pthread_mutex_unlock(&progress_mutex); + pthread_cleanup_pop(1); } diff -Nru squashfs-tools-4.2+20130409/progressbar.h squashfs-tools-4.3+20140919/progressbar.h --- squashfs-tools-4.2+20130409/progressbar.h 2013-05-09 10:39:11.000000000 +0200 +++ squashfs-tools-4.3+20140919/progressbar.h 2015-07-20 21:03:05.000000000 +0200 @@ -1,8 +1,10 @@ +#ifndef PROGRESSBAR_H +#define PROGRESSBAR_H /* * Create a squashfs filesystem. This is a highly compressed read only * filesystem. * - * Copyright (c) 2012, 2013 + * Copyright (c) 2012, 2013, 2014 * Phillip Lougher * * This program is free software; you can redistribute it and/or @@ -28,3 +30,5 @@ extern void enable_progress_bar(); extern void disable_progress_bar(); extern void init_progress_bar(); +extern void set_progressbar_state(int); +#endif diff -Nru squashfs-tools-4.2+20130409/pseudo.c squashfs-tools-4.3+20140919/pseudo.c --- squashfs-tools-4.2+20130409/pseudo.c 2013-05-09 10:39:11.000000000 +0200 +++ squashfs-tools-4.3+20140919/pseudo.c 2015-07-20 21:03:05.000000000 +0200 @@ -2,7 +2,7 @@ * Create a squashfs filesystem. This is a highly compressed read only * filesystem. * - * Copyright (c) 2009, 2010, 2012 + * Copyright (c) 2009, 2010, 2012, 2014 * Phillip Lougher * * This program is free software; you can redistribute it and/or @@ -36,6 +36,7 @@ #include "pseudo.h" #include "error.h" +#include "progressbar.h" #define TRUE 1 #define FALSE 0 @@ -46,32 +47,6 @@ struct pseudo *pseudo = NULL; int pseudo_count = 0; -static void dump_pseudo(struct pseudo *pseudo, char *string) -{ - int i, res; - char *path; - - for(i = 0; i < pseudo->names; i++) { - struct pseudo_entry *entry = &pseudo->name[i]; - if(string) { - res = asprintf(&path, "%s/%s", string, entry->name); - if(res == -1) - BAD_ERROR("asprintf failed in dump_pseudo\n"); - } else - path = entry->name; - if(entry->pseudo == NULL) - ERROR("%s %c %o %d %d %d %d\n", path, entry->dev->type, - entry->dev->mode, entry->dev->uid, - entry->dev->gid, entry->dev->major, - entry->dev->minor); - else - dump_pseudo(entry->pseudo, path); - if(string) - free(path); - } -} - - static char *get_component(char *target, char **targname) { char *start; @@ -80,11 +55,14 @@ target ++; start = target; - while(*target != '/' && *target!= '\0') + while(*target != '/' && *target != '\0') target ++; *targname = strndup(start, target - start); + while(*target == '/') + target ++; + return target; } @@ -154,16 +132,23 @@ pseudo->name[i].pseudo = add_pseudo(NULL, pseudo_dev, target, alltarget); - else - ERROR("%s already exists as a non " - "directory. Ignoring %s!\n", - targname, alltarget); + else { + ERROR_START("%s already exists as a " + "non directory.", + pseudo->name[i].name); + ERROR_EXIT(". Ignoring %s!\n", + alltarget); + } } else if(memcmp(pseudo_dev, pseudo->name[i].dev, - sizeof(struct pseudo_dev)) != 0) - ERROR("%s already exists as a different pseudo " - "definition. Ignoring!\n", alltarget); - else ERROR("%s already exists as an identical " - "pseudo definition!\n", alltarget); + sizeof(struct pseudo_dev)) != 0) { + ERROR_START("%s already exists as a different " + "pseudo definition.", alltarget); + ERROR_EXIT(" Ignoring!\n"); + } else { + ERROR_START("%s already exists as an identical " + "pseudo definition!", alltarget); + ERROR_EXIT(" Ignoring!\n"); + } } else { if(target[0] == '\0') { /* @@ -176,10 +161,13 @@ pseudo->name[i].pathname = strdup(alltarget); pseudo->name[i].dev = pseudo_dev; - } else - ERROR("%s already exists as a different" - " pseudo definition. Ignoring" - " %s!\n", targname, alltarget); + } else { + ERROR_START("%s already exists as a " + "different pseudo definition.", + pseudo->name[i].name); + ERROR_EXIT(" Ignoring %s!\n", + alltarget); + } } else /* recurse adding child components */ add_pseudo(pseudo->name[i].pseudo, pseudo_dev, @@ -227,75 +215,40 @@ } -int exec_file(char *command, struct pseudo_dev *dev) +int pseudo_exec_file(struct pseudo_dev *dev, int *child) { - int child, res; - static pid_t pid = -1; - int pipefd[2]; -#ifdef USE_TMP_FILE - char *filename; - int status; - static int number = 0; -#endif - - if(pid == -1) - pid = getpid(); + int res, pipefd[2]; -#ifdef USE_TMP_FILE - res = asprintf(&filename, "/tmp/squashfs_pseudo_%d_%d", pid, number ++); - if(res == -1) { - ERROR("asprint failed in exec_file()\n"); - return -1; - } - pipefd[1] = open(filename, O_CREAT | O_TRUNC | O_RDWR, S_IRWXU); - if(pipefd[1] == -1) { - ERROR("Executing dynamic pseudo file, open failed\n"); - free(filename); - return -1; - } -#else res = pipe(pipefd); if(res == -1) { ERROR("Executing dynamic pseudo file, pipe failed\n"); - return -1; + return 0; } -#endif - child = fork(); - if(child == -1) { + *child = fork(); + if(*child == -1) { ERROR("Executing dynamic pseudo file, fork failed\n"); goto failed; } - if(child == 0) { + if(*child == 0) { + close(pipefd[0]); close(STDOUT_FILENO); res = dup(pipefd[1]); if(res == -1) exit(EXIT_FAILURE); - execl("/bin/sh", "sh", "-c", command, (char *) NULL); + execl("/bin/sh", "sh", "-c", dev->command, (char *) NULL); exit(EXIT_FAILURE); } -#ifdef USE_TMP_FILE - res = waitpid(child, &status, 0); close(pipefd[1]); - if(res != -1 && WIFEXITED(status) && WEXITSTATUS(status) == 0) { - dev->filename = filename; - return 0; - } + return pipefd[0]; + failed: - unlink(filename); - free(filename); - return -1; -#else + close(pipefd[0]); close(pipefd[1]); - dev->fd = pipefd[0]; - dev->child = child; return 0; -failed: - return -1; -#endif } @@ -311,17 +264,6 @@ } -void delete_pseudo_files() -{ -#ifdef USE_TMP_FILE - int i; - - for(i = 0; i < pseudo_count; i++) - unlink(pseudo_file[i]->filename); -#endif -} - - struct pseudo_dev *get_pseudo_file(int pseudo_id) { return pseudo_file[pseudo_id]; @@ -513,20 +455,8 @@ dev->gid = gid; dev->major = major; dev->minor = minor; - if(type == 'f') { - int res; - - printf("Executing dynamic pseudo file\n"); - printf("\t\"%s\"\n", orig_def); - res = exec_file(def, dev); - if(res == -1) { - ERROR("Failed to execute dynamic pseudo file definition" - " \"%s\"\n", orig_def); - free(filename); - free(dev); - return FALSE; - } + dev->command = strdup(def); add_pseudo_file(dev); } @@ -557,3 +487,41 @@ { return pseudo; } + + +#ifdef SQUASHFS_TRACE +static void dump_pseudo(struct pseudo *pseudo, char *string) +{ + int i, res; + char *path; + + for(i = 0; i < pseudo->names; i++) { + struct pseudo_entry *entry = &pseudo->name[i]; + if(string) { + res = asprintf(&path, "%s/%s", string, entry->name); + if(res == -1) + BAD_ERROR("asprintf failed in dump_pseudo\n"); + } else + path = entry->name; + if(entry->dev) + ERROR("%s %c 0%o %d %d %d %d\n", path, entry->dev->type, + entry->dev->mode & ~S_IFMT, entry->dev->uid, + entry->dev->gid, entry->dev->major, + entry->dev->minor); + if(entry->pseudo) + dump_pseudo(entry->pseudo, path); + if(string) + free(path); + } +} + + +void dump_pseudos() +{ + dump_pseudo(pseudo, NULL); +} +#else +void dump_pseudos() +{ +} +#endif diff -Nru squashfs-tools-4.2+20130409/pseudo.h squashfs-tools-4.3+20140919/pseudo.h --- squashfs-tools-4.2+20130409/pseudo.h 2013-05-09 10:39:11.000000000 +0200 +++ squashfs-tools-4.3+20140919/pseudo.h 2015-07-20 21:03:05.000000000 +0200 @@ -1,8 +1,10 @@ +#ifndef PSEUDO_H +#define PSEUDO_H /* * Create a squashfs filesystem. This is a highly compressed read only * filesystem. * - * Copyright (c) 2009, 2010 + * Copyright (c) 2009, 2010, 2014 * Phillip Lougher * * This program is free software; you can redistribute it and/or @@ -29,11 +31,7 @@ unsigned int major; unsigned int minor; int pseudo_id; - int fd; - int child; -#ifdef USE_TMP_FILE - char *filename; -#endif + char *command; }; struct pseudo_entry { @@ -54,5 +52,7 @@ extern struct pseudo *pseudo_subdir(char *, struct pseudo *); extern struct pseudo_entry *pseudo_readdir(struct pseudo *); extern struct pseudo_dev *get_pseudo_file(int); -extern void delete_pseudo_files(); +extern int pseudo_exec_file(struct pseudo_dev *, int *); extern struct pseudo *get_pseudo(); +extern void dump_pseudos(); +#endif diff -Nru squashfs-tools-4.2+20130409/read_fs.c squashfs-tools-4.3+20140919/read_fs.c --- squashfs-tools-4.2+20130409/read_fs.c 2013-05-09 10:39:11.000000000 +0200 +++ squashfs-tools-4.3+20140919/read_fs.c 2015-07-20 21:03:05.000000000 +0200 @@ -3,7 +3,7 @@ * filesystem. * * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, - * 2012, 2013 + * 2012, 2013, 2014 * Phillip Lougher * * This program is free software; you can redistribute it and/or @@ -33,6 +33,7 @@ #include #include #include +#include #ifndef linux #define __BYTE_ORDER BYTE_ORDER @@ -46,19 +47,10 @@ #include "squashfs_fs.h" #include "squashfs_swap.h" -#include "read_fs.h" #include "compressor.h" #include "xattr.h" #include "error.h" - -extern int read_fs_bytes(int, long long, int, void *); -extern int add_file(long long, long long, long long, unsigned int *, int, - unsigned int, int, int); -extern void *create_id(unsigned int); -extern unsigned int get_uid(unsigned int); -extern unsigned int get_guid(unsigned int); - -static struct compressor *comp; +#include "mksquashfs.h" int read_block(int fd, long long start, long long *next, int expected, void *block) @@ -207,7 +199,7 @@ */ *root_inode_size = bytes - (*root_inode_block + root_inode_offset); bytes = *root_inode_block + root_inode_offset; - SQUASHFS_SWAP_DIR_INODE_HEADER(&dir_inode->dir, *inode_table + bytes); + SQUASHFS_SWAP_DIR_INODE_HEADER(*inode_table + bytes, &dir_inode->dir); if(dir_inode->base.inode_type == SQUASHFS_DIR_TYPE) directory_start_block = dir_inode->dir.start_block; @@ -215,8 +207,8 @@ if(*root_inode_size < sizeof(struct squashfs_ldir_inode_header)) /* corrupted filesystem */ goto corrupted; - SQUASHFS_SWAP_LDIR_INODE_HEADER(&dir_inode->ldir, - *inode_table + bytes); + SQUASHFS_SWAP_LDIR_INODE_HEADER(*inode_table + bytes, + &dir_inode->ldir); directory_start_block = dir_inode->ldir.start_block; } else /* bad type, corrupted filesystem */ @@ -225,12 +217,17 @@ get_uid(id_table[dir_inode->base.uid]); get_guid(id_table[dir_inode->base.guid]); + /* allocate fragment to file mapping table */ + file_mapping = calloc(sBlk->fragments, sizeof(struct append_file *)); + if(file_mapping == NULL) + MEM_ERROR(); + for(cur_ptr = *inode_table; cur_ptr < *inode_table + bytes; files ++) { if(NO_INODE_BYTES(squashfs_base_inode_header)) /* corrupted filesystem */ goto corrupted; - SQUASHFS_SWAP_BASE_INODE_HEADER(&base, cur_ptr); + SQUASHFS_SWAP_BASE_INODE_HEADER(cur_ptr, &base); TRACE("scan_inode_table: processing inode @ byte position " "0x%x, type 0x%x\n", @@ -251,7 +248,7 @@ /* corrupted filesystem */ goto corrupted; - SQUASHFS_SWAP_REG_INODE_HEADER(&inode, cur_ptr); + SQUASHFS_SWAP_REG_INODE_HEADER(cur_ptr, &inode); frag_bytes = inode.fragment == SQUASHFS_INVALID_FRAG ? 0 : inode.file_size % sBlk->block_size; @@ -273,7 +270,7 @@ MEM_ERROR(); cur_ptr += sizeof(inode); - SQUASHFS_SWAP_INTS(block_list, cur_ptr, blocks); + SQUASHFS_SWAP_INTS(cur_ptr, block_list, blocks); *uncompressed_file += inode.file_size; (*file_count) ++; @@ -283,6 +280,12 @@ SQUASHFS_COMPRESSED_SIZE_BLOCK (block_list[i]); + if(inode.fragment != SQUASHFS_INVALID_FRAG && + inode.fragment >= sBlk->fragments) { + free(block_list); + goto corrupted; + } + add_file(start, inode.file_size, file_bytes, block_list, blocks, inode.fragment, inode.offset, frag_bytes); @@ -300,7 +303,7 @@ /* corrupted filesystem */ goto corrupted; - SQUASHFS_SWAP_LREG_INODE_HEADER(&inode, cur_ptr); + SQUASHFS_SWAP_LREG_INODE_HEADER(cur_ptr, &inode); frag_bytes = inode.fragment == SQUASHFS_INVALID_FRAG ? 0 : inode.file_size % sBlk->block_size; @@ -323,7 +326,7 @@ MEM_ERROR(); cur_ptr += sizeof(inode); - SQUASHFS_SWAP_INTS(block_list, cur_ptr, blocks); + SQUASHFS_SWAP_INTS(cur_ptr, block_list, blocks); *uncompressed_file += inode.file_size; (*file_count) ++; @@ -333,6 +336,12 @@ SQUASHFS_COMPRESSED_SIZE_BLOCK (block_list[i]); + if(inode.fragment != SQUASHFS_INVALID_FRAG && + inode.fragment >= sBlk->fragments) { + free(block_list); + goto corrupted; + } + add_file(start, inode.file_size, file_bytes, block_list, blocks, inode.fragment, inode.offset, frag_bytes); @@ -348,7 +357,7 @@ /* corrupted filesystem */ goto corrupted; - SQUASHFS_SWAP_SYMLINK_INODE_HEADER(&inode, cur_ptr); + SQUASHFS_SWAP_SYMLINK_INODE_HEADER(cur_ptr, &inode); (*sym_count) ++; @@ -374,7 +383,7 @@ /* corrupted filesystem */ goto corrupted; - SQUASHFS_SWAP_DIR_INODE_HEADER(&dir_inode, cur_ptr); + SQUASHFS_SWAP_DIR_INODE_HEADER(cur_ptr, &dir_inode); if(dir_inode.start_block < directory_start_block) *uncompressed_directory += dir_inode.file_size; @@ -391,7 +400,7 @@ /* corrupted filesystem */ goto corrupted; - SQUASHFS_SWAP_LDIR_INODE_HEADER(&dir_inode, cur_ptr); + SQUASHFS_SWAP_LDIR_INODE_HEADER(cur_ptr, &dir_inode); if(dir_inode.start_block < directory_start_block) *uncompressed_directory += dir_inode.file_size; @@ -406,7 +415,7 @@ /* corrupted filesystem */ goto corrupted; - SQUASHFS_SWAP_DIR_INDEX(&index, cur_ptr); + SQUASHFS_SWAP_DIR_INDEX(cur_ptr, &index); if(NO_BYTES(index.size + 1)) /* corrupted filesystem */ @@ -531,7 +540,7 @@ /* Check the compression type */ comp = lookup_compressor_id(sBlk->compression); if(!comp->supported) { - ERROR("Filesystem on %s uses %s compression, this is" + ERROR("Filesystem on %s uses %s compression, this is " "unsupported by this version\n", source, comp->name); ERROR("Compressors available:\n"); display_compressors("", ""); @@ -548,6 +557,10 @@ * is still called to set the default options (the defaults may have * been changed by the user specifying options on the command * line which need to be over-ridden). + * + * Compressor_extract_options is also used to ensure that + * we know how decompress a filesystem compressed with these + * compression options. */ if(SQUASHFS_COMP_OPTS(sBlk->flags)) { bytes = read_block(fd, sizeof(*sBlk), NULL, 0, buffer); @@ -579,7 +592,7 @@ SQUASHFS_UNCOMPRESSED_XATTRS(sBlk->flags) ? "un" : ""); printf("\tFragments are %spresent in the filesystem\n", SQUASHFS_NO_FRAGMENTS(sBlk->flags) ? "not " : ""); - printf("\tAlways_use_fragments option is %sspecified\n", + printf("\tAlways-use-fragments option is %sspecified\n", SQUASHFS_ALWAYS_FRAGMENTS(sBlk->flags) ? "" : "not "); printf("\tDuplicates are %sremoved\n", SQUASHFS_DUPLICATES(sBlk->flags) ? "" : "not "); @@ -651,7 +664,7 @@ bytes = offset; while(bytes < size) { - SQUASHFS_SWAP_DIR_HEADER(&dirh, directory_table + bytes); + SQUASHFS_SWAP_DIR_HEADER(directory_table + bytes, &dirh); dir_count = dirh.count + 1; TRACE("squashfs_readdir: Read directory header @ byte position " @@ -659,7 +672,7 @@ bytes += sizeof(dirh); while(dir_count--) { - SQUASHFS_SWAP_DIR_ENTRY(dire, directory_table + bytes); + SQUASHFS_SWAP_DIR_ENTRY(directory_table + bytes, dire); bytes += sizeof(*dire); memcpy(dire->name, directory_table + bytes, diff -Nru squashfs-tools-4.2+20130409/read_fs.h squashfs-tools-4.3+20140919/read_fs.h --- squashfs-tools-4.2+20130409/read_fs.h 2013-05-09 10:39:11.000000000 +0200 +++ squashfs-tools-4.3+20140919/read_fs.h 2015-07-20 21:03:05.000000000 +0200 @@ -3,7 +3,7 @@ /* * Squashfs * - * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 + * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2013 * Phillip Lougher * * This program is free software; you can redistribute it and/or @@ -23,20 +23,12 @@ * read_fs.h * */ - -#if __BYTE_ORDER == __BIG_ENDIAN -#define SQUASHFS_SWAP_SHORTS(d, s, n) swap_le16_num(s, d, n) -#define SQUASHFS_SWAP_INTS(d, s, n) swap_le32_num(s, d, n) -#define SQUASHFS_SWAP_LONG_LONGS(d, s, n) swap_le64_num(s, d, n) - -#define SWAP_LE16(d, s) swap_le16(s, d) -#define SWAP_LE32(d, s) swap_le32(s, d) -#define SWAP_LE64(d, s) swap_le64(s, d) -#else -#define SQUASHFS_MEMCPY(d, s, n) memcpy(d, s, n) -#define SQUASHFS_SWAP_SHORTS(d, s, n) memcpy(d, s, n * sizeof(short)) -#define SQUASHFS_SWAP_INTS(d, s, n) memcpy(d, s, n * sizeof(int)) -#define SQUASHFS_SWAP_LONG_LONGS(d, s, n) \ - memcpy(d, s, n * sizeof(long long)) -#endif +extern struct compressor *read_super(int, struct squashfs_super_block *, + char *); +extern long long read_filesystem(char *, int, struct squashfs_super_block *, +char **, char **, char **, char **, unsigned int *, unsigned int *, +unsigned int *, unsigned int *, unsigned int *, int *, int *, int *, int *, +int *, int *, long long *, unsigned int *, unsigned int *, unsigned int *, +unsigned int *, void (push_directory_entry)(char *, squashfs_inode, int, int), +struct squashfs_fragment_entry **, squashfs_inode **); #endif diff -Nru squashfs-tools-4.2+20130409/read_xattrs.c squashfs-tools-4.3+20140919/read_xattrs.c --- squashfs-tools-4.2+20130409/read_xattrs.c 2013-05-09 10:39:11.000000000 +0200 +++ squashfs-tools-4.3+20140919/read_xattrs.c 2015-07-20 21:03:05.000000000 +0200 @@ -41,7 +41,6 @@ #include "squashfs_fs.h" #include "squashfs_swap.h" -#include "read_fs.h" #include "xattr.h" #include "error.h" @@ -338,7 +337,7 @@ MEM_ERROR(); } - SQUASHFS_SWAP_XATTR_ENTRY(&entry, xptr); + SQUASHFS_SWAP_XATTR_ENTRY(xptr, &entry); xptr += sizeof(entry); res = read_xattr_entry(&xattr_list[j], &entry, xptr); @@ -361,15 +360,15 @@ void *ool_xptr; xptr += sizeof(val); - SQUASHFS_SWAP_LONG_LONGS(&xattr, xptr, 1); + SQUASHFS_SWAP_LONG_LONGS(xptr, &xattr, 1); xptr += sizeof(xattr); start = SQUASHFS_XATTR_BLK(xattr) + xattr_table_start; offset = SQUASHFS_XATTR_OFFSET(xattr); ool_xptr = xattrs + get_xattr_block(start) + offset; - SQUASHFS_SWAP_XATTR_VAL(&val, ool_xptr); + SQUASHFS_SWAP_XATTR_VAL(ool_xptr, &val); xattr_list[j].value = ool_xptr + sizeof(val); } else { - SQUASHFS_SWAP_XATTR_VAL(&val, xptr); + SQUASHFS_SWAP_XATTR_VAL(xptr, &val); xattr_list[j].value = xptr + sizeof(val); xptr += sizeof(val) + val.vsize; } diff -Nru squashfs-tools-4.2+20130409/restore.c squashfs-tools-4.3+20140919/restore.c --- squashfs-tools-4.2+20130409/restore.c 2013-05-09 10:39:11.000000000 +0200 +++ squashfs-tools-4.3+20140919/restore.c 2015-07-20 21:03:05.000000000 +0200 @@ -2,7 +2,7 @@ * Create a squashfs filesystem. This is a highly compressed read only * filesystem. * - * Copyright (c) 2013 + * Copyright (c) 2013, 2014 * Phillip Lougher * * This program is free software; you can redistribute it and/or @@ -36,22 +36,30 @@ #include #include +#include "caches-queues-lists.h" #include "squashfs_fs.h" #include "mksquashfs.h" #include "error.h" #include "progressbar.h" #include "info.h" -pthread_t restore_thread, main_thread; -int interrupted = 0; +#define FALSE 0 +#define TRUE 1 +extern pthread_t reader_thread, writer_thread, main_thread; +extern pthread_t *deflator_thread, *frag_deflator_thread, *frag_thread; +extern struct queue *to_deflate, *to_writer, *to_frag, *to_process_frag; +extern struct seq_queue *to_main; extern void restorefs(); +extern int processors; +static int interrupted = 0; +static pthread_t restore_thread; void *restore_thrd(void *arg) { sigset_t sigmask, old_mask; - int sig; + int i, sig; sigemptyset(&sigmask); sigaddset(&sigmask, SIGINT); @@ -62,31 +70,86 @@ while(1) { sigwait(&sigmask, &sig); - if(sig == SIGINT || sig == SIGTERM) { - interrupted ++; - - if(interrupted == 1) { - ERROR("Interrupting will restore original " - "filesystem!\n"); - ERROR("Interrupt again to quit\n"); - } + if((sig == SIGINT || sig == SIGTERM) && !interrupted) { + ERROR("Interrupting will restore original " + "filesystem!\n"); + ERROR("Interrupt again to quit\n"); + interrupted = TRUE; + continue; } - if(interrupted == 2 || sig == SIGUSR1) { - disable_progress_bar(); - disable_info(); - pthread_cancel(main_thread); - pthread_join(main_thread, NULL); + /* kill main thread/worker threads and restore */ + set_progressbar_state(FALSE); + disable_info(); + + /* first kill the reader thread */ + pthread_cancel(reader_thread); + pthread_join(reader_thread, NULL); + + /* + * then flush the reader to deflator thread(s) output queue. + * The deflator thread(s) will idle + */ + queue_flush(to_deflate); + + /* now kill the deflator thread(s) */ + for(i = 0; i < processors; i++) + pthread_cancel(deflator_thread[i]); + for(i = 0; i < processors; i++) + pthread_join(deflator_thread[i], NULL); + + /* + * then flush the reader to process fragment thread(s) output + * queue. The process fragment thread(s) will idle + */ + queue_flush(to_process_frag); + + /* now kill the process fragment thread(s) */ + for(i = 0; i < processors; i++) + pthread_cancel(frag_thread[i]); + for(i = 0; i < processors; i++) + pthread_join(frag_thread[i], NULL); + + /* + * then flush the reader/deflator/process fragment to main + * thread output queue. The main thread will idle + */ + seq_queue_flush(to_main); + + /* now kill the main thread */ + pthread_cancel(main_thread); + pthread_join(main_thread, NULL); + + /* then flush the main thread to fragment deflator thread(s) + * queue. The fragment deflator thread(s) will idle + */ + queue_flush(to_frag); + + /* now kill the fragment deflator thread(s) */ + for(i = 0; i < processors; i++) + pthread_cancel(frag_deflator_thread[i]); + for(i = 0; i < processors; i++) + pthread_join(frag_deflator_thread[i], NULL); + + /* + * then flush the main thread/fragment deflator thread(s) + * to writer thread queue. The writer thread will idle + */ + queue_flush(to_writer); + + /* now kill the writer thread */ + pthread_cancel(writer_thread); + pthread_join(writer_thread, NULL); - restorefs(); - } + TRACE("All threads cancelled\n"); + + restorefs(); } } -pthread_t *init_restore_thread(pthread_t thread) +pthread_t *init_restore_thread() { - main_thread = thread; pthread_create(&restore_thread, NULL, restore_thrd, NULL); return &restore_thread; } diff -Nru squashfs-tools-4.2+20130409/restore.h squashfs-tools-4.3+20140919/restore.h --- squashfs-tools-4.2+20130409/restore.h 1970-01-01 01:00:00.000000000 +0100 +++ squashfs-tools-4.3+20140919/restore.h 2015-07-20 21:03:05.000000000 +0200 @@ -0,0 +1,28 @@ +#ifndef RESTORE_H +#define RESTORE_H +/* + * Create a squashfs filesystem. This is a highly compressed read only + * filesystem. + * + * Copyright (c) 2013, 2014 + * Phillip Lougher + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2, + * or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * restore.h + */ + +extern pthread_t *init_restore_thread(); +#endif diff -Nru squashfs-tools-4.2+20130409/sort.c squashfs-tools-4.3+20140919/sort.c --- squashfs-tools-4.2+20130409/sort.c 2013-05-09 10:39:11.000000000 +0200 +++ squashfs-tools-4.3+20140919/sort.c 2015-07-20 21:03:05.000000000 +0200 @@ -3,7 +3,7 @@ * filesystem. * * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2012, - * 2013 + * 2013, 2014 * Phillip Lougher * * This program is free software; you can redistribute it and/or @@ -42,6 +42,7 @@ #include "mksquashfs.h" #include "sort.h" #include "error.h" +#include "progressbar.h" int mkisofs_style = -1; @@ -170,9 +171,10 @@ } error: - ERROR("Cannot stat sortlist entry \"%s\"\n", path); + ERROR_START("Cannot stat sortlist entry \"%s\"\n", path); ERROR("This is probably because you're using the wrong file\n"); - ERROR("path relative to the source directories\n"); + ERROR("path relative to the source directories."); + ERROR_EXIT(" Ignoring"); /* * Historical note * Failure to stat a sortlist entry is deliberately ignored, even diff -Nru squashfs-tools-4.2+20130409/sort.h squashfs-tools-4.3+20140919/sort.h --- squashfs-tools-4.2+20130409/sort.h 2013-05-09 10:39:11.000000000 +0200 +++ squashfs-tools-4.3+20140919/sort.h 2015-07-20 21:03:05.000000000 +0200 @@ -4,7 +4,7 @@ /* * Squashfs * - * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 + * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2013 * Phillip Lougher * * This program is free software; you can redistribute it and/or @@ -28,4 +28,10 @@ struct dir_ent *dir; struct priority_entry *next; }; + +extern int read_sort_file(char *, int, char *[]); +extern void sort_files_and_write(struct dir_info *); +extern void generate_file_priorities(struct dir_info *, int priority, + struct stat *); +extern struct priority_entry *priority_list[65536]; #endif diff -Nru squashfs-tools-4.2+20130409/squashfs_compat.h squashfs-tools-4.3+20140919/squashfs_compat.h --- squashfs-tools-4.2+20130409/squashfs_compat.h 2013-05-09 10:39:11.000000000 +0200 +++ squashfs-tools-4.3+20140919/squashfs_compat.h 2015-07-20 21:03:05.000000000 +0200 @@ -3,7 +3,7 @@ /* * Squashfs * - * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 + * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2014 * Phillip Lougher * * This program is free software; you can redistribute it and/or @@ -50,7 +50,7 @@ unsigned int flags:8; unsigned int no_uids:8; unsigned int no_guids:8; - unsigned int mkfs_time /* time of filesystem creation */; + int mkfs_time /* time of filesystem creation */; squashfs_inode root_inode; unsigned int block_size; unsigned int fragments; @@ -71,38 +71,55 @@ unsigned char name[0]; } __attribute__ ((packed)); -#define SQUASHFS_BASE_INODE_HEADER_3 \ - unsigned int inode_type:4; \ - unsigned int mode:12; \ - unsigned int uid:8; \ - unsigned int guid:8; \ - unsigned int mtime; \ - unsigned int inode_number; - struct squashfs_base_inode_header_3 { - SQUASHFS_BASE_INODE_HEADER_3; + unsigned int inode_type:4; + unsigned int mode:12; + unsigned int uid:8; + unsigned int guid:8; + int mtime; + unsigned int inode_number; } __attribute__ ((packed)); struct squashfs_ipc_inode_header_3 { - SQUASHFS_BASE_INODE_HEADER_3; + unsigned int inode_type:4; + unsigned int mode:12; + unsigned int uid:8; + unsigned int guid:8; + int mtime; + unsigned int inode_number; unsigned int nlink; } __attribute__ ((packed)); struct squashfs_dev_inode_header_3 { - SQUASHFS_BASE_INODE_HEADER_3; + unsigned int inode_type:4; + unsigned int mode:12; + unsigned int uid:8; + unsigned int guid:8; + int mtime; + unsigned int inode_number; unsigned int nlink; unsigned short rdev; } __attribute__ ((packed)); struct squashfs_symlink_inode_header_3 { - SQUASHFS_BASE_INODE_HEADER_3; + unsigned int inode_type:4; + unsigned int mode:12; + unsigned int uid:8; + unsigned int guid:8; + int mtime; + unsigned int inode_number; unsigned int nlink; unsigned short symlink_size; char symlink[0]; } __attribute__ ((packed)); struct squashfs_reg_inode_header_3 { - SQUASHFS_BASE_INODE_HEADER_3; + unsigned int inode_type:4; + unsigned int mode:12; + unsigned int uid:8; + unsigned int guid:8; + int mtime; + unsigned int inode_number; squashfs_block start_block; unsigned int fragment; unsigned int offset; @@ -111,7 +128,12 @@ } __attribute__ ((packed)); struct squashfs_lreg_inode_header_3 { - SQUASHFS_BASE_INODE_HEADER_3; + unsigned int inode_type:4; + unsigned int mode:12; + unsigned int uid:8; + unsigned int guid:8; + int mtime; + unsigned int inode_number; unsigned int nlink; squashfs_block start_block; unsigned int fragment; @@ -121,7 +143,12 @@ } __attribute__ ((packed)); struct squashfs_dir_inode_header_3 { - SQUASHFS_BASE_INODE_HEADER_3; + unsigned int inode_type:4; + unsigned int mode:12; + unsigned int uid:8; + unsigned int guid:8; + int mtime; + unsigned int inode_number; unsigned int nlink; unsigned int file_size:19; unsigned int offset:13; @@ -130,7 +157,12 @@ } __attribute__ ((packed)); struct squashfs_ldir_inode_header_3 { - SQUASHFS_BASE_INODE_HEADER_3; + unsigned int inode_type:4; + unsigned int mode:12; + unsigned int uid:8; + unsigned int guid:8; + int mtime; + unsigned int inode_number; unsigned int nlink; unsigned int file_size:27; unsigned int offset:13; @@ -449,7 +481,7 @@ unsigned int mode:12; /* protection */ unsigned int uid:4; /* index into uid table */ unsigned int guid:4; /* index into guid table */ - unsigned int mtime; + int mtime; unsigned int start_block; unsigned int file_size:32; unsigned short block_list[0]; @@ -462,7 +494,7 @@ unsigned int guid:4; /* index into guid table */ unsigned int file_size:19; unsigned int offset:13; - unsigned int mtime; + int mtime; unsigned int start_block:24; } __attribute__ ((packed)); @@ -582,7 +614,7 @@ unsigned int mode:12; /* protection */ unsigned int uid:8; /* index into uid table */ unsigned int guid:8; /* index into guid table */ - unsigned int mtime; + int mtime; unsigned int start_block; unsigned int fragment; unsigned int offset; @@ -597,7 +629,7 @@ unsigned int guid:8; /* index into guid table */ unsigned int file_size:19; unsigned int offset:13; - unsigned int mtime; + int mtime; unsigned int start_block:24; } __attribute__ ((packed)); @@ -608,7 +640,7 @@ unsigned int guid:8; /* index into guid table */ unsigned int file_size:27; unsigned int offset:13; - unsigned int mtime; + int mtime; unsigned int start_block:24; unsigned int i_count:16; struct squashfs_dir_index_2 index[0]; diff -Nru squashfs-tools-4.2+20130409/squashfs_fs.h squashfs-tools-4.3+20140919/squashfs_fs.h --- squashfs-tools-4.2+20130409/squashfs_fs.h 2013-05-09 10:39:11.000000000 +0200 +++ squashfs-tools-4.3+20140919/squashfs_fs.h 2015-07-20 21:03:05.000000000 +0200 @@ -4,7 +4,7 @@ * Squashfs * * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2012, - * 2013 + * 2013, 2014 * Phillip Lougher * * This program is free software; you can redistribute it and/or @@ -276,11 +276,12 @@ #define LZMA_COMPRESSION 2 #define LZO_COMPRESSION 3 #define XZ_COMPRESSION 4 +#define LZ4_COMPRESSION 5 struct squashfs_super_block { unsigned int s_magic; unsigned int inodes; - unsigned int mkfs_time /* time of filesystem creation */; + int mkfs_time /* time of filesystem creation */; unsigned int block_size; unsigned int fragments; unsigned short compression; @@ -311,7 +312,7 @@ unsigned short mode; unsigned short uid; unsigned short guid; - unsigned int mtime; + int mtime; unsigned int inode_number; }; @@ -320,7 +321,7 @@ unsigned short mode; unsigned short uid; unsigned short guid; - unsigned int mtime; + int mtime; unsigned int inode_number; unsigned int nlink; }; @@ -330,7 +331,7 @@ unsigned short mode; unsigned short uid; unsigned short guid; - unsigned int mtime; + int mtime; unsigned int inode_number; unsigned int nlink; unsigned int xattr; @@ -341,7 +342,7 @@ unsigned short mode; unsigned short uid; unsigned short guid; - unsigned int mtime; + int mtime; unsigned int inode_number; unsigned int nlink; unsigned int rdev; @@ -352,7 +353,7 @@ unsigned short mode; unsigned short uid; unsigned short guid; - unsigned int mtime; + int mtime; unsigned int inode_number; unsigned int nlink; unsigned int rdev; @@ -364,7 +365,7 @@ unsigned short mode; unsigned short uid; unsigned short guid; - unsigned int mtime; + int mtime; unsigned int inode_number; unsigned int nlink; unsigned int symlink_size; @@ -376,7 +377,7 @@ unsigned short mode; unsigned short uid; unsigned short guid; - unsigned int mtime; + int mtime; unsigned int inode_number; unsigned int start_block; unsigned int fragment; @@ -390,7 +391,7 @@ unsigned short mode; unsigned short uid; unsigned short guid; - unsigned int mtime; + int mtime; unsigned int inode_number; squashfs_block start_block; long long file_size; @@ -407,7 +408,7 @@ unsigned short mode; unsigned short uid; unsigned short guid; - unsigned int mtime; + int mtime; unsigned int inode_number; unsigned int start_block; unsigned int nlink; @@ -421,7 +422,7 @@ unsigned short mode; unsigned short uid; unsigned short guid; - unsigned int mtime; + int mtime; unsigned int inode_number; unsigned int nlink; unsigned int file_size; diff -Nru squashfs-tools-4.2+20130409/squashfs_swap.h squashfs-tools-4.3+20140919/squashfs_swap.h --- squashfs-tools-4.2+20130409/squashfs_swap.h 2013-05-09 10:39:11.000000000 +0200 +++ squashfs-tools-4.3+20140919/squashfs_swap.h 2015-07-20 21:03:05.000000000 +0200 @@ -3,7 +3,7 @@ /* * Squashfs * - * Copyright (c) 2008, 2009, 2010 + * Copyright (c) 2008, 2009, 2010, 2013, 2014 * Phillip Lougher * * This program is free software; you can redistribute it and/or @@ -45,7 +45,7 @@ #define _SQUASHFS_SWAP_SUPER_BLOCK(s, d, SWAP_FUNC) {\ SWAP_FUNC(32, s, d, s_magic, struct squashfs_super_block);\ SWAP_FUNC(32, s, d, inodes, struct squashfs_super_block);\ - SWAP_FUNC(32, s, d, mkfs_time, struct squashfs_super_block);\ + SWAP_FUNC##S(32, s, d, mkfs_time, struct squashfs_super_block);\ SWAP_FUNC(32, s, d, block_size, struct squashfs_super_block);\ SWAP_FUNC(32, s, d, fragments, struct squashfs_super_block);\ SWAP_FUNC(16, s, d, compression, struct squashfs_super_block);\ @@ -75,7 +75,7 @@ SWAP_FUNC(16, s, d, mode, struct squashfs_base_inode_header);\ SWAP_FUNC(16, s, d, uid, struct squashfs_base_inode_header);\ SWAP_FUNC(16, s, d, guid, struct squashfs_base_inode_header);\ - SWAP_FUNC(32, s, d, mtime, struct squashfs_base_inode_header);\ + SWAP_FUNC##S(32, s, d, mtime, struct squashfs_base_inode_header);\ SWAP_FUNC(32, s, d, inode_number, struct squashfs_base_inode_header);\ } @@ -84,7 +84,7 @@ SWAP_FUNC(16, s, d, mode, struct squashfs_ipc_inode_header);\ SWAP_FUNC(16, s, d, uid, struct squashfs_ipc_inode_header);\ SWAP_FUNC(16, s, d, guid, struct squashfs_ipc_inode_header);\ - SWAP_FUNC(32, s, d, mtime, struct squashfs_ipc_inode_header);\ + SWAP_FUNC##S(32, s, d, mtime, struct squashfs_ipc_inode_header);\ SWAP_FUNC(32, s, d, inode_number, struct squashfs_ipc_inode_header);\ SWAP_FUNC(32, s, d, nlink, struct squashfs_ipc_inode_header);\ } @@ -94,7 +94,7 @@ SWAP_FUNC(16, s, d, mode, struct squashfs_lipc_inode_header);\ SWAP_FUNC(16, s, d, uid, struct squashfs_lipc_inode_header);\ SWAP_FUNC(16, s, d, guid, struct squashfs_lipc_inode_header);\ - SWAP_FUNC(32, s, d, mtime, struct squashfs_lipc_inode_header);\ + SWAP_FUNC##S(32, s, d, mtime, struct squashfs_lipc_inode_header);\ SWAP_FUNC(32, s, d, inode_number, struct squashfs_lipc_inode_header);\ SWAP_FUNC(32, s, d, nlink, struct squashfs_lipc_inode_header);\ SWAP_FUNC(32, s, d, xattr, struct squashfs_lipc_inode_header);\ @@ -105,7 +105,7 @@ SWAP_FUNC(16, s, d, mode, struct squashfs_dev_inode_header);\ SWAP_FUNC(16, s, d, uid, struct squashfs_dev_inode_header);\ SWAP_FUNC(16, s, d, guid, struct squashfs_dev_inode_header);\ - SWAP_FUNC(32, s, d, mtime, struct squashfs_dev_inode_header);\ + SWAP_FUNC##S(32, s, d, mtime, struct squashfs_dev_inode_header);\ SWAP_FUNC(32, s, d, inode_number, struct squashfs_dev_inode_header);\ SWAP_FUNC(32, s, d, nlink, struct squashfs_dev_inode_header);\ SWAP_FUNC(32, s, d, rdev, struct squashfs_dev_inode_header);\ @@ -116,7 +116,7 @@ SWAP_FUNC(16, s, d, mode, struct squashfs_ldev_inode_header);\ SWAP_FUNC(16, s, d, uid, struct squashfs_ldev_inode_header);\ SWAP_FUNC(16, s, d, guid, struct squashfs_ldev_inode_header);\ - SWAP_FUNC(32, s, d, mtime, struct squashfs_ldev_inode_header);\ + SWAP_FUNC##S(32, s, d, mtime, struct squashfs_ldev_inode_header);\ SWAP_FUNC(32, s, d, inode_number, struct squashfs_ldev_inode_header);\ SWAP_FUNC(32, s, d, nlink, struct squashfs_ldev_inode_header);\ SWAP_FUNC(32, s, d, rdev, struct squashfs_ldev_inode_header);\ @@ -128,7 +128,7 @@ SWAP_FUNC(16, s, d, mode, struct squashfs_symlink_inode_header);\ SWAP_FUNC(16, s, d, uid, struct squashfs_symlink_inode_header);\ SWAP_FUNC(16, s, d, guid, struct squashfs_symlink_inode_header);\ - SWAP_FUNC(32, s, d, mtime, struct squashfs_symlink_inode_header);\ + SWAP_FUNC##S(32, s, d, mtime, struct squashfs_symlink_inode_header);\ SWAP_FUNC(32, s, d, inode_number, struct squashfs_symlink_inode_header);\ SWAP_FUNC(32, s, d, nlink, struct squashfs_symlink_inode_header);\ SWAP_FUNC(32, s, d, symlink_size, struct squashfs_symlink_inode_header);\ @@ -139,7 +139,7 @@ SWAP_FUNC(16, s, d, mode, struct squashfs_reg_inode_header);\ SWAP_FUNC(16, s, d, uid, struct squashfs_reg_inode_header);\ SWAP_FUNC(16, s, d, guid, struct squashfs_reg_inode_header);\ - SWAP_FUNC(32, s, d, mtime, struct squashfs_reg_inode_header);\ + SWAP_FUNC##S(32, s, d, mtime, struct squashfs_reg_inode_header);\ SWAP_FUNC(32, s, d, inode_number, struct squashfs_reg_inode_header);\ SWAP_FUNC(32, s, d, start_block, struct squashfs_reg_inode_header);\ SWAP_FUNC(32, s, d, fragment, struct squashfs_reg_inode_header);\ @@ -152,7 +152,7 @@ SWAP_FUNC(16, s, d, mode, struct squashfs_lreg_inode_header);\ SWAP_FUNC(16, s, d, uid, struct squashfs_lreg_inode_header);\ SWAP_FUNC(16, s, d, guid, struct squashfs_lreg_inode_header);\ - SWAP_FUNC(32, s, d, mtime, struct squashfs_lreg_inode_header);\ + SWAP_FUNC##S(32, s, d, mtime, struct squashfs_lreg_inode_header);\ SWAP_FUNC(32, s, d, inode_number, struct squashfs_lreg_inode_header);\ SWAP_FUNC(64, s, d, start_block, struct squashfs_lreg_inode_header);\ SWAP_FUNC(64, s, d, file_size, struct squashfs_lreg_inode_header);\ @@ -168,7 +168,7 @@ SWAP_FUNC(16, s, d, mode, struct squashfs_dir_inode_header);\ SWAP_FUNC(16, s, d, uid, struct squashfs_dir_inode_header);\ SWAP_FUNC(16, s, d, guid, struct squashfs_dir_inode_header);\ - SWAP_FUNC(32, s, d, mtime, struct squashfs_dir_inode_header);\ + SWAP_FUNC##S(32, s, d, mtime, struct squashfs_dir_inode_header);\ SWAP_FUNC(32, s, d, inode_number, struct squashfs_dir_inode_header);\ SWAP_FUNC(32, s, d, start_block, struct squashfs_dir_inode_header);\ SWAP_FUNC(32, s, d, nlink, struct squashfs_dir_inode_header);\ @@ -182,7 +182,7 @@ SWAP_FUNC(16, s, d, mode, struct squashfs_ldir_inode_header);\ SWAP_FUNC(16, s, d, uid, struct squashfs_ldir_inode_header);\ SWAP_FUNC(16, s, d, guid, struct squashfs_ldir_inode_header);\ - SWAP_FUNC(32, s, d, mtime, struct squashfs_ldir_inode_header);\ + SWAP_FUNC##S(32, s, d, mtime, struct squashfs_ldir_inode_header);\ SWAP_FUNC(32, s, d, inode_number, struct squashfs_ldir_inode_header);\ SWAP_FUNC(32, s, d, nlink, struct squashfs_ldir_inode_header);\ SWAP_FUNC(32, s, d, file_size, struct squashfs_ldir_inode_header);\ @@ -281,6 +281,14 @@ #define SQUASHFS_SWAP_LOOKUP_BLOCKS(s, d, n) SQUASHFS_SWAP_LONG_LONGS(s, d, n) #define SQUASHFS_SWAP_ID_BLOCKS(s, d, n) SQUASHFS_SWAP_LONG_LONGS(s, d, n) +#define SQUASHFS_SWAP_SHORTS(s, d, n) swap_le16_num(s, d, n) +#define SQUASHFS_SWAP_INTS(s, d, n) swap_le32_num(s, d, n) +#define SQUASHFS_SWAP_LONG_LONGS(s, d, n) swap_le64_num(s, d, n) + +#define SWAP_LE16(s, d) swap_le16(s, d) +#define SWAP_LE32(s, d) swap_le32(s, d) +#define SWAP_LE64(s, d) swap_le64(s, d) + /* big endian architecture swap in-place macros */ #define SQUASHFS_INSWAP_SUPER_BLOCK(s) \ _SQUASHFS_SWAP_SUPER_BLOCK(s, s, INSWAP_LE) @@ -323,8 +331,9 @@ #define INSWAP_LE(bits, s, d, field, type) \ (s)->field = inswap_le##bits((s)->field) #define INSWAP_LES(bits, s, d, field, type) \ - (s)->field = (short) inswap_le##bits((unsigned short) \ - (s)->field) + (s)->field = INSWAP_LES##bits((s)->field) +#define INSWAP_LES16(num) (short) inswap_le16((unsigned short) (num)) +#define INSWAP_LES32(num) (int) inswap_le32((unsigned int) (num)) #define SQUASHFS_INSWAP_INODE_T(s) s = inswap_le64(s) #define SQUASHFS_INSWAP_FRAGMENT_INDEXES(s, n) inswap_le64_num(s, n) #define SQUASHFS_INSWAP_LOOKUP_BLOCKS(s, n) inswap_le64_num(s, n) @@ -335,49 +344,55 @@ #else /* little endian architecture, just copy */ #define SQUASHFS_SWAP_SUPER_BLOCK(s, d) \ - SQUASHFS_MEMCPY(s, d, sizeof(*(s))) + SQUASHFS_MEMCPY(s, d, sizeof(struct squashfs_super_block)) #define SQUASHFS_SWAP_DIR_INDEX(s, d) \ - SQUASHFS_MEMCPY(s, d, sizeof(*(s))) + SQUASHFS_MEMCPY(s, d, sizeof(struct squashfs_dir_index)) #define SQUASHFS_SWAP_BASE_INODE_HEADER(s, d) \ - SQUASHFS_MEMCPY(s, d, sizeof(*(s))) + SQUASHFS_MEMCPY(s, d, sizeof(struct squashfs_base_inode_header)) #define SQUASHFS_SWAP_IPC_INODE_HEADER(s, d) \ - SQUASHFS_MEMCPY(s, d, sizeof(*(s))) + SQUASHFS_MEMCPY(s, d, sizeof(struct squashfs_ipc_inode_header)) #define SQUASHFS_SWAP_LIPC_INODE_HEADER(s, d) \ - SQUASHFS_MEMCPY(s, d, sizeof(*(s))) + SQUASHFS_MEMCPY(s, d, sizeof(struct squashfs_lipc_inode_header)) #define SQUASHFS_SWAP_DEV_INODE_HEADER(s, d) \ - SQUASHFS_MEMCPY(s, d, sizeof(*(s))) + SQUASHFS_MEMCPY(s, d, sizeof(struct squashfs_dev_inode_header)) #define SQUASHFS_SWAP_LDEV_INODE_HEADER(s, d) \ - SQUASHFS_MEMCPY(s, d, sizeof(*(s))) + SQUASHFS_MEMCPY(s, d, sizeof(struct squashfs_ldev_inode_header)) #define SQUASHFS_SWAP_SYMLINK_INODE_HEADER(s, d) \ - SQUASHFS_MEMCPY(s, d, sizeof(*(s))) + SQUASHFS_MEMCPY(s, d, sizeof(struct squashfs_symlink_inode_header)) #define SQUASHFS_SWAP_REG_INODE_HEADER(s, d) \ - SQUASHFS_MEMCPY(s, d, sizeof(*(s))) + SQUASHFS_MEMCPY(s, d, sizeof(struct squashfs_reg_inode_header)) #define SQUASHFS_SWAP_LREG_INODE_HEADER(s, d) \ - SQUASHFS_MEMCPY(s, d, sizeof(*(s))) + SQUASHFS_MEMCPY(s, d, sizeof(struct squashfs_lreg_inode_header)) #define SQUASHFS_SWAP_DIR_INODE_HEADER(s, d) \ - SQUASHFS_MEMCPY(s, d, sizeof(*(s))) + SQUASHFS_MEMCPY(s, d, sizeof(struct squashfs_dir_inode_header)) #define SQUASHFS_SWAP_LDIR_INODE_HEADER(s, d) \ - SQUASHFS_MEMCPY(s, d, sizeof(*(s))) + SQUASHFS_MEMCPY(s, d, sizeof(struct squashfs_ldir_inode_header)) #define SQUASHFS_SWAP_DIR_ENTRY(s, d) \ - SQUASHFS_MEMCPY(s, d, sizeof(*(s))) + SQUASHFS_MEMCPY(s, d, sizeof(struct squashfs_dir_entry)) #define SQUASHFS_SWAP_DIR_HEADER(s, d) \ - SQUASHFS_MEMCPY(s, d, sizeof(*(s))) + SQUASHFS_MEMCPY(s, d, sizeof(struct squashfs_dir_header)) #define SQUASHFS_SWAP_FRAGMENT_ENTRY(s, d) \ - SQUASHFS_MEMCPY(s, d, sizeof(*(s))) + SQUASHFS_MEMCPY(s, d, sizeof(struct squashfs_fragment_entry)) #define SQUASHFS_SWAP_XATTR_ENTRY(s, d) \ - SQUASHFS_MEMCPY(s, d, sizeof(*(s))) + SQUASHFS_MEMCPY(s, d, sizeof(struct squashfs_xattr_entry)) #define SQUASHFS_SWAP_XATTR_VAL(s, d) \ - SQUASHFS_MEMCPY(s, d, sizeof(*(s))) + SQUASHFS_MEMCPY(s, d, sizeof(struct squashfs_xattr_val)) #define SQUASHFS_SWAP_XATTR_ID(s, d) \ - SQUASHFS_MEMCPY(s, d, sizeof(*(s))) + SQUASHFS_MEMCPY(s, d, sizeof(struct squashfs_xattr_id)) #define SQUASHFS_SWAP_XATTR_TABLE(s, d) \ - SQUASHFS_MEMCPY(s, d, sizeof(*(s))) + SQUASHFS_MEMCPY(s, d, sizeof(struct squashfs_xattr_table)) #define SQUASHFS_SWAP_INODE_T(s, d) SQUASHFS_SWAP_LONG_LONGS(s, d, 1) #define SQUASHFS_SWAP_FRAGMENT_INDEXES(s, d, n) \ SQUASHFS_SWAP_LONG_LONGS(s, d, n) #define SQUASHFS_SWAP_LOOKUP_BLOCKS(s, d, n) SQUASHFS_SWAP_LONG_LONGS(s, d, n) #define SQUASHFS_SWAP_ID_BLOCKS(s, d, n) SQUASHFS_SWAP_LONG_LONGS(s, d, n) +#define SQUASHFS_MEMCPY(s, d, n) memcpy(d, s, n) +#define SQUASHFS_SWAP_SHORTS(s, d, n) memcpy(d, s, n * sizeof(short)) +#define SQUASHFS_SWAP_INTS(s, d, n) memcpy(d, s, n * sizeof(int)) +#define SQUASHFS_SWAP_LONG_LONGS(s, d, n) \ + memcpy(d, s, n * sizeof(long long)) + /* little endian architecture, data already in place so do nothing */ #define SQUASHFS_INSWAP_SUPER_BLOCK(s) #define SQUASHFS_INSWAP_DIR_INDEX(s) diff -Nru squashfs-tools-4.2+20130409/unsquash-4.c squashfs-tools-4.3+20140919/unsquash-4.c --- squashfs-tools-4.2+20130409/unsquash-4.c 2013-05-09 10:39:11.000000000 +0200 +++ squashfs-tools-4.3+20140919/unsquash-4.c 2015-07-20 21:03:05.000000000 +0200 @@ -24,7 +24,6 @@ #include "unsquashfs.h" #include "squashfs_swap.h" -#include "read_fs.h" static struct squashfs_fragment_entry *fragment_table; static unsigned int *id_table; @@ -109,7 +108,7 @@ EXIT_UNSQUASH("read_inode: inode table block %lld not found\n", start); - SQUASHFS_SWAP_BASE_INODE_HEADER(&header.base, block_ptr); + SQUASHFS_SWAP_BASE_INODE_HEADER(block_ptr, &header.base); i.uid = (uid_t) id_table[header.base.uid]; i.gid = (uid_t) id_table[header.base.guid]; @@ -122,7 +121,7 @@ case SQUASHFS_DIR_TYPE: { struct squashfs_dir_inode_header *inode = &header.dir; - SQUASHFS_SWAP_DIR_INODE_HEADER(inode, block_ptr); + SQUASHFS_SWAP_DIR_INODE_HEADER(block_ptr, inode); i.data = inode->file_size; i.offset = inode->offset; @@ -133,7 +132,7 @@ case SQUASHFS_LDIR_TYPE: { struct squashfs_ldir_inode_header *inode = &header.ldir; - SQUASHFS_SWAP_LDIR_INODE_HEADER(inode, block_ptr); + SQUASHFS_SWAP_LDIR_INODE_HEADER(block_ptr, inode); i.data = inode->file_size; i.offset = inode->offset; @@ -144,7 +143,7 @@ case SQUASHFS_FILE_TYPE: { struct squashfs_reg_inode_header *inode = &header.reg; - SQUASHFS_SWAP_REG_INODE_HEADER(inode, block_ptr); + SQUASHFS_SWAP_REG_INODE_HEADER(block_ptr, inode); i.data = inode->file_size; i.frag_bytes = inode->fragment == SQUASHFS_INVALID_FRAG @@ -164,7 +163,7 @@ case SQUASHFS_LREG_TYPE: { struct squashfs_lreg_inode_header *inode = &header.lreg; - SQUASHFS_SWAP_LREG_INODE_HEADER(inode, block_ptr); + SQUASHFS_SWAP_LREG_INODE_HEADER(block_ptr, inode); i.data = inode->file_size; i.frag_bytes = inode->fragment == SQUASHFS_INVALID_FRAG @@ -185,7 +184,7 @@ case SQUASHFS_LSYMLINK_TYPE: { struct squashfs_symlink_inode_header *inode = &header.symlink; - SQUASHFS_SWAP_SYMLINK_INODE_HEADER(inode, block_ptr); + SQUASHFS_SWAP_SYMLINK_INODE_HEADER(block_ptr, inode); i.symlink = malloc(inode->symlink_size + 1); if(i.symlink == NULL) @@ -198,9 +197,9 @@ i.data = inode->symlink_size; if(header.base.inode_type == SQUASHFS_LSYMLINK_TYPE) - SQUASHFS_SWAP_INTS(&i.xattr, block_ptr + + SQUASHFS_SWAP_INTS(block_ptr + sizeof(struct squashfs_symlink_inode_header) + - inode->symlink_size, 1); + inode->symlink_size, &i.xattr, 1); else i.xattr = SQUASHFS_INVALID_XATTR; break; @@ -209,7 +208,7 @@ case SQUASHFS_CHRDEV_TYPE: { struct squashfs_dev_inode_header *inode = &header.dev; - SQUASHFS_SWAP_DEV_INODE_HEADER(inode, block_ptr); + SQUASHFS_SWAP_DEV_INODE_HEADER(block_ptr, inode); i.data = inode->rdev; i.xattr = SQUASHFS_INVALID_XATTR; @@ -219,7 +218,7 @@ case SQUASHFS_LCHRDEV_TYPE: { struct squashfs_ldev_inode_header *inode = &header.ldev; - SQUASHFS_SWAP_LDEV_INODE_HEADER(inode, block_ptr); + SQUASHFS_SWAP_LDEV_INODE_HEADER(block_ptr, inode); i.data = inode->rdev; i.xattr = inode->xattr; @@ -234,7 +233,7 @@ case SQUASHFS_LSOCKET_TYPE: { struct squashfs_lipc_inode_header *inode = &header.lipc; - SQUASHFS_SWAP_LIPC_INODE_HEADER(inode, block_ptr); + SQUASHFS_SWAP_LIPC_INODE_HEADER(block_ptr, inode); i.data = 0; i.xattr = inode->xattr; @@ -299,7 +298,7 @@ size = (*i)->data + bytes - 3; while(bytes < size) { - SQUASHFS_SWAP_DIR_HEADER(&dirh, directory_table + bytes); + SQUASHFS_SWAP_DIR_HEADER(directory_table + bytes, &dirh); dir_count = dirh.count + 1; TRACE("squashfs_opendir: Read directory header @ byte position " @@ -311,7 +310,7 @@ goto corrupted; while(dir_count--) { - SQUASHFS_SWAP_DIR_ENTRY(dire, directory_table + bytes); + SQUASHFS_SWAP_DIR_ENTRY(directory_table + bytes, dire); bytes += sizeof(*dire); diff -Nru squashfs-tools-4.2+20130409/unsquashfs.c squashfs-tools-4.3+20140919/unsquashfs.c --- squashfs-tools-4.2+20130409/unsquashfs.c 2013-05-09 10:39:11.000000000 +0200 +++ squashfs-tools-4.3+20140919/unsquashfs.c 2015-07-20 21:03:05.000000000 +0200 @@ -3,7 +3,7 @@ * filesystem. * * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, - * 2012, 2013 + * 2012, 2013, 2014 * Phillip Lougher * * This program is free software; you can redistribute it and/or @@ -26,9 +26,9 @@ #include "unsquashfs.h" #include "squashfs_swap.h" #include "squashfs_compat.h" -#include "read_fs.h" #include "compressor.h" #include "xattr.h" +#include "unsquashfs_info.h" #include "stdarg.h" #include @@ -39,8 +39,8 @@ #include struct cache *fragment_cache, *data_cache; -struct queue *to_reader, *to_deflate, *to_writer, *from_writer; -pthread_t *thread, *deflator_thread; +struct queue *to_reader, *to_inflate, *to_writer, *from_writer; +pthread_t *thread, *inflator_thread; pthread_mutex_t fragment_mutex; /* user options that control parallelisation */ @@ -69,7 +69,6 @@ int columns; int rotate = 0; pthread_mutex_t screen_mutex; -pthread_cond_t progress_wait; int progress = TRUE, progress_enabled = FALSE; unsigned int total_blocks = 0, total_files = 0, total_inodes = 0; unsigned int cur_blocks = 0; @@ -121,7 +120,6 @@ }; void progress_bar(long long current, long long max, int columns); -void update_progress_bar(); #define MAX_LINE 16384 @@ -226,6 +224,21 @@ } +void dump_queue(struct queue *queue) +{ + pthread_mutex_lock(&queue->mutex); + + printf("Max size %d, size %d%s\n", queue->size - 1, + queue->readp <= queue->writep ? queue->writep - queue->readp : + queue->size - queue->readp + queue->writep, + queue->readp == queue->writep ? " (EMPTY)" : + ((queue->writep + 1) % queue->size) == queue->readp ? + " (FULL)" : ""); + + pthread_mutex_unlock(&queue->mutex); +} + + /* Called with the cache mutex held */ void insert_hash_table(struct cache *cache, struct cache_entry *entry) { @@ -272,7 +285,7 @@ /* Called with the cache mutex held */ void remove_free_list(struct cache *cache, struct cache_entry *entry) { - if(entry->free_prev == NULL && entry->free_next == NULL) + if(entry->free_prev == NULL || entry->free_next == NULL) /* not in free list */ return; else if(entry->free_prev == entry && entry->free_next == entry) { @@ -300,6 +313,7 @@ cache->max_buffers = max_buffers; cache->buffer_size = buffer_size; cache->count = 0; + cache->used = 0; cache->free_list = NULL; memset(cache->hash_table, 0, sizeof(struct cache_entry *) * 65536); cache->wait_free = FALSE; @@ -316,7 +330,7 @@ { /* * Get a block out of the cache. If the block isn't in the cache - * it is added and queued to the reader() and deflate() threads for + * it is added and queued to the reader() and inflate() threads for * reading off disk and decompression. The cache grows until max_blocks * is reached, once this occurs existing discarded blocks on the free * list are reused @@ -332,11 +346,14 @@ if(entry) { /* - * found the block in the cache, increment used count and - * if necessary remove from free list so it won't disappear + * found the block in the cache. If the block is currently unused + * remove it from the free list and increment cache used count. */ + if(entry->used == 0) { + cache->used ++; + remove_free_list(cache, entry); + } entry->used ++; - remove_free_list(cache, entry); pthread_mutex_unlock(&cache->mutex); } else { /* @@ -369,7 +386,11 @@ } /* - * initialise block and insert into the hash table + * Initialise block and insert into the hash table. + * Increment used which tracks how many buffers in the + * cache are actively in use (the other blocks, count - used, + * are in the cache and available for lookup, but can also be + * re-used). */ entry->block = block; entry->size = size; @@ -377,6 +398,7 @@ entry->error = FALSE; entry->pending = TRUE; insert_hash_table(cache, entry); + cache->used ++; /* * queue to read thread to read and ultimately (via the @@ -446,6 +468,7 @@ entry->used --; if(entry->used == 0) { insert_free_list(entry->cache, entry); + entry->cache->used --; /* * if the wait_free flag is set, one or more threads may be @@ -461,6 +484,18 @@ } +void dump_cache(struct cache *cache) +{ + pthread_mutex_lock(&cache->mutex); + + printf("Max buffers %d, Current size %d, Used %d, %s\n", + cache->max_buffers, cache->count, cache->used, + cache->free_list ? "Free buffers" : "No free buffers"); + + pthread_mutex_unlock(&cache->mutex); +} + + char *modestr(char *str, int mode) { int i; @@ -1223,11 +1258,14 @@ target ++; start = target; - while(*target != '/' && *target!= '\0') + while(*target != '/' && *target != '\0') target ++; *targname = strndup(start, target - start); + while(*target == '/') + target ++; + return target; } @@ -1554,26 +1592,27 @@ if(res == -1) EXIT_UNSQUASH("asprintf failed in dir_scan\n"); - if(type == SQUASHFS_DIR_TYPE) + if(type == SQUASHFS_DIR_TYPE) { dir_scan(pathname, start_block, offset, new); - else if(new == NULL) { + free(pathname); + } else if(new == NULL) { + update_info(pathname); + i = s_ops.read_inode(start_block, offset); if(lsonly || info) print_filename(pathname, i); - if(!lsonly) { + if(!lsonly) create_inode(pathname, i); - update_progress_bar(); - } if(i->type == SQUASHFS_SYMLINK_TYPE || i->type == SQUASHFS_LSYMLINK_TYPE) free(i->symlink); - } + } else + free(pathname); free_subdir(new); - free(pathname); } if(!lsonly) @@ -1609,7 +1648,7 @@ printf("Compression %s\n", comp->name); if(SQUASHFS_COMP_OPTS(sBlk.s.flags)) { - char buffer[SQUASHFS_METADATA_SIZE]; + char buffer[SQUASHFS_METADATA_SIZE] __attribute__ ((aligned)); int bytes; bytes = read_block(fd, sizeof(sBlk.s), NULL, 0, buffer); @@ -1637,7 +1676,7 @@ printf("Fragments are %scompressed\n", SQUASHFS_UNCOMPRESSED_FRAGMENTS(sBlk.s.flags) ? "un" : ""); - printf("Always_use_fragments option is %sspecified\n", + printf("Always-use-fragments option is %sspecified\n", SQUASHFS_ALWAYS_FRAGMENTS(sBlk.s.flags) ? "" : "not "); } @@ -1698,6 +1737,42 @@ } +int check_compression(struct compressor *comp) +{ + int res, bytes = 0; + char buffer[SQUASHFS_METADATA_SIZE] __attribute__ ((aligned)); + + if(!comp->supported) { + ERROR("Filesystem uses %s compression, this is " + "unsupported by this version\n", comp->name); + ERROR("Decompressors available:\n"); + display_compressors("", ""); + return 0; + } + + /* + * Read compression options from disk if present, and pass to + * the compressor to ensure we know how to decompress a filesystem + * compressed with these compression options. + * + * Note, even if there is no compression options we still call the + * compressor because some compression options may be mandatory + * for some compressors. + */ + if(SQUASHFS_COMP_OPTS(sBlk.s.flags)) { + bytes = read_block(fd, sizeof(sBlk.s), NULL, 0, buffer); + if(bytes == 0) { + ERROR("Failed to read compressor options\n"); + return 0; + } + } + + res = compressor_check_options(comp, sBlk.s.block_size, buffer, bytes); + + return res != -1; +} + + int read_super(char *source) { squashfs_super_block_3 sBlk_3; @@ -1897,10 +1972,10 @@ if(res && SQUASHFS_COMPRESSED_BLOCK(entry->size)) /* - * queue successfully read block to the deflate + * queue successfully read block to the inflate * thread(s) for further processing */ - queue_put(to_deflate, entry); + queue_put(to_inflate, entry); else /* * block has either been successfully read and is @@ -2019,12 +2094,12 @@ /* * decompress thread. This decompresses buffers queued by the read thread */ -void *deflator(void *arg) +void *inflator(void *arg) { char tmp[block_size]; while(1) { - struct cache_entry *entry = queue_get(to_deflate); + struct cache_entry *entry = queue_get(to_inflate); int error, res; res = compressor_uncompress(comp, tmp, entry->data, @@ -2049,8 +2124,7 @@ void *progress_thread(void *arg) { - struct timeval timeval; - struct timespec timespec; + struct timespec requested_time, remaining; struct itimerval itimerval; struct winsize winsize; @@ -2070,22 +2144,22 @@ itimerval.it_interval.tv_usec = 250000; setitimer(ITIMER_REAL, &itimerval, NULL); - pthread_cond_init(&progress_wait, NULL); + requested_time.tv_sec = 0; + requested_time.tv_nsec = 250000000; - pthread_mutex_lock(&screen_mutex); while(1) { - gettimeofday(&timeval, NULL); - timespec.tv_sec = timeval.tv_sec; - if(timeval.tv_usec + 250000 > 999999) - timespec.tv_sec++; - timespec.tv_nsec = ((timeval.tv_usec + 250000) % 1000000) * - 1000; - pthread_cond_timedwait(&progress_wait, &screen_mutex, - ×pec); - if(progress_enabled) + int res = nanosleep(&requested_time, &remaining); + + if(res == -1 && errno != EINTR) + EXIT_UNSQUASH("nanosleep failed in progress thread\n"); + + if(progress_enabled) { + pthread_mutex_lock(&screen_mutex); progress_bar(sym_count + dev_count + fifo_count + cur_blocks, total_inodes - total_files + total_blocks, columns); + pthread_mutex_unlock(&screen_mutex); + } } } @@ -2096,11 +2170,23 @@ int i, max_files, res; sigset_t sigmask, old_mask; + /* block SIGQUIT and SIGHUP, these are handled by the info thread */ sigemptyset(&sigmask); - sigaddset(&sigmask, SIGINT); sigaddset(&sigmask, SIGQUIT); - if(sigprocmask(SIG_BLOCK, &sigmask, &old_mask) == -1) - EXIT_UNSQUASH("Failed to set signal mask in intialise_threads" + sigaddset(&sigmask, SIGHUP); + if(pthread_sigmask(SIG_BLOCK, &sigmask, NULL) == -1) + EXIT_UNSQUASH("Failed to set signal mask in initialise_threads" + "\n"); + + /* + * temporarily block these signals so the created sub-threads will + * ignore them, ensuring the main thread handles them + */ + sigemptyset(&sigmask); + sigaddset(&sigmask, SIGINT); + sigaddset(&sigmask, SIGTERM); + if(pthread_sigmask(SIG_BLOCK, &sigmask, &old_mask) == -1) + EXIT_UNSQUASH("Failed to set signal mask in initialise_threads" "\n"); if(processors == -1) { @@ -2132,14 +2218,14 @@ thread = malloc((3 + processors) * sizeof(pthread_t)); if(thread == NULL) EXIT_UNSQUASH("Out of memory allocating thread descriptors\n"); - deflator_thread = &thread[3]; + inflator_thread = &thread[3]; /* - * dimensioning the to_reader and to_deflate queues. The size of + * dimensioning the to_reader and to_inflate queues. The size of * these queues is directly related to the amount of block * read-ahead possible. To_reader queues block read requests to - * the reader thread and to_deflate queues block decompression - * requests to the deflate thread(s) (once the block has been read by + * the reader thread and to_inflate queues block decompression + * requests to the inflate thread(s) (once the block has been read by * the reader thread). The amount of read-ahead is determined by * the combined size of the data_block and fragment caches which * determine the total number of blocks which can be "in flight" @@ -2162,7 +2248,7 @@ * * dimensioning the to_writer queue. The size of this queue is * directly related to the amount of block read-ahead possible. - * However, unlike the to_reader and to_deflate queues, this is + * However, unlike the to_reader and to_inflate queues, this is * complicated by the fact the to_writer queue not only contains * entries for fragments and data_blocks but it also contains * file entries, one per open file in the read-ahead. @@ -2196,7 +2282,7 @@ open_init(max_files); /* - * allocate to_reader, to_deflate and to_writer queues. Set based on + * allocate to_reader, to_inflate and to_writer queues. Set based on * open file limit and cache size, unless open file limit is unlimited, * in which case set purely based on cache limits * @@ -2209,7 +2295,7 @@ EXIT_UNSQUASH("Data queue size is too large\n"); to_reader = queue_init(max_files + data_buffer_size); - to_deflate = queue_init(max_files + data_buffer_size); + to_inflate = queue_init(max_files + data_buffer_size); to_writer = queue_init(max_files * 2 + data_buffer_size); } else { int all_buffers_size; @@ -2225,7 +2311,7 @@ " too large\n"); to_reader = queue_init(all_buffers_size); - to_deflate = queue_init(all_buffers_size); + to_inflate = queue_init(all_buffers_size); to_writer = queue_init(all_buffers_size * 2); } @@ -2236,10 +2322,11 @@ pthread_create(&thread[0], NULL, reader, NULL); pthread_create(&thread[1], NULL, writer, NULL); pthread_create(&thread[2], NULL, progress_thread, NULL); + init_info(); pthread_mutex_init(&fragment_mutex, NULL); for(i = 0; i < processors; i++) { - if(pthread_create(&deflator_thread[i], NULL, deflator, NULL) != + if(pthread_create(&inflator_thread[i], NULL, inflator, NULL) != 0) EXIT_UNSQUASH("Failed to create thread\n"); } @@ -2247,8 +2334,8 @@ printf("Parallel unsquashfs: Using %d processor%s\n", processors, processors == 1 ? "" : "s"); - if(sigprocmask(SIG_SETMASK, &old_mask, NULL) == -1) - EXIT_UNSQUASH("Failed to set signal mask in intialise_threads" + if(pthread_sigmask(SIG_SETMASK, &old_mask, NULL) == -1) + EXIT_UNSQUASH("Failed to set signal mask in initialise_threads" "\n"); } @@ -2256,7 +2343,7 @@ void enable_progress_bar() { pthread_mutex_lock(&screen_mutex); - progress_enabled = TRUE; + progress_enabled = progress; pthread_mutex_unlock(&screen_mutex); } @@ -2264,19 +2351,16 @@ void disable_progress_bar() { pthread_mutex_lock(&screen_mutex); + if(progress_enabled) { + progress_bar(sym_count + dev_count + fifo_count + cur_blocks, + total_inodes - total_files + total_blocks, columns); + printf("\n"); + } progress_enabled = FALSE; pthread_mutex_unlock(&screen_mutex); } -void update_progress_bar() -{ - pthread_mutex_lock(&screen_mutex); - pthread_cond_signal(&progress_wait); - pthread_mutex_unlock(&screen_mutex); -} - - void progressbar_error(char *fmt, ...) { va_list ap; @@ -2393,8 +2477,8 @@ #define VERSION() \ - printf("unsquashfs version 4.2-git (2013/03/13)\n");\ - printf("copyright (C) 2013 Phillip Lougher "\ + printf("unsquashfs version 4.3 (2014/05/12)\n");\ + printf("copyright (C) 2014 Phillip Lougher "\ "\n\n");\ printf("This program is free software; you can redistribute it and/or"\ "\n");\ @@ -2535,6 +2619,11 @@ progress = FALSE; #ifdef SQUASHFS_TRACE + /* + * Disable progress bar if full debug tracing is enabled. + * The progress bar in this case just gets in the way of the + * debug trace output + */ progress = FALSE; #endif @@ -2608,13 +2697,8 @@ exit(0); } - if(!comp->supported) { - ERROR("Filesystem uses %s compression, this is " - "unsupported by this version\n", comp->name); - ERROR("Decompressors available:\n"); - display_compressors("", ""); + if(!check_compression(comp)) exit(1); - } block_size = sBlk.s.block_size; block_log = sBlk.s.block_log; @@ -2707,8 +2791,7 @@ printf("%d inodes (%d blocks) to write\n\n", total_inodes, total_inodes - total_files + total_blocks); - if(progress) - enable_progress_bar(); + enable_progress_bar(); dir_scan(dest, SQUASHFS_INODE_BLK(sBlk.s.root_inode), SQUASHFS_INODE_OFFSET(sBlk.s.root_inode), paths); @@ -2716,11 +2799,7 @@ queue_put(to_writer, NULL); queue_get(from_writer); - if(progress) { - disable_progress_bar(); - progress_bar(sym_count + dev_count + fifo_count + cur_blocks, - total_inodes - total_files + total_blocks, columns); - } + disable_progress_bar(); if(!lsonly) { printf("\n"); diff -Nru squashfs-tools-4.2+20130409/unsquashfs.h squashfs-tools-4.3+20140919/unsquashfs.h --- squashfs-tools-4.2+20130409/unsquashfs.h 2013-05-09 10:39:11.000000000 +0200 +++ squashfs-tools-4.3+20140919/unsquashfs.h 2015-07-20 21:03:05.000000000 +0200 @@ -1,8 +1,10 @@ +#ifndef UNSQUASHFS_H +#define UNSQUASHFS_H /* * Unsquash a squashfs filesystem. This is a highly compressed read only * filesystem. * - * Copyright (c) 2009, 2010, 2013 + * Copyright (c) 2009, 2010, 2013, 2014 * Phillip Lougher * * This program is free software; you can redistribute it and/or @@ -121,6 +123,7 @@ struct cache { int max_buffers; int count; + int used; int buffer_size; int wait_free; int wait_pending; @@ -232,11 +235,17 @@ extern int inode_number; extern int lookup_type[]; extern int fd; +extern struct queue *to_reader, *to_inflate, *to_writer; +extern struct cache *fragment_cache, *data_cache; /* unsquashfs.c */ extern int lookup_entry(struct hash_table_entry **, long long); extern int read_fs_bytes(int fd, long long, int, void *); extern int read_block(int, long long, long long *, int, void *); +extern void enable_progress_bar(); +extern void disable_progress_bar(); +extern void dump_queue(struct queue *); +extern void dump_cache(struct cache *); /* unsquash-1.c */ extern void read_block_list_1(unsigned int *, char *, int); @@ -266,3 +275,4 @@ extern struct dir *squashfs_opendir_4(unsigned int, unsigned int, struct inode **); extern int read_uids_guids_4(); +#endif diff -Nru squashfs-tools-4.2+20130409/unsquashfs_info.c squashfs-tools-4.3+20140919/unsquashfs_info.c --- squashfs-tools-4.2+20130409/unsquashfs_info.c 1970-01-01 01:00:00.000000000 +0100 +++ squashfs-tools-4.3+20140919/unsquashfs_info.c 2015-07-20 21:03:05.000000000 +0200 @@ -0,0 +1,145 @@ +/* + * Create a squashfs filesystem. This is a highly compressed read only + * filesystem. + * + * Copyright (c) 2013 + * Phillip Lougher + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2, + * or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * unsquashfs_info.c + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "squashfs_fs.h" +#include "unsquashfs.h" +#include "error.h" + +static int silent = 0; +char *pathname = NULL; + +pthread_t info_thread; + + +void disable_info() +{ + if(pathname) + free(pathname); + + pathname = NULL; +} + + +void update_info(char *name) +{ + if(pathname) + free(pathname); + + pathname = name; +} + + +void dump_state() +{ + disable_progress_bar(); + + printf("Queue and cache status dump\n"); + printf("===========================\n"); + + printf("file buffer read queue (main thread -> reader thread)\n"); + dump_queue(to_reader); + + printf("file buffer decompress queue (reader thread -> inflate" + " thread(s))\n"); + dump_queue(to_inflate); + + printf("file buffer write queue (main thread -> writer thread)\n"); + dump_queue(to_writer); + + printf("\nbuffer cache (uncompressed blocks and compressed blocks " + "'in flight')\n"); + dump_cache(data_cache); + + printf("fragment buffer cache (uncompressed frags and compressed" + " frags 'in flight')\n"); + dump_cache(fragment_cache); + + enable_progress_bar(); +} + + +void *info_thrd(void *arg) +{ + sigset_t sigmask; + struct timespec timespec = { .tv_sec = 1, .tv_nsec = 0 }; + int sig, waiting = 0; + + sigemptyset(&sigmask); + sigaddset(&sigmask, SIGQUIT); + sigaddset(&sigmask, SIGHUP); + + while(1) { + if(waiting) + sig = sigtimedwait(&sigmask, NULL, ×pec); + else + sig = sigwaitinfo(&sigmask, NULL); + + if(sig == -1) { + switch(errno) { + case EAGAIN: + /* interval timed out */ + waiting = 0; + /* FALLTHROUGH */ + case EINTR: + /* if waiting, the wait will be longer, but + that's OK */ + continue; + default: + BAD_ERROR("sigtimedwait/sigwaitinfo failed " + "because %s\n", strerror(errno)); + } + } + + if(sig == SIGQUIT && !waiting) { + if(pathname) + INFO("%s\n", pathname); + + /* set one second interval period, if ^\ received + within then, dump queue and cache status */ + waiting = 1; + } else + dump_state(); + } +} + + +void init_info() +{ + pthread_create(&info_thread, NULL, info_thrd, NULL); +} diff -Nru squashfs-tools-4.2+20130409/unsquashfs_info.h squashfs-tools-4.3+20140919/unsquashfs_info.h --- squashfs-tools-4.2+20130409/unsquashfs_info.h 1970-01-01 01:00:00.000000000 +0100 +++ squashfs-tools-4.3+20140919/unsquashfs_info.h 2015-07-20 21:03:05.000000000 +0200 @@ -0,0 +1,30 @@ +#ifndef UNSQUASHFS_INFO_H +#define UNSQUASHFS_INFO_H +/* + * Create a squashfs filesystem. This is a highly compressed read only + * filesystem. + * + * Copyright (c) 2013, 2014 + * Phillip Lougher + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2, + * or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * unsquashfs_info.h + */ + +extern void disable_info(); +extern void update_info(char *); +extern void init_info(); +#endif diff -Nru squashfs-tools-4.2+20130409/xattr.c squashfs-tools-4.3+20140919/xattr.c --- squashfs-tools-4.2+20130409/xattr.c 2013-05-09 10:39:11.000000000 +0200 +++ squashfs-tools-4.3+20140919/xattr.c 2015-07-20 21:03:05.000000000 +0200 @@ -2,7 +2,7 @@ * Create a squashfs filesystem. This is a highly compressed read only * filesystem. * - * Copyright (c) 2008, 2009, 2010, 2012 + * Copyright (c) 2008, 2009, 2010, 2012, 2014 * Phillip Lougher * * This program is free software; you can redistribute it and/or @@ -41,6 +41,7 @@ #include "mksquashfs.h" #include "xattr.h" #include "error.h" +#include "progressbar.h" /* compressed xattr table */ static char *xattr_table = NULL; @@ -65,7 +66,7 @@ static int sxattr_ids = 0; /* xattr hash table for value duplicate detection */ -static struct xattr_list *dupl[65536]; +static struct xattr_list *dupl_value[65536]; /* xattr hash table for id duplicate detection */ static struct dupl_id *dupl_id[65536]; @@ -120,10 +121,12 @@ while(1) { size = llistxattr(filename, NULL, 0); if(size <= 0) { - if(size < 0 && errno != ENOTSUP) - ERROR("llistxattr for %s failed in read_attrs," - " because %s\n", filename, + if(size < 0 && errno != ENOTSUP) { + ERROR_START("llistxattr for %s failed in " + "read_attrs, because %s", filename, strerror(errno)); + ERROR_EXIT(". Ignoring"); + } return 0; } @@ -138,9 +141,10 @@ /* xattr list grew? Try again */ continue; else { - ERROR("llistxattr for %s failed in read_attrs," - " because %s\n", filename, + ERROR_START("llistxattr for %s failed in " + "read_attrs, because %s", filename, strerror(errno)); + ERROR_EXIT(". Ignoring"); return 0; } } @@ -169,9 +173,10 @@ vsize = lgetxattr(filename, xattr_list[i].full_name, NULL, 0); if(vsize < 0) { - ERROR("lgetxattr failed for %s in read_attrs," - " because %s\n", filename, + ERROR_START("lgetxattr failed for %s in " + "read_attrs, because %s", filename, strerror(errno)); + ERROR_EXIT(". Ignoring"); free(xattr_list[i].full_name); goto failed; } @@ -188,9 +193,10 @@ /* xattr grew? Try again */ continue; else { - ERROR("lgetxattr failed for %s in " - "read_attrs, because %s\n", + ERROR_START("lgetxattr failed for %s " + "in read_attrs, because %s", filename, strerror(errno)); + ERROR_EXIT(". Ignoring"); free(xattr_list[i].full_name); goto failed; } @@ -342,7 +348,7 @@ /* Check if this is a duplicate of an existing value */ xattr->vchecksum = get_checksum(xattr->value, xattr->vsize, 0); - for(entry = dupl[xattr->vchecksum]; entry; entry = entry->vnext) { + for(entry = dupl_value[xattr->vchecksum]; entry; entry = entry->vnext) { if(entry->vsize != xattr->vsize) continue; @@ -355,8 +361,8 @@ * No duplicate exists, add to hash table, and mark as * requiring writing */ - xattr->vnext = dupl[xattr->vchecksum]; - dupl[xattr->vchecksum] = xattr; + xattr->vnext = dupl_value[xattr->vchecksum]; + dupl_value[xattr->vchecksum] = xattr; xattr->ool_value = SQUASHFS_INVALID_BLK; } else { /* diff -Nru squashfs-tools-4.2+20130409/xattr.h squashfs-tools-4.3+20140919/xattr.h --- squashfs-tools-4.2+20130409/xattr.h 2013-05-09 10:39:11.000000000 +0200 +++ squashfs-tools-4.3+20140919/xattr.h 2015-07-20 21:03:05.000000000 +0200 @@ -1,8 +1,10 @@ +#ifndef XATTR_H +#define XATTR_H /* * Create a squashfs filesystem. This is a highly compressed read only * filesystem. * - * Copyright (c) 2010, 2012, 2013 + * Copyright (c) 2010, 2012, 2013, 2014 * Phillip Lougher * * This program is free software; you can redistribute it and/or @@ -124,7 +126,7 @@ } -static inline struct xattr_list *get_xattr(int i, unsigned int *count, int) +static inline struct xattr_list *get_xattr(int i, unsigned int *count, int j) { return NULL; } @@ -145,4 +147,4 @@ #define XOPT_STR " (unsupported)" #define XATTR_DEF 1 #endif - +#endif diff -Nru squashfs-tools-4.2+20130409/xz_wrapper.c squashfs-tools-4.3+20140919/xz_wrapper.c --- squashfs-tools-4.2+20130409/xz_wrapper.c 2013-05-09 10:39:11.000000000 +0200 +++ squashfs-tools-4.3+20140919/xz_wrapper.c 2015-07-20 21:03:05.000000000 +0200 @@ -41,8 +41,6 @@ { NULL, LZMA_VLI_UNKNOWN, 0 } }; -static struct comp_opts comp_opts; - static int filter_count = 1; static int dictionary_size = 0; static float dictionary_percent = 0; @@ -56,10 +54,11 @@ * -Xbcj * -Xdict-size * - * This function returns 1 on successful parsing of an option - * -1 if the option was unrecognised, or - * -2 if the option was recognised, but otherwise bad in - * some way (e.g. invalid parameter) + * This function returns: + * >=0 (number of additional args parsed) on success + * -1 if the option was unrecognised, or + * -2 if the option was recognised, but otherwise bad in + * some way (e.g. invalid parameter) * * Note: this function sets internal compressor state, but does not * pass back the results of the parsing other than success/failure. @@ -226,6 +225,7 @@ */ static void *xz_dump_options(int block_size, int *size) { + static struct comp_opts comp_opts; int flags = 0, i; /* @@ -329,7 +329,7 @@ int i, n; /* check passed comp opts struct is of the correct length */ - if(size != sizeof(comp_opts)) + if(size != sizeof(struct comp_opts)) goto failed; SQUASHFS_INSWAP_COMP_OPTS(comp_opts); @@ -494,8 +494,12 @@ lzma_ret res = lzma_stream_buffer_decode(&memlimit, 0, NULL, src, &src_pos, size, dest, &dest_pos, outsize); - *error = res; - return res == LZMA_OK && size == (int) src_pos ? (int) dest_pos : -1; + if(res == LZMA_OK && size == (int) src_pos) + return (int) dest_pos; + else { + *error = res; + return -1; + } }