diff options
Diffstat (limited to 'src/ustar.c')
-rw-r--r-- | src/ustar.c | 446 |
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); -} |