From: Michihiro NAKAJIMA Date: Thu, 4 Feb 2010 13:22:04 +0000 (-0500) Subject: Add support for afio large ASCII header. X-Git-Tag: v3.0.0a~1285 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=19cc1b68626481da55796728787c6a51e3ec07ee;p=thirdparty%2Flibarchive.git Add support for afio large ASCII header. SVN-Revision: 1869 --- diff --git a/Makefile.am b/Makefile.am index a2268f75b..3ad407d08 100644 --- a/Makefile.am +++ b/Makefile.am @@ -252,6 +252,7 @@ libarchive_test_SOURCES= \ libarchive/test/test_read_extract.c \ libarchive/test/test_read_file_nonexistent.c \ libarchive/test/test_read_format_ar.c \ + libarchive/test/test_read_format_cpio_afio.c \ libarchive/test/test_read_format_cpio_bin.c \ libarchive/test/test_read_format_cpio_bin_Z.c \ libarchive/test/test_read_format_cpio_bin_be.c \ diff --git a/libarchive/archive.h b/libarchive/archive.h index 000abaeef..e534068b2 100644 --- a/libarchive/archive.h +++ b/libarchive/archive.h @@ -269,6 +269,7 @@ typedef int archive_close_callback(struct archive *, void *_client_data); #define ARCHIVE_FORMAT_CPIO_BIN_BE (ARCHIVE_FORMAT_CPIO | 3) #define ARCHIVE_FORMAT_CPIO_SVR4_NOCRC (ARCHIVE_FORMAT_CPIO | 4) #define ARCHIVE_FORMAT_CPIO_SVR4_CRC (ARCHIVE_FORMAT_CPIO | 5) +#define ARCHIVE_FORMAT_CPIO_AFIO_LARGE (ARCHIVE_FORMAT_CPIO | 6) #define ARCHIVE_FORMAT_SHAR 0x20000 #define ARCHIVE_FORMAT_SHAR_BASE (ARCHIVE_FORMAT_SHAR | 1) #define ARCHIVE_FORMAT_SHAR_DUMP (ARCHIVE_FORMAT_SHAR | 2) diff --git a/libarchive/archive_read_support_format_cpio.c b/libarchive/archive_read_support_format_cpio.c index 1def0006a..d4237d819 100644 --- a/libarchive/archive_read_support_format_cpio.c +++ b/libarchive/archive_read_support_format_cpio.c @@ -1,5 +1,6 @@ /*- * Copyright (c) 2003-2007 Tim Kientzle + * Copyright (c) 2010 Michihiro NAKAJIMA * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -87,6 +88,34 @@ struct cpio_newc_header { char c_crc[8]; }; +/* + * An afio large ASCII header, which they named itself. + * afio utility uses this header, if a file size is larger than 2G bytes + * or inode/uid/gid is bigger than 65535(0xFFFF) or mtime is bigger than + * 0x7fffffff, which we cannot record to odc header because of its limit. + * If not, uses odc header. + */ +struct cpio_afiol_header { + char c_magic[6]; + char c_dev[8]; /* hex */ + char c_ino[16]; /* hex */ + char c_ino_m; /* 'm' */ + char c_mode[6]; /* oct */ + char c_uid[8]; /* hex */ + char c_gid[8]; /* hex */ + char c_nlink[8]; /* hex */ + char c_rdev[8]; /* hex */ + char c_mtime[16]; /* hex */ + char c_mtime_n; /* 'n' */ + char c_namesize[4]; /* hex */ + char c_flag[4]; /* hex */ + char c_xsize[4]; /* hex */ + char c_xsize_s; /* 's' */ + char c_filesize[16]; /* hex */ + char c_filesize_c; /* ':' */ +}; + + struct links_entry { struct links_entry *next; struct links_entry *previous; @@ -128,6 +157,8 @@ static int header_newc(struct archive_read *, struct cpio *, struct archive_entry *, size_t *, size_t *); static int header_odc(struct archive_read *, struct cpio *, struct archive_entry *, size_t *, size_t *); +static int header_afiol(struct archive_read *, struct cpio *, + struct archive_entry *, size_t *, size_t *); static int is_octal(const char *, size_t); static int is_hex(const char *, size_t); static int le4(const unsigned char *); @@ -187,6 +218,14 @@ archive_read_format_cpio_bid(struct archive_read *a) * XXX TODO: More verification; Could check that only octal * digits appear in appropriate header locations. XXX */ + } else if (memcmp(p, "070727", 6) == 0) { + /* afio large ASCII cpio archive */ + cpio->read_header = header_odc; + bid += 48; + /* + * XXX TODO: More verification; Could check that almost hex + * digits appear in appropriate header locations. XXX + */ } else if (memcmp(p, "070701", 6) == 0) { /* ASCII cpio archive (SVR4 without CRC) */ cpio->read_header = header_newc; @@ -466,6 +505,27 @@ is_octal(const char *p, size_t len) return (1); } +static int +is_afio_large(const char *p, size_t len) +{ + const struct cpio_afiol_header *h = (const struct cpio_afiol_header *)p; + + if (len < sizeof(*h)) + return (0); + if (h->c_ino_m != 'm' || h->c_mtime_n != 'n' || + h->c_xsize_s != 's' || h->c_filesize_c != ':') + return (0); + if (!is_hex(h->c_dev, &(h->c_ino_m) - h->c_dev)) + return (0); + if (!is_hex(h->c_mode, &(h->c_mtime_n) - h->c_mode)) + return (0); + if (!is_hex(h->c_namesize, &(h->c_xsize_s) - h->c_namesize)) + return (0); + if (!is_hex(h->c_filesize, &(h->c_filesize_c) - h->c_filesize)) + return (0); + return (1); +} + static int find_odc_header(struct archive_read *a) { @@ -485,6 +545,10 @@ find_odc_header(struct archive_read *a) if (memcmp("070707", p, 6) == 0 && is_octal(p, sizeof(struct cpio_odc_header))) return (ARCHIVE_OK); + if (memcmp("070727", p, 6) == 0 && is_afio_large(p, bytes)) { + a->archive.archive_format = ARCHIVE_FORMAT_CPIO_AFIO_LARGE; + return (ARCHIVE_OK); + } /* * Scan ahead until we find something that looks @@ -493,11 +557,16 @@ find_odc_header(struct archive_read *a) while (p + sizeof(struct cpio_odc_header) <= q) { switch (p[5]) { case '7': - if (memcmp("070707", p, 6) == 0 - && is_octal(p, sizeof(struct cpio_odc_header))) { + if ((memcmp("070707", p, 6) == 0 + && is_octal(p, sizeof(struct cpio_odc_header))) + || (memcmp("070727", p, 6) == 0 + && is_afio_large(p, q - p))) { skip = p - (const char *)h; __archive_read_consume(a, skip); skipped += skip; + if (p[4] == '2') + a->archive.archive_format = + ARCHIVE_FORMAT_CPIO_AFIO_LARGE; if (skipped > 0) { archive_set_error(&a->archive, 0, @@ -540,6 +609,14 @@ header_odc(struct archive_read *a, struct cpio *cpio, if (r < ARCHIVE_WARN) return (r); + if (a->archive.archive_format == ARCHIVE_FORMAT_CPIO_AFIO_LARGE) { + int r2 = (header_afiol(a, cpio, entry, namelength, name_pad)); + if (r2 == ARCHIVE_OK) + return (r); + else + return (r2); + } + /* Read fixed-size portion of header. */ h = __archive_read_ahead(a, sizeof(struct cpio_odc_header), NULL); if (h == NULL) @@ -572,6 +649,51 @@ header_odc(struct archive_read *a, struct cpio *cpio, return (r); } +/* + * NOTE: if a filename suffix is ".z", it is the file gziped by afio. + * it would be nice that we can show uncompressed file size and we can + * uncompressed file contents automatically, unfortunately we have nothing + * to get a uncompressed file size while reading each header. it means + * we also cannot uncompressed file contens under the our framework. + */ +static int +header_afiol(struct archive_read *a, struct cpio *cpio, + struct archive_entry *entry, size_t *namelength, size_t *name_pad) +{ + const void *h; + const struct cpio_afiol_header *header; + + a->archive.archive_format = ARCHIVE_FORMAT_CPIO_AFIO_LARGE; + a->archive.archive_format_name = "afio large ASCII"; + + /* Read fixed-size portion of header. */ + h = __archive_read_ahead(a, sizeof(struct cpio_afiol_header), NULL); + if (h == NULL) + return (ARCHIVE_FATAL); + __archive_read_consume(a, sizeof(struct cpio_afiol_header)); + + /* Parse out octal fields. */ + header = (const struct cpio_afiol_header *)h; + + archive_entry_set_dev(entry, atol16(header->c_dev, sizeof(header->c_dev))); + archive_entry_set_ino(entry, atol16(header->c_ino, sizeof(header->c_ino))); + archive_entry_set_mode(entry, atol8(header->c_mode, sizeof(header->c_mode))); + archive_entry_set_uid(entry, atol16(header->c_uid, sizeof(header->c_uid))); + archive_entry_set_gid(entry, atol16(header->c_gid, sizeof(header->c_gid))); + archive_entry_set_nlink(entry, atol16(header->c_nlink, sizeof(header->c_nlink))); + archive_entry_set_rdev(entry, atol16(header->c_rdev, sizeof(header->c_rdev))); + archive_entry_set_mtime(entry, atol16(header->c_mtime, sizeof(header->c_mtime)), 0); + *namelength = atol16(header->c_namesize, sizeof(header->c_namesize)); + *name_pad = 0; /* No padding of filename. */ + + cpio->entry_bytes_remaining = + atol16(header->c_filesize, sizeof(header->c_filesize)); + archive_entry_set_size(entry, cpio->entry_bytes_remaining); + cpio->entry_padding = 0; + return (ARCHIVE_OK); +} + + static int header_bin_le(struct archive_read *a, struct cpio *cpio, struct archive_entry *entry, size_t *namelength, size_t *name_pad) diff --git a/libarchive/test/CMakeLists.txt b/libarchive/test/CMakeLists.txt index f4d2ebf57..620b672fe 100644 --- a/libarchive/test/CMakeLists.txt +++ b/libarchive/test/CMakeLists.txt @@ -44,6 +44,7 @@ IF(ENABLE_TEST) test_read_extract.c test_read_file_nonexistent.c test_read_format_ar.c + test_read_format_cpio_afio.c test_read_format_cpio_bin.c test_read_format_cpio_bin_Z.c test_read_format_cpio_bin_be.c diff --git a/libarchive/test/test_read_format_cpio_afio.c b/libarchive/test/test_read_format_cpio_afio.c new file mode 100644 index 000000000..71748e01b --- /dev/null +++ b/libarchive/test/test_read_format_cpio_afio.c @@ -0,0 +1,105 @@ +/*- + * Copyright (c) 2003-2007 Tim Kientzle + * Copyright (c) 2010 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$"); + +/* +ecute the following to rebuild the data for this program: + tail -n +33 test_read_format_cpio_afio.c | /bin/sh + +# How to make a sample data. +echo "0123456789abcdef" > file1 +echo "0123456789abcdef" > file2 +# make afio use a large ASCII header +sudo chown 65536 file2 +find . -name "file[12]" | afio -o sample +od -c sample | sed -E -e "s/^0[0-9]+//;s/^ //;s/( +)([^ ]{1,2})/'\2',/g;s/'\\0'/0/g;/^[*]/d" > test_read_format_cpio_afio.sample.txt +rm -f file1 file2 sample +exit1 +*/ + +static unsigned char archive[] = { +'0','7','0','7','0','7','0','0','0','1','4','3','1','2','5','3', +'2','1','1','0','0','6','4','4','0','0','1','7','5','1','0','0', +'1','7','5','1','0','0','0','0','0','1','0','0','0','0','0','0', +'1','1','3','3','2','2','4','5','0','2','0','0','0','0','0','0', +'6','0','0','0','0','0','0','0','0','0','2','1','f','i','l','e', +'1',0,'0','1','2','3','4','5','6','7','8','9','a','b','c','d', +'e','f','\n','0','7','0','7','2','7','0','0','0','0','0','0','6', +'3','0','0','0','0','0','0','0','0','0','0','0','D','A','A','E', +'6','m','1','0','0','6','4','4','0','0','0','1','0','0','0','0', +'0','0','0','0','0','3','E','9','0','0','0','0','0','0','0','1', +'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0', +'4','B','6','9','4','A','1','0','n','0','0','0','6','0','0','0', +'0','0','0','0','0','s','0','0','0','0','0','0','0','0','0','0', +'0','0','0','0','1','1',':','f','i','l','e','2',0,'0','1','2', +'3','4','5','6','7','8','9','a','b','c','d','e','f','\n','0','7', +'0','7','0','7','0','0','0','0','0','0','0','0','0','0','0','0', +'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0', +'0','0','0','0','0','0','0','1','0','0','0','0','0','0','0','0', +'0','0','0','0','0','0','0','0','0','0','0','0','0','1','3','0', +'0','0','0','0','0','1','1','2','7','3','T','R','A','I','L','E', +'R','!','!','!',0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +}; + +DEFINE_TEST(test_read_format_cpio_afio) +{ + unsigned char *p; + size_t size; + struct archive_entry *ae; + struct archive *a; + + /* The default block size of afio is 5120. we simulate it */ + size = (sizeof(archive) + 5120 -1 / 5120) * 5120; + if (!assert((p = malloc(size)) != NULL)) + return; + memset(p, 0, size); + memcpy(p, archive, sizeof(archive)); + assert((a = archive_read_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_compression_all(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_open_memory(a, p, size)); + /* + * First entry is odc format. + */ + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualInt(17, archive_entry_size(ae)); + assertA(archive_compression(a) == ARCHIVE_COMPRESSION_NONE); + assertA(archive_format(a) == ARCHIVE_FORMAT_CPIO_POSIX); + /* + * Second entry is afio large ASCII format. + */ + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualInt(17, archive_entry_size(ae)); + assertEqualInt(65536, archive_entry_uid(ae)); + assertA(archive_compression(a) == ARCHIVE_COMPRESSION_NONE); + assertA(archive_format(a) == ARCHIVE_FORMAT_CPIO_AFIO_LARGE); + assertEqualInt(ARCHIVE_OK, archive_read_close(a)); + assertEqualInt(ARCHIVE_OK, archive_read_finish(a)); + + free(p); +}