On Fri, Sep 25, 2026 at 11:40:07PM +0200, [email protected] wrote:
> From: Martin Wilck <[email protected]>
> diff --git a/libmultipath/discovery.c b/libmultipath/discovery.c
> index 344da19..66af333 100644
> --- a/libmultipath/discovery.c
> +++ b/libmultipath/discovery.c
> @@ -37,6 +37,7 @@
> #include "print.h"
> #include "strbuf.h"
> #include "pgpolicies.h"
> +#include "wwids.h"
>
> #define VPD_BUFLEN 4096
>
> @@ -45,6 +46,97 @@ struct vpd_vendor_page vpd_vendor_pages[VPD_VP_ARRAY_SIZE]
> = {
> [VPD_VP_HP3PAR] = { 0xc0, "hp3par" },
> };
>
> +/* from libdm/libdm-common.c; license: LGPL-2.1-only */
> +static bool _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 true;
> +
> + return false;
> +}
> +
> +size_t maybe_unmangle_wwid(char new_wwid[WWID_SIZE], const char *wwid)
> +{
> + char buf[WWID_SIZE], *dst;
> + const char *src;
> + bool mangled = false, invalid = false;
> + size_t rc, len;
> +
> + if (!wwid) {
> + *new_wwid = '\0';
> + return 0;
> + }
> +
> + /* this might be > WWID_SIZE - 1 */
> + len = strlen(wwid);
> + /*
> + * A correctly mangled string doesn't contain invalid characters.
> + * In particular, if the original string contained `\xXY`, it will
> + * itself be mangled to '\x5cxXY`.
> + */
> + /* clang-format off */
> + for (src = wwid, dst = buf;
> + dst - buf < WWID_SIZE - 1 && *src;
> + src++, dst++) {
> + /*clang-format on */
> + if (_is_whitelisted_char(*src)) {
> + *dst = *src;
> + continue;
> + }
> + if (*src == '\\') {
> + char hex[3], c;
> +
> + /*
> + * udev mangling always outputs 2-digit hex numbers
> + * 1-digit would be unprintable, anyway
> + */
> + if (src + 4 > wwid + len || *(src + 1) != 'x' ||
> + sscanf(src + 2, "%2[A-Fa-f0-9]", hex) == 0 ||
> + strlen(hex) != 2) {
> + condlog(3, "%s: wwid \"%s\" contains invalid
> mangle sequence \"%s\"",
> + __func__, wwid, src);
> + invalid = true;
> + break;
> + }
> + mangled = true;
> + src += 1 + strlen(hex);
> + c = strtol(hex, NULL, 16);
> + /* Allow non-ascii unicode, but avoid non-printable
> characters */
> + *dst = (c & 0x80) || isprint(c) ? c : '_';
> + } else {
> + condlog(3, "%s: wwid \"%s\" contains illegal character
> '%02x'",
> + __func__, wwid, (unsigned char)*src);
> + invalid = true;
> + break;
> + }
> + }
> +
> + /*
> + * 1. If we found an invalid character, it can't be mangled by udev,
> + * use original WWID.
> + * 2. If we didn't found a `\xHH`, it isn't mangled, use original.
> + * 3. If the unmangled WWID is known, use it.
> + * 4. If the original WWID is known, use it.
> + * 5. If in doubt, use the unmangled WWID.
> + */
> + if (!invalid && mangled &&
This check should use "buf" not "new_wwid".
> + (!check_wwids_file(new_wwid, 0) || check_wwids_file(wwid, 0))) {
> + *dst = '\0';
> + rc = strlcpy(new_wwid, buf, WWID_SIZE);
> + condlog(3, "%s: unmangled WWID \"%s\" -> \"%s\"", __func__,
> + wwid, new_wwid);
> + } else {
> + rc = strlcpy(new_wwid, wwid, WWID_SIZE);
> + condlog(4, "%s: kept WWID \"%s\"", __func__, new_wwid);
> + }
Nitpick: I think these conditionals would be easier to check against
rules as:
if (invalid || !mangled || check_wwids_file(wwid, 0) == 0) {
/* Keep original wwid */
} else {
/* Use unmangled wwid */
}
> + return rc;
> +}
> +
> int
> alloc_path_with_pathinfo (struct config *conf, struct udev_device *udevice,
> const char *wwid, int flag, struct path **pp_ptr)
> @@ -65,8 +157,7 @@ alloc_path_with_pathinfo (struct config *conf, struct
> udev_device *udevice,
> if (!pp)
> return PATHINFO_FAILED;
>
> - if (wwid)
> - strlcpy(pp->wwid, wwid, sizeof(pp->wwid));
> + maybe_unmangle_wwid(pp->wwid, wwid);
>
> if (safe_sprintf(pp->dev, "%s", devname)) {
> condlog(0, "pp->dev too small");
> @@ -2163,8 +2254,8 @@ get_udev_uid(struct path * pp, const char
> *uid_attribute, struct udev_device *ud
> value = udev_device_get_property_value(udev, uid_attribute);
> if ((!value || strlen(value) == 0) && pp->can_use_env_uid)
> value = getenv(uid_attribute);
> - if (value && strlen(value)) {
> - len = strlcpy(pp->wwid, value, WWID_SIZE);
> + if (value && *value) {
> + len = maybe_unmangle_wwid(pp->wwid, value);
> if (len >= WWID_SIZE) {
> len = fix_broken_nvme_wwid(pp, value, WWID_SIZE);
> if (len > 0)
> @@ -2224,7 +2315,7 @@ static ssize_t dasd_get_uid(struct path *pp)
> if (p)
> *p = '\0';
>
> - return strlcpy(pp->wwid, value, WWID_SIZE);
> + return maybe_unmangle_wwid(pp->wwid, value);
We're reading this from sysfs, so I don't think this one needs to be
unmangled.
> }
>
> static ssize_t uid_fallback(struct path *pp, int path_state,
> @@ -2254,6 +2345,8 @@ static ssize_t uid_fallback(struct path *pp, int
> path_state,
> sizeof(value));
> if (!sysfs_attr_value_ok(len, sizeof(value)))
> return -1;
> +
> + /* WWID read from sysfs, unmangling is not necessary */
I'm pretty sure that unmangling is unnecessary in all of the methods in
uid_fallback, so this comment is confusing.
-Ben