Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e426492549 |
No files matched your search
+14
-2
@@ -887,13 +887,25 @@ func (f *Fs) nativePath(name string) string {
|
||||
// happen depending on the encoding.
|
||||
func (f *Fs) localPath(name string) (string, error) {
|
||||
localPath := filepath.Join(f.root, f.nativePath(name))
|
||||
rel, err := filepath.Rel(f.root, localPath)
|
||||
rel, err := f.relToRoot(localPath)
|
||||
if err != nil || rel == ".." || strings.HasPrefix(rel, ".."+string(filepath.Separator)) {
|
||||
return "", fserrors.NoRetryError(fmt.Errorf("%q: %w", name, errPathEscapes))
|
||||
}
|
||||
return localPath, nil
|
||||
}
|
||||
|
||||
// relToRoot returns localPath relative to f.root.
|
||||
func (f *Fs) relToRoot(localPath string) (string, error) {
|
||||
// On Windows filepath.Rel loops forever if the base is a bare UNC
|
||||
// share and the target is the same share with a trailing
|
||||
// separator, eg `\\server\share` and `\\server\share\`, which is
|
||||
// what filepath.Join makes of f.root and "".
|
||||
if localPath == f.root || localPath == f.root+string(filepath.Separator) {
|
||||
return ".", nil
|
||||
}
|
||||
return filepath.Rel(f.root, localPath)
|
||||
}
|
||||
|
||||
// Put the Object to the local filesystem
|
||||
func (f *Fs) Put(ctx context.Context, in io.Reader, src fs.ObjectInfo, options ...fs.OpenOption) (fs.Object, error) {
|
||||
// Temporary Object under construction - info filled in by Update()
|
||||
@@ -1594,7 +1606,7 @@ func (o *Object) Open(ctx context.Context, options ...fs.OpenOption) (in io.Read
|
||||
// f.root is the trusted destination the user chose, so it is created (and
|
||||
// followed if it is itself a symlink) before being opened.
|
||||
func (f *Fs) osRoot(localPath string) (root *os.Root, rel string, err error) {
|
||||
rel, err = filepath.Rel(f.root, localPath)
|
||||
rel, err = f.relToRoot(localPath)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"runtime"
|
||||
"syscall"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/rclone/rclone/fs/operations"
|
||||
"github.com/rclone/rclone/fstest"
|
||||
@@ -38,3 +39,36 @@ func TestRmdirWindows(t *testing.T) {
|
||||
err = operations.Rmdir(context.Background(), r.Flocal, "testdir")
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
// TestLocalPathUNCShareRoot checks localPath works when the root is a
|
||||
// bare UNC share, where filepath.Rel would never return.
|
||||
func TestLocalPathUNCShareRoot(t *testing.T) {
|
||||
for _, root := range []string{`\\server\share`, `\\?\UNC\server\share`} {
|
||||
f := &Fs{root: root}
|
||||
for _, test := range []struct {
|
||||
name string
|
||||
want string
|
||||
}{
|
||||
{name: "", want: root + `\`},
|
||||
{name: "file.txt", want: root + `\file.txt`},
|
||||
{name: "sub/file.txt", want: root + `\sub\file.txt`},
|
||||
} {
|
||||
type result struct {
|
||||
path string
|
||||
err error
|
||||
}
|
||||
done := make(chan result, 1)
|
||||
go func() {
|
||||
path, err := f.localPath(test.name)
|
||||
done <- result{path, err}
|
||||
}()
|
||||
select {
|
||||
case res := <-done:
|
||||
require.NoError(t, res.err, root, test.name)
|
||||
assert.Equal(t, test.want, res.path, root, test.name)
|
||||
case <-time.After(10 * time.Second):
|
||||
t.Fatalf("localPath(%q) with root %q did not return", test.name, root)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user