yeahuh / rpms / qemu-kvm

Forked from rpms/qemu-kvm 2 years ago
Clone

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

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