From f0d104cdadfb250f980431178648e081a6b23e28 Mon Sep 17 00:00:00 2001
From: Ondrej Kozina <okozina@redhat.com>
Date: Wed, 15 Aug 2012 11:18:34 +0200
Subject: [PATCH] dmsetup tests

---
 tools/dmsetup.c | 226 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 223 insertions(+), 3 deletions(-)

diff --git a/tools/dmsetup.c b/tools/dmsetup.c
index 6934de8..99331b0 100644
--- a/tools/dmsetup.c
+++ b/tools/dmsetup.c
@@ -112,6 +112,9 @@ extern char *optarg;
 
 #define err(msg, x...) fprintf(stderr, msg "\n", ##x)
 
+#define MAX_FDS 32
+#define FD_PREFIX "fd:"
+
 /*
  * We have only very simple switches ATM.
  */
@@ -149,6 +152,7 @@ enum {
 	SORT_ARG,
 	TABLE_ARG,
 	TARGET_ARG,
+	TRANSLATE_TABLE_ARG,
 	TREE_ARG,
 	UID_ARG,
 	UNBUFFERED_ARG,
@@ -191,6 +195,9 @@ static struct dm_report *_report;
 static report_type_t _report_type;
 static dev_name_t _dev_name_type;
 
+static int _file_descriptors[MAX_FDS];
+static int _fds_count = 0;
+
 /*
  * Commands
  */
@@ -208,10 +215,205 @@ struct command {
 	command_fn fn;
 };
 
+static int _open_fd(const char *dev_path)
+{
+	int fd;
+	if (_fds_count >= MAX_FDS) {
+		// error
+		return -2;
+	}
+
+	fd = open(dev_path, O_RDONLY);
+	if (fd >= 0)
+		_file_descriptors[_fds_count++] = fd;
+
+	return fd;
+}
+
+static void _close_fds(void)
+{
+	while (_fds_count > 0)
+		close(_file_descriptors[--_fds_count]);
+}
+
+static void _close_last_fd(void)
+{
+	if (_fds_count)
+		close(_file_descriptors[--_fds_count]);
+}
+
+#ifdef UDEV_SYNC_SUPPORT
+static char *_get_devpath_from_udev(dev_t dev_id)
+{
+	char *ret = NULL;
+
+	struct udev *udev;
+	struct udev_device *udev_dev;
+
+	udev = udev_new();
+	if (!udev)
+		return NULL;
+
+	udev_dev = udev_device_new_from_devnum(udev, 'b', dev_id);
+	if (!udev_dev)
+		goto bad;
+
+	ret = strdup(udev_device_get_devnode(udev_dev));
+
+	udev_device_unref(udev_dev);
+bad:
+	udev_unref(udev);
+
+	return ret;
+}
+#endif
+
+static char *_get_devpath_by_devid(dev_t dev_id)
+{
+	char *ret;
+
+#ifdef UDEV_SYNC_SUPPORT
+	ret = _get_devpath_from_udev(dev_id);
+#else
+	/* TODO: How to get devnode path w/o udev */
+	ret = NULL;
+#endif
+
+	return ret;
+}
+
+static char *_lookup_dev_path(const char *dev_ptr, size_t str_len) {
+	dev_t dev_id;
+	char dummy;
+	char *ret = NULL, *dev_cpy;
+
+	int maj, min;
+
+	/*
+	 * this is safe, there's at least one '\0' char
+	 * after dev_ptr + str_len
+	 */
+	dev_cpy = strndup(dev_ptr, str_len);
+	if (!dev_cpy)
+		return dev_cpy;
+
+	if (sscanf(dev_cpy, "%d:%d%c", &maj, &min, &dummy) == 2) {
+		if (maj < 0 || min < 0)
+			goto out;
+
+		dev_id = makedev(maj, min);
+		if (major(dev_id) != maj || minor(dev_id) != min)
+			goto out;
+
+		ret = _get_devpath_by_devid(dev_id);
+		goto out;
+	}
+	else
+		ret = dev_cpy;
+
+	return ret;
+out:
+	free(dev_cpy);
+	return ret;
+}
+
+static int _translate_string(char **dest, const char *dev_ptr, size_t dev_str_len, size_t *remaining_size)
+{
+	int r;
+	int written;
+
+	char *dev_str;
+
+	dev_str = _lookup_dev_path(dev_ptr, dev_str_len);
+	if (!dev_str)
+		return 1;
+
+	/* "fd:" + at least one char for number after colon */
+	if (*remaining_size < (strlen(FD_PREFIX) + 1))
+		return -ENOMEM;
+
+	r = _open_fd(dev_str);
+	if (r < 0)
+		return r;
+
+	written = snprintf(*dest, *remaining_size, "%s%u", FD_PREFIX, r);
+	if (written >= *remaining_size) {
+		free(dev_str);
+		_close_last_fd();
+		return -ENOMEM;
+	}
+
+	*remaining_size -= written;
+	*dest += written;
+
+	free(dev_str);
+
+	return 0;
+}
+
+/* @orig points to trimed string */
+static int _translate_table_line(char *orig, char **new)
+{
+	char *begin = NULL, *buffer, *copy_from, *copy_to;
+	int r;
+	size_t b_current = 2 * strlen(orig);
+
+	buffer = (char *) malloc((b_current * 2 + 1) * sizeof(char));
+	if (!buffer)
+		return -ENOMEM;
+
+	begin = copy_from = orig;
+	copy_to = buffer;
+
+	while (*orig++) {
+		// first non-white char is begin of substr
+		if (isspace(*orig) || !*orig) {
+			if (begin) {
+
+				if (b_current < (begin - copy_from)) {
+					r = -ENOMEM;
+					goto bad;
+				}
+
+				if (begin - copy_from)
+					memcpy(copy_to, copy_from, begin - copy_from);
+				b_current -= begin - copy_from;
+				copy_to += begin - copy_from;
+
+				r = _translate_string(&copy_to, begin, orig - begin, &b_current);
+
+				if (r) {
+					if (r == -ENOMEM)
+						goto bad;
+
+					copy_from = begin;
+				} else
+					copy_from = orig;
+
+
+				begin = NULL;
+			}
+		}
+		else
+			begin = begin ?: orig;
+	}
+
+	if (copy_from != (orig - 1))
+		memcpy(copy_to, copy_from, (orig - 1) - copy_from);
+
+	*new = buffer;
+
+	return 0;
+
+bad:
+	free(buffer);
+	return r;
+}
+
 static int _parse_line(struct dm_task *dmt, char *buffer, const char *file,
 		       int line)
 {
-	char ttype[LINE_SIZE], *ptr, *comment;
+	char ttype[LINE_SIZE], *ptr, *comment, *translated_line;
 	unsigned long long start, size;
 	int n;
 
@@ -239,9 +441,20 @@ static int _parse_line(struct dm_task *dmt, char *buffer, const char *file,
 	if ((comment = strchr(ptr, (int) '#')))
 		*comment = '\0';
 
+	if (_switches[TRANSLATE_TABLE_ARG]) {
+		log_debug("table translation enabled!\n");
+		if (!_translate_table_line(ptr, &translated_line)) {
+			log_debug("this is translated line: %s\n", translated_line);
+			ptr = translated_line;
+		}
+	}
+
 	if (!dm_task_add_target(dmt, start, size, ttype, ptr))
 		return 0;
 
+	if (_switches[TRANSLATE_TABLE_ARG] && ptr == translated_line)
+		free(translated_line);
+
 	return 1;
 }
 
@@ -2985,7 +3198,7 @@ static struct command _commands[] = {
 	{"create", "<dev_name> [-j|--major <major> -m|--minor <minor>]\n"
 	  "\t                  [-U|--uid <uid>] [-G|--gid <gid>] [-M|--mode <octal_mode>]\n"
 	  "\t                  [-u|uuid <uuid>] [{--addnodeonresume|--addnodeoncreate}]\n"
-	  "\t                  [--notable | --table <table> | <table_file>]",
+	  "\t                  [--notable | --table <table> | <table_file>] [-T|--translate-table]",
 	 1, 2,0,  _create},
 	{"remove", "[-f|--force] <device>", 0, -1, 1, _remove},
 	{"remove_all", "[-f|--force]", 0, 0, 0,  _remove_all},
@@ -3476,6 +3689,7 @@ static int _process_switches(int *argc, char ***argv, const char *dev_dir)
 		{"yes", 0, &ind, YES_ARG},
 		{"addnodeonresume", 0, &ind, ADD_NODE_ON_RESUME_ARG},
 		{"addnodeoncreate", 0, &ind, ADD_NODE_ON_CREATE_ARG},
+		{"translatetable", 0, &ind, TRANSLATE_TABLE_ARG},
 		{0, 0, 0, 0}
 	};
 #else
@@ -3534,7 +3748,7 @@ static int _process_switches(int *argc, char ***argv, const char *dev_dir)
 
 	optarg = 0;
 	optind = OPTIND_INIT;
-	while ((ind = -1, c = GETOPTLONG_FN(*argc, *argv, "cCfG:hj:m:M:no:O:ru:U:vy",
+	while ((ind = -1, c = GETOPTLONG_FN(*argc, *argv, "cCfG:hj:m:M:no:O:ru:TU:vy",
 					    long_options, NULL)) != -1) {
 		if (c == ':' || c == '?')
 			return 0;
@@ -3574,6 +3788,8 @@ static int _process_switches(int *argc, char ***argv, const char *dev_dir)
 			_switches[UUID_ARG]++;
 			_uuid = optarg;
 		}
+		if (c == 'T' || ind == TRANSLATE_TABLE_ARG)
+			_switches[TRANSLATE_TABLE_ARG]++;
 		if (c == 'y' || ind == YES_ARG)
 			_switches[YES_ARG]++;
 		if (ind == ADD_NODE_ON_RESUME_ARG)
@@ -3792,9 +4008,13 @@ int main(int argc, char **argv)
 		}
 	} while (cmd->repeatable_cmd && argc > 1);
 
+	_close_fds();
+
 	r = 0;
 
 out:
+	_close_fds();
+
 	if (_report) {
 		dm_report_output(_report);
 		dm_report_free(_report);
-- 
1.8.0

