/* * 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 . */ #include #include #include #include #include "defs.h" #include "gzip.h" #include "ustar.h" #define OPKG_OPK_USTAR_NUM_BASE_ 8 struct _opkg_opk_ustar_header { unsigned char name [100]; unsigned char mode [8]; unsigned char uid [8]; unsigned char gid [8]; unsigned char size [12]; unsigned char mtime [12]; unsigned char chksum [8]; unsigned char typeflag [1]; unsigned char linkname [100]; unsigned char magic [6]; unsigned char version [2]; unsigned char uname [32]; unsigned char gname [32]; unsigned char devmajor [8]; unsigned char devminor [8]; unsigned char prefix [155]; unsigned char padding [12]; } __attribute__((__packed__)); struct opkg_opk_ustar { struct opkg_opk_gzip *gzip; int32_t data_size_remaining; unsigned char read_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, struct _opkg_opk_ustar_header *header) { static unsigned char record[OPKG_OPK_USTAR_RECORD_SIZE]; char *size_end; switch (opkg_opk_gzip_read(ustar->gzip, header)) { case OPKG_OPK_OK: break; case OPKG_OPK_END: case OPKG_OPK_ERROR: return OPKG_OPK_ERROR; } memset(record, 0, OPKG_OPK_USTAR_RECORD_SIZE); if (memcmp(header, record, OPKG_OPK_USTAR_RECORD_SIZE) == 0) { return OPKG_OPK_END; } if (memcmp(header->magic, "ustar", strlen("ustar")) != 0) { return OPKG_OPK_ERROR; } ustar->data_size_remaining = strtol((char *) header->size, &size_end, OPKG_OPK_USTAR_NUM_BASE_); if (*size_end != '\0') { return OPKG_OPK_ERROR; } return OPKG_OPK_OK; } int opkg_opk_ustar_list(struct opkg_opk_ustar *ustar, struct opkg_opk_ustar_member *member) { static struct _opkg_opk_ustar_header header; int ret; if ((ret =_opkg_opk_ustar_next(ustar, &header)) != OPKG_OPK_OK) { return ret; /* Error or end of archive */ } if (header.prefix[0] != '\0') { sprintf(member->name, "%s/%s", header.prefix, header.name); } else { memcpy(member->name, header.name, sizeof(header.name)); member->name[sizeof(header.name)] = '\0'; } while (ustar->data_size_remaining > 0) { if (opkg_opk_ustar_read(ustar, NULL, NULL) == OPKG_OPK_ERROR) { return OPKG_OPK_ERROR; } } return OPKG_OPK_OK; /* Possibly more members in archive */ } int opkg_opk_ustar_seek(struct opkg_opk_ustar *ustar, const char *member) { static struct _opkg_opk_ustar_header header; static unsigned char name[OPKG_OPK_USTAR_NAME_MAX_LEN]; for (;;) { if (_opkg_opk_ustar_next(ustar, &header) != OPKG_OPK_OK) { return OPKG_OPK_ERROR; /* Error or end (not found) */ } if (header.prefix[0] != '\0') { sprintf((char *) name, "%s/%s", header.prefix, header.name); } else { memcpy(name, header.name, sizeof(header.name)); name[sizeof(header.name)] = '\0'; } if (strcmp((char *) name, member) == 0) { return OPKG_OPK_OK; /* Member found */ } while (ustar->data_size_remaining > 0) { if (opkg_opk_ustar_read(ustar, NULL, NULL) == OPKG_OPK_ERROR) { return OPKG_OPK_ERROR; } } } return OPKG_OPK_ERROR; /* Member not found */ } 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; } switch (opkg_opk_gzip_read(ustar->gzip, ustar->read_record)) { case OPKG_OPK_OK: break; case OPKG_OPK_END: case OPKG_OPK_ERROR: return OPKG_OPK_ERROR; } if (buffer != NULL) { *buffer = ustar->read_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; } void opkg_opk_ustar_free(struct opkg_opk_ustar *ustar) { free(ustar); }