2013-11-05 13:28:45 +08:00
|
|
|
package sftp
|
|
|
|
|
|
|
|
// SSH_FXP_ATTRS support
|
|
|
|
// see http://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#section-5
|
|
|
|
|
|
|
|
import (
|
|
|
|
"os"
|
2013-11-05 14:21:30 +08:00
|
|
|
"syscall"
|
2013-11-05 13:28:45 +08:00
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
SSH_FILEXFER_ATTR_SIZE = 0x00000001
|
|
|
|
SSH_FILEXFER_ATTR_UIDGID = 0x00000002
|
|
|
|
SSH_FILEXFER_ATTR_PERMISSIONS = 0x00000004
|
|
|
|
SSH_FILEXFER_ATTR_ACMODTIME = 0x00000008
|
|
|
|
SSH_FILEXFER_ATTR_EXTENDED = 0x80000000
|
|
|
|
)
|
|
|
|
|
|
|
|
type attr struct {
|
2013-11-05 14:21:30 +08:00
|
|
|
name string
|
2013-11-05 13:28:45 +08:00
|
|
|
size uint64
|
|
|
|
mode os.FileMode
|
|
|
|
mtime time.Time
|
|
|
|
}
|
|
|
|
|
|
|
|
// Name returns the base name of the file.
|
2013-11-05 19:11:34 +08:00
|
|
|
func (a *attr) Name() string { return a.name }
|
2013-11-05 13:28:45 +08:00
|
|
|
|
|
|
|
// Size returns the length in bytes for regular files; system-dependent for others.
|
|
|
|
func (a *attr) Size() int64 { return int64(a.size) }
|
|
|
|
|
|
|
|
// Mode returns file mode bits.
|
|
|
|
func (a *attr) Mode() os.FileMode { return a.mode }
|
|
|
|
|
|
|
|
// ModTime returns the last modification time of the file.
|
|
|
|
func (a *attr) ModTime() time.Time { return a.mtime }
|
|
|
|
|
|
|
|
// IsDir returns true if the file is a directory.
|
|
|
|
func (a *attr) IsDir() bool { return a.Mode().IsDir() }
|
|
|
|
|
|
|
|
func (a *attr) Sys() interface{} { return a }
|
|
|
|
|
|
|
|
func unmarshalAttrs(b []byte) (*attr, []byte) {
|
|
|
|
flags, b := unmarshalUint32(b)
|
|
|
|
var a attr
|
|
|
|
if flags&SSH_FILEXFER_ATTR_SIZE == SSH_FILEXFER_ATTR_SIZE {
|
|
|
|
a.size, b = unmarshalUint64(b)
|
|
|
|
}
|
|
|
|
if flags&SSH_FILEXFER_ATTR_UIDGID == SSH_FILEXFER_ATTR_UIDGID {
|
|
|
|
_, b = unmarshalUint32(b) // discarded
|
|
|
|
}
|
|
|
|
if flags&SSH_FILEXFER_ATTR_UIDGID == SSH_FILEXFER_ATTR_UIDGID {
|
|
|
|
_, b = unmarshalUint32(b) // discarded
|
|
|
|
}
|
|
|
|
if flags&SSH_FILEXFER_ATTR_PERMISSIONS == SSH_FILEXFER_ATTR_PERMISSIONS {
|
|
|
|
var mode uint32
|
|
|
|
mode, b = unmarshalUint32(b)
|
2013-11-05 14:21:30 +08:00
|
|
|
a.mode = toFileMode(mode)
|
2013-11-05 13:28:45 +08:00
|
|
|
}
|
|
|
|
if flags&SSH_FILEXFER_ATTR_ACMODTIME == SSH_FILEXFER_ATTR_ACMODTIME {
|
|
|
|
var mtime uint32
|
|
|
|
_, b = unmarshalUint32(b) // discarded
|
|
|
|
mtime, b = unmarshalUint32(b)
|
|
|
|
a.mtime = time.Unix(int64(mtime), 0)
|
|
|
|
}
|
|
|
|
if flags&SSH_FILEXFER_ATTR_EXTENDED == SSH_FILEXFER_ATTR_EXTENDED {
|
|
|
|
var count uint32
|
|
|
|
count, b = unmarshalUint32(b)
|
|
|
|
for i := uint32(0); i < count; i++ {
|
|
|
|
_, b = unmarshalString(b)
|
|
|
|
_, b = unmarshalString(b)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return &a, b
|
|
|
|
}
|
2013-11-05 14:21:30 +08:00
|
|
|
|
|
|
|
// toFileMode converts sftp filemode bits to the os.FileMode specification
|
|
|
|
func toFileMode(mode uint32) os.FileMode {
|
|
|
|
var fm = os.FileMode(mode & 0777)
|
|
|
|
switch mode & syscall.S_IFMT {
|
|
|
|
case syscall.S_IFBLK:
|
|
|
|
fm |= os.ModeDevice
|
|
|
|
case syscall.S_IFCHR:
|
|
|
|
fm |= os.ModeDevice | os.ModeCharDevice
|
|
|
|
case syscall.S_IFDIR:
|
|
|
|
fm |= os.ModeDir
|
|
|
|
case syscall.S_IFIFO:
|
|
|
|
fm |= os.ModeNamedPipe
|
|
|
|
case syscall.S_IFLNK:
|
|
|
|
fm |= os.ModeSymlink
|
|
|
|
case syscall.S_IFREG:
|
|
|
|
// nothing to do
|
|
|
|
case syscall.S_IFSOCK:
|
|
|
|
fm |= os.ModeSocket
|
|
|
|
}
|
|
|
|
if mode&syscall.S_ISGID != 0 {
|
|
|
|
fm |= os.ModeSetgid
|
|
|
|
}
|
|
|
|
if mode&syscall.S_ISUID != 0 {
|
|
|
|
fm |= os.ModeSetuid
|
|
|
|
}
|
|
|
|
if mode&syscall.S_ISVTX != 0 {
|
|
|
|
fm |= os.ModeSticky
|
|
|
|
}
|
|
|
|
return fm
|
|
|
|
}
|