python / python/cpython

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

Aperta
#156,210 0 commenti 0 reazioni 0 assegnatari Vedi su GitHub

Nessuno ha ancora preso questa issue.

stdlib type-bug
Lingua principale
Python
Stelle
77.2k
Fork
35.9k
Metriche di merge delle PR
Metriche PR in attesa

Descrizione

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

Guida per i contributori

Apri la guida per i contributori

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Direzione di ricerca

Inizia in Lib/shutil.py, in _copytree(), e esamina il PR collegato gh-156214 prima di apportare modifiche. Riproduci i casi di symlink relativi dell’issue, quindi aggiungi o aggiorna la copertura di regressione nei test shutil esistenti in modo che i link validi vengano copiati e i link pendenti vengano ignorati indipendentemente dalla directory di lavoro corrente.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Valutazione

Stack tecnologico
python
Ambito
tooling
Tipo di issue
Bug
Difficoltà
3/5
Tempo stimato
1-2 giorni
Stato di attività
Ferma
Chiarezza
Specificata chiaramente
Idoneità per principianti
25/100

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.