1package getter
2
3import (
4	"context"
5	"os"
6	"path/filepath"
7	"strings"
8)
9
10// mode returns the file mode masked by the umask
11func mode(mode, umask os.FileMode) os.FileMode {
12	return mode & ^umask
13}
14
15// copyDir copies the src directory contents into dst. Both directories
16// should already exist.
17//
18// If ignoreDot is set to true, then dot-prefixed files/folders are ignored.
19func copyDir(ctx context.Context, dst string, src string, ignoreDot bool, umask os.FileMode) error {
20	src, err := filepath.EvalSymlinks(src)
21	if err != nil {
22		return err
23	}
24
25	walkFn := func(path string, info os.FileInfo, err error) error {
26		if err != nil {
27			return err
28		}
29		if path == src {
30			return nil
31		}
32
33		if ignoreDot && strings.HasPrefix(filepath.Base(path), ".") {
34			// Skip any dot files
35			if info.IsDir() {
36				return filepath.SkipDir
37			} else {
38				return nil
39			}
40		}
41
42		// The "path" has the src prefixed to it. We need to join our
43		// destination with the path without the src on it.
44		dstPath := filepath.Join(dst, path[len(src):])
45
46		// If we have a directory, make that subdirectory, then continue
47		// the walk.
48		if info.IsDir() {
49			if path == filepath.Join(src, dst) {
50				// dst is in src; don't walk it.
51				return nil
52			}
53
54			if err := os.MkdirAll(dstPath, mode(0755, umask)); err != nil {
55				return err
56			}
57
58			return nil
59		}
60
61		// If we have a file, copy the contents.
62		_, err = copyFile(ctx, dstPath, path, info.Mode(), umask)
63		return err
64	}
65
66	return filepath.Walk(src, walkFn)
67}
68