| // Copyright 2020 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 fs defines basic interfaces to a file system. |
| // A file system can be provided by the host operating system |
| // but also by other packages. |
| package fs |
| |
| import ( |
| "internal/oserror" |
| "time" |
| "unicode/utf8" |
| ) |
| |
| // An FS provides access to a hierarchical file system. |
| // |
| // The FS interface is the minimum implementation required of the file system. |
| // A file system may implement additional interfaces, |
| // such as ReadFileFS, to provide additional or optimized functionality. |
| type FS interface { |
| // Open opens the named file. |
| // |
| // When Open returns an error, it should be of type *PathError |
| // with the Op field set to "open", the Path field set to name, |
| // and the Err field describing the problem. |
| // |
| // Open should reject attempts to open names that do not satisfy |
| // ValidPath(name), returning a *PathError with Err set to |
| // ErrInvalid or ErrNotExist. |
| Open(name string) (File, error) |
| } |
| |
| // ValidPath reports whether the given path name |
| // is valid for use in a call to Open. |
| // |
| // Path names passed to open are UTF-8-encoded, |
| // unrooted, slash-separated sequences of path elements, like “x/y/z”. |
| // Path names must not contain an element that is “.” or “..” or the empty string, |
| // except for the special case that the root directory is named “.”. |
| // Paths must not start or end with a slash: “/x” and “x/” are invalid. |
| // |
| // Note that paths are slash-separated on all systems, even Windows. |
| // Paths containing other characters such as backslash and colon |
| // are accepted as valid, but those characters must never be |
| // interpreted by an FS implementation as path element separators. |
| func ValidPath(name string) bool { |
| if !utf8.ValidString(name) { |
| return false |
| } |
| |
| if name == "." { |
| // special case |
| return true |
| } |
| |
| // Iterate over elements in name, checking each. |
| for { |
| i := 0 |
| for i < len(name) && name[i] != '/' { |
| i++ |
| } |
| elem := name[:i] |
| if elem == "" || elem == "." || elem == ".." { |
| return false |
| } |
| if i == len(name) { |
| return true // reached clean ending |
| } |
| name = name[i+1:] |
| } |
| } |
| |
| // A File provides access to a single file. |
| // The File interface is the minimum implementation required of the file. |
| // Directory files should also implement ReadDirFile. |
| // A file may implement io.ReaderAt or io.Seeker as optimizations. |
| type File interface { |
| Stat() (FileInfo, error) |
| Read([]byte) (int, error) |
| Close() error |
| } |
| |
| // A DirEntry is an entry read from a directory |
| // (using the ReadDir function or a ReadDirFile's ReadDir method). |
| type DirEntry interface { |
| // Name returns the name of the file (or subdirectory) described by the entry. |
| // This name is only the final element of the path (the base name), not the entire path. |
| // For example, Name would return "hello.go" not "home/gopher/hello.go". |
| Name() string |
| |
| // IsDir reports whether the entry describes a directory. |
| IsDir() bool |
| |
| // Type returns the type bits for the entry. |
| // The type bits are a subset of the usual FileMode bits, those returned by the FileMode.Type method. |
| Type() FileMode |
| |
| // Info returns the FileInfo for the file or subdirectory described by the entry. |
| // The returned FileInfo may be from the time of the original directory read |
| // or from the time of the call to Info. If the file has been removed or renamed |
| // since the directory read, Info may return an error satisfying errors.Is(err, ErrNotExist). |
| // If the entry denotes a symbolic link, Info reports the information about the link itself, |
| // not the link's target. |
| Info() (FileInfo, error) |
| } |
| |
| // A ReadDirFile is a directory file whose entries can be read with the ReadDir method. |
| // Every directory file should implement this interface. |
| // (It is permissible for any file to implement this interface, |
| // but if so ReadDir should return an error for non-directories.) |
| type ReadDirFile interface { |
| File |
| |
| // ReadDir reads the contents of the directory and returns |
| // a slice of up to n DirEntry values in directory order. |
| // Subsequent calls on the same file will yield further DirEntry values. |
| // |
| // If n > 0, ReadDir returns at most n DirEntry structures. |
| // In this case, if ReadDir returns an empty slice, it will return |
| // a non-nil error explaining why. |
| // At the end of a directory, the error is io.EOF. |
| // |
| // If n <= 0, ReadDir returns all the DirEntry values from the directory |
| // in a single slice. In this case, if ReadDir succeeds (reads all the way |
| // to the end of the directory), it returns the slice and a nil error. |
| // If it encounters an error before the end of the directory, |
| // ReadDir returns the DirEntry list read until that point and a non-nil error. |
| ReadDir(n int) ([]DirEntry, error) |
| } |
| |
| // Generic file system errors. |
| // Errors returned by file systems can be tested against these errors |
| // using errors.Is. |
| var ( |
| ErrInvalid = errInvalid() // "invalid argument" |
| ErrPermission = errPermission() // "permission denied" |
| ErrExist = errExist() // "file already exists" |
| ErrNotExist = errNotExist() // "file does not exist" |
| ErrClosed = errClosed() // "file already closed" |
| ) |
| |
| func errInvalid() error { return oserror.ErrInvalid } |
| func errPermission() error { return oserror.ErrPermission } |
| func errExist() error { return oserror.ErrExist } |
| func errNotExist() error { return oserror.ErrNotExist } |
| func errClosed() error { return oserror.ErrClosed } |
| |
| // A FileInfo describes a file and is returned by Stat. |
| type FileInfo interface { |
| Name() string // base name of the file |
| Size() int64 // length in bytes for regular files; system-dependent for others |
| Mode() FileMode // file mode bits |
| ModTime() time.Time // modification time |
| IsDir() bool // abbreviation for Mode().IsDir() |
| Sys() any // underlying data source (can return nil) |
| } |
| |
| // A FileMode represents a file's mode and permission bits. |
| // The bits have the same definition on all systems, so that |
| // information about files can be moved from one system |
| // to another portably. Not all bits apply to all systems. |
| // The only required bit is ModeDir for directories. |
| type FileMode uint32 |
| |
| // The defined file mode bits are the most significant bits of the FileMode. |
| // The nine least-significant bits are the standard Unix rwxrwxrwx permissions. |
| // The values of these bits should be considered part of the public API and |
| // may be used in wire protocols or disk representations: they must not be |
| // changed, although new bits might be added. |
| const ( |
| // The single letters are the abbreviations |
| // used by the String method's formatting. |
| ModeDir FileMode = 1 << (32 - 1 - iota) // d: is a directory |
| ModeAppend // a: append-only |
| ModeExclusive // l: exclusive use |
| ModeTemporary // T: temporary file; Plan 9 only |
| ModeSymlink // L: symbolic link |
| ModeDevice // D: device file |
| ModeNamedPipe // p: named pipe (FIFO) |
| ModeSocket // S: Unix domain socket |
| ModeSetuid // u: setuid |
| ModeSetgid // g: setgid |
| ModeCharDevice // c: Unix character device, when ModeDevice is set |
| ModeSticky // t: sticky |
| ModeIrregular // ?: non-regular file; nothing else is known about this file |
| |
| // Mask for the type bits. For regular files, none will be set. |
| ModeType = ModeDir | ModeSymlink | ModeNamedPipe | ModeSocket | ModeDevice | ModeCharDevice | ModeIrregular |
| |
| ModePerm FileMode = 0777 // Unix permission bits |
| ) |
| |
| func (m FileMode) String() string { |
| const str = "dalTLDpSugct?" |
| var buf [32]byte // Mode is uint32. |
| w := 0 |
| for i, c := range str { |
| if m&(1<<uint(32-1-i)) != 0 { |
| buf[w] = byte(c) |
| w++ |
| } |
| } |
| if w == 0 { |
| buf[w] = '-' |
| w++ |
| } |
| const rwx = "rwxrwxrwx" |
| for i, c := range rwx { |
| if m&(1<<uint(9-1-i)) != 0 { |
| buf[w] = byte(c) |
| } else { |
| buf[w] = '-' |
| } |
| w++ |
| } |
| return string(buf[:w]) |
| } |
| |
| // IsDir reports whether m describes a directory. |
| // That is, it tests for the ModeDir bit being set in m. |
| func (m FileMode) IsDir() bool { |
| return m&ModeDir != 0 |
| } |
| |
| // IsRegular reports whether m describes a regular file. |
| // That is, it tests that no mode type bits are set. |
| func (m FileMode) IsRegular() bool { |
| return m&ModeType == 0 |
| } |
| |
| // Perm returns the Unix permission bits in m (m & ModePerm). |
| func (m FileMode) Perm() FileMode { |
| return m & ModePerm |
| } |
| |
| // Type returns type bits in m (m & ModeType). |
| func (m FileMode) Type() FileMode { |
| return m & ModeType |
| } |
| |
| // PathError records an error and the operation and file path that caused it. |
| type PathError struct { |
| Op string |
| Path string |
| Err error |
| } |
| |
| func (e *PathError) Error() string { return e.Op + " " + e.Path + ": " + e.Err.Error() } |
| |
| func (e *PathError) Unwrap() error { return e.Err } |
| |
| // Timeout reports whether this error represents a timeout. |
| func (e *PathError) Timeout() bool { |
| t, ok := e.Err.(interface{ Timeout() bool }) |
| return ok && t.Timeout() |
| } |