sftp/request.go

245 lines
5.4 KiB
Go
Raw Normal View History

2016-07-09 03:38:35 +08:00
package sftp
2016-07-09 08:22:52 +08:00
import (
"io"
"os"
"path"
"path/filepath"
2016-07-30 06:57:06 +08:00
"sync"
2016-07-09 08:22:52 +08:00
"syscall"
)
2016-07-26 02:42:18 +08:00
// Request contains the data and state for the incoming service request.
2016-07-09 03:38:35 +08:00
type Request struct {
2016-07-15 03:07:16 +08:00
// Get, Put, SetStat, Stat, Rename, Remove
// Rmdir, Mkdir, List, Readlink, Symlink
2016-07-09 03:38:35 +08:00
Method string
Filepath string
Attrs []byte // convert to sub-struct
Target string // for renames and sym-links
2016-07-12 11:19:49 +08:00
// packet data
2016-07-30 06:57:06 +08:00
packets []packet_data
packetsLock sync.RWMutex
2016-07-12 11:19:49 +08:00
// reader/writer from handlers
2016-07-30 06:57:06 +08:00
put_writer io.WriterAt
get_reader io.ReaderAt
2016-07-15 05:45:50 +08:00
eof bool // hack for readdir to keep eof state
2016-07-09 03:38:35 +08:00
}
2016-07-30 06:57:06 +08:00
type packet_data struct {
id uint32
data []byte
length uint32
offset int64
}
2016-07-13 02:23:03 +08:00
// Here mainly to specify that Filepath is required
2016-07-12 11:19:49 +08:00
func newRequest(path string) *Request {
2016-07-19 02:06:53 +08:00
request := &Request{Filepath: filepath.Clean(path)}
2016-07-09 03:38:35 +08:00
return request
}
2016-07-30 06:57:06 +08:00
// push packet_data into fifo
func (r *Request) pushPacket(pd packet_data) {
r.packetsLock.Lock()
defer r.packetsLock.Unlock()
r.packets = append(r.packets, pd)
}
// pop packet_data into fifo
func (r *Request) popPacket() packet_data {
r.packetsLock.Lock()
defer r.packetsLock.Unlock()
var pd packet_data
pd, r.packets = r.packets[0], r.packets[1:]
return pd
}
func (r *Request) pkt_id() uint32 {
return r.packets[0].id
}
2016-07-13 02:23:03 +08:00
// called from worker to handle packet/request
2016-07-26 02:52:07 +08:00
func (r *Request) handle(handlers Handlers) (responsePacket, error) {
2016-07-12 11:19:49 +08:00
var err error
2016-07-26 02:52:07 +08:00
var rpkt responsePacket
2016-07-12 11:19:49 +08:00
switch r.Method {
case "Get":
rpkt, err = fileget(handlers.FileGet, r)
case "Put": // add "Append" to this to handle append only file writes
2016-07-12 11:19:49 +08:00
rpkt, err = fileput(handlers.FilePut, r)
2016-07-15 03:07:16 +08:00
case "SetStat", "Rename", "Rmdir", "Mkdir", "Symlink", "Remove":
2016-07-12 11:19:49 +08:00
rpkt, err = filecmd(handlers.FileCmd, r)
case "List", "Stat", "Readlink":
2016-07-12 11:19:49 +08:00
rpkt, err = fileinfo(handlers.FileInfo, r)
2016-07-09 03:38:35 +08:00
}
2016-07-13 07:50:59 +08:00
return rpkt, err
2016-07-09 03:38:35 +08:00
}
2016-07-13 02:23:03 +08:00
// wrap FileReader handler
2016-07-26 02:52:07 +08:00
func fileget(h FileReader, r *Request) (responsePacket, error) {
2016-07-12 11:19:49 +08:00
if r.get_reader == nil {
reader, err := h.Fileread(r)
2016-07-26 02:42:18 +08:00
if err != nil {
return nil, syscall.EBADF
}
2016-07-12 11:19:49 +08:00
r.get_reader = reader
}
reader := r.get_reader
2016-07-30 06:57:06 +08:00
pd := r.popPacket()
data := make([]byte, clamp(pd.length, maxTxPacket))
n, err := reader.ReadAt(data, pd.offset)
2016-07-26 02:42:18 +08:00
if err != nil && (err != io.EOF || n == 0) {
return nil, err
}
return &sshFxpDataPacket{
2016-07-30 06:57:06 +08:00
ID: pd.id,
2016-07-09 08:22:52 +08:00
Length: uint32(n),
2016-07-13 05:56:24 +08:00
Data: data[:n],
}, nil
2016-07-09 08:22:52 +08:00
}
2016-07-13 02:23:03 +08:00
// wrap FileWriter handler
2016-07-26 02:52:07 +08:00
func fileput(h FileWriter, r *Request) (responsePacket, error) {
2016-07-12 11:19:49 +08:00
if r.put_writer == nil {
writer, err := h.Filewrite(r)
2016-07-26 02:42:18 +08:00
if err != nil {
return nil, syscall.EBADF
}
2016-07-12 11:19:49 +08:00
r.put_writer = writer
}
writer := r.put_writer
2016-07-30 06:57:06 +08:00
pd := r.popPacket()
_, err := writer.WriteAt(pd.data, pd.offset)
2016-07-26 02:42:18 +08:00
if err != nil {
return nil, err
}
return &sshFxpStatusPacket{
2016-07-30 06:57:06 +08:00
ID: pd.id,
StatusError: StatusError{
Code: ssh_FX_OK,
}}, nil
2016-07-09 08:22:52 +08:00
}
2016-07-13 02:23:03 +08:00
// wrap FileCmder handler
2016-07-26 02:52:07 +08:00
func filecmd(h FileCmder, r *Request) (responsePacket, error) {
2016-07-09 08:22:52 +08:00
err := h.Filecmd(r)
2016-07-26 02:42:18 +08:00
if err != nil {
return nil, err
}
2016-07-13 05:56:24 +08:00
return &sshFxpStatusPacket{
2016-07-30 06:57:06 +08:00
ID: r.pkt_id(),
StatusError: StatusError{
Code: ssh_FX_OK,
}}, nil
2016-07-09 08:22:52 +08:00
}
2016-07-13 02:23:03 +08:00
// wrap FileInfoer handler
2016-07-26 02:52:07 +08:00
func fileinfo(h FileInfoer, r *Request) (responsePacket, error) {
2016-07-26 02:42:18 +08:00
if r.eof {
return nil, io.EOF
}
2016-07-09 08:22:52 +08:00
finfo, err := h.Fileinfo(r)
2016-07-26 02:42:18 +08:00
if err != nil {
return nil, err
}
2016-07-09 08:22:52 +08:00
switch r.Method {
case "List":
dirname := path.Base(r.Filepath)
2016-07-30 06:57:06 +08:00
ret := &sshFxpNamePacket{ID: r.pkt_id()}
2016-07-09 08:22:52 +08:00
for _, fi := range finfo {
ret.NameAttrs = append(ret.NameAttrs, sshFxpNameAttr{
Name: fi.Name(),
LongName: runLs(dirname, fi),
Attrs: []interface{}{fi},
})
}
2016-07-15 05:45:50 +08:00
r.eof = true
2016-07-13 05:56:24 +08:00
return ret, nil
2016-07-09 08:22:52 +08:00
case "Stat":
if len(finfo) == 0 {
2016-07-26 04:01:16 +08:00
err = &os.PathError{Op: "stat", Path: r.Filepath,
Err: syscall.ENOENT}
return nil, err
2016-07-09 08:22:52 +08:00
}
return &sshFxpStatResponse{
2016-07-30 06:57:06 +08:00
ID: r.pkt_id(),
2016-07-09 08:22:52 +08:00
info: finfo[0],
}, nil
case "Readlink":
2016-07-09 08:22:52 +08:00
if len(finfo) == 0 {
2016-07-26 04:01:16 +08:00
err = &os.PathError{Op: "readlink", Path: r.Filepath,
Err: syscall.ENOENT}
return nil, err
2016-07-09 08:22:52 +08:00
}
2016-07-23 08:11:09 +08:00
filename := finfo[0].Name()
2016-07-13 05:56:24 +08:00
return &sshFxpNamePacket{
2016-07-30 06:57:06 +08:00
ID: r.pkt_id(),
2016-07-09 08:22:52 +08:00
NameAttrs: []sshFxpNameAttr{{
Name: filename,
LongName: filename,
2016-07-09 08:22:52 +08:00
Attrs: emptyFileStat,
}},
}, nil
2016-07-09 08:22:52 +08:00
}
return nil, err
2016-07-09 08:22:52 +08:00
}
2016-07-13 02:23:03 +08:00
// populate attributes of request object from packet data
2016-07-09 03:38:35 +08:00
func (r *Request) populate(p interface{}) {
2016-07-13 05:56:24 +08:00
// r.Filepath should already be set
2016-07-30 06:57:06 +08:00
var pd packet_data
2016-07-09 03:38:35 +08:00
switch p := p.(type) {
case *sshFxpSetstatPacket:
r.Method = "Setstat"
r.Attrs = p.Attrs.([]byte)
2016-07-30 06:57:06 +08:00
pd.id = p.id()
2016-07-09 03:38:35 +08:00
case *sshFxpFsetstatPacket:
r.Method = "Setstat"
r.Attrs = p.Attrs.([]byte)
2016-07-30 06:57:06 +08:00
pd.id = p.id()
2016-07-09 03:38:35 +08:00
case *sshFxpRenamePacket:
r.Method = "Rename"
2016-07-19 02:06:53 +08:00
r.Target = filepath.Clean(p.Newpath)
2016-07-30 06:57:06 +08:00
pd.id = p.id()
2016-07-09 03:38:35 +08:00
case *sshFxpSymlinkPacket:
r.Method = "Symlink"
2016-07-19 02:06:53 +08:00
r.Target = filepath.Clean(p.Linkpath)
2016-07-30 06:57:06 +08:00
pd.id = p.id()
2016-07-12 05:54:46 +08:00
case *sshFxpReadPacket:
r.Method = "Get"
2016-07-30 06:57:06 +08:00
pd.length = p.Len
pd.offset = int64(p.Offset)
pd.id = p.id()
2016-07-12 05:54:46 +08:00
case *sshFxpWritePacket:
r.Method = "Put"
2016-07-30 06:57:06 +08:00
pd.id = p.id()
pd.data = p.Data
pd.length = p.Length
pd.offset = int64(p.Offset)
2016-07-09 03:38:35 +08:00
case *sshFxpReaddirPacket:
r.Method = "List"
2016-07-30 06:57:06 +08:00
pd.id = p.id()
2016-07-15 05:12:46 +08:00
case *sshFxpRemovePacket:
r.Method = "Remove"
2016-07-30 06:57:06 +08:00
pd.id = p.id()
2016-07-15 05:12:46 +08:00
case *sshFxpStatPacket, *sshFxpLstatPacket, *sshFxpFstatPacket:
2016-07-09 03:38:35 +08:00
r.Method = "Stat"
2016-07-30 06:57:06 +08:00
pd.id = p.(packet).id()
2016-07-09 03:38:35 +08:00
case *sshFxpRmdirPacket:
r.Method = "Rmdir"
2016-07-30 06:57:06 +08:00
pd.id = p.id()
2016-07-09 03:38:35 +08:00
case *sshFxpReadlinkPacket:
r.Method = "Readlink"
2016-07-30 06:57:06 +08:00
pd.id = p.id()
2016-07-09 03:38:35 +08:00
case *sshFxpMkdirPacket:
r.Method = "Mkdir"
2016-07-30 06:57:06 +08:00
pd.id = p.id()
2016-07-09 08:22:52 +08:00
//r.Attrs are ignored in ./packet.go
2016-07-09 03:38:35 +08:00
}
2016-07-30 06:57:06 +08:00
r.pushPacket(pd)
2016-07-09 03:38:35 +08:00
}