Gentoo Websites Logo
Go to: Gentoo Home Documentation Forums Lists Bugs Planet Store Wiki Get Gentoo!
Bug 836400 - portage does not merge package symlinks atomically (util.movefile claims to be atomic but is not)
Summary: portage does not merge package symlinks atomically (util.movefile claims to b...
Status: RESOLVED FIXED
Alias: None
Product: Portage Development
Classification: Unclassified
Component: Core (show other bugs)
Hardware: All Linux
: Normal normal (vote)
Assignee: Portage team
URL:
Whiteboard:
Keywords: InVCS
Depends on: 837899
Blocks: 563614 835380
  Show dependency tree
 
Reported: 2022-03-30 07:21 UTC by SpanKY
Modified: 2023-12-24 18:01 UTC (History)
2 users (show)

See Also:
Package list:
Runtime testing required: ---


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description SpanKY gentoo-dev 2022-03-30 07:21:14 UTC
from lib/portage/util/movefile.py:
def movefile(...):
  """... Move is atomic."""

but then it does:
    if destexists:
        if stat.S_ISLNK(dstat[stat.ST_MODE]):
            try:
>>              os.unlink(dest)
                destexists = 0
...
    if stat.S_ISLNK(sstat[stat.ST_MODE]):
        try:
            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)
            try:
                if selinux_enabled:
>>                  selinux.symlink(target, dest, src)
                else:
>>                  os.symlink(target, dest)

that is by definition not atomic.  when merging a package, there's a small window where the symlink doesn't exist, so anyone using that symlink (e.g. a symlink to a program) can break.  it's a small window, but on loaded systems running a lot of things in parallel, it's not that small.  we're seeing this flake in CrOS builders on a semi-regular basis.  bug 563614 might be an example of this that i misattributed to binutils-config.

it's not clear why this code is deleting the symlink in the first place.  POSIX says that rename supports moving symlinks over symlinks, symlinks over files, and files over symlinks:
https://pubs.opengroup.org/onlinepubs/9699919799/functions/rename.html

so this code should try os.rename first like it does with files, and if that fails (e.g. EXDEV), fallback to the same method as files where it creates a unique name in the same fs and then renames from there.
Comment 1 Larry the Git Cow gentoo-dev 2022-04-15 02:46:25 UTC
The bug has been referenced in the following commit(s):

https://gitweb.gentoo.org/proj/portage.git/commit/?id=fa82725ba03ffa5edf0b82db7307c87fc97e83f2

commit fa82725ba03ffa5edf0b82db7307c87fc97e83f2
Author:     Mike Frysinger <vapier@chromium.org>
AuthorDate: 2022-03-30 06:32:07 +0000
Commit:     Sam James <sam@gentoo.org>
CommitDate: 2022-04-15 02:46:19 +0000

    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>

 lib/portage/util/movefile.py | 16 +++++++++++++---
 1 file changed, 13 insertions(+), 3 deletions(-)