summaryrefslogtreecommitdiff
path: root/libdm
diff options
context:
space:
mode:
Diffstat (limited to 'libdm')
-rw-r--r--libdm/Makefile.in4
-rw-r--r--libdm/datastruct/hash.c12
-rw-r--r--libdm/ioctl/libdm-compat.h123
-rw-r--r--libdm/ioctl/libdm-iface.c1229
-rw-r--r--libdm/ioctl/libdm-targets.h9
-rw-r--r--libdm/libdevmapper.h463
-rw-r--r--libdm/libdm-common.c1235
-rw-r--r--libdm/libdm-common.h32
-rw-r--r--libdm/libdm-config.c1186
-rw-r--r--libdm/libdm-deptree.c1794
-rw-r--r--libdm/libdm-file.c28
-rw-r--r--libdm/libdm-report.c77
-rw-r--r--libdm/libdm-string.c299
-rw-r--r--libdm/misc/dm-ioctl.h14
-rw-r--r--libdm/misc/dm-log-userspace.h31
-rw-r--r--libdm/mm/dbg_malloc.c24
-rw-r--r--libdm/mm/dbg_malloc.h46
-rw-r--r--libdm/mm/pool-debug.c46
-rw-r--r--libdm/mm/pool-fast.c88
-rw-r--r--libdm/mm/pool.c114
-rw-r--r--libdm/regex/matcher.c183
-rw-r--r--libdm/regex/ttree.c1
22 files changed, 5412 insertions, 1626 deletions
diff --git a/libdm/Makefile.in b/libdm/Makefile.in
index 70be309..bddb0a0 100644
--- a/libdm/Makefile.in
+++ b/libdm/Makefile.in
@@ -25,6 +25,7 @@ SOURCES =\
libdm-deptree.c \
libdm-string.c \
libdm-report.c \
+ libdm-config.c \
mm/dbg_malloc.c \
mm/pool.c \
regex/matcher.c \
@@ -33,6 +34,9 @@ SOURCES =\
$(interface)/libdm-iface.c
INCLUDES = -I$(srcdir)/$(interface) -I$(srcdir)
+ifeq ("@VALGRIND_POOL@", "yes")
+INCLUDES += @VALGRIND_CFLAGS@
+endif
ifeq ("@STATIC_LINK@", "yes")
LIB_STATIC = $(interface)/libdevmapper.a
diff --git a/libdm/datastruct/hash.c b/libdm/datastruct/hash.c
index d4543df..30b4a97 100644
--- a/libdm/datastruct/hash.c
+++ b/libdm/datastruct/hash.c
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
- * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2004-2011 Red Hat, Inc. All rights reserved.
*
* This file is part of the device-mapper userspace tools.
*
@@ -133,7 +133,7 @@ void dm_hash_destroy(struct dm_hash_table *t)
dm_free(t);
}
-static struct dm_hash_node **_find(struct dm_hash_table *t, const char *key,
+static struct dm_hash_node **_find(struct dm_hash_table *t, const void *key,
uint32_t len)
{
unsigned h = _hash(key, len) & (t->num_slots - 1);
@@ -150,15 +150,15 @@ static struct dm_hash_node **_find(struct dm_hash_table *t, const char *key,
return c;
}
-void *dm_hash_lookup_binary(struct dm_hash_table *t, const char *key,
- uint32_t len)
+void *dm_hash_lookup_binary(struct dm_hash_table *t, const void *key,
+ uint32_t len)
{
struct dm_hash_node **c = _find(t, key, len);
return *c ? (*c)->data : 0;
}
-int dm_hash_insert_binary(struct dm_hash_table *t, const char *key,
+int dm_hash_insert_binary(struct dm_hash_table *t, const void *key,
uint32_t len, void *data)
{
struct dm_hash_node **c = _find(t, key, len);
@@ -180,7 +180,7 @@ int dm_hash_insert_binary(struct dm_hash_table *t, const char *key,
return 1;
}
-void dm_hash_remove_binary(struct dm_hash_table *t, const char *key,
+void dm_hash_remove_binary(struct dm_hash_table *t, const void *key,
uint32_t len)
{
struct dm_hash_node **c = _find(t, key, len);
diff --git a/libdm/ioctl/libdm-compat.h b/libdm/ioctl/libdm-compat.h
deleted file mode 100644
index 5ba63ce..0000000
--- a/libdm/ioctl/libdm-compat.h
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
- * Copyright (C) 2004-2006 Red Hat, Inc. All rights reserved.
- *
- * This file is part of the device-mapper userspace tools.
- *
- * This copyrighted material is made available to anyone wishing to use,
- * modify, copy, or redistribute it subject to the terms and conditions
- * of the GNU Lesser General Public License v.2.1.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-#ifndef _LINUX_LIBDM_COMPAT_H
-#define _LINUX_LIBDM_COMPAT_H
-
-#include "kdev_t.h"
-#include "dm-ioctl.h"
-#include <inttypes.h>
-#include <sys/ioctl.h>
-
-struct dm_task;
-struct dm_info;
-
-/*
- * Old versions of structures for backwards compatibility.
- */
-
-struct dm_ioctl_v1 {
- uint32_t version[3]; /* in/out */
- uint32_t data_size; /* total size of data passed in
- * including this struct */
-
- uint32_t data_start; /* offset to start of data
- * relative to start of this struct */
-
- int32_t target_count; /* in/out */
- int32_t open_count; /* out */
- uint32_t flags; /* in/out */
-
- __kernel_dev_t dev; /* in/out */
-
- char name[DM_NAME_LEN]; /* device name */
- char uuid[DM_UUID_LEN]; /* unique identifier for
- * the block device */
-};
-
-struct dm_target_spec_v1 {
- int32_t status; /* used when reading from kernel only */
- uint64_t sector_start;
- uint32_t length;
- uint32_t next;
-
- char target_type[DM_MAX_TYPE_NAME];
-
-};
-
-struct dm_target_deps_v1 {
- uint32_t count;
-
- __kernel_dev_t dev[0]; /* out */
-};
-
-enum {
- /* Top level cmds */
- DM_VERSION_CMD_V1 = 0,
- DM_REMOVE_ALL_CMD_V1,
-
- /* device level cmds */
- DM_DEV_CREATE_CMD_V1,
- DM_DEV_REMOVE_CMD_V1,
- DM_DEV_RELOAD_CMD_V1,
- DM_DEV_RENAME_CMD_V1,
- DM_DEV_SUSPEND_CMD_V1,
- DM_DEV_DEPS_CMD_V1,
- DM_DEV_STATUS_CMD_V1,
-
- /* target level cmds */
- DM_TARGET_STATUS_CMD_V1,
- DM_TARGET_WAIT_CMD_V1,
-};
-
-#define DM_VERSION_V1 _IOWR(DM_IOCTL, DM_VERSION_CMD_V1, struct dm_ioctl)
-#define DM_REMOVE_ALL_V1 _IOWR(DM_IOCTL, DM_REMOVE_ALL_CMD_V1, struct dm_ioctl)
-
-#define DM_DEV_CREATE_V1 _IOWR(DM_IOCTL, DM_DEV_CREATE_CMD_V1, struct dm_ioctl)
-#define DM_DEV_REMOVE_V1 _IOWR(DM_IOCTL, DM_DEV_REMOVE_CMD_V1, struct dm_ioctl)
-#define DM_DEV_RELOAD_V1 _IOWR(DM_IOCTL, DM_DEV_RELOAD_CMD_V1, struct dm_ioctl)
-#define DM_DEV_SUSPEND_V1 _IOWR(DM_IOCTL, DM_DEV_SUSPEND_CMD_V1, struct dm_ioctl)
-#define DM_DEV_RENAME_V1 _IOWR(DM_IOCTL, DM_DEV_RENAME_CMD_V1, struct dm_ioctl)
-#define DM_DEV_DEPS_V1 _IOWR(DM_IOCTL, DM_DEV_DEPS_CMD_V1, struct dm_ioctl)
-#define DM_DEV_STATUS_V1 _IOWR(DM_IOCTL, DM_DEV_STATUS_CMD_V1, struct dm_ioctl)
-
-#define DM_TARGET_STATUS_V1 _IOWR(DM_IOCTL, DM_TARGET_STATUS_CMD_V1, struct dm_ioctl)
-#define DM_TARGET_WAIT_V1 _IOWR(DM_IOCTL, DM_TARGET_WAIT_CMD_V1, struct dm_ioctl)
-
-/* *INDENT-OFF* */
-static struct cmd_data _cmd_data_v1[] = {
- { "create", DM_DEV_CREATE_V1, {1, 0, 0} },
- { "reload", DM_DEV_RELOAD_V1, {1, 0, 0} },
- { "remove", DM_DEV_REMOVE_V1, {1, 0, 0} },
- { "remove_all", DM_REMOVE_ALL_V1, {1, 0, 0} },
- { "suspend", DM_DEV_SUSPEND_V1, {1, 0, 0} },
- { "resume", DM_DEV_SUSPEND_V1, {1, 0, 0} },
- { "info", DM_DEV_STATUS_V1, {1, 0, 0} },
- { "deps", DM_DEV_DEPS_V1, {1, 0, 0} },
- { "rename", DM_DEV_RENAME_V1, {1, 0, 0} },
- { "version", DM_VERSION_V1, {1, 0, 0} },
- { "status", DM_TARGET_STATUS_V1, {1, 0, 0} },
- { "table", DM_TARGET_STATUS_V1, {1, 0, 0} },
- { "waitevent", DM_TARGET_WAIT_V1, {1, 0, 0} },
- { "names", 0, {4, 0, 0} },
- { "clear", 0, {4, 0, 0} },
- { "mknodes", 0, {4, 0, 0} },
- { "versions", 0, {4, 1, 0} },
- { "message", 0, {4, 2, 0} },
- { "setgeometry",0, {4, 6, 0} },
-};
-/* *INDENT-ON* */
-
-#endif
diff --git a/libdm/ioctl/libdm-iface.c b/libdm/ioctl/libdm-iface.c
index 4a8dfc6..9e78e1c 100644
--- a/libdm/ioctl/libdm-iface.c
+++ b/libdm/ioctl/libdm-iface.c
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
- * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2004-2012 Red Hat, Inc. All rights reserved.
*
* This file is part of the device-mapper userspace tools.
*
@@ -17,10 +17,6 @@
#include "libdm-targets.h"
#include "libdm-common.h"
-#ifdef DM_COMPAT
-# include "libdm-compat.h"
-#endif
-
#include <fcntl.h>
#include <dirent.h>
#include <sys/ioctl.h>
@@ -44,8 +40,7 @@
* in the _cmd_data arrays.
*/
-#if !((DM_VERSION_MAJOR == 1 && DM_VERSION_MINOR >= 0) || \
- (DM_VERSION_MAJOR == 4 && DM_VERSION_MINOR >= 0))
+#if !((DM_VERSION_MAJOR == 4 && DM_VERSION_MINOR >= 6))
#error The version of dm-ioctl.h included is incompatible.
#endif
@@ -73,9 +68,9 @@ static unsigned _dm_version_minor = 0;
static unsigned _dm_version_patchlevel = 0;
static int _log_suppress = 0;
-static int _kernel_major;
-static int _kernel_minor;
-static int _kernel_release;
+static int _kernel_major = 0;
+static int _kernel_minor = 0;
+static int _kernel_release = 0;
/*
* If the kernel dm driver only supports one major number
@@ -92,16 +87,7 @@ static int _version_checked = 0;
static int _version_ok = 1;
static unsigned _ioctl_buffer_double_factor = 0;
-/*
- * Support both old and new major numbers to ease the transition.
- * Clumsy, but only temporary.
- */
-#if DM_VERSION_MAJOR == 4 && defined(DM_COMPAT)
-const int _dm_compat = 1;
-#else
const int _dm_compat = 0;
-#endif
-
/* *INDENT-OFF* */
static struct cmd_data _cmd_data_v4[] = {
@@ -133,7 +119,6 @@ static struct cmd_data _cmd_data_v4[] = {
};
/* *INDENT-ON* */
-#define ALIGNMENT_V1 sizeof(int)
#define ALIGNMENT 8
/* FIXME Rejig library to record & use errno instead */
@@ -141,17 +126,18 @@ static struct cmd_data _cmd_data_v4[] = {
# define DM_EXISTS_FLAG 0x00000004
#endif
-static void *_align(void *ptr, unsigned int a)
+static char *_align(char *ptr, unsigned int a)
{
register unsigned long agn = --a;
- return (void *) (((unsigned long) ptr + agn) & ~agn);
+ return (char *) (((unsigned long) ptr + agn) & ~agn);
}
static int _uname(void)
{
static int _uts_set = 0;
struct utsname _uts;
+ int parts;
if (_uts_set)
return 1;
@@ -160,10 +146,14 @@ static int _uname(void)
log_error("uname failed: %s", strerror(errno));
return 0;
}
- if (sscanf(_uts.release, "%d.%d.%d",
+
+ parts = sscanf(_uts.release, "%d.%d.%d",
&_kernel_major,
&_kernel_minor,
- &_kernel_release) != 3) {
+ &_kernel_release);
+
+ /* Kernels with a major number of 2 always had 3 parts. */
+ if (parts < 1 || (_kernel_major < 3 && parts < 3)) {
log_error("Could not determine kernel version used.");
return 0;
}
@@ -182,7 +172,8 @@ static int _get_proc_number(const char *file, const char *name,
{
FILE *fl;
char nm[256];
- int c;
+ char *line = NULL;
+ size_t len;
uint32_t num;
if (!(fl = fopen(file, "r"))) {
@@ -190,23 +181,23 @@ static int _get_proc_number(const char *file, const char *name,
return 0;
}
- while (!feof(fl)) {
- if (fscanf(fl, "%d %255s\n", &num, &nm[0]) == 2) {
+ while (getline(&line, &len, fl) != -1) {
+ if (sscanf(line, "%d %255s\n", &num, &nm[0]) == 2) {
if (!strcmp(name, nm)) {
if (number) {
*number = num;
if (fclose(fl))
log_sys_error("fclose", file);
+ free(line);
return 1;
}
dm_bit_set(_dm_bitset, num);
}
- } else do {
- c = fgetc(fl);
- } while (c != EOF && c != '\n');
+ }
}
if (fclose(fl))
log_sys_error("fclose", file);
+ free(line);
if (number) {
log_error("%s: No entry for %s found", file, name);
@@ -228,7 +219,7 @@ static int _control_device_number(uint32_t *major, uint32_t *minor)
}
/*
- * Returns 1 if exists; 0 if it doesn't; -1 if it's wrong
+ * Returns 1 if it exists on returning; 0 if it doesn't; -1 if it's wrong.
*/
static int _control_exists(const char *control, uint32_t major, uint32_t minor)
{
@@ -248,7 +239,7 @@ static int _control_exists(const char *control, uint32_t major, uint32_t minor)
return -1;
}
- if (major && buf.st_rdev != MKDEV(major, minor)) {
+ if (major && buf.st_rdev != MKDEV((dev_t)major, minor)) {
log_verbose("%s: Wrong device number: (%u, %u) instead of "
"(%u, %u)", control,
MAJOR(buf.st_mode), MINOR(buf.st_mode),
@@ -267,8 +258,15 @@ static int _create_control(const char *control, uint32_t major, uint32_t minor)
int ret;
mode_t old_umask;
- if (!major)
- return 0;
+ /*
+ * Return if the control already exists with intended major/minor
+ * or there's an error unlinking an apparently incorrect one.
+ */
+ ret = _control_exists(control, major, minor);
+ if (ret == -1)
+ return 0; /* Failed to unlink existing incorrect node */
+ if (ret)
+ return 1; /* Already exists and correct */
(void) dm_prepare_selinux_context(dm_dir(), S_IFDIR);
old_umask = umask(DM_DEV_DIR_UMASK);
@@ -282,12 +280,14 @@ static int _create_control(const char *control, uint32_t major, uint32_t minor)
log_verbose("Creating device %s (%u, %u)", control, major, minor);
(void) dm_prepare_selinux_context(control, S_IFCHR);
+ old_umask = umask(DM_CONTROL_NODE_UMASK);
if (mknod(control, S_IFCHR | S_IRUSR | S_IWUSR,
- MKDEV(major, minor)) < 0) {
+ MKDEV((dev_t)major, minor)) < 0) {
log_sys_error("mknod", control);
(void) dm_prepare_selinux_context(NULL, 0);
return 0;
}
+ umask(old_umask);
(void) dm_prepare_selinux_context(NULL, 0);
return 1;
@@ -357,14 +357,10 @@ static void _close_control_fd(void)
}
}
-static int _open_and_assign_control_fd(const char *control,
- int ignore_nodev)
+static int _open_and_assign_control_fd(const char *control)
{
- _close_control_fd();
-
if ((_control_fd = open(control, O_RDWR)) < 0) {
- if (!(ignore_nodev && errno == ENODEV))
- log_sys_error("open", control);
+ log_sys_error("open", control);
return 0;
}
@@ -375,8 +371,8 @@ static int _open_control(void)
{
#ifdef DM_IOCTLS
char control[PATH_MAX];
- uint32_t major = 0, minor;
- int dm_mod_autoload_support, needs_open;
+ uint32_t major = MISC_MAJOR;
+ uint32_t minor = MAPPER_CTRL_MINOR;
if (_control_fd != -1)
return 1;
@@ -387,61 +383,27 @@ static int _open_control(void)
snprintf(control, sizeof(control), "%s/%s", dm_dir(), DM_CONTROL_NODE);
/*
- * dm-mod autoloading is supported since kernel 2.6.36.
- * Udev daemon will try to read modules.devname file extracted
- * by depmod and create any static nodes needed.
- * The /dev/mapper/control node can be created and prepared this way.
- * First access to such node should load dm-mod module automatically.
- */
- dm_mod_autoload_support = KERNEL_VERSION(_kernel_major, _kernel_minor,
- _kernel_release) >= KERNEL_VERSION(2, 6, 36);
-
- /*
- * If dm-mod autoloading is supported and the control node exists
- * already try to open it now. This should autoload dm-mod module.
+ * Prior to 2.6.36 the minor number should be looked up in /proc.
*/
- if (dm_mod_autoload_support) {
- if (!_get_proc_number(PROC_DEVICES, MISC_NAME, &major))
- /* If major not found, just fallback to hardcoded value. */
- major = MISC_MAJOR;
-
- /* Recreate the node with correct major and minor if needed. */
- if (!_control_exists(control, major, MAPPER_CTRL_MINOR) &&
- !_create_control(control, major, MAPPER_CTRL_MINOR))
- goto error;
-
- _open_and_assign_control_fd(control, 1);
- }
+ if ((KERNEL_VERSION(_kernel_major, _kernel_minor, _kernel_release) <
+ KERNEL_VERSION(2, 6, 36)) &&
+ !_control_device_number(&major, &minor))
+ goto_bad;
/*
- * Get major and minor number assigned for the control node.
- * In case we make use of the module autoload support, this
- * information should be accessible now as well.
+ * Create the node with correct major and minor if not already done.
+ * Udev may already have created /dev/mapper/control
+ * from the modules.devname file generated by depmod.
*/
- if (!_control_device_number(&major, &minor))
- log_error("Is device-mapper driver missing from kernel?");
+ if (!_create_control(control, major, minor))
+ goto_bad;
/*
- * Check the control node and its major and minor number.
- * If there's anything wrong, remove the old node and create
- * a correct one.
+ * As of 2.6.36 kernels, the open can trigger autoloading dm-mod.
*/
- if ((needs_open = !_control_exists(control, major, minor)) &&
- !_create_control(control, major, minor)) {
- _close_control_fd();
- goto error;
- }
-
- /*
- * For older kernels without dm-mod autoloading support, we always
- * need to open the control node here - we still haven't done that!
- * For newer kernels with dm-mod autoloading, we open it only if the
- * node was recreated and corrected in previous step.
- */
- if ((!dm_mod_autoload_support || needs_open) &&
- !_open_and_assign_control_fd(control, 0))
- goto error;
-
+ if (!_open_and_assign_control_fd(control))
+ goto_bad;
+
if (!_create_dm_bitset()) {
log_error("Failed to set up list of device-mapper major numbers");
return 0;
@@ -449,8 +411,10 @@ static int _open_control(void)
return 1;
-error:
+bad:
log_error("Failure to communicate with kernel device-mapper driver.");
+ if (!geteuid())
+ log_error("Check that device-mapper is available in the kernel.");
return 0;
#else
return 1;
@@ -484,427 +448,18 @@ void dm_task_destroy(struct dm_task *dmt)
dm_free(t);
}
- if (dmt->dev_name)
- dm_free(dmt->dev_name);
-
- if (dmt->newname)
- dm_free(dmt->newname);
-
- if (dmt->message)
- dm_free(dmt->message);
-
_dm_zfree_dmi(dmt->dmi.v4);
-
- if (dmt->uuid)
- dm_free(dmt->uuid);
-
+ dm_free(dmt->dev_name);
+ dm_free(dmt->mangled_dev_name);
+ dm_free(dmt->newname);
+ dm_free(dmt->message);
+ dm_free(dmt->geometry);
+ dm_free(dmt->uuid);
+ dm_free(dmt->mangled_uuid);
dm_free(dmt);
}
/*
- * Protocol Version 1 compatibility functions.
- */
-
-#ifdef DM_COMPAT
-
-static void _dm_zfree_dmi_v1(struct dm_ioctl_v1 *dmi)
-{
- if (dmi) {
- memset(dmi, 0, dmi->data_size);
- dm_free(dmi);
- }
-}
-
-static int _dm_task_get_driver_version_v1(struct dm_task *dmt, char *version,
- size_t size)
-{
- unsigned int *v;
-
- if (!dmt->dmi.v1) {
- version[0] = '\0';
- return 0;
- }
-
- v = dmt->dmi.v1->version;
- snprintf(version, size, "%u.%u.%u", v[0], v[1], v[2]);
- return 1;
-}
-
-/* Unmarshall the target info returned from a status call */
-static int _unmarshal_status_v1(struct dm_task *dmt, struct dm_ioctl_v1 *dmi)
-{
- char *outbuf = (char *) dmi + dmi->data_start;
- char *outptr = outbuf;
- int32_t i;
- struct dm_target_spec_v1 *spec;
-
- for (i = 0; i < dmi->target_count; i++) {
- spec = (struct dm_target_spec_v1 *) outptr;
-
- if (!dm_task_add_target(dmt, spec->sector_start,
- (uint64_t) spec->length,
- spec->target_type,
- outptr + sizeof(*spec))) {
- return 0;
- }
-
- outptr = outbuf + spec->next;
- }
-
- return 1;
-}
-
-static int _dm_format_dev_v1(char *buf, int bufsize, uint32_t dev_major,
- uint32_t dev_minor)
-{
- int r;
-
- if (bufsize < 8)
- return 0;
-
- r = snprintf(buf, bufsize, "%03x:%03x", dev_major, dev_minor);
- if (r < 0 || r > bufsize - 1)
- return 0;
-
- return 1;
-}
-
-static int _dm_task_get_info_v1(struct dm_task *dmt, struct dm_info *info)
-{
- if (!dmt->dmi.v1)
- return 0;
-
- memset(info, 0, sizeof(*info));
-
- info->exists = dmt->dmi.v1->flags & DM_EXISTS_FLAG ? 1 : 0;
- if (!info->exists)
- return 1;
-
- info->suspended = dmt->dmi.v1->flags & DM_SUSPEND_FLAG ? 1 : 0;
- info->read_only = dmt->dmi.v1->flags & DM_READONLY_FLAG ? 1 : 0;
- info->target_count = dmt->dmi.v1->target_count;
- info->open_count = dmt->dmi.v1->open_count;
- info->event_nr = 0;
- info->major = MAJOR(dmt->dmi.v1->dev);
- info->minor = MINOR(dmt->dmi.v1->dev);
- info->live_table = 1;
- info->inactive_table = 0;
-
- return 1;
-}
-
-static const char *_dm_task_get_name_v1(const struct dm_task *dmt)
-{
- return (dmt->dmi.v1->name);
-}
-
-static const char *_dm_task_get_uuid_v1(const struct dm_task *dmt)
-{
- return (dmt->dmi.v1->uuid);
-}
-
-static struct dm_deps *_dm_task_get_deps_v1(struct dm_task *dmt)
-{
- log_error("deps version 1 no longer supported by libdevmapper");
- return NULL;
-}
-
-static struct dm_names *_dm_task_get_names_v1(struct dm_task *dmt)
-{
- return (struct dm_names *) (((void *) dmt->dmi.v1) +
- dmt->dmi.v1->data_start);
-}
-
-static void *_add_target_v1(struct target *t, void *out, void *end)
-{
- void *out_sp = out;
- struct dm_target_spec_v1 sp;
- size_t sp_size = sizeof(struct dm_target_spec_v1);
- int len;
-
- out += sp_size;
- if (out >= end)
- return_NULL;
-
- sp.status = 0;
- sp.sector_start = t->start;
- sp.length = t->length;
- strncpy(sp.target_type, t->type, sizeof(sp.target_type));
-
- len = strlen(t->params);
-
- if ((out + len + 1) >= end)
- return_NULL;
-
- strcpy((char *) out, t->params);
- out += len + 1;
-
- /* align next block */
- out = _align(out, ALIGNMENT_V1);
-
- sp.next = out - out_sp;
-
- memcpy(out_sp, &sp, sp_size);
-
- return out;
-}
-
-static struct dm_ioctl_v1 *_flatten_v1(struct dm_task *dmt)
-{
- const size_t min_size = 16 * 1024;
- const int (*version)[3];
-
- struct dm_ioctl_v1 *dmi;
- struct target *t;
- size_t len = sizeof(struct dm_ioctl_v1);
- void *b, *e;
- int count = 0;
-
- for (t = dmt->head; t; t = t->next) {
- len += sizeof(struct dm_target_spec_v1);
- len += strlen(t->params) + 1 + ALIGNMENT_V1;
- count++;
- }
-
- if (count && dmt->newname) {
- log_error("targets and newname are incompatible");
- return NULL;
- }
-
- if (dmt->newname)
- len += strlen(dmt->newname) + 1;
-
- /*
- * Give len a minimum size so that we have space to store
- * dependencies or status information.
- */
- if (len < min_size)
- len = min_size;
-
- if (!(dmi = dm_malloc(len)))
- return NULL;
-
- memset(dmi, 0, len);
-
- version = &_cmd_data_v1[dmt->type].version;
-
- dmi->version[0] = (*version)[0];
- dmi->version[1] = (*version)[1];
- dmi->version[2] = (*version)[2];
-
- dmi->data_size = len;
- dmi->data_start = sizeof(struct dm_ioctl_v1);
-
- if (dmt->dev_name)
- strncpy(dmi->name, dmt->dev_name, sizeof(dmi->name));
-
- if (dmt->type == DM_DEVICE_SUSPEND)
- dmi->flags |= DM_SUSPEND_FLAG;
- if (dmt->read_only)
- dmi->flags |= DM_READONLY_FLAG;
-
- if (dmt->minor >= 0) {
- if (dmt->major <= 0) {
- log_error("Missing major number for persistent device");
- return NULL;
- }
- dmi->flags |= DM_PERSISTENT_DEV_FLAG;
- dmi->dev = MKDEV(dmt->major, dmt->minor);
- }
-
- if (dmt->uuid)
- strncpy(dmi->uuid, dmt->uuid, sizeof(dmi->uuid));
-
- dmi->target_count = count;
-
- b = (void *) (dmi + 1);
- e = (void *) ((char *) dmi + len);
-
- for (t = dmt->head; t; t = t->next)
- if (!(b = _add_target_v1(t, b, e))) {
- log_error("Ran out of memory building ioctl parameter");
- goto bad;
- }
-
- if (dmt->newname)
- strcpy(b, dmt->newname);
-
- return dmi;
-
- bad:
- _dm_zfree_dmi_v1(dmi);
- return NULL;
-}
-
-static int _dm_names_v1(struct dm_ioctl_v1 *dmi)
-{
- const char *dev_dir = dm_dir();
- int r = 1, len;
- const char *name;
- struct dirent *dirent;
- DIR *d;
- struct dm_names *names, *old_names = NULL;
- void *end = (void *) dmi + dmi->data_size;
- struct stat buf;
- char path[PATH_MAX];
-
- log_warn("WARNING: Device list may be incomplete with interface "
- "version 1.");
- log_warn("Please upgrade your kernel device-mapper driver.");
-
- if (!(d = opendir(dev_dir))) {
- log_sys_error("opendir", dev_dir);
- return 0;
- }
-
- names = (struct dm_names *) ((void *) dmi + dmi->data_start);
-
- names->dev = 0; /* Flags no data */
-
- while ((dirent = readdir(d))) {
- name = dirent->d_name;
-
- if (name[0] == '.' || !strcmp(name, "control"))
- continue;
-
- if (old_names)
- old_names->next = (uint32_t) ((void *) names -
- (void *) old_names);
- snprintf(path, sizeof(path), "%s/%s", dev_dir, name);
- if (stat(path, &buf)) {
- log_sys_error("stat", path);
- continue;
- }
- if (!S_ISBLK(buf.st_mode))
- continue;
- names->dev = (uint64_t) buf.st_rdev;
- names->next = 0;
- len = strlen(name);
- if (((void *) (names + 1) + len + 1) >= end) {
- log_error("Insufficient buffer space for device list");
- r = 0;
- break;
- }
-
- strcpy(names->name, name);
-
- old_names = names;
- names = _align((void *) ++names + len + 1, ALIGNMENT);
- }
-
- if (closedir(d))
- log_sys_error("closedir", dev_dir);
-
- return r;
-}
-
-static int _dm_task_run_v1(struct dm_task *dmt)
-{
- struct dm_ioctl_v1 *dmi;
- unsigned int command;
-
- dmi = _flatten_v1(dmt);
- if (!dmi) {
- log_error("Couldn't create ioctl argument.");
- return 0;
- }
-
- if (!_open_control())
- return 0;
-
- if ((unsigned) dmt->type >=
- (sizeof(_cmd_data_v1) / sizeof(*_cmd_data_v1))) {
- log_error(INTERNAL_ERROR "unknown device-mapper task %d",
- dmt->type);
- goto bad;
- }
-
- command = _cmd_data_v1[dmt->type].cmd;
-
- if (dmt->type == DM_DEVICE_TABLE)
- dmi->flags |= DM_STATUS_TABLE_FLAG;
-
- if (dmt->new_uuid) {
- log_error("Changing UUID is not supported by kernel.");
- goto bad;
- }
-
- log_debug("dm %s %s %s%s%s [%u]", _cmd_data_v1[dmt->type].name,
- dmi->name, dmi->uuid, dmt->newname ? " " : "",
- dmt->newname ? dmt->newname : "",
- dmi->data_size);
- if (dmt->type == DM_DEVICE_LIST) {
- if (!_dm_names_v1(dmi))
- goto bad;
- }
-#ifdef DM_IOCTLS
- else if (ioctl(_control_fd, command, dmi) < 0) {
- if (_log_suppress)
- log_verbose("device-mapper: %s ioctl failed: %s",
- _cmd_data_v1[dmt->type].name,
- strerror(errno));
- else
- log_error("device-mapper: %s ioctl failed: %s",
- _cmd_data_v1[dmt->type].name,
- strerror(errno));
- goto bad;
- }
-#else /* Userspace alternative for testing */
-#endif
-
- if (dmi->flags & DM_BUFFER_FULL_FLAG)
- /* FIXME Increase buffer size and retry operation (if query) */
- log_error("WARNING: libdevmapper buffer too small for data");
-
- switch (dmt->type) {
- case DM_DEVICE_CREATE:
- add_dev_node(dmt->dev_name, MAJOR(dmi->dev), MINOR(dmi->dev),
- dmt->uid, dmt->gid, dmt->mode, 0);
- break;
-
- case DM_DEVICE_REMOVE:
- rm_dev_node(dmt->dev_name, 0);
- break;
-
- case DM_DEVICE_RENAME:
- rename_dev_node(dmt->dev_name, dmt->newname, 0);
- break;
-
- case DM_DEVICE_MKNODES:
- if (dmi->flags & DM_EXISTS_FLAG)
- add_dev_node(dmt->dev_name, MAJOR(dmi->dev),
- MINOR(dmi->dev), dmt->uid,
- dmt->gid, dmt->mode, 0);
- else
- rm_dev_node(dmt->dev_name, 0);
- break;
-
- case DM_DEVICE_STATUS:
- case DM_DEVICE_TABLE:
- if (!_unmarshal_status_v1(dmt, dmi))
- goto bad;
- break;
-
- case DM_DEVICE_SUSPEND:
- case DM_DEVICE_RESUME:
- dmt->type = DM_DEVICE_INFO;
- if (!dm_task_run(dmt))
- goto bad;
- _dm_zfree_dmi_v1(dmi); /* We'll use what info returned */
- return 1;
- }
-
- dmt->dmi.v1 = dmi;
- return 1;
-
- bad:
- _dm_zfree_dmi_v1(dmi);
- return 0;
-}
-
-#endif
-
-/*
* Protocol Version 4 functions.
*/
@@ -912,20 +467,22 @@ int dm_task_get_driver_version(struct dm_task *dmt, char *version, size_t size)
{
unsigned *v;
-#ifdef DM_COMPAT
- if (_dm_version == 1)
- return _dm_task_get_driver_version_v1(dmt, version, size);
-#endif
-
if (!dmt->dmi.v4) {
- version[0] = '\0';
+ if (version)
+ version[0] = '\0';
return 0;
}
v = dmt->dmi.v4->version;
- snprintf(version, size, "%u.%u.%u", v[0], v[1], v[2]);
_dm_version_minor = v[1];
_dm_version_patchlevel = v[2];
+ if (version &&
+ (snprintf(version, size, "%u.%u.%u", v[0], v[1], v[2]) < 0)) {
+ log_error("Buffer for version is to short.");
+ if (size > 0)
+ version[0] = '\0';
+ return 0;
+ }
return 1;
}
@@ -945,7 +502,8 @@ static int _check_version(char *version, size_t size, int log_suppress)
_log_suppress = 1;
r = dm_task_run(task);
- dm_task_get_driver_version(task, version, size);
+ if (!dm_task_get_driver_version(task, version, size))
+ stack;
dm_task_destroy(task);
_log_suppress = 0;
@@ -970,7 +528,7 @@ int dm_check_version(void)
return 1;
if (!_dm_compat)
- goto bad;
+ goto_bad;
log_verbose("device-mapper ioctl protocol version %u failed. "
"Trying protocol version 1.", _dm_version);
@@ -995,8 +553,25 @@ int dm_check_version(void)
int dm_cookie_supported(void)
{
return (dm_check_version() &&
- _dm_version >= 4 &&
- _dm_version_minor >= 15);
+ _dm_version >= 4 &&
+ _dm_version_minor >= 15);
+}
+
+static int dm_inactive_supported(void)
+{
+ int inactive_supported = 0;
+
+ if (dm_check_version() && _dm_version >= 4) {
+ if (_dm_version_minor >= 16)
+ inactive_supported = 1; /* upstream */
+ else if (_dm_version_minor == 11 &&
+ (_dm_version_patchlevel >= 6 &&
+ _dm_version_patchlevel <= 40)) {
+ inactive_supported = 1; /* RHEL 5.7 */
+ }
+ }
+
+ return inactive_supported;
}
void *dm_get_next_target(struct dm_task *dmt, void *next,
@@ -1008,8 +583,13 @@ void *dm_get_next_target(struct dm_task *dmt, void *next,
if (!t)
t = dmt->head;
- if (!t)
+ if (!t) {
+ *start = 0;
+ *length = 0;
+ *target_type = 0;
+ *params = 0;
return NULL;
+ }
*start = t->start;
*length = t->length;
@@ -1047,11 +627,6 @@ int dm_format_dev(char *buf, int bufsize, uint32_t dev_major,
{
int r;
-#ifdef DM_COMPAT
- if (_dm_version == 1)
- return _dm_format_dev_v1(buf, bufsize, dev_major, dev_minor);
-#endif
-
if (bufsize < 8)
return 0;
@@ -1064,11 +639,6 @@ int dm_format_dev(char *buf, int bufsize, uint32_t dev_major,
int dm_task_get_info(struct dm_task *dmt, struct dm_info *info)
{
-#ifdef DM_COMPAT
- if (_dm_version == 1)
- return _dm_task_get_info_v1(dmt, info);
-#endif
-
if (!dmt->dmi.v4)
return 0;
@@ -1093,70 +663,33 @@ int dm_task_get_info(struct dm_task *dmt, struct dm_info *info)
}
uint32_t dm_task_get_read_ahead(const struct dm_task *dmt, uint32_t *read_ahead)
-{
+{
const char *dev_name;
*read_ahead = 0;
-#ifdef DM_COMPAT
- /* Not supporting this */
- if (_dm_version == 1)
- return 1;
-#endif
-
- if (!dmt->dmi.v4 || !(dmt->dmi.v4->flags & DM_EXISTS_FLAG))
+ if (!dmt->dmi.v4 || !(dmt->dmi.v4->flags & DM_EXISTS_FLAG))
return 0;
if (*dmt->dmi.v4->name)
dev_name = dmt->dmi.v4->name;
- else if (dmt->dev_name)
- dev_name = dmt->dev_name;
- else {
+ else if (!(dev_name = DEV_NAME(dmt))) {
log_error("Get read ahead request failed: device name unrecorded.");
return 0;
}
- return get_dev_node_read_ahead(dev_name, read_ahead);
-}
-
-const char *dm_task_get_name(const struct dm_task *dmt)
-{
-#ifdef DM_COMPAT
- if (_dm_version == 1)
- return _dm_task_get_name_v1(dmt);
-#endif
-
- return (dmt->dmi.v4->name);
-}
-
-const char *dm_task_get_uuid(const struct dm_task *dmt)
-{
-#ifdef DM_COMPAT
- if (_dm_version == 1)
- return _dm_task_get_uuid_v1(dmt);
-#endif
-
- return (dmt->dmi.v4->uuid);
+ return get_dev_node_read_ahead(dev_name, MAJOR(dmt->dmi.v4->dev),
+ MINOR(dmt->dmi.v4->dev), read_ahead);
}
struct dm_deps *dm_task_get_deps(struct dm_task *dmt)
{
-#ifdef DM_COMPAT
- if (_dm_version == 1)
- return _dm_task_get_deps_v1(dmt);
-#endif
-
return (struct dm_deps *) (((char *) dmt->dmi.v4) +
dmt->dmi.v4->data_start);
}
struct dm_names *dm_task_get_names(struct dm_task *dmt)
{
-#ifdef DM_COMPAT
- if (_dm_version == 1)
- return _dm_task_get_names_v1(dmt);
-#endif
-
return (struct dm_names *) (((char *) dmt->dmi.v4) +
dmt->dmi.v4->data_start);
}
@@ -1188,39 +721,52 @@ int dm_task_suppress_identical_reload(struct dm_task *dmt)
return 1;
}
-int dm_task_set_newuuid(struct dm_task *dmt, const char *newuuid)
+int dm_task_set_add_node(struct dm_task *dmt, dm_add_node_t add_node)
{
- if (strlen(newuuid) >= DM_UUID_LEN) {
- log_error("Uuid \"%s\" too long", newuuid);
+ switch (add_node) {
+ case DM_ADD_NODE_ON_RESUME:
+ case DM_ADD_NODE_ON_CREATE:
+ dmt->add_node = add_node;
+ return 1;
+ default:
+ log_error("Unknown add node parameter");
return 0;
}
+}
- if (!(dmt->newname = dm_strdup(newuuid))) {
- log_error("dm_task_set_newuuid: strdup(%s) failed", newuuid);
+int dm_task_set_newuuid(struct dm_task *dmt, const char *newuuid)
+{
+ dm_string_mangling_t mangling_mode = dm_get_name_mangling_mode();
+ char mangled_uuid[DM_UUID_LEN];
+ int r = 0;
+
+ if (strlen(newuuid) >= DM_UUID_LEN) {
+ log_error("Uuid \"%s\" too long", newuuid);
return 0;
}
- dmt->new_uuid = 1;
- return 1;
-}
+ if (!check_multiple_mangled_string_allowed(newuuid, "new UUID", mangling_mode))
+ return_0;
-int dm_task_set_newname(struct dm_task *dmt, const char *newname)
-{
- if (strchr(newname, '/')) {
- log_error("Name \"%s\" invalid. It contains \"/\".", newname);
+ if (mangling_mode != DM_STRING_MANGLING_NONE &&
+ (r = mangle_string(newuuid, "new UUID", strlen(newuuid), mangled_uuid,
+ sizeof(mangled_uuid), mangling_mode)) < 0) {
+ log_error("Failed to mangle new device UUID \"%s\"", newuuid);
return 0;
}
- if (strlen(newname) >= DM_NAME_LEN) {
- log_error("Name \"%s\" too long", newname);
- return 0;
+ if (r) {
+ log_debug("New device uuid mangled [%s]: %s --> %s",
+ mangling_mode == DM_STRING_MANGLING_AUTO ? "auto" : "hex",
+ newuuid, mangled_uuid);
+ newuuid = mangled_uuid;
}
- if (!(dmt->newname = dm_strdup(newname))) {
- log_error("dm_task_set_newname: strdup(%s) failed", newname);
+ if (!(dmt->newname = dm_strdup(newuuid))) {
+ log_error("dm_task_set_newuuid: strdup(%s) failed", newuuid);
return 0;
}
- dmt->new_uuid = 0;
+ dmt->new_uuid = 1;
return 1;
}
@@ -1242,16 +788,11 @@ int dm_task_set_sector(struct dm_task *dmt, uint64_t sector)
return 1;
}
-int dm_task_set_geometry(struct dm_task *dmt, const char *cylinders, const char *heads, const char *sectors, const char *start)
+int dm_task_set_geometry(struct dm_task *dmt, const char *cylinders, const char *heads,
+ const char *sectors, const char *start)
{
- size_t len = strlen(cylinders) + 1 + strlen(heads) + 1 + strlen(sectors) + 1 + strlen(start) + 1;
-
- if (!(dmt->geometry = dm_malloc(len))) {
- log_error("dm_task_set_geometry: dm_malloc failed");
- return 0;
- }
-
- if (sprintf(dmt->geometry, "%s %s %s %s", cylinders, heads, sectors, start) < 0) {
+ if (dm_asprintf(&(dmt->geometry), "%s %s %s %s",
+ cylinders, heads, sectors, start) < 0) {
log_error("dm_task_set_geometry: sprintf failed");
return 0;
}
@@ -1280,6 +821,20 @@ int dm_task_skip_lockfs(struct dm_task *dmt)
return 1;
}
+int dm_task_secure_data(struct dm_task *dmt)
+{
+ dmt->secure_data = 1;
+
+ return 1;
+}
+
+int dm_task_retry_remove(struct dm_task *dmt)
+{
+ dmt->retry_remove = 1;
+
+ return 1;
+}
+
int dm_task_query_inactive_table(struct dm_task *dmt)
{
dmt->query_inactive_table = 1;
@@ -1297,16 +852,19 @@ int dm_task_set_event_nr(struct dm_task *dmt, uint32_t event_nr)
struct target *create_target(uint64_t start, uint64_t len, const char *type,
const char *params)
{
- struct target *t = dm_malloc(sizeof(*t));
+ struct target *t;
- if (!t) {
+ if (strlen(type) >= DM_MAX_TYPE_NAME) {
+ log_error("Target type name %s is too long.", type);
+ return NULL;
+ }
+
+ if (!(t = dm_zalloc(sizeof(*t)))) {
log_error("create_target: malloc(%" PRIsize_t ") failed",
sizeof(*t));
return NULL;
}
- memset(t, 0, sizeof(*t));
-
if (!(t->params = dm_strdup(params))) {
log_error("create_target: strdup(params) failed");
goto bad;
@@ -1328,29 +886,53 @@ struct target *create_target(uint64_t start, uint64_t len, const char *type,
return NULL;
}
-static void *_add_target(struct target *t, void *out, void *end)
+static char *_add_target(struct target *t, char *out, char *end)
{
- void *out_sp = out;
+ char *out_sp = out;
struct dm_target_spec sp;
size_t sp_size = sizeof(struct dm_target_spec);
+ unsigned int backslash_count = 0;
int len;
+ char *pt;
- out += sp_size;
- if (out >= end)
- return_NULL;
+ if (strlen(t->type) >= sizeof(sp.target_type)) {
+ log_error("Target type name %s is too long.", t->type);
+ return NULL;
+ }
sp.status = 0;
sp.sector_start = t->start;
sp.length = t->length;
- strncpy(sp.target_type, t->type, sizeof(sp.target_type));
+ strncpy(sp.target_type, t->type, sizeof(sp.target_type) - 1);
+ sp.target_type[sizeof(sp.target_type) - 1] = '\0';
- len = strlen(t->params);
+ out += sp_size;
+ pt = t->params;
- if ((out + len + 1) >= end)
- return_NULL;
+ while (*pt)
+ if (*pt++ == '\\')
+ backslash_count++;
+ len = strlen(t->params) + backslash_count;
- strcpy((char *) out, t->params);
- out += len + 1;
+ if ((out >= end) || (out + len + 1) >= end) {
+ log_error("Ran out of memory building ioctl parameter");
+ return NULL;
+ }
+
+ if (backslash_count) {
+ /* replace "\" with "\\" */
+ pt = t->params;
+ do {
+ if (*pt == '\\')
+ *out++ = '\\';
+ *out++ = *pt++;
+ } while (*pt);
+ *out++ = '\0';
+ }
+ else {
+ strcpy(out, t->params);
+ out += len + 1;
+ }
/* align next block */
out = _align(out, ALIGNMENT);
@@ -1381,7 +963,7 @@ static int _lookup_dev_name(uint64_t dev, char *buf, size_t len)
goto out;
do {
- names = (void *) names + next;
+ names = (struct dm_names *)((char *) names + next);
if (names->dev == dev) {
strncpy(buf, names->name, len);
r = 1;
@@ -1404,7 +986,7 @@ static struct dm_ioctl *_flatten(struct dm_task *dmt, unsigned repeat_count)
struct target *t;
struct dm_target_msg *tmsg;
size_t len = sizeof(struct dm_ioctl);
- void *b, *e;
+ char *b, *e;
int count = 0;
for (t = dmt->head; t; t = t->next) {
@@ -1497,11 +1079,11 @@ static struct dm_ioctl *_flatten(struct dm_task *dmt, unsigned repeat_count)
}
dmi->flags |= DM_PERSISTENT_DEV_FLAG;
- dmi->dev = MKDEV(dmt->major, dmt->minor);
+ dmi->dev = MKDEV((dev_t)dmt->major, dmt->minor);
}
/* Does driver support device number referencing? */
- if (_dm_version_minor < 3 && !dmt->dev_name && !dmt->uuid && dmi->dev) {
+ if (_dm_version_minor < 3 && !DEV_NAME(dmt) && !DEV_UUID(dmt) && dmi->dev) {
if (!_lookup_dev_name(dmi->dev, dmi->name, sizeof(dmi->name))) {
log_error("Unable to find name for device (%" PRIu32
":%" PRIu32 ")", dmt->major, dmt->minor);
@@ -1513,12 +1095,12 @@ static struct dm_ioctl *_flatten(struct dm_task *dmt, unsigned repeat_count)
}
/* FIXME Until resume ioctl supplies name, use dev_name for readahead */
- if (dmt->dev_name && (dmt->type != DM_DEVICE_RESUME || dmt->minor < 0 ||
+ if (DEV_NAME(dmt) && (dmt->type != DM_DEVICE_RESUME || dmt->minor < 0 ||
dmt->major < 0))
- strncpy(dmi->name, dmt->dev_name, sizeof(dmi->name));
+ strncpy(dmi->name, DEV_NAME(dmt), sizeof(dmi->name));
- if (dmt->uuid)
- strncpy(dmi->uuid, dmt->uuid, sizeof(dmi->uuid));
+ if (DEV_UUID(dmt))
+ strncpy(dmi->uuid, DEV_UUID(dmt), sizeof(dmi->uuid));
if (dmt->type == DM_DEVICE_SUSPEND)
dmi->flags |= DM_SUSPEND_FLAG;
@@ -1528,8 +1110,14 @@ static struct dm_ioctl *_flatten(struct dm_task *dmt, unsigned repeat_count)
dmi->flags |= DM_READONLY_FLAG;
if (dmt->skip_lockfs)
dmi->flags |= DM_SKIP_LOCKFS_FLAG;
+ if (dmt->secure_data) {
+ if (_dm_version_minor < 20)
+ log_verbose("Secure data flag unsupported by kernel. "
+ "Buffers will not be wiped after use.");
+ dmi->flags |= DM_SECURE_DATA_FLAG;
+ }
if (dmt->query_inactive_table) {
- if (_dm_version_minor < 16)
+ if (!dm_inactive_supported())
log_warn("WARNING: Inactive table query unsupported "
"by kernel. It will use live table.");
dmi->flags |= DM_QUERY_INACTIVE_TABLE_FLAG;
@@ -1546,14 +1134,12 @@ static struct dm_ioctl *_flatten(struct dm_task *dmt, unsigned repeat_count)
dmi->target_count = count;
dmi->event_nr = dmt->event_nr;
- b = (void *) (dmi + 1);
- e = (void *) ((char *) dmi + len);
+ b = (char *) (dmi + 1);
+ e = (char *) dmi + len;
for (t = dmt->head; t; t = t->next)
- if (!(b = _add_target(t, b, e))) {
- log_error("Ran out of memory building ioctl parameter");
- goto bad;
- }
+ if (!(b = _add_target(t, b, e)))
+ goto_bad;
if (dmt->newname)
strcpy(b, dmt->newname);
@@ -1633,7 +1219,7 @@ static int _process_all_v4(struct dm_task *dmt)
goto out;
do {
- names = (void *) names + next;
+ names = (struct dm_names *)((char *) names + next);
if (!dm_task_set_name(dmt, names->name)) {
r = 0;
goto out;
@@ -1691,23 +1277,16 @@ static int _create_and_load_v4(struct dm_task *dmt)
/* Use new task struct to create the device */
if (!(task = dm_task_create(DM_DEVICE_CREATE))) {
- log_error("Failed to create device-mapper task struct");
_udev_complete(dmt);
- return 0;
+ return_0;
}
/* Copy across relevant fields */
- if (dmt->dev_name && !dm_task_set_name(task, dmt->dev_name)) {
- dm_task_destroy(task);
- _udev_complete(dmt);
- return 0;
- }
+ if (dmt->dev_name && !dm_task_set_name(task, dmt->dev_name))
+ goto_bad;
- if (dmt->uuid && !dm_task_set_uuid(task, dmt->uuid)) {
- dm_task_destroy(task);
- _udev_complete(dmt);
- return 0;
- }
+ if (dmt->uuid && !dm_task_set_uuid(task, dmt->uuid))
+ goto_bad;
task->major = dmt->major;
task->minor = dmt->minor;
@@ -1717,40 +1296,41 @@ static int _create_and_load_v4(struct dm_task *dmt)
/* FIXME: Just for udev_check in dm_task_run. Can we avoid this? */
task->event_nr = dmt->event_nr & DM_UDEV_FLAGS_MASK;
task->cookie_set = dmt->cookie_set;
+ task->add_node = dmt->add_node;
+
+ if (!dm_task_run(task))
+ goto_bad;
- r = dm_task_run(task);
dm_task_destroy(task);
- if (!r) {
- _udev_complete(dmt);
- return 0;
- }
/* Next load the table */
if (!(task = dm_task_create(DM_DEVICE_RELOAD))) {
- log_error("Failed to create device-mapper task struct");
+ stack;
_udev_complete(dmt);
- r = 0;
goto revert;
}
/* Copy across relevant fields */
if (dmt->dev_name && !dm_task_set_name(task, dmt->dev_name)) {
+ stack;
dm_task_destroy(task);
_udev_complete(dmt);
- r = 0;
goto revert;
}
task->read_only = dmt->read_only;
task->head = dmt->head;
task->tail = dmt->tail;
+ task->secure_data = dmt->secure_data;
r = dm_task_run(task);
task->head = NULL;
task->tail = NULL;
dm_task_destroy(task);
+
if (!r) {
+ stack;
_udev_complete(dmt);
goto revert;
}
@@ -1759,16 +1339,18 @@ static int _create_and_load_v4(struct dm_task *dmt)
dmt->type = DM_DEVICE_RESUME;
dm_free(dmt->uuid);
dmt->uuid = NULL;
+ dm_free(dmt->mangled_uuid);
+ dmt->mangled_uuid = NULL;
- r = dm_task_run(dmt);
-
- if (r)
- return r;
+ if (dm_task_run(dmt))
+ return 1;
revert:
- dmt->type = DM_DEVICE_REMOVE;
+ dmt->type = DM_DEVICE_REMOVE;
dm_free(dmt->uuid);
dmt->uuid = NULL;
+ dm_free(dmt->mangled_uuid);
+ dmt->mangled_uuid = NULL;
/*
* Also udev-synchronize "remove" dm task that is a part of this revert!
@@ -1780,13 +1362,19 @@ static int _create_and_load_v4(struct dm_task *dmt)
if (!dm_task_set_cookie(dmt, &cookie,
(dmt->event_nr & DM_UDEV_FLAGS_MASK) >>
DM_UDEV_FLAGS_SHIFT))
- stack; /* keep going */
+ stack; /* keep going */
}
if (!dm_task_run(dmt))
log_error("Failed to revert device creation.");
- return r;
+ return 0;
+
+ bad:
+ dm_task_destroy(task);
+ _udev_complete(dmt);
+
+ return 0;
}
uint64_t dm_task_get_existing_table_size(struct dm_task *dmt)
@@ -1798,6 +1386,7 @@ static int _reload_with_suppression_v4(struct dm_task *dmt)
{
struct dm_task *task;
struct target *t1, *t2;
+ size_t len;
int r;
/* New task to get existing table information */
@@ -1833,15 +1422,16 @@ static int _reload_with_suppression_v4(struct dm_task *dmt)
t2 = t2->next;
dmt->existing_table_size = t2 ? t2->start + t2->length : 0;
- if ((task->dmi.v4->flags & DM_READONLY_FLAG) ? 1 : 0 != dmt->read_only)
+ if (((task->dmi.v4->flags & DM_READONLY_FLAG) ? 1 : 0) != dmt->read_only)
goto no_match;
t1 = dmt->head;
t2 = task->head;
while (t1 && t2) {
- while (t2->params[strlen(t2->params) - 1] == ' ')
- t2->params[strlen(t2->params) - 1] = '\0';
+ len = strlen(t2->params);
+ while (len-- > 0 && t2->params[len] == ' ')
+ t2->params[len] = '\0';
if ((t1->start != t2->start) ||
(t1->length != t2->length) ||
(strcmp(t1->type, t2->type)) ||
@@ -1868,6 +1458,107 @@ no_match:
return r;
}
+static int _check_children_not_suspended_v4(struct dm_task *dmt, uint64_t device)
+{
+ struct dm_task *task;
+ struct dm_info info;
+ struct dm_deps *deps;
+ int r = 0;
+ uint32_t i;
+
+ /* Find dependencies */
+ if (!(task = dm_task_create(DM_DEVICE_DEPS)))
+ return 0;
+
+ /* Copy across or set relevant fields */
+ if (device) {
+ task->major = MAJOR(device);
+ task->minor = MINOR(device);
+ } else {
+ if (dmt->dev_name && !dm_task_set_name(task, dmt->dev_name))
+ goto out;
+
+ if (dmt->uuid && !dm_task_set_uuid(task, dmt->uuid))
+ goto out;
+
+ task->major = dmt->major;
+ task->minor = dmt->minor;
+ }
+
+ task->uid = dmt->uid;
+ task->gid = dmt->gid;
+ task->mode = dmt->mode;
+ /* FIXME: Just for udev_check in dm_task_run. Can we avoid this? */
+ task->event_nr = dmt->event_nr & DM_UDEV_FLAGS_MASK;
+ task->cookie_set = dmt->cookie_set;
+ task->add_node = dmt->add_node;
+
+ if (!(r = dm_task_run(task)))
+ goto out;
+
+ if (!dm_task_get_info(task, &info) || !info.exists)
+ goto out;
+
+ /*
+ * Warn if any of the devices this device depends upon are already
+ * suspended: I/O could become trapped between the two devices.
+ */
+ if (info.suspended) {
+ if (!device)
+ log_debug("Attempting to suspend a device that is already suspended "
+ "(%u:%u)", info.major, info.minor);
+ else
+ log_error(INTERNAL_ERROR "Attempt to suspend device %s%s%s%.0d%s%.0d%s%s"
+ "that uses already-suspended device (%u:%u)",
+ DEV_NAME(dmt) ? : "", DEV_UUID(dmt) ? : "",
+ dmt->major > 0 ? "(" : "",
+ dmt->major > 0 ? dmt->major : 0,
+ dmt->major > 0 ? ":" : "",
+ dmt->minor > 0 ? dmt->minor : 0,
+ dmt->major > 0 && dmt->minor == 0 ? "0" : "",
+ dmt->major > 0 ? ") " : "",
+ info.major, info.minor);
+
+ /* No need for further recursion */
+ r = 1;
+ goto out;
+ }
+
+ if (!(deps = dm_task_get_deps(task)))
+ goto out;
+
+ for (i = 0; i < deps->count; i++) {
+ /* Only recurse with dm devices */
+ if (MAJOR(deps->device[i]) != _dm_device_major)
+ continue;
+
+ if (!_check_children_not_suspended_v4(task, deps->device[i]))
+ goto out;
+ }
+
+ r = 1;
+
+out:
+ dm_task_destroy(task);
+
+ return r;
+}
+
+static int _suspend_with_validation_v4(struct dm_task *dmt)
+{
+ /* Avoid recursion */
+ dmt->enable_checks = 0;
+
+ /*
+ * Ensure we can't leave any I/O trapped between suspended devices.
+ */
+ if (!_check_children_not_suspended_v4(dmt, 0))
+ return 0;
+
+ /* Finally, perform the original suspend. */
+ return dm_task_run(dmt);
+}
+
static const char *_sanitise_message(char *message)
{
const char *sanitised_message = message ?: "";
@@ -1880,13 +1571,76 @@ static const char *_sanitise_message(char *message)
return sanitised_message;
}
+static int _do_dm_ioctl_unmangle_string(char *str, const char *str_name,
+ char *buf, size_t buf_size,
+ dm_string_mangling_t mode)
+{
+ int r;
+
+ if (mode == DM_STRING_MANGLING_NONE)
+ return 1;
+
+ if (!check_multiple_mangled_string_allowed(str, str_name, mode))
+ return_0;
+
+ if ((r = unmangle_string(str, str_name, strlen(str), buf, buf_size, mode)) < 0) {
+ log_debug("_do_dm_ioctl_unmangle_string: failed to "
+ "unmangle %s \"%s\"", str_name, str);
+ return 0;
+ } else if (r)
+ memcpy(str, buf, strlen(buf) + 1);
+
+ return 1;
+}
+
+static int _dm_ioctl_unmangle_names(int type, struct dm_ioctl *dmi)
+{
+ char buf[DM_NAME_LEN];
+ struct dm_names *names;
+ unsigned next = 0;
+ char *name;
+ int r = 1;
+
+ if ((name = dmi->name))
+ r = _do_dm_ioctl_unmangle_string(name, "name", buf, sizeof(buf),
+ dm_get_name_mangling_mode());
+
+ if (type == DM_DEVICE_LIST &&
+ ((names = ((struct dm_names *) ((char *)dmi + dmi->data_start)))) &&
+ names->dev) {
+ do {
+ names = (struct dm_names *)((char *) names + next);
+ r = _do_dm_ioctl_unmangle_string(names->name, "name",
+ buf, sizeof(buf),
+ dm_get_name_mangling_mode());
+ next = names->next;
+ } while (next);
+ }
+
+ return r;
+}
+
+static int _dm_ioctl_unmangle_uuids(int type, struct dm_ioctl *dmi)
+{
+ char buf[DM_UUID_LEN];
+ char *uuid = dmi->uuid;
+
+ if (uuid)
+ return _do_dm_ioctl_unmangle_string(uuid, "UUID", buf, sizeof(buf),
+ dm_get_name_mangling_mode());
+
+ return 1;
+}
+
static struct dm_ioctl *_do_dm_ioctl(struct dm_task *dmt, unsigned command,
- unsigned repeat_count)
+ unsigned buffer_repeat_count,
+ unsigned retry_repeat_count,
+ int *retryable)
{
struct dm_ioctl *dmi;
int ioctl_with_uevent;
- dmi = _flatten(dmt, repeat_count);
+ dmi = _flatten(dmt, buffer_repeat_count);
if (!dmi) {
log_error("Couldn't create ioctl argument.");
return NULL;
@@ -1947,7 +1701,7 @@ static struct dm_ioctl *_do_dm_ioctl(struct dm_task *dmt, unsigned command,
}
log_debug("dm %s %s%s %s%s%s %s%.0d%s%.0d%s"
- "%s%c%c%s%s %.0" PRIu64 " %s [%u]",
+ "%s%c%c%s%s%s%s%s%s %.0" PRIu64 " %s [%u] (*%u)",
_cmd_data_v4[dmt->type].name,
dmt->new_uuid ? "UUID " : "",
dmi->name, dmi->uuid, dmt->newname ? " " : "",
@@ -1960,12 +1714,17 @@ static struct dm_ioctl *_do_dm_ioctl(struct dm_task *dmt, unsigned command,
dmt->major > 0 ? ") " : "",
dmt->no_open_count ? 'N' : 'O',
dmt->no_flush ? 'N' : 'F',
+ dmt->read_only ? "R" : "",
dmt->skip_lockfs ? "S " : "",
+ dmt->retry_remove ? "T " : "",
+ dmt->secure_data ? "W " : "",
dmt->query_inactive_table ? "I " : "",
+ dmt->enable_checks ? "C" : "",
dmt->sector, _sanitise_message(dmt->message),
- dmi->data_size);
+ dmi->data_size, retry_repeat_count);
#ifdef DM_IOCTLS
- if (ioctl(_control_fd, command, dmi) < 0) {
+ if (ioctl(_control_fd, command, dmi) < 0 &&
+ dmt->expected_errno != errno) {
if (errno == ENXIO && ((dmt->type == DM_DEVICE_INFO) ||
(dmt->type == DM_DEVICE_MKNODES) ||
(dmt->type == DM_DEVICE_STATUS)))
@@ -1977,21 +1736,43 @@ static struct dm_ioctl *_do_dm_ioctl(struct dm_task *dmt, unsigned command,
_cmd_data_v4[dmt->type].name,
strerror(errno));
else
- log_error("device-mapper: %s ioctl "
+ log_error("device-mapper: %s ioctl on %s "
"failed: %s",
- _cmd_data_v4[dmt->type].name,
- strerror(errno));
- _dm_zfree_dmi(dmi);
- return NULL;
+ _cmd_data_v4[dmt->type].name,
+ dmi->name, strerror(errno));
+
+ /*
+ * It's sometimes worth retrying after EBUSY in case
+ * it's a transient failure caused by an asynchronous
+ * process quickly scanning the device.
+ */
+ *retryable = errno == EBUSY;
+
+ goto error;
}
}
- if (ioctl_with_uevent && !_check_uevent_generated(dmi))
+ if (ioctl_with_uevent && dm_udev_get_sync_support() &&
+ !_check_uevent_generated(dmi)) {
+ log_debug("Uevent not generated! Calling udev_complete "
+ "internally to avoid process lock-up.");
_udev_complete(dmt);
+ }
+
+ if (!_dm_ioctl_unmangle_names(dmt->type, dmi))
+ goto error;
+
+ if (dmt->type != DM_DEVICE_REMOVE &&
+ !_dm_ioctl_unmangle_uuids(dmt->type, dmi))
+ goto error;
#else /* Userspace alternative for testing */
#endif
return dmi;
+
+error:
+ _dm_zfree_dmi(dmi);
+ return NULL;
}
void dm_task_update_nodes(void)
@@ -1999,17 +1780,20 @@ void dm_task_update_nodes(void)
update_devs();
}
+#define DM_IOCTL_RETRIES 25
+#define DM_RETRY_USLEEP_DELAY 200000
+
int dm_task_run(struct dm_task *dmt)
{
struct dm_ioctl *dmi;
unsigned command;
int check_udev;
- int udev_only;
-
-#ifdef DM_COMPAT
- if (_dm_version == 1)
- return _dm_task_run_v1(dmt);
-#endif
+ int rely_on_udev;
+ int suspended_counter;
+ unsigned ioctl_retry = 1;
+ int retryable = 0;
+ const char *dev_name = DEV_NAME(dmt);
+ const char *dev_uuid = DEV_UUID(dmt);
if ((unsigned) dmt->type >=
(sizeof(_cmd_data_v4) / sizeof(*_cmd_data_v4))) {
@@ -2024,21 +1808,53 @@ int dm_task_run(struct dm_task *dmt)
if (dmt->type == DM_DEVICE_CREATE && dmt->head)
return _create_and_load_v4(dmt);
- if (dmt->type == DM_DEVICE_MKNODES && !dmt->dev_name &&
- !dmt->uuid && dmt->major <= 0)
+ if (dmt->type == DM_DEVICE_MKNODES && !dev_name &&
+ !dev_uuid && dmt->major <= 0)
return _mknodes_v4(dmt);
if ((dmt->type == DM_DEVICE_RELOAD) && dmt->suppress_identical_reload)
return _reload_with_suppression_v4(dmt);
+ if ((dmt->type == DM_DEVICE_SUSPEND) && dmt->enable_checks)
+ return _suspend_with_validation_v4(dmt);
+
if (!_open_control()) {
_udev_complete(dmt);
- return 0;
- }
+ return_0;
+ }
+
+ if ((suspended_counter = dm_get_suspended_counter()) &&
+ dmt->type == DM_DEVICE_RELOAD)
+ log_error(INTERNAL_ERROR "Performing unsafe table load while %d device(s) "
+ "are known to be suspended: "
+ "%s%s%s %s%.0d%s%.0d%s%s",
+ suspended_counter,
+ dev_name ? : "",
+ dev_uuid ? " UUID " : "",
+ dev_uuid ? : "",
+ dmt->major > 0 ? "(" : "",
+ dmt->major > 0 ? dmt->major : 0,
+ dmt->major > 0 ? ":" : "",
+ dmt->minor > 0 ? dmt->minor : 0,
+ dmt->major > 0 && dmt->minor == 0 ? "0" : "",
+ dmt->major > 0 ? ") " : "");
/* FIXME Detect and warn if cookie set but should not be. */
repeat_ioctl:
- if (!(dmi = _do_dm_ioctl(dmt, command, _ioctl_buffer_double_factor))) {
+ if (!(dmi = _do_dm_ioctl(dmt, command, _ioctl_buffer_double_factor,
+ ioctl_retry, &retryable))) {
+ /*
+ * Async udev rules that scan devices commonly cause transient
+ * failures. Normally you'd expect the user to have made sure
+ * nothing was using the device before issuing REMOVE, so it's
+ * worth retrying in case the failure is indeed transient.
+ */
+ if (retryable && dmt->type == DM_DEVICE_REMOVE &&
+ dmt->retry_remove && ++ioctl_retry <= DM_IOCTL_RETRIES) {
+ usleep(DM_RETRY_USLEEP_DELAY);
+ goto repeat_ioctl;
+ }
+
_udev_complete(dmt);
return 0;
}
@@ -2059,46 +1875,56 @@ repeat_ioctl:
}
}
+ /*
+ * Are we expecting a udev operation to occur that we need to check for?
+ */
check_udev = dmt->cookie_set &&
!(dmt->event_nr >> DM_UDEV_FLAGS_SHIFT &
DM_UDEV_DISABLE_DM_RULES_FLAG);
- udev_only = dmt->cookie_set ? (dmt->event_nr >> DM_UDEV_FLAGS_SHIFT &
- DM_UDEV_DISABLE_LIBRARY_FALLBACK) : 0;
+ rely_on_udev = dmt->cookie_set ? (dmt->event_nr >> DM_UDEV_FLAGS_SHIFT &
+ DM_UDEV_DISABLE_LIBRARY_FALLBACK) : 0;
switch (dmt->type) {
case DM_DEVICE_CREATE:
- if (dmt->dev_name && *dmt->dev_name && !udev_only)
- add_dev_node(dmt->dev_name, MAJOR(dmi->dev),
+ if ((dmt->add_node == DM_ADD_NODE_ON_CREATE) &&
+ dev_name && *dev_name && !rely_on_udev)
+ add_dev_node(dev_name, MAJOR(dmi->dev),
MINOR(dmi->dev), dmt->uid, dmt->gid,
- dmt->mode, check_udev);
+ dmt->mode, check_udev, rely_on_udev);
break;
case DM_DEVICE_REMOVE:
/* FIXME Kernel needs to fill in dmi->name */
- if (dmt->dev_name && !udev_only)
- rm_dev_node(dmt->dev_name, check_udev);
+ if (dev_name && !rely_on_udev)
+ rm_dev_node(dev_name, check_udev, rely_on_udev);
break;
case DM_DEVICE_RENAME:
/* FIXME Kernel needs to fill in dmi->name */
- if (!dmt->new_uuid && dmt->dev_name && !udev_only)
- rename_dev_node(dmt->dev_name, dmt->newname,
- check_udev);
+ if (!dmt->new_uuid && dev_name)
+ rename_dev_node(dev_name, dmt->newname,
+ check_udev, rely_on_udev);
break;
case DM_DEVICE_RESUME:
+ if ((dmt->add_node == DM_ADD_NODE_ON_RESUME) &&
+ dev_name && *dev_name)
+ add_dev_node(dev_name, MAJOR(dmi->dev),
+ MINOR(dmi->dev), dmt->uid, dmt->gid,
+ dmt->mode, check_udev, rely_on_udev);
/* FIXME Kernel needs to fill in dmi->name */
- set_dev_node_read_ahead(dmt->dev_name, dmt->read_ahead,
- dmt->read_ahead_flags);
+ set_dev_node_read_ahead(dev_name,
+ MAJOR(dmi->dev), MINOR(dmi->dev),
+ dmt->read_ahead, dmt->read_ahead_flags);
break;
case DM_DEVICE_MKNODES:
if (dmi->flags & DM_EXISTS_FLAG)
add_dev_node(dmi->name, MAJOR(dmi->dev),
MINOR(dmi->dev), dmt->uid,
- dmt->gid, dmt->mode, 0);
- else if (dmt->dev_name)
- rm_dev_node(dmt->dev_name, 0);
+ dmt->gid, dmt->mode, 0, rely_on_udev);
+ else if (dev_name)
+ rm_dev_node(dev_name, 0, rely_on_udev);
break;
case DM_DEVICE_STATUS:
@@ -2129,6 +1955,15 @@ void dm_pools_check_leaks(void);
void dm_lib_exit(void)
{
+ int suspended_counter;
+ static unsigned _exited = 0;
+
+ if (_exited++)
+ return;
+
+ if ((suspended_counter = dm_get_suspended_counter()))
+ log_error("libdevmapper exiting with %d device(s) still suspended.", suspended_counter);
+
dm_lib_release();
selinux_release();
if (_dm_bitset)
diff --git a/libdm/ioctl/libdm-targets.h b/libdm/ioctl/libdm-targets.h
index d8cee45..8fc8738 100644
--- a/libdm/ioctl/libdm-targets.h
+++ b/libdm/ioctl/libdm-targets.h
@@ -20,7 +20,6 @@
#include <sys/types.h>
struct dm_ioctl;
-struct dm_ioctl_v1;
struct target {
uint64_t start;
@@ -34,6 +33,7 @@ struct target {
struct dm_task {
int type;
char *dev_name;
+ char *mangled_dev_name;
struct target *head, *tail;
@@ -49,7 +49,6 @@ struct dm_task {
uint32_t read_ahead_flags;
union {
struct dm_ioctl *v4;
- struct dm_ioctl_v1 *v1;
} dmi;
char *newname;
char *message;
@@ -60,11 +59,17 @@ struct dm_task {
int skip_lockfs;
int query_inactive_table;
int suppress_identical_reload;
+ dm_add_node_t add_node;
uint64_t existing_table_size;
int cookie_set;
int new_uuid;
+ int secure_data;
+ int retry_remove;
+ int enable_checks;
+ int expected_errno;
char *uuid;
+ char *mangled_uuid;
};
struct cmd_data {
diff --git a/libdm/libdevmapper.h b/libdm/libdevmapper.h
index 4aa9991..c853ab4 100644
--- a/libdm/libdevmapper.h
+++ b/libdm/libdevmapper.h
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
- * Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2004-2011 Red Hat, Inc. All rights reserved.
*
* This file is part of the device-mapper userspace tools.
*
@@ -19,6 +19,7 @@
#include <inttypes.h>
#include <stdarg.h>
#include <sys/types.h>
+#include <sys/stat.h>
#ifdef linux
# include <linux/types.h>
@@ -74,6 +75,11 @@ void dm_log_init(dm_log_fn fn);
*/
int dm_log_is_non_default(void);
+/*
+ * Number of devices currently in suspended state (via the library).
+ */
+int dm_get_suspended_counter(void);
+
enum {
DM_DEVICE_CREATE,
DM_DEVICE_RELOAD,
@@ -158,13 +164,33 @@ struct dm_versions {
int dm_get_library_version(char *version, size_t size);
int dm_task_get_driver_version(struct dm_task *dmt, char *version, size_t size);
int dm_task_get_info(struct dm_task *dmt, struct dm_info *dmi);
-const char *dm_task_get_name(const struct dm_task *dmt);
+
+/*
+ * This function returns dm device's UUID based on the value
+ * of the mangling mode set during preceding dm_task_run call:
+ * - unmangled UUID for DM_STRING_MANGLING_{AUTO, HEX},
+ * - UUID without any changes for DM_STRING_MANGLING_NONE.
+ *
+ * To get mangled or unmangled form of the UUID directly, use
+ * dm_task_get_uuid_mangled or dm_task_get_uuid_unmangled function.
+ */
const char *dm_task_get_uuid(const struct dm_task *dmt);
struct dm_deps *dm_task_get_deps(struct dm_task *dmt);
-struct dm_names *dm_task_get_names(struct dm_task *dmt);
struct dm_versions *dm_task_get_versions(struct dm_task *dmt);
+/*
+ * These functions return device-mapper names based on the value
+ * of the mangling mode set during preceding dm_task_run call:
+ * - unmangled name for DM_STRING_MANGLING_{AUTO, HEX},
+ * - name without any changes for DM_STRING_MANGLING_NONE.
+ *
+ * To get mangled or unmangled form of the name directly, use
+ * dm_task_get_name_mangled or dm_task_get_name_unmangled function.
+ */
+const char *dm_task_get_name(const struct dm_task *dmt);
+struct dm_names *dm_task_get_names(struct dm_task *dmt);
+
int dm_task_set_ro(struct dm_task *dmt);
int dm_task_set_newname(struct dm_task *dmt, const char *newname);
int dm_task_set_newuuid(struct dm_task *dmt, const char *newuuid);
@@ -184,6 +210,19 @@ int dm_task_no_open_count(struct dm_task *dmt);
int dm_task_skip_lockfs(struct dm_task *dmt);
int dm_task_query_inactive_table(struct dm_task *dmt);
int dm_task_suppress_identical_reload(struct dm_task *dmt);
+int dm_task_secure_data(struct dm_task *dmt);
+int dm_task_retry_remove(struct dm_task *dmt);
+
+/*
+ * Enable checks for common mistakes such as issuing ioctls in an unsafe order.
+ */
+int dm_task_enable_checks(struct dm_task *dmt);
+
+typedef enum {
+ DM_ADD_NODE_ON_RESUME, /* add /dev/mapper node with dmsetup resume */
+ DM_ADD_NODE_ON_CREATE /* add /dev/mapper node with dmsetup create */
+} dm_add_node_t;
+int dm_task_set_add_node(struct dm_task *dmt, dm_add_node_t add_node);
/*
* Control read_ahead.
@@ -219,6 +258,30 @@ void *dm_get_next_target(struct dm_task *dmt,
void *next, uint64_t *start, uint64_t *length,
char **target_type, char **params);
+/* Parse params from STATUS call for thin_pool target */
+struct dm_pool;
+
+struct dm_status_thin_pool {
+ uint64_t transaction_id;
+ uint64_t used_metadata_blocks;
+ uint64_t total_metadata_blocks;
+ uint64_t used_data_blocks;
+ uint64_t total_data_blocks;
+ uint64_t held_metadata_root;
+};
+
+int dm_get_status_thin_pool(struct dm_pool *mem, const char *params,
+ struct dm_status_thin_pool **status);
+
+/* Parse params from STATUS call for thin target */
+struct dm_status_thin {
+ uint64_t mapped_sectors;
+ uint64_t highest_mapped_sector;
+};
+
+int dm_get_status_thin(struct dm_pool *mem, const char *params,
+ struct dm_status_thin **status);
+
/*
* Call this to actually run the ioctl.
*/
@@ -231,17 +294,96 @@ int dm_task_run(struct dm_task *dmt);
void dm_task_update_nodes(void);
/*
+ * Mangling support
+ *
+ * Character whitelist: 0-9, A-Z, a-z, #+-.:=@_
+ * HEX mangling format: \xNN, NN being the hex value of the character.
+ * (whitelist and format supported by udev)
+*/
+typedef enum {
+ DM_STRING_MANGLING_NONE, /* do not mangle at all */
+ DM_STRING_MANGLING_AUTO, /* mangle only if not already mangled with hex, error when mixed */
+ DM_STRING_MANGLING_HEX /* always mangle with hex encoding, no matter what the input is */
+} dm_string_mangling_t;
+
+/*
+ * Set/get mangling mode used for device-mapper names and uuids.
+ */
+int dm_set_name_mangling_mode(dm_string_mangling_t name_mangling);
+dm_string_mangling_t dm_get_name_mangling_mode(void);
+
+/*
+ * Get mangled/unmangled form of the device-mapper name or uuid
+ * irrespective of the global setting (set by dm_set_name_mangling_mode).
+ * The name or uuid returned needs to be freed after use by calling dm_free!
+ */
+char *dm_task_get_name_mangled(const struct dm_task *dmt);
+char *dm_task_get_name_unmangled(const struct dm_task *dmt);
+char *dm_task_get_uuid_mangled(const struct dm_task *dmt);
+char *dm_task_get_uuid_unmangled(const struct dm_task *dmt);
+
+/*
* Configure the device-mapper directory
*/
int dm_set_dev_dir(const char *dir);
const char *dm_dir(void);
/*
+ * Configure sysfs directory, /sys by default
+ */
+int dm_set_sysfs_dir(const char *dir);
+const char *dm_sysfs_dir(void);
+
+/*
+ * Configure default UUID prefix string.
+ * Conventionally this is a short capitalised prefix indicating the subsystem
+ * that is managing the devices, e.g. "LVM-" or "MPATH-".
+ * To support stacks of devices from different subsystems, recursive functions
+ * stop recursing if they reach a device with a different prefix.
+ */
+int dm_set_uuid_prefix(const char *uuid_prefix);
+const char *dm_uuid_prefix(void);
+
+/*
* Determine whether a major number belongs to device-mapper or not.
*/
int dm_is_dm_major(uint32_t major);
/*
+ * Get associated device name for given major and minor number by reading
+ * the sysfs content. If this is a dm device, get associated dm name, the one
+ * that appears in /dev/mapper. DM names could be resolved this way only if
+ * kernel used >= 2.6.29, kernel name is found otherwise (e.g. dm-0).
+ * If prefer_kernel_name is set, the kernel name is always preferred over
+ * device-mapper name for dm devices no matter what the kernel version is.
+ * For non-dm devices, we always get associated kernel name, e.g sda, md0 etc.
+ * Returns 0 on error or if sysfs is not used (or configured incorrectly),
+ * otherwise returns 1 and the supplied buffer holds the device name.
+ */
+int dm_device_get_name(uint32_t major, uint32_t minor,
+ int prefer_kernel_name,
+ char *buf, size_t buf_size);
+
+/*
+ * Determine whether a device has any holders (devices
+ * using this device). If sysfs is not used (or configured
+ * incorrectly), returns 0.
+ */
+int dm_device_has_holders(uint32_t major, uint32_t minor);
+
+/*
+ * Determine whether a device contains mounted filesystem.
+ * If sysfs is not used (or configured incorrectly), returns 0.
+ */
+int dm_device_has_mounted_fs(uint32_t major, uint32_t minor);
+
+
+/*
+ * Initialise library
+ */
+void dm_lib_init(void) __attribute__((constructor));
+
+/*
* Release library resources
*/
void dm_lib_release(void);
@@ -384,6 +526,11 @@ void dm_tree_skip_lockfs(struct dm_tree_node *dnode);
void dm_tree_use_no_flush_suspend(struct dm_tree_node *dnode);
/*
+ * Retry removal of each device if not successful.
+ */
+void dm_tree_retry_remove(struct dm_tree_node *dnode);
+
+/*
* Is the uuid prefix present in the tree?
* Only returns 0 if every node was checked successfully.
* Returns 1 if the tree walk has to be aborted.
@@ -450,6 +597,14 @@ int dm_tree_node_add_mirror_target_log(struct dm_tree_node *node,
unsigned area_count,
uint32_t flags);
+int dm_tree_node_add_raid_target(struct dm_tree_node *node,
+ uint64_t size,
+ const char *raid_type,
+ uint32_t region_size,
+ uint32_t stripe_size,
+ uint64_t rebuilds,
+ uint64_t flags);
+
/*
* Replicator operation mode
* Note: API for Replicator is not yet stable
@@ -484,6 +639,63 @@ int dm_tree_node_add_replicator_dev_target(struct dm_tree_node *node,
uint32_t slog_region_size);
/* End of Replicator API */
+/*
+ * FIXME: Defines bellow are based on kernel's dm-thin.c defines
+ * DATA_DEV_BLOCK_SIZE_MIN_SECTORS (64 * 1024 >> SECTOR_SHIFT)
+ * DATA_DEV_BLOCK_SIZE_MAX_SECTORS (1024 * 1024 * 1024 >> SECTOR_SHIFT)
+ */
+#define DM_THIN_MIN_DATA_BLOCK_SIZE (UINT32_C(128))
+#define DM_THIN_MAX_DATA_BLOCK_SIZE (UINT32_C(2097152))
+
+int dm_tree_node_add_thin_pool_target(struct dm_tree_node *node,
+ uint64_t size,
+ uint64_t transaction_id,
+ const char *metadata_uuid,
+ const char *pool_uuid,
+ uint32_t data_block_size,
+ uint64_t low_water_mark,
+ unsigned skip_block_zeroing);
+
+/* Supported messages for thin provision target */
+typedef enum {
+ DM_THIN_MESSAGE_CREATE_SNAP, /* device_id, origin_id */
+ DM_THIN_MESSAGE_CREATE_THIN, /* device_id */
+ DM_THIN_MESSAGE_DELETE, /* device_id */
+ DM_THIN_MESSAGE_SET_TRANSACTION_ID, /* current_id, new_id */
+ DM_THIN_MESSAGE_RESERVE_METADATA_SNAP, /* target version >= 1.1 */
+ DM_THIN_MESSAGE_RELEASE_METADATA_SNAP, /* target version >= 1.1 */
+} dm_thin_message_t;
+
+int dm_tree_node_add_thin_pool_message(struct dm_tree_node *node,
+ dm_thin_message_t type,
+ uint64_t id1, uint64_t id2);
+
+/*
+ * Set thin pool discard features
+ * ignore - Disable support for discards
+ * no_passdown - Don't pass discards down to underlying data device,
+ * just remove the mapping
+ * Feature is available since version 1.1 of the thin target.
+ */
+int dm_tree_node_set_thin_pool_discard(struct dm_tree_node *node,
+ unsigned ignore,
+ unsigned no_passdown);
+
+/*
+ * FIXME: Defines bellow are based on kernel's dm-thin.c defines
+ * MAX_DEV_ID ((1 << 24) - 1)
+ */
+#define DM_THIN_MAX_DEVICE_ID (UINT32_C((1 << 24) - 1))
+int dm_tree_node_add_thin_target(struct dm_tree_node *node,
+ uint64_t size,
+ const char *pool_uuid,
+ uint32_t device_id);
+
+int dm_tree_node_set_thin_external_origin(struct dm_tree_node *node,
+ const char *external_uuid);
+
+void dm_tree_node_set_udev_flags(struct dm_tree_node *node, uint16_t udev_flags);
+
void dm_tree_node_set_presuspend_node(struct dm_tree_node *node,
struct dm_tree_node *presuspend_node);
@@ -493,12 +705,31 @@ int dm_tree_node_add_target_area(struct dm_tree_node *node,
uint64_t offset);
/*
+ * Only for temporarily-missing raid devices where changes are tracked.
+ */
+int dm_tree_node_add_null_area(struct dm_tree_node *node, uint64_t offset);
+
+/*
* Set readahead (in sectors) after loading the node.
*/
void dm_tree_node_set_read_ahead(struct dm_tree_node *dnode,
uint32_t read_ahead,
uint32_t read_ahead_flags);
+/*
+ * Set node callback hook before de/activation.
+ * Callback is called before 'activation' of node for activation tree,
+ * or 'deactivation' of node for deactivation tree.
+ */
+typedef enum {
+ DM_NODE_CALLBACK_PRELOADED, /* Node has preload deps */
+ DM_NODE_CALLBACK_DEACTIVATED, /* Node is deactivated */
+} dm_node_callback_t;
+typedef int (*dm_node_callback_fn) (struct dm_tree_node *node,
+ dm_node_callback_t type, void *cb_data);
+void dm_tree_node_set_callback(struct dm_tree_node *node,
+ dm_node_callback_fn cb, void *cb_data);
+
void dm_tree_set_cookie(struct dm_tree_node *node, uint32_t cookie);
uint32_t dm_tree_get_cookie(struct dm_tree_node *node);
@@ -510,13 +741,19 @@ uint32_t dm_tree_get_cookie(struct dm_tree_node *node);
* Memory management
*******************/
-void *dm_malloc_aux(size_t s, const char *file, int line);
-void *dm_malloc_aux_debug(size_t s, const char *file, int line);
-void *dm_zalloc_aux(size_t s, const char *file, int line);
-void *dm_zalloc_aux_debug(size_t s, const char *file, int line);
-char *dm_strdup_aux(const char *str, const char *file, int line);
+void *dm_malloc_aux(size_t s, const char *file, int line)
+ __attribute__((malloc)) __attribute__((__warn_unused_result__));
+void *dm_malloc_aux_debug(size_t s, const char *file, int line)
+ __attribute__((__warn_unused_result__));
+void *dm_zalloc_aux(size_t s, const char *file, int line)
+ __attribute__((malloc)) __attribute__((__warn_unused_result__));
+void *dm_zalloc_aux_debug(size_t s, const char *file, int line)
+ __attribute__((__warn_unused_result__));
+char *dm_strdup_aux(const char *str, const char *file, int line)
+ __attribute__((malloc)) __attribute__((__warn_unused_result__));
void dm_free_aux(void *p);
-void *dm_realloc_aux(void *p, unsigned int s, const char *file, int line);
+void *dm_realloc_aux(void *p, unsigned int s, const char *file, int line)
+ __attribute__((__warn_unused_result__));
int dm_dump_memory_debug(void);
void dm_bounds_check_debug(void);
@@ -579,16 +816,35 @@ void dm_bounds_check_debug(void);
struct dm_pool;
/* constructor and destructor */
-struct dm_pool *dm_pool_create(const char *name, size_t chunk_hint);
+struct dm_pool *dm_pool_create(const char *name, size_t chunk_hint)
+ __attribute__((__warn_unused_result__));
void dm_pool_destroy(struct dm_pool *p);
/* simple allocation/free routines */
-void *dm_pool_alloc(struct dm_pool *p, size_t s);
-void *dm_pool_alloc_aligned(struct dm_pool *p, size_t s, unsigned alignment);
+void *dm_pool_alloc(struct dm_pool *p, size_t s)
+ __attribute__((__warn_unused_result__));
+void *dm_pool_alloc_aligned(struct dm_pool *p, size_t s, unsigned alignment)
+ __attribute__((__warn_unused_result__));
void dm_pool_empty(struct dm_pool *p);
void dm_pool_free(struct dm_pool *p, void *ptr);
/*
+ * To aid debugging, a pool can be locked. Any modifications made
+ * to the content of the pool while it is locked can be detected.
+ * Default compilation is using a crc checksum to notice modifications.
+ * The pool locking is using the mprotect with the compilation flag
+ * DEBUG_ENFORCE_POOL_LOCKING to enforce the memory protection.
+ */
+/* query pool lock status */
+int dm_pool_locked(struct dm_pool *p);
+/* mark pool as locked */
+int dm_pool_lock(struct dm_pool *p, int crc)
+ __attribute__((__warn_unused_result__));
+/* mark pool as unlocked */
+int dm_pool_unlock(struct dm_pool *p, int crc)
+ __attribute__((__warn_unused_result__));
+
+/*
* Object building routines:
*
* These allow you to 'grow' an object, useful for
@@ -639,9 +895,12 @@ void *dm_pool_end_object(struct dm_pool *p);
void dm_pool_abandon_object(struct dm_pool *p);
/* utilities */
-char *dm_pool_strdup(struct dm_pool *p, const char *str);
-char *dm_pool_strndup(struct dm_pool *p, const char *str, size_t n);
-void *dm_pool_zalloc(struct dm_pool *p, size_t s);
+char *dm_pool_strdup(struct dm_pool *p, const char *str)
+ __attribute__((__warn_unused_result__));
+char *dm_pool_strndup(struct dm_pool *p, const char *str, size_t n)
+ __attribute__((__warn_unused_result__));
+void *dm_pool_zalloc(struct dm_pool *p, size_t s)
+ __attribute__((__warn_unused_result__));
/******************
* bitset functions
@@ -699,7 +958,8 @@ struct dm_hash_node;
typedef void (*dm_hash_iterate_fn) (void *data);
-struct dm_hash_table *dm_hash_create(unsigned size_hint);
+struct dm_hash_table *dm_hash_create(unsigned size_hint)
+ __attribute__((__warn_unused_result__));
void dm_hash_destroy(struct dm_hash_table *t);
void dm_hash_wipe(struct dm_hash_table *t);
@@ -707,10 +967,10 @@ void *dm_hash_lookup(struct dm_hash_table *t, const char *key);
int dm_hash_insert(struct dm_hash_table *t, const char *key, void *data);
void dm_hash_remove(struct dm_hash_table *t, const char *key);
-void *dm_hash_lookup_binary(struct dm_hash_table *t, const char *key, uint32_t len);
-int dm_hash_insert_binary(struct dm_hash_table *t, const char *key, uint32_t len,
+void *dm_hash_lookup_binary(struct dm_hash_table *t, const void *key, uint32_t len);
+int dm_hash_insert_binary(struct dm_hash_table *t, const void *key, uint32_t len,
void *data);
-void dm_hash_remove_binary(struct dm_hash_table *t, const char *key, uint32_t len);
+void dm_hash_remove_binary(struct dm_hash_table *t, const void *key, uint32_t len);
unsigned dm_hash_get_num_entries(struct dm_hash_table *t);
void dm_hash_iter(struct dm_hash_table *t, dm_hash_iterate_fn f);
@@ -770,7 +1030,7 @@ void dm_list_del(struct dm_list *elem);
void dm_list_move(struct dm_list *head, struct dm_list *elem);
/*
- * Join 'head1' to the of 'head'.
+ * Join 'head1' to the end of 'head'.
*/
void dm_list_splice(struct dm_list *head, struct dm_list *head1);
@@ -968,6 +1228,52 @@ int dm_snprintf(char *buf, size_t bufsize, const char *format, ...)
*/
const char *dm_basename(const char *path);
+/*
+ * Returns number of occurrences of 'c' in 'str' of length 'size'.
+ */
+unsigned dm_count_chars(const char *str, size_t len, const int c);
+
+/*
+ * Length of string after escaping double quotes and backslashes.
+ */
+size_t dm_escaped_len(const char *str);
+
+/*
+ * <vg>-<lv>-<layer> or if !layer just <vg>-<lv>.
+ */
+char *dm_build_dm_name(struct dm_pool *mem, const char *vgname,
+ const char *lvname, const char *layer);
+char *dm_build_dm_uuid(struct dm_pool *mem, const char *prefix, const char *lvid, const char *layer);
+
+/*
+ * Copies a string, quoting double quotes with backslashes.
+ */
+char *dm_escape_double_quotes(char *out, const char *src);
+
+/*
+ * Undo quoting in situ.
+ */
+void dm_unescape_double_quotes(char *src);
+
+/*
+ * Unescape colons and "at" signs in situ and save the substrings
+ * starting at the position of the first unescaped colon and the
+ * first unescaped "at" sign. This is normally used to unescape
+ * device names used as PVs.
+ */
+void dm_unescape_colons_and_at_signs(char *src,
+ char **substr_first_unquoted_colon,
+ char **substr_first_unquoted_at_sign);
+
+/*
+ * Replacement for strncpy() function.
+ *
+ * Copies no more than n bytes from string pointed by src to the buffer
+ * pointed by dest and ensure string is finished with '\0'.
+ * Returns 0 if the whole string does not fit.
+ */
+int dm_strncpy(char *dest, const char *src, size_t n);
+
/**************************
* file/stream manipulation
**************************/
@@ -978,6 +1284,8 @@ const char *dm_basename(const char *path);
*/
int dm_create_dir(const char *dir);
+int dm_is_empty_dir(const char *dir);
+
/*
* Close a stream, with nicer error checking than fclose's.
* Derived from gnulib's close-stream.c.
@@ -995,6 +1303,7 @@ int dm_fclose(FILE *stream);
*/
int dm_asprintf(char **buf, const char *format, ...)
__attribute__ ((format(printf, 2, 3)));
+int dm_vasprintf(char **buf, const char *format, va_list ap);
/*
* create lockfile (pidfile) - create and lock a lock file
@@ -1117,7 +1426,7 @@ int dm_report_set_output_field_name_prefix(struct dm_report *rh,
* They take care of allocating copies of the data.
*/
int dm_report_field_string(struct dm_report *rh, struct dm_report_field *field,
- const char **data);
+ const char *const *data);
int dm_report_field_int32(struct dm_report *rh, struct dm_report_field *field,
const int32_t *data);
int dm_report_field_uint32(struct dm_report *rh, struct dm_report_field *field,
@@ -1135,7 +1444,112 @@ int dm_report_field_uint64(struct dm_report *rh, struct dm_report_field *field,
void dm_report_field_set_value(struct dm_report_field *field, const void *value,
const void *sortvalue);
+/*************************
+ * config file parse/print
+ *************************/
+typedef enum {
+ DM_CFG_INT,
+ DM_CFG_FLOAT,
+ DM_CFG_STRING,
+ DM_CFG_EMPTY_ARRAY
+} dm_config_value_type_t;
+
+struct dm_config_value {
+ dm_config_value_type_t type;
+
+ union {
+ int64_t i;
+ float f;
+ double d; /* Unused. */
+ const char *str;
+ } v;
+
+ struct dm_config_value *next; /* For arrays */
+};
+
+struct dm_config_node {
+ const char *key;
+ struct dm_config_node *parent, *sib, *child;
+ struct dm_config_value *v;
+};
+
+struct dm_config_tree {
+ struct dm_config_node *root;
+ struct dm_config_tree *cascade;
+ struct dm_pool *mem;
+ void *custom;
+};
+
+struct dm_config_tree *dm_config_create(void);
+struct dm_config_tree *dm_config_from_string(const char *config_settings);
+int dm_config_parse(struct dm_config_tree *cft, const char *start, const char *end);
+
+void *dm_config_get_custom(struct dm_config_tree *cft);
+void dm_config_set_custom(struct dm_config_tree *cft, void *custom);
+
+/*
+ * When searching, first_cft is checked before second_cft.
+ */
+struct dm_config_tree *dm_config_insert_cascaded_tree(struct dm_config_tree *first_cft, struct dm_config_tree *second_cft);
+
+/*
+ * If there's a cascaded dm_config_tree, remove the top layer
+ * and return the layer below. Otherwise return NULL.
+ */
+struct dm_config_tree *dm_config_remove_cascaded_tree(struct dm_config_tree *cft);
+
+void dm_config_destroy(struct dm_config_tree *cft);
+
+typedef int (*dm_putline_fn)(const char *line, void *baton);
+/* Write the node and any subsequent siblings it has. */
+int dm_config_write_node(const struct dm_config_node *cn, dm_putline_fn putline, void *baton);
+/* Write given node only without subsequent siblings. */
+int dm_config_write_one_node(const struct dm_config_node *cn, dm_putline_fn putline, void *baton);
+
+struct dm_config_node *dm_config_find_node(const struct dm_config_node *cn, const char *path);
+int dm_config_has_node(const struct dm_config_node *cn, const char *path);
+const char *dm_config_find_str(const struct dm_config_node *cn, const char *path, const char *fail);
+const char *dm_config_find_str_allow_empty(const struct dm_config_node *cn, const char *path, const char *fail);
+int dm_config_find_int(const struct dm_config_node *cn, const char *path, int fail);
+int64_t dm_config_find_int64(const struct dm_config_node *cn, const char *path, int64_t fail);
+float dm_config_find_float(const struct dm_config_node *cn, const char *path, float fail);
+
+const struct dm_config_node *dm_config_tree_find_node(const struct dm_config_tree *cft, const char *path);
+const char *dm_config_tree_find_str(const struct dm_config_tree *cft, const char *path, const char *fail);
+const char *dm_config_tree_find_str_allow_empty(const struct dm_config_tree *cft, const char *path, const char *fail);
+int dm_config_tree_find_int(const struct dm_config_tree *cft, const char *path, int fail);
+int64_t dm_config_tree_find_int64(const struct dm_config_tree *cft, const char *path, int64_t fail);
+float dm_config_tree_find_float(const struct dm_config_tree *cft, const char *path, float fail);
+int dm_config_tree_find_bool(const struct dm_config_tree *cft, const char *path, int fail);
+
+/*
+ * Understands (0, ~0), (y, n), (yes, no), (on,
+ * off), (true, false).
+ */
+int dm_config_find_bool(const struct dm_config_node *cn, const char *path, int fail);
+
+int dm_config_get_uint32(const struct dm_config_node *cn, const char *path, uint32_t *result);
+int dm_config_get_uint64(const struct dm_config_node *cn, const char *path, uint64_t *result);
+int dm_config_get_str(const struct dm_config_node *cn, const char *path, const char **result);
+int dm_config_get_list(const struct dm_config_node *cn, const char *path, const struct dm_config_value **result);
+int dm_config_get_section(const struct dm_config_node *cn, const char *path, const struct dm_config_node **result);
+
+unsigned dm_config_maybe_section(const char *str, unsigned len);
+
+const char *dm_config_parent_name(const struct dm_config_node *n);
+
+struct dm_config_node *dm_config_clone_node_with_mem(struct dm_pool *mem, const struct dm_config_node *node, int siblings);
+struct dm_config_node *dm_config_create_node(struct dm_config_tree *cft, const char *key);
+struct dm_config_value *dm_config_create_value(struct dm_config_tree *cft);
+struct dm_config_node *dm_config_clone_node(struct dm_config_tree *cft, const struct dm_config_node *cn, int siblings);
+
+struct dm_pool *dm_config_memory(struct dm_config_tree *cft);
+
+/* Udev device directory. */
+#define DM_UDEV_DEV_DIR "/dev/"
+
/* Cookie prefixes.
+ *
* The cookie value consists of a prefix (16 bits) and a base (16 bits).
* We can use the prefix to store the flags. These flags are sent to
* kernel within given dm task. When returned back to userspace in
@@ -1143,6 +1557,7 @@ void dm_report_field_set_value(struct dm_report_field *field, const void *value,
* of udev rules we use by decoding the cookie prefix. When doing the
* notification, we replace the cookie prefix with DM_COOKIE_MAGIC,
* so we notify the right semaphore.
+ *
* It is still possible to use cookies for passing the flags to udev
* rules even when udev_sync is disabled. The base part of the cookie
* will be zero (there's no notification semaphore) and prefix will be
@@ -1214,11 +1629,17 @@ void dm_udev_set_sync_support(int sync_with_udev);
int dm_udev_get_sync_support(void);
void dm_udev_set_checking(int checking);
int dm_udev_get_checking(void);
+
+/*
+ * Default value to get new auto generated cookie created
+ */
+#define DM_COOKIE_AUTO_CREATE 0
int dm_udev_create_cookie(uint32_t *cookie);
int dm_udev_complete(uint32_t cookie);
int dm_udev_wait(uint32_t cookie);
#define DM_DEV_DIR_UMASK 0022
+#define DM_CONTROL_NODE_UMASK 0177
#ifdef __cplusplus
}
diff --git a/libdm/libdm-common.c b/libdm/libdm-common.c
index 6d8bcbd..b8533ed 100644
--- a/libdm/libdm-common.c
+++ b/libdm/libdm-common.c
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
- * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2004-2012 Red Hat, Inc. All rights reserved.
*
* This file is part of the device-mapper userspace tools.
*
@@ -29,7 +29,6 @@
# include <sys/types.h>
# include <sys/ipc.h>
# include <sys/sem.h>
-# define LIBUDEV_I_KNOW_THE_API_IS_SUBJECT_TO_CHANGE
# include <libudev.h>
#endif
@@ -44,6 +43,8 @@
# include <selinux/label.h>
#endif
+#define DM_DEFAULT_NAME_MANGLING_MODE_ENV_VAR_NAME "DM_DEFAULT_NAME_MANGLING_MODE"
+
#define DEV_DIR "/dev/"
#ifdef UDEV_SYNC_SUPPORT
@@ -59,8 +60,15 @@ union semun
#endif
static char _dm_dir[PATH_MAX] = DEV_DIR DM_DIR;
+static char _sysfs_dir[PATH_MAX] = "/sys/";
+static char _path0[PATH_MAX]; /* path buffer, safe 4kB on stack */
+
+#define DM_MAX_UUID_PREFIX_LEN 15
+static char _default_uuid_prefix[DM_MAX_UUID_PREFIX_LEN + 1] = "LVM-";
static int _verbose = 0;
+static int _suspended_dev_counter = 0;
+static dm_string_mangling_t _name_mangling_mode = DEFAULT_DM_NAME_MANGLING;
#ifdef HAVE_SELINUX_LABEL_H
static struct selabel_handle *_selabel_handle = NULL;
@@ -73,11 +81,28 @@ static int _sync_with_udev = 1;
static int _udev_checking = 1;
#endif
+void dm_lib_init(void)
+{
+ const char *env;
+
+ env = getenv(DM_DEFAULT_NAME_MANGLING_MODE_ENV_VAR_NAME);
+ if (env && *env) {
+ if (!strcasecmp(env, "none"))
+ _name_mangling_mode = DM_STRING_MANGLING_NONE;
+ else if (!strcasecmp(env, "auto"))
+ _name_mangling_mode = DM_STRING_MANGLING_AUTO;
+ else if (!strcasecmp(env, "hex"))
+ _name_mangling_mode = DM_STRING_MANGLING_HEX;
+ } else
+ _name_mangling_mode = DEFAULT_DM_NAME_MANGLING;
+}
+
/*
* Library users can provide their own logging
* function.
*/
+__attribute__((format(printf, 5, 0)))
static void _default_log_line(int level,
const char *file __attribute__((unused)),
int line __attribute__((unused)), int dm_errno,
@@ -101,6 +126,7 @@ static void _default_log_line(int level,
fprintf(use_stderr ? stderr : stdout, "\n");
}
+__attribute__((format(printf, 5, 6)))
static void _default_log_with_errno(int level,
const char *file __attribute__((unused)),
int line __attribute__((unused)), int dm_errno,
@@ -113,6 +139,7 @@ static void _default_log_with_errno(int level,
va_end(ap);
}
+__attribute__((format(printf, 4, 5)))
static void _default_log(int level, const char *file,
int line, const char *f, ...)
{
@@ -171,6 +198,40 @@ int dm_get_library_version(char *version, size_t size)
return 1;
}
+void inc_suspended(void)
+{
+ _suspended_dev_counter++;
+ log_debug("Suspended device counter increased to %d", _suspended_dev_counter);
+}
+
+void dec_suspended(void)
+{
+ if (!_suspended_dev_counter) {
+ log_error("Attempted to decrement suspended device counter below zero.");
+ return;
+ }
+
+ _suspended_dev_counter--;
+ log_debug("Suspended device counter reduced to %d", _suspended_dev_counter);
+}
+
+int dm_get_suspended_counter(void)
+{
+ return _suspended_dev_counter;
+}
+
+int dm_set_name_mangling_mode(dm_string_mangling_t name_mangling_mode)
+{
+ _name_mangling_mode = name_mangling_mode;
+
+ return 1;
+}
+
+dm_string_mangling_t dm_get_name_mangling_mode(void)
+{
+ return _name_mangling_mode;
+}
+
struct dm_task *dm_task_create(int type)
{
struct dm_task *dmt = dm_zalloc(sizeof(*dmt));
@@ -183,7 +244,7 @@ struct dm_task *dm_task_create(int type)
if (!dm_check_version()) {
dm_free(dmt);
- return NULL;
+ return_NULL;
}
dmt->type = type;
@@ -200,6 +261,7 @@ struct dm_task *dm_task_create(int type)
dmt->cookie_set = 0;
dmt->query_inactive_table = 0;
dmt->new_uuid = 0;
+ dmt->secure_data = 0;
return dmt;
}
@@ -207,18 +269,18 @@ struct dm_task *dm_task_create(int type)
/*
* Find the name associated with a given device number by scanning _dm_dir.
*/
-static char *_find_dm_name_of_device(dev_t st_rdev)
+static int _find_dm_name_of_device(dev_t st_rdev, char *buf, size_t buf_len)
{
const char *name;
char path[PATH_MAX];
struct dirent *dirent;
DIR *d;
- struct stat buf;
- char *new_name = NULL;
+ struct stat st;
+ int r = 0;
if (!(d = opendir(_dm_dir))) {
log_sys_error("opendir", _dm_dir);
- return NULL;
+ return 0;
}
while ((dirent = readdir(d))) {
@@ -233,13 +295,12 @@ static char *_find_dm_name_of_device(dev_t st_rdev)
continue;
}
- if (stat(path, &buf))
+ if (stat(path, &st))
continue;
- if (buf.st_rdev == st_rdev) {
- if (!(new_name = dm_strdup(name)))
- log_error("dm_task_set_name: strdup(%s) failed",
- name);
+ if (st.st_rdev == st_rdev) {
+ strncpy(buf, name, buf_len);
+ r = 1;
break;
}
}
@@ -247,73 +308,444 @@ static char *_find_dm_name_of_device(dev_t st_rdev)
if (closedir(d))
log_sys_error("closedir", _dm_dir);
- return new_name;
+ return r;
}
-int dm_task_set_name(struct dm_task *dmt, const char *name)
+static int _is_whitelisted_char(char c)
{
- char *pos;
- char *new_name = NULL;
- char path[PATH_MAX];
- struct stat st1, st2;
-
- dm_free(dmt->dev_name);
- dmt->dev_name = NULL;
-
/*
- * Path supplied for existing device?
+ * Actually, DM supports any character in a device name.
+ * This whitelist is just for proper integration with udev.
*/
- if ((pos = strrchr(name, '/'))) {
- if (dmt->type == DM_DEVICE_CREATE) {
- log_error("Name \"%s\" invalid. It contains \"/\".", name);
- return 0;
+ if ((c >= '0' && c <= '9') ||
+ (c >= 'A' && c <= 'Z') ||
+ (c >= 'a' && c <= 'z') ||
+ strchr("#+-.:=@_", c) != NULL)
+ return 1;
+
+ return 0;
+}
+
+int check_multiple_mangled_string_allowed(const char *str, const char *str_name,
+ dm_string_mangling_t mode)
+{
+ if (mode == DM_STRING_MANGLING_AUTO && strstr(str, "\\x5cx")) {
+ log_error("The %s \"%s\" seems to be mangled more than once. "
+ "This is not allowed in auto mode.", str_name, str);
+ return 0;
+ }
+
+ return 1;
+}
+
+/*
+ * Mangle all characters in the input string which are not on a whitelist
+ * with '\xNN' format where NN is the hex value of the character.
+ */
+int mangle_string(const char *str, const char *str_name, size_t len,
+ char *buf, size_t buf_len, dm_string_mangling_t mode)
+{
+ int need_mangling = -1; /* -1 don't know yet, 0 no, 1 yes */
+ size_t i, j;
+
+ if (!str || !buf)
+ return -1;
+
+ /* Is there anything to do at all? */
+ if (!*str || !len)
+ return 0;
+
+ if (buf_len < DM_NAME_LEN) {
+ log_error(INTERNAL_ERROR "mangle_string: supplied buffer too small");
+ return -1;
+ }
+
+ if (mode == DM_STRING_MANGLING_NONE)
+ mode = DM_STRING_MANGLING_AUTO;
+
+ for (i = 0, j = 0; str[i]; i++) {
+ if (mode == DM_STRING_MANGLING_AUTO) {
+ /*
+ * Detect already mangled part of the string and keep it.
+ * Return error on mixture of mangled/not mangled!
+ */
+ if (str[i] == '\\' && str[i+1] == 'x') {
+ if ((len - i < 4) || (need_mangling == 1))
+ goto bad1;
+ if (buf_len - j < 4)
+ goto bad2;
+
+ memcpy(&buf[j], &str[i], 4);
+ i+=3; j+=4;
+
+ need_mangling = 0;
+ continue;
+ }
}
- if (stat(name, &st1)) {
- log_error("Device %s not found", name);
- return 0;
+ if (_is_whitelisted_char(str[i])) {
+ /* whitelisted, keep it. */
+ if (buf_len - j < 1)
+ goto bad2;
+ buf[j] = str[i];
+ j++;
+ } else {
+ /*
+ * Not on a whitelist, mangle it.
+ * Return error on mixture of mangled/not mangled
+ * unless a DM_STRING_MANGLING_HEX is used!.
+ */
+ if ((mode != DM_STRING_MANGLING_HEX) && (need_mangling == 0))
+ goto bad1;
+ if (buf_len - j < 4)
+ goto bad2;
+
+ sprintf(&buf[j], "\\x%02x", (unsigned char) str[i]);
+ j+=4;
+
+ need_mangling = 1;
}
+ }
- /*
- * If supplied path points to same device as last component
- * under /dev/mapper, use that name directly. Otherwise call
- * _find_dm_name_of_device() to scan _dm_dir for a match.
- */
- if (dm_snprintf(path, sizeof(path), "%s/%s", _dm_dir,
- pos + 1) == -1) {
- log_error("Couldn't create path for %s", pos + 1);
- return 0;
+ if (buf_len - j < 1)
+ goto bad2;
+ buf[j] = '\0';
+
+ /* All chars in the string whitelisted? */
+ if (need_mangling == -1)
+ need_mangling = 0;
+
+ return need_mangling;
+
+bad1:
+ log_error("The %s \"%s\" contains mixed mangled and unmangled "
+ "characters or it's already mangled improperly.", str_name, str);
+ return -1;
+bad2:
+ log_error("Mangled form of the %s too long for \"%s\".", str_name, str);
+ return -1;
+}
+
+/*
+ * Try to unmangle supplied string.
+ * Return value: -1 on error, 0 when no unmangling needed, 1 when unmangling applied
+ */
+int unmangle_string(const char *str, const char *str_name, size_t len,
+ char *buf, size_t buf_len, dm_string_mangling_t mode)
+{
+ int strict = mode != DM_STRING_MANGLING_NONE;
+ char str_rest[DM_NAME_LEN];
+ size_t i, j;
+ int code;
+ int r = 0;
+
+ if (!str || !buf)
+ return -1;
+
+ /* Is there anything to do at all? */
+ if (!*str || !len)
+ return 0;
+
+ if (buf_len < DM_NAME_LEN) {
+ log_error(INTERNAL_ERROR "unmangle_string: supplied buffer too small");
+ return -1;
+ }
+
+ for (i = 0, j = 0; str[i]; i++, j++) {
+ if (strict && !(_is_whitelisted_char(str[i]) || str[i]=='\\')) {
+ log_error("The %s \"%s\" should be mangled but "
+ "it contains blacklisted characters.", str_name, str);
+ j=0; r=-1;
+ goto out;
}
- if (!stat(path, &st2) && (st1.st_rdev == st2.st_rdev))
- name = pos + 1;
- else if ((new_name = _find_dm_name_of_device(st1.st_rdev)))
- name = new_name;
- else {
- log_error("Device %s not found", name);
+ if (str[i] == '\\' && str[i+1] == 'x') {
+ if (!sscanf(&str[i+2], "%2x%s", &code, str_rest)) {
+ log_debug("Hex encoding mismatch detected in %s \"%s\" "
+ "while trying to unmangle it.", str_name, str);
+ goto out;
+ }
+ buf[j] = (unsigned char) code;
+
+ /* skip the encoded part we've just decoded! */
+ i+= 3;
+
+ /* unmangling applied */
+ r = 1;
+ } else
+ buf[j] = str[i];
+ }
+
+out:
+ buf[j] = '\0';
+ return r;
+}
+
+static int _dm_task_set_name(struct dm_task *dmt, const char *name,
+ dm_string_mangling_t mangling_mode)
+{
+ char mangled_name[DM_NAME_LEN];
+ int r = 0;
+
+ dm_free(dmt->dev_name);
+ dmt->dev_name = NULL;
+ dm_free(dmt->mangled_dev_name);
+ dmt->mangled_dev_name = NULL;
+
+ if (strlen(name) >= DM_NAME_LEN) {
+ log_error("Name \"%s\" too long.", name);
+ return 0;
+ }
+
+ if (!check_multiple_mangled_string_allowed(name, "name", mangling_mode))
+ return_0;
+
+ if (mangling_mode != DM_STRING_MANGLING_NONE &&
+ (r = mangle_string(name, "name", strlen(name), mangled_name,
+ sizeof(mangled_name), mangling_mode)) < 0) {
+ log_error("Failed to mangle device name \"%s\".", name);
+ return 0;
+ }
+
+ /* Store mangled_dev_name only if it differs from dev_name! */
+ if (r) {
+ log_debug("Device name mangled [%s]: %s --> %s",
+ mangling_mode == DM_STRING_MANGLING_AUTO ? "auto" : "hex",
+ name, mangled_name);
+ if (!(dmt->mangled_dev_name = dm_strdup(mangled_name))) {
+ log_error("_dm_task_set_name: dm_strdup(%s) failed", mangled_name);
return 0;
}
}
- if (strlen(name) >= DM_NAME_LEN) {
- log_error("Name \"%s\" too long", name);
- dm_free(new_name);
+ if (!(dmt->dev_name = dm_strdup(name))) {
+ log_error("_dm_task_set_name: strdup(%s) failed", name);
+ return 0;
+ }
+
+ return 1;
+}
+
+static int _dm_task_set_name_from_path(struct dm_task *dmt, const char *path,
+ const char *name)
+{
+ char buf[PATH_MAX];
+ struct stat st1, st2;
+ const char *final_name;
+
+ if (dmt->type == DM_DEVICE_CREATE) {
+ log_error("Name \"%s\" invalid. It contains \"/\".", path);
return 0;
}
- if (new_name)
- dmt->dev_name = new_name;
- else if (!(dmt->dev_name = dm_strdup(name))) {
- log_error("dm_task_set_name: strdup(%s) failed", name);
+ if (stat(path, &st1)) {
+ log_error("Device %s not found", path);
return 0;
}
+ /*
+ * If supplied path points to same device as last component
+ * under /dev/mapper, use that name directly. Otherwise call
+ * _find_dm_name_of_device() to scan _dm_dir for a match.
+ */
+ if (dm_snprintf(buf, sizeof(buf), "%s/%s", _dm_dir, name) == -1) {
+ log_error("Couldn't create path for %s", name);
+ return 0;
+ }
+
+ if (!stat(buf, &st2) && (st1.st_rdev == st2.st_rdev))
+ final_name = name;
+ else if (_find_dm_name_of_device(st1.st_rdev, buf, sizeof(buf)))
+ final_name = buf;
+ else {
+ log_error("Device %s not found", name);
+ return 0;
+ }
+
+ /* This is an already existing path - do not mangle! */
+ return _dm_task_set_name(dmt, final_name, DM_STRING_MANGLING_NONE);
+}
+
+int dm_task_set_name(struct dm_task *dmt, const char *name)
+{
+ char *pos;
+
+ /* Path supplied for existing device? */
+ if ((pos = strrchr(name, '/')))
+ return _dm_task_set_name_from_path(dmt, name, pos + 1);
+
+ return _dm_task_set_name(dmt, name, dm_get_name_mangling_mode());
+}
+
+const char *dm_task_get_name(const struct dm_task *dmt)
+{
+ return (dmt->dmi.v4->name);
+}
+
+static char *_task_get_string_mangled(const char *str, const char *str_name,
+ char *buf, size_t buf_size,
+ dm_string_mangling_t mode)
+{
+ char *rs;
+ int r;
+
+ if ((r = mangle_string(str, str_name, strlen(str), buf, buf_size, mode)) < 0)
+ return NULL;
+
+ if (!(rs = r ? dm_strdup(buf) : dm_strdup(str)))
+ log_error("_task_get_string_mangled: dm_strdup failed");
+
+ return rs;
+}
+
+static char *_task_get_string_unmangled(const char *str, const char *str_name,
+ char *buf, size_t buf_size,
+ dm_string_mangling_t mode)
+{
+ char *rs;
+ int r = 0;
+
+ /*
+ * Unless the mode used is 'none', the string
+ * is *already* unmangled on ioctl return!
+ */
+ if (mode == DM_STRING_MANGLING_NONE &&
+ (r = unmangle_string(str, str_name, strlen(str), buf, buf_size, mode)) < 0)
+ return NULL;
+
+ if (!(rs = r ? dm_strdup(buf) : dm_strdup(str)))
+ log_error("_task_get_string_unmangled: dm_strdup failed");
+
+ return rs;
+}
+
+char *dm_task_get_name_mangled(const struct dm_task *dmt)
+{
+ const char *s = dm_task_get_name(dmt);
+ char buf[DM_NAME_LEN];
+ char *rs;
+
+ if (!(rs = _task_get_string_mangled(s, "name", buf, sizeof(buf), dm_get_name_mangling_mode())))
+ log_error("Failed to mangle device name \"%s\".", s);
+
+ return rs;
+}
+
+char *dm_task_get_name_unmangled(const struct dm_task *dmt)
+{
+ const char *s = dm_task_get_name(dmt);
+ char buf[DM_NAME_LEN];
+ char *rs;
+
+ if (!(rs = _task_get_string_unmangled(s, "name", buf, sizeof(buf), dm_get_name_mangling_mode())))
+ log_error("Failed to unmangle device name \"%s\".", s);
+
+ return rs;
+}
+
+const char *dm_task_get_uuid(const struct dm_task *dmt)
+{
+ return (dmt->dmi.v4->uuid);
+}
+
+char *dm_task_get_uuid_mangled(const struct dm_task *dmt)
+{
+ const char *s = dm_task_get_uuid(dmt);
+ char buf[DM_UUID_LEN];
+ char *rs;
+
+ if (!(rs = _task_get_string_mangled(s, "UUID", buf, sizeof(buf), dm_get_name_mangling_mode())))
+ log_error("Failed to mangle device uuid \"%s\".", s);
+
+ return rs;
+}
+
+char *dm_task_get_uuid_unmangled(const struct dm_task *dmt)
+{
+ const char *s = dm_task_get_uuid(dmt);
+ char buf[DM_UUID_LEN];
+ char *rs;
+
+ if (!(rs = _task_get_string_unmangled(s, "UUID", buf, sizeof(buf), dm_get_name_mangling_mode())))
+ log_error("Failed to unmangle device uuid \"%s\".", s);
+
+ return rs;
+}
+
+int dm_task_set_newname(struct dm_task *dmt, const char *newname)
+{
+ dm_string_mangling_t mangling_mode = dm_get_name_mangling_mode();
+ char mangled_name[DM_NAME_LEN];
+ int r = 0;
+
+ if (strchr(newname, '/')) {
+ log_error("Name \"%s\" invalid. It contains \"/\".", newname);
+ return 0;
+ }
+
+ if (strlen(newname) >= DM_NAME_LEN) {
+ log_error("Name \"%s\" too long", newname);
+ return 0;
+ }
+
+ if (!check_multiple_mangled_string_allowed(newname, "new name", mangling_mode))
+ return_0;
+
+ if (mangling_mode != DM_STRING_MANGLING_NONE &&
+ (r = mangle_string(newname, "new name", strlen(newname), mangled_name,
+ sizeof(mangled_name), mangling_mode)) < 0) {
+ log_error("Failed to mangle new device name \"%s\"", newname);
+ return 0;
+ }
+
+ if (r) {
+ log_debug("New device name mangled [%s]: %s --> %s",
+ mangling_mode == DM_STRING_MANGLING_AUTO ? "auto" : "hex",
+ newname, mangled_name);
+ newname = mangled_name;
+ }
+
+ if (!(dmt->newname = dm_strdup(newname))) {
+ log_error("dm_task_set_newname: strdup(%s) failed", newname);
+ return 0;
+ }
+
+ dmt->new_uuid = 0;
+
return 1;
}
int dm_task_set_uuid(struct dm_task *dmt, const char *uuid)
{
+ char mangled_uuid[DM_UUID_LEN];
+ dm_string_mangling_t mangling_mode = dm_get_name_mangling_mode();
+ int r = 0;
+
dm_free(dmt->uuid);
+ dmt->uuid = NULL;
+ dm_free(dmt->mangled_uuid);
+ dmt->mangled_uuid = NULL;
+
+ if (!check_multiple_mangled_string_allowed(uuid, "UUID", mangling_mode))
+ return_0;
+
+ if (mangling_mode != DM_STRING_MANGLING_NONE &&
+ (r = mangle_string(uuid, "UUID", strlen(uuid), mangled_uuid,
+ sizeof(mangled_uuid), mangling_mode)) < 0) {
+ log_error("Failed to mangle device uuid \"%s\".", uuid);
+ return 0;
+ }
+
+ if (r) {
+ log_debug("Device uuid mangled [%s]: %s --> %s",
+ mangling_mode == DM_STRING_MANGLING_AUTO ? "auto" : "hex",
+ uuid, mangled_uuid);
+
+ if (!(dmt->mangled_uuid = dm_strdup(mangled_uuid))) {
+ log_error("dm_task_set_uuid: dm_strdup(%s) failed", mangled_uuid);
+ return 0;
+ }
+ }
if (!(dmt->uuid = dm_strdup(uuid))) {
log_error("dm_task_set_uuid: strdup(%s) failed", uuid);
@@ -369,13 +801,19 @@ int dm_task_set_mode(struct dm_task *dmt, mode_t mode)
return 1;
}
+int dm_task_enable_checks(struct dm_task *dmt)
+{
+ dmt->enable_checks = 1;
+
+ return 1;
+}
+
int dm_task_add_target(struct dm_task *dmt, uint64_t start, uint64_t size,
const char *ttype, const char *params)
{
struct target *t = create_target(start, size, ttype, params);
-
if (!t)
- return 0;
+ return_0;
if (!dmt->head)
dmt->head = dmt->tail = t;
@@ -399,12 +837,14 @@ static int _selabel_lookup(const char *path, mode_t mode,
}
if (selabel_lookup(_selabel_handle, scontext, path, mode)) {
- log_error("selabel_lookup failed: %s", strerror(errno));
+ log_debug("selabel_lookup failed for %s: %s",
+ path, strerror(errno));
return 0;
}
#else
if (matchpathcon(path, mode, scontext)) {
- log_error("matchpathcon failed: %s", strerror(errno));
+ log_debug("matchpathcon failed for %s: %s",
+ path, strerror(errno));
return 0;
}
#endif
@@ -473,12 +913,17 @@ void selinux_release(void)
#endif
}
+static int _warn_if_op_needed(int warn_if_udev_failed)
+{
+ return warn_if_udev_failed && dm_udev_get_sync_support() && dm_udev_get_checking();
+}
+
static int _add_dev_node(const char *dev_name, uint32_t major, uint32_t minor,
- uid_t uid, gid_t gid, mode_t mode, int check_udev)
+ uid_t uid, gid_t gid, mode_t mode, int warn_if_udev_failed)
{
char path[PATH_MAX];
struct stat info;
- dev_t dev = MKDEV(major, minor);
+ dev_t dev = MKDEV((dev_t)major, minor);
mode_t old_mask;
_build_dev_path(path, sizeof(path), dev_name);
@@ -499,15 +944,14 @@ static int _add_dev_node(const char *dev_name, uint32_t major, uint32_t minor,
dev_name);
return 0;
}
- } else if (dm_udev_get_sync_support() && dm_udev_get_checking() &&
- check_udev)
+ } else if (_warn_if_op_needed(warn_if_udev_failed))
log_warn("%s not set up by udev: Falling back to direct "
"node creation.", path);
(void) dm_prepare_selinux_context(path, S_IFBLK);
old_mask = umask(0);
if (mknod(path, S_IFBLK | mode, dev) < 0) {
- log_error("Unable to make device node for '%s'", dev_name);
+ log_error("%s: mknod for %s failed: %s", path, dev_name, strerror(errno));
umask(old_mask);
(void) dm_prepare_selinux_context(NULL, 0);
return 0;
@@ -525,7 +969,7 @@ static int _add_dev_node(const char *dev_name, uint32_t major, uint32_t minor,
return 1;
}
-static int _rm_dev_node(const char *dev_name, int check_udev)
+static int _rm_dev_node(const char *dev_name, int warn_if_udev_failed)
{
char path[PATH_MAX];
struct stat info;
@@ -534,8 +978,7 @@ static int _rm_dev_node(const char *dev_name, int check_udev)
if (stat(path, &info) < 0)
return 1;
- else if (dm_udev_get_sync_support() && dm_udev_get_checking() &&
- check_udev)
+ else if (_warn_if_op_needed(warn_if_udev_failed))
log_warn("Node %s was not removed by udev. "
"Falling back to direct node removal.", path);
@@ -550,7 +993,7 @@ static int _rm_dev_node(const char *dev_name, int check_udev)
}
static int _rename_dev_node(const char *old_name, const char *new_name,
- int check_udev)
+ int warn_if_udev_failed)
{
char oldpath[PATH_MAX];
char newpath[PATH_MAX];
@@ -565,8 +1008,7 @@ static int _rename_dev_node(const char *old_name, const char *new_name,
"is already present", newpath);
return 0;
}
- else if (dm_udev_get_sync_support() && dm_udev_get_checking() &&
- check_udev) {
+ else if (_warn_if_op_needed(warn_if_udev_failed)) {
if (stat(oldpath, &info) < 0 &&
errno == ENOENT)
/* assume udev already deleted this */
@@ -590,8 +1032,7 @@ static int _rename_dev_node(const char *old_name, const char *new_name,
return 0;
}
}
- else if (dm_udev_get_sync_support() && dm_udev_get_checking() &&
- check_udev)
+ else if (_warn_if_op_needed(warn_if_udev_failed))
log_warn("The node %s should have been renamed to %s "
"by udev but new node is not present. "
"Falling back to direct node rename.",
@@ -622,12 +1063,53 @@ static int _open_dev_node(const char *dev_name)
return fd;
}
-int get_dev_node_read_ahead(const char *dev_name, uint32_t *read_ahead)
+int get_dev_node_read_ahead(const char *dev_name, uint32_t major, uint32_t minor,
+ uint32_t *read_ahead)
{
+ char buf[24];
+ int len;
int r = 1;
int fd;
long read_ahead_long;
+ /*
+ * If we know the device number, use sysfs if we can.
+ * Otherwise use BLKRAGET ioctl.
+ */
+ if (*_sysfs_dir && major != 0) {
+ if (dm_snprintf(_path0, sizeof(_path0), "%sdev/block/%" PRIu32
+ ":%" PRIu32 "/bdi/read_ahead_kb", _sysfs_dir,
+ major, minor) < 0) {
+ log_error("Failed to build sysfs_path.");
+ return 0;
+ }
+
+ if ((fd = open(_path0, O_RDONLY, 0)) != -1) {
+ /* Reading from sysfs, expecting number\n */
+ if ((len = read(fd, buf, sizeof(buf) - 1)) < 1) {
+ log_sys_error("read", _path0);
+ r = 0;
+ } else {
+ buf[len] = 0; /* kill \n and ensure \0 */
+ *read_ahead = atoi(buf) * 2;
+ log_debug("%s (%d:%d): read ahead is %" PRIu32,
+ dev_name, major, minor, *read_ahead);
+ }
+
+ if (close(fd))
+ log_sys_debug("close", _path0);
+
+ return r;
+ }
+
+ log_sys_debug("open", _path0);
+ /* Fall back to use dev_name */
+ }
+
+ /*
+ * Open/close dev_name may block the process
+ * (i.e. overfilled thin pool volume)
+ */
if (!*dev_name) {
log_error("Empty device name passed to BLKRAGET");
return 0;
@@ -640,23 +1122,64 @@ int get_dev_node_read_ahead(const char *dev_name, uint32_t *read_ahead)
log_sys_error("BLKRAGET", dev_name);
*read_ahead = 0;
r = 0;
- } else {
+ } else {
*read_ahead = (uint32_t) read_ahead_long;
log_debug("%s: read ahead is %" PRIu32, dev_name, *read_ahead);
}
if (close(fd))
- stack;
+ log_sys_debug("close", dev_name);
return r;
}
-static int _set_read_ahead(const char *dev_name, uint32_t read_ahead)
+static int _set_read_ahead(const char *dev_name, uint32_t major, uint32_t minor,
+ uint32_t read_ahead)
{
+ char buf[24];
+ int len;
int r = 1;
int fd;
long read_ahead_long = (long) read_ahead;
+ log_debug("%s (%d:%d): Setting read ahead to %" PRIu32, dev_name,
+ major, minor, read_ahead);
+
+ /*
+ * If we know the device number, use sysfs if we can.
+ * Otherwise use BLKRASET ioctl. RA is set after resume.
+ */
+ if (*_sysfs_dir && major != 0) {
+ if (dm_snprintf(_path0, sizeof(_path0), "%sdev/block/%" PRIu32
+ ":%" PRIu32 "/bdi/read_ahead_kb",
+ _sysfs_dir, major, minor) < 0) {
+ log_error("Failed to build sysfs_path.");
+ return 0;
+ }
+
+ /* Sysfs is kB based, round up to kB */
+ if ((len = dm_snprintf(buf, sizeof(buf), "%" PRIu32,
+ (read_ahead + 1) / 2)) < 0) {
+ log_error("Failed to build size in kB.");
+ return 0;
+ }
+
+ if ((fd = open(_path0, O_WRONLY, 0)) != -1) {
+ if (write(fd, buf, len) < len) {
+ log_sys_error("write", _path0);
+ r = 0;
+ }
+
+ if (close(fd))
+ log_sys_debug("close", _path0);
+
+ return r;
+ }
+
+ log_sys_debug("open", _path0);
+ /* Fall back to use dev_name */
+ }
+
if (!*dev_name) {
log_error("Empty device name passed to BLKRAGET");
return 0;
@@ -665,21 +1188,20 @@ static int _set_read_ahead(const char *dev_name, uint32_t read_ahead)
if ((fd = _open_dev_node(dev_name)) < 0)
return_0;
- log_debug("%s: Setting read ahead to %" PRIu32, dev_name, read_ahead);
-
if (ioctl(fd, BLKRASET, read_ahead_long)) {
log_sys_error("BLKRASET", dev_name);
r = 0;
}
if (close(fd))
- stack;
+ log_sys_debug("close", dev_name);
return r;
}
-static int _set_dev_node_read_ahead(const char *dev_name, uint32_t read_ahead,
- uint32_t read_ahead_flags)
+static int _set_dev_node_read_ahead(const char *dev_name,
+ uint32_t major, uint32_t minor,
+ uint32_t read_ahead, uint32_t read_ahead_flags)
{
uint32_t current_read_ahead;
@@ -690,7 +1212,7 @@ static int _set_dev_node_read_ahead(const char *dev_name, uint32_t read_ahead,
read_ahead = 0;
if (read_ahead_flags & DM_READ_AHEAD_MINIMUM_FLAG) {
- if (!get_dev_node_read_ahead(dev_name, &current_read_ahead))
+ if (!get_dev_node_read_ahead(dev_name, major, minor, &current_read_ahead))
return_0;
if (current_read_ahead > read_ahead) {
@@ -701,7 +1223,7 @@ static int _set_dev_node_read_ahead(const char *dev_name, uint32_t read_ahead,
}
}
- return _set_read_ahead(dev_name, read_ahead);
+ return _set_read_ahead(dev_name, major, minor, read_ahead);
}
#else
@@ -713,8 +1235,9 @@ int get_dev_node_read_ahead(const char *dev_name, uint32_t *read_ahead)
return 1;
}
-static int _set_dev_node_read_ahead(const char *dev_name, uint32_t read_ahead,
- uint32_t read_ahead_flags)
+static int _set_dev_node_read_ahead(const char *dev_name,
+ uint32_t major, uint32_t minor,
+ uint32_t read_ahead, uint32_t read_ahead_flags)
{
return 1;
}
@@ -724,31 +1247,35 @@ typedef enum {
NODE_ADD,
NODE_DEL,
NODE_RENAME,
- NODE_READ_AHEAD
+ NODE_READ_AHEAD,
+ NUM_NODES
} node_op_t;
static int _do_node_op(node_op_t type, const char *dev_name, uint32_t major,
uint32_t minor, uid_t uid, gid_t gid, mode_t mode,
const char *old_name, uint32_t read_ahead,
- uint32_t read_ahead_flags, int check_udev)
+ uint32_t read_ahead_flags, int warn_if_udev_failed)
{
switch (type) {
case NODE_ADD:
return _add_dev_node(dev_name, major, minor, uid, gid,
- mode, check_udev);
+ mode, warn_if_udev_failed);
case NODE_DEL:
- return _rm_dev_node(dev_name, check_udev);
+ return _rm_dev_node(dev_name, warn_if_udev_failed);
case NODE_RENAME:
- return _rename_dev_node(old_name, dev_name, check_udev);
+ return _rename_dev_node(old_name, dev_name, warn_if_udev_failed);
case NODE_READ_AHEAD:
- return _set_dev_node_read_ahead(dev_name, read_ahead,
- read_ahead_flags);
+ return _set_dev_node_read_ahead(dev_name, major, minor,
+ read_ahead, read_ahead_flags);
+ default:
+ ; /* NOTREACHED */
}
return 1;
}
static DM_LIST_INIT(_node_ops);
+static int _count_node_ops[NUM_NODES];
struct node_op_parms {
struct dm_list list;
@@ -762,7 +1289,8 @@ struct node_op_parms {
uint32_t read_ahead;
uint32_t read_ahead_flags;
char *old_name;
- int check_udev;
+ int warn_if_udev_failed;
+ unsigned rely_on_udev;
char names[0];
};
@@ -773,10 +1301,56 @@ static void _store_str(char **pos, char **ptr, const char *str)
*pos += strlen(*ptr) + 1;
}
+static void _del_node_op(struct node_op_parms *nop)
+{
+ _count_node_ops[nop->type]--;
+ dm_list_del(&nop->list);
+ dm_free(nop);
+
+}
+
+/* Check if there is other the type of node operation stacked */
+static int _other_node_ops(node_op_t type)
+{
+ unsigned i;
+
+ for (i = 0; i < NUM_NODES; i++)
+ if (type != i && _count_node_ops[i])
+ return 1;
+ return 0;
+}
+
+static void _log_node_op(const char *action_str, struct node_op_parms *nop)
+{
+ const char *rely = nop->rely_on_udev ? " [trust_udev]" : "" ;
+ const char *verify = nop->warn_if_udev_failed ? " [verify_udev]" : "";
+
+ switch (nop->type) {
+ case NODE_ADD:
+ log_debug("%s: %s NODE_ADD (%" PRIu32 ",%" PRIu32 ") %u:%u 0%o%s%s",
+ nop->dev_name, action_str, nop->major, nop->minor, nop->uid, nop->gid, nop->mode,
+ rely, verify);
+ break;
+ case NODE_DEL:
+ log_debug("%s: %s NODE_DEL%s%s", nop->dev_name, action_str, rely, verify);
+ break;
+ case NODE_RENAME:
+ log_debug("%s: %s NODE_RENAME to %s%s%s", nop->old_name, action_str, nop->dev_name, rely, verify);
+ break;
+ case NODE_READ_AHEAD:
+ log_debug("%s: %s NODE_READ_AHEAD %" PRIu32 " (flags=%" PRIu32 ")%s%s",
+ nop->dev_name, action_str, nop->read_ahead, nop->read_ahead_flags, rely, verify);
+ break;
+ default:
+ ; /* NOTREACHED */
+ }
+}
+
static int _stack_node_op(node_op_t type, const char *dev_name, uint32_t major,
uint32_t minor, uid_t uid, gid_t gid, mode_t mode,
const char *old_name, uint32_t read_ahead,
- uint32_t read_ahead_flags, int check_udev)
+ uint32_t read_ahead_flags, int warn_if_udev_failed,
+ unsigned rely_on_udev)
{
struct node_op_parms *nop;
struct dm_list *noph, *nopht;
@@ -784,16 +1358,55 @@ static int _stack_node_op(node_op_t type, const char *dev_name, uint32_t major,
char *pos;
/*
- * Ignore any outstanding operations on the node if deleting it
+ * Note: warn_if_udev_failed must have valid content
*/
- if (type == NODE_DEL) {
+ if ((type == NODE_DEL) && _other_node_ops(type))
+ /*
+ * Ignore any outstanding operations on the node if deleting it.
+ */
dm_list_iterate_safe(noph, nopht, &_node_ops) {
nop = dm_list_item(noph, struct node_op_parms);
if (!strcmp(dev_name, nop->dev_name)) {
- dm_list_del(&nop->list);
- dm_free(nop);
+ _log_node_op("Unstacking", nop);
+ _del_node_op(nop);
+ if (!_other_node_ops(type))
+ break; /* no other non DEL ops */
+ }
+ }
+ else if ((type == NODE_ADD) && _count_node_ops[NODE_DEL])
+ /*
+ * Ignore previous DEL operation on added node.
+ * (No other operations for this device then DEL could be stacked here).
+ */
+ dm_list_iterate_safe(noph, nopht, &_node_ops) {
+ nop = dm_list_item(noph, struct node_op_parms);
+ if ((nop->type == NODE_DEL) &&
+ !strcmp(dev_name, nop->dev_name)) {
+ _log_node_op("Unstacking", nop);
+ _del_node_op(nop);
+ break; /* no other DEL ops */
}
}
+ else if (type == NODE_RENAME)
+ /*
+ * Ignore any outstanding operations if renaming it.
+ *
+ * Currently RENAME operation happens through 'suspend -> resume'.
+ * On 'resume' device is added with read_ahead settings, so it is
+ * safe to remove any stacked ADD, RENAME, READ_AHEAD operation
+ * There cannot be any DEL operation on the renamed device.
+ */
+ dm_list_iterate_safe(noph, nopht, &_node_ops) {
+ nop = dm_list_item(noph, struct node_op_parms);
+ if (!strcmp(old_name, nop->dev_name)) {
+ _log_node_op("Unstacking", nop);
+ _del_node_op(nop);
+ }
+ }
+ else if (type == NODE_READ_AHEAD) {
+ /* udev doesn't process readahead */
+ rely_on_udev = 0;
+ warn_if_udev_failed = 0;
}
if (!(nop = dm_malloc(sizeof(*nop) + len))) {
@@ -810,13 +1423,22 @@ static int _stack_node_op(node_op_t type, const char *dev_name, uint32_t major,
nop->mode = mode;
nop->read_ahead = read_ahead;
nop->read_ahead_flags = read_ahead_flags;
- nop->check_udev = check_udev;
+ nop->rely_on_udev = rely_on_udev;
+
+ /*
+ * Clear warn_if_udev_failed if rely_on_udev is set. It doesn't get
+ * checked in this case - this just removes the flag from log messages.
+ */
+ nop->warn_if_udev_failed = rely_on_udev ? 0 : warn_if_udev_failed;
_store_str(&pos, &nop->dev_name, dev_name);
_store_str(&pos, &nop->old_name, old_name);
+ _count_node_ops[type]++;
dm_list_add(&_node_ops, &nop->list);
+ _log_node_op("Stacking", nop);
+
return 1;
}
@@ -827,52 +1449,46 @@ static void _pop_node_ops(void)
dm_list_iterate_safe(noph, nopht, &_node_ops) {
nop = dm_list_item(noph, struct node_op_parms);
- _do_node_op(nop->type, nop->dev_name, nop->major, nop->minor,
- nop->uid, nop->gid, nop->mode, nop->old_name,
- nop->read_ahead, nop->read_ahead_flags,
- nop->check_udev);
- dm_list_del(&nop->list);
- dm_free(nop);
+ if (!nop->rely_on_udev) {
+ _log_node_op("Processing", nop);
+ _do_node_op(nop->type, nop->dev_name, nop->major, nop->minor,
+ nop->uid, nop->gid, nop->mode, nop->old_name,
+ nop->read_ahead, nop->read_ahead_flags,
+ nop->warn_if_udev_failed);
+ } else
+ _log_node_op("Skipping", nop);
+ _del_node_op(nop);
}
}
int add_dev_node(const char *dev_name, uint32_t major, uint32_t minor,
- uid_t uid, gid_t gid, mode_t mode, int check_udev)
+ uid_t uid, gid_t gid, mode_t mode, int check_udev, unsigned rely_on_udev)
{
- log_debug("%s: Stacking NODE_ADD (%" PRIu32 ",%" PRIu32 ") %u:%u 0%o",
- dev_name, major, minor, uid, gid, mode);
-
return _stack_node_op(NODE_ADD, dev_name, major, minor, uid,
- gid, mode, "", 0, 0, check_udev);
+ gid, mode, "", 0, 0, check_udev, rely_on_udev);
}
-int rename_dev_node(const char *old_name, const char *new_name, int check_udev)
+int rename_dev_node(const char *old_name, const char *new_name, int check_udev, unsigned rely_on_udev)
{
- log_debug("%s: Stacking NODE_RENAME to %s", old_name, new_name);
-
return _stack_node_op(NODE_RENAME, new_name, 0, 0, 0,
- 0, 0, old_name, 0, 0, check_udev);
+ 0, 0, old_name, 0, 0, check_udev, rely_on_udev);
}
-int rm_dev_node(const char *dev_name, int check_udev)
+int rm_dev_node(const char *dev_name, int check_udev, unsigned rely_on_udev)
{
- log_debug("%s: Stacking NODE_DEL (replaces other stacked ops)", dev_name);
-
return _stack_node_op(NODE_DEL, dev_name, 0, 0, 0,
- 0, 0, "", 0, 0, check_udev);
+ 0, 0, "", 0, 0, check_udev, rely_on_udev);
}
-int set_dev_node_read_ahead(const char *dev_name, uint32_t read_ahead,
- uint32_t read_ahead_flags)
+int set_dev_node_read_ahead(const char *dev_name,
+ uint32_t major, uint32_t minor,
+ uint32_t read_ahead, uint32_t read_ahead_flags)
{
if (read_ahead == DM_READ_AHEAD_AUTO)
return 1;
- log_debug("%s: Stacking NODE_READ_AHEAD %" PRIu32 " (flags=%" PRIu32
- ")", dev_name, read_ahead, read_ahead_flags);
-
- return _stack_node_op(NODE_READ_AHEAD, dev_name, 0, 0, 0, 0,
- 0, "", read_ahead, read_ahead_flags, 0);
+ return _stack_node_op(NODE_READ_AHEAD, dev_name, major, minor, 0, 0,
+ 0, "", read_ahead, read_ahead_flags, 0, 0);
}
void update_devs(void)
@@ -880,33 +1496,282 @@ void update_devs(void)
_pop_node_ops();
}
-int dm_set_dev_dir(const char *dev_dir)
+static int _canonicalize_and_set_dir(const char *src, const char *suffix, size_t max_len, char *dir)
{
size_t len;
const char *slash;
- if (*dev_dir != '/') {
- log_debug("Invalid dev_dir value, %s: "
- "not an absolute name.", dev_dir);
+
+ if (*src != '/') {
+ log_debug("Invalid directory value, %s: "
+ "not an absolute name.", src);
return 0;
}
- len = strlen(dev_dir);
- slash = dev_dir[len-1] == '/' ? "" : "/";
+ len = strlen(src);
+ slash = src[len-1] == '/' ? "" : "/";
- if (snprintf(_dm_dir, sizeof _dm_dir, "%s%s%s", dev_dir, slash, DM_DIR)
- >= sizeof _dm_dir) {
- log_debug("Invalid dev_dir value, %s: name too long.", dev_dir);
+ if (dm_snprintf(dir, max_len, "%s%s%s", src, slash, suffix ? suffix : "") < 0) {
+ log_debug("Invalid directory value, %s: name too long.", src);
return 0;
}
return 1;
}
+int dm_set_dev_dir(const char *dev_dir)
+{
+ return _canonicalize_and_set_dir(dev_dir, DM_DIR, sizeof _dm_dir, _dm_dir);
+}
+
const char *dm_dir(void)
{
return _dm_dir;
}
+int dm_set_sysfs_dir(const char *sysfs_dir)
+{
+ if (!sysfs_dir || !*sysfs_dir) {
+ _sysfs_dir[0] = '\0';
+ return 1;
+ }
+ else
+ return _canonicalize_and_set_dir(sysfs_dir, NULL, sizeof _sysfs_dir, _sysfs_dir);
+}
+
+const char *dm_sysfs_dir(void)
+{
+ return _sysfs_dir;
+}
+
+/*
+ * Replace existing uuid_prefix provided it isn't too long.
+ */
+int dm_set_uuid_prefix(const char *uuid_prefix)
+{
+ if (!uuid_prefix)
+ return_0;
+
+ if (strlen(uuid_prefix) > DM_MAX_UUID_PREFIX_LEN) {
+ log_error("New uuid prefix %s too long.", uuid_prefix);
+ return 0;
+ }
+
+ strcpy(_default_uuid_prefix, uuid_prefix);
+
+ return 1;
+}
+
+const char *dm_uuid_prefix(void)
+{
+ return _default_uuid_prefix;
+}
+
+static int _sysfs_get_dm_name(uint32_t major, uint32_t minor, char *buf, size_t buf_size)
+{
+ char *sysfs_path, *temp_buf = NULL;
+ FILE *fp = NULL;
+ int r = 0;
+ size_t len;
+
+ if (!(sysfs_path = dm_malloc(PATH_MAX)) ||
+ !(temp_buf = dm_malloc(PATH_MAX))) {
+ log_error("_sysfs_get_dm_name: failed to allocate temporary buffers");
+ goto bad;
+ }
+
+ if (dm_snprintf(sysfs_path, PATH_MAX, "%sdev/block/%" PRIu32 ":%" PRIu32
+ "/dm/name", _sysfs_dir, major, minor) < 0) {
+ log_error("_sysfs_get_dm_name: dm_snprintf failed");
+ goto bad;
+ }
+
+ if (!(fp = fopen(sysfs_path, "r"))) {
+ if (errno != ENOENT)
+ log_sys_error("fopen", sysfs_path);
+ else
+ log_sys_debug("fopen", sysfs_path);
+ goto bad;
+ }
+
+ if (!fgets(temp_buf, PATH_MAX, fp)) {
+ log_sys_error("fgets", sysfs_path);
+ goto bad;
+ }
+
+ len = strlen(temp_buf);
+
+ if (len > buf_size) {
+ log_error("_sysfs_get_dm_name: supplied buffer too small");
+ goto bad;
+ }
+
+ temp_buf[len ? len - 1 : 0] = '\0'; /* \n */
+ strcpy(buf, temp_buf);
+ r = 1;
+bad:
+ if (fp && fclose(fp))
+ log_sys_error("fclose", sysfs_path);
+
+ dm_free(temp_buf);
+ dm_free(sysfs_path);
+
+ return r;
+}
+
+static int _sysfs_get_kernel_name(uint32_t major, uint32_t minor, char *buf, size_t buf_size)
+{
+ char *name, *sysfs_path, *temp_buf = NULL;
+ ssize_t size;
+ size_t len;
+ int r = 0;
+
+ if (!(sysfs_path = dm_malloc(PATH_MAX)) ||
+ !(temp_buf = dm_malloc(PATH_MAX))) {
+ log_error("_sysfs_get_kernel_name: failed to allocate temporary buffers");
+ goto bad;
+ }
+
+ if (dm_snprintf(sysfs_path, PATH_MAX, "%sdev/block/%" PRIu32 ":%" PRIu32,
+ _sysfs_dir, major, minor) < 0) {
+ log_error("_sysfs_get_kernel_name: dm_snprintf failed");
+ goto bad;
+ }
+
+ if ((size = readlink(sysfs_path, temp_buf, PATH_MAX - 1)) < 0) {
+ if (errno != ENOENT)
+ log_sys_error("readlink", sysfs_path);
+ else
+ log_sys_debug("readlink", sysfs_path);
+ goto bad;
+ }
+ temp_buf[size] = '\0';
+
+ if (!(name = strrchr(temp_buf, '/'))) {
+ log_error("Could not locate device kernel name in sysfs path %s", temp_buf);
+ goto bad;
+ }
+ name += 1;
+ len = size - (name - temp_buf) + 1;
+
+ if (len > buf_size) {
+ log_error("_sysfs_get_kernel_name: output buffer too small");
+ goto bad;
+ }
+
+ strcpy(buf, name);
+ r = 1;
+bad:
+ dm_free(temp_buf);
+ dm_free(sysfs_path);
+
+ return r;
+}
+
+int dm_device_get_name(uint32_t major, uint32_t minor, int prefer_kernel_name,
+ char *buf, size_t buf_size)
+{
+ if (!*_sysfs_dir)
+ return 0;
+
+ /*
+ * device-mapper devices and prefer_kernel_name = 0
+ * get dm name by reading /sys/dev/block/major:minor/dm/name,
+ * fallback to _sysfs_get_kernel_name if not successful
+ */
+ if (dm_is_dm_major(major) && !prefer_kernel_name) {
+ if (_sysfs_get_dm_name(major, minor, buf, buf_size))
+ return 1;
+ else
+ stack;
+ }
+
+ /*
+ * non-device-mapper devices or prefer_kernel_name = 1
+ * get kernel name using readlink /sys/dev/block/major:minor -> .../dm-X
+ */
+ return _sysfs_get_kernel_name(major, minor, buf, buf_size);
+}
+
+int dm_device_has_holders(uint32_t major, uint32_t minor)
+{
+ char sysfs_path[PATH_MAX];
+ struct stat st;
+
+ if (!*_sysfs_dir)
+ return 0;
+
+ if (dm_snprintf(sysfs_path, PATH_MAX, "%sdev/block/%" PRIu32
+ ":%" PRIu32 "/holders", _sysfs_dir, major, minor) < 0) {
+ log_error("sysfs_path dm_snprintf failed");
+ return 0;
+ }
+
+ if (stat(sysfs_path, &st)) {
+ log_sys_error("stat", sysfs_path);
+ return 0;
+ }
+
+ return !dm_is_empty_dir(sysfs_path);
+}
+
+static int _mounted_fs_on_device(const char *kernel_dev_name)
+{
+ char sysfs_path[PATH_MAX];
+ struct dirent *dirent;
+ DIR *d;
+ struct stat st;
+ int r = 0;
+
+ if (dm_snprintf(sysfs_path, PATH_MAX, "%sfs", _sysfs_dir) < 0) {
+ log_error("sysfs_path dm_snprintf failed");
+ return 0;
+ }
+
+ if (!(d = opendir(sysfs_path))) {
+ if (errno != ENOENT)
+ log_sys_error("opendir", sysfs_path);
+ return 0;
+ }
+
+ while ((dirent = readdir(d))) {
+ if (!strcmp(dirent->d_name, ".") || !strcmp(dirent->d_name, ".."))
+ continue;
+
+ if (dm_snprintf(sysfs_path, PATH_MAX, "%sfs/%s/%s",
+ _sysfs_dir, dirent->d_name, kernel_dev_name) < 0) {
+ log_error("sysfs_path dm_snprintf failed");
+ break;
+ }
+
+ if (!stat(sysfs_path, &st)) {
+ /* found! */
+ r = 1;
+ break;
+ }
+ else if (errno != ENOENT) {
+ log_sys_error("stat", sysfs_path);
+ break;
+ }
+ }
+
+ if (closedir(d))
+ log_error("_fs_present_on_device: %s: closedir failed", kernel_dev_name);
+
+ return r;
+}
+
+int dm_device_has_mounted_fs(uint32_t major, uint32_t minor)
+{
+ char kernel_dev_name[PATH_MAX];
+
+ /* Get kernel device name first */
+ if (!dm_device_get_name(major, minor, 1, kernel_dev_name, PATH_MAX))
+ return 0;
+
+ /* Check /sys/fs/<fs_name>/<kernel_dev_name> presence */
+ return _mounted_fs_on_device(kernel_dev_name);
+}
+
int dm_mknodes(const char *name)
{
struct dm_task *dmt;
@@ -984,6 +1849,8 @@ int dm_udev_complete(uint32_t cookie)
int dm_udev_wait(uint32_t cookie)
{
+ update_devs();
+
return 1;
}
@@ -1111,6 +1978,7 @@ static int _get_cookie_sem(uint32_t cookie, int *semid)
static int _udev_notify_sem_inc(uint32_t cookie, int semid)
{
struct sembuf sb = {0, 1, 0};
+ int val;
if (semop(semid, &sb, 1) < 0) {
log_error("semid %d: semop failed for cookie 0x%" PRIx32 ": %s",
@@ -1118,8 +1986,15 @@ static int _udev_notify_sem_inc(uint32_t cookie, int semid)
return 0;
}
- log_debug("Udev cookie 0x%" PRIx32 " (semid %d) incremented",
- cookie, semid);
+ if ((val = semctl(semid, 0, GETVAL)) < 0) {
+ log_error("semid %d: sem_ctl GETVAL failed for "
+ "cookie 0x%" PRIx32 ": %s",
+ semid, cookie, strerror(errno));
+ return 0;
+ }
+
+ log_debug("Udev cookie 0x%" PRIx32 " (semid %d) incremented to %d",
+ cookie, semid, val);
return 1;
}
@@ -1127,6 +2002,14 @@ static int _udev_notify_sem_inc(uint32_t cookie, int semid)
static int _udev_notify_sem_dec(uint32_t cookie, int semid)
{
struct sembuf sb = {0, -1, IPC_NOWAIT};
+ int val;
+
+ if ((val = semctl(semid, 0, GETVAL)) < 0) {
+ log_error("semid %d: sem_ctl GETVAL failed for "
+ "cookie 0x%" PRIx32 ": %s",
+ semid, cookie, strerror(errno));
+ return 0;
+ }
if (semop(semid, &sb, 1) < 0) {
switch (errno) {
@@ -1145,8 +2028,8 @@ static int _udev_notify_sem_dec(uint32_t cookie, int semid)
return 0;
}
- log_debug("Udev cookie 0x%" PRIx32 " (semid %d) decremented",
- cookie, semid);
+ log_debug("Udev cookie 0x%" PRIx32 " (semid %d) decremented to %d",
+ cookie, semid, val - 1);
return 1;
}
@@ -1170,6 +2053,7 @@ static int _udev_notify_sem_create(uint32_t *cookie, int *semid)
{
int fd;
int gen_semid;
+ int val;
uint16_t base_cookie;
uint32_t gen_cookie;
union semun sem_arg;
@@ -1230,8 +2114,15 @@ static int _udev_notify_sem_create(uint32_t *cookie, int *semid)
goto bad;
}
- log_debug("Udev cookie 0x%" PRIx32 " (semid %d) incremented",
- gen_cookie, gen_semid);
+ if ((val = semctl(gen_semid, 0, GETVAL)) < 0) {
+ log_error("semid %d: sem_ctl GETVAL failed for "
+ "cookie 0x%" PRIx32 ": %s",
+ gen_semid, gen_cookie, strerror(errno));
+ goto bad;
+ }
+
+ log_debug("Udev cookie 0x%" PRIx32 " (semid %d) incremented to %d",
+ gen_cookie, gen_semid, val);
if (close(fd))
stack;
@@ -1262,6 +2153,51 @@ int dm_udev_create_cookie(uint32_t *cookie)
return _udev_notify_sem_create(cookie, &semid);
}
+static const char *_task_type_disp(int type)
+{
+ switch(type) {
+ case DM_DEVICE_CREATE:
+ return "CREATE";
+ case DM_DEVICE_RELOAD:
+ return "RELOAD";
+ case DM_DEVICE_REMOVE:
+ return "REMOVE";
+ case DM_DEVICE_REMOVE_ALL:
+ return "REMOVE_ALL";
+ case DM_DEVICE_SUSPEND:
+ return "SUSPEND";
+ case DM_DEVICE_RESUME:
+ return "RESUME";
+ case DM_DEVICE_INFO:
+ return "INFO";
+ case DM_DEVICE_DEPS:
+ return "DEPS";
+ case DM_DEVICE_RENAME:
+ return "RENAME";
+ case DM_DEVICE_VERSION:
+ return "VERSION";
+ case DM_DEVICE_STATUS:
+ return "STATUS";
+ case DM_DEVICE_TABLE:
+ return "TABLE";
+ case DM_DEVICE_WAITEVENT:
+ return "WAITEVENT";
+ case DM_DEVICE_LIST:
+ return "LIST";
+ case DM_DEVICE_CLEAR:
+ return "CLEAR";
+ case DM_DEVICE_MKNODES:
+ return "MKNODES";
+ case DM_DEVICE_LIST_VERSIONS:
+ return "LIST_VERSIONS";
+ case DM_DEVICE_TARGET_MSG:
+ return "TARGET_MSG";
+ case DM_DEVICE_SET_GEOMETRY:
+ return "SET_GEOMETRY";
+ }
+ return "unknown";
+}
+
int dm_task_set_cookie(struct dm_task *dmt, uint32_t *cookie, uint16_t flags)
{
int semid;
@@ -1290,8 +2226,16 @@ int dm_task_set_cookie(struct dm_task *dmt, uint32_t *cookie, uint16_t flags)
dmt->event_nr |= ~DM_UDEV_FLAGS_MASK & *cookie;
dmt->cookie_set = 1;
- log_debug("Udev cookie 0x%" PRIx32 " (semid %d) assigned to dm_task "
- "type %d with flags 0x%" PRIx16, *cookie, semid, dmt->type, flags);
+ log_debug("Udev cookie 0x%" PRIx32 " (semid %d) assigned to "
+ "%s task(%d) with flags%s%s%s%s%s%s%s (0x%" PRIx16 ")", *cookie, semid, _task_type_disp(dmt->type), dmt->type,
+ (flags & DM_UDEV_DISABLE_DM_RULES_FLAG) ? " DISABLE_DM_RULES" : "",
+ (flags & DM_UDEV_DISABLE_SUBSYSTEM_RULES_FLAG) ? " DISABLE_SUBSYSTEM_RULES" : "",
+ (flags & DM_UDEV_DISABLE_DISK_RULES_FLAG) ? " DISABLE_DISK_RULES" : "",
+ (flags & DM_UDEV_DISABLE_OTHER_RULES_FLAG) ? " DISABLE_OTHER_RULES" : "",
+ (flags & DM_UDEV_LOW_PRIORITY_FLAG) ? " LOW_PRIORITY" : "",
+ (flags & DM_UDEV_DISABLE_LIBRARY_FALLBACK) ? " DISABLE_LIBRARY_FALLBACK" : "",
+ (flags & DM_UDEV_PRIMARY_SOURCE_FLAG) ? " PRIMARY_SOURCE" : "",
+ flags);
return 1;
@@ -1320,7 +2264,7 @@ int dm_udev_complete(uint32_t cookie)
return 1;
}
-int dm_udev_wait(uint32_t cookie)
+static int _udev_wait(uint32_t cookie)
{
int semid;
struct sembuf sb = {0, 0, 0};
@@ -1340,7 +2284,7 @@ int dm_udev_wait(uint32_t cookie)
return 0;
}
- log_debug("Udev cookie 0x%" PRIx32 " (semid %d): Waiting for zero",
+ log_debug("Udev cookie 0x%" PRIx32 " (semid %d) waiting for zero",
cookie, semid);
repeat_wait:
@@ -1360,4 +2304,13 @@ repeat_wait:
return _udev_notify_sem_destroy(cookie, semid);
}
+int dm_udev_wait(uint32_t cookie)
+{
+ int r = _udev_wait(cookie);
+
+ update_devs();
+
+ return r;
+}
+
#endif /* UDEV_SYNC_SUPPORT */
diff --git a/libdm/libdm-common.h b/libdm/libdm-common.h
index 3267cfc..4705a77 100644
--- a/libdm/libdm-common.h
+++ b/libdm/libdm-common.h
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
- * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2004-2012 Red Hat, Inc. All rights reserved.
*
* This file is part of the device-mapper userspace tools.
*
@@ -18,19 +18,37 @@
#include "libdevmapper.h"
+#define DM_DEFAULT_NAME_MANGLING_MODE_ENV_VAR_NAME "DM_DEFAULT_NAME_MANGLING_MODE"
+
+#define DEV_NAME(dmt) (dmt->mangled_dev_name ? : dmt->dev_name)
+#define DEV_UUID(DMT) (dmt->mangled_uuid ? : dmt->uuid)
+
+int mangle_string(const char *str, const char *str_name, size_t len,
+ char *buf, size_t buf_len, dm_string_mangling_t mode);
+
+int unmangle_string(const char *str, const char *str_name, size_t len,
+ char *buf, size_t buf_len, dm_string_mangling_t mode);
+
+int check_multiple_mangled_string_allowed(const char *str, const char *str_name,
+ dm_string_mangling_t mode);
+
struct target *create_target(uint64_t start,
uint64_t len,
const char *type, const char *params);
int add_dev_node(const char *dev_name, uint32_t minor, uint32_t major,
- uid_t uid, gid_t gid, mode_t mode, int check_udev);
-int rm_dev_node(const char *dev_name, int check_udev);
+ uid_t uid, gid_t gid, mode_t mode, int check_udev, unsigned rely_on_udev);
+int rm_dev_node(const char *dev_name, int check_udev, unsigned rely_on_udev);
int rename_dev_node(const char *old_name, const char *new_name,
- int check_udev);
-int get_dev_node_read_ahead(const char *dev_name, uint32_t *read_ahead);
-int set_dev_node_read_ahead(const char *dev_name, uint32_t read_ahead,
- uint32_t read_ahead_flags);
+ int check_udev, unsigned rely_on_udev);
+int get_dev_node_read_ahead(const char *dev_name, uint32_t major, uint32_t minor,
+ uint32_t *read_ahead);
+int set_dev_node_read_ahead(const char *dev_name, uint32_t major, uint32_t minor,
+ uint32_t read_ahead, uint32_t read_ahead_flags);
void update_devs(void);
void selinux_release(void);
+void inc_suspended(void);
+void dec_suspended(void);
+
#endif
diff --git a/libdm/libdm-config.c b/libdm/libdm-config.c
new file mode 100644
index 0000000..c19f51d
--- /dev/null
+++ b/libdm/libdm-config.c
@@ -0,0 +1,1186 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2011 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "dmlib.h"
+
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <ctype.h>
+
+#define SECTION_B_CHAR '{'
+#define SECTION_E_CHAR '}'
+
+enum {
+ TOK_INT,
+ TOK_FLOAT,
+ TOK_STRING, /* Single quotes */
+ TOK_STRING_ESCAPED, /* Double quotes */
+ TOK_EQ,
+ TOK_SECTION_B,
+ TOK_SECTION_E,
+ TOK_ARRAY_B,
+ TOK_ARRAY_E,
+ TOK_IDENTIFIER,
+ TOK_COMMA,
+ TOK_EOF
+};
+
+struct parser {
+ const char *fb, *fe; /* file limits */
+
+ int t; /* token limits and type */
+ const char *tb, *te;
+
+ int line; /* line number we are on */
+
+ struct dm_pool *mem;
+};
+
+struct output_line {
+ struct dm_pool *mem;
+ dm_putline_fn putline;
+ void *putline_baton;
+};
+
+static void _get_token(struct parser *p, int tok_prev);
+static void _eat_space(struct parser *p);
+static struct dm_config_node *_file(struct parser *p);
+static struct dm_config_node *_section(struct parser *p);
+static struct dm_config_value *_value(struct parser *p);
+static struct dm_config_value *_type(struct parser *p);
+static int _match_aux(struct parser *p, int t);
+static struct dm_config_value *_create_value(struct dm_pool *mem);
+static struct dm_config_node *_create_node(struct dm_pool *mem);
+static char *_dup_tok(struct parser *p);
+
+static const int sep = '/';
+
+#define MAX_INDENT 32
+
+#define match(t) do {\
+ if (!_match_aux(p, (t))) {\
+ log_error("Parse error at byte %" PRIptrdiff_t " (line %d): unexpected token", \
+ p->tb - p->fb + 1, p->line); \
+ return 0;\
+ } \
+} while(0)
+
+static int _tok_match(const char *str, const char *b, const char *e)
+{
+ while (*str && (b != e)) {
+ if (*str++ != *b++)
+ return 0;
+ }
+
+ return !(*str || (b != e));
+}
+
+struct dm_config_tree *dm_config_create(void)
+{
+ struct dm_config_tree *cft;
+ struct dm_pool *mem = dm_pool_create("config", 10 * 1024);
+
+ if (!mem) {
+ log_error("Failed to allocate config pool.");
+ return 0;
+ }
+
+ if (!(cft = dm_pool_zalloc(mem, sizeof(*cft)))) {
+ log_error("Failed to allocate config tree.");
+ dm_pool_destroy(mem);
+ return 0;
+ }
+ cft->root = NULL;
+ cft->cascade = NULL;
+ cft->custom = NULL;
+ cft->mem = mem;
+ return cft;
+}
+
+void dm_config_set_custom(struct dm_config_tree *cft, void *custom)
+{
+ cft->custom = custom;
+}
+
+void *dm_config_get_custom(struct dm_config_tree *cft)
+{
+ return cft->custom;
+}
+
+void dm_config_destroy(struct dm_config_tree *cft)
+{
+ dm_pool_destroy(cft->mem);
+}
+
+/*
+ * If there's a cascaded dm_config_tree, remove and return it, otherwise
+ * return NULL.
+ */
+struct dm_config_tree *dm_config_remove_cascaded_tree(struct dm_config_tree *cft)
+{
+ struct dm_config_tree *second_cft;
+
+ if (!cft)
+ return NULL;
+
+ second_cft = cft->cascade;
+ cft->cascade = NULL;
+
+ return second_cft;
+}
+
+/*
+ * When searching, first_cft is checked before second_cft.
+ */
+struct dm_config_tree *dm_config_insert_cascaded_tree(struct dm_config_tree *first_cft, struct dm_config_tree *second_cft)
+{
+ first_cft->cascade = second_cft;
+
+ return first_cft;
+}
+
+int dm_config_parse(struct dm_config_tree *cft, const char *start, const char *end)
+{
+ /* TODO? if (start == end) return 1; */
+
+ struct parser *p;
+ if (!(p = dm_pool_alloc(cft->mem, sizeof(*p))))
+ return_0;
+
+ p->mem = cft->mem;
+ p->fb = start;
+ p->fe = end;
+ p->tb = p->te = p->fb;
+ p->line = 1;
+
+ _get_token(p, TOK_SECTION_E);
+ if (!(cft->root = _file(p)))
+ return_0;
+
+ return 1;
+}
+
+struct dm_config_tree *dm_config_from_string(const char *config_settings)
+{
+ struct dm_config_tree *cft;
+
+ if (!(cft = dm_config_create()))
+ return_NULL;
+
+ if (!dm_config_parse(cft, config_settings, config_settings + strlen(config_settings))) {
+ dm_config_destroy(cft);
+ return_NULL;
+ }
+
+ return cft;
+}
+
+static int _line_start(struct output_line *outline)
+{
+ if (!dm_pool_begin_object(outline->mem, 128)) {
+ log_error("dm_pool_begin_object failed for config line");
+ return 0;
+ }
+
+ return 1;
+}
+
+__attribute__ ((format(printf, 2, 3)))
+static int _line_append(struct output_line *outline, const char *fmt, ...)
+{
+ char buf[4096];
+ va_list ap;
+ int n;
+
+ va_start(ap, fmt);
+ n = vsnprintf(&buf[0], sizeof buf - 1, fmt, ap);
+ va_end(ap);
+
+ if (n < 0 || n > (int) sizeof buf - 1) {
+ log_error("vsnprintf failed for config line");
+ return 0;
+ }
+
+ if (!dm_pool_grow_object(outline->mem, &buf[0], strlen(buf))) {
+ log_error("dm_pool_grow_object failed for config line");
+ return 0;
+ }
+
+ return 1;
+}
+
+#define line_append(args...) do {if (!_line_append(outline, args)) {return_0;}} while (0)
+
+static int _line_end(struct output_line *outline)
+{
+ const char *line;
+
+ if (!dm_pool_grow_object(outline->mem, "\0", 1)) {
+ log_error("dm_pool_grow_object failed for config line");
+ return 0;
+ }
+
+ line = dm_pool_end_object(outline->mem);
+
+ if (!outline->putline)
+ return 0;
+
+ outline->putline(line, outline->putline_baton);
+
+ return 1;
+}
+
+static int _write_value(struct output_line *outline, const struct dm_config_value *v)
+{
+ char *buf;
+
+ switch (v->type) {
+ case DM_CFG_STRING:
+ if (!(buf = alloca(dm_escaped_len(v->v.str)))) {
+ log_error("temporary stack allocation for a config "
+ "string failed");
+ return 0;
+ }
+ line_append("\"%s\"", dm_escape_double_quotes(buf, v->v.str));
+ break;
+
+ case DM_CFG_FLOAT:
+ line_append("%f", v->v.f);
+ break;
+
+ case DM_CFG_INT:
+ line_append("%" PRId64, v->v.i);
+ break;
+
+ case DM_CFG_EMPTY_ARRAY:
+ line_append("[]");
+ break;
+
+ default:
+ log_error("_write_value: Unknown value type: %d", v->type);
+
+ }
+
+ return 1;
+}
+
+static int _write_config(const struct dm_config_node *n, int only_one,
+ struct output_line *outline, int level)
+{
+ char space[MAX_INDENT + 1];
+ int l = (level < MAX_INDENT) ? level : MAX_INDENT;
+ int i;
+
+ if (!n)
+ return 1;
+
+ for (i = 0; i < l; i++)
+ space[i] = '\t';
+ space[i] = '\0';
+
+ do {
+ if (!_line_start(outline))
+ return_0;
+ line_append("%s%s", space, n->key);
+ if (!n->v) {
+ /* it's a sub section */
+ line_append(" {");
+ if (!_line_end(outline))
+ return_0;
+ _write_config(n->child, 0, outline, level + 1);
+ if (!_line_start(outline))
+ return_0;
+ line_append("%s}", space);
+ } else {
+ /* it's a value */
+ const struct dm_config_value *v = n->v;
+ line_append("=");
+ if (v->next) {
+ line_append("[");
+ while (v && v->type != DM_CFG_EMPTY_ARRAY) {
+ if (!_write_value(outline, v))
+ return_0;
+ v = v->next;
+ if (v && v->type != DM_CFG_EMPTY_ARRAY)
+ line_append(", ");
+ }
+ line_append("]");
+ } else
+ if (!_write_value(outline, v))
+ return_0;
+ }
+ if (!_line_end(outline))
+ return_0;
+ n = n->sib;
+ } while (n && !only_one);
+ /* FIXME: add error checking */
+ return 1;
+}
+
+static int _write_node(const struct dm_config_node *cn, int only_one,
+ dm_putline_fn putline, void *baton)
+{
+ struct output_line outline;
+ if (!(outline.mem = dm_pool_create("config_line", 1024)))
+ return_0;
+ outline.putline = putline;
+ outline.putline_baton = baton;
+ if (!_write_config(cn, only_one, &outline, 0)) {
+ dm_pool_destroy(outline.mem);
+ return_0;
+ }
+ dm_pool_destroy(outline.mem);
+ return 1;
+}
+
+int dm_config_write_one_node(const struct dm_config_node *cn, dm_putline_fn putline, void *baton)
+{
+ return _write_node(cn, 1, putline, baton);
+}
+
+int dm_config_write_node(const struct dm_config_node *cn, dm_putline_fn putline, void *baton)
+{
+ return _write_node(cn, 0, putline, baton);
+}
+
+/*
+ * parser
+ */
+static struct dm_config_node *_file(struct parser *p)
+{
+ struct dm_config_node *root = NULL, *n, *l = NULL;
+ while (p->t != TOK_EOF) {
+ if (!(n = _section(p)))
+ return_NULL;
+
+ if (!root)
+ root = n;
+ else
+ l->sib = n;
+ n->parent = root;
+ l = n;
+ }
+ return root;
+}
+
+static struct dm_config_node *_section(struct parser *p)
+{
+ /* IDENTIFIER SECTION_B_CHAR VALUE* SECTION_E_CHAR */
+ struct dm_config_node *root, *n, *l = NULL;
+ if (!(root = _create_node(p->mem))) {
+ log_error("Failed to allocate section node");
+ return NULL;
+ }
+
+ if (!(root->key = _dup_tok(p)))
+ return_NULL;
+
+ match(TOK_IDENTIFIER);
+
+ if (p->t == TOK_SECTION_B) {
+ match(TOK_SECTION_B);
+ while (p->t != TOK_SECTION_E) {
+ if (!(n = _section(p)))
+ return_NULL;
+
+ if (!l)
+ root->child = n;
+ else
+ l->sib = n;
+ n->parent = root;
+ l = n;
+ }
+ match(TOK_SECTION_E);
+ } else {
+ match(TOK_EQ);
+ if (!(root->v = _value(p)))
+ return_NULL;
+ }
+
+ return root;
+}
+
+static struct dm_config_value *_value(struct parser *p)
+{
+ /* '[' TYPE* ']' | TYPE */
+ struct dm_config_value *h = NULL, *l, *ll = NULL;
+ if (p->t == TOK_ARRAY_B) {
+ match(TOK_ARRAY_B);
+ while (p->t != TOK_ARRAY_E) {
+ if (!(l = _type(p)))
+ return_NULL;
+
+ if (!h)
+ h = l;
+ else
+ ll->next = l;
+ ll = l;
+
+ if (p->t == TOK_COMMA)
+ match(TOK_COMMA);
+ }
+ match(TOK_ARRAY_E);
+ /*
+ * Special case for an empty array.
+ */
+ if (!h) {
+ if (!(h = _create_value(p->mem))) {
+ log_error("Failed to allocate value");
+ return NULL;
+ }
+
+ h->type = DM_CFG_EMPTY_ARRAY;
+ }
+
+ } else
+ if (!(h = _type(p)))
+ return_NULL;
+
+ return h;
+}
+
+static struct dm_config_value *_type(struct parser *p)
+{
+ /* [+-]{0,1}[0-9]+ | [0-9]*\.[0-9]* | ".*" */
+ struct dm_config_value *v = _create_value(p->mem);
+ char *str;
+
+ if (!v) {
+ log_error("Failed to allocate type value");
+ return NULL;
+ }
+
+ switch (p->t) {
+ case TOK_INT:
+ v->type = DM_CFG_INT;
+ v->v.i = strtoll(p->tb, NULL, 0); /* FIXME: check error */
+ match(TOK_INT);
+ break;
+
+ case TOK_FLOAT:
+ v->type = DM_CFG_FLOAT;
+ v->v.f = strtod(p->tb, NULL); /* FIXME: check error */
+ match(TOK_FLOAT);
+ break;
+
+ case TOK_STRING:
+ v->type = DM_CFG_STRING;
+
+ p->tb++, p->te--; /* strip "'s */
+ if (!(v->v.str = _dup_tok(p)))
+ return_NULL;
+ p->te++;
+ match(TOK_STRING);
+ break;
+
+ case TOK_STRING_ESCAPED:
+ v->type = DM_CFG_STRING;
+
+ p->tb++, p->te--; /* strip "'s */
+ if (!(str = _dup_tok(p)))
+ return_NULL;
+ dm_unescape_double_quotes(str);
+ v->v.str = str;
+ p->te++;
+ match(TOK_STRING_ESCAPED);
+ break;
+
+ default:
+ log_error("Parse error at byte %" PRIptrdiff_t " (line %d): expected a value",
+ p->tb - p->fb + 1, p->line);
+ return NULL;
+ }
+ return v;
+}
+
+static int _match_aux(struct parser *p, int t)
+{
+ if (p->t != t)
+ return 0;
+
+ _get_token(p, t);
+ return 1;
+}
+
+/*
+ * tokeniser
+ */
+static void _get_token(struct parser *p, int tok_prev)
+{
+ int values_allowed = 0;
+
+ const char *te;
+
+ p->tb = p->te;
+ _eat_space(p);
+ if (p->tb == p->fe || !*p->tb) {
+ p->t = TOK_EOF;
+ return;
+ }
+
+ /* Should next token be interpreted as value instead of identifier? */
+ if (tok_prev == TOK_EQ || tok_prev == TOK_ARRAY_B ||
+ tok_prev == TOK_COMMA)
+ values_allowed = 1;
+
+ p->t = TOK_INT; /* fudge so the fall through for
+ floats works */
+
+ te = p->te;
+ switch (*te) {
+ case SECTION_B_CHAR:
+ p->t = TOK_SECTION_B;
+ te++;
+ break;
+
+ case SECTION_E_CHAR:
+ p->t = TOK_SECTION_E;
+ te++;
+ break;
+
+ case '[':
+ p->t = TOK_ARRAY_B;
+ te++;
+ break;
+
+ case ']':
+ p->t = TOK_ARRAY_E;
+ te++;
+ break;
+
+ case ',':
+ p->t = TOK_COMMA;
+ te++;
+ break;
+
+ case '=':
+ p->t = TOK_EQ;
+ te++;
+ break;
+
+ case '"':
+ p->t = TOK_STRING_ESCAPED;
+ te++;
+ while ((te != p->fe) && (*te) && (*te != '"')) {
+ if ((*te == '\\') && (te + 1 != p->fe) &&
+ *(te + 1))
+ te++;
+ te++;
+ }
+
+ if ((te != p->fe) && (*te))
+ te++;
+ break;
+
+ case '\'':
+ p->t = TOK_STRING;
+ te++;
+ while ((te != p->fe) && (*te) && (*te != '\''))
+ te++;
+
+ if ((te != p->fe) && (*te))
+ te++;
+ break;
+
+ case '.':
+ p->t = TOK_FLOAT;
+ /* Fall through */
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ case '+':
+ case '-':
+ if (values_allowed) {
+ while (++te != p->fe) {
+ if (!isdigit((int) *te)) {
+ if (*te == '.') {
+ if (p->t != TOK_FLOAT) {
+ p->t = TOK_FLOAT;
+ continue;
+ }
+ }
+ break;
+ }
+ }
+ break;
+ }
+ /* fall through */
+
+ default:
+ p->t = TOK_IDENTIFIER;
+ while ((te != p->fe) && (*te) && !isspace(*te) &&
+ (*te != '#') && (*te != '=') &&
+ (*te != SECTION_B_CHAR) &&
+ (*te != SECTION_E_CHAR))
+ te++;
+ break;
+ }
+
+ p->te = te;
+}
+
+static void _eat_space(struct parser *p)
+{
+ while (p->tb != p->fe) {
+ if (*p->te == '#')
+ while ((p->te != p->fe) && (*p->te != '\n') && (*p->te))
+ ++p->te;
+
+ else if (!isspace(*p->te))
+ break;
+
+ while ((p->te != p->fe) && isspace(*p->te)) {
+ if (*p->te == '\n')
+ ++p->line;
+ ++p->te;
+ }
+
+ p->tb = p->te;
+ }
+}
+
+/*
+ * memory management
+ */
+static struct dm_config_value *_create_value(struct dm_pool *mem)
+{
+ return dm_pool_zalloc(mem, sizeof(struct dm_config_value));
+}
+
+static struct dm_config_node *_create_node(struct dm_pool *mem)
+{
+ return dm_pool_zalloc(mem, sizeof(struct dm_config_node));
+}
+
+static char *_dup_tok(struct parser *p)
+{
+ size_t len = p->te - p->tb;
+ char *str = dm_pool_alloc(p->mem, len + 1);
+ if (!str) {
+ log_error("Failed to duplicate token.");
+ return 0;
+ }
+ memcpy(str, p->tb, len);
+ str[len] = '\0';
+ return str;
+}
+
+/*
+ * Utility functions
+ */
+
+/*
+ * node_lookup_fn is either:
+ * _find_config_node to perform a lookup starting from a given config_node
+ * in a config_tree;
+ * or
+ * _find_first_config_node to find the first config_node in a set of
+ * cascaded trees.
+ */
+typedef const struct dm_config_node *node_lookup_fn(const void *start, const char *path);
+
+static const struct dm_config_node *_find_config_node(const void *start,
+ const char *path)
+{
+ const char *e;
+ const struct dm_config_node *cn = start;
+ const struct dm_config_node *cn_found = NULL;
+
+ while (cn) {
+ /* trim any leading slashes */
+ while (*path && (*path == sep))
+ path++;
+
+ /* find the end of this segment */
+ for (e = path; *e && (*e != sep); e++) ;
+
+ /* hunt for the node */
+ cn_found = NULL;
+ while (cn) {
+ if (_tok_match(cn->key, path, e)) {
+ /* Inefficient */
+ if (!cn_found)
+ cn_found = cn;
+ else
+ log_warn("WARNING: Ignoring duplicate"
+ " config node: %s ("
+ "seeking %s)", cn->key, path);
+ }
+
+ cn = cn->sib;
+ }
+
+ if (cn_found && *e)
+ cn = cn_found->child;
+ else
+ break; /* don't move into the last node */
+
+ path = e;
+ }
+
+ return cn_found;
+}
+
+static const struct dm_config_node *_find_first_config_node(const void *start, const char *path)
+{
+ const struct dm_config_tree *cft = start;
+ const struct dm_config_node *cn = NULL;
+
+ while (cft) {
+ if ((cn = _find_config_node(cft->root, path)))
+ return cn;
+ cft = cft->cascade;
+ }
+
+ return NULL;
+}
+
+static const char *_find_config_str(const void *start, node_lookup_fn find_fn,
+ const char *path, const char *fail, int allow_empty)
+{
+ const struct dm_config_node *n = find_fn(start, path);
+
+ /* Empty strings are ignored if allow_empty is set */
+ if (n && n->v) {
+ if ((n->v->type == DM_CFG_STRING) &&
+ (allow_empty || (*n->v->v.str))) {
+ log_very_verbose("Setting %s to %s", path, n->v->v.str);
+ return n->v->v.str;
+ }
+ if ((n->v->type != DM_CFG_STRING) || (!allow_empty && fail))
+ log_warn("WARNING: Ignoring unsupported value for %s.", path);
+ }
+
+ if (fail)
+ log_very_verbose("%s not found in config: defaulting to %s",
+ path, fail);
+ return fail;
+}
+
+const char *dm_config_find_str(const struct dm_config_node *cn,
+ const char *path, const char *fail)
+{
+ return _find_config_str(cn, _find_config_node, path, fail, 0);
+}
+
+const char *dm_config_find_str_allow_empty(const struct dm_config_node *cn,
+ const char *path, const char *fail)
+{
+ return _find_config_str(cn, _find_config_node, path, fail, 1);
+}
+
+static int64_t _find_config_int64(const void *start, node_lookup_fn find,
+ const char *path, int64_t fail)
+{
+ const struct dm_config_node *n = find(start, path);
+
+ if (n && n->v && n->v->type == DM_CFG_INT) {
+ log_very_verbose("Setting %s to %" PRId64, path, n->v->v.i);
+ return n->v->v.i;
+ }
+
+ log_very_verbose("%s not found in config: defaulting to %" PRId64,
+ path, fail);
+ return fail;
+}
+
+static float _find_config_float(const void *start, node_lookup_fn find,
+ const char *path, float fail)
+{
+ const struct dm_config_node *n = find(start, path);
+
+ if (n && n->v && n->v->type == DM_CFG_FLOAT) {
+ log_very_verbose("Setting %s to %f", path, n->v->v.f);
+ return n->v->v.f;
+ }
+
+ log_very_verbose("%s not found in config: defaulting to %f",
+ path, fail);
+
+ return fail;
+
+}
+
+static int _str_in_array(const char *str, const char * const values[])
+{
+ int i;
+
+ for (i = 0; values[i]; i++)
+ if (!strcasecmp(str, values[i]))
+ return 1;
+
+ return 0;
+}
+
+static int _str_to_bool(const char *str, int fail)
+{
+ const char * const _true_values[] = { "y", "yes", "on", "true", NULL };
+ const char * const _false_values[] = { "n", "no", "off", "false", NULL };
+
+ if (_str_in_array(str, _true_values))
+ return 1;
+
+ if (_str_in_array(str, _false_values))
+ return 0;
+
+ return fail;
+}
+
+static int _find_config_bool(const void *start, node_lookup_fn find,
+ const char *path, int fail)
+{
+ const struct dm_config_node *n = find(start, path);
+ const struct dm_config_value *v;
+ int b;
+
+ if (n) {
+ v = n->v;
+
+ switch (v->type) {
+ case DM_CFG_INT:
+ b = v->v.i ? 1 : 0;
+ log_very_verbose("Setting %s to %d", path, b);
+ return b;
+
+ case DM_CFG_STRING:
+ b = _str_to_bool(v->v.str, fail);
+ log_very_verbose("Setting %s to %d", path, b);
+ return b;
+ default:
+ ;
+ }
+ }
+
+ log_very_verbose("%s not found in config: defaulting to %d",
+ path, fail);
+
+ return fail;
+}
+
+/***********************************
+ * node-based lookup
+ **/
+
+struct dm_config_node *dm_config_find_node(const struct dm_config_node *cn,
+ const char *path)
+{
+ return (struct dm_config_node *) _find_config_node(cn, path);
+}
+
+int dm_config_find_int(const struct dm_config_node *cn, const char *path, int fail)
+{
+ /* FIXME Add log_error message on overflow */
+ return (int) _find_config_int64(cn, _find_config_node, path, (int64_t) fail);
+}
+
+int64_t dm_config_find_int64(const struct dm_config_node *cn, const char *path, int64_t fail)
+{
+ return _find_config_int64(cn, _find_config_node, path, fail);
+}
+
+float dm_config_find_float(const struct dm_config_node *cn, const char *path,
+ float fail)
+{
+ return _find_config_float(cn, _find_config_node, path, fail);
+}
+
+int dm_config_find_bool(const struct dm_config_node *cn, const char *path, int fail)
+{
+ return _find_config_bool(cn, _find_config_node, path, fail);
+}
+
+/***********************************
+ * tree-based lookup
+ **/
+
+const struct dm_config_node *dm_config_tree_find_node(const struct dm_config_tree *cft,
+ const char *path)
+{
+ return _find_first_config_node(cft, path);
+}
+
+const char *dm_config_tree_find_str(const struct dm_config_tree *cft, const char *path,
+ const char *fail)
+{
+ return _find_config_str(cft, _find_first_config_node, path, fail, 0);
+}
+
+const char *dm_config_tree_find_str_allow_empty(const struct dm_config_tree *cft, const char *path,
+ const char *fail)
+{
+ return _find_config_str(cft, _find_first_config_node, path, fail, 1);
+}
+
+int dm_config_tree_find_int(const struct dm_config_tree *cft, const char *path, int fail)
+{
+ /* FIXME Add log_error message on overflow */
+ return (int) _find_config_int64(cft, _find_first_config_node, path, (int64_t) fail);
+}
+
+int64_t dm_config_tree_find_int64(const struct dm_config_tree *cft, const char *path, int64_t fail)
+{
+ return _find_config_int64(cft, _find_first_config_node, path, fail);
+}
+
+float dm_config_tree_find_float(const struct dm_config_tree *cft, const char *path,
+ float fail)
+{
+ return _find_config_float(cft, _find_first_config_node, path, fail);
+}
+
+int dm_config_tree_find_bool(const struct dm_config_tree *cft, const char *path, int fail)
+{
+ return _find_config_bool(cft, _find_first_config_node, path, fail);
+}
+
+/************************************/
+
+
+int dm_config_get_uint32(const struct dm_config_node *cn, const char *path,
+ uint32_t *result)
+{
+ const struct dm_config_node *n;
+
+ n = _find_config_node(cn, path);
+
+ if (!n || !n->v || n->v->type != DM_CFG_INT)
+ return 0;
+
+ if (result)
+ *result = n->v->v.i;
+ return 1;
+}
+
+int dm_config_get_uint64(const struct dm_config_node *cn, const char *path,
+ uint64_t *result)
+{
+ const struct dm_config_node *n;
+
+ n = _find_config_node(cn, path);
+
+ if (!n || !n->v || n->v->type != DM_CFG_INT)
+ return 0;
+
+ if (result)
+ *result = (uint64_t) n->v->v.i;
+ return 1;
+}
+
+int dm_config_get_str(const struct dm_config_node *cn, const char *path,
+ const char **result)
+{
+ const struct dm_config_node *n;
+
+ n = _find_config_node(cn, path);
+
+ if (!n || !n->v || n->v->type != DM_CFG_STRING)
+ return 0;
+
+ if (result)
+ *result = n->v->v.str;
+ return 1;
+}
+
+int dm_config_get_list(const struct dm_config_node *cn, const char *path,
+ const struct dm_config_value **result)
+{
+ const struct dm_config_node *n;
+
+ n = _find_config_node(cn, path);
+ /* TODO when we represent single-item lists consistently, add a check
+ * for n->v->next != NULL */
+ if (!n || !n->v)
+ return 0;
+
+ if (result)
+ *result = n->v;
+ return 1;
+}
+
+int dm_config_get_section(const struct dm_config_node *cn, const char *path,
+ const struct dm_config_node **result)
+{
+ const struct dm_config_node *n;
+
+ n = _find_config_node(cn, path);
+ if (!n || n->v)
+ return 0;
+
+ if (result)
+ *result = n;
+ return 1;
+}
+
+int dm_config_has_node(const struct dm_config_node *cn, const char *path)
+{
+ return _find_config_node(cn, path) ? 1 : 0;
+}
+
+/*
+ * Convert a token type to the char it represents.
+ */
+static char _token_type_to_char(int type)
+{
+ switch (type) {
+ case TOK_SECTION_B:
+ return SECTION_B_CHAR;
+ case TOK_SECTION_E:
+ return SECTION_E_CHAR;
+ default:
+ return 0;
+ }
+}
+
+/*
+ * Returns:
+ * # of 'type' tokens in 'str'.
+ */
+static unsigned _count_tokens(const char *str, unsigned len, int type)
+{
+ char c;
+
+ c = _token_type_to_char(type);
+
+ return dm_count_chars(str, len, c);
+}
+
+const char *dm_config_parent_name(const struct dm_config_node *n)
+{
+ return (n->parent ? n->parent->key : "(root)");
+}
+/*
+ * Heuristic function to make a quick guess as to whether a text
+ * region probably contains a valid config "section". (Useful for
+ * scanning areas of the disk for old metadata.)
+ * Config sections contain various tokens, may contain other sections
+ * and strings, and are delimited by begin (type 'TOK_SECTION_B') and
+ * end (type 'TOK_SECTION_E') tokens. As a quick heuristic, we just
+ * count the number of begin and end tokens, and see if they are
+ * non-zero and the counts match.
+ * Full validation of the section should be done with another function
+ * (for example, read_config_fd).
+ *
+ * Returns:
+ * 0 - probably is not a valid config section
+ * 1 - probably _is_ a valid config section
+ */
+unsigned dm_config_maybe_section(const char *str, unsigned len)
+{
+ int begin_count;
+ int end_count;
+
+ begin_count = _count_tokens(str, len, TOK_SECTION_B);
+ end_count = _count_tokens(str, len, TOK_SECTION_E);
+
+ if (begin_count && end_count && (begin_count == end_count))
+ return 1;
+ else
+ return 0;
+}
+
+__attribute__((nonnull(1, 2)))
+static struct dm_config_value *_clone_config_value(struct dm_pool *mem,
+ const struct dm_config_value *v)
+{
+ struct dm_config_value *new_cv;
+
+ if (!(new_cv = _create_value(mem))) {
+ log_error("Failed to clone config value.");
+ return NULL;
+ }
+
+ new_cv->type = v->type;
+ if (v->type == DM_CFG_STRING) {
+ if (!(new_cv->v.str = dm_pool_strdup(mem, v->v.str))) {
+ log_error("Failed to clone config string value.");
+ return NULL;
+ }
+ } else
+ new_cv->v = v->v;
+
+ if (v->next && !(new_cv->next = _clone_config_value(mem, v->next)))
+ return_NULL;
+
+ return new_cv;
+}
+
+struct dm_config_node *dm_config_clone_node_with_mem(struct dm_pool *mem, const struct dm_config_node *cn, int siblings)
+{
+ struct dm_config_node *new_cn;
+
+ if (!cn) {
+ log_error("Cannot clone NULL config node.");
+ return NULL;
+ }
+
+ if (!(new_cn = _create_node(mem))) {
+ log_error("Failed to clone config node.");
+ return NULL;
+ }
+
+ if ((cn->key && !(new_cn->key = dm_pool_strdup(mem, cn->key)))) {
+ log_error("Failed to clone config node key.");
+ return NULL;
+ }
+
+ if ((cn->v && !(new_cn->v = _clone_config_value(mem, cn->v))) ||
+ (cn->child && !(new_cn->child = dm_config_clone_node_with_mem(mem, cn->child, 1))) ||
+ (siblings && cn->sib && !(new_cn->sib = dm_config_clone_node_with_mem(mem, cn->sib, siblings))))
+ return_NULL; /* 'new_cn' released with mem pool */
+
+ return new_cn;
+}
+
+struct dm_config_node *dm_config_clone_node(struct dm_config_tree *cft, const struct dm_config_node *node, int sib)
+{
+ return dm_config_clone_node_with_mem(cft->mem, node, sib);
+}
+
+struct dm_config_node *dm_config_create_node(struct dm_config_tree *cft, const char *key)
+{
+ struct dm_config_node *cn;
+
+ if (!(cn = _create_node(cft->mem))) {
+ log_error("Failed to create config node.");
+ return NULL;
+ }
+ if (!(cn->key = dm_pool_strdup(cft->mem, key))) {
+ log_error("Failed to create config node's key.");
+ return NULL;
+ }
+ cn->parent = NULL;
+ cn->v = NULL;
+
+ return cn;
+}
+
+struct dm_config_value *dm_config_create_value(struct dm_config_tree *cft)
+{
+ return _create_value(cft->mem);
+}
+
+struct dm_pool *dm_config_memory(struct dm_config_tree *cft)
+{
+ return cft->mem;
+}
diff --git a/libdm/libdm-deptree.c b/libdm/libdm-deptree.c
index 8d00514..096eba2 100644
--- a/libdm/libdm-deptree.c
+++ b/libdm/libdm-deptree.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2005-2010 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2005-2012 Red Hat, Inc. All rights reserved.
*
* This file is part of the device-mapper userspace tools.
*
@@ -16,7 +16,6 @@
#include "libdm-targets.h"
#include "libdm-common.h"
#include "kdev_t.h"
-#include "dm-ioctl.h"
#include <stdarg.h>
#include <sys/param.h>
@@ -24,9 +23,6 @@
#define MAX_TARGET_PARAMSIZE 500000
-/* FIXME Fix interface so this is used only by LVM */
-#define UUID_PREFIX "LVM-"
-
#define REPLICATOR_LOCAL_SITE 0
/* Supported segment types */
@@ -42,6 +38,19 @@ enum {
SEG_SNAPSHOT_MERGE,
SEG_STRIPED,
SEG_ZERO,
+ SEG_THIN_POOL,
+ SEG_THIN,
+ SEG_RAID1,
+ SEG_RAID10,
+ SEG_RAID4,
+ SEG_RAID5_LA,
+ SEG_RAID5_RA,
+ SEG_RAID5_LS,
+ SEG_RAID5_RS,
+ SEG_RAID6_ZR,
+ SEG_RAID6_NR,
+ SEG_RAID6_NC,
+ SEG_LAST,
};
/* FIXME Add crypt and multipath support */
@@ -61,6 +70,26 @@ struct {
{ SEG_SNAPSHOT_MERGE, "snapshot-merge" },
{ SEG_STRIPED, "striped" },
{ SEG_ZERO, "zero"},
+ { SEG_THIN_POOL, "thin-pool"},
+ { SEG_THIN, "thin"},
+ { SEG_RAID1, "raid1"},
+ { SEG_RAID10, "raid10"},
+ { SEG_RAID4, "raid4"},
+ { SEG_RAID5_LA, "raid5_la"},
+ { SEG_RAID5_RA, "raid5_ra"},
+ { SEG_RAID5_LS, "raid5_ls"},
+ { SEG_RAID5_RS, "raid5_rs"},
+ { SEG_RAID6_ZR, "raid6_zr"},
+ { SEG_RAID6_NR, "raid6_nr"},
+ { SEG_RAID6_NC, "raid6_nc"},
+
+ /*
+ *WARNING: Since 'raid' target overloads this 1:1 mapping table
+ * for search do not add new enum elements past them!
+ */
+ { SEG_RAID5_LS, "raid5"}, /* same as "raid5_ls" (default for MD also) */
+ { SEG_RAID6_ZR, "raid6"}, /* same as "raid6_zr" */
+ { SEG_LAST, NULL },
};
/* Some segment types have a list of areas of other devices attached */
@@ -77,6 +106,32 @@ struct seg_area {
uint32_t flags; /* Replicator sync log flags */
};
+struct dm_thin_message {
+ dm_thin_message_t type;
+ union {
+ struct {
+ uint32_t device_id;
+ uint32_t origin_id;
+ } m_create_snap;
+ struct {
+ uint32_t device_id;
+ } m_create_thin;
+ struct {
+ uint32_t device_id;
+ } m_delete;
+ struct {
+ uint64_t current_id;
+ uint64_t new_id;
+ } m_set_transaction_id;
+ } u;
+};
+
+struct thin_message {
+ struct dm_list list;
+ struct dm_thin_message message;
+ int expected_errno;
+};
+
/* Replicator-log has a list of sites */
/* FIXME: maybe move to seg_area too? */
struct replicator_site {
@@ -100,7 +155,7 @@ struct load_segment {
unsigned area_count; /* Linear + Striped + Mirrored + Crypt + Replicator */
struct dm_list areas; /* Linear + Striped + Mirrored + Crypt + Replicator */
- uint32_t stripe_size; /* Striped */
+ uint32_t stripe_size; /* Striped + raid */
int persistent; /* Snapshot */
uint32_t chunk_size; /* Snapshot */
@@ -109,7 +164,7 @@ struct load_segment {
struct dm_tree_node *merge; /* Snapshot */
struct dm_tree_node *log; /* Mirror + Replicator */
- uint32_t region_size; /* Mirror */
+ uint32_t region_size; /* Mirror + raid */
unsigned clustered; /* Mirror */
unsigned mirror_area_count; /* Mirror */
uint32_t flags; /* Mirror log */
@@ -127,6 +182,21 @@ struct load_segment {
unsigned rdevice_count; /* Replicator */
struct dm_tree_node *replicator;/* Replicator-dev */
uint64_t rdevice_index; /* Replicator-dev */
+
+ uint64_t rebuilds; /* raid */
+
+ struct dm_tree_node *metadata; /* Thin_pool */
+ struct dm_tree_node *pool; /* Thin_pool, Thin */
+ struct dm_tree_node *external; /* Thin */
+ struct dm_list thin_messages; /* Thin_pool */
+ uint64_t transaction_id; /* Thin_pool */
+ uint64_t low_water_mark; /* Thin_pool */
+ uint32_t data_block_size; /* Thin_pool */
+ unsigned skip_block_zeroing; /* Thin_pool */
+ unsigned ignore_discard; /* Thin_pool target vsn 1.1 */
+ unsigned no_discard_passdown; /* Thin_pool target vsn 1.1 */
+ uint32_t device_id; /* Thin */
+
};
/* Per-device properties */
@@ -149,7 +219,19 @@ struct load_properties {
* and processing of dm tree). This will also flush all stacked dev
* node operations, synchronizing with udev.
*/
- int immediate_dev_node;
+ unsigned immediate_dev_node;
+
+ /*
+ * If the device size changed from zero and this is set,
+ * don't resume the device immediately, even if the device
+ * has parents. This works provided the parents do not
+ * validate the device size and is required by pvmove to
+ * avoid starting the mirror resync operation too early.
+ */
+ unsigned delay_resume_if_new;
+
+ /* Send messages for this node in preload */
+ unsigned send_messages;
};
/* Two of these used to join two nodes with uses and used_by. */
@@ -161,12 +243,12 @@ struct dm_tree_link {
struct dm_tree_node {
struct dm_tree *dtree;
- const char *name;
- const char *uuid;
- struct dm_info info;
+ const char *name;
+ const char *uuid;
+ struct dm_info info;
- struct dm_list uses; /* Nodes this node uses */
- struct dm_list used_by; /* Nodes that use this node */
+ struct dm_list uses; /* Nodes this node uses */
+ struct dm_list used_by; /* Nodes that use this node */
int activation_priority; /* 0 gets activated first */
@@ -181,6 +263,10 @@ struct dm_tree_node {
* Note: only direct child is allowed
*/
struct dm_tree_node *presuspend_node;
+
+ /* Callback */
+ dm_node_callback_fn callback;
+ void *callback_data;
};
struct dm_tree {
@@ -189,16 +275,24 @@ struct dm_tree {
struct dm_hash_table *uuids;
struct dm_tree_node root;
int skip_lockfs; /* 1 skips lockfs (for non-snapshots) */
- int no_flush; /* 1 sets noflush (mirrors/multipath) */
+ int no_flush; /* 1 sets noflush (mirrors/multipath) */
+ int retry_remove; /* 1 retries remove if not successful */
uint32_t cookie;
};
+/*
+ * Tree functions.
+ */
struct dm_tree *dm_tree_create(void)
{
+ struct dm_pool *dmem;
struct dm_tree *dtree;
- if (!(dtree = dm_zalloc(sizeof(*dtree)))) {
- log_error("dm_tree_create malloc failed");
+ if (!(dmem = dm_pool_create("dtree", 1024)) ||
+ !(dtree = dm_pool_zalloc(dmem, sizeof(*dtree)))) {
+ log_error("Failed to allocate dtree.");
+ if (dmem)
+ dm_pool_destroy(dmem);
return NULL;
}
@@ -207,17 +301,11 @@ struct dm_tree *dm_tree_create(void)
dm_list_init(&dtree->root.used_by);
dtree->skip_lockfs = 0;
dtree->no_flush = 0;
-
- if (!(dtree->mem = dm_pool_create("dtree", 1024))) {
- log_error("dtree pool creation failed");
- dm_free(dtree);
- return NULL;
- }
+ dtree->mem = dmem;
if (!(dtree->devs = dm_hash_create(8))) {
log_error("dtree hash creation failed");
dm_pool_destroy(dtree->mem);
- dm_free(dtree);
return NULL;
}
@@ -225,7 +313,6 @@ struct dm_tree *dm_tree_create(void)
log_error("dtree uuid hash creation failed");
dm_hash_destroy(dtree->devs);
dm_pool_destroy(dtree->mem);
- dm_free(dtree);
return NULL;
}
@@ -240,9 +327,36 @@ void dm_tree_free(struct dm_tree *dtree)
dm_hash_destroy(dtree->uuids);
dm_hash_destroy(dtree->devs);
dm_pool_destroy(dtree->mem);
- dm_free(dtree);
}
+void dm_tree_set_cookie(struct dm_tree_node *node, uint32_t cookie)
+{
+ node->dtree->cookie = cookie;
+}
+
+uint32_t dm_tree_get_cookie(struct dm_tree_node *node)
+{
+ return node->dtree->cookie;
+}
+
+void dm_tree_skip_lockfs(struct dm_tree_node *dnode)
+{
+ dnode->dtree->skip_lockfs = 1;
+}
+
+void dm_tree_use_no_flush_suspend(struct dm_tree_node *dnode)
+{
+ dnode->dtree->no_flush = 1;
+}
+
+void dm_tree_retry_remove(struct dm_tree_node *dnode)
+{
+ dnode->dtree->retry_remove = 1;
+}
+
+/*
+ * Node functions.
+ */
static int _nodes_are_linked(const struct dm_tree_node *parent,
const struct dm_tree_node *child)
{
@@ -329,13 +443,13 @@ static void _remove_from_bottomlevel(struct dm_tree_node *node)
static int _link_tree_nodes(struct dm_tree_node *parent, struct dm_tree_node *child)
{
/* Don't link to root node if child already has a parent */
- if ((parent == &parent->dtree->root)) {
+ if (parent == &parent->dtree->root) {
if (dm_tree_node_num_children(child, 1))
return 1;
} else
_remove_from_toplevel(child);
- if ((child == &child->dtree->root)) {
+ if (child == &child->dtree->root) {
if (dm_tree_node_num_children(parent, 0))
return 1;
} else
@@ -352,7 +466,7 @@ static struct dm_tree_node *_create_dm_tree_node(struct dm_tree *dtree,
uint16_t udev_flags)
{
struct dm_tree_node *node;
- uint64_t dev;
+ dev_t dev;
if (!(node = dm_pool_zalloc(dtree->mem, sizeof(*node)))) {
log_error("_create_dm_tree_node alloc failed");
@@ -372,7 +486,7 @@ static struct dm_tree_node *_create_dm_tree_node(struct dm_tree *dtree,
dm_list_init(&node->used_by);
dm_list_init(&node->props.segs);
- dev = MKDEV(info->major, info->minor);
+ dev = MKDEV((dev_t)info->major, info->minor);
if (!dm_hash_insert_binary(dtree->devs, (const char *) &dev,
sizeof(dev), node)) {
@@ -396,7 +510,7 @@ static struct dm_tree_node *_create_dm_tree_node(struct dm_tree *dtree,
static struct dm_tree_node *_find_dm_tree_node(struct dm_tree *dtree,
uint32_t major, uint32_t minor)
{
- uint64_t dev = MKDEV(major, minor);
+ dev_t dev = MKDEV((dev_t)major, minor);
return dm_hash_lookup_binary(dtree->devs, (const char *) &dev,
sizeof(dev));
@@ -406,287 +520,34 @@ static struct dm_tree_node *_find_dm_tree_node_by_uuid(struct dm_tree *dtree,
const char *uuid)
{
struct dm_tree_node *node;
+ const char *default_uuid_prefix;
+ size_t default_uuid_prefix_len;
if ((node = dm_hash_lookup(dtree->uuids, uuid)))
return node;
- if (strncmp(uuid, UUID_PREFIX, sizeof(UUID_PREFIX) - 1))
- return NULL;
-
- return dm_hash_lookup(dtree->uuids, uuid + sizeof(UUID_PREFIX) - 1);
-}
+ default_uuid_prefix = dm_uuid_prefix();
+ default_uuid_prefix_len = strlen(default_uuid_prefix);
-static int _deps(struct dm_task **dmt, struct dm_pool *mem, uint32_t major, uint32_t minor,
- const char **name, const char **uuid,
- struct dm_info *info, struct dm_deps **deps)
-{
- memset(info, 0, sizeof(*info));
-
- if (!dm_is_dm_major(major)) {
- *name = "";
- *uuid = "";
- *deps = NULL;
- info->major = major;
- info->minor = minor;
- info->exists = 0;
- info->live_table = 0;
- info->inactive_table = 0;
- info->read_only = 0;
- return 1;
- }
-
- if (!(*dmt = dm_task_create(DM_DEVICE_DEPS))) {
- log_error("deps dm_task creation failed");
- return 0;
- }
-
- if (!dm_task_set_major(*dmt, major)) {
- log_error("_deps: failed to set major for (%" PRIu32 ":%" PRIu32 ")",
- major, minor);
- goto failed;
- }
-
- if (!dm_task_set_minor(*dmt, minor)) {
- log_error("_deps: failed to set minor for (%" PRIu32 ":%" PRIu32 ")",
- major, minor);
- goto failed;
- }
-
- if (!dm_task_run(*dmt)) {
- log_error("_deps: task run failed for (%" PRIu32 ":%" PRIu32 ")",
- major, minor);
- goto failed;
- }
-
- if (!dm_task_get_info(*dmt, info)) {
- log_error("_deps: failed to get info for (%" PRIu32 ":%" PRIu32 ")",
- major, minor);
- goto failed;
- }
-
- if (!info->exists) {
- *name = "";
- *uuid = "";
- *deps = NULL;
- } else {
- if (info->major != major) {
- log_error("Inconsistent dtree major number: %u != %u",
- major, info->major);
- goto failed;
- }
- if (info->minor != minor) {
- log_error("Inconsistent dtree minor number: %u != %u",
- minor, info->minor);
- goto failed;
- }
- if (!(*name = dm_pool_strdup(mem, dm_task_get_name(*dmt)))) {
- log_error("name pool_strdup failed");
- goto failed;
- }
- if (!(*uuid = dm_pool_strdup(mem, dm_task_get_uuid(*dmt)))) {
- log_error("uuid pool_strdup failed");
- goto failed;
- }
- *deps = dm_task_get_deps(*dmt);
- }
-
- return 1;
+ if (strncmp(uuid, default_uuid_prefix, default_uuid_prefix_len))
+ return NULL;
-failed:
- dm_task_destroy(*dmt);
- return 0;
+ return dm_hash_lookup(dtree->uuids, uuid + default_uuid_prefix_len);
}
-static struct dm_tree_node *_add_dev(struct dm_tree *dtree,
- struct dm_tree_node *parent,
- uint32_t major, uint32_t minor,
- uint16_t udev_flags)
-{
- struct dm_task *dmt = NULL;
- struct dm_info info;
- struct dm_deps *deps = NULL;
- const char *name = NULL;
- const char *uuid = NULL;
- struct dm_tree_node *node = NULL;
- uint32_t i;
- int new = 0;
-
- /* Already in tree? */
- if (!(node = _find_dm_tree_node(dtree, major, minor))) {
- if (!_deps(&dmt, dtree->mem, major, minor, &name, &uuid, &info, &deps))
- return_NULL;
-
- if (!(node = _create_dm_tree_node(dtree, name, uuid, &info,
- NULL, udev_flags)))
- goto_out;
- new = 1;
- }
-
- if (!_link_tree_nodes(parent, node)) {
- node = NULL;
- goto_out;
- }
-
- /* If node was already in tree, no need to recurse. */
- if (!new)
- goto out;
+void dm_tree_node_set_udev_flags(struct dm_tree_node *dnode, uint16_t udev_flags)
- /* Can't recurse if not a mapped device or there are no dependencies */
- if (!node->info.exists || !deps->count) {
- if (!_add_to_bottomlevel(node)) {
- stack;
- node = NULL;
- }
- goto out;
- }
-
- /* Add dependencies to tree */
- for (i = 0; i < deps->count; i++)
- if (!_add_dev(dtree, node, MAJOR(deps->device[i]),
- MINOR(deps->device[i]), udev_flags)) {
- node = NULL;
- goto_out;
- }
-
-out:
- if (dmt)
- dm_task_destroy(dmt);
-
- return node;
-}
-
-static int _node_clear_table(struct dm_tree_node *dnode)
{
- struct dm_task *dmt;
- struct dm_info *info;
- const char *name;
- int r;
+ struct dm_info *dinfo = &dnode->info;
- if (!(info = &dnode->info)) {
- log_error("_node_clear_table failed: missing info");
- return 0;
- }
-
- if (!(name = dm_tree_node_get_name(dnode))) {
- log_error("_node_clear_table failed: missing name");
- return 0;
- }
-
- /* Is there a table? */
- if (!info->exists || !info->inactive_table)
- return 1;
-
- log_verbose("Clearing inactive table %s (%" PRIu32 ":%" PRIu32 ")",
- name, info->major, info->minor);
-
- if (!(dmt = dm_task_create(DM_DEVICE_CLEAR))) {
- log_error("Table clear dm_task creation failed for %s", name);
- return 0;
- }
-
- if (!dm_task_set_major(dmt, info->major) ||
- !dm_task_set_minor(dmt, info->minor)) {
- log_error("Failed to set device number for %s table clear", name);
- dm_task_destroy(dmt);
- return 0;
- }
-
- r = dm_task_run(dmt);
-
- if (!dm_task_get_info(dmt, info)) {
- log_error("_node_clear_table failed: info missing after running task for %s", name);
- r = 0;
- }
-
- dm_task_destroy(dmt);
-
- return r;
+ if (udev_flags != dnode->udev_flags)
+ log_debug("Resetting %s (%" PRIu32 ":%" PRIu32
+ ") udev_flags from 0x%x to 0x%x",
+ dnode->name, dinfo->major, dinfo->minor,
+ dnode->udev_flags, udev_flags);
+ dnode->udev_flags = udev_flags;
}
-struct dm_tree_node *dm_tree_add_new_dev(struct dm_tree *dtree,
- const char *name,
- const char *uuid,
- uint32_t major, uint32_t minor,
- int read_only,
- int clear_inactive,
- void *context)
-{
- struct dm_tree_node *dnode;
- struct dm_info info;
- const char *name2;
- const char *uuid2;
-
- /* Do we need to add node to tree? */
- if (!(dnode = dm_tree_find_node_by_uuid(dtree, uuid))) {
- if (!(name2 = dm_pool_strdup(dtree->mem, name))) {
- log_error("name pool_strdup failed");
- return NULL;
- }
- if (!(uuid2 = dm_pool_strdup(dtree->mem, uuid))) {
- log_error("uuid pool_strdup failed");
- return NULL;
- }
-
- info.major = 0;
- info.minor = 0;
- info.exists = 0;
- info.live_table = 0;
- info.inactive_table = 0;
- info.read_only = 0;
-
- if (!(dnode = _create_dm_tree_node(dtree, name2, uuid2, &info,
- context, 0)))
- return_NULL;
-
- /* Attach to root node until a table is supplied */
- if (!_add_to_toplevel(dnode) || !_add_to_bottomlevel(dnode))
- return_NULL;
-
- dnode->props.major = major;
- dnode->props.minor = minor;
- dnode->props.new_name = NULL;
- dnode->props.size_changed = 0;
- } else if (strcmp(name, dnode->name)) {
- /* Do we need to rename node? */
- if (!(dnode->props.new_name = dm_pool_strdup(dtree->mem, name))) {
- log_error("name pool_strdup failed");
- return 0;
- }
- }
-
- dnode->props.read_only = read_only ? 1 : 0;
- dnode->props.read_ahead = DM_READ_AHEAD_AUTO;
- dnode->props.read_ahead_flags = 0;
-
- if (clear_inactive && !_node_clear_table(dnode))
- return_NULL;
-
- dnode->context = context;
- dnode->udev_flags = 0;
-
- return dnode;
-}
-
-struct dm_tree_node *dm_tree_add_new_dev_with_udev_flags(struct dm_tree *dtree,
- const char *name,
- const char *uuid,
- uint32_t major,
- uint32_t minor,
- int read_only,
- int clear_inactive,
- void *context,
- uint16_t udev_flags)
-{
- struct dm_tree_node *node;
-
- if ((node = dm_tree_add_new_dev(dtree, name, uuid, major, minor, read_only,
- clear_inactive, context)))
- node->udev_flags = udev_flags;
-
- return node;
-}
-
-
void dm_tree_node_set_read_ahead(struct dm_tree_node *dnode,
uint32_t read_ahead,
uint32_t read_ahead_flags)
@@ -701,17 +562,6 @@ void dm_tree_node_set_presuspend_node(struct dm_tree_node *node,
node->presuspend_node = presuspend_node;
}
-int dm_tree_add_dev(struct dm_tree *dtree, uint32_t major, uint32_t minor)
-{
- return _add_dev(dtree, &dtree->root, major, minor, 0) ? 1 : 0;
-}
-
-int dm_tree_add_dev_with_udev_flags(struct dm_tree *dtree, uint32_t major,
- uint32_t minor, uint16_t udev_flags)
-{
- return _add_dev(dtree, &dtree->root, major, minor, udev_flags) ? 1 : 0;
-}
-
const char *dm_tree_node_get_name(const struct dm_tree_node *node)
{
return node->info.exists ? node->name : "";
@@ -756,6 +606,9 @@ int dm_tree_node_num_children(const struct dm_tree_node *node, uint32_t inverted
*/
static int _uuid_prefix_matches(const char *uuid, const char *uuid_prefix, size_t uuid_prefix_len)
{
+ const char *default_uuid_prefix = dm_uuid_prefix();
+ size_t default_uuid_prefix_len = strlen(default_uuid_prefix);
+
if (!uuid_prefix)
return 1;
@@ -766,13 +619,13 @@ static int _uuid_prefix_matches(const char *uuid, const char *uuid_prefix, size_
if (uuid_prefix_len <= 4)
return 0;
- if (!strncmp(uuid, UUID_PREFIX, sizeof(UUID_PREFIX) - 1))
+ if (!strncmp(uuid, default_uuid_prefix, default_uuid_prefix_len))
return 0;
- if (strncmp(uuid_prefix, UUID_PREFIX, sizeof(UUID_PREFIX) - 1))
+ if (strncmp(uuid_prefix, default_uuid_prefix, default_uuid_prefix_len))
return 0;
- if (!strncmp(uuid, uuid_prefix + sizeof(UUID_PREFIX) - 1, uuid_prefix_len - (sizeof(UUID_PREFIX) - 1)))
+ if (!strncmp(uuid, uuid_prefix + default_uuid_prefix_len, uuid_prefix_len - default_uuid_prefix_len))
return 1;
return 0;
@@ -876,11 +729,99 @@ struct dm_tree_node *dm_tree_next_child(void **handle,
return (*dlink) ? dm_list_item(*dlink, struct dm_tree_link)->node : NULL;
}
+static int _deps(struct dm_task **dmt, struct dm_pool *mem, uint32_t major, uint32_t minor,
+ const char **name, const char **uuid, unsigned inactive_table,
+ struct dm_info *info, struct dm_deps **deps)
+{
+ memset(info, 0, sizeof(*info));
+
+ if (!dm_is_dm_major(major)) {
+ if (name)
+ *name = "";
+ if (uuid)
+ *uuid = "";
+ *deps = NULL;
+ info->major = major;
+ info->minor = minor;
+ return 1;
+ }
+
+ if (!(*dmt = dm_task_create(DM_DEVICE_DEPS))) {
+ log_error("deps dm_task creation failed");
+ return 0;
+ }
+
+ if (!dm_task_set_major(*dmt, major)) {
+ log_error("_deps: failed to set major for (%" PRIu32 ":%" PRIu32 ")",
+ major, minor);
+ goto failed;
+ }
+
+ if (!dm_task_set_minor(*dmt, minor)) {
+ log_error("_deps: failed to set minor for (%" PRIu32 ":%" PRIu32 ")",
+ major, minor);
+ goto failed;
+ }
+
+ if (inactive_table && !dm_task_query_inactive_table(*dmt)) {
+ log_error("_deps: failed to set inactive table for (%" PRIu32 ":%" PRIu32 ")",
+ major, minor);
+ goto failed;
+ }
+
+ if (!dm_task_run(*dmt)) {
+ log_error("_deps: task run failed for (%" PRIu32 ":%" PRIu32 ")",
+ major, minor);
+ goto failed;
+ }
+
+ if (!dm_task_get_info(*dmt, info)) {
+ log_error("_deps: failed to get info for (%" PRIu32 ":%" PRIu32 ")",
+ major, minor);
+ goto failed;
+ }
+
+ if (!info->exists) {
+ if (name)
+ *name = "";
+ if (uuid)
+ *uuid = "";
+ *deps = NULL;
+ } else {
+ if (info->major != major) {
+ log_error("Inconsistent dtree major number: %u != %u",
+ major, info->major);
+ goto failed;
+ }
+ if (info->minor != minor) {
+ log_error("Inconsistent dtree minor number: %u != %u",
+ minor, info->minor);
+ goto failed;
+ }
+ if (name && !(*name = dm_pool_strdup(mem, dm_task_get_name(*dmt)))) {
+ log_error("name pool_strdup failed");
+ goto failed;
+ }
+ if (uuid && !(*uuid = dm_pool_strdup(mem, dm_task_get_uuid(*dmt)))) {
+ log_error("uuid pool_strdup failed");
+ goto failed;
+ }
+ *deps = dm_task_get_deps(*dmt);
+ }
+
+ return 1;
+
+failed:
+ dm_task_destroy(*dmt);
+ return 0;
+}
+
/*
* Deactivate a device with its dependencies if the uuid prefix matches.
*/
static int _info_by_dev(uint32_t major, uint32_t minor, int with_open_count,
- struct dm_info *info)
+ struct dm_info *info, struct dm_pool *mem,
+ const char **name, const char **uuid)
{
struct dm_task *dmt;
int r;
@@ -899,14 +840,61 @@ static int _info_by_dev(uint32_t major, uint32_t minor, int with_open_count,
if (!with_open_count && !dm_task_no_open_count(dmt))
log_error("Failed to disable open_count");
- if ((r = dm_task_run(dmt)))
- r = dm_task_get_info(dmt, info);
+ if (!(r = dm_task_run(dmt)))
+ goto_out;
+ if (!(r = dm_task_get_info(dmt, info)))
+ goto_out;
+
+ if (name && !(*name = dm_pool_strdup(mem, dm_task_get_name(dmt)))) {
+ log_error("name pool_strdup failed");
+ r = 0;
+ goto_out;
+ }
+
+ if (uuid && !(*uuid = dm_pool_strdup(mem, dm_task_get_uuid(dmt)))) {
+ log_error("uuid pool_strdup failed");
+ r = 0;
+ goto_out;
+ }
+
+out:
dm_task_destroy(dmt);
return r;
}
+static int _check_device_not_in_use(const char *name, struct dm_info *info)
+{
+ if (!info->exists)
+ return 1;
+
+ /* If sysfs is not used, use open_count information only. */
+ if (!*dm_sysfs_dir()) {
+ if (info->open_count) {
+ log_error("Device %s (%" PRIu32 ":%" PRIu32 ") in use",
+ name, info->major, info->minor);
+ return 0;
+ }
+
+ return 1;
+ }
+
+ if (dm_device_has_holders(info->major, info->minor)) {
+ log_error("Device %s (%" PRIu32 ":%" PRIu32 ") is used "
+ "by another device.", name, info->major, info->minor);
+ return 0;
+ }
+
+ if (dm_device_has_mounted_fs(info->major, info->minor)) {
+ log_error("Device %s (%" PRIu32 ":%" PRIu32 ") contains "
+ "a filesystem in use.", name, info->major, info->minor);
+ return 0;
+ }
+
+ return 1;
+}
+
/* Check if all parent nodes of given node have open_count == 0 */
static int _node_has_closed_parents(struct dm_tree_node *node,
const char *uuid_prefix,
@@ -934,19 +922,22 @@ static int _node_has_closed_parents(struct dm_tree_node *node,
}
/* Refresh open_count */
- if (!_info_by_dev(dinfo->major, dinfo->minor, 1, &info) ||
+ if (!_info_by_dev(dinfo->major, dinfo->minor, 1, &info, NULL, NULL, NULL) ||
!info.exists)
continue;
- if (info.open_count)
+ if (info.open_count) {
+ log_debug("Node %s %d:%d has open_count %d", uuid_prefix,
+ dinfo->major, dinfo->minor, info.open_count);
return 0;
+ }
}
return 1;
}
static int _deactivate_node(const char *name, uint32_t major, uint32_t minor,
- uint32_t *cookie, uint16_t udev_flags)
+ uint32_t *cookie, uint16_t udev_flags, int retry)
{
struct dm_task *dmt;
int r = 0;
@@ -966,14 +957,18 @@ static int _deactivate_node(const char *name, uint32_t major, uint32_t minor,
if (!dm_task_no_open_count(dmt))
log_error("Failed to disable open_count");
- if (!dm_task_set_cookie(dmt, cookie, udev_flags))
- goto out;
+ if (cookie)
+ if (!dm_task_set_cookie(dmt, cookie, udev_flags))
+ goto out;
+
+ if (retry)
+ dm_task_retry_remove(dmt);
r = dm_task_run(dmt);
- /* FIXME Until kernel returns actual name so dm-ioctl.c can handle it */
- rm_dev_node(name, dmt->cookie_set &&
- !(udev_flags & DM_UDEV_DISABLE_DM_RULES_FLAG));
+ /* FIXME Until kernel returns actual name so dm-iface.c can handle it */
+ rm_dev_node(name, dmt->cookie_set && !(udev_flags & DM_UDEV_DISABLE_DM_RULES_FLAG),
+ dmt->cookie_set && (udev_flags & DM_UDEV_DISABLE_LIBRARY_FALLBACK));
/* FIXME Remove node from tree or mark invalid? */
@@ -983,6 +978,244 @@ out:
return r;
}
+static int _node_clear_table(struct dm_tree_node *dnode, uint16_t udev_flags)
+{
+ struct dm_task *dmt = NULL, *deps_dmt = NULL;
+ struct dm_info *info, deps_info;
+ struct dm_deps *deps = NULL;
+ const char *name, *uuid;
+ const char *default_uuid_prefix;
+ size_t default_uuid_prefix_len;
+ uint32_t i;
+ int r = 0;
+
+ if (!(info = &dnode->info)) {
+ log_error("_node_clear_table failed: missing info");
+ return 0;
+ }
+
+ if (!(name = dm_tree_node_get_name(dnode))) {
+ log_error("_node_clear_table failed: missing name");
+ return 0;
+ }
+
+ /* Is there a table? */
+ if (!info->exists || !info->inactive_table)
+ return 1;
+
+ /* Get devices used by inactive table that's about to be deleted. */
+ if (!_deps(&deps_dmt, dnode->dtree->mem, info->major, info->minor, NULL, NULL, 1, info, &deps)) {
+ log_error("Failed to obtain dependencies for %s before clearing table.", name);
+ return 0;
+ }
+
+ log_verbose("Clearing inactive table %s (%" PRIu32 ":%" PRIu32 ")",
+ name, info->major, info->minor);
+
+ if (!(dmt = dm_task_create(DM_DEVICE_CLEAR))) {
+ log_error("Table clear dm_task creation failed for %s", name);
+ goto_out;
+ }
+
+ if (!dm_task_set_major(dmt, info->major) ||
+ !dm_task_set_minor(dmt, info->minor)) {
+ log_error("Failed to set device number for %s table clear", name);
+ goto_out;
+ }
+
+ r = dm_task_run(dmt);
+
+ if (!dm_task_get_info(dmt, info)) {
+ log_error("_node_clear_table failed: info missing after running task for %s", name);
+ r = 0;
+ }
+
+ if (!r || !deps)
+ goto_out;
+
+ /*
+ * Remove (incomplete) devices that the inactive table referred to but
+ * which are not in the tree, no longer referenced and don't have a live
+ * table.
+ */
+ default_uuid_prefix = dm_uuid_prefix();
+ default_uuid_prefix_len = strlen(default_uuid_prefix);
+
+ for (i = 0; i < deps->count; i++) {
+ /* If already in tree, assume it's under control */
+ if (_find_dm_tree_node(dnode->dtree, MAJOR(deps->device[i]), MINOR(deps->device[i])))
+ continue;
+
+ if (!_info_by_dev(MAJOR(deps->device[i]), MINOR(deps->device[i]), 1,
+ &deps_info, dnode->dtree->mem, &name, &uuid))
+ continue;
+
+ /* Proceed if device is an 'orphan' - unreferenced and without a live table. */
+ if (!deps_info.exists || deps_info.live_table || deps_info.open_count)
+ continue;
+
+ if (strncmp(uuid, default_uuid_prefix, default_uuid_prefix_len))
+ continue;
+
+ /* Remove device. */
+ if (!_deactivate_node(name, deps_info.major, deps_info.minor, &dnode->dtree->cookie, udev_flags, 0)) {
+ log_error("Failed to deactivate no-longer-used device %s (%"
+ PRIu32 ":%" PRIu32 ")", name, deps_info.major, deps_info.minor);
+ } else if (deps_info.suspended)
+ dec_suspended();
+ }
+
+out:
+ if (dmt)
+ dm_task_destroy(dmt);
+
+ if (deps_dmt)
+ dm_task_destroy(deps_dmt);
+
+ return r;
+}
+
+struct dm_tree_node *dm_tree_add_new_dev_with_udev_flags(struct dm_tree *dtree,
+ const char *name,
+ const char *uuid,
+ uint32_t major,
+ uint32_t minor,
+ int read_only,
+ int clear_inactive,
+ void *context,
+ uint16_t udev_flags)
+{
+ struct dm_tree_node *dnode;
+ struct dm_info info = { 0 };
+ const char *name2;
+ const char *uuid2;
+
+ if (!name || !uuid) {
+ log_error("Cannot add device without name and uuid.");
+ return NULL;
+ }
+
+ /* Do we need to add node to tree? */
+ if (!(dnode = dm_tree_find_node_by_uuid(dtree, uuid))) {
+ if (!(name2 = dm_pool_strdup(dtree->mem, name))) {
+ log_error("name pool_strdup failed");
+ return NULL;
+ }
+ if (!(uuid2 = dm_pool_strdup(dtree->mem, uuid))) {
+ log_error("uuid pool_strdup failed");
+ return NULL;
+ }
+
+ if (!(dnode = _create_dm_tree_node(dtree, name2, uuid2, &info,
+ context, 0)))
+ return_NULL;
+
+ /* Attach to root node until a table is supplied */
+ if (!_add_to_toplevel(dnode) || !_add_to_bottomlevel(dnode))
+ return_NULL;
+
+ dnode->props.major = major;
+ dnode->props.minor = minor;
+ dnode->props.new_name = NULL;
+ dnode->props.size_changed = 0;
+ } else if (strcmp(name, dnode->name)) {
+ /* Do we need to rename node? */
+ if (!(dnode->props.new_name = dm_pool_strdup(dtree->mem, name))) {
+ log_error("name pool_strdup failed");
+ return NULL;
+ }
+ }
+
+ dnode->props.read_only = read_only ? 1 : 0;
+ dnode->props.read_ahead = DM_READ_AHEAD_AUTO;
+ dnode->props.read_ahead_flags = 0;
+
+ if (clear_inactive && !_node_clear_table(dnode, udev_flags))
+ return_NULL;
+
+ dnode->context = context;
+ dnode->udev_flags = udev_flags;
+
+ return dnode;
+}
+
+struct dm_tree_node *dm_tree_add_new_dev(struct dm_tree *dtree, const char *name,
+ const char *uuid, uint32_t major, uint32_t minor,
+ int read_only, int clear_inactive, void *context)
+{
+ return dm_tree_add_new_dev_with_udev_flags(dtree, name, uuid, major, minor,
+ read_only, clear_inactive, context, 0);
+}
+
+static struct dm_tree_node *_add_dev(struct dm_tree *dtree,
+ struct dm_tree_node *parent,
+ uint32_t major, uint32_t minor,
+ uint16_t udev_flags)
+{
+ struct dm_task *dmt = NULL;
+ struct dm_info info;
+ struct dm_deps *deps = NULL;
+ const char *name = NULL;
+ const char *uuid = NULL;
+ struct dm_tree_node *node = NULL;
+ uint32_t i;
+ int new = 0;
+
+ /* Already in tree? */
+ if (!(node = _find_dm_tree_node(dtree, major, minor))) {
+ if (!_deps(&dmt, dtree->mem, major, minor, &name, &uuid, 0, &info, &deps))
+ return_NULL;
+
+ if (!(node = _create_dm_tree_node(dtree, name, uuid, &info,
+ NULL, udev_flags)))
+ goto_out;
+ new = 1;
+ }
+
+ if (!_link_tree_nodes(parent, node)) {
+ node = NULL;
+ goto_out;
+ }
+
+ /* If node was already in tree, no need to recurse. */
+ if (!new)
+ goto out;
+
+ /* Can't recurse if not a mapped device or there are no dependencies */
+ if (!node->info.exists || !deps || !deps->count) {
+ if (!_add_to_bottomlevel(node)) {
+ stack;
+ node = NULL;
+ }
+ goto out;
+ }
+
+ /* Add dependencies to tree */
+ for (i = 0; i < deps->count; i++)
+ if (!_add_dev(dtree, node, MAJOR(deps->device[i]),
+ MINOR(deps->device[i]), udev_flags)) {
+ node = NULL;
+ goto_out;
+ }
+
+out:
+ if (dmt)
+ dm_task_destroy(dmt);
+
+ return node;
+}
+
+int dm_tree_add_dev(struct dm_tree *dtree, uint32_t major, uint32_t minor)
+{
+ return _add_dev(dtree, &dtree->root, major, minor, 0) ? 1 : 0;
+}
+
+int dm_tree_add_dev_with_udev_flags(struct dm_tree *dtree, uint32_t major,
+ uint32_t minor, uint16_t udev_flags)
+{
+ return _add_dev(dtree, &dtree->root, major, minor, udev_flags) ? 1 : 0;
+}
+
static int _rename_node(const char *old_name, const char *new_name, uint32_t major,
uint32_t minor, uint32_t *cookie, uint16_t udev_flags)
{
@@ -1002,7 +1235,7 @@ static int _rename_node(const char *old_name, const char *new_name, uint32_t maj
}
if (!dm_task_set_newname(dmt, new_name))
- goto_out;
+ goto_out;
if (!dm_task_no_open_count(dmt))
log_error("Failed to disable open_count");
@@ -1022,7 +1255,7 @@ out:
static int _resume_node(const char *name, uint32_t major, uint32_t minor,
uint32_t read_ahead, uint32_t read_ahead_flags,
struct dm_info *newinfo, uint32_t *cookie,
- uint16_t udev_flags)
+ uint16_t udev_flags, int already_suspended)
{
struct dm_task *dmt;
int r = 0;
@@ -1030,13 +1263,13 @@ static int _resume_node(const char *name, uint32_t major, uint32_t minor,
log_verbose("Resuming %s (%" PRIu32 ":%" PRIu32 ")", name, major, minor);
if (!(dmt = dm_task_create(DM_DEVICE_RESUME))) {
- log_error("Suspend dm_task creation failed for %s", name);
+ log_debug("Suspend dm_task creation failed for %s.", name);
return 0;
}
/* FIXME Kernel should fill in name on return instead */
if (!dm_task_set_name(dmt, name)) {
- log_error("Failed to set readahead device name for %s", name);
+ log_debug("Failed to set device name for %s resumption.", name);
goto out;
}
@@ -1052,10 +1285,16 @@ static int _resume_node(const char *name, uint32_t major, uint32_t minor,
log_error("Failed to set read ahead");
if (!dm_task_set_cookie(dmt, cookie, udev_flags))
- goto out;
+ goto_out;
- if ((r = dm_task_run(dmt)))
- r = dm_task_get_info(dmt, newinfo);
+ if (!(r = dm_task_run(dmt)))
+ goto_out;
+
+ if (already_suspended)
+ dec_suspended();
+
+ if (!(r = dm_task_get_info(dmt, newinfo)))
+ stack;
out:
dm_task_destroy(dmt);
@@ -1094,14 +1333,176 @@ static int _suspend_node(const char *name, uint32_t major, uint32_t minor,
if (no_flush && !dm_task_no_flush(dmt))
log_error("Failed to set no_flush flag.");
- if ((r = dm_task_run(dmt)))
+ if ((r = dm_task_run(dmt))) {
+ inc_suspended();
r = dm_task_get_info(dmt, newinfo);
+ }
+
+ dm_task_destroy(dmt);
+
+ return r;
+}
+
+static int _thin_pool_status_transaction_id(struct dm_tree_node *dnode, uint64_t *transaction_id)
+{
+ struct dm_task *dmt;
+ int r = 0;
+ uint64_t start, length;
+ char *type = NULL;
+ char *params = NULL;
+
+ if (!(dmt = dm_task_create(DM_DEVICE_STATUS)))
+ return_0;
+
+ if (!dm_task_set_major(dmt, dnode->info.major) ||
+ !dm_task_set_minor(dmt, dnode->info.minor)) {
+ log_error("Failed to set major minor.");
+ goto out;
+ }
+
+ if (!dm_task_run(dmt))
+ goto_out;
+
+ dm_get_next_target(dmt, NULL, &start, &length, &type, &params);
+
+ if (type && (strcmp(type, "thin-pool") != 0)) {
+ log_error("Expected thin-pool target for %d:%d and got %s.",
+ dnode->info.major, dnode->info.minor, type);
+ goto out;
+ }
+
+ if (!params || (sscanf(params, "%" PRIu64, transaction_id) != 1)) {
+ log_error("Failed to parse transaction_id from %s.", params);
+ goto out;
+ }
+
+ log_debug("Thin pool transaction id: %" PRIu64 " status: %s.", *transaction_id, params);
+ r = 1;
+out:
dm_task_destroy(dmt);
return r;
}
+static int _thin_pool_node_message(struct dm_tree_node *dnode, struct thin_message *tm)
+{
+ struct dm_task *dmt;
+ struct dm_thin_message *m = &tm->message;
+ char buf[64];
+ int r;
+
+ switch (m->type) {
+ case DM_THIN_MESSAGE_CREATE_SNAP:
+ r = dm_snprintf(buf, sizeof(buf), "create_snap %u %u",
+ m->u.m_create_snap.device_id,
+ m->u.m_create_snap.origin_id);
+ break;
+ case DM_THIN_MESSAGE_CREATE_THIN:
+ r = dm_snprintf(buf, sizeof(buf), "create_thin %u",
+ m->u.m_create_thin.device_id);
+ break;
+ case DM_THIN_MESSAGE_DELETE:
+ r = dm_snprintf(buf, sizeof(buf), "delete %u",
+ m->u.m_delete.device_id);
+ break;
+ case DM_THIN_MESSAGE_SET_TRANSACTION_ID:
+ r = dm_snprintf(buf, sizeof(buf),
+ "set_transaction_id %" PRIu64 " %" PRIu64,
+ m->u.m_set_transaction_id.current_id,
+ m->u.m_set_transaction_id.new_id);
+ break;
+ case DM_THIN_MESSAGE_RESERVE_METADATA_SNAP: /* target vsn 1.1 */
+ r = dm_snprintf(buf, sizeof(buf), "reserve_metadata_snap");
+ break;
+ case DM_THIN_MESSAGE_RELEASE_METADATA_SNAP: /* target vsn 1.1 */
+ r = dm_snprintf(buf, sizeof(buf), "release_metadata_snap");
+ break;
+ default:
+ r = -1;
+ }
+
+ if (r < 0) {
+ log_error("Failed to prepare message.");
+ return 0;
+ }
+
+ r = 0;
+
+ if (!(dmt = dm_task_create(DM_DEVICE_TARGET_MSG)))
+ return_0;
+
+ if (!dm_task_set_major(dmt, dnode->info.major) ||
+ !dm_task_set_minor(dmt, dnode->info.minor)) {
+ log_error("Failed to set message major minor.");
+ goto out;
+ }
+
+ if (!dm_task_set_message(dmt, buf))
+ goto_out;
+
+ /* Internal functionality of dm_task */
+ dmt->expected_errno = tm->expected_errno;
+
+ if (!dm_task_run(dmt))
+ goto_out;
+
+ r = 1;
+out:
+ dm_task_destroy(dmt);
+
+ return r;
+}
+
+static int _node_send_messages(struct dm_tree_node *dnode,
+ const char *uuid_prefix,
+ size_t uuid_prefix_len)
+{
+ struct load_segment *seg;
+ struct thin_message *tmsg;
+ uint64_t trans_id;
+ const char *uuid;
+
+ if (!dnode->info.exists || (dm_list_size(&dnode->props.segs) != 1))
+ return 1;
+
+ seg = dm_list_item(dm_list_last(&dnode->props.segs), struct load_segment);
+ if (seg->type != SEG_THIN_POOL)
+ return 1;
+
+ if (!(uuid = dm_tree_node_get_uuid(dnode)))
+ return_0;
+
+ if (!_uuid_prefix_matches(uuid, uuid_prefix, uuid_prefix_len)) {
+ log_debug("UUID \"%s\" does not match.", uuid);
+ return 1;
+ }
+
+ if (!_thin_pool_status_transaction_id(dnode, &trans_id))
+ goto_bad;
+
+ if (trans_id == seg->transaction_id)
+ return 1; /* In sync - skip messages */
+
+ if (trans_id != (seg->transaction_id - 1)) {
+ log_error("Thin pool transaction_id=%" PRIu64 ", while expected: %" PRIu64 ".",
+ trans_id, seg->transaction_id - 1);
+ goto bad; /* Nothing to send */
+ }
+
+ dm_list_iterate_items(tmsg, &seg->thin_messages)
+ if (!(_thin_pool_node_message(dnode, tmsg)))
+ goto_bad;
+
+ return 1;
+bad:
+ /* Try to deactivate */
+ if (!(dm_tree_deactivate_children(dnode, uuid_prefix, uuid_prefix_len)))
+ log_error("Failed to deactivate %s", dnode->name);
+
+ return 0;
+}
+
/*
* FIXME Don't attempt to deactivate known internal dependencies.
*/
@@ -1139,13 +1540,34 @@ static int _dm_tree_deactivate_children(struct dm_tree_node *dnode,
continue;
/* Refresh open_count */
- if (!_info_by_dev(dinfo->major, dinfo->minor, 1, &info) ||
+ if (!_info_by_dev(dinfo->major, dinfo->minor, 1, &info, NULL, NULL, NULL) ||
!info.exists)
continue;
+ if (info.open_count) {
+ /* Skip internal non-toplevel opened nodes */
+ if (level)
+ continue;
+
+ /* When retry is not allowed, error */
+ if (!child->dtree->retry_remove) {
+ log_error("Unable to deactivate open %s (%" PRIu32
+ ":%" PRIu32 ")", name, info.major, info.minor);
+ r = 0;
+ continue;
+ }
+
+ /* Check toplevel node for holders/mounted fs */
+ if (!_check_device_not_in_use(name, &info)) {
+ stack;
+ r = 0;
+ continue;
+ }
+ /* Go on with retry */
+ }
+
/* Also checking open_count in parent nodes of presuspend_node */
- if (info.open_count ||
- (child->presuspend_node &&
+ if ((child->presuspend_node &&
!_node_has_closed_parents(child->presuspend_node,
uuid_prefix, uuid_prefix_len))) {
/* Only report error from (likely non-internal) dependency at top level */
@@ -1164,18 +1586,27 @@ static int _dm_tree_deactivate_children(struct dm_tree_node *dnode,
continue;
if (!_deactivate_node(name, info.major, info.minor,
- &child->dtree->cookie, child->udev_flags)) {
+ &child->dtree->cookie, child->udev_flags,
+ (level == 0) ? child->dtree->retry_remove : 0)) {
log_error("Unable to deactivate %s (%" PRIu32
":%" PRIu32 ")", name, info.major,
info.minor);
r = 0;
continue;
- }
+ } else if (info.suspended)
+ dec_suspended();
- if (dm_tree_node_num_children(child, 0)) {
- if (!_dm_tree_deactivate_children(child, uuid_prefix, uuid_prefix_len, level + 1))
- return_0;
- }
+ if (child->callback &&
+ !child->callback(child, DM_NODE_CALLBACK_DEACTIVATED,
+ child->callback_data))
+ stack;
+ // FIXME: We need to let lvremove pass,
+ // so for now deactivation ignores check result
+ //r = 0; // FIXME: _node_clear_table() without callback ?
+
+ if (dm_tree_node_num_children(child, 0) &&
+ !_dm_tree_deactivate_children(child, uuid_prefix, uuid_prefix_len, level + 1))
+ return_0;
}
return r;
@@ -1188,16 +1619,6 @@ int dm_tree_deactivate_children(struct dm_tree_node *dnode,
return _dm_tree_deactivate_children(dnode, uuid_prefix, uuid_prefix_len, 0);
}
-void dm_tree_skip_lockfs(struct dm_tree_node *dnode)
-{
- dnode->dtree->skip_lockfs = 1;
-}
-
-void dm_tree_use_no_flush_suspend(struct dm_tree_node *dnode)
-{
- dnode->dtree->no_flush = 1;
-}
-
int dm_tree_suspend_children(struct dm_tree_node *dnode,
const char *uuid_prefix,
size_t uuid_prefix_len)
@@ -1235,7 +1656,7 @@ int dm_tree_suspend_children(struct dm_tree_node *dnode,
if (!_children_suspended(child, 1, uuid_prefix, uuid_prefix_len))
continue;
- if (!_info_by_dev(dinfo->major, dinfo->minor, 0, &info) ||
+ if (!_info_by_dev(dinfo->major, dinfo->minor, 0, &info, NULL, NULL, NULL) ||
!info.exists || info.suspended)
continue;
@@ -1305,6 +1726,9 @@ int dm_tree_activate_children(struct dm_tree_node *dnode,
for (priority = 0; priority < 3; priority++) {
while ((child = dm_tree_next_child(&handle, dnode, 0))) {
+ if (priority != child->activation_priority)
+ continue;
+
if (!(uuid = dm_tree_node_get_uuid(child))) {
stack;
continue;
@@ -1313,9 +1737,6 @@ int dm_tree_activate_children(struct dm_tree_node *dnode,
if (!_uuid_prefix_matches(uuid, uuid_prefix, uuid_prefix_len))
continue;
- if (priority != child->activation_priority)
- continue;
-
if (!(name = dm_tree_node_get_name(child))) {
stack;
continue;
@@ -1340,7 +1761,7 @@ int dm_tree_activate_children(struct dm_tree_node *dnode,
if (!_resume_node(child->name, child->info.major, child->info.minor,
child->props.read_ahead, child->props.read_ahead_flags,
- &newinfo, &child->dtree->cookie, child->udev_flags)) {
+ &newinfo, &child->dtree->cookie, child->udev_flags, child->info.suspended)) {
log_error("Unable to resume %s (%" PRIu32
":%" PRIu32 ")", child->name, child->info.major,
child->info.minor);
@@ -1353,6 +1774,17 @@ int dm_tree_activate_children(struct dm_tree_node *dnode,
}
}
+ /*
+ * FIXME: Implement delayed error reporting
+ * activation should be stopped only in the case,
+ * the submission of transation_id message fails,
+ * resume should continue further, just whole command
+ * has to report failure.
+ */
+ if (r && dnode->props.send_messages &&
+ !(r = _node_send_messages(dnode, uuid_prefix, uuid_prefix_len)))
+ stack;
+
handle = NULL;
return r;
@@ -1408,10 +1840,10 @@ out:
static int _build_dev_string(char *devbuf, size_t bufsize, struct dm_tree_node *node)
{
if (!dm_format_dev(devbuf, bufsize, node->info.major, node->info.minor)) {
- log_error("Failed to format %s device number for %s as dm "
- "target (%u,%u)",
- node->name, node->uuid, node->info.major, node->info.minor);
- return 0;
+ log_error("Failed to format %s device number for %s as dm "
+ "target (%u,%u)",
+ node->name, node->uuid, node->info.major, node->info.minor);
+ return 0;
}
return 1;
@@ -1444,11 +1876,11 @@ static int _emit_areas_line(struct dm_task *dmt __attribute__((unused)),
unsigned log_parm_count;
dm_list_iterate_items(area, &seg->areas) {
- if (!_build_dev_string(devbuf, sizeof(devbuf), area->dev_node))
- return_0;
-
switch (seg->type) {
case SEG_REPLICATOR_DEV:
+ if (!_build_dev_string(devbuf, sizeof(devbuf), area->dev_node))
+ return_0;
+
EMIT_PARAMS(*pos, " %d 1 %s", area->rsite_index, devbuf);
if (first_time)
EMIT_PARAMS(*pos, " nolog 0");
@@ -1481,7 +1913,29 @@ static int _emit_areas_line(struct dm_task *dmt __attribute__((unused)),
EMIT_PARAMS(*pos, "%s", synctype);
}
break;
+ case SEG_RAID1:
+ case SEG_RAID10:
+ case SEG_RAID4:
+ case SEG_RAID5_LA:
+ case SEG_RAID5_RA:
+ case SEG_RAID5_LS:
+ case SEG_RAID5_RS:
+ case SEG_RAID6_ZR:
+ case SEG_RAID6_NR:
+ case SEG_RAID6_NC:
+ if (!area->dev_node) {
+ EMIT_PARAMS(*pos, " -");
+ break;
+ }
+ if (!_build_dev_string(devbuf, sizeof(devbuf), area->dev_node))
+ return_0;
+
+ EMIT_PARAMS(*pos, " %s", devbuf);
+ break;
default:
+ if (!_build_dev_string(devbuf, sizeof(devbuf), area->dev_node))
+ return_0;
+
EMIT_PARAMS(*pos, "%s%s %" PRIu64, first_time ? "" : " ",
devbuf, area->offset);
}
@@ -1531,23 +1985,28 @@ static int _replicator_emit_segment_line(const struct load_segment *seg, char *p
/*
* Returns: 1 on success, 0 on failure
*/
-static int _mirror_emit_segment_line(struct dm_task *dmt, uint32_t major,
- uint32_t minor, struct load_segment *seg,
- uint64_t *seg_start, char *params,
- size_t paramsize)
+static int _mirror_emit_segment_line(struct dm_task *dmt, struct load_segment *seg,
+ char *params, size_t paramsize)
{
int block_on_error = 0;
int handle_errors = 0;
int dm_log_userspace = 0;
struct utsname uts;
unsigned log_parm_count;
- int pos = 0;
+ int pos = 0, parts;
char logbuf[DM_FORMAT_DEV_BUFSIZE];
const char *logtype;
- unsigned kmaj, kmin, krel;
+ unsigned kmaj = 0, kmin = 0, krel = 0;
- if (uname(&uts) == -1 || sscanf(uts.release, "%u.%u.%u", &kmaj, &kmin, &krel) != 3) {
- log_error("Cannot read kernel release version");
+ if (uname(&uts) == -1) {
+ log_error("Cannot read kernel release version.");
+ return 0;
+ }
+
+ /* Kernels with a major number of 2 always had 3 parts. */
+ parts = sscanf(uts.release, "%u.%u.%u", &kmaj, &kmin, &krel);
+ if (parts < 1 || (kmaj < 3 && parts < 3)) {
+ log_error("Wrong kernel release version %s.", uts.release);
return 0;
}
@@ -1651,6 +2110,103 @@ static int _mirror_emit_segment_line(struct dm_task *dmt, uint32_t major,
return 1;
}
+static int _raid_emit_segment_line(struct dm_task *dmt, uint32_t major,
+ uint32_t minor, struct load_segment *seg,
+ uint64_t *seg_start, char *params,
+ size_t paramsize)
+{
+ uint32_t i;
+ int param_count = 1; /* mandatory 'chunk size'/'stripe size' arg */
+ int pos = 0;
+
+ if ((seg->flags & DM_NOSYNC) || (seg->flags & DM_FORCESYNC))
+ param_count++;
+
+ if (seg->region_size)
+ param_count += 2;
+
+ /* rebuilds is 64-bit */
+ param_count += 2 * hweight32(seg->rebuilds & 0xFFFFFFFF);
+ param_count += 2 * hweight32(seg->rebuilds >> 32);
+
+ if ((seg->type == SEG_RAID1) && seg->stripe_size)
+ log_error("WARNING: Ignoring RAID1 stripe size");
+
+ EMIT_PARAMS(pos, "%s %d %u", dm_segtypes[seg->type].target,
+ param_count, seg->stripe_size);
+
+ if (seg->flags & DM_NOSYNC)
+ EMIT_PARAMS(pos, " nosync");
+ else if (seg->flags & DM_FORCESYNC)
+ EMIT_PARAMS(pos, " sync");
+
+ if (seg->region_size)
+ EMIT_PARAMS(pos, " region_size %u", seg->region_size);
+
+ for (i = 0; i < (seg->area_count / 2); i++)
+ if (seg->rebuilds & (1 << i))
+ EMIT_PARAMS(pos, " rebuild %u", i);
+
+ /* Print number of metadata/data device pairs */
+ EMIT_PARAMS(pos, " %u", seg->area_count/2);
+
+ if (_emit_areas_line(dmt, seg, params, paramsize, &pos) <= 0)
+ return_0;
+
+ return 1;
+}
+
+static int _thin_pool_emit_segment_line(struct dm_task *dmt,
+ struct load_segment *seg,
+ char *params, size_t paramsize)
+{
+ int pos = 0;
+ char pool[DM_FORMAT_DEV_BUFSIZE], metadata[DM_FORMAT_DEV_BUFSIZE];
+ int features = (seg->skip_block_zeroing ? 1 : 0) +
+ (seg->ignore_discard ? 1 : 0) +
+ (seg->no_discard_passdown ? 1 : 0);
+
+ if (!_build_dev_string(metadata, sizeof(metadata), seg->metadata))
+ return_0;
+
+ if (!_build_dev_string(pool, sizeof(pool), seg->pool))
+ return_0;
+
+ EMIT_PARAMS(pos, "%s %s %d %" PRIu64 " %d%s%s%s", metadata, pool,
+ seg->data_block_size, seg->low_water_mark, features,
+ seg->skip_block_zeroing ? " skip_block_zeroing" : "",
+ seg->ignore_discard ? " ignore_discard" : "",
+ seg->no_discard_passdown ? " no_discard_passdown" : ""
+ );
+
+ return 1;
+}
+
+static int _thin_emit_segment_line(struct dm_task *dmt,
+ struct load_segment *seg,
+ char *params, size_t paramsize)
+{
+ int pos = 0;
+ char pool[DM_FORMAT_DEV_BUFSIZE];
+ char external[DM_FORMAT_DEV_BUFSIZE + 1];
+
+ if (!_build_dev_string(pool, sizeof(pool), seg->pool))
+ return_0;
+
+ if (!seg->external)
+ *external = 0;
+ else {
+ *external = ' ';
+ if (!_build_dev_string(external + 1, sizeof(external) - 1,
+ seg->external))
+ return_0;
+ }
+
+ EMIT_PARAMS(pos, "%s %d%s", pool, seg->device_id, external);
+
+ return 1;
+}
+
static int _emit_segment_line(struct dm_task *dmt, uint32_t major,
uint32_t minor, struct load_segment *seg,
uint64_t *seg_start, char *params,
@@ -1658,6 +2214,7 @@ static int _emit_segment_line(struct dm_task *dmt, uint32_t major,
{
int pos = 0;
int r;
+ int target_type_is_raid = 0;
char originbuf[DM_FORMAT_DEV_BUFSIZE], cowbuf[DM_FORMAT_DEV_BUFSIZE];
switch(seg->type) {
@@ -1667,8 +2224,7 @@ static int _emit_segment_line(struct dm_task *dmt, uint32_t major,
break;
case SEG_MIRRORED:
/* Mirrors are pretty complicated - now in separate function */
- r = _mirror_emit_segment_line(dmt, major, minor, seg, seg_start,
- params, paramsize);
+ r = _mirror_emit_segment_line(dmt, seg, params, paramsize);
if (!r)
return_0;
break;
@@ -1711,6 +2267,31 @@ static int _emit_segment_line(struct dm_task *dmt, uint32_t major,
seg->iv_offset != DM_CRYPT_IV_DEFAULT ?
seg->iv_offset : *seg_start);
break;
+ case SEG_RAID1:
+ case SEG_RAID10:
+ case SEG_RAID4:
+ case SEG_RAID5_LA:
+ case SEG_RAID5_RA:
+ case SEG_RAID5_LS:
+ case SEG_RAID5_RS:
+ case SEG_RAID6_ZR:
+ case SEG_RAID6_NR:
+ case SEG_RAID6_NC:
+ target_type_is_raid = 1;
+ r = _raid_emit_segment_line(dmt, major, minor, seg, seg_start,
+ params, paramsize);
+ if (!r)
+ return_0;
+
+ break;
+ case SEG_THIN_POOL:
+ if (!_thin_pool_emit_segment_line(dmt, seg, params, paramsize))
+ return_0;
+ break;
+ case SEG_THIN:
+ if (!_thin_emit_segment_line(dmt, seg, params, paramsize))
+ return_0;
+ break;
}
switch(seg->type) {
@@ -1720,6 +2301,8 @@ static int _emit_segment_line(struct dm_task *dmt, uint32_t major,
case SEG_SNAPSHOT_ORIGIN:
case SEG_SNAPSHOT_MERGE:
case SEG_ZERO:
+ case SEG_THIN_POOL:
+ case SEG_THIN:
break;
case SEG_CRYPT:
case SEG_LINEAR:
@@ -1729,14 +2312,23 @@ static int _emit_segment_line(struct dm_task *dmt, uint32_t major,
stack;
return r;
}
+ if (!params[0]) {
+ log_error("No parameters supplied for %s target "
+ "%u:%u.", dm_segtypes[seg->type].target,
+ major, minor);
+ return 0;
+ }
break;
}
log_debug("Adding target to (%" PRIu32 ":%" PRIu32 "): %" PRIu64
" %" PRIu64 " %s %s", major, minor,
- *seg_start, seg->size, dm_segtypes[seg->type].target, params);
+ *seg_start, seg->size, target_type_is_raid ? "raid" :
+ dm_segtypes[seg->type].target, params);
- if (!dm_task_add_target(dmt, *seg_start, seg->size, dm_segtypes[seg->type].target, params))
+ if (!dm_task_add_target(dmt, *seg_start, seg->size,
+ target_type_is_raid ? "raid" :
+ dm_segtypes[seg->type].target, params))
return_0;
*seg_start += seg->size;
@@ -1785,7 +2377,7 @@ static int _load_node(struct dm_tree_node *dnode)
int r = 0;
struct dm_task *dmt;
struct load_segment *seg;
- uint64_t seg_start = 0;
+ uint64_t seg_start = 0, existing_table_size;
log_verbose("Loading %s table (%" PRIu32 ":%" PRIu32 ")", dnode->name,
dnode->info.major, dnode->info.minor);
@@ -1820,15 +2412,28 @@ static int _load_node(struct dm_tree_node *dnode)
if ((r = dm_task_run(dmt))) {
r = dm_task_get_info(dmt, &dnode->info);
if (r && !dnode->info.inactive_table)
- log_verbose("Suppressed %s identical table reload.",
- dnode->name);
+ log_verbose("Suppressed %s (%" PRIu32 ":%" PRIu32
+ ") identical table reload.",
+ dnode->name,
+ dnode->info.major, dnode->info.minor);
+ existing_table_size = dm_task_get_existing_table_size(dmt);
if ((dnode->props.size_changed =
- (dm_task_get_existing_table_size(dmt) == seg_start) ? 0 : 1))
+ (existing_table_size == seg_start) ? 0 : 1)) {
+ /*
+ * Kernel usually skips size validation on zero-length devices
+ * now so no need to preload them.
+ */
+ /* FIXME In which kernel version did this begin? */
+ if (!existing_table_size && dnode->props.delay_resume_if_new)
+ dnode->props.size_changed = 0;
+
log_debug("Table size changed from %" PRIu64 " to %"
- PRIu64 " for %s",
- dm_task_get_existing_table_size(dmt),
- seg_start, dnode->name);
+ PRIu64 " for %s (%" PRIu32 ":%" PRIu32 ").%s",
+ existing_table_size, seg_start, dnode->name,
+ dnode->info.major, dnode->info.minor,
+ dnode->props.size_changed ? "" : " (Ignoring.)");
+ }
}
dnode->props.segment_count = 0;
@@ -1865,19 +2470,13 @@ int dm_tree_preload_children(struct dm_tree_node *dnode,
return_0;
/* FIXME Cope if name exists with no uuid? */
- if (!child->info.exists) {
- if (!_create_node(child)) {
- stack;
- return 0;
- }
- }
+ if (!child->info.exists && !_create_node(child))
+ return_0;
- if (!child->info.inactive_table && child->props.segment_count) {
- if (!_load_node(child)) {
- stack;
- return 0;
- }
- }
+ if (!child->info.inactive_table &&
+ child->props.segment_count &&
+ !_load_node(child))
+ return_0;
/* Propagate device size change change */
if (child->props.size_changed)
@@ -1892,7 +2491,8 @@ int dm_tree_preload_children(struct dm_tree_node *dnode,
if (!_resume_node(child->name, child->info.major, child->info.minor,
child->props.read_ahead, child->props.read_ahead_flags,
- &newinfo, &child->dtree->cookie, child->udev_flags)) {
+ &newinfo, &child->dtree->cookie, child->udev_flags,
+ child->info.suspended)) {
log_error("Unable to resume %s (%" PRIu32
":%" PRIu32 ")", child->name, child->info.major,
child->info.minor);
@@ -1902,7 +2502,6 @@ int dm_tree_preload_children(struct dm_tree_node *dnode,
/* Update cached info */
child->info = newinfo;
-
/*
* Prepare for immediate synchronization with udev and flush all stacked
* dev node operations if requested by immediate_dev_node property. But
@@ -1910,16 +2509,18 @@ int dm_tree_preload_children(struct dm_tree_node *dnode,
*/
if (child->props.immediate_dev_node)
update_devs_flag = 1;
-
}
- handle = NULL;
-
- if (update_devs_flag) {
+ if (update_devs_flag ||
+ (!dnode->info.exists && dnode->callback)) {
if (!dm_udev_wait(dm_tree_get_cookie(dnode)))
stack;
dm_tree_set_cookie(dnode, 0);
- dm_task_update_nodes();
+
+ if (!dnode->info.exists && dnode->callback &&
+ !dnode->callback(child, DM_NODE_CALLBACK_PRELOADED,
+ dnode->callback_data))
+ return_0;
}
return r;
@@ -1982,8 +2583,8 @@ static struct load_segment *_add_segment(struct dm_tree_node *dnode, unsigned ty
}
int dm_tree_node_add_snapshot_origin_target(struct dm_tree_node *dnode,
- uint64_t size,
- const char *origin_uuid)
+ uint64_t size,
+ const char *origin_uuid)
{
struct load_segment *seg;
struct dm_tree_node *origin_node;
@@ -2003,6 +2604,12 @@ int dm_tree_node_add_snapshot_origin_target(struct dm_tree_node *dnode,
/* Resume snapshot origins after new snapshots */
dnode->activation_priority = 1;
+ /*
+ * Don't resume the origin immediately in case it is a non-trivial
+ * target that must not be active more than once concurrently!
+ */
+ origin_node->props.delay_resume_if_new = 1;
+
return 1;
}
@@ -2088,7 +2695,7 @@ int dm_tree_node_add_snapshot_merge_target(struct dm_tree_node *node,
}
int dm_tree_node_add_error_target(struct dm_tree_node *node,
- uint64_t size)
+ uint64_t size)
{
if (!_add_segment(node, SEG_ERROR, size))
return_0;
@@ -2097,7 +2704,7 @@ int dm_tree_node_add_error_target(struct dm_tree_node *node,
}
int dm_tree_node_add_zero_target(struct dm_tree_node *node,
- uint64_t size)
+ uint64_t size)
{
if (!_add_segment(node, SEG_ZERO, size))
return_0;
@@ -2106,7 +2713,7 @@ int dm_tree_node_add_zero_target(struct dm_tree_node *node,
}
int dm_tree_node_add_linear_target(struct dm_tree_node *node,
- uint64_t size)
+ uint64_t size)
{
if (!_add_segment(node, SEG_LINEAR, size))
return_0;
@@ -2115,8 +2722,8 @@ int dm_tree_node_add_linear_target(struct dm_tree_node *node,
}
int dm_tree_node_add_striped_target(struct dm_tree_node *node,
- uint64_t size,
- uint32_t stripe_size)
+ uint64_t size,
+ uint32_t stripe_size)
{
struct load_segment *seg;
@@ -2172,7 +2779,10 @@ int dm_tree_node_add_mirror_target_log(struct dm_tree_node *node,
log_error("log uuid pool_strdup failed");
return 0;
}
- if (!(flags & DM_CORELOG)) {
+ if ((flags & DM_CORELOG))
+ /* For pvmove: immediate resume (for size validation) isn't needed. */
+ node->props.delay_resume_if_new = 1;
+ else {
if (!(log_node = dm_tree_find_node_by_uuid(node->dtree, log_uuid))) {
log_error("Couldn't find mirror log uuid %s.", log_uuid);
return 0;
@@ -2181,6 +2791,10 @@ int dm_tree_node_add_mirror_target_log(struct dm_tree_node *node,
if (clustered)
log_node->props.immediate_dev_node = 1;
+ /* The kernel validates the size of disk logs. */
+ /* FIXME Propagate to any devices below */
+ log_node->props.delay_resume_if_new = 0;
+
if (!_link_tree_nodes(node, log_node))
return_0;
}
@@ -2196,7 +2810,7 @@ int dm_tree_node_add_mirror_target_log(struct dm_tree_node *node,
}
int dm_tree_node_add_mirror_target(struct dm_tree_node *node,
- uint64_t size)
+ uint64_t size)
{
if (!_add_segment(node, SEG_MIRRORED, size))
return_0;
@@ -2204,6 +2818,35 @@ int dm_tree_node_add_mirror_target(struct dm_tree_node *node,
return 1;
}
+int dm_tree_node_add_raid_target(struct dm_tree_node *node,
+ uint64_t size,
+ const char *raid_type,
+ uint32_t region_size,
+ uint32_t stripe_size,
+ uint64_t rebuilds,
+ uint64_t flags)
+{
+ int i;
+ struct load_segment *seg = NULL;
+
+ for (i = 0; dm_segtypes[i].target && !seg; i++)
+ if (!strcmp(raid_type, dm_segtypes[i].target))
+ if (!(seg = _add_segment(node,
+ dm_segtypes[i].type, size)))
+ return_0;
+
+ if (!seg)
+ return_0;
+
+ seg->region_size = region_size;
+ seg->stripe_size = stripe_size;
+ seg->area_count = 0;
+ seg->rebuilds = rebuilds;
+ seg->flags = flags;
+
+ return 1;
+}
+
int dm_tree_node_add_replicator_target(struct dm_tree_node *node,
uint64_t size,
const char *rlog_uuid,
@@ -2369,6 +3012,296 @@ int dm_tree_node_add_replicator_dev_target(struct dm_tree_node *node,
return 1;
}
+static struct load_segment *_get_single_load_segment(struct dm_tree_node *node,
+ unsigned type)
+{
+ struct load_segment *seg;
+
+ if (node->props.segment_count != 1) {
+ log_error("Node %s must have only one segment.",
+ dm_segtypes[type].target);
+ return NULL;
+ }
+
+ seg = dm_list_item(dm_list_last(&node->props.segs), struct load_segment);
+ if (seg->type != type) {
+ log_error("Node %s has segment type %s.",
+ dm_segtypes[type].target,
+ dm_segtypes[seg->type].target);
+ return NULL;
+ }
+
+ return seg;
+}
+
+static int _thin_validate_device_id(uint32_t device_id)
+{
+ if (device_id > DM_THIN_MAX_DEVICE_ID) {
+ log_error("Device id %u is higher then %u.",
+ device_id, DM_THIN_MAX_DEVICE_ID);
+ return 0;
+ }
+
+ return 1;
+}
+
+int dm_tree_node_add_thin_pool_target(struct dm_tree_node *node,
+ uint64_t size,
+ uint64_t transaction_id,
+ const char *metadata_uuid,
+ const char *pool_uuid,
+ uint32_t data_block_size,
+ uint64_t low_water_mark,
+ unsigned skip_block_zeroing)
+{
+ struct load_segment *seg, *mseg;
+ uint64_t devsize = 0;
+ /*
+ * Max supported size for thin pool metadata device
+ * Limitation is hardcoded into kernel and bigger
+ * device size is not accepted. (16978542592)
+ */
+ const uint64_t max_metadata_size =
+ 255ULL * (1 << 14) * (4096 / (1 << 9)) - 256 * 1024;
+
+ if (data_block_size < DM_THIN_MIN_DATA_BLOCK_SIZE) {
+ log_error("Data block size %u is lower then %u sectors.",
+ data_block_size, DM_THIN_MIN_DATA_BLOCK_SIZE);
+ return 0;
+ }
+
+ if (data_block_size > DM_THIN_MAX_DATA_BLOCK_SIZE) {
+ log_error("Data block size %u is higher then %u sectors.",
+ data_block_size, DM_THIN_MAX_DATA_BLOCK_SIZE);
+ return 0;
+ }
+
+ if (!(seg = _add_segment(node, SEG_THIN_POOL, size)))
+ return_0;
+
+ if (!(seg->metadata = dm_tree_find_node_by_uuid(node->dtree, metadata_uuid))) {
+ log_error("Missing metadata uuid %s.", metadata_uuid);
+ return 0;
+ }
+
+ if (!_link_tree_nodes(node, seg->metadata))
+ return_0;
+
+ /* FIXME: more complex target may need more tweaks */
+ dm_list_iterate_items(mseg, &seg->metadata->props.segs) {
+ devsize += mseg->size;
+ if (devsize > max_metadata_size) {
+ log_debug("Ignoring %" PRIu64 " of device.",
+ devsize - max_metadata_size);
+ mseg->size -= (devsize - max_metadata_size);
+ devsize = max_metadata_size;
+ /* FIXME: drop remaining segs */
+ }
+ }
+
+ if (!(seg->pool = dm_tree_find_node_by_uuid(node->dtree, pool_uuid))) {
+ log_error("Missing pool uuid %s.", pool_uuid);
+ return 0;
+ }
+
+ if (!_link_tree_nodes(node, seg->pool))
+ return_0;
+
+ /* Clean flag delay_resume_if_new - so corelog gets resumed */
+ seg->metadata->props.delay_resume_if_new = 0;
+ seg->pool->props.delay_resume_if_new = 0;
+
+ node->props.send_messages = 1;
+ seg->transaction_id = transaction_id;
+ seg->low_water_mark = low_water_mark;
+ seg->data_block_size = data_block_size;
+ seg->skip_block_zeroing = skip_block_zeroing;
+ dm_list_init(&seg->thin_messages);
+
+ return 1;
+}
+
+int dm_tree_node_add_thin_pool_message(struct dm_tree_node *node,
+ dm_thin_message_t type,
+ uint64_t id1, uint64_t id2)
+{
+ struct thin_message *tm;
+ struct load_segment *seg;
+
+ if (!(seg = _get_single_load_segment(node, SEG_THIN_POOL)))
+ return_0;
+
+ if (!(tm = dm_pool_zalloc(node->dtree->mem, sizeof (*tm)))) {
+ log_error("Failed to allocate thin message.");
+ return 0;
+ }
+
+ switch (type) {
+ case DM_THIN_MESSAGE_CREATE_SNAP:
+ /* If the thin origin is active, it must be suspend first! */
+ if (id1 == id2) {
+ log_error("Cannot use same device id for origin and its snapshot.");
+ return 0;
+ }
+ if (!_thin_validate_device_id(id1) ||
+ !_thin_validate_device_id(id2))
+ return_0;
+ tm->message.u.m_create_snap.device_id = id1;
+ tm->message.u.m_create_snap.origin_id = id2;
+ break;
+ case DM_THIN_MESSAGE_CREATE_THIN:
+ if (!_thin_validate_device_id(id1))
+ return_0;
+ tm->message.u.m_create_thin.device_id = id1;
+ tm->expected_errno = EEXIST;
+ break;
+ case DM_THIN_MESSAGE_DELETE:
+ if (!_thin_validate_device_id(id1))
+ return_0;
+ tm->message.u.m_delete.device_id = id1;
+ tm->expected_errno = ENODATA;
+ break;
+ case DM_THIN_MESSAGE_SET_TRANSACTION_ID:
+ if ((id1 + 1) != id2) {
+ log_error("New transaction id must be sequential.");
+ return 0; /* FIXME: Maybe too strict here? */
+ }
+ if (id2 != seg->transaction_id) {
+ log_error("Current transaction id is different from thin pool.");
+ return 0; /* FIXME: Maybe too strict here? */
+ }
+ tm->message.u.m_set_transaction_id.current_id = id1;
+ tm->message.u.m_set_transaction_id.new_id = id2;
+ break;
+ default:
+ log_error("Unsupported message type %d.", (int) type);
+ return 0;
+ }
+
+ tm->message.type = type;
+ dm_list_add(&seg->thin_messages, &tm->list);
+
+ return 1;
+}
+
+int dm_tree_node_set_thin_pool_discard(struct dm_tree_node *node,
+ unsigned ignore,
+ unsigned no_passdown)
+{
+ struct load_segment *seg;
+
+ if (!(seg = _get_single_load_segment(node, SEG_THIN_POOL)))
+ return_0;
+
+ seg->ignore_discard = ignore;
+ seg->no_discard_passdown = no_passdown;
+
+ return 1;
+}
+
+int dm_tree_node_add_thin_target(struct dm_tree_node *node,
+ uint64_t size,
+ const char *pool_uuid,
+ uint32_t device_id)
+{
+ struct dm_tree_node *pool;
+ struct load_segment *seg;
+
+ if (!(pool = dm_tree_find_node_by_uuid(node->dtree, pool_uuid))) {
+ log_error("Missing thin pool uuid %s.", pool_uuid);
+ return 0;
+ }
+
+ if (!_link_tree_nodes(node, pool))
+ return_0;
+
+ if (!_thin_validate_device_id(device_id))
+ return_0;
+
+ if (!(seg = _add_segment(node, SEG_THIN, size)))
+ return_0;
+
+ seg->pool = pool;
+ seg->device_id = device_id;
+
+ return 1;
+}
+
+int dm_tree_node_set_thin_external_origin(struct dm_tree_node *node,
+ const char *external_uuid)
+{
+ struct dm_tree_node *external;
+ struct load_segment *seg;
+
+ if (!(seg = _get_single_load_segment(node, SEG_THIN)))
+ return_0;
+
+ if (!(external = dm_tree_find_node_by_uuid(node->dtree,
+ external_uuid))) {
+ log_error("Missing thin external origin uuid %s.",
+ external_uuid);
+ return 0;
+ }
+
+ if (!_link_tree_nodes(node, external))
+ return_0;
+
+ seg->external = external;
+
+ return 1;
+}
+
+int dm_get_status_thin_pool(struct dm_pool *mem, const char *params,
+ struct dm_status_thin_pool **status)
+{
+ struct dm_status_thin_pool *s;
+
+ if (!(s = dm_pool_zalloc(mem, sizeof(struct dm_status_thin_pool)))) {
+ log_error("Failed to allocate thin_pool status structure.");
+ return 0;
+ }
+
+ /* FIXME: add support for held metadata root */
+ if (sscanf(params, "%" PRIu64 " %" PRIu64 "/%" PRIu64 " %" PRIu64 "/%" PRIu64,
+ &s->transaction_id,
+ &s->used_metadata_blocks,
+ &s->total_metadata_blocks,
+ &s->used_data_blocks,
+ &s->total_data_blocks) != 5) {
+ log_error("Failed to parse thin pool params: %s.", params);
+ return 0;
+ }
+
+ *status = s;
+
+ return 1;
+}
+
+int dm_get_status_thin(struct dm_pool *mem, const char *params,
+ struct dm_status_thin **status)
+{
+ struct dm_status_thin *s;
+
+ if (!(s = dm_pool_zalloc(mem, sizeof(struct dm_status_thin)))) {
+ log_error("Failed to allocate thin status structure.");
+ return 0;
+ }
+
+ if (strchr(params, '-')) {
+ s->mapped_sectors = 0;
+ s->highest_mapped_sector = 0;
+ } else if (sscanf(params, "%" PRIu64 " %" PRIu64,
+ &s->mapped_sectors,
+ &s->highest_mapped_sector) != 2) {
+ log_error("Failed to parse thin params: %s.", params);
+ return 0;
+ }
+
+ *status = s;
+
+ return 1;
+}
+
static int _add_area(struct dm_tree_node *node, struct load_segment *seg, struct dm_tree_node *dev_node, uint64_t offset)
{
struct seg_area *area;
@@ -2388,9 +3321,9 @@ static int _add_area(struct dm_tree_node *node, struct load_segment *seg, struct
}
int dm_tree_node_add_target_area(struct dm_tree_node *node,
- const char *dev_name,
- const char *uuid,
- uint64_t offset)
+ const char *dev_name,
+ const char *uuid,
+ uint64_t offset)
{
struct load_segment *seg;
struct stat info;
@@ -2409,12 +3342,12 @@ int dm_tree_node_add_target_area(struct dm_tree_node *node,
if (!_link_tree_nodes(node, dev_node))
return_0;
} else {
- if (stat(dev_name, &info) < 0) {
+ if (stat(dev_name, &info) < 0) {
log_error("Device %s not found.", dev_name);
return 0;
}
- if (!S_ISBLK(info.st_mode)) {
+ if (!S_ISBLK(info.st_mode)) {
log_error("Device %s is not a block device.", dev_name);
return 0;
}
@@ -2438,12 +3371,37 @@ int dm_tree_node_add_target_area(struct dm_tree_node *node,
return 1;
}
-void dm_tree_set_cookie(struct dm_tree_node *node, uint32_t cookie)
+int dm_tree_node_add_null_area(struct dm_tree_node *node, uint64_t offset)
{
- node->dtree->cookie = cookie;
+ struct load_segment *seg;
+
+ seg = dm_list_item(dm_list_last(&node->props.segs), struct load_segment);
+
+ switch (seg->type) {
+ case SEG_RAID1:
+ case SEG_RAID4:
+ case SEG_RAID5_LA:
+ case SEG_RAID5_RA:
+ case SEG_RAID5_LS:
+ case SEG_RAID5_RS:
+ case SEG_RAID6_ZR:
+ case SEG_RAID6_NR:
+ case SEG_RAID6_NC:
+ break;
+ default:
+ log_error("dm_tree_node_add_null_area() called on an unsupported segment type");
+ return 0;
+ }
+
+ if (!_add_area(node, seg, NULL, offset))
+ return_0;
+
+ return 1;
}
-uint32_t dm_tree_get_cookie(struct dm_tree_node *node)
+void dm_tree_node_set_callback(struct dm_tree_node *dnode,
+ dm_node_callback_fn cb, void *data)
{
- return node->dtree->cookie;
+ dnode->callback = cb;
+ dnode->callback_data = data;
}
diff --git a/libdm/libdm-file.c b/libdm/libdm-file.c
index 82fae34..26c8241 100644
--- a/libdm/libdm-file.c
+++ b/libdm/libdm-file.c
@@ -27,6 +27,11 @@ static int _create_dir_recursive(const char *dir)
log_verbose("Creating directory \"%s\"", dir);
/* Create parent directories */
orig = s = dm_strdup(dir);
+ if (!s) {
+ log_error("Failed to duplicate directory name.");
+ return 0;
+ }
+
while ((s = strchr(s, '/')) != NULL) {
*s = '\0';
if (*orig) {
@@ -71,6 +76,26 @@ int dm_create_dir(const char *dir)
return 0;
}
+int dm_is_empty_dir(const char *dir)
+{
+ struct dirent *dirent;
+ DIR *d;
+
+ if (!(d = opendir(dir))) {
+ log_sys_error("opendir", dir);
+ return 0;
+ }
+
+ while ((dirent = readdir(d)))
+ if (strcmp(dirent->d_name, ".") && strcmp(dirent->d_name, ".."))
+ break;
+
+ if (closedir(d))
+ log_sys_error("closedir", dir);
+
+ return dirent ? 0 : 1;
+}
+
int dm_fclose(FILE *stream)
{
int prev_fail = ferror(stream);
@@ -110,7 +135,6 @@ retry_fcntl:
switch (errno) {
case EINTR:
goto retry_fcntl;
- break;
case EACCES:
case EAGAIN:
if (retries == 20) {
@@ -149,7 +173,7 @@ retry_fcntl:
goto fail_close_unlink;
}
- if ((write_out == 0) || (write_out < bufferlen)) {
+ if ((write_out == 0) || ((size_t)write_out < bufferlen)) {
log_error("Cannot write pid to pidfile [%s], shortwrite of"
"[%" PRIsize_t "] bytes, expected [%" PRIsize_t "]\n",
lockfile, write_out, bufferlen);
diff --git a/libdm/libdm-report.c b/libdm/libdm-report.c
index 9b8e3c1..0bafa86 100644
--- a/libdm/libdm-report.c
+++ b/libdm/libdm-report.c
@@ -102,7 +102,7 @@ static const struct dm_report_object_type *_find_type(struct dm_report *rh,
*/
int dm_report_field_string(struct dm_report *rh,
- struct dm_report_field *field, const char **data)
+ struct dm_report_field *field, const char *const *data)
{
char *repstr;
@@ -139,7 +139,7 @@ int dm_report_field_int(struct dm_report *rh,
return 0;
}
- *sortval = (const uint64_t) value;
+ *sortval = (uint64_t) value;
field->sort_value = sortval;
field->report_string = repstr;
@@ -168,7 +168,7 @@ int dm_report_field_uint32(struct dm_report *rh,
return 0;
}
- *sortval = (const uint64_t) value;
+ *sortval = (uint64_t) value;
field->sort_value = sortval;
field->report_string = repstr;
@@ -197,7 +197,7 @@ int dm_report_field_int32(struct dm_report *rh,
return 0;
}
- *sortval = (const uint64_t) value;
+ *sortval = (uint64_t) value;
field->sort_value = sortval;
field->report_string = repstr;
@@ -367,33 +367,32 @@ static uint32_t _all_match(struct dm_report *rh, const char *field, size_t flen)
{
size_t prefix_len;
const struct dm_report_object_type *t;
- char prefixed_all[32];
+ uint32_t report_types = 0;
+ unsigned unprefixed_all_matched = 0;
if (!strncasecmp(field, "all", 3) && flen == 3) {
- if (strlen(rh->field_prefix)) {
- strcpy(prefixed_all, rh->field_prefix);
- strcat(prefixed_all, "all");
- /*
- * Add also prefix to receive all attributes
- * (e.g.LABEL/PVS use the same prefix)
- */
- return rh->report_types |
- _all_match(rh, prefixed_all,
- strlen(prefixed_all));
- } else
- return (rh->report_types)
- ? rh->report_types : REPORT_TYPES_ALL;
+ /* If there's no report prefix, match all report types */
+ if (!(flen = strlen(rh->field_prefix)))
+ return rh->report_types ? : REPORT_TYPES_ALL;
+
+ /* otherwise include all fields beginning with the report prefix. */
+ unprefixed_all_matched = 1;
+ field = rh->field_prefix;
+ report_types = rh->report_types;
}
+ /* Combine all report types that have a matching prefix. */
for (t = rh->types; t->data_fn; t++) {
prefix_len = strlen(t->prefix);
+
if (!strncasecmp(t->prefix, field, prefix_len) &&
- !strncasecmp(field + prefix_len, "all", 3) &&
- flen == prefix_len + 3)
- return t->id;
+ ((unprefixed_all_matched && (flen == prefix_len)) ||
+ (!strncasecmp(field + prefix_len, "all", 3) &&
+ (flen == prefix_len + 3))))
+ report_types |= t->id;
}
- return 0;
+ return report_types;
}
/*
@@ -542,6 +541,9 @@ static int _parse_keys(struct dm_report *rh, const char *keys,
const char *ws; /* Word start */
const char *we = keys; /* Word end */
+ if (!keys)
+ return 1;
+
while (*we) {
/* Allow consecutive commas */
while (*we && *we == ',')
@@ -675,15 +677,15 @@ int dm_report_set_output_field_name_prefix(struct dm_report *rh, const char *out
/*
* Create a row of data for an object
*/
-static void * _report_get_field_data(struct dm_report *rh,
- struct field_properties *fp, void *object)
+static void *_report_get_field_data(struct dm_report *rh,
+ struct field_properties *fp, void *object)
{
- void *ret = fp->type->data_fn(object);
+ char *ret = fp->type->data_fn(object);
if (!ret)
return NULL;
- return ret + rh->fields[fp->field_num].offset;
+ return (void *)(ret + rh->fields[fp->field_num].offset);
}
int dm_report_object(struct dm_report *rh, void *object)
@@ -693,6 +695,11 @@ int dm_report_object(struct dm_report *rh, void *object)
struct dm_report_field *field;
void *data = NULL;
+ if (!rh) {
+ log_error(INTERNAL_ERROR "dm_report handler is NULL.");
+ return 0;
+ }
+
if (!(row = dm_pool_zalloc(rh->mem, sizeof(*row)))) {
log_error("dm_report_object: struct row allocation failed");
return 0;
@@ -734,8 +741,8 @@ int dm_report_object(struct dm_report *rh, void *object)
return 0;
}
- if ((strlen(field->report_string) > field->props->width))
- field->props->width = strlen(field->report_string);
+ if (((int) strlen(field->report_string) > field->props->width))
+ field->props->width = (int) strlen(field->report_string);
if ((rh->flags & RH_SORT_REQUIRED) &&
(field->props->flags & FLD_SORT_KEY)) {
@@ -775,8 +782,8 @@ static int _report_headings(struct dm_report *rh)
}
dm_list_iterate_items(fp, &rh->field_props) {
- if (buf_size < fp->width)
- buf_size = fp->width;
+ if ((int) buf_size < fp->width)
+ buf_size = (size_t) fp->width;
}
/* Including trailing '\0'! */
buf_size++;
@@ -1006,11 +1013,6 @@ static int _output_as_rows(struct dm_report *rh)
struct dm_report_field *field;
struct row *row;
- if (!dm_pool_begin_object(rh->mem, 512)) {
- log_error("dm_report: Unable to allocate output line");
- return 0;
- }
-
dm_list_iterate_items(fp, &rh->field_props) {
if (fp->flags & FLD_HIDDEN) {
dm_list_iterate_items(row, &rh->rows) {
@@ -1020,6 +1022,11 @@ static int _output_as_rows(struct dm_report *rh)
continue;
}
+ if (!dm_pool_begin_object(rh->mem, 512)) {
+ log_error("dm_report: Unable to allocate output line");
+ return 0;
+ }
+
if ((rh->flags & DM_REPORT_OUTPUT_HEADINGS)) {
if (!dm_pool_grow_object(rh->mem, rh->fields[fp->field_num].heading, 0)) {
log_error("dm_report: Failed to extend row for field name");
diff --git a/libdm/libdm-string.c b/libdm/libdm-string.c
index 365e7ec..5ef6334 100644
--- a/libdm/libdm-string.c
+++ b/libdm/libdm-string.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2006-2007 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2006-2012 Red Hat, Inc. All rights reserved.
*
* This file is part of the device-mapper userspace tools.
*
@@ -13,7 +13,6 @@
*/
#include "dmlib.h"
-#include "libdevmapper.h"
#include <ctype.h>
@@ -130,9 +129,9 @@ const char *dm_basename(const char *path)
return p ? p + 1 : path;
}
-int dm_asprintf(char **result, const char *format, ...)
+int dm_vasprintf(char **result, const char *format, va_list aq)
{
- int n, ok = 0, size = 32;
+ int i, n, size = 16;
va_list ap;
char *buf = dm_malloc(size);
@@ -141,23 +140,293 @@ int dm_asprintf(char **result, const char *format, ...)
if (!buf)
return -1;
- while (!ok) {
- va_start(ap, format);
+ for (i = 0;; i++) {
+ va_copy(ap, aq);
n = vsnprintf(buf, size, format, ap);
va_end(ap);
if (0 <= n && n < size)
- ok = 1;
- else {
+ break;
+
+ dm_free(buf);
+ /* Up to glibc 2.0.6 returns -1 */
+ size = (n < 0) ? size * 2 : n + 1;
+ if (!(buf = dm_malloc(size)))
+ return -1;
+ }
+
+ if (i > 1) {
+ /* Reallocating more then once? */
+ if (!(*result = dm_strdup(buf))) {
dm_free(buf);
- size *= 2;
- buf = dm_malloc(size);
- if (!buf)
- return -1;
+ return -1;
}
- }
+ dm_free(buf);
+ } else
+ *result = buf;
- *result = dm_strdup(buf);
- dm_free(buf);
return n + 1;
}
+
+int dm_asprintf(char **result, const char *format, ...)
+{
+ int r;
+ va_list ap;
+ va_start(ap, format);
+ r = dm_vasprintf(result, format, ap);
+ va_end(ap);
+ return r;
+}
+
+/*
+ * Count occurences of 'c' in 'str' until we reach a null char.
+ *
+ * Returns:
+ * len - incremented for each char we encounter.
+ * count - number of occurrences of 'c' and 'c2'.
+ */
+static void _count_chars(const char *str, size_t *len, int *count,
+ const int c1, const int c2)
+{
+ const char *ptr;
+
+ for (ptr = str; *ptr; ptr++, (*len)++)
+ if (*ptr == c1 || *ptr == c2)
+ (*count)++;
+}
+
+/*
+ * Count occurrences of 'c' in 'str' of length 'size'.
+ *
+ * Returns:
+ * Number of occurrences of 'c'
+ */
+unsigned dm_count_chars(const char *str, size_t len, const int c)
+{
+ size_t i;
+ unsigned count = 0;
+
+ for (i = 0; i < len; i++)
+ if (str[i] == c)
+ count++;
+
+ return count;
+}
+
+/*
+ * Length of string after escaping double quotes and backslashes.
+ */
+size_t dm_escaped_len(const char *str)
+{
+ size_t len = 1;
+ int count = 0;
+
+ _count_chars(str, &len, &count, '\"', '\\');
+
+ return count + len;
+}
+
+/*
+ * Copies a string, quoting orig_char with quote_char.
+ * Optionally also quote quote_char.
+ */
+static void _quote_characters(char **out, const char *src,
+ const int orig_char, const int quote_char,
+ int quote_quote_char)
+{
+ while (*src) {
+ if (*src == orig_char ||
+ (*src == quote_char && quote_quote_char))
+ *(*out)++ = quote_char;
+
+ *(*out)++ = *src++;
+ }
+}
+
+static void _unquote_one_character(char *src, const char orig_char,
+ const char quote_char)
+{
+ char *out;
+ char s, n;
+
+ /* Optimise for the common case where no changes are needed. */
+ while ((s = *src++)) {
+ if (s == quote_char &&
+ ((n = *src) == orig_char || n == quote_char)) {
+ out = src++;
+ *(out - 1) = n;
+
+ while ((s = *src++)) {
+ if (s == quote_char &&
+ ((n = *src) == orig_char || n == quote_char)) {
+ s = n;
+ src++;
+ }
+ *out = s;
+ out++;
+ }
+
+ *out = '\0';
+ return;
+ }
+ }
+}
+
+/*
+ * Unquote each character given in orig_char array and unquote quote_char
+ * as well. Also save the first occurrence of each character from orig_char
+ * that was found unquoted in arr_substr_first_unquoted array. This way we can
+ * process several characters in one go.
+ */
+static void _unquote_characters(char *src, const char *orig_chars,
+ size_t num_orig_chars,
+ const char quote_char,
+ char *arr_substr_first_unquoted[])
+{
+ char *out = src;
+ char c, s, n;
+ unsigned i;
+
+ while ((s = *src++)) {
+ for (i = 0; i < num_orig_chars; i++) {
+ c = orig_chars[i];
+ if (s == quote_char &&
+ ((n = *src) == c || n == quote_char)) {
+ s = n;
+ src++;
+ break;
+ }
+ if (arr_substr_first_unquoted && (s == c) &&
+ !arr_substr_first_unquoted[i])
+ arr_substr_first_unquoted[i] = out;
+ };
+ *out++ = s;
+ }
+
+ *out = '\0';
+}
+
+/*
+ * Copies a string, quoting hyphens with hyphens.
+ */
+static void _quote_hyphens(char **out, const char *src)
+{
+ _quote_characters(out, src, '-', '-', 0);
+}
+
+/*
+ * <vg>-<lv>-<layer> or if !layer just <vg>-<lv>.
+ */
+char *dm_build_dm_name(struct dm_pool *mem, const char *vgname,
+ const char *lvname, const char *layer)
+{
+ size_t len = 1;
+ int hyphens = 1;
+ char *r, *out;
+
+ _count_chars(vgname, &len, &hyphens, '-', 0);
+ _count_chars(lvname, &len, &hyphens, '-', 0);
+
+ if (layer && *layer) {
+ _count_chars(layer, &len, &hyphens, '-', 0);
+ hyphens++;
+ }
+
+ len += hyphens;
+
+ if (!(r = dm_pool_alloc(mem, len))) {
+ log_error("build_dm_name: Allocation failed for %" PRIsize_t
+ " for %s %s %s.", len, vgname, lvname, layer);
+ return NULL;
+ }
+
+ out = r;
+ _quote_hyphens(&out, vgname);
+ *out++ = '-';
+ _quote_hyphens(&out, lvname);
+
+ if (layer && *layer) {
+ /* No hyphen if the layer begins with _ e.g. _mlog */
+ if (*layer != '_')
+ *out++ = '-';
+ _quote_hyphens(&out, layer);
+ }
+ *out = '\0';
+
+ return r;
+}
+
+char *dm_build_dm_uuid(struct dm_pool *mem, const char *uuid_prefix, const char *lvid, const char *layer)
+{
+ char *dmuuid;
+ size_t len;
+
+ if (!layer)
+ layer = "";
+
+ len = strlen(uuid_prefix) + strlen(lvid) + strlen(layer) + 2;
+
+ if (!(dmuuid = dm_pool_alloc(mem, len))) {
+ log_error("build_dm_name: Allocation failed for %" PRIsize_t
+ " %s %s.", len, lvid, layer);
+ return NULL;
+ }
+
+ sprintf(dmuuid, "%s%s%s%s", uuid_prefix, lvid, (*layer) ? "-" : "", layer);
+
+ return dmuuid;
+}
+
+/*
+ * Copies a string, quoting double quotes with backslashes.
+ */
+char *dm_escape_double_quotes(char *out, const char *src)
+{
+ char *buf = out;
+
+ _quote_characters(&buf, src, '\"', '\\', 1);
+ *buf = '\0';
+
+ return out;
+}
+
+/*
+ * Undo quoting in situ.
+ */
+void dm_unescape_double_quotes(char *src)
+{
+ _unquote_one_character(src, '\"', '\\');
+}
+
+/*
+ * Unescape colons and "at" signs in situ and save the substrings
+ * starting at the position of the first unescaped colon and the
+ * first unescaped "at" sign. This is normally used to unescape
+ * device names used as PVs.
+ */
+void dm_unescape_colons_and_at_signs(char *src,
+ char **substr_first_unquoted_colon,
+ char **substr_first_unquoted_at_sign)
+{
+ const char *orig_chars = ":@";
+ char *arr_substr_first_unquoted[] = {NULL, NULL, NULL};
+
+ _unquote_characters(src, orig_chars, 2, '\\', arr_substr_first_unquoted);
+
+ if (substr_first_unquoted_colon)
+ *substr_first_unquoted_colon = arr_substr_first_unquoted[0];
+
+ if (substr_first_unquoted_at_sign)
+ *substr_first_unquoted_at_sign = arr_substr_first_unquoted[1];
+}
+
+int dm_strncpy(char *dest, const char *src, size_t n)
+{
+ if (memccpy(dest, src, 0, n))
+ return 1;
+
+ if (n > 0)
+ dest[n - 1] = '\0';
+
+ return 0;
+}
diff --git a/libdm/misc/dm-ioctl.h b/libdm/misc/dm-ioctl.h
index fb11b5c..1cf66fe 100644
--- a/libdm/misc/dm-ioctl.h
+++ b/libdm/misc/dm-ioctl.h
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2001 - 2003 Sistina Software (UK) Limited.
- * Copyright (C) 2004 - 2009 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2004 - 2012 Red Hat, Inc. All rights reserved.
*
* This file is released under the LGPL.
*/
@@ -269,9 +269,9 @@ enum {
#define DM_DEV_SET_GEOMETRY _IOWR(DM_IOCTL, DM_DEV_SET_GEOMETRY_CMD, struct dm_ioctl)
#define DM_VERSION_MAJOR 4
-#define DM_VERSION_MINOR 19
+#define DM_VERSION_MINOR 23
#define DM_VERSION_PATCHLEVEL 0
-#define DM_VERSION_EXTRA "-ioctl (2010-10-14)"
+#define DM_VERSION_EXTRA "-ioctl (2012-07-25)"
/* Status bits */
#define DM_READONLY_FLAG (1 << 0) /* In/Out */
@@ -309,6 +309,8 @@ enum {
/*
* Set this to suspend without flushing queued ios.
+ * Also disables flushing uncommitted changes in the thin target before
+ * generating statistics for DM_TABLE_STATUS and DM_DEV_WAIT.
*/
#define DM_NOFLUSH_FLAG (1 << 11) /* In */
@@ -330,4 +332,10 @@ enum {
*/
#define DM_UUID_FLAG (1 << 14) /* In */
+/*
+ * If set, all buffers are wiped after use. Use when sending
+ * or requesting sensitive data such as an encryption key.
+ */
+#define DM_SECURE_DATA_FLAG (1 << 15) /* In */
+
#endif /* _LINUX_DM_IOCTL_H */
diff --git a/libdm/misc/dm-log-userspace.h b/libdm/misc/dm-log-userspace.h
index 7cacb18..3317938 100644
--- a/libdm/misc/dm-log-userspace.h
+++ b/libdm/misc/dm-log-userspace.h
@@ -52,15 +52,20 @@
* Payload-to-userspace:
* A single string containing all the argv arguments separated by ' 's
* Payload-to-kernel:
- * None. ('data_size' in the dm_ulog_request struct should be 0.)
+ * The name of the device that is used as the backing store for the log
+ * data. 'dm_get_device' will be called on this device. ('dm_put_device'
+ * will be called on this device automatically after calling DM_ULOG_DTR.)
+ * If there is no device needed for log data, 'data_size' in the
+ * dm_ulog_request struct should be 0.
*
* The UUID contained in the dm_ulog_request structure is the reference that
* will be used by all request types to a specific log. The constructor must
- * record this assotiation with instance created.
+ * record this assotiation with the instance created.
*
* When the request has been processed, user-space must return the
- * dm_ulog_request to the kernel - setting the 'error' field and
- * 'data_size' appropriately.
+ * dm_ulog_request to the kernel - setting the 'error' field, filling the
+ * data field with the log device if necessary, and setting 'data_size'
+ * appropriately.
*/
#define DM_ULOG_CTR 1
@@ -363,13 +368,26 @@
* various request types above. The remaining 24-bits are currently
* set to zero and are reserved for future use and compatibility concerns.
*
- * User-space should always use DM_ULOG_REQUEST_TYPE to aquire the
+ * User-space should always use DM_ULOG_REQUEST_TYPE to acquire the
* request type from the 'request_type' field to maintain forward compatibility.
*/
#define DM_ULOG_REQUEST_MASK 0xFF
#define DM_ULOG_REQUEST_TYPE(request_type) \
(DM_ULOG_REQUEST_MASK & (request_type))
+/*
+ * DM_ULOG_REQUEST_VERSION is incremented when there is a
+ * change to the way information is passed between kernel
+ * and userspace. This could be a structure change of
+ * dm_ulog_request or a change in the way requests are
+ * issued/handled. Changes are outlined here:
+ * version 1: Initial implementation
+ * version 2: DM_ULOG_CTR allowed to return a string containing a
+ * device name that is to be registered with DM via
+ * 'dm_get_device'.
+ */
+#define DM_ULOG_REQUEST_VERSION 2
+
struct dm_ulog_request {
/*
* The local unique identifier (luid) and the universally unique
@@ -383,8 +401,9 @@ struct dm_ulog_request {
*/
uint64_t luid;
char uuid[DM_UUID_LEN];
- char padding[7]; /* Padding because DM_UUID_LEN = 129 */
+ char padding[3]; /* Padding because DM_UUID_LEN = 129 */
+ uint32_t version; /* See DM_ULOG_REQUEST_VERSION */
int32_t error; /* Used to report back processing errors */
uint32_t seq; /* Sequence number for request */
diff --git a/libdm/mm/dbg_malloc.c b/libdm/mm/dbg_malloc.c
index db7f5f3..d37083f 100644
--- a/libdm/mm/dbg_malloc.c
+++ b/libdm/mm/dbg_malloc.c
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
- * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2004-2011 Red Hat, Inc. All rights reserved.
*
* This file is part of the device-mapper userspace tools.
*
@@ -15,6 +15,10 @@
#include "dmlib.h"
+#ifdef VALGRIND_POOL
+#include "memcheck.h"
+#endif
+
#include <assert.h>
#include <stdarg.h>
@@ -115,7 +119,9 @@ void *dm_malloc_aux_debug(size_t s, const char *file, int line)
/* log_debug("Allocated: %u %u %u", nb->id, _mem_stats.blocks_allocated,
_mem_stats.bytes); */
-
+#ifdef VALGRIND_POOL
+ VALGRIND_MAKE_MEM_UNDEFINED(nb + 1, s);
+#endif
return nb + 1;
}
@@ -141,11 +147,13 @@ void dm_free_aux(void *p)
/* sanity check */
assert(mb->magic == p);
-
+#ifdef VALGRIND_POOL
+ VALGRIND_MAKE_MEM_DEFINED(p, mb->length);
+#endif
/* check data at the far boundary */
- ptr = ((char *) mb) + sizeof(struct memblock) + mb->length;
+ ptr = (char *) p + mb->length;
for (i = 0; i < sizeof(unsigned long); i++)
- if (*ptr++ != (char) mb->id)
+ if (ptr[i] != (char) mb->id)
assert(!"Damage at far end of block");
/* have we freed this before ? */
@@ -165,9 +173,9 @@ void dm_free_aux(void *p)
mb->id = 0;
/* stomp a different pattern across the memory */
- ptr = ((char *) mb) + sizeof(struct memblock);
+ ptr = p;
for (i = 0; i < mb->length; i++)
- *ptr++ = i & 1 ? (char) 0xde : (char) 0xad;
+ ptr[i] = i & 1 ? (char) 0xde : (char) 0xad;
assert(_mem_stats.blocks_allocated);
_mem_stats.blocks_allocated--;
@@ -184,7 +192,7 @@ void *dm_realloc_aux(void *p, unsigned int s, const char *file, int line)
r = dm_malloc_aux_debug(s, file, line);
- if (p) {
+ if (r && p) {
memcpy(r, p, mb->length);
dm_free_aux(p);
}
diff --git a/libdm/mm/dbg_malloc.h b/libdm/mm/dbg_malloc.h
deleted file mode 100644
index 6de1020..0000000
--- a/libdm/mm/dbg_malloc.h
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
- * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
- *
- * This file is part of the device-mapper userspace tools.
- *
- * This copyrighted material is made available to anyone wishing to use,
- * modify, copy, or redistribute it subject to the terms and conditions
- * of the GNU Lesser General Public License v.2.1.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-#ifndef _DM_DBG_MALLOC_H
-#define _DM_DBG_MALLOC_H
-
-#include <stdlib.h>
-#include <string.h>
-
-void *malloc_aux(size_t s, const char *file, int line);
-#define dm_malloc(s) malloc_aux((s), __FILE__, __LINE__)
-
-char *dbg_strdup(const char *str);
-
-#ifdef DEBUG_MEM
-
-void free_aux(void *p);
-void *realloc_aux(void *p, unsigned int s, const char *file, int line);
-int dump_memory(void);
-void bounds_check(void);
-
-# define dm_free(p) free_aux(p)
-# define dbg_realloc(p, s) realloc_aux(p, s, __FILE__, __LINE__)
-
-#else
-
-# define dm_free(p) do {if (p) free(p); } while (0)
-# define dbg_realloc(p, s) realloc(p, s)
-# define dump_memory()
-# define bounds_check()
-
-#endif
-
-#endif
diff --git a/libdm/mm/pool-debug.c b/libdm/mm/pool-debug.c
index 7fde3a6..7f9a0e4 100644
--- a/libdm/mm/pool-debug.c
+++ b/libdm/mm/pool-debug.c
@@ -33,6 +33,8 @@ struct dm_pool {
struct dm_list list;
const char *name;
void *orig_pool; /* to pair it with first allocation call */
+ unsigned locked;
+ long crc;
int begun;
struct block *object;
@@ -48,7 +50,7 @@ struct dm_pool {
struct dm_pool *dm_pool_create(const char *name, size_t chunk_hint)
{
- struct dm_pool *mem = dm_malloc(sizeof(*mem));
+ struct dm_pool *mem = dm_zalloc(sizeof(*mem));
if (!mem) {
log_error("Couldn't create memory pool %s (size %"
@@ -57,16 +59,6 @@ struct dm_pool *dm_pool_create(const char *name, size_t chunk_hint)
}
mem->name = name;
- mem->begun = 0;
- mem->object = 0;
- mem->blocks = mem->tail = NULL;
-
- mem->stats.block_serialno = 0;
- mem->stats.blocks_allocated = 0;
- mem->stats.blocks_max = 0;
- mem->stats.bytes = 0;
- mem->stats.maxbytes = 0;
-
mem->orig_pool = mem;
#ifdef DEBUG_POOL
@@ -81,6 +73,10 @@ static void _free_blocks(struct dm_pool *p, struct block *b)
{
struct block *n;
+ if (p->locked)
+ log_error(INTERNAL_ERROR "_free_blocks from locked pool %s",
+ p->name);
+
while (b) {
p->stats.bytes -= b->size;
p->stats.blocks_allocated--;
@@ -119,6 +115,10 @@ void *dm_pool_alloc(struct dm_pool *p, size_t s)
static void _append_block(struct dm_pool *p, struct block *b)
{
+ if (p->locked)
+ log_error(INTERNAL_ERROR "_append_blocks to locked pool %s",
+ p->name);
+
if (p->tail) {
p->tail->next = b;
p->tail = b;
@@ -146,7 +146,7 @@ static struct block *_new_block(size_t s, unsigned alignment)
* I don't think LVM will use anything but default
* align.
*/
- assert(alignment == DEFAULT_ALIGNMENT);
+ assert(alignment <= DEFAULT_ALIGNMENT);
if (!b) {
log_error("Out of memory");
@@ -226,6 +226,10 @@ int dm_pool_grow_object(struct dm_pool *p, const void *extra, size_t delta)
struct block *new;
size_t new_size;
+ if (p->locked)
+ log_error(INTERNAL_ERROR "Grow objects in locked pool %s",
+ p->name);
+
if (!delta)
delta = strlen(extra);
@@ -248,7 +252,7 @@ int dm_pool_grow_object(struct dm_pool *p, const void *extra, size_t delta)
}
p->object = new;
- memcpy(new->data + new_size - delta, extra, delta);
+ memcpy((char*)new->data + new_size - delta, extra, delta);
return 1;
}
@@ -270,3 +274,19 @@ void dm_pool_abandon_object(struct dm_pool *p)
p->begun = 0;
p->object = NULL;
}
+
+static long _pool_crc(const struct dm_pool *p)
+{
+#ifndef DEBUG_ENFORCE_POOL_LOCKING
+#warning pool crc not implemented with pool debug
+#endif
+ return 0;
+}
+
+static int _pool_protect(struct dm_pool *p, int prot)
+{
+#ifdef DEBUG_ENFORCE_POOL_LOCKING
+#warning pool mprotect not implemented with pool debug
+#endif
+ return 1;
+}
diff --git a/libdm/mm/pool-fast.c b/libdm/mm/pool-fast.c
index ebd982e..61e076a 100644
--- a/libdm/mm/pool-fast.c
+++ b/libdm/mm/pool-fast.c
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
- * Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2004-2011 Red Hat, Inc. All rights reserved.
*
* This file is part of the device-mapper userspace tools.
*
@@ -14,23 +14,27 @@
*/
#ifdef VALGRIND_POOL
-#include "valgrind/memcheck.h"
+#include "memcheck.h"
#endif
#include "dmlib.h"
+#include <malloc.h>
struct chunk {
char *begin, *end;
struct chunk *prev;
-};
+} __attribute__((aligned(8)));
struct dm_pool {
struct dm_list list;
struct chunk *chunk, *spare_chunk; /* spare_chunk is a one entry free
list to stop 'bobbling' */
+ const char *name;
size_t chunk_size;
size_t object_len;
unsigned object_alignment;
+ int locked;
+ long crc;
};
static void _align_chunk(struct chunk *c, unsigned alignment);
@@ -51,6 +55,7 @@ struct dm_pool *dm_pool_create(const char *name, size_t chunk_hint)
return 0;
}
+ p->name = name;
/* round chunk_hint up to the next power of 2 */
p->chunk_size = chunk_hint + sizeof(struct chunk);
while (new_size < p->chunk_size)
@@ -236,6 +241,9 @@ void *dm_pool_end_object(struct dm_pool *p)
void dm_pool_abandon_object(struct dm_pool *p)
{
+#ifdef VALGRIND_POOL
+ VALGRIND_MAKE_MEM_NOACCESS(p->chunk, p->object_len);
+#endif
p->object_len = 0;
p->object_alignment = DEFAULT_ALIGNMENT;
}
@@ -250,12 +258,28 @@ static struct chunk *_new_chunk(struct dm_pool *p, size_t s)
struct chunk *c;
if (p->spare_chunk &&
- ((p->spare_chunk->end - p->spare_chunk->begin) >= s)) {
+ ((p->spare_chunk->end - p->spare_chunk->begin) >= (ptrdiff_t)s)) {
/* reuse old chunk */
c = p->spare_chunk;
p->spare_chunk = 0;
} else {
- if (!(c = dm_malloc(s))) {
+#ifdef DEBUG_ENFORCE_POOL_LOCKING
+ if (!pagesize) {
+ pagesize = getpagesize(); /* lvm_pagesize(); */
+ pagesize_mask = pagesize - 1;
+ }
+ /*
+ * Allocate page aligned size so malloc could work.
+ * Otherwise page fault would happen from pool unrelated
+ * memory writes of internal malloc pointers.
+ */
+# define aligned_malloc(s) (posix_memalign((void**)&c, pagesize, \
+ ALIGN_ON_PAGE(s)) == 0)
+#else
+# define aligned_malloc(s) (c = dm_malloc(s))
+#endif /* DEBUG_ENFORCE_POOL_LOCKING */
+ if (!aligned_malloc(s)) {
+#undef aligned_malloc
log_error("Out of memory. Requested %" PRIsize_t
" bytes.", s);
return NULL;
@@ -276,11 +300,59 @@ static struct chunk *_new_chunk(struct dm_pool *p, size_t s)
static void _free_chunk(struct chunk *c)
{
- if (c) {
#ifdef VALGRIND_POOL
- VALGRIND_MAKE_MEM_UNDEFINED(c, c->end - (char *) c);
+# ifdef DEBUG_MEM
+ if (c)
+ VALGRIND_MAKE_MEM_UNDEFINED(c + 1, c->end - (char *) (c + 1));
+# endif
+#endif
+#ifdef DEBUG_ENFORCE_POOL_LOCKING
+ /* since DEBUG_MEM is using own memory list */
+ free(c); /* for posix_memalign() */
+#else
+ dm_free(c);
+#endif
+}
+
+
+/**
+ * Calc crc/hash from pool's memory chunks with internal pointers
+ */
+static long _pool_crc(const struct dm_pool *p)
+{
+ long crc_hash = 0;
+#ifndef DEBUG_ENFORCE_POOL_LOCKING
+ const struct chunk *c;
+ const long *ptr, *end;
+
+ for (c = p->chunk; c; c = c->prev) {
+ end = (const long *) (c->begin < c->end ? (long) c->begin & ~7: (long) c->end);
+ ptr = (const long *) c;
+#ifdef VALGRIND_POOL
+ VALGRIND_MAKE_MEM_DEFINED(ptr, (end - ptr) * sizeof(*end));
#endif
+ while (ptr < end) {
+ crc_hash += *ptr++;
+ crc_hash += (crc_hash << 10);
+ crc_hash ^= (crc_hash >> 6);
+ }
+ }
+#endif /* DEBUG_ENFORCE_POOL_LOCKING */
+
+ return crc_hash;
+}
+
+static int _pool_protect(struct dm_pool *p, int prot)
+{
+#ifdef DEBUG_ENFORCE_POOL_LOCKING
+ struct chunk *c;
- dm_free(c);
+ for (c = p->chunk; c; c = c->prev) {
+ if (mprotect(c, (size_t) ((c->end - (char *) c) - 1), prot) != 0) {
+ log_sys_error("mprotect", "");
+ return 0;
+ }
}
+#endif
+ return 1;
}
diff --git a/libdm/mm/pool.c b/libdm/mm/pool.c
index 35bfffa..b81a9b6 100644
--- a/libdm/mm/pool.c
+++ b/libdm/mm/pool.c
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
- * Copyright (C) 2004-2005 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2004-2011 Red Hat, Inc. All rights reserved.
*
* This file is part of the device-mapper userspace tools.
*
@@ -14,11 +14,31 @@
*/
#include "dmlib.h"
+#include <sys/mman.h>
/* FIXME: thread unsafe */
static DM_LIST_INIT(_dm_pools);
void dm_pools_check_leaks(void);
+#ifdef DEBUG_ENFORCE_POOL_LOCKING
+#ifdef DEBUG_POOL
+#error Do not use DEBUG_POOL with DEBUG_ENFORCE_POOL_LOCKING
+#endif
+
+/*
+ * Use mprotect system call to ensure all locked pages are not writable.
+ * Generates segmentation fault with write access to the locked pool.
+ *
+ * - Implementation is using posix_memalign() to get page aligned
+ * memory blocks (could be implemented also through malloc).
+ * - Only pool-fast is properly handled for now.
+ * - Checksum is slower compared to mprotect.
+ */
+static size_t pagesize = 0;
+static size_t pagesize_mask = 0;
+#define ALIGN_ON_PAGE(size) (((size) + (pagesize_mask)) & ~(pagesize_mask))
+#endif
+
#ifdef DEBUG_POOL
#include "pool-debug.c"
#else
@@ -27,7 +47,7 @@ void dm_pools_check_leaks(void);
char *dm_pool_strdup(struct dm_pool *p, const char *str)
{
- char *ret = dm_pool_alloc(p, strlen(str) + 1);
+ char *ret = dm_pool_alloc_aligned(p, strlen(str) + 1, 2);
if (ret)
strcpy(ret, str);
@@ -37,7 +57,7 @@ char *dm_pool_strdup(struct dm_pool *p, const char *str)
char *dm_pool_strndup(struct dm_pool *p, const char *str, size_t n)
{
- char *ret = dm_pool_alloc(p, n + 1);
+ char *ret = dm_pool_alloc_aligned(p, n + 1, 2);
if (ret) {
strncpy(ret, str, n);
@@ -71,7 +91,93 @@ void dm_pools_check_leaks(void)
p->orig_pool,
p->name, p->stats.bytes);
#else
- log_error(" [%p]", p);
+ log_error(" [%p] %s", p, p->name);
#endif
}
+ log_error(INTERNAL_ERROR "Unreleased memory pool(s) found.");
+}
+
+/**
+ * Status of locked pool.
+ *
+ * \param p
+ * Pool to be tested for lock status.
+ *
+ * \return
+ * 1 when the pool is locked, 0 otherwise.
+ */
+int dm_pool_locked(struct dm_pool *p)
+{
+ return p->locked;
+}
+
+/**
+ * Lock memory pool.
+ *
+ * \param p
+ * Pool to be locked.
+ *
+ * \param crc
+ * Bool specifies whether to store the pool crc/hash checksum.
+ *
+ * \return
+ * 1 (success) when the pool was preperly locked, 0 otherwise.
+ */
+int dm_pool_lock(struct dm_pool *p, int crc)
+{
+ if (p->locked) {
+ log_error(INTERNAL_ERROR "Pool %s is already locked.",
+ p->name);
+ return 0;
+ }
+
+ if (crc)
+ p->crc = _pool_crc(p); /* Get crc for pool */
+
+ if (!_pool_protect(p, PROT_READ)) {
+ _pool_protect(p, PROT_READ | PROT_WRITE);
+ return_0;
+ }
+
+ p->locked = 1;
+
+ log_debug("Pool %s is locked.", p->name);
+
+ return 1;
+}
+
+/**
+ * Unlock memory pool.
+ *
+ * \param p
+ * Pool to be unlocked.
+ *
+ * \param crc
+ * Bool enables compare of the pool crc/hash with the stored value
+ * at pool lock. The pool is not properly unlocked if there is a mismatch.
+ *
+ * \return
+ * 1 (success) when the pool was properly unlocked, 0 otherwise.
+ */
+int dm_pool_unlock(struct dm_pool *p, int crc)
+{
+ if (!p->locked) {
+ log_error(INTERNAL_ERROR "Pool %s is already unlocked.",
+ p->name);
+ return 0;
+ }
+
+ p->locked = 0;
+
+ if (!_pool_protect(p, PROT_READ | PROT_WRITE))
+ return_0;
+
+ log_debug("Pool %s is unlocked.", p->name);
+
+ if (crc && (p->crc != _pool_crc(p))) {
+ log_error(INTERNAL_ERROR "Pool %s crc mismatch.", p->name);
+ return 0;
+ }
+
+ return 1;
}
diff --git a/libdm/regex/matcher.c b/libdm/regex/matcher.c
index 9590865..aa58d98 100644
--- a/libdm/regex/matcher.c
+++ b/libdm/regex/matcher.c
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
- * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2004-2012 Red Hat, Inc. All rights reserved.
*
* This file is part of the device-mapper userspace tools.
*
@@ -98,21 +98,27 @@ static void _fill_table(struct dm_regex *m, struct rx_node *rx)
m->charsets[m->charsets_entered++] = rx;
}
-static void _create_bitsets(struct dm_regex *m)
+static int _create_bitsets(struct dm_regex *m)
{
- int i;
+ unsigned i;
+ struct rx_node *n;
for (i = 0; i < m->num_nodes; i++) {
- struct rx_node *n = m->nodes[i];
- n->firstpos = dm_bitset_create(m->scratch, m->num_charsets);
- n->lastpos = dm_bitset_create(m->scratch, m->num_charsets);
- n->followpos = dm_bitset_create(m->scratch, m->num_charsets);
+ n = m->nodes[i];
+ if (!(n->firstpos = dm_bitset_create(m->scratch, m->num_charsets)))
+ return_0;
+ if (!(n->lastpos = dm_bitset_create(m->scratch, m->num_charsets)))
+ return_0;
+ if (!(n->followpos = dm_bitset_create(m->scratch, m->num_charsets)))
+ return_0;
}
+
+ return 1;
}
static void _calc_functions(struct dm_regex *m)
{
- int i, j, final = 1;
+ unsigned i, j, final = 1;
struct rx_node *rx, *c1, *c2;
for (i = 0; i < m->num_nodes; i++) {
@@ -206,14 +212,17 @@ static struct dfa_state *_create_state_queue(struct dm_pool *mem,
struct dfa_state *dfa,
dm_bitset_t bits)
{
- dfa->bits = dm_bitset_create(mem, bits[0]); /* first element is the size */
+ if (!(dfa->bits = dm_bitset_create(mem, bits[0]))) /* first element is the size */
+ return_NULL;
+
dm_bit_copy(dfa->bits, bits);
dfa->next = 0;
- dfa->final = -1;
+ dfa->final = -1;
+
return dfa;
}
-static void _calc_state(struct dm_regex *m, struct dfa_state *dfa, int a)
+static int _calc_state(struct dm_regex *m, struct dfa_state *dfa, int a)
{
int set_bits = 0, i;
dm_bitset_t dfa_bits = dfa->bits;
@@ -233,9 +242,12 @@ static void _calc_state(struct dm_regex *m, struct dfa_state *dfa, int a)
struct dfa_state *ldfa = ttree_lookup(m->tt, m->bs + 1);
if (!ldfa) {
/* push */
- ldfa = _create_dfa_state(m->mem);
- ttree_insert(m->tt, m->bs + 1, ldfa);
- tmp = _create_state_queue(m->scratch, ldfa, m->bs);
+ if (!(ldfa = _create_dfa_state(m->mem)))
+ return_0;
+
+ ttree_insert(m->tt, m->bs + 1, ldfa);
+ if (!(tmp = _create_state_queue(m->scratch, ldfa, m->bs)))
+ return_0;
if (!m->h)
m->h = m->t = tmp;
else {
@@ -247,31 +259,32 @@ static void _calc_state(struct dm_regex *m, struct dfa_state *dfa, int a)
dfa->lookup[a] = ldfa;
dm_bit_clear_all(m->bs);
}
+
+ return 1;
}
static int _calc_states(struct dm_regex *m, struct rx_node *rx)
{
unsigned iwidth = (m->num_charsets / DM_BITS_PER_INT) + 1;
struct dfa_state *dfa;
- int i, a;
+ struct rx_node *n;
+ unsigned i;
+ int a;
- m->tt = ttree_create(m->scratch, iwidth);
- if (!m->tt)
+ if (!(m->tt = ttree_create(m->scratch, iwidth)))
return_0;
if (!(m->bs = dm_bitset_create(m->scratch, m->num_charsets)))
return_0;
/* build some char maps */
- for (a = 0; a < 256; a++) {
- m->charmap[a] = dm_bitset_create(m->scratch, m->num_charsets);
- if (!m->charmap[a])
- return_0;
- }
+ for (a = 0; a < 256; a++)
+ if (!(m->charmap[a] = dm_bitset_create(m->scratch, m->num_charsets)))
+ return_0;
for (i = 0; i < m->num_nodes; i++) {
- struct rx_node *n = m->nodes[i];
- if (n->type == CHARSET) {
+ n = m->nodes[i];
+ if (n->type == CHARSET) {
for (a = dm_bit_get_first(n->charset);
a >= 0; a = dm_bit_get_next(n->charset, a))
dm_bit_set(m->charmap[a], n->charset_index);
@@ -279,13 +292,19 @@ static int _calc_states(struct dm_regex *m, struct rx_node *rx)
}
/* create first state */
- dfa = _create_dfa_state(m->mem);
+ if (!(dfa = _create_dfa_state(m->mem)))
+ return_0;
+
m->start = dfa;
ttree_insert(m->tt, rx->firstpos + 1, dfa);
/* prime the queue */
- m->h = m->t = _create_state_queue(m->scratch, dfa, rx->firstpos);
- m->dfa_copy = dm_bitset_create(m->scratch, m->num_charsets);
+ if (!(m->h = m->t = _create_state_queue(m->scratch, dfa, rx->firstpos)))
+ return_0;
+
+ if (!(m->dfa_copy = dm_bitset_create(m->scratch, m->num_charsets)))
+ return_0;
+
return 1;
}
@@ -293,7 +312,7 @@ static int _calc_states(struct dm_regex *m, struct rx_node *rx)
* Forces all the dfa states to be calculated up front, ie. what
* _calc_states() used to do before we switched to calculating on demand.
*/
-static void _force_states(struct dm_regex *m)
+static int _force_states(struct dm_regex *m)
{
int a;
@@ -306,15 +325,18 @@ static void _force_states(struct dm_regex *m)
/* iterate through all the inputs for this state */
dm_bit_clear_all(m->bs);
for (a = 0; a < 256; a++)
- _calc_state(m, s, a);
+ if (!_calc_state(m, s, a))
+ return_0;
}
+
+ return 1;
}
struct dm_regex *dm_regex_create(struct dm_pool *mem, const char * const *patterns,
unsigned num_patterns)
{
char *all, *ptr;
- int i;
+ unsigned i;
size_t len = 0;
struct rx_node *rx;
struct dm_regex *m;
@@ -347,24 +369,29 @@ struct dm_regex *dm_regex_create(struct dm_pool *mem, const char * const *patter
m->mem = mem;
m->scratch = scratch;
m->num_nodes = _count_nodes(rx);
- m->num_charsets = _count_charsets(rx);
- _enumerate_charsets(rx);
- m->nodes = dm_pool_alloc(scratch, sizeof(*m->nodes) * m->num_nodes);
- if (!m->nodes)
+ m->num_charsets = _count_charsets(rx);
+ _enumerate_charsets(rx);
+ if (!(m->nodes = dm_pool_alloc(scratch, sizeof(*m->nodes) * m->num_nodes)))
goto_bad;
- m->charsets = dm_pool_alloc(scratch, sizeof(*m->charsets) * m->num_charsets);
- if (!m->charsets)
- goto_bad;
+ if (!(m->charsets = dm_pool_alloc(scratch, sizeof(*m->charsets) * m->num_charsets)))
+ goto_bad;
_fill_table(m, rx);
- _create_bitsets(m);
+
+ if (!_create_bitsets(m))
+ goto_bad;
+
_calc_functions(m);
- _calc_states(m, rx);
+
+ if (!_calc_states(m, rx))
+ goto_bad;
+
return m;
bad:
dm_pool_free(mem, m);
+
return NULL;
}
@@ -373,14 +400,17 @@ static struct dfa_state *_step_matcher(struct dm_regex *m, int c, struct dfa_sta
struct dfa_state *ns;
if (!(ns = cs->lookup[(unsigned char) c])) {
- _calc_state(m, cs, (unsigned char) c);
+ if (!_calc_state(m, cs, (unsigned char) c))
+ return_NULL;
+
if (!(ns = cs->lookup[(unsigned char) c]))
return NULL;
}
// yuck, we have to special case the target trans
- if (ns->final == -1)
- _calc_state(m, ns, TARGET_TRANS);
+ if ((ns->final == -1) &&
+ !_calc_state(m, ns, TARGET_TRANS))
+ return_NULL;
if (ns->final && (ns->final > *r))
*r = ns->final;
@@ -433,14 +463,14 @@ struct printer {
unsigned next_index;
};
-static uint32_t randomise_(uint32_t n)
+static uint32_t _randomise(uint32_t n)
{
/* 2^32 - 5 */
uint32_t const prime = (~0) - 4;
return n * prime;
}
-static int seen_(struct node_list *n, struct dfa_state *node, uint32_t *i)
+static int _seen(struct node_list *n, struct dfa_state *node, uint32_t *i)
{
while (n) {
if (n->node == node) {
@@ -456,32 +486,36 @@ static int seen_(struct node_list *n, struct dfa_state *node, uint32_t *i)
/*
* Push node if it's not been seen before, returning a unique index.
*/
-static uint32_t push_node_(struct printer *p, struct dfa_state *node)
+static uint32_t _push_node(struct printer *p, struct dfa_state *node)
{
uint32_t i;
- if (seen_(p->pending, node, &i) ||
- seen_(p->processed, node, &i))
+ struct node_list *n;
+
+ if (_seen(p->pending, node, &i) ||
+ _seen(p->processed, node, &i))
return i;
- else {
- struct node_list *n = dm_pool_alloc(p->mem, sizeof(*n));
- assert(n);
- n->node_id = p->next_index++;
- n->node = node;
- n->next = p->pending;
- p->pending = n;
- return n->node_id;
- }
+
+ if (!(n = dm_pool_alloc(p->mem, sizeof(*n))))
+ return_0;
+
+ n->node_id = ++p->next_index; /* start from 1, keep 0 as error code */
+ n->node = node;
+ n->next = p->pending;
+ p->pending = n;
+
+ return n->node_id;
}
/*
* Pop the front node, and fill out it's previously assigned index.
*/
-static struct dfa_state *pop_node_(struct printer *p)
+static struct dfa_state *_pop_node(struct printer *p)
{
struct dfa_state *node = NULL;
+ struct node_list *n;
- if (p->pending) {
- struct node_list *n = p->pending;
+ if (p->pending) {
+ n = p->pending;
p->pending = n->next;
n->next = p->processed;
p->processed = n;
@@ -492,22 +526,22 @@ static struct dfa_state *pop_node_(struct printer *p)
return node;
}
-static uint32_t combine_(uint32_t n1, uint32_t n2)
+static uint32_t _combine(uint32_t n1, uint32_t n2)
{
- return ((n1 << 8) | (n1 >> 24)) ^ randomise_(n2);
+ return ((n1 << 8) | (n1 >> 24)) ^ _randomise(n2);
}
-static uint32_t fingerprint_(struct printer *p)
+static uint32_t _fingerprint(struct printer *p)
{
int c;
uint32_t result = 0;
struct dfa_state *node;
- while ((node = pop_node_(p))) {
- result = combine_(result, node->final < 0 ? 0 : node->final);
+ while ((node = _pop_node(p))) {
+ result = _combine(result, (node->final < 0) ? 0 : node->final);
for (c = 0; c < 256; c++)
- result = combine_(result,
- push_node_(p, node->lookup[c]));
+ result = _combine(result,
+ _push_node(p, node->lookup[c]));
}
return result;
@@ -515,20 +549,27 @@ static uint32_t fingerprint_(struct printer *p)
uint32_t dm_regex_fingerprint(struct dm_regex *regex)
{
- uint32_t result;
struct printer p;
+ uint32_t result = 0;
struct dm_pool *mem = dm_pool_create("regex fingerprint", 1024);
- _force_states(regex);
+ if (!mem)
+ return_0;
+
+ if (!_force_states(regex))
+ goto_out;
- assert(mem);
p.mem = mem;
p.pending = NULL;
p.processed = NULL;
p.next_index = 0;
- push_node_(&p, regex->start);
- result = fingerprint_(&p);
+ if (!_push_node(&p, regex->start))
+ goto_out;
+
+ result = _fingerprint(&p);
+out:
dm_pool_destroy(mem);
+
return result;
}
diff --git a/libdm/regex/ttree.c b/libdm/regex/ttree.c
index ec97c98..00b371e 100644
--- a/libdm/regex/ttree.c
+++ b/libdm/regex/ttree.c
@@ -28,6 +28,7 @@ struct ttree {
struct node *root;
};
+__attribute__((nonnull(1)))
static struct node **_lookup_single(struct node **c, unsigned int k)
{
while (*c) {