Blame src/utils/dev_utils.c

Packit 2ba279
/*
Packit 2ba279
 * Copyright (C) 2017  Red Hat, Inc.
Packit 2ba279
 *
Packit 2ba279
 * This library is free software; you can redistribute it and/or
Packit 2ba279
 * modify it under the terms of the GNU Lesser General Public
Packit 2ba279
 * License as published by the Free Software Foundation; either
Packit 2ba279
 * version 2.1 of the License, or (at your option) any later version.
Packit 2ba279
 *
Packit 2ba279
 * This library is distributed in the hope that it will be useful,
Packit 2ba279
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 2ba279
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 2ba279
 * Lesser General Public License for more details.
Packit 2ba279
 *
Packit 2ba279
 * You should have received a copy of the GNU Lesser General Public
Packit 2ba279
 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
Packit 2ba279
 *
Packit 2ba279
 * Author: Vratislav Podzimek <vpodzime@redhat.com>
Packit 2ba279
 */
Packit 2ba279
Packit 2ba279
#include <glib.h>
Packit 2ba279
#include <libudev.h>
Packit 2ba279
Packit 2ba279
#include "dev_utils.h"
Packit 2ba279
Packit 2ba279
/**
Packit 2ba279
 * bd_utils_dev_utils_error_quark: (skip)
Packit 2ba279
 */
Packit 2ba279
GQuark bd_utils_dev_utils_error_quark (void)
Packit 2ba279
{
Packit 2ba279
    return g_quark_from_static_string ("g-bd-utils-dev_utils-error-quark");
Packit 2ba279
}
Packit 2ba279
Packit 2ba279
/**
Packit 2ba279
 * bd_utils_resolve_device:
Packit 2ba279
 * @dev_spec: specification of the device (e.g. "/dev/sda", any symlink, or the name of a file
Packit 2ba279
 *            under "/dev")
Packit 2ba279
 * @error: (out): place to store error (if any)
Packit 2ba279
 *
Packit 2ba279
 * Returns: (transfer full): the full real path of the device (e.g. "/dev/md126"
Packit 2ba279
 *                           for "/dev/md/my_raid") or %NULL in case of error
Packit 2ba279
 */
Packit 2ba279
gchar* bd_utils_resolve_device (const gchar *dev_spec, GError **error) {
Packit 2ba279
    gchar *path = NULL;
Packit 2ba279
    gchar *symlink = NULL;
Packit 2ba279
Packit 2ba279
    /* TODO: check that the resulting path is a block device? */
Packit 2ba279
Packit 2ba279
    if (!g_str_has_prefix (dev_spec, "/dev/"))
Packit 2ba279
        path = g_strdup_printf ("/dev/%s", dev_spec);
Packit 2ba279
    else
Packit 2ba279
        path = g_strdup (dev_spec);
Packit 2ba279
Packit 2ba279
    symlink = g_file_read_link (path, error);
Packit 2ba279
    if (!symlink) {
Packit 2ba279
        if (g_error_matches (*error, G_FILE_ERROR, G_FILE_ERROR_INVAL)) {
Packit 2ba279
            /* invalid argument -> not a symlink -> nothing to resolve */
Packit 2ba279
            g_clear_error (error);
Packit 2ba279
            return path;
Packit 2ba279
        } else {
Packit 2ba279
            /* some other error, just report it */
Packit 2ba279
            g_free (path);
Packit 2ba279
            return NULL;
Packit 2ba279
        }
Packit 2ba279
    }
Packit 2ba279
    g_free (path);
Packit 2ba279
Packit 2ba279
    if (g_str_has_prefix (symlink, "../"))
Packit 2ba279
        path = g_strdup_printf ("/dev/%s", symlink + 3);
Packit 2ba279
    else
Packit 2ba279
        path = g_strdup_printf ("/dev/%s", symlink);
Packit 2ba279
    g_free (symlink);
Packit 2ba279
Packit 2ba279
    return path;
Packit 2ba279
}
Packit 2ba279
Packit 2ba279
/**
Packit 2ba279
 * bd_utils_get_device_symlinks:
Packit 2ba279
 * @dev_spec: specification of the device (e.g. "/dev/sda", any symlink, or the name of a file
Packit 2ba279
 *            under "/dev")
Packit 2ba279
 * @error: (out): place to store error (if any)
Packit 2ba279
 *
Packit 2ba279
 * Returns: (transfer full) (array zero-terminated=1): a list of all symlinks (known to udev) for the
Packit 2ba279
 *                                                     device specified with @dev_spec or %NULL in
Packit 2ba279
 *                                                     case of error
Packit 2ba279
 */
Packit 2ba279
gchar** bd_utils_get_device_symlinks (const gchar *dev_spec, GError **error) {
Packit 2ba279
    gchar *dev_path;
Packit 2ba279
    struct udev *context;
Packit 2ba279
    struct udev_device *device;
Packit 2ba279
    struct udev_list_entry *entry = NULL;
Packit 2ba279
    struct udev_list_entry *ent_it = NULL;
Packit 2ba279
    guint64 n_links = 0;
Packit 2ba279
    guint64 i = 0;
Packit 2ba279
    gchar **ret = NULL;
Packit 2ba279
Packit 2ba279
    dev_path = bd_utils_resolve_device (dev_spec, error);
Packit 2ba279
    if (!dev_path)
Packit 2ba279
        return NULL;
Packit 2ba279
Packit 2ba279
    context = udev_new ();
Packit 2ba279
    /* dev_path is the full path like "/dev/sda", we only need the device name ("sda") */
Packit 2ba279
    device = udev_device_new_from_subsystem_sysname (context, "block", dev_path + 5);
Packit 2ba279
Packit 2ba279
    if (!device) {
Packit 2ba279
        g_set_error (error, BD_UTILS_DEV_UTILS_ERROR, BD_UTILS_DEV_UTILS_ERROR_FAILED,
Packit 2ba279
                     "Failed to get information about the device '%s' from udev database",
Packit 2ba279
                     dev_path);
Packit 2ba279
        g_free (dev_path);
Packit 2ba279
        udev_unref (context);
Packit 2ba279
        return NULL;
Packit 2ba279
    }
Packit 2ba279
Packit 2ba279
    entry = udev_device_get_devlinks_list_entry (device);
Packit 2ba279
    if (!entry) {
Packit 2ba279
        g_set_error (error, BD_UTILS_DEV_UTILS_ERROR, BD_UTILS_DEV_UTILS_ERROR_FAILED,
Packit 2ba279
                     "Failed to get symlinks for the device '%s'", dev_path);
Packit 2ba279
        g_free (dev_path);
Packit 2ba279
        udev_device_unref (device);
Packit 2ba279
        udev_unref (context);
Packit 2ba279
        return NULL;
Packit 2ba279
    }
Packit 2ba279
    g_free (dev_path);
Packit 2ba279
Packit 2ba279
    ent_it = entry;
Packit 2ba279
    while (ent_it) {
Packit 2ba279
        n_links++;
Packit 2ba279
        ent_it = udev_list_entry_get_next (ent_it);
Packit 2ba279
    }
Packit 2ba279
Packit 2ba279
    ret = g_new0 (gchar*, n_links + 1);
Packit 2ba279
    ent_it = entry;
Packit 2ba279
    while (ent_it) {
Packit 2ba279
        ret[i++] = g_strdup (udev_list_entry_get_name (ent_it));
Packit 2ba279
        ent_it = udev_list_entry_get_next (ent_it);
Packit 2ba279
    }
Packit 2ba279
    ret[i] = NULL;
Packit 2ba279
Packit 2ba279
    udev_device_unref (device);
Packit 2ba279
    udev_unref (context);
Packit 2ba279
Packit 2ba279
    return ret;
Packit 2ba279
}