github.com/pingcap/br@v5.3.0-alpha.0.20220125034240-ec59c7b6ce30+incompatible/pkg/storage/local.go (about)

     1  // Copyright 2020 PingCAP, Inc. Licensed under Apache-2.0.
     2  
     3  package storage
     4  
     5  import (
     6  	"bufio"
     7  	"context"
     8  	"os"
     9  	"path/filepath"
    10  
    11  	"github.com/pingcap/errors"
    12  )
    13  
    14  const (
    15  	localDirPerm  os.FileMode = 0o755
    16  	localFilePerm os.FileMode = 0o644
    17  	// LocalURIPrefix represents the local storage prefix.
    18  	LocalURIPrefix = "file://"
    19  )
    20  
    21  // LocalStorage represents local file system storage.
    22  //
    23  // export for using in tests.
    24  type LocalStorage struct {
    25  	base string
    26  }
    27  
    28  // WriteFile writes data to a file to storage.
    29  func (l *LocalStorage) WriteFile(ctx context.Context, name string, data []byte) error {
    30  	path := filepath.Join(l.base, name)
    31  	return os.WriteFile(path, data, localFilePerm)
    32  	// the backup meta file _is_ intended to be world-readable.
    33  }
    34  
    35  // ReadFile reads the file from the storage and returns the contents.
    36  func (l *LocalStorage) ReadFile(ctx context.Context, name string) ([]byte, error) {
    37  	path := filepath.Join(l.base, name)
    38  	return os.ReadFile(path)
    39  }
    40  
    41  // FileExists implement ExternalStorage.FileExists.
    42  func (l *LocalStorage) FileExists(ctx context.Context, name string) (bool, error) {
    43  	path := filepath.Join(l.base, name)
    44  	return pathExists(path)
    45  }
    46  
    47  // WalkDir traverse all the files in a dir.
    48  //
    49  // fn is the function called for each regular file visited by WalkDir.
    50  // The first argument is the file path that can be used in `Open`
    51  // function; the second argument is the size in byte of the file determined
    52  // by path.
    53  func (l *LocalStorage) WalkDir(ctx context.Context, opt *WalkOption, fn func(string, int64) error) error {
    54  	base := filepath.Join(l.base, opt.SubDir)
    55  	return filepath.Walk(base, func(path string, f os.FileInfo, err error) error {
    56  		if os.IsNotExist(err) {
    57  			// if path not exists, we should return nil to continue.
    58  			return nil
    59  		}
    60  		if err != nil {
    61  			return errors.Trace(err)
    62  		}
    63  
    64  		if f == nil || f.IsDir() {
    65  			return nil
    66  		}
    67  		// in mac osx, the path parameter is absolute path; in linux, the path is relative path to execution base dir,
    68  		// so use Rel to convert to relative path to l.base
    69  		path, _ = filepath.Rel(l.base, path)
    70  
    71  		size := f.Size()
    72  		// if not a regular file, we need to use os.stat to get the real file size
    73  		if !f.Mode().IsRegular() {
    74  			stat, err := os.Stat(filepath.Join(l.base, path))
    75  			if err != nil {
    76  				return errors.Trace(err)
    77  			}
    78  			size = stat.Size()
    79  		}
    80  		return fn(path, size)
    81  	})
    82  }
    83  
    84  // URI returns the base path as an URI with a file:/// prefix.
    85  func (l *LocalStorage) URI() string {
    86  	return LocalURIPrefix + "/" + l.base
    87  }
    88  
    89  // Open a Reader by file path, path is a relative path to base path.
    90  func (l *LocalStorage) Open(ctx context.Context, path string) (ExternalFileReader, error) {
    91  	return os.Open(filepath.Join(l.base, path))
    92  }
    93  
    94  // Create implements ExternalStorage interface.
    95  func (l *LocalStorage) Create(ctx context.Context, name string) (ExternalFileWriter, error) {
    96  	file, err := os.Create(filepath.Join(l.base, name))
    97  	if err != nil {
    98  		return nil, errors.Trace(err)
    99  	}
   100  	buf := bufio.NewWriter(file)
   101  	return newFlushStorageWriter(buf, buf, file), nil
   102  }
   103  
   104  func pathExists(_path string) (bool, error) {
   105  	_, err := os.Stat(_path)
   106  	if err != nil {
   107  		if os.IsNotExist(err) {
   108  			return false, nil
   109  		}
   110  		return false, errors.Trace(err)
   111  	}
   112  	return true, nil
   113  }
   114  
   115  // NewLocalStorage return a LocalStorage at directory `base`.
   116  //
   117  // export for test.
   118  func NewLocalStorage(base string) (*LocalStorage, error) {
   119  	ok, err := pathExists(base)
   120  	if err != nil {
   121  		return nil, errors.Trace(err)
   122  	}
   123  	if !ok {
   124  		err := mkdirAll(base)
   125  		if err != nil {
   126  			return nil, errors.Trace(err)
   127  		}
   128  	}
   129  	return &LocalStorage{base: base}, nil
   130  }