Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

local.go 3.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. // Copyright 2020 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package storage
  4. import (
  5. "context"
  6. "io"
  7. "net/url"
  8. "os"
  9. "path"
  10. "path/filepath"
  11. "strings"
  12. "code.gitea.io/gitea/modules/log"
  13. "code.gitea.io/gitea/modules/util"
  14. )
  15. var _ ObjectStorage = &LocalStorage{}
  16. // LocalStorageType is the type descriptor for local storage
  17. const LocalStorageType Type = "local"
  18. // LocalStorageConfig represents the configuration for a local storage
  19. type LocalStorageConfig struct {
  20. Path string `ini:"PATH"`
  21. TemporaryPath string `ini:"TEMPORARY_PATH"`
  22. }
  23. // LocalStorage represents a local files storage
  24. type LocalStorage struct {
  25. ctx context.Context
  26. dir string
  27. tmpdir string
  28. }
  29. // NewLocalStorage returns a local files
  30. func NewLocalStorage(ctx context.Context, cfg interface{}) (ObjectStorage, error) {
  31. configInterface, err := toConfig(LocalStorageConfig{}, cfg)
  32. if err != nil {
  33. return nil, err
  34. }
  35. config := configInterface.(LocalStorageConfig)
  36. log.Info("Creating new Local Storage at %s", config.Path)
  37. if err := os.MkdirAll(config.Path, os.ModePerm); err != nil {
  38. return nil, err
  39. }
  40. if config.TemporaryPath == "" {
  41. config.TemporaryPath = config.Path + "/tmp"
  42. }
  43. return &LocalStorage{
  44. ctx: ctx,
  45. dir: config.Path,
  46. tmpdir: config.TemporaryPath,
  47. }, nil
  48. }
  49. func (l *LocalStorage) buildLocalPath(p string) string {
  50. return filepath.Join(l.dir, path.Clean("/" + strings.ReplaceAll(p, "\\", "/"))[1:])
  51. }
  52. // Open a file
  53. func (l *LocalStorage) Open(path string) (Object, error) {
  54. return os.Open(l.buildLocalPath(path))
  55. }
  56. // Save a file
  57. func (l *LocalStorage) Save(path string, r io.Reader, size int64) (int64, error) {
  58. p := l.buildLocalPath(path)
  59. if err := os.MkdirAll(filepath.Dir(p), os.ModePerm); err != nil {
  60. return 0, err
  61. }
  62. // Create a temporary file to save to
  63. if err := os.MkdirAll(l.tmpdir, os.ModePerm); err != nil {
  64. return 0, err
  65. }
  66. tmp, err := os.CreateTemp(l.tmpdir, "upload-*")
  67. if err != nil {
  68. return 0, err
  69. }
  70. tmpRemoved := false
  71. defer func() {
  72. if !tmpRemoved {
  73. _ = util.Remove(tmp.Name())
  74. }
  75. }()
  76. n, err := io.Copy(tmp, r)
  77. if err != nil {
  78. return 0, err
  79. }
  80. if err := tmp.Close(); err != nil {
  81. return 0, err
  82. }
  83. if err := util.Rename(tmp.Name(), p); err != nil {
  84. return 0, err
  85. }
  86. // Golang's tmp file (os.CreateTemp) always have 0o600 mode, so we need to change the file to follow the umask (as what Create/MkDir does)
  87. // but we don't want to make these files executable - so ensure that we mask out the executable bits
  88. if err := util.ApplyUmask(p, os.ModePerm&0o666); err != nil {
  89. return 0, err
  90. }
  91. tmpRemoved = true
  92. return n, nil
  93. }
  94. // Stat returns the info of the file
  95. func (l *LocalStorage) Stat(path string) (os.FileInfo, error) {
  96. return os.Stat(l.buildLocalPath(path))
  97. }
  98. // Delete delete a file
  99. func (l *LocalStorage) Delete(path string) error {
  100. return util.Remove(l.buildLocalPath(path))
  101. }
  102. // URL gets the redirect URL to a file
  103. func (l *LocalStorage) URL(path, name string) (*url.URL, error) {
  104. return nil, ErrURLNotSupported
  105. }
  106. // IterateObjects iterates across the objects in the local storage
  107. func (l *LocalStorage) IterateObjects(fn func(path string, obj Object) error) error {
  108. return filepath.WalkDir(l.dir, func(path string, d os.DirEntry, err error) error {
  109. if err != nil {
  110. return err
  111. }
  112. select {
  113. case <-l.ctx.Done():
  114. return l.ctx.Err()
  115. default:
  116. }
  117. if path == l.dir {
  118. return nil
  119. }
  120. if d.IsDir() {
  121. return nil
  122. }
  123. relPath, err := filepath.Rel(l.dir, path)
  124. if err != nil {
  125. return err
  126. }
  127. obj, err := os.Open(path)
  128. if err != nil {
  129. return err
  130. }
  131. defer obj.Close()
  132. return fn(relPath, obj)
  133. })
  134. }
  135. func init() {
  136. RegisterStorageType(LocalStorageType, NewLocalStorage)
  137. }