]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/cryptsetup/cryptsetup-generator.c
cryptsetup-generator: guess whether the keyfile argument is two items or one
[thirdparty/systemd.git] / src / cryptsetup / cryptsetup-generator.c
index c51bb9ae189c362341b59bf7a2ec6998e29a69bb..811a9468c1757ccfbb619cdb11c66c41b97a995b 100644 (file)
@@ -37,6 +37,8 @@ typedef struct crypto_device {
 static const char *arg_dest = NULL;
 static bool arg_enabled = true;
 static bool arg_read_crypttab = true;
+static const char *arg_crypttab = NULL;
+static const char *arg_runtime_directory = NULL;
 static bool arg_whitelist = false;
 static Hashmap *arg_disks = NULL;
 static char *arg_default_options = NULL;
@@ -46,36 +48,59 @@ STATIC_DESTRUCTOR_REGISTER(arg_disks, hashmap_freep);
 STATIC_DESTRUCTOR_REGISTER(arg_default_options, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_default_keyfile, freep);
 
-static int split_keyspec(const char *keyspec, char **keyfile, char **keydev) {
-        _cleanup_free_ char *kfile = NULL, *kdev = NULL;
-        char *c;
+static int split_keyspec(const char *keyspec, char **ret_keyfile, char **ret_keydev) {
+        _cleanup_free_ char *keyfile = NULL, *keydev = NULL;
+        const char *c;
 
-        assert(keyspec);
-        assert(keyfile);
-        assert(keydev);
+        assert(ret_keyfile);
+        assert(ret_keydev);
+
+        if (!keyspec) {
+                *ret_keyfile = *ret_keydev = NULL;
+                return 0;
+        }
 
         c = strrchr(keyspec, ':');
         if (c) {
-                kfile = strndup(keyspec, c-keyspec);
-                kdev = strdup(c + 1);
-                if (!*kfile || !*kdev)
-                        return log_oom();
-        } else {
-                /* No keydev specified */
-                kfile = strdup(keyspec);
-                kdev = NULL;
-                if (!*kfile)
+                /* The keydev part has to be either an absolute path to device node (/dev/something,
+                 * /dev/foo/something, or even possibly /dev/foo/something:part), or a fstab device
+                 * specification starting with LABEL= or similar. The keyfile part has the same syntax.
+                 *
+                 * Let's try to guess if the second part looks like a keydev specification, or just part of a
+                 * filename with a colon. fstab_node_to_udev_node() will convert the fstab device syntax to
+                 * an absolute path. If we didn't get an absolute path, assume that it is just part of the
+                 * first keyfile argument. */
+
+                keydev = fstab_node_to_udev_node(c + 1);
+                if (!keydev)
                         return log_oom();
+
+                if (path_is_absolute(keydev))
+                        keyfile = strndup(keyspec, c-keyspec);
+                else {
+                        log_debug("Keyspec argument contains a colon, but \"%s\" doesn't look like a device specification.\n"
+                                  "Assuming that \"%s\" is a single device specification.",
+                                  c + 1, keyspec);
+                        keydev = mfree(keydev);
+                        c = NULL;
+                }
         }
 
-        *keyfile = TAKE_PTR(kfile);
-        *keydev = TAKE_PTR(kdev);
+        if (!c)
+                /* No keydev specified */
+                keyfile = strdup(keyspec);
+
+        if (!keyfile)
+                return log_oom();
+
+        *ret_keyfile = TAKE_PTR(keyfile);
+        *ret_keydev = TAKE_PTR(keydev);
 
         return 0;
 }
 
 static int generate_keydev_mount(const char *name, const char *keydev, const char *keydev_timeout, bool canfail, char **unit, char **mount) {
-        _cleanup_free_ char *u = NULL, *what = NULL, *where = NULL, *name_escaped = NULL, *device_unit = NULL;
+        _cleanup_free_ char *u = NULL, *where = NULL, *name_escaped = NULL, *device_unit = NULL;
         _cleanup_fclose_ FILE *f = NULL;
         int r;
         usec_t timeout_us;
@@ -85,11 +110,11 @@ static int generate_keydev_mount(const char *name, const char *keydev, const cha
         assert(unit);
         assert(mount);
 
-        r = mkdir_parents("/run/systemd/cryptsetup", 0755);
+        r = mkdir_parents(arg_runtime_directory, 0755);
         if (r < 0)
                 return r;
 
-        r = mkdir("/run/systemd/cryptsetup", 0700);
+        r = mkdir(arg_runtime_directory, 0700);
         if (r < 0 && errno != EEXIST)
                 return -errno;
 
@@ -97,7 +122,7 @@ static int generate_keydev_mount(const char *name, const char *keydev, const cha
         if (!name_escaped)
                 return -ENOMEM;
 
-        where = strjoin("/run/systemd/cryptsetup/keydev-", name_escaped);
+        where = strjoin(arg_runtime_directory, "/keydev-", name_escaped);
         if (!where)
                 return -ENOMEM;
 
@@ -113,22 +138,18 @@ static int generate_keydev_mount(const char *name, const char *keydev, const cha
         if (r < 0)
                 return r;
 
-        what = fstab_node_to_udev_node(keydev);
-        if (!what)
-                return -ENOMEM;
-
         fprintf(f,
                 "[Unit]\n"
                 "DefaultDependencies=no\n\n"
                 "[Mount]\n"
                 "What=%s\n"
                 "Where=%s\n"
-                "Options=ro%s\n", what, where, canfail ? ",nofail" : "");
+                "Options=ro%s\n", keydev, where, canfail ? ",nofail" : "");
 
         if (keydev_timeout) {
                 r = parse_sec_fix_0(keydev_timeout, &timeout_us);
                 if (r >= 0) {
-                        r = unit_name_from_path(what, ".device", &device_unit);
+                        r = unit_name_from_path(keydev, ".device", &device_unit);
                         if (r < 0)
                                 return log_error_errno(r, "Failed to generate unit name: %m");
 
@@ -267,11 +288,12 @@ static int create_disk(
                 "[Unit]\n"
                 "Description=Cryptography Setup for %%I\n"
                 "Documentation=man:crypttab(5) man:systemd-cryptsetup-generator(8) man:systemd-cryptsetup@.service(8)\n"
-                "SourcePath=/etc/crypttab\n"
+                "SourcePath=%s\n"
                 "DefaultDependencies=no\n"
                 "Conflicts=umount.target\n"
                 "IgnoreOnIsolate=true\n"
                 "After=%s\n",
+                arg_crypttab,
                 netdev ? "remote-fs-pre.target" : "cryptsetup-pre.target");
 
         if (password) {
@@ -554,27 +576,27 @@ static int add_crypttab_devices(void) {
         if (!arg_read_crypttab)
                 return 0;
 
-        r = fopen_unlocked("/etc/crypttab", "re", &f);
+        r = fopen_unlocked(arg_crypttab, "re", &f);
         if (r < 0) {
                 if (errno != ENOENT)
-                        log_error_errno(errno, "Failed to open /etc/crypttab: %m");
+                        log_error_errno(errno, "Failed to open %s: %m", arg_crypttab);
                 return 0;
         }
 
         if (fstat(fileno(f), &st) < 0) {
-                log_error_errno(errno, "Failed to stat /etc/crypttab: %m");
+                log_error_errno(errno, "Failed to stat %s: %m", arg_crypttab);
                 return 0;
         }
 
         for (;;) {
-                _cleanup_free_ char *line = NULL, *name = NULL, *device = NULL, *keydev = NULL, *keyfile = NULL, *keyspec = NULL, *options = NULL;
+                _cleanup_free_ char *line = NULL, *name = NULL, *device = NULL, *keyspec = NULL, *options = NULL, *keyfile = NULL, *keydev = NULL;
                 crypto_device *d = NULL;
                 char *l, *uuid;
                 int k;
 
                 r = read_line(f, LONG_LINE_MAX, &line);
                 if (r < 0)
-                        return log_error_errno(r, "Failed to read /etc/crypttab: %m");
+                        return log_error_errno(r, "Failed to read %s: %m", arg_crypttab);
                 if (r == 0)
                         break;
 
@@ -586,7 +608,7 @@ static int add_crypttab_devices(void) {
 
                 k = sscanf(l, "%ms %ms %ms %ms", &name, &device, &keyspec, &options);
                 if (k < 2 || k > 4) {
-                        log_error("Failed to parse /etc/crypttab:%u, ignoring.", crypttab_line);
+                        log_error("Failed to parse %s:%u, ignoring.", arg_crypttab, crypttab_line);
                         continue;
                 }
 
@@ -663,6 +685,9 @@ static int run(const char *dest, const char *dest_early, const char *dest_late)
 
         assert_se(arg_dest = dest);
 
+        arg_crypttab = getenv("SYSTEMD_CRYPTTAB") ?: "/etc/crypttab";
+        arg_runtime_directory = getenv("RUNTIME_DIRECTORY") ?: "/run/systemd/cryptsetup";
+
         arg_disks = hashmap_new(&crypt_device_hash_ops);
         if (!arg_disks)
                 return log_oom();