transfer_test.go 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468
  1. // Copyright (C) 2019 Nicola Murino
  2. //
  3. // This program is free software: you can redistribute it and/or modify
  4. // it under the terms of the GNU Affero General Public License as published
  5. // by the Free Software Foundation, version 3.
  6. //
  7. // This program is distributed in the hope that it will be useful,
  8. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. // GNU Affero General Public License for more details.
  11. //
  12. // You should have received a copy of the GNU Affero General Public License
  13. // along with this program. If not, see <https://www.gnu.org/licenses/>.
  14. package common
  15. import (
  16. "errors"
  17. "fmt"
  18. "os"
  19. "path/filepath"
  20. "testing"
  21. "time"
  22. "github.com/pkg/sftp"
  23. "github.com/sftpgo/sdk"
  24. "github.com/stretchr/testify/assert"
  25. "github.com/stretchr/testify/require"
  26. "github.com/drakkan/sftpgo/v2/internal/dataprovider"
  27. "github.com/drakkan/sftpgo/v2/internal/kms"
  28. "github.com/drakkan/sftpgo/v2/internal/vfs"
  29. )
  30. func TestTransferUpdateQuota(t *testing.T) {
  31. conn := NewBaseConnection("", ProtocolSFTP, "", "", dataprovider.User{})
  32. transfer := BaseTransfer{
  33. Connection: conn,
  34. transferType: TransferUpload,
  35. Fs: vfs.NewOsFs("", os.TempDir(), "", nil),
  36. }
  37. transfer.BytesReceived.Store(123)
  38. errFake := errors.New("fake error")
  39. transfer.TransferError(errFake)
  40. err := transfer.Close()
  41. if assert.Error(t, err) {
  42. assert.EqualError(t, err, errFake.Error())
  43. }
  44. mappedPath := filepath.Join(os.TempDir(), "vdir")
  45. vdirPath := "/vdir"
  46. conn.User.VirtualFolders = append(conn.User.VirtualFolders, vfs.VirtualFolder{
  47. BaseVirtualFolder: vfs.BaseVirtualFolder{
  48. MappedPath: mappedPath,
  49. },
  50. VirtualPath: vdirPath,
  51. QuotaFiles: -1,
  52. QuotaSize: -1,
  53. })
  54. transfer.ErrTransfer = nil
  55. transfer.BytesReceived.Store(1)
  56. transfer.requestPath = "/vdir/file"
  57. assert.True(t, transfer.updateQuota(1, 0))
  58. err = transfer.Close()
  59. assert.NoError(t, err)
  60. transfer.ErrTransfer = errFake
  61. transfer.Fs = newMockOsFs(true, "", "", "S3Fs fake", nil)
  62. assert.False(t, transfer.updateQuota(1, 0))
  63. }
  64. func TestTransferThrottling(t *testing.T) {
  65. u := dataprovider.User{
  66. BaseUser: sdk.BaseUser{
  67. Username: "test",
  68. UploadBandwidth: 50,
  69. DownloadBandwidth: 40,
  70. },
  71. }
  72. fs := vfs.NewOsFs("", os.TempDir(), "", nil)
  73. testFileSize := int64(131072)
  74. wantedUploadElapsed := 1000 * (testFileSize / 1024) / u.UploadBandwidth
  75. wantedDownloadElapsed := 1000 * (testFileSize / 1024) / u.DownloadBandwidth
  76. // some tolerance
  77. wantedUploadElapsed -= wantedDownloadElapsed / 10
  78. wantedDownloadElapsed -= wantedDownloadElapsed / 10
  79. conn := NewBaseConnection("id", ProtocolSCP, "", "", u)
  80. transfer := NewBaseTransfer(nil, conn, nil, "", "", "", TransferUpload, 0, 0, 0, 0, true, fs, dataprovider.TransferQuota{})
  81. transfer.BytesReceived.Store(testFileSize)
  82. transfer.Connection.UpdateLastActivity()
  83. startTime := transfer.Connection.GetLastActivity()
  84. transfer.HandleThrottle()
  85. elapsed := time.Since(startTime).Nanoseconds() / 1000000
  86. assert.GreaterOrEqual(t, elapsed, wantedUploadElapsed, "upload bandwidth throttling not respected")
  87. err := transfer.Close()
  88. assert.NoError(t, err)
  89. transfer = NewBaseTransfer(nil, conn, nil, "", "", "", TransferDownload, 0, 0, 0, 0, true, fs, dataprovider.TransferQuota{})
  90. transfer.BytesSent.Store(testFileSize)
  91. transfer.Connection.UpdateLastActivity()
  92. startTime = transfer.Connection.GetLastActivity()
  93. transfer.HandleThrottle()
  94. elapsed = time.Since(startTime).Nanoseconds() / 1000000
  95. assert.GreaterOrEqual(t, elapsed, wantedDownloadElapsed, "download bandwidth throttling not respected")
  96. err = transfer.Close()
  97. assert.NoError(t, err)
  98. }
  99. func TestRealPath(t *testing.T) {
  100. testFile := filepath.Join(os.TempDir(), "afile.txt")
  101. fs := vfs.NewOsFs("123", os.TempDir(), "", nil)
  102. u := dataprovider.User{
  103. BaseUser: sdk.BaseUser{
  104. Username: "user",
  105. HomeDir: os.TempDir(),
  106. },
  107. }
  108. u.Permissions = make(map[string][]string)
  109. u.Permissions["/"] = []string{dataprovider.PermAny}
  110. file, err := os.Create(testFile)
  111. require.NoError(t, err)
  112. conn := NewBaseConnection(fs.ConnectionID(), ProtocolSFTP, "", "", u)
  113. transfer := NewBaseTransfer(file, conn, nil, testFile, testFile, "/transfer_test_file",
  114. TransferUpload, 0, 0, 0, 0, true, fs, dataprovider.TransferQuota{})
  115. rPath := transfer.GetRealFsPath(testFile)
  116. assert.Equal(t, testFile, rPath)
  117. rPath = conn.getRealFsPath(testFile)
  118. assert.Equal(t, testFile, rPath)
  119. err = transfer.Close()
  120. assert.NoError(t, err)
  121. err = file.Close()
  122. assert.NoError(t, err)
  123. transfer.File = nil
  124. rPath = transfer.GetRealFsPath(testFile)
  125. assert.Equal(t, testFile, rPath)
  126. rPath = transfer.GetRealFsPath("")
  127. assert.Empty(t, rPath)
  128. err = os.Remove(testFile)
  129. assert.NoError(t, err)
  130. assert.Len(t, conn.GetTransfers(), 0)
  131. }
  132. func TestTruncate(t *testing.T) {
  133. testFile := filepath.Join(os.TempDir(), "transfer_test_file")
  134. fs := vfs.NewOsFs("123", os.TempDir(), "", nil)
  135. u := dataprovider.User{
  136. BaseUser: sdk.BaseUser{
  137. Username: "user",
  138. HomeDir: os.TempDir(),
  139. },
  140. }
  141. u.Permissions = make(map[string][]string)
  142. u.Permissions["/"] = []string{dataprovider.PermAny}
  143. file, err := os.Create(testFile)
  144. if !assert.NoError(t, err) {
  145. assert.FailNow(t, "unable to open test file")
  146. }
  147. _, err = file.Write([]byte("hello"))
  148. assert.NoError(t, err)
  149. conn := NewBaseConnection(fs.ConnectionID(), ProtocolSFTP, "", "", u)
  150. transfer := NewBaseTransfer(file, conn, nil, testFile, testFile, "/transfer_test_file", TransferUpload, 0, 5,
  151. 100, 0, false, fs, dataprovider.TransferQuota{})
  152. err = conn.SetStat("/transfer_test_file", &StatAttributes{
  153. Size: 2,
  154. Flags: StatAttrSize,
  155. })
  156. assert.NoError(t, err)
  157. assert.Equal(t, int64(103), transfer.MaxWriteSize)
  158. err = transfer.Close()
  159. assert.NoError(t, err)
  160. err = file.Close()
  161. assert.NoError(t, err)
  162. fi, err := os.Stat(testFile)
  163. if assert.NoError(t, err) {
  164. assert.Equal(t, int64(2), fi.Size())
  165. }
  166. transfer = NewBaseTransfer(file, conn, nil, testFile, testFile, "/transfer_test_file", TransferUpload, 0, 0,
  167. 100, 0, true, fs, dataprovider.TransferQuota{})
  168. // file.Stat will fail on a closed file
  169. err = conn.SetStat("/transfer_test_file", &StatAttributes{
  170. Size: 2,
  171. Flags: StatAttrSize,
  172. })
  173. assert.Error(t, err)
  174. err = transfer.Close()
  175. assert.NoError(t, err)
  176. transfer = NewBaseTransfer(nil, conn, nil, testFile, testFile, "", TransferUpload, 0, 0, 0, 0, true,
  177. fs, dataprovider.TransferQuota{})
  178. _, err = transfer.Truncate("mismatch", 0)
  179. assert.EqualError(t, err, errTransferMismatch.Error())
  180. _, err = transfer.Truncate(testFile, 0)
  181. assert.NoError(t, err)
  182. _, err = transfer.Truncate(testFile, 1)
  183. assert.EqualError(t, err, vfs.ErrVfsUnsupported.Error())
  184. err = transfer.Close()
  185. assert.NoError(t, err)
  186. err = os.Remove(testFile)
  187. assert.NoError(t, err)
  188. assert.Len(t, conn.GetTransfers(), 0)
  189. }
  190. func TestTransferErrors(t *testing.T) {
  191. isCancelled := false
  192. cancelFn := func() {
  193. isCancelled = true
  194. }
  195. testFile := filepath.Join(os.TempDir(), "transfer_test_file")
  196. fs := vfs.NewOsFs("id", os.TempDir(), "", nil)
  197. u := dataprovider.User{
  198. BaseUser: sdk.BaseUser{
  199. Username: "test",
  200. HomeDir: os.TempDir(),
  201. },
  202. }
  203. err := os.WriteFile(testFile, []byte("test data"), os.ModePerm)
  204. assert.NoError(t, err)
  205. file, err := os.Open(testFile)
  206. if !assert.NoError(t, err) {
  207. assert.FailNow(t, "unable to open test file")
  208. }
  209. conn := NewBaseConnection("id", ProtocolSFTP, "", "", u)
  210. transfer := NewBaseTransfer(file, conn, nil, testFile, testFile, "/transfer_test_file", TransferUpload,
  211. 0, 0, 0, 0, true, fs, dataprovider.TransferQuota{})
  212. pathError := &os.PathError{
  213. Op: "test",
  214. Path: testFile,
  215. Err: os.ErrInvalid,
  216. }
  217. err = transfer.ConvertError(pathError)
  218. assert.EqualError(t, err, fmt.Sprintf("%s %s: %s", pathError.Op, "/transfer_test_file", pathError.Err.Error()))
  219. err = transfer.ConvertError(os.ErrNotExist)
  220. assert.ErrorIs(t, err, sftp.ErrSSHFxNoSuchFile)
  221. err = transfer.ConvertError(os.ErrPermission)
  222. assert.ErrorIs(t, err, sftp.ErrSSHFxPermissionDenied)
  223. assert.Nil(t, transfer.cancelFn)
  224. assert.Equal(t, testFile, transfer.GetFsPath())
  225. transfer.SetMetadata(map[string]string{"key": "val"})
  226. transfer.SetCancelFn(cancelFn)
  227. errFake := errors.New("err fake")
  228. transfer.BytesReceived.Store(9)
  229. transfer.TransferError(ErrQuotaExceeded)
  230. assert.True(t, isCancelled)
  231. transfer.TransferError(errFake)
  232. assert.Error(t, transfer.ErrTransfer, ErrQuotaExceeded.Error())
  233. // the file is closed from the embedding struct before to call close
  234. err = file.Close()
  235. assert.NoError(t, err)
  236. err = transfer.Close()
  237. if assert.Error(t, err) {
  238. assert.Error(t, err, ErrQuotaExceeded.Error())
  239. }
  240. assert.NoFileExists(t, testFile)
  241. err = os.WriteFile(testFile, []byte("test data"), os.ModePerm)
  242. assert.NoError(t, err)
  243. file, err = os.Open(testFile)
  244. if !assert.NoError(t, err) {
  245. assert.FailNow(t, "unable to open test file")
  246. }
  247. fsPath := filepath.Join(os.TempDir(), "test_file")
  248. transfer = NewBaseTransfer(file, conn, nil, fsPath, file.Name(), "/test_file", TransferUpload, 0, 0, 0, 0, true,
  249. fs, dataprovider.TransferQuota{})
  250. transfer.BytesReceived.Store(9)
  251. transfer.TransferError(errFake)
  252. assert.Error(t, transfer.ErrTransfer, errFake.Error())
  253. // the file is closed from the embedding struct before to call close
  254. err = file.Close()
  255. assert.NoError(t, err)
  256. err = transfer.Close()
  257. if assert.Error(t, err) {
  258. assert.Error(t, err, errFake.Error())
  259. }
  260. assert.NoFileExists(t, testFile)
  261. err = os.WriteFile(testFile, []byte("test data"), os.ModePerm)
  262. assert.NoError(t, err)
  263. file, err = os.Open(testFile)
  264. if !assert.NoError(t, err) {
  265. assert.FailNow(t, "unable to open test file")
  266. }
  267. transfer = NewBaseTransfer(file, conn, nil, fsPath, file.Name(), "/test_file", TransferUpload, 0, 0, 0, 0, true,
  268. fs, dataprovider.TransferQuota{})
  269. transfer.BytesReceived.Store(9)
  270. // the file is closed from the embedding struct before to call close
  271. err = file.Close()
  272. assert.NoError(t, err)
  273. err = transfer.Close()
  274. assert.NoError(t, err)
  275. assert.NoFileExists(t, testFile)
  276. assert.FileExists(t, fsPath)
  277. err = os.Remove(fsPath)
  278. assert.NoError(t, err)
  279. assert.Len(t, conn.GetTransfers(), 0)
  280. }
  281. func TestRemovePartialCryptoFile(t *testing.T) {
  282. testFile := filepath.Join(os.TempDir(), "transfer_test_file")
  283. fs, err := vfs.NewCryptFs("id", os.TempDir(), "", vfs.CryptFsConfig{Passphrase: kms.NewPlainSecret("secret")})
  284. require.NoError(t, err)
  285. u := dataprovider.User{
  286. BaseUser: sdk.BaseUser{
  287. Username: "test",
  288. HomeDir: os.TempDir(),
  289. },
  290. }
  291. conn := NewBaseConnection(fs.ConnectionID(), ProtocolSFTP, "", "", u)
  292. transfer := NewBaseTransfer(nil, conn, nil, testFile, testFile, "/transfer_test_file", TransferUpload,
  293. 0, 0, 0, 0, true, fs, dataprovider.TransferQuota{})
  294. transfer.ErrTransfer = errors.New("test error")
  295. _, _, err = transfer.getUploadFileSize()
  296. assert.Error(t, err)
  297. err = os.WriteFile(testFile, []byte("test data"), os.ModePerm)
  298. assert.NoError(t, err)
  299. size, deletedFiles, err := transfer.getUploadFileSize()
  300. assert.NoError(t, err)
  301. assert.Equal(t, int64(0), size)
  302. assert.Equal(t, 1, deletedFiles)
  303. assert.NoFileExists(t, testFile)
  304. }
  305. func TestFTPMode(t *testing.T) {
  306. conn := NewBaseConnection("", ProtocolFTP, "", "", dataprovider.User{})
  307. transfer := BaseTransfer{
  308. Connection: conn,
  309. transferType: TransferUpload,
  310. Fs: vfs.NewOsFs("", os.TempDir(), "", nil),
  311. }
  312. transfer.BytesReceived.Store(123)
  313. assert.Empty(t, transfer.ftpMode)
  314. transfer.SetFtpMode("active")
  315. assert.Equal(t, "active", transfer.ftpMode)
  316. }
  317. func TestTransferQuota(t *testing.T) {
  318. user := dataprovider.User{
  319. BaseUser: sdk.BaseUser{
  320. TotalDataTransfer: 3,
  321. UploadDataTransfer: 2,
  322. DownloadDataTransfer: 1,
  323. },
  324. }
  325. ul, dl, total := user.GetDataTransferLimits()
  326. assert.Equal(t, int64(2*1048576), ul)
  327. assert.Equal(t, int64(1*1048576), dl)
  328. assert.Equal(t, int64(3*1048576), total)
  329. user.TotalDataTransfer = -1
  330. user.UploadDataTransfer = -1
  331. user.DownloadDataTransfer = -1
  332. ul, dl, total = user.GetDataTransferLimits()
  333. assert.Equal(t, int64(0), ul)
  334. assert.Equal(t, int64(0), dl)
  335. assert.Equal(t, int64(0), total)
  336. transferQuota := dataprovider.TransferQuota{}
  337. assert.True(t, transferQuota.HasDownloadSpace())
  338. assert.True(t, transferQuota.HasUploadSpace())
  339. transferQuota.TotalSize = -1
  340. transferQuota.ULSize = -1
  341. transferQuota.DLSize = -1
  342. assert.True(t, transferQuota.HasDownloadSpace())
  343. assert.True(t, transferQuota.HasUploadSpace())
  344. transferQuota.TotalSize = 100
  345. transferQuota.AllowedTotalSize = 10
  346. assert.True(t, transferQuota.HasDownloadSpace())
  347. assert.True(t, transferQuota.HasUploadSpace())
  348. transferQuota.AllowedTotalSize = 0
  349. assert.False(t, transferQuota.HasDownloadSpace())
  350. assert.False(t, transferQuota.HasUploadSpace())
  351. transferQuota.TotalSize = 0
  352. transferQuota.DLSize = 100
  353. transferQuota.ULSize = 50
  354. transferQuota.AllowedTotalSize = 0
  355. assert.False(t, transferQuota.HasDownloadSpace())
  356. assert.False(t, transferQuota.HasUploadSpace())
  357. transferQuota.AllowedDLSize = 1
  358. transferQuota.AllowedULSize = 1
  359. assert.True(t, transferQuota.HasDownloadSpace())
  360. assert.True(t, transferQuota.HasUploadSpace())
  361. transferQuota.AllowedDLSize = -10
  362. transferQuota.AllowedULSize = -1
  363. assert.False(t, transferQuota.HasDownloadSpace())
  364. assert.False(t, transferQuota.HasUploadSpace())
  365. conn := NewBaseConnection("", ProtocolSFTP, "", "", user)
  366. transfer := NewBaseTransfer(nil, conn, nil, "file.txt", "file.txt", "/transfer_test_file", TransferUpload,
  367. 0, 0, 0, 0, true, vfs.NewOsFs("", os.TempDir(), "", nil), dataprovider.TransferQuota{})
  368. err := transfer.CheckRead()
  369. assert.NoError(t, err)
  370. err = transfer.CheckWrite()
  371. assert.NoError(t, err)
  372. transfer.transferQuota = dataprovider.TransferQuota{
  373. AllowedTotalSize: 10,
  374. }
  375. transfer.BytesReceived.Store(5)
  376. transfer.BytesSent.Store(4)
  377. err = transfer.CheckRead()
  378. assert.NoError(t, err)
  379. err = transfer.CheckWrite()
  380. assert.NoError(t, err)
  381. transfer.BytesSent.Store(6)
  382. err = transfer.CheckRead()
  383. if assert.Error(t, err) {
  384. assert.Contains(t, err.Error(), ErrReadQuotaExceeded.Error())
  385. }
  386. err = transfer.CheckWrite()
  387. assert.True(t, conn.IsQuotaExceededError(err))
  388. transferQuota = dataprovider.TransferQuota{
  389. AllowedTotalSize: 0,
  390. AllowedULSize: 10,
  391. AllowedDLSize: 5,
  392. }
  393. transfer.transferQuota = transferQuota
  394. assert.Equal(t, transferQuota, transfer.GetTransferQuota())
  395. err = transfer.CheckRead()
  396. if assert.Error(t, err) {
  397. assert.Contains(t, err.Error(), ErrReadQuotaExceeded.Error())
  398. }
  399. err = transfer.CheckWrite()
  400. assert.NoError(t, err)
  401. transfer.BytesReceived.Store(11)
  402. err = transfer.CheckRead()
  403. if assert.Error(t, err) {
  404. assert.Contains(t, err.Error(), ErrReadQuotaExceeded.Error())
  405. }
  406. err = transfer.CheckWrite()
  407. assert.True(t, conn.IsQuotaExceededError(err))
  408. }
  409. func TestUploadOutsideHomeRenameError(t *testing.T) {
  410. oldTempPath := Config.TempPath
  411. conn := NewBaseConnection("", ProtocolSFTP, "", "", dataprovider.User{})
  412. transfer := BaseTransfer{
  413. Connection: conn,
  414. transferType: TransferUpload,
  415. Fs: vfs.NewOsFs("", filepath.Join(os.TempDir(), "home"), "", nil),
  416. }
  417. transfer.BytesReceived.Store(123)
  418. fileName := filepath.Join(os.TempDir(), "_temp")
  419. err := os.WriteFile(fileName, []byte(`data`), 0644)
  420. assert.NoError(t, err)
  421. transfer.effectiveFsPath = fileName
  422. res := transfer.checkUploadOutsideHomeDir(os.ErrPermission)
  423. assert.Equal(t, 0, res)
  424. Config.TempPath = filepath.Clean(os.TempDir())
  425. res = transfer.checkUploadOutsideHomeDir(nil)
  426. assert.Equal(t, 0, res)
  427. assert.Greater(t, transfer.BytesReceived.Load(), int64(0))
  428. res = transfer.checkUploadOutsideHomeDir(os.ErrPermission)
  429. assert.Equal(t, 1, res)
  430. assert.Equal(t, int64(0), transfer.BytesReceived.Load())
  431. assert.NoFileExists(t, fileName)
  432. Config.TempPath = oldTempPath
  433. }