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