1
2
3
4
5 package filepath
6
7 import (
8 "errors"
9 "io/fs"
10 "os"
11 "runtime"
12 "syscall"
13 )
14
15 func walkSymlinks(path string) (string, error) {
16 volLen := volumeNameLen(path)
17 pathSeparator := string(os.PathSeparator)
18
19 if volLen < len(path) && os.IsPathSeparator(path[volLen]) {
20 volLen++
21 }
22 vol := path[:volLen]
23 dest := vol
24 linksWalked := 0
25 for start, end := volLen, volLen; start < len(path); start = end {
26 for start < len(path) && os.IsPathSeparator(path[start]) {
27 start++
28 }
29 end = start
30 for end < len(path) && !os.IsPathSeparator(path[end]) {
31 end++
32 }
33
34
35
36
37 isWindowsDot := runtime.GOOS == "windows" && path[volumeNameLen(path):] == "."
38
39
40 if end == start {
41
42 break
43 } else if path[start:end] == "." && !isWindowsDot {
44
45 continue
46 } else if path[start:end] == ".." {
47
48
49
50
51
52 var r int
53 for r = len(dest) - 1; r >= volLen; r-- {
54 if os.IsPathSeparator(dest[r]) {
55 break
56 }
57 }
58 if r < volLen || dest[r+1:] == ".." {
59
60
61
62
63 if len(dest) > volLen {
64 dest += pathSeparator
65 }
66 dest += ".."
67 } else {
68
69 dest = dest[:r]
70 }
71 continue
72 }
73
74
75
76 if len(dest) > volumeNameLen(dest) && !os.IsPathSeparator(dest[len(dest)-1]) {
77 dest += pathSeparator
78 }
79
80 dest += path[start:end]
81
82
83
84 fi, err := os.Lstat(dest)
85 if err != nil {
86 return "", err
87 }
88
89 if fi.Mode()&fs.ModeSymlink == 0 {
90 if !fi.Mode().IsDir() && end < len(path) {
91 return "", syscall.ENOTDIR
92 }
93 continue
94 }
95
96
97
98 linksWalked++
99 if linksWalked > 255 {
100 return "", errors.New("EvalSymlinks: too many links")
101 }
102
103 link, err := os.Readlink(dest)
104 if err != nil {
105 return "", err
106 }
107
108 if isWindowsDot && !IsAbs(link) {
109
110
111 break
112 }
113
114 path = link + path[end:]
115
116 v := volumeNameLen(link)
117 if v > 0 {
118
119 if v < len(link) && os.IsPathSeparator(link[v]) {
120 v++
121 }
122 vol = link[:v]
123 dest = vol
124 end = len(vol)
125 } else if len(link) > 0 && os.IsPathSeparator(link[0]) {
126
127 dest = link[:1]
128 end = 1
129 } else {
130
131
132 var r int
133 for r = len(dest) - 1; r >= volLen; r-- {
134 if os.IsPathSeparator(dest[r]) {
135 break
136 }
137 }
138 if r < volLen {
139 dest = vol
140 } else {
141 dest = dest[:r]
142 }
143 end = 0
144 }
145 }
146 return Clean(dest), nil
147 }
148
View as plain text