summaryrefslogtreecommitdiff
path: root/lib/tar/write_header.c
diff options
context:
space:
mode:
authorDavid Oberhollenzer <david.oberhollenzer@sigma-star.at>2019-07-03 15:08:15 +0200
committerDavid Oberhollenzer <david.oberhollenzer@sigma-star.at>2019-07-03 15:17:57 +0200
commit70dcf39f5926a66d76eb9fde2cbaef4b6a23a9e1 (patch)
treee4bfefd6c4559ad3abbd89af546099cb56a24040 /lib/tar/write_header.c
parentb8d4983147141310383d423b1bcb9d19a490977a (diff)
tar writer: replace PAX headers with GNU extensions
Some experiments seem to indicate that the various GNU extensions are more widely supported than their POSIX equivalents[1]. Possibly because they are easier to implement and possibly because of the wide spread use of GNU tar. This commit replaces the PAX writer in the write_tar_header implementation with a GNU extension based writer. The writer is also cleaned up by removing all global state. The record counter is moved outside into the tar2sqfs program and passed in as function argument. [1] https://dev.gentoo.org/~mgorny/articles/portability-of-tar-features.html Signed-off-by: David Oberhollenzer <david.oberhollenzer@sigma-star.at>
Diffstat (limited to 'lib/tar/write_header.c')
-rw-r--r--lib/tar/write_header.c282
1 files changed, 119 insertions, 163 deletions
diff --git a/lib/tar/write_header.c b/lib/tar/write_header.c
index b84324b..79fa49a 100644
--- a/lib/tar/write_header.c
+++ b/lib/tar/write_header.c
@@ -1,197 +1,166 @@
/* SPDX-License-Identifier: GPL-3.0-or-later */
#include "internal.h"
-static unsigned long pax_hdr_counter = 0;
-static char buffer[4096];
-
-static int name_to_tar_header(tar_header_t *hdr, const char *path)
+static void write_binary(char *dst, uint64_t value, int digits)
{
- size_t len = strlen(path);
- const char *ptr;
+ memset(dst, 0, digits);
- if ((len + 1) <= sizeof(hdr->name)) {
- memcpy(hdr->name, path, len);
- return 0;
+ while (digits > 0) {
+ ((unsigned char *)dst)[digits - 1] = value & 0xFF;
+ --digits;
+ value >>= 8;
}
- for (ptr = path; ; ++ptr) {
- ptr = strchr(ptr, '/');
- if (ptr == NULL)
- return -1;
-
- len = ptr - path;
- if (len >= sizeof(hdr->tail.posix.prefix))
- continue;
- if (strlen(ptr + 1) >= sizeof(hdr->name))
- continue;
- break;
- }
-
- memcpy(hdr->tail.posix.prefix, path, ptr - path);
- memcpy(hdr->name, ptr + 1, strlen(ptr + 1));
- return 0;
+ ((unsigned char *)dst)[0] |= 0x80;
}
-static void init_header(tar_header_t *hdr, const struct stat *sb,
- const char *name, const char *slink_target)
+static void write_number(char *dst, uint64_t value, int digits)
{
- memset(hdr, 0, sizeof(*hdr));
-
- name_to_tar_header(hdr, name);
- memcpy(hdr->magic, TAR_MAGIC, sizeof(hdr->magic));
- memcpy(hdr->version, TAR_VERSION, sizeof(hdr->version));
- write_octal(hdr->mode, sb->st_mode & ~S_IFMT, 6);
- write_octal(hdr->uid, sb->st_uid, 6);
- write_octal(hdr->gid, sb->st_gid, 6);
- write_octal(hdr->mtime, sb->st_mtime, 11);
- write_octal(hdr->size, 0, 11);
- write_octal(hdr->devmajor, 0, 6);
- write_octal(hdr->devminor, 0, 6);
-
- switch (sb->st_mode & S_IFMT) {
- case S_IFREG:
- write_octal(hdr->size, sb->st_size & 077777777777L, 11);
- break;
- case S_IFLNK:
- if (sb->st_size < (off_t)sizeof(hdr->linkname))
- strcpy(hdr->linkname, slink_target);
- break;
- case S_IFCHR:
- case S_IFBLK:
- write_octal(hdr->devmajor, major(sb->st_rdev), 6);
- write_octal(hdr->devminor, minor(sb->st_rdev), 6);
- break;
+ uint64_t mask = 0;
+ char buffer[64];
+ int i;
+
+ for (i = 0; i < (digits - 1); ++i)
+ mask = (mask << 3) | 7;
+
+ if (value <= mask) {
+ sprintf(buffer, "%0*o ", digits - 1, (unsigned int)value);
+ memcpy(dst, buffer, digits);
+ } else if (value <= ((mask << 3) | 7)) {
+ sprintf(buffer, "%0*o", digits, (unsigned int)value);
+ memcpy(dst, buffer, digits);
+ } else {
+ write_binary(dst, value, digits);
}
-
- sprintf(hdr->uname, "%u", sb->st_uid);
- sprintf(hdr->gname, "%u", sb->st_gid);
}
-static bool need_pax_header(const struct stat *sb, const char *name)
+static void write_number_signed(char *dst, int64_t value, int digits)
{
- tar_header_t tmp;
-
- if (sb->st_uid > 0777777 || sb->st_gid > 0777777)
- return true;
-
- if (S_ISREG(sb->st_mode) && sb->st_size > 077777777777L)
- return true;
-
- if (S_ISLNK(sb->st_mode) && sb->st_size >= (off_t)sizeof(tmp.linkname))
- return true;
-
- if (name_to_tar_header(&tmp, name))
- return true;
+ uint64_t neg;
- return false;
+ if (value < 0) {
+ neg = -value;
+ write_binary(dst, ~neg + 1, digits);
+ } else {
+ write_number(dst, value, digits);
+ }
}
-static char *write_pax_entry(char *dst, const char *key, const char *value)
+static int write_header(int fd, const struct stat *sb, const char *name,
+ const char *slink_target, int type)
{
- size_t i, len, prefix = 0, oldprefix;
+ int maj = 0, min = 0;
+ uint64_t size = 0;
+ tar_header_t hdr;
+ ssize_t ret;
- do {
- len = prefix + 1 + strlen(key) + 1 + strlen(value) + 1;
+ if (S_ISCHR(sb->st_mode) || S_ISBLK(sb->st_mode)) {
+ maj = major(sb->st_rdev);
+ min = minor(sb->st_rdev);
+ }
+
+ if (S_ISREG(sb->st_mode))
+ size = sb->st_size;
+
+ memset(&hdr, 0, sizeof(hdr));
+
+ memcpy(hdr.name, name, strlen(name));
+ write_number(hdr.mode, sb->st_mode & ~S_IFMT, sizeof(hdr.mode));
+ write_number(hdr.uid, sb->st_uid, sizeof(hdr.uid));
+ write_number(hdr.gid, sb->st_gid, sizeof(hdr.gid));
+ write_number(hdr.size, size, sizeof(hdr.size));
+ write_number_signed(hdr.mtime, sb->st_mtime, sizeof(hdr.mtime));
+ hdr.typeflag = type;
+ if (slink_target != NULL)
+ memcpy(hdr.linkname, slink_target, sb->st_size);
+ memcpy(hdr.magic, TAR_MAGIC_OLD, sizeof(hdr.magic));
+ memcpy(hdr.version, TAR_VERSION_OLD, sizeof(hdr.version));
+ sprintf(hdr.uname, "%u", sb->st_uid);
+ sprintf(hdr.gname, "%u", sb->st_gid);
+ write_number(hdr.devmajor, maj, sizeof(hdr.devmajor));
+ write_number(hdr.devminor, min, sizeof(hdr.devminor));
- oldprefix = prefix;
- prefix = 1;
+ update_checksum(&hdr);
- for (i = len; i >= 10; i /= 10)
- ++prefix;
- } while (oldprefix != prefix);
+ ret = write_retry(fd, &hdr, sizeof(hdr));
+ if (ret < 0) {
+ perror("writing header record");
+ return -1;
+ }
- sprintf(dst, "%zu %s=%s\n", len, key, value);
+ if ((size_t)ret < sizeof(hdr)) {
+ fputs("writing header record: truncated write\n", stderr);
+ return -1;
+ }
- return dst + len;
+ return 0;
}
-static int write_pax_header(int fd, const struct stat *sb, const char *name,
- const char *slink_target)
+static int write_gnu_header(int fd, const struct stat *orig,
+ const char *payload, size_t payload_len,
+ int type, const char *name)
{
- char temp[64], *ptr;
- struct stat fakesb;
- tar_header_t hdr;
+ struct stat sb;
ssize_t ret;
- size_t len;
-
- memset(buffer, 0, sizeof(buffer));
- memset(&fakesb, 0, sizeof(fakesb));
- fakesb.st_mode = S_IFREG | 0644;
-
- sprintf(temp, "pax%lu", pax_hdr_counter);
- init_header(&hdr, &fakesb, temp, NULL);
- hdr.typeflag = TAR_TYPE_PAX;
-
- sprintf(temp, "%u", sb->st_uid);
- ptr = buffer;
- ptr = write_pax_entry(ptr, "uid", temp);
- ptr = write_pax_entry(ptr, "uname", temp);
- sprintf(temp, "%lu", sb->st_mtime);
- ptr = write_pax_entry(ptr, "mtime", temp);
+ sb = *orig;
+ sb.st_mode = S_IFREG | 0644;
+ sb.st_size = payload_len;
- sprintf(temp, "%u", sb->st_gid);
- ptr = write_pax_entry(ptr, "gid", temp);
- ptr = write_pax_entry(ptr, "gname", temp);
+ if (write_header(fd, &sb, name, NULL, type))
+ return -1;
- ptr = write_pax_entry(ptr, "path", name);
-
- if (S_ISLNK(sb->st_mode)) {
- write_pax_entry(ptr, "linkpath", slink_target);
- } else if (S_ISREG(sb->st_mode)) {
- sprintf(temp, "%lu", sb->st_size);
- write_pax_entry(ptr, "size", temp);
+ ret = write_retry(fd, payload, payload_len);
+ if (ret < 0) {
+ perror("writing GNU extension header");
+ return -1;
}
- len = strlen(buffer);
- write_octal(hdr.size, len, 11);
- update_checksum(&hdr);
+ if ((size_t)ret < payload_len) {
+ fputs("writing GNU extension header: truncated write\n", stderr);
+ return -1;
+ }
- ret = write_retry(fd, &hdr, sizeof(hdr));
- if (ret < 0)
- goto fail_wr;
- if ((size_t)ret < sizeof(hdr))
- goto fail_trunc;
-
- ret = write_retry(fd, buffer, len);
- if (ret < 0)
- goto fail_wr;
- if ((size_t)ret < len)
- goto fail_trunc;
-
- return padd_file(fd, len, 512);
-fail_wr:
- perror("writing pax header");
- return -1;
-fail_trunc:
- fputs("writing pax header: truncated write\n", stderr);
- return -1;
+ return padd_file(fd, payload_len, 512);
}
int write_tar_header(int fd, const struct stat *sb, const char *name,
- const char *slink_target)
+ const char *slink_target, unsigned int counter)
{
const char *reason;
- tar_header_t hdr;
- ssize_t ret;
+ char buffer[64];
+ int type;
- if (need_pax_header(sb, name)) {
- if (write_pax_header(fd, sb, name, slink_target))
+ if (!S_ISLNK(sb->st_mode))
+ slink_target = NULL;
+
+ if (S_ISLNK(sb->st_mode) && sb->st_size >= 100) {
+ sprintf(buffer, "gnu/target%u", counter);
+ if (write_gnu_header(fd, sb, slink_target, sb->st_size,
+ TAR_TYPE_GNU_SLINK, buffer))
return -1;
+ slink_target = NULL;
+ }
+
+ if (strlen(name) >= 100) {
+ sprintf(buffer, "gnu/name%u", counter);
- sprintf(buffer, "pax%lu_data", pax_hdr_counter++);
+ if (write_gnu_header(fd, sb, name, strlen(name),
+ TAR_TYPE_GNU_PATH, buffer)) {
+ return -1;
+ }
+
+ sprintf(buffer, "gnu/data%u", counter);
name = buffer;
}
- init_header(&hdr, sb, name, slink_target);
-
switch (sb->st_mode & S_IFMT) {
- case S_IFCHR: hdr.typeflag = TAR_TYPE_CHARDEV; break;
- case S_IFBLK: hdr.typeflag = TAR_TYPE_BLOCKDEV; break;
- case S_IFLNK: hdr.typeflag = TAR_TYPE_SLINK; break;
- case S_IFREG: hdr.typeflag = TAR_TYPE_FILE; break;
- case S_IFDIR: hdr.typeflag = TAR_TYPE_DIR; break;
- case S_IFIFO: hdr.typeflag = TAR_TYPE_FIFO; break;
+ case S_IFCHR: type = TAR_TYPE_CHARDEV; break;
+ case S_IFBLK: type = TAR_TYPE_BLOCKDEV; break;
+ case S_IFLNK: type = TAR_TYPE_SLINK; break;
+ case S_IFREG: type = TAR_TYPE_FILE; break;
+ case S_IFDIR: type = TAR_TYPE_DIR; break;
+ case S_IFIFO: type = TAR_TYPE_FIFO; break;
case S_IFSOCK:
reason = "cannot pack socket";
goto out_skip;
@@ -200,20 +169,7 @@ int write_tar_header(int fd, const struct stat *sb, const char *name,
goto out_skip;
}
- update_checksum(&hdr);
-
- ret = write_retry(fd, &hdr, sizeof(hdr));
-
- if (ret < 0) {
- perror("writing header record");
- } else if ((size_t)ret < sizeof(hdr)) {
- fputs("writing header record: truncated write\n", stderr);
- ret = -1;
- } else {
- ret = 0;
- }
-
- return ret;
+ return write_header(fd, sb, name, slink_target, type);
out_skip:
fprintf(stderr, "WARNING: skipping '%s' (%s)\n", name, reason);
return 1;