Blame SOURCES/kvm-qga-add-implementation-of-guest-get-disks-for-Linux.patch

a19a21
From 086957b970a8f4165249589e2bc0cc08d1800db3 Mon Sep 17 00:00:00 2001
709dde
From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= <marcandre.lureau@redhat.com>
a19a21
Date: Wed, 16 Dec 2020 16:06:12 -0500
a19a21
Subject: [PATCH 11/14] qga: add implementation of guest-get-disks for Linux
709dde
MIME-Version: 1.0
709dde
Content-Type: text/plain; charset=UTF-8
709dde
Content-Transfer-Encoding: 8bit
709dde
709dde
RH-Author: Marc-André Lureau <marcandre.lureau@redhat.com>
a19a21
Message-id: <20201216160615.324213-8-marcandre.lureau@redhat.com>
a19a21
Patchwork-id: 100478
a19a21
O-Subject: [RHEL-8.4.0 qemu-kvm PATCH v2 07/10] qga: add implementation of guest-get-disks for Linux
a19a21
Bugzilla: 1859494
a19a21
RH-Acked-by: Danilo de Paula <ddepaula@redhat.com>
a19a21
RH-Acked-by: Sergio Lopez Pascual <slp@redhat.com>
a19a21
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
709dde
709dde
From: Tomáš Golembiovský <tgolembi@redhat.com>
709dde
709dde
The command lists all disks (real and virtual) as well as disk
709dde
partitions. For each disk the list of dependent disks is also listed and
709dde
/dev path is used as a handle so it can be matched with "name" field of
709dde
other returned disk entries. For disk partitions the "dependents" list
709dde
is populated with the the parent device for easier tracking of
709dde
hierarchy.
709dde
709dde
Example output:
709dde
{
709dde
  "return": [
709dde
    ...
709dde
    {
709dde
      "name": "/dev/dm-0",
709dde
      "partition": false,
709dde
      "dependents": [
709dde
        "/dev/sda2"
709dde
      ],
709dde
      "alias": "luks-7062202e-5b9b-433e-81e8-6628c40da9f7"
709dde
    },
709dde
    {
709dde
      "name": "/dev/sda2",
709dde
      "partition": true,
709dde
      "dependents": [
709dde
        "/dev/sda"
709dde
      ]
709dde
    },
709dde
    {
709dde
      "name": "/dev/sda",
709dde
      "partition": false,
709dde
      "address": {
709dde
        "serial": "SAMSUNG_MZ7LN512HCHP-000L1_S1ZKNXAG822493",
709dde
        "bus-type": "sata",
709dde
        ...
709dde
        "dev": "/dev/sda",
709dde
        "target": 0
709dde
      },
709dde
      "dependents": []
709dde
    },
709dde
    ...
709dde
  ]
709dde
}
709dde
709dde
Signed-off-by: Tomáš Golembiovský <tgolembi@redhat.com>
709dde
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
709dde
*add missing stub for !defined(CONFIG_FSFREEZE)
709dde
*remove unused deps_dir variable
709dde
Signed-off-by: Michael Roth <michael.roth@amd.com>
709dde
(cherry picked from commit fed3956429d560a06fc2d2fcf1a01efb58659f87)
709dde
Signed-off-by: Danilo C. L. de Paula <ddepaula@redhat.com>
709dde
---
709dde
 qga/commands-posix.c | 303 +++++++++++++++++++++++++++++++++++++++++--
709dde
 1 file changed, 292 insertions(+), 11 deletions(-)
709dde
709dde
diff --git a/qga/commands-posix.c b/qga/commands-posix.c
a19a21
index 5095104afc0..96f5ddafd3a 100644
709dde
--- a/qga/commands-posix.c
709dde
+++ b/qga/commands-posix.c
a19a21
@@ -1152,13 +1152,27 @@ static void build_guest_fsinfo_for_virtual_device(char const *syspath,
709dde
     closedir(dir);
709dde
 }
