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 )
15
16 type sysfdType = int
17
18
19 func openRootNolog(name string) (*Root, error) {
20 var fd int
21 err := ignoringEINTR(func() error {
22 var err error
23 fd, _, err = open(name, syscall.O_CLOEXEC, 0)
24 return err
25 })
26 if err != nil {
27 return nil, &PathError{Op: "open", Path: name, Err: err}
28 }
29 return newRoot(fd, name)
30 }
31
32
33
34 func newRoot(fd int, name string) (*Root, error) {
35 var fs fileStat
36 err := ignoringEINTR(func() error {
37 return syscall.Fstat(fd, &fs.sys)
38 })
39 fillFileStatFromSys(&fs, name)
40 if err == nil && !fs.IsDir() {
41 syscall.Close(fd)
42 return nil, &PathError{Op: "open", Path: name, Err: errors.New("not a directory")}
43 }
44
45
46
47 if !supportsCloseOnExec {
48 syscall.CloseOnExec(fd)
49 }
50
51 r := &Root{root{
52 fd: fd,
53 name: name,
54 }}
55 runtime.SetFinalizer(&r.root, (*root).Close)
56 return r, nil
57 }
58
59
60 func openRootInRoot(r *Root, name string) (*Root, error) {
61 fd, err := doInRoot(r, name, func(parent int, name string) (fd int, err error) {
62 ignoringEINTR(func() error {
63 fd, err = unix.Openat(parent, name, syscall.O_NOFOLLOW|syscall.O_CLOEXEC, 0)
64 if isNoFollowErr(err) {
65 err = checkSymlink(parent, name, err)
66 }
67 return err
68 })
69 return fd, err
70 })
71 if err != nil {
72 return nil, &PathError{Op: "openat", Path: name, Err: err}
73 }
74 return newRoot(fd, name)
75 }
76
77
78 func rootOpenFileNolog(root *Root, name string, flag int, perm FileMode) (*File, error) {
79 fd, err := doInRoot(root, name, func(parent int, name string) (fd int, err error) {
80 ignoringEINTR(func() error {
81 fd, err = unix.Openat(parent, name, syscall.O_NOFOLLOW|syscall.O_CLOEXEC|flag, uint32(perm))
82 if isNoFollowErr(err) || err == syscall.ENOTDIR {
83 err = checkSymlink(parent, name, err)
84 }
85 return err
86 })
87 return fd, err
88 })
89 if err != nil {
90 return nil, &PathError{Op: "openat", Path: name, Err: err}
91 }
92 f := newFile(fd, joinPath(root.Name(), name), kindOpenFile, unix.HasNonblockFlag(flag))
93 return f, nil
94 }
95
96 func rootOpenDir(parent int, name string) (int, error) {
97 var (
98 fd int
99 err error
100 )
101 ignoringEINTR(func() error {
102 fd, err = unix.Openat(parent, name, syscall.O_NOFOLLOW|syscall.O_CLOEXEC|syscall.O_DIRECTORY, 0)
103 if isNoFollowErr(err) || err == syscall.ENOTDIR {
104 err = checkSymlink(parent, name, err)
105 } else if err == syscall.ENOTSUP || err == syscall.EOPNOTSUPP {
106
107
108
109 err = syscall.ENOTDIR
110 }
111 return err
112 })
113 return fd, err
114 }
115
116 func rootStat(r *Root, name string, lstat bool) (FileInfo, error) {
117 fi, err := doInRoot(r, name, func(parent sysfdType, n string) (FileInfo, error) {
118 var fs fileStat
119 if err := unix.Fstatat(parent, n, &fs.sys, unix.AT_SYMLINK_NOFOLLOW); err != nil {
120 return nil, err
121 }
122 fillFileStatFromSys(&fs, name)
123 if !lstat && fs.Mode()&ModeSymlink != 0 {
124 return nil, checkSymlink(parent, n, syscall.ELOOP)
125 }
126 return &fs, nil
127 })
128 if err != nil {
129 return nil, &PathError{Op: "statat", Path: name, Err: err}
130 }
131 return fi, nil
132 }
133
134 func mkdirat(fd int, name string, perm FileMode) error {
135 return ignoringEINTR(func() error {
136 return unix.Mkdirat(fd, name, syscallMode(perm))
137 })
138 }
139
140 func removeat(fd int, name string) error {
141
142
143 e := ignoringEINTR(func() error {
144 return unix.Unlinkat(fd, name, 0)
145 })
146 if e == nil {
147 return nil
148 }
149 e1 := ignoringEINTR(func() error {
150 return unix.Unlinkat(fd, name, unix.AT_REMOVEDIR)
151 })
152 if e1 == nil {
153 return nil
154 }
155
156 if e1 != syscall.ENOTDIR {
157 return e1
158 }
159 return e
160 }
161
162
163
164
165
166 func checkSymlink(parent int, name string, origError error) error {
167 link, err := readlinkat(parent, name)
168 if err != nil {
169 return origError
170 }
171 return errSymlink(link)
172 }
173
174 func readlinkat(fd int, name string) (string, error) {
175 for len := 128; ; len *= 2 {
176 b := make([]byte, len)
177 var (
178 n int
179 e error
180 )
181 ignoringEINTR(func() error {
182 n, e = unix.Readlinkat(fd, name, b)
183 return e
184 })
185 if e == syscall.ERANGE {
186 continue
187 }
188 if e != nil {
189 return "", e
190 }
191 if n < 0 {
192 n = 0
193 }
194 if n < len {
195 return string(b[0:n]), nil
196 }
197 }
198 }
199
View as plain text