mirror of
https://github.com/tuna/tunasync.git
synced 2025-04-20 20:22:46 +00:00
91 lines
2.7 KiB
Go
91 lines
2.7 KiB
Go
package worker
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"github.com/dennwc/btrfs"
|
|
)
|
|
|
|
type btrfsSnapshotHook struct {
|
|
provider mirrorProvider
|
|
mirrorSnapshotPath string
|
|
}
|
|
|
|
// the user who runs the jobs (typically `tunasync`) should be granted the permission to run btrfs commands
|
|
// TODO: check if the filesystem is Btrfs
|
|
func newBtrfsSnapshotHook(provider mirrorProvider, snapshotPath string, mirror mirrorConfig) *btrfsSnapshotHook {
|
|
mirrorSnapshotPath := mirror.SnapshotPath
|
|
if mirrorSnapshotPath == "" {
|
|
mirrorSnapshotPath = filepath.Join(snapshotPath, provider.Name())
|
|
}
|
|
return &btrfsSnapshotHook{
|
|
provider: provider,
|
|
mirrorSnapshotPath: mirrorSnapshotPath,
|
|
}
|
|
}
|
|
|
|
// check if path `snapshotPath/providerName` exists
|
|
// Case 1: Not exists => create a new subvolume
|
|
// Case 2: Exists as a subvolume => nothing to do
|
|
// Case 3: Exists as a directory => error detected
|
|
func (h *btrfsSnapshotHook) preJob() error {
|
|
path := h.provider.WorkingDir()
|
|
if _, err := os.Stat(path); os.IsNotExist(err) {
|
|
// create subvolume
|
|
err := btrfs.CreateSubVolume(path)
|
|
if err != nil {
|
|
logger.Errorf("failed to create Btrfs subvolume %s: %s", path, err.Error())
|
|
return err
|
|
}
|
|
logger.Noticef("created new Btrfs subvolume %s", path)
|
|
} else {
|
|
if is, err := btrfs.IsSubVolume(path); err != nil {
|
|
return err
|
|
} else if !is {
|
|
return fmt.Errorf("path %s exists but isn't a Btrfs subvolume", path)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (h *btrfsSnapshotHook) preExec() error {
|
|
return nil
|
|
}
|
|
|
|
func (h *btrfsSnapshotHook) postExec() error {
|
|
return nil
|
|
}
|
|
|
|
// delete old snapshot if exists, then create a new snapshot
|
|
func (h *btrfsSnapshotHook) postSuccess() error {
|
|
if _, err := os.Stat(h.mirrorSnapshotPath); !os.IsNotExist(err) {
|
|
isSubVol, err := btrfs.IsSubVolume(h.mirrorSnapshotPath)
|
|
if err != nil {
|
|
return err
|
|
} else if !isSubVol {
|
|
return fmt.Errorf("path %s exists and isn't a Btrfs snapshot", h.mirrorSnapshotPath)
|
|
}
|
|
// is old snapshot => delete it
|
|
if err := btrfs.DeleteSubVolume(h.mirrorSnapshotPath); err != nil {
|
|
logger.Errorf("failed to delete old Btrfs snapshot %s", h.mirrorSnapshotPath)
|
|
return err
|
|
}
|
|
logger.Noticef("deleted old snapshot %s", h.mirrorSnapshotPath)
|
|
}
|
|
// create a new writable snapshot
|
|
// (the snapshot is writable so that it can be deleted easily)
|
|
if err := btrfs.SnapshotSubVolume(h.provider.WorkingDir(), h.mirrorSnapshotPath, false); err != nil {
|
|
logger.Errorf("failed to create new Btrfs snapshot %s", h.mirrorSnapshotPath)
|
|
return err
|
|
}
|
|
logger.Noticef("created new Btrfs snapshot %s", h.mirrorSnapshotPath)
|
|
return nil
|
|
}
|
|
|
|
// keep the old snapshot => nothing to do
|
|
func (h *btrfsSnapshotHook) postFail() error {
|
|
return nil
|
|
}
|