scp.go 19 KB

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