709dde
 
709dde
+static bool is_disk_virtual(const char *devpath, Error **errp)
709dde
+{
709dde
+    g_autofree char *syspath = realpath(devpath, NULL);
709dde
+
709dde
+    if (!syspath) {
709dde
+        error_setg_errno(errp, errno, "realpath(\"%s\")", devpath);
709dde
+        return false;
709dde
+    }
709dde
+    return strstr(syspath, "/devices/virtual/block/") != NULL;
709dde
+}
709dde
+
709dde
 /* Dispatch to functions for virtual/real device */
709dde
 static void build_guest_fsinfo_for_device(char const *devpath,
709dde
                                           GuestFilesystemInfo *fs,
709dde
                                           Error **errp)
709dde
 {
709dde
-    char *syspath = realpath(devpath, NULL);
709dde
+    ERRP_GUARD();
709dde
+    g_autofree char *syspath = NULL;
709dde
+    bool is_virtual = false;
709dde
 
709dde
+    syspath = realpath(devpath, NULL);
709dde
     if (!syspath) {
709dde
         error_setg_errno(errp, errno, "realpath(\"%s\")", devpath);
709dde
         return;
a19a21
@@ -1169,16 +1183,281 @@ static void build_guest_fsinfo_for_device(char const *devpath,
709dde
     }
709dde
 
709dde
     g_debug("  parse sysfs path '%s'", syspath);
709dde
-
709dde
-    if (strstr(syspath, "/devices/virtual/block/")) {
709dde
+    is_virtual = is_disk_virtual(syspath, errp);
709dde
+    if (*errp != NULL) {
709dde
+        return;
709dde
+    }
709dde
+    if (is_virtual) {
709dde
         build_guest_fsinfo_for_virtual_device(syspath, fs, errp);
709dde
     } else {
709dde
         build_guest_fsinfo_for_real_device(syspath, fs, errp);
709dde
     }
709dde
+}
709dde
+
709dde
+#ifdef CONFIG_LIBUDEV
709dde
+
709dde
+/*
709dde
+ * Wrapper around build_guest_fsinfo_for_device() for getting just
709dde
+ * the disk address.
709dde
+ */
709dde
+static GuestDiskAddress *get_disk_address(const char *syspath, Error **errp)
709dde
+{
709dde
+    g_autoptr(GuestFilesystemInfo) fs = NULL;
709dde
 
709dde
-    free(syspath);
709dde
+    fs = g_new0(GuestFilesystemInfo, 1);
709dde
+    build_guest_fsinfo_for_device(syspath, fs, errp);
709dde
+    if (fs->disk != NULL) {
709dde
+        return g_steal_pointer(&fs->disk->value);
709dde
+    }
709dde
+    return NULL;
709dde
 }
709dde
 
709dde
+static char *get_alias_for_syspath(const char *syspath)
709dde
+{
709dde
+    struct udev *udev = NULL;
709dde
+    struct udev_device *udevice = NULL;
709dde
+    char *ret = NULL;
709dde
+
709dde
+    udev = udev_new();
709dde
+    if (udev == NULL) {
709dde
+        g_debug("failed to query udev");
709dde
+        goto out;
709dde
+    }
709dde
+    udevice = udev_device_new_from_syspath(udev, syspath);
709dde
+    if (udevice == NULL) {
709dde
+        g_debug("failed to query udev for path: %s", syspath);
709dde
+        goto out;
709dde
+    } else {
709dde
+        const char *alias = udev_device_get_property_value(
709dde
+            udevice, "DM_NAME");
709dde
+        /*
709dde
+         * NULL means there was an error and empty string means there is no
709dde
+         * alias. In case of no alias we return NULL instead of empty string.
709dde
+         */
709dde
+        if (alias == NULL) {
709dde
+            g_debug("failed to query udev for device alias for: %s",
709dde
+                syspath);
709dde
+        } else if (*alias != 0) {
709dde
+            ret = g_strdup(alias);
709dde
+        }
709dde
+    }
709dde
+
709dde
+out:
709dde
+    udev_unref(udev);
709dde
+    udev_device_unref(udevice);
709dde
+    return ret;
709dde
+}
709dde
+
709dde
+static char *get_device_for_syspath(const char *syspath)
709dde
+{
709dde
+    struct udev *udev = NULL;
709dde
+    struct udev_device *udevice = NULL;
709dde
+    char *ret = NULL;
709dde
+
709dde
+    udev = udev_new();
709dde
+    if (udev == NULL) {
709dde
+        g_debug("failed to query udev");
709dde
+        goto out;
709dde
+    }
709dde
+    udevice = udev_device_new_from_syspath(udev, syspath);
709dde
+    if (udevice == NULL) {
709dde
+        g_debug("failed to query udev for path: %s", syspath);
709dde
+        goto out;
709dde
+    } else {
709dde
+        ret = g_strdup(udev_device_get_devnode(udevice));
709dde
+    }
709dde
+
709dde
+out:
709dde
+    udev_unref(udev);
709dde
+    udev_device_unref(udevice);
709dde
+    return ret;
709dde
+}
709dde
+
709dde
+static void get_disk_deps(const char *disk_dir, GuestDiskInfo *disk)
709dde
+{
709dde
+    g_autofree char *deps_dir = NULL;
709dde
+    const gchar *dep;
709dde
+    GDir *dp_deps = NULL;
709dde
+
709dde
+    /* List dependent disks */
709dde
+    deps_dir = g_strdup_printf("%s/slaves", disk_dir);
709dde
+    g_debug("  listing entries in: %s", deps_dir);
709dde
+    dp_deps = g_dir_open(deps_dir, 0, NULL);
709dde
+    if (dp_deps == NULL) {
709dde
+        g_debug("failed to list entries in %s", deps_dir);
709dde
+        return;
709dde
+    }
709dde
+    while ((dep = g_dir_read_name(dp_deps)) != NULL) {
709dde
+        g_autofree char *dep_dir = NULL;
709dde
+        strList *dep_item = NULL;
709dde
+        char *dev_name;
709dde
+
709dde
+        /* Add dependent disks */
709dde
+        dep_dir = g_strdup_printf("%s/%s", deps_dir, dep);
709dde
+        dev_name = get_device_for_syspath(dep_dir);
709dde
+        if (dev_name != NULL) {
709dde
+            g_debug("  adding dependent device: %s", dev_name);
709dde
+            dep_item = g_new0(strList, 1);
709dde
+            dep_item->value = dev_name;
709dde
+            dep_item->next = disk->dependents;
709dde
+            disk->dependents = dep_item;
709dde
+        }
709dde
+    }
709dde
+    g_dir_close(dp_deps);
709dde
+}
709dde
+
709dde
+/*
709dde
+ * Detect partitions subdirectory, name is "<disk_name><number>" or
709dde
+ * "<disk_name>p<number>"
709dde
+ *
709dde
+ * @disk_name -- last component of /sys path (e.g. sda)
709dde
+ * @disk_dir -- sys path of the disk (e.g. /sys/block/sda)
709dde
+ * @disk_dev -- device node of the disk (e.g. /dev/sda)
709dde
+ */
709dde
+static GuestDiskInfoList *get_disk_partitions(
709dde
+    GuestDiskInfoList *list,
709dde
+    const char *disk_name, const char *disk_dir,
709dde
+    const char *disk_dev)
709dde
+{
709dde
+    GuestDiskInfoList *item, *ret = list;
709dde
+    struct dirent *de_disk;
709dde
+    DIR *dp_disk = NULL;
709dde
+    size_t len = strlen(disk_name);
709dde
+
709dde
+    dp_disk = opendir(disk_dir);
709dde
+    while ((de_disk = readdir(dp_disk)) != NULL) {
709dde
+        g_autofree char *partition_dir = NULL;
709dde
+        char *dev_name;
709dde
+        GuestDiskInfo *partition;
709dde
+
709dde
+        if (!(de_disk->d_type & DT_DIR)) {
709dde
+            continue;
709dde
+        }
709dde
+
709dde
+        if (!(strncmp(disk_name, de_disk->d_name, len) == 0 &&
709dde
+            ((*(de_disk->d_name + len) == 'p' &&
709dde
+            isdigit(*(de_disk->d_name + len + 1))) ||
709dde
+                isdigit(*(de_disk->d_name + len))))) {
709dde
+            continue;
709dde
+        }
709dde
+
709dde
+        partition_dir = g_strdup_printf("%s/%s",
709dde
+            disk_dir, de_disk->d_name);
709dde
+        dev_name = get_device_for_syspath(partition_dir);
709dde
+        if (dev_name == NULL) {
709dde
+            g_debug("Failed to get device name for syspath: %s",
709dde
+                disk_dir);
709dde
+            continue;
709dde
+        }
709dde
+        partition = g_new0(GuestDiskInfo, 1);
709dde
+        partition->name = dev_name;
709dde
+        partition->partition = true;
709dde
+        /* Add parent disk as dependent for easier tracking of hierarchy */
709dde
+        partition->dependents = g_new0(strList, 1);
709dde
+        partition->dependents->value = g_strdup(disk_dev);
709dde
+
709dde
+        item = g_new0(GuestDiskInfoList, 1);
709dde
+        item->value = partition;
709dde
+        item->next = ret;
709dde
+        ret = item;
709dde
+
709dde
+    }
709dde
+    closedir(dp_disk);
709dde
+
709dde
+    return ret;
709dde
+}
709dde
+
709dde
+GuestDiskInfoList *qmp_guest_get_disks(Error **errp)
709dde
+{
709dde
+    GuestDiskInfoList *item, *ret = NULL;
709dde
+    GuestDiskInfo *disk;
709dde
+    DIR *dp = NULL;
709dde
+    struct dirent *de = NULL;
709dde
+
709dde
+    g_debug("listing /sys/block directory");
709dde
+    dp = opendir("/sys/block");
709dde
+    if (dp == NULL) {
709dde
+        error_setg_errno(errp, errno, "Can't open directory \"/sys/block\"");
709dde
+        return NULL;
709dde
+    }
709dde
+    while ((de = readdir(dp)) != NULL) {
709dde
+        g_autofree char *disk_dir = NULL, *line = NULL,
709dde
+            *size_path = NULL;
709dde
+        char *dev_name;
709dde
+        Error *local_err = NULL;
709dde
+        if (de->d_type != DT_LNK) {
709dde
+            g_debug("  skipping entry: %s", de->d_name);
709dde
+            continue;
709dde
+        }
709dde
+
709dde
+        /* Check size and skip zero-sized disks */
709dde
+        g_debug("  checking disk size");
709dde
+        size_path = g_strdup_printf("/sys/block/%s/size", de->d_name);
709dde
+        if (!g_file_get_contents(size_path, &line, NULL, NULL)) {
709dde
+            g_debug("  failed to read disk size");
709dde
+            continue;
709dde
+        }
709dde
+        if (g_strcmp0(line, "0\n") == 0) {
709dde
+            g_debug("  skipping zero-sized disk");
709dde
+            continue;
709dde
+        }
709dde
+
709dde
+        g_debug("  adding %s", de->d_name);
709dde
+        disk_dir = g_strdup_printf("/sys/block/%s", de->d_name);
709dde
+        dev_name = get_device_for_syspath(disk_dir);
709dde
+        if (dev_name == NULL) {
709dde
+            g_debug("Failed to get device name for syspath: %s",
709dde
+                disk_dir);
709dde
+            continue;
709dde
+        }
709dde
+        disk = g_new0(GuestDiskInfo, 1);
709dde
+        disk->name = dev_name;
709dde
+        disk->partition = false;
709dde
+        disk->alias = get_alias_for_syspath(disk_dir);
709dde
+        disk->has_alias = (disk->alias != NULL);
709dde
+        item = g_new0(GuestDiskInfoList, 1);
709dde
+        item->value = disk;
709dde
+        item->next = ret;
709dde
+        ret = item;
709dde
+
709dde
+        /* Get address for non-virtual devices */
709dde
+        bool is_virtual = is_disk_virtual(disk_dir, &local_err);
709dde
+        if (local_err != NULL) {
709dde
+            g_debug("  failed to check disk path, ignoring error: %s",
709dde
+                error_get_pretty(local_err));
709dde
+            error_free(local_err);
709dde
+            local_err = NULL;
709dde
+            /* Don't try to get the address */
709dde
+            is_virtual = true;
709dde
+        }
709dde
+        if (!is_virtual) {
709dde
+            disk->address = get_disk_address(disk_dir, &local_err);
709dde
+            if (local_err != NULL) {
709dde
+                g_debug("  failed to get device info, ignoring error: %s",
709dde
+                    error_get_pretty(local_err));
709dde
+                error_free(local_err);
709dde
+                local_err = NULL;
709dde
+            } else if (disk->address != NULL) {
709dde
+                disk->has_address = true;
709dde
+            }
709dde
+        }
709dde
+
709dde
+        get_disk_deps(disk_dir, disk);
709dde
+        ret = get_disk_partitions(ret, de->d_name, disk_dir, dev_name);
709dde
+    }
709dde
+    return ret;
709dde
+}
709dde
+
709dde
+#else
709dde
+
709dde
+GuestDiskInfoList *qmp_guest_get_disks(Error **errp)
709dde
+{
709dde
+    error_setg(errp, QERR_UNSUPPORTED);
709dde
+    return NULL;
709dde
+}
709dde
+
709dde
+#endif
709dde
+
709dde
 /* Return a list of the disk device(s)' info which @mount lies on */
709dde
 static GuestFilesystemInfo *build_guest_fsinfo(struct FsMount *mount,
709dde
                                                Error **errp)
a19a21
@@ -2770,6 +3049,13 @@ int64_t qmp_guest_fsfreeze_thaw(Error **errp)
709dde
 
709dde
     return 0;
709dde
 }
709dde
+
709dde
+GuestDiskInfoList *qmp_guest_get_disks(Error **errp)
709dde
+{
709dde
+    error_setg(errp, QERR_UNSUPPORTED);
709dde
+    return NULL;
709dde
+}
709dde
+
709dde
 #endif /* CONFIG_FSFREEZE */
709dde
 
709dde
 #if !defined(CONFIG_FSTRIM)
a19a21
@@ -2806,7 +3092,8 @@ GList *ga_command_blacklist_init(GList *blacklist)
709dde
         const char *list[] = {
709dde
             "guest-get-fsinfo", "guest-fsfreeze-status",
709dde
             "guest-fsfreeze-freeze", "guest-fsfreeze-freeze-list",
709dde
-            "guest-fsfreeze-thaw", "guest-get-fsinfo", NULL};
709dde
+            "guest-fsfreeze-thaw", "guest-get-fsinfo",
709dde
+            "guest-get-disks", NULL};
709dde
         char **p = (char **)list;
709dde
 
709dde
         while (*p) {
a19a21
@@ -3039,9 +3326,3 @@ GuestOSInfo *qmp_guest_get_osinfo(Error **errp)
709dde
 
709dde
     return info;
709dde
 }
709dde
-
709dde
-GuestDiskInfoList *qmp_guest_get_disks(Error **errp)
709dde
-{
709dde
-    error_setg(errp, QERR_UNSUPPORTED);
709dde
-    return NULL;
709dde
-}
709dde
-- 
709dde
2.27.0
709dde