Skip to content

Commit

Permalink
RealPathFileLister: allow to return an error
Browse files Browse the repository at this point in the history
added legacyRealPathFileLister for backward compatibility

Fixes #512
  • Loading branch information
drakkan committed Jul 15, 2022
1 parent cf523d9 commit 4dfbef1
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 18 deletions.
37 changes: 29 additions & 8 deletions request-example.go
Original file line number Diff line number Diff line change
Expand Up @@ -464,14 +464,6 @@ func (fs *root) Lstat(r *Request) (ListerAt, error) {
return listerat{file}, nil
}

// implements RealpathFileLister interface
func (fs *root) Realpath(p string) string {
if fs.startDirectory == "" || fs.startDirectory == "/" {
return cleanPath(p)
}
return cleanPathWithBase(fs.startDirectory, p)
}

// In memory file-system-y thing that the Hanlders live on
type root struct {
rootFile *memFile
Expand Down Expand Up @@ -555,6 +547,35 @@ func (fs *root) fetch(path string) (*memFile, error) {
return file, nil
}

// In memory file-system which implements the RealPathFileLister
type rootWithRealPather struct {
root
}

// implements RealpathFileLister interface
func (fs *rootWithRealPather) Realpath(p string) (string, error) {
if fs.mockErr != nil {
return "", fs.mockErr
}
if fs.startDirectory == "" || fs.startDirectory == "/" {
return cleanPath(p), nil
}
return cleanPathWithBase(fs.startDirectory, p), nil
}

// In memory file-system which implements legacyRealPathFileLister
type rootWithLegacyRealPather struct {
root
}

// implements RealpathFileLister interface
func (fs *rootWithLegacyRealPather) Realpath(p string) string {
if fs.startDirectory == "" || fs.startDirectory == "/" {
return cleanPath(p)
}
return cleanPathWithBase(fs.startDirectory, p)
}

// Implements os.FileInfo, io.ReaderAt and io.WriterAt interfaces.
// These are the 3 interfaces necessary for the Handlers.
// Implements the optional interface TransferError.
Expand Down
15 changes: 11 additions & 4 deletions request-interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,12 +87,19 @@ type LstatFileLister interface {
}

// RealPathFileLister is a FileLister that implements the Realpath method.
// We use "/" as start directory for relative paths, implementing this
// interface you can customize the start directory.
// The built-in RealPath implementation does not resolve symbolic links,
// by implementing this interface you can customize the returned path
// and, for example, resolve symbolinc links if needed for your use case.
// You have to return an absolute POSIX path.
//
// Deprecated: if you want to set a start directory use WithStartDirectory RequestServerOption instead.
type RealPathFileLister interface {
FileLister
RealPath(string) (string, error)
}

// This interface is here for backward compatibility only.
// Up to v1.13.5 we didn't allow to return an error from the RealPathFileLister
// interface, which is wrong
type legacyRealPathFileLister interface {
FileLister
RealPath(string) string
}
Expand Down
11 changes: 9 additions & 2 deletions request-server.go
Original file line number Diff line number Diff line change
Expand Up @@ -219,12 +219,19 @@ func (rs *RequestServer) packetWorker(ctx context.Context, pktChan chan orderedR
rpkt = statusFromError(pkt.ID, rs.closeRequest(handle))
case *sshFxpRealpathPacket:
var realPath string
var err error
if realPather, ok := rs.Handlers.FileList.(RealPathFileLister); ok {
realPath = realPather.RealPath(pkt.getPath())
realPath, err = realPather.RealPath(pkt.getPath())
} else if legacyRealPather, ok := rs.Handlers.FileList.(legacyRealPathFileLister); ok {
realPath = legacyRealPather.RealPath(pkt.getPath())
} else {
realPath = cleanPathWithBase(rs.startDirectory, pkt.getPath())
}
rpkt = cleanPacketPath(pkt, realPath)
if err != nil {
rpkt = statusFromError(pkt.ID, err)
} else {
rpkt = cleanPacketPath(pkt, realPath)
}
case *sshFxpOpendirPacket:
request := requestFromPacket(ctx, pkt, rs.startDirectory)
handle := rs.nextRequest(request)
Expand Down
41 changes: 37 additions & 4 deletions request-server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package sftp

import (
"context"
"errors"
"fmt"
"io"
"io/ioutil"
Expand Down Expand Up @@ -840,10 +841,42 @@ func TestUncleanDisconnect(t *testing.T) {
}

func TestRealPath(t *testing.T) {
root := &root{
rootFile: &memFile{name: "/", modtime: time.Now(), isdir: true},
files: make(map[string]*memFile),
startDirectory: "/apath",
root := &rootWithRealPather{
root: root{
rootFile: &memFile{name: "/", modtime: time.Now(), isdir: true},
files: make(map[string]*memFile),
startDirectory: "/apath",
},
}

p, err := root.Realpath(".")
require.NoError(t, err)
assert.Equal(t, root.startDirectory, p)
p, err = root.Realpath("/")
require.NoError(t, err)
assert.Equal(t, "/", p)
p, err = root.Realpath("..")
require.NoError(t, err)
assert.Equal(t, "/", p)
p, err = root.Realpath("../../..")
require.NoError(t, err)
assert.Equal(t, "/", p)
p, err = root.Realpath("relpath")
require.NoError(t, err)
assert.Equal(t, path.Join(root.startDirectory, "relpath"), p)
// test an error
root.returnErr(errors.New("test error"))
_, err = root.Realpath(".")
require.Error(t, err)
}

func TestLegacyRealPath(t *testing.T) {
root := &rootWithLegacyRealPather{
root: root{
rootFile: &memFile{name: "/", modtime: time.Now(), isdir: true},
files: make(map[string]*memFile),
startDirectory: "/apath",
},
}

p := root.Realpath(".")
Expand Down

0 comments on commit 4dfbef1

Please sign in to comment.