fix(skills): make _rmtree_writable handle read-only directories, not just files
The cherry-picked fix's onerror handler chmod'd only the failing path, but unlinking a child requires write permission on its PARENT directory. On a true Nix-store copy (r-xr-xr-x dirs + files) rmtree still failed. Now chmod the parent dir as well before retrying. Also rewrites the regression test: the original asserted the helper FAILS on a read-only dir (documenting the limitation), which is the wrong success criterion. Split into two tests — restore succeeds on a full read-only tree (real Nix case), and manifest is preserved when removal genuinely cannot proceed (monkeypatched).
This commit is contained in:
+14
-5
@@ -567,15 +567,24 @@ def sync_skills(quiet: bool = False) -> dict:
|
||||
|
||||
|
||||
def _rmtree_writable(path: Path) -> None:
|
||||
"""Remove a directory tree, making read-only files writable first.
|
||||
"""Remove a directory tree, making read-only entries writable first.
|
||||
|
||||
Handles immutable package sources (Nix store, deb/rpm installs) that
|
||||
preserve read-only permissions on copied files. See #34860, #34972.
|
||||
preserve read-only permissions on copied files *and* directories
|
||||
(``r-xr-xr-x``). Removing a child requires write permission on its
|
||||
parent directory, so the retry handler makes the failing path **and its
|
||||
parent** writable before re-attempting. See #34860, #34972.
|
||||
"""
|
||||
import stat
|
||||
|
||||
def _on_error(func, fpath, exc_info):
|
||||
# Make the file/directory writable and retry
|
||||
import stat
|
||||
os.chmod(fpath, stat.S_IWRITE)
|
||||
# Unlinking a child requires the parent dir to be writable, so chmod
|
||||
# the parent as well as the failing path, then retry.
|
||||
for target in (os.path.dirname(fpath), fpath):
|
||||
try:
|
||||
os.chmod(target, stat.S_IRWXU)
|
||||
except OSError:
|
||||
pass
|
||||
func(fpath)
|
||||
|
||||
shutil.rmtree(path, onerror=_on_error)
|
||||
|
||||
Reference in New Issue
Block a user