From: Martin Matuska Date: Wed, 17 Apr 2019 16:52:27 +0000 (+0200) Subject: RAR5 reader: add support for symlinks, hardlinks, owner and group data X-Git-Tag: v3.4.0~71 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=64e68f2034e6c5c6fd20d6b274b67fbdb88c3804;p=thirdparty%2Flibarchive.git RAR5 reader: add support for symlinks, hardlinks, owner and group data Add missing test archives to Makefile.am --- diff --git a/Makefile.am b/Makefile.am index 639936a64..b7a2a6bca 100644 --- a/Makefile.am +++ b/Makefile.am @@ -825,6 +825,30 @@ libarchive_test_EXTRA_DIST=\ libarchive/test/test_read_format_rar_subblock.rar.uu \ libarchive/test/test_read_format_rar_unicode.rar.uu \ libarchive/test/test_read_format_rar_windows.rar.uu \ + libarchive/test/test_read_format_rar5_arm.rar.uu \ + libarchive/test/test_read_format_rar5_blake2.rar.uu \ + libarchive/test/test_read_format_rar5_compressed.rar.uu \ + libarchive/test/test_read_format_rar5_hardlink.rar.uu \ + libarchive/test/test_read_format_rar5_multiarchive.part01.rar.uu \ + libarchive/test/test_read_format_rar5_multiarchive.part02.rar.uu \ + libarchive/test/test_read_format_rar5_multiarchive.part03.rar.uu \ + libarchive/test/test_read_format_rar5_multiarchive.part04.rar.uu \ + libarchive/test/test_read_format_rar5_multiarchive.part05.rar.uu \ + libarchive/test/test_read_format_rar5_multiarchive.part06.rar.uu \ + libarchive/test/test_read_format_rar5_multiarchive.part07.rar.uu \ + libarchive/test/test_read_format_rar5_multiarchive.part08.rar.uu \ + libarchive/test/test_read_format_rar5_multiarchive_solid.part01.rar.uu \ + libarchive/test/test_read_format_rar5_multiarchive_solid.part02.rar.uu \ + libarchive/test/test_read_format_rar5_multiarchive_solid.part03.rar.uu \ + libarchive/test/test_read_format_rar5_multiarchive_solid.part04.rar.uu \ + libarchive/test/test_read_format_rar5_multiple_files.rar.uu \ + libarchive/test/test_read_format_rar5_multiple_files_solid.rar.uu \ + libarchive/test/test_read_format_rar5_owner.rar.uu \ + libarchive/test/test_read_format_rar5_solid.rar.uu \ + libarchive/test/test_read_format_rar5_stored.rar.uu \ + libarchive/test/test_read_format_rar5_stored_manyfiles.rar.uu \ + libarchive/test/test_read_format_rar5_symlink.rar.uu \ + libarchive/test/test_read_format_rar5_win32.rar.uu \ libarchive/test/test_read_format_raw.bufr.uu \ libarchive/test/test_read_format_raw.data.Z.uu \ libarchive/test/test_read_format_raw.data.uu \ @@ -837,6 +861,9 @@ libarchive_test_EXTRA_DIST=\ libarchive/test/test_read_format_ustar_filename_koi8r.tar.Z.uu \ libarchive/test/test_read_format_warc.warc.uu \ libarchive/test/test_read_format_zip.zip.uu \ + libarchive/test/test_read_format_zip_bz2_hang.zip.uu \ + libarchive/test/test_read_format_zip_bzip2.zipx.uu \ + libarchive/test/test_read_format_zip_bzip2_multi.zipx.uu \ libarchive/test/test_read_format_zip_comment_stored_1.zip.uu \ libarchive/test/test_read_format_zip_comment_stored_2.zip.uu \ libarchive/test/test_read_format_zip_encryption_data.zip.uu \ @@ -850,6 +877,7 @@ libarchive_test_EXTRA_DIST=\ libarchive/test/test_read_format_zip_filename_utf8_ru2.zip.uu \ libarchive/test/test_read_format_zip_high_compression.zip.uu \ libarchive/test/test_read_format_zip_length_at_end.zip.uu \ + libarchive/test/test_read_format_zip_lzma.zipx.uu \ libarchive/test/test_read_format_zip_jar.jar.uu \ libarchive/test/test_read_format_zip_mac_metadata.zip.uu \ libarchive/test/test_read_format_zip_malformed1.zip.uu \ @@ -859,6 +887,10 @@ libarchive_test_EXTRA_DIST=\ libarchive/test/test_read_format_zip_padded1.zip.uu \ libarchive/test/test_read_format_zip_padded2.zip.uu \ libarchive/test/test_read_format_zip_padded3.zip.uu \ + libarchive/test/test_read_format_zip_ppmd8.zipx.uu \ + libarchive/test/test_read_format_zip_ppmd8_crash_1.zipx.uu \ + libarchive/test/test_read_format_zip_ppmd8_crash_2.zipx.uu \ + libarchive/test/test_read_format_zip_ppmd8_multi.zipx.uu \ libarchive/test/test_read_format_zip_sfx.uu \ libarchive/test/test_read_format_zip_symlink.zip.uu \ libarchive/test/test_read_format_zip_traditional_encryption_data.zip.uu \ @@ -868,6 +900,7 @@ libarchive_test_EXTRA_DIST=\ libarchive/test/test_read_format_zip_winzip_aes256_large.zip.uu \ libarchive/test/test_read_format_zip_winzip_aes256_stored.zip.uu \ libarchive/test/test_read_format_zip_with_invalid_traditional_eocd.zip.uu \ + libarchive/test/test_read_format_zip_xz_multi.zipx.uu \ libarchive/test/test_read_format_zip_zip64a.zip.uu \ libarchive/test/test_read_format_zip_zip64b.zip.uu \ libarchive/test/test_read_large_splitted_rar_aa.uu \ diff --git a/libarchive/archive_read_support_format_rar5.c b/libarchive/archive_read_support_format_rar5.c index 159c3b61f..70796e358 100644 --- a/libarchive/archive_read_support_format_rar5.c +++ b/libarchive/archive_read_support_format_rar5.c @@ -103,8 +103,39 @@ struct file_header { uint8_t blake2sp[32]; blake2sp_state b2state; char has_blake2; + + /* Optional redir fields */ + uint64_t redir_type; + uint64_t redir_flags; +}; + +enum EXTRA { + EX_CRYPT = 0x01, + EX_HASH = 0x02, + EX_HTIME = 0x03, + EX_VERSION = 0x04, + EX_REDIR = 0x05, + EX_UOWNER = 0x06, + EX_SUBDATA = 0x07 }; +#define REDIR_SYMLINK_IS_DIR 1 + +enum REDIR_TYPE { + REDIR_TYPE_NONE = 0, + REDIR_TYPE_UNIXSYMLINK = 1, + REDIR_TYPE_WINSYMLINK = 2, + REDIR_TYPE_JUNCTION = 3, + REDIR_TYPE_HARDLINK = 4, + REDIR_TYPE_FILECOPY = 5, +}; + +#define OWNER_USER_NAME 0x01 +#define OWNER_GROUP_NAME 0x02 +#define OWNER_USER_UID 0x04 +#define OWNER_GROUP_GID 0x08 +#define OWNER_MAXNAMELEN 256 + enum FILTER_TYPE { FILTER_DELTA = 0, /* Generic pattern. */ FILTER_E8 = 1, /* Intel x86 code. */ @@ -615,7 +646,7 @@ static int run_filter(struct archive_read* a, struct filter_info* flt) { default: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Unsupported filter type: 0x%02x", flt->type); + "Unsupported filter type: 0x%x", flt->type); return ARCHIVE_FATAL; } @@ -797,6 +828,9 @@ static void reset_file_context(struct rar5* rar) { rar->cstate.last_write_ptr = 0; rar->cstate.last_unstore_ptr = 0; + rar->file.redir_type = REDIR_TYPE_NONE; + rar->file.redir_flags = 0; + free_filters(rar); } @@ -1135,7 +1169,7 @@ static int parse_file_extra_hash(struct archive_read* a, struct rar5* rar, *extra_data_size -= hash_size; } else { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Unsupported hash type (0x%02x)", (int) hash_type); + "Unsupported hash type (0x%x)", (int) hash_type); return ARCHIVE_FATAL; } @@ -1221,6 +1255,152 @@ static int parse_file_extra_htime(struct archive_read* a, return ARCHIVE_OK; } +static int parse_file_extra_redir(struct archive_read* a, + struct archive_entry* e, struct rar5* rar, + ssize_t* extra_data_size) +{ + size_t value_len = 0; + char target_utf8_buf[2048 * 4]; + const uint8_t* p; + + if(!read_var_sized(a, &rar->file.redir_type, &value_len)) + return ARCHIVE_EOF; + if(ARCHIVE_OK != consume(a, value_len)) + return ARCHIVE_EOF; + *extra_data_size -= value_len; + + if(!read_var_sized(a, &rar->file.redir_flags, &value_len)) + return ARCHIVE_EOF; + if(ARCHIVE_OK != consume(a, value_len)) + return ARCHIVE_EOF; + *extra_data_size -= value_len; + + if(!read_var_sized(a, &value_len, NULL)) + return ARCHIVE_EOF; + *extra_data_size -= value_len + 1; + if(!read_ahead(a, value_len, &p)) + return ARCHIVE_EOF; + + if(value_len > 2047) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Link target is too long"); + return ARCHIVE_FATAL; + } + if(value_len == 0) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "No link target specified"); + return ARCHIVE_FATAL; + } + memcpy(target_utf8_buf, p, value_len); + target_utf8_buf[value_len] = 0; + + if(ARCHIVE_OK != consume(a, value_len)) + return ARCHIVE_EOF; + + switch(rar->file.redir_type) { + case REDIR_TYPE_UNIXSYMLINK: + case REDIR_TYPE_WINSYMLINK: + archive_entry_set_filetype(e, AE_IFLNK); + archive_entry_update_symlink_utf8(e, target_utf8_buf); + if (rar->file.redir_flags & REDIR_SYMLINK_IS_DIR) { + archive_entry_set_symlink_type(e, + AE_SYMLINK_TYPE_DIRECTORY); + } else { + archive_entry_set_symlink_type(e, + AE_SYMLINK_TYPE_FILE); + } + break; + + case REDIR_TYPE_HARDLINK: + archive_entry_set_filetype(e, AE_IFREG); + archive_entry_update_hardlink_utf8(e, target_utf8_buf); + break; + + default: + /* Unknown redir type */ + if(ARCHIVE_OK != consume(a, value_len)) + return ARCHIVE_EOF; + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Unsupported redir type: %ld", + rar->file.redir_type); + return ARCHIVE_FATAL; + break; + } + return ARCHIVE_OK; +} + +static int parse_file_extra_owner(struct archive_read* a, + struct archive_entry* e, ssize_t* extra_data_size) +{ + uint64_t flags = 0; + size_t value_len = 0; + size_t name_len = 0; + size_t id = 0; + char namebuf[OWNER_MAXNAMELEN]; + const uint8_t* p; + + if(!read_var_sized(a, &flags, &value_len)) + return ARCHIVE_EOF; + if(ARCHIVE_OK != consume(a, value_len)) + return ARCHIVE_EOF; + *extra_data_size -= value_len; + + if ((flags & OWNER_USER_NAME) != 0) { + if(!read_var_sized(a, &value_len, NULL)) + return ARCHIVE_EOF; + *extra_data_size -= value_len + 1; + if(!read_ahead(a, value_len, &p)) + return ARCHIVE_EOF; + if (value_len > OWNER_MAXNAMELEN) + name_len = OWNER_MAXNAMELEN; + else + name_len = value_len; + memcpy(namebuf, p, name_len); + namebuf[name_len] = 0; + if(ARCHIVE_OK != consume(a, value_len)) + return ARCHIVE_EOF; + + archive_entry_set_uname(e, namebuf); + } + if ((flags & OWNER_GROUP_NAME) != 0) { + if(!read_var_sized(a, &value_len, NULL)) + return ARCHIVE_EOF; + *extra_data_size -= value_len + 1; + if(!read_ahead(a, value_len, &p)) + return ARCHIVE_EOF; + if (value_len > OWNER_MAXNAMELEN) + name_len = OWNER_MAXNAMELEN; + else + name_len = value_len; + memcpy(namebuf, p, name_len); + namebuf[name_len] = 0; + if(ARCHIVE_OK != consume(a, value_len)) + return ARCHIVE_EOF; + + archive_entry_set_gname(e, namebuf); + } + if ((flags & OWNER_USER_UID) != 0) { + if(!read_var_sized(a, &id, &value_len)) + return ARCHIVE_EOF; + if(ARCHIVE_OK != consume(a, value_len)) + return ARCHIVE_EOF; + *extra_data_size -= value_len; + + archive_entry_set_uid(e, id); + } + if ((flags & OWNER_GROUP_GID) != 0) { + if(!read_var_sized(a, &id, &value_len)) + return ARCHIVE_EOF; + if(ARCHIVE_OK != consume(a, value_len)) + return ARCHIVE_EOF; + *extra_data_size -= value_len; + + archive_entry_set_gid(e, id); + } + return ARCHIVE_OK; +} + static int process_head_file_extra(struct archive_read* a, struct archive_entry* e, struct rar5* rar, ssize_t extra_data_size) @@ -1230,11 +1410,6 @@ static int process_head_file_extra(struct archive_read* a, int ret = ARCHIVE_FATAL; size_t var_size; - enum EXTRA { - CRYPT = 0x01, HASH = 0x02, HTIME = 0x03, VERSION_ = 0x04, - REDIR = 0x05, UOWNER = 0x06, SUBDATA = 0x07 - }; - while(extra_data_size > 0) { if(!read_var_sized(a, &extra_field_size, &var_size)) return ARCHIVE_EOF; @@ -1253,25 +1428,27 @@ static int process_head_file_extra(struct archive_read* a, } switch(extra_field_id) { - case HASH: + case EX_HASH: ret = parse_file_extra_hash(a, rar, &extra_data_size); break; - case HTIME: + case EX_HTIME: ret = parse_file_extra_htime(a, e, rar, &extra_data_size); break; - case CRYPT: - /* fallthrough */ - case VERSION_: - /* fallthrough */ - case REDIR: + case EX_REDIR: + ret = parse_file_extra_redir(a, e, rar, &extra_data_size); + break; + case EX_UOWNER: + ret = parse_file_extra_owner(a, e, &extra_data_size); + break; + case EX_CRYPT: /* fallthrough */ - case UOWNER: + case EX_VERSION: /* fallthrough */ - case SUBDATA: + case EX_SUBDATA: /* fallthrough */ default: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Unknown extra field in file/service block: 0x%02x", + "Unknown extra field in file/service block: 0x%x", (int) extra_field_id); return ARCHIVE_FATAL; } @@ -1406,7 +1583,7 @@ static int process_head_file(struct archive_read* a, struct rar5* rar, } else { /* Unknown host OS */ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Unsupported Host OS: 0x%02x", (int) host_os); + "Unsupported Host OS: 0x%x", (int) host_os); return ARCHIVE_FATAL; } @@ -1453,7 +1630,8 @@ static int process_head_file(struct archive_read* a, struct rar5* rar, if((file_flags & UNKNOWN_UNPACKED_SIZE) == 0) { rar->file.unpacked_size = (ssize_t) unpacked_size; - archive_entry_set_size(entry, unpacked_size); + if(rar->file.redir_type == REDIR_TYPE_NONE) + archive_entry_set_size(entry, unpacked_size); } if(file_flags & UTIME) { @@ -1596,7 +1774,7 @@ static int process_head_main(struct archive_read* a, struct rar5* rar, break; default: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Unsupported extra type (0x%02x)", (int) extra_field_id); + "Unsupported extra type (0x%x)", (int) extra_field_id); return ARCHIVE_FATAL; } @@ -2217,7 +2395,7 @@ static int parse_block_header(struct archive_read* a, const uint8_t* p, if(calculated_cksum != hdr->block_cksum) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Block checksum error: got 0x%02x, expected 0x%02x", + "Block checksum error: got 0x%x, expected 0x%x", hdr->block_cksum, calculated_cksum); return ARCHIVE_FATAL; @@ -2570,7 +2748,7 @@ static int do_uncompress_block(struct archive_read* a, const uint8_t* p) { /* The program counter shouldn't reach here. */ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Unsupported block code: 0x%02x", num); + "Unsupported block code: 0x%x", num); return ARCHIVE_FATAL; } @@ -3195,7 +3373,7 @@ static int do_unpack(struct archive_read* a, struct rar5* rar, return uncompress_file(a); default: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Compression method not supported: 0x%08x", + "Compression method not supported: 0x%x", rar->cstate.method); return ARCHIVE_FATAL; diff --git a/libarchive/test/test_read_format_rar5.c b/libarchive/test/test_read_format_rar5.c index 0be9c45a5..e87e278f0 100644 --- a/libarchive/test/test_read_format_rar5.c +++ b/libarchive/test/test_read_format_rar5.c @@ -768,3 +768,102 @@ DEFINE_TEST(test_read_format_rar5_block_by_block) assertEqualInt(computed_crc, 0x7CCA70CD); EPILOGUE(); } + +DEFINE_TEST(test_read_format_rar5_owner) +{ + const int DATA_SIZE = 5; + uint8_t buff[5]; + + PROLOGUE("test_read_format_rar5_owner.rar"); + + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("root.txt", archive_entry_pathname(ae)); + assertEqualString("root", archive_entry_uname(ae)); + assertEqualString("wheel", archive_entry_gname(ae)); + assertA((int) archive_entry_mtime(ae) > 0); + assertEqualInt(DATA_SIZE, archive_entry_size(ae)); + assertA(DATA_SIZE == archive_read_data(a, buff, DATA_SIZE)); + + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("nobody.txt", archive_entry_pathname(ae)); + assertEqualString("nobody", archive_entry_uname(ae)); + assertEqualString("nogroup", archive_entry_gname(ae)); + assertA((int) archive_entry_mtime(ae) > 0); + assertEqualInt(DATA_SIZE, archive_entry_size(ae)); + assertA(DATA_SIZE == archive_read_data(a, buff, DATA_SIZE)); + + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("numeric.txt", archive_entry_pathname(ae)); + assertEqualInt(9999, archive_entry_uid(ae)); + assertEqualInt(8888, archive_entry_gid(ae)); + assertA((int) archive_entry_mtime(ae) > 0); + assertEqualInt(DATA_SIZE, archive_entry_size(ae)); + assertA(DATA_SIZE == archive_read_data(a, buff, DATA_SIZE)); + + assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae)); + + EPILOGUE(); +} + +DEFINE_TEST(test_read_format_rar5_symlink) +{ + const int DATA_SIZE = 5; + uint8_t buff[5]; + + PROLOGUE("test_read_format_rar5_symlink.rar"); + + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("file.txt", archive_entry_pathname(ae)); + assertEqualInt(AE_IFREG, archive_entry_filetype(ae)); + assertA((int) archive_entry_mtime(ae) > 0); + assertEqualInt(DATA_SIZE, archive_entry_size(ae)); + assertA(DATA_SIZE == archive_read_data(a, buff, DATA_SIZE)); + + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("symlink.txt", archive_entry_pathname(ae)); + assertEqualInt(AE_IFLNK, archive_entry_filetype(ae)); + assertEqualString("file.txt", archive_entry_symlink(ae)); + assertEqualInt(AE_SYMLINK_TYPE_FILE, archive_entry_symlink_type(ae)); + assertA(0 == archive_read_data(a, NULL, archive_entry_size(ae))); + + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("dirlink", archive_entry_pathname(ae)); + assertEqualInt(AE_IFLNK, archive_entry_filetype(ae)); + assertEqualString("dir", archive_entry_symlink(ae)); + assertEqualInt(AE_SYMLINK_TYPE_DIRECTORY, archive_entry_symlink_type(ae)); + assertA(0 == archive_read_data(a, NULL, archive_entry_size(ae))); + + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("dir", archive_entry_pathname(ae)); + assertEqualInt(AE_IFDIR, archive_entry_filetype(ae)); + assertA(0 == archive_read_data(a, NULL, archive_entry_size(ae))); + + assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae)); + + EPILOGUE(); +} + +DEFINE_TEST(test_read_format_rar5_hardlink) +{ + const int DATA_SIZE = 5; + uint8_t buff[5]; + + PROLOGUE("test_read_format_rar5_hardlink.rar"); + + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("file.txt", archive_entry_pathname(ae)); + assertEqualInt(AE_IFREG, archive_entry_filetype(ae)); + assertA((int) archive_entry_mtime(ae) > 0); + assertEqualInt(DATA_SIZE, archive_entry_size(ae)); + assertA(DATA_SIZE == archive_read_data(a, buff, DATA_SIZE)); + + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("hardlink.txt", archive_entry_pathname(ae)); + assertEqualInt(AE_IFREG, archive_entry_filetype(ae)); + assertEqualString("file.txt", archive_entry_hardlink(ae)); + assertA(0 == archive_read_data(a, NULL, archive_entry_size(ae))); + + assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae)); + + EPILOGUE(); +} diff --git a/libarchive/test/test_read_format_rar5_hardlink.rar.uu b/libarchive/test/test_read_format_rar5_hardlink.rar.uu new file mode 100644 index 000000000..0ec085d47 --- /dev/null +++ b/libarchive/test/test_read_format_rar5_hardlink.rar.uu @@ -0,0 +1,6 @@ +begin 644 test_read_format_rar5_hardlink.rar +M4F%R(1H'`0`SDK7E"@$%!@`%`0&`@`!KI,1X'@("A0`&A0"D@P(XC;=<(1>3 +M?8```0AF:6QE+G1X=#$R,S0*[8@"T2X"`PT`!@6D@P(XC;=<`````(```0QH +@87)D;&EN:RYT>'0,!00`"&9I;&4N='AT'7=640,%!``` +` +end diff --git a/libarchive/test/test_read_format_rar5_owner.rar.uu b/libarchive/test/test_read_format_rar5_owner.rar.uu new file mode 100644 index 000000000..285bdb91c --- /dev/null +++ b/libarchive/test/test_read_format_rar5_owner.rar.uu @@ -0,0 +1,8 @@ +begin 644 test_read_format_rar5_owner.rar +M4F%R(1H'`0`SDK7E"@$%!@`%`0&`@`!W:E-^+0(##H4`!H4`I(,"_8VW7"$7 +MDWV```$('0-!@,$2YT>'01!@,&;F]B;V1Y!VYO9W)O=7`U +M-CB#;(H```0MN=6UE3 +M?8!``0AF:6QE+G1X=#$R,S0*8QI3.2T"`PT`!@CMPP)7C;=<`````(!``0MS +M>6UL:6YK+G1X=`P%`0`(9FEL92YT>'2.NOQU)`(#"``&`^W#`DF6MUP````` +M@$`!!V1I