mirror of
https://github.com/rclone/rclone.git
synced 2024-11-28 03:09:48 +08:00
155 lines
3.0 KiB
Go
155 lines
3.0 KiB
Go
|
// +build !plan9
|
||
|
|
||
|
package sftp
|
||
|
|
||
|
import (
|
||
|
"io"
|
||
|
"os"
|
||
|
"syscall"
|
||
|
"time"
|
||
|
|
||
|
"github.com/ncw/rclone/fs"
|
||
|
"github.com/ncw/rclone/vfs"
|
||
|
"github.com/pkg/sftp"
|
||
|
)
|
||
|
|
||
|
// vfsHandler converts the VFS to be served by SFTP
|
||
|
type vfsHandler struct {
|
||
|
*vfs.VFS
|
||
|
}
|
||
|
|
||
|
// vfsHandler returns a Handlers object with the test handlers.
|
||
|
func newVFSHandler(vfs *vfs.VFS) (sftp.Handlers, error) {
|
||
|
v := vfsHandler{VFS: vfs}
|
||
|
return sftp.Handlers{
|
||
|
FileGet: v,
|
||
|
FilePut: v,
|
||
|
FileCmd: v,
|
||
|
FileList: v,
|
||
|
}, nil
|
||
|
}
|
||
|
|
||
|
func (v vfsHandler) Fileread(r *sftp.Request) (io.ReaderAt, error) {
|
||
|
file, err := v.OpenFile(r.Filepath, os.O_RDONLY, 0777)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
return file, nil
|
||
|
}
|
||
|
|
||
|
func (v vfsHandler) Filewrite(r *sftp.Request) (io.WriterAt, error) {
|
||
|
file, err := v.OpenFile(r.Filepath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0777)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
return file, nil
|
||
|
}
|
||
|
|
||
|
func (v vfsHandler) Filecmd(r *sftp.Request) error {
|
||
|
switch r.Method {
|
||
|
case "Setstat":
|
||
|
node, err := v.Stat(r.Filepath)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
attr := r.Attributes()
|
||
|
if attr.Mtime != 0 {
|
||
|
modTime := time.Unix(int64(attr.Mtime), 0)
|
||
|
err := node.SetModTime(modTime)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
}
|
||
|
return nil
|
||
|
case "Rename":
|
||
|
err := v.Rename(r.Filepath, r.Target)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
case "Rmdir", "Remove":
|
||
|
node, err := v.Stat(r.Filepath)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
err = node.Remove()
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
case "Mkdir":
|
||
|
dir, leaf, err := v.StatParent(r.Filepath)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
_, err = dir.Mkdir(leaf)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
case "Symlink":
|
||
|
// FIXME
|
||
|
// _, err := v.fetch(r.Filepath)
|
||
|
// if err != nil {
|
||
|
// return err
|
||
|
// }
|
||
|
// link := newMemFile(r.Target, false)
|
||
|
// link.symlink = r.Filepath
|
||
|
// v.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 (v vfsHandler) Filelist(r *sftp.Request) (l sftp.ListerAt, err error) {
|
||
|
var node vfs.Node
|
||
|
var handle vfs.Handle
|
||
|
switch r.Method {
|
||
|
case "List":
|
||
|
node, err = v.Stat(r.Filepath)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
if !node.IsDir() {
|
||
|
return nil, syscall.ENOTDIR
|
||
|
}
|
||
|
handle, err = node.Open(os.O_RDONLY)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
defer fs.CheckClose(handle, &err)
|
||
|
fis, err := handle.Readdir(-1)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
return listerat(fis), nil
|
||
|
case "Stat":
|
||
|
node, err = v.Stat(r.Filepath)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
return listerat([]os.FileInfo{node}), nil
|
||
|
case "Readlink":
|
||
|
// FIXME
|
||
|
// if file.symlink != "" {
|
||
|
// file, err = v.fetch(file.symlink)
|
||
|
// if err != nil {
|
||
|
// return nil, err
|
||
|
// }
|
||
|
// }
|
||
|
// return listerat([]os.FileInfo{file}), nil
|
||
|
}
|
||
|
return nil, nil
|
||
|
}
|