]> git.ipfire.org Git - thirdparty/libarchive.git/commitdiff
Improve support for AFIO 64-bit inode values (#2589)
authorTim Kientzle <kientzle@acm.org>
Sat, 26 Apr 2025 23:10:45 +0000 (16:10 -0700)
committerGitHub <noreply@github.com>
Sat, 26 Apr 2025 23:10:45 +0000 (16:10 -0700)
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.

libarchive/archive.h
libarchive/archive_entry.h
libarchive/archive_read_support_format_cpio.c

index d6de7fd12c59df58db93b2fae6cbd02ff9f2c8e7..aeef45271f54f32b3e8a17ee2a63efb6bb3f338d 100644 (file)
 #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 <unistd.h>  /* 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
index 5643905977a073ddad3cdebeeba6fbf5d9ed6ce1..84ed1b0bd93a935ac947bab713d0445cb5cca9f6 100644 (file)
@@ -40,7 +40,7 @@
 
 #include <sys/types.h>
 #include <stddef.h>  /* for wchar_t */
-#include <stdint.h>
+#include <stdint.h>  /* for C99 int64_t, etc. */
 #if ARCHIVE_VERSION_NUMBER < 4000000
 /* time_t is slated to be removed from public includes in 4.0 */
 #include <time.h>
 #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 <unistd.h>
 #  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 *);
index 42a89c1b69c45f170e895d26e5c25f7a2e534f68..74f3549d159ec985eac50dc8b10a623a2dfae6cf 100644 (file)
@@ -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