gitea源码

blob.go 6.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. // Copyright 2022 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package container
  4. import (
  5. "context"
  6. "encoding/hex"
  7. "errors"
  8. "fmt"
  9. "os"
  10. "strings"
  11. "code.gitea.io/gitea/models/db"
  12. packages_model "code.gitea.io/gitea/models/packages"
  13. container_model "code.gitea.io/gitea/models/packages/container"
  14. "code.gitea.io/gitea/modules/globallock"
  15. "code.gitea.io/gitea/modules/log"
  16. packages_module "code.gitea.io/gitea/modules/packages"
  17. container_module "code.gitea.io/gitea/modules/packages/container"
  18. "code.gitea.io/gitea/modules/util"
  19. packages_service "code.gitea.io/gitea/services/packages"
  20. "github.com/opencontainers/go-digest"
  21. )
  22. // saveAsPackageBlob creates a package blob from an upload
  23. // The uploaded blob gets stored in a special upload version to link them to the package/image
  24. func saveAsPackageBlob(ctx context.Context, hsr packages_module.HashedSizeReader, pci *packages_service.PackageCreationInfo) (*packages_model.PackageBlob, error) { //nolint:unparam // PackageBlob is never used
  25. pb := packages_service.NewPackageBlob(hsr)
  26. exists := false
  27. contentStore := packages_module.NewContentStore()
  28. uploadVersion, err := getOrCreateUploadVersion(ctx, &pci.PackageInfo)
  29. if err != nil {
  30. return nil, err
  31. }
  32. err = db.WithTx(ctx, func(ctx context.Context) error {
  33. if err := packages_service.CheckSizeQuotaExceeded(ctx, pci.Creator, pci.Owner, packages_model.TypeContainer, hsr.Size()); err != nil {
  34. return err
  35. }
  36. pb, exists, err = packages_model.GetOrInsertBlob(ctx, pb)
  37. if err != nil {
  38. log.Error("Error inserting package blob: %v", err)
  39. return err
  40. }
  41. // FIXME: Workaround to be removed in v1.20
  42. // https://github.com/go-gitea/gitea/issues/19586
  43. if exists {
  44. err = contentStore.Has(packages_module.BlobHash256Key(pb.HashSHA256))
  45. if err != nil && (errors.Is(err, util.ErrNotExist) || errors.Is(err, os.ErrNotExist)) {
  46. log.Debug("Package registry inconsistent: blob %s does not exist on file system", pb.HashSHA256)
  47. exists = false
  48. }
  49. }
  50. if !exists {
  51. if err := contentStore.Save(packages_module.BlobHash256Key(pb.HashSHA256), hsr, hsr.Size()); err != nil {
  52. log.Error("Error saving package blob in content store: %v", err)
  53. return err
  54. }
  55. }
  56. return createFileForBlob(ctx, uploadVersion, pb)
  57. })
  58. if err != nil {
  59. if !exists {
  60. if err := contentStore.Delete(packages_module.BlobHash256Key(pb.HashSHA256)); err != nil {
  61. log.Error("Error deleting package blob from content store: %v", err)
  62. }
  63. }
  64. return nil, err
  65. }
  66. return pb, nil
  67. }
  68. // mountBlob mounts the specific blob to a different package
  69. func mountBlob(ctx context.Context, pi *packages_service.PackageInfo, pb *packages_model.PackageBlob) error {
  70. uploadVersion, err := getOrCreateUploadVersion(ctx, pi)
  71. if err != nil {
  72. return err
  73. }
  74. return db.WithTx(ctx, func(ctx context.Context) error {
  75. return createFileForBlob(ctx, uploadVersion, pb)
  76. })
  77. }
  78. func containerGlobalLockKey(piOwnerID int64, piName, usage string) string {
  79. return fmt.Sprintf("pkg_%d_container_%s_%s", piOwnerID, strings.ToLower(piName), usage)
  80. }
  81. func getOrCreateUploadVersion(ctx context.Context, pi *packages_service.PackageInfo) (*packages_model.PackageVersion, error) {
  82. releaser, err := globallock.Lock(ctx, containerGlobalLockKey(pi.Owner.ID, pi.Name, "package"))
  83. if err != nil {
  84. return nil, err
  85. }
  86. defer releaser()
  87. return db.WithTx2(ctx, func(ctx context.Context) (*packages_model.PackageVersion, error) {
  88. created := true
  89. p := &packages_model.Package{
  90. OwnerID: pi.Owner.ID,
  91. Type: packages_model.TypeContainer,
  92. Name: strings.ToLower(pi.Name),
  93. LowerName: strings.ToLower(pi.Name),
  94. }
  95. var err error
  96. if p, err = packages_model.TryInsertPackage(ctx, p); err != nil {
  97. if !errors.Is(err, packages_model.ErrDuplicatePackage) {
  98. log.Error("Error inserting package: %v", err)
  99. return nil, err
  100. }
  101. created = false
  102. }
  103. if created {
  104. if _, err := packages_model.InsertProperty(ctx, packages_model.PropertyTypePackage, p.ID, container_module.PropertyRepository, strings.ToLower(pi.Owner.LowerName+"/"+pi.Name)); err != nil {
  105. log.Error("Error setting package property: %v", err)
  106. return nil, err
  107. }
  108. }
  109. pv := &packages_model.PackageVersion{
  110. PackageID: p.ID,
  111. CreatorID: pi.Owner.ID,
  112. Version: container_module.UploadVersion,
  113. LowerVersion: container_module.UploadVersion,
  114. IsInternal: true,
  115. MetadataJSON: "null",
  116. }
  117. if pv, err = packages_model.GetOrInsertVersion(ctx, pv); err != nil {
  118. if !errors.Is(err, packages_model.ErrDuplicatePackageVersion) {
  119. log.Error("Error inserting package: %v", err)
  120. return nil, err
  121. }
  122. }
  123. return pv, nil
  124. })
  125. }
  126. func createFileForBlob(ctx context.Context, pv *packages_model.PackageVersion, pb *packages_model.PackageBlob) error {
  127. filename := strings.ToLower("sha256_" + pb.HashSHA256)
  128. pf := &packages_model.PackageFile{
  129. VersionID: pv.ID,
  130. BlobID: pb.ID,
  131. Name: filename,
  132. LowerName: filename,
  133. CompositeKey: packages_model.EmptyFileKey,
  134. }
  135. var err error
  136. if pf, err = packages_model.TryInsertFile(ctx, pf); err != nil {
  137. if errors.Is(err, packages_model.ErrDuplicatePackageFile) {
  138. return nil
  139. }
  140. log.Error("Error inserting package file: %v", err)
  141. return err
  142. }
  143. if _, err := packages_model.InsertProperty(ctx, packages_model.PropertyTypeFile, pf.ID, container_module.PropertyDigest, digestFromPackageBlob(pb)); err != nil {
  144. log.Error("Error setting package file property: %v", err)
  145. return err
  146. }
  147. return nil
  148. }
  149. func deleteBlob(ctx context.Context, ownerID int64, image string, digest digest.Digest) error {
  150. releaser, err := globallock.Lock(ctx, containerGlobalLockKey(ownerID, image, "blob"))
  151. if err != nil {
  152. return err
  153. }
  154. defer releaser()
  155. return db.WithTx(ctx, func(ctx context.Context) error {
  156. pfds, err := container_model.GetContainerBlobs(ctx, &container_model.BlobSearchOptions{
  157. OwnerID: ownerID,
  158. Image: image,
  159. Digest: string(digest),
  160. })
  161. if err != nil {
  162. return err
  163. }
  164. for _, file := range pfds {
  165. if err := packages_service.DeletePackageFile(ctx, file.File); err != nil {
  166. return err
  167. }
  168. }
  169. return nil
  170. })
  171. }
  172. func digestFromHashSummer(h packages_module.HashSummer) string {
  173. _, _, hashSHA256, _ := h.Sums()
  174. return "sha256:" + hex.EncodeToString(hashSHA256)
  175. }
  176. func digestFromPackageBlob(pb *packages_model.PackageBlob) string {
  177. return "sha256:" + pb.HashSHA256
  178. }