add --module flag

Support a new concept in containers.conf called "modules".  A "module"
is a containers.conf file located at a specific directory.  More than
one module can be loaded in the specified order, following existing
override semantics.

There are three directories to load modules from:
 - $CONFIG_HOME/containers/containers.conf.modules
 - /etc/containers/containers.conf.modules
 - /usr/share/containers/containers.conf.modules

With CONFIG_HOME pointing to $HOME/.config or, if set, $XDG_CONFIG_HOME.
Absolute paths will be loaded as is, relative paths will be resolved
relative to the three directories above allowing for admin configs
(/etc/) to override system configs (/usr/share/) and user configs
($CONFIG_HOME) to override admin configs.

Pulls in containers/common/pull/1599.

Signed-off-by: Valentin Rothberg <vrothberg@redhat.com>
This commit is contained in:
Valentin Rothberg
2023-08-09 15:50:15 +02:00
parent 9cd4286922
commit d5841ed528
65 changed files with 1253 additions and 756 deletions

33
vendor/github.com/pkg/sftp/attrs.go generated vendored
View File

@ -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"
@ -69,6 +69,20 @@ func fileInfoFromStat(stat *FileStat, name string) os.FileInfo {
}
}
// FileInfoUidGid extends os.FileInfo and adds callbacks for Uid and Gid retrieval,
// as an alternative to *syscall.Stat_t objects on unix systems.
type FileInfoUidGid interface {
os.FileInfo
Uid() uint32
Gid() uint32
}
// FileInfoUidGid extends os.FileInfo and adds a callbacks for extended data retrieval.
type FileInfoExtendedData interface {
os.FileInfo
Extended() []StatExtended
}
func fileStatFromInfo(fi os.FileInfo) (uint32, *FileStat) {
mtime := fi.ModTime().Unix()
atime := mtime
@ -86,5 +100,22 @@ func fileStatFromInfo(fi os.FileInfo) (uint32, *FileStat) {
// os specific file stat decoding
fileStatFromInfoOs(fi, &flags, fileStat)
// The call above will include the sshFileXferAttrUIDGID in case
// the os.FileInfo can be casted to *syscall.Stat_t on unix.
// If fi implements FileInfoUidGid, retrieve Uid, Gid from it instead.
if fiExt, ok := fi.(FileInfoUidGid); ok {
flags |= sshFileXferAttrUIDGID
fileStat.UID = fiExt.Uid()
fileStat.GID = fiExt.Gid()
}
// if fi implements FileInfoExtendedData, retrieve extended data from it
if fiExt, ok := fi.(FileInfoExtendedData); ok {
fileStat.Extended = fiExt.Extended()
if len(fileStat.Extended) > 0 {
flags |= sshFileXferAttrExtended
}
}
return flags, fileStat
}

View File

@ -1,3 +1,4 @@
//go:build plan9 || windows || android
// +build plan9 windows android
package sftp

View File

@ -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

65
vendor/github.com/pkg/sftp/client.go generated vendored
View File

@ -4,6 +4,7 @@ import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"io"
"math"
"os"
@ -226,15 +227,22 @@ func NewClientPipe(rd io.Reader, wr io.WriteCloser, opts ...ClientOption) (*Clie
if err := sftp.sendInit(); err != nil {
wr.Close()
return nil, err
return nil, fmt.Errorf("error sending init packet to server: %w", err)
}
if err := sftp.recvVersion(); err != nil {
wr.Close()
return nil, err
return nil, fmt.Errorf("error receiving version packet from server: %w", err)
}
sftp.clientConn.wg.Add(1)
go sftp.loop()
go func() {
defer sftp.clientConn.wg.Done()
if err := sftp.clientConn.recv(); err != nil {
sftp.clientConn.broadcastErr(err)
}
}()
return sftp, nil
}
@ -251,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
})
}
@ -267,8 +275,13 @@ func (c *Client) nextID() uint32 {
func (c *Client) recvVersion() error {
typ, data, err := c.recvPacket(0)
if err != nil {
if err == io.EOF {
return fmt.Errorf("server unexpectedly closed connection: %w", io.ErrUnexpectedEOF)
}
return err
}
if typ != sshFxpVersion {
return &unexpectedPacketErr{sshFxpVersion, typ}
}
@ -277,6 +290,7 @@ func (c *Client) recvVersion() error {
if err != nil {
return err
}
if version != sftpProtocolVersion {
return &unexpectedVersionErr{sftpProtocolVersion, version}
}
@ -910,6 +924,45 @@ func (c *Client) MkdirAll(path string) error {
return nil
}
// RemoveAll delete files recursively in the directory and Recursively delete subdirectories.
// An error will be returned if no file or directory with the specified path exists
func (c *Client) RemoveAll(path string) error {
// Get the file/directory information
fi, err := c.Stat(path)
if err != nil {
return err
}
if fi.IsDir() {
// Delete files recursively in the directory
files, err := c.ReadDir(path)
if err != nil {
return err
}
for _, file := range files {
if file.IsDir() {
// Recursively delete subdirectories
err = c.RemoveAll(path + "/" + file.Name())
if err != nil {
return err
}
} else {
// Delete individual files
err = c.Remove(path + "/" + file.Name())
if err != nil {
return err
}
}
}
}
return c.Remove(path)
}
// File represents a remote file.
type File struct {
c *Client
@ -1660,7 +1713,7 @@ func (f *File) ReadFromWithConcurrency(r io.Reader, concurrency int) (read int64
Handle: f.handle,
Offset: uint64(off),
Length: uint32(n),
Data: b,
Data: b[:n],
})
select {

12
vendor/github.com/pkg/sftp/conn.go generated vendored
View File

@ -18,7 +18,9 @@ type conn struct {
}
// the orderID is used in server mode if the allocator is enabled.
// For the client mode just pass 0
// For the client mode just pass 0.
// It returns io.EOF if the connection is closed and
// there are no more packets to read.
func (c *conn) recvPacket(orderID uint32) (uint8, []byte, error) {
return recvPacket(c, c.alloc, orderID)
}
@ -61,14 +63,6 @@ func (c *clientConn) Close() error {
return c.conn.Close()
}
func (c *clientConn) loop() {
defer c.wg.Done()
err := c.recv()
if err != nil {
c.broadcastErr(err)
}
}
// recv continuously reads from the server and forwards responses to the
// appropriate channel.
func (c *clientConn) recv() error {

View File

@ -1,3 +1,4 @@
//go:build debug
// +build debug
package sftp

1
vendor/github.com/pkg/sftp/fuzz.go generated vendored
View File

@ -1,3 +1,4 @@
//go:build gofuzz
// +build gofuzz
package sftp

View File

@ -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.

View File

@ -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 twos 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 twos 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.

View File

@ -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 {

View File

@ -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.

View File

@ -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.
//

View File

@ -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

View File

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

View File

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

View File

@ -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 {

View File

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

View File

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

View File

@ -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,18 +309,18 @@ 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.
//
// 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
// Covered in Section 4.1 of https://github.com/openssh/openssh-portable/blob/master/PROTOCOL
type SymlinkPacket struct {
LinkPath string
TargetPath string
@ -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) {
// Arguments were inadvertently reversed.
if p.TargetPath, err = buf.ConsumeString(); err != nil {
return err
*p = SymlinkPacket{
// Arguments were inadvertently reversed.
TargetPath: buf.ConsumeString(),
LinkPath: buf.ConsumeString(),
}
if p.LinkPath, err = buf.ConsumeString(); err != nil {
return err
}
return nil
return buf.Err
}

View File

@ -1,4 +1,4 @@
package filexfer
package sshfx
// FileMode represents a files mode and permission bits.
// The bits are defined according to POSIX standards,

View File

@ -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
@ -209,7 +196,7 @@ func (p *NamePacket) UnmarshalPacketBody(buf *Buffer) (err error) {
p.Entries = append(p.Entries, &e)
}
return nil
return buf.Err
}
// AttrsPacket defines the SSH_FXP_ATTRS packet.

View File

@ -60,6 +60,13 @@ func runLs(idLookup NameLookupFileLister, dirent os.FileInfo) string {
uid = lsFormatID(sys.UID)
gid = lsFormatID(sys.GID)
default:
if fiExt, ok := dirent.(FileInfoUidGid); ok {
uid = lsFormatID(fiExt.Uid())
gid = lsFormatID(fiExt.Gid())
break
}
numLinks, uid, gid = lsLinksUIDGID(dirent)
}

View File

@ -1,3 +1,4 @@
//go:build plan9
// +build plan9
package sftp

View File

@ -1,3 +1,4 @@
//go:build windows || android
// +build windows android
package sftp

View File

@ -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

View File

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

View File

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

25
vendor/github.com/pkg/sftp/packet.go generated vendored
View File

@ -71,6 +71,15 @@ func marshalFileInfo(b []byte, fi os.FileInfo) []byte {
b = marshalUint32(b, fileStat.Mtime)
}
if flags&sshFileXferAttrExtended != 0 {
b = marshalUint32(b, uint32(len(fileStat.Extended)))
for _, attr := range fileStat.Extended {
b = marshalString(b, attr.ExtType)
b = marshalString(b, attr.ExtData)
}
}
return b
}
@ -281,6 +290,11 @@ func recvPacket(r io.Reader, alloc *allocator, orderID uint32) (uint8, []byte, e
b = make([]byte, length)
}
if _, err := io.ReadFull(r, b[:length]); err != nil {
// ReadFull only returns EOF if it has read no bytes.
// In this case, that means a partial packet, and thus unexpected.
if err == io.EOF {
err = io.ErrUnexpectedEOF
}
debug("recv packet %d bytes: err %v", length, err)
return 0, nil, err
}
@ -522,7 +536,12 @@ func (p *sshFxpRmdirPacket) UnmarshalBinary(b []byte) error {
}
type sshFxpSymlinkPacket struct {
ID uint32
ID uint32
// 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 4.1 of https://github.com/openssh/openssh-portable/blob/master/PROTOCOL
Targetpath string
Linkpath string
}
@ -1242,7 +1261,7 @@ func (p *sshFxpExtendedPacketPosixRename) UnmarshalBinary(b []byte) error {
}
func (p *sshFxpExtendedPacketPosixRename) respond(s *Server) responsePacket {
err := os.Rename(toLocalPath(p.Oldpath), toLocalPath(p.Newpath))
err := os.Rename(s.toLocalPath(p.Oldpath), s.toLocalPath(p.Newpath))
return statusFromError(p.ID, err)
}
@ -1271,6 +1290,6 @@ func (p *sshFxpExtendedPacketHardlink) UnmarshalBinary(b []byte) error {
}
func (p *sshFxpExtendedPacketHardlink) respond(s *Server) responsePacket {
err := os.Link(toLocalPath(p.Oldpath), toLocalPath(p.Newpath))
err := os.Link(s.toLocalPath(p.Oldpath), s.toLocalPath(p.Newpath))
return statusFromError(p.ID, err)
}

View File

@ -1,3 +1,4 @@
//go:build !debug
// +build !debug
package sftp

View File

@ -391,21 +391,6 @@ func (fs *root) Filelist(r *Request) (ListerAt, error) {
return nil, err
}
return listerat{file}, nil
case "Readlink":
symlink, err := fs.readlink(r.Filepath)
if err != nil {
return nil, err
}
// SFTP-v2: The server will respond with a SSH_FXP_NAME packet containing only
// one name and a dummy attributes value.
return listerat{
&memFile{
name: symlink,
err: os.ErrNotExist, // prevent accidental use as a reader/writer.
},
}, nil
}
return nil, errors.New("unsupported")
@ -434,7 +419,7 @@ func (fs *root) readdir(pathname string) ([]os.FileInfo, error) {
return files, nil
}
func (fs *root) readlink(pathname string) (string, error) {
func (fs *root) Readlink(pathname string) (string, error) {
file, err := fs.lfetch(pathname)
if err != nil {
return "", err
@ -464,19 +449,10 @@ func (fs *root) Lstat(r *Request) (ListerAt, error) {
return listerat{file}, nil
}
// implements RealpathFileLister interface
func (fs *root) Realpath(p string) string {
if fs.startDirectory == "" || fs.startDirectory == "/" {
return cleanPath(p)
}
return cleanPathWithBase(fs.startDirectory, p)
}
// In memory file-system-y thing that the Hanlders live on
type root struct {
rootFile *memFile
mockErr error
startDirectory string
rootFile *memFile
mockErr error
mu sync.Mutex
files map[string]*memFile
@ -534,8 +510,8 @@ func (fs *root) exists(path string) bool {
return err != os.ErrNotExist
}
func (fs *root) fetch(path string) (*memFile, error) {
file, err := fs.lfetch(path)
func (fs *root) fetch(pathname string) (*memFile, error) {
file, err := fs.lfetch(pathname)
if err != nil {
return nil, err
}
@ -546,7 +522,12 @@ func (fs *root) fetch(path string) (*memFile, error) {
return nil, errTooManySymlinks
}
file, err = fs.lfetch(file.symlink)
linkTarget := file.symlink
if !path.IsAbs(linkTarget) {
linkTarget = path.Join(path.Dir(file.name), linkTarget)
}
file, err = fs.lfetch(linkTarget)
if err != nil {
return nil, err
}

View File

@ -74,6 +74,11 @@ type StatVFSFileCmder interface {
// FileLister should return an object that fulfils the ListerAt interface
// Note in cases of an error, the error text will be sent to the client.
// Called for Methods: List, Stat, Readlink
//
// Since Filelist returns an os.FileInfo, this can make it non-ideal for implementing Readlink.
// This is because the Name receiver method defined by that interface defines that it should only return the base name.
// However, Readlink is required to be capable of returning essentially any arbitrary valid path relative or absolute.
// In order to implement this more expressive requirement, implement [ReadlinkFileLister] which will then be used instead.
type FileLister interface {
Filelist(*Request) (ListerAt, error)
}
@ -87,12 +92,33 @@ type LstatFileLister interface {
}
// RealPathFileLister is a FileLister that implements the Realpath method.
// We use "/" as start directory for relative paths, implementing this
// interface you can customize the start directory.
// The built-in RealPath implementation does not resolve symbolic links.
// By implementing this interface you can customize the returned path
// and, for example, resolve symbolinc links if needed for your use case.
// You have to return an absolute POSIX path.
//
// Deprecated: if you want to set a start directory use WithStartDirectory RequestServerOption instead.
// Up to v1.13.5 the signature for the RealPath method was:
//
// # RealPath(string) string
//
// we have added a legacyRealPathFileLister that implements the old method
// to ensure that your code does not break.
// You should use the new method signature to avoid future issues
type RealPathFileLister interface {
FileLister
RealPath(string) (string, error)
}
// ReadlinkFileLister is a FileLister that implements the Readlink method.
// By implementing the Readlink method, it is possible to return any arbitrary valid path relative or absolute.
// This allows giving a better response than via the default FileLister (which is limited to os.FileInfo, whose Name method should only return the base name of a file)
type ReadlinkFileLister interface {
FileLister
Readlink(string) (string, error)
}
// This interface is here for backward compatibility only
type legacyRealPathFileLister interface {
FileLister
RealPath(string) string
}
@ -105,11 +131,19 @@ type NameLookupFileLister interface {
LookupGroupName(string) string
}
// ListerAt does for file lists what io.ReaderAt does for files.
// ListAt should return the number of entries copied and an io.EOF
// error if at end of list. This is testable by comparing how many you
// copied to how many could be copied (eg. n < len(ls) below).
// ListerAt does for file lists what io.ReaderAt does for files, i.e. a []os.FileInfo buffer is passed to the ListAt function
// and the entries that are populated in the buffer will be passed to the client.
//
// ListAt should return the number of entries copied and an io.EOF error if at end of list.
// This is testable by comparing how many you copied to how many could be copied (eg. n < len(ls) below).
// The copy() builtin is best for the copying.
//
// Uid and gid information will on unix systems be retrieved from [os.FileInfo.Sys]
// if this function returns a [syscall.Stat_t] when called on a populated entry.
// Alternatively, if the entry implements [FileInfoUidGid], it will be used for uid and gid information.
//
// If a populated entry implements [FileInfoExtendedData], extended attributes will also be returned to the client.
//
// Note in cases of an error, the error text will be sent to the client.
type ListerAt interface {
ListAt([]os.FileInfo, int64) (int, error)

View File

@ -1,10 +1,9 @@
//go:build plan9
// +build plan9
package sftp
import (
"path"
"path/filepath"
"syscall"
)
@ -15,20 +14,3 @@ func fakeFileInfoSys() interface{} {
func testOsSys(sys interface{}) error {
return nil
}
func toLocalPath(p string) string {
lp := filepath.FromSlash(p)
if path.IsAbs(p) {
tmp := lp[1:]
if filepath.IsAbs(tmp) {
// If the FromSlash without any starting slashes is absolute,
// then we have a filepath encoded with a prefix '/'.
// e.g. "/#s/boot" to "#s/boot"
return tmp
}
}
return lp
}

View File

@ -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

View File

@ -219,12 +219,21 @@ func (rs *RequestServer) packetWorker(ctx context.Context, pktChan chan orderedR
rpkt = statusFromError(pkt.ID, rs.closeRequest(handle))
case *sshFxpRealpathPacket:
var realPath string
if realPather, ok := rs.Handlers.FileList.(RealPathFileLister); ok {
realPath = realPather.RealPath(pkt.getPath())
} else {
var err error
switch pather := rs.Handlers.FileList.(type) {
case RealPathFileLister:
realPath, err = pather.RealPath(pkt.getPath())
case legacyRealPathFileLister:
realPath = pather.RealPath(pkt.getPath())
default:
realPath = cleanPathWithBase(rs.startDirectory, pkt.getPath())
}
rpkt = cleanPacketPath(pkt, realPath)
if err != nil {
rpkt = statusFromError(pkt.ID, err)
} else {
rpkt = cleanPacketPath(pkt, realPath)
}
case *sshFxpOpendirPacket:
request := requestFromPacket(ctx, pkt, rs.startDirectory)
handle := rs.nextRequest(request)

View File

@ -1,3 +1,4 @@
//go:build !windows && !plan9
// +build !windows,!plan9
package sftp
@ -21,7 +22,3 @@ func testOsSys(sys interface{}) error {
}
return nil
}
func toLocalPath(p string) string {
return p
}

View File

@ -187,6 +187,7 @@ func requestFromPacket(ctx context.Context, pkt hasPath, baseDir string) *Reques
// NOTE: given a POSIX compliant signature: symlink(target, linkpath string)
// this makes Request.Target the linkpath, and Request.Filepath the target.
request.Target = cleanPathWithBase(baseDir, p.Linkpath)
request.Filepath = p.Targetpath
case *sshFxpExtendedPacketHardlink:
request.Target = cleanPathWithBase(baseDir, p.Newpath)
}
@ -294,7 +295,12 @@ func (r *Request) call(handlers Handlers, pkt requestPacket, alloc *allocator, o
return filecmd(handlers.FileCmd, r, pkt)
case "List":
return filelist(handlers.FileList, r, pkt)
case "Stat", "Lstat", "Readlink":
case "Stat", "Lstat":
return filestat(handlers.FileList, r, pkt)
case "Readlink":
if readlinkFileLister, ok := handlers.FileList.(ReadlinkFileLister); ok {
return readlink(readlinkFileLister, r, pkt)
}
return filestat(handlers.FileList, r, pkt)
default:
return statusFromError(pkt.id(), fmt.Errorf("unexpected method: %s", r.Method))
@ -598,6 +604,23 @@ func filestat(h FileLister, r *Request, pkt requestPacket) responsePacket {
}
}
func readlink(readlinkFileLister ReadlinkFileLister, r *Request, pkt requestPacket) responsePacket {
resolved, err := readlinkFileLister.Readlink(r.Filepath)
if err != nil {
return statusFromError(pkt.id(), err)
}
return &sshFxpNamePacket{
ID: pkt.id(),
NameAttrs: []*sshFxpNameAttr{
{
Name: resolved,
LongName: resolved,
Attrs: emptyFileStat,
},
},
}
}
// init attributes of request object from packet data
func requestMethod(p requestPacket) (method string) {
switch p.(type) {

View File

@ -1,8 +1,6 @@
package sftp
import (
"path"
"path/filepath"
"syscall"
)
@ -13,32 +11,3 @@ func fakeFileInfoSys() interface{} {
func testOsSys(sys interface{}) error {
return nil
}
func toLocalPath(p string) string {
lp := filepath.FromSlash(p)
if path.IsAbs(p) {
tmp := lp
for len(tmp) > 0 && tmp[0] == '\\' {
tmp = tmp[1:]
}
if filepath.IsAbs(tmp) {
// If the FromSlash without any starting slashes is absolute,
// then we have a filepath encoded with a prefix '/'.
// e.g. "/C:/Windows" to "C:\\Windows"
return tmp
}
tmp += "\\"
if filepath.IsAbs(tmp) {
// If the FromSlash without any starting slashes but with extra end slash is absolute,
// then we have a filepath encoded with a prefix '/' and a dropped '/' at the end.
// e.g. "/C:" to "C:\\"
return tmp
}
}
return lp
}

62
vendor/github.com/pkg/sftp/server.go generated vendored
View File

@ -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
@ -33,6 +33,7 @@ type Server struct {
openFiles map[string]*os.File
openFilesLock sync.RWMutex
handleCount int
workDir string
}
func (svr *Server) nextHandle(f *os.File) string {
@ -128,6 +129,16 @@ func WithAllocator() ServerOption {
}
}
// WithServerWorkingDirectory sets a working directory to use as base
// for relative paths.
// If unset the default is current working directory (os.Getwd).
func WithServerWorkingDirectory(workDir string) ServerOption {
return func(s *Server) error {
s.workDir = cleanPath(workDir)
return nil
}
}
type rxPacket struct {
pktType fxp
pktBytes []byte
@ -174,7 +185,7 @@ func handlePacket(s *Server, p orderedRequest) error {
}
case *sshFxpStatPacket:
// stat the requested file
info, err := os.Stat(toLocalPath(p.Path))
info, err := os.Stat(s.toLocalPath(p.Path))
rpkt = &sshFxpStatResponse{
ID: p.ID,
info: info,
@ -184,7 +195,7 @@ func handlePacket(s *Server, p orderedRequest) error {
}
case *sshFxpLstatPacket:
// stat the requested file
info, err := os.Lstat(toLocalPath(p.Path))
info, err := os.Lstat(s.toLocalPath(p.Path))
rpkt = &sshFxpStatResponse{
ID: p.ID,
info: info,
@ -208,24 +219,24 @@ func handlePacket(s *Server, p orderedRequest) error {
}
case *sshFxpMkdirPacket:
// TODO FIXME: ignore flags field
err := os.Mkdir(toLocalPath(p.Path), 0755)
err := os.Mkdir(s.toLocalPath(p.Path), 0o755)
rpkt = statusFromError(p.ID, err)
case *sshFxpRmdirPacket:
err := os.Remove(toLocalPath(p.Path))
err := os.Remove(s.toLocalPath(p.Path))
rpkt = statusFromError(p.ID, err)
case *sshFxpRemovePacket:
err := os.Remove(toLocalPath(p.Filename))
err := os.Remove(s.toLocalPath(p.Filename))
rpkt = statusFromError(p.ID, err)
case *sshFxpRenamePacket:
err := os.Rename(toLocalPath(p.Oldpath), toLocalPath(p.Newpath))
err := os.Rename(s.toLocalPath(p.Oldpath), s.toLocalPath(p.Newpath))
rpkt = statusFromError(p.ID, err)
case *sshFxpSymlinkPacket:
err := os.Symlink(toLocalPath(p.Targetpath), toLocalPath(p.Linkpath))
err := os.Symlink(s.toLocalPath(p.Targetpath), s.toLocalPath(p.Linkpath))
rpkt = statusFromError(p.ID, err)
case *sshFxpClosePacket:
rpkt = statusFromError(p.ID, s.closeHandle(p.Handle))
case *sshFxpReadlinkPacket:
f, err := os.Readlink(toLocalPath(p.Path))
f, err := os.Readlink(s.toLocalPath(p.Path))
rpkt = &sshFxpNamePacket{
ID: p.ID,
NameAttrs: []*sshFxpNameAttr{
@ -240,7 +251,7 @@ func handlePacket(s *Server, p orderedRequest) error {
rpkt = statusFromError(p.ID, err)
}
case *sshFxpRealpathPacket:
f, err := filepath.Abs(toLocalPath(p.Path))
f, err := filepath.Abs(s.toLocalPath(p.Path))
f = cleanPath(f)
rpkt = &sshFxpNamePacket{
ID: p.ID,
@ -256,13 +267,14 @@ func handlePacket(s *Server, p orderedRequest) error {
rpkt = statusFromError(p.ID, err)
}
case *sshFxpOpendirPacket:
p.Path = toLocalPath(p.Path)
lp := s.toLocalPath(p.Path)
if stat, err := os.Stat(p.Path); err != nil {
if stat, err := os.Stat(lp); err != nil {
rpkt = statusFromError(p.ID, err)
} else if !stat.IsDir() {
rpkt = statusFromError(p.ID, &os.PathError{
Path: p.Path, Err: syscall.ENOTDIR})
Path: lp, Err: syscall.ENOTDIR,
})
} else {
rpkt = (&sshFxpOpenPacket{
ID: p.ID,
@ -315,7 +327,7 @@ func handlePacket(s *Server, p orderedRequest) error {
}
// Serve serves SFTP connections until the streams stop or the SFTP subsystem
// is stopped.
// is stopped. It returns nil if the server exits cleanly.
func (svr *Server) Serve() error {
defer func() {
if svr.pktMgr.alloc != nil {
@ -341,6 +353,10 @@ func (svr *Server) Serve() error {
for {
pktType, pktBytes, err = svr.serverConn.recvPacket(svr.pktMgr.getNextOrderID())
if err != nil {
// Check whether the connection terminated cleanly in-between packets.
if err == io.EOF {
err = nil
}
// we don't care about releasing allocated pages here, the server will quit and the allocator freed
break
}
@ -446,7 +462,7 @@ func (p *sshFxpOpenPacket) respond(svr *Server) responsePacket {
osFlags |= os.O_EXCL
}
f, err := os.OpenFile(toLocalPath(p.Path), osFlags, 0644)
f, err := os.OpenFile(svr.toLocalPath(p.Path), osFlags, 0o644)
if err != nil {
return statusFromError(p.ID, err)
}
@ -484,7 +500,7 @@ func (p *sshFxpSetstatPacket) respond(svr *Server) responsePacket {
b := p.Attrs.([]byte)
var err error
p.Path = toLocalPath(p.Path)
p.Path = svr.toLocalPath(p.Path)
debug("setstat name \"%s\"", p.Path)
if (p.Flags & sshFileXferAttrSize) != 0 {
@ -603,13 +619,15 @@ func statusFromError(id uint32, err error) *sshFxpStatusPacket {
return ret
}
switch e := err.(type) {
case fxerr:
if errors.Is(err, io.EOF) {
ret.StatusError.Code = sshFxEOF
return ret
}
var e fxerr
if errors.As(err, &e) {
ret.StatusError.Code = uint32(e)
default:
if e == io.EOF {
ret.StatusError.Code = sshFxEOF
}
return ret
}
return ret

27
vendor/github.com/pkg/sftp/server_plan9.go generated vendored Normal file
View File

@ -0,0 +1,27 @@
package sftp
import (
"path"
"path/filepath"
)
func (s *Server) toLocalPath(p string) string {
if s.workDir != "" && !path.IsAbs(p) {
p = path.Join(s.workDir, p)
}
lp := filepath.FromSlash(p)
if path.IsAbs(p) {
tmp := lp[1:]
if filepath.IsAbs(tmp) {
// If the FromSlash without any starting slashes is absolute,
// then we have a filepath encoded with a prefix '/'.
// e.g. "/#s/boot" to "#s/boot"
return tmp
}
}
return lp
}

View File

@ -1,3 +1,4 @@
//go:build darwin || linux
// +build darwin linux
// fill in statvfs structure with OS specific values

View File

@ -1,3 +1,4 @@
//go:build linux
// +build linux
package sftp

View File

@ -1,3 +1,4 @@
//go:build !darwin && !linux && !plan9
// +build !darwin,!linux,!plan9
package sftp

16
vendor/github.com/pkg/sftp/server_unix.go generated vendored Normal file
View File

@ -0,0 +1,16 @@
//go:build !windows && !plan9
// +build !windows,!plan9
package sftp
import (
"path"
)
func (s *Server) toLocalPath(p string) string {
if s.workDir != "" && !path.IsAbs(p) {
p = path.Join(s.workDir, p)
}
return p
}

39
vendor/github.com/pkg/sftp/server_windows.go generated vendored Normal file
View File

@ -0,0 +1,39 @@
package sftp
import (
"path"
"path/filepath"
)
func (s *Server) toLocalPath(p string) string {
if s.workDir != "" && !path.IsAbs(p) {
p = path.Join(s.workDir, p)
}
lp := filepath.FromSlash(p)
if path.IsAbs(p) {
tmp := lp
for len(tmp) > 0 && tmp[0] == '\\' {
tmp = tmp[1:]
}
if filepath.IsAbs(tmp) {
// If the FromSlash without any starting slashes is absolute,
// then we have a filepath encoded with a prefix '/'.
// e.g. "/C:/Windows" to "C:\\Windows"
return tmp
}
tmp += "\\"
if filepath.IsAbs(tmp) {
// If the FromSlash without any starting slashes but with extra end slash is absolute,
// then we have a filepath encoded with a prefix '/' and a dropped '/' at the end.
// e.g. "/C:" to "C:\\"
return tmp
}
}
return lp
}

2
vendor/github.com/pkg/sftp/sftp.go generated vendored
View File

@ -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 (

View File

@ -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

View File

@ -1,4 +1,6 @@
// +build !plan9,!windows
//go:build !plan9 && !windows && (!js || !wasm)
// +build !plan9
// +build !windows
// +build !js !wasm
package sftp