From 961178f0e566f932e79f5859c703e93fae9efd7c Mon Sep 17 00:00:00 2001 From: Wayne Mesard Date: Mon, 22 Aug 2022 16:32:23 +0000 Subject: [PATCH] Use either "trusted.overlay.opaque" or "user.overlay.opaque". This is a cherrypick of https://github.com/containerd/stargz-snapshotter/pull/681 Signed-off-by: Wayne Mesard --- fs/fs.go | 15 +++++++++++---- fs/layer/layer.go | 7 +++++-- fs/layer/node.go | 44 +++++++++++++++++++++++++++++++------------- fs/layer/testutil.go | 30 ++++++++++++++++++++---------- service/service.go | 12 ++++++++++-- 5 files changed, 77 insertions(+), 31 deletions(-) diff --git a/fs/fs.go b/fs/fs.go index f01426709..1c12a2a07 100644 --- a/fs/fs.go +++ b/fs/fs.go @@ -82,9 +82,10 @@ const ( type Option func(*options) type options struct { - getSources source.GetSources - resolveHandlers map[string]remote.Handler - metadataStore metadata.Store + getSources source.GetSources + resolveHandlers map[string]remote.Handler + metadataStore metadata.Store + overlayOpaqueType layer.OverlayOpaqueType } func WithGetSources(s source.GetSources) Option { @@ -108,6 +109,12 @@ func WithMetadataStore(metadataStore metadata.Store) Option { } } +func WithOverlayOpaqueType(overlayOpaqueType layer.OverlayOpaqueType) Option { + return func(opts *options) { + opts.overlayOpaqueType = overlayOpaqueType + } +} + func NewFilesystem(root string, cfg config.Config, opts ...Option) (_ snapshot.FileSystem, err error) { var fsOpts options for _, o := range opts { @@ -143,7 +150,7 @@ func NewFilesystem(root string, cfg config.Config, opts ...Option) (_ snapshot.F } tm := task.NewBackgroundTaskManager(maxConcurrency, 5*time.Second) - r, err := layer.NewResolver(root, tm, cfg, fsOpts.resolveHandlers, metadataStore, store) + r, err := layer.NewResolver(root, tm, cfg, fsOpts.resolveHandlers, metadataStore, store, fsOpts.overlayOpaqueType) if err != nil { return nil, errors.Wrapf(err, "failed to setup resolver") } diff --git a/fs/layer/layer.go b/fs/layer/layer.go index b06a195de..470eb4229 100644 --- a/fs/layer/layer.go +++ b/fs/layer/layer.go @@ -132,10 +132,12 @@ type Resolver struct { config config.Config metadataStore metadata.Store artifactStore content.Storage + overlayOpaqueType OverlayOpaqueType } // NewResolver returns a new layer resolver. -func NewResolver(root string, backgroundTaskManager *task.BackgroundTaskManager, cfg config.Config, resolveHandlers map[string]remote.Handler, metadataStore metadata.Store, artifactStore content.Storage) (*Resolver, error) { +func NewResolver(root string, backgroundTaskManager *task.BackgroundTaskManager, cfg config.Config, resolveHandlers map[string]remote.Handler, + metadataStore metadata.Store, artifactStore content.Storage, overlayOpaqueType OverlayOpaqueType) (*Resolver, error) { resolveResultEntry := cfg.ResolveResultEntry if resolveResultEntry == 0 { resolveResultEntry = defaultResolveResultEntry @@ -177,6 +179,7 @@ func NewResolver(root string, backgroundTaskManager *task.BackgroundTaskManager, resolveLock: new(namedmutex.NamedMutex), metadataStore: metadataStore, artifactStore: artifactStore, + overlayOpaqueType: overlayOpaqueType, }, nil } @@ -521,7 +524,7 @@ func (l *layer) RootNode(baseInode uint32) (fusefs.InodeEmbedder, error) { if l.r == nil { return nil, fmt.Errorf("layer hasn't been verified yet") } - return newNode(l.desc.Digest, l.r, l.blob, baseInode) + return newNode(l.desc.Digest, l.r, l.blob, baseInode, l.resolver.overlayOpaqueType) } func (l *layer) ReadAt(p []byte, offset int64, opts ...remote.Option) (int, error) { diff --git a/fs/layer/node.go b/fs/layer/node.go index 43682806f..8ed3a5be4 100644 --- a/fs/layer/node.go +++ b/fs/layer/node.go @@ -74,19 +74,36 @@ const ( stateDirMode = syscall.S_IFDIR | 0500 // dr-x------ ) -var opaqueXattrs = []string{"trusted.overlay.opaque", "user.overlay.opaque"} +type OverlayOpaqueType int -func newNode(layerDgst digest.Digest, r reader.Reader, blob remote.Blob, baseInode uint32) (fusefs.InodeEmbedder, error) { +const ( + OverlayOpaqueAll OverlayOpaqueType = iota + OverlayOpaqueTrusted + OverlayOpaqueUser +) + +var opaqueXattrs = map[OverlayOpaqueType][]string{ + OverlayOpaqueAll: {"trusted.overlay.opaque", "user.overlay.opaque"}, + OverlayOpaqueTrusted: {"trusted.overlay.opaque"}, + OverlayOpaqueUser: {"user.overlay.opaque"}, +} + +func newNode(layerDgst digest.Digest, r reader.Reader, blob remote.Blob, baseInode uint32, opaque OverlayOpaqueType) (fusefs.InodeEmbedder, error) { rootID := r.Metadata().RootID() rootAttr, err := r.Metadata().GetAttr(rootID) if err != nil { return nil, err } + opq, ok := opaqueXattrs[opaque] + if !ok { + return nil, fmt.Errorf("unknown overlay opaque type") + } ffs := &fs{ - r: r, - layerDigest: layerDgst, - baseInode: baseInode, - rootID: rootID, + r: r, + layerDigest: layerDgst, + baseInode: baseInode, + rootID: rootID, + opaqueXattrs: opq, } ffs.s = ffs.newState(layerDgst, blob) return &node{ @@ -98,11 +115,12 @@ func newNode(layerDgst digest.Digest, r reader.Reader, blob remote.Blob, baseIno // fs contains global metadata used by nodes type fs struct { - r reader.Reader - s *state - layerDigest digest.Digest - baseInode uint32 - rootID uint32 + r reader.Reader + s *state + layerDigest digest.Digest + baseInode uint32 + rootID uint32 + opaqueXattrs []string } func (fs *fs) inodeOfState() uint64 { @@ -338,7 +356,7 @@ var _ = (fusefs.NodeGetxattrer)((*node)(nil)) func (n *node) Getxattr(ctx context.Context, attr string, dest []byte) (uint32, syscall.Errno) { ent := n.attr opq := n.isOpaque() - for _, opaqueXattr := range opaqueXattrs { + for _, opaqueXattr := range n.fs.opaqueXattrs { if attr == opaqueXattr && opq { // This node is an opaque directory so give overlayfs-compliant indicator. if len(dest) < len(opaqueXattrValue) { @@ -364,7 +382,7 @@ func (n *node) Listxattr(ctx context.Context, dest []byte) (uint32, syscall.Errn var attrs []byte if opq { // This node is an opaque directory so add overlayfs-compliant indicator. - for _, opaqueXattr := range opaqueXattrs { + for _, opaqueXattr := range n.fs.opaqueXattrs { attrs = append(attrs, []byte(opaqueXattr+"\x00")...) } } diff --git a/fs/layer/testutil.go b/fs/layer/testutil.go index 39a7cefc3..4c890fe1c 100644 --- a/fs/layer/testutil.go +++ b/fs/layer/testutil.go @@ -183,7 +183,7 @@ func makeNodeReader(t *testing.T, contents []byte, spanSize int64, factory metad t.Fatalf("failed to make new reader: %v", err) } r := vr.GetReader() - rootNode := getRootNode(t, r) + rootNode := getRootNode(t, r, OverlayOpaqueAll) var eo fuse.EntryOut inode, errno := rootNode.Lookup(context.Background(), testName, &eo) if errno != 0 { @@ -199,6 +199,19 @@ func makeNodeReader(t *testing.T, contents []byte, spanSize int64, factory metad } func testExistence(t *testing.T, factory metadata.Store) { + for _, o := range []OverlayOpaqueType{OverlayOpaqueAll, OverlayOpaqueTrusted, OverlayOpaqueUser} { + testExistenceWithOpaque(t, factory, o) + } +} + +func testExistenceWithOpaque(t *testing.T, factory metadata.Store, opaque OverlayOpaqueType) { + hasOpaque := func(entry string) check { + return func(t *testing.T, root *node) { + for _, k := range opaqueXattrs[opaque] { + hasNodeXattrs(entry, k, opaqueXattrValue)(t, root) + } + } + } tests := []struct { name string in []testutil.TarEntry @@ -235,8 +248,7 @@ func testExistence(t *testing.T, factory metadata.Store) { testutil.File("foo/.wh..wh..opq", ""), }, want: []check{ - hasNodeXattrs("foo/", opaqueXattrs[0], opaqueXattrValue), - hasNodeXattrs("foo/", opaqueXattrs[1], opaqueXattrValue), + hasOpaque("foo/"), fileNotExist("foo/.wh..wh..opq"), }, }, @@ -248,8 +260,7 @@ func testExistence(t *testing.T, factory metadata.Store) { testutil.File("foo/bar.txt", "test"), }, want: []check{ - hasNodeXattrs("foo/", opaqueXattrs[0], opaqueXattrValue), - hasNodeXattrs("foo/", opaqueXattrs[1], opaqueXattrValue), + hasOpaque("foo/"), hasFileDigest("foo/bar.txt", digestFor("test")), fileNotExist("foo/.wh..wh..opq"), }, @@ -261,8 +272,7 @@ func testExistence(t *testing.T, factory metadata.Store) { testutil.File("foo/.wh..wh..opq", ""), }, want: []check{ - hasNodeXattrs("foo/", opaqueXattrs[0], opaqueXattrValue), - hasNodeXattrs("foo/", opaqueXattrs[1], opaqueXattrValue), + hasOpaque("foo/"), hasNodeXattrs("foo/", "SCHILY.xattr.foo", "bar"), fileNotExist("foo/.wh..wh..opq"), }, @@ -326,7 +336,7 @@ func testExistence(t *testing.T, factory metadata.Store) { } r := vr.GetReader() defer r.Close() - rootNode := getRootNode(t, r) + rootNode := getRootNode(t, r, opaque) for _, want := range tt.want { want(t, rootNode) } @@ -335,8 +345,8 @@ func testExistence(t *testing.T, factory metadata.Store) { } } -func getRootNode(t *testing.T, r reader.Reader) *node { - rootNode, err := newNode(testStateLayerDigest, &testReader{r}, &testBlobState{10, 5}, 100) +func getRootNode(t *testing.T, r reader.Reader, opaque OverlayOpaqueType) *node { + rootNode, err := newNode(testStateLayerDigest, &testReader{r}, &testBlobState{10, 5}, 100, opaque) if err != nil { t.Fatalf("failed to get root node: %v", err) } diff --git a/service/service.go b/service/service.go index 340c3ccce..16279524f 100644 --- a/service/service.go +++ b/service/service.go @@ -37,6 +37,7 @@ import ( "path/filepath" socifs "github.com/awslabs/soci-snapshotter/fs" + "github.com/awslabs/soci-snapshotter/fs/layer" "github.com/awslabs/soci-snapshotter/fs/source" "github.com/awslabs/soci-snapshotter/service/resolver" snbase "github.com/awslabs/soci-snapshotter/snapshot" @@ -87,12 +88,19 @@ func NewSociSnapshotterService(ctx context.Context, root string, config *Config, // Use RegistryHosts based on ResolverConfig and keychain hosts = resolver.RegistryHostsFromConfig(resolver.Config(config.ResolverConfig), sOpts.credsFuncs...) } - + userxattr, err := overlayutils.NeedsUserXAttr(snapshotterRoot(root)) + if err != nil { + log.G(ctx).WithError(err).Warnf("cannot detect whether \"userxattr\" option needs to be used, assuming to be %v", userxattr) + } + opq := layer.OverlayOpaqueTrusted + if userxattr { + opq = layer.OverlayOpaqueUser + } // Configure filesystem and snapshotter fsOpts := append(sOpts.fsOpts, socifs.WithGetSources(sources( sourceFromCRILabels(hosts), // provides source info based on CRI labels source.FromDefaultLabels(hosts), // provides source info based on default labels - ))) + )), socifs.WithOverlayOpaqueType(opq)) fs, err := socifs.NewFilesystem(fsRoot(root), config.Config, fsOpts...) if err != nil { log.G(ctx).WithError(err).Fatalf("failed to configure filesystem")