Source file
src/os/root_unix.go
1
2
3
4
5
6
7 package os
8
9 import (
10 "errors"
11 "internal/syscall/unix"
12 "runtime"
13 "syscall"
14 "time"
15 )
16
17
18
19
20 type sysfdType = int
21
22
23 func openRootNolog(name string) (*Root, error) {
24 var fd int
25 err := ignoringEINTR(func() error {
26 var err error
27 fd, _, err = open(name, syscall.O_CLOEXEC, 0)
28 return err
29 })
30 if err != nil {
31 return nil, &PathError{Op: "open", Path: name, Err: err}
32 }
33 return newRoot(fd, name)
34 }
35
36
37
38 func newRoot(fd int, name string) (*Root, error) {
39 var fs fileStat
40 err := ignoringEINTR(func() error {
41 return syscall.Fstat(fd, &fs.sys)
42 })
43 fillFileStatFromSys(&fs, name)
44 if err == nil && !fs.IsDir() {
45 syscall.Close(fd)
46 return nil, &PathError{Op: "open", Path: name, Err: errors.New("not a directory")}
47 }
48
49
50
51 if !supportsCloseOnExec {
52 syscall.CloseOnExec(fd)
53 }
54
55 r := &Root{&root{
56 fd: fd,
57 name: name,
58 }}
59 r.root.cleanup = runtime.AddCleanup(r, func(f *root) { f.Close() }, r.root)
60 return r, nil
61 }
62
63
64 func openRootInRoot(r *Root, name string) (*Root, error) {
65 fd, err := doInRoot(r, name, nil, func(parent int, name string) (fd int, err error) {
66 ignoringEINTR(func() error {
67 fd, err = unix.Openat(parent, name, syscall.O_NOFOLLOW|syscall.O_CLOEXEC, 0)
68 if isNoFollowErr(err) {
69 err = checkSymlink(parent, name, err)
70 }
71 return err
72 })
73 return fd, err
74 })
75 if err != nil {
76 return nil, &PathError{Op: "openat", Path: name, Err: err}
77 }
78 return newRoot(fd, name)
79 }
80
81
82 func rootOpenFileNolog(root *Root, name string, flag int, perm FileMode) (*File, error) {
83 fd, err := doInRoot(root, name, nil, func(parent int, name string) (fd int, err error) {
84 ignoringEINTR(func() error {
85 fd, err = unix.Openat(parent, name, syscall.O_NOFOLLOW|syscall.O_CLOEXEC|flag, uint32(perm))
86 if err != nil {
87
88
89 isCreateExcl := flag&(O_CREATE|O_EXCL) == (O_CREATE | O_EXCL)
90 if !isCreateExcl && (isNoFollowErr(err) || err == syscall.ENOTDIR) {
91 err = checkSymlink(parent, name, err)
92 }
93
94
95 if isCreateExcl && err == syscall.ELOOP {
96 err = syscall.EEXIST
97 }
98 }
99 return err
100 })
101 return fd, err
102 })
103 if err != nil {
104 return nil, &PathError{Op: "openat", Path: name, Err: err}
105 }
106 f := newFile(fd, joinPath(root.Name(), name), kindOpenFile, unix.HasNonblockFlag(flag))
107 return f, nil
108 }
109
110 func rootOpenDir(parent int, name string) (int, error) {
111 var (
112 fd int
113 err error
114 )
115 ignoringEINTR(func() error {
116 fd, err = unix.Openat(parent, name, syscall.O_NOFOLLOW|syscall.O_CLOEXEC|syscall.O_DIRECTORY, 0)
117 if isNoFollowErr(err) || err == syscall.ENOTDIR {
118 err = checkSymlink(parent, name, err)
119 } else if err == syscall.ENOTSUP || err == syscall.EOPNOTSUPP {
120
121
122
123 err = syscall.ENOTDIR
124 }
125 return err
126 })
127 return fd, err
128 }
129
130 func rootStat(r *Root, name string, lstat bool) (FileInfo, error) {
131 fi, err := doInRoot(r, name, nil, func(parent sysfdType, n string) (FileInfo, error) {
132 var fs fileStat
133 if err := unix.Fstatat(parent, n, &fs.sys, unix.AT_SYMLINK_NOFOLLOW); err != nil {
134 return nil, err
135 }
136 fillFileStatFromSys(&fs, name)
137 if !lstat && fs.Mode()&ModeSymlink != 0 {
138 return nil, checkSymlink(parent, n, syscall.ELOOP)
139 }
140 return &fs, nil
141 })
142 if err != nil {
143 return nil, &PathError{Op: "statat", Path: name, Err: err}
144 }
145 return fi, nil
146 }
147
148 func rootSymlink(r *Root, oldname, newname string) error {
149 _, err := doInRoot(r, newname, nil, func(parent sysfdType, name string) (struct{}, error) {
150 return struct{}{}, symlinkat(oldname, parent, name)
151 })
152 if err != nil {
153 return &LinkError{"symlinkat", oldname, newname, err}
154 }
155 return nil
156 }
157
158
159
160
161
162
163
164
165
166
167
168
169 func afterResolvingSymlink(parent int, name string, f func() error) error {
170 if err := checkSymlink(parent, name, nil); err != nil {
171 return err
172 }
173 return f()
174 }
175
176 func chmodat(parent int, name string, mode FileMode) error {
177 return afterResolvingSymlink(parent, name, func() error {
178 return ignoringEINTR(func() error {
179 return unix.Fchmodat(parent, name, syscallMode(mode), unix.AT_SYMLINK_NOFOLLOW)
180 })
181 })
182 }
183
184 func chownat(parent int, name string, uid, gid int) error {
185 return afterResolvingSymlink(parent, name, func() error {
186 return ignoringEINTR(func() error {
187 return unix.Fchownat(parent, name, uid, gid, unix.AT_SYMLINK_NOFOLLOW)
188 })
189 })
190 }
191
192 func lchownat(parent int, name string, uid, gid int) error {
193 return ignoringEINTR(func() error {
194 return unix.Fchownat(parent, name, uid, gid, unix.AT_SYMLINK_NOFOLLOW)
195 })
196 }
197
198 func chtimesat(parent int, name string, atime time.Time, mtime time.Time) error {
199 return afterResolvingSymlink(parent, name, func() error {
200 return ignoringEINTR(func() error {
201 utimes := chtimesUtimes(atime, mtime)
202 return unix.Utimensat(parent, name, &utimes, unix.AT_SYMLINK_NOFOLLOW)
203 })
204 })
205 }
206
207 func mkdirat(fd int, name string, perm FileMode) error {
208 return ignoringEINTR(func() error {
209 return unix.Mkdirat(fd, name, syscallMode(perm))
210 })
211 }
212
213 func removeat(fd int, name string) error {
214
215
216 e := ignoringEINTR(func() error {
217 return unix.Unlinkat(fd, name, 0)
218 })
219 if e == nil {
220 return nil
221 }
222 e1 := ignoringEINTR(func() error {
223 return unix.Unlinkat(fd, name, unix.AT_REMOVEDIR)
224 })
225 if e1 == nil {
226 return nil
227 }
228
229 if e1 != syscall.ENOTDIR {
230 return e1
231 }
232 return e
233 }
234
235 func removefileat(fd int, name string) error {
236 return ignoringEINTR(func() error {
237 return unix.Unlinkat(fd, name, 0)
238 })
239 }
240
241 func removedirat(fd int, name string) error {
242 return ignoringEINTR(func() error {
243 return unix.Unlinkat(fd, name, unix.AT_REMOVEDIR)
244 })
245 }
246
247 func renameat(oldfd int, oldname string, newfd int, newname string) error {
248 return unix.Renameat(oldfd, oldname, newfd, newname)
249 }
250
251 func linkat(oldfd int, oldname string, newfd int, newname string) error {
252 return unix.Linkat(oldfd, oldname, newfd, newname, 0)
253 }
254
255 func symlinkat(oldname string, newfd int, newname string) error {
256 return unix.Symlinkat(oldname, newfd, newname)
257 }
258
259 func modeAt(parent int, name string) (FileMode, error) {
260 var fs fileStat
261 if err := unix.Fstatat(parent, name, &fs.sys, unix.AT_SYMLINK_NOFOLLOW); err != nil {
262 return 0, err
263 }
264 fillFileStatFromSys(&fs, name)
265 return fs.mode, nil
266 }
267
268
269
270
271
272 func checkSymlink(parent int, name string, origError error) error {
273 link, err := readlinkat(parent, name)
274 if err != nil {
275 return origError
276 }
277 return errSymlink(link)
278 }
279
280 func readlinkat(fd int, name string) (string, error) {
281 for len := 128; ; len *= 2 {
282 b := make([]byte, len)
283 var (
284 n int
285 e error
286 )
287 ignoringEINTR(func() error {
288 n, e = unix.Readlinkat(fd, name, b)
289 return e
290 })
291 if e == syscall.ERANGE {
292 continue
293 }
294 if e != nil {
295 return "", e
296 }
297 n = max(n, 0)
298 if n < len {
299 return string(b[0:n]), nil
300 }
301 }
302 }
303
View as plain text