// backup the app data. // This method won't lock the taskMutex. You have to handle it! func (a *App) backup() error { // Don't backup during some special app tasks. if a.task == taskCloneSource || a.task == taskUpdate { return fmt.Errorf("can't backup app '%s' during an update task!", a.name) } // Get the app's base backup folder. backupPath := a.BackupDirectoryPath() // Create the base app backup folder if not present. err := utils.MkDirIfNotExists(backupPath) if err != nil { return fmt.Errorf("failed to backup app '%s': %v", a.name, err) } // Create a new backup directory with the current timestamp. backupPath += "/" + strconv.FormatInt(time.Now().Unix(), 10) // Log log.Infof("creating backup of app '%s': %s", a.name, backupPath) // Create a snapshot of the complete app subvolume. err = btrfs.Snapshot(a.path, backupPath, true) if err != nil { return fmt.Errorf("failed to backup app '%s': %v", a.name, err) } return nil }
// RestoreBackup restores the given app backup. func (a *App) RestoreBackup(timestamp string) (err error) { // Lock the task mutex. // The app should not be started during a backup process. a.taskMutex.Lock() defer a.taskMutex.Unlock() // Abort if any app ask is running. if a.IsTaskRunning() { return fmt.Errorf("the app is running!") } // Create the backup directory path. backupPath := a.BackupDirectoryPath() + "/" + timestamp // Check if the backup exists. if !btrfs.IsSubvolume(backupPath) { return fmt.Errorf("no backup '%s' found!", timestamp) } // Create the apps backup path for the current data. newAppBackupPath := a.BackupDirectoryPath() + "/" + strconv.FormatInt(time.Now().Unix(), 10) // Log log.Infof("restoring backup of app '%s': %s", a.name, timestamp) // Reload the turtlefile and settings on defer. defer func() { err = a.reload() }() // Move the current subvolume to the backup location with the current timestamp. err = os.Rename(a.path, newAppBackupPath) if err != nil { return fmt.Errorf("failed to move apps current subvolume: %v", err) } // Restore the current backup subvolume on failure. defer func() { if err != nil { errR := os.Rename(newAppBackupPath, a.path) if errR != nil { log.Errorf("failed to restore apps current subvolume backup: %v", errR) } else { // Remove the readonly flag again. errR = btrfs.SetSubvolumeReadonly(a.path, false) if errR != nil { log.Errorf("failed to restore apps subvolume flag: %v", errR) } } } }() // Set the readonly flag on the moved subvolume. err = btrfs.SetSubvolumeReadonly(newAppBackupPath, true) if err != nil { return err } // Create a snapshot of the backup and restore it to the app path. err = btrfs.Snapshot(backupPath, a.path, false) if err != nil { return fmt.Errorf("failed to restore app '%s': %v", a.name, err) } return nil }