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")