Skip to content
Closed
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions _testdata/folder_containing_symlinks/file
Empty file.
1 change: 1 addition & 0 deletions _testdata/folder_containing_symlinks/folder
41 changes: 33 additions & 8 deletions paths.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,13 @@ func (p *Path) Stat() (os.FileInfo, error) {
return os.Stat(p.path)
}

// Lstat returns a FileInfo describing the named file.
// If the file is a symbolic link, the returned FileInfo
// describes the symbolic link. Lstat makes no attempt to follow the link.
func (p *Path) Lstat() (os.FileInfo, error) {
return os.Lstat(p.path)
}

// Clone create a copy of the Path object
func (p *Path) Clone() *Path {
return New(p.path)
Expand Down Expand Up @@ -311,6 +318,16 @@ func (p *Path) IsDirCheck() (bool, error) {
return false, err
}

// IsDirCheck return true if the path exists and is a symlink. In all the other
// cases (and also in case of any error) false is returned.
func (p *Path) IsSymlink() (bool, error) {
info, err := p.Lstat()
if err != nil {
return false, fmt.Errorf("getting lstat info for %s: %s", p.path, err)
}
return info.Mode()&os.ModeSymlink != 0, nil
}

// CopyTo copies the contents of the file named src to the file named
// by dst. The file will be created if it does not already exist. If the
// destination file exists, all it's contents will be replaced by the contents
Expand Down Expand Up @@ -382,22 +399,30 @@ func (p *Path) CopyDirTo(dst *Path) error {
}

for _, srcPath := range srcFiles {
srcPathInfo, err := srcPath.Stat()
srcPathInfo, err := srcPath.Lstat()
if err != nil {
return fmt.Errorf("getting stat info for %s: %s", srcPath, err)
return fmt.Errorf("getting lstat info for %s: %s", srcPath, err)
}
dstPath := dst.Join(srcPath.Base())

if srcPathInfo.IsDir() {
if err := srcPath.CopyDirTo(dstPath); err != nil {
return fmt.Errorf("copying %s to %s: %s", srcPath, dstPath, err)
// In case is a symlink, copy the symlink
if srcPathInfo.Mode()&os.ModeSymlink != 0 {
namedLink, err := os.Readlink(srcPath.path)
if err != nil {
return fmt.Errorf("could not read symlink: %s", err.Error())
}

if err := os.Symlink(namedLink, dstPath.path); err != nil {
return fmt.Errorf("creating symlink (%s) of %s to %s: %s", namedLink, srcPath, dstPath, err)
}

continue
}

// Skip symlinks.
if srcPathInfo.Mode()&os.ModeSymlink != 0 {
// TODO
if srcPathInfo.IsDir() {
if err := srcPath.CopyDirTo(dstPath); err != nil {
return fmt.Errorf("copying %s to %s: %s", srcPath, dstPath, err)
}
continue
}

Expand Down
61 changes: 51 additions & 10 deletions paths_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
package paths

import (
"os"
"path/filepath"
"runtime"
"strings"
Expand Down Expand Up @@ -243,6 +244,43 @@ func TestCopyDir(t *testing.T) {

err = src.Join("file").CopyDirTo(tmp.Join("dest2"))
require.Error(t, err, "copying file as dir")

// Check if the file is a symlink
isSymlink, err := tmp.Join("dest", "folder_containing_symlinks", "file").IsSymlink()
require.True(t, isSymlink)
require.NoError(t, err)

// Check if the folder is a symlink
isSymlink, err = tmp.Join("dest", "folder_containing_symlinks", "folder").IsSymlink()
require.True(t, isSymlink)
require.NoError(t, err)

// Broken symlink is copied
{
// create broken folder containing the broken symlink file
tmpDestFolder := New(tmp.Join("broken").String())
require.NoError(t, tmpDestFolder.Mkdir())

// Create a symlink that will raise a too many levels of symblic links error
err = os.Symlink("broken_symlink", tmpDestFolder.Join("broken_symlink").String())
require.NoError(t, err)
// Create a symlinking pointing to a not existing file
err = os.Symlink("symlink", tmpDestFolder.Join("broken").String())
require.NoError(t, err)

src := tmpDestFolder
err = src.CopyDirTo(tmp.Join("broken_dest"))
require.NoError(t, err)

exist, err = tmp.Join("broken_dest", "broken_symlink").ExistCheck()
require.False(t, exist)
require.Error(t, err)
require.Contains(t, err.Error(), "too many levels of symbolic links")
Copy link
Contributor Author

@alessio-perugini alessio-perugini Sep 7, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have to figure out how to trigger this error on Windows.
EDIT: I'm not able to find a way to reproduce the same message on windows. Skipping this strict check for now.


exist, err = tmp.Join("broken_dest", "symlink").ExistCheck()
require.False(t, exist)
require.NoError(t, err)
}
}

func TestParents(t *testing.T) {
Expand All @@ -268,34 +306,37 @@ func TestFilterDirs(t *testing.T) {

list, err := testPath.ReadDir()
require.NoError(t, err)
require.Len(t, list, 6)
require.Len(t, list, 7)

pathEqualsTo(t, "_testdata/anotherFile", list[0])
pathEqualsTo(t, "_testdata/file", list[1])
pathEqualsTo(t, "_testdata/folder", list[2])
pathEqualsTo(t, "_testdata/symlinktofolder", list[3])
pathEqualsTo(t, "_testdata/test.txt", list[4])
pathEqualsTo(t, "_testdata/test.txt.gz", list[5])
pathEqualsTo(t, "_testdata/folder_containing_symlinks", list[3])
pathEqualsTo(t, "_testdata/symlinktofolder", list[4])
pathEqualsTo(t, "_testdata/test.txt", list[5])
pathEqualsTo(t, "_testdata/test.txt.gz", list[6])

list.FilterDirs()
require.Len(t, list, 2)
require.Len(t, list, 3)
pathEqualsTo(t, "_testdata/folder", list[0])
pathEqualsTo(t, "_testdata/symlinktofolder", list[1])
pathEqualsTo(t, "_testdata/folder_containing_symlinks", list[1])
pathEqualsTo(t, "_testdata/symlinktofolder", list[2])
}

func TestFilterOutDirs(t *testing.T) {
testPath := New("_testdata")

list, err := testPath.ReadDir()
require.NoError(t, err)
require.Len(t, list, 6)
require.Len(t, list, 7)

pathEqualsTo(t, "_testdata/anotherFile", list[0])
pathEqualsTo(t, "_testdata/file", list[1])
pathEqualsTo(t, "_testdata/folder", list[2])
pathEqualsTo(t, "_testdata/symlinktofolder", list[3])
pathEqualsTo(t, "_testdata/test.txt", list[4])
pathEqualsTo(t, "_testdata/test.txt.gz", list[5])
pathEqualsTo(t, "_testdata/folder_containing_symlinks", list[3])
pathEqualsTo(t, "_testdata/symlinktofolder", list[4])
pathEqualsTo(t, "_testdata/test.txt", list[5])
pathEqualsTo(t, "_testdata/test.txt.gz", list[6])

list.FilterOutDirs()
require.Len(t, list, 4)
Expand Down
Loading