db, cache: automatically remount cache read-write when needed

.. and back to read-only after finishing with modifications.

fixes #512
cute-signatures
Timo Teräs 2011-03-16 14:55:08 +02:00
parent 1e17da9d70
commit 415e230a7f
3 changed files with 104 additions and 14 deletions

View File

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

View File

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

View File

@ -12,6 +12,7 @@
#include <errno.h>
#include <stdio.h>
#include <fcntl.h>
#include <mntent.h>
#include <limits.h>
#include <unistd.h>
#include <malloc.h>
@ -21,7 +22,9 @@
#include <fnmatch.h>
#include <sys/vfs.h>
#include <sys/file.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
#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);