Skip to content

fix(workspace): allow cloud reparse points on the workspace ancestry - #943

Open
Artoria2e5 wants to merge 1 commit into
T-Auto:mainfrom
Artoria2e5:reparse
Open

Artoria2e5 wants to merge 1 commit into
T-Auto:mainfrom
Artoria2e5:reparse

Conversation

@Artoria2e5

Copy link
Copy Markdown

Closes #937

What was wrong

Any workspace below OneDrive was refused before ACL preparation:

DSH workspace preparation failed: the workspace root C:\Users\arthu\OneDrive\Documents\Essays does not meet the sandbox prerequisite (the host needs WRITE_OWNER on it): Workspace ancestry contains a reparse point; refusing ACL preparation.

bin/prepare-workspace.ps1 refused every ancestor that carried FILE_ATTRIBUTE_REPARSE_POINT. OneDrive tags a synced directory in place with a cloud reparse point (IO_REPARSE_TAG_CLOUD 0x9000001A, provider variants 0x9000N01A); on the reporting host %USERPROFILE%\OneDrive is 0x9000701A and OneDrive\Documents is 0x9000601A. 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.ts was not the cause: its pre-check asks lstat().isSymbolicLink(), which is false for a cloud-tagged directory, so only the script refused.

The change

  • bin/workspace-reparse.ps1 (new, pure). Test-DshOpsAllowedReparseTag allows the cloud family (($Tag -band 0xFFFF0FFF) -eq 0x9000001A); Find-DshOpsUnaccountedReparsePoint returns the first ancestor entry that is not an allowed placeholder. No I/O, dot-sourced like workspace-acl.ps1.
  • bin/prepare-workspace.ps1. The ancestry walk still records every entry. Only when a reparse point is present does an Add-Type reader open each one with FILE_FLAG_OPEN_REPARSE_POINT and FSCTL_GET_REPARSE_POINT and 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. docs/troubleshooting.md and docs/常见问题.md (the DSH workspace preparation failed section), plus a ## [Unreleased] entry in docs/CHANGELOG.md in 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

  • 本 PR 有一次 pull_request 事件触发的 CI required run,且已把该 run 的 URL 贴到下方代码块
  • 本地门禁 scripts/verify-gate.mjs --skip-install(CI 同序步骤:build、cargo test、build:ptc、Niubash staging、legacy MSYS fixture、manifest、全量 suite、runtime closure、pack)全绿
  • 撤掉本次实现后相关检查变红 — 由 CI 承担:本地未做旧脚本的对照运行(见下)
  • docs/CHANGELOG.md 的 ## [Unreleased] 已加条目
  • 行为变化已同步相关 docs(中英双语)
  • 未新增依赖,未改 package.json 的版本与 optionalDependencies
  • 面向模型的描述或参数未变(classifyPreparationFailure 的 /reparse point/ 关键词仍匹配新消息)
$ fnm exec --using=24 -- node scripts/verify-gate.mjs --skip-install
…
=== summary
  ok   versions
  ok   cargo version
  ok   build the plugin
  ok   native worker unit tests
  ok   build and stage the native worker
  ok   stage complete frozen Niubash payload
  ok   Acquire explicit legacy MSYS fixture
  ok   Legacy Windows IPC payloads
  ok   manifest contract
  ok   full suite verification
  ok   staged runtime closure
  ok   pack the release artifact
  ok   unpack the release artifact
  ok   packed runtime closure
  245 suites passed; skipped: linux-attributes, linux-shell, tui-host, tui-native (allowed: file-metadata, linux-attributes, linux-shell, tui-host, tui-native)
  manifest: 92/59 checks

win32-x64: gate green

The new suite inside that run:

$ node test/run.mjs workspace-reparse
=== workspace-reparse.test.mjs
  ok   cloud storage placeholders are allowed on the workspace ancestry (#937)
  ok   junctions, symlinks and other reparse points stay refused (#937)
  ok   the shipped script refuses a junction root, names its tag and writes no receipt
  ok   the shipped script refuses a junction above the workspace root

dsh-ops workspace-reparse: all 4 checks passed

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.ps1 against real junctions and assert a non-zero exit, the 0xA0000003 tag in stderr, and no backup receipt.

Revert evidence: not run locally. The red/green pair needs the pre-change script to reach Get-Acl on 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.

$ gh run view <run-id> --json url,event,conclusion --jq '"\(.url) \(.event) \(.conclusion)"'
<CI required run URL, event must be pull_request>

Environment

Windows 11 (x64), Node v24.22.3, rustc 1.101.0-beta.2 as the local default (the gate also compiles in CI against the native/rust-toolchain.toml pin, 1.97.1), base commit 88ecc729. Local checkout, npm ci --ignore-scripts.

A 虎鲸 (orca) did this.

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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[请求] White list some reparse points

1 participant