r204 - in devmapper/trunk: . dmsetup include kernel/ioctl lib
lib/datastruct lib/ioctl lib/mm
Bastian Blank
waldi at costa.debian.org
Tue Nov 15 17:44:36 UTC 2005
Author: waldi
Date: Tue Nov 15 17:44:33 2005
New Revision: 204
Added:
devmapper/trunk/lib/datastruct/
devmapper/trunk/lib/datastruct/bitset.c
devmapper/trunk/lib/datastruct/bitset.h
devmapper/trunk/lib/datastruct/hash.c
devmapper/trunk/lib/datastruct/hash.h
devmapper/trunk/lib/libdm-deptree.c
devmapper/trunk/lib/mm/
devmapper/trunk/lib/mm/dbg_malloc.c
devmapper/trunk/lib/mm/dbg_malloc.h
devmapper/trunk/lib/mm/pool-debug.c
devmapper/trunk/lib/mm/pool-fast.c
devmapper/trunk/lib/mm/pool.c
devmapper/trunk/lib/mm/pool.h
Modified:
devmapper/trunk/ (props changed)
devmapper/trunk/VERSION
devmapper/trunk/WHATS_NEW
devmapper/trunk/configure
devmapper/trunk/configure.in
devmapper/trunk/dmsetup/dmsetup.c
devmapper/trunk/include/lib.h
devmapper/trunk/include/log.h
devmapper/trunk/kernel/ioctl/dm-ioctl.h
devmapper/trunk/lib/.exported_symbols
devmapper/trunk/lib/Makefile.in
devmapper/trunk/lib/ioctl/libdm-iface.c
devmapper/trunk/lib/ioctl/libdm-targets.h
devmapper/trunk/lib/libdevmapper.h
devmapper/trunk/lib/libdm-common.c
devmapper/trunk/lib/libdm-common.h.in
devmapper/trunk/make.tmpl.in
Log:
Merge /devmapper/upstream/current (1.02.00).
Modified: devmapper/trunk/VERSION
==============================================================================
--- devmapper/trunk/VERSION (original)
+++ devmapper/trunk/VERSION Tue Nov 15 17:44:33 2005
@@ -1 +1 @@
-1.01.05 (2005-09-26)
+1.02.00 (2005-11-10)
Modified: devmapper/trunk/WHATS_NEW
==============================================================================
--- devmapper/trunk/WHATS_NEW (original)
+++ devmapper/trunk/WHATS_NEW Tue Nov 15 17:44:33 2005
@@ -1,3 +1,15 @@
+Version 1.02.00 - 10 Nov 2005
+=============================
+ Added activation functions to library.
+ Added return macros.
+ Also suppress error if device doesn't exist with DM_DEVICE_STATUS.
+ Export dm_set_selinux_context().
+ Add dm_driver_version().
+ Added dependency tree functions to library.
+ Added hash, bitset, pool, dbg_malloc to library.
+ Added ls --tree to dmsetup.
+ Added dmsetup --nolockfs support for suspend/reload.
+
Version 1.01.05 - 26 Sep 2005
=============================
Resync list.h with LVM2.
Modified: devmapper/trunk/configure
==============================================================================
--- devmapper/trunk/configure (original)
+++ devmapper/trunk/configure Tue Nov 15 17:44:33 2005
@@ -3710,6 +3710,148 @@
done
+
+for ac_header in termios.h
+do
+as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+ echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
+else
+ # Is the header compilable?
+echo "$as_me:$LINENO: checking $ac_header usability" >&5
+echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6
+cat >conftest.$ac_ext <<_ACEOF
+#line $LINENO "configure"
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_header_compiler=no
+fi
+rm -f conftest.$ac_objext conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6
+
+# Is the header present?
+echo "$as_me:$LINENO: checking $ac_header presence" >&5
+echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6
+cat >conftest.$ac_ext <<_ACEOF
+#line $LINENO "configure"
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <$ac_header>
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+ (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null; then
+ if test -s conftest.err; then
+ ac_cpp_err=$ac_c_preproc_warn_flag
+ else
+ ac_cpp_err=
+ fi
+else
+ ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+rm -f conftest.err conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc in
+ yes:no )
+ { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+ (
+ cat <<\_ASBOX
+## ------------------------------------ ##
+## Report this to bug-autoconf at gnu.org. ##
+## ------------------------------------ ##
+_ASBOX
+ ) |
+ sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+ no:yes )
+ { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+ (
+ cat <<\_ASBOX
+## ------------------------------------ ##
+## Report this to bug-autoconf at gnu.org. ##
+## ------------------------------------ ##
+_ASBOX
+ ) |
+ sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ eval "$as_ac_Header=$ac_header_preproc"
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
+
+fi
+if test `eval echo '${'$as_ac_Header'}'` = yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
################################################################################
echo "$as_me:$LINENO: checking for an ANSI C-conforming const" >&5
echo $ECHO_N "checking for an ANSI C-conforming const... $ECHO_C" >&6
Modified: devmapper/trunk/configure.in
==============================================================================
--- devmapper/trunk/configure.in (original)
+++ devmapper/trunk/configure.in Tue Nov 15 17:44:33 2005
@@ -64,6 +64,7 @@
AC_CHECK_HEADERS(ctype.h dirent.h errno.h fcntl.h getopt.h inttypes.h limits.h stdarg.h stdio.h stdlib.h string.h sys/ioctl.h sys/param.h sys/stat.h sys/types.h unistd.h,,AC_MSG_ERROR(bailing out))
+AC_CHECK_HEADERS(termios.h)
################################################################################
dnl -- Checks for typedefs, structures, and compiler characteristics.
AC_C_CONST
Modified: devmapper/trunk/dmsetup/dmsetup.c
==============================================================================
--- devmapper/trunk/dmsetup/dmsetup.c (original)
+++ devmapper/trunk/dmsetup/dmsetup.c Tue Nov 15 17:44:33 2005
@@ -1,9 +1,12 @@
/*
* Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
- * Copyright (C) 2004 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2004-2005 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2005 NEC Corperation
*
* This file is part of the device-mapper userspace tools.
*
+ * It includes tree drawing code based on pstree: http://psmisc.sourceforge.net/
+ *
* 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 General Public License v.2.
@@ -30,6 +33,16 @@
#include <sys/wait.h>
#include <unistd.h>
#include <sys/param.h>
+#include <locale.h>
+#include <langinfo.h>
+
+#ifdef HAVE_SYS_IOCTL_H
+# include <sys/ioctl.h>
+#endif
+
+#if HAVE_TERMIOS_H
+# include <termios.h>
+#endif
#ifdef HAVE_GETOPTLONG
# include <getopt.h>
@@ -76,10 +89,12 @@
MAJOR_ARG,
MINOR_ARG,
NOHEADINGS_ARG,
+ NOLOCKFS_ARG,
NOOPENCOUNT_ARG,
NOTABLE_ARG,
OPTIONS_ARG,
TARGET_ARG,
+ TREE_ARG,
UUID_ARG,
VERBOSE_ARG,
VERSION_ARG,
@@ -92,6 +107,7 @@
static char *_fields;
static char *_target;
static char *_command;
+static struct dm_tree *_dtree;
/*
* Commands
@@ -481,32 +497,17 @@
static int _version(int argc, char **argv, void *data)
{
- int r = 0;
- struct dm_task *dmt;
char version[80];
if (dm_get_library_version(version, sizeof(version)))
printf("Library version: %s\n", version);
- if (!(dmt = dm_task_create(DM_DEVICE_VERSION)))
+ if (dm_driver_version(version, sizeof(version)))
return 0;
- if (!dm_task_run(dmt))
- goto out;
-
- if (!dm_task_get_driver_version(dmt, (char *) &version,
- sizeof(version))) {
- goto out;
- }
-
printf("Driver version: %s\n", version);
- r = 1;
-
- out:
- dm_task_destroy(dmt);
-
- return r;
+ return 1;
}
static int _simple(int task, const char *name, uint32_t event_nr, int display)
@@ -527,6 +528,9 @@
if (_switches[NOOPENCOUNT_ARG] && !dm_task_no_open_count(dmt))
goto out;
+ if (_switches[NOLOCKFS_ARG] && !dm_task_skip_lockfs(dmt))
+ goto out;
+
r = dm_task_run(dmt);
if (r && display && _switches[VERBOSE_ARG])
@@ -626,33 +630,9 @@
printf("%s\t(%u, %u)\n", name, info.major, info.minor);
}
-static int _mknodes_single(char *name)
-{
- struct dm_task *dmt;
- int r = 0;
-
- if (!(dmt = dm_task_create(DM_DEVICE_MKNODES)))
- return 0;
-
- if (!_set_task_device(dmt, name, 1))
- goto out;
-
- if (_switches[NOOPENCOUNT_ARG] && !dm_task_no_open_count(dmt))
- goto out;
-
- if (!dm_task_run(dmt))
- goto out;
-
- r = 1;
-
- out:
- dm_task_destroy(dmt);
- return r;
-}
-
static int _mknodes(int argc, char **argv, void *data)
{
- return _mknodes_single(argc > 1 ? argv[1] : NULL);
+ return dm_mknodes(argc > 1 ? argv[1] : NULL);
}
static int _exec_command(char *name)
@@ -667,7 +647,7 @@
if (argc < 0)
return 0;
- if (!_mknodes_single(name))
+ if (!dm_mknodes(name))
return 0;
n = snprintf(path, sizeof(path), "%s/%s", dm_dir(), name);
@@ -727,6 +707,7 @@
char *name = NULL;
int matched = 0;
int ls_only = 0;
+ struct dm_info info;
if (data)
name = names->name;
@@ -757,6 +738,9 @@
if (!dm_task_run(dmt))
goto out;
+ if (!dm_task_get_info(dmt, &info) || !info.exists)
+ goto out;
+
if (!name)
name = (char *) dm_task_get_name(dmt);
@@ -947,11 +931,348 @@
return 1;
}
+/*
+ * Tree drawing code
+ */
+
+enum {
+ TR_DEVICE=0, /* display device major:minor number */
+ TR_TABLE,
+ TR_STATUS,
+ TR_ACTIVE,
+ TR_RW,
+ TR_OPENCOUNT,
+ TR_UUID,
+ TR_COMPACT,
+ TR_TRUNCATE,
+ TR_BOTTOMUP,
+ NUM_TREEMODE,
+};
+
+static int _tree_switches[NUM_TREEMODE];
+
+#define TR_PRINT_ATTRIBUTE ( _tree_switches[TR_ACTIVE] || \
+ _tree_switches[TR_RW] || \
+ _tree_switches[TR_OPENCOUNT] || \
+ _tree_switches[TR_UUID] )
+
+#define TR_PRINT_TARGETS ( _tree_switches[TR_TABLE] || \
+ _tree_switches[TR_STATUS] )
+
+/* Compact - fewer newlines */
+#define TR_PRINT_COMPACT (_tree_switches[TR_COMPACT] && \
+ !TR_PRINT_ATTRIBUTE && \
+ !TR_PRINT_TARGETS)
+
+/* FIXME Get rid of this */
+#define MAX_DEPTH 100
+
+/* Drawing character definition from pstree */
+/* [pstree comment] UTF-8 defines by Johan Myreen, updated by Ben Winslow */
+#define UTF_V "\342\224\202" /* U+2502, Vertical line drawing char */
+#define UTF_VR "\342\224\234" /* U+251C, Vertical and right */
+#define UTF_H "\342\224\200" /* U+2500, Horizontal */
+#define UTF_UR "\342\224\224" /* U+2514, Up and right */
+#define UTF_HD "\342\224\254" /* U+252C, Horizontal and down */
+
+#define VT_BEG "\033(0\017" /* use graphic chars */
+#define VT_END "\033(B" /* back to normal char set */
+#define VT_V "x" /* see UTF definitions above */
+#define VT_VR "t"
+#define VT_H "q"
+#define VT_UR "m"
+#define VT_HD "w"
+
+static struct {
+ const char *empty_2; /* */
+ const char *branch_2; /* |- */
+ const char *vert_2; /* | */
+ const char *last_2; /* `- */
+ const char *single_3; /* --- */
+ const char *first_3; /* -+- */
+}
+_tsym_ascii = {
+ " ",
+ "|-",
+ "| ",
+ "`-",
+ "---",
+ "-+-"
+},
+_tsym_utf = {
+ " ",
+ UTF_VR UTF_H,
+ UTF_V " ",
+ UTF_UR UTF_H,
+ UTF_H UTF_H UTF_H,
+ UTF_H UTF_HD UTF_H
+},
+_tsym_vt100 = {
+ " ",
+ VT_BEG VT_VR VT_H VT_END,
+ VT_BEG VT_V VT_END " ",
+ VT_BEG VT_UR VT_H VT_END,
+ VT_BEG VT_H VT_H VT_H VT_END,
+ VT_BEG VT_H VT_HD VT_H VT_END
+},
+*_tsym = &_tsym_ascii;
+
+/*
+ * Tree drawing functions.
+ */
+/* FIXME Get rid of these statics - use dynamic struct */
+/* FIXME Explain what these vars are for */
+static int _tree_width[MAX_DEPTH], _tree_more[MAX_DEPTH];
+static int _termwidth = 80; /* Maximum output width */
+static int _cur_x = 1; /* Current horizontal output position */
+static char _last_char = 0;
+
+static void _out_char(char c)
+{
+ /* Only first UTF-8 char counts */
+ _cur_x += ((c & 0xc0) != 0x80);
+
+ if (!_tree_switches[TR_TRUNCATE]) {
+ putchar(c);
+ return;
+ }
+
+ /* Truncation? */
+ if (_cur_x <= _termwidth)
+ putchar(c);
+
+ if (_cur_x == _termwidth + 1 && ((c & 0xc0) != 0x80)) {
+ if (_last_char || (c & 0x80)) {
+ putchar('.');
+ putchar('.');
+ putchar('.');
+ } else {
+ _last_char = c;
+ _cur_x--;
+ }
+ }
+}
+
+static void _out_string(const char *str)
+{
+ while (*str)
+ _out_char(*str++);
+}
+
+/* non-negative integers only */
+static unsigned _out_int(unsigned num)
+{
+ unsigned digits = 0;
+ unsigned divi;
+
+ if (!num) {
+ _out_char('0');
+ return 1;
+ }
+
+ /* non zero case */
+ for (divi = 1; num / divi; divi *= 10)
+ digits++;
+
+ for (divi /= 10; divi; divi /= 10)
+ _out_char('0' + (num / divi) % 10);
+
+ return digits;
+}
+
+static void _out_newline(void)
+{
+ if (_last_char && _cur_x == _termwidth)
+ putchar(_last_char);
+ _last_char = 0;
+ putchar('\n');
+ _cur_x = 1;
+}
+
+static void _out_prefix(int depth)
+{
+ int x, d;
+
+ for (d = 0; d < depth; d++) {
+ for (x = _tree_width[d] + 1; x > 0; x--)
+ _out_char(' ');
+
+ _out_string(d == depth - 1 ?
+ !_tree_more[depth] ? _tsym->last_2 : _tsym->branch_2
+ : _tree_more[d + 1] ?
+ _tsym->vert_2 : _tsym->empty_2);
+ }
+}
+
+/*
+ * Display tree
+ */
+static void _display_tree_attributes(struct dm_tree_node *node)
+{
+ int attr = 0;
+ const char *uuid;
+ const struct dm_info *info;
+
+ uuid = dm_tree_node_get_uuid(node);
+ info = dm_tree_node_get_info(node);
+
+ if (!info->exists)
+ return;
+
+ if (_tree_switches[TR_ACTIVE]) {
+ _out_string(attr++ ? ", " : " [");
+ _out_string(info->suspended ? "SUSPENDED" : "ACTIVE");
+ }
+
+ if (_tree_switches[TR_RW]) {
+ _out_string(attr++ ? ", " : " [");
+ _out_string(info->read_only ? "RO" : "RW");
+ }
+
+ if (_tree_switches[TR_OPENCOUNT]) {
+ _out_string(attr++ ? ", " : " [");
+ (void) _out_int(info->open_count);
+ }
+
+ if (_tree_switches[TR_UUID]) {
+ _out_string(attr++ ? ", " : " [");
+ _out_string(uuid && *uuid ? uuid : "");
+ }
+
+ if (attr)
+ _out_char(']');
+}
+
+static void _display_tree_node(struct dm_tree_node *node, unsigned depth,
+ unsigned first_child, unsigned last_child,
+ unsigned has_children)
+{
+ int offset;
+ const char *name;
+ const struct dm_info *info;
+ int first_on_line = 0;
+
+ /* Sub-tree for targets has 2 more depth */
+ if (depth + 2 > MAX_DEPTH)
+ return;
+
+ name = dm_tree_node_get_name(node);
+
+ if ((!name || !*name) && !_tree_switches[TR_DEVICE])
+ return;
+
+ /* Indicate whether there are more nodes at this depth */
+ _tree_more[depth] = !last_child;
+ _tree_width[depth] = 0;
+
+ if (_cur_x == 1)
+ first_on_line = 1;
+
+ if (!TR_PRINT_COMPACT || first_on_line)
+ _out_prefix(depth);
+
+ /* Remember the starting point for compact */
+ offset = _cur_x;
+
+ if (TR_PRINT_COMPACT && !first_on_line)
+ _out_string(_tree_more[depth] ? _tsym->first_3 : _tsym->single_3);
+
+ /* display node */
+ if (name)
+ _out_string(name);
+
+ info = dm_tree_node_get_info(node);
+
+ if (_tree_switches[TR_DEVICE]) {
+ _out_string(name ? " (" : "(");
+ (void) _out_int(info->major);
+ _out_char(':');
+ (void) _out_int(info->minor);
+ _out_char(')');
+ }
+
+ /* display additional info */
+ if (TR_PRINT_ATTRIBUTE)
+ _display_tree_attributes(node);
+
+ if (TR_PRINT_COMPACT)
+ _tree_width[depth] = _cur_x - offset;
+
+ if (!TR_PRINT_COMPACT || !has_children)
+ _out_newline();
+
+ if (TR_PRINT_TARGETS) {
+ _tree_more[depth + 1] = has_children;
+ // FIXME _display_tree_targets(name, depth + 2);
+ }
+}
+
+/*
+ * Walk the dependency tree
+ */
+static void _tree_walk_children(struct dm_tree_node *node, unsigned depth)
+{
+ struct dm_tree_node *child, *next_child;
+ void *handle = NULL;
+ uint32_t inverted = _tree_switches[TR_BOTTOMUP];
+ unsigned first_child = 1;
+ unsigned has_children;
+
+ next_child = dm_tree_next_child(&handle, node, inverted);
+
+ while ((child = next_child)) {
+ next_child = dm_tree_next_child(&handle, node, inverted);
+ has_children =
+ dm_tree_node_num_children(child, inverted) ? 1 : 0;
+
+ _display_tree_node(child, depth, first_child,
+ next_child ? 0 : 1, has_children);
+
+ if (has_children)
+ _tree_walk_children(child, depth + 1);
+
+ first_child = 0;
+ }
+}
+
+static int _add_dep(int argc, char **argv, void *data)
+{
+ struct dm_names *names = (struct dm_names *) data;
+
+ if (!dm_tree_add_dev(_dtree, MAJOR(names->dev), MINOR(names->dev)))
+ return 0;
+
+ return 1;
+}
+
+/*
+ * Create and walk dependency tree
+ */
+static int _tree(int argc, char **argv, void *data)
+{
+ if (!(_dtree = dm_tree_create()))
+ return 0;
+
+ if (!_process_all(argc, argv, _add_dep))
+ return 0;
+
+ _tree_walk_children(dm_tree_find_node(_dtree, 0, 0), 0);
+
+ dm_tree_free(_dtree);
+
+ return 1;
+}
+
+/*
+ * List devices
+ */
static int _ls(int argc, char **argv, void *data)
{
if ((_switches[TARGET_ARG] && _target) ||
(_switches[EXEC_ARG] && _command))
return _status(argc, argv, data);
+ else if ((_switches[TREE_ARG]))
+ return _tree(argc, argv, data);
else
return _process_all(argc, argv, _display_name);
}
@@ -982,7 +1303,7 @@
{"reload", "<device> [<table_file>]", 0, 2, _load},
{"rename", "<device> <new_name>", 1, 2, _rename},
{"message", "<device> <sector> <message>", 2, -1, _message},
- {"ls", "[--target <target_type>] [--exec <command>]", 0, 0, _ls},
+ {"ls", "[--target <target_type>] [--exec <command>] [--tree]", 0, 0, _ls},
{"info", "[<device>]", 0, 1, _info},
{"deps", "[<device>]", 0, 1, _deps},
{"status", "[<device>] [--target <target_type>]", 0, 1, _status},
@@ -1000,7 +1321,7 @@
fprintf(out, "Usage:\n\n");
fprintf(out, "dmsetup [--version] [-v|--verbose [-v|--verbose ...]]\n"
- " [-r|--readonly] [--noopencount]\n\n");
+ " [-r|--readonly] [--noopencount] [--nolockfs]\n\n");
for (i = 0; _commands[i].name; i++)
fprintf(out, "\t%s %s\n", _commands[i].name, _commands[i].help);
fprintf(out, "\n<device> may be device name or -u <uuid> or "
@@ -1020,6 +1341,72 @@
return NULL;
}
+static int _process_tree_options(const char *options)
+{
+ const char *s, *end;
+ struct winsize winsz;
+ int len;
+
+ /* Symbol set default */
+ if (!strcmp(nl_langinfo(CODESET), "UTF-8"))
+ _tsym = &_tsym_utf;
+ else
+ _tsym = &_tsym_ascii;
+
+ /* Default */
+ _tree_switches[TR_DEVICE] = 1;
+ _tree_switches[TR_TRUNCATE] = 1;
+
+ /* parse */
+ for (s = options; s && *s; s++) {
+ len = 0;
+ for (end = s; *end && *end != ','; end++, len++)
+ ;
+ if (!strncmp(s, "device", len))
+ _tree_switches[TR_DEVICE] = 1;
+ else if (!strncmp(s, "nodevice", len))
+ _tree_switches[TR_DEVICE] = 0;
+ else if (!strncmp(s, "status", len))
+ _tree_switches[TR_STATUS] = 1;
+ else if (!strncmp(s, "table", len))
+ _tree_switches[TR_TABLE] = 1;
+ else if (!strncmp(s, "active", len))
+ _tree_switches[TR_ACTIVE] = 1;
+ else if (!strncmp(s, "open", len))
+ _tree_switches[TR_OPENCOUNT] = 1;
+ else if (!strncmp(s, "uuid", len))
+ _tree_switches[TR_UUID] = 1;
+ else if (!strncmp(s, "rw", len))
+ _tree_switches[TR_RW] = 1;
+ else if (!strncmp(s, "utf", len))
+ _tsym = &_tsym_utf;
+ else if (!strncmp(s, "vt100", len))
+ _tsym = &_tsym_vt100;
+ else if (!strncmp(s, "ascii", len))
+ _tsym = &_tsym_ascii;
+ else if (!strncmp(s, "inverted", len))
+ _tree_switches[TR_BOTTOMUP] = 1;
+ else if (!strncmp(s, "compact", len))
+ _tree_switches[TR_COMPACT] = 1;
+ else if (!strncmp(s, "notrunc", len))
+ _tree_switches[TR_TRUNCATE] = 0;
+ else {
+ fprintf(stderr, "Tree options not recognised: %s\n", s);
+ return 0;
+ }
+ if (!*end)
+ break;
+ s = end;
+ }
+
+ /* Truncation doesn't work well with vt100 drawing char */
+ if (_tsym != &_tsym_vt100)
+ if (ioctl(1, TIOCGWINSZ, &winsz) >= 0 && winsz.ws_col > 3)
+ _termwidth = winsz.ws_col - 3;
+
+ return 1;
+}
+
static int _process_switches(int *argc, char ***argv)
{
char *base, *namebase;
@@ -1034,10 +1421,12 @@
{"major", 1, &ind, MAJOR_ARG},
{"minor", 1, &ind, MINOR_ARG},
{"noheadings", 0, &ind, NOHEADINGS_ARG},
+ {"nolockfs", 0, &ind, NOLOCKFS_ARG},
{"noopencount", 0, &ind, NOOPENCOUNT_ARG},
{"notable", 0, &ind, NOTABLE_ARG},
{"options", 1, &ind, OPTIONS_ARG},
{"target", 1, &ind, TARGET_ARG},
+ {"tree", 0, &ind, TREE_ARG},
{"uuid", 1, &ind, UUID_ARG},
{"verbose", 1, &ind, VERBOSE_ARG},
{"version", 0, &ind, VERSION_ARG},
@@ -1125,8 +1514,12 @@
}
if ((ind == NOHEADINGS_ARG))
_switches[NOHEADINGS_ARG]++;
+ if ((ind == NOLOCKFS_ARG))
+ _switches[NOLOCKFS_ARG]++;
if ((ind == NOOPENCOUNT_ARG))
_switches[NOOPENCOUNT_ARG]++;
+ if ((ind == TREE_ARG))
+ _switches[TREE_ARG]++;
if ((ind == VERSION_ARG))
_switches[VERSION_ARG]++;
}
@@ -1141,11 +1534,15 @@
return 0;
}
- if (_switches[OPTIONS_ARG] && strcmp(_fields, "name")) {
+ if (_switches[COLS_ARG] && _switches[OPTIONS_ARG] &&
+ strcmp(_fields, "name")) {
fprintf(stderr, "Only -o name is supported so far.\n");
return 0;
}
+ if (_switches[TREE_ARG] && !_process_tree_options(_fields))
+ return 0;
+
*argv += optind;
*argc -= optind;
return 1;
@@ -1154,10 +1551,13 @@
int main(int argc, char **argv)
{
struct command *c;
+ int r = 1;
+
+ (void) setlocale(LC_ALL, "");
if (!_process_switches(&argc, &argv)) {
fprintf(stderr, "Couldn't process command line.\n");
- exit(1);
+ goto out;
}
if (_switches[VERSION_ARG]) {
@@ -1167,27 +1567,30 @@
if (argc == 0) {
_usage(stderr);
- exit(1);
+ goto out;
}
if (!(c = _find_command(argv[0]))) {
fprintf(stderr, "Unknown command\n");
_usage(stderr);
- exit(1);
+ goto out;
}
if (argc < c->min_args + 1 ||
(c->max_args >= 0 && argc > c->max_args + 1)) {
fprintf(stderr, "Incorrect number of arguments\n");
_usage(stderr);
- exit(1);
+ goto out;
}
doit:
if (!c->fn(argc, argv, NULL)) {
fprintf(stderr, "Command failed\n");
- exit(1);
+ goto out;
}
- return 0;
+ r = 0;
+
+out:
+ return r;
}
Modified: devmapper/trunk/include/lib.h
==============================================================================
--- devmapper/trunk/include/lib.h (original)
+++ devmapper/trunk/include/lib.h Tue Nov 15 17:44:33 2005
@@ -34,4 +34,7 @@
#include <sys/types.h>
#include <sys/stat.h>
+/* Define some portable printing types */
+#define PRIsize_t "zu"
+
#endif
Modified: devmapper/trunk/include/log.h
==============================================================================
--- devmapper/trunk/include/log.h (original)
+++ devmapper/trunk/include/log.h Tue Nov 15 17:44:33 2005
@@ -37,4 +37,8 @@
#define stack log_debug("<backtrace>") /* Backtrace on error */
+#define return_0 do { stack; return 0; } while (0)
+#define return_NULL do { stack; return NULL; } while (0)
+#define goto_out do { stack; goto out; } while (0)
+
#endif
Modified: devmapper/trunk/kernel/ioctl/dm-ioctl.h
==============================================================================
--- devmapper/trunk/kernel/ioctl/dm-ioctl.h (original)
+++ devmapper/trunk/kernel/ioctl/dm-ioctl.h Tue Nov 15 17:44:33 2005
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2001 - 2003 Sistina Software (UK) Limited.
+ * Copyright (C) 2004 - 2005 Red Hat, Inc. All rights reserved.
*
* This file is released under the LGPL.
*/
@@ -114,7 +115,7 @@
uint32_t target_count; /* in/out */
int32_t open_count; /* out */
uint32_t flags; /* in/out */
- uint32_t event_nr; /* in/out */
+ uint32_t event_nr; /* in/out */
uint32_t padding;
uint64_t dev; /* in/out */
@@ -158,7 +159,7 @@
* Used to retrieve the target dependencies.
*/
struct dm_target_deps {
- uint32_t count; /* Array size */
+ uint32_t count; /* Array size */
uint32_t padding; /* unused */
uint64_t dev[0]; /* out */
};
@@ -177,10 +178,10 @@
* Used to retrieve the target versions
*/
struct dm_target_versions {
- uint32_t next;
- uint32_t version[3];
+ uint32_t next;
+ uint32_t version[3];
- char name[0];
+ char name[0];
};
/*
@@ -273,9 +274,9 @@
#define DM_TARGET_MSG _IOWR(DM_IOCTL, DM_TARGET_MSG_CMD, struct dm_ioctl)
#define DM_VERSION_MAJOR 4
-#define DM_VERSION_MINOR 2
+#define DM_VERSION_MINOR 5
#define DM_VERSION_PATCHLEVEL 0
-#define DM_VERSION_EXTRA "-ioctl (2004-06-08)"
+#define DM_VERSION_EXTRA "-ioctl (2005-10-04)"
/* Status bits */
#define DM_READONLY_FLAG (1 << 0) /* In/Out */
@@ -302,8 +303,13 @@
#define DM_BUFFER_FULL_FLAG (1 << 8) /* Out */
/*
- * Set this to improve performance when you aren't going to use open_count
+ * Set this to improve performance when you aren't going to use open_count.
*/
#define DM_SKIP_BDGET_FLAG (1 << 9) /* In */
+/*
+ * Set this to avoid attempting to freeze any filesystem when suspending.
+ */
+#define DM_SKIP_LOCKFS_FLAG (1 << 10) /* In */
+
#endif /* _LINUX_DM_IOCTL_H */
Modified: devmapper/trunk/lib/.exported_symbols
==============================================================================
--- devmapper/trunk/lib/.exported_symbols (original)
+++ devmapper/trunk/lib/.exported_symbols Tue Nov 15 17:44:33 2005
@@ -1,10 +1,13 @@
+dm_lib_release
+dm_lib_exit
+dm_driver_version
+dm_get_library_version
dm_log_init
dm_log_init_verbose
dm_task_create
dm_task_destroy
dm_task_set_name
dm_task_set_uuid
-dm_get_library_version
dm_task_get_driver_version
dm_task_get_info
dm_task_get_deps
@@ -21,10 +24,79 @@
dm_task_set_message
dm_task_add_target
dm_task_no_open_count
-dm_get_next_target
+dm_task_skip_lockfs
dm_task_run
+dm_get_next_target
dm_set_dev_dir
dm_dir
dm_format_dev
-dm_lib_release
-dm_lib_exit
+dm_tree_create
+dm_tree_free
+dm_tree_add_dev
+dm_tree_add_new_dev
+dm_tree_node_get_name
+dm_tree_node_get_uuid
+dm_tree_node_get_info
+dm_tree_node_get_context
+dm_tree_node_num_children
+dm_tree_node_num_parents
+dm_tree_find_node
+dm_tree_find_node_by_uuid
+dm_tree_next_child
+dm_tree_next_parent
+dm_tree_deactivate_children
+dm_tree_activate_children
+dm_tree_preload_children
+dm_tree_suspend_children
+dm_tree_children_use_uuid
+dm_tree_node_add_snapshot_origin_target
+dm_tree_node_add_snapshot_target
+dm_tree_node_add_error_target
+dm_tree_node_add_zero_target
+dm_tree_node_add_linear_target
+dm_tree_node_add_striped_target
+dm_tree_node_add_mirror_target
+dm_tree_node_add_mirror_target_log
+dm_tree_node_add_target_area
+dm_is_dm_major
+dm_mknodes
+dm_malloc_aux
+dm_strdup
+dm_free_aux
+dm_realloc_aux
+dm_dump_memory
+dm_bounds_check
+dm_pool_create
+dm_pool_destroy
+dm_pool_alloc
+dm_pool_alloc_aligned
+dm_pool_empty
+dm_pool_free
+dm_pool_begin_object
+dm_pool_grow_object
+dm_pool_end_object
+dm_pool_abandon_object
+dm_pool_strdup
+dm_pool_strndup
+dm_pool_zalloc
+dm_bitset_create
+dm_bitset_destroy
+dm_bit_union
+dm_bit_get_first
+dm_bit_get_next
+dm_hash_create
+dm_hash_destroy
+dm_hash_wipe
+dm_hash_lookup
+dm_hash_insert
+dm_hash_remove
+dm_hash_lookup_binary
+dm_hash_insert_binary
+dm_hash_remove_binary
+dm_hash_get_num_entries
+dm_hash_iter
+dm_hash_get_key
+dm_hash_get_data
+dm_hash_get_first
+dm_hash_get_next
+dm_set_selinux_context
Modified: devmapper/trunk/lib/Makefile.in
==============================================================================
--- devmapper/trunk/lib/Makefile.in (original)
+++ devmapper/trunk/lib/Makefile.in Tue Nov 15 17:44:33 2005
@@ -17,15 +17,15 @@
VPATH = @srcdir@
interface = @interface@
-ifeq ("@DMEVENTD@", "yes")
- SUBDIRS += event
-endif
-
-ifeq ($(MAKECMDGOALS),distclean)
- SUBDIRS += event
-endif
-
-SOURCES = libdm-common.c libdm-file.c $(interface)/libdm-iface.c
+SOURCES =\
+ datastruct/bitset.c \
+ datastruct/hash.c \
+ libdm-common.c \
+ libdm-file.c \
+ libdm-deptree.c \
+ mm/dbg_malloc.c \
+ mm/pool.c \
+ $(interface)/libdm-iface.c
INCLUDES = -I$(interface)
Added: devmapper/trunk/lib/datastruct/bitset.c
==============================================================================
--- (empty file)
+++ devmapper/trunk/lib/datastruct/bitset.c Tue Nov 15 17:44:33 2005
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2005 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 General Public License v.2.
+ *
+ * You should have received a copy of the GNU 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 "lib.h"
+
+/* FIXME: calculate this. */
+#define INT_SHIFT 5
+
+dm_bitset_t dm_bitset_create(struct dm_pool *mem, unsigned num_bits)
+{
+ unsigned n = (num_bits / DM_BITS_PER_INT) + 2;
+ size_t size = sizeof(int) * n;
+ dm_bitset_t bs;
+
+ if (mem)
+ bs = dm_pool_zalloc(mem, size);
+ else
+ bs = dm_malloc(size);
+
+ if (!bs)
+ return NULL;
+
+ *bs = num_bits;
+
+ if (!mem)
+ dm_bit_clear_all(bs);
+
+ return bs;
+}
+
+void dm_bitset_destroy(dm_bitset_t bs)
+{
+ dm_free(bs);
+}
+
+void dm_bit_union(dm_bitset_t out, dm_bitset_t in1, dm_bitset_t in2)
+{
+ int i;
+ for (i = (in1[0] / DM_BITS_PER_INT) + 1; i; i--)
+ out[i] = in1[i] | in2[i];
+}
+
+/*
+ * FIXME: slow
+ */
+static inline int _test_word(uint32_t test, int bit)
+{
+ while (bit < DM_BITS_PER_INT) {
+ if (test & (0x1 << bit))
+ return bit;
+ bit++;
+ }
+
+ return -1;
+}
+
+int dm_bit_get_next(dm_bitset_t bs, int last_bit)
+{
+ int bit, word;
+ uint32_t test;
+
+ last_bit++; /* otherwise we'll return the same bit again */
+
+ while (last_bit < bs[0]) {
+ word = last_bit >> INT_SHIFT;
+ test = bs[word + 1];
+ bit = last_bit & (DM_BITS_PER_INT - 1);
+
+ if ((bit = _test_word(test, bit)) >= 0)
+ return (word * DM_BITS_PER_INT) + bit;
+
+ last_bit = last_bit - (last_bit & (DM_BITS_PER_INT - 1)) +
+ DM_BITS_PER_INT;
+ }
+
+ return -1;
+}
+
+int dm_bit_get_first(dm_bitset_t bs)
+{
+ return dm_bit_get_next(bs, -1);
+}
Added: devmapper/trunk/lib/datastruct/bitset.h
==============================================================================
--- (empty file)
+++ devmapper/trunk/lib/datastruct/bitset.h Tue Nov 15 17:44:33 2005
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2005 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 General Public License v.2.
+ *
+ * You should have received a copy of the GNU 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 _LVM_BITSET_H
+#define _LVM_BITSET_H
+
+#include "pool.h"
+
+#include <limits.h>
+
+typedef uint32_t *bitset_t;
+
+bitset_t bitset_create(struct pool *mem, unsigned num_bits);
+void bitset_destroy(bitset_t bs);
+
+void bit_union(bitset_t out, bitset_t in1, bitset_t in2);
+int bit_get_first(bitset_t bs);
+int bit_get_next(bitset_t bs, int last_bit);
+
+#define BITS_PER_INT (sizeof(int) * CHAR_BIT)
+
+#define bit(bs, i) \
+ (bs[(i / BITS_PER_INT) + 1] & (0x1 << (i & (BITS_PER_INT - 1))))
+
+#define bit_set(bs, i) \
+ (bs[(i / BITS_PER_INT) + 1] |= (0x1 << (i & (BITS_PER_INT - 1))))
+
+#define bit_clear(bs, i) \
+ (bs[(i / BITS_PER_INT) + 1] &= ~(0x1 << (i & (BITS_PER_INT - 1))))
+
+#define bit_set_all(bs) \
+ memset(bs + 1, -1, ((*bs / BITS_PER_INT) + 1) * sizeof(int))
+
+#define bit_clear_all(bs) \
+ memset(bs + 1, 0, ((*bs / BITS_PER_INT) + 1) * sizeof(int))
+
+#define bit_copy(bs1, bs2) \
+ memcpy(bs1 + 1, bs2 + 1, ((*bs1 / BITS_PER_INT) + 1) * sizeof(int))
+
+#endif
Added: devmapper/trunk/lib/datastruct/hash.c
==============================================================================
--- (empty file)
+++ devmapper/trunk/lib/datastruct/hash.c Tue Nov 15 17:44:33 2005
@@ -0,0 +1,261 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2005 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 General Public License v.2.
+ *
+ * You should have received a copy of the GNU 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 "lib.h"
+
+struct dm_hash_node {
+ struct dm_hash_node *next;
+ void *data;
+ int keylen;
+ char key[0];
+};
+
+struct dm_hash_table {
+ int num_nodes;
+ int num_slots;
+ struct dm_hash_node **slots;
+};
+
+/* Permutation of the Integers 0 through 255 */
+static unsigned char _nums[] = {
+ 1, 14, 110, 25, 97, 174, 132, 119, 138, 170, 125, 118, 27, 233, 140, 51,
+ 87, 197, 177, 107, 234, 169, 56, 68, 30, 7, 173, 73, 188, 40, 36, 65,
+ 49, 213, 104, 190, 57, 211, 148, 223, 48, 115, 15, 2, 67, 186, 210, 28,
+ 12, 181, 103, 70, 22, 58, 75, 78, 183, 167, 238, 157, 124, 147, 172,
+ 144,
+ 176, 161, 141, 86, 60, 66, 128, 83, 156, 241, 79, 46, 168, 198, 41, 254,
+ 178, 85, 253, 237, 250, 154, 133, 88, 35, 206, 95, 116, 252, 192, 54,
+ 221,
+ 102, 218, 255, 240, 82, 106, 158, 201, 61, 3, 89, 9, 42, 155, 159, 93,
+ 166, 80, 50, 34, 175, 195, 100, 99, 26, 150, 16, 145, 4, 33, 8, 189,
+ 121, 64, 77, 72, 208, 245, 130, 122, 143, 55, 105, 134, 29, 164, 185,
+ 194,
+ 193, 239, 101, 242, 5, 171, 126, 11, 74, 59, 137, 228, 108, 191, 232,
+ 139,
+ 6, 24, 81, 20, 127, 17, 91, 92, 251, 151, 225, 207, 21, 98, 113, 112,
+ 84, 226, 18, 214, 199, 187, 13, 32, 94, 220, 224, 212, 247, 204, 196,
+ 43,
+ 249, 236, 45, 244, 111, 182, 153, 136, 129, 90, 217, 202, 19, 165, 231,
+ 71,
+ 230, 142, 96, 227, 62, 179, 246, 114, 162, 53, 160, 215, 205, 180, 47,
+ 109,
+ 44, 38, 31, 149, 135, 0, 216, 52, 63, 23, 37, 69, 39, 117, 146, 184,
+ 163, 200, 222, 235, 248, 243, 219, 10, 152, 131, 123, 229, 203, 76, 120,
+ 209
+};
+
+static struct dm_hash_node *_create_node(const char *str, int len)
+{
+ struct dm_hash_node *n = dm_malloc(sizeof(*n) + len);
+
+ if (n) {
+ memcpy(n->key, str, len);
+ n->keylen = len;
+ }
+
+ return n;
+}
+
+static unsigned _hash(const char *str, uint32_t len)
+{
+ unsigned long h = 0, g, i;
+
+ for (i = 0; i < len; i++) {
+ h <<= 4;
+ h += _nums[(int) *str++];
+ g = h & ((unsigned long) 0xf << 16u);
+ if (g) {
+ h ^= g >> 16u;
+ h ^= g >> 5u;
+ }
+ }
+
+ return h;
+}
+
+struct dm_hash_table *dm_hash_create(unsigned size_hint)
+{
+ size_t len;
+ unsigned new_size = 16u;
+ struct dm_hash_table *hc = dm_malloc(sizeof(*hc));
+
+ if (!hc) {
+ stack;
+ return 0;
+ }
+
+ memset(hc, 0, sizeof(*hc));
+
+ /* round size hint up to a power of two */
+ while (new_size < size_hint)
+ new_size = new_size << 1;
+
+ hc->num_slots = new_size;
+ len = sizeof(*(hc->slots)) * new_size;
+ if (!(hc->slots = dm_malloc(len))) {
+ stack;
+ goto bad;
+ }
+ memset(hc->slots, 0, len);
+ return hc;
+
+ bad:
+ dm_free(hc->slots);
+ dm_free(hc);
+ return 0;
+}
+
+static void _free_nodes(struct dm_hash_table *t)
+{
+ struct dm_hash_node *c, *n;
+ int i;
+
+ for (i = 0; i < t->num_slots; i++)
+ for (c = t->slots[i]; c; c = n) {
+ n = c->next;
+ dm_free(c);
+ }
+}
+
+void dm_hash_destroy(struct dm_hash_table *t)
+{
+ _free_nodes(t);
+ dm_free(t->slots);
+ dm_free(t);
+}
+
+static inline struct dm_hash_node **_find(struct dm_hash_table *t, const char *key,
+ uint32_t len)
+{
+ unsigned h = _hash(key, len) & (t->num_slots - 1);
+ struct dm_hash_node **c;
+
+ for (c = &t->slots[h]; *c; c = &((*c)->next))
+ if (!memcmp(key, (*c)->key, len))
+ break;
+
+ return c;
+}
+
+void *dm_hash_lookup_binary(struct dm_hash_table *t, const char *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,
+ uint32_t len, void *data)
+{
+ struct dm_hash_node **c = _find(t, key, len);
+
+ if (*c)
+ (*c)->data = data;
+ else {
+ struct dm_hash_node *n = _create_node(key, len);
+
+ if (!n)
+ return 0;
+
+ n->data = data;
+ n->next = 0;
+ *c = n;
+ t->num_nodes++;
+ }
+
+ return 1;
+}
+
+void dm_hash_remove_binary(struct dm_hash_table *t, const char *key,
+ uint32_t len)
+{
+ struct dm_hash_node **c = _find(t, key, len);
+
+ if (*c) {
+ struct dm_hash_node *old = *c;
+ *c = (*c)->next;
+ dm_free(old);
+ t->num_nodes--;
+ }
+}
+
+void *dm_hash_lookup(struct dm_hash_table *t, const char *key)
+{
+ return dm_hash_lookup_binary(t, key, strlen(key) + 1);
+}
+
+int dm_hash_insert(struct dm_hash_table *t, const char *key, void *data)
+{
+ return dm_hash_insert_binary(t, key, strlen(key) + 1, data);
+}
+
+void dm_hash_remove(struct dm_hash_table *t, const char *key)
+{
+ dm_hash_remove_binary(t, key, strlen(key) + 1);
+}
+
+unsigned dm_hash_get_num_entries(struct dm_hash_table *t)
+{
+ return t->num_nodes;
+}
+
+void dm_hash_iter(struct dm_hash_table *t, dm_hash_iterate_fn f)
+{
+ struct dm_hash_node *c;
+ int i;
+
+ for (i = 0; i < t->num_slots; i++)
+ for (c = t->slots[i]; c; c = c->next)
+ f(c->data);
+}
+
+void dm_hash_wipe(struct dm_hash_table *t)
+{
+ _free_nodes(t);
+ memset(t->slots, 0, sizeof(struct dm_hash_node *) * t->num_slots);
+ t->num_nodes = 0;
+}
+
+char *dm_hash_get_key(struct dm_hash_table *t, struct dm_hash_node *n)
+{
+ return n->key;
+}
+
+void *dm_hash_get_data(struct dm_hash_table *t, struct dm_hash_node *n)
+{
+ return n->data;
+}
+
+static struct dm_hash_node *_next_slot(struct dm_hash_table *t, unsigned s)
+{
+ struct dm_hash_node *c = NULL;
+ int i;
+
+ for (i = s; i < t->num_slots && !c; i++)
+ c = t->slots[i];
+
+ return c;
+}
+
+struct dm_hash_node *dm_hash_get_first(struct dm_hash_table *t)
+{
+ return _next_slot(t, 0);
+}
+
+struct dm_hash_node *dm_hash_get_next(struct dm_hash_table *t, struct dm_hash_node *n)
+{
+ unsigned h = _hash(n->key, n->keylen) & (t->num_slots - 1);
+ return n->next ? n->next : _next_slot(t, h + 1);
+}
Added: devmapper/trunk/lib/datastruct/hash.h
==============================================================================
--- (empty file)
+++ devmapper/trunk/lib/datastruct/hash.h Tue Nov 15 17:44:33 2005
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2005 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 General Public License v.2.
+ *
+ * You should have received a copy of the GNU 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 _LVM_HASH_H
+#define _LVM_HASH_H
+
+struct hash_table;
+struct hash_node;
+
+typedef void (*iterate_fn) (void *data);
+
+struct hash_table *hash_create(unsigned size_hint);
+void hash_destroy(struct hash_table *t);
+void hash_wipe(struct hash_table *t);
+
+void *hash_lookup(struct hash_table *t, const char *key);
+int hash_insert(struct hash_table *t, const char *key, void *data);
+void hash_remove(struct hash_table *t, const char *key);
+
+void *hash_lookup_binary(struct hash_table *t, const char *key, uint32_t len);
+int hash_insert_binary(struct hash_table *t, const char *key, uint32_t len,
+ void *data);
+void hash_remove_binary(struct hash_table *t, const char *key, uint32_t len);
+
+unsigned hash_get_num_entries(struct hash_table *t);
+void hash_iter(struct hash_table *t, iterate_fn f);
+
+char *hash_get_key(struct hash_table *t, struct hash_node *n);
+void *hash_get_data(struct hash_table *t, struct hash_node *n);
+struct hash_node *hash_get_first(struct hash_table *t);
+struct hash_node *hash_get_next(struct hash_table *t, struct hash_node *n);
+
+#define hash_iterate(v, h) \
+ for (v = hash_get_first(h); v; \
+ v = hash_get_next(h, v))
+
+#endif
Modified: devmapper/trunk/lib/ioctl/libdm-iface.c
==============================================================================
--- devmapper/trunk/lib/ioctl/libdm-iface.c (original)
+++ devmapper/trunk/lib/ioctl/libdm-iface.c Tue Nov 15 17:44:33 2005
@@ -56,10 +56,13 @@
#define PROC_DEVICES "/proc/devices"
#define MISC_NAME "misc"
+#define NUMBER_OF_MAJORS 4096
+
/* dm major version no for running kernel */
static int _dm_version = DM_VERSION_MAJOR;
static int _log_suppress = 0;
+static dm_bitset_t _dm_bitset = NULL;
static int _control_fd = -1;
static int _version_checked = 0;
static int _version_ok = 1;
@@ -119,12 +122,17 @@
}
#ifdef DM_IOCTLS
+/*
+ * Set number to NULL to populate _dm_bitset - otherwise first
+ * match is returned.
+ */
static int _get_proc_number(const char *file, const char *name,
uint32_t *number)
{
FILE *fl;
char nm[256];
int c;
+ uint32_t num;
if (!(fl = fopen(file, "r"))) {
log_error("%s: fopen failed: %s", file, strerror(errno));
@@ -132,10 +140,14 @@
}
while (!feof(fl)) {
- if (fscanf(fl, "%d %255s\n", number, &nm[0]) == 2) {
+ if (fscanf(fl, "%d %255s\n", &num, &nm[0]) == 2) {
if (!strcmp(name, nm)) {
- fclose(fl);
- return 1;
+ if (number) {
+ *number = num;
+ fclose(fl);
+ return 1;
+ }
+ dm_bit_set(_dm_bitset, num);
}
} else do {
c = fgetc(fl);
@@ -143,8 +155,12 @@
}
fclose(fl);
- log_error("%s: No entry for %s found", file, name);
- return 0;
+ if (number) {
+ log_error("%s: No entry for %s found", file, name);
+ return 0;
+ }
+
+ return 1;
}
static int _control_device_number(uint32_t *major, uint32_t *minor)
@@ -230,6 +246,35 @@
}
#endif
+static int _create_dm_bitset(void)
+{
+#ifdef DM_IOCTLS
+ if (_dm_bitset)
+ return 1;
+
+ if (!(_dm_bitset = dm_bitset_create(NULL, NUMBER_OF_MAJORS)))
+ return 0;
+
+ if (!_get_proc_number(PROC_DEVICES, DM_NAME, NULL)) {
+ dm_bitset_destroy(_dm_bitset);
+ _dm_bitset = NULL;
+ return 0;
+ }
+
+ return 1;
+#else
+ return 0;
+#endif
+}
+
+int dm_is_dm_major(uint32_t major)
+{
+ if (!_create_dm_bitset())
+ return 0;
+
+ return dm_bit(_dm_bitset, major) ? 1 : 0;
+}
+
static int _open_control(void)
{
#ifdef DM_IOCTLS
@@ -253,6 +298,11 @@
goto error;
}
+ if (!_create_dm_bitset()) {
+ log_error("Failed to set up list of device-mapper major numbers");
+ return 0;
+ }
+
return 1;
error:
@@ -269,27 +319,27 @@
for (t = dmt->head; t; t = n) {
n = t->next;
- free(t->params);
- free(t->type);
- free(t);
+ dm_free(t->params);
+ dm_free(t->type);
+ dm_free(t);
}
if (dmt->dev_name)
- free(dmt->dev_name);
+ dm_free(dmt->dev_name);
if (dmt->newname)
- free(dmt->newname);
+ dm_free(dmt->newname);
if (dmt->message)
- free(dmt->message);
+ dm_free(dmt->message);
if (dmt->dmi.v4)
- free(dmt->dmi.v4);
+ dm_free(dmt->dmi.v4);
if (dmt->uuid)
- free(dmt->uuid);
+ dm_free(dmt->uuid);
- free(dmt);
+ dm_free(dmt);
}
/*
@@ -470,7 +520,7 @@
if (len < min_size)
len = min_size;
- if (!(dmi = malloc(len)))
+ if (!(dmi = dm_malloc(len)))
return NULL;
memset(dmi, 0, len);
@@ -519,7 +569,7 @@
return dmi;
bad:
- free(dmi);
+ dm_free(dmi);
return NULL;
}
@@ -592,7 +642,7 @@
dmi = _flatten_v1(dmt);
if (!dmi) {
- log_error("Couldn't create ioctl argument");
+ log_error("Couldn't create ioctl argument.");
return 0;
}
@@ -670,7 +720,7 @@
dmt->type = DM_DEVICE_INFO;
if (!dm_task_run(dmt))
goto bad;
- free(dmi); /* We'll use what info returned */
+ dm_free(dmi); /* We'll use what info returned */
return 1;
}
@@ -678,7 +728,7 @@
return 1;
bad:
- free(dmi);
+ dm_free(dmi);
return 0;
}
@@ -918,7 +968,7 @@
int dm_task_set_newname(struct dm_task *dmt, const char *newname)
{
- if (!(dmt->newname = strdup(newname))) {
+ if (!(dmt->newname = dm_strdup(newname))) {
log_error("dm_task_set_newname: strdup(%s) failed", newname);
return 0;
}
@@ -928,7 +978,7 @@
int dm_task_set_message(struct dm_task *dmt, const char *message)
{
- if (!(dmt->message = strdup(message))) {
+ if (!(dmt->message = dm_strdup(message))) {
log_error("dm_task_set_message: strdup(%s) failed", message);
return 0;
}
@@ -950,6 +1000,13 @@
return 1;
}
+int dm_task_skip_lockfs(struct dm_task *dmt)
+{
+ dmt->skip_lockfs = 1;
+
+ return 1;
+}
+
int dm_task_set_event_nr(struct dm_task *dmt, uint32_t event_nr)
{
dmt->event_nr = event_nr;
@@ -960,7 +1017,7 @@
struct target *create_target(uint64_t start, uint64_t len, const char *type,
const char *params)
{
- struct target *t = malloc(sizeof(*t));
+ struct target *t = dm_malloc(sizeof(*t));
if (!t) {
log_error("create_target: malloc(%d) failed", sizeof(*t));
@@ -969,12 +1026,12 @@
memset(t, 0, sizeof(*t));
- if (!(t->params = strdup(params))) {
+ if (!(t->params = dm_strdup(params))) {
log_error("create_target: strdup(params) failed");
goto bad;
}
- if (!(t->type = strdup(type))) {
+ if (!(t->type = dm_strdup(type))) {
log_error("create_target: strdup(type) failed");
goto bad;
}
@@ -984,9 +1041,9 @@
return t;
bad:
- free(t->params);
- free(t->type);
- free(t);
+ dm_free(t->params);
+ dm_free(t->type);
+ dm_free(t);
return NULL;
}
@@ -1084,7 +1141,7 @@
while (repeat_count--)
len *= 2;
- if (!(dmi = malloc(len)))
+ if (!(dmi = dm_malloc(len)))
return NULL;
memset(dmi, 0, len);
@@ -1105,11 +1162,13 @@
dmi->flags |= DM_SUSPEND_FLAG;
if (dmt->read_only)
dmi->flags |= DM_READONLY_FLAG;
+ if (dmt->skip_lockfs)
+ dmi->flags |= DM_SKIP_LOCKFS_FLAG;
if (dmt->minor >= 0) {
if (dmt->major <= 0) {
- log_error("Missing major number for persistent device");
- return NULL;
+ log_error("Missing major number for persistent device.");
+ goto bad;
}
dmi->flags |= DM_PERSISTENT_DEV_FLAG;
dmi->dev = MKDEV(dmt->major, dmt->minor);
@@ -1140,7 +1199,7 @@
return dmi;
bad:
- free(dmi);
+ dm_free(dmi);
return NULL;
}
@@ -1275,7 +1334,7 @@
/* Use the original structure last so the info will be correct */
dmt->type = DM_DEVICE_RESUME;
- free(dmt->uuid);
+ dm_free(dmt->uuid);
dmt->uuid = NULL;
r = dm_task_run(dmt);
@@ -1290,7 +1349,7 @@
dmi = _flatten(dmt, repeat_count);
if (!dmi) {
- log_error("Couldn't create ioctl argument");
+ log_error("Couldn't create ioctl argument.");
return NULL;
}
@@ -1302,17 +1361,25 @@
if (dmt->no_open_count)
dmi->flags |= DM_SKIP_BDGET_FLAG;
- log_debug("dm %s %s %s%s%s %c %.0llu %s [%u]",
+ log_debug("dm %s %s %s%s%s %s%0.0d%s%0.0d%s"
+ "%s%c %.0llu %s [%u]",
_cmd_data_v4[dmt->type].name,
dmi->name, dmi->uuid, dmt->newname ? " " : "",
dmt->newname ? dmt->newname : "",
+ 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 ? ") " : "",
dmt->no_open_count ? 'N' : 'O',
dmt->sector, dmt->message ? dmt->message : "",
dmi->data_size);
#ifdef DM_IOCTLS
if (ioctl(_control_fd, command, dmi) < 0) {
if (errno == ENXIO && ((dmt->type == DM_DEVICE_INFO) ||
- (dmt->type == DM_DEVICE_MKNODES)))
+ (dmt->type == DM_DEVICE_MKNODES) ||
+ (dmt->type == DM_DEVICE_STATUS)))
dmi->flags &= ~DM_EXISTS_FLAG; /* FIXME */
else {
if (_log_suppress)
@@ -1323,7 +1390,7 @@
log_error("device-mapper ioctl "
"cmd %d failed: %s",
_IOC_NR(command), strerror(errno));
- free(dmi);
+ dm_free(dmi);
return NULL;
}
}
@@ -1375,7 +1442,7 @@
case DM_DEVICE_TABLE:
case DM_DEVICE_WAITEVENT:
_ioctl_buffer_double_factor++;
- free(dmi);
+ dm_free(dmi);
goto repeat_ioctl;
default:
log_error("Warning: libdevmapper buffer too small for data");
@@ -1421,7 +1488,7 @@
return 1;
bad:
- free(dmi);
+ dm_free(dmi);
return 0;
}
@@ -1437,6 +1504,10 @@
void dm_lib_exit(void)
{
dm_lib_release();
+ if (_dm_bitset)
+ dm_bitset_destroy(_dm_bitset);
+ _dm_bitset = NULL;
+ dm_dump_memory();
_version_ok = 1;
_version_checked = 0;
}
Modified: devmapper/trunk/lib/ioctl/libdm-targets.h
==============================================================================
--- devmapper/trunk/lib/ioctl/libdm-targets.h (original)
+++ devmapper/trunk/lib/ioctl/libdm-targets.h Tue Nov 15 17:44:33 2005
@@ -52,6 +52,7 @@
char *message;
uint64_t sector;
int no_open_count;
+ int skip_lockfs;
char *uuid;
};
Modified: devmapper/trunk/lib/libdevmapper.h
==============================================================================
--- devmapper/trunk/lib/libdevmapper.h (original)
+++ devmapper/trunk/lib/libdevmapper.h Tue Nov 15 17:44:33 2005
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
- * Copyright (C) 2004 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2004-2005 Red Hat, Inc. All rights reserved.
*
* This file is part of the device-mapper userspace tools.
*
@@ -23,6 +23,15 @@
# include <linux/types.h>
#endif
+#include <limits.h>
+#include <string.h>
+#include <stdlib.h>
+
+/*****************************************************************
+ * The first section of this file provides direct access to the
+ * individual device-mapper ioctls.
+ ****************************************************************/
+
/*
* Since it is quite laborious to build the ioctl
* arguments for the device-mapper people are
@@ -135,6 +144,7 @@
int dm_task_set_message(struct dm_task *dmt, const char *message);
int dm_task_set_sector(struct dm_task *dmt, uint64_t sector);
int dm_task_no_open_count(struct dm_task *dmt);
+int dm_task_skip_lockfs(struct dm_task *dmt);
/*
* Use these to prepare for a create or reload.
@@ -164,8 +174,367 @@
int dm_set_dev_dir(const char *dir);
const char *dm_dir(void);
-/* Release library resources */
+/*
+ * Determine whether a major number belongs to device-mapper or not.
+ */
+int dm_is_dm_major(uint32_t major);
+
+/*
+ * Release library resources
+ */
void dm_lib_release(void);
void dm_lib_exit(void) __attribute((destructor));
+/*
+ * Use NULL for all devices.
+ */
+int dm_mknodes(const char *name);
+int dm_driver_version(char *version, size_t size);
+
+/******************************************************
+ * Functions to build and manipulate trees of devices *
+ ******************************************************/
+struct dm_tree;
+struct dm_tree_node;
+
+/*
+ * Initialise an empty dependency tree.
+ *
+ * The tree consists of a root node together with one node for each mapped
+ * device which has child nodes for each device referenced in its table.
+ *
+ * Every node in the tree has one or more children and one or more parents.
+ *
+ * The root node is the parent/child of every node that doesn't have other
+ * parents/children.
+ */
+struct dm_tree *dm_tree_create(void);
+void dm_tree_free(struct dm_tree *tree);
+
+/*
+ * Add nodes to the tree for a given device and all the devices it uses.
+ */
+int dm_tree_add_dev(struct dm_tree *tree, uint32_t major, uint32_t minor);
+
+/*
+ * Add a new node to the tree if it doesn't already exist.
+ */
+struct dm_tree_node *dm_tree_add_new_dev(struct dm_tree *tree,
+ const char *name,
+ const char *uuid,
+ uint32_t major, uint32_t minor,
+ int read_only,
+ int clear_inactive,
+ void *context);
+
+/*
+ * Search for a node in the tree.
+ * Set major and minor to 0 or uuid to NULL to get the root node.
+ */
+struct dm_tree_node *dm_tree_find_node(struct dm_tree *tree,
+ uint32_t major,
+ uint32_t minor);
+struct dm_tree_node *dm_tree_find_node_by_uuid(struct dm_tree *tree,
+ const char *uuid);
+
+/*
+ * Use this to walk through all children of a given node.
+ * Set handle to NULL in first call.
+ * Returns NULL after the last child.
+ * Set inverted to use inverted tree.
+ */
+struct dm_tree_node *dm_tree_next_child(void **handle,
+ struct dm_tree_node *parent,
+ uint32_t inverted);
+
+/*
+ * Get properties of a node.
+ */
+const char *dm_tree_node_get_name(struct dm_tree_node *node);
+const char *dm_tree_node_get_uuid(struct dm_tree_node *node);
+const struct dm_info *dm_tree_node_get_info(struct dm_tree_node *node);
+void *dm_tree_node_get_context(struct dm_tree_node *node);
+
+/*
+ * Returns the number of children of the given node (excluding the root node).
+ * Set inverted for the number of parents.
+ */
+int dm_tree_node_num_children(struct dm_tree_node *node, uint32_t inverted);
+
+/*
+ * Deactivate a device plus all dependencies.
+ * Ignores devices that don't have a uuid starting with uuid_prefix.
+ */
+int dm_tree_deactivate_children(struct dm_tree_node *dnode,
+ const char *uuid_prefix,
+ size_t uuid_prefix_len);
+/*
+ * Preload/create a device plus all dependencies.
+ * Ignores devices that don't have a uuid starting with uuid_prefix.
+ */
+int dm_tree_preload_children(struct dm_tree_node *dnode,
+ const char *uuid_prefix,
+ size_t uuid_prefix_len);
+
+/*
+ * Resume a device plus all dependencies.
+ * Ignores devices that don't have a uuid starting with uuid_prefix.
+ */
+int dm_tree_activate_children(struct dm_tree_node *dnode,
+ const char *uuid_prefix,
+ size_t uuid_prefix_len);
+
+/*
+ * Suspend a device plus all dependencies.
+ * Ignores devices that don't have a uuid starting with uuid_prefix.
+ */
+int dm_tree_suspend_children(struct dm_tree_node *dnode,
+ const char *uuid_prefix,
+ size_t uuid_prefix_len);
+
+/*
+ * 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.
+ */
+int dm_tree_children_use_uuid(struct dm_tree_node *dnode,
+ const char *uuid_prefix,
+ size_t uuid_prefix_len);
+
+/*
+ * Construct tables for new nodes before activating them.
+ */
+int dm_tree_node_add_snapshot_origin_target(struct dm_tree_node *dnode,
+ uint64_t size,
+ const char *origin_uuid);
+int dm_tree_node_add_snapshot_target(struct dm_tree_node *node,
+ uint64_t size,
+ const char *origin_uuid,
+ const char *cow_uuid,
+ int persistent,
+ uint32_t chunk_size);
+int dm_tree_node_add_error_target(struct dm_tree_node *node,
+ uint64_t size);
+int dm_tree_node_add_zero_target(struct dm_tree_node *node,
+ uint64_t size);
+int dm_tree_node_add_linear_target(struct dm_tree_node *node,
+ uint64_t size);
+int dm_tree_node_add_striped_target(struct dm_tree_node *node,
+ uint64_t size,
+ uint32_t stripe_size);
+int dm_tree_node_add_mirror_target(struct dm_tree_node *node,
+ uint64_t size);
+int dm_tree_node_add_mirror_target_log(struct dm_tree_node *node,
+ uint32_t region_size,
+ unsigned clustered,
+ const char *log_uuid,
+ unsigned area_count);
+int dm_tree_node_add_target_area(struct dm_tree_node *node,
+ const char *dev_name,
+ const char *dlid,
+ uint64_t offset);
+
+/*****************************************************************************
+ * Library functions
+ *****************************************************************************/
+
+/*******************
+ * Memory management
+ *******************/
+
+void *dm_malloc_aux(size_t s, const char *file, int line);
+#define dm_malloc(s) dm_malloc_aux((s), __FILE__, __LINE__)
+
+char *dm_strdup(const char *str);
+
+#ifdef DEBUG_MEM
+
+void dm_free_aux(void *p);
+void *dm_realloc_aux(void *p, unsigned int s, const char *file, int line);
+int dm_dump_memory(void);
+void dm_bounds_check(void);
+
+# define dm_free(p) dm_free_aux(p)
+# define dm_realloc(p, s) dm_realloc_aux(p, s, __FILE__, __LINE__)
+
+#else
+
+# define dm_free(p) free(p)
+# define dm_realloc(p, s) realloc(p, s)
+# define dm_dump_memory()
+# define dm_bounds_check()
+
+#endif
+
+/*
+ * The pool allocator is useful when you are going to allocate
+ * lots of memory, use the memory for a bit, and then free the
+ * memory in one go. A surprising amount of code has this usage
+ * profile.
+ *
+ * You should think of the pool as an infinite, contiguous chunk
+ * of memory. The front of this chunk of memory contains
+ * allocated objects, the second half is free. dm_pool_alloc grabs
+ * the next 'size' bytes from the free half, in effect moving it
+ * into the allocated half. This operation is very efficient.
+ *
+ * dm_pool_free frees the allocated object *and* all objects
+ * allocated after it. It is important to note this semantic
+ * difference from malloc/free. This is also extremely
+ * efficient, since a single dm_pool_free can dispose of a large
+ * complex object.
+ *
+ * dm_pool_destroy frees all allocated memory.
+ *
+ * eg, If you are building a binary tree in your program, and
+ * know that you are only ever going to insert into your tree,
+ * and not delete (eg, maintaining a symbol table for a
+ * compiler). You can create yourself a pool, allocate the nodes
+ * from it, and when the tree becomes redundant call dm_pool_destroy
+ * (no nasty iterating through the tree to free nodes).
+ *
+ * eg, On the other hand if you wanted to repeatedly insert and
+ * remove objects into the tree, you would be better off
+ * allocating the nodes from a free list; you cannot free a
+ * single arbitrary node with pool.
+ */
+
+struct dm_pool;
+
+/* constructor and destructor */
+struct dm_pool *dm_pool_create(const char *name, size_t chunk_hint);
+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_empty(struct dm_pool *p);
+void dm_pool_free(struct dm_pool *p, void *ptr);
+
+/*
+ * Object building routines:
+ *
+ * These allow you to 'grow' an object, useful for
+ * building strings, or filling in dynamic
+ * arrays.
+ *
+ * It's probably best explained with an example:
+ *
+ * char *build_string(struct dm_pool *mem)
+ * {
+ * int i;
+ * char buffer[16];
+ *
+ * if (!dm_pool_begin_object(mem, 128))
+ * return NULL;
+ *
+ * for (i = 0; i < 50; i++) {
+ * snprintf(buffer, sizeof(buffer), "%d, ", i);
+ * if (!dm_pool_grow_object(mem, buffer, strlen(buffer)))
+ * goto bad;
+ * }
+ *
+ * // add null
+ * if (!dm_pool_grow_object(mem, "\0", 1))
+ * goto bad;
+ *
+ * return dm_pool_end_object(mem);
+ *
+ * bad:
+ *
+ * dm_pool_abandon_object(mem);
+ * return NULL;
+ *}
+ *
+ * So start an object by calling dm_pool_begin_object
+ * with a guess at the final object size - if in
+ * doubt make the guess too small.
+ *
+ * Then append chunks of data to your object with
+ * dm_pool_grow_object. Finally get your object with
+ * a call to dm_pool_end_object.
+ *
+ */
+int dm_pool_begin_object(struct dm_pool *p, size_t hint);
+int dm_pool_grow_object(struct dm_pool *p, const void *extra, size_t delta);
+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);
+
+/******************
+ * bitset functions
+ ******************/
+
+typedef uint32_t *dm_bitset_t;
+
+dm_bitset_t dm_bitset_create(struct dm_pool *mem, unsigned num_bits);
+void dm_bitset_destroy(dm_bitset_t bs);
+
+void dm_bit_union(dm_bitset_t out, dm_bitset_t in1, dm_bitset_t in2);
+int dm_bit_get_first(dm_bitset_t bs);
+int dm_bit_get_next(dm_bitset_t bs, int last_bit);
+
+#define DM_BITS_PER_INT (sizeof(int) * CHAR_BIT)
+
+#define dm_bit(bs, i) \
+ (bs[(i / DM_BITS_PER_INT) + 1] & (0x1 << (i & (DM_BITS_PER_INT - 1))))
+
+#define dm_bit_set(bs, i) \
+ (bs[(i / DM_BITS_PER_INT) + 1] |= (0x1 << (i & (DM_BITS_PER_INT - 1))))
+
+#define dm_bit_clear(bs, i) \
+ (bs[(i / DM_BITS_PER_INT) + 1] &= ~(0x1 << (i & (DM_BITS_PER_INT - 1))))
+
+#define dm_bit_set_all(bs) \
+ memset(bs + 1, -1, ((*bs / DM_BITS_PER_INT) + 1) * sizeof(int))
+
+#define dm_bit_clear_all(bs) \
+ memset(bs + 1, 0, ((*bs / DM_BITS_PER_INT) + 1) * sizeof(int))
+
+#define dm_bit_copy(bs1, bs2) \
+ memcpy(bs1 + 1, bs2 + 1, ((*bs1 / DM_BITS_PER_INT) + 1) * sizeof(int))
+
+/****************
+ * hash functions
+ ****************/
+
+struct dm_hash_table;
+struct dm_hash_node;
+
+typedef void (*dm_hash_iterate_fn) (void *data);
+
+struct dm_hash_table *dm_hash_create(unsigned size_hint);
+void dm_hash_destroy(struct dm_hash_table *t);
+void dm_hash_wipe(struct dm_hash_table *t);
+
+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 *data);
+void dm_hash_remove_binary(struct dm_hash_table *t, const char *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);
+
+char *dm_hash_get_key(struct dm_hash_table *t, struct dm_hash_node *n);
+void *dm_hash_get_data(struct dm_hash_table *t, struct dm_hash_node *n);
+struct dm_hash_node *dm_hash_get_first(struct dm_hash_table *t);
+struct dm_hash_node *dm_hash_get_next(struct dm_hash_table *t, struct dm_hash_node *n);
+
+#define dm_hash_iterate(v, h) \
+ for (v = dm_hash_get_first(h); v; \
+ v = dm_hash_get_next(h, v))
+
#endif /* LIB_DEVICE_MAPPER_H */
+
+/*********
+ * selinux
+ *********/
+int dm_set_selinux_context(const char *path, mode_t mode);
Modified: devmapper/trunk/lib/libdm-common.c
==============================================================================
--- devmapper/trunk/lib/libdm-common.c (original)
+++ devmapper/trunk/lib/libdm-common.c Tue Nov 15 17:44:33 2005
@@ -93,7 +93,7 @@
struct dm_task *dm_task_create(int type)
{
- struct dm_task *dmt = malloc(sizeof(*dmt));
+ struct dm_task *dmt = dm_malloc(sizeof(*dmt));
if (!dmt) {
log_error("dm_task_create: malloc(%d) failed", sizeof(*dmt));
@@ -123,7 +123,7 @@
struct stat st1, st2;
if (dmt->dev_name) {
- free(dmt->dev_name);
+ dm_free(dmt->dev_name);
dmt->dev_name = NULL;
}
@@ -143,7 +143,7 @@
name = pos + 1;
}
- if (!(dmt->dev_name = strdup(name))) {
+ if (!(dmt->dev_name = dm_strdup(name))) {
log_error("dm_task_set_name: strdup(%s) failed", name);
return 0;
}
@@ -154,11 +154,11 @@
int dm_task_set_uuid(struct dm_task *dmt, const char *uuid)
{
if (dmt->uuid) {
- free(dmt->uuid);
+ dm_free(dmt->uuid);
dmt->uuid = NULL;
}
- if (!(dmt->uuid = strdup(uuid))) {
+ if (!(dmt->uuid = dm_strdup(uuid))) {
log_error("dm_task_set_uuid: strdup(%s) failed", uuid);
return 0;
}
@@ -169,7 +169,6 @@
int dm_task_set_major(struct dm_task *dmt, int major)
{
dmt->major = major;
- log_debug("Setting major: %d", dmt->major);
return 1;
}
@@ -177,7 +176,6 @@
int dm_task_set_minor(struct dm_task *dmt, int minor)
{
dmt->minor = minor;
- log_debug("Setting minor: %d", dmt->minor);
return 1;
}
@@ -201,7 +199,7 @@
}
#ifdef HAVE_SELINUX
-int set_selinux_context(const char *path, mode_t mode)
+int dm_set_selinux_context(const char *path, mode_t mode)
{
security_context_t scontext;
@@ -268,7 +266,7 @@
}
#ifdef HAVE_SELINUX
- if (!set_selinux_context(path, S_IFBLK))
+ if (!dm_set_selinux_context(path, S_IFBLK))
return 0;
#endif
@@ -381,7 +379,7 @@
size_t len = strlen(dev_name) + strlen(old_name) + 2;
char *pos;
- if (!(nop = malloc(sizeof(*nop) + len))) {
+ if (!(nop = dm_malloc(sizeof(*nop) + len))) {
log_error("Insufficient memory to stack mknod operation");
return 0;
}
@@ -412,7 +410,7 @@
_do_node_op(nop->type, nop->dev_name, nop->major, nop->minor,
nop->uid, nop->gid, nop->mode, nop->old_name);
list_del(&nop->list);
- free(nop);
+ dm_free(nop);
}
}
@@ -448,3 +446,45 @@
{
return _dm_dir;
}
+
+int dm_mknodes(const char *name)
+{
+ struct dm_task *dmt;
+ int r = 0;
+
+ if (!(dmt = dm_task_create(DM_DEVICE_MKNODES)))
+ return 0;
+
+ if (name && !dm_task_set_name(dmt, name))
+ goto out;
+
+ if (!dm_task_no_open_count(dmt))
+ goto out;
+
+ r = dm_task_run(dmt);
+
+out:
+ dm_task_destroy(dmt);
+ return r;
+}
+
+int dm_driver_version(char *version, size_t size)
+{
+ struct dm_task *dmt;
+ int r = 0;
+
+ if (!(dmt = dm_task_create(DM_DEVICE_VERSION)))
+ return 0;
+
+ if (!dm_task_run(dmt))
+ log_error("Failed to get driver version");
+
+ if (!dm_task_get_driver_version(dmt, version, size))
+ goto out;
+
+ r = 1;
+
+out:
+ dm_task_destroy(dmt);
+ return r;
+}
Modified: devmapper/trunk/lib/libdm-common.h.in
==============================================================================
--- devmapper/trunk/lib/libdm-common.h.in (original)
+++ devmapper/trunk/lib/libdm-common.h.in Tue Nov 15 17:44:33 2005
@@ -28,8 +28,6 @@
int rename_dev_node(const char *old_name, const char *new_name);
void update_devs(void);
-int set_selinux_context(const char *path, mode_t mode);
-
#define DM_LIB_VERSION @DM_LIB_VERSION@
#endif
Added: devmapper/trunk/lib/libdm-deptree.c
==============================================================================
--- (empty file)
+++ devmapper/trunk/lib/libdm-deptree.c Tue Nov 15 17:44:33 2005
@@ -0,0 +1,1686 @@
+/*
+ * Copyright (C) 2005 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
+ */
+
+#include "lib.h"
+#include "libdm-targets.h"
+#include "libdm-common.h"
+#include "list.h"
+#include "kdev_t.h"
+
+#include <stdarg.h>
+#include <sys/param.h>
+
+#include <linux/dm-ioctl.h>
+
+#define MAX_TARGET_PARAMSIZE 500000
+
+/* Supported segment types */
+enum {
+ SEG_ERROR,
+ SEG_LINEAR,
+ SEG_MIRRORED,
+ SEG_SNAPSHOT,
+ SEG_SNAPSHOT_ORIGIN,
+ SEG_STRIPED,
+ SEG_ZERO,
+};
+
+/* FIXME Add crypt and multipath support */
+
+struct {
+ unsigned type;
+ const char *target;
+} dm_segtypes[] = {
+ { SEG_ERROR, "error" },
+ { SEG_LINEAR, "linear" },
+ { SEG_MIRRORED, "mirror" },
+ { SEG_SNAPSHOT, "snapshot" },
+ { SEG_SNAPSHOT_ORIGIN, "snapshot-origin" },
+ { SEG_STRIPED, "striped" },
+ { SEG_ZERO, "zero"},
+};
+
+/* Some segment types have a list of areas of other devices attached */
+struct seg_area {
+ struct list list;
+
+ struct dm_tree_node *dev_node;
+
+ uint64_t offset;
+};
+
+/* Per-segment properties */
+struct load_segment {
+ struct list list;
+
+ unsigned type;
+
+ uint64_t size;
+
+ unsigned area_count; /* Linear + Striped + Mirrored */
+ struct list areas; /* Linear + Striped + Mirrored */
+
+ uint32_t stripe_size; /* Striped */
+
+ int persistent; /* Snapshot */
+ uint32_t chunk_size; /* Snapshot */
+ struct dm_tree_node *cow; /* Snapshot */
+ struct dm_tree_node *origin; /* Snapshot + Snapshot origin */
+
+ struct dm_tree_node *log; /* Mirror */
+ uint32_t region_size; /* Mirror */
+ unsigned clustered; /* Mirror */
+ unsigned mirror_area_count; /* Mirror */
+};
+
+/* Per-device properties */
+struct load_properties {
+ int read_only;
+ uint32_t major;
+ uint32_t minor;
+
+ unsigned segment_count;
+ struct list segs;
+
+ const char *new_name;
+};
+
+/* Two of these used to join two nodes with uses and used_by. */
+struct dm_tree_link {
+ struct list list;
+ struct dm_tree_node *node;
+};
+
+struct dm_tree_node {
+ struct dm_tree *dtree;
+
+ const char *name;
+ const char *uuid;
+ struct dm_info info;
+
+ struct list uses; /* Nodes this node uses */
+ struct list used_by; /* Nodes that use this node */
+
+ void *context; /* External supplied context */
+
+ struct load_properties props; /* For creation/table (re)load */
+};
+
+struct dm_tree {
+ struct dm_pool *mem;
+ struct dm_hash_table *devs;
+ struct dm_hash_table *uuids;
+ struct dm_tree_node root;
+};
+
+/* FIXME Consider exporting this */
+static int _dm_snprintf(char *buf, size_t bufsize, const char *format, ...)
+{
+ int n;
+ va_list ap;
+
+ va_start(ap, format);
+ n = vsnprintf(buf, bufsize, format, ap);
+ va_end(ap);
+
+ if (n < 0 || (n > bufsize - 1))
+ return -1;
+
+ return n;
+}
+
+struct dm_tree *dm_tree_create(void)
+{
+ struct dm_tree *dtree;
+
+ if (!(dtree = dm_malloc(sizeof(*dtree)))) {
+ log_error("dm_tree_create malloc failed");
+ return NULL;
+ }
+
+ memset(dtree, 0, sizeof(*dtree));
+ dtree->root.dtree = dtree;
+ list_init(&dtree->root.uses);
+ list_init(&dtree->root.used_by);
+
+ if (!(dtree->mem = dm_pool_create("dtree", 1024))) {
+ log_error("dtree pool creation failed");
+ dm_free(dtree);
+ return NULL;
+ }
+
+ if (!(dtree->devs = dm_hash_create(8))) {
+ log_error("dtree hash creation failed");
+ dm_pool_destroy(dtree->mem);
+ dm_free(dtree);
+ return NULL;
+ }
+
+ if (!(dtree->uuids = dm_hash_create(32))) {
+ log_error("dtree uuid hash creation failed");
+ dm_hash_destroy(dtree->devs);
+ dm_pool_destroy(dtree->mem);
+ dm_free(dtree);
+ return NULL;
+ }
+
+ return dtree;
+}
+
+void dm_tree_free(struct dm_tree *dtree)
+{
+ if (!dtree)
+ return;
+
+ dm_hash_destroy(dtree->uuids);
+ dm_hash_destroy(dtree->devs);
+ dm_pool_destroy(dtree->mem);
+ dm_free(dtree);
+}
+
+static int _nodes_are_linked(struct dm_tree_node *parent,
+ struct dm_tree_node *child)
+{
+ struct dm_tree_link *dlink;
+
+ list_iterate_items(dlink, &parent->uses)
+ if (dlink->node == child)
+ return 1;
+
+ return 0;
+}
+
+static int _link(struct list *list, struct dm_tree_node *node)
+{
+ struct dm_tree_link *dlink;
+
+ if (!(dlink = dm_pool_alloc(node->dtree->mem, sizeof(*dlink)))) {
+ log_error("dtree link allocation failed");
+ return 0;
+ }
+
+ dlink->node = node;
+ list_add(list, &dlink->list);
+
+ return 1;
+}
+
+static int _link_nodes(struct dm_tree_node *parent,
+ struct dm_tree_node *child)
+{
+ if (_nodes_are_linked(parent, child))
+ return 1;
+
+ if (!_link(&parent->uses, child))
+ return 0;
+
+ if (!_link(&child->used_by, parent))
+ return 0;
+
+ return 1;
+}
+
+static void _unlink(struct list *list, struct dm_tree_node *node)
+{
+ struct dm_tree_link *dlink;
+
+ list_iterate_items(dlink, list)
+ if (dlink->node == node) {
+ list_del(&dlink->list);
+ break;
+ }
+}
+
+static void _unlink_nodes(struct dm_tree_node *parent,
+ struct dm_tree_node *child)
+{
+ if (!_nodes_are_linked(parent, child))
+ return;
+
+ _unlink(&parent->uses, child);
+ _unlink(&child->used_by, parent);
+}
+
+static int _add_to_toplevel(struct dm_tree_node *node)
+{
+ return _link_nodes(&node->dtree->root, node);
+}
+
+static void _remove_from_toplevel(struct dm_tree_node *node)
+{
+ return _unlink_nodes(&node->dtree->root, node);
+}
+
+static int _add_to_bottomlevel(struct dm_tree_node *node)
+{
+ return _link_nodes(node, &node->dtree->root);
+}
+
+static void _remove_from_bottomlevel(struct dm_tree_node *node)
+{
+ return _unlink_nodes(node, &node->dtree->root);
+}
+
+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 (dm_tree_node_num_children(child, 1))
+ return 1;
+ } else
+ _remove_from_toplevel(child);
+
+ if ((child == &child->dtree->root)) {
+ if (dm_tree_node_num_children(parent, 0))
+ return 1;
+ } else
+ _remove_from_bottomlevel(parent);
+
+ return _link_nodes(parent, child);
+}
+
+static struct dm_tree_node *_create_dm_tree_node(struct dm_tree *dtree,
+ const char *name,
+ const char *uuid,
+ struct dm_info *info,
+ void *context)
+{
+ struct dm_tree_node *node;
+ uint64_t dev;
+
+ if (!(node = dm_pool_zalloc(dtree->mem, sizeof(*node)))) {
+ log_error("_create_dm_tree_node alloc failed");
+ return NULL;
+ }
+
+ node->dtree = dtree;
+
+ node->name = name;
+ node->uuid = uuid;
+ node->info = *info;
+ node->context = context;
+
+ list_init(&node->uses);
+ list_init(&node->used_by);
+ list_init(&node->props.segs);
+
+ dev = MKDEV(info->major, info->minor);
+
+ if (!dm_hash_insert_binary(dtree->devs, (const char *) &dev,
+ sizeof(dev), node)) {
+ log_error("dtree node hash insertion failed");
+ dm_pool_free(dtree->mem, node);
+ return NULL;
+ }
+
+ if (uuid && *uuid &&
+ !dm_hash_insert(dtree->uuids, uuid, node)) {
+ log_error("dtree uuid hash insertion failed");
+ dm_hash_remove_binary(dtree->devs, (const char *) &dev,
+ sizeof(dev));
+ dm_pool_free(dtree->mem, node);
+ return NULL;
+ }
+
+ return node;
+}
+
+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);
+
+ return dm_hash_lookup_binary(dtree->devs, (const char *) &dev,
+ sizeof(dev));
+}
+
+static struct dm_tree_node *_find_dm_tree_node_by_uuid(struct dm_tree *dtree,
+ const char *uuid)
+{
+ /* FIXME Do we need to cope with missing LVM- prefix too? */
+ return dm_hash_lookup(dtree->uuids, uuid);
+}
+
+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;
+
+failed:
+ dm_task_destroy(*dmt);
+ return 0;
+}
+
+static struct dm_tree_node *_add_dev(struct dm_tree *dtree,
+ struct dm_tree_node *parent,
+ uint32_t major, uint32_t minor)
+{
+ 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)))
+ 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->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]))) {
+ 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;
+
+ 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))) {
+ dm_task_destroy(dmt);
+ 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;
+}
+
+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)))
+ 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;
+ } 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;
+
+ if (clear_inactive && !_node_clear_table(dnode))
+ return_NULL;
+
+ dnode->context = context;
+
+ return dnode;
+}
+
+int dm_tree_add_dev(struct dm_tree *dtree, uint32_t major, uint32_t minor)
+{
+ return _add_dev(dtree, &dtree->root, major, minor) ? 1 : 0;
+}
+
+const char *dm_tree_node_get_name(struct dm_tree_node *node)
+{
+ return node->info.exists ? node->name : "";
+}
+
+const char *dm_tree_node_get_uuid(struct dm_tree_node *node)
+{
+ return node->info.exists ? node->uuid : "";
+}
+
+const struct dm_info *dm_tree_node_get_info(struct dm_tree_node *node)
+{
+ return &node->info;
+}
+
+void *dm_tree_node_get_context(struct dm_tree_node *node)
+{
+ return node->context;
+}
+
+int dm_tree_node_num_children(struct dm_tree_node *node, uint32_t inverted)
+{
+ if (inverted) {
+ if (_nodes_are_linked(&node->dtree->root, node))
+ return 0;
+ return list_size(&node->used_by);
+ }
+
+ if (_nodes_are_linked(node, &node->dtree->root))
+ return 0;
+
+ return list_size(&node->uses);
+}
+
+/*
+ * Returns 1 if no prefix supplied
+ */
+static int _uuid_prefix_matches(const char *uuid, const char *uuid_prefix, size_t uuid_prefix_len)
+{
+ if (!uuid_prefix)
+ return 1;
+
+ if (!strncmp(uuid, uuid_prefix, uuid_prefix_len))
+ return 1;
+
+ /* Handle transition: active device uuids might be missing the prefix */
+ if (uuid_prefix_len <= 4)
+ return 0;
+
+ if (!strncmp(uuid, "LVM-", 4))
+ return 0;
+
+ if (strncmp(uuid_prefix, "LVM-", 4))
+ return 0;
+
+ if (!strncmp(uuid, uuid_prefix + 4, uuid_prefix_len - 4))
+ return 1;
+
+ return 0;
+}
+
+/*
+ * Returns 1 if no children.
+ */
+static int _children_suspended(struct dm_tree_node *node,
+ uint32_t inverted,
+ const char *uuid_prefix,
+ size_t uuid_prefix_len)
+{
+ struct list *list;
+ struct dm_tree_link *dlink;
+ const struct dm_info *dinfo;
+ const char *uuid;
+
+ if (inverted) {
+ if (_nodes_are_linked(&node->dtree->root, node))
+ return 1;
+ list = &node->used_by;
+ } else {
+ if (_nodes_are_linked(node, &node->dtree->root))
+ return 1;
+ list = &node->uses;
+ }
+
+ list_iterate_items(dlink, list) {
+ if (!(uuid = dm_tree_node_get_uuid(dlink->node))) {
+ stack;
+ continue;
+ }
+
+ /* Ignore if it doesn't belong to this VG */
+ if (!_uuid_prefix_matches(uuid, uuid_prefix, uuid_prefix_len))
+ continue;
+
+ if (!(dinfo = dm_tree_node_get_info(dlink->node))) {
+ stack; /* FIXME Is this normal? */
+ return 0;
+ }
+
+ if (!dinfo->suspended)
+ return 0;
+ }
+
+ return 1;
+}
+
+/*
+ * Set major and minor to zero for root of tree.
+ */
+struct dm_tree_node *dm_tree_find_node(struct dm_tree *dtree,
+ uint32_t major,
+ uint32_t minor)
+{
+ if (!major && !minor)
+ return &dtree->root;
+
+ return _find_dm_tree_node(dtree, major, minor);
+}
+
+/*
+ * Set uuid to NULL for root of tree.
+ */
+struct dm_tree_node *dm_tree_find_node_by_uuid(struct dm_tree *dtree,
+ const char *uuid)
+{
+ if (!uuid || !*uuid)
+ return &dtree->root;
+
+ return _find_dm_tree_node_by_uuid(dtree, uuid);
+}
+
+/*
+ * First time set *handle to NULL.
+ * Set inverted to invert the tree.
+ */
+struct dm_tree_node *dm_tree_next_child(void **handle,
+ struct dm_tree_node *parent,
+ uint32_t inverted)
+{
+ struct list **dlink = (struct list **) handle;
+ struct list *use_list;
+
+ if (inverted)
+ use_list = &parent->used_by;
+ else
+ use_list = &parent->uses;
+
+ if (!*dlink)
+ *dlink = list_first(use_list);
+ else
+ *dlink = list_next(use_list, *dlink);
+
+ return (*dlink) ? list_item(*dlink, struct dm_tree_link)->node : NULL;
+}
+
+/*
+ * 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_task *dmt;
+ int r;
+
+ if (!(dmt = dm_task_create(DM_DEVICE_INFO))) {
+ log_error("_info_by_dev: dm_task creation failed");
+ return 0;
+ }
+
+ if (!dm_task_set_major(dmt, major) || !dm_task_set_minor(dmt, minor)) {
+ log_error("_info_by_dev: Failed to set device number");
+ dm_task_destroy(dmt);
+ return 0;
+ }
+
+ 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);
+
+ dm_task_destroy(dmt);
+
+ return r;
+}
+
+static int _deactivate_node(const char *name, uint32_t major, uint32_t minor)
+{
+ struct dm_task *dmt;
+ int r;
+
+ log_verbose("Removing %s (%" PRIu32 ":%" PRIu32 ")", name, major, minor);
+
+ if (!(dmt = dm_task_create(DM_DEVICE_REMOVE))) {
+ log_error("Deactivation dm_task creation failed for %s", name);
+ return 0;
+ }
+
+ if (!dm_task_set_major(dmt, major) || !dm_task_set_minor(dmt, minor)) {
+ log_error("Failed to set device number for %s deactivation", name);
+ dm_task_destroy(dmt);
+ return 0;
+ }
+
+ if (!dm_task_no_open_count(dmt))
+ log_error("Failed to disable open_count");
+
+ r = dm_task_run(dmt);
+
+ /* FIXME Until kernel returns actual name so dm-ioctl.c can handle it */
+ rm_dev_node(name);
+
+ /* FIXME Remove node from tree or mark invalid? */
+
+ dm_task_destroy(dmt);
+
+ return r;
+}
+
+static int _rename_node(const char *old_name, const char *new_name, uint32_t major, uint32_t minor)
+{
+ struct dm_task *dmt;
+ int r = 0;
+
+ log_verbose("Renaming %s (%" PRIu32 ":%" PRIu32 ") to %s", old_name, major, minor, new_name);
+
+ if (!(dmt = dm_task_create(DM_DEVICE_RENAME))) {
+ log_error("Rename dm_task creation failed for %s", old_name);
+ return 0;
+ }
+
+ if (!dm_task_set_name(dmt, old_name)) {
+ log_error("Failed to set name for %s rename.", old_name);
+ goto out;
+ }
+
+ if (!dm_task_set_newname(dmt, new_name))
+ goto_out;
+
+ if (!dm_task_no_open_count(dmt))
+ log_error("Failed to disable open_count");
+
+ r = dm_task_run(dmt);
+
+out:
+ dm_task_destroy(dmt);
+
+ return r;
+}
+
+/* FIXME Merge with _suspend_node? */
+static int _resume_node(const char *name, uint32_t major, uint32_t minor,
+ struct dm_info *newinfo)
+{
+ struct dm_task *dmt;
+ int r;
+
+ 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);
+ return 0;
+ }
+
+ if (!dm_task_set_major(dmt, major) || !dm_task_set_minor(dmt, minor)) {
+ log_error("Failed to set device number for %s resumption.", name);
+ dm_task_destroy(dmt);
+ return 0;
+ }
+
+ if (!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, newinfo);
+
+ dm_task_destroy(dmt);
+
+ return r;
+}
+
+static int _suspend_node(const char *name, uint32_t major, uint32_t minor,
+ struct dm_info *newinfo)
+{
+ struct dm_task *dmt;
+ int r;
+
+ log_verbose("Suspending %s (%" PRIu32 ":%" PRIu32 ")", name, major, minor);
+
+ if (!(dmt = dm_task_create(DM_DEVICE_SUSPEND))) {
+ log_error("Suspend dm_task creation failed for %s", name);
+ return 0;
+ }
+
+ if (!dm_task_set_major(dmt, major) || !dm_task_set_minor(dmt, minor)) {
+ log_error("Failed to set device number for %s suspension.", name);
+ dm_task_destroy(dmt);
+ return 0;
+ }
+
+ if (!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, newinfo);
+
+ dm_task_destroy(dmt);
+
+ return r;
+}
+
+int dm_tree_deactivate_children(struct dm_tree_node *dnode,
+ const char *uuid_prefix,
+ size_t uuid_prefix_len)
+{
+ void *handle = NULL;
+ struct dm_tree_node *child = dnode;
+ struct dm_info info;
+ const struct dm_info *dinfo;
+ const char *name;
+ const char *uuid;
+
+ while ((child = dm_tree_next_child(&handle, dnode, 0))) {
+ if (!(dinfo = dm_tree_node_get_info(child))) {
+ stack;
+ continue;
+ }
+
+ if (!(name = dm_tree_node_get_name(child))) {
+ stack;
+ continue;
+ }
+
+ if (!(uuid = dm_tree_node_get_uuid(child))) {
+ stack;
+ continue;
+ }
+
+ /* Ignore if it doesn't belong to this VG */
+ if (!_uuid_prefix_matches(uuid, uuid_prefix, uuid_prefix_len))
+ continue;
+
+ /* Refresh open_count */
+ if (!_info_by_dev(dinfo->major, dinfo->minor, 1, &info) ||
+ !info.exists || info.open_count)
+ continue;
+
+ if (!_deactivate_node(name, info.major, info.minor)) {
+ log_error("Unable to deactivate %s (%" PRIu32
+ ":%" PRIu32 ")", name, info.major,
+ info.minor);
+ continue;
+ }
+
+ if (dm_tree_node_num_children(child, 0))
+ dm_tree_deactivate_children(child, uuid_prefix, uuid_prefix_len);
+ }
+
+ return 1;
+}
+
+int dm_tree_suspend_children(struct dm_tree_node *dnode,
+ const char *uuid_prefix,
+ size_t uuid_prefix_len)
+{
+ void *handle = NULL;
+ struct dm_tree_node *child = dnode;
+ struct dm_info info, newinfo;
+ const struct dm_info *dinfo;
+ const char *name;
+ const char *uuid;
+
+ /* Suspend nodes at this level of the tree */
+ while ((child = dm_tree_next_child(&handle, dnode, 0))) {
+ if (!(dinfo = dm_tree_node_get_info(child))) {
+ stack;
+ continue;
+ }
+
+ if (!(name = dm_tree_node_get_name(child))) {
+ stack;
+ continue;
+ }
+
+ if (!(uuid = dm_tree_node_get_uuid(child))) {
+ stack;
+ continue;
+ }
+
+ /* Ignore if it doesn't belong to this VG */
+ if (!_uuid_prefix_matches(uuid, uuid_prefix, uuid_prefix_len))
+ continue;
+
+ /* Ensure immediate parents are already suspended */
+ if (!_children_suspended(child, 1, uuid_prefix, uuid_prefix_len))
+ continue;
+
+ if (!_info_by_dev(dinfo->major, dinfo->minor, 0, &info) ||
+ !info.exists)
+ continue;
+
+ if (!_suspend_node(name, info.major, info.minor, &newinfo)) {
+ log_error("Unable to suspend %s (%" PRIu32
+ ":%" PRIu32 ")", name, info.major,
+ info.minor);
+ continue;
+ }
+
+ /* Update cached info */
+ child->info = newinfo;
+ }
+
+ /* Then suspend any child nodes */
+ handle = NULL;
+
+ while ((child = dm_tree_next_child(&handle, dnode, 0))) {
+ if (!(uuid = dm_tree_node_get_uuid(child))) {
+ stack;
+ continue;
+ }
+
+ /* Ignore if it doesn't belong to this VG */
+ if (uuid_prefix && strncmp(uuid, uuid_prefix, uuid_prefix_len))
+ continue;
+
+ if (dm_tree_node_num_children(child, 0))
+ dm_tree_suspend_children(child, uuid_prefix, uuid_prefix_len);
+ }
+
+ return 1;
+}
+
+int dm_tree_activate_children(struct dm_tree_node *dnode,
+ const char *uuid_prefix,
+ size_t uuid_prefix_len)
+{
+ void *handle = NULL;
+ struct dm_tree_node *child = dnode;
+ struct dm_info newinfo;
+ const char *name;
+ const char *uuid;
+
+ /* Activate children first */
+ while ((child = dm_tree_next_child(&handle, dnode, 0))) {
+ if (!(uuid = dm_tree_node_get_uuid(child))) {
+ stack;
+ continue;
+ }
+
+ if (!_uuid_prefix_matches(uuid, uuid_prefix, uuid_prefix_len))
+ continue;
+
+ if (dm_tree_node_num_children(child, 0))
+ dm_tree_activate_children(child, uuid_prefix, uuid_prefix_len);
+
+ if (!(name = dm_tree_node_get_name(child))) {
+ stack;
+ continue;
+ }
+
+ /* Rename? */
+ if (child->props.new_name) {
+ if (!_rename_node(name, child->props.new_name, child->info.major, child->info.minor)) {
+ log_error("Failed to rename %s (%" PRIu32
+ ":%" PRIu32 ") to %s", name, child->info.major,
+ child->info.minor, child->props.new_name);
+ return 0;
+ }
+ child->name = child->props.new_name;
+ child->props.new_name = NULL;
+ }
+
+ if (!child->info.inactive_table && !child->info.suspended)
+ continue;
+
+ if (!_resume_node(name, child->info.major, child->info.minor, &newinfo)) {
+ log_error("Unable to resume %s (%" PRIu32
+ ":%" PRIu32 ")", name, child->info.major,
+ child->info.minor);
+ continue;
+ }
+
+ /* Update cached info */
+ child->info = newinfo;
+ }
+
+ handle = NULL;
+
+ return 1;
+}
+
+static int _create_node(struct dm_tree_node *dnode)
+{
+ int r = 0;
+ struct dm_task *dmt;
+
+ log_verbose("Creating %s", dnode->name);
+
+ if (!(dmt = dm_task_create(DM_DEVICE_CREATE))) {
+ log_error("Create dm_task creation failed for %s", dnode->name);
+ return 0;
+ }
+
+ if (!dm_task_set_name(dmt, dnode->name)) {
+ log_error("Failed to set device name for %s", dnode->name);
+ goto out;
+ }
+
+ if (!dm_task_set_uuid(dmt, dnode->uuid)) {
+ log_error("Failed to set uuid for %s", dnode->name);
+ goto out;
+ }
+
+ if (dnode->props.major &&
+ (!dm_task_set_major(dmt, dnode->props.major) ||
+ !dm_task_set_minor(dmt, dnode->props.minor))) {
+ log_error("Failed to set device number for %s creation.", dnode->name);
+ goto out;
+ }
+
+ if (dnode->props.read_only && !dm_task_set_ro(dmt)) {
+ log_error("Failed to set read only flag for %s", dnode->name);
+ goto out;
+ }
+
+ if (!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, &dnode->info);
+
+out:
+ dm_task_destroy(dmt);
+
+ return r;
+}
+
+
+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;
+ }
+
+ return 1;
+}
+
+static int _emit_areas_line(struct dm_task *dmt, struct load_segment *seg, char *params, size_t paramsize, int *pos)
+{
+ struct seg_area *area;
+ char devbuf[10];
+ int tw;
+ const char *prefix = "";
+
+ list_iterate_items(area, &seg->areas) {
+ if (!_build_dev_string(devbuf, sizeof(devbuf), area->dev_node))
+ return_0;
+
+ if ((tw = _dm_snprintf(params + *pos, paramsize - *pos, "%s%s %" PRIu64,
+ prefix, devbuf, area->offset)) < 0) {
+ stack; /* Out of space */
+ return -1;
+ }
+
+ prefix = " ";
+ *pos += tw;
+ }
+
+ return 1;
+}
+
+static int _emit_segment_line(struct dm_task *dmt, struct load_segment *seg, uint64_t *seg_start, char *params, size_t paramsize)
+{
+ int pos = 0;
+ int tw;
+ int r;
+ char originbuf[10], cowbuf[10], logbuf[10];
+
+ switch(seg->type) {
+ case SEG_ERROR:
+ case SEG_ZERO:
+ params[0] = '\0';
+ case SEG_LINEAR:
+ break;
+ case SEG_MIRRORED:
+ if (seg->clustered) {
+ if ((tw = _dm_snprintf(params + pos, paramsize - pos, "clustered ")) < 0) {
+ stack; /* Out of space */
+ return -1;
+ }
+ pos += tw;
+ }
+ if (!seg->log) {
+ if ((tw = _dm_snprintf(params + pos, paramsize - pos, "core 1 ")) < 0) {
+ stack; /* Out of space */
+ return -1;
+ }
+ pos += tw;
+ } else {
+ if (!_build_dev_string(logbuf, sizeof(logbuf), seg->log))
+ return_0;
+ if ((tw = _dm_snprintf(params + pos, paramsize - pos, "disk 2 %s ", logbuf)) < 0) {
+ stack; /* Out of space */
+ return -1;
+ }
+ pos += tw;
+ }
+ if ((tw = _dm_snprintf(params + pos, paramsize - pos, "%u %u ", seg->region_size, seg->mirror_area_count)) < 0) {
+ stack; /* Out of space */
+ return -1;
+ }
+ pos += tw;
+ break;
+ case SEG_SNAPSHOT:
+ if (!_build_dev_string(originbuf, sizeof(originbuf), seg->origin))
+ return_0;
+ if (!_build_dev_string(cowbuf, sizeof(cowbuf), seg->cow))
+ return_0;
+ if ((pos = _dm_snprintf(params, paramsize, "%s %s %c %d",
+ originbuf, cowbuf,
+ seg->persistent ? 'P' : 'N',
+ seg->chunk_size)) < 0) {
+ stack; /* Out of space */
+ return -1;
+ }
+ break;
+ case SEG_SNAPSHOT_ORIGIN:
+ if (!_build_dev_string(originbuf, sizeof(originbuf), seg->origin))
+ return_0;
+ if ((pos = _dm_snprintf(params, paramsize, "%s",
+ originbuf)) < 0) {
+ stack; /* Out of space */
+ return -1;
+ }
+ break;
+ case SEG_STRIPED:
+ if ((pos = _dm_snprintf(params, paramsize, "%u %u ",
+ seg->area_count,
+ seg->stripe_size)) < 0) {
+ stack; /* Out of space */
+ return -1;
+ }
+ break;
+ }
+
+ switch(seg->type) {
+ case SEG_ERROR:
+ case SEG_SNAPSHOT:
+ case SEG_SNAPSHOT_ORIGIN:
+ case SEG_ZERO:
+ break;
+ case SEG_LINEAR:
+ case SEG_MIRRORED:
+ case SEG_STRIPED:
+ if ((r = _emit_areas_line(dmt, seg, params, paramsize, &pos)) <= 0) {
+ stack;
+ return r;
+ }
+ break;
+ }
+
+ log_debug("Adding target: %" PRIu64 " %" PRIu64 " %s %s",
+ *seg_start, seg->size, dm_segtypes[seg->type].target, params);
+
+ if (!dm_task_add_target(dmt, *seg_start, seg->size, dm_segtypes[seg->type].target, params))
+ return_0;
+
+ *seg_start += seg->size;
+
+ return 1;
+}
+
+static int _emit_segment(struct dm_task *dmt, struct load_segment *seg,
+ uint64_t *seg_start)
+{
+ char *params;
+ size_t paramsize = 4096;
+ int ret;
+
+ do {
+ if (!(params = dm_malloc(paramsize))) {
+ log_error("Insufficient space for target parameters.");
+ return 0;
+ }
+
+ ret = _emit_segment_line(dmt, seg, seg_start, params, paramsize);
+ dm_free(params);
+
+ if (!ret)
+ stack;
+
+ if (ret >= 0)
+ return ret;
+
+ log_debug("Insufficient space in params[%" PRIsize_t
+ "] for target parameters.", paramsize);
+
+ paramsize *= 2;
+ } while (paramsize < MAX_TARGET_PARAMSIZE);
+
+ log_error("Target parameter size too big. Aborting.");
+ return 0;
+}
+
+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;
+
+ log_verbose("Loading %s table", dnode->name);
+
+ if (!(dmt = dm_task_create(DM_DEVICE_RELOAD))) {
+ log_error("Reload dm_task creation failed for %s", dnode->name);
+ return 0;
+ }
+
+ if (!dm_task_set_major(dmt, dnode->info.major) ||
+ !dm_task_set_minor(dmt, dnode->info.minor)) {
+ log_error("Failed to set device number for %s reload.", dnode->name);
+ goto out;
+ }
+
+ if (dnode->props.read_only && !dm_task_set_ro(dmt)) {
+ log_error("Failed to set read only flag for %s", dnode->name);
+ goto out;
+ }
+
+ if (!dm_task_no_open_count(dmt))
+ log_error("Failed to disable open_count");
+
+ list_iterate_items(seg, &dnode->props.segs)
+ if (!_emit_segment(dmt, seg, &seg_start))
+ goto_out;
+
+ if ((r = dm_task_run(dmt)))
+ r = dm_task_get_info(dmt, &dnode->info);
+
+ dnode->props.segment_count = 0;
+
+out:
+ dm_task_destroy(dmt);
+
+ return r;
+
+}
+
+int dm_tree_preload_children(struct dm_tree_node *dnode,
+ const char *uuid_prefix,
+ size_t uuid_prefix_len)
+{
+ void *handle = NULL;
+ struct dm_tree_node *child;
+ struct dm_info newinfo;
+ const char *name;
+
+ /* Preload children first */
+ while ((child = dm_tree_next_child(&handle, dnode, 0))) {
+ /* Skip existing non-device-mapper devices */
+ if (!child->info.exists && child->info.major)
+ continue;
+
+ /* Ignore if it doesn't belong to this VG */
+ if (uuid_prefix && child->info.exists &&
+ strncmp(child->uuid, uuid_prefix, uuid_prefix_len))
+ continue;
+
+ if (dm_tree_node_num_children(child, 0))
+ dm_tree_preload_children(child, uuid_prefix, uuid_prefix_len);
+
+ if (!(name = dm_tree_node_get_name(child))) {
+ stack;
+ continue;
+ }
+
+ /* FIXME Cope if name exists with no uuid? */
+ if (!child->info.exists) {
+ if (!_create_node(child)) {
+ stack;
+ return 0;
+ }
+ }
+
+ if (!child->info.inactive_table && child->props.segment_count) {
+ if (!_load_node(child)) {
+ stack;
+ return 0;
+ }
+ }
+
+ /* Resume device immediately if it has parents */
+ if (!dm_tree_node_num_children(child, 1))
+ continue;
+
+ if (!_resume_node(name, child->info.major, child->info.minor, &newinfo)) {
+ log_error("Unable to resume %s (%" PRIu32
+ ":%" PRIu32 ")", name, child->info.major,
+ child->info.minor);
+ continue;
+ }
+
+ /* Update cached info */
+ child->info = newinfo;
+ }
+
+ handle = NULL;
+
+ return 1;
+}
+
+/*
+ * Returns 1 if unsure.
+ */
+int dm_tree_children_use_uuid(struct dm_tree_node *dnode,
+ const char *uuid_prefix,
+ size_t uuid_prefix_len)
+{
+ void *handle = NULL;
+ struct dm_tree_node *child = dnode;
+ const char *uuid;
+
+ while ((child = dm_tree_next_child(&handle, dnode, 0))) {
+ if (!(uuid = dm_tree_node_get_uuid(child))) {
+ log_error("Failed to get uuid for dtree node.");
+ return 1;
+ }
+
+ if (!strncmp(uuid, uuid_prefix, uuid_prefix_len))
+ return 1;
+
+ if (dm_tree_node_num_children(child, 0))
+ dm_tree_children_use_uuid(child, uuid_prefix, uuid_prefix_len);
+ }
+
+ return 0;
+}
+
+/*
+ * Target functions
+ */
+static struct load_segment *_add_segment(struct dm_tree_node *dnode, unsigned type, uint64_t size)
+{
+ struct load_segment *seg;
+
+ if (!(seg = dm_pool_zalloc(dnode->dtree->mem, sizeof(*seg)))) {
+ log_error("dtree node segment allocation failed");
+ return NULL;
+ }
+
+ seg->type = type;
+ seg->size = size;
+ seg->area_count = 0;
+ list_init(&seg->areas);
+ seg->stripe_size = 0;
+ seg->persistent = 0;
+ seg->chunk_size = 0;
+ seg->cow = NULL;
+ seg->origin = NULL;
+
+ list_add(&dnode->props.segs, &seg->list);
+ dnode->props.segment_count++;
+
+ return seg;
+}
+
+int dm_tree_node_add_snapshot_origin_target(struct dm_tree_node *dnode,
+ uint64_t size,
+ const char *origin_uuid)
+{
+ struct load_segment *seg;
+ struct dm_tree_node *origin_node;
+
+ if (!(seg = _add_segment(dnode, SEG_SNAPSHOT_ORIGIN, size)))
+ return_0;
+
+ if (!(origin_node = dm_tree_find_node_by_uuid(dnode->dtree, origin_uuid))) {
+ log_error("Couldn't find snapshot origin uuid %s.", origin_uuid);
+ return 0;
+ }
+
+ seg->origin = origin_node;
+ if (!_link_tree_nodes(dnode, origin_node))
+ return_0;
+
+ return 1;
+}
+
+int dm_tree_node_add_snapshot_target(struct dm_tree_node *node,
+ uint64_t size,
+ const char *origin_uuid,
+ const char *cow_uuid,
+ int persistent,
+ uint32_t chunk_size)
+{
+ struct load_segment *seg;
+ struct dm_tree_node *origin_node, *cow_node;
+
+ if (!(seg = _add_segment(node, SEG_SNAPSHOT, size)))
+ return_0;
+
+ if (!(origin_node = dm_tree_find_node_by_uuid(node->dtree, origin_uuid))) {
+ log_error("Couldn't find snapshot origin uuid %s.", origin_uuid);
+ return 0;
+ }
+
+ seg->origin = origin_node;
+ if (!_link_tree_nodes(node, origin_node))
+ return_0;
+
+ if (!(cow_node = dm_tree_find_node_by_uuid(node->dtree, cow_uuid))) {
+ log_error("Couldn't find snapshot origin uuid %s.", cow_uuid);
+ return 0;
+ }
+
+ seg->cow = cow_node;
+ if (!_link_tree_nodes(node, cow_node))
+ return_0;
+
+ seg->persistent = persistent ? 1 : 0;
+ seg->chunk_size = chunk_size;
+
+ return 1;
+}
+
+int dm_tree_node_add_error_target(struct dm_tree_node *node,
+ uint64_t size)
+{
+ if (!_add_segment(node, SEG_ERROR, size))
+ return_0;
+
+ return 1;
+}
+
+int dm_tree_node_add_zero_target(struct dm_tree_node *node,
+ uint64_t size)
+{
+ if (!_add_segment(node, SEG_ZERO, size))
+ return_0;
+
+ return 1;
+}
+
+int dm_tree_node_add_linear_target(struct dm_tree_node *node,
+ uint64_t size)
+{
+ if (!_add_segment(node, SEG_LINEAR, size))
+ return_0;
+
+ return 1;
+}
+
+int dm_tree_node_add_striped_target(struct dm_tree_node *node,
+ uint64_t size,
+ uint32_t stripe_size)
+{
+ struct load_segment *seg;
+
+ if (!(seg = _add_segment(node, SEG_STRIPED, size)))
+ return_0;
+
+ seg->stripe_size = stripe_size;
+
+ return 1;
+}
+
+int dm_tree_node_add_mirror_target_log(struct dm_tree_node *node,
+ uint32_t region_size,
+ unsigned clustered,
+ const char *log_uuid,
+ unsigned area_count)
+{
+ struct dm_tree_node *log_node = NULL;
+ struct load_segment *seg;
+
+ if (!node->props.segment_count) {
+ log_error("Internal error: Attempt to add target area to missing segment.");
+ return 0;
+ }
+
+ seg = list_item(list_last(&node->props.segs), struct load_segment);
+
+ if (log_uuid) {
+ 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;
+ }
+ if (!_link_tree_nodes(node, log_node))
+ return_0;
+ }
+
+ seg->log = log_node;
+ seg->region_size = region_size;
+ seg->clustered = clustered;
+ seg->mirror_area_count = area_count;
+
+ return 1;
+}
+
+int dm_tree_node_add_mirror_target(struct dm_tree_node *node,
+ uint64_t size)
+{
+ struct load_segment *seg;
+
+ if (!(seg = _add_segment(node, SEG_MIRRORED, size)))
+ return_0;
+
+ 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;
+
+ if (!(area = dm_pool_zalloc(node->dtree->mem, sizeof (*area)))) {
+ log_error("Failed to allocate target segment area.");
+ return 0;
+ }
+
+ area->dev_node = dev_node;
+ area->offset = offset;
+
+ list_add(&seg->areas, &area->list);
+ seg->area_count++;
+
+ return 1;
+}
+
+int dm_tree_node_add_target_area(struct dm_tree_node *node,
+ const char *dev_name,
+ const char *uuid,
+ uint64_t offset)
+{
+ struct load_segment *seg;
+ struct stat info;
+ struct dm_tree_node *dev_node;
+
+ if ((!dev_name || !*dev_name) && (!uuid || !*uuid)) {
+ log_error("dm_tree_node_add_target_area called without device");
+ return 0;
+ }
+
+ if (uuid) {
+ if (!(dev_node = dm_tree_find_node_by_uuid(node->dtree, uuid))) {
+ log_error("Couldn't find area uuid %s.", uuid);
+ return 0;
+ }
+ if (!_link_tree_nodes(node, dev_node))
+ return_0;
+ } else {
+ if (stat(dev_name, &info) < 0) {
+ log_error("Device %s not found.", dev_name);
+ return 0;
+ }
+
+ if (!S_ISBLK(info.st_mode)) {
+ log_error("Device %s is not a block device.", dev_name);
+ return 0;
+ }
+
+ /* FIXME Check correct macro use */
+ if (!(dev_node = _add_dev(node->dtree, node, MAJOR(info.st_rdev), MINOR(info.st_rdev))))
+ return_0;
+ }
+
+ if (!node->props.segment_count) {
+ log_error("Internal error: Attempt to add target area to missing segment.");
+ return 0;
+ }
+
+ seg = list_item(list_last(&node->props.segs), struct load_segment);
+
+ if (!_add_area(node, seg, dev_node, offset))
+ return_0;
+
+ return 1;
+}
Added: devmapper/trunk/lib/mm/dbg_malloc.c
==============================================================================
--- (empty file)
+++ devmapper/trunk/lib/mm/dbg_malloc.c Tue Nov 15 17:44:33 2005
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2005 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 General Public License v.2.
+ *
+ * You should have received a copy of the GNU 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 "lib.h"
+
+#include <assert.h>
+#include <stdarg.h>
+
+char *dm_strdup(const char *str)
+{
+ char *ret = dm_malloc(strlen(str) + 1);
+
+ if (ret)
+ strcpy(ret, str);
+
+ return ret;
+}
+
+#ifdef DEBUG_MEM
+
+struct memblock {
+ struct memblock *prev, *next; /* All allocated blocks are linked */
+ size_t length; /* Size of the requested block */
+ int id; /* Index of the block */
+ const char *file; /* File that allocated */
+ int line; /* Line that allocated */
+ void *magic; /* Address of this block */
+};
+
+static struct {
+ unsigned block_serialno;/* Non-decreasing serialno of block */
+ unsigned blocks_allocated; /* Current number of blocks allocated */
+ unsigned blocks_max; /* Max no of concurrently-allocated blocks */
+ unsigned int bytes, mbytes;
+
+} _mem_stats = {
+0, 0, 0, 0, 0};
+
+static struct memblock *_head = 0;
+static struct memblock *_tail = 0;
+
+void *dm_malloc_aux(size_t s, const char *file, int line)
+{
+ struct memblock *nb;
+ size_t tsize = s + sizeof(*nb) + sizeof(unsigned long);
+
+ if (s > 50000000) {
+ log_error("Huge memory allocation (size %" PRIsize_t
+ ") rejected - metadata corruption?", s);
+ return 0;
+ }
+
+ if (!(nb = malloc(tsize))) {
+ log_error("couldn't allocate any memory, size = %" PRIsize_t,
+ s);
+ return 0;
+ }
+
+ /* set up the file and line info */
+ nb->file = file;
+ nb->line = line;
+
+#ifdef BOUNDS_CHECK
+ dm_bounds_check();
+#endif
+
+ /* setup fields */
+ nb->magic = nb + 1;
+ nb->length = s;
+ nb->id = ++_mem_stats.block_serialno;
+ nb->next = 0;
+ nb->prev = _tail;
+
+ /* link to tail of the list */
+ if (!_head)
+ _head = _tail = nb;
+ else {
+ _tail->next = nb;
+ _tail = nb;
+ }
+
+ /* stomp a pretty pattern across the new memory
+ and fill in the boundary bytes */
+ {
+ char *ptr = (char *) (nb + 1);
+ size_t i;
+ for (i = 0; i < s; i++)
+ *ptr++ = i & 0x1 ? (char) 0xba : (char) 0xbe;
+
+ for (i = 0; i < sizeof(unsigned long); i++)
+ *ptr++ = (char) nb->id;
+ }
+
+ _mem_stats.blocks_allocated++;
+ if (_mem_stats.blocks_allocated > _mem_stats.blocks_max)
+ _mem_stats.blocks_max = _mem_stats.blocks_allocated;
+
+ _mem_stats.bytes += s;
+ if (_mem_stats.bytes > _mem_stats.mbytes)
+ _mem_stats.mbytes = _mem_stats.bytes;
+
+ /* log_debug("Allocated: %u %u %u", nb->id, _mem_stats.blocks_allocated,
+ _mem_stats.bytes); */
+
+ return nb + 1;
+}
+
+void dm_free_aux(void *p)
+{
+ char *ptr;
+ size_t i;
+ struct memblock *mb = ((struct memblock *) p) - 1;
+ if (!p)
+ return;
+
+#ifdef BOUNDS_CHECK
+ dm_bounds_check();
+#endif
+
+ /* sanity check */
+ assert(mb->magic == p);
+
+ /* check data at the far boundary */
+ ptr = ((char *) mb) + sizeof(struct memblock) + mb->length;
+ for (i = 0; i < sizeof(unsigned long); i++)
+ if (*ptr++ != (char) mb->id)
+ assert(!"Damage at far end of block");
+
+ /* have we freed this before ? */
+ assert(mb->id != 0);
+ mb->id = 0;
+
+ /* stomp a different pattern across the memory */
+ ptr = ((char *) mb) + sizeof(struct memblock);
+ for (i = 0; i < mb->length; i++)
+ *ptr++ = i & 1 ? (char) 0xde : (char) 0xad;
+
+ /* unlink */
+ if (mb->prev)
+ mb->prev->next = mb->next;
+ else
+ _head = mb->next;
+
+ if (mb->next)
+ mb->next->prev = mb->prev;
+ else
+ _tail = mb->prev;
+
+ assert(_mem_stats.blocks_allocated);
+ _mem_stats.blocks_allocated--;
+ _mem_stats.bytes -= mb->length;
+
+ /* free the memory */
+ free(mb);
+}
+
+void *dm_realloc_aux(void *p, unsigned int s, const char *file, int line)
+{
+ void *r;
+ struct memblock *mb = ((struct memblock *) p) - 1;
+
+ r = dm_malloc_aux(s, file, line);
+
+ if (p) {
+ memcpy(r, p, mb->length);
+ dm_free_aux(p);
+ }
+
+ return r;
+}
+
+int dm_dump_memory(void)
+{
+ unsigned long tot = 0;
+ struct memblock *mb;
+ char str[32];
+ size_t c;
+
+ if (_head)
+ log_very_verbose("You have a memory leak:");
+
+ for (mb = _head; mb; mb = mb->next) {
+ for (c = 0; c < sizeof(str) - 1; c++) {
+ if (c >= mb->length)
+ str[c] = ' ';
+ else if (*(char *)(mb->magic + c) == '\0')
+ str[c] = '\0';
+ else if (*(char *)(mb->magic + c) < ' ')
+ str[c] = '?';
+ else
+ str[c] = *(char *)(mb->magic + c);
+ }
+ str[sizeof(str) - 1] = '\0';
+
+ _log(_LOG_INFO, mb->file, mb->line,
+ "block %d at %p, size %" PRIsize_t "\t [%s]",
+ mb->id, mb->magic, mb->length, str);
+ tot += mb->length;
+ }
+
+ if (_head)
+ log_very_verbose("%ld bytes leaked in total", tot);
+
+ return 1;
+}
+
+void dm_bounds_check(void)
+{
+ struct memblock *mb = _head;
+ while (mb) {
+ size_t i;
+ char *ptr = ((char *) (mb + 1)) + mb->length;
+ for (i = 0; i < sizeof(unsigned long); i++)
+ if (*ptr++ != (char) mb->id)
+ assert(!"Memory smash");
+
+ mb = mb->next;
+ }
+}
+
+#else
+
+void *dm_malloc_aux(size_t s, const char *file, int line)
+{
+ if (s > 50000000) {
+ log_error("Huge memory allocation (size %" PRIsize_t
+ ") rejected - metadata corruption?", s);
+ return 0;
+ }
+
+ return malloc(s);
+}
+
+#endif
Added: devmapper/trunk/lib/mm/dbg_malloc.h
==============================================================================
--- (empty file)
+++ devmapper/trunk/lib/mm/dbg_malloc.h Tue Nov 15 17:44:33 2005
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2005 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 General Public License v.2.
+ *
+ * You should have received a copy of the GNU 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 _LVM_DBG_MALLOC_H
+#define _LVM_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) free(p)
+# define dbg_realloc(p, s) realloc(p, s)
+# define dump_memory()
+# define bounds_check()
+
+#endif
+
+#endif
Added: devmapper/trunk/lib/mm/pool-debug.c
==============================================================================
--- (empty file)
+++ devmapper/trunk/lib/mm/pool-debug.c Tue Nov 15 17:44:33 2005
@@ -0,0 +1,262 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2005 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 General Public License v.2.
+ *
+ * You should have received a copy of the GNU 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 "lib.h"
+
+struct block {
+ struct block *next;
+ size_t size;
+ void *data;
+};
+
+typedef struct {
+ unsigned block_serialno; /* Non-decreasing serialno of block */
+ unsigned blocks_allocated; /* Current number of blocks allocated */
+ unsigned blocks_max; /* Max no of concurrently-allocated blocks */
+ unsigned int bytes, maxbytes;
+} pool_stats;
+
+struct dm_pool {
+ const char *name;
+
+ int begun;
+ struct block *object;
+
+ struct block *blocks;
+ struct block *tail;
+
+ pool_stats stats;
+};
+
+/* by default things come out aligned for doubles */
+#define DEFAULT_ALIGNMENT __alignof__ (double)
+
+struct pool *dm_pool_create(const char *name, size_t chunk_hint)
+{
+ struct dm_pool *mem = dm_malloc(sizeof(*mem));
+
+ if (!mem) {
+ log_error("Couldn't create memory pool %s (size %"
+ PRIsize_t ")", name, sizeof(*mem));
+ return NULL;
+ }
+
+ 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;
+
+#ifdef DEBUG_POOL
+ log_debug("Created mempool %s", name);
+#endif
+
+ return mem;
+}
+
+static void _free_blocks(struct dm_pool *p, struct block *b)
+{
+ struct block *n;
+
+ while (b) {
+ p->stats.bytes -= b->size;
+ p->stats.blocks_allocated--;
+
+ n = b->next;
+ dm_free(b->data);
+ dm_free(b);
+ b = n;
+ }
+}
+
+static void _pool_stats(struct dm_pool *p, const char *action)
+{
+#ifdef DEBUG_POOL
+ log_debug("%s mempool %s: %u/%u bytes, %u/%u blocks, "
+ "%u allocations)", action, p->name, p->stats.bytes,
+ p->stats.maxbytes, p->stats.blocks_allocated,
+ p->stats.blocks_max, p->stats.block_serialno);
+#else
+ ;
+#endif
+}
+
+void dm_pool_destroy(struct dm_pool *p)
+{
+ _pool_stats(p, "Destroying");
+ _free_blocks(p, p->blocks);
+ dm_free(p);
+}
+
+void *dm_pool_alloc(struct dm_pool *p, size_t s)
+{
+ return dm_pool_alloc_aligned(p, s, DEFAULT_ALIGNMENT);
+}
+
+static void _append_block(struct dm_pool *p, struct block *b)
+{
+ if (p->tail) {
+ p->tail->next = b;
+ p->tail = b;
+ } else
+ p->blocks = p->tail = b;
+
+ p->stats.block_serialno++;
+ p->stats.blocks_allocated++;
+ if (p->stats.blocks_allocated > p->stats.blocks_max)
+ p->stats.blocks_max = p->stats.blocks_allocated;
+
+ p->stats.bytes += b->size;
+ if (p->stats.bytes > p->stats.maxbytes)
+ p->stats.maxbytes = p->stats.bytes;
+}
+
+static struct block *_new_block(size_t s, unsigned alignment)
+{
+ static const char *_oom = "Out of memory";
+
+ /* FIXME: I'm currently ignoring the alignment arg. */
+ size_t len = sizeof(struct block) + s;
+ struct block *b = dm_malloc(len);
+
+ /*
+ * Too lazy to implement alignment for debug version, and
+ * I don't think LVM will use anything but default
+ * align.
+ */
+ assert(alignment == DEFAULT_ALIGNMENT);
+
+ if (!b) {
+ log_err(_oom);
+ return NULL;
+ }
+
+ if (!(b->data = dm_malloc(s))) {
+ log_err(_oom);
+ dm_free(b);
+ return NULL;
+ }
+
+ b->next = NULL;
+ b->size = s;
+
+ return b;
+}
+
+void *dm_pool_alloc_aligned(struct dm_pool *p, size_t s, unsigned alignment)
+{
+ struct block *b = _new_block(s, alignment);
+
+ if (!b)
+ return NULL;
+
+ _append_block(p, b);
+
+ return b->data;
+}
+
+void dm_pool_empty(struct dm_pool *p)
+{
+ _pool_stats(p, "Emptying");
+ _free_blocks(p, p->blocks);
+ p->blocks = p->tail = NULL;
+}
+
+void dm_pool_free(struct dm_pool *p, void *ptr)
+{
+ struct block *b, *prev = NULL;
+
+ _pool_stats(p, "Freeing (before)");
+
+ for (b = p->blocks; b; b = b->next) {
+ if (b->data == ptr)
+ break;
+ prev = b;
+ }
+
+ /*
+ * If this fires then you tried to free a
+ * pointer that either wasn't from this
+ * pool, or isn't the start of a block.
+ */
+ assert(b);
+
+ _free_blocks(p, b);
+
+ if (prev) {
+ p->tail = prev;
+ prev->next = NULL;
+ } else
+ p->blocks = p->tail = NULL;
+
+ _pool_stats(p, "Freeing (after)");
+}
+
+int dm_pool_begin_object(struct dm_pool *p, size_t init_size)
+{
+ assert(!p->begun);
+ p->begun = 1;
+ return 1;
+}
+
+int dm_pool_grow_object(struct dm_pool *p, const void *buffer, size_t delta)
+{
+ struct block *new;
+ size_t size = delta;
+
+ assert(p->begun);
+
+ if (p->object)
+ size += p->object->size;
+
+ if (!(new = _new_block(size, DEFAULT_ALIGNMENT))) {
+ log_err("Couldn't extend object.");
+ return 0;
+ }
+
+ if (p->object) {
+ memcpy(new->data, p->object->data, p->object->size);
+ dm_free(p->object->data);
+ dm_free(p->object);
+ }
+ p->object = new;
+
+ memcpy(new->data + size - delta, buffer, delta);
+
+ return 1;
+}
+
+void *dm_pool_end_object(struct dm_pool *p)
+{
+ assert(p->begun);
+ _append_block(p, p->object);
+
+ p->begun = 0;
+ p->object = NULL;
+ return p->tail->data;
+}
+
+void dm_pool_abandon_object(struct dm_pool *p)
+{
+ assert(p->begun);
+ dm_free(p->object);
+ p->begun = 0;
+ p->object = NULL;
+}
Added: devmapper/trunk/lib/mm/pool-fast.c
==============================================================================
--- (empty file)
+++ devmapper/trunk/lib/mm/pool-fast.c Tue Nov 15 17:44:33 2005
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2005 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 General Public License v.2.
+ *
+ * You should have received a copy of the GNU 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 "lib.h"
+
+struct chunk {
+ char *begin, *end;
+ struct chunk *prev;
+};
+
+struct dm_pool {
+ struct chunk *chunk, *spare_chunk; /* spare_chunk is a one entry free
+ list to stop 'bobbling' */
+ size_t chunk_size;
+ size_t object_len;
+ unsigned object_alignment;
+};
+
+void _align_chunk(struct chunk *c, unsigned alignment);
+struct chunk *_new_chunk(struct dm_pool *p, size_t s);
+
+/* by default things come out aligned for doubles */
+#define DEFAULT_ALIGNMENT __alignof__ (double)
+
+struct dm_pool *dm_pool_create(const char *name, size_t chunk_hint)
+{
+ size_t new_size = 1024;
+ struct dm_pool *p = dm_malloc(sizeof(*p));
+
+ if (!p) {
+ log_error("Couldn't create memory pool %s (size %"
+ PRIsize_t ")", name, sizeof(*p));
+ return 0;
+ }
+ memset(p, 0, sizeof(*p));
+
+ /* round chunk_hint up to the next power of 2 */
+ p->chunk_size = chunk_hint + sizeof(struct chunk);
+ while (new_size < p->chunk_size)
+ new_size <<= 1;
+ p->chunk_size = new_size;
+ return p;
+}
+
+void dm_pool_destroy(struct dm_pool *p)
+{
+ struct chunk *c, *pr;
+ dm_free(p->spare_chunk);
+ c = p->chunk;
+ while (c) {
+ pr = c->prev;
+ dm_free(c);
+ c = pr;
+ }
+
+ dm_free(p);
+}
+
+void *dm_pool_alloc(struct dm_pool *p, size_t s)
+{
+ return dm_pool_alloc_aligned(p, s, DEFAULT_ALIGNMENT);
+}
+
+void *dm_pool_alloc_aligned(struct dm_pool *p, size_t s, unsigned alignment)
+{
+ struct chunk *c = p->chunk;
+ void *r;
+
+ /* realign begin */
+ if (c)
+ _align_chunk(c, alignment);
+
+ /* have we got room ? */
+ if (!c || (c->begin > c->end) || (c->end - c->begin < s)) {
+ /* allocate new chunk */
+ int needed = s + alignment + sizeof(struct chunk);
+ c = _new_chunk(p, (needed > p->chunk_size) ?
+ needed : p->chunk_size);
+
+ if (!c)
+ return NULL;
+
+ _align_chunk(c, alignment);
+ }
+
+ r = c->begin;
+ c->begin += s;
+ return r;
+}
+
+void dm_pool_empty(struct dm_pool *p)
+{
+ struct chunk *c;
+
+ for (c = p->chunk; c && c->prev; c = c->prev)
+ ;
+
+ if (c)
+ dm_pool_free(p, (char *) (c + 1));
+}
+
+void dm_pool_free(struct dm_pool *p, void *ptr)
+{
+ struct chunk *c = p->chunk;
+
+ while (c) {
+ if (((char *) c < (char *) ptr) &&
+ ((char *) c->end > (char *) ptr)) {
+ c->begin = ptr;
+ break;
+ }
+
+ if (p->spare_chunk)
+ dm_free(p->spare_chunk);
+ p->spare_chunk = c;
+ c = c->prev;
+ }
+
+ if (!c)
+ log_error("Internal error: pool_free asked to free pointer "
+ "not in pool");
+ else
+ p->chunk = c;
+}
+
+int dm_pool_begin_object(struct dm_pool *p, size_t hint)
+{
+ struct chunk *c = p->chunk;
+ const size_t align = DEFAULT_ALIGNMENT;
+
+ p->object_len = 0;
+ p->object_alignment = align;
+
+ if (c)
+ _align_chunk(c, align);
+
+ if (!c || (c->begin > c->end) || (c->end - c->begin < hint)) {
+ /* allocate a new chunk */
+ c = _new_chunk(p,
+ hint > (p->chunk_size - sizeof(struct chunk)) ?
+ hint + sizeof(struct chunk) + align :
+ p->chunk_size);
+
+ if (!c)
+ return 0;
+
+ _align_chunk(c, align);
+ }
+
+ return 1;
+}
+
+int dm_pool_grow_object(struct dm_pool *p, const void *extra, size_t n)
+{
+ struct chunk *c = p->chunk, *nc;
+
+ if (c->end - (c->begin + p->object_len) < n) {
+ /* move into a new chunk */
+ if (p->object_len + n > (p->chunk_size / 2))
+ nc = _new_chunk(p, (p->object_len + n) * 2);
+ else
+ nc = _new_chunk(p, p->chunk_size);
+
+ if (!nc)
+ return 0;
+
+ _align_chunk(p->chunk, p->object_alignment);
+ memcpy(p->chunk->begin, c->begin, p->object_len);
+ c = p->chunk;
+ }
+
+ memcpy(c->begin + p->object_len, extra, n);
+ p->object_len += n;
+ return 1;
+}
+
+void *dm_pool_end_object(struct dm_pool *p)
+{
+ struct chunk *c = p->chunk;
+ void *r = c->begin;
+ c->begin += p->object_len;
+ p->object_len = 0u;
+ p->object_alignment = DEFAULT_ALIGNMENT;
+ return r;
+}
+
+void dm_pool_abandon_object(struct dm_pool *p)
+{
+ p->object_len = 0;
+ p->object_alignment = DEFAULT_ALIGNMENT;
+}
+
+void _align_chunk(struct chunk *c, unsigned alignment)
+{
+ c->begin += alignment - ((unsigned long) c->begin & (alignment - 1));
+}
+
+struct chunk *_new_chunk(struct dm_pool *p, size_t s)
+{
+ struct chunk *c;
+
+ if (p->spare_chunk &&
+ ((p->spare_chunk->end - (char *) p->spare_chunk) >= s)) {
+ /* reuse old chunk */
+ c = p->spare_chunk;
+ p->spare_chunk = 0;
+ } else {
+ if (!(c = dm_malloc(s))) {
+ log_error("Out of memory. Requested %" PRIsize_t
+ " bytes.", s);
+ return NULL;
+ }
+
+ c->end = (char *) c + s;
+ }
+
+ c->prev = p->chunk;
+ c->begin = (char *) (c + 1);
+ p->chunk = c;
+
+ return c;
+}
Added: devmapper/trunk/lib/mm/pool.c
==============================================================================
--- (empty file)
+++ devmapper/trunk/lib/mm/pool.c Tue Nov 15 17:44:33 2005
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2005 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 General Public License v.2.
+ *
+ * You should have received a copy of the GNU 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
+ */
+
+#ifdef DEBUG_POOL
+#include "pool-debug.c"
+#else
+#include "pool-fast.c"
+#endif
+
+char *dm_pool_strdup(struct dm_pool *p, const char *str)
+{
+ char *ret = dm_pool_alloc(p, strlen(str) + 1);
+
+ if (ret)
+ strcpy(ret, str);
+
+ return ret;
+}
+
+char *dm_pool_strndup(struct dm_pool *p, const char *str, size_t n)
+{
+ char *ret = dm_pool_alloc(p, n + 1);
+
+ if (ret) {
+ strncpy(ret, str, n);
+ ret[n] = '\0';
+ }
+
+ return ret;
+}
+
+void *dm_pool_zalloc(struct dm_pool *p, size_t s)
+{
+ void *ptr = dm_pool_alloc(p, s);
+
+ if (ptr)
+ memset(ptr, 0, s);
+
+ return ptr;
+}
Added: devmapper/trunk/lib/mm/pool.h
==============================================================================
--- (empty file)
+++ devmapper/trunk/lib/mm/pool.h Tue Nov 15 17:44:33 2005
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2005 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 General Public License v.2.
+ *
+ * You should have received a copy of the GNU 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 _LVM_POOL_H
+#define _LVM_POOL_H
+
+#include <string.h>
+#include <stdlib.h>
+
+/*
+ * The pool allocator is useful when you are going to allocate
+ * lots of memory, use the memory for a bit, and then free the
+ * memory in one go. A surprising amount of code has this usage
+ * profile.
+ *
+ * You should think of the pool as an infinite, contiguous chunk
+ * of memory. The front of this chunk of memory contains
+ * allocated objects, the second half is free. pool_alloc grabs
+ * the next 'size' bytes from the free half, in effect moving it
+ * into the allocated half. This operation is very efficient.
+ *
+ * pool_free frees the allocated object *and* all objects
+ * allocated after it. It is important to note this semantic
+ * difference from malloc/free. This is also extremely
+ * efficient, since a single pool_free can dispose of a large
+ * complex object.
+ *
+ * pool_destroy frees all allocated memory.
+ *
+ * eg, If you are building a binary tree in your program, and
+ * know that you are only ever going to insert into your tree,
+ * and not delete (eg, maintaining a symbol table for a
+ * compiler). You can create yourself a pool, allocate the nodes
+ * from it, and when the tree becomes redundant call pool_destroy
+ * (no nasty iterating through the tree to free nodes).
+ *
+ * eg, On the other hand if you wanted to repeatedly insert and
+ * remove objects into the tree, you would be better off
+ * allocating the nodes from a free list; you cannot free a
+ * single arbitrary node with pool.
+ */
+
+struct pool;
+
+/* constructor and destructor */
+struct pool *pool_create(const char *name, size_t chunk_hint);
+void pool_destroy(struct pool *p);
+
+/* simple allocation/free routines */
+void *pool_alloc(struct pool *p, size_t s);
+void *pool_alloc_aligned(struct pool *p, size_t s, unsigned alignment);
+void pool_empty(struct pool *p);
+void pool_free(struct pool *p, void *ptr);
+
+/*
+ * Object building routines:
+ *
+ * These allow you to 'grow' an object, useful for
+ * building strings, or filling in dynamic
+ * arrays.
+ *
+ * It's probably best explained with an example:
+ *
+ * char *build_string(struct pool *mem)
+ * {
+ * int i;
+ * char buffer[16];
+ *
+ * if (!pool_begin_object(mem, 128))
+ * return NULL;
+ *
+ * for (i = 0; i < 50; i++) {
+ * snprintf(buffer, sizeof(buffer), "%d, ", i);
+ * if (!pool_grow_object(mem, buffer, strlen(buffer)))
+ * goto bad;
+ * }
+ *
+ * // add null
+ * if (!pool_grow_object(mem, "\0", 1))
+ * goto bad;
+ *
+ * return pool_end_object(mem);
+ *
+ * bad:
+ *
+ * pool_abandon_object(mem);
+ * return NULL;
+ *}
+ *
+ * So start an object by calling pool_begin_object
+ * with a guess at the final object size - if in
+ * doubt make the guess too small.
+ *
+ * Then append chunks of data to your object with
+ * pool_grow_object. Finally get your object with
+ * a call to pool_end_object.
+ *
+ */
+int pool_begin_object(struct pool *p, size_t hint);
+int pool_grow_object(struct pool *p, const void *extra, size_t delta);
+void *pool_end_object(struct pool *p);
+void pool_abandon_object(struct pool *p);
+
+/* utilities */
+char *pool_strdup(struct pool *p, const char *str);
+char *pool_strndup(struct pool *p, const char *str, size_t n);
+void *pool_zalloc(struct pool *p, size_t s);
+
+#endif
Modified: devmapper/trunk/make.tmpl.in
==============================================================================
--- devmapper/trunk/make.tmpl.in (original)
+++ devmapper/trunk/make.tmpl.in Tue Nov 15 17:44:33 2005
@@ -24,6 +24,7 @@
MSGFMT = @MSGFMT@
LN_S = @LN_S@
LIBS = @LIBS@
+CFLAGS += @DEFS@
CFLAGS += @CFLAGS@
CLDFLAGS += @CLDFLAGS@
LDDEPS += @LDDEPS@
@@ -64,6 +65,7 @@
ifeq ("@DEBUG@", "yes")
CFLAGS += -g -fno-omit-frame-pointer -DDEBUG
+ CFLAGS += -DDEBUG_MEM
endif
ifeq ("@INTL@", "yes")
@@ -80,6 +82,9 @@
CFLAGS += -DDM_IOCTLS
endif
+#CFLAGS += -DDEBUG_POOL
+#CFLAGS += -DBOUNDS_CHECK
+
#CFLAGS += -pg
#LDFLAGS += -pg
More information about the pkg-lvm-commits
mailing list