handler.go 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614
  1. package sftpd
  2. import (
  3. "io"
  4. "net"
  5. "os"
  6. "path"
  7. "sync"
  8. "time"
  9. "github.com/pkg/sftp"
  10. "golang.org/x/crypto/ssh"
  11. "github.com/drakkan/sftpgo/dataprovider"
  12. "github.com/drakkan/sftpgo/logger"
  13. "github.com/drakkan/sftpgo/vfs"
  14. )
  15. // Connection details for an authenticated user
  16. type Connection struct {
  17. // Unique identifier for the connection
  18. ID string
  19. // logged in user's details
  20. User dataprovider.User
  21. // client's version string
  22. ClientVersion string
  23. // Remote address for this connection
  24. RemoteAddr net.Addr
  25. // start time for this connection
  26. StartTime time.Time
  27. // last activity for this connection
  28. lastActivity time.Time
  29. protocol string
  30. netConn net.Conn
  31. channel ssh.Channel
  32. command string
  33. fs vfs.Fs
  34. }
  35. // Log outputs a log entry to the configured logger
  36. func (c Connection) Log(level logger.LogLevel, sender string, format string, v ...interface{}) {
  37. logger.Log(level, sender, c.ID, format, v...)
  38. }
  39. // Fileread creates a reader for a file on the system and returns the reader back.
  40. func (c Connection) Fileread(request *sftp.Request) (io.ReaderAt, error) {
  41. updateConnectionActivity(c.ID)
  42. if !c.User.HasPerm(dataprovider.PermDownload, path.Dir(request.Filepath)) {
  43. return nil, sftp.ErrSSHFxPermissionDenied
  44. }
  45. if !c.User.IsFileAllowed(request.Filepath) {
  46. c.Log(logger.LevelWarn, logSender, "reading file %#v is not allowed", request.Filepath)
  47. return nil, sftp.ErrSSHFxPermissionDenied
  48. }
  49. p, err := c.fs.ResolvePath(request.Filepath)
  50. if err != nil {
  51. return nil, vfs.GetSFTPError(c.fs, err)
  52. }
  53. file, r, cancelFn, err := c.fs.Open(p)
  54. if err != nil {
  55. c.Log(logger.LevelWarn, logSender, "could not open file %#v for reading: %+v", p, err)
  56. return nil, vfs.GetSFTPError(c.fs, err)
  57. }
  58. c.Log(logger.LevelDebug, logSender, "fileread requested for path: %#v", p)
  59. transfer := Transfer{
  60. file: file,
  61. readerAt: r,
  62. writerAt: nil,
  63. cancelFn: cancelFn,
  64. path: p,
  65. start: time.Now(),
  66. bytesSent: 0,
  67. bytesReceived: 0,
  68. user: c.User,
  69. connectionID: c.ID,
  70. transferType: transferDownload,
  71. lastActivity: time.Now(),
  72. isNewFile: false,
  73. protocol: c.protocol,
  74. transferError: nil,
  75. isFinished: false,
  76. minWriteOffset: 0,
  77. isExcludedFromQuota: c.User.IsFileExcludedFromQuota(request.Filepath),
  78. lock: new(sync.Mutex),
  79. }
  80. addTransfer(&transfer)
  81. return &transfer, nil
  82. }
  83. // Filewrite handles the write actions for a file on the system.
  84. func (c Connection) Filewrite(request *sftp.Request) (io.WriterAt, error) {
  85. updateConnectionActivity(c.ID)
  86. if !c.User.IsFileAllowed(request.Filepath) {
  87. c.Log(logger.LevelWarn, logSender, "writing file %#v is not allowed", request.Filepath)
  88. return nil, sftp.ErrSSHFxPermissionDenied
  89. }
  90. p, err := c.fs.ResolvePath(request.Filepath)
  91. if err != nil {
  92. return nil, vfs.GetSFTPError(c.fs, err)
  93. }
  94. filePath := p
  95. if isAtomicUploadEnabled() && c.fs.IsAtomicUploadSupported() {
  96. filePath = c.fs.GetAtomicUploadPath(p)
  97. }
  98. stat, statErr := c.fs.Stat(p)
  99. if c.fs.IsNotExist(statErr) {
  100. if !c.User.HasPerm(dataprovider.PermUpload, path.Dir(request.Filepath)) {
  101. return nil, sftp.ErrSSHFxPermissionDenied
  102. }
  103. return c.handleSFTPUploadToNewFile(p, filePath, c.User.IsFileExcludedFromQuota(request.Filepath))
  104. }
  105. if statErr != nil {
  106. c.Log(logger.LevelError, logSender, "error performing file stat %#v: %+v", p, statErr)
  107. return nil, vfs.GetSFTPError(c.fs, statErr)
  108. }
  109. // This happen if we upload a file that has the same name of an existing directory
  110. if stat.IsDir() {
  111. c.Log(logger.LevelWarn, logSender, "attempted to open a directory for writing to: %#v", p)
  112. return nil, sftp.ErrSSHFxOpUnsupported
  113. }
  114. if !c.User.HasPerm(dataprovider.PermOverwrite, path.Dir(request.Filepath)) {
  115. return nil, sftp.ErrSSHFxPermissionDenied
  116. }
  117. return c.handleSFTPUploadToExistingFile(request.Pflags(), p, filePath, stat.Size(),
  118. c.User.IsFileExcludedFromQuota(request.Filepath))
  119. }
  120. // Filecmd hander for basic SFTP system calls related to files, but not anything to do with reading
  121. // or writing to those files.
  122. func (c Connection) Filecmd(request *sftp.Request) error {
  123. updateConnectionActivity(c.ID)
  124. p, err := c.fs.ResolvePath(request.Filepath)
  125. if err != nil {
  126. return vfs.GetSFTPError(c.fs, err)
  127. }
  128. target, err := c.getSFTPCmdTargetPath(request.Target)
  129. if err != nil {
  130. return err
  131. }
  132. c.Log(logger.LevelDebug, logSender, "new cmd, method: %v, sourcePath: %#v, targetPath: %#v", request.Method,
  133. p, target)
  134. switch request.Method {
  135. case "Setstat":
  136. return c.handleSFTPSetstat(p, request)
  137. case "Rename":
  138. if err = c.handleSFTPRename(p, target, request); err != nil {
  139. return err
  140. }
  141. case "Rmdir":
  142. return c.handleSFTPRmdir(p, request)
  143. case "Mkdir":
  144. err = c.handleSFTPMkdir(p, request)
  145. if err != nil {
  146. return err
  147. }
  148. case "Symlink":
  149. if err = c.handleSFTPSymlink(p, target, request); err != nil {
  150. return err
  151. }
  152. case "Remove":
  153. return c.handleSFTPRemove(p, request)
  154. default:
  155. return sftp.ErrSSHFxOpUnsupported
  156. }
  157. var fileLocation = p
  158. if target != "" {
  159. fileLocation = target
  160. }
  161. // we return if we remove a file or a dir so source path or target path always exists here
  162. vfs.SetPathPermissions(c.fs, fileLocation, c.User.GetUID(), c.User.GetGID())
  163. return sftp.ErrSSHFxOk
  164. }
  165. // Filelist is the handler for SFTP filesystem list calls. This will handle calls to list the contents of
  166. // a directory as well as perform file/folder stat calls.
  167. func (c Connection) Filelist(request *sftp.Request) (sftp.ListerAt, error) {
  168. updateConnectionActivity(c.ID)
  169. p, err := c.fs.ResolvePath(request.Filepath)
  170. if err != nil {
  171. return nil, vfs.GetSFTPError(c.fs, err)
  172. }
  173. switch request.Method {
  174. case "List":
  175. if !c.User.HasPerm(dataprovider.PermListItems, request.Filepath) {
  176. return nil, sftp.ErrSSHFxPermissionDenied
  177. }
  178. c.Log(logger.LevelDebug, logSender, "requested list file for dir: %#v", p)
  179. files, err := c.fs.ReadDir(p)
  180. if err != nil {
  181. c.Log(logger.LevelWarn, logSender, "error listing directory: %+v", err)
  182. return nil, vfs.GetSFTPError(c.fs, err)
  183. }
  184. return listerAt(c.User.AddVirtualDirs(files, request.Filepath)), nil
  185. case "Stat":
  186. if !c.User.HasPerm(dataprovider.PermListItems, path.Dir(request.Filepath)) {
  187. return nil, sftp.ErrSSHFxPermissionDenied
  188. }
  189. c.Log(logger.LevelDebug, logSender, "requested stat for path: %#v", p)
  190. s, err := c.fs.Stat(p)
  191. if err != nil {
  192. c.Log(logger.LevelWarn, logSender, "error running stat on path: %+v", err)
  193. return nil, vfs.GetSFTPError(c.fs, err)
  194. }
  195. return listerAt([]os.FileInfo{s}), nil
  196. default:
  197. return nil, sftp.ErrSSHFxOpUnsupported
  198. }
  199. }
  200. func (c Connection) getSFTPCmdTargetPath(requestTarget string) (string, error) {
  201. var target string
  202. // If a target is provided in this request validate that it is going to the correct
  203. // location for the server. If it is not, return an error
  204. if len(requestTarget) > 0 {
  205. var err error
  206. target, err = c.fs.ResolvePath(requestTarget)
  207. if err != nil {
  208. return target, vfs.GetSFTPError(c.fs, err)
  209. }
  210. }
  211. return target, nil
  212. }
  213. func (c Connection) handleSFTPSetstat(filePath string, request *sftp.Request) error {
  214. if setstatMode == 1 {
  215. return nil
  216. }
  217. pathForPerms := request.Filepath
  218. if fi, err := c.fs.Lstat(filePath); err == nil {
  219. if fi.IsDir() {
  220. pathForPerms = path.Dir(request.Filepath)
  221. }
  222. }
  223. attrFlags := request.AttrFlags()
  224. if attrFlags.Permissions {
  225. if !c.User.HasPerm(dataprovider.PermChmod, pathForPerms) {
  226. return sftp.ErrSSHFxPermissionDenied
  227. }
  228. fileMode := request.Attributes().FileMode()
  229. if err := c.fs.Chmod(filePath, fileMode); err != nil {
  230. c.Log(logger.LevelWarn, logSender, "failed to chmod path %#v, mode: %v, err: %+v", filePath, fileMode.String(), err)
  231. return vfs.GetSFTPError(c.fs, err)
  232. }
  233. logger.CommandLog(chmodLogSender, filePath, "", c.User.Username, fileMode.String(), c.ID, c.protocol, -1, -1, "", "", "")
  234. return nil
  235. } else if attrFlags.UidGid {
  236. if !c.User.HasPerm(dataprovider.PermChown, pathForPerms) {
  237. return sftp.ErrSSHFxPermissionDenied
  238. }
  239. uid := int(request.Attributes().UID)
  240. gid := int(request.Attributes().GID)
  241. if err := c.fs.Chown(filePath, uid, gid); err != nil {
  242. c.Log(logger.LevelWarn, logSender, "failed to chown path %#v, uid: %v, gid: %v, err: %+v", filePath, uid, gid, err)
  243. return vfs.GetSFTPError(c.fs, err)
  244. }
  245. logger.CommandLog(chownLogSender, filePath, "", c.User.Username, "", c.ID, c.protocol, uid, gid, "", "", "")
  246. return nil
  247. } else if attrFlags.Acmodtime {
  248. if !c.User.HasPerm(dataprovider.PermChtimes, pathForPerms) {
  249. return sftp.ErrSSHFxPermissionDenied
  250. }
  251. dateFormat := "2006-01-02T15:04:05" // YYYY-MM-DDTHH:MM:SS
  252. accessTime := time.Unix(int64(request.Attributes().Atime), 0)
  253. modificationTime := time.Unix(int64(request.Attributes().Mtime), 0)
  254. accessTimeString := accessTime.Format(dateFormat)
  255. modificationTimeString := modificationTime.Format(dateFormat)
  256. if err := c.fs.Chtimes(filePath, accessTime, modificationTime); err != nil {
  257. c.Log(logger.LevelWarn, logSender, "failed to chtimes for path %#v, access time: %v, modification time: %v, err: %+v",
  258. filePath, accessTime, modificationTime, err)
  259. return vfs.GetSFTPError(c.fs, err)
  260. }
  261. logger.CommandLog(chtimesLogSender, filePath, "", c.User.Username, "", c.ID, c.protocol, -1, -1, accessTimeString,
  262. modificationTimeString, "")
  263. return nil
  264. }
  265. return nil
  266. }
  267. func (c Connection) handleSFTPRename(sourcePath string, targetPath string, request *sftp.Request) error {
  268. if c.fs.GetRelativePath(sourcePath) == "/" {
  269. c.Log(logger.LevelWarn, logSender, "renaming root dir is not allowed")
  270. return sftp.ErrSSHFxPermissionDenied
  271. }
  272. if c.User.IsVirtualFolder(request.Filepath) || c.User.IsVirtualFolder(request.Target) {
  273. c.Log(logger.LevelWarn, logSender, "renaming a virtual folder is not allowed")
  274. return sftp.ErrSSHFxPermissionDenied
  275. }
  276. if !c.User.IsFileAllowed(request.Filepath) || !c.User.IsFileAllowed(request.Target) {
  277. if fi, err := c.fs.Lstat(sourcePath); err == nil && fi.Mode().IsRegular() {
  278. c.Log(logger.LevelDebug, logSender, "renaming file is not allowed, source: %#v target: %#v", request.Filepath,
  279. request.Target)
  280. return sftp.ErrSSHFxPermissionDenied
  281. }
  282. }
  283. if !c.User.HasPerm(dataprovider.PermRename, path.Dir(request.Target)) {
  284. return sftp.ErrSSHFxPermissionDenied
  285. }
  286. if err := c.fs.Rename(sourcePath, targetPath); err != nil {
  287. c.Log(logger.LevelWarn, logSender, "failed to rename file, source: %#v target: %#v: %+v", sourcePath, targetPath, err)
  288. return vfs.GetSFTPError(c.fs, err)
  289. }
  290. logger.CommandLog(renameLogSender, sourcePath, targetPath, c.User.Username, "", c.ID, c.protocol, -1, -1, "", "", "")
  291. // the returned error is used in test cases only, we already log the error inside executeAction
  292. go executeAction(newActionNotification(c.User, operationRename, sourcePath, targetPath, "", 0, nil)) //nolint:errcheck
  293. return nil
  294. }
  295. func (c Connection) handleSFTPRmdir(dirPath string, request *sftp.Request) error {
  296. if c.fs.GetRelativePath(dirPath) == "/" {
  297. c.Log(logger.LevelWarn, logSender, "removing root dir is not allowed")
  298. return sftp.ErrSSHFxPermissionDenied
  299. }
  300. if c.User.IsVirtualFolder(request.Filepath) {
  301. c.Log(logger.LevelWarn, logSender, "removing a virtual folder is not allowed: %#v", request.Filepath)
  302. return sftp.ErrSSHFxPermissionDenied
  303. }
  304. if !c.User.HasPerm(dataprovider.PermDelete, path.Dir(request.Filepath)) {
  305. return sftp.ErrSSHFxPermissionDenied
  306. }
  307. var fi os.FileInfo
  308. var err error
  309. if fi, err = c.fs.Lstat(dirPath); err != nil {
  310. c.Log(logger.LevelWarn, logSender, "failed to remove a dir %#v: stat error: %+v", dirPath, err)
  311. return vfs.GetSFTPError(c.fs, err)
  312. }
  313. if !fi.IsDir() || fi.Mode()&os.ModeSymlink == os.ModeSymlink {
  314. c.Log(logger.LevelDebug, logSender, "cannot remove %#v is not a directory", dirPath)
  315. return sftp.ErrSSHFxFailure
  316. }
  317. if err = c.fs.Remove(dirPath, true); err != nil {
  318. c.Log(logger.LevelWarn, logSender, "failed to remove directory %#v: %+v", dirPath, err)
  319. return vfs.GetSFTPError(c.fs, err)
  320. }
  321. logger.CommandLog(rmdirLogSender, dirPath, "", c.User.Username, "", c.ID, c.protocol, -1, -1, "", "", "")
  322. return sftp.ErrSSHFxOk
  323. }
  324. func (c Connection) handleSFTPSymlink(sourcePath string, targetPath string, request *sftp.Request) error {
  325. if c.fs.GetRelativePath(sourcePath) == "/" {
  326. c.Log(logger.LevelWarn, logSender, "symlinking root dir is not allowed")
  327. return sftp.ErrSSHFxPermissionDenied
  328. }
  329. if c.User.IsVirtualFolder(request.Target) {
  330. c.Log(logger.LevelWarn, logSender, "symlinking a virtual folder is not allowed")
  331. return sftp.ErrSSHFxPermissionDenied
  332. }
  333. if !c.User.HasPerm(dataprovider.PermCreateSymlinks, path.Dir(request.Target)) {
  334. return sftp.ErrSSHFxPermissionDenied
  335. }
  336. if err := c.fs.Symlink(sourcePath, targetPath); err != nil {
  337. c.Log(logger.LevelWarn, logSender, "failed to create symlink %#v -> %#v: %+v", sourcePath, targetPath, err)
  338. return vfs.GetSFTPError(c.fs, err)
  339. }
  340. logger.CommandLog(symlinkLogSender, sourcePath, targetPath, c.User.Username, "", c.ID, c.protocol, -1, -1, "", "", "")
  341. return nil
  342. }
  343. func (c Connection) handleSFTPMkdir(dirPath string, request *sftp.Request) error {
  344. if !c.User.HasPerm(dataprovider.PermCreateDirs, path.Dir(request.Filepath)) {
  345. return sftp.ErrSSHFxPermissionDenied
  346. }
  347. if c.User.IsVirtualFolder(request.Filepath) {
  348. c.Log(logger.LevelWarn, logSender, "mkdir not allowed %#v is virtual folder is not allowed", request.Filepath)
  349. return sftp.ErrSSHFxPermissionDenied
  350. }
  351. if err := c.fs.Mkdir(dirPath); err != nil {
  352. c.Log(logger.LevelWarn, logSender, "error creating missing dir: %#v error: %+v", dirPath, err)
  353. return vfs.GetSFTPError(c.fs, err)
  354. }
  355. vfs.SetPathPermissions(c.fs, dirPath, c.User.GetUID(), c.User.GetGID())
  356. logger.CommandLog(mkdirLogSender, dirPath, "", c.User.Username, "", c.ID, c.protocol, -1, -1, "", "", "")
  357. return nil
  358. }
  359. func (c Connection) handleSFTPRemove(filePath string, request *sftp.Request) error {
  360. if !c.User.HasPerm(dataprovider.PermDelete, path.Dir(request.Filepath)) {
  361. return sftp.ErrSSHFxPermissionDenied
  362. }
  363. var size int64
  364. var fi os.FileInfo
  365. var err error
  366. if fi, err = c.fs.Lstat(filePath); err != nil {
  367. c.Log(logger.LevelWarn, logSender, "failed to remove a file %#v: stat error: %+v", filePath, err)
  368. return vfs.GetSFTPError(c.fs, err)
  369. }
  370. if fi.IsDir() && fi.Mode()&os.ModeSymlink != os.ModeSymlink {
  371. c.Log(logger.LevelDebug, logSender, "cannot remove %#v is not a file/symlink", filePath)
  372. return sftp.ErrSSHFxFailure
  373. }
  374. if !c.User.IsFileAllowed(request.Filepath) {
  375. c.Log(logger.LevelDebug, logSender, "removing file %#v is not allowed", filePath)
  376. return sftp.ErrSSHFxPermissionDenied
  377. }
  378. size = fi.Size()
  379. actionErr := executeAction(newActionNotification(c.User, operationPreDelete, filePath, "", "", fi.Size(), nil))
  380. if actionErr == nil {
  381. c.Log(logger.LevelDebug, logSender, "remove for path %#v handled by pre-delete action", filePath)
  382. } else {
  383. if err := c.fs.Remove(filePath, false); err != nil {
  384. c.Log(logger.LevelWarn, logSender, "failed to remove a file/symlink %#v: %+v", filePath, err)
  385. return vfs.GetSFTPError(c.fs, err)
  386. }
  387. }
  388. logger.CommandLog(removeLogSender, filePath, "", c.User.Username, "", c.ID, c.protocol, -1, -1, "", "", "")
  389. if fi.Mode()&os.ModeSymlink != os.ModeSymlink {
  390. if !c.User.IsFileExcludedFromQuota(request.Filepath) {
  391. dataprovider.UpdateUserQuota(dataProvider, c.User, -1, -size, false) //nolint:errcheck
  392. }
  393. }
  394. if actionErr != nil {
  395. go executeAction(newActionNotification(c.User, operationDelete, filePath, "", "", fi.Size(), nil)) //nolint:errcheck
  396. }
  397. return sftp.ErrSSHFxOk
  398. }
  399. func (c Connection) handleSFTPUploadToNewFile(requestPath, filePath string, isExcludedFromQuota bool) (io.WriterAt, error) {
  400. if !c.hasSpace(true) {
  401. c.Log(logger.LevelInfo, logSender, "denying file write due to space limit")
  402. return nil, sftp.ErrSSHFxFailure
  403. }
  404. file, w, cancelFn, err := c.fs.Create(filePath, 0)
  405. if err != nil {
  406. c.Log(logger.LevelWarn, logSender, "error creating file %#v: %+v", requestPath, err)
  407. return nil, vfs.GetSFTPError(c.fs, err)
  408. }
  409. vfs.SetPathPermissions(c.fs, filePath, c.User.GetUID(), c.User.GetGID())
  410. transfer := Transfer{
  411. file: file,
  412. writerAt: w,
  413. readerAt: nil,
  414. cancelFn: cancelFn,
  415. path: requestPath,
  416. start: time.Now(),
  417. bytesSent: 0,
  418. bytesReceived: 0,
  419. user: c.User,
  420. connectionID: c.ID,
  421. transferType: transferUpload,
  422. lastActivity: time.Now(),
  423. isNewFile: true,
  424. protocol: c.protocol,
  425. transferError: nil,
  426. isFinished: false,
  427. minWriteOffset: 0,
  428. isExcludedFromQuota: isExcludedFromQuota,
  429. lock: new(sync.Mutex),
  430. }
  431. addTransfer(&transfer)
  432. return &transfer, nil
  433. }
  434. func (c Connection) handleSFTPUploadToExistingFile(pflags sftp.FileOpenFlags, requestPath, filePath string,
  435. fileSize int64, isExcludedFromQuota bool) (io.WriterAt, error) {
  436. var err error
  437. if !c.hasSpace(false) {
  438. c.Log(logger.LevelInfo, logSender, "denying file write due to space limit")
  439. return nil, sftp.ErrSSHFxFailure
  440. }
  441. minWriteOffset := int64(0)
  442. osFlags := getOSOpenFlags(pflags)
  443. if pflags.Append && osFlags&os.O_TRUNC == 0 && !c.fs.IsUploadResumeSupported() {
  444. c.Log(logger.LevelInfo, logSender, "upload resume requested for path: %#v but not supported in fs implementation",
  445. requestPath)
  446. return nil, sftp.ErrSSHFxOpUnsupported
  447. }
  448. if isAtomicUploadEnabled() && c.fs.IsAtomicUploadSupported() {
  449. err = c.fs.Rename(requestPath, filePath)
  450. if err != nil {
  451. c.Log(logger.LevelWarn, logSender, "error renaming existing file for atomic upload, source: %#v, dest: %#v, err: %+v",
  452. requestPath, filePath, err)
  453. return nil, vfs.GetSFTPError(c.fs, err)
  454. }
  455. }
  456. file, w, cancelFn, err := c.fs.Create(filePath, osFlags)
  457. if err != nil {
  458. c.Log(logger.LevelWarn, logSender, "error opening existing file, flags: %v, source: %#v, err: %+v", pflags, filePath, err)
  459. return nil, vfs.GetSFTPError(c.fs, err)
  460. }
  461. initialSize := int64(0)
  462. if pflags.Append && osFlags&os.O_TRUNC == 0 {
  463. c.Log(logger.LevelDebug, logSender, "upload resume requested, file path: %#v initial size: %v", filePath, fileSize)
  464. minWriteOffset = fileSize
  465. } else {
  466. if vfs.IsLocalOsFs(c.fs) {
  467. if !isExcludedFromQuota {
  468. dataprovider.UpdateUserQuota(dataProvider, c.User, 0, -fileSize, false) //nolint:errcheck
  469. }
  470. } else {
  471. initialSize = fileSize
  472. }
  473. }
  474. vfs.SetPathPermissions(c.fs, filePath, c.User.GetUID(), c.User.GetGID())
  475. transfer := Transfer{
  476. file: file,
  477. writerAt: w,
  478. readerAt: nil,
  479. cancelFn: cancelFn,
  480. path: requestPath,
  481. start: time.Now(),
  482. bytesSent: 0,
  483. bytesReceived: 0,
  484. user: c.User,
  485. connectionID: c.ID,
  486. transferType: transferUpload,
  487. lastActivity: time.Now(),
  488. isNewFile: false,
  489. protocol: c.protocol,
  490. transferError: nil,
  491. isFinished: false,
  492. minWriteOffset: minWriteOffset,
  493. initialSize: initialSize,
  494. isExcludedFromQuota: isExcludedFromQuota,
  495. lock: new(sync.Mutex),
  496. }
  497. addTransfer(&transfer)
  498. return &transfer, nil
  499. }
  500. func (c Connection) hasSpace(checkFiles bool) bool {
  501. if (checkFiles && c.User.QuotaFiles > 0) || c.User.QuotaSize > 0 {
  502. numFile, size, err := dataprovider.GetUsedQuota(dataProvider, c.User.Username)
  503. if err != nil {
  504. if _, ok := err.(*dataprovider.MethodDisabledError); ok {
  505. c.Log(logger.LevelWarn, logSender, "quota enforcement not possible for user %#v: %v", c.User.Username, err)
  506. return true
  507. }
  508. c.Log(logger.LevelWarn, logSender, "error getting used quota for %#v: %v", c.User.Username, err)
  509. return false
  510. }
  511. if (checkFiles && c.User.QuotaFiles > 0 && numFile >= c.User.QuotaFiles) ||
  512. (c.User.QuotaSize > 0 && size >= c.User.QuotaSize) {
  513. c.Log(logger.LevelDebug, logSender, "quota exceed for user %#v, num files: %v/%v, size: %v/%v check files: %v",
  514. c.User.Username, numFile, c.User.QuotaFiles, size, c.User.QuotaSize, checkFiles)
  515. return false
  516. }
  517. }
  518. return true
  519. }
  520. func (c Connection) close() error {
  521. if c.channel != nil {
  522. err := c.channel.Close()
  523. c.Log(logger.LevelInfo, logSender, "channel close, err: %v", err)
  524. }
  525. return c.netConn.Close()
  526. }
  527. func getOSOpenFlags(requestFlags sftp.FileOpenFlags) (flags int) {
  528. var osFlags int
  529. if requestFlags.Read && requestFlags.Write {
  530. osFlags |= os.O_RDWR
  531. } else if requestFlags.Write {
  532. osFlags |= os.O_WRONLY
  533. }
  534. // we ignore Append flag since pkg/sftp use WriteAt that cannot work with os.O_APPEND
  535. /*if requestFlags.Append {
  536. osFlags |= os.O_APPEND
  537. }*/
  538. if requestFlags.Creat {
  539. osFlags |= os.O_CREATE
  540. }
  541. if requestFlags.Trunc {
  542. osFlags |= os.O_TRUNC
  543. }
  544. if requestFlags.Excl {
  545. osFlags |= os.O_EXCL
  546. }
  547. return osFlags
  548. }