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  }