diff options
Diffstat (limited to 'archive-tar.c')
-rw-r--r-- | archive-tar.c | 107 |
1 files changed, 72 insertions, 35 deletions
diff --git a/archive-tar.c b/archive-tar.c index 0d1e6bd754..380e3aedd2 100644 --- a/archive-tar.c +++ b/archive-tar.c @@ -18,6 +18,21 @@ static int tar_umask = 002; static int write_tar_filter_archive(const struct archiver *ar, struct archiver_args *args); +/* + * This is the max value that a ustar size header can specify, as it is fixed + * at 11 octal digits. POSIX specifies that we switch to extended headers at + * this size. + * + * Likewise for the mtime (which happens to use a buffer of the same size). + */ +#if ULONG_MAX == 0xFFFFFFFF +#define USTAR_MAX_SIZE ULONG_MAX +#define USTAR_MAX_MTIME ULONG_MAX +#else +#define USTAR_MAX_SIZE 077777777777UL +#define USTAR_MAX_MTIME 077777777777UL +#endif + /* writes out the whole block, but only if it is full */ static void write_if_needed(void) { @@ -137,6 +152,20 @@ static void strbuf_append_ext_header(struct strbuf *sb, const char *keyword, strbuf_addch(sb, '\n'); } +/* + * Like strbuf_append_ext_header, but for numeric values. + */ +static void strbuf_append_ext_header_uint(struct strbuf *sb, + const char *keyword, + uintmax_t value) +{ + char buf[40]; /* big enough for 2^128 in decimal, plus NUL */ + int len; + + len = xsnprintf(buf, sizeof(buf), "%"PRIuMAX, value); + strbuf_append_ext_header(sb, keyword, buf, len); +} + static unsigned int ustar_header_chksum(const struct ustar_header *header) { const unsigned char *p = (const unsigned char *)header; @@ -167,37 +196,36 @@ static void prepare_header(struct archiver_args *args, struct ustar_header *header, unsigned int mode, unsigned long size) { - sprintf(header->mode, "%07o", mode & 07777); - sprintf(header->size, "%011lo", S_ISREG(mode) ? size : 0); - sprintf(header->mtime, "%011lo", (unsigned long) args->time); + xsnprintf(header->mode, sizeof(header->mode), "%07o", mode & 07777); + xsnprintf(header->size, sizeof(header->size), "%011lo", S_ISREG(mode) ? size : 0); + xsnprintf(header->mtime, sizeof(header->mtime), "%011lo", (unsigned long) args->time); - sprintf(header->uid, "%07o", 0); - sprintf(header->gid, "%07o", 0); + xsnprintf(header->uid, sizeof(header->uid), "%07o", 0); + xsnprintf(header->gid, sizeof(header->gid), "%07o", 0); strlcpy(header->uname, "root", sizeof(header->uname)); strlcpy(header->gname, "root", sizeof(header->gname)); - sprintf(header->devmajor, "%07o", 0); - sprintf(header->devminor, "%07o", 0); + xsnprintf(header->devmajor, sizeof(header->devmajor), "%07o", 0); + xsnprintf(header->devminor, sizeof(header->devminor), "%07o", 0); memcpy(header->magic, "ustar", 6); memcpy(header->version, "00", 2); - sprintf(header->chksum, "%07o", ustar_header_chksum(header)); + xsnprintf(header->chksum, sizeof(header->chksum), "%07o", ustar_header_chksum(header)); } -static int write_extended_header(struct archiver_args *args, - const unsigned char *sha1, - const void *buffer, unsigned long size) +static void write_extended_header(struct archiver_args *args, + const unsigned char *sha1, + const void *buffer, unsigned long size) { struct ustar_header header; unsigned int mode; memset(&header, 0, sizeof(header)); *header.typeflag = TYPEFLAG_EXT_HEADER; mode = 0100666; - sprintf(header.name, "%s.paxheader", sha1_to_hex(sha1)); + xsnprintf(header.name, sizeof(header.name), "%s.paxheader", sha1_to_hex(sha1)); prepare_header(args, &header, mode, size); write_blocked(&header, sizeof(header)); write_blocked(buffer, size); - return 0; } static int write_tar_entry(struct archiver_args *args, @@ -208,7 +236,7 @@ static int write_tar_entry(struct archiver_args *args, struct ustar_header header; struct strbuf ext_header = STRBUF_INIT; unsigned int old_mode = mode; - unsigned long size; + unsigned long size, size_in_header; void *buffer; int err = 0; @@ -233,10 +261,10 @@ static int write_tar_entry(struct archiver_args *args, size_t rest = pathlen - plen - 1; if (plen > 0 && rest <= sizeof(header.name)) { memcpy(header.prefix, path, plen); - memcpy(header.name, path + plen + 1, rest); + memcpy(header.name, path + plen + 1, rest); } else { - sprintf(header.name, "%s.data", - sha1_to_hex(sha1)); + xsnprintf(header.name, sizeof(header.name), "%s.data", + sha1_to_hex(sha1)); strbuf_append_ext_header(&ext_header, "path", path, pathlen); } @@ -259,23 +287,25 @@ static int write_tar_entry(struct archiver_args *args, if (S_ISLNK(mode)) { if (size > sizeof(header.linkname)) { - sprintf(header.linkname, "see %s.paxheader", - sha1_to_hex(sha1)); + xsnprintf(header.linkname, sizeof(header.linkname), + "see %s.paxheader", sha1_to_hex(sha1)); strbuf_append_ext_header(&ext_header, "linkpath", buffer, size); } else memcpy(header.linkname, buffer, size); } - prepare_header(args, &header, mode, size); + size_in_header = size; + if (S_ISREG(mode) && size > USTAR_MAX_SIZE) { + size_in_header = 0; + strbuf_append_ext_header_uint(&ext_header, "size", size); + } + + prepare_header(args, &header, mode, size_in_header); if (ext_header.len > 0) { - err = write_extended_header(args, sha1, ext_header.buf, - ext_header.len); - if (err) { - free(buffer); - return err; - } + write_extended_header(args, sha1, ext_header.buf, + ext_header.len); } strbuf_release(&ext_header); write_blocked(&header, sizeof(header)); @@ -289,24 +319,33 @@ static int write_tar_entry(struct archiver_args *args, return err; } -static int write_global_extended_header(struct archiver_args *args) +static void write_global_extended_header(struct archiver_args *args) { const unsigned char *sha1 = args->commit_sha1; struct strbuf ext_header = STRBUF_INIT; struct ustar_header header; unsigned int mode; - int err = 0; - strbuf_append_ext_header(&ext_header, "comment", sha1_to_hex(sha1), 40); + if (sha1) + strbuf_append_ext_header(&ext_header, "comment", + sha1_to_hex(sha1), 40); + if (args->time > USTAR_MAX_MTIME) { + strbuf_append_ext_header_uint(&ext_header, "mtime", + args->time); + args->time = USTAR_MAX_MTIME; + } + + if (!ext_header.len) + return; + memset(&header, 0, sizeof(header)); *header.typeflag = TYPEFLAG_GLOBAL_HEADER; mode = 0100666; - strcpy(header.name, "pax_global_header"); + xsnprintf(header.name, sizeof(header.name), "pax_global_header"); prepare_header(args, &header, mode, ext_header.len); write_blocked(&header, sizeof(header)); write_blocked(ext_header.buf, ext_header.len); strbuf_release(&ext_header); - return err; } static struct archiver **tar_filters; @@ -382,10 +421,8 @@ static int write_tar_archive(const struct archiver *ar, { int err = 0; - if (args->commit_sha1) - err = write_global_extended_header(args); - if (!err) - err = write_archive_entries(args, write_tar_entry); + write_global_extended_header(args); + err = write_archive_entries(args, write_tar_entry); if (!err) write_trailer(); return err; |