-
Notifications
You must be signed in to change notification settings - Fork 1k
internal/fs: handle symlinks in copyFile() #657
Conversation
Signed-off-by: Ibrahim AshShohail <[email protected]>
Signed-off-by: Ibrahim AshShohail <[email protected]>
Signed-off-by: Ibrahim AshShohail <[email protected]>
Signed-off-by: Ibrahim AshShohail <[email protected]>
I borrowed some functions from #641 . 😉 |
Signed-off-by: Ibrahim AshShohail <[email protected]>
489e562
to
4af7780
Compare
internal/fs/fs.go
Outdated
@@ -242,6 +242,13 @@ func CopyDir(src, dst string) error { | |||
// of the source file. The file mode will be copied from the source and | |||
// the copied data is synced/flushed to stable storage. | |||
func copyFile(src, dst string) (err error) { | |||
if isSymlink, err := IsSymlink(src); err != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For a short function like this, using a shorter variable name - e.g., is
or sym
instead of isSymlink
- is more idiomatic.
Signed-off-by: Ibrahim AshShohail <[email protected]>
internal/fs/fs_test.go
Outdated
} | ||
|
||
for path, want := range tests { | ||
if runtime.GOOS == "windows" { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do this at the top of the test before doing any setup work
internal/fs/fs_test.go
Outdated
got, err := IsSymlink(path) | ||
if err != nil { | ||
if !want.err { | ||
t.Fatalf("expected no error, got %v", err) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
use t.Errorf(); continue
so that you get all the test failures, not just the first
internal/fs/fs.go
Outdated
@@ -242,6 +242,13 @@ func CopyDir(src, dst string) error { | |||
// of the source file. The file mode will be copied from the source and | |||
// the copied data is synced/flushed to stable storage. | |||
func copyFile(src, dst string) (err error) { | |||
if sym, err := IsSymlink(src); err != nil { | |||
return errors.Wrapf(err, "could not lstat %s", src) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just return the error, it's already wrapped
} | ||
|
||
err = os.Symlink(resolved, dst) | ||
if err != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you can write this
return errors.Wrapf(err, "...")
if err == nil, errors.Wrapf returns nil.
This might be too magical.
@@ -337,3 +360,13 @@ func IsRegular(name string) (bool, error) { | |||
} | |||
return true, nil | |||
} | |||
|
|||
// IsSymlink determines if the given path is a symbolic link. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add a note to describe the behaviour of this function on Windows.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It should behave in the same way. os.Lstat()
call GetFileAttributesEx
on Windows and FileStat.Mode()
will convert the attribute syscall.FILE_ATTRIBUTE_REPARSE_POINT
to os.ModeSymlink
. (see os/stat_windows.go and os/types_windows.go).
Unless I'm missing something else. 😄
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The reason why they the tests for this function are skipped is that creating a symlink (using os.Symlink()
) is not supported on Windows. But if the symlink already exists, it will be detected by this function.
Signed-off-by: Ibrahim AshShohail <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
small style nits, then LGTM
internal/fs/fs.go
Outdated
|
||
err = os.Symlink(resolved, dst) | ||
|
||
return errors.Wrapf(err, "failed to create symlink %s to %s", src, resolved) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is awkwardly halfway between a terse form and the standard form. Either combine this and the preceding line into one (errors.Wrapf(os.Symlink(resolved, dst), ...)
), or do the more standard if err != nil
check.
internal/fs/fs_test.go
Outdated
} | ||
|
||
inaccessibleSymlink = filepath.Join(dir, "symlink") | ||
err = os.Symlink(inaccessibleFile, inaccessibleSymlink) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just return this final call directly, no need to assign into var
Signed-off-by: Ibrahim AshShohail <[email protected]>
Done. |
great, thanks! 🎉 |
fs.copyFile()
currently fails whensrc
is a symlink to a directory.This PR adds 2 functions:
fs.IsSymlink()
to check if a path is a symbolic link.fs.copySymlink()
to copy a symlink (preserves relative symlinks)And update
fs.copyFile()
to handle symlinks correctly.Should solve #651 .