summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/pkg/sftp/request-example.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/pkg/sftp/request-example.go')
-rw-r--r--vendor/github.com/pkg/sftp/request-example.go261
1 files changed, 261 insertions, 0 deletions
diff --git a/vendor/github.com/pkg/sftp/request-example.go b/vendor/github.com/pkg/sftp/request-example.go
new file mode 100644
index 00000000..96191e0d
--- /dev/null
+++ b/vendor/github.com/pkg/sftp/request-example.go
@@ -0,0 +1,261 @@
+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 (
+ "bytes"
+ "fmt"
+ "io"
+ "os"
+ "path/filepath"
+ "sort"
+ "sync"
+ "time"
+)
+
+// InMemHandler returns a Hanlders object with the test handlers
+func InMemHandler() Handlers {
+ root := &root{
+ files: make(map[string]*memFile),
+ }
+ root.memFile = newMemFile("/", true)
+ return Handlers{root, root, root, root}
+}
+
+// Handlers
+func (fs *root) Fileread(r *Request) (io.ReaderAt, error) {
+ if fs.mockErr != nil {
+ return nil, fs.mockErr
+ }
+ fs.filesLock.Lock()
+ defer fs.filesLock.Unlock()
+ file, err := fs.fetch(r.Filepath)
+ if err != nil {
+ return nil, err
+ }
+ if file.symlink != "" {
+ file, err = fs.fetch(file.symlink)
+ if err != nil {
+ return nil, err
+ }
+ }
+ return file.ReaderAt()
+}
+
+func (fs *root) Filewrite(r *Request) (io.WriterAt, error) {
+ if fs.mockErr != nil {
+ return nil, fs.mockErr
+ }
+ fs.filesLock.Lock()
+ defer fs.filesLock.Unlock()
+ file, err := fs.fetch(r.Filepath)
+ if err == os.ErrNotExist {
+ dir, err := fs.fetch(filepath.Dir(r.Filepath))
+ if err != nil {
+ return nil, err
+ }
+ if !dir.isdir {
+ return nil, os.ErrInvalid
+ }
+ file = newMemFile(r.Filepath, false)
+ fs.files[r.Filepath] = file
+ }
+ return file.WriterAt()
+}
+
+func (fs *root) Filecmd(r *Request) error {
+ if fs.mockErr != nil {
+ return fs.mockErr
+ }
+ fs.filesLock.Lock()
+ defer fs.filesLock.Unlock()
+ switch r.Method {
+ case "Setstat":
+ return nil
+ case "Rename":
+ file, err := fs.fetch(r.Filepath)
+ if err != nil {
+ return err
+ }
+ if _, ok := fs.files[r.Target]; ok {
+ return &os.LinkError{Op: "rename", Old: r.Filepath, New: r.Target,
+ Err: fmt.Errorf("dest file exists")}
+ }
+ fs.files[r.Target] = file
+ delete(fs.files, r.Filepath)
+ case "Rmdir", "Remove":
+ _, err := fs.fetch(filepath.Dir(r.Filepath))
+ if err != nil {
+ return err
+ }
+ delete(fs.files, r.Filepath)
+ case "Mkdir":
+ _, err := fs.fetch(filepath.Dir(r.Filepath))
+ if err != nil {
+ return err
+ }
+ fs.files[r.Filepath] = newMemFile(r.Filepath, true)
+ case "Symlink":
+ _, err := fs.fetch(r.Filepath)
+ if err != nil {
+ return err
+ }
+ link := newMemFile(r.Target, false)
+ link.symlink = r.Filepath
+ fs.files[r.Target] = link
+ }
+ 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
+ }
+ fs.filesLock.Lock()
+ defer fs.filesLock.Unlock()
+
+ switch r.Method {
+ case "List":
+ ordered_names := []string{}
+ for fn, _ := range fs.files {
+ if filepath.Dir(fn) == r.Filepath {
+ ordered_names = append(ordered_names, fn)
+ }
+ }
+ sort.Strings(ordered_names)
+ list := make([]os.FileInfo, len(ordered_names))
+ for i, fn := range ordered_names {
+ list[i] = fs.files[fn]
+ }
+ return listerat(list), nil
+ case "Stat":
+ file, err := fs.fetch(r.Filepath)
+ if err != nil {
+ return nil, err
+ }
+ return listerat([]os.FileInfo{file}), nil
+ case "Readlink":
+ file, err := fs.fetch(r.Filepath)
+ if err != nil {
+ return nil, err
+ }
+ if file.symlink != "" {
+ file, err = fs.fetch(file.symlink)
+ if err != nil {
+ return nil, err
+ }
+ }
+ return listerat([]os.FileInfo{file}), nil
+ }
+ return nil, nil
+}
+
+// In memory file-system-y thing that the Hanlders live on
+type root struct {
+ *memFile
+ files map[string]*memFile
+ filesLock sync.Mutex
+ mockErr error
+}
+
+// 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) fetch(path string) (*memFile, error) {
+ if path == "/" {
+ return fs.memFile, nil
+ }
+ if file, ok := fs.files[path]; ok {
+ return file, nil
+ }
+ return nil, os.ErrNotExist
+}
+
+// Implements os.FileInfo, Reader and Writer interfaces.
+// These are the 3 interfaces necessary for the Handlers.
+type memFile struct {
+ name string
+ modtime time.Time
+ symlink string
+ isdir bool
+ content []byte
+ contentLock sync.RWMutex
+}
+
+// factory to make sure modtime is set
+func newMemFile(name string, isdir bool) *memFile {
+ return &memFile{
+ name: name,
+ modtime: time.Now(),
+ isdir: isdir,
+ }
+}
+
+// Have memFile fulfill os.FileInfo interface
+func (f *memFile) Name() string { return filepath.Base(f.name) }
+func (f *memFile) Size() int64 { return int64(len(f.content)) }
+func (f *memFile) Mode() os.FileMode {
+ ret := os.FileMode(0644)
+ if f.isdir {
+ ret = os.FileMode(0755) | os.ModeDir
+ }
+ if f.symlink != "" {
+ ret = os.FileMode(0777) | os.ModeSymlink
+ }
+ return ret
+}
+func (f *memFile) ModTime() time.Time { return f.modtime }
+func (f *memFile) IsDir() bool { return f.isdir }
+func (f *memFile) Sys() interface{} {
+ return fakeFileInfoSys()
+}
+
+// Read/Write
+func (f *memFile) ReaderAt() (io.ReaderAt, error) {
+ if f.isdir {
+ return nil, os.ErrInvalid
+ }
+ return bytes.NewReader(f.content), nil
+}
+
+func (f *memFile) WriterAt() (io.WriterAt, error) {
+ if f.isdir {
+ return nil, os.ErrInvalid
+ }
+ return f, nil
+}
+func (f *memFile) WriteAt(p []byte, off int64) (int, error) {
+ // fmt.Println(string(p), off)
+ // mimic write delays, should be optional
+ time.Sleep(time.Microsecond * time.Duration(len(p)))
+ f.contentLock.Lock()
+ defer f.contentLock.Unlock()
+ plen := len(p) + int(off)
+ if plen >= len(f.content) {
+ nc := make([]byte, plen)
+ copy(nc, f.content)
+ f.content = nc
+ }
+ copy(f.content[off:], p)
+ return len(p), nil
+}