diff --git a/src/basic/btrfs-util.c b/src/basic/btrfs-util.c index bc1fb8d29d0..adf9d03eb90 100644 --- a/src/basic/btrfs-util.c +++ b/src/basic/btrfs-util.c @@ -19,7 +19,6 @@ #include "btrfs-util.h" #include "chattr-util.h" #include "copy.h" -#include "device-nodes.h" #include "fd-util.h" #include "fileio.h" #include "fs-util.h" diff --git a/src/basic/device-nodes.h b/src/basic/device-nodes.h deleted file mode 100644 index 9e5c79f67d2..00000000000 --- a/src/basic/device-nodes.h +++ /dev/null @@ -1,16 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -#pragma once - -#include -#include - -#include "macro.h" -#include "stdio-util.h" - -int encode_devnode_name(const char *str, char *str_enc, size_t len); -int allow_listed_char_for_devnode(char c, const char *additional); - -#define DEV_NUM_PATH_MAX \ - (STRLEN("/dev/block/") + DECIMAL_STR_MAX(dev_t) + 1 + DECIMAL_STR_MAX(dev_t)) -#define xsprintf_dev_num_path(buf, type, devno) \ - xsprintf(buf, "/dev/%s/%u:%u", type, major(devno), minor(devno)) diff --git a/src/basic/hexdecoct.c b/src/basic/hexdecoct.c index a5edccad202..da1add7c76a 100644 --- a/src/basic/hexdecoct.c +++ b/src/basic/hexdecoct.c @@ -526,6 +526,16 @@ char base64char(int x) { return table[x & 63]; } +/* This is almost base64char(), but not entirely, as it uses the "url and filename safe" alphabet, + * since we don't want "/" appear in interface names (since interfaces appear in sysfs as filenames). + * See section #5 of RFC 4648. */ +char urlsafe_base64char(int x) { + static const char table[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789-_"; + return table[x & 63]; +} + int unbase64char(char c) { unsigned offset; diff --git a/src/basic/hexdecoct.h b/src/basic/hexdecoct.h index 7e2a6892c0a..4ace5b7a995 100644 --- a/src/basic/hexdecoct.h +++ b/src/basic/hexdecoct.h @@ -27,6 +27,7 @@ char base32hexchar(int x) _const_; int unbase32hexchar(char c) _const_; char base64char(int x) _const_; +char urlsafe_base64char(int x) _const_; int unbase64char(char c) _const_; char *base32hexmem(const void *p, size_t l, bool padding); diff --git a/src/basic/meson.build b/src/basic/meson.build index 18084875bdf..034f30093d7 100644 --- a/src/basic/meson.build +++ b/src/basic/meson.build @@ -38,8 +38,6 @@ basic_sources = files(''' creds-util.c creds-util.h def.h - device-nodes.c - device-nodes.h dirent-util.c dirent-util.h dlfcn-util.c diff --git a/src/nspawn/nspawn-network.c b/src/nspawn/nspawn-network.c index d6b7d8e1d89..95e4b0213b5 100644 --- a/src/nspawn/nspawn-network.c +++ b/src/nspawn/nspawn-network.c @@ -11,6 +11,7 @@ #include "alloc-util.h" #include "ether-addr-util.h" +#include "hexdecoct.h" #include "lockfile-util.h" #include "missing_network.h" #include "netif-naming-scheme.h" @@ -200,16 +201,6 @@ static int add_veth( return 0; } -/* This is almost base64char(), but not entirely, as it uses the "url and filename safe" alphabet, since we - * don't want "/" appear in interface names (since interfaces appear in sysfs as filenames). See section #5 - * of RFC 4648. */ -static char urlsafe_base64char(int x) { - static const char table[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "abcdefghijklmnopqrstuvwxyz" - "0123456789-_"; - return table[x & 63]; -} - static int shorten_ifname(char *ifname) { char new_ifname[IFNAMSIZ]; diff --git a/src/basic/device-nodes.c b/src/shared/device-nodes.c similarity index 100% rename from src/basic/device-nodes.c rename to src/shared/device-nodes.c diff --git a/src/shared/device-nodes.h b/src/shared/device-nodes.h new file mode 100644 index 00000000000..a8b25643149 --- /dev/null +++ b/src/shared/device-nodes.h @@ -0,0 +1,7 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include + +int encode_devnode_name(const char *str, char *str_enc, size_t len); +int allow_listed_char_for_devnode(char c, const char *additional); diff --git a/src/shared/meson.build b/src/shared/meson.build index 6ecdf576ab8..8d9cb323559 100644 --- a/src/shared/meson.build +++ b/src/shared/meson.build @@ -80,6 +80,8 @@ shared_sources = files(''' daemon-util.h dev-setup.c dev-setup.h + device-nodes.c + device-nodes.h devnode-acl.h discover-image.c discover-image.h diff --git a/src/test/test-device-nodes.c b/src/test/test-device-nodes.c index c914d78324f..7ba05b53e97 100644 --- a/src/test/test-device-nodes.c +++ b/src/test/test-device-nodes.c @@ -1,11 +1,11 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ +#include #include #include "alloc-util.h" #include "device-nodes.h" #include "string-util.h" -#include "util.h" /* helpers for test_encode_devnode_name */ static char *do_encode_string(const char *in) { diff --git a/src/udev/meson.build b/src/udev/meson.build index 53787fa1255..4e80f9bfd71 100644 --- a/src/udev/meson.build +++ b/src/udev/meson.build @@ -200,6 +200,12 @@ tests += [ [threads, libacl]], + [['src/udev/test-udev-node.c'], + [libudevd_core, + libshared], + [threads, + libacl]], + [['src/udev/test-udev-builtin.c'], [libudevd_core, libshared], diff --git a/src/udev/test-udev-node.c b/src/udev/test-udev-node.c new file mode 100644 index 00000000000..fe3dd437d5c --- /dev/null +++ b/src/udev/test-udev-node.c @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "tests.h" +#include "udev-node.h" + +static void test_udev_node_escape_path_one(const char *path, const char *expected) { + char buf[NAME_MAX+1]; + size_t r; + + r = udev_node_escape_path(path, buf, sizeof buf); + log_debug("udev_node_escape_path(%s) -> %s (expected: %s)", path, buf, expected); + assert_se(r == strlen(expected)); + assert_se(streq(buf, expected)); +} + +static void test_udev_node_escape_path(void) { + char a[NAME_MAX+1], b[NAME_MAX+1]; + + test_udev_node_escape_path_one("/disk/by-id/nvme-eui.1922908022470001001b448b44ccb9d6", "\\x2fdisk\\x2fby-id\\x2fnvme-eui.1922908022470001001b448b44ccb9d6"); + test_udev_node_escape_path_one("/disk/by-id/nvme-eui.1922908022470001001b448b44ccb9d6-part1", "\\x2fdisk\\x2fby-id\\x2fnvme-eui.1922908022470001001b448b44ccb9d6-part1"); + test_udev_node_escape_path_one("/disk/by-id/nvme-eui.1922908022470001001b448b44ccb9d6-part2", "\\x2fdisk\\x2fby-id\\x2fnvme-eui.1922908022470001001b448b44ccb9d6-part2"); + test_udev_node_escape_path_one("/disk/by-id/nvme-WDC_PC_SN720_SDAQNTW-512G-1001_192290802247", "\\x2fdisk\\x2fby-id\\x2fnvme-WDC_PC_SN720_SDAQNTW-512G-1001_192290802247"); + test_udev_node_escape_path_one("/disk/by-id/nvme-WDC_PC_SN720_SDAQNTW-512G-1001_192290802247-part1", "\\x2fdisk\\x2fby-id\\x2fnvme-WDC_PC_SN720_SDAQNTW-512G-1001_192290802247-part1"); + test_udev_node_escape_path_one("/disk/by-id/nvme-WDC_PC_SN720_SDAQNTW-512G-1001_192290802247-part2", "\\x2fdisk\\x2fby-id\\x2fnvme-WDC_PC_SN720_SDAQNTW-512G-1001_192290802247-part2"); + test_udev_node_escape_path_one("/disk/by-id/usb-Generic-_SD_MMC_20120501030900000-0:0", "\\x2fdisk\\x2fby-id\\x2fusb-Generic-_SD_MMC_20120501030900000-0:0"); + + memset(a, 'a', sizeof(a) - 1); + memcpy(a, "/disk/by-id/", strlen("/disk/by-id/")); + char_array_0(a); + + memset(b, 'a', sizeof(b) - 1); + memcpy(b, "\\x2fdisk\\x2fby-id\\x2f", strlen("\\x2fdisk\\x2fby-id\\x2f")); + strcpy(b + sizeof(b) - 12, "N3YhcCqFeID"); + + test_udev_node_escape_path_one(a, b); +} + +int main(int argc, char *argv[]) { + test_setup_logging(LOG_INFO); + + test_udev_node_escape_path(); + + return 0; +} diff --git a/src/udev/udev-node.c b/src/udev/udev-node.c index a6abaa1a9da..a56084de7eb 100644 --- a/src/udev/udev-node.c +++ b/src/udev/udev-node.c @@ -7,14 +7,16 @@ #include #include +#include "sd-id128.h" + #include "alloc-util.h" -#include "device-nodes.h" #include "device-private.h" #include "device-util.h" #include "dirent-util.h" #include "fd-util.h" #include "format-util.h" #include "fs-util.h" +#include "hexdecoct.h" #include "mkdir.h" #include "path-util.h" #include "selinux-util.h" @@ -26,7 +28,36 @@ #include "udev-node.h" #include "user-util.h" +#define CREATE_LINK_MAX_RETRIES 128 #define LINK_UPDATE_MAX_RETRIES 128 +#define TOUCH_FILE_MAX_RETRIES 128 +#define UDEV_NODE_HASH_KEY SD_ID128_MAKE(b9,6a,f1,ce,40,31,44,1a,9e,19,ec,8b,ae,f3,e3,2f) + +static int create_symlink(const char *target, const char *slink) { + int r; + + assert(target); + assert(slink); + + for (unsigned i = 0; i < CREATE_LINK_MAX_RETRIES; i++) { + r = mkdir_parents_label(slink, 0755); + if (r == -ENOENT) + continue; + if (r < 0) + return r; + + mac_selinux_create_file_prepare(slink, S_IFLNK); + if (symlink(target, slink) < 0) + r = -errno; + else + r = 0; + mac_selinux_create_file_clear(); + if (r != -ENOENT) + return r; + } + + return r; +} static int node_symlink(sd_device *dev, const char *node, const char *slink) { _cleanup_free_ char *slink_dirname = NULL, *target = NULL; @@ -38,87 +69,80 @@ static int node_symlink(sd_device *dev, const char *node, const char *slink) { assert(node); assert(slink); - slink_dirname = dirname_malloc(slink); - if (!slink_dirname) - return log_oom(); + r = path_extract_directory(slink, &slink_dirname); + if (r < 0) + return log_device_debug_errno(dev, r, "Failed to get parent directory of '%s': %m", slink); /* use relative link */ r = path_make_relative(slink_dirname, node, &target); if (r < 0) - return log_device_error_errno(dev, r, "Failed to get relative path from '%s' to '%s': %m", slink, node); + return log_device_debug_errno(dev, r, "Failed to get relative path from '%s' to '%s': %m", slink, node); - /* preserve link with correct target, do not replace node of other device */ - if (lstat(slink, &stats) == 0) { - if (S_ISBLK(stats.st_mode) || S_ISCHR(stats.st_mode)) - return log_device_error_errno(dev, SYNTHETIC_ERRNO(EOPNOTSUPP), - "Conflicting device node '%s' found, link to '%s' will not be created.", slink, node); - else if (S_ISLNK(stats.st_mode)) { - _cleanup_free_ char *buf = NULL; + if (lstat(slink, &stats) >= 0) { + _cleanup_free_ char *buf = NULL; + + if (!S_ISLNK(stats.st_mode)) + return log_device_debug_errno(dev, SYNTHETIC_ERRNO(EEXIST), + "Conflicting inode '%s' found, link to '%s' will not be created.", slink, node); + + if (readlink_malloc(slink, &buf) >= 0 && + path_equal(target, buf)) { + /* preserve link with correct target, do not replace node of other device */ + log_device_debug(dev, "Preserve already existing symlink '%s' to '%s'", slink, target); + + (void) label_fix(slink, LABEL_IGNORE_ENOENT); + (void) utimensat(AT_FDCWD, slink, NULL, AT_SYMLINK_NOFOLLOW); - if (readlink_malloc(slink, &buf) >= 0 && - streq(target, buf)) { - log_device_debug(dev, "Preserve already existing symlink '%s' to '%s'", slink, target); - (void) label_fix(slink, LABEL_IGNORE_ENOENT); - (void) utimensat(AT_FDCWD, slink, NULL, AT_SYMLINK_NOFOLLOW); - return 0; - } - } - } else { - log_device_debug(dev, "Creating symlink '%s' to '%s'", slink, target); - do { - r = mkdir_parents_label(slink, 0755); - if (!IN_SET(r, 0, -ENOENT)) - break; - mac_selinux_create_file_prepare(slink, S_IFLNK); - if (symlink(target, slink) < 0) - r = -errno; - mac_selinux_create_file_clear(); - } while (r == -ENOENT); - if (r == 0) return 0; - if (r < 0) - log_device_debug_errno(dev, r, "Failed to create symlink '%s' to '%s', trying to replace '%s': %m", slink, target, slink); - } + } + } else if (errno == ENOENT) { + log_device_debug(dev, "Creating symlink '%s' to '%s'", slink, target); + + r = create_symlink(target, slink); + if (r >= 0) + return 0; + + log_device_debug_errno(dev, r, "Failed to create symlink '%s' to '%s', trying to replace '%s': %m", slink, target, slink); + } else + return log_device_debug_errno(dev, errno, "Failed to lstat() '%s': %m", slink); log_device_debug(dev, "Atomically replace '%s'", slink); + r = device_get_device_id(dev, &id); if (r < 0) - return log_device_error_errno(dev, r, "Failed to get device id: %m"); + return log_device_debug_errno(dev, r, "Failed to get device id: %m"); slink_tmp = strjoina(slink, ".tmp-", id); + (void) unlink(slink_tmp); - do { - r = mkdir_parents_label(slink_tmp, 0755); - if (!IN_SET(r, 0, -ENOENT)) - break; - mac_selinux_create_file_prepare(slink_tmp, S_IFLNK); - if (symlink(target, slink_tmp) < 0) - r = -errno; - mac_selinux_create_file_clear(); - } while (r == -ENOENT); + + r = create_symlink(target, slink_tmp); if (r < 0) - return log_device_error_errno(dev, r, "Failed to create symlink '%s' to '%s': %m", slink_tmp, target); + return log_device_debug_errno(dev, r, "Failed to create symlink '%s' to '%s': %m", slink_tmp, target); if (rename(slink_tmp, slink) < 0) { - r = log_device_error_errno(dev, errno, "Failed to rename '%s' to '%s': %m", slink_tmp, slink); + r = log_device_debug_errno(dev, errno, "Failed to rename '%s' to '%s': %m", slink_tmp, slink); (void) unlink(slink_tmp); - } else - /* Tell caller that we replaced already existing symlink. */ - r = 1; + return r; + } - return r; + /* Tell caller that we replaced already existing symlink. */ + return 1; } -/* find device node of device with highest priority */ static int link_find_prioritized(sd_device *dev, bool add, const char *stackdir, char **ret) { _cleanup_closedir_ DIR *dir = NULL; _cleanup_free_ char *target = NULL; struct dirent *dent; int r, priority = 0; + const char *id; - assert(!add || dev); + assert(dev); assert(stackdir); assert(ret); + /* Find device node of device with highest priority. This returns 1 if a device found, 0 if no + * device found, or a negative errno. */ + if (add) { const char *devnode; @@ -137,17 +161,21 @@ static int link_find_prioritized(sd_device *dev, bool add, const char *stackdir, dir = opendir(stackdir); if (!dir) { - if (target) { + if (errno == ENOENT) { *ret = TAKE_PTR(target); - return 0; + return !!*ret; } return -errno; } + r = device_get_device_id(dev, &id); + if (r < 0) + return r; + FOREACH_DIRENT_ALL(dent, dir, break) { _cleanup_(sd_device_unrefp) sd_device *dev_db = NULL; - const char *devnode, *id; + const char *devnode; int db_prio = 0; if (dent->d_name[0] == '\0') @@ -157,9 +185,6 @@ static int link_find_prioritized(sd_device *dev, bool add, const char *stackdir, log_device_debug(dev, "Found '%s' claiming '%s'", dent->d_name, stackdir); - if (device_get_device_id(dev, &id) < 0) - continue; - /* did we find ourself? */ if (streq(dent->d_name, id)) continue; @@ -184,59 +209,73 @@ static int link_find_prioritized(sd_device *dev, bool add, const char *stackdir, priority = db_prio; } - if (!target) - return -ENOENT; - *ret = TAKE_PTR(target); - return 0; + return !!*ret; } -static size_t escape_path(const char *src, char *dest, size_t size) { +size_t udev_node_escape_path(const char *src, char *dest, size_t size) { size_t i, j; + uint64_t h; assert(src); assert(dest); for (i = 0, j = 0; src[i] != '\0'; i++) { if (src[i] == '/') { - if (j+4 >= size) { - j = 0; - break; - } + if (j+4 >= size) + goto toolong; memcpy(&dest[j], "\\x2f", 4); j += 4; } else if (src[i] == '\\') { - if (j+4 >= size) { - j = 0; - break; - } + if (j+4 >= size) + goto toolong; memcpy(&dest[j], "\\x5c", 4); j += 4; } else { - if (j+1 >= size) { - j = 0; - break; - } + if (j+1 >= size) + goto toolong; dest[j] = src[i]; j++; } } dest[j] = '\0'; return j; + +toolong: + /* If the input path is too long to encode as a filename, then let's suffix with a string + * generated from the hash of the path. */ + + h = siphash24_string(src, UDEV_NODE_HASH_KEY.bytes); + + assert(size >= 12); + + for (unsigned k = 0; k <= 10; k++) + dest[size - k - 2] = urlsafe_base64char((h >> (k * 6)) & 63); + + dest[size - 1] = '\0'; + return size - 1; } /* manage "stack of names" with possibly specified device priorities */ -static int link_update(sd_device *dev, const char *slink, bool add) { - _cleanup_free_ char *filename = NULL, *dirname = NULL; +static int link_update(sd_device *dev, const char *slink_in, bool add) { + _cleanup_free_ char *slink = NULL, *filename = NULL, *dirname = NULL; const char *slink_name, *id; - char name_enc[PATH_MAX]; + char name_enc[NAME_MAX+1]; int i, r, retries; assert(dev); - assert(slink); + assert(slink_in); + + slink = strdup(slink_in); + if (!slink) + return log_oom_debug(); + + path_simplify(slink); slink_name = path_startswith(slink, "/dev"); - if (!slink_name) + if (!slink_name || + empty_or_root(slink_name) || + !path_is_normalized(slink_name)) return log_device_debug_errno(dev, SYNTHETIC_ERRNO(EINVAL), "Invalid symbolic link of device node: %s", slink); @@ -244,31 +283,31 @@ static int link_update(sd_device *dev, const char *slink, bool add) { if (r < 0) return log_device_debug_errno(dev, r, "Failed to get device id: %m"); - escape_path(slink_name, name_enc, sizeof(name_enc)); + (void) udev_node_escape_path(slink_name, name_enc, sizeof(name_enc)); dirname = path_join("/run/udev/links/", name_enc); if (!dirname) - return log_oom(); + return log_oom_debug(); + filename = path_join(dirname, id); if (!filename) - return log_oom(); + return log_oom_debug(); if (!add) { - if (unlink(filename) == 0) - (void) rmdir(dirname); - } else - for (;;) { - _cleanup_close_ int fd = -1; + if (unlink(filename) < 0 && errno != ENOENT) + log_device_debug_errno(dev, errno, "Failed to remove %s, ignoring: %m", filename); - r = mkdir_parents(filename, 0755); - if (!IN_SET(r, 0, -ENOENT)) - return r; - - fd = open(filename, O_WRONLY|O_CREAT|O_CLOEXEC|O_TRUNC|O_NOFOLLOW, 0444); - if (fd >= 0) + (void) rmdir(dirname); + } else { + for (unsigned j = 0; j < TOUCH_FILE_MAX_RETRIES; j++) { + /* This may fail with -ENOENT when the parent directory is removed during + * creating the file by another udevd worker. */ + r = touch_file(filename, /* parents= */ true, USEC_INFINITY, UID_INVALID, GID_INVALID, 0444); + if (r != -ENOENT) break; - if (errno != ENOENT) - return -errno; } + if (r < 0) + return log_device_debug_errno(dev, r, "Failed to create %s: %m", filename); + } /* If the database entry is not written yet we will just do one iteration and possibly wrong symlink * will be fixed in the second invocation. */ @@ -280,23 +319,25 @@ static int link_update(sd_device *dev, const char *slink, bool add) { r = stat(dirname, &st1); if (r < 0 && errno != ENOENT) - return -errno; + return log_device_debug_errno(dev, errno, "Failed to stat %s: %m", dirname); r = link_find_prioritized(dev, add, dirname, &target); - if (r == -ENOENT) { - log_device_debug(dev, "No reference left, removing '%s'", slink); - if (unlink(slink) == 0) - (void) rmdir_parents(slink, "/"); + if (r < 0) + return log_device_debug_errno(dev, r, "Failed to determine highest priority for symlink '%s': %m", slink); + if (r == 0) { + log_device_debug(dev, "No reference left for '%s', removing", slink); + if (unlink(slink) < 0 && errno != ENOENT) + log_device_debug_errno(dev, errno, "Failed to remove '%s', ignoring: %m", slink); + + (void) rmdir_parents(slink, "/dev"); break; - } else if (r < 0) - return log_device_error_errno(dev, r, "Failed to determine highest priority symlink: %m"); + } r = node_symlink(dev, target, slink); - if (r < 0) { - (void) unlink(filename); - break; - } else if (r == 1) + if (r < 0) + return r; + if (r == 1) /* We have replaced already existing symlink, possibly there is some other device trying * to claim the same symlink. Let's do one more iteration to give us a chance to fix * the error if other device actually claims the symlink with higher priority. */ @@ -306,7 +347,7 @@ static int link_update(sd_device *dev, const char *slink, bool add) { if ((st1.st_mode & S_IFMT) != 0) { r = stat(dirname, &st2); if (r < 0 && errno != ENOENT) - return -errno; + return log_device_debug_errno(dev, errno, "Failed to stat %s: %m", dirname); if (stat_inode_unmodified(&st1, &st2)) break; @@ -317,16 +358,12 @@ static int link_update(sd_device *dev, const char *slink, bool add) { } int udev_node_update_old_links(sd_device *dev, sd_device *dev_old) { - const char *name, *devpath; + const char *name; int r; assert(dev); assert(dev_old); - r = sd_device_get_devpath(dev, &devpath); - if (r < 0) - return log_device_debug_errno(dev, r, "Failed to get devpath: %m"); - /* update possible left-over symlinks */ FOREACH_DEVICE_DEVLINK(dev_old, name) { const char *name_current; @@ -342,8 +379,10 @@ int udev_node_update_old_links(sd_device *dev, sd_device *dev_old) { if (found) continue; - log_device_debug(dev, "Updating old name, '%s' no longer belonging to '%s'", - name, devpath); + log_device_debug(dev, + "Updating old device symlink '%s', which is no longer belonging to this device.", + name); + r = link_update(dev, name, false); if (r < 0) log_device_warning_errno(dev, r, @@ -477,7 +516,6 @@ static int node_permissions_apply(sd_device *dev, bool apply_mac, } static int xsprintf_dev_num_path_from_sd_device(sd_device *dev, char **ret) { - char filename[DEV_NUM_PATH_MAX], *s; const char *subsystem; dev_t devnum; int r; @@ -492,16 +530,7 @@ static int xsprintf_dev_num_path_from_sd_device(sd_device *dev, char **ret) { if (r < 0) return r; - xsprintf_dev_num_path(filename, - streq(subsystem, "block") ? "block" : "char", - devnum); - - s = strdup(filename); - if (!s) - return -ENOMEM; - - *ret = s; - return 0; + return device_path_make_major_minor(streq(subsystem, "block") ? S_IFBLK : S_IFCHR, devnum, ret); } int udev_node_add(sd_device *dev, bool apply, @@ -528,13 +557,6 @@ int udev_node_add(sd_device *dev, bool apply, if (r < 0) return r; - r = xsprintf_dev_num_path_from_sd_device(dev, &filename); - if (r < 0) - return log_device_debug_errno(dev, r, "Failed to get device path: %m"); - - /* always add /dev/{block,char}/$major:$minor */ - (void) node_symlink(dev, devnode, filename); - /* create/update symlinks, add symlinks to name index */ FOREACH_DEVICE_DEVLINK(dev, devlink) { r = link_update(dev, devlink, true); @@ -544,6 +566,15 @@ int udev_node_add(sd_device *dev, bool apply, devlink); } + r = xsprintf_dev_num_path_from_sd_device(dev, &filename); + if (r < 0) + return log_device_debug_errno(dev, r, "Failed to get device path: %m"); + + /* always add /dev/{block,char}/$major:$minor */ + r = node_symlink(dev, devnode, filename); + if (r < 0) + return log_device_warning_errno(dev, r, "Failed to create device symlink '%s': %m", filename); + return 0; } @@ -568,7 +599,8 @@ int udev_node_remove(sd_device *dev) { return log_device_debug_errno(dev, r, "Failed to get device path: %m"); /* remove /dev/{block,char}/$major:$minor */ - (void) unlink(filename); + if (unlink(filename) < 0 && errno != ENOENT) + return log_device_debug_errno(dev, errno, "Failed to remove '%s': %m", filename); return 0; } diff --git a/src/udev/udev-node.h b/src/udev/udev-node.h index 84c7e4567fa..2349f9c471f 100644 --- a/src/udev/udev-node.h +++ b/src/udev/udev-node.h @@ -13,3 +13,5 @@ int udev_node_add(sd_device *dev, bool apply, OrderedHashmap *seclabel_list); int udev_node_remove(sd_device *dev); int udev_node_update_old_links(sd_device *dev, sd_device *dev_old); + +size_t udev_node_escape_path(const char *src, char *dest, size_t size);