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
This commit is contained in:
Nick Craig-Wood 2020-06-29 15:49:19 +01:00
parent 96c2fdb445
commit fefcbf60fa
1 changed files with 21 additions and 9 deletions

View File

@ -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