database: implement uvol support
by adding an abstraction layer to the file systemcute-signatures
parent
d441cf523c
commit
a673653200
|
@ -21,7 +21,7 @@ libapk_so := $(obj)/libapk.so.$(libapk_soname)
|
|||
libapk.so.$(libapk_soname)-objs := \
|
||||
adb.o adb_comp.o adb_walk_adb.o adb_walk_genadb.o adb_walk_gentext.o adb_walk_text.o apk_adb.o \
|
||||
atom.o blob.o commit.o common.o context.o crypto_openssl.o database.o hash.o \
|
||||
extract.o extract_v2.o extract_v3.o io.o io_gunzip.o io_url.o tar.o \
|
||||
extract_v2.o extract_v3.o fs_fsys.o fs_uvol.o io.o io_gunzip.o io_url.o tar.o \
|
||||
package.o pathbuilder.o print.o solver.o trust.o version.o
|
||||
|
||||
libapk.so.$(libapk_soname)-libs := libfetch/libfetch.a
|
||||
|
|
|
@ -9,9 +9,11 @@
|
|||
#ifndef APK_CONTEXT_H
|
||||
#define APK_CONTEXT_H
|
||||
|
||||
#include "apk_blob.h"
|
||||
#include "apk_print.h"
|
||||
#include "apk_trust.h"
|
||||
#include "apk_io.h"
|
||||
#include "apk_crypto.h"
|
||||
#include "adb.h"
|
||||
|
||||
#define APK_SIMULATE BIT(0)
|
||||
|
@ -34,8 +36,6 @@
|
|||
#define APK_FORCE_NON_REPOSITORY BIT(4)
|
||||
#define APK_FORCE_BINARY_STDOUT BIT(5)
|
||||
|
||||
struct apk_database;
|
||||
|
||||
#define APK_OPENF_READ 0x0001
|
||||
#define APK_OPENF_WRITE 0x0002
|
||||
#define APK_OPENF_CREATE 0x0004
|
||||
|
@ -53,6 +53,8 @@ struct apk_database;
|
|||
APK_OPENF_NO_SCRIPTS | \
|
||||
APK_OPENF_NO_WORLD)
|
||||
|
||||
struct apk_database;
|
||||
|
||||
struct apk_ctx {
|
||||
unsigned int flags, force, lock_wait;
|
||||
struct apk_out out;
|
||||
|
@ -70,7 +72,9 @@ struct apk_ctx {
|
|||
struct apk_trust trust;
|
||||
struct apk_id_cache id_cache;
|
||||
struct apk_database *db;
|
||||
int root_fd;
|
||||
int root_fd, dest_fd;
|
||||
|
||||
struct apk_digest_ctx dctx;
|
||||
};
|
||||
|
||||
void apk_ctx_init(struct apk_ctx *ac);
|
||||
|
@ -81,6 +85,7 @@ struct apk_trust *apk_ctx_get_trust(struct apk_ctx *ac);
|
|||
struct apk_id_cache *apk_ctx_get_id_cache(struct apk_ctx *ac);
|
||||
|
||||
static inline int apk_ctx_fd_root(struct apk_ctx *ac) { return ac->root_fd; }
|
||||
static inline int apk_ctx_fd_dest(struct apk_ctx *ac) { return ac->dest_fd; }
|
||||
static inline time_t apk_ctx_since(struct apk_ctx *ac, time_t since) {
|
||||
return (ac->force & APK_FORCE_REFRESH) ? APK_ISTREAM_FORCE_REFRESH : since;
|
||||
}
|
||||
|
|
|
@ -90,10 +90,16 @@ static inline int apk_digest_ctx_init(struct apk_digest_ctx *dctx, uint8_t alg)
|
|||
#ifdef EVP_MD_CTX_FLAG_FINALISE
|
||||
EVP_MD_CTX_set_flags(dctx->mdctx, EVP_MD_CTX_FLAG_FINALISE);
|
||||
#endif
|
||||
EVP_DigestInit_ex(dctx->mdctx, apk_digest_alg_to_evp(alg), 0);
|
||||
if (alg != APK_DIGEST_NONE) EVP_DigestInit_ex(dctx->mdctx, apk_digest_alg_to_evp(alg), 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void apk_digest_ctx_reset(struct apk_digest_ctx *dctx, uint8_t alg)
|
||||
{
|
||||
dctx->alg = alg;
|
||||
EVP_DigestInit_ex(dctx->mdctx, apk_digest_alg_to_evp(alg), 0);
|
||||
}
|
||||
|
||||
static inline void apk_digest_ctx_free(struct apk_digest_ctx *dctx) {
|
||||
EVP_MD_CTX_free(dctx->mdctx);
|
||||
dctx->mdctx = 0;
|
||||
|
|
|
@ -62,7 +62,9 @@ enum {
|
|||
APKE_INDEX_STALE,
|
||||
APKE_FILE_INTEGRITY,
|
||||
APKE_CACHE_NOT_AVAILABLE,
|
||||
APKE_UVOL,
|
||||
APKE_UVOL_NOT_AVAILABLE,
|
||||
APKE_UVOL_ERROR,
|
||||
APKE_UVOL_ROOT,
|
||||
};
|
||||
|
||||
static inline void *ERR_PTR(long error) { return (void*) error; }
|
||||
|
|
|
@ -18,16 +18,6 @@ struct adb_obj;
|
|||
struct apk_ctx;
|
||||
struct apk_extract_ctx;
|
||||
|
||||
#define APK_EXTRACT_SKIP_FILE 0x111
|
||||
|
||||
#define APK_EXTRACTF_NO_CHOWN 0x0001
|
||||
#define APK_EXTRACTF_NO_OVERWRITE 0x0002
|
||||
|
||||
int apk_extract_file(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,
|
||||
unsigned int extract_flags, struct apk_ctx *ac);
|
||||
|
||||
struct apk_extract_ops {
|
||||
int (*v2index)(struct apk_extract_ctx *, apk_blob_t *desc, struct apk_istream *is);
|
||||
int (*v2meta)(struct apk_extract_ctx *, struct apk_istream *is);
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
/* apk_fs.h - Alpine Package Keeper (APK)
|
||||
*
|
||||
* Copyright (C) 2021 Timo Teräs <timo.teras@iki.fi>
|
||||
* All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-only
|
||||
*/
|
||||
|
||||
#ifndef APK_FS_H
|
||||
#define APK_FS_H
|
||||
|
||||
#include "apk_context.h"
|
||||
#include "apk_io.h"
|
||||
#include "apk_pathbuilder.h"
|
||||
|
||||
#define APK_FS_CTRL_COMMIT 1
|
||||
#define APK_FS_CTRL_APKNEW 2
|
||||
#define APK_FS_CTRL_CANCEL 3
|
||||
#define APK_FS_CTRL_DELETE 4
|
||||
|
||||
#define APK_FS_DIR_MODIFIED 1
|
||||
|
||||
struct apk_fsdir_ops;
|
||||
|
||||
struct apk_fsdir {
|
||||
struct apk_ctx *ac;
|
||||
const struct apk_fsdir_ops *ops;
|
||||
struct apk_pathbuilder pb;
|
||||
apk_blob_t pkgctx;
|
||||
};
|
||||
|
||||
struct apk_fsdir_ops {
|
||||
int (*dir_create)(struct apk_fsdir *, mode_t);
|
||||
int (*dir_delete)(struct apk_fsdir *);
|
||||
int (*dir_check)(struct apk_fsdir *, mode_t, uid_t, gid_t);
|
||||
int (*dir_update_perms)(struct apk_fsdir *, mode_t, uid_t, gid_t);
|
||||
|
||||
int (*file_extract)(struct apk_ctx *, const struct apk_file_info *, struct apk_istream *, apk_progress_cb, void *, unsigned int, apk_blob_t);
|
||||
int (*file_control)(struct apk_fsdir *, apk_blob_t, int);
|
||||
int (*file_digest)(struct apk_fsdir *, apk_blob_t, uint8_t alg, struct apk_digest *);
|
||||
};
|
||||
|
||||
#define APK_FSEXTRACTF_NO_CHOWN 0x0001
|
||||
#define APK_FSEXTRACTF_NO_OVERWRITE 0x0002
|
||||
|
||||
int apk_fs_extract(struct apk_ctx *, const struct apk_file_info *, struct apk_istream *, apk_progress_cb, void *, unsigned int, apk_blob_t);
|
||||
|
||||
void apk_fsdir_get(struct apk_fsdir *, apk_blob_t dir, struct apk_ctx *, apk_blob_t);
|
||||
|
||||
static inline int apk_fsdir_create(struct apk_fsdir *fs, mode_t mode) {
|
||||
return fs->ops->dir_create(fs, mode);
|
||||
}
|
||||
static inline int apk_fsdir_delete(struct apk_fsdir *fs) {
|
||||
return fs->ops->dir_delete(fs);
|
||||
}
|
||||
static inline int apk_fsdir_check(struct apk_fsdir *fs, mode_t mode, uid_t uid, gid_t gid) {
|
||||
return fs->ops->dir_check(fs, mode, uid, gid);
|
||||
}
|
||||
static inline int apk_fsdir_update_perms(struct apk_fsdir *fs, mode_t mode, uid_t uid, gid_t gid) {
|
||||
return fs->ops->dir_update_perms(fs, mode, uid, gid);
|
||||
}
|
||||
|
||||
static inline int apk_fsdir_file_control(struct apk_fsdir *fs, apk_blob_t filename, int ctrl) {
|
||||
return fs->ops->file_control(fs, filename, ctrl);
|
||||
}
|
||||
static inline int apk_fsdir_file_digest(struct apk_fsdir *fs, apk_blob_t filename, uint8_t alg, struct apk_digest *dgst) {
|
||||
return fs->ops->file_digest(fs, filename, alg, dgst);
|
||||
}
|
||||
|
||||
#endif
|
|
@ -44,7 +44,6 @@ struct apk_file_meta {
|
|||
struct apk_file_info {
|
||||
const char *name;
|
||||
const char *link_target;
|
||||
const char *uvol_name;
|
||||
const char *uname;
|
||||
const char *gname;
|
||||
off_t size;
|
||||
|
@ -140,8 +139,9 @@ struct apk_digest_istream {
|
|||
struct apk_istream *pis;
|
||||
struct apk_digest *digest;
|
||||
struct apk_digest_ctx dctx;
|
||||
off_t size_left;
|
||||
};
|
||||
struct apk_istream *apk_istream_verify(struct apk_digest_istream *dis, struct apk_istream *is, struct apk_digest *d);
|
||||
struct apk_istream *apk_istream_verify(struct apk_digest_istream *dis, struct apk_istream *is, off_t size, struct apk_digest *d);
|
||||
|
||||
#define APK_ISTREAM_TEE_COPY_META 1
|
||||
#define APK_ISTREAM_TEE_OPTIONAL 2
|
||||
|
|
|
@ -10,11 +10,13 @@
|
|||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "apk_applet.h"
|
||||
#include "apk_database.h"
|
||||
#include "apk_print.h"
|
||||
#include "apk_solver.h"
|
||||
#include "apk_extract.h"
|
||||
#include "apk_fs.h"
|
||||
|
||||
struct add_ctx {
|
||||
const char *virtpkg;
|
||||
|
@ -43,7 +45,7 @@ static int option_parse_applet(void *ctx, struct apk_ctx *ac, int opt, const cha
|
|||
actx->solver_flags |= APK_SOLVERF_LATEST;
|
||||
break;
|
||||
case OPT_ADD_no_chown:
|
||||
actx->extract_flags |= APK_EXTRACTF_NO_CHOWN;
|
||||
actx->extract_flags |= APK_FSEXTRACTF_NO_CHOWN;
|
||||
break;
|
||||
case OPT_ADD_upgrade:
|
||||
actx->solver_flags |= APK_SOLVERF_UPGRADE;
|
||||
|
@ -124,8 +126,8 @@ static int add_main(void *ctx, struct apk_ctx *ac, struct apk_string_array *args
|
|||
|
||||
apk_dependency_array_copy(&world, db->world);
|
||||
|
||||
if (getuid() != 0 || (actx->extract_flags & APK_EXTRACTF_NO_CHOWN))
|
||||
db->extract_flags |= APK_EXTRACTF_NO_CHOWN;
|
||||
if (getuid() != 0 || (actx->extract_flags & APK_FSEXTRACTF_NO_CHOWN))
|
||||
db->extract_flags |= APK_FSEXTRACTF_NO_CHOWN;
|
||||
|
||||
if (actx->virtpkg) {
|
||||
apk_blob_t b = APK_BLOB_STR(actx->virtpkg);
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include "apk_applet.h"
|
||||
#include "apk_print.h"
|
||||
#include "apk_extract.h"
|
||||
#include "apk_fs.h"
|
||||
|
||||
struct extract_ctx {
|
||||
const char *destination;
|
||||
|
@ -22,7 +23,6 @@ struct extract_ctx {
|
|||
|
||||
struct apk_extract_ctx ectx;
|
||||
struct apk_ctx *ac;
|
||||
int root_fd;
|
||||
};
|
||||
|
||||
|
||||
|
@ -41,7 +41,7 @@ static int option_parse_applet(void *pctx, struct apk_ctx *ac, int opt, const ch
|
|||
ctx->destination = optarg;
|
||||
break;
|
||||
case OPT_EXTRACT_no_chown:
|
||||
ctx->extract_flags |= APK_EXTRACTF_NO_CHOWN;
|
||||
ctx->extract_flags |= APK_FSEXTRACTF_NO_CHOWN;
|
||||
break;
|
||||
default:
|
||||
return -ENOTSUP;
|
||||
|
@ -66,8 +66,7 @@ static int extract_file(struct apk_extract_ctx *ectx, const struct apk_file_info
|
|||
|
||||
apk_dbg2(out, "%s", fi->name);
|
||||
|
||||
return apk_extract_file(ctx->root_fd, fi, 0, 0, is, 0, 0,
|
||||
ctx->extract_flags, ectx->ac);
|
||||
return apk_fs_extract(ctx->ac, fi, is, 0, 0, ctx->extract_flags, APK_BLOB_NULL);
|
||||
}
|
||||
|
||||
static const struct apk_extract_ops extract_ops = {
|
||||
|
@ -84,10 +83,11 @@ static int extract_main(void *pctx, struct apk_ctx *ac, struct apk_string_array
|
|||
int r = 0;
|
||||
|
||||
ctx->ac = ac;
|
||||
if (!(ac->force & APK_FORCE_OVERWRITE)) ctx->extract_flags |= APK_EXTRACTF_NO_OVERWRITE;
|
||||
if (!(ac->force & APK_FORCE_OVERWRITE)) ctx->extract_flags |= APK_FSEXTRACTF_NO_OVERWRITE;
|
||||
if (!ctx->destination) ctx->destination = ".";
|
||||
ctx->root_fd = openat(AT_FDCWD, ctx->destination, O_RDONLY);
|
||||
if (ctx->root_fd < 0) {
|
||||
|
||||
ac->dest_fd = openat(AT_FDCWD, ctx->destination, O_RDONLY);
|
||||
if (ac->dest_fd < 0) {
|
||||
r = -errno;
|
||||
apk_err(out, "Error opening destination '%s': %s",
|
||||
ctx->destination, apk_error_str(r));
|
||||
|
@ -103,7 +103,7 @@ static int extract_main(void *pctx, struct apk_ctx *ac, struct apk_string_array
|
|||
break;
|
||||
}
|
||||
}
|
||||
close(ctx->root_fd);
|
||||
close(ac->dest_fd);
|
||||
return r;
|
||||
}
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ void apk_ctx_init(struct apk_ctx *ac)
|
|||
ac->out.out = stdout;
|
||||
ac->out.err = stderr;
|
||||
ac->out.verbosity = 1;
|
||||
apk_digest_ctx_init(&ac->dctx, APK_DIGEST_SHA256);
|
||||
}
|
||||
|
||||
void apk_ctx_free(struct apk_ctx *ac)
|
||||
|
@ -43,8 +44,19 @@ int apk_ctx_prepare(struct apk_ctx *ac)
|
|||
if (!ac->keys_dir) ac->keys_dir = "etc/apk/keys";
|
||||
if (!ac->root) ac->root = "/";
|
||||
if (!ac->cache_max_age) ac->cache_max_age = 4*60*60; /* 4 hours default */
|
||||
if (!strcmp(ac->root, "/")) ac->flags |= APK_NO_CHROOT; /* skip chroot if root is default */
|
||||
ac->uvol = getenv("APK_UVOL") ?: "/usr/bin/uvol";
|
||||
|
||||
if (!strcmp(ac->root, "/")) {
|
||||
// No chroot needed if using system root
|
||||
ac->flags |= APK_NO_CHROOT;
|
||||
|
||||
// Check uvol availability
|
||||
ac->uvol = getenv("APK_UVOL") ?: "/usr/bin/uvol";
|
||||
if (access(ac->uvol, X_OK) != 0)
|
||||
ac->uvol = ERR_PTR(-APKE_UVOL_NOT_AVAILABLE);
|
||||
} else {
|
||||
ac->uvol = ERR_PTR(-APKE_UVOL_ROOT);
|
||||
}
|
||||
|
||||
|
||||
ac->root_fd = openat(AT_FDCWD, ac->root, O_RDONLY | O_CLOEXEC);
|
||||
if (ac->root_fd < 0 && (ac->open_flags & APK_OPENF_CREATE)) {
|
||||
|
@ -55,13 +67,15 @@ int apk_ctx_prepare(struct apk_ctx *ac)
|
|||
apk_err(&ac->out, "Unable to open root: %s", apk_error_str(errno));
|
||||
return -errno;
|
||||
}
|
||||
ac->dest_fd = ac->root_fd;
|
||||
|
||||
if (ac->open_flags & APK_OPENF_WRITE) {
|
||||
const char *log_path = "var/log/apk.log", *log_dir = "var/log";
|
||||
const char *log_path = "var/log/apk.log";
|
||||
const int lflags = O_WRONLY | O_APPEND | O_CREAT | O_CLOEXEC;
|
||||
int fd = openat(ac->root_fd, log_path, lflags, 0644);
|
||||
if (fd < 0 && (ac->open_flags & APK_OPENF_CREATE)) {
|
||||
mkdirat(ac->root_fd, log_dir, 0755);
|
||||
mkdirat(ac->root_fd, "var", 0755);
|
||||
mkdirat(ac->root_fd, "var/log", 0755);
|
||||
fd = openat(ac->root_fd, log_path, lflags, 0644);
|
||||
}
|
||||
if (fd < 0) {
|
||||
|
|
210
src/database.c
210
src/database.c
|
@ -37,6 +37,7 @@
|
|||
#include "apk_openssl.h"
|
||||
#include "apk_tar.h"
|
||||
#include "apk_adb.h"
|
||||
#include "apk_fs.h"
|
||||
|
||||
static const apk_spn_match_def apk_spn_repo_separators = {
|
||||
[1] = (1<<1) /* tab */,
|
||||
|
@ -83,6 +84,11 @@ struct install_ctx {
|
|||
struct hlist_node **file_diri_node;
|
||||
};
|
||||
|
||||
static apk_blob_t apk_pkg_ctx(struct apk_package *pkg)
|
||||
{
|
||||
return APK_BLOB_PTR_LEN(pkg->name->name, strlen(pkg->name->name)+1);
|
||||
}
|
||||
|
||||
static apk_blob_t pkg_name_get_key(apk_hash_item item)
|
||||
{
|
||||
return APK_BLOB_STR(((struct apk_name *) item)->name);
|
||||
|
@ -244,23 +250,21 @@ static struct apk_db_acl *apk_db_acl_atomize_digest(struct apk_database *db, mod
|
|||
|
||||
static void apk_db_dir_prepare(struct apk_database *db, struct apk_db_dir *dir, mode_t newmode)
|
||||
{
|
||||
struct stat st;
|
||||
struct apk_fsdir d;
|
||||
|
||||
if (dir->namelen == 0) return;
|
||||
if (dir->created) return;
|
||||
|
||||
if (fstatat(db->root_fd, dir->name, &st, AT_SYMLINK_NOFOLLOW) == 0) {
|
||||
/* If directory exists and stats match what we expect,
|
||||
* then we can allow auto updating the permissions */
|
||||
dir->created = 1;
|
||||
dir->update_permissions |=
|
||||
(st.st_mode & 07777) == (dir->mode & 07777) &&
|
||||
st.st_uid == dir->uid && st.st_gid == dir->gid;
|
||||
} else if (newmode) {
|
||||
apk_fsdir_get(&d, APK_BLOB_PTR_LEN(dir->name, dir->namelen), db->ctx, APK_BLOB_NULL);
|
||||
switch (apk_fsdir_check(&d, dir->mode, dir->uid, dir->gid)) {
|
||||
default:
|
||||
if (!(db->ctx->flags & APK_SIMULATE))
|
||||
mkdirat(db->root_fd, dir->name, newmode);
|
||||
dir->created = 1;
|
||||
apk_fsdir_create(&d, dir->mode);
|
||||
case 0:
|
||||
dir->update_permissions = 1;
|
||||
case APK_FS_DIR_MODIFIED:
|
||||
dir->created = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -272,9 +276,11 @@ void apk_db_dir_unref(struct apk_database *db, struct apk_db_dir *dir, int rmdir
|
|||
if (dir->namelen != 0) {
|
||||
if (rmdir_mode == APK_DIR_REMOVE) {
|
||||
dir->modified = 1;
|
||||
if (!(db->ctx->flags & APK_SIMULATE) &&
|
||||
unlinkat(db->root_fd, dir->name, AT_REMOVEDIR) != 0)
|
||||
;
|
||||
if (!(db->ctx->flags & APK_SIMULATE)) {
|
||||
struct apk_fsdir d;
|
||||
apk_fsdir_get(&d, APK_BLOB_PTR_LEN(dir->name, dir->namelen), db->ctx, APK_BLOB_NULL);
|
||||
apk_fsdir_delete(&d);
|
||||
}
|
||||
}
|
||||
apk_db_dir_unref(db, dir->parent, rmdir_mode);
|
||||
dir->parent = NULL;
|
||||
|
@ -1953,20 +1959,15 @@ static int update_permissions(apk_hash_item item, void *pctx)
|
|||
struct update_permissions_ctx *ctx = pctx;
|
||||
struct apk_database *db = ctx->db;
|
||||
struct apk_db_dir *dir = (struct apk_db_dir *) item;
|
||||
struct stat st;
|
||||
int r;
|
||||
struct apk_fsdir d;
|
||||
|
||||
if (dir->refs == 0) return 0;
|
||||
if (!dir->update_permissions) return 0;
|
||||
dir->seen = 0;
|
||||
|
||||
r = fstatat(db->root_fd, dir->name, &st, AT_SYMLINK_NOFOLLOW);
|
||||
if (r < 0 || (st.st_mode & 07777) != (dir->mode & 07777))
|
||||
if (fchmodat(db->root_fd, dir->name, dir->mode, 0) < 0)
|
||||
ctx->errors++;
|
||||
if (r < 0 || st.st_uid != dir->uid || st.st_gid != dir->gid)
|
||||
if (fchownat(db->root_fd, dir->name, dir->uid, dir->gid, 0) < 0)
|
||||
ctx->errors++;
|
||||
apk_fsdir_get(&d, APK_BLOB_PTR_LEN(dir->name, dir->namelen), db->ctx, APK_BLOB_NULL);
|
||||
if (apk_fsdir_update_perms(&d, dir->mode, dir->uid, dir->gid) != 0)
|
||||
ctx->errors++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -2330,37 +2331,6 @@ static struct apk_db_dir_instance *apk_db_install_directory_entry(struct install
|
|||
return diri;
|
||||
}
|
||||
|
||||
#define TMPNAME_MAX (PATH_MAX + 64)
|
||||
|
||||
static const char *format_tmpname(struct apk_package *pkg, struct apk_db_file *f, char tmpname[static TMPNAME_MAX])
|
||||
{
|
||||
struct apk_digest_ctx dctx;
|
||||
struct apk_digest d;
|
||||
apk_blob_t b = APK_BLOB_PTR_LEN(tmpname, TMPNAME_MAX);
|
||||
|
||||
if (!f) return NULL;
|
||||
|
||||
if (apk_digest_ctx_init(&dctx, APK_DIGEST_SHA256) != 0) return NULL;
|
||||
|
||||
apk_digest_ctx_update(&dctx, pkg->name->name, strlen(pkg->name->name) + 1);
|
||||
apk_digest_ctx_update(&dctx, f->diri->dir->name, f->diri->dir->namelen);
|
||||
apk_digest_ctx_update(&dctx, "/", 1);
|
||||
apk_digest_ctx_update(&dctx, f->name, f->namelen);
|
||||
apk_digest_ctx_final(&dctx, &d);
|
||||
apk_digest_ctx_free(&dctx);
|
||||
|
||||
apk_blob_push_blob(&b, APK_BLOB_PTR_LEN(f->diri->dir->name, f->diri->dir->namelen));
|
||||
if (f->diri->dir->namelen > 0) {
|
||||
apk_blob_push_blob(&b, APK_BLOB_STR("/.apk."));
|
||||
} else {
|
||||
apk_blob_push_blob(&b, APK_BLOB_STR(".apk."));
|
||||
}
|
||||
apk_blob_push_hexdump(&b, APK_BLOB_PTR_LEN((char *)d.data, 24));
|
||||
apk_blob_push_blob(&b, APK_BLOB_PTR_LEN("", 1));
|
||||
|
||||
return tmpname;
|
||||
}
|
||||
|
||||
static int contains_control_character(const char *str)
|
||||
{
|
||||
for (const uint8_t *p = (const uint8_t *) str; *p; p++) {
|
||||
|
@ -2386,7 +2356,7 @@ static int apk_db_install_v3meta(struct apk_extract_ctx *ectx, struct adb_obj *p
|
|||
struct adb_obj triggers, pkginfo, obj;
|
||||
int i;
|
||||
|
||||
apk_pkg_from_adb(db, ctx->pkg, pkg);
|
||||
// Extract the information not available in index
|
||||
adb_ro_obj(pkg, ADBI_PKG_PKGINFO, &pkginfo);
|
||||
apk_deps_from_adb(&ipkg->replaces, db, adb_ro_obj(&pkginfo, ADBI_PI_REPLACES, &obj));
|
||||
ipkg->replaces_priority = adb_ro_int(&pkginfo, ADBI_PI_PRIORITY);
|
||||
|
@ -2424,7 +2394,6 @@ static int apk_db_install_file(struct apk_extract_ctx *ectx, const struct apk_fi
|
|||
struct apk_db_dir_instance *diri = ctx->diri;
|
||||
struct apk_db_file *file, *link_target_file = NULL;
|
||||
int ret = 0, r;
|
||||
char tmpname_file[TMPNAME_MAX], tmpname_link_target[TMPNAME_MAX];
|
||||
|
||||
apk_db_run_pending_script(ctx);
|
||||
if (ae->name[0] == '.') return 0;
|
||||
|
@ -2440,13 +2409,6 @@ static int apk_db_install_file(struct apk_extract_ctx *ectx, const struct apk_fi
|
|||
return 0;
|
||||
}
|
||||
|
||||
if (ae->uvol_name) {
|
||||
apk_warn(out, PKG_VER_FMT": %s: uvol not supported yet",
|
||||
PKG_VER_PRINTF(pkg), ae->name);
|
||||
ipkg->broken_files = 1;
|
||||
return APK_EXTRACT_SKIP_FILE;
|
||||
}
|
||||
|
||||
/* Installable entry */
|
||||
ctx->current_file_size = apk_calc_installed_size(ae->size);
|
||||
if (!S_ISDIR(ae->mode)) {
|
||||
|
@ -2563,12 +2525,7 @@ static int apk_db_install_file(struct apk_extract_ctx *ectx, const struct apk_fi
|
|||
|
||||
/* Extract the file with temporary name */
|
||||
file->acl = apk_db_acl_atomize_digest(db, ae->mode, ae->uid, ae->gid, &ae->xattr_digest);
|
||||
r = apk_extract_file(
|
||||
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, ac);
|
||||
|
||||
r = apk_fs_extract(ac, ae, is, extract_cb, ctx, db->extract_flags, apk_pkg_ctx(pkg));
|
||||
switch (r) {
|
||||
case 0:
|
||||
/* Hardlinks need special care for checksum */
|
||||
|
@ -2596,6 +2553,10 @@ static int apk_db_install_file(struct apk_extract_ctx *ectx, const struct apk_fi
|
|||
ctx->missing_checksum = 1;
|
||||
}
|
||||
break;
|
||||
case -APKE_UVOL_ROOT:
|
||||
case -APKE_UVOL_NOT_AVAILABLE:
|
||||
ipkg->broken_files = 1;
|
||||
break;
|
||||
case -ENOTSUP:
|
||||
ipkg->broken_xattr = 1;
|
||||
break;
|
||||
|
@ -2638,22 +2599,20 @@ static void apk_db_purge_pkg(struct apk_database *db,
|
|||
struct apk_db_dir_instance *diri;
|
||||
struct apk_db_file *file;
|
||||
struct apk_db_file_hash_key key;
|
||||
struct apk_file_info fi;
|
||||
struct apk_fsdir d;
|
||||
struct apk_digest dgst;
|
||||
struct hlist_node *dc, *dn, *fc, *fn;
|
||||
unsigned long hash;
|
||||
char name[TMPNAME_MAX];
|
||||
int ctrl = is_installed ? APK_FS_CTRL_DELETE : APK_FS_CTRL_CANCEL;
|
||||
|
||||
hlist_for_each_entry_safe(diri, dc, dn, &ipkg->owned_dirs, pkg_dirs_list) {
|
||||
apk_blob_t dirname = APK_BLOB_PTR_LEN(diri->dir->name, diri->dir->namelen);
|
||||
if (is_installed) diri->dir->modified = 1;
|
||||
apk_fsdir_get(&d, dirname, db->ctx, apk_pkg_ctx(ipkg->pkg));
|
||||
|
||||
hlist_for_each_entry_safe(file, fc, fn, &diri->owned_files, diri_files_list) {
|
||||
if (is_installed)
|
||||
snprintf(name, sizeof name, DIR_FILE_FMT, DIR_FILE_PRINTF(diri->dir, file));
|
||||
else
|
||||
format_tmpname(ipkg->pkg, file, name);
|
||||
|
||||
key = (struct apk_db_file_hash_key) {
|
||||
.dirname = APK_BLOB_PTR_LEN(diri->dir->name, diri->dir->namelen),
|
||||
.dirname = dirname,
|
||||
.filename = APK_BLOB_PTR_LEN(file->name, file->namelen),
|
||||
};
|
||||
hash = apk_blob_hash_seed(key.filename, diri->dir->hash);
|
||||
|
@ -2661,10 +2620,11 @@ static void apk_db_purge_pkg(struct apk_database *db,
|
|||
(diri->dir->protect_mode == APK_PROTECT_NONE) ||
|
||||
(db->ctx->flags & APK_PURGE) ||
|
||||
(file->csum.type != APK_CHECKSUM_NONE &&
|
||||
apk_fileinfo_get(db->root_fd, name, APK_FI_NOFOLLOW | APK_FI_DIGEST(apk_dbf_digest(file)), &fi, &db->atoms) == 0 &&
|
||||
apk_digest_cmp_csum(&fi.digest, &file->csum) == 0))
|
||||
unlinkat(db->root_fd, name, 0);
|
||||
apk_dbg2(out, "%s", name);
|
||||
apk_fsdir_file_digest(&d, key.filename, apk_dbf_digest(file), &dgst) == 0 &&
|
||||
apk_digest_cmp_csum(&dgst, &file->csum) == 0))
|
||||
apk_fsdir_file_control(&d, key.filename, ctrl);
|
||||
|
||||
apk_dbg2(out, DIR_FILE_FMT, DIR_FILE_PRINTF(diri->dir, file));
|
||||
__hlist_del(fc, &diri->owned_files.first);
|
||||
if (is_installed) {
|
||||
apk_hash_delete_hashed(&db->installed.files, APK_BLOB_BUF(&key), hash);
|
||||
|
@ -2685,22 +2645,22 @@ static void apk_db_migrate_files(struct apk_database *db,
|
|||
struct apk_db_dir *dir;
|
||||
struct apk_db_file *file, *ofile;
|
||||
struct apk_db_file_hash_key key;
|
||||
struct apk_file_info fi;
|
||||
struct hlist_node *dc, *dn, *fc, *fn;
|
||||
struct apk_fsdir d;
|
||||
struct apk_digest dgst;
|
||||
unsigned long hash;
|
||||
char name[PATH_MAX], tmpname[TMPNAME_MAX];
|
||||
int cstype, r;
|
||||
apk_blob_t dirname;
|
||||
int r, ctrl;
|
||||
|
||||
hlist_for_each_entry_safe(diri, dc, dn, &ipkg->owned_dirs, pkg_dirs_list) {
|
||||
dir = diri->dir;
|
||||
dir->modified = 1;
|
||||
dirname = APK_BLOB_PTR_LEN(dir->name, dir->namelen);
|
||||
apk_fsdir_get(&d, dirname, db->ctx, apk_pkg_ctx(ipkg->pkg));
|
||||
|
||||
hlist_for_each_entry_safe(file, fc, fn, &diri->owned_files, diri_files_list) {
|
||||
snprintf(name, sizeof(name), DIR_FILE_FMT, DIR_FILE_PRINTF(diri->dir, file));
|
||||
format_tmpname(ipkg->pkg, file, tmpname);
|
||||
|
||||
key = (struct apk_db_file_hash_key) {
|
||||
.dirname = APK_BLOB_PTR_LEN(dir->name, dir->namelen),
|
||||
.dirname = dirname,
|
||||
.filename = APK_BLOB_PTR_LEN(file->name, file->namelen),
|
||||
};
|
||||
|
||||
|
@ -2710,61 +2670,41 @@ static void apk_db_migrate_files(struct apk_database *db,
|
|||
ofile = (struct apk_db_file *) apk_hash_get_hashed(
|
||||
&db->installed.files, APK_BLOB_BUF(&key), hash);
|
||||
|
||||
/* We want to compare checksums only if one exists
|
||||
* in db, and the file is in a protected path */
|
||||
cstype = APK_CHECKSUM_NONE;
|
||||
if (ofile != NULL && diri->dir->protect_mode != APK_PROTECT_NONE)
|
||||
cstype = APK_FI_DIGEST(apk_dbf_digest(ofile));
|
||||
cstype |= APK_FI_NOFOLLOW;
|
||||
|
||||
r = apk_fileinfo_get(db->root_fd, name, cstype, &fi, &db->atoms);
|
||||
ctrl = APK_FS_CTRL_COMMIT;
|
||||
if (ofile && ofile->diri->pkg->name == NULL) {
|
||||
/* File was from overlay, delete the
|
||||
* packages version */
|
||||
unlinkat(db->root_fd, tmpname, 0);
|
||||
// File was from overlay, delete the package's version
|
||||
ctrl = APK_FS_CTRL_CANCEL;
|
||||
} else if ((diri->dir->protect_mode != APK_PROTECT_NONE) &&
|
||||
(r == 0) &&
|
||||
(ofile == NULL ||
|
||||
ofile->csum.type == APK_CHECKSUM_NONE ||
|
||||
apk_digest_cmp_csum(&fi.digest, &ofile->csum) != 0)) {
|
||||
/* Protected directory, with file without
|
||||
* db entry, or local modifications.
|
||||
*
|
||||
* Delete the apk-new if it's identical with the
|
||||
* existing file */
|
||||
if (ofile == NULL ||
|
||||
ofile->csum.type != file->csum.type)
|
||||
apk_fileinfo_get(db->root_fd, name,
|
||||
APK_FI_NOFOLLOW |APK_FI_DIGEST(apk_dbf_digest(file)),
|
||||
&fi, &db->atoms);
|
||||
(!ofile || ofile->csum.type == APK_CHECKSUM_NONE ||
|
||||
(apk_fsdir_file_digest(&d, key.filename, apk_dbf_digest(ofile), &dgst) == 0 &&
|
||||
apk_digest_cmp_csum(&dgst, &ofile->csum) != 0))) {
|
||||
// Protected directory, and a file without db entry
|
||||
// or with local modifications. Keep the filesystem file.
|
||||
// Determine if the package's file should be kept as .apk-new
|
||||
if ((db->ctx->flags & APK_CLEAN_PROTECTED) ||
|
||||
(file->csum.type != APK_CHECKSUM_NONE &&
|
||||
apk_digest_cmp_csum(&fi.digest, &file->csum) == 0)) {
|
||||
unlinkat(db->root_fd, tmpname, 0);
|
||||
(apk_fsdir_file_digest(&d, key.filename, apk_dbf_digest(file), &dgst) == 0 &&
|
||||
apk_digest_cmp_csum(&dgst, &file->csum) == 0))) {
|
||||
// No .apk-new files allowed, or the file on disk has the same
|
||||
// hash as the file from new package. Keep the on disk one.
|
||||
ctrl = APK_FS_CTRL_CANCEL;
|
||||
} else {
|
||||
snprintf(name, sizeof name,
|
||||
DIR_FILE_FMT ".apk-new",
|
||||
DIR_FILE_PRINTF(diri->dir, file));
|
||||
if (renameat(db->root_fd, tmpname,
|
||||
db->root_fd, name) != 0) {
|
||||
apk_err(out, PKG_VER_FMT": failed to rename %s to %s.",
|
||||
PKG_VER_PRINTF(ipkg->pkg),
|
||||
tmpname, name);
|
||||
ipkg->broken_files = 1;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
/* Overwrite the old file */
|
||||
if (renameat(db->root_fd, tmpname,
|
||||
db->root_fd, name) != 0) {
|
||||
apk_err(out, PKG_VER_FMT": failed to rename %s to %s.",
|
||||
PKG_VER_PRINTF(ipkg->pkg), tmpname, name);
|
||||
ipkg->broken_files = 1;
|
||||
// All files difference. Use the package's file as .apk-new.
|
||||
ctrl = APK_FS_CTRL_APKNEW;
|
||||
}
|
||||
}
|
||||
|
||||
/* Claim ownership of the file in db */
|
||||
// Commit changes
|
||||
r = apk_fsdir_file_control(&d, key.filename, ctrl);
|
||||
if (r < 0) {
|
||||
apk_err(out, PKG_VER_FMT": failed to commit " DIR_FILE_FMT ": %s",
|
||||
PKG_VER_PRINTF(ipkg->pkg),
|
||||
DIR_FILE_PRINTF(diri->dir, file),
|
||||
apk_error_str(r));
|
||||
ipkg->broken_files = 1;
|
||||
}
|
||||
|
||||
// Claim ownership of the file in db
|
||||
if (ofile != file) {
|
||||
if (ofile != NULL) {
|
||||
hlist_del(&ofile->diri_files_list,
|
||||
|
|
235
src/extract.c
235
src/extract.c
|
@ -1,235 +0,0 @@
|
|||
/* extract.c - Alpine Package Keeper (APK)
|
||||
*
|
||||
* Copyright (C) 2005-2008 Natanael Copa <n@tanael.org>
|
||||
* Copyright (C) 2008-2011 Timo Teräs <timo.teras@iki.fi>
|
||||
* All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-only
|
||||
*/
|
||||
|
||||
#include <spawn.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/xattr.h>
|
||||
|
||||
#include "apk_context.h"
|
||||
#include "apk_extract.h"
|
||||
|
||||
static int uvol_run(struct apk_ctx *ac, char *action, const char *volname, char *arg1, char *arg2)
|
||||
{
|
||||
struct apk_out *out = &ac->out;
|
||||
pid_t pid;
|
||||
int r, status;
|
||||
char *argv[] = { (char*)apk_ctx_get_uvol(ac), action, (char*) volname, arg1, arg2, 0 };
|
||||
posix_spawn_file_actions_t act;
|
||||
|
||||
posix_spawn_file_actions_init(&act);
|
||||
posix_spawn_file_actions_addclose(&act, STDIN_FILENO);
|
||||
r = posix_spawn(&pid, apk_ctx_get_uvol(ac), &act, 0, argv, environ);
|
||||
posix_spawn_file_actions_destroy(&act);
|
||||
if (r != 0) {
|
||||
apk_err(out, "%s: uvol exec error: %s", volname, apk_error_str(r));
|
||||
return r;
|
||||
}
|
||||
while (waitpid(pid, &status, 0) < 0 && errno == EINTR);
|
||||
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
|
||||
apk_err(out, "%s: uvol exited with error %d", volname, WEXITSTATUS(status));
|
||||
return -APKE_UVOL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int uvol_extract(struct apk_ctx *ac, const char *volname, char *arg1, off_t sz,
|
||||
struct apk_istream *is, apk_progress_cb cb, void *cb_ctx)
|
||||
{
|
||||
struct apk_out *out = &ac->out;
|
||||
struct apk_ostream *os;
|
||||
pid_t pid;
|
||||
int r, status, pipefds[2];
|
||||
char *argv[] = { (char*)apk_ctx_get_uvol(ac), "write", (char*) volname, arg1, 0 };
|
||||
posix_spawn_file_actions_t act;
|
||||
|
||||
if (pipe2(pipefds, O_CLOEXEC) != 0) return -errno;
|
||||
|
||||
posix_spawn_file_actions_init(&act);
|
||||
posix_spawn_file_actions_adddup2(&act, pipefds[0], STDIN_FILENO);
|
||||
r = posix_spawn(&pid, apk_ctx_get_uvol(ac), &act, 0, argv, environ);
|
||||
posix_spawn_file_actions_destroy(&act);
|
||||
if (r != 0) {
|
||||
apk_err(out, "%s: uvol exec error: %s", volname, apk_error_str(r));
|
||||
return r;
|
||||
}
|
||||
close(pipefds[0]);
|
||||
os = apk_ostream_to_fd(pipefds[1]);
|
||||
apk_stream_copy(is, os, sz, cb, cb_ctx, 0);
|
||||
r = apk_ostream_close(os);
|
||||
if (r != 0) {
|
||||
if (r >= 0) r = -APKE_UVOL;
|
||||
apk_err(out, "%s: uvol write error: %s", volname, apk_error_str(r));
|
||||
return r;
|
||||
}
|
||||
|
||||
while (waitpid(pid, &status, 0) < 0 && errno == EINTR);
|
||||
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
|
||||
apk_err(out, "%s: uvol exited with error %d", volname, WEXITSTATUS(status));
|
||||
return -APKE_UVOL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int apk_extract_volume(struct apk_ctx *ac, const struct apk_file_info *fi,
|
||||
struct apk_istream *is, apk_progress_cb cb, void *cb_ctx)
|
||||
{
|
||||
char size[64];
|
||||
int r;
|
||||
|
||||
snprintf(size, sizeof size, "%ju", fi->size);
|
||||
r = uvol_run(ac, "create", fi->uvol_name, size, "ro");
|
||||
if (r != 0) return r;
|
||||
|
||||
r = uvol_extract(ac, fi->uvol_name, size, fi->size, is, cb, cb_ctx);
|
||||
if (r == 0) r = uvol_run(ac, "up", fi->uvol_name, 0, 0);
|
||||
if (r != 0) uvol_run(ac, "remove", fi->uvol_name, 0, 0);
|
||||
return r;
|
||||
}
|
||||
|
||||
int apk_extract_file(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 extract_flags, struct apk_ctx *ac)
|
||||
{
|
||||
struct apk_out *out = &ac->out;
|
||||
struct apk_xattr *xattr;
|
||||
const char *fn = extract_name ?: ae->name;
|
||||
int fd, r = -1, atflags = 0, ret = 0;
|
||||
|
||||
if (ae->uvol_name && is) {
|
||||
if (extract_name || link_target) return -APKE_UVOL;
|
||||
return apk_extract_volume(ac, ae, is, cb, cb_ctx);
|
||||
}
|
||||
|
||||
if (!S_ISDIR(ae->mode) && !(extract_flags & APK_EXTRACTF_NO_OVERWRITE)) {
|
||||
if (unlinkat(atfd, fn, 0) != 0 && errno != ENOENT) return -errno;
|
||||
}
|
||||
|
||||
switch (ae->mode & S_IFMT) {
|
||||
case S_IFDIR:
|
||||
r = mkdirat(atfd, fn, ae->mode & 07777);
|
||||
if (r < 0 && errno != EEXIST)
|
||||
ret = -errno;
|
||||
break;
|
||||
case S_IFREG:
|
||||
if (ae->link_target == NULL) {
|
||||
int flags = O_RDWR | O_CREAT | O_TRUNC | O_CLOEXEC | O_EXCL;
|
||||
int fd = openat(atfd, fn, flags, ae->mode & 07777);
|
||||
if (fd < 0) {
|
||||
ret = -errno;
|
||||
break;
|
||||
}
|
||||
struct apk_ostream *os = apk_ostream_to_fd(fd);
|
||||
if (IS_ERR(os)) {
|
||||
ret = PTR_ERR(os);
|
||||
break;
|
||||
}
|
||||
apk_stream_copy(is, os, ae->size, cb, cb_ctx, 0);
|
||||
r = apk_ostream_close(os);
|
||||
if (r < 0) {
|
||||
unlinkat(atfd, fn, 0);
|
||||
ret = r;
|
||||
}
|
||||
} else {
|
||||
r = linkat(atfd, link_target ?: ae->link_target, atfd, fn, 0);
|
||||
if (r < 0) ret = -errno;
|
||||
}
|
||||
break;
|
||||
case S_IFLNK:
|
||||
r = symlinkat(link_target ?: ae->link_target, atfd, fn);
|
||||
if (r < 0) ret = -errno;
|
||||
atflags |= AT_SYMLINK_NOFOLLOW;
|
||||
break;
|
||||
case S_IFBLK:
|
||||
case S_IFCHR:
|
||||
case S_IFIFO:
|
||||
r = mknodat(atfd, fn, ae->mode, ae->device);
|
||||
if (r < 0) ret = -errno;
|
||||
break;
|
||||
}
|
||||
if (ret) {
|
||||
apk_err(out, "Failed to create %s: %s", ae->name, strerror(-ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
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",
|
||||
fn, strerror(errno));
|
||||
if (!ret) ret = -errno;
|
||||
}
|
||||
|
||||
/* chown resets suid bit so we need set it again */
|
||||
if (ae->mode & 07000) {
|
||||
r = fchmodat(atfd, fn, ae->mode & 07777, atflags);
|
||||
if (r < 0) {
|
||||
apk_err(out, "Failed to set file permissions on %s: %s",
|
||||
fn, strerror(errno));
|
||||
if (!ret) ret = -errno;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* extract xattrs */
|
||||
if (!S_ISLNK(ae->mode) && ae->xattrs && ae->xattrs->num) {
|
||||
r = 0;
|
||||
fd = openat(atfd, fn, O_RDWR);
|
||||
if (fd >= 0) {
|
||||
foreach_array_item(xattr, ae->xattrs) {
|
||||
if (fsetxattr(fd, xattr->name, xattr->value.ptr, xattr->value.len, 0) < 0) {
|
||||
r = -errno;
|
||||
if (r != -ENOTSUP) break;
|
||||
}
|
||||
}
|
||||
close(fd);
|
||||
} else {
|
||||
r = -errno;
|
||||
}
|
||||
if (r) {
|
||||
if (r != -ENOTSUP)
|
||||
apk_err(out, "Failed to set xattrs on %s: %s",
|
||||
fn, strerror(-r));
|
||||
if (!ret) ret = r;
|
||||
}
|
||||
}
|
||||
|
||||
if (!S_ISLNK(ae->mode)) {
|
||||
/* preserve modification time */
|
||||
struct timespec times[2];
|
||||
|
||||
times[0].tv_sec = times[1].tv_sec = ae->mtime;
|
||||
times[0].tv_nsec = times[1].tv_nsec = 0;
|
||||
r = utimensat(atfd, fn, times, atflags);
|
||||
if (r < 0) {
|
||||
apk_err(out, "Failed to preserve modification time on %s: %s",
|
||||
fn, strerror(errno));
|
||||
if (!ret || ret == -ENOTSUP) ret = -errno;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int apk_extract(struct apk_extract_ctx *ectx, struct apk_istream *is)
|
||||
{
|
||||
void *sig;
|
||||
|
||||
if (IS_ERR(is)) return PTR_ERR(is);
|
||||
|
||||
sig = apk_istream_peek(is, 4);
|
||||
if (IS_ERR(sig)) return apk_istream_close_error(is, PTR_ERR(sig));
|
||||
|
||||
if (memcmp(sig, "ADB", 3) == 0) return apk_extract_v3(ectx, is);
|
||||
return apk_extract_v2(ectx, is);
|
||||
}
|
|
@ -22,15 +22,6 @@ struct apk_extract_v3_ctx {
|
|||
struct apk_pathbuilder pb;
|
||||
};
|
||||
|
||||
static const char *uvol_detect(struct apk_ctx *ac, const char *path)
|
||||
{
|
||||
if (!apk_ctx_get_uvol(ac)) return 0;
|
||||
if (strncmp(path, "uvol", 4) != 0) return 0;
|
||||
if (path[4] == 0) return path;
|
||||
if (path[4] == '/') return &path[5];
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void apk_extract_v3_acl(struct apk_file_info *fi, struct adb_obj *o, struct apk_id_cache *idc)
|
||||
{
|
||||
fi->mode = adb_ro_int(o, ADBI_ACL_MODE);
|
||||
|
@ -41,11 +32,9 @@ static void apk_extract_v3_acl(struct apk_file_info *fi, struct adb_obj *o, stru
|
|||
static int apk_extract_v3_file(struct apk_extract_ctx *ectx, off_t sz, struct apk_istream *is)
|
||||
{
|
||||
struct apk_extract_v3_ctx *ctx = ectx->pctx;
|
||||
struct apk_ctx *ac = ectx->ac;
|
||||
const char *path_name = apk_pathbuilder_cstr(&ctx->pb);
|
||||
struct apk_file_info fi = {
|
||||
.name = path_name,
|
||||
.uvol_name = uvol_detect(ac, path_name),
|
||||
.size = adb_ro_int(&ctx->file, ADBI_FI_SIZE),
|
||||
.mtime = adb_ro_int(&ctx->file, ADBI_FI_MTIME),
|
||||
};
|
||||
|
@ -92,23 +81,18 @@ static int apk_extract_v3_file(struct apk_extract_ctx *ectx, off_t sz, struct ap
|
|||
if (fi.digest.alg == APK_DIGEST_NONE) return -APKE_ADB_SCHEMA;
|
||||
|
||||
fi.mode |= S_IFREG;
|
||||
r = ectx->ops->file(ectx, &fi, apk_istream_verify(&dis, is, &fi.digest));
|
||||
if (r == APK_EXTRACT_SKIP_FILE)
|
||||
return 0;
|
||||
r = ectx->ops->file(ectx, &fi, apk_istream_verify(&dis, is, fi.size, &fi.digest));
|
||||
return apk_istream_close_error(&dis.is, r);
|
||||
}
|
||||
|
||||
static int apk_extract_v3_directory(struct apk_extract_ctx *ectx)
|
||||
{
|
||||
struct apk_extract_v3_ctx *ctx = ectx->pctx;
|
||||
struct apk_ctx *ac = ectx->ac;
|
||||
struct apk_file_info fi = {
|
||||
.name = apk_pathbuilder_cstr(&ctx->pb),
|
||||
};
|
||||
struct adb_obj acl;
|
||||
|
||||
if (uvol_detect(ac, fi.name)) return 0;
|
||||
|
||||
apk_extract_v3_acl(&fi, adb_ro_obj(&ctx->path, ADBI_DI_ACL, &acl), apk_ctx_get_id_cache(ectx->ac));
|
||||
fi.mode |= S_IFDIR;
|
||||
|
||||
|
@ -263,3 +247,16 @@ int apk_extract_v3(struct apk_extract_ctx *ectx, struct apk_istream *is)
|
|||
|
||||
return r;
|
||||
}
|
||||
|
||||
int apk_extract(struct apk_extract_ctx *ectx, struct apk_istream *is)
|
||||
{
|
||||
void *sig;
|
||||
|
||||
if (IS_ERR(is)) return PTR_ERR(is);
|
||||
|
||||
sig = apk_istream_peek(is, 4);
|
||||
if (IS_ERR(sig)) return apk_istream_close_error(is, PTR_ERR(sig));
|
||||
|
||||
if (memcmp(sig, "ADB", 3) == 0) return apk_extract_v3(ectx, is);
|
||||
return apk_extract_v2(ectx, is);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,316 @@
|
|||
/* fsops_sys.c - Alpine Package Keeper (APK)
|
||||
*
|
||||
* Copyright (C) 2005-2008 Natanael Copa <n@tanael.org>
|
||||
* Copyright (C) 2008-2011 Timo Teräs <timo.teras@iki.fi>
|
||||
* All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-only
|
||||
*/
|
||||
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/xattr.h>
|
||||
|
||||
#include "apk_fs.h"
|
||||
|
||||
#define TMPNAME_MAX (PATH_MAX + 64)
|
||||
|
||||
static int fsys_dir_create(struct apk_fsdir *d, mode_t mode)
|
||||
{
|
||||
if (mkdirat(apk_ctx_fd_dest(d->ac), apk_pathbuilder_cstr(&d->pb), mode) < 0)
|
||||
return -errno;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsys_dir_delete(struct apk_fsdir *d)
|
||||
{
|
||||
if (unlinkat(apk_ctx_fd_dest(d->ac), apk_pathbuilder_cstr(&d->pb), AT_REMOVEDIR) < 0)
|
||||
return -errno;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsys_dir_check(struct apk_fsdir *d, mode_t mode, uid_t uid, gid_t gid)
|
||||
{
|
||||
struct stat st;
|
||||
|
||||
if (fstatat(apk_ctx_fd_dest(d->ac), apk_pathbuilder_cstr(&d->pb), &st, AT_SYMLINK_NOFOLLOW) != 0)
|
||||
return -errno;
|
||||
|
||||
if ((st.st_mode & 07777) != (mode & 07777) || st.st_uid != uid || st.st_gid != gid)
|
||||
return APK_FS_DIR_MODIFIED;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsys_dir_update_perms(struct apk_fsdir *d, mode_t mode, uid_t uid, gid_t gid)
|
||||
{
|
||||
struct stat st;
|
||||
int fd = apk_ctx_fd_dest(d->ac), rc = 0;
|
||||
const char *dirname = apk_pathbuilder_cstr(&d->pb);
|
||||
|
||||
if (fstatat(fd, dirname, &st, AT_SYMLINK_NOFOLLOW) != 0)
|
||||
return -errno;
|
||||
|
||||
if ((st.st_mode & 07777) != (mode & 07777)) {
|
||||
if (fchmodat(fd, dirname, mode, 0) < 0)
|
||||
rc = -errno;
|
||||
}
|
||||
if (st.st_uid != uid || st.st_gid != gid) {
|
||||
if (fchownat(fd, dirname, uid, gid, 0) < 0)
|
||||
rc = -errno;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static const char *format_tmpname(struct apk_digest_ctx *dctx, apk_blob_t pkgctx,
|
||||
apk_blob_t dirname, apk_blob_t fullname, char tmpname[static TMPNAME_MAX])
|
||||
{
|
||||
struct apk_digest d;
|
||||
apk_blob_t b = APK_BLOB_PTR_LEN(tmpname, TMPNAME_MAX);
|
||||
|
||||
apk_digest_ctx_reset(dctx, APK_DIGEST_SHA256);
|
||||
apk_digest_ctx_update(dctx, pkgctx.ptr, pkgctx.len);
|
||||
apk_digest_ctx_update(dctx, fullname.ptr, fullname.len);
|
||||
apk_digest_ctx_final(dctx, &d);
|
||||
|
||||
apk_blob_push_blob(&b, dirname);
|
||||
if (dirname.len > 0) {
|
||||
apk_blob_push_blob(&b, APK_BLOB_STR("/.apk."));
|
||||
} else {
|
||||
apk_blob_push_blob(&b, APK_BLOB_STR(".apk."));
|
||||
}
|
||||
apk_blob_push_hexdump(&b, APK_BLOB_PTR_LEN((char *)d.data, 24));
|
||||
apk_blob_push_blob(&b, APK_BLOB_PTR_LEN("", 1));
|
||||
|
||||
return tmpname;
|
||||
}
|
||||
|
||||
static apk_blob_t get_dirname(const char *fullname)
|
||||
{
|
||||
char *slash = strrchr(fullname, '/');
|
||||
if (!slash) return APK_BLOB_NULL;
|
||||
return APK_BLOB_PTR_PTR((char*)fullname, slash);
|
||||
}
|
||||
|
||||
static int fsys_file_extract(struct apk_ctx *ac, const struct apk_file_info *fi, struct apk_istream *is,
|
||||
apk_progress_cb cb, void *cb_ctx, unsigned int extract_flags, apk_blob_t pkgctx)
|
||||
{
|
||||
char tmpname_file[TMPNAME_MAX], tmpname_linktarget[TMPNAME_MAX];
|
||||
struct apk_out *out = &ac->out;
|
||||
struct apk_xattr *xattr;
|
||||
int fd, r = -1, atflags = 0, ret = 0;
|
||||
int atfd = apk_ctx_fd_dest(ac);
|
||||
const char *fn = fi->name, *link_target = fi->link_target;
|
||||
|
||||
if (pkgctx.ptr) {
|
||||
fn = format_tmpname(&ac->dctx, pkgctx, get_dirname(fn), APK_BLOB_STR(fn), tmpname_file);
|
||||
if (link_target)
|
||||
link_target = format_tmpname(&ac->dctx, pkgctx, get_dirname(link_target), APK_BLOB_STR(link_target), tmpname_linktarget);
|
||||
}
|
||||
|
||||
if (!S_ISDIR(fi->mode) && !(extract_flags & APK_FSEXTRACTF_NO_OVERWRITE)) {
|
||||
if (unlinkat(atfd, fn, 0) != 0 && errno != ENOENT) return -errno;
|
||||
}
|
||||
|
||||
switch (fi->mode & S_IFMT) {
|
||||
case S_IFDIR:
|
||||
r = mkdirat(atfd, fn, fi->mode & 07777);
|
||||
if (r < 0 && errno != EEXIST)
|
||||
ret = -errno;
|
||||
break;
|
||||
case S_IFREG:
|
||||
if (fi->link_target == NULL) {
|
||||
int flags = O_RDWR | O_CREAT | O_TRUNC | O_CLOEXEC | O_EXCL;
|
||||
int fd = openat(atfd, fn, flags, fi->mode & 07777);
|
||||
if (fd < 0) {
|
||||
ret = -errno;
|
||||
break;
|
||||
}
|
||||
struct apk_ostream *os = apk_ostream_to_fd(fd);
|
||||
if (IS_ERR(os)) {
|
||||
ret = PTR_ERR(os);
|
||||
break;
|
||||
}
|
||||
apk_stream_copy(is, os, fi->size, cb, cb_ctx, 0);
|
||||
r = apk_ostream_close(os);
|
||||
if (r < 0) {
|
||||
unlinkat(atfd, fn, 0);
|
||||
ret = r;
|
||||
}
|
||||
} else {
|
||||
r = linkat(atfd, link_target, atfd, fn, 0);
|
||||
if (r < 0) ret = -errno;
|
||||
}
|
||||
break;
|
||||
case S_IFLNK:
|
||||
r = symlinkat(link_target, atfd, fn);
|
||||
if (r < 0) ret = -errno;
|
||||
atflags |= AT_SYMLINK_NOFOLLOW;
|
||||
break;
|
||||
case S_IFBLK:
|
||||
case S_IFCHR:
|
||||
case S_IFIFO:
|
||||
r = mknodat(atfd, fn, fi->mode, fi->device);
|
||||
if (r < 0) ret = -errno;
|
||||
break;
|
||||
}
|
||||
if (ret) {
|
||||
apk_err(out, "Failed to create %s: %s", fi->name, strerror(-ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!(extract_flags & APK_FSEXTRACTF_NO_CHOWN)) {
|
||||
r = fchownat(atfd, fn, fi->uid, fi->gid, atflags);
|
||||
if (r < 0) {
|
||||
apk_err(out, "Failed to set ownership on %s: %s",
|
||||
fn, strerror(errno));
|
||||
if (!ret) ret = -errno;
|
||||
}
|
||||
|
||||
/* chown resets suid bit so we need set it again */
|
||||
if (fi->mode & 07000) {
|
||||
r = fchmodat(atfd, fn, fi->mode & 07777, atflags);
|
||||
if (r < 0) {
|
||||
apk_err(out, "Failed to set file permissions on %s: %s",
|
||||
fn, strerror(errno));
|
||||
if (!ret) ret = -errno;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* extract xattrs */
|
||||
if (!S_ISLNK(fi->mode) && fi->xattrs && fi->xattrs->num) {
|
||||
r = 0;
|
||||
fd = openat(atfd, fn, O_RDWR);
|
||||
if (fd >= 0) {
|
||||
foreach_array_item(xattr, fi->xattrs) {
|
||||
if (fsetxattr(fd, xattr->name, xattr->value.ptr, xattr->value.len, 0) < 0) {
|
||||
r = -errno;
|
||||
if (r != -ENOTSUP) break;
|
||||
}
|
||||
}
|
||||
close(fd);
|
||||
} else {
|
||||
r = -errno;
|
||||
}
|
||||
if (r) {
|
||||
if (r != -ENOTSUP)
|
||||
apk_err(out, "Failed to set xattrs on %s: %s",
|
||||
fn, strerror(-r));
|
||||
if (!ret) ret = r;
|
||||
}
|
||||
}
|
||||
|
||||
if (!S_ISLNK(fi->mode)) {
|
||||
/* preserve modification time */
|
||||
struct timespec times[2];
|
||||
|
||||
times[0].tv_sec = times[1].tv_sec = fi->mtime;
|
||||
times[0].tv_nsec = times[1].tv_nsec = 0;
|
||||
r = utimensat(atfd, fn, times, atflags);
|
||||
if (r < 0) {
|
||||
apk_err(out, "Failed to preserve modification time on %s: %s",
|
||||
fn, strerror(errno));
|
||||
if (!ret || ret == -ENOTSUP) ret = -errno;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int fsys_file_control(struct apk_fsdir *d, apk_blob_t filename, int ctrl)
|
||||
{
|
||||
struct apk_ctx *ac = d->ac;
|
||||
char tmpname[TMPNAME_MAX], apknewname[TMPNAME_MAX];
|
||||
const char *fn;
|
||||
int rc = 0, atfd = apk_ctx_fd_dest(d->ac);
|
||||
apk_blob_t dirname = apk_pathbuilder_get(&d->pb);
|
||||
|
||||
apk_pathbuilder_pushb(&d->pb, filename);
|
||||
fn = apk_pathbuilder_cstr(&d->pb);
|
||||
|
||||
switch (ctrl) {
|
||||
case APK_FS_CTRL_COMMIT:
|
||||
// rename tmpname -> realname
|
||||
if (renameat(atfd, format_tmpname(&ac->dctx, d->pkgctx, dirname, apk_pathbuilder_get(&d->pb), tmpname),
|
||||
atfd, fn) < 0)
|
||||
rc = -errno;
|
||||
break;
|
||||
case APK_FS_CTRL_APKNEW:
|
||||
// rename tmpname -> realname.apk-new
|
||||
snprintf(apknewname, sizeof apknewname, "%s%s", fn, ".apk-new");
|
||||
if (renameat(atfd, format_tmpname(&ac->dctx, d->pkgctx, dirname, apk_pathbuilder_get(&d->pb), tmpname),
|
||||
atfd, apknewname) < 0)
|
||||
rc = -errno;
|
||||
break;
|
||||
case APK_FS_CTRL_CANCEL:
|
||||
// unlink tmpname
|
||||
if (unlinkat(atfd, format_tmpname(&ac->dctx, d->pkgctx, dirname, apk_pathbuilder_get(&d->pb), tmpname), 0) < 0)
|
||||
rc = -errno;
|
||||
break;
|
||||
case APK_FS_CTRL_DELETE:
|
||||
// unlink realname
|
||||
if (unlinkat(atfd, fn, 0) < 0)
|
||||
rc = -errno;
|
||||
break;
|
||||
default:
|
||||
rc = -ENOSYS;
|
||||
break;
|
||||
}
|
||||
|
||||
apk_pathbuilder_pop(&d->pb);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int fsys_file_digest(struct apk_fsdir *d, apk_blob_t filename, uint8_t alg, struct apk_digest *dgst)
|
||||
{
|
||||
struct apk_ctx *ac = d->ac;
|
||||
struct apk_istream *is;
|
||||
apk_blob_t blob;
|
||||
|
||||
apk_pathbuilder_pushb(&d->pb, filename);
|
||||
is = apk_istream_from_file(apk_ctx_fd_dest(ac), apk_pathbuilder_cstr(&d->pb));
|
||||
apk_pathbuilder_pop(&d->pb);
|
||||
if (IS_ERR(is)) return PTR_ERR(is);
|
||||
|
||||
apk_digest_ctx_reset(&ac->dctx, alg);
|
||||
while (apk_istream_get_all(is, &blob) == 0)
|
||||
apk_digest_ctx_update(&ac->dctx, blob.ptr, blob.len);
|
||||
apk_digest_ctx_final(&ac->dctx, dgst);
|
||||
return apk_istream_close(is);
|
||||
}
|
||||
|
||||
static const struct apk_fsdir_ops fsdir_ops_fsys = {
|
||||
.dir_create = fsys_dir_create,
|
||||
.dir_delete = fsys_dir_delete,
|
||||
.dir_check = fsys_dir_check,
|
||||
.dir_update_perms = fsys_dir_update_perms,
|
||||
.file_extract = fsys_file_extract,
|
||||
.file_control = fsys_file_control,
|
||||
.file_digest = fsys_file_digest,
|
||||
};
|
||||
|
||||
static const struct apk_fsdir_ops *apk_fsops_get(apk_blob_t dir)
|
||||
{
|
||||
if (dir.len >= 4 && memcmp(dir.ptr, "uvol", 4) == 0 && (dir.len == 4 || dir.ptr[4] == '/')) {
|
||||
extern const struct apk_fsdir_ops fsdir_ops_uvol;
|
||||
return &fsdir_ops_uvol;
|
||||
}
|
||||
|
||||
return &fsdir_ops_fsys;
|
||||
}
|
||||
|
||||
int apk_fs_extract(struct apk_ctx *ac, const struct apk_file_info *fi, struct apk_istream *is,
|
||||
apk_progress_cb cb, void *cb_ctx, unsigned int extract_flags, apk_blob_t pkgctx)
|
||||
{
|
||||
const struct apk_fsdir_ops *ops = apk_fsops_get(APK_BLOB_PTR_LEN((char*)fi->name, strnlen(fi->name, 5)));
|
||||
return ops->file_extract(ac, fi, is, cb, cb_ctx, extract_flags, pkgctx);
|
||||
}
|
||||
|
||||
void apk_fsdir_get(struct apk_fsdir *d, apk_blob_t dir, struct apk_ctx *ac, apk_blob_t pkgctx)
|
||||
{
|
||||
d->ac = ac;
|
||||
d->pkgctx = pkgctx;
|
||||
d->ops = apk_fsops_get(dir);
|
||||
apk_pathbuilder_setb(&d->pb, dir);
|
||||
}
|
|
@ -0,0 +1,162 @@
|
|||
/* fsops_uvol.c - Alpine Package Keeper (APK)
|
||||
*
|
||||
* Copyright (C) 2005-2008 Natanael Copa <n@tanael.org>
|
||||
* Copyright (C) 2008-2011 Timo Teräs <timo.teras@iki.fi>
|
||||
* All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-only
|
||||
*/
|
||||
|
||||
#include <spawn.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include "apk_context.h"
|
||||
#include "apk_fs.h"
|
||||
|
||||
static int uvol_run(struct apk_ctx *ac, char *action, const char *volname, char *arg1, char *arg2)
|
||||
{
|
||||
struct apk_out *out = &ac->out;
|
||||
pid_t pid;
|
||||
int r, status;
|
||||
char *argv[] = { (char*)apk_ctx_get_uvol(ac), action, (char*) volname, arg1, arg2, 0 };
|
||||
posix_spawn_file_actions_t act;
|
||||
|
||||
posix_spawn_file_actions_init(&act);
|
||||
posix_spawn_file_actions_addclose(&act, STDIN_FILENO);
|
||||
r = posix_spawn(&pid, apk_ctx_get_uvol(ac), &act, 0, argv, environ);
|
||||
posix_spawn_file_actions_destroy(&act);
|
||||
if (r != 0) {
|
||||
apk_err(out, "%s: uvol exec error: %s", volname, apk_error_str(r));
|
||||
return r;
|
||||
}
|
||||
while (waitpid(pid, &status, 0) < 0 && errno == EINTR);
|
||||
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
|
||||
apk_err(out, "%s: uvol exited with error %d", volname, WEXITSTATUS(status));
|
||||
return -APKE_UVOL_ERROR;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int uvol_extract(struct apk_ctx *ac, const char *volname, char *arg1, off_t sz,
|
||||
struct apk_istream *is, apk_progress_cb cb, void *cb_ctx)
|
||||
{
|
||||
struct apk_out *out = &ac->out;
|
||||
struct apk_ostream *os;
|
||||
pid_t pid;
|
||||
int r, status, pipefds[2];
|
||||
char *argv[] = { (char*)apk_ctx_get_uvol(ac), "write", (char*) volname, arg1, 0 };
|
||||
posix_spawn_file_actions_t act;
|
||||
|
||||
if (pipe2(pipefds, O_CLOEXEC) != 0) return -errno;
|
||||
|
||||
posix_spawn_file_actions_init(&act);
|
||||
posix_spawn_file_actions_adddup2(&act, pipefds[0], STDIN_FILENO);
|
||||
r = posix_spawn(&pid, apk_ctx_get_uvol(ac), &act, 0, argv, environ);
|
||||
posix_spawn_file_actions_destroy(&act);
|
||||
if (r != 0) {
|
||||
apk_err(out, "%s: uvol exec error: %s", volname, apk_error_str(r));
|
||||
return r;
|
||||
}
|
||||
close(pipefds[0]);
|
||||
os = apk_ostream_to_fd(pipefds[1]);
|
||||
apk_stream_copy(is, os, sz, cb, cb_ctx, 0);
|
||||
r = apk_ostream_close(os);
|
||||
if (r != 0) {
|
||||
if (r >= 0) r = -APKE_UVOL_ERROR;
|
||||
apk_err(out, "%s: uvol write error: %s", volname, apk_error_str(r));
|
||||
return r;
|
||||
}
|
||||
|
||||
while (waitpid(pid, &status, 0) < 0 && errno == EINTR);
|
||||
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
|
||||
apk_err(out, "%s: uvol exited with error %d", volname, WEXITSTATUS(status));
|
||||
return -APKE_UVOL_ERROR;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int uvol_dir_create(struct apk_fsdir *d, mode_t mode)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int uvol_dir_delete(struct apk_fsdir *d)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int uvol_dir_check(struct apk_fsdir *d, mode_t mode, uid_t uid, gid_t gid)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int uvol_dir_update_perms(struct apk_fsdir *d, mode_t mode, uid_t uid, gid_t gid)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int uvol_file_extract(struct apk_ctx *ac, const struct apk_file_info *fi, struct apk_istream *is,
|
||||
apk_progress_cb cb, void *cb_ctx, unsigned int extract_flags, apk_blob_t pkgctx)
|
||||
{
|
||||
char size[64];
|
||||
const char *uvol_name;
|
||||
int r;
|
||||
|
||||
if (IS_ERR(ac->uvol)) return PTR_ERR(ac->uvol);
|
||||
|
||||
uvol_name = strrchr(fi->name, '/');
|
||||
uvol_name = uvol_name ? uvol_name + 1 : fi->name;
|
||||
|
||||
snprintf(size, sizeof size, "%ju", fi->size);
|
||||
r = uvol_run(ac, "create", uvol_name, size, "ro");
|
||||
if (r != 0) return r;
|
||||
|
||||
r = uvol_extract(ac, uvol_name, size, fi->size, is, cb, cb_ctx);
|
||||
if (r == 0 && !pkgctx.ptr)
|
||||
r = uvol_run(ac, "up", uvol_name, 0, 0);
|
||||
|
||||
if (r != 0) uvol_run(ac, "remove", uvol_name, 0, 0);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int uvol_file_control(struct apk_fsdir *d, apk_blob_t filename, int ctrl)
|
||||
{
|
||||
struct apk_ctx *ac = d->ac;
|
||||
struct apk_pathbuilder pb;
|
||||
const char *uvol_name;
|
||||
|
||||
if (IS_ERR(ac->uvol)) return PTR_ERR(ac->uvol);
|
||||
|
||||
apk_pathbuilder_setb(&pb, filename);
|
||||
uvol_name = apk_pathbuilder_cstr(&pb);
|
||||
|
||||
switch (ctrl) {
|
||||
case APK_FS_CTRL_COMMIT:
|
||||
return uvol_run(ac, "up", uvol_name, 0, 0);
|
||||
case APK_FS_CTRL_APKNEW:
|
||||
case APK_FS_CTRL_CANCEL:
|
||||
case APK_FS_CTRL_DELETE:
|
||||
return uvol_run(ac, "remove", uvol_name, 0, 0);
|
||||
default:
|
||||
return -APKE_UVOL_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
static int uvol_file_digest(struct apk_fsdir *d, apk_blob_t filename, uint8_t alg, struct apk_digest *dgst)
|
||||
{
|
||||
return -APKE_UVOL_ERROR;
|
||||
}
|
||||
|
||||
const struct apk_fsdir_ops fsdir_ops_uvol = {
|
||||
.dir_create = uvol_dir_create,
|
||||
.dir_delete = uvol_dir_delete,
|
||||
.dir_check = uvol_dir_check,
|
||||
.dir_update_perms = uvol_dir_update_perms,
|
||||
.file_extract = uvol_file_extract,
|
||||
.file_control = uvol_file_control,
|
||||
.file_digest = uvol_file_digest,
|
||||
};
|
14
src/io.c
14
src/io.c
|
@ -314,7 +314,10 @@ static ssize_t digest_read(struct apk_istream *is, void *ptr, size_t size)
|
|||
ssize_t r;
|
||||
|
||||
r = dis->pis->ops->read(dis->pis, ptr, size);
|
||||
if (r > 0) apk_digest_ctx_update(&dis->dctx, ptr, r);
|
||||
if (r > 0) {
|
||||
apk_digest_ctx_update(&dis->dctx, ptr, r);
|
||||
dis->size_left -= r;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
|
@ -322,7 +325,7 @@ static int digest_close(struct apk_istream *is)
|
|||
{
|
||||
struct apk_digest_istream *dis = container_of(is, struct apk_digest_istream, is);
|
||||
|
||||
if (dis->digest) {
|
||||
if (dis->digest && dis->size_left == 0) {
|
||||
struct apk_digest res;
|
||||
apk_digest_ctx_final(&dis->dctx, &res);
|
||||
if (apk_digest_cmp(&res, dis->digest) != 0)
|
||||
|
@ -340,7 +343,7 @@ static const struct apk_istream_ops digest_istream_ops = {
|
|||
.close = digest_close,
|
||||
};
|
||||
|
||||
struct apk_istream *apk_istream_verify(struct apk_digest_istream *dis, struct apk_istream *is, struct apk_digest *d)
|
||||
struct apk_istream *apk_istream_verify(struct apk_digest_istream *dis, struct apk_istream *is, off_t size, struct apk_digest *d)
|
||||
{
|
||||
*dis = (struct apk_digest_istream) {
|
||||
.is.ops = &digest_istream_ops,
|
||||
|
@ -350,10 +353,13 @@ struct apk_istream *apk_istream_verify(struct apk_digest_istream *dis, struct ap
|
|||
.is.end = is->end,
|
||||
.pis = is,
|
||||
.digest = d,
|
||||
.size_left = size,
|
||||
};
|
||||
apk_digest_ctx_init(&dis->dctx, d->alg);
|
||||
if (dis->is.ptr != dis->is.end)
|
||||
if (dis->is.ptr != dis->is.end) {
|
||||
apk_digest_ctx_update(&dis->dctx, dis->is.ptr, dis->is.end - dis->is.ptr);
|
||||
dis->size_left -= dis->is.end - dis->is.ptr;
|
||||
}
|
||||
return &dis->is;
|
||||
}
|
||||
|
||||
|
|
|
@ -14,9 +14,10 @@ libapk_src = [
|
|||
'context.c',
|
||||
'crypto_openssl.c',
|
||||
'database.c',
|
||||
'extract.c',
|
||||
'extract_v2.c',
|
||||
'extract_v3.c',
|
||||
'fs_fsys.c',
|
||||
'fs_uvol.c',
|
||||
'hash.c',
|
||||
'io.c',
|
||||
'io_url.c',
|
||||
|
@ -38,6 +39,7 @@ libapk_headers = [
|
|||
'apk_database.h',
|
||||
'apk_defines.h',
|
||||
'apk_extract.h',
|
||||
'apk_fs.h',
|
||||
'apk_hash.h',
|
||||
'apk_io.h',
|
||||
'apk_openssl.h',
|
||||
|
|
|
@ -591,7 +591,7 @@ void apk_pkg_from_adb(struct apk_database *db, struct apk_package *pkg, struct a
|
|||
adb_ro_obj(pkgo, ADBI_PKG_PKGINFO, &pkginfo);
|
||||
|
||||
uid = adb_ro_blob(&pkginfo, ADBI_PI_UNIQUE_ID);
|
||||
if (uid.len == APK_CHECKSUM_SHA1) {
|
||||
if (uid.len >= APK_CHECKSUM_SHA1) {
|
||||
pkg->csum.type = APK_CHECKSUM_SHA1;
|
||||
memcpy(pkg->csum.data, uid.ptr, uid.len);
|
||||
}
|
||||
|
@ -723,19 +723,16 @@ err:
|
|||
|
||||
void apk_pkg_free(struct apk_package *pkg)
|
||||
{
|
||||
if (pkg == NULL)
|
||||
return;
|
||||
if (!pkg) return;
|
||||
|
||||
apk_pkg_uninstall(NULL, pkg);
|
||||
apk_dependency_array_free(&pkg->depends);
|
||||
apk_dependency_array_free(&pkg->provides);
|
||||
apk_dependency_array_free(&pkg->install_if);
|
||||
if (pkg->url)
|
||||
free(pkg->url);
|
||||
if (pkg->description)
|
||||
free(pkg->description);
|
||||
if (pkg->commit)
|
||||
free(pkg->commit);
|
||||
if (pkg->url) free(pkg->url);
|
||||
if (pkg->description) free(pkg->description);
|
||||
if (pkg->commit) free(pkg->commit);
|
||||
if (pkg->filename) free(pkg->filename);
|
||||
free(pkg);
|
||||
}
|
||||
|
||||
|
|
|
@ -58,7 +58,9 @@ const char *apk_error_str(int error)
|
|||
case APKE_INDEX_STALE: return "package mentioned in index not found (try 'apk update')";
|
||||
case APKE_FILE_INTEGRITY: return "file integrity error";
|
||||
case APKE_CACHE_NOT_AVAILABLE: return "cache not available";
|
||||
case APKE_UVOL: return "uvol error";
|
||||
case APKE_UVOL_NOT_AVAILABLE: return "uvol manager not available";
|
||||
case APKE_UVOL_ERROR: return "uvol error";
|
||||
case APKE_UVOL_ROOT: return "uvol not supported with --root";
|
||||
default:
|
||||
return strerror(error);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue