◐ Shell
clean mode source ↗

gh-142155: Fix infinite recursion in shutil.copytree on Windows junctions by ChuheLin · Pull Request #142156 · python/cpython

Summary

This PR fixes a critical crash (WinError 206 / WinError 1921) in shutil.copytree on Windows when encountering directory junctions that point to a parent directory (recursive cycles).

The Problem

On Windows, shutil.copytree contains specific logic to force traversal into Directory Junctions by treating them as standard directories (is_symlink is forced to False).

However, unlike os.walk or shutil.rmtree (after recent fixes), copytree lacked a cycle detection mechanism for these traversed junctions. This caused infinite recursion until the path exceeded the OS limit, resulting in an unhandled OSError (Crash) rather than a Python RecursionError.

The Fix

I implemented cycle detection using file system identity (st_dev, st_ino), similar to how deepcopy handles recursion:

  1. Added a hidden _seen set parameter to copytree and its helper _copytree.
  2. Before entering a directory, the code now checks if (st.st_dev, st.st_ino) is already in _seen.
  3. If a cycle is detected, it raises a shutil.Error("Infinite recursion detected") immediately, preventing the crash.

Verification

  1. Regression Test: Added test_copytree_recursive_junction to Lib/test/test_shutil.py.
    • Status: PASSED on local Windows build.
  2. Manual Verification: Verified with a reproduction script that previously caused a WinError 206 crash.
    • Before fix: Script crashed after creating a path > 4000 chars.
    • After fix: Script correctly raises shutil.Error: Infinite recursion detected.