From 0044bc614af52d3f9acdd4db18e053e635c25ccd Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Tue, 11 Feb 2025 12:56:27 -0800 Subject: [PATCH] os: consolidate and clarify File.Fd docs Change-Id: Id062b969fe7d6908a0797b36a4a379e4d46ba557 Reviewed-on: https://go-review.googlesource.com/c/go/+/648516 LUCI-TryBot-Result: Go LUCI Auto-Submit: Ian Lance Taylor Reviewed-by: Ian Lance Taylor Reviewed-by: Damien Neil --- src/os/file.go | 17 +++++++++++++++++ src/os/file_plan9.go | 37 +++++++++++++++---------------------- src/os/file_unix.go | 15 ++------------- src/os/file_windows.go | 9 ++------- src/os/stat_plan9.go | 2 +- 5 files changed, 37 insertions(+), 43 deletions(-) diff --git a/src/os/file.go b/src/os/file.go index 1d4382e486..32ff6be7be 100644 --- a/src/os/file.go +++ b/src/os/file.go @@ -685,6 +685,23 @@ func (f *File) SyscallConn() (syscall.RawConn, error) { return newRawConn(f) } +// Fd returns the system file descriptor or handle referencing the open file. +// If f is closed, the descriptor becomes invalid. +// If f is garbage collected, a cleanup may close the descriptor, +// making it invalid; see [runtime.AddCleanup] for more information on when +// a cleanup might be run. +// +// Do not close the returned descriptor; that could cause a later +// close of f to close an unrelated descriptor. +// +// On Unix systems this will cause the [File.SetDeadline] +// methods to stop working. +// +// For most uses prefer the f.SyscallConn method. +func (f *File) Fd() uintptr { + return f.fd() +} + // DirFS returns a file system (an fs.FS) for the tree of files rooted at the directory dir. // // Note that DirFS("/prefix") only guarantees that the Open calls it makes to the diff --git a/src/os/file_plan9.go b/src/os/file_plan9.go index f74dbf20c4..73df3b086d 100644 --- a/src/os/file_plan9.go +++ b/src/os/file_plan9.go @@ -27,26 +27,19 @@ func fixLongPath(path string) string { // to close the wrong file descriptor. type file struct { fdmu poll.FDMutex - fd int + sysfd int name string dirinfo atomic.Pointer[dirInfo] // nil unless directory being read appendMode bool // whether file is opened for appending cleanup runtime.Cleanup // cleanup closes the file when no longer referenced } -// Fd returns the integer Plan 9 file descriptor referencing the open file. -// If f is closed, the file descriptor becomes invalid. -// If f is garbage collected, a cleanup may close the file descriptor, -// making it invalid; see [runtime.AddCleanup] for more information on when -// a cleanup might be run. On Unix systems this will cause the [File.SetDeadline] -// methods to stop working. -// -// As an alternative, see the f.SyscallConn method. -func (f *File) Fd() uintptr { +// fd is the Plan 9 implementation of Fd. +func (f *File) fd() uintptr { if f == nil { return ^(uintptr(0)) } - return uintptr(f.fd) + return uintptr(f.sysfd) } // NewFile returns a new File with the given file descriptor and @@ -57,7 +50,7 @@ func NewFile(fd uintptr, name string) *File { if fdi < 0 { return nil } - f := &File{&file{fd: fdi, name: name}} + f := &File{&file{sysfd: fdi, name: name}} f.cleanup = runtime.AddCleanup(f, func(f *file) { f.close() }, f.file) return f } @@ -180,7 +173,7 @@ func (file *file) close() error { // and writeUnlock methods. func (file *file) destroy() error { var err error - if e := syscall.Close(file.fd); e != nil { + if e := syscall.Close(file.sysfd); e != nil { err = &PathError{Op: "close", Path: file.name, Err: e} } return err @@ -222,7 +215,7 @@ func (f *File) Truncate(size int64) error { } defer f.decref() - if err = syscall.Fwstat(f.fd, buf[:n]); err != nil { + if err = syscall.Fwstat(f.sysfd, buf[:n]); err != nil { return &PathError{Op: "truncate", Path: f.name, Err: err} } return nil @@ -254,7 +247,7 @@ func (f *File) chmod(mode FileMode) error { } defer f.decref() - if err = syscall.Fwstat(f.fd, buf[:n]); err != nil { + if err = syscall.Fwstat(f.sysfd, buf[:n]); err != nil { return &PathError{Op: "chmod", Path: f.name, Err: err} } return nil @@ -281,7 +274,7 @@ func (f *File) Sync() error { } defer f.decref() - if err = syscall.Fwstat(f.fd, buf[:n]); err != nil { + if err = syscall.Fwstat(f.sysfd, buf[:n]); err != nil { return &PathError{Op: "sync", Path: f.name, Err: err} } return nil @@ -294,7 +287,7 @@ func (f *File) read(b []byte) (n int, err error) { return 0, err } defer f.readUnlock() - n, e := fixCount(syscall.Read(f.fd, b)) + n, e := fixCount(syscall.Read(f.sysfd, b)) if n == 0 && len(b) > 0 && e == nil { return 0, io.EOF } @@ -309,7 +302,7 @@ func (f *File) pread(b []byte, off int64) (n int, err error) { return 0, err } defer f.readUnlock() - n, e := fixCount(syscall.Pread(f.fd, b, off)) + n, e := fixCount(syscall.Pread(f.sysfd, b, off)) if n == 0 && len(b) > 0 && e == nil { return 0, io.EOF } @@ -328,7 +321,7 @@ func (f *File) write(b []byte) (n int, err error) { if len(b) == 0 { return 0, nil } - return fixCount(syscall.Write(f.fd, b)) + return fixCount(syscall.Write(f.sysfd, b)) } // pwrite writes len(b) bytes to the File starting at byte offset off. @@ -343,7 +336,7 @@ func (f *File) pwrite(b []byte, off int64) (n int, err error) { if len(b) == 0 { return 0, nil } - return fixCount(syscall.Pwrite(f.fd, b, off)) + return fixCount(syscall.Pwrite(f.sysfd, b, off)) } // seek sets the offset for the next Read or Write on file to offset, interpreted @@ -358,7 +351,7 @@ func (f *File) seek(offset int64, whence int) (ret int64, err error) { // Free cached dirinfo, so we allocate a new one if we // access this file as a directory again. See #35767 and #37161. f.dirinfo.Store(nil) - return syscall.Seek(f.fd, offset, whence) + return syscall.Seek(f.sysfd, offset, whence) } // Truncate changes the size of the named file. @@ -555,7 +548,7 @@ func (f *File) Chdir() error { return err } defer f.decref() - if e := syscall.Fchdir(f.fd); e != nil { + if e := syscall.Fchdir(f.sysfd); e != nil { return &PathError{Op: "chdir", Path: f.name, Err: e} } return nil diff --git a/src/os/file_unix.go b/src/os/file_unix.go index 5e9239edc5..bb99b5279d 100644 --- a/src/os/file_unix.go +++ b/src/os/file_unix.go @@ -66,19 +66,8 @@ type file struct { cleanup runtime.Cleanup // cleanup closes the file when no longer referenced } -// Fd returns the integer Unix file descriptor referencing the open file. -// If f is closed, the file descriptor becomes invalid. -// If f is garbage collected, a cleanup may close the file descriptor, -// making it invalid; see [runtime.AddCleanup] for more information on when -// a cleanup might be run. On Unix systems this will cause the [File.SetDeadline] -// methods to stop working. -// Because file descriptors can be reused, the returned file descriptor may -// only be closed through the [File.Close] method of f, or by its cleanup during -// garbage collection. Otherwise, during garbage collection the cleanup -// may close an unrelated file descriptor with the same (reused) number. -// -// As an alternative, see the f.SyscallConn method. -func (f *File) Fd() uintptr { +// fd is the Unix implementation of Fd. +func (f *File) fd() uintptr { if f == nil { return ^(uintptr(0)) } diff --git a/src/os/file_windows.go b/src/os/file_windows.go index 2da924fe43..c209a9f003 100644 --- a/src/os/file_windows.go +++ b/src/os/file_windows.go @@ -32,13 +32,8 @@ type file struct { cleanup runtime.Cleanup // cleanup closes the file when no longer referenced } -// Fd returns the Windows handle referencing the open file. -// If f is closed, the file descriptor becomes invalid. -// If f is garbage collected, a cleanup may close the file descriptor, -// making it invalid; see [runtime.AddCleanup] for more information on when -// a cleanup might be run. On Unix systems this will cause the [File.SetDeadline] -// methods to stop working. -func (file *File) Fd() uintptr { +// fd is the Windows implementation of Fd. +func (file *File) fd() uintptr { if file == nil { return uintptr(syscall.InvalidHandle) } diff --git a/src/os/stat_plan9.go b/src/os/stat_plan9.go index a5e9901379..e9fba17e9d 100644 --- a/src/os/stat_plan9.go +++ b/src/os/stat_plan9.go @@ -59,7 +59,7 @@ func dirstat(arg any) (*syscall.Dir, error) { if err := a.incref("fstat"); err != nil { return nil, err } - n, err = syscall.Fstat(a.fd, buf) + n, err = syscall.Fstat(a.sysfd, buf) a.decref() case string: name = a -- 2.50.0