Skip to content

Commit

Permalink
Fix relative symlinks copying
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
psergee committed Jun 1, 2024
1 parent c464078 commit ece0720
Show file tree
Hide file tree
Showing 6 changed files with 15 additions and 11 deletions.
4 changes: 2 additions & 2 deletions all_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ 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 ;)
Expand Down Expand Up @@ -58,7 +58,7 @@ func TestCopy(t *testing.T) {
When(t, "source directory includes symbolic link", func(t *testing.T) {
err := Copy("test/data/case03", "test/data.copy/case03")
Expect(t, err).ToBe(nil)
info, err := os.Lstat("test/data.copy/case03/case01")
info, err := os.Lstat("test/data.copy/case03/relative_case01")
Expect(t, err).ToBe(nil)
Expect(t, info.Mode()&os.ModeSymlink).Not().ToBe(0)
})
Expand Down
3 changes: 1 addition & 2 deletions copy.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"io/fs"
"os"
"path/filepath"
"strings"
"time"

"golang.org/x/sync/errgroup"
Expand Down Expand Up @@ -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)
}
Expand Down
15 changes: 10 additions & 5 deletions symlink_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,36 +13,41 @@ func TestOptions_OnSymlink(t *testing.T) {
opt := Options{OnSymlink: func(string) SymlinkAction { return Deep }}
err := Copy("test/data/case03", "test/data.copy/case03.deep", opt)
Expect(t, err).ToBe(nil)
info, err := os.Lstat("test/data.copy/case03.deep/case01")
info, err := os.Lstat("test/data.copy/case03.deep/relative_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))

opt = Options{OnSymlink: func(string) SymlinkAction { return Shallow }}
err = Copy("test/data/case03", "test/data.copy/case03.shallow", opt)
Expect(t, err).ToBe(nil)
info, err = os.Lstat("test/data.copy/case03.shallow/case01")
info, err = os.Lstat("test/data.copy/case03.shallow/relative_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)
info, err = os.Lstat("test/data.copy/case03.default/case01")
info, err = os.Lstat("test/data.copy/case03.default/relative_case01")
Expect(t, err).ToBe(nil)
Expect(t, info.Mode()&os.ModeSymlink).Not().ToBe(os.FileMode(0))

opt = Options{OnSymlink: nil}
err = Copy("test/data/case03", "test/data.copy/case03.not-specified", opt)
Expect(t, err).ToBe(nil)
info, err = os.Lstat("test/data.copy/case03.not-specified/case01")
info, err = os.Lstat("test/data.copy/case03.not-specified/relative_case01")
Expect(t, err).ToBe(nil)
Expect(t, info.Mode()&os.ModeSymlink).Not().ToBe(os.FileMode(0))
}
Empty file added test/data/case03/foo
Empty file.
2 changes: 1 addition & 1 deletion test_setup_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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("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)
Expand Down
2 changes: 1 addition & 1 deletion test_setup_x_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}

0 comments on commit ece0720

Please sign in to comment.