]> git.ipfire.org Git - thirdparty/dracut.git/blobdiff - install/dracut-install.c
dracut_mkdir(): create parent directories as needed.
[thirdparty/dracut.git] / install / dracut-install.c
index cc64bd69b1bbc0c815f9720bdb4dce664fce73f2..3d64ed7a0872c10f619d3de470a91679e7c8c8d5 100644 (file)
@@ -41,6 +41,7 @@
 #include <libkmod.h>
 #include <fts.h>
 #include <regex.h>
+#include <sys/utsname.h>
 
 #include "log.h"
 #include "hashmap.h"
@@ -54,14 +55,19 @@ static bool arg_optional = false;
 static bool arg_silent = false;
 static bool arg_all = false;
 static bool arg_module = false;
+static bool arg_modalias = false;
 static bool arg_resolvelazy = false;
 static bool arg_resolvedeps = false;
 static bool arg_hostonly = false;
+static bool no_xattr = false;
 static char *destrootdir = NULL;
+static char *sysrootdir = NULL;
+static size_t sysrootdirlen = 0;
 static char *kerneldir = NULL;
 static size_t kerneldirlen = 0;
 static char **firmwaredirs = NULL;
 static char **pathdirs;
+static char *ldd = NULL;
 static char *logdir = NULL;
 static char *logfile = NULL;
 FILE *logfile_f = NULL;
@@ -82,6 +88,11 @@ static bool arg_mod_filter_noname = false;
 static int dracut_install(const char *src, const char *dst, bool isdir, bool resolvedeps, bool hashdst);
 
 
+static inline void kmod_module_unrefp(struct kmod_module **p) {
+        if (*p)
+                kmod_module_unref(*p);
+}
+#define _cleanup_kmod_module_unref_ _cleanup_(kmod_module_unrefp)
 
 static inline void kmod_module_unref_listp(struct kmod_list **p) {
         if (*p)
@@ -275,8 +286,13 @@ static int cp(const char *src, const char *dst)
                 if (ret == 0) {
                         struct timeval tv[2];
                         if (fchown(dest_desc, sb.st_uid, sb.st_gid) != 0)
-                                if(fchown(dest_desc, (uid_t) - 1, sb.st_gid) != 0)
-                                    log_error("Failed to chown %s: %m", dst);
+                                if(fchown(dest_desc, (uid_t) - 1, sb.st_gid) != 0) {
+                                        if (geteuid() == 0)
+                                                log_error("Failed to chown %s: %m", dst);
+                                        else
+                                                log_info("Failed to chown %s: %m", dst);
+                                }
+
                         tv[0].tv_sec = sb.st_atime;
                         tv[0].tv_usec = 0;
                         tv[1].tv_sec = sb.st_mtime;
@@ -295,16 +311,24 @@ static int cp(const char *src, const char *dst)
  normal_copy:
         pid = fork();
         if (pid == 0) {
-                execlp("cp", "cp", "--reflink=auto", "--sparse=auto", "--preserve=mode,timestamps,xattr", "-fL", src, dst,
-                       NULL);
+                if (geteuid() == 0 && no_xattr == false)
+                        execlp("cp", "cp", "--reflink=auto", "--sparse=auto", "--preserve=mode,xattr,timestamps", "-fL", src, dst,
+                               NULL);
+                else
+                        execlp("cp", "cp", "--reflink=auto", "--sparse=auto", "--preserve=mode,timestamps", "-fL", src, dst,
+                               NULL);
                 _exit(EXIT_FAILURE);
         }
 
         while (waitpid(pid, &ret, 0) < 0) {
                 if (errno != EINTR) {
                         ret = -1;
-                        log_error("Failed: cp --reflink=auto --sparse=auto --preserve=mode,timestamps,xattr -fL %s %s", src,
-                                  dst);
+                        if (geteuid() == 0 && no_xattr == false)
+                                log_error("Failed: cp --reflink=auto --sparse=auto --preserve=mode,xattr,timestamps -fL %s %s", src,
+                                          dst);
+                        else
+                                log_error("Failed: cp --reflink=auto --sparse=auto --preserve=mode,timestamps -fL %s %s", src,
+                                          dst);
                         break;
                 }
         }
@@ -378,6 +402,75 @@ static int library_install(const char *src, const char *lib)
         return ret;
 }
 
+static char *get_real_file(const char *src, bool fullyresolve)
+{
+        char linktarget[PATH_MAX + 1];
+        ssize_t linksz;
+        _cleanup_free_ char *fullsrcpath;
+        char *abspath = NULL;
+        struct stat sb;
+
+        if (sysrootdirlen) {
+                if (strncmp(src, sysrootdir, sysrootdirlen) == 0)
+                        fullsrcpath = strdup(src);
+                else if (asprintf(&fullsrcpath, "%s/%s", (sysrootdirlen ? sysrootdir : ""), (src[0] == '/' ? src+1 : src)) < 0)
+                                       _exit(EXIT_FAILURE);
+        } else
+                fullsrcpath = strdup(src);
+
+        log_debug("get_real_file('%s')", fullsrcpath);
+
+        if (lstat(fullsrcpath, &sb) < 0)
+            return NULL;
+
+        switch (sb.st_mode & S_IFMT) {
+        case S_IFDIR:
+        case S_IFREG:
+            return strdup(fullsrcpath);
+        case S_IFLNK:
+            break;
+        default:
+            return NULL;
+        }
+
+        linksz = readlink(fullsrcpath, linktarget, sizeof(linktarget));
+        if (linksz < 0)
+                return NULL;
+        linktarget[linksz] = '\0';
+
+        log_debug("get_real_file: readlink('%s') returns '%s'", fullsrcpath, linktarget);
+
+        if (linktarget[0] == '/') {
+                if (asprintf(&abspath, "%s%s", (sysrootdirlen ? sysrootdir : ""), linktarget) < 0)
+                        return NULL;
+        } else {
+                _cleanup_free_ char *fullsrcdir = strdup(fullsrcpath);
+
+                if (!fullsrcdir) {
+                        log_error("Out of memory!");
+                        return NULL;
+                }
+
+                fullsrcdir[dir_len(fullsrcdir)] = '\0';
+
+                if (asprintf(&abspath, "%s/%s", fullsrcdir, linktarget) < 0)
+                        return NULL;
+        }
+
+        if (fullyresolve) {
+                struct stat st;
+                if (lstat(abspath, &st) < 0) {
+                        if (errno != ENOENT)
+                                return NULL;
+                }
+                if (S_ISLNK(st.st_mode))
+                        return get_real_file(abspath, fullyresolve);
+        }
+
+        log_debug("get_real_file('%s') => '%s'", src, abspath);
+        return abspath;
+}
+
 static int resolve_deps(const char *src)
 {
         int ret = 0;
@@ -386,14 +479,20 @@ static int resolve_deps(const char *src)
         size_t linesize = LINE_MAX;
         _cleanup_pclose_ FILE *fptr = NULL;
         _cleanup_free_ char *cmd = NULL;
+        _cleanup_free_ char *fullsrcpath = NULL;
+
+        fullsrcpath = get_real_file(src, true);
+        log_debug("resolve_deps('%s') -> get_real_file('%s', true) = '%s'", src, src, fullsrcpath);
+        if (!fullsrcpath)
+                return 0;
 
-       buf = malloc(LINE_MAX);
-       if (buf == NULL)
-               return -errno;
+        buf = malloc(LINE_MAX);
+        if (buf == NULL)
+                return -errno;
 
         if (strstr(src, ".so") == 0) {
                 _cleanup_close_ int fd = -1;
-                fd = open(src, O_RDONLY | O_CLOEXEC);
+                fd = open(fullsrcpath, O_RDONLY | O_CLOEXEC);
                 if (fd < 0)
                         return -errno;
 
@@ -417,12 +516,14 @@ static int resolve_deps(const char *src)
         }
 
         /* run ldd */
-        ret = asprintf(&cmd, "ldd %s 2>&1", src);
+        ret = asprintf(&cmd, "%s %s 2>&1", ldd, fullsrcpath);
         if (ret < 0) {
                 log_error("Out of memory!");
                 exit(EXIT_FAILURE);
         }
 
+        log_debug("%s", cmd);
+
         ret = 0;
 
         fptr = popen(cmd, "r");
@@ -441,11 +542,18 @@ static int resolve_deps(const char *src)
                         break;
                 }
 
-               /* musl ldd */
-               if (strstr(buf, "Not a valid dynamic program"))
-                       break;
+                /* errors from cross-compiler-ldd */
+                if (strstr(buf, "unable to find sysroot") || strstr(buf, "command not found")) {
+                        log_error("%s", buf);
+                        ret += 1;
+                        break;
+                }
+
+                /* musl ldd */
+                if (strstr(buf, "Not a valid dynamic program"))
+                        break;
 
-               /* glibc */
+                /* glibc */
                 if (strstr(buf, "cannot execute binary file"))
                         break;
 
@@ -464,11 +572,7 @@ static int resolve_deps(const char *src)
                 if (strstr(buf, destrootdir))
                         break;
 
-                p = strstr(buf, "=>");
-                if (!p)
-                        p = buf;
-
-                p = strchr(p, '/');
+                p = strchr(buf, '/');
                 if (p) {
                         char *q;
 
@@ -581,16 +685,80 @@ static bool check_hashmap(Hashmap *hm, const char *item)
         return false;
 }
 
-static int dracut_install(const char *src, const char *dst, bool isdir, bool resolvedeps, bool hashdst)
+static int dracut_mkdir(const char *src) {
+        _cleanup_free_ char *parent = NULL;
+        char *path;
+        struct stat sb;
+
+        parent = strdup(src);
+        if (!parent)
+                return 1;
+
+        path = parent[0] == '/' ? parent+1 : parent;
+        while (path) {
+                path = strstr(path, "/");
+                if (path)
+                        *path = '\0';
+
+                if (stat(parent, &sb) == 0) {
+                        if (!S_ISDIR(sb.st_mode)) {
+                                log_error("%s exists but is not a directory!", parent);
+                                return 1;
+                        }
+                } else if (errno != ENOENT) {
+                        log_error("ERROR: stat '%s': %s", parent, strerror(errno));
+                        return 1;
+                } else {
+                        if (mkdir(parent, 0755) < 0) {
+                                log_error("ERROR: mkdir '%s': %s", parent, strerror(errno));
+                                return 1;
+                        }
+                }
+
+                if (path) {
+                        *path = '/';
+                        path++;
+                }
+        }
+
+        return 0;
+}
+
+static int dracut_install(const char *orig_src, const char *orig_dst, bool isdir, bool resolvedeps, bool hashdst)
 {
         struct stat sb, db;
+        _cleanup_free_ char *fullsrcpath = NULL;
         _cleanup_free_ char *fulldstpath = NULL;
         _cleanup_free_ char *fulldstdir = NULL;
         int ret;
-        bool src_exists = true;
+        bool src_islink = false;
+        bool src_isdir = false;
+        mode_t src_mode = 0;
+        bool dst_exists = true;
         char *i = NULL;
+        _cleanup_free_ char *src;
+        _cleanup_free_ char *dst;
 
-        log_debug("dracut_install('%s', '%s')", src, dst);
+        if (sysrootdirlen) {
+                if (strncmp(orig_src, sysrootdir, sysrootdirlen) == 0) {
+                        src = strdup(orig_src + sysrootdirlen);
+                        fullsrcpath = strdup(orig_src);
+                } else {
+                        src = strdup(orig_src);
+                        if (asprintf(&fullsrcpath, "%s%s", sysrootdir, src) < 0)
+                                _exit(EXIT_FAILURE);
+                }
+                if (strncmp(orig_dst, sysrootdir, sysrootdirlen) == 0)
+                        dst = strdup(orig_dst + sysrootdirlen);
+                else
+                        dst = strdup(orig_dst);
+        } else {
+                src = strdup(orig_src);
+                fullsrcpath = strdup(src);
+                dst = strdup(orig_dst);
+        }
+
+        log_debug("dracut_install('%s', '%s', %d, %d, %d)", src, dst, isdir, resolvedeps, hashdst);
 
         if (check_hashmap(items_failed, src)) {
                 log_debug("hash hit items_failed for '%s'", src);
@@ -602,23 +770,20 @@ static int dracut_install(const char *src, const char *dst, bool isdir, bool res
                 return 0;
         }
 
-        if (lstat(src, &sb) < 0) {
-                src_exists = false;
+        if (lstat(fullsrcpath, &sb) < 0) {
                 if (!isdir) {
                         i = strdup(src);
                         hashmap_put(items_failed, i, i);
                         /* src does not exist */
                         return 1;
                 }
+        } else {
+                src_islink = S_ISLNK(sb.st_mode);
+                src_isdir = S_ISDIR(sb.st_mode);
+                src_mode = sb.st_mode;
         }
 
-        i = strdup(dst);
-        if (!i)
-                return -ENOMEM;
-
-        hashmap_put(items, i, i);
-
-        ret = asprintf(&fulldstpath, "%s/%s", destrootdir, dst);
+        ret = asprintf(&fulldstpath, "%s/%s", destrootdir, (dst[0]=='/' ? (dst+1) : dst));
         if (ret < 0) {
                 log_error("Out of memory!");
                 exit(EXIT_FAILURE);
@@ -626,15 +791,18 @@ static int dracut_install(const char *src, const char *dst, bool isdir, bool res
 
         ret = stat(fulldstpath, &sb);
 
-        if (ret != 0 && (errno != ENOENT)) {
-                log_error("ERROR: stat '%s': %m", fulldstpath);
-                return 1;
+        if (ret != 0) {
+                dst_exists = false;
+                if (errno != ENOENT) {
+                        log_error("ERROR: stat '%s': %m", fulldstpath);
+                        return 1;
+                }
         }
 
         if (ret == 0) {
                 if (resolvedeps && S_ISREG(sb.st_mode) && (sb.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) {
                         log_debug("'%s' already exists, but checking for any deps", fulldstpath);
-                        ret = resolve_deps(src);
+                        ret = resolve_deps(fullsrcpath + sysrootdirlen);
                 } else
                         log_debug("'%s' already exists", fulldstpath);
 
@@ -644,6 +812,10 @@ static int dracut_install(const char *src, const char *dst, bool isdir, bool res
 
         /* check destination directory */
         fulldstdir = strdup(fulldstpath);
+        if (!fulldstdir) {
+                log_error("Out of memory!");
+                return 1;
+        }
         fulldstdir[dir_len(fulldstdir)] = '\0';
 
         ret = stat(fulldstdir, &db);
@@ -670,24 +842,34 @@ static int dracut_install(const char *src, const char *dst, bool isdir, bool res
                 }
         }
 
-        if (isdir && !src_exists) {
+        if (src_isdir) {
+                if (dst_exists) {
+                        if (S_ISDIR(sb.st_mode)) {
+                                log_debug("dest dir '%s' already exists", fulldstpath);
+                                return 0;
+                        }
+                        log_error("dest dir '%s' already exists but is not a directory", fulldstpath);
+                        return 1;
+                }
+
                 log_info("mkdir '%s'", fulldstpath);
-                ret = mkdir(fulldstpath, 0755);
+                ret = dracut_mkdir(fulldstpath);
+                if (ret == 0) {
+                        i = strdup(dst);
+                        if (!i)
+                                return -ENOMEM;
+
+                        hashmap_put(items, i, i);
+                }
                 return ret;
         }
 
         /* ready to install src */
 
-        if (S_ISDIR(sb.st_mode)) {
-                log_info("mkdir '%s'", fulldstpath);
-                ret = mkdir(fulldstpath, sb.st_mode | S_IWUSR);
-                return ret;
-        }
-
-        if (S_ISLNK(sb.st_mode)) {
+        if (src_islink) {
                 _cleanup_free_ char *abspath = NULL;
 
-                abspath = realpath(src, NULL);
+                abspath = get_real_file(src, false);
 
                 if (abspath == NULL)
                         return 1;
@@ -705,7 +887,7 @@ static int dracut_install(const char *src, const char *dst, bool isdir, bool res
                 if (lstat(fulldstpath, &sb) != 0) {
                         _cleanup_free_ char *absdestpath = NULL;
 
-                        ret = asprintf(&absdestpath, "%s/%s", destrootdir, abspath);
+                        ret = asprintf(&absdestpath, "%s/%s", destrootdir, (abspath[0]=='/' ? (abspath+1) : abspath) + sysrootdirlen);
                         if (ret < 0) {
                                 log_error("Out of memory!");
                                 exit(EXIT_FAILURE);
@@ -722,9 +904,9 @@ static int dracut_install(const char *src, const char *dst, bool isdir, bool res
                 return 0;
         }
 
-        if (sb.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) {
+        if (src_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) {
                 if (resolvedeps)
-                        ret += resolve_deps(src);
+                        ret += resolve_deps(fullsrcpath + sysrootdirlen);
                 if (arg_hmac) {
                         /* copy .hmac files also */
                         hmac_install(src, dst, NULL);
@@ -732,14 +914,28 @@ static int dracut_install(const char *src, const char *dst, bool isdir, bool res
         }
 
         log_debug("dracut_install ret = %d", ret);
-        log_info("cp '%s' '%s'", src, fulldstpath);
 
         if (arg_hostonly && !arg_module)
                 mark_hostonly(dst);
 
-        ret += cp(src, fulldstpath);
-        if (ret == 0 && logfile_f)
-                dracut_log_cp(src);
+        if (isdir) {
+                log_info("mkdir '%s'", fulldstpath);
+                ret += dracut_mkdir(fulldstpath);
+        } else {
+                log_info("cp '%s' '%s'", fullsrcpath, fulldstpath);
+                ret += cp(fullsrcpath, fulldstpath);
+        }
+
+        if (ret == 0) {
+                i = strdup(dst);
+                if (!i)
+                        return -ENOMEM;
+
+                hashmap_put(items, i, i);
+
+                if (logfile_f)
+                        dracut_log_cp(src);
+        }
 
         log_debug("dracut_install ret = %d", ret);
 
@@ -755,11 +951,11 @@ static void item_free(char *i)
 static void usage(int status)
 {
               /*                                                                                */
-        printf("Usage: %s -D DESTROOTDIR [OPTION]... -a SOURCE...\n"
-               "or: %s -D DESTROOTDIR [OPTION]... SOURCE DEST\n"
-               "or: %s -D DESTROOTDIR [OPTION]... -m KERNELMODULE [KERNELMODULE â€¦]\n"
+        printf("Usage: %s -D DESTROOTDIR [-r SYSROOTDIR] [OPTION]... -a SOURCE...\n"
+               "or: %s -D DESTROOTDIR [-r SYSROOTDIR] [OPTION]... SOURCE DEST\n"
+               "or: %s -D DESTROOTDIR [-r SYSROOTDIR] [OPTION]... -m KERNELMODULE [KERNELMODULE â€¦]\n"
                "\n"
-               "Install SOURCE to DEST in DESTROOTDIR with all needed dependencies.\n"
+               "Install SOURCE (from rootfs or SYSROOTDIR) to DEST in DESTROOTDIR with all needed dependencies.\n"
                "\n"
                "  KERNELMODULE can have the format:\n"
                "     <absolute path> with a leading /\n"
@@ -767,6 +963,7 @@ static void usage(int status)
                "     <module name>\n"
                "\n"
                "  -D --destrootdir  Install all files to DESTROOTDIR as the root\n"
+               "  -r --sysrootdir   Install all files from SYSROOTDIR\n"
                "  -a --all          Install all SOURCE arguments to DESTROOTDIR\n"
                "  -o --optional     If SOURCE does not exist, do not fail\n"
                "  -d --dir          SOURCE is a directory\n"
@@ -781,6 +978,7 @@ static void usage(int status)
                "  --kerneldir       Specify the kernel module directory\n"
                "  --firmwaredirs    Specify the firmware directory search path with : separation\n"
                "  --silent          Don't display error messages for kernel module install\n"
+               "  --modalias        Only generate module list from /sys/devices modalias list\n"
                "  -o --optional     If kernel module does not exist, do not fail\n"
                "  -p --mod-filter-path      Filter kernel modules by path regexp\n"
                "  -P --mod-filter-nopath    Exclude kernel modules by path regexp\n"
@@ -805,6 +1003,7 @@ static int parse_argv(int argc, char *argv[])
         enum {
                 ARG_VERSION = 0x100,
                 ARG_SILENT,
+                ARG_MODALIAS,
                 ARG_KERNELDIR,
                 ARG_FIRMWAREDIRS,
                 ARG_DEBUG
@@ -824,19 +1023,21 @@ static int parse_argv(int argc, char *argv[])
                 {"module", no_argument, NULL, 'm'},
                 {"fips", no_argument, NULL, 'f'},
                 {"destrootdir", required_argument, NULL, 'D'},
+                {"sysrootdir", required_argument, NULL, 'r'},
                 {"logdir", required_argument, NULL, 'L'},
                 {"mod-filter-path", required_argument, NULL, 'p'},
                 {"mod-filter-nopath", required_argument, NULL, 'P'},
                 {"mod-filter-symbol", required_argument, NULL, 's'},
                 {"mod-filter-nosymbol", required_argument, NULL, 'S'},
                 {"mod-filter-noname", required_argument, NULL, 'N'},
+                {"modalias", no_argument, NULL, ARG_MODALIAS},
                 {"silent", no_argument, NULL, ARG_SILENT},
                 {"kerneldir", required_argument, NULL, ARG_KERNELDIR},
                 {"firmwaredirs", required_argument, NULL, ARG_FIRMWAREDIRS},
                 {NULL, 0, NULL, 0}
         };
 
-        while ((c = getopt_long(argc, argv, "madfhlL:oD:HRp:P:s:S:N:", options, NULL)) != -1) {
+        while ((c = getopt_long(argc, argv, "madfhlL:oD:Hr:Rp:P:s:S:N:v", options, NULL)) != -1) {
                 switch (c) {
                 case ARG_VERSION:
                         puts(PROGRAM_VERSION_STRING);
@@ -850,6 +1051,10 @@ static int parse_argv(int argc, char *argv[])
                 case ARG_SILENT:
                         arg_silent = true;
                         break;
+                case ARG_MODALIAS:
+                        arg_modalias = true;
+                        return 1;
+                        break;
                 case 'v':
                         arg_loglevel = LOG_INFO;
                         break;
@@ -871,6 +1076,10 @@ static int parse_argv(int argc, char *argv[])
                 case 'D':
                         destrootdir = strdup(optarg);
                         break;
+                case 'r':
+                        sysrootdir = strdup(optarg);
+                        sysrootdirlen = strlen(sysrootdir);
+                        break;
                 case 'p':
                         if (regcomp(&mod_filter_path, optarg, REG_NOSUB|REG_EXTENDED) != 0) {
                                 log_error("Module path filter %s is not a regular expression", optarg);
@@ -911,13 +1120,6 @@ static int parse_argv(int argc, char *argv[])
                         break;
                 case ARG_KERNELDIR:
                         kerneldir = strdup(optarg);
-                        if ((strncmp("/lib/modules/", kerneldir, 13) != 0)
-                            && (strncmp("/usr/lib/modules/", kerneldir, 17) != 0)) {
-                                char *p;
-                                p = strstr(kerneldir, "/lib/modules/");
-                                if (p != NULL)
-                                        kerneldirlen = p - kerneldir;
-                        }
                         break;
                 case ARG_FIRMWAREDIRS:
                         firmwaredirs = strv_split(optarg, ":");
@@ -936,6 +1138,16 @@ static int parse_argv(int argc, char *argv[])
                 }
         }
 
+        if (!kerneldir) {
+                struct utsname buf;
+                uname(&buf);
+                kerneldir = strdup(buf.version);
+        }
+
+        if (arg_modalias) {
+                return 1;
+        }
+
         if (arg_module) {
                 if (!firmwaredirs) {
                         char *path = NULL;
@@ -952,6 +1164,7 @@ static int parse_argv(int argc, char *argv[])
                         firmwaredirs = strv_split(path, ":");
                 }
         }
+
         if (!optind || optind == argc) {
                 log_error("No SOURCE argument given");
                 usage(EXIT_FAILURE);
@@ -995,6 +1208,7 @@ static char **find_binary(const char *src)
 {
         char **ret = NULL;
         char **q;
+        char *fullsrcpath;
         char *newsrc = NULL;
 
         STRV_FOREACH(q, pathdirs) {
@@ -1007,15 +1221,28 @@ static char **find_binary(const char *src)
                         exit(EXIT_FAILURE);
                 }
 
-                if (stat(newsrc, &sb) != 0) {
-                        log_debug("stat(%s) != 0", newsrc);
+                fullsrcpath = get_real_file(newsrc, false);
+
+                if (!fullsrcpath) {
+                        log_debug("get_real_file(%s) not found", newsrc);
                         free(newsrc);
                         newsrc = NULL;
                         continue;
                 }
 
+                if (lstat(fullsrcpath, &sb) != 0) {
+                        log_debug("stat(%s) != 0", fullsrcpath);
+                        free(newsrc);
+                        newsrc = NULL;
+                        free(fullsrcpath);
+                        fullsrcpath = NULL;
+                        continue;
+                }
+
                 strv_push(&ret, newsrc);
 
+                free(fullsrcpath);
+                fullsrcpath = NULL;
         };
 
         if (ret) {
@@ -1035,7 +1262,7 @@ static int install_one(const char *src, const char *dst)
         if (strchr(src, '/') == NULL) {
                 char **p = find_binary(src);
                 if (p) {
-                       char **q = NULL;
+                        char **q = NULL;
                         STRV_FOREACH(q, p) {
                                 char *newsrc = *q;
                                 log_debug("dracut_install '%s' '%s'", newsrc, dst);
@@ -1071,7 +1298,7 @@ static int install_all(int argc, char **argv)
                 if (strchr(argv[i], '/') == NULL) {
                         char **p = find_binary(argv[i]);
                         if (p) {
-                               char **q = NULL;
+                                char **q = NULL;
                                 STRV_FOREACH(q, p) {
                                         char *newsrc = *q;
                                         log_debug("dracut_install '%s'", newsrc);
@@ -1124,6 +1351,8 @@ static int install_firmware(struct kmod_module *mod)
                 ret = -1;
                 STRV_FOREACH(q, firmwaredirs) {
                         _cleanup_free_ char *fwpath = NULL;
+                        _cleanup_free_ char *fwpath_xz = NULL;
+                        const char *fw;
                         struct stat sb;
                         int r;
 
@@ -1133,12 +1362,21 @@ static int install_firmware(struct kmod_module *mod)
                                 exit(EXIT_FAILURE);
                         }
 
+                        fw = fwpath;
                         if (stat(fwpath, &sb) != 0) {
-                                log_debug("stat(%s) != 0", fwpath);
-                                continue;
+                                r = asprintf(&fwpath_xz, "%s.xz", fwpath);
+                                if (r < 0) {
+                                        log_error("Out of memory!");
+                                        exit(EXIT_FAILURE);
+                                }
+                                if (stat(fwpath_xz, &sb) != 0) {
+                                        log_debug("stat(%s) != 0", fwpath);
+                                        continue;
+                                }
+                                fw = fwpath_xz;
                         }
 
-                        ret = dracut_install(fwpath, fwpath, false, false, true);
+                        ret = dracut_install(fw, fw, false, false, true);
                         if (ret == 0)
                                 log_debug("dracut_install '%s' OK", fwpath);
                 }
@@ -1205,21 +1443,62 @@ static bool check_module_path(const char *path)
         return true;
 }
 
-static bool check_module_hostonly(struct kmod_module *mod)
+static int install_dependent_modules(struct kmod_list *modlist)
 {
-        const char *name = kmod_module_get_name(mod);
+        struct kmod_list *itr;
+        const char *path = NULL;
+        const char *name = NULL;
+        int ret = 0;
 
-        if (check_hashmap(modules_loaded, name))
-                return true;
+        kmod_list_foreach(itr, modlist) {
+               _cleanup_kmod_module_unref_ struct kmod_module *mod = NULL;
+                mod = kmod_module_get_module(itr);
+                path = kmod_module_get_path(mod);
 
-        return false;
+                if (path == NULL)
+                        continue;
+
+               if (check_hashmap(items_failed, path))
+                       return -1;
+
+               if (check_hashmap(items, path)) {
+                       continue;
+               }
+
+                name = kmod_module_get_name(mod);
+
+                if (arg_mod_filter_noname && (regexec(&mod_filter_noname, name, 0, NULL, 0) == 0)) {
+                        continue;
+                }
+
+                ret = dracut_install(path, &path[kerneldirlen], false, false, true);
+                if (ret == 0) {
+                       _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;
+                        log_debug("dracut_install '%s' '%s' OK", path, &path[kerneldirlen]);
+                        install_firmware(mod);
+                       modlist = kmod_module_get_dependencies(mod);
+                       ret = install_dependent_modules(modlist);
+                       if (ret == 0) {
+                               ret = kmod_module_get_softdeps(mod, &modpre, &modpost);
+                               if (ret == 0)
+                                       ret = install_dependent_modules(modpre);
+                       }
+                } else {
+                        log_error("dracut_install '%s' '%s' ERROR", path, &path[kerneldirlen]);
+                }
+        }
+
+        return ret;
 }
 
 static int install_module(struct kmod_module *mod)
 {
         int ret = 0;
-        struct kmod_list *itr;
         _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;
         const char *path = NULL;
         const char *name = NULL;
 
@@ -1229,7 +1508,7 @@ static int install_module(struct kmod_module *mod)
                 return 0;
         }
 
-        if (arg_hostonly && ! check_module_hostonly(mod)) {
+        if (arg_hostonly && !check_hashmap(modules_loaded, name)) {
                 log_debug("dracut_install '%s' not hostonly", name);
                 return 0;
         }
@@ -1245,7 +1524,7 @@ static int install_module(struct kmod_module *mod)
                 return 0;
 
         if (!check_module_path(path) || !check_module_symbols(mod)) {
-                log_debug("No symbol or patch match for '%s'", path);
+                log_debug("No symbol or path match for '%s'", path);
                 return 1;
         }
 
@@ -1262,84 +1541,162 @@ static int install_module(struct kmod_module *mod)
         install_firmware(mod);
 
         modlist = kmod_module_get_dependencies(mod);
-        kmod_list_foreach(itr, modlist) {
-                mod = kmod_module_get_module(itr);
-                path = kmod_module_get_path(mod);
+        ret = install_dependent_modules(modlist);
 
-                name = kmod_module_get_name(mod);
-                if (arg_mod_filter_noname && (regexec(&mod_filter_noname, name, 0, NULL, 0) == 0)) {
-                        kmod_module_unref(mod);
+        if (ret == 0) {
+                ret = kmod_module_get_softdeps(mod, &modpre, &modpost);
+                if (ret == 0)
+                        ret = install_dependent_modules(modpre);
+        }
+
+        return ret;
+}
+
+static int modalias_list(struct kmod_ctx *ctx)
+{
+        int err;
+        struct kmod_list *loaded_list = NULL;
+        struct kmod_list *itr, *l;
+        _cleanup_fts_close_ FTS *fts = NULL;
+
+        {
+                char *paths[] = { "/sys/devices", NULL };
+                fts = fts_open(paths, FTS_NOCHDIR|FTS_NOSTAT, NULL);
+        }
+        for (FTSENT *ftsent = fts_read(fts); ftsent != NULL; ftsent = fts_read(fts)) {
+                _cleanup_fclose_ FILE *f = NULL;
+                _cleanup_kmod_module_unref_list_ struct kmod_list *list = NULL;
+                struct kmod_list *l;
+
+                int err;
+
+                char alias[2048];
+                size_t len;
+
+                if (strncmp("modalias", ftsent->fts_name, 8) != 0)
                         continue;
+                if (!(f = fopen(ftsent->fts_accpath, "r")))
+                        continue;
+
+                if(!fgets(alias, sizeof(alias), f))
+                        continue;
+
+                len = strlen(alias);
+
+                if (len == 0)
+                        continue;
+
+                if (alias[len-1] == '\n')
+                        alias[len-1] = 0;
+
+                err = kmod_module_new_from_lookup(ctx, alias, &list);
+                if (err < 0)
+                        continue;
+
+                kmod_list_foreach(l, list) {
+                        struct kmod_module *mod = kmod_module_get_module(l);
+                        char *name = strdup(kmod_module_get_name(mod));
+                        kmod_module_unref(mod);
+                        hashmap_put(modules_loaded, name, name);
                 }
-                ret = dracut_install(path, &path[kerneldirlen], false, false, true);
-                if (ret == 0) {
-                        log_debug("dracut_install '%s' '%s' OK", path, &path[kerneldirlen]);
-                        install_firmware(mod);
-                } else {
-                        log_error("dracut_install '%s' '%s' ERROR", path, &path[kerneldirlen]);
-                }
-                kmod_module_unref(mod);
         }
 
-        return ret;
+        err = kmod_module_new_from_loaded(ctx, &loaded_list);
+        if (err < 0) {
+                errno = err;
+                log_error("Could not get list of loaded modules: %m. Switching to non-hostonly mode.");
+                arg_hostonly = false;
+        } else {
+                kmod_list_foreach(itr, loaded_list) {
+                        _cleanup_kmod_module_unref_list_ struct kmod_list *modlist = NULL;
+
+                        struct kmod_module *mod = kmod_module_get_module(itr);
+                        char *name = strdup(kmod_module_get_name(mod));
+                        hashmap_put(modules_loaded, name, name);
+                        kmod_module_unref(mod);
+
+                        /* also put the modules from the new kernel in the hashmap,
+                         * which resolve the name as an alias, in case a kernel module is
+                         * renamed.
+                         */
+                        err = kmod_module_new_from_lookup(ctx, name, &modlist);
+                        if (err < 0)
+                                continue;
+                        if (!modlist)
+                                continue;
+                        kmod_list_foreach(l, modlist) {
+                                mod = kmod_module_get_module(l);
+                                char *name = strdup(kmod_module_get_name(mod));
+                                hashmap_put(modules_loaded, name, name);
+                                kmod_module_unref(mod);
+                        }
+                }
+                kmod_module_unref_list(loaded_list);
+        }
+        return 0;
 }
 
 static int install_modules(int argc, char **argv)
 {
         _cleanup_kmod_unref_ struct kmod_ctx *ctx = NULL;
-        struct kmod_list *loaded_list = NULL;
-        struct kmod_list *itr, *l;
-        int err;
+        struct kmod_list *itr;
 
         struct kmod_module *mod = NULL, *mod_o = NULL;
 
-        const char *modname = NULL;
+        const char *abskpath = NULL;
+        char *p;
         int i;
+        int modinst = 0;
 
         ctx = kmod_new(kerneldir, NULL);
+        abskpath = kmod_get_dirname(ctx);
+
+        p = strstr(abskpath, "/lib/modules/");
+        if (p != NULL)
+                kerneldirlen = p - abskpath;
+
         if (arg_hostonly) {
-                err = kmod_module_new_from_loaded(ctx, &loaded_list);
-                if (err < 0) {
-                        errno = err;
-                        log_error("Could not get list of loaded modules: %m. Switching to non-hostonly mode.");
-                        arg_hostonly = false;
+                char *modalias_file;
+                modalias_file = getenv("DRACUT_KERNEL_MODALIASES");
+
+                if (modalias_file == NULL) {
+                        modalias_list(ctx);
                 } else {
-                        kmod_list_foreach(itr, loaded_list) {
-                                _cleanup_kmod_module_unref_list_ struct kmod_list *modlist = NULL;
+                        _cleanup_fclose_ FILE *f = NULL;
+                        if ((f = fopen(modalias_file, "r"))) {
+                                char name[2048];
 
-                                struct kmod_module *mod = kmod_module_get_module(itr);
-                                char *name = strdup(kmod_module_get_name(mod));
-                                hashmap_put(modules_loaded, name, name);
-                                kmod_module_unref(mod);
+                                while (!feof(f)) {
+                                        size_t len;
+                                        char *dupname = NULL;
 
-                                /* also put the modules from the new kernel in the hashmap,
-                                 * which resolve the name as an alias, in case a kernel module is
-                                 * renamed.
-                                 */
-                                err = kmod_module_new_from_lookup(ctx, name, &modlist);
-                                if (err < 0)
-                                        continue;
-                                if (!modlist)
-                                        continue;
-                                kmod_list_foreach(l, modlist) {
-                                        mod = kmod_module_get_module(l);
-                                        char *name = strdup(kmod_module_get_name(mod));
-                                        hashmap_put(modules_loaded, name, name);
-                                        kmod_module_unref(mod);
+                                        if(!(fgets(name, sizeof(name), f)))
+                                                continue;
+                                        len = strlen(name);
+
+                                        if (len == 0)
+                                                continue;
+
+                                        if (name[len-1] == '\n')
+                                                name[len-1] = 0;
+
+                                        log_debug("Adding module '%s' to hostonly module list", name);
+                                        dupname = strdup(name);
+                                        hashmap_put(modules_loaded, dupname, dupname);
                                 }
                         }
-                        kmod_module_unref_list(loaded_list);
                 }
+
         }
 
         for (i = 0; i < argc; i++) {
                 int r = 0;
                 int ret = -1;
-
                 log_debug("Handle module '%s'", argv[i]);
 
                 if (argv[i][0] == '/') {
                         _cleanup_kmod_module_unref_list_ struct kmod_list *modlist = NULL;
+                        _cleanup_free_ const char *modname = NULL;
 
                         r = kmod_module_new_from_path(ctx, argv[i], &mod_o);
                         if (r < 0) {
@@ -1349,7 +1706,7 @@ static int install_modules(int argc, char **argv)
                                 continue;
                         }
                         /* Check, if we have to load another module with that name instead */
-                        modname = kmod_module_get_name(mod_o);
+                        modname = strdup(kmod_module_get_name(mod_o));
 
                         if (!modname) {
                                 if (!arg_optional) {
@@ -1393,6 +1750,7 @@ static int install_modules(int argc, char **argv)
                                         return -ENOENT;
                                 };
                                 ret = ( ret == 0 ? 0 : r );
+                                modinst = 1;
                         }
                 } else if (argv[i][0] == '=') {
                         _cleanup_free_ char *path1 = NULL, *path2 = NULL, *path3 = NULL;
@@ -1425,6 +1783,7 @@ static int install_modules(int argc, char **argv)
 
                         for (FTSENT *ftsent = fts_read(fts); ftsent != NULL; ftsent = fts_read(fts)) {
                                 _cleanup_kmod_module_unref_list_ struct kmod_list *modlist = NULL;
+                                _cleanup_free_ const char *modname = NULL;
 
                                 if((ftsent->fts_info == FTS_D) && !check_module_path(ftsent->fts_accpath)) {
                                         fts_set(fts, ftsent, FTS_SKIP);
@@ -1447,7 +1806,7 @@ static int install_modules(int argc, char **argv)
                                 }
 
                                 /* Check, if we have to load another module with that name instead */
-                                modname = kmod_module_get_name(mod_o);
+                                modname = strdup(kmod_module_get_name(mod_o));
 
                                 if (!modname) {
                                         log_error("Failed to get name for module '%s'", ftsent->fts_accpath);
@@ -1486,6 +1845,7 @@ static int install_modules(int argc, char **argv)
                                                 return -ENOENT;
                                         };
                                         ret = ( ret == 0 ? 0 : r );
+                                        modinst = 1;
                                 }
                         }
                         if (errno) {
@@ -1532,10 +1892,11 @@ static int install_modules(int argc, char **argv)
                                         return -ENOENT;
                                 };
                                 ret = ( ret == 0 ? 0 : r );
+                                modinst = 1;
                         }
                 }
 
-                if ((ret != 0) && (!arg_optional)) {
+                if ((modinst != 0) && (ret != 0) && (!arg_optional)) {
                         if (!arg_silent)
                                 log_error("ERROR: installing '%s'", argv[i]);
                         return EXIT_FAILURE;
@@ -1550,6 +1911,7 @@ int main(int argc, char **argv)
         int r;
         char *i;
         char *path = NULL;
+        char *env_no_xattr = NULL;
 
         r = parse_argv(argc, argv);
         if (r <= 0)
@@ -1563,7 +1925,27 @@ int main(int argc, char **argv)
 
         log_open();
 
-        path = getenv("PATH");
+        modules_loaded = hashmap_new(string_hash_func, string_compare_func);
+        if (arg_modalias) {
+                Iterator i;
+                char *name;
+                _cleanup_kmod_unref_ struct kmod_ctx *ctx = NULL;
+                ctx = kmod_new(kerneldir, NULL);
+
+                modalias_list(ctx);
+                HASHMAP_FOREACH(name, modules_loaded, i) {
+                        printf("%s\n", name);
+                }
+                exit(0);
+        }
+
+        log_debug("Program arguments:");
+        for (r = 0; r < argc; r++)
+                log_debug("%s", argv[r]);
+
+        path = getenv("DRACUT_INSTALL_PATH");
+        if (path == NULL)
+                path = getenv("PATH");
 
         if (path == NULL) {
                 log_error("PATH is not set");
@@ -1572,6 +1954,15 @@ int main(int argc, char **argv)
 
         log_debug("PATH=%s", path);
 
+        ldd = getenv("DRACUT_LDD");
+        if (ldd == NULL)
+                ldd = "ldd";
+        log_debug("LDD=%s", ldd);
+
+        env_no_xattr = getenv("DRACUT_NO_XATTR");
+        if (env_no_xattr != NULL)
+                no_xattr = true;
+
         pathdirs = strv_split(path, ":");
 
         umask(0022);
@@ -1601,7 +1992,6 @@ 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);
-        modules_loaded = hashmap_new(string_hash_func, string_compare_func);
 
         if (!items || !items_failed || !modules_loaded) {
                 log_error("Out of memory");