diff --git a/go.mod b/go.mod index 267811bab1..d136add445 100644 --- a/go.mod +++ b/go.mod @@ -64,7 +64,7 @@ require ( github.com/open-policy-agent/opa v1.15.2 github.com/opencloud-eu/icap-client v0.0.0-20250930132611-28a2afe62d89 github.com/opencloud-eu/libre-graph-api-go v1.0.8-0.20260310090739-853d972b282d - github.com/opencloud-eu/reva/v2 v2.46.5 + github.com/opencloud-eu/reva/v2 v2.46.6 github.com/opensearch-project/opensearch-go/v4 v4.6.0 github.com/orcaman/concurrent-map v1.0.0 github.com/pkg/errors v0.9.1 diff --git a/go.sum b/go.sum index 40f3ea7a08..2d94b43ccd 100644 --- a/go.sum +++ b/go.sum @@ -948,8 +948,8 @@ github.com/opencloud-eu/icap-client v0.0.0-20250930132611-28a2afe62d89 h1:W1ms+l github.com/opencloud-eu/icap-client v0.0.0-20250930132611-28a2afe62d89/go.mod h1:vigJkNss1N2QEceCuNw/ullDehncuJNFB6mEnzfq9UI= github.com/opencloud-eu/libre-graph-api-go v1.0.8-0.20260310090739-853d972b282d h1:JcqGDiyrcaQwVyV861TUyQgO7uEmsjkhfm7aQd84dOw= github.com/opencloud-eu/libre-graph-api-go v1.0.8-0.20260310090739-853d972b282d/go.mod h1:pzatilMEHZFT3qV7C/X3MqOa3NlRQuYhlRhZTL+hN6Q= -github.com/opencloud-eu/reva/v2 v2.46.5 h1:Zv3CUj//N8qalWEklQ5zJEPyPJvqDnT0gcqF47bUEso= -github.com/opencloud-eu/reva/v2 v2.46.5/go.mod h1:RoFQt+u7edxwzHr1IZ2Y6VaDinMiRPQupAvMBy3WVmE= +github.com/opencloud-eu/reva/v2 v2.46.6 h1:Q20wE4A2OpWVyDnRaC4KW1+nzo+DQaK+RK5y3RFnWIg= +github.com/opencloud-eu/reva/v2 v2.46.6/go.mod h1:RoFQt+u7edxwzHr1IZ2Y6VaDinMiRPQupAvMBy3WVmE= github.com/opencloud-eu/secure v0.0.0-20260312082735-b6f5cb2244e4 h1:l2oB/RctH+t8r7QBj5p8thfEHCM/jF35aAY3WQ3hADI= github.com/opencloud-eu/secure v0.0.0-20260312082735-b6f5cb2244e4/go.mod h1:BmF5hyM6tXczk3MpQkFf1hpKSRqCyhqcbiQtiAF7+40= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= diff --git a/vendor/github.com/opencloud-eu/reva/v2/pkg/storage/pkg/decomposedfs/metadata/hybrid_backend.go b/vendor/github.com/opencloud-eu/reva/v2/pkg/storage/pkg/decomposedfs/metadata/hybrid_backend.go index 23b06a9299..f82221413a 100644 --- a/vendor/github.com/opencloud-eu/reva/v2/pkg/storage/pkg/decomposedfs/metadata/hybrid_backend.go +++ b/vendor/github.com/opencloud-eu/reva/v2/pkg/storage/pkg/decomposedfs/metadata/hybrid_backend.go @@ -504,6 +504,11 @@ func (b HybridBackend) LockfilePath(n MetadataNode) string { // Lock locks the metadata for the given path func (b HybridBackend) Lock(n MetadataNode) (UnlockFunc, error) { + f, _, err := b.LockAndRead(n) + return f, err +} + +func (b HybridBackend) LockAndRead(n MetadataNode) (UnlockFunc, io.Reader, error) { metaLockPath := b.LockfilePath(n) mlock, err := lockedfile.OpenFile(metaLockPath, os.O_RDWR|os.O_CREATE, 0600) if err != nil { @@ -511,20 +516,20 @@ func (b HybridBackend) Lock(n MetadataNode) (UnlockFunc, error) { // create the parent directory err = os.MkdirAll(filepath.Dir(metaLockPath), 0700) if err != nil { - return nil, err + return nil, nil, err } mlock, err = lockedfile.OpenFile(metaLockPath, os.O_RDWR|os.O_CREATE, 0600) if err != nil { - return nil, err + return nil, nil, err } } else { - return nil, err + return nil, nil, err } } return func() error { // Warning: do not remove the lockfile or we may lock the same file more than once, https://github.com/opencloud-eu/opencloud/issues/1793 return mlock.Close() - }, nil + }, mlock, nil } func (b HybridBackend) cacheKey(n MetadataNode) string { diff --git a/vendor/github.com/opencloud-eu/reva/v2/pkg/storage/pkg/decomposedfs/metadata/messagepack_backend.go b/vendor/github.com/opencloud-eu/reva/v2/pkg/storage/pkg/decomposedfs/metadata/messagepack_backend.go index 7e6a8aeab3..21bed5bfdd 100644 --- a/vendor/github.com/opencloud-eu/reva/v2/pkg/storage/pkg/decomposedfs/metadata/messagepack_backend.go +++ b/vendor/github.com/opencloud-eu/reva/v2/pkg/storage/pkg/decomposedfs/metadata/messagepack_backend.go @@ -325,6 +325,20 @@ func (b MessagePackBackend) Lock(n MetadataNode) (UnlockFunc, error) { }, nil } +func (b MessagePackBackend) LockAndRead(n MetadataNode) (UnlockFunc, io.Reader, error) { + unlock, err := b.Lock(n) + if err != nil { + return nil, nil, err + } + + f, err := os.Open(b.MetadataPath(n)) + if err != nil { + _ = unlock() + return nil, nil, err + } + return unlock, f, nil +} + func (b MessagePackBackend) cacheKey(n MetadataNode) string { return n.GetSpaceID() + "/" + n.GetID() } diff --git a/vendor/github.com/opencloud-eu/reva/v2/pkg/storage/pkg/decomposedfs/metadata/metadata.go b/vendor/github.com/opencloud-eu/reva/v2/pkg/storage/pkg/decomposedfs/metadata/metadata.go index b60b19df4c..2e1a8747cc 100644 --- a/vendor/github.com/opencloud-eu/reva/v2/pkg/storage/pkg/decomposedfs/metadata/metadata.go +++ b/vendor/github.com/opencloud-eu/reva/v2/pkg/storage/pkg/decomposedfs/metadata/metadata.go @@ -59,6 +59,7 @@ type Backend interface { Remove(ctx context.Context, n MetadataNode, key string, acquireLock bool) error Lock(n MetadataNode) (UnlockFunc, error) + LockAndRead(n MetadataNode) (UnlockFunc, io.Reader, error) Purge(ctx context.Context, n MetadataNode) error Rename(oldNode, newNode MetadataNode) error MetadataPath(n MetadataNode) string @@ -113,6 +114,11 @@ func (NullBackend) Lock(n MetadataNode) (UnlockFunc, error) { return nil, nil } +// LockAndRead locks the metadata for reading +func (NullBackend) LockAndRead(n MetadataNode) (UnlockFunc, io.Reader, error) { + return nil, nil, nil +} + // IsMetaFile returns whether the given path represents a meta file func (NullBackend) IsMetaFile(path string) bool { return false } diff --git a/vendor/github.com/opencloud-eu/reva/v2/pkg/storage/pkg/decomposedfs/metadata/xattrs_backend.go b/vendor/github.com/opencloud-eu/reva/v2/pkg/storage/pkg/decomposedfs/metadata/xattrs_backend.go index 38e76d1079..f5b2b3662a 100644 --- a/vendor/github.com/opencloud-eu/reva/v2/pkg/storage/pkg/decomposedfs/metadata/xattrs_backend.go +++ b/vendor/github.com/opencloud-eu/reva/v2/pkg/storage/pkg/decomposedfs/metadata/xattrs_backend.go @@ -286,6 +286,21 @@ func (b XattrsBackend) Lock(n MetadataNode) (UnlockFunc, error) { }, nil } +// LockAndRead locks the metadata for reading +func (b XattrsBackend) LockAndRead(n MetadataNode) (UnlockFunc, io.Reader, error) { + unlock, err := b.Lock(n) + if err != nil { + return nil, nil, err + } + + f, err := os.Open(b.MetadataPath(n)) + if err != nil { + _ = unlock() + return nil, nil, err + } + return unlock, f, nil +} + // AllWithLockedSource reads all extended attributes from the given reader. // The path argument is used for storing the data in the cache func (b XattrsBackend) AllWithLockedSource(ctx context.Context, n MetadataNode, _ io.Reader) (map[string][]byte, error) { diff --git a/vendor/github.com/opencloud-eu/reva/v2/pkg/storage/pkg/decomposedfs/node/node.go b/vendor/github.com/opencloud-eu/reva/v2/pkg/storage/pkg/decomposedfs/node/node.go index 835710f658..ae2002fa47 100644 --- a/vendor/github.com/opencloud-eu/reva/v2/pkg/storage/pkg/decomposedfs/node/node.go +++ b/vendor/github.com/opencloud-eu/reva/v2/pkg/storage/pkg/decomposedfs/node/node.go @@ -357,13 +357,13 @@ func LockAndReadNode(ctx context.Context, lu PathLookup, spaceID, nodeID, intern _, subspan := tracer.Start(ctx, "lockedfile.OpenFile") bn := NewBaseNode(spaceID, nodeID, lu) - unlock, err := lu.MetadataBackend().Lock(bn) + unlock, r, err := lu.MetadataBackend().LockAndRead(bn) subspan.End() if err != nil { return nil, nil, err } - n, err := ReadNode(ctx, lu, spaceID, nodeID, internalPath, canListDisabledSpace, spaceRoot, skipParentCheck) + n, err := readNode(ctx, lu, spaceID, nodeID, internalPath, canListDisabledSpace, spaceRoot, skipParentCheck, r) if err != nil { _ = unlock() return nil, nil, err @@ -378,6 +378,12 @@ func LockAndReadNode(ctx context.Context, lu PathLookup, spaceID, nodeID, intern // ReadNode creates a new instance from an id and checks if it exists func ReadNode(ctx context.Context, lu PathLookup, spaceID, nodeID, internalPath string, canListDisabledSpace bool, spaceRoot *Node, skipParentCheck bool) (*Node, error) { + return readNode(ctx, lu, spaceID, nodeID, internalPath, canListDisabledSpace, spaceRoot, skipParentCheck, nil) +} + +// readNode reads a node by its id. If a reader is provided, it will be passed to the metadata backend to read the metadata. +// This is useful when the caller already holds a lock to prevent deadlocks when reading the metadata. +func readNode(ctx context.Context, lu PathLookup, spaceID, nodeID, internalPath string, canListDisabledSpace bool, spaceRoot *Node, skipParentCheck bool, r io.Reader) (*Node, error) { ctx, span := tracer.Start(ctx, "ReadNode") defer span.End() var err error @@ -392,6 +398,20 @@ func ReadNode(ctx context.Context, lu PathLookup, spaceID, nodeID, internalPath }, } spaceRoot.SpaceRoot = spaceRoot + + // If we hold the lock on the space root itself, prime its attribute cache + // through the no-lock path so the owner/name/disabled reads below do not try + // to re-acquire the already-held lock and self-deadlock. + if r != nil && nodeID == spaceID { + _, err = spaceRoot.XattrsWithReader(ctx, r) + switch { + case metadata.IsNotExist(err): + return spaceRoot, nil // swallow not found, the node defaults to exists = false + case err != nil: + return nil, err + } + } + spaceRoot.owner, err = spaceRoot.readOwner(ctx) switch { case metadata.IsNotExist(err): @@ -456,7 +476,8 @@ func ReadNode(ctx context.Context, lu PathLookup, spaceID, nodeID, internalPath } }() - attrs, err := n.Xattrs(ctx) + var attrs Attributes + attrs, err = n.XattrsWithReader(ctx, r) switch { case metadata.IsNotExist(err): return n, nil // swallow not found, the node defaults to exists = false diff --git a/vendor/github.com/opencloud-eu/reva/v2/pkg/storage/pkg/decomposedfs/tree/propagator/async.go b/vendor/github.com/opencloud-eu/reva/v2/pkg/storage/pkg/decomposedfs/tree/propagator/async.go index dfa57a7b44..4658913560 100644 --- a/vendor/github.com/opencloud-eu/reva/v2/pkg/storage/pkg/decomposedfs/tree/propagator/async.go +++ b/vendor/github.com/opencloud-eu/reva/v2/pkg/storage/pkg/decomposedfs/tree/propagator/async.go @@ -285,29 +285,20 @@ func (p AsyncPropagator) propagate(ctx context.Context, pn PropagationNode, reca attrs := node.Attributes{} - // lock parent before reading treesize or tree time - _, subspan = tracer.Start(ctx, "lockedfile.OpenFile") - unlock, err := p.lookup.MetadataBackend().Lock(pn) + _, subspan = tracer.Start(ctx, "node.LockAndReadNode") + n, unlock, err := node.LockAndReadNode(ctx, p.lookup, pn.GetSpaceID(), pn.GetID(), "", false, nil, false) subspan.End() if err != nil { - log.Error().Err(err). - Str("lock filepath", p.lookup.MetadataBackend().LockfilePath(pn)). - Msg("Propagation failed. Could not open metadata for node with lock.") + if n != nil && !n.Exists { + log.Debug().Str("attr", prefixes.PropagationAttr).Msg("node does not exist anymore, not propagating") + } else { + log.Error().Err(err).Msg("Propagation failed. Could not read node with lock.") + } cleanup() return } defer func() { _ = unlock() }() - _, subspan = tracer.Start(ctx, "node.ReadNode") - n, err := node.ReadNode(ctx, p.lookup, pn.GetSpaceID(), pn.GetID(), "", false, nil, false) - if err != nil { - log.Error().Err(err). - Msg("Propagation failed. Could not read node.") - cleanup() - return - } - subspan.End() - if !n.Exists { log.Debug().Str("attr", prefixes.PropagationAttr).Msg("node does not exist anymore, not propagating") cleanup() diff --git a/vendor/modules.txt b/vendor/modules.txt index 93c871972a..79935bad33 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1360,7 +1360,7 @@ github.com/opencloud-eu/icap-client # github.com/opencloud-eu/libre-graph-api-go v1.0.8-0.20260310090739-853d972b282d ## explicit; go 1.18 github.com/opencloud-eu/libre-graph-api-go -# github.com/opencloud-eu/reva/v2 v2.46.5 +# github.com/opencloud-eu/reva/v2 v2.46.6 ## explicit; go 1.25.0 github.com/opencloud-eu/reva/v2/cmd/revad/internal/grace github.com/opencloud-eu/reva/v2/cmd/revad/runtime