1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
|
// Adapted from https://github.com/thought-machine/please
// Copyright Thought Machine, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package fs
import (
"errors"
"os"
"path/filepath"
"github.com/karrick/godirwalk"
)
// RecursiveCopy copies either a single file or a directory.
// 'mode' is the mode of the destination file.
func RecursiveCopy(from string, to string) error {
// Verified all callers are passing in absolute paths for from (and to)
statedFrom := LstatCachedFile{Path: UnsafeToAbsoluteSystemPath(from)}
fromType, err := statedFrom.GetType()
if err != nil {
return err
}
if fromType.IsDir() {
return WalkMode(statedFrom.Path.ToStringDuringMigration(), func(name string, isDir bool, fileType os.FileMode) error {
dest := filepath.Join(to, name[len(statedFrom.Path.ToString()):])
// name is absolute, (originates from godirwalk)
src := LstatCachedFile{Path: UnsafeToAbsoluteSystemPath(name), fileType: &fileType}
if isDir {
mode, err := src.GetMode()
if err != nil {
return err
}
return os.MkdirAll(dest, mode)
}
return CopyFile(&src, dest)
})
}
return CopyFile(&statedFrom, to)
}
// Walk implements an equivalent to filepath.Walk.
// It's implemented over github.com/karrick/godirwalk but the provided interface doesn't use that
// to make it a little easier to handle.
func Walk(rootPath string, callback func(name string, isDir bool) error) error {
return WalkMode(rootPath, func(name string, isDir bool, mode os.FileMode) error {
return callback(name, isDir)
})
}
// WalkMode is like Walk but the callback receives an additional type specifying the file mode type.
// N.B. This only includes the bits of the mode that determine the mode type, not the permissions.
func WalkMode(rootPath string, callback func(name string, isDir bool, mode os.FileMode) error) error {
return godirwalk.Walk(rootPath, &godirwalk.Options{
Callback: func(name string, info *godirwalk.Dirent) error {
// currently we support symlinked files, but not symlinked directories:
// For copying, we Mkdir and bail if we encounter a symlink to a directoy
// For finding packages, we enumerate the symlink, but don't follow inside
isDir, err := info.IsDirOrSymlinkToDir()
if err != nil {
pathErr := &os.PathError{}
if errors.As(err, &pathErr) {
// If we have a broken link, skip this entry
return godirwalk.SkipThis
}
return err
}
return callback(name, isDir, info.ModeType())
},
ErrorCallback: func(pathname string, err error) godirwalk.ErrorAction {
pathErr := &os.PathError{}
if errors.As(err, &pathErr) {
return godirwalk.SkipNode
}
return godirwalk.Halt
},
Unsorted: true,
AllowNonDirectory: true,
FollowSymbolicLinks: false,
})
}
|