Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. // Copyright 2020 The Gitea Authors. All rights reserved.
  2. // Use of this source code is governed by a MIT-style
  3. // license that can be found in the LICENSE file.
  4. package models
  5. import (
  6. "crypto/sha256"
  7. "encoding/hex"
  8. "errors"
  9. "fmt"
  10. "io"
  11. "code.gitea.io/gitea/modules/timeutil"
  12. "xorm.io/builder"
  13. )
  14. // LFSMetaObject stores metadata for LFS tracked files.
  15. type LFSMetaObject struct {
  16. ID int64 `xorm:"pk autoincr"`
  17. Oid string `xorm:"UNIQUE(s) INDEX NOT NULL"`
  18. Size int64 `xorm:"NOT NULL"`
  19. RepositoryID int64 `xorm:"UNIQUE(s) INDEX NOT NULL"`
  20. Existing bool `xorm:"-"`
  21. CreatedUnix timeutil.TimeStamp `xorm:"created"`
  22. }
  23. // Pointer returns the string representation of an LFS pointer file
  24. func (m *LFSMetaObject) Pointer() string {
  25. return fmt.Sprintf("%s\n%s%s\nsize %d\n", LFSMetaFileIdentifier, LFSMetaFileOidPrefix, m.Oid, m.Size)
  26. }
  27. // LFSTokenResponse defines the JSON structure in which the JWT token is stored.
  28. // This structure is fetched via SSH and passed by the Git LFS client to the server
  29. // endpoint for authorization.
  30. type LFSTokenResponse struct {
  31. Header map[string]string `json:"header"`
  32. Href string `json:"href"`
  33. }
  34. var (
  35. // ErrLFSObjectNotExist is returned from lfs models functions in order
  36. // to differentiate between database and missing object errors.
  37. ErrLFSObjectNotExist = errors.New("LFS Meta object does not exist")
  38. )
  39. const (
  40. // LFSMetaFileIdentifier is the string appearing at the first line of LFS pointer files.
  41. // https://github.com/git-lfs/git-lfs/blob/master/docs/spec.md
  42. LFSMetaFileIdentifier = "version https://git-lfs.github.com/spec/v1"
  43. // LFSMetaFileOidPrefix appears in LFS pointer files on a line before the sha256 hash.
  44. LFSMetaFileOidPrefix = "oid sha256:"
  45. )
  46. // NewLFSMetaObject stores a given populated LFSMetaObject structure in the database
  47. // if it is not already present.
  48. func NewLFSMetaObject(m *LFSMetaObject) (*LFSMetaObject, error) {
  49. var err error
  50. sess := x.NewSession()
  51. defer sess.Close()
  52. if err = sess.Begin(); err != nil {
  53. return nil, err
  54. }
  55. has, err := sess.Get(m)
  56. if err != nil {
  57. return nil, err
  58. }
  59. if has {
  60. m.Existing = true
  61. return m, sess.Commit()
  62. }
  63. if _, err = sess.Insert(m); err != nil {
  64. return nil, err
  65. }
  66. return m, sess.Commit()
  67. }
  68. // GenerateLFSOid generates a Sha256Sum to represent an oid for arbitrary content
  69. func GenerateLFSOid(content io.Reader) (string, error) {
  70. h := sha256.New()
  71. if _, err := io.Copy(h, content); err != nil {
  72. return "", err
  73. }
  74. sum := h.Sum(nil)
  75. return hex.EncodeToString(sum), nil
  76. }
  77. // GetLFSMetaObjectByOid selects a LFSMetaObject entry from database by its OID.
  78. // It may return ErrLFSObjectNotExist or a database error. If the error is nil,
  79. // the returned pointer is a valid LFSMetaObject.
  80. func (repo *Repository) GetLFSMetaObjectByOid(oid string) (*LFSMetaObject, error) {
  81. if len(oid) == 0 {
  82. return nil, ErrLFSObjectNotExist
  83. }
  84. m := &LFSMetaObject{Oid: oid, RepositoryID: repo.ID}
  85. has, err := x.Get(m)
  86. if err != nil {
  87. return nil, err
  88. } else if !has {
  89. return nil, ErrLFSObjectNotExist
  90. }
  91. return m, nil
  92. }
  93. // RemoveLFSMetaObjectByOid removes a LFSMetaObject entry from database by its OID.
  94. // It may return ErrLFSObjectNotExist or a database error.
  95. func (repo *Repository) RemoveLFSMetaObjectByOid(oid string) (int64, error) {
  96. if len(oid) == 0 {
  97. return 0, ErrLFSObjectNotExist
  98. }
  99. sess := x.NewSession()
  100. defer sess.Close()
  101. if err := sess.Begin(); err != nil {
  102. return -1, err
  103. }
  104. m := &LFSMetaObject{Oid: oid, RepositoryID: repo.ID}
  105. if _, err := sess.Delete(m); err != nil {
  106. return -1, err
  107. }
  108. count, err := sess.Count(&LFSMetaObject{Oid: oid})
  109. if err != nil {
  110. return count, err
  111. }
  112. return count, sess.Commit()
  113. }
  114. // GetLFSMetaObjects returns all LFSMetaObjects associated with a repository
  115. func (repo *Repository) GetLFSMetaObjects(page, pageSize int) ([]*LFSMetaObject, error) {
  116. sess := x.NewSession()
  117. defer sess.Close()
  118. if page >= 0 && pageSize > 0 {
  119. start := 0
  120. if page > 0 {
  121. start = (page - 1) * pageSize
  122. }
  123. sess.Limit(pageSize, start)
  124. }
  125. lfsObjects := make([]*LFSMetaObject, 0, pageSize)
  126. return lfsObjects, sess.Find(&lfsObjects, &LFSMetaObject{RepositoryID: repo.ID})
  127. }
  128. // CountLFSMetaObjects returns a count of all LFSMetaObjects associated with a repository
  129. func (repo *Repository) CountLFSMetaObjects() (int64, error) {
  130. return x.Count(&LFSMetaObject{RepositoryID: repo.ID})
  131. }
  132. // LFSObjectAccessible checks if a provided Oid is accessible to the user
  133. func LFSObjectAccessible(user *User, oid string) (bool, error) {
  134. if user.IsAdmin {
  135. count, err := x.Count(&LFSMetaObject{Oid: oid})
  136. return (count > 0), err
  137. }
  138. cond := accessibleRepositoryCondition(user)
  139. count, err := x.Where(cond).Join("INNER", "repository", "`lfs_meta_object`.repository_id = `repository`.id").Count(&LFSMetaObject{Oid: oid})
  140. return (count > 0), err
  141. }
  142. // LFSAutoAssociate auto associates accessible LFSMetaObjects
  143. func LFSAutoAssociate(metas []*LFSMetaObject, user *User, repoID int64) error {
  144. sess := x.NewSession()
  145. defer sess.Close()
  146. if err := sess.Begin(); err != nil {
  147. return err
  148. }
  149. oids := make([]interface{}, len(metas))
  150. oidMap := make(map[string]*LFSMetaObject, len(metas))
  151. for i, meta := range metas {
  152. oids[i] = meta.Oid
  153. oidMap[meta.Oid] = meta
  154. }
  155. cond := builder.NewCond()
  156. if !user.IsAdmin {
  157. cond = builder.In("`lfs_meta_object`.repository_id",
  158. builder.Select("`repository`.id").From("repository").Where(accessibleRepositoryCondition(user)))
  159. }
  160. newMetas := make([]*LFSMetaObject, 0, len(metas))
  161. if err := sess.Cols("oid").Where(cond).In("oid", oids...).GroupBy("oid").Find(&newMetas); err != nil {
  162. return err
  163. }
  164. for i := range newMetas {
  165. newMetas[i].Size = oidMap[newMetas[i].Oid].Size
  166. newMetas[i].RepositoryID = repoID
  167. }
  168. if _, err := sess.InsertMulti(newMetas); err != nil {
  169. return err
  170. }
  171. return sess.Commit()
  172. }