aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorElliott Hughes <enh@google.com>2021-03-08 09:47:54 -0800
committerRob Landley <rob@landley.net>2021-03-09 03:54:59 -0600
commit5c6628e724e9d3625f161db077b15b232e2ca096 (patch)
tree333fdc1b9038652017cd95aa94efedf29eda9f9a
parent668572a8650f3d4ff0fbe54ea07499b941244bdb (diff)
downloadtoybox-5c6628e724e9d3625f161db077b15b232e2ca096.tar.gz
tar: fix base-256 output.
A tar file created by toybox tar with values large enough to need base-256 rather than ASCII octal caused a tar reader to crash, and caused GNU tar to complain. I note from the docs at https://github.com/libarchive/libarchive/blob/master/libarchive/tar.5#L326 that they imply that only the top *bit* should be set to indicate this format, not the whole top byte, to give a 95-bit or 63-bit field. But I don't think we can hit that in practice? Bug: http://issuetracker.google.com/181683612
-rw-r--r--tests/tar.test5
-rw-r--r--toys/posix/tar.c17
2 files changed, 17 insertions, 5 deletions
diff --git a/tests/tar.test b/tests/tar.test
index 4b2f2120..70668c9c 100644
--- a/tests/tar.test
+++ b/tests/tar.test
@@ -59,6 +59,11 @@ skipnot id nobody >/dev/null
testing "pass group" "tar c --owner root --group nobody --mtime @0 file | LST" \
"-rw-rw-r-- root/nobody 0 1970-01-01 00:00 file\n" "" ""
+# Historically we output a "base 256" format that _we_ could decode but that
+# GNU tar choked on, so check the exact bytes with SUM, not a LST round trip.
+testing "huge values" "tar c --owner 9999999 --group 8888888 --mtime @0 file | SUM 3" \
+ "396b07fd2f80eeb312462e3bfb7dc1325dc6bcfb\n" "" ""
+
touch -t 198701231234.56 file
testing "pass mtime" \
"tar c --owner root --group root file | LST --full-time" \
diff --git a/toys/posix/tar.c b/toys/posix/tar.c
index f31640c5..25dfc167 100644
--- a/toys/posix/tar.c
+++ b/toys/posix/tar.c
@@ -85,14 +85,19 @@ struct tar_hdr {
prefix[155], padd[12];
};
+// Tar uses ASCII octal when it fits, base-256 otherwise.
+#define ASCII_FITS(val, len) (!(val>>(3*(len-1))))
// convert from int to octal (or base-256)
static void itoo(char *str, int len, unsigned long long val)
{
- // Do we need binary encoding?
- if (!(val>>(3*(len-1)))) sprintf(str, "%0*llo", len-1, val);
+ if (ASCII_FITS(val, len)) sprintf(str, "%0*llo", len-1, val);
else {
+ str += len;
+ while (len--) {
+ *--str = val;
+ val >>= 8;
+ }
*str = 128;
- while (--len) *++str = val>>(3*len);
}
}
#define ITOO(x, y) itoo(x, sizeof(x), y)
@@ -288,9 +293,11 @@ static int add_to_tar(struct dirtree *node)
if (strlen(hname) > sizeof(hdr.name)) write_longname(hname, 'L');
if (!FLAG(numeric_owner)) {
- if (TT.owner || (pw = bufgetpwuid(st->st_uid)))
+ if ((TT.owner || (pw = bufgetpwuid(st->st_uid))) &&
+ ASCII_FITS(st->st_uid, sizeof(hdr.uid)))
strncpy(hdr.uname, TT.owner ? TT.owner : pw->pw_name, sizeof(hdr.uname));
- if (TT.group || (gr = bufgetgrgid(st->st_gid)))
+ if ((TT.group || (gr = bufgetgrgid(st->st_gid))) &&
+ ASCII_FITS(st->st_gid, sizeof(hdr.gid)))
strncpy(hdr.gname, TT.group ? TT.group : gr->gr_name, sizeof(hdr.gname));
}