forked from TrueCloudLab/rclone
vendor: update all dependencies
This commit is contained in:
parent
3f0789e2db
commit
08021c4636
2474 changed files with 435818 additions and 282709 deletions
6
vendor/github.com/pkg/sftp/attrs.go
generated
vendored
6
vendor/github.com/pkg/sftp/attrs.go
generated
vendored
|
@ -43,8 +43,10 @@ func (fi *fileInfo) IsDir() bool { return fi.Mode().IsDir() }
|
|||
|
||||
func (fi *fileInfo) Sys() interface{} { return fi.sys }
|
||||
|
||||
// FileStat holds the original unmarshalled values from a call to READDIR or *STAT.
|
||||
// It is exported for the purposes of accessing the raw values via os.FileInfo.Sys()
|
||||
// FileStat holds the original unmarshalled values from a call to READDIR or
|
||||
// *STAT. It is exported for the purposes of accessing the raw values via
|
||||
// os.FileInfo.Sys(). It is also used server side to store the unmarshalled
|
||||
// values for SetStat.
|
||||
type FileStat struct {
|
||||
Size uint64
|
||||
Mode uint32
|
||||
|
|
106
vendor/github.com/pkg/sftp/client.go
generated
vendored
106
vendor/github.com/pkg/sftp/client.go
generated
vendored
|
@ -7,6 +7,7 @@ import (
|
|||
"os"
|
||||
"path"
|
||||
"sync/atomic"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/kr/fs"
|
||||
|
@ -75,6 +76,19 @@ func MaxPacket(size int) ClientOption {
|
|||
return MaxPacketChecked(size)
|
||||
}
|
||||
|
||||
// MaxConcurrentRequestsPerFile sets the maximum concurrent requests allowed for a single file.
|
||||
//
|
||||
// The default maximum concurrent requests is 64.
|
||||
func MaxConcurrentRequestsPerFile(n int) ClientOption {
|
||||
return func(c *Client) error {
|
||||
if n < 1 {
|
||||
return errors.Errorf("n must be greater or equal to 1")
|
||||
}
|
||||
c.maxConcurrentRequests = n
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// NewClient creates a new SFTP client on conn, using zero or more option
|
||||
// functions.
|
||||
func NewClient(conn *ssh.Client, opts ...ClientOption) (*Client, error) {
|
||||
|
@ -109,7 +123,8 @@ func NewClientPipe(rd io.Reader, wr io.WriteCloser, opts ...ClientOption) (*Clie
|
|||
},
|
||||
inflight: make(map[uint32]chan<- result),
|
||||
},
|
||||
maxPacket: 1 << 15,
|
||||
maxPacket: 1 << 15,
|
||||
maxConcurrentRequests: 64,
|
||||
}
|
||||
if err := sftp.applyOptions(opts...); err != nil {
|
||||
wr.Close()
|
||||
|
@ -136,8 +151,9 @@ func NewClientPipe(rd io.Reader, wr io.WriteCloser, opts ...ClientOption) (*Clie
|
|||
type Client struct {
|
||||
clientConn
|
||||
|
||||
maxPacket int // max packet size read or written.
|
||||
nextid uint32
|
||||
maxPacket int // max packet size read or written.
|
||||
nextid uint32
|
||||
maxConcurrentRequests int
|
||||
}
|
||||
|
||||
// Create creates the named file mode 0666 (before umask), truncating it if it
|
||||
|
@ -680,6 +696,54 @@ func (c *Client) Mkdir(path string) error {
|
|||
}
|
||||
}
|
||||
|
||||
// MkdirAll creates a directory named path, along with any necessary parents,
|
||||
// and returns nil, or else returns an error.
|
||||
// If path is already a directory, MkdirAll does nothing and returns nil.
|
||||
// If path contains a regular file, an error is returned
|
||||
func (c *Client) MkdirAll(path string) error {
|
||||
// Most of this code mimics https://golang.org/src/os/path.go?s=514:561#L13
|
||||
// Fast path: if we can tell whether path is a directory or file, stop with success or error.
|
||||
dir, err := c.Stat(path)
|
||||
if err == nil {
|
||||
if dir.IsDir() {
|
||||
return nil
|
||||
}
|
||||
return &os.PathError{Op: "mkdir", Path: path, Err: syscall.ENOTDIR}
|
||||
}
|
||||
|
||||
// Slow path: make sure parent exists and then call Mkdir for path.
|
||||
i := len(path)
|
||||
for i > 0 && os.IsPathSeparator(path[i-1]) { // Skip trailing path separator.
|
||||
i--
|
||||
}
|
||||
|
||||
j := i
|
||||
for j > 0 && !os.IsPathSeparator(path[j-1]) { // Scan backward over element.
|
||||
j--
|
||||
}
|
||||
|
||||
if j > 1 {
|
||||
// Create parent
|
||||
err = c.MkdirAll(path[0 : j-1])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Parent now exists; invoke Mkdir and use its result.
|
||||
err = c.Mkdir(path)
|
||||
if err != nil {
|
||||
// Handle arguments like "foo/." by
|
||||
// double-checking that directory doesn't exist.
|
||||
dir, err1 := c.Lstat(path)
|
||||
if err1 == nil && dir.IsDir() {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// applyOptions applies options functions to the Client.
|
||||
// If an error is encountered, option processing ceases.
|
||||
func (c *Client) applyOptions(opts ...ClientOption) error {
|
||||
|
@ -710,12 +774,15 @@ func (f *File) Name() string {
|
|||
return f.path
|
||||
}
|
||||
|
||||
const maxConcurrentRequests = 64
|
||||
|
||||
// Read reads up to len(b) bytes from the File. It returns the number of bytes
|
||||
// read and an error, if any. Read follows io.Reader semantics, so when Read
|
||||
// encounters an error or EOF condition after successfully reading n > 0 bytes,
|
||||
// it returns the number of bytes read.
|
||||
//
|
||||
// To maximise throughput for transferring the entire file (especially
|
||||
// over high latency links) it is recommended to use WriteTo rather
|
||||
// than calling Read multiple times. io.Copy will do this
|
||||
// automatically.
|
||||
func (f *File) Read(b []byte) (int, error) {
|
||||
// Split the read into multiple maxPacket sized concurrent reads
|
||||
// bounded by maxConcurrentRequests. This allows reads with a suitably
|
||||
|
@ -726,7 +793,7 @@ func (f *File) Read(b []byte) (int, error) {
|
|||
offset := f.offset
|
||||
// maxConcurrentRequests buffer to deal with broadcastErr() floods
|
||||
// also must have a buffer of max value of (desiredInFlight - inFlight)
|
||||
ch := make(chan result, maxConcurrentRequests+1)
|
||||
ch := make(chan result, f.c.maxConcurrentRequests+1)
|
||||
type inflightRead struct {
|
||||
b []byte
|
||||
offset uint64
|
||||
|
@ -791,7 +858,7 @@ func (f *File) Read(b []byte) (int, error) {
|
|||
if n < len(req.b) {
|
||||
sendReq(req.b[l:], req.offset+uint64(l))
|
||||
}
|
||||
if desiredInFlight < maxConcurrentRequests {
|
||||
if desiredInFlight < f.c.maxConcurrentRequests {
|
||||
desiredInFlight++
|
||||
}
|
||||
default:
|
||||
|
@ -811,6 +878,10 @@ func (f *File) Read(b []byte) (int, error) {
|
|||
|
||||
// WriteTo writes the file to w. The return value is the number of bytes
|
||||
// written. Any error encountered during the write is also returned.
|
||||
//
|
||||
// This method is preferred over calling Read multiple times to
|
||||
// maximise throughput for transferring the entire file (especially
|
||||
// over high latency links).
|
||||
func (f *File) WriteTo(w io.Writer) (int64, error) {
|
||||
fi, err := f.Stat()
|
||||
if err != nil {
|
||||
|
@ -822,7 +893,7 @@ func (f *File) WriteTo(w io.Writer) (int64, error) {
|
|||
writeOffset := offset
|
||||
fileSize := uint64(fi.Size())
|
||||
// see comment on same line in Read() above
|
||||
ch := make(chan result, maxConcurrentRequests+1)
|
||||
ch := make(chan result, f.c.maxConcurrentRequests+1)
|
||||
type inflightRead struct {
|
||||
b []byte
|
||||
offset uint64
|
||||
|
@ -902,7 +973,7 @@ func (f *File) WriteTo(w io.Writer) (int64, error) {
|
|||
switch {
|
||||
case offset > fileSize:
|
||||
desiredInFlight = 1
|
||||
case desiredInFlight < maxConcurrentRequests:
|
||||
case desiredInFlight < f.c.maxConcurrentRequests:
|
||||
desiredInFlight++
|
||||
}
|
||||
writeOffset += uint64(nbytes)
|
||||
|
@ -956,6 +1027,11 @@ func (f *File) Stat() (os.FileInfo, error) {
|
|||
// Write writes len(b) bytes to the File. It returns the number of bytes
|
||||
// written and an error, if any. Write returns a non-nil error when n !=
|
||||
// len(b).
|
||||
//
|
||||
// To maximise throughput for transferring the entire file (especially
|
||||
// over high latency links) it is recommended to use ReadFrom rather
|
||||
// than calling Write multiple times. io.Copy will do this
|
||||
// automatically.
|
||||
func (f *File) Write(b []byte) (int, error) {
|
||||
// Split the write into multiple maxPacket sized concurrent writes
|
||||
// bounded by maxConcurrentRequests. This allows writes with a suitably
|
||||
|
@ -965,7 +1041,7 @@ func (f *File) Write(b []byte) (int, error) {
|
|||
desiredInFlight := 1
|
||||
offset := f.offset
|
||||
// see comment on same line in Read() above
|
||||
ch := make(chan result, maxConcurrentRequests+1)
|
||||
ch := make(chan result, f.c.maxConcurrentRequests+1)
|
||||
var firstErr error
|
||||
written := len(b)
|
||||
for len(b) > 0 || inFlight > 0 {
|
||||
|
@ -1001,7 +1077,7 @@ func (f *File) Write(b []byte) (int, error) {
|
|||
firstErr = err
|
||||
break
|
||||
}
|
||||
if desiredInFlight < maxConcurrentRequests {
|
||||
if desiredInFlight < f.c.maxConcurrentRequests {
|
||||
desiredInFlight++
|
||||
}
|
||||
default:
|
||||
|
@ -1021,12 +1097,16 @@ func (f *File) Write(b []byte) (int, error) {
|
|||
// ReadFrom reads data from r until EOF and writes it to the file. The return
|
||||
// value is the number of bytes read. Any error except io.EOF encountered
|
||||
// during the read is also returned.
|
||||
//
|
||||
// This method is preferred over calling Write multiple times to
|
||||
// maximise throughput for transferring the entire file (especially
|
||||
// over high latency links).
|
||||
func (f *File) ReadFrom(r io.Reader) (int64, error) {
|
||||
inFlight := 0
|
||||
desiredInFlight := 1
|
||||
offset := f.offset
|
||||
// see comment on same line in Read() above
|
||||
ch := make(chan result, maxConcurrentRequests+1)
|
||||
ch := make(chan result, f.c.maxConcurrentRequests+1)
|
||||
var firstErr error
|
||||
read := int64(0)
|
||||
b := make([]byte, f.c.maxPacket)
|
||||
|
@ -1065,7 +1145,7 @@ func (f *File) ReadFrom(r io.Reader) (int64, error) {
|
|||
firstErr = err
|
||||
break
|
||||
}
|
||||
if desiredInFlight < maxConcurrentRequests {
|
||||
if desiredInFlight < f.c.maxConcurrentRequests {
|
||||
desiredInFlight++
|
||||
}
|
||||
default:
|
||||
|
|
21
vendor/github.com/pkg/sftp/client_integration_test.go
generated
vendored
21
vendor/github.com/pkg/sftp/client_integration_test.go
generated
vendored
|
@ -260,6 +260,27 @@ func TestClientMkdir(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
func TestClientMkdirAll(t *testing.T) {
|
||||
sftp, cmd := testClient(t, READWRITE, NO_DELAY)
|
||||
defer cmd.Wait()
|
||||
defer sftp.Close()
|
||||
|
||||
dir, err := ioutil.TempDir("", "sftptest")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
sub := path.Join(dir, "mkdir1", "mkdir2", "mkdir3")
|
||||
if err := sftp.MkdirAll(sub); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
info, err := os.Lstat(sub)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !info.IsDir() {
|
||||
t.Fatalf("Expected mkdirall to create dir at: %s", sub)
|
||||
}
|
||||
}
|
||||
|
||||
func TestClientOpen(t *testing.T) {
|
||||
sftp, cmd := testClient(t, READONLY, NO_DELAY)
|
||||
|
|
52
vendor/github.com/pkg/sftp/request-attrs.go
generated
vendored
52
vendor/github.com/pkg/sftp/request-attrs.go
generated
vendored
|
@ -3,17 +3,16 @@ 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 {
|
||||
// File Open and Write Flags. Correlate directly with with os.OpenFile flags
|
||||
// (https://golang.org/pkg/os/#pkg-constants).
|
||||
type FileOpenFlags struct {
|
||||
Read, Write, Append, Creat, Trunc, Excl bool
|
||||
}
|
||||
|
||||
// testable constructor
|
||||
func newPflags(flags uint32) pflags {
|
||||
return pflags{
|
||||
func newFileOpenFlags(flags uint32) FileOpenFlags {
|
||||
return FileOpenFlags{
|
||||
Read: flags&ssh_FXF_READ != 0,
|
||||
Write: flags&ssh_FXF_WRITE != 0,
|
||||
Append: flags&ssh_FXF_APPEND != 0,
|
||||
|
@ -23,19 +22,21 @@ func newPflags(flags uint32) pflags {
|
|||
}
|
||||
}
|
||||
|
||||
// Check bitmap/uint32 for Open packet pflag values
|
||||
func (r *Request) Pflags() pflags {
|
||||
return newPflags(r.Flags)
|
||||
// Pflags converts the bitmap/uint32 from SFTP Open packet pflag values,
|
||||
// into a FileOpenFlags struct with booleans set for flags set in bitmap.
|
||||
func (r *Request) Pflags() FileOpenFlags {
|
||||
return newFileOpenFlags(r.Flags)
|
||||
}
|
||||
|
||||
// File attribute flags
|
||||
type aflags struct {
|
||||
// Flags that indicate whether SFTP file attributes were passed. When a flag is
|
||||
// true the corresponding attribute should be available from the FileStat
|
||||
// object returned by Attributes method. Used with SetStat.
|
||||
type FileAttrFlags struct {
|
||||
Size, UidGid, Permissions, Acmodtime bool
|
||||
}
|
||||
|
||||
// testable constructor
|
||||
func newAflags(flags uint32) aflags {
|
||||
return aflags{
|
||||
func newFileAttrFlags(flags uint32) FileAttrFlags {
|
||||
return FileAttrFlags{
|
||||
Size: (flags & ssh_FILEXFER_ATTR_SIZE) != 0,
|
||||
UidGid: (flags & ssh_FILEXFER_ATTR_UIDGID) != 0,
|
||||
Permissions: (flags & ssh_FILEXFER_ATTR_PERMISSIONS) != 0,
|
||||
|
@ -43,21 +44,20 @@ func newAflags(flags uint32) aflags {
|
|||
}
|
||||
}
|
||||
|
||||
// Check bitmap/uint32 for file attribute flags
|
||||
func (r *Request) AttrFlags(flags uint32) aflags {
|
||||
return newAflags(r.Flags)
|
||||
// FileAttrFlags returns a FileAttrFlags boolean struct based on the
|
||||
// bitmap/uint32 file attribute flags from the SFTP packaet.
|
||||
func (r *Request) AttrFlags() FileAttrFlags {
|
||||
return newFileAttrFlags(r.Flags)
|
||||
}
|
||||
|
||||
// File attributes
|
||||
type fileattrs FileStat
|
||||
|
||||
// Return Mode wrapped in os.FileMode
|
||||
func (a fileattrs) FileMode() os.FileMode {
|
||||
// FileMode returns the Mode SFTP file attributes wrapped as os.FileMode
|
||||
func (a FileStat) 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)
|
||||
// Attributres parses file attributes byte blob and return them in a
|
||||
// FileStat object.
|
||||
func (r *Request) Attributes() *FileStat {
|
||||
fs, _ := getFileStat(r.Flags, r.Attrs)
|
||||
return fs
|
||||
}
|
||||
|
|
20
vendor/github.com/pkg/sftp/request-attrs_test.go
generated
vendored
20
vendor/github.com/pkg/sftp/request-attrs_test.go
generated
vendored
|
@ -9,7 +9,7 @@ import (
|
|||
)
|
||||
|
||||
func TestRequestPflags(t *testing.T) {
|
||||
pflags := newPflags(ssh_FXF_READ | ssh_FXF_WRITE | ssh_FXF_APPEND)
|
||||
pflags := newFileOpenFlags(ssh_FXF_READ | ssh_FXF_WRITE | ssh_FXF_APPEND)
|
||||
assert.True(t, pflags.Read)
|
||||
assert.True(t, pflags.Write)
|
||||
assert.True(t, pflags.Append)
|
||||
|
@ -19,7 +19,8 @@ func TestRequestPflags(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestRequestAflags(t *testing.T) {
|
||||
aflags := newAflags(ssh_FILEXFER_ATTR_SIZE | ssh_FILEXFER_ATTR_UIDGID)
|
||||
aflags := newFileAttrFlags(
|
||||
ssh_FILEXFER_ATTR_SIZE | ssh_FILEXFER_ATTR_UIDGID)
|
||||
assert.True(t, aflags.Size)
|
||||
assert.True(t, aflags.UidGid)
|
||||
assert.False(t, aflags.Acmodtime)
|
||||
|
@ -28,24 +29,23 @@ func TestRequestAflags(t *testing.T) {
|
|||
|
||||
func TestRequestAttributes(t *testing.T) {
|
||||
// UID/GID
|
||||
fa := fileattrs{UID: 1, GID: 2}
|
||||
fa := FileStat{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))
|
||||
assert.Equal(t, fa, *test_fs)
|
||||
// Size and Mode
|
||||
fa = fileattrs{Mode: 700, Size: 99}
|
||||
fa = FileStat{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)
|
||||
assert.Equal(t, fa, *test_fs)
|
||||
// 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())
|
||||
assert.True(t, test_fs.FileMode().IsRegular())
|
||||
assert.False(t, test_fs.FileMode().IsDir())
|
||||
assert.Equal(t, test_fs.FileMode().Perm(), os.FileMode(700).Perm())
|
||||
}
|
||||
|
|
4
vendor/github.com/pkg/sftp/request-example.go
generated
vendored
4
vendor/github.com/pkg/sftp/request-example.go
generated
vendored
|
@ -15,7 +15,7 @@ import (
|
|||
"time"
|
||||
)
|
||||
|
||||
// InMemHandler returns a Hanlders object with the test handlers
|
||||
// InMemHandler returns a Hanlders object with the test handlers.
|
||||
func InMemHandler() Handlers {
|
||||
root := &root{
|
||||
files: make(map[string]*memFile),
|
||||
|
@ -24,7 +24,7 @@ func InMemHandler() Handlers {
|
|||
return Handlers{root, root, root, root}
|
||||
}
|
||||
|
||||
// Handlers
|
||||
// Example Handlers
|
||||
func (fs *root) Fileread(r *Request) (io.ReaderAt, error) {
|
||||
if fs.mockErr != nil {
|
||||
return nil, fs.mockErr
|
||||
|
|
9
vendor/github.com/pkg/sftp/request-interfaces.go
generated
vendored
9
vendor/github.com/pkg/sftp/request-interfaces.go
generated
vendored
|
@ -8,8 +8,14 @@ import (
|
|||
// Interfaces are differentiated based on required returned values.
|
||||
// All input arguments are to be pulled from Request (the only arg).
|
||||
|
||||
// The Handler interfaces all take the Request object as its only argument.
|
||||
// All the data you should need to handle the call are in the Request object.
|
||||
// The request.Method attribute is initially the most important one as it
|
||||
// determines which Handler gets called.
|
||||
|
||||
// FileReader should return an io.ReaderAt for the filepath
|
||||
// Note in cases of an error, the error text will be sent to the client.
|
||||
// Called for Methods: Get
|
||||
type FileReader interface {
|
||||
Fileread(*Request) (io.ReaderAt, error)
|
||||
}
|
||||
|
@ -19,18 +25,21 @@ type FileReader interface {
|
|||
// The request server code will call Close() on the returned io.WriterAt
|
||||
// ojbect if an io.Closer type assertion succeeds.
|
||||
// Note in cases of an error, the error text will be sent to the client.
|
||||
// Called for Methods: Put, Open
|
||||
type FileWriter interface {
|
||||
Filewrite(*Request) (io.WriterAt, error)
|
||||
}
|
||||
|
||||
// FileCmder should return an error
|
||||
// Note in cases of an error, the error text will be sent to the client.
|
||||
// Called for Methods: Setstat, Rename, Rmdir, Mkdir, Symlink, Remove
|
||||
type FileCmder interface {
|
||||
Filecmd(*Request) error
|
||||
}
|
||||
|
||||
// FileLister should return an object that fulfils the ListerAt interface
|
||||
// Note in cases of an error, the error text will be sent to the client.
|
||||
// Called for Methods: List, Stat, Readlink
|
||||
type FileLister interface {
|
||||
Filelist(*Request) (ListerAt, error)
|
||||
}
|
||||
|
|
13
vendor/github.com/pkg/sftp/request-server.go
generated
vendored
13
vendor/github.com/pkg/sftp/request-server.go
generated
vendored
|
@ -4,6 +4,7 @@ import (
|
|||
"context"
|
||||
"encoding"
|
||||
"io"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
|
@ -173,8 +174,16 @@ func (rs *RequestServer) packetWorker(
|
|||
rpkt = cleanPacketPath(pkt)
|
||||
case *sshFxpOpendirPacket:
|
||||
request := requestFromPacket(ctx, pkt)
|
||||
handle := rs.nextRequest(request)
|
||||
rpkt = sshFxpHandlePacket{pkt.id(), handle}
|
||||
rpkt = request.call(rs.Handlers, pkt)
|
||||
if stat, ok := rpkt.(*sshFxpStatResponse); ok {
|
||||
if stat.info.IsDir() {
|
||||
handle := rs.nextRequest(request)
|
||||
rpkt = sshFxpHandlePacket{pkt.id(), handle}
|
||||
} else {
|
||||
rpkt = statusFromError(pkt, &os.PathError{
|
||||
Path: request.Filepath, Err: syscall.ENOTDIR})
|
||||
}
|
||||
}
|
||||
case *sshFxpOpenPacket:
|
||||
request := requestFromPacket(ctx, pkt)
|
||||
handle := rs.nextRequest(request)
|
||||
|
|
5
vendor/github.com/pkg/sftp/request-server_test.go
generated
vendored
5
vendor/github.com/pkg/sftp/request-server_test.go
generated
vendored
|
@ -355,6 +355,11 @@ func TestRequestReaddir(t *testing.T) {
|
|||
_, err := putTestFile(p.cli, fname, fname)
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
_, err := p.cli.ReadDir("/foo_01")
|
||||
assert.Equal(t, &StatusError{Code: ssh_FX_FAILURE,
|
||||
msg: " /foo_01: not a directory"}, err)
|
||||
_, err = p.cli.ReadDir("/does_not_exist")
|
||||
assert.Equal(t, os.ErrNotExist, err)
|
||||
di, err := p.cli.ReadDir("/")
|
||||
assert.Nil(t, err)
|
||||
assert.Len(t, di, 100)
|
||||
|
|
40
vendor/github.com/pkg/sftp/request.go
generated
vendored
40
vendor/github.com/pkg/sftp/request.go
generated
vendored
|
@ -152,6 +152,11 @@ func (r *Request) getLister() ListerAt {
|
|||
|
||||
// Close reader/writer if possible
|
||||
func (r *Request) close() error {
|
||||
defer func() {
|
||||
if r.cancelCtx != nil {
|
||||
r.cancelCtx()
|
||||
}
|
||||
}()
|
||||
rd := r.getReader()
|
||||
if c, ok := rd.(io.Closer); ok {
|
||||
return c.Close()
|
||||
|
@ -160,9 +165,6 @@ func (r *Request) close() error {
|
|||
if c, ok := wt.(io.Closer); ok {
|
||||
return c.Close()
|
||||
}
|
||||
if r.cancelCtx != nil {
|
||||
r.cancelCtx()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -175,8 +177,10 @@ func (r *Request) call(handlers Handlers, pkt requestPacket) responsePacket {
|
|||
return fileput(handlers.FilePut, r, pkt)
|
||||
case "Setstat", "Rename", "Rmdir", "Mkdir", "Symlink", "Remove":
|
||||
return filecmd(handlers.FileCmd, r, pkt)
|
||||
case "List", "Stat", "Readlink":
|
||||
case "List":
|
||||
return filelist(handlers.FileList, r, pkt)
|
||||
case "Stat", "Readlink":
|
||||
return filestat(handlers.FileList, r, pkt)
|
||||
default:
|
||||
return statusFromError(pkt,
|
||||
errors.Errorf("unexpected method: %s", r.Method))
|
||||
|
@ -242,6 +246,12 @@ func fileput(h FileWriter, r *Request, pkt requestPacket) responsePacket {
|
|||
|
||||
// wrap FileCmder handler
|
||||
func filecmd(h FileCmder, r *Request, pkt requestPacket) responsePacket {
|
||||
|
||||
switch p := pkt.(type) {
|
||||
case *sshFxpFsetstatPacket:
|
||||
r.Flags = p.Flags
|
||||
r.Attrs = p.Attrs.([]byte)
|
||||
}
|
||||
err := h.Filecmd(r)
|
||||
return statusFromError(pkt, err)
|
||||
}
|
||||
|
@ -270,7 +280,7 @@ func filelist(h FileLister, r *Request, pkt requestPacket) responsePacket {
|
|||
if err != nil && err != io.EOF {
|
||||
return statusFromError(pkt, err)
|
||||
}
|
||||
if n == 0 {
|
||||
if err == io.EOF && n == 0 {
|
||||
return statusFromError(pkt, io.EOF)
|
||||
}
|
||||
dirname := filepath.ToSlash(path.Base(r.Filepath))
|
||||
|
@ -284,6 +294,22 @@ func filelist(h FileLister, r *Request, pkt requestPacket) responsePacket {
|
|||
})
|
||||
}
|
||||
return ret
|
||||
default:
|
||||
err = errors.Errorf("unexpected method: %s", r.Method)
|
||||
return statusFromError(pkt, err)
|
||||
}
|
||||
}
|
||||
|
||||
func filestat(h FileLister, r *Request, pkt requestPacket) responsePacket {
|
||||
lister, err := h.Filelist(r)
|
||||
if err != nil {
|
||||
return statusFromError(pkt, err)
|
||||
}
|
||||
finfo := make([]os.FileInfo, 1)
|
||||
n, err := lister.ListAt(finfo, 0)
|
||||
finfo = finfo[:n] // avoid need for nil tests below
|
||||
|
||||
switch r.Method {
|
||||
case "Stat":
|
||||
if err != nil && err != io.EOF {
|
||||
return statusFromError(pkt, err)
|
||||
|
@ -330,8 +356,10 @@ func requestMethod(p requestPacket) (method string) {
|
|||
method = "Put"
|
||||
case *sshFxpReaddirPacket:
|
||||
method = "List"
|
||||
case *sshFxpOpenPacket, *sshFxpOpendirPacket:
|
||||
case *sshFxpOpenPacket:
|
||||
method = "Open"
|
||||
case *sshFxpOpendirPacket:
|
||||
method = "Stat"
|
||||
case *sshFxpSetstatPacket, *sshFxpFsetstatPacket:
|
||||
method = "Setstat"
|
||||
case *sshFxpRenamePacket:
|
||||
|
|
17
vendor/github.com/pkg/sftp/request_test.go
generated
vendored
17
vendor/github.com/pkg/sftp/request_test.go
generated
vendored
|
@ -176,8 +176,6 @@ func TestRequestCmdr(t *testing.T) {
|
|||
assert.Equal(t, rpkt, statusFromError(rpkt, errTest))
|
||||
}
|
||||
|
||||
func TestRequestInfoList(t *testing.T) { testInfoMethod(t, "List") }
|
||||
func TestRequestInfoReadlink(t *testing.T) { testInfoMethod(t, "Readlink") }
|
||||
func TestRequestInfoStat(t *testing.T) {
|
||||
handlers := newTestHandlers()
|
||||
request := testRequest("Stat")
|
||||
|
@ -188,6 +186,8 @@ func TestRequestInfoStat(t *testing.T) {
|
|||
assert.Equal(t, spkt.info.Name(), "request_test.go")
|
||||
}
|
||||
|
||||
func TestRequestInfoList(t *testing.T) { testInfoMethod(t, "List") }
|
||||
func TestRequestInfoReadlink(t *testing.T) { testInfoMethod(t, "Readlink") }
|
||||
func testInfoMethod(t *testing.T, method string) {
|
||||
handlers := newTestHandlers()
|
||||
request := testRequest(method)
|
||||
|
@ -198,3 +198,16 @@ func testInfoMethod(t *testing.T, method string) {
|
|||
assert.IsType(t, sshFxpNameAttr{}, npkt.NameAttrs[0])
|
||||
assert.Equal(t, npkt.NameAttrs[0].Name, "request_test.go")
|
||||
}
|
||||
|
||||
func TestOpendirHandleReuse(t *testing.T) {
|
||||
handlers := newTestHandlers()
|
||||
request := testRequest("Stat")
|
||||
pkt := fakePacket{myid: 1}
|
||||
rpkt := request.call(handlers, pkt)
|
||||
assert.IsType(t, &sshFxpStatResponse{}, rpkt)
|
||||
|
||||
request.Method = "List"
|
||||
pkt = fakePacket{myid: 2}
|
||||
rpkt = request.call(handlers, pkt)
|
||||
assert.IsType(t, &sshFxpNamePacket{}, rpkt)
|
||||
}
|
||||
|
|
6
vendor/github.com/pkg/sftp/server.go
generated
vendored
6
vendor/github.com/pkg/sftp/server.go
generated
vendored
|
@ -240,6 +240,12 @@ func handlePacket(s *Server, p interface{}) error {
|
|||
}},
|
||||
})
|
||||
case *sshFxpOpendirPacket:
|
||||
if stat, err := os.Stat(p.Path); err != nil {
|
||||
return s.sendError(p, err)
|
||||
} else if !stat.IsDir() {
|
||||
return s.sendError(p, &os.PathError{
|
||||
Path: p.Path, Err: syscall.ENOTDIR})
|
||||
}
|
||||
return sshFxpOpenPacket{
|
||||
ID: p.ID,
|
||||
Path: p.Path,
|
||||
|
|
19
vendor/github.com/pkg/sftp/server_unix.go
generated
vendored
19
vendor/github.com/pkg/sftp/server_unix.go
generated
vendored
|
@ -6,9 +6,7 @@ package sftp
|
|||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/user"
|
||||
"path"
|
||||
"strconv"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
@ -22,21 +20,10 @@ func runLsStatt(dirent os.FileInfo, statt *syscall.Stat_t) string {
|
|||
typeword := runLsTypeWord(dirent)
|
||||
numLinks := statt.Nlink
|
||||
uid := statt.Uid
|
||||
usr, err := user.LookupId(strconv.Itoa(int(uid)))
|
||||
var username string
|
||||
if err == nil {
|
||||
username = usr.Username
|
||||
} else {
|
||||
username = fmt.Sprintf("%d", uid)
|
||||
}
|
||||
gid := statt.Gid
|
||||
grp, err := user.LookupGroupId(strconv.Itoa(int(gid)))
|
||||
var groupname string
|
||||
if err == nil {
|
||||
groupname = grp.Name
|
||||
} else {
|
||||
groupname = fmt.Sprintf("%d", gid)
|
||||
}
|
||||
username := fmt.Sprintf("%d", uid)
|
||||
groupname := fmt.Sprintf("%d", gid)
|
||||
// TODO FIXME: uid -> username, gid -> groupname lookup for ls -l format output
|
||||
|
||||
mtime := dirent.ModTime()
|
||||
monthStr := mtime.Month().String()[0:3]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue