123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238 |
- package sftp
- // ssh_FXP_ATTRS support
- // see http://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#section-5
- import (
- "os"
- "syscall"
- "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
- )
- // fileInfo is an artificial type designed to satisfy os.FileInfo.
- type fileInfo struct {
- name string
- size int64
- mode os.FileMode
- mtime time.Time
- sys interface{}
- }
- // Name returns the base name of the file.
- func (fi *fileInfo) Name() string { return fi.name }
- // Size returns the length in bytes for regular files; system-dependent for others.
- func (fi *fileInfo) Size() int64 { return fi.size }
- // Mode returns file mode bits.
- func (fi *fileInfo) Mode() os.FileMode { return fi.mode }
- // ModTime returns the last modification time of the file.
- func (fi *fileInfo) ModTime() time.Time { return fi.mtime }
- // IsDir returns true if the file is a directory.
- func (fi *fileInfo) IsDir() bool { return fi.Mode().IsDir() }
- func (fi *fileInfo) Sys() interface{} { return fi.sys }
- // FileStat holds the original unmarshalled values from a call to READDIR or *STAT.
- // It is exported for the purposes of accessing the raw values via os.FileInfo.Sys()
- type FileStat struct {
- Size uint64
- Mode uint32
- Mtime uint32
- Atime uint32
- UID uint32
- GID uint32
- Extended []StatExtended
- }
- // StatExtended contains additional, extended information for a FileStat.
- type StatExtended struct {
- ExtType string
- ExtData string
- }
- func fileInfoFromStat(st *FileStat, name string) os.FileInfo {
- fs := &fileInfo{
- name: name,
- size: int64(st.Size),
- mode: toFileMode(st.Mode),
- mtime: time.Unix(int64(st.Mtime), 0),
- sys: st,
- }
- return fs
- }
- func fileStatFromInfo(fi os.FileInfo) (uint32, FileStat) {
- mtime := fi.ModTime().Unix()
- atime := mtime
- var flags uint32 = ssh_FILEXFER_ATTR_SIZE |
- ssh_FILEXFER_ATTR_PERMISSIONS |
- ssh_FILEXFER_ATTR_ACMODTIME
- fileStat := FileStat{
- Size: uint64(fi.Size()),
- Mode: fromFileMode(fi.Mode()),
- Mtime: uint32(mtime),
- Atime: uint32(atime),
- }
- // os specific file stat decoding
- fileStatFromInfoOs(fi, &flags, &fileStat)
- return flags, fileStat
- }
- func unmarshalAttrs(b []byte) (*FileStat, []byte) {
- flags, b := unmarshalUint32(b)
- var fs FileStat
- if flags&ssh_FILEXFER_ATTR_SIZE == ssh_FILEXFER_ATTR_SIZE {
- fs.Size, b = unmarshalUint64(b)
- }
- if flags&ssh_FILEXFER_ATTR_UIDGID == ssh_FILEXFER_ATTR_UIDGID {
- fs.UID, b = unmarshalUint32(b)
- }
- if flags&ssh_FILEXFER_ATTR_UIDGID == ssh_FILEXFER_ATTR_UIDGID {
- fs.GID, b = unmarshalUint32(b)
- }
- if flags&ssh_FILEXFER_ATTR_PERMISSIONS == ssh_FILEXFER_ATTR_PERMISSIONS {
- fs.Mode, b = unmarshalUint32(b)
- }
- if flags&ssh_FILEXFER_ATTR_ACMODTIME == ssh_FILEXFER_ATTR_ACMODTIME {
- fs.Atime, b = unmarshalUint32(b)
- fs.Mtime, b = unmarshalUint32(b)
- }
- if flags&ssh_FILEXFER_ATTR_EXTENDED == ssh_FILEXFER_ATTR_EXTENDED {
- var count uint32
- count, b = unmarshalUint32(b)
- ext := make([]StatExtended, count, count)
- for i := uint32(0); i < count; i++ {
- var typ string
- var data string
- typ, b = unmarshalString(b)
- data, b = unmarshalString(b)
- ext[i] = StatExtended{typ, data}
- }
- fs.Extended = ext
- }
- return &fs, b
- }
- func marshalFileInfo(b []byte, fi os.FileInfo) []byte {
- // attributes variable struct, and also variable per protocol version
- // spec version 3 attributes:
- // uint32 flags
- // uint64 size present only if flag SSH_FILEXFER_ATTR_SIZE
- // uint32 uid present only if flag SSH_FILEXFER_ATTR_UIDGID
- // uint32 gid present only if flag SSH_FILEXFER_ATTR_UIDGID
- // uint32 permissions present only if flag SSH_FILEXFER_ATTR_PERMISSIONS
- // uint32 atime present only if flag SSH_FILEXFER_ACMODTIME
- // uint32 mtime present only if flag SSH_FILEXFER_ACMODTIME
- // uint32 extended_count present only if flag SSH_FILEXFER_ATTR_EXTENDED
- // string extended_type
- // string extended_data
- // ... more extended data (extended_type - extended_data pairs),
- // so that number of pairs equals extended_count
- flags, fileStat := fileStatFromInfo(fi)
- b = marshalUint32(b, flags)
- if flags&ssh_FILEXFER_ATTR_SIZE != 0 {
- b = marshalUint64(b, fileStat.Size)
- }
- if flags&ssh_FILEXFER_ATTR_UIDGID != 0 {
- b = marshalUint32(b, fileStat.UID)
- b = marshalUint32(b, fileStat.GID)
- }
- if flags&ssh_FILEXFER_ATTR_PERMISSIONS != 0 {
- b = marshalUint32(b, fileStat.Mode)
- }
- if flags&ssh_FILEXFER_ATTR_ACMODTIME != 0 {
- b = marshalUint32(b, fileStat.Atime)
- b = marshalUint32(b, fileStat.Mtime)
- }
- return b
- }
- // 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
- }
- // fromFileMode converts from the os.FileMode specification to sftp filemode bits
- func fromFileMode(mode os.FileMode) uint32 {
- ret := uint32(0)
- if mode&os.ModeDevice != 0 {
- if mode&os.ModeCharDevice != 0 {
- ret |= syscall.S_IFCHR
- } else {
- ret |= syscall.S_IFBLK
- }
- }
- if mode&os.ModeDir != 0 {
- ret |= syscall.S_IFDIR
- }
- if mode&os.ModeSymlink != 0 {
- ret |= syscall.S_IFLNK
- }
- if mode&os.ModeNamedPipe != 0 {
- ret |= syscall.S_IFIFO
- }
- if mode&os.ModeSetgid != 0 {
- ret |= syscall.S_ISGID
- }
- if mode&os.ModeSetuid != 0 {
- ret |= syscall.S_ISUID
- }
- if mode&os.ModeSticky != 0 {
- ret |= syscall.S_ISVTX
- }
- if mode&os.ModeSocket != 0 {
- ret |= syscall.S_IFSOCK
- }
- if mode&os.ModeType == 0 {
- ret |= syscall.S_IFREG
- }
- ret |= uint32(mode & os.ModePerm)
- return ret
- }
|