diff options
| author | YDBot <[email protected]> | 2026-06-10 06:27:27 +0000 |
|---|---|---|
| committer | YDBot <[email protected]> | 2026-06-10 06:27:27 +0000 |
| commit | eb8c7d3ee0c13034ecf5d8d35c24cefc40f0bb3f (patch) | |
| tree | a1eba7fec49a258bb24bfa77808233496ac0047f /contrib/go/_std_1.25/src/os/file.go | |
| parent | c4011885693f041c96b035f368aae8a1baac8885 (diff) | |
| parent | 72cfbf8958fa6fa5227e9ad6466abfc635fdeb15 (diff) | |
Diffstat (limited to 'contrib/go/_std_1.25/src/os/file.go')
| -rw-r--r-- | contrib/go/_std_1.25/src/os/file.go | 942 |
1 files changed, 0 insertions, 942 deletions
diff --git a/contrib/go/_std_1.25/src/os/file.go b/contrib/go/_std_1.25/src/os/file.go deleted file mode 100644 index 80857240f5c..00000000000 --- a/contrib/go/_std_1.25/src/os/file.go +++ /dev/null @@ -1,942 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package os provides a platform-independent interface to operating system -// functionality. The design is Unix-like, although the error handling is -// Go-like; failing calls return values of type error rather than error numbers. -// Often, more information is available within the error. For example, -// if a call that takes a file name fails, such as [Open] or [Stat], the error -// will include the failing file name when printed and will be of type -// [*PathError], which may be unpacked for more information. -// -// The os interface is intended to be uniform across all operating systems. -// Features not generally available appear in the system-specific package syscall. -// -// Here is a simple example, opening a file and reading some of it. -// -// file, err := os.Open("file.go") // For read access. -// if err != nil { -// log.Fatal(err) -// } -// -// If the open fails, the error string will be self-explanatory, like -// -// open file.go: no such file or directory -// -// The file's data can then be read into a slice of bytes. Read and -// Write take their byte counts from the length of the argument slice. -// -// data := make([]byte, 100) -// count, err := file.Read(data) -// if err != nil { -// log.Fatal(err) -// } -// fmt.Printf("read %d bytes: %q\n", count, data[:count]) -// -// # Concurrency -// -// The methods of [File] correspond to file system operations. All are -// safe for concurrent use. The maximum number of concurrent -// operations on a File may be limited by the OS or the system. The -// number should be high, but exceeding it may degrade performance or -// cause other issues. -package os - -import ( - "errors" - "internal/filepathlite" - "internal/poll" - "internal/testlog" - "io" - "io/fs" - "runtime" - "slices" - "syscall" - "time" - "unsafe" -) - -// Name returns the name of the file as presented to Open. -// -// It is safe to call Name after [Close]. -func (f *File) Name() string { return f.name } - -// Stdin, Stdout, and Stderr are open Files pointing to the standard input, -// standard output, and standard error file descriptors. -// -// Note that the Go runtime writes to standard error for panics and crashes; -// closing Stderr may cause those messages to go elsewhere, perhaps -// to a file opened later. -var ( - Stdin = NewFile(uintptr(syscall.Stdin), "/dev/stdin") - Stdout = NewFile(uintptr(syscall.Stdout), "/dev/stdout") - Stderr = NewFile(uintptr(syscall.Stderr), "/dev/stderr") -) - -// Flags to OpenFile wrapping those of the underlying system. Not all -// flags may be implemented on a given system. -const ( - // Exactly one of O_RDONLY, O_WRONLY, or O_RDWR must be specified. - O_RDONLY int = syscall.O_RDONLY // open the file read-only. - O_WRONLY int = syscall.O_WRONLY // open the file write-only. - O_RDWR int = syscall.O_RDWR // open the file read-write. - // The remaining values may be or'ed in to control behavior. - O_APPEND int = syscall.O_APPEND // append data to the file when writing. - O_CREATE int = syscall.O_CREAT // create a new file if none exists. - O_EXCL int = syscall.O_EXCL // used with O_CREATE, file must not exist. - O_SYNC int = syscall.O_SYNC // open for synchronous I/O. - O_TRUNC int = syscall.O_TRUNC // truncate regular writable file when opened. -) - -// Seek whence values. -// -// Deprecated: Use io.SeekStart, io.SeekCurrent, and io.SeekEnd. -const ( - SEEK_SET int = 0 // seek relative to the origin of the file - SEEK_CUR int = 1 // seek relative to the current offset - SEEK_END int = 2 // seek relative to the end -) - -// LinkError records an error during a link or symlink or rename -// system call and the paths that caused it. -type LinkError struct { - Op string - Old string - New string - Err error -} - -func (e *LinkError) Error() string { - return e.Op + " " + e.Old + " " + e.New + ": " + e.Err.Error() -} - -func (e *LinkError) Unwrap() error { - return e.Err -} - -// NewFile returns a new [File] with the given file descriptor and name. -// The returned value will be nil if fd is not a valid file descriptor. -// -// NewFile's behavior differs on some platforms: -// -// - On Unix, if fd is in non-blocking mode, NewFile will attempt to return a pollable file. -// - On Windows, if fd is opened for asynchronous I/O (that is, [syscall.FILE_FLAG_OVERLAPPED] -// has been specified in the [syscall.CreateFile] call), NewFile will attempt to return a pollable -// file by associating fd with the Go runtime I/O completion port. -// The I/O operations will be performed synchronously if the association fails. -// -// Only pollable files support [File.SetDeadline], [File.SetReadDeadline], and [File.SetWriteDeadline]. -// -// After passing it to NewFile, fd may become invalid under the same conditions described -// in the comments of [File.Fd], and the same constraints apply. -func NewFile(fd uintptr, name string) *File { - return newFileFromNewFile(fd, name) -} - -// Read reads up to len(b) bytes from the File and stores them in b. -// It returns the number of bytes read and any error encountered. -// At end of file, Read returns 0, io.EOF. -func (f *File) Read(b []byte) (n int, err error) { - if err := f.checkValid("read"); err != nil { - return 0, err - } - n, e := f.read(b) - return n, f.wrapErr("read", e) -} - -// ReadAt reads len(b) bytes from the File starting at byte offset off. -// It returns the number of bytes read and the error, if any. -// ReadAt always returns a non-nil error when n < len(b). -// At end of file, that error is io.EOF. -func (f *File) ReadAt(b []byte, off int64) (n int, err error) { - if err := f.checkValid("read"); err != nil { - return 0, err - } - - if off < 0 { - return 0, &PathError{Op: "readat", Path: f.name, Err: errors.New("negative offset")} - } - - for len(b) > 0 { - m, e := f.pread(b, off) - if e != nil { - err = f.wrapErr("read", e) - break - } - n += m - b = b[m:] - off += int64(m) - } - return -} - -// ReadFrom implements io.ReaderFrom. -func (f *File) ReadFrom(r io.Reader) (n int64, err error) { - if err := f.checkValid("write"); err != nil { - return 0, err - } - n, handled, e := f.readFrom(r) - if !handled { - return genericReadFrom(f, r) // without wrapping - } - return n, f.wrapErr("write", e) -} - -// noReadFrom can be embedded alongside another type to -// hide the ReadFrom method of that other type. -type noReadFrom struct{} - -// ReadFrom hides another ReadFrom method. -// It should never be called. -func (noReadFrom) ReadFrom(io.Reader) (int64, error) { - panic("can't happen") -} - -// fileWithoutReadFrom implements all the methods of *File other -// than ReadFrom. This is used to permit ReadFrom to call io.Copy -// without leading to a recursive call to ReadFrom. -type fileWithoutReadFrom struct { - noReadFrom - *File -} - -func genericReadFrom(f *File, r io.Reader) (int64, error) { - return io.Copy(fileWithoutReadFrom{File: f}, r) -} - -// Write writes len(b) bytes from b to the File. -// It returns the number of bytes written and an error, if any. -// Write returns a non-nil error when n != len(b). -func (f *File) Write(b []byte) (n int, err error) { - if err := f.checkValid("write"); err != nil { - return 0, err - } - n, e := f.write(b) - if n < 0 { - n = 0 - } - if n != len(b) { - err = io.ErrShortWrite - } - - epipecheck(f, e) - - if e != nil { - err = f.wrapErr("write", e) - } - - return n, err -} - -var errWriteAtInAppendMode = errors.New("os: invalid use of WriteAt on file opened with O_APPEND") - -// WriteAt writes len(b) bytes to the File starting at byte offset off. -// It returns the number of bytes written and an error, if any. -// WriteAt returns a non-nil error when n != len(b). -// -// If file was opened with the [O_APPEND] flag, WriteAt returns an error. -func (f *File) WriteAt(b []byte, off int64) (n int, err error) { - if err := f.checkValid("write"); err != nil { - return 0, err - } - if f.appendMode { - return 0, errWriteAtInAppendMode - } - - if off < 0 { - return 0, &PathError{Op: "writeat", Path: f.name, Err: errors.New("negative offset")} - } - - for len(b) > 0 { - m, e := f.pwrite(b, off) - if e != nil { - err = f.wrapErr("write", e) - break - } - n += m - b = b[m:] - off += int64(m) - } - return -} - -// WriteTo implements io.WriterTo. -func (f *File) WriteTo(w io.Writer) (n int64, err error) { - if err := f.checkValid("read"); err != nil { - return 0, err - } - n, handled, e := f.writeTo(w) - if handled { - return n, f.wrapErr("read", e) - } - return genericWriteTo(f, w) // without wrapping -} - -// noWriteTo can be embedded alongside another type to -// hide the WriteTo method of that other type. -type noWriteTo struct{} - -// WriteTo hides another WriteTo method. -// It should never be called. -func (noWriteTo) WriteTo(io.Writer) (int64, error) { - panic("can't happen") -} - -// fileWithoutWriteTo implements all the methods of *File other -// than WriteTo. This is used to permit WriteTo to call io.Copy -// without leading to a recursive call to WriteTo. -type fileWithoutWriteTo struct { - noWriteTo - *File -} - -func genericWriteTo(f *File, w io.Writer) (int64, error) { - return io.Copy(w, fileWithoutWriteTo{File: f}) -} - -// Seek sets the offset for the next Read or Write on file to offset, interpreted -// according to whence: 0 means relative to the origin of the file, 1 means -// relative to the current offset, and 2 means relative to the end. -// It returns the new offset and an error, if any. -// The behavior of Seek on a file opened with [O_APPEND] is not specified. -func (f *File) Seek(offset int64, whence int) (ret int64, err error) { - if err := f.checkValid("seek"); err != nil { - return 0, err - } - r, e := f.seek(offset, whence) - if e == nil && f.dirinfo.Load() != nil && r != 0 { - e = syscall.EISDIR - } - if e != nil { - return 0, f.wrapErr("seek", e) - } - return r, nil -} - -// WriteString is like Write, but writes the contents of string s rather than -// a slice of bytes. -func (f *File) WriteString(s string) (n int, err error) { - b := unsafe.Slice(unsafe.StringData(s), len(s)) - return f.Write(b) -} - -// Mkdir creates a new directory with the specified name and permission -// bits (before umask). -// If there is an error, it will be of type [*PathError]. -func Mkdir(name string, perm FileMode) error { - longName := fixLongPath(name) - e := ignoringEINTR(func() error { - return syscall.Mkdir(longName, syscallMode(perm)) - }) - - if e != nil { - return &PathError{Op: "mkdir", Path: name, Err: e} - } - - // mkdir(2) itself won't handle the sticky bit on *BSD and Solaris - if !supportsCreateWithStickyBit && perm&ModeSticky != 0 { - e = setStickyBit(name) - - if e != nil { - Remove(name) - return e - } - } - - return nil -} - -// setStickyBit adds ModeSticky to the permission bits of path, non atomic. -func setStickyBit(name string) error { - fi, err := Stat(name) - if err != nil { - return err - } - return Chmod(name, fi.Mode()|ModeSticky) -} - -// Chdir changes the current working directory to the named directory. -// If there is an error, it will be of type [*PathError]. -func Chdir(dir string) error { - if e := syscall.Chdir(dir); e != nil { - testlog.Open(dir) // observe likely non-existent directory - return &PathError{Op: "chdir", Path: dir, Err: e} - } - if runtime.GOOS == "windows" { - abs := filepathlite.IsAbs(dir) - getwdCache.Lock() - if abs { - getwdCache.dir = dir - } else { - getwdCache.dir = "" - } - getwdCache.Unlock() - } - if log := testlog.Logger(); log != nil { - wd, err := Getwd() - if err == nil { - log.Chdir(wd) - } - } - return nil -} - -// Open opens the named file for reading. If successful, methods on -// the returned file can be used for reading; the associated file -// descriptor has mode [O_RDONLY]. -// If there is an error, it will be of type [*PathError]. -func Open(name string) (*File, error) { - return OpenFile(name, O_RDONLY, 0) -} - -// Create creates or truncates the named file. If the file already exists, -// it is truncated. If the file does not exist, it is created with mode 0o666 -// (before umask). If successful, methods on the returned File can -// be used for I/O; the associated file descriptor has mode [O_RDWR]. -// The directory containing the file must already exist. -// If there is an error, it will be of type [*PathError]. -func Create(name string) (*File, error) { - return OpenFile(name, O_RDWR|O_CREATE|O_TRUNC, 0666) -} - -// OpenFile is the generalized open call; most users will use Open -// or Create instead. It opens the named file with specified flag -// ([O_RDONLY] etc.). If the file does not exist, and the [O_CREATE] flag -// is passed, it is created with mode perm (before umask); -// the containing directory must exist. If successful, -// methods on the returned File can be used for I/O. -// If there is an error, it will be of type [*PathError]. -func OpenFile(name string, flag int, perm FileMode) (*File, error) { - testlog.Open(name) - f, err := openFileNolog(name, flag, perm) - if err != nil { - return nil, err - } - f.appendMode = flag&O_APPEND != 0 - - return f, nil -} - -var errPathEscapes = errors.New("path escapes from parent") - -// openDir opens a file which is assumed to be a directory. As such, it skips -// the syscalls that make the file descriptor non-blocking as these take time -// and will fail on file descriptors for directories. -func openDir(name string) (*File, error) { - testlog.Open(name) - return openDirNolog(name) -} - -// Rename renames (moves) oldpath to newpath. -// If newpath already exists and is not a directory, Rename replaces it. -// If newpath already exists and is a directory, Rename returns an error. -// OS-specific restrictions may apply when oldpath and newpath are in different directories. -// Even within the same directory, on non-Unix platforms Rename is not an atomic operation. -// If there is an error, it will be of type *LinkError. -func Rename(oldpath, newpath string) error { - return rename(oldpath, newpath) -} - -// Readlink returns the destination of the named symbolic link. -// If there is an error, it will be of type [*PathError]. -// -// If the link destination is relative, Readlink returns the relative path -// without resolving it to an absolute one. -func Readlink(name string) (string, error) { - return readlink(name) -} - -// Many functions in package syscall return a count of -1 instead of 0. -// Using fixCount(call()) instead of call() corrects the count. -func fixCount(n int, err error) (int, error) { - if n < 0 { - n = 0 - } - return n, err -} - -// checkWrapErr is the test hook to enable checking unexpected wrapped errors of poll.ErrFileClosing. -// It is set to true in the export_test.go for tests (including fuzz tests). -var checkWrapErr = false - -// wrapErr wraps an error that occurred during an operation on an open file. -// It passes io.EOF through unchanged, otherwise converts -// poll.ErrFileClosing to ErrClosed and wraps the error in a PathError. -func (f *File) wrapErr(op string, err error) error { - if err == nil || err == io.EOF { - return err - } - if err == poll.ErrFileClosing { - err = ErrClosed - } else if checkWrapErr && errors.Is(err, poll.ErrFileClosing) { - panic("unexpected error wrapping poll.ErrFileClosing: " + err.Error()) - } - return &PathError{Op: op, Path: f.name, Err: err} -} - -// TempDir returns the default directory to use for temporary files. -// -// On Unix systems, it returns $TMPDIR if non-empty, else /tmp. -// On Windows, it uses GetTempPath, returning the first non-empty -// value from %TMP%, %TEMP%, %USERPROFILE%, or the Windows directory. -// On Plan 9, it returns /tmp. -// -// The directory is neither guaranteed to exist nor have accessible -// permissions. -func TempDir() string { - return tempDir() -} - -// UserCacheDir returns the default root directory to use for user-specific -// cached data. Users should create their own application-specific subdirectory -// within this one and use that. -// -// On Unix systems, it returns $XDG_CACHE_HOME as specified by -// https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html if -// non-empty, else $HOME/.cache. -// On Darwin, it returns $HOME/Library/Caches. -// On Windows, it returns %LocalAppData%. -// On Plan 9, it returns $home/lib/cache. -// -// If the location cannot be determined (for example, $HOME is not defined) or -// the path in $XDG_CACHE_HOME is relative, then it will return an error. -func UserCacheDir() (string, error) { - var dir string - - switch runtime.GOOS { - case "windows": - dir = Getenv("LocalAppData") - if dir == "" { - return "", errors.New("%LocalAppData% is not defined") - } - - case "darwin", "ios": - dir = Getenv("HOME") - if dir == "" { - return "", errors.New("$HOME is not defined") - } - dir += "/Library/Caches" - - case "plan9": - dir = Getenv("home") - if dir == "" { - return "", errors.New("$home is not defined") - } - dir += "/lib/cache" - - default: // Unix - dir = Getenv("XDG_CACHE_HOME") - if dir == "" { - dir = Getenv("HOME") - if dir == "" { - return "", errors.New("neither $XDG_CACHE_HOME nor $HOME are defined") - } - dir += "/.cache" - } else if !filepathlite.IsAbs(dir) { - return "", errors.New("path in $XDG_CACHE_HOME is relative") - } - } - - return dir, nil -} - -// UserConfigDir returns the default root directory to use for user-specific -// configuration data. Users should create their own application-specific -// subdirectory within this one and use that. -// -// On Unix systems, it returns $XDG_CONFIG_HOME as specified by -// https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html if -// non-empty, else $HOME/.config. -// On Darwin, it returns $HOME/Library/Application Support. -// On Windows, it returns %AppData%. -// On Plan 9, it returns $home/lib. -// -// If the location cannot be determined (for example, $HOME is not defined) or -// the path in $XDG_CONFIG_HOME is relative, then it will return an error. -func UserConfigDir() (string, error) { - var dir string - - switch runtime.GOOS { - case "windows": - dir = Getenv("AppData") - if dir == "" { - return "", errors.New("%AppData% is not defined") - } - - case "darwin", "ios": - dir = Getenv("HOME") - if dir == "" { - return "", errors.New("$HOME is not defined") - } - dir += "/Library/Application Support" - - case "plan9": - dir = Getenv("home") - if dir == "" { - return "", errors.New("$home is not defined") - } - dir += "/lib" - - default: // Unix - dir = Getenv("XDG_CONFIG_HOME") - if dir == "" { - dir = Getenv("HOME") - if dir == "" { - return "", errors.New("neither $XDG_CONFIG_HOME nor $HOME are defined") - } - dir += "/.config" - } else if !filepathlite.IsAbs(dir) { - return "", errors.New("path in $XDG_CONFIG_HOME is relative") - } - } - - return dir, nil -} - -// UserHomeDir returns the current user's home directory. -// -// On Unix, including macOS, it returns the $HOME environment variable. -// On Windows, it returns %USERPROFILE%. -// On Plan 9, it returns the $home environment variable. -// -// If the expected variable is not set in the environment, UserHomeDir -// returns either a platform-specific default value or a non-nil error. -func UserHomeDir() (string, error) { - env, enverr := "HOME", "$HOME" - switch runtime.GOOS { - case "windows": - env, enverr = "USERPROFILE", "%userprofile%" - case "plan9": - env, enverr = "home", "$home" - } - if v := Getenv(env); v != "" { - return v, nil - } - // On some operating systems the home directory is not always defined. - switch runtime.GOOS { - case "android": - return "/sdcard", nil - case "ios": - return "/", nil - } - return "", errors.New(enverr + " is not defined") -} - -// Chmod changes the mode of the named file to mode. -// If the file is a symbolic link, it changes the mode of the link's target. -// If there is an error, it will be of type [*PathError]. -// -// A different subset of the mode bits are used, depending on the -// operating system. -// -// On Unix, the mode's permission bits, [ModeSetuid], [ModeSetgid], and -// [ModeSticky] are used. -// -// On Windows, only the 0o200 bit (owner writable) of mode is used; it -// controls whether the file's read-only attribute is set or cleared. -// The other bits are currently unused. For compatibility with Go 1.12 -// and earlier, use a non-zero mode. Use mode 0o400 for a read-only -// file and 0o600 for a readable+writable file. -// -// On Plan 9, the mode's permission bits, [ModeAppend], [ModeExclusive], -// and [ModeTemporary] are used. -func Chmod(name string, mode FileMode) error { return chmod(name, mode) } - -// Chmod changes the mode of the file to mode. -// If there is an error, it will be of type [*PathError]. -func (f *File) Chmod(mode FileMode) error { return f.chmod(mode) } - -// SetDeadline sets the read and write deadlines for a File. -// It is equivalent to calling both SetReadDeadline and SetWriteDeadline. -// -// Only some kinds of files support setting a deadline. Calls to SetDeadline -// for files that do not support deadlines will return ErrNoDeadline. -// On most systems ordinary files do not support deadlines, but pipes do. -// -// A deadline is an absolute time after which I/O operations fail with an -// error instead of blocking. The deadline applies to all future and pending -// I/O, not just the immediately following call to Read or Write. -// After a deadline has been exceeded, the connection can be refreshed -// by setting a deadline in the future. -// -// If the deadline is exceeded a call to Read or Write or to other I/O -// methods will return an error that wraps ErrDeadlineExceeded. -// This can be tested using errors.Is(err, os.ErrDeadlineExceeded). -// That error implements the Timeout method, and calling the Timeout -// method will return true, but there are other possible errors for which -// the Timeout will return true even if the deadline has not been exceeded. -// -// An idle timeout can be implemented by repeatedly extending -// the deadline after successful Read or Write calls. -// -// A zero value for t means I/O operations will not time out. -func (f *File) SetDeadline(t time.Time) error { - return f.setDeadline(t) -} - -// SetReadDeadline sets the deadline for future Read calls and any -// currently-blocked Read call. -// A zero value for t means Read will not time out. -// Not all files support setting deadlines; see SetDeadline. -func (f *File) SetReadDeadline(t time.Time) error { - return f.setReadDeadline(t) -} - -// SetWriteDeadline sets the deadline for any future Write calls and any -// currently-blocked Write call. -// Even if Write times out, it may return n > 0, indicating that -// some of the data was successfully written. -// A zero value for t means Write will not time out. -// Not all files support setting deadlines; see SetDeadline. -func (f *File) SetWriteDeadline(t time.Time) error { - return f.setWriteDeadline(t) -} - -// SyscallConn returns a raw file. -// This implements the syscall.Conn interface. -func (f *File) SyscallConn() (syscall.RawConn, error) { - if err := f.checkValid("SyscallConn"); err != nil { - return nil, err - } - 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 finalizer may close the descriptor, -// making it invalid; see [runtime.SetFinalizer] for more information on when -// a finalizer might be run. -// -// Do not close the returned descriptor; that could cause a later -// close of f to close an unrelated descriptor. -// -// Fd's behavior differs on some platforms: -// -// - On Unix and Windows, [File.SetDeadline] methods will stop working. -// - On Windows, the file descriptor will be disassociated from the -// Go runtime I/O completion port if there are no concurrent I/O -// operations on the file. -// -// 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 -// operating system will begin with "/prefix": DirFS("/prefix").Open("file") is the -// same as os.Open("/prefix/file"). So if /prefix/file is a symbolic link pointing outside -// the /prefix tree, then using DirFS does not stop the access any more than using -// os.Open does. Additionally, the root of the fs.FS returned for a relative path, -// DirFS("prefix"), will be affected by later calls to Chdir. DirFS is therefore not -// a general substitute for a chroot-style security mechanism when the directory tree -// contains arbitrary content. -// -// Use [Root.FS] to obtain a fs.FS that prevents escapes from the tree via symbolic links. -// -// The directory dir must not be "". -// -// The result implements [io/fs.StatFS], [io/fs.ReadFileFS], [io/fs.ReadDirFS], and -// [io/fs.ReadLinkFS]. -func DirFS(dir string) fs.FS { - return dirFS(dir) -} - -var _ fs.StatFS = dirFS("") -var _ fs.ReadFileFS = dirFS("") -var _ fs.ReadDirFS = dirFS("") -var _ fs.ReadLinkFS = dirFS("") - -type dirFS string - -func (dir dirFS) Open(name string) (fs.File, error) { - fullname, err := dir.join(name) - if err != nil { - return nil, &PathError{Op: "open", Path: name, Err: err} - } - f, err := Open(fullname) - if err != nil { - // DirFS takes a string appropriate for GOOS, - // while the name argument here is always slash separated. - // dir.join will have mixed the two; undo that for - // error reporting. - err.(*PathError).Path = name - return nil, err - } - return f, nil -} - -// The ReadFile method calls the [ReadFile] function for the file -// with the given name in the directory. The function provides -// robust handling for small files and special file systems. -// Through this method, dirFS implements [io/fs.ReadFileFS]. -func (dir dirFS) ReadFile(name string) ([]byte, error) { - fullname, err := dir.join(name) - if err != nil { - return nil, &PathError{Op: "readfile", Path: name, Err: err} - } - b, err := ReadFile(fullname) - if err != nil { - if e, ok := err.(*PathError); ok { - // See comment in dirFS.Open. - e.Path = name - } - return nil, err - } - return b, nil -} - -// ReadDir reads the named directory, returning all its directory entries sorted -// by filename. Through this method, dirFS implements [io/fs.ReadDirFS]. -func (dir dirFS) ReadDir(name string) ([]DirEntry, error) { - fullname, err := dir.join(name) - if err != nil { - return nil, &PathError{Op: "readdir", Path: name, Err: err} - } - entries, err := ReadDir(fullname) - if err != nil { - if e, ok := err.(*PathError); ok { - // See comment in dirFS.Open. - e.Path = name - } - return nil, err - } - return entries, nil -} - -func (dir dirFS) Stat(name string) (fs.FileInfo, error) { - fullname, err := dir.join(name) - if err != nil { - return nil, &PathError{Op: "stat", Path: name, Err: err} - } - f, err := Stat(fullname) - if err != nil { - // See comment in dirFS.Open. - err.(*PathError).Path = name - return nil, err - } - return f, nil -} - -func (dir dirFS) Lstat(name string) (fs.FileInfo, error) { - fullname, err := dir.join(name) - if err != nil { - return nil, &PathError{Op: "lstat", Path: name, Err: err} - } - f, err := Lstat(fullname) - if err != nil { - // See comment in dirFS.Open. - err.(*PathError).Path = name - return nil, err - } - return f, nil -} - -func (dir dirFS) ReadLink(name string) (string, error) { - fullname, err := dir.join(name) - if err != nil { - return "", &PathError{Op: "readlink", Path: name, Err: err} - } - return Readlink(fullname) -} - -// join returns the path for name in dir. -func (dir dirFS) join(name string) (string, error) { - if dir == "" { - return "", errors.New("os: DirFS with empty root") - } - name, err := filepathlite.Localize(name) - if err != nil { - return "", ErrInvalid - } - if IsPathSeparator(dir[len(dir)-1]) { - return string(dir) + name, nil - } - return string(dir) + string(PathSeparator) + name, nil -} - -// ReadFile reads the named file and returns the contents. -// A successful call returns err == nil, not err == EOF. -// Because ReadFile reads the whole file, it does not treat an EOF from Read -// as an error to be reported. -func ReadFile(name string) ([]byte, error) { - f, err := Open(name) - if err != nil { - return nil, err - } - defer f.Close() - - return readFileContents(statOrZero(f), f.Read) -} - -func statOrZero(f *File) int64 { - if fi, err := f.Stat(); err == nil { - return fi.Size() - } - return 0 -} - -// readFileContents reads the contents of a file using the provided read function -// (*os.File.Read, except in tests) one or more times, until an error is seen. -// -// The provided size is the stat size of the file, which might be 0 for a -// /proc-like file that doesn't report a size. -func readFileContents(statSize int64, read func([]byte) (int, error)) ([]byte, error) { - zeroSize := statSize == 0 - - // Figure out how big to make the initial slice. For files with known size - // that fit in memory, use that size + 1. Otherwise, use a small buffer and - // we'll grow. - var size int - if int64(int(statSize)) == statSize { - size = int(statSize) - } - size++ // one byte for final read at EOF - - const minBuf = 512 - // If a file claims a small size, read at least 512 bytes. In particular, - // files in Linux's /proc claim size 0 but then do not work right if read in - // small pieces, so an initial read of 1 byte would not work correctly. - if size < minBuf { - size = minBuf - } - - data := make([]byte, 0, size) - for { - n, err := read(data[len(data):cap(data)]) - data = data[:len(data)+n] - if err != nil { - if err == io.EOF { - err = nil - } - return data, err - } - - // If we're either out of capacity or if the file was a /proc-like zero - // sized file, grow the buffer. Per Issue 72080, we always want to issue - // Read calls on zero-length files with a non-tiny buffer size. - capRemain := cap(data) - len(data) - if capRemain == 0 || (zeroSize && capRemain < minBuf) { - data = slices.Grow(data, minBuf) - } - } -} - -// WriteFile writes data to the named file, creating it if necessary. -// If the file does not exist, WriteFile creates it with permissions perm (before umask); -// otherwise WriteFile truncates it before writing, without changing permissions. -// Since WriteFile requires multiple system calls to complete, a failure mid-operation -// can leave the file in a partially written state. -func WriteFile(name string, data []byte, perm FileMode) error { - f, err := OpenFile(name, O_WRONLY|O_CREATE|O_TRUNC, perm) - if err != nil { - return err - } - _, err = f.Write(data) - if err1 := f.Close(); err1 != nil && err == nil { - err = err1 - } - return err -} |
