diff options
author | David Oberhollenzer <david.oberhollenzer@sigma-star.at> | 2023-01-31 11:21:30 +0100 |
---|---|---|
committer | David Oberhollenzer <david.oberhollenzer@sigma-star.at> | 2023-01-31 13:51:49 +0100 |
commit | cdccc69c62579b0c13b35fad0728079652b8f3c9 (patch) | |
tree | 9fa54c710f73c5e08a9c8466e7a712eb63ee07ac /lib/tar/src/write_header.c | |
parent | 2182129c8f359c4fa1390eaba7a65b595ccd4182 (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.c | 282 |
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)); +} |