From 1a40bceb1d8e50067ddb3687e06e4ff13ed76613 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fabian=20M=C3=B6ller?= Date: Tue, 9 Oct 2018 13:35:27 +0200 Subject: [PATCH] backend: unify NewFs path handling for wrapping remotes Use the same function to join the root paths for the wrapping remotes alias, cache and crypt. The new function fspath.JoinRootPath is equivalent to path.Join, but if the first non empty element starts with "//", this is preserved to allow Windows network path to be used in these remotes. --- backend/alias/alias.go | 12 +++--------- backend/cache/cache.go | 3 ++- backend/crypt/crypt.go | 6 +++--- fs/fspath/path.go | 17 +++++++++++++++++ fs/fspath/path_test.go | 29 +++++++++++++++++++++++++++++ 5 files changed, 54 insertions(+), 13 deletions(-) diff --git a/backend/alias/alias.go b/backend/alias/alias.go index df41019dd..b90037fb6 100644 --- a/backend/alias/alias.go +++ b/backend/alias/alias.go @@ -2,13 +2,12 @@ package alias import ( "errors" - "path" - "path/filepath" "strings" "github.com/ncw/rclone/fs" "github.com/ncw/rclone/fs/config/configmap" "github.com/ncw/rclone/fs/config/configstruct" + "github.com/ncw/rclone/fs/fspath" ) // Register with Fs @@ -47,14 +46,9 @@ func NewFs(name, root string, m configmap.Mapper) (fs.Fs, error) { if strings.HasPrefix(opt.Remote, name+":") { return nil, errors.New("can't point alias remote at itself - check the value of the remote setting") } - _, configName, fsPath, err := fs.ParseRemote(opt.Remote) + fsInfo, configName, fsPath, config, err := fs.ConfigFs(opt.Remote) if err != nil { return nil, err } - if configName == "local" { - root = filepath.Join(fsPath, root) - return fs.NewFs(root) - } - root = path.Join(fsPath, filepath.ToSlash(root)) - return fs.NewFs(configName + ":" + root) + return fsInfo.NewFs(configName, fspath.JoinRootPath(fsPath, root), config) } diff --git a/backend/cache/cache.go b/backend/cache/cache.go index 7ce38d7ea..b613df377 100644 --- a/backend/cache/cache.go +++ b/backend/cache/cache.go @@ -24,6 +24,7 @@ import ( "github.com/ncw/rclone/fs/config/configmap" "github.com/ncw/rclone/fs/config/configstruct" "github.com/ncw/rclone/fs/config/obscure" + "github.com/ncw/rclone/fs/fspath" "github.com/ncw/rclone/fs/hash" "github.com/ncw/rclone/fs/rc" "github.com/ncw/rclone/fs/walk" @@ -363,7 +364,7 @@ func NewFs(name, rootPath string, m configmap.Mapper) (fs.Fs, error) { return nil, errors.Wrapf(err, "failed to parse remote %q to wrap", opt.Remote) } - remotePath := path.Join(wPath, rootPath) + remotePath := fspath.JoinRootPath(wPath, rootPath) wrappedFs, wrapErr := wInfo.NewFs(wName, remotePath, wConfig) if wrapErr != nil && wrapErr != fs.ErrorIsFile { return nil, errors.Wrapf(wrapErr, "failed to make remote %s:%s to wrap", wName, remotePath) diff --git a/backend/crypt/crypt.go b/backend/crypt/crypt.go index 363d153f4..23fe1c9c0 100644 --- a/backend/crypt/crypt.go +++ b/backend/crypt/crypt.go @@ -4,7 +4,6 @@ package crypt import ( "fmt" "io" - "path" "strings" "time" @@ -13,6 +12,7 @@ import ( "github.com/ncw/rclone/fs/config/configmap" "github.com/ncw/rclone/fs/config/configstruct" "github.com/ncw/rclone/fs/config/obscure" + "github.com/ncw/rclone/fs/fspath" "github.com/ncw/rclone/fs/hash" "github.com/pkg/errors" ) @@ -143,11 +143,11 @@ func NewFs(name, rpath string, m configmap.Mapper) (fs.Fs, error) { return nil, errors.Wrapf(err, "failed to parse remote %q to wrap", remote) } // Look for a file first - remotePath := path.Join(wPath, cipher.EncryptFileName(rpath)) + remotePath := fspath.JoinRootPath(wPath, cipher.EncryptFileName(rpath)) wrappedFs, err := wInfo.NewFs(wName, remotePath, wConfig) // if that didn't produce a file, look for a directory if err != fs.ErrorIsFile { - remotePath = path.Join(wPath, cipher.EncryptDirName(rpath)) + remotePath = fspath.JoinRootPath(wPath, cipher.EncryptDirName(rpath)) wrappedFs, err = wInfo.NewFs(wName, remotePath, wConfig) } if err != fs.ErrorIsFile && err != nil { diff --git a/fs/fspath/path.go b/fs/fspath/path.go index baa89bbd2..42f3bcd44 100644 --- a/fs/fspath/path.go +++ b/fs/fspath/path.go @@ -5,6 +5,7 @@ import ( "path" "path/filepath" "regexp" + "strings" "github.com/ncw/rclone/fs/driveletter" ) @@ -48,3 +49,19 @@ func Split(remote string) (parent string, leaf string) { parent, leaf = path.Split(remotePath) return remoteName + parent, leaf } + +// JoinRootPath joins any number of path elements into a single path, adding a +// separating slash if necessary. The result is Cleaned; in particular, +// all empty strings are ignored. +// If the first non empty element has a leading "//" this is preserved. +func JoinRootPath(elem ...string) string { + for i, e := range elem { + if e != "" { + if strings.HasPrefix(e, "//") { + return "/" + path.Clean(strings.Join(elem[i:], "/")) + } + return path.Clean(strings.Join(elem[i:], "/")) + } + } + return "" +} diff --git a/fs/fspath/path_test.go b/fs/fspath/path_test.go index d5b41a473..f900d62be 100644 --- a/fs/fspath/path_test.go +++ b/fs/fspath/path_test.go @@ -58,3 +58,32 @@ func TestSplit(t *testing.T) { assert.Equal(t, test.remote, gotParent+gotLeaf, fmt.Sprintf("%s: %q + %q != %q", test.remote, gotParent, gotLeaf, test.remote)) } } +func TestJoinRootPath(t *testing.T) { + for _, test := range []struct { + elements []string + want string + }{ + {nil, ""}, + {[]string{""}, ""}, + {[]string{"/"}, "/"}, + {[]string{"/", "/"}, "/"}, + {[]string{"/", "//"}, "/"}, + {[]string{"/root", ""}, "/root"}, + {[]string{"/root", "/"}, "/root"}, + {[]string{"/root", "//"}, "/root"}, + {[]string{"/a/b"}, "/a/b"}, + {[]string{"//", "/"}, "//"}, + {[]string{"//server", "path"}, "//server/path"}, + {[]string{"//server/sub", "path"}, "//server/sub/path"}, + {[]string{"//server", "//path"}, "//server/path"}, + {[]string{"//server/sub", "//path"}, "//server/sub/path"}, + {[]string{"", "//", "/"}, "//"}, + {[]string{"", "//server", "path"}, "//server/path"}, + {[]string{"", "//server/sub", "path"}, "//server/sub/path"}, + {[]string{"", "//server", "//path"}, "//server/path"}, + {[]string{"", "//server/sub", "//path"}, "//server/sub/path"}, + } { + got := JoinRootPath(test.elements...) + assert.Equal(t, test.want, got) + } +}