Merge pull request #262 from ghafoors/aix_compatibility
added build tags required for gcc-go on AIX
diff --git a/packet-manager.go b/packet-manager.go
index bf822e6..2c1c656 100644
--- a/packet-manager.go
+++ b/packet-manager.go
@@ -78,7 +78,8 @@
// 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 {
+func (s *packetManager) workerChan(runWorker func(chan requestPacket),
+) chan requestPacket {
rwChan := make(chan requestPacket, SftpServerWorkerCount)
for i := 0; i < SftpServerWorkerCount; i++ {
diff --git a/packet-typing.go b/packet-typing.go
index 2466472..bcff9bf 100644
--- a/packet-typing.go
+++ b/packet-typing.go
@@ -12,8 +12,6 @@
id() uint32
}
-type requestChan chan requestPacket
-
type responsePacket interface {
encoding.BinaryMarshaler
id() uint32
@@ -77,6 +75,7 @@
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
diff --git a/packet.go b/packet.go
index c6c26d8..4eec06d 100644
--- a/packet.go
+++ b/packet.go
@@ -882,9 +882,9 @@
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 @@
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)
}
diff --git a/request-server.go b/request-server.go
index 604b20d..e589672 100644
--- a/request-server.go
+++ b/request-server.go
@@ -2,7 +2,6 @@
import (
"context"
- "encoding"
"io"
"os"
"path"
@@ -106,7 +105,7 @@
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
var wg sync.WaitGroup
- runWorker := func(ch requestChan) {
+ runWorker := func(ch chan requestPacket) {
wg.Add(1)
go func() {
defer wg.Done()
@@ -166,7 +165,7 @@
var rpkt responsePacket
switch pkt := pkt.(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 @@
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 @@
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,7 @@
return errors.Errorf("unexpected packet type %T", pkt)
}
- err := rs.sendPacket(rpkt)
- if err != nil {
- return err
- }
+ rs.sendPacket(rpkt)
}
return nil
}
@@ -246,11 +242,6 @@
}
// 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
+func (rs *RequestServer) sendPacket(pkt responsePacket) {
+ rs.pktMgr.readyPacket(pkt)
}
diff --git a/request_test.go b/request_test.go
index 9670401..fd471d7 100644
--- a/request_test.go
+++ b/request_test.go
@@ -134,7 +134,8 @@
request := testRequest("Get")
// req.length is 5, so we test reads in 5 byte chunks
for i, txt := range []string{"file-", "data."} {
- pkt := &sshFxpReadPacket{uint32(i), "a", uint64(i * 5), 5}
+ pkt := &sshFxpReadPacket{ID: uint32(i), Handle: "a",
+ Offset: uint64(i * 5), Len: 5}
rpkt := request.call(handlers, pkt)
dpkt := rpkt.(*sshFxpDataPacket)
assert.Equal(t, dpkt.id(), uint32(i))
@@ -155,10 +156,12 @@
func TestRequestPut(t *testing.T) {
handlers := newTestHandlers()
request := testRequest("Put")
- pkt := &sshFxpWritePacket{0, "a", 0, 5, []byte("file-")}
+ pkt := &sshFxpWritePacket{ID: 0, Handle: "a", Offset: 0, Length: 5,
+ Data: []byte("file-")}
rpkt := request.call(handlers, pkt)
checkOkStatus(t, rpkt)
- pkt = &sshFxpWritePacket{1, "a", 5, 5, []byte("data.")}
+ pkt = &sshFxpWritePacket{ID: 1, Handle: "a", Offset: 5, Length: 5,
+ Data: []byte("data.")}
rpkt = request.call(handlers, pkt)
checkOkStatus(t, rpkt)
assert.Equal(t, "file-data.", handlers.getOutString())
diff --git a/server.go b/server.go
index aaa1c9e..a5dd2cd 100644
--- a/server.go
+++ b/server.go
@@ -66,7 +66,7 @@
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
@@ -140,9 +140,7 @@
// 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(statusFromError(pkt, syscall.EPERM))
continue
}
@@ -153,141 +151,146 @@
return nil
}
-func handlePacket(s *Server, p interface{}) error {
+func handlePacket(s *Server, p requestPacket) error {
+ var rpkt responsePacket
switch p := p.(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:
+ fmt.Println("fstat")
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.sendPacket(rpkt)
+ 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 requestPacket) {
wg.Add(1)
go func() {
defer wg.Done()
@@ -339,19 +342,11 @@
}
// 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)
- }
+func (svr *Server) sendPacket(pkt responsePacket) error {
+ svr.pktMgr.readyPacket(pkt)
return nil
}
-func (svr *Server) sendError(p ider, err error) error {
- return svr.sendPacket(statusFromError(p, err))
-}
-
type ider interface {
id() uint32
}
@@ -386,7 +381,7 @@
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 +391,7 @@
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 +409,23 @@
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 +436,10 @@
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 +478,13 @@
}
}
- 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 +525,7 @@
}
}
- return svr.sendError(p, err)
+ return statusFromError(p, err)
}
// translateErrno translates a syscall error number to a SFTP error code.
diff --git a/server_statvfs_impl.go b/server_statvfs_impl.go
index c37a34a..4cf91dc 100644
--- a/server_statvfs_impl.go
+++ b/server_statvfs_impl.go
@@ -9,17 +9,17 @@
"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
}
diff --git a/server_statvfs_stubs.go b/server_statvfs_stubs.go
index 3fe4078..c6f6164 100644
--- a/server_statvfs_stubs.go
+++ b/server_statvfs_stubs.go
@@ -6,6 +6,6 @@
"syscall"
)
-func (p sshFxpExtendedPacketStatVFS) respond(svr *Server) error {
- return syscall.ENOTSUP
+func (p sshFxpExtendedPacketStatVFS) respond(svr *Server) responsePacket {
+ return statusFromError(p, syscall.ENOTSUP)
}