From 54207c7bf2cea3834a081332900c25b8073a6c72 Mon Sep 17 00:00:00 2001 From: Patrick McDermott Date: Fri, 07 Jul 2023 04:52:21 -0400 Subject: ustar: Support reading GNU long name/link records --- diff --git a/opkg-opk/ustar.c b/opkg-opk/ustar.c index 5a202c7..67236cc 100644 --- a/opkg-opk/ustar.c +++ b/opkg-opk/ustar.c @@ -74,7 +74,32 @@ opkg_opk_ustar_init(struct opkg_opk_gzip *gzip) } static int -_opkg_opk_ustar_next(struct opkg_opk_ustar *ustar) +_opkg_opk_ustar_read_gnu_long_records(struct opkg_opk_ustar *ustar, + char **gnu_long_buf) +{ + char *gnu_long_buf_sub; + char *read_buf; + size_t size; + + gnu_long_buf_sub = *gnu_long_buf = malloc(ustar->data_size_remaining); + if (*gnu_long_buf == NULL) { + return OPKG_OPK_ERROR; + } + + while (ustar->data_size_remaining > 0) { + if (opkg_opk_ustar_read(ustar, &read_buf, &size) == + OPKG_OPK_ERROR) { + return OPKG_OPK_ERROR; + } + memcpy(gnu_long_buf_sub, read_buf, size); + gnu_long_buf_sub += size; + } + + return OPKG_OPK_OK; +} + +static int +_opkg_opk_ustar_next(struct opkg_opk_ustar *ustar, char **name, char **linkname) { char *end; uint32_t chksum_got; @@ -82,6 +107,11 @@ _opkg_opk_ustar_next(struct opkg_opk_ustar *ustar) size_t i; unsigned char *header_uc; + *name = NULL; + *linkname = NULL; + + again: /* Go to here if a GNU long name/link is read. */ + /* 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) { @@ -131,7 +161,48 @@ _opkg_opk_ustar_next(struct opkg_opk_ustar *ustar) return OPKG_OPK_ERROR; } - /* Depending on type, get size. */ + /* Fill (possibly long) name and/or linkname buffers for caller. */ + if (*name == NULL) { + /* No GNU long name */ + if (ustar->header.prefix[0] != '\0') { + *name = malloc(sizeof(ustar->header.prefix) + + sizeof(ustar->header.name) + 1); + if (*name == NULL) { + if (*linkname != NULL) { + free(*linkname); + } + return OPKG_OPK_ERROR; + } + sprintf(*name, "%s/%s", ustar->header.prefix, + ustar->header.name); + } else { + *name = malloc(sizeof(ustar->header.name) + 1); + if (*name == NULL) { + if (*linkname != NULL) { + free(*linkname); + } + return OPKG_OPK_ERROR; + } + /* Use memcpy() because ustar->header.name may not be + * NUL-terminated. */ + memcpy(*name, ustar->header.name, + sizeof(ustar->header.name)); + (*name)[sizeof(ustar->header.name)] = '\0'; + } + } + if (*linkname == NULL && *ustar->header.typeflag == '2') { + /* Symbolic link without GNU long link name */ + *linkname = malloc(sizeof(ustar->header.linkname) + 1); + if (*linkname == NULL) { + free(*name); + return OPKG_OPK_ERROR; + } + strncpy(*linkname, ustar->header.linkname, + sizeof(ustar->header.linkname)); + (*linkname)[sizeof(ustar->header.linkname)] = '\0'; + } + + /* Depending on type, get size or GNU long name/link records. */ switch (*ustar->header.typeflag) { case '0': /* Regular file */ case '7': /* High-performance or regular file */ @@ -153,6 +224,19 @@ _opkg_opk_ustar_next(struct opkg_opk_ustar *ustar) ustar->data_size_remaining = 0; break; case '1': /* Link */ + return OPKG_OPK_ERROR; /* Unsupported */ + case 'L': /* GNU long name */ + if (_opkg_opk_ustar_read_gnu_long_records(ustar, + name) != OPKG_OPK_OK) { + return OPKG_OPK_ERROR; + } + goto again; + case 'K': /* GNU long link */ + if (_opkg_opk_ustar_read_gnu_long_records(ustar, + linkname) != OPKG_OPK_OK) { + return OPKG_OPK_ERROR; + } + goto again; default: /* Reserved */ return OPKG_OPK_ERROR; /* Unsupported */ } @@ -167,42 +251,35 @@ opkg_opk_ustar_list(struct opkg_opk_ustar *ustar, 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'; + /* Get next header record, if any. */ + if ((ret = _opkg_opk_ustar_next(ustar, &((*member)->name), + &((*member)->linkname))) != OPKG_OPK_OK) + { + free(*member); + return ret; /* Error or end of archive */ } - /* Assumes mode and mtime are NUL-terminated. Not required by POSIX, + + /* Set mode, size, mtime, type, uname, gname, devmajor, and devminor. + * Name and linkname are already set by _opkg_opk_ustar_next(). + * 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); + opkg_opk_ustar_member_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); + opkg_opk_ustar_member_free(*member); return OPKG_OPK_ERROR; } switch (*ustar->header.typeflag) { @@ -212,10 +289,6 @@ opkg_opk_ustar_list(struct opkg_opk_ustar *ustar, 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'; @@ -231,7 +304,7 @@ opkg_opk_ustar_list(struct opkg_opk_ustar *ustar, break; case '1': /* Link */ default: /* Reserved */ - free(*member); + opkg_opk_ustar_member_free(*member); return OPKG_OPK_ERROR; /* Unsupported */ } strncpy((*member)->uname, ustar->header.uname, @@ -241,13 +314,13 @@ opkg_opk_ustar_list(struct opkg_opk_ustar *ustar, (*member)->devmajor = strtol(ustar->header.devmajor, &end, OPKG_OPK_USTAR_NUM_BASE_); if (*end != '\0') { - free(*member); + opkg_opk_ustar_member_free(*member); return OPKG_OPK_ERROR; } (*member)->devminor = strtol(ustar->header.devminor, &end, OPKG_OPK_USTAR_NUM_BASE_); if (*end != '\0') { - free(*member); + opkg_opk_ustar_member_free(*member); return OPKG_OPK_ERROR; } @@ -442,6 +515,8 @@ opkg_opk_ustar_write_trailer(struct opkg_opk_ustar *ustar) void opkg_opk_ustar_member_free(struct opkg_opk_ustar_member *member) { + free(member->name); + free(member->linkname); free(member); } diff --git a/opkg-opk/ustar.h b/opkg-opk/ustar.h index 234964c..9dd5194 100644 --- a/opkg-opk/ustar.h +++ b/opkg-opk/ustar.h @@ -32,12 +32,12 @@ struct opkg_opk_ustar; struct opkg_opk_ustar_member { - char name [OPKG_OPK_USTAR_NAME_SIZE]; + char *name; uint16_t mode; uint64_t size; uint64_t mtime; char type; - char linkname[OPKG_OPK_USTAR_LINKNAME_SIZE]; + char *linkname; char uname [32]; char gname [32]; uint32_t devmajor; -- cgit v0.9.1