Fix issues with sync.Pool being passed an array and not a pointer. See https://github.com/dominikh/go-tools/blob/master/cmd/staticcheck/docs/checks/SA6002 Add missing tests for content.Copy Fix T.Fatal being called in a goroutine Signed-off-by: Daniel Nephin <dnephin@gmail.com>
		
			
				
	
	
		
			120 lines
		
	
	
		
			3.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			120 lines
		
	
	
		
			3.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package fs
 | 
						|
 | 
						|
import (
 | 
						|
	"io/ioutil"
 | 
						|
	"os"
 | 
						|
	"path/filepath"
 | 
						|
	"sync"
 | 
						|
 | 
						|
	"github.com/pkg/errors"
 | 
						|
)
 | 
						|
 | 
						|
var bufferPool = &sync.Pool{
 | 
						|
	New: func() interface{} {
 | 
						|
		buffer := make([]byte, 32*1024)
 | 
						|
		return &buffer
 | 
						|
	},
 | 
						|
}
 | 
						|
 | 
						|
// CopyDir copies the directory from src to dst.
 | 
						|
// Most efficient copy of files is attempted.
 | 
						|
func CopyDir(dst, src string) error {
 | 
						|
	inodes := map[uint64]string{}
 | 
						|
	return copyDirectory(dst, src, inodes)
 | 
						|
}
 | 
						|
 | 
						|
func copyDirectory(dst, src string, inodes map[uint64]string) error {
 | 
						|
	stat, err := os.Stat(src)
 | 
						|
	if err != nil {
 | 
						|
		return errors.Wrapf(err, "failed to stat %s", src)
 | 
						|
	}
 | 
						|
	if !stat.IsDir() {
 | 
						|
		return errors.Errorf("source is not directory")
 | 
						|
	}
 | 
						|
 | 
						|
	if st, err := os.Stat(dst); err != nil {
 | 
						|
		if err := os.Mkdir(dst, stat.Mode()); err != nil {
 | 
						|
			return errors.Wrapf(err, "failed to mkdir %s", dst)
 | 
						|
		}
 | 
						|
	} else if !st.IsDir() {
 | 
						|
		return errors.Errorf("cannot copy to non-directory: %s", dst)
 | 
						|
	} else {
 | 
						|
		if err := os.Chmod(dst, stat.Mode()); err != nil {
 | 
						|
			return errors.Wrapf(err, "failed to chmod on %s", dst)
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	fis, err := ioutil.ReadDir(src)
 | 
						|
	if err != nil {
 | 
						|
		return errors.Wrapf(err, "failed to read %s", src)
 | 
						|
	}
 | 
						|
 | 
						|
	if err := copyFileInfo(stat, dst); err != nil {
 | 
						|
		return errors.Wrapf(err, "failed to copy file info for %s", dst)
 | 
						|
	}
 | 
						|
 | 
						|
	for _, fi := range fis {
 | 
						|
		source := filepath.Join(src, fi.Name())
 | 
						|
		target := filepath.Join(dst, fi.Name())
 | 
						|
 | 
						|
		switch {
 | 
						|
		case fi.IsDir():
 | 
						|
			if err := copyDirectory(target, source, inodes); err != nil {
 | 
						|
				return err
 | 
						|
			}
 | 
						|
			continue
 | 
						|
		case (fi.Mode() & os.ModeType) == 0:
 | 
						|
			link, err := getLinkSource(target, fi, inodes)
 | 
						|
			if err != nil {
 | 
						|
				return errors.Wrap(err, "failed to get hardlink")
 | 
						|
			}
 | 
						|
			if link != "" {
 | 
						|
				if err := os.Link(link, target); err != nil {
 | 
						|
					return errors.Wrap(err, "failed to create hard link")
 | 
						|
				}
 | 
						|
			} else if err := copyFile(source, target); err != nil {
 | 
						|
				return errors.Wrap(err, "failed to copy files")
 | 
						|
			}
 | 
						|
		case (fi.Mode() & os.ModeSymlink) == os.ModeSymlink:
 | 
						|
			link, err := os.Readlink(source)
 | 
						|
			if err != nil {
 | 
						|
				return errors.Wrapf(err, "failed to read link: %s", source)
 | 
						|
			}
 | 
						|
			if err := os.Symlink(link, target); err != nil {
 | 
						|
				return errors.Wrapf(err, "failed to create symlink: %s", target)
 | 
						|
			}
 | 
						|
		case (fi.Mode() & os.ModeDevice) == os.ModeDevice:
 | 
						|
			if err := copyDevice(target, fi); err != nil {
 | 
						|
				return errors.Wrapf(err, "failed to create device")
 | 
						|
			}
 | 
						|
		default:
 | 
						|
			// TODO: Support pipes and sockets
 | 
						|
			return errors.Wrapf(err, "unsupported mode %s", fi.Mode())
 | 
						|
		}
 | 
						|
		if err := copyFileInfo(fi, target); err != nil {
 | 
						|
			return errors.Wrap(err, "failed to copy file info")
 | 
						|
		}
 | 
						|
 | 
						|
		if err := copyXAttrs(target, source); err != nil {
 | 
						|
			return errors.Wrap(err, "failed to copy xattrs")
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func copyFile(source, target string) error {
 | 
						|
	src, err := os.Open(source)
 | 
						|
	if err != nil {
 | 
						|
		return errors.Wrapf(err, "failed to open source %s", source)
 | 
						|
	}
 | 
						|
	defer src.Close()
 | 
						|
	tgt, err := os.Create(target)
 | 
						|
	if err != nil {
 | 
						|
		return errors.Wrapf(err, "failed to open target %s", target)
 | 
						|
	}
 | 
						|
	defer tgt.Close()
 | 
						|
 | 
						|
	return copyFileContent(tgt, src)
 | 
						|
}
 |