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 } 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 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 } // 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 }