tar: accomodate non-terminated tar.chksum fields as seen from github.com
authorDenys Vlasenko <vda.linux@googlemail.com>
Sat, 27 Jan 2018 18:04:08 +0000 (19:04 +0100)
committerDenys Vlasenko <vda.linux@googlemail.com>
Wed, 14 Feb 2018 16:38:29 +0000 (17:38 +0100)
function                                             old     new   delta
get_header_tar                                      1783    1696     -87

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
archival/libarchive/get_header_tar.c

index aeb54190f88269b6a1a91c3acfe92ccc7608dcfe..5c495e14e66b89c27f3e86ac412f84a92e3f6cec 100644 (file)
@@ -152,6 +152,7 @@ char FAST_FUNC get_header_tar(archive_handle_t *archive_handle)
        file_header_t *file_header = archive_handle->file_header;
        struct tar_header_t tar;
        char *cp;
+       int tar_typeflag; /* can be "char", "int" seems give smaller code */
        int i, sum_u, sum;
 #if ENABLE_FEATURE_TAR_OLDSUN_COMPATIBILITY
        int sum_s;
@@ -253,10 +254,10 @@ char FAST_FUNC get_header_tar(archive_handle_t *archive_handle)
         * POSIX says that checksum is done on unsigned bytes, but
         * Sun and HP-UX gets it wrong... more details in
         * GNU tar source. */
+       sum_u = ' ' * sizeof(tar.chksum);
 #if ENABLE_FEATURE_TAR_OLDSUN_COMPATIBILITY
-       sum_s = ' ' * sizeof(tar.chksum);
+       sum_s = sum_u;
 #endif
-       sum_u = ' ' * sizeof(tar.chksum);
        for (i = 0; i < 148; i++) {
                sum_u += ((unsigned char*)&tar)[i];
 #if ENABLE_FEATURE_TAR_OLDSUN_COMPATIBILITY
@@ -269,27 +270,22 @@ char FAST_FUNC get_header_tar(archive_handle_t *archive_handle)
                sum_s += ((signed char*)&tar)[i];
 #endif
        }
-       /* This field does not need special treatment (getOctal) */
-       {
-               char *endp; /* gcc likes temp var for &endp */
-               sum = strtoul(tar.chksum, &endp, 8);
-               if ((*endp != '\0' && *endp != ' ')
-                || (sum_u != sum IF_FEATURE_TAR_OLDSUN_COMPATIBILITY(&& sum_s != sum))
-               ) {
-                       bb_error_msg_and_die("invalid tar header checksum");
-               }
-       }
-       /* don't use xstrtoul, tar.chksum may have leading spaces */
-       sum = strtoul(tar.chksum, NULL, 8);
-       if (sum_u != sum IF_FEATURE_TAR_OLDSUN_COMPATIBILITY(&& sum_s != sum)) {
+       /* Most tarfiles have tar.chksum NUL or space terminated, but
+        * github.com decided to be "special" and have unterminated field:
+        * 0090: 30343300 30303031 33323731 30000000 |043.000132710...|
+        *                                                ^^^^^^^^|
+        * Need to use GET_OCTAL. This overwrites tar.typeflag ---+
+        * (the '0' char immediately after chksum in example above) with NUL.
+        */
+       tar_typeflag = (uint8_t)tar.typeflag; /* save it */
+       sum = GET_OCTAL(tar.chksum);
+       if (sum_u != sum
+           IF_FEATURE_TAR_OLDSUN_COMPATIBILITY(&& sum_s != sum)
+       ) {
                bb_error_msg_and_die("invalid tar header checksum");
        }
 
-       /* 0 is reserved for high perf file, treat as normal file */
-       if (!tar.typeflag) tar.typeflag = '0';
-       parse_names = (tar.typeflag >= '0' && tar.typeflag <= '7');
-
-       /* getOctal trashes subsequent field, therefore we call it
+       /* GET_OCTAL trashes subsequent field, therefore we call it
         * on fields in reverse order */
        if (tar.devmajor[0]) {
                char t = tar.prefix[0];
@@ -299,6 +295,11 @@ char FAST_FUNC get_header_tar(archive_handle_t *archive_handle)
                file_header->device = makedev(major, minor);
                tar.prefix[0] = t;
        }
+
+       /* 0 is reserved for high perf file, treat as normal file */
+       if (tar_typeflag == '\0') tar_typeflag = '0';
+       parse_names = (tar_typeflag >= '0' && tar_typeflag <= '7');
+
        file_header->link_target = NULL;
        if (!p_linkname && parse_names && tar.linkname[0]) {
                file_header->link_target = xstrndup(tar.linkname, sizeof(tar.linkname));
@@ -332,7 +333,7 @@ char FAST_FUNC get_header_tar(archive_handle_t *archive_handle)
 
        /* Set bits 12-15 of the files mode */
        /* (typeflag was not trashed because chksum does not use getOctal) */
-       switch (tar.typeflag) {
+       switch (tar_typeflag) {
        case '1': /* hardlink */
                /* we mark hardlinks as regular files with zero size and a link name */
                file_header->mode |= S_IFREG;
@@ -381,7 +382,7 @@ char FAST_FUNC get_header_tar(archive_handle_t *archive_handle)
        case 'x': {     /* pax extended header */
                if ((uoff_t)file_header->size > 0xfffff) /* paranoia */
                        goto skip_ext_hdr;
-               process_pax_hdr(archive_handle, file_header->size, (tar.typeflag == 'g'));
+               process_pax_hdr(archive_handle, file_header->size, (tar_typeflag == 'g'));
                goto again_after_align;
 #if ENABLE_FEATURE_TAR_GNU_EXTENSIONS
 /* See http://www.gnu.org/software/tar/manual/html_node/Extensions.html */
@@ -419,7 +420,7 @@ char FAST_FUNC get_header_tar(archive_handle_t *archive_handle)
  skip_ext_hdr:
        {
                off_t sz;
-               bb_error_msg("warning: skipping header '%c'", tar.typeflag);
+               bb_error_msg("warning: skipping header '%c'", tar_typeflag);
                sz = (file_header->size + 511) & ~(off_t)511;
                archive_handle->offset += sz;
                sz >>= 9; /* sz /= 512 but w/o contortions for signed div */
@@ -429,7 +430,7 @@ char FAST_FUNC get_header_tar(archive_handle_t *archive_handle)
                goto again_after_align;
        }
        default:
-               bb_error_msg_and_die("unknown typeflag: 0x%x", tar.typeflag);
+               bb_error_msg_and_die("unknown typeflag: 0x%x", tar_typeflag);
        }
 
 #if ENABLE_FEATURE_TAR_GNU_EXTENSIONS