From ba3b58119e3c28885b17e1af5e82dbe9100c6ea4 Mon Sep 17 00:00:00 2001 From: Michihiro NAKAJIMA Date: Mon, 8 Oct 2012 12:38:04 +0900 Subject: [PATCH] Introduce b64encode filter. --- Makefile.am | 4 + cpio/cmdline.c | 1 + cpio/cpio.c | 6 + cpio/cpio.h | 3 +- cpio/test/CMakeLists.txt | 1 + cpio/test/test_option_b64encode.c | 54 +++ libarchive/CMakeLists.txt | 1 + libarchive/archive.h | 1 + .../archive_write_add_filter_b64encode.c | 314 ++++++++++++++++++ libarchive/test/CMakeLists.txt | 1 + libarchive/test/test_write_filter_b64encode.c | 173 ++++++++++ tar/bsdtar.c | 11 + tar/bsdtar.h | 3 +- tar/cmdline.c | 1 + tar/test/CMakeLists.txt | 1 + tar/test/test_option_b64encode.c | 54 +++ tar/write.c | 6 +- 17 files changed, 632 insertions(+), 3 deletions(-) create mode 100644 cpio/test/test_option_b64encode.c create mode 100644 libarchive/archive_write_add_filter_b64encode.c create mode 100644 libarchive/test/test_write_filter_b64encode.c create mode 100644 tar/test/test_option_b64encode.c diff --git a/Makefile.am b/Makefile.am index c4c0f9ca9..4c408efbe 100644 --- a/Makefile.am +++ b/Makefile.am @@ -173,6 +173,7 @@ libarchive_la_SOURCES= \ libarchive/archive_write_open_memory.c \ libarchive/archive_write_private.h \ libarchive/archive_write_add_filter.c \ + libarchive/archive_write_add_filter_b64encode.c \ libarchive/archive_write_add_filter_bzip2.c \ libarchive/archive_write_add_filter_compress.c \ libarchive/archive_write_add_filter_gzip.c \ @@ -420,6 +421,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_b64encode.c \ libarchive/test/test_write_filter_uuencode.c \ libarchive/test/test_write_format_7zip.c \ libarchive/test/test_write_format_ar.c \ @@ -724,6 +726,7 @@ bsdtar_test_SOURCES= \ tar/test/test_option_U_upper.c \ tar/test/test_option_X_upper.c \ tar/test/test_option_b.c \ + tar/test/test_option_b64encode.c \ tar/test/test_option_exclude.c \ tar/test/test_option_gid_gname.c \ tar/test/test_option_k.c \ @@ -848,6 +851,7 @@ bsdcpio_test_SOURCES= \ cpio/test/test_option_L_upper.c \ cpio/test/test_option_Z_upper.c \ cpio/test/test_option_a.c \ + cpio/test/test_option_b64encode.c \ cpio/test/test_option_c.c \ cpio/test/test_option_d.c \ cpio/test/test_option_f.c \ diff --git a/cpio/cmdline.c b/cpio/cmdline.c index 232aed6b8..3dff0b6b3 100644 --- a/cpio/cmdline.c +++ b/cpio/cmdline.c @@ -61,6 +61,7 @@ static const struct option { int required; /* 1 if this option requires an argument */ int equivalent; /* Equivalent short option. */ } cpio_longopts[] = { + { "b64encode", 0, OPTION_B64ENCODE }, { "create", 0, 'o' }, { "dot", 0, 'V' }, { "extract", 0, 'i' }, diff --git a/cpio/cpio.c b/cpio/cpio.c index 9feb5e3e2..89fc25bd0 100644 --- a/cpio/cpio.c +++ b/cpio/cpio.c @@ -207,6 +207,9 @@ main(int argc, char *argv[]) case 'B': /* POSIX 1997 */ cpio->bytes_per_block = 5120; break; + case OPTION_B64ENCODE: + cpio->add_filter = opt; + break; case 'C': /* NetBSD/OpenBSD */ cpio->bytes_per_block = atoi(cpio->argument); if (cpio->bytes_per_block <= 0) @@ -553,6 +556,9 @@ mode_out(struct cpio *cpio) case 0: r = ARCHIVE_OK; break; + case OPTION_B64ENCODE: + r = archive_write_add_filter_b64encode(cpio->archive); + break; case OPTION_UUENCODE: r = archive_write_add_filter_uuencode(cpio->archive); break; diff --git a/cpio/cpio.h b/cpio/cpio.h index 31f038b7d..b1d89427f 100644 --- a/cpio/cpio.h +++ b/cpio/cpio.h @@ -97,7 +97,8 @@ const char *owner_parse(const char *, int *, int *); /* Fake short equivalents for long options that otherwise lack them. */ enum { - OPTION_INSECURE = 1, + OPTION_B64ENCODE = 1, + OPTION_INSECURE, OPTION_LRZIP, OPTION_LZMA, OPTION_LZOP, diff --git a/cpio/test/CMakeLists.txt b/cpio/test/CMakeLists.txt index 1c1ac89de..55a12e7a9 100644 --- a/cpio/test/CMakeLists.txt +++ b/cpio/test/CMakeLists.txt @@ -22,6 +22,7 @@ IF(ENABLE_CPIO AND ENABLE_TEST) test_option_L_upper.c test_option_Z_upper.c test_option_a.c + test_option_b64encode.c test_option_c.c test_option_d.c test_option_f.c diff --git a/cpio/test/test_option_b64encode.c b/cpio/test/test_option_b64encode.c new file mode 100644 index 000000000..8f6b4157c --- /dev/null +++ b/cpio/test/test_option_b64encode.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_b64encode) +{ + 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 --b64encode >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-base64 644", 16); + + /* Archive it with uuencode only. */ + assertEqualInt(0, + systemf("echo f | %s -o --b64encode >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-base64 644", 16); +} diff --git a/libarchive/CMakeLists.txt b/libarchive/CMakeLists.txt index 14c77e2e8..de2a40aa6 100644 --- a/libarchive/CMakeLists.txt +++ b/libarchive/CMakeLists.txt @@ -97,6 +97,7 @@ SET(libarchive_SOURCES archive_write_open_filename.c archive_write_open_memory.c archive_write_add_filter.c + archive_write_add_filter_b64encode.c archive_write_add_filter_bzip2.c archive_write_add_filter_compress.c archive_write_add_filter_gzip.c diff --git a/libarchive/archive.h b/libarchive/archive.h index 3922bfa50..ae19c2505 100644 --- a/libarchive/archive.h +++ b/libarchive/archive.h @@ -613,6 +613,7 @@ __LA_DECL int archive_write_set_compression_xz(struct archive *); /* A convenience function to set the filter based on the code. */ __LA_DECL int archive_write_add_filter(struct archive *, int filter_code); +__LA_DECL int archive_write_add_filter_b64encode(struct archive *); __LA_DECL int archive_write_add_filter_bzip2(struct archive *); __LA_DECL int archive_write_add_filter_compress(struct archive *); __LA_DECL int archive_write_add_filter_gzip(struct archive *); diff --git a/libarchive/archive_write_add_filter_b64encode.c b/libarchive/archive_write_add_filter_b64encode.c new file mode 100644 index 000000000..85eb087b0 --- /dev/null +++ b/libarchive/archive_write_add_filter_b64encode.c @@ -0,0 +1,314 @@ +/*- + * 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 57 + +struct private_b64encode { + 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_b64encode_options(struct archive_write_filter *, + const char *, const char *); +static int archive_filter_b64encode_open(struct archive_write_filter *); +static int archive_filter_b64encode_write(struct archive_write_filter *, + const void *, size_t); +static int archive_filter_b64encode_close(struct archive_write_filter *); +static int archive_filter_b64encode_free(struct archive_write_filter *); +static void b64_encode(struct archive_string *, const unsigned char *, size_t); +static int64_t atol8(const char *, size_t); + +static const char base64[] = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', + 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', + 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', + 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', + 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', + 'w', 'x', 'y', 'z', '0', '1', '2', '3', + '4', '5', '6', '7', '8', '9', '+', '/' +}; + +/* + * Add a compress filter to this write handle. + */ +int +archive_write_add_filter_b64encode(struct archive *_a) +{ + struct archive_write *a = (struct archive_write *)_a; + struct archive_write_filter *f = __archive_write_allocate_filter(_a); + struct private_b64encode *state; + + archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, + ARCHIVE_STATE_NEW, "archive_write_add_filter_uu"); + + state = (struct private_b64encode *)calloc(1, sizeof(*state)); + if (state == NULL) { + archive_set_error(f->archive, ENOMEM, + "Can't allocate data for b64encode filter"); + return (ARCHIVE_FATAL); + } + archive_strcpy(&state->name, "-"); + state->mode = 0644; + + f->data = state; + f->name = "b64encode"; + f->code = ARCHIVE_FILTER_UU; + f->open = archive_filter_b64encode_open; + f->options = archive_filter_b64encode_options; + f->write = archive_filter_b64encode_write; + f->close = archive_filter_b64encode_close; + f->free = archive_filter_b64encode_free; + + return (ARCHIVE_OK); +} + +/* + * Set write options. + */ +static int +archive_filter_b64encode_options(struct archive_write_filter *f, const char *key, + const char *value) +{ + struct private_b64encode *state = (struct private_b64encode *)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_b64encode_open(struct archive_write_filter *f) +{ + struct private_b64encode *state = (struct private_b64encode *)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 b64encode buffer"); + return (ARCHIVE_FATAL); + } + + archive_string_sprintf(&state->encoded_buff, "begin-base64 %o %s\n", + state->mode, state->name.s); + + f->data = state; + return (0); +} + +static void +b64_encode(struct archive_string *as, const unsigned char *p, size_t len) +{ + int c; + + for (; len >= 3; p += 3, len -= 3) { + c = p[0] >> 2; + archive_strappend_char(as, base64[c]); + c = ((p[0] & 0x03) << 4) | ((p[1] & 0xf0) >> 4); + archive_strappend_char(as, base64[c]); + c = ((p[1] & 0x0f) << 2) | ((p[2] & 0xc0) >> 6); + archive_strappend_char(as, base64[c]); + c = p[2] & 0x3f; + archive_strappend_char(as, base64[c]); + } + if (len > 0) { + c = p[0] >> 2; + archive_strappend_char(as, base64[c]); + c = (p[0] & 0x03) << 4; + if (len == 1) { + archive_strappend_char(as, base64[c]); + archive_strappend_char(as, '='); + archive_strappend_char(as, '='); + } else { + c |= (p[1] & 0xf0) >> 4; + archive_strappend_char(as, base64[c]); + c = (p[1] & 0x0f) << 2; + archive_strappend_char(as, base64[c]); + archive_strappend_char(as, '='); + } + } + archive_strappend_char(as, '\n'); +} + +/* + * Write data to the encoded stream. + */ +static int +archive_filter_b64encode_write(struct archive_write_filter *f, const void *buff, + size_t length) +{ + struct private_b64encode *state = (struct private_b64encode *)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); + b64_encode(&state->encoded_buff, state->hold, LBYTES); + state->hold_len = 0; + } + + for (; length >= LBYTES; length -= LBYTES, p += LBYTES) + b64_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_b64encode_close(struct archive_write_filter *f) +{ + struct private_b64encode *state = (struct private_b64encode *)f->data; + int ret, ret2; + + /* Flush remaining bytes. */ + if (state->hold_len != 0) + b64_encode(&state->encoded_buff, state->hold, state->hold_len); + archive_string_sprintf(&state->encoded_buff, "====\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_b64encode_free(struct archive_write_filter *f) +{ + struct private_b64encode *state = (struct private_b64encode *)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 ed1464685..5cd6bb2cd 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_b64encode.c test_write_filter_uuencode.c test_write_format_7zip.c test_write_format_ar.c diff --git a/libarchive/test/test_write_filter_b64encode.c b/libarchive/test/test_write_filter_b64encode.c new file mode 100644 index 000000000..665087b97 --- /dev/null +++ b/libarchive/test/test_write_filter_b64encode.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 b64encode reading and writing. + */ + +DEFINE_TEST(test_write_filter_b64encode) +{ + 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_b64encode(a)); + assertEqualIntA(a, ARCHIVE_OK, + archive_write_set_bytes_per_block(a, 10)); + assertEqualInt(ARCHIVE_FILTER_UU, archive_filter_code(a, 0)); + assertEqualString("b64encode", 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_b64encode(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_b64encode(a)); + assertEqualInt(ARCHIVE_OK, archive_write_free(a)); + + assert((a = archive_write_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, archive_write_add_filter_b64encode(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_b64encode(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_b64encode(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 4b167a85e..2b572315f 100644 --- a/tar/bsdtar.c +++ b/tar/bsdtar.c @@ -267,6 +267,13 @@ main(int argc, char **argv) /* Explicit -b forces last block size. */ bsdtar->bytes_in_last_block = bsdtar->bytes_per_block; break; + case OPTION_B64ENCODE: + if (bsdtar->add_filter != '\0') + lafe_errc(1, 0, + "Can't specify both --uuencode and " + "--b64encode"); + bsdtar->add_filter = opt; + break; case 'C': /* GNU tar */ if (strlen(bsdtar->argument) == 0) lafe_errc(1, 0, @@ -573,6 +580,10 @@ main(int argc, char **argv) bsdtar->uname = bsdtar->argument; break; case OPTION_UUENCODE: + if (bsdtar->add_filter != '\0') + lafe_errc(1, 0, + "Can't specify both --uuencode and " + "--b64encode"); bsdtar->add_filter = opt; break; case 'v': /* SUSv2 */ diff --git a/tar/bsdtar.h b/tar/bsdtar.h index 91af45214..80e922ea2 100644 --- a/tar/bsdtar.h +++ b/tar/bsdtar.h @@ -112,7 +112,8 @@ struct bsdtar { /* Fake short equivalents for long options that otherwise lack them. */ enum { - OPTION_CHECK_LINKS = 1, + OPTION_B64ENCODE = 1, + OPTION_CHECK_LINKS, OPTION_CHROOT, OPTION_DISABLE_COPYFILE, OPTION_EXCLUDE, diff --git a/tar/cmdline.c b/tar/cmdline.c index 05d4e2616..684e28bfd 100644 --- a/tar/cmdline.c +++ b/tar/cmdline.c @@ -65,6 +65,7 @@ static const struct bsdtar_option { } tar_longopts[] = { { "absolute-paths", 0, 'P' }, { "append", 0, 'r' }, + { "b64encode", 0, OPTION_B64ENCODE }, { "block-size", 1, 'b' }, { "bunzip2", 0, 'j' }, { "bzip", 0, 'j' }, diff --git a/tar/test/CMakeLists.txt b/tar/test/CMakeLists.txt index 6ff9036c9..03ab7c19e 100644 --- a/tar/test/CMakeLists.txt +++ b/tar/test/CMakeLists.txt @@ -28,6 +28,7 @@ IF(ENABLE_TAR AND ENABLE_TEST) test_option_U_upper.c test_option_X_upper.c test_option_b.c + test_option_b64encode.c test_option_exclude.c test_option_gid_gname.c test_option_k.c diff --git a/tar/test/test_option_b64encode.c b/tar/test/test_option_b64encode.c new file mode 100644 index 000000000..1e7c57175 --- /dev/null +++ b/tar/test/test_option_b64encode.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_b64encode) +{ + 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 --b64encode 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-base64 644", 16); + + /* Archive it with uuencode only. */ + assertEqualInt(0, + systemf("%s -cf - --b64encode 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-base64 644", 16); +} diff --git a/tar/write.c b/tar/write.c index 89fdc8559..5c8cffb0c 100644 --- a/tar/write.c +++ b/tar/write.c @@ -168,7 +168,7 @@ tar_mode_c(struct bsdtar *bsdtar) if (bsdtar->compress_program) { archive_write_add_filter_program(a, bsdtar->compress_program); } else { - const char *name; + const char *name = "?"; switch (bsdtar->create_compression) { case 0: @@ -212,6 +212,10 @@ tar_mode_c(struct bsdtar *bsdtar) case 0: r = ARCHIVE_OK; break; + case OPTION_B64ENCODE: + r = archive_write_add_filter_b64encode(a); + name = "b64encode"; + break; case OPTION_UUENCODE: r = archive_write_add_filter_uuencode(a); name = "uuencode"; -- 2.47.2