extract: new applet to extract v2 packages
parent
7ce4cc4b73
commit
91cb2514e4
89
src/adb.c
89
src/adb.c
|
@ -44,7 +44,6 @@ int adb_free(struct adb *db)
|
|||
} else {
|
||||
struct adb_w_bucket *bucket, *nxt;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < db->num_buckets; i++)
|
||||
list_for_each_entry_safe(bucket, nxt, &db->bucket[i], node)
|
||||
free(bucket);
|
||||
|
@ -135,6 +134,79 @@ err:
|
|||
return r;
|
||||
}
|
||||
|
||||
int adb_m_stream(struct adb *db, struct apk_istream *is, uint32_t expected_schema,
|
||||
struct apk_trust *t, int (*datacb)(struct adb *, size_t, struct apk_istream *))
|
||||
{
|
||||
struct adb_verify_ctx vfy = {};
|
||||
struct adb_block blk;
|
||||
struct apk_segment_istream seg;
|
||||
void *sig;
|
||||
int r, block_no = 0;
|
||||
int trusted = t ? 0 : 1;
|
||||
size_t sz;
|
||||
|
||||
if (IS_ERR(is)) return PTR_ERR(is);
|
||||
if ((r = apk_istream_read(is, &db->hdr, sizeof db->hdr)) != sizeof db->hdr) goto err;
|
||||
if (db->hdr.magic != htole32(ADB_FORMAT_MAGIC)) goto bad_msg;
|
||||
|
||||
do {
|
||||
r = apk_istream_read(is, &blk, sizeof blk);
|
||||
if (r == 0) {
|
||||
if (!trusted) r = -ENOKEY;
|
||||
else if (!db->adb.ptr) r = -ENOMSG;
|
||||
goto done;
|
||||
}
|
||||
if (r < 0 || r != sizeof blk) goto err;
|
||||
|
||||
if ((block_no++ == 0) != (adb_block_type(&blk) == ADB_BLOCK_ADB))
|
||||
goto bad_msg;
|
||||
|
||||
sz = adb_block_size(&blk) - sizeof blk;
|
||||
switch (adb_block_type(&blk)) {
|
||||
case ADB_BLOCK_ADB:
|
||||
if (!APK_BLOB_IS_NULL(db->adb)) goto bad_msg;
|
||||
db->adb.ptr = malloc(sz);
|
||||
db->adb.len = adb_block_length(&blk);
|
||||
if ((r = apk_istream_read(is, db->adb.ptr, sz)) != sz) goto err;
|
||||
break;
|
||||
case ADB_BLOCK_SIG:
|
||||
if (APK_BLOB_IS_NULL(db->adb)) goto bad_msg;
|
||||
sig = apk_istream_get(is, sz);
|
||||
if (IS_ERR(sig)) {
|
||||
r = PTR_ERR(sig);
|
||||
goto err;
|
||||
}
|
||||
if (!trusted &&
|
||||
adb_trust_verify_signature(t, db, &vfy, APK_BLOB_PTR_LEN(sig, adb_block_length(&blk))) == 0)
|
||||
trusted = 1;
|
||||
break;
|
||||
case ADB_BLOCK_DATA:
|
||||
if (APK_BLOB_IS_NULL(db->adb)) goto bad_msg;
|
||||
if (!trusted) {
|
||||
r = -ENOKEY;
|
||||
goto err;
|
||||
}
|
||||
r = datacb(db, adb_block_length(&blk),
|
||||
apk_istream_segment(&seg, is, adb_block_size(&blk) - sizeof blk, 0));
|
||||
if (r < 0) goto err;
|
||||
if (seg.bytes_left) {
|
||||
r = apk_istream_read(is, NULL, seg.bytes_left);
|
||||
if (r < 0) goto err;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
goto bad_msg;
|
||||
}
|
||||
} while (1);
|
||||
bad_msg:
|
||||
r = -EBADMSG;
|
||||
err:
|
||||
if (r >= 0) r = -EBADMSG;
|
||||
done:
|
||||
apk_istream_close(is);
|
||||
return r;
|
||||
}
|
||||
|
||||
int adb_w_init_dynamic(struct adb *db, uint32_t schema, void *buckets, size_t num_buckets)
|
||||
{
|
||||
size_t i;
|
||||
|
@ -947,7 +1019,7 @@ int adb_trust_write_signatures(struct apk_trust *trust, struct adb *db, struct a
|
|||
union {
|
||||
struct adb_sign_hdr hdr;
|
||||
struct adb_sign_v0 v0;
|
||||
unsigned char buf[8192];
|
||||
unsigned char buf[ADB_MAX_SIGNATURE_LEN];
|
||||
} sig;
|
||||
struct apk_trust_key *tkey;
|
||||
apk_blob_t md;
|
||||
|
@ -1023,7 +1095,7 @@ int adb_c_xfrm(struct adb_xfrm *x, int (*cb)(struct adb_xfrm *, struct adb_block
|
|||
size_t sz;
|
||||
|
||||
r = apk_istream_read(x->is, &x->db.hdr, sizeof x->db.hdr);
|
||||
if (r < 0) goto err;
|
||||
if (r != sizeof x->db.hdr) goto err;
|
||||
|
||||
if (x->db.hdr.magic != htole32(ADB_FORMAT_MAGIC)) goto bad_msg;
|
||||
r = apk_ostream_write(x->os, &x->db.hdr, sizeof x->db.hdr);
|
||||
|
@ -1031,9 +1103,9 @@ int adb_c_xfrm(struct adb_xfrm *x, int (*cb)(struct adb_xfrm *, struct adb_block
|
|||
|
||||
do {
|
||||
r = apk_istream_read(x->is, &blk, sizeof blk);
|
||||
if (r <= 0) {
|
||||
if (r == 0) r = cb(x, NULL, NULL);
|
||||
goto err;
|
||||
if (r != sizeof blk) {
|
||||
if (r != 0) goto err;
|
||||
return cb(x, NULL, NULL);
|
||||
}
|
||||
|
||||
if ((block_no++ == 0) != (adb_block_type(&blk) == ADB_BLOCK_ADB)) goto bad_msg;
|
||||
|
@ -1048,13 +1120,14 @@ int adb_c_xfrm(struct adb_xfrm *x, int (*cb)(struct adb_xfrm *, struct adb_block
|
|||
r = apk_stream_copy(x->is, x->os, sz, 0, 0, 0);
|
||||
if (r < 0) goto err;
|
||||
} else if (seg.bytes_left > 0) {
|
||||
r = apk_istream_read(x->is, NULL, sz - seg.bytes_left);
|
||||
if (r < 0) goto err;
|
||||
r = apk_istream_read(x->is, NULL, seg.bytes_left);
|
||||
if (r != seg.bytes_left) goto err;
|
||||
}
|
||||
} while (1);
|
||||
bad_msg:
|
||||
r = -EBADMSG;
|
||||
err:
|
||||
if (r >= 0) r = -EBADMSG;
|
||||
apk_ostream_cancel(x->os, r);
|
||||
return r;
|
||||
}
|
||||
|
|
|
@ -74,6 +74,8 @@ static inline apk_blob_t adb_block_blob(struct adb_block *b) {
|
|||
return APK_BLOB_PTR_LEN(adb_block_payload(b), adb_block_length(b));
|
||||
}
|
||||
|
||||
#define ADB_MAX_SIGNATURE_LEN 256
|
||||
|
||||
struct adb_sign_hdr {
|
||||
uint8_t sign_ver, hash_alg;
|
||||
};
|
||||
|
@ -161,6 +163,7 @@ void adb_reset(struct adb *);
|
|||
|
||||
int adb_m_blob(struct adb *, apk_blob_t, struct apk_trust *);
|
||||
int adb_m_map(struct adb *, int fd, uint32_t expected_schema, struct apk_trust *);
|
||||
int adb_m_stream(struct adb *db, struct apk_istream *is, uint32_t expected_schema, struct apk_trust *trust, int (*datacb)(struct adb *, size_t, struct apk_istream *));
|
||||
#define adb_w_init_alloca(db, schema, num_buckets) adb_w_init_dynamic(db, schema, alloca(sizeof(struct list_head[num_buckets])), num_buckets)
|
||||
#define adb_w_init_tmp(db, size) adb_w_init_static(db, alloca(size), size)
|
||||
int adb_w_init_dynamic(struct adb *db, uint32_t schema, void *buckets, size_t num_buckets);
|
||||
|
|
|
@ -73,6 +73,11 @@
|
|||
#define ADBI_PKG_PASSWD 0x05
|
||||
#define ADBI_PKG_MAX 0x06
|
||||
|
||||
struct adb_data_package {
|
||||
uint32_t path_idx;
|
||||
uint32_t file_idx;
|
||||
};
|
||||
|
||||
/* Index */
|
||||
#define ADBI_NDX_DESCRIPTION 0x01
|
||||
#define ADBI_NDX_PACKAGES 0x02
|
||||
|
|
|
@ -15,12 +15,6 @@
|
|||
#include "apk_print.h"
|
||||
#include "apk_io.h"
|
||||
|
||||
#define APK_EXTRACTF_NO_CHOWN 0x0001
|
||||
|
||||
typedef int (*apk_archive_entry_parser)(void *ctx,
|
||||
const struct apk_file_info *ae,
|
||||
struct apk_istream *istream);
|
||||
|
||||
int apk_tar_parse(struct apk_istream *,
|
||||
apk_archive_entry_parser parser, void *ctx,
|
||||
struct apk_id_cache *);
|
||||
|
@ -28,10 +22,13 @@ int apk_tar_write_entry(struct apk_ostream *, const struct apk_file_info *ae,
|
|||
const char *data);
|
||||
int apk_tar_write_padding(struct apk_ostream *, const struct apk_file_info *ae);
|
||||
|
||||
#define APK_EXTRACTF_NO_CHOWN 0x0001
|
||||
#define APK_EXTRACTF_NO_OVERWRITE 0x0002
|
||||
|
||||
int apk_archive_entry_extract(int atfd, const struct apk_file_info *ae,
|
||||
const char *extract_name, const char *hardlink_name,
|
||||
struct apk_istream *is,
|
||||
apk_progress_cb cb, void *cb_ctx,
|
||||
apk_progress_cb cb, void *cb_ctx, struct apk_digest_ctx *dctx,
|
||||
unsigned int extract_flags,
|
||||
struct apk_out *out);
|
||||
|
||||
|
|
|
@ -52,21 +52,17 @@ static inline const EVP_MD *apk_digest_alg_to_evp(uint8_t alg) {
|
|||
}
|
||||
}
|
||||
|
||||
static inline int apk_digest_alg_len(uint8_t alg) {
|
||||
switch (alg) {
|
||||
case APK_DIGEST_MD5: return 16;
|
||||
case APK_DIGEST_SHA1: return 20;
|
||||
case APK_DIGEST_SHA256: return 32;
|
||||
case APK_DIGEST_SHA512: return 64;
|
||||
default:
|
||||
assert(alg);
|
||||
return 0;
|
||||
}
|
||||
int apk_digest_alg_len(uint8_t alg);
|
||||
uint8_t apk_digest_alg_by_len(int len);
|
||||
|
||||
static inline int apk_digest_cmp(struct apk_digest *a, struct apk_digest *b) {
|
||||
if (a->alg != b->alg) return b->alg - a->alg;
|
||||
return memcmp(a->data, b->data, a->len);
|
||||
}
|
||||
|
||||
static inline void apk_digest_reset(struct apk_digest *d) {
|
||||
d->len = 0;
|
||||
d->alg = APK_DIGEST_NONE;
|
||||
d->len = 0;
|
||||
}
|
||||
|
||||
static inline void apk_digest_set(struct apk_digest *d, uint8_t alg) {
|
||||
|
@ -114,6 +110,7 @@ static inline int apk_digest_ctx_final(struct apk_digest_ctx *dctx, struct apk_d
|
|||
}
|
||||
|
||||
#include "apk_blob.h"
|
||||
uint8_t apk_digest_from_blob(struct apk_digest *d, apk_blob_t b);
|
||||
static inline int apk_digest_cmp_csum(const struct apk_digest *d, const struct apk_checksum *csum)
|
||||
{
|
||||
return apk_blob_compare(APK_DIGEST_BLOB(*d), APK_BLOB_CSUM(*csum));
|
||||
|
|
20
src/apk_io.h
20
src/apk_io.h
|
@ -41,10 +41,10 @@ struct apk_file_meta {
|
|||
void apk_file_meta_to_fd(int fd, struct apk_file_meta *meta);
|
||||
|
||||
struct apk_file_info {
|
||||
char *name;
|
||||
char *link_target;
|
||||
char *uname;
|
||||
char *gname;
|
||||
const char *name;
|
||||
const char *link_target;
|
||||
const char *uname;
|
||||
const char *gname;
|
||||
off_t size;
|
||||
uid_t uid;
|
||||
gid_t gid;
|
||||
|
@ -77,6 +77,10 @@ struct apk_istream {
|
|||
const struct apk_istream_ops *ops;
|
||||
};
|
||||
|
||||
typedef int (*apk_archive_entry_parser)(void *ctx,
|
||||
const struct apk_file_info *ae,
|
||||
struct apk_istream *istream);
|
||||
|
||||
#define APK_IO_ALL ((size_t)-1)
|
||||
|
||||
#define APK_ISTREAM_FORCE_REFRESH ((time_t) -1)
|
||||
|
@ -87,12 +91,12 @@ struct apk_istream *apk_istream_from_fd(int fd);
|
|||
struct apk_istream *apk_istream_from_fd_url_if_modified(int atfd, const char *url, time_t since);
|
||||
static inline int apk_istream_error(struct apk_istream *is, int err) { if (!is->err) is->err = err; return err; }
|
||||
ssize_t apk_istream_read(struct apk_istream *is, void *ptr, size_t size);
|
||||
apk_blob_t apk_istream_get(struct apk_istream *is, size_t len);
|
||||
void *apk_istream_get(struct apk_istream *is, size_t len);
|
||||
apk_blob_t apk_istream_get_max(struct apk_istream *is, size_t size);
|
||||
apk_blob_t apk_istream_get_delim(struct apk_istream *is, apk_blob_t token);
|
||||
static inline apk_blob_t apk_istream_get_all(struct apk_istream *is) { return apk_istream_get_max(is, APK_IO_ALL); }
|
||||
ssize_t apk_istream_splice(struct apk_istream *is, int fd, size_t size,
|
||||
apk_progress_cb cb, void *cb_ctx);
|
||||
apk_progress_cb cb, void *cb_ctx, struct apk_digest_ctx *dctx);
|
||||
ssize_t apk_stream_copy(struct apk_istream *is, struct apk_ostream *os, size_t size,
|
||||
apk_progress_cb cb, void *cb_ctx, struct apk_digest_ctx *dctx);
|
||||
|
||||
|
@ -172,9 +176,9 @@ int apk_blob_to_file(int atfd, const char *file, apk_blob_t b, unsigned int flag
|
|||
|
||||
#define APK_FI_NOFOLLOW 0x80000000
|
||||
#define APK_FI_XATTR_DIGEST(x) (((x) & 0xff) << 8)
|
||||
#define APK_FI_XATTR_CSUM(x) APK_FI_XATTR_DIGEST(apk_digest_alg_from_csum(x))
|
||||
#define APK_FI_XATTR_CSUM(x) APK_FI_XATTR_DIGEST(apk_digest_alg_by_len(x))
|
||||
#define APK_FI_DIGEST(x) (((x) & 0xff))
|
||||
#define APK_FI_CSUM(x) APK_FI_DIGEST(apk_digest_alg_from_csum(x))
|
||||
#define APK_FI_CSUM(x) APK_FI_DIGEST(apk_digest_alg_by_len(x))
|
||||
int apk_fileinfo_get(int atfd, const char *filename, unsigned int flags,
|
||||
struct apk_file_info *fi, struct apk_atom_pool *atoms);
|
||||
void apk_fileinfo_hash_xattr(struct apk_file_info *fi, uint8_t alg);
|
||||
|
|
|
@ -0,0 +1,239 @@
|
|||
/* extract.c - Alpine Package Keeper (APK)
|
||||
*
|
||||
* Copyright (C) 2008-2021 Timo Teräs <timo.teras@iki.fi>
|
||||
* All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-only
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "apk_applet.h"
|
||||
#include "apk_print.h"
|
||||
#include "apk_adb.h"
|
||||
#include "apk_pathbuilder.h"
|
||||
|
||||
struct extract_ctx {
|
||||
const char *destination;
|
||||
unsigned int extract_flags;
|
||||
|
||||
struct apk_ctx *ac;
|
||||
struct adb db;
|
||||
int root_fd;
|
||||
|
||||
struct adb_obj pkg, paths, path, files, file;
|
||||
unsigned int cur_path, cur_file;
|
||||
|
||||
struct apk_pathbuilder pb;
|
||||
};
|
||||
|
||||
|
||||
#define EXTRACT_OPTIONS(OPT) \
|
||||
OPT(OPT_EXTRACT_destination, APK_OPT_ARG "destination") \
|
||||
OPT(OPT_EXTRACT_no_chown, "no-chown")
|
||||
|
||||
APK_OPT_APPLET(option_desc, EXTRACT_OPTIONS);
|
||||
|
||||
static int option_parse_applet(void *pctx, struct apk_ctx *ac, int opt, const char *optarg)
|
||||
{
|
||||
struct extract_ctx *ctx = (struct extract_ctx *) pctx;
|
||||
|
||||
switch (opt) {
|
||||
case OPT_EXTRACT_destination:
|
||||
ctx->destination = optarg;
|
||||
break;
|
||||
case OPT_EXTRACT_no_chown:
|
||||
ctx->extract_flags |= APK_EXTRACTF_NO_CHOWN;
|
||||
break;
|
||||
default:
|
||||
return -ENOTSUP;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct apk_option_group optgroup_applet = {
|
||||
.desc = option_desc,
|
||||
.parse = option_parse_applet,
|
||||
};
|
||||
|
||||
static void apk_extract_acl(struct apk_file_info *fi, struct adb_obj *o, struct apk_id_cache *idc)
|
||||
{
|
||||
fi->mode = adb_ro_int(o, ADBI_ACL_MODE);
|
||||
fi->uid = apk_id_cache_resolve_uid(idc, adb_ro_blob(o, ADBI_ACL_USER), 65534);
|
||||
fi->gid = apk_id_cache_resolve_gid(idc, adb_ro_blob(o, ADBI_ACL_GROUP), 65534);
|
||||
}
|
||||
|
||||
static int apk_extract_file(struct extract_ctx *ctx, off_t sz, struct apk_istream *is)
|
||||
{
|
||||
struct apk_ctx *ac = ctx->ac;
|
||||
struct apk_out *out = &ac->out;
|
||||
struct apk_file_info fi = {
|
||||
.name = apk_pathbuilder_cstr(&ctx->pb),
|
||||
.size = adb_ro_int(&ctx->file, ADBI_FI_SIZE),
|
||||
.mtime = adb_ro_int(&ctx->file, ADBI_FI_MTIME),
|
||||
};
|
||||
struct adb_obj acl;
|
||||
struct apk_digest_ctx dctx;
|
||||
struct apk_digest d;
|
||||
int r;
|
||||
|
||||
apk_digest_from_blob(&fi.digest, adb_ro_blob(&ctx->file, ADBI_FI_HASHES));
|
||||
if (fi.digest.alg == APK_DIGEST_NONE) return -EAPKFORMAT;
|
||||
apk_extract_acl(&fi, adb_ro_obj(&ctx->file, ADBI_FI_ACL, &acl), apk_ctx_get_id_cache(ctx->ac));
|
||||
fi.mode |= S_IFREG;
|
||||
|
||||
apk_digest_ctx_init(&dctx, fi.digest.alg);
|
||||
r = apk_archive_entry_extract(
|
||||
ctx->root_fd, &fi, 0, 0, is, 0, 0, &dctx,
|
||||
ctx->extract_flags, out);
|
||||
apk_digest_ctx_final(&dctx, &d);
|
||||
apk_digest_ctx_free(&dctx);
|
||||
if (r != 0) return r;
|
||||
if (apk_digest_cmp(&fi.digest, &d) != 0) return -EAPKDBFORMAT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int apk_extract_directory(struct extract_ctx *ctx)
|
||||
{
|
||||
struct apk_ctx *ac = ctx->ac;
|
||||
struct apk_out *out = &ac->out;
|
||||
struct apk_file_info fi = {
|
||||
.name = apk_pathbuilder_cstr(&ctx->pb),
|
||||
};
|
||||
struct adb_obj acl;
|
||||
|
||||
apk_extract_acl(&fi, adb_ro_obj(&ctx->path, ADBI_DI_ACL, &acl), apk_ctx_get_id_cache(ctx->ac));
|
||||
fi.mode |= S_IFDIR;
|
||||
|
||||
return apk_archive_entry_extract(
|
||||
ctx->root_fd, &fi, 0, 0, 0, 0, 0, 0,
|
||||
ctx->extract_flags, out);
|
||||
}
|
||||
|
||||
static int apk_extract_next_file(struct extract_ctx *ctx)
|
||||
{
|
||||
apk_blob_t target;
|
||||
int r;
|
||||
|
||||
if (!ctx->cur_path) {
|
||||
// one time init
|
||||
ctx->cur_path = ADBI_FIRST;
|
||||
ctx->cur_file = 0;
|
||||
adb_r_rootobj(&ctx->db, &ctx->pkg, &schema_package);
|
||||
adb_ro_obj(&ctx->pkg, ADBI_PKG_PATHS, &ctx->paths);
|
||||
adb_ro_obj(&ctx->paths, ctx->cur_path, &ctx->path);
|
||||
adb_ro_obj(&ctx->path, ADBI_DI_FILES, &ctx->files);
|
||||
}
|
||||
|
||||
do {
|
||||
ctx->cur_file++;
|
||||
while (ctx->cur_file > adb_ra_num(&ctx->files)) {
|
||||
ctx->cur_path++;
|
||||
ctx->cur_file = ADBI_FIRST;
|
||||
if (ctx->cur_path > adb_ra_num(&ctx->paths)) return 1;
|
||||
adb_ro_obj(&ctx->paths, ctx->cur_path, &ctx->path);
|
||||
apk_pathbuilder_setb(&ctx->pb, adb_ro_blob(&ctx->path, ADBI_DI_NAME));
|
||||
adb_ro_obj(&ctx->path, ADBI_DI_FILES, &ctx->files);
|
||||
r = apk_extract_directory(ctx);
|
||||
if (r != 0) return r;
|
||||
}
|
||||
adb_ro_obj(&ctx->files, ctx->cur_file, &ctx->file);
|
||||
apk_pathbuilder_setb(&ctx->pb, adb_ro_blob(&ctx->path, ADBI_DI_NAME));
|
||||
apk_pathbuilder_pushb(&ctx->pb, adb_ro_blob(&ctx->file, ADBI_FI_NAME));
|
||||
target = adb_ro_blob(&ctx->file, ADBI_FI_TARGET);
|
||||
if (adb_ro_int(&ctx->file, ADBI_FI_SIZE) != 0 &&
|
||||
APK_BLOB_IS_NULL(target)) {
|
||||
return 0;
|
||||
}
|
||||
r = apk_extract_file(ctx, 0, 0);
|
||||
if (r != 0) return r;
|
||||
} while (1);
|
||||
}
|
||||
|
||||
static int apk_extract_data_block(struct adb *db, size_t sz, struct apk_istream *is)
|
||||
{
|
||||
struct extract_ctx *ctx = container_of(db, struct extract_ctx, db);
|
||||
struct adb_data_package *hdr;
|
||||
int r;
|
||||
|
||||
r = apk_extract_next_file(ctx);
|
||||
if (r != 0) {
|
||||
if (r > 0) r = -EAPKFORMAT;
|
||||
return r;
|
||||
}
|
||||
|
||||
hdr = apk_istream_get(is, sizeof *hdr);
|
||||
sz -= sizeof *hdr;
|
||||
if (IS_ERR(hdr)) return PTR_ERR(hdr);
|
||||
|
||||
if (hdr->path_idx != ctx->cur_path ||
|
||||
hdr->file_idx != ctx->cur_file ||
|
||||
sz != adb_ro_int(&ctx->file, ADBI_FI_SIZE)) {
|
||||
// got data for some unexpected file
|
||||
return -EAPKFORMAT;
|
||||
}
|
||||
|
||||
return apk_extract_file(ctx, sz, is);
|
||||
}
|
||||
|
||||
static int apk_extract_pkg(struct extract_ctx *ctx, const char *fn)
|
||||
{
|
||||
struct apk_ctx *ac = ctx->ac;
|
||||
struct apk_trust *trust = apk_ctx_get_trust(ac);
|
||||
int r;
|
||||
|
||||
r = adb_m_stream(&ctx->db,
|
||||
apk_istream_gunzip(apk_istream_from_fd_url(AT_FDCWD, fn, apk_ctx_since(ac, 0))),
|
||||
ADB_SCHEMA_PACKAGE, trust, apk_extract_data_block);
|
||||
if (r == 0) {
|
||||
r = apk_extract_next_file(ctx);
|
||||
if (r == 0) r = -EAPKFORMAT;
|
||||
if (r == 1) r = 0;
|
||||
}
|
||||
adb_free(&ctx->db);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int extract_main(void *pctx, struct apk_ctx *ac, struct apk_string_array *args)
|
||||
{
|
||||
struct extract_ctx *ctx = pctx;
|
||||
struct apk_out *out = &ac->out;
|
||||
char **parg;
|
||||
int r;
|
||||
|
||||
ctx->ac = ac;
|
||||
ctx->extract_flags |= APK_EXTRACTF_NO_OVERWRITE;
|
||||
if (!ctx->destination) ctx->destination = ".";
|
||||
ctx->root_fd = openat(AT_FDCWD, ctx->destination, O_RDONLY);
|
||||
if (ctx->root_fd < 0) {
|
||||
r = -errno;
|
||||
apk_err(out, "Error opening destination '%s': %s",
|
||||
ctx->destination, apk_error_str(r));
|
||||
return r;
|
||||
}
|
||||
|
||||
foreach_array_item(parg, args) {
|
||||
apk_out(out, "Extracting %s...", *parg);
|
||||
r = apk_extract_pkg(ctx, *parg);
|
||||
if (r != 0) {
|
||||
apk_err(out, "%s: %s", *parg, apk_error_str(r));
|
||||
break;
|
||||
}
|
||||
}
|
||||
close(ctx->root_fd);
|
||||
return r;
|
||||
}
|
||||
|
||||
static struct apk_applet apk_extract = {
|
||||
.name = "extract",
|
||||
.context_size = sizeof(struct extract_ctx),
|
||||
.optgroups = { &optgroup_global, &optgroup_applet },
|
||||
.main = extract_main,
|
||||
};
|
||||
|
||||
APK_DEFINE_APPLET(apk_extract);
|
||||
|
|
@ -176,7 +176,7 @@ static int fetch_package(apk_hash_item item, void *pctx)
|
|||
goto err;
|
||||
}
|
||||
|
||||
r = apk_istream_splice(is, fd, pkg->size, progress_cb, ctx);
|
||||
r = apk_istream_splice(is, fd, pkg->size, progress_cb, ctx, 0);
|
||||
if (fd != STDOUT_FILENO) {
|
||||
struct apk_file_meta meta;
|
||||
apk_istream_get_meta(is, &meta);
|
||||
|
|
|
@ -251,11 +251,10 @@ static int mkpkg_main(void *pctx, struct apk_ctx *ac, struct apk_string_array *a
|
|||
size_t sz = adb_ro_int(&file, ADBI_FI_SIZE);
|
||||
if (!APK_BLOB_IS_NULL(target)) continue;
|
||||
if (!sz) continue;
|
||||
struct {
|
||||
uint32_t path_idx;
|
||||
uint32_t file_idx;
|
||||
} hdr = { i, j };
|
||||
|
||||
struct adb_data_package hdr = {
|
||||
.path_idx = i,
|
||||
.file_idx = j,
|
||||
};
|
||||
apk_pathbuilder_pushb(&ctx->pb, filename);
|
||||
adb_c_block_data(
|
||||
os, APK_BLOB_STRUCT(hdr), sz,
|
||||
|
|
|
@ -23,6 +23,29 @@ const char *apk_digest_alg_str(uint8_t alg)
|
|||
return alg_str;
|
||||
}
|
||||
|
||||
int apk_digest_alg_len(uint8_t alg)
|
||||
{
|
||||
switch (alg) {
|
||||
case APK_DIGEST_MD5: return 16;
|
||||
case APK_DIGEST_SHA1: return 20;
|
||||
case APK_DIGEST_SHA256: return 32;
|
||||
case APK_DIGEST_SHA512: return 64;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t apk_digest_alg_by_len(int len)
|
||||
{
|
||||
switch (len) {
|
||||
case 0: return APK_DIGEST_NONE;
|
||||
case 16: return APK_DIGEST_MD5;
|
||||
case 20: return APK_DIGEST_SHA1;
|
||||
case 32: return APK_DIGEST_SHA256;
|
||||
case 64: return APK_DIGEST_SHA512;
|
||||
default: return APK_DIGEST_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t apk_digest_alg_from_csum(int csum)
|
||||
{
|
||||
switch (csum) {
|
||||
|
@ -33,6 +56,17 @@ uint8_t apk_digest_alg_from_csum(int csum)
|
|||
}
|
||||
}
|
||||
|
||||
uint8_t apk_digest_from_blob(struct apk_digest *d, apk_blob_t b)
|
||||
{
|
||||
d->alg = apk_digest_alg_by_len(b.len);
|
||||
d->len = 0;
|
||||
if (d->alg != APK_DIGEST_NONE) {
|
||||
d->len = b.len;
|
||||
memcpy(d->data, b.ptr, d->len);
|
||||
}
|
||||
return d->alg;
|
||||
}
|
||||
|
||||
int apk_pkey_init(struct apk_pkey *pkey, EVP_PKEY *key)
|
||||
{
|
||||
unsigned char dig[EVP_MAX_MD_SIZE], *pub = NULL;
|
||||
|
|
|
@ -672,7 +672,7 @@ int apk_cache_download(struct apk_database *db, struct apk_repository *repo,
|
|||
|
||||
if (fd >= 0) {
|
||||
struct apk_file_meta meta;
|
||||
r = apk_istream_splice(is, fd, APK_IO_ALL, cb, cb_ctx);
|
||||
r = apk_istream_splice(is, fd, APK_IO_ALL, cb, cb_ctx, 0);
|
||||
if (!autoupdate) {
|
||||
apk_istream_get_meta(is, &meta);
|
||||
apk_file_meta_to_fd(fd, &meta);
|
||||
|
@ -2566,7 +2566,7 @@ static int apk_db_install_archive_entry(void *_ctx,
|
|||
db->root_fd, ae,
|
||||
format_tmpname(pkg, file, tmpname_file),
|
||||
format_tmpname(pkg, link_target_file, tmpname_link_target),
|
||||
is, extract_cb, ctx, db->extract_flags, out);
|
||||
is, extract_cb, ctx, 0, db->extract_flags, out);
|
||||
|
||||
switch (r) {
|
||||
case 0:
|
||||
|
|
27
src/io.c
27
src/io.c
|
@ -114,27 +114,21 @@ static int __apk_istream_fill(struct apk_istream *is)
|
|||
return 0;
|
||||
}
|
||||
|
||||
apk_blob_t apk_istream_get(struct apk_istream *is, size_t len)
|
||||
void *apk_istream_get(struct apk_istream *is, size_t len)
|
||||
{
|
||||
apk_blob_t ret = APK_BLOB_NULL;
|
||||
|
||||
do {
|
||||
if (is->end - is->ptr >= len) {
|
||||
ret = APK_BLOB_PTR_LEN((char*)is->ptr, len);
|
||||
break;
|
||||
}
|
||||
if (is->err>0 || is->end-is->ptr == is->buf_size) {
|
||||
ret = APK_BLOB_PTR_LEN((char*)is->ptr, is->end - is->ptr);
|
||||
break;
|
||||
void *ptr = is->ptr;
|
||||
is->ptr += len;
|
||||
return ptr;
|
||||
}
|
||||
} while (!__apk_istream_fill(is));
|
||||
|
||||
if (!APK_BLOB_IS_NULL(ret)) {
|
||||
is->ptr = (uint8_t*)ret.ptr + ret.len;
|
||||
return ret;
|
||||
}
|
||||
|
||||
return (struct apk_blob) { .len = is->err < 0 ? is->err : 0 };
|
||||
if (is->end-is->ptr == is->buf_size)
|
||||
return ERR_PTR(-ENOBUFS);
|
||||
if (is->err > 0)
|
||||
return ERR_PTR(-ENOMSG);
|
||||
return ERR_PTR(-EIO);
|
||||
}
|
||||
|
||||
apk_blob_t apk_istream_get_max(struct apk_istream *is, size_t max)
|
||||
|
@ -518,7 +512,7 @@ ssize_t apk_stream_copy(struct apk_istream *is, struct apk_ostream *os, size_t s
|
|||
}
|
||||
|
||||
ssize_t apk_istream_splice(struct apk_istream *is, int fd, size_t size,
|
||||
apk_progress_cb cb, void *cb_ctx)
|
||||
apk_progress_cb cb, void *cb_ctx, struct apk_digest_ctx *dctx)
|
||||
{
|
||||
static void *splice_buffer = NULL;
|
||||
unsigned char *buf, *mmapbase = MAP_FAILED;
|
||||
|
@ -558,6 +552,7 @@ ssize_t apk_istream_splice(struct apk_istream *is, int fd, size_t size,
|
|||
}
|
||||
break;
|
||||
}
|
||||
if (dctx) apk_digest_ctx_update(dctx, buf, r);
|
||||
|
||||
if (mmapbase == MAP_FAILED) {
|
||||
if (write(fd, buf, r) != r) {
|
||||
|
|
|
@ -184,8 +184,8 @@ int apk_tar_parse(struct apk_istream *is, apk_archive_entry_parser parser,
|
|||
if ((r = blob_realloc(&longname, entry.size+1)) != 0 ||
|
||||
(r = apk_istream_read(is, longname.ptr, entry.size)) != entry.size)
|
||||
goto err;
|
||||
longname.ptr[entry.size] = 0;
|
||||
entry.name = longname.ptr;
|
||||
entry.name[entry.size] = 0;
|
||||
toskip -= entry.size;
|
||||
break;
|
||||
case 'K': /* GNU long link target extension - ignored */
|
||||
|
@ -336,15 +336,16 @@ int apk_tar_write_padding(struct apk_ostream *os, const struct apk_file_info *ae
|
|||
int apk_archive_entry_extract(int atfd, const struct apk_file_info *ae,
|
||||
const char *extract_name, const char *link_target,
|
||||
struct apk_istream *is,
|
||||
apk_progress_cb cb, void *cb_ctx,
|
||||
unsigned int apk_extract_flags,
|
||||
struct apk_out *out)
|
||||
apk_progress_cb cb, void *cb_ctx, struct apk_digest_ctx *dctx,
|
||||
unsigned int extract_flags, struct apk_out *out)
|
||||
{
|
||||
struct apk_xattr *xattr;
|
||||
const char *fn = extract_name ?: ae->name;
|
||||
int fd, r = -1, atflags = 0, ret = 0;
|
||||
|
||||
if (!(extract_flags & APK_EXTRACTF_NO_OVERWRITE)) {
|
||||
if (unlinkat(atfd, fn, 0) != 0 && errno != ENOENT) return -errno;
|
||||
}
|
||||
|
||||
switch (ae->mode & S_IFMT) {
|
||||
case S_IFDIR:
|
||||
|
@ -361,7 +362,7 @@ int apk_archive_entry_extract(int atfd, const struct apk_file_info *ae,
|
|||
ret = -errno;
|
||||
break;
|
||||
}
|
||||
r = apk_istream_splice(is, fd, ae->size, cb, cb_ctx);
|
||||
r = apk_istream_splice(is, fd, ae->size, cb, cb_ctx, dctx);
|
||||
if (r != ae->size) ret = r < 0 ? r : -ENOSPC;
|
||||
close(fd);
|
||||
} else {
|
||||
|
@ -386,7 +387,7 @@ int apk_archive_entry_extract(int atfd, const struct apk_file_info *ae,
|
|||
return ret;
|
||||
}
|
||||
|
||||
if (!(apk_extract_flags & APK_EXTRACTF_NO_CHOWN)) {
|
||||
if (!(extract_flags & APK_EXTRACTF_NO_CHOWN)) {
|
||||
r = fchownat(atfd, fn, ae->uid, ae->gid, atflags);
|
||||
if (r < 0) {
|
||||
apk_err(out, "Failed to set ownership on %s: %s",
|
||||
|
|
|
@ -57,6 +57,7 @@ apk_src = [
|
|||
'app_convndx.c',
|
||||
'app_del.c',
|
||||
'app_dot.c',
|
||||
'app_extract.c',
|
||||
'app_fetch.c',
|
||||
'app_fix.c',
|
||||
'app_index.c',
|
||||
|
|
Loading…
Reference in New Issue