github.com/zuoyebang/bitalostable@v1.0.1-0.20240229032404-e3b99a834294/vfs/vfs.go (about) 1 // Copyright 2012 The LevelDB-Go and Pebble Authors. All rights reserved. Use 2 // of this source code is governed by a BSD-style license that can be found in 3 // the LICENSE file. 4 5 package vfs 6 7 import ( 8 "io" 9 "os" 10 "path/filepath" 11 "syscall" 12 13 "github.com/cockroachdb/errors" 14 "github.com/cockroachdb/errors/oserror" 15 ) 16 17 // File is a readable, writable sequence of bytes. 18 // 19 // Typically, it will be an *os.File, but test code may choose to substitute 20 // memory-backed implementations. 21 // 22 // Write-oriented operations (Write, Sync) must be called sequentially: At most 23 // 1 call to Write or Sync may be executed at any given time. 24 type File interface { 25 io.Closer 26 io.Reader 27 io.ReaderAt 28 // Unlike the specification for io.Writer.Write(), the vfs.File.Write() 29 // method *is* allowed to modify the slice passed in, whether temporarily 30 // or permanently. Callers of Write() need to take this into account. 31 io.Writer 32 Stat() (os.FileInfo, error) 33 Sync() error 34 } 35 36 // OpenOption provide an interface to do work on file handles in the Open() 37 // call. 38 type OpenOption interface { 39 // Apply is called on the file handle after it's opened. 40 Apply(File) 41 } 42 43 // FS is a namespace for files. 44 // 45 // The names are filepath names: they may be / separated or \ separated, 46 // depending on the underlying operating system. 47 type FS interface { 48 // Create creates the named file for reading and writing. If a file 49 // already exists at the provided name, it's removed first ensuring the 50 // resulting file descriptor points to a new inode. 51 Create(name string) (File, error) 52 53 // Link creates newname as a hard link to the oldname file. 54 Link(oldname, newname string) error 55 56 // Open opens the named file for reading. openOptions provides 57 Open(name string, opts ...OpenOption) (File, error) 58 59 // OpenDir opens the named directory for syncing. 60 OpenDir(name string) (File, error) 61 62 // Remove removes the named file or directory. 63 Remove(name string) error 64 65 // Remove removes the named file or directory and any children it 66 // contains. It removes everything it can but returns the first error it 67 // encounters. 68 RemoveAll(name string) error 69 70 // Rename renames a file. It overwrites the file at newname if one exists, 71 // the same as os.Rename. 72 Rename(oldname, newname string) error 73 74 // ReuseForWrite attempts to reuse the file with oldname by renaming it to newname and opening 75 // it for writing without truncation. It is acceptable for the implementation to choose not 76 // to reuse oldname, and simply create the file with newname -- in this case the implementation 77 // should delete oldname. If the caller calls this function with an oldname that does not exist, 78 // the implementation may return an error. 79 ReuseForWrite(oldname, newname string) (File, error) 80 81 // MkdirAll creates a directory and all necessary parents. The permission 82 // bits perm have the same semantics as in os.MkdirAll. If the directory 83 // already exists, MkdirAll does nothing and returns nil. 84 MkdirAll(dir string, perm os.FileMode) error 85 86 // Lock locks the given file, creating the file if necessary, and 87 // truncating the file if it already exists. The lock is an exclusive lock 88 // (a write lock), but locked files should neither be read from nor written 89 // to. Such files should have zero size and only exist to co-ordinate 90 // ownership across processes. 91 // 92 // A nil Closer is returned if an error occurred. Otherwise, close that 93 // Closer to release the lock. 94 // 95 // On Linux and OSX, a lock has the same semantics as fcntl(2)'s advisory 96 // locks. In particular, closing any other file descriptor for the same 97 // file will release the lock prematurely. 98 // 99 // Attempting to lock a file that is already locked by the current process 100 // returns an error and leaves the existing lock untouched. 101 // 102 // Lock is not yet implemented on other operating systems, and calling it 103 // will return an error. 104 Lock(name string) (io.Closer, error) 105 106 // List returns a listing of the given directory. The names returned are 107 // relative to dir. 108 List(dir string) ([]string, error) 109 110 // Stat returns an os.FileInfo describing the named file. 111 Stat(name string) (os.FileInfo, error) 112 113 // PathBase returns the last element of path. Trailing path separators are 114 // removed before extracting the last element. If the path is empty, PathBase 115 // returns ".". If the path consists entirely of separators, PathBase returns a 116 // single separator. 117 PathBase(path string) string 118 119 // PathJoin joins any number of path elements into a single path, adding a 120 // separator if necessary. 121 PathJoin(elem ...string) string 122 123 // PathDir returns all but the last element of path, typically the path's directory. 124 PathDir(path string) string 125 126 // GetDiskUsage returns disk space statistics for the filesystem where 127 // path is any file or directory within that filesystem. 128 GetDiskUsage(path string) (DiskUsage, error) 129 } 130 131 // DiskUsage summarizes disk space usage on a filesystem. 132 type DiskUsage struct { 133 // Total disk space available to the current process in bytes. 134 AvailBytes uint64 135 // Total disk space in bytes. 136 TotalBytes uint64 137 // Used disk space in bytes. 138 UsedBytes uint64 139 } 140 141 // Default is a FS implementation backed by the underlying operating system's 142 // file system. 143 var Default FS = defaultFS{} 144 145 type defaultFS struct{} 146 147 func (defaultFS) Create(name string) (File, error) { 148 const openFlags = os.O_RDWR | os.O_CREATE | os.O_EXCL | syscall.O_CLOEXEC 149 150 f, err := os.OpenFile(name, openFlags, 0666) 151 // If the file already exists, remove it and try again. 152 // 153 // NB: We choose to remove the file instead of truncating it, despite the 154 // fact that we can't do so atomically, because it's more resistant to 155 // misuse when using hard links. 156 157 // We must loop in case another goroutine/thread/process is also 158 // attempting to create the a file at the same path. 159 for oserror.IsExist(err) { 160 if removeErr := os.Remove(name); removeErr != nil && !oserror.IsNotExist(removeErr) { 161 return f, errors.WithStack(removeErr) 162 } 163 f, err = os.OpenFile(name, openFlags, 0666) 164 } 165 return f, errors.WithStack(err) 166 } 167 168 func (defaultFS) Link(oldname, newname string) error { 169 return errors.WithStack(os.Link(oldname, newname)) 170 } 171 172 func (defaultFS) Open(name string, opts ...OpenOption) (File, error) { 173 file, err := os.OpenFile(name, os.O_RDONLY|syscall.O_CLOEXEC, 0) 174 if err != nil { 175 return nil, errors.WithStack(err) 176 } 177 for _, opt := range opts { 178 opt.Apply(file) 179 } 180 return file, nil 181 } 182 183 func (defaultFS) Remove(name string) error { 184 return errors.WithStack(os.Remove(name)) 185 } 186 187 func (defaultFS) RemoveAll(name string) error { 188 return errors.WithStack(os.RemoveAll(name)) 189 } 190 191 func (defaultFS) Rename(oldname, newname string) error { 192 return errors.WithStack(os.Rename(oldname, newname)) 193 } 194 195 func (fs defaultFS) ReuseForWrite(oldname, newname string) (File, error) { 196 if err := fs.Rename(oldname, newname); err != nil { 197 return nil, errors.WithStack(err) 198 } 199 f, err := os.OpenFile(newname, os.O_RDWR|os.O_CREATE|syscall.O_CLOEXEC, 0666) 200 return f, errors.WithStack(err) 201 } 202 203 func (defaultFS) MkdirAll(dir string, perm os.FileMode) error { 204 return errors.WithStack(os.MkdirAll(dir, perm)) 205 } 206 207 func (defaultFS) List(dir string) ([]string, error) { 208 f, err := os.Open(dir) 209 if err != nil { 210 return nil, err 211 } 212 defer f.Close() 213 dirnames, err := f.Readdirnames(-1) 214 return dirnames, errors.WithStack(err) 215 } 216 217 func (defaultFS) Stat(name string) (os.FileInfo, error) { 218 finfo, err := os.Stat(name) 219 return finfo, errors.WithStack(err) 220 } 221 222 func (defaultFS) PathBase(path string) string { 223 return filepath.Base(path) 224 } 225 226 func (defaultFS) PathJoin(elem ...string) string { 227 return filepath.Join(elem...) 228 } 229 230 func (defaultFS) PathDir(path string) string { 231 return filepath.Dir(path) 232 } 233 234 type randomReadsOption struct{} 235 236 // RandomReadsOption is an OpenOption that optimizes opened file handle for 237 // random reads, by calling fadvise() with POSIX_FADV_RANDOM on Linux systems 238 // to disable readahead. 239 var RandomReadsOption OpenOption = &randomReadsOption{} 240 241 // Apply implements the OpenOption interface. 242 func (randomReadsOption) Apply(f File) { 243 type fd interface { 244 Fd() uintptr 245 } 246 if fdFile, ok := f.(fd); ok { 247 _ = fadviseRandom(fdFile.Fd()) 248 } 249 } 250 251 type sequentialReadsOption struct{} 252 253 // SequentialReadsOption is an OpenOption that optimizes opened file handle for 254 // sequential reads, by calling fadvise() with POSIX_FADV_SEQUENTIAL on Linux 255 // systems to enable readahead. 256 var SequentialReadsOption OpenOption = &sequentialReadsOption{} 257 258 // Apply implements the OpenOption interface. 259 func (sequentialReadsOption) Apply(f File) { 260 type fd interface { 261 Fd() uintptr 262 } 263 if fdFile, ok := f.(fd); ok { 264 _ = fadviseSequential(fdFile.Fd()) 265 } 266 } 267 268 // Copy copies the contents of oldname to newname. If newname exists, it will 269 // be overwritten. 270 func Copy(fs FS, oldname, newname string) error { 271 src, err := fs.Open(oldname) 272 if err != nil { 273 return err 274 } 275 defer src.Close() 276 277 dst, err := fs.Create(newname) 278 if err != nil { 279 return err 280 } 281 defer dst.Close() 282 283 if _, err := io.Copy(dst, src); err != nil { 284 return err 285 } 286 return dst.Sync() 287 } 288 289 // LimitedCopy copies up to maxBytes from oldname to newname. If newname 290 // exists, it will be overwritten. 291 func LimitedCopy(fs FS, oldname, newname string, maxBytes int64) error { 292 src, err := fs.Open(oldname) 293 if err != nil { 294 return err 295 } 296 defer src.Close() 297 298 dst, err := fs.Create(newname) 299 if err != nil { 300 return err 301 } 302 defer dst.Close() 303 304 if _, err := io.Copy(dst, &io.LimitedReader{R: src, N: maxBytes}); err != nil { 305 return err 306 } 307 return dst.Sync() 308 } 309 310 // LinkOrCopy creates newname as a hard link to the oldname file. If creating 311 // the hard link fails, LinkOrCopy falls back to copying the file (which may 312 // also fail if newname doesn't exist or oldname already exists). 313 func LinkOrCopy(fs FS, oldname, newname string) error { 314 err := fs.Link(oldname, newname) 315 if err == nil { 316 return nil 317 } 318 // Permit a handful of errors which we know won't be fixed by copying the 319 // file. Note that we don't check for the specifics of the error code as it 320 // isn't easy to do so in a portable manner. On Unix we'd have to check for 321 // LinkError.Err == syscall.EXDEV. On Windows we'd have to check for 322 // ERROR_NOT_SAME_DEVICE, ERROR_INVALID_FUNCTION, and 323 // ERROR_INVALID_PARAMETER. Rather that such OS specific checks, we fall back 324 // to always trying to copy if hard-linking failed. 325 if oserror.IsExist(err) || oserror.IsNotExist(err) || oserror.IsPermission(err) { 326 return err 327 } 328 return Copy(fs, oldname, newname) 329 } 330 331 // Root returns the base FS implementation, unwrapping all nested FSs that 332 // expose an Unwrap method. 333 func Root(fs FS) FS { 334 type unwrapper interface { 335 Unwrap() FS 336 } 337 338 for { 339 u, ok := fs.(unwrapper) 340 if !ok { 341 break 342 } 343 fs = u.Unwrap() 344 } 345 return fs 346 } 347 348 // ErrUnsupported may be returned a FS when it does not support an operation. 349 var ErrUnsupported = errors.New("bitalostable: not supported")