diff --git a/backport-basic-stat-util-make-mtime-check-stricter-and-use-en.patch b/backport-basic-stat-util-make-mtime-check-stricter-and-use-en.patch new file mode 100644 index 0000000..b61423c --- /dev/null +++ b/backport-basic-stat-util-make-mtime-check-stricter-and-use-en.patch @@ -0,0 +1,34 @@ +From a59b0a9f768f6e27b25f4f1bab6de08842e78d74 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Michal=20Sekleta=CC=81r?= +Date: Thu, 5 Nov 2020 17:55:25 +0100 +Subject: [PATCH] basic/stat-util: make mtime check stricter and use entire + timestamp + +Note that st_mtime member of struct stat is defined as follows, + + #define st_mtime st_mtim.tv_sec + +Hence we omitted checking nanosecond part of the timestamp (struct +timespec) and possibly would miss modifications that happened within the +same second. +--- + src/basic/stat-util.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/src/basic/stat-util.c b/src/basic/stat-util.c +index 4595ba7..b44d75c 100644 +--- a/src/basic/stat-util.c ++++ b/src/basic/stat-util.c +@@ -394,7 +394,8 @@ bool stat_inode_unmodified(const struct stat *a, const struct stat *b) { + return a && b && + (a->st_mode & S_IFMT) != 0 && /* We use the check for .st_mode if the structure was ever initialized */ + ((a->st_mode ^ b->st_mode) & S_IFMT) == 0 && /* same inode type */ +- a->st_mtime == b->st_mtime && ++ a->st_mtim.tv_sec == b->st_mtim.tv_sec && ++ a->st_mtim.tv_nsec == b->st_mtim.tv_nsec && + (!S_ISREG(a->st_mode) || a->st_size == b->st_size) && /* if regular file, compare file size */ + a->st_dev == b->st_dev && + a->st_ino == b->st_ino && +-- +2.23.0 + diff --git a/backport-stat-util-add-stat_inode_unmodified-helper-that-chec.patch b/backport-stat-util-add-stat_inode_unmodified-helper-that-chec.patch new file mode 100644 index 0000000..c901844 --- /dev/null +++ b/backport-stat-util-add-stat_inode_unmodified-helper-that-chec.patch @@ -0,0 +1,53 @@ +From fee5c52ac260d021466c1062499f0ebd5241db5f Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Tue, 28 Apr 2020 18:16:25 +0200 +Subject: [PATCH] stat-util: add stat_inode_unmodified() helper that checks if + an inode was modified + +--- + src/basic/stat-util.c | 21 +++++++++++++++++++++ + src/basic/stat-util.h | 2 ++ + 2 files changed, 23 insertions(+) + +diff --git a/src/basic/stat-util.c b/src/basic/stat-util.c +index 2cd722c..4595ba7 100644 +--- a/src/basic/stat-util.c ++++ b/src/basic/stat-util.c +@@ -379,3 +379,24 @@ int device_path_parse_major_minor(const char *path, mode_t *ret_mode, dev_t *ret + + return 0; + } ++ ++bool stat_inode_unmodified(const struct stat *a, const struct stat *b) { ++ ++ /* Returns if the specified stat structures reference the same, unmodified inode. This check tries to ++ * be reasonably careful when detecting changes: we check both inode and mtime, to cater for file ++ * systems where mtimes are fixed to 0 (think: ostree/nixos type installations). We also check file ++ * size, backing device, inode type and if this refers to a device not the major/minor. ++ * ++ * Note that we don't care if file attributes such as ownership or access mode change, this here is ++ * about contents of the file. The purpose here is to detect file contents changes, and nothing ++ * else. */ ++ ++ return a && b && ++ (a->st_mode & S_IFMT) != 0 && /* We use the check for .st_mode if the structure was ever initialized */ ++ ((a->st_mode ^ b->st_mode) & S_IFMT) == 0 && /* same inode type */ ++ a->st_mtime == b->st_mtime && ++ (!S_ISREG(a->st_mode) || a->st_size == b->st_size) && /* if regular file, compare file size */ ++ a->st_dev == b->st_dev && ++ a->st_ino == b->st_ino && ++ (!(S_ISCHR(a->st_mode) || S_ISBLK(a->st_mode)) || a->st_rdev == b->st_rdev); /* if device node, also compare major/minor, because we can */ ++} +diff --git a/src/basic/stat-util.h b/src/basic/stat-util.h +index 7824af3..3665059 100644 +--- a/src/basic/stat-util.h ++++ b/src/basic/stat-util.h +@@ -87,3 +87,5 @@ int fd_verify_directory(int fd); + int device_path_make_major_minor(mode_t mode, dev_t devno, char **ret); + int device_path_make_canonical(mode_t mode, dev_t devno, char **ret); + int device_path_parse_major_minor(const char *path, mode_t *ret_mode, dev_t *ret_devno); ++ ++bool stat_inode_unmodified(const struct stat *a, const struct stat *b); +-- +2.23.0 + diff --git a/backport-udev-make-algorithm-that-selects-highest-priority-de.patch b/backport-udev-make-algorithm-that-selects-highest-priority-de.patch new file mode 100644 index 0000000..139d3e3 --- /dev/null +++ b/backport-udev-make-algorithm-that-selects-highest-priority-de.patch @@ -0,0 +1,174 @@ +From 30f6dce62cb3a738b20253f2192270607c31b55b Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Michal=20Sekleta=CC=81r?= +Date: Fri, 23 Oct 2020 16:30:23 +0200 +Subject: [PATCH] udev: make algorithm that selects highest priority devlink + less susceptible to race conditions + +Previously it was very likely, when multiple contenders for the symlink +appear in parallel, that algorithm would select wrong symlink (i.e. one +with lower-priority). + +Now the algorithm is much more defensive and when we detect change in +set of contenders for the symlink we reevaluate the selection. Same +happens when new symlink replaces already existing symlink that points +to different device node. +--- + src/udev/udev-event.c | 7 ++++ + src/udev/udev-node.c | 75 ++++++++++++++++++++++++++++++++++--------- + 2 files changed, 67 insertions(+), 15 deletions(-) + +diff --git a/src/udev/udev-event.c b/src/udev/udev-event.c +index ae78aef..1a8becb 100644 +--- a/src/udev/udev-event.c ++++ b/src/udev/udev-event.c +@@ -1017,6 +1017,13 @@ int udev_event_execute_rules(UdevEvent *event, + if (r < 0) + return log_device_debug_errno(dev, r, "Failed to update database under /run/udev/data/: %m"); + ++ /* Yes, we run update_devnode() twice, because in the first invocation, that is before update of udev database, ++ * it could happen that two contenders are replacing each other's symlink. Hence we run it again to make sure ++ * symlinks point to devices that claim them with the highest priority. */ ++ r = update_devnode(event); ++ if (r < 0) ++ return r; ++ + device_set_is_initialized(dev); + + event->dev_db_clone = sd_device_unref(event->dev_db_clone); +diff --git a/src/udev/udev-node.c b/src/udev/udev-node.c +index ce95e20..2694185 100644 +--- a/src/udev/udev-node.c ++++ b/src/udev/udev-node.c +@@ -22,12 +22,15 @@ + #include "path-util.h" + #include "selinux-util.h" + #include "smack-util.h" ++#include "stat-util.h" + #include "stdio-util.h" + #include "string-util.h" + #include "strxcpyx.h" + #include "udev-node.h" + #include "user-util.h" + ++#define LINK_UPDATE_MAX_RETRIES 128 ++ + static int node_symlink(sd_device *dev, const char *node, const char *slink) { + _cleanup_free_ char *slink_dirname = NULL, *target = NULL; + const char *id_filename, *slink_tmp; +@@ -101,7 +104,9 @@ static int node_symlink(sd_device *dev, const char *node, const char *slink) { + if (rename(slink_tmp, slink) < 0) { + r = log_device_error_errno(dev, errno, "Failed to rename '%s' to '%s': %m", slink_tmp, slink); + (void) unlink(slink_tmp); +- } ++ } else ++ /* Tell caller that we replaced already existing symlink. */ ++ r = 1; + + return r; + } +@@ -194,7 +199,7 @@ static int link_update(sd_device *dev, const char *slink, bool add) { + _cleanup_free_ char *target = NULL, *filename = NULL, *dirname = NULL; + char name_enc[PATH_MAX]; + const char *id_filename; +- int r; ++ int i, r, retries; + + assert(dev); + assert(slink); +@@ -214,14 +219,6 @@ static int link_update(sd_device *dev, const char *slink, bool add) { + if (!add && unlink(filename) == 0) + (void) rmdir(dirname); + +- r = link_find_prioritized(dev, add, dirname, &target); +- if (r < 0) { +- log_device_debug(dev, "No reference left, removing '%s'", slink); +- if (unlink(slink) == 0) +- (void) rmdir_parents(slink, "/"); +- } else +- (void) node_symlink(dev, target, slink); +- + if (add) + do { + _cleanup_close_ int fd = -1; +@@ -234,7 +231,49 @@ static int link_update(sd_device *dev, const char *slink, bool add) { + r = -errno; + } while (r == -ENOENT); + +- return r; ++ /* If the database entry is not written yet we will just do one iteration and possibly wrong symlink ++ * will be fixed in the second invocation. */ ++ retries = sd_device_get_is_initialized(dev) > 0 ? LINK_UPDATE_MAX_RETRIES : 1; ++ ++ for (i = 0; i < retries; i++) { ++ struct stat st1 = {}, st2 = {}; ++ ++ r = stat(dirname, &st1); ++ if (r < 0 && errno != ENOENT) ++ return -errno; ++ ++ r = link_find_prioritized(dev, add, dirname, &target); ++ if (r == -ENOENT) { ++ log_device_debug(dev, "No reference left, removing '%s'", slink); ++ if (unlink(slink) == 0) ++ (void) rmdir_parents(slink, "/"); ++ ++ break; ++ } else if (r < 0) ++ return log_device_error_errno(dev, r, "Failed to determine highest priority symlink: %m"); ++ ++ r = node_symlink(dev, target, slink); ++ if (r < 0) { ++ (void) unlink(filename); ++ break; ++ } else if (r == 1) ++ /* We have replaced already existing symlink, possibly there is some other device trying ++ * to claim the same symlink. Let's do one more iteration to give us a chance to fix ++ * the error if other device actually claims the symlink with higher priority. */ ++ continue; ++ ++ /* Skip the second stat() if the first failed, stat_inode_unmodified() would return false regardless. */ ++ if ((st1.st_mode & S_IFMT) != 0) { ++ r = stat(dirname, &st2); ++ if (r < 0 && errno != ENOENT) ++ return -errno; ++ ++ if (stat_inode_unmodified(&st1, &st2)) ++ break; ++ } ++ } ++ ++ return i < LINK_UPDATE_MAX_RETRIES ? 0 : -ELOOP; + } + + int udev_node_update_old_links(sd_device *dev, sd_device *dev_old) { +@@ -442,8 +481,11 @@ int udev_node_add(sd_device *dev, bool apply, + (void) node_symlink(dev, devnode, filename); + + /* create/update symlinks, add symlinks to name index */ +- FOREACH_DEVICE_DEVLINK(dev, devlink) +- (void) link_update(dev, devlink, true); ++ FOREACH_DEVICE_DEVLINK(dev, devlink) { ++ r = link_update(dev, devlink, true); ++ if (r < 0) ++ log_device_info_errno(dev, r, "Failed to update device symlinks: %m"); ++ } + + return 0; + } +@@ -456,8 +498,11 @@ int udev_node_remove(sd_device *dev) { + assert(dev); + + /* remove/update symlinks, remove symlinks from name index */ +- FOREACH_DEVICE_DEVLINK(dev, devlink) +- (void) link_update(dev, devlink, false); ++ FOREACH_DEVICE_DEVLINK(dev, devlink) { ++ r = link_update(dev, devlink, false); ++ if (r < 0) ++ log_device_info_errno(dev, r, "Failed to update device symlinks: %m"); ++ } + + r = xsprintf_dev_num_path_from_sd_device(dev, &filename); + if (r < 0) +-- +2.23.0 + diff --git a/systemd.spec b/systemd.spec index 630d2c5..539a230 100644 --- a/systemd.spec +++ b/systemd.spec @@ -16,7 +16,7 @@ Name: systemd Url: https://www.freedesktop.org/wiki/Software/systemd Version: 243 -Release: 42 +Release: 43 License: MIT and LGPLv2+ and GPLv2+ Summary: System and Service Manager @@ -133,6 +133,9 @@ Patch0085: backport-execute-Fix-migration-from-DynamicUser-yes-to-no.patch Patch0086: 0086-fix-CVE-2021-33910.patch Patch0087: backport-units-restore-RemainAfterExit-yes-in-systemd-vconsol.patch Patch0088: backport-udevd-don-t-kill-worker-in-manager_kill_workers-when.patch +Patch0089: backport-stat-util-add-stat_inode_unmodified-helper-that-chec.patch +Patch0090: backport-basic-stat-util-make-mtime-check-stricter-and-use-en.patch +Patch0091: backport-udev-make-algorithm-that-selects-highest-priority-de.patch #openEuler Patch9002: 1509-fix-journal-file-descriptors-leak-problems.patch @@ -1519,6 +1522,12 @@ fi %exclude /usr/share/man/man3/* %changelog +* Tue Aug 03 2021 yangmingtai - 243-43 +- Type:bugfix +- ID:NA +- SUG:NA +- DESC:udevd: fix broken symbolic links when same link for different devices + * Mon Aug 02 2021 fangxiuning - 243-42 - Type:bugfix - ID:NA