gitea源码

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. // Copyright 2025 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package tempdir
  4. import (
  5. "os"
  6. "path/filepath"
  7. "time"
  8. "code.gitea.io/gitea/modules/log"
  9. "code.gitea.io/gitea/modules/util"
  10. )
  11. type TempDir struct {
  12. // base is the base directory for temporary files, it must exist before accessing and won't be created automatically.
  13. // for example: base="/system-tmpdir", sub="gitea-tmp"
  14. base, sub string
  15. }
  16. func (td *TempDir) JoinPath(elems ...string) string {
  17. return filepath.Join(append([]string{td.base, td.sub}, elems...)...)
  18. }
  19. // MkdirAllSub works like os.MkdirAll, but the base directory must exist
  20. func (td *TempDir) MkdirAllSub(dir string) (string, error) {
  21. if _, err := os.Stat(td.base); err != nil {
  22. return "", err
  23. }
  24. full := filepath.Join(td.base, td.sub, dir)
  25. if err := os.MkdirAll(full, os.ModePerm); err != nil {
  26. return "", err
  27. }
  28. return full, nil
  29. }
  30. func (td *TempDir) prepareDirWithPattern(elems ...string) (dir, pattern string, err error) {
  31. if _, err = os.Stat(td.base); err != nil {
  32. return "", "", err
  33. }
  34. dir, pattern = filepath.Split(filepath.Join(append([]string{td.base, td.sub}, elems...)...))
  35. if err = os.MkdirAll(dir, os.ModePerm); err != nil {
  36. return "", "", err
  37. }
  38. return dir, pattern, nil
  39. }
  40. // MkdirTempRandom works like os.MkdirTemp, the last path field is the "pattern"
  41. func (td *TempDir) MkdirTempRandom(elems ...string) (string, func(), error) {
  42. dir, pattern, err := td.prepareDirWithPattern(elems...)
  43. if err != nil {
  44. return "", nil, err
  45. }
  46. dir, err = os.MkdirTemp(dir, pattern)
  47. if err != nil {
  48. return "", nil, err
  49. }
  50. return dir, func() {
  51. if err := util.RemoveAll(dir); err != nil {
  52. log.Error("Failed to remove temp directory %s: %v", dir, err)
  53. }
  54. }, nil
  55. }
  56. // CreateTempFileRandom works like os.CreateTemp, the last path field is the "pattern"
  57. func (td *TempDir) CreateTempFileRandom(elems ...string) (*os.File, func(), error) {
  58. dir, pattern, err := td.prepareDirWithPattern(elems...)
  59. if err != nil {
  60. return nil, nil, err
  61. }
  62. f, err := os.CreateTemp(dir, pattern)
  63. if err != nil {
  64. return nil, nil, err
  65. }
  66. filename := f.Name()
  67. return f, func() {
  68. _ = f.Close()
  69. if err := util.Remove(filename); err != nil {
  70. log.Error("Unable to remove temporary file: %s: Error: %v", filename, err)
  71. }
  72. }, err
  73. }
  74. func (td *TempDir) RemoveOutdated(d time.Duration) {
  75. var remove func(path string)
  76. remove = func(path string) {
  77. entries, _ := os.ReadDir(path)
  78. for _, entry := range entries {
  79. full := filepath.Join(path, entry.Name())
  80. if entry.IsDir() {
  81. remove(full)
  82. _ = os.Remove(full)
  83. continue
  84. }
  85. info, err := entry.Info()
  86. if err == nil && time.Since(info.ModTime()) > d {
  87. _ = os.Remove(full)
  88. }
  89. }
  90. }
  91. remove(td.JoinPath(""))
  92. }
  93. // New create a new TempDir instance, "base" must be an existing directory,
  94. // "sub" could be a multi-level directory and will be created if not exist
  95. func New(base, sub string) *TempDir {
  96. return &TempDir{base: base, sub: sub}
  97. }
  98. func OsTempDir(sub string) *TempDir {
  99. return New(os.TempDir(), sub)
  100. }