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 runtime.SetFinalizer(r.root, (*root).Close)
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, joinPath(r.Name(), 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 f.inRoot = true
108 return f, nil
109 }
110
111 func rootOpenDir(parent int, name string) (int, error) {
112 var (
113 fd int
114 err error
115 )
116 ignoringEINTR(func() error {
117 fd, err = unix.Openat(parent, name, syscall.O_NOFOLLOW|syscall.O_CLOEXEC|syscall.O_DIRECTORY, 0)
118 if isNoFollowErr(err) || err == syscall.ENOTDIR {
119 err = checkSymlink(parent, name, err)
120 } else if err == syscall.ENOTSUP || err == syscall.EOPNOTSUPP {
121
122
123
124 err = syscall.ENOTDIR
125 }
126 return err
127 })
128 return fd, err
129 }
130
131 func rootStat(r *Root, name string, lstat bool) (FileInfo, error) {
132 fi, err := doInRoot(r, name, nil, func(parent sysfdType, n string) (FileInfo, error) {
133 var fs fileStat
134 if err := unix.Fstatat(parent, n, &fs.sys, unix.AT_SYMLINK_NOFOLLOW); err != nil {
135 return nil, err
136 }
137 fillFileStatFromSys(&fs, name)
138 if !lstat && fs.Mode()&ModeSymlink != 0 {
139 return nil, checkSymlink(parent, n, syscall.ELOOP)
140 }
141 return &fs, nil
142 })
143 if err != nil {
144 return nil, &PathError{Op: "statat", Path: name, Err: err}
145 }
146 return fi, nil
147 }
148
149 func rootSymlink(r *Root, oldname, newname string) error {
150 _, err := doInRoot(r, newname, nil, func(parent sysfdType, name string) (struct{}, error) {
151 return struct{}{}, symlinkat(oldname, parent, name)
152 })
153 if err != nil {
154 return &LinkError{"symlinkat", oldname, newname, err}
155 }
156 return nil
157 }
158
159
160
161
162
163
164
165
166
167
168
169
170 func afterResolvingSymlink(parent int, name string, f func() error) error {
171 if err := checkSymlink(parent, name, nil); err != nil {
172 return err
173 }
174 return f()
175 }
176
177 func chmodat(parent int, name string, mode FileMode) error {
178 return afterResolvingSymlink(parent, name, func() error {
179 return ignoringEINTR(func() error {
180 return unix.Fchmodat(parent, name, syscallMode(mode), unix.AT_SYMLINK_NOFOLLOW)
181 })
182 })
183 }
184
185 func chownat(parent int, name string, uid, gid int) error {
186 return afterResolvingSymlink(parent, name, func() error {
187 return ignoringEINTR(func() error {
188 return unix.Fchownat(parent, name, uid, gid, unix.AT_SYMLINK_NOFOLLOW)
189 })
190 })
191 }
192
193 func lchownat(parent int, name string, uid, gid int) error {
194 return ignoringEINTR(func() error {
195 return unix.Fchownat(parent, name, uid, gid, unix.AT_SYMLINK_NOFOLLOW)
196 })
197 }
198
199 func chtimesat(parent int, name string, atime time.Time, mtime time.Time) error {
200 return afterResolvingSymlink(parent, name, func() error {
201 return ignoringEINTR(func() error {
202 utimes := chtimesUtimes(atime, mtime)
203 return unix.Utimensat(parent, name, &utimes, unix.AT_SYMLINK_NOFOLLOW)
204 })
205 })
206 }
207
208 func mkdirat(fd int, name string, perm FileMode) error {
209 return ignoringEINTR(func() error {
210 return unix.Mkdirat(fd, name, syscallMode(perm))
211 })
212 }
213
214 func removeat(fd int, name string) error {
215
216
217 e := ignoringEINTR(func() error {
218 return unix.Unlinkat(fd, name, 0)
219 })
220 if e == nil {
221 return nil
222 }
223 e1 := ignoringEINTR(func() error {
224 return unix.Unlinkat(fd, name, unix.AT_REMOVEDIR)
225 })
226 if e1 == nil {
227 return nil
228 }
229
230 if e1 != syscall.ENOTDIR {
231 return e1
232 }
233 return e
234 }
235
236 func removefileat(fd int, name string) error {
237 return ignoringEINTR(func() error {
238 return unix.Unlinkat(fd, name, 0)
239 })
240 }
241
242 func removedirat(fd int, name string) error {
243 return ignoringEINTR(func() error {
244 return unix.Unlinkat(fd, name, unix.AT_REMOVEDIR)
245 })
246 }
247
248 func renameat(oldfd int, oldname string, newfd int, newname string) error {
249 return unix.Renameat(oldfd, oldname, newfd, newname)
250 }
251
252 func linkat(oldfd int, oldname string, newfd int, newname string) error {
253 return unix.Linkat(oldfd, oldname, newfd, newname, 0)
254 }
255
256 func symlinkat(oldname string, newfd int, newname string) error {
257 return unix.Symlinkat(oldname, newfd, newname)
258 }
259
260 func modeAt(parent int, name string) (FileMode, error) {
261 var fs fileStat
262 if err := unix.Fstatat(parent, name, &fs.sys, unix.AT_SYMLINK_NOFOLLOW); err != nil {
263 return 0, err
264 }
265 fillFileStatFromSys(&fs, name)
266 return fs.mode, nil
267 }
268
269
270
271
272
273 func checkSymlink(parent int, name string, origError error) error {
274 link, err := readlinkat(parent, name)
275 if err != nil {
276 return origError
277 }
278 return errSymlink(link)
279 }
280
281 func readlinkat(fd int, name string) (string, error) {
282 for len := 128; ; len *= 2 {
283 b := make([]byte, len)
284 var (
285 n int
286 e error
287 )
288 ignoringEINTR(func() error {
289 n, e = unix.Readlinkat(fd, name, b)
290 return e
291 })
292 if e == syscall.ERANGE {
293 continue
294 }
295 if e != nil {
296 return "", e
297 }
298 n = max(n, 0)
299 if n < len {
300 return string(b[0:n]), nil
301 }
302 }
303 }
304
View as plain text