Unify backup write logic

Use the same backup write helper function for both periodic
background backups and for temporary backups in safeWrite().

Besides just removing code duplication, this brings the advantages
of both together:

- Temporary backups in safeWrite() now use the same atomic mechanism
  when replacing an already existing backup. So that if micro crashes
  in the middle of writing the backup in safeWrite(), this corrupted
  backup will not overwrite a previous good backup.

- Better error handling for periodic backups.
This commit is contained in:
Dmytro Maluka
2025-08-03 18:08:05 +02:00
parent 04b878bc2d
commit a862c9709e
2 changed files with 40 additions and 45 deletions

View File

@@ -92,35 +92,51 @@ func (b *SharedBuffer) keepBackup() bool {
return b.forceKeepBackup || b.Settings["permbackup"].(bool)
}
// Backup saves the current buffer to the backups directory
func (b *SharedBuffer) writeBackup(path string) (string, error) {
backupdir := b.backupDir()
if _, err := os.Stat(backupdir); err != nil {
if !errors.Is(err, fs.ErrNotExist) {
return "", err
}
if err = os.Mkdir(backupdir, os.ModePerm); err != nil {
return "", err
}
}
name := util.DetermineEscapePath(backupdir, path)
// If no existing backup, just write the backup.
if _, err := os.Stat(name); errors.Is(err, fs.ErrNotExist) {
_, err = b.overwriteFile(name)
if err != nil {
os.Remove(name)
}
return name, err
}
// If a backup already exists, replace it atomically.
tmp := util.AppendBackupSuffix(name)
_, err := b.overwriteFile(tmp)
if err != nil {
os.Remove(tmp)
return name, err
}
err = os.Rename(tmp, name)
if err != nil {
os.Remove(tmp)
return name, err
}
return name, nil
}
// Backup saves the buffer to the backups directory
func (b *SharedBuffer) Backup() error {
if !b.Settings["backup"].(bool) || b.Path == "" || b.Type != BTDefault {
return nil
}
backupdir := b.backupDir()
if _, err := os.Stat(backupdir); errors.Is(err, fs.ErrNotExist) {
os.Mkdir(backupdir, os.ModePerm)
}
name := util.DetermineEscapePath(backupdir, b.AbsPath)
if _, err := os.Stat(name); errors.Is(err, fs.ErrNotExist) {
_, err = b.overwriteFile(name)
return err
}
tmp := util.AppendBackupSuffix(name)
_, err := b.overwriteFile(tmp)
if err != nil {
os.Remove(tmp)
return err
}
err = os.Rename(tmp, name)
if err != nil {
os.Remove(tmp)
return err
}
_, err := b.writeBackup(b.AbsPath)
return err
}

View File

@@ -333,27 +333,6 @@ func (b *Buffer) saveToFile(filename string, withSudo bool, autoSave bool) error
return err
}
func (b *SharedBuffer) writeBackup(path string) (string, error) {
backupDir := b.backupDir()
if _, err := os.Stat(backupDir); err != nil {
if !errors.Is(err, fs.ErrNotExist) {
return "", err
}
if err = os.Mkdir(backupDir, os.ModePerm); err != nil {
return "", err
}
}
backupName := util.DetermineEscapePath(backupDir, path)
_, err := b.overwriteFile(backupName)
if err != nil {
os.Remove(backupName)
return "", err
}
return backupName, nil
}
// safeWrite writes the buffer to a file in a "safe" way, preventing loss of the
// contents of the file if it fails to write the new contents.
// This means that the file is not overwritten directly but by writing to the