python / python/cpython

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

Ouverte
#156,210 0 commentaires 0 réactions 0 personnes assignées Voir sur GitHub

Personne n'a encore pris cette issue.

stdlib type-bug
Langage dominant
Python
Étoiles
77.2k
Forks
35.9k
Métriques de merge des PR
Métriques de PR en attente

Description

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

Guide de contribution

Ouvrir le guide de contribution

Par où commencer

  1. Lisez l'issue en entier, puis le guide de contribution du projet.
  2. Signalez en commentaire que vous la prenez — cela évite que deux personnes fassent le même travail.
  3. Forkez le dépôt et travaillez sur une branche.
  4. Ouvrez une pull request qui référence le numéro de l'issue.

Piste de recherche

Commencez dans Lib/shutil.py, au niveau de _copytree(), et examinez le PR lié gh-156214 avant d’apporter des modifications. Reproduisez les cas de liens symboliques relatifs de l’issue, puis ajoutez ou mettez à jour la couverture de régression dans les tests shutil existants afin que les liens valides soient copiés et que les liens pendants soient ignorés quel que soit le répertoire de travail courant.

Rédigé par le modèle d'indexation à partir du texte de l'issue.

Évaluation

Stack technique
python
Domaine
tooling
Type d'issue
Bug
Difficulté
3/5
Temps estimé
1-2 jours
Activité
À l'abandon
Clarté
Clairement spécifiée
Accessibilité débutants
25/100

Recevez les nouvelles issues par e-mail

Un résumé court des issues GitHub adaptées aux débutants.