diff options
Diffstat (limited to 'lib/libzfs/libzfs_pool.c')
-rw-r--r-- | lib/libzfs/libzfs_pool.c | 200 |
1 files changed, 197 insertions, 3 deletions
diff --git a/lib/libzfs/libzfs_pool.c b/lib/libzfs/libzfs_pool.c index 68cc5f3ee..ebca76834 100644 --- a/lib/libzfs/libzfs_pool.c +++ b/lib/libzfs/libzfs_pool.c @@ -41,6 +41,9 @@ #include <sys/vtoc.h> #include <sys/zfs_ioctl.h> #include <dlfcn.h> +#if HAVE_LIBDEVMAPPER +#include <libdevmapper.h> +#endif #include "zfs_namecheck.h" #include "zfs_prop.h" @@ -3401,10 +3404,12 @@ set_path(zpool_handle_t *zhp, nvlist_t *nv, const char *path) * caller must free the returned string */ char * -zfs_strip_partition(libzfs_handle_t *hdl, char *path) +zfs_strip_partition(char *path) { - char *tmp = zfs_strdup(hdl, path); + char *tmp = strdup(path); char *part = NULL, *d = NULL; + if (!tmp) + return (NULL); if ((part = strstr(tmp, "-part")) && part != tmp) { d = part + 5; @@ -3422,6 +3427,7 @@ zfs_strip_partition(libzfs_handle_t *hdl, char *path) if (*d == '\0') *part = '\0'; } + return (tmp); } @@ -3544,7 +3550,7 @@ zpool_vdev_name(libzfs_handle_t *hdl, zpool_handle_t *zhp, nvlist_t *nv, */ if (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_WHOLE_DISK, &value) == 0 && value && !(name_flags & VDEV_NAME_PATH)) { - return (zfs_strip_partition(hdl, path)); + return (zfs_strip_partition(path)); } } else { verify(nvlist_lookup_string(nv, ZPOOL_CONFIG_TYPE, &path) == 0); @@ -4310,3 +4316,191 @@ zpool_label_disk(libzfs_handle_t *hdl, zpool_handle_t *zhp, char *name) return (0); } + +#if HAVE_LIBDEVMAPPER +static void libdevmapper_dummy_log(int level, const char *file, int line, + int dm_errno_or_class, const char *f, ...) {} + +/* Disable libdevmapper error logging */ +static void disable_libdevmapper_errors(void) { + dm_log_with_errno_init(libdevmapper_dummy_log); +} +/* Enable libdevmapper error logging */ +static void enable_libdevmapper_errors(void) { + dm_log_with_errno_init(NULL); +} +#endif + +/* + * Allocate and return the underlying device name for a device mapper device. + * If a device mapper device maps to multiple devices, return the first device. + * + * For example, dm_name = "/dev/dm-0" could return "/dev/sda" + * + * dm_name should include the "/dev[/mapper]" prefix. + * + * Returns device name, or NULL on error or no match. If dm_name is not a DM + * device then return NULL. + * + * NOTE: The returned name string must be *freed*. + */ +static char * dm_get_underlying_path(char *dm_name) +{ + char *name = NULL; +#if HAVE_LIBDEVMAPPER + char *tmp; + struct dm_task *dmt = NULL; + struct dm_tree *dt = NULL; + struct dm_tree_node *root, *child; + void *handle = NULL; + struct dm_info info; + const struct dm_info *child_info; + + /* + * Disable libdevmapper errors. It's entirely possible user is not + * running devmapper, or that dm_name is not a devmapper device. + * That's totally ok, we will just harmlessly and silently return NULL. + */ + disable_libdevmapper_errors(); + + /* + * libdevmapper tutorial + * + * libdevmapper is basically a fancy wrapper for its ioctls. You + * create a "task", fill in the needed info to the task (fill in the + * ioctl fields), then run the task (call the ioctl). + * + * First we need the major/minor number for our DM device. + */ + if (!(dmt = dm_task_create(DM_DEVICE_INFO))) + goto end; + + /* Lookup the name in libdevmapper */ + if (!dm_task_set_name(dmt, dm_name)) { + enable_libdevmapper_errors(); + goto end; + } + + if (!dm_task_run(dmt)) + goto end; + + /* Get DM device's major/minor */ + if (!dm_task_get_info(dmt, &info)) + goto end; + + /* We have major/minor number. Lookup the dm device's children */ + if (!(dt = dm_tree_create())) + goto end; + + /* We add the device into the tree and its children get populated */ + if (!dm_tree_add_dev(dt, info.major, info.minor)) + goto end; + + if (!(root = dm_tree_find_node(dt, 0, 0))) + goto end; + + if (!(child = dm_tree_next_child(&handle, root, 1))) + goto end; + + /* Get child's major/minor numbers */ + if (!(child_info = dm_tree_node_get_info(child))) + goto end; + + if ((asprintf(&tmp, "/dev/block/%d:%d", child_info->major, + child_info->minor) == -1) || !tmp) + goto end; + + /* Further translate /dev/block/ name into the normal name */ + name = realpath(tmp, NULL); + free(tmp); + +end: + if (dmt) + dm_task_destroy(dmt); + if (dt) + dm_tree_free(dt); + enable_libdevmapper_errors(); +#endif /* HAVE_LIBDEVMAPPER */ + + return (name); +} + +/* + * Return 1 if device is a device mapper or multipath device. + * Return 0 if not. + */ +int +dev_is_dm(char *devname) +{ + + char *tmp; + tmp = dm_get_underlying_path(devname); + if (!tmp) + return (0); + + free(tmp); + return (1); +} + +/* + * Lookup the underlying device for a device name + * + * Often you'll have a symlink to a device, a partition device, + * or a multipath device, and want to look up the underlying device. + * This function returns the underlying device name. If the device + * name is already the underlying device, then just return the same + * name. If the device is a DM device with multiple underlying devices + * then return the first one. + * + * For example: + * + * 1. /dev/disk/by-id/ata-QEMU_HARDDISK_QM00001 -> ../../sda + * dev_name: /dev/disk/by-id/ata-QEMU_HARDDISK_QM00001 + * returns: /dev/sda + * + * 2. /dev/mapper/mpatha (made up of /dev/sda and /dev/sdb) + * dev_name: /dev/mapper/mpatha + * returns: /dev/sda (first device) + * + * 3. /dev/sda (already the underlying device) + * dev_name: /dev/sda + * returns: /dev/sda + * + * 4. /dev/dm-3 (mapped to /dev/sda) + * dev_name: /dev/dm-3 + * returns: /dev/sda + * + * 5. /dev/disk/by-id/scsi-0QEMU_drive-scsi0-0-0-0-part9 -> ../../sdb9 + * dev_name: /dev/disk/by-id/scsi-0QEMU_drive-scsi0-0-0-0-part9 + * returns: /dev/sdb + * + * 6. /dev/disk/by-uuid/5df030cf-3cd9-46e4-8e99-3ccb462a4e9a -> ../dev/sda2 + * dev_name: /dev/disk/by-uuid/5df030cf-3cd9-46e4-8e99-3ccb462a4e9a + * returns: /dev/sda + * + * Returns underlying device name, or NULL on error or no match. + * + * NOTE: The returned name string must be *freed*. + */ +char * +get_underlying_path(libzfs_handle_t *hdl, char *dev_name) +{ + char *name = NULL; + char *tmp; + + if (!dev_name) + return (NULL); + + tmp = dm_get_underlying_path(dev_name); + + /* dev_name not a DM device, so just un-symlinkize it */ + if (!tmp) + tmp = realpath(dev_name, NULL); + + if (tmp) { + name = zfs_strip_partition(tmp); + free(tmp); + } + + return (name); +} |