diff --git a/snapshots/overlay/overlay.go b/snapshots/overlay/overlay.go index 74bb722f5..247e77675 100644 --- a/snapshots/overlay/overlay.go +++ b/snapshots/overlay/overlay.go @@ -44,6 +44,7 @@ const upperdirKey = "containerd.io/snapshot/overlay.upperdir" type SnapshotterConfig struct { asyncRemove bool upperdirLabel bool + mountOptions []string } // Opt is an option to configure the overlay snapshotter @@ -67,13 +68,21 @@ func WithUpperdirLabel(config *SnapshotterConfig) error { return nil } +// WithMountOptions defines the default mount options used for the overlay mount. +// NOTE: Options are not applied to bind mounts. +func WithMountOptions(options []string) Opt { + return func(config *SnapshotterConfig) error { + config.mountOptions = append(config.mountOptions, options...) + return nil + } +} + type snapshotter struct { root string ms *storage.MetaStore asyncRemove bool upperdirLabel bool - indexOff bool - userxattr bool // whether to enable "userxattr" mount option + options []string } // NewSnapshotter returns a Snapshotter which uses overlayfs. The overlayfs @@ -105,10 +114,20 @@ func NewSnapshotter(root string, opts ...Opt) (snapshots.Snapshotter, error) { if err := os.Mkdir(filepath.Join(root, "snapshots"), 0700); err != nil && !os.IsExist(err) { return nil, err } - // figure out whether "userxattr" option is recognized by the kernel && needed - userxattr, err := overlayutils.NeedsUserXAttr(root) - if err != nil { - logrus.WithError(err).Warnf("cannot detect whether \"userxattr\" option needs to be used, assuming to be %v", userxattr) + + if !hasOption(config.mountOptions, "userxattr", false) { + // figure out whether "userxattr" option is recognized by the kernel && needed + userxattr, err := overlayutils.NeedsUserXAttr(root) + if err != nil { + logrus.WithError(err).Warnf("cannot detect whether \"userxattr\" option needs to be used, assuming to be %v", userxattr) + } + if userxattr { + config.mountOptions = append(config.mountOptions, "userxattr") + } + } + + if !hasOption(config.mountOptions, "index", false) && supportsIndex() { + config.mountOptions = append(config.mountOptions, "index=off") } return &snapshotter{ @@ -116,11 +135,23 @@ func NewSnapshotter(root string, opts ...Opt) (snapshots.Snapshotter, error) { ms: ms, asyncRemove: config.asyncRemove, upperdirLabel: config.upperdirLabel, - indexOff: supportsIndex(), - userxattr: userxattr, + options: config.mountOptions, }, nil } +func hasOption(options []string, key string, hasValue bool) bool { + for _, option := range options { + if hasValue { + if strings.HasPrefix(option, key) && len(option) > len(key) && option[len(key)] == '=' { + return true + } + } else if option == key { + return true + } + } + return false +} + // Stat returns the info for an active or committed snapshot by name or // key. // @@ -453,17 +484,8 @@ func (o *snapshotter) mounts(s storage.Snapshot) []mount.Mount { }, } } - var options []string - - // set index=off when mount overlayfs - if o.indexOff { - options = append(options, "index=off") - } - - if o.userxattr { - options = append(options, "userxattr") - } + options := o.options if s.Kind == snapshots.KindActive { options = append(options, fmt.Sprintf("workdir=%s", o.workPath(s.ID)), diff --git a/snapshots/overlay/overlay_test.go b/snapshots/overlay/overlay_test.go index c0357df41..d7909c433 100644 --- a/snapshots/overlay/overlay_test.go +++ b/snapshots/overlay/overlay_test.go @@ -70,7 +70,7 @@ func TestOverlay(t *testing.T) { testOverlayOverlayRead(t, newSnapshotter) }) t.Run("TestOverlayView", func(t *testing.T) { - testOverlayView(t, newSnapshotter) + testOverlayView(t, newSnapshotterWithOpts(append(opts, WithMountOptions([]string{"volatile"}))...)) }) }) } @@ -329,7 +329,7 @@ func testOverlayView(t *testing.T, newSnapshotter testsuite.SnapshotterFunc) { } supportsIndex := supportsIndex() - expectedOptions := 2 + expectedOptions := 3 if !supportsIndex { expectedOptions-- } @@ -346,13 +346,16 @@ func testOverlayView(t *testing.T, newSnapshotter testsuite.SnapshotterFunc) { } lowers := getParents(ctx, o, root, "/tmp/view2") expected = fmt.Sprintf("lowerdir=%s:%s", lowers[0], lowers[1]) - optIdx := 1 + optIdx := 2 if !supportsIndex { optIdx-- } if userxattr { optIdx++ } + if m.Options[0] != "volatile" { + t.Error("expected option first option to be provided option \"volatile\"") + } if m.Options[optIdx] != expected { t.Errorf("expected option %q but received %q", expected, m.Options[optIdx]) } diff --git a/snapshots/overlay/plugin/plugin.go b/snapshots/overlay/plugin/plugin.go index 0d3633a2e..6c6dce75d 100644 --- a/snapshots/overlay/plugin/plugin.go +++ b/snapshots/overlay/plugin/plugin.go @@ -32,6 +32,9 @@ type Config struct { RootPath string `toml:"root_path"` UpperdirLabel bool `toml:"upperdir_label"` SyncRemove bool `toml:"sync_remove"` + + // MountOptions are options used for the overlay mount (not used on bind mounts) + MountOptions []string `toml:"mount_options"` } func init() { @@ -60,6 +63,10 @@ func init() { oOpts = append(oOpts, overlay.AsynchronousRemove) } + if len(config.MountOptions) > 0 { + oOpts = append(oOpts, overlay.WithMountOptions(config.MountOptions)) + } + ic.Meta.Exports["root"] = root return overlay.NewSnapshotter(root, oOpts...) },