aboutsummaryrefslogtreecommitdiff
path: root/vendor/github.com/pkg/sftp/internal/encoding/ssh
diff options
context:
space:
mode:
authorCharlie Doern <cdoern@redhat.com>2022-07-15 15:42:14 -0400
committerCharlie Doern <cdoern@redhat.com>2022-08-09 14:00:58 -0400
commit280f5d8cb01d115618d5ef131c718496a3b4900e (patch)
tree17e506cde2a18252da41096dbcc634ef485eff5e /vendor/github.com/pkg/sftp/internal/encoding/ssh
parentc33dc90ace724f920c14e41769ce237f5c5d14ec (diff)
downloadpodman-280f5d8cb01d115618d5ef131c718496a3b4900e.tar.gz
podman-280f5d8cb01d115618d5ef131c718496a3b4900e.tar.bz2
podman-280f5d8cb01d115618d5ef131c718496a3b4900e.zip
podman ssh work, using new c/common interface
implement new ssh interface into podman this completely redesigns the entire functionality of podman image scp, podman system connection add, and podman --remote. All references to golang.org/x/crypto/ssh have been moved to common as have native ssh/scp execs and the new usage of the sftp package. this PR adds a global flag, --ssh to podman which has two valid inputs `golang` and `native` where golang is the default. Users should not notice any difference in their everyday workflows if they continue using the golang option. UNLESS they have been using an improperly verified ssh key, this will now fail. This is because podman was incorrectly using the ssh callback method to IGNORE the ssh known hosts file which is very insecure and golang tells you not yo use this in production. The native paths allows for immense flexibility, with a new containers.conf field `SSH_CONFIG` that specifies a specific ssh config file to be used in all operations. Else the users ~/.ssh/config file will be used. podman --remote currently only uses the golang path, given its deep interconnection with dialing multiple clients and urls. My goal after this PR is to go back and abstract the idea of podman --remote from golang's dialed clients, as it should not be so intrinsically connected. Overall, this is a v1 of a long process of offering native ssh, and one that covers some good ground with podman system connection add and podman image scp. Signed-off-by: Charlie Doern <cdoern@redhat.com>
Diffstat (limited to 'vendor/github.com/pkg/sftp/internal/encoding/ssh')
-rw-r--r--vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/attrs.go325
-rw-r--r--vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/buffer.go293
-rw-r--r--vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/extended_packets.go142
-rw-r--r--vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/extensions.go46
-rw-r--r--vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/filexfer.go54
-rw-r--r--vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/fx.go147
-rw-r--r--vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/fxp.go124
-rw-r--r--vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/handle_packets.go249
-rw-r--r--vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/init_packets.go99
-rw-r--r--vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/open_packets.go89
-rw-r--r--vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/packets.go323
-rw-r--r--vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/path_packets.go368
-rw-r--r--vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/permissions.go114
-rw-r--r--vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/response_packets.go243
14 files changed, 2616 insertions, 0 deletions
diff --git a/vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/attrs.go b/vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/attrs.go
new file mode 100644
index 000000000..eed61bfc6
--- /dev/null
+++ b/vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/attrs.go
@@ -0,0 +1,325 @@
+package filexfer
+
+// Attributes related flags.
+const (
+ AttrSize = 1 << iota // SSH_FILEXFER_ATTR_SIZE
+ AttrUIDGID // SSH_FILEXFER_ATTR_UIDGID
+ AttrPermissions // SSH_FILEXFER_ATTR_PERMISSIONS
+ AttrACModTime // SSH_FILEXFER_ACMODTIME
+
+ AttrExtended = 1 << 31 // SSH_FILEXFER_ATTR_EXTENDED
+)
+
+// 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
+type Attributes struct {
+ Flags uint32
+
+ // AttrSize
+ Size uint64
+
+ // AttrUIDGID
+ UID uint32
+ GID uint32
+
+ // AttrPermissions
+ Permissions FileMode
+
+ // AttrACmodTime
+ ATime uint32
+ MTime uint32
+
+ // AttrExtended
+ ExtendedAttributes []ExtendedAttribute
+}
+
+// GetSize returns the Size field and a bool that is true if and only if the value is valid/defined.
+func (a *Attributes) GetSize() (size uint64, ok bool) {
+ return a.Size, a.Flags&AttrSize != 0
+}
+
+// SetSize is a convenience function that sets the Size field,
+// and marks the field as valid/defined in Flags.
+func (a *Attributes) SetSize(size uint64) {
+ a.Flags |= AttrSize
+ a.Size = size
+}
+
+// GetUIDGID returns the UID and GID fields and a bool that is true if and only if the values are valid/defined.
+func (a *Attributes) GetUIDGID() (uid, gid uint32, ok bool) {
+ return a.UID, a.GID, a.Flags&AttrUIDGID != 0
+}
+
+// SetUIDGID is a convenience function that sets the UID and GID fields,
+// and marks the fields as valid/defined in Flags.
+func (a *Attributes) SetUIDGID(uid, gid uint32) {
+ a.Flags |= AttrUIDGID
+ a.UID = uid
+ a.GID = gid
+}
+
+// GetPermissions returns the Permissions field and a bool that is true if and only if the value is valid/defined.
+func (a *Attributes) GetPermissions() (perms FileMode, ok bool) {
+ return a.Permissions, a.Flags&AttrPermissions != 0
+}
+
+// SetPermissions is a convenience function that sets the Permissions field,
+// and marks the field as valid/defined in Flags.
+func (a *Attributes) SetPermissions(perms FileMode) {
+ a.Flags |= AttrPermissions
+ a.Permissions = perms
+}
+
+// GetACModTime returns the ATime and MTime fields and a bool that is true if and only if the values are valid/defined.
+func (a *Attributes) GetACModTime() (atime, mtime uint32, ok bool) {
+ return a.ATime, a.MTime, a.Flags&AttrACModTime != 0
+}
+
+// SetACModTime is a convenience function that sets the ATime and MTime fields,
+// and marks the fields as valid/defined in Flags.
+func (a *Attributes) SetACModTime(atime, mtime uint32) {
+ a.Flags |= AttrACModTime
+ a.ATime = atime
+ a.MTime = mtime
+}
+
+// Len returns the number of bytes a would marshal into.
+func (a *Attributes) Len() int {
+ length := 4
+
+ if a.Flags&AttrSize != 0 {
+ length += 8
+ }
+
+ if a.Flags&AttrUIDGID != 0 {
+ length += 4 + 4
+ }
+
+ if a.Flags&AttrPermissions != 0 {
+ length += 4
+ }
+
+ if a.Flags&AttrACModTime != 0 {
+ length += 4 + 4
+ }
+
+ if a.Flags&AttrExtended != 0 {
+ length += 4
+
+ for _, ext := range a.ExtendedAttributes {
+ length += ext.Len()
+ }
+ }
+
+ return length
+}
+
+// MarshalInto marshals e onto the end of the given Buffer.
+func (a *Attributes) MarshalInto(b *Buffer) {
+ b.AppendUint32(a.Flags)
+
+ if a.Flags&AttrSize != 0 {
+ b.AppendUint64(a.Size)
+ }
+
+ if a.Flags&AttrUIDGID != 0 {
+ b.AppendUint32(a.UID)
+ b.AppendUint32(a.GID)
+ }
+
+ if a.Flags&AttrPermissions != 0 {
+ b.AppendUint32(uint32(a.Permissions))
+ }
+
+ if a.Flags&AttrACModTime != 0 {
+ b.AppendUint32(a.ATime)
+ b.AppendUint32(a.MTime)
+ }
+
+ if a.Flags&AttrExtended != 0 {
+ b.AppendUint32(uint32(len(a.ExtendedAttributes)))
+
+ for _, ext := range a.ExtendedAttributes {
+ ext.MarshalInto(b)
+ }
+ }
+}
+
+// MarshalBinary returns a as the binary encoding of a.
+func (a *Attributes) MarshalBinary() ([]byte, error) {
+ buf := NewBuffer(make([]byte, 0, a.Len()))
+ a.MarshalInto(buf)
+ return buf.Bytes(), nil
+}
+
+// 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
+ }
+
+ return a.XXX_UnmarshalByFlags(flags, b)
+}
+
+// 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) {
+ a.Flags = flags
+
+ // Short-circuit dummy attributes.
+ if a.Flags == 0 {
+ return nil
+ }
+
+ if a.Flags&AttrSize != 0 {
+ if a.Size, err = b.ConsumeUint64(); err != nil {
+ return err
+ }
+ }
+
+ 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
+ }
+ }
+
+ if a.Flags&AttrPermissions != 0 {
+ m, err := b.ConsumeUint32()
+ if err != nil {
+ return err
+ }
+
+ a.Permissions = FileMode(m)
+ }
+
+ 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
+ }
+ }
+
+ if a.Flags&AttrExtended != 0 {
+ count, err := b.ConsumeUint32()
+ if err != nil {
+ return err
+ }
+
+ a.ExtendedAttributes = make([]ExtendedAttribute, count)
+ for i := range a.ExtendedAttributes {
+ a.ExtendedAttributes[i].UnmarshalFrom(b)
+ }
+ }
+
+ return nil
+}
+
+// UnmarshalBinary decodes the binary encoding of Attributes into e.
+func (a *Attributes) UnmarshalBinary(data []byte) error {
+ return a.UnmarshalFrom(NewBuffer(data))
+}
+
+// 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
+type ExtendedAttribute struct {
+ Type string
+ Data string
+}
+
+// Len returns the number of bytes e would marshal into.
+func (e *ExtendedAttribute) Len() int {
+ return 4 + len(e.Type) + 4 + len(e.Data)
+}
+
+// MarshalInto marshals e onto the end of the given Buffer.
+func (e *ExtendedAttribute) MarshalInto(b *Buffer) {
+ b.AppendString(e.Type)
+ b.AppendString(e.Data)
+}
+
+// MarshalBinary returns e as the binary encoding of e.
+func (e *ExtendedAttribute) MarshalBinary() ([]byte, error) {
+ buf := NewBuffer(make([]byte, 0, e.Len()))
+ e.MarshalInto(buf)
+ return buf.Bytes(), nil
+}
+
+// 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
+ }
+
+ if e.Data, err = b.ConsumeString(); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+// UnmarshalBinary decodes the binary encoding of ExtendedAttribute into e.
+func (e *ExtendedAttribute) UnmarshalBinary(data []byte) error {
+ return e.UnmarshalFrom(NewBuffer(data))
+}
+
+// NameEntry implements the SSH_FXP_NAME repeated data type from draft-ietf-secsh-filexfer-02
+//
+// This type is incompatible with versions 4 or higher.
+type NameEntry struct {
+ Filename string
+ Longname string
+ Attrs Attributes
+}
+
+// Len returns the number of bytes e would marshal into.
+func (e *NameEntry) Len() int {
+ return 4 + len(e.Filename) + 4 + len(e.Longname) + e.Attrs.Len()
+}
+
+// MarshalInto marshals e onto the end of the given Buffer.
+func (e *NameEntry) MarshalInto(b *Buffer) {
+ b.AppendString(e.Filename)
+ b.AppendString(e.Longname)
+
+ e.Attrs.MarshalInto(b)
+}
+
+// MarshalBinary returns e as the binary encoding of e.
+func (e *NameEntry) MarshalBinary() ([]byte, error) {
+ buf := NewBuffer(make([]byte, 0, e.Len()))
+ e.MarshalInto(buf)
+ return buf.Bytes(), nil
+}
+
+// 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
+ }
+
+ if e.Longname, err = b.ConsumeString(); err != nil {
+ return err
+ }
+
+ return e.Attrs.UnmarshalFrom(b)
+}
+
+// UnmarshalBinary decodes the binary encoding of NameEntry into e.
+func (e *NameEntry) UnmarshalBinary(data []byte) error {
+ return e.UnmarshalFrom(NewBuffer(data))
+}
diff --git a/vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/buffer.go b/vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/buffer.go
new file mode 100644
index 000000000..a6086036e
--- /dev/null
+++ b/vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/buffer.go
@@ -0,0 +1,293 @@
+package filexfer
+
+import (
+ "encoding/binary"
+ "errors"
+)
+
+// Various encoding errors.
+var (
+ ErrShortPacket = errors.New("packet too short")
+ ErrLongPacket = errors.New("packet too long")
+)
+
+// Buffer wraps up the various encoding details of the SSH format.
+//
+// Data types are encoded as per section 4 from https://tools.ietf.org/html/draft-ietf-secsh-architecture-09#page-8
+type Buffer struct {
+ b []byte
+ off int
+}
+
+// NewBuffer creates and initializes a new buffer using buf as its initial contents.
+// The new buffer takes ownership of buf, and the caller should not use buf after this call.
+//
+// In most cases, new(Buffer) (or just declaring a Buffer variable) is sufficient to initialize a Buffer.
+func NewBuffer(buf []byte) *Buffer {
+ return &Buffer{
+ b: buf,
+ }
+}
+
+// NewMarshalBuffer creates a new Buffer ready to start marshaling a Packet into.
+// It preallocates enough space for uint32(length), uint8(type), uint32(request-id) and size more bytes.
+func NewMarshalBuffer(size int) *Buffer {
+ return NewBuffer(make([]byte, 4+1+4+size))
+}
+
+// Bytes returns a slice of length b.Len() holding the unconsumed bytes in the Buffer.
+// The slice is valid for use only until the next buffer modification
+// (that is, only until the next call to an Append or Consume method).
+func (b *Buffer) Bytes() []byte {
+ return b.b[b.off:]
+}
+
+// Len returns the number of unconsumed bytes in the buffer.
+func (b *Buffer) Len() int { return len(b.b) - b.off }
+
+// Cap returns the capacity of the buffer’s underlying byte slice,
+// that is, the total space allocated for the buffer’s data.
+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
+}
+
+// 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.AppendUint8(uint8(packetType))
+ b.AppendUint32(requestID)
+}
+
+// Packet finalizes the packet started from StartPacket.
+// It is expected that this will end the ownership of the underlying byte-slice,
+// and so the returned byte-slices may be reused the same as any other byte-slice,
+// the caller should not use this buffer after this call.
+//
+// It writes the packet body length into the first four bytes of the buffer in network byte order (big endian).
+// The packet body length is the length of this buffer less the 4-byte length itself, plus the length of payload.
+//
+// It is assumed that no Consume methods have been called on this buffer,
+// and so it returns the whole underlying slice.
+func (b *Buffer) Packet(payload []byte) (header, payloadPassThru []byte, err error) {
+ b.PutLength(len(b.b) - 4 + len(payload))
+
+ return b.b, payload, nil
+}
+
+// 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 b.Len() < 1 {
+ return 0, ErrShortPacket
+ }
+
+ var v uint8
+ v, b.off = b.b[b.off], b.off+1
+ return v, nil
+}
+
+// AppendUint8 appends a single byte into the buffer.
+func (b *Buffer) AppendUint8(v uint8) {
+ b.b = append(b.b, v)
+}
+
+// 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
+}
+
+// AppendBool appends a single bool into the buffer.
+// It encodes it as a single byte, with false as 0, and true as 1.
+func (b *Buffer) AppendBool(v bool) {
+ if v {
+ b.AppendUint8(1)
+ } else {
+ b.AppendUint8(0)
+ }
+}
+
+// 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 b.Len() < 2 {
+ return 0, ErrShortPacket
+ }
+
+ v := binary.BigEndian.Uint16(b.b[b.off:])
+ b.off += 2
+ return v, nil
+}
+
+// AppendUint16 appends single uint16 into the buffer, in network byte order (big-endian).
+func (b *Buffer) AppendUint16(v uint16) {
+ b.b = append(b.b,
+ byte(v>>8),
+ byte(v>>0),
+ )
+}
+
+// unmarshalUint32 is used internally to read the packet length.
+// It is unsafe, and so not exported.
+// Even within this package, its use should be avoided.
+func unmarshalUint32(b []byte) uint32 {
+ return binary.BigEndian.Uint32(b[:4])
+}
+
+// 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 b.Len() < 4 {
+ return 0, ErrShortPacket
+ }
+
+ v := binary.BigEndian.Uint32(b.b[b.off:])
+ b.off += 4
+ return v, nil
+}
+
+// AppendUint32 appends a single uint32 into the buffer, in network byte order (big-endian).
+func (b *Buffer) AppendUint32(v uint32) {
+ b.b = append(b.b,
+ byte(v>>24),
+ byte(v>>16),
+ byte(v>>8),
+ byte(v>>0),
+ )
+}
+
+// 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 b.Len() < 8 {
+ return 0, ErrShortPacket
+ }
+
+ v := binary.BigEndian.Uint64(b.b[b.off:])
+ b.off += 8
+ return v, nil
+}
+
+// AppendUint64 appends a single uint64 into the buffer, in network byte order (big-endian).
+func (b *Buffer) AppendUint64(v uint64) {
+ b.b = append(b.b,
+ byte(v>>56),
+ byte(v>>48),
+ byte(v>>40),
+ byte(v>>32),
+ byte(v>>24),
+ byte(v>>16),
+ byte(v>>8),
+ byte(v>>0),
+ )
+}
+
+// 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
+}
+
+// AppendInt64 appends a single int64 into the buffer, in network byte order (big-endian) with two’s complement.
+func (b *Buffer) AppendInt64(v int64) {
+ b.AppendUint64(uint64(v))
+}
+
+// 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.
+//
+// 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
+ }
+
+ if b.Len() < int(length) {
+ return nil, ErrShortPacket
+ }
+
+ v := b.b[b.off:]
+ if len(v) > int(length) {
+ v = v[:length:length]
+ }
+ b.off += int(length)
+ return v, nil
+}
+
+// AppendByteSlice appends a single string of raw binary data into the buffer.
+// A string is a uint32 length, followed by that number of raw bytes.
+func (b *Buffer) AppendByteSlice(v []byte) {
+ b.AppendUint32(uint32(len(v)))
+ b.b = append(b.b, v...)
+}
+
+// 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.
+//
+// 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
+}
+
+// AppendString appends a single string of binary data into the buffer.
+// A string is a uint32 length, followed by that number of raw bytes.
+func (b *Buffer) AppendString(v string) {
+ b.AppendByteSlice([]byte(v))
+}
+
+// PutLength writes the given size into the first four bytes of the buffer in network byte order (big endian).
+func (b *Buffer) PutLength(size int) {
+ if len(b.b) < 4 {
+ b.b = append(b.b, make([]byte, 4-len(b.b))...)
+ }
+
+ binary.BigEndian.PutUint32(b.b, uint32(size))
+}
+
+// MarshalBinary returns a clone of the full internal buffer.
+func (b *Buffer) MarshalBinary() ([]byte, error) {
+ clone := make([]byte, len(b.b))
+ n := copy(clone, b.b)
+ return clone[:n], nil
+}
+
+// UnmarshalBinary sets the internal buffer of b to be a clone of data, and zeros the internal offset.
+func (b *Buffer) UnmarshalBinary(data []byte) error {
+ if grow := len(data) - len(b.b); grow > 0 {
+ b.b = append(b.b, make([]byte, grow)...)
+ }
+
+ n := copy(b.b, data)
+ b.b = b.b[:n]
+ b.off = 0
+ return nil
+}
diff --git a/vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/extended_packets.go b/vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/extended_packets.go
new file mode 100644
index 000000000..6b7b2cef4
--- /dev/null
+++ b/vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/extended_packets.go
@@ -0,0 +1,142 @@
+package filexfer
+
+import (
+ "encoding"
+ "sync"
+)
+
+// ExtendedData aliases the untyped interface composition of encoding.BinaryMarshaler and encoding.BinaryUnmarshaler.
+type ExtendedData = interface {
+ encoding.BinaryMarshaler
+ encoding.BinaryUnmarshaler
+}
+
+// ExtendedDataConstructor defines a function that returns a new(ArbitraryExtendedPacket).
+type ExtendedDataConstructor func() ExtendedData
+
+var extendedPacketTypes = struct {
+ mu sync.RWMutex
+ constructors map[string]ExtendedDataConstructor
+}{
+ constructors: make(map[string]ExtendedDataConstructor),
+}
+
+// RegisterExtendedPacketType defines a specific ExtendedDataConstructor for the given extension string.
+func RegisterExtendedPacketType(extension string, constructor ExtendedDataConstructor) {
+ extendedPacketTypes.mu.Lock()
+ defer extendedPacketTypes.mu.Unlock()
+
+ if _, exist := extendedPacketTypes.constructors[extension]; exist {
+ panic("encoding/ssh/filexfer: multiple registration of extended packet type " + extension)
+ }
+
+ extendedPacketTypes.constructors[extension] = constructor
+}
+
+func newExtendedPacket(extension string) ExtendedData {
+ extendedPacketTypes.mu.RLock()
+ defer extendedPacketTypes.mu.RUnlock()
+
+ if f := extendedPacketTypes.constructors[extension]; f != nil {
+ return f()
+ }
+
+ return new(Buffer)
+}
+
+// ExtendedPacket defines the SSH_FXP_CLOSE packet.
+type ExtendedPacket struct {
+ ExtendedRequest string
+
+ Data ExtendedData
+}
+
+// Type returns the SSH_FXP_xy value associated with this packet type.
+func (p *ExtendedPacket) Type() PacketType {
+ return PacketTypeExtended
+}
+
+// MarshalPacket returns p as a two-part binary encoding of p.
+//
+// The Data is marshaled into binary, and returned as the payload.
+func (p *ExtendedPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
+ buf := NewBuffer(b)
+ if buf.Cap() < 9 {
+ size := 4 + len(p.ExtendedRequest) // string(extended-request)
+ buf = NewMarshalBuffer(size)
+ }
+
+ buf.StartPacket(PacketTypeExtended, reqid)
+ buf.AppendString(p.ExtendedRequest)
+
+ if p.Data != nil {
+ payload, err = p.Data.MarshalBinary()
+ if err != nil {
+ return nil, nil, err
+ }
+ }
+
+ return buf.Packet(payload)
+}
+
+// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
+// It is assumed that the uint32(request-id) has already been consumed.
+//
+// If p.Data is nil, and the extension has been registered, a new type will be made from the registration.
+// 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
+ }
+
+ if p.Data == nil {
+ p.Data = newExtendedPacket(p.ExtendedRequest)
+ }
+
+ return p.Data.UnmarshalBinary(buf.Bytes())
+}
+
+// ExtendedReplyPacket defines the SSH_FXP_CLOSE packet.
+type ExtendedReplyPacket struct {
+ Data ExtendedData
+}
+
+// Type returns the SSH_FXP_xy value associated with this packet type.
+func (p *ExtendedReplyPacket) Type() PacketType {
+ return PacketTypeExtendedReply
+}
+
+// MarshalPacket returns p as a two-part binary encoding of p.
+//
+// The Data is marshaled into binary, and returned as the payload.
+func (p *ExtendedReplyPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
+ buf := NewBuffer(b)
+ if buf.Cap() < 9 {
+ buf = NewMarshalBuffer(0)
+ }
+
+ buf.StartPacket(PacketTypeExtendedReply, reqid)
+
+ if p.Data != nil {
+ payload, err = p.Data.MarshalBinary()
+ if err != nil {
+ return nil, nil, err
+ }
+ }
+
+ return buf.Packet(payload)
+}
+
+// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
+// It is assumed that the uint32(request-id) has already been consumed.
+//
+// If p.Data is nil, and there is request-specific-data,
+// then the request-specific-data will be wrapped in a Buffer and assigned to p.Data.
+func (p *ExtendedReplyPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
+ if p.Data == nil {
+ p.Data = new(Buffer)
+ }
+
+ return p.Data.UnmarshalBinary(buf.Bytes())
+}
diff --git a/vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/extensions.go b/vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/extensions.go
new file mode 100644
index 000000000..11c0b99c2
--- /dev/null
+++ b/vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/extensions.go
@@ -0,0 +1,46 @@
+package filexfer
+
+// 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.
+//
+// Defined in: https://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-4.2
+type ExtensionPair struct {
+ Name string
+ Data string
+}
+
+// Len returns the number of bytes e would marshal into.
+func (e *ExtensionPair) Len() int {
+ return 4 + len(e.Name) + 4 + len(e.Data)
+}
+
+// MarshalInto marshals e onto the end of the given Buffer.
+func (e *ExtensionPair) MarshalInto(buf *Buffer) {
+ buf.AppendString(e.Name)
+ buf.AppendString(e.Data)
+}
+
+// MarshalBinary returns e as the binary encoding of e.
+func (e *ExtensionPair) MarshalBinary() ([]byte, error) {
+ buf := NewBuffer(make([]byte, 0, e.Len()))
+ e.MarshalInto(buf)
+ return buf.Bytes(), nil
+}
+
+// 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
+ }
+
+ if e.Data, err = buf.ConsumeString(); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+// UnmarshalBinary decodes the binary encoding of ExtensionPair into e.
+func (e *ExtensionPair) UnmarshalBinary(data []byte) error {
+ return e.UnmarshalFrom(NewBuffer(data))
+}
diff --git a/vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/filexfer.go b/vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/filexfer.go
new file mode 100644
index 000000000..1e5abf746
--- /dev/null
+++ b/vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/filexfer.go
@@ -0,0 +1,54 @@
+// Package filexfer implements the wire encoding for secsh-filexfer as described in https://tools.ietf.org/html/draft-ietf-secsh-filexfer-02
+package filexfer
+
+// PacketMarshaller narrowly defines packets that will only be transmitted.
+//
+// ExtendedPacket types will often only implement this interface,
+// since decoding the whole packet body of an ExtendedPacket can only be done dependent on the ExtendedRequest field.
+type PacketMarshaller interface {
+ // MarshalPacket is the primary intended way to encode a packet.
+ // The request-id for the packet is set from reqid.
+ //
+ // An optional buffer may be given in b.
+ // If the buffer has a minimum capacity, it shall be truncated and used to marshal the header into.
+ // The minimum capacity for the packet must be a constant expression, and should be at least 9.
+ //
+ // It shall return the main body of the encoded packet in header,
+ // and may optionally return an additional payload to be written immediately after the header.
+ //
+ // It shall encode in the first 4-bytes of the header the proper length of the rest of the header+payload.
+ MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error)
+}
+
+// Packet defines the behavior of a full generic SFTP packet.
+//
+// InitPacket, and VersionPacket are not generic SFTP packets, and instead implement (Un)MarshalBinary.
+//
+// ExtendedPacket types should not iplement this interface,
+// since decoding the whole packet body of an ExtendedPacket can only be done dependent on the ExtendedRequest field.
+type Packet interface {
+ PacketMarshaller
+
+ // Type returns the SSH_FXP_xy value associated with the specific packet.
+ Type() PacketType
+
+ // UnmarshalPacketBody decodes a packet body from the given Buffer.
+ // It is assumed that the common header values of the length, type and request-id have already been consumed.
+ //
+ // Implementations should not alias the given Buffer,
+ // instead they can consider prepopulating an internal buffer as a hint,
+ // and copying into that buffer if it has sufficient length.
+ UnmarshalPacketBody(buf *Buffer) error
+}
+
+// ComposePacket converts returns from MarshalPacket into an equivalent call to MarshalBinary.
+func ComposePacket(header, payload []byte, err error) ([]byte, error) {
+ return append(header, payload...), err
+}
+
+// Default length values,
+// Defined in draft-ietf-secsh-filexfer-02 section 3.
+const (
+ DefaultMaxPacketLength = 34000
+ DefaultMaxDataLength = 32768
+)
diff --git a/vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/fx.go b/vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/fx.go
new file mode 100644
index 000000000..48f869861
--- /dev/null
+++ b/vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/fx.go
@@ -0,0 +1,147 @@
+package filexfer
+
+import (
+ "fmt"
+)
+
+// Status defines the SFTP error codes used in SSH_FXP_STATUS response packets.
+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
+ StatusOK = Status(iota)
+ StatusEOF
+ StatusNoSuchFile
+ StatusPermissionDenied
+ StatusFailure
+ StatusBadMessage
+ StatusNoConnection
+ StatusConnectionLost
+ StatusOPUnsupported
+
+ // https://tools.ietf.org/html/draft-ietf-secsh-filexfer-03#section-7
+ StatusV4InvalidHandle
+ StatusV4NoSuchPath
+ StatusV4FileAlreadyExists
+ StatusV4WriteProtect
+
+ // https://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-7
+ StatusV4NoMedia
+
+ // https://tools.ietf.org/html/draft-ietf-secsh-filexfer-05#section-7
+ StatusV5NoSpaceOnFilesystem
+ StatusV5QuotaExceeded
+ StatusV5UnknownPrincipal
+ StatusV5LockConflict
+
+ // https://tools.ietf.org/html/draft-ietf-secsh-filexfer-06#section-8
+ StatusV6DirNotEmpty
+ StatusV6NotADirectory
+ StatusV6InvalidFilename
+ StatusV6LinkLoop
+
+ // https://tools.ietf.org/html/draft-ietf-secsh-filexfer-07#section-8
+ StatusV6CannotDelete
+ StatusV6InvalidParameter
+ StatusV6FileIsADirectory
+ StatusV6ByteRangeLockConflict
+ StatusV6ByteRangeLockRefused
+ StatusV6DeletePending
+
+ // https://tools.ietf.org/html/draft-ietf-secsh-filexfer-08#section-8.1
+ StatusV6FileCorrupt
+
+ // https://tools.ietf.org/html/draft-ietf-secsh-filexfer-10#section-9.1
+ StatusV6OwnerInvalid
+ StatusV6GroupInvalid
+
+ // https://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-9.1
+ StatusV6NoMatchingByteRangeLock
+)
+
+func (s Status) Error() string {
+ return s.String()
+}
+
+// Is returns true if the target is the same Status code,
+// or target is a StatusPacket with the same Status code.
+func (s Status) Is(target error) bool {
+ if target, ok := target.(*StatusPacket); ok {
+ return target.StatusCode == s
+ }
+
+ return s == target
+}
+
+func (s Status) String() string {
+ switch s {
+ case StatusOK:
+ return "SSH_FX_OK"
+ case StatusEOF:
+ return "SSH_FX_EOF"
+ case StatusNoSuchFile:
+ return "SSH_FX_NO_SUCH_FILE"
+ case StatusPermissionDenied:
+ return "SSH_FX_PERMISSION_DENIED"
+ case StatusFailure:
+ return "SSH_FX_FAILURE"
+ case StatusBadMessage:
+ return "SSH_FX_BAD_MESSAGE"
+ case StatusNoConnection:
+ return "SSH_FX_NO_CONNECTION"
+ case StatusConnectionLost:
+ return "SSH_FX_CONNECTION_LOST"
+ case StatusOPUnsupported:
+ return "SSH_FX_OP_UNSUPPORTED"
+ case StatusV4InvalidHandle:
+ return "SSH_FX_INVALID_HANDLE"
+ case StatusV4NoSuchPath:
+ return "SSH_FX_NO_SUCH_PATH"
+ case StatusV4FileAlreadyExists:
+ return "SSH_FX_FILE_ALREADY_EXISTS"
+ case StatusV4WriteProtect:
+ return "SSH_FX_WRITE_PROTECT"
+ case StatusV4NoMedia:
+ return "SSH_FX_NO_MEDIA"
+ case StatusV5NoSpaceOnFilesystem:
+ return "SSH_FX_NO_SPACE_ON_FILESYSTEM"
+ case StatusV5QuotaExceeded:
+ return "SSH_FX_QUOTA_EXCEEDED"
+ case StatusV5UnknownPrincipal:
+ return "SSH_FX_UNKNOWN_PRINCIPAL"
+ case StatusV5LockConflict:
+ return "SSH_FX_LOCK_CONFLICT"
+ case StatusV6DirNotEmpty:
+ return "SSH_FX_DIR_NOT_EMPTY"
+ case StatusV6NotADirectory:
+ return "SSH_FX_NOT_A_DIRECTORY"
+ case StatusV6InvalidFilename:
+ return "SSH_FX_INVALID_FILENAME"
+ case StatusV6LinkLoop:
+ return "SSH_FX_LINK_LOOP"
+ case StatusV6CannotDelete:
+ return "SSH_FX_CANNOT_DELETE"
+ case StatusV6InvalidParameter:
+ return "SSH_FX_INVALID_PARAMETER"
+ case StatusV6FileIsADirectory:
+ return "SSH_FX_FILE_IS_A_DIRECTORY"
+ case StatusV6ByteRangeLockConflict:
+ return "SSH_FX_BYTE_RANGE_LOCK_CONFLICT"
+ case StatusV6ByteRangeLockRefused:
+ return "SSH_FX_BYTE_RANGE_LOCK_REFUSED"
+ case StatusV6DeletePending:
+ return "SSH_FX_DELETE_PENDING"
+ case StatusV6FileCorrupt:
+ return "SSH_FX_FILE_CORRUPT"
+ case StatusV6OwnerInvalid:
+ return "SSH_FX_OWNER_INVALID"
+ case StatusV6GroupInvalid:
+ return "SSH_FX_GROUP_INVALID"
+ case StatusV6NoMatchingByteRangeLock:
+ return "SSH_FX_NO_MATCHING_BYTE_RANGE_LOCK"
+ default:
+ return fmt.Sprintf("SSH_FX_UNKNOWN(%d)", s)
+ }
+}
diff --git a/vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/fxp.go b/vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/fxp.go
new file mode 100644
index 000000000..15caf6d28
--- /dev/null
+++ b/vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/fxp.go
@@ -0,0 +1,124 @@
+package filexfer
+
+import (
+ "fmt"
+)
+
+// PacketType defines the various SFTP packet types.
+type PacketType uint8
+
+// Request packet types.
+const (
+ // https://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#section-3
+ PacketTypeInit = PacketType(iota + 1)
+ PacketTypeVersion
+ PacketTypeOpen
+ PacketTypeClose
+ PacketTypeRead
+ PacketTypeWrite
+ PacketTypeLStat
+ PacketTypeFStat
+ PacketTypeSetstat
+ PacketTypeFSetstat
+ PacketTypeOpenDir
+ PacketTypeReadDir
+ PacketTypeRemove
+ PacketTypeMkdir
+ PacketTypeRmdir
+ PacketTypeRealPath
+ PacketTypeStat
+ PacketTypeRename
+ PacketTypeReadLink
+ PacketTypeSymlink
+
+ // https://tools.ietf.org/html/draft-ietf-secsh-filexfer-07#section-3.3
+ PacketTypeV6Link
+
+ // https://tools.ietf.org/html/draft-ietf-secsh-filexfer-08#section-3.3
+ PacketTypeV6Block
+ PacketTypeV6Unblock
+)
+
+// Response packet types.
+const (
+ // https://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#section-3
+ PacketTypeStatus = PacketType(iota + 101)
+ PacketTypeHandle
+ PacketTypeData
+ PacketTypeName
+ PacketTypeAttrs
+)
+
+// Extended packet types.
+const (
+ // https://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#section-3
+ PacketTypeExtended = PacketType(iota + 200)
+ PacketTypeExtendedReply
+)
+
+func (f PacketType) String() string {
+ switch f {
+ case PacketTypeInit:
+ return "SSH_FXP_INIT"
+ case PacketTypeVersion:
+ return "SSH_FXP_VERSION"
+ case PacketTypeOpen:
+ return "SSH_FXP_OPEN"
+ case PacketTypeClose:
+ return "SSH_FXP_CLOSE"
+ case PacketTypeRead:
+ return "SSH_FXP_READ"
+ case PacketTypeWrite:
+ return "SSH_FXP_WRITE"
+ case PacketTypeLStat:
+ return "SSH_FXP_LSTAT"
+ case PacketTypeFStat:
+ return "SSH_FXP_FSTAT"
+ case PacketTypeSetstat:
+ return "SSH_FXP_SETSTAT"
+ case PacketTypeFSetstat:
+ return "SSH_FXP_FSETSTAT"
+ case PacketTypeOpenDir:
+ return "SSH_FXP_OPENDIR"
+ case PacketTypeReadDir:
+ return "SSH_FXP_READDIR"
+ case PacketTypeRemove:
+ return "SSH_FXP_REMOVE"
+ case PacketTypeMkdir:
+ return "SSH_FXP_MKDIR"
+ case PacketTypeRmdir:
+ return "SSH_FXP_RMDIR"
+ case PacketTypeRealPath:
+ return "SSH_FXP_REALPATH"
+ case PacketTypeStat:
+ return "SSH_FXP_STAT"
+ case PacketTypeRename:
+ return "SSH_FXP_RENAME"
+ case PacketTypeReadLink:
+ return "SSH_FXP_READLINK"
+ case PacketTypeSymlink:
+ return "SSH_FXP_SYMLINK"
+ case PacketTypeV6Link:
+ return "SSH_FXP_LINK"
+ case PacketTypeV6Block:
+ return "SSH_FXP_BLOCK"
+ case PacketTypeV6Unblock:
+ return "SSH_FXP_UNBLOCK"
+ case PacketTypeStatus:
+ return "SSH_FXP_STATUS"
+ case PacketTypeHandle:
+ return "SSH_FXP_HANDLE"
+ case PacketTypeData:
+ return "SSH_FXP_DATA"
+ case PacketTypeName:
+ return "SSH_FXP_NAME"
+ case PacketTypeAttrs:
+ return "SSH_FXP_ATTRS"
+ case PacketTypeExtended:
+ return "SSH_FXP_EXTENDED"
+ case PacketTypeExtendedReply:
+ return "SSH_FXP_EXTENDED_REPLY"
+ default:
+ return fmt.Sprintf("SSH_FXP_UNKNOWN(%d)", f)
+ }
+}
diff --git a/vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/handle_packets.go b/vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/handle_packets.go
new file mode 100644
index 000000000..a14277128
--- /dev/null
+++ b/vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/handle_packets.go
@@ -0,0 +1,249 @@
+package filexfer
+
+// ClosePacket defines the SSH_FXP_CLOSE packet.
+type ClosePacket struct {
+ Handle string
+}
+
+// Type returns the SSH_FXP_xy value associated with this packet type.
+func (p *ClosePacket) Type() PacketType {
+ return PacketTypeClose
+}
+
+// MarshalPacket returns p as a two-part binary encoding of p.
+func (p *ClosePacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
+ buf := NewBuffer(b)
+ if buf.Cap() < 9 {
+ size := 4 + len(p.Handle) // string(handle)
+ buf = NewMarshalBuffer(size)
+ }
+
+ buf.StartPacket(PacketTypeClose, reqid)
+ buf.AppendString(p.Handle)
+
+ return buf.Packet(payload)
+}
+
+// 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
+ }
+
+ return nil
+}
+
+// ReadPacket defines the SSH_FXP_READ packet.
+type ReadPacket struct {
+ Handle string
+ Offset uint64
+ Len uint32
+}
+
+// Type returns the SSH_FXP_xy value associated with this packet type.
+func (p *ReadPacket) Type() PacketType {
+ return PacketTypeRead
+}
+
+// MarshalPacket returns p as a two-part binary encoding of p.
+func (p *ReadPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
+ buf := NewBuffer(b)
+ if buf.Cap() < 9 {
+ // string(handle) + uint64(offset) + uint32(len)
+ size := 4 + len(p.Handle) + 8 + 4
+ buf = NewMarshalBuffer(size)
+ }
+
+ buf.StartPacket(PacketTypeRead, reqid)
+ buf.AppendString(p.Handle)
+ buf.AppendUint64(p.Offset)
+ buf.AppendUint32(p.Len)
+
+ return buf.Packet(payload)
+}
+
+// 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
+ }
+
+ if p.Offset, err = buf.ConsumeUint64(); err != nil {
+ return err
+ }
+
+ if p.Len, err = buf.ConsumeUint32(); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+// WritePacket defines the SSH_FXP_WRITE packet.
+type WritePacket struct {
+ Handle string
+ Offset uint64
+ Data []byte
+}
+
+// Type returns the SSH_FXP_xy value associated with this packet type.
+func (p *WritePacket) Type() PacketType {
+ return PacketTypeWrite
+}
+
+// MarshalPacket returns p as a two-part binary encoding of p.
+func (p *WritePacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
+ buf := NewBuffer(b)
+ if buf.Cap() < 9 {
+ // string(handle) + uint64(offset) + uint32(len(data)); data content in payload
+ size := 4 + len(p.Handle) + 8 + 4
+ buf = NewMarshalBuffer(size)
+ }
+
+ buf.StartPacket(PacketTypeWrite, reqid)
+ buf.AppendString(p.Handle)
+ buf.AppendUint64(p.Offset)
+ buf.AppendUint32(uint32(len(p.Data)))
+
+ return buf.Packet(p.Data)
+}
+
+// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
+// It is assumed that the uint32(request-id) has already been consumed.
+//
+// If p.Data is already populated, and of sufficient length to hold the data,
+// then this will copy the data into that byte slice.
+//
+// If p.Data has a length insufficient to hold the data,
+// then this will make a new slice of sufficient length, and copy the data into that.
+//
+// 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
+ }
+
+ 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
+}
+
+// FStatPacket defines the SSH_FXP_FSTAT packet.
+type FStatPacket struct {
+ Handle string
+}
+
+// Type returns the SSH_FXP_xy value associated with this packet type.
+func (p *FStatPacket) Type() PacketType {
+ return PacketTypeFStat
+}
+
+// MarshalPacket returns p as a two-part binary encoding of p.
+func (p *FStatPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
+ buf := NewBuffer(b)
+ if buf.Cap() < 9 {
+ size := 4 + len(p.Handle) // string(handle)
+ buf = NewMarshalBuffer(size)
+ }
+
+ buf.StartPacket(PacketTypeFStat, reqid)
+ buf.AppendString(p.Handle)
+
+ return buf.Packet(payload)
+}
+
+// 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
+ }
+
+ return nil
+}
+
+// FSetstatPacket defines the SSH_FXP_FSETSTAT packet.
+type FSetstatPacket struct {
+ Handle string
+ Attrs Attributes
+}
+
+// Type returns the SSH_FXP_xy value associated with this packet type.
+func (p *FSetstatPacket) Type() PacketType {
+ return PacketTypeFSetstat
+}
+
+// MarshalPacket returns p as a two-part binary encoding of p.
+func (p *FSetstatPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
+ buf := NewBuffer(b)
+ if buf.Cap() < 9 {
+ size := 4 + len(p.Handle) + p.Attrs.Len() // string(handle) + ATTRS(attrs)
+ buf = NewMarshalBuffer(size)
+ }
+
+ buf.StartPacket(PacketTypeFSetstat, reqid)
+ buf.AppendString(p.Handle)
+
+ p.Attrs.MarshalInto(buf)
+
+ return buf.Packet(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
+ }
+
+ return p.Attrs.UnmarshalFrom(buf)
+}
+
+// ReadDirPacket defines the SSH_FXP_READDIR packet.
+type ReadDirPacket struct {
+ Handle string
+}
+
+// Type returns the SSH_FXP_xy value associated with this packet type.
+func (p *ReadDirPacket) Type() PacketType {
+ return PacketTypeReadDir
+}
+
+// MarshalPacket returns p as a two-part binary encoding of p.
+func (p *ReadDirPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
+ buf := NewBuffer(b)
+ if buf.Cap() < 9 {
+ size := 4 + len(p.Handle) // string(handle)
+ buf = NewMarshalBuffer(size)
+ }
+
+ buf.StartPacket(PacketTypeReadDir, reqid)
+ buf.AppendString(p.Handle)
+
+ return buf.Packet(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
+ }
+
+ return nil
+}
diff --git a/vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/init_packets.go b/vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/init_packets.go
new file mode 100644
index 000000000..b0bc6f505
--- /dev/null
+++ b/vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/init_packets.go
@@ -0,0 +1,99 @@
+package filexfer
+
+// InitPacket defines the SSH_FXP_INIT packet.
+type InitPacket struct {
+ Version uint32
+ Extensions []*ExtensionPair
+}
+
+// MarshalBinary returns p as the binary encoding of p.
+func (p *InitPacket) MarshalBinary() ([]byte, error) {
+ size := 1 + 4 // byte(type) + uint32(version)
+
+ for _, ext := range p.Extensions {
+ size += ext.Len()
+ }
+
+ b := NewBuffer(make([]byte, 4, 4+size))
+ b.AppendUint8(uint8(PacketTypeInit))
+ b.AppendUint32(p.Version)
+
+ for _, ext := range p.Extensions {
+ ext.MarshalInto(b)
+ }
+
+ b.PutLength(size)
+
+ return b.Bytes(), nil
+}
+
+// UnmarshalBinary unmarshals a full raw packet out of the given data.
+// It is assumed that the uint32(length) has already been consumed to receive the data.
+// It is also assumed that the uint8(type) has already been consumed to which packet to unmarshal into.
+func (p *InitPacket) UnmarshalBinary(data []byte) (err error) {
+ buf := NewBuffer(data)
+
+ if p.Version, err = buf.ConsumeUint32(); err != nil {
+ return err
+ }
+
+ for buf.Len() > 0 {
+ var ext ExtensionPair
+ if err := ext.UnmarshalFrom(buf); err != nil {
+ return err
+ }
+
+ p.Extensions = append(p.Extensions, &ext)
+ }
+
+ return nil
+}
+
+// VersionPacket defines the SSH_FXP_VERSION packet.
+type VersionPacket struct {
+ Version uint32
+ Extensions []*ExtensionPair
+}
+
+// MarshalBinary returns p as the binary encoding of p.
+func (p *VersionPacket) MarshalBinary() ([]byte, error) {
+ size := 1 + 4 // byte(type) + uint32(version)
+
+ for _, ext := range p.Extensions {
+ size += ext.Len()
+ }
+
+ b := NewBuffer(make([]byte, 4, 4+size))
+ b.AppendUint8(uint8(PacketTypeVersion))
+ b.AppendUint32(p.Version)
+
+ for _, ext := range p.Extensions {
+ ext.MarshalInto(b)
+ }
+
+ b.PutLength(size)
+
+ return b.Bytes(), nil
+}
+
+// UnmarshalBinary unmarshals a full raw packet out of the given data.
+// It is assumed that the uint32(length) has already been consumed to receive the data.
+// It is also assumed that the uint8(type) has already been consumed to which packet to unmarshal into.
+func (p *VersionPacket) UnmarshalBinary(data []byte) (err error) {
+ buf := NewBuffer(data)
+
+ if p.Version, err = buf.ConsumeUint32(); err != nil {
+ return err
+ }
+
+ for buf.Len() > 0 {
+ var ext ExtensionPair
+ if err := ext.UnmarshalFrom(buf); err != nil {
+ return err
+ }
+
+ p.Extensions = append(p.Extensions, &ext)
+ }
+
+ return nil
+}
diff --git a/vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/open_packets.go b/vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/open_packets.go
new file mode 100644
index 000000000..135871142
--- /dev/null
+++ b/vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/open_packets.go
@@ -0,0 +1,89 @@
+package filexfer
+
+// SSH_FXF_* flags.
+const (
+ FlagRead = 1 << iota // SSH_FXF_READ
+ FlagWrite // SSH_FXF_WRITE
+ FlagAppend // SSH_FXF_APPEND
+ FlagCreate // SSH_FXF_CREAT
+ FlagTruncate // SSH_FXF_TRUNC
+ FlagExclusive // SSH_FXF_EXCL
+)
+
+// OpenPacket defines the SSH_FXP_OPEN packet.
+type OpenPacket struct {
+ Filename string
+ PFlags uint32
+ Attrs Attributes
+}
+
+// Type returns the SSH_FXP_xy value associated with this packet type.
+func (p *OpenPacket) Type() PacketType {
+ return PacketTypeOpen
+}
+
+// MarshalPacket returns p as a two-part binary encoding of p.
+func (p *OpenPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
+ buf := NewBuffer(b)
+ if buf.Cap() < 9 {
+ // string(filename) + uint32(pflags) + ATTRS(attrs)
+ size := 4 + len(p.Filename) + 4 + p.Attrs.Len()
+ buf = NewMarshalBuffer(size)
+ }
+
+ buf.StartPacket(PacketTypeOpen, reqid)
+ buf.AppendString(p.Filename)
+ buf.AppendUint32(p.PFlags)
+
+ p.Attrs.MarshalInto(buf)
+
+ return buf.Packet(payload)
+}
+
+// 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
+ }
+
+ return p.Attrs.UnmarshalFrom(buf)
+}
+
+// OpenDirPacket defines the SSH_FXP_OPENDIR packet.
+type OpenDirPacket struct {
+ Path string
+}
+
+// Type returns the SSH_FXP_xy value associated with this packet type.
+func (p *OpenDirPacket) Type() PacketType {
+ return PacketTypeOpenDir
+}
+
+// MarshalPacket returns p as a two-part binary encoding of p.
+func (p *OpenDirPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
+ buf := NewBuffer(b)
+ if buf.Cap() < 9 {
+ size := 4 + len(p.Path) // string(path)
+ buf = NewMarshalBuffer(size)
+ }
+
+ buf.StartPacket(PacketTypeOpenDir, reqid)
+ buf.AppendString(p.Path)
+
+ return buf.Packet(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
+ }
+
+ return nil
+}
diff --git a/vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/packets.go b/vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/packets.go
new file mode 100644
index 000000000..3f24e9c22
--- /dev/null
+++ b/vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/packets.go
@@ -0,0 +1,323 @@
+package filexfer
+
+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,
+// where a response will be expected to be of a limited number of types,
+// and unmarshaling unknown/unexpected response packets is unnecessary.
+//
+// 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
+type RawPacket struct {
+ PacketType PacketType
+ RequestID uint32
+
+ Data Buffer
+}
+
+// Type returns the Type field defining the SSH_FXP_xy type for this packet.
+func (p *RawPacket) Type() PacketType {
+ return p.PacketType
+}
+
+// Reset clears the pointers and reference-semantic variables of RawPacket,
+// releasing underlying resources, and making them and the RawPacket suitable to be reused,
+// so long as no other references have been kept.
+func (p *RawPacket) Reset() {
+ p.Data = Buffer{}
+}
+
+// MarshalPacket returns p as a two-part binary encoding of p.
+//
+// The internal p.RequestID is overridden by the reqid argument.
+func (p *RawPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
+ buf := NewBuffer(b)
+ if buf.Cap() < 9 {
+ buf = NewMarshalBuffer(0)
+ }
+
+ buf.StartPacket(p.PacketType, reqid)
+
+ return buf.Packet(p.Data.Bytes())
+}
+
+// MarshalBinary returns p as the binary encoding of p.
+//
+// This is a convenience implementation primarily intended for tests,
+// because it is inefficient with allocations.
+func (p *RawPacket) MarshalBinary() ([]byte, error) {
+ return ComposePacket(p.MarshalPacket(p.RequestID, nil))
+}
+
+// UnmarshalFrom decodes a RawPacket from the given Buffer into p.
+//
+// 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.Data = *buf
+ return nil
+}
+
+// UnmarshalBinary decodes a full raw packet out of the given data.
+// It is assumed that the uint32(length) has already been consumed to receive the data.
+//
+// This is a convenience implementation primarily intended for tests,
+// because this must clone the given data byte slice,
+// as Data is not allowed to alias any part of the data byte slice.
+func (p *RawPacket) UnmarshalBinary(data []byte) error {
+ clone := make([]byte, len(data))
+ n := copy(clone, data)
+ return p.UnmarshalFrom(NewBuffer(clone[:n]))
+}
+
+// readPacket reads a uint32 length-prefixed binary data packet from r.
+// using the given byte slice as a backing array.
+//
+// If the packet length read from r is bigger than maxPacketLength,
+// or greater than math.MaxInt32 on a 32-bit implementation,
+// then a `ErrLongPacket` error will be returned.
+//
+// If the given byte slice is insufficient to hold the packet,
+// then it will be extended to fill the packet size.
+func readPacket(r io.Reader, b []byte, maxPacketLength uint32) ([]byte, error) {
+ if cap(b) < 4 {
+ // We will need allocate our own buffer just for reading the packet length.
+
+ // However, we don’t really want to allocate an extremely narrow buffer (4-bytes),
+ // and cause unnecessary allocation churn from both length reads and small packet reads,
+ // so we use smallBufferSize from the bytes package as a reasonable guess.
+
+ // But if callers really do want to force narrow throw-away allocation of every packet body,
+ // they can do so with a buffer of capacity 4.
+ b = make([]byte, smallBufferSize)
+ }
+
+ if _, err := io.ReadFull(r, b[:4]); err != nil {
+ return nil, err
+ }
+
+ length := unmarshalUint32(b)
+ if int(length) < 5 {
+ // Must have at least uint8(type) and uint32(request-id)
+
+ if int(length) < 0 {
+ // Only possible when strconv.IntSize == 32,
+ // the packet length is longer than math.MaxInt32,
+ // and thus longer than any possible slice.
+ return nil, ErrLongPacket
+ }
+
+ return nil, ErrShortPacket
+ }
+ if length > maxPacketLength {
+ return nil, ErrLongPacket
+ }
+
+ if int(length) > cap(b) {
+ // We know int(length) must be positive, because of tests above.
+ b = make([]byte, length)
+ }
+
+ n, err := io.ReadFull(r, b[:length])
+ return b[:n], err
+}
+
+// ReadFrom provides a simple functional packet reader,
+// using the given byte slice as a backing array.
+//
+// To protect against potential denial of service attacks,
+// if the read packet length is longer than maxPacketLength,
+// then no packet data will be read, and ErrLongPacket will be returned.
+// (On 32-bit int architectures, all packets >= 2^31 in length
+// will return ErrLongPacket regardless of maxPacketLength.)
+//
+// If the read packet length is longer than cap(b),
+// then a throw-away slice will allocated to meet the exact packet length.
+// This can be used to limit the length of reused buffers,
+// while still allowing reception of occasional large packets.
+//
+// The Data field may alias the passed in byte slice,
+// so the byte slice passed in should not be reused before RawPacket.Reset().
+func (p *RawPacket) ReadFrom(r io.Reader, b []byte, maxPacketLength uint32) error {
+ b, err := readPacket(r, b, maxPacketLength)
+ if err != nil {
+ return err
+ }
+
+ return p.UnmarshalFrom(NewBuffer(b))
+}
+
+// RequestPacket implements the general packet format from draft-ietf-secsh-filexfer-02
+// but also automatically decode/encodes valid request packets (2 < type < 100 || type == 200).
+//
+// RequestPacket is intended for use in servers receiving requests,
+// where any arbitrary request may be received, and so decoding them automatically
+// is useful.
+//
+// For clients expecting to receive specific response packet types,
+// 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
+type RequestPacket struct {
+ RequestID uint32
+
+ Request Packet
+}
+
+// Type returns the SSH_FXP_xy value associated with the underlying packet.
+func (p *RequestPacket) Type() PacketType {
+ return p.Request.Type()
+}
+
+// Reset clears the pointers and reference-semantic variables in RequestPacket,
+// releasing underlying resources, and making them and the RequestPacket suitable to be reused,
+// so long as no other references have been kept.
+func (p *RequestPacket) Reset() {
+ p.Request = nil
+}
+
+// MarshalPacket returns p as a two-part binary encoding of p.
+//
+// The internal p.RequestID is overridden by the reqid argument.
+func (p *RequestPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
+ if p.Request == nil {
+ return nil, nil, errors.New("empty request packet")
+ }
+
+ return p.Request.MarshalPacket(reqid, b)
+}
+
+// MarshalBinary returns p as the binary encoding of p.
+//
+// This is a convenience implementation primarily intended for tests,
+// because it is inefficient with allocations.
+func (p *RequestPacket) MarshalBinary() ([]byte, error) {
+ return ComposePacket(p.MarshalPacket(p.RequestID, nil))
+}
+
+// UnmarshalFrom decodes a RequestPacket from the given Buffer into p.
+//
+// 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()
+ 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
+ }
+
+ return p.Request.UnmarshalPacketBody(buf)
+}
+
+// UnmarshalBinary decodes a full request packet out of the given data.
+// It is assumed that the uint32(length) has already been consumed to receive the data.
+//
+// This is a convenience implementation primarily intended for tests,
+// because this must clone the given data byte slice,
+// as Request is not allowed to alias any part of the data byte slice.
+func (p *RequestPacket) UnmarshalBinary(data []byte) error {
+ clone := make([]byte, len(data))
+ n := copy(clone, data)
+ return p.UnmarshalFrom(NewBuffer(clone[:n]))
+}
+
+// ReadFrom provides a simple functional packet reader,
+// using the given byte slice as a backing array.
+//
+// To protect against potential denial of service attacks,
+// if the read packet length is longer than maxPacketLength,
+// then no packet data will be read, and ErrLongPacket will be returned.
+// (On 32-bit int architectures, all packets >= 2^31 in length
+// will return ErrLongPacket regardless of maxPacketLength.)
+//
+// If the read packet length is longer than cap(b),
+// then a throw-away slice will allocated to meet the exact packet length.
+// This can be used to limit the length of reused buffers,
+// while still allowing reception of occasional large packets.
+//
+// The Request field may alias the passed in byte slice,
+// so the byte slice passed in should not be reused before RawPacket.Reset().
+func (p *RequestPacket) ReadFrom(r io.Reader, b []byte, maxPacketLength uint32) error {
+ b, err := readPacket(r, b, maxPacketLength)
+ if err != nil {
+ return err
+ }
+
+ return p.UnmarshalFrom(NewBuffer(b))
+}
diff --git a/vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/path_packets.go b/vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/path_packets.go
new file mode 100644
index 000000000..e6f692d9f
--- /dev/null
+++ b/vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/path_packets.go
@@ -0,0 +1,368 @@
+package filexfer
+
+// LStatPacket defines the SSH_FXP_LSTAT packet.
+type LStatPacket struct {
+ Path string
+}
+
+// Type returns the SSH_FXP_xy value associated with this packet type.
+func (p *LStatPacket) Type() PacketType {
+ return PacketTypeLStat
+}
+
+// MarshalPacket returns p as a two-part binary encoding of p.
+func (p *LStatPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
+ buf := NewBuffer(b)
+ if buf.Cap() < 9 {
+ size := 4 + len(p.Path) // string(path)
+ buf = NewMarshalBuffer(size)
+ }
+
+ buf.StartPacket(PacketTypeLStat, reqid)
+ buf.AppendString(p.Path)
+
+ return buf.Packet(payload)
+}
+
+// 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
+ }
+
+ return nil
+}
+
+// SetstatPacket defines the SSH_FXP_SETSTAT packet.
+type SetstatPacket struct {
+ Path string
+ Attrs Attributes
+}
+
+// Type returns the SSH_FXP_xy value associated with this packet type.
+func (p *SetstatPacket) Type() PacketType {
+ return PacketTypeSetstat
+}
+
+// MarshalPacket returns p as a two-part binary encoding of p.
+func (p *SetstatPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
+ buf := NewBuffer(b)
+ if buf.Cap() < 9 {
+ size := 4 + len(p.Path) + p.Attrs.Len() // string(path) + ATTRS(attrs)
+ buf = NewMarshalBuffer(size)
+ }
+
+ buf.StartPacket(PacketTypeSetstat, reqid)
+ buf.AppendString(p.Path)
+
+ p.Attrs.MarshalInto(buf)
+
+ return buf.Packet(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
+ }
+
+ return p.Attrs.UnmarshalFrom(buf)
+}
+
+// RemovePacket defines the SSH_FXP_REMOVE packet.
+type RemovePacket struct {
+ Path string
+}
+
+// Type returns the SSH_FXP_xy value associated with this packet type.
+func (p *RemovePacket) Type() PacketType {
+ return PacketTypeRemove
+}
+
+// MarshalPacket returns p as a two-part binary encoding of p.
+func (p *RemovePacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
+ buf := NewBuffer(b)
+ if buf.Cap() < 9 {
+ size := 4 + len(p.Path) // string(path)
+ buf = NewMarshalBuffer(size)
+ }
+
+ buf.StartPacket(PacketTypeRemove, reqid)
+ buf.AppendString(p.Path)
+
+ return buf.Packet(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
+ }
+
+ return nil
+}
+
+// MkdirPacket defines the SSH_FXP_MKDIR packet.
+type MkdirPacket struct {
+ Path string
+ Attrs Attributes
+}
+
+// Type returns the SSH_FXP_xy value associated with this packet type.
+func (p *MkdirPacket) Type() PacketType {
+ return PacketTypeMkdir
+}
+
+// MarshalPacket returns p as a two-part binary encoding of p.
+func (p *MkdirPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
+ buf := NewBuffer(b)
+ if buf.Cap() < 9 {
+ size := 4 + len(p.Path) + p.Attrs.Len() // string(path) + ATTRS(attrs)
+ buf = NewMarshalBuffer(size)
+ }
+
+ buf.StartPacket(PacketTypeMkdir, reqid)
+ buf.AppendString(p.Path)
+
+ p.Attrs.MarshalInto(buf)
+
+ return buf.Packet(payload)
+}
+
+// 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
+ }
+
+ return p.Attrs.UnmarshalFrom(buf)
+}
+
+// RmdirPacket defines the SSH_FXP_RMDIR packet.
+type RmdirPacket struct {
+ Path string
+}
+
+// Type returns the SSH_FXP_xy value associated with this packet type.
+func (p *RmdirPacket) Type() PacketType {
+ return PacketTypeRmdir
+}
+
+// MarshalPacket returns p as a two-part binary encoding of p.
+func (p *RmdirPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
+ buf := NewBuffer(b)
+ if buf.Cap() < 9 {
+ size := 4 + len(p.Path) // string(path)
+ buf = NewMarshalBuffer(size)
+ }
+
+ buf.StartPacket(PacketTypeRmdir, reqid)
+ buf.AppendString(p.Path)
+
+ return buf.Packet(payload)
+}
+
+// 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
+ }
+
+ return nil
+}
+
+// RealPathPacket defines the SSH_FXP_REALPATH packet.
+type RealPathPacket struct {
+ Path string
+}
+
+// Type returns the SSH_FXP_xy value associated with this packet type.
+func (p *RealPathPacket) Type() PacketType {
+ return PacketTypeRealPath
+}
+
+// MarshalPacket returns p as a two-part binary encoding of p.
+func (p *RealPathPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
+ buf := NewBuffer(b)
+ if buf.Cap() < 9 {
+ size := 4 + len(p.Path) // string(path)
+ buf = NewMarshalBuffer(size)
+ }
+
+ buf.StartPacket(PacketTypeRealPath, reqid)
+ buf.AppendString(p.Path)
+
+ return buf.Packet(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
+ }
+
+ return nil
+}
+
+// StatPacket defines the SSH_FXP_STAT packet.
+type StatPacket struct {
+ Path string
+}
+
+// Type returns the SSH_FXP_xy value associated with this packet type.
+func (p *StatPacket) Type() PacketType {
+ return PacketTypeStat
+}
+
+// MarshalPacket returns p as a two-part binary encoding of p.
+func (p *StatPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
+ buf := NewBuffer(b)
+ if buf.Cap() < 9 {
+ size := 4 + len(p.Path) // string(path)
+ buf = NewMarshalBuffer(size)
+ }
+
+ buf.StartPacket(PacketTypeStat, reqid)
+ buf.AppendString(p.Path)
+
+ return buf.Packet(payload)
+}
+
+// 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
+ }
+
+ return nil
+}
+
+// RenamePacket defines the SSH_FXP_RENAME packet.
+type RenamePacket struct {
+ OldPath string
+ NewPath string
+}
+
+// Type returns the SSH_FXP_xy value associated with this packet type.
+func (p *RenamePacket) Type() PacketType {
+ return PacketTypeRename
+}
+
+// MarshalPacket returns p as a two-part binary encoding of p.
+func (p *RenamePacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
+ buf := NewBuffer(b)
+ if buf.Cap() < 9 {
+ // string(oldpath) + string(newpath)
+ size := 4 + len(p.OldPath) + 4 + len(p.NewPath)
+ buf = NewMarshalBuffer(size)
+ }
+
+ buf.StartPacket(PacketTypeRename, reqid)
+ buf.AppendString(p.OldPath)
+ buf.AppendString(p.NewPath)
+
+ return buf.Packet(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
+ }
+
+ if p.NewPath, err = buf.ConsumeString(); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+// ReadLinkPacket defines the SSH_FXP_READLINK packet.
+type ReadLinkPacket struct {
+ Path string
+}
+
+// Type returns the SSH_FXP_xy value associated with this packet type.
+func (p *ReadLinkPacket) Type() PacketType {
+ return PacketTypeReadLink
+}
+
+// MarshalPacket returns p as a two-part binary encoding of p.
+func (p *ReadLinkPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
+ buf := NewBuffer(b)
+ if buf.Cap() < 9 {
+ size := 4 + len(p.Path) // string(path)
+ buf = NewMarshalBuffer(size)
+ }
+
+ buf.StartPacket(PacketTypeReadLink, reqid)
+ buf.AppendString(p.Path)
+
+ return buf.Packet(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
+ }
+
+ return nil
+}
+
+// SymlinkPacket defines the SSH_FXP_SYMLINK packet.
+//
+// The order of the arguments to the SSH_FXP_SYMLINK method was inadvertently reversed.
+// Unfortunately, the reversal was not noticed until the server was widely deployed.
+// Covered in Section 3.1 of https://github.com/openssh/openssh-portable/blob/master/PROTOCOL
+type SymlinkPacket struct {
+ LinkPath string
+ TargetPath string
+}
+
+// Type returns the SSH_FXP_xy value associated with this packet type.
+func (p *SymlinkPacket) Type() PacketType {
+ return PacketTypeSymlink
+}
+
+// MarshalPacket returns p as a two-part binary encoding of p.
+func (p *SymlinkPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
+ buf := NewBuffer(b)
+ if buf.Cap() < 9 {
+ // string(targetpath) + string(linkpath)
+ size := 4 + len(p.TargetPath) + 4 + len(p.LinkPath)
+ buf = NewMarshalBuffer(size)
+ }
+
+ buf.StartPacket(PacketTypeSymlink, reqid)
+
+ // Arguments were inadvertently reversed.
+ buf.AppendString(p.TargetPath)
+ buf.AppendString(p.LinkPath)
+
+ return buf.Packet(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) {
+ // Arguments were inadvertently reversed.
+ if p.TargetPath, err = buf.ConsumeString(); err != nil {
+ return err
+ }
+
+ if p.LinkPath, err = buf.ConsumeString(); err != nil {
+ return err
+ }
+
+ return nil
+}
diff --git a/vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/permissions.go b/vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/permissions.go
new file mode 100644
index 000000000..2fe63d591
--- /dev/null
+++ b/vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/permissions.go
@@ -0,0 +1,114 @@
+package filexfer
+
+// FileMode represents a file’s mode and permission bits.
+// The bits are defined according to POSIX standards,
+// and may not apply to the OS being built for.
+type FileMode uint32
+
+// Permission flags, defined here to avoid potential inconsistencies in individual OS implementations.
+const (
+ ModePerm FileMode = 0o0777 // S_IRWXU | S_IRWXG | S_IRWXO
+ ModeUserRead FileMode = 0o0400 // S_IRUSR
+ ModeUserWrite FileMode = 0o0200 // S_IWUSR
+ ModeUserExec FileMode = 0o0100 // S_IXUSR
+ ModeGroupRead FileMode = 0o0040 // S_IRGRP
+ ModeGroupWrite FileMode = 0o0020 // S_IWGRP
+ ModeGroupExec FileMode = 0o0010 // S_IXGRP
+ ModeOtherRead FileMode = 0o0004 // S_IROTH
+ ModeOtherWrite FileMode = 0o0002 // S_IWOTH
+ ModeOtherExec FileMode = 0o0001 // S_IXOTH
+
+ ModeSetUID FileMode = 0o4000 // S_ISUID
+ ModeSetGID FileMode = 0o2000 // S_ISGID
+ ModeSticky FileMode = 0o1000 // S_ISVTX
+
+ ModeType FileMode = 0xF000 // S_IFMT
+ ModeNamedPipe FileMode = 0x1000 // S_IFIFO
+ ModeCharDevice FileMode = 0x2000 // S_IFCHR
+ ModeDir FileMode = 0x4000 // S_IFDIR
+ ModeDevice FileMode = 0x6000 // S_IFBLK
+ ModeRegular FileMode = 0x8000 // S_IFREG
+ ModeSymlink FileMode = 0xA000 // S_IFLNK
+ ModeSocket FileMode = 0xC000 // S_IFSOCK
+)
+
+// IsDir reports whether m describes a directory.
+// That is, it tests for m.Type() == ModeDir.
+func (m FileMode) IsDir() bool {
+ return (m & ModeType) == ModeDir
+}
+
+// IsRegular reports whether m describes a regular file.
+// That is, it tests for m.Type() == ModeRegular
+func (m FileMode) IsRegular() bool {
+ return (m & ModeType) == ModeRegular
+}
+
+// Perm returns the POSIX permission bits in m (m & ModePerm).
+func (m FileMode) Perm() FileMode {
+ return (m & ModePerm)
+}
+
+// Type returns the type bits in m (m & ModeType).
+func (m FileMode) Type() FileMode {
+ return (m & ModeType)
+}
+
+// String returns a `-rwxrwxrwx` style string representing the `ls -l` POSIX permissions string.
+func (m FileMode) String() string {
+ var buf [10]byte
+
+ switch m.Type() {
+ case ModeRegular:
+ buf[0] = '-'
+ case ModeDir:
+ buf[0] = 'd'
+ case ModeSymlink:
+ buf[0] = 'l'
+ case ModeDevice:
+ buf[0] = 'b'
+ case ModeCharDevice:
+ buf[0] = 'c'
+ case ModeNamedPipe:
+ buf[0] = 'p'
+ case ModeSocket:
+ buf[0] = 's'
+ default:
+ buf[0] = '?'
+ }
+
+ const rwx = "rwxrwxrwx"
+ for i, c := range rwx {
+ if m&(1<<uint(9-1-i)) != 0 {
+ buf[i+1] = byte(c)
+ } else {
+ buf[i+1] = '-'
+ }
+ }
+
+ if m&ModeSetUID != 0 {
+ if buf[3] == 'x' {
+ buf[3] = 's'
+ } else {
+ buf[3] = 'S'
+ }
+ }
+
+ if m&ModeSetGID != 0 {
+ if buf[6] == 'x' {
+ buf[6] = 's'
+ } else {
+ buf[6] = 'S'
+ }
+ }
+
+ if m&ModeSticky != 0 {
+ if buf[9] == 'x' {
+ buf[9] = 't'
+ } else {
+ buf[9] = 'T'
+ }
+ }
+
+ return string(buf[:])
+}
diff --git a/vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/response_packets.go b/vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/response_packets.go
new file mode 100644
index 000000000..7a9b3eae8
--- /dev/null
+++ b/vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/response_packets.go
@@ -0,0 +1,243 @@
+package filexfer
+
+import (
+ "fmt"
+)
+
+// StatusPacket defines the SSH_FXP_STATUS packet.
+//
+// Specified in https://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#section-7
+type StatusPacket struct {
+ StatusCode Status
+ ErrorMessage string
+ LanguageTag string
+}
+
+// Error makes StatusPacket an error type.
+func (p *StatusPacket) Error() string {
+ if p.ErrorMessage == "" {
+ return "sftp: " + p.StatusCode.String()
+ }
+
+ return fmt.Sprintf("sftp: %q (%s)", p.ErrorMessage, p.StatusCode)
+}
+
+// Is returns true if target is a StatusPacket with the same StatusCode,
+// or target is a Status code which is the same as SatusCode.
+func (p *StatusPacket) Is(target error) bool {
+ if target, ok := target.(*StatusPacket); ok {
+ return p.StatusCode == target.StatusCode
+ }
+
+ return p.StatusCode == target
+}
+
+// Type returns the SSH_FXP_xy value associated with this packet type.
+func (p *StatusPacket) Type() PacketType {
+ return PacketTypeStatus
+}
+
+// MarshalPacket returns p as a two-part binary encoding of p.
+func (p *StatusPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
+ buf := NewBuffer(b)
+ if buf.Cap() < 9 {
+ // uint32(error/status code) + string(error message) + string(language tag)
+ size := 4 + 4 + len(p.ErrorMessage) + 4 + len(p.LanguageTag)
+ buf = NewMarshalBuffer(size)
+ }
+
+ buf.StartPacket(PacketTypeStatus, reqid)
+ buf.AppendUint32(uint32(p.StatusCode))
+ buf.AppendString(p.ErrorMessage)
+ buf.AppendString(p.LanguageTag)
+
+ return buf.Packet(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
+ }
+
+ if p.LanguageTag, err = buf.ConsumeString(); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+// HandlePacket defines the SSH_FXP_HANDLE packet.
+type HandlePacket struct {
+ Handle string
+}
+
+// Type returns the SSH_FXP_xy value associated with this packet type.
+func (p *HandlePacket) Type() PacketType {
+ return PacketTypeHandle
+}
+
+// MarshalPacket returns p as a two-part binary encoding of p.
+func (p *HandlePacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
+ buf := NewBuffer(b)
+ if buf.Cap() < 9 {
+ size := 4 + len(p.Handle) // string(handle)
+ buf = NewMarshalBuffer(size)
+ }
+
+ buf.StartPacket(PacketTypeHandle, reqid)
+ buf.AppendString(p.Handle)
+
+ return buf.Packet(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
+ }
+
+ return nil
+}
+
+// DataPacket defines the SSH_FXP_DATA packet.
+type DataPacket struct {
+ Data []byte
+}
+
+// Type returns the SSH_FXP_xy value associated with this packet type.
+func (p *DataPacket) Type() PacketType {
+ return PacketTypeData
+}
+
+// MarshalPacket returns p as a two-part binary encoding of p.
+func (p *DataPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
+ buf := NewBuffer(b)
+ if buf.Cap() < 9 {
+ size := 4 // uint32(len(data)); data content in payload
+ buf = NewMarshalBuffer(size)
+ }
+
+ buf.StartPacket(PacketTypeData, reqid)
+ buf.AppendUint32(uint32(len(p.Data)))
+
+ return buf.Packet(p.Data)
+}
+
+// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
+// It is assumed that the uint32(request-id) has already been consumed.
+//
+// If p.Data is already populated, and of sufficient length to hold the data,
+// then this will copy the data into that byte slice.
+//
+// If p.Data has a length insufficient to hold the data,
+// then this will make a new slice of sufficient length, and copy the data into that.
+//
+// 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
+ }
+
+ if len(p.Data) < len(data) {
+ p.Data = make([]byte, len(data))
+ }
+
+ n := copy(p.Data, data)
+ p.Data = p.Data[:n]
+ return nil
+}
+
+// NamePacket defines the SSH_FXP_NAME packet.
+type NamePacket struct {
+ Entries []*NameEntry
+}
+
+// Type returns the SSH_FXP_xy value associated with this packet type.
+func (p *NamePacket) Type() PacketType {
+ return PacketTypeName
+}
+
+// MarshalPacket returns p as a two-part binary encoding of p.
+func (p *NamePacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
+ buf := NewBuffer(b)
+ if buf.Cap() < 9 {
+ size := 4 // uint32(len(entries))
+
+ for _, e := range p.Entries {
+ size += e.Len()
+ }
+
+ buf = NewMarshalBuffer(size)
+ }
+
+ buf.StartPacket(PacketTypeName, reqid)
+ buf.AppendUint32(uint32(len(p.Entries)))
+
+ for _, e := range p.Entries {
+ e.MarshalInto(buf)
+ }
+
+ return buf.Packet(payload)
+}
+
+// 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
+ }
+
+ p.Entries = make([]*NameEntry, 0, count)
+
+ for i := uint32(0); i < count; i++ {
+ var e NameEntry
+ if err := e.UnmarshalFrom(buf); err != nil {
+ return err
+ }
+
+ p.Entries = append(p.Entries, &e)
+ }
+
+ return nil
+}
+
+// AttrsPacket defines the SSH_FXP_ATTRS packet.
+type AttrsPacket struct {
+ Attrs Attributes
+}
+
+// Type returns the SSH_FXP_xy value associated with this packet type.
+func (p *AttrsPacket) Type() PacketType {
+ return PacketTypeAttrs
+}
+
+// MarshalPacket returns p as a two-part binary encoding of p.
+func (p *AttrsPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
+ buf := NewBuffer(b)
+ if buf.Cap() < 9 {
+ size := p.Attrs.Len() // ATTRS(attrs)
+ buf = NewMarshalBuffer(size)
+ }
+
+ buf.StartPacket(PacketTypeAttrs, reqid)
+ p.Attrs.MarshalInto(buf)
+
+ return buf.Packet(payload)
+}
+
+// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
+// It is assumed that the uint32(request-id) has already been consumed.
+func (p *AttrsPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
+ return p.Attrs.UnmarshalFrom(buf)
+}