summaryrefslogtreecommitdiffstats
path: root/src/install/dracut-install.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/install/dracut-install.c')
-rw-r--r--src/install/dracut-install.c152
1 files changed, 145 insertions, 7 deletions
diff --git a/src/install/dracut-install.c b/src/install/dracut-install.c
index 0c34363..ccb3200 100644
--- a/src/install/dracut-install.c
+++ b/src/install/dracut-install.c
@@ -42,6 +42,7 @@
#include <fts.h>
#include <regex.h>
#include <sys/utsname.h>
+#include <sys/xattr.h>
#include "log.h"
#include "hashmap.h"
@@ -84,6 +85,7 @@ static Hashmap *items_failed = NULL;
static Hashmap *modules_loaded = NULL;
static Hashmap *modules_suppliers = NULL;
static Hashmap *processed_suppliers = NULL;
+static Hashmap *modalias_to_kmod = NULL;
static regex_t mod_filter_path;
static regex_t mod_filter_nopath;
static regex_t mod_filter_symbol;
@@ -291,6 +293,56 @@ static inline int clone_file(int dest_fd, int src_fd)
return ioctl(dest_fd, BTRFS_IOC_CLONE, src_fd);
}
+static int copy_xattr(int dest_fd, int src_fd)
+{
+ int ret = 0;
+ ssize_t name_len = 0, value_len = 0;
+ char *name_buf = NULL, *name = NULL, *value = NULL, *value_save = NULL;
+
+ name_len = flistxattr(src_fd, NULL, 0);
+ if (name_len < 0)
+ return -1;
+
+ name_buf = calloc(1, name_len + 1);
+ if (name_buf == NULL)
+ return -1;
+
+ name_len = flistxattr(src_fd, name_buf, name_len);
+ if (name_len < 0)
+ goto out;
+
+ for (name = name_buf; name != name_buf + name_len; name = strchr(name, '\0') + 1) {
+ value_len = fgetxattr(src_fd, name, NULL, 0);
+ if (value_len < 0) {
+ ret = -1;
+ continue;
+ }
+
+ value_save = value;
+ value = realloc(value, value_len);
+ if (value == NULL) {
+ value = value_save;
+ ret = -1;
+ goto out;
+ }
+
+ value_len = fgetxattr(src_fd, name, value, value_len);
+ if (value_len < 0) {
+ ret = -1;
+ continue;
+ }
+
+ value_len = fsetxattr(dest_fd, name, value, value_len, 0);
+ if (value_len < 0)
+ ret = -1;
+ }
+
+out:
+ free(name_buf);
+ free(value);
+ return ret;
+}
+
static bool use_clone = true;
static int cp(const char *src, const char *dst)
@@ -332,6 +384,11 @@ static int cp(const char *src, const char *dst)
log_info("Failed to chown %s: %m", dst);
}
+ if (geteuid() == 0 && no_xattr == false) {
+ if (copy_xattr(dest_desc, source_desc) != 0)
+ log_error("Failed to copy xattr %s: %m", dst);
+ }
+
tv[0].tv_sec = sb.st_atime;
tv[0].tv_usec = 0;
tv[1].tv_sec = sb.st_mtime;
@@ -1507,8 +1564,26 @@ static bool check_module_path(const char *path)
return true;
}
-static int find_kmod_module_from_sysfs_node(struct kmod_ctx *ctx, const char *sysfs_node, int sysfs_node_len,
- struct kmod_list **modules)
+static int find_kmod_module_from_sysfs_driver(struct kmod_ctx *ctx, const char *sysfs_node, int sysfs_node_len,
+ struct kmod_module **module)
+{
+ char mod_path[PATH_MAX], mod_realpath[PATH_MAX];
+ const char *mod_name;
+ if (snprintf(mod_path, sizeof(mod_path), "%.*s/driver/module",
+ sysfs_node_len, sysfs_node) >= sizeof(mod_path))
+ return -1;
+
+ if (realpath(mod_path, mod_realpath) == NULL)
+ return -1;
+
+ if ((mod_name = basename(mod_realpath)) == NULL)
+ return -1;
+
+ return kmod_module_new_from_name(ctx, mod_name, module);
+}
+
+static int find_kmod_module_from_sysfs_modalias(struct kmod_ctx *ctx, const char *sysfs_node, int sysfs_node_len,
+ struct kmod_list **modules)
{
char modalias_path[PATH_MAX];
if (snprintf(modalias_path, sizeof(modalias_path), "%.*s/modalias", sysfs_node_len,
@@ -1523,15 +1598,35 @@ static int find_kmod_module_from_sysfs_node(struct kmod_ctx *ctx, const char *sy
ssize_t len = read(modalias_file, alias, sizeof(alias));
alias[len - 1] = '\0';
- return kmod_module_new_from_lookup(ctx, alias, modules);
+ void *list;
+
+ if (hashmap_get_exists(modalias_to_kmod, alias, &list) == 1) {
+ *modules = list;
+ return 0;
+ }
+
+ int ret = kmod_module_new_from_lookup(ctx, alias, modules);
+ if (!ret) {
+ hashmap_put(modalias_to_kmod, strdup(alias), *modules);
+ }
+
+ return ret;
}
static int find_modules_from_sysfs_node(struct kmod_ctx *ctx, const char *sysfs_node, Hashmap *modules)
{
- _cleanup_kmod_module_unref_list_ struct kmod_list *list = NULL;
+ _cleanup_kmod_module_unref_ struct kmod_module *drv = NULL;
+ struct kmod_list *list = NULL;
struct kmod_list *l = NULL;
- if (find_kmod_module_from_sysfs_node(ctx, sysfs_node, strlen(sysfs_node), &list) >= 0) {
+ if (find_kmod_module_from_sysfs_driver(ctx, sysfs_node, strlen(sysfs_node), &drv) >= 0) {
+ char *module = strdup(kmod_module_get_name(drv));
+ if (hashmap_put(modules, module, module) < 0)
+ free(module);
+ return 0;
+ }
+
+ if (find_kmod_module_from_sysfs_modalias(ctx, sysfs_node, strlen(sysfs_node), &list) >= 0) {
kmod_list_foreach(l, list) {
struct kmod_module *mod = kmod_module_get_module(l);
char *module = strdup(kmod_module_get_name(mod));
@@ -1585,10 +1680,25 @@ static void find_suppliers(struct kmod_ctx *ctx)
for (FTSENT *ftsent = fts_read(fts); ftsent != NULL; ftsent = fts_read(fts)) {
if (strcmp(ftsent->fts_name, "modalias") == 0) {
- _cleanup_kmod_module_unref_list_ struct kmod_list *list = NULL;
+ _cleanup_kmod_module_unref_ struct kmod_module *drv = NULL;
+ struct kmod_list *list = NULL;
struct kmod_list *l;
- if (find_kmod_module_from_sysfs_node(ctx, ftsent->fts_parent->fts_path, ftsent->fts_parent->fts_pathlen, &list) < 0)
+ if (find_kmod_module_from_sysfs_driver(ctx, ftsent->fts_parent->fts_path, ftsent->fts_parent->fts_pathlen, &drv) >= 0) {
+ const char *name = kmod_module_get_name(drv);
+ Hashmap *suppliers = hashmap_get(modules_suppliers, name);
+ if (suppliers == NULL) {
+ suppliers = hashmap_new(string_hash_func, string_compare_func);
+ hashmap_put(modules_suppliers, strdup(name), suppliers);
+ }
+
+ find_suppliers_for_sys_node(ctx, suppliers, ftsent->fts_parent->fts_path, ftsent->fts_parent->fts_pathlen);
+
+ /* Skip modalias check */
+ continue;
+ }
+
+ if (find_kmod_module_from_sysfs_modalias(ctx, ftsent->fts_parent->fts_path, ftsent->fts_parent->fts_pathlen, &list) < 0)
continue;
kmod_list_foreach(l, list) {
@@ -1639,6 +1749,9 @@ static int install_dependent_module(struct kmod_ctx *ctx, struct kmod_module *mo
_cleanup_kmod_module_unref_list_ struct kmod_list *modlist = NULL;
_cleanup_kmod_module_unref_list_ struct kmod_list *modpre = NULL;
_cleanup_kmod_module_unref_list_ struct kmod_list *modpost = NULL;
+#ifdef CONFIG_WEAKDEP
+ _cleanup_kmod_module_unref_list_ struct kmod_list *modweak = NULL;
+#endif
log_debug("dracut_install '%s' '%s' OK", path, &path[kerneldirlen]);
install_firmware(mod);
modlist = kmod_module_get_dependencies(mod);
@@ -1652,6 +1765,13 @@ static int install_dependent_module(struct kmod_ctx *ctx, struct kmod_module *mo
*err = *err ? : r;
}
}
+#ifdef CONFIG_WEAKDEP
+ if (*err == 0) {
+ *err = kmod_module_get_weakdeps(mod, &modweak);
+ if (*err == 0)
+ *err = install_dependent_modules(ctx, modweak, NULL);
+ }
+#endif
} else {
log_error("dracut_install '%s' '%s' ERROR", path, &path[kerneldirlen]);
}
@@ -1710,6 +1830,9 @@ static int install_module(struct kmod_ctx *ctx, struct kmod_module *mod)
_cleanup_kmod_module_unref_list_ struct kmod_list *modlist = NULL;
_cleanup_kmod_module_unref_list_ struct kmod_list *modpre = NULL;
_cleanup_kmod_module_unref_list_ struct kmod_list *modpost = NULL;
+#ifdef CONFIG_WEAKDEP
+ _cleanup_kmod_module_unref_list_ struct kmod_list *modweak = NULL;
+#endif
const char *path = NULL;
const char *name = NULL;
@@ -1767,6 +1890,13 @@ static int install_module(struct kmod_ctx *ctx, struct kmod_module *mod)
ret = ret ? : r;
}
}
+#ifdef CONFIG_WEAKDEP
+ if (ret == 0) {
+ ret = kmod_module_get_weakdeps(mod, &modweak);
+ if (ret == 0)
+ ret = install_dependent_modules(ctx, modweak, NULL);
+ }
+#endif
return ret;
}
@@ -1868,6 +1998,7 @@ static int install_modules(int argc, char **argv)
int modinst = 0;
ctx = kmod_new(kerneldir, NULL);
+ kmod_load_resources(ctx);
abskpath = kmod_get_dirname(ctx);
p = strstr(abskpath, "/lib/modules/");
@@ -2196,6 +2327,7 @@ int main(int argc, char **argv)
items = hashmap_new(string_hash_func, string_compare_func);
items_failed = hashmap_new(string_hash_func, string_compare_func);
processed_suppliers = hashmap_new(string_hash_func, string_compare_func);
+ modalias_to_kmod = hashmap_new(string_hash_func, string_compare_func);
if (!items || !items_failed || !processed_suppliers || !modules_loaded) {
log_error("Out of memory");
@@ -2269,11 +2401,17 @@ finish2:
while ((i = hashmap_steal_first(processed_suppliers)))
item_free(i);
+ /*
+ * Note: modalias_to_kmod's values are freed implicitly by the kmod context destruction
+ * in kmod_unref().
+ */
+
hashmap_free(items);
hashmap_free(items_failed);
hashmap_free(modules_loaded);
hashmap_free(modules_suppliers);
hashmap_free(processed_suppliers);
+ hashmap_free(modalias_to_kmod);
if (arg_mod_filter_path)
regfree(&mod_filter_path);