2
0

btrfs_snapshot_hook.go 2.7 KB

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