sftp/request-example.go

212 lines
4.6 KiB
Go
Raw Normal View History

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"
2016-07-30 06:57:06 +08:00
"sync"
"syscall"
"time"
)
2016-07-26 02:42:18 +08:00
// 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
2016-07-30 06:57:06 +08:00
func (fs *root) Fileread(r *Request) (io.ReaderAt, error) {
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
}
}
2016-07-30 06:57:06 +08:00
return file.ReaderAt()
}
2016-07-30 06:57:06 +08:00
func (fs *root) Filewrite(r *Request) (io.WriterAt, error) {
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
}
2016-07-30 06:57:06 +08:00
return file.WriterAt()
}
func (fs *root) Filecmd(r *Request) error {
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 {
2016-07-26 04:01:16 +08:00
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
}
func (fs *root) Fileinfo(r *Request) ([]os.FileInfo, error) {
switch r.Method {
case "List":
list := []os.FileInfo{}
for fn, fi := range fs.files {
if filepath.Dir(fn) == r.Filepath {
list = append(list, fi)
}
}
return list, nil
case "Stat":
file, err := fs.fetch(r.Filepath)
if err != nil {
return nil, err
}
return []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 []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
}
2016-07-26 02:42:18 +08:00
func (fs *root) fetch(path string) (*memFile, error) {
if path == "/" {
2016-07-26 02:42:18 +08:00
return fs.memFile, nil
}
2016-07-26 02:42:18 +08:00
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 {
2016-07-30 06:57:06 +08:00
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
}
2016-07-20 02:25:33 +08:00
if f.symlink != "" {
ret = os.FileMode(0777) | os.ModeSymlink
}
return ret
}
func (f *memFile) ModTime() time.Time { return time.Now() }
func (f *memFile) IsDir() bool { return f.isdir }
func (f *memFile) Sys() interface{} {
return &syscall.Stat_t{Uid: 65534, Gid: 65534}
}
// Read/Write
2016-07-30 06:57:06 +08:00
func (f *memFile) ReaderAt() (io.ReaderAt, error) {
if f.isdir {
return nil, os.ErrInvalid
}
return bytes.NewReader(f.content), nil
}
2016-07-30 06:57:06 +08:00
func (f *memFile) WriterAt() (io.WriterAt, error) {
if f.isdir {
return nil, os.ErrInvalid
}
return f, nil
}
2016-07-30 06:57:06 +08:00
func (f *memFile) WriteAt(p []byte, off int64) (int, error) {
// mimic write delays, should be optional
time.Sleep(time.Microsecond * time.Duration(len(p)))
2016-07-30 06:57:06 +08:00
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
}