diff --git a/src/libsystemd/sd-device/sd-device.c b/src/libsystemd/sd-device/sd-device.c index 1050234afe..8574337bda 100644 --- a/src/libsystemd/sd-device/sd-device.c +++ b/src/libsystemd/sd-device/sd-device.c @@ -145,15 +145,12 @@ int device_set_syspath(sd_device *device, const char *_syspath, bool verify) { assert(device); assert(_syspath); - /* must be a subdirectory of /sys */ - if (!path_startswith(_syspath, "/sys/")) - return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), - "sd-device: Syspath '%s' is not a subdirectory of /sys", - _syspath); - if (verify) { _cleanup_close_ int fd = -1; + /* The input path maybe a symlink located outside of /sys. Let's try to chase the symlink at first. + * The primary usecase is that e.g. /proc/device-tree is a symlink to /sys/firmware/devicetree/base. + * By chasing symlinks in the path at first, we can call sd_device_new_from_path() with such path. */ r = chase_symlinks(_syspath, NULL, 0, &syspath, &fd); if (r == -ENOENT) /* the device does not exist (any more?) */ @@ -230,6 +227,12 @@ int device_set_syspath(sd_device *device, const char *_syspath, bool verify) { "sd-device: the syspath \"%s\" is outside of sysfs, refusing.", syspath); } } else { + /* must be a subdirectory of /sys */ + if (!path_startswith(_syspath, "/sys/")) + return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), + "sd-device: Syspath '%s' is not a subdirectory of /sys", + _syspath); + syspath = strdup(_syspath); if (!syspath) return log_oom_debug(); @@ -250,13 +253,16 @@ int device_set_syspath(sd_device *device, const char *_syspath, bool verify) { return 0; } -_public_ int sd_device_new_from_syspath(sd_device **ret, const char *syspath) { +static int device_new_from_syspath(sd_device **ret, const char *syspath, bool strict) { _cleanup_(sd_device_unrefp) sd_device *device = NULL; int r; assert_return(ret, -EINVAL); assert_return(syspath, -EINVAL); + if (strict && !path_startswith(syspath, "/sys/")) + return -EINVAL; + r = device_new_aux(&device); if (r < 0) return r; @@ -269,6 +275,10 @@ _public_ int sd_device_new_from_syspath(sd_device **ret, const char *syspath) { return 0; } +_public_ int sd_device_new_from_syspath(sd_device **ret, const char *syspath) { + return device_new_from_syspath(ret, syspath, /* strict = */ true); +} + static int device_new_from_mode_and_devnum(sd_device **ret, mode_t mode, dev_t devnum) { _cleanup_(sd_device_unrefp) sd_device *dev = NULL; _cleanup_free_ char *syspath = NULL; @@ -516,7 +526,7 @@ _public_ int sd_device_new_from_path(sd_device **ret, const char *path) { if (path_startswith(path, "/dev")) return sd_device_new_from_devname(ret, path); - return sd_device_new_from_syspath(ret, path); + return device_new_from_syspath(ret, path, /* strict = */ false); } int device_set_devtype(sd_device *device, const char *devtype) { diff --git a/src/libsystemd/sd-device/test-sd-device.c b/src/libsystemd/sd-device/test-sd-device.c index d625dde85d..8172c64e45 100644 --- a/src/libsystemd/sd-device/test-sd-device.c +++ b/src/libsystemd/sd-device/test-sd-device.c @@ -2,6 +2,7 @@ #include #include +#include #include "device-enumerator-private.h" #include "device-internal.h" @@ -12,9 +13,11 @@ #include "hashmap.h" #include "nulstr-util.h" #include "path-util.h" +#include "rm-rf.h" #include "string-util.h" #include "tests.h" #include "time-util.h" +#include "tmpfile-util.h" static void test_sd_device_one(sd_device *d) { _cleanup_(sd_device_unrefp) sd_device *dev = NULL; @@ -406,4 +409,50 @@ TEST(sd_device_new_from_nulstr) { } } +TEST(sd_device_new_from_path) { + _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL; + _cleanup_(rm_rf_physical_and_freep) char *tmpdir = NULL; + sd_device *dev; + int r; + + assert_se(mkdtemp_malloc("/tmp/test-sd-device.XXXXXXX", &tmpdir) >= 0); + + assert_se(sd_device_enumerator_new(&e) >= 0); + assert_se(sd_device_enumerator_allow_uninitialized(e) >= 0); + assert_se(sd_device_enumerator_add_match_subsystem(e, "block", true) >= 0); + assert_se(sd_device_enumerator_add_nomatch_sysname(e, "loop*") >= 0); + assert_se(sd_device_enumerator_add_match_property(e, "DEVNAME", "*") >= 0); + + FOREACH_DEVICE(e, dev) { + _cleanup_(sd_device_unrefp) sd_device *d = NULL; + const char *syspath, *devpath, *sysname, *s; + _cleanup_free_ char *path = NULL; + + assert_se(sd_device_get_sysname(dev, &sysname) >= 0); + + log_debug("%s(%s)", __func__, sysname); + + assert_se(sd_device_get_syspath(dev, &syspath) >= 0); + assert_se(sd_device_new_from_path(&d, syspath) >= 0); + assert_se(sd_device_get_syspath(d, &s) >= 0); + assert_se(streq(s, syspath)); + d = sd_device_unref(d); + + assert_se(sd_device_get_devname(dev, &devpath) >= 0); + r = sd_device_new_from_path(&d, devpath); + if (r >= 0) { + assert_se(sd_device_get_syspath(d, &s) >= 0); + assert_se(streq(s, syspath)); + d = sd_device_unref(d); + } else + assert_se(r == -ENODEV || ERRNO_IS_PRIVILEGE(r)); + + assert_se(path = path_join(tmpdir, sysname)); + assert_se(symlink(syspath, path) >= 0); + assert_se(sd_device_new_from_path(&d, path) >= 0); + assert_se(sd_device_get_syspath(d, &s) >= 0); + assert_se(streq(s, syspath)); + } +} + DEFINE_TEST_MAIN(LOG_INFO); diff --git a/src/udev/udev-builtin-net_id.c b/src/udev/udev-builtin-net_id.c index ef1f398927..33fce6e140 100644 --- a/src/udev/udev-builtin-net_id.c +++ b/src/udev/udev-builtin-net_id.c @@ -604,9 +604,8 @@ static int names_platform(sd_device *dev, NetNames *names, bool test) { } static int dev_devicetree_onboard(sd_device *dev, NetNames *names) { - const char *alias, *ofnode_path, *ofnode_syspath; - _cleanup_free_ char *devicetree_syspath = NULL; - _cleanup_(sd_device_unrefp) sd_device *aliases_dev = NULL, *ofnode_dev = NULL; + _cleanup_(sd_device_unrefp) sd_device *aliases_dev = NULL, *ofnode_dev = NULL, *devicetree_dev = NULL; + const char *alias, *ofnode_path, *ofnode_syspath, *devicetree_syspath; sd_device *parent; int r; @@ -626,7 +625,12 @@ static int dev_devicetree_onboard(sd_device *dev, NetNames *names) { if (r < 0) return r; - r = chase_symlinks("/proc/device-tree", NULL, 0, &devicetree_syspath, NULL); + /* /proc/device-tree should be a symlink to /sys/firmware/devicetree/base. */ + r = sd_device_new_from_path(&devicetree_dev, "/proc/device-tree"); + if (r < 0) + return r; + + r = sd_device_get_syspath(devicetree_dev, &devicetree_syspath); if (r < 0) return r; @@ -644,7 +648,7 @@ static int dev_devicetree_onboard(sd_device *dev, NetNames *names) { ofnode_path--; assert(path_is_absolute(ofnode_path)); - r = sd_device_new_from_syspath(&aliases_dev, strjoina(devicetree_syspath, "/aliases")); + r = sd_device_new_child(&aliases_dev, devicetree_dev, "aliases"); if (r < 0) return r;