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