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