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 }