You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

upload.go 4.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. // Copyright 2016 The Gogs Authors. All rights reserved.
  2. // Copyright 2019 The Gitea Authors. All rights reserved.
  3. // SPDX-License-Identifier: MIT
  4. package repo
  5. import (
  6. "context"
  7. "fmt"
  8. "io"
  9. "mime/multipart"
  10. "os"
  11. "path"
  12. "code.gitea.io/gitea/models/db"
  13. "code.gitea.io/gitea/modules/log"
  14. "code.gitea.io/gitea/modules/setting"
  15. "code.gitea.io/gitea/modules/util"
  16. gouuid "github.com/google/uuid"
  17. )
  18. // ErrUploadNotExist represents a "UploadNotExist" kind of error.
  19. type ErrUploadNotExist struct {
  20. ID int64
  21. UUID string
  22. }
  23. // IsErrUploadNotExist checks if an error is a ErrUploadNotExist.
  24. func IsErrUploadNotExist(err error) bool {
  25. _, ok := err.(ErrUploadNotExist)
  26. return ok
  27. }
  28. func (err ErrUploadNotExist) Error() string {
  29. return fmt.Sprintf("attachment does not exist [id: %d, uuid: %s]", err.ID, err.UUID)
  30. }
  31. func (err ErrUploadNotExist) Unwrap() error {
  32. return util.ErrNotExist
  33. }
  34. // Upload represent a uploaded file to a repo to be deleted when moved
  35. type Upload struct {
  36. ID int64 `xorm:"pk autoincr"`
  37. UUID string `xorm:"uuid UNIQUE"`
  38. Name string
  39. }
  40. func init() {
  41. db.RegisterModel(new(Upload))
  42. }
  43. // UploadLocalPath returns where uploads is stored in local file system based on given UUID.
  44. func UploadLocalPath(uuid string) string {
  45. return path.Join(setting.Repository.Upload.TempPath, uuid[0:1], uuid[1:2], uuid)
  46. }
  47. // LocalPath returns where uploads are temporarily stored in local file system.
  48. func (upload *Upload) LocalPath() string {
  49. return UploadLocalPath(upload.UUID)
  50. }
  51. // NewUpload creates a new upload object.
  52. func NewUpload(ctx context.Context, name string, buf []byte, file multipart.File) (_ *Upload, err error) {
  53. upload := &Upload{
  54. UUID: gouuid.New().String(),
  55. Name: name,
  56. }
  57. localPath := upload.LocalPath()
  58. if err = os.MkdirAll(path.Dir(localPath), os.ModePerm); err != nil {
  59. return nil, fmt.Errorf("MkdirAll: %w", err)
  60. }
  61. fw, err := os.Create(localPath)
  62. if err != nil {
  63. return nil, fmt.Errorf("Create: %w", err)
  64. }
  65. defer fw.Close()
  66. if _, err = fw.Write(buf); err != nil {
  67. return nil, fmt.Errorf("Write: %w", err)
  68. } else if _, err = io.Copy(fw, file); err != nil {
  69. return nil, fmt.Errorf("Copy: %w", err)
  70. }
  71. if _, err := db.GetEngine(ctx).Insert(upload); err != nil {
  72. return nil, err
  73. }
  74. return upload, nil
  75. }
  76. // GetUploadByUUID returns the Upload by UUID
  77. func GetUploadByUUID(ctx context.Context, uuid string) (*Upload, error) {
  78. upload := &Upload{}
  79. has, err := db.GetEngine(ctx).Where("uuid=?", uuid).Get(upload)
  80. if err != nil {
  81. return nil, err
  82. } else if !has {
  83. return nil, ErrUploadNotExist{0, uuid}
  84. }
  85. return upload, nil
  86. }
  87. // GetUploadsByUUIDs returns multiple uploads by UUIDS
  88. func GetUploadsByUUIDs(ctx context.Context, uuids []string) ([]*Upload, error) {
  89. if len(uuids) == 0 {
  90. return []*Upload{}, nil
  91. }
  92. // Silently drop invalid uuids.
  93. uploads := make([]*Upload, 0, len(uuids))
  94. return uploads, db.GetEngine(ctx).In("uuid", uuids).Find(&uploads)
  95. }
  96. // DeleteUploads deletes multiple uploads
  97. func DeleteUploads(ctx context.Context, uploads ...*Upload) (err error) {
  98. if len(uploads) == 0 {
  99. return nil
  100. }
  101. ctx, committer, err := db.TxContext(ctx)
  102. if err != nil {
  103. return err
  104. }
  105. defer committer.Close()
  106. ids := make([]int64, len(uploads))
  107. for i := 0; i < len(uploads); i++ {
  108. ids[i] = uploads[i].ID
  109. }
  110. if _, err = db.GetEngine(ctx).
  111. In("id", ids).
  112. Delete(new(Upload)); err != nil {
  113. return fmt.Errorf("delete uploads: %w", err)
  114. }
  115. if err = committer.Commit(); err != nil {
  116. return err
  117. }
  118. for _, upload := range uploads {
  119. localPath := upload.LocalPath()
  120. isFile, err := util.IsFile(localPath)
  121. if err != nil {
  122. log.Error("Unable to check if %s is a file. Error: %v", localPath, err)
  123. }
  124. if !isFile {
  125. continue
  126. }
  127. if err := util.Remove(localPath); err != nil {
  128. return fmt.Errorf("remove upload: %w", err)
  129. }
  130. }
  131. return nil
  132. }
  133. // DeleteUploadByUUID deletes a upload by UUID
  134. func DeleteUploadByUUID(ctx context.Context, uuid string) error {
  135. upload, err := GetUploadByUUID(ctx, uuid)
  136. if err != nil {
  137. if IsErrUploadNotExist(err) {
  138. return nil
  139. }
  140. return fmt.Errorf("GetUploadByUUID: %w", err)
  141. }
  142. if err := DeleteUploads(ctx, upload); err != nil {
  143. return fmt.Errorf("DeleteUpload: %w", err)
  144. }
  145. return nil
  146. }