diff options
| -rw-r--r-- | lib/tar/pax_header.c | 265 | 
1 files changed, 192 insertions, 73 deletions
diff --git a/lib/tar/pax_header.c b/lib/tar/pax_header.c index e62ffc4..71b849b 100644 --- a/lib/tar/pax_header.c +++ b/lib/tar/pax_header.c @@ -8,6 +8,134 @@  #include "internal.h" +static int pax_uid(tar_header_decoded_t *out, sqfs_u64 id) +{ +	out->sb.st_uid = id; +	return 0; +} + +static int pax_gid(tar_header_decoded_t *out, sqfs_u64 id) +{ +	out->sb.st_gid = id; +	return 0; +} + +static int pax_size(tar_header_decoded_t *out, sqfs_u64 size) +{ +	out->record_size = size; +	return 0; +} + +static int pax_mtime(tar_header_decoded_t *out, sqfs_s64 mtime) +{ +	out->mtime = mtime; +	return 0; +} + +static int pax_rsize(tar_header_decoded_t *out, sqfs_u64 size) +{ +	out->actual_size = size; +	return 0; +} + +static int pax_path(tar_header_decoded_t *out, char *path) +{ +	free(out->name); +	out->name = path; +	return 0; +} + +static int pax_slink(tar_header_decoded_t *out, char *path) +{ +	free(out->link_target); +	out->link_target = path; +	return 0; +} + +static int pax_xattr_schily(tar_header_decoded_t *out, +			    tar_xattr_t *xattr) +{ +	xattr->next = out->xattr; +	out->xattr = xattr; +	return 0; +} + +static int pax_xattr_libarchive(tar_header_decoded_t *out, +				tar_xattr_t *xattr) +{ +	urldecode(xattr->key); +	xattr->value_len = base64_decode(xattr->value, +					 (const char *)xattr->value, +					 xattr->value_len); +	xattr->next = out->xattr; +	out->xattr = xattr; +	return 0; +} + +enum { +	PAX_TYPE_SINT = 0, +	PAX_TYPE_UINT, +	PAX_TYPE_STRING, +	PAX_TYPE_PREFIXED_XATTR, +	PAX_TYPE_IGNORE, +}; + +static const struct pax_handler_t { +	const char *name; +	int flag; +	int type; +	union { +		int (*sint)(tar_header_decoded_t *out, sqfs_s64 sval); +		int (*uint)(tar_header_decoded_t *out, sqfs_u64 uval); +		int (*str)(tar_header_decoded_t *out, char *str); +		int (*xattr)(tar_header_decoded_t *out, tar_xattr_t *xattr); +	} cb; +} pax_fields[] = { +	{ "uid", PAX_UID, PAX_TYPE_UINT, { .uint = pax_uid } }, +	{ "gid", PAX_GID, PAX_TYPE_UINT, { .uint = pax_gid } }, +	{ "path", PAX_NAME, PAX_TYPE_STRING, { .str = pax_path } }, +	{ "size", PAX_SIZE, PAX_TYPE_UINT, { .uint = pax_size } }, +	{ "linkpath", PAX_SLINK_TARGET, PAX_TYPE_STRING, { .str = pax_slink } }, +	{ "mtime", PAX_MTIME, PAX_TYPE_SINT, { .sint = pax_mtime } }, +	{ "GNU.sparse.name", PAX_NAME, PAX_TYPE_STRING, { .str = pax_path } }, +	{ "GNU.sparse.size", PAX_SPARSE_SIZE, PAX_TYPE_UINT, +	  {.uint = pax_rsize} }, +	{ "GNU.sparse.realsize", PAX_SPARSE_SIZE, PAX_TYPE_UINT, +	  {.uint = pax_rsize} }, +	{ "GNU.sparse.major", PAX_SPARSE_GNU_1_X, PAX_TYPE_IGNORE, +	  { .str = NULL } }, +	{ "GNU.sparse.minor", PAX_SPARSE_GNU_1_X, PAX_TYPE_IGNORE, +	  { .str = NULL }}, +	{ "SCHILY.xattr", 0, PAX_TYPE_PREFIXED_XATTR, +	  { .xattr = pax_xattr_schily } }, +	{ "LIBARCHIVE.xattr", 0, PAX_TYPE_PREFIXED_XATTR, +	  { .xattr = pax_xattr_libarchive } }, +}; + +static const struct pax_handler_t *find_handler(const char *key) +{ +	size_t i, fieldlen; + +	for (i = 0; i < sizeof(pax_fields) / sizeof(pax_fields[0]); ++i) { +		if (pax_fields[i].type == PAX_TYPE_PREFIXED_XATTR) { +			fieldlen = strlen(pax_fields[i].name); + +			if (strncmp(key, pax_fields[i].name, fieldlen)) +				continue; + +			if (key[fieldlen] != '.') +				continue; + +			return pax_fields + i; +		} + +		if (!strcmp(key, pax_fields[i].name)) +			return pax_fields + i; +	} + +	return NULL; +} +  static tar_xattr_t *mkxattr(const char *key,  			    const char *value, size_t valuelen)  { @@ -30,13 +158,68 @@ static tar_xattr_t *mkxattr(const char *key,  	return xattr;  } +static int apply_handler(tar_header_decoded_t *out, +			 const struct pax_handler_t *field, const char *key, +			 const char *value, size_t valuelen) +{ +	tar_xattr_t *xattr; +	sqfs_s64 s64val; +	sqfs_u64 uval; +	char *copy; + +	switch (field->type) { +	case PAX_TYPE_SINT: +		if (value[0] == '-') { +			if (pax_read_decimal(value + 1, &uval)) +				return -1; +			s64val = -((sqfs_s64)uval); +		} else { +			if (pax_read_decimal(value, &uval)) +				return -1; +			s64val = (sqfs_s64)uval; +		} +		return field->cb.sint(out, s64val); +	case PAX_TYPE_UINT: +		if (pax_read_decimal(value, &uval)) +			return -1; +		return field->cb.uint(out, uval); +	case PAX_TYPE_STRING: +		copy = strdup(value); +		if (copy == NULL) { +			perror("processing pax header"); +			return -1; +		} +		if (field->cb.str(out, copy)) { +			free(copy); +			return -1; +		} +		break; +	case PAX_TYPE_PREFIXED_XATTR: +		xattr = mkxattr(key + strlen(field->name) + 1, +				value, valuelen); +		if (xattr == NULL) { +			perror("reading pax xattr field"); +			return -1; +		} +		if (field->cb.xattr(out, xattr)) { +			free(xattr); +			return -1; +		} +		break; +	default: +		break; +	} + +	return 0; +} +  int read_pax_header(istream_t *fp, sqfs_u64 entsize, unsigned int *set_by_pax,  		    tar_header_decoded_t *out)  {  	char *buffer, *line, *key, *ptr, *value, *end;  	sparse_map_t *sparse_last = NULL, *sparse; -	sqfs_u64 field, offset = 0, num_bytes = 0; -	tar_xattr_t *xattr; +	sqfs_u64 offset = 0, num_bytes = 0; +	const struct pax_handler_t *field;  	long len;  	buffer = record_to_memory(fp, entsize); @@ -72,49 +255,15 @@ int read_pax_header(istream_t *fp, sqfs_u64 entsize, unsigned int *set_by_pax,  		*(ptr++) = '\0';  		value = ptr; -		if (!strcmp(key, "uid")) { -			if (pax_read_decimal(value, &field)) -				goto fail; -			out->sb.st_uid = field; -			*set_by_pax |= PAX_UID; -		} else if (!strcmp(key, "gid")) { -			if (pax_read_decimal(value, &field)) -				goto fail; -			out->sb.st_gid = field; -			*set_by_pax |= PAX_GID; -		} else if (!strcmp(key, "path")) { -			free(out->name); -			out->name = strdup(value); -			if (out->name == NULL) -				goto fail_errno; -			*set_by_pax |= PAX_NAME; -		} else if (!strcmp(key, "size")) { -			if (pax_read_decimal(value, &out->record_size)) +		field = find_handler(key); + +		if (field != NULL) { +			if (apply_handler(out, field, key, value, +					  len - (value - line) - 1)) {  				goto fail; -			*set_by_pax |= PAX_SIZE; -		} else if (!strcmp(key, "linkpath")) { -			free(out->link_target); -			out->link_target = strdup(value); -			if (out->link_target == NULL) -				goto fail_errno; -			*set_by_pax |= PAX_SLINK_TARGET; -		} else if (!strcmp(key, "mtime")) { -			if (value[0] == '-') { -				if (pax_read_decimal(value + 1, &field)) -					goto fail; -				out->mtime = -((sqfs_s64)field); -			} else { -				if (pax_read_decimal(value, &field)) -					goto fail; -				out->mtime = field;  			} -			*set_by_pax |= PAX_MTIME; -		} else if (!strcmp(key, "GNU.sparse.name")) { -			free(out->name); -			out->name = strdup(value); -			if (out->name == NULL) -				goto fail_errno; -			*set_by_pax |= PAX_NAME; + +			*set_by_pax |= field->flag;  		} else if (!strcmp(key, "GNU.sparse.map")) {  			free_sparse_list(out->sparse);  			sparse_last = NULL; @@ -122,17 +271,6 @@ int read_pax_header(istream_t *fp, sqfs_u64 entsize, unsigned int *set_by_pax,  			out->sparse = read_sparse_map(value);  			if (out->sparse == NULL)  				goto fail; -		} else if (!strcmp(key, "GNU.sparse.size")) { -			if (pax_read_decimal(value, &out->actual_size)) -				goto fail; -			*set_by_pax |= PAX_SPARSE_SIZE; -		} else if (!strcmp(key, "GNU.sparse.realsize")) { -			if (pax_read_decimal(value, &out->actual_size)) -				goto fail; -			*set_by_pax |= PAX_SPARSE_SIZE; -		} else if (!strcmp(key, "GNU.sparse.major") || -			   !strcmp(key, "GNU.sparse.minor")) { -			*set_by_pax |= PAX_SPARSE_GNU_1_X;  		} else if (!strcmp(key, "GNU.sparse.offset")) {  			if (pax_read_decimal(value, &offset))  				goto fail; @@ -151,25 +289,6 @@ int read_pax_header(istream_t *fp, sqfs_u64 entsize, unsigned int *set_by_pax,  				sparse_last->next = sparse;  				sparse_last = sparse;  			} -		} else if (!strncmp(key, "SCHILY.xattr.", 13)) { -			xattr = mkxattr(key + 13, value, -					len - (value - line) - 1); -			if (xattr == NULL) -				goto fail_errno; - -			xattr->next = out->xattr; -			out->xattr = xattr; -		} else if (!strncmp(key, "LIBARCHIVE.xattr.", 17)) { -			xattr = mkxattr(key + 17, value, strlen(value)); -			if (xattr == NULL) -				goto fail_errno; - -			urldecode(xattr->key); -			xattr->value_len = base64_decode(xattr->value, value, -							 xattr->value_len); - -			xattr->next = out->xattr; -			out->xattr = xattr;  		}  	}  | 
