mirror of
https://github.com/RuoJi6/ARTEX.git
synced 2026-10-27 17:03:49 +08:00
fix(task-archive): 遇到符号链接跳过而非整包失败
归档格式端到端只支持普通文件与目录(解包端对其它类型直接报错),原打包器遇到 任何 symlink 即整包失败(task archive refuses symlink),导致工作目录里一条软链 就卡死整个任务归档。改为跳过该符号链接并记日志:lstat 不跟随、不越出目录树、 不写 symlink 条目;链接指向树内时目标文件仍会被单独遍历归档。安全防护不变。 新增 TestTaskArchivePackageSkipsSymlink 覆盖。
This commit is contained in:
1 parent
33c26dc72a
commit
37950f4a6f
2 files changed
+39
-1
No files matched your search
@@ -9,6 +9,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
@@ -577,7 +578,11 @@ func writeTaskArchivePackage(path, payloadDir string, snapshot *pgdb.TaskArchive
|
||||
return err
|
||||
}
|
||||
if info.Mode()&os.ModeSymlink != 0 {
|
||||
return fmt.Errorf("task archive refuses symlink: %s", current)
|
||||
// 归档格式端到端只支持普通文件与目录(解包端对其它类型直接报错),
|
||||
// 无法还原符号链接。跳过而非整包失败:不读取链接目标(lstat,不越出目录树),
|
||||
// 也不写入 symlink 条目;链接指向树内时目标文件本身仍会被单独遍历归档。
|
||||
log.Printf("[task-archive] 跳过符号链接(归档不支持,不影响其它文件):%s", current)
|
||||
return nil
|
||||
}
|
||||
header, err := tar.FileInfoHeader(info, "")
|
||||
if err != nil {
|
||||
|
||||
@@ -94,6 +94,39 @@ func TestTaskArchivePackageFilesRoundTrip(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestTaskArchivePackageSkipsSymlink(t *testing.T) {
|
||||
dataDir := t.TempDir()
|
||||
payload := filepath.Join(dataDir, "payload")
|
||||
if err := os.MkdirAll(payload, archiveDirMode); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
regular := filepath.Join(payload, "keep.txt")
|
||||
if err := os.WriteFile(regular, []byte("keep me"), archiveFileMode); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// 工作目录里出现的符号链接应被跳过,而不是让整个归档失败。
|
||||
if err := os.Symlink(regular, filepath.Join(payload, "link.txt")); err != nil {
|
||||
t.Skipf("symlink unsupported on this platform: %v", err)
|
||||
}
|
||||
|
||||
archivePath := filepath.Join(dataDir, "archive.tar.zst")
|
||||
snapshot := &pgdb.TaskArchiveSnapshot{FormatVersion: pgdb.TaskArchiveFormatVersion, TaskID: 42}
|
||||
_, _, checksum, err := writeTaskArchivePackage(archivePath, payload, snapshot)
|
||||
if err != nil {
|
||||
t.Fatalf("archive should skip symlink, not fail: %v", err)
|
||||
}
|
||||
extracted := filepath.Join(dataDir, "restore")
|
||||
if err := extractTaskArchivePackage(archivePath, checksum, extracted); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got, err := os.ReadFile(filepath.Join(extracted, "keep.txt")); err != nil || string(got) != "keep me" {
|
||||
t.Fatalf("regular file not archived: got=%q err=%v", got, err)
|
||||
}
|
||||
if _, err := os.Lstat(filepath.Join(extracted, "link.txt")); !os.IsNotExist(err) {
|
||||
t.Fatalf("symlink should have been skipped, but link.txt exists: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTaskArchiveStageJournalRollsBackInterruptedMoves(t *testing.T) {
|
||||
dataDir := t.TempDir()
|
||||
taskFile := filepath.Join(dataDir, "tasks", "42", "evidence.txt")
|
||||
|
||||
Reference in new issue
Block a user