scp.go 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767
  1. package sftpd
  2. import (
  3. "fmt"
  4. "io"
  5. "math"
  6. "os"
  7. "path"
  8. "path/filepath"
  9. "strconv"
  10. "strings"
  11. "time"
  12. "github.com/drakkan/sftpgo/dataprovider"
  13. "github.com/drakkan/sftpgo/logger"
  14. "github.com/drakkan/sftpgo/utils"
  15. "golang.org/x/crypto/ssh"
  16. )
  17. var (
  18. okMsg = []byte{0x00}
  19. warnMsg = []byte{0x01} // must be followed by an optional message and a newline
  20. errMsg = []byte{0x02} // must be followed by an optional message and a newline
  21. newLine = []byte{0x0A}
  22. )
  23. type execMsg struct {
  24. Command string
  25. }
  26. type exitStatusMsg struct {
  27. Status uint32
  28. }
  29. type scpCommand struct {
  30. connection Connection
  31. args []string
  32. channel ssh.Channel
  33. }
  34. func (c *scpCommand) handle() error {
  35. var err error
  36. addConnection(c.connection.ID, c.connection)
  37. defer removeConnection(c.connection.ID)
  38. destPath := c.getDestPath()
  39. commandType := c.getCommandType()
  40. logger.Debug(logSenderSCP, c.connection.ID, "handle scp command, args: %v user: %v command type: %v, dest path: %#v",
  41. c.args, c.connection.User.Username, commandType, destPath)
  42. if commandType == "-t" {
  43. // -t means "to", so upload
  44. err = c.handleRecursiveUpload()
  45. if err != nil {
  46. return err
  47. }
  48. } else if commandType == "-f" {
  49. // -f means "from" so download
  50. err = c.readConfirmationMessage()
  51. if err != nil {
  52. return err
  53. }
  54. err = c.handleDownload(destPath)
  55. if err != nil {
  56. return err
  57. }
  58. } else {
  59. err = fmt.Errorf("scp command not supported, args: %v", c.args)
  60. }
  61. c.sendExitStatus(err)
  62. return err
  63. }
  64. func (c *scpCommand) handleRecursiveUpload() error {
  65. var err error
  66. numDirs := 0
  67. destPath := c.getDestPath()
  68. for {
  69. err = c.sendConfirmationMessage()
  70. if err != nil {
  71. return err
  72. }
  73. command, err := c.getNextUploadProtocolMessage()
  74. if err != nil {
  75. return err
  76. }
  77. if strings.HasPrefix(command, "E") {
  78. numDirs--
  79. logger.Debug(logSenderSCP, c.connection.ID, "received end dir command, num dirs: %v", numDirs)
  80. if numDirs == 0 {
  81. // upload is now complete send confirmation message
  82. err = c.sendConfirmationMessage()
  83. if err != nil {
  84. return err
  85. }
  86. } else {
  87. // the destination dir is now the parent directory
  88. destPath = filepath.Join(destPath, "..")
  89. }
  90. } else {
  91. sizeToRead, name, err := c.parseUploadMessage(command)
  92. if err != nil {
  93. return err
  94. }
  95. if strings.HasPrefix(command, "D") {
  96. numDirs++
  97. destPath = path.Join(destPath, name)
  98. err = c.handleCreateDir(destPath)
  99. if err != nil {
  100. return err
  101. }
  102. logger.Debug(logSenderSCP, c.connection.ID, "received start dir command, num dirs: %v destPath: %v", numDirs, destPath)
  103. } else if strings.HasPrefix(command, "C") {
  104. err = c.handleUpload(c.getFileUploadDestPath(destPath, name), sizeToRead)
  105. if err != nil {
  106. return err
  107. }
  108. }
  109. }
  110. if err != nil || numDirs == 0 {
  111. break
  112. }
  113. }
  114. return err
  115. }
  116. func (c *scpCommand) handleCreateDir(dirPath string) error {
  117. updateConnectionActivity(c.connection.ID)
  118. if !c.connection.User.HasPerm(dataprovider.PermCreateDirs) {
  119. err := fmt.Errorf("Permission denied")
  120. logger.Warn(logSenderSCP, c.connection.ID, "error creating dir: %v, permission denied", dirPath)
  121. c.sendErrorMessage(err.Error())
  122. return err
  123. }
  124. p, err := c.connection.buildPath(dirPath)
  125. if err != nil {
  126. logger.Warn(logSenderSCP, c.connection.ID, "error creating dir: %v, invalid file path, err: %v", dirPath, err)
  127. c.sendErrorMessage(err.Error())
  128. return err
  129. }
  130. err = c.createDir(p)
  131. if err != nil {
  132. return err
  133. }
  134. logger.CommandLog(mkdirLogSender, dirPath, "", c.connection.User.Username, c.connection.ID, c.connection.protocol)
  135. return nil
  136. }
  137. // we need to close the transfer if we have an error
  138. func (c *scpCommand) getUploadFileData(sizeToRead int64, transfer *Transfer) error {
  139. err := c.sendConfirmationMessage()
  140. if err != nil {
  141. transfer.Close()
  142. return err
  143. }
  144. if sizeToRead > 0 {
  145. remaining := sizeToRead
  146. buf := make([]byte, int64(math.Min(32768, float64(sizeToRead))))
  147. for {
  148. n, err := c.channel.Read(buf)
  149. if err != nil {
  150. c.sendErrorMessage(err.Error())
  151. transfer.Close()
  152. return err
  153. }
  154. transfer.WriteAt(buf[:n], sizeToRead-remaining)
  155. remaining -= int64(n)
  156. if remaining <= 0 {
  157. break
  158. }
  159. if remaining < int64(len(buf)) {
  160. buf = make([]byte, remaining)
  161. }
  162. }
  163. }
  164. err = c.readConfirmationMessage()
  165. if err != nil {
  166. transfer.Close()
  167. return err
  168. }
  169. err = transfer.Close()
  170. if err != nil {
  171. c.sendErrorMessage(err.Error())
  172. return err
  173. }
  174. return c.sendConfirmationMessage()
  175. }
  176. func (c *scpCommand) handleUploadFile(requestPath, filePath string, sizeToRead int64, isNewFile bool) error {
  177. if !c.connection.hasSpace(true) {
  178. err := fmt.Errorf("denying file write due to space limit")
  179. logger.Warn(logSenderSCP, c.connection.ID, "error uploading file: %v, err: %v", filePath, err)
  180. c.sendErrorMessage(err.Error())
  181. return err
  182. }
  183. if _, err := os.Stat(filepath.Dir(requestPath)); os.IsNotExist(err) {
  184. if !c.connection.User.HasPerm(dataprovider.PermCreateDirs) {
  185. err := fmt.Errorf("Permission denied")
  186. logger.Warn(logSenderSCP, c.connection.ID, "error uploading file: %v, permission denied", requestPath)
  187. c.sendErrorMessage(err.Error())
  188. return err
  189. }
  190. }
  191. file, err := os.Create(filePath)
  192. if err != nil {
  193. logger.Error(logSenderSCP, "error creating file %v: %v", requestPath, err)
  194. c.sendErrorMessage(err.Error())
  195. return err
  196. }
  197. utils.SetPathPermissions(filePath, c.connection.User.GetUID(), c.connection.User.GetGID())
  198. transfer := Transfer{
  199. file: file,
  200. path: requestPath,
  201. start: time.Now(),
  202. bytesSent: 0,
  203. bytesReceived: 0,
  204. user: c.connection.User,
  205. connectionID: c.connection.ID,
  206. transferType: transferUpload,
  207. lastActivity: time.Now(),
  208. isNewFile: isNewFile,
  209. protocol: c.connection.protocol,
  210. }
  211. addTransfer(&transfer)
  212. return c.getUploadFileData(sizeToRead, &transfer)
  213. }
  214. func (c *scpCommand) handleUpload(uploadFilePath string, sizeToRead int64) error {
  215. var err error
  216. updateConnectionActivity(c.connection.ID)
  217. if !c.connection.User.HasPerm(dataprovider.PermUpload) {
  218. err := fmt.Errorf("Permission denied")
  219. logger.Warn(logSenderSCP, c.connection.ID, "error uploading file: %v, permission denied", uploadFilePath)
  220. c.sendErrorMessage(err.Error())
  221. return err
  222. }
  223. p, err := c.connection.buildPath(uploadFilePath)
  224. if err != nil {
  225. logger.Warn(logSenderSCP, c.connection.ID, "error uploading file: %v, err: %v", uploadFilePath, err)
  226. c.sendErrorMessage(err.Error())
  227. return err
  228. }
  229. filePath := p
  230. if uploadMode == uploadModeAtomic {
  231. filePath = getUploadTempFilePath(p)
  232. }
  233. stat, statErr := os.Stat(p)
  234. if os.IsNotExist(statErr) {
  235. return c.handleUploadFile(p, filePath, sizeToRead, true)
  236. }
  237. if statErr != nil {
  238. logger.Error(logSenderSCP, "error performing file stat %v: %v", p, statErr)
  239. c.sendErrorMessage(err.Error())
  240. return err
  241. }
  242. if stat.IsDir() {
  243. logger.Warn(logSenderSCP, c.connection.ID, "attempted to open a directory for writing to: %v", p)
  244. err = fmt.Errorf("Attempted to open a directory for writing: %v", p)
  245. c.sendErrorMessage(err.Error())
  246. return err
  247. }
  248. if uploadMode == uploadModeAtomic {
  249. err = os.Rename(p, filePath)
  250. if err != nil {
  251. logger.Error(logSenderSCP, "error renaming existing file for atomic upload, source: %v, dest: %v, err: %v",
  252. p, filePath, err)
  253. c.sendErrorMessage(err.Error())
  254. return err
  255. }
  256. }
  257. dataprovider.UpdateUserQuota(dataProvider, c.connection.User, 0, -stat.Size(), false)
  258. return c.handleUploadFile(p, filePath, sizeToRead, false)
  259. }
  260. func (c *scpCommand) sendDownloadProtocolMessages(dirPath string, stat os.FileInfo) error {
  261. var err error
  262. if c.sendFileTime() {
  263. modTime := stat.ModTime().UnixNano() / 1000000000
  264. tCommand := fmt.Sprintf("T%v 0 %v 0\n", modTime, modTime)
  265. err = c.sendProtocolMessage(tCommand)
  266. if err != nil {
  267. return err
  268. }
  269. err = c.readConfirmationMessage()
  270. if err != nil {
  271. return err
  272. }
  273. }
  274. fileMode := fmt.Sprintf("D%v 0 %v\n", getFileModeAsString(stat.Mode(), stat.IsDir()), filepath.Base(dirPath))
  275. err = c.sendProtocolMessage(fileMode)
  276. if err != nil {
  277. return err
  278. }
  279. err = c.readConfirmationMessage()
  280. return err
  281. }
  282. // we send first all the files in the roor directory and then the directories
  283. // for each directory we recursively call this method again
  284. func (c *scpCommand) handleRecursiveDownload(dirPath string, stat os.FileInfo) error {
  285. var err error
  286. if c.isRecursive() {
  287. logger.Debug(logSenderSCP, c.connection.ID, "recursive download, dir path: %v", dirPath)
  288. err = c.sendDownloadProtocolMessages(dirPath, stat)
  289. if err != nil {
  290. return err
  291. }
  292. files, err := getDirContents(dirPath)
  293. if err != nil {
  294. c.sendErrorMessage(err.Error())
  295. return err
  296. }
  297. var dirs []string
  298. for _, file := range files {
  299. filePath := c.connection.User.GetRelativePath(filepath.Join(dirPath, file.Name()))
  300. if file.Mode().IsRegular() || file.Mode()&os.ModeSymlink == os.ModeSymlink {
  301. err = c.handleDownload(filePath)
  302. if err != nil {
  303. break
  304. }
  305. } else if file.IsDir() {
  306. dirs = append(dirs, filePath)
  307. }
  308. }
  309. if err != nil {
  310. c.sendErrorMessage(err.Error())
  311. return err
  312. }
  313. for _, dir := range dirs {
  314. err = c.handleDownload(dir)
  315. if err != nil {
  316. break
  317. }
  318. }
  319. if err != nil {
  320. c.sendErrorMessage(err.Error())
  321. return err
  322. }
  323. err = c.sendProtocolMessage("E\n")
  324. if err != nil {
  325. return err
  326. }
  327. err = c.readConfirmationMessage()
  328. if err != nil {
  329. return err
  330. }
  331. return err
  332. }
  333. err = fmt.Errorf("Unable to send directory for non recursive copy")
  334. c.sendErrorMessage(err.Error())
  335. return err
  336. }
  337. func (c *scpCommand) sendDownloadFileData(filePath string, stat os.FileInfo, transfer *Transfer) error {
  338. var err error
  339. if c.sendFileTime() {
  340. modTime := stat.ModTime().UnixNano() / 1000000000
  341. tCommand := fmt.Sprintf("T%v 0 %v 0\n", modTime, modTime)
  342. err = c.sendProtocolMessage(tCommand)
  343. if err != nil {
  344. return err
  345. }
  346. err = c.readConfirmationMessage()
  347. if err != nil {
  348. return err
  349. }
  350. }
  351. fileSize := stat.Size()
  352. readed := int64(0)
  353. fileMode := fmt.Sprintf("C%v %v %v\n", getFileModeAsString(stat.Mode(), stat.IsDir()), fileSize, filepath.Base(filePath))
  354. err = c.sendProtocolMessage(fileMode)
  355. if err != nil {
  356. return err
  357. }
  358. err = c.readConfirmationMessage()
  359. if err != nil {
  360. return err
  361. }
  362. buf := make([]byte, 32768)
  363. for {
  364. n, err := transfer.ReadAt(buf, readed)
  365. if err == nil || err == io.EOF {
  366. if n > 0 {
  367. _, err = c.channel.Write(buf[:n])
  368. }
  369. }
  370. readed += int64(n)
  371. if err != nil {
  372. break
  373. }
  374. }
  375. if err != nil && err != io.EOF {
  376. c.sendErrorMessage(err.Error())
  377. return err
  378. }
  379. err = c.sendConfirmationMessage()
  380. if err != nil {
  381. return err
  382. }
  383. err = c.readConfirmationMessage()
  384. return err
  385. }
  386. func (c *scpCommand) handleDownload(filePath string) error {
  387. var err error
  388. updateConnectionActivity(c.connection.ID)
  389. if !c.connection.User.HasPerm(dataprovider.PermDownload) {
  390. err := fmt.Errorf("Permission denied")
  391. logger.Warn(logSenderSCP, c.connection.ID, "error downloading file: %v, permission denied", filePath)
  392. c.sendErrorMessage(err.Error())
  393. return err
  394. }
  395. p, err := c.connection.buildPath(filePath)
  396. if err != nil {
  397. err := fmt.Errorf("Invalid file path")
  398. logger.Warn(logSenderSCP, c.connection.ID, "error downloading file: %v, invalid file path", filePath)
  399. c.sendErrorMessage(err.Error())
  400. return err
  401. }
  402. var stat os.FileInfo
  403. if stat, err = os.Stat(p); os.IsNotExist(err) {
  404. logger.Warn(logSenderSCP, c.connection.ID, "error downloading file: %v, err: %v", p, err)
  405. c.sendErrorMessage(err.Error())
  406. return err
  407. }
  408. if stat.IsDir() {
  409. err = c.handleRecursiveDownload(p, stat)
  410. return err
  411. }
  412. file, err := os.Open(p)
  413. if err != nil {
  414. logger.Error(logSenderSCP, "could not open file \"%v\" for reading: %v", p, err)
  415. c.sendErrorMessage(err.Error())
  416. return err
  417. }
  418. transfer := Transfer{
  419. file: file,
  420. path: p,
  421. start: time.Now(),
  422. bytesSent: 0,
  423. bytesReceived: 0,
  424. user: c.connection.User,
  425. connectionID: c.connection.ID,
  426. transferType: transferDownload,
  427. lastActivity: time.Now(),
  428. isNewFile: false,
  429. protocol: c.connection.protocol,
  430. }
  431. addTransfer(&transfer)
  432. err = c.sendDownloadFileData(p, stat, &transfer)
  433. // we need to call Close anyway and return close error if any and
  434. // if we have no previous error
  435. if err == nil {
  436. err = transfer.Close()
  437. } else {
  438. transfer.Close()
  439. }
  440. return err
  441. }
  442. // returns the SCP destination path.
  443. // We ensure that the path is absolute and in SFTP (UNIX) format
  444. func (c *scpCommand) getDestPath() string {
  445. destPath := filepath.ToSlash(c.args[len(c.args)-1])
  446. if !filepath.IsAbs(destPath) {
  447. destPath = "/" + destPath
  448. }
  449. return destPath
  450. }
  451. func (c *scpCommand) getCommandType() string {
  452. return c.args[len(c.args)-2]
  453. }
  454. func (c *scpCommand) sendFileTime() bool {
  455. return utils.IsStringInSlice("-p", c.args)
  456. }
  457. func (c *scpCommand) isRecursive() bool {
  458. return utils.IsStringInSlice("-r", c.args)
  459. }
  460. // read the SCP confirmation message and the optional text message
  461. // the channel will be closed on errors
  462. func (c *scpCommand) readConfirmationMessage() error {
  463. var msg strings.Builder
  464. buf := make([]byte, 1)
  465. n, err := c.channel.Read(buf)
  466. if err != nil {
  467. c.channel.Close()
  468. return err
  469. }
  470. if n == 1 && (buf[0] == warnMsg[0] || buf[0] == errMsg[0]) {
  471. isError := buf[0] == errMsg[0]
  472. for {
  473. n, err = c.channel.Read(buf)
  474. readed := buf[:n]
  475. if err != nil || (n == 1 && readed[0] == newLine[0]) {
  476. break
  477. }
  478. if n > 0 {
  479. msg.WriteString(string(readed))
  480. }
  481. }
  482. logger.Info(logSenderSCP, "scp error message received: %v is error: %v", msg.String(), isError)
  483. err = fmt.Errorf("%v", msg.String())
  484. c.channel.Close()
  485. }
  486. return err
  487. }
  488. // protool messages are newline terminated
  489. func (c *scpCommand) readProtocolMessage() (string, error) {
  490. var command strings.Builder
  491. var err error
  492. buf := make([]byte, 1)
  493. for {
  494. var n int
  495. n, err = c.channel.Read(buf)
  496. if err != nil {
  497. break
  498. }
  499. if n > 0 {
  500. readed := buf[:n]
  501. if n == 1 && readed[0] == newLine[0] {
  502. break
  503. }
  504. command.WriteString(string(readed))
  505. }
  506. }
  507. if err != nil {
  508. c.channel.Close()
  509. }
  510. return command.String(), err
  511. }
  512. // send an error message and close the channel
  513. func (c *scpCommand) sendErrorMessage(error string) {
  514. c.channel.Write(errMsg)
  515. c.channel.Write([]byte(error))
  516. c.channel.Write(newLine)
  517. c.channel.Close()
  518. }
  519. // send scp confirmation message and close the channel if an error happen
  520. func (c *scpCommand) sendConfirmationMessage() error {
  521. _, err := c.channel.Write(okMsg)
  522. if err != nil {
  523. c.channel.Close()
  524. }
  525. return err
  526. }
  527. // sends a protocol message and close the channel on error
  528. func (c *scpCommand) sendProtocolMessage(message string) error {
  529. _, err := c.channel.Write([]byte(message))
  530. if err != nil {
  531. logger.Warn(logSenderSCP, c.connection.ID, "error sending protocol message: %v, err: %v", message, err)
  532. c.channel.Close()
  533. }
  534. return err
  535. }
  536. // sends the SCP command exit status
  537. func (c *scpCommand) sendExitStatus(err error) {
  538. status := uint32(0)
  539. if err != nil {
  540. status = 1
  541. }
  542. ex := exitStatusMsg{
  543. Status: status,
  544. }
  545. logger.Debug(logSenderSCP, c.connection.ID, "send exit status for command with args: %v user: %v err: %v",
  546. c.args, c.connection.User.Username, err)
  547. c.channel.SendRequest("exit-status", false, ssh.Marshal(&ex))
  548. c.channel.Close()
  549. }
  550. // get the next upload protocol message ignoring T command if any
  551. // we use our own user setting for permissions
  552. func (c *scpCommand) getNextUploadProtocolMessage() (string, error) {
  553. var command string
  554. var err error
  555. for {
  556. command, err = c.readProtocolMessage()
  557. if err != nil {
  558. return command, err
  559. }
  560. if strings.HasPrefix(command, "T") {
  561. err = c.sendConfirmationMessage()
  562. if err != nil {
  563. return command, err
  564. }
  565. } else {
  566. break
  567. }
  568. }
  569. return command, err
  570. }
  571. func (c *scpCommand) createDir(dirPath string) error {
  572. var err error
  573. if err = os.Mkdir(dirPath, 0777); err != nil {
  574. logger.Error(logSenderSCP, "error creating dir: %v", dirPath)
  575. c.sendErrorMessage(err.Error())
  576. return err
  577. }
  578. utils.SetPathPermissions(dirPath, c.connection.User.GetUID(), c.connection.User.GetGID())
  579. return err
  580. }
  581. // parse protocol messages such as:
  582. // D0755 0 testdir
  583. // or:
  584. // C0644 6 testfile
  585. // and returns file size and file/directory name
  586. func (c *scpCommand) parseUploadMessage(command string) (int64, string, error) {
  587. var size int64
  588. var name string
  589. var err error
  590. if !strings.HasPrefix(command, "C") && !strings.HasPrefix(command, "D") {
  591. err = fmt.Errorf("unknown or invalid upload message: %v args: %v user: %v",
  592. command, c.args, c.connection.User.Username)
  593. logger.Warn(logSenderSCP, c.connection.ID, "error: %v", err)
  594. c.sendErrorMessage(err.Error())
  595. return size, name, err
  596. }
  597. parts := strings.Split(command, " ")
  598. if len(parts) == 3 {
  599. size, err = strconv.ParseInt(parts[1], 10, 64)
  600. if err != nil {
  601. logger.Warn(logSenderSCP, c.connection.ID, "error getting size from upload message: %v", err)
  602. c.sendErrorMessage(fmt.Sprintf("Error getting size: %v", err))
  603. return size, name, err
  604. }
  605. name = parts[2]
  606. if len(name) == 0 {
  607. err = fmt.Errorf("error getting name from upload message, cannot be empty")
  608. logger.Warn(logSenderSCP, c.connection.ID, "error: %v", err)
  609. c.sendErrorMessage(err.Error())
  610. return size, name, err
  611. }
  612. } else {
  613. err = fmt.Errorf("Error splitting upload message: %v", command)
  614. logger.Warn(logSenderSCP, c.connection.ID, "error: %v", err)
  615. c.sendErrorMessage(err.Error())
  616. return size, name, err
  617. }
  618. return size, name, err
  619. }
  620. func (c *scpCommand) getFileUploadDestPath(scpDestPath, fileName string) string {
  621. if !c.isRecursive() {
  622. // if the upload is not recursive and the destination path does not end with "/"
  623. // then scpDestPath is the wanted filename, for example:
  624. // scp fileName.txt [email protected]:/newFileName.txt
  625. // or
  626. // scp fileName.txt [email protected]:/fileName.txt
  627. if !strings.HasSuffix(scpDestPath, "/") {
  628. // but if scpDestPath is an existing directory then we put the uploaded file
  629. // inside that directory this is as scp command works, for example:
  630. // scp fileName.txt [email protected]:/existing_dir
  631. if p, err := c.connection.buildPath(scpDestPath); err == nil {
  632. if stat, err := os.Stat(p); err == nil {
  633. if stat.IsDir() {
  634. return path.Join(scpDestPath, fileName)
  635. }
  636. }
  637. }
  638. return scpDestPath
  639. }
  640. }
  641. // if the upload is recursive or scpDestPath has the "/" suffix then the destination
  642. // file is relative to scpDestPath
  643. return path.Join(scpDestPath, fileName)
  644. }
  645. func getFileModeAsString(fileMode os.FileMode, isDir bool) string {
  646. var defaultMode string
  647. if isDir {
  648. defaultMode = "0755"
  649. } else {
  650. defaultMode = "0644"
  651. }
  652. if fileMode == 0 {
  653. return defaultMode
  654. }
  655. modeString := []byte(fileMode.String())
  656. nullPerm := []byte("-")
  657. u := 0
  658. g := 0
  659. o := 0
  660. s := 0
  661. lastChar := len(modeString) - 1
  662. if fileMode&os.ModeSticky != 0 {
  663. s++
  664. }
  665. if fileMode&os.ModeSetuid != 0 {
  666. s += 2
  667. }
  668. if fileMode&os.ModeSetgid != 0 {
  669. s += 4
  670. }
  671. if modeString[lastChar-8] != nullPerm[0] {
  672. u += 4
  673. }
  674. if modeString[lastChar-7] != nullPerm[0] {
  675. u += 2
  676. }
  677. if modeString[lastChar-6] != nullPerm[0] {
  678. u++
  679. }
  680. if modeString[lastChar-5] != nullPerm[0] {
  681. g += 4
  682. }
  683. if modeString[lastChar-4] != nullPerm[0] {
  684. g += 2
  685. }
  686. if modeString[lastChar-3] != nullPerm[0] {
  687. g++
  688. }
  689. if modeString[lastChar-2] != nullPerm[0] {
  690. o += 4
  691. }
  692. if modeString[lastChar-1] != nullPerm[0] {
  693. o += 2
  694. }
  695. if modeString[lastChar] != nullPerm[0] {
  696. o++
  697. }
  698. return fmt.Sprintf("%v%v%v%v", s, u, g, o)
  699. }
  700. func getDirContents(path string) ([]os.FileInfo, error) {
  701. var files []os.FileInfo
  702. f, err := os.Open(path)
  703. if err != nil {
  704. return files, err
  705. }
  706. files, err = f.Readdir(-1)
  707. f.Close()
  708. return files, err
  709. }