Merge pull request #379 from drakkan/lstat

request-server: add lstat support
This commit is contained in:
Nicola Murino 2020-09-11 09:11:46 +02:00 committed by GitHub
commit ec5b80d98e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 63 additions and 5 deletions

View File

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

View File

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

View File

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

View File

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