attrs.go 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. package sftp
  2. // ssh_FXP_ATTRS support
  3. // see http://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#section-5
  4. import (
  5. "os"
  6. "syscall"
  7. "time"
  8. )
  9. const (
  10. ssh_FILEXFER_ATTR_SIZE = 0x00000001
  11. ssh_FILEXFER_ATTR_UIDGID = 0x00000002
  12. ssh_FILEXFER_ATTR_PERMISSIONS = 0x00000004
  13. ssh_FILEXFER_ATTR_ACMODTIME = 0x00000008
  14. ssh_FILEXFER_ATTR_EXTENDED = 0x80000000
  15. )
  16. // fileInfo is an artificial type designed to satisfy os.FileInfo.
  17. type fileInfo struct {
  18. name string
  19. size int64
  20. mode os.FileMode
  21. mtime time.Time
  22. sys interface{}
  23. }
  24. // Name returns the base name of the file.
  25. func (fi *fileInfo) Name() string { return fi.name }
  26. // Size returns the length in bytes for regular files; system-dependent for others.
  27. func (fi *fileInfo) Size() int64 { return fi.size }
  28. // Mode returns file mode bits.
  29. func (fi *fileInfo) Mode() os.FileMode { return fi.mode }
  30. // ModTime returns the last modification time of the file.
  31. func (fi *fileInfo) ModTime() time.Time { return fi.mtime }
  32. // IsDir returns true if the file is a directory.
  33. func (fi *fileInfo) IsDir() bool { return fi.Mode().IsDir() }
  34. func (fi *fileInfo) Sys() interface{} { return fi.sys }
  35. // FileStat holds the original unmarshalled values from a call to READDIR or *STAT.
  36. // It is exported for the purposes of accessing the raw values via os.FileInfo.Sys()
  37. type FileStat struct {
  38. Size uint64
  39. Mode uint32
  40. Mtime uint32
  41. Atime uint32
  42. UID uint32
  43. GID uint32
  44. Extended []StatExtended
  45. }
  46. // StatExtended contains additional, extended information for a FileStat.
  47. type StatExtended struct {
  48. ExtType string
  49. ExtData string
  50. }
  51. func fileInfoFromStat(st *FileStat, name string) os.FileInfo {
  52. fs := &fileInfo{
  53. name: name,
  54. size: int64(st.Size),
  55. mode: toFileMode(st.Mode),
  56. mtime: time.Unix(int64(st.Mtime), 0),
  57. sys: st,
  58. }
  59. return fs
  60. }
  61. func fileStatFromInfo(fi os.FileInfo) (uint32, FileStat) {
  62. mtime := fi.ModTime().Unix()
  63. atime := mtime
  64. var flags uint32 = ssh_FILEXFER_ATTR_SIZE |
  65. ssh_FILEXFER_ATTR_PERMISSIONS |
  66. ssh_FILEXFER_ATTR_ACMODTIME
  67. fileStat := FileStat{
  68. Size: uint64(fi.Size()),
  69. Mode: fromFileMode(fi.Mode()),
  70. Mtime: uint32(mtime),
  71. Atime: uint32(atime),
  72. }
  73. // os specific file stat decoding
  74. fileStatFromInfoOs(fi, &flags, &fileStat)
  75. return flags, fileStat
  76. }
  77. func unmarshalAttrs(b []byte) (*FileStat, []byte) {
  78. flags, b := unmarshalUint32(b)
  79. var fs FileStat
  80. if flags&ssh_FILEXFER_ATTR_SIZE == ssh_FILEXFER_ATTR_SIZE {
  81. fs.Size, b = unmarshalUint64(b)
  82. }
  83. if flags&ssh_FILEXFER_ATTR_UIDGID == ssh_FILEXFER_ATTR_UIDGID {
  84. fs.UID, b = unmarshalUint32(b)
  85. }
  86. if flags&ssh_FILEXFER_ATTR_UIDGID == ssh_FILEXFER_ATTR_UIDGID {
  87. fs.GID, b = unmarshalUint32(b)
  88. }
  89. if flags&ssh_FILEXFER_ATTR_PERMISSIONS == ssh_FILEXFER_ATTR_PERMISSIONS {
  90. fs.Mode, b = unmarshalUint32(b)
  91. }
  92. if flags&ssh_FILEXFER_ATTR_ACMODTIME == ssh_FILEXFER_ATTR_ACMODTIME {
  93. fs.Atime, b = unmarshalUint32(b)
  94. fs.Mtime, b = unmarshalUint32(b)
  95. }
  96. if flags&ssh_FILEXFER_ATTR_EXTENDED == ssh_FILEXFER_ATTR_EXTENDED {
  97. var count uint32
  98. count, b = unmarshalUint32(b)
  99. ext := make([]StatExtended, count, count)
  100. for i := uint32(0); i < count; i++ {
  101. var typ string
  102. var data string
  103. typ, b = unmarshalString(b)
  104. data, b = unmarshalString(b)
  105. ext[i] = StatExtended{typ, data}
  106. }
  107. fs.Extended = ext
  108. }
  109. return &fs, b
  110. }
  111. func marshalFileInfo(b []byte, fi os.FileInfo) []byte {
  112. // attributes variable struct, and also variable per protocol version
  113. // spec version 3 attributes:
  114. // uint32 flags
  115. // uint64 size present only if flag SSH_FILEXFER_ATTR_SIZE
  116. // uint32 uid present only if flag SSH_FILEXFER_ATTR_UIDGID
  117. // uint32 gid present only if flag SSH_FILEXFER_ATTR_UIDGID
  118. // uint32 permissions present only if flag SSH_FILEXFER_ATTR_PERMISSIONS
  119. // uint32 atime present only if flag SSH_FILEXFER_ACMODTIME
  120. // uint32 mtime present only if flag SSH_FILEXFER_ACMODTIME
  121. // uint32 extended_count present only if flag SSH_FILEXFER_ATTR_EXTENDED
  122. // string extended_type
  123. // string extended_data
  124. // ... more extended data (extended_type - extended_data pairs),
  125. // so that number of pairs equals extended_count
  126. flags, fileStat := fileStatFromInfo(fi)
  127. b = marshalUint32(b, flags)
  128. if flags&ssh_FILEXFER_ATTR_SIZE != 0 {
  129. b = marshalUint64(b, fileStat.Size)
  130. }
  131. if flags&ssh_FILEXFER_ATTR_UIDGID != 0 {
  132. b = marshalUint32(b, fileStat.UID)
  133. b = marshalUint32(b, fileStat.GID)
  134. }
  135. if flags&ssh_FILEXFER_ATTR_PERMISSIONS != 0 {
  136. b = marshalUint32(b, fileStat.Mode)
  137. }
  138. if flags&ssh_FILEXFER_ATTR_ACMODTIME != 0 {
  139. b = marshalUint32(b, fileStat.Atime)
  140. b = marshalUint32(b, fileStat.Mtime)
  141. }
  142. return b
  143. }
  144. // toFileMode converts sftp filemode bits to the os.FileMode specification
  145. func toFileMode(mode uint32) os.FileMode {
  146. var fm = os.FileMode(mode & 0777)
  147. switch mode & syscall.S_IFMT {
  148. case syscall.S_IFBLK:
  149. fm |= os.ModeDevice
  150. case syscall.S_IFCHR:
  151. fm |= os.ModeDevice | os.ModeCharDevice
  152. case syscall.S_IFDIR:
  153. fm |= os.ModeDir
  154. case syscall.S_IFIFO:
  155. fm |= os.ModeNamedPipe
  156. case syscall.S_IFLNK:
  157. fm |= os.ModeSymlink
  158. case syscall.S_IFREG:
  159. // nothing to do
  160. case syscall.S_IFSOCK:
  161. fm |= os.ModeSocket
  162. }
  163. if mode&syscall.S_ISGID != 0 {
  164. fm |= os.ModeSetgid
  165. }
  166. if mode&syscall.S_ISUID != 0 {
  167. fm |= os.ModeSetuid
  168. }
  169. if mode&syscall.S_ISVTX != 0 {
  170. fm |= os.ModeSticky
  171. }
  172. return fm
  173. }
  174. // fromFileMode converts from the os.FileMode specification to sftp filemode bits
  175. func fromFileMode(mode os.FileMode) uint32 {
  176. ret := uint32(0)
  177. if mode&os.ModeDevice != 0 {
  178. if mode&os.ModeCharDevice != 0 {
  179. ret |= syscall.S_IFCHR
  180. } else {
  181. ret |= syscall.S_IFBLK
  182. }
  183. }
  184. if mode&os.ModeDir != 0 {
  185. ret |= syscall.S_IFDIR
  186. }
  187. if mode&os.ModeSymlink != 0 {
  188. ret |= syscall.S_IFLNK
  189. }
  190. if mode&os.ModeNamedPipe != 0 {
  191. ret |= syscall.S_IFIFO
  192. }
  193. if mode&os.ModeSetgid != 0 {
  194. ret |= syscall.S_ISGID
  195. }
  196. if mode&os.ModeSetuid != 0 {
  197. ret |= syscall.S_ISUID
  198. }
  199. if mode&os.ModeSticky != 0 {
  200. ret |= syscall.S_ISVTX
  201. }
  202. if mode&os.ModeSocket != 0 {
  203. ret |= syscall.S_IFSOCK
  204. }
  205. if mode&os.ModeType == 0 {
  206. ret |= syscall.S_IFREG
  207. }
  208. ret |= uint32(mode & os.ModePerm)
  209. return ret
  210. }