// 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. // // See the [testing/fstest] package for support with testing // implementations of file systems. 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. // // [testing/fstest.TestFS] may be used to test implementations of an FS for // correctness. 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. // (ReadDir must return io.EOF itself, not an error wrapping 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<