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, 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 ret, err = f(dirfd, parts[i])
166 if _, ok := err.(errSymlink); !ok {
167 return ret, err
168 }
169 } else {
170 var fd sysfdType
171 fd, err = rootOpenDir(dirfd, parts[i])
172 if err == nil {
173 if dirfd != rootfd {
174 syscall.Close(dirfd)
175 }
176 dirfd = fd
177 } else if _, ok := err.(errSymlink); !ok {
178 return ret, err
179 }
180 }
181
182 if e, ok := err.(errSymlink); ok {
183 symlinks++
184 if symlinks > rootMaxSymlinks {
185 return ret, syscall.ELOOP
186 }
187 newparts, err := splitPathInRoot(string(e), parts[:i], parts[i+1:])
188 if err != nil {
189 return ret, err
190 }
191 if len(newparts) < i || !slices.Equal(parts[:i], newparts[:i]) {
192
193
194 i = 0
195 if dirfd != rootfd {
196 syscall.Close(dirfd)
197 }
198 dirfd = rootfd
199 }
200 parts = newparts
201 continue
202 }
203
204 i++
205 }
206 }
207
208
209
210 type errSymlink string
211
212 func (errSymlink) Error() string { panic("errSymlink is not user-visible") }
213
View as plain text