diff options
| author | 2023-04-28 01:36:44 +0800 | |
|---|---|---|
| committer | 2023-04-28 01:36:44 +0800 | |
| commit | dd84b9d64fb98746a230cd24233ff50a562c39c9 (patch) | |
| tree | b583261ef00b3afe72ec4d6dacb31e57779a6faf /cli/internal/fs/copy_file.go | |
| parent | 0b46fcd72ac34382387b2bcf9095233efbcc52f4 (diff) | |
| download | HydroRoll-dd84b9d64fb98746a230cd24233ff50a562c39c9.tar.gz HydroRoll-dd84b9d64fb98746a230cd24233ff50a562c39c9.zip | |
Diffstat (limited to 'cli/internal/fs/copy_file.go')
| -rw-r--r-- | cli/internal/fs/copy_file.go | 81 |
1 files changed, 81 insertions, 0 deletions
diff --git a/cli/internal/fs/copy_file.go b/cli/internal/fs/copy_file.go new file mode 100644 index 0000000..e7619de --- /dev/null +++ b/cli/internal/fs/copy_file.go @@ -0,0 +1,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, + }) +} |
