readdir, rename, remove

This commit is contained in:
Mark Sheahan 2015-07-31 15:46:13 -07:00
parent 7ab0966023
commit 435f753792
3 changed files with 183 additions and 21 deletions

View File

@ -404,7 +404,7 @@ func TestClientRename(t *testing.T) {
}
}
func TestClientReadLine(t *testing.T) {
func TestClientReadLink(t *testing.T) {
sftp, cmd := testClient(t, READWRITE)
defer cmd.Wait()
defer sftp.Close()

View File

@ -4,6 +4,7 @@ import (
"encoding"
"fmt"
"io"
"os"
"reflect"
)
@ -22,6 +23,9 @@ func marshalString(b []byte, v string) []byte {
}
func marshal(b []byte, v interface{}) []byte {
if v == nil {
return b
}
switch v := v.(type) {
case uint8:
return append(b, v)
@ -31,6 +35,8 @@ func marshal(b []byte, v interface{}) []byte {
return marshalUint64(b, v)
case string:
return marshalString(b, v)
case os.FileInfo:
return marshalFileInfo(b, v)
default:
switch d := reflect.ValueOf(v); d.Kind() {
case reflect.Struct:
@ -245,6 +251,10 @@ func (p sshFxpReaddirPacket) MarshalBinary() ([]byte, error) {
return marshalIdString(ssh_FXP_READDIR, p.Id, p.Handle)
}
func (p *sshFxpReaddirPacket) UnmarshalBinary(b []byte) error {
return unmarshalIdString(b, &p.Id, &p.Handle)
}
type sshFxpOpendirPacket struct {
Id uint32
Path string
@ -254,6 +264,10 @@ func (p sshFxpOpendirPacket) MarshalBinary() ([]byte, error) {
return marshalIdString(ssh_FXP_OPENDIR, p.Id, p.Path)
}
func (p *sshFxpOpendirPacket) UnmarshalBinary(b []byte) error {
return unmarshalIdString(b, &p.Id, &p.Path)
}
type sshFxpLstatPacket struct {
Id uint32
Path string
@ -302,6 +316,10 @@ func (p sshFxpRemovePacket) MarshalBinary() ([]byte, error) {
return marshalIdString(ssh_FXP_REMOVE, p.Id, p.Filename)
}
func (p *sshFxpRemovePacket) UnmarshalBinary(b []byte) error {
return unmarshalIdString(b, &p.Id, &p.Filename)
}
type sshFxpRmdirPacket struct {
Id uint32
Path string
@ -311,6 +329,10 @@ func (p sshFxpRmdirPacket) MarshalBinary() ([]byte, error) {
return marshalIdString(ssh_FXP_RMDIR, p.Id, p.Path)
}
func (p *sshFxpRmdirPacket) UnmarshalBinary(b []byte) error {
return unmarshalIdString(b, &p.Id, &p.Path)
}
type sshFxpReadlinkPacket struct {
Id uint32
Path string
@ -320,6 +342,42 @@ func (p sshFxpReadlinkPacket) MarshalBinary() ([]byte, error) {
return marshalIdString(ssh_FXP_READLINK, p.Id, p.Path)
}
func (p *sshFxpReadlinkPacket) UnmarshalBinary(b []byte) error {
return unmarshalIdString(b, &p.Id, &p.Path)
}
type sshFxpNameAttr struct {
Name string
Attrs interface{}
}
func (p sshFxpNameAttr) MarshalBinary() ([]byte, error) {
b := []byte{}
b = marshalString(b, p.Name)
b = marshal(b, p.Attrs)
return b, nil
}
type sshFxpNamePacket struct {
Id uint32
NameAttrs []sshFxpNameAttr
}
func (p sshFxpNamePacket) MarshalBinary() ([]byte, error) {
b := []byte{}
b = append(b, ssh_FXP_NAME)
b = marshalUint32(b, p.Id)
b = marshalUint32(b, uint32(len(p.NameAttrs)))
for _, na := range p.NameAttrs {
if ab, err := na.MarshalBinary(); err != nil {
return nil, err
} else {
b = append(b, ab...)
}
}
return b, nil
}
type sshFxpOpenPacket struct {
Id uint32
Path string
@ -407,6 +465,17 @@ func (p sshFxpRenamePacket) MarshalBinary() ([]byte, error) {
return b, nil
}
func (p *sshFxpRenamePacket) UnmarshalBinary(b []byte) (err error) {
if p.Id, b, err = unmarshalUint32Safe(b); err != nil {
return
} else if p.Oldpath, b, err = unmarshalStringSafe(b); err != nil {
return
} else if p.Newpath, b, err = unmarshalStringSafe(b); err != nil {
return
}
return
}
type sshFxpWritePacket struct {
Id uint32
Handle string

133
server.go
View File

@ -12,18 +12,23 @@ import (
)
type FileSystem interface {
Lstat(p string) (os.FileInfo, error)
Lstat(name string) (os.FileInfo, error)
Remove(name string) error
Rename(oldpath, newpath string) error
}
type FileSystemOS interface {
FileSystem
OpenFile(name string, flag int, perm os.FileMode) (file *os.File, err error)
Readlink(path string) (string, error)
Mkdir(name string, perm os.FileMode) error
}
type FileSystemOpen interface {
FileSystem
OpenFile(name string, flag int, perm os.FileMode) (file *os.File, err error)
}
type FileSystemSFTPOpen interface {
type FileSystemSFTP interface {
FileSystem
OpenFile(path string, f int) (*File, error) // sftp package has a strange OpenFile method with no perm
ReadLink(path string) (string, error)
Mkdir(name string) error
}
// common subset of os.File and sftp.File
@ -43,12 +48,18 @@ type svrFile interface {
type nativeFs struct {
}
func (nfs *nativeFs) Lstat(p string) (os.FileInfo, error) { return os.Lstat(p) }
func (nfs *nativeFs) Mkdir(name string, perm os.FileMode) error { return os.Mkdir(name, perm) }
func (nfs *nativeFs) OpenFile(name string, flag int, perm os.FileMode) (file *os.File, err error) {
return os.OpenFile(name, flag, perm)
func (nfs *nativeFs) Lstat(path string) (os.FileInfo, error) { return os.Lstat(path) }
func (nfs *nativeFs) Mkdir(path string, perm os.FileMode) error { return os.Mkdir(path, perm) }
func (nfs *nativeFs) Remove(path string) error { return os.Remove(path) }
func (nfs *nativeFs) Rename(oldpath, newpath string) error { return os.Rename(oldpath, newpath) }
func (nfs *nativeFs) Readlink(path string) (string, error) { return os.Readlink(path) }
func (nfs *nativeFs) OpenFile(path string, flag int, perm os.FileMode) (file *os.File, err error) {
return os.OpenFile(path, flag, perm)
}
var __typecheck_fsos FileSystemOS = &nativeFs{}
var __typecheck_sftpos FileSystemSFTP = &Client{}
type Server struct {
in io.Reader
out io.Writer
@ -164,15 +175,20 @@ func (svr *Server) decodePacket(pktType fxp, pktBytes []byte) (serverRespondable
case ssh_FXP_SETSTAT:
case ssh_FXP_FSETSTAT:
case ssh_FXP_OPENDIR:
pkt = &sshFxpOpendirPacket{}
case ssh_FXP_READDIR:
pkt = &sshFxpReaddirPacket{}
case ssh_FXP_REMOVE:
pkt = &sshFxpRemovePacket{}
case ssh_FXP_MKDIR:
pkt = &sshFxpMkdirPacket{}
case ssh_FXP_RMDIR:
case ssh_FXP_REALPATH:
case ssh_FXP_STAT:
case ssh_FXP_RENAME:
pkt = &sshFxpRenamePacket{}
case ssh_FXP_READLINK:
pkt = &sshFxpReadlinkPacket{}
case ssh_FXP_SYMLINK:
case ssh_FXP_STATUS:
case ssh_FXP_HANDLE:
@ -206,12 +222,12 @@ func (p sshFxInitPacket) respond(svr *Server) error {
return svr.sendPacket(sshFxVersionPacket{sftpProtocolVersion, nil})
}
type sshFxpStatReponse struct {
type sshFxpStatResponse struct {
Id uint32
info os.FileInfo
}
func (p sshFxpStatReponse) MarshalBinary() ([]byte, error) {
func (p sshFxpStatResponse) MarshalBinary() ([]byte, error) {
b := []byte{ssh_FXP_ATTRS}
b = marshalUint32(b, p.Id)
b = marshalFileInfo(b, p.info)
@ -223,7 +239,7 @@ func (p sshFxpLstatPacket) respond(svr *Server) error {
if info, err := svr.fs.Lstat(p.Path); err != nil {
return svr.sendPacket(statusFromError(p.Id, err))
} else {
return svr.sendPacket(sshFxpStatReponse{p.Id, info})
return svr.sendPacket(sshFxpStatResponse{p.Id, info})
}
}
@ -234,7 +250,7 @@ func (p sshFxpFstatPacket) respond(svr *Server) error {
if info, err := osf.Stat(); err != nil {
return svr.sendPacket(statusFromError(p.Id, err))
} else {
return svr.sendPacket(sshFxpStatReponse{p.Id, info})
return svr.sendPacket(sshFxpStatResponse{p.Id, info})
}
} else {
// server error...
@ -243,11 +259,59 @@ func (p sshFxpFstatPacket) respond(svr *Server) error {
}
func (p sshFxpMkdirPacket) respond(svr *Server) error {
// ignore flags field
err := svr.fs.Mkdir(p.Path, 0755)
if svr.readOnly {
return svr.sendPacket(statusFromError(p.Id, syscall.EPERM))
}
// TODO FIXME: ignore flags field
if fso, ok := svr.fs.(FileSystemOS); ok {
err := fso.Mkdir(p.Path, 0755)
return svr.sendPacket(statusFromError(p.Id, err))
} else if sftpo, ok := svr.fs.(FileSystemSFTP); ok {
err := sftpo.Mkdir(p.Path)
return svr.sendPacket(statusFromError(p.Id, err))
} else {
return svr.sendPacket(statusFromError(p.Id, fmt.Errorf("unknown filesystem backend")))
}
}
func (p sshFxpRemovePacket) respond(svr *Server) error {
if svr.readOnly {
return svr.sendPacket(statusFromError(p.Id, syscall.EPERM))
}
err := svr.fs.Remove(p.Filename)
return svr.sendPacket(statusFromError(p.Id, err))
}
func (p sshFxpRenamePacket) respond(svr *Server) error {
if svr.readOnly {
return svr.sendPacket(statusFromError(p.Id, syscall.EPERM))
}
err := svr.fs.Rename(p.Oldpath, p.Newpath)
return svr.sendPacket(statusFromError(p.Id, err))
}
func (p sshFxpReadlinkPacket) respond(svr *Server) error {
if fso, ok := svr.fs.(FileSystemOS); ok {
if f, err := fso.Readlink(p.Path); err != nil {
return svr.sendPacket(statusFromError(p.Id, err))
} else {
return svr.sendPacket(sshFxpNamePacket{p.Id, []sshFxpNameAttr{sshFxpNameAttr{f, nil}}})
}
} else if sftpo, ok := svr.fs.(FileSystemSFTP); ok {
if f, err := sftpo.ReadLink(p.Path); err != nil {
return svr.sendPacket(statusFromError(p.Id, err))
} else {
return svr.sendPacket(sshFxpNamePacket{p.Id, []sshFxpNameAttr{sshFxpNameAttr{f, nil}}})
}
} else {
return svr.sendPacket(statusFromError(p.Id, fmt.Errorf("unknown filesystem backend")))
}
}
func (p sshFxpOpendirPacket) respond(svr *Server) error {
return sshFxpOpenPacket{p.Id, p.Path, ssh_FXF_READ, 0}.respond(svr)
}
func (p sshFxpOpenPacket) respond(svr *Server) error {
osFlags := 0
if p.Pflags&ssh_FXF_READ != 0 && p.Pflags&ssh_FXF_WRITE != 0 {
@ -281,14 +345,14 @@ func (p sshFxpOpenPacket) respond(svr *Server) error {
osFlags |= os.O_EXCL
}
if fso, ok := svr.fs.(FileSystemOpen); ok {
if fso, ok := svr.fs.(FileSystemOS); ok {
if f, err := fso.OpenFile(p.Path, osFlags, 0644); err != nil {
return svr.sendPacket(statusFromError(p.Id, err))
} else {
handle := svr.nextHandle(f)
return svr.sendPacket(sshFxpHandlePacket{p.Id, handle})
}
} else if sftpo, ok := svr.fs.(FileSystemSFTPOpen); ok {
} else if sftpo, ok := svr.fs.(FileSystemSFTP); ok {
if f, err := sftpo.OpenFile(p.Path, osFlags); err != nil {
return svr.sendPacket(statusFromError(p.Id, err))
} else {
@ -312,7 +376,6 @@ func (p sshFxpReadPacket) respond(svr *Server) error {
p.Len = maxWritePacket
}
if osf, ok := f.(*os.File); ok {
debug("in readpacket server respond: len %d", p.Len)
ret := sshFxpDataPacket{Id: p.Id, Length: p.Len, Data: make([]byte, p.Len)}
if n, err := osf.ReadAt(ret.Data, int64(p.Offset)); err != nil && (err != io.EOF || n == 0) {
return svr.sendPacket(statusFromError(p.Id, err))
@ -328,6 +391,10 @@ func (p sshFxpReadPacket) respond(svr *Server) error {
}
func (p sshFxpWritePacket) respond(svr *Server) error {
if svr.readOnly {
// shouldn't really get here, the open should have failed
return svr.sendPacket(statusFromError(p.Id, syscall.EPERM))
}
if f, ok := svr.getHandle(p.Handle); !ok {
return svr.sendPacket(statusFromError(p.Id, syscall.EBADF))
} else if osf, ok := f.(*os.File); ok {
@ -339,6 +406,32 @@ func (p sshFxpWritePacket) respond(svr *Server) error {
}
}
func (p sshFxpReaddirPacket) respond(svr *Server) error {
if f, ok := svr.getHandle(p.Handle); !ok {
return svr.sendPacket(statusFromError(p.Id, syscall.EBADF))
} else {
dirents := []os.FileInfo{}
var err error = nil
if osf, ok := f.(*os.File); ok {
dirents, err = osf.Readdir(128)
} else {
// server error...
return svr.sendPacket(statusFromError(p.Id, syscall.EBADF))
}
if err != nil {
return svr.sendPacket(statusFromError(p.Id, err))
}
ret := sshFxpNamePacket{p.Id, nil}
for _, dirent := range dirents {
ret.NameAttrs = append(ret.NameAttrs, sshFxpNameAttr{dirent.Name(), dirent})
}
return svr.sendPacket(ret)
}
}
func errnoToSshErr(errno syscall.Errno) uint32 {
if errno == 0 {
return ssh_FX_OK