From 83c340c1c416268e31a3f3c2a9cecc5aef3ce84a Mon Sep 17 00:00:00 2001 From: Michael Stingl Date: Sat, 27 Jun 2026 14:03:29 +0200 Subject: [PATCH] fix(decomposedfs): tolerate a space root with an unset name attribute ReadNode failed with an internal error when a space root had no user.oc.name attribute, which happens for a half-deleted user space (its node lingers while the name attribute is already gone). Reading the name with XattrString returned the ENODATA error and aborted the read, which surfaces to the caller as code 15. The search service's tree walk stats every space root through this path, so one such node aborted the entire space index and logged "error while indexing a space" on every run; any other ReadNode caller hit the same hard failure. Swallow the unset-name case the same way the owner lookup just above already swallows a missing attribute: match metadata.IsAttrUnset and keep the empty name instead of failing the read. Any other read error keeps its existing behaviour, so a genuine read failure is not hidden. Related: https://github.com/opencloud-eu/opencloud/issues/1878 --- pkg/storage/pkg/decomposedfs/node/node.go | 10 ++++++++-- pkg/storage/pkg/decomposedfs/node/node_test.go | 16 ++++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/pkg/storage/pkg/decomposedfs/node/node.go b/pkg/storage/pkg/decomposedfs/node/node.go index ae2002fa47..e061306f71 100644 --- a/pkg/storage/pkg/decomposedfs/node/node.go +++ b/pkg/storage/pkg/decomposedfs/node/node.go @@ -422,8 +422,14 @@ func readNode(ctx context.Context, lu PathLookup, spaceID, nodeID, internalPath spaceRoot.Exists = true // lookup name in extended attributes - spaceRoot.Name, err = spaceRoot.XattrString(ctx, prefixes.NameAttr) - if err != nil { + var name string + name, err = spaceRoot.XattrString(ctx, prefixes.NameAttr) + switch { + case err == nil: + spaceRoot.Name = name + case metadata.IsAttrUnset(err): + // ignore + default: return nil, err } } diff --git a/pkg/storage/pkg/decomposedfs/node/node_test.go b/pkg/storage/pkg/decomposedfs/node/node_test.go index ce1fff24ce..8922652fe1 100644 --- a/pkg/storage/pkg/decomposedfs/node/node_test.go +++ b/pkg/storage/pkg/decomposedfs/node/node_test.go @@ -29,6 +29,7 @@ import ( . "github.com/onsi/gomega" ocsconv "github.com/opencloud-eu/reva/v2/pkg/conversions" ctxpkg "github.com/opencloud-eu/reva/v2/pkg/ctx" + "github.com/opencloud-eu/reva/v2/pkg/storage/pkg/decomposedfs/metadata/prefixes" "github.com/opencloud-eu/reva/v2/pkg/storage/pkg/decomposedfs/node" helpers "github.com/opencloud-eu/reva/v2/pkg/storage/pkg/decomposedfs/testhelpers" "github.com/opencloud-eu/reva/v2/pkg/storage/utils/grants" @@ -81,6 +82,21 @@ var _ = Describe("Node", func() { Expect(err).ToNot(HaveOccurred()) Expect(n.BlobID).To(Equal("file1-blobid")) }) + + It("treats a space root with an unset name attribute as nameless instead of failing", func() { + spaceRoot, err := env.Lookup.NodeFromSpaceID(env.Ctx, env.SpaceRootRes.SpaceId) + Expect(err).ToNot(HaveOccurred()) + + // simulate a half-deleted space root whose name attribute is gone + err = spaceRoot.RemoveXattr(env.Ctx, prefixes.NameAttr, true) + Expect(err).ToNot(HaveOccurred()) + + n, err := node.ReadNode(env.Ctx, env.Lookup, spaceRoot.SpaceID, spaceRoot.ID, "", false, nil, false) + Expect(err).ToNot(HaveOccurred()) + Expect(n).ToNot(BeNil()) + Expect(n.Exists).To(BeTrue()) + Expect(n.Name).To(Equal("")) + }) }) Describe("WriteMetadata", func() {