From fefcbf60faf907eb1eed65fcd1e15a2bfd368283 Mon Sep 17 00:00:00 2001 From: Nick Craig-Wood Date: Mon, 29 Jun 2020 15:49:19 +0100 Subject: [PATCH] sftp: use the absolute path instead of the relative path Before this change rclone used the relative path from the current working directory. It appears that WS FTP doesn't like this and the openssh sftp tool also uses absolute paths which is a good reason for switching to absolute paths. This change reads the current working directory at startup and bases all file requests from there. See: https://forum.rclone.org/t/sftp-ssh-fx-failure-directory-not-found/17436 --- backend/sftp/sftp.go | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/backend/sftp/sftp.go b/backend/sftp/sftp.go index a3d2618e6..fceea7a3d 100644 --- a/backend/sftp/sftp.go +++ b/backend/sftp/sftp.go @@ -193,6 +193,7 @@ type Options struct { type Fs struct { name string root string + absRoot string opt Options // parsed options m configmap.Mapper // config features *fs.Features // optional features @@ -491,6 +492,7 @@ func NewFsWithConnection(ctx context.Context, name string, root string, m config f := &Fs{ name: name, root: root, + absRoot: root, opt: *opt, m: m, config: sshConfig, @@ -507,11 +509,20 @@ func NewFsWithConnection(ctx context.Context, name string, root string, m config if err != nil { return nil, errors.Wrap(err, "NewFs") } + cwd, err := c.sftpClient.Getwd() f.putSftpConnection(&c, nil) + if err != nil { + fs.Debugf(f, "Failed to read current directory - using relative paths: %v", err) + } else if !path.IsAbs(f.root) { + f.absRoot = path.Join(cwd, f.root) + fs.Debugf(f, "Using absolute root directory %q", f.absRoot) + } if root != "" { // Check to see if the root actually an existing file + oldAbsRoot := f.absRoot remote := path.Base(root) f.root = path.Dir(root) + f.absRoot = path.Dir(f.absRoot) if f.root == "." { f.root = "" } @@ -520,6 +531,7 @@ func NewFsWithConnection(ctx context.Context, name string, root string, m config if err == fs.ErrorObjectNotFound || errors.Cause(err) == fs.ErrorNotAFile { // File doesn't exist so return old f f.root = root + f.absRoot = oldAbsRoot return f, nil } return nil, err @@ -602,7 +614,7 @@ func (f *Fs) dirExists(dir string) (bool, error) { // This should return ErrDirNotFound if the directory isn't // found. func (f *Fs) List(ctx context.Context, dir string) (entries fs.DirEntries, err error) { - root := path.Join(f.root, dir) + root := path.Join(f.absRoot, dir) ok, err := f.dirExists(root) if err != nil { return nil, errors.Wrap(err, "List failed") @@ -683,7 +695,7 @@ func (f *Fs) PutStream(ctx context.Context, in io.Reader, src fs.ObjectInfo, opt // directories above that func (f *Fs) mkParentDir(remote string) error { parent := path.Dir(remote) - return f.mkdir(path.Join(f.root, parent)) + return f.mkdir(path.Join(f.absRoot, parent)) } // mkdir makes the directory and parents using native paths @@ -719,7 +731,7 @@ func (f *Fs) mkdir(dirPath string) error { // Mkdir makes the root directory of the Fs object func (f *Fs) Mkdir(ctx context.Context, dir string) error { - root := path.Join(f.root, dir) + root := path.Join(f.absRoot, dir) return f.mkdir(root) } @@ -735,7 +747,7 @@ func (f *Fs) Rmdir(ctx context.Context, dir string) error { return fs.ErrorDirectoryNotEmpty } // Remove the directory - root := path.Join(f.root, dir) + root := path.Join(f.absRoot, dir) c, err := f.getSftpConnection() if err != nil { return errors.Wrap(err, "Rmdir") @@ -762,7 +774,7 @@ func (f *Fs) Move(ctx context.Context, src fs.Object, remote string) (fs.Object, } err = c.sftpClient.Rename( srcObj.path(), - path.Join(f.root, remote), + path.Join(f.absRoot, remote), ) f.putSftpConnection(&c, err) if err != nil { @@ -789,8 +801,8 @@ func (f *Fs) DirMove(ctx context.Context, src fs.Fs, srcRemote, dstRemote string fs.Debugf(srcFs, "Can't move directory - not same remote type") return fs.ErrorCantDirMove } - srcPath := path.Join(srcFs.root, srcRemote) - dstPath := path.Join(f.root, dstRemote) + srcPath := path.Join(srcFs.absRoot, srcRemote) + dstPath := path.Join(f.absRoot, dstRemote) // Check if destination exists ok, err := f.dirExists(dstPath) @@ -1076,7 +1088,7 @@ func (o *Object) ModTime(ctx context.Context) time.Time { // path returns the native path of the object func (o *Object) path() string { - return path.Join(o.fs.root, o.remote) + return path.Join(o.fs.absRoot, o.remote) } // setMetadata updates the info in the object from the stat result passed in @@ -1092,7 +1104,7 @@ func (f *Fs) stat(remote string) (info os.FileInfo, err error) { if err != nil { return nil, errors.Wrap(err, "stat") } - absPath := path.Join(f.root, remote) + absPath := path.Join(f.absRoot, remote) info, err = c.sftpClient.Stat(absPath) f.putSftpConnection(&c, err) return info, err