Update dependencies

This commit is contained in:
Alexander Neumann
2018-09-03 20:23:56 +02:00
parent 0922367308
commit 96f7be5d9b
185 changed files with 20976 additions and 2150 deletions
+1 -1
View File
@@ -1,4 +1,4 @@
// +build darwin dragonfly freebsd !android,linux netbsd openbsd solaris
// +build darwin dragonfly freebsd !android,linux netbsd openbsd solaris aix
// +build cgo
package sftp
+99 -71
View File
@@ -7,30 +7,30 @@ import (
)
// The goal of the packetManager is to keep the outgoing packets in the same
// order as the incoming. This is due to some sftp clients requiring this
// behavior (eg. winscp).
// order as the incoming as is requires by section 7 of the RFC.
type packetManager struct {
requests chan orderedPacket
responses chan orderedPacket
fini chan struct{}
incoming orderedPackets
outgoing orderedPackets
sender packetSender // connection object
working *sync.WaitGroup
packetCount uint32
}
type packetSender interface {
sendPacket(encoding.BinaryMarshaler) error
}
type packetManager struct {
requests chan requestPacket
responses chan responsePacket
fini chan struct{}
incoming requestPacketIDs
outgoing responsePackets
sender packetSender // connection object
working *sync.WaitGroup
}
func newPktMgr(sender packetSender) *packetManager {
s := &packetManager{
requests: make(chan requestPacket, SftpServerWorkerCount),
responses: make(chan responsePacket, SftpServerWorkerCount),
requests: make(chan orderedPacket, SftpServerWorkerCount),
responses: make(chan orderedPacket, SftpServerWorkerCount),
fini: make(chan struct{}),
incoming: make([]uint32, 0, SftpServerWorkerCount),
outgoing: make([]responsePacket, 0, SftpServerWorkerCount),
incoming: make([]orderedPacket, 0, SftpServerWorkerCount),
outgoing: make([]orderedPacket, 0, SftpServerWorkerCount),
sender: sender,
working: &sync.WaitGroup{},
}
@@ -38,31 +38,56 @@ func newPktMgr(sender packetSender) *packetManager {
return s
}
type responsePackets []responsePacket
//// packet ordering
func (s *packetManager) newOrderId() uint32 {
s.packetCount++
return s.packetCount
}
func (r responsePackets) Sort() {
sort.Slice(r, func(i, j int) bool {
return r[i].id() < r[j].id()
})
}
type requestPacketIDs []uint32
func (r requestPacketIDs) Sort() {
sort.Slice(r, func(i, j int) bool {
return r[i] < r[j]
type orderedRequest struct {
requestPacket
orderid uint32
}
func (s *packetManager) newOrderedRequest(p requestPacket) orderedRequest {
return orderedRequest{requestPacket: p, orderid: s.newOrderId()}
}
func (p orderedRequest) orderId() uint32 { return p.orderid }
func (p orderedRequest) setOrderId(oid uint32) { p.orderid = oid }
type orderedResponse struct {
responsePacket
orderid uint32
}
func (s *packetManager) newOrderedResponse(p responsePacket, id uint32,
) orderedResponse {
return orderedResponse{responsePacket: p, orderid: id}
}
func (p orderedResponse) orderId() uint32 { return p.orderid }
func (p orderedResponse) setOrderId(oid uint32) { p.orderid = oid }
type orderedPacket interface {
id() uint32
orderId() uint32
}
type orderedPackets []orderedPacket
func (o orderedPackets) Sort() {
sort.Slice(o, func(i, j int) bool {
return o[i].orderId() < o[j].orderId()
})
}
//// packet registry
// register incoming packets to be handled
// send id of 0 for packets without id
func (s *packetManager) incomingPacket(pkt requestPacket) {
func (s *packetManager) incomingPacket(pkt orderedRequest) {
s.working.Add(1)
s.requests <- pkt // buffer == SftpServerWorkerCount
s.requests <- pkt
}
// register outgoing packets as being ready
func (s *packetManager) readyPacket(pkt responsePacket) {
func (s *packetManager) readyPacket(pkt orderedResponse) {
s.responses <- pkt
s.working.Done()
}
@@ -75,38 +100,37 @@ func (s *packetManager) close() {
}
// Passed a worker function, returns a channel for incoming packets.
// The goal is to process packets in the order they are received as is
// requires by section 7 of the RFC, while maximizing throughput of file
// transfers.
func (s *packetManager) workerChan(runWorker func(requestChan)) requestChan {
// Keep process packet responses in the order they are received while
// maximizing throughput of file transfers.
func (s *packetManager) workerChan(runWorker func(chan orderedRequest),
) chan orderedRequest {
rwChan := make(chan requestPacket, SftpServerWorkerCount)
// multiple workers for faster read/writes
rwChan := make(chan orderedRequest, SftpServerWorkerCount)
for i := 0; i < SftpServerWorkerCount; i++ {
runWorker(rwChan)
}
cmdChan := make(chan requestPacket)
// single worker to enforce sequential processing of everything else
cmdChan := make(chan orderedRequest)
runWorker(cmdChan)
pktChan := make(chan requestPacket, SftpServerWorkerCount)
pktChan := make(chan orderedRequest, SftpServerWorkerCount)
go func() {
// start with cmdChan
curChan := cmdChan
for pkt := range pktChan {
// on file open packet, switch to rwChan
switch pkt.(type) {
case *sshFxpOpenPacket:
curChan = rwChan
// on file close packet, switch back to cmdChan
// after waiting for any reads/writes to finish
switch pkt.requestPacket.(type) {
case *sshFxpReadPacket, *sshFxpWritePacket:
s.incomingPacket(pkt)
rwChan <- pkt
continue
case *sshFxpClosePacket:
// wait for rwChan to finish
// wait for reads/writes to finish when file is closed
// incomingPacket() call must occur after this
s.working.Wait()
// stop using rwChan
curChan = cmdChan
}
s.incomingPacket(pkt)
curChan <- pkt
// all non-RW use sequential cmdChan
cmdChan <- pkt
}
close(rwChan)
close(cmdChan)
@@ -121,17 +145,13 @@ func (s *packetManager) controller() {
for {
select {
case pkt := <-s.requests:
debug("incoming id: %v", pkt.id())
s.incoming = append(s.incoming, pkt.id())
if len(s.incoming) > 1 {
s.incoming.Sort()
}
debug("incoming id (oid): %v (%v)", pkt.id(), pkt.orderId())
s.incoming = append(s.incoming, pkt)
s.incoming.Sort()
case pkt := <-s.responses:
debug("outgoing pkt: %v", pkt.id())
debug("outgoing id (oid): %v (%v)", pkt.id(), pkt.orderId())
s.outgoing = append(s.outgoing, pkt)
if len(s.outgoing) > 1 {
s.outgoing.Sort()
}
s.outgoing.Sort()
case <-s.fini:
return
}
@@ -149,10 +169,11 @@ func (s *packetManager) maybeSendPackets() {
}
out := s.outgoing[0]
in := s.incoming[0]
// debug("incoming: %v", s.incoming)
// debug("outgoing: %v", outfilter(s.outgoing))
if in == out.id() {
s.sender.sendPacket(out)
// debug("incoming: %v", ids(s.incoming))
// debug("outgoing: %v", ids(s.outgoing))
if in.orderId() == out.orderId() {
debug("Sending packet: %v", out.id())
s.sender.sendPacket(out.(encoding.BinaryMarshaler))
// pop off heads
copy(s.incoming, s.incoming[1:]) // shift left
s.incoming = s.incoming[:len(s.incoming)-1] // remove last
@@ -164,10 +185,17 @@ func (s *packetManager) maybeSendPackets() {
}
}
//func outfilter(o []responsePacket) []uint32 {
// res := make([]uint32, 0, len(o))
// for _, v := range o {
// res = append(res, v.id())
// }
// return res
//}
// func oids(o []orderedPacket) []uint32 {
// res := make([]uint32, 0, len(o))
// for _, v := range o {
// res = append(res, v.orderId())
// }
// return res
// }
// func ids(o []orderedPacket) []uint32 {
// res := make([]uint32, 0, len(o))
// for _, v := range o {
// res = append(res, v.id())
// }
// return res
// }
+1 -2
View File
@@ -12,8 +12,6 @@ type requestPacket interface {
id() uint32
}
type requestChan chan requestPacket
type responsePacket interface {
encoding.BinaryMarshaler
id() uint32
@@ -77,6 +75,7 @@ func (p sshFxpStatusPacket) id() uint32 { return p.ID }
func (p sshFxpStatResponse) id() uint32 { return p.ID }
func (p sshFxpNamePacket) id() uint32 { return p.ID }
func (p sshFxpHandlePacket) id() uint32 { return p.ID }
func (p StatVFS) id() uint32 { return p.ID }
func (p sshFxVersionPacket) id() uint32 { return 0 }
// take raw incoming packet data and build packet objects
+4 -4
View File
@@ -882,9 +882,9 @@ func (p sshFxpExtendedPacket) readonly() bool {
return p.SpecificPacket.readonly()
}
func (p sshFxpExtendedPacket) respond(svr *Server) error {
func (p sshFxpExtendedPacket) respond(svr *Server) responsePacket {
if p.SpecificPacket == nil {
return nil
return statusFromError(p, nil)
}
return p.SpecificPacket.respond(svr)
}
@@ -954,7 +954,7 @@ func (p *sshFxpExtendedPacketPosixRename) UnmarshalBinary(b []byte) error {
return nil
}
func (p sshFxpExtendedPacketPosixRename) respond(s *Server) error {
func (p sshFxpExtendedPacketPosixRename) respond(s *Server) responsePacket {
err := os.Rename(p.Oldpath, p.Newpath)
return s.sendError(p, err)
return statusFromError(p, err)
}
+1
View File
@@ -83,6 +83,7 @@ func (fs *root) Filecmd(r *Request) error {
return &os.LinkError{Op: "rename", Old: r.Filepath, New: r.Target,
Err: fmt.Errorf("dest file exists")}
}
file.name = r.Target
fs.files[r.Target] = file
delete(fs.files, r.Filepath)
case "Rmdir", "Remove":
+9 -22
View File
@@ -2,7 +2,6 @@ package sftp
import (
"context"
"encoding"
"io"
"os"
"path"
@@ -106,7 +105,7 @@ func (rs *RequestServer) Serve() error {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
var wg sync.WaitGroup
runWorker := func(ch requestChan) {
runWorker := func(ch chan orderedRequest) {
wg.Add(1)
go func() {
defer wg.Done()
@@ -143,7 +142,7 @@ func (rs *RequestServer) Serve() error {
}
}
pktChan <- pkt
pktChan <- rs.pktMgr.newOrderedRequest(pkt)
}
close(pktChan) // shuts down sftpServerWorkers
@@ -160,13 +159,13 @@ func (rs *RequestServer) Serve() error {
}
func (rs *RequestServer) packetWorker(
ctx context.Context, pktChan chan requestPacket,
ctx context.Context, pktChan chan orderedRequest,
) error {
for pkt := range pktChan {
var rpkt responsePacket
switch pkt := pkt.(type) {
switch pkt := pkt.requestPacket.(type) {
case *sshFxInitPacket:
rpkt = sshFxVersionPacket{sftpProtocolVersion, nil}
rpkt = sshFxVersionPacket{Version: sftpProtocolVersion}
case *sshFxpClosePacket:
handle := pkt.getHandle()
rpkt = statusFromError(pkt, rs.closeRequest(handle))
@@ -178,7 +177,7 @@ func (rs *RequestServer) packetWorker(
if stat, ok := rpkt.(*sshFxpStatResponse); ok {
if stat.info.IsDir() {
handle := rs.nextRequest(request)
rpkt = sshFxpHandlePacket{pkt.id(), handle}
rpkt = sshFxpHandlePacket{ID: pkt.id(), Handle: handle}
} else {
rpkt = statusFromError(pkt, &os.PathError{
Path: request.Filepath, Err: syscall.ENOTDIR})
@@ -187,7 +186,7 @@ func (rs *RequestServer) packetWorker(
case *sshFxpOpenPacket:
request := requestFromPacket(ctx, pkt)
handle := rs.nextRequest(request)
rpkt = sshFxpHandlePacket{pkt.id(), handle}
rpkt = sshFxpHandlePacket{ID: pkt.id(), Handle: handle}
if pkt.hasPflags(ssh_FXF_CREAT) {
if p := request.call(rs.Handlers, pkt); !statusOk(p) {
rpkt = p // if error in write, return it
@@ -209,10 +208,8 @@ func (rs *RequestServer) packetWorker(
return errors.Errorf("unexpected packet type %T", pkt)
}
err := rs.sendPacket(rpkt)
if err != nil {
return err
}
rs.pktMgr.readyPacket(
rs.pktMgr.newOrderedResponse(rpkt, pkt.orderId()))
}
return nil
}
@@ -244,13 +241,3 @@ func cleanPath(p string) string {
}
return path.Clean(p)
}
// Wrap underlying connection methods to use packetManager
func (rs *RequestServer) sendPacket(m encoding.BinaryMarshaler) error {
if pkt, ok := m.(responsePacket); ok {
rs.pktMgr.readyPacket(pkt)
} else {
return errors.Errorf("unexpected packet type %T", m)
}
return nil
}
+92 -103
View File
@@ -66,7 +66,7 @@ func (svr *Server) getHandle(handle string) (*os.File, bool) {
type serverRespondablePacket interface {
encoding.BinaryUnmarshaler
id() uint32
respond(svr *Server) error
respond(svr *Server) responsePacket
}
// NewServer creates a new Server instance around the provided streams, serving
@@ -123,12 +123,11 @@ type rxPacket struct {
}
// Up to N parallel servers
func (svr *Server) sftpServerWorker(pktChan chan requestPacket) error {
func (svr *Server) sftpServerWorker(pktChan chan orderedRequest) error {
for pkt := range pktChan {
// readonly checks
readonly := true
switch pkt := pkt.(type) {
switch pkt := pkt.requestPacket.(type) {
case notReadOnly:
readonly = false
case *sshFxpOpenPacket:
@@ -140,9 +139,9 @@ func (svr *Server) sftpServerWorker(pktChan chan requestPacket) error {
// If server is operating read-only and a write operation is requested,
// return permission denied
if !readonly && svr.readOnly {
if err := svr.sendError(pkt, syscall.EPERM); err != nil {
return errors.Wrap(err, "failed to send read only packet response")
}
svr.sendPacket(orderedResponse{
responsePacket: statusFromError(pkt, syscall.EPERM),
orderid: pkt.orderId()})
continue
}
@@ -153,141 +152,145 @@ func (svr *Server) sftpServerWorker(pktChan chan requestPacket) error {
return nil
}
func handlePacket(s *Server, p interface{}) error {
switch p := p.(type) {
func handlePacket(s *Server, p orderedRequest) error {
var rpkt responsePacket
switch p := p.requestPacket.(type) {
case *sshFxInitPacket:
return s.sendPacket(sshFxVersionPacket{sftpProtocolVersion, nil})
rpkt = sshFxVersionPacket{Version: sftpProtocolVersion}
case *sshFxpStatPacket:
// stat the requested file
info, err := os.Stat(p.Path)
if err != nil {
return s.sendError(p, err)
}
return s.sendPacket(sshFxpStatResponse{
rpkt = sshFxpStatResponse{
ID: p.ID,
info: info,
})
}
if err != nil {
rpkt = statusFromError(p, err)
}
case *sshFxpLstatPacket:
// stat the requested file
info, err := os.Lstat(p.Path)
if err != nil {
return s.sendError(p, err)
}
return s.sendPacket(sshFxpStatResponse{
rpkt = sshFxpStatResponse{
ID: p.ID,
info: info,
})
}
if err != nil {
rpkt = statusFromError(p, err)
}
case *sshFxpFstatPacket:
f, ok := s.getHandle(p.Handle)
if !ok {
return s.sendError(p, syscall.EBADF)
var err error = syscall.EBADF
var info os.FileInfo
if ok {
info, err = f.Stat()
rpkt = sshFxpStatResponse{
ID: p.ID,
info: info,
}
}
info, err := f.Stat()
if err != nil {
return s.sendError(p, err)
rpkt = statusFromError(p, err)
}
return s.sendPacket(sshFxpStatResponse{
ID: p.ID,
info: info,
})
case *sshFxpMkdirPacket:
// TODO FIXME: ignore flags field
err := os.Mkdir(p.Path, 0755)
return s.sendError(p, err)
rpkt = statusFromError(p, err)
case *sshFxpRmdirPacket:
err := os.Remove(p.Path)
return s.sendError(p, err)
rpkt = statusFromError(p, err)
case *sshFxpRemovePacket:
err := os.Remove(p.Filename)
return s.sendError(p, err)
rpkt = statusFromError(p, err)
case *sshFxpRenamePacket:
err := os.Rename(p.Oldpath, p.Newpath)
return s.sendError(p, err)
rpkt = statusFromError(p, err)
case *sshFxpSymlinkPacket:
err := os.Symlink(p.Targetpath, p.Linkpath)
return s.sendError(p, err)
rpkt = statusFromError(p, err)
case *sshFxpClosePacket:
return s.sendError(p, s.closeHandle(p.Handle))
rpkt = statusFromError(p, s.closeHandle(p.Handle))
case *sshFxpReadlinkPacket:
f, err := os.Readlink(p.Path)
if err != nil {
return s.sendError(p, err)
}
return s.sendPacket(sshFxpNamePacket{
rpkt = sshFxpNamePacket{
ID: p.ID,
NameAttrs: []sshFxpNameAttr{{
Name: f,
LongName: f,
Attrs: emptyFileStat,
}},
})
}
if err != nil {
rpkt = statusFromError(p, err)
}
case *sshFxpRealpathPacket:
f, err := filepath.Abs(p.Path)
if err != nil {
return s.sendError(p, err)
}
f = cleanPath(f)
return s.sendPacket(sshFxpNamePacket{
rpkt = sshFxpNamePacket{
ID: p.ID,
NameAttrs: []sshFxpNameAttr{{
Name: f,
LongName: f,
Attrs: emptyFileStat,
}},
})
}
if err != nil {
rpkt = statusFromError(p, err)
}
case *sshFxpOpendirPacket:
if stat, err := os.Stat(p.Path); err != nil {
return s.sendError(p, err)
rpkt = statusFromError(p, err)
} else if !stat.IsDir() {
return s.sendError(p, &os.PathError{
rpkt = statusFromError(p, &os.PathError{
Path: p.Path, Err: syscall.ENOTDIR})
} else {
rpkt = sshFxpOpenPacket{
ID: p.ID,
Path: p.Path,
Pflags: ssh_FXF_READ,
}.respond(s)
}
return sshFxpOpenPacket{
ID: p.ID,
Path: p.Path,
Pflags: ssh_FXF_READ,
}.respond(s)
case *sshFxpReadPacket:
var err error = syscall.EBADF
f, ok := s.getHandle(p.Handle)
if !ok {
return s.sendError(p, syscall.EBADF)
if ok {
err = nil
data := make([]byte, clamp(p.Len, s.maxTxPacket))
n, _err := f.ReadAt(data, int64(p.Offset))
if _err != nil && (_err != io.EOF || n == 0) {
err = _err
}
rpkt = sshFxpDataPacket{
ID: p.ID,
Length: uint32(n),
Data: data[:n],
}
}
if err != nil {
rpkt = statusFromError(p, err)
}
data := make([]byte, clamp(p.Len, s.maxTxPacket))
n, err := f.ReadAt(data, int64(p.Offset))
if err != nil && (err != io.EOF || n == 0) {
return s.sendError(p, err)
}
return s.sendPacket(sshFxpDataPacket{
ID: p.ID,
Length: uint32(n),
Data: data[:n],
})
case *sshFxpWritePacket:
f, ok := s.getHandle(p.Handle)
if !ok {
return s.sendError(p, syscall.EBADF)
var err error = syscall.EBADF
if ok {
_, err = f.WriteAt(p.Data, int64(p.Offset))
}
_, err := f.WriteAt(p.Data, int64(p.Offset))
return s.sendError(p, err)
rpkt = statusFromError(p, err)
case serverRespondablePacket:
err := p.respond(s)
return errors.Wrap(err, "pkt.respond failed")
rpkt = p.respond(s)
default:
return errors.Errorf("unexpected packet type %T", p)
}
s.pktMgr.readyPacket(s.pktMgr.newOrderedResponse(rpkt, p.orderId()))
return nil
}
// Serve serves SFTP connections until the streams stop or the SFTP subsystem
// is stopped.
func (svr *Server) Serve() error {
var wg sync.WaitGroup
runWorker := func(ch requestChan) {
runWorker := func(ch chan orderedRequest) {
wg.Add(1)
go func() {
defer wg.Done()
@@ -324,7 +327,7 @@ func (svr *Server) Serve() error {
}
}
pktChan <- pkt
pktChan <- svr.pktMgr.newOrderedRequest(pkt)
}
close(pktChan) // shuts down sftpServerWorkers
@@ -338,20 +341,6 @@ func (svr *Server) Serve() error {
return err // error from recvPacket
}
// Wrap underlying connection methods to use packetManager
func (svr *Server) sendPacket(m encoding.BinaryMarshaler) error {
if pkt, ok := m.(responsePacket); ok {
svr.pktMgr.readyPacket(pkt)
} else {
return errors.Errorf("unexpected packet type %T", m)
}
return nil
}
func (svr *Server) sendError(p ider, err error) error {
return svr.sendPacket(statusFromError(p, err))
}
type ider interface {
id() uint32
}
@@ -386,7 +375,7 @@ func (p sshFxpOpenPacket) hasPflags(flags ...uint32) bool {
return true
}
func (p sshFxpOpenPacket) respond(svr *Server) error {
func (p sshFxpOpenPacket) respond(svr *Server) responsePacket {
var osFlags int
if p.hasPflags(ssh_FXF_READ, ssh_FXF_WRITE) {
osFlags |= os.O_RDWR
@@ -396,7 +385,7 @@ func (p sshFxpOpenPacket) respond(svr *Server) error {
osFlags |= os.O_RDONLY
} else {
// how are they opening?
return svr.sendError(p, syscall.EINVAL)
return statusFromError(p, syscall.EINVAL)
}
if p.hasPflags(ssh_FXF_APPEND) {
@@ -414,23 +403,23 @@ func (p sshFxpOpenPacket) respond(svr *Server) error {
f, err := os.OpenFile(p.Path, osFlags, 0644)
if err != nil {
return svr.sendError(p, err)
return statusFromError(p, err)
}
handle := svr.nextHandle(f)
return svr.sendPacket(sshFxpHandlePacket{p.ID, handle})
return sshFxpHandlePacket{ID: p.id(), Handle: handle}
}
func (p sshFxpReaddirPacket) respond(svr *Server) error {
func (p sshFxpReaddirPacket) respond(svr *Server) responsePacket {
f, ok := svr.getHandle(p.Handle)
if !ok {
return svr.sendError(p, syscall.EBADF)
return statusFromError(p, syscall.EBADF)
}
dirname := f.Name()
dirents, err := f.Readdir(128)
if err != nil {
return svr.sendError(p, err)
return statusFromError(p, err)
}
ret := sshFxpNamePacket{ID: p.ID}
@@ -441,10 +430,10 @@ func (p sshFxpReaddirPacket) respond(svr *Server) error {
Attrs: []interface{}{dirent},
})
}
return svr.sendPacket(ret)
return ret
}
func (p sshFxpSetstatPacket) respond(svr *Server) error {
func (p sshFxpSetstatPacket) respond(svr *Server) responsePacket {
// additional unmarshalling is required for each possibility here
b := p.Attrs.([]byte)
var err error
@@ -483,13 +472,13 @@ func (p sshFxpSetstatPacket) respond(svr *Server) error {
}
}
return svr.sendError(p, err)
return statusFromError(p, err)
}
func (p sshFxpFsetstatPacket) respond(svr *Server) error {
func (p sshFxpFsetstatPacket) respond(svr *Server) responsePacket {
f, ok := svr.getHandle(p.Handle)
if !ok {
return svr.sendError(p, syscall.EBADF)
return statusFromError(p, syscall.EBADF)
}
// additional unmarshalling is required for each possibility here
@@ -530,7 +519,7 @@ func (p sshFxpFsetstatPacket) respond(svr *Server) error {
}
}
return svr.sendError(p, err)
return statusFromError(p, err)
}
// translateErrno translates a syscall error number to a SFTP error code.
+4 -4
View File
@@ -9,17 +9,17 @@ import (
"syscall"
)
func (p sshFxpExtendedPacketStatVFS) respond(svr *Server) error {
func (p sshFxpExtendedPacketStatVFS) respond(svr *Server) responsePacket {
stat := &syscall.Statfs_t{}
if err := syscall.Statfs(p.Path, stat); err != nil {
return svr.sendPacket(statusFromError(p, err))
return statusFromError(p, err)
}
retPkt, err := statvfsFromStatfst(stat)
if err != nil {
return svr.sendPacket(statusFromError(p, err))
return statusFromError(p, err)
}
retPkt.ID = p.ID
return svr.sendPacket(retPkt)
return retPkt
}
+2 -2
View File
@@ -6,6 +6,6 @@ import (
"syscall"
)
func (p sshFxpExtendedPacketStatVFS) respond(svr *Server) error {
return syscall.ENOTSUP
func (p sshFxpExtendedPacketStatVFS) respond(svr *Server) responsePacket {
return statusFromError(p, syscall.ENOTSUP)
}
+1 -1
View File
@@ -1,4 +1,4 @@
// +build darwin dragonfly freebsd !android,linux netbsd openbsd solaris
// +build darwin dragonfly freebsd !android,linux netbsd openbsd solaris aix
// +build cgo
package sftp