From c66b76761528f6294849cacaa8a99c49967d0070 Mon Sep 17 00:00:00 2001 Message-Id: From: Michal Privoznik Date: Mon, 9 Apr 2018 15:46:49 +0200 Subject: [PATCH] util: Introduce virDevMapperGetTargets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit https://bugzilla.redhat.com/show_bug.cgi?id=1564996 This helper fetches dependencies for given device mapper target. At the same time, we need to provide a dummy log function because by default libdevmapper prints out error messages to stderr which we need to suppress. Signed-off-by: Michal Privoznik (cherry picked from commit fd9d1e686db64fa9481b9eab4dabafa46713e2cf) Signed-off-by: Michal Privoznik Conflicts: src/util/Makefile.inc.am: src/Makefile split is not backported yet (9cd0bdd1a178a76dbce4) Signed-off-by: Michal Privoznik Reviewed-by: Andrea Bolognani Reviewed-by: Ján Tomko --- src/Makefile.am | 1 + src/libvirt_private.syms | 4 + src/util/virdevmapper.c | 199 +++++++++++++++++++++++++++++++++++++++ src/util/virdevmapper.h | 31 ++++++ 4 files changed, 235 insertions(+) create mode 100644 src/util/virdevmapper.c create mode 100644 src/util/virdevmapper.h diff --git a/src/Makefile.am b/src/Makefile.am index 6b24a47afe..2130859045 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -111,6 +111,7 @@ UTIL_SOURCES = \ util/virconf.c util/virconf.h \ util/vircrypto.c util/vircrypto.h \ util/virdbus.c util/virdbus.h util/virdbuspriv.h \ + util/virdevmapper.c util/virdevmapper.h \ util/virdnsmasq.c util/virdnsmasq.h \ util/virebtables.c util/virebtables.h \ util/virendian.h \ diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 15cc83931a..5bacafb462 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1625,6 +1625,10 @@ virDBusMessageUnref; virDBusSetSharedBus; +# util/virdevmapper.h +virDevMapperGetTargets; + + # util/virdnsmasq.h dnsmasqAddDhcpHost; dnsmasqAddHost; diff --git a/src/util/virdevmapper.c b/src/util/virdevmapper.c new file mode 100644 index 0000000000..d2c25af003 --- /dev/null +++ b/src/util/virdevmapper.c @@ -0,0 +1,199 @@ +/* + * virdevmapper.c: Functions for handling device mapper + * + * Copyright (C) 2018 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * . + * + * Authors: + * Michal Privoznik + */ + +#include + +#ifdef MAJOR_IN_MKDEV +# include +#elif MAJOR_IN_SYSMACROS +# include +#endif + +#ifdef WITH_DEVMAPPER +# include +#endif + +#include "virdevmapper.h" +#include "internal.h" +#include "virthread.h" +#include "viralloc.h" +#include "virstring.h" + +#ifdef WITH_DEVMAPPER +static void +virDevMapperDummyLogger(int level ATTRIBUTE_UNUSED, + const char *file ATTRIBUTE_UNUSED, + int line ATTRIBUTE_UNUSED, + int dm_errno ATTRIBUTE_UNUSED, + const char *fmt ATTRIBUTE_UNUSED, + ...) +{ + return; +} + +static int +virDevMapperOnceInit(void) +{ + /* Ideally, we would not need this. But libdevmapper prints + * error messages to stderr by default. Sad but true. */ + dm_log_with_errno_init(virDevMapperDummyLogger); + return 0; +} + + +VIR_ONCE_GLOBAL_INIT(virDevMapper) + + +static int +virDevMapperGetTargetsImpl(const char *path, + char ***devPaths_ret, + unsigned int ttl) +{ + struct dm_task *dmt = NULL; + struct dm_deps *deps; + struct dm_info info; + char **devPaths = NULL; + char **recursiveDevPaths = NULL; + size_t i; + int ret = -1; + + *devPaths_ret = NULL; + + if (virDevMapperInitialize() < 0) + return ret; + + if (ttl == 0) { + errno = ELOOP; + return ret; + } + + if (!(dmt = dm_task_create(DM_DEVICE_DEPS))) + return ret; + + if (!dm_task_set_name(dmt, path)) { + if (errno == ENOENT) { + /* It's okay, @path is not managed by devmapper => + * not a devmapper device. */ + ret = 0; + } + goto cleanup; + } + + dm_task_no_open_count(dmt); + + if (!dm_task_run(dmt)) + goto cleanup; + + if (!dm_task_get_info(dmt, &info)) + goto cleanup; + + if (!info.exists) { + ret = 0; + goto cleanup; + } + + if (!(deps = dm_task_get_deps(dmt))) + goto cleanup; + + if (VIR_ALLOC_N_QUIET(devPaths, deps->count + 1) < 0) + goto cleanup; + + for (i = 0; i < deps->count; i++) { + if (virAsprintfQuiet(&devPaths[i], "/dev/block/%u:%u", + major(deps->device[i]), + minor(deps->device[i])) < 0) + goto cleanup; + } + + recursiveDevPaths = NULL; + for (i = 0; i < deps->count; i++) { + char **tmpPaths; + + if (virDevMapperGetTargetsImpl(devPaths[i], &tmpPaths, ttl - 1) < 0) + goto cleanup; + + if (tmpPaths && + virStringListMerge(&recursiveDevPaths, &tmpPaths) < 0) { + virStringListFree(tmpPaths); + goto cleanup; + } + } + + if (virStringListMerge(&devPaths, &recursiveDevPaths) < 0) + goto cleanup; + + VIR_STEAL_PTR(*devPaths_ret, devPaths); + ret = 0; + cleanup: + virStringListFree(recursiveDevPaths); + virStringListFree(devPaths); + dm_task_destroy(dmt); + return ret; +} + + +/** + * virDevMapperGetTargets: + * @path: devmapper target + * @devPaths: returned string list of devices + * + * For given @path figure out its targets, and store them in + * @devPaths array. Note, @devPaths is a string list so it's NULL + * terminated. + * + * If @path is not a devmapper device, @devPaths is set to NULL and + * success is returned. + * + * If @path consists of yet another devmapper targets these are + * consulted recursively. + * + * If we don't have permissions to talk to kernel, -1 is returned + * and errno is set to EBADF. + * + * Returns 0 on success, + * -1 otherwise (with errno set, no libvirt error is + * reported) + */ +int +virDevMapperGetTargets(const char *path, + char ***devPaths) +{ + const unsigned int ttl = 32; + + /* Arbitrary limit on recursion level. A devmapper target can + * consist of devices or yet another targets. If that's the + * case, we have to stop recursion somewhere. */ + + return virDevMapperGetTargetsImpl(path, devPaths, ttl); +} + +#else /* ! WITH_DEVMAPPER */ + +int +virDevMapperGetTargets(const char *path ATTRIBUTE_UNUSED, + char ***devPaths ATTRIBUTE_UNUSED) +{ + errno = ENOSYS; + return -1; +} +#endif /* ! WITH_DEVMAPPER */ diff --git a/src/util/virdevmapper.h b/src/util/virdevmapper.h new file mode 100644 index 0000000000..34d6655e77 --- /dev/null +++ b/src/util/virdevmapper.h @@ -0,0 +1,31 @@ +/* + * virdevmapper.h: Functions for handling device mapper + * + * Copyright (C) 2018 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * . + * + * Authors: + * Michal Privoznik + */ + +#ifndef __VIR_DEVMAPPER_H__ +# define __VIR_DEVMAPPER_H__ + +int +virDevMapperGetTargets(const char *path, + char ***devPaths); + +#endif /* __VIR_DEVMAPPER_H__ */ -- 2.17.0