From d1bb8efb88ee1871a82732e9edc95e3363c550ba Mon Sep 17 00:00:00 2001 From: Nick Craig-Wood Date: Fri, 16 Mar 2018 15:36:47 +0000 Subject: [PATCH] sftp: follow symlinks correctly - fixes #2145 The sftp library delivers the attributes of the symlink rather than the object pointed to in directory listings, however when we use Stat from the library it points to the objects. Previous to this fix this caused items pointed to by symlinks to be unusable. After the fix both symlinked files and directories work as expected. --- backend/sftp/sftp.go | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/backend/sftp/sftp.go b/backend/sftp/sftp.go index 6453a9783..4b481a206 100644 --- a/backend/sftp/sftp.go +++ b/backend/sftp/sftp.go @@ -481,6 +481,14 @@ func (f *Fs) List(dir string) (entries fs.DirEntries, err error) { } for _, info := range infos { remote := path.Join(dir, info.Name()) + // If file is a symlink (not a regular file is the best cross platform test we can do), do a stat to + // pick up the size and type of the destination, instead of the size and type of the symlink. + if !info.Mode().IsRegular() { + info, err = f.stat(remote) + if err != nil { + return nil, errors.Wrap(err, "stat of non-regular file/dir failed") + } + } if info.IsDir() { d := fs.NewDir(remote, info.ModTime()) entries = append(entries, d) @@ -805,14 +813,21 @@ func (o *Object) setMetadata(info os.FileInfo) { o.mode = info.Mode() } +// statRemote stats the file or directory at the remote given +func (f *Fs) stat(remote string) (info os.FileInfo, err error) { + c, err := f.getSftpConnection() + if err != nil { + return nil, errors.Wrap(err, "stat") + } + absPath := path.Join(f.root, remote) + info, err = c.sftpClient.Stat(absPath) + f.putSftpConnection(&c, err) + return info, err +} + // stat updates the info in the Object func (o *Object) stat() error { - c, err := o.fs.getSftpConnection() - if err != nil { - return errors.Wrap(err, "stat") - } - info, err := c.sftpClient.Stat(o.path()) - o.fs.putSftpConnection(&c, err) + info, err := o.fs.stat(o.remote) if err != nil { if os.IsNotExist(err) { return fs.ErrorObjectNotFound