discover: Handle BTRFS root subvolumes
authorSam Mendoza-Jonas <sam@mendozajonas.com>
Tue, 5 Jan 2016 04:01:58 +0000 (15:01 +1100)
committerSam Mendoza-Jonas <sam@mendozajonas.com>
Tue, 9 Feb 2016 02:37:37 +0000 (13:37 +1100)
During install some distributions[0] will create subvolumes when formatting
the root filesystem with BTRFS. In particular this can mean that
bootloader config files will appear (in the case of GRUB) under
/var/petitboot/mnt/dev/$device/@/boot/grub/
rather than the expected
/var/petitboot/mnt/dev/$device/boot/grub/

If this is the case, perform all file operations from the parser
relative to this subvolume rather than the mount point. At the moment
this only supports the trivial case where the subvolume name for root is
blank (ie. '@').

[0] In particular, Ubuntu from at least 14.04

Signed-off-by: Sam Mendoza-Jonas <sam@mendozajonas.com>
discover/device-handler.c
discover/device-handler.h
discover/parser.c

index 5df070073b4d298c8a49a5488010434b73ff4f32..77f9e3c59b9bb761d02a668f26ad130187f516d9 100644 (file)
@@ -1285,6 +1285,28 @@ static inline const char *get_device_path(struct discover_device *dev)
        return dev->ramdisk ? dev->ramdisk->snapshot : dev->device_path;
 }
 
+static char *check_subvols(struct discover_device *dev)
+{
+       const char *fstype = discover_device_get_param(dev, "ID_FS_TYPE");
+       struct stat sb;
+       char *path;
+       int rc;
+
+       if (strncmp(fstype, "btrfs", strlen("btrfs")))
+               return dev->mount_path;
+
+       /* On btrfs a device's root may be under a subvolume path */
+       path = join_paths(dev, dev->mount_path, "@");
+       rc = stat(path, &sb);
+       if (!rc && S_ISDIR(sb.st_mode)) {
+               pb_debug("Using '%s' for btrfs root path\n", path);
+               return path;
+       }
+
+       talloc_free(path);
+       return dev->mount_path;
+}
+
 static bool check_existing_mount(struct discover_device *dev)
 {
        struct stat devstat, mntstat;
@@ -1326,6 +1348,7 @@ static bool check_existing_mount(struct discover_device *dev)
 
                if (mntstat.st_rdev == devstat.st_rdev) {
                        dev->mount_path = talloc_strdup(dev, mnt->mnt_dir);
+                       dev->root_path = check_subvols(dev);
                        dev->mounted_rw = !!hasmntopt(mnt, "rw");
                        dev->mounted = true;
                        dev->unmount = false;
@@ -1388,6 +1411,7 @@ static int mount_device(struct discover_device *dev)
                dev->mounted = true;
                dev->mounted_rw = false;
                dev->unmount = true;
+               dev->root_path = check_subvols(dev);
                return 0;
        }
 
@@ -1426,6 +1450,7 @@ static int umount_device(struct discover_device *dev)
 
        talloc_free(dev->mount_path);
        dev->mount_path = NULL;
+       dev->root_path = NULL;
 
        return 0;
 }
index e5501ec41d6e4911acc39aba2fd9efcc617eb9e8..12f5ce07e139d0d5ba6a142d01224ec4ce4f2272 100644 (file)
@@ -25,6 +25,7 @@ struct discover_device {
        const char              *label;
 
        char                    *mount_path;
+       char                    *root_path;
        const char              *device_path;
        struct ramdisk_device   *ramdisk;
        bool                    mounted;
index 8e767c6727c27f00ddc1ecb5ecaf173d288fdd60..fbf31b2806060009a4d136906fc5b03be64917dc 100644 (file)
@@ -25,7 +25,7 @@ static char *local_path(struct discover_context *ctx,
                struct discover_device *dev,
                const char *filename)
 {
-       return join_paths(ctx, dev->mount_path, filename);
+       return join_paths(ctx, dev->root_path, filename);
 }
 
 int parser_request_file(struct discover_context *ctx,