|  | // 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. | 
|  | // A file may implement additional interfaces, such as | 
|  | // ReadDirFile, ReaderAt, or Seeker, to provide additional or optimized functionality. | 
|  | 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() interface{}   // 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() | 
|  | } |