Repository navigation
fix(workspace): allow cloud reparse points on the workspace ancestry - #943
Open
Artoria2e5 wants to merge 1 commit into
Open
Artoria2e5 wants to merge 1 commit into
Artoria2e5 wants to merge 1 commit into
Conversation
OneDrive tags a synced directory with IO_REPARSE_TAG_CLOUD, so every workspace below it was refused before ACL preparation. Read the tag through the reparse point and accept the cloud family 0x9000?01A. Junctions, symlinks, deduplication and any other tag still refuse before the ACL write, and the refusal now names the offending path and tag. Refs T-Auto#937
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #937
What was wrong
Any workspace below OneDrive was refused before ACL preparation:
bin/prepare-workspace.ps1refused every ancestor that carriedFILE_ATTRIBUTE_REPARSE_POINT. OneDrive tags a synced directory in place with a cloud reparse point (IO_REPARSE_TAG_CLOUD0x9000001A, provider variants0x9000N01A); on the reporting host%USERPROFILE%\OneDriveis0x9000701AandOneDrive\Documentsis0x9000601A. Such a directory keeps its path and holds provider state, so nothing about the ACL write changes; the blanket refusal made every synced folder unusable.src/workspace-preparation.tswas not the cause: its pre-check askslstat().isSymbolicLink(), which is false for a cloud-tagged directory, so only the script refused.The change
bin/workspace-reparse.ps1(new, pure).Test-DshOpsAllowedReparseTagallows the cloud family (($Tag -band 0xFFFF0FFF) -eq 0x9000001A);Find-DshOpsUnaccountedReparsePointreturns the first ancestor entry that is not an allowed placeholder. No I/O, dot-sourced likeworkspace-acl.ps1.bin/prepare-workspace.ps1. The ancestry walk still records every entry. Only when a reparse point is present does anAdd-Typereader open each one withFILE_FLAG_OPEN_REPARSE_POINTandFSCTL_GET_REPARSE_POINTand read the tag. A tag outside the cloud family is refused before any ACL work, and the message now names the path and the tag (… is not a cloud storage placeholder: <path> (tag 0xA0000003)). Cloud placeholders fall through to the unchanged DACL decision.src/workspace-preparation.ts. JSDoc only: the check asks whether the path resolves through a junction or symlink, and says why a cloud placeholder is not one.docs/troubleshooting.mdanddocs/常见问题.md(theDSH workspace preparation failedsection), plus a## [Unreleased]entry indocs/CHANGELOG.mdin both languages.Deliberately not done: junctions, symlinks, deduplication reparse points and every other tag stay refused — they redirect the path, so the ACL write could land outside the workspace the caller named. Fabricating a cloud tag in a test is not possible either (assigning one by hand makes the Cloud Files filter reject the directory), so the allow decision is tested through the helper with the tags a real OneDrive reports, and the refusal through real junctions the shipped script walks.
Validation
pull_request事件触发的CI requiredrun,且已把该 run 的 URL 贴到下方代码块scripts/verify-gate.mjs --skip-install(CI 同序步骤:build、cargo test、build:ptc、Niubash staging、legacy MSYS fixture、manifest、全量 suite、runtime closure、pack)全绿docs/CHANGELOG.md的## [Unreleased]已加条目package.json的版本与optionalDependenciesclassifyPreparationFailure的/reparse point/关键词仍匹配新消息)The new suite inside that run:
The first two checks call the production helper. The second also feeds it synthetic ancestry (cloud above a junction) to check that the nearest unaccounted entry is the one reported. The last two run the shipped
prepare-workspace.ps1against real junctions and assert a non-zero exit, the0xA0000003tag in stderr, and no backup receipt.Revert evidence: not run locally. The red/green pair needs the pre-change script to reach
Get-Aclon a live OneDrive folder, and that run hung the local session; instead of risking a partial DACL state it was left to CI. Every check above is anchored to the new refusal branches, and the two shipped-script checks fail if the walk stops reading tags.Environment
Windows 11 (x64), Node v24.22.3,
rustc 1.101.0-beta.2as the local default (the gate also compiles in CI against thenative/rust-toolchain.tomlpin, 1.97.1), base commit88ecc729. Local checkout,npm ci --ignore-scripts.A 虎鲸 (orca) did this.