Skip to content

fix(ckpt): drop --copy-unsafe-links from init rsync#873

Open
Ziqi002 wants to merge 1 commit into
alibaba:mainfrom
Ziqi002:fix/ckpt-issue-798
Open

fix(ckpt): drop --copy-unsafe-links from init rsync#873
Ziqi002 wants to merge 1 commit into
alibaba:mainfrom
Ziqi002:fix/ckpt-issue-798

Conversation

@Ziqi002

@Ziqi002 Ziqi002 commented Jun 12, 2026

Copy link
Copy Markdown
Collaborator

Description

  • init rsync 去掉 --copy-unsafe-links,修复绝对路径内部 symlink 导致 exit 23

问题

未初始化的 workspace 里有指向自身的绝对路径 symlink 时,init 把目录
rename 到 backup 后,这些 symlink 的目标路径断裂。rsync --copy-unsafe-links
把它们当"指向源树外"的 symlink 尝试解引用,目标不存在报 exit 23。

修法

去掉 --copy-unsafe-links,只保留 -a。symlink 原样保留,init 完成后
original-path -> subvolume 的 symlink 让绝对内部 symlink 通过链式解析
正确工作,和 InPlace 场景的 cp -a 行为一致。

Related Issue

closes #798

Type of Change

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update
  • Refactoring (no functional change)
  • Performance improvement
  • CI/CD or build changes

Scope

  • cosh (copilot-shell)
  • sec-core (agent-sec-core)
  • skill (os-skills)
  • sight (agentsight)
  • tokenless (tokenless)
  • memory (agent-memory)
  • ckpt (ws-ckpt)
  • Multiple / Project-wide

Checklist

  • I have read the Contributing Guide
  • My code follows the project's code style
  • I have added tests that prove my fix is effective or that my feature works
  • I have updated the documentation accordingly
  • For cosh: Lint passes, type check passes, and tests pass
  • For sec-core (Rust): cargo clippy -- -D warnings and cargo fmt --check pass
  • For sec-core (Python): Ruff format and pytest pass
  • For skill: Skill directory structure is valid and shell scripts pass syntax check
  • For sight: cargo clippy -- -D warnings and cargo fmt --check pass
  • For tokenless: cargo clippy -- -D warnings and cargo fmt --check pass
  • For memory (Linux only): cargo clippy --all-targets -- -D warnings, cargo fmt --check, and cargo test pass
  • Lock files are up to date (package-lock.json / Cargo.lock)

Testing

测试脚本

#!/usr/bin/env bash
# 10-issue-798-symlink-preserve.sh
# 验证 init 时 workspace 内含绝对路径 symlink 不会导致失败(issue #798)
#
# 修复前: rsync 遇到指向自身的绝对 symlink 会 exit 23
# 修复后: init 成功,symlink 仍可正常解析
#
# 用法: bash 10-issue-798-symlink-preserve.sh

set -euo pipefail

WS_CKPT="${WS_CKPT:-$(command -v ws-ckpt 2>/dev/null || echo /usr/local/bin/ws-ckpt)}"
WS="/tmp/test-798"

RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; NC='\033[0m'
PASS=0; FAIL=0

ok()   { echo -e "  ${GREEN}PASS${NC}: $*"; PASS=$((PASS+1)); }
bad()  { echo -e "  ${RED}FAIL${NC}: $*"; FAIL=$((FAIL+1)); }
log()  { echo -e "\n${YELLOW}=== $* ===${NC}"; }

# ── 清理 ──────────────────────────────────────────────────────────
cleanup() {
    "$WS_CKPT" recover -w "$WS" --force >/dev/null 2>&1 || true
    rm -rf "$WS" 2>/dev/null || true
}
trap cleanup EXIT
cleanup

# ── 准备 workspace ────────────────────────────────────────────────
log "准备: 创建含绝对路径 symlink 的目录"

mkdir -p "$WS"
echo "hello" > "$WS/target.txt"
ln -s "$WS/target.txt" "$WS/link.txt"

if [ -L "$WS/link.txt" ] && [ "$(cat "$WS/link.txt")" = "hello" ]; then
    ok "初始 symlink 可读"
else
    bad "初始 symlink 就有问题,跳过后续测试"
    exit 1
fi

# ── 场景 1: init 不应 exit 23 ─────────────────────────────────────
log "场景 1: init 含绝对 symlink 的 workspace"

set +e
OUT=$("$WS_CKPT" init -w "$WS" 2>&1)
RC=$?
set -e

if [ "$RC" -eq 0 ]; then
    ok "init 成功 (rc=0)"
else
    bad "init 失败 (rc=$RC), 输出: $OUT"
fi

if [ "$RC" -eq 23 ]; then
    bad "命中 issue #798: exit 23 (rsync partial transfer)"
fi

# ── 场景 2: init 后 symlink 仍可解析 ──────────────────────────────
log "场景 2: init 后 symlink 仍可解析"

if [ -L "$WS/link.txt" ]; then
    ok "symlink 仍然存在"
else
    bad "symlink 消失了"
fi

CONTENT=$(cat "$WS/link.txt" 2>/dev/null || echo "__MISSING__")
if [ "$CONTENT" = "hello" ]; then
    ok "symlink 内容正确: hello"
else
    bad "symlink 内容不对, 期望 hello, 实际: $CONTENT"
fi

# ── 场景 3: init 后 workspace 正常可用 ─────────────────────────────
log "场景 3: init 后 workspace 可正常 checkpoint"

set +e
OUT=$("$WS_CKPT" checkpoint -w "$WS" -i "snap-798" -m "test issue 798" 2>&1)
RC=$?
set -e

if [ "$RC" -eq 0 ]; then
    ok "checkpoint 成功"
else
    bad "checkpoint 失败 (rc=$RC), 输出: $OUT"
fi

# ── 场景 4: recover 后 symlink 恢复 ───────────────────────────────
log "场景 4: recover 后 symlink 仍可解析"

set +e
OUT=$("$WS_CKPT" recover -w "$WS" --force 2>&1)
RC=$?
set -e

if [ "$RC" -eq 0 ]; then
    ok "recover 成功"
else
    bad "recover 失败 (rc=$RC), 输出: $OUT"
fi

CONTENT=$(cat "$WS/link.txt" 2>/dev/null || echo "__MISSING__")
if [ "$CONTENT" = "hello" ]; then
    ok "recover 后 symlink 内容正确: hello"
else
    bad "recover 后 symlink 内容不对, 期望 hello, 实际: $CONTENT"
fi

# ── 汇总 ──────────────────────────────────────────────────────────
echo ""
TOTAL=$((PASS + FAIL))
echo -e "结果: ${PASS}/${TOTAL} passed, ${FAIL} failed"
if [ "$FAIL" -gt 0 ]; then
    echo -e "${RED}VERDICT: FAIL${NC}"
    exit 1
else
    echo -e "${GREEN}VERDICT: PASS${NC}"
    exit 0
fi

Additional Notes

Absolute internal symlinks break after rename moves the workspace to
a backup dir. rsync --copy-unsafe-links tries to dereference them and
fails with exit 23 (issue alibaba#798). Plain -a preserves symlinks as-is;
once init creates the original-path -> subvolume symlink, they resolve
correctly via chain resolution.

Signed-off-by: Ziqi Huang <ziqi02@alibaba-inc.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[ckpt] bug: workspace 包含指向自身的绝对路径 symlink 时,checkpoint (auto-init) rsync 失败 (exit code 23)

1 participant