forked from TrueCloudLab/rclone
sftp: add required packages to vendor
This commit is contained in:
parent
9d331ce04b
commit
27e18b6efa
94 changed files with 25240 additions and 2 deletions
8
vendor/github.com/pkg/sftp/.gitignore
generated
vendored
Normal file
8
vendor/github.com/pkg/sftp/.gitignore
generated
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
.*.swo
|
||||
.*.swp
|
||||
|
||||
server_standalone/server_standalone
|
||||
|
||||
examples/sftp-server/id_rsa
|
||||
examples/sftp-server/id_rsa.pub
|
||||
examples/sftp-server/sftp-server
|
29
vendor/github.com/pkg/sftp/.travis.yml
generated
vendored
Normal file
29
vendor/github.com/pkg/sftp/.travis.yml
generated
vendored
Normal file
|
@ -0,0 +1,29 @@
|
|||
language: go
|
||||
go_import_path: github.com/pkg/sftp
|
||||
go:
|
||||
- 1.5.x
|
||||
- 1.6.x
|
||||
- 1.7.x
|
||||
- tip
|
||||
|
||||
os:
|
||||
- linux
|
||||
- osx
|
||||
|
||||
sudo: false
|
||||
|
||||
addons:
|
||||
ssh_known_hosts:
|
||||
- bitbucket.org
|
||||
|
||||
install:
|
||||
- go get -t -v ./...
|
||||
- ssh-keygen -t rsa -q -P "" -f $HOME/.ssh/id_rsa
|
||||
|
||||
script:
|
||||
- go test -integration -v ./...
|
||||
- go test -testserver -v ./...
|
||||
- go test -integration -testserver -v ./...
|
||||
- go test -race -integration -v ./...
|
||||
- go test -race -testserver -v ./...
|
||||
- go test -race -integration -testserver -v ./...
|
3
vendor/github.com/pkg/sftp/CONTRIBUTORS
generated
vendored
Normal file
3
vendor/github.com/pkg/sftp/CONTRIBUTORS
generated
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
Dave Cheney <dave@cheney.net>
|
||||
Saulius Gurklys <s4uliu5@gmail.com>
|
||||
John Eikenberry <jae@zhar.net>
|
9
vendor/github.com/pkg/sftp/LICENSE
generated
vendored
Normal file
9
vendor/github.com/pkg/sftp/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,9 @@
|
|||
Copyright (c) 2013, Dave Cheney
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
48
vendor/github.com/pkg/sftp/README.md
generated
vendored
Normal file
48
vendor/github.com/pkg/sftp/README.md
generated
vendored
Normal file
|
@ -0,0 +1,48 @@
|
|||
sftp
|
||||
----
|
||||
|
||||
The `sftp` package provides support for file system operations on remote ssh
|
||||
servers using the SFTP subsystem. It also implements an SFTP server for serving
|
||||
files from the filesystem.
|
||||
|
||||
[](https://travis-ci.org/pkg/sftp) [](http://godoc.org/github.com/pkg/sftp)
|
||||
|
||||
usage and examples
|
||||
------------------
|
||||
|
||||
See [godoc.org/github.com/pkg/sftp](http://godoc.org/github.com/pkg/sftp) for
|
||||
examples and usage.
|
||||
|
||||
The basic operation of the package mirrors the facilities of the
|
||||
[os](http://golang.org/pkg/os) package.
|
||||
|
||||
The Walker interface for directory traversal is heavily inspired by Keith
|
||||
Rarick's [fs](http://godoc.org/github.com/kr/fs) package.
|
||||
|
||||
roadmap
|
||||
-------
|
||||
|
||||
* There is way too much duplication in the Client methods. If there was an
|
||||
unmarshal(interface{}) method this would reduce a heap of the duplication.
|
||||
|
||||
contributing
|
||||
------------
|
||||
|
||||
We welcome pull requests, bug fixes and issue reports.
|
||||
|
||||
Before proposing a large change, first please discuss your change by raising an
|
||||
issue.
|
||||
|
||||
For API/code bugs, please include a small, self contained code example to
|
||||
reproduce the issue. For pull requests, remember test coverage.
|
||||
|
||||
We handle issues and pull requests with a 0 open philosophy. That means we will
|
||||
try to address the submission as soon as possible and will work toward a
|
||||
resolution. If progress can no longer be made (eg. unreproducible bug) or stops
|
||||
(eg. unresponsive submitter), we will close the bug.
|
||||
|
||||
Please remember that we don't have infinite time and so it is a good guideline
|
||||
that the less time it takes us to work with you on your idea/issue the greater
|
||||
the chance we will have the time to do it.
|
||||
|
||||
Thanks.
|
237
vendor/github.com/pkg/sftp/attrs.go
generated
vendored
Normal file
237
vendor/github.com/pkg/sftp/attrs.go
generated
vendored
Normal file
|
@ -0,0 +1,237 @@
|
|||
package sftp
|
||||
|
||||
// ssh_FXP_ATTRS support
|
||||
// see http://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#section-5
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
ssh_FILEXFER_ATTR_SIZE = 0x00000001
|
||||
ssh_FILEXFER_ATTR_UIDGID = 0x00000002
|
||||
ssh_FILEXFER_ATTR_PERMISSIONS = 0x00000004
|
||||
ssh_FILEXFER_ATTR_ACMODTIME = 0x00000008
|
||||
ssh_FILEXFER_ATTR_EXTENDED = 0x80000000
|
||||
)
|
||||
|
||||
// fileInfo is an artificial type designed to satisfy os.FileInfo.
|
||||
type fileInfo struct {
|
||||
name string
|
||||
size int64
|
||||
mode os.FileMode
|
||||
mtime time.Time
|
||||
sys interface{}
|
||||
}
|
||||
|
||||
// Name returns the base name of the file.
|
||||
func (fi *fileInfo) Name() string { return fi.name }
|
||||
|
||||
// Size returns the length in bytes for regular files; system-dependent for others.
|
||||
func (fi *fileInfo) Size() int64 { return fi.size }
|
||||
|
||||
// Mode returns file mode bits.
|
||||
func (fi *fileInfo) Mode() os.FileMode { return fi.mode }
|
||||
|
||||
// ModTime returns the last modification time of the file.
|
||||
func (fi *fileInfo) ModTime() time.Time { return fi.mtime }
|
||||
|
||||
// IsDir returns true if the file is a directory.
|
||||
func (fi *fileInfo) IsDir() bool { return fi.Mode().IsDir() }
|
||||
|
||||
func (fi *fileInfo) Sys() interface{} { return fi.sys }
|
||||
|
||||
// FileStat holds the original unmarshalled values from a call to READDIR or *STAT.
|
||||
// It is exported for the purposes of accessing the raw values via os.FileInfo.Sys()
|
||||
type FileStat struct {
|
||||
Size uint64
|
||||
Mode uint32
|
||||
Mtime uint32
|
||||
Atime uint32
|
||||
UID uint32
|
||||
GID uint32
|
||||
Extended []StatExtended
|
||||
}
|
||||
|
||||
// StatExtended contains additional, extended information for a FileStat.
|
||||
type StatExtended struct {
|
||||
ExtType string
|
||||
ExtData string
|
||||
}
|
||||
|
||||
func fileInfoFromStat(st *FileStat, name string) os.FileInfo {
|
||||
fs := &fileInfo{
|
||||
name: name,
|
||||
size: int64(st.Size),
|
||||
mode: toFileMode(st.Mode),
|
||||
mtime: time.Unix(int64(st.Mtime), 0),
|
||||
sys: st,
|
||||
}
|
||||
return fs
|
||||
}
|
||||
|
||||
func fileStatFromInfo(fi os.FileInfo) (uint32, FileStat) {
|
||||
mtime := fi.ModTime().Unix()
|
||||
atime := mtime
|
||||
var flags uint32 = ssh_FILEXFER_ATTR_SIZE |
|
||||
ssh_FILEXFER_ATTR_PERMISSIONS |
|
||||
ssh_FILEXFER_ATTR_ACMODTIME
|
||||
|
||||
fileStat := FileStat{
|
||||
Size: uint64(fi.Size()),
|
||||
Mode: fromFileMode(fi.Mode()),
|
||||
Mtime: uint32(mtime),
|
||||
Atime: uint32(atime),
|
||||
}
|
||||
|
||||
// os specific file stat decoding
|
||||
fileStatFromInfoOs(fi, &flags, &fileStat)
|
||||
|
||||
return flags, fileStat
|
||||
}
|
||||
|
||||
func unmarshalAttrs(b []byte) (*FileStat, []byte) {
|
||||
flags, b := unmarshalUint32(b)
|
||||
var fs FileStat
|
||||
if flags&ssh_FILEXFER_ATTR_SIZE == ssh_FILEXFER_ATTR_SIZE {
|
||||
fs.Size, b = unmarshalUint64(b)
|
||||
}
|
||||
if flags&ssh_FILEXFER_ATTR_UIDGID == ssh_FILEXFER_ATTR_UIDGID {
|
||||
fs.UID, b = unmarshalUint32(b)
|
||||
}
|
||||
if flags&ssh_FILEXFER_ATTR_UIDGID == ssh_FILEXFER_ATTR_UIDGID {
|
||||
fs.GID, b = unmarshalUint32(b)
|
||||
}
|
||||
if flags&ssh_FILEXFER_ATTR_PERMISSIONS == ssh_FILEXFER_ATTR_PERMISSIONS {
|
||||
fs.Mode, b = unmarshalUint32(b)
|
||||
}
|
||||
if flags&ssh_FILEXFER_ATTR_ACMODTIME == ssh_FILEXFER_ATTR_ACMODTIME {
|
||||
fs.Atime, b = unmarshalUint32(b)
|
||||
fs.Mtime, b = unmarshalUint32(b)
|
||||
}
|
||||
if flags&ssh_FILEXFER_ATTR_EXTENDED == ssh_FILEXFER_ATTR_EXTENDED {
|
||||
var count uint32
|
||||
count, b = unmarshalUint32(b)
|
||||
ext := make([]StatExtended, count, count)
|
||||
for i := uint32(0); i < count; i++ {
|
||||
var typ string
|
||||
var data string
|
||||
typ, b = unmarshalString(b)
|
||||
data, b = unmarshalString(b)
|
||||
ext[i] = StatExtended{typ, data}
|
||||
}
|
||||
fs.Extended = ext
|
||||
}
|
||||
return &fs, b
|
||||
}
|
||||
|
||||
func marshalFileInfo(b []byte, fi os.FileInfo) []byte {
|
||||
// attributes variable struct, and also variable per protocol version
|
||||
// spec version 3 attributes:
|
||||
// uint32 flags
|
||||
// uint64 size present only if flag SSH_FILEXFER_ATTR_SIZE
|
||||
// uint32 uid present only if flag SSH_FILEXFER_ATTR_UIDGID
|
||||
// uint32 gid present only if flag SSH_FILEXFER_ATTR_UIDGID
|
||||
// uint32 permissions present only if flag SSH_FILEXFER_ATTR_PERMISSIONS
|
||||
// uint32 atime present only if flag SSH_FILEXFER_ACMODTIME
|
||||
// uint32 mtime present only if flag SSH_FILEXFER_ACMODTIME
|
||||
// uint32 extended_count present only if flag SSH_FILEXFER_ATTR_EXTENDED
|
||||
// string extended_type
|
||||
// string extended_data
|
||||
// ... more extended data (extended_type - extended_data pairs),
|
||||
// so that number of pairs equals extended_count
|
||||
|
||||
flags, fileStat := fileStatFromInfo(fi)
|
||||
|
||||
b = marshalUint32(b, flags)
|
||||
if flags&ssh_FILEXFER_ATTR_SIZE != 0 {
|
||||
b = marshalUint64(b, fileStat.Size)
|
||||
}
|
||||
if flags&ssh_FILEXFER_ATTR_UIDGID != 0 {
|
||||
b = marshalUint32(b, fileStat.UID)
|
||||
b = marshalUint32(b, fileStat.GID)
|
||||
}
|
||||
if flags&ssh_FILEXFER_ATTR_PERMISSIONS != 0 {
|
||||
b = marshalUint32(b, fileStat.Mode)
|
||||
}
|
||||
if flags&ssh_FILEXFER_ATTR_ACMODTIME != 0 {
|
||||
b = marshalUint32(b, fileStat.Atime)
|
||||
b = marshalUint32(b, fileStat.Mtime)
|
||||
}
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
// toFileMode converts sftp filemode bits to the os.FileMode specification
|
||||
func toFileMode(mode uint32) os.FileMode {
|
||||
var fm = os.FileMode(mode & 0777)
|
||||
switch mode & syscall.S_IFMT {
|
||||
case syscall.S_IFBLK:
|
||||
fm |= os.ModeDevice
|
||||
case syscall.S_IFCHR:
|
||||
fm |= os.ModeDevice | os.ModeCharDevice
|
||||
case syscall.S_IFDIR:
|
||||
fm |= os.ModeDir
|
||||
case syscall.S_IFIFO:
|
||||
fm |= os.ModeNamedPipe
|
||||
case syscall.S_IFLNK:
|
||||
fm |= os.ModeSymlink
|
||||
case syscall.S_IFREG:
|
||||
// nothing to do
|
||||
case syscall.S_IFSOCK:
|
||||
fm |= os.ModeSocket
|
||||
}
|
||||
if mode&syscall.S_ISGID != 0 {
|
||||
fm |= os.ModeSetgid
|
||||
}
|
||||
if mode&syscall.S_ISUID != 0 {
|
||||
fm |= os.ModeSetuid
|
||||
}
|
||||
if mode&syscall.S_ISVTX != 0 {
|
||||
fm |= os.ModeSticky
|
||||
}
|
||||
return fm
|
||||
}
|
||||
|
||||
// fromFileMode converts from the os.FileMode specification to sftp filemode bits
|
||||
func fromFileMode(mode os.FileMode) uint32 {
|
||||
ret := uint32(0)
|
||||
|
||||
if mode&os.ModeDevice != 0 {
|
||||
if mode&os.ModeCharDevice != 0 {
|
||||
ret |= syscall.S_IFCHR
|
||||
} else {
|
||||
ret |= syscall.S_IFBLK
|
||||
}
|
||||
}
|
||||
if mode&os.ModeDir != 0 {
|
||||
ret |= syscall.S_IFDIR
|
||||
}
|
||||
if mode&os.ModeSymlink != 0 {
|
||||
ret |= syscall.S_IFLNK
|
||||
}
|
||||
if mode&os.ModeNamedPipe != 0 {
|
||||
ret |= syscall.S_IFIFO
|
||||
}
|
||||
if mode&os.ModeSetgid != 0 {
|
||||
ret |= syscall.S_ISGID
|
||||
}
|
||||
if mode&os.ModeSetuid != 0 {
|
||||
ret |= syscall.S_ISUID
|
||||
}
|
||||
if mode&os.ModeSticky != 0 {
|
||||
ret |= syscall.S_ISVTX
|
||||
}
|
||||
if mode&os.ModeSocket != 0 {
|
||||
ret |= syscall.S_IFSOCK
|
||||
}
|
||||
|
||||
if mode&os.ModeType == 0 {
|
||||
ret |= syscall.S_IFREG
|
||||
}
|
||||
ret |= uint32(mode & os.ModePerm)
|
||||
|
||||
return ret
|
||||
}
|
11
vendor/github.com/pkg/sftp/attrs_stubs.go
generated
vendored
Normal file
11
vendor/github.com/pkg/sftp/attrs_stubs.go
generated
vendored
Normal file
|
@ -0,0 +1,11 @@
|
|||
// +build !cgo,!plan9 windows android
|
||||
|
||||
package sftp
|
||||
|
||||
import (
|
||||
"os"
|
||||
)
|
||||
|
||||
func fileStatFromInfoOs(fi os.FileInfo, flags *uint32, fileStat *FileStat) {
|
||||
// todo
|
||||
}
|
17
vendor/github.com/pkg/sftp/attrs_unix.go
generated
vendored
Normal file
17
vendor/github.com/pkg/sftp/attrs_unix.go
generated
vendored
Normal file
|
@ -0,0 +1,17 @@
|
|||
// +build darwin dragonfly freebsd !android,linux netbsd openbsd solaris
|
||||
// +build cgo
|
||||
|
||||
package sftp
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func fileStatFromInfoOs(fi os.FileInfo, flags *uint32, fileStat *FileStat) {
|
||||
if statt, ok := fi.Sys().(*syscall.Stat_t); ok {
|
||||
*flags |= ssh_FILEXFER_ATTR_UIDGID
|
||||
fileStat.UID = statt.Uid
|
||||
fileStat.GID = statt.Gid
|
||||
}
|
||||
}
|
1129
vendor/github.com/pkg/sftp/client.go
generated
vendored
Normal file
1129
vendor/github.com/pkg/sftp/client.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
126
vendor/github.com/pkg/sftp/conn.go
generated
vendored
Normal file
126
vendor/github.com/pkg/sftp/conn.go
generated
vendored
Normal file
|
@ -0,0 +1,126 @@
|
|||
package sftp
|
||||
|
||||
import (
|
||||
"encoding"
|
||||
"io"
|
||||
"sync"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// conn implements a bidirectional channel on which client and server
|
||||
// connections are multiplexed.
|
||||
type conn struct {
|
||||
io.Reader
|
||||
io.WriteCloser
|
||||
sync.Mutex // used to serialise writes to sendPacket
|
||||
}
|
||||
|
||||
func (c *conn) recvPacket() (uint8, []byte, error) {
|
||||
return recvPacket(c)
|
||||
}
|
||||
|
||||
func (c *conn) sendPacket(m encoding.BinaryMarshaler) error {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
return sendPacket(c, m)
|
||||
}
|
||||
|
||||
type clientConn struct {
|
||||
conn
|
||||
wg sync.WaitGroup
|
||||
sync.Mutex // protects inflight
|
||||
inflight map[uint32]chan<- result // outstanding requests
|
||||
}
|
||||
|
||||
// Close closes the SFTP session.
|
||||
func (c *clientConn) Close() error {
|
||||
defer c.wg.Wait()
|
||||
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 {
|
||||
defer func() {
|
||||
c.Lock()
|
||||
c.conn.Close()
|
||||
c.Unlock()
|
||||
}()
|
||||
for {
|
||||
typ, data, err := c.recvPacket()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sid, _ := unmarshalUint32(data)
|
||||
c.Lock()
|
||||
ch, ok := c.inflight[sid]
|
||||
delete(c.inflight, sid)
|
||||
c.Unlock()
|
||||
if !ok {
|
||||
// This is an unexpected occurrence. Send the error
|
||||
// back to all listeners so that they terminate
|
||||
// gracefully.
|
||||
return errors.Errorf("sid: %v not fond", sid)
|
||||
}
|
||||
ch <- result{typ: typ, data: data}
|
||||
}
|
||||
}
|
||||
|
||||
// result captures the result of receiving the a packet from the server
|
||||
type result struct {
|
||||
typ byte
|
||||
data []byte
|
||||
err error
|
||||
}
|
||||
|
||||
type idmarshaler interface {
|
||||
id() uint32
|
||||
encoding.BinaryMarshaler
|
||||
}
|
||||
|
||||
func (c *clientConn) sendPacket(p idmarshaler) (byte, []byte, error) {
|
||||
ch := make(chan result, 1)
|
||||
c.dispatchRequest(ch, p)
|
||||
s := <-ch
|
||||
return s.typ, s.data, s.err
|
||||
}
|
||||
|
||||
func (c *clientConn) dispatchRequest(ch chan<- result, p idmarshaler) {
|
||||
c.Lock()
|
||||
c.inflight[p.id()] = ch
|
||||
if err := c.conn.sendPacket(p); err != nil {
|
||||
delete(c.inflight, p.id())
|
||||
ch <- result{err: err}
|
||||
}
|
||||
c.Unlock()
|
||||
}
|
||||
|
||||
// broadcastErr sends an error to all goroutines waiting for a response.
|
||||
func (c *clientConn) broadcastErr(err error) {
|
||||
c.Lock()
|
||||
listeners := make([]chan<- result, 0, len(c.inflight))
|
||||
for _, ch := range c.inflight {
|
||||
listeners = append(listeners, ch)
|
||||
}
|
||||
c.Unlock()
|
||||
for _, ch := range listeners {
|
||||
ch <- result{err: err}
|
||||
}
|
||||
}
|
||||
|
||||
type serverConn struct {
|
||||
conn
|
||||
}
|
||||
|
||||
func (s *serverConn) sendError(p id, err error) error {
|
||||
return s.sendPacket(statusFromError(p, err))
|
||||
}
|
9
vendor/github.com/pkg/sftp/debug.go
generated
vendored
Normal file
9
vendor/github.com/pkg/sftp/debug.go
generated
vendored
Normal file
|
@ -0,0 +1,9 @@
|
|||
// +build debug
|
||||
|
||||
package sftp
|
||||
|
||||
import "log"
|
||||
|
||||
func debug(fmt string, args ...interface{}) {
|
||||
log.Printf(fmt, args...)
|
||||
}
|
901
vendor/github.com/pkg/sftp/packet.go
generated
vendored
Normal file
901
vendor/github.com/pkg/sftp/packet.go
generated
vendored
Normal file
|
@ -0,0 +1,901 @@
|
|||
package sftp
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"reflect"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
var (
|
||||
errShortPacket = errors.New("packet too short")
|
||||
errUnknownExtendedPacket = errors.New("unknown extended packet")
|
||||
)
|
||||
|
||||
const (
|
||||
debugDumpTxPacket = false
|
||||
debugDumpRxPacket = false
|
||||
debugDumpTxPacketBytes = false
|
||||
debugDumpRxPacketBytes = false
|
||||
)
|
||||
|
||||
func marshalUint32(b []byte, v uint32) []byte {
|
||||
return append(b, byte(v>>24), byte(v>>16), byte(v>>8), byte(v))
|
||||
}
|
||||
|
||||
func marshalUint64(b []byte, v uint64) []byte {
|
||||
return marshalUint32(marshalUint32(b, uint32(v>>32)), uint32(v))
|
||||
}
|
||||
|
||||
func marshalString(b []byte, v string) []byte {
|
||||
return append(marshalUint32(b, uint32(len(v))), v...)
|
||||
}
|
||||
|
||||
func marshal(b []byte, v interface{}) []byte {
|
||||
if v == nil {
|
||||
return b
|
||||
}
|
||||
switch v := v.(type) {
|
||||
case uint8:
|
||||
return append(b, v)
|
||||
case uint32:
|
||||
return marshalUint32(b, v)
|
||||
case uint64:
|
||||
return marshalUint64(b, v)
|
||||
case string:
|
||||
return marshalString(b, v)
|
||||
case os.FileInfo:
|
||||
return marshalFileInfo(b, v)
|
||||
default:
|
||||
switch d := reflect.ValueOf(v); d.Kind() {
|
||||
case reflect.Struct:
|
||||
for i, n := 0, d.NumField(); i < n; i++ {
|
||||
b = append(marshal(b, d.Field(i).Interface()))
|
||||
}
|
||||
return b
|
||||
case reflect.Slice:
|
||||
for i, n := 0, d.Len(); i < n; i++ {
|
||||
b = append(marshal(b, d.Index(i).Interface()))
|
||||
}
|
||||
return b
|
||||
default:
|
||||
panic(fmt.Sprintf("marshal(%#v): cannot handle type %T", v, v))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func unmarshalUint32(b []byte) (uint32, []byte) {
|
||||
v := uint32(b[3]) | uint32(b[2])<<8 | uint32(b[1])<<16 | uint32(b[0])<<24
|
||||
return v, b[4:]
|
||||
}
|
||||
|
||||
func unmarshalUint32Safe(b []byte) (uint32, []byte, error) {
|
||||
var v uint32
|
||||
if len(b) < 4 {
|
||||
return 0, nil, errShortPacket
|
||||
}
|
||||
v, b = unmarshalUint32(b)
|
||||
return v, b, nil
|
||||
}
|
||||
|
||||
func unmarshalUint64(b []byte) (uint64, []byte) {
|
||||
h, b := unmarshalUint32(b)
|
||||
l, b := unmarshalUint32(b)
|
||||
return uint64(h)<<32 | uint64(l), b
|
||||
}
|
||||
|
||||
func unmarshalUint64Safe(b []byte) (uint64, []byte, error) {
|
||||
var v uint64
|
||||
if len(b) < 8 {
|
||||
return 0, nil, errShortPacket
|
||||
}
|
||||
v, b = unmarshalUint64(b)
|
||||
return v, b, nil
|
||||
}
|
||||
|
||||
func unmarshalString(b []byte) (string, []byte) {
|
||||
n, b := unmarshalUint32(b)
|
||||
return string(b[:n]), b[n:]
|
||||
}
|
||||
|
||||
func unmarshalStringSafe(b []byte) (string, []byte, error) {
|
||||
n, b, err := unmarshalUint32Safe(b)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
if int64(n) > int64(len(b)) {
|
||||
return "", nil, errShortPacket
|
||||
}
|
||||
return string(b[:n]), b[n:], nil
|
||||
}
|
||||
|
||||
// sendPacket marshals p according to RFC 4234.
|
||||
func sendPacket(w io.Writer, m encoding.BinaryMarshaler) error {
|
||||
bb, err := m.MarshalBinary()
|
||||
if err != nil {
|
||||
return errors.Errorf("binary marshaller failed: %v", err)
|
||||
}
|
||||
if debugDumpTxPacketBytes {
|
||||
debug("send packet: %s %d bytes %x", fxp(bb[0]), len(bb), bb[1:])
|
||||
} else if debugDumpTxPacket {
|
||||
debug("send packet: %s %d bytes", fxp(bb[0]), len(bb))
|
||||
}
|
||||
l := uint32(len(bb))
|
||||
hdr := []byte{byte(l >> 24), byte(l >> 16), byte(l >> 8), byte(l)}
|
||||
_, err = w.Write(hdr)
|
||||
if err != nil {
|
||||
return errors.Errorf("failed to send packet header: %v", err)
|
||||
}
|
||||
_, err = w.Write(bb)
|
||||
if err != nil {
|
||||
return errors.Errorf("failed to send packet body: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func recvPacket(r io.Reader) (uint8, []byte, error) {
|
||||
var b = []byte{0, 0, 0, 0}
|
||||
if _, err := io.ReadFull(r, b); err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
l, _ := unmarshalUint32(b)
|
||||
b = make([]byte, l)
|
||||
if _, err := io.ReadFull(r, b); err != nil {
|
||||
debug("recv packet %d bytes: err %v", l, err)
|
||||
return 0, nil, err
|
||||
}
|
||||
if debugDumpRxPacketBytes {
|
||||
debug("recv packet: %s %d bytes %x", fxp(b[0]), l, b[1:])
|
||||
} else if debugDumpRxPacket {
|
||||
debug("recv packet: %s %d bytes", fxp(b[0]), l)
|
||||
}
|
||||
return b[0], b[1:], nil
|
||||
}
|
||||
|
||||
type extensionPair struct {
|
||||
Name string
|
||||
Data string
|
||||
}
|
||||
|
||||
func unmarshalExtensionPair(b []byte) (extensionPair, []byte, error) {
|
||||
var ep extensionPair
|
||||
var err error
|
||||
ep.Name, b, err = unmarshalStringSafe(b)
|
||||
if err != nil {
|
||||
return ep, b, err
|
||||
}
|
||||
ep.Data, b, err = unmarshalStringSafe(b)
|
||||
if err != nil {
|
||||
return ep, b, err
|
||||
}
|
||||
return ep, b, err
|
||||
}
|
||||
|
||||
// Here starts the definition of packets along with their MarshalBinary
|
||||
// implementations.
|
||||
// Manually writing the marshalling logic wins us a lot of time and
|
||||
// allocation.
|
||||
|
||||
type sshFxInitPacket struct {
|
||||
Version uint32
|
||||
Extensions []extensionPair
|
||||
}
|
||||
|
||||
func (p sshFxInitPacket) MarshalBinary() ([]byte, error) {
|
||||
l := 1 + 4 // byte + uint32
|
||||
for _, e := range p.Extensions {
|
||||
l += 4 + len(e.Name) + 4 + len(e.Data)
|
||||
}
|
||||
|
||||
b := make([]byte, 0, l)
|
||||
b = append(b, ssh_FXP_INIT)
|
||||
b = marshalUint32(b, p.Version)
|
||||
for _, e := range p.Extensions {
|
||||
b = marshalString(b, e.Name)
|
||||
b = marshalString(b, e.Data)
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func (p *sshFxInitPacket) UnmarshalBinary(b []byte) error {
|
||||
var err error
|
||||
if p.Version, b, err = unmarshalUint32Safe(b); err != nil {
|
||||
return err
|
||||
}
|
||||
for len(b) > 0 {
|
||||
var ep extensionPair
|
||||
ep, b, err = unmarshalExtensionPair(b)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
p.Extensions = append(p.Extensions, ep)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type sshFxVersionPacket struct {
|
||||
Version uint32
|
||||
Extensions []struct {
|
||||
Name, Data string
|
||||
}
|
||||
}
|
||||
|
||||
func (p sshFxVersionPacket) MarshalBinary() ([]byte, error) {
|
||||
l := 1 + 4 // byte + uint32
|
||||
for _, e := range p.Extensions {
|
||||
l += 4 + len(e.Name) + 4 + len(e.Data)
|
||||
}
|
||||
|
||||
b := make([]byte, 0, l)
|
||||
b = append(b, ssh_FXP_VERSION)
|
||||
b = marshalUint32(b, p.Version)
|
||||
for _, e := range p.Extensions {
|
||||
b = marshalString(b, e.Name)
|
||||
b = marshalString(b, e.Data)
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func marshalIDString(packetType byte, id uint32, str string) ([]byte, error) {
|
||||
l := 1 + 4 + // type(byte) + uint32
|
||||
4 + len(str)
|
||||
|
||||
b := make([]byte, 0, l)
|
||||
b = append(b, packetType)
|
||||
b = marshalUint32(b, id)
|
||||
b = marshalString(b, str)
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func unmarshalIDString(b []byte, id *uint32, str *string) error {
|
||||
var err error
|
||||
*id, b, err = unmarshalUint32Safe(b)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*str, b, err = unmarshalStringSafe(b)
|
||||
return err
|
||||
}
|
||||
|
||||
type sshFxpReaddirPacket struct {
|
||||
ID uint32
|
||||
Handle string
|
||||
}
|
||||
|
||||
func (p sshFxpReaddirPacket) id() uint32 { return p.ID }
|
||||
|
||||
func (p sshFxpReaddirPacket) MarshalBinary() ([]byte, error) {
|
||||
return marshalIDString(ssh_FXP_READDIR, p.ID, p.Handle)
|
||||
}
|
||||
|
||||
func (p *sshFxpReaddirPacket) UnmarshalBinary(b []byte) error {
|
||||
return unmarshalIDString(b, &p.ID, &p.Handle)
|
||||
}
|
||||
|
||||
type sshFxpOpendirPacket struct {
|
||||
ID uint32
|
||||
Path string
|
||||
}
|
||||
|
||||
func (p sshFxpOpendirPacket) id() uint32 { return p.ID }
|
||||
|
||||
func (p sshFxpOpendirPacket) MarshalBinary() ([]byte, error) {
|
||||
return marshalIDString(ssh_FXP_OPENDIR, p.ID, p.Path)
|
||||
}
|
||||
|
||||
func (p *sshFxpOpendirPacket) UnmarshalBinary(b []byte) error {
|
||||
return unmarshalIDString(b, &p.ID, &p.Path)
|
||||
}
|
||||
|
||||
type sshFxpLstatPacket struct {
|
||||
ID uint32
|
||||
Path string
|
||||
}
|
||||
|
||||
func (p sshFxpLstatPacket) id() uint32 { return p.ID }
|
||||
|
||||
func (p sshFxpLstatPacket) MarshalBinary() ([]byte, error) {
|
||||
return marshalIDString(ssh_FXP_LSTAT, p.ID, p.Path)
|
||||
}
|
||||
|
||||
func (p *sshFxpLstatPacket) UnmarshalBinary(b []byte) error {
|
||||
return unmarshalIDString(b, &p.ID, &p.Path)
|
||||
}
|
||||
|
||||
type sshFxpStatPacket struct {
|
||||
ID uint32
|
||||
Path string
|
||||
}
|
||||
|
||||
func (p sshFxpStatPacket) id() uint32 { return p.ID }
|
||||
|
||||
func (p sshFxpStatPacket) MarshalBinary() ([]byte, error) {
|
||||
return marshalIDString(ssh_FXP_STAT, p.ID, p.Path)
|
||||
}
|
||||
|
||||
func (p *sshFxpStatPacket) UnmarshalBinary(b []byte) error {
|
||||
return unmarshalIDString(b, &p.ID, &p.Path)
|
||||
}
|
||||
|
||||
type sshFxpFstatPacket struct {
|
||||
ID uint32
|
||||
Handle string
|
||||
}
|
||||
|
||||
func (p sshFxpFstatPacket) id() uint32 { return p.ID }
|
||||
|
||||
func (p sshFxpFstatPacket) MarshalBinary() ([]byte, error) {
|
||||
return marshalIDString(ssh_FXP_FSTAT, p.ID, p.Handle)
|
||||
}
|
||||
|
||||
func (p *sshFxpFstatPacket) UnmarshalBinary(b []byte) error {
|
||||
return unmarshalIDString(b, &p.ID, &p.Handle)
|
||||
}
|
||||
|
||||
type sshFxpClosePacket struct {
|
||||
ID uint32
|
||||
Handle string
|
||||
}
|
||||
|
||||
func (p sshFxpClosePacket) id() uint32 { return p.ID }
|
||||
|
||||
func (p sshFxpClosePacket) MarshalBinary() ([]byte, error) {
|
||||
return marshalIDString(ssh_FXP_CLOSE, p.ID, p.Handle)
|
||||
}
|
||||
|
||||
func (p *sshFxpClosePacket) UnmarshalBinary(b []byte) error {
|
||||
return unmarshalIDString(b, &p.ID, &p.Handle)
|
||||
}
|
||||
|
||||
type sshFxpRemovePacket struct {
|
||||
ID uint32
|
||||
Filename string
|
||||
}
|
||||
|
||||
func (p sshFxpRemovePacket) id() uint32 { return p.ID }
|
||||
|
||||
func (p sshFxpRemovePacket) MarshalBinary() ([]byte, error) {
|
||||
return marshalIDString(ssh_FXP_REMOVE, p.ID, p.Filename)
|
||||
}
|
||||
|
||||
func (p *sshFxpRemovePacket) UnmarshalBinary(b []byte) error {
|
||||
return unmarshalIDString(b, &p.ID, &p.Filename)
|
||||
}
|
||||
|
||||
type sshFxpRmdirPacket struct {
|
||||
ID uint32
|
||||
Path string
|
||||
}
|
||||
|
||||
func (p sshFxpRmdirPacket) id() uint32 { return p.ID }
|
||||
|
||||
func (p sshFxpRmdirPacket) MarshalBinary() ([]byte, error) {
|
||||
return marshalIDString(ssh_FXP_RMDIR, p.ID, p.Path)
|
||||
}
|
||||
|
||||
func (p *sshFxpRmdirPacket) UnmarshalBinary(b []byte) error {
|
||||
return unmarshalIDString(b, &p.ID, &p.Path)
|
||||
}
|
||||
|
||||
type sshFxpSymlinkPacket struct {
|
||||
ID uint32
|
||||
Targetpath string
|
||||
Linkpath string
|
||||
}
|
||||
|
||||
func (p sshFxpSymlinkPacket) id() uint32 { return p.ID }
|
||||
|
||||
func (p sshFxpSymlinkPacket) MarshalBinary() ([]byte, error) {
|
||||
l := 1 + 4 + // type(byte) + uint32
|
||||
4 + len(p.Targetpath) +
|
||||
4 + len(p.Linkpath)
|
||||
|
||||
b := make([]byte, 0, l)
|
||||
b = append(b, ssh_FXP_SYMLINK)
|
||||
b = marshalUint32(b, p.ID)
|
||||
b = marshalString(b, p.Targetpath)
|
||||
b = marshalString(b, p.Linkpath)
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func (p *sshFxpSymlinkPacket) UnmarshalBinary(b []byte) error {
|
||||
var err error
|
||||
if p.ID, b, err = unmarshalUint32Safe(b); err != nil {
|
||||
return err
|
||||
} else if p.Targetpath, b, err = unmarshalStringSafe(b); err != nil {
|
||||
return err
|
||||
} else if p.Linkpath, b, err = unmarshalStringSafe(b); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type sshFxpReadlinkPacket struct {
|
||||
ID uint32
|
||||
Path string
|
||||
}
|
||||
|
||||
func (p sshFxpReadlinkPacket) id() uint32 { return p.ID }
|
||||
|
||||
func (p sshFxpReadlinkPacket) MarshalBinary() ([]byte, error) {
|
||||
return marshalIDString(ssh_FXP_READLINK, p.ID, p.Path)
|
||||
}
|
||||
|
||||
func (p *sshFxpReadlinkPacket) UnmarshalBinary(b []byte) error {
|
||||
return unmarshalIDString(b, &p.ID, &p.Path)
|
||||
}
|
||||
|
||||
type sshFxpRealpathPacket struct {
|
||||
ID uint32
|
||||
Path string
|
||||
}
|
||||
|
||||
func (p sshFxpRealpathPacket) id() uint32 { return p.ID }
|
||||
|
||||
func (p sshFxpRealpathPacket) MarshalBinary() ([]byte, error) {
|
||||
return marshalIDString(ssh_FXP_REALPATH, p.ID, p.Path)
|
||||
}
|
||||
|
||||
func (p *sshFxpRealpathPacket) UnmarshalBinary(b []byte) error {
|
||||
return unmarshalIDString(b, &p.ID, &p.Path)
|
||||
}
|
||||
|
||||
type sshFxpNameAttr struct {
|
||||
Name string
|
||||
LongName string
|
||||
Attrs []interface{}
|
||||
}
|
||||
|
||||
func (p sshFxpNameAttr) MarshalBinary() ([]byte, error) {
|
||||
b := []byte{}
|
||||
b = marshalString(b, p.Name)
|
||||
b = marshalString(b, p.LongName)
|
||||
for _, attr := range p.Attrs {
|
||||
b = marshal(b, attr)
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
type sshFxpNamePacket struct {
|
||||
ID uint32
|
||||
NameAttrs []sshFxpNameAttr
|
||||
}
|
||||
|
||||
func (p sshFxpNamePacket) MarshalBinary() ([]byte, error) {
|
||||
b := []byte{}
|
||||
b = append(b, ssh_FXP_NAME)
|
||||
b = marshalUint32(b, p.ID)
|
||||
b = marshalUint32(b, uint32(len(p.NameAttrs)))
|
||||
for _, na := range p.NameAttrs {
|
||||
ab, err := na.MarshalBinary()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
b = append(b, ab...)
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
type sshFxpOpenPacket struct {
|
||||
ID uint32
|
||||
Path string
|
||||
Pflags uint32
|
||||
Flags uint32 // ignored
|
||||
}
|
||||
|
||||
func (p sshFxpOpenPacket) id() uint32 { return p.ID }
|
||||
|
||||
func (p sshFxpOpenPacket) MarshalBinary() ([]byte, error) {
|
||||
l := 1 + 4 +
|
||||
4 + len(p.Path) +
|
||||
4 + 4
|
||||
|
||||
b := make([]byte, 0, l)
|
||||
b = append(b, ssh_FXP_OPEN)
|
||||
b = marshalUint32(b, p.ID)
|
||||
b = marshalString(b, p.Path)
|
||||
b = marshalUint32(b, p.Pflags)
|
||||
b = marshalUint32(b, p.Flags)
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func (p *sshFxpOpenPacket) UnmarshalBinary(b []byte) error {
|
||||
var err error
|
||||
if p.ID, b, err = unmarshalUint32Safe(b); err != nil {
|
||||
return err
|
||||
} else if p.Path, b, err = unmarshalStringSafe(b); err != nil {
|
||||
return err
|
||||
} else if p.Pflags, b, err = unmarshalUint32Safe(b); err != nil {
|
||||
return err
|
||||
} else if p.Flags, b, err = unmarshalUint32Safe(b); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type sshFxpReadPacket struct {
|
||||
ID uint32
|
||||
Handle string
|
||||
Offset uint64
|
||||
Len uint32
|
||||
}
|
||||
|
||||
func (p sshFxpReadPacket) id() uint32 { return p.ID }
|
||||
|
||||
func (p sshFxpReadPacket) MarshalBinary() ([]byte, error) {
|
||||
l := 1 + 4 + // type(byte) + uint32
|
||||
4 + len(p.Handle) +
|
||||
8 + 4 // uint64 + uint32
|
||||
|
||||
b := make([]byte, 0, l)
|
||||
b = append(b, ssh_FXP_READ)
|
||||
b = marshalUint32(b, p.ID)
|
||||
b = marshalString(b, p.Handle)
|
||||
b = marshalUint64(b, p.Offset)
|
||||
b = marshalUint32(b, p.Len)
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func (p *sshFxpReadPacket) UnmarshalBinary(b []byte) error {
|
||||
var err error
|
||||
if p.ID, b, err = unmarshalUint32Safe(b); err != nil {
|
||||
return err
|
||||
} else if p.Handle, b, err = unmarshalStringSafe(b); err != nil {
|
||||
return err
|
||||
} else if p.Offset, b, err = unmarshalUint64Safe(b); err != nil {
|
||||
return err
|
||||
} else if p.Len, b, err = unmarshalUint32Safe(b); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type sshFxpRenamePacket struct {
|
||||
ID uint32
|
||||
Oldpath string
|
||||
Newpath string
|
||||
}
|
||||
|
||||
func (p sshFxpRenamePacket) id() uint32 { return p.ID }
|
||||
|
||||
func (p sshFxpRenamePacket) MarshalBinary() ([]byte, error) {
|
||||
l := 1 + 4 + // type(byte) + uint32
|
||||
4 + len(p.Oldpath) +
|
||||
4 + len(p.Newpath)
|
||||
|
||||
b := make([]byte, 0, l)
|
||||
b = append(b, ssh_FXP_RENAME)
|
||||
b = marshalUint32(b, p.ID)
|
||||
b = marshalString(b, p.Oldpath)
|
||||
b = marshalString(b, p.Newpath)
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func (p *sshFxpRenamePacket) UnmarshalBinary(b []byte) error {
|
||||
var err error
|
||||
if p.ID, b, err = unmarshalUint32Safe(b); err != nil {
|
||||
return err
|
||||
} else if p.Oldpath, b, err = unmarshalStringSafe(b); err != nil {
|
||||
return err
|
||||
} else if p.Newpath, b, err = unmarshalStringSafe(b); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type sshFxpWritePacket struct {
|
||||
ID uint32
|
||||
Handle string
|
||||
Offset uint64
|
||||
Length uint32
|
||||
Data []byte
|
||||
}
|
||||
|
||||
func (p sshFxpWritePacket) id() uint32 { return p.ID }
|
||||
|
||||
func (p sshFxpWritePacket) MarshalBinary() ([]byte, error) {
|
||||
l := 1 + 4 + // type(byte) + uint32
|
||||
4 + len(p.Handle) +
|
||||
8 + 4 + // uint64 + uint32
|
||||
len(p.Data)
|
||||
|
||||
b := make([]byte, 0, l)
|
||||
b = append(b, ssh_FXP_WRITE)
|
||||
b = marshalUint32(b, p.ID)
|
||||
b = marshalString(b, p.Handle)
|
||||
b = marshalUint64(b, p.Offset)
|
||||
b = marshalUint32(b, p.Length)
|
||||
b = append(b, p.Data...)
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func (p *sshFxpWritePacket) UnmarshalBinary(b []byte) error {
|
||||
var err error
|
||||
if p.ID, b, err = unmarshalUint32Safe(b); err != nil {
|
||||
return err
|
||||
} else if p.Handle, b, err = unmarshalStringSafe(b); err != nil {
|
||||
return err
|
||||
} else if p.Offset, b, err = unmarshalUint64Safe(b); err != nil {
|
||||
return err
|
||||
} else if p.Length, b, err = unmarshalUint32Safe(b); err != nil {
|
||||
return err
|
||||
} else if uint32(len(b)) < p.Length {
|
||||
return errShortPacket
|
||||
}
|
||||
|
||||
p.Data = append([]byte{}, b[:p.Length]...)
|
||||
return nil
|
||||
}
|
||||
|
||||
type sshFxpMkdirPacket struct {
|
||||
ID uint32
|
||||
Path string
|
||||
Flags uint32 // ignored
|
||||
}
|
||||
|
||||
func (p sshFxpMkdirPacket) id() uint32 { return p.ID }
|
||||
|
||||
func (p sshFxpMkdirPacket) MarshalBinary() ([]byte, error) {
|
||||
l := 1 + 4 + // type(byte) + uint32
|
||||
4 + len(p.Path) +
|
||||
4 // uint32
|
||||
|
||||
b := make([]byte, 0, l)
|
||||
b = append(b, ssh_FXP_MKDIR)
|
||||
b = marshalUint32(b, p.ID)
|
||||
b = marshalString(b, p.Path)
|
||||
b = marshalUint32(b, p.Flags)
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func (p *sshFxpMkdirPacket) UnmarshalBinary(b []byte) error {
|
||||
var err error
|
||||
if p.ID, b, err = unmarshalUint32Safe(b); err != nil {
|
||||
return err
|
||||
} else if p.Path, b, err = unmarshalStringSafe(b); err != nil {
|
||||
return err
|
||||
} else if p.Flags, b, err = unmarshalUint32Safe(b); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type sshFxpSetstatPacket struct {
|
||||
ID uint32
|
||||
Path string
|
||||
Flags uint32
|
||||
Attrs interface{}
|
||||
}
|
||||
|
||||
type sshFxpFsetstatPacket struct {
|
||||
ID uint32
|
||||
Handle string
|
||||
Flags uint32
|
||||
Attrs interface{}
|
||||
}
|
||||
|
||||
func (p sshFxpSetstatPacket) id() uint32 { return p.ID }
|
||||
func (p sshFxpFsetstatPacket) id() uint32 { return p.ID }
|
||||
|
||||
func (p sshFxpSetstatPacket) MarshalBinary() ([]byte, error) {
|
||||
l := 1 + 4 + // type(byte) + uint32
|
||||
4 + len(p.Path) +
|
||||
4 // uint32 + uint64
|
||||
|
||||
b := make([]byte, 0, l)
|
||||
b = append(b, ssh_FXP_SETSTAT)
|
||||
b = marshalUint32(b, p.ID)
|
||||
b = marshalString(b, p.Path)
|
||||
b = marshalUint32(b, p.Flags)
|
||||
b = marshal(b, p.Attrs)
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func (p sshFxpFsetstatPacket) MarshalBinary() ([]byte, error) {
|
||||
l := 1 + 4 + // type(byte) + uint32
|
||||
4 + len(p.Handle) +
|
||||
4 // uint32 + uint64
|
||||
|
||||
b := make([]byte, 0, l)
|
||||
b = append(b, ssh_FXP_FSETSTAT)
|
||||
b = marshalUint32(b, p.ID)
|
||||
b = marshalString(b, p.Handle)
|
||||
b = marshalUint32(b, p.Flags)
|
||||
b = marshal(b, p.Attrs)
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func (p *sshFxpSetstatPacket) UnmarshalBinary(b []byte) error {
|
||||
var err error
|
||||
if p.ID, b, err = unmarshalUint32Safe(b); err != nil {
|
||||
return err
|
||||
} else if p.Path, b, err = unmarshalStringSafe(b); err != nil {
|
||||
return err
|
||||
} else if p.Flags, b, err = unmarshalUint32Safe(b); err != nil {
|
||||
return err
|
||||
}
|
||||
p.Attrs = b
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *sshFxpFsetstatPacket) UnmarshalBinary(b []byte) error {
|
||||
var err error
|
||||
if p.ID, b, err = unmarshalUint32Safe(b); err != nil {
|
||||
return err
|
||||
} else if p.Handle, b, err = unmarshalStringSafe(b); err != nil {
|
||||
return err
|
||||
} else if p.Flags, b, err = unmarshalUint32Safe(b); err != nil {
|
||||
return err
|
||||
}
|
||||
p.Attrs = b
|
||||
return nil
|
||||
}
|
||||
|
||||
type sshFxpHandlePacket struct {
|
||||
ID uint32
|
||||
Handle string
|
||||
}
|
||||
|
||||
func (p sshFxpHandlePacket) MarshalBinary() ([]byte, error) {
|
||||
b := []byte{ssh_FXP_HANDLE}
|
||||
b = marshalUint32(b, p.ID)
|
||||
b = marshalString(b, p.Handle)
|
||||
return b, nil
|
||||
}
|
||||
|
||||
type sshFxpStatusPacket struct {
|
||||
ID uint32
|
||||
StatusError
|
||||
}
|
||||
|
||||
func (p sshFxpStatusPacket) MarshalBinary() ([]byte, error) {
|
||||
b := []byte{ssh_FXP_STATUS}
|
||||
b = marshalUint32(b, p.ID)
|
||||
b = marshalStatus(b, p.StatusError)
|
||||
return b, nil
|
||||
}
|
||||
|
||||
type sshFxpDataPacket struct {
|
||||
ID uint32
|
||||
Length uint32
|
||||
Data []byte
|
||||
}
|
||||
|
||||
func (p sshFxpDataPacket) MarshalBinary() ([]byte, error) {
|
||||
b := []byte{ssh_FXP_DATA}
|
||||
b = marshalUint32(b, p.ID)
|
||||
b = marshalUint32(b, p.Length)
|
||||
b = append(b, p.Data[:p.Length]...)
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func (p *sshFxpDataPacket) UnmarshalBinary(b []byte) error {
|
||||
var err error
|
||||
if p.ID, b, err = unmarshalUint32Safe(b); err != nil {
|
||||
return err
|
||||
} else if p.Length, b, err = unmarshalUint32Safe(b); err != nil {
|
||||
return err
|
||||
} else if uint32(len(b)) < p.Length {
|
||||
return errors.New("truncated packet")
|
||||
}
|
||||
|
||||
p.Data = make([]byte, p.Length)
|
||||
copy(p.Data, b)
|
||||
return nil
|
||||
}
|
||||
|
||||
type sshFxpStatvfsPacket struct {
|
||||
ID uint32
|
||||
Path string
|
||||
}
|
||||
|
||||
func (p sshFxpStatvfsPacket) id() uint32 { return p.ID }
|
||||
|
||||
func (p sshFxpStatvfsPacket) MarshalBinary() ([]byte, error) {
|
||||
l := 1 + 4 + // type(byte) + uint32
|
||||
len(p.Path) +
|
||||
len("statvfs@openssh.com")
|
||||
|
||||
b := make([]byte, 0, l)
|
||||
b = append(b, ssh_FXP_EXTENDED)
|
||||
b = marshalUint32(b, p.ID)
|
||||
b = marshalString(b, "statvfs@openssh.com")
|
||||
b = marshalString(b, p.Path)
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// A StatVFS contains statistics about a filesystem.
|
||||
type StatVFS struct {
|
||||
ID uint32
|
||||
Bsize uint64 /* file system block size */
|
||||
Frsize uint64 /* fundamental fs block size */
|
||||
Blocks uint64 /* number of blocks (unit f_frsize) */
|
||||
Bfree uint64 /* free blocks in file system */
|
||||
Bavail uint64 /* free blocks for non-root */
|
||||
Files uint64 /* total file inodes */
|
||||
Ffree uint64 /* free file inodes */
|
||||
Favail uint64 /* free file inodes for to non-root */
|
||||
Fsid uint64 /* file system id */
|
||||
Flag uint64 /* bit mask of f_flag values */
|
||||
Namemax uint64 /* maximum filename length */
|
||||
}
|
||||
|
||||
// TotalSpace calculates the amount of total space in a filesystem.
|
||||
func (p *StatVFS) TotalSpace() uint64 {
|
||||
return p.Frsize * p.Blocks
|
||||
}
|
||||
|
||||
// FreeSpace calculates the amount of free space in a filesystem.
|
||||
func (p *StatVFS) FreeSpace() uint64 {
|
||||
return p.Frsize * p.Bfree
|
||||
}
|
||||
|
||||
// Convert to ssh_FXP_EXTENDED_REPLY packet binary format
|
||||
func (p *StatVFS) MarshalBinary() ([]byte, error) {
|
||||
var buf bytes.Buffer
|
||||
buf.Write([]byte{ssh_FXP_EXTENDED_REPLY})
|
||||
err := binary.Write(&buf, binary.BigEndian, p)
|
||||
return buf.Bytes(), err
|
||||
}
|
||||
|
||||
type sshFxpExtendedPacket struct {
|
||||
ID uint32
|
||||
ExtendedRequest string
|
||||
SpecificPacket interface {
|
||||
serverRespondablePacket
|
||||
readonly() bool
|
||||
}
|
||||
}
|
||||
|
||||
func (p sshFxpExtendedPacket) id() uint32 { return p.ID }
|
||||
func (p sshFxpExtendedPacket) readonly() bool { return p.SpecificPacket.readonly() }
|
||||
|
||||
func (p sshFxpExtendedPacket) respond(svr *Server) error {
|
||||
return p.SpecificPacket.respond(svr)
|
||||
}
|
||||
|
||||
func (p *sshFxpExtendedPacket) UnmarshalBinary(b []byte) error {
|
||||
var err error
|
||||
bOrig := b
|
||||
if p.ID, b, err = unmarshalUint32Safe(b); err != nil {
|
||||
return err
|
||||
} else if p.ExtendedRequest, b, err = unmarshalStringSafe(b); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// specific unmarshalling
|
||||
switch p.ExtendedRequest {
|
||||
case "statvfs@openssh.com":
|
||||
p.SpecificPacket = &sshFxpExtendedPacketStatVFS{}
|
||||
default:
|
||||
return errUnknownExtendedPacket
|
||||
}
|
||||
|
||||
return p.SpecificPacket.UnmarshalBinary(bOrig)
|
||||
}
|
||||
|
||||
type sshFxpExtendedPacketStatVFS struct {
|
||||
ID uint32
|
||||
ExtendedRequest string
|
||||
Path string
|
||||
}
|
||||
|
||||
func (p sshFxpExtendedPacketStatVFS) id() uint32 { return p.ID }
|
||||
func (p sshFxpExtendedPacketStatVFS) readonly() bool { return true }
|
||||
func (p *sshFxpExtendedPacketStatVFS) UnmarshalBinary(b []byte) error {
|
||||
var err error
|
||||
if p.ID, b, err = unmarshalUint32Safe(b); err != nil {
|
||||
return err
|
||||
} else if p.ExtendedRequest, b, err = unmarshalStringSafe(b); err != nil {
|
||||
return err
|
||||
} else if p.Path, b, err = unmarshalStringSafe(b); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
5
vendor/github.com/pkg/sftp/release.go
generated
vendored
Normal file
5
vendor/github.com/pkg/sftp/release.go
generated
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
// +build !debug
|
||||
|
||||
package sftp
|
||||
|
||||
func debug(fmt string, args ...interface{}) {}
|
607
vendor/github.com/pkg/sftp/server.go
generated
vendored
Normal file
607
vendor/github.com/pkg/sftp/server.go
generated
vendored
Normal file
|
@ -0,0 +1,607 @@
|
|||
package sftp
|
||||
|
||||
// sftp server counterpart
|
||||
|
||||
import (
|
||||
"encoding"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
sftpServerWorkerCount = 8
|
||||
)
|
||||
|
||||
// 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
|
||||
type Server struct {
|
||||
serverConn
|
||||
debugStream io.Writer
|
||||
readOnly bool
|
||||
pktChan chan rxPacket
|
||||
openFiles map[string]*os.File
|
||||
openFilesLock sync.RWMutex
|
||||
handleCount int
|
||||
maxTxPacket uint32
|
||||
}
|
||||
|
||||
func (svr *Server) nextHandle(f *os.File) string {
|
||||
svr.openFilesLock.Lock()
|
||||
defer svr.openFilesLock.Unlock()
|
||||
svr.handleCount++
|
||||
handle := strconv.Itoa(svr.handleCount)
|
||||
svr.openFiles[handle] = f
|
||||
return handle
|
||||
}
|
||||
|
||||
func (svr *Server) closeHandle(handle string) error {
|
||||
svr.openFilesLock.Lock()
|
||||
defer svr.openFilesLock.Unlock()
|
||||
if f, ok := svr.openFiles[handle]; ok {
|
||||
delete(svr.openFiles, handle)
|
||||
return f.Close()
|
||||
}
|
||||
|
||||
return syscall.EBADF
|
||||
}
|
||||
|
||||
func (svr *Server) getHandle(handle string) (*os.File, bool) {
|
||||
svr.openFilesLock.RLock()
|
||||
defer svr.openFilesLock.RUnlock()
|
||||
f, ok := svr.openFiles[handle]
|
||||
return f, ok
|
||||
}
|
||||
|
||||
type serverRespondablePacket interface {
|
||||
encoding.BinaryUnmarshaler
|
||||
id() uint32
|
||||
respond(svr *Server) error
|
||||
}
|
||||
|
||||
// NewServer creates a new Server instance around the provided streams, serving
|
||||
// content from the root of the filesystem. Optionally, ServerOption
|
||||
// functions may be specified to further configure the Server.
|
||||
//
|
||||
// A subsequent call to Serve() is required to begin serving files over SFTP.
|
||||
func NewServer(rwc io.ReadWriteCloser, options ...ServerOption) (*Server, error) {
|
||||
s := &Server{
|
||||
serverConn: serverConn{
|
||||
conn: conn{
|
||||
Reader: rwc,
|
||||
WriteCloser: rwc,
|
||||
},
|
||||
},
|
||||
debugStream: ioutil.Discard,
|
||||
pktChan: make(chan rxPacket, sftpServerWorkerCount),
|
||||
openFiles: make(map[string]*os.File),
|
||||
maxTxPacket: 1 << 15,
|
||||
}
|
||||
|
||||
for _, o := range options {
|
||||
if err := o(s); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// A ServerOption is a function which applies configuration to a Server.
|
||||
type ServerOption func(*Server) error
|
||||
|
||||
// WithDebug enables Server debugging output to the supplied io.Writer.
|
||||
func WithDebug(w io.Writer) ServerOption {
|
||||
return func(s *Server) error {
|
||||
s.debugStream = w
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// ReadOnly configures a Server to serve files in read-only mode.
|
||||
func ReadOnly() ServerOption {
|
||||
return func(s *Server) error {
|
||||
s.readOnly = true
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
type rxPacket struct {
|
||||
pktType fxp
|
||||
pktBytes []byte
|
||||
}
|
||||
|
||||
// Up to N parallel servers
|
||||
func (svr *Server) sftpServerWorker() error {
|
||||
for p := range svr.pktChan {
|
||||
var pkt interface {
|
||||
encoding.BinaryUnmarshaler
|
||||
id() uint32
|
||||
}
|
||||
var readonly = true
|
||||
switch p.pktType {
|
||||
case ssh_FXP_INIT:
|
||||
pkt = &sshFxInitPacket{}
|
||||
case ssh_FXP_LSTAT:
|
||||
pkt = &sshFxpLstatPacket{}
|
||||
case ssh_FXP_OPEN:
|
||||
pkt = &sshFxpOpenPacket{}
|
||||
// readonly handled specially below
|
||||
case ssh_FXP_CLOSE:
|
||||
pkt = &sshFxpClosePacket{}
|
||||
case ssh_FXP_READ:
|
||||
pkt = &sshFxpReadPacket{}
|
||||
case ssh_FXP_WRITE:
|
||||
pkt = &sshFxpWritePacket{}
|
||||
readonly = false
|
||||
case ssh_FXP_FSTAT:
|
||||
pkt = &sshFxpFstatPacket{}
|
||||
case ssh_FXP_SETSTAT:
|
||||
pkt = &sshFxpSetstatPacket{}
|
||||
readonly = false
|
||||
case ssh_FXP_FSETSTAT:
|
||||
pkt = &sshFxpFsetstatPacket{}
|
||||
readonly = false
|
||||
case ssh_FXP_OPENDIR:
|
||||
pkt = &sshFxpOpendirPacket{}
|
||||
case ssh_FXP_READDIR:
|
||||
pkt = &sshFxpReaddirPacket{}
|
||||
case ssh_FXP_REMOVE:
|
||||
pkt = &sshFxpRemovePacket{}
|
||||
readonly = false
|
||||
case ssh_FXP_MKDIR:
|
||||
pkt = &sshFxpMkdirPacket{}
|
||||
readonly = false
|
||||
case ssh_FXP_RMDIR:
|
||||
pkt = &sshFxpRmdirPacket{}
|
||||
readonly = false
|
||||
case ssh_FXP_REALPATH:
|
||||
pkt = &sshFxpRealpathPacket{}
|
||||
case ssh_FXP_STAT:
|
||||
pkt = &sshFxpStatPacket{}
|
||||
case ssh_FXP_RENAME:
|
||||
pkt = &sshFxpRenamePacket{}
|
||||
readonly = false
|
||||
case ssh_FXP_READLINK:
|
||||
pkt = &sshFxpReadlinkPacket{}
|
||||
case ssh_FXP_SYMLINK:
|
||||
pkt = &sshFxpSymlinkPacket{}
|
||||
readonly = false
|
||||
case ssh_FXP_EXTENDED:
|
||||
pkt = &sshFxpExtendedPacket{}
|
||||
default:
|
||||
return errors.Errorf("unhandled packet type: %s", p.pktType)
|
||||
}
|
||||
if err := pkt.UnmarshalBinary(p.pktBytes); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// handle FXP_OPENDIR specially
|
||||
switch pkt := pkt.(type) {
|
||||
case *sshFxpOpenPacket:
|
||||
readonly = pkt.readonly()
|
||||
case *sshFxpExtendedPacket:
|
||||
readonly = pkt.SpecificPacket.readonly()
|
||||
}
|
||||
|
||||
// If server is operating read-only and a write operation is requested,
|
||||
// return permission denied
|
||||
if !readonly && svr.readOnly {
|
||||
if err := svr.sendError(pkt, syscall.EPERM); err != nil {
|
||||
return errors.Wrap(err, "failed to send read only packet response")
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if err := handlePacket(svr, pkt); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func handlePacket(s *Server, p interface{}) error {
|
||||
switch p := p.(type) {
|
||||
case *sshFxInitPacket:
|
||||
return s.sendPacket(sshFxVersionPacket{sftpProtocolVersion, nil})
|
||||
case *sshFxpStatPacket:
|
||||
// stat the requested file
|
||||
info, err := os.Stat(p.Path)
|
||||
if err != nil {
|
||||
return s.sendError(p, err)
|
||||
}
|
||||
return s.sendPacket(sshFxpStatResponse{
|
||||
ID: p.ID,
|
||||
info: info,
|
||||
})
|
||||
case *sshFxpLstatPacket:
|
||||
// stat the requested file
|
||||
info, err := os.Lstat(p.Path)
|
||||
if err != nil {
|
||||
return s.sendError(p, err)
|
||||
}
|
||||
return s.sendPacket(sshFxpStatResponse{
|
||||
ID: p.ID,
|
||||
info: info,
|
||||
})
|
||||
case *sshFxpFstatPacket:
|
||||
f, ok := s.getHandle(p.Handle)
|
||||
if !ok {
|
||||
return s.sendError(p, syscall.EBADF)
|
||||
}
|
||||
|
||||
info, err := f.Stat()
|
||||
if err != nil {
|
||||
return s.sendError(p, err)
|
||||
}
|
||||
|
||||
return s.sendPacket(sshFxpStatResponse{
|
||||
ID: p.ID,
|
||||
info: info,
|
||||
})
|
||||
case *sshFxpMkdirPacket:
|
||||
// TODO FIXME: ignore flags field
|
||||
err := os.Mkdir(p.Path, 0755)
|
||||
return s.sendError(p, err)
|
||||
case *sshFxpRmdirPacket:
|
||||
err := os.Remove(p.Path)
|
||||
return s.sendError(p, err)
|
||||
case *sshFxpRemovePacket:
|
||||
err := os.Remove(p.Filename)
|
||||
return s.sendError(p, err)
|
||||
case *sshFxpRenamePacket:
|
||||
err := os.Rename(p.Oldpath, p.Newpath)
|
||||
return s.sendError(p, err)
|
||||
case *sshFxpSymlinkPacket:
|
||||
err := os.Symlink(p.Targetpath, p.Linkpath)
|
||||
return s.sendError(p, err)
|
||||
case *sshFxpClosePacket:
|
||||
return s.sendError(p, s.closeHandle(p.Handle))
|
||||
case *sshFxpReadlinkPacket:
|
||||
f, err := os.Readlink(p.Path)
|
||||
if err != nil {
|
||||
return s.sendError(p, err)
|
||||
}
|
||||
|
||||
return s.sendPacket(sshFxpNamePacket{
|
||||
ID: p.ID,
|
||||
NameAttrs: []sshFxpNameAttr{{
|
||||
Name: f,
|
||||
LongName: f,
|
||||
Attrs: emptyFileStat,
|
||||
}},
|
||||
})
|
||||
|
||||
case *sshFxpRealpathPacket:
|
||||
f, err := filepath.Abs(p.Path)
|
||||
if err != nil {
|
||||
return s.sendError(p, err)
|
||||
}
|
||||
f = filepath.Clean(f)
|
||||
return s.sendPacket(sshFxpNamePacket{
|
||||
ID: p.ID,
|
||||
NameAttrs: []sshFxpNameAttr{{
|
||||
Name: f,
|
||||
LongName: f,
|
||||
Attrs: emptyFileStat,
|
||||
}},
|
||||
})
|
||||
case *sshFxpOpendirPacket:
|
||||
return sshFxpOpenPacket{
|
||||
ID: p.ID,
|
||||
Path: p.Path,
|
||||
Pflags: ssh_FXF_READ,
|
||||
}.respond(s)
|
||||
case *sshFxpReadPacket:
|
||||
f, ok := s.getHandle(p.Handle)
|
||||
if !ok {
|
||||
return s.sendError(p, syscall.EBADF)
|
||||
}
|
||||
|
||||
data := make([]byte, clamp(p.Len, s.maxTxPacket))
|
||||
n, err := f.ReadAt(data, int64(p.Offset))
|
||||
if err != nil && (err != io.EOF || n == 0) {
|
||||
return s.sendError(p, err)
|
||||
}
|
||||
return s.sendPacket(sshFxpDataPacket{
|
||||
ID: p.ID,
|
||||
Length: uint32(n),
|
||||
Data: data[:n],
|
||||
})
|
||||
case *sshFxpWritePacket:
|
||||
f, ok := s.getHandle(p.Handle)
|
||||
if !ok {
|
||||
return s.sendError(p, syscall.EBADF)
|
||||
}
|
||||
|
||||
_, err := f.WriteAt(p.Data, int64(p.Offset))
|
||||
return s.sendError(p, err)
|
||||
case serverRespondablePacket:
|
||||
err := p.respond(s)
|
||||
return errors.Wrap(err, "pkt.respond failed")
|
||||
default:
|
||||
return errors.Errorf("unexpected packet type %T", p)
|
||||
}
|
||||
}
|
||||
|
||||
// Serve serves SFTP connections until the streams stop or the SFTP subsystem
|
||||
// is stopped.
|
||||
func (svr *Server) Serve() error {
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(sftpServerWorkerCount)
|
||||
for i := 0; i < sftpServerWorkerCount; i++ {
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
if err := svr.sftpServerWorker(); err != nil {
|
||||
svr.conn.Close() // shuts down recvPacket
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
var err error
|
||||
var pktType uint8
|
||||
var pktBytes []byte
|
||||
for {
|
||||
pktType, pktBytes, err = svr.recvPacket()
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
svr.pktChan <- rxPacket{fxp(pktType), pktBytes}
|
||||
}
|
||||
|
||||
close(svr.pktChan) // shuts down sftpServerWorkers
|
||||
wg.Wait() // wait for all workers to exit
|
||||
|
||||
// close any still-open files
|
||||
for handle, file := range svr.openFiles {
|
||||
fmt.Fprintf(svr.debugStream, "sftp server file with handle %q left open: %v\n", handle, file.Name())
|
||||
file.Close()
|
||||
}
|
||||
return err // error from recvPacket
|
||||
}
|
||||
|
||||
type id interface {
|
||||
id() uint32
|
||||
}
|
||||
|
||||
// The init packet has no ID, so we just return a zero-value ID
|
||||
func (p sshFxInitPacket) id() uint32 { return 0 }
|
||||
|
||||
type sshFxpStatResponse struct {
|
||||
ID uint32
|
||||
info os.FileInfo
|
||||
}
|
||||
|
||||
func (p sshFxpStatResponse) MarshalBinary() ([]byte, error) {
|
||||
b := []byte{ssh_FXP_ATTRS}
|
||||
b = marshalUint32(b, p.ID)
|
||||
b = marshalFileInfo(b, p.info)
|
||||
return b, nil
|
||||
}
|
||||
|
||||
var emptyFileStat = []interface{}{uint32(0)}
|
||||
|
||||
func (p sshFxpOpenPacket) readonly() bool {
|
||||
return !p.hasPflags(ssh_FXF_WRITE)
|
||||
}
|
||||
|
||||
func (p sshFxpOpenPacket) hasPflags(flags ...uint32) bool {
|
||||
for _, f := range flags {
|
||||
if p.Pflags&f == 0 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (p sshFxpOpenPacket) respond(svr *Server) error {
|
||||
var osFlags int
|
||||
if p.hasPflags(ssh_FXF_READ, ssh_FXF_WRITE) {
|
||||
osFlags |= os.O_RDWR
|
||||
} else if p.hasPflags(ssh_FXF_WRITE) {
|
||||
osFlags |= os.O_WRONLY
|
||||
} else if p.hasPflags(ssh_FXF_READ) {
|
||||
osFlags |= os.O_RDONLY
|
||||
} else {
|
||||
// how are they opening?
|
||||
return svr.sendError(p, syscall.EINVAL)
|
||||
}
|
||||
|
||||
if p.hasPflags(ssh_FXF_APPEND) {
|
||||
osFlags |= os.O_APPEND
|
||||
}
|
||||
if p.hasPflags(ssh_FXF_CREAT) {
|
||||
osFlags |= os.O_CREATE
|
||||
}
|
||||
if p.hasPflags(ssh_FXF_TRUNC) {
|
||||
osFlags |= os.O_TRUNC
|
||||
}
|
||||
if p.hasPflags(ssh_FXF_EXCL) {
|
||||
osFlags |= os.O_EXCL
|
||||
}
|
||||
|
||||
f, err := os.OpenFile(p.Path, osFlags, 0644)
|
||||
if err != nil {
|
||||
return svr.sendError(p, err)
|
||||
}
|
||||
|
||||
handle := svr.nextHandle(f)
|
||||
return svr.sendPacket(sshFxpHandlePacket{p.ID, handle})
|
||||
}
|
||||
|
||||
func (p sshFxpReaddirPacket) respond(svr *Server) error {
|
||||
f, ok := svr.getHandle(p.Handle)
|
||||
if !ok {
|
||||
return svr.sendError(p, syscall.EBADF)
|
||||
}
|
||||
|
||||
dirname := f.Name()
|
||||
dirents, err := f.Readdir(128)
|
||||
if err != nil {
|
||||
return svr.sendError(p, err)
|
||||
}
|
||||
|
||||
ret := sshFxpNamePacket{ID: p.ID}
|
||||
for _, dirent := range dirents {
|
||||
ret.NameAttrs = append(ret.NameAttrs, sshFxpNameAttr{
|
||||
Name: dirent.Name(),
|
||||
LongName: runLs(dirname, dirent),
|
||||
Attrs: []interface{}{dirent},
|
||||
})
|
||||
}
|
||||
return svr.sendPacket(ret)
|
||||
}
|
||||
|
||||
func (p sshFxpSetstatPacket) respond(svr *Server) error {
|
||||
// additional unmarshalling is required for each possibility here
|
||||
b := p.Attrs.([]byte)
|
||||
var err error
|
||||
|
||||
debug("setstat name \"%s\"", p.Path)
|
||||
if (p.Flags & ssh_FILEXFER_ATTR_SIZE) != 0 {
|
||||
var size uint64
|
||||
if size, b, err = unmarshalUint64Safe(b); err == nil {
|
||||
err = os.Truncate(p.Path, int64(size))
|
||||
}
|
||||
}
|
||||
if (p.Flags & ssh_FILEXFER_ATTR_PERMISSIONS) != 0 {
|
||||
var mode uint32
|
||||
if mode, b, err = unmarshalUint32Safe(b); err == nil {
|
||||
err = os.Chmod(p.Path, os.FileMode(mode))
|
||||
}
|
||||
}
|
||||
if (p.Flags & ssh_FILEXFER_ATTR_ACMODTIME) != 0 {
|
||||
var atime uint32
|
||||
var mtime uint32
|
||||
if atime, b, err = unmarshalUint32Safe(b); err != nil {
|
||||
} else if mtime, b, err = unmarshalUint32Safe(b); err != nil {
|
||||
} else {
|
||||
atimeT := time.Unix(int64(atime), 0)
|
||||
mtimeT := time.Unix(int64(mtime), 0)
|
||||
err = os.Chtimes(p.Path, atimeT, mtimeT)
|
||||
}
|
||||
}
|
||||
if (p.Flags & ssh_FILEXFER_ATTR_UIDGID) != 0 {
|
||||
var uid uint32
|
||||
var gid uint32
|
||||
if uid, b, err = unmarshalUint32Safe(b); err != nil {
|
||||
} else if gid, b, err = unmarshalUint32Safe(b); err != nil {
|
||||
} else {
|
||||
err = os.Chown(p.Path, int(uid), int(gid))
|
||||
}
|
||||
}
|
||||
|
||||
return svr.sendError(p, err)
|
||||
}
|
||||
|
||||
func (p sshFxpFsetstatPacket) respond(svr *Server) error {
|
||||
f, ok := svr.getHandle(p.Handle)
|
||||
if !ok {
|
||||
return svr.sendError(p, syscall.EBADF)
|
||||
}
|
||||
|
||||
// additional unmarshalling is required for each possibility here
|
||||
b := p.Attrs.([]byte)
|
||||
var err error
|
||||
|
||||
debug("fsetstat name \"%s\"", f.Name())
|
||||
if (p.Flags & ssh_FILEXFER_ATTR_SIZE) != 0 {
|
||||
var size uint64
|
||||
if size, b, err = unmarshalUint64Safe(b); err == nil {
|
||||
err = f.Truncate(int64(size))
|
||||
}
|
||||
}
|
||||
if (p.Flags & ssh_FILEXFER_ATTR_PERMISSIONS) != 0 {
|
||||
var mode uint32
|
||||
if mode, b, err = unmarshalUint32Safe(b); err == nil {
|
||||
err = f.Chmod(os.FileMode(mode))
|
||||
}
|
||||
}
|
||||
if (p.Flags & ssh_FILEXFER_ATTR_ACMODTIME) != 0 {
|
||||
var atime uint32
|
||||
var mtime uint32
|
||||
if atime, b, err = unmarshalUint32Safe(b); err != nil {
|
||||
} else if mtime, b, err = unmarshalUint32Safe(b); err != nil {
|
||||
} else {
|
||||
atimeT := time.Unix(int64(atime), 0)
|
||||
mtimeT := time.Unix(int64(mtime), 0)
|
||||
err = os.Chtimes(f.Name(), atimeT, mtimeT)
|
||||
}
|
||||
}
|
||||
if (p.Flags & ssh_FILEXFER_ATTR_UIDGID) != 0 {
|
||||
var uid uint32
|
||||
var gid uint32
|
||||
if uid, b, err = unmarshalUint32Safe(b); err != nil {
|
||||
} else if gid, b, err = unmarshalUint32Safe(b); err != nil {
|
||||
} else {
|
||||
err = f.Chown(int(uid), int(gid))
|
||||
}
|
||||
}
|
||||
|
||||
return svr.sendError(p, err)
|
||||
}
|
||||
|
||||
// translateErrno translates a syscall error number to a SFTP error code.
|
||||
func translateErrno(errno syscall.Errno) uint32 {
|
||||
switch errno {
|
||||
case 0:
|
||||
return ssh_FX_OK
|
||||
case syscall.ENOENT:
|
||||
return ssh_FX_NO_SUCH_FILE
|
||||
case syscall.EPERM:
|
||||
return ssh_FX_PERMISSION_DENIED
|
||||
}
|
||||
|
||||
return ssh_FX_FAILURE
|
||||
}
|
||||
|
||||
func statusFromError(p id, err error) sshFxpStatusPacket {
|
||||
ret := sshFxpStatusPacket{
|
||||
ID: p.id(),
|
||||
StatusError: StatusError{
|
||||
// ssh_FX_OK = 0
|
||||
// ssh_FX_EOF = 1
|
||||
// ssh_FX_NO_SUCH_FILE = 2 ENOENT
|
||||
// ssh_FX_PERMISSION_DENIED = 3
|
||||
// ssh_FX_FAILURE = 4
|
||||
// ssh_FX_BAD_MESSAGE = 5
|
||||
// ssh_FX_NO_CONNECTION = 6
|
||||
// ssh_FX_CONNECTION_LOST = 7
|
||||
// ssh_FX_OP_UNSUPPORTED = 8
|
||||
Code: ssh_FX_OK,
|
||||
},
|
||||
}
|
||||
if err != nil {
|
||||
debug("statusFromError: error is %T %#v", err, err)
|
||||
ret.StatusError.Code = ssh_FX_FAILURE
|
||||
ret.StatusError.msg = err.Error()
|
||||
if err == io.EOF {
|
||||
ret.StatusError.Code = ssh_FX_EOF
|
||||
} else if errno, ok := err.(syscall.Errno); ok {
|
||||
ret.StatusError.Code = translateErrno(errno)
|
||||
} else if pathError, ok := err.(*os.PathError); ok {
|
||||
debug("statusFromError: error is %T %#v", pathError.Err, pathError.Err)
|
||||
if errno, ok := pathError.Err.(syscall.Errno); ok {
|
||||
ret.StatusError.Code = translateErrno(errno)
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func clamp(v, max uint32) uint32 {
|
||||
if v > max {
|
||||
return max
|
||||
}
|
||||
return v
|
||||
}
|
21
vendor/github.com/pkg/sftp/server_statvfs_darwin.go
generated
vendored
Normal file
21
vendor/github.com/pkg/sftp/server_statvfs_darwin.go
generated
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
package sftp
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func statvfsFromStatfst(stat *syscall.Statfs_t) (*StatVFS, error) {
|
||||
return &StatVFS{
|
||||
Bsize: uint64(stat.Bsize),
|
||||
Frsize: uint64(stat.Bsize), // fragment size is a linux thing; use block size here
|
||||
Blocks: stat.Blocks,
|
||||
Bfree: stat.Bfree,
|
||||
Bavail: stat.Bavail,
|
||||
Files: stat.Files,
|
||||
Ffree: stat.Ffree,
|
||||
Favail: stat.Ffree, // not sure how to calculate Favail
|
||||
Fsid: uint64(uint64(stat.Fsid.Val[1])<<32 | uint64(stat.Fsid.Val[0])), // endianness?
|
||||
Flag: uint64(stat.Flags), // assuming POSIX?
|
||||
Namemax: 1024, // man 2 statfs shows: #define MAXPATHLEN 1024
|
||||
}, nil
|
||||
}
|
25
vendor/github.com/pkg/sftp/server_statvfs_impl.go
generated
vendored
Normal file
25
vendor/github.com/pkg/sftp/server_statvfs_impl.go
generated
vendored
Normal file
|
@ -0,0 +1,25 @@
|
|||
// +build darwin linux,!gccgo
|
||||
|
||||
// fill in statvfs structure with OS specific values
|
||||
// Statfs_t is different per-kernel, and only exists on some unixes (not Solaris for instance)
|
||||
|
||||
package sftp
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func (p sshFxpExtendedPacketStatVFS) respond(svr *Server) error {
|
||||
stat := &syscall.Statfs_t{}
|
||||
if err := syscall.Statfs(p.Path, stat); err != nil {
|
||||
return svr.sendPacket(statusFromError(p, err))
|
||||
}
|
||||
|
||||
retPkt, err := statvfsFromStatfst(stat)
|
||||
if err != nil {
|
||||
return svr.sendPacket(statusFromError(p, err))
|
||||
}
|
||||
retPkt.ID = p.ID
|
||||
|
||||
return svr.sendPacket(retPkt)
|
||||
}
|
22
vendor/github.com/pkg/sftp/server_statvfs_linux.go
generated
vendored
Normal file
22
vendor/github.com/pkg/sftp/server_statvfs_linux.go
generated
vendored
Normal file
|
@ -0,0 +1,22 @@
|
|||
// +build !gccgo,linux
|
||||
|
||||
package sftp
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func statvfsFromStatfst(stat *syscall.Statfs_t) (*StatVFS, error) {
|
||||
return &StatVFS{
|
||||
Bsize: uint64(stat.Bsize),
|
||||
Frsize: uint64(stat.Frsize),
|
||||
Blocks: stat.Blocks,
|
||||
Bfree: stat.Bfree,
|
||||
Bavail: stat.Bavail,
|
||||
Files: stat.Files,
|
||||
Ffree: stat.Ffree,
|
||||
Favail: stat.Ffree, // not sure how to calculate Favail
|
||||
Flag: uint64(stat.Flags), // assuming POSIX?
|
||||
Namemax: uint64(stat.Namelen),
|
||||
}, nil
|
||||
}
|
11
vendor/github.com/pkg/sftp/server_statvfs_stubs.go
generated
vendored
Normal file
11
vendor/github.com/pkg/sftp/server_statvfs_stubs.go
generated
vendored
Normal file
|
@ -0,0 +1,11 @@
|
|||
// +build !darwin,!linux gccgo
|
||||
|
||||
package sftp
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func (p sshFxpExtendedPacketStatVFS) respond(svr *Server) error {
|
||||
return syscall.ENOTSUP
|
||||
}
|
12
vendor/github.com/pkg/sftp/server_stubs.go
generated
vendored
Normal file
12
vendor/github.com/pkg/sftp/server_stubs.go
generated
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
// +build !cgo,!plan9 windows android
|
||||
|
||||
package sftp
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path"
|
||||
)
|
||||
|
||||
func runLs(dirname string, dirent os.FileInfo) string {
|
||||
return path.Join(dirname, dirent.Name())
|
||||
}
|
143
vendor/github.com/pkg/sftp/server_unix.go
generated
vendored
Normal file
143
vendor/github.com/pkg/sftp/server_unix.go
generated
vendored
Normal file
|
@ -0,0 +1,143 @@
|
|||
// +build darwin dragonfly freebsd !android,linux netbsd openbsd solaris
|
||||
// +build cgo
|
||||
|
||||
package sftp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
func runLsTypeWord(dirent os.FileInfo) string {
|
||||
// find first character, the type char
|
||||
// b Block special file.
|
||||
// c Character special file.
|
||||
// d Directory.
|
||||
// l Symbolic link.
|
||||
// s Socket link.
|
||||
// p FIFO.
|
||||
// - Regular file.
|
||||
tc := '-'
|
||||
mode := dirent.Mode()
|
||||
if (mode & os.ModeDir) != 0 {
|
||||
tc = 'd'
|
||||
} else if (mode & os.ModeDevice) != 0 {
|
||||
tc = 'b'
|
||||
if (mode & os.ModeCharDevice) != 0 {
|
||||
tc = 'c'
|
||||
}
|
||||
} else if (mode & os.ModeSymlink) != 0 {
|
||||
tc = 'l'
|
||||
} else if (mode & os.ModeSocket) != 0 {
|
||||
tc = 's'
|
||||
} else if (mode & os.ModeNamedPipe) != 0 {
|
||||
tc = 'p'
|
||||
}
|
||||
|
||||
// owner
|
||||
orc := '-'
|
||||
if (mode & 0400) != 0 {
|
||||
orc = 'r'
|
||||
}
|
||||
owc := '-'
|
||||
if (mode & 0200) != 0 {
|
||||
owc = 'w'
|
||||
}
|
||||
oxc := '-'
|
||||
ox := (mode & 0100) != 0
|
||||
setuid := (mode & os.ModeSetuid) != 0
|
||||
if ox && setuid {
|
||||
oxc = 's'
|
||||
} else if setuid {
|
||||
oxc = 'S'
|
||||
} else if ox {
|
||||
oxc = 'x'
|
||||
}
|
||||
|
||||
// group
|
||||
grc := '-'
|
||||
if (mode & 040) != 0 {
|
||||
grc = 'r'
|
||||
}
|
||||
gwc := '-'
|
||||
if (mode & 020) != 0 {
|
||||
gwc = 'w'
|
||||
}
|
||||
gxc := '-'
|
||||
gx := (mode & 010) != 0
|
||||
setgid := (mode & os.ModeSetgid) != 0
|
||||
if gx && setgid {
|
||||
gxc = 's'
|
||||
} else if setgid {
|
||||
gxc = 'S'
|
||||
} else if gx {
|
||||
gxc = 'x'
|
||||
}
|
||||
|
||||
// all / others
|
||||
arc := '-'
|
||||
if (mode & 04) != 0 {
|
||||
arc = 'r'
|
||||
}
|
||||
awc := '-'
|
||||
if (mode & 02) != 0 {
|
||||
awc = 'w'
|
||||
}
|
||||
axc := '-'
|
||||
ax := (mode & 01) != 0
|
||||
sticky := (mode & os.ModeSticky) != 0
|
||||
if ax && sticky {
|
||||
axc = 't'
|
||||
} else if sticky {
|
||||
axc = 'T'
|
||||
} else if ax {
|
||||
axc = 'x'
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%c%c%c%c%c%c%c%c%c%c", tc, orc, owc, oxc, grc, gwc, gxc, arc, awc, axc)
|
||||
}
|
||||
|
||||
func runLsStatt(dirname string, dirent os.FileInfo, statt *syscall.Stat_t) string {
|
||||
// example from openssh sftp server:
|
||||
// crw-rw-rw- 1 root wheel 0 Jul 31 20:52 ttyvd
|
||||
// format:
|
||||
// {directory / char device / etc}{rwxrwxrwx} {number of links} owner group size month day [time (this year) | year (otherwise)] name
|
||||
|
||||
typeword := runLsTypeWord(dirent)
|
||||
numLinks := statt.Nlink
|
||||
uid := statt.Uid
|
||||
gid := statt.Gid
|
||||
username := fmt.Sprintf("%d", uid)
|
||||
groupname := fmt.Sprintf("%d", gid)
|
||||
// TODO FIXME: uid -> username, gid -> groupname lookup for ls -l format output
|
||||
|
||||
mtime := dirent.ModTime()
|
||||
monthStr := mtime.Month().String()[0:3]
|
||||
day := mtime.Day()
|
||||
year := mtime.Year()
|
||||
now := time.Now()
|
||||
isOld := mtime.Before(now.Add(-time.Hour * 24 * 365 / 2))
|
||||
|
||||
yearOrTime := fmt.Sprintf("%02d:%02d", mtime.Hour(), mtime.Minute())
|
||||
if isOld {
|
||||
yearOrTime = fmt.Sprintf("%d", year)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s %4d %-8s %-8s %8d %s %2d %5s %s", typeword, numLinks, username, groupname, dirent.Size(), monthStr, day, yearOrTime, dirent.Name())
|
||||
}
|
||||
|
||||
// ls -l style output for a file, which is in the 'long output' section of a readdir response packet
|
||||
// this is a very simple (lazy) implementation, just enough to look almost like openssh in a few basic cases
|
||||
func runLs(dirname string, dirent os.FileInfo) string {
|
||||
dsys := dirent.Sys()
|
||||
if dsys == nil {
|
||||
} else if statt, ok := dsys.(*syscall.Stat_t); !ok {
|
||||
} else {
|
||||
return runLsStatt(dirname, dirent, statt)
|
||||
}
|
||||
|
||||
return path.Join(dirname, dirent.Name())
|
||||
}
|
217
vendor/github.com/pkg/sftp/sftp.go
generated
vendored
Normal file
217
vendor/github.com/pkg/sftp/sftp.go
generated
vendored
Normal file
|
@ -0,0 +1,217 @@
|
|||
// Package sftp implements the SSH File Transfer Protocol as described in
|
||||
// https://filezilla-project.org/specs/draft-ietf-secsh-filexfer-02.txt
|
||||
package sftp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
ssh_FXP_INIT = 1
|
||||
ssh_FXP_VERSION = 2
|
||||
ssh_FXP_OPEN = 3
|
||||
ssh_FXP_CLOSE = 4
|
||||
ssh_FXP_READ = 5
|
||||
ssh_FXP_WRITE = 6
|
||||
ssh_FXP_LSTAT = 7
|
||||
ssh_FXP_FSTAT = 8
|
||||
ssh_FXP_SETSTAT = 9
|
||||
ssh_FXP_FSETSTAT = 10
|
||||
ssh_FXP_OPENDIR = 11
|
||||
ssh_FXP_READDIR = 12
|
||||
ssh_FXP_REMOVE = 13
|
||||
ssh_FXP_MKDIR = 14
|
||||
ssh_FXP_RMDIR = 15
|
||||
ssh_FXP_REALPATH = 16
|
||||
ssh_FXP_STAT = 17
|
||||
ssh_FXP_RENAME = 18
|
||||
ssh_FXP_READLINK = 19
|
||||
ssh_FXP_SYMLINK = 20
|
||||
ssh_FXP_STATUS = 101
|
||||
ssh_FXP_HANDLE = 102
|
||||
ssh_FXP_DATA = 103
|
||||
ssh_FXP_NAME = 104
|
||||
ssh_FXP_ATTRS = 105
|
||||
ssh_FXP_EXTENDED = 200
|
||||
ssh_FXP_EXTENDED_REPLY = 201
|
||||
)
|
||||
|
||||
const (
|
||||
ssh_FX_OK = 0
|
||||
ssh_FX_EOF = 1
|
||||
ssh_FX_NO_SUCH_FILE = 2
|
||||
ssh_FX_PERMISSION_DENIED = 3
|
||||
ssh_FX_FAILURE = 4
|
||||
ssh_FX_BAD_MESSAGE = 5
|
||||
ssh_FX_NO_CONNECTION = 6
|
||||
ssh_FX_CONNECTION_LOST = 7
|
||||
ssh_FX_OP_UNSUPPORTED = 8
|
||||
|
||||
// see draft-ietf-secsh-filexfer-13
|
||||
// https://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-9.1
|
||||
ssh_FX_INVALID_HANDLE = 9
|
||||
ssh_FX_NO_SUCH_PATH = 10
|
||||
ssh_FX_FILE_ALREADY_EXISTS = 11
|
||||
ssh_FX_WRITE_PROTECT = 12
|
||||
ssh_FX_NO_MEDIA = 13
|
||||
ssh_FX_NO_SPACE_ON_FILESYSTEM = 14
|
||||
ssh_FX_QUOTA_EXCEEDED = 15
|
||||
ssh_FX_UNKNOWN_PRINCIPAL = 16
|
||||
ssh_FX_LOCK_CONFLICT = 17
|
||||
ssh_FX_DIR_NOT_EMPTY = 18
|
||||
ssh_FX_NOT_A_DIRECTORY = 19
|
||||
ssh_FX_INVALID_FILENAME = 20
|
||||
ssh_FX_LINK_LOOP = 21
|
||||
ssh_FX_CANNOT_DELETE = 22
|
||||
ssh_FX_INVALID_PARAMETER = 23
|
||||
ssh_FX_FILE_IS_A_DIRECTORY = 24
|
||||
ssh_FX_BYTE_RANGE_LOCK_CONFLICT = 25
|
||||
ssh_FX_BYTE_RANGE_LOCK_REFUSED = 26
|
||||
ssh_FX_DELETE_PENDING = 27
|
||||
ssh_FX_FILE_CORRUPT = 28
|
||||
ssh_FX_OWNER_INVALID = 29
|
||||
ssh_FX_GROUP_INVALID = 30
|
||||
ssh_FX_NO_MATCHING_BYTE_RANGE_LOCK = 31
|
||||
)
|
||||
|
||||
const (
|
||||
ssh_FXF_READ = 0x00000001
|
||||
ssh_FXF_WRITE = 0x00000002
|
||||
ssh_FXF_APPEND = 0x00000004
|
||||
ssh_FXF_CREAT = 0x00000008
|
||||
ssh_FXF_TRUNC = 0x00000010
|
||||
ssh_FXF_EXCL = 0x00000020
|
||||
)
|
||||
|
||||
type fxp uint8
|
||||
|
||||
func (f fxp) String() string {
|
||||
switch f {
|
||||
case ssh_FXP_INIT:
|
||||
return "SSH_FXP_INIT"
|
||||
case ssh_FXP_VERSION:
|
||||
return "SSH_FXP_VERSION"
|
||||
case ssh_FXP_OPEN:
|
||||
return "SSH_FXP_OPEN"
|
||||
case ssh_FXP_CLOSE:
|
||||
return "SSH_FXP_CLOSE"
|
||||
case ssh_FXP_READ:
|
||||
return "SSH_FXP_READ"
|
||||
case ssh_FXP_WRITE:
|
||||
return "SSH_FXP_WRITE"
|
||||
case ssh_FXP_LSTAT:
|
||||
return "SSH_FXP_LSTAT"
|
||||
case ssh_FXP_FSTAT:
|
||||
return "SSH_FXP_FSTAT"
|
||||
case ssh_FXP_SETSTAT:
|
||||
return "SSH_FXP_SETSTAT"
|
||||
case ssh_FXP_FSETSTAT:
|
||||
return "SSH_FXP_FSETSTAT"
|
||||
case ssh_FXP_OPENDIR:
|
||||
return "SSH_FXP_OPENDIR"
|
||||
case ssh_FXP_READDIR:
|
||||
return "SSH_FXP_READDIR"
|
||||
case ssh_FXP_REMOVE:
|
||||
return "SSH_FXP_REMOVE"
|
||||
case ssh_FXP_MKDIR:
|
||||
return "SSH_FXP_MKDIR"
|
||||
case ssh_FXP_RMDIR:
|
||||
return "SSH_FXP_RMDIR"
|
||||
case ssh_FXP_REALPATH:
|
||||
return "SSH_FXP_REALPATH"
|
||||
case ssh_FXP_STAT:
|
||||
return "SSH_FXP_STAT"
|
||||
case ssh_FXP_RENAME:
|
||||
return "SSH_FXP_RENAME"
|
||||
case ssh_FXP_READLINK:
|
||||
return "SSH_FXP_READLINK"
|
||||
case ssh_FXP_SYMLINK:
|
||||
return "SSH_FXP_SYMLINK"
|
||||
case ssh_FXP_STATUS:
|
||||
return "SSH_FXP_STATUS"
|
||||
case ssh_FXP_HANDLE:
|
||||
return "SSH_FXP_HANDLE"
|
||||
case ssh_FXP_DATA:
|
||||
return "SSH_FXP_DATA"
|
||||
case ssh_FXP_NAME:
|
||||
return "SSH_FXP_NAME"
|
||||
case ssh_FXP_ATTRS:
|
||||
return "SSH_FXP_ATTRS"
|
||||
case ssh_FXP_EXTENDED:
|
||||
return "SSH_FXP_EXTENDED"
|
||||
case ssh_FXP_EXTENDED_REPLY:
|
||||
return "SSH_FXP_EXTENDED_REPLY"
|
||||
default:
|
||||
return "unknown"
|
||||
}
|
||||
}
|
||||
|
||||
type fx uint8
|
||||
|
||||
func (f fx) String() string {
|
||||
switch f {
|
||||
case ssh_FX_OK:
|
||||
return "SSH_FX_OK"
|
||||
case ssh_FX_EOF:
|
||||
return "SSH_FX_EOF"
|
||||
case ssh_FX_NO_SUCH_FILE:
|
||||
return "SSH_FX_NO_SUCH_FILE"
|
||||
case ssh_FX_PERMISSION_DENIED:
|
||||
return "SSH_FX_PERMISSION_DENIED"
|
||||
case ssh_FX_FAILURE:
|
||||
return "SSH_FX_FAILURE"
|
||||
case ssh_FX_BAD_MESSAGE:
|
||||
return "SSH_FX_BAD_MESSAGE"
|
||||
case ssh_FX_NO_CONNECTION:
|
||||
return "SSH_FX_NO_CONNECTION"
|
||||
case ssh_FX_CONNECTION_LOST:
|
||||
return "SSH_FX_CONNECTION_LOST"
|
||||
case ssh_FX_OP_UNSUPPORTED:
|
||||
return "SSH_FX_OP_UNSUPPORTED"
|
||||
default:
|
||||
return "unknown"
|
||||
}
|
||||
}
|
||||
|
||||
type unexpectedPacketErr struct {
|
||||
want, got uint8
|
||||
}
|
||||
|
||||
func (u *unexpectedPacketErr) Error() string {
|
||||
return fmt.Sprintf("sftp: unexpected packet: want %v, got %v", fxp(u.want), fxp(u.got))
|
||||
}
|
||||
|
||||
func unimplementedPacketErr(u uint8) error {
|
||||
return errors.Errorf("sftp: unimplemented packet type: got %v", fxp(u))
|
||||
}
|
||||
|
||||
type unexpectedIDErr struct{ want, got uint32 }
|
||||
|
||||
func (u *unexpectedIDErr) Error() string {
|
||||
return fmt.Sprintf("sftp: unexpected id: want %v, got %v", u.want, u.got)
|
||||
}
|
||||
|
||||
func unimplementedSeekWhence(whence int) error {
|
||||
return errors.Errorf("sftp: unimplemented seek whence %v", whence)
|
||||
}
|
||||
|
||||
func unexpectedCount(want, got uint32) error {
|
||||
return errors.Errorf("sftp: unexpected count: want %v, got %v", want, got)
|
||||
}
|
||||
|
||||
type unexpectedVersionErr struct{ want, got uint32 }
|
||||
|
||||
func (u *unexpectedVersionErr) Error() string {
|
||||
return fmt.Sprintf("sftp: unexpected server version: want %v, got %v", u.want, u.got)
|
||||
}
|
||||
|
||||
// A StatusError is returned when an SFTP operation fails, and provides
|
||||
// additional information about the failure.
|
||||
type StatusError struct {
|
||||
Code uint32
|
||||
msg, lang string
|
||||
}
|
||||
|
||||
func (s *StatusError) Error() string { return fmt.Sprintf("sftp: %q (%v)", s.msg, fx(s.Code)) }
|
Loading…
Add table
Add a link
Reference in a new issue