From 415e230a7f0874206d4bbe2662c9f9cbf7e4c307 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Ter=C3=A4s?= Date: Wed, 16 Mar 2011 14:55:08 +0200 Subject: [PATCH] db, cache: automatically remount cache read-write when needed .. and back to read-only after finishing with modifications. fixes #512 --- src/apk_database.h | 3 ++ src/cache.c | 2 +- src/database.c | 113 +++++++++++++++++++++++++++++++++++++++------ 3 files changed, 104 insertions(+), 14 deletions(-) diff --git a/src/apk_database.h b/src/apk_database.h index 64c4fed..988a668 100644 --- a/src/apk_database.h +++ b/src/apk_database.h @@ -105,9 +105,11 @@ struct apk_database { int root_fd, lock_fd, cache_fd, cachetmp_fd, keys_fd; unsigned name_id, num_repos; const char *cache_dir; + char *cache_remount_dir; apk_blob_t *arch; unsigned int local_repos; int permanent : 1; + int ro_cache : 1; int compat_newfeatures : 1; int compat_notinstallable : 1; @@ -155,6 +157,7 @@ struct apk_db_file *apk_db_file_query(struct apk_database *db, #define APK_OPENF_NO_WORLD 0x0040 #define APK_OPENF_NO_SYS_REPOS 0x0100 #define APK_OPENF_NO_INSTALLED_REPO 0x0200 +#define APK_OPENF_CACHE_WRITE 0x0400 #define APK_OPENF_NO_REPOS (APK_OPENF_NO_SYS_REPOS | \ APK_OPENF_NO_INSTALLED_REPO) diff --git a/src/cache.c b/src/cache.c index f827cc1..8cee1e4 100644 --- a/src/cache.c +++ b/src/cache.c @@ -172,7 +172,7 @@ static struct apk_applet apk_cache = { "making /etc/apk/cache a symlink to the directory (on boot " "media) that will be used as package cache.", .arguments = "sync | clean | download", - .open_flags = APK_OPENF_READ|APK_OPENF_NO_SCRIPTS|APK_OPENF_NO_INSTALLED, + .open_flags = APK_OPENF_READ|APK_OPENF_NO_SCRIPTS|APK_OPENF_NO_INSTALLED|APK_OPENF_CACHE_WRITE, .main = cache_main, }; diff --git a/src/database.c b/src/database.c index 4531eeb..f2cd3b7 100644 --- a/src/database.c +++ b/src/database.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -21,7 +22,9 @@ #include #include #include +#include #include +#include #include "apk_defines.h" #include "apk_package.h" @@ -503,8 +506,10 @@ int apk_cache_download(struct apk_database *db, const char *url, apk_blob_t *arc } } - if (renameat(db->cachetmp_fd, cacheitem, db->cache_fd, cacheitem) < 0) - return -errno; + if (db->cachetmp_fd != db->cache_fd) { + if (renameat(db->cachetmp_fd, cacheitem, db->cache_fd, cacheitem) < 0) + return -errno; + } return 0; } @@ -1059,15 +1064,65 @@ static void handle_alarm(int sig) { } +static char *find_mountpoint(int atfd, const char *rel_path) +{ + struct mntent *me; + struct stat64 st; + FILE *f; + char *ret = NULL; + dev_t dev; + + if (fstatat64(atfd, rel_path, &st, 0) != 0) + return NULL; + dev = st.st_dev; + + f = setmntent("/proc/mounts", "r"); + if (f == NULL) + return NULL; + while ((me = getmntent(f)) != NULL) { + if (strcmp(me->mnt_fsname, "rootfs") == 0) + continue; + if (fstatat64(atfd, me->mnt_dir, &st, 0) == 0 && + st.st_dev == dev) { + ret = strdup(me->mnt_dir); + break; + } + } + endmntent(f); + + return ret; +} + +static int do_remount(const char *path, const char *option) +{ + pid_t pid; + int status; + + pid = fork(); + if (pid < 0) + return -errno; + + if (pid == 0) { + execl("/bin/mount", "mount", "-o", "remount", "-o", + option, path, NULL); + return 1; + } + + waitpid(pid, &status, 0); + if (!WIFEXITED(status)) + return -1; + + return WEXITSTATUS(status); +} + int apk_db_open(struct apk_database *db, struct apk_db_options *dbopts) { const char *msg = NULL; struct apk_repository_list *repo = NULL; struct apk_bstream *bs; - struct stat64 st; struct statfs stfs; apk_blob_t blob; - int r, rr = 0; + int r, fd, rr = 0; memset(db, 0, sizeof(*db)); if (apk_flags & APK_SIMULATE) { @@ -1088,7 +1143,6 @@ int apk_db_open(struct apk_database *db, struct apk_db_options *dbopts) list_init(&db->installed.triggers); apk_dependency_array_init(&db->world); apk_string_array_init(&db->protected_paths); - db->cache_dir = apk_static_cache_dir; db->permanent = 1; db->root = strdup(dbopts->root ?: "/"); @@ -1105,10 +1159,6 @@ int apk_db_open(struct apk_database *db, struct apk_db_options *dbopts) stfs.f_type == 0x01021994 /* TMPFS_MAGIC */) db->permanent = 0; - if (fstatat64(db->root_fd, apk_linked_cache_dir, &st, 0) == 0 && - S_ISDIR(st.st_mode) && major(st.st_dev) != 0) - db->cache_dir = apk_linked_cache_dir; - apk_id_cache_init(&db->id_cache, db->root_fd); if (dbopts->open_flags & APK_OPENF_WRITE) { @@ -1150,10 +1200,26 @@ int apk_db_open(struct apk_database *db, struct apk_db_options *dbopts) blob = APK_BLOB_STR("etc:*etc/init.d"); apk_blob_for_each_segment(blob, ":", add_protected_path, db); + /* figure out where to have the cache */ + fd = openat(db->root_fd, apk_linked_cache_dir, O_RDONLY | O_CLOEXEC); + if (fd >= 0 && fstatfs(fd, &stfs) == 0 /*&& stfs.f_type != 0x01021994*/ /* TMPFS_MAGIC */) { + struct statvfs stvfs; + + db->cache_dir = apk_linked_cache_dir; + db->cache_fd = fd; + mkdirat(db->cache_fd, "tmp", 0644); + db->cachetmp_fd = openat(db->cache_fd, "tmp", O_RDONLY | O_CLOEXEC); + if (fstatvfs(fd, &stvfs) == 0 && (stvfs.f_flag & ST_RDONLY) != 0) + db->ro_cache = 1; + } else { + if (fd >= 0) + close(fd); + db->cache_dir = apk_static_cache_dir; + db->cache_fd = openat(db->root_fd, db->cache_dir, O_RDONLY | O_CLOEXEC); + db->cachetmp_fd = db->cache_fd; + } + db->arch = apk_blob_atomize(APK_BLOB_STR(apk_arch)); - db->cache_fd = openat(db->root_fd, db->cache_dir, O_RDONLY | O_CLOEXEC); - mkdirat(db->cache_fd, "tmp", 0644); - db->cachetmp_fd = openat(db->cache_fd, "tmp", O_RDONLY | O_CLOEXEC); db->keys_fd = openat(db->root_fd, dbopts->keys_dir ?: "etc/apk/keys", O_RDONLY | O_CLOEXEC); @@ -1217,6 +1283,21 @@ int apk_db_open(struct apk_database *db, struct apk_db_options *dbopts) "might not function properly"); } + if ((dbopts->open_flags & (APK_OPENF_WRITE | APK_OPENF_CACHE_WRITE)) && + db->ro_cache) { + /* remount cache read-write */ + db->cache_remount_dir = find_mountpoint(db->root_fd, db->cache_dir); + if (db->cache_remount_dir == NULL) { + apk_warning("Unable to find cache directory mount point"); + } else if (do_remount(db->cache_remount_dir, "rw") != 0) { + free(db->cache_remount_dir); + db->cache_remount_dir = NULL; + apk_error("Unable to remount cache read-write"); + r = EROFS; + goto ret_r; + } + } + return rr; ret_errno: @@ -1306,6 +1387,12 @@ void apk_db_close(struct apk_database *db) struct hlist_node *dc, *dn; int i; + if (db->cache_remount_dir) { + do_remount(db->cache_remount_dir, "ro"); + free(db->cache_remount_dir); + db->cache_remount_dir = NULL; + } + apk_id_cache_free(&db->id_cache); list_for_each_entry(ipkg, &db->installed.packages, installed_pkgs_list) { @@ -1329,7 +1416,7 @@ void apk_db_close(struct apk_database *db) if (db->keys_fd) close(db->keys_fd); - if (db->cachetmp_fd) + if (db->cachetmp_fd && db->cachetmp_fd != db->cache_fd) close(db->cachetmp_fd); if (db->cache_fd) close(db->cache_fd);