chanbackup: add new MultiFile struct for storing+updating Multis on disk
In this commit, we add a new MultiFile struct. We'll use this struct in
store the latest multi-channel backup on disk, swap it out atomically,
and finally extract+unpack the contents of the multi-file. The format
that's written to disk is the same as a regular Packed multi. The
contents of this new file are meant to be used to safely implement an
always up to date multi file on disk as a way for users to easily rsync
or fsnotiy (when it changes) the backup state of their channels.
We implement an atomic update and swap in the UpdateAndSwap. The method
uses relies on the underlying file system supporting an atomic rename
syscall. We first make a temporary backup file, write the latest
contents to that, then swap the temp file with the main file using
rename(2). This way, we ensure that we always have a single up to date
file, if the protocol aborts before the rename, then we can detect this,
remove the temp file, and attempt another swap.
2018-12-09 19:06:41 -08:00
|
|
|
package chanbackup
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2024-10-29 11:56:59 +01:00
|
|
|
"io"
|
chanbackup: add new MultiFile struct for storing+updating Multis on disk
In this commit, we add a new MultiFile struct. We'll use this struct in
store the latest multi-channel backup on disk, swap it out atomically,
and finally extract+unpack the contents of the multi-file. The format
that's written to disk is the same as a regular Packed multi. The
contents of this new file are meant to be used to safely implement an
always up to date multi file on disk as a way for users to easily rsync
or fsnotiy (when it changes) the backup state of their channels.
We implement an atomic update and swap in the UpdateAndSwap. The method
uses relies on the underlying file system supporting an atomic rename
syscall. We first make a temporary backup file, write the latest
contents to that, then swap the temp file with the main file using
rename(2). This way, we ensure that we always have a single up to date
file, if the protocol aborts before the rename, then we can detect this,
remove the temp file, and attempt another swap.
2018-12-09 19:06:41 -08:00
|
|
|
"os"
|
|
|
|
"path/filepath"
|
2024-10-29 11:56:59 +01:00
|
|
|
"time"
|
chanbackup: add new MultiFile struct for storing+updating Multis on disk
In this commit, we add a new MultiFile struct. We'll use this struct in
store the latest multi-channel backup on disk, swap it out atomically,
and finally extract+unpack the contents of the multi-file. The format
that's written to disk is the same as a regular Packed multi. The
contents of this new file are meant to be used to safely implement an
always up to date multi file on disk as a way for users to easily rsync
or fsnotiy (when it changes) the backup state of their channels.
We implement an atomic update and swap in the UpdateAndSwap. The method
uses relies on the underlying file system supporting an atomic rename
syscall. We first make a temporary backup file, write the latest
contents to that, then swap the temp file with the main file using
rename(2). This way, we ensure that we always have a single up to date
file, if the protocol aborts before the rename, then we can detect this,
remove the temp file, and attempt another swap.
2018-12-09 19:06:41 -08:00
|
|
|
|
|
|
|
"github.com/lightningnetwork/lnd/keychain"
|
2024-10-29 11:56:59 +01:00
|
|
|
"github.com/lightningnetwork/lnd/lnrpc"
|
chanbackup: add new MultiFile struct for storing+updating Multis on disk
In this commit, we add a new MultiFile struct. We'll use this struct in
store the latest multi-channel backup on disk, swap it out atomically,
and finally extract+unpack the contents of the multi-file. The format
that's written to disk is the same as a regular Packed multi. The
contents of this new file are meant to be used to safely implement an
always up to date multi file on disk as a way for users to easily rsync
or fsnotiy (when it changes) the backup state of their channels.
We implement an atomic update and swap in the UpdateAndSwap. The method
uses relies on the underlying file system supporting an atomic rename
syscall. We first make a temporary backup file, write the latest
contents to that, then swap the temp file with the main file using
rename(2). This way, we ensure that we always have a single up to date
file, if the protocol aborts before the rename, then we can detect this,
remove the temp file, and attempt another swap.
2018-12-09 19:06:41 -08:00
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
// DefaultBackupFileName is the default name of the auto updated static
|
|
|
|
// channel backup fie.
|
|
|
|
DefaultBackupFileName = "channel.backup"
|
|
|
|
|
|
|
|
// DefaultTempBackupFileName is the default name of the temporary SCB
|
|
|
|
// file that we'll use to atomically update the primary back up file
|
|
|
|
// when new channel are detected.
|
|
|
|
DefaultTempBackupFileName = "temp-dont-use.backup"
|
2024-10-29 11:56:59 +01:00
|
|
|
|
|
|
|
// DefaultChanBackupArchiveDirName is the default name of the directory
|
|
|
|
// that we'll use to store old channel backups.
|
|
|
|
DefaultChanBackupArchiveDirName = "chan-backup-archives"
|
chanbackup: add new MultiFile struct for storing+updating Multis on disk
In this commit, we add a new MultiFile struct. We'll use this struct in
store the latest multi-channel backup on disk, swap it out atomically,
and finally extract+unpack the contents of the multi-file. The format
that's written to disk is the same as a regular Packed multi. The
contents of this new file are meant to be used to safely implement an
always up to date multi file on disk as a way for users to easily rsync
or fsnotiy (when it changes) the backup state of their channels.
We implement an atomic update and swap in the UpdateAndSwap. The method
uses relies on the underlying file system supporting an atomic rename
syscall. We first make a temporary backup file, write the latest
contents to that, then swap the temp file with the main file using
rename(2). This way, we ensure that we always have a single up to date
file, if the protocol aborts before the rename, then we can detect this,
remove the temp file, and attempt another swap.
2018-12-09 19:06:41 -08:00
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
// ErrNoBackupFileExists is returned if caller attempts to call
|
|
|
|
// UpdateAndSwap with the file name not set.
|
|
|
|
ErrNoBackupFileExists = fmt.Errorf("back up file name not set")
|
|
|
|
|
|
|
|
// ErrNoTempBackupFile is returned if caller attempts to call
|
|
|
|
// UpdateAndSwap with the temp back up file name not set.
|
|
|
|
ErrNoTempBackupFile = fmt.Errorf("temp backup file not set")
|
|
|
|
)
|
|
|
|
|
|
|
|
// MultiFile represents a file on disk that a caller can use to read the packed
|
|
|
|
// multi backup into an unpacked one, and also atomically update the contents
|
|
|
|
// on disk once new channels have been opened, and old ones closed. This struct
|
|
|
|
// relies on an atomic file rename property which most widely use file systems
|
|
|
|
// have.
|
|
|
|
type MultiFile struct {
|
|
|
|
// fileName is the file name of the main back up file.
|
|
|
|
fileName string
|
|
|
|
|
|
|
|
// tempFileName is the name of the file that we'll use to stage a new
|
|
|
|
// packed multi-chan backup, and the rename to the main back up file.
|
|
|
|
tempFileName string
|
|
|
|
|
|
|
|
// tempFile is an open handle to the temp back up file.
|
|
|
|
tempFile *os.File
|
2024-10-29 11:56:59 +01:00
|
|
|
|
|
|
|
// archiveDir is the directory where we'll store old channel backups.
|
|
|
|
archiveDir string
|
2024-11-01 12:24:04 +01:00
|
|
|
|
|
|
|
// noBackupArchive indicates whether old backups should be deleted
|
|
|
|
// rather than archived.
|
|
|
|
noBackupArchive bool
|
chanbackup: add new MultiFile struct for storing+updating Multis on disk
In this commit, we add a new MultiFile struct. We'll use this struct in
store the latest multi-channel backup on disk, swap it out atomically,
and finally extract+unpack the contents of the multi-file. The format
that's written to disk is the same as a regular Packed multi. The
contents of this new file are meant to be used to safely implement an
always up to date multi file on disk as a way for users to easily rsync
or fsnotiy (when it changes) the backup state of their channels.
We implement an atomic update and swap in the UpdateAndSwap. The method
uses relies on the underlying file system supporting an atomic rename
syscall. We first make a temporary backup file, write the latest
contents to that, then swap the temp file with the main file using
rename(2). This way, we ensure that we always have a single up to date
file, if the protocol aborts before the rename, then we can detect this,
remove the temp file, and attempt another swap.
2018-12-09 19:06:41 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
// NewMultiFile create a new multi-file instance at the target location on the
|
|
|
|
// file system.
|
2024-11-01 12:24:04 +01:00
|
|
|
func NewMultiFile(fileName string, noBackupArchive bool) *MultiFile {
|
chanbackup: add new MultiFile struct for storing+updating Multis on disk
In this commit, we add a new MultiFile struct. We'll use this struct in
store the latest multi-channel backup on disk, swap it out atomically,
and finally extract+unpack the contents of the multi-file. The format
that's written to disk is the same as a regular Packed multi. The
contents of this new file are meant to be used to safely implement an
always up to date multi file on disk as a way for users to easily rsync
or fsnotiy (when it changes) the backup state of their channels.
We implement an atomic update and swap in the UpdateAndSwap. The method
uses relies on the underlying file system supporting an atomic rename
syscall. We first make a temporary backup file, write the latest
contents to that, then swap the temp file with the main file using
rename(2). This way, we ensure that we always have a single up to date
file, if the protocol aborts before the rename, then we can detect this,
remove the temp file, and attempt another swap.
2018-12-09 19:06:41 -08:00
|
|
|
// We'll our temporary backup file in the very same directory as the
|
|
|
|
// main backup file.
|
|
|
|
backupFileDir := filepath.Dir(fileName)
|
|
|
|
tempFileName := filepath.Join(
|
|
|
|
backupFileDir, DefaultTempBackupFileName,
|
|
|
|
)
|
2024-10-29 11:56:59 +01:00
|
|
|
archiveDir := filepath.Join(
|
|
|
|
backupFileDir, DefaultChanBackupArchiveDirName,
|
|
|
|
)
|
chanbackup: add new MultiFile struct for storing+updating Multis on disk
In this commit, we add a new MultiFile struct. We'll use this struct in
store the latest multi-channel backup on disk, swap it out atomically,
and finally extract+unpack the contents of the multi-file. The format
that's written to disk is the same as a regular Packed multi. The
contents of this new file are meant to be used to safely implement an
always up to date multi file on disk as a way for users to easily rsync
or fsnotiy (when it changes) the backup state of their channels.
We implement an atomic update and swap in the UpdateAndSwap. The method
uses relies on the underlying file system supporting an atomic rename
syscall. We first make a temporary backup file, write the latest
contents to that, then swap the temp file with the main file using
rename(2). This way, we ensure that we always have a single up to date
file, if the protocol aborts before the rename, then we can detect this,
remove the temp file, and attempt another swap.
2018-12-09 19:06:41 -08:00
|
|
|
|
|
|
|
return &MultiFile{
|
2024-11-01 12:24:04 +01:00
|
|
|
fileName: fileName,
|
|
|
|
tempFileName: tempFileName,
|
|
|
|
archiveDir: archiveDir,
|
|
|
|
noBackupArchive: noBackupArchive,
|
chanbackup: add new MultiFile struct for storing+updating Multis on disk
In this commit, we add a new MultiFile struct. We'll use this struct in
store the latest multi-channel backup on disk, swap it out atomically,
and finally extract+unpack the contents of the multi-file. The format
that's written to disk is the same as a regular Packed multi. The
contents of this new file are meant to be used to safely implement an
always up to date multi file on disk as a way for users to easily rsync
or fsnotiy (when it changes) the backup state of their channels.
We implement an atomic update and swap in the UpdateAndSwap. The method
uses relies on the underlying file system supporting an atomic rename
syscall. We first make a temporary backup file, write the latest
contents to that, then swap the temp file with the main file using
rename(2). This way, we ensure that we always have a single up to date
file, if the protocol aborts before the rename, then we can detect this,
remove the temp file, and attempt another swap.
2018-12-09 19:06:41 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// UpdateAndSwap will attempt write a new temporary backup file to disk with
|
|
|
|
// the newBackup encoded, then atomically swap (via rename) the old file for
|
2024-11-01 12:24:04 +01:00
|
|
|
// the new file by updating the name of the new file to the old. It also checks
|
|
|
|
// if the old file should be archived first before swapping it.
|
chanbackup: add new MultiFile struct for storing+updating Multis on disk
In this commit, we add a new MultiFile struct. We'll use this struct in
store the latest multi-channel backup on disk, swap it out atomically,
and finally extract+unpack the contents of the multi-file. The format
that's written to disk is the same as a regular Packed multi. The
contents of this new file are meant to be used to safely implement an
always up to date multi file on disk as a way for users to easily rsync
or fsnotiy (when it changes) the backup state of their channels.
We implement an atomic update and swap in the UpdateAndSwap. The method
uses relies on the underlying file system supporting an atomic rename
syscall. We first make a temporary backup file, write the latest
contents to that, then swap the temp file with the main file using
rename(2). This way, we ensure that we always have a single up to date
file, if the protocol aborts before the rename, then we can detect this,
remove the temp file, and attempt another swap.
2018-12-09 19:06:41 -08:00
|
|
|
func (b *MultiFile) UpdateAndSwap(newBackup PackedMulti) error {
|
|
|
|
// If the main backup file isn't set, then we can't proceed.
|
|
|
|
if b.fileName == "" {
|
|
|
|
return ErrNoBackupFileExists
|
|
|
|
}
|
|
|
|
|
2019-02-08 19:22:06 -08:00
|
|
|
log.Infof("Updating backup file at %v", b.fileName)
|
|
|
|
|
chanbackup: add new MultiFile struct for storing+updating Multis on disk
In this commit, we add a new MultiFile struct. We'll use this struct in
store the latest multi-channel backup on disk, swap it out atomically,
and finally extract+unpack the contents of the multi-file. The format
that's written to disk is the same as a regular Packed multi. The
contents of this new file are meant to be used to safely implement an
always up to date multi file on disk as a way for users to easily rsync
or fsnotiy (when it changes) the backup state of their channels.
We implement an atomic update and swap in the UpdateAndSwap. The method
uses relies on the underlying file system supporting an atomic rename
syscall. We first make a temporary backup file, write the latest
contents to that, then swap the temp file with the main file using
rename(2). This way, we ensure that we always have a single up to date
file, if the protocol aborts before the rename, then we can detect this,
remove the temp file, and attempt another swap.
2018-12-09 19:06:41 -08:00
|
|
|
// If the old back up file still exists, then we'll delete it before
|
|
|
|
// proceeding.
|
|
|
|
if _, err := os.Stat(b.tempFileName); err == nil {
|
|
|
|
log.Infof("Found old temp backup @ %v, removing before swap",
|
|
|
|
b.tempFileName)
|
|
|
|
|
|
|
|
err = os.Remove(b.tempFileName)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("unable to remove temp "+
|
|
|
|
"backup file: %v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now that we know the staging area is clear, we'll create the new
|
|
|
|
// temporary back up file.
|
|
|
|
var err error
|
|
|
|
b.tempFile, err = os.Create(b.tempFileName)
|
|
|
|
if err != nil {
|
2024-02-26 11:19:38 +00:00
|
|
|
return fmt.Errorf("unable to create temp file: %w", err)
|
chanbackup: add new MultiFile struct for storing+updating Multis on disk
In this commit, we add a new MultiFile struct. We'll use this struct in
store the latest multi-channel backup on disk, swap it out atomically,
and finally extract+unpack the contents of the multi-file. The format
that's written to disk is the same as a regular Packed multi. The
contents of this new file are meant to be used to safely implement an
always up to date multi file on disk as a way for users to easily rsync
or fsnotiy (when it changes) the backup state of their channels.
We implement an atomic update and swap in the UpdateAndSwap. The method
uses relies on the underlying file system supporting an atomic rename
syscall. We first make a temporary backup file, write the latest
contents to that, then swap the temp file with the main file using
rename(2). This way, we ensure that we always have a single up to date
file, if the protocol aborts before the rename, then we can detect this,
remove the temp file, and attempt another swap.
2018-12-09 19:06:41 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
// With the file created, we'll write the new packed multi backup and
|
|
|
|
// remove the temporary file all together once this method exits.
|
|
|
|
_, err = b.tempFile.Write([]byte(newBackup))
|
|
|
|
if err != nil {
|
2024-02-26 11:19:38 +00:00
|
|
|
return fmt.Errorf("unable to write backup to temp file: %w",
|
|
|
|
err)
|
chanbackup: add new MultiFile struct for storing+updating Multis on disk
In this commit, we add a new MultiFile struct. We'll use this struct in
store the latest multi-channel backup on disk, swap it out atomically,
and finally extract+unpack the contents of the multi-file. The format
that's written to disk is the same as a regular Packed multi. The
contents of this new file are meant to be used to safely implement an
always up to date multi file on disk as a way for users to easily rsync
or fsnotiy (when it changes) the backup state of their channels.
We implement an atomic update and swap in the UpdateAndSwap. The method
uses relies on the underlying file system supporting an atomic rename
syscall. We first make a temporary backup file, write the latest
contents to that, then swap the temp file with the main file using
rename(2). This way, we ensure that we always have a single up to date
file, if the protocol aborts before the rename, then we can detect this,
remove the temp file, and attempt another swap.
2018-12-09 19:06:41 -08:00
|
|
|
}
|
|
|
|
if err := b.tempFile.Sync(); err != nil {
|
2024-02-26 11:19:38 +00:00
|
|
|
return fmt.Errorf("unable to sync temp file: %w", err)
|
chanbackup: add new MultiFile struct for storing+updating Multis on disk
In this commit, we add a new MultiFile struct. We'll use this struct in
store the latest multi-channel backup on disk, swap it out atomically,
and finally extract+unpack the contents of the multi-file. The format
that's written to disk is the same as a regular Packed multi. The
contents of this new file are meant to be used to safely implement an
always up to date multi file on disk as a way for users to easily rsync
or fsnotiy (when it changes) the backup state of their channels.
We implement an atomic update and swap in the UpdateAndSwap. The method
uses relies on the underlying file system supporting an atomic rename
syscall. We first make a temporary backup file, write the latest
contents to that, then swap the temp file with the main file using
rename(2). This way, we ensure that we always have a single up to date
file, if the protocol aborts before the rename, then we can detect this,
remove the temp file, and attempt another swap.
2018-12-09 19:06:41 -08:00
|
|
|
}
|
|
|
|
defer os.Remove(b.tempFileName)
|
|
|
|
|
|
|
|
log.Infof("Swapping old multi backup file from %v to %v",
|
|
|
|
b.tempFileName, b.fileName)
|
|
|
|
|
2019-05-27 15:36:50 -07:00
|
|
|
// Before we rename the swap (atomic name swap), we'll make
|
|
|
|
// sure to close the current file as some OSes don't support
|
|
|
|
// renaming a file that's already open (Windows).
|
|
|
|
if err := b.tempFile.Close(); err != nil {
|
2024-02-26 11:19:38 +00:00
|
|
|
return fmt.Errorf("unable to close file: %w", err)
|
2019-05-27 15:36:50 -07:00
|
|
|
}
|
|
|
|
|
2024-10-29 11:56:59 +01:00
|
|
|
// Archive the old channel backup file before replacing.
|
|
|
|
if err := b.createArchiveFile(); err != nil {
|
|
|
|
return fmt.Errorf("unable to archive old channel "+
|
|
|
|
"backup file: %w", err)
|
|
|
|
}
|
|
|
|
|
chanbackup: add new MultiFile struct for storing+updating Multis on disk
In this commit, we add a new MultiFile struct. We'll use this struct in
store the latest multi-channel backup on disk, swap it out atomically,
and finally extract+unpack the contents of the multi-file. The format
that's written to disk is the same as a regular Packed multi. The
contents of this new file are meant to be used to safely implement an
always up to date multi file on disk as a way for users to easily rsync
or fsnotiy (when it changes) the backup state of their channels.
We implement an atomic update and swap in the UpdateAndSwap. The method
uses relies on the underlying file system supporting an atomic rename
syscall. We first make a temporary backup file, write the latest
contents to that, then swap the temp file with the main file using
rename(2). This way, we ensure that we always have a single up to date
file, if the protocol aborts before the rename, then we can detect this,
remove the temp file, and attempt another swap.
2018-12-09 19:06:41 -08:00
|
|
|
// Finally, we'll attempt to atomically rename the temporary file to
|
|
|
|
// the main back up file. If this succeeds, then we'll only have a
|
|
|
|
// single file on disk once this method exits.
|
|
|
|
return os.Rename(b.tempFileName, b.fileName)
|
|
|
|
}
|
|
|
|
|
|
|
|
// ExtractMulti attempts to extract the packed multi backup we currently point
|
|
|
|
// to into an unpacked version. This method will fail if no backup file
|
|
|
|
// currently exists as the specified location.
|
|
|
|
func (b *MultiFile) ExtractMulti(keyChain keychain.KeyRing) (*Multi, error) {
|
|
|
|
var err error
|
|
|
|
|
2020-07-08 17:37:53 -07:00
|
|
|
// We'll return an error if the main file isn't currently set.
|
|
|
|
if b.fileName == "" {
|
|
|
|
return nil, ErrNoBackupFileExists
|
chanbackup: add new MultiFile struct for storing+updating Multis on disk
In this commit, we add a new MultiFile struct. We'll use this struct in
store the latest multi-channel backup on disk, swap it out atomically,
and finally extract+unpack the contents of the multi-file. The format
that's written to disk is the same as a regular Packed multi. The
contents of this new file are meant to be used to safely implement an
always up to date multi file on disk as a way for users to easily rsync
or fsnotiy (when it changes) the backup state of their channels.
We implement an atomic update and swap in the UpdateAndSwap. The method
uses relies on the underlying file system supporting an atomic rename
syscall. We first make a temporary backup file, write the latest
contents to that, then swap the temp file with the main file using
rename(2). This way, we ensure that we always have a single up to date
file, if the protocol aborts before the rename, then we can detect this,
remove the temp file, and attempt another swap.
2018-12-09 19:06:41 -08:00
|
|
|
}
|
|
|
|
|
2020-07-08 17:37:53 -07:00
|
|
|
// Now that we've confirmed the target file is populated, we'll read
|
|
|
|
// all the contents of the file. This function ensures that file is
|
|
|
|
// always closed, even if we can't read the contents.
|
2023-06-13 21:22:44 +07:00
|
|
|
multiBytes, err := os.ReadFile(b.fileName)
|
chanbackup: add new MultiFile struct for storing+updating Multis on disk
In this commit, we add a new MultiFile struct. We'll use this struct in
store the latest multi-channel backup on disk, swap it out atomically,
and finally extract+unpack the contents of the multi-file. The format
that's written to disk is the same as a regular Packed multi. The
contents of this new file are meant to be used to safely implement an
always up to date multi file on disk as a way for users to easily rsync
or fsnotiy (when it changes) the backup state of their channels.
We implement an atomic update and swap in the UpdateAndSwap. The method
uses relies on the underlying file system supporting an atomic rename
syscall. We first make a temporary backup file, write the latest
contents to that, then swap the temp file with the main file using
rename(2). This way, we ensure that we always have a single up to date
file, if the protocol aborts before the rename, then we can detect this,
remove the temp file, and attempt another swap.
2018-12-09 19:06:41 -08:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Finally, we'll attempt to unpack the file and return the unpack
|
|
|
|
// version to the caller.
|
|
|
|
packedMulti := PackedMulti(multiBytes)
|
|
|
|
return packedMulti.Unpack(keyChain)
|
|
|
|
}
|
2024-10-29 11:56:59 +01:00
|
|
|
|
|
|
|
// createArchiveFile creates an archive file with a timestamped name in the
|
|
|
|
// specified archive directory, and copies the contents of the main backup file
|
|
|
|
// to the new archive file.
|
|
|
|
func (b *MultiFile) createArchiveFile() error {
|
2024-11-01 12:24:04 +01:00
|
|
|
// User can skip archiving of old backup files to save disk space.
|
|
|
|
if b.noBackupArchive {
|
|
|
|
log.Debug("Skipping archive of old backup file as configured")
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check for old channel backup file.
|
2024-10-29 11:56:59 +01:00
|
|
|
oldFileExists := lnrpc.FileExists(b.fileName)
|
|
|
|
if !oldFileExists {
|
|
|
|
log.Debug("No old channel backup file to archive")
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
log.Infof("Archiving old channel backup to %v", b.archiveDir)
|
|
|
|
|
|
|
|
// Generate archive file path with timestamped name.
|
|
|
|
baseFileName := filepath.Base(b.fileName)
|
|
|
|
timestamp := time.Now().Format("2006-01-02-15-04-05")
|
|
|
|
|
|
|
|
archiveFileName := fmt.Sprintf("%s-%s", baseFileName, timestamp)
|
|
|
|
archiveFilePath := filepath.Join(b.archiveDir, archiveFileName)
|
|
|
|
|
|
|
|
oldBackupFile, err := os.Open(b.fileName)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("unable to open old channel backup file: "+
|
|
|
|
"%w", err)
|
|
|
|
}
|
|
|
|
defer func() {
|
|
|
|
err := oldBackupFile.Close()
|
|
|
|
if err != nil {
|
|
|
|
log.Errorf("unable to close old channel backup file: "+
|
|
|
|
"%v", err)
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
// Ensure the archive directory exists. If it doesn't we create it.
|
|
|
|
const archiveDirPermissions = 0o700
|
|
|
|
err = os.MkdirAll(b.archiveDir, archiveDirPermissions)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("unable to create archive directory: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create new archive file.
|
|
|
|
archiveFile, err := os.Create(archiveFilePath)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("unable to create archive file: %w", err)
|
|
|
|
}
|
|
|
|
defer func() {
|
|
|
|
err := archiveFile.Close()
|
|
|
|
if err != nil {
|
|
|
|
log.Errorf("unable to close archive file: %v", err)
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
// Copy contents of old backup to the newly created archive files.
|
|
|
|
_, err = io.Copy(archiveFile, oldBackupFile)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("unable to copy to archive file: %w", err)
|
|
|
|
}
|
|
|
|
err = archiveFile.Sync()
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("unable to sync archive file: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|