Source file
src/os/file_unix.go
1
2
3
4
5
6
7 package os
8
9 import (
10 "internal/poll"
11 "internal/syscall/unix"
12 "io/fs"
13 "runtime"
14 "sync/atomic"
15 "syscall"
16 _ "unsafe"
17 )
18
19 const _UTIME_OMIT = unix.UTIME_OMIT
20
21
22 func fixLongPath(path string) string {
23 return path
24 }
25
26 func rename(oldname, newname string) error {
27 fi, err := Lstat(newname)
28 if err == nil && fi.IsDir() {
29
30
31
32
33
34
35
36
37 if ofi, err := Lstat(oldname); err != nil {
38 if pe, ok := err.(*PathError); ok {
39 err = pe.Err
40 }
41 return &LinkError{"rename", oldname, newname, err}
42 } else if newname == oldname || !SameFile(fi, ofi) {
43 return &LinkError{"rename", oldname, newname, syscall.EEXIST}
44 }
45 }
46 err = ignoringEINTR(func() error {
47 return syscall.Rename(oldname, newname)
48 })
49 if err != nil {
50 return &LinkError{"rename", oldname, newname, err}
51 }
52 return nil
53 }
54
55
56
57
58
59 type file struct {
60 pfd poll.FD
61 name string
62 dirinfo atomic.Pointer[dirInfo]
63 nonblock bool
64 stdoutOrErr bool
65 appendMode bool
66 }
67
68
69 func (f *File) fd() uintptr {
70 if f == nil {
71 return ^(uintptr(0))
72 }
73
74
75
76
77
78
79 if f.nonblock {
80 f.pfd.SetBlocking()
81 }
82
83 return uintptr(f.pfd.Sysfd)
84 }
85
86
87 func newFileFromNewFile(fd uintptr, name string) *File {
88 fdi := int(fd)
89 if fdi < 0 {
90 return nil
91 }
92
93 flags, err := unix.Fcntl(fdi, syscall.F_GETFL, 0)
94 if err != nil {
95 flags = 0
96 }
97 f := newFile(fdi, name, kindNewFile, unix.HasNonblockFlag(flags))
98 f.appendMode = flags&syscall.O_APPEND != 0
99 return f
100 }
101
102
103
104
105
106
107
108
109
110
111
112 func net_newUnixFile(fd int, name string) *File {
113 if fd < 0 {
114 panic("invalid FD")
115 }
116
117 return newFile(fd, name, kindSock, true)
118 }
119
120
121 type newFileKind int
122
123 const (
124
125 kindNewFile newFileKind = iota
126
127
128 kindOpenFile
129
130 kindPipe
131
132
133 kindSock
134
135
136
137 kindNoPoll
138 )
139
140
141
142
143 func newFile(fd int, name string, kind newFileKind, nonBlocking bool) *File {
144 f := &File{&file{
145 pfd: poll.FD{
146 Sysfd: fd,
147 IsStream: true,
148 ZeroReadIsEOF: true,
149 },
150 name: name,
151 stdoutOrErr: fd == 1 || fd == 2,
152 }}
153
154 pollable := kind == kindOpenFile || kind == kindPipe || kind == kindSock || nonBlocking
155
156
157
158
159
160
161
162
163
164 if kind == kindOpenFile {
165 switch runtime.GOOS {
166 case "darwin", "ios", "dragonfly", "freebsd", "netbsd", "openbsd":
167 var st syscall.Stat_t
168 err := ignoringEINTR(func() error {
169 return syscall.Fstat(fd, &st)
170 })
171 typ := st.Mode & syscall.S_IFMT
172
173
174
175
176
177
178
179 if err == nil && (typ == syscall.S_IFREG || typ == syscall.S_IFDIR) {
180 pollable = false
181 }
182
183
184
185
186
187 if (runtime.GOOS == "darwin" || runtime.GOOS == "ios") && typ == syscall.S_IFIFO {
188 pollable = false
189 }
190 }
191 }
192
193 clearNonBlock := false
194 if pollable {
195
196
197
198 if nonBlocking {
199
200 if kind == kindSock {
201 f.nonblock = true
202 }
203 } else if err := syscall.SetNonblock(fd, true); err == nil {
204 f.nonblock = true
205 clearNonBlock = true
206 } else {
207 pollable = false
208 }
209 }
210
211
212
213
214
215
216
217
218 if pollErr := f.pfd.Init("file", pollable); pollErr != nil && clearNonBlock {
219 if err := syscall.SetNonblock(fd, false); err == nil {
220 f.nonblock = false
221 }
222 }
223
224 runtime.SetFinalizer(f.file, (*file).close)
225 return f
226 }
227
228 func sigpipe()
229
230
231
232
233 func epipecheck(file *File, e error) {
234 if e == syscall.EPIPE && file.stdoutOrErr {
235 sigpipe()
236 }
237 }
238
239
240
241 const DevNull = "/dev/null"
242
243
244
245 func openFileNolog(name string, flag int, perm FileMode) (*File, error) {
246 setSticky := false
247 if !supportsCreateWithStickyBit && flag&O_CREATE != 0 && perm&ModeSticky != 0 {
248 if _, err := Stat(name); IsNotExist(err) {
249 setSticky = true
250 }
251 }
252
253 var (
254 r int
255 s poll.SysFile
256 e error
257 )
258
259 ignoringEINTR(func() error {
260 r, s, e = open(name, flag|syscall.O_CLOEXEC, syscallMode(perm))
261 return e
262 })
263 if e != nil {
264 return nil, &PathError{Op: "open", Path: name, Err: e}
265 }
266
267
268 if setSticky {
269 setStickyBit(name)
270 }
271
272
273
274 if !supportsCloseOnExec {
275 syscall.CloseOnExec(r)
276 }
277
278 f := newFile(r, name, kindOpenFile, unix.HasNonblockFlag(flag))
279 f.pfd.SysFile = s
280 return f, nil
281 }
282
283 func openDirNolog(name string) (*File, error) {
284 var (
285 r int
286 s poll.SysFile
287 e error
288 )
289 ignoringEINTR(func() error {
290 r, s, e = open(name, O_RDONLY|syscall.O_CLOEXEC|syscall.O_DIRECTORY, 0)
291 return e
292 })
293 if e != nil {
294 return nil, &PathError{Op: "open", Path: name, Err: e}
295 }
296
297 if !supportsCloseOnExec {
298 syscall.CloseOnExec(r)
299 }
300
301 f := newFile(r, name, kindNoPoll, false)
302 f.pfd.SysFile = s
303 return f, nil
304 }
305
306 func (file *file) close() error {
307 if file == nil {
308 return syscall.EINVAL
309 }
310 if info := file.dirinfo.Swap(nil); info != nil {
311 info.close()
312 }
313 var err error
314 if e := file.pfd.Close(); e != nil {
315 if e == poll.ErrFileClosing {
316 e = ErrClosed
317 }
318 err = &PathError{Op: "close", Path: file.name, Err: e}
319 }
320
321
322 runtime.SetFinalizer(file, nil)
323 return err
324 }
325
326
327
328
329
330 func (f *File) seek(offset int64, whence int) (ret int64, err error) {
331 if info := f.dirinfo.Swap(nil); info != nil {
332
333
334 info.close()
335 }
336 ret, err = f.pfd.Seek(offset, whence)
337 runtime.KeepAlive(f)
338 return ret, err
339 }
340
341
342
343
344 func Truncate(name string, size int64) error {
345 e := ignoringEINTR(func() error {
346 return syscall.Truncate(name, size)
347 })
348 if e != nil {
349 return &PathError{Op: "truncate", Path: name, Err: e}
350 }
351 return nil
352 }
353
354
355
356 func Remove(name string) error {
357
358
359
360
361 e := ignoringEINTR(func() error {
362 return syscall.Unlink(name)
363 })
364 if e == nil {
365 return nil
366 }
367 e1 := ignoringEINTR(func() error {
368 return syscall.Rmdir(name)
369 })
370 if e1 == nil {
371 return nil
372 }
373
374
375
376
377
378
379
380
381
382
383 if e1 != syscall.ENOTDIR {
384 e = e1
385 }
386 return &PathError{Op: "remove", Path: name, Err: e}
387 }
388
389 func tempDir() string {
390 dir := Getenv("TMPDIR")
391 if dir == "" {
392 if runtime.GOOS == "android" {
393 dir = "/data/local/tmp"
394 } else {
395 dir = "/tmp"
396 }
397 }
398 return dir
399 }
400
401
402
403 func Link(oldname, newname string) error {
404 e := ignoringEINTR(func() error {
405 return syscall.Link(oldname, newname)
406 })
407 if e != nil {
408 return &LinkError{"link", oldname, newname, e}
409 }
410 return nil
411 }
412
413
414
415
416
417 func Symlink(oldname, newname string) error {
418 e := ignoringEINTR(func() error {
419 return syscall.Symlink(oldname, newname)
420 })
421 if e != nil {
422 return &LinkError{"symlink", oldname, newname, e}
423 }
424 return nil
425 }
426
427 func readlink(name string) (string, error) {
428 for len := 128; ; len *= 2 {
429 b := make([]byte, len)
430 n, err := ignoringEINTR2(func() (int, error) {
431 return fixCount(syscall.Readlink(name, b))
432 })
433
434 if (runtime.GOOS == "aix" || runtime.GOOS == "wasip1") && err == syscall.ERANGE {
435 continue
436 }
437 if err != nil {
438 return "", &PathError{Op: "readlink", Path: name, Err: err}
439 }
440 if n < len {
441 return string(b[0:n]), nil
442 }
443 }
444 }
445
446 type unixDirent struct {
447 parent string
448 name string
449 typ FileMode
450 info FileInfo
451 }
452
453 func (d *unixDirent) Name() string { return d.name }
454 func (d *unixDirent) IsDir() bool { return d.typ.IsDir() }
455 func (d *unixDirent) Type() FileMode { return d.typ }
456
457 func (d *unixDirent) Info() (FileInfo, error) {
458 if d.info != nil {
459 return d.info, nil
460 }
461 return lstat(d.parent + "/" + d.name)
462 }
463
464 func (d *unixDirent) String() string {
465 return fs.FormatDirEntry(d)
466 }
467
468 func newUnixDirent(parent, name string, typ FileMode) (DirEntry, error) {
469 ude := &unixDirent{
470 parent: parent,
471 name: name,
472 typ: typ,
473 }
474 if typ != ^FileMode(0) {
475 return ude, nil
476 }
477
478 info, err := lstat(parent + "/" + name)
479 if err != nil {
480 return nil, err
481 }
482
483 ude.typ = info.Mode().Type()
484 ude.info = info
485 return ude, nil
486 }
487
View as plain text