From: Michihiro NAKAJIMA Date: Thu, 4 Oct 2012 00:18:30 +0000 (+0900) Subject: Make a filter program be called with its arguments. X-Git-Tag: v3.1.0~40^2~110 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=40722a255fe8f25a6a8dcd9d66fda49478756b17;p=thirdparty%2Flibarchive.git Make a filter program be called with its arguments. --- diff --git a/Makefile.am b/Makefile.am index 3e277aba5..9ae870f41 100644 --- a/Makefile.am +++ b/Makefile.am @@ -331,6 +331,7 @@ libarchive_test_SOURCES= \ libarchive/test/test_read_disk_entry_from_file.c \ libarchive/test/test_read_extract.c \ libarchive/test/test_read_file_nonexistent.c \ + libarchive/test/test_read_filter_program_signature.c \ libarchive/test/test_read_format_7zip.c \ libarchive/test/test_read_format_ar.c \ libarchive/test/test_read_format_cab.c \ diff --git a/libarchive/archive.h b/libarchive/archive.h index 7b7e75eeb..d914ed43c 100644 --- a/libarchive/archive.h +++ b/libarchive/archive.h @@ -328,9 +328,22 @@ __LA_DECL int archive_read_support_filter_lzma(struct archive *); __LA_DECL int archive_read_support_filter_none(struct archive *); __LA_DECL int archive_read_support_filter_program(struct archive *, const char *command); +__LA_DECL int archive_read_support_filter_programl(struct archive *, + const char *command, const char *arg, + .../* (char *)0 */); +__LA_DECL int archive_read_support_filter_programv(struct archive *, + const char *command, char * const argv[]); __LA_DECL int archive_read_support_filter_program_signature - (struct archive *, const char *, + (struct archive *, const char * /* cmd */, const void * /* match */, size_t); +__LA_DECL int archive_read_support_filter_programl_signature + (struct archive *, const char * /* cmd */, + const char * /* arg */, + .../* , (char *)0, const void *, size_t */); +__LA_DECL int archive_read_support_filter_programv_signature + (struct archive *, const char * /* cmd */, + char * const [] /* argv */, + const void * /* match */, size_t); __LA_DECL int archive_read_support_filter_rpm(struct archive *); __LA_DECL int archive_read_support_filter_uu(struct archive *); @@ -601,6 +614,10 @@ __LA_DECL int archive_write_add_filter_lzma(struct archive *); __LA_DECL int archive_write_add_filter_none(struct archive *); __LA_DECL int archive_write_add_filter_program(struct archive *, const char *cmd); +__LA_DECL int archive_write_add_filter_programl(struct archive *, + const char *cmd, const char *arg, .../*, (char *)0 */); +__LA_DECL int archive_write_add_filter_programv(struct archive *, + const char *cmd, char * const argv[]); __LA_DECL int archive_write_add_filter_xz(struct archive *); diff --git a/libarchive/archive_read_private.h b/libarchive/archive_read_private.h index 4d956a080..03a7733f1 100644 --- a/libarchive/archive_read_private.h +++ b/libarchive/archive_read_private.h @@ -226,4 +226,8 @@ int64_t __archive_read_filter_seek(struct archive_read_filter *, int64_t, int); int64_t __archive_read_consume(struct archive_read *, int64_t); int64_t __archive_read_filter_consume(struct archive_read_filter *, int64_t); int __archive_read_program(struct archive_read_filter *, const char *); +int __archive_read_programl(struct archive_read_filter *, const char *, + const char *, .../*, (char *)0 */); +int __archive_read_programv(struct archive_read_filter *, const char *, + char * const argv[]); #endif diff --git a/libarchive/archive_read_support_filter_program.c b/libarchive/archive_read_support_filter_program.c index fe6048e89..2b5540aba 100644 --- a/libarchive/archive_read_support_filter_program.c +++ b/libarchive/archive_read_support_filter_program.c @@ -1,5 +1,6 @@ /*- * Copyright (c) 2007 Joerg Sonnenberger + * Copyright (c) 2012 Michihiro NAKAJIMA * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -53,6 +54,7 @@ __FBSDID("$FreeBSD$"); #include "archive.h" #include "archive_private.h" +#include "archive_string.h" #include "archive_read_private.h" #include "filter_fork.h" @@ -80,6 +82,71 @@ archive_read_support_filter_program(struct archive *a, const char *cmd) return (archive_read_support_filter_program_signature(a, cmd, NULL, 0)); } +static void +free_argv(char **argv) +{ + int i; + + if (argv) { + for (i = 0; argv[i] != NULL; i++) + free(argv[i]); + free(argv); + } +} + +int +archive_read_support_filter_programl(struct archive *a, const char *cmd, + const char *arg, ...) +{ + va_list ap; + char **argv; + int i, r; + + i = 2; + if (arg != NULL) { + va_start(ap, arg); + while (va_arg(ap, char *) != NULL) + i++; + va_end(ap); + } + argv = calloc(i, sizeof(char *)); + if (argv == NULL) + goto memerr; + argv[0] = strdup((arg)?arg:cmd); + if (argv[0] == NULL) + goto memerr; + i = 1; + va_start(ap, arg); + if (arg) { + char *val; + while ((val = va_arg(ap, char *)) != NULL) { + argv[i] = strdup(val); + if (argv[i] == NULL) { + va_end(ap); + goto memerr; + } + i++; + } + } + va_end(ap); + argv[i] = NULL; + r = archive_read_support_filter_programv_signature(a, cmd, argv, + NULL, 0); + free_argv(argv); + return (r); +memerr: + free_argv(argv); + archive_set_error(a, ENOMEM, "Can't allocate memory"); + return (ARCHIVE_FATAL); +} + +int +archive_read_support_filter_programv(struct archive *a, const char *cmd, + char * const argv[]) +{ + return (archive_read_support_filter_programv_signature(a, cmd, argv, + NULL, 0)); +} /* * The bidder object stores the command and the signature to watch for. @@ -88,6 +155,7 @@ archive_read_support_filter_program(struct archive *a, const char *cmd) */ struct program_bidder { char *cmd; + char **argv; void *signature; size_t signature_len; int inhibit; @@ -102,7 +170,7 @@ static int program_bidder_free(struct archive_read_filter_bidder *); * The actual filter needs to track input and output data. */ struct program_filter { - char *description; + struct archive_string description; pid_t child; int exit_status; int waitpid_return; @@ -115,6 +183,29 @@ struct program_filter { static ssize_t program_filter_read(struct archive_read_filter *, const void **); static int program_filter_close(struct archive_read_filter *); +static void free_state(struct program_bidder *); + +static int +set_bidder_signature(struct archive_read_filter_bidder *bidder, + struct program_bidder *state, const void *signature, size_t signature_len) +{ + + if (signature != NULL && signature_len > 0) { + state->signature_len = signature_len; + state->signature = malloc(signature_len); + memcpy(state->signature, signature, signature_len); + } + + /* + * Fill in the bidder object. + */ + bidder->data = state; + bidder->bid = program_bidder_bid; + bidder->init = program_bidder_init; + bidder->options = NULL; + bidder->free = program_bidder_free; + return (ARCHIVE_OK); +} int archive_read_support_filter_program_signature(struct archive *_a, @@ -133,37 +224,159 @@ archive_read_support_filter_program_signature(struct archive *_a, /* * Allocate our private state. */ - state = (struct program_bidder *)calloc(sizeof (*state), 1); + state = (struct program_bidder *)calloc(1, sizeof (*state)); if (state == NULL) + goto memerr; + state->cmd = strdup(cmd); + if (state->cmd == NULL) + goto memerr; + state->argv = calloc(2, sizeof(char *)); + if (state->argv == NULL) + goto memerr; + state->argv[0] = strdup(cmd); + if (state->argv[0] == NULL) + goto memerr; + state->argv[1] = NULL; + + return set_bidder_signature(bidder, state, signature, signature_len); +memerr: + free_state(state); + archive_set_error(_a, ENOMEM, "Can't allocate memory"); + return (ARCHIVE_FATAL); +} + +int +archive_read_support_filter_programl_signature(struct archive *_a, + const char *cmd, const char *arg, .../* , signature, signature_len */) +{ + struct archive_read *a = (struct archive_read *)_a; + struct archive_read_filter_bidder *bidder; + struct program_bidder *state; + const void *signature; + size_t signature_len; + va_list ap; + int i; + + /* + * Get a bidder object from the read core. + */ + if (__archive_read_get_bidder(a, &bidder) != ARCHIVE_OK) return (ARCHIVE_FATAL); + + /* + * Allocate our private state. + */ + state = (struct program_bidder *)calloc(1, sizeof (*state)); + if (state == NULL) + goto memerr; state->cmd = strdup(cmd); - if (signature != NULL && signature_len > 0) { - state->signature_len = signature_len; - state->signature = malloc(signature_len); - memcpy(state->signature, signature, signature_len); + if (state->cmd == NULL) + goto memerr; + + i = 2; + if (arg != NULL) { + va_start(ap, arg); + while (va_arg(ap, char *) != NULL) + i++; + va_end(ap); } + state->argv = calloc(i, sizeof(char *)); + if (state->argv == NULL) + goto memerr; + state->argv[0] = strdup((arg)?arg:cmd); + if (state->argv[0] == NULL) + goto memerr; + i = 1; + va_start(ap, arg); + if (arg) { + char *val; + while ((val = va_arg(ap, char *)) != NULL) { + state->argv[i] = strdup(val); + if (state->argv[i] == NULL) { + va_end(ap); + goto memerr; + } + i++; + } + } + signature = va_arg(ap, const void *); + signature_len = va_arg(ap, size_t); + va_end(ap); + state->argv[i] = NULL; + + return set_bidder_signature(bidder, state, signature, signature_len); +memerr: + free_state(state); + archive_set_error(_a, ENOMEM, "Can't allocate memory"); + return (ARCHIVE_FATAL); +} + +int +archive_read_support_filter_programv_signature(struct archive *_a, + const char *cmd, char * const argv[], const void *signature, + size_t signature_len) +{ + struct archive_read *a = (struct archive_read *)_a; + struct archive_read_filter_bidder *bidder; + struct program_bidder *state; + int i; /* - * Fill in the bidder object. + * Get a bidder object from the read core. */ - bidder->data = state; - bidder->bid = program_bidder_bid; - bidder->init = program_bidder_init; - bidder->options = NULL; - bidder->free = program_bidder_free; - return (ARCHIVE_OK); + if (__archive_read_get_bidder(a, &bidder) != ARCHIVE_OK) + return (ARCHIVE_FATAL); + + /* + * Allocate our private state. + */ + state = (struct program_bidder *)calloc(1, sizeof (*state)); + if (state == NULL) + goto memerr; + state->cmd = strdup(cmd); + if (state->cmd == NULL) + goto memerr; + + for (i = 0; argv[i] != NULL; i++) + ; + state->argv = calloc(i + 1, sizeof(char *)); + if (state->argv == NULL) + goto memerr; + for (i = 0; argv[i] != NULL; i++) { + state->argv[i] = strdup(argv[i]); + if (state->argv[i] == NULL) + goto memerr; + } + state->argv[i] = NULL; + + return set_bidder_signature(bidder, state, signature, signature_len); +memerr: + free_state(state); + archive_set_error(_a, ENOMEM, "Can't allocate memory"); + return (ARCHIVE_FATAL); } static int program_bidder_free(struct archive_read_filter_bidder *self) { struct program_bidder *state = (struct program_bidder *)self->data; - free(state->cmd); - free(state->signature); - free(self->data); + + free_state(state); return (ARCHIVE_OK); } +static void +free_state(struct program_bidder *state) +{ + + if (state) { + free(state->cmd); + free_argv(state->argv); + free(state->signature); + free(state); + } +} + /* * If we do have a signature, bid only if that matches. * @@ -336,40 +549,118 @@ child_read(struct archive_read_filter *self, char *buf, size_t buf_len) int __archive_read_program(struct archive_read_filter *self, const char *cmd) +{ + char *argv[2]; + int r; + + argv[0] = strdup(cmd); + if (argv[0] == NULL) { + archive_set_error(&self->archive->archive, ENOMEM, + "Can't allocate input data"); + return (ARCHIVE_FATAL); + } + argv[1] = NULL; + r = __archive_read_programv(self, cmd, argv); + free(argv[0]); + return (r); +} + +int +__archive_read_programl(struct archive_read_filter *self, const char *cmd, + const char *arg, ...) +{ + va_list ap; + char **argv; + char *val; + int i, r; + + i = 2; + if (arg != NULL) { + va_start(ap, arg); + while (va_arg(ap, char *) != NULL) + i++; + va_end(ap); + } + argv = malloc(i * sizeof(char *)); + if (argv == NULL) + goto memerr; + + if (arg != NULL) { + argv[0] = strdup(arg); + if (argv[0] == NULL) + goto memerr; + i = 1; + va_start(ap, arg); + while ((val = va_arg(ap, char *)) != NULL) { + argv[i] = strdup(val); + if (argv[i] == NULL) + goto memerr; + i++; + } + va_end(ap); + argv[i] = NULL; + } else { + argv[0] = strdup(cmd); + if (argv[0] == NULL) + goto memerr; + argv[1] = NULL; + } + + r = __archive_read_programv(self, cmd, argv); + free_argv(argv); + return (r); +memerr: + free_argv(argv); + archive_set_error(&self->archive->archive, ENOMEM, + "Can't allocate input data"); + return (ARCHIVE_FATAL); +} + +int +__archive_read_programv(struct archive_read_filter *self, const char *cmd, + char * const argv[]) { struct program_filter *state; static const size_t out_buf_len = 65536; char *out_buf; - char *description; const char *prefix = "Program: "; + int i; + size_t l; + l = strlen(prefix) + strlen(cmd) + 1; + for (i = 0; argv[i] != NULL; i++) + l += strlen(argv[i]) + 1; state = (struct program_filter *)calloc(1, sizeof(*state)); out_buf = (char *)malloc(out_buf_len); - description = (char *)malloc(strlen(prefix) + strlen(cmd) + 1); - if (state == NULL || out_buf == NULL || description == NULL) { + if (state == NULL || out_buf == NULL || + archive_string_ensure(&state->description, l) == NULL) { archive_set_error(&self->archive->archive, ENOMEM, "Can't allocate input data"); + archive_string_free(&state->description); free(state); free(out_buf); - free(description); return (ARCHIVE_FATAL); } + archive_strcpy(&state->description, prefix); + archive_strcat(&state->description, cmd); + for (i = 0; argv[i] != NULL; i++) { + archive_strappend_char(&state->description, ' '); + archive_strcat(&state->description, argv[i]); + } self->code = ARCHIVE_COMPRESSION_PROGRAM; - state->description = description; - strcpy(state->description, prefix); - strcat(state->description, cmd); - self->name = state->description; + self->name = state->description.s; state->out_buf = out_buf; state->out_buf_len = out_buf_len; - if ((state->child = __archive_create_child(cmd, + if ((state->child = __archive_create_child(cmd, argv, &state->child_stdin, &state->child_stdout)) == -1) { free(state->out_buf); free(state); archive_set_error(&self->archive->archive, EINVAL, - "Can't initialize filter; unable to run program \"%s\"", cmd); + "Can't initialize filter; unable to run program \"%s\"", + cmd); return (ARCHIVE_FATAL); } @@ -388,7 +679,8 @@ program_bidder_init(struct archive_read_filter *self) struct program_bidder *bidder_state; bidder_state = (struct program_bidder *)self->bidder->data; - return (__archive_read_program(self, bidder_state->cmd)); + return (__archive_read_programv(self, bidder_state->cmd, + bidder_state->argv)); } static ssize_t @@ -431,7 +723,7 @@ program_filter_close(struct archive_read_filter *self) /* Release our private data. */ free(state->out_buf); - free(state->description); + archive_string_free(&state->description); free(state); return (e); diff --git a/libarchive/archive_write_add_filter_program.c b/libarchive/archive_write_add_filter_program.c index 778967003..7ccc059c1 100644 --- a/libarchive/archive_write_add_filter_program.c +++ b/libarchive/archive_write_add_filter_program.c @@ -1,5 +1,6 @@ /*- * Copyright (c) 2007 Joerg Sonnenberger + * Copyright (c) 2012 Michihiro NAKAJIMA * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -44,6 +45,7 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_compression_program.c #include "archive.h" #include "archive_private.h" +#include "archive_string.h" #include "archive_write_private.h" #include "filter_fork.h" @@ -58,7 +60,8 @@ archive_write_set_compression_program(struct archive *a, const char *cmd) struct private_data { char *cmd; - char *description; + char **argv; + struct archive_string description; pid_t child; int child_stdin, child_stdout; @@ -71,6 +74,8 @@ static int archive_compressor_program_write(struct archive_write_filter *, const void *, size_t); static int archive_compressor_program_close(struct archive_write_filter *); static int archive_compressor_program_free(struct archive_write_filter *); +static int write_add_filter_programv(struct archive *, const char *, + char * const *); /* * Add a filter to this write handle that passes all data through an @@ -79,28 +84,148 @@ static int archive_compressor_program_free(struct archive_write_filter *); int archive_write_add_filter_program(struct archive *_a, const char *cmd) { + char *argv[2]; + int r; + + archive_check_magic(_a, ARCHIVE_WRITE_MAGIC, + ARCHIVE_STATE_NEW, "archive_write_add_filter_program"); + + argv[0] = strdup(cmd); + if (argv[0] == NULL) { + archive_set_error(_a, ENOMEM, + "Can't allocate memory for filter program"); + return (ARCHIVE_FATAL); + } + argv[1] = NULL; + r = write_add_filter_programv(_a, cmd, argv); + free(argv[0]); + return (r); +} + +int +archive_write_add_filter_programl(struct archive *_a, const char *cmd, + const char *arg, ...) +{ + va_list ap; + char **argv; + char *val; + int i, r; + + archive_check_magic(_a, ARCHIVE_WRITE_MAGIC, + ARCHIVE_STATE_NEW, "archive_write_add_filter_programl"); + + i = 2; + if (arg != NULL) { + va_start(ap, arg); + while (va_arg(ap, char *) != NULL) + i++; + va_end(ap); + } + argv = malloc(i * sizeof(char *)); + if (argv == NULL) + goto memerr; + + if (arg != NULL) { + argv[0] = strdup(arg); + if (argv[0] == NULL) + goto memerr; + i = 1; + va_start(ap, arg); + while ((val = va_arg(ap, char *)) != NULL) { + argv[i] = strdup(val); + if (argv[i] == NULL) + goto memerr; + i++; + } + va_end(ap); + argv[i] = NULL; + } else { + argv[0] = strdup(cmd); + if (argv[0] == NULL) + goto memerr; + argv[1] = NULL; + } + + r = write_add_filter_programv(_a, cmd, argv); + for (i = 0; argv[i] != NULL; i++) + free(argv[i]); + free(argv); + return (r); +memerr: + if (argv) { + for (i = 0; argv[i] != NULL; i++) + free(argv[i]); + free(argv); + } + archive_set_error(_a, ENOMEM, + "Can't allocate memory for filter program"); + return (ARCHIVE_FATAL); +} + +int +archive_write_add_filter_programv(struct archive *_a, const char *cmd, + char * const argv[]) +{ + + archive_check_magic(_a, ARCHIVE_WRITE_MAGIC, + ARCHIVE_STATE_NEW, "archive_write_add_filter_programv"); + + return write_add_filter_programv(_a, cmd, argv); +} + +static int +write_add_filter_programv(struct archive *_a, const char *cmd, + char * const argv[]) +{ + struct archive_write_filter *f = __archive_write_allocate_filter(_a); - struct archive_write *a = (struct archive_write *)_a; struct private_data *data; static const char *prefix = "Program: "; - archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, - ARCHIVE_STATE_NEW, "archive_write_add_filter_program"); + int i; + size_t l; + data = calloc(1, sizeof(*data)); - if (data == NULL) { - archive_set_error(&a->archive, ENOMEM, "Out of memory"); - return (ARCHIVE_FATAL); - } + if (data == NULL) + goto memerr; data->cmd = strdup(cmd); - data->description = (char *)malloc(strlen(prefix) + strlen(cmd) + 1); - strcpy(data->description, prefix); - strcat(data->description, cmd); + if (data->cmd == NULL) + goto memerr; + + /* Reproduce argv. */ + for (i = 0; argv[i] != NULL; i++) + ; + data->argv = calloc(i + 1, sizeof(char *)); + if (data->argv == NULL) + goto memerr; + l = strlen(prefix) + strlen(cmd) + 1; + for (i = 0; argv[i] != NULL; i++) { + data->argv[i] = strdup(argv[i]); + if (data->argv[i] == NULL) + goto memerr; + l += strlen(data->argv[i]) + 1; + } + + /* Make up a description string. */ + if (archive_string_ensure(&data->description, l) == NULL) + goto memerr; + archive_strcpy(&data->description, prefix); + archive_strcpy(&data->description, cmd); + for (i = 0; argv[i] != NULL; i++) { + archive_strappend_char(&data->description, ' '); + archive_strcat(&data->description, data->argv[i]); + } - f->name = data->description; + f->name = data->description.s; f->data = data; f->open = &archive_compressor_program_open; f->code = ARCHIVE_COMPRESSION_PROGRAM; f->free = archive_compressor_program_free; return (ARCHIVE_OK); +memerr: + archive_compressor_program_free(f); + archive_set_error(_a, ENOMEM, + "Can't allocate memory for filter program"); + return (ARCHIVE_FATAL); } /* @@ -129,6 +254,7 @@ archive_compressor_program_open(struct archive_write_filter *f) } if ((data->child = __archive_create_child(data->cmd, + (char * const *)data->argv, &data->child_stdin, &data->child_stdout)) == -1) { archive_set_error(f->archive, EINVAL, "Can't initialise filter"); @@ -299,10 +425,19 @@ static int archive_compressor_program_free(struct archive_write_filter *f) { struct private_data *data = (struct private_data *)f->data; - free(data->cmd); - free(data->description); - free(data->child_buf); - free(data); - f->data = NULL; + + if (data) { + if (data->argv != NULL) { + int i; + for (i = 0; data->argv[i] != NULL; i++) + free(data->argv[i]); + free(data->argv); + } + free(data->cmd); + archive_string_free(&data->description); + free(data->child_buf); + free(data); + f->data = NULL; + } return (ARCHIVE_OK); } diff --git a/libarchive/filter_fork.c b/libarchive/filter_fork.c index 95796ac2b..bfaa5adce 100644 --- a/libarchive/filter_fork.c +++ b/libarchive/filter_fork.c @@ -70,13 +70,13 @@ __FBSDID("$FreeBSD: head/lib/libarchive/filter_fork.c 182958 2008-09-12 05:33:00 #include "filter_fork.h" pid_t -__archive_create_child(const char *path, int *child_stdin, int *child_stdout) +__archive_create_child(const char *path, char * const argv[], int *child_stdin, + int *child_stdout) { pid_t child; int stdin_pipe[2], stdout_pipe[2], tmp; #if HAVE_POSIX_SPAWNP posix_spawn_file_actions_t actions; - char *argv[2]; int r; #endif @@ -128,13 +128,7 @@ __archive_create_child(const char *path, int *child_stdin, int *child_stdout) if (r != 0) goto actions_inited; } - argv[0] = strdup(path); - if (argv[0] == NULL) - goto actions_inited2; - argv[1] = NULL; - r = posix_spawnp(&child, path, &actions, NULL, - (char ** const)argv, NULL); - free(argv[0]); + r = posix_spawnp(&child, path, &actions, NULL, argv, NULL); if (r != 0) goto actions_inited; posix_spawn_file_actions_destroy(&actions); @@ -159,7 +153,7 @@ __archive_create_child(const char *path, int *child_stdin, int *child_stdout) _exit(254); if (stdout_pipe[1] != 1 /* stdout */) close(stdout_pipe[1]); - execlp(path, path, (char *)NULL); + execvp(path, argv); _exit(254); } #endif /* HAVE_POSIX_SPAWNP */ @@ -177,7 +171,6 @@ __archive_create_child(const char *path, int *child_stdin, int *child_stdout) #if HAVE_POSIX_SPAWNP actions_inited: errno = r; -actions_inited2: posix_spawn_file_actions_destroy(&actions); #endif stdout_opened: diff --git a/libarchive/filter_fork.h b/libarchive/filter_fork.h index 453d032d1..1652c85d0 100644 --- a/libarchive/filter_fork.h +++ b/libarchive/filter_fork.h @@ -33,7 +33,8 @@ #define FILTER_FORK_H pid_t -__archive_create_child(const char *path, int *child_stdin, int *child_stdout); +__archive_create_child(const char *path, char * const argv[], int *child_stdin, + int *child_stdout); void __archive_check_child(int in, int out); diff --git a/libarchive/filter_fork_windows.c b/libarchive/filter_fork_windows.c index 8d8beb01e..c18ddff34 100644 --- a/libarchive/filter_fork_windows.c +++ b/libarchive/filter_fork_windows.c @@ -24,19 +24,43 @@ */ #include "archive_platform.h" +#include "archive_string.h" #if defined(_WIN32) && !defined(__CYGWIN__) #include "filter_fork.h" pid_t -__archive_create_child(const char *path, int *child_stdin, int *child_stdout) +__archive_create_child(const char *path, char * const argv[], int *child_stdin, + int *child_stdout) { HANDLE childStdout[2], childStdin[2],childStderr; SECURITY_ATTRIBUTES secAtts; STARTUPINFO staInfo; PROCESS_INFORMATION childInfo; - char cmd[MAX_PATH]; + struct archive_string appname; + struct archive_string cmdline; + int i, l; + + archive_string_init(&appname); + archive_string_init(&cmdline); + if (archive_string_ensure(&appname, strlen(path) + 1) == NULL) + goto fail; + archive_strcpy(&appname, path); + for (l = 0, i = 0; argv[i] != NULL; i++) { + if (i == 0) + continue; + l += strlen(argv[i]) + 1; + } + if (archive_string_ensure(&cmdline, l + 1) == NULL) + goto fail; + for (i = 0; argv[i] != NULL; i++) { + if (i == 0) + continue; + if (i > 1) + archive_strappend_char(&cmdline, ' '); + archive_strcat(&cmdline, argv[i]); + } secAtts.nLength = sizeof(SECURITY_ATTRIBUTES); secAtts.bInheritHandle = TRUE; @@ -79,10 +103,8 @@ __archive_create_child(const char *path, int *child_stdin, int *child_stdout) staInfo.hStdInput = childStdin[0]; staInfo.wShowWindow = SW_HIDE; staInfo.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; - strncpy(cmd, path, sizeof(cmd)-1); - cmd[sizeof(cmd)-1] = '\0'; - if (CreateProcessA(NULL, cmd, NULL, NULL, TRUE, 0, NULL, NULL, - &staInfo, &childInfo) == 0) { + if (CreateProcessA(appname.s, cmdline.s, NULL, NULL, TRUE, 0, + NULL, NULL, &staInfo, &childInfo) == 0) { CloseHandle(childStdout[0]); CloseHandle(childStdout[1]); CloseHandle(childStdin[0]); @@ -100,9 +122,13 @@ __archive_create_child(const char *path, int *child_stdin, int *child_stdout) CloseHandle(childStdout[1]); CloseHandle(childStdin[0]); + archive_string_free(&appname); + archive_string_free(&cmdline); return (childInfo.dwProcessId); fail: + archive_string_free(&appname); + archive_string_free(&cmdline); return (-1); } diff --git a/libarchive/test/CMakeLists.txt b/libarchive/test/CMakeLists.txt index 0ae41ae63..30846588f 100644 --- a/libarchive/test/CMakeLists.txt +++ b/libarchive/test/CMakeLists.txt @@ -76,6 +76,7 @@ IF(ENABLE_TEST) test_read_disk_entry_from_file.c test_read_extract.c test_read_file_nonexistent.c + test_read_filter_program_signature.c test_read_format_7zip.c test_read_format_ar.c test_read_format_cab.c diff --git a/libarchive/test/test_read_compress_program.c b/libarchive/test/test_read_compress_program.c index 751abd54f..92f7aed30 100644 --- a/libarchive/test/test_read_compress_program.c +++ b/libarchive/test/test_read_compress_program.c @@ -1,5 +1,6 @@ /*- * Copyright (c) 2003-2007 Tim Kientzle + * Copyright (c) 2012 Michhiro NAKAJIMA * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -82,3 +83,96 @@ DEFINE_TEST(test_read_compress_program) } +DEFINE_TEST(test_read_compress_programl) +{ + int r; + struct archive_entry *ae; + struct archive *a; + + /* + * First, test handling when a non-existent compression + * program is requested. + */ + assert((a = archive_read_new()) != NULL); + r = archive_read_support_filter_program(a, "nonexistent"); + if (r == ARCHIVE_FATAL) { + skipping("archive_read_support_filter_program() " + "unsupported on this platform"); + return; + } + assertEqualIntA(a, ARCHIVE_OK, r); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_support_format_all(a)); + assertEqualIntA(a, ARCHIVE_FATAL, + archive_read_open_memory(a, archive, sizeof(archive))); + assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); + + if (!canGzip()) { + skipping("Can't run gzip program on this platform"); + return; + } + assert((a = archive_read_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_support_filter_none(a)); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_support_filter_programl(a, "gzip", "gzip", + "-d", NULL)); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_support_format_all(a)); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_open_memory(a, archive, sizeof(archive))); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_next_header(a, &ae)); + assertEqualInt(archive_compression(a), ARCHIVE_COMPRESSION_PROGRAM); + assertEqualInt(archive_format(a), ARCHIVE_FORMAT_TAR_USTAR); + assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); +} + +DEFINE_TEST(test_read_compress_programv) +{ + int r; + struct archive_entry *ae; + struct archive *a; + char * const argv[] = {"gzip", "-d", NULL}; + + /* + * First, test handling when a non-existent compression + * program is requested. + */ + assert((a = archive_read_new()) != NULL); + r = archive_read_support_filter_program(a, "nonexistent"); + if (r == ARCHIVE_FATAL) { + skipping("archive_read_support_filter_program() " + "unsupported on this platform"); + return; + } + assertEqualIntA(a, ARCHIVE_OK, r); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_support_format_all(a)); + assertEqualIntA(a, ARCHIVE_FATAL, + archive_read_open_memory(a, archive, sizeof(archive))); + assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); + + if (!canGzip()) { + skipping("Can't run gzip program on this platform"); + return; + } + assert((a = archive_read_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_support_filter_none(a)); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_support_filter_programv(a, "gzip", argv)); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_support_format_all(a)); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_open_memory(a, archive, sizeof(archive))); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_next_header(a, &ae)); + assertEqualInt(archive_compression(a), ARCHIVE_COMPRESSION_PROGRAM); + assertEqualInt(archive_format(a), ARCHIVE_FORMAT_TAR_USTAR); + assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); +} diff --git a/libarchive/test/test_read_filter_program_signature.c b/libarchive/test/test_read_filter_program_signature.c new file mode 100644 index 000000000..0512560ee --- /dev/null +++ b/libarchive/test/test_read_filter_program_signature.c @@ -0,0 +1,231 @@ +/*- + * Copyright (c) 2003-2007 Tim Kientzle + * Copyright (c) 2012 Michhiro NAKAJIMA + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "test.h" +__FBSDID("$FreeBSD$"); + +static unsigned char archive[] = { +31,139,8,0,222,'C','p','C',0,3,211,'c',160,'=','0','0','0','0','7','5','U', +0,210,134,230,166,6,200,'4',28,'(',24,26,24,27,155,24,152,24,154,27,155,')', +24,24,26,152,154,25,'2','(',152,210,193,'m',12,165,197,'%',137,'E','@',167, +148,'d',230,226,'U','G','H',30,234,15,'8','=',10,'F',193,'(',24,5,131,28, +0,0,29,172,5,240,0,6,0,0}; +#define signature archive +#define badsignature (&archive[1]) +#define signature_len 4 + +DEFINE_TEST(test_read_filter_program_signature) +{ + int r; + struct archive_entry *ae; + struct archive *a; + + /* + * First, test handling when a non-existent compression + * program is requested. + */ + assert((a = archive_read_new()) != NULL); + r = archive_read_support_filter_program(a, "nonexistent"); + if (r == ARCHIVE_FATAL) { + skipping("archive_read_support_filter_program() " + "unsupported on this platform"); + return; + } + assertEqualIntA(a, ARCHIVE_OK, r); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_support_format_all(a)); + assertEqualIntA(a, ARCHIVE_FATAL, + archive_read_open_memory(a, archive, sizeof(archive))); + assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); + + /* + * If we have "gzip -d", try using that. + */ + if (!canGunzip()) { + skipping("Can't run gunzip program on this platform"); + return; + } + assert((a = archive_read_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_support_filter_none(a)); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_support_filter_program_signature(a, "gunzip", + signature, signature_len)); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_support_format_all(a)); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_open_memory(a, archive, sizeof(archive))); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_next_header(a, &ae)); + assertEqualInt(archive_compression(a), ARCHIVE_COMPRESSION_PROGRAM); + assertEqualInt(archive_format(a), ARCHIVE_FORMAT_TAR_USTAR); + assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); + + /* + * Test bad signature. + */ + assert((a = archive_read_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_support_filter_none(a)); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_support_filter_program_signature(a, "gunzip", + badsignature, signature_len)); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_support_format_all(a)); + assertEqualIntA(a, ARCHIVE_FATAL, + archive_read_open_memory(a, archive, sizeof(archive))); + assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); +} + + +DEFINE_TEST(test_read_filter_programl_signature) +{ + int r; + struct archive_entry *ae; + struct archive *a; + + /* + * First, test handling when a non-existent compression + * program is requested. + */ + assert((a = archive_read_new()) != NULL); + r = archive_read_support_filter_program(a, "nonexistent"); + if (r == ARCHIVE_FATAL) { + skipping("archive_read_support_filter_program() " + "unsupported on this platform"); + return; + } + assertEqualIntA(a, ARCHIVE_OK, r); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_support_format_all(a)); + assertEqualIntA(a, ARCHIVE_FATAL, + archive_read_open_memory(a, archive, sizeof(archive))); + assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); + + if (!canGzip()) { + skipping("Can't run gzip program on this platform"); + return; + } + assert((a = archive_read_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_support_filter_none(a)); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_support_filter_programl_signature(a, "gzip", "gzip", + "-d", NULL, signature, signature_len)); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_support_format_all(a)); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_open_memory(a, archive, sizeof(archive))); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_next_header(a, &ae)); + assertEqualInt(archive_compression(a), ARCHIVE_COMPRESSION_PROGRAM); + assertEqualInt(archive_format(a), ARCHIVE_FORMAT_TAR_USTAR); + assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); + + /* + * Test bad signature. + */ + assert((a = archive_read_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_support_filter_none(a)); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_support_filter_programl_signature(a, "gzip", "gzip", + "-d", NULL, badsignature, signature_len)); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_support_format_all(a)); + assertEqualIntA(a, ARCHIVE_FATAL, + archive_read_open_memory(a, archive, sizeof(archive))); + assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); +} + +DEFINE_TEST(test_read_filter_programv_signature) +{ + int r; + struct archive_entry *ae; + struct archive *a; + char * const argv[] = {"gzip", "-d", NULL}; + + /* + * First, test handling when a non-existent compression + * program is requested. + */ + assert((a = archive_read_new()) != NULL); + r = archive_read_support_filter_program(a, "nonexistent"); + if (r == ARCHIVE_FATAL) { + skipping("archive_read_support_filter_program() " + "unsupported on this platform"); + return; + } + assertEqualIntA(a, ARCHIVE_OK, r); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_support_format_all(a)); + assertEqualIntA(a, ARCHIVE_FATAL, + archive_read_open_memory(a, archive, sizeof(archive))); + assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); + + if (!canGzip()) { + skipping("Can't run gzip program on this platform"); + return; + } + assert((a = archive_read_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_support_filter_none(a)); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_support_filter_programv_signature(a, "gzip", argv, + signature, signature_len)); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_support_format_all(a)); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_open_memory(a, archive, sizeof(archive))); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_next_header(a, &ae)); + assertEqualInt(archive_compression(a), ARCHIVE_COMPRESSION_PROGRAM); + assertEqualInt(archive_format(a), ARCHIVE_FORMAT_TAR_USTAR); + assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); + + /* + * Test bad signature. + */ + assert((a = archive_read_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_support_filter_none(a)); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_support_filter_programv_signature(a, "gzip", argv, + badsignature, signature_len)); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_support_format_all(a)); + assertEqualIntA(a, ARCHIVE_FATAL, + archive_read_open_memory(a, archive, sizeof(archive))); + assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); +} diff --git a/libarchive/test/test_write_compress_program.c b/libarchive/test/test_write_compress_program.c index 4956e1a6f..63908aca6 100644 --- a/libarchive/test/test_write_compress_program.c +++ b/libarchive/test/test_write_compress_program.c @@ -1,5 +1,6 @@ /*- * Copyright (c) 2003-2007 Tim Kientzle + * Copyright (c) 2012 Michihiro NAKAJIMA * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -54,7 +55,196 @@ DEFINE_TEST(test_write_compress_program) /* Write it through an external "gzip" program. */ assert((a = archive_write_new()) != NULL); assertA(0 == archive_write_set_format_ustar(a)); - r = archive_write_set_compression_program(a, "gzip"); + r = archive_write_add_filter_program(a, "gzip"); + if (r == ARCHIVE_FATAL) { + skipping("Write compression via external " + "program unsupported on this platform"); + archive_write_free(a); + return; + } + assertA(0 == archive_write_set_bytes_per_block(a, blocksize)); + assertA(0 == archive_write_set_bytes_in_last_block(a, blocksize)); + assertA(blocksize == archive_write_get_bytes_in_last_block(a)); + assertA(0 == archive_write_open_memory(a, buff, sizeof(buff), &used)); + assertA(blocksize == archive_write_get_bytes_in_last_block(a)); + + /* + * Write a file to it. + */ + assert((ae = archive_entry_new()) != NULL); + archive_entry_set_mtime(ae, 1, 10); + archive_entry_copy_pathname(ae, "file"); + archive_entry_set_mode(ae, S_IFREG | 0755); + archive_entry_set_size(ae, 8); + + assertA(0 == archive_write_header(a, ae)); + archive_entry_free(ae); + assertA(8 == archive_write_data(a, "12345678", 9)); + + /* Close out the archive. */ + assertEqualIntA(a, ARCHIVE_OK, archive_write_close(a)); + assertEqualInt(ARCHIVE_OK, archive_write_free(a)); + + /* + * Now, read the data back through the built-in gzip support. + */ + assert((a = archive_read_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); + r = archive_read_support_filter_gzip(a); + /* The compression_gzip() handler will fall back to gunzip + * automatically, but if we know gunzip isn't available, then + * skip the rest. */ + if (r != ARCHIVE_OK && !canGunzip()) { + skipping("No libz and no gunzip program, " + "unable to verify gzip compression"); + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); + return; + } + assertEqualIntA(a, ARCHIVE_OK, archive_read_open_memory(a, buff, used)); + + if (!assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae))) { + archive_read_free(a); + return; + } + + assertEqualInt(1, archive_entry_mtime(ae)); + assertEqualInt(0, archive_entry_atime(ae)); + assertEqualInt(0, archive_entry_ctime(ae)); + assertEqualString("file", archive_entry_pathname(ae)); + assertEqualInt((S_IFREG | 0755), archive_entry_mode(ae)); + assertEqualInt(8, archive_entry_size(ae)); + assertEqualIntA(a, 8, archive_read_data(a, buff2, 10)); + assertEqualMem(buff2, "12345678", 8); + + /* Verify the end of the archive. */ + assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); +} + +DEFINE_TEST(test_write_compress_programl) +{ + struct archive_entry *ae; + struct archive *a; + size_t used; + int blocksize = 1024; + int r; + + if (!canGzip()) { + skipping("Cannot run 'gzip'"); + return; + } + /* NOTE: Setting blocksize=1024 will cause gunzip failure because + * it add extra bytes that gunzip ignores with its warning and + * exit code 1. So we should set blocksize=1 in order not to + * yield the extra bytes when using gunzip. */ + assert((a = archive_read_new()) != NULL); + r = archive_read_support_filter_gzip(a); + if (r != ARCHIVE_OK && canGunzip()) + blocksize = 1; + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); + + /* Create a new archive in memory. */ + /* Write it through an external "gzip" program. */ + assert((a = archive_write_new()) != NULL); + assertA(0 == archive_write_set_format_ustar(a)); + r = archive_write_add_filter_programl(a, "gzip", "gzip", NULL); + if (r == ARCHIVE_FATAL) { + skipping("Write compression via external " + "program unsupported on this platform"); + archive_write_free(a); + return; + } + assertA(0 == archive_write_set_bytes_per_block(a, blocksize)); + assertA(0 == archive_write_set_bytes_in_last_block(a, blocksize)); + assertA(blocksize == archive_write_get_bytes_in_last_block(a)); + assertA(0 == archive_write_open_memory(a, buff, sizeof(buff), &used)); + assertA(blocksize == archive_write_get_bytes_in_last_block(a)); + + /* + * Write a file to it. + */ + assert((ae = archive_entry_new()) != NULL); + archive_entry_set_mtime(ae, 1, 10); + archive_entry_copy_pathname(ae, "file"); + archive_entry_set_mode(ae, S_IFREG | 0755); + archive_entry_set_size(ae, 8); + + assertA(0 == archive_write_header(a, ae)); + archive_entry_free(ae); + assertA(8 == archive_write_data(a, "12345678", 9)); + + /* Close out the archive. */ + assertEqualIntA(a, ARCHIVE_OK, archive_write_close(a)); + assertEqualInt(ARCHIVE_OK, archive_write_free(a)); + + /* + * Now, read the data back through the built-in gzip support. + */ + assert((a = archive_read_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); + r = archive_read_support_filter_gzip(a); + /* The compression_gzip() handler will fall back to gunzip + * automatically, but if we know gunzip isn't available, then + * skip the rest. */ + if (r != ARCHIVE_OK && !canGunzip()) { + skipping("No libz and no gunzip program, " + "unable to verify gzip compression"); + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); + return; + } + assertEqualIntA(a, ARCHIVE_OK, archive_read_open_memory(a, buff, used)); + + if (!assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae))) { + archive_read_free(a); + return; + } + + assertEqualInt(1, archive_entry_mtime(ae)); + assertEqualInt(0, archive_entry_atime(ae)); + assertEqualInt(0, archive_entry_ctime(ae)); + assertEqualString("file", archive_entry_pathname(ae)); + assertEqualInt((S_IFREG | 0755), archive_entry_mode(ae)); + assertEqualInt(8, archive_entry_size(ae)); + assertEqualIntA(a, 8, archive_read_data(a, buff2, 10)); + assertEqualMem(buff2, "12345678", 8); + + /* Verify the end of the archive. */ + assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); +} + +DEFINE_TEST(test_write_compress_programv) +{ + struct archive_entry *ae; + struct archive *a; + size_t used; + int blocksize = 1024; + int r; + char * const argv[] = {"gzip", NULL}; + + if (!canGzip()) { + skipping("Cannot run 'gzip'"); + return; + } + /* NOTE: Setting blocksize=1024 will cause gunzip failure because + * it add extra bytes that gunzip ignores with its warning and + * exit code 1. So we should set blocksize=1 in order not to + * yield the extra bytes when using gunzip. */ + assert((a = archive_read_new()) != NULL); + r = archive_read_support_filter_gzip(a); + if (r != ARCHIVE_OK && canGunzip()) + blocksize = 1; + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); + + /* Create a new archive in memory. */ + /* Write it through an external "gzip" program. */ + assert((a = archive_write_new()) != NULL); + assertA(0 == archive_write_set_format_ustar(a)); + r = archive_write_add_filter_programv(a, "gzip", argv); if (r == ARCHIVE_FATAL) { skipping("Write compression via external " "program unsupported on this platform");