mirror of https://github.com/pkg/sftp.git
refactor sshfx encoding, fix link rot, go fmt
This commit is contained in:
parent
971c283182
commit
bd61319b07
2
attrs.go
2
attrs.go
|
@ -1,7 +1,7 @@
|
|||
package sftp
|
||||
|
||||
// ssh_FXP_ATTRS support
|
||||
// see http://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#section-5
|
||||
// see https://filezilla-project.org/specs/draft-ietf-secsh-filexfer-02.txt#section-5
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
//go:build plan9 || windows || android
|
||||
// +build plan9 windows android
|
||||
|
||||
package sftp
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
//go:build darwin || dragonfly || freebsd || (!android && linux) || netbsd || openbsd || solaris || aix || js
|
||||
// +build darwin dragonfly freebsd !android,linux netbsd openbsd solaris aix js
|
||||
|
||||
package sftp
|
||||
|
|
|
@ -259,11 +259,11 @@ func (c *Client) Create(path string) (*File, error) {
|
|||
return c.open(path, flags(os.O_RDWR|os.O_CREATE|os.O_TRUNC))
|
||||
}
|
||||
|
||||
const sftpProtocolVersion = 3 // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-02
|
||||
const sftpProtocolVersion = 3 // https://filezilla-project.org/specs/draft-ietf-secsh-filexfer-02.txt
|
||||
|
||||
func (c *Client) sendInit() error {
|
||||
return c.clientConn.conn.sendPacket(&sshFxInitPacket{
|
||||
Version: sftpProtocolVersion, // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-02
|
||||
Version: sftpProtocolVersion, // https://filezilla-project.org/specs/draft-ietf-secsh-filexfer-02.txt
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package filexfer
|
||||
package sshfx
|
||||
|
||||
// Attributes related flags.
|
||||
const (
|
||||
|
@ -12,7 +12,7 @@ const (
|
|||
|
||||
// Attributes defines the file attributes type defined in draft-ietf-secsh-filexfer-02
|
||||
//
|
||||
// Defined in: https://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#section-5
|
||||
// Defined in: https://filezilla-project.org/specs/draft-ietf-secsh-filexfer-02.txt#section-5
|
||||
type Attributes struct {
|
||||
Flags uint32
|
||||
|
||||
|
@ -116,32 +116,32 @@ func (a *Attributes) Len() int {
|
|||
}
|
||||
|
||||
// MarshalInto marshals e onto the end of the given Buffer.
|
||||
func (a *Attributes) MarshalInto(b *Buffer) {
|
||||
b.AppendUint32(a.Flags)
|
||||
func (a *Attributes) MarshalInto(buf *Buffer) {
|
||||
buf.AppendUint32(a.Flags)
|
||||
|
||||
if a.Flags&AttrSize != 0 {
|
||||
b.AppendUint64(a.Size)
|
||||
buf.AppendUint64(a.Size)
|
||||
}
|
||||
|
||||
if a.Flags&AttrUIDGID != 0 {
|
||||
b.AppendUint32(a.UID)
|
||||
b.AppendUint32(a.GID)
|
||||
buf.AppendUint32(a.UID)
|
||||
buf.AppendUint32(a.GID)
|
||||
}
|
||||
|
||||
if a.Flags&AttrPermissions != 0 {
|
||||
b.AppendUint32(uint32(a.Permissions))
|
||||
buf.AppendUint32(uint32(a.Permissions))
|
||||
}
|
||||
|
||||
if a.Flags&AttrACModTime != 0 {
|
||||
b.AppendUint32(a.ATime)
|
||||
b.AppendUint32(a.MTime)
|
||||
buf.AppendUint32(a.ATime)
|
||||
buf.AppendUint32(a.MTime)
|
||||
}
|
||||
|
||||
if a.Flags&AttrExtended != 0 {
|
||||
b.AppendUint32(uint32(len(a.ExtendedAttributes)))
|
||||
buf.AppendUint32(uint32(len(a.ExtendedAttributes)))
|
||||
|
||||
for _, ext := range a.ExtendedAttributes {
|
||||
ext.MarshalInto(b)
|
||||
ext.MarshalInto(buf)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -156,74 +156,51 @@ func (a *Attributes) MarshalBinary() ([]byte, error) {
|
|||
// UnmarshalFrom unmarshals an Attributes from the given Buffer into e.
|
||||
//
|
||||
// NOTE: The values of fields not covered in the a.Flags are explicitly undefined.
|
||||
func (a *Attributes) UnmarshalFrom(b *Buffer) (err error) {
|
||||
flags, err := b.ConsumeUint32()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
func (a *Attributes) UnmarshalFrom(buf *Buffer) (err error) {
|
||||
flags := buf.ConsumeUint32()
|
||||
|
||||
return a.XXX_UnmarshalByFlags(flags, b)
|
||||
return a.XXX_UnmarshalByFlags(flags, buf)
|
||||
}
|
||||
|
||||
// XXX_UnmarshalByFlags uses the pre-existing a.Flags field to determine which fields to decode.
|
||||
// DO NOT USE THIS: it is an anti-corruption function to implement existing internal usage in pkg/sftp.
|
||||
// This function is not a part of any compatibility promise.
|
||||
func (a *Attributes) XXX_UnmarshalByFlags(flags uint32, b *Buffer) (err error) {
|
||||
func (a *Attributes) XXX_UnmarshalByFlags(flags uint32, buf *Buffer) (err error) {
|
||||
a.Flags = flags
|
||||
|
||||
// Short-circuit dummy attributes.
|
||||
if a.Flags == 0 {
|
||||
return nil
|
||||
return buf.Err
|
||||
}
|
||||
|
||||
if a.Flags&AttrSize != 0 {
|
||||
if a.Size, err = b.ConsumeUint64(); err != nil {
|
||||
return err
|
||||
}
|
||||
a.Size = buf.ConsumeUint64()
|
||||
}
|
||||
|
||||
if a.Flags&AttrUIDGID != 0 {
|
||||
if a.UID, err = b.ConsumeUint32(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if a.GID, err = b.ConsumeUint32(); err != nil {
|
||||
return err
|
||||
}
|
||||
a.UID = buf.ConsumeUint32()
|
||||
a.GID = buf.ConsumeUint32()
|
||||
}
|
||||
|
||||
if a.Flags&AttrPermissions != 0 {
|
||||
m, err := b.ConsumeUint32()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
a.Permissions = FileMode(m)
|
||||
a.Permissions = FileMode(buf.ConsumeUint32())
|
||||
}
|
||||
|
||||
if a.Flags&AttrACModTime != 0 {
|
||||
if a.ATime, err = b.ConsumeUint32(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if a.MTime, err = b.ConsumeUint32(); err != nil {
|
||||
return err
|
||||
}
|
||||
a.ATime = buf.ConsumeUint32()
|
||||
a.MTime = buf.ConsumeUint32()
|
||||
}
|
||||
|
||||
if a.Flags&AttrExtended != 0 {
|
||||
count, err := b.ConsumeUint32()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
count := buf.ConsumeCount()
|
||||
|
||||
a.ExtendedAttributes = make([]ExtendedAttribute, count)
|
||||
for i := range a.ExtendedAttributes {
|
||||
a.ExtendedAttributes[i].UnmarshalFrom(b)
|
||||
a.ExtendedAttributes[i].UnmarshalFrom(buf)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
return buf.Err
|
||||
}
|
||||
|
||||
// UnmarshalBinary decodes the binary encoding of Attributes into e.
|
||||
|
@ -233,7 +210,7 @@ func (a *Attributes) UnmarshalBinary(data []byte) error {
|
|||
|
||||
// ExtendedAttribute defines the extended file attribute type defined in draft-ietf-secsh-filexfer-02
|
||||
//
|
||||
// Defined in: https://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#section-5
|
||||
// Defined in: https://filezilla-project.org/specs/draft-ietf-secsh-filexfer-02.txt#section-5
|
||||
type ExtendedAttribute struct {
|
||||
Type string
|
||||
Data string
|
||||
|
@ -245,9 +222,9 @@ func (e *ExtendedAttribute) Len() int {
|
|||
}
|
||||
|
||||
// MarshalInto marshals e onto the end of the given Buffer.
|
||||
func (e *ExtendedAttribute) MarshalInto(b *Buffer) {
|
||||
b.AppendString(e.Type)
|
||||
b.AppendString(e.Data)
|
||||
func (e *ExtendedAttribute) MarshalInto(buf *Buffer) {
|
||||
buf.AppendString(e.Type)
|
||||
buf.AppendString(e.Data)
|
||||
}
|
||||
|
||||
// MarshalBinary returns e as the binary encoding of e.
|
||||
|
@ -258,16 +235,13 @@ func (e *ExtendedAttribute) MarshalBinary() ([]byte, error) {
|
|||
}
|
||||
|
||||
// UnmarshalFrom unmarshals an ExtendedAattribute from the given Buffer into e.
|
||||
func (e *ExtendedAttribute) UnmarshalFrom(b *Buffer) (err error) {
|
||||
if e.Type, err = b.ConsumeString(); err != nil {
|
||||
return err
|
||||
func (e *ExtendedAttribute) UnmarshalFrom(buf *Buffer) (err error) {
|
||||
*e = ExtendedAttribute{
|
||||
Type: buf.ConsumeString(),
|
||||
Data: buf.ConsumeString(),
|
||||
}
|
||||
|
||||
if e.Data, err = b.ConsumeString(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
return buf.Err
|
||||
}
|
||||
|
||||
// UnmarshalBinary decodes the binary encoding of ExtendedAttribute into e.
|
||||
|
@ -290,11 +264,11 @@ func (e *NameEntry) Len() int {
|
|||
}
|
||||
|
||||
// MarshalInto marshals e onto the end of the given Buffer.
|
||||
func (e *NameEntry) MarshalInto(b *Buffer) {
|
||||
b.AppendString(e.Filename)
|
||||
b.AppendString(e.Longname)
|
||||
func (e *NameEntry) MarshalInto(buf *Buffer) {
|
||||
buf.AppendString(e.Filename)
|
||||
buf.AppendString(e.Longname)
|
||||
|
||||
e.Attrs.MarshalInto(b)
|
||||
e.Attrs.MarshalInto(buf)
|
||||
}
|
||||
|
||||
// MarshalBinary returns e as the binary encoding of e.
|
||||
|
@ -307,16 +281,13 @@ func (e *NameEntry) MarshalBinary() ([]byte, error) {
|
|||
// UnmarshalFrom unmarshals an NameEntry from the given Buffer into e.
|
||||
//
|
||||
// NOTE: The values of fields not covered in the a.Flags are explicitly undefined.
|
||||
func (e *NameEntry) UnmarshalFrom(b *Buffer) (err error) {
|
||||
if e.Filename, err = b.ConsumeString(); err != nil {
|
||||
return err
|
||||
func (e *NameEntry) UnmarshalFrom(buf *Buffer) (err error) {
|
||||
*e = NameEntry{
|
||||
Filename: buf.ConsumeString(),
|
||||
Longname: buf.ConsumeString(),
|
||||
}
|
||||
|
||||
if e.Longname, err = b.ConsumeString(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return e.Attrs.UnmarshalFrom(b)
|
||||
return e.Attrs.UnmarshalFrom(buf)
|
||||
}
|
||||
|
||||
// UnmarshalBinary decodes the binary encoding of NameEntry into e.
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package filexfer
|
||||
package sshfx
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package filexfer
|
||||
package sshfx
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
|
@ -17,6 +17,7 @@ var (
|
|||
type Buffer struct {
|
||||
b []byte
|
||||
off int
|
||||
Err error
|
||||
}
|
||||
|
||||
// NewBuffer creates and initializes a new buffer using buf as its initial contents.
|
||||
|
@ -51,14 +52,17 @@ func (b *Buffer) Cap() int { return cap(b.b) }
|
|||
|
||||
// Reset resets the buffer to be empty, but it retains the underlying storage for use by future Appends.
|
||||
func (b *Buffer) Reset() {
|
||||
b.b = b.b[:0]
|
||||
b.off = 0
|
||||
*b = Buffer{
|
||||
b: b.b[:0],
|
||||
}
|
||||
}
|
||||
|
||||
// StartPacket resets and initializes the buffer to be ready to start marshaling a packet into.
|
||||
// It truncates the buffer, reserves space for uint32(length), then appends the given packetType and requestID.
|
||||
func (b *Buffer) StartPacket(packetType PacketType, requestID uint32) {
|
||||
b.b, b.off = append(b.b[:0], make([]byte, 4)...), 0
|
||||
*b = Buffer{
|
||||
b: append(b.b[:0], make([]byte, 4)...),
|
||||
}
|
||||
|
||||
b.AppendUint8(uint8(packetType))
|
||||
b.AppendUint32(requestID)
|
||||
|
@ -81,15 +85,21 @@ func (b *Buffer) Packet(payload []byte) (header, payloadPassThru []byte, err err
|
|||
}
|
||||
|
||||
// ConsumeUint8 consumes a single byte from the buffer.
|
||||
// If the buffer does not have enough data, it will return ErrShortPacket.
|
||||
func (b *Buffer) ConsumeUint8() (uint8, error) {
|
||||
// If the buffer does not have enough data, it will set Err to ErrShortPacket.
|
||||
func (b *Buffer) ConsumeUint8() uint8 {
|
||||
if b.Err != nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
if b.Len() < 1 {
|
||||
return 0, ErrShortPacket
|
||||
b.off = len(b.b)
|
||||
b.Err = ErrShortPacket
|
||||
return 0
|
||||
}
|
||||
|
||||
var v uint8
|
||||
v, b.off = b.b[b.off], b.off+1
|
||||
return v, nil
|
||||
return v
|
||||
}
|
||||
|
||||
// AppendUint8 appends a single byte into the buffer.
|
||||
|
@ -98,14 +108,9 @@ func (b *Buffer) AppendUint8(v uint8) {
|
|||
}
|
||||
|
||||
// ConsumeBool consumes a single byte from the buffer, and returns true if that byte is non-zero.
|
||||
// If the buffer does not have enough data, it will return ErrShortPacket.
|
||||
func (b *Buffer) ConsumeBool() (bool, error) {
|
||||
v, err := b.ConsumeUint8()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return v != 0, nil
|
||||
// If the buffer does not have enough data, it will set Err to ErrShortPacket.
|
||||
func (b *Buffer) ConsumeBool() bool {
|
||||
return b.ConsumeUint8() != 0
|
||||
}
|
||||
|
||||
// AppendBool appends a single bool into the buffer.
|
||||
|
@ -119,15 +124,21 @@ func (b *Buffer) AppendBool(v bool) {
|
|||
}
|
||||
|
||||
// ConsumeUint16 consumes a single uint16 from the buffer, in network byte order (big-endian).
|
||||
// If the buffer does not have enough data, it will return ErrShortPacket.
|
||||
func (b *Buffer) ConsumeUint16() (uint16, error) {
|
||||
// If the buffer does not have enough data, it will set Err to ErrShortPacket.
|
||||
func (b *Buffer) ConsumeUint16() uint16 {
|
||||
if b.Err != nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
if b.Len() < 2 {
|
||||
return 0, ErrShortPacket
|
||||
b.off = len(b.b)
|
||||
b.Err = ErrShortPacket
|
||||
return 0
|
||||
}
|
||||
|
||||
v := binary.BigEndian.Uint16(b.b[b.off:])
|
||||
b.off += 2
|
||||
return v, nil
|
||||
return v
|
||||
}
|
||||
|
||||
// AppendUint16 appends single uint16 into the buffer, in network byte order (big-endian).
|
||||
|
@ -146,15 +157,21 @@ func unmarshalUint32(b []byte) uint32 {
|
|||
}
|
||||
|
||||
// ConsumeUint32 consumes a single uint32 from the buffer, in network byte order (big-endian).
|
||||
// If the buffer does not have enough data, it will return ErrShortPacket.
|
||||
func (b *Buffer) ConsumeUint32() (uint32, error) {
|
||||
// If the buffer does not have enough data, it will set Err to ErrShortPacket.
|
||||
func (b *Buffer) ConsumeUint32() uint32 {
|
||||
if b.Err != nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
if b.Len() < 4 {
|
||||
return 0, ErrShortPacket
|
||||
b.off = len(b.b)
|
||||
b.Err = ErrShortPacket
|
||||
return 0
|
||||
}
|
||||
|
||||
v := binary.BigEndian.Uint32(b.b[b.off:])
|
||||
b.off += 4
|
||||
return v, nil
|
||||
return v
|
||||
}
|
||||
|
||||
// AppendUint32 appends a single uint32 into the buffer, in network byte order (big-endian).
|
||||
|
@ -167,16 +184,33 @@ func (b *Buffer) AppendUint32(v uint32) {
|
|||
)
|
||||
}
|
||||
|
||||
// ConsumeCount consumes a single uint32 count from the buffer, in network byte order (big-endian) as an int.
|
||||
// If the buffer does not have enough data, it will set Err to ErrShortPacket.
|
||||
func (b *Buffer) ConsumeCount() int {
|
||||
return int(b.ConsumeUint32())
|
||||
}
|
||||
|
||||
// AppendCount appends a single int length as a uint32 into the buffer, in network byte order (big-endian).
|
||||
func (b *Buffer) AppendCount(v int) {
|
||||
b.AppendUint32(uint32(v))
|
||||
}
|
||||
|
||||
// ConsumeUint64 consumes a single uint64 from the buffer, in network byte order (big-endian).
|
||||
// If the buffer does not have enough data, it will return ErrShortPacket.
|
||||
func (b *Buffer) ConsumeUint64() (uint64, error) {
|
||||
// If the buffer does not have enough data, it will set Err to ErrShortPacket.
|
||||
func (b *Buffer) ConsumeUint64() uint64 {
|
||||
if b.Err != nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
if b.Len() < 8 {
|
||||
return 0, ErrShortPacket
|
||||
b.off = len(b.b)
|
||||
b.Err = ErrShortPacket
|
||||
return 0
|
||||
}
|
||||
|
||||
v := binary.BigEndian.Uint64(b.b[b.off:])
|
||||
b.off += 8
|
||||
return v, nil
|
||||
return v
|
||||
}
|
||||
|
||||
// AppendUint64 appends a single uint64 into the buffer, in network byte order (big-endian).
|
||||
|
@ -194,14 +228,9 @@ func (b *Buffer) AppendUint64(v uint64) {
|
|||
}
|
||||
|
||||
// ConsumeInt64 consumes a single int64 from the buffer, in network byte order (big-endian) with two’s complement.
|
||||
// If the buffer does not have enough data, it will return ErrShortPacket.
|
||||
func (b *Buffer) ConsumeInt64() (int64, error) {
|
||||
u, err := b.ConsumeUint64()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return int64(u), err
|
||||
// If the buffer does not have enough data, it will set Err to ErrShortPacket.
|
||||
func (b *Buffer) ConsumeInt64() int64 {
|
||||
return int64(b.ConsumeUint64())
|
||||
}
|
||||
|
||||
// AppendInt64 appends a single int64 into the buffer, in network byte order (big-endian) with two’s complement.
|
||||
|
@ -211,29 +240,52 @@ func (b *Buffer) AppendInt64(v int64) {
|
|||
|
||||
// ConsumeByteSlice consumes a single string of raw binary data from the buffer.
|
||||
// A string is a uint32 length, followed by that number of raw bytes.
|
||||
// If the buffer does not have enough data, or defines a length larger than available, it will return ErrShortPacket.
|
||||
// If the buffer does not have enough data, or defines a length larger than available, it will set Err to ErrShortPacket.
|
||||
//
|
||||
// The returned slice aliases the buffer contents, and is valid only as long as the buffer is not reused
|
||||
// (that is, only until the next call to Reset, PutLength, StartPacket, or UnmarshalBinary).
|
||||
//
|
||||
// In no case will any Consume calls return overlapping slice aliases,
|
||||
// and Append calls are guaranteed to not disturb this slice alias.
|
||||
func (b *Buffer) ConsumeByteSlice() ([]byte, error) {
|
||||
length, err := b.ConsumeUint32()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
func (b *Buffer) ConsumeByteSlice() []byte {
|
||||
length := int(b.ConsumeUint32())
|
||||
if b.Err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if b.Len() < int(length) {
|
||||
return nil, ErrShortPacket
|
||||
if b.Len() < length || length < 0 {
|
||||
b.off = len(b.b)
|
||||
b.Err = ErrShortPacket
|
||||
return nil
|
||||
}
|
||||
|
||||
v := b.b[b.off:]
|
||||
if len(v) > int(length) {
|
||||
if len(v) > length || cap(v) > length {
|
||||
v = v[:length:length]
|
||||
}
|
||||
b.off += int(length)
|
||||
return v, nil
|
||||
return v
|
||||
}
|
||||
|
||||
// ConsumeByteSliceCopy consumes a single string of raw binary data as a copy from the buffer.
|
||||
// A string is a uint32 length, followed by that number of raw bytes.
|
||||
// If the buffer does not have enough data, or defines a length larger than available, it will set Err to ErrShortPacket.
|
||||
//
|
||||
// The returned slice does not alias any buffer contents,
|
||||
// and will therefore be valid even if the buffer is later reused.
|
||||
//
|
||||
// If hint has sufficient capacity to hold the data, it will be reused and overwritten,
|
||||
// otherwise a new backing slice will be allocated and returned.
|
||||
func (b *Buffer) ConsumeByteSliceCopy(hint []byte) []byte {
|
||||
data := b.ConsumeByteSlice()
|
||||
|
||||
if grow := len(data) - len(hint); grow > 0 {
|
||||
hint = append(hint, make([]byte, grow)...)
|
||||
}
|
||||
|
||||
n := copy(hint, data)
|
||||
hint = hint[:n]
|
||||
return hint
|
||||
}
|
||||
|
||||
// AppendByteSlice appends a single string of raw binary data into the buffer.
|
||||
|
@ -245,17 +297,12 @@ func (b *Buffer) AppendByteSlice(v []byte) {
|
|||
|
||||
// ConsumeString consumes a single string of binary data from the buffer.
|
||||
// A string is a uint32 length, followed by that number of raw bytes.
|
||||
// If the buffer does not have enough data, or defines a length larger than available, it will return ErrShortPacket.
|
||||
// If the buffer does not have enough data, or defines a length larger than available, it will set Err to ErrShortPacket.
|
||||
//
|
||||
// NOTE: Go implicitly assumes that strings contain UTF-8 encoded data.
|
||||
// All caveats on using arbitrary binary data in Go strings applies.
|
||||
func (b *Buffer) ConsumeString() (string, error) {
|
||||
v, err := b.ConsumeByteSlice()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return string(v), nil
|
||||
func (b *Buffer) ConsumeString() string {
|
||||
return string(b.ConsumeByteSlice())
|
||||
}
|
||||
|
||||
// AppendString appends a single string of binary data into the buffer.
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package filexfer
|
||||
package sshfx
|
||||
|
||||
import (
|
||||
"encoding"
|
||||
|
@ -86,8 +86,9 @@ func (p *ExtendedPacket) MarshalPacket(reqid uint32, b []byte) (header, payload
|
|||
// If the extension has not been registered, then a new Buffer will be allocated.
|
||||
// Then the request-specific-data will be unmarshaled from the rest of the buffer.
|
||||
func (p *ExtendedPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
if p.ExtendedRequest, err = buf.ConsumeString(); err != nil {
|
||||
return err
|
||||
p.ExtendedRequest = buf.ConsumeString()
|
||||
if buf.Err != nil {
|
||||
return buf.Err
|
||||
}
|
||||
|
||||
if p.Data == nil {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package filexfer
|
||||
package sshfx
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
@ -20,9 +20,9 @@ func (d *testExtendedData) MarshalBinary() ([]byte, error) {
|
|||
func (d *testExtendedData) UnmarshalBinary(data []byte) error {
|
||||
buf := NewBuffer(data)
|
||||
|
||||
v, err := buf.ConsumeUint8()
|
||||
if err != nil {
|
||||
return err
|
||||
v := buf.ConsumeUint8()
|
||||
if buf.Err != nil {
|
||||
return buf.Err
|
||||
}
|
||||
|
||||
d.value = v ^ 0x2a
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package filexfer
|
||||
package sshfx
|
||||
|
||||
// ExtensionPair defines the extension-pair type defined in draft-ietf-secsh-filexfer-13.
|
||||
// This type is backwards-compatible with how draft-ietf-secsh-filexfer-02 defines extensions.
|
||||
|
@ -29,15 +29,12 @@ func (e *ExtensionPair) MarshalBinary() ([]byte, error) {
|
|||
|
||||
// UnmarshalFrom unmarshals an ExtensionPair from the given Buffer into e.
|
||||
func (e *ExtensionPair) UnmarshalFrom(buf *Buffer) (err error) {
|
||||
if e.Name, err = buf.ConsumeString(); err != nil {
|
||||
return err
|
||||
*e = ExtensionPair{
|
||||
Name: buf.ConsumeString(),
|
||||
Data: buf.ConsumeString(),
|
||||
}
|
||||
|
||||
if e.Data, err = buf.ConsumeString(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
return buf.Err
|
||||
}
|
||||
|
||||
// UnmarshalBinary decodes the binary encoding of ExtensionPair into e.
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package filexfer
|
||||
package sshfx
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// Package filexfer implements the wire encoding for secsh-filexfer as described in https://tools.ietf.org/html/draft-ietf-secsh-filexfer-02
|
||||
package filexfer
|
||||
// Package sshfx implements the wire encoding for secsh-filexfer as described in https://filezilla-project.org/specs/draft-ietf-secsh-filexfer-02.txt
|
||||
package sshfx
|
||||
|
||||
// PacketMarshaller narrowly defines packets that will only be transmitted.
|
||||
//
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package filexfer
|
||||
package sshfx
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
@ -10,7 +10,7 @@ type Status uint32
|
|||
// Defines the various SSH_FX_* values.
|
||||
const (
|
||||
// see draft-ietf-secsh-filexfer-02
|
||||
// https://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#section-7
|
||||
// https://filezilla-project.org/specs/draft-ietf-secsh-filexfer-02.txt#section-7
|
||||
StatusOK = Status(iota)
|
||||
StatusEOF
|
||||
StatusNoSuchFile
|
||||
|
@ -21,28 +21,28 @@ const (
|
|||
StatusConnectionLost
|
||||
StatusOPUnsupported
|
||||
|
||||
// https://tools.ietf.org/html/draft-ietf-secsh-filexfer-03#section-7
|
||||
// https://filezilla-project.org/specs/draft-ietf-secsh-filexfer-03.txt#section-7
|
||||
StatusV4InvalidHandle
|
||||
StatusV4NoSuchPath
|
||||
StatusV4FileAlreadyExists
|
||||
StatusV4WriteProtect
|
||||
|
||||
// https://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-7
|
||||
// https://filezilla-project.org/specs/draft-ietf-secsh-filexfer-04.txt#section-7
|
||||
StatusV4NoMedia
|
||||
|
||||
// https://tools.ietf.org/html/draft-ietf-secsh-filexfer-05#section-7
|
||||
// https://filezilla-project.org/specs/draft-ietf-secsh-filexfer-05.txt#section-7
|
||||
StatusV5NoSpaceOnFilesystem
|
||||
StatusV5QuotaExceeded
|
||||
StatusV5UnknownPrincipal
|
||||
StatusV5LockConflict
|
||||
|
||||
// https://tools.ietf.org/html/draft-ietf-secsh-filexfer-06#section-8
|
||||
// https://filezilla-project.org/specs/draft-ietf-secsh-filexfer-06.txt#section-8
|
||||
StatusV6DirNotEmpty
|
||||
StatusV6NotADirectory
|
||||
StatusV6InvalidFilename
|
||||
StatusV6LinkLoop
|
||||
|
||||
// https://tools.ietf.org/html/draft-ietf-secsh-filexfer-07#section-8
|
||||
// https://filezilla-project.org/specs/draft-ietf-secsh-filexfer-07.txt#section-8
|
||||
StatusV6CannotDelete
|
||||
StatusV6InvalidParameter
|
||||
StatusV6FileIsADirectory
|
||||
|
@ -50,10 +50,10 @@ const (
|
|||
StatusV6ByteRangeLockRefused
|
||||
StatusV6DeletePending
|
||||
|
||||
// https://tools.ietf.org/html/draft-ietf-secsh-filexfer-08#section-8.1
|
||||
// https://filezilla-project.org/specs/draft-ietf-secsh-filexfer-08.txt#section-8.1
|
||||
StatusV6FileCorrupt
|
||||
|
||||
// https://tools.ietf.org/html/draft-ietf-secsh-filexfer-10#section-9.1
|
||||
// https://filezilla-project.org/specs/draft-ietf-secsh-filexfer-10.txt#section-9.1
|
||||
StatusV6OwnerInvalid
|
||||
StatusV6GroupInvalid
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package filexfer
|
||||
package sshfx
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
|
@ -9,7 +9,7 @@ import (
|
|||
"testing"
|
||||
)
|
||||
|
||||
// This string data is copied verbatim from https://tools.ietf.org/html/draft-ietf-secsh-filexfer-13
|
||||
// This string data is copied verbatim from https://filezilla-project.org/specs/draft-ietf-secsh-filexfer-13.txt
|
||||
var fxStandardsText = `
|
||||
SSH_FX_OK 0
|
||||
SSH_FX_EOF 1
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package filexfer
|
||||
package sshfx
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
@ -9,7 +9,7 @@ type PacketType uint8
|
|||
|
||||
// Request packet types.
|
||||
const (
|
||||
// https://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#section-3
|
||||
// https://filezilla-project.org/specs/draft-ietf-secsh-filexfer-02.txt#section-3
|
||||
PacketTypeInit = PacketType(iota + 1)
|
||||
PacketTypeVersion
|
||||
PacketTypeOpen
|
||||
|
@ -31,17 +31,17 @@ const (
|
|||
PacketTypeReadLink
|
||||
PacketTypeSymlink
|
||||
|
||||
// https://tools.ietf.org/html/draft-ietf-secsh-filexfer-07#section-3.3
|
||||
// https://filezilla-project.org/specs/draft-ietf-secsh-filexfer-07.txt#section-3.3
|
||||
PacketTypeV6Link
|
||||
|
||||
// https://tools.ietf.org/html/draft-ietf-secsh-filexfer-08#section-3.3
|
||||
// https://filezilla-project.org/specs/draft-ietf-secsh-filexfer-08.txt#section-3.3
|
||||
PacketTypeV6Block
|
||||
PacketTypeV6Unblock
|
||||
)
|
||||
|
||||
// Response packet types.
|
||||
const (
|
||||
// https://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#section-3
|
||||
// https://filezilla-project.org/specs/draft-ietf-secsh-filexfer-02.txt#section-3
|
||||
PacketTypeStatus = PacketType(iota + 101)
|
||||
PacketTypeHandle
|
||||
PacketTypeData
|
||||
|
@ -51,7 +51,7 @@ const (
|
|||
|
||||
// Extended packet types.
|
||||
const (
|
||||
// https://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#section-3
|
||||
// https://filezilla-project.org/specs/draft-ietf-secsh-filexfer-02.txt#section-3
|
||||
PacketTypeExtended = PacketType(iota + 200)
|
||||
PacketTypeExtendedReply
|
||||
)
|
||||
|
@ -122,3 +122,48 @@ func (f PacketType) String() string {
|
|||
return fmt.Sprintf("SSH_FXP_UNKNOWN(%d)", f)
|
||||
}
|
||||
}
|
||||
|
||||
func newPacketFromType(typ PacketType) (Packet, error) {
|
||||
switch typ {
|
||||
case PacketTypeOpen:
|
||||
return new(OpenPacket), nil
|
||||
case PacketTypeClose:
|
||||
return new(ClosePacket), nil
|
||||
case PacketTypeRead:
|
||||
return new(ReadPacket), nil
|
||||
case PacketTypeWrite:
|
||||
return new(WritePacket), nil
|
||||
case PacketTypeLStat:
|
||||
return new(LStatPacket), nil
|
||||
case PacketTypeFStat:
|
||||
return new(FStatPacket), nil
|
||||
case PacketTypeSetstat:
|
||||
return new(SetstatPacket), nil
|
||||
case PacketTypeFSetstat:
|
||||
return new(FSetstatPacket), nil
|
||||
case PacketTypeOpenDir:
|
||||
return new(OpenDirPacket), nil
|
||||
case PacketTypeReadDir:
|
||||
return new(ReadDirPacket), nil
|
||||
case PacketTypeRemove:
|
||||
return new(RemovePacket), nil
|
||||
case PacketTypeMkdir:
|
||||
return new(MkdirPacket), nil
|
||||
case PacketTypeRmdir:
|
||||
return new(RmdirPacket), nil
|
||||
case PacketTypeRealPath:
|
||||
return new(RealPathPacket), nil
|
||||
case PacketTypeStat:
|
||||
return new(StatPacket), nil
|
||||
case PacketTypeRename:
|
||||
return new(RenamePacket), nil
|
||||
case PacketTypeReadLink:
|
||||
return new(ReadLinkPacket), nil
|
||||
case PacketTypeSymlink:
|
||||
return new(SymlinkPacket), nil
|
||||
case PacketTypeExtended:
|
||||
return new(ExtendedPacket), nil
|
||||
default:
|
||||
return nil, fmt.Errorf("unexpected request packet type: %v", typ)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package filexfer
|
||||
package sshfx
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
|
@ -8,7 +8,8 @@ import (
|
|||
"testing"
|
||||
)
|
||||
|
||||
// This string data is copied verbatim from https://tools.ietf.org/html/draft-ietf-secsh-filexfer-13
|
||||
// This string data is copied verbatim from https://filezilla-project.org/specs/draft-ietf-secsh-filexfer-13.txt
|
||||
// except where commented that it was taken from a different source.
|
||||
var fxpStandardsText = `
|
||||
SSH_FXP_INIT 1
|
||||
SSH_FXP_VERSION 2
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package filexfer
|
||||
package sshfx
|
||||
|
||||
// ClosePacket defines the SSH_FXP_CLOSE packet.
|
||||
type ClosePacket struct {
|
||||
|
@ -27,18 +27,18 @@ func (p *ClosePacket) MarshalPacket(reqid uint32, b []byte) (header, payload []b
|
|||
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
||||
// It is assumed that the uint32(request-id) has already been consumed.
|
||||
func (p *ClosePacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
if p.Handle, err = buf.ConsumeString(); err != nil {
|
||||
return err
|
||||
*p = ClosePacket{
|
||||
Handle: buf.ConsumeString(),
|
||||
}
|
||||
|
||||
return nil
|
||||
return buf.Err
|
||||
}
|
||||
|
||||
// ReadPacket defines the SSH_FXP_READ packet.
|
||||
type ReadPacket struct {
|
||||
Handle string
|
||||
Offset uint64
|
||||
Len uint32
|
||||
Length uint32
|
||||
}
|
||||
|
||||
// Type returns the SSH_FXP_xy value associated with this packet type.
|
||||
|
@ -58,7 +58,7 @@ func (p *ReadPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []by
|
|||
buf.StartPacket(PacketTypeRead, reqid)
|
||||
buf.AppendString(p.Handle)
|
||||
buf.AppendUint64(p.Offset)
|
||||
buf.AppendUint32(p.Len)
|
||||
buf.AppendUint32(p.Length)
|
||||
|
||||
return buf.Packet(payload)
|
||||
}
|
||||
|
@ -66,19 +66,13 @@ func (p *ReadPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []by
|
|||
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
||||
// It is assumed that the uint32(request-id) has already been consumed.
|
||||
func (p *ReadPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
if p.Handle, err = buf.ConsumeString(); err != nil {
|
||||
return err
|
||||
*p = ReadPacket{
|
||||
Handle: buf.ConsumeString(),
|
||||
Offset: buf.ConsumeUint64(),
|
||||
Length: buf.ConsumeUint32(),
|
||||
}
|
||||
|
||||
if p.Offset, err = buf.ConsumeUint64(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if p.Len, err = buf.ConsumeUint32(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
return buf.Err
|
||||
}
|
||||
|
||||
// WritePacket defines the SSH_FXP_WRITE packet.
|
||||
|
@ -121,26 +115,13 @@ func (p *WritePacket) MarshalPacket(reqid uint32, b []byte) (header, payload []b
|
|||
//
|
||||
// This means this _does not_ alias any of the data buffer that is passed in.
|
||||
func (p *WritePacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
if p.Handle, err = buf.ConsumeString(); err != nil {
|
||||
return err
|
||||
*p = WritePacket{
|
||||
Handle: buf.ConsumeString(),
|
||||
Offset: buf.ConsumeUint64(),
|
||||
Data: buf.ConsumeByteSliceCopy(p.Data),
|
||||
}
|
||||
|
||||
if p.Offset, err = buf.ConsumeUint64(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
data, err := buf.ConsumeByteSlice()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(p.Data) < len(data) {
|
||||
p.Data = make([]byte, len(data))
|
||||
}
|
||||
|
||||
n := copy(p.Data, data)
|
||||
p.Data = p.Data[:n]
|
||||
return nil
|
||||
return buf.Err
|
||||
}
|
||||
|
||||
// FStatPacket defines the SSH_FXP_FSTAT packet.
|
||||
|
@ -170,11 +151,11 @@ func (p *FStatPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []b
|
|||
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
||||
// It is assumed that the uint32(request-id) has already been consumed.
|
||||
func (p *FStatPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
if p.Handle, err = buf.ConsumeString(); err != nil {
|
||||
return err
|
||||
*p = FStatPacket{
|
||||
Handle: buf.ConsumeString(),
|
||||
}
|
||||
|
||||
return nil
|
||||
return buf.Err
|
||||
}
|
||||
|
||||
// FSetstatPacket defines the SSH_FXP_FSETSTAT packet.
|
||||
|
@ -207,8 +188,8 @@ func (p *FSetstatPacket) MarshalPacket(reqid uint32, b []byte) (header, payload
|
|||
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
||||
// It is assumed that the uint32(request-id) has already been consumed.
|
||||
func (p *FSetstatPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
if p.Handle, err = buf.ConsumeString(); err != nil {
|
||||
return err
|
||||
*p = FSetstatPacket{
|
||||
Handle: buf.ConsumeString(),
|
||||
}
|
||||
|
||||
return p.Attrs.UnmarshalFrom(buf)
|
||||
|
@ -241,9 +222,9 @@ func (p *ReadDirPacket) MarshalPacket(reqid uint32, b []byte) (header, payload [
|
|||
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
||||
// It is assumed that the uint32(request-id) has already been consumed.
|
||||
func (p *ReadDirPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
if p.Handle, err = buf.ConsumeString(); err != nil {
|
||||
return err
|
||||
*p = ReadDirPacket{
|
||||
Handle: buf.ConsumeString(),
|
||||
}
|
||||
|
||||
return nil
|
||||
return buf.Err
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package filexfer
|
||||
package sshfx
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
@ -58,7 +58,7 @@ func TestReadPacket(t *testing.T) {
|
|||
p := &ReadPacket{
|
||||
Handle: "somehandle",
|
||||
Offset: offset,
|
||||
Len: length,
|
||||
Length: length,
|
||||
}
|
||||
|
||||
buf, err := ComposePacket(p.MarshalPacket(id, nil))
|
||||
|
@ -94,8 +94,8 @@ func TestReadPacket(t *testing.T) {
|
|||
t.Errorf("UnmarshalPacketBody(): Offset was %x, but expected %x", p.Offset, offset)
|
||||
}
|
||||
|
||||
if p.Len != length {
|
||||
t.Errorf("UnmarshalPacketBody(): Len was %x, but expected %x", p.Len, length)
|
||||
if p.Length != length {
|
||||
t.Errorf("UnmarshalPacketBody(): Length was %x, but expected %x", p.Length, length)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package filexfer
|
||||
package sshfx
|
||||
|
||||
// InitPacket defines the SSH_FXP_INIT packet.
|
||||
type InitPacket struct {
|
||||
|
@ -33,8 +33,8 @@ func (p *InitPacket) MarshalBinary() ([]byte, error) {
|
|||
func (p *InitPacket) UnmarshalBinary(data []byte) (err error) {
|
||||
buf := NewBuffer(data)
|
||||
|
||||
if p.Version, err = buf.ConsumeUint32(); err != nil {
|
||||
return err
|
||||
*p = InitPacket{
|
||||
Version: buf.ConsumeUint32(),
|
||||
}
|
||||
|
||||
for buf.Len() > 0 {
|
||||
|
@ -46,7 +46,7 @@ func (p *InitPacket) UnmarshalBinary(data []byte) (err error) {
|
|||
p.Extensions = append(p.Extensions, &ext)
|
||||
}
|
||||
|
||||
return nil
|
||||
return buf.Err
|
||||
}
|
||||
|
||||
// VersionPacket defines the SSH_FXP_VERSION packet.
|
||||
|
@ -82,8 +82,8 @@ func (p *VersionPacket) MarshalBinary() ([]byte, error) {
|
|||
func (p *VersionPacket) UnmarshalBinary(data []byte) (err error) {
|
||||
buf := NewBuffer(data)
|
||||
|
||||
if p.Version, err = buf.ConsumeUint32(); err != nil {
|
||||
return err
|
||||
*p = VersionPacket{
|
||||
Version: buf.ConsumeUint32(),
|
||||
}
|
||||
|
||||
for buf.Len() > 0 {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package filexfer
|
||||
package sshfx
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package filexfer
|
||||
package sshfx
|
||||
|
||||
// SSH_FXF_* flags.
|
||||
const (
|
||||
|
@ -43,12 +43,9 @@ func (p *OpenPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []by
|
|||
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
||||
// It is assumed that the uint32(request-id) has already been consumed.
|
||||
func (p *OpenPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
if p.Filename, err = buf.ConsumeString(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if p.PFlags, err = buf.ConsumeUint32(); err != nil {
|
||||
return err
|
||||
*p = OpenPacket{
|
||||
Filename: buf.ConsumeString(),
|
||||
PFlags: buf.ConsumeUint32(),
|
||||
}
|
||||
|
||||
return p.Attrs.UnmarshalFrom(buf)
|
||||
|
@ -81,9 +78,9 @@ func (p *OpenDirPacket) MarshalPacket(reqid uint32, b []byte) (header, payload [
|
|||
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
||||
// It is assumed that the uint32(request-id) has already been consumed.
|
||||
func (p *OpenDirPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
if p.Path, err = buf.ConsumeString(); err != nil {
|
||||
return err
|
||||
*p = OpenDirPacket{
|
||||
Path: buf.ConsumeString(),
|
||||
}
|
||||
|
||||
return nil
|
||||
return buf.Err
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package filexfer
|
||||
package sshfx
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
|
|
@ -60,11 +60,11 @@ func (ep *FSyncExtendedPacket) MarshalBinary() ([]byte, error) {
|
|||
|
||||
// UnmarshalFrom decodes the fsync@openssh.com extended packet-specific data from buf.
|
||||
func (ep *FSyncExtendedPacket) UnmarshalFrom(buf *sshfx.Buffer) (err error) {
|
||||
if ep.Handle, err = buf.ConsumeString(); err != nil {
|
||||
return err
|
||||
*ep = FSyncExtendedPacket{
|
||||
Handle: buf.ConsumeString(),
|
||||
}
|
||||
|
||||
return nil
|
||||
return buf.Err
|
||||
}
|
||||
|
||||
// UnmarshalBinary decodes the fsync@openssh.com extended packet-specific data into ep.
|
||||
|
|
|
@ -62,15 +62,12 @@ func (ep *HardlinkExtendedPacket) MarshalBinary() ([]byte, error) {
|
|||
|
||||
// UnmarshalFrom decodes the hardlink@openssh.com extended packet-specific data from buf.
|
||||
func (ep *HardlinkExtendedPacket) UnmarshalFrom(buf *sshfx.Buffer) (err error) {
|
||||
if ep.OldPath, err = buf.ConsumeString(); err != nil {
|
||||
return err
|
||||
*ep = HardlinkExtendedPacket{
|
||||
OldPath: buf.ConsumeString(),
|
||||
NewPath: buf.ConsumeString(),
|
||||
}
|
||||
|
||||
if ep.NewPath, err = buf.ConsumeString(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
return buf.Err
|
||||
}
|
||||
|
||||
// UnmarshalBinary decodes the hardlink@openssh.com extended packet-specific data into ep.
|
||||
|
|
|
@ -4,38 +4,38 @@ import (
|
|||
sshfx "github.com/pkg/sftp/internal/encoding/ssh/filexfer"
|
||||
)
|
||||
|
||||
const extensionPosixRename = "posix-rename@openssh.com"
|
||||
const extensionPOSIXRename = "posix-rename@openssh.com"
|
||||
|
||||
// RegisterExtensionPosixRename registers the "posix-rename@openssh.com" extended packet with the encoding/ssh/filexfer package.
|
||||
func RegisterExtensionPosixRename() {
|
||||
sshfx.RegisterExtendedPacketType(extensionPosixRename, func() sshfx.ExtendedData {
|
||||
return new(PosixRenameExtendedPacket)
|
||||
// RegisterExtensionPOSIXRename registers the "posix-rename@openssh.com" extended packet with the encoding/ssh/filexfer package.
|
||||
func RegisterExtensionPOSIXRename() {
|
||||
sshfx.RegisterExtendedPacketType(extensionPOSIXRename, func() sshfx.ExtendedData {
|
||||
return new(POSIXRenameExtendedPacket)
|
||||
})
|
||||
}
|
||||
|
||||
// ExtensionPosixRename returns an ExtensionPair suitable to append into an sshfx.InitPacket or sshfx.VersionPacket.
|
||||
func ExtensionPosixRename() *sshfx.ExtensionPair {
|
||||
// ExtensionPOSIXRename returns an ExtensionPair suitable to append into an sshfx.InitPacket or sshfx.VersionPacket.
|
||||
func ExtensionPOSIXRename() *sshfx.ExtensionPair {
|
||||
return &sshfx.ExtensionPair{
|
||||
Name: extensionPosixRename,
|
||||
Name: extensionPOSIXRename,
|
||||
Data: "1",
|
||||
}
|
||||
}
|
||||
|
||||
// PosixRenameExtendedPacket defines the posix-rename@openssh.com extend packet.
|
||||
type PosixRenameExtendedPacket struct {
|
||||
// POSIXRenameExtendedPacket defines the posix-rename@openssh.com extend packet.
|
||||
type POSIXRenameExtendedPacket struct {
|
||||
OldPath string
|
||||
NewPath string
|
||||
}
|
||||
|
||||
// Type returns the SSH_FXP_EXTENDED packet type.
|
||||
func (ep *PosixRenameExtendedPacket) Type() sshfx.PacketType {
|
||||
func (ep *POSIXRenameExtendedPacket) Type() sshfx.PacketType {
|
||||
return sshfx.PacketTypeExtended
|
||||
}
|
||||
|
||||
// MarshalPacket returns ep as a two-part binary encoding of the full extended packet.
|
||||
func (ep *PosixRenameExtendedPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
||||
func (ep *POSIXRenameExtendedPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
||||
p := &sshfx.ExtendedPacket{
|
||||
ExtendedRequest: extensionPosixRename,
|
||||
ExtendedRequest: extensionPOSIXRename,
|
||||
|
||||
Data: ep,
|
||||
}
|
||||
|
@ -43,7 +43,7 @@ func (ep *PosixRenameExtendedPacket) MarshalPacket(reqid uint32, b []byte) (head
|
|||
}
|
||||
|
||||
// MarshalInto encodes ep into the binary encoding of the hardlink@openssh.com extended packet-specific data.
|
||||
func (ep *PosixRenameExtendedPacket) MarshalInto(buf *sshfx.Buffer) {
|
||||
func (ep *POSIXRenameExtendedPacket) MarshalInto(buf *sshfx.Buffer) {
|
||||
buf.AppendString(ep.OldPath)
|
||||
buf.AppendString(ep.NewPath)
|
||||
}
|
||||
|
@ -51,7 +51,7 @@ func (ep *PosixRenameExtendedPacket) MarshalInto(buf *sshfx.Buffer) {
|
|||
// MarshalBinary encodes ep into the binary encoding of the hardlink@openssh.com extended packet-specific data.
|
||||
//
|
||||
// NOTE: This _only_ encodes the packet-specific data, it does not encode the full extended packet.
|
||||
func (ep *PosixRenameExtendedPacket) MarshalBinary() ([]byte, error) {
|
||||
func (ep *POSIXRenameExtendedPacket) MarshalBinary() ([]byte, error) {
|
||||
// string(oldpath) + string(newpath)
|
||||
size := 4 + len(ep.OldPath) + 4 + len(ep.NewPath)
|
||||
|
||||
|
@ -61,19 +61,16 @@ func (ep *PosixRenameExtendedPacket) MarshalBinary() ([]byte, error) {
|
|||
}
|
||||
|
||||
// UnmarshalFrom decodes the hardlink@openssh.com extended packet-specific data from buf.
|
||||
func (ep *PosixRenameExtendedPacket) UnmarshalFrom(buf *sshfx.Buffer) (err error) {
|
||||
if ep.OldPath, err = buf.ConsumeString(); err != nil {
|
||||
return err
|
||||
func (ep *POSIXRenameExtendedPacket) UnmarshalFrom(buf *sshfx.Buffer) (err error) {
|
||||
*ep = POSIXRenameExtendedPacket{
|
||||
OldPath: buf.ConsumeString(),
|
||||
NewPath: buf.ConsumeString(),
|
||||
}
|
||||
|
||||
if ep.NewPath, err = buf.ConsumeString(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
return buf.Err
|
||||
}
|
||||
|
||||
// UnmarshalBinary decodes the hardlink@openssh.com extended packet-specific data into ep.
|
||||
func (ep *PosixRenameExtendedPacket) UnmarshalBinary(data []byte) (err error) {
|
||||
func (ep *POSIXRenameExtendedPacket) UnmarshalBinary(data []byte) (err error) {
|
||||
return ep.UnmarshalFrom(sshfx.NewBuffer(data))
|
||||
}
|
||||
|
|
|
@ -7,20 +7,20 @@ import (
|
|||
sshfx "github.com/pkg/sftp/internal/encoding/ssh/filexfer"
|
||||
)
|
||||
|
||||
var _ sshfx.PacketMarshaller = &PosixRenameExtendedPacket{}
|
||||
var _ sshfx.PacketMarshaller = &POSIXRenameExtendedPacket{}
|
||||
|
||||
func init() {
|
||||
RegisterExtensionPosixRename()
|
||||
RegisterExtensionPOSIXRename()
|
||||
}
|
||||
|
||||
func TestPosixRenameExtendedPacket(t *testing.T) {
|
||||
func TestPOSIXRenameExtendedPacket(t *testing.T) {
|
||||
const (
|
||||
id = 42
|
||||
oldpath = "/foo"
|
||||
newpath = "/bar"
|
||||
)
|
||||
|
||||
ep := &PosixRenameExtendedPacket{
|
||||
ep := &POSIXRenameExtendedPacket{
|
||||
OldPath: oldpath,
|
||||
NewPath: newpath,
|
||||
}
|
||||
|
@ -50,13 +50,13 @@ func TestPosixRenameExtendedPacket(t *testing.T) {
|
|||
t.Fatal("unexpected error:", err)
|
||||
}
|
||||
|
||||
if p.ExtendedRequest != extensionPosixRename {
|
||||
t.Errorf("UnmarshalPacketBody(): ExtendedRequest was %q, but expected %q", p.ExtendedRequest, extensionPosixRename)
|
||||
if p.ExtendedRequest != extensionPOSIXRename {
|
||||
t.Errorf("UnmarshalPacketBody(): ExtendedRequest was %q, but expected %q", p.ExtendedRequest, extensionPOSIXRename)
|
||||
}
|
||||
|
||||
ep, ok := p.Data.(*PosixRenameExtendedPacket)
|
||||
ep, ok := p.Data.(*POSIXRenameExtendedPacket)
|
||||
if !ok {
|
||||
t.Fatalf("UnmarshaledPacketBody(): Data was type %T, but expected *PosixRenameExtendedPacket", p.Data)
|
||||
t.Fatalf("UnmarshaledPacketBody(): Data was type %T, but expected *POSIXRenameExtendedPacket", p.Data)
|
||||
}
|
||||
|
||||
if ep.OldPath != oldpath {
|
||||
|
|
|
@ -61,11 +61,11 @@ func (ep *StatVFSExtendedPacket) MarshalBinary() ([]byte, error) {
|
|||
|
||||
// UnmarshalFrom decodes the statvfs@openssh.com extended packet-specific data into ep.
|
||||
func (ep *StatVFSExtendedPacket) UnmarshalFrom(buf *sshfx.Buffer) (err error) {
|
||||
if ep.Path, err = buf.ConsumeString(); err != nil {
|
||||
return err
|
||||
*ep = StatVFSExtendedPacket{
|
||||
Path: buf.ConsumeString(),
|
||||
}
|
||||
|
||||
return nil
|
||||
return buf.Err
|
||||
}
|
||||
|
||||
// UnmarshalBinary decodes the statvfs@openssh.com extended packet-specific data into ep.
|
||||
|
@ -130,11 +130,11 @@ func (ep *FStatVFSExtendedPacket) MarshalBinary() ([]byte, error) {
|
|||
|
||||
// UnmarshalFrom decodes the statvfs@openssh.com extended packet-specific data into ep.
|
||||
func (ep *FStatVFSExtendedPacket) UnmarshalFrom(buf *sshfx.Buffer) (err error) {
|
||||
if ep.Path, err = buf.ConsumeString(); err != nil {
|
||||
return err
|
||||
*ep = FStatVFSExtendedPacket{
|
||||
Path: buf.ConsumeString(),
|
||||
}
|
||||
|
||||
return nil
|
||||
return buf.Err
|
||||
}
|
||||
|
||||
// UnmarshalBinary decodes the statvfs@openssh.com extended packet-specific data into ep.
|
||||
|
@ -213,41 +213,21 @@ func (ep *StatVFSExtendedReplyPacket) MarshalBinary() ([]byte, error) {
|
|||
|
||||
// UnmarshalFrom decodes the fstatvfs@openssh.com extended reply packet-specific data into ep.
|
||||
func (ep *StatVFSExtendedReplyPacket) UnmarshalFrom(buf *sshfx.Buffer) (err error) {
|
||||
if ep.BlockSize, err = buf.ConsumeUint64(); err != nil {
|
||||
return err
|
||||
}
|
||||
if ep.FragmentSize, err = buf.ConsumeUint64(); err != nil {
|
||||
return err
|
||||
}
|
||||
if ep.Blocks, err = buf.ConsumeUint64(); err != nil {
|
||||
return err
|
||||
}
|
||||
if ep.BlocksFree, err = buf.ConsumeUint64(); err != nil {
|
||||
return err
|
||||
}
|
||||
if ep.BlocksAvail, err = buf.ConsumeUint64(); err != nil {
|
||||
return err
|
||||
}
|
||||
if ep.Files, err = buf.ConsumeUint64(); err != nil {
|
||||
return err
|
||||
}
|
||||
if ep.FilesFree, err = buf.ConsumeUint64(); err != nil {
|
||||
return err
|
||||
}
|
||||
if ep.FilesAvail, err = buf.ConsumeUint64(); err != nil {
|
||||
return err
|
||||
}
|
||||
if ep.FilesystemID, err = buf.ConsumeUint64(); err != nil {
|
||||
return err
|
||||
}
|
||||
if ep.MountFlags, err = buf.ConsumeUint64(); err != nil {
|
||||
return err
|
||||
}
|
||||
if ep.MaxNameLength, err = buf.ConsumeUint64(); err != nil {
|
||||
return err
|
||||
*ep = StatVFSExtendedReplyPacket{
|
||||
BlockSize: buf.ConsumeUint64(),
|
||||
FragmentSize: buf.ConsumeUint64(),
|
||||
Blocks: buf.ConsumeUint64(),
|
||||
BlocksFree: buf.ConsumeUint64(),
|
||||
BlocksAvail: buf.ConsumeUint64(),
|
||||
Files: buf.ConsumeUint64(),
|
||||
FilesFree: buf.ConsumeUint64(),
|
||||
FilesAvail: buf.ConsumeUint64(),
|
||||
FilesystemID: buf.ConsumeUint64(),
|
||||
MountFlags: buf.ConsumeUint64(),
|
||||
MaxNameLength: buf.ConsumeUint64(),
|
||||
}
|
||||
|
||||
return nil
|
||||
return buf.Err
|
||||
}
|
||||
|
||||
// UnmarshalBinary decodes the fstatvfs@openssh.com extended reply packet-specific data into ep.
|
||||
|
|
|
@ -1,59 +1,13 @@
|
|||
package filexfer
|
||||
package sshfx
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
// smallBufferSize is an initial allocation minimal capacity.
|
||||
const smallBufferSize = 64
|
||||
|
||||
func newPacketFromType(typ PacketType) (Packet, error) {
|
||||
switch typ {
|
||||
case PacketTypeOpen:
|
||||
return new(OpenPacket), nil
|
||||
case PacketTypeClose:
|
||||
return new(ClosePacket), nil
|
||||
case PacketTypeRead:
|
||||
return new(ReadPacket), nil
|
||||
case PacketTypeWrite:
|
||||
return new(WritePacket), nil
|
||||
case PacketTypeLStat:
|
||||
return new(LStatPacket), nil
|
||||
case PacketTypeFStat:
|
||||
return new(FStatPacket), nil
|
||||
case PacketTypeSetstat:
|
||||
return new(SetstatPacket), nil
|
||||
case PacketTypeFSetstat:
|
||||
return new(FSetstatPacket), nil
|
||||
case PacketTypeOpenDir:
|
||||
return new(OpenDirPacket), nil
|
||||
case PacketTypeReadDir:
|
||||
return new(ReadDirPacket), nil
|
||||
case PacketTypeRemove:
|
||||
return new(RemovePacket), nil
|
||||
case PacketTypeMkdir:
|
||||
return new(MkdirPacket), nil
|
||||
case PacketTypeRmdir:
|
||||
return new(RmdirPacket), nil
|
||||
case PacketTypeRealPath:
|
||||
return new(RealPathPacket), nil
|
||||
case PacketTypeStat:
|
||||
return new(StatPacket), nil
|
||||
case PacketTypeRename:
|
||||
return new(RenamePacket), nil
|
||||
case PacketTypeReadLink:
|
||||
return new(ReadLinkPacket), nil
|
||||
case PacketTypeSymlink:
|
||||
return new(SymlinkPacket), nil
|
||||
case PacketTypeExtended:
|
||||
return new(ExtendedPacket), nil
|
||||
default:
|
||||
return nil, fmt.Errorf("unexpected request packet type: %v", typ)
|
||||
}
|
||||
}
|
||||
|
||||
// RawPacket implements the general packet format from draft-ietf-secsh-filexfer-02
|
||||
//
|
||||
// RawPacket is intended for use in clients receiving responses,
|
||||
|
@ -63,7 +17,7 @@ func newPacketFromType(typ PacketType) (Packet, error) {
|
|||
// For servers expecting to receive arbitrary request packet types,
|
||||
// use RequestPacket.
|
||||
//
|
||||
// Defined in https://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#section-3
|
||||
// Defined in https://filezilla-project.org/specs/draft-ietf-secsh-filexfer-02.txt#section-3
|
||||
type RawPacket struct {
|
||||
PacketType PacketType
|
||||
RequestID uint32
|
||||
|
@ -110,19 +64,14 @@ func (p *RawPacket) MarshalBinary() ([]byte, error) {
|
|||
// The Data field will alias the passed in Buffer,
|
||||
// so the buffer passed in should not be reused before RawPacket.Reset().
|
||||
func (p *RawPacket) UnmarshalFrom(buf *Buffer) error {
|
||||
typ, err := buf.ConsumeUint8()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p.PacketType = PacketType(typ)
|
||||
|
||||
if p.RequestID, err = buf.ConsumeUint32(); err != nil {
|
||||
return err
|
||||
*p = RawPacket{
|
||||
PacketType: PacketType(buf.ConsumeUint8()),
|
||||
RequestID: buf.ConsumeUint32(),
|
||||
}
|
||||
|
||||
p.Data = *buf
|
||||
return nil
|
||||
|
||||
return buf.Err
|
||||
}
|
||||
|
||||
// UnmarshalBinary decodes a full raw packet out of the given data.
|
||||
|
@ -225,7 +174,7 @@ func (p *RawPacket) ReadFrom(r io.Reader, b []byte, maxPacketLength uint32) erro
|
|||
// where automatic unmarshaling of the packet body does not make sense,
|
||||
// use RawPacket.
|
||||
//
|
||||
// Defined in https://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#section-3
|
||||
// Defined in https://filezilla-project.org/specs/draft-ietf-secsh-filexfer-02.txt#section-3
|
||||
type RequestPacket struct {
|
||||
RequestID uint32
|
||||
|
||||
|
@ -268,18 +217,19 @@ func (p *RequestPacket) MarshalBinary() ([]byte, error) {
|
|||
// The Request field may alias the passed in Buffer, (e.g. SSH_FXP_WRITE),
|
||||
// so the buffer passed in should not be reused before RequestPacket.Reset().
|
||||
func (p *RequestPacket) UnmarshalFrom(buf *Buffer) error {
|
||||
typ, err := buf.ConsumeUint8()
|
||||
typ := PacketType(buf.ConsumeUint8())
|
||||
if buf.Err != nil {
|
||||
return buf.Err
|
||||
}
|
||||
|
||||
req, err := newPacketFromType(typ)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p.Request, err = newPacketFromType(PacketType(typ))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if p.RequestID, err = buf.ConsumeUint32(); err != nil {
|
||||
return err
|
||||
*p = RequestPacket{
|
||||
RequestID: buf.ConsumeUint32(),
|
||||
Request: req,
|
||||
}
|
||||
|
||||
return p.Request.UnmarshalPacketBody(buf)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package filexfer
|
||||
package sshfx
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package filexfer
|
||||
package sshfx
|
||||
|
||||
// LStatPacket defines the SSH_FXP_LSTAT packet.
|
||||
type LStatPacket struct {
|
||||
|
@ -27,11 +27,11 @@ func (p *LStatPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []b
|
|||
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
||||
// It is assumed that the uint32(request-id) has already been consumed.
|
||||
func (p *LStatPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
if p.Path, err = buf.ConsumeString(); err != nil {
|
||||
return err
|
||||
*p = LStatPacket{
|
||||
Path: buf.ConsumeString(),
|
||||
}
|
||||
|
||||
return nil
|
||||
return buf.Err
|
||||
}
|
||||
|
||||
// SetstatPacket defines the SSH_FXP_SETSTAT packet.
|
||||
|
@ -64,8 +64,8 @@ func (p *SetstatPacket) MarshalPacket(reqid uint32, b []byte) (header, payload [
|
|||
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
||||
// It is assumed that the uint32(request-id) has already been consumed.
|
||||
func (p *SetstatPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
if p.Path, err = buf.ConsumeString(); err != nil {
|
||||
return err
|
||||
*p = SetstatPacket{
|
||||
Path: buf.ConsumeString(),
|
||||
}
|
||||
|
||||
return p.Attrs.UnmarshalFrom(buf)
|
||||
|
@ -98,11 +98,11 @@ func (p *RemovePacket) MarshalPacket(reqid uint32, b []byte) (header, payload []
|
|||
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
||||
// It is assumed that the uint32(request-id) has already been consumed.
|
||||
func (p *RemovePacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
if p.Path, err = buf.ConsumeString(); err != nil {
|
||||
return err
|
||||
*p = RemovePacket{
|
||||
Path: buf.ConsumeString(),
|
||||
}
|
||||
|
||||
return nil
|
||||
return buf.Err
|
||||
}
|
||||
|
||||
// MkdirPacket defines the SSH_FXP_MKDIR packet.
|
||||
|
@ -135,8 +135,8 @@ func (p *MkdirPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []b
|
|||
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
||||
// It is assumed that the uint32(request-id) has already been consumed.
|
||||
func (p *MkdirPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
if p.Path, err = buf.ConsumeString(); err != nil {
|
||||
return err
|
||||
*p = MkdirPacket{
|
||||
Path: buf.ConsumeString(),
|
||||
}
|
||||
|
||||
return p.Attrs.UnmarshalFrom(buf)
|
||||
|
@ -169,11 +169,11 @@ func (p *RmdirPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []b
|
|||
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
||||
// It is assumed that the uint32(request-id) has already been consumed.
|
||||
func (p *RmdirPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
if p.Path, err = buf.ConsumeString(); err != nil {
|
||||
return err
|
||||
*p = RmdirPacket{
|
||||
Path: buf.ConsumeString(),
|
||||
}
|
||||
|
||||
return nil
|
||||
return buf.Err
|
||||
}
|
||||
|
||||
// RealPathPacket defines the SSH_FXP_REALPATH packet.
|
||||
|
@ -203,11 +203,11 @@ func (p *RealPathPacket) MarshalPacket(reqid uint32, b []byte) (header, payload
|
|||
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
||||
// It is assumed that the uint32(request-id) has already been consumed.
|
||||
func (p *RealPathPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
if p.Path, err = buf.ConsumeString(); err != nil {
|
||||
return err
|
||||
*p = RealPathPacket{
|
||||
Path: buf.ConsumeString(),
|
||||
}
|
||||
|
||||
return nil
|
||||
return buf.Err
|
||||
}
|
||||
|
||||
// StatPacket defines the SSH_FXP_STAT packet.
|
||||
|
@ -237,11 +237,11 @@ func (p *StatPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []by
|
|||
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
||||
// It is assumed that the uint32(request-id) has already been consumed.
|
||||
func (p *StatPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
if p.Path, err = buf.ConsumeString(); err != nil {
|
||||
return err
|
||||
*p = StatPacket{
|
||||
Path: buf.ConsumeString(),
|
||||
}
|
||||
|
||||
return nil
|
||||
return buf.Err
|
||||
}
|
||||
|
||||
// RenamePacket defines the SSH_FXP_RENAME packet.
|
||||
|
@ -274,15 +274,12 @@ func (p *RenamePacket) MarshalPacket(reqid uint32, b []byte) (header, payload []
|
|||
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
||||
// It is assumed that the uint32(request-id) has already been consumed.
|
||||
func (p *RenamePacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
if p.OldPath, err = buf.ConsumeString(); err != nil {
|
||||
return err
|
||||
*p = RenamePacket{
|
||||
OldPath: buf.ConsumeString(),
|
||||
NewPath: buf.ConsumeString(),
|
||||
}
|
||||
|
||||
if p.NewPath, err = buf.ConsumeString(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
return buf.Err
|
||||
}
|
||||
|
||||
// ReadLinkPacket defines the SSH_FXP_READLINK packet.
|
||||
|
@ -312,11 +309,11 @@ func (p *ReadLinkPacket) MarshalPacket(reqid uint32, b []byte) (header, payload
|
|||
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
||||
// It is assumed that the uint32(request-id) has already been consumed.
|
||||
func (p *ReadLinkPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
if p.Path, err = buf.ConsumeString(); err != nil {
|
||||
return err
|
||||
*p = ReadLinkPacket{
|
||||
Path: buf.ConsumeString(),
|
||||
}
|
||||
|
||||
return nil
|
||||
return buf.Err
|
||||
}
|
||||
|
||||
// SymlinkPacket defines the SSH_FXP_SYMLINK packet.
|
||||
|
@ -355,14 +352,11 @@ func (p *SymlinkPacket) MarshalPacket(reqid uint32, b []byte) (header, payload [
|
|||
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
||||
// It is assumed that the uint32(request-id) has already been consumed.
|
||||
func (p *SymlinkPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
*p = SymlinkPacket{
|
||||
// Arguments were inadvertently reversed.
|
||||
if p.TargetPath, err = buf.ConsumeString(); err != nil {
|
||||
return err
|
||||
TargetPath: buf.ConsumeString(),
|
||||
LinkPath: buf.ConsumeString(),
|
||||
}
|
||||
|
||||
if p.LinkPath, err = buf.ConsumeString(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
return buf.Err
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package filexfer
|
||||
package sshfx
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package filexfer
|
||||
package sshfx
|
||||
|
||||
// FileMode represents a file’s mode and permission bits.
|
||||
// The bits are defined according to POSIX standards,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package filexfer
|
||||
package sshfx
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
@ -6,7 +6,7 @@ import (
|
|||
|
||||
// StatusPacket defines the SSH_FXP_STATUS packet.
|
||||
//
|
||||
// Specified in https://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#section-7
|
||||
// Specified in https://filezilla-project.org/specs/draft-ietf-secsh-filexfer-02.txt#section-7
|
||||
type StatusPacket struct {
|
||||
StatusCode Status
|
||||
ErrorMessage string
|
||||
|
@ -19,7 +19,7 @@ func (p *StatusPacket) Error() string {
|
|||
return "sftp: " + p.StatusCode.String()
|
||||
}
|
||||
|
||||
return fmt.Sprintf("sftp: %q (%s)", p.ErrorMessage, p.StatusCode)
|
||||
return fmt.Sprintf("sftp: %s: %q", p.StatusCode, p.ErrorMessage)
|
||||
}
|
||||
|
||||
// Is returns true if target is a StatusPacket with the same StatusCode,
|
||||
|
@ -57,21 +57,13 @@ func (p *StatusPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []
|
|||
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
||||
// It is assumed that the uint32(request-id) has already been consumed.
|
||||
func (p *StatusPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
statusCode, err := buf.ConsumeUint32()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
p.StatusCode = Status(statusCode)
|
||||
|
||||
if p.ErrorMessage, err = buf.ConsumeString(); err != nil {
|
||||
return err
|
||||
*p = StatusPacket{
|
||||
StatusCode: Status(buf.ConsumeUint32()),
|
||||
ErrorMessage: buf.ConsumeString(),
|
||||
LanguageTag: buf.ConsumeString(),
|
||||
}
|
||||
|
||||
if p.LanguageTag, err = buf.ConsumeString(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
return buf.Err
|
||||
}
|
||||
|
||||
// HandlePacket defines the SSH_FXP_HANDLE packet.
|
||||
|
@ -101,11 +93,11 @@ func (p *HandlePacket) MarshalPacket(reqid uint32, b []byte) (header, payload []
|
|||
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
||||
// It is assumed that the uint32(request-id) has already been consumed.
|
||||
func (p *HandlePacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
if p.Handle, err = buf.ConsumeString(); err != nil {
|
||||
return err
|
||||
*p = HandlePacket{
|
||||
Handle: buf.ConsumeString(),
|
||||
}
|
||||
|
||||
return nil
|
||||
return buf.Err
|
||||
}
|
||||
|
||||
// DataPacket defines the SSH_FXP_DATA packet.
|
||||
|
@ -143,18 +135,11 @@ func (p *DataPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []by
|
|||
//
|
||||
// This means this _does not_ alias any of the data buffer that is passed in.
|
||||
func (p *DataPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
data, err := buf.ConsumeByteSlice()
|
||||
if err != nil {
|
||||
return err
|
||||
*p = DataPacket{
|
||||
Data: buf.ConsumeByteSliceCopy(p.Data),
|
||||
}
|
||||
|
||||
if len(p.Data) < len(data) {
|
||||
p.Data = make([]byte, len(data))
|
||||
}
|
||||
|
||||
n := copy(p.Data, data)
|
||||
p.Data = p.Data[:n]
|
||||
return nil
|
||||
return buf.Err
|
||||
}
|
||||
|
||||
// NamePacket defines the SSH_FXP_NAME packet.
|
||||
|
@ -193,14 +178,16 @@ func (p *NamePacket) MarshalPacket(reqid uint32, b []byte) (header, payload []by
|
|||
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
||||
// It is assumed that the uint32(request-id) has already been consumed.
|
||||
func (p *NamePacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
count, err := buf.ConsumeUint32()
|
||||
if err != nil {
|
||||
return err
|
||||
count := buf.ConsumeCount()
|
||||
if buf.Err != nil {
|
||||
return buf.Err
|
||||
}
|
||||
|
||||
p.Entries = make([]*NameEntry, 0, count)
|
||||
*p = NamePacket{
|
||||
Entries: make([]*NameEntry, 0, count),
|
||||
}
|
||||
|
||||
for i := uint32(0); i < count; i++ {
|
||||
for i := 0; i < count; i++ {
|
||||
var e NameEntry
|
||||
if err := e.UnmarshalFrom(buf); err != nil {
|
||||
return err
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package filexfer
|
||||
package sshfx
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
|
|
@ -42,41 +42,41 @@ func TestRunLsWithLicensesFileWithOSLookup(t *testing.T) {
|
|||
}
|
||||
|
||||
/*
|
||||
The format of the `longname' field is unspecified by this protocol.
|
||||
It MUST be suitable for use in the output of a directory listing
|
||||
command (in fact, the recommended operation for a directory listing
|
||||
command is to simply display this data). However, clients SHOULD NOT
|
||||
attempt to parse the longname field for file attributes; they SHOULD
|
||||
use the attrs field instead.
|
||||
The format of the `longname' field is unspecified by this protocol.
|
||||
It MUST be suitable for use in the output of a directory listing
|
||||
command (in fact, the recommended operation for a directory listing
|
||||
command is to simply display this data). However, clients SHOULD NOT
|
||||
attempt to parse the longname field for file attributes; they SHOULD
|
||||
use the attrs field instead.
|
||||
|
||||
The recommended format for the longname field is as follows:
|
||||
|
||||
-rwxr-xr-x 1 mjos staff 348911 Mar 25 14:29 t-filexfer
|
||||
1234567890 123 12345678 12345678 12345678 123456789012
|
||||
|
||||
Here, the first line is sample output, and the second field indicates
|
||||
widths of the various fields. Fields are separated by spaces. The
|
||||
first field lists file permissions for user, group, and others; the
|
||||
second field is link count; the third field is the name of the user
|
||||
who owns the file; the fourth field is the name of the group that
|
||||
owns the file; the fifth field is the size of the file in bytes; the
|
||||
sixth field (which actually may contain spaces, but is fixed to 12
|
||||
characters) is the file modification time, and the seventh field is
|
||||
the file name. Each field is specified to be a minimum of certain
|
||||
number of character positions (indicated by the second line above),
|
||||
but may also be longer if the data does not fit in the specified
|
||||
length.
|
||||
Here, the first line is sample output, and the second field indicates
|
||||
widths of the various fields. Fields are separated by spaces. The
|
||||
first field lists file permissions for user, group, and others; the
|
||||
second field is link count; the third field is the name of the user
|
||||
who owns the file; the fourth field is the name of the group that
|
||||
owns the file; the fifth field is the size of the file in bytes; the
|
||||
sixth field (which actually may contain spaces, but is fixed to 12
|
||||
characters) is the file modification time, and the seventh field is
|
||||
the file name. Each field is specified to be a minimum of certain
|
||||
number of character positions (indicated by the second line above),
|
||||
but may also be longer if the data does not fit in the specified
|
||||
length.
|
||||
|
||||
The SSH_FXP_ATTRS response has the following format:
|
||||
|
||||
uint32 id
|
||||
ATTRS attrs
|
||||
|
||||
where `id' is the request identifier, and `attrs' is the returned
|
||||
file attributes as described in Section ``File Attributes''.
|
||||
where `id' is the request identifier, and `attrs' is the returned
|
||||
file attributes as described in Section “File Attributes”.
|
||||
|
||||
N.B.: FileZilla does parse this ls formatting, and so not rendering it
|
||||
on any particular GOOS/GOARCH can cause compatibility issues with this client.
|
||||
N.B.: FileZilla does parse this ls formatting, and so not rendering it
|
||||
on any particular GOOS/GOARCH can cause compatibility issues with this client.
|
||||
*/
|
||||
func runLsTestHelper(t *testing.T, result, expectedType, path string) {
|
||||
// using regular expressions to make tests work on all systems
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
//go:build plan9
|
||||
// +build plan9
|
||||
|
||||
package sftp
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
//go:build windows || android
|
||||
// +build windows android
|
||||
|
||||
package sftp
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
//go:build aix || darwin || dragonfly || freebsd || (!android && linux) || netbsd || openbsd || solaris || js
|
||||
// +build aix darwin dragonfly freebsd !android,linux netbsd openbsd solaris js
|
||||
|
||||
package sftp
|
||||
|
|
|
@ -40,7 +40,7 @@ func newPktMgr(sender packetSender) *packetManager {
|
|||
return s
|
||||
}
|
||||
|
||||
//// packet ordering
|
||||
// // packet ordering
|
||||
func (s *packetManager) newOrderID() uint32 {
|
||||
s.packetCount++
|
||||
return s.packetCount
|
||||
|
@ -89,7 +89,7 @@ func (o orderedPackets) Sort() {
|
|||
})
|
||||
}
|
||||
|
||||
//// packet registry
|
||||
// // packet registry
|
||||
// register incoming packets to be handled
|
||||
func (s *packetManager) incomingPacket(pkt orderedRequest) {
|
||||
s.working.Add(1)
|
||||
|
|
|
@ -31,7 +31,7 @@ type notReadOnly interface {
|
|||
notReadOnly()
|
||||
}
|
||||
|
||||
//// define types by adding methods
|
||||
// // define types by adding methods
|
||||
// hasPath
|
||||
func (p *sshFxpLstatPacket) getPath() string { return p.Path }
|
||||
func (p *sshFxpStatPacket) getPath() string { return p.Path }
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
//go:build !debug
|
||||
// +build !debug
|
||||
|
||||
package sftp
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
//go:build plan9
|
||||
// +build plan9
|
||||
|
||||
package sftp
|
||||
|
|
|
@ -28,7 +28,7 @@ then sends to the client.
|
|||
Handler for "Put" method and returns an io.Writer for the file which the server
|
||||
then writes the uploaded file to. The file opening "pflags" are currently
|
||||
preserved in the Request.Flags field as a 32bit bitmask value. See the [SFTP
|
||||
spec](https://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#section-6.3) for
|
||||
spec](https://filezilla-project.org/specs/draft-ietf-secsh-filexfer-02.txt#section-6.3) for
|
||||
details.
|
||||
|
||||
### Filecmd(*Request) error
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
//go:build !windows && !plan9
|
||||
// +build !windows,!plan9
|
||||
|
||||
package sftp
|
||||
|
|
|
@ -24,7 +24,7 @@ const (
|
|||
// Server is an SSH File Transfer Protocol (sftp) server.
|
||||
// This is intended to provide the sftp subsystem to an ssh server daemon.
|
||||
// This implementation currently supports most of sftp server protocol version 3,
|
||||
// as specified at http://tools.ietf.org/html/draft-ietf-secsh-filexfer-02
|
||||
// as specified at https://filezilla-project.org/specs/draft-ietf-secsh-filexfer-02.txt
|
||||
type Server struct {
|
||||
*serverConn
|
||||
debugStream io.Writer
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
//go:build darwin || linux
|
||||
// +build darwin linux
|
||||
|
||||
// fill in statvfs structure with OS specific values
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
//go:build linux
|
||||
// +build linux
|
||||
|
||||
package sftp
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
//go:build !darwin && !linux && !plan9
|
||||
// +build !darwin,!linux,!plan9
|
||||
|
||||
package sftp
|
||||
|
|
2
sftp.go
2
sftp.go
|
@ -1,5 +1,5 @@
|
|||
// Package sftp implements the SSH File Transfer Protocol as described in
|
||||
// https://tools.ietf.org/html/draft-ietf-secsh-filexfer-02
|
||||
// https://filezilla-project.org/specs/draft-ietf-secsh-filexfer-02.txt
|
||||
package sftp
|
||||
|
||||
import (
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
//go:build plan9 || windows || (js && wasm)
|
||||
// +build plan9 windows js,wasm
|
||||
|
||||
// Go defines S_IFMT on windows, plan9 and js/wasm as 0x1f000 instead of
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
// +build !plan9,!windows
|
||||
//go:build !plan9 && !windows && (!js || !wasm)
|
||||
// +build !plan9
|
||||
// +build !windows
|
||||
// +build !js !wasm
|
||||
|
||||
package sftp
|
||||
|
|
Loading…
Reference in New Issue