返回 DeepSeek-Reasonix
plan_tree.go
根目录 / internal / repair / plan_tree.go
1 package repair
2
3 import (
4 "fmt"
5 "os"
6 "path"
7 "strings"
8 )
9
10 func repairPlanTreePayloadStateID(root string) (string, error) {
11 return repairPlanTreeDigest(root, repairPlanTreePayloadEntries)
12 }
13
14 // AppBundlePayloadTreeDigest is the macOS handoff identity that ignores POSIX
15 // mode bits and AppleDouble sidecar files.
16 func AppBundlePayloadTreeDigest(path string) (string, error) {
17 return repairPlanTreePayloadStateID(path)
18 }
19
20 func repairPlanTreeDigest(root string, adapt func([]repairPlanTreeEntry) []repairPlanTreeEntry) (string, error) {
21 info, err := os.Lstat(root)
22 if err != nil {
23 return "", err
24 }
25 if !info.IsDir() {
26 return "", fmt.Errorf("expected directory, got %s", info.Mode().Type())
27 }
28 entries, err := repairPlanTreeEntries(root)
29 if err != nil {
30 return "", err
31 }
32 if adapt != nil {
33 entries = adapt(entries)
34 }
35 for _, entry := range entries {
36 switch entry.Kind {
37 case "unreadable", "file-unreadable", "symlink-unreadable":
38 return "", fmt.Errorf("cannot read bundle entry %q", entry.Rel)
39 case "other":
40 return "", fmt.Errorf("unsupported bundle entry %q", entry.Rel)
41 }
42 }
43 return repairPlanStateID(entries), nil
44 }
45
46 func repairPlanTreeHandoffAppMatches(path, expected string) (bool, error) {
47 payload, err := repairPlanTreePayloadStateID(path)
48 if err != nil {
49 return false, err
50 }
51 if payload == expected {
52 return true, nil
53 }
54 strict, err := repairPlanTreeContentStateID(path)
55 if err != nil {
56 return false, err
57 }
58 return strict == expected, nil
59 }
60
61 // repairPlanTreePayloadEntries drops POSIX mode bits and AppleDouble sidecar
62 // files that ditto cannot preserve on volumes such as exFAT.
63 func repairPlanTreePayloadEntries(entries []repairPlanTreeEntry) []repairPlanTreeEntry {
64 have := make(map[string]struct{}, len(entries))
65 for _, entry := range entries {
66 have[entry.Rel] = struct{}{}
67 }
68 out := make([]repairPlanTreeEntry, 0, len(entries))
69 for _, entry := range entries {
70 if entry.Kind == "file" && appleDoubleSidecarRel(entry.Rel, have) {
71 continue
72 }
73 entry.Mode = 0
74 out = append(out, entry)
75 }
76 return out
77 }
78
79 func appleDoubleSidecarRel(rel string, have map[string]struct{}) bool {
80 base := path.Base(rel)
81 if !strings.HasPrefix(base, "._") || base == "._" {
82 return false
83 }
84 sibling := strings.TrimPrefix(base, "._")
85 if dir := path.Dir(rel); dir != "." {
86 sibling = dir + "/" + sibling
87 }
88 _, ok := have[sibling]
89 return ok
90 }
91
91 lines GO