@@ -2,8 +2,10 @@ package chanbackup
2
2
3
3
import (
4
4
"fmt"
5
+ "io"
5
6
"os"
6
7
"path/filepath"
8
+ "time"
7
9
8
10
"github.com/lightningnetwork/lnd/keychain"
9
11
)
@@ -17,6 +19,10 @@ const (
17
19
// file that we'll use to atomically update the primary back up file
18
20
// when new channel are detected.
19
21
DefaultTempBackupFileName = "temp-dont-use.backup"
22
+
23
+ // DefaultChanBackupDirName is the default name of the directory that
24
+ // we'll use to store old channel backups.
25
+ DefaultChanBackupArchiveDirName = "channelarchives"
20
26
)
21
27
22
28
var (
@@ -44,6 +50,9 @@ type MultiFile struct {
44
50
45
51
// tempFile is an open handle to the temp back up file.
46
52
tempFile * os.File
53
+
54
+ // archiveDir is the directory where we'll store old channel backups.
55
+ archiveDir string
47
56
}
48
57
49
58
// NewMultiFile create a new multi-file instance at the target location on the
@@ -56,10 +65,14 @@ func NewMultiFile(fileName string) *MultiFile {
56
65
tempFileName := filepath .Join (
57
66
backupFileDir , DefaultTempBackupFileName ,
58
67
)
68
+ archiveDir := filepath .Join (
69
+ backupFileDir , DefaultChanBackupArchiveDirName ,
70
+ )
59
71
60
72
return & MultiFile {
61
73
fileName : fileName ,
62
74
tempFileName : tempFileName ,
75
+ archiveDir : archiveDir ,
63
76
}
64
77
}
65
78
@@ -117,6 +130,19 @@ func (b *MultiFile) UpdateAndSwap(newBackup PackedMulti) error {
117
130
return fmt .Errorf ("unable to close file: %w" , err )
118
131
}
119
132
133
+ // We check if the main backup file exists, if it does we archive it
134
+ // before replacing it with the new backup.
135
+ if _ , err := os .Stat (b .fileName ); err == nil {
136
+ log .Infof ("Archiving old backup file at %v" , b .fileName )
137
+
138
+ if err := createArchiveFile (
139
+ b .archiveDir , b .fileName ,
140
+ ); err != nil {
141
+ return fmt .Errorf ("unable to archive old backup file:" +
142
+ " %w" , err )
143
+ }
144
+ }
145
+
120
146
// Finally, we'll attempt to atomically rename the temporary file to
121
147
// the main back up file. If this succeeds, then we'll only have a
122
148
// single file on disk once this method exits.
@@ -147,3 +173,48 @@ func (b *MultiFile) ExtractMulti(keyChain keychain.KeyRing) (*Multi, error) {
147
173
packedMulti := PackedMulti (multiBytes )
148
174
return packedMulti .Unpack (keyChain )
149
175
}
176
+
177
+ func createArchiveFile (archiveDir string , fileName string ) error {
178
+ // Generate archive file path with timestamped name.
179
+ baseFileName := filepath .Base (fileName )
180
+ timestamp := time .Now ().Format ("2006-01-02-15-04-05" )
181
+
182
+ archiveFileName := fmt .Sprintf ("%s-%s" , baseFileName , timestamp )
183
+ archiveFilePath := filepath .Join (archiveDir , archiveFileName )
184
+
185
+ oldBackupFile , err := os .Open (fileName )
186
+ if err != nil {
187
+ return fmt .Errorf ("unable to open old backup file: %w" , err )
188
+ }
189
+ defer func () {
190
+ if cerr := oldBackupFile .Close (); cerr != nil && err == nil {
191
+ err = fmt .Errorf ("error closing old backup file: %w" ,
192
+ cerr )
193
+ }
194
+ }()
195
+
196
+ // Ensure the archive directory exists. If it doesn't we create it.
197
+ const archiveDirPermissions = 0o755
198
+ err = os .MkdirAll (archiveDir , archiveDirPermissions )
199
+ if err != nil {
200
+ return fmt .Errorf ("unable to create archive directory: %w" , err )
201
+ }
202
+
203
+ // Create new archive file.
204
+ archiveFile , err := os .Create (archiveFilePath )
205
+ if err != nil {
206
+ return fmt .Errorf ("unable to create archive file: %w" , err )
207
+ }
208
+ defer func () {
209
+ if cerr := archiveFile .Close (); cerr != nil && err == nil {
210
+ err = fmt .Errorf ("error closing archive file: %w" , cerr )
211
+ }
212
+ }()
213
+
214
+ // Copy contents of old backup to the newly created archive file.
215
+ if _ , err := io .Copy (archiveFile , oldBackupFile ); err != nil {
216
+ return fmt .Errorf ("error copying to archive file: %w" , err )
217
+ }
218
+
219
+ return nil
220
+ }
0 commit comments