sftp.go 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. // Package sftp implements the SSH File Transfer Protocol as described in
  2. // https://filezilla-project.org/specs/draft-ietf-secsh-filexfer-02.txt
  3. package sftp
  4. import (
  5. "fmt"
  6. )
  7. const (
  8. sshFxpInit = 1
  9. sshFxpVersion = 2
  10. sshFxpOpen = 3
  11. sshFxpClose = 4
  12. sshFxpRead = 5
  13. sshFxpWrite = 6
  14. sshFxpLstat = 7
  15. sshFxpFstat = 8
  16. sshFxpSetstat = 9
  17. sshFxpFsetstat = 10
  18. sshFxpOpendir = 11
  19. sshFxpReaddir = 12
  20. sshFxpRemove = 13
  21. sshFxpMkdir = 14
  22. sshFxpRmdir = 15
  23. sshFxpRealpath = 16
  24. sshFxpStat = 17
  25. sshFxpRename = 18
  26. sshFxpReadlink = 19
  27. sshFxpSymlink = 20
  28. sshFxpStatus = 101
  29. sshFxpHandle = 102
  30. sshFxpData = 103
  31. sshFxpName = 104
  32. sshFxpAttrs = 105
  33. sshFxpExtended = 200
  34. sshFxpExtendedReply = 201
  35. )
  36. const (
  37. sshFxOk = 0
  38. sshFxEOF = 1
  39. sshFxNoSuchFile = 2
  40. sshFxPermissionDenied = 3
  41. sshFxFailure = 4
  42. sshFxBadMessage = 5
  43. sshFxNoConnection = 6
  44. sshFxConnectionLost = 7
  45. sshFxOPUnsupported = 8
  46. // see draft-ietf-secsh-filexfer-13
  47. // https://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-9.1
  48. sshFxInvalidHandle = 9
  49. sshFxNoSuchPath = 10
  50. sshFxFileAlreadyExists = 11
  51. sshFxWriteProtect = 12
  52. sshFxNoMedia = 13
  53. sshFxNoSpaceOnFilesystem = 14
  54. sshFxQuotaExceeded = 15
  55. sshFxUnknownPrincipal = 16
  56. sshFxLockConflict = 17
  57. sshFxDirNotEmpty = 18
  58. sshFxNotADirectory = 19
  59. sshFxInvalidFilename = 20
  60. sshFxLinkLoop = 21
  61. sshFxCannotDelete = 22
  62. sshFxInvalidParameter = 23
  63. sshFxFileIsADirectory = 24
  64. sshFxByteRangeLockConflict = 25
  65. sshFxByteRangeLockRefused = 26
  66. sshFxDeletePending = 27
  67. sshFxFileCorrupt = 28
  68. sshFxOwnerInvalid = 29
  69. sshFxGroupInvalid = 30
  70. sshFxNoMatchingByteRangeLock = 31
  71. )
  72. const (
  73. sshFxfRead = 0x00000001
  74. sshFxfWrite = 0x00000002
  75. sshFxfAppend = 0x00000004
  76. sshFxfCreat = 0x00000008
  77. sshFxfTrunc = 0x00000010
  78. sshFxfExcl = 0x00000020
  79. )
  80. var (
  81. // supportedSFTPExtensions defines the supported extensions
  82. supportedSFTPExtensions = []sshExtensionPair{
  83. {"hardlink@openssh.com", "1"},
  84. {"posix-rename@openssh.com", "1"},
  85. {"statvfs@openssh.com", "2"},
  86. }
  87. sftpExtensions = supportedSFTPExtensions
  88. )
  89. type fxp uint8
  90. func (f fxp) String() string {
  91. switch f {
  92. case sshFxpInit:
  93. return "SSH_FXP_INIT"
  94. case sshFxpVersion:
  95. return "SSH_FXP_VERSION"
  96. case sshFxpOpen:
  97. return "SSH_FXP_OPEN"
  98. case sshFxpClose:
  99. return "SSH_FXP_CLOSE"
  100. case sshFxpRead:
  101. return "SSH_FXP_READ"
  102. case sshFxpWrite:
  103. return "SSH_FXP_WRITE"
  104. case sshFxpLstat:
  105. return "SSH_FXP_LSTAT"
  106. case sshFxpFstat:
  107. return "SSH_FXP_FSTAT"
  108. case sshFxpSetstat:
  109. return "SSH_FXP_SETSTAT"
  110. case sshFxpFsetstat:
  111. return "SSH_FXP_FSETSTAT"
  112. case sshFxpOpendir:
  113. return "SSH_FXP_OPENDIR"
  114. case sshFxpReaddir:
  115. return "SSH_FXP_READDIR"
  116. case sshFxpRemove:
  117. return "SSH_FXP_REMOVE"
  118. case sshFxpMkdir:
  119. return "SSH_FXP_MKDIR"
  120. case sshFxpRmdir:
  121. return "SSH_FXP_RMDIR"
  122. case sshFxpRealpath:
  123. return "SSH_FXP_REALPATH"
  124. case sshFxpStat:
  125. return "SSH_FXP_STAT"
  126. case sshFxpRename:
  127. return "SSH_FXP_RENAME"
  128. case sshFxpReadlink:
  129. return "SSH_FXP_READLINK"
  130. case sshFxpSymlink:
  131. return "SSH_FXP_SYMLINK"
  132. case sshFxpStatus:
  133. return "SSH_FXP_STATUS"
  134. case sshFxpHandle:
  135. return "SSH_FXP_HANDLE"
  136. case sshFxpData:
  137. return "SSH_FXP_DATA"
  138. case sshFxpName:
  139. return "SSH_FXP_NAME"
  140. case sshFxpAttrs:
  141. return "SSH_FXP_ATTRS"
  142. case sshFxpExtended:
  143. return "SSH_FXP_EXTENDED"
  144. case sshFxpExtendedReply:
  145. return "SSH_FXP_EXTENDED_REPLY"
  146. default:
  147. return "unknown"
  148. }
  149. }
  150. type fx uint8
  151. func (f fx) String() string {
  152. switch f {
  153. case sshFxOk:
  154. return "SSH_FX_OK"
  155. case sshFxEOF:
  156. return "SSH_FX_EOF"
  157. case sshFxNoSuchFile:
  158. return "SSH_FX_NO_SUCH_FILE"
  159. case sshFxPermissionDenied:
  160. return "SSH_FX_PERMISSION_DENIED"
  161. case sshFxFailure:
  162. return "SSH_FX_FAILURE"
  163. case sshFxBadMessage:
  164. return "SSH_FX_BAD_MESSAGE"
  165. case sshFxNoConnection:
  166. return "SSH_FX_NO_CONNECTION"
  167. case sshFxConnectionLost:
  168. return "SSH_FX_CONNECTION_LOST"
  169. case sshFxOPUnsupported:
  170. return "SSH_FX_OP_UNSUPPORTED"
  171. default:
  172. return "unknown"
  173. }
  174. }
  175. type unexpectedPacketErr struct {
  176. want, got uint8
  177. }
  178. func (u *unexpectedPacketErr) Error() string {
  179. return fmt.Sprintf("sftp: unexpected packet: want %v, got %v", fxp(u.want), fxp(u.got))
  180. }
  181. func unimplementedPacketErr(u uint8) error {
  182. return fmt.Errorf("sftp: unimplemented packet type: got %v", fxp(u))
  183. }
  184. type unexpectedIDErr struct{ want, got uint32 }
  185. func (u *unexpectedIDErr) Error() string {
  186. return fmt.Sprintf("sftp: unexpected id: want %d, got %d", u.want, u.got)
  187. }
  188. func unimplementedSeekWhence(whence int) error {
  189. return fmt.Errorf("sftp: unimplemented seek whence %d", whence)
  190. }
  191. func unexpectedCount(want, got uint32) error {
  192. return fmt.Errorf("sftp: unexpected count: want %d, got %d", want, got)
  193. }
  194. type unexpectedVersionErr struct{ want, got uint32 }
  195. func (u *unexpectedVersionErr) Error() string {
  196. return fmt.Sprintf("sftp: unexpected server version: want %v, got %v", u.want, u.got)
  197. }
  198. // A StatusError is returned when an SFTP operation fails, and provides
  199. // additional information about the failure.
  200. type StatusError struct {
  201. Code uint32
  202. msg, lang string
  203. }
  204. func (s *StatusError) Error() string {
  205. return fmt.Sprintf("sftp: %q (%v)", s.msg, fx(s.Code))
  206. }
  207. // FxCode returns the error code typed to match against the exported codes
  208. func (s *StatusError) FxCode() fxerr {
  209. return fxerr(s.Code)
  210. }
  211. func getSupportedExtensionByName(extensionName string) (sshExtensionPair, error) {
  212. for _, supportedExtension := range supportedSFTPExtensions {
  213. if supportedExtension.Name == extensionName {
  214. return supportedExtension, nil
  215. }
  216. }
  217. return sshExtensionPair{}, fmt.Errorf("unsupported extension: %s", extensionName)
  218. }
  219. // SetSFTPExtensions allows to customize the supported server extensions.
  220. // See the variable supportedSFTPExtensions for supported extensions.
  221. // This method accepts a slice of sshExtensionPair names for example 'hardlink@openssh.com'.
  222. // If an invalid extension is given an error will be returned and nothing will be changed
  223. func SetSFTPExtensions(extensions ...string) error {
  224. tempExtensions := []sshExtensionPair{}
  225. for _, extension := range extensions {
  226. sftpExtension, err := getSupportedExtensionByName(extension)
  227. if err != nil {
  228. return err
  229. }
  230. tempExtensions = append(tempExtensions, sftpExtension)
  231. }
  232. sftpExtensions = tempExtensions
  233. return nil
  234. }