aboutsummaryrefslogtreecommitdiff
path: root/lib/tar/src/write_header.c
diff options
context:
space:
mode:
authorDavid Oberhollenzer <david.oberhollenzer@sigma-star.at>2023-01-31 11:21:30 +0100
committerDavid Oberhollenzer <david.oberhollenzer@sigma-star.at>2023-01-31 13:51:49 +0100
commitcdccc69c62579b0c13b35fad0728079652b8f3c9 (patch)
tree9fa54c710f73c5e08a9c8466e7a712eb63ee07ac /lib/tar/src/write_header.c
parent2182129c8f359c4fa1390eaba7a65b595ccd4182 (diff)
Move library source into src sub-directory
Signed-off-by: David Oberhollenzer <david.oberhollenzer@sigma-star.at>
Diffstat (limited to 'lib/tar/src/write_header.c')
-rw-r--r--lib/tar/src/write_header.c282
1 files changed, 282 insertions, 0 deletions
diff --git a/lib/tar/src/write_header.c b/lib/tar/src/write_header.c
new file mode 100644
index 0000000..b0711b3
--- /dev/null
+++ b/lib/tar/src/write_header.c
@@ -0,0 +1,282 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later */
+/*
+ * write_header.c
+ *
+ * Copyright (C) 2019 David Oberhollenzer <goliath@infraroot.at>
+ */
+#include "config.h"
+
+#include "internal.h"
+#include <string.h>
+
+static void write_binary(char *dst, sqfs_u64 value, int digits)
+{
+ memset(dst, 0, digits);
+
+ while (digits > 0) {
+ ((unsigned char *)dst)[digits - 1] = value & 0xFF;
+ --digits;
+ value >>= 8;
+ }
+
+ ((unsigned char *)dst)[0] |= 0x80;
+}
+
+static void write_number(char *dst, sqfs_u64 value, int digits)
+{
+ sqfs_u64 mask = 0;
+ char buffer[64];
+ int i;
+
+ for (i = 0; i < (digits - 1); ++i)
+ mask = (mask << 3) | 7;
+
+ if (value <= mask) {
+ sprintf(buffer, "%0*lo ", digits - 1, (unsigned long)value);
+ memcpy(dst, buffer, digits);
+ } else if (value <= ((mask << 3) | 7)) {
+ sprintf(buffer, "%0*lo", digits, (unsigned long)value);
+ memcpy(dst, buffer, digits);
+ } else {
+ write_binary(dst, value, digits);
+ }
+}
+
+static void write_number_signed(char *dst, sqfs_s64 value, int digits)
+{
+ sqfs_u64 neg;
+
+ if (value < 0) {
+ neg = -value;
+ write_binary(dst, ~neg + 1, digits);
+ } else {
+ write_number(dst, value, digits);
+ }
+}
+
+static int write_header(ostream_t *fp, const struct stat *sb, const char *name,
+ const char *slink_target, int type)
+{
+ int maj = 0, min = 0;
+ sqfs_u64 size = 0;
+ tar_header_t hdr;
+
+ 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));
+
+ strncpy(hdr.name, name, sizeof(hdr.name) - 1);
+ 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));
+
+ update_checksum(&hdr);
+
+ return ostream_append(fp, &hdr, sizeof(hdr));
+}
+
+static int write_gnu_header(ostream_t *fp, const struct stat *orig,
+ const char *payload, size_t payload_len,
+ int type, const char *name)
+{
+ struct stat sb;
+
+ sb = *orig;
+ sb.st_mode = S_IFREG | 0644;
+ sb.st_size = payload_len;
+
+ if (write_header(fp, &sb, name, NULL, type))
+ return -1;
+
+ if (ostream_append(fp, payload, payload_len))
+ return -1;
+
+ return padd_file(fp, payload_len);
+}
+
+static size_t num_digits(size_t num)
+{
+ size_t i = 1;
+
+ while (num >= 10) {
+ num /= 10;
+ ++i;
+ }
+
+ return i;
+}
+
+static size_t prefix_digit_len(size_t len)
+{
+ size_t old_ndigit, ndigit = 0;
+
+ do {
+ old_ndigit = ndigit;
+ ndigit = num_digits(len + ndigit);
+ } while (old_ndigit != ndigit);
+
+ return ndigit;
+}
+
+static int write_schily_xattr(ostream_t *fp, const struct stat *orig,
+ const char *name, const tar_xattr_t *xattr)
+{
+ static const char *prefix = "SCHILY.xattr.";
+ size_t len, total_size = 0;
+ const tar_xattr_t *it;
+ struct stat sb;
+
+ for (it = xattr; it != NULL; it = it->next) {
+ len = strlen(prefix) + strlen(it->key) + it->value_len + 3;
+
+ total_size += len + prefix_digit_len(len);
+ }
+
+ sb = *orig;
+ sb.st_mode = S_IFREG | 0644;
+ sb.st_size = total_size;
+
+ if (write_header(fp, &sb, name, NULL, TAR_TYPE_PAX))
+ return -1;
+
+ for (it = xattr; it != NULL; it = it->next) {
+ len = strlen(prefix) + strlen(it->key) + it->value_len + 3;
+ len += prefix_digit_len(len);
+
+ if (ostream_printf(fp, PRI_SZ " %s%s=",
+ len, prefix, it->key) < 0) {
+ return -1;
+ }
+ if (ostream_append(fp, it->value, it->value_len))
+ return -1;
+ if (ostream_append(fp, "\n", 1))
+ return -1;
+ }
+
+ return padd_file(fp, total_size);
+}
+
+int write_tar_header(ostream_t *fp, const struct stat *sb, const char *name,
+ const char *slink_target, const tar_xattr_t *xattr,
+ unsigned int counter)
+{
+ const char *reason;
+ char buffer[64];
+ int type;
+
+ if (xattr != NULL) {
+ sprintf(buffer, "pax/xattr%u", counter);
+
+ if (write_schily_xattr(fp, sb, buffer, xattr))
+ return -1;
+ }
+
+ 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(fp, 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);
+
+ if (write_gnu_header(fp, sb, name, strlen(name),
+ TAR_TYPE_GNU_PATH, buffer)) {
+ return -1;
+ }
+
+ sprintf(buffer, "gnu/data%u", counter);
+ name = buffer;
+ }
+
+ switch (sb->st_mode & S_IFMT) {
+ 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;
+ default:
+ reason = "unknown type";
+ goto out_skip;
+ }
+
+ return write_header(fp, sb, name, slink_target, type);
+out_skip:
+ fprintf(stderr, "WARNING: %s: %s\n", name, reason);
+ return 1;
+}
+
+int write_hard_link(ostream_t *fp, const struct stat *sb, const char *name,
+ const char *target, unsigned int counter)
+{
+ tar_header_t hdr;
+ char buffer[64];
+ size_t len;
+
+ memset(&hdr, 0, sizeof(hdr));
+
+ len = strlen(target);
+ if (len >= 100) {
+ sprintf(buffer, "gnu/target%u", counter);
+ if (write_gnu_header(fp, sb, target, len,
+ TAR_TYPE_GNU_SLINK, buffer))
+ return -1;
+ sprintf(hdr.linkname, "hardlink_%u", counter);
+ } else {
+ memcpy(hdr.linkname, target, len);
+ }
+
+ len = strlen(name);
+ if (len >= 100) {
+ sprintf(buffer, "gnu/name%u", counter);
+ if (write_gnu_header(fp, sb, name, len,
+ TAR_TYPE_GNU_PATH, buffer)) {
+ return -1;
+ }
+ sprintf(hdr.name, "gnu/data%u", counter);
+ } else {
+ memcpy(hdr.name, name, len);
+ }
+
+ 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, 0, sizeof(hdr.size));
+ write_number_signed(hdr.mtime, sb->st_mtime, sizeof(hdr.mtime));
+ hdr.typeflag = TAR_TYPE_LINK;
+ 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, 0, sizeof(hdr.devmajor));
+ write_number(hdr.devminor, 0, sizeof(hdr.devminor));
+
+ update_checksum(&hdr);
+ return ostream_append(fp, &hdr, sizeof(hdr));
+}