2016-07-09 03:38:35 +08:00
|
|
|
package sftp
|
|
|
|
|
2016-07-09 08:22:52 +08:00
|
|
|
import (
|
|
|
|
"io"
|
|
|
|
"os"
|
|
|
|
"path"
|
|
|
|
"syscall"
|
|
|
|
)
|
|
|
|
|
2016-07-12 04:13:06 +08:00
|
|
|
// response passed back to packet handling code
|
2016-07-12 04:56:47 +08:00
|
|
|
type response struct {
|
|
|
|
pkt resp_packet
|
2016-07-12 04:13:06 +08:00
|
|
|
err error
|
|
|
|
}
|
|
|
|
|
2016-07-09 03:38:35 +08:00
|
|
|
type Request struct {
|
2016-07-12 05:04:24 +08:00
|
|
|
// Get, Put, SetStat, Rename, Rmdir, Mkdir, Symlink, List, Stat, Readlink
|
2016-07-09 03:38:35 +08:00
|
|
|
Method string
|
|
|
|
Filepath string
|
|
|
|
Pflags uint32
|
|
|
|
Attrs []byte // convert to sub-struct
|
|
|
|
Target string // for renames and sym-links
|
2016-07-12 04:56:47 +08:00
|
|
|
data []byte
|
|
|
|
length uint32
|
2016-07-09 03:38:35 +08:00
|
|
|
pktChan chan packet
|
2016-07-12 04:56:47 +08:00
|
|
|
rspChan chan response
|
2016-07-12 05:04:24 +08:00
|
|
|
handlers Handlers
|
2016-07-09 03:38:35 +08:00
|
|
|
}
|
|
|
|
|
2016-07-12 05:04:24 +08:00
|
|
|
func newRequest(path string, handlers Handlers) *Request {
|
|
|
|
request := &Request{Filepath: path, handlers: handlers}
|
2016-07-09 03:38:35 +08:00
|
|
|
go request.requestWorker()
|
|
|
|
return request
|
|
|
|
}
|
|
|
|
|
2016-07-12 04:13:06 +08:00
|
|
|
func (r *Request) close() {
|
|
|
|
close(r.pktChan)
|
|
|
|
close(r.rspChan)
|
2016-07-09 03:38:35 +08:00
|
|
|
}
|
|
|
|
|
2016-07-12 04:13:06 +08:00
|
|
|
func (r *Request) requestWorker() {
|
2016-07-12 04:56:47 +08:00
|
|
|
for pkt := range r.pktChan {
|
|
|
|
r.populate(pkt)
|
2016-07-12 05:04:24 +08:00
|
|
|
handlers := r.handlers
|
2016-07-12 04:13:06 +08:00
|
|
|
var err error
|
2016-07-12 04:56:47 +08:00
|
|
|
var rpkt resp_packet
|
2016-07-09 08:22:52 +08:00
|
|
|
switch r.Method {
|
|
|
|
case "Get":
|
2016-07-12 04:56:47 +08:00
|
|
|
rpkt, err = fileget(handlers.FileGet, r, pkt.id())
|
2016-07-09 08:22:52 +08:00
|
|
|
case "Put":
|
2016-07-12 04:56:47 +08:00
|
|
|
rpkt, err = fileput(handlers.FilePut, r, pkt.id())
|
2016-07-09 08:22:52 +08:00
|
|
|
case "SetStat", "Rename", "Rmdir", "Mkdir", "Symlink":
|
2016-07-12 04:56:47 +08:00
|
|
|
rpkt, err = filecmd(handlers.FileCmd, r, pkt.id())
|
2016-07-09 08:22:52 +08:00
|
|
|
case "List", "Stat", "Readlink":
|
2016-07-12 04:56:47 +08:00
|
|
|
rpkt, err = fileinfo(handlers.FileInfo, r, pkt.id())
|
2016-07-09 08:22:52 +08:00
|
|
|
}
|
2016-07-12 04:56:47 +08:00
|
|
|
if err != nil { r.rspChan <- response{nil, err} }
|
|
|
|
r.rspChan <- response{rpkt, nil}
|
2016-07-09 03:38:35 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-07-12 04:56:47 +08:00
|
|
|
func fileget(h FileReader, r *Request, pkt_id uint32) (resp_packet, error) {
|
2016-07-12 02:16:08 +08:00
|
|
|
reader, err := h.Fileread(r)
|
2016-07-12 04:56:47 +08:00
|
|
|
if err != nil { return nil, syscall.EBADF }
|
|
|
|
data := make([]byte, clamp(r.length, maxTxPacket))
|
2016-07-09 08:22:52 +08:00
|
|
|
n, err := reader.Read(data)
|
2016-07-12 04:56:47 +08:00
|
|
|
if err != nil && (err != io.EOF || n == 0) { return nil, err }
|
|
|
|
return &sshFxpDataPacket{
|
|
|
|
ID: pkt_id,
|
2016-07-09 08:22:52 +08:00
|
|
|
Length: uint32(n),
|
2016-07-12 04:56:47 +08:00
|
|
|
Data: r.data[:n],
|
|
|
|
}, nil
|
2016-07-09 08:22:52 +08:00
|
|
|
}
|
2016-07-12 04:56:47 +08:00
|
|
|
func fileput(h FileWriter, r *Request, pkt_id uint32) (resp_packet, error) {
|
2016-07-12 02:16:08 +08:00
|
|
|
writer, err := h.Filewrite(r)
|
2016-07-12 04:56:47 +08:00
|
|
|
if err != nil { return nil, syscall.EBADF }
|
|
|
|
_, err = writer.Write(r.data)
|
|
|
|
if err != nil { return nil, err }
|
|
|
|
return &sshFxpStatusPacket{
|
|
|
|
ID: pkt_id,
|
|
|
|
StatusError: StatusError{
|
|
|
|
Code: ssh_FX_OK,
|
|
|
|
}}, nil
|
2016-07-09 08:22:52 +08:00
|
|
|
}
|
2016-07-12 04:56:47 +08:00
|
|
|
func filecmd(h FileCmder, r *Request, pkt_id uint32) (resp_packet, error) {
|
2016-07-09 08:22:52 +08:00
|
|
|
err := h.Filecmd(r)
|
2016-07-12 04:56:47 +08:00
|
|
|
if err != nil { return nil, err }
|
|
|
|
return sshFxpStatusPacket{
|
|
|
|
ID: pkt_id,
|
|
|
|
StatusError: StatusError{
|
|
|
|
Code: ssh_FX_OK,
|
|
|
|
}}, nil
|
2016-07-09 08:22:52 +08:00
|
|
|
}
|
2016-07-12 04:56:47 +08:00
|
|
|
func fileinfo(h FileInfoer, r *Request, pkt_id uint32) (resp_packet, error) {
|
2016-07-09 08:22:52 +08:00
|
|
|
finfo, err := h.Fileinfo(r)
|
2016-07-12 04:56:47 +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-12 04:56:47 +08:00
|
|
|
ret := sshFxpNamePacket{ID: 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},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
case "Stat":
|
|
|
|
if len(finfo) == 0 {
|
|
|
|
err = &os.PathError{"stat", r.Filepath, syscall.ENOENT}
|
2016-07-12 04:56:47 +08:00
|
|
|
return nil, err
|
2016-07-09 08:22:52 +08:00
|
|
|
}
|
2016-07-12 04:56:47 +08:00
|
|
|
return &sshFxpStatResponse{
|
|
|
|
ID: pkt_id,
|
2016-07-09 08:22:52 +08:00
|
|
|
info: finfo[0],
|
2016-07-12 04:56:47 +08:00
|
|
|
}, nil
|
2016-07-09 08:22:52 +08:00
|
|
|
case "Readlink":
|
|
|
|
if len(finfo) == 0 {
|
|
|
|
err = &os.PathError{"readlink", r.Filepath, syscall.ENOENT}
|
2016-07-12 04:56:47 +08:00
|
|
|
return nil, err
|
2016-07-09 08:22:52 +08:00
|
|
|
}
|
2016-07-12 04:56:47 +08:00
|
|
|
return sshFxpNamePacket{
|
|
|
|
ID: pkt_id,
|
2016-07-09 08:22:52 +08:00
|
|
|
NameAttrs: []sshFxpNameAttr{{
|
|
|
|
Name: finfo[0].Name(),
|
|
|
|
LongName: finfo[0].Name(),
|
|
|
|
Attrs: emptyFileStat,
|
|
|
|
}},
|
2016-07-12 04:56:47 +08:00
|
|
|
}, nil
|
2016-07-09 08:22:52 +08:00
|
|
|
}
|
2016-07-12 04:56:47 +08:00
|
|
|
return nil, err
|
2016-07-09 08:22:52 +08:00
|
|
|
}
|
|
|
|
|
2016-07-09 03:38:35 +08:00
|
|
|
func (r *Request) populate(p interface{}) {
|
2016-07-12 03:18:59 +08:00
|
|
|
// r.Filepath set in newRequest()
|
2016-07-09 03:38:35 +08:00
|
|
|
switch p := p.(type) {
|
|
|
|
case *sshFxpSetstatPacket:
|
|
|
|
r.Method = "Setstat"
|
|
|
|
r.Pflags = p.Flags
|
|
|
|
r.Attrs = p.Attrs.([]byte)
|
|
|
|
case *sshFxpFsetstatPacket:
|
|
|
|
r.Method = "Setstat"
|
|
|
|
r.Pflags = p.Flags
|
|
|
|
r.Attrs = p.Attrs.([]byte)
|
|
|
|
case *sshFxpRenamePacket:
|
|
|
|
r.Method = "Rename"
|
|
|
|
r.Target = p.Newpath
|
|
|
|
case *sshFxpSymlinkPacket:
|
|
|
|
r.Method = "Symlink"
|
|
|
|
r.Target = p.Linkpath
|
2016-07-12 05:54:46 +08:00
|
|
|
case *sshFxpReadPacket:
|
|
|
|
r.Method = "Get"
|
|
|
|
r.length = p.Len
|
|
|
|
case *sshFxpWritePacket:
|
|
|
|
r.Method = "Put"
|
|
|
|
r.data = p.Data
|
|
|
|
r.length = p.Length
|
2016-07-09 03:38:35 +08:00
|
|
|
// below here method and path are all the data
|
|
|
|
case *sshFxpReaddirPacket:
|
|
|
|
r.Method = "List"
|
|
|
|
case *sshFxpStatPacket, *sshFxpLstatPacket, *sshFxpFstatPacket,
|
|
|
|
*sshFxpRealpathPacket, *sshFxpRemovePacket:
|
|
|
|
r.Method = "Stat"
|
|
|
|
case *sshFxpRmdirPacket:
|
|
|
|
r.Method = "Rmdir"
|
|
|
|
case *sshFxpReadlinkPacket:
|
|
|
|
r.Method = "Readlink"
|
|
|
|
// special cases
|
|
|
|
case *sshFxpMkdirPacket:
|
|
|
|
r.Method = "Mkdir"
|
2016-07-09 08:22:52 +08:00
|
|
|
//r.Attrs are ignored in ./packet.go
|
2016-07-09 03:38:35 +08:00
|
|
|
}
|
|
|
|
}
|