From a862c9709e0c328e7f754feae650dc98033ea71e Mon Sep 17 00:00:00 2001 From: Dmytro Maluka Date: Sun, 3 Aug 2025 18:08:05 +0200 Subject: [PATCH] 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. --- internal/buffer/backup.go | 64 ++++++++++++++++++++++++--------------- internal/buffer/save.go | 21 ------------- 2 files changed, 40 insertions(+), 45 deletions(-) diff --git a/internal/buffer/backup.go b/internal/buffer/backup.go index 9470c663..e93cb069 100644 --- a/internal/buffer/backup.go +++ b/internal/buffer/backup.go @@ -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 } diff --git a/internal/buffer/save.go b/internal/buffer/save.go index d2cd3533..7d943929 100644 --- a/internal/buffer/save.go +++ b/internal/buffer/save.go @@ -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