From 4895fee9e71a0eacd77bdf66f1eeff69710b776b Mon Sep 17 00:00:00 2001 From: Natanael Copa Date: Wed, 5 Apr 2023 08:55:02 +0200 Subject: [PATCH 1/2] Add tests for file/symlink/dir deleted while copy Files, symlinks and directories may be deleted while or after directory list is read. Add test to simulate this so we can fix the desired behavior. ref https://github.com/otiai10/copy/issues/72 --- all_test.go | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/all_test.go b/all_test.go index 910570a..3c43b2d 100644 --- a/all_test.go +++ b/all_test.go @@ -5,6 +5,7 @@ import ( "io" "io/ioutil" "os" + "path/filepath" "runtime" "strings" "testing" @@ -86,6 +87,48 @@ func TestCopy(t *testing.T) { err = os.Chmod(dest, 0o755) Expect(t, err).ToBe(nil) }) + When(t, "file is deleted while copying", func(t *testing.T) { + src := t.TempDir() + dest := t.TempDir() + + file := filepath.Join(src, "file") + f, err := os.Create(file) + Expect(t, err).ToBe(nil) + f.Close() + + opt := Options{Skip: func(info os.FileInfo, src, dest string) (bool, error) { + os.Remove(src) + return false, nil + }} + err = Copy(src, dest, opt) + Expect(t, err).ToBe(nil) + }) + When(t, "symlink is deleted while copying", func(t *testing.T) { + src := t.TempDir() + dest := t.TempDir() + + Expect(t, os.Symlink(".", filepath.Join(src, "symlink"))).ToBe(nil) + + opt := Options{Skip: func(info os.FileInfo, src, dest string) (bool, error) { + os.Remove(src) + return false, nil + }} + err = Copy(src, dest, opt) + Expect(t, err).ToBe(nil) + }) + When(t, "directory is deleted while copying", func(t *testing.T) { + src := t.TempDir() + dest := t.TempDir() + + Expect(t, os.Mkdir(filepath.Join(src, "dir"), 0755)).ToBe(nil) + + opt := Options{Skip: func(info os.FileInfo, src, dest string) (bool, error) { + os.Remove(src) + return false, nil + }} + err = Copy(src, dest, opt) + Expect(t, err).ToBe(nil) + }) } func TestCopy_NamedPipe(t *testing.T) { From b23de9d9cb66793a853b8305edfe0dbabe0b24f0 Mon Sep 17 00:00:00 2001 From: Natanael Copa Date: Wed, 5 Apr 2023 11:47:41 +0200 Subject: [PATCH 2/2] Gracefully handle files/symlinks/dirs deleted while copy Ignore not-exist errors while doing the actualy copy because files/symlinks/dirs may be deleted while reading the source directory. fixes https://github.com/otiai10/copy/issues/72 --- copy.go | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/copy.go b/copy.go index 60643dd..d0450e7 100644 --- a/copy.go +++ b/copy.go @@ -65,6 +65,14 @@ 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) { + s, err := os.Open(src) + if err != nil { + if os.IsNotExist(err) { + return nil + } + return + } + defer fclose(s, &err) if err = os.MkdirAll(filepath.Dir(dest), os.ModePerm); err != nil { return @@ -82,12 +90,6 @@ func fcopy(src, dest string, info os.FileInfo, opt Options) (err error) { } chmodfunc(&err) - s, err := os.Open(src) - if err != nil { - return - } - defer fclose(s, &err) - var buf []byte = nil var w io.Writer = f var r io.Reader = s @@ -146,6 +148,9 @@ func dcopy(srcdir, destdir string, info os.FileInfo, opt Options) (err error) { contents, err := ioutil.ReadDir(srcdir) if err != nil { + if os.IsNotExist(err) { + return nil + } return } @@ -222,6 +227,9 @@ func onsymlink(src, dest string, opt Options) error { func lcopy(src, dest string) error { src, err := os.Readlink(src) if err != nil { + if os.IsNotExist(err) { + return nil + } return err } return os.Symlink(src, dest)