simple.go 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. // Copyright (C) 2014 Jakob Borg and Contributors (see the CONTRIBUTORS file).
  2. // All rights reserved. Use of this source code is governed by an MIT-style
  3. // license that can be found in the LICENSE file.
  4. package versioner
  5. import (
  6. "os"
  7. "path/filepath"
  8. "sort"
  9. "strconv"
  10. "github.com/syncthing/syncthing/internal/osutil"
  11. )
  12. func init() {
  13. // Register the constructor for this type of versioner with the name "simple"
  14. Factories["simple"] = NewSimple
  15. }
  16. // The type holds our configuration
  17. type Simple struct {
  18. keep int
  19. repoPath string
  20. }
  21. // The constructor function takes a map of parameters and creates the type.
  22. func NewSimple(repoID, repoPath string, params map[string]string) Versioner {
  23. keep, err := strconv.Atoi(params["keep"])
  24. if err != nil {
  25. keep = 5 // A reasonable default
  26. }
  27. s := Simple{
  28. keep: keep,
  29. repoPath: repoPath,
  30. }
  31. if debug {
  32. l.Debugf("instantiated %#v", s)
  33. }
  34. return s
  35. }
  36. // Move away the named file to a version archive. If this function returns
  37. // nil, the named file does not exist any more (has been archived).
  38. func (v Simple) Archive(filePath string) error {
  39. fileInfo, err := os.Stat(filePath)
  40. if err != nil {
  41. if os.IsNotExist(err) {
  42. if debug {
  43. l.Debugln("not archiving nonexistent file", filePath)
  44. }
  45. return nil
  46. } else {
  47. return err
  48. }
  49. }
  50. versionsDir := filepath.Join(v.repoPath, ".stversions")
  51. _, err = os.Stat(versionsDir)
  52. if err != nil {
  53. if os.IsNotExist(err) {
  54. if debug {
  55. l.Debugln("creating versions dir", versionsDir)
  56. }
  57. os.MkdirAll(versionsDir, 0755)
  58. osutil.HideFile(versionsDir)
  59. } else {
  60. return err
  61. }
  62. }
  63. if debug {
  64. l.Debugln("archiving", filePath)
  65. }
  66. file := filepath.Base(filePath)
  67. inRepoPath, err := filepath.Rel(v.repoPath, filepath.Dir(filePath))
  68. if err != nil {
  69. return err
  70. }
  71. dir := filepath.Join(versionsDir, inRepoPath)
  72. err = os.MkdirAll(dir, 0755)
  73. if err != nil && !os.IsExist(err) {
  74. return err
  75. }
  76. ver := file + "~" + fileInfo.ModTime().Format("20060102-150405")
  77. dst := filepath.Join(dir, ver)
  78. if debug {
  79. l.Debugln("moving to", dst)
  80. }
  81. err = osutil.Rename(filePath, dst)
  82. if err != nil {
  83. return err
  84. }
  85. versions, err := filepath.Glob(filepath.Join(dir, file+"~[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]-[0-9][0-9][0-9][0-9][0-9][0-9]"))
  86. if err != nil {
  87. l.Warnln("globbing:", err)
  88. return nil
  89. }
  90. if len(versions) > v.keep {
  91. sort.Strings(versions)
  92. for _, toRemove := range versions[:len(versions)-v.keep] {
  93. if debug {
  94. l.Debugln("cleaning out", toRemove)
  95. }
  96. err = os.Remove(toRemove)
  97. if err != nil {
  98. l.Warnln("removing old version:", err)
  99. }
  100. }
  101. }
  102. return nil
  103. }