From f48fc107cbc1cf28cbd305b37c399cd180cefeed Mon Sep 17 00:00:00 2001 From: Patrick McDermott Date: Tue, 02 May 2023 22:30:32 -0400 Subject: ustar: Improve compatibility of written headers --- (limited to 'src') diff --git a/src/ustar.c b/src/ustar.c index d70a616..b5b0310 100644 --- a/src/ustar.c +++ b/src/ustar.c @@ -28,6 +28,8 @@ #define OPKG_OPK_USTAR_NUM_BASE_ 8 +/* #define OPKG_OPK_USTAR_BB_EXACT_HEADER_ 1 */ + struct _opkg_opk_ustar_header { char name [100]; char mode [8]; @@ -284,13 +286,16 @@ opkg_opk_ustar_write_header(struct opkg_opk_ustar *ustar, sizeof(ustar->header.prefix)) != OPKG_OPK_OK) { return OPKG_OPK_ERROR; } - sprintf(ustar->header.mode, "%o", mode); - sprintf(ustar->header.uid, "%o", uid); - sprintf(ustar->header.gid, "%o", gid); - strncpy(ustar->header.uname, uname, sizeof(ustar->header.uname)); - strncpy(ustar->header.gname, gname, sizeof(ustar->header.gname)); - sprintf(ustar->header.size, "%o", size); - sprintf(ustar->header.mtime, "%o", mtime); + /* POSIX doesn't say to NUL-terminate mode, uid, gid, size, or mtime. + * GNU and BB tar accept values that aren't (but generate values that + * ARE) NUL-terminated. But more importantly, opkg-lede (see file + * libbb/unarchive.c) parses these with simple strtol() calls, without + * even checking endptr, so we have to NUL-terminate these fields. */ + sprintf(ustar->header.mode, "%07o", mode); + sprintf(ustar->header.uid, "%07o", uid); + sprintf(ustar->header.gid, "%07o", gid); + sprintf(ustar->header.size, "%011o", size); + sprintf(ustar->header.mtime, "%011o", mtime); switch (type) { case '-': /* Regular file */ *ustar->header.typeflag = '0'; @@ -315,9 +320,24 @@ opkg_opk_ustar_write_header(struct opkg_opk_ustar *ustar, default: return OPKG_OPK_ERROR; /* Unsupported */ } + strncpy(ustar->header.uname, uname, sizeof(ustar->header.uname)); + strncpy(ustar->header.gname, gname, sizeof(ustar->header.gname)); + + /* In these fields POSIX says to write: But GNU and BB tar write: */ + strcpy(ustar->header.magic, "ustar"); /* "ustar " */ + memcpy(ustar->header.version, "00", 2); /* " \0" i.e. 0x20 0x00 */ + /* See files in BusyBox: + * include/bb_archive.h + * archival/chksum_and_xwrite_tar_header.c + * archival/libarchive/get_header_tar.c + * GNU and BB tar accept POSIX-conformant magic and version fields. + * opkg-lede libbb/unarchive.c only checks first 5 bytes of magic and + * ignores version. So conforming to POSIX seems safe. + */ +#if OPKG_OPK_USTAR_BB_EXACT_HEADER_ + strcpy(ustar->header.magic, "ustar "); +#endif - strcpy(ustar->header.magic, "ustar"); - memcpy(ustar->header.version, "00", 2); chksum = 0; for (i = 0; i < sizeof(ustar->header.chksum); ++i) { ustar->header.chksum[i] = ' '; @@ -326,7 +346,17 @@ opkg_opk_ustar_write_header(struct opkg_opk_ustar *ustar, for (i = 0; i < OPKG_OPK_USTAR_RECORD_SIZE; ++i) { chksum += header_uc[i]; } - sprintf(ustar->header.chksum, "%o", chksum); + /* See above about NUL-terminating and strtol() in opkg-lede. */ + /* Since commit 5661fe078eed752780b11f3f4fdd33bbd76a6c5e, BB tar has + * filled chksum with "[6] digits, a null, then a space -- rather than + * digits, followed by a null like the other fields...". The maxiumum + * checksum is (255 x 512 = octal 377000), i.e. a maximum of 6 octal + * digits, so it doesn't matter whether we use 6 or 7 digits. */ +#if OPKG_OPK_USTAR_BB_EXACT_HEADER_ + sprintf(ustar->header.chksum, "%06o", chksum); +#else + sprintf(ustar->header.chksum, "%07o", chksum); +#endif if (opkg_opk_gzip_write(ustar->gzip, &ustar->header, OPKG_OPK_USTAR_RECORD_SIZE) != OPKG_OPK_OK) { -- cgit v0.9.1