handler.go 20 KB

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