@@ -19,6 +19,7 @@ package blockfile
1919import (
2020 "context"
2121 "fmt"
22+ "io"
2223 "os"
2324 "path/filepath"
2425
@@ -27,10 +28,11 @@ import (
2728 "github.com/containerd/containerd/plugin"
2829 "github.com/containerd/containerd/snapshots"
2930 "github.com/containerd/containerd/snapshots/storage"
30-
31- "github.com/containerd/continuity/fs"
3231)
3332
33+ // viewHookHelper is only used in test for recover the filesystem.
34+ type viewHookHelper func (backingFile string , fsType string , defaultOpts []string ) error
35+
3436// SnapshotterConfig holds the configurable properties for the blockfile snapshotter
3537type SnapshotterConfig struct {
3638 // recreateScratch is whether scratch should be recreated even
@@ -44,6 +46,17 @@ type SnapshotterConfig struct {
4446
4547 // mountOptions are the base options added to the mount (defaults to ["loop"])
4648 mountOptions []string
49+
50+ // testViewHookHelper is used to fsck or mount with rw to handle
51+ // the recovery. If we mount ro for view snapshot, we might hit
52+ // the issue like
53+ //
54+ // (ext4) INFO: recovery required on readonly filesystem
55+ // (ext4) write access unavailable, cannot proceed (try mounting with noload)
56+ //
57+ // FIXME(fuweid): I don't hit the readonly issue in ssd storage. But it's
58+ // easy to reproduce it in slow-storage.
59+ testViewHookHelper viewHookHelper
4760}
4861
4962// Opt is an option to configure the overlay snapshotter
@@ -55,7 +68,7 @@ func WithScratchFile(src string) Opt {
5568 return func (root string , config * SnapshotterConfig ) {
5669 config .scratchGenerator = func (dst string ) error {
5770 // Copy src to dst
58- if err := fs . CopyFile (dst , src ); err != nil {
71+ if err := copyFileWithSync (dst , src ); err != nil {
5972 return fmt .Errorf ("failed to copy scratch: %w" , err )
6073 }
6174 return nil
@@ -78,12 +91,30 @@ func WithMountOptions(options []string) Opt {
7891
7992}
8093
94+ // WithRecreateScratch is used to determine that scratch should be recreated
95+ // even if already exists.
96+ func WithRecreateScratch (recreate bool ) Opt {
97+ return func (root string , config * SnapshotterConfig ) {
98+ config .recreateScratch = recreate
99+ }
100+ }
101+
102+ // withViewHookHelper introduces hook for preparing snapshot for View. It
103+ // should be used in test only.
104+ func withViewHookHelper (fn viewHookHelper ) Opt {
105+ return func (_ string , config * SnapshotterConfig ) {
106+ config .testViewHookHelper = fn
107+ }
108+ }
109+
81110type snapshotter struct {
82111 root string
83112 scratch string
84113 fsType string
85114 options []string
86115 ms * storage.MetaStore
116+
117+ testViewHookHelper viewHookHelper
87118}
88119
89120// NewSnapshotter returns a Snapshotter which copies layers on the underlying
@@ -140,6 +171,8 @@ func NewSnapshotter(root string, opts ...Opt) (snapshots.Snapshotter, error) {
140171 fsType : config .fsType ,
141172 options : config .mountOptions ,
142173 ms : ms ,
174+
175+ testViewHookHelper : config .testViewHookHelper ,
143176 }, nil
144177}
145178
@@ -343,18 +376,27 @@ func (o *snapshotter) createSnapshot(ctx context.Context, kind snapshots.Kind, k
343376 return fmt .Errorf ("failed to create snapshot: %w" , err )
344377 }
345378
379+ var path string
346380 if len (s .ParentIDs ) == 0 || s .Kind == snapshots .KindActive {
347- path : = o .getBlockFile (s .ID )
381+ path = o .getBlockFile (s .ID )
348382
349383 if len (s .ParentIDs ) > 0 {
350- if err = fs . CopyFile (path , o .getBlockFile (s .ParentIDs [0 ])); err != nil {
384+ if err = copyFileWithSync (path , o .getBlockFile (s .ParentIDs [0 ])); err != nil {
351385 return fmt .Errorf ("copying of parent failed: %w" , err )
352386 }
353387 } else {
354- if err = fs . CopyFile (path , o .scratch ); err != nil {
388+ if err = copyFileWithSync (path , o .scratch ); err != nil {
355389 return fmt .Errorf ("copying of scratch failed: %w" , err )
356390 }
357391 }
392+ } else {
393+ path = o .getBlockFile (s .ParentIDs [0 ])
394+ }
395+
396+ if o .testViewHookHelper != nil {
397+ if err := o .testViewHookHelper (path , o .fsType , o .options ); err != nil {
398+ return fmt .Errorf ("failed to handle the viewHookHelper: %w" , err )
399+ }
358400 }
359401
360402 return nil
@@ -401,3 +443,20 @@ func (o *snapshotter) mounts(s storage.Snapshot) []mount.Mount {
401443func (o * snapshotter ) Close () error {
402444 return o .ms .Close ()
403445}
446+
447+ func copyFileWithSync (target , source string ) error {
448+ src , err := os .Open (source )
449+ if err != nil {
450+ return fmt .Errorf ("failed to open source %s: %w" , source , err )
451+ }
452+ defer src .Close ()
453+ tgt , err := os .Create (target )
454+ if err != nil {
455+ return fmt .Errorf ("failed to open target %s: %w" , target , err )
456+ }
457+ defer tgt .Close ()
458+ defer tgt .Sync ()
459+
460+ _ , err = io .Copy (tgt , src )
461+ return err
462+ }
0 commit comments