Vendor packages and add Makefile
* Vendors packages * Adds a Makefile that uses docker to build a static binary against musl using alpine linux. Signed-off-by: Gabriel Adrian Samfira <gsamfira@cloudbasesolutions.com>
This commit is contained in:
parent
0a17578798
commit
bbbe67bf7c
1763 changed files with 755342 additions and 5 deletions
24
vendor/github.com/pkg/errors/.gitignore
generated
vendored
Normal file
24
vendor/github.com/pkg/errors/.gitignore
generated
vendored
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
*.test
|
||||
*.prof
|
||||
10
vendor/github.com/pkg/errors/.travis.yml
generated
vendored
Normal file
10
vendor/github.com/pkg/errors/.travis.yml
generated
vendored
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
language: go
|
||||
go_import_path: github.com/pkg/errors
|
||||
go:
|
||||
- 1.11.x
|
||||
- 1.12.x
|
||||
- 1.13.x
|
||||
- tip
|
||||
|
||||
script:
|
||||
- make check
|
||||
23
vendor/github.com/pkg/errors/LICENSE
generated
vendored
Normal file
23
vendor/github.com/pkg/errors/LICENSE
generated
vendored
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
Copyright (c) 2015, Dave Cheney <dave@cheney.net>
|
||||
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.
|
||||
44
vendor/github.com/pkg/errors/Makefile
generated
vendored
Normal file
44
vendor/github.com/pkg/errors/Makefile
generated
vendored
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
PKGS := github.com/pkg/errors
|
||||
SRCDIRS := $(shell go list -f '{{.Dir}}' $(PKGS))
|
||||
GO := go
|
||||
|
||||
check: test vet gofmt misspell unconvert staticcheck ineffassign unparam
|
||||
|
||||
test:
|
||||
$(GO) test $(PKGS)
|
||||
|
||||
vet: | test
|
||||
$(GO) vet $(PKGS)
|
||||
|
||||
staticcheck:
|
||||
$(GO) get honnef.co/go/tools/cmd/staticcheck
|
||||
staticcheck -checks all $(PKGS)
|
||||
|
||||
misspell:
|
||||
$(GO) get github.com/client9/misspell/cmd/misspell
|
||||
misspell \
|
||||
-locale GB \
|
||||
-error \
|
||||
*.md *.go
|
||||
|
||||
unconvert:
|
||||
$(GO) get github.com/mdempsky/unconvert
|
||||
unconvert -v $(PKGS)
|
||||
|
||||
ineffassign:
|
||||
$(GO) get github.com/gordonklaus/ineffassign
|
||||
find $(SRCDIRS) -name '*.go' | xargs ineffassign
|
||||
|
||||
pedantic: check errcheck
|
||||
|
||||
unparam:
|
||||
$(GO) get mvdan.cc/unparam
|
||||
unparam ./...
|
||||
|
||||
errcheck:
|
||||
$(GO) get github.com/kisielk/errcheck
|
||||
errcheck $(PKGS)
|
||||
|
||||
gofmt:
|
||||
@echo Checking code is gofmted
|
||||
@test -z "$(shell gofmt -s -l -d -e $(SRCDIRS) | tee /dev/stderr)"
|
||||
59
vendor/github.com/pkg/errors/README.md
generated
vendored
Normal file
59
vendor/github.com/pkg/errors/README.md
generated
vendored
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
# errors [](https://travis-ci.org/pkg/errors) [](https://ci.appveyor.com/project/davecheney/errors/branch/master) [](http://godoc.org/github.com/pkg/errors) [](https://goreportcard.com/report/github.com/pkg/errors) [](https://sourcegraph.com/github.com/pkg/errors?badge)
|
||||
|
||||
Package errors provides simple error handling primitives.
|
||||
|
||||
`go get github.com/pkg/errors`
|
||||
|
||||
The traditional error handling idiom in Go is roughly akin to
|
||||
```go
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
```
|
||||
which applied recursively up the call stack results in error reports without context or debugging information. The errors package allows programmers to add context to the failure path in their code in a way that does not destroy the original value of the error.
|
||||
|
||||
## Adding context to an error
|
||||
|
||||
The errors.Wrap function returns a new error that adds context to the original error. For example
|
||||
```go
|
||||
_, err := ioutil.ReadAll(r)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "read failed")
|
||||
}
|
||||
```
|
||||
## Retrieving the cause of an error
|
||||
|
||||
Using `errors.Wrap` constructs a stack of errors, adding context to the preceding error. Depending on the nature of the error it may be necessary to reverse the operation of errors.Wrap to retrieve the original error for inspection. Any error value which implements this interface can be inspected by `errors.Cause`.
|
||||
```go
|
||||
type causer interface {
|
||||
Cause() error
|
||||
}
|
||||
```
|
||||
`errors.Cause` will recursively retrieve the topmost error which does not implement `causer`, which is assumed to be the original cause. For example:
|
||||
```go
|
||||
switch err := errors.Cause(err).(type) {
|
||||
case *MyError:
|
||||
// handle specifically
|
||||
default:
|
||||
// unknown error
|
||||
}
|
||||
```
|
||||
|
||||
[Read the package documentation for more information](https://godoc.org/github.com/pkg/errors).
|
||||
|
||||
## Roadmap
|
||||
|
||||
With the upcoming [Go2 error proposals](https://go.googlesource.com/proposal/+/master/design/go2draft.md) this package is moving into maintenance mode. The roadmap for a 1.0 release is as follows:
|
||||
|
||||
- 0.9. Remove pre Go 1.9 and Go 1.10 support, address outstanding pull requests (if possible)
|
||||
- 1.0. Final release.
|
||||
|
||||
## Contributing
|
||||
|
||||
Because of the Go2 errors changes, this package is not accepting proposals for new functionality. With that said, we welcome pull requests, bug fixes and issue reports.
|
||||
|
||||
Before sending a PR, please discuss your change by raising an issue.
|
||||
|
||||
## License
|
||||
|
||||
BSD-2-Clause
|
||||
32
vendor/github.com/pkg/errors/appveyor.yml
generated
vendored
Normal file
32
vendor/github.com/pkg/errors/appveyor.yml
generated
vendored
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
version: build-{build}.{branch}
|
||||
|
||||
clone_folder: C:\gopath\src\github.com\pkg\errors
|
||||
shallow_clone: true # for startup speed
|
||||
|
||||
environment:
|
||||
GOPATH: C:\gopath
|
||||
|
||||
platform:
|
||||
- x64
|
||||
|
||||
# http://www.appveyor.com/docs/installed-software
|
||||
install:
|
||||
# some helpful output for debugging builds
|
||||
- go version
|
||||
- go env
|
||||
# pre-installed MinGW at C:\MinGW is 32bit only
|
||||
# but MSYS2 at C:\msys64 has mingw64
|
||||
- set PATH=C:\msys64\mingw64\bin;%PATH%
|
||||
- gcc --version
|
||||
- g++ --version
|
||||
|
||||
build_script:
|
||||
- go install -v ./...
|
||||
|
||||
test_script:
|
||||
- set PATH=C:\gopath\bin;%PATH%
|
||||
- go test -v ./...
|
||||
|
||||
#artifacts:
|
||||
# - path: '%GOPATH%\bin\*.exe'
|
||||
deploy: off
|
||||
288
vendor/github.com/pkg/errors/errors.go
generated
vendored
Normal file
288
vendor/github.com/pkg/errors/errors.go
generated
vendored
Normal file
|
|
@ -0,0 +1,288 @@
|
|||
// Package errors provides simple error handling primitives.
|
||||
//
|
||||
// The traditional error handling idiom in Go is roughly akin to
|
||||
//
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
//
|
||||
// which when applied recursively up the call stack results in error reports
|
||||
// without context or debugging information. The errors package allows
|
||||
// programmers to add context to the failure path in their code in a way
|
||||
// that does not destroy the original value of the error.
|
||||
//
|
||||
// Adding context to an error
|
||||
//
|
||||
// The errors.Wrap function returns a new error that adds context to the
|
||||
// original error by recording a stack trace at the point Wrap is called,
|
||||
// together with the supplied message. For example
|
||||
//
|
||||
// _, err := ioutil.ReadAll(r)
|
||||
// if err != nil {
|
||||
// return errors.Wrap(err, "read failed")
|
||||
// }
|
||||
//
|
||||
// If additional control is required, the errors.WithStack and
|
||||
// errors.WithMessage functions destructure errors.Wrap into its component
|
||||
// operations: annotating an error with a stack trace and with a message,
|
||||
// respectively.
|
||||
//
|
||||
// Retrieving the cause of an error
|
||||
//
|
||||
// Using errors.Wrap constructs a stack of errors, adding context to the
|
||||
// preceding error. Depending on the nature of the error it may be necessary
|
||||
// to reverse the operation of errors.Wrap to retrieve the original error
|
||||
// for inspection. Any error value which implements this interface
|
||||
//
|
||||
// type causer interface {
|
||||
// Cause() error
|
||||
// }
|
||||
//
|
||||
// can be inspected by errors.Cause. errors.Cause will recursively retrieve
|
||||
// the topmost error that does not implement causer, which is assumed to be
|
||||
// the original cause. For example:
|
||||
//
|
||||
// switch err := errors.Cause(err).(type) {
|
||||
// case *MyError:
|
||||
// // handle specifically
|
||||
// default:
|
||||
// // unknown error
|
||||
// }
|
||||
//
|
||||
// Although the causer interface is not exported by this package, it is
|
||||
// considered a part of its stable public interface.
|
||||
//
|
||||
// Formatted printing of errors
|
||||
//
|
||||
// All error values returned from this package implement fmt.Formatter and can
|
||||
// be formatted by the fmt package. The following verbs are supported:
|
||||
//
|
||||
// %s print the error. If the error has a Cause it will be
|
||||
// printed recursively.
|
||||
// %v see %s
|
||||
// %+v extended format. Each Frame of the error's StackTrace will
|
||||
// be printed in detail.
|
||||
//
|
||||
// Retrieving the stack trace of an error or wrapper
|
||||
//
|
||||
// New, Errorf, Wrap, and Wrapf record a stack trace at the point they are
|
||||
// invoked. This information can be retrieved with the following interface:
|
||||
//
|
||||
// type stackTracer interface {
|
||||
// StackTrace() errors.StackTrace
|
||||
// }
|
||||
//
|
||||
// The returned errors.StackTrace type is defined as
|
||||
//
|
||||
// type StackTrace []Frame
|
||||
//
|
||||
// The Frame type represents a call site in the stack trace. Frame supports
|
||||
// the fmt.Formatter interface that can be used for printing information about
|
||||
// the stack trace of this error. For example:
|
||||
//
|
||||
// if err, ok := err.(stackTracer); ok {
|
||||
// for _, f := range err.StackTrace() {
|
||||
// fmt.Printf("%+s:%d\n", f, f)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// Although the stackTracer interface is not exported by this package, it is
|
||||
// considered a part of its stable public interface.
|
||||
//
|
||||
// See the documentation for Frame.Format for more details.
|
||||
package errors
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
// New returns an error with the supplied message.
|
||||
// New also records the stack trace at the point it was called.
|
||||
func New(message string) error {
|
||||
return &fundamental{
|
||||
msg: message,
|
||||
stack: callers(),
|
||||
}
|
||||
}
|
||||
|
||||
// Errorf formats according to a format specifier and returns the string
|
||||
// as a value that satisfies error.
|
||||
// Errorf also records the stack trace at the point it was called.
|
||||
func Errorf(format string, args ...interface{}) error {
|
||||
return &fundamental{
|
||||
msg: fmt.Sprintf(format, args...),
|
||||
stack: callers(),
|
||||
}
|
||||
}
|
||||
|
||||
// fundamental is an error that has a message and a stack, but no caller.
|
||||
type fundamental struct {
|
||||
msg string
|
||||
*stack
|
||||
}
|
||||
|
||||
func (f *fundamental) Error() string { return f.msg }
|
||||
|
||||
func (f *fundamental) Format(s fmt.State, verb rune) {
|
||||
switch verb {
|
||||
case 'v':
|
||||
if s.Flag('+') {
|
||||
io.WriteString(s, f.msg)
|
||||
f.stack.Format(s, verb)
|
||||
return
|
||||
}
|
||||
fallthrough
|
||||
case 's':
|
||||
io.WriteString(s, f.msg)
|
||||
case 'q':
|
||||
fmt.Fprintf(s, "%q", f.msg)
|
||||
}
|
||||
}
|
||||
|
||||
// WithStack annotates err with a stack trace at the point WithStack was called.
|
||||
// If err is nil, WithStack returns nil.
|
||||
func WithStack(err error) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
return &withStack{
|
||||
err,
|
||||
callers(),
|
||||
}
|
||||
}
|
||||
|
||||
type withStack struct {
|
||||
error
|
||||
*stack
|
||||
}
|
||||
|
||||
func (w *withStack) Cause() error { return w.error }
|
||||
|
||||
// Unwrap provides compatibility for Go 1.13 error chains.
|
||||
func (w *withStack) Unwrap() error { return w.error }
|
||||
|
||||
func (w *withStack) Format(s fmt.State, verb rune) {
|
||||
switch verb {
|
||||
case 'v':
|
||||
if s.Flag('+') {
|
||||
fmt.Fprintf(s, "%+v", w.Cause())
|
||||
w.stack.Format(s, verb)
|
||||
return
|
||||
}
|
||||
fallthrough
|
||||
case 's':
|
||||
io.WriteString(s, w.Error())
|
||||
case 'q':
|
||||
fmt.Fprintf(s, "%q", w.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// Wrap returns an error annotating err with a stack trace
|
||||
// at the point Wrap is called, and the supplied message.
|
||||
// If err is nil, Wrap returns nil.
|
||||
func Wrap(err error, message string) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
err = &withMessage{
|
||||
cause: err,
|
||||
msg: message,
|
||||
}
|
||||
return &withStack{
|
||||
err,
|
||||
callers(),
|
||||
}
|
||||
}
|
||||
|
||||
// Wrapf returns an error annotating err with a stack trace
|
||||
// at the point Wrapf is called, and the format specifier.
|
||||
// If err is nil, Wrapf returns nil.
|
||||
func Wrapf(err error, format string, args ...interface{}) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
err = &withMessage{
|
||||
cause: err,
|
||||
msg: fmt.Sprintf(format, args...),
|
||||
}
|
||||
return &withStack{
|
||||
err,
|
||||
callers(),
|
||||
}
|
||||
}
|
||||
|
||||
// WithMessage annotates err with a new message.
|
||||
// If err is nil, WithMessage returns nil.
|
||||
func WithMessage(err error, message string) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
return &withMessage{
|
||||
cause: err,
|
||||
msg: message,
|
||||
}
|
||||
}
|
||||
|
||||
// WithMessagef annotates err with the format specifier.
|
||||
// If err is nil, WithMessagef returns nil.
|
||||
func WithMessagef(err error, format string, args ...interface{}) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
return &withMessage{
|
||||
cause: err,
|
||||
msg: fmt.Sprintf(format, args...),
|
||||
}
|
||||
}
|
||||
|
||||
type withMessage struct {
|
||||
cause error
|
||||
msg string
|
||||
}
|
||||
|
||||
func (w *withMessage) Error() string { return w.msg + ": " + w.cause.Error() }
|
||||
func (w *withMessage) Cause() error { return w.cause }
|
||||
|
||||
// Unwrap provides compatibility for Go 1.13 error chains.
|
||||
func (w *withMessage) Unwrap() error { return w.cause }
|
||||
|
||||
func (w *withMessage) Format(s fmt.State, verb rune) {
|
||||
switch verb {
|
||||
case 'v':
|
||||
if s.Flag('+') {
|
||||
fmt.Fprintf(s, "%+v\n", w.Cause())
|
||||
io.WriteString(s, w.msg)
|
||||
return
|
||||
}
|
||||
fallthrough
|
||||
case 's', 'q':
|
||||
io.WriteString(s, w.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// Cause returns the underlying cause of the error, if possible.
|
||||
// An error value has a cause if it implements the following
|
||||
// interface:
|
||||
//
|
||||
// type causer interface {
|
||||
// Cause() error
|
||||
// }
|
||||
//
|
||||
// If the error does not implement Cause, the original error will
|
||||
// be returned. If the error is nil, nil will be returned without further
|
||||
// investigation.
|
||||
func Cause(err error) error {
|
||||
type causer interface {
|
||||
Cause() error
|
||||
}
|
||||
|
||||
for err != nil {
|
||||
cause, ok := err.(causer)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
err = cause.Cause()
|
||||
}
|
||||
return err
|
||||
}
|
||||
38
vendor/github.com/pkg/errors/go113.go
generated
vendored
Normal file
38
vendor/github.com/pkg/errors/go113.go
generated
vendored
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
// +build go1.13
|
||||
|
||||
package errors
|
||||
|
||||
import (
|
||||
stderrors "errors"
|
||||
)
|
||||
|
||||
// Is reports whether any error in err's chain matches target.
|
||||
//
|
||||
// The chain consists of err itself followed by the sequence of errors obtained by
|
||||
// repeatedly calling Unwrap.
|
||||
//
|
||||
// An error is considered to match a target if it is equal to that target or if
|
||||
// it implements a method Is(error) bool such that Is(target) returns true.
|
||||
func Is(err, target error) bool { return stderrors.Is(err, target) }
|
||||
|
||||
// As finds the first error in err's chain that matches target, and if so, sets
|
||||
// target to that error value and returns true.
|
||||
//
|
||||
// The chain consists of err itself followed by the sequence of errors obtained by
|
||||
// repeatedly calling Unwrap.
|
||||
//
|
||||
// An error matches target if the error's concrete value is assignable to the value
|
||||
// pointed to by target, or if the error has a method As(interface{}) bool such that
|
||||
// As(target) returns true. In the latter case, the As method is responsible for
|
||||
// setting target.
|
||||
//
|
||||
// As will panic if target is not a non-nil pointer to either a type that implements
|
||||
// error, or to any interface type. As returns false if err is nil.
|
||||
func As(err error, target interface{}) bool { return stderrors.As(err, target) }
|
||||
|
||||
// Unwrap returns the result of calling the Unwrap method on err, if err's
|
||||
// type contains an Unwrap method returning error.
|
||||
// Otherwise, Unwrap returns nil.
|
||||
func Unwrap(err error) error {
|
||||
return stderrors.Unwrap(err)
|
||||
}
|
||||
177
vendor/github.com/pkg/errors/stack.go
generated
vendored
Normal file
177
vendor/github.com/pkg/errors/stack.go
generated
vendored
Normal file
|
|
@ -0,0 +1,177 @@
|
|||
package errors
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"path"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Frame represents a program counter inside a stack frame.
|
||||
// For historical reasons if Frame is interpreted as a uintptr
|
||||
// its value represents the program counter + 1.
|
||||
type Frame uintptr
|
||||
|
||||
// pc returns the program counter for this frame;
|
||||
// multiple frames may have the same PC value.
|
||||
func (f Frame) pc() uintptr { return uintptr(f) - 1 }
|
||||
|
||||
// file returns the full path to the file that contains the
|
||||
// function for this Frame's pc.
|
||||
func (f Frame) file() string {
|
||||
fn := runtime.FuncForPC(f.pc())
|
||||
if fn == nil {
|
||||
return "unknown"
|
||||
}
|
||||
file, _ := fn.FileLine(f.pc())
|
||||
return file
|
||||
}
|
||||
|
||||
// line returns the line number of source code of the
|
||||
// function for this Frame's pc.
|
||||
func (f Frame) line() int {
|
||||
fn := runtime.FuncForPC(f.pc())
|
||||
if fn == nil {
|
||||
return 0
|
||||
}
|
||||
_, line := fn.FileLine(f.pc())
|
||||
return line
|
||||
}
|
||||
|
||||
// name returns the name of this function, if known.
|
||||
func (f Frame) name() string {
|
||||
fn := runtime.FuncForPC(f.pc())
|
||||
if fn == nil {
|
||||
return "unknown"
|
||||
}
|
||||
return fn.Name()
|
||||
}
|
||||
|
||||
// Format formats the frame according to the fmt.Formatter interface.
|
||||
//
|
||||
// %s source file
|
||||
// %d source line
|
||||
// %n function name
|
||||
// %v equivalent to %s:%d
|
||||
//
|
||||
// Format accepts flags that alter the printing of some verbs, as follows:
|
||||
//
|
||||
// %+s function name and path of source file relative to the compile time
|
||||
// GOPATH separated by \n\t (<funcname>\n\t<path>)
|
||||
// %+v equivalent to %+s:%d
|
||||
func (f Frame) Format(s fmt.State, verb rune) {
|
||||
switch verb {
|
||||
case 's':
|
||||
switch {
|
||||
case s.Flag('+'):
|
||||
io.WriteString(s, f.name())
|
||||
io.WriteString(s, "\n\t")
|
||||
io.WriteString(s, f.file())
|
||||
default:
|
||||
io.WriteString(s, path.Base(f.file()))
|
||||
}
|
||||
case 'd':
|
||||
io.WriteString(s, strconv.Itoa(f.line()))
|
||||
case 'n':
|
||||
io.WriteString(s, funcname(f.name()))
|
||||
case 'v':
|
||||
f.Format(s, 's')
|
||||
io.WriteString(s, ":")
|
||||
f.Format(s, 'd')
|
||||
}
|
||||
}
|
||||
|
||||
// MarshalText formats a stacktrace Frame as a text string. The output is the
|
||||
// same as that of fmt.Sprintf("%+v", f), but without newlines or tabs.
|
||||
func (f Frame) MarshalText() ([]byte, error) {
|
||||
name := f.name()
|
||||
if name == "unknown" {
|
||||
return []byte(name), nil
|
||||
}
|
||||
return []byte(fmt.Sprintf("%s %s:%d", name, f.file(), f.line())), nil
|
||||
}
|
||||
|
||||
// StackTrace is stack of Frames from innermost (newest) to outermost (oldest).
|
||||
type StackTrace []Frame
|
||||
|
||||
// Format formats the stack of Frames according to the fmt.Formatter interface.
|
||||
//
|
||||
// %s lists source files for each Frame in the stack
|
||||
// %v lists the source file and line number for each Frame in the stack
|
||||
//
|
||||
// Format accepts flags that alter the printing of some verbs, as follows:
|
||||
//
|
||||
// %+v Prints filename, function, and line number for each Frame in the stack.
|
||||
func (st StackTrace) Format(s fmt.State, verb rune) {
|
||||
switch verb {
|
||||
case 'v':
|
||||
switch {
|
||||
case s.Flag('+'):
|
||||
for _, f := range st {
|
||||
io.WriteString(s, "\n")
|
||||
f.Format(s, verb)
|
||||
}
|
||||
case s.Flag('#'):
|
||||
fmt.Fprintf(s, "%#v", []Frame(st))
|
||||
default:
|
||||
st.formatSlice(s, verb)
|
||||
}
|
||||
case 's':
|
||||
st.formatSlice(s, verb)
|
||||
}
|
||||
}
|
||||
|
||||
// formatSlice will format this StackTrace into the given buffer as a slice of
|
||||
// Frame, only valid when called with '%s' or '%v'.
|
||||
func (st StackTrace) formatSlice(s fmt.State, verb rune) {
|
||||
io.WriteString(s, "[")
|
||||
for i, f := range st {
|
||||
if i > 0 {
|
||||
io.WriteString(s, " ")
|
||||
}
|
||||
f.Format(s, verb)
|
||||
}
|
||||
io.WriteString(s, "]")
|
||||
}
|
||||
|
||||
// stack represents a stack of program counters.
|
||||
type stack []uintptr
|
||||
|
||||
func (s *stack) Format(st fmt.State, verb rune) {
|
||||
switch verb {
|
||||
case 'v':
|
||||
switch {
|
||||
case st.Flag('+'):
|
||||
for _, pc := range *s {
|
||||
f := Frame(pc)
|
||||
fmt.Fprintf(st, "\n%+v", f)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *stack) StackTrace() StackTrace {
|
||||
f := make([]Frame, len(*s))
|
||||
for i := 0; i < len(f); i++ {
|
||||
f[i] = Frame((*s)[i])
|
||||
}
|
||||
return f
|
||||
}
|
||||
|
||||
func callers() *stack {
|
||||
const depth = 32
|
||||
var pcs [depth]uintptr
|
||||
n := runtime.Callers(3, pcs[:])
|
||||
var st stack = pcs[0:n]
|
||||
return &st
|
||||
}
|
||||
|
||||
// funcname removes the path prefix component of a function's name reported by func.Name().
|
||||
func funcname(name string) string {
|
||||
i := strings.LastIndex(name, "/")
|
||||
name = name[i+1:]
|
||||
i = strings.Index(name, ".")
|
||||
return name[i+1:]
|
||||
}
|
||||
10
vendor/github.com/pkg/sftp/.gitignore
generated
vendored
Normal file
10
vendor/github.com/pkg/sftp/.gitignore
generated
vendored
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
.*.swo
|
||||
.*.swp
|
||||
|
||||
server_standalone/server_standalone
|
||||
|
||||
examples/*/id_rsa
|
||||
examples/*/id_rsa.pub
|
||||
|
||||
memprofile.out
|
||||
memprofile.svg
|
||||
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.
|
||||
27
vendor/github.com/pkg/sftp/Makefile
generated
vendored
Normal file
27
vendor/github.com/pkg/sftp/Makefile
generated
vendored
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
.PHONY: integration integration_w_race benchmark
|
||||
|
||||
integration:
|
||||
go test -integration -v ./...
|
||||
go test -testserver -v ./...
|
||||
go test -integration -testserver -v ./...
|
||||
go test -integration -allocator -v ./...
|
||||
go test -testserver -allocator -v ./...
|
||||
go test -integration -testserver -allocator -v ./...
|
||||
|
||||
integration_w_race:
|
||||
go test -race -integration -v ./...
|
||||
go test -race -testserver -v ./...
|
||||
go test -race -integration -testserver -v ./...
|
||||
go test -race -integration -allocator -v ./...
|
||||
go test -race -testserver -allocator -v ./...
|
||||
go test -race -integration -allocator -testserver -v ./...
|
||||
|
||||
COUNT ?= 1
|
||||
BENCHMARK_PATTERN ?= "."
|
||||
|
||||
benchmark:
|
||||
go test -integration -run=NONE -bench=$(BENCHMARK_PATTERN) -benchmem -count=$(COUNT)
|
||||
|
||||
benchmark_w_memprofile:
|
||||
go test -integration -run=NONE -bench=$(BENCHMARK_PATTERN) -benchmem -count=$(COUNT) -memprofile memprofile.out
|
||||
go tool pprof -svg -output=memprofile.svg memprofile.out
|
||||
44
vendor/github.com/pkg/sftp/README.md
generated
vendored
Normal file
44
vendor/github.com/pkg/sftp/README.md
generated
vendored
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
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://pkg.go.dev/github.com/pkg/sftp)
|
||||
|
||||
usage and examples
|
||||
------------------
|
||||
|
||||
See [https://pkg.go.dev/github.com/pkg/sftp](https://pkg.go.dev/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](https://pkg.go.dev/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 try to 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.
|
||||
|
||||
Thanks.
|
||||
96
vendor/github.com/pkg/sftp/allocator.go
generated
vendored
Normal file
96
vendor/github.com/pkg/sftp/allocator.go
generated
vendored
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
package sftp
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
type allocator struct {
|
||||
sync.Mutex
|
||||
available [][]byte
|
||||
// map key is the request order
|
||||
used map[uint32][][]byte
|
||||
}
|
||||
|
||||
func newAllocator() *allocator {
|
||||
return &allocator{
|
||||
// micro optimization: initialize available pages with an initial capacity
|
||||
available: make([][]byte, 0, SftpServerWorkerCount*2),
|
||||
used: make(map[uint32][][]byte),
|
||||
}
|
||||
}
|
||||
|
||||
// GetPage returns a previously allocated and unused []byte or create a new one.
|
||||
// The slice have a fixed size = maxMsgLength, this value is suitable for both
|
||||
// receiving new packets and reading the files to serve
|
||||
func (a *allocator) GetPage(requestOrderID uint32) []byte {
|
||||
a.Lock()
|
||||
defer a.Unlock()
|
||||
|
||||
var result []byte
|
||||
|
||||
// get an available page and remove it from the available ones.
|
||||
if len(a.available) > 0 {
|
||||
truncLength := len(a.available) - 1
|
||||
result = a.available[truncLength]
|
||||
|
||||
a.available[truncLength] = nil // clear out the internal pointer
|
||||
a.available = a.available[:truncLength] // truncate the slice
|
||||
}
|
||||
|
||||
// no preallocated slice found, just allocate a new one
|
||||
if result == nil {
|
||||
result = make([]byte, maxMsgLength)
|
||||
}
|
||||
|
||||
// put result in used pages
|
||||
a.used[requestOrderID] = append(a.used[requestOrderID], result)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// ReleasePages marks unused all pages in use for the given requestID
|
||||
func (a *allocator) ReleasePages(requestOrderID uint32) {
|
||||
a.Lock()
|
||||
defer a.Unlock()
|
||||
|
||||
if used := a.used[requestOrderID]; len(used) > 0 {
|
||||
a.available = append(a.available, used...)
|
||||
}
|
||||
delete(a.used, requestOrderID)
|
||||
}
|
||||
|
||||
// Free removes all the used and available pages.
|
||||
// Call this method when the allocator is not needed anymore
|
||||
func (a *allocator) Free() {
|
||||
a.Lock()
|
||||
defer a.Unlock()
|
||||
|
||||
a.available = nil
|
||||
a.used = make(map[uint32][][]byte)
|
||||
}
|
||||
|
||||
func (a *allocator) countUsedPages() int {
|
||||
a.Lock()
|
||||
defer a.Unlock()
|
||||
|
||||
num := 0
|
||||
for _, p := range a.used {
|
||||
num += len(p)
|
||||
}
|
||||
return num
|
||||
}
|
||||
|
||||
func (a *allocator) countAvailablePages() int {
|
||||
a.Lock()
|
||||
defer a.Unlock()
|
||||
|
||||
return len(a.available)
|
||||
}
|
||||
|
||||
func (a *allocator) isRequestOrderIDUsed(requestOrderID uint32) bool {
|
||||
a.Lock()
|
||||
defer a.Unlock()
|
||||
|
||||
_, ok := a.used[requestOrderID]
|
||||
return ok
|
||||
}
|
||||
90
vendor/github.com/pkg/sftp/attrs.go
generated
vendored
Normal file
90
vendor/github.com/pkg/sftp/attrs.go
generated
vendored
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
package sftp
|
||||
|
||||
// ssh_FXP_ATTRS support
|
||||
// see http://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#section-5
|
||||
|
||||
import (
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
sshFileXferAttrSize = 0x00000001
|
||||
sshFileXferAttrUIDGID = 0x00000002
|
||||
sshFileXferAttrPermissions = 0x00000004
|
||||
sshFileXferAttrACmodTime = 0x00000008
|
||||
sshFileXferAttrExtended = 0x80000000
|
||||
|
||||
sshFileXferAttrAll = sshFileXferAttrSize | sshFileXferAttrUIDGID | sshFileXferAttrPermissions |
|
||||
sshFileXferAttrACmodTime | sshFileXferAttrExtended
|
||||
)
|
||||
|
||||
// fileInfo is an artificial type designed to satisfy os.FileInfo.
|
||||
type fileInfo struct {
|
||||
name string
|
||||
stat *FileStat
|
||||
}
|
||||
|
||||
// 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 int64(fi.stat.Size) }
|
||||
|
||||
// Mode returns file mode bits.
|
||||
func (fi *fileInfo) Mode() os.FileMode { return toFileMode(fi.stat.Mode) }
|
||||
|
||||
// ModTime returns the last modification time of the file.
|
||||
func (fi *fileInfo) ModTime() time.Time { return time.Unix(int64(fi.stat.Mtime), 0) }
|
||||
|
||||
// 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.stat }
|
||||
|
||||
// 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(). It is also used server side to store the unmarshalled
|
||||
// values for SetStat.
|
||||
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(stat *FileStat, name string) os.FileInfo {
|
||||
return &fileInfo{
|
||||
name: name,
|
||||
stat: stat,
|
||||
}
|
||||
}
|
||||
|
||||
func fileStatFromInfo(fi os.FileInfo) (uint32, *FileStat) {
|
||||
mtime := fi.ModTime().Unix()
|
||||
atime := mtime
|
||||
var flags uint32 = sshFileXferAttrSize |
|
||||
sshFileXferAttrPermissions |
|
||||
sshFileXferAttrACmodTime
|
||||
|
||||
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
|
||||
}
|
||||
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 plan9 windows android
|
||||
|
||||
package sftp
|
||||
|
||||
import (
|
||||
"os"
|
||||
)
|
||||
|
||||
func fileStatFromInfoOs(fi os.FileInfo, flags *uint32, fileStat *FileStat) {
|
||||
// todo
|
||||
}
|
||||
16
vendor/github.com/pkg/sftp/attrs_unix.go
generated
vendored
Normal file
16
vendor/github.com/pkg/sftp/attrs_unix.go
generated
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
// +build darwin dragonfly freebsd !android,linux netbsd openbsd solaris aix js
|
||||
|
||||
package sftp
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func fileStatFromInfoOs(fi os.FileInfo, flags *uint32, fileStat *FileStat) {
|
||||
if statt, ok := fi.Sys().(*syscall.Stat_t); ok {
|
||||
*flags |= sshFileXferAttrUIDGID
|
||||
fileStat.UID = statt.Uid
|
||||
fileStat.GID = statt.Gid
|
||||
}
|
||||
}
|
||||
1936
vendor/github.com/pkg/sftp/client.go
generated
vendored
Normal file
1936
vendor/github.com/pkg/sftp/client.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
189
vendor/github.com/pkg/sftp/conn.go
generated
vendored
Normal file
189
vendor/github.com/pkg/sftp/conn.go
generated
vendored
Normal file
|
|
@ -0,0 +1,189 @@
|
|||
package sftp
|
||||
|
||||
import (
|
||||
"encoding"
|
||||
"fmt"
|
||||
"io"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// conn implements a bidirectional channel on which client and server
|
||||
// connections are multiplexed.
|
||||
type conn struct {
|
||||
io.Reader
|
||||
io.WriteCloser
|
||||
// this is the same allocator used in packet manager
|
||||
alloc *allocator
|
||||
sync.Mutex // used to serialise writes to sendPacket
|
||||
}
|
||||
|
||||
// the orderID is used in server mode if the allocator is enabled.
|
||||
// For the client mode just pass 0
|
||||
func (c *conn) recvPacket(orderID uint32) (uint8, []byte, error) {
|
||||
return recvPacket(c, c.alloc, orderID)
|
||||
}
|
||||
|
||||
func (c *conn) sendPacket(m encoding.BinaryMarshaler) error {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
|
||||
return sendPacket(c, m)
|
||||
}
|
||||
|
||||
func (c *conn) Close() error {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
return c.WriteCloser.Close()
|
||||
}
|
||||
|
||||
type clientConn struct {
|
||||
conn
|
||||
wg sync.WaitGroup
|
||||
|
||||
sync.Mutex // protects inflight
|
||||
inflight map[uint32]chan<- result // outstanding requests
|
||||
|
||||
closed chan struct{}
|
||||
err error
|
||||
}
|
||||
|
||||
// Wait blocks until the conn has shut down, and return the error
|
||||
// causing the shutdown. It can be called concurrently from multiple
|
||||
// goroutines.
|
||||
func (c *clientConn) Wait() error {
|
||||
<-c.closed
|
||||
return c.err
|
||||
}
|
||||
|
||||
// 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 c.conn.Close()
|
||||
|
||||
for {
|
||||
typ, data, err := c.recvPacket(0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sid, _, err := unmarshalUint32Safe(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ch, ok := c.getChannel(sid)
|
||||
if !ok {
|
||||
// This is an unexpected occurrence. Send the error
|
||||
// back to all listeners so that they terminate
|
||||
// gracefully.
|
||||
return fmt.Errorf("sid not found: %d", sid)
|
||||
}
|
||||
|
||||
ch <- result{typ: typ, data: data}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *clientConn) putChannel(ch chan<- result, sid uint32) bool {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
|
||||
select {
|
||||
case <-c.closed:
|
||||
// already closed with broadcastErr, return error on chan.
|
||||
ch <- result{err: ErrSSHFxConnectionLost}
|
||||
return false
|
||||
default:
|
||||
}
|
||||
|
||||
c.inflight[sid] = ch
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *clientConn) getChannel(sid uint32) (chan<- result, bool) {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
|
||||
ch, ok := c.inflight[sid]
|
||||
delete(c.inflight, sid)
|
||||
|
||||
return ch, ok
|
||||
}
|
||||
|
||||
// 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(ch chan result, p idmarshaler) (byte, []byte, error) {
|
||||
if cap(ch) < 1 {
|
||||
ch = make(chan result, 1)
|
||||
}
|
||||
|
||||
c.dispatchRequest(ch, p)
|
||||
s := <-ch
|
||||
return s.typ, s.data, s.err
|
||||
}
|
||||
|
||||
// dispatchRequest should ideally only be called by race-detection tests outside of this file,
|
||||
// where you have to ensure two packets are in flight sequentially after each other.
|
||||
func (c *clientConn) dispatchRequest(ch chan<- result, p idmarshaler) {
|
||||
sid := p.id()
|
||||
|
||||
if !c.putChannel(ch, sid) {
|
||||
// already closed.
|
||||
return
|
||||
}
|
||||
|
||||
if err := c.conn.sendPacket(p); err != nil {
|
||||
if ch, ok := c.getChannel(sid); ok {
|
||||
ch <- result{err: err}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// broadcastErr sends an error to all goroutines waiting for a response.
|
||||
func (c *clientConn) broadcastErr(err error) {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
|
||||
bcastRes := result{err: ErrSSHFxConnectionLost}
|
||||
for sid, ch := range c.inflight {
|
||||
ch <- bcastRes
|
||||
|
||||
// Replace the chan in inflight,
|
||||
// we have hijacked this chan,
|
||||
// and this guarantees always-only-once sending.
|
||||
c.inflight[sid] = make(chan<- result, 1)
|
||||
}
|
||||
|
||||
c.err = err
|
||||
close(c.closed)
|
||||
}
|
||||
|
||||
type serverConn struct {
|
||||
conn
|
||||
}
|
||||
|
||||
func (s *serverConn) sendError(id uint32, err error) error {
|
||||
return s.sendPacket(statusFromError(id, 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...)
|
||||
}
|
||||
22
vendor/github.com/pkg/sftp/fuzz.go
generated
vendored
Normal file
22
vendor/github.com/pkg/sftp/fuzz.go
generated
vendored
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
// +build gofuzz
|
||||
|
||||
package sftp
|
||||
|
||||
import "bytes"
|
||||
|
||||
type sinkfuzz struct{}
|
||||
|
||||
func (*sinkfuzz) Close() error { return nil }
|
||||
func (*sinkfuzz) Write(p []byte) (int, error) { return len(p), nil }
|
||||
|
||||
var devnull = &sinkfuzz{}
|
||||
|
||||
// To run: go-fuzz-build && go-fuzz
|
||||
func Fuzz(data []byte) int {
|
||||
c, err := NewClientPipe(bytes.NewReader(data), devnull)
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
c.Close()
|
||||
return 1
|
||||
}
|
||||
326
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/attrs.go
generated
vendored
Normal file
326
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/attrs.go
generated
vendored
Normal file
|
|
@ -0,0 +1,326 @@
|
|||
package filexfer
|
||||
|
||||
// Attributes related flags.
|
||||
const (
|
||||
AttrSize = 1 << iota // SSH_FILEXFER_ATTR_SIZE
|
||||
AttrUIDGID // SSH_FILEXFER_ATTR_UIDGID
|
||||
AttrPermissions // SSH_FILEXFER_ATTR_PERMISSIONS
|
||||
AttrACModTime // SSH_FILEXFER_ACMODTIME
|
||||
|
||||
AttrExtended = 1 << 31 // SSH_FILEXFER_ATTR_EXTENDED
|
||||
)
|
||||
|
||||
// Attributes defines the file attributes type defined in draft-ietf-secsh-filexfer-02
|
||||
//
|
||||
// Defined in: https://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#section-5
|
||||
type Attributes struct {
|
||||
Flags uint32
|
||||
|
||||
// AttrSize
|
||||
Size uint64
|
||||
|
||||
// AttrUIDGID
|
||||
UID uint32
|
||||
GID uint32
|
||||
|
||||
// AttrPermissions
|
||||
Permissions FileMode
|
||||
|
||||
// AttrACmodTime
|
||||
ATime uint32
|
||||
MTime uint32
|
||||
|
||||
// AttrExtended
|
||||
ExtendedAttributes []ExtendedAttribute
|
||||
}
|
||||
|
||||
// GetSize returns the Size field and a bool that is true if and only if the value is valid/defined.
|
||||
func (a *Attributes) GetSize() (size uint64, ok bool) {
|
||||
return a.Size, a.Flags&AttrSize != 0
|
||||
}
|
||||
|
||||
// SetSize is a convenience function that sets the Size field,
|
||||
// and marks the field as valid/defined in Flags.
|
||||
func (a *Attributes) SetSize(size uint64) {
|
||||
a.Flags |= AttrSize
|
||||
a.Size = size
|
||||
}
|
||||
|
||||
// GetUIDGID returns the UID and GID fields and a bool that is true if and only if the values are valid/defined.
|
||||
func (a *Attributes) GetUIDGID() (uid, gid uint32, ok bool) {
|
||||
return a.UID, a.GID, a.Flags&AttrUIDGID != 0
|
||||
}
|
||||
|
||||
// SetUIDGID is a convenience function that sets the UID and GID fields,
|
||||
// and marks the fields as valid/defined in Flags.
|
||||
func (a *Attributes) SetUIDGID(uid, gid uint32) {
|
||||
a.Flags |= AttrUIDGID
|
||||
a.UID = uid
|
||||
a.GID = gid
|
||||
}
|
||||
|
||||
// GetPermissions returns the Permissions field and a bool that is true if and only if the value is valid/defined.
|
||||
func (a *Attributes) GetPermissions() (perms FileMode, ok bool) {
|
||||
return a.Permissions, a.Flags&AttrPermissions != 0
|
||||
}
|
||||
|
||||
// SetPermissions is a convenience function that sets the Permissions field,
|
||||
// and marks the field as valid/defined in Flags.
|
||||
func (a *Attributes) SetPermissions(perms FileMode) {
|
||||
a.Flags |= AttrPermissions
|
||||
a.Permissions = perms
|
||||
}
|
||||
|
||||
// GetACModTime returns the ATime and MTime fields and a bool that is true if and only if the values are valid/defined.
|
||||
func (a *Attributes) GetACModTime() (atime, mtime uint32, ok bool) {
|
||||
return a.ATime, a.MTime, a.Flags&AttrACModTime != 0
|
||||
return a.ATime, a.MTime, a.Flags&AttrACModTime != 0
|
||||
}
|
||||
|
||||
// SetACModTime is a convenience function that sets the ATime and MTime fields,
|
||||
// and marks the fields as valid/defined in Flags.
|
||||
func (a *Attributes) SetACModTime(atime, mtime uint32) {
|
||||
a.Flags |= AttrACModTime
|
||||
a.ATime = atime
|
||||
a.MTime = mtime
|
||||
}
|
||||
|
||||
// Len returns the number of bytes a would marshal into.
|
||||
func (a *Attributes) Len() int {
|
||||
length := 4
|
||||
|
||||
if a.Flags&AttrSize != 0 {
|
||||
length += 8
|
||||
}
|
||||
|
||||
if a.Flags&AttrUIDGID != 0 {
|
||||
length += 4 + 4
|
||||
}
|
||||
|
||||
if a.Flags&AttrPermissions != 0 {
|
||||
length += 4
|
||||
}
|
||||
|
||||
if a.Flags&AttrACModTime != 0 {
|
||||
length += 4 + 4
|
||||
}
|
||||
|
||||
if a.Flags&AttrExtended != 0 {
|
||||
length += 4
|
||||
|
||||
for _, ext := range a.ExtendedAttributes {
|
||||
length += ext.Len()
|
||||
}
|
||||
}
|
||||
|
||||
return length
|
||||
}
|
||||
|
||||
// MarshalInto marshals e onto the end of the given Buffer.
|
||||
func (a *Attributes) MarshalInto(b *Buffer) {
|
||||
b.AppendUint32(a.Flags)
|
||||
|
||||
if a.Flags&AttrSize != 0 {
|
||||
b.AppendUint64(a.Size)
|
||||
}
|
||||
|
||||
if a.Flags&AttrUIDGID != 0 {
|
||||
b.AppendUint32(a.UID)
|
||||
b.AppendUint32(a.GID)
|
||||
}
|
||||
|
||||
if a.Flags&AttrPermissions != 0 {
|
||||
b.AppendUint32(uint32(a.Permissions))
|
||||
}
|
||||
|
||||
if a.Flags&AttrACModTime != 0 {
|
||||
b.AppendUint32(a.ATime)
|
||||
b.AppendUint32(a.MTime)
|
||||
}
|
||||
|
||||
if a.Flags&AttrExtended != 0 {
|
||||
b.AppendUint32(uint32(len(a.ExtendedAttributes)))
|
||||
|
||||
for _, ext := range a.ExtendedAttributes {
|
||||
ext.MarshalInto(b)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MarshalBinary returns a as the binary encoding of a.
|
||||
func (a *Attributes) MarshalBinary() ([]byte, error) {
|
||||
buf := NewBuffer(make([]byte, 0, a.Len()))
|
||||
a.MarshalInto(buf)
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
// UnmarshalFrom unmarshals an Attributes from the given Buffer into e.
|
||||
//
|
||||
// NOTE: The values of fields not covered in the a.Flags are explicitly undefined.
|
||||
func (a *Attributes) UnmarshalFrom(b *Buffer) (err error) {
|
||||
flags, err := b.ConsumeUint32()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return a.XXX_UnmarshalByFlags(flags, b)
|
||||
}
|
||||
|
||||
// XXX_UnmarshalByFlags uses the pre-existing a.Flags field to determine which fields to decode.
|
||||
// DO NOT USE THIS: it is an anti-corruption function to implement existing internal usage in pkg/sftp.
|
||||
// This function is not a part of any compatibility promise.
|
||||
func (a *Attributes) XXX_UnmarshalByFlags(flags uint32, b *Buffer) (err error) {
|
||||
a.Flags = flags
|
||||
|
||||
// Short-circuit dummy attributes.
|
||||
if a.Flags == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if a.Flags&AttrSize != 0 {
|
||||
if a.Size, err = b.ConsumeUint64(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if a.Flags&AttrUIDGID != 0 {
|
||||
if a.UID, err = b.ConsumeUint32(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if a.GID, err = b.ConsumeUint32(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if a.Flags&AttrPermissions != 0 {
|
||||
m, err := b.ConsumeUint32()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
a.Permissions = FileMode(m)
|
||||
}
|
||||
|
||||
if a.Flags&AttrACModTime != 0 {
|
||||
if a.ATime, err = b.ConsumeUint32(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if a.MTime, err = b.ConsumeUint32(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if a.Flags&AttrExtended != 0 {
|
||||
count, err := b.ConsumeUint32()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
a.ExtendedAttributes = make([]ExtendedAttribute, count)
|
||||
for i := range a.ExtendedAttributes {
|
||||
a.ExtendedAttributes[i].UnmarshalFrom(b)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmarshalBinary decodes the binary encoding of Attributes into e.
|
||||
func (a *Attributes) UnmarshalBinary(data []byte) error {
|
||||
return a.UnmarshalFrom(NewBuffer(data))
|
||||
}
|
||||
|
||||
// ExtendedAttribute defines the extended file attribute type defined in draft-ietf-secsh-filexfer-02
|
||||
//
|
||||
// Defined in: https://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#section-5
|
||||
type ExtendedAttribute struct {
|
||||
Type string
|
||||
Data string
|
||||
}
|
||||
|
||||
// Len returns the number of bytes e would marshal into.
|
||||
func (e *ExtendedAttribute) Len() int {
|
||||
return 4 + len(e.Type) + 4 + len(e.Data)
|
||||
}
|
||||
|
||||
// MarshalInto marshals e onto the end of the given Buffer.
|
||||
func (e *ExtendedAttribute) MarshalInto(b *Buffer) {
|
||||
b.AppendString(e.Type)
|
||||
b.AppendString(e.Data)
|
||||
}
|
||||
|
||||
// MarshalBinary returns e as the binary encoding of e.
|
||||
func (e *ExtendedAttribute) MarshalBinary() ([]byte, error) {
|
||||
buf := NewBuffer(make([]byte, 0, e.Len()))
|
||||
e.MarshalInto(buf)
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
// UnmarshalFrom unmarshals an ExtendedAattribute from the given Buffer into e.
|
||||
func (e *ExtendedAttribute) UnmarshalFrom(b *Buffer) (err error) {
|
||||
if e.Type, err = b.ConsumeString(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if e.Data, err = b.ConsumeString(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmarshalBinary decodes the binary encoding of ExtendedAttribute into e.
|
||||
func (e *ExtendedAttribute) UnmarshalBinary(data []byte) error {
|
||||
return e.UnmarshalFrom(NewBuffer(data))
|
||||
}
|
||||
|
||||
// NameEntry implements the SSH_FXP_NAME repeated data type from draft-ietf-secsh-filexfer-02
|
||||
//
|
||||
// This type is incompatible with versions 4 or higher.
|
||||
type NameEntry struct {
|
||||
Filename string
|
||||
Longname string
|
||||
Attrs Attributes
|
||||
}
|
||||
|
||||
// Len returns the number of bytes e would marshal into.
|
||||
func (e *NameEntry) Len() int {
|
||||
return 4 + len(e.Filename) + 4 + len(e.Longname) + e.Attrs.Len()
|
||||
}
|
||||
|
||||
// MarshalInto marshals e onto the end of the given Buffer.
|
||||
func (e *NameEntry) MarshalInto(b *Buffer) {
|
||||
b.AppendString(e.Filename)
|
||||
b.AppendString(e.Longname)
|
||||
|
||||
e.Attrs.MarshalInto(b)
|
||||
}
|
||||
|
||||
// MarshalBinary returns e as the binary encoding of e.
|
||||
func (e *NameEntry) MarshalBinary() ([]byte, error) {
|
||||
buf := NewBuffer(make([]byte, 0, e.Len()))
|
||||
e.MarshalInto(buf)
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
// UnmarshalFrom unmarshals an NameEntry from the given Buffer into e.
|
||||
//
|
||||
// NOTE: The values of fields not covered in the a.Flags are explicitly undefined.
|
||||
func (e *NameEntry) UnmarshalFrom(b *Buffer) (err error) {
|
||||
if e.Filename, err = b.ConsumeString(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if e.Longname, err = b.ConsumeString(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return e.Attrs.UnmarshalFrom(b)
|
||||
}
|
||||
|
||||
// UnmarshalBinary decodes the binary encoding of NameEntry into e.
|
||||
func (e *NameEntry) UnmarshalBinary(data []byte) error {
|
||||
return e.UnmarshalFrom(NewBuffer(data))
|
||||
}
|
||||
293
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/buffer.go
generated
vendored
Normal file
293
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/buffer.go
generated
vendored
Normal file
|
|
@ -0,0 +1,293 @@
|
|||
package filexfer
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
)
|
||||
|
||||
// Various encoding errors.
|
||||
var (
|
||||
ErrShortPacket = errors.New("packet too short")
|
||||
ErrLongPacket = errors.New("packet too long")
|
||||
)
|
||||
|
||||
// Buffer wraps up the various encoding details of the SSH format.
|
||||
//
|
||||
// Data types are encoded as per section 4 from https://tools.ietf.org/html/draft-ietf-secsh-architecture-09#page-8
|
||||
type Buffer struct {
|
||||
b []byte
|
||||
off int
|
||||
}
|
||||
|
||||
// NewBuffer creates and initializes a new buffer using buf as its initial contents.
|
||||
// The new buffer takes ownership of buf, and the caller should not use buf after this call.
|
||||
//
|
||||
// In most cases, new(Buffer) (or just declaring a Buffer variable) is sufficient to initialize a Buffer.
|
||||
func NewBuffer(buf []byte) *Buffer {
|
||||
return &Buffer{
|
||||
b: buf,
|
||||
}
|
||||
}
|
||||
|
||||
// NewMarshalBuffer creates a new Buffer ready to start marshaling a Packet into.
|
||||
// It preallocates enough space for uint32(length), uint8(type), uint32(request-id) and size more bytes.
|
||||
func NewMarshalBuffer(size int) *Buffer {
|
||||
return NewBuffer(make([]byte, 4+1+4+size))
|
||||
}
|
||||
|
||||
// Bytes returns a slice of length b.Len() holding the unconsumed bytes in the Buffer.
|
||||
// The slice is valid for use only until the next buffer modification
|
||||
// (that is, only until the next call to an Append or Consume method).
|
||||
func (b *Buffer) Bytes() []byte {
|
||||
return b.b[b.off:]
|
||||
}
|
||||
|
||||
// Len returns the number of unconsumed bytes in the buffer.
|
||||
func (b *Buffer) Len() int { return len(b.b) - b.off }
|
||||
|
||||
// Cap returns the capacity of the buffer’s underlying byte slice,
|
||||
// that is, the total space allocated for the buffer’s data.
|
||||
func (b *Buffer) Cap() int { return cap(b.b) }
|
||||
|
||||
// Reset resets the buffer to be empty, but it retains the underlying storage for use by future Appends.
|
||||
func (b *Buffer) Reset() {
|
||||
b.b = b.b[:0]
|
||||
b.off = 0
|
||||
}
|
||||
|
||||
// StartPacket resets and initializes the buffer to be ready to start marshaling a packet into.
|
||||
// It truncates the buffer, reserves space for uint32(length), then appends the given packetType and requestID.
|
||||
func (b *Buffer) StartPacket(packetType PacketType, requestID uint32) {
|
||||
b.b, b.off = append(b.b[:0], make([]byte, 4)...), 0
|
||||
|
||||
b.AppendUint8(uint8(packetType))
|
||||
b.AppendUint32(requestID)
|
||||
}
|
||||
|
||||
// Packet finalizes the packet started from StartPacket.
|
||||
// It is expected that this will end the ownership of the underlying byte-slice,
|
||||
// and so the returned byte-slices may be reused the same as any other byte-slice,
|
||||
// the caller should not use this buffer after this call.
|
||||
//
|
||||
// It writes the packet body length into the first four bytes of the buffer in network byte order (big endian).
|
||||
// The packet body length is the length of this buffer less the 4-byte length itself, plus the length of payload.
|
||||
//
|
||||
// It is assumed that no Consume methods have been called on this buffer,
|
||||
// and so it returns the whole underlying slice.
|
||||
func (b *Buffer) Packet(payload []byte) (header, payloadPassThru []byte, err error) {
|
||||
b.PutLength(len(b.b) - 4 + len(payload))
|
||||
|
||||
return b.b, payload, nil
|
||||
}
|
||||
|
||||
// ConsumeUint8 consumes a single byte from the buffer.
|
||||
// If the buffer does not have enough data, it will return ErrShortPacket.
|
||||
func (b *Buffer) ConsumeUint8() (uint8, error) {
|
||||
if b.Len() < 1 {
|
||||
return 0, ErrShortPacket
|
||||
}
|
||||
|
||||
var v uint8
|
||||
v, b.off = b.b[b.off], b.off+1
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// AppendUint8 appends a single byte into the buffer.
|
||||
func (b *Buffer) AppendUint8(v uint8) {
|
||||
b.b = append(b.b, v)
|
||||
}
|
||||
|
||||
// ConsumeBool consumes a single byte from the buffer, and returns true if that byte is non-zero.
|
||||
// If the buffer does not have enough data, it will return ErrShortPacket.
|
||||
func (b *Buffer) ConsumeBool() (bool, error) {
|
||||
v, err := b.ConsumeUint8()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return v != 0, nil
|
||||
}
|
||||
|
||||
// AppendBool appends a single bool into the buffer.
|
||||
// It encodes it as a single byte, with false as 0, and true as 1.
|
||||
func (b *Buffer) AppendBool(v bool) {
|
||||
if v {
|
||||
b.AppendUint8(1)
|
||||
} else {
|
||||
b.AppendUint8(0)
|
||||
}
|
||||
}
|
||||
|
||||
// ConsumeUint16 consumes a single uint16 from the buffer, in network byte order (big-endian).
|
||||
// If the buffer does not have enough data, it will return ErrShortPacket.
|
||||
func (b *Buffer) ConsumeUint16() (uint16, error) {
|
||||
if b.Len() < 2 {
|
||||
return 0, ErrShortPacket
|
||||
}
|
||||
|
||||
v := binary.BigEndian.Uint16(b.b[b.off:])
|
||||
b.off += 2
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// AppendUint16 appends single uint16 into the buffer, in network byte order (big-endian).
|
||||
func (b *Buffer) AppendUint16(v uint16) {
|
||||
b.b = append(b.b,
|
||||
byte(v>>8),
|
||||
byte(v>>0),
|
||||
)
|
||||
}
|
||||
|
||||
// unmarshalUint32 is used internally to read the packet length.
|
||||
// It is unsafe, and so not exported.
|
||||
// Even within this package, its use should be avoided.
|
||||
func unmarshalUint32(b []byte) uint32 {
|
||||
return binary.BigEndian.Uint32(b[:4])
|
||||
}
|
||||
|
||||
// ConsumeUint32 consumes a single uint32 from the buffer, in network byte order (big-endian).
|
||||
// If the buffer does not have enough data, it will return ErrShortPacket.
|
||||
func (b *Buffer) ConsumeUint32() (uint32, error) {
|
||||
if b.Len() < 4 {
|
||||
return 0, ErrShortPacket
|
||||
}
|
||||
|
||||
v := binary.BigEndian.Uint32(b.b[b.off:])
|
||||
b.off += 4
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// AppendUint32 appends a single uint32 into the buffer, in network byte order (big-endian).
|
||||
func (b *Buffer) AppendUint32(v uint32) {
|
||||
b.b = append(b.b,
|
||||
byte(v>>24),
|
||||
byte(v>>16),
|
||||
byte(v>>8),
|
||||
byte(v>>0),
|
||||
)
|
||||
}
|
||||
|
||||
// ConsumeUint64 consumes a single uint64 from the buffer, in network byte order (big-endian).
|
||||
// If the buffer does not have enough data, it will return ErrShortPacket.
|
||||
func (b *Buffer) ConsumeUint64() (uint64, error) {
|
||||
if b.Len() < 8 {
|
||||
return 0, ErrShortPacket
|
||||
}
|
||||
|
||||
v := binary.BigEndian.Uint64(b.b[b.off:])
|
||||
b.off += 8
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// AppendUint64 appends a single uint64 into the buffer, in network byte order (big-endian).
|
||||
func (b *Buffer) AppendUint64(v uint64) {
|
||||
b.b = append(b.b,
|
||||
byte(v>>56),
|
||||
byte(v>>48),
|
||||
byte(v>>40),
|
||||
byte(v>>32),
|
||||
byte(v>>24),
|
||||
byte(v>>16),
|
||||
byte(v>>8),
|
||||
byte(v>>0),
|
||||
)
|
||||
}
|
||||
|
||||
// ConsumeInt64 consumes a single int64 from the buffer, in network byte order (big-endian) with two’s complement.
|
||||
// If the buffer does not have enough data, it will return ErrShortPacket.
|
||||
func (b *Buffer) ConsumeInt64() (int64, error) {
|
||||
u, err := b.ConsumeUint64()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return int64(u), err
|
||||
}
|
||||
|
||||
// AppendInt64 appends a single int64 into the buffer, in network byte order (big-endian) with two’s complement.
|
||||
func (b *Buffer) AppendInt64(v int64) {
|
||||
b.AppendUint64(uint64(v))
|
||||
}
|
||||
|
||||
// ConsumeByteSlice consumes a single string of raw binary data from the buffer.
|
||||
// A string is a uint32 length, followed by that number of raw bytes.
|
||||
// If the buffer does not have enough data, or defines a length larger than available, it will return ErrShortPacket.
|
||||
//
|
||||
// The returned slice aliases the buffer contents, and is valid only as long as the buffer is not reused
|
||||
// (that is, only until the next call to Reset, PutLength, StartPacket, or UnmarshalBinary).
|
||||
//
|
||||
// In no case will any Consume calls return overlapping slice aliases,
|
||||
// and Append calls are guaranteed to not disturb this slice alias.
|
||||
func (b *Buffer) ConsumeByteSlice() ([]byte, error) {
|
||||
length, err := b.ConsumeUint32()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if b.Len() < int(length) {
|
||||
return nil, ErrShortPacket
|
||||
}
|
||||
|
||||
v := b.b[b.off:]
|
||||
if len(v) > int(length) {
|
||||
v = v[:length:length]
|
||||
}
|
||||
b.off += int(length)
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// AppendByteSlice appends a single string of raw binary data into the buffer.
|
||||
// A string is a uint32 length, followed by that number of raw bytes.
|
||||
func (b *Buffer) AppendByteSlice(v []byte) {
|
||||
b.AppendUint32(uint32(len(v)))
|
||||
b.b = append(b.b, v...)
|
||||
}
|
||||
|
||||
// ConsumeString consumes a single string of binary data from the buffer.
|
||||
// A string is a uint32 length, followed by that number of raw bytes.
|
||||
// If the buffer does not have enough data, or defines a length larger than available, it will return ErrShortPacket.
|
||||
//
|
||||
// NOTE: Go implicitly assumes that strings contain UTF-8 encoded data.
|
||||
// All caveats on using arbitrary binary data in Go strings applies.
|
||||
func (b *Buffer) ConsumeString() (string, error) {
|
||||
v, err := b.ConsumeByteSlice()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return string(v), nil
|
||||
}
|
||||
|
||||
// AppendString appends a single string of binary data into the buffer.
|
||||
// A string is a uint32 length, followed by that number of raw bytes.
|
||||
func (b *Buffer) AppendString(v string) {
|
||||
b.AppendByteSlice([]byte(v))
|
||||
}
|
||||
|
||||
// PutLength writes the given size into the first four bytes of the buffer in network byte order (big endian).
|
||||
func (b *Buffer) PutLength(size int) {
|
||||
if len(b.b) < 4 {
|
||||
b.b = append(b.b, make([]byte, 4-len(b.b))...)
|
||||
}
|
||||
|
||||
binary.BigEndian.PutUint32(b.b, uint32(size))
|
||||
}
|
||||
|
||||
// MarshalBinary returns a clone of the full internal buffer.
|
||||
func (b *Buffer) MarshalBinary() ([]byte, error) {
|
||||
clone := make([]byte, len(b.b))
|
||||
n := copy(clone, b.b)
|
||||
return clone[:n], nil
|
||||
}
|
||||
|
||||
// UnmarshalBinary sets the internal buffer of b to be a clone of data, and zeros the internal offset.
|
||||
func (b *Buffer) UnmarshalBinary(data []byte) error {
|
||||
if grow := len(data) - len(b.b); grow > 0 {
|
||||
b.b = append(b.b, make([]byte, grow)...)
|
||||
}
|
||||
|
||||
n := copy(b.b, data)
|
||||
b.b = b.b[:n]
|
||||
b.off = 0
|
||||
return nil
|
||||
}
|
||||
142
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/extended_packets.go
generated
vendored
Normal file
142
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/extended_packets.go
generated
vendored
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
package filexfer
|
||||
|
||||
import (
|
||||
"encoding"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// ExtendedData aliases the untyped interface composition of encoding.BinaryMarshaler and encoding.BinaryUnmarshaler.
|
||||
type ExtendedData = interface {
|
||||
encoding.BinaryMarshaler
|
||||
encoding.BinaryUnmarshaler
|
||||
}
|
||||
|
||||
// ExtendedDataConstructor defines a function that returns a new(ArbitraryExtendedPacket).
|
||||
type ExtendedDataConstructor func() ExtendedData
|
||||
|
||||
var extendedPacketTypes = struct {
|
||||
mu sync.RWMutex
|
||||
constructors map[string]ExtendedDataConstructor
|
||||
}{
|
||||
constructors: make(map[string]ExtendedDataConstructor),
|
||||
}
|
||||
|
||||
// RegisterExtendedPacketType defines a specific ExtendedDataConstructor for the given extension string.
|
||||
func RegisterExtendedPacketType(extension string, constructor ExtendedDataConstructor) {
|
||||
extendedPacketTypes.mu.Lock()
|
||||
defer extendedPacketTypes.mu.Unlock()
|
||||
|
||||
if _, exist := extendedPacketTypes.constructors[extension]; exist {
|
||||
panic("encoding/ssh/filexfer: multiple registration of extended packet type " + extension)
|
||||
}
|
||||
|
||||
extendedPacketTypes.constructors[extension] = constructor
|
||||
}
|
||||
|
||||
func newExtendedPacket(extension string) ExtendedData {
|
||||
extendedPacketTypes.mu.RLock()
|
||||
defer extendedPacketTypes.mu.RUnlock()
|
||||
|
||||
if f := extendedPacketTypes.constructors[extension]; f != nil {
|
||||
return f()
|
||||
}
|
||||
|
||||
return new(Buffer)
|
||||
}
|
||||
|
||||
// ExtendedPacket defines the SSH_FXP_CLOSE packet.
|
||||
type ExtendedPacket struct {
|
||||
ExtendedRequest string
|
||||
|
||||
Data ExtendedData
|
||||
}
|
||||
|
||||
// Type returns the SSH_FXP_xy value associated with this packet type.
|
||||
func (p *ExtendedPacket) Type() PacketType {
|
||||
return PacketTypeExtended
|
||||
}
|
||||
|
||||
// MarshalPacket returns p as a two-part binary encoding of p.
|
||||
//
|
||||
// The Data is marshaled into binary, and returned as the payload.
|
||||
func (p *ExtendedPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
||||
buf := NewBuffer(b)
|
||||
if buf.Cap() < 9 {
|
||||
size := 4 + len(p.ExtendedRequest) // string(extended-request)
|
||||
buf = NewMarshalBuffer(size)
|
||||
}
|
||||
|
||||
buf.StartPacket(PacketTypeExtended, reqid)
|
||||
buf.AppendString(p.ExtendedRequest)
|
||||
|
||||
if p.Data != nil {
|
||||
payload, err = p.Data.MarshalBinary()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return buf.Packet(payload)
|
||||
}
|
||||
|
||||
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
||||
// It is assumed that the uint32(request-id) has already been consumed.
|
||||
//
|
||||
// If p.Data is nil, and the extension has been registered, a new type will be made from the registration.
|
||||
// If the extension has not been registered, then a new Buffer will be allocated.
|
||||
// Then the request-specific-data will be unmarshaled from the rest of the buffer.
|
||||
func (p *ExtendedPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
if p.ExtendedRequest, err = buf.ConsumeString(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if p.Data == nil {
|
||||
p.Data = newExtendedPacket(p.ExtendedRequest)
|
||||
}
|
||||
|
||||
return p.Data.UnmarshalBinary(buf.Bytes())
|
||||
}
|
||||
|
||||
// ExtendedReplyPacket defines the SSH_FXP_CLOSE packet.
|
||||
type ExtendedReplyPacket struct {
|
||||
Data ExtendedData
|
||||
}
|
||||
|
||||
// Type returns the SSH_FXP_xy value associated with this packet type.
|
||||
func (p *ExtendedReplyPacket) Type() PacketType {
|
||||
return PacketTypeExtendedReply
|
||||
}
|
||||
|
||||
// MarshalPacket returns p as a two-part binary encoding of p.
|
||||
//
|
||||
// The Data is marshaled into binary, and returned as the payload.
|
||||
func (p *ExtendedReplyPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
||||
buf := NewBuffer(b)
|
||||
if buf.Cap() < 9 {
|
||||
buf = NewMarshalBuffer(0)
|
||||
}
|
||||
|
||||
buf.StartPacket(PacketTypeExtendedReply, reqid)
|
||||
|
||||
if p.Data != nil {
|
||||
payload, err = p.Data.MarshalBinary()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return buf.Packet(payload)
|
||||
}
|
||||
|
||||
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
||||
// It is assumed that the uint32(request-id) has already been consumed.
|
||||
//
|
||||
// If p.Data is nil, and there is request-specific-data,
|
||||
// then the request-specific-data will be wrapped in a Buffer and assigned to p.Data.
|
||||
func (p *ExtendedReplyPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
if p.Data == nil {
|
||||
p.Data = new(Buffer)
|
||||
}
|
||||
|
||||
return p.Data.UnmarshalBinary(buf.Bytes())
|
||||
}
|
||||
46
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/extensions.go
generated
vendored
Normal file
46
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/extensions.go
generated
vendored
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
package filexfer
|
||||
|
||||
// ExtensionPair defines the extension-pair type defined in draft-ietf-secsh-filexfer-13.
|
||||
// This type is backwards-compatible with how draft-ietf-secsh-filexfer-02 defines extensions.
|
||||
//
|
||||
// Defined in: https://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-4.2
|
||||
type ExtensionPair struct {
|
||||
Name string
|
||||
Data string
|
||||
}
|
||||
|
||||
// Len returns the number of bytes e would marshal into.
|
||||
func (e *ExtensionPair) Len() int {
|
||||
return 4 + len(e.Name) + 4 + len(e.Data)
|
||||
}
|
||||
|
||||
// MarshalInto marshals e onto the end of the given Buffer.
|
||||
func (e *ExtensionPair) MarshalInto(buf *Buffer) {
|
||||
buf.AppendString(e.Name)
|
||||
buf.AppendString(e.Data)
|
||||
}
|
||||
|
||||
// MarshalBinary returns e as the binary encoding of e.
|
||||
func (e *ExtensionPair) MarshalBinary() ([]byte, error) {
|
||||
buf := NewBuffer(make([]byte, 0, e.Len()))
|
||||
e.MarshalInto(buf)
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
// UnmarshalFrom unmarshals an ExtensionPair from the given Buffer into e.
|
||||
func (e *ExtensionPair) UnmarshalFrom(buf *Buffer) (err error) {
|
||||
if e.Name, err = buf.ConsumeString(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if e.Data, err = buf.ConsumeString(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmarshalBinary decodes the binary encoding of ExtensionPair into e.
|
||||
func (e *ExtensionPair) UnmarshalBinary(data []byte) error {
|
||||
return e.UnmarshalFrom(NewBuffer(data))
|
||||
}
|
||||
54
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/filexfer.go
generated
vendored
Normal file
54
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/filexfer.go
generated
vendored
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
// Package filexfer implements the wire encoding for secsh-filexfer as described in https://tools.ietf.org/html/draft-ietf-secsh-filexfer-02
|
||||
package filexfer
|
||||
|
||||
// PacketMarshaller narrowly defines packets that will only be transmitted.
|
||||
//
|
||||
// ExtendedPacket types will often only implement this interface,
|
||||
// since decoding the whole packet body of an ExtendedPacket can only be done dependent on the ExtendedRequest field.
|
||||
type PacketMarshaller interface {
|
||||
// MarshalPacket is the primary intended way to encode a packet.
|
||||
// The request-id for the packet is set from reqid.
|
||||
//
|
||||
// An optional buffer may be given in b.
|
||||
// If the buffer has a minimum capacity, it shall be truncated and used to marshal the header into.
|
||||
// The minimum capacity for the packet must be a constant expression, and should be at least 9.
|
||||
//
|
||||
// It shall return the main body of the encoded packet in header,
|
||||
// and may optionally return an additional payload to be written immediately after the header.
|
||||
//
|
||||
// It shall encode in the first 4-bytes of the header the proper length of the rest of the header+payload.
|
||||
MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error)
|
||||
}
|
||||
|
||||
// Packet defines the behavior of a full generic SFTP packet.
|
||||
//
|
||||
// InitPacket, and VersionPacket are not generic SFTP packets, and instead implement (Un)MarshalBinary.
|
||||
//
|
||||
// ExtendedPacket types should not iplement this interface,
|
||||
// since decoding the whole packet body of an ExtendedPacket can only be done dependent on the ExtendedRequest field.
|
||||
type Packet interface {
|
||||
PacketMarshaller
|
||||
|
||||
// Type returns the SSH_FXP_xy value associated with the specific packet.
|
||||
Type() PacketType
|
||||
|
||||
// UnmarshalPacketBody decodes a packet body from the given Buffer.
|
||||
// It is assumed that the common header values of the length, type and request-id have already been consumed.
|
||||
//
|
||||
// Implementations should not alias the given Buffer,
|
||||
// instead they can consider prepopulating an internal buffer as a hint,
|
||||
// and copying into that buffer if it has sufficient length.
|
||||
UnmarshalPacketBody(buf *Buffer) error
|
||||
}
|
||||
|
||||
// ComposePacket converts returns from MarshalPacket into an equivalent call to MarshalBinary.
|
||||
func ComposePacket(header, payload []byte, err error) ([]byte, error) {
|
||||
return append(header, payload...), err
|
||||
}
|
||||
|
||||
// Default length values,
|
||||
// Defined in draft-ietf-secsh-filexfer-02 section 3.
|
||||
const (
|
||||
DefaultMaxPacketLength = 34000
|
||||
DefaultMaxDataLength = 32768
|
||||
)
|
||||
147
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/fx.go
generated
vendored
Normal file
147
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/fx.go
generated
vendored
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
package filexfer
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Status defines the SFTP error codes used in SSH_FXP_STATUS response packets.
|
||||
type Status uint32
|
||||
|
||||
// Defines the various SSH_FX_* values.
|
||||
const (
|
||||
// see draft-ietf-secsh-filexfer-02
|
||||
// https://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#section-7
|
||||
StatusOK = Status(iota)
|
||||
StatusEOF
|
||||
StatusNoSuchFile
|
||||
StatusPermissionDenied
|
||||
StatusFailure
|
||||
StatusBadMessage
|
||||
StatusNoConnection
|
||||
StatusConnectionLost
|
||||
StatusOPUnsupported
|
||||
|
||||
// https://tools.ietf.org/html/draft-ietf-secsh-filexfer-03#section-7
|
||||
StatusV4InvalidHandle
|
||||
StatusV4NoSuchPath
|
||||
StatusV4FileAlreadyExists
|
||||
StatusV4WriteProtect
|
||||
|
||||
// https://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-7
|
||||
StatusV4NoMedia
|
||||
|
||||
// https://tools.ietf.org/html/draft-ietf-secsh-filexfer-05#section-7
|
||||
StatusV5NoSpaceOnFilesystem
|
||||
StatusV5QuotaExceeded
|
||||
StatusV5UnknownPrincipal
|
||||
StatusV5LockConflict
|
||||
|
||||
// https://tools.ietf.org/html/draft-ietf-secsh-filexfer-06#section-8
|
||||
StatusV6DirNotEmpty
|
||||
StatusV6NotADirectory
|
||||
StatusV6InvalidFilename
|
||||
StatusV6LinkLoop
|
||||
|
||||
// https://tools.ietf.org/html/draft-ietf-secsh-filexfer-07#section-8
|
||||
StatusV6CannotDelete
|
||||
StatusV6InvalidParameter
|
||||
StatusV6FileIsADirectory
|
||||
StatusV6ByteRangeLockConflict
|
||||
StatusV6ByteRangeLockRefused
|
||||
StatusV6DeletePending
|
||||
|
||||
// https://tools.ietf.org/html/draft-ietf-secsh-filexfer-08#section-8.1
|
||||
StatusV6FileCorrupt
|
||||
|
||||
// https://tools.ietf.org/html/draft-ietf-secsh-filexfer-10#section-9.1
|
||||
StatusV6OwnerInvalid
|
||||
StatusV6GroupInvalid
|
||||
|
||||
// https://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-9.1
|
||||
StatusV6NoMatchingByteRangeLock
|
||||
)
|
||||
|
||||
func (s Status) Error() string {
|
||||
return s.String()
|
||||
}
|
||||
|
||||
// Is returns true if the target is the same Status code,
|
||||
// or target is a StatusPacket with the same Status code.
|
||||
func (s Status) Is(target error) bool {
|
||||
if target, ok := target.(*StatusPacket); ok {
|
||||
return target.StatusCode == s
|
||||
}
|
||||
|
||||
return s == target
|
||||
}
|
||||
|
||||
func (s Status) String() string {
|
||||
switch s {
|
||||
case StatusOK:
|
||||
return "SSH_FX_OK"
|
||||
case StatusEOF:
|
||||
return "SSH_FX_EOF"
|
||||
case StatusNoSuchFile:
|
||||
return "SSH_FX_NO_SUCH_FILE"
|
||||
case StatusPermissionDenied:
|
||||
return "SSH_FX_PERMISSION_DENIED"
|
||||
case StatusFailure:
|
||||
return "SSH_FX_FAILURE"
|
||||
case StatusBadMessage:
|
||||
return "SSH_FX_BAD_MESSAGE"
|
||||
case StatusNoConnection:
|
||||
return "SSH_FX_NO_CONNECTION"
|
||||
case StatusConnectionLost:
|
||||
return "SSH_FX_CONNECTION_LOST"
|
||||
case StatusOPUnsupported:
|
||||
return "SSH_FX_OP_UNSUPPORTED"
|
||||
case StatusV4InvalidHandle:
|
||||
return "SSH_FX_INVALID_HANDLE"
|
||||
case StatusV4NoSuchPath:
|
||||
return "SSH_FX_NO_SUCH_PATH"
|
||||
case StatusV4FileAlreadyExists:
|
||||
return "SSH_FX_FILE_ALREADY_EXISTS"
|
||||
case StatusV4WriteProtect:
|
||||
return "SSH_FX_WRITE_PROTECT"
|
||||
case StatusV4NoMedia:
|
||||
return "SSH_FX_NO_MEDIA"
|
||||
case StatusV5NoSpaceOnFilesystem:
|
||||
return "SSH_FX_NO_SPACE_ON_FILESYSTEM"
|
||||
case StatusV5QuotaExceeded:
|
||||
return "SSH_FX_QUOTA_EXCEEDED"
|
||||
case StatusV5UnknownPrincipal:
|
||||
return "SSH_FX_UNKNOWN_PRINCIPAL"
|
||||
case StatusV5LockConflict:
|
||||
return "SSH_FX_LOCK_CONFLICT"
|
||||
case StatusV6DirNotEmpty:
|
||||
return "SSH_FX_DIR_NOT_EMPTY"
|
||||
case StatusV6NotADirectory:
|
||||
return "SSH_FX_NOT_A_DIRECTORY"
|
||||
case StatusV6InvalidFilename:
|
||||
return "SSH_FX_INVALID_FILENAME"
|
||||
case StatusV6LinkLoop:
|
||||
return "SSH_FX_LINK_LOOP"
|
||||
case StatusV6CannotDelete:
|
||||
return "SSH_FX_CANNOT_DELETE"
|
||||
case StatusV6InvalidParameter:
|
||||
return "SSH_FX_INVALID_PARAMETER"
|
||||
case StatusV6FileIsADirectory:
|
||||
return "SSH_FX_FILE_IS_A_DIRECTORY"
|
||||
case StatusV6ByteRangeLockConflict:
|
||||
return "SSH_FX_BYTE_RANGE_LOCK_CONFLICT"
|
||||
case StatusV6ByteRangeLockRefused:
|
||||
return "SSH_FX_BYTE_RANGE_LOCK_REFUSED"
|
||||
case StatusV6DeletePending:
|
||||
return "SSH_FX_DELETE_PENDING"
|
||||
case StatusV6FileCorrupt:
|
||||
return "SSH_FX_FILE_CORRUPT"
|
||||
case StatusV6OwnerInvalid:
|
||||
return "SSH_FX_OWNER_INVALID"
|
||||
case StatusV6GroupInvalid:
|
||||
return "SSH_FX_GROUP_INVALID"
|
||||
case StatusV6NoMatchingByteRangeLock:
|
||||
return "SSH_FX_NO_MATCHING_BYTE_RANGE_LOCK"
|
||||
default:
|
||||
return fmt.Sprintf("SSH_FX_UNKNOWN(%d)", s)
|
||||
}
|
||||
}
|
||||
124
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/fxp.go
generated
vendored
Normal file
124
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/fxp.go
generated
vendored
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
package filexfer
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// PacketType defines the various SFTP packet types.
|
||||
type PacketType uint8
|
||||
|
||||
// Request packet types.
|
||||
const (
|
||||
// https://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#section-3
|
||||
PacketTypeInit = PacketType(iota + 1)
|
||||
PacketTypeVersion
|
||||
PacketTypeOpen
|
||||
PacketTypeClose
|
||||
PacketTypeRead
|
||||
PacketTypeWrite
|
||||
PacketTypeLStat
|
||||
PacketTypeFStat
|
||||
PacketTypeSetstat
|
||||
PacketTypeFSetstat
|
||||
PacketTypeOpenDir
|
||||
PacketTypeReadDir
|
||||
PacketTypeRemove
|
||||
PacketTypeMkdir
|
||||
PacketTypeRmdir
|
||||
PacketTypeRealPath
|
||||
PacketTypeStat
|
||||
PacketTypeRename
|
||||
PacketTypeReadLink
|
||||
PacketTypeSymlink
|
||||
|
||||
// https://tools.ietf.org/html/draft-ietf-secsh-filexfer-07#section-3.3
|
||||
PacketTypeV6Link
|
||||
|
||||
// https://tools.ietf.org/html/draft-ietf-secsh-filexfer-08#section-3.3
|
||||
PacketTypeV6Block
|
||||
PacketTypeV6Unblock
|
||||
)
|
||||
|
||||
// Response packet types.
|
||||
const (
|
||||
// https://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#section-3
|
||||
PacketTypeStatus = PacketType(iota + 101)
|
||||
PacketTypeHandle
|
||||
PacketTypeData
|
||||
PacketTypeName
|
||||
PacketTypeAttrs
|
||||
)
|
||||
|
||||
// Extended packet types.
|
||||
const (
|
||||
// https://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#section-3
|
||||
PacketTypeExtended = PacketType(iota + 200)
|
||||
PacketTypeExtendedReply
|
||||
)
|
||||
|
||||
func (f PacketType) String() string {
|
||||
switch f {
|
||||
case PacketTypeInit:
|
||||
return "SSH_FXP_INIT"
|
||||
case PacketTypeVersion:
|
||||
return "SSH_FXP_VERSION"
|
||||
case PacketTypeOpen:
|
||||
return "SSH_FXP_OPEN"
|
||||
case PacketTypeClose:
|
||||
return "SSH_FXP_CLOSE"
|
||||
case PacketTypeRead:
|
||||
return "SSH_FXP_READ"
|
||||
case PacketTypeWrite:
|
||||
return "SSH_FXP_WRITE"
|
||||
case PacketTypeLStat:
|
||||
return "SSH_FXP_LSTAT"
|
||||
case PacketTypeFStat:
|
||||
return "SSH_FXP_FSTAT"
|
||||
case PacketTypeSetstat:
|
||||
return "SSH_FXP_SETSTAT"
|
||||
case PacketTypeFSetstat:
|
||||
return "SSH_FXP_FSETSTAT"
|
||||
case PacketTypeOpenDir:
|
||||
return "SSH_FXP_OPENDIR"
|
||||
case PacketTypeReadDir:
|
||||
return "SSH_FXP_READDIR"
|
||||
case PacketTypeRemove:
|
||||
return "SSH_FXP_REMOVE"
|
||||
case PacketTypeMkdir:
|
||||
return "SSH_FXP_MKDIR"
|
||||
case PacketTypeRmdir:
|
||||
return "SSH_FXP_RMDIR"
|
||||
case PacketTypeRealPath:
|
||||
return "SSH_FXP_REALPATH"
|
||||
case PacketTypeStat:
|
||||
return "SSH_FXP_STAT"
|
||||
case PacketTypeRename:
|
||||
return "SSH_FXP_RENAME"
|
||||
case PacketTypeReadLink:
|
||||
return "SSH_FXP_READLINK"
|
||||
case PacketTypeSymlink:
|
||||
return "SSH_FXP_SYMLINK"
|
||||
case PacketTypeV6Link:
|
||||
return "SSH_FXP_LINK"
|
||||
case PacketTypeV6Block:
|
||||
return "SSH_FXP_BLOCK"
|
||||
case PacketTypeV6Unblock:
|
||||
return "SSH_FXP_UNBLOCK"
|
||||
case PacketTypeStatus:
|
||||
return "SSH_FXP_STATUS"
|
||||
case PacketTypeHandle:
|
||||
return "SSH_FXP_HANDLE"
|
||||
case PacketTypeData:
|
||||
return "SSH_FXP_DATA"
|
||||
case PacketTypeName:
|
||||
return "SSH_FXP_NAME"
|
||||
case PacketTypeAttrs:
|
||||
return "SSH_FXP_ATTRS"
|
||||
case PacketTypeExtended:
|
||||
return "SSH_FXP_EXTENDED"
|
||||
case PacketTypeExtendedReply:
|
||||
return "SSH_FXP_EXTENDED_REPLY"
|
||||
default:
|
||||
return fmt.Sprintf("SSH_FXP_UNKNOWN(%d)", f)
|
||||
}
|
||||
}
|
||||
249
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/handle_packets.go
generated
vendored
Normal file
249
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/handle_packets.go
generated
vendored
Normal file
|
|
@ -0,0 +1,249 @@
|
|||
package filexfer
|
||||
|
||||
// ClosePacket defines the SSH_FXP_CLOSE packet.
|
||||
type ClosePacket struct {
|
||||
Handle string
|
||||
}
|
||||
|
||||
// Type returns the SSH_FXP_xy value associated with this packet type.
|
||||
func (p *ClosePacket) Type() PacketType {
|
||||
return PacketTypeClose
|
||||
}
|
||||
|
||||
// MarshalPacket returns p as a two-part binary encoding of p.
|
||||
func (p *ClosePacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
||||
buf := NewBuffer(b)
|
||||
if buf.Cap() < 9 {
|
||||
size := 4 + len(p.Handle) // string(handle)
|
||||
buf = NewMarshalBuffer(size)
|
||||
}
|
||||
|
||||
buf.StartPacket(PacketTypeClose, reqid)
|
||||
buf.AppendString(p.Handle)
|
||||
|
||||
return buf.Packet(payload)
|
||||
}
|
||||
|
||||
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
||||
// It is assumed that the uint32(request-id) has already been consumed.
|
||||
func (p *ClosePacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
if p.Handle, err = buf.ConsumeString(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReadPacket defines the SSH_FXP_READ packet.
|
||||
type ReadPacket struct {
|
||||
Handle string
|
||||
Offset uint64
|
||||
Len uint32
|
||||
}
|
||||
|
||||
// Type returns the SSH_FXP_xy value associated with this packet type.
|
||||
func (p *ReadPacket) Type() PacketType {
|
||||
return PacketTypeRead
|
||||
}
|
||||
|
||||
// MarshalPacket returns p as a two-part binary encoding of p.
|
||||
func (p *ReadPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
||||
buf := NewBuffer(b)
|
||||
if buf.Cap() < 9 {
|
||||
// string(handle) + uint64(offset) + uint32(len)
|
||||
size := 4 + len(p.Handle) + 8 + 4
|
||||
buf = NewMarshalBuffer(size)
|
||||
}
|
||||
|
||||
buf.StartPacket(PacketTypeRead, reqid)
|
||||
buf.AppendString(p.Handle)
|
||||
buf.AppendUint64(p.Offset)
|
||||
buf.AppendUint32(p.Len)
|
||||
|
||||
return buf.Packet(payload)
|
||||
}
|
||||
|
||||
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
||||
// It is assumed that the uint32(request-id) has already been consumed.
|
||||
func (p *ReadPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
if p.Handle, err = buf.ConsumeString(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if p.Offset, err = buf.ConsumeUint64(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if p.Len, err = buf.ConsumeUint32(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// WritePacket defines the SSH_FXP_WRITE packet.
|
||||
type WritePacket struct {
|
||||
Handle string
|
||||
Offset uint64
|
||||
Data []byte
|
||||
}
|
||||
|
||||
// Type returns the SSH_FXP_xy value associated with this packet type.
|
||||
func (p *WritePacket) Type() PacketType {
|
||||
return PacketTypeWrite
|
||||
}
|
||||
|
||||
// MarshalPacket returns p as a two-part binary encoding of p.
|
||||
func (p *WritePacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
||||
buf := NewBuffer(b)
|
||||
if buf.Cap() < 9 {
|
||||
// string(handle) + uint64(offset) + uint32(len(data)); data content in payload
|
||||
size := 4 + len(p.Handle) + 8 + 4
|
||||
buf = NewMarshalBuffer(size)
|
||||
}
|
||||
|
||||
buf.StartPacket(PacketTypeWrite, reqid)
|
||||
buf.AppendString(p.Handle)
|
||||
buf.AppendUint64(p.Offset)
|
||||
buf.AppendUint32(uint32(len(p.Data)))
|
||||
|
||||
return buf.Packet(p.Data)
|
||||
}
|
||||
|
||||
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
||||
// It is assumed that the uint32(request-id) has already been consumed.
|
||||
//
|
||||
// If p.Data is already populated, and of sufficient length to hold the data,
|
||||
// then this will copy the data into that byte slice.
|
||||
//
|
||||
// If p.Data has a length insufficient to hold the data,
|
||||
// then this will make a new slice of sufficient length, and copy the data into that.
|
||||
//
|
||||
// This means this _does not_ alias any of the data buffer that is passed in.
|
||||
func (p *WritePacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
if p.Handle, err = buf.ConsumeString(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if p.Offset, err = buf.ConsumeUint64(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
data, err := buf.ConsumeByteSlice()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(p.Data) < len(data) {
|
||||
p.Data = make([]byte, len(data))
|
||||
}
|
||||
|
||||
n := copy(p.Data, data)
|
||||
p.Data = p.Data[:n]
|
||||
return nil
|
||||
}
|
||||
|
||||
// FStatPacket defines the SSH_FXP_FSTAT packet.
|
||||
type FStatPacket struct {
|
||||
Handle string
|
||||
}
|
||||
|
||||
// Type returns the SSH_FXP_xy value associated with this packet type.
|
||||
func (p *FStatPacket) Type() PacketType {
|
||||
return PacketTypeFStat
|
||||
}
|
||||
|
||||
// MarshalPacket returns p as a two-part binary encoding of p.
|
||||
func (p *FStatPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
||||
buf := NewBuffer(b)
|
||||
if buf.Cap() < 9 {
|
||||
size := 4 + len(p.Handle) // string(handle)
|
||||
buf = NewMarshalBuffer(size)
|
||||
}
|
||||
|
||||
buf.StartPacket(PacketTypeFStat, reqid)
|
||||
buf.AppendString(p.Handle)
|
||||
|
||||
return buf.Packet(payload)
|
||||
}
|
||||
|
||||
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
||||
// It is assumed that the uint32(request-id) has already been consumed.
|
||||
func (p *FStatPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
if p.Handle, err = buf.ConsumeString(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// FSetstatPacket defines the SSH_FXP_FSETSTAT packet.
|
||||
type FSetstatPacket struct {
|
||||
Handle string
|
||||
Attrs Attributes
|
||||
}
|
||||
|
||||
// Type returns the SSH_FXP_xy value associated with this packet type.
|
||||
func (p *FSetstatPacket) Type() PacketType {
|
||||
return PacketTypeFSetstat
|
||||
}
|
||||
|
||||
// MarshalPacket returns p as a two-part binary encoding of p.
|
||||
func (p *FSetstatPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
||||
buf := NewBuffer(b)
|
||||
if buf.Cap() < 9 {
|
||||
size := 4 + len(p.Handle) + p.Attrs.Len() // string(handle) + ATTRS(attrs)
|
||||
buf = NewMarshalBuffer(size)
|
||||
}
|
||||
|
||||
buf.StartPacket(PacketTypeFSetstat, reqid)
|
||||
buf.AppendString(p.Handle)
|
||||
|
||||
p.Attrs.MarshalInto(buf)
|
||||
|
||||
return buf.Packet(payload)
|
||||
}
|
||||
|
||||
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
||||
// It is assumed that the uint32(request-id) has already been consumed.
|
||||
func (p *FSetstatPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
if p.Handle, err = buf.ConsumeString(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return p.Attrs.UnmarshalFrom(buf)
|
||||
}
|
||||
|
||||
// ReadDirPacket defines the SSH_FXP_READDIR packet.
|
||||
type ReadDirPacket struct {
|
||||
Handle string
|
||||
}
|
||||
|
||||
// Type returns the SSH_FXP_xy value associated with this packet type.
|
||||
func (p *ReadDirPacket) Type() PacketType {
|
||||
return PacketTypeReadDir
|
||||
}
|
||||
|
||||
// MarshalPacket returns p as a two-part binary encoding of p.
|
||||
func (p *ReadDirPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
||||
buf := NewBuffer(b)
|
||||
if buf.Cap() < 9 {
|
||||
size := 4 + len(p.Handle) // string(handle)
|
||||
buf = NewMarshalBuffer(size)
|
||||
}
|
||||
|
||||
buf.StartPacket(PacketTypeReadDir, reqid)
|
||||
buf.AppendString(p.Handle)
|
||||
|
||||
return buf.Packet(payload)
|
||||
}
|
||||
|
||||
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
||||
// It is assumed that the uint32(request-id) has already been consumed.
|
||||
func (p *ReadDirPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
if p.Handle, err = buf.ConsumeString(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
99
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/init_packets.go
generated
vendored
Normal file
99
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/init_packets.go
generated
vendored
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
package filexfer
|
||||
|
||||
// InitPacket defines the SSH_FXP_INIT packet.
|
||||
type InitPacket struct {
|
||||
Version uint32
|
||||
Extensions []*ExtensionPair
|
||||
}
|
||||
|
||||
// MarshalBinary returns p as the binary encoding of p.
|
||||
func (p *InitPacket) MarshalBinary() ([]byte, error) {
|
||||
size := 1 + 4 // byte(type) + uint32(version)
|
||||
|
||||
for _, ext := range p.Extensions {
|
||||
size += ext.Len()
|
||||
}
|
||||
|
||||
b := NewBuffer(make([]byte, 4, 4+size))
|
||||
b.AppendUint8(uint8(PacketTypeInit))
|
||||
b.AppendUint32(p.Version)
|
||||
|
||||
for _, ext := range p.Extensions {
|
||||
ext.MarshalInto(b)
|
||||
}
|
||||
|
||||
b.PutLength(size)
|
||||
|
||||
return b.Bytes(), nil
|
||||
}
|
||||
|
||||
// UnmarshalBinary unmarshals a full raw packet out of the given data.
|
||||
// It is assumed that the uint32(length) has already been consumed to receive the data.
|
||||
// It is also assumed that the uint8(type) has already been consumed to which packet to unmarshal into.
|
||||
func (p *InitPacket) UnmarshalBinary(data []byte) (err error) {
|
||||
buf := NewBuffer(data)
|
||||
|
||||
if p.Version, err = buf.ConsumeUint32(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for buf.Len() > 0 {
|
||||
var ext ExtensionPair
|
||||
if err := ext.UnmarshalFrom(buf); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p.Extensions = append(p.Extensions, &ext)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// VersionPacket defines the SSH_FXP_VERSION packet.
|
||||
type VersionPacket struct {
|
||||
Version uint32
|
||||
Extensions []*ExtensionPair
|
||||
}
|
||||
|
||||
// MarshalBinary returns p as the binary encoding of p.
|
||||
func (p *VersionPacket) MarshalBinary() ([]byte, error) {
|
||||
size := 1 + 4 // byte(type) + uint32(version)
|
||||
|
||||
for _, ext := range p.Extensions {
|
||||
size += ext.Len()
|
||||
}
|
||||
|
||||
b := NewBuffer(make([]byte, 4, 4+size))
|
||||
b.AppendUint8(uint8(PacketTypeVersion))
|
||||
b.AppendUint32(p.Version)
|
||||
|
||||
for _, ext := range p.Extensions {
|
||||
ext.MarshalInto(b)
|
||||
}
|
||||
|
||||
b.PutLength(size)
|
||||
|
||||
return b.Bytes(), nil
|
||||
}
|
||||
|
||||
// UnmarshalBinary unmarshals a full raw packet out of the given data.
|
||||
// It is assumed that the uint32(length) has already been consumed to receive the data.
|
||||
// It is also assumed that the uint8(type) has already been consumed to which packet to unmarshal into.
|
||||
func (p *VersionPacket) UnmarshalBinary(data []byte) (err error) {
|
||||
buf := NewBuffer(data)
|
||||
|
||||
if p.Version, err = buf.ConsumeUint32(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for buf.Len() > 0 {
|
||||
var ext ExtensionPair
|
||||
if err := ext.UnmarshalFrom(buf); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p.Extensions = append(p.Extensions, &ext)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
89
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/open_packets.go
generated
vendored
Normal file
89
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/open_packets.go
generated
vendored
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
package filexfer
|
||||
|
||||
// SSH_FXF_* flags.
|
||||
const (
|
||||
FlagRead = 1 << iota // SSH_FXF_READ
|
||||
FlagWrite // SSH_FXF_WRITE
|
||||
FlagAppend // SSH_FXF_APPEND
|
||||
FlagCreate // SSH_FXF_CREAT
|
||||
FlagTruncate // SSH_FXF_TRUNC
|
||||
FlagExclusive // SSH_FXF_EXCL
|
||||
)
|
||||
|
||||
// OpenPacket defines the SSH_FXP_OPEN packet.
|
||||
type OpenPacket struct {
|
||||
Filename string
|
||||
PFlags uint32
|
||||
Attrs Attributes
|
||||
}
|
||||
|
||||
// Type returns the SSH_FXP_xy value associated with this packet type.
|
||||
func (p *OpenPacket) Type() PacketType {
|
||||
return PacketTypeOpen
|
||||
}
|
||||
|
||||
// MarshalPacket returns p as a two-part binary encoding of p.
|
||||
func (p *OpenPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
||||
buf := NewBuffer(b)
|
||||
if buf.Cap() < 9 {
|
||||
// string(filename) + uint32(pflags) + ATTRS(attrs)
|
||||
size := 4 + len(p.Filename) + 4 + p.Attrs.Len()
|
||||
buf = NewMarshalBuffer(size)
|
||||
}
|
||||
|
||||
buf.StartPacket(PacketTypeOpen, reqid)
|
||||
buf.AppendString(p.Filename)
|
||||
buf.AppendUint32(p.PFlags)
|
||||
|
||||
p.Attrs.MarshalInto(buf)
|
||||
|
||||
return buf.Packet(payload)
|
||||
}
|
||||
|
||||
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
||||
// It is assumed that the uint32(request-id) has already been consumed.
|
||||
func (p *OpenPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
if p.Filename, err = buf.ConsumeString(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if p.PFlags, err = buf.ConsumeUint32(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return p.Attrs.UnmarshalFrom(buf)
|
||||
}
|
||||
|
||||
// OpenDirPacket defines the SSH_FXP_OPENDIR packet.
|
||||
type OpenDirPacket struct {
|
||||
Path string
|
||||
}
|
||||
|
||||
// Type returns the SSH_FXP_xy value associated with this packet type.
|
||||
func (p *OpenDirPacket) Type() PacketType {
|
||||
return PacketTypeOpenDir
|
||||
}
|
||||
|
||||
// MarshalPacket returns p as a two-part binary encoding of p.
|
||||
func (p *OpenDirPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
||||
buf := NewBuffer(b)
|
||||
if buf.Cap() < 9 {
|
||||
size := 4 + len(p.Path) // string(path)
|
||||
buf = NewMarshalBuffer(size)
|
||||
}
|
||||
|
||||
buf.StartPacket(PacketTypeOpenDir, reqid)
|
||||
buf.AppendString(p.Path)
|
||||
|
||||
return buf.Packet(payload)
|
||||
}
|
||||
|
||||
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
||||
// It is assumed that the uint32(request-id) has already been consumed.
|
||||
func (p *OpenDirPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
if p.Path, err = buf.ConsumeString(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
323
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/packets.go
generated
vendored
Normal file
323
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/packets.go
generated
vendored
Normal file
|
|
@ -0,0 +1,323 @@
|
|||
package filexfer
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
// smallBufferSize is an initial allocation minimal capacity.
|
||||
const smallBufferSize = 64
|
||||
|
||||
func newPacketFromType(typ PacketType) (Packet, error) {
|
||||
switch typ {
|
||||
case PacketTypeOpen:
|
||||
return new(OpenPacket), nil
|
||||
case PacketTypeClose:
|
||||
return new(ClosePacket), nil
|
||||
case PacketTypeRead:
|
||||
return new(ReadPacket), nil
|
||||
case PacketTypeWrite:
|
||||
return new(WritePacket), nil
|
||||
case PacketTypeLStat:
|
||||
return new(LStatPacket), nil
|
||||
case PacketTypeFStat:
|
||||
return new(FStatPacket), nil
|
||||
case PacketTypeSetstat:
|
||||
return new(SetstatPacket), nil
|
||||
case PacketTypeFSetstat:
|
||||
return new(FSetstatPacket), nil
|
||||
case PacketTypeOpenDir:
|
||||
return new(OpenDirPacket), nil
|
||||
case PacketTypeReadDir:
|
||||
return new(ReadDirPacket), nil
|
||||
case PacketTypeRemove:
|
||||
return new(RemovePacket), nil
|
||||
case PacketTypeMkdir:
|
||||
return new(MkdirPacket), nil
|
||||
case PacketTypeRmdir:
|
||||
return new(RmdirPacket), nil
|
||||
case PacketTypeRealPath:
|
||||
return new(RealPathPacket), nil
|
||||
case PacketTypeStat:
|
||||
return new(StatPacket), nil
|
||||
case PacketTypeRename:
|
||||
return new(RenamePacket), nil
|
||||
case PacketTypeReadLink:
|
||||
return new(ReadLinkPacket), nil
|
||||
case PacketTypeSymlink:
|
||||
return new(SymlinkPacket), nil
|
||||
case PacketTypeExtended:
|
||||
return new(ExtendedPacket), nil
|
||||
default:
|
||||
return nil, fmt.Errorf("unexpected request packet type: %v", typ)
|
||||
}
|
||||
}
|
||||
|
||||
// RawPacket implements the general packet format from draft-ietf-secsh-filexfer-02
|
||||
//
|
||||
// RawPacket is intended for use in clients receiving responses,
|
||||
// where a response will be expected to be of a limited number of types,
|
||||
// and unmarshaling unknown/unexpected response packets is unnecessary.
|
||||
//
|
||||
// For servers expecting to receive arbitrary request packet types,
|
||||
// use RequestPacket.
|
||||
//
|
||||
// Defined in https://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#section-3
|
||||
type RawPacket struct {
|
||||
PacketType PacketType
|
||||
RequestID uint32
|
||||
|
||||
Data Buffer
|
||||
}
|
||||
|
||||
// Type returns the Type field defining the SSH_FXP_xy type for this packet.
|
||||
func (p *RawPacket) Type() PacketType {
|
||||
return p.PacketType
|
||||
}
|
||||
|
||||
// Reset clears the pointers and reference-semantic variables of RawPacket,
|
||||
// releasing underlying resources, and making them and the RawPacket suitable to be reused,
|
||||
// so long as no other references have been kept.
|
||||
func (p *RawPacket) Reset() {
|
||||
p.Data = Buffer{}
|
||||
}
|
||||
|
||||
// MarshalPacket returns p as a two-part binary encoding of p.
|
||||
//
|
||||
// The internal p.RequestID is overridden by the reqid argument.
|
||||
func (p *RawPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
||||
buf := NewBuffer(b)
|
||||
if buf.Cap() < 9 {
|
||||
buf = NewMarshalBuffer(0)
|
||||
}
|
||||
|
||||
buf.StartPacket(p.PacketType, reqid)
|
||||
|
||||
return buf.Packet(p.Data.Bytes())
|
||||
}
|
||||
|
||||
// MarshalBinary returns p as the binary encoding of p.
|
||||
//
|
||||
// This is a convenience implementation primarily intended for tests,
|
||||
// because it is inefficient with allocations.
|
||||
func (p *RawPacket) MarshalBinary() ([]byte, error) {
|
||||
return ComposePacket(p.MarshalPacket(p.RequestID, nil))
|
||||
}
|
||||
|
||||
// UnmarshalFrom decodes a RawPacket from the given Buffer into p.
|
||||
//
|
||||
// The Data field will alias the passed in Buffer,
|
||||
// so the buffer passed in should not be reused before RawPacket.Reset().
|
||||
func (p *RawPacket) UnmarshalFrom(buf *Buffer) error {
|
||||
typ, err := buf.ConsumeUint8()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p.PacketType = PacketType(typ)
|
||||
|
||||
if p.RequestID, err = buf.ConsumeUint32(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p.Data = *buf
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmarshalBinary decodes a full raw packet out of the given data.
|
||||
// It is assumed that the uint32(length) has already been consumed to receive the data.
|
||||
//
|
||||
// This is a convenience implementation primarily intended for tests,
|
||||
// because this must clone the given data byte slice,
|
||||
// as Data is not allowed to alias any part of the data byte slice.
|
||||
func (p *RawPacket) UnmarshalBinary(data []byte) error {
|
||||
clone := make([]byte, len(data))
|
||||
n := copy(clone, data)
|
||||
return p.UnmarshalFrom(NewBuffer(clone[:n]))
|
||||
}
|
||||
|
||||
// readPacket reads a uint32 length-prefixed binary data packet from r.
|
||||
// using the given byte slice as a backing array.
|
||||
//
|
||||
// If the packet length read from r is bigger than maxPacketLength,
|
||||
// or greater than math.MaxInt32 on a 32-bit implementation,
|
||||
// then a `ErrLongPacket` error will be returned.
|
||||
//
|
||||
// If the given byte slice is insufficient to hold the packet,
|
||||
// then it will be extended to fill the packet size.
|
||||
func readPacket(r io.Reader, b []byte, maxPacketLength uint32) ([]byte, error) {
|
||||
if cap(b) < 4 {
|
||||
// We will need allocate our own buffer just for reading the packet length.
|
||||
|
||||
// However, we don’t really want to allocate an extremely narrow buffer (4-bytes),
|
||||
// and cause unnecessary allocation churn from both length reads and small packet reads,
|
||||
// so we use smallBufferSize from the bytes package as a reasonable guess.
|
||||
|
||||
// But if callers really do want to force narrow throw-away allocation of every packet body,
|
||||
// they can do so with a buffer of capacity 4.
|
||||
b = make([]byte, smallBufferSize)
|
||||
}
|
||||
|
||||
if _, err := io.ReadFull(r, b[:4]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
length := unmarshalUint32(b)
|
||||
if int(length) < 5 {
|
||||
// Must have at least uint8(type) and uint32(request-id)
|
||||
|
||||
if int(length) < 0 {
|
||||
// Only possible when strconv.IntSize == 32,
|
||||
// the packet length is longer than math.MaxInt32,
|
||||
// and thus longer than any possible slice.
|
||||
return nil, ErrLongPacket
|
||||
}
|
||||
|
||||
return nil, ErrShortPacket
|
||||
}
|
||||
if length > maxPacketLength {
|
||||
return nil, ErrLongPacket
|
||||
}
|
||||
|
||||
if int(length) > cap(b) {
|
||||
// We know int(length) must be positive, because of tests above.
|
||||
b = make([]byte, length)
|
||||
}
|
||||
|
||||
n, err := io.ReadFull(r, b[:length])
|
||||
return b[:n], err
|
||||
}
|
||||
|
||||
// ReadFrom provides a simple functional packet reader,
|
||||
// using the given byte slice as a backing array.
|
||||
//
|
||||
// To protect against potential denial of service attacks,
|
||||
// if the read packet length is longer than maxPacketLength,
|
||||
// then no packet data will be read, and ErrLongPacket will be returned.
|
||||
// (On 32-bit int architectures, all packets >= 2^31 in length
|
||||
// will return ErrLongPacket regardless of maxPacketLength.)
|
||||
//
|
||||
// If the read packet length is longer than cap(b),
|
||||
// then a throw-away slice will allocated to meet the exact packet length.
|
||||
// This can be used to limit the length of reused buffers,
|
||||
// while still allowing reception of occasional large packets.
|
||||
//
|
||||
// The Data field may alias the passed in byte slice,
|
||||
// so the byte slice passed in should not be reused before RawPacket.Reset().
|
||||
func (p *RawPacket) ReadFrom(r io.Reader, b []byte, maxPacketLength uint32) error {
|
||||
b, err := readPacket(r, b, maxPacketLength)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return p.UnmarshalFrom(NewBuffer(b))
|
||||
}
|
||||
|
||||
// RequestPacket implements the general packet format from draft-ietf-secsh-filexfer-02
|
||||
// but also automatically decode/encodes valid request packets (2 < type < 100 || type == 200).
|
||||
//
|
||||
// RequestPacket is intended for use in servers receiving requests,
|
||||
// where any arbitrary request may be received, and so decoding them automatically
|
||||
// is useful.
|
||||
//
|
||||
// For clients expecting to receive specific response packet types,
|
||||
// where automatic unmarshaling of the packet body does not make sense,
|
||||
// use RawPacket.
|
||||
//
|
||||
// Defined in https://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#section-3
|
||||
type RequestPacket struct {
|
||||
RequestID uint32
|
||||
|
||||
Request Packet
|
||||
}
|
||||
|
||||
// Type returns the SSH_FXP_xy value associated with the underlying packet.
|
||||
func (p *RequestPacket) Type() PacketType {
|
||||
return p.Request.Type()
|
||||
}
|
||||
|
||||
// Reset clears the pointers and reference-semantic variables in RequestPacket,
|
||||
// releasing underlying resources, and making them and the RequestPacket suitable to be reused,
|
||||
// so long as no other references have been kept.
|
||||
func (p *RequestPacket) Reset() {
|
||||
p.Request = nil
|
||||
}
|
||||
|
||||
// MarshalPacket returns p as a two-part binary encoding of p.
|
||||
//
|
||||
// The internal p.RequestID is overridden by the reqid argument.
|
||||
func (p *RequestPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
||||
if p.Request == nil {
|
||||
return nil, nil, errors.New("empty request packet")
|
||||
}
|
||||
|
||||
return p.Request.MarshalPacket(reqid, b)
|
||||
}
|
||||
|
||||
// MarshalBinary returns p as the binary encoding of p.
|
||||
//
|
||||
// This is a convenience implementation primarily intended for tests,
|
||||
// because it is inefficient with allocations.
|
||||
func (p *RequestPacket) MarshalBinary() ([]byte, error) {
|
||||
return ComposePacket(p.MarshalPacket(p.RequestID, nil))
|
||||
}
|
||||
|
||||
// UnmarshalFrom decodes a RequestPacket from the given Buffer into p.
|
||||
//
|
||||
// The Request field may alias the passed in Buffer, (e.g. SSH_FXP_WRITE),
|
||||
// so the buffer passed in should not be reused before RequestPacket.Reset().
|
||||
func (p *RequestPacket) UnmarshalFrom(buf *Buffer) error {
|
||||
typ, err := buf.ConsumeUint8()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p.Request, err = newPacketFromType(PacketType(typ))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if p.RequestID, err = buf.ConsumeUint32(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return p.Request.UnmarshalPacketBody(buf)
|
||||
}
|
||||
|
||||
// UnmarshalBinary decodes a full request packet out of the given data.
|
||||
// It is assumed that the uint32(length) has already been consumed to receive the data.
|
||||
//
|
||||
// This is a convenience implementation primarily intended for tests,
|
||||
// because this must clone the given data byte slice,
|
||||
// as Request is not allowed to alias any part of the data byte slice.
|
||||
func (p *RequestPacket) UnmarshalBinary(data []byte) error {
|
||||
clone := make([]byte, len(data))
|
||||
n := copy(clone, data)
|
||||
return p.UnmarshalFrom(NewBuffer(clone[:n]))
|
||||
}
|
||||
|
||||
// ReadFrom provides a simple functional packet reader,
|
||||
// using the given byte slice as a backing array.
|
||||
//
|
||||
// To protect against potential denial of service attacks,
|
||||
// if the read packet length is longer than maxPacketLength,
|
||||
// then no packet data will be read, and ErrLongPacket will be returned.
|
||||
// (On 32-bit int architectures, all packets >= 2^31 in length
|
||||
// will return ErrLongPacket regardless of maxPacketLength.)
|
||||
//
|
||||
// If the read packet length is longer than cap(b),
|
||||
// then a throw-away slice will allocated to meet the exact packet length.
|
||||
// This can be used to limit the length of reused buffers,
|
||||
// while still allowing reception of occasional large packets.
|
||||
//
|
||||
// The Request field may alias the passed in byte slice,
|
||||
// so the byte slice passed in should not be reused before RawPacket.Reset().
|
||||
func (p *RequestPacket) ReadFrom(r io.Reader, b []byte, maxPacketLength uint32) error {
|
||||
b, err := readPacket(r, b, maxPacketLength)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return p.UnmarshalFrom(NewBuffer(b))
|
||||
}
|
||||
368
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/path_packets.go
generated
vendored
Normal file
368
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/path_packets.go
generated
vendored
Normal file
|
|
@ -0,0 +1,368 @@
|
|||
package filexfer
|
||||
|
||||
// LStatPacket defines the SSH_FXP_LSTAT packet.
|
||||
type LStatPacket struct {
|
||||
Path string
|
||||
}
|
||||
|
||||
// Type returns the SSH_FXP_xy value associated with this packet type.
|
||||
func (p *LStatPacket) Type() PacketType {
|
||||
return PacketTypeLStat
|
||||
}
|
||||
|
||||
// MarshalPacket returns p as a two-part binary encoding of p.
|
||||
func (p *LStatPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
||||
buf := NewBuffer(b)
|
||||
if buf.Cap() < 9 {
|
||||
size := 4 + len(p.Path) // string(path)
|
||||
buf = NewMarshalBuffer(size)
|
||||
}
|
||||
|
||||
buf.StartPacket(PacketTypeLStat, reqid)
|
||||
buf.AppendString(p.Path)
|
||||
|
||||
return buf.Packet(payload)
|
||||
}
|
||||
|
||||
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
||||
// It is assumed that the uint32(request-id) has already been consumed.
|
||||
func (p *LStatPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
if p.Path, err = buf.ConsumeString(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetstatPacket defines the SSH_FXP_SETSTAT packet.
|
||||
type SetstatPacket struct {
|
||||
Path string
|
||||
Attrs Attributes
|
||||
}
|
||||
|
||||
// Type returns the SSH_FXP_xy value associated with this packet type.
|
||||
func (p *SetstatPacket) Type() PacketType {
|
||||
return PacketTypeSetstat
|
||||
}
|
||||
|
||||
// MarshalPacket returns p as a two-part binary encoding of p.
|
||||
func (p *SetstatPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
||||
buf := NewBuffer(b)
|
||||
if buf.Cap() < 9 {
|
||||
size := 4 + len(p.Path) + p.Attrs.Len() // string(path) + ATTRS(attrs)
|
||||
buf = NewMarshalBuffer(size)
|
||||
}
|
||||
|
||||
buf.StartPacket(PacketTypeSetstat, reqid)
|
||||
buf.AppendString(p.Path)
|
||||
|
||||
p.Attrs.MarshalInto(buf)
|
||||
|
||||
return buf.Packet(payload)
|
||||
}
|
||||
|
||||
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
||||
// It is assumed that the uint32(request-id) has already been consumed.
|
||||
func (p *SetstatPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
if p.Path, err = buf.ConsumeString(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return p.Attrs.UnmarshalFrom(buf)
|
||||
}
|
||||
|
||||
// RemovePacket defines the SSH_FXP_REMOVE packet.
|
||||
type RemovePacket struct {
|
||||
Path string
|
||||
}
|
||||
|
||||
// Type returns the SSH_FXP_xy value associated with this packet type.
|
||||
func (p *RemovePacket) Type() PacketType {
|
||||
return PacketTypeRemove
|
||||
}
|
||||
|
||||
// MarshalPacket returns p as a two-part binary encoding of p.
|
||||
func (p *RemovePacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
||||
buf := NewBuffer(b)
|
||||
if buf.Cap() < 9 {
|
||||
size := 4 + len(p.Path) // string(path)
|
||||
buf = NewMarshalBuffer(size)
|
||||
}
|
||||
|
||||
buf.StartPacket(PacketTypeRemove, reqid)
|
||||
buf.AppendString(p.Path)
|
||||
|
||||
return buf.Packet(payload)
|
||||
}
|
||||
|
||||
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
||||
// It is assumed that the uint32(request-id) has already been consumed.
|
||||
func (p *RemovePacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
if p.Path, err = buf.ConsumeString(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MkdirPacket defines the SSH_FXP_MKDIR packet.
|
||||
type MkdirPacket struct {
|
||||
Path string
|
||||
Attrs Attributes
|
||||
}
|
||||
|
||||
// Type returns the SSH_FXP_xy value associated with this packet type.
|
||||
func (p *MkdirPacket) Type() PacketType {
|
||||
return PacketTypeMkdir
|
||||
}
|
||||
|
||||
// MarshalPacket returns p as a two-part binary encoding of p.
|
||||
func (p *MkdirPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
||||
buf := NewBuffer(b)
|
||||
if buf.Cap() < 9 {
|
||||
size := 4 + len(p.Path) + p.Attrs.Len() // string(path) + ATTRS(attrs)
|
||||
buf = NewMarshalBuffer(size)
|
||||
}
|
||||
|
||||
buf.StartPacket(PacketTypeMkdir, reqid)
|
||||
buf.AppendString(p.Path)
|
||||
|
||||
p.Attrs.MarshalInto(buf)
|
||||
|
||||
return buf.Packet(payload)
|
||||
}
|
||||
|
||||
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
||||
// It is assumed that the uint32(request-id) has already been consumed.
|
||||
func (p *MkdirPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
if p.Path, err = buf.ConsumeString(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return p.Attrs.UnmarshalFrom(buf)
|
||||
}
|
||||
|
||||
// RmdirPacket defines the SSH_FXP_RMDIR packet.
|
||||
type RmdirPacket struct {
|
||||
Path string
|
||||
}
|
||||
|
||||
// Type returns the SSH_FXP_xy value associated with this packet type.
|
||||
func (p *RmdirPacket) Type() PacketType {
|
||||
return PacketTypeRmdir
|
||||
}
|
||||
|
||||
// MarshalPacket returns p as a two-part binary encoding of p.
|
||||
func (p *RmdirPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
||||
buf := NewBuffer(b)
|
||||
if buf.Cap() < 9 {
|
||||
size := 4 + len(p.Path) // string(path)
|
||||
buf = NewMarshalBuffer(size)
|
||||
}
|
||||
|
||||
buf.StartPacket(PacketTypeRmdir, reqid)
|
||||
buf.AppendString(p.Path)
|
||||
|
||||
return buf.Packet(payload)
|
||||
}
|
||||
|
||||
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
||||
// It is assumed that the uint32(request-id) has already been consumed.
|
||||
func (p *RmdirPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
if p.Path, err = buf.ConsumeString(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RealPathPacket defines the SSH_FXP_REALPATH packet.
|
||||
type RealPathPacket struct {
|
||||
Path string
|
||||
}
|
||||
|
||||
// Type returns the SSH_FXP_xy value associated with this packet type.
|
||||
func (p *RealPathPacket) Type() PacketType {
|
||||
return PacketTypeRealPath
|
||||
}
|
||||
|
||||
// MarshalPacket returns p as a two-part binary encoding of p.
|
||||
func (p *RealPathPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
||||
buf := NewBuffer(b)
|
||||
if buf.Cap() < 9 {
|
||||
size := 4 + len(p.Path) // string(path)
|
||||
buf = NewMarshalBuffer(size)
|
||||
}
|
||||
|
||||
buf.StartPacket(PacketTypeRealPath, reqid)
|
||||
buf.AppendString(p.Path)
|
||||
|
||||
return buf.Packet(payload)
|
||||
}
|
||||
|
||||
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
||||
// It is assumed that the uint32(request-id) has already been consumed.
|
||||
func (p *RealPathPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
if p.Path, err = buf.ConsumeString(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// StatPacket defines the SSH_FXP_STAT packet.
|
||||
type StatPacket struct {
|
||||
Path string
|
||||
}
|
||||
|
||||
// Type returns the SSH_FXP_xy value associated with this packet type.
|
||||
func (p *StatPacket) Type() PacketType {
|
||||
return PacketTypeStat
|
||||
}
|
||||
|
||||
// MarshalPacket returns p as a two-part binary encoding of p.
|
||||
func (p *StatPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
||||
buf := NewBuffer(b)
|
||||
if buf.Cap() < 9 {
|
||||
size := 4 + len(p.Path) // string(path)
|
||||
buf = NewMarshalBuffer(size)
|
||||
}
|
||||
|
||||
buf.StartPacket(PacketTypeStat, reqid)
|
||||
buf.AppendString(p.Path)
|
||||
|
||||
return buf.Packet(payload)
|
||||
}
|
||||
|
||||
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
||||
// It is assumed that the uint32(request-id) has already been consumed.
|
||||
func (p *StatPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
if p.Path, err = buf.ConsumeString(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RenamePacket defines the SSH_FXP_RENAME packet.
|
||||
type RenamePacket struct {
|
||||
OldPath string
|
||||
NewPath string
|
||||
}
|
||||
|
||||
// Type returns the SSH_FXP_xy value associated with this packet type.
|
||||
func (p *RenamePacket) Type() PacketType {
|
||||
return PacketTypeRename
|
||||
}
|
||||
|
||||
// MarshalPacket returns p as a two-part binary encoding of p.
|
||||
func (p *RenamePacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
||||
buf := NewBuffer(b)
|
||||
if buf.Cap() < 9 {
|
||||
// string(oldpath) + string(newpath)
|
||||
size := 4 + len(p.OldPath) + 4 + len(p.NewPath)
|
||||
buf = NewMarshalBuffer(size)
|
||||
}
|
||||
|
||||
buf.StartPacket(PacketTypeRename, reqid)
|
||||
buf.AppendString(p.OldPath)
|
||||
buf.AppendString(p.NewPath)
|
||||
|
||||
return buf.Packet(payload)
|
||||
}
|
||||
|
||||
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
||||
// It is assumed that the uint32(request-id) has already been consumed.
|
||||
func (p *RenamePacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
if p.OldPath, err = buf.ConsumeString(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if p.NewPath, err = buf.ConsumeString(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReadLinkPacket defines the SSH_FXP_READLINK packet.
|
||||
type ReadLinkPacket struct {
|
||||
Path string
|
||||
}
|
||||
|
||||
// Type returns the SSH_FXP_xy value associated with this packet type.
|
||||
func (p *ReadLinkPacket) Type() PacketType {
|
||||
return PacketTypeReadLink
|
||||
}
|
||||
|
||||
// MarshalPacket returns p as a two-part binary encoding of p.
|
||||
func (p *ReadLinkPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
||||
buf := NewBuffer(b)
|
||||
if buf.Cap() < 9 {
|
||||
size := 4 + len(p.Path) // string(path)
|
||||
buf = NewMarshalBuffer(size)
|
||||
}
|
||||
|
||||
buf.StartPacket(PacketTypeReadLink, reqid)
|
||||
buf.AppendString(p.Path)
|
||||
|
||||
return buf.Packet(payload)
|
||||
}
|
||||
|
||||
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
||||
// It is assumed that the uint32(request-id) has already been consumed.
|
||||
func (p *ReadLinkPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
if p.Path, err = buf.ConsumeString(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SymlinkPacket defines the SSH_FXP_SYMLINK packet.
|
||||
//
|
||||
// The order of the arguments to the SSH_FXP_SYMLINK method was inadvertently reversed.
|
||||
// Unfortunately, the reversal was not noticed until the server was widely deployed.
|
||||
// Covered in Section 3.1 of https://github.com/openssh/openssh-portable/blob/master/PROTOCOL
|
||||
type SymlinkPacket struct {
|
||||
LinkPath string
|
||||
TargetPath string
|
||||
}
|
||||
|
||||
// Type returns the SSH_FXP_xy value associated with this packet type.
|
||||
func (p *SymlinkPacket) Type() PacketType {
|
||||
return PacketTypeSymlink
|
||||
}
|
||||
|
||||
// MarshalPacket returns p as a two-part binary encoding of p.
|
||||
func (p *SymlinkPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
||||
buf := NewBuffer(b)
|
||||
if buf.Cap() < 9 {
|
||||
// string(targetpath) + string(linkpath)
|
||||
size := 4 + len(p.TargetPath) + 4 + len(p.LinkPath)
|
||||
buf = NewMarshalBuffer(size)
|
||||
}
|
||||
|
||||
buf.StartPacket(PacketTypeSymlink, reqid)
|
||||
|
||||
// Arguments were inadvertently reversed.
|
||||
buf.AppendString(p.TargetPath)
|
||||
buf.AppendString(p.LinkPath)
|
||||
|
||||
return buf.Packet(payload)
|
||||
}
|
||||
|
||||
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
||||
// It is assumed that the uint32(request-id) has already been consumed.
|
||||
func (p *SymlinkPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
// Arguments were inadvertently reversed.
|
||||
if p.TargetPath, err = buf.ConsumeString(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if p.LinkPath, err = buf.ConsumeString(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
114
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/permissions.go
generated
vendored
Normal file
114
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/permissions.go
generated
vendored
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
package filexfer
|
||||
|
||||
// FileMode represents a file’s mode and permission bits.
|
||||
// The bits are defined according to POSIX standards,
|
||||
// and may not apply to the OS being built for.
|
||||
type FileMode uint32
|
||||
|
||||
// Permission flags, defined here to avoid potential inconsistencies in individual OS implementations.
|
||||
const (
|
||||
ModePerm FileMode = 0o0777 // S_IRWXU | S_IRWXG | S_IRWXO
|
||||
ModeUserRead FileMode = 0o0400 // S_IRUSR
|
||||
ModeUserWrite FileMode = 0o0200 // S_IWUSR
|
||||
ModeUserExec FileMode = 0o0100 // S_IXUSR
|
||||
ModeGroupRead FileMode = 0o0040 // S_IRGRP
|
||||
ModeGroupWrite FileMode = 0o0020 // S_IWGRP
|
||||
ModeGroupExec FileMode = 0o0010 // S_IXGRP
|
||||
ModeOtherRead FileMode = 0o0004 // S_IROTH
|
||||
ModeOtherWrite FileMode = 0o0002 // S_IWOTH
|
||||
ModeOtherExec FileMode = 0o0001 // S_IXOTH
|
||||
|
||||
ModeSetUID FileMode = 0o4000 // S_ISUID
|
||||
ModeSetGID FileMode = 0o2000 // S_ISGID
|
||||
ModeSticky FileMode = 0o1000 // S_ISVTX
|
||||
|
||||
ModeType FileMode = 0xF000 // S_IFMT
|
||||
ModeNamedPipe FileMode = 0x1000 // S_IFIFO
|
||||
ModeCharDevice FileMode = 0x2000 // S_IFCHR
|
||||
ModeDir FileMode = 0x4000 // S_IFDIR
|
||||
ModeDevice FileMode = 0x6000 // S_IFBLK
|
||||
ModeRegular FileMode = 0x8000 // S_IFREG
|
||||
ModeSymlink FileMode = 0xA000 // S_IFLNK
|
||||
ModeSocket FileMode = 0xC000 // S_IFSOCK
|
||||
)
|
||||
|
||||
// IsDir reports whether m describes a directory.
|
||||
// That is, it tests for m.Type() == ModeDir.
|
||||
func (m FileMode) IsDir() bool {
|
||||
return (m & ModeType) == ModeDir
|
||||
}
|
||||
|
||||
// IsRegular reports whether m describes a regular file.
|
||||
// That is, it tests for m.Type() == ModeRegular
|
||||
func (m FileMode) IsRegular() bool {
|
||||
return (m & ModeType) == ModeRegular
|
||||
}
|
||||
|
||||
// Perm returns the POSIX permission bits in m (m & ModePerm).
|
||||
func (m FileMode) Perm() FileMode {
|
||||
return (m & ModePerm)
|
||||
}
|
||||
|
||||
// Type returns the type bits in m (m & ModeType).
|
||||
func (m FileMode) Type() FileMode {
|
||||
return (m & ModeType)
|
||||
}
|
||||
|
||||
// String returns a `-rwxrwxrwx` style string representing the `ls -l` POSIX permissions string.
|
||||
func (m FileMode) String() string {
|
||||
var buf [10]byte
|
||||
|
||||
switch m.Type() {
|
||||
case ModeRegular:
|
||||
buf[0] = '-'
|
||||
case ModeDir:
|
||||
buf[0] = 'd'
|
||||
case ModeSymlink:
|
||||
buf[0] = 'l'
|
||||
case ModeDevice:
|
||||
buf[0] = 'b'
|
||||
case ModeCharDevice:
|
||||
buf[0] = 'c'
|
||||
case ModeNamedPipe:
|
||||
buf[0] = 'p'
|
||||
case ModeSocket:
|
||||
buf[0] = 's'
|
||||
default:
|
||||
buf[0] = '?'
|
||||
}
|
||||
|
||||
const rwx = "rwxrwxrwx"
|
||||
for i, c := range rwx {
|
||||
if m&(1<<uint(9-1-i)) != 0 {
|
||||
buf[i+1] = byte(c)
|
||||
} else {
|
||||
buf[i+1] = '-'
|
||||
}
|
||||
}
|
||||
|
||||
if m&ModeSetUID != 0 {
|
||||
if buf[3] == 'x' {
|
||||
buf[3] = 's'
|
||||
} else {
|
||||
buf[3] = 'S'
|
||||
}
|
||||
}
|
||||
|
||||
if m&ModeSetGID != 0 {
|
||||
if buf[6] == 'x' {
|
||||
buf[6] = 's'
|
||||
} else {
|
||||
buf[6] = 'S'
|
||||
}
|
||||
}
|
||||
|
||||
if m&ModeSticky != 0 {
|
||||
if buf[9] == 'x' {
|
||||
buf[9] = 't'
|
||||
} else {
|
||||
buf[9] = 'T'
|
||||
}
|
||||
}
|
||||
|
||||
return string(buf[:])
|
||||
}
|
||||
243
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/response_packets.go
generated
vendored
Normal file
243
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/response_packets.go
generated
vendored
Normal file
|
|
@ -0,0 +1,243 @@
|
|||
package filexfer
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// StatusPacket defines the SSH_FXP_STATUS packet.
|
||||
//
|
||||
// Specified in https://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#section-7
|
||||
type StatusPacket struct {
|
||||
StatusCode Status
|
||||
ErrorMessage string
|
||||
LanguageTag string
|
||||
}
|
||||
|
||||
// Error makes StatusPacket an error type.
|
||||
func (p *StatusPacket) Error() string {
|
||||
if p.ErrorMessage == "" {
|
||||
return "sftp: " + p.StatusCode.String()
|
||||
}
|
||||
|
||||
return fmt.Sprintf("sftp: %q (%s)", p.ErrorMessage, p.StatusCode)
|
||||
}
|
||||
|
||||
// Is returns true if target is a StatusPacket with the same StatusCode,
|
||||
// or target is a Status code which is the same as SatusCode.
|
||||
func (p *StatusPacket) Is(target error) bool {
|
||||
if target, ok := target.(*StatusPacket); ok {
|
||||
return p.StatusCode == target.StatusCode
|
||||
}
|
||||
|
||||
return p.StatusCode == target
|
||||
}
|
||||
|
||||
// Type returns the SSH_FXP_xy value associated with this packet type.
|
||||
func (p *StatusPacket) Type() PacketType {
|
||||
return PacketTypeStatus
|
||||
}
|
||||
|
||||
// MarshalPacket returns p as a two-part binary encoding of p.
|
||||
func (p *StatusPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
||||
buf := NewBuffer(b)
|
||||
if buf.Cap() < 9 {
|
||||
// uint32(error/status code) + string(error message) + string(language tag)
|
||||
size := 4 + 4 + len(p.ErrorMessage) + 4 + len(p.LanguageTag)
|
||||
buf = NewMarshalBuffer(size)
|
||||
}
|
||||
|
||||
buf.StartPacket(PacketTypeStatus, reqid)
|
||||
buf.AppendUint32(uint32(p.StatusCode))
|
||||
buf.AppendString(p.ErrorMessage)
|
||||
buf.AppendString(p.LanguageTag)
|
||||
|
||||
return buf.Packet(payload)
|
||||
}
|
||||
|
||||
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
||||
// It is assumed that the uint32(request-id) has already been consumed.
|
||||
func (p *StatusPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
statusCode, err := buf.ConsumeUint32()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
p.StatusCode = Status(statusCode)
|
||||
|
||||
if p.ErrorMessage, err = buf.ConsumeString(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if p.LanguageTag, err = buf.ConsumeString(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// HandlePacket defines the SSH_FXP_HANDLE packet.
|
||||
type HandlePacket struct {
|
||||
Handle string
|
||||
}
|
||||
|
||||
// Type returns the SSH_FXP_xy value associated with this packet type.
|
||||
func (p *HandlePacket) Type() PacketType {
|
||||
return PacketTypeHandle
|
||||
}
|
||||
|
||||
// MarshalPacket returns p as a two-part binary encoding of p.
|
||||
func (p *HandlePacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
||||
buf := NewBuffer(b)
|
||||
if buf.Cap() < 9 {
|
||||
size := 4 + len(p.Handle) // string(handle)
|
||||
buf = NewMarshalBuffer(size)
|
||||
}
|
||||
|
||||
buf.StartPacket(PacketTypeHandle, reqid)
|
||||
buf.AppendString(p.Handle)
|
||||
|
||||
return buf.Packet(payload)
|
||||
}
|
||||
|
||||
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
||||
// It is assumed that the uint32(request-id) has already been consumed.
|
||||
func (p *HandlePacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
if p.Handle, err = buf.ConsumeString(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DataPacket defines the SSH_FXP_DATA packet.
|
||||
type DataPacket struct {
|
||||
Data []byte
|
||||
}
|
||||
|
||||
// Type returns the SSH_FXP_xy value associated with this packet type.
|
||||
func (p *DataPacket) Type() PacketType {
|
||||
return PacketTypeData
|
||||
}
|
||||
|
||||
// MarshalPacket returns p as a two-part binary encoding of p.
|
||||
func (p *DataPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
||||
buf := NewBuffer(b)
|
||||
if buf.Cap() < 9 {
|
||||
size := 4 // uint32(len(data)); data content in payload
|
||||
buf = NewMarshalBuffer(size)
|
||||
}
|
||||
|
||||
buf.StartPacket(PacketTypeData, reqid)
|
||||
buf.AppendUint32(uint32(len(p.Data)))
|
||||
|
||||
return buf.Packet(p.Data)
|
||||
}
|
||||
|
||||
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
||||
// It is assumed that the uint32(request-id) has already been consumed.
|
||||
//
|
||||
// If p.Data is already populated, and of sufficient length to hold the data,
|
||||
// then this will copy the data into that byte slice.
|
||||
//
|
||||
// If p.Data has a length insufficient to hold the data,
|
||||
// then this will make a new slice of sufficient length, and copy the data into that.
|
||||
//
|
||||
// This means this _does not_ alias any of the data buffer that is passed in.
|
||||
func (p *DataPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
data, err := buf.ConsumeByteSlice()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(p.Data) < len(data) {
|
||||
p.Data = make([]byte, len(data))
|
||||
}
|
||||
|
||||
n := copy(p.Data, data)
|
||||
p.Data = p.Data[:n]
|
||||
return nil
|
||||
}
|
||||
|
||||
// NamePacket defines the SSH_FXP_NAME packet.
|
||||
type NamePacket struct {
|
||||
Entries []*NameEntry
|
||||
}
|
||||
|
||||
// Type returns the SSH_FXP_xy value associated with this packet type.
|
||||
func (p *NamePacket) Type() PacketType {
|
||||
return PacketTypeName
|
||||
}
|
||||
|
||||
// MarshalPacket returns p as a two-part binary encoding of p.
|
||||
func (p *NamePacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
||||
buf := NewBuffer(b)
|
||||
if buf.Cap() < 9 {
|
||||
size := 4 // uint32(len(entries))
|
||||
|
||||
for _, e := range p.Entries {
|
||||
size += e.Len()
|
||||
}
|
||||
|
||||
buf = NewMarshalBuffer(size)
|
||||
}
|
||||
|
||||
buf.StartPacket(PacketTypeName, reqid)
|
||||
buf.AppendUint32(uint32(len(p.Entries)))
|
||||
|
||||
for _, e := range p.Entries {
|
||||
e.MarshalInto(buf)
|
||||
}
|
||||
|
||||
return buf.Packet(payload)
|
||||
}
|
||||
|
||||
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
||||
// It is assumed that the uint32(request-id) has already been consumed.
|
||||
func (p *NamePacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
count, err := buf.ConsumeUint32()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p.Entries = make([]*NameEntry, 0, count)
|
||||
|
||||
for i := uint32(0); i < count; i++ {
|
||||
var e NameEntry
|
||||
if err := e.UnmarshalFrom(buf); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p.Entries = append(p.Entries, &e)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// AttrsPacket defines the SSH_FXP_ATTRS packet.
|
||||
type AttrsPacket struct {
|
||||
Attrs Attributes
|
||||
}
|
||||
|
||||
// Type returns the SSH_FXP_xy value associated with this packet type.
|
||||
func (p *AttrsPacket) Type() PacketType {
|
||||
return PacketTypeAttrs
|
||||
}
|
||||
|
||||
// MarshalPacket returns p as a two-part binary encoding of p.
|
||||
func (p *AttrsPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
||||
buf := NewBuffer(b)
|
||||
if buf.Cap() < 9 {
|
||||
size := p.Attrs.Len() // ATTRS(attrs)
|
||||
buf = NewMarshalBuffer(size)
|
||||
}
|
||||
|
||||
buf.StartPacket(PacketTypeAttrs, reqid)
|
||||
p.Attrs.MarshalInto(buf)
|
||||
|
||||
return buf.Packet(payload)
|
||||
}
|
||||
|
||||
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
||||
// It is assumed that the uint32(request-id) has already been consumed.
|
||||
func (p *AttrsPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
return p.Attrs.UnmarshalFrom(buf)
|
||||
}
|
||||
81
vendor/github.com/pkg/sftp/ls_formatting.go
generated
vendored
Normal file
81
vendor/github.com/pkg/sftp/ls_formatting.go
generated
vendored
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
package sftp
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/user"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
sshfx "github.com/pkg/sftp/internal/encoding/ssh/filexfer"
|
||||
)
|
||||
|
||||
func lsFormatID(id uint32) string {
|
||||
return strconv.FormatUint(uint64(id), 10)
|
||||
}
|
||||
|
||||
type osIDLookup struct{}
|
||||
|
||||
func (osIDLookup) Filelist(*Request) (ListerAt, error) {
|
||||
return nil, errors.New("unimplemented stub")
|
||||
}
|
||||
|
||||
func (osIDLookup) LookupUserName(uid string) string {
|
||||
u, err := user.LookupId(uid)
|
||||
if err != nil {
|
||||
return uid
|
||||
}
|
||||
|
||||
return u.Username
|
||||
}
|
||||
|
||||
func (osIDLookup) LookupGroupName(gid string) string {
|
||||
g, err := user.LookupGroupId(gid)
|
||||
if err != nil {
|
||||
return gid
|
||||
}
|
||||
|
||||
return g.Name
|
||||
}
|
||||
|
||||
// runLs formats the FileInfo as per `ls -l` style, which is in the 'longname' field of a SSH_FXP_NAME entry.
|
||||
// This is a fairly simple implementation, just enough to look close to openssh in simple cases.
|
||||
func runLs(idLookup NameLookupFileLister, dirent os.FileInfo) 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
|
||||
|
||||
symPerms := sshfx.FileMode(fromFileMode(dirent.Mode())).String()
|
||||
|
||||
var numLinks uint64 = 1
|
||||
uid, gid := "0", "0"
|
||||
|
||||
switch sys := dirent.Sys().(type) {
|
||||
case *sshfx.Attributes:
|
||||
uid = lsFormatID(sys.UID)
|
||||
gid = lsFormatID(sys.GID)
|
||||
case *FileStat:
|
||||
uid = lsFormatID(sys.UID)
|
||||
gid = lsFormatID(sys.GID)
|
||||
default:
|
||||
numLinks, uid, gid = lsLinksUIDGID(dirent)
|
||||
}
|
||||
|
||||
if idLookup != nil {
|
||||
uid, gid = idLookup.LookupUserName(uid), idLookup.LookupGroupName(gid)
|
||||
}
|
||||
|
||||
mtime := dirent.ModTime()
|
||||
date := mtime.Format("Jan 2")
|
||||
|
||||
var yearOrTime string
|
||||
if mtime.Before(time.Now().AddDate(0, -6, 0)) {
|
||||
yearOrTime = mtime.Format("2006")
|
||||
} else {
|
||||
yearOrTime = mtime.Format("15:04")
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s %4d %-8s %-8s %8d %s %5s %s", symPerms, numLinks, uid, gid, dirent.Size(), date, yearOrTime, dirent.Name())
|
||||
}
|
||||
21
vendor/github.com/pkg/sftp/ls_plan9.go
generated
vendored
Normal file
21
vendor/github.com/pkg/sftp/ls_plan9.go
generated
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
// +build plan9
|
||||
|
||||
package sftp
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func lsLinksUIDGID(fi os.FileInfo) (numLinks uint64, uid, gid string) {
|
||||
numLinks = 1
|
||||
uid, gid = "0", "0"
|
||||
|
||||
switch sys := fi.Sys().(type) {
|
||||
case *syscall.Dir:
|
||||
uid = sys.Uid
|
||||
gid = sys.Gid
|
||||
}
|
||||
|
||||
return numLinks, uid, gid
|
||||
}
|
||||
11
vendor/github.com/pkg/sftp/ls_stub.go
generated
vendored
Normal file
11
vendor/github.com/pkg/sftp/ls_stub.go
generated
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
// +build windows android
|
||||
|
||||
package sftp
|
||||
|
||||
import (
|
||||
"os"
|
||||
)
|
||||
|
||||
func lsLinksUIDGID(fi os.FileInfo) (numLinks uint64, uid, gid string) {
|
||||
return 1, "0", "0"
|
||||
}
|
||||
23
vendor/github.com/pkg/sftp/ls_unix.go
generated
vendored
Normal file
23
vendor/github.com/pkg/sftp/ls_unix.go
generated
vendored
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
// +build aix darwin dragonfly freebsd !android,linux netbsd openbsd solaris js
|
||||
|
||||
package sftp
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func lsLinksUIDGID(fi os.FileInfo) (numLinks uint64, uid, gid string) {
|
||||
numLinks = 1
|
||||
uid, gid = "0", "0"
|
||||
|
||||
switch sys := fi.Sys().(type) {
|
||||
case *syscall.Stat_t:
|
||||
numLinks = uint64(sys.Nlink)
|
||||
uid = lsFormatID(sys.Uid)
|
||||
gid = lsFormatID(sys.Gid)
|
||||
default:
|
||||
}
|
||||
|
||||
return numLinks, uid, gid
|
||||
}
|
||||
137
vendor/github.com/pkg/sftp/match.go
generated
vendored
Normal file
137
vendor/github.com/pkg/sftp/match.go
generated
vendored
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
package sftp
|
||||
|
||||
import (
|
||||
"path"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ErrBadPattern indicates a globbing pattern was malformed.
|
||||
var ErrBadPattern = path.ErrBadPattern
|
||||
|
||||
// Match reports whether name matches the shell pattern.
|
||||
//
|
||||
// This is an alias for path.Match from the standard library,
|
||||
// offered so that callers need not import the path package.
|
||||
// For details, see https://golang.org/pkg/path/#Match.
|
||||
func Match(pattern, name string) (matched bool, err error) {
|
||||
return path.Match(pattern, name)
|
||||
}
|
||||
|
||||
// detect if byte(char) is path separator
|
||||
func isPathSeparator(c byte) bool {
|
||||
return c == '/'
|
||||
}
|
||||
|
||||
// Split splits the path p immediately following the final slash,
|
||||
// separating it into a directory and file name component.
|
||||
//
|
||||
// This is an alias for path.Split from the standard library,
|
||||
// offered so that callers need not import the path package.
|
||||
// For details, see https://golang.org/pkg/path/#Split.
|
||||
func Split(p string) (dir, file string) {
|
||||
return path.Split(p)
|
||||
}
|
||||
|
||||
// Glob returns the names of all files matching pattern or nil
|
||||
// if there is no matching file. The syntax of patterns is the same
|
||||
// as in Match. The pattern may describe hierarchical names such as
|
||||
// /usr/*/bin/ed.
|
||||
//
|
||||
// Glob ignores file system errors such as I/O errors reading directories.
|
||||
// The only possible returned error is ErrBadPattern, when pattern
|
||||
// is malformed.
|
||||
func (c *Client) Glob(pattern string) (matches []string, err error) {
|
||||
if !hasMeta(pattern) {
|
||||
file, err := c.Lstat(pattern)
|
||||
if err != nil {
|
||||
return nil, nil
|
||||
}
|
||||
dir, _ := Split(pattern)
|
||||
dir = cleanGlobPath(dir)
|
||||
return []string{Join(dir, file.Name())}, nil
|
||||
}
|
||||
|
||||
dir, file := Split(pattern)
|
||||
dir = cleanGlobPath(dir)
|
||||
|
||||
if !hasMeta(dir) {
|
||||
return c.glob(dir, file, nil)
|
||||
}
|
||||
|
||||
// Prevent infinite recursion. See issue 15879.
|
||||
if dir == pattern {
|
||||
return nil, ErrBadPattern
|
||||
}
|
||||
|
||||
var m []string
|
||||
m, err = c.Glob(dir)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for _, d := range m {
|
||||
matches, err = c.glob(d, file, matches)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// cleanGlobPath prepares path for glob matching.
|
||||
func cleanGlobPath(path string) string {
|
||||
switch path {
|
||||
case "":
|
||||
return "."
|
||||
case "/":
|
||||
return path
|
||||
default:
|
||||
return path[0 : len(path)-1] // chop off trailing separator
|
||||
}
|
||||
}
|
||||
|
||||
// glob searches for files matching pattern in the directory dir
|
||||
// and appends them to matches. If the directory cannot be
|
||||
// opened, it returns the existing matches. New matches are
|
||||
// added in lexicographical order.
|
||||
func (c *Client) glob(dir, pattern string, matches []string) (m []string, e error) {
|
||||
m = matches
|
||||
fi, err := c.Stat(dir)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if !fi.IsDir() {
|
||||
return
|
||||
}
|
||||
names, err := c.ReadDir(dir)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
//sort.Strings(names)
|
||||
|
||||
for _, n := range names {
|
||||
matched, err := Match(pattern, n.Name())
|
||||
if err != nil {
|
||||
return m, err
|
||||
}
|
||||
if matched {
|
||||
m = append(m, Join(dir, n.Name()))
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Join joins any number of path elements into a single path, separating
|
||||
// them with slashes.
|
||||
//
|
||||
// This is an alias for path.Join from the standard library,
|
||||
// offered so that callers need not import the path package.
|
||||
// For details, see https://golang.org/pkg/path/#Join.
|
||||
func Join(elem ...string) string {
|
||||
return path.Join(elem...)
|
||||
}
|
||||
|
||||
// hasMeta reports whether path contains any of the magic characters
|
||||
// recognized by Match.
|
||||
func hasMeta(path string) bool {
|
||||
return strings.ContainsAny(path, "\\*?[")
|
||||
}
|
||||
216
vendor/github.com/pkg/sftp/packet-manager.go
generated
vendored
Normal file
216
vendor/github.com/pkg/sftp/packet-manager.go
generated
vendored
Normal file
|
|
@ -0,0 +1,216 @@
|
|||
package sftp
|
||||
|
||||
import (
|
||||
"encoding"
|
||||
"sort"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// The goal of the packetManager is to keep the outgoing packets in the same
|
||||
// order as the incoming as is requires by section 7 of the RFC.
|
||||
|
||||
type packetManager struct {
|
||||
requests chan orderedPacket
|
||||
responses chan orderedPacket
|
||||
fini chan struct{}
|
||||
incoming orderedPackets
|
||||
outgoing orderedPackets
|
||||
sender packetSender // connection object
|
||||
working *sync.WaitGroup
|
||||
packetCount uint32
|
||||
// it is not nil if the allocator is enabled
|
||||
alloc *allocator
|
||||
}
|
||||
|
||||
type packetSender interface {
|
||||
sendPacket(encoding.BinaryMarshaler) error
|
||||
}
|
||||
|
||||
func newPktMgr(sender packetSender) *packetManager {
|
||||
s := &packetManager{
|
||||
requests: make(chan orderedPacket, SftpServerWorkerCount),
|
||||
responses: make(chan orderedPacket, SftpServerWorkerCount),
|
||||
fini: make(chan struct{}),
|
||||
incoming: make([]orderedPacket, 0, SftpServerWorkerCount),
|
||||
outgoing: make([]orderedPacket, 0, SftpServerWorkerCount),
|
||||
sender: sender,
|
||||
working: &sync.WaitGroup{},
|
||||
}
|
||||
go s.controller()
|
||||
return s
|
||||
}
|
||||
|
||||
//// packet ordering
|
||||
func (s *packetManager) newOrderID() uint32 {
|
||||
s.packetCount++
|
||||
return s.packetCount
|
||||
}
|
||||
|
||||
// returns the next orderID without incrementing it.
|
||||
// This is used before receiving a new packet, with the allocator enabled, to associate
|
||||
// the slice allocated for the received packet with the orderID that will be used to mark
|
||||
// the allocated slices for reuse once the request is served
|
||||
func (s *packetManager) getNextOrderID() uint32 {
|
||||
return s.packetCount + 1
|
||||
}
|
||||
|
||||
type orderedRequest struct {
|
||||
requestPacket
|
||||
orderid uint32
|
||||
}
|
||||
|
||||
func (s *packetManager) newOrderedRequest(p requestPacket) orderedRequest {
|
||||
return orderedRequest{requestPacket: p, orderid: s.newOrderID()}
|
||||
}
|
||||
func (p orderedRequest) orderID() uint32 { return p.orderid }
|
||||
func (p orderedRequest) setOrderID(oid uint32) { p.orderid = oid }
|
||||
|
||||
type orderedResponse struct {
|
||||
responsePacket
|
||||
orderid uint32
|
||||
}
|
||||
|
||||
func (s *packetManager) newOrderedResponse(p responsePacket, id uint32,
|
||||
) orderedResponse {
|
||||
return orderedResponse{responsePacket: p, orderid: id}
|
||||
}
|
||||
func (p orderedResponse) orderID() uint32 { return p.orderid }
|
||||
func (p orderedResponse) setOrderID(oid uint32) { p.orderid = oid }
|
||||
|
||||
type orderedPacket interface {
|
||||
id() uint32
|
||||
orderID() uint32
|
||||
}
|
||||
type orderedPackets []orderedPacket
|
||||
|
||||
func (o orderedPackets) Sort() {
|
||||
sort.Slice(o, func(i, j int) bool {
|
||||
return o[i].orderID() < o[j].orderID()
|
||||
})
|
||||
}
|
||||
|
||||
//// packet registry
|
||||
// register incoming packets to be handled
|
||||
func (s *packetManager) incomingPacket(pkt orderedRequest) {
|
||||
s.working.Add(1)
|
||||
s.requests <- pkt
|
||||
}
|
||||
|
||||
// register outgoing packets as being ready
|
||||
func (s *packetManager) readyPacket(pkt orderedResponse) {
|
||||
s.responses <- pkt
|
||||
s.working.Done()
|
||||
}
|
||||
|
||||
// shut down packetManager controller
|
||||
func (s *packetManager) close() {
|
||||
// pause until current packets are processed
|
||||
s.working.Wait()
|
||||
close(s.fini)
|
||||
}
|
||||
|
||||
// Passed a worker function, returns a channel for incoming packets.
|
||||
// Keep process packet responses in the order they are received while
|
||||
// maximizing throughput of file transfers.
|
||||
func (s *packetManager) workerChan(runWorker func(chan orderedRequest),
|
||||
) chan orderedRequest {
|
||||
// multiple workers for faster read/writes
|
||||
rwChan := make(chan orderedRequest, SftpServerWorkerCount)
|
||||
for i := 0; i < SftpServerWorkerCount; i++ {
|
||||
runWorker(rwChan)
|
||||
}
|
||||
|
||||
// single worker to enforce sequential processing of everything else
|
||||
cmdChan := make(chan orderedRequest)
|
||||
runWorker(cmdChan)
|
||||
|
||||
pktChan := make(chan orderedRequest, SftpServerWorkerCount)
|
||||
go func() {
|
||||
for pkt := range pktChan {
|
||||
switch pkt.requestPacket.(type) {
|
||||
case *sshFxpReadPacket, *sshFxpWritePacket:
|
||||
s.incomingPacket(pkt)
|
||||
rwChan <- pkt
|
||||
continue
|
||||
case *sshFxpClosePacket:
|
||||
// wait for reads/writes to finish when file is closed
|
||||
// incomingPacket() call must occur after this
|
||||
s.working.Wait()
|
||||
}
|
||||
s.incomingPacket(pkt)
|
||||
// all non-RW use sequential cmdChan
|
||||
cmdChan <- pkt
|
||||
}
|
||||
close(rwChan)
|
||||
close(cmdChan)
|
||||
s.close()
|
||||
}()
|
||||
|
||||
return pktChan
|
||||
}
|
||||
|
||||
// process packets
|
||||
func (s *packetManager) controller() {
|
||||
for {
|
||||
select {
|
||||
case pkt := <-s.requests:
|
||||
debug("incoming id (oid): %v (%v)", pkt.id(), pkt.orderID())
|
||||
s.incoming = append(s.incoming, pkt)
|
||||
s.incoming.Sort()
|
||||
case pkt := <-s.responses:
|
||||
debug("outgoing id (oid): %v (%v)", pkt.id(), pkt.orderID())
|
||||
s.outgoing = append(s.outgoing, pkt)
|
||||
s.outgoing.Sort()
|
||||
case <-s.fini:
|
||||
return
|
||||
}
|
||||
s.maybeSendPackets()
|
||||
}
|
||||
}
|
||||
|
||||
// send as many packets as are ready
|
||||
func (s *packetManager) maybeSendPackets() {
|
||||
for {
|
||||
if len(s.outgoing) == 0 || len(s.incoming) == 0 {
|
||||
debug("break! -- outgoing: %v; incoming: %v",
|
||||
len(s.outgoing), len(s.incoming))
|
||||
break
|
||||
}
|
||||
out := s.outgoing[0]
|
||||
in := s.incoming[0]
|
||||
// debug("incoming: %v", ids(s.incoming))
|
||||
// debug("outgoing: %v", ids(s.outgoing))
|
||||
if in.orderID() == out.orderID() {
|
||||
debug("Sending packet: %v", out.id())
|
||||
s.sender.sendPacket(out.(encoding.BinaryMarshaler))
|
||||
if s.alloc != nil {
|
||||
// mark for reuse the slices allocated for this request
|
||||
s.alloc.ReleasePages(in.orderID())
|
||||
}
|
||||
// pop off heads
|
||||
copy(s.incoming, s.incoming[1:]) // shift left
|
||||
s.incoming[len(s.incoming)-1] = nil // clear last
|
||||
s.incoming = s.incoming[:len(s.incoming)-1] // remove last
|
||||
copy(s.outgoing, s.outgoing[1:]) // shift left
|
||||
s.outgoing[len(s.outgoing)-1] = nil // clear last
|
||||
s.outgoing = s.outgoing[:len(s.outgoing)-1] // remove last
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// func oids(o []orderedPacket) []uint32 {
|
||||
// res := make([]uint32, 0, len(o))
|
||||
// for _, v := range o {
|
||||
// res = append(res, v.orderId())
|
||||
// }
|
||||
// return res
|
||||
// }
|
||||
// func ids(o []orderedPacket) []uint32 {
|
||||
// res := make([]uint32, 0, len(o))
|
||||
// for _, v := range o {
|
||||
// res = append(res, v.id())
|
||||
// }
|
||||
// return res
|
||||
// }
|
||||
135
vendor/github.com/pkg/sftp/packet-typing.go
generated
vendored
Normal file
135
vendor/github.com/pkg/sftp/packet-typing.go
generated
vendored
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
package sftp
|
||||
|
||||
import (
|
||||
"encoding"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// all incoming packets
|
||||
type requestPacket interface {
|
||||
encoding.BinaryUnmarshaler
|
||||
id() uint32
|
||||
}
|
||||
|
||||
type responsePacket interface {
|
||||
encoding.BinaryMarshaler
|
||||
id() uint32
|
||||
}
|
||||
|
||||
// interfaces to group types
|
||||
type hasPath interface {
|
||||
requestPacket
|
||||
getPath() string
|
||||
}
|
||||
|
||||
type hasHandle interface {
|
||||
requestPacket
|
||||
getHandle() string
|
||||
}
|
||||
|
||||
type notReadOnly interface {
|
||||
notReadOnly()
|
||||
}
|
||||
|
||||
//// define types by adding methods
|
||||
// hasPath
|
||||
func (p *sshFxpLstatPacket) getPath() string { return p.Path }
|
||||
func (p *sshFxpStatPacket) getPath() string { return p.Path }
|
||||
func (p *sshFxpRmdirPacket) getPath() string { return p.Path }
|
||||
func (p *sshFxpReadlinkPacket) getPath() string { return p.Path }
|
||||
func (p *sshFxpRealpathPacket) getPath() string { return p.Path }
|
||||
func (p *sshFxpMkdirPacket) getPath() string { return p.Path }
|
||||
func (p *sshFxpSetstatPacket) getPath() string { return p.Path }
|
||||
func (p *sshFxpStatvfsPacket) getPath() string { return p.Path }
|
||||
func (p *sshFxpRemovePacket) getPath() string { return p.Filename }
|
||||
func (p *sshFxpRenamePacket) getPath() string { return p.Oldpath }
|
||||
func (p *sshFxpSymlinkPacket) getPath() string { return p.Targetpath }
|
||||
func (p *sshFxpOpendirPacket) getPath() string { return p.Path }
|
||||
func (p *sshFxpOpenPacket) getPath() string { return p.Path }
|
||||
|
||||
func (p *sshFxpExtendedPacketPosixRename) getPath() string { return p.Oldpath }
|
||||
func (p *sshFxpExtendedPacketHardlink) getPath() string { return p.Oldpath }
|
||||
|
||||
// getHandle
|
||||
func (p *sshFxpFstatPacket) getHandle() string { return p.Handle }
|
||||
func (p *sshFxpFsetstatPacket) getHandle() string { return p.Handle }
|
||||
func (p *sshFxpReadPacket) getHandle() string { return p.Handle }
|
||||
func (p *sshFxpWritePacket) getHandle() string { return p.Handle }
|
||||
func (p *sshFxpReaddirPacket) getHandle() string { return p.Handle }
|
||||
func (p *sshFxpClosePacket) getHandle() string { return p.Handle }
|
||||
|
||||
// notReadOnly
|
||||
func (p *sshFxpWritePacket) notReadOnly() {}
|
||||
func (p *sshFxpSetstatPacket) notReadOnly() {}
|
||||
func (p *sshFxpFsetstatPacket) notReadOnly() {}
|
||||
func (p *sshFxpRemovePacket) notReadOnly() {}
|
||||
func (p *sshFxpMkdirPacket) notReadOnly() {}
|
||||
func (p *sshFxpRmdirPacket) notReadOnly() {}
|
||||
func (p *sshFxpRenamePacket) notReadOnly() {}
|
||||
func (p *sshFxpSymlinkPacket) notReadOnly() {}
|
||||
func (p *sshFxpExtendedPacketPosixRename) notReadOnly() {}
|
||||
func (p *sshFxpExtendedPacketHardlink) notReadOnly() {}
|
||||
|
||||
// some packets with ID are missing id()
|
||||
func (p *sshFxpDataPacket) id() uint32 { return p.ID }
|
||||
func (p *sshFxpStatusPacket) id() uint32 { return p.ID }
|
||||
func (p *sshFxpStatResponse) id() uint32 { return p.ID }
|
||||
func (p *sshFxpNamePacket) id() uint32 { return p.ID }
|
||||
func (p *sshFxpHandlePacket) id() uint32 { return p.ID }
|
||||
func (p *StatVFS) id() uint32 { return p.ID }
|
||||
func (p *sshFxVersionPacket) id() uint32 { return 0 }
|
||||
|
||||
// take raw incoming packet data and build packet objects
|
||||
func makePacket(p rxPacket) (requestPacket, error) {
|
||||
var pkt requestPacket
|
||||
switch p.pktType {
|
||||
case sshFxpInit:
|
||||
pkt = &sshFxInitPacket{}
|
||||
case sshFxpLstat:
|
||||
pkt = &sshFxpLstatPacket{}
|
||||
case sshFxpOpen:
|
||||
pkt = &sshFxpOpenPacket{}
|
||||
case sshFxpClose:
|
||||
pkt = &sshFxpClosePacket{}
|
||||
case sshFxpRead:
|
||||
pkt = &sshFxpReadPacket{}
|
||||
case sshFxpWrite:
|
||||
pkt = &sshFxpWritePacket{}
|
||||
case sshFxpFstat:
|
||||
pkt = &sshFxpFstatPacket{}
|
||||
case sshFxpSetstat:
|
||||
pkt = &sshFxpSetstatPacket{}
|
||||
case sshFxpFsetstat:
|
||||
pkt = &sshFxpFsetstatPacket{}
|
||||
case sshFxpOpendir:
|
||||
pkt = &sshFxpOpendirPacket{}
|
||||
case sshFxpReaddir:
|
||||
pkt = &sshFxpReaddirPacket{}
|
||||
case sshFxpRemove:
|
||||
pkt = &sshFxpRemovePacket{}
|
||||
case sshFxpMkdir:
|
||||
pkt = &sshFxpMkdirPacket{}
|
||||
case sshFxpRmdir:
|
||||
pkt = &sshFxpRmdirPacket{}
|
||||
case sshFxpRealpath:
|
||||
pkt = &sshFxpRealpathPacket{}
|
||||
case sshFxpStat:
|
||||
pkt = &sshFxpStatPacket{}
|
||||
case sshFxpRename:
|
||||
pkt = &sshFxpRenamePacket{}
|
||||
case sshFxpReadlink:
|
||||
pkt = &sshFxpReadlinkPacket{}
|
||||
case sshFxpSymlink:
|
||||
pkt = &sshFxpSymlinkPacket{}
|
||||
case sshFxpExtended:
|
||||
pkt = &sshFxpExtendedPacket{}
|
||||
default:
|
||||
return nil, fmt.Errorf("unhandled packet type: %s", p.pktType)
|
||||
}
|
||||
if err := pkt.UnmarshalBinary(p.pktBytes); err != nil {
|
||||
// Return partially unpacked packet to allow callers to return
|
||||
// error messages appropriately with necessary id() method.
|
||||
return pkt, err
|
||||
}
|
||||
return pkt, nil
|
||||
}
|
||||
1276
vendor/github.com/pkg/sftp/packet.go
generated
vendored
Normal file
1276
vendor/github.com/pkg/sftp/packet.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
79
vendor/github.com/pkg/sftp/pool.go
generated
vendored
Normal file
79
vendor/github.com/pkg/sftp/pool.go
generated
vendored
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
package sftp
|
||||
|
||||
// bufPool provides a pool of byte-slices to be reused in various parts of the package.
|
||||
// It is safe to use concurrently through a pointer.
|
||||
type bufPool struct {
|
||||
ch chan []byte
|
||||
blen int
|
||||
}
|
||||
|
||||
func newBufPool(depth, bufLen int) *bufPool {
|
||||
return &bufPool{
|
||||
ch: make(chan []byte, depth),
|
||||
blen: bufLen,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *bufPool) Get() []byte {
|
||||
if p.blen <= 0 {
|
||||
panic("bufPool: new buffer creation length must be greater than zero")
|
||||
}
|
||||
|
||||
for {
|
||||
select {
|
||||
case b := <-p.ch:
|
||||
if cap(b) < p.blen {
|
||||
// just in case: throw away any buffer with insufficient capacity.
|
||||
continue
|
||||
}
|
||||
|
||||
return b[:p.blen]
|
||||
|
||||
default:
|
||||
return make([]byte, p.blen)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *bufPool) Put(b []byte) {
|
||||
if p == nil {
|
||||
// functional default: no reuse.
|
||||
return
|
||||
}
|
||||
|
||||
if cap(b) < p.blen || cap(b) > p.blen*2 {
|
||||
// DO NOT reuse buffers with insufficient capacity.
|
||||
// This could cause panics when resizing to p.blen.
|
||||
|
||||
// DO NOT reuse buffers with excessive capacity.
|
||||
// This could cause memory leaks.
|
||||
return
|
||||
}
|
||||
|
||||
select {
|
||||
case p.ch <- b:
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
type resChanPool chan chan result
|
||||
|
||||
func newResChanPool(depth int) resChanPool {
|
||||
return make(chan chan result, depth)
|
||||
}
|
||||
|
||||
func (p resChanPool) Get() chan result {
|
||||
select {
|
||||
case ch := <-p:
|
||||
return ch
|
||||
default:
|
||||
return make(chan result, 1)
|
||||
}
|
||||
}
|
||||
|
||||
func (p resChanPool) Put(ch chan result) {
|
||||
select {
|
||||
case p <- ch:
|
||||
default:
|
||||
}
|
||||
}
|
||||
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{}) {}
|
||||
63
vendor/github.com/pkg/sftp/request-attrs.go
generated
vendored
Normal file
63
vendor/github.com/pkg/sftp/request-attrs.go
generated
vendored
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
package sftp
|
||||
|
||||
// Methods on the Request object to make working with the Flags bitmasks and
|
||||
// Attr(ibutes) byte blob easier. Use Pflags() when working with an Open/Write
|
||||
// request and AttrFlags() and Attributes() when working with SetStat requests.
|
||||
import "os"
|
||||
|
||||
// FileOpenFlags defines Open and Write Flags. Correlate directly with with os.OpenFile flags
|
||||
// (https://golang.org/pkg/os/#pkg-constants).
|
||||
type FileOpenFlags struct {
|
||||
Read, Write, Append, Creat, Trunc, Excl bool
|
||||
}
|
||||
|
||||
func newFileOpenFlags(flags uint32) FileOpenFlags {
|
||||
return FileOpenFlags{
|
||||
Read: flags&sshFxfRead != 0,
|
||||
Write: flags&sshFxfWrite != 0,
|
||||
Append: flags&sshFxfAppend != 0,
|
||||
Creat: flags&sshFxfCreat != 0,
|
||||
Trunc: flags&sshFxfTrunc != 0,
|
||||
Excl: flags&sshFxfExcl != 0,
|
||||
}
|
||||
}
|
||||
|
||||
// Pflags converts the bitmap/uint32 from SFTP Open packet pflag values,
|
||||
// into a FileOpenFlags struct with booleans set for flags set in bitmap.
|
||||
func (r *Request) Pflags() FileOpenFlags {
|
||||
return newFileOpenFlags(r.Flags)
|
||||
}
|
||||
|
||||
// FileAttrFlags that indicate whether SFTP file attributes were passed. When a flag is
|
||||
// true the corresponding attribute should be available from the FileStat
|
||||
// object returned by Attributes method. Used with SetStat.
|
||||
type FileAttrFlags struct {
|
||||
Size, UidGid, Permissions, Acmodtime bool
|
||||
}
|
||||
|
||||
func newFileAttrFlags(flags uint32) FileAttrFlags {
|
||||
return FileAttrFlags{
|
||||
Size: (flags & sshFileXferAttrSize) != 0,
|
||||
UidGid: (flags & sshFileXferAttrUIDGID) != 0,
|
||||
Permissions: (flags & sshFileXferAttrPermissions) != 0,
|
||||
Acmodtime: (flags & sshFileXferAttrACmodTime) != 0,
|
||||
}
|
||||
}
|
||||
|
||||
// AttrFlags returns a FileAttrFlags boolean struct based on the
|
||||
// bitmap/uint32 file attribute flags from the SFTP packaet.
|
||||
func (r *Request) AttrFlags() FileAttrFlags {
|
||||
return newFileAttrFlags(r.Flags)
|
||||
}
|
||||
|
||||
// FileMode returns the Mode SFTP file attributes wrapped as os.FileMode
|
||||
func (a FileStat) FileMode() os.FileMode {
|
||||
return os.FileMode(a.Mode)
|
||||
}
|
||||
|
||||
// Attributes parses file attributes byte blob and return them in a
|
||||
// FileStat object.
|
||||
func (r *Request) Attributes() *FileStat {
|
||||
fs, _ := unmarshalFileStat(r.Flags, r.Attrs)
|
||||
return fs
|
||||
}
|
||||
54
vendor/github.com/pkg/sftp/request-errors.go
generated
vendored
Normal file
54
vendor/github.com/pkg/sftp/request-errors.go
generated
vendored
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
package sftp
|
||||
|
||||
type fxerr uint32
|
||||
|
||||
// Error types that match the SFTP's SSH_FXP_STATUS codes. Gives you more
|
||||
// direct control of the errors being sent vs. letting the library work them
|
||||
// out from the standard os/io errors.
|
||||
const (
|
||||
ErrSSHFxOk = fxerr(sshFxOk)
|
||||
ErrSSHFxEOF = fxerr(sshFxEOF)
|
||||
ErrSSHFxNoSuchFile = fxerr(sshFxNoSuchFile)
|
||||
ErrSSHFxPermissionDenied = fxerr(sshFxPermissionDenied)
|
||||
ErrSSHFxFailure = fxerr(sshFxFailure)
|
||||
ErrSSHFxBadMessage = fxerr(sshFxBadMessage)
|
||||
ErrSSHFxNoConnection = fxerr(sshFxNoConnection)
|
||||
ErrSSHFxConnectionLost = fxerr(sshFxConnectionLost)
|
||||
ErrSSHFxOpUnsupported = fxerr(sshFxOPUnsupported)
|
||||
)
|
||||
|
||||
// Deprecated error types, these are aliases for the new ones, please use the new ones directly
|
||||
const (
|
||||
ErrSshFxOk = ErrSSHFxOk
|
||||
ErrSshFxEof = ErrSSHFxEOF
|
||||
ErrSshFxNoSuchFile = ErrSSHFxNoSuchFile
|
||||
ErrSshFxPermissionDenied = ErrSSHFxPermissionDenied
|
||||
ErrSshFxFailure = ErrSSHFxFailure
|
||||
ErrSshFxBadMessage = ErrSSHFxBadMessage
|
||||
ErrSshFxNoConnection = ErrSSHFxNoConnection
|
||||
ErrSshFxConnectionLost = ErrSSHFxConnectionLost
|
||||
ErrSshFxOpUnsupported = ErrSSHFxOpUnsupported
|
||||
)
|
||||
|
||||
func (e fxerr) Error() string {
|
||||
switch e {
|
||||
case ErrSSHFxOk:
|
||||
return "OK"
|
||||
case ErrSSHFxEOF:
|
||||
return "EOF"
|
||||
case ErrSSHFxNoSuchFile:
|
||||
return "no such file"
|
||||
case ErrSSHFxPermissionDenied:
|
||||
return "permission denied"
|
||||
case ErrSSHFxBadMessage:
|
||||
return "bad message"
|
||||
case ErrSSHFxNoConnection:
|
||||
return "no connection"
|
||||
case ErrSSHFxConnectionLost:
|
||||
return "connection lost"
|
||||
case ErrSSHFxOpUnsupported:
|
||||
return "operation unsupported"
|
||||
default:
|
||||
return "failure"
|
||||
}
|
||||
}
|
||||
666
vendor/github.com/pkg/sftp/request-example.go
generated
vendored
Normal file
666
vendor/github.com/pkg/sftp/request-example.go
generated
vendored
Normal file
|
|
@ -0,0 +1,666 @@
|
|||
package sftp
|
||||
|
||||
// This serves as an example of how to implement the request server handler as
|
||||
// well as a dummy backend for testing. It implements an in-memory backend that
|
||||
// works as a very simple filesystem with simple flat key-value lookup system.
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"os"
|
||||
"path"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
const maxSymlinkFollows = 5
|
||||
|
||||
var errTooManySymlinks = errors.New("too many symbolic links")
|
||||
|
||||
// InMemHandler returns a Hanlders object with the test handlers.
|
||||
func InMemHandler() Handlers {
|
||||
root := &root{
|
||||
rootFile: &memFile{name: "/", modtime: time.Now(), isdir: true},
|
||||
files: make(map[string]*memFile),
|
||||
}
|
||||
return Handlers{root, root, root, root}
|
||||
}
|
||||
|
||||
// Example Handlers
|
||||
func (fs *root) Fileread(r *Request) (io.ReaderAt, error) {
|
||||
flags := r.Pflags()
|
||||
if !flags.Read {
|
||||
// sanity check
|
||||
return nil, os.ErrInvalid
|
||||
}
|
||||
|
||||
return fs.OpenFile(r)
|
||||
}
|
||||
|
||||
func (fs *root) Filewrite(r *Request) (io.WriterAt, error) {
|
||||
flags := r.Pflags()
|
||||
if !flags.Write {
|
||||
// sanity check
|
||||
return nil, os.ErrInvalid
|
||||
}
|
||||
|
||||
return fs.OpenFile(r)
|
||||
}
|
||||
|
||||
func (fs *root) OpenFile(r *Request) (WriterAtReaderAt, error) {
|
||||
if fs.mockErr != nil {
|
||||
return nil, fs.mockErr
|
||||
}
|
||||
_ = r.WithContext(r.Context()) // initialize context for deadlock testing
|
||||
|
||||
fs.mu.Lock()
|
||||
defer fs.mu.Unlock()
|
||||
|
||||
return fs.openfile(r.Filepath, r.Flags)
|
||||
}
|
||||
|
||||
func (fs *root) putfile(pathname string, file *memFile) error {
|
||||
pathname, err := fs.canonName(pathname)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(pathname, "/") {
|
||||
return os.ErrInvalid
|
||||
}
|
||||
|
||||
if _, err := fs.lfetch(pathname); err != os.ErrNotExist {
|
||||
return os.ErrExist
|
||||
}
|
||||
|
||||
file.name = pathname
|
||||
fs.files[pathname] = file
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (fs *root) openfile(pathname string, flags uint32) (*memFile, error) {
|
||||
pflags := newFileOpenFlags(flags)
|
||||
|
||||
file, err := fs.fetch(pathname)
|
||||
if err == os.ErrNotExist {
|
||||
if !pflags.Creat {
|
||||
return nil, os.ErrNotExist
|
||||
}
|
||||
|
||||
var count int
|
||||
// You can create files through dangling symlinks.
|
||||
link, err := fs.lfetch(pathname)
|
||||
for err == nil && link.symlink != "" {
|
||||
if pflags.Excl {
|
||||
// unless you also passed in O_EXCL
|
||||
return nil, os.ErrInvalid
|
||||
}
|
||||
|
||||
if count++; count > maxSymlinkFollows {
|
||||
return nil, errTooManySymlinks
|
||||
}
|
||||
|
||||
pathname = link.symlink
|
||||
link, err = fs.lfetch(pathname)
|
||||
}
|
||||
|
||||
file := &memFile{
|
||||
modtime: time.Now(),
|
||||
}
|
||||
|
||||
if err := fs.putfile(pathname, file); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return file, nil
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if pflags.Creat && pflags.Excl {
|
||||
return nil, os.ErrExist
|
||||
}
|
||||
|
||||
if file.IsDir() {
|
||||
return nil, os.ErrInvalid
|
||||
}
|
||||
|
||||
if pflags.Trunc {
|
||||
if err := file.Truncate(0); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return file, nil
|
||||
}
|
||||
|
||||
func (fs *root) Filecmd(r *Request) error {
|
||||
if fs.mockErr != nil {
|
||||
return fs.mockErr
|
||||
}
|
||||
_ = r.WithContext(r.Context()) // initialize context for deadlock testing
|
||||
|
||||
fs.mu.Lock()
|
||||
defer fs.mu.Unlock()
|
||||
|
||||
switch r.Method {
|
||||
case "Setstat":
|
||||
file, err := fs.openfile(r.Filepath, sshFxfWrite)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if r.AttrFlags().Size {
|
||||
return file.Truncate(int64(r.Attributes().Size))
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
case "Rename":
|
||||
// SFTP-v2: "It is an error if there already exists a file with the name specified by newpath."
|
||||
// This varies from the POSIX specification, which allows limited replacement of target files.
|
||||
if fs.exists(r.Target) {
|
||||
return os.ErrExist
|
||||
}
|
||||
|
||||
return fs.rename(r.Filepath, r.Target)
|
||||
|
||||
case "Rmdir":
|
||||
return fs.rmdir(r.Filepath)
|
||||
|
||||
case "Remove":
|
||||
// IEEE 1003.1 remove explicitly can unlink files and remove empty directories.
|
||||
// We use instead here the semantics of unlink, which is allowed to be restricted against directories.
|
||||
return fs.unlink(r.Filepath)
|
||||
|
||||
case "Mkdir":
|
||||
return fs.mkdir(r.Filepath)
|
||||
|
||||
case "Link":
|
||||
return fs.link(r.Filepath, r.Target)
|
||||
|
||||
case "Symlink":
|
||||
// NOTE: r.Filepath is the target, and r.Target is the linkpath.
|
||||
return fs.symlink(r.Filepath, r.Target)
|
||||
}
|
||||
|
||||
return errors.New("unsupported")
|
||||
}
|
||||
|
||||
func (fs *root) rename(oldpath, newpath string) error {
|
||||
file, err := fs.lfetch(oldpath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
newpath, err = fs.canonName(newpath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(newpath, "/") {
|
||||
return os.ErrInvalid
|
||||
}
|
||||
|
||||
target, err := fs.lfetch(newpath)
|
||||
if err != os.ErrNotExist {
|
||||
if target == file {
|
||||
// IEEE 1003.1: if oldpath and newpath are the same directory entry,
|
||||
// then return no error, and perform no further action.
|
||||
return nil
|
||||
}
|
||||
|
||||
switch {
|
||||
case file.IsDir():
|
||||
// IEEE 1003.1: if oldpath is a directory, and newpath exists,
|
||||
// then newpath must be a directory, and empty.
|
||||
// It is to be removed prior to rename.
|
||||
if err := fs.rmdir(newpath); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
case target.IsDir():
|
||||
// IEEE 1003.1: if oldpath is not a directory, and newpath exists,
|
||||
// then newpath may not be a directory.
|
||||
return syscall.EISDIR
|
||||
}
|
||||
}
|
||||
|
||||
fs.files[newpath] = file
|
||||
|
||||
if file.IsDir() {
|
||||
dirprefix := file.name + "/"
|
||||
|
||||
for name, file := range fs.files {
|
||||
if strings.HasPrefix(name, dirprefix) {
|
||||
newname := path.Join(newpath, strings.TrimPrefix(name, dirprefix))
|
||||
|
||||
fs.files[newname] = file
|
||||
file.name = newname
|
||||
delete(fs.files, name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
file.name = newpath
|
||||
delete(fs.files, oldpath)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (fs *root) PosixRename(r *Request) error {
|
||||
if fs.mockErr != nil {
|
||||
return fs.mockErr
|
||||
}
|
||||
_ = r.WithContext(r.Context()) // initialize context for deadlock testing
|
||||
|
||||
fs.mu.Lock()
|
||||
defer fs.mu.Unlock()
|
||||
|
||||
return fs.rename(r.Filepath, r.Target)
|
||||
}
|
||||
|
||||
func (fs *root) StatVFS(r *Request) (*StatVFS, error) {
|
||||
if fs.mockErr != nil {
|
||||
return nil, fs.mockErr
|
||||
}
|
||||
|
||||
return getStatVFSForPath(r.Filepath)
|
||||
}
|
||||
|
||||
func (fs *root) mkdir(pathname string) error {
|
||||
dir := &memFile{
|
||||
modtime: time.Now(),
|
||||
isdir: true,
|
||||
}
|
||||
|
||||
return fs.putfile(pathname, dir)
|
||||
}
|
||||
|
||||
func (fs *root) rmdir(pathname string) error {
|
||||
// IEEE 1003.1: If pathname is a symlink, then rmdir should fail with ENOTDIR.
|
||||
dir, err := fs.lfetch(pathname)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !dir.IsDir() {
|
||||
return syscall.ENOTDIR
|
||||
}
|
||||
|
||||
// use the dir‘s internal name not the pathname we passed in.
|
||||
// the dir.name is always the canonical name of a directory.
|
||||
pathname = dir.name
|
||||
|
||||
for name := range fs.files {
|
||||
if path.Dir(name) == pathname {
|
||||
return errors.New("directory not empty")
|
||||
}
|
||||
}
|
||||
|
||||
delete(fs.files, pathname)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (fs *root) link(oldpath, newpath string) error {
|
||||
file, err := fs.lfetch(oldpath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if file.IsDir() {
|
||||
return errors.New("hard link not allowed for directory")
|
||||
}
|
||||
|
||||
return fs.putfile(newpath, file)
|
||||
}
|
||||
|
||||
// symlink() creates a symbolic link named `linkpath` which contains the string `target`.
|
||||
// NOTE! This would be called with `symlink(req.Filepath, req.Target)` due to different semantics.
|
||||
func (fs *root) symlink(target, linkpath string) error {
|
||||
link := &memFile{
|
||||
modtime: time.Now(),
|
||||
symlink: target,
|
||||
}
|
||||
|
||||
return fs.putfile(linkpath, link)
|
||||
}
|
||||
|
||||
func (fs *root) unlink(pathname string) error {
|
||||
// does not follow symlinks!
|
||||
file, err := fs.lfetch(pathname)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if file.IsDir() {
|
||||
// IEEE 1003.1: implementations may opt out of allowing the unlinking of directories.
|
||||
// SFTP-v2: SSH_FXP_REMOVE may not remove directories.
|
||||
return os.ErrInvalid
|
||||
}
|
||||
|
||||
// DO NOT use the file’s internal name.
|
||||
// because of hard-links files cannot have a single canonical name.
|
||||
delete(fs.files, pathname)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type listerat []os.FileInfo
|
||||
|
||||
// Modeled after strings.Reader's ReadAt() implementation
|
||||
func (f listerat) ListAt(ls []os.FileInfo, offset int64) (int, error) {
|
||||
var n int
|
||||
if offset >= int64(len(f)) {
|
||||
return 0, io.EOF
|
||||
}
|
||||
n = copy(ls, f[offset:])
|
||||
if n < len(ls) {
|
||||
return n, io.EOF
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func (fs *root) Filelist(r *Request) (ListerAt, error) {
|
||||
if fs.mockErr != nil {
|
||||
return nil, fs.mockErr
|
||||
}
|
||||
_ = r.WithContext(r.Context()) // initialize context for deadlock testing
|
||||
|
||||
fs.mu.Lock()
|
||||
defer fs.mu.Unlock()
|
||||
|
||||
switch r.Method {
|
||||
case "List":
|
||||
files, err := fs.readdir(r.Filepath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return listerat(files), nil
|
||||
|
||||
case "Stat":
|
||||
file, err := fs.fetch(r.Filepath)
|
||||
if err != nil {
|
||||
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")
|
||||
}
|
||||
|
||||
func (fs *root) readdir(pathname string) ([]os.FileInfo, error) {
|
||||
dir, err := fs.fetch(pathname)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !dir.IsDir() {
|
||||
return nil, syscall.ENOTDIR
|
||||
}
|
||||
|
||||
var files []os.FileInfo
|
||||
|
||||
for name, file := range fs.files {
|
||||
if path.Dir(name) == dir.name {
|
||||
files = append(files, file)
|
||||
}
|
||||
}
|
||||
|
||||
sort.Slice(files, func(i, j int) bool { return files[i].Name() < files[j].Name() })
|
||||
|
||||
return files, nil
|
||||
}
|
||||
|
||||
func (fs *root) readlink(pathname string) (string, error) {
|
||||
file, err := fs.lfetch(pathname)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if file.symlink == "" {
|
||||
return "", os.ErrInvalid
|
||||
}
|
||||
|
||||
return file.symlink, nil
|
||||
}
|
||||
|
||||
// implements LstatFileLister interface
|
||||
func (fs *root) Lstat(r *Request) (ListerAt, error) {
|
||||
if fs.mockErr != nil {
|
||||
return nil, fs.mockErr
|
||||
}
|
||||
_ = r.WithContext(r.Context()) // initialize context for deadlock testing
|
||||
|
||||
fs.mu.Lock()
|
||||
defer fs.mu.Unlock()
|
||||
|
||||
file, err := fs.lfetch(r.Filepath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
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
|
||||
|
||||
mu sync.Mutex
|
||||
files map[string]*memFile
|
||||
}
|
||||
|
||||
// Set a mocked error that the next handler call will return.
|
||||
// Set to nil to reset for no error.
|
||||
func (fs *root) returnErr(err error) {
|
||||
fs.mockErr = err
|
||||
}
|
||||
|
||||
func (fs *root) lfetch(path string) (*memFile, error) {
|
||||
if path == "/" {
|
||||
return fs.rootFile, nil
|
||||
}
|
||||
|
||||
file, ok := fs.files[path]
|
||||
if file == nil {
|
||||
if ok {
|
||||
delete(fs.files, path)
|
||||
}
|
||||
|
||||
return nil, os.ErrNotExist
|
||||
}
|
||||
|
||||
return file, nil
|
||||
}
|
||||
|
||||
// canonName returns the “canonical” name of a file, that is:
|
||||
// if the directory of the pathname is a symlink, it follows that symlink to the valid directory name.
|
||||
// this is relatively easy, since `dir.name` will be the only valid canonical path for a directory.
|
||||
func (fs *root) canonName(pathname string) (string, error) {
|
||||
dirname, filename := path.Dir(pathname), path.Base(pathname)
|
||||
|
||||
dir, err := fs.fetch(dirname)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if !dir.IsDir() {
|
||||
return "", syscall.ENOTDIR
|
||||
}
|
||||
|
||||
return path.Join(dir.name, filename), nil
|
||||
}
|
||||
|
||||
func (fs *root) exists(path string) bool {
|
||||
path, err := fs.canonName(path)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
_, err = fs.lfetch(path)
|
||||
|
||||
return err != os.ErrNotExist
|
||||
}
|
||||
|
||||
func (fs *root) fetch(path string) (*memFile, error) {
|
||||
file, err := fs.lfetch(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var count int
|
||||
for file.symlink != "" {
|
||||
if count++; count > maxSymlinkFollows {
|
||||
return nil, errTooManySymlinks
|
||||
}
|
||||
|
||||
file, err = fs.lfetch(file.symlink)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return file, nil
|
||||
}
|
||||
|
||||
// Implements os.FileInfo, io.ReaderAt and io.WriterAt interfaces.
|
||||
// These are the 3 interfaces necessary for the Handlers.
|
||||
// Implements the optional interface TransferError.
|
||||
type memFile struct {
|
||||
name string
|
||||
modtime time.Time
|
||||
symlink string
|
||||
isdir bool
|
||||
|
||||
mu sync.RWMutex
|
||||
content []byte
|
||||
err error
|
||||
}
|
||||
|
||||
// These are helper functions, they must be called while holding the memFile.mu mutex
|
||||
func (f *memFile) size() int64 { return int64(len(f.content)) }
|
||||
func (f *memFile) grow(n int64) { f.content = append(f.content, make([]byte, n)...) }
|
||||
|
||||
// Have memFile fulfill os.FileInfo interface
|
||||
func (f *memFile) Name() string { return path.Base(f.name) }
|
||||
func (f *memFile) Size() int64 {
|
||||
f.mu.Lock()
|
||||
defer f.mu.Unlock()
|
||||
|
||||
return f.size()
|
||||
}
|
||||
func (f *memFile) Mode() os.FileMode {
|
||||
if f.isdir {
|
||||
return os.FileMode(0755) | os.ModeDir
|
||||
}
|
||||
if f.symlink != "" {
|
||||
return os.FileMode(0777) | os.ModeSymlink
|
||||
}
|
||||
return os.FileMode(0644)
|
||||
}
|
||||
func (f *memFile) ModTime() time.Time { return f.modtime }
|
||||
func (f *memFile) IsDir() bool { return f.isdir }
|
||||
func (f *memFile) Sys() interface{} {
|
||||
return fakeFileInfoSys()
|
||||
}
|
||||
|
||||
func (f *memFile) ReadAt(b []byte, off int64) (int, error) {
|
||||
f.mu.Lock()
|
||||
defer f.mu.Unlock()
|
||||
|
||||
if f.err != nil {
|
||||
return 0, f.err
|
||||
}
|
||||
|
||||
if off < 0 {
|
||||
return 0, errors.New("memFile.ReadAt: negative offset")
|
||||
}
|
||||
|
||||
if off >= f.size() {
|
||||
return 0, io.EOF
|
||||
}
|
||||
|
||||
n := copy(b, f.content[off:])
|
||||
if n < len(b) {
|
||||
return n, io.EOF
|
||||
}
|
||||
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func (f *memFile) WriteAt(b []byte, off int64) (int, error) {
|
||||
// fmt.Println(string(p), off)
|
||||
// mimic write delays, should be optional
|
||||
time.Sleep(time.Microsecond * time.Duration(len(b)))
|
||||
|
||||
f.mu.Lock()
|
||||
defer f.mu.Unlock()
|
||||
|
||||
if f.err != nil {
|
||||
return 0, f.err
|
||||
}
|
||||
|
||||
grow := int64(len(b)) + off - f.size()
|
||||
if grow > 0 {
|
||||
f.grow(grow)
|
||||
}
|
||||
|
||||
return copy(f.content[off:], b), nil
|
||||
}
|
||||
|
||||
func (f *memFile) Truncate(size int64) error {
|
||||
f.mu.Lock()
|
||||
defer f.mu.Unlock()
|
||||
|
||||
if f.err != nil {
|
||||
return f.err
|
||||
}
|
||||
|
||||
grow := size - f.size()
|
||||
if grow <= 0 {
|
||||
f.content = f.content[:size]
|
||||
} else {
|
||||
f.grow(grow)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *memFile) TransferError(err error) {
|
||||
f.mu.Lock()
|
||||
defer f.mu.Unlock()
|
||||
|
||||
f.err = err
|
||||
}
|
||||
121
vendor/github.com/pkg/sftp/request-interfaces.go
generated
vendored
Normal file
121
vendor/github.com/pkg/sftp/request-interfaces.go
generated
vendored
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
package sftp
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
||||
// WriterAtReaderAt defines the interface to return when a file is to
|
||||
// be opened for reading and writing
|
||||
type WriterAtReaderAt interface {
|
||||
io.WriterAt
|
||||
io.ReaderAt
|
||||
}
|
||||
|
||||
// Interfaces are differentiated based on required returned values.
|
||||
// All input arguments are to be pulled from Request (the only arg).
|
||||
|
||||
// The Handler interfaces all take the Request object as its only argument.
|
||||
// All the data you should need to handle the call are in the Request object.
|
||||
// The request.Method attribute is initially the most important one as it
|
||||
// determines which Handler gets called.
|
||||
|
||||
// FileReader should return an io.ReaderAt for the filepath
|
||||
// Note in cases of an error, the error text will be sent to the client.
|
||||
// Called for Methods: Get
|
||||
type FileReader interface {
|
||||
Fileread(*Request) (io.ReaderAt, error)
|
||||
}
|
||||
|
||||
// FileWriter should return an io.WriterAt for the filepath.
|
||||
//
|
||||
// The request server code will call Close() on the returned io.WriterAt
|
||||
// ojbect if an io.Closer type assertion succeeds.
|
||||
// Note in cases of an error, the error text will be sent to the client.
|
||||
// Note when receiving an Append flag it is important to not open files using
|
||||
// O_APPEND if you plan to use WriteAt, as they conflict.
|
||||
// Called for Methods: Put, Open
|
||||
type FileWriter interface {
|
||||
Filewrite(*Request) (io.WriterAt, error)
|
||||
}
|
||||
|
||||
// OpenFileWriter is a FileWriter that implements the generic OpenFile method.
|
||||
// You need to implement this optional interface if you want to be able
|
||||
// to read and write from/to the same handle.
|
||||
// Called for Methods: Open
|
||||
type OpenFileWriter interface {
|
||||
FileWriter
|
||||
OpenFile(*Request) (WriterAtReaderAt, error)
|
||||
}
|
||||
|
||||
// FileCmder should return an error
|
||||
// Note in cases of an error, the error text will be sent to the client.
|
||||
// Called for Methods: Setstat, Rename, Rmdir, Mkdir, Link, Symlink, Remove
|
||||
type FileCmder interface {
|
||||
Filecmd(*Request) error
|
||||
}
|
||||
|
||||
// PosixRenameFileCmder is a FileCmder that implements the PosixRename method.
|
||||
// If this interface is implemented PosixRename requests will call it
|
||||
// otherwise they will be handled in the same way as Rename
|
||||
type PosixRenameFileCmder interface {
|
||||
FileCmder
|
||||
PosixRename(*Request) error
|
||||
}
|
||||
|
||||
// StatVFSFileCmder is a FileCmder that implements the StatVFS method.
|
||||
// You need to implement this interface if you want to handle statvfs requests.
|
||||
// Please also be sure that the statvfs@openssh.com extension is enabled
|
||||
type StatVFSFileCmder interface {
|
||||
FileCmder
|
||||
StatVFS(*Request) (*StatVFS, error)
|
||||
}
|
||||
|
||||
// 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
|
||||
type FileLister interface {
|
||||
Filelist(*Request) (ListerAt, error)
|
||||
}
|
||||
|
||||
// LstatFileLister is a FileLister that implements the Lstat method.
|
||||
// If this interface is implemented Lstat requests will call it
|
||||
// otherwise they will be handled in the same way as Stat
|
||||
type LstatFileLister interface {
|
||||
FileLister
|
||||
Lstat(*Request) (ListerAt, error)
|
||||
}
|
||||
|
||||
// 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.
|
||||
// You have to return an absolute POSIX path.
|
||||
type RealPathFileLister interface {
|
||||
FileLister
|
||||
RealPath(string) string
|
||||
}
|
||||
|
||||
// NameLookupFileLister is a FileLister that implmeents the LookupUsername and LookupGroupName methods.
|
||||
// If this interface is implemented, then longname ls formatting will use these to convert usernames and groupnames.
|
||||
type NameLookupFileLister interface {
|
||||
FileLister
|
||||
LookupUserName(string) string
|
||||
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).
|
||||
// The copy() builtin is best for the copying.
|
||||
// Note in cases of an error, the error text will be sent to the client.
|
||||
type ListerAt interface {
|
||||
ListAt([]os.FileInfo, int64) (int, error)
|
||||
}
|
||||
|
||||
// TransferError is an optional interface that readerAt and writerAt
|
||||
// can implement to be notified about the error causing Serve() to exit
|
||||
// with the request still open
|
||||
type TransferError interface {
|
||||
TransferError(err error)
|
||||
}
|
||||
34
vendor/github.com/pkg/sftp/request-plan9.go
generated
vendored
Normal file
34
vendor/github.com/pkg/sftp/request-plan9.go
generated
vendored
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
// +build plan9
|
||||
|
||||
package sftp
|
||||
|
||||
import (
|
||||
"path"
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func fakeFileInfoSys() interface{} {
|
||||
return &syscall.Dir{}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
53
vendor/github.com/pkg/sftp/request-readme.md
generated
vendored
Normal file
53
vendor/github.com/pkg/sftp/request-readme.md
generated
vendored
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
# Request Based SFTP API
|
||||
|
||||
The request based API allows for custom backends in a way similar to the http
|
||||
package. In order to create a backend you need to implement 4 handler
|
||||
interfaces; one for reading, one for writing, one for misc commands and one for
|
||||
listing files. Each has 1 required method and in each case those methods take
|
||||
the Request as the only parameter and they each return something different.
|
||||
These 4 interfaces are enough to handle all the SFTP traffic in a simplified
|
||||
manner.
|
||||
|
||||
The Request structure has 5 public fields which you will deal with.
|
||||
|
||||
- Method (string) - string name of incoming call
|
||||
- Filepath (string) - POSIX path of file to act on
|
||||
- Flags (uint32) - 32bit bitmask value of file open/create flags
|
||||
- Attrs ([]byte) - byte string of file attribute data
|
||||
- Target (string) - target path for renames and sym-links
|
||||
|
||||
Below are the methods and a brief description of what they need to do.
|
||||
|
||||
### Fileread(*Request) (io.Reader, error)
|
||||
|
||||
Handler for "Get" method and returns an io.Reader for the file which the server
|
||||
then sends to the client.
|
||||
|
||||
### Filewrite(*Request) (io.Writer, error)
|
||||
|
||||
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
|
||||
details.
|
||||
|
||||
### Filecmd(*Request) error
|
||||
|
||||
Handles "SetStat", "Rename", "Rmdir", "Mkdir" and "Symlink" methods. Makes the
|
||||
appropriate changes and returns nil for success or an filesystem like error
|
||||
(eg. os.ErrNotExist). The attributes are currently propagated in their raw form
|
||||
([]byte) and will need to be unmarshalled to be useful. See the respond method
|
||||
on sshFxpSetstatPacket for example of you might want to do this.
|
||||
|
||||
### Fileinfo(*Request) ([]os.FileInfo, error)
|
||||
|
||||
Handles "List", "Stat", "Readlink" methods. Gathers/creates FileInfo structs
|
||||
with the data on the files and returns in a list (list of 1 for Stat and
|
||||
Readlink).
|
||||
|
||||
|
||||
## TODO
|
||||
|
||||
- Add support for API users to see trace/debugging info of what is going on
|
||||
inside SFTP server.
|
||||
- Unmarshal the file attributes into a structure on the Request object.
|
||||
304
vendor/github.com/pkg/sftp/request-server.go
generated
vendored
Normal file
304
vendor/github.com/pkg/sftp/request-server.go
generated
vendored
Normal file
|
|
@ -0,0 +1,304 @@
|
|||
package sftp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var maxTxPacket uint32 = 1 << 15
|
||||
|
||||
// Handlers contains the 4 SFTP server request handlers.
|
||||
type Handlers struct {
|
||||
FileGet FileReader
|
||||
FilePut FileWriter
|
||||
FileCmd FileCmder
|
||||
FileList FileLister
|
||||
}
|
||||
|
||||
// RequestServer abstracts the sftp protocol with an http request-like protocol
|
||||
type RequestServer struct {
|
||||
Handlers Handlers
|
||||
|
||||
*serverConn
|
||||
pktMgr *packetManager
|
||||
|
||||
mu sync.RWMutex
|
||||
handleCount int
|
||||
openRequests map[string]*Request
|
||||
}
|
||||
|
||||
// A RequestServerOption is a function which applies configuration to a RequestServer.
|
||||
type RequestServerOption func(*RequestServer)
|
||||
|
||||
// WithRSAllocator enable the allocator.
|
||||
// After processing a packet we keep in memory the allocated slices
|
||||
// and we reuse them for new packets.
|
||||
// The allocator is experimental
|
||||
func WithRSAllocator() RequestServerOption {
|
||||
return func(rs *RequestServer) {
|
||||
alloc := newAllocator()
|
||||
rs.pktMgr.alloc = alloc
|
||||
rs.conn.alloc = alloc
|
||||
}
|
||||
}
|
||||
|
||||
// NewRequestServer creates/allocates/returns new RequestServer.
|
||||
// Normally there will be one server per user-session.
|
||||
func NewRequestServer(rwc io.ReadWriteCloser, h Handlers, options ...RequestServerOption) *RequestServer {
|
||||
svrConn := &serverConn{
|
||||
conn: conn{
|
||||
Reader: rwc,
|
||||
WriteCloser: rwc,
|
||||
},
|
||||
}
|
||||
rs := &RequestServer{
|
||||
Handlers: h,
|
||||
|
||||
serverConn: svrConn,
|
||||
pktMgr: newPktMgr(svrConn),
|
||||
|
||||
openRequests: make(map[string]*Request),
|
||||
}
|
||||
|
||||
for _, o := range options {
|
||||
o(rs)
|
||||
}
|
||||
return rs
|
||||
}
|
||||
|
||||
// New Open packet/Request
|
||||
func (rs *RequestServer) nextRequest(r *Request) string {
|
||||
rs.mu.Lock()
|
||||
defer rs.mu.Unlock()
|
||||
|
||||
rs.handleCount++
|
||||
|
||||
r.handle = strconv.Itoa(rs.handleCount)
|
||||
rs.openRequests[r.handle] = r
|
||||
|
||||
return r.handle
|
||||
}
|
||||
|
||||
// Returns Request from openRequests, bool is false if it is missing.
|
||||
//
|
||||
// The Requests in openRequests work essentially as open file descriptors that
|
||||
// you can do different things with. What you are doing with it are denoted by
|
||||
// the first packet of that type (read/write/etc).
|
||||
func (rs *RequestServer) getRequest(handle string) (*Request, bool) {
|
||||
rs.mu.RLock()
|
||||
defer rs.mu.RUnlock()
|
||||
|
||||
r, ok := rs.openRequests[handle]
|
||||
return r, ok
|
||||
}
|
||||
|
||||
// Close the Request and clear from openRequests map
|
||||
func (rs *RequestServer) closeRequest(handle string) error {
|
||||
rs.mu.Lock()
|
||||
defer rs.mu.Unlock()
|
||||
|
||||
if r, ok := rs.openRequests[handle]; ok {
|
||||
delete(rs.openRequests, handle)
|
||||
return r.close()
|
||||
}
|
||||
|
||||
return EBADF
|
||||
}
|
||||
|
||||
// Close the read/write/closer to trigger exiting the main server loop
|
||||
func (rs *RequestServer) Close() error { return rs.conn.Close() }
|
||||
|
||||
func (rs *RequestServer) serveLoop(pktChan chan<- orderedRequest) error {
|
||||
defer close(pktChan) // shuts down sftpServerWorkers
|
||||
|
||||
var err error
|
||||
var pkt requestPacket
|
||||
var pktType uint8
|
||||
var pktBytes []byte
|
||||
|
||||
for {
|
||||
pktType, pktBytes, err = rs.serverConn.recvPacket(rs.pktMgr.getNextOrderID())
|
||||
if err != nil {
|
||||
// we don't care about releasing allocated pages here, the server will quit and the allocator freed
|
||||
return err
|
||||
}
|
||||
|
||||
pkt, err = makePacket(rxPacket{fxp(pktType), pktBytes})
|
||||
if err != nil {
|
||||
switch {
|
||||
case errors.Is(err, errUnknownExtendedPacket):
|
||||
// do nothing
|
||||
default:
|
||||
debug("makePacket err: %v", err)
|
||||
rs.conn.Close() // shuts down recvPacket
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
pktChan <- rs.pktMgr.newOrderedRequest(pkt)
|
||||
}
|
||||
}
|
||||
|
||||
// Serve requests for user session
|
||||
func (rs *RequestServer) Serve() error {
|
||||
defer func() {
|
||||
if rs.pktMgr.alloc != nil {
|
||||
rs.pktMgr.alloc.Free()
|
||||
}
|
||||
}()
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
var wg sync.WaitGroup
|
||||
runWorker := func(ch chan orderedRequest) {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
if err := rs.packetWorker(ctx, ch); err != nil {
|
||||
rs.conn.Close() // shuts down recvPacket
|
||||
}
|
||||
}()
|
||||
}
|
||||
pktChan := rs.pktMgr.workerChan(runWorker)
|
||||
|
||||
err := rs.serveLoop(pktChan)
|
||||
|
||||
wg.Wait() // wait for all workers to exit
|
||||
|
||||
rs.mu.Lock()
|
||||
defer rs.mu.Unlock()
|
||||
|
||||
// make sure all open requests are properly closed
|
||||
// (eg. possible on dropped connections, client crashes, etc.)
|
||||
for handle, req := range rs.openRequests {
|
||||
if err == io.EOF {
|
||||
err = io.ErrUnexpectedEOF
|
||||
}
|
||||
req.transferError(err)
|
||||
|
||||
delete(rs.openRequests, handle)
|
||||
req.close()
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (rs *RequestServer) packetWorker(ctx context.Context, pktChan chan orderedRequest) error {
|
||||
for pkt := range pktChan {
|
||||
orderID := pkt.orderID()
|
||||
if epkt, ok := pkt.requestPacket.(*sshFxpExtendedPacket); ok {
|
||||
if epkt.SpecificPacket != nil {
|
||||
pkt.requestPacket = epkt.SpecificPacket
|
||||
}
|
||||
}
|
||||
|
||||
var rpkt responsePacket
|
||||
switch pkt := pkt.requestPacket.(type) {
|
||||
case *sshFxInitPacket:
|
||||
rpkt = &sshFxVersionPacket{Version: sftpProtocolVersion, Extensions: sftpExtensions}
|
||||
case *sshFxpClosePacket:
|
||||
handle := pkt.getHandle()
|
||||
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 {
|
||||
realPath = cleanPath(pkt.getPath())
|
||||
}
|
||||
rpkt = cleanPacketPath(pkt, realPath)
|
||||
case *sshFxpOpendirPacket:
|
||||
request := requestFromPacket(ctx, pkt)
|
||||
handle := rs.nextRequest(request)
|
||||
rpkt = request.opendir(rs.Handlers, pkt)
|
||||
if _, ok := rpkt.(*sshFxpHandlePacket); !ok {
|
||||
// if we return an error we have to remove the handle from the active ones
|
||||
rs.closeRequest(handle)
|
||||
}
|
||||
case *sshFxpOpenPacket:
|
||||
request := requestFromPacket(ctx, pkt)
|
||||
handle := rs.nextRequest(request)
|
||||
rpkt = request.open(rs.Handlers, pkt)
|
||||
if _, ok := rpkt.(*sshFxpHandlePacket); !ok {
|
||||
// if we return an error we have to remove the handle from the active ones
|
||||
rs.closeRequest(handle)
|
||||
}
|
||||
case *sshFxpFstatPacket:
|
||||
handle := pkt.getHandle()
|
||||
request, ok := rs.getRequest(handle)
|
||||
if !ok {
|
||||
rpkt = statusFromError(pkt.ID, EBADF)
|
||||
} else {
|
||||
request = NewRequest("Stat", request.Filepath)
|
||||
rpkt = request.call(rs.Handlers, pkt, rs.pktMgr.alloc, orderID)
|
||||
}
|
||||
case *sshFxpFsetstatPacket:
|
||||
handle := pkt.getHandle()
|
||||
request, ok := rs.getRequest(handle)
|
||||
if !ok {
|
||||
rpkt = statusFromError(pkt.ID, EBADF)
|
||||
} else {
|
||||
request = NewRequest("Setstat", request.Filepath)
|
||||
rpkt = request.call(rs.Handlers, pkt, rs.pktMgr.alloc, orderID)
|
||||
}
|
||||
case *sshFxpExtendedPacketPosixRename:
|
||||
request := NewRequest("PosixRename", pkt.Oldpath)
|
||||
request.Target = pkt.Newpath
|
||||
rpkt = request.call(rs.Handlers, pkt, rs.pktMgr.alloc, orderID)
|
||||
case *sshFxpExtendedPacketStatVFS:
|
||||
request := NewRequest("StatVFS", pkt.Path)
|
||||
rpkt = request.call(rs.Handlers, pkt, rs.pktMgr.alloc, orderID)
|
||||
case hasHandle:
|
||||
handle := pkt.getHandle()
|
||||
request, ok := rs.getRequest(handle)
|
||||
if !ok {
|
||||
rpkt = statusFromError(pkt.id(), EBADF)
|
||||
} else {
|
||||
rpkt = request.call(rs.Handlers, pkt, rs.pktMgr.alloc, orderID)
|
||||
}
|
||||
case hasPath:
|
||||
request := requestFromPacket(ctx, pkt)
|
||||
rpkt = request.call(rs.Handlers, pkt, rs.pktMgr.alloc, orderID)
|
||||
request.close()
|
||||
default:
|
||||
rpkt = statusFromError(pkt.id(), ErrSSHFxOpUnsupported)
|
||||
}
|
||||
|
||||
rs.pktMgr.readyPacket(
|
||||
rs.pktMgr.newOrderedResponse(rpkt, orderID))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// clean and return name packet for file
|
||||
func cleanPacketPath(pkt *sshFxpRealpathPacket, realPath string) responsePacket {
|
||||
return &sshFxpNamePacket{
|
||||
ID: pkt.id(),
|
||||
NameAttrs: []*sshFxpNameAttr{
|
||||
{
|
||||
Name: realPath,
|
||||
LongName: realPath,
|
||||
Attrs: emptyFileStat,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Makes sure we have a clean POSIX (/) absolute path to work with
|
||||
func cleanPath(p string) string {
|
||||
return cleanPathWithBase("/", p)
|
||||
}
|
||||
|
||||
func cleanPathWithBase(base, p string) string {
|
||||
p = filepath.ToSlash(filepath.Clean(p))
|
||||
if !path.IsAbs(p) {
|
||||
return path.Join(base, p)
|
||||
}
|
||||
return p
|
||||
}
|
||||
27
vendor/github.com/pkg/sftp/request-unix.go
generated
vendored
Normal file
27
vendor/github.com/pkg/sftp/request-unix.go
generated
vendored
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
// +build !windows,!plan9
|
||||
|
||||
package sftp
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func fakeFileInfoSys() interface{} {
|
||||
return &syscall.Stat_t{Uid: 65534, Gid: 65534}
|
||||
}
|
||||
|
||||
func testOsSys(sys interface{}) error {
|
||||
fstat := sys.(*FileStat)
|
||||
if fstat.UID != uint32(65534) {
|
||||
return errors.New("Uid failed to match")
|
||||
}
|
||||
if fstat.GID != uint32(65534) {
|
||||
return errors.New("Gid failed to match")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func toLocalPath(p string) string {
|
||||
return p
|
||||
}
|
||||
628
vendor/github.com/pkg/sftp/request.go
generated
vendored
Normal file
628
vendor/github.com/pkg/sftp/request.go
generated
vendored
Normal file
|
|
@ -0,0 +1,628 @@
|
|||
package sftp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// MaxFilelist is the max number of files to return in a readdir batch.
|
||||
var MaxFilelist int64 = 100
|
||||
|
||||
// state encapsulates the reader/writer/readdir from handlers.
|
||||
type state struct {
|
||||
mu sync.RWMutex
|
||||
|
||||
writerAt io.WriterAt
|
||||
readerAt io.ReaderAt
|
||||
writerAtReaderAt WriterAtReaderAt
|
||||
listerAt ListerAt
|
||||
lsoffset int64
|
||||
}
|
||||
|
||||
// copy returns a shallow copy the state.
|
||||
// This is broken out to specific fields,
|
||||
// because we have to copy around the mutex in state.
|
||||
func (s *state) copy() state {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
|
||||
return state{
|
||||
writerAt: s.writerAt,
|
||||
readerAt: s.readerAt,
|
||||
writerAtReaderAt: s.writerAtReaderAt,
|
||||
listerAt: s.listerAt,
|
||||
lsoffset: s.lsoffset,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *state) setReaderAt(rd io.ReaderAt) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
s.readerAt = rd
|
||||
}
|
||||
|
||||
func (s *state) getReaderAt() io.ReaderAt {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
|
||||
return s.readerAt
|
||||
}
|
||||
|
||||
func (s *state) setWriterAt(rd io.WriterAt) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
s.writerAt = rd
|
||||
}
|
||||
|
||||
func (s *state) getWriterAt() io.WriterAt {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
|
||||
return s.writerAt
|
||||
}
|
||||
|
||||
func (s *state) setWriterAtReaderAt(rw WriterAtReaderAt) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
s.writerAtReaderAt = rw
|
||||
}
|
||||
|
||||
func (s *state) getWriterAtReaderAt() WriterAtReaderAt {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
|
||||
return s.writerAtReaderAt
|
||||
}
|
||||
|
||||
func (s *state) getAllReaderWriters() (io.ReaderAt, io.WriterAt, WriterAtReaderAt) {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
|
||||
return s.readerAt, s.writerAt, s.writerAtReaderAt
|
||||
}
|
||||
|
||||
// Returns current offset for file list
|
||||
func (s *state) lsNext() int64 {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
|
||||
return s.lsoffset
|
||||
}
|
||||
|
||||
// Increases next offset
|
||||
func (s *state) lsInc(offset int64) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
s.lsoffset += offset
|
||||
}
|
||||
|
||||
// manage file read/write state
|
||||
func (s *state) setListerAt(la ListerAt) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
s.listerAt = la
|
||||
}
|
||||
|
||||
func (s *state) getListerAt() ListerAt {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
|
||||
return s.listerAt
|
||||
}
|
||||
|
||||
// Request contains the data and state for the incoming service request.
|
||||
type Request struct {
|
||||
// Get, Put, Setstat, Stat, Rename, Remove
|
||||
// Rmdir, Mkdir, List, Readlink, Link, Symlink
|
||||
Method string
|
||||
Filepath string
|
||||
Flags uint32
|
||||
Attrs []byte // convert to sub-struct
|
||||
Target string // for renames and sym-links
|
||||
handle string
|
||||
|
||||
// reader/writer/readdir from handlers
|
||||
state
|
||||
|
||||
// context lasts duration of request
|
||||
ctx context.Context
|
||||
cancelCtx context.CancelFunc
|
||||
}
|
||||
|
||||
// NewRequest creates a new Request object.
|
||||
func NewRequest(method, path string) *Request {
|
||||
return &Request{
|
||||
Method: method,
|
||||
Filepath: cleanPath(path),
|
||||
}
|
||||
}
|
||||
|
||||
// copy returns a shallow copy of existing request.
|
||||
// This is broken out to specific fields,
|
||||
// because we have to copy around the mutex in state.
|
||||
func (r *Request) copy() *Request {
|
||||
return &Request{
|
||||
Method: r.Method,
|
||||
Filepath: r.Filepath,
|
||||
Flags: r.Flags,
|
||||
Attrs: r.Attrs,
|
||||
Target: r.Target,
|
||||
handle: r.handle,
|
||||
|
||||
state: r.state.copy(),
|
||||
|
||||
ctx: r.ctx,
|
||||
cancelCtx: r.cancelCtx,
|
||||
}
|
||||
}
|
||||
|
||||
// New Request initialized based on packet data
|
||||
func requestFromPacket(ctx context.Context, pkt hasPath) *Request {
|
||||
method := requestMethod(pkt)
|
||||
request := NewRequest(method, pkt.getPath())
|
||||
request.ctx, request.cancelCtx = context.WithCancel(ctx)
|
||||
|
||||
switch p := pkt.(type) {
|
||||
case *sshFxpOpenPacket:
|
||||
request.Flags = p.Pflags
|
||||
case *sshFxpSetstatPacket:
|
||||
request.Flags = p.Flags
|
||||
request.Attrs = p.Attrs.([]byte)
|
||||
case *sshFxpRenamePacket:
|
||||
request.Target = cleanPath(p.Newpath)
|
||||
case *sshFxpSymlinkPacket:
|
||||
// NOTE: given a POSIX compliant signature: symlink(target, linkpath string)
|
||||
// this makes Request.Target the linkpath, and Request.Filepath the target.
|
||||
request.Target = cleanPath(p.Linkpath)
|
||||
case *sshFxpExtendedPacketHardlink:
|
||||
request.Target = cleanPath(p.Newpath)
|
||||
}
|
||||
return request
|
||||
}
|
||||
|
||||
// Context returns the request's context. To change the context,
|
||||
// use WithContext.
|
||||
//
|
||||
// The returned context is always non-nil; it defaults to the
|
||||
// background context.
|
||||
//
|
||||
// For incoming server requests, the context is canceled when the
|
||||
// request is complete or the client's connection closes.
|
||||
func (r *Request) Context() context.Context {
|
||||
if r.ctx != nil {
|
||||
return r.ctx
|
||||
}
|
||||
return context.Background()
|
||||
}
|
||||
|
||||
// WithContext returns a copy of r with its context changed to ctx.
|
||||
// The provided ctx must be non-nil.
|
||||
func (r *Request) WithContext(ctx context.Context) *Request {
|
||||
if ctx == nil {
|
||||
panic("nil context")
|
||||
}
|
||||
r2 := r.copy()
|
||||
r2.ctx = ctx
|
||||
r2.cancelCtx = nil
|
||||
return r2
|
||||
}
|
||||
|
||||
// Close reader/writer if possible
|
||||
func (r *Request) close() error {
|
||||
defer func() {
|
||||
if r.cancelCtx != nil {
|
||||
r.cancelCtx()
|
||||
}
|
||||
}()
|
||||
|
||||
rd, wr, rw := r.getAllReaderWriters()
|
||||
|
||||
var err error
|
||||
|
||||
// Close errors on a Writer are far more likely to be the important one.
|
||||
// As they can be information that there was a loss of data.
|
||||
if c, ok := wr.(io.Closer); ok {
|
||||
if err2 := c.Close(); err == nil {
|
||||
// update error if it is still nil
|
||||
err = err2
|
||||
}
|
||||
}
|
||||
|
||||
if c, ok := rw.(io.Closer); ok {
|
||||
if err2 := c.Close(); err == nil {
|
||||
// update error if it is still nil
|
||||
err = err2
|
||||
|
||||
r.setWriterAtReaderAt(nil)
|
||||
}
|
||||
}
|
||||
|
||||
if c, ok := rd.(io.Closer); ok {
|
||||
if err2 := c.Close(); err == nil {
|
||||
// update error if it is still nil
|
||||
err = err2
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// Notify transfer error if any
|
||||
func (r *Request) transferError(err error) {
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
|
||||
rd, wr, rw := r.getAllReaderWriters()
|
||||
|
||||
if t, ok := wr.(TransferError); ok {
|
||||
t.TransferError(err)
|
||||
}
|
||||
|
||||
if t, ok := rw.(TransferError); ok {
|
||||
t.TransferError(err)
|
||||
}
|
||||
|
||||
if t, ok := rd.(TransferError); ok {
|
||||
t.TransferError(err)
|
||||
}
|
||||
}
|
||||
|
||||
// called from worker to handle packet/request
|
||||
func (r *Request) call(handlers Handlers, pkt requestPacket, alloc *allocator, orderID uint32) responsePacket {
|
||||
switch r.Method {
|
||||
case "Get":
|
||||
return fileget(handlers.FileGet, r, pkt, alloc, orderID)
|
||||
case "Put":
|
||||
return fileput(handlers.FilePut, r, pkt, alloc, orderID)
|
||||
case "Open":
|
||||
return fileputget(handlers.FilePut, r, pkt, alloc, orderID)
|
||||
case "Setstat", "Rename", "Rmdir", "Mkdir", "Link", "Symlink", "Remove", "PosixRename", "StatVFS":
|
||||
return filecmd(handlers.FileCmd, r, pkt)
|
||||
case "List":
|
||||
return filelist(handlers.FileList, r, pkt)
|
||||
case "Stat", "Lstat", "Readlink":
|
||||
return filestat(handlers.FileList, r, pkt)
|
||||
default:
|
||||
return statusFromError(pkt.id(), fmt.Errorf("unexpected method: %s", r.Method))
|
||||
}
|
||||
}
|
||||
|
||||
// Additional initialization for Open packets
|
||||
func (r *Request) open(h Handlers, pkt requestPacket) responsePacket {
|
||||
flags := r.Pflags()
|
||||
|
||||
id := pkt.id()
|
||||
|
||||
switch {
|
||||
case flags.Write, flags.Append, flags.Creat, flags.Trunc:
|
||||
if flags.Read {
|
||||
if openFileWriter, ok := h.FilePut.(OpenFileWriter); ok {
|
||||
r.Method = "Open"
|
||||
rw, err := openFileWriter.OpenFile(r)
|
||||
if err != nil {
|
||||
return statusFromError(id, err)
|
||||
}
|
||||
|
||||
r.setWriterAtReaderAt(rw)
|
||||
|
||||
return &sshFxpHandlePacket{
|
||||
ID: id,
|
||||
Handle: r.handle,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
r.Method = "Put"
|
||||
wr, err := h.FilePut.Filewrite(r)
|
||||
if err != nil {
|
||||
return statusFromError(id, err)
|
||||
}
|
||||
|
||||
r.setWriterAt(wr)
|
||||
|
||||
case flags.Read:
|
||||
r.Method = "Get"
|
||||
rd, err := h.FileGet.Fileread(r)
|
||||
if err != nil {
|
||||
return statusFromError(id, err)
|
||||
}
|
||||
|
||||
r.setReaderAt(rd)
|
||||
|
||||
default:
|
||||
return statusFromError(id, errors.New("bad file flags"))
|
||||
}
|
||||
|
||||
return &sshFxpHandlePacket{
|
||||
ID: id,
|
||||
Handle: r.handle,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Request) opendir(h Handlers, pkt requestPacket) responsePacket {
|
||||
r.Method = "List"
|
||||
la, err := h.FileList.Filelist(r)
|
||||
if err != nil {
|
||||
return statusFromError(pkt.id(), wrapPathError(r.Filepath, err))
|
||||
}
|
||||
|
||||
r.setListerAt(la)
|
||||
|
||||
return &sshFxpHandlePacket{
|
||||
ID: pkt.id(),
|
||||
Handle: r.handle,
|
||||
}
|
||||
}
|
||||
|
||||
// wrap FileReader handler
|
||||
func fileget(h FileReader, r *Request, pkt requestPacket, alloc *allocator, orderID uint32) responsePacket {
|
||||
rd := r.getReaderAt()
|
||||
if rd == nil {
|
||||
return statusFromError(pkt.id(), errors.New("unexpected read packet"))
|
||||
}
|
||||
|
||||
data, offset, _ := packetData(pkt, alloc, orderID)
|
||||
|
||||
n, err := rd.ReadAt(data, offset)
|
||||
// only return EOF error if no data left to read
|
||||
if err != nil && (err != io.EOF || n == 0) {
|
||||
return statusFromError(pkt.id(), err)
|
||||
}
|
||||
|
||||
return &sshFxpDataPacket{
|
||||
ID: pkt.id(),
|
||||
Length: uint32(n),
|
||||
Data: data[:n],
|
||||
}
|
||||
}
|
||||
|
||||
// wrap FileWriter handler
|
||||
func fileput(h FileWriter, r *Request, pkt requestPacket, alloc *allocator, orderID uint32) responsePacket {
|
||||
wr := r.getWriterAt()
|
||||
if wr == nil {
|
||||
return statusFromError(pkt.id(), errors.New("unexpected write packet"))
|
||||
}
|
||||
|
||||
data, offset, _ := packetData(pkt, alloc, orderID)
|
||||
|
||||
_, err := wr.WriteAt(data, offset)
|
||||
return statusFromError(pkt.id(), err)
|
||||
}
|
||||
|
||||
// wrap OpenFileWriter handler
|
||||
func fileputget(h FileWriter, r *Request, pkt requestPacket, alloc *allocator, orderID uint32) responsePacket {
|
||||
rw := r.getWriterAtReaderAt()
|
||||
if rw == nil {
|
||||
return statusFromError(pkt.id(), errors.New("unexpected write and read packet"))
|
||||
}
|
||||
|
||||
switch p := pkt.(type) {
|
||||
case *sshFxpReadPacket:
|
||||
data, offset := p.getDataSlice(alloc, orderID), int64(p.Offset)
|
||||
|
||||
n, err := rw.ReadAt(data, offset)
|
||||
// only return EOF error if no data left to read
|
||||
if err != nil && (err != io.EOF || n == 0) {
|
||||
return statusFromError(pkt.id(), err)
|
||||
}
|
||||
|
||||
return &sshFxpDataPacket{
|
||||
ID: pkt.id(),
|
||||
Length: uint32(n),
|
||||
Data: data[:n],
|
||||
}
|
||||
|
||||
case *sshFxpWritePacket:
|
||||
data, offset := p.Data, int64(p.Offset)
|
||||
|
||||
_, err := rw.WriteAt(data, offset)
|
||||
return statusFromError(pkt.id(), err)
|
||||
|
||||
default:
|
||||
return statusFromError(pkt.id(), errors.New("unexpected packet type for read or write"))
|
||||
}
|
||||
}
|
||||
|
||||
// file data for additional read/write packets
|
||||
func packetData(p requestPacket, alloc *allocator, orderID uint32) (data []byte, offset int64, length uint32) {
|
||||
switch p := p.(type) {
|
||||
case *sshFxpReadPacket:
|
||||
return p.getDataSlice(alloc, orderID), int64(p.Offset), p.Len
|
||||
case *sshFxpWritePacket:
|
||||
return p.Data, int64(p.Offset), p.Length
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// wrap FileCmder handler
|
||||
func filecmd(h FileCmder, r *Request, pkt requestPacket) responsePacket {
|
||||
switch p := pkt.(type) {
|
||||
case *sshFxpFsetstatPacket:
|
||||
r.Flags = p.Flags
|
||||
r.Attrs = p.Attrs.([]byte)
|
||||
}
|
||||
|
||||
switch r.Method {
|
||||
case "PosixRename":
|
||||
if posixRenamer, ok := h.(PosixRenameFileCmder); ok {
|
||||
err := posixRenamer.PosixRename(r)
|
||||
return statusFromError(pkt.id(), err)
|
||||
}
|
||||
|
||||
// PosixRenameFileCmder not implemented handle this request as a Rename
|
||||
r.Method = "Rename"
|
||||
err := h.Filecmd(r)
|
||||
return statusFromError(pkt.id(), err)
|
||||
|
||||
case "StatVFS":
|
||||
if statVFSCmdr, ok := h.(StatVFSFileCmder); ok {
|
||||
stat, err := statVFSCmdr.StatVFS(r)
|
||||
if err != nil {
|
||||
return statusFromError(pkt.id(), err)
|
||||
}
|
||||
stat.ID = pkt.id()
|
||||
return stat
|
||||
}
|
||||
|
||||
return statusFromError(pkt.id(), ErrSSHFxOpUnsupported)
|
||||
}
|
||||
|
||||
err := h.Filecmd(r)
|
||||
return statusFromError(pkt.id(), err)
|
||||
}
|
||||
|
||||
// wrap FileLister handler
|
||||
func filelist(h FileLister, r *Request, pkt requestPacket) responsePacket {
|
||||
lister := r.getListerAt()
|
||||
if lister == nil {
|
||||
return statusFromError(pkt.id(), errors.New("unexpected dir packet"))
|
||||
}
|
||||
|
||||
offset := r.lsNext()
|
||||
finfo := make([]os.FileInfo, MaxFilelist)
|
||||
n, err := lister.ListAt(finfo, offset)
|
||||
r.lsInc(int64(n))
|
||||
// ignore EOF as we only return it when there are no results
|
||||
finfo = finfo[:n] // avoid need for nil tests below
|
||||
|
||||
switch r.Method {
|
||||
case "List":
|
||||
if err != nil && (err != io.EOF || n == 0) {
|
||||
return statusFromError(pkt.id(), err)
|
||||
}
|
||||
|
||||
nameAttrs := make([]*sshFxpNameAttr, 0, len(finfo))
|
||||
|
||||
// If the type conversion fails, we get untyped `nil`,
|
||||
// which is handled by not looking up any names.
|
||||
idLookup, _ := h.(NameLookupFileLister)
|
||||
|
||||
for _, fi := range finfo {
|
||||
nameAttrs = append(nameAttrs, &sshFxpNameAttr{
|
||||
Name: fi.Name(),
|
||||
LongName: runLs(idLookup, fi),
|
||||
Attrs: []interface{}{fi},
|
||||
})
|
||||
}
|
||||
|
||||
return &sshFxpNamePacket{
|
||||
ID: pkt.id(),
|
||||
NameAttrs: nameAttrs,
|
||||
}
|
||||
|
||||
default:
|
||||
err = fmt.Errorf("unexpected method: %s", r.Method)
|
||||
return statusFromError(pkt.id(), err)
|
||||
}
|
||||
}
|
||||
|
||||
func filestat(h FileLister, r *Request, pkt requestPacket) responsePacket {
|
||||
var lister ListerAt
|
||||
var err error
|
||||
|
||||
if r.Method == "Lstat" {
|
||||
if lstatFileLister, ok := h.(LstatFileLister); ok {
|
||||
lister, err = lstatFileLister.Lstat(r)
|
||||
} else {
|
||||
// LstatFileLister not implemented handle this request as a Stat
|
||||
r.Method = "Stat"
|
||||
lister, err = h.Filelist(r)
|
||||
}
|
||||
} else {
|
||||
lister, err = h.Filelist(r)
|
||||
}
|
||||
if err != nil {
|
||||
return statusFromError(pkt.id(), err)
|
||||
}
|
||||
finfo := make([]os.FileInfo, 1)
|
||||
n, err := lister.ListAt(finfo, 0)
|
||||
finfo = finfo[:n] // avoid need for nil tests below
|
||||
|
||||
switch r.Method {
|
||||
case "Stat", "Lstat":
|
||||
if err != nil && err != io.EOF {
|
||||
return statusFromError(pkt.id(), err)
|
||||
}
|
||||
if n == 0 {
|
||||
err = &os.PathError{
|
||||
Op: strings.ToLower(r.Method),
|
||||
Path: r.Filepath,
|
||||
Err: syscall.ENOENT,
|
||||
}
|
||||
return statusFromError(pkt.id(), err)
|
||||
}
|
||||
return &sshFxpStatResponse{
|
||||
ID: pkt.id(),
|
||||
info: finfo[0],
|
||||
}
|
||||
case "Readlink":
|
||||
if err != nil && err != io.EOF {
|
||||
return statusFromError(pkt.id(), err)
|
||||
}
|
||||
if n == 0 {
|
||||
err = &os.PathError{
|
||||
Op: "readlink",
|
||||
Path: r.Filepath,
|
||||
Err: syscall.ENOENT,
|
||||
}
|
||||
return statusFromError(pkt.id(), err)
|
||||
}
|
||||
filename := finfo[0].Name()
|
||||
return &sshFxpNamePacket{
|
||||
ID: pkt.id(),
|
||||
NameAttrs: []*sshFxpNameAttr{
|
||||
{
|
||||
Name: filename,
|
||||
LongName: filename,
|
||||
Attrs: emptyFileStat,
|
||||
},
|
||||
},
|
||||
}
|
||||
default:
|
||||
err = fmt.Errorf("unexpected method: %s", r.Method)
|
||||
return statusFromError(pkt.id(), err)
|
||||
}
|
||||
}
|
||||
|
||||
// init attributes of request object from packet data
|
||||
func requestMethod(p requestPacket) (method string) {
|
||||
switch p.(type) {
|
||||
case *sshFxpReadPacket, *sshFxpWritePacket, *sshFxpOpenPacket:
|
||||
// set in open() above
|
||||
case *sshFxpOpendirPacket, *sshFxpReaddirPacket:
|
||||
// set in opendir() above
|
||||
case *sshFxpSetstatPacket, *sshFxpFsetstatPacket:
|
||||
method = "Setstat"
|
||||
case *sshFxpRenamePacket:
|
||||
method = "Rename"
|
||||
case *sshFxpSymlinkPacket:
|
||||
method = "Symlink"
|
||||
case *sshFxpRemovePacket:
|
||||
method = "Remove"
|
||||
case *sshFxpStatPacket, *sshFxpFstatPacket:
|
||||
method = "Stat"
|
||||
case *sshFxpLstatPacket:
|
||||
method = "Lstat"
|
||||
case *sshFxpRmdirPacket:
|
||||
method = "Rmdir"
|
||||
case *sshFxpReadlinkPacket:
|
||||
method = "Readlink"
|
||||
case *sshFxpMkdirPacket:
|
||||
method = "Mkdir"
|
||||
case *sshFxpExtendedPacketHardlink:
|
||||
method = "Link"
|
||||
}
|
||||
return method
|
||||
}
|
||||
44
vendor/github.com/pkg/sftp/request_windows.go
generated
vendored
Normal file
44
vendor/github.com/pkg/sftp/request_windows.go
generated
vendored
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
package sftp
|
||||
|
||||
import (
|
||||
"path"
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func fakeFileInfoSys() interface{} {
|
||||
return syscall.Win32FileAttributeData{}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
616
vendor/github.com/pkg/sftp/server.go
generated
vendored
Normal file
616
vendor/github.com/pkg/sftp/server.go
generated
vendored
Normal file
|
|
@ -0,0 +1,616 @@
|
|||
package sftp
|
||||
|
||||
// sftp server counterpart
|
||||
|
||||
import (
|
||||
"encoding"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
// SftpServerWorkerCount defines the number of workers for the SFTP server
|
||||
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
|
||||
pktMgr *packetManager
|
||||
openFiles map[string]*os.File
|
||||
openFilesLock sync.RWMutex
|
||||
handleCount int
|
||||
}
|
||||
|
||||
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 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) responsePacket
|
||||
}
|
||||
|
||||
// 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) {
|
||||
svrConn := &serverConn{
|
||||
conn: conn{
|
||||
Reader: rwc,
|
||||
WriteCloser: rwc,
|
||||
},
|
||||
}
|
||||
s := &Server{
|
||||
serverConn: svrConn,
|
||||
debugStream: ioutil.Discard,
|
||||
pktMgr: newPktMgr(svrConn),
|
||||
openFiles: make(map[string]*os.File),
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
// WithAllocator enable the allocator.
|
||||
// After processing a packet we keep in memory the allocated slices
|
||||
// and we reuse them for new packets.
|
||||
// The allocator is experimental
|
||||
func WithAllocator() ServerOption {
|
||||
return func(s *Server) error {
|
||||
alloc := newAllocator()
|
||||
s.pktMgr.alloc = alloc
|
||||
s.conn.alloc = alloc
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
type rxPacket struct {
|
||||
pktType fxp
|
||||
pktBytes []byte
|
||||
}
|
||||
|
||||
// Up to N parallel servers
|
||||
func (svr *Server) sftpServerWorker(pktChan chan orderedRequest) error {
|
||||
for pkt := range pktChan {
|
||||
// readonly checks
|
||||
readonly := true
|
||||
switch pkt := pkt.requestPacket.(type) {
|
||||
case notReadOnly:
|
||||
readonly = false
|
||||
case *sshFxpOpenPacket:
|
||||
readonly = pkt.readonly()
|
||||
case *sshFxpExtendedPacket:
|
||||
readonly = pkt.readonly()
|
||||
}
|
||||
|
||||
// If server is operating read-only and a write operation is requested,
|
||||
// return permission denied
|
||||
if !readonly && svr.readOnly {
|
||||
svr.pktMgr.readyPacket(
|
||||
svr.pktMgr.newOrderedResponse(statusFromError(pkt.id(), syscall.EPERM), pkt.orderID()),
|
||||
)
|
||||
continue
|
||||
}
|
||||
|
||||
if err := handlePacket(svr, pkt); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func handlePacket(s *Server, p orderedRequest) error {
|
||||
var rpkt responsePacket
|
||||
orderID := p.orderID()
|
||||
switch p := p.requestPacket.(type) {
|
||||
case *sshFxInitPacket:
|
||||
rpkt = &sshFxVersionPacket{
|
||||
Version: sftpProtocolVersion,
|
||||
Extensions: sftpExtensions,
|
||||
}
|
||||
case *sshFxpStatPacket:
|
||||
// stat the requested file
|
||||
info, err := os.Stat(toLocalPath(p.Path))
|
||||
rpkt = &sshFxpStatResponse{
|
||||
ID: p.ID,
|
||||
info: info,
|
||||
}
|
||||
if err != nil {
|
||||
rpkt = statusFromError(p.ID, err)
|
||||
}
|
||||
case *sshFxpLstatPacket:
|
||||
// stat the requested file
|
||||
info, err := os.Lstat(toLocalPath(p.Path))
|
||||
rpkt = &sshFxpStatResponse{
|
||||
ID: p.ID,
|
||||
info: info,
|
||||
}
|
||||
if err != nil {
|
||||
rpkt = statusFromError(p.ID, err)
|
||||
}
|
||||
case *sshFxpFstatPacket:
|
||||
f, ok := s.getHandle(p.Handle)
|
||||
var err error = EBADF
|
||||
var info os.FileInfo
|
||||
if ok {
|
||||
info, err = f.Stat()
|
||||
rpkt = &sshFxpStatResponse{
|
||||
ID: p.ID,
|
||||
info: info,
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
rpkt = statusFromError(p.ID, err)
|
||||
}
|
||||
case *sshFxpMkdirPacket:
|
||||
// TODO FIXME: ignore flags field
|
||||
err := os.Mkdir(toLocalPath(p.Path), 0755)
|
||||
rpkt = statusFromError(p.ID, err)
|
||||
case *sshFxpRmdirPacket:
|
||||
err := os.Remove(toLocalPath(p.Path))
|
||||
rpkt = statusFromError(p.ID, err)
|
||||
case *sshFxpRemovePacket:
|
||||
err := os.Remove(toLocalPath(p.Filename))
|
||||
rpkt = statusFromError(p.ID, err)
|
||||
case *sshFxpRenamePacket:
|
||||
err := os.Rename(toLocalPath(p.Oldpath), toLocalPath(p.Newpath))
|
||||
rpkt = statusFromError(p.ID, err)
|
||||
case *sshFxpSymlinkPacket:
|
||||
err := os.Symlink(toLocalPath(p.Targetpath), 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))
|
||||
rpkt = &sshFxpNamePacket{
|
||||
ID: p.ID,
|
||||
NameAttrs: []*sshFxpNameAttr{
|
||||
{
|
||||
Name: f,
|
||||
LongName: f,
|
||||
Attrs: emptyFileStat,
|
||||
},
|
||||
},
|
||||
}
|
||||
if err != nil {
|
||||
rpkt = statusFromError(p.ID, err)
|
||||
}
|
||||
case *sshFxpRealpathPacket:
|
||||
f, err := filepath.Abs(toLocalPath(p.Path))
|
||||
f = cleanPath(f)
|
||||
rpkt = &sshFxpNamePacket{
|
||||
ID: p.ID,
|
||||
NameAttrs: []*sshFxpNameAttr{
|
||||
{
|
||||
Name: f,
|
||||
LongName: f,
|
||||
Attrs: emptyFileStat,
|
||||
},
|
||||
},
|
||||
}
|
||||
if err != nil {
|
||||
rpkt = statusFromError(p.ID, err)
|
||||
}
|
||||
case *sshFxpOpendirPacket:
|
||||
p.Path = toLocalPath(p.Path)
|
||||
|
||||
if stat, err := os.Stat(p.Path); err != nil {
|
||||
rpkt = statusFromError(p.ID, err)
|
||||
} else if !stat.IsDir() {
|
||||
rpkt = statusFromError(p.ID, &os.PathError{
|
||||
Path: p.Path, Err: syscall.ENOTDIR})
|
||||
} else {
|
||||
rpkt = (&sshFxpOpenPacket{
|
||||
ID: p.ID,
|
||||
Path: p.Path,
|
||||
Pflags: sshFxfRead,
|
||||
}).respond(s)
|
||||
}
|
||||
case *sshFxpReadPacket:
|
||||
var err error = EBADF
|
||||
f, ok := s.getHandle(p.Handle)
|
||||
if ok {
|
||||
err = nil
|
||||
data := p.getDataSlice(s.pktMgr.alloc, orderID)
|
||||
n, _err := f.ReadAt(data, int64(p.Offset))
|
||||
if _err != nil && (_err != io.EOF || n == 0) {
|
||||
err = _err
|
||||
}
|
||||
rpkt = &sshFxpDataPacket{
|
||||
ID: p.ID,
|
||||
Length: uint32(n),
|
||||
Data: data[:n],
|
||||
// do not use data[:n:n] here to clamp the capacity, we allocated extra capacity above to avoid reallocations
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
rpkt = statusFromError(p.ID, err)
|
||||
}
|
||||
|
||||
case *sshFxpWritePacket:
|
||||
f, ok := s.getHandle(p.Handle)
|
||||
var err error = EBADF
|
||||
if ok {
|
||||
_, err = f.WriteAt(p.Data, int64(p.Offset))
|
||||
}
|
||||
rpkt = statusFromError(p.ID, err)
|
||||
case *sshFxpExtendedPacket:
|
||||
if p.SpecificPacket == nil {
|
||||
rpkt = statusFromError(p.ID, ErrSSHFxOpUnsupported)
|
||||
} else {
|
||||
rpkt = p.respond(s)
|
||||
}
|
||||
case serverRespondablePacket:
|
||||
rpkt = p.respond(s)
|
||||
default:
|
||||
return fmt.Errorf("unexpected packet type %T", p)
|
||||
}
|
||||
|
||||
s.pktMgr.readyPacket(s.pktMgr.newOrderedResponse(rpkt, orderID))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Serve serves SFTP connections until the streams stop or the SFTP subsystem
|
||||
// is stopped.
|
||||
func (svr *Server) Serve() error {
|
||||
defer func() {
|
||||
if svr.pktMgr.alloc != nil {
|
||||
svr.pktMgr.alloc.Free()
|
||||
}
|
||||
}()
|
||||
var wg sync.WaitGroup
|
||||
runWorker := func(ch chan orderedRequest) {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
if err := svr.sftpServerWorker(ch); err != nil {
|
||||
svr.conn.Close() // shuts down recvPacket
|
||||
}
|
||||
}()
|
||||
}
|
||||
pktChan := svr.pktMgr.workerChan(runWorker)
|
||||
|
||||
var err error
|
||||
var pkt requestPacket
|
||||
var pktType uint8
|
||||
var pktBytes []byte
|
||||
for {
|
||||
pktType, pktBytes, err = svr.serverConn.recvPacket(svr.pktMgr.getNextOrderID())
|
||||
if err != nil {
|
||||
// we don't care about releasing allocated pages here, the server will quit and the allocator freed
|
||||
break
|
||||
}
|
||||
|
||||
pkt, err = makePacket(rxPacket{fxp(pktType), pktBytes})
|
||||
if err != nil {
|
||||
switch {
|
||||
case errors.Is(err, errUnknownExtendedPacket):
|
||||
//if err := svr.serverConn.sendError(pkt, ErrSshFxOpUnsupported); err != nil {
|
||||
// debug("failed to send err packet: %v", err)
|
||||
// svr.conn.Close() // shuts down recvPacket
|
||||
// break
|
||||
//}
|
||||
default:
|
||||
debug("makePacket err: %v", err)
|
||||
svr.conn.Close() // shuts down recvPacket
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
pktChan <- svr.pktMgr.newOrderedRequest(pkt)
|
||||
}
|
||||
|
||||
close(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 ider 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) marshalPacket() ([]byte, []byte, error) {
|
||||
l := 4 + 1 + 4 // uint32(length) + byte(type) + uint32(id)
|
||||
|
||||
b := make([]byte, 4, l)
|
||||
b = append(b, sshFxpAttrs)
|
||||
b = marshalUint32(b, p.ID)
|
||||
|
||||
var payload []byte
|
||||
payload = marshalFileInfo(payload, p.info)
|
||||
|
||||
return b, payload, nil
|
||||
}
|
||||
|
||||
func (p *sshFxpStatResponse) MarshalBinary() ([]byte, error) {
|
||||
header, payload, err := p.marshalPacket()
|
||||
return append(header, payload...), err
|
||||
}
|
||||
|
||||
var emptyFileStat = []interface{}{uint32(0)}
|
||||
|
||||
func (p *sshFxpOpenPacket) readonly() bool {
|
||||
return !p.hasPflags(sshFxfWrite)
|
||||
}
|
||||
|
||||
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) responsePacket {
|
||||
var osFlags int
|
||||
if p.hasPflags(sshFxfRead, sshFxfWrite) {
|
||||
osFlags |= os.O_RDWR
|
||||
} else if p.hasPflags(sshFxfWrite) {
|
||||
osFlags |= os.O_WRONLY
|
||||
} else if p.hasPflags(sshFxfRead) {
|
||||
osFlags |= os.O_RDONLY
|
||||
} else {
|
||||
// how are they opening?
|
||||
return statusFromError(p.ID, syscall.EINVAL)
|
||||
}
|
||||
|
||||
// Don't use O_APPEND flag as it conflicts with WriteAt.
|
||||
// The sshFxfAppend flag is a no-op here as the client sends the offsets.
|
||||
|
||||
if p.hasPflags(sshFxfCreat) {
|
||||
osFlags |= os.O_CREATE
|
||||
}
|
||||
if p.hasPflags(sshFxfTrunc) {
|
||||
osFlags |= os.O_TRUNC
|
||||
}
|
||||
if p.hasPflags(sshFxfExcl) {
|
||||
osFlags |= os.O_EXCL
|
||||
}
|
||||
|
||||
f, err := os.OpenFile(toLocalPath(p.Path), osFlags, 0644)
|
||||
if err != nil {
|
||||
return statusFromError(p.ID, err)
|
||||
}
|
||||
|
||||
handle := svr.nextHandle(f)
|
||||
return &sshFxpHandlePacket{ID: p.ID, Handle: handle}
|
||||
}
|
||||
|
||||
func (p *sshFxpReaddirPacket) respond(svr *Server) responsePacket {
|
||||
f, ok := svr.getHandle(p.Handle)
|
||||
if !ok {
|
||||
return statusFromError(p.ID, EBADF)
|
||||
}
|
||||
|
||||
dirents, err := f.Readdir(128)
|
||||
if err != nil {
|
||||
return statusFromError(p.ID, err)
|
||||
}
|
||||
|
||||
idLookup := osIDLookup{}
|
||||
|
||||
ret := &sshFxpNamePacket{ID: p.ID}
|
||||
for _, dirent := range dirents {
|
||||
ret.NameAttrs = append(ret.NameAttrs, &sshFxpNameAttr{
|
||||
Name: dirent.Name(),
|
||||
LongName: runLs(idLookup, dirent),
|
||||
Attrs: []interface{}{dirent},
|
||||
})
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func (p *sshFxpSetstatPacket) respond(svr *Server) responsePacket {
|
||||
// additional unmarshalling is required for each possibility here
|
||||
b := p.Attrs.([]byte)
|
||||
var err error
|
||||
|
||||
p.Path = toLocalPath(p.Path)
|
||||
|
||||
debug("setstat name \"%s\"", p.Path)
|
||||
if (p.Flags & sshFileXferAttrSize) != 0 {
|
||||
var size uint64
|
||||
if size, b, err = unmarshalUint64Safe(b); err == nil {
|
||||
err = os.Truncate(p.Path, int64(size))
|
||||
}
|
||||
}
|
||||
if (p.Flags & sshFileXferAttrPermissions) != 0 {
|
||||
var mode uint32
|
||||
if mode, b, err = unmarshalUint32Safe(b); err == nil {
|
||||
err = os.Chmod(p.Path, os.FileMode(mode))
|
||||
}
|
||||
}
|
||||
if (p.Flags & sshFileXferAttrACmodTime) != 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 & sshFileXferAttrUIDGID) != 0 {
|
||||
var uid uint32
|
||||
var gid uint32
|
||||
if uid, b, err = unmarshalUint32Safe(b); err != nil {
|
||||
} else if gid, _, err = unmarshalUint32Safe(b); err != nil {
|
||||
} else {
|
||||
err = os.Chown(p.Path, int(uid), int(gid))
|
||||
}
|
||||
}
|
||||
|
||||
return statusFromError(p.ID, err)
|
||||
}
|
||||
|
||||
func (p *sshFxpFsetstatPacket) respond(svr *Server) responsePacket {
|
||||
f, ok := svr.getHandle(p.Handle)
|
||||
if !ok {
|
||||
return statusFromError(p.ID, 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 & sshFileXferAttrSize) != 0 {
|
||||
var size uint64
|
||||
if size, b, err = unmarshalUint64Safe(b); err == nil {
|
||||
err = f.Truncate(int64(size))
|
||||
}
|
||||
}
|
||||
if (p.Flags & sshFileXferAttrPermissions) != 0 {
|
||||
var mode uint32
|
||||
if mode, b, err = unmarshalUint32Safe(b); err == nil {
|
||||
err = f.Chmod(os.FileMode(mode))
|
||||
}
|
||||
}
|
||||
if (p.Flags & sshFileXferAttrACmodTime) != 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 & sshFileXferAttrUIDGID) != 0 {
|
||||
var uid uint32
|
||||
var gid uint32
|
||||
if uid, b, err = unmarshalUint32Safe(b); err != nil {
|
||||
} else if gid, _, err = unmarshalUint32Safe(b); err != nil {
|
||||
} else {
|
||||
err = f.Chown(int(uid), int(gid))
|
||||
}
|
||||
}
|
||||
|
||||
return statusFromError(p.ID, err)
|
||||
}
|
||||
|
||||
func statusFromError(id uint32, err error) *sshFxpStatusPacket {
|
||||
ret := &sshFxpStatusPacket{
|
||||
ID: id,
|
||||
StatusError: StatusError{
|
||||
// sshFXOk = 0
|
||||
// sshFXEOF = 1
|
||||
// sshFXNoSuchFile = 2 ENOENT
|
||||
// sshFXPermissionDenied = 3
|
||||
// sshFXFailure = 4
|
||||
// sshFXBadMessage = 5
|
||||
// sshFXNoConnection = 6
|
||||
// sshFXConnectionLost = 7
|
||||
// sshFXOPUnsupported = 8
|
||||
Code: sshFxOk,
|
||||
},
|
||||
}
|
||||
if err == nil {
|
||||
return ret
|
||||
}
|
||||
|
||||
debug("statusFromError: error is %T %#v", err, err)
|
||||
ret.StatusError.Code = sshFxFailure
|
||||
ret.StatusError.msg = err.Error()
|
||||
|
||||
if os.IsNotExist(err) {
|
||||
ret.StatusError.Code = sshFxNoSuchFile
|
||||
return ret
|
||||
}
|
||||
if code, ok := translateSyscallError(err); ok {
|
||||
ret.StatusError.Code = code
|
||||
return ret
|
||||
}
|
||||
|
||||
switch e := err.(type) {
|
||||
case fxerr:
|
||||
ret.StatusError.Code = uint32(e)
|
||||
default:
|
||||
if e == io.EOF {
|
||||
ret.StatusError.Code = sshFxEOF
|
||||
}
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
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
|
||||
}
|
||||
29
vendor/github.com/pkg/sftp/server_statvfs_impl.go
generated
vendored
Normal file
29
vendor/github.com/pkg/sftp/server_statvfs_impl.go
generated
vendored
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
// +build darwin linux
|
||||
|
||||
// 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) responsePacket {
|
||||
retPkt, err := getStatVFSForPath(p.Path)
|
||||
if err != nil {
|
||||
return statusFromError(p.ID, err)
|
||||
}
|
||||
retPkt.ID = p.ID
|
||||
|
||||
return retPkt
|
||||
}
|
||||
|
||||
func getStatVFSForPath(name string) (*StatVFS, error) {
|
||||
var stat syscall.Statfs_t
|
||||
if err := syscall.Statfs(name, &stat); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return statvfsFromStatfst(&stat)
|
||||
}
|
||||
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 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
|
||||
}
|
||||
13
vendor/github.com/pkg/sftp/server_statvfs_plan9.go
generated
vendored
Normal file
13
vendor/github.com/pkg/sftp/server_statvfs_plan9.go
generated
vendored
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
package sftp
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func (p *sshFxpExtendedPacketStatVFS) respond(svr *Server) responsePacket {
|
||||
return statusFromError(p.ID, syscall.EPLAN9)
|
||||
}
|
||||
|
||||
func getStatVFSForPath(name string) (*StatVFS, error) {
|
||||
return nil, syscall.EPLAN9
|
||||
}
|
||||
15
vendor/github.com/pkg/sftp/server_statvfs_stubs.go
generated
vendored
Normal file
15
vendor/github.com/pkg/sftp/server_statvfs_stubs.go
generated
vendored
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
// +build !darwin,!linux,!plan9
|
||||
|
||||
package sftp
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func (p *sshFxpExtendedPacketStatVFS) respond(svr *Server) responsePacket {
|
||||
return statusFromError(p.ID, syscall.ENOTSUP)
|
||||
}
|
||||
|
||||
func getStatVFSForPath(name string) (*StatVFS, error) {
|
||||
return nil, syscall.ENOTSUP
|
||||
}
|
||||
258
vendor/github.com/pkg/sftp/sftp.go
generated
vendored
Normal file
258
vendor/github.com/pkg/sftp/sftp.go
generated
vendored
Normal file
|
|
@ -0,0 +1,258 @@
|
|||
// Package sftp implements the SSH File Transfer Protocol as described in
|
||||
// https://tools.ietf.org/html/draft-ietf-secsh-filexfer-02
|
||||
package sftp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
const (
|
||||
sshFxpInit = 1
|
||||
sshFxpVersion = 2
|
||||
sshFxpOpen = 3
|
||||
sshFxpClose = 4
|
||||
sshFxpRead = 5
|
||||
sshFxpWrite = 6
|
||||
sshFxpLstat = 7
|
||||
sshFxpFstat = 8
|
||||
sshFxpSetstat = 9
|
||||
sshFxpFsetstat = 10
|
||||
sshFxpOpendir = 11
|
||||
sshFxpReaddir = 12
|
||||
sshFxpRemove = 13
|
||||
sshFxpMkdir = 14
|
||||
sshFxpRmdir = 15
|
||||
sshFxpRealpath = 16
|
||||
sshFxpStat = 17
|
||||
sshFxpRename = 18
|
||||
sshFxpReadlink = 19
|
||||
sshFxpSymlink = 20
|
||||
sshFxpStatus = 101
|
||||
sshFxpHandle = 102
|
||||
sshFxpData = 103
|
||||
sshFxpName = 104
|
||||
sshFxpAttrs = 105
|
||||
sshFxpExtended = 200
|
||||
sshFxpExtendedReply = 201
|
||||
)
|
||||
|
||||
const (
|
||||
sshFxOk = 0
|
||||
sshFxEOF = 1
|
||||
sshFxNoSuchFile = 2
|
||||
sshFxPermissionDenied = 3
|
||||
sshFxFailure = 4
|
||||
sshFxBadMessage = 5
|
||||
sshFxNoConnection = 6
|
||||
sshFxConnectionLost = 7
|
||||
sshFxOPUnsupported = 8
|
||||
|
||||
// see draft-ietf-secsh-filexfer-13
|
||||
// https://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-9.1
|
||||
sshFxInvalidHandle = 9
|
||||
sshFxNoSuchPath = 10
|
||||
sshFxFileAlreadyExists = 11
|
||||
sshFxWriteProtect = 12
|
||||
sshFxNoMedia = 13
|
||||
sshFxNoSpaceOnFilesystem = 14
|
||||
sshFxQuotaExceeded = 15
|
||||
sshFxUnknownPrincipal = 16
|
||||
sshFxLockConflict = 17
|
||||
sshFxDirNotEmpty = 18
|
||||
sshFxNotADirectory = 19
|
||||
sshFxInvalidFilename = 20
|
||||
sshFxLinkLoop = 21
|
||||
sshFxCannotDelete = 22
|
||||
sshFxInvalidParameter = 23
|
||||
sshFxFileIsADirectory = 24
|
||||
sshFxByteRangeLockConflict = 25
|
||||
sshFxByteRangeLockRefused = 26
|
||||
sshFxDeletePending = 27
|
||||
sshFxFileCorrupt = 28
|
||||
sshFxOwnerInvalid = 29
|
||||
sshFxGroupInvalid = 30
|
||||
sshFxNoMatchingByteRangeLock = 31
|
||||
)
|
||||
|
||||
const (
|
||||
sshFxfRead = 0x00000001
|
||||
sshFxfWrite = 0x00000002
|
||||
sshFxfAppend = 0x00000004
|
||||
sshFxfCreat = 0x00000008
|
||||
sshFxfTrunc = 0x00000010
|
||||
sshFxfExcl = 0x00000020
|
||||
)
|
||||
|
||||
var (
|
||||
// supportedSFTPExtensions defines the supported extensions
|
||||
supportedSFTPExtensions = []sshExtensionPair{
|
||||
{"hardlink@openssh.com", "1"},
|
||||
{"posix-rename@openssh.com", "1"},
|
||||
{"statvfs@openssh.com", "2"},
|
||||
}
|
||||
sftpExtensions = supportedSFTPExtensions
|
||||
)
|
||||
|
||||
type fxp uint8
|
||||
|
||||
func (f fxp) String() string {
|
||||
switch f {
|
||||
case sshFxpInit:
|
||||
return "SSH_FXP_INIT"
|
||||
case sshFxpVersion:
|
||||
return "SSH_FXP_VERSION"
|
||||
case sshFxpOpen:
|
||||
return "SSH_FXP_OPEN"
|
||||
case sshFxpClose:
|
||||
return "SSH_FXP_CLOSE"
|
||||
case sshFxpRead:
|
||||
return "SSH_FXP_READ"
|
||||
case sshFxpWrite:
|
||||
return "SSH_FXP_WRITE"
|
||||
case sshFxpLstat:
|
||||
return "SSH_FXP_LSTAT"
|
||||
case sshFxpFstat:
|
||||
return "SSH_FXP_FSTAT"
|
||||
case sshFxpSetstat:
|
||||
return "SSH_FXP_SETSTAT"
|
||||
case sshFxpFsetstat:
|
||||
return "SSH_FXP_FSETSTAT"
|
||||
case sshFxpOpendir:
|
||||
return "SSH_FXP_OPENDIR"
|
||||
case sshFxpReaddir:
|
||||
return "SSH_FXP_READDIR"
|
||||
case sshFxpRemove:
|
||||
return "SSH_FXP_REMOVE"
|
||||
case sshFxpMkdir:
|
||||
return "SSH_FXP_MKDIR"
|
||||
case sshFxpRmdir:
|
||||
return "SSH_FXP_RMDIR"
|
||||
case sshFxpRealpath:
|
||||
return "SSH_FXP_REALPATH"
|
||||
case sshFxpStat:
|
||||
return "SSH_FXP_STAT"
|
||||
case sshFxpRename:
|
||||
return "SSH_FXP_RENAME"
|
||||
case sshFxpReadlink:
|
||||
return "SSH_FXP_READLINK"
|
||||
case sshFxpSymlink:
|
||||
return "SSH_FXP_SYMLINK"
|
||||
case sshFxpStatus:
|
||||
return "SSH_FXP_STATUS"
|
||||
case sshFxpHandle:
|
||||
return "SSH_FXP_HANDLE"
|
||||
case sshFxpData:
|
||||
return "SSH_FXP_DATA"
|
||||
case sshFxpName:
|
||||
return "SSH_FXP_NAME"
|
||||
case sshFxpAttrs:
|
||||
return "SSH_FXP_ATTRS"
|
||||
case sshFxpExtended:
|
||||
return "SSH_FXP_EXTENDED"
|
||||
case sshFxpExtendedReply:
|
||||
return "SSH_FXP_EXTENDED_REPLY"
|
||||
default:
|
||||
return "unknown"
|
||||
}
|
||||
}
|
||||
|
||||
type fx uint8
|
||||
|
||||
func (f fx) String() string {
|
||||
switch f {
|
||||
case sshFxOk:
|
||||
return "SSH_FX_OK"
|
||||
case sshFxEOF:
|
||||
return "SSH_FX_EOF"
|
||||
case sshFxNoSuchFile:
|
||||
return "SSH_FX_NO_SUCH_FILE"
|
||||
case sshFxPermissionDenied:
|
||||
return "SSH_FX_PERMISSION_DENIED"
|
||||
case sshFxFailure:
|
||||
return "SSH_FX_FAILURE"
|
||||
case sshFxBadMessage:
|
||||
return "SSH_FX_BAD_MESSAGE"
|
||||
case sshFxNoConnection:
|
||||
return "SSH_FX_NO_CONNECTION"
|
||||
case sshFxConnectionLost:
|
||||
return "SSH_FX_CONNECTION_LOST"
|
||||
case sshFxOPUnsupported:
|
||||
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 fmt.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 %d, got %d", u.want, u.got)
|
||||
}
|
||||
|
||||
func unimplementedSeekWhence(whence int) error {
|
||||
return fmt.Errorf("sftp: unimplemented seek whence %d", whence)
|
||||
}
|
||||
|
||||
func unexpectedCount(want, got uint32) error {
|
||||
return fmt.Errorf("sftp: unexpected count: want %d, got %d", 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))
|
||||
}
|
||||
|
||||
// FxCode returns the error code typed to match against the exported codes
|
||||
func (s *StatusError) FxCode() fxerr {
|
||||
return fxerr(s.Code)
|
||||
}
|
||||
|
||||
func getSupportedExtensionByName(extensionName string) (sshExtensionPair, error) {
|
||||
for _, supportedExtension := range supportedSFTPExtensions {
|
||||
if supportedExtension.Name == extensionName {
|
||||
return supportedExtension, nil
|
||||
}
|
||||
}
|
||||
return sshExtensionPair{}, fmt.Errorf("unsupported extension: %s", extensionName)
|
||||
}
|
||||
|
||||
// SetSFTPExtensions allows to customize the supported server extensions.
|
||||
// See the variable supportedSFTPExtensions for supported extensions.
|
||||
// This method accepts a slice of sshExtensionPair names for example 'hardlink@openssh.com'.
|
||||
// If an invalid extension is given an error will be returned and nothing will be changed
|
||||
func SetSFTPExtensions(extensions ...string) error {
|
||||
tempExtensions := []sshExtensionPair{}
|
||||
for _, extension := range extensions {
|
||||
sftpExtension, err := getSupportedExtensionByName(extension)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tempExtensions = append(tempExtensions, sftpExtension)
|
||||
}
|
||||
sftpExtensions = tempExtensions
|
||||
return nil
|
||||
}
|
||||
103
vendor/github.com/pkg/sftp/stat_plan9.go
generated
vendored
Normal file
103
vendor/github.com/pkg/sftp/stat_plan9.go
generated
vendored
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
package sftp
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
var EBADF = syscall.NewError("fd out of range or not open")
|
||||
|
||||
func wrapPathError(filepath string, err error) error {
|
||||
if errno, ok := err.(syscall.ErrorString); ok {
|
||||
return &os.PathError{Path: filepath, Err: errno}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// translateErrno translates a syscall error number to a SFTP error code.
|
||||
func translateErrno(errno syscall.ErrorString) uint32 {
|
||||
switch errno {
|
||||
case "":
|
||||
return sshFxOk
|
||||
case syscall.ENOENT:
|
||||
return sshFxNoSuchFile
|
||||
case syscall.EPERM:
|
||||
return sshFxPermissionDenied
|
||||
}
|
||||
|
||||
return sshFxFailure
|
||||
}
|
||||
|
||||
func translateSyscallError(err error) (uint32, bool) {
|
||||
switch e := err.(type) {
|
||||
case syscall.ErrorString:
|
||||
return translateErrno(e), true
|
||||
case *os.PathError:
|
||||
debug("statusFromError,pathError: error is %T %#v", e.Err, e.Err)
|
||||
if errno, ok := e.Err.(syscall.ErrorString); ok {
|
||||
return translateErrno(errno), true
|
||||
}
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// isRegular returns true if the mode describes a regular file.
|
||||
func isRegular(mode uint32) bool {
|
||||
return mode&S_IFMT == syscall.S_IFREG
|
||||
}
|
||||
|
||||
// toFileMode converts sftp filemode bits to the os.FileMode specification
|
||||
func toFileMode(mode uint32) os.FileMode {
|
||||
var fm = os.FileMode(mode & 0777)
|
||||
|
||||
switch mode & 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
|
||||
}
|
||||
|
||||
return fm
|
||||
}
|
||||
|
||||
// fromFileMode converts from the os.FileMode specification to sftp filemode bits
|
||||
func fromFileMode(mode os.FileMode) uint32 {
|
||||
ret := uint32(mode & os.ModePerm)
|
||||
|
||||
switch mode & os.ModeType {
|
||||
case os.ModeDevice | os.ModeCharDevice:
|
||||
ret |= syscall.S_IFCHR
|
||||
case os.ModeDevice:
|
||||
ret |= syscall.S_IFBLK
|
||||
case os.ModeDir:
|
||||
ret |= syscall.S_IFDIR
|
||||
case os.ModeNamedPipe:
|
||||
ret |= syscall.S_IFIFO
|
||||
case os.ModeSymlink:
|
||||
ret |= syscall.S_IFLNK
|
||||
case 0:
|
||||
ret |= syscall.S_IFREG
|
||||
case os.ModeSocket:
|
||||
ret |= syscall.S_IFSOCK
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
// Plan 9 doesn't have setuid, setgid or sticky, but a Plan 9 client should
|
||||
// be able to send these bits to a POSIX server.
|
||||
const (
|
||||
s_ISUID = 04000
|
||||
s_ISGID = 02000
|
||||
s_ISVTX = 01000
|
||||
)
|
||||
123
vendor/github.com/pkg/sftp/stat_posix.go
generated
vendored
Normal file
123
vendor/github.com/pkg/sftp/stat_posix.go
generated
vendored
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
// +build !plan9
|
||||
|
||||
package sftp
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
const EBADF = syscall.EBADF
|
||||
|
||||
func wrapPathError(filepath string, err error) error {
|
||||
if errno, ok := err.(syscall.Errno); ok {
|
||||
return &os.PathError{Path: filepath, Err: errno}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// translateErrno translates a syscall error number to a SFTP error code.
|
||||
func translateErrno(errno syscall.Errno) uint32 {
|
||||
switch errno {
|
||||
case 0:
|
||||
return sshFxOk
|
||||
case syscall.ENOENT:
|
||||
return sshFxNoSuchFile
|
||||
case syscall.EPERM:
|
||||
return sshFxPermissionDenied
|
||||
}
|
||||
|
||||
return sshFxFailure
|
||||
}
|
||||
|
||||
func translateSyscallError(err error) (uint32, bool) {
|
||||
switch e := err.(type) {
|
||||
case syscall.Errno:
|
||||
return translateErrno(e), true
|
||||
case *os.PathError:
|
||||
debug("statusFromError,pathError: error is %T %#v", e.Err, e.Err)
|
||||
if errno, ok := e.Err.(syscall.Errno); ok {
|
||||
return translateErrno(errno), true
|
||||
}
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// isRegular returns true if the mode describes a regular file.
|
||||
func isRegular(mode uint32) bool {
|
||||
return mode&S_IFMT == syscall.S_IFREG
|
||||
}
|
||||
|
||||
// toFileMode converts sftp filemode bits to the os.FileMode specification
|
||||
func toFileMode(mode uint32) os.FileMode {
|
||||
var fm = os.FileMode(mode & 0777)
|
||||
|
||||
switch mode & 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_ISUID != 0 {
|
||||
fm |= os.ModeSetuid
|
||||
}
|
||||
if mode&syscall.S_ISGID != 0 {
|
||||
fm |= os.ModeSetgid
|
||||
}
|
||||
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(mode & os.ModePerm)
|
||||
|
||||
switch mode & os.ModeType {
|
||||
case os.ModeDevice | os.ModeCharDevice:
|
||||
ret |= syscall.S_IFCHR
|
||||
case os.ModeDevice:
|
||||
ret |= syscall.S_IFBLK
|
||||
case os.ModeDir:
|
||||
ret |= syscall.S_IFDIR
|
||||
case os.ModeNamedPipe:
|
||||
ret |= syscall.S_IFIFO
|
||||
case os.ModeSymlink:
|
||||
ret |= syscall.S_IFLNK
|
||||
case 0:
|
||||
ret |= syscall.S_IFREG
|
||||
case os.ModeSocket:
|
||||
ret |= syscall.S_IFSOCK
|
||||
}
|
||||
|
||||
if mode&os.ModeSetuid != 0 {
|
||||
ret |= syscall.S_ISUID
|
||||
}
|
||||
if mode&os.ModeSetgid != 0 {
|
||||
ret |= syscall.S_ISGID
|
||||
}
|
||||
if mode&os.ModeSticky != 0 {
|
||||
ret |= syscall.S_ISVTX
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
const (
|
||||
s_ISUID = syscall.S_ISUID
|
||||
s_ISGID = syscall.S_ISGID
|
||||
s_ISVTX = syscall.S_ISVTX
|
||||
)
|
||||
9
vendor/github.com/pkg/sftp/syscall_fixed.go
generated
vendored
Normal file
9
vendor/github.com/pkg/sftp/syscall_fixed.go
generated
vendored
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
// +build plan9 windows js,wasm
|
||||
|
||||
// Go defines S_IFMT on windows, plan9 and js/wasm as 0x1f000 instead of
|
||||
// 0xf000. None of the the other S_IFxyz values include the "1" (in 0x1f000)
|
||||
// which prevents them from matching the bitmask.
|
||||
|
||||
package sftp
|
||||
|
||||
const S_IFMT = 0xf000
|
||||
8
vendor/github.com/pkg/sftp/syscall_good.go
generated
vendored
Normal file
8
vendor/github.com/pkg/sftp/syscall_good.go
generated
vendored
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
// +build !plan9,!windows
|
||||
// +build !js !wasm
|
||||
|
||||
package sftp
|
||||
|
||||
import "syscall"
|
||||
|
||||
const S_IFMT = syscall.S_IFMT
|
||||
Loading…
Add table
Add a link
Reference in a new issue