From 3585160e2c7f6e57b76badabb8ba7c7207235c54 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 8 Feb 2018 18:31:15 +0100 Subject: [PATCH 01/25] hash-func: add generic hash_ops implementation for hashing paths This is similar to string_hash_ops but operates one file system paths specifically. It will ensure that "/foo//bar" and "///foo/bar" are considered to be the same path for hashmap purposes. This makes use of the existing path_compare() API, and adds a matching hashing function for it. Note that relative and absolute paths will hash to different values, however whether the path is suffixed with a slash or not is not detected. This matches the existing path_compare() behaviour, and follows the logic that on Linux there can't be two different objects at path /foo/bar and /foo/bar/ either. --- src/basic/hash-funcs.c | 50 +++++++++++++++++++++++++++++++++++++++++ src/basic/hash-funcs.h | 9 +++++--- src/test/test-hashmap.c | 31 +++++++++++++++++++++++++ 3 files changed, 87 insertions(+), 3 deletions(-) diff --git a/src/basic/hash-funcs.c b/src/basic/hash-funcs.c index c3a4a011b5b..7ffb00c7696 100644 --- a/src/basic/hash-funcs.c +++ b/src/basic/hash-funcs.c @@ -19,6 +19,7 @@ ***/ #include "hash-funcs.h" +#include "path-util.h" void string_hash_func(const void *p, struct siphash *state) { siphash24_compress(p, strlen(p) + 1, state); @@ -33,6 +34,55 @@ const struct hash_ops string_hash_ops = { .compare = string_compare_func }; + +void path_hash_func(const void *p, struct siphash *state) { + const char *q = p; + size_t n; + + assert(q); + assert(state); + + /* Calculates a hash for a path in a way this duplicate inner slashes don't make a differences, and also + * whether there's a trailing slash or not. This fits well with the semantics of path_compare(), which does + * similar checks and also doesn't care for trailing slashes. Note that relative and absolute paths (i.e. those + * which begin in a slash or not) will hash differently though. */ + + n = strspn(q, "/"); + if (n > 0) { /* Eat up initial slashes, and add one "/" to the hash for all of them */ + siphash24_compress(q, 1, state); + q += n; + } + + for (;;) { + /* Determine length of next component */ + n = strcspn(q, "/"); + if (n == 0) /* Reached the end? */ + break; + + /* Add this component to the hash and skip over it */ + siphash24_compress(q, n, state); + q += n; + + /* How many slashes follow this component? */ + n = strspn(q, "/"); + if (q[n] == 0) /* Is this a trailing slash? If so, we are at the end, and don't care about the slashes anymore */ + break; + + /* We are not add the end yet. Hash exactly one slash for all of the ones we just encountered. */ + siphash24_compress(q, 1, state); + q += n; + } +} + +int path_compare_func(const void *a, const void *b) { + return path_compare(a, b); +} + +const struct hash_ops path_hash_ops = { + .hash = path_hash_func, + .compare = path_compare_func +}; + void trivial_hash_func(const void *p, struct siphash *state) { siphash24_compress(&p, sizeof(p), state); } diff --git a/src/basic/hash-funcs.h b/src/basic/hash-funcs.h index 299189d143d..f3315afe584 100644 --- a/src/basic/hash-funcs.h +++ b/src/basic/hash-funcs.h @@ -35,9 +35,12 @@ void string_hash_func(const void *p, struct siphash *state); int string_compare_func(const void *a, const void *b) _pure_; extern const struct hash_ops string_hash_ops; -/* This will compare the passed pointers directly, and will not - * dereference them. This is hence not useful for strings or - * suchlike. */ +void path_hash_func(const void *p, struct siphash *state); +int path_compare_func(const void *a, const void *b) _pure_; +extern const struct hash_ops path_hash_ops; + +/* This will compare the passed pointers directly, and will not dereference them. This is hence not useful for strings + * or suchlike. */ void trivial_hash_func(const void *p, struct siphash *state); int trivial_compare_func(const void *a, const void *b) _const_; extern const struct hash_ops trivial_hash_ops; diff --git a/src/test/test-hashmap.c b/src/test/test-hashmap.c index 83cea360e69..ef61aa285cf 100644 --- a/src/test/test-hashmap.c +++ b/src/test/test-hashmap.c @@ -56,6 +56,34 @@ static void test_string_compare_func(void) { assert_se(string_compare_func("fred", "fred") == 0); } +static void test_path_hashmap(void) { + _cleanup_(hashmap_freep) Hashmap *h = NULL; + + assert_se(h = hashmap_new(&path_hash_ops)); + + assert_se(hashmap_put(h, "foo", INT_TO_PTR(1)) >= 0); + assert_se(hashmap_put(h, "/foo", INT_TO_PTR(2)) >= 0); + assert_se(hashmap_put(h, "//foo", INT_TO_PTR(3)) == -EEXIST); + assert_se(hashmap_put(h, "//foox/", INT_TO_PTR(4)) >= 0); + assert_se(hashmap_put(h, "/foox////", INT_TO_PTR(5)) == -EEXIST); + assert_se(hashmap_put(h, "foo//////bar/quux//", INT_TO_PTR(6)) >= 0); + assert_se(hashmap_put(h, "foo/bar//quux/", INT_TO_PTR(8)) == -EEXIST); + + assert_se(hashmap_get(h, "foo") == INT_TO_PTR(1)); + assert_se(hashmap_get(h, "foo/") == INT_TO_PTR(1)); + assert_se(hashmap_get(h, "foo////") == INT_TO_PTR(1)); + assert_se(hashmap_get(h, "/foo") == INT_TO_PTR(2)); + assert_se(hashmap_get(h, "//foo") == INT_TO_PTR(2)); + assert_se(hashmap_get(h, "/////foo////") == INT_TO_PTR(2)); + assert_se(hashmap_get(h, "/////foox////") == INT_TO_PTR(4)); + assert_se(hashmap_get(h, "/foox/") == INT_TO_PTR(4)); + assert_se(hashmap_get(h, "/foox") == INT_TO_PTR(4)); + assert_se(!hashmap_get(h, "foox")); + assert_se(hashmap_get(h, "foo/bar/quux") == INT_TO_PTR(6)); + assert_se(hashmap_get(h, "foo////bar////quux/////") == INT_TO_PTR(6)); + assert_se(!hashmap_get(h, "/foo////bar////quux/////")); +} + int main(int argc, const char *argv[]) { test_hashmap_funcs(); test_ordered_hashmap_funcs(); @@ -64,4 +92,7 @@ int main(int argc, const char *argv[]) { test_uint64_compare_func(); test_trivial_compare_func(); test_string_compare_func(); + test_path_hashmap(); + + return 0; } From 0c2c0e724a401837c6101b91249ff8a40c192150 Mon Sep 17 00:00:00 2001 From: Martin Wilck Date: Mon, 23 Apr 2018 21:59:40 +0200 Subject: [PATCH 02/25] test/udev-test.pl: create /dev/null /dev/null is required by udev for output redirection, otherwise it emits irritating warnings for several test cases. Create this device in the test environment. This requires dropping "nodev" from the mountpoint for /dev in the test environment. --- test/udev-test.pl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/udev-test.pl b/test/udev-test.pl index 0d348e5c08c..191934a56f7 100755 --- a/test/udev-test.pl +++ b/test/udev-test.pl @@ -1538,7 +1538,7 @@ sub udev_setup { system("umount", $udev_tmpfs); rmdir($udev_tmpfs); mkdir($udev_tmpfs) || die "unable to create udev_tmpfs: $udev_tmpfs\n"; - system("mount", "-o", "rw,mode=755,nosuid,noexec,nodev", "-t", "tmpfs", "tmpfs", $udev_tmpfs) && die "unable to mount tmpfs"; + system("mount", "-o", "rw,mode=755,nosuid,noexec", "-t", "tmpfs", "tmpfs", $udev_tmpfs) && die "unable to mount tmpfs"; mkdir($udev_dev) || die "unable to create udev_dev: $udev_dev\n"; # setting group and mode of udev_dev ensures the tests work @@ -1546,6 +1546,8 @@ sub udev_setup { chown (0, 0, $udev_dev) || die "unable to chown $udev_dev\n"; chmod (0755, $udev_dev) || die "unable to chmod $udev_dev\n"; + system ("mknod", "$udev_dev/null", "b", "1", "3"); + chown (0666, "$udev_dev/null"); system("cp", "-r", "test/sys/", $udev_sys) && die "unable to copy test/sys"; system("rm", "-rf", "$udev_run"); From daa4a622f8a8dd70071bc2f964996293ed0b9103 Mon Sep 17 00:00:00 2001 From: Martin Wilck Date: Fri, 20 Apr 2018 22:38:30 +0200 Subject: [PATCH 03/25] test/udev-test.pl: allow multiple devices per test Allow testing cases where multiple devices are added and removed. This implies a change of the data structure: every test allows for multiple devices to be added, and "exp_name" etc. are now properties of the device, not of the test. --- test/udev-test.pl | 1249 +++++++++++++++++++++++++++++++-------------- 1 file changed, 862 insertions(+), 387 deletions(-) diff --git a/test/udev-test.pl b/test/udev-test.pl index 191934a56f7..8afa95ce7bd 100755 --- a/test/udev-test.pl +++ b/test/udev-test.pl @@ -43,17 +43,28 @@ my @tests = ( { desc => "no rules", - devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "sda" , - exp_rem_error => "yes", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", + exp_name => "sda" , + exp_rem_error => "yes", + }, + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", + exp_name => "sda1" , + exp_rem_error => "yes", + }], rules => < "label test of scsi disc", - devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "boot_disk" , + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", + exp_name => "boot_disk" , + }], rules => < "label test of scsi disc", - devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "boot_disk" , + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", + exp_name => "boot_disk" , + }], rules => < "label test of scsi disc", - devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "boot_disk" , + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", + exp_name => "boot_disk" , + }], rules => < "label test of scsi partition", - devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", - exp_name => "boot_disk1" , + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", + exp_name => "boot_disk1" , + }], rules => < "label test of pattern match", - devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", - exp_name => "boot_disk1" , + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", + exp_name => "boot_disk1" , + }], rules => < "label test of multiple sysfs files", - devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", - exp_name => "boot_disk1" , + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", + exp_name => "boot_disk1" , + }], rules => < "label test of max sysfs files (skip invalid rule)", - devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", - exp_name => "boot_disk1" , + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", + exp_name => "boot_disk1" , + }], rules => < "catch device by *", - devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "modem/0" , + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", + exp_name => "modem/0" , + }], rules => < "catch device by * - take 2", - devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "modem/0" , + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", + exp_name => "modem/0" , + }], rules => < "catch device by ?", - devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "modem/0" , + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", + exp_name => "modem/0" , + }], rules => < "catch device by character class", - devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "modem/0" , + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", + exp_name => "modem/0" , + }], rules => < "replace kernel name", - devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "modem" , + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", + exp_name => "modem" , + }], rules => < "Handle comment lines in config file (and replace kernel name)", - devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "modem" , + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", + exp_name => "modem" , + }], rules => < "Handle comment lines in config file with whitespace (and replace kernel name)", - devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "modem" , + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", + exp_name => "modem" , + }], rules => < "Handle whitespace only lines (and replace kernel name)", - devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "whitespace" , + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", + exp_name => "whitespace" , + }], rules => < "Handle empty lines in config file (and replace kernel name)", - devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "modem" , + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", + exp_name => "modem" , + }], rules => < "Handle backslashed multi lines in config file (and replace kernel name)", - devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "modem" , + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", + exp_name => "modem" , + }], rules => < "preserve backslashes, if they are not for a newline", - devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "aaa", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", + exp_name => "aaa", + }], rules => < "Handle stupid backslashed multi lines in config file (and replace kernel name)", - devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "modem" , + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", + exp_name => "modem" , + }], rules => < "subdirectory handling", - devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "sub/direct/ory/modem" , + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", + exp_name => "sub/direct/ory/modem" , + }], rules => < "parent device name match of scsi partition", - devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", - exp_name => "first_disk5" , + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", + exp_name => "first_disk5" , + }], rules => < "test substitution chars", - devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", - exp_name => "Major:8:minor:5:kernelnumber:5:id:0:0:0:0" , + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", + exp_name => "Major:8:minor:5:kernelnumber:5:id:0:0:0:0" , + }], rules => < "import of shell-value returned from program", - devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "node12345678", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", + exp_name => "node12345678", + }], rules => < "sustitution of sysfs value (%s{file})", - devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "disk-ATA-sda" , + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", + exp_name => "disk-ATA-sda" , + }], rules => < "program result substitution", - devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", - exp_name => "special-device-5" , - not_exp_name => "not" , + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", + exp_name => "special-device-5" , + not_exp_name => "not" , + }], rules => < "program result substitution (newline removal)", - devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", - exp_name => "newline_removed" , + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", + exp_name => "newline_removed" , + }], rules => < "program result substitution", - devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", - exp_name => "test-0:0:0:0" , + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", + exp_name => "test-0:0:0:0" , + }], rules => < "program with lots of arguments", - devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", - exp_name => "foo9" , + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", + exp_name => "foo9" , + }], rules => < "program with subshell", - devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", - exp_name => "bar9" , + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", + exp_name => "bar9" , + }], rules => < "program arguments combined with apostrophes", - devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", - exp_name => "foo7" , + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", + exp_name => "foo7" , + }], rules => < "program arguments combined with escaped double quotes, part 1", - devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", - exp_name => "foo2" , + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", + exp_name => "foo2" , + }], rules => < "program arguments combined with escaped double quotes, part 2", - devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", - exp_name => "foo2" , + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", + exp_name => "foo2" , + }], rules => < "program arguments combined with escaped double quotes, part 3", - devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", - exp_name => "foo2" , + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", + exp_name => "foo2" , + }], rules => < "characters before the %c{N} substitution", - devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", - exp_name => "my-foo9" , + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", + exp_name => "my-foo9" , + }], rules => < "substitute the second to last argument", - devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", - exp_name => "my-foo8" , + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", + exp_name => "my-foo8" , + }], rules => < "test substitution by variable name", - devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", - exp_name => "Major:8-minor:5-kernelnumber:5-id:0:0:0:0", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", + exp_name => "Major:8-minor:5-kernelnumber:5-id:0:0:0:0", + }], rules => < "test substitution by variable name 2", - devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", - exp_name => "Major:8-minor:5-kernelnumber:5-id:0:0:0:0", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", + exp_name => "Major:8-minor:5-kernelnumber:5-id:0:0:0:0", + }], rules => < "test substitution by variable name 3", - devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", - exp_name => "850:0:0:05" , + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", + exp_name => "850:0:0:05" , + }], rules => < "test substitution by variable name 4", - devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", - exp_name => "855" , + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", + exp_name => "855" , + }], rules => < "test substitution by variable name 5", - devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", - exp_name => "8550:0:0:0" , + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", + exp_name => "8550:0:0:0" , + }], rules => < "non matching SUBSYSTEMS for device with no parent", - devpath => "/devices/virtual/tty/console", - exp_name => "TTY", + devices => [ + { + devpath => "/devices/virtual/tty/console", + exp_name => "TTY", + }], rules => < "non matching SUBSYSTEMS", - devpath => "/devices/virtual/tty/console", - exp_name => "TTY" , + devices => [ + { + devpath => "/devices/virtual/tty/console", + exp_name => "TTY" , + }], rules => < "ATTRS match", - devpath => "/devices/virtual/tty/console", - exp_name => "foo" , + devices => [ + { + devpath => "/devices/virtual/tty/console", + exp_name => "foo" , + }], rules => < "ATTR (empty file)", - devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "empty" , + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", + exp_name => "empty" , + }], rules => < "ATTR (non-existent file)", - devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "non-existent" , + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", + exp_name => "non-existent" , + }], rules => < "program and bus type match", - devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "scsi-0:0:0:0" , + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", + exp_name => "scsi-0:0:0:0" , + }], rules => < "sysfs parent hierarchy", - devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "modem" , + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", + exp_name => "modem" , + }], rules => < "name test with ! in the name", - devpath => "/devices/virtual/block/fake!blockdev0", - exp_name => "is/a/fake/blockdev0" , + devices => [ + { + devpath => "/devices/virtual/block/fake!blockdev0", + exp_name => "is/a/fake/blockdev0" , + }], rules => < "name test with ! in the name, but no matching rule", - devpath => "/devices/virtual/block/fake!blockdev0", - exp_name => "fake/blockdev0" , - exp_rem_error => "yes", + devices => [ + { + devpath => "/devices/virtual/block/fake!blockdev0", + exp_name => "fake/blockdev0" , + exp_rem_error => "yes", + }], rules => < "KERNELS rule", - devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "scsi-0:0:0:0", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", + exp_name => "scsi-0:0:0:0", + }], rules => < "KERNELS wildcard all", - devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "scsi-0:0:0:0", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", + exp_name => "scsi-0:0:0:0", + }], rules => < "KERNELS wildcard partial", - devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "scsi-0:0:0:0", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", + exp_name => "scsi-0:0:0:0", + }], rules => < "KERNELS wildcard partial 2", - devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "scsi-0:0:0:0", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", + exp_name => "scsi-0:0:0:0", + }], rules => < "substitute attr with link target value (first match)", - devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "driver-is-sd", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", + exp_name => "driver-is-sd", + }], rules => < "substitute attr with link target value (currently selected device)", - devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "driver-is-ahci", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", + exp_name => "driver-is-ahci", + }], rules => < "ignore ATTRS attribute whitespace", - devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "ignored", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", + exp_name => "ignored", + }], rules => < "do not ignore ATTRS attribute whitespace", - devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "matched-with-space", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", + exp_name => "matched-with-space", + }], rules => < "permissions USER=bad GROUP=name", - devpath => "/devices/virtual/tty/tty33", - exp_name => "tty33", - exp_perms => "0:0:0600", + devices => [ + { + devpath => "/devices/virtual/tty/tty33", + exp_name => "tty33", + exp_perms => "0:0:0600", + }], rules => < "permissions OWNER=1", - devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "node", - exp_perms => "1::0600", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", + exp_name => "node", + exp_perms => "1::0600", + }], rules => < "permissions GROUP=1", - devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "node", - exp_perms => ":1:0660", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", + exp_name => "node", + exp_perms => ":1:0660", + }], rules => < "textual user id", - devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "node", - exp_perms => "nobody::0600", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", + exp_name => "node", + exp_perms => "daemon::0600", + }], rules => < "textual group id", - devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "node", - exp_perms => ":daemon:0660", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", + exp_name => "node", + exp_perms => ":daemon:0660", + }], rules => < "textual user/group id", - devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "node", - exp_perms => "root:mail:0660", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", + exp_name => "node", + exp_perms => "root:mail:0660", + }], rules => < "permissions MODE=0777", - devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "node", - exp_perms => "::0777", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", + exp_name => "node", + exp_perms => "::0777", + }], rules => < "permissions OWNER=1 GROUP=1 MODE=0777", - devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "node", - exp_perms => "1:1:0777", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", + exp_name => "node", + exp_perms => "1:1:0777", + }], rules => < "permissions OWNER to 1", - devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "ttyACM0", - exp_perms => "1::", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", + exp_name => "ttyACM0", + exp_perms => "1::", + }], rules => < "permissions GROUP to 1", - devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "ttyACM0", - exp_perms => ":1:0660", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", + exp_name => "ttyACM0", + exp_perms => ":1:0660", + }], rules => < "permissions MODE to 0060", - devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "ttyACM0", - exp_perms => "::0060", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", + exp_name => "ttyACM0", + exp_perms => "::0060", + }], rules => < "permissions OWNER, GROUP, MODE", - devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "ttyACM0", - exp_perms => "1:1:0777", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", + exp_name => "ttyACM0", + exp_perms => "1:1:0777", + }], rules => < "permissions only rule", - devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "ttyACM0", - exp_perms => "1:1:0777", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", + exp_name => "ttyACM0", + exp_perms => "1:1:0777", + }], rules => < "multiple permissions only rule", - devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "ttyACM0", - exp_perms => "1:1:0777", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", + exp_name => "ttyACM0", + exp_perms => "1:1:0777", + }], rules => < "permissions only rule with override at SYMLINK+ rule", - devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "ttyACM0", - exp_perms => "1:2:0777", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", + exp_name => "ttyACM0", + exp_perms => "1:2:0777", + }], rules => < "major/minor number test", - devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "node", - exp_majorminor => "8:0", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", + exp_name => "node", + exp_majorminor => "8:0", + }], rules => < "big major number test", - devpath => "/devices/virtual/misc/misc-fake1", - exp_name => "node", - exp_majorminor => "4095:1", + devices => [ + { + devpath => "/devices/virtual/misc/misc-fake1", + exp_name => "node", + exp_majorminor => "4095:1", + }], rules => < "big major and big minor number test", - devpath => "/devices/virtual/misc/misc-fake89999", - exp_name => "node", - exp_majorminor => "4095:89999", + devices => [ + { + devpath => "/devices/virtual/misc/misc-fake89999", + exp_name => "node", + exp_majorminor => "4095:89999", + }], rules => < "multiple symlinks with format char", - devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "symlink2-ttyACM0", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", + exp_name => "symlink2-ttyACM0", + }], rules => < "multiple symlinks with a lot of s p a c e s", - devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "one", - not_exp_name => " ", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", + exp_name => "one", + not_exp_name => " ", + }], rules => < "symlink with spaces in substituted variable", - devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "name-one_two_three-end", - not_exp_name => " ", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", + exp_name => "name-one_two_three-end", + not_exp_name => " ", + }], rules => < "symlink with leading space in substituted variable", - devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "name-one_two_three-end", - not_exp_name => " ", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", + exp_name => "name-one_two_three-end", + not_exp_name => " ", + }], rules => < "symlink with trailing space in substituted variable", - devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "name-one_two_three-end", - not_exp_name => " ", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", + exp_name => "name-one_two_three-end", + not_exp_name => " ", + }], rules => < "symlink with lots of space in substituted variable", - devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "name-one_two_three-end", - not_exp_name => " ", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", + exp_name => "name-one_two_three-end", + not_exp_name => " ", + }], rules => < "symlink with multiple spaces in substituted variable", - devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "name-one_two_three-end", - not_exp_name => " ", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", + exp_name => "name-one_two_three-end", + not_exp_name => " ", + }], rules => < "symlink with space and var with space, part 1", - devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "first", - not_exp_name => " ", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", + exp_name => "first", + not_exp_name => " ", + }], rules => < "symlink with space and var with space, part 2", - devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "name-one_two_three-end", - not_exp_name => " ", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", + exp_name => "name-one_two_three-end", + not_exp_name => " ", + }], rules => < "symlink with space and var with space, part 3", - devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "another_symlink", - not_exp_name => " ", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", + exp_name => "another_symlink", + not_exp_name => " ", + }], rules => < "symlink creation (same directory)", - devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "modem0", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", + exp_name => "modem0", + }], rules => < "multiple symlinks", - devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "second-0" , + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", + exp_name => "second-0" , + }], rules => < "symlink name '.'", - devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => ".", - exp_add_error => "yes", - exp_rem_error => "yes", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", + exp_name => ".", + exp_add_error => "yes", + exp_rem_error => "yes", + }], rules => < "symlink node to itself", - devpath => "/devices/virtual/tty/tty0", - exp_name => "link", - exp_add_error => "yes", - exp_rem_error => "yes", + devices => [ + { + devpath => "/devices/virtual/tty/tty0", + exp_name => "link", + exp_add_error => "yes", + exp_rem_error => "yes", + }], option => "clean", rules => < "symlink %n substitution", - devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "symlink0", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", + exp_name => "symlink0", + }], rules => < "symlink %k substitution", - devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "symlink-ttyACM0", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", + exp_name => "symlink-ttyACM0", + }], rules => < "symlink %M:%m substitution", - devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "major-166:0", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", + exp_name => "major-166:0", + }], rules => < "symlink %b substitution", - devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "symlink-0:0:0:0", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", + exp_name => "symlink-0:0:0:0", + }], rules => < "symlink %c substitution", - devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "test", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", + exp_name => "test", + }], rules => < "symlink %c{N} substitution", - devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "test", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", + exp_name => "test", + }], rules => < "symlink %c{N+} substitution", - devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "this", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", + exp_name => "this", + }], rules => < "symlink only rule with %c{N+}", - devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "test", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", + exp_name => "test", + }], rules => < "symlink %s{filename} substitution", - devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "166:0", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", + exp_name => "166:0", + }], rules => < "program result substitution (numbered part of)", - devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", - exp_name => "link1", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", + exp_name => "link1", + }], rules => < "program result substitution (numbered part of+)", - devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", - exp_name => "link4", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", + exp_name => "link4", + }], rules => < "SUBSYSTEM match test", - devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "node", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", + exp_name => "node", + }], rules => < "DRIVERS match test", - devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "node", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", + exp_name => "node", + }], rules => < "devnode substitution test", - devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "node", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", + exp_name => "node", + }], rules => < "parent node name substitution test", - devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", - exp_name => "sda-part-1", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", + exp_name => "sda-part-1", + }], rules => < "udev_root substitution", - devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", - exp_name => "start-/dev-end", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", + exp_name => "start-/dev-end", + }], rules => < "last_rule option", - devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", - exp_name => "last", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", + exp_name => "last", + }], rules => < "negation KERNEL!=", - devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", - exp_name => "match", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", + exp_name => "match", + }], rules => < "negation SUBSYSTEM!=", - devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", - exp_name => "not-anything", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", + exp_name => "not-anything", + }], rules => < "negation PROGRAM!= exit code", - devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", - exp_name => "nonzero-program", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", + exp_name => "nonzero-program", + }], rules => < "ENV{} test", - devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", - exp_name => "true", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", + exp_name => "true", + }], rules => < "ENV{} test", - devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", - exp_name => "true", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", + exp_name => "true", + }], rules => < "ENV{} test (assign)", - devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", - exp_name => "true", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", + exp_name => "true", + }], rules => < "ENV{} test (assign 2 times)", - devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", - exp_name => "true", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", + exp_name => "true", + }], rules => < "ENV{} test (assign2)", - devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", - exp_name => "part", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", + exp_name => "part", + }], rules => < "untrusted string sanitize", - devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", - exp_name => "sane", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", + exp_name => "sane", + }], rules => < "untrusted string sanitize (don't replace utf8)", - devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", - exp_name => "uber", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", + exp_name => "uber", + }], rules => < "untrusted string sanitize (replace invalid utf8)", - devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", - exp_name => "replaced", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", + exp_name => "replaced", + }], rules => < "read sysfs value from parent device", - devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "serial-354172020305000", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", + exp_name => "serial-354172020305000", + }], rules => < "match against empty key string", - devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "ok", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", + exp_name => "ok", + }], rules => < "check ACTION value", - devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "ok", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", + exp_name => "ok", + }], rules => < "final assignment", - devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "ok", - exp_perms => "root:tty:0640", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", + exp_name => "ok", + exp_perms => "root:tty:0640", + }], rules => < "final assignment 2", - devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "ok", - exp_perms => "root:tty:0640", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", + exp_name => "ok", + exp_perms => "root:tty:0640", + }], rules => < "env substitution", - devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "node-add-me", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", + exp_name => "node-add-me", + }], rules => < "reset list to current value", - devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "three", - not_exp_name => "two", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", + exp_name => "three", + not_exp_name => "two", + }], rules => < "test empty SYMLINK+ (empty override)", - devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "right", - not_exp_name => "wrong", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", + exp_name => "right", + not_exp_name => "wrong", + }], rules => < "test multi matches", - devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "right", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", + exp_name => "right", + }], rules => < "test multi matches 2", - devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "right", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", + exp_name => "right", + }], rules => < "test multi matches 3", - devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "right", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", + exp_name => "right", + }], rules => < "test multi matches 4", - devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "right", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", + exp_name => "right", + }], rules => < "IMPORT parent test sequence 1/2 (keep)", - devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "parent", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", + exp_name => "parent", + }], option => "keep", rules => < "IMPORT parent test sequence 2/2 (keep)", - devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", - exp_name => "parentenv-parent_right", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", + exp_name => "parentenv-parent_right", + }], option => "clean", rules => < "GOTO test", - devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", - exp_name => "right", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", + exp_name => "right", + }], rules => < "GOTO label does not exist", - devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", - exp_name => "right", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", + exp_name => "right", + }], rules => < "SYMLINK+ compare test", - devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", - exp_name => "right", - not_exp_name => "wrong", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", + exp_name => "right", + not_exp_name => "wrong", + }], rules => < "invalid key operation", - devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", - exp_name => "yes", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", + exp_name => "yes", + }], rules => < "operator chars in attribute", - devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "yes", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", + exp_name => "yes", + }], rules => < "overlong comment line", - devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", - exp_name => "yes", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", + exp_name => "yes", + }], rules => < "magic subsys/kernel lookup", - devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "00:16:41:e2:8d:ff", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", + exp_name => "00:16:41:e2:8d:ff", + }], rules => < "TEST absolute path", - devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "there", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", + exp_name => "there", + }], rules => < "TEST subsys/kernel lookup", - devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "yes", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", + exp_name => "yes", + }], rules => < "TEST relative path", - devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "relative", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", + exp_name => "relative", + }], rules => < "TEST wildcard substitution (find queue/nr_requests)", - devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "found-subdir", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", + exp_name => "found-subdir", + }], rules => < "TEST MODE=0000", - devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "sda", - exp_perms => "0:0:0000", - exp_rem_error => "yes", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", + exp_name => "sda", + exp_perms => "0:0:0000", + exp_rem_error => "yes", + }], rules => < "TEST PROGRAM feeds OWNER, GROUP, MODE", - devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "sda", - exp_perms => "1:1:0400", - exp_rem_error => "yes", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", + exp_name => "sda", + exp_perms => "1:1:0400", + exp_rem_error => "yes", + }], rules => < "TEST PROGRAM feeds MODE with overflow", - devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "sda", - exp_perms => "0:0:0440", - exp_rem_error => "yes", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", + exp_name => "sda", + exp_perms => "0:0:0440", + exp_rem_error => "yes", + }], rules => < "magic [subsys/sysname] attribute substitution", - devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "sda-8741C4G-end", - exp_perms => "0:0:0600", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", + exp_name => "sda-8741C4G-end", + exp_perms => "0:0:0600", + }], rules => < "builtin path_id", - devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "disk/by-path/pci-0000:00:1f.2-scsi-0:0:0:0", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", + exp_name => "disk/by-path/pci-0000:00:1f.2-scsi-0:0:0:0", + }], rules => < "add and match tag", - devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "found", - not_exp_name => "bad" , + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", + exp_name => "found", + not_exp_name => "bad" , + }], rules => < "don't crash with lots of tags", - devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "found", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", + exp_name => "found", + }], rules => $rules_10k_tags . <{desc}\n"; - print "device \'$rules->{devpath}\' expecting node/link \'$rules->{exp_name}\'\n"; +sub check_add { + my ($device) = @_; - $rc = udev("add", $rules->{devpath}, \$rules->{rules}); - if ($rc != 0) { - print "$udev_bin add failed with code $rc\n"; - $error++; - } - if (defined($rules->{not_exp_name})) { - if ((-e "$udev_dev/$rules->{not_exp_name}") || - (-l "$udev_dev/$rules->{not_exp_name}")) { - print "nonexistent: error \'$rules->{not_exp_name}\' not expected to be there\n"; + if (defined($device->{not_exp_name})) { + if ((-e "$udev_dev/$device->{not_exp_name}") || + (-l "$udev_dev/$device->{not_exp_name}")) { + print "nonexistent: error \'$device->{not_exp_name}\' not expected to be there\n"; $error++; sleep(1); } } - - if ((-e "$udev_dev/$rules->{exp_name}") || - (-l "$udev_dev/$rules->{exp_name}")) { + if ((-e "$udev_dev/$device->{exp_name}") || + (-l "$udev_dev/$device->{exp_name}")) { my ($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size, - $atime, $mtime, $ctime, $blksize, $blocks) = stat("$udev_dev/$rules->{exp_name}"); + $atime, $mtime, $ctime, $blksize, $blocks) = stat("$udev_dev/$device->{exp_name}"); - if (defined($rules->{exp_perms})) { - permissions_test($rules, $uid, $gid, $mode); + if (defined($device->{exp_perms})) { + permissions_test($device, $uid, $gid, $mode); } - if (defined($rules->{exp_majorminor})) { - major_minor_test($rules, $rdev); + if (defined($device->{exp_majorminor})) { + major_minor_test($device, $rdev); } - print "add: ok\n"; + print "add $device->{devpath}: ok\n"; } else { - print "add: error"; - if ($rules->{exp_add_error}) { + print "add $device->{devpath}: error"; + if ($device->{exp_add_error}) { print " as expected\n"; } else { print "\n"; @@ -1599,21 +2044,15 @@ sub run_test { sleep(1); } } +} - if (defined($rules->{option}) && $rules->{option} eq "keep") { - print "\n\n"; - return; - } +sub check_remove { + my ($device) = @_; - $rc = udev("remove", $rules->{devpath}, \$rules->{rules}); - if ($rc != 0) { - print "$udev_bin remove failed with code $rc\n"; - $error++; - } - if ((-e "$udev_dev/$rules->{exp_name}") || - (-l "$udev_dev/$rules->{exp_name}")) { - print "remove: error"; - if ($rules->{exp_rem_error}) { + if ((-e "$udev_dev/$device->{exp_name}") || + (-l "$udev_dev/$device->{exp_name}")) { + print "remove $device->{devpath}: error"; + if ($device->{exp_rem_error}) { print " as expected\n"; } else { print "\n"; @@ -1623,7 +2062,43 @@ sub run_test { sleep(1); } } else { - print "remove: ok\n"; + print "remove $device->{devpath}: ok\n"; + } +} + +sub run_test { + my ($rules, $number) = @_; + my $rc; + my @devices = @{$rules->{devices}}; + + print "TEST $number: $rules->{desc}\n"; + foreach my $dev (@devices) { + print "device \'$dev->{devpath}\' expecting node/link \'$dev->{exp_name}\'\n"; + $rc = udev("add", $dev->{devpath}, \$rules->{rules}); + if ($rc != 0) { + print "$udev_bin add failed with code $rc\n"; + $error++; + } + } + + foreach my $dev (@devices) { + check_add($dev); + } + + if (defined($rules->{option}) && $rules->{option} eq "keep") { + print "\n\n"; + return; + } + + foreach my $dev (@devices) { + $rc = udev("remove", $dev->{devpath}, \$rules->{rules}); + if ($rc != 0) { + print "$udev_bin remove failed with code $rc\n"; + $error++; + } + } + foreach my $dev (@devices) { + check_remove($dev); } print "\n"; From 5da06c254edf664ba1c7bfa34158dc44b46f5c6f Mon Sep 17 00:00:00 2001 From: Martin Wilck Date: Mon, 23 Apr 2018 21:58:12 +0200 Subject: [PATCH 04/25] test/udev-test.pl: create rules only once It's not necessary to write the rules for every udev run, as we now may have many (rather than just 2) per test. --- test/udev-test.pl | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/test/udev-test.pl b/test/udev-test.pl index 8afa95ce7bd..7e3cc9e0364 100755 --- a/test/udev-test.pl +++ b/test/udev-test.pl @@ -1904,14 +1904,18 @@ }, ); -sub udev { - my ($action, $devpath, $rules) = @_; +sub create_rules { + my ($rules) = @_; # create temporary rules system("mkdir", "-p", "$udev_rules_dir"); open CONF, ">$udev_rules" || die "unable to create rules file: $udev_rules"; print CONF $$rules; close CONF; +} + +sub udev { + my ($action, $devpath) = @_; if ($valgrind > 0) { return system("$udev_bin_valgrind $action $devpath"); @@ -2072,9 +2076,10 @@ sub run_test { my @devices = @{$rules->{devices}}; print "TEST $number: $rules->{desc}\n"; + create_rules(\$rules->{rules}); foreach my $dev (@devices) { print "device \'$dev->{devpath}\' expecting node/link \'$dev->{exp_name}\'\n"; - $rc = udev("add", $dev->{devpath}, \$rules->{rules}); + $rc = udev("add", $dev->{devpath}); if ($rc != 0) { print "$udev_bin add failed with code $rc\n"; $error++; @@ -2091,7 +2096,7 @@ sub run_test { } foreach my $dev (@devices) { - $rc = udev("remove", $dev->{devpath}, \$rules->{rules}); + $rc = udev("remove", $dev->{devpath}); if ($rc != 0) { print "$udev_bin remove failed with code $rc\n"; $error++; From 3775b8f393f35823db6bfa5d44f5e2ac18190c96 Mon Sep 17 00:00:00 2001 From: Martin Wilck Date: Mon, 23 Apr 2018 21:59:05 +0200 Subject: [PATCH 05/25] test/udev-test.pl: allow concurrent additions and removals Allow testing cases where multiple devices are added and removed simultaneously. Tests are started as synchronously as possible using a semaphore, in order to test possible race conditions. If this isn't desired, the test parameter "sleep_us" can be set to the number of microseconds to wait between udev invocations. --- test/udev-test.pl | 90 +++++++++++++++++++++++++++++++++++++---------- 1 file changed, 72 insertions(+), 18 deletions(-) diff --git a/test/udev-test.pl b/test/udev-test.pl index 7e3cc9e0364..4d2db9b33ed 100755 --- a/test/udev-test.pl +++ b/test/udev-test.pl @@ -19,6 +19,10 @@ use warnings; use strict; +use POSIX qw(WIFEXITED WEXITSTATUS); +use IPC::SysV qw(IPC_PRIVATE S_IRUSR S_IWUSR IPC_CREAT); +use IPC::Semaphore; +use Time::HiRes qw(usleep); my $udev_bin = "./test-udev"; my $valgrind = 0; @@ -2023,6 +2027,8 @@ sub check_add { sleep(1); } } + + print "device \'$device->{devpath}\' expecting node/link \'$device->{exp_name}\'\n"; if ((-e "$udev_dev/$device->{exp_name}") || (-l "$udev_dev/$device->{exp_name}")) { @@ -2070,21 +2076,72 @@ sub check_remove { } } +sub run_udev { + my ($action, $dev, $sleep_us, $sema) = @_; + + # Notify main process that this worker has started + $sema->op(0, 1, 0); + + # Wait for start + $sema->op(0, 0, 0); + usleep($sleep_us) if defined ($sleep_us); + my $rc = udev($action, $dev->{devpath}); + exit $rc; +} + +sub fork_and_run_udev { + my ($action, $rules, $sema) = @_; + my @devices = @{$rules->{devices}}; + my $dev; + my $k = 0; + + $sema->setval(0, 1); + foreach $dev (@devices) { + my $pid = fork(); + + if (!$pid) { + run_udev($action, $dev, + defined($rules->{sleep_us}) ? $k * $rules->{sleep_us} : undef, + $sema); + } else { + $dev->{pid} = $pid; + } + $k++; + } + + # This operation waits for all workers to become ready, and + # starts them off when that's the case. + $sema->op(0, -($#devices + 2), 0); + + foreach $dev (@devices) { + my $rc; + my $pid; + + $pid = waitpid($dev->{pid}, 0); + if ($pid == -1) { + print "error waiting for pid dev->{pid}\n"; + $error += 1; + } + if (WIFEXITED($?)) { + $rc = WEXITSTATUS($?); + + if ($rc) { + print "$udev_bin $action for $dev->{devpath} failed with code $rc\n"; + $error += 1; + } + } + } +} + sub run_test { - my ($rules, $number) = @_; + my ($rules, $number, $sema) = @_; my $rc; my @devices = @{$rules->{devices}}; print "TEST $number: $rules->{desc}\n"; create_rules(\$rules->{rules}); - foreach my $dev (@devices) { - print "device \'$dev->{devpath}\' expecting node/link \'$dev->{exp_name}\'\n"; - $rc = udev("add", $dev->{devpath}); - if ($rc != 0) { - print "$udev_bin add failed with code $rc\n"; - $error++; - } - } + + fork_and_run_udev("add", $rules, $sema); foreach my $dev (@devices) { check_add($dev); @@ -2095,13 +2152,8 @@ sub run_test { return; } - foreach my $dev (@devices) { - $rc = udev("remove", $dev->{devpath}); - if ($rc != 0) { - print "$udev_bin remove failed with code $rc\n"; - $error++; - } - } + fork_and_run_udev("remove", $rules, $sema); + foreach my $dev (@devices) { check_remove($dev); } @@ -2154,12 +2206,13 @@ sub run_test { push(@list, $arg); } } +my $sema = IPC::Semaphore->new(IPC_PRIVATE, 1, S_IRUSR | S_IWUSR | IPC_CREAT); if ($list[0]) { foreach my $arg (@list) { if (defined($tests[$arg-1]->{desc})) { print "udev-test will run test number $arg:\n\n"; - run_test($tests[$arg-1], $arg); + run_test($tests[$arg-1], $arg, $sema); } else { print "test does not exist.\n"; } @@ -2169,11 +2222,12 @@ sub run_test { print "\nudev-test will run ".($#tests + 1)." tests:\n\n"; foreach my $rules (@tests) { - run_test($rules, $test_num); + run_test($rules, $test_num, $sema); $test_num++; } } +$sema->remove; print "$error errors occurred\n\n"; # cleanup From 4b2f92c4007a0ed272a42430bf0b24e93000ac53 Mon Sep 17 00:00:00 2001 From: Martin Wilck Date: Tue, 24 Apr 2018 09:38:26 +0200 Subject: [PATCH 06/25] test/udev-test.pl: use computed devnode name More often than not, the created devnode is the basename of the sysfs entry. The "devnode" device may be used to override the auto-detected node name. Permissions and major/minor number are now verified on the devnode itself, not on symlinks. For those tests where exp_name is set to the computed devnode name, the explicit "exp_name" can be removed. "exp_name" is only required for symlinks. This allows separate testing for devnodes and symlinks an a follow-up patch. --- test/udev-test.pl | 92 +++++++++++++++++++++++++++++++++-------------- 1 file changed, 66 insertions(+), 26 deletions(-) diff --git a/test/udev-test.pl b/test/udev-test.pl index 4d2db9b33ed..326ad895fb5 100755 --- a/test/udev-test.pl +++ b/test/udev-test.pl @@ -50,12 +50,10 @@ devices => [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "sda" , exp_rem_error => "yes", }, { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", - exp_name => "sda1" , exp_rem_error => "yes", }], rules => < [ { devpath => "/devices/virtual/block/fake!blockdev0", + devnode => "fake/blockdev0", exp_name => "is/a/fake/blockdev0" , }], rules => < [ { devpath => "/devices/virtual/block/fake!blockdev0", - exp_name => "fake/blockdev0" , + devnode => "fake/blockdev0", exp_rem_error => "yes", }], rules => < [ { devpath => "/devices/virtual/tty/tty33", - exp_name => "tty33", exp_perms => "0:0:0600", }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "ttyACM0", exp_perms => "1::", }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "ttyACM0", exp_perms => ":1:0660", }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "ttyACM0", exp_perms => "::0060", }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "ttyACM0", exp_perms => "1:1:0777", }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "ttyACM0", exp_perms => "1:1:0777", }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "ttyACM0", exp_perms => "1:1:0777", }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "ttyACM0", exp_perms => "1:2:0777", }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "sda", exp_perms => "0:0:0000", exp_rem_error => "yes", }], @@ -1832,7 +1822,6 @@ devices => [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "sda", exp_perms => "1:1:0400", exp_rem_error => "yes", }], @@ -1846,7 +1835,6 @@ devices => [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "sda", exp_perms => "0:0:0440", exp_rem_error => "yes", }], @@ -2016,6 +2004,44 @@ sub udev_setup { system("rm", "-rf", "$udev_run"); } +sub get_devnode { + my ($device) = @_; + my $devnode; + + if (defined($device->{devnode})) { + $devnode = "$udev_dev/$device->{devnode}"; + } else { + $devnode = "$device->{devpath}"; + $devnode =~ s!.*/!$udev_dev/!; + } + return $devnode; +} + +sub check_devnode { + my ($device) = @_; + my $devnode = get_devnode($device); + + my @st = lstat("$devnode"); + if (! (-b _ || -c _)) { + print "add $devnode: error\n"; + system("tree", "$udev_dev"); + $error++; + return undef; + } + + my ($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size, + $atime, $mtime, $ctime, $blksize, $blocks) = @st; + + if (defined($device->{exp_perms})) { + permissions_test($device, $uid, $gid, $mode); + } + if (defined($device->{exp_majorminor})) { + major_minor_test($device, $rdev); + } + print "add $devnode: ok\n"; + return $devnode; +} + sub check_add { my ($device) = @_; @@ -2028,19 +2054,13 @@ sub check_add { } } + my $devnode = check_devnode($device); + print "device \'$device->{devpath}\' expecting node/link \'$device->{exp_name}\'\n"; + return if (!defined($device->{exp_name})); + if ((-e "$udev_dev/$device->{exp_name}") || (-l "$udev_dev/$device->{exp_name}")) { - - my ($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size, - $atime, $mtime, $ctime, $blksize, $blocks) = stat("$udev_dev/$device->{exp_name}"); - - if (defined($device->{exp_perms})) { - permissions_test($device, $uid, $gid, $mode); - } - if (defined($device->{exp_majorminor})) { - major_minor_test($device, $rdev); - } print "add $device->{devpath}: ok\n"; } else { print "add $device->{devpath}: error"; @@ -2056,12 +2076,32 @@ sub check_add { } } +sub check_remove_devnode { + my ($device) = @_; + my $devnode = get_devnode($device); + + if (-e "$devnode") { + print "remove $devnode: error"; + print "\n"; + system("tree", "$udev_dev"); + print "\n"; + $error++; + sleep(1); + } else { + print "remove $devnode: ok\n"; + } +} + sub check_remove { my ($device) = @_; + check_remove_devnode($device); + + return if (!defined($device->{exp_name})); + if ((-e "$udev_dev/$device->{exp_name}") || (-l "$udev_dev/$device->{exp_name}")) { - print "remove $device->{devpath}: error"; + print "remove $device->{exp_name}: error"; if ($device->{exp_rem_error}) { print " as expected\n"; } else { @@ -2072,7 +2112,7 @@ sub check_remove { sleep(1); } } else { - print "remove $device->{devpath}: ok\n"; + print "remove $device->{exp_name}: ok\n"; } } From 2ab3fac178a5dde4b6f90879ff309a0ae4c4e7cd Mon Sep 17 00:00:00 2001 From: Martin Wilck Date: Tue, 24 Apr 2018 10:50:24 +0200 Subject: [PATCH 07/25] test/udev-test.pl: test correctness of symlink targets Test if symlinks are created correctly by comparing the symlink targets to the devnode path. This implies (for the symlink) that major/minor numbers and permissions are correct, as we have tested that on the devnode already. --- test/udev-test.pl | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/test/udev-test.pl b/test/udev-test.pl index 326ad895fb5..95570e4e196 100755 --- a/test/udev-test.pl +++ b/test/udev-test.pl @@ -23,6 +23,7 @@ use IPC::SysV qw(IPC_PRIVATE S_IRUSR S_IWUSR IPC_CREAT); use IPC::Semaphore; use Time::HiRes qw(usleep); +use Cwd qw(getcwd abs_path); my $udev_bin = "./test-udev"; my $valgrind = 0; @@ -2056,14 +2057,26 @@ sub check_add { my $devnode = check_devnode($device); - print "device \'$device->{devpath}\' expecting node/link \'$device->{exp_name}\'\n"; return if (!defined($device->{exp_name})); - if ((-e "$udev_dev/$device->{exp_name}") || - (-l "$udev_dev/$device->{exp_name}")) { - print "add $device->{devpath}: ok\n"; + my @st = lstat("$udev_dev/$device->{exp_name}"); + if (-l _) { + my $cwd = getcwd(); + my $dir = "$udev_dev/$device->{exp_name}"; + $dir =~ s!/[^/]*$!!; + my $tgt = readlink("$udev_dev/$device->{exp_name}"); + $tgt = abs_path("$dir/$tgt"); + $tgt =~ s!^$cwd/!!; + + if ($tgt ne $devnode) { + print "symlink $device->{exp_name}: error, found -> $tgt\n"; + $error++; + system("tree", "$udev_dev"); + } else { + print "symlink $device->{exp_name}: ok\n"; + } } else { - print "add $device->{devpath}: error"; + print "symlink $device->{exp_name}: error"; if ($device->{exp_add_error}) { print " as expected\n"; } else { From 07a3bfd6a8ccd78ac6cba7ea86a9fb78e0a55083 Mon Sep 17 00:00:00 2001 From: Martin Wilck Date: Tue, 24 Apr 2018 17:15:58 +0200 Subject: [PATCH 08/25] test/udev-test.pl: allow checking multiple symlinks Instead of testing the existence or non-exisitence of just a single symlink, allow testing of several links per device. Change the test definitions accordingly. --- test/udev-test.pl | 475 ++++++++++++++++++++++++++++------------------ 1 file changed, 286 insertions(+), 189 deletions(-) diff --git a/test/udev-test.pl b/test/udev-test.pl index 95570e4e196..539032bc254 100755 --- a/test/udev-test.pl +++ b/test/udev-test.pl @@ -66,7 +66,7 @@ devices => [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "boot_disk" , + exp_links => ["boot_disk"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "boot_disk" , + exp_links => ["boot_disk"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "boot_disk" , + exp_links => ["boot_disk"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", - exp_name => "boot_disk1" , + exp_links => ["boot_disk1"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", - exp_name => "boot_disk1" , + exp_links => ["boot_disk1", "boot_disk1-4", "boot_disk1-5"], + not_exp_links => ["boot_disk1-1", "boot_disk1-2", "boot_disk1-3"] }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", - exp_name => "boot_disk1" , + exp_links => ["boot_disk1"], + not_exp_links => ["boot_diskX1"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", - exp_name => "boot_disk1" , + exp_links => ["boot_disk1", "boot_diskXY1"], + not_exp_links => ["boot_diskXX1"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "modem/0" , + exp_links => ["modem/0", "catch-all"], }], rules => < "catch device by * - take 2", devices => [ { devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "modem/0" , + exp_links => ["modem/0"], + not_exp_links => ["bad"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "modem/0" , + exp_links => ["modem/0"], + not_exp_links => ["modem/0-1", "modem/0-2"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "modem/0" , + exp_links => ["modem/0"], + not_exp_links => ["modem/0-1", "modem/0-2"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "modem" , + exp_links => ["modem"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "modem" , + exp_links => ["modem"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "modem" , + exp_links => ["modem"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "whitespace" , + exp_links => ["whitespace"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "modem" , + exp_links => ["modem"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "modem" , + exp_links => ["modem"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "aaa", + exp_links => ["aaa"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "modem" , + exp_links => ["modem"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "sub/direct/ory/modem" , + exp_links => ["sub/direct/ory/modem"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", - exp_name => "first_disk5" , + exp_links => ["first_disk5"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", - exp_name => "Major:8:minor:5:kernelnumber:5:id:0:0:0:0" , + exp_links => ["Major:8:minor:5:kernelnumber:5:id:0:0:0:0"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "node12345678", + exp_links => ["node12345678"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "disk-ATA-sda" , + exp_links => ["disk-ATA-sda"], + not_exp_links => ["modem"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", - exp_name => "special-device-5" , - not_exp_name => "not" , + exp_links => ["special-device-5"], + not_exp_links => ["not"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", - exp_name => "newline_removed" , + exp_links => ["newline_removed"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", - exp_name => "test-0:0:0:0" , + exp_links => ["test-0:0:0:0"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", - exp_name => "foo9" , + exp_links => ["foo9"], + not_exp_links => ["foo3", "foo4", "foo5", "foo6", "foo7", "foo8"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", - exp_name => "bar9" , + exp_links => ["bar9"], + not_exp_links => ["foo3", "foo4", "foo5", "foo6", "foo7", "foo8"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", - exp_name => "foo7" , + exp_links => ["foo7"], + not_exp_links => ["foo3", "foo4", "foo5", "foo6", "foo8"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", - exp_name => "foo2" , + exp_links => ["foo2"], + not_exp_links => ["foo1"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", - exp_name => "foo2" , + exp_links => ["foo2"], + not_exp_links => ["foo1"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", - exp_name => "foo2" , + exp_links => ["foo2"], + not_exp_links => ["foo1", "foo3"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", - exp_name => "my-foo9" , + exp_links => ["my-foo9"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", - exp_name => "my-foo8" , + exp_links => ["my-foo8"], + not_exp_links => ["my-foo3", "my-foo4", "my-foo5", "my-foo6", "my-foo7", "my-foo9"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", - exp_name => "Major:8-minor:5-kernelnumber:5-id:0:0:0:0", + exp_links => ["Major:8-minor:5-kernelnumber:5-id:0:0:0:0"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", - exp_name => "Major:8-minor:5-kernelnumber:5-id:0:0:0:0", + exp_links => ["Major:8-minor:5-kernelnumber:5-id:0:0:0:0"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", - exp_name => "850:0:0:05" , + exp_links => ["850:0:0:05"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", - exp_name => "855" , + exp_links => ["855"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", - exp_name => "8550:0:0:0" , + exp_links => ["8550:0:0:0"], }], rules => < [ { devpath => "/devices/virtual/tty/console", - exp_name => "TTY", + exp_links => ["TTY"], + not_exp_links => ["foo"], }], rules => < [ { devpath => "/devices/virtual/tty/console", - exp_name => "TTY" , + exp_links => ["TTY"], + not_exp_links => ["foo"], }], rules => < [ { devpath => "/devices/virtual/tty/console", - exp_name => "foo" , + exp_links => ["foo", "TTY"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "empty" , + exp_links => ["empty", "not-something"], + not_exp_links => ["something", "not-empty"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "non-existent" , + exp_links => ["non-existent", "wrong"], + not_exp_links => ["something", "empty", "not-empty", + "not-something", "something"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "scsi-0:0:0:0" , + exp_links => ["scsi-0:0:0:0"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "modem" , + exp_links => ["modem"], }], rules => < "/devices/virtual/block/fake!blockdev0", devnode => "fake/blockdev0", - exp_name => "is/a/fake/blockdev0" , + exp_links => ["is/a/fake/blockdev0"], + not_exp_links => ["is/not/a/fake/blockdev0", "modem"], }], rules => < "/devices/virtual/block/fake!blockdev0", devnode => "fake/blockdev0", - exp_rem_error => "yes", + not_exp_links => ["modem"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "scsi-0:0:0:0", + exp_links => ["scsi-0:0:0:0"], + not_exp_links => ["no-match", "short-id", "not-scsi"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "scsi-0:0:0:0", + exp_links => ["scsi-0:0:0:0"], + not_exp_links => ["no-match", "before"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "scsi-0:0:0:0", + exp_links => ["scsi-0:0:0:0", "before"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "scsi-0:0:0:0", + exp_links => ["scsi-0:0:0:0", "before"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "driver-is-sd", + exp_links => ["driver-is-sd"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "driver-is-ahci", + exp_links => ["driver-is-ahci"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "ignored", + exp_links => ["ignored"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "matched-with-space", + exp_links => ["matched-with-space"], + not_exp_links => ["wrong-to-ignore"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "node", + exp_links => ["node"], exp_perms => "1::0600", }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "node", + exp_links => ["node"], exp_perms => ":1:0660", }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "node", + exp_links => ["node"], exp_perms => "daemon::0600", }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "node", + exp_links => ["node"], exp_perms => ":daemon:0660", }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "node", + exp_links => ["node"], exp_perms => "root:mail:0660", }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "node", + exp_links => ["node"], exp_perms => "::0777", }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "node", + exp_links => ["node"], exp_perms => "1:1:0777", }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "node", + exp_links => ["node"], exp_majorminor => "8:0", }], rules => < [ { devpath => "/devices/virtual/misc/misc-fake1", - exp_name => "node", + exp_links => ["node"], exp_majorminor => "4095:1", }], rules => < [ { devpath => "/devices/virtual/misc/misc-fake89999", - exp_name => "node", + exp_links => ["node"], exp_majorminor => "4095:89999", }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "symlink2-ttyACM0", + exp_links => ["symlink1-0", "symlink2-ttyACM0", "symlink3-"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "one", - not_exp_name => " ", + exp_links => ["one", "two"], + not_exp_links => [" "], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "name-one_two_three-end", - not_exp_name => " ", + exp_links => ["name-one_two_three-end"], + not_exp_links => [" "], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "name-one_two_three-end", - not_exp_name => " ", + exp_links => ["name-one_two_three-end"], + not_exp_links => [" "], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "name-one_two_three-end", - not_exp_name => " ", + exp_links => ["name-one_two_three-end"], + not_exp_links => [" "], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "name-one_two_three-end", - not_exp_name => " ", + exp_links => ["name-one_two_three-end"], + not_exp_links => [" "], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "name-one_two_three-end", - not_exp_name => " ", + exp_links => ["name-one_two_three-end"], + not_exp_links => [" "], }], rules => < "symlink with space and var with space, part 1", + desc => "symlink with space and var with space", devices => [ { devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "first", - not_exp_name => " ", + exp_links => ["first"], + not_exp_links => [" "], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "name-one_two_three-end", - not_exp_name => " ", + exp_links => ["name-one_two_three-end"], + not_exp_links => [" "], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "another_symlink", - not_exp_name => " ", + exp_links => ["another_symlink"], + not_exp_links => [" "], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "modem0", + exp_links => ["modem0"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "second-0" , + exp_links => ["first-0", "second-0", "third-0"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => ".", + exp_links => ["."], exp_add_error => "yes", exp_rem_error => "yes", }], @@ -1143,7 +1171,7 @@ devices => [ { devpath => "/devices/virtual/tty/tty0", - exp_name => "link", + exp_links => ["link"], exp_add_error => "yes", exp_rem_error => "yes", }], @@ -1157,7 +1185,7 @@ devices => [ { devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "symlink0", + exp_links => ["symlink0"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "symlink-ttyACM0", + exp_links => ["symlink-ttyACM0"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "major-166:0", + exp_links => ["major-166:0"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "symlink-0:0:0:0", + exp_links => ["symlink-0:0:0:0"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "test", + exp_links => ["test"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "test", + exp_links => ["test"], + not_exp_links => ["symlink", "this"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "this", + exp_links => ["test", "this"], + not_exp_links => ["symlink"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "test", + exp_links => ["test", "this"], + not_exp_links => ["symlink"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "166:0", + exp_links => ["166:0"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", - exp_name => "link1", + exp_links => ["link1", "link2"], + not_exp_links => ["node"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", - exp_name => "link4", + exp_links => ["link1", "link2", "link3", "link4"], + not_exp_links => ["node"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "node", + exp_links => ["node"], + not_exp_links => ["should_not_match", "should_not_match2"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "node", + exp_links => ["node"], + not_exp_links => ["should_not_match"] }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "node", + exp_links => ["node"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", - exp_name => "sda-part-1", + exp_links => ["sda-part-1"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", - exp_name => "start-/dev-end", + exp_links => ["start-/dev-end"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", - exp_name => "last", + exp_links => ["last"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", - exp_name => "match", + exp_links => ["match", "before"], + not_exp_links => ["matches-but-is-negated"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", - exp_name => "not-anything", + exp_links => ["before", "not-anything"], + not_exp_links => ["matches-but-is-negated"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", - exp_name => "nonzero-program", + exp_links => ["before", "nonzero-program"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", - exp_name => "true", + exp_links => ["true"], + not_exp_links => ["bad", "wrong"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", - exp_name => "true", + exp_links => ["true"], + not_exp_links => ["bad", "wrong", "no"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", - exp_name => "true", + exp_links => ["true", "before"], + not_exp_links => ["no"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", - exp_name => "true", + exp_links => ["true", "before"], + not_exp_links => ["no", "bad"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", - exp_name => "part", - }], + exp_links => ["part"], + not_exp_links => ["disk"], + }, + ], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", - exp_name => "sane", + exp_links => ["sane"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", - exp_name => "uber", + exp_links => ["uber"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", - exp_name => "replaced", + exp_links => ["replaced"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "serial-354172020305000", + exp_links => ["serial-354172020305000"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "ok", + exp_links => ["ok"], + not_exp_links => ["not-1-ok", "not-2-ok", "not-3-ok"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "ok", + exp_links => ["ok"], + not_exp_links => ["unknown-not-ok"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "ok", + exp_links => ["ok"], exp_perms => "root:tty:0640", }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "ok", + exp_links => ["ok"], exp_perms => "root:tty:0640", }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "node-add-me", + exp_links => ["node-add-me"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "three", - not_exp_name => "two", + exp_links => ["three"], + not_exp_links => ["two", "one"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "right", - not_exp_name => "wrong", + exp_links => ["right"], + not_exp_links => ["wrong"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "right", + exp_links => ["right", "before"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "right", + exp_links => ["right", "before"], + not_exp_links => ["nomatch"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "right", + exp_links => ["right"], + not_exp_links => ["nomatch", "wrong1", "wrong2"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "right", + exp_links => ["right"], + not_exp_links => ["nomatch", "wrong1", "wrong2", "wrong3"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "parent", + exp_links => ["parent"], }], option => "keep", rules => < [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", - exp_name => "parentenv-parent_right", + exp_links => ["parentenv-parent_right"], }], option => "clean", rules => < [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", - exp_name => "right", + exp_links => ["right"], + not_exp_test => ["wrong", "wrong2"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", - exp_name => "right", + exp_links => ["right"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", - exp_name => "right", - not_exp_name => "wrong", + exp_links => ["right", "link"], + not_exp_links => ["wrong"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", - exp_name => "yes", + exp_links => ["yes"], + not_exp_links => ["no"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "yes", + exp_links => ["yes"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", - exp_name => "yes", + exp_links => ["yes"], + not_exp_links => ["no"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "00:16:41:e2:8d:ff", + exp_links => ["00:16:41:e2:8d:ff"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "there", + exp_links => ["there"], + not_exp_links => ["notthere"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "yes", + exp_links => ["yes"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "relative", + exp_links => ["relative"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "found-subdir", + exp_links => ["found-subdir"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "sda-8741C4G-end", + exp_links => ["sda-8741C4G-end"], exp_perms => "0:0:0600", }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "disk/by-path/pci-0000:00:1f.2-scsi-0:0:0:0", + exp_links => ["disk/by-path/pci-0000:00:1f.2-scsi-0:0:0:0"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "found", - not_exp_name => "bad" , + exp_links => ["found"], + not_exp_links => ["bad"], }], rules => < [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "found", + exp_links => ["found"], }], rules => $rules_10k_tags . <{not_exp_name})) { - if ((-e "$udev_dev/$device->{not_exp_name}") || - (-l "$udev_dev/$device->{not_exp_name}")) { - print "nonexistent: error \'$device->{not_exp_name}\' not expected to be there\n"; - $error++; - sleep(1); - } - } +sub get_link_target { + my ($link) = @_; - my $devnode = check_devnode($device); + my $cwd = getcwd(); + my $dir = "$udev_dev/$link"; + my $tgt = readlink("$udev_dev/$link"); + $dir =~ s!/[^/]*$!!; + $tgt = abs_path("$dir/$tgt"); + $tgt =~ s!^$cwd/!!; + return $tgt; +} - return if (!defined($device->{exp_name})); +sub check_link_add { + my ($link, $devnode, $err_expected) = @_; - my @st = lstat("$udev_dev/$device->{exp_name}"); + my @st = lstat("$udev_dev/$link"); if (-l _) { - my $cwd = getcwd(); - my $dir = "$udev_dev/$device->{exp_name}"; - $dir =~ s!/[^/]*$!!; - my $tgt = readlink("$udev_dev/$device->{exp_name}"); - $tgt = abs_path("$dir/$tgt"); - $tgt =~ s!^$cwd/!!; + my $tgt = get_link_target($link); if ($tgt ne $devnode) { - print "symlink $device->{exp_name}: error, found -> $tgt\n"; + print "symlink $link: error, found -> $tgt\n"; $error++; system("tree", "$udev_dev"); } else { - print "symlink $device->{exp_name}: ok\n"; + print "symlink $link: ok\n"; } } else { - print "symlink $device->{exp_name}: error"; - if ($device->{exp_add_error}) { + print "symlink $link: error"; + if ($err_expected) { print " as expected\n"; } else { print "\n"; @@ -2089,6 +2135,49 @@ sub check_add { } } +sub check_link_nonexistent { + my ($link, $devnode, $err_expected) = @_; + + if ((-e "$udev_dev/$link") || (-l "$udev_dev/$link")) { + my $tgt = get_link_target($link); + + if ($tgt ne $devnode) { + print "nonexistent: '$link' points to other device (ok)\n"; + } else { + print "nonexistent: error \'$link\' should not be there"; + if ($err_expected) { + print " (as expected)\n"; + } else { + print "\n"; + system("tree", "$udev_dev"); + print "\n"; + $error++; + sleep(1); + } + } + } else { + print "nonexistent $link: ok\n"; + } +} + +sub check_add { + my ($device) = @_; + my $devnode = check_devnode($device); + + if (defined($device->{exp_links})) { + foreach my $link (@{$device->{exp_links}}) { + check_link_add($link, $devnode, + $device->{exp_add_error}); + } + } + if (defined $device->{not_exp_links}) { + foreach my $link (@{$device->{not_exp_links}}) { + check_link_nonexistent($link, $devnode, + $device->{exp_nodev_error}); + } + } +} + sub check_remove_devnode { my ($device) = @_; my $devnode = get_devnode($device); @@ -2105,17 +2194,13 @@ sub check_remove_devnode { } } -sub check_remove { - my ($device) = @_; +sub check_link_remove { + my ($link, $err_expected) = @_; - check_remove_devnode($device); - - return if (!defined($device->{exp_name})); - - if ((-e "$udev_dev/$device->{exp_name}") || - (-l "$udev_dev/$device->{exp_name}")) { - print "remove $device->{exp_name}: error"; - if ($device->{exp_rem_error}) { + if ((-e "$udev_dev/$link") || + (-l "$udev_dev/$link")) { + print "remove $link: error"; + if ($err_expected) { print " as expected\n"; } else { print "\n"; @@ -2125,7 +2210,19 @@ sub check_remove { sleep(1); } } else { - print "remove $device->{exp_name}: ok\n"; + print "remove $link: ok\n"; + } +} + +sub check_remove { + my ($device) = @_; + + check_remove_devnode($device); + + return if (!defined($device->{exp_links})); + + foreach my $link (@{$device->{exp_links}}) { + check_link_remove($link, $device->{exp_rem_error}); } } From 9456d957cd453dd3fd808bc725e4dbef7071556d Mon Sep 17 00:00:00 2001 From: Martin Wilck Date: Tue, 24 Apr 2018 17:57:47 +0200 Subject: [PATCH 09/25] test/udev-test.pl: fix wrong test descriptions udev hasn't supported renaming device nodes for some time. --- test/udev-test.pl | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/test/udev-test.pl b/test/udev-test.pl index 539032bc254..beed4124fc2 100755 --- a/test/udev-test.pl +++ b/test/udev-test.pl @@ -207,7 +207,7 @@ EOF }, { - desc => "replace kernel name", + desc => "don't replace kernel name", devices => [ { devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", @@ -218,7 +218,7 @@ EOF }, { - desc => "Handle comment lines in config file (and replace kernel name)", + desc => "Handle comment lines in config file (and don't replace kernel name)", devices => [ { devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", @@ -231,7 +231,7 @@ EOF }, { - desc => "Handle comment lines in config file with whitespace (and replace kernel name)", + desc => "Handle comment lines in config file with whitespace (and don't replace kernel name)", devices => [ { devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", @@ -244,7 +244,7 @@ EOF }, { - desc => "Handle whitespace only lines (and replace kernel name)", + desc => "Handle whitespace only lines (and don't replace kernel name)", devices => [ { devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", @@ -262,7 +262,7 @@ EOF }, { - desc => "Handle empty lines in config file (and replace kernel name)", + desc => "Handle empty lines in config file (and don't replace kernel name)", devices => [ { devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", @@ -275,7 +275,7 @@ EOF }, { - desc => "Handle backslashed multi lines in config file (and replace kernel name)", + desc => "Handle backslashed multi lines in config file (and don't replace kernel name)", devices => [ { devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", @@ -298,8 +298,9 @@ KERNEL=="ttyACM0", PROGRAM=="/bin/echo -e \\101", RESULT=="A", SYMLINK+="aaa" EOF }, + # 20 { - desc => "Handle stupid backslashed multi lines in config file (and replace kernel name)", + desc => "Handle stupid backslashed multi lines in config file (and don't replace kernel name)", devices => [ { devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", From a5fa208dfd2dcc65a729cb0ab368cb4691622108 Mon Sep 17 00:00:00 2001 From: Martin Wilck Date: Tue, 24 Apr 2018 18:08:18 +0200 Subject: [PATCH 10/25] test/udev-test.pl: last_rule is unsupported the "last_rule" option hasn't been supported for some time. Therefore this test fails if a "not_exp_links" attribute is added, as it should be. Mark it appropriately. --- test/udev-test.pl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/udev-test.pl b/test/udev-test.pl index beed4124fc2..38bd552a6f1 100755 --- a/test/udev-test.pl +++ b/test/udev-test.pl @@ -1368,11 +1368,14 @@ EOF }, { + # This is not supported any more desc => "last_rule option", devices => [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", exp_links => ["last"], + not_exp_links => ["very-last"], + exp_nodev_error => "yes", }], rules => < Date: Tue, 24 Apr 2018 18:09:50 +0200 Subject: [PATCH 11/25] test/udev-test.pl: Make some tests a little harder Add some rules that make it a bit harder to pass, mainly the non-existence checks. --- test/udev-test.pl | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/test/udev-test.pl b/test/udev-test.pl index 38bd552a6f1..373d9265c14 100755 --- a/test/udev-test.pl +++ b/test/udev-test.pl @@ -1353,7 +1353,7 @@ exp_links => ["sda-part-1"], }], rules => < ["part"], not_exp_links => ["disk"], }, + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", + exp_links => ["disk"], + not_exp_links => ["part"], + }, ], rules => < < < "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", exp_perms => "1:1:0400", - exp_rem_error => "yes", }], rules => < Date: Tue, 24 Apr 2018 18:16:59 +0200 Subject: [PATCH 12/25] test/udev-test.pl: remove bogus rules from magic subsys test These rules have survived from an ancient version of the code and save no purpose any more. --- test/udev-test.pl | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/udev-test.pl b/test/udev-test.pl index 373d9265c14..4dddc4bf5c7 100755 --- a/test/udev-test.pl +++ b/test/udev-test.pl @@ -1914,8 +1914,6 @@ exp_perms => "0:0:0600", }], rules => < Date: Tue, 24 Apr 2018 18:27:25 +0200 Subject: [PATCH 13/25] test/udev-test.pl: merge "space and var with space" tests As we can check multiple links in a single test now, these 3 tests can be merged into one. --- test/udev-test.pl | 31 +++---------------------------- 1 file changed, 3 insertions(+), 28 deletions(-) diff --git a/test/udev-test.pl b/test/udev-test.pl index 4dddc4bf5c7..f96ba79847c 100755 --- a/test/udev-test.pl +++ b/test/udev-test.pl @@ -1098,34 +1098,9 @@ devices => [ { devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_links => ["first"], - not_exp_links => [" "], - }], - rules => < "symlink with space and var with space, part 2", - devices => [ - { - devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_links => ["name-one_two_three-end"], - not_exp_links => [" "], - }], - rules => < "symlink with space and var with space, part 3", - devices => [ - { - devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_links => ["another_symlink"], - not_exp_links => [" "], + exp_links => ["first", "name-one_two_three-end", + "another_symlink", "a", "b", "c"], + not_exp_links => [" "], }], rules => < Date: Tue, 24 Apr 2018 18:30:09 +0200 Subject: [PATCH 14/25] test/udev-test.pl: merge import parent tests into one As we can test multiple devices and multiple links per device in one test now, these two tests can be merged into one. --- test/udev-test.pl | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/test/udev-test.pl b/test/udev-test.pl index f96ba79847c..7b8c176e264 100755 --- a/test/udev-test.pl +++ b/test/udev-test.pl @@ -1678,28 +1678,21 @@ EOF }, { - desc => "IMPORT parent test sequence 1/2 (keep)", + desc => "IMPORT parent test", devices => [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", exp_links => ["parent"], - }], - option => "keep", - rules => < "IMPORT parent test sequence 2/2 (keep)", - devices => [ + }, { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", exp_links => ["parentenv-parent_right"], }], - option => "clean", + sleep_us => 500000, # Serialized! We need to sleep here after adding sda rules => < Date: Tue, 24 Apr 2018 20:55:01 +0200 Subject: [PATCH 15/25] test/udev-test.pl: count "good" results This is helpful to catch possible regressions in the test. Also, don't count wait() errors, they are likely not udev errors. --- test/udev-test.pl | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/test/udev-test.pl b/test/udev-test.pl index 7b8c176e264..797b3d62034 100755 --- a/test/udev-test.pl +++ b/test/udev-test.pl @@ -1949,6 +1949,7 @@ sub udev { } my $error = 0; +my $good = 0; sub permissions_test { my($rules, $uid, $gid, $mode) = @_; @@ -1979,6 +1980,7 @@ sub permissions_test { } if ($wrong == 0) { print "permissions: ok\n"; + $good++; } else { printf " expected permissions are: %s:%s:%#o\n", $1, $2, oct($3); printf " created permissions are : %i:%i:%#o\n", $uid, $gid, $mode & 07777; @@ -2004,6 +2006,7 @@ sub major_minor_test { } if ($wrong == 0) { print "major:minor: ok\n"; + $good++; } else { printf " expected major:minor is: %i:%i\n", $1, $2; printf " created major:minor is : %i:%i\n", $major, $minor; @@ -2067,6 +2070,7 @@ sub check_devnode { major_minor_test($device, $rdev); } print "add $devnode: ok\n"; + $good++; return $devnode; } @@ -2095,11 +2099,13 @@ sub check_link_add { system("tree", "$udev_dev"); } else { print "symlink $link: ok\n"; + $good++; } } else { print "symlink $link: error"; if ($err_expected) { print " as expected\n"; + $good++; } else { print "\n"; system("tree", "$udev_dev"); @@ -2118,10 +2124,12 @@ sub check_link_nonexistent { if ($tgt ne $devnode) { print "nonexistent: '$link' points to other device (ok)\n"; + $good++; } else { print "nonexistent: error \'$link\' should not be there"; if ($err_expected) { print " (as expected)\n"; + $good++; } else { print "\n"; system("tree", "$udev_dev"); @@ -2132,6 +2140,7 @@ sub check_link_nonexistent { } } else { print "nonexistent $link: ok\n"; + $good++; } } @@ -2166,6 +2175,7 @@ sub check_remove_devnode { sleep(1); } else { print "remove $devnode: ok\n"; + $good++; } } @@ -2177,6 +2187,7 @@ sub check_link_remove { print "remove $link: error"; if ($err_expected) { print " as expected\n"; + $good++; } else { print "\n"; system("tree", "$udev_dev"); @@ -2186,6 +2197,7 @@ sub check_link_remove { } } else { print "remove $link: ok\n"; + $good++; } } @@ -2245,7 +2257,6 @@ sub fork_and_run_udev { $pid = waitpid($dev->{pid}, 0); if ($pid == -1) { print "error waiting for pid dev->{pid}\n"; - $error += 1; } if (WIFEXITED($?)) { $rc = WEXITSTATUS($?); @@ -2253,6 +2264,8 @@ sub fork_and_run_udev { if ($rc) { print "$udev_bin $action for $dev->{devpath} failed with code $rc\n"; $error += 1; + } else { + $good++; } } } @@ -2353,7 +2366,7 @@ sub run_test { } $sema->remove; -print "$error errors occurred\n\n"; +print "$error errors occurred. $good good results.\n\n"; # cleanup system("rm", "-rf", "$udev_run"); From ab0472566c43bd290b9fecda46976932ff88fb2e Mon Sep 17 00:00:00 2001 From: Martin Wilck Date: Tue, 24 Apr 2018 21:40:23 +0200 Subject: [PATCH 16/25] test/sys-script.py: add missing DEVNAME entries to uevents --- test/sys-script.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/sys-script.py b/test/sys-script.py index 6c9ee5ff834..0340bbe398b 100755 --- a/test/sys-script.py +++ b/test/sys-script.py @@ -11688,6 +11688,7 @@ def f(path, mode, contents): f('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:0/block/sdb/uevent', 0o644, b'''MAJOR=8 MINOR=16 DEVTYPE=disk +DEVNAME=sdb ''') d('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:0/block/sdb/queue', 0o755) l('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:0/block/sdb/queue/bsg', '../../../bsg/7:0:0:0') @@ -11720,6 +11721,7 @@ def f(path, mode, contents): f('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:0/block/sdb/sdb1/uevent', 0o644, b'''MAJOR=8 MINOR=17 DEVTYPE=partition +DEVNAME=sdb1 ''') d('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:0/block/sdb/sdb1/power', 0o755) f('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:0/block/sdb/sdb1/power/wakeup', 0o644, b'\n') @@ -13161,6 +13163,7 @@ def f(path, mode, contents): f('sys/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda10/uevent', 0o644, b'''MAJOR=8 MINOR=10 DEVTYPE=partition +DEVNAME=sda10 ''') d('sys/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda10/power', 0o755) f('sys/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda10/power/wakeup', 0o644, b'\n') @@ -13174,6 +13177,7 @@ def f(path, mode, contents): f('sys/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda9/uevent', 0o644, b'''MAJOR=8 MINOR=9 DEVTYPE=partition +DEVNAME=sda9 ''') d('sys/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda9/holders', 0o755) l('sys/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda9/holders/md0', '../../../../../../../../../virtual/block/md0') @@ -13189,6 +13193,7 @@ def f(path, mode, contents): f('sys/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda7/uevent', 0o644, b'''MAJOR=8 MINOR=7 DEVTYPE=partition +DEVNAME=sda7 ''') d('sys/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda7/power', 0o755) f('sys/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda7/power/wakeup', 0o644, b'\n') @@ -13216,6 +13221,7 @@ def f(path, mode, contents): f('sys/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda8/uevent', 0o644, b'''MAJOR=8 MINOR=8 DEVTYPE=partition +DEVNAME=sda8 ''') d('sys/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda8/power', 0o755) f('sys/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda8/power/wakeup', 0o644, b'\n') @@ -13243,6 +13249,7 @@ def f(path, mode, contents): f('sys/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda6/uevent', 0o644, b'''MAJOR=8 MINOR=6 DEVTYPE=partition +DEVNAME=sda6 ''') d('sys/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda6/power', 0o755) f('sys/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda6/power/wakeup', 0o644, b'\n') From 3b984636b3dc876963a7859731f962972a5578a4 Mon Sep 17 00:00:00 2001 From: Martin Wilck Date: Tue, 24 Apr 2018 22:04:55 +0200 Subject: [PATCH 17/25] tests/udev-test.pl: add multiple device test Add 4 new tests using multiple devices. Number 2-4 use many devices claiming the same symlink, where only one device has a higher priority thatn the others. They fail sporadically with the current code, if a race condition causes the symlink to point to the wrong device. Test 4 is like test 2 with sleeps in between, it's much less likely to fail. --- test/udev-test.pl | 169 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 169 insertions(+) diff --git a/test/udev-test.pl b/test/udev-test.pl index 797b3d62034..83fd8094779 100755 --- a/test/udev-test.pl +++ b/test/udev-test.pl @@ -1920,6 +1920,175 @@ }], rules => $rules_10k_tags . < "multiple devices", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", + exp_links => ["part-1"], + }, + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", + exp_links => ["part-5"], + }, + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda6", + exp_links => ["part-6"], + }, + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda7", + exp_links => ["part-7"], + }, + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda8", + exp_links => ["part-8"], + }, + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda9", + exp_links => ["part-9"], + }, + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda10", + exp_links => ["part-10"], + }, + ], + rules => < "multiple devices, same link name, positive prio", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", + exp_links => ["part-1"], + not_exp_links => ["partition"], + }, + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", + exp_links => ["part-5"], + not_exp_links => ["partition"], + }, + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda6", + not_exp_links => ["partition"], + exp_links => ["part-6"], + }, + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda7", + exp_links => ["part-7", "partition"], + }, + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda8", + not_exp_links => ["partition"], + exp_links => ["part-8"], + }, + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda9", + not_exp_links => ["partition"], + exp_links => ["part-9"], + }, + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda10", + not_exp_links => ["partition"], + exp_links => ["part-10"], + }, + ], + rules => < "multiple devices, same link name, negative prio", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", + exp_links => ["part-1"], + not_exp_links => ["partition"], + }, + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", + exp_links => ["part-5"], + not_exp_links => ["partition"], + }, + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda6", + not_exp_links => ["partition"], + exp_links => ["part-6"], + }, + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda7", + exp_links => ["part-7", "partition"], + }, + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda8", + not_exp_links => ["partition"], + exp_links => ["part-8"], + }, + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda9", + not_exp_links => ["partition"], + exp_links => ["part-9"], + }, + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda10", + not_exp_links => ["partition"], + exp_links => ["part-10"], + }, + ], + rules => < "multiple devices, same link name, positive prio, sleep", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", + exp_links => ["part-1"], + not_exp_links => ["partition"], + }, + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", + exp_links => ["part-5"], + not_exp_links => ["partition"], + }, + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda6", + not_exp_links => ["partition"], + exp_links => ["part-6"], + }, + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda7", + exp_links => ["part-7", "partition"], + }, + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda8", + not_exp_links => ["partition"], + exp_links => ["part-8"], + }, + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda9", + not_exp_links => ["partition"], + exp_links => ["part-9"], + }, + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda10", + not_exp_links => ["partition"], + exp_links => ["part-10"], + }, + ], + sleep_us => 10000, + rules => < Date: Tue, 24 Apr 2018 22:24:43 +0200 Subject: [PATCH 18/25] test/udev-test.pl: add repeat count for easier reproduction of sporadic test failures. --- test/udev-test.pl | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/udev-test.pl b/test/udev-test.pl index 83fd8094779..f4682a146b2 100755 --- a/test/udev-test.pl +++ b/test/udev-test.pl @@ -1960,6 +1960,7 @@ }, { desc => "multiple devices, same link name, positive prio", + repeat => 100, devices => [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", @@ -2448,6 +2449,7 @@ sub run_test { print "TEST $number: $rules->{desc}\n"; create_rules(\$rules->{rules}); + REPEAT: fork_and_run_udev("add", $rules, $sema); foreach my $dev (@devices) { @@ -2466,6 +2468,9 @@ sub run_test { } print "\n"; + if (defined($rules->{repeat}) && --($rules->{repeat}) > 0) { + goto REPEAT; + } if (defined($rules->{option}) && $rules->{option} eq "clean") { udev_setup(); From 68af9be36cb49161b1990583728ad8613c073dd6 Mon Sep 17 00:00:00 2001 From: Martin Wilck Date: Wed, 25 Apr 2018 09:54:26 +0200 Subject: [PATCH 19/25] test/udev-test.pl: generator for large list of block devices Manually listing all devices in the test definition becomes cumbersome with lots of devices. Add a function that scans on all block devices in the test sysfs and generates a list of devices to test. --- test/udev-test.pl | 60 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 59 insertions(+), 1 deletion(-) diff --git a/test/udev-test.pl b/test/udev-test.pl index f4682a146b2..bf32df3fe6f 100755 --- a/test/udev-test.pl +++ b/test/udev-test.pl @@ -45,6 +45,50 @@ $rules_10k_tags .= 'KERNEL=="sda", TAG+="test' . $i . "\"\n"; } +# Create a device list with all block devices under /sys +# (except virtual devices and cd-roms) +# the optional argument exp_func returns expected and non-expected +# symlinks for the device. +sub all_block_devs { + my ($exp_func) = @_; + my @devices; + + foreach my $bd (glob "$udev_sys/dev/block/*") { + my $tgt = readlink($bd); + my ($exp, $notexp) = (undef, undef); + + next if ($tgt =~ m!/virtual/! || $tgt =~ m!/sr[0-9]*$!); + + $tgt =~ s!^\.\./\.\.!!; + ($exp, $notexp) = $exp_func->($tgt) if defined($exp_func); + my $device = { + devpath => $tgt, + exp_links => $exp, + not_exp_links => $notexp, + }; + push(@devices, $device); + } + return \@devices; +} + +# This generator returns a suitable exp_func for use with +# all_block_devs(). +sub expect_for_some { + my ($pattern, $links, $donot) = @_; + my $_expect = sub { + my ($name) = @_; + + if ($name =~ /$pattern/) { + return ($links, undef); + } elsif ($donot) { + return (undef, $links); + } else { + return (undef, undef); + } + }; + return $_expect; +} + my @tests = ( { desc => "no rules", @@ -2092,6 +2136,15 @@ KERNEL=="*7", OPTIONS+="link_priority=10" EOF }, + { + desc => 'all_block_devs', + generator => expect_for_some("\\/sda6\$", ["blockdev"]), + repeat => 10, + rules => <{devices}}; + my @devices; + + if (!defined $rules->{devices}) { + $rules->{devices} = all_block_devs($rules->{generator}); + } + @devices = @{$rules->{devices}}; print "TEST $number: $rules->{desc}\n"; create_rules(\$rules->{rules}); From e2ef663f5fb83f4ad98882cbd6d00120158bb395 Mon Sep 17 00:00:00 2001 From: Martin Wilck Date: Wed, 25 Apr 2018 17:21:49 +0200 Subject: [PATCH 20/25] test/sd-script.py: new helper script for udev testing Script for generating LOTS of SCSI disk and partition devices in the fake sysfs we use for udev testing. This script is supposed to be run after sys-script.py. It uses code from sys-script.py as template to generate additional SCSI disk data structures. Together with the "generator" code in udev-test.pl added in the previous patch, it allows to run udev tests with almost arbitrarily many devices, and thus to do performance scaling tests. --- test/sd-script.py | 338 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 338 insertions(+) create mode 100644 test/sd-script.py diff --git a/test/sd-script.py b/test/sd-script.py new file mode 100644 index 00000000000..9c8ec0b41db --- /dev/null +++ b/test/sd-script.py @@ -0,0 +1,338 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: LGPL-2.1+ +# +# sd-script.py: create LOTS of sd device entries in fake sysfs +# +# (C) 2018 Martin Wilck, SUSE Linux GmbH +# +# Run after sys-script.py +# Usage: sd-script.py +# is the number of device nodes (disks + partititions) +# to create in addition to what sys-script.py already did. +# The script can be run several times in a row if is increased, +# adding yet more device entries. +# Tested up to 1000 entries, more are possible. +# Note that sys-script.py already creates 10 sd device nodes +# (sda+sdb and partitions). This script starts with sdc. + +import re +import os +import errno +import sys + +def d(path, mode): + os.mkdir(path, mode) + +def l(path, src): + os.symlink(src, path) + +def f(path, mode, contents): + with open(path, "wb") as f: + f.write(contents) + os.chmod(path, mode) + +class SD(object): + + sd_major = [8] + list(range(65, 72)) + list(range(128, 136)) + _name_re = re.compile(r'sd(?P[a-z]*)$') + + def _init_from_name(self, name): + mt = self._name_re.match(name) + if mt is None: + raise RuntimeError("invalid name %s" % name) + nm = mt.group("f") + base = 1 + ls = nm[-1] + nm = nm[:-1] + n = base * (ord(ls)-ord('a')) + while len(nm) > 0: + ls = nm[-1] + nm = nm[:-1] + base *= 26 + n += base * (1 + ord(ls)-ord('a')) + self._num = n + + def _init_from_dev(self, dev): + maj, min = dev.split(":") + maj = self.sd_major.index(int(maj, 10)) + min = int(min, 10) + num = int(min / 16) + self._num = 16*maj + num%16 + 256*int(num/16) + + @staticmethod + def _disk_num(a, b): + n = ord(a)-ord('a') + if b != '': + n = 26 * (n+1) + ord(b)-ord('a') + return n + + @staticmethod + def _get_major(n): + return SD.sd_major[int((n%256)/16)] + @staticmethod + def _get_minor(n): + return 16 * (n % 16 + 16 * int(n/256)) + + @staticmethod + def _get_name(n): + # see sd_format_disk_name() (sd.c) + s = chr(n % 26 + ord('a')) + n = int(n / 26) - 1 + while n >= 0: + s = chr(n % 26 + ord('a')) + s + n = int(n / 26) - 1 + return "sd" + s + + @staticmethod + def _get_dev_t(n): + maj = SD._get_major(n) + min = SD._get_minor(n) + return (maj << 20) + min + + def __init__(self, arg): + if type(arg) is type(0): + self._num = arg + elif arg.startswith("sd"): + self._init_from_name(arg) + else: + self._init_from_dev(arg) + + def __cmp__(self, other): + return cmp(self._num, other._num) + + def __hash__(self): + return hash(self._num) + + def __str__(self): + return "%s/%s" % ( + self.devstr(), + self._get_name(self._num)) + + def major(self): + return self._get_major(self._num) + + def minor(self): + return self._get_minor(self._num) + + def devstr(self): + return "%d:%d" % (self._get_major(self._num), + self._get_minor(self._num)) + + def namestr(self): + return self._get_name(self._num) + + def longstr(self): + return "%d\t%s\t%s\t%08x" % (self._num, + self.devstr(), + self.namestr(), + self._get_dev_t(self._num)) + +class MySD(SD): + def subst(self, first_sg): + disk = { + "lun": self._num, + "major": self.major(), + "devnode": self.namestr(), + "disk_minor": self.minor(), + "sg_minor": first_sg + self._num, + } + return disk + +disk_template = r"""\ +l('sys/bus/scsi/drivers/sd/7:0:0:{lun}', '../../../../devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}') +l('sys/bus/scsi/devices/7:0:0:{lun}', '../../../devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}') +l('sys/dev/char/254:{sg_minor}', '../../devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/bsg/7:0:0:{lun}') +l('sys/dev/char/21:{sg_minor}', '../../devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/scsi_generic/sg{sg_minor}') +l('sys/class/scsi_disk/7:0:0:{lun}', '../../devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/scsi_disk/7:0:0:{lun}') +l('sys/class/scsi_generic/sg{sg_minor}', '../../devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/scsi_generic/sg{sg_minor}') +l('sys/class/bsg/7:0:0:{lun}', '../../devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/bsg/7:0:0:{lun}') +l('sys/class/scsi_device/7:0:0:{lun}', '../../devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/scsi_device/7:0:0:{lun}') +d('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}', 0o755) +l('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/generic', 'scsi_generic/sg{sg_minor}') +l('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/subsystem', '../../../../../../../../../bus/scsi') +l('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/driver', '../../../../../../../../../bus/scsi/drivers/sd') +f('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/iodone_cnt', 0o644, b'0xc3\n') +f('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/device_blocked', 0o644, b'0\n') +f('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/max_sectors', 0o644, b'240\n') +f('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/modalias', 0o644, b'scsi:t-0x00\n') +f('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/scsi_level', 0o644, b'3\n') +f('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/queue_depth', 0o644, b'1\n') +f('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/rev', 0o644, b'1.00\n') +f('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/type', 0o644, b'0\n') +f('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/iocounterbits', 0o644, b'32\n') +f('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/vendor', 0o644, b'Generic \n') +f('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/state', 0o644, b'running\n') +f('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/queue_type', 0o644, b'none\n') +f('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/iorequest_cnt', 0o644, b'0xc3\n') +f('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/evt_media_change', 0o644, b'0\n') +f('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/model', 0o644, b'USB Flash Drive \n') +f('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/ioerr_cnt', 0o644, b'0x2\n') +f('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/uevent', 0o644, b'''DEVTYPE=scsi_device +DRIVER=sd +MODALIAS=scsi:t-0x00 +''') +f('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/timeout', 0o644, b'60\n') +d('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/scsi_disk', 0o755) +d('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/scsi_disk/7:0:0:{lun}', 0o755) +l('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/scsi_disk/7:0:0:{lun}/subsystem', '../../../../../../../../../../../class/scsi_disk') +l('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/scsi_disk/7:0:0:{lun}/device', '../../../7:0:0:{lun}') +f('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/scsi_disk/7:0:0:{lun}/app_tag_own', 0o644, b'0\n') +f('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/scsi_disk/7:0:0:{lun}/FUA', 0o644, b'0\n') +f('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/scsi_disk/7:0:0:{lun}/cache_type', 0o644, b'write through\n') +f('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/scsi_disk/7:0:0:{lun}/protection_type', 0o644, b'0\n') +f('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/scsi_disk/7:0:0:{lun}/manage_start_stop', 0o644, b'0\n') +f('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/scsi_disk/7:0:0:{lun}/allow_restart', 0o644, b'1\n') +f('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/scsi_disk/7:0:0:{lun}/uevent', 0o644, b'') +d('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/scsi_disk/7:0:0:{lun}/power', 0o755) +f('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/scsi_disk/7:0:0:{lun}/power/wakeup', 0o644, b'\n') +d('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/power', 0o755) +f('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/power/wakeup', 0o644, b'\n') +d('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/scsi_generic', 0o755) +d('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/scsi_generic/sg{sg_minor}', 0o755) +l('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/scsi_generic/sg{sg_minor}/subsystem', '../../../../../../../../../../../class/scsi_generic') +l('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/scsi_generic/sg{sg_minor}/device', '../../../7:0:0:{lun}') +f('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/scsi_generic/sg{sg_minor}/dev', 0o644, b'21:{sg_minor}\n') +f('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/scsi_generic/sg{sg_minor}/uevent', 0o644, b'''MAJOR=21 +MINOR={sg_minor} +''') +d('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/scsi_generic/sg{sg_minor}/power', 0o755) +f('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/scsi_generic/sg{sg_minor}/power/wakeup', 0o644, b'\n') +d('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/bsg', 0o755) +d('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/bsg/7:0:0:{lun}', 0o755) +l('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/bsg/7:0:0:{lun}/subsystem', '../../../../../../../../../../../class/bsg') +l('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/bsg/7:0:0:{lun}/device', '../../../7:0:0:{lun}') +f('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/bsg/7:0:0:{lun}/dev', 0o644, b'254:{sg_minor}\n') +f('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/bsg/7:0:0:{lun}/uevent', 0o644, b'''MAJOR=254 +MINOR={sg_minor} +''') +d('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/bsg/7:0:0:{lun}/power', 0o755) +f('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/bsg/7:0:0:{lun}/power/wakeup', 0o644, b'\n') +d('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/block', 0o755) +d('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/scsi_device', 0o755) +d('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/scsi_device/7:0:0:{lun}', 0o755) +l('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/scsi_device/7:0:0:{lun}/subsystem', '../../../../../../../../../../../class/scsi_device') +l('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/scsi_device/7:0:0:{lun}/device', '../../../7:0:0:{lun}') +f('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/scsi_device/7:0:0:{lun}/uevent', 0o644, b'') +d('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/scsi_device/7:0:0:{lun}/power', 0o755) +f('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/scsi_device/7:0:0:{lun}/power/wakeup', 0o644, b'\n') +l('sys/dev/block/{major}:{disk_minor}', '../../devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/block/{devnode}') +l('sys/class/block/{devnode}', '../../devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/block/{devnode}') +l('sys/block/{devnode}', '../devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/block/{devnode}') +d('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/block/{devnode}', 0o755) +l('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/block/{devnode}/subsystem', '../../../../../../../../../../../class/block') +l('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/block/{devnode}/bdi', '../../../../../../../../../../virtual/bdi/{major}:{disk_minor}') +l('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/block/{devnode}/device', '../../../7:0:0:{lun}') +f('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/block/{devnode}/capability', 0o644, b'13\n') +f('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/block/{devnode}/ro', 0o644, b'0\n') +f('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/block/{devnode}/make-it-fail', 0o644, b'0\n') +f('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/block/{devnode}/size', 0o644, b'257024\n') +f('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/block/{devnode}/dev', 0o644, b'{major}:{disk_minor}\n') +f('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/block/{devnode}/range', 0o644, b'16\n') +f('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/block/{devnode}/removable', 0o644, b'1\n') +f('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/block/{devnode}/stat', 0o644, b' 117 409 2103 272 0 0 0 0 0 194 272\n') +f('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/block/{devnode}/uevent', 0o644, b'''MAJOR={major} +MINOR={disk_minor} +DEVTYPE=disk +DEVNAME={devnode} +''') +d('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/block/{devnode}/queue', 0o755) +l('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/block/{devnode}/queue/bsg', '../../../bsg/7:0:0:{lun}') +f('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/block/{devnode}/queue/nr_requests', 0o644, b'128\n') +f('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/block/{devnode}/queue/nomerges', 0o644, b'0\n') +f('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/block/{devnode}/queue/scheduler', 0o644, b'noop anticipatory deadline [cfq] \n') +f('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/block/{devnode}/queue/hw_sector_size', 0o644, b'512\n') +f('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/block/{devnode}/queue/max_hw_sectors_kb', 0o644, b'120\n') +f('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/block/{devnode}/queue/read_ahead_kb', 0o644, b'128\n') +f('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/block/{devnode}/queue/max_sectors_kb', 0o644, b'120\n') +d('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/block/{devnode}/queue/iosched', 0o755) +f('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/block/{devnode}/queue/iosched/slice_async_rq', 0o644, b'2\n') +f('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/block/{devnode}/queue/iosched/back_seek_max', 0o644, b'16384\n') +f('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/block/{devnode}/queue/iosched/slice_sync', 0o644, b'100\n') +f('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/block/{devnode}/queue/iosched/slice_async', 0o644, b'40\n') +f('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/block/{devnode}/queue/iosched/fifo_expire_sync', 0o644, b'125\n') +f('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/block/{devnode}/queue/iosched/slice_idle', 0o644, b'8\n') +f('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/block/{devnode}/queue/iosched/back_seek_penalty', 0o644, b'2\n') +f('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/block/{devnode}/queue/iosched/fifo_expire_async', 0o644, b'250\n') +f('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/block/{devnode}/queue/iosched/quantum', 0o644, b'4\n') +d('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/block/{devnode}/power', 0o755) +f('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/block/{devnode}/power/wakeup', 0o644, b'\n') +""" + +part_template = r"""\ +l('sys/dev/block/{major}:{part_minor}', '../../devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/block/{devnode}/{devnode}{part_num}') +l('sys/class/block/{devnode}{part_num}', '../../devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/block/{devnode}/{devnode}{part_num}') +d('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/block/{devnode}/{devnode}{part_num}', 0o755) +l('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/block/{devnode}/{devnode}{part_num}/subsystem', '../../../../../../../../../../../../class/block') +f('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/block/{devnode}/{devnode}{part_num}/start', 0o644, b'32\n') +f('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/block/{devnode}/{devnode}{part_num}/make-it-fail', 0o644, b'0\n') +f('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/block/{devnode}/{devnode}{part_num}/size', 0o644, b'256992\n') +f('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/block/{devnode}/{devnode}{part_num}/dev', 0o644, b'{major}:{part_minor}\n') +f('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/block/{devnode}/{devnode}{part_num}/stat', 0o644, b' 109 392 1903 246 0 0 0 0 0 169 246\n') +f('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}/block/{devnode}/{devnode}{part_num}/uevent', 0o644, b'''MAJOR={major} +MINOR={part_minor} +DEVTYPE=partition +DEVNAME={devnode}{part_num} +''') +""" + +if len(sys.argv) != 3: + exit("Usage: {} ".format(sys.argv[0])) + +if not os.path.isdir(sys.argv[1]): + exit("Target dir {} not found".format(sys.argv[1])) + +def create_part_sysfs(disk, sd, prt): + part = disk + part.update ({ + "part_num": prt, + "part_minor": disk["disk_minor"] + prt, + }) + + try: + exec(part_template.format(**part)) + except OSError: + si = sys.exc_info()[1] + if (si.errno == errno.EEXIST): + print("sysfs structures for %s%d exist" % (sd.namestr(), prt)) + else: + print("error for %s%d: %s" % (sd.namestr(), prt, si[1])) + raise + else: + print("sysfs structures for %s%d created" % (sd.namestr(), prt)) + +def create_disk_sysfs(dsk, first_sg, n): + sd = MySD(dsk) + disk = sd.subst(first_sg) + + try: + exec(disk_template.format(**disk)) + except OSError: + si = sys.exc_info()[1] + if (si.errno == errno.EEXIST): + print("sysfs structures for %s exist" % sd.namestr()) + else: + print("error for %s: %s" % (sd.namestr(), si[1])) + raise + else: + print("sysfs structures for %s created" % sd.namestr()) + + n += 1 + if n >= last: + return n + + for prt in range(1, 16): + create_part_sysfs(disk, sd, prt) + n += 1 + if n >= last: + return n + + return n + +os.chdir(sys.argv[1]) +n = 0 +last = int(sys.argv[2]) +first_sg = 2 +for dsk in range(2, 1000): + n = create_disk_sysfs(dsk, first_sg, n) + if n >= last: + break From ddd24ee17fe7b4568f85d89723512497da0086ed Mon Sep 17 00:00:00 2001 From: Martin Wilck Date: Thu, 26 Apr 2018 13:25:11 +0200 Subject: [PATCH 21/25] test/udev-test.pl: suppress umount error message at startup umount emits an error message "no mount point specified" if the tmpfs isn't mounted yet, which is the normal case. Suppress that by redirecting stderr. --- test/udev-test.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/udev-test.pl b/test/udev-test.pl index bf32df3fe6f..5be40a6f184 100755 --- a/test/udev-test.pl +++ b/test/udev-test.pl @@ -2240,7 +2240,7 @@ sub major_minor_test { } sub udev_setup { - system("umount", $udev_tmpfs); + system("umount \"$udev_tmpfs\" 2>/dev/null"); rmdir($udev_tmpfs); mkdir($udev_tmpfs) || die "unable to create udev_tmpfs: $udev_tmpfs\n"; system("mount", "-o", "rw,mode=755,nosuid,noexec", "-t", "tmpfs", "tmpfs", $udev_tmpfs) && die "unable to mount tmpfs"; From d73118a7cc86f8dcb6ed2f4a74ac4fbeb59f71bb Mon Sep 17 00:00:00 2001 From: Martin Wilck Date: Thu, 26 Apr 2018 14:07:27 +0200 Subject: [PATCH 22/25] test/udev_test.pl: add "expected good" count Since 'test/udev-test.pl: count "good" results', we know how many checks succeeded. Add an "expected good" count to make that number more meaningful. --- test/udev-test.pl | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/test/udev-test.pl b/test/udev-test.pl index 5be40a6f184..f77551950b1 100755 --- a/test/udev-test.pl +++ b/test/udev-test.pl @@ -2173,6 +2173,7 @@ sub udev { my $error = 0; my $good = 0; +my $exp_good = 0; sub permissions_test { my($rules, $uid, $gid, $mode) = @_; @@ -2498,12 +2499,27 @@ sub run_test { my ($rules, $number, $sema) = @_; my $rc; my @devices; + my $ntests; + my $cur_good = $good; + my $cur_error = $error; if (!defined $rules->{devices}) { $rules->{devices} = all_block_devs($rules->{generator}); } @devices = @{$rules->{devices}}; + # For each device: exit status and devnode test for add & remove + $ntests += 4 * ($#devices + 1); + foreach my $dev (@devices) { + $ntests += 2 * ($#{$dev->{exp_links}} + 1) + + ($#{$dev->{not_exp_links}} + 1) + + (defined $dev->{exp_perms} ? 1 : 0) + + (defined $dev->{exp_majorminor} ? 1 : 0); + } + if (defined $rules->{repeat}) { + $ntests *= $rules->{repeat}; + } + $exp_good += $ntests; print "TEST $number: $rules->{desc}\n"; create_rules(\$rules->{rules}); @@ -2525,10 +2541,11 @@ sub run_test { check_remove($dev); } - print "\n"; if (defined($rules->{repeat}) && --($rules->{repeat}) > 0) { goto REPEAT; } + printf "TEST $number: errors: %d good: %d/%d\n\n", $error-$cur_error, + $good-$cur_good, $ntests; if (defined($rules->{option}) && $rules->{option} eq "clean") { udev_setup(); @@ -2598,7 +2615,7 @@ sub run_test { } $sema->remove; -print "$error errors occurred. $good good results.\n\n"; +print "$error errors occurred. $good/$exp_good good results.\n\n"; # cleanup system("rm", "-rf", "$udev_run"); From b9211714af99f93d978ca6eaec8520794075c0b4 Mon Sep 17 00:00:00 2001 From: Martin Wilck Date: Thu, 5 Apr 2018 15:58:17 +0200 Subject: [PATCH 23/25] udev: lock symlink directories in link_update() When uevents are processed in parallel, in particular during coldplug, it can happen that several events for the same symlink arrive at the same time (e.g. SCSI disk and for a multipath DM device for that disk). This opens up a race window where a higher-priority link target may not be visible under /run/udev/links/ yet when a lower-priority one is processed, leading to the creation of the wrong symlink. The recently introduced "multiple symlinks" test from the udev test suite reveals this race condition, most easily if run on a system with many cores. We have to avoid this by locking the directory under /run/udev/links/. I have extensively studied alternative approaches; nothing besides locking avoids the said race condition reliably. Initial attempts to do this with systemd's make_lock_file_for() API revealed another problem: In certain situations, there may be a lot of contenders for a given symlink (for example, on various distributions, almost all GPT partitions have the partition labels "primary" or "logical" due to a bug in past parted releases - I have seen systems with literally thousands of contenders for the /dev/disk/by-partlabel/primary symlink). As of 4.18, the kernel implementation of fcntl(F_OFD_SETLKW) is inefficient, leading to busy-waiting in spin-locks and thus very bad scaling when many contending processes compete for a file lock. The kernel implementation can be improved and patches are under discussion, but it will take a long time until that kernel-side improvement is generally available. To avoid this problem, this patch doesn't use make_lock_file_for(). Rather, it uses a set of SysV semaphores, where the index into the semaphore set is derived from the symlink name using udev's built-in hash function. This yields good collision avoidance for different symlink names and performs way better than fcntl(F_OFD_SETLKW) with many contenders. --- src/udev/udev-node.c | 98 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) diff --git a/src/udev/udev-node.c b/src/udev/udev-node.c index 53cfd9c053f..cb368eec78d 100644 --- a/src/udev/udev-node.c +++ b/src/udev/udev-node.c @@ -23,6 +23,7 @@ #include #include #include +#include #include "dirent-util.h" #include "format-util.h" @@ -31,6 +32,8 @@ #include "smack-util.h" #include "stdio-util.h" #include "string-util.h" +#include "siphash24.h" +#include "hash-funcs.h" #include "udev.h" static int node_symlink(struct udev_device *dev, const char *node, const char *slink) { @@ -177,6 +180,85 @@ static const char *link_find_prioritized(struct udev_device *dev, bool add, cons return target; } +#define NSEM 1024 +static int init_link_semaphores(const char *path) +{ + key_t key = ftok(path, 0); + int semid; + + semid = semget(key, NSEM, 0600|IPC_CREAT|IPC_EXCL); + if (semid != -1) { + unsigned short val[NSEM]; + int i; + struct sembuf dummy_op[] = { + { .sem_num = 0, .sem_op = -1, .sem_flg = 0 }, + { .sem_num = 0, .sem_op = 1, .sem_flg = 0 }, + }; + + for (i = 0; i < NSEM; i++) + val[i] = 1; + if (semctl(semid, 0, SETALL, val) == -1) + return -errno; + + /* Dummy semop to set sem_otime */ + if (semop(semid, dummy_op, (sizeof(dummy_op)/sizeof(*dummy_op))) + == -1) + return -errno; + return semid; + } else { + const int RETRIES = 10; + int i; + + semid = semget(key, NSEM, 0); + if (semid == -1) + return -errno; + + for (i = 0; i < RETRIES; i++) { + struct semid_ds ds; + + /* Wait for initialization to finish */ + if (semctl(semid, 0, IPC_STAT, &ds) != -1 && + ds.sem_otime != 0) + return semid; + usleep(10000); + } + return -1; + } +} + +static unsigned short get_sema_index(const char *link) +{ + static const unsigned char seed[16] = { 0x6b, 0xb0, 0xb1, 0x28, + 0xf7, 0x8c, 0x59, 0xb2, + 0x05, 0x1d, 0xd1, 0xa2, + 0xcc, 0x12, 0xae, 0xb7 }; + struct siphash state; + uint64_t hash; + + siphash24_init(&state, seed); + path_hash_func(link, &state); + hash = siphash24_finalize(&state); + + return hash & (NSEM-1); +} + +static int _slink_semop(int semid, unsigned short semidx, + int op, const char *msg) +{ + struct sembuf sb = { .sem_num = semidx, .sem_op = op, .sem_flg = 0 }; + + if (semop(semid, &sb, 1) == -1) { + log_warning_errno(-errno, "failed to %s semaphore", msg); + return -1; + } + return 0; +} + +#define lock_slink(semid, semidx) \ + _slink_semop((semid), (semidx), -1, "acquire") +#define unlock_slink(semid, semidx) \ + _slink_semop((semid), (semidx), 1, "release") + /* manage "stack of names" with possibly specified device priorities */ static void link_update(struct udev_device *dev, const char *slink, bool add) { char name_enc[UTIL_PATH_SIZE]; @@ -184,11 +266,26 @@ static void link_update(struct udev_device *dev, const char *slink, bool add) { char dirname[UTIL_PATH_SIZE]; const char *target; char buf[UTIL_PATH_SIZE]; + static int semid = -1; + unsigned short semidx; + + if (semid == -1) { + semid = init_link_semaphores("/run/udev/links"); + if (semid == -1) { + log_error_errno(-errno, "failed to set up semaphores"); + return; + } + } + semidx = get_sema_index(slink); util_path_encode(slink + strlen("/dev"), name_enc, sizeof(name_enc)); strscpyl(dirname, sizeof(dirname), "/run/udev/links/", name_enc, NULL); strscpyl(filename, sizeof(filename), dirname, "/", udev_device_get_id_filename(dev), NULL); + mkdir_parents(dirname, 0755); + if (lock_slink(semid, semidx) == -1) + return; + if (!add && unlink(filename) == 0) rmdir(dirname); @@ -218,6 +315,7 @@ static void link_update(struct udev_device *dev, const char *slink, bool add) { err = -errno; } while (err == -ENOENT); } + unlock_slink(semid, semidx); } void udev_node_update_old_links(struct udev_device *dev, struct udev_device *dev_old) { From 8f3b1cbf8e4f0b94769844a260208b2010c295a9 Mon Sep 17 00:00:00 2001 From: Martin Wilck Date: Fri, 20 Apr 2018 11:56:57 +0200 Subject: [PATCH 24/25] udev: change structure in /run/udev/links to improve efficiency The patch "udev: lock symlink directories in link_update()" adds locking in the link_update() code path which figures out the highest-prio target for any symlink claimed by a current uevent, and does so with good general efficiency. Still, in cases with possibly thousands of contenders, the lock may be contended, and udev run time may be increased, which is undesirable. Furthermore, there is a remaining race condition between link_update() and the updating of the udev db (see below). This patch fixes both issues by changing the way udev stores information about symlinks and their priorities under /run/udev/links. Currently, udev handles symlink targets using files that look like this: /run/udev/links/${encoded_symlink_name}/${dev_id} where dev_id is "b$major:$minor" or "c$major:$minor" for block and character devices, respectively. When an event is processed, udev adds or removes these files according to event type and scans the whole directory /run/udev/links/${encoded_symlink_name} to find the entry with the highest priority. In the process, it needs to get an udev device ref for every entry and derive the priority from libudev using udev_device_get_devlink_priority(). If there are many contenders for the symlink at hand, this procedure is inefficient. Furthermore, there's also a race condition: calling udev_device_get_devlink_priority() assumes that the udev db for the device at hand is up to date, which may not be the case while the symlinks are still being processed (the db file is (re-)written after calling link_update()). (Note: This race is real, but I haven't been able to reproduce it with the previous (locking) patch applied, probably because that patch makes the race window extremely small. I did observe this race when I tried to explore alternatives to locking of the symlink directory). This patch changes the data structure as follows: /run/udev/links/${encoded_symlink_name}/L:$prio/${dev_id} Thus entries are now grouped in subdirectories by priority, and finding the highest-prio entry is much quicker because we hardly ever have more than 2 or 3 prio levels for any given symlink. An L:$prio directory may have many entries, but as we know that they all have the same prio, we can just pick the first entry and be done. Thus the time during which the lock is held is drastically reduced. The mentioned race with the udev db is avoided es well, because there's no need any more to call udev_device_get_devlink_priority() in the link_update() code path any more. This patch doesn't cause any test suite regressions. --- src/udev/udev-node.c | 237 ++++++++++++++++++++++++++++++++----------- 1 file changed, 180 insertions(+), 57 deletions(-) diff --git a/src/udev/udev-node.c b/src/udev/udev-node.c index cb368eec78d..af7eb27ca0c 100644 --- a/src/udev/udev-node.c +++ b/src/udev/udev-node.c @@ -128,56 +128,157 @@ static int node_symlink(struct udev_device *dev, const char *node, const char *s return err; } +static const char links_dirname[] = "/run/udev/links/"; +#define PRIONAME_SIZE 32 +static const char prio_prefix[] = "L:"; + +static int make_prio_name(int prio, char *buf, size_t buflen) +{ + return snprintf(buf, buflen, "%s%d", prio_prefix, prio); +} + +enum { + NO_TARGET_FOUND, + TARGET_FOUND, +}; + /* find device node of device with highest priority */ -static const char *link_find_prioritized(struct udev_device *dev, bool add, const char *stackdir, char *buf, size_t bufsize) { +static int link_find_prioritized(struct udev_device *dev, bool add, int dfd, + DIR *dir, char *buf, size_t bufsize, + const char *slink) { struct udev *udev = udev_device_get_udev(dev); - DIR *dir; - struct dirent *dent; - int priority = 0; - const char *target = NULL; + int ret, priority; + struct dirent *de; if (add) { priority = udev_device_get_devlink_priority(dev); strscpy(buf, bufsize, udev_device_get_devnode(dev)); - target = buf; + ret = TARGET_FOUND; + } else { + priority = INT_MIN; + buf[0] = '\0'; + ret = NO_TARGET_FOUND; } - dir = opendir(stackdir); - if (dir == NULL) - return target; - FOREACH_DIRENT_ALL(dent, dir, break) { - struct udev_device *dev_db; + rewinddir(dir); + FOREACH_DIRENT_ALL(de, dir, break) { + const char *name = de->d_name; + int prio = INT_MIN; - if (dent->d_name[0] == '\0') + if (name[0] == '\0') break; - if (dent->d_name[0] == '.') + if (dot_or_dot_dot(name)) continue; - log_debug("found '%s' claiming '%s'", dent->d_name, stackdir); + if (prio > priority) { + int priofd; + DIR *pdir; + struct dirent *dent; + const char *devnode; + struct udev_device *dev_db; - /* did we find ourself? */ - if (streq(dent->d_name, udev_device_get_id_filename(dev))) - continue; + priofd = openat(dfd, name, O_RDONLY|O_DIRECTORY); + /* May race with another remove */ + if (priofd == -1) + continue; + pdir = fdopendir(priofd); + if (pdir == NULL) + continue; - dev_db = udev_device_new_from_device_id(udev, dent->d_name); - if (dev_db != NULL) { - const char *devnode; + dent = readdir_no_dot(pdir); + + if (dent == NULL) { + closedir(pdir); + continue; + } + dev_db = udev_device_new_from_device_id(udev, + dent->d_name); + if (dev_db == NULL) { + closedir(pdir); + continue; + } devnode = udev_device_get_devnode(dev_db); if (devnode != NULL) { - if (target == NULL || udev_device_get_devlink_priority(dev_db) > priority) { - log_debug("'%s' claims priority %i for '%s'", - udev_device_get_syspath(dev_db), udev_device_get_devlink_priority(dev_db), stackdir); - priority = udev_device_get_devlink_priority(dev_db); - strscpy(buf, bufsize, devnode); - target = buf; - } + strscpy(buf, bufsize, devnode); + priority = prio; + ret = TARGET_FOUND; + log_debug("'%s' claims priority %i for '%s'", + udev_device_get_syspath(dev_db), prio, + slink); } udev_device_unref(dev_db); + closedir(pdir); } } - closedir(dir); - return target; + + return ret; +} + +static int create_target_entry(int dirfd, const char *prioname, + const char *filename, const char *slink) +{ + int priofd; + int ret = 0; + + mkdirat(dirfd, prioname, 0755); + priofd = openat(dirfd, prioname, O_RDONLY|O_DIRECTORY); + if (priofd == -1) { + ret = -1; + goto out; + } + + if (symlinkat(".", priofd, filename) == 0) + log_debug("added target %s/%s for %s", + prioname, filename, slink); + else if (errno != EEXIST) + ret = -1; + close(priofd); + +out: + if (ret == -1) + log_error_errno(-errno, + "failed to add target %s/%s for %s", + prioname, filename, filename); + return ret; +} + +static int delete_target_entry(int dirfd, const char *prioname, + const char *filename, const char *slink) +{ + int priofd; + int ret = 0, r; + + priofd = openat(dirfd, prioname, O_RDONLY|O_DIRECTORY); + if (priofd == -1) { + if (errno == ENOENT) + return 0; + else { + ret = -1; + goto out; + } + } + + r = unlinkat(priofd, filename, 0); + if (r == 0) + log_debug("removed target %s/%s for %s", + prioname, filename, slink); + else if (errno != ENOENT) + ret = -1; + + r = unlinkat(dirfd, prioname, AT_REMOVEDIR); + if (r == 0) + log_debug("removed last target for %s in %s", + slink, prioname); + else if (errno != ENOTEMPTY && errno != ENOENT) + log_warning_errno(-errno, "failed to rmdir %s for %s", + prioname, slink); + close(priofd); +out: + if (ret == -1) + log_error("failed to remove target %s/%s for %s", + prioname, filename, slink); + return ret; } #define NSEM 1024 @@ -262,10 +363,12 @@ static int _slink_semop(int semid, unsigned short semidx, /* manage "stack of names" with possibly specified device priorities */ static void link_update(struct udev_device *dev, const char *slink, bool add) { char name_enc[UTIL_PATH_SIZE]; - char filename[UTIL_PATH_SIZE * 2]; char dirname[UTIL_PATH_SIZE]; - const char *target; char buf[UTIL_PATH_SIZE]; + char prioname[PRIONAME_SIZE]; + const char *filename; + int r, links_fd, dfd, priority; + DIR *dir = NULL; static int semid = -1; unsigned short semidx; @@ -278,44 +381,64 @@ static void link_update(struct udev_device *dev, const char *slink, bool add) { } semidx = get_sema_index(slink); + mkdir_p(links_dirname, 0755); + links_fd = open(links_dirname, O_RDONLY|O_DIRECTORY); + if (links_fd == -1) { + log_error_errno(-errno, "failed to open %s", dirname); + return; + } + util_path_encode(slink + strlen("/dev"), name_enc, sizeof(name_enc)); - strscpyl(dirname, sizeof(dirname), "/run/udev/links/", name_enc, NULL); - strscpyl(filename, sizeof(filename), dirname, "/", udev_device_get_id_filename(dev), NULL); + strscpyl(dirname, sizeof(dirname), links_dirname, "/", name_enc, NULL); + priority = udev_device_get_devlink_priority(dev); + make_prio_name(priority, prioname, sizeof(prioname)); + filename = udev_device_get_id_filename(dev); + + if (add) { + mkdirat(links_fd, name_enc, 0755); + dfd = openat(links_fd, name_enc, O_RDONLY|O_DIRECTORY); + if (dfd == -1) { + log_error_errno(-errno, "failed to open %s", dirname); + goto out; + } + create_target_entry(dfd, prioname, filename, slink); + } else { + dfd = openat(links_fd, name_enc, O_RDONLY|O_DIRECTORY); + if (dfd == -1 && errno != ENOENT) { + log_error_errno(-errno, "failed to open %s", dirname); + goto out; + } + delete_target_entry(dfd, prioname, filename, slink); + } + + dir = fdopendir(dfd); + if (dir == NULL) { + close(dfd); + goto out; + } mkdir_parents(dirname, 0755); if (lock_slink(semid, semidx) == -1) - return; - - if (!add && unlink(filename) == 0) - rmdir(dirname); + goto out_dir; - target = link_find_prioritized(dev, add, dirname, buf, sizeof(buf)); - if (target == NULL) { + r = link_find_prioritized(dev, add, dfd, dir, + buf, sizeof(buf), slink); + if (r == NO_TARGET_FOUND) { log_debug("no reference left, remove '%s'", slink); if (unlink(slink) == 0) rmdir_parents(slink, "/"); } else { - log_debug("creating link '%s' to '%s'", slink, target); - node_symlink(dev, target, slink); + log_debug("creating link '%s' to '%s'", slink, buf); + mkdir_parents(slink, 0755); + node_symlink(dev, buf, slink); } - if (add) { - int err; - - do { - int fd; - - err = mkdir_parents(filename, 0755); - if (err != 0 && err != -ENOENT) - break; - fd = open(filename, O_WRONLY|O_CREAT|O_CLOEXEC|O_TRUNC|O_NOFOLLOW, 0444); - if (fd >= 0) - close(fd); - else - err = -errno; - } while (err == -ENOENT); - } unlock_slink(semid, semidx); + +out_dir: + closedir(dir); +out: + close(links_fd); } void udev_node_update_old_links(struct udev_device *dev, struct udev_device *dev_old) { From b68d1049a7d64ebb7bb30591d6123eebd8f0b5d9 Mon Sep 17 00:00:00 2001 From: Martin Wilck Date: Mon, 9 Jul 2018 13:59:09 +0200 Subject: [PATCH 25/25] udev: Add migration code for /run/udev/links If udevd is restarted after applying the changes for /run/udev/links from "udev: change structure in /run/udev/links to improve efficiency", it may find the old data structures left over from a previous version. Deal with this situation by detecting it and moving the target files to the new places. --- src/udev/udev-node.c | 116 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) diff --git a/src/udev/udev-node.c b/src/udev/udev-node.c index af7eb27ca0c..87bcf38f667 100644 --- a/src/udev/udev-node.c +++ b/src/udev/udev-node.c @@ -137,9 +137,43 @@ static int make_prio_name(int prio, char *buf, size_t buflen) return snprintf(buf, buflen, "%s%d", prio_prefix, prio); } +static bool is_prio_name(const char *name, int *priority) +{ + int len = sizeof(prio_prefix) - 1; + long prio; + char *e; + + if (name == NULL || + strncmp(name, prio_prefix, len) || name[len] == '\0') + return false; + + prio = strtol(name + len, &e, 10); + if (*e != '\0' || prio < INT_MIN || prio >INT_MAX) + return false; + + *priority = prio; + return true; +} + +static bool is_prio_dirent(DIR *dir, struct dirent *de, int *priority) +{ + int prio; + + if (!is_prio_name(de->d_name, &prio)) + return false; + + dirent_ensure_type(dir, de); + if (de->d_type != DT_DIR) + return false; + + *priority = prio; + return true; +} + enum { NO_TARGET_FOUND, TARGET_FOUND, + TARGET_NEEDS_CLEANUP, }; /* find device node of device with highest priority */ @@ -170,6 +204,11 @@ static int link_find_prioritized(struct udev_device *dev, bool add, int dfd, if (dot_or_dot_dot(name)) continue; + if (!is_prio_dirent(dir, de, &prio)) { + ret = TARGET_NEEDS_CLEANUP; + break; + } + if (prio > priority) { int priofd; DIR *pdir; @@ -249,6 +288,8 @@ static int delete_target_entry(int dirfd, const char *prioname, int priofd; int ret = 0, r; + unlinkat(dirfd, filename, 0); + priofd = openat(dirfd, prioname, O_RDONLY|O_DIRECTORY); if (priofd == -1) { if (errno == ENOENT) @@ -360,6 +401,74 @@ static int _slink_semop(int semid, unsigned short semidx, #define unlock_slink(semid, semidx) \ _slink_semop((semid), (semidx), 1, "release") +static int cleanup_filter(const struct dirent *de) +{ + /* + * can't use is_prio_dirent() here, because it needs to call + * dirent_ensure_type() + */ + return !dot_or_dot_dot(de->d_name); +} + +static void cleanup_old_targets(const char *dirname, struct udev_device *dev) +{ + struct udev *udev = udev_device_get_udev(dev); + struct dirent **darr; + int n; + int dfd = -1; + DIR *dir; + + log_info("migrating symlink targets in %s", dirname); + + /* Use scandir here to avoid races with deleting entries */ + n = scandir(dirname, &darr, cleanup_filter, alphasort); + if (n < 0) { + log_error_errno(-errno, "error scanning %s", dirname); + return; + } + if (n == 0) + return; + + dir = opendir(dirname); + if (dir != NULL) + dfd = dirfd(dir); + if (dfd == -1) { + log_error_errno(-errno, "error opening %s", dirname); + return; + } + + while (n--) { + struct dirent *de = darr[n]; + struct udev_device *ud; + int prio, r; + char prioname[PRIONAME_SIZE]; + + if (is_prio_dirent(dir, de, &prio)) + continue; + /* is_prio_dirent() called dirent_ensure_type() */ + r = unlinkat(dfd, de->d_name, + de->d_type == DT_DIR ? AT_REMOVEDIR : 0); + if (r == 0) + log_debug("removed %s/%s", dirname, de->d_name); + else + log_error_errno(-errno, "failed to remove %s/%s", + dirname, de->d_name); + + ud = udev_device_new_from_device_id(udev, de->d_name); + if (ud == NULL) + continue; + + prio = udev_device_get_devlink_priority(ud); + udev_device_unref(ud); + + make_prio_name(prio, prioname, sizeof(prioname)); + create_target_entry(dfd, prioname, de->d_name, dirname); + } + + closedir(dir); + free(darr); +} + /* manage "stack of names" with possibly specified device priorities */ static void link_update(struct udev_device *dev, const char *slink, bool add) { char name_enc[UTIL_PATH_SIZE]; @@ -423,6 +532,13 @@ static void link_update(struct udev_device *dev, const char *slink, bool add) { r = link_find_prioritized(dev, add, dfd, dir, buf, sizeof(buf), slink); + if (r == TARGET_NEEDS_CLEANUP) { + cleanup_old_targets(dirname, dev); + r = link_find_prioritized(dev, add, dfd, dir, + buf, sizeof(buf), slink); + /* A single cleanup must be enough */ + assert(r != TARGET_NEEDS_CLEANUP); + } if (r == NO_TARGET_FOUND) { log_debug("no reference left, remove '%s'", slink); if (unlink(slink) == 0)