database: implement uvol support

by adding an abstraction layer to the file system
cute-signatures
Timo Teräs 2021-11-05 13:20:19 +02:00
parent d441cf523c
commit a673653200
19 changed files with 711 additions and 435 deletions

View File

@ -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

View File

@ -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;
}

View File

@ -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;

View File

@ -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; }

View File

@ -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);

70
src/apk_fs.h Normal file
View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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;
}

View File

@ -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) {

View File

@ -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,

View File

@ -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);
}

View File

@ -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);
}

316
src/fs_fsys.c Normal file
View File

@ -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);
}

162
src/fs_uvol.c Normal file
View File

@ -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,
};

View File

@ -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;
}

View File

@ -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',

View File

@ -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);
}

View File

@ -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);
}