From 7bb84a487e2f7e75206dedf676ab01f746340b2f Mon Sep 17 00:00:00 2001 From: "Ethan P." Date: Sat, 24 Aug 2024 15:37:34 -0700 Subject: [PATCH] refactor: Split common code out of CopyBytes Permission changes, ownership preservation, and access/modify/creation time presevation is something all implementations need. I brought it out of the CopyBytes implementation so it can be shared when more implementations are added later. A small note: The "ignore if source file deleted" check had to be brought out of `CopyBytes`. The way it worked before is it returned no error, which meant `fcopy` would try and modify the destination file (which was never created). Instead, I check for the error explicitly in `fcopy`. --- copy.go | 41 ++++++++++++++++++++++++++++++++++++++++- copy_methods.go | 26 -------------------------- 2 files changed, 40 insertions(+), 27 deletions(-) diff --git a/copy.go b/copy.go index 39ce984..2de32a8 100644 --- a/copy.go +++ b/copy.go @@ -2,6 +2,7 @@ package copy import ( "context" + "errors" "io" "io/fs" "os" @@ -86,7 +87,45 @@ func copyNextOrSkip(src, dest string, info os.FileInfo, opt Options) error { // with considering existence of parent directory // and file permission. func fcopy(src, dest string, info os.FileInfo, opt Options) (err error) { - return opt.FileCopyFunc.fcopy(src, dest, info, opt) + if err = os.MkdirAll(filepath.Dir(dest), os.ModePerm); err != nil { + return + } + + // Use FileCopyMethod to do copy. + if err = opt.FileCopyFunc.fcopy(src, dest, info, opt); err != nil { + if pathError, ok := err.(*os.PathError); ok && errors.Is(err, os.ErrNotExist) && pathError.Path == src { + // Source file deleted. Ignore error. + return nil + } + + return err + } + + // Change file permissions. + chmodfunc, err := opt.PermissionControl(info, dest) + if err != nil { + return err + } + + chmodfunc(&err) + if err != nil { + return err + } + + // Preserve file ownership and times. + if opt.PreserveOwner { + if err := preserveOwner(src, dest, info); err != nil { + return err + } + } + + if opt.PreserveTimes { + if err := preserveTimes(info, dest); err != nil { + return err + } + } + + return } // dcopy is for a directory, diff --git a/copy_methods.go b/copy_methods.go index e41e323..19f8ec8 100644 --- a/copy_methods.go +++ b/copy_methods.go @@ -4,7 +4,6 @@ import ( "errors" "io" "os" - "path/filepath" ) // ErrUnsupportedCopyMethod is returned when the FileCopyMethod specified in @@ -17,7 +16,6 @@ var ErrUnsupportedCopyMethod = errors.New( // then writing the buffer back to the destination file. var CopyBytes = FileCopyMethod{ fcopy: func(src, dest string, info os.FileInfo, opt Options) (err error) { - var readcloser io.ReadCloser if opt.FS != nil { readcloser, err = opt.FS.Open(src) @@ -25,29 +23,16 @@ var CopyBytes = FileCopyMethod{ readcloser, err = os.Open(src) } if err != nil { - if os.IsNotExist(err) { - return nil - } return } defer fclose(readcloser, &err) - if err = os.MkdirAll(filepath.Dir(dest), os.ModePerm); err != nil { - return - } - f, err := os.Create(dest) if err != nil { return } defer fclose(f, &err) - chmodfunc, err := opt.PermissionControl(info, dest) - if err != nil { - return err - } - chmodfunc(&err) - var buf []byte = nil var w io.Writer = f var r io.Reader = readcloser @@ -72,17 +57,6 @@ var CopyBytes = FileCopyMethod{ err = f.Sync() } - if opt.PreserveOwner { - if err := preserveOwner(src, dest, info); err != nil { - return err - } - } - if opt.PreserveTimes { - if err := preserveTimes(info, dest); err != nil { - return err - } - } - return }, }