[lvm-devel] [PATCH] Add support for escaping device-mapper names (to be in sync with udev whitelist)
Peter Rajnoha
prajnoha at redhat.com
Fri Oct 7 13:43:27 UTC 2011
This patch restrict device names used in device-mapper to comply
with udev whitelist. If not whitelisted, we use an escape mechanism
that udev accepts - that is \xNN where NN is a hex value of the
character.
(Related rhbz #736486, #740575)
Peter
---
libdm/ioctl/libdm-iface.c | 7 ++++
libdm/ioctl/libdm-targets.h | 1 +
libdm/libdevmapper.h | 1 +
libdm/libdm-common.c | 67 +++++++++++++++++++++++++++++++++++++++++--
tools/dmsetup.c | 11 ++++++-
5 files changed, 82 insertions(+), 5 deletions(-)
diff --git a/libdm/ioctl/libdm-iface.c b/libdm/ioctl/libdm-iface.c
index aab6305..4565266 100644
--- a/libdm/ioctl/libdm-iface.c
+++ b/libdm/ioctl/libdm-iface.c
@@ -835,6 +835,13 @@ int dm_task_retry_remove(struct dm_task *dmt)
return 1;
}
+int dm_task_name_escape(struct dm_task *dmt)
+{
+ dmt->name_escape = 1;
+
+ return 1;
+}
+
int dm_task_query_inactive_table(struct dm_task *dmt)
{
dmt->query_inactive_table = 1;
diff --git a/libdm/ioctl/libdm-targets.h b/libdm/ioctl/libdm-targets.h
index ca08fe8..46c5e88 100644
--- a/libdm/ioctl/libdm-targets.h
+++ b/libdm/ioctl/libdm-targets.h
@@ -64,6 +64,7 @@ struct dm_task {
int new_uuid;
int secure_data;
int retry_remove;
+ int name_escape;
int enable_checks;
char *uuid;
diff --git a/libdm/libdevmapper.h b/libdm/libdevmapper.h
index fdf8943..ae2803e 100644
--- a/libdm/libdevmapper.h
+++ b/libdm/libdevmapper.h
@@ -192,6 +192,7 @@ int dm_task_query_inactive_table(struct dm_task *dmt);
int dm_task_suppress_identical_reload(struct dm_task *dmt);
int dm_task_secure_data(struct dm_task *dmt);
int dm_task_retry_remove(struct dm_task *dmt);
+int dm_task_name_escape(struct dm_task *dmt);
/*
* Enable checks for common mistakes such as issuing ioctls in an unsafe order.
diff --git a/libdm/libdm-common.c b/libdm/libdm-common.c
index 23539d7..22a3d32 100644
--- a/libdm/libdm-common.c
+++ b/libdm/libdm-common.c
@@ -277,11 +277,62 @@ static char *_find_dm_name_of_device(dev_t st_rdev)
return new_name;
}
+static int _is_whitelisted_char(char c)
+{
+ /*
+ * Actually, DM supports any character in a device name.
+ * This whitelist is just for proper integration with udev.
+ */
+ if ((c >= '0' && c <= '9') ||
+ (c >= 'A' && c <= 'Z') ||
+ (c >= 'a' && c <= 'z') ||
+ strchr("#+-.:=@_", c) != NULL)
+ return 1;
+
+ return 0;
+}
+
+/*
+ * Encode all characters in the input string which are not on a whitelist
+ * with '\xNN' format where NN is the hex value of the character.
+ */
+static int _escape_dev_name(const char *str, char *str_enc, size_t len)
+{
+ size_t i, j;
+
+ if (str == NULL || str_enc == NULL)
+ return 0;
+
+ for (i = 0, j = 0; str[i] != '\0'; i++) {
+ if (str[i] == '\\' || !_is_whitelisted_char(str[i])) {
+ if (len - j < 4)
+ goto error;
+ sprintf(&str_enc[j], "\\x%02x", (unsigned char) str[i]);
+ j+=4;
+ } else {
+ if (len - j < 1)
+ goto error;
+ str_enc[j] = str[i];
+ j++;
+ }
+ }
+
+ if (len - j < 1)
+ goto error;
+ str_enc[j] = '\0';
+
+ return 1;
+error:
+ log_error("Escaped name too long for device name %s", str);
+ return 0;
+}
+
int dm_task_set_name(struct dm_task *dmt, const char *name)
{
char *pos;
char *new_name = NULL;
char path[PATH_MAX];
+ char esc_name[DM_NAME_LEN];
struct stat st1, st2;
dm_free(dmt->dev_name);
@@ -330,9 +381,19 @@ int dm_task_set_name(struct dm_task *dmt, const char *name)
if (new_name)
dmt->dev_name = new_name;
- else if (!(dmt->dev_name = dm_strdup(name))) {
- log_error("dm_task_set_name: strdup(%s) failed", name);
- return 0;
+ else {
+ if (dmt->name_escape) {
+ if (!_escape_dev_name(name, esc_name, sizeof(esc_name))) {
+ log_error("Failed to encode device name %s", name);
+ return 0;
+ }
+ name = esc_name;
+ }
+
+ if (!(dmt->dev_name = dm_strdup(name))) {
+ log_error("dm_task_set_name: strdup(%s) failed", name);
+ return 0;
+ }
}
return 1;
diff --git a/tools/dmsetup.c b/tools/dmsetup.c
index f35c8a5..41637f5 100644
--- a/tools/dmsetup.c
+++ b/tools/dmsetup.c
@@ -130,6 +130,7 @@ enum {
MINOR_ARG,
MODE_ARG,
NAMEPREFIXES_ARG,
+ NONAMEESCAPE_ARG,
NOFLUSH_ARG,
NOHEADINGS_ARG,
NOLOCKFS_ARG,
@@ -608,6 +609,9 @@ static int _create(CMD_ARGS)
if (!(dmt = dm_task_create(DM_DEVICE_CREATE)))
return 0;
+ if (!_switches[NONAMEESCAPE_ARG])
+ dm_task_name_escape(dmt);
+
if (!dm_task_set_name(dmt, argv[1]))
goto out;
@@ -2806,8 +2810,8 @@ static void _usage(FILE *out)
fprintf(out, "Usage:\n\n");
fprintf(out, "dmsetup [--version] [-h|--help [-c|-C|--columns]]\n"
- " [--checks] [-v|--verbose [-v|--verbose ...]]\n"
- " [-r|--readonly] [--noopencount] [--nolockfs] [--inactive]\n"
+ " [--checks] [-v|--verbose [-v|--verbose ...]] [-r|--readonly]\n"
+ " [--nonameescape] [--noopencount] [--nolockfs] [--inactive]\n"
" [--udevcookie [cookie]] [--noudevrules] [--noudevsync] [--verifyudev]\n"
" [-y|--yes] [--readahead [+]<sectors>|auto|none] [--retry]\n"
" [-c|-C|--columns] [-o <fields>] [-O|--sort <sort_fields>]\n"
@@ -3172,6 +3176,7 @@ static int _process_switches(int *argc, char ***argv, const char *dev_dir)
{"minor", 1, &ind, MINOR_ARG},
{"mode", 1, &ind, MODE_ARG},
{"nameprefixes", 0, &ind, NAMEPREFIXES_ARG},
+ {"nonameescape", 0, &ind, NONAMEESCAPE_ARG},
{"noflush", 0, &ind, NOFLUSH_ARG},
{"noheadings", 0, &ind, NOHEADINGS_ARG},
{"nolockfs", 0, &ind, NOLOCKFS_ARG},
@@ -3339,6 +3344,8 @@ static int _process_switches(int *argc, char ***argv, const char *dev_dir)
_switches[INACTIVE_ARG]++;
if ((ind == NAMEPREFIXES_ARG))
_switches[NAMEPREFIXES_ARG]++;
+ if ((ind == NONAMEESCAPE_ARG))
+ _switches[NONAMEESCAPE_ARG]++;
if ((ind == NOFLUSH_ARG))
_switches[NOFLUSH_ARG]++;
if ((ind == NOHEADINGS_ARG))
More information about the lvm-devel
mailing list