| package sftp |
| |
| import ( |
| "bytes" |
| "encoding" |
| "encoding/binary" |
| "fmt" |
| "io" |
| "os" |
| "reflect" |
| |
| "github.com/pkg/errors" |
| ) |
| |
| var ( |
| errShortPacket = errors.New("packet too short") |
| errUnknownExtendedPacket = errors.New("unknown extended packet") |
| ) |
| |
| const ( |
| debugDumpTxPacket = false |
| debugDumpRxPacket = false |
| debugDumpTxPacketBytes = false |
| debugDumpRxPacketBytes = false |
| ) |
| |
| func marshalUint32(b []byte, v uint32) []byte { |
| return append(b, byte(v>>24), byte(v>>16), byte(v>>8), byte(v)) |
| } |
| |
| func marshalUint64(b []byte, v uint64) []byte { |
| return marshalUint32(marshalUint32(b, uint32(v>>32)), uint32(v)) |
| } |
| |
| func marshalString(b []byte, v string) []byte { |
| return append(marshalUint32(b, uint32(len(v))), v...) |
| } |
| |
| func marshal(b []byte, v interface{}) []byte { |
| if v == nil { |
| return b |
| } |
| switch v := v.(type) { |
| case uint8: |
| return append(b, v) |
| case uint32: |
| return marshalUint32(b, v) |
| case uint64: |
| 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: |
| for i, n := 0, d.NumField(); i < n; i++ { |
| b = append(marshal(b, d.Field(i).Interface())) |
| } |
| return b |
| case reflect.Slice: |
| for i, n := 0, d.Len(); i < n; i++ { |
| b = append(marshal(b, d.Index(i).Interface())) |
| } |
| return b |
| default: |
| panic(fmt.Sprintf("marshal(%#v): cannot handle type %T", v, v)) |
| } |
| } |
| } |
| |
| func unmarshalUint32(b []byte) (uint32, []byte) { |
| v := uint32(b[3]) | uint32(b[2])<<8 | uint32(b[1])<<16 | uint32(b[0])<<24 |
| return v, b[4:] |
| } |
| |
| func unmarshalUint32Safe(b []byte) (uint32, []byte, error) { |
| var v uint32 |
| if len(b) < 4 { |
| return 0, nil, errShortPacket |
| } |
| v, b = unmarshalUint32(b) |
| return v, b, nil |
| } |
| |
| func unmarshalUint64(b []byte) (uint64, []byte) { |
| h, b := unmarshalUint32(b) |
| l, b := unmarshalUint32(b) |
| return uint64(h)<<32 | uint64(l), b |
| } |
| |
| func unmarshalUint64Safe(b []byte) (uint64, []byte, error) { |
| var v uint64 |
| if len(b) < 8 { |
| return 0, nil, errShortPacket |
| } |
| v, b = unmarshalUint64(b) |
| return v, b, nil |
| } |
| |
| func unmarshalString(b []byte) (string, []byte) { |
| n, b := unmarshalUint32(b) |
| return string(b[:n]), b[n:] |
| } |
| |
| func unmarshalStringSafe(b []byte) (string, []byte, error) { |
| n, b, err := unmarshalUint32Safe(b) |
| if err != nil { |
| return "", nil, err |
| } |
| if int64(n) > int64(len(b)) { |
| return "", nil, errShortPacket |
| } |
| return string(b[:n]), b[n:], nil |
| } |
| |
| // sendPacket marshals p according to RFC 4234. |
| func sendPacket(w io.Writer, m encoding.BinaryMarshaler) error { |
| bb, err := m.MarshalBinary() |
| if err != nil { |
| return errors.Errorf("binary marshaller failed: %v", err) |
| } |
| if debugDumpTxPacketBytes { |
| debug("send packet: %s %d bytes %x", fxp(bb[0]), len(bb), bb[1:]) |
| } else if debugDumpTxPacket { |
| debug("send packet: %s %d bytes", fxp(bb[0]), len(bb)) |
| } |
| l := uint32(len(bb)) |
| hdr := []byte{byte(l >> 24), byte(l >> 16), byte(l >> 8), byte(l)} |
| _, err = w.Write(hdr) |
| if err != nil { |
| return errors.Errorf("failed to send packet header: %v", err) |
| } |
| _, err = w.Write(bb) |
| if err != nil { |
| return errors.Errorf("failed to send packet body: %v", err) |
| } |
| return nil |
| } |
| |
| func recvPacket(r io.Reader) (uint8, []byte, error) { |
| var b = []byte{0, 0, 0, 0} |
| if _, err := io.ReadFull(r, b); err != nil { |
| return 0, nil, err |
| } |
| l, _ := unmarshalUint32(b) |
| b = make([]byte, l) |
| if _, err := io.ReadFull(r, b); err != nil { |
| debug("recv packet %d bytes: err %v", l, err) |
| return 0, nil, err |
| } |
| if debugDumpRxPacketBytes { |
| debug("recv packet: %s %d bytes %x", fxp(b[0]), l, b[1:]) |
| } else if debugDumpRxPacket { |
| debug("recv packet: %s %d bytes", fxp(b[0]), l) |
| } |
| return b[0], b[1:], nil |
| } |
| |
| type extensionPair struct { |
| Name string |
| Data string |
| } |
| |
| func unmarshalExtensionPair(b []byte) (extensionPair, []byte, error) { |
| var ep extensionPair |
| var err error |
| ep.Name, b, err = unmarshalStringSafe(b) |
| if err != nil { |
| return ep, b, err |
| } |
| ep.Data, b, err = unmarshalStringSafe(b) |
| return ep, b, err |
| } |
| |
| // Here starts the definition of packets along with their MarshalBinary |
| // implementations. |
| // Manually writing the marshalling logic wins us a lot of time and |
| // allocation. |
| |
| type sshFxInitPacket struct { |
| Version uint32 |
| Extensions []extensionPair |
| } |
| |
| func (p sshFxInitPacket) MarshalBinary() ([]byte, error) { |
| l := 1 + 4 // byte + uint32 |
| for _, e := range p.Extensions { |
| l += 4 + len(e.Name) + 4 + len(e.Data) |
| } |
| |
| b := make([]byte, 0, l) |
| b = append(b, ssh_FXP_INIT) |
| b = marshalUint32(b, p.Version) |
| for _, e := range p.Extensions { |
| b = marshalString(b, e.Name) |
| b = marshalString(b, e.Data) |
| } |
| return b, nil |
| } |
| |
| func (p *sshFxInitPacket) UnmarshalBinary(b []byte) error { |
| var err error |
| if p.Version, b, err = unmarshalUint32Safe(b); err != nil { |
| return err |
| } |
| for len(b) > 0 { |
| var ep extensionPair |
| ep, b, err = unmarshalExtensionPair(b) |
| if err != nil { |
| return err |
| } |
| p.Extensions = append(p.Extensions, ep) |
| } |
| return nil |
| } |
| |
| type sshFxVersionPacket struct { |
| Version uint32 |
| Extensions []struct { |
| Name, Data string |
| } |
| } |
| |
| func (p sshFxVersionPacket) MarshalBinary() ([]byte, error) { |
| l := 1 + 4 // byte + uint32 |
| for _, e := range p.Extensions { |
| l += 4 + len(e.Name) + 4 + len(e.Data) |
| } |
| |
| b := make([]byte, 0, l) |
| b = append(b, ssh_FXP_VERSION) |
| b = marshalUint32(b, p.Version) |
| for _, e := range p.Extensions { |
| b = marshalString(b, e.Name) |
| b = marshalString(b, e.Data) |
| } |
| return b, nil |
| } |
| |
| func marshalIDString(packetType byte, id uint32, str string) ([]byte, error) { |
| l := 1 + 4 + // type(byte) + uint32 |
| 4 + len(str) |
| |
| b := make([]byte, 0, l) |
| b = append(b, packetType) |
| b = marshalUint32(b, id) |
| b = marshalString(b, str) |
| return b, nil |
| } |
| |
| func unmarshalIDString(b []byte, id *uint32, str *string) error { |
| var err error |
| *id, b, err = unmarshalUint32Safe(b) |
| if err != nil { |
| return err |
| } |
| *str, b, err = unmarshalStringSafe(b) |
| return err |
| } |
| |
| type sshFxpReaddirPacket struct { |
| ID uint32 |
| Handle string |
| } |
| |
| func (p sshFxpReaddirPacket) id() uint32 { return p.ID } |
| |
| 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 |
| } |
| |
| func (p sshFxpOpendirPacket) id() uint32 { return p.ID } |
| |
| 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 |
| } |
| |
| func (p sshFxpLstatPacket) id() uint32 { return p.ID } |
| |
| func (p sshFxpLstatPacket) MarshalBinary() ([]byte, error) { |
| return marshalIDString(ssh_FXP_LSTAT, p.ID, p.Path) |
| } |
| |
| func (p *sshFxpLstatPacket) UnmarshalBinary(b []byte) error { |
| return unmarshalIDString(b, &p.ID, &p.Path) |
| } |
| |
| type sshFxpStatPacket struct { |
| ID uint32 |
| Path string |
| } |
| |
| func (p sshFxpStatPacket) id() uint32 { return p.ID } |
| |
| func (p sshFxpStatPacket) MarshalBinary() ([]byte, error) { |
| return marshalIDString(ssh_FXP_STAT, p.ID, p.Path) |
| } |
| |
| func (p *sshFxpStatPacket) UnmarshalBinary(b []byte) error { |
| return unmarshalIDString(b, &p.ID, &p.Path) |
| } |
| |
| type sshFxpFstatPacket struct { |
| ID uint32 |
| Handle string |
| } |
| |
| func (p sshFxpFstatPacket) id() uint32 { return p.ID } |
| |
| func (p sshFxpFstatPacket) MarshalBinary() ([]byte, error) { |
| return marshalIDString(ssh_FXP_FSTAT, p.ID, p.Handle) |
| } |
| |
| func (p *sshFxpFstatPacket) UnmarshalBinary(b []byte) error { |
| return unmarshalIDString(b, &p.ID, &p.Handle) |
| } |
| |
| type sshFxpClosePacket struct { |
| ID uint32 |
| Handle string |
| } |
| |
| func (p sshFxpClosePacket) id() uint32 { return p.ID } |
| |
| func (p sshFxpClosePacket) MarshalBinary() ([]byte, error) { |
| return marshalIDString(ssh_FXP_CLOSE, p.ID, p.Handle) |
| } |
| |
| func (p *sshFxpClosePacket) UnmarshalBinary(b []byte) error { |
| return unmarshalIDString(b, &p.ID, &p.Handle) |
| } |
| |
| type sshFxpRemovePacket struct { |
| ID uint32 |
| Filename string |
| } |
| |
| func (p sshFxpRemovePacket) id() uint32 { return p.ID } |
| |
| 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 |
| } |
| |
| func (p sshFxpRmdirPacket) id() uint32 { return p.ID } |
| |
| 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 sshFxpSymlinkPacket struct { |
| ID uint32 |
| Targetpath string |
| Linkpath string |
| } |
| |
| func (p sshFxpSymlinkPacket) id() uint32 { return p.ID } |
| |
| func (p sshFxpSymlinkPacket) MarshalBinary() ([]byte, error) { |
| l := 1 + 4 + // type(byte) + uint32 |
| 4 + len(p.Targetpath) + |
| 4 + len(p.Linkpath) |
| |
| b := make([]byte, 0, l) |
| b = append(b, ssh_FXP_SYMLINK) |
| b = marshalUint32(b, p.ID) |
| b = marshalString(b, p.Targetpath) |
| b = marshalString(b, p.Linkpath) |
| return b, nil |
| } |
| |
| func (p *sshFxpSymlinkPacket) UnmarshalBinary(b []byte) error { |
| var err error |
| if p.ID, b, err = unmarshalUint32Safe(b); err != nil { |
| return err |
| } else if p.Targetpath, b, err = unmarshalStringSafe(b); err != nil { |
| return err |
| } else if p.Linkpath, b, err = unmarshalStringSafe(b); err != nil { |
| return err |
| } |
| return nil |
| } |
| |
| type sshFxpReadlinkPacket struct { |
| ID uint32 |
| Path string |
| } |
| |
| func (p sshFxpReadlinkPacket) id() uint32 { return p.ID } |
| |
| 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 sshFxpRealpathPacket struct { |
| ID uint32 |
| Path string |
| } |
| |
| func (p sshFxpRealpathPacket) id() uint32 { return p.ID } |
| |
| func (p sshFxpRealpathPacket) MarshalBinary() ([]byte, error) { |
| return marshalIDString(ssh_FXP_REALPATH, p.ID, p.Path) |
| } |
| |
| func (p *sshFxpRealpathPacket) UnmarshalBinary(b []byte) error { |
| return unmarshalIDString(b, &p.ID, &p.Path) |
| } |
| |
| type sshFxpNameAttr struct { |
| Name string |
| LongName string |
| Attrs []interface{} |
| } |
| |
| func (p sshFxpNameAttr) MarshalBinary() ([]byte, error) { |
| b := []byte{} |
| b = marshalString(b, p.Name) |
| b = marshalString(b, p.LongName) |
| for _, attr := range p.Attrs { |
| b = marshal(b, attr) |
| } |
| 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 { |
| ab, err := na.MarshalBinary() |
| if err != nil { |
| return nil, err |
| } |
| |
| b = append(b, ab...) |
| } |
| return b, nil |
| } |
| |
| type sshFxpOpenPacket struct { |
| ID uint32 |
| Path string |
| Pflags uint32 |
| Flags uint32 // ignored |
| } |
| |
| func (p sshFxpOpenPacket) id() uint32 { return p.ID } |
| |
| func (p sshFxpOpenPacket) MarshalBinary() ([]byte, error) { |
| l := 1 + 4 + |
| 4 + len(p.Path) + |
| 4 + 4 |
| |
| b := make([]byte, 0, l) |
| b = append(b, ssh_FXP_OPEN) |
| b = marshalUint32(b, p.ID) |
| b = marshalString(b, p.Path) |
| b = marshalUint32(b, p.Pflags) |
| b = marshalUint32(b, p.Flags) |
| return b, nil |
| } |
| |
| func (p *sshFxpOpenPacket) UnmarshalBinary(b []byte) error { |
| var err error |
| if p.ID, b, err = unmarshalUint32Safe(b); err != nil { |
| return err |
| } else if p.Path, b, err = unmarshalStringSafe(b); err != nil { |
| return err |
| } else if p.Pflags, b, err = unmarshalUint32Safe(b); err != nil { |
| return err |
| } else if p.Flags, b, err = unmarshalUint32Safe(b); err != nil { |
| return err |
| } |
| return nil |
| } |
| |
| type sshFxpReadPacket struct { |
| ID uint32 |
| Handle string |
| Offset uint64 |
| Len uint32 |
| } |
| |
| func (p sshFxpReadPacket) id() uint32 { return p.ID } |
| |
| func (p sshFxpReadPacket) MarshalBinary() ([]byte, error) { |
| l := 1 + 4 + // type(byte) + uint32 |
| 4 + len(p.Handle) + |
| 8 + 4 // uint64 + uint32 |
| |
| b := make([]byte, 0, l) |
| b = append(b, ssh_FXP_READ) |
| b = marshalUint32(b, p.ID) |
| b = marshalString(b, p.Handle) |
| b = marshalUint64(b, p.Offset) |
| b = marshalUint32(b, p.Len) |
| return b, nil |
| } |
| |
| func (p *sshFxpReadPacket) UnmarshalBinary(b []byte) error { |
| var err error |
| if p.ID, b, err = unmarshalUint32Safe(b); err != nil { |
| return err |
| } else if p.Handle, b, err = unmarshalStringSafe(b); err != nil { |
| return err |
| } else if p.Offset, b, err = unmarshalUint64Safe(b); err != nil { |
| return err |
| } else if p.Len, b, err = unmarshalUint32Safe(b); err != nil { |
| return err |
| } |
| return nil |
| } |
| |
| type sshFxpRenamePacket struct { |
| ID uint32 |
| Oldpath string |
| Newpath string |
| } |
| |
| func (p sshFxpRenamePacket) id() uint32 { return p.ID } |
| |
| func (p sshFxpRenamePacket) MarshalBinary() ([]byte, error) { |
| l := 1 + 4 + // type(byte) + uint32 |
| 4 + len(p.Oldpath) + |
| 4 + len(p.Newpath) |
| |
| b := make([]byte, 0, l) |
| b = append(b, ssh_FXP_RENAME) |
| b = marshalUint32(b, p.ID) |
| b = marshalString(b, p.Oldpath) |
| b = marshalString(b, p.Newpath) |
| return b, nil |
| } |
| |
| func (p *sshFxpRenamePacket) UnmarshalBinary(b []byte) error { |
| var err error |
| if p.ID, b, err = unmarshalUint32Safe(b); err != nil { |
| return err |
| } else if p.Oldpath, b, err = unmarshalStringSafe(b); err != nil { |
| return err |
| } else if p.Newpath, b, err = unmarshalStringSafe(b); err != nil { |
| return err |
| } |
| return nil |
| } |
| |
| type sshFxpWritePacket struct { |
| ID uint32 |
| Handle string |
| Offset uint64 |
| Length uint32 |
| Data []byte |
| } |
| |
| func (p sshFxpWritePacket) id() uint32 { return p.ID } |
| |
| func (p sshFxpWritePacket) MarshalBinary() ([]byte, error) { |
| l := 1 + 4 + // type(byte) + uint32 |
| 4 + len(p.Handle) + |
| 8 + 4 + // uint64 + uint32 |
| len(p.Data) |
| |
| b := make([]byte, 0, l) |
| b = append(b, ssh_FXP_WRITE) |
| b = marshalUint32(b, p.ID) |
| b = marshalString(b, p.Handle) |
| b = marshalUint64(b, p.Offset) |
| b = marshalUint32(b, p.Length) |
| b = append(b, p.Data...) |
| return b, nil |
| } |
| |
| func (p *sshFxpWritePacket) UnmarshalBinary(b []byte) error { |
| var err error |
| if p.ID, b, err = unmarshalUint32Safe(b); err != nil { |
| return err |
| } else if p.Handle, b, err = unmarshalStringSafe(b); err != nil { |
| return err |
| } else if p.Offset, b, err = unmarshalUint64Safe(b); err != nil { |
| return err |
| } else if p.Length, b, err = unmarshalUint32Safe(b); err != nil { |
| return err |
| } else if uint32(len(b)) < p.Length { |
| return errShortPacket |
| } |
| |
| p.Data = append([]byte{}, b[:p.Length]...) |
| return nil |
| } |
| |
| type sshFxpMkdirPacket struct { |
| ID uint32 |
| Path string |
| Flags uint32 // ignored |
| } |
| |
| func (p sshFxpMkdirPacket) id() uint32 { return p.ID } |
| |
| func (p sshFxpMkdirPacket) MarshalBinary() ([]byte, error) { |
| l := 1 + 4 + // type(byte) + uint32 |
| 4 + len(p.Path) + |
| 4 // uint32 |
| |
| b := make([]byte, 0, l) |
| b = append(b, ssh_FXP_MKDIR) |
| b = marshalUint32(b, p.ID) |
| b = marshalString(b, p.Path) |
| b = marshalUint32(b, p.Flags) |
| return b, nil |
| } |
| |
| func (p *sshFxpMkdirPacket) UnmarshalBinary(b []byte) error { |
| var err error |
| if p.ID, b, err = unmarshalUint32Safe(b); err != nil { |
| return err |
| } else if p.Path, b, err = unmarshalStringSafe(b); err != nil { |
| return err |
| } else if p.Flags, b, err = unmarshalUint32Safe(b); err != nil { |
| return err |
| } |
| return nil |
| } |
| |
| type sshFxpSetstatPacket struct { |
| ID uint32 |
| Path string |
| Flags uint32 |
| Attrs interface{} |
| } |
| |
| type sshFxpFsetstatPacket struct { |
| ID uint32 |
| Handle string |
| Flags uint32 |
| Attrs interface{} |
| } |
| |
| func (p sshFxpSetstatPacket) id() uint32 { return p.ID } |
| func (p sshFxpFsetstatPacket) id() uint32 { return p.ID } |
| |
| func (p sshFxpSetstatPacket) MarshalBinary() ([]byte, error) { |
| l := 1 + 4 + // type(byte) + uint32 |
| 4 + len(p.Path) + |
| 4 // uint32 + uint64 |
| |
| b := make([]byte, 0, l) |
| b = append(b, ssh_FXP_SETSTAT) |
| b = marshalUint32(b, p.ID) |
| b = marshalString(b, p.Path) |
| b = marshalUint32(b, p.Flags) |
| b = marshal(b, p.Attrs) |
| return b, nil |
| } |
| |
| func (p sshFxpFsetstatPacket) MarshalBinary() ([]byte, error) { |
| l := 1 + 4 + // type(byte) + uint32 |
| 4 + len(p.Handle) + |
| 4 // uint32 + uint64 |
| |
| b := make([]byte, 0, l) |
| b = append(b, ssh_FXP_FSETSTAT) |
| b = marshalUint32(b, p.ID) |
| b = marshalString(b, p.Handle) |
| b = marshalUint32(b, p.Flags) |
| b = marshal(b, p.Attrs) |
| return b, nil |
| } |
| |
| func (p *sshFxpSetstatPacket) UnmarshalBinary(b []byte) error { |
| var err error |
| if p.ID, b, err = unmarshalUint32Safe(b); err != nil { |
| return err |
| } else if p.Path, b, err = unmarshalStringSafe(b); err != nil { |
| return err |
| } else if p.Flags, b, err = unmarshalUint32Safe(b); err != nil { |
| return err |
| } |
| p.Attrs = b |
| return nil |
| } |
| |
| func (p *sshFxpFsetstatPacket) UnmarshalBinary(b []byte) error { |
| var err error |
| if p.ID, b, err = unmarshalUint32Safe(b); err != nil { |
| return err |
| } else if p.Handle, b, err = unmarshalStringSafe(b); err != nil { |
| return err |
| } else if p.Flags, b, err = unmarshalUint32Safe(b); err != nil { |
| return err |
| } |
| p.Attrs = b |
| return nil |
| } |
| |
| type sshFxpHandlePacket struct { |
| ID uint32 |
| Handle string |
| } |
| |
| func (p sshFxpHandlePacket) MarshalBinary() ([]byte, error) { |
| b := []byte{ssh_FXP_HANDLE} |
| b = marshalUint32(b, p.ID) |
| b = marshalString(b, p.Handle) |
| return b, nil |
| } |
| |
| type sshFxpStatusPacket struct { |
| ID uint32 |
| StatusError |
| } |
| |
| func (p sshFxpStatusPacket) MarshalBinary() ([]byte, error) { |
| b := []byte{ssh_FXP_STATUS} |
| b = marshalUint32(b, p.ID) |
| b = marshalStatus(b, p.StatusError) |
| return b, nil |
| } |
| |
| type sshFxpDataPacket struct { |
| ID uint32 |
| Length uint32 |
| Data []byte |
| } |
| |
| func (p sshFxpDataPacket) MarshalBinary() ([]byte, error) { |
| b := []byte{ssh_FXP_DATA} |
| b = marshalUint32(b, p.ID) |
| b = marshalUint32(b, p.Length) |
| b = append(b, p.Data[:p.Length]...) |
| return b, nil |
| } |
| |
| func (p *sshFxpDataPacket) UnmarshalBinary(b []byte) error { |
| var err error |
| if p.ID, b, err = unmarshalUint32Safe(b); err != nil { |
| return err |
| } else if p.Length, b, err = unmarshalUint32Safe(b); err != nil { |
| return err |
| } else if uint32(len(b)) < p.Length { |
| return errors.New("truncated packet") |
| } |
| |
| p.Data = make([]byte, p.Length) |
| copy(p.Data, b) |
| return nil |
| } |
| |
| type sshFxpStatvfsPacket struct { |
| ID uint32 |
| Path string |
| } |
| |
| func (p sshFxpStatvfsPacket) id() uint32 { return p.ID } |
| |
| func (p sshFxpStatvfsPacket) MarshalBinary() ([]byte, error) { |
| l := 1 + 4 + // type(byte) + uint32 |
| len(p.Path) + |
| len("statvfs@openssh.com") |
| |
| b := make([]byte, 0, l) |
| b = append(b, ssh_FXP_EXTENDED) |
| b = marshalUint32(b, p.ID) |
| b = marshalString(b, "statvfs@openssh.com") |
| b = marshalString(b, p.Path) |
| return b, nil |
| } |
| |
| // A StatVFS contains statistics about a filesystem. |
| type StatVFS struct { |
| ID uint32 |
| Bsize uint64 /* file system block size */ |
| Frsize uint64 /* fundamental fs block size */ |
| Blocks uint64 /* number of blocks (unit f_frsize) */ |
| Bfree uint64 /* free blocks in file system */ |
| Bavail uint64 /* free blocks for non-root */ |
| Files uint64 /* total file inodes */ |
| Ffree uint64 /* free file inodes */ |
| Favail uint64 /* free file inodes for to non-root */ |
| Fsid uint64 /* file system id */ |
| Flag uint64 /* bit mask of f_flag values */ |
| Namemax uint64 /* maximum filename length */ |
| } |
| |
| // TotalSpace calculates the amount of total space in a filesystem. |
| func (p *StatVFS) TotalSpace() uint64 { |
| return p.Frsize * p.Blocks |
| } |
| |
| // FreeSpace calculates the amount of free space in a filesystem. |
| func (p *StatVFS) FreeSpace() uint64 { |
| return p.Frsize * p.Bfree |
| } |
| |
| // Convert to ssh_FXP_EXTENDED_REPLY packet binary format |
| func (p *StatVFS) MarshalBinary() ([]byte, error) { |
| var buf bytes.Buffer |
| buf.Write([]byte{ssh_FXP_EXTENDED_REPLY}) |
| err := binary.Write(&buf, binary.BigEndian, p) |
| return buf.Bytes(), err |
| } |
| |
| type sshFxpExtendedPacket struct { |
| ID uint32 |
| ExtendedRequest string |
| SpecificPacket interface { |
| serverRespondablePacket |
| readonly() bool |
| } |
| } |
| |
| func (p sshFxpExtendedPacket) id() uint32 { return p.ID } |
| func (p sshFxpExtendedPacket) readonly() bool { return p.SpecificPacket.readonly() } |
| |
| func (p sshFxpExtendedPacket) respond(svr *Server) error { |
| return p.SpecificPacket.respond(svr) |
| } |
| |
| func (p *sshFxpExtendedPacket) UnmarshalBinary(b []byte) error { |
| var err error |
| bOrig := b |
| if p.ID, b, err = unmarshalUint32Safe(b); err != nil { |
| return err |
| } else if p.ExtendedRequest, b, err = unmarshalStringSafe(b); err != nil { |
| return err |
| } |
| |
| // specific unmarshalling |
| switch p.ExtendedRequest { |
| case "statvfs@openssh.com": |
| p.SpecificPacket = &sshFxpExtendedPacketStatVFS{} |
| default: |
| return errUnknownExtendedPacket |
| } |
| |
| return p.SpecificPacket.UnmarshalBinary(bOrig) |
| } |
| |
| type sshFxpExtendedPacketStatVFS struct { |
| ID uint32 |
| ExtendedRequest string |
| Path string |
| } |
| |
| func (p sshFxpExtendedPacketStatVFS) id() uint32 { return p.ID } |
| func (p sshFxpExtendedPacketStatVFS) readonly() bool { return true } |
| func (p *sshFxpExtendedPacketStatVFS) UnmarshalBinary(b []byte) error { |
| var err error |
| if p.ID, b, err = unmarshalUint32Safe(b); err != nil { |
| return err |
| } else if p.ExtendedRequest, b, err = unmarshalStringSafe(b); err != nil { |
| return err |
| } else if p.Path, b, err = unmarshalStringSafe(b); err != nil { |
| return err |
| } |
| return nil |
| } |