diff --git a/all_test.go b/all_test.go index 4b24aac..9934b83 100644 --- a/all_test.go +++ b/all_test.go @@ -27,6 +27,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.RemoveAll("test/data.copy") os.RemoveAll("test/data.copyTime") os.RemoveAll("test/owned-by-root") // Do not check the error ;) @@ -165,41 +166,6 @@ func TestCopy_NamedPipe(t *testing.T) { }) } -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") - 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") - Expect(t, err).ToBe(nil) - Expect(t, info.Mode()&os.ModeSymlink).Not().ToBe(os.FileMode(0)) - - 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 = Copy("test/data/case03", "test/data.copy/case03.default") - Expect(t, err).ToBe(nil) - info, err = os.Lstat("test/data.copy/case03.default/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") - Expect(t, err).ToBe(nil) - Expect(t, info.Mode()&os.ModeSymlink).Not().ToBe(os.FileMode(0)) -} - func TestOptions_Skip(t *testing.T) { opt := Options{Skip: func(info os.FileInfo, src, dest string) (bool, error) { switch { diff --git a/copy.go b/copy.go index 4d3ad18..592d825 100644 --- a/copy.go +++ b/copy.go @@ -6,6 +6,7 @@ import ( "io/fs" "os" "path/filepath" + "strings" "time" "golang.org/x/sync/errgroup" @@ -286,6 +287,10 @@ func onsymlink(src, dest string, opt Options) error { if err != nil { return err } + if strings.HasPrefix(orig, ".") { + // orig is a relative link: need to add src dir to orig + orig = filepath.Join(filepath.Dir(src), orig) + } info, err := os.Lstat(orig) if err != nil { return err diff --git a/symlink_test.go b/symlink_test.go new file mode 100644 index 0000000..9f464cd --- /dev/null +++ b/symlink_test.go @@ -0,0 +1,49 @@ +//go:build !windows && !plan9 && !netbsd && !aix && !illumos && !solaris && !js +// +build !windows,!plan9,!netbsd,!aix,!illumos,!solaris,!js + +package copy + +import ( + "os" + "testing" + + . "github.com/otiai10/mint" +) + +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") + 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") + 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") + Expect(t, err).ToBe(nil) + Expect(t, info.Mode()&os.ModeSymlink).Not().ToBe(os.FileMode(0)) + + 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 = Copy("test/data/case03", "test/data.copy/case03.default") + Expect(t, err).ToBe(nil) + info, err = os.Lstat("test/data.copy/case03.default/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") + Expect(t, err).ToBe(nil) + Expect(t, info.Mode()&os.ModeSymlink).Not().ToBe(os.FileMode(0)) +} diff --git a/symlink_test_x.go b/symlink_test_x.go new file mode 100644 index 0000000..523f7f0 --- /dev/null +++ b/symlink_test_x.go @@ -0,0 +1,46 @@ +//go:build windows || plan9 || netbsd || aix || illumos || solaris || js +// +build windows plan9 netbsd aix illumos solaris js + +package copy + +import ( + "os" + "testing" + + . "github.com/otiai10/mint" +) + +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") + 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") + Expect(t, err).ToBe(nil) + Expect(t, info.Mode()&os.ModeSymlink).Not().ToBe(os.FileMode(0)) + + 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 = Copy("test/data/case03", "test/data.copy/case03.default") + Expect(t, err).ToBe(nil) + info, err = os.Lstat("test/data.copy/case03.default/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") + Expect(t, err).ToBe(nil) + Expect(t, info.Mode()&os.ModeSymlink).Not().ToBe(os.FileMode(0)) +} diff --git a/test_setup_test.go b/test_setup_test.go index 64a5292..de18dc4 100644 --- a/test_setup_test.go +++ b/test_setup_test.go @@ -13,6 +13,7 @@ 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.Chmod("test/data/case07/dir_0555", 0o555) os.Chmod("test/data/case07/file_0444", 0o444) syscall.Mkfifo("test/data/case11/foo/bar", 0o555)