aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Frysinger <vapier@chromium.org>2022-03-30 07:32:07 +0100
committerSam James <sam@gentoo.org>2022-04-15 03:46:19 +0100
commitfa82725ba03ffa5edf0b82db7307c87fc97e83f2 (patch)
treea499d10f296d86a3e7ccac5d55c38e6a673d460f /lib/portage
parentebuild.sh: add missing IDEPEND re-sourcing (diff)
downloadportage-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.py16
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)