1// Copyright 2009 The Go Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE file. 4 5//go:build unix || (js && wasm) || wasip1 6 7package os 8 9import ( 10 "internal/poll" 11 "internal/syscall/unix" 12 "io/fs" 13 "runtime" 14 "sync/atomic" 15 "syscall" 16 _ "unsafe" // for go:linkname 17) 18 19const _UTIME_OMIT = unix.UTIME_OMIT 20 21// fixLongPath is a noop on non-Windows platforms. 22func fixLongPath(path string) string { 23 return path 24} 25 26func rename(oldname, newname string) error { 27 fi, err := Lstat(newname) 28 if err == nil && fi.IsDir() { 29 // There are two independent errors this function can return: 30 // one for a bad oldname, and one for a bad newname. 31 // At this point we've determined the newname is bad. 32 // But just in case oldname is also bad, prioritize returning 33 // the oldname error because that's what we did historically. 34 // However, if the old name and new name are not the same, yet 35 // they refer to the same file, it implies a case-only 36 // rename on a case-insensitive filesystem, which is ok. 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// file is the real representation of *File. 56// The extra level of indirection ensures that no clients of os 57// can overwrite this data, which could cause the finalizer 58// to close the wrong file descriptor. 59type file struct { 60 pfd poll.FD 61 name string 62 dirinfo atomic.Pointer[dirInfo] // nil unless directory being read 63 nonblock bool // whether we set nonblocking mode 64 stdoutOrErr bool // whether this is stdout or stderr 65 appendMode bool // whether file is opened for appending 66} 67 68// Fd returns the integer Unix file descriptor referencing the open file. 69// If f is closed, the file descriptor becomes invalid. 70// If f is garbage collected, a finalizer may close the file descriptor, 71// making it invalid; see [runtime.SetFinalizer] for more information on when 72// a finalizer might be run. On Unix systems this will cause the [File.SetDeadline] 73// methods to stop working. 74// Because file descriptors can be reused, the returned file descriptor may 75// only be closed through the [File.Close] method of f, or by its finalizer during 76// garbage collection. Otherwise, during garbage collection the finalizer 77// may close an unrelated file descriptor with the same (reused) number. 78// 79// As an alternative, see the f.SyscallConn method. 80func (f *File) Fd() uintptr { 81 if f == nil { 82 return ^(uintptr(0)) 83 } 84 85 // If we put the file descriptor into nonblocking mode, 86 // then set it to blocking mode before we return it, 87 // because historically we have always returned a descriptor 88 // opened in blocking mode. The File will continue to work, 89 // but any blocking operation will tie up a thread. 90 if f.nonblock { 91 f.pfd.SetBlocking() 92 } 93 94 return uintptr(f.pfd.Sysfd) 95} 96 97// NewFile returns a new File with the given file descriptor and 98// name. The returned value will be nil if fd is not a valid file 99// descriptor. On Unix systems, if the file descriptor is in 100// non-blocking mode, NewFile will attempt to return a pollable File 101// (one for which the SetDeadline methods work). 102// 103// After passing it to NewFile, fd may become invalid under the same 104// conditions described in the comments of the Fd method, and the same 105// constraints apply. 106func NewFile(fd uintptr, name string) *File { 107 fdi := int(fd) 108 if fdi < 0 { 109 return nil 110 } 111 112 flags, err := unix.Fcntl(fdi, syscall.F_GETFL, 0) 113 if err != nil { 114 flags = 0 115 } 116 f := newFile(fdi, name, kindNewFile, unix.HasNonblockFlag(flags)) 117 f.appendMode = flags&syscall.O_APPEND != 0 118 return f 119} 120 121// net_newUnixFile is a hidden entry point called by net.conn.File. 122// This is used so that a nonblocking network connection will become 123// blocking if code calls the Fd method. We don't want that for direct 124// calls to NewFile: passing a nonblocking descriptor to NewFile should 125// remain nonblocking if you get it back using Fd. But for net.conn.File 126// the call to NewFile is hidden from the user. Historically in that case 127// the Fd method has returned a blocking descriptor, and we want to 128// retain that behavior because existing code expects it and depends on it. 129// 130//go:linkname net_newUnixFile net.newUnixFile 131func net_newUnixFile(fd int, name string) *File { 132 if fd < 0 { 133 panic("invalid FD") 134 } 135 136 return newFile(fd, name, kindSock, true) 137} 138 139// newFileKind describes the kind of file to newFile. 140type newFileKind int 141 142const ( 143 // kindNewFile means that the descriptor was passed to us via NewFile. 144 kindNewFile newFileKind = iota 145 // kindOpenFile means that the descriptor was opened using 146 // Open, Create, or OpenFile. 147 kindOpenFile 148 // kindPipe means that the descriptor was opened using Pipe. 149 kindPipe 150 // kindSock means that the descriptor is a network file descriptor 151 // that was created from net package and was opened using net_newUnixFile. 152 kindSock 153 // kindNoPoll means that we should not put the descriptor into 154 // non-blocking mode, because we know it is not a pipe or FIFO. 155 // Used by openDirAt and openDirNolog for directories. 156 kindNoPoll 157) 158 159// newFile is like NewFile, but if called from OpenFile or Pipe 160// (as passed in the kind parameter) it tries to add the file to 161// the runtime poller. 162func newFile(fd int, name string, kind newFileKind, nonBlocking bool) *File { 163 f := &File{&file{ 164 pfd: poll.FD{ 165 Sysfd: fd, 166 IsStream: true, 167 ZeroReadIsEOF: true, 168 }, 169 name: name, 170 stdoutOrErr: fd == 1 || fd == 2, 171 }} 172 173 pollable := kind == kindOpenFile || kind == kindPipe || kind == kindSock || nonBlocking 174 175 // Things like regular files and FIFOs in kqueue on *BSD/Darwin 176 // may not work properly (or accurately according to its manual). 177 // As a result, we should avoid adding those to the kqueue-based 178 // netpoller. Check out #19093, #24164, and #66239 for more contexts. 179 // 180 // If the fd was passed to us via any path other than OpenFile, 181 // we assume those callers know what they were doing, so we won't 182 // perform this check and allow it to be added to the kqueue. 183 if kind == kindOpenFile { 184 switch runtime.GOOS { 185 case "darwin", "ios", "dragonfly", "freebsd", "netbsd", "openbsd": 186 var st syscall.Stat_t 187 err := ignoringEINTR(func() error { 188 return syscall.Fstat(fd, &st) 189 }) 190 typ := st.Mode & syscall.S_IFMT 191 // Don't try to use kqueue with regular files on *BSDs. 192 // On FreeBSD a regular file is always 193 // reported as ready for writing. 194 // On Dragonfly, NetBSD and OpenBSD the fd is signaled 195 // only once as ready (both read and write). 196 // Issue 19093. 197 // Also don't add directories to the netpoller. 198 if err == nil && (typ == syscall.S_IFREG || typ == syscall.S_IFDIR) { 199 pollable = false 200 } 201 202 // In addition to the behavior described above for regular files, 203 // on Darwin, kqueue does not work properly with fifos: 204 // closing the last writer does not cause a kqueue event 205 // for any readers. See issue #24164. 206 if (runtime.GOOS == "darwin" || runtime.GOOS == "ios") && typ == syscall.S_IFIFO { 207 pollable = false 208 } 209 } 210 } 211 212 clearNonBlock := false 213 if pollable { 214 // The descriptor is already in non-blocking mode. 215 // We only set f.nonblock if we put the file into 216 // non-blocking mode. 217 if nonBlocking { 218 // See the comments on net_newUnixFile. 219 if kind == kindSock { 220 f.nonblock = true // tell Fd to return blocking descriptor 221 } 222 } else if err := syscall.SetNonblock(fd, true); err == nil { 223 f.nonblock = true 224 clearNonBlock = true 225 } else { 226 pollable = false 227 } 228 } 229 230 // An error here indicates a failure to register 231 // with the netpoll system. That can happen for 232 // a file descriptor that is not supported by 233 // epoll/kqueue; for example, disk files on 234 // Linux systems. We assume that any real error 235 // will show up in later I/O. 236 // We do restore the blocking behavior if it was set by us. 237 if pollErr := f.pfd.Init("file", pollable); pollErr != nil && clearNonBlock { 238 if err := syscall.SetNonblock(fd, false); err == nil { 239 f.nonblock = false 240 } 241 } 242 243 runtime.SetFinalizer(f.file, (*file).close) 244 return f 245} 246 247func sigpipe() // implemented in package runtime 248 249// epipecheck raises SIGPIPE if we get an EPIPE error on standard 250// output or standard error. See the SIGPIPE docs in os/signal, and 251// issue 11845. 252func epipecheck(file *File, e error) { 253 if e == syscall.EPIPE && file.stdoutOrErr { 254 sigpipe() 255 } 256} 257 258// DevNull is the name of the operating system's “null device.” 259// On Unix-like systems, it is "/dev/null"; on Windows, "NUL". 260const DevNull = "/dev/null" 261 262// openFileNolog is the Unix implementation of OpenFile. 263// Changes here should be reflected in openDirAt and openDirNolog, if relevant. 264func openFileNolog(name string, flag int, perm FileMode) (*File, error) { 265 setSticky := false 266 if !supportsCreateWithStickyBit && flag&O_CREATE != 0 && perm&ModeSticky != 0 { 267 if _, err := Stat(name); IsNotExist(err) { 268 setSticky = true 269 } 270 } 271 272 var ( 273 r int 274 s poll.SysFile 275 e error 276 ) 277 // We have to check EINTR here, per issues 11180 and 39237. 278 ignoringEINTR(func() error { 279 r, s, e = open(name, flag|syscall.O_CLOEXEC, syscallMode(perm)) 280 return e 281 }) 282 if e != nil { 283 return nil, &PathError{Op: "open", Path: name, Err: e} 284 } 285 286 // open(2) itself won't handle the sticky bit on *BSD and Solaris 287 if setSticky { 288 setStickyBit(name) 289 } 290 291 // There's a race here with fork/exec, which we are 292 // content to live with. See ../syscall/exec_unix.go. 293 if !supportsCloseOnExec { 294 syscall.CloseOnExec(r) 295 } 296 297 f := newFile(r, name, kindOpenFile, unix.HasNonblockFlag(flag)) 298 f.pfd.SysFile = s 299 return f, nil 300} 301 302func openDirNolog(name string) (*File, error) { 303 var ( 304 r int 305 s poll.SysFile 306 e error 307 ) 308 ignoringEINTR(func() error { 309 r, s, e = open(name, O_RDONLY|syscall.O_CLOEXEC, 0) 310 return e 311 }) 312 if e != nil { 313 return nil, &PathError{Op: "open", Path: name, Err: e} 314 } 315 316 if !supportsCloseOnExec { 317 syscall.CloseOnExec(r) 318 } 319 320 f := newFile(r, name, kindNoPoll, false) 321 f.pfd.SysFile = s 322 return f, nil 323} 324 325func (file *file) close() error { 326 if file == nil { 327 return syscall.EINVAL 328 } 329 if info := file.dirinfo.Swap(nil); info != nil { 330 info.close() 331 } 332 var err error 333 if e := file.pfd.Close(); e != nil { 334 if e == poll.ErrFileClosing { 335 e = ErrClosed 336 } 337 err = &PathError{Op: "close", Path: file.name, Err: e} 338 } 339 340 // no need for a finalizer anymore 341 runtime.SetFinalizer(file, nil) 342 return err 343} 344 345// seek sets the offset for the next Read or Write on file to offset, interpreted 346// according to whence: 0 means relative to the origin of the file, 1 means 347// relative to the current offset, and 2 means relative to the end. 348// It returns the new offset and an error, if any. 349func (f *File) seek(offset int64, whence int) (ret int64, err error) { 350 if info := f.dirinfo.Swap(nil); info != nil { 351 // Free cached dirinfo, so we allocate a new one if we 352 // access this file as a directory again. See #35767 and #37161. 353 info.close() 354 } 355 ret, err = f.pfd.Seek(offset, whence) 356 runtime.KeepAlive(f) 357 return ret, err 358} 359 360// Truncate changes the size of the named file. 361// If the file is a symbolic link, it changes the size of the link's target. 362// If there is an error, it will be of type *PathError. 363func Truncate(name string, size int64) error { 364 e := ignoringEINTR(func() error { 365 return syscall.Truncate(name, size) 366 }) 367 if e != nil { 368 return &PathError{Op: "truncate", Path: name, Err: e} 369 } 370 return nil 371} 372 373// Remove removes the named file or (empty) directory. 374// If there is an error, it will be of type *PathError. 375func Remove(name string) error { 376 // System call interface forces us to know 377 // whether name is a file or directory. 378 // Try both: it is cheaper on average than 379 // doing a Stat plus the right one. 380 e := ignoringEINTR(func() error { 381 return syscall.Unlink(name) 382 }) 383 if e == nil { 384 return nil 385 } 386 e1 := ignoringEINTR(func() error { 387 return syscall.Rmdir(name) 388 }) 389 if e1 == nil { 390 return nil 391 } 392 393 // Both failed: figure out which error to return. 394 // OS X and Linux differ on whether unlink(dir) 395 // returns EISDIR, so can't use that. However, 396 // both agree that rmdir(file) returns ENOTDIR, 397 // so we can use that to decide which error is real. 398 // Rmdir might also return ENOTDIR if given a bad 399 // file path, like /etc/passwd/foo, but in that case, 400 // both errors will be ENOTDIR, so it's okay to 401 // use the error from unlink. 402 if e1 != syscall.ENOTDIR { 403 e = e1 404 } 405 return &PathError{Op: "remove", Path: name, Err: e} 406} 407 408func tempDir() string { 409 dir := Getenv("TMPDIR") 410 if dir == "" { 411 if runtime.GOOS == "android" { 412 dir = "/data/local/tmp" 413 } else { 414 dir = "/tmp" 415 } 416 } 417 return dir 418} 419 420// Link creates newname as a hard link to the oldname file. 421// If there is an error, it will be of type *LinkError. 422func Link(oldname, newname string) error { 423 e := ignoringEINTR(func() error { 424 return syscall.Link(oldname, newname) 425 }) 426 if e != nil { 427 return &LinkError{"link", oldname, newname, e} 428 } 429 return nil 430} 431 432// Symlink creates newname as a symbolic link to oldname. 433// On Windows, a symlink to a non-existent oldname creates a file symlink; 434// if oldname is later created as a directory the symlink will not work. 435// If there is an error, it will be of type *LinkError. 436func Symlink(oldname, newname string) error { 437 e := ignoringEINTR(func() error { 438 return syscall.Symlink(oldname, newname) 439 }) 440 if e != nil { 441 return &LinkError{"symlink", oldname, newname, e} 442 } 443 return nil 444} 445 446func readlink(name string) (string, error) { 447 for len := 128; ; len *= 2 { 448 b := make([]byte, len) 449 var ( 450 n int 451 e error 452 ) 453 for { 454 n, e = fixCount(syscall.Readlink(name, b)) 455 if e != syscall.EINTR { 456 break 457 } 458 } 459 // buffer too small 460 if (runtime.GOOS == "aix" || runtime.GOOS == "wasip1") && e == syscall.ERANGE { 461 continue 462 } 463 if e != nil { 464 return "", &PathError{Op: "readlink", Path: name, Err: e} 465 } 466 if n < len { 467 return string(b[0:n]), nil 468 } 469 } 470} 471 472type unixDirent struct { 473 parent string 474 name string 475 typ FileMode 476 info FileInfo 477} 478 479func (d *unixDirent) Name() string { return d.name } 480func (d *unixDirent) IsDir() bool { return d.typ.IsDir() } 481func (d *unixDirent) Type() FileMode { return d.typ } 482 483func (d *unixDirent) Info() (FileInfo, error) { 484 if d.info != nil { 485 return d.info, nil 486 } 487 return lstat(d.parent + "/" + d.name) 488} 489 490func (d *unixDirent) String() string { 491 return fs.FormatDirEntry(d) 492} 493 494func newUnixDirent(parent, name string, typ FileMode) (DirEntry, error) { 495 ude := &unixDirent{ 496 parent: parent, 497 name: name, 498 typ: typ, 499 } 500 if typ != ^FileMode(0) && !testingForceReadDirLstat { 501 return ude, nil 502 } 503 504 info, err := lstat(parent + "/" + name) 505 if err != nil { 506 return nil, err 507 } 508 509 ude.typ = info.Mode().Type() 510 ude.info = info 511 return ude, nil 512} 513