You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
472 lines
14 KiB
472 lines
14 KiB
6 months ago
|
From e2eb503bd797908f515b58428b274f1ba6a05349 Mon Sep 17 00:00:00 2001
|
||
|
From: Mariusz Tkaczyk <mariusz.tkaczyk@linux.intel.com>
|
||
|
Date: Thu, 1 Jun 2023 09:27:50 +0200
|
||
|
Subject: [PATCH 157/165] mdadm: Follow POSIX Portable Character Set
|
||
|
|
||
|
When the user creates a device with a name that contains whitespace,
|
||
|
mdadm timeouts and throws an error. This issue is caused by udev, which
|
||
|
truncates /dev/md link until the first whitespace.
|
||
|
|
||
|
This patch introduces prohibition of characters other than A-Za-z0-9.-_
|
||
|
in the device name. Also, it prohibits using leading "-" in device name,
|
||
|
so name won't be confused with cli parameter.
|
||
|
Set of allowed characters is taken from POSIX 3.280 Portable Character
|
||
|
Set. Also, device name length now is limited to NAME_MAX.
|
||
|
|
||
|
In some places, there are other requirements for string length (e.g. size
|
||
|
up to MD_NAME_MAX for device name). This routine is made to follow POSIX
|
||
|
and other, more strict limitations should be checked separately.
|
||
|
We are aware of the risk of regression in exceptional cases (as
|
||
|
escape_devname function is removed) that should be fixed by updating
|
||
|
the array name.
|
||
|
|
||
|
The POSIX validation is added for:
|
||
|
- 'name' parameter in every mode.
|
||
|
- first devlist entry, for Build, Create, Assemble, Manage, Grow.
|
||
|
- config entries, both devname and "name=".
|
||
|
|
||
|
Additionally, some manual cleanups are made.
|
||
|
|
||
|
Signed-off-by: Mariusz Tkaczyk <mariusz.tkaczyk@linux.intel.com>
|
||
|
Signed-off-by: Jes Sorensen <jes@trained-monkey.org>
|
||
|
---
|
||
|
Detail.c | 17 ++++--------
|
||
|
config.c | 13 ++++++---
|
||
|
lib.c | 58 ++++++++++++++++++++++++++++-----------
|
||
|
mdadm.8.in | 70 ++++++++++++++++++++---------------------------
|
||
|
mdadm.conf.5.in | 4 ---
|
||
|
mdadm.h | 2 +-
|
||
|
super-intel.c | 47 ++++++++++++++++---------------
|
||
|
tests/00confnames | 4 +--
|
||
|
8 files changed, 113 insertions(+), 102 deletions(-)
|
||
|
|
||
|
diff --git a/Detail.c b/Detail.c
|
||
|
index 206d88e3..57ac336f 100644
|
||
|
--- a/Detail.c
|
||
|
+++ b/Detail.c
|
||
|
@@ -254,11 +254,9 @@ int Detail(char *dev, struct context *c)
|
||
|
fname_from_uuid(st, info, nbuf, ':');
|
||
|
printf("MD_UUID=%s\n", nbuf + 5);
|
||
|
mp = map_by_uuid(&map, info->uuid);
|
||
|
- if (mp && mp->path && strncmp(mp->path, DEV_MD_DIR, DEV_MD_DIR_LEN) == 0) {
|
||
|
- printf("MD_DEVNAME=");
|
||
|
- print_escape(mp->path + DEV_MD_DIR_LEN);
|
||
|
- putchar('\n');
|
||
|
- }
|
||
|
+
|
||
|
+ if (mp && mp->path && strncmp(mp->path, DEV_MD_DIR, DEV_MD_DIR_LEN) == 0)
|
||
|
+ printf("MD_DEVNAME=%s\n", mp->path + DEV_MD_DIR_LEN);
|
||
|
|
||
|
if (st->ss->export_detail_super)
|
||
|
st->ss->export_detail_super(st);
|
||
|
@@ -271,12 +269,9 @@ int Detail(char *dev, struct context *c)
|
||
|
__fname_from_uuid(mp->uuid, 0, nbuf, ':');
|
||
|
printf("MD_UUID=%s\n", nbuf+5);
|
||
|
}
|
||
|
- if (mp && mp->path &&
|
||
|
- strncmp(mp->path, DEV_MD_DIR, DEV_MD_DIR_LEN) == 0) {
|
||
|
- printf("MD_DEVNAME=");
|
||
|
- print_escape(mp->path + DEV_MD_DIR_LEN);
|
||
|
- putchar('\n');
|
||
|
- }
|
||
|
+ if (mp && mp->path && strncmp(mp->path, DEV_MD_DIR, DEV_MD_DIR_LEN) == 0)
|
||
|
+ printf("MD_DEVNAME=%s\n", mp->path + DEV_MD_DIR_LEN);
|
||
|
+
|
||
|
map_free(map);
|
||
|
}
|
||
|
if (!c->no_devices && sra) {
|
||
|
diff --git a/config.c b/config.c
|
||
|
index a0424845..5f12a1f8 100644
|
||
|
--- a/config.c
|
||
|
+++ b/config.c
|
||
|
@@ -199,9 +199,9 @@ inline void ident_init(struct mddev_ident *ident)
|
||
|
* /dev/md_d{number} (legacy)
|
||
|
* /dev/md_{name}
|
||
|
* /dev/md/{name}
|
||
|
- * {name} - anything that doesn't start from '/' or '<'.
|
||
|
+ * {name}
|
||
|
*
|
||
|
- * {name} must follow name's criteria.
|
||
|
+ * {name} must follow name's criteria and be POSIX compatible.
|
||
|
* If criteria passed, duplicate memory and set devname in @ident.
|
||
|
*
|
||
|
* Return: %MDADM_STATUS_SUCCESS or %MDADM_STATUS_ERROR.
|
||
|
@@ -241,8 +241,8 @@ mdadm_status_t _ident_set_devname(struct mddev_ident *ident, const char *devname
|
||
|
else
|
||
|
name = devname;
|
||
|
|
||
|
- if (*name == '/' || *name == '<') {
|
||
|
- ident_log(prop_name, devname, "Cannot be started from \'/\' or \'<\'", cmdline);
|
||
|
+ if (is_name_posix_compatible(name) == false) {
|
||
|
+ ident_log(prop_name, name, "Not POSIX compatible", cmdline);
|
||
|
return MDADM_STATUS_ERROR;
|
||
|
}
|
||
|
|
||
|
@@ -283,6 +283,11 @@ static mdadm_status_t _ident_set_name(struct mddev_ident *ident, const char *nam
|
||
|
return MDADM_STATUS_ERROR;
|
||
|
}
|
||
|
|
||
|
+ if (is_name_posix_compatible(name) == false) {
|
||
|
+ ident_log(prop_name, name, "Not POSIX compatible", cmdline);
|
||
|
+ return MDADM_STATUS_ERROR;
|
||
|
+ }
|
||
|
+
|
||
|
snprintf(ident->name, MD_NAME_MAX + 1, "%s", name);
|
||
|
return MDADM_STATUS_SUCCESS;
|
||
|
}
|
||
|
diff --git a/lib.c b/lib.c
|
||
|
index 03198e2d..7ab59988 100644
|
||
|
--- a/lib.c
|
||
|
+++ b/lib.c
|
||
|
@@ -483,24 +483,50 @@ void print_quoted(char *str)
|
||
|
putchar(q);
|
||
|
}
|
||
|
|
||
|
-void print_escape(char *str)
|
||
|
+/**
|
||
|
+ * is_alphanum() - Check if sign is letter or digit.
|
||
|
+ * @c: char to analyze.
|
||
|
+ *
|
||
|
+ * Similar to isalnum() but additional locales are excluded.
|
||
|
+ *
|
||
|
+ * Return: %true on success, %false otherwise.
|
||
|
+ */
|
||
|
+bool is_alphanum(const char c)
|
||
|
{
|
||
|
- /* print str, but change space and tab to '_'
|
||
|
- * as is suitable for device names
|
||
|
- */
|
||
|
- for (; *str; str++) {
|
||
|
- switch (*str) {
|
||
|
- case ' ':
|
||
|
- case '\t':
|
||
|
- putchar('_');
|
||
|
- break;
|
||
|
- case '/':
|
||
|
- putchar('-');
|
||
|
- break;
|
||
|
- default:
|
||
|
- putchar(*str);
|
||
|
- }
|
||
|
+ if (isupper(c) || islower(c) || isdigit(c) != 0)
|
||
|
+ return true;
|
||
|
+ return false;
|
||
|
+}
|
||
|
+
|
||
|
+/**
|
||
|
+ * is_name_posix_compatible() - Check if name is POSIX compatible.
|
||
|
+ * @name: name to check.
|
||
|
+ *
|
||
|
+ * POSIX portable file name character set contains ASCII letters,
|
||
|
+ * digits, '_', '.', and '-'. Also forbid leading '-'.
|
||
|
+ * The length of the name cannot exceed NAME_MAX - 1 (ensure NULL ending).
|
||
|
+ *
|
||
|
+ * Return: %true on success, %false otherwise.
|
||
|
+ */
|
||
|
+bool is_name_posix_compatible(const char * const name)
|
||
|
+{
|
||
|
+ assert(name);
|
||
|
+
|
||
|
+ char allowed_symbols[] = "-_.";
|
||
|
+ const char *n = name;
|
||
|
+
|
||
|
+ if (!is_string_lq(name, NAME_MAX))
|
||
|
+ return false;
|
||
|
+
|
||
|
+ if (*n == '-')
|
||
|
+ return false;
|
||
|
+
|
||
|
+ while (*n != '\0') {
|
||
|
+ if (!is_alphanum(*n) && !strchr(allowed_symbols, *n))
|
||
|
+ return false;
|
||
|
+ n++;
|
||
|
}
|
||
|
+ return true;
|
||
|
}
|
||
|
|
||
|
int check_env(char *name)
|
||
|
diff --git a/mdadm.8.in b/mdadm.8.in
|
||
|
index b7159509..3142436f 100644
|
||
|
--- a/mdadm.8.in
|
||
|
+++ b/mdadm.8.in
|
||
|
@@ -364,7 +364,7 @@ Use the Intel(R) Matrix Storage Manager metadata format. This creates a
|
||
|
which is managed in a similar manner to DDF, and is supported by an
|
||
|
option-rom on some platforms:
|
||
|
.IP
|
||
|
-.B https://www.intel.com/content/www/us/en/support/products/122484/memory-and-storage/ssd-software/intel-virtual-raid-on-cpu-intel-vroc.html
|
||
|
+.B https://www.intel.com/content/www/us/en/support/products/122484
|
||
|
.PP
|
||
|
.RE
|
||
|
|
||
|
@@ -932,17 +932,14 @@ option will be ignored.
|
||
|
.BR \-N ", " \-\-name=
|
||
|
Set a
|
||
|
.B name
|
||
|
-for the array. This is currently only effective when creating an
|
||
|
-array with a version-1 superblock, or an array in a DDF container.
|
||
|
-The name is a simple textual string that can be used to identify array
|
||
|
-components when assembling. If name is needed but not specified, it
|
||
|
-is taken from the basename of the device that is being created.
|
||
|
-e.g. when creating
|
||
|
-.I /dev/md/home
|
||
|
-the
|
||
|
-.B name
|
||
|
-will default to
|
||
|
-.IR home .
|
||
|
+for the array. It must be
|
||
|
+.BR "POSIX PORTABLE NAME"
|
||
|
+compatible and cannot be longer than 32 chars. This is effective when creating an array
|
||
|
+with a v1 metadata, or an external array.
|
||
|
+
|
||
|
+If name is needed but not specified, it is taken from the basename of the device
|
||
|
+that is being created. See
|
||
|
+.BR "DEVICE NAMES"
|
||
|
|
||
|
.TP
|
||
|
.BR \-R ", " \-\-run
|
||
|
@@ -1132,8 +1129,10 @@ is much safer.
|
||
|
|
||
|
.TP
|
||
|
.BR \-N ", " \-\-name=
|
||
|
-Specify the name of the array to assemble. This must be the name
|
||
|
-that was specified when creating the array. It must either match
|
||
|
+Specify the name of the array to assemble. It must be
|
||
|
+.BR "POSIX PORTABLE NAME"
|
||
|
+compatible and cannot be longer than 32 chars. This must be the name
|
||
|
+that was specified when creating the array. It must either match
|
||
|
the name stored in the superblock exactly, or it must match
|
||
|
with the current
|
||
|
.I homehost
|
||
|
@@ -2179,14 +2178,17 @@ Usage:
|
||
|
.I md-device
|
||
|
.BI \-\-chunk= X
|
||
|
.BI \-\-level= Y
|
||
|
-.br
|
||
|
.BI \-\-raid\-devices= Z
|
||
|
.I devices
|
||
|
|
||
|
.PP
|
||
|
-This usage will initialise a new md array, associate some devices with
|
||
|
+This usage will initialize a new md array, associate some devices with
|
||
|
it, and activate the array.
|
||
|
|
||
|
+.I md-device
|
||
|
+is a new device. This could be standard name or chosen name. For details see:
|
||
|
+.BR "DEVICE NAMES"
|
||
|
+
|
||
|
The named device will normally not exist when
|
||
|
.I "mdadm \-\-create"
|
||
|
is run, but will be created by
|
||
|
@@ -2227,24 +2229,6 @@ array. This feature can be overridden with the
|
||
|
.B \-\-force
|
||
|
option.
|
||
|
|
||
|
-When creating an array with version-1 metadata a name for the array is
|
||
|
-required.
|
||
|
-If this is not given with the
|
||
|
-.B \-\-name
|
||
|
-option,
|
||
|
-.I mdadm
|
||
|
-will choose a name based on the last component of the name of the
|
||
|
-device being created. So if
|
||
|
-.B /dev/md3
|
||
|
-is being created, then the name
|
||
|
-.B 3
|
||
|
-will be chosen.
|
||
|
-If
|
||
|
-.B /dev/md/home
|
||
|
-is being created, then the name
|
||
|
-.B home
|
||
|
-will be used.
|
||
|
-
|
||
|
When creating a partition based array, using
|
||
|
.I mdadm
|
||
|
with version-1.x metadata, the partition type should be set to
|
||
|
@@ -2429,12 +2413,10 @@ and
|
||
|
|
||
|
The
|
||
|
.B name
|
||
|
-option updates the subarray name in the metadata, it may not affect the
|
||
|
-device node name or the device node symlink until the subarray is
|
||
|
-re\-assembled. If updating
|
||
|
-.B name
|
||
|
-would change the UUID of an active subarray this operation is blocked,
|
||
|
-and the command will end in an error.
|
||
|
+option updates the subarray name in the metadata. It must be
|
||
|
+.BR "POSIX PORTABLE NAME"
|
||
|
+compatible and cannot be longer than 32 chars. If successes, new value will be respected after
|
||
|
+next assembly.
|
||
|
|
||
|
The
|
||
|
.B ppl
|
||
|
@@ -3395,6 +3377,10 @@ When
|
||
|
.B \-\-incremental
|
||
|
mode is used, this file gets a list of arrays currently being created.
|
||
|
|
||
|
+.SH POSIX PORTABLE NAME
|
||
|
+A valid name can only consist of characters "A-Za-z0-9.-_".
|
||
|
+The name cannot start with a leading "-" and cannot exceed 255 chars.
|
||
|
+
|
||
|
.SH DEVICE NAMES
|
||
|
|
||
|
.I mdadm
|
||
|
@@ -3416,6 +3402,10 @@ can be given, or just the suffix of the second sort of name, such as
|
||
|
.I home
|
||
|
can be given.
|
||
|
|
||
|
+In every style, raw name must be compatible with
|
||
|
+.BR "POSIX PORTABLE NAME"
|
||
|
+and has to be no longer than 32 chars.
|
||
|
+
|
||
|
When
|
||
|
.I mdadm
|
||
|
chooses device names during auto-assembly or incremental assembly, it
|
||
|
diff --git a/mdadm.conf.5.in b/mdadm.conf.5.in
|
||
|
index bc2295c2..94e23dd0 100644
|
||
|
--- a/mdadm.conf.5.in
|
||
|
+++ b/mdadm.conf.5.in
|
||
|
@@ -717,10 +717,6 @@ ARRAY /dev/md/home UUID=9187a482:5dde19d9:eea3cc4a:d646ab8b
|
||
|
.br
|
||
|
auto=part
|
||
|
.br
|
||
|
-# The name of this array contains a space.
|
||
|
-.br
|
||
|
-ARRAY /dev/md9 name='Data Storage'
|
||
|
-.sp
|
||
|
POLICY domain=domain1 metadata=imsm path=pci-0000:00:1f.2-scsi-*
|
||
|
.br
|
||
|
action=spare
|
||
|
diff --git a/mdadm.h b/mdadm.h
|
||
|
index 49422e24..9effb941 100644
|
||
|
--- a/mdadm.h
|
||
|
+++ b/mdadm.h
|
||
|
@@ -1617,6 +1617,7 @@ extern int check_raid(int fd, char *name);
|
||
|
extern int check_partitions(int fd, char *dname,
|
||
|
unsigned long long freesize,
|
||
|
unsigned long long size);
|
||
|
+extern bool is_name_posix_compatible(const char *path);
|
||
|
extern int fstat_is_blkdev(int fd, char *devname, dev_t *rdev);
|
||
|
extern int stat_is_blkdev(char *devname, dev_t *rdev);
|
||
|
|
||
|
@@ -1657,7 +1658,6 @@ extern int conf_get_monitor_delay(void);
|
||
|
extern char *conf_line(FILE *file);
|
||
|
extern char *conf_word(FILE *file, int allow_key);
|
||
|
extern void print_quoted(char *str);
|
||
|
-extern void print_escape(char *str);
|
||
|
extern int use_udev(void);
|
||
|
extern unsigned long GCD(unsigned long a, unsigned long b);
|
||
|
extern int conf_name_is_free(char *name);
|
||
|
diff --git a/super-intel.c b/super-intel.c
|
||
|
index 77b0066f..05d3b056 100644
|
||
|
--- a/super-intel.c
|
||
|
+++ b/super-intel.c
|
||
|
@@ -5561,40 +5561,37 @@ static void imsm_update_version_info(struct intel_super *super)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
-static int check_name(struct intel_super *super, char *name, int quiet)
|
||
|
+/**
|
||
|
+ * imsm_check_name() - check imsm naming criteria.
|
||
|
+ * @super: &intel_super pointer, not NULL.
|
||
|
+ * @name: name to check.
|
||
|
+ * @verbose: verbose level.
|
||
|
+ *
|
||
|
+ * Name must be no longer than &MAX_RAID_SERIAL_LEN and must be unique across volumes.
|
||
|
+ *
|
||
|
+ * Returns: &true if @name matches, &false otherwise.
|
||
|
+ */
|
||
|
+static bool imsm_is_name_allowed(struct intel_super *super, const char * const name,
|
||
|
+ const int verbose)
|
||
|
{
|
||
|
struct imsm_super *mpb = super->anchor;
|
||
|
- char *reason = NULL;
|
||
|
- char *start = name;
|
||
|
- size_t len = strlen(name);
|
||
|
int i;
|
||
|
|
||
|
- if (len > 0) {
|
||
|
- while (isspace(start[len - 1]))
|
||
|
- start[--len] = 0;
|
||
|
- while (*start && isspace(*start))
|
||
|
- ++start, --len;
|
||
|
- memmove(name, start, len + 1);
|
||
|
+ if (is_string_lq(name, MAX_RAID_SERIAL_LEN + 1) == false) {
|
||
|
+ pr_vrb("imsm: Name \"%s\" is too long\n", name);
|
||
|
+ return false;
|
||
|
}
|
||
|
|
||
|
- if (len > MAX_RAID_SERIAL_LEN)
|
||
|
- reason = "must be 16 characters or less";
|
||
|
- else if (len == 0)
|
||
|
- reason = "must be a non-empty string";
|
||
|
-
|
||
|
for (i = 0; i < mpb->num_raid_devs; i++) {
|
||
|
struct imsm_dev *dev = get_imsm_dev(super, i);
|
||
|
|
||
|
if (strncmp((char *) dev->volume, name, MAX_RAID_SERIAL_LEN) == 0) {
|
||
|
- reason = "already exists";
|
||
|
- break;
|
||
|
+ pr_vrb("imsm: Name \"%s\" already exists\n", name);
|
||
|
+ return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
- if (reason && !quiet)
|
||
|
- pr_err("imsm volume name %s\n", reason);
|
||
|
-
|
||
|
- return !reason;
|
||
|
+ return true;
|
||
|
}
|
||
|
|
||
|
static int init_super_imsm_volume(struct supertype *st, mdu_array_info_t *info,
|
||
|
@@ -5689,8 +5686,9 @@ static int init_super_imsm_volume(struct supertype *st, mdu_array_info_t *info,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
- if (!check_name(super, name, 0))
|
||
|
+ if (imsm_is_name_allowed(super, name, 1) == false)
|
||
|
return 0;
|
||
|
+
|
||
|
dv = xmalloc(sizeof(*dv));
|
||
|
dev = xcalloc(1, sizeof(*dev) + sizeof(__u32) * (info->raid_disks - 1));
|
||
|
/*
|
||
|
@@ -8018,7 +8016,7 @@ static int update_subarray_imsm(struct supertype *st, char *subarray,
|
||
|
char *ep;
|
||
|
int vol;
|
||
|
|
||
|
- if (!check_name(super, name, 0))
|
||
|
+ if (imsm_is_name_allowed(super, name, 1) == false)
|
||
|
return 2;
|
||
|
|
||
|
vol = strtoul(subarray, &ep, 10);
|
||
|
@@ -10345,7 +10343,8 @@ static void imsm_process_update(struct supertype *st,
|
||
|
if (a->info.container_member == target)
|
||
|
break;
|
||
|
dev = get_imsm_dev(super, u->dev_idx);
|
||
|
- if (a || !check_name(super, name, 1)) {
|
||
|
+
|
||
|
+ if (a || !dev || imsm_is_name_allowed(super, name, 0) == false) {
|
||
|
dprintf("failed to rename subarray-%d\n", target);
|
||
|
break;
|
||
|
}
|
||
|
diff --git a/tests/00confnames b/tests/00confnames
|
||
|
index 25a7127b..10823f01 100644
|
||
|
--- a/tests/00confnames
|
||
|
+++ b/tests/00confnames
|
||
|
@@ -79,10 +79,10 @@ names_verify "/dev/md127" "name" "name"
|
||
|
mdadm -S "/dev/md127"
|
||
|
|
||
|
# 11. <devname> with some special symbols and locales, no <name>.
|
||
|
-# It needs to wait a while for timeout because udev cannot create a link - known issue.
|
||
|
+# <devname> should be ignored.
|
||
|
names_make_conf $_UUID "tźż-\.,<>st+-" "empty" $config
|
||
|
mdadm -I $dev0 --config=$config
|
||
|
-names_verify "/dev/md127" "tźż-\.,<>st+-" "name"
|
||
|
+names_verify "/dev/md127" "name" "name"
|
||
|
mdadm -S "/dev/md127"
|
||
|
|
||
|
# 12. No <devname> and <name> set.
|
||
|
--
|
||
|
2.40.1
|
||
|
|