summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPatrick McDermott <patrick.mcdermott@libiquity.com>2023-07-07 04:52:21 (EDT)
committer Patrick McDermott <patrick.mcdermott@libiquity.com>2023-07-30 22:43:45 (EDT)
commit54207c7bf2cea3834a081332900c25b8073a6c72 (patch)
tree102f7dcf386866c5ff3847d903fdfa5bca78467b
parent8042464eb99542c9421a98cce16164a55d0766ac (diff)
ustar: Support reading GNU long name/link records
-rw-r--r--opkg-opk/ustar.c131
-rw-r--r--opkg-opk/ustar.h4
2 files changed, 105 insertions, 30 deletions
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;