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