1 // Copyright 2020 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // Package fs defines basic interfaces to a file system. 6 // A file system can be provided by the host operating system 7 // but also by other packages. 8 package fs 9 10 import ( 11 "internal/oserror" 12 "time" 13 "unicode/utf8" 14 ) 15 16 // An FS provides access to a hierarchical file system. 17 // 18 // The FS interface is the minimum implementation required of the file system. 19 // A file system may implement additional interfaces, 20 // such as ReadFileFS, to provide additional or optimized functionality. 21 type FS interface { 22 // Open opens the named file. 23 // 24 // When Open returns an error, it should be of type *PathError 25 // with the Op field set to "open", the Path field set to name, 26 // and the Err field describing the problem. 27 // 28 // Open should reject attempts to open names that do not satisfy 29 // ValidPath(name), returning a *PathError with Err set to 30 // ErrInvalid or ErrNotExist. 31 Open(name string) (File, error) 32 } 33 34 // ValidPath reports whether the given path name 35 // is valid for use in a call to Open. 36 // 37 // Path names passed to open are UTF-8-encoded, 38 // unrooted, slash-separated sequences of path elements, like “x/y/z”. 39 // Path names must not contain an element that is “.” or “..” or the empty string, 40 // except for the special case that the root directory is named “.”. 41 // Paths must not start or end with a slash: “/x” and “x/” are invalid. 42 // 43 // Note that paths are slash-separated on all systems, even Windows. 44 // Paths containing other characters such as backslash and colon 45 // are accepted as valid, but those characters must never be 46 // interpreted by an FS implementation as path element separators. 47 func ValidPath(name string) bool { 48 if !utf8.ValidString(name) { 49 return false 50 } 51 52 if name == "." { 53 // special case 54 return true 55 } 56 57 // Iterate over elements in name, checking each. 58 for { 59 i := 0 60 for i < len(name) && name[i] != '/' { 61 i++ 62 } 63 elem := name[:i] 64 if elem == "" || elem == "." || elem == ".." { 65 return false 66 } 67 if i == len(name) { 68 return true // reached clean ending 69 } 70 name = name[i+1:] 71 } 72 } 73 74 // A File provides access to a single file. 75 // The File interface is the minimum implementation required of the file. 76 // Directory files should also implement ReadDirFile. 77 // A file may implement io.ReaderAt or io.Seeker as optimizations. 78 type File interface { 79 Stat() (FileInfo, error) 80 Read([]byte) (int, error) 81 Close() error 82 } 83 84 // A DirEntry is an entry read from a directory 85 // (using the ReadDir function or a ReadDirFile's ReadDir method). 86 type DirEntry interface { 87 // Name returns the name of the file (or subdirectory) described by the entry. 88 // This name is only the final element of the path (the base name), not the entire path. 89 // For example, Name would return "hello.go" not "home/gopher/hello.go". 90 Name() string 91 92 // IsDir reports whether the entry describes a directory. 93 IsDir() bool 94 95 // Type returns the type bits for the entry. 96 // The type bits are a subset of the usual FileMode bits, those returned by the FileMode.Type method. 97 Type() FileMode 98 99 // Info returns the FileInfo for the file or subdirectory described by the entry. 100 // The returned FileInfo may be from the time of the original directory read 101 // or from the time of the call to Info. If the file has been removed or renamed 102 // since the directory read, Info may return an error satisfying errors.Is(err, ErrNotExist). 103 // If the entry denotes a symbolic link, Info reports the information about the link itself, 104 // not the link's target. 105 Info() (FileInfo, error) 106 } 107 108 // A ReadDirFile is a directory file whose entries can be read with the ReadDir method. 109 // Every directory file should implement this interface. 110 // (It is permissible for any file to implement this interface, 111 // but if so ReadDir should return an error for non-directories.) 112 type ReadDirFile interface { 113 File 114 115 // ReadDir reads the contents of the directory and returns 116 // a slice of up to n DirEntry values in directory order. 117 // Subsequent calls on the same file will yield further DirEntry values. 118 // 119 // If n > 0, ReadDir returns at most n DirEntry structures. 120 // In this case, if ReadDir returns an empty slice, it will return 121 // a non-nil error explaining why. 122 // At the end of a directory, the error is io.EOF. 123 // 124 // If n <= 0, ReadDir returns all the DirEntry values from the directory 125 // in a single slice. In this case, if ReadDir succeeds (reads all the way 126 // to the end of the directory), it returns the slice and a nil error. 127 // If it encounters an error before the end of the directory, 128 // ReadDir returns the DirEntry list read until that point and a non-nil error. 129 ReadDir(n int) ([]DirEntry, error) 130 } 131 132 // Generic file system errors. 133 // Errors returned by file systems can be tested against these errors 134 // using errors.Is. 135 var ( 136 ErrInvalid = errInvalid() // "invalid argument" 137 ErrPermission = errPermission() // "permission denied" 138 ErrExist = errExist() // "file already exists" 139 ErrNotExist = errNotExist() // "file does not exist" 140 ErrClosed = errClosed() // "file already closed" 141 ) 142 143 func errInvalid() error { return oserror.ErrInvalid } 144 func errPermission() error { return oserror.ErrPermission } 145 func errExist() error { return oserror.ErrExist } 146 func errNotExist() error { return oserror.ErrNotExist } 147 func errClosed() error { return oserror.ErrClosed } 148 149 // A FileInfo describes a file and is returned by Stat. 150 type FileInfo interface { 151 Name() string // base name of the file 152 Size() int64 // length in bytes for regular files; system-dependent for others 153 Mode() FileMode // file mode bits 154 ModTime() time.Time // modification time 155 IsDir() bool // abbreviation for Mode().IsDir() 156 Sys() interface{} // underlying data source (can return nil) 157 } 158 159 // A FileMode represents a file's mode and permission bits. 160 // The bits have the same definition on all systems, so that 161 // information about files can be moved from one system 162 // to another portably. Not all bits apply to all systems. 163 // The only required bit is ModeDir for directories. 164 type FileMode uint32 165 166 // The defined file mode bits are the most significant bits of the FileMode. 167 // The nine least-significant bits are the standard Unix rwxrwxrwx permissions. 168 // The values of these bits should be considered part of the public API and 169 // may be used in wire protocols or disk representations: they must not be 170 // changed, although new bits might be added. 171 const ( 172 // The single letters are the abbreviations 173 // used by the String method's formatting. 174 ModeDir FileMode = 1 << (32 - 1 - iota) // d: is a directory 175 ModeAppend // a: append-only 176 ModeExclusive // l: exclusive use 177 ModeTemporary // T: temporary file; Plan 9 only 178 ModeSymlink // L: symbolic link 179 ModeDevice // D: device file 180 ModeNamedPipe // p: named pipe (FIFO) 181 ModeSocket // S: Unix domain socket 182 ModeSetuid // u: setuid 183 ModeSetgid // g: setgid 184 ModeCharDevice // c: Unix character device, when ModeDevice is set 185 ModeSticky // t: sticky 186 ModeIrregular // ?: non-regular file; nothing else is known about this file 187 188 // Mask for the type bits. For regular files, none will be set. 189 ModeType = ModeDir | ModeSymlink | ModeNamedPipe | ModeSocket | ModeDevice | ModeCharDevice | ModeIrregular 190 191 ModePerm FileMode = 0777 // Unix permission bits 192 ) 193 194 func (m FileMode) String() string { 195 const str = "dalTLDpSugct?" 196 var buf [32]byte // Mode is uint32. 197 w := 0 198 for i, c := range str { 199 if m&(1<<uint(32-1-i)) != 0 { 200 buf[w] = byte(c) 201 w++ 202 } 203 } 204 if w == 0 { 205 buf[w] = '-' 206 w++ 207 } 208 const rwx = "rwxrwxrwx" 209 for i, c := range rwx { 210 if m&(1<<uint(9-1-i)) != 0 { 211 buf[w] = byte(c) 212 } else { 213 buf[w] = '-' 214 } 215 w++ 216 } 217 return string(buf[:w]) 218 } 219 220 // IsDir reports whether m describes a directory. 221 // That is, it tests for the ModeDir bit being set in m. 222 func (m FileMode) IsDir() bool { 223 return m&ModeDir != 0 224 } 225 226 // IsRegular reports whether m describes a regular file. 227 // That is, it tests that no mode type bits are set. 228 func (m FileMode) IsRegular() bool { 229 return m&ModeType == 0 230 } 231 232 // Perm returns the Unix permission bits in m (m & ModePerm). 233 func (m FileMode) Perm() FileMode { 234 return m & ModePerm 235 } 236 237 // Type returns type bits in m (m & ModeType). 238 func (m FileMode) Type() FileMode { 239 return m & ModeType 240 } 241 242 // PathError records an error and the operation and file path that caused it. 243 type PathError struct { 244 Op string 245 Path string 246 Err error 247 } 248 249 func (e *PathError) Error() string { return e.Op + " " + e.Path + ": " + e.Err.Error() } 250 251 func (e *PathError) Unwrap() error { return e.Err } 252 253 // Timeout reports whether this error represents a timeout. 254 func (e *PathError) Timeout() bool { 255 t, ok := e.Err.(interface{ Timeout() bool }) 256 return ok && t.Timeout() 257 } 258