btrfs_snapshot_hook.go 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  1. package worker
  2. import (
  3. "fmt"
  4. "os"
  5. "path/filepath"
  6. "github.com/dennwc/btrfs"
  7. )
  8. type btrfsSnapshotHook struct {
  9. provider mirrorProvider
  10. mirrorSnapshotPath string
  11. }
  12. // the user who runs the jobs (typically `tunasync`) should be granted the permission to run btrfs commands
  13. // TODO: check if the filesystem is Btrfs
  14. func newBtrfsSnapshotHook(provider mirrorProvider, snapshotPath string, mirror mirrorConfig) *btrfsSnapshotHook {
  15. mirrorSnapshotPath := mirror.SnapshotPath
  16. if mirrorSnapshotPath == "" {
  17. mirrorSnapshotPath = filepath.Join(snapshotPath, provider.Name())
  18. }
  19. return &btrfsSnapshotHook{
  20. provider: provider,
  21. mirrorSnapshotPath: mirrorSnapshotPath,
  22. }
  23. }
  24. // check if path `snapshotPath/providerName` exists
  25. // Case 1: Not exists => create a new subvolume
  26. // Case 2: Exists as a subvolume => nothing to do
  27. // Case 3: Exists as a directory => error detected
  28. func (h *btrfsSnapshotHook) preJob() error {
  29. path := h.provider.WorkingDir()
  30. if _, err := os.Stat(path); os.IsNotExist(err) {
  31. // create subvolume
  32. err := btrfs.CreateSubVolume(path)
  33. if err != nil {
  34. logger.Errorf("failed to create Btrfs subvolume %s: %s", path, err.Error())
  35. return err
  36. }
  37. logger.Noticef("created new Btrfs subvolume %s", path)
  38. } else {
  39. if is, err := btrfs.IsSubVolume(path); err != nil {
  40. return err
  41. } else if !is {
  42. return fmt.Errorf("path %s exists but isn't a Btrfs subvolume", path)
  43. }
  44. }
  45. return nil
  46. }
  47. func (h *btrfsSnapshotHook) preExec() error {
  48. return nil
  49. }
  50. func (h *btrfsSnapshotHook) postExec() error {
  51. return nil
  52. }
  53. // delete old snapshot if exists, then create a new snapshot
  54. func (h *btrfsSnapshotHook) postSuccess() error {
  55. if _, err := os.Stat(h.mirrorSnapshotPath); !os.IsNotExist(err) {
  56. isSubVol, err := btrfs.IsSubVolume(h.mirrorSnapshotPath)
  57. if err != nil {
  58. return err
  59. } else if !isSubVol {
  60. return fmt.Errorf("path %s exists and isn't a Btrfs snapshot", h.mirrorSnapshotPath)
  61. }
  62. // is old snapshot => delete it
  63. if err := btrfs.DeleteSubVolume(h.mirrorSnapshotPath); err != nil {
  64. logger.Errorf("failed to delete old Btrfs snapshot %s", h.mirrorSnapshotPath)
  65. return err
  66. }
  67. logger.Noticef("deleted old snapshot %s", h.mirrorSnapshotPath)
  68. }
  69. // create a new writable snapshot
  70. // (the snapshot is writable so that it can be deleted easily)
  71. if err := btrfs.SnapshotSubVolume(h.provider.WorkingDir(), h.mirrorSnapshotPath, false); err != nil {
  72. logger.Errorf("failed to create new Btrfs snapshot %s", h.mirrorSnapshotPath)
  73. return err
  74. }
  75. logger.Noticef("created new Btrfs snapshot %s", h.mirrorSnapshotPath)
  76. return nil
  77. }
  78. // keep the old snapshot => nothing to do
  79. func (h *btrfsSnapshotHook) postFail() error {
  80. return nil
  81. }