From 1ddce651c8095269210a10836a52f47748e68a98 Mon Sep 17 00:00:00 2001 From: psergee Date: Fri, 31 May 2024 17:35:36 +0300 Subject: [PATCH] Fix relative symlinks copying All relative paths of symbolic links should be resolved from the directory where the link is located. The current working directory does not affect the resolution of the path. A symbolic link can be absolute or relative, and may or may not have a leading dot. Directory content example: file symlink -> file Copying of the directory with such content does not work currently. When copying `symlink`, `Copy` returns an error because the `file` does not exist in the current working directory. In the existing tests, there is a symbolic link named "symlink" that points to a file named "README.md". However, when resolving the path of the file, the file is located in the repository's root directory, not the test case directory, and copying of these dirs work fine. The solution is to check whether the resolved path is absolute or not. If it is not, append it to the file's directory path. --- all_test.go | 2 +- copy.go | 3 +-- symlink_test.go | 7 ++++++- test/data/case03/foo | 0 test_setup_test.go | 4 ++-- test_setup_x_test.go | 2 +- 6 files changed, 11 insertions(+), 7 deletions(-) create mode 100644 test/data/case03/foo diff --git a/all_test.go b/all_test.go index a2bcd0c..67f1216 100644 --- a/all_test.go +++ b/all_test.go @@ -26,7 +26,7 @@ func TestMain(m *testing.M) { func teardown(m *testing.M) { os.RemoveAll("test/data/case03/case01") - os.RemoveAll("test/data/case03/relative_case01") + os.Remove("test/data/case03/relative_foo") os.RemoveAll("test/data.copy") os.RemoveAll("test/data.copyTime") os.RemoveAll("test/owned-by-root") // Do not check the error ;) diff --git a/copy.go b/copy.go index 39a0b01..f9787cd 100644 --- a/copy.go +++ b/copy.go @@ -6,7 +6,6 @@ import ( "io/fs" "os" "path/filepath" - "strings" "time" "golang.org/x/sync/errgroup" @@ -293,7 +292,7 @@ func onsymlink(src, dest string, opt Options) error { if err != nil { return err } - if strings.HasPrefix(orig, ".") { + if !filepath.IsAbs(orig) { // orig is a relative link: need to add src dir to orig orig = filepath.Join(filepath.Dir(src), orig) } diff --git a/symlink_test.go b/symlink_test.go index 8cad9bc..96fc4b2 100644 --- a/symlink_test.go +++ b/symlink_test.go @@ -16,7 +16,7 @@ func TestOptions_OnSymlink(t *testing.T) { info, err := os.Lstat("test/data.copy/case03.deep/case01") Expect(t, err).ToBe(nil) Expect(t, info.Mode()&os.ModeSymlink).ToBe(os.FileMode(0)) - info, err = os.Lstat("test/data.copy/case03.deep/relative_case01") + info, err = os.Lstat("test/data.copy/case03.deep/relative_foo") Expect(t, err).ToBe(nil) Expect(t, info.Mode()&os.ModeSymlink).ToBe(os.FileMode(0)) @@ -26,12 +26,17 @@ func TestOptions_OnSymlink(t *testing.T) { info, err = os.Lstat("test/data.copy/case03.shallow/case01") Expect(t, err).ToBe(nil) Expect(t, info.Mode()&os.ModeSymlink).Not().ToBe(os.FileMode(0)) + linkTarget, err := os.Readlink("test/data.copy/case03.shallow/relative_foo") + Expect(t, err).ToBe(nil) + Expect(t, linkTarget).ToBe("foo") opt = Options{OnSymlink: func(string) SymlinkAction { return Skip }} err = Copy("test/data/case03", "test/data.copy/case03.skip", opt) Expect(t, err).ToBe(nil) _, err = os.Stat("test/data.copy/case03.skip/case01") Expect(t, os.IsNotExist(err)).ToBe(true) + _, err = os.Stat("test/data.copy/case03.skip/relative_foo") + Expect(t, os.IsNotExist(err)).ToBe(true) err = Copy("test/data/case03", "test/data.copy/case03.default") Expect(t, err).ToBe(nil) diff --git a/test/data/case03/foo b/test/data/case03/foo new file mode 100644 index 0000000..e69de29 diff --git a/test_setup_test.go b/test_setup_test.go index e9059ba..a4492d6 100644 --- a/test_setup_test.go +++ b/test_setup_test.go @@ -11,8 +11,8 @@ import ( func setup(m *testing.M) { os.RemoveAll("test/data.copy") os.MkdirAll("test/data.copy", os.ModePerm) - os.Symlink("test/data/case01", "test/data/case03/case01") - os.Symlink("../case01", "test/data/case03/relative_case01") + os.Symlink("../case01", "test/data/case03/case01") + os.Symlink("foo", "test/data/case03/relative_foo") os.Chmod("test/data/case07/dir_0555", 0o555) os.Chmod("test/data/case07/file_0444", 0o444) syscall.Mkfifo("test/data/case11/foo/bar", 0o555) diff --git a/test_setup_x_test.go b/test_setup_x_test.go index 97ff6af..14a393a 100644 --- a/test_setup_x_test.go +++ b/test_setup_x_test.go @@ -10,7 +10,7 @@ import ( func setup(m *testing.M) { os.RemoveAll("test/data.copy") os.MkdirAll("test/data.copy", os.ModePerm) - os.Symlink("test/data/case01", "test/data/case03/case01") + os.Symlink("../case01", "test/data/case03/case01") os.Chmod("test/data/case07/dir_0555", 0555) os.Chmod("test/data/case07/file_0444", 0444) }