Commit 09e48d8f authored by Timo Teräs's avatar Timo Teräs

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
parent e0f9b089
...@@ -47,7 +47,7 @@ enum apk_protect_mode { ...@@ -47,7 +47,7 @@ enum apk_protect_mode {
struct apk_protected_path { struct apk_protected_path {
char *relative_pattern; char *relative_pattern;
unsigned protect_mode : 4; unsigned protect_mode : 3;
}; };
APK_ARRAY(apk_protected_path_array, struct apk_protected_path); APK_ARRAY(apk_protected_path_array, struct apk_protected_path);
...@@ -64,10 +64,13 @@ struct apk_db_dir { ...@@ -64,10 +64,13 @@ struct apk_db_dir {
unsigned short refs; unsigned short refs;
unsigned short namelen; unsigned short namelen;
unsigned protect_mode : 4; unsigned protect_mode : 3;
unsigned has_protected_children : 1; unsigned has_protected_children : 1;
unsigned seen : 1;
unsigned created : 1;
unsigned modified : 1; unsigned modified : 1;
unsigned recalc_mode : 1; unsigned update_permissions : 1;
char rooted_name[1]; char rooted_name[1];
char name[]; char name[];
...@@ -211,6 +214,7 @@ int apk_db_write_config(struct apk_database *db); ...@@ -211,6 +214,7 @@ int apk_db_write_config(struct apk_database *db);
int apk_db_permanent(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_check_world(struct apk_database *db, struct apk_dependency_array *world);
int apk_db_fire_triggers(struct apk_database *db); 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_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); struct apk_package *apk_db_get_pkg(struct apk_database *db, struct apk_checksum *csum);
......
...@@ -302,6 +302,7 @@ int apk_solver_commit_changeset(struct apk_database *db, ...@@ -302,6 +302,7 @@ int apk_solver_commit_changeset(struct apk_database *db,
apk_print_progress(prog.total.bytes + prog.total.packages, apk_print_progress(prog.total.bytes + prog.total.packages,
prog.total.bytes + prog.total.packages); prog.total.bytes + prog.total.packages);
apk_db_update_directory_permissions(db);
run_triggers(db, changeset); run_triggers(db, changeset);
all_done: all_done:
......
...@@ -39,8 +39,8 @@ static const apk_spn_match_def apk_spn_repo_separators = { ...@@ -39,8 +39,8 @@ static const apk_spn_match_def apk_spn_repo_separators = {
}; };
enum { enum {
APK_DISALLOW_RMDIR = 0, APK_DIR_FREE = 0,
APK_ALLOW_RMDIR = 1 APK_DIR_REMOVE
}; };
int apk_verbosity = 1; int apk_verbosity = 1;
...@@ -222,59 +222,39 @@ struct apk_name *apk_db_get_name(struct apk_database *db, apk_blob_t name) ...@@ -222,59 +222,39 @@ struct apk_name *apk_db_get_name(struct apk_database *db, apk_blob_t name)
return pn; 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) struct stat st;
return;
/* Don't mess with root, as no package provides it directly */ if (dir->namelen == 0) return;
if (dir->namelen == 0) if (dir->created) return;
return;
if ((dir->refs == 1) || if (fstatat(db->root_fd, dir->name, &st, AT_SYMLINK_NOFOLLOW) == 0) {
(fchmodat(db->root_fd, dir->name, dir->mode, 0) != 0 && /* If directory exists and stats match what we expect,
errno == ENOENT)) * then we can allow auto updating the permissions */
if ((mkdirat(db->root_fd, dir->name, dir->mode) != 0 && dir->created = 1;
errno == EEXIST)) dir->update_permissions |=
if (fchmodat(db->root_fd, dir->name, dir->mode, 0) != 0) (st.st_mode & 07777) == (dir->mode & 07777) &&
; st.st_uid == dir->uid && st.st_gid == dir->gid;
} else if (newmode) {
if (fchownat(db->root_fd, dir->name, dir->uid, dir->gid, 0) != 0) 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) return;
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;
}
db->installed.stats.dirs--; db->installed.stats.dirs--;
if (dir->namelen == 0) return;
if (allow_rmdir) { if (rmdir_mode == APK_DIR_REMOVE && !(apk_flags & APK_SIMULATE))
/* The final instance of this directory was removed, if (unlinkat(db->root_fd, dir->name, AT_REMOVEDIR))
* 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 (dir->namelen) apk_db_dir_unref(db, dir->parent, rmdir_mode);
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);
} }
struct apk_db_dir *apk_db_dir_ref(struct apk_db_dir *dir) 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, ...@@ -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, static void apk_db_diri_free(struct apk_database *db,
struct apk_db_dir_instance *diri, struct apk_db_dir_instance *diri,
int allow_rmdir) int rmdir_mode)
{ {
if (allow_rmdir == APK_DISALLOW_RMDIR && struct apk_db_dir *dir = diri->dir;
diri->dir->recalc_mode)
apk_db_dir_apply_diri_permissions(diri);
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); free(diri);
} }
...@@ -731,6 +712,7 @@ int apk_db_read_overlay(struct apk_database *db, struct apk_bstream *bs) ...@@ -731,6 +712,7 @@ int apk_db_read_overlay(struct apk_database *db, struct apk_bstream *bs)
if (bfile.len == 0) { if (bfile.len == 0) {
diri = apk_db_diri_new(db, pkg, bdir, &diri_node); diri = apk_db_diri_new(db, pkg, bdir, &diri_node);
file_diri_node = &diri->owned_files.first; file_diri_node = &diri->owned_files.first;
diri->dir->created = 1;
} else { } else {
diri = find_diri(ipkg, bdir, diri, &file_diri_node); diri = find_diri(ipkg, bdir, diri, &file_diri_node);
if (diri == NULL) { if (diri == NULL) {
...@@ -1752,7 +1734,7 @@ void apk_db_close(struct apk_database *db) ...@@ -1752,7 +1734,7 @@ void apk_db_close(struct apk_database *db)
* directories to be reset. */ * directories to be reset. */
list_for_each_entry(ipkg, &db->installed.packages, installed_pkgs_list) { 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) { 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) ...@@ -1865,6 +1847,49 @@ int apk_db_fire_triggers(struct apk_database *db)
return db->pending_triggers; 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) int apk_db_cache_active(struct apk_database *db)
{ {
return db->cache_dir != apk_static_cache_dir; return db->cache_dir != apk_static_cache_dir;
...@@ -2388,8 +2413,8 @@ static int apk_db_install_archive_entry(void *_ctx, ...@@ -2388,8 +2413,8 @@ static int apk_db_install_archive_entry(void *_ctx,
name.len--; name.len--;
diri = apk_db_install_directory_entry(ctx, name); 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_diri_set(diri, ae->mode, ae->uid, ae->gid);
apk_db_dir_mkdir(db, diri->dir);
} }
ctx->installed_size += ctx->current_file_size; ctx->installed_size += ctx->current_file_size;
...@@ -2438,7 +2463,7 @@ static void apk_db_purge_pkg(struct apk_database *db, ...@@ -2438,7 +2463,7 @@ static void apk_db_purge_pkg(struct apk_database *db,
} }
} }
__hlist_del(dc, &ipkg->owned_dirs.first); __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);
} }
} }
......
...@@ -48,7 +48,7 @@ static int fix_parse(void *pctx, struct apk_db_options *dbopts, ...@@ -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) static int mark_recalculate(apk_hash_item item, void *ctx)
{ {
struct apk_db_dir *dir = (struct apk_db_dir *) item; struct apk_db_dir *dir = (struct apk_db_dir *) item;
dir->recalc_mode = 1; dir->update_permissions = 1;
return 0; return 0;
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment