summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTimo Teräs <timo.teras@iki.fi>2014-10-07 13:29:34 +0300
committerTimo Teräs <timo.teras@iki.fi>2014-10-07 14:11:29 +0300
commit09e48d8f06bfb924b80ce3e95c281524c4891828 (patch)
treeb3da94f56dc065577baa06e0f3d8cd43d2500bf8
parente0f9b0897be8e5524eaeef302249c90498d3218a (diff)
downloadapk-tools-09e48d8f06bfb924b80ce3e95c281524c4891828.tar.bz2
apk-tools-09e48d8f06bfb924b80ce3e95c281524c4891828.tar.xz
db: rework directory permission handling
Apk used to reset directory permissions always, but this is undesirable if user has modified the permissions - especially during tmpfs boot. Though, it is desirable to update the permissions when packaging has changed permissions, or a new package is installed and the merged permission mask / owner changes. Thus the new code updates the permissions only if: 1) We are booting and directory is not in apkovl 2) The directory is modified by a package install/remove/upgrade 3) The filesystem directory permission matched database Additionally "apk fix --directory-permissions" can be used to reset all directory permissions to the database defaults. Fixes #2966
-rw-r--r--src/apk_database.h10
-rw-r--r--src/commit.c1
-rw-r--r--src/database.c133
-rw-r--r--src/fix.c2
4 files changed, 88 insertions, 58 deletions
diff --git a/src/apk_database.h b/src/apk_database.h
index 23a2dd6..7d95011 100644
--- a/src/apk_database.h
+++ b/src/apk_database.h
@@ -47,7 +47,7 @@ enum apk_protect_mode {
struct apk_protected_path {
char *relative_pattern;
- unsigned protect_mode : 4;
+ unsigned protect_mode : 3;
};
APK_ARRAY(apk_protected_path_array, struct apk_protected_path);
@@ -64,10 +64,13 @@ struct apk_db_dir {
unsigned short refs;
unsigned short namelen;
- unsigned protect_mode : 4;
+ unsigned protect_mode : 3;
unsigned has_protected_children : 1;
+
+ unsigned seen : 1;
+ unsigned created : 1;
unsigned modified : 1;
- unsigned recalc_mode : 1;
+ unsigned update_permissions : 1;
char rooted_name[1];
char name[];
@@ -211,6 +214,7 @@ int apk_db_write_config(struct apk_database *db);
int apk_db_permanent(struct apk_database *db);
int apk_db_check_world(struct apk_database *db, struct apk_dependency_array *world);
int apk_db_fire_triggers(struct apk_database *db);
+void apk_db_update_directory_permissions(struct apk_database *db);
struct apk_package *apk_db_pkg_add(struct apk_database *db, struct apk_package *pkg);
struct apk_package *apk_db_get_pkg(struct apk_database *db, struct apk_checksum *csum);
diff --git a/src/commit.c b/src/commit.c
index bf3b4a2..5b47d9e 100644
--- a/src/commit.c
+++ b/src/commit.c
@@ -302,6 +302,7 @@ int apk_solver_commit_changeset(struct apk_database *db,
apk_print_progress(prog.total.bytes + prog.total.packages,
prog.total.bytes + prog.total.packages);
+ apk_db_update_directory_permissions(db);
run_triggers(db, changeset);
all_done:
diff --git a/src/database.c b/src/database.c
index 8d633b4..064957e 100644
--- a/src/database.c
+++ b/src/database.c
@@ -39,8 +39,8 @@ static const apk_spn_match_def apk_spn_repo_separators = {
};
enum {
- APK_DISALLOW_RMDIR = 0,
- APK_ALLOW_RMDIR = 1
+ APK_DIR_FREE = 0,
+ APK_DIR_REMOVE
};
int apk_verbosity = 1;
@@ -222,59 +222,39 @@ struct apk_name *apk_db_get_name(struct apk_database *db, apk_blob_t name)
return pn;
}
-static void apk_db_dir_mkdir(struct apk_database *db, struct apk_db_dir *dir)
+static void apk_db_dir_prepare(struct apk_database *db, struct apk_db_dir *dir, mode_t newmode)
{
- if (apk_flags & APK_SIMULATE)
- return;
+ struct stat st;
- /* Don't mess with root, as no package provides it directly */
- if (dir->namelen == 0)
- return;
+ if (dir->namelen == 0) return;
+ if (dir->created) return;
- if ((dir->refs == 1) ||
- (fchmodat(db->root_fd, dir->name, dir->mode, 0) != 0 &&
- errno == ENOENT))
- if ((mkdirat(db->root_fd, dir->name, dir->mode) != 0 &&
- errno == EEXIST))
- if (fchmodat(db->root_fd, dir->name, dir->mode, 0) != 0)
- ;
-
- if (fchownat(db->root_fd, dir->name, dir->uid, dir->gid, 0) != 0)
- ;
+ 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) {
+ if (!(apk_flags & APK_SIMULATE))
+ mkdirat(db->root_fd, dir->name, newmode);
+ dir->created = 1;
+ dir->update_permissions = 1;
+ }
}
-void apk_db_dir_unref(struct apk_database *db, struct apk_db_dir *dir, int allow_rmdir)
+void apk_db_dir_unref(struct apk_database *db, struct apk_db_dir *dir, int rmdir_mode)
{
- dir->refs--;
- if (dir->refs > 0) {
- if (allow_rmdir) {
- dir->recalc_mode = 1;
- dir->mode = 0;
- dir->uid = (uid_t) -1;
- dir->gid = (gid_t) -1;
- }
- return;
- }
-
+ if (--dir->refs > 0) return;
db->installed.stats.dirs--;
+ if (dir->namelen == 0) return;
- if (allow_rmdir) {
- /* The final instance of this directory was removed,
- * so this directory gets deleted in reality too. */
- dir->recalc_mode = 0;
- dir->mode = 0;
- dir->uid = (uid_t) -1;
- dir->gid = (gid_t) -1;
+ if (rmdir_mode == APK_DIR_REMOVE && !(apk_flags & APK_SIMULATE))
+ if (unlinkat(db->root_fd, dir->name, AT_REMOVEDIR))
+ ;
- if (dir->namelen)
- unlinkat(db->root_fd, dir->name, AT_REMOVEDIR);
- } else if (dir->recalc_mode) {
- /* Directory permissions need a reset. */
- apk_db_dir_mkdir(db, dir);
- }
-
- if (dir->parent != NULL)
- apk_db_dir_unref(db, dir->parent, allow_rmdir);
+ apk_db_dir_unref(db, dir->parent, rmdir_mode);
}
struct apk_db_dir *apk_db_dir_ref(struct apk_db_dir *dir)
@@ -406,13 +386,14 @@ static void apk_db_diri_set(struct apk_db_dir_instance *diri, mode_t mode,
static void apk_db_diri_free(struct apk_database *db,
struct apk_db_dir_instance *diri,
- int allow_rmdir)
+ int rmdir_mode)
{
- if (allow_rmdir == APK_DISALLOW_RMDIR &&
- diri->dir->recalc_mode)
- apk_db_dir_apply_diri_permissions(diri);
+ struct apk_db_dir *dir = diri->dir;
- apk_db_dir_unref(db, diri->dir, allow_rmdir);
+ if (rmdir_mode == APK_DIR_REMOVE)
+ apk_db_dir_prepare(db, diri->dir, 0);
+
+ apk_db_dir_unref(db, dir, rmdir_mode);
free(diri);
}
@@ -731,6 +712,7 @@ int apk_db_read_overlay(struct apk_database *db, struct apk_bstream *bs)
if (bfile.len == 0) {
diri = apk_db_diri_new(db, pkg, bdir, &diri_node);
file_diri_node = &diri->owned_files.first;
+ diri->dir->created = 1;
} else {
diri = find_diri(ipkg, bdir, diri, &file_diri_node);
if (diri == NULL) {
@@ -1752,7 +1734,7 @@ void apk_db_close(struct apk_database *db)
* directories to be reset. */
list_for_each_entry(ipkg, &db->installed.packages, installed_pkgs_list) {
hlist_for_each_entry_safe(diri, dc, dn, &ipkg->owned_dirs, pkg_dirs_list) {
- apk_db_diri_free(db, diri, APK_DISALLOW_RMDIR);
+ apk_db_diri_free(db, diri, APK_DIR_FREE);
}
}
@@ -1865,6 +1847,49 @@ int apk_db_fire_triggers(struct apk_database *db)
return db->pending_triggers;
}
+static int update_permissions(apk_hash_item item, void *ctx)
+{
+ struct apk_database *db = (struct apk_database *) ctx;
+ struct apk_db_dir *dir = (struct apk_db_dir *) item;
+ struct stat st;
+ int r;
+
+ 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))
+ fchmodat(db->root_fd, dir->name, dir->mode, 0);
+ if (r < 0 || st.st_uid != dir->uid || st.st_gid != dir->gid)
+ fchownat(db->root_fd, dir->name, dir->uid, dir->gid, 0);
+
+ return 0;
+}
+
+void apk_db_update_directory_permissions(struct apk_database *db)
+{
+ struct apk_installed_package *ipkg;
+ struct apk_db_dir_instance *diri;
+ struct apk_db_dir *dir;
+ struct hlist_node *dc, *dn;
+
+ list_for_each_entry(ipkg, &db->installed.packages, installed_pkgs_list) {
+ hlist_for_each_entry_safe(diri, dc, dn, &ipkg->owned_dirs, pkg_dirs_list) {
+ dir = diri->dir;
+ if (!dir->update_permissions) continue;
+ if (!dir->seen) {
+ dir->seen = 1;
+ dir->mode = 0;
+ dir->uid = (uid_t) -1;
+ dir->gid = (gid_t) -1;
+ }
+ apk_db_dir_apply_diri_permissions(diri);
+ }
+ }
+ apk_hash_foreach(&db->installed.dirs, update_permissions, db);
+}
+
int apk_db_cache_active(struct apk_database *db)
{
return db->cache_dir != apk_static_cache_dir;
@@ -2388,8 +2413,8 @@ static int apk_db_install_archive_entry(void *_ctx,
name.len--;
diri = apk_db_install_directory_entry(ctx, name);
+ apk_db_dir_prepare(db, diri->dir, ae->mode);
apk_db_diri_set(diri, ae->mode, ae->uid, ae->gid);
- apk_db_dir_mkdir(db, diri->dir);
}
ctx->installed_size += ctx->current_file_size;
@@ -2438,7 +2463,7 @@ static void apk_db_purge_pkg(struct apk_database *db,
}
}
__hlist_del(dc, &ipkg->owned_dirs.first);
- apk_db_diri_free(db, diri, APK_ALLOW_RMDIR);
+ apk_db_diri_free(db, diri, APK_DIR_REMOVE);
}
}
diff --git a/src/fix.c b/src/fix.c
index 3a7a8e6..df85c2c 100644
--- a/src/fix.c
+++ b/src/fix.c
@@ -48,7 +48,7 @@ static int fix_parse(void *pctx, struct apk_db_options *dbopts,
static int mark_recalculate(apk_hash_item item, void *ctx)
{
struct apk_db_dir *dir = (struct apk_db_dir *) item;
- dir->recalc_mode = 1;
+ dir->update_permissions = 1;
return 0;
}