Source file
src/os/root_openat.go
1
2
3
4
5
6
7 package os
8
9 import (
10 "runtime"
11 "slices"
12 "sync"
13 "syscall"
14 )
15
16
17
18 type root struct {
19 name string
20
21
22
23
24 mu sync.Mutex
25 fd sysfdType
26 refs int
27 closed bool
28 }
29
30 func (r *root) Close() error {
31 r.mu.Lock()
32 defer r.mu.Unlock()
33 if !r.closed && r.refs == 0 {
34 syscall.Close(r.fd)
35 }
36 r.closed = true
37 runtime.SetFinalizer(r, nil)
38 return nil
39 }
40
41 func (r *root) incref() error {
42 r.mu.Lock()
43 defer r.mu.Unlock()
44 if r.closed {
45 return ErrClosed
46 }
47 r.refs++
48 return nil
49 }
50
51 func (r *root) decref() {
52 r.mu.Lock()
53 defer r.mu.Unlock()
54 if r.refs <= 0 {
55 panic("bad Root refcount")
56 }
57 r.refs--
58 if r.closed && r.refs == 0 {
59 syscall.Close(r.fd)
60 }
61 }
62
63 func (r *root) Name() string {
64 return r.name
65 }
66
67 func rootMkdir(r *Root, name string, perm FileMode) error {
68 _, err := doInRoot(r, name, func(parent sysfdType, name string) (struct{}, error) {
69 return struct{}{}, mkdirat(parent, name, perm)
70 })
71 if err != nil {
72 return &PathError{Op: "mkdirat", Path: name, Err: err}
73 }
74 return err
75 }
76
77 func rootRemove(r *Root, name string) error {
78 _, err := doInRoot(r, name, func(parent sysfdType, name string) (struct{}, error) {
79 return struct{}{}, removeat(parent, name)
80 })
81 if err != nil {
82 return &PathError{Op: "removeat", Path: name, Err: err}
83 }
84 return err
85 }
86
87
88
89
90
91
92
93
94
95 func doInRoot[T any](r *Root, name string, f func(parent sysfdType, name string) (T, error)) (ret T, err error) {
96 if err := r.root.incref(); err != nil {
97 return ret, err
98 }
99 defer r.root.decref()
100
101 parts, suffixSep, err := splitPathInRoot(name, nil, nil)
102 if err != nil {
103 return ret, err
104 }
105
106 rootfd := r.root.fd
107 dirfd := rootfd
108 defer func() {
109 if dirfd != rootfd {
110 syscall.Close(dirfd)
111 }
112 }()
113
114
115
116
117
118
119
120 const maxSteps = 255
121 const maxRestarts = 8
122
123 i := 0
124 steps := 0
125 restarts := 0
126 symlinks := 0
127 for {
128 steps++
129 if steps > maxSteps && restarts > maxRestarts {
130 return ret, syscall.ENAMETOOLONG
131 }
132
133 if parts[i] == ".." {
134
135
136
137
138
139 restarts++
140 end := i + 1
141 for end < len(parts) && parts[end] == ".." {
142 end++
143 }
144 count := end - i
145 if count > i {
146 return ret, errPathEscapes
147 }
148 parts = slices.Delete(parts, i-count, end)
149 if len(parts) == 0 {
150 parts = []string{"."}
151 }
152 i = 0
153 if dirfd != rootfd {
154 syscall.Close(dirfd)
155 }
156 dirfd = rootfd
157 continue
158 }
159
160 if i == len(parts)-1 {
161
162
163
164
165
166
167 ret, err = f(dirfd, parts[i]+suffixSep)
168 if _, ok := err.(errSymlink); !ok {
169 return ret, err
170 }
171 } else {
172 var fd sysfdType
173 fd, err = rootOpenDir(dirfd, parts[i])
174 if err == nil {
175 if dirfd != rootfd {
176 syscall.Close(dirfd)
177 }
178 dirfd = fd
179 } else if _, ok := err.(errSymlink); !ok {
180 return ret, err
181 }
182 }
183
184 if e, ok := err.(errSymlink); ok {
185 symlinks++
186 if symlinks > rootMaxSymlinks {
187 return ret, syscall.ELOOP
188 }
189 newparts, newSuffixSep, err := splitPathInRoot(string(e), parts[:i], parts[i+1:])
190 if err != nil {
191 return ret, err
192 }
193 if i == len(parts)-1 {
194
195
196
197
198
199
200 suffixSep = newSuffixSep
201 }
202 if len(newparts) < i || !slices.Equal(parts[:i], newparts[:i]) {
203
204
205 i = 0
206 if dirfd != rootfd {
207 syscall.Close(dirfd)
208 }
209 dirfd = rootfd
210 }
211 parts = newparts
212 continue
213 }
214
215 i++
216 }
217 }
218
219
220
221 type errSymlink string
222
223 func (errSymlink) Error() string { panic("errSymlink is not user-visible") }
224
View as plain text