summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTimo Teräs <timo.teras@iki.fi>2021-11-05 13:20:19 +0200
committerTimo Teräs <timo.teras@iki.fi>2021-11-09 21:50:11 +0200
commita6736532001fd625f1ab3dd82abc2a4c5366c79c (patch)
tree47ead4bc00519f0b1e3ea52bf576da5bb8b66530
parentd441cf523cea6ab2d2ee1c0f50fab0bb620f2ea1 (diff)
downloadapk-tools-a6736532001fd625f1ab3dd82abc2a4c5366c79c.tar.gz
apk-tools-a6736532001fd625f1ab3dd82abc2a4c5366c79c.tar.bz2
apk-tools-a6736532001fd625f1ab3dd82abc2a4c5366c79c.tar.xz
apk-tools-a6736532001fd625f1ab3dd82abc2a4c5366c79c.zip
database: implement uvol support
by adding an abstraction layer to the file system
-rw-r--r--src/Makefile2
-rw-r--r--src/apk_context.h11
-rw-r--r--src/apk_crypto.h8
-rw-r--r--src/apk_defines.h4
-rw-r--r--src/apk_extract.h10
-rw-r--r--src/apk_fs.h70
-rw-r--r--src/apk_io.h4
-rw-r--r--src/app_add.c8
-rw-r--r--src/app_extract.c16
-rw-r--r--src/context.c22
-rw-r--r--src/database.c208
-rw-r--r--src/extract.c235
-rw-r--r--src/extract_v3.c31
-rw-r--r--src/fs_fsys.c316
-rw-r--r--src/fs_uvol.c162
-rw-r--r--src/io.c14
-rw-r--r--src/meson.build4
-rw-r--r--src/package.c15
-rw-r--r--src/print.c4
19 files changed, 710 insertions, 434 deletions
diff --git a/src/Makefile b/src/Makefile
index f7ebb66..0540cd8 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -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
diff --git a/src/apk_context.h b/src/apk_context.h
index 1bec2b9..7aae61b 100644
--- a/src/apk_context.h
+++ b/src/apk_context.h
@@ -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;
}
diff --git a/src/apk_crypto.h b/src/apk_crypto.h
index 0330694..18bf3b5 100644
--- a/src/apk_crypto.h
+++ b/src/apk_crypto.h
@@ -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;
diff --git a/src/apk_defines.h b/src/apk_defines.h
index 395958b..0a50284 100644
--- a/src/apk_defines.h
+++ b/src/apk_defines.h
@@ -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; }
diff --git a/src/apk_extract.h b/src/apk_extract.h
index ecb39ed..e3fabad 100644
--- a/src/apk_extract.h
+++ b/src/apk_extract.h
@@ -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);
diff --git a/src/apk_fs.h b/src/apk_fs.h
new file mode 100644
index 0000000..6a2a285
--- /dev/null
+++ b/src/apk_fs.h
@@ -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
diff --git a/src/apk_io.h b/src/apk_io.h
index 762f9e0..d04638a 100644
--- a/src/apk_io.h
+++ b/src/apk_io.h
@@ -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
diff --git a/src/app_add.c b/src/app_add.c
index 1d20e81..614dfb4 100644
--- a/src/app_add.c
+++ b/src/app_add.c
@@ -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);
diff --git a/src/app_extract.c b/src/app_extract.c
index 2dd310b..fea4924 100644
--- a/src/app_extract.c
+++ b/src/app_extract.c
@@ -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;
}
diff --git a/src/context.c b/src/context.c
index 4ad2bbf..7afb579 100644
--- a/src/context.c
+++ b/src/context.c
@@ -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) {
diff --git a/src/database.c b/src/database.c
index f5c8d8c..a3a5800 100644
--- a/src/database.c
+++ b/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;
- }
+ // All files difference. Use the package's file as .apk-new.
+ ctrl = APK_FS_CTRL_APKNEW;
}
+ }
- } 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;
- }
+ // 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 */
+ // Claim ownership of the file in db
if (ofile != file) {
if (ofile != NULL) {
hlist_del(&ofile->diri_files_list,
diff --git a/src/extract.c b/src/extract.c
deleted file mode 100644
index 00d871f..0000000
--- a/src/extract.c
+++ /dev/null
@@ -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);
-}
diff --git a/src/extract_v3.c b/src/extract_v3.c
index dcc5c24..c25b2ae 100644
--- a/src/extract_v3.c
+++ b/src/extract_v3.c
@@ -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);
+}
diff --git a/src/fs_fsys.c b/src/fs_fsys.c
new file mode 100644
index 0000000..7615b79
--- /dev/null
+++ b/src/fs_fsys.c
@@ -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);
+}
diff --git a/src/fs_uvol.c b/src/fs_uvol.c
new file mode 100644
index 0000000..f2ba3f1
--- /dev/null
+++ b/src/fs_uvol.c
@@ -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,
+};
diff --git a/src/io.c b/src/io.c
index a8821e4..cf30bb6 100644
--- a/src/io.c
+++ b/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;
}
diff --git a/src/meson.build b/src/meson.build
index 3bdb477..9422ca6 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -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',
diff --git a/src/package.c b/src/package.c
index cba999f..f08101f 100644
--- a/src/package.c
+++ b/src/package.c
@@ -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);
}
diff --git a/src/print.c b/src/print.c
index ea66b90..31a9fbb 100644
--- a/src/print.c
+++ b/src/print.c
@@ -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);
}