summaryrefslogtreecommitdiffstats
path: root/src/ustar.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/ustar.c')
-rw-r--r--src/ustar.c446
1 files changed, 0 insertions, 446 deletions
diff --git a/src/ustar.c b/src/ustar.c
deleted file mode 100644
index 2b26ed1..0000000
--- a/src/ustar.c
+++ /dev/null
@@ -1,446 +0,0 @@
-/*
- * Copyright (C) 2023 Patrick McDermott
- *
- * This file is part of opkg-opk.
- *
- * opkg-opk is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * opkg-opk is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with opkg-opk. If not, see <https://www.gnu.org/licenses/>.
- */
-
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include "defs.h"
-#include "dirent.h"
-#include "gzip.h"
-#include "ustar.h"
-
-#define OPKG_OPK_USTAR_NUM_BASE_ 8
-
-/* #define OPKG_OPK_USTAR_BB_EXACT_HEADER_ 1 */
-
-struct _opkg_opk_ustar_header {
- char name [100];
- char mode [8];
- char uid [8];
- char gid [8];
- char size [12];
- char mtime [12];
- char chksum [8];
- char typeflag [1];
- char linkname [100];
- char magic [6];
- char version [2];
- char uname [32];
- char gname [32];
- char devmajor [8];
- char devminor [8];
- char prefix [155];
- char padding [12];
-} __attribute__((__packed__));
-
-struct opkg_opk_ustar {
- struct opkg_opk_gzip *gzip;
- uint64_t data_size_remaining;
- struct _opkg_opk_ustar_header header;
- char record[OPKG_OPK_USTAR_RECORD_SIZE];
-};
-
-struct opkg_opk_ustar *
-opkg_opk_ustar_init(struct opkg_opk_gzip *gzip)
-{
- struct opkg_opk_ustar *ustar;
-
- ustar = malloc(sizeof(*ustar));
- if (ustar == NULL) {
- return NULL;
- }
-
- ustar->gzip = gzip;
- ustar->data_size_remaining = 0;
-
- return ustar;
-}
-
-static int
-_opkg_opk_ustar_next(struct opkg_opk_ustar *ustar)
-{
- char *end;
- uint32_t chksum_got;
- uint32_t chksum_exp;
- size_t i;
- unsigned char *header_uc;
-
- /* Seek through data records until next header record. */
- while (ustar->data_size_remaining > 0) {
- if (opkg_opk_ustar_read(ustar, NULL, NULL) == OPKG_OPK_ERROR) {
- return OPKG_OPK_ERROR;
- }
- }
-
- /* Decompress next (hopefully valid header) record. */
- switch (opkg_opk_gzip_read(ustar->gzip, &ustar->header)) {
- case OPKG_OPK_OK:
- break;
- case OPKG_OPK_END:
- /* End of gzip stream before end of ustar archive */
- case OPKG_OPK_ERROR:
- return OPKG_OPK_ERROR;
- }
-
- /* Check for end of archive. */
- memset(ustar->record, 0, OPKG_OPK_USTAR_RECORD_SIZE);
- if (memcmp(&ustar->header, ustar->record,
- OPKG_OPK_USTAR_RECORD_SIZE) == 0) {
- return OPKG_OPK_END;
- }
-
- /* Verify magic. */
- if (memcmp(ustar->header.magic, "ustar", strlen("ustar")) != 0) {
- return OPKG_OPK_ERROR;
- }
-
- /* Verify checksum. */
- /* Assumes chksum is NUL-terminated. Not required by POSIX, but done by
- * GNU and BB tar and opkg_opk_ustar_write_header(). */
- chksum_got = strtol(ustar->header.chksum, &end,
- OPKG_OPK_USTAR_NUM_BASE_);
- chksum_exp = 0;
- if (*end != '\0') {
- return OPKG_OPK_ERROR;
- }
- for (i = 0; i < sizeof(ustar->header.chksum); ++i) {
- ustar->header.chksum[i] = ' ';
- }
- header_uc = (unsigned char *) &ustar->header;
- for (i = 0; i < OPKG_OPK_USTAR_RECORD_SIZE; ++i) {
- chksum_exp += header_uc[i];
- }
- if (chksum_got != chksum_exp) {
- return OPKG_OPK_ERROR;
- }
-
- /* Depending on type, get size. */
- switch (*ustar->header.typeflag) {
- case '0': /* Regular file */
- case '7': /* High-performance or regular file */
- /* Assumes size is NUL-terminated. Not required by
- * POSIX, but done by GNU and BB tar and
- * opkg_opk_ustar_write_header(). */
- ustar->data_size_remaining = strtol(
- ustar->header.size, &end,
- OPKG_OPK_USTAR_NUM_BASE_);
- if (*end != '\0') {
- return OPKG_OPK_ERROR;
- }
- break;
- case '2': /* Symbolic link */
- case '3': /* Character special file */
- case '4': /* Block special file */
- case '5': /* Directory */
- case '6': /* FIFO special file */
- ustar->data_size_remaining = 0;
- break;
- case '1': /* Link */
- default: /* Reserved */
- return OPKG_OPK_ERROR; /* Unsupported */
- }
-
- return OPKG_OPK_OK;
-}
-
-int
-opkg_opk_ustar_list(struct opkg_opk_ustar *ustar,
- struct opkg_opk_ustar_member **member)
-{
- int ret;
- char *end;
-
- /* Get next header record, if any. */
- if ((ret =_opkg_opk_ustar_next(ustar)) != OPKG_OPK_OK) {
- return ret; /* Error or end of archive */
- }
-
- /* Allocate outward-facing member information structure. */
- *member = malloc(sizeof(**member));
- if (*member == NULL) {
- return OPKG_OPK_ERROR;
- }
-
- /* Set name, mode, size, mtime, type, linkname, uname, gname, devmajor,
- * and devminor. */
- if (ustar->header.prefix[0] != '\0') {
- sprintf((*member)->name, "%s/%s",
- ustar->header.prefix, ustar->header.name);
- } else {
- /* Use memcpy() because ustar->header.name may not be
- * NUL-terminated. */
- memcpy((*member)->name, ustar->header.name,
- sizeof(ustar->header.name));
- (*member)->name[sizeof(ustar->header.name)] = '\0';
- }
- /* Assumes mode and mtime are NUL-terminated. Not required by POSIX,
- * but done by GNU and BB tar and opkg_opk_ustar_write_header(). */
- (*member)->mode = strtol(ustar->header.mode, &end,
- OPKG_OPK_USTAR_NUM_BASE_);
- if (*end != '\0') {
- free(*member);
- return OPKG_OPK_ERROR;
- }
- (*member)->size = ustar->data_size_remaining;
- (*member)->mtime = strtol(ustar->header.mtime, &end,
- OPKG_OPK_USTAR_NUM_BASE_);
- if (*end != '\0') {
- free(*member);
- return OPKG_OPK_ERROR;
- }
- switch (*ustar->header.typeflag) {
- case '0': /* Regular file */
- case '7': /* High-performance or regular file */
- (*member)->type = '-';
- break;
- case '2': /* Symbolic link */
- (*member)->type = 'l';
- strncpy((*member)->linkname, ustar->header.linkname,
- sizeof(ustar->header.linkname));
- (*member)->linkname[sizeof((*member)->linkname) - 1] =
- '\0';
- break;
- case '3': /* Character special file */
- (*member)->type = 'c';
- break;
- case '4': /* Block special file */
- (*member)->type = 'b';
- break;
- case '5': /* Directory */
- (*member)->type = 'd';
- break;
- case '6': /* FIFO special file */
- (*member)->type = 'p';
- break;
- case '1': /* Link */
- default: /* Reserved */
- free(*member);
- return OPKG_OPK_ERROR; /* Unsupported */
- }
- strncpy((*member)->uname, ustar->header.uname,
- sizeof((*member)->uname));
- strncpy((*member)->gname, ustar->header.gname,
- sizeof((*member)->gname));
- (*member)->devmajor = strtol(ustar->header.devmajor, &end,
- OPKG_OPK_USTAR_NUM_BASE_);
- if (*end != '\0') {
- free(*member);
- return OPKG_OPK_ERROR;
- }
- (*member)->devminor = strtol(ustar->header.devminor, &end,
- OPKG_OPK_USTAR_NUM_BASE_);
- if (*end != '\0') {
- free(*member);
- return OPKG_OPK_ERROR;
- }
-
- return OPKG_OPK_OK; /* Possibly more members in archive */
-}
-
-int
-opkg_opk_ustar_read(struct opkg_opk_ustar *ustar, char **buffer, size_t *size)
-{
- if (ustar->data_size_remaining == 0) {
- return OPKG_OPK_END;
- }
-
- /* Decompress next data record. */
- switch (opkg_opk_gzip_read(ustar->gzip, ustar->record)) {
- case OPKG_OPK_OK:
- break;
- case OPKG_OPK_END:
- case OPKG_OPK_ERROR:
- return OPKG_OPK_ERROR;
- }
-
- /* Store buffer and size in caller's memory and update remaining size.
- */
- if (buffer != NULL) {
- *buffer = ustar->record;
- }
- if (ustar->data_size_remaining >= OPKG_OPK_USTAR_RECORD_SIZE) {
- if (size != NULL) {
- *size = OPKG_OPK_USTAR_RECORD_SIZE;
- }
- ustar->data_size_remaining -= OPKG_OPK_USTAR_RECORD_SIZE;
- } else {
- if (size != NULL) {
- *size = ustar->data_size_remaining;
- }
- ustar->data_size_remaining = 0;
- }
- return OPKG_OPK_OK;
-}
-
-int
-opkg_opk_ustar_write_header(struct opkg_opk_ustar *ustar,
- struct opkg_opk_dirent *dirent, uint16_t mode,
- uid_t uid, const char *uname, gid_t gid, const char *gname,
- uint32_t devmajor, uint32_t devminor,
- uint64_t size, uint64_t mtime, char type, const char *linkname)
-{
- uint32_t chksum;
- size_t i;
- unsigned char *header_uc;
-
- memset(&ustar->header, 0, sizeof(ustar->header));
-
- if (opkg_opk_dirent_name_prefix(dirent, (type == 'd' ? 1 : 0),
- ustar->header.name,
- sizeof(ustar->header.name),
- ustar->header.prefix,
- sizeof(ustar->header.prefix)) != OPKG_OPK_OK) {
- return OPKG_OPK_ERROR;
- }
- /* POSIX doesn't say to NUL-terminate mode, uid, gid, size, or mtime.
- * GNU and BB tar accept values that aren't (but generate values that
- * ARE) NUL-terminated. But more importantly, opkg-lede (see file
- * libbb/unarchive.c) parses these with simple strtol() calls, without
- * even checking endptr, so we have to NUL-terminate these fields. Also
- * parsing these fields with strtol() are our very own
- * _opkg_opk_ustar_next() and opkg_opk_ustar_list() above. */
- sprintf(ustar->header.mode, "%07o", mode);
- sprintf(ustar->header.uid, "%07o", uid);
- sprintf(ustar->header.gid, "%07o", gid);
- sprintf(ustar->header.size, "%011o", size);
- sprintf(ustar->header.mtime, "%011o", mtime);
- sprintf(ustar->header.devmajor, "%07o", devmajor);
- sprintf(ustar->header.devminor, "%07o", devminor);
- switch (type) {
- case '-': /* Regular file */
- *ustar->header.typeflag = '0';
- break;
- case 'l': /* Symbolic link */
- *ustar->header.typeflag = '2';
- strncpy(ustar->header.linkname, linkname,
- sizeof(ustar->header.linkname));
- break;
- case 'c': /* Character special file */
- *ustar->header.typeflag = '3';
- break;
- case 'b': /* Block special file */
- *ustar->header.typeflag = '4';
- break;
- case 'd': /* Directory */
- *ustar->header.typeflag = '5';
- break;
- case 'p': /* FIFO special file */
- *ustar->header.typeflag = '6';
- break;
- default:
- return OPKG_OPK_ERROR; /* Unsupported */
- }
- strncpy(ustar->header.uname, uname, sizeof(ustar->header.uname));
- strncpy(ustar->header.gname, gname, sizeof(ustar->header.gname));
-
- /* In these fields POSIX says to write: But GNU and BB tar write: */
- strcpy(ustar->header.magic, "ustar"); /* "ustar " */
- memcpy(ustar->header.version, "00", 2); /* " \0" i.e. 0x20 0x00 */
- /* See files in BusyBox:
- * include/bb_archive.h
- * archival/chksum_and_xwrite_tar_header.c
- * archival/libarchive/get_header_tar.c
- * GNU and BB tar accept POSIX-conformant magic and version fields.
- * opkg-lede libbb/unarchive.c only checks first 5 bytes of magic and
- * ignores version. So conforming to POSIX seems safe.
- */
-#if OPKG_OPK_USTAR_BB_EXACT_HEADER_
- strcpy(ustar->header.magic, "ustar ");
-#endif
-
- chksum = 0;
- for (i = 0; i < sizeof(ustar->header.chksum); ++i) {
- ustar->header.chksum[i] = ' ';
- }
- header_uc = (unsigned char *) &ustar->header;
- for (i = 0; i < OPKG_OPK_USTAR_RECORD_SIZE; ++i) {
- chksum += header_uc[i];
- }
- /* See above about NUL-terminating and strtol() in opkg-lede. */
- /* Since commit 5661fe078eed752780b11f3f4fdd33bbd76a6c5e, BB tar has
- * filled chksum with "[6] digits, a null, then a space -- rather than
- * digits, followed by a null like the other fields...". The maxiumum
- * checksum is (255 x 512 = octal 377000), i.e. a maximum of 6 octal
- * digits, so it doesn't matter whether we use 6 or 7 digits. */
-#if OPKG_OPK_USTAR_BB_EXACT_HEADER_
- sprintf(ustar->header.chksum, "%06o", chksum);
-#else
- sprintf(ustar->header.chksum, "%07o", chksum);
-#endif
-
- if (opkg_opk_gzip_write(ustar->gzip, &ustar->header,
- OPKG_OPK_USTAR_RECORD_SIZE) != OPKG_OPK_OK) {
- return OPKG_OPK_ERROR;
- }
-
- ustar->data_size_remaining = size;
- return OPKG_OPK_OK;
-}
-
-int
-opkg_opk_ustar_get_buffer(struct opkg_opk_ustar *ustar, char **buffer,
- size_t *size)
-{
- *buffer = ustar->record;
- *size = sizeof(ustar->record);
- return OPKG_OPK_OK;
-}
-
-int
-opkg_opk_ustar_write_data(struct opkg_opk_ustar *ustar, size_t size)
-{
- /* Sanity check. */
- if (size > ustar->data_size_remaining ||
- size > OPKG_OPK_USTAR_RECORD_SIZE) {
- return OPKG_OPK_ERROR;
- }
-
- /* Zero out end of record. */
- memset(ustar->record + size, 0, OPKG_OPK_USTAR_RECORD_SIZE - size);
-
- /* Write to gzip stream. */
- if (opkg_opk_gzip_write(ustar->gzip, ustar->record,
- OPKG_OPK_USTAR_RECORD_SIZE) != OPKG_OPK_OK) {
- return OPKG_OPK_ERROR;
- }
-
- ustar->data_size_remaining -= size;
- return OPKG_OPK_OK;
-}
-
-int
-opkg_opk_ustar_write_trailer(struct opkg_opk_ustar *ustar)
-{
- memset(ustar->record, 0, OPKG_OPK_USTAR_RECORD_SIZE);
- if (opkg_opk_gzip_write(ustar->gzip, ustar->record,
- OPKG_OPK_USTAR_RECORD_SIZE) != OPKG_OPK_OK ||
- opkg_opk_gzip_write(ustar->gzip, ustar->record,
- OPKG_OPK_USTAR_RECORD_SIZE) != OPKG_OPK_OK)
- {
- return OPKG_OPK_ERROR;
- }
- return OPKG_OPK_OK;
-}
-
-void
-opkg_opk_ustar_free(struct opkg_opk_ustar *ustar)
-{
- free(ustar);
-}