From: Michihiro NAKAJIMA Date: Mon, 8 Oct 2012 00:38:59 +0000 (+0900) Subject: Introduce uuencode filter. X-Git-Tag: v3.1.0~40^2~75 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=1cbc562a4f839570871baace28f55c14b2fe4e94;p=thirdparty%2Flibarchive.git Introduce uuencode filter. --- diff --git a/Makefile.am b/Makefile.am index 166f4b6da..c4c0f9ca9 100644 --- a/Makefile.am +++ b/Makefile.am @@ -180,6 +180,7 @@ libarchive_la_SOURCES= \ libarchive/archive_write_add_filter_lzop.c \ libarchive/archive_write_add_filter_none.c \ libarchive/archive_write_add_filter_program.c \ + libarchive/archive_write_add_filter_uuencode.c \ libarchive/archive_write_add_filter_xz.c \ libarchive/archive_write_set_format.c \ libarchive/archive_write_set_format_7zip.c \ @@ -419,6 +420,7 @@ libarchive_test_SOURCES= \ libarchive/test/test_write_disk_sparse.c \ libarchive/test/test_write_disk_symlink.c \ libarchive/test/test_write_disk_times.c \ + libarchive/test/test_write_filter_uuencode.c \ libarchive/test/test_write_format_7zip.c \ libarchive/test/test_write_format_ar.c \ libarchive/test/test_write_format_cpio.c \ @@ -737,6 +739,7 @@ bsdtar_test_SOURCES= \ tar/test/test_option_r.c \ tar/test/test_option_s.c \ tar/test/test_option_uid_uname.c \ + tar/test/test_option_uuencode.c \ tar/test/test_option_xz.c \ tar/test/test_patterns.c \ tar/test/test_print_longpath.c \ @@ -856,6 +859,7 @@ bsdcpio_test_SOURCES= \ cpio/test/test_option_m.c \ cpio/test/test_option_t.c \ cpio/test/test_option_u.c \ + cpio/test/test_option_uuencode.c \ cpio/test/test_option_version.c \ cpio/test/test_option_xz.c \ cpio/test/test_option_y.c \ diff --git a/cpio/cmdline.c b/cpio/cmdline.c index bd54880b6..232aed6b8 100644 --- a/cpio/cmdline.c +++ b/cpio/cmdline.c @@ -83,6 +83,7 @@ static const struct option { { "preserve-owner", 0, OPTION_PRESERVE_OWNER }, { "quiet", 0, OPTION_QUIET }, { "unconditional", 0, 'u' }, + { "uuencode", 0, OPTION_UUENCODE }, { "verbose", 0, 'v' }, { "version", 0, OPTION_VERSION }, { "xz", 0, 'J' }, diff --git a/cpio/cpio.c b/cpio/cpio.c index ca722579a..9feb5e3e2 100644 --- a/cpio/cpio.c +++ b/cpio/cpio.c @@ -328,6 +328,9 @@ main(int argc, char *argv[]) cpio->extract_flags &= ~ARCHIVE_EXTRACT_NO_OVERWRITE_NEWER; break; + case OPTION_UUENCODE: + cpio->add_filter = opt; + break; case 'v': /* POSIX 1997 */ cpio->verbose++; break; @@ -546,6 +549,16 @@ mode_out(struct cpio *cpio) } if (r < ARCHIVE_WARN) lafe_errc(1, 0, "Requested compression not available"); + switch (cpio->add_filter) { + case 0: + r = ARCHIVE_OK; + break; + case OPTION_UUENCODE: + r = archive_write_add_filter_uuencode(cpio->archive); + break; + } + if (r < ARCHIVE_WARN) + lafe_errc(1, 0, "Requested filter not available"); r = archive_write_set_format_by_name(cpio->archive, cpio->format); if (r != ARCHIVE_OK) lafe_errc(1, 0, "%s", archive_error_string(cpio->archive)); diff --git a/cpio/cpio.h b/cpio/cpio.h index 6d052bbd9..31f038b7d 100644 --- a/cpio/cpio.h +++ b/cpio/cpio.h @@ -44,6 +44,7 @@ struct cpio { const char *argument; /* Options */ + int add_filter; /* --uuencode */ const char *filename; int mode; /* -i -o -p */ int compress; /* -j, -y, or -z */ @@ -103,6 +104,7 @@ enum { OPTION_NO_PRESERVE_OWNER, OPTION_PRESERVE_OWNER, OPTION_QUIET, + OPTION_UUENCODE, OPTION_VERSION }; diff --git a/cpio/test/CMakeLists.txt b/cpio/test/CMakeLists.txt index 69db631a1..1c1ac89de 100644 --- a/cpio/test/CMakeLists.txt +++ b/cpio/test/CMakeLists.txt @@ -33,6 +33,7 @@ IF(ENABLE_CPIO AND ENABLE_TEST) test_option_m.c test_option_t.c test_option_u.c + test_option_uuencode.c test_option_version.c test_option_xz.c test_option_y.c diff --git a/cpio/test/test_option_uuencode.c b/cpio/test/test_option_uuencode.c new file mode 100644 index 000000000..ecf354f8f --- /dev/null +++ b/cpio/test/test_option_uuencode.c @@ -0,0 +1,54 @@ +/*- + * 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 + * 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$"); + +DEFINE_TEST(test_option_uuencode) +{ + char *p; + size_t s; + + /* Create a file. */ + assertMakeFile("f", 0644, "a"); + + /* Archive it with compress compression and uuencode. */ + assertEqualInt(0, + systemf("echo f | %s -o -Z --uuencode >archive.out 2>archive.err", + testprog)); + /* Check that the archive file has an uuencode signature. */ + p = slurpfile(&s, "archive.out"); + assert(s > 2); + assertEqualMem(p, "begin 644", 9); + + /* Archive it with uuencode only. */ + assertEqualInt(0, + systemf("echo f | %s -o --uuencode >archive.out 2>archive.err", + testprog)); + /* Check that the archive file has an uuencode signature. */ + p = slurpfile(&s, "archive.out"); + assert(s > 2); + assertEqualMem(p, "begin 644", 9); +} diff --git a/libarchive/CMakeLists.txt b/libarchive/CMakeLists.txt index 0c26a5c4a..14c77e2e8 100644 --- a/libarchive/CMakeLists.txt +++ b/libarchive/CMakeLists.txt @@ -104,6 +104,7 @@ SET(libarchive_SOURCES archive_write_add_filter_lzop.c archive_write_add_filter_none.c archive_write_add_filter_program.c + archive_write_add_filter_uuencode.c archive_write_add_filter_xz.c archive_write_set_format.c archive_write_set_format_7zip.c diff --git a/libarchive/archive.h b/libarchive/archive.h index d31de44fb..3922bfa50 100644 --- a/libarchive/archive.h +++ b/libarchive/archive.h @@ -627,6 +627,7 @@ __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_uuencode(struct archive *); __LA_DECL int archive_write_add_filter_xz(struct archive *); diff --git a/libarchive/archive_write_add_filter_uuencode.c b/libarchive/archive_write_add_filter_uuencode.c new file mode 100644 index 000000000..6535f1a2d --- /dev/null +++ b/libarchive/archive_write_add_filter_uuencode.c @@ -0,0 +1,305 @@ +/*- + * Copyright (c) 2012 Michihiro 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 "archive_platform.h" + +__FBSDID("$FreeBSD$"); + +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif + +#include "archive.h" +#include "archive_private.h" +#include "archive_string.h" +#include "archive_write_private.h" + +#define LBYTES 45 + +struct private_uuencode { + int mode; + struct archive_string name; + struct archive_string encoded_buff; + size_t bs; + size_t hold_len; + unsigned char hold[LBYTES]; +}; + +static int archive_filter_uuencode_options(struct archive_write_filter *, + const char *, const char *); +static int archive_filter_uuencode_open(struct archive_write_filter *); +static int archive_filter_uuencode_write(struct archive_write_filter *, + const void *, size_t); +static int archive_filter_uuencode_close(struct archive_write_filter *); +static int archive_filter_uuencode_free(struct archive_write_filter *); +static void uu_encode(struct archive_string *, const unsigned char *, size_t); +static int64_t atol8(const char *, size_t); + +/* + * Add a compress filter to this write handle. + */ +int +archive_write_add_filter_uuencode(struct archive *_a) +{ + struct archive_write *a = (struct archive_write *)_a; + struct archive_write_filter *f = __archive_write_allocate_filter(_a); + struct private_uuencode *state; + + archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, + ARCHIVE_STATE_NEW, "archive_write_add_filter_uu"); + + state = (struct private_uuencode *)calloc(1, sizeof(*state)); + if (state == NULL) { + archive_set_error(f->archive, ENOMEM, + "Can't allocate data for uuencode filter"); + return (ARCHIVE_FATAL); + } + archive_strcpy(&state->name, "-"); + state->mode = 0644; + + f->data = state; + f->name = "uuencode"; + f->code = ARCHIVE_FILTER_UU; + f->open = archive_filter_uuencode_open; + f->options = archive_filter_uuencode_options; + f->write = archive_filter_uuencode_write; + f->close = archive_filter_uuencode_close; + f->free = archive_filter_uuencode_free; + + return (ARCHIVE_OK); +} + +/* + * Set write options. + */ +static int +archive_filter_uuencode_options(struct archive_write_filter *f, const char *key, + const char *value) +{ + struct private_uuencode *state = (struct private_uuencode *)f->data; + + if (strcmp(key, "mode") == 0) { + if (value == NULL) { + archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, + "mode option requires octal digits"); + return (ARCHIVE_FAILED); + } + state->mode = (int)atol8(value, strlen(value)) & 0777; + return (ARCHIVE_OK); + } else if (strcmp(key, "name") == 0) { + if (value == NULL) { + archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, + "name option requires a string"); + return (ARCHIVE_FAILED); + } + archive_strcpy(&state->name, value); + return (ARCHIVE_OK); + } + + /* Note: The "warn" return is just to inform the options + * supervisor that we didn't handle it. It will generate + * a suitable error if no one used this option. */ + return (ARCHIVE_WARN); +} + +/* + * Setup callback. + */ +static int +archive_filter_uuencode_open(struct archive_write_filter *f) +{ + struct private_uuencode *state = (struct private_uuencode *)f->data; + size_t bs = 65536, bpb; + int ret; + + ret = __archive_write_open_filter(f->next_filter); + if (ret != ARCHIVE_OK) + return (ret); + + if (f->archive->magic == ARCHIVE_WRITE_MAGIC) { + /* Buffer size should be a multiple number of the of bytes + * per block for performance. */ + bpb = archive_write_get_bytes_per_block(f->archive); + if (bpb > bs) + bs = bpb; + else if (bpb != 0) + bs -= bs % bpb; + } + + state->bs = bs; + if (archive_string_ensure(&state->encoded_buff, bs + 512) == NULL) { + archive_set_error(f->archive, ENOMEM, + "Can't allocate data for uuencode buffer"); + return (ARCHIVE_FATAL); + } + + archive_string_sprintf(&state->encoded_buff, "begin %o %s\n", + state->mode, state->name.s); + + f->data = state; + return (0); +} + +static void +uu_encode(struct archive_string *as, const unsigned char *p, size_t len) +{ + int c; + + c = len; + archive_strappend_char(as, c?c + 0x20:'`'); + for (; len >= 3; p += 3, len -= 3) { + c = p[0] >> 2; + archive_strappend_char(as, c?c + 0x20:'`'); + c = ((p[0] & 0x03) << 4) | ((p[1] & 0xf0) >> 4); + archive_strappend_char(as, c?c + 0x20:'`'); + c = ((p[1] & 0x0f) << 2) | ((p[2] & 0xc0) >> 6); + archive_strappend_char(as, c?c + 0x20:'`'); + c = p[2] & 0x3f; + archive_strappend_char(as, c?c + 0x20:'`'); + } + if (len > 0) { + c = p[0] >> 2; + archive_strappend_char(as, c?c + 0x20:'`'); + c = (p[0] & 0x03) << 4; + if (len == 1) { + archive_strappend_char(as, c?c + 0x20:'`'); + archive_strappend_char(as, '`'); + archive_strappend_char(as, '`'); + } else { + c |= (p[1] & 0xf0) >> 4; + archive_strappend_char(as, c?c + 0x20:'`'); + c = (p[1] & 0x0f) << 2; + archive_strappend_char(as, c?c + 0x20:'`'); + archive_strappend_char(as, '`'); + } + } + archive_strappend_char(as, '\n'); +} + +/* + * Write data to the encoded stream. + */ +static int +archive_filter_uuencode_write(struct archive_write_filter *f, const void *buff, + size_t length) +{ + struct private_uuencode *state = (struct private_uuencode *)f->data; + const unsigned char *p = buff; + int ret = ARCHIVE_OK; + + if (length == 0) + return (ret); + + if (state->hold_len) { + while (state->hold_len < LBYTES && length > 0) { + state->hold[state->hold_len++] = *p++; + length--; + } + if (state->hold_len < LBYTES) + return (ret); + uu_encode(&state->encoded_buff, state->hold, LBYTES); + state->hold_len = 0; + } + + for (; length >= LBYTES; length -= LBYTES, p += LBYTES) + uu_encode(&state->encoded_buff, p, LBYTES); + + /* Save remaining bytes. */ + if (length > 0) { + memcpy(state->hold, p, length); + state->hold_len = length; + } + while (archive_strlen(&state->encoded_buff) >= state->bs) { + ret = __archive_write_filter(f->next_filter, + state->encoded_buff.s, state->bs); + memmove(state->encoded_buff.s, + state->encoded_buff.s + state->bs, + state->encoded_buff.length - state->bs); + state->encoded_buff.length -= state->bs; + } + + return (ret); +} + + +/* + * Finish the compression... + */ +static int +archive_filter_uuencode_close(struct archive_write_filter *f) +{ + struct private_uuencode *state = (struct private_uuencode *)f->data; + int ret, ret2; + + /* Flush remaining bytes. */ + if (state->hold_len != 0) + uu_encode(&state->encoded_buff, state->hold, state->hold_len); + archive_string_sprintf(&state->encoded_buff, "`\nend\n"); + /* Write the last block */ + archive_write_set_bytes_in_last_block(f->archive, 1); + ret = __archive_write_filter(f->next_filter, + state->encoded_buff.s, archive_strlen(&state->encoded_buff)); + ret2 = __archive_write_close_filter(f->next_filter); + if (ret > ret2) + ret = ret2; + return (ret); +} + +static int +archive_filter_uuencode_free(struct archive_write_filter *f) +{ + struct private_uuencode *state = (struct private_uuencode *)f->data; + + archive_string_free(&state->name); + archive_string_free(&state->encoded_buff); + free(state); + return (ARCHIVE_OK); +} + +static int64_t +atol8(const char *p, size_t char_cnt) +{ + int64_t l; + int digit; + + l = 0; + while (char_cnt-- > 0) { + if (*p >= '0' && *p <= '7') + digit = *p - '0'; + else + break; + p++; + l <<= 3; + l |= digit; + } + return (l); +} + diff --git a/libarchive/test/CMakeLists.txt b/libarchive/test/CMakeLists.txt index b5a1e6ce4..ed1464685 100644 --- a/libarchive/test/CMakeLists.txt +++ b/libarchive/test/CMakeLists.txt @@ -162,6 +162,7 @@ IF(ENABLE_TEST) test_write_disk_sparse.c test_write_disk_symlink.c test_write_disk_times.c + test_write_filter_uuencode.c test_write_format_7zip.c test_write_format_ar.c test_write_format_cpio.c diff --git a/libarchive/test/test_write_filter_uuencode.c b/libarchive/test/test_write_filter_uuencode.c new file mode 100644 index 000000000..57a4b49bd --- /dev/null +++ b/libarchive/test/test_write_filter_uuencode.c @@ -0,0 +1,173 @@ +/*- + * Copyright (c) 2012 Michihiro 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 + * in this position and unchanged. + * 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$"); + +/* + * A basic exercise of uuencode reading and writing. + */ + +DEFINE_TEST(test_write_filter_uuencode) +{ + struct archive_entry *ae; + struct archive* a; + char *buff, *data; + size_t buffsize, datasize; + char path[16]; + size_t used1, used2; + int i; + + buffsize = 2000000; + assert(NULL != (buff = (char *)malloc(buffsize))); + + datasize = 10000; + assert(NULL != (data = (char *)malloc(datasize))); + memset(data, 0, datasize); + + /* + * Write a 100 files and read them all back. + */ + assert((a = archive_write_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, archive_write_set_format_ustar(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_write_add_filter_uuencode(a)); + assertEqualIntA(a, ARCHIVE_OK, + archive_write_set_bytes_per_block(a, 10)); + assertEqualInt(ARCHIVE_FILTER_UU, archive_filter_code(a, 0)); + assertEqualString("uuencode", archive_filter_name(a, 0)); + assertEqualIntA(a, ARCHIVE_OK, + archive_write_open_memory(a, buff, buffsize, &used1)); + for (i = 0; i < 99; i++) { + assert((ae = archive_entry_new()) != NULL); + archive_entry_set_filetype(ae, AE_IFREG); + archive_entry_set_size(ae, datasize); + sprintf(path, "file%03d", i); + archive_entry_copy_pathname(ae, path); + assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae)); + assertA(datasize + == (size_t)archive_write_data(a, data, datasize)); + archive_entry_free(ae); + } + assertEqualIntA(a, ARCHIVE_OK, archive_write_close(a)); + assertEqualInt(ARCHIVE_OK, archive_write_free(a)); + + 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)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_open_memory(a, buff, used1)); + for (i = 0; i < 99; i++) { + sprintf(path, "file%03d", i); + if (!assertEqualIntA(a, 0, archive_read_next_header(a, &ae))) + break; + assertEqualString(path, archive_entry_pathname(ae)); + assertEqualInt((int)datasize, archive_entry_size(ae)); + } + assertEqualInt(ARCHIVE_FILTER_UU, archive_filter_code(a, 0)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); + + /* + * Repeat the cycle again, this time setting name and mode + * options. + */ + assert((a = archive_write_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, archive_write_set_format_ustar(a)); + assertEqualIntA(a, ARCHIVE_OK, + archive_write_set_bytes_per_block(a, 10)); + assertEqualIntA(a, ARCHIVE_OK, archive_write_add_filter_uuencode(a)); + assertEqualIntA(a, ARCHIVE_FAILED, + archive_write_set_filter_option(a, NULL, "nonexistent-option", "0")); + assertEqualIntA(a, ARCHIVE_FAILED, + archive_write_set_filter_option(a, NULL, "compression-level", "abc")); + assertEqualIntA(a, ARCHIVE_FAILED, + archive_write_set_filter_option(a, NULL, "compression-level", "99")); + assertEqualIntA(a, ARCHIVE_OK, + archive_write_set_filter_option(a, NULL, "name", "test.tar")); + assertEqualIntA(a, ARCHIVE_OK, + archive_write_set_filter_option(a, NULL, "mode", "0640")); + assertEqualIntA(a, ARCHIVE_OK, + archive_write_open_memory(a, buff, buffsize, &used2)); + for (i = 0; i < 99; i++) { + sprintf(path, "file%03d", i); + assert((ae = archive_entry_new()) != NULL); + archive_entry_copy_pathname(ae, path); + archive_entry_set_size(ae, datasize); + archive_entry_set_filetype(ae, AE_IFREG); + assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae)); + assertA(datasize == (size_t)archive_write_data(a, data, datasize)); + archive_entry_free(ae); + } + assertEqualIntA(a, ARCHIVE_OK, archive_write_close(a)); + assertEqualInt(ARCHIVE_OK, archive_write_free(a)); + + 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)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_open_memory(a, buff, used2)); + for (i = 0; i < 99; i++) { + sprintf(path, "file%03d", i); + if (!assertEqualInt(0, archive_read_next_header(a, &ae))) + break; + assertEqualString(path, archive_entry_pathname(ae)); + assertEqualInt((int)datasize, archive_entry_size(ae)); + } + assertEqualInt(ARCHIVE_FILTER_UU, archive_filter_code(a, 0)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); + + /* + * Test various premature shutdown scenarios to make sure we + * don't crash or leak memory. + */ + assert((a = archive_write_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, archive_write_add_filter_uuencode(a)); + assertEqualInt(ARCHIVE_OK, archive_write_free(a)); + + assert((a = archive_write_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, archive_write_add_filter_uuencode(a)); + assertEqualInt(ARCHIVE_OK, archive_write_close(a)); + assertEqualInt(ARCHIVE_OK, archive_write_free(a)); + + assert((a = archive_write_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, archive_write_set_format_ustar(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_write_add_filter_uuencode(a)); + assertEqualInt(ARCHIVE_OK, archive_write_close(a)); + assertEqualInt(ARCHIVE_OK, archive_write_free(a)); + + assert((a = archive_write_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, archive_write_set_format_ustar(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_write_add_filter_uuencode(a)); + assertEqualIntA(a, ARCHIVE_OK, + archive_write_open_memory(a, buff, buffsize, &used2)); + assertEqualInt(ARCHIVE_OK, archive_write_close(a)); + assertEqualInt(ARCHIVE_OK, archive_write_free(a)); + + /* + * Clean up. + */ + free(data); + free(buff); +} diff --git a/tar/bsdtar.c b/tar/bsdtar.c index f4ae865af..4b167a85e 100644 --- a/tar/bsdtar.c +++ b/tar/bsdtar.c @@ -572,6 +572,9 @@ main(int argc, char **argv) case OPTION_UNAME: /* cpio */ bsdtar->uname = bsdtar->argument; break; + case OPTION_UUENCODE: + bsdtar->add_filter = opt; + break; case 'v': /* SUSv2 */ bsdtar->verbose++; break; diff --git a/tar/bsdtar.h b/tar/bsdtar.h index 506702826..91af45214 100644 --- a/tar/bsdtar.h +++ b/tar/bsdtar.h @@ -58,6 +58,7 @@ struct bsdtar { char symlink_mode; /* H or L, per BSD conventions */ char create_compression; /* j, y, or z */ const char *compress_program; + char add_filter; /* uuencode */ char option_absolute_paths; /* -P */ char option_chroot; /* --chroot */ char option_fast_read; /* --fast-read */ @@ -147,6 +148,7 @@ enum { OPTION_UID, OPTION_UNAME, OPTION_USE_COMPRESS_PROGRAM, + OPTION_UUENCODE, OPTION_VERSION }; diff --git a/tar/cmdline.c b/tar/cmdline.c index af599ea53..05d4e2616 100644 --- a/tar/cmdline.c +++ b/tar/cmdline.c @@ -137,6 +137,7 @@ static const struct bsdtar_option { { "unlink-first", 0, 'U' }, { "update", 0, 'u' }, { "use-compress-program", 1, OPTION_USE_COMPRESS_PROGRAM }, + { "uuencode", 0, OPTION_UUENCODE }, { "verbose", 0, 'v' }, { "version", 0, OPTION_VERSION }, { "xz", 0, 'J' }, diff --git a/tar/test/CMakeLists.txt b/tar/test/CMakeLists.txt index 862e10007..6ff9036c9 100644 --- a/tar/test/CMakeLists.txt +++ b/tar/test/CMakeLists.txt @@ -43,6 +43,7 @@ IF(ENABLE_TAR AND ENABLE_TEST) test_option_r.c test_option_s.c test_option_uid_uname.c + test_option_uuencode.c test_option_xz.c test_patterns.c test_print_longpath.c diff --git a/tar/test/test_option_uuencode.c b/tar/test/test_option_uuencode.c new file mode 100644 index 000000000..cdc6babd5 --- /dev/null +++ b/tar/test/test_option_uuencode.c @@ -0,0 +1,54 @@ +/*- + * 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 + * 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$"); + +DEFINE_TEST(test_option_uuencode) +{ + char *p; + size_t s; + + /* Create a file. */ + assertMakeFile("f", 0644, "a"); + + /* Archive it with compress compression and uuencode. */ + assertEqualInt(0, + systemf("%s -cf - -Z --uuencode f >archive.out 2>archive.err", + testprog)); + /* Check that the archive file has an uuencode signature. */ + p = slurpfile(&s, "archive.out"); + assert(s > 2); + assertEqualMem(p, "begin 644", 9); + + /* Archive it with uuencode only. */ + assertEqualInt(0, + systemf("%s -cf - --uuencode f >archive.out 2>archive.err", + testprog)); + /* Check that the archive file has an uuencode signature. */ + p = slurpfile(&s, "archive.out"); + assert(s > 2); + assertEqualMem(p, "begin 644", 9); +} diff --git a/tar/write.c b/tar/write.c index 66c7e17ef..89fdc8559 100644 --- a/tar/write.c +++ b/tar/write.c @@ -168,6 +168,8 @@ tar_mode_c(struct bsdtar *bsdtar) if (bsdtar->compress_program) { archive_write_add_filter_program(a, bsdtar->compress_program); } else { + const char *name; + switch (bsdtar->create_compression) { case 0: r = ARCHIVE_OK; @@ -206,6 +208,23 @@ tar_mode_c(struct bsdtar *bsdtar) "Unsupported compression option -%c", bsdtar->create_compression); } + switch (bsdtar->add_filter) { + case 0: + r = ARCHIVE_OK; + break; + case OPTION_UUENCODE: + r = archive_write_add_filter_uuencode(a); + name = "uuencode"; + break; + default: + lafe_errc(1, 0, + "Unrecognized compression option -%c", + bsdtar->add_filter); + } + if (r < ARCHIVE_WARN) { + lafe_errc(1, 0, + "Unsupported filter option --%s", name); + } } if (ARCHIVE_OK != archive_write_set_options(a, bsdtar->option_options))