mirror of
https://github.com/unslothai/unsloth.git
synced 2026-08-21 06:43:53 +00:00
* Stop the temp probe leaving anything behind Three pieces of residue an audit of #9178 turned up, all on hosts whose temp is degraded, which is the population that change exists for. Probing the host's own TMP no longer creates it. New-Item -Force builds the whole parent chain, so a stale or mistyped TMP had the installer silently materialize a tree at a path nobody chose and then trust it as temp. Absent now reads as unusable, which is what the private fallback is for. A directory the installer owns is still created, through an explicit switch. A probe file that could not be deleted is reclaimed by the next run, once it is a day old. The probe cannot clean up after itself when deletion is exactly what failed, and nothing else knows the name, so each such run used to leave one more file in the host's temp forever. The age gate keeps it away from a probe running concurrently in another process. A candidate root that fails its probe is taken back. The probe creates the directory before it tests it, so an install that tried every root and gave up left a "Unsloth Studio" tree on a machine Studio was never installed on. Only if empty, so a directory that already held something is never touched. * Keep the lock name in step with the runtime gate, and narrow the uninstall Two corrections to #9178, both found by auditing it after it merged. The final normalization kept the \\?\ prefix on a volume GUID. That string is hashed into the runtime mutex name, and unsloth_cli/_studio_runtime_gate.py strips \\?\ unconditionally, so on a host that resolves to a volume GUID the installer and a running Studio computed different names for one directory and neither excluded the other. Every extended spelling comes off again, as it did before #9178. Rootedness does matter while a link target is being anchored, and Resolve-StudioLinkTarget still keeps the extended form for that; nothing after this point anchors anything. The uninstaller resolved both LocalAppData spellings and gave each the full data-directory delete. That delete is recursive and, alone among the deletes in that script, requires no ownership sentinel and consults no deny list. The two spellings differ mainly when one of them names a DIFFERENT USER's profile: CreateProcessAsUser with a null environment block, runas /env, a service token. The second spelling now gets only what install.ps1 puts there, ust- directories matched by shape rather than prefix, and the temp directory and its parent only if they are left empty. The port-file stop and the stop-roots list go back to the single resolved root. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Take back the parents the failed candidate created, not just the leaf New-Item -Force builds the whole chain, so removing only the empty ust-* directory still left "Unsloth Studio\temp" standing, which is the data directory tree this cleanup exists to avoid conjuring on a machine Studio was never installed on. It now walks back up, but only through the directory names this path is made of and only while each one is empty, so a tree that already held something is left alone and ~\.unsloth, which is shared, is never removed. The test asserts both halves: nothing remains under a root that was empty before, and a pre-existing studio.port under one of the same parents survives. * Sweep by shape, not by prefix, and say when the native resolver gives up Two audits of the merged change independently reproduced the same deletion on a HEALTHY host: the stale sweep matched ust-* by prefix, and a name whose tail is not a parseable PID leaves $ownerPid at zero, which skips the liveness check entirely. So a pre-existing directory such as "ust-legacy" or "ust-user-cache" under LOCALAPPDATA\Unsloth Studio\temp, older than a day, was recursively deleted by an ordinary install. The uninstaller added in the previous commit already required the allocator's exact shape; the two had drifted apart. The sweep now requires it too, case-insensitively, since Windows filenames are. Separately, a native resolver that COMPILES and then throws falls back to the lexical resolver, which is right, and marks the answer inexact, which is also right, but said nothing. The degraded warning fires only when the compile itself failed, so an operator on a host that looks perfectly healthy was left with a silently inexact identity. It now warns once, with wording distinct from the compile-failure warning. Exit codes and the fallback itself are unchanged: aborting the install is the #9140 symptom, not the fix. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * An owner nobody recorded is unknown, not abandoned Three findings from the second audit round, all in the ownership model. The directory name carries the INSTALLER's pid. An installer killed between Start-Process and the owner.pid write leaves a dead pid in the name while the Studio it started is using that directory as its own %TEMP%, and a later run read that as proof of abandonment and deleted it out from under the live process. Reading owner.pid is proof; guessing from the name is not, so an unrecorded owner now needs a week rather than a day before it is collected. The pile still stays bounded, and a Studio actually using the directory refreshes its timestamp long before that. The uninstaller's private temp sweep ignored owner.pid entirely, so it removed a directory a Studio from another install root, or another user, was live on. It now leaves a live owner alone, exactly as install.ps1's sweep does. That sweep also walked through a link: Get-ChildItem on a reparse point enumerates the target, whose children carry no ReparsePoint attribute, so the recursive delete took the target's tree. It now refuses a temp directory whose own path or parent is a link. In install.ps1 the reparse branch moves ahead of the owner logic, since the allocator never creates a link, reading owner.pid out of one would read through it, and unlinking is safe whatever owns it. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Unwind only what the probe created, and require an owner outside our profile Two review items on this branch. The cleanup walked back up through any empty, correctly named parent. A pre-provisioned "Unsloth Studio\temp" with its own ACLs, or an empty relocation junction, is configuration this installer did not create, and empty plus correctly named is not the same as ours. Which ancestors were absent is now recorded before the probe touches anything, and only those are unwound. A reparse point stops the walk regardless. The uninstaller's private temp sweep treated the directory-name shape as ownership proof in every profile it looked at. Under the profile being uninstalled that is right, since that is what is being removed. In the other LocalAppData spelling, which can be a different user, a directory with no recorded owner now stays: install.ps1 reads that state as unknown rather than abandoned, because an installer killed before writing owner.pid leaves a live Studio holding the directory. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Reject a reparse point anywhere above another profile's temp root A junction does not have to sit next to the temp directory to redirect it. LocalAppData, the profile directory or the drive root can be the reparse point, and then "<root>\Unsloth Studio\temp" and its parent both look like ordinary directories while Get-ChildItem lands somewhere else. For a LocalAppData spelling that is not the profile being uninstalled, that somewhere else can be another user's tree. Walk every ancestor up to the root for the alternate spelling. The profile the uninstall is actually for keeps the two-level check: a redirected LocalAppData there is the same user's own storage, and refusing would leave the installer's own temp tree behind on every host that uses folder redirection. * Sweep the private temp trees before removing the data directory The primary private temp directory lives inside the data directory, and the data directory is removed wholesale. A Studio from another install root can still be alive on that temp directory as its %TEMP%, so the live-owner check never got the chance to protect it. Run the sweep first and have it hand back everything it kept, then let the data-dir removal delete around those paths through _RemoveTreeKeeping. With an empty preserve list that is _RemovePath and nothing else, which is what every ordinary uninstall gets. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Sweep only the probe names the installer generates The stale-probe cleanup runs in the HOST's temp directory, where a file that merely starts with unsloth-probe- and ends in .tmp belongs to somebody else. Match the shape the probe actually writes, eight hex characters, the same rule the private temp sweep already uses. --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> |
||
|---|---|---|
| .. | ||
| data | ||
| build_prequant_checkpoint.py | ||
| build_te_prequant_checkpoint.py | ||
| build_whisper_cpp.sh | ||
| check_frontend_dep_removal.py | ||
| check_new_install_scripts.py | ||
| compare_engines.py | ||
| compile_probe.py | ||
| diffusion_bench.py | ||
| diffusion_quality.py | ||
| enforce_kwargs_spacing.py | ||
| fbcache_flux_probe.py | ||
| fp8_overflow_check.py | ||
| image_speedmem_bench.py | ||
| install_gemma4_mlx.sh | ||
| install_qwen3_6_mlx.sh | ||
| install_rocm_wsl_strixhalo.sh | ||
| int8_linear_probe.py | ||
| leverage_probe.py | ||
| lint_backend_python_floor.py | ||
| lint_no_parallel_clamp.py | ||
| lint_workflow_triggers.py | ||
| lockfile_supply_chain_audit.py | ||
| make_dmg_background.py | ||
| notebook_to_python.py | ||
| notebook_validator.py | ||
| nvfp4_probe.py | ||
| nvfp4_t211_probe.py | ||
| online_tokenization_ab.py | ||
| perf_levers_probe.py | ||
| perf_verify.py | ||
| prequant_probe.py | ||
| profile_startup.py | ||
| quant_probe.py | ||
| run_ruff_format.py | ||
| scan_npm_packages.py | ||
| scan_npm_packages_baseline.json | ||
| scan_packages.py | ||
| scan_packages_baseline.json | ||
| sd_cpp_smoke.py | ||
| sdpa_mask_backend_probe.py | ||
| sparse_accum_probe.py | ||
| stamp_studio_release.py | ||
| sync_allow_scripts_pins.py | ||
| uninstall.ps1 | ||
| uninstall.sh | ||
| verify_comment_only_diff.py | ||
| verify_import_hoist.py | ||
| verify_prequant_backend.py | ||
| video_quality.py | ||
| virustotal_scan.py | ||