From: Tim Kientzle Date: Sat, 26 Apr 2025 23:10:45 +0000 (-0700) Subject: Improve support for AFIO 64-bit inode values (#2589) X-Git-Tag: v3.8.0~37 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=966010ce79a491b5d4e6aed1e74c17163137bd18;p=thirdparty%2Flibarchive.git Improve support for AFIO 64-bit inode values (#2589) PR #2258 hardened AFIO parsing by converting all inode values >= 2^63 to zero values. This turns out to be problematic for filesystems that use very large inode values; it results in all such files being viewed as hardlinks to each other. PR #2587 partially addressed this by instead considering inode values >= 2^63 as invalid and just ignoring them. This prevented the accidental hardlinking, but at the cost of losing all hardlinks that involved large inode values. This PR further improves things by stripping the high order bit from 64-bit inode values in the AFIO parser. This allows them to be mostly preserved and should allow hardlinks to get properly processed in the vast majority of cases. The only false hardlinks would be when there are inode values that differ exactly in the high order bit, which should be very rare. A full solution will require expanding inode handling to use unsigned 64-bit values; we can't do that without a major version bump, but this PR also sets the stage for migrating inode support in a future libarchive 4.0. --- diff --git a/libarchive/archive.h b/libarchive/archive.h index d6de7fd12..aeef45271 100644 --- a/libarchive/archive.h +++ b/libarchive/archive.h @@ -66,12 +66,15 @@ #define __LA_INT64_T_DEFINED # if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__WATCOMC__) typedef __int64 la_int64_t; +typedef unsigned __int64 la_uint64_t; # else # include /* ssize_t */ # if defined(_SCO_DS) || defined(__osf__) typedef long long la_int64_t; +typedef unsigned long long la_uint64_t; # else typedef int64_t la_int64_t; +typedef uint64_t la_uint64_t; # endif # endif #endif diff --git a/libarchive/archive_entry.h b/libarchive/archive_entry.h index 564390597..84ed1b0bd 100644 --- a/libarchive/archive_entry.h +++ b/libarchive/archive_entry.h @@ -40,7 +40,7 @@ #include #include /* for wchar_t */ -#include +#include /* for C99 int64_t, etc. */ #if ARCHIVE_VERSION_NUMBER < 4000000 /* time_t is slated to be removed from public includes in 4.0 */ #include @@ -58,12 +58,15 @@ #define __LA_INT64_T_DEFINED # if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__WATCOMC__) typedef __int64 la_int64_t; +typedef unsigned __int64 la_uint64_t; # else #include # if defined(_SCO_DS) || defined(__osf__) typedef long long la_int64_t; +typedef unsigned long long la_uint64_t; # else typedef int64_t la_int64_t; +typedef uint64_t la_uint64_t; # endif # endif #endif @@ -115,6 +118,14 @@ typedef ssize_t la_ssize_t; #define __LA_DEV_T la_int64_t #endif +#if ARCHIVE_VERSION_NUMBER < 4000000 +/* Libarchive 3.x used signed int64 for inode numbers */ +#define __LA_INO_T la_int64_t +#else +/* Switch to unsigned for libarchive 4.0 */ +#define __LA_INO_T la_uint64_t +#endif + /* Large file support for Android */ #if defined(__LIBARCHIVE_BUILD) && defined(__ANDROID__) #include "android_lf.h" @@ -284,8 +295,8 @@ __LA_DECL const char *archive_entry_hardlink(struct archive_entry *); __LA_DECL const char *archive_entry_hardlink_utf8(struct archive_entry *); __LA_DECL const wchar_t *archive_entry_hardlink_w(struct archive_entry *); __LA_DECL int archive_entry_hardlink_is_set(struct archive_entry *); -__LA_DECL la_int64_t archive_entry_ino(struct archive_entry *); -__LA_DECL la_int64_t archive_entry_ino64(struct archive_entry *); +__LA_DECL __LA_INO_T archive_entry_ino(struct archive_entry *); +__LA_DECL __LA_INO_T archive_entry_ino64(struct archive_entry *); __LA_DECL int archive_entry_ino_is_set(struct archive_entry *); __LA_DECL __LA_MODE_T archive_entry_mode(struct archive_entry *); __LA_DECL time_t archive_entry_mtime(struct archive_entry *); @@ -363,8 +374,8 @@ __LA_DECL void archive_entry_set_hardlink_utf8(struct archive_entry *, const cha __LA_DECL void archive_entry_copy_hardlink(struct archive_entry *, const char *); __LA_DECL void archive_entry_copy_hardlink_w(struct archive_entry *, const wchar_t *); __LA_DECL int archive_entry_update_hardlink_utf8(struct archive_entry *, const char *); -__LA_DECL void archive_entry_set_ino(struct archive_entry *, la_int64_t); -__LA_DECL void archive_entry_set_ino64(struct archive_entry *, la_int64_t); +__LA_DECL void archive_entry_set_ino(struct archive_entry *, __LA_INO_T); +__LA_DECL void archive_entry_set_ino64(struct archive_entry *, __LA_INO_T); __LA_DECL void archive_entry_set_link(struct archive_entry *, const char *); __LA_DECL void archive_entry_set_link_utf8(struct archive_entry *, const char *); __LA_DECL void archive_entry_copy_link(struct archive_entry *, const char *); diff --git a/libarchive/archive_read_support_format_cpio.c b/libarchive/archive_read_support_format_cpio.c index 42a89c1b6..74f3549d1 100644 --- a/libarchive/archive_read_support_format_cpio.c +++ b/libarchive/archive_read_support_format_cpio.c @@ -189,6 +189,7 @@ struct cpio { }; static int64_t atol16(const char *, unsigned); +static uint64_t atol16u(const char *, unsigned); static int64_t atol8(const char *, unsigned); static int archive_read_format_cpio_bid(struct archive_read *, int); static int archive_read_format_cpio_options(struct archive_read *, @@ -835,6 +836,7 @@ header_afiol(struct archive_read *a, struct cpio *cpio, struct archive_entry *entry, size_t *namelength, size_t *name_pad) { int64_t t; + uint64_t u; const void *h; const char *header; @@ -851,12 +853,12 @@ header_afiol(struct archive_read *a, struct cpio *cpio, archive_entry_set_dev(entry, (dev_t)atol16(header + afiol_dev_offset, afiol_dev_size)); - t = atol16(header + afiol_ino_offset, afiol_ino_size); - if (t < 0) { - archive_set_error(&a->archive, 0, "Nonsensical ino value"); - return (ARCHIVE_FATAL); - } - archive_entry_set_ino(entry, t); + u = atol16u(header + afiol_ino_offset, afiol_ino_size); +#if ARCHIVE_VERSION_NUMBER < 4000000 + archive_entry_set_ino(entry, (int64_t)(u & INT64_MAX)); +#else + archive_entry_set_ino(entry, u); +#endif archive_entry_set_mode(entry, (mode_t)atol8(header + afiol_mode_offset, afiol_mode_size)); archive_entry_set_uid(entry, atol16(header + afiol_uid_offset, afiol_uid_size)); @@ -1030,6 +1032,12 @@ atol8(const char *p, unsigned char_cnt) static int64_t atol16(const char *p, unsigned char_cnt) +{ + return ((int64_t)atol16u(p, char_cnt)); +} + +static uint64_t +atol16u(const char *p, unsigned char_cnt) { uint64_t l; int digit; @@ -1048,7 +1056,7 @@ atol16(const char *p, unsigned char_cnt) l <<= 4; l |= digit; } - return ((int64_t)l); + return (l); } static int