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)
 }