1package sftp 2 3// ssh_FXP_ATTRS support 4// see http://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#section-5 5 6import ( 7 "os" 8 "time" 9) 10 11const ( 12 sshFileXferAttrSize = 0x00000001 13 sshFileXferAttrUIDGID = 0x00000002 14 sshFileXferAttrPermissions = 0x00000004 15 sshFileXferAttrACmodTime = 0x00000008 16 sshFileXferAttrExtented = 0x80000000 17 18 sshFileXferAttrAll = sshFileXferAttrSize | sshFileXferAttrUIDGID | sshFileXferAttrPermissions | 19 sshFileXferAttrACmodTime | sshFileXferAttrExtented 20) 21 22// fileInfo is an artificial type designed to satisfy os.FileInfo. 23type fileInfo struct { 24 name string 25 size int64 26 mode os.FileMode 27 mtime time.Time 28 sys interface{} 29} 30 31// Name returns the base name of the file. 32func (fi *fileInfo) Name() string { return fi.name } 33 34// Size returns the length in bytes for regular files; system-dependent for others. 35func (fi *fileInfo) Size() int64 { return fi.size } 36 37// Mode returns file mode bits. 38func (fi *fileInfo) Mode() os.FileMode { return fi.mode } 39 40// ModTime returns the last modification time of the file. 41func (fi *fileInfo) ModTime() time.Time { return fi.mtime } 42 43// IsDir returns true if the file is a directory. 44func (fi *fileInfo) IsDir() bool { return fi.Mode().IsDir() } 45 46func (fi *fileInfo) Sys() interface{} { return fi.sys } 47 48// FileStat holds the original unmarshalled values from a call to READDIR or 49// *STAT. It is exported for the purposes of accessing the raw values via 50// os.FileInfo.Sys(). It is also used server side to store the unmarshalled 51// values for SetStat. 52type FileStat struct { 53 Size uint64 54 Mode uint32 55 Mtime uint32 56 Atime uint32 57 UID uint32 58 GID uint32 59 Extended []StatExtended 60} 61 62// StatExtended contains additional, extended information for a FileStat. 63type StatExtended struct { 64 ExtType string 65 ExtData string 66} 67 68func fileInfoFromStat(st *FileStat, name string) os.FileInfo { 69 fs := &fileInfo{ 70 name: name, 71 size: int64(st.Size), 72 mode: toFileMode(st.Mode), 73 mtime: time.Unix(int64(st.Mtime), 0), 74 sys: st, 75 } 76 return fs 77} 78 79func fileStatFromInfo(fi os.FileInfo) (uint32, FileStat) { 80 mtime := fi.ModTime().Unix() 81 atime := mtime 82 var flags uint32 = sshFileXferAttrSize | 83 sshFileXferAttrPermissions | 84 sshFileXferAttrACmodTime 85 86 fileStat := FileStat{ 87 Size: uint64(fi.Size()), 88 Mode: fromFileMode(fi.Mode()), 89 Mtime: uint32(mtime), 90 Atime: uint32(atime), 91 } 92 93 // os specific file stat decoding 94 fileStatFromInfoOs(fi, &flags, &fileStat) 95 96 return flags, fileStat 97} 98 99func unmarshalAttrs(b []byte) (*FileStat, []byte) { 100 flags, b := unmarshalUint32(b) 101 return getFileStat(flags, b) 102} 103 104func getFileStat(flags uint32, b []byte) (*FileStat, []byte) { 105 var fs FileStat 106 if flags&sshFileXferAttrSize == sshFileXferAttrSize { 107 fs.Size, b, _ = unmarshalUint64Safe(b) 108 } 109 if flags&sshFileXferAttrUIDGID == sshFileXferAttrUIDGID { 110 fs.UID, b, _ = unmarshalUint32Safe(b) 111 } 112 if flags&sshFileXferAttrUIDGID == sshFileXferAttrUIDGID { 113 fs.GID, b, _ = unmarshalUint32Safe(b) 114 } 115 if flags&sshFileXferAttrPermissions == sshFileXferAttrPermissions { 116 fs.Mode, b, _ = unmarshalUint32Safe(b) 117 } 118 if flags&sshFileXferAttrACmodTime == sshFileXferAttrACmodTime { 119 fs.Atime, b, _ = unmarshalUint32Safe(b) 120 fs.Mtime, b, _ = unmarshalUint32Safe(b) 121 } 122 if flags&sshFileXferAttrExtented == sshFileXferAttrExtented { 123 var count uint32 124 count, b, _ = unmarshalUint32Safe(b) 125 ext := make([]StatExtended, count) 126 for i := uint32(0); i < count; i++ { 127 var typ string 128 var data string 129 typ, b, _ = unmarshalStringSafe(b) 130 data, b, _ = unmarshalStringSafe(b) 131 ext[i] = StatExtended{typ, data} 132 } 133 fs.Extended = ext 134 } 135 return &fs, b 136} 137 138func marshalFileInfo(b []byte, fi os.FileInfo) []byte { 139 // attributes variable struct, and also variable per protocol version 140 // spec version 3 attributes: 141 // uint32 flags 142 // uint64 size present only if flag SSH_FILEXFER_ATTR_SIZE 143 // uint32 uid present only if flag SSH_FILEXFER_ATTR_UIDGID 144 // uint32 gid present only if flag SSH_FILEXFER_ATTR_UIDGID 145 // uint32 permissions present only if flag SSH_FILEXFER_ATTR_PERMISSIONS 146 // uint32 atime present only if flag SSH_FILEXFER_ACMODTIME 147 // uint32 mtime present only if flag SSH_FILEXFER_ACMODTIME 148 // uint32 extended_count present only if flag SSH_FILEXFER_ATTR_EXTENDED 149 // string extended_type 150 // string extended_data 151 // ... more extended data (extended_type - extended_data pairs), 152 // so that number of pairs equals extended_count 153 154 flags, fileStat := fileStatFromInfo(fi) 155 156 b = marshalUint32(b, flags) 157 if flags&sshFileXferAttrSize != 0 { 158 b = marshalUint64(b, fileStat.Size) 159 } 160 if flags&sshFileXferAttrUIDGID != 0 { 161 b = marshalUint32(b, fileStat.UID) 162 b = marshalUint32(b, fileStat.GID) 163 } 164 if flags&sshFileXferAttrPermissions != 0 { 165 b = marshalUint32(b, fileStat.Mode) 166 } 167 if flags&sshFileXferAttrACmodTime != 0 { 168 b = marshalUint32(b, fileStat.Atime) 169 b = marshalUint32(b, fileStat.Mtime) 170 } 171 172 return b 173} 174