diff options
author | Mike Frysinger <vapier@chromium.org> | 2022-03-30 07:32:07 +0100 |
---|---|---|
committer | Sam James <sam@gentoo.org> | 2022-04-15 03:46:19 +0100 |
commit | fa82725ba03ffa5edf0b82db7307c87fc97e83f2 (patch) | |
tree | a499d10f296d86a3e7ccac5d55c38e6a673d460f /lib/portage | |
parent | ebuild.sh: add missing IDEPEND re-sourcing (diff) | |
download | portage-fa82725ba03ffa5edf0b82db7307c87fc97e83f2.tar.gz portage-fa82725ba03ffa5edf0b82db7307c87fc97e83f2.tar.bz2 portage-fa82725ba03ffa5edf0b82db7307c87fc97e83f2.zip |
movefile: merge symlinks atomically
Since POSIX allows renaming symlinks with symlinks, use that to get
atomic updates. This is faster and less flaky to parallel processes:
removing a live symlink and then recreating it leaves a small window
where other things might try to use it but fail.
[sam: cherry-picked from chromiumos' third_party/portage_tool repo]
(cherry picked from commit cherry-pick 80ed2dac95db81acac8043e6685d0a853a08d268)
Bug: https://bugs.gentoo.org/836400
Signed-off-by: Sam James <sam@gentoo.org>
Closes: https://github.com/gentoo/portage/pull/816
Signed-off-by: Sam James <sam@gentoo.org>
Diffstat (limited to 'lib/portage')
-rw-r--r-- | lib/portage/util/movefile.py | 16 |
1 files changed, 13 insertions, 3 deletions
diff --git a/lib/portage/util/movefile.py b/lib/portage/util/movefile.py index ddafe5571..4dc08af26 100644 --- a/lib/portage/util/movefile.py +++ b/lib/portage/util/movefile.py @@ -171,7 +171,7 @@ def movefile( bsd_chflags.chflags(os.path.dirname(dest), 0) if destexists: - if stat.S_ISLNK(dstat[stat.ST_MODE]): + if not stat.S_ISLNK(sstat[stat.ST_MODE]) and stat.S_ISLNK(dstat[stat.ST_MODE]): try: os.unlink(dest) destexists = 0 @@ -185,8 +185,18 @@ def movefile( target = os.readlink(src) if mysettings and "D" in mysettings and target.startswith(mysettings["D"]): target = target[len(mysettings["D"]) - 1 :] - if destexists and not stat.S_ISDIR(dstat[stat.ST_MODE]): - os.unlink(dest) + # Atomically update the path if it exists. + try: + os.rename(src, dest) + return sstat.st_mtime_ns + except OSError: + # If it failed due to cross-link device, fallthru below. + # Clear the target first so we can create it. + try: + os.unlink(dest) + except FileNotFoundError: + pass + try: if selinux_enabled: selinux.symlink(target, dest, src) |