From c7d5e7de72241507c45a9ff0f989b3cd3b385424 Mon Sep 17 00:00:00 2001 From: Lea Anthony Date: Wed, 30 Dec 2020 08:25:16 +1100 Subject: [PATCH] Add CopyDir --- v2/internal/fs/fs.go | 59 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/v2/internal/fs/fs.go b/v2/internal/fs/fs.go index 1915bc22..ff89de77 100644 --- a/v2/internal/fs/fs.go +++ b/v2/internal/fs/fs.go @@ -218,3 +218,62 @@ func DirIsEmpty(dir string) (bool, error) { } return false, err // Either not empty or error, suits both cases } + +// Credit: https://gist.github.com/r0l1/92462b38df26839a3ca324697c8cba04 +// CopyDir recursively copies a directory tree, attempting to preserve permissions. +// Source directory must exist, destination directory must *not* exist. +// Symlinks are ignored and skipped. +func CopyDir(src string, dst string) (err error) { + src = filepath.Clean(src) + dst = filepath.Clean(dst) + + si, err := os.Stat(src) + if err != nil { + return err + } + if !si.IsDir() { + return fmt.Errorf("source is not a directory") + } + + _, err = os.Stat(dst) + if err != nil && !os.IsNotExist(err) { + return + } + if err == nil { + return fmt.Errorf("destination already exists") + } + + err = os.MkdirAll(dst, si.Mode()) + if err != nil { + return + } + + entries, err := ioutil.ReadDir(src) + if err != nil { + return + } + + for _, entry := range entries { + srcPath := filepath.Join(src, entry.Name()) + dstPath := filepath.Join(dst, entry.Name()) + + if entry.IsDir() { + err = CopyDir(srcPath, dstPath) + if err != nil { + return + } + } else { + // Skip symlinks. + if entry.Mode()&os.ModeSymlink != 0 { + continue + } + + err = CopyFile(srcPath, dstPath) + if err != nil { + return + } + } + } + + return +}