gpt-auto: try to handle LUKS root partitions better
authorLennart Poettering <lennart@poettering.net>
Sun, 6 Sep 2015 21:04:32 +0000 (23:04 +0200)
committerLennart Poettering <lennart@poettering.net>
Sun, 6 Sep 2015 22:11:51 +0000 (00:11 +0200)
If the root file system is located on an encrypted root disk, we'll not
find the GPT partition table for it. Let's fix that by following the
slaves/ symlinks in /sys for the device. We only handle devices having
exactly one backing device.

Also see: #1167

src/gpt-auto-generator/gpt-auto-generator.c

index 0a34f86..2ab7257 100644 (file)
@@ -799,6 +799,10 @@ static int get_block_device(const char *path, dev_t *dev) {
         assert(path);
         assert(dev);
 
+        /* Get's the block device directly backing a file system. If
+         * the block device is encrypted, returns the device mapper
+         * block device. */
+
         if (lstat(path, &st))
                 return -errno;
 
@@ -816,6 +820,77 @@ static int get_block_device(const char *path, dev_t *dev) {
         return 0;
 }
 
+static int get_block_device_harder(const char *path, dev_t *dev) {
+        _cleanup_closedir_ DIR *d = NULL;
+        _cleanup_free_ char *p = NULL, *t = NULL;
+        struct dirent *de, *found = NULL;
+        const char *q;
+        unsigned maj, min;
+        dev_t dt;
+        int r;
+
+        assert(path);
+        assert(dev);
+
+        /* Gets the backing block device for a file system, and
+         * handles LUKS encrypted file systems, looking for its
+         * immediate parent, if there is one. */
+
+        r = get_block_device(path, &dt);
+        if (r <= 0)
+                return r;
+
+        if (asprintf(&p, "/sys/dev/block/%u:%u/slaves", major(dt), minor(dt)) < 0)
+                return -ENOMEM;
+
+        d = opendir(p);
+        if (!d) {
+                if (errno == ENOENT)
+                        goto fallback;
+
+                return -errno;
+        }
+
+        FOREACH_DIRENT_ALL(de, d, return -errno) {
+
+                if (STR_IN_SET(de->d_name, ".", ".."))
+                        continue;
+
+                if (!IN_SET(de->d_type, DT_LNK, DT_UNKNOWN))
+                        continue;
+
+                if (found) /* Don't try to support multiple backing block devices */
+                        goto fallback;
+
+                found = de;
+                break;
+        }
+
+        if (!found)
+                goto fallback;
+
+        q = strjoina(p, "/", found->d_name, "/dev");
+
+        r = read_one_line_file(q, &t);
+        if (r == -ENOENT)
+                goto fallback;
+        if (r < 0)
+                return r;
+
+        if (sscanf(t, "%u:%u", &maj, &min) != 2)
+                return -EINVAL;
+
+        if (maj == 0)
+                goto fallback;
+
+        *dev = makedev(maj, min);
+        return 1;
+
+fallback:
+        *dev = dt;
+        return 1;
+}
+
 static int parse_proc_cmdline_item(const char *key, const char *value) {
         int r;
 
@@ -883,11 +958,11 @@ static int add_mounts(void) {
         dev_t devno;
         int r;
 
-        r = get_block_device("/", &devno);
+        r = get_block_device_harder("/", &devno);
         if (r < 0)
                 return log_error_errno(r, "Failed to determine block device of root file system: %m");
         else if (r == 0) {
-                r = get_block_device("/usr", &devno);
+                r = get_block_device_harder("/usr", &devno);
                 if (r < 0)
                         return log_error_errno(r, "Failed to determine block device of /usr file system: %m");
                 else if (r == 0) {