Refuse sd.cpp install into unowned non-empty target dir

When the install target already exists, is non-empty and lacks the
.unsloth-studio-owned marker (a user's own stable-diffusion.cpp checkout,
or unrelated files beside a custom Studio root), install() previously still
extracted the release into it. Skipping the ownership marker only stopped the
uninstaller from deleting the directory; extraction still merged binaries into
the user's working tree and could overwrite same-named files.

Fail up front with a clear message pointing the user at a fresh/empty location
before any download or extraction, leaving their directory untouched. Update
the ownership test suite to assert the refusal.
This commit is contained in:
Daniel Han 2026-07-13 03:17:49 +00:00
commit da1770bb44
2 changed files with 21 additions and 8 deletions

View file

@ -273,11 +273,12 @@ def test_install_into_empty_dir_claims_ownership(tmp_path, monkeypatch):
assert (empty / ".unsloth-studio-owned").is_file()
def test_install_into_nonempty_unowned_dir_does_not_claim_ownership(tmp_path, monkeypatch):
def test_install_into_nonempty_unowned_dir_is_refused(tmp_path, monkeypatch):
# A pre-existing, non-empty directory that Studio did not create (e.g. a user's own
# stable-diffusion.cpp checkout) must NOT be marked owned: writing the marker would make the
# uninstaller recursively delete the user's directory. The install still proceeds (extracts the
# binary) but leaves the directory unowned so the uninstaller keeps it.
# stable-diffusion.cpp checkout) must NOT be extracted into. Merging the release into it would
# overwrite or mix our binaries into the user's working tree, and leaving it unowned only stops
# the uninstaller from deleting it later. install() refuses up front and leaves the dir untouched
# so the user can point us at a fresh/empty location.
zb = _zip_with_sd_cli()
_stub_release(monkeypatch, zip_bytes = zb, digest = "sha256:" + hashlib.sha256(zb).hexdigest())
target = tmp_path / "stable-diffusion.cpp"
@ -285,13 +286,13 @@ def test_install_into_nonempty_unowned_dir_does_not_claim_ownership(tmp_path, mo
user_file = target / "USER_WORK"
user_file.write_text("keep", encoding = "utf-8")
sd_cli = install(install_dir = target)
with pytest.raises(RuntimeError, match = "not a Studio-managed directory"):
install(install_dir = target)
# The user's file survives and no ownership marker was written.
# The user's directory is left exactly as it was: file intact, no marker, nothing extracted.
assert user_file.read_text(encoding = "utf-8") == "keep"
assert not (target / ".unsloth-studio-owned").exists()
# The binary was still installed (install is not refused).
assert sd_cli.is_file() and sd_cli.name == "sd-cli"
assert list(target.iterdir()) == [user_file]
def test_reinstall_into_owned_dir_keeps_ownership(tmp_path, monkeypatch):

View file

@ -418,6 +418,18 @@ def install(
_pre_existing_entries = True
# Empty dir, or one we already own, may be (re)claimed; a non-empty unowned dir may not.
_may_own = (not _pre_existing_entries) or marker.is_file()
# Refuse to extract into a pre-existing, non-empty directory we do not own (a user's own
# stable-diffusion.cpp checkout, or unrelated files beside a custom Studio root). Not writing
# the ownership marker only protects the uninstaller; extracting the release here would still
# merge our binaries into the user's working tree and can overwrite same-named files. Fail with
# a clear message so the user points us at a fresh/empty location instead of corrupting theirs.
if not _may_own:
raise RuntimeError(
f"sd.cpp install target already exists and is not a Studio-managed directory: {target}. "
f"Refusing to extract prebuilt binaries into it to avoid overwriting or mixing them "
f"into your files. Remove or move that directory, or install into a different, empty "
f"location (pass a different --install-dir / set the Studio sd.cpp install dir)."
)
used_repo, release, chosen = _resolve_with_fallback(accelerator, token)
if release is None or not chosen: