github.com/zuoyebang/bitalostable@v1.0.1-0.20240229032404-e3b99a834294/vfs/clone.go (about) 1 // Copyright 2019 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/ioutil" 9 "sort" 10 11 "github.com/cockroachdb/errors/oserror" 12 ) 13 14 type cloneOpts struct { 15 skip func(string) bool 16 sync bool 17 } 18 19 // A CloneOption configures the behavior of Clone. 20 type CloneOption func(*cloneOpts) 21 22 // CloneSkip configures Clone to skip files for which the provided function 23 // returns true when passed the file's path. 24 func CloneSkip(fn func(string) bool) CloneOption { 25 return func(co *cloneOpts) { co.skip = fn } 26 } 27 28 // CloneSync configures Clone to sync files and directories. 29 var CloneSync CloneOption = func(o *cloneOpts) { o.sync = true } 30 31 // Clone recursively copies a directory structure from srcFS to dstFS. srcPath 32 // specifies the path in srcFS to copy from and must be compatible with the 33 // srcFS path format. dstDir is the target directory in dstFS and must be 34 // compatible with the dstFS path format. Returns (true,nil) on a successful 35 // copy, (false,nil) if srcPath does not exist, and (false,err) if an error 36 // occurred. 37 func Clone(srcFS, dstFS FS, srcPath, dstPath string, opts ...CloneOption) (bool, error) { 38 var o cloneOpts 39 for _, opt := range opts { 40 opt(&o) 41 } 42 43 srcFile, err := srcFS.Open(srcPath) 44 if err != nil { 45 if oserror.IsNotExist(err) { 46 // Ignore non-existent errors. Those will translate into non-existent 47 // files in the destination filesystem. 48 return false, nil 49 } 50 return false, err 51 } 52 53 stat, err := srcFile.Stat() 54 if err != nil { 55 return false, err 56 } 57 58 if stat.IsDir() { 59 if err := srcFile.Close(); err != nil { 60 return false, err 61 } 62 if err := dstFS.MkdirAll(dstPath, 0755); err != nil { 63 return false, err 64 } 65 list, err := srcFS.List(srcPath) 66 if err != nil { 67 return false, err 68 } 69 // Sort the paths so we get deterministic test output. 70 sort.Strings(list) 71 for _, name := range list { 72 if o.skip != nil && o.skip(srcFS.PathJoin(srcPath, name)) { 73 continue 74 } 75 _, err := Clone(srcFS, dstFS, srcFS.PathJoin(srcPath, name), dstFS.PathJoin(dstPath, name), opts...) 76 if err != nil { 77 return false, err 78 } 79 } 80 81 if o.sync { 82 dir, err := dstFS.OpenDir(dstPath) 83 if err != nil { 84 return false, err 85 } 86 if err := dir.Sync(); err != nil { 87 return false, err 88 } 89 if err := dir.Close(); err != nil { 90 return false, err 91 } 92 } 93 94 return true, nil 95 } 96 97 data, err := ioutil.ReadAll(srcFile) 98 if err != nil { 99 return false, err 100 } 101 if err := srcFile.Close(); err != nil { 102 return false, err 103 } 104 dstFile, err := dstFS.Create(dstPath) 105 if err != nil { 106 return false, err 107 } 108 if _, err = dstFile.Write(data); err != nil { 109 return false, err 110 } 111 if o.sync { 112 if err := dstFile.Sync(); err != nil { 113 return false, err 114 } 115 } 116 117 if err := dstFile.Close(); err != nil { 118 return false, err 119 } 120 return true, nil 121 }