mirror of https://github.com/pkg/sftp.git
Merge pull request #379 from drakkan/lstat
request-server: add lstat support
This commit is contained in:
commit
ec5b80d98e
|
@ -213,6 +213,12 @@ func (fs *root) Filelist(r *Request) (ListerAt, error) {
|
|||
}
|
||||
return listerat(list), nil
|
||||
case "Stat":
|
||||
if file.symlink != "" {
|
||||
file, err = fs.fetch(file.symlink)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return listerat([]os.FileInfo{file}), nil
|
||||
case "Readlink":
|
||||
if file.symlink != "" {
|
||||
|
@ -226,6 +232,22 @@ func (fs *root) Filelist(r *Request) (ListerAt, error) {
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
// implements LstatFileLister interface
|
||||
func (fs *root) Lstat(r *Request) (ListerAt, error) {
|
||||
if fs.mockErr != nil {
|
||||
return nil, fs.mockErr
|
||||
}
|
||||
_ = r.WithContext(r.Context()) // initialize context for deadlock testing
|
||||
fs.filesLock.Lock()
|
||||
defer fs.filesLock.Unlock()
|
||||
|
||||
file, err := fs.fetch(r.Filepath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return listerat([]os.FileInfo{file}), nil
|
||||
}
|
||||
|
||||
// In memory file-system-y thing that the Hanlders live on
|
||||
type root struct {
|
||||
*memFile
|
||||
|
|
|
@ -63,6 +63,13 @@ type FileLister interface {
|
|||
Filelist(*Request) (ListerAt, error)
|
||||
}
|
||||
|
||||
// LstatFileLister is a FileLister that implements the Lstat method.
|
||||
// If this interface is implemented Lstat requests will call it
|
||||
// otherwise they will be handled in the same way as Stat
|
||||
type LstatFileLister interface {
|
||||
Lstat(*Request) (ListerAt, error)
|
||||
}
|
||||
|
||||
// ListerAt does for file lists what io.ReaderAt does for files.
|
||||
// ListAt should return the number of entries copied and an io.EOF
|
||||
// error if at end of list. This is testable by comparing how many you
|
||||
|
|
|
@ -405,6 +405,19 @@ func TestRequestStatFail(t *testing.T) {
|
|||
checkRequestServerAllocator(t, p)
|
||||
}
|
||||
|
||||
func TestRequestLstat(t *testing.T) {
|
||||
p := clientRequestServerPair(t)
|
||||
defer p.Close()
|
||||
_, err := putTestFile(p.cli, "/foo", "hello")
|
||||
require.NoError(t, err)
|
||||
err = p.cli.Symlink("/foo", "/bar")
|
||||
require.NoError(t, err)
|
||||
fi, err := p.cli.Lstat("/bar")
|
||||
require.NoError(t, err)
|
||||
assert.True(t, fi.Mode()&os.ModeSymlink == os.ModeSymlink)
|
||||
checkRequestServerAllocator(t, p)
|
||||
}
|
||||
|
||||
func TestRequestLink(t *testing.T) {
|
||||
p := clientRequestServerPair(t)
|
||||
defer p.Close()
|
||||
|
|
26
request.go
26
request.go
|
@ -6,6 +6,7 @@ import (
|
|||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
|
||||
|
@ -213,7 +214,7 @@ func (r *Request) call(handlers Handlers, pkt requestPacket, alloc *allocator, o
|
|||
return filecmd(handlers.FileCmd, r, pkt)
|
||||
case "List":
|
||||
return filelist(handlers.FileList, r, pkt)
|
||||
case "Stat", "Readlink":
|
||||
case "Stat", "Lstat", "Readlink":
|
||||
return filestat(handlers.FileList, r, pkt)
|
||||
default:
|
||||
return statusFromError(pkt,
|
||||
|
@ -405,7 +406,20 @@ func filelist(h FileLister, r *Request, pkt requestPacket) responsePacket {
|
|||
}
|
||||
|
||||
func filestat(h FileLister, r *Request, pkt requestPacket) responsePacket {
|
||||
lister, err := h.Filelist(r)
|
||||
var lister ListerAt
|
||||
var err error
|
||||
|
||||
if r.Method == "Lstat" {
|
||||
if lstatFileLister, ok := h.(LstatFileLister); ok {
|
||||
lister, err = lstatFileLister.Lstat(r)
|
||||
} else {
|
||||
// LstatFileLister not implemented handle this request as a Stat
|
||||
r.Method = "Stat"
|
||||
lister, err = h.Filelist(r)
|
||||
}
|
||||
} else {
|
||||
lister, err = h.Filelist(r)
|
||||
}
|
||||
if err != nil {
|
||||
return statusFromError(pkt, err)
|
||||
}
|
||||
|
@ -414,12 +428,12 @@ func filestat(h FileLister, r *Request, pkt requestPacket) responsePacket {
|
|||
finfo = finfo[:n] // avoid need for nil tests below
|
||||
|
||||
switch r.Method {
|
||||
case "Stat":
|
||||
case "Stat", "Lstat":
|
||||
if err != nil && err != io.EOF {
|
||||
return statusFromError(pkt, err)
|
||||
}
|
||||
if n == 0 {
|
||||
err = &os.PathError{Op: "stat", Path: r.Filepath,
|
||||
err = &os.PathError{Op: strings.ToLower(r.Method), Path: r.Filepath,
|
||||
Err: syscall.ENOENT}
|
||||
return statusFromError(pkt, err)
|
||||
}
|
||||
|
@ -466,8 +480,10 @@ func requestMethod(p requestPacket) (method string) {
|
|||
method = "Symlink"
|
||||
case *sshFxpRemovePacket:
|
||||
method = "Remove"
|
||||
case *sshFxpStatPacket, *sshFxpLstatPacket, *sshFxpFstatPacket:
|
||||
case *sshFxpStatPacket, *sshFxpFstatPacket:
|
||||
method = "Stat"
|
||||
case *sshFxpLstatPacket:
|
||||
method = "Lstat"
|
||||
case *sshFxpRmdirPacket:
|
||||
method = "Rmdir"
|
||||
case *sshFxpReadlinkPacket:
|
||||
|
|
Loading…
Reference in New Issue