diff options
-rw-r--r-- | src/apk.c | 2 | ||||
-rw-r--r-- | src/apk_io.h | 6 | ||||
-rw-r--r-- | src/archive.c | 5 | ||||
-rw-r--r-- | src/io.c | 126 | ||||
-rw-r--r-- | src/package.c | 2 |
5 files changed, 140 insertions, 1 deletions
@@ -244,6 +244,7 @@ int main(int argc, char **argv) memset(&dbopts, 0, sizeof(dbopts)); list_init(&dbopts.repository_list); umask(0); + apk_id_cache_init(); applet = deduce_applet(argc, argv); num_options = ARRAY_SIZE(generic_options) + 1; @@ -365,6 +366,7 @@ int main(int argc, char **argv) r = applet->main(ctx, &db, argc, argv); apk_db_close(&db); + apk_id_cache_free(); if (r == -EINVAL) return usage(applet); diff --git a/src/apk_io.h b/src/apk_io.h index 68f9925..3a3abff 100644 --- a/src/apk_io.h +++ b/src/apk_io.h @@ -95,4 +95,10 @@ int apk_file_get_info(int atfd, const char *filename, unsigned int flags, int apk_url_download(const char *url, int atfd, const char *file); const char *apk_url_local_file(const char *url); +void apk_id_cache_init(void); +void apk_id_cache_free(void); +void apk_id_cache_reset(void); +uid_t apk_resolve_uid(const char *username, uid_t default_uid); +uid_t apk_resolve_gid(const char *groupname, uid_t default_gid); + #endif diff --git a/src/archive.c b/src/archive.c index e7260b6..f60cb1b 100644 --- a/src/archive.c +++ b/src/archive.c @@ -388,7 +388,10 @@ int apk_archive_entry_extract(int atfd, const struct apk_file_info *ae, break; } if (r == 0) { - r = fchownat(atfd, fn, ae->uid, ae->gid, atflags); + r = fchownat(atfd, fn, + apk_resolve_uid(ae->uname, ae->uid), + apk_resolve_gid(ae->gname, ae->gid), + atflags); if (r < 0) { apk_error("Failed to set ownership on %s: %s", fn, strerror(errno)); @@ -15,9 +15,12 @@ #include <unistd.h> #include <malloc.h> #include <sys/mman.h> +#include <pwd.h> +#include <grp.h> #include "apk_defines.h" #include "apk_io.h" +#include "apk_hash.h" struct apk_fd_istream { struct apk_istream is; @@ -715,3 +718,126 @@ size_t apk_ostream_write_string(struct apk_ostream *os, const char *string) return len; } + +struct cache_item { + apk_hash_node hash_node; + unsigned int genid; + union { + uid_t uid; + gid_t gid; + }; + unsigned short len; + char name[]; +}; + +static apk_blob_t cache_item_get_key(apk_hash_item item) +{ + struct cache_item *ci = (struct cache_item *) item; + return APK_BLOB_PTR_LEN(ci->name, ci->len); +} + +static const struct apk_hash_ops id_hash_ops = { + .node_offset = offsetof(struct cache_item, hash_node), + .get_key = cache_item_get_key, + .hash_key = apk_blob_hash, + .compare = apk_blob_compare, + .delete_item = (apk_hash_delete_f) free, +}; + +static struct cache_item *resolve_cache_item(struct apk_hash *hash, apk_blob_t name) +{ + struct cache_item *ci; + unsigned long h; + + h = id_hash_ops.hash_key(name); + ci = (struct cache_item *) apk_hash_get_hashed(hash, name, h); + if (ci != NULL) + return ci; + + ci = calloc(1, sizeof(struct cache_item) + name.len); + if (ci == NULL) + return NULL; + + ci->len = name.len; + memcpy(ci->name, name.ptr, name.len); + apk_hash_insert_hashed(hash, ci, h); + + return ci; +} + +static unsigned int id_genid = 0; +static struct apk_hash uid_cache, gid_cache; + +void apk_id_cache_init(void) +{ + apk_hash_init(&uid_cache, &id_hash_ops, 256); + apk_hash_init(&gid_cache, &id_hash_ops, 256); + id_genid = 1; +} + +void apk_id_cache_free(void) +{ + apk_hash_free(&uid_cache); + apk_hash_free(&gid_cache); +} + +void apk_id_cache_reset(void) +{ + id_genid++; + if (id_genid == 0) + id_genid = 1; +} + +uid_t apk_resolve_uid(const char *username, uid_t default_uid) +{ + struct cache_item *ci; + struct passwd pwent, *pwd; + char buf[1024]; + int r; + + ci = resolve_cache_item(&uid_cache, APK_BLOB_STR(username)); + if (ci == NULL) + return default_uid; + + if (ci->genid != id_genid) { + r = getpwnam_r(username, &pwent, buf, sizeof(buf), &pwd); + if (pwd != NULL) + ci->uid = pwd->pw_uid; + else + ci->uid = -1; + if (r == 0) + ci->genid = id_genid; + } + + if (ci->uid != -1) + return ci->uid; + + return default_uid; +} + +uid_t apk_resolve_gid(const char *groupname, uid_t default_gid) +{ + struct cache_item *ci; + struct group grent, *grp; + char buf[1024]; + int r; + + ci = resolve_cache_item(&gid_cache, APK_BLOB_STR(groupname)); + if (ci == NULL) + return default_gid; + + if (ci->genid != id_genid) { + r = getgrnam_r(groupname, &grent, buf, sizeof(buf), &grp); + if (grp != NULL) + ci->gid = grp->gr_gid; + else + ci->gid = -1; + if (r == 0) + ci->genid = id_genid; + } + + if (ci->gid != -1) + return ci->gid; + + return default_gid; +} diff --git a/src/package.c b/src/package.c index 0dd89d3..e73814a 100644 --- a/src/package.c +++ b/src/package.c @@ -910,6 +910,8 @@ int apk_ipkg_run_script(struct apk_installed_package *ipkg, int root_fd, } waitpid(pid, &status, 0); unlinkat(root_fd, fn, 0); + apk_id_cache_reset(); + if (WIFEXITED(status)) return WEXITSTATUS(status); return -1; |