python / python/cpython

`shutil.copytree()` incorrectly classifies relative symlinks as dangling

Offen
#156,210 0 Kommentare 0 Reaktionen 0 zugewiesene Personen Auf GitHub ansehen

Dieses Issue hat noch niemand übernommen.

stdlib type-bug
Vorherrschende Sprache
Python
Sterne
77.2k
Forks
35.9k
PR-Merge-Kennzahlen
PR-Kennzahlen ausstehend

Beschreibung

Bug report

Bug description:
Summary

shutil.copytree(..., symlinks=False, ignore_dangling_symlinks=True) tests a relative symlink target against the process current working directory rather than against the directory containing the symlink. Consequently, a valid relative symlink can be silently omitted from a successful copy, and a truly dangling symlink can still cause shutil.Error when the current working directory happens to contain a file with the same name as its target.

Affected code

Lib/shutil.py, _copytree():

linkto = os.readlink(srcname)
...
if not os.path.exists(linkto) and ignore_dangling_symlinks:
    continue

os.readlink() returns the raw target. A relative target is interpreted by the operating system relative to the symlink's parent directory, but os.path.exists(linkto) interprets it relative to the process current working
directory.

Reproducer
import os
import shutil
import tempfile

with tempfile.TemporaryDirectory() as root:
    src = os.path.join(root, "src")
    dst = os.path.join(root, "dst")
    os.mkdir(src)
    with open(os.path.join(src, "target"), "w") as f:
        f.write("data")
    os.symlink("target", os.path.join(src, "link"))

    old_cwd = os.getcwd()
    try:
        os.chdir(root)  # root/target does not exist
        shutil.copytree(src, dst, ignore_dangling_symlinks=True)
        print("exception: none")
    finally:
        os.chdir(old_cwd)

    print("source link exists?", os.path.lexists(os.path.join(src, "link")))
    print("destination entries:", sorted(os.listdir(dst)))
    print("destination link exists?", os.path.lexists(os.path.join(dst, "link")))

Actual output:

exception: none
source link exists? True
destination entries: ['target']
destination link exists? False

The source symlink is valid: src/link -> target resolves to src/target. With symlinks=False, the destination should contain a regular link file with the contents of src/target, in addition to target. Instead, the operation returns successfully with an incomplete destination tree.

The inverse case also fails: if src/link -> target is actually dangling but the current working directory contains target, it is not ignored and copytree() raises shutil.Error after creating the destination directory.

CPython versions tested on:

CPython main branch

Operating systems tested on:

Linux

Linked PRs
  • gh-156214

Beitragsleitfaden

Beitragsleitfaden öffnen

Erste Schritte

  1. Lies das ganze Issue und danach den Beitragsleitfaden des Projekts.
  2. Schreib ins Issue, dass du es übernimmst — das erspart doppelte Arbeit.
  3. Forke das Repository und arbeite in einem Branch.
  4. Öffne einen Pull Request, der die Issue-Nummer nennt.

Rechercherichtung

Beginnen Sie in Lib/shutil.py bei _copytree() und prüfen Sie den verknüpften PR gh-156214, bevor Sie Änderungen vornehmen. Reproduzieren Sie die Fälle mit relativen Symlinks aus dem Issue und fügen Sie anschließend in den vorhandenen shutil-Tests Regressionstests hinzu oder aktualisieren Sie diese, sodass gültige Links kopiert und hängende Links unabhängig vom aktuellen Arbeitsverzeichnis ignoriert werden.

Vom Indexierungsmodell aus dem Issue-Text verfasst.

Bewertung

Tech-Stack
python
Bereich
tooling
Issue-Typ
Bug
Schwierigkeit
3/5
Geschätzter Aufwand
1-2 Tage
Aktivitätsstatus
Veraltet
Klarheit
Klar beschrieben
Anfängerfreundlichkeit
25/100

Neue Issues direkt in Ihr Postfach

Eine kurze Übersicht über anfängerfreundliche GitHub-Issues.