forked from TrueCloudLab/rclone
vendor: update all dependencies to latest versions
This commit is contained in:
parent
8e83fb6fb9
commit
7d3a17725d
4878 changed files with 1974229 additions and 201215 deletions
7
vendor/github.com/pkg/sftp/.travis.yml
generated
vendored
7
vendor/github.com/pkg/sftp/.travis.yml
generated
vendored
|
@ -1,10 +1,11 @@
|
|||
language: go
|
||||
go_import_path: github.com/pkg/sftp
|
||||
|
||||
# current and previous stable releases, and tip
|
||||
# current and previous stable releases, plus tip
|
||||
# remember to exclude previous and tip for macs below
|
||||
go:
|
||||
- 1.7.x
|
||||
- 1.8.x
|
||||
- 1.9.x
|
||||
- tip
|
||||
|
||||
os:
|
||||
|
@ -14,7 +15,7 @@ os:
|
|||
matrix:
|
||||
exclude:
|
||||
- os: osx
|
||||
go: 1.7.x
|
||||
go: 1.8.x
|
||||
- os: osx
|
||||
go: tip
|
||||
|
||||
|
|
4
vendor/github.com/pkg/sftp/attrs.go
generated
vendored
4
vendor/github.com/pkg/sftp/attrs.go
generated
vendored
|
@ -94,6 +94,10 @@ func fileStatFromInfo(fi os.FileInfo) (uint32, FileStat) {
|
|||
|
||||
func unmarshalAttrs(b []byte) (*FileStat, []byte) {
|
||||
flags, b := unmarshalUint32(b)
|
||||
return getFileStat(flags, b)
|
||||
}
|
||||
|
||||
func getFileStat(flags uint32, b []byte) (*FileStat, []byte) {
|
||||
var fs FileStat
|
||||
if flags&ssh_FILEXFER_ATTR_SIZE == ssh_FILEXFER_ATTR_SIZE {
|
||||
fs.Size, b = unmarshalUint64(b)
|
||||
|
|
26
vendor/github.com/pkg/sftp/client.go
generated
vendored
26
vendor/github.com/pkg/sftp/client.go
generated
vendored
|
@ -15,8 +15,10 @@ import (
|
|||
)
|
||||
|
||||
// InternalInconsistency indicates the packets sent and the data queued to be
|
||||
// written to the file don't match up. It is an unusual error and if you get it
|
||||
// you should file a ticket.
|
||||
// written to the file don't match up. It is an unusual error and usually is
|
||||
// caused by bad behavior server side or connection issues. The error is
|
||||
// limited in scope to the call where it happened, the client object is still
|
||||
// OK to use as long as the connection is still open.
|
||||
var InternalInconsistency = errors.New("internal inconsistency")
|
||||
|
||||
// A ClientOption is a function which applies configuration to a Client.
|
||||
|
@ -575,6 +577,26 @@ func (c *Client) Rename(oldname, newname string) error {
|
|||
}
|
||||
}
|
||||
|
||||
// PosixRename renames a file using the posix-rename@openssh.com extension
|
||||
// which will replace newname if it already exists.
|
||||
func (c *Client) PosixRename(oldname, newname string) error {
|
||||
id := c.nextID()
|
||||
typ, data, err := c.sendPacket(sshFxpPosixRenamePacket{
|
||||
ID: id,
|
||||
Oldpath: oldname,
|
||||
Newpath: newname,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
switch typ {
|
||||
case ssh_FXP_STATUS:
|
||||
return normaliseError(unmarshalStatus(id, data))
|
||||
default:
|
||||
return unimplementedPacketErr(typ)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) realpath(path string) (string, error) {
|
||||
id := c.nextID()
|
||||
typ, data, err := c.sendPacket(sshFxpRealpathPacket{
|
||||
|
|
21
vendor/github.com/pkg/sftp/client_integration_test.go
generated
vendored
21
vendor/github.com/pkg/sftp/client_integration_test.go
generated
vendored
|
@ -627,6 +627,27 @@ func TestClientRename(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestClientPosixRename(t *testing.T) {
|
||||
sftp, cmd := testClient(t, READWRITE, NO_DELAY)
|
||||
defer cmd.Wait()
|
||||
defer sftp.Close()
|
||||
|
||||
f, err := ioutil.TempFile("", "sftptest")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
f2 := f.Name() + ".new"
|
||||
if err := sftp.PosixRename(f.Name(), f2); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := os.Lstat(f.Name()); !os.IsNotExist(err) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := os.Lstat(f2); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestClientGetwd(t *testing.T) {
|
||||
sftp, cmd := testClient(t, READONLY, NO_DELAY)
|
||||
defer cmd.Wait()
|
||||
|
|
21
vendor/github.com/pkg/sftp/example_test.go
generated
vendored
21
vendor/github.com/pkg/sftp/example_test.go
generated
vendored
|
@ -1,7 +1,9 @@
|
|||
package sftp_test
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
|
@ -107,13 +109,13 @@ func ExampleClient_Mkdir_parents() {
|
|||
sshFxFailure := uint32(4)
|
||||
mkdirParents := func(client *sftp.Client, dir string) (err error) {
|
||||
var parents string
|
||||
|
||||
|
||||
if path.IsAbs(dir) {
|
||||
// Otherwise, an absolute path given below would be turned in to a relative one
|
||||
// by splitting on "/"
|
||||
parents = "/"
|
||||
}
|
||||
|
||||
|
||||
for _, name := range strings.Split(dir, "/") {
|
||||
if name == "" {
|
||||
// Paths with double-/ in them should just move along
|
||||
|
@ -145,3 +147,18 @@ func ExampleClient_Mkdir_parents() {
|
|||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func ExampleFile_ReadFrom_bufio() {
|
||||
// Using Bufio to buffer writes going to an sftp.File won't buffer as it
|
||||
// skips buffering if the underlying writer support ReadFrom. The
|
||||
// workaround is to wrap your writer in a struct that only implements
|
||||
// io.Writer.
|
||||
//
|
||||
// For background see github.com/pkg/sftp/issues/125
|
||||
|
||||
var data_source io.Reader
|
||||
var f *sftp.File
|
||||
type writerOnly struct{ io.Writer }
|
||||
bw := bufio.NewWriter(writerOnly{f}) // no ReadFrom()
|
||||
bw.ReadFrom(data_source)
|
||||
}
|
||||
|
|
30
vendor/github.com/pkg/sftp/packet-typing.go
generated
vendored
30
vendor/github.com/pkg/sftp/packet-typing.go
generated
vendored
|
@ -30,11 +30,6 @@ type hasHandle interface {
|
|||
getHandle() string
|
||||
}
|
||||
|
||||
type isOpener interface {
|
||||
hasPath
|
||||
isOpener()
|
||||
}
|
||||
|
||||
type notReadOnly interface {
|
||||
notReadOnly()
|
||||
}
|
||||
|
@ -52,12 +47,10 @@ func (p sshFxpStatvfsPacket) getPath() string { return p.Path }
|
|||
func (p sshFxpRemovePacket) getPath() string { return p.Filename }
|
||||
func (p sshFxpRenamePacket) getPath() string { return p.Oldpath }
|
||||
func (p sshFxpSymlinkPacket) getPath() string { return p.Targetpath }
|
||||
func (p sshFxpOpendirPacket) getPath() string { return p.Path }
|
||||
func (p sshFxpOpenPacket) getPath() string { return p.Path }
|
||||
|
||||
// Openers implement hasPath and isOpener
|
||||
func (p sshFxpOpendirPacket) getPath() string { return p.Path }
|
||||
func (p sshFxpOpendirPacket) isOpener() {}
|
||||
func (p sshFxpOpenPacket) getPath() string { return p.Path }
|
||||
func (p sshFxpOpenPacket) isOpener() {}
|
||||
func (p sshFxpExtendedPacketPosixRename) getPath() string { return p.Oldpath }
|
||||
|
||||
// hasHandle
|
||||
func (p sshFxpFstatPacket) getHandle() string { return p.Handle }
|
||||
|
@ -67,14 +60,15 @@ func (p sshFxpWritePacket) getHandle() string { return p.Handle }
|
|||
func (p sshFxpReaddirPacket) getHandle() string { return p.Handle }
|
||||
|
||||
// notReadOnly
|
||||
func (p sshFxpWritePacket) notReadOnly() {}
|
||||
func (p sshFxpSetstatPacket) notReadOnly() {}
|
||||
func (p sshFxpFsetstatPacket) notReadOnly() {}
|
||||
func (p sshFxpRemovePacket) notReadOnly() {}
|
||||
func (p sshFxpMkdirPacket) notReadOnly() {}
|
||||
func (p sshFxpRmdirPacket) notReadOnly() {}
|
||||
func (p sshFxpRenamePacket) notReadOnly() {}
|
||||
func (p sshFxpSymlinkPacket) notReadOnly() {}
|
||||
func (p sshFxpWritePacket) notReadOnly() {}
|
||||
func (p sshFxpSetstatPacket) notReadOnly() {}
|
||||
func (p sshFxpFsetstatPacket) notReadOnly() {}
|
||||
func (p sshFxpRemovePacket) notReadOnly() {}
|
||||
func (p sshFxpMkdirPacket) notReadOnly() {}
|
||||
func (p sshFxpRmdirPacket) notReadOnly() {}
|
||||
func (p sshFxpRenamePacket) notReadOnly() {}
|
||||
func (p sshFxpSymlinkPacket) notReadOnly() {}
|
||||
func (p sshFxpExtendedPacketPosixRename) notReadOnly() {}
|
||||
|
||||
// this has a handle, but is only used for close
|
||||
func (p sshFxpClosePacket) getHandle() string { return p.Handle }
|
||||
|
|
54
vendor/github.com/pkg/sftp/packet.go
generated
vendored
54
vendor/github.com/pkg/sftp/packet.go
generated
vendored
|
@ -586,6 +586,30 @@ func (p *sshFxpRenamePacket) UnmarshalBinary(b []byte) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
type sshFxpPosixRenamePacket struct {
|
||||
ID uint32
|
||||
Oldpath string
|
||||
Newpath string
|
||||
}
|
||||
|
||||
func (p sshFxpPosixRenamePacket) id() uint32 { return p.ID }
|
||||
|
||||
func (p sshFxpPosixRenamePacket) MarshalBinary() ([]byte, error) {
|
||||
const ext = "posix-rename@openssh.com"
|
||||
l := 1 + 4 + // type(byte) + uint32
|
||||
4 + len(ext) +
|
||||
4 + len(p.Oldpath) +
|
||||
4 + len(p.Newpath)
|
||||
|
||||
b := make([]byte, 0, l)
|
||||
b = append(b, ssh_FXP_EXTENDED)
|
||||
b = marshalUint32(b, p.ID)
|
||||
b = marshalString(b, ext)
|
||||
b = marshalString(b, p.Oldpath)
|
||||
b = marshalString(b, p.Newpath)
|
||||
return b, nil
|
||||
}
|
||||
|
||||
type sshFxpWritePacket struct {
|
||||
ID uint32
|
||||
Handle string
|
||||
|
@ -870,6 +894,8 @@ func (p *sshFxpExtendedPacket) UnmarshalBinary(b []byte) error {
|
|||
switch p.ExtendedRequest {
|
||||
case "statvfs@openssh.com":
|
||||
p.SpecificPacket = &sshFxpExtendedPacketStatVFS{}
|
||||
case "posix-rename@openssh.com":
|
||||
p.SpecificPacket = &sshFxpExtendedPacketPosixRename{}
|
||||
default:
|
||||
return errUnknownExtendedPacket
|
||||
}
|
||||
|
@ -896,3 +922,31 @@ func (p *sshFxpExtendedPacketStatVFS) UnmarshalBinary(b []byte) error {
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type sshFxpExtendedPacketPosixRename struct {
|
||||
ID uint32
|
||||
ExtendedRequest string
|
||||
Oldpath string
|
||||
Newpath string
|
||||
}
|
||||
|
||||
func (p sshFxpExtendedPacketPosixRename) id() uint32 { return p.ID }
|
||||
func (p sshFxpExtendedPacketPosixRename) readonly() bool { return false }
|
||||
func (p *sshFxpExtendedPacketPosixRename) UnmarshalBinary(b []byte) error {
|
||||
var err error
|
||||
if p.ID, b, err = unmarshalUint32Safe(b); err != nil {
|
||||
return err
|
||||
} else if p.ExtendedRequest, b, err = unmarshalStringSafe(b); err != nil {
|
||||
return err
|
||||
} else if p.Oldpath, b, err = unmarshalStringSafe(b); err != nil {
|
||||
return err
|
||||
} else if p.Newpath, b, err = unmarshalStringSafe(b); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p sshFxpExtendedPacketPosixRename) respond(s *Server) error {
|
||||
err := os.Rename(p.Oldpath, p.Newpath)
|
||||
return s.sendError(p, err)
|
||||
}
|
||||
|
|
63
vendor/github.com/pkg/sftp/request-attrs.go
generated
vendored
Normal file
63
vendor/github.com/pkg/sftp/request-attrs.go
generated
vendored
Normal file
|
@ -0,0 +1,63 @@
|
|||
package sftp
|
||||
|
||||
// Methods on the Request object to make working with the Flags bitmasks and
|
||||
// Attr(ibutes) byte blob easier. Use Pflags() when working with an Open/Write
|
||||
// request and AttrFlags() and Attributes() when working with SetStat requests.
|
||||
|
||||
import "os"
|
||||
|
||||
// Open packet pflags
|
||||
type pflags struct {
|
||||
Read, Write, Append, Creat, Trunc, Excl bool
|
||||
}
|
||||
|
||||
// testable constructor
|
||||
func newPflags(flags uint32) pflags {
|
||||
return pflags{
|
||||
Read: flags&ssh_FXF_READ != 0,
|
||||
Write: flags&ssh_FXF_WRITE != 0,
|
||||
Append: flags&ssh_FXF_APPEND != 0,
|
||||
Creat: flags&ssh_FXF_CREAT != 0,
|
||||
Trunc: flags&ssh_FXF_TRUNC != 0,
|
||||
Excl: flags&ssh_FXF_EXCL != 0,
|
||||
}
|
||||
}
|
||||
|
||||
// Check bitmap/uint32 for Open packet pflag values
|
||||
func (r *Request) Pflags() pflags {
|
||||
return newPflags(r.Flags)
|
||||
}
|
||||
|
||||
// File attribute flags
|
||||
type aflags struct {
|
||||
Size, UidGid, Permissions, Acmodtime bool
|
||||
}
|
||||
|
||||
// testable constructor
|
||||
func newAflags(flags uint32) aflags {
|
||||
return aflags{
|
||||
Size: (flags & ssh_FILEXFER_ATTR_SIZE) != 0,
|
||||
UidGid: (flags & ssh_FILEXFER_ATTR_UIDGID) != 0,
|
||||
Permissions: (flags & ssh_FILEXFER_ATTR_PERMISSIONS) != 0,
|
||||
Acmodtime: (flags & ssh_FILEXFER_ATTR_ACMODTIME) != 0,
|
||||
}
|
||||
}
|
||||
|
||||
// Check bitmap/uint32 for file attribute flags
|
||||
func (r *Request) AttrFlags(flags uint32) aflags {
|
||||
return newAflags(r.Flags)
|
||||
}
|
||||
|
||||
// File attributes
|
||||
type fileattrs FileStat
|
||||
|
||||
// Return Mode wrapped in os.FileMode
|
||||
func (a fileattrs) FileMode() os.FileMode {
|
||||
return os.FileMode(a.Mode)
|
||||
}
|
||||
|
||||
// Parse file attributes byte blob and return them in object
|
||||
func (r *Request) Attributes() fileattrs {
|
||||
fa, _ := getFileStat(r.Flags, r.Attrs)
|
||||
return fileattrs(*fa)
|
||||
}
|
51
vendor/github.com/pkg/sftp/request-attrs_test.go
generated
vendored
Normal file
51
vendor/github.com/pkg/sftp/request-attrs_test.go
generated
vendored
Normal file
|
@ -0,0 +1,51 @@
|
|||
package sftp
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRequestPflags(t *testing.T) {
|
||||
pflags := newPflags(ssh_FXF_READ | ssh_FXF_WRITE | ssh_FXF_APPEND)
|
||||
assert.True(t, pflags.Read)
|
||||
assert.True(t, pflags.Write)
|
||||
assert.True(t, pflags.Append)
|
||||
assert.False(t, pflags.Creat)
|
||||
assert.False(t, pflags.Trunc)
|
||||
assert.False(t, pflags.Excl)
|
||||
}
|
||||
|
||||
func TestRequestAflags(t *testing.T) {
|
||||
aflags := newAflags(ssh_FILEXFER_ATTR_SIZE | ssh_FILEXFER_ATTR_UIDGID)
|
||||
assert.True(t, aflags.Size)
|
||||
assert.True(t, aflags.UidGid)
|
||||
assert.False(t, aflags.Acmodtime)
|
||||
assert.False(t, aflags.Permissions)
|
||||
}
|
||||
|
||||
func TestRequestAttributes(t *testing.T) {
|
||||
// UID/GID
|
||||
fa := fileattrs{UID: 1, GID: 2}
|
||||
fl := uint32(ssh_FILEXFER_ATTR_UIDGID)
|
||||
at := []byte{}
|
||||
at = marshalUint32(at, 1)
|
||||
at = marshalUint32(at, 2)
|
||||
test_fs, _ := getFileStat(fl, at)
|
||||
assert.Equal(t, fa, fileattrs(*test_fs))
|
||||
// Size and Mode
|
||||
fa = fileattrs{Mode: 700, Size: 99}
|
||||
fl = uint32(ssh_FILEXFER_ATTR_SIZE | ssh_FILEXFER_ATTR_PERMISSIONS)
|
||||
at = []byte{}
|
||||
at = marshalUint64(at, 99)
|
||||
at = marshalUint32(at, 700)
|
||||
test_fs, _ = getFileStat(fl, at)
|
||||
test_fa := fileattrs(*test_fs)
|
||||
assert.Equal(t, fa, test_fa)
|
||||
// FileMode
|
||||
assert.True(t, test_fa.FileMode().IsRegular())
|
||||
assert.False(t, test_fa.FileMode().IsDir())
|
||||
assert.Equal(t, test_fa.FileMode().Perm(), os.FileMode(700).Perm())
|
||||
}
|
20
vendor/github.com/pkg/sftp/request-example.go
generated
vendored
20
vendor/github.com/pkg/sftp/request-example.go
generated
vendored
|
@ -24,8 +24,19 @@ func InMemHandler() Handlers {
|
|||
return Handlers{root, root, root, root}
|
||||
}
|
||||
|
||||
// So I can test Handlers returning errors
|
||||
var (
|
||||
readErr error = nil
|
||||
writeErr error = nil
|
||||
cmdErr error = nil
|
||||
listErr error = nil
|
||||
)
|
||||
|
||||
// Handlers
|
||||
func (fs *root) Fileread(r *Request) (io.ReaderAt, error) {
|
||||
if readErr != nil {
|
||||
return nil, readErr
|
||||
}
|
||||
fs.filesLock.Lock()
|
||||
defer fs.filesLock.Unlock()
|
||||
file, err := fs.fetch(r.Filepath)
|
||||
|
@ -42,6 +53,9 @@ func (fs *root) Fileread(r *Request) (io.ReaderAt, error) {
|
|||
}
|
||||
|
||||
func (fs *root) Filewrite(r *Request) (io.WriterAt, error) {
|
||||
if writeErr != nil {
|
||||
return nil, writeErr
|
||||
}
|
||||
fs.filesLock.Lock()
|
||||
defer fs.filesLock.Unlock()
|
||||
file, err := fs.fetch(r.Filepath)
|
||||
|
@ -60,6 +74,9 @@ func (fs *root) Filewrite(r *Request) (io.WriterAt, error) {
|
|||
}
|
||||
|
||||
func (fs *root) Filecmd(r *Request) error {
|
||||
if cmdErr != nil {
|
||||
return cmdErr
|
||||
}
|
||||
fs.filesLock.Lock()
|
||||
defer fs.filesLock.Unlock()
|
||||
switch r.Method {
|
||||
|
@ -116,6 +133,9 @@ func (f listerat) ListAt(ls []os.FileInfo, offset int64) (int, error) {
|
|||
}
|
||||
|
||||
func (fs *root) Filelist(r *Request) (ListerAt, error) {
|
||||
if listErr != nil {
|
||||
return nil, listErr
|
||||
}
|
||||
fs.filesLock.Lock()
|
||||
defer fs.filesLock.Unlock()
|
||||
|
||||
|
|
15
vendor/github.com/pkg/sftp/request-readme.md
generated
vendored
15
vendor/github.com/pkg/sftp/request-readme.md
generated
vendored
|
@ -11,7 +11,8 @@ manner.
|
|||
The Request structure has 5 public fields which you will deal with.
|
||||
|
||||
- Method (string) - string name of incoming call
|
||||
- Filepath (string) - path of file to act on
|
||||
- Filepath (string) - POSIX path of file to act on
|
||||
- Flags (uint32) - 32bit bitmask value of file open/create flags
|
||||
- Attrs ([]byte) - byte string of file attribute data
|
||||
- Target (string) - target path for renames and sym-links
|
||||
|
||||
|
@ -25,13 +26,18 @@ then sends to the client.
|
|||
### Filewrite(*Request) (io.Writer, error)
|
||||
|
||||
Handler for "Put" method and returns an io.Writer for the file which the server
|
||||
then writes the uploaded file to.
|
||||
then writes the uploaded file to. The file opening "pflags" are currently
|
||||
preserved in the Request.Flags field as a 32bit bitmask value. See the [SFTP
|
||||
spec](https://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#section-6.3) for
|
||||
details.
|
||||
|
||||
### Filecmd(*Request) error
|
||||
|
||||
Handles "SetStat", "Rename", "Rmdir", "Mkdir" and "Symlink" methods. Makes the
|
||||
appropriate changes and returns nil for success or an filesystem like error
|
||||
(eg. os.ErrNotExist).
|
||||
(eg. os.ErrNotExist). The attributes are currently propagated in their raw form
|
||||
([]byte) and will need to be unmarshalled to be useful. See the respond method
|
||||
on sshFxpSetstatPacket for example of you might want to do this.
|
||||
|
||||
### Fileinfo(*Request) ([]os.FileInfo, error)
|
||||
|
||||
|
@ -44,5 +50,4 @@ Readlink).
|
|||
|
||||
- Add support for API users to see trace/debugging info of what is going on
|
||||
inside SFTP server.
|
||||
- Consider adding support for SFTP file append only mode.
|
||||
|
||||
- Unmarshal the file attributes into a structure on the Request object.
|
||||
|
|
104
vendor/github.com/pkg/sftp/request-server.go
generated
vendored
104
vendor/github.com/pkg/sftp/request-server.go
generated
vendored
|
@ -3,9 +3,9 @@ package sftp
|
|||
import (
|
||||
"encoding"
|
||||
"io"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
|
||||
|
@ -29,7 +29,7 @@ type RequestServer struct {
|
|||
*serverConn
|
||||
Handlers Handlers
|
||||
pktMgr *packetManager
|
||||
openRequests map[string]Request
|
||||
openRequests map[string]*Request
|
||||
openRequestLock sync.RWMutex
|
||||
handleCount int
|
||||
}
|
||||
|
@ -47,35 +47,54 @@ func NewRequestServer(rwc io.ReadWriteCloser, h Handlers) *RequestServer {
|
|||
serverConn: svrConn,
|
||||
Handlers: h,
|
||||
pktMgr: newPktMgr(svrConn),
|
||||
openRequests: make(map[string]Request),
|
||||
openRequests: make(map[string]*Request),
|
||||
}
|
||||
}
|
||||
|
||||
// Note that we are explicitly saving the Request as a value.
|
||||
// New Open packet/Request
|
||||
func (rs *RequestServer) nextRequest(r *Request) string {
|
||||
rs.openRequestLock.Lock()
|
||||
defer rs.openRequestLock.Unlock()
|
||||
rs.handleCount++
|
||||
handle := strconv.Itoa(rs.handleCount)
|
||||
rs.openRequests[handle] = *r
|
||||
rs.openRequests[handle] = r
|
||||
return handle
|
||||
}
|
||||
|
||||
// Returns pointer to new copy of Request object
|
||||
func (rs *RequestServer) getRequest(handle string) (*Request, bool) {
|
||||
// Returns Request from openRequests, bool is false if it is missing
|
||||
// If the method is different, save/return a new Request w/ that Method.
|
||||
//
|
||||
// The Requests in openRequests work essentially as open file descriptors that
|
||||
// you can do different things with. What you are doing with it are denoted by
|
||||
// the first packet of that type (read/write/etc). We create a new Request when
|
||||
// it changes to set the request.Method attribute in a thread safe way.
|
||||
func (rs *RequestServer) getRequest(handle, method string) (*Request, bool) {
|
||||
rs.openRequestLock.RLock()
|
||||
defer rs.openRequestLock.RUnlock()
|
||||
r, ok := rs.openRequests[handle]
|
||||
return &r, ok
|
||||
rs.openRequestLock.RUnlock()
|
||||
if !ok || r.Method == method {
|
||||
return r, ok
|
||||
}
|
||||
// if we make it here we need to replace the request
|
||||
rs.openRequestLock.Lock()
|
||||
defer rs.openRequestLock.Unlock()
|
||||
r, ok = rs.openRequests[handle]
|
||||
if !ok || r.Method == method { // re-check needed b/c lock race
|
||||
return r, ok
|
||||
}
|
||||
r = &Request{Method: method, Filepath: r.Filepath, state: r.state}
|
||||
rs.openRequests[handle] = r
|
||||
return r, ok
|
||||
}
|
||||
|
||||
func (rs *RequestServer) closeRequest(handle string) {
|
||||
func (rs *RequestServer) closeRequest(handle string) error {
|
||||
rs.openRequestLock.Lock()
|
||||
defer rs.openRequestLock.Unlock()
|
||||
if r, ok := rs.openRequests[handle]; ok {
|
||||
r.close()
|
||||
delete(rs.openRequests, handle)
|
||||
return r.close()
|
||||
}
|
||||
return syscall.EBADF
|
||||
}
|
||||
|
||||
// Close the read/write/closer to trigger exiting the main server loop
|
||||
|
@ -129,43 +148,26 @@ func (rs *RequestServer) packetWorker(pktChan chan requestPacket) error {
|
|||
rpkt = sshFxVersionPacket{sftpProtocolVersion, nil}
|
||||
case *sshFxpClosePacket:
|
||||
handle := pkt.getHandle()
|
||||
rs.closeRequest(handle)
|
||||
rpkt = statusFromError(pkt, nil)
|
||||
rpkt = statusFromError(pkt, rs.closeRequest(handle))
|
||||
case *sshFxpRealpathPacket:
|
||||
rpkt = cleanPacketPath(pkt)
|
||||
case isOpener:
|
||||
handle := rs.nextRequest(requestFromPacket(pkt))
|
||||
case *sshFxpOpendirPacket:
|
||||
request := requestFromPacket(pkt)
|
||||
handle := rs.nextRequest(request)
|
||||
rpkt = sshFxpHandlePacket{pkt.id(), handle}
|
||||
case *sshFxpFstatPacket:
|
||||
handle := pkt.getHandle()
|
||||
request, ok := rs.getRequest(handle)
|
||||
if !ok {
|
||||
rpkt = statusFromError(pkt, syscall.EBADF)
|
||||
} else {
|
||||
request = requestFromPacket(
|
||||
&sshFxpStatPacket{ID: pkt.id(), Path: request.Filepath})
|
||||
rpkt = request.call(rs.Handlers, pkt)
|
||||
}
|
||||
case *sshFxpFsetstatPacket:
|
||||
handle := pkt.getHandle()
|
||||
request, ok := rs.getRequest(handle)
|
||||
if !ok {
|
||||
rpkt = statusFromError(pkt, syscall.EBADF)
|
||||
} else {
|
||||
request = requestFromPacket(
|
||||
&sshFxpSetstatPacket{ID: pkt.id(), Path: request.Filepath,
|
||||
Flags: pkt.Flags, Attrs: pkt.Attrs,
|
||||
})
|
||||
rpkt = request.call(rs.Handlers, pkt)
|
||||
case *sshFxpOpenPacket:
|
||||
request := requestFromPacket(pkt)
|
||||
handle := rs.nextRequest(request)
|
||||
rpkt = sshFxpHandlePacket{pkt.id(), handle}
|
||||
if pkt.hasPflags(ssh_FXF_CREAT) {
|
||||
if p := request.call(rs.Handlers, pkt); !isOk(p) {
|
||||
rpkt = p // if error in write, return it
|
||||
}
|
||||
}
|
||||
case hasHandle:
|
||||
handle := pkt.getHandle()
|
||||
request, ok := rs.getRequest(handle)
|
||||
uerr := request.updateMethod(pkt)
|
||||
if !ok || uerr != nil {
|
||||
if uerr == nil {
|
||||
uerr = syscall.EBADF
|
||||
}
|
||||
request, ok := rs.getRequest(handle, requestMethod(pkt))
|
||||
if !ok {
|
||||
rpkt = statusFromError(pkt, syscall.EBADF)
|
||||
} else {
|
||||
rpkt = request.call(rs.Handlers, pkt)
|
||||
|
@ -185,6 +187,13 @@ func (rs *RequestServer) packetWorker(pktChan chan requestPacket) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// True is responsePacket is an OK status packet
|
||||
func isOk(rpkt responsePacket) bool {
|
||||
p, ok := rpkt.(sshFxpStatusPacket)
|
||||
return ok && p.StatusError.Code == ssh_FX_OK
|
||||
}
|
||||
|
||||
// clean and return name packet for file
|
||||
func cleanPacketPath(pkt *sshFxpRealpathPacket) responsePacket {
|
||||
path := cleanPath(pkt.getPath())
|
||||
return &sshFxpNamePacket{
|
||||
|
@ -197,12 +206,13 @@ func cleanPacketPath(pkt *sshFxpRealpathPacket) responsePacket {
|
|||
}
|
||||
}
|
||||
|
||||
func cleanPath(path string) string {
|
||||
cleanSlashPath := filepath.ToSlash(filepath.Clean(path))
|
||||
if !strings.HasPrefix(cleanSlashPath, "/") {
|
||||
return "/" + cleanSlashPath
|
||||
// Makes sure we have a clean POSIX (/) absolute path to work with
|
||||
func cleanPath(p string) string {
|
||||
p = filepath.ToSlash(p)
|
||||
if !filepath.IsAbs(p) {
|
||||
p = "/" + p
|
||||
}
|
||||
return cleanSlashPath
|
||||
return path.Clean(p)
|
||||
}
|
||||
|
||||
// Wrap underlying connection methods to use packetManager
|
||||
|
|
55
vendor/github.com/pkg/sftp/request-server_test.go
generated
vendored
55
vendor/github.com/pkg/sftp/request-server_test.go
generated
vendored
|
@ -82,10 +82,10 @@ func TestRequestCache(t *testing.T) {
|
|||
fh := p.svr.nextRequest(foo)
|
||||
bh := p.svr.nextRequest(bar)
|
||||
assert.Len(t, p.svr.openRequests, 2)
|
||||
_foo, ok := p.svr.getRequest(fh)
|
||||
_foo, ok := p.svr.getRequest(fh, "")
|
||||
assert.Equal(t, foo, _foo)
|
||||
assert.True(t, ok)
|
||||
_, ok = p.svr.getRequest("zed")
|
||||
_, ok = p.svr.getRequest("zed", "")
|
||||
assert.False(t, ok)
|
||||
p.svr.closeRequest(fh)
|
||||
p.svr.closeRequest(bh)
|
||||
|
@ -127,16 +127,36 @@ func TestRequestWrite(t *testing.T) {
|
|||
assert.Equal(t, f.content, []byte("hello"))
|
||||
}
|
||||
|
||||
// needs fail check
|
||||
func TestRequestWriteEmpty(t *testing.T) {
|
||||
p := clientRequestServerPair(t)
|
||||
defer p.Close()
|
||||
n, err := putTestFile(p.cli, "/foo", "")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 0, n)
|
||||
r := p.testHandler()
|
||||
f, err := r.fetch("/foo")
|
||||
if assert.Nil(t, err) {
|
||||
assert.False(t, f.isdir)
|
||||
assert.Equal(t, f.content, []byte(""))
|
||||
}
|
||||
// lets test with an error
|
||||
writeErr = os.ErrInvalid
|
||||
n, err = putTestFile(p.cli, "/bar", "")
|
||||
assert.Error(t, err)
|
||||
writeErr = nil
|
||||
}
|
||||
|
||||
func TestRequestFilename(t *testing.T) {
|
||||
p := clientRequestServerPair(t)
|
||||
defer p.Close()
|
||||
_, err := putTestFile(p.cli, "/foo", "hello")
|
||||
assert.Nil(t, err)
|
||||
assert.NoError(t, err)
|
||||
r := p.testHandler()
|
||||
f, err := r.fetch("/foo")
|
||||
assert.Nil(t, err)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, f.Name(), "foo")
|
||||
f, err = r.fetch("/bar")
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestRequestRead(t *testing.T) {
|
||||
|
@ -266,11 +286,12 @@ func TestRequestFstat(t *testing.T) {
|
|||
fp, err := p.cli.Open("/foo")
|
||||
assert.Nil(t, err)
|
||||
fi, err := fp.Stat()
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, fi.Name(), "foo")
|
||||
assert.Equal(t, fi.Size(), int64(5))
|
||||
assert.Equal(t, fi.Mode(), os.FileMode(0644))
|
||||
assert.NoError(t, testOsSys(fi.Sys()))
|
||||
if assert.NoError(t, err) {
|
||||
assert.Equal(t, fi.Name(), "foo")
|
||||
assert.Equal(t, fi.Size(), int64(5))
|
||||
assert.Equal(t, fi.Mode(), os.FileMode(0644))
|
||||
assert.NoError(t, testOsSys(fi.Sys()))
|
||||
}
|
||||
}
|
||||
|
||||
func TestRequestStatFail(t *testing.T) {
|
||||
|
@ -331,17 +352,25 @@ func TestRequestReaddir(t *testing.T) {
|
|||
|
||||
func TestCleanPath(t *testing.T) {
|
||||
assert.Equal(t, "/", cleanPath("/"))
|
||||
assert.Equal(t, "/", cleanPath("."))
|
||||
assert.Equal(t, "/", cleanPath("/."))
|
||||
assert.Equal(t, "/", cleanPath("/a/.."))
|
||||
assert.Equal(t, "/a/c", cleanPath("/a/b/../c"))
|
||||
assert.Equal(t, "/a/c", cleanPath("/a/b/../c/"))
|
||||
assert.Equal(t, "/a", cleanPath("/a/b/.."))
|
||||
assert.Equal(t, "/a/b/c", cleanPath("/a/b/c"))
|
||||
assert.Equal(t, "/", cleanPath("//"))
|
||||
assert.Equal(t, "/a", cleanPath("/a/"))
|
||||
assert.Equal(t, "/a", cleanPath("a/"))
|
||||
assert.Equal(t, "/a/b/c", cleanPath("/a//b//c/"))
|
||||
|
||||
// filepath.ToSlash does not touch \ as char on unix systems, so os.PathSeparator is used for windows compatible tests
|
||||
// filepath.ToSlash does not touch \ as char on unix systems
|
||||
// so os.PathSeparator is used for windows compatible tests
|
||||
bslash := string(os.PathSeparator)
|
||||
assert.Equal(t, "/", cleanPath(bslash))
|
||||
assert.Equal(t, "/", cleanPath(bslash+bslash))
|
||||
assert.Equal(t, "/a", cleanPath(bslash+"a"+bslash))
|
||||
assert.Equal(t, "/a", cleanPath("a"+bslash))
|
||||
assert.Equal(t, "/a/b/c", cleanPath(bslash+"a"+bslash+bslash+"b"+bslash+bslash+"c"+bslash))
|
||||
|
||||
assert.Equal(t, "/a/b/c",
|
||||
cleanPath(bslash+"a"+bslash+bslash+"b"+bslash+bslash+"c"+bslash))
|
||||
}
|
||||
|
|
179
vendor/github.com/pkg/sftp/request.go
generated
vendored
179
vendor/github.com/pkg/sftp/request.go
generated
vendored
|
@ -24,8 +24,8 @@ type Request struct {
|
|||
Attrs []byte // convert to sub-struct
|
||||
Target string // for renames and sym-links
|
||||
// reader/writer/readdir from handlers
|
||||
stateLock *sync.RWMutex
|
||||
state *state
|
||||
stateLock sync.RWMutex
|
||||
state state
|
||||
}
|
||||
|
||||
type state struct {
|
||||
|
@ -35,22 +35,13 @@ type state struct {
|
|||
lsoffset int64
|
||||
}
|
||||
|
||||
type packet_data struct {
|
||||
_id uint32
|
||||
data []byte
|
||||
length uint32
|
||||
offset int64
|
||||
}
|
||||
|
||||
func (pd packet_data) id() uint32 {
|
||||
return pd._id
|
||||
}
|
||||
|
||||
// New Request initialized based on packet data
|
||||
func requestFromPacket(pkt hasPath) *Request {
|
||||
method := requestMethod(pkt)
|
||||
request := NewRequest(method, pkt.getPath())
|
||||
switch p := pkt.(type) {
|
||||
case *sshFxpOpenPacket:
|
||||
request.Flags = p.Pflags
|
||||
case *sshFxpSetstatPacket:
|
||||
request.Flags = p.Flags
|
||||
request.Attrs = p.Attrs.([]byte)
|
||||
|
@ -62,16 +53,9 @@ func requestFromPacket(pkt hasPath) *Request {
|
|||
return request
|
||||
}
|
||||
|
||||
func newRequest() *Request {
|
||||
return &Request{state: &state{}, stateLock: &sync.RWMutex{}}
|
||||
}
|
||||
|
||||
// NewRequest creates a new Request object.
|
||||
func NewRequest(method, path string) *Request {
|
||||
request := newRequest()
|
||||
request.Method = method
|
||||
request.Filepath = cleanPath(path)
|
||||
return request
|
||||
return &Request{Method: method, Filepath: cleanPath(path)}
|
||||
}
|
||||
|
||||
// Returns current offset for file list
|
||||
|
@ -89,19 +73,20 @@ func (r *Request) lsInc(offset int64) {
|
|||
}
|
||||
|
||||
// manage file read/write state
|
||||
func (r *Request) setFileState(s interface{}) {
|
||||
func (r *Request) setWriterState(wa io.WriterAt) {
|
||||
r.stateLock.Lock()
|
||||
defer r.stateLock.Unlock()
|
||||
switch s := s.(type) {
|
||||
case io.WriterAt:
|
||||
r.state.writerAt = s
|
||||
case io.ReaderAt:
|
||||
r.state.readerAt = s
|
||||
case ListerAt:
|
||||
r.state.listerAt = s
|
||||
case int64:
|
||||
r.state.lsoffset = s
|
||||
}
|
||||
r.state.writerAt = wa
|
||||
}
|
||||
func (r *Request) setReaderState(ra io.ReaderAt) {
|
||||
r.stateLock.Lock()
|
||||
defer r.stateLock.Unlock()
|
||||
r.state.readerAt = ra
|
||||
}
|
||||
func (r *Request) setListerState(la ListerAt) {
|
||||
r.stateLock.Lock()
|
||||
defer r.stateLock.Unlock()
|
||||
r.state.listerAt = la
|
||||
}
|
||||
|
||||
func (r *Request) getWriter() io.WriterAt {
|
||||
|
@ -123,29 +108,29 @@ func (r *Request) getLister() ListerAt {
|
|||
}
|
||||
|
||||
// Close reader/writer if possible
|
||||
func (r *Request) close() {
|
||||
func (r *Request) close() error {
|
||||
rd := r.getReader()
|
||||
if c, ok := rd.(io.Closer); ok {
|
||||
c.Close()
|
||||
return c.Close()
|
||||
}
|
||||
wt := r.getWriter()
|
||||
if c, ok := wt.(io.Closer); ok {
|
||||
c.Close()
|
||||
return c.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// called from worker to handle packet/request
|
||||
func (r *Request) call(handlers Handlers, pkt requestPacket) responsePacket {
|
||||
pd := packetData(pkt)
|
||||
switch r.Method {
|
||||
case "Get":
|
||||
return fileget(handlers.FileGet, r, pd)
|
||||
case "Put": // add "Append" to this to handle append only file writes
|
||||
return fileput(handlers.FilePut, r, pd)
|
||||
return fileget(handlers.FileGet, r, pkt)
|
||||
case "Put", "Open":
|
||||
return fileput(handlers.FilePut, r, pkt)
|
||||
case "Setstat", "Rename", "Rmdir", "Mkdir", "Symlink", "Remove":
|
||||
return filecmd(handlers.FileCmd, r, pd)
|
||||
return filecmd(handlers.FileCmd, r, pkt)
|
||||
case "List", "Stat", "Readlink":
|
||||
return filelist(handlers.FileList, r, pd)
|
||||
return filelist(handlers.FileList, r, pkt)
|
||||
default:
|
||||
return statusFromError(pkt,
|
||||
errors.Errorf("unexpected method: %s", r.Method))
|
||||
|
@ -153,91 +138,78 @@ func (r *Request) call(handlers Handlers, pkt requestPacket) responsePacket {
|
|||
}
|
||||
|
||||
// file data for additional read/write packets
|
||||
func packetData(p requestPacket) packet_data {
|
||||
pd := packet_data{_id: p.id()}
|
||||
func packetData(p requestPacket) (data []byte, offset int64, length uint32) {
|
||||
switch p := p.(type) {
|
||||
case *sshFxpReadPacket:
|
||||
pd.length = p.Len
|
||||
pd.offset = int64(p.Offset)
|
||||
length = p.Len
|
||||
offset = int64(p.Offset)
|
||||
case *sshFxpWritePacket:
|
||||
pd.data = p.Data
|
||||
pd.length = p.Length
|
||||
pd.offset = int64(p.Offset)
|
||||
data = p.Data
|
||||
length = p.Length
|
||||
offset = int64(p.Offset)
|
||||
}
|
||||
return pd
|
||||
return
|
||||
}
|
||||
|
||||
// wrap FileReader handler
|
||||
func fileget(h FileReader, r *Request, pd packet_data) responsePacket {
|
||||
func fileget(h FileReader, r *Request, pkt requestPacket) responsePacket {
|
||||
var err error
|
||||
reader := r.getReader()
|
||||
if reader == nil {
|
||||
reader, err = h.Fileread(r)
|
||||
if err != nil {
|
||||
return statusFromError(pd, err)
|
||||
return statusFromError(pkt, err)
|
||||
}
|
||||
r.setFileState(reader)
|
||||
r.setReaderState(reader)
|
||||
}
|
||||
|
||||
data := make([]byte, clamp(pd.length, maxTxPacket))
|
||||
n, err := reader.ReadAt(data, pd.offset)
|
||||
_, offset, length := packetData(pkt)
|
||||
data := make([]byte, clamp(length, maxTxPacket))
|
||||
n, err := reader.ReadAt(data, offset)
|
||||
// only return EOF erro if no data left to read
|
||||
if err != nil && (err != io.EOF || n == 0) {
|
||||
return statusFromError(pd, err)
|
||||
return statusFromError(pkt, err)
|
||||
}
|
||||
return &sshFxpDataPacket{
|
||||
ID: pd.id(),
|
||||
ID: pkt.id(),
|
||||
Length: uint32(n),
|
||||
Data: data[:n],
|
||||
}
|
||||
}
|
||||
|
||||
// wrap FileWriter handler
|
||||
func fileput(h FileWriter, r *Request, pd packet_data) responsePacket {
|
||||
func fileput(h FileWriter, r *Request, pkt requestPacket) responsePacket {
|
||||
var err error
|
||||
writer := r.getWriter()
|
||||
if writer == nil {
|
||||
writer, err = h.Filewrite(r)
|
||||
if err != nil {
|
||||
return statusFromError(pd, err)
|
||||
return statusFromError(pkt, err)
|
||||
}
|
||||
r.setFileState(writer)
|
||||
r.setWriterState(writer)
|
||||
}
|
||||
|
||||
_, err = writer.WriteAt(pd.data, pd.offset)
|
||||
if err != nil {
|
||||
return statusFromError(pd, err)
|
||||
}
|
||||
return &sshFxpStatusPacket{
|
||||
ID: pd.id(),
|
||||
StatusError: StatusError{
|
||||
Code: ssh_FX_OK,
|
||||
}}
|
||||
data, offset, _ := packetData(pkt)
|
||||
_, err = writer.WriteAt(data, offset)
|
||||
return statusFromError(pkt, err)
|
||||
}
|
||||
|
||||
// wrap FileCmder handler
|
||||
func filecmd(h FileCmder, r *Request, pd packet_data) responsePacket {
|
||||
func filecmd(h FileCmder, r *Request, pkt requestPacket) responsePacket {
|
||||
err := h.Filecmd(r)
|
||||
if err != nil {
|
||||
return statusFromError(pd, err)
|
||||
}
|
||||
return &sshFxpStatusPacket{
|
||||
ID: pd.id(),
|
||||
StatusError: StatusError{
|
||||
Code: ssh_FX_OK,
|
||||
}}
|
||||
return statusFromError(pkt, err)
|
||||
}
|
||||
|
||||
// wrap FileLister handler
|
||||
func filelist(h FileLister, r *Request, pd packet_data) responsePacket {
|
||||
func filelist(h FileLister, r *Request, pkt requestPacket) responsePacket {
|
||||
var err error
|
||||
lister := r.getLister()
|
||||
if lister == nil {
|
||||
lister, err = h.Filelist(r)
|
||||
if err != nil {
|
||||
return statusFromError(pd, err)
|
||||
return statusFromError(pkt, err)
|
||||
}
|
||||
r.setFileState(lister)
|
||||
r.setListerState(lister)
|
||||
}
|
||||
|
||||
offset := r.lsNext()
|
||||
|
@ -250,13 +222,13 @@ func filelist(h FileLister, r *Request, pd packet_data) responsePacket {
|
|||
switch r.Method {
|
||||
case "List":
|
||||
if err != nil && err != io.EOF {
|
||||
return statusFromError(pd, err)
|
||||
return statusFromError(pkt, err)
|
||||
}
|
||||
if n == 0 {
|
||||
return statusFromError(pd, io.EOF)
|
||||
return statusFromError(pkt, io.EOF)
|
||||
}
|
||||
dirname := filepath.ToSlash(path.Base(r.Filepath))
|
||||
ret := &sshFxpNamePacket{ID: pd.id()}
|
||||
ret := &sshFxpNamePacket{ID: pkt.id()}
|
||||
|
||||
for _, fi := range finfo {
|
||||
ret.NameAttrs = append(ret.NameAttrs, sshFxpNameAttr{
|
||||
|
@ -268,29 +240,29 @@ func filelist(h FileLister, r *Request, pd packet_data) responsePacket {
|
|||
return ret
|
||||
case "Stat":
|
||||
if err != nil && err != io.EOF {
|
||||
return statusFromError(pd, err)
|
||||
return statusFromError(pkt, err)
|
||||
}
|
||||
if n == 0 {
|
||||
err = &os.PathError{Op: "stat", Path: r.Filepath,
|
||||
Err: syscall.ENOENT}
|
||||
return statusFromError(pd, err)
|
||||
return statusFromError(pkt, err)
|
||||
}
|
||||
return &sshFxpStatResponse{
|
||||
ID: pd.id(),
|
||||
ID: pkt.id(),
|
||||
info: finfo[0],
|
||||
}
|
||||
case "Readlink":
|
||||
if err != nil && err != io.EOF {
|
||||
return statusFromError(pd, err)
|
||||
return statusFromError(pkt, err)
|
||||
}
|
||||
if n == 0 {
|
||||
err = &os.PathError{Op: "readlink", Path: r.Filepath,
|
||||
Err: syscall.ENOENT}
|
||||
return statusFromError(pd, err)
|
||||
return statusFromError(pkt, err)
|
||||
}
|
||||
filename := finfo[0].Name()
|
||||
return &sshFxpNamePacket{
|
||||
ID: pd.id(),
|
||||
ID: pkt.id(),
|
||||
NameAttrs: []sshFxpNameAttr{{
|
||||
Name: filename,
|
||||
LongName: filename,
|
||||
|
@ -299,31 +271,22 @@ func filelist(h FileLister, r *Request, pd packet_data) responsePacket {
|
|||
}
|
||||
default:
|
||||
err = errors.Errorf("unexpected method: %s", r.Method)
|
||||
return statusFromError(pd, err)
|
||||
return statusFromError(pkt, err)
|
||||
}
|
||||
}
|
||||
|
||||
// file data for additional read/write packets
|
||||
func (r *Request) updateMethod(p hasHandle) error {
|
||||
switch p := p.(type) {
|
||||
case *sshFxpReadPacket:
|
||||
r.Method = "Get"
|
||||
case *sshFxpWritePacket:
|
||||
r.Method = "Put"
|
||||
case *sshFxpReaddirPacket:
|
||||
r.Method = "List"
|
||||
default:
|
||||
return errors.Errorf("unexpected packet type %T", p)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// init attributes of request object from packet data
|
||||
func requestMethod(p hasPath) (method string) {
|
||||
func requestMethod(p requestPacket) (method string) {
|
||||
switch p.(type) {
|
||||
case *sshFxpReadPacket:
|
||||
method = "Get"
|
||||
case *sshFxpWritePacket:
|
||||
method = "Put"
|
||||
case *sshFxpReaddirPacket:
|
||||
method = "List"
|
||||
case *sshFxpOpenPacket, *sshFxpOpendirPacket:
|
||||
method = "Open"
|
||||
case *sshFxpSetstatPacket:
|
||||
case *sshFxpSetstatPacket, *sshFxpFsetstatPacket:
|
||||
method = "Setstat"
|
||||
case *sshFxpRenamePacket:
|
||||
method = "Rename"
|
||||
|
@ -331,7 +294,7 @@ func requestMethod(p hasPath) (method string) {
|
|||
method = "Symlink"
|
||||
case *sshFxpRemovePacket:
|
||||
method = "Remove"
|
||||
case *sshFxpStatPacket, *sshFxpLstatPacket:
|
||||
case *sshFxpStatPacket, *sshFxpLstatPacket, *sshFxpFstatPacket:
|
||||
method = "Stat"
|
||||
case *sshFxpRmdirPacket:
|
||||
method = "Rmdir"
|
||||
|
|
12
vendor/github.com/pkg/sftp/request_test.go
generated
vendored
12
vendor/github.com/pkg/sftp/request_test.go
generated
vendored
|
@ -1,8 +1,6 @@
|
|||
package sftp
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"bytes"
|
||||
|
@ -58,12 +56,10 @@ var filecontents = []byte("file-data.")
|
|||
|
||||
func testRequest(method string) *Request {
|
||||
request := &Request{
|
||||
Filepath: "./request_test.go",
|
||||
Method: method,
|
||||
Attrs: []byte("foo"),
|
||||
Target: "foo",
|
||||
state: &state{},
|
||||
stateLock: &sync.RWMutex{},
|
||||
Filepath: "./request_test.go",
|
||||
Method: method,
|
||||
Attrs: []byte("foo"),
|
||||
Target: "foo",
|
||||
}
|
||||
return request
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue