@@ -19,6 +19,7 @@ package rawdb
19
19
import (
20
20
"errors"
21
21
"fmt"
22
+ "io"
22
23
"math"
23
24
"os"
24
25
"path/filepath"
@@ -73,6 +74,9 @@ type Freezer struct {
73
74
tables map [string ]* freezerTable // Data tables for storing everything
74
75
instanceLock FileLock // File-system lock to prevent double opens
75
76
closeOnce sync.Once
77
+
78
+ datadir string
79
+ createSnapshotMutex sync.Mutex
76
80
}
77
81
78
82
// NewFreezer creates a freezer instance for maintaining immutable ordered
@@ -115,6 +119,7 @@ func NewFreezer(datadir string, namespace string, readonly bool, maxTableSize ui
115
119
readonly : readonly ,
116
120
tables : make (map [string ]* freezerTable ),
117
121
instanceLock : lock ,
122
+ datadir : datadir ,
118
123
}
119
124
120
125
// Create the tables.
@@ -316,6 +321,8 @@ func (f *Freezer) TruncateTail(tail uint64) (uint64, error) {
316
321
317
322
// Sync flushes all data tables to disk.
318
323
func (f * Freezer ) Sync () error {
324
+ f .createSnapshotMutex .Lock ()
325
+ defer f .createSnapshotMutex .Unlock ()
319
326
var errs []error
320
327
for _ , table := range f .tables {
321
328
if err := table .Sync (); err != nil {
@@ -328,6 +335,63 @@ func (f *Freezer) Sync() error {
328
335
return nil
329
336
}
330
337
338
+ func (f * Freezer ) CreateDBSnapshot (dir string ) error {
339
+ f .createSnapshotMutex .Lock ()
340
+ defer f .createSnapshotMutex .Unlock ()
341
+ snapshotDir := filepath .Join (dir , "l2chaindata" , "ancient" , "chain" ) // Format currently used
342
+ if err := os .MkdirAll (snapshotDir , os .ModePerm ); err != nil {
343
+ return fmt .Errorf ("failed to create snapshot of ancient directory: %w" , err )
344
+ }
345
+ // Manually copy contents of ancient to the snapshotDir, createSnapshotMutex makes sure ancient is not updated while copying contents
346
+ err := filepath .Walk (f .datadir , func (path string , info os.FileInfo , err error ) error {
347
+ if err != nil {
348
+ return fmt .Errorf ("error accessing path %s: %w" , path , err )
349
+ }
350
+ relPath , err := filepath .Rel (f .datadir , path )
351
+ if err != nil {
352
+ return fmt .Errorf ("error calculating relative path: %w" , err )
353
+ }
354
+ destPath := filepath .Join (snapshotDir , relPath )
355
+ if info .IsDir () {
356
+ if err := os .MkdirAll (destPath , info .Mode ()); err != nil {
357
+ return fmt .Errorf ("failed to create directory %s: %w" , destPath , err )
358
+ }
359
+ } else {
360
+ if err := copyFile (path , destPath ); err != nil {
361
+ return fmt .Errorf ("failed to copy file %s: %w" , path , err )
362
+ }
363
+ }
364
+ return nil
365
+ })
366
+ return err
367
+ }
368
+
369
+ func copyFile (src , dest string ) error {
370
+ srcFile , err := os .Open (src )
371
+ if err != nil {
372
+ return fmt .Errorf ("failed to open source file: %w" , err )
373
+ }
374
+ defer srcFile .Close ()
375
+ destFile , err := os .Create (dest )
376
+ if err != nil {
377
+ return fmt .Errorf ("failed to create destination file: %w" , err )
378
+ }
379
+ defer destFile .Close ()
380
+ _ , err = io .Copy (destFile , srcFile )
381
+ if err != nil {
382
+ return fmt .Errorf ("failed to copy contents: %w" , err )
383
+ }
384
+ srcInfo , err := os .Stat (src )
385
+ if err != nil {
386
+ return fmt .Errorf ("failed to stat source file: %w" , err )
387
+ }
388
+ err = os .Chmod (dest , srcInfo .Mode ())
389
+ if err != nil {
390
+ return fmt .Errorf ("failed to set permissions: %w" , err )
391
+ }
392
+ return nil
393
+ }
394
+
331
395
// validate checks that every table has the same boundary.
332
396
// Used instead of `repair` in readonly mode.
333
397
func (f * Freezer ) validate () error {
0 commit comments