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