Bump reva for latest fixes

This commit is contained in:
Ralf Haferkamp
2025-02-25 16:11:18 +01:00
parent 0c8add46af
commit 201e6dd764
74 changed files with 2036 additions and 748 deletions
@@ -54,6 +54,11 @@ func New(m map[string]interface{}) (*Options, error) {
return nil, err
}
// default to hybrid metadatabackend for posixfs
if _, ok := m["metadata_backend"]; !ok {
m["metadata_backend"] = "hybrid"
}
do, err := decomposedoptions.New(m)
if err != nil {
return nil, err
+13 -3
View File
@@ -25,6 +25,7 @@ import (
"context"
"fmt"
"os"
"path/filepath"
"syscall"
"github.com/rs/zerolog"
@@ -78,10 +79,19 @@ func New(m map[string]interface{}, stream events.Stream, log *zerolog.Logger) (s
switch o.MetadataBackend {
case "xattrs":
lu = lookup.New(metadata.NewXattrsBackend(o.Root, o.FileMetadataCache), um, o, &timemanager.Manager{})
case "messagepack":
lu = lookup.New(metadata.NewMessagePackBackend(o.Root, o.FileMetadataCache), um, o, &timemanager.Manager{})
case "hybrid":
lu = lookup.New(metadata.NewHybridBackend(1024, // start offloading grants after 1KB
func(n metadata.MetadataNode) string {
spaceRoot, _ := lu.IDCache.Get(context.Background(), n.GetSpaceID(), n.GetSpaceID())
if len(spaceRoot) == 0 {
return ""
}
return filepath.Join(spaceRoot, lookup.RevisionsDir, lookup.Pathify(n.GetID(), 4, 2)+".mpk")
},
o.FileMetadataCache), um, o, &timemanager.Manager{})
default:
return nil, fmt.Errorf("unknown metadata backend %s, only 'messagepack' or 'xattrs' (default) supported", o.MetadataBackend)
return nil, fmt.Errorf("unknown metadata backend %s, only 'xattrs' or 'hybrid' (default) supported", o.MetadataBackend)
}
trashbin, err := trashbin.New(o, lu, log)
@@ -39,7 +39,6 @@ import (
userv1beta1 "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
"github.com/opencloud-eu/reva/v2/pkg/events"
"github.com/opencloud-eu/reva/v2/pkg/storage/fs/posix/lookup"
"github.com/opencloud-eu/reva/v2/pkg/storage/pkg/decomposedfs/metadata"
"github.com/opencloud-eu/reva/v2/pkg/storage/pkg/decomposedfs/metadata/prefixes"
"github.com/opencloud-eu/reva/v2/pkg/storage/pkg/decomposedfs/node"
@@ -273,27 +272,32 @@ func (t *Tree) Scan(path string, action EventAction, isDir bool) error {
}
func (t *Tree) HandleFileDelete(path string) error {
n, err := t.getNodeForPath(filepath.Dir(path))
spaceID, id, err := t.lookup.IDsForPath(context.Background(), path)
if err != nil {
return err
}
n := node.NewBaseNode(spaceID, id, t.lookup)
// purge metadata
if err := t.lookup.(*lookup.Lookup).IDCache.DeleteByPath(context.Background(), path); err != nil {
if err := t.lookup.IDCache.DeleteByPath(context.Background(), path); err != nil {
t.log.Error().Err(err).Str("path", path).Msg("could not delete id cache entry by path")
}
if err := t.lookup.MetadataBackend().Purge(context.Background(), n); err != nil {
t.log.Error().Err(err).Str("path", path).Msg("could not purge metadata")
}
parentNode, err := t.getNodeForPath(filepath.Dir(path))
if err != nil {
return err
}
t.PublishEvent(events.ItemTrashed{
Owner: n.Owner(),
Executant: n.Owner(),
Owner: parentNode.Owner(),
Executant: parentNode.Owner(),
Ref: &provider.Reference{
ResourceId: &provider.ResourceId{
StorageId: t.options.MountID,
SpaceId: n.SpaceID,
OpaqueId: n.ParentID,
OpaqueId: parentNode.ID,
},
Path: filepath.Base(path),
},
@@ -309,14 +313,12 @@ func (t *Tree) HandleFileDelete(path string) error {
}
func (t *Tree) getNodeForPath(path string) (*node.Node, error) {
lu := t.lookup.(*lookup.Lookup)
spaceID, nodeID, err := lu.IDsForPath(context.Background(), path)
spaceID, nodeID, err := t.lookup.IDsForPath(context.Background(), path)
if err != nil {
return nil, err
}
return node.ReadNode(context.Background(), lu, spaceID, nodeID, false, nil, false)
return node.ReadNode(context.Background(), t.lookup, spaceID, nodeID, false, nil, false)
}
func (t *Tree) findSpaceId(path string) (string, node.Attributes, error) {
@@ -324,7 +326,7 @@ func (t *Tree) findSpaceId(path string) (string, node.Attributes, error) {
spaceCandidate := path
spaceAttrs := node.Attributes{}
for strings.HasPrefix(spaceCandidate, t.options.Root) {
spaceID, _, err := t.lookup.(*lookup.Lookup).IDsForPath(context.Background(), spaceCandidate)
spaceID, _, err := t.lookup.IDsForPath(context.Background(), spaceCandidate)
if err == nil && len(spaceID) > 0 {
if t.options.UseSpaceGroups {
// set the uid and gid for the space
@@ -385,7 +387,7 @@ func (t *Tree) assimilate(item scanItem) error {
// the file has an id set, we already know it from the past
n := node.NewBaseNode(spaceID, id, t.lookup)
previousPath, ok := t.lookup.(*lookup.Lookup).GetCachedID(context.Background(), spaceID, string(id))
previousPath, ok := t.lookup.GetCachedID(context.Background(), spaceID, string(id))
previousParentID, _ := t.lookup.MetadataBackend().Get(context.Background(), n, prefixes.ParentidAttr)
// compare metadata mtime with actual mtime. if it matches AND the path hasn't changed (move operation)
@@ -416,7 +418,7 @@ func (t *Tree) assimilate(item scanItem) error {
// this is a move
t.log.Debug().Str("path", item.Path).Msg("move detected")
if err := t.lookup.(*lookup.Lookup).CacheID(context.Background(), spaceID, string(id), item.Path); err != nil {
if err := t.lookup.CacheID(context.Background(), spaceID, string(id), item.Path); err != nil {
t.log.Error().Err(err).Str("spaceID", spaceID).Str("id", string(id)).Str("path", item.Path).Msg("could not cache id")
}
_, attrs, err := t.updateFile(item.Path, string(id), spaceID)
@@ -425,7 +427,7 @@ func (t *Tree) assimilate(item scanItem) error {
}
// Delete the path entry using DeletePath(reverse lookup), not the whole entry pair.
if err := t.lookup.(*lookup.Lookup).IDCache.DeletePath(context.Background(), previousPath); err != nil {
if err := t.lookup.IDCache.DeletePath(context.Background(), previousPath); err != nil {
t.log.Error().Err(err).Str("path", previousPath).Msg("could not delete id cache entry by path")
}
@@ -469,7 +471,7 @@ func (t *Tree) assimilate(item scanItem) error {
} else {
// This item had already been assimilated in the past. Update the path
t.log.Debug().Str("path", item.Path).Msg("updating cached path")
if err := t.lookup.(*lookup.Lookup).CacheID(context.Background(), spaceID, string(id), item.Path); err != nil {
if err := t.lookup.CacheID(context.Background(), spaceID, string(id), item.Path); err != nil {
t.log.Error().Err(err).Str("spaceID", spaceID).Str("id", string(id)).Str("path", item.Path).Msg("could not cache id")
}
@@ -530,7 +532,7 @@ assimilate:
if id != spaceID {
// read parent
var err error
_, parentID, err = t.lookup.(*lookup.Lookup).IDsForPath(context.Background(), filepath.Dir(path))
_, parentID, err = t.lookup.IDsForPath(context.Background(), filepath.Dir(path))
if err != nil {
return nil, nil, fmt.Errorf("failed to read parent id")
}
@@ -668,7 +670,7 @@ assimilate:
return nil, nil, errors.Wrap(err, "failed to set attributes")
}
if err := t.lookup.(*lookup.Lookup).CacheID(context.Background(), spaceID, id, path); err != nil {
if err := t.lookup.CacheID(context.Background(), spaceID, id, path); err != nil {
t.log.Error().Err(err).Str("spaceID", spaceID).Str("id", id).Str("path", path).Msg("could not cache id")
}
@@ -741,7 +743,7 @@ func (t *Tree) WarmupIDCache(root string, assimilate, onlyDirty bool) error {
// try to find space
spaceCandidate := path
for strings.HasPrefix(spaceCandidate, t.options.Root) {
spaceID, _, err = t.lookup.(*lookup.Lookup).IDsForPath(context.Background(), spaceCandidate)
spaceID, _, err = t.lookup.IDsForPath(context.Background(), spaceCandidate)
if err == nil && len(spaceID) > 0 {
err = scopeSpace(path)
if err != nil {
@@ -767,7 +769,7 @@ func (t *Tree) WarmupIDCache(root string, assimilate, onlyDirty bool) error {
if id != "" {
// Check if the item on the previous still exists. In this case it might have been a copy with extended attributes -> set new ID
previousPath, ok := t.lookup.(*lookup.Lookup).GetCachedID(context.Background(), spaceID, id)
previousPath, ok := t.lookup.GetCachedID(context.Background(), spaceID, id)
if ok && previousPath != path {
// this id clashes with an existing id -> re-assimilate
_, err := os.Stat(previousPath)
@@ -775,7 +777,7 @@ func (t *Tree) WarmupIDCache(root string, assimilate, onlyDirty bool) error {
_ = t.assimilate(scanItem{Path: path, ForceRescan: true})
}
}
if err := t.lookup.(*lookup.Lookup).CacheID(context.Background(), spaceID, id, path); err != nil {
if err := t.lookup.CacheID(context.Background(), spaceID, id, path); err != nil {
t.log.Error().Err(err).Str("spaceID", spaceID).Str("id", id).Str("path", path).Msg("could not cache id")
}
}
@@ -788,7 +790,7 @@ func (t *Tree) WarmupIDCache(root string, assimilate, onlyDirty bool) error {
})
for dir, size := range sizes {
spaceID, id, err := t.lookup.(*lookup.Lookup).IDsForPath(context.Background(), dir)
spaceID, id, err := t.lookup.IDsForPath(context.Background(), dir)
if err != nil {
t.log.Error().Err(err).Str("path", dir).Msg("could not get ids for path")
continue
+7 -7
View File
@@ -81,7 +81,7 @@ type scanItem struct {
// Tree manages a hierarchical tree
type Tree struct {
lookup node.PathLookup
lookup *lookup.Lookup
blobstore Blobstore
trashbin *trashbin.Trashbin
propagator propagator.Propagator
@@ -106,7 +106,7 @@ type PermissionCheckFunc func(rp *provider.ResourcePermissions) bool
func New(lu node.PathLookup, bs Blobstore, um usermapper.Mapper, trashbin *trashbin.Trashbin, permissions permissions.Permissions, o *options.Options, es events.Stream, cache store.Store, log *zerolog.Logger) (*Tree, error) {
scanQueue := make(chan scanItem)
t := &Tree{
lookup: lu,
lookup: lu.(*lookup.Lookup),
blobstore: bs,
userMapper: um,
trashbin: trashbin,
@@ -220,7 +220,7 @@ func (t *Tree) TouchFile(ctx context.Context, n *node.Node, markprocessing bool,
n.SetType(provider.ResourceType_RESOURCE_TYPE_FILE)
// Set id in cache
if err := t.lookup.(*lookup.Lookup).CacheID(context.Background(), n.SpaceID, n.ID, nodePath); err != nil {
if err := t.lookup.CacheID(context.Background(), n.SpaceID, n.ID, nodePath); err != nil {
t.log.Error().Err(err).Str("spaceID", n.SpaceID).Str("id", n.ID).Str("path", nodePath).Msg("could not cache id")
}
@@ -325,7 +325,7 @@ func (t *Tree) Move(ctx context.Context, oldNode *node.Node, newNode *node.Node)
if newNode.ID == "" {
newNode.ID = oldNode.ID
}
if err := t.lookup.(*lookup.Lookup).CacheID(ctx, newNode.SpaceID, newNode.ID, filepath.Join(newNode.ParentPath(), newNode.Name)); err != nil {
if err := t.lookup.CacheID(ctx, newNode.SpaceID, newNode.ID, filepath.Join(newNode.ParentPath(), newNode.Name)); err != nil {
t.log.Error().Err(err).Str("spaceID", newNode.SpaceID).Str("id", newNode.ID).Str("path", filepath.Join(newNode.ParentPath(), newNode.Name)).Msg("could not cache id")
}
@@ -437,7 +437,7 @@ func (t *Tree) ListFolder(ctx context.Context, n *node.Node) ([]*node.Node, erro
for name := range work {
path := filepath.Join(dir, name)
_, nodeID, err := t.lookup.(*lookup.Lookup).IDsForPath(ctx, path)
_, nodeID, err := t.lookup.IDsForPath(ctx, path)
if err != nil {
return err
}
@@ -536,7 +536,7 @@ func (t *Tree) WriteBlob(n *node.Node, source string) error {
var err error
if t.options.EnableFSRevisions {
currentPath = t.lookup.(*lookup.Lookup).CurrentPath(n.SpaceID, n.ID)
currentPath = t.lookup.CurrentPath(n.SpaceID, n.ID)
defer func() {
_ = t.lookup.CopyMetadata(context.Background(), n, node.NewBaseNode(n.SpaceID, n.ID+node.CurrentIDDelimiter, t.lookup), func(attributeName string, value []byte) (newValue []byte, copy bool) {
@@ -612,7 +612,7 @@ func (t *Tree) createDirNode(ctx context.Context, n *node.Node) (err error) {
ctx, span := tracer.Start(ctx, "createDirNode")
defer span.End()
idcache := t.lookup.(*lookup.Lookup).IDCache
idcache := t.lookup.IDCache
// create a directory node
parentPath, ok := idcache.Get(ctx, n.SpaceID, n.ParentID)
if !ok {
@@ -0,0 +1,515 @@
package metadata
import (
"context"
"io"
"io/fs"
"os"
"path/filepath"
"strconv"
"strings"
"time"
"github.com/google/renameio/v2"
"github.com/pkg/errors"
"github.com/pkg/xattr"
"github.com/rogpeppe/go-internal/lockedfile"
"github.com/shamaton/msgpack/v2"
"github.com/opencloud-eu/reva/v2/pkg/storage/cache"
"github.com/opencloud-eu/reva/v2/pkg/storage/pkg/decomposedfs/metadata/prefixes"
"github.com/opencloud-eu/reva/v2/pkg/storage/utils/filelocks"
)
var _metadataOffloadedAttr = prefixes.OcPrefix + "metadata_offloaded"
type MetadataPathFunc func(MetadataNode) string
// HybridBackend stores the file attributes in extended attributes
type HybridBackend struct {
offloadLimit int
metaCache cache.FileMetadataCache
metadataPathFunc MetadataPathFunc
}
// NewMessageBackend returns a new HybridBackend instance
func NewHybridBackend(offloadLimit int, metadataPathFunc MetadataPathFunc, o cache.Config) HybridBackend {
return HybridBackend{
offloadLimit: offloadLimit,
metaCache: cache.GetFileMetadataCache(o),
metadataPathFunc: metadataPathFunc,
}
}
// Name returns the name of the backend
func (HybridBackend) Name() string { return "hybrid" }
// IdentifyPath returns the space id, node id and mtime of a file
func (b HybridBackend) IdentifyPath(_ context.Context, path string) (string, string, time.Time, error) {
spaceID, _ := xattr.Get(path, prefixes.SpaceIDAttr)
id, _ := xattr.Get(path, prefixes.IDAttr)
mtimeAttr, _ := xattr.Get(path, prefixes.MTimeAttr)
mtime, _ := time.Parse(time.RFC3339Nano, string(mtimeAttr))
return string(spaceID), string(id), mtime, nil
}
// Get an extended attribute value for the given key
// No file locking is involved here as reading a single xattr is
// considered to be atomic.
func (b HybridBackend) Get(ctx context.Context, n MetadataNode, key string) ([]byte, error) {
attribs := map[string][]byte{}
err := b.metaCache.PullFromCache(b.cacheKey(n), &attribs)
if err == nil && len(attribs[key]) > 0 {
return attribs[key], err
}
if isOffloadingAttribute(key) {
// check if key is offloaded
offloaded, err := xattr.Get(n.InternalPath(), _metadataOffloadedAttr)
if err == nil && string(offloaded) == "1" {
msgpackAttribs := map[string][]byte{}
msgBytes, err := os.ReadFile(b.MetadataPath(n))
if err != nil {
return nil, err
}
err = msgpack.Unmarshal(msgBytes, &msgpackAttribs)
if err != nil {
return nil, err
}
if val, ok := msgpackAttribs[key]; ok {
return val, nil
} else {
return nil, &xattr.Error{Op: "HybridBackend.Get", Path: n.InternalPath(), Err: xattr.ENOATTR} // attribute not found
}
}
}
return xattr.Get(n.InternalPath(), key)
}
// GetInt64 reads a string as int64 from the xattrs
func (b HybridBackend) GetInt64(ctx context.Context, n MetadataNode, key string) (int64, error) {
attr, err := b.Get(ctx, n, key)
if err != nil {
return 0, err
}
v, err := strconv.ParseInt(string(attr), 10, 64)
if err != nil {
return 0, err
}
return v, nil
}
func (b HybridBackend) list(ctx context.Context, n MetadataNode, acquireLock bool) (attribs []string, err error) {
filePath := n.InternalPath()
attrs, err := xattr.List(filePath)
if err == nil {
return attrs, nil
}
// listing xattrs failed, try again, either with lock or without
if acquireLock {
f, err := lockedfile.OpenFile(filePath+filelocks.LockFileSuffix, os.O_CREATE|os.O_WRONLY, 0600)
if err != nil {
return nil, err
}
defer cleanupLockfile(ctx, f)
}
return xattr.List(filePath)
}
// All reads all extended attributes for a node, protected by a
// shared file lock
func (b HybridBackend) All(ctx context.Context, n MetadataNode) (map[string][]byte, error) {
return b.getAll(ctx, n, false, false, true)
}
func (b HybridBackend) getAll(ctx context.Context, n MetadataNode, skipCache, skipOffloaded, acquireLock bool) (map[string][]byte, error) {
attribs := map[string][]byte{}
if !skipCache {
err := b.metaCache.PullFromCache(b.cacheKey(n), &attribs)
if err == nil {
return attribs, err
}
}
attrNames, err := b.list(ctx, n, acquireLock)
if err != nil {
return nil, err
}
if len(attrNames) == 0 {
return attribs, nil
}
var (
xerrs = 0
xerr error
)
// error handling: Count if there are errors while reading all attribs.
// if there were any, return an error.
attribs = make(map[string][]byte, len(attrNames))
path := n.InternalPath()
for _, name := range attrNames {
var val []byte
if val, xerr = xattr.Get(path, name); xerr != nil && !IsAttrUnset(xerr) {
xerrs++
} else {
attribs[name] = val
}
}
if xerrs > 0 {
return nil, errors.Wrap(xerr, "Failed to read all xattrs")
}
// merge the attributes from the offload file
offloaded, err := xattr.Get(path, _metadataOffloadedAttr)
if !skipOffloaded && err == nil && string(offloaded) == "1" {
msgpackAttribs := map[string][]byte{}
msgBytes, err := os.ReadFile(b.MetadataPath(n))
if err != nil {
return nil, err
}
err = msgpack.Unmarshal(msgBytes, &msgpackAttribs)
if err != nil {
return nil, err
}
for key, val := range msgpackAttribs {
attribs[key] = val
}
}
err = b.metaCache.PushToCache(b.cacheKey(n), attribs)
if err != nil {
return nil, err
}
return attribs, nil
}
// Set sets one attribute for the given path
func (b HybridBackend) Set(ctx context.Context, n MetadataNode, key string, val []byte) (err error) {
return b.SetMultiple(ctx, n, map[string][]byte{key: val}, true)
}
// SetMultiple sets a set of attribute for the given path
func (b HybridBackend) SetMultiple(ctx context.Context, n MetadataNode, attribs map[string][]byte, acquireLock bool) (err error) {
path := n.InternalPath()
if acquireLock {
err := os.MkdirAll(filepath.Dir(path), 0600)
if err != nil {
return err
}
lockedFile, err := lockedfile.OpenFile(b.LockfilePath(n), os.O_CREATE|os.O_WRONLY, 0600)
if err != nil {
return err
}
defer cleanupLockfile(ctx, lockedFile)
}
offloadAttr, err := xattr.Get(path, _metadataOffloadedAttr)
offloaded := err == nil && string(offloadAttr) == "1"
// offload if the offloading metadata size exceeds the limit
hasOffloadingAttrs := false
for key := range attribs {
if isOffloadingAttribute(key) {
hasOffloadingAttrs = true
break
}
}
if hasOffloadingAttrs && !offloaded {
mdSize := 0
for key := range attribs {
if isOffloadingAttribute(key) {
mdSize += len(attribs[key]) + len(key)
}
}
existingAttribs, err := b.getAll(ctx, n, true, true, false)
if err != nil {
return err
}
for key := range existingAttribs {
if isOffloadingAttribute(key) {
mdSize += len(existingAttribs[key]) + len(key)
}
}
if mdSize > b.offloadLimit {
err = b.offloadMetadata(ctx, n)
if err != nil {
return err
}
offloaded = true
}
}
if offloaded {
metaPath := b.MetadataPath(n)
var msgBytes []byte
msgBytes, err = os.ReadFile(metaPath)
mpkAttribs := map[string][]byte{}
switch {
case err != nil:
if !errors.Is(err, fs.ErrNotExist) {
return err
}
default:
err = msgpack.Unmarshal(msgBytes, &mpkAttribs)
if err != nil {
return err
}
}
// prepare offloaded metadata
for key, val := range attribs {
if isOffloadingAttribute(key) {
mpkAttribs[key] = val
delete(attribs, key)
}
}
var d []byte
d, err = msgpack.Marshal(mpkAttribs)
if err != nil {
return err
}
// overwrite file atomically
err = renameio.WriteFile(metaPath, d, 0600)
if err != nil {
return err
}
}
xerrs := 0
var xerr error
// error handling: Count if there are errors while setting the attribs.
// if there were any, return an error.
for key, val := range attribs {
if xerr = xattr.Set(path, key, val); xerr != nil {
// log
xerrs++
}
}
if xerrs > 0 {
return errors.Wrap(xerr, "Failed to set all xattrs")
}
attribs, err = b.getAll(ctx, n, true, false, false)
if err != nil {
return err
}
err = b.metaCache.PushToCache(b.cacheKey(n), attribs)
if err != nil {
return err
}
return nil
}
func (b HybridBackend) offloadMetadata(ctx context.Context, n MetadataNode) error {
path := n.InternalPath()
msgpackAttribs := map[string][]byte{}
xerrs := 0
var xerr error
// collect attributes to move
existingAttribs, err := b.getAll(ctx, n, true, true, false)
if err != nil {
return err
}
for key, val := range existingAttribs {
if isOffloadingAttribute(key) {
msgpackAttribs[key] = val
}
}
var d []byte
d, err = msgpack.Marshal(msgpackAttribs)
if err != nil {
return err
}
err = os.MkdirAll(filepath.Dir(b.MetadataPath(n)), 0700)
if err != nil {
return err
}
err = renameio.WriteFile(b.MetadataPath(n), d, 0600)
if err != nil {
return err
}
// set the metadata offloaded attribute
err = xattr.Set(path, _metadataOffloadedAttr, []byte("1"))
if err != nil {
return err
}
// remove offloaded attributes from xattrs
for key := range msgpackAttribs {
xerr = xattr.Remove(path, key)
if xerr != nil {
xerrs++
}
}
if xerrs > 0 {
return errors.Wrap(xerr, "Failed to remove xattrs while offloading")
}
return nil
}
// Remove an extended attribute key
func (b HybridBackend) Remove(ctx context.Context, n MetadataNode, key string, acquireLock bool) error {
path := n.InternalPath()
if acquireLock {
lockedFile, err := lockedfile.OpenFile(path+filelocks.LockFileSuffix, os.O_CREATE|os.O_WRONLY, 0600)
if err != nil {
return err
}
defer cleanupLockfile(ctx, lockedFile)
}
if isOffloadingAttribute(key) {
offloadAttr, err := xattr.Get(path, _metadataOffloadedAttr)
offloaded := err == nil && string(offloadAttr) == "1"
if offloaded {
// remove from offloaded metadata
// 1. read offloaded metadata
metaPath := b.MetadataPath(n)
var msgBytes []byte
msgBytes, err = os.ReadFile(metaPath)
mpkAttribs := map[string][]byte{}
switch {
case err != nil:
if !errors.Is(err, fs.ErrNotExist) {
return err
}
default:
err = msgpack.Unmarshal(msgBytes, &mpkAttribs)
if err != nil {
return err
}
}
if _, ok := mpkAttribs[key]; !ok {
return &xattr.Error{Op: "HybridBackend.Remove", Path: n.InternalPath(), Err: xattr.ENOATTR} // attribute not found
}
// 2. remove attribute
delete(mpkAttribs, key)
// 3. write back to file
var d []byte
d, err = msgpack.Marshal(mpkAttribs)
if err != nil {
return err
}
err = renameio.WriteFile(b.MetadataPath(n), d, 0600)
if err != nil {
return err
}
} else {
// remove from xattrs
err := xattr.Remove(path, key)
if err != nil {
return err
}
}
} else {
// remove from xattrs
err := xattr.Remove(path, key)
if err != nil {
return err
}
}
attribs, err := b.getAll(ctx, n, true, false, false)
if err != nil {
return err
}
return b.metaCache.PushToCache(b.cacheKey(n), attribs)
}
// IsMetaFile returns whether the given path represents a meta file
func (HybridBackend) IsMetaFile(path string) bool { return strings.HasSuffix(path, ".meta.lock") }
// Purge purges the data of a given path
func (b HybridBackend) Purge(ctx context.Context, n MetadataNode) error {
path := n.InternalPath()
_, err := os.Stat(path)
if err == nil {
attribs, err := b.getAll(ctx, n, true, false, true)
if err != nil {
return err
}
for attr := range attribs {
if strings.HasPrefix(attr, prefixes.OcPrefix) {
err := xattr.Remove(path, attr)
if err != nil {
return err
}
}
}
}
return b.metaCache.RemoveMetadata(b.cacheKey(n))
}
// Rename moves the data for a given path to a new path
func (b HybridBackend) Rename(oldNode, newNode MetadataNode) error {
data := map[string][]byte{}
err := b.metaCache.PullFromCache(b.cacheKey(oldNode), &data)
if err == nil {
err = b.metaCache.PushToCache(b.cacheKey(newNode), data)
if err != nil {
return err
}
}
return b.metaCache.RemoveMetadata(b.cacheKey(oldNode))
}
// MetadataPath returns the path of the file holding the metadata for the given path
func (b HybridBackend) MetadataPath(n MetadataNode) string { return b.metadataPathFunc(n) }
// LockfilePath returns the path of the lock file
func (HybridBackend) LockfilePath(n MetadataNode) string { return n.InternalPath() + ".mlock" }
// Lock locks the metadata for the given path
func (b HybridBackend) Lock(n MetadataNode) (UnlockFunc, error) {
metaLockPath := b.LockfilePath(n)
mlock, err := lockedfile.OpenFile(metaLockPath, os.O_RDWR|os.O_CREATE, 0600)
if err != nil {
return nil, err
}
return func() error {
err := mlock.Close()
if err != nil {
return err
}
return os.Remove(metaLockPath)
}, nil
}
// AllWithLockedSource reads all extended attributes from the given reader.
// The path argument is used for storing the data in the cache
func (b HybridBackend) AllWithLockedSource(ctx context.Context, n MetadataNode, _ io.Reader) (map[string][]byte, error) {
return b.All(ctx, n)
}
func (b HybridBackend) cacheKey(n MetadataNode) string {
// rootPath is guaranteed to have no trailing slash
// the cache key shouldn't begin with a slash as some stores drop it which can cause
// confusion
return n.GetSpaceID() + "/" + n.GetID()
}
func isOffloadingAttribute(key string) bool {
return strings.HasPrefix(key, prefixes.GrantPrefix) || strings.HasPrefix(key, prefixes.MetadataPrefix)
}
@@ -126,20 +126,6 @@ func (b MessagePackBackend) GetInt64(ctx context.Context, n MetadataNode, key st
return i, nil
}
// List retrieves a list of names of extended attributes associated with the
// given path in the file system.
func (b MessagePackBackend) List(ctx context.Context, n MetadataNode) ([]string, error) {
attribs, err := b.loadAttributes(ctx, n, nil)
if err != nil {
return nil, err
}
keys := []string{}
for k := range attribs {
keys = append(keys, k)
}
return keys, nil
}
// Set sets one attribute for the given path
func (b MessagePackBackend) Set(ctx context.Context, n MetadataNode, key string, val []byte) error {
return b.SetMultiple(ctx, n, map[string][]byte{key: val}, true)
@@ -54,7 +54,6 @@ type Backend interface {
Get(ctx context.Context, n MetadataNode, key string) ([]byte, error)
GetInt64(ctx context.Context, n MetadataNode, key string) (int64, error)
List(ctx context.Context, n MetadataNode) (attribs []string, err error)
Set(ctx context.Context, n MetadataNode, key string, val []byte) error
SetMultiple(ctx context.Context, n MetadataNode, attribs map[string][]byte, acquireLock bool) error
Remove(ctx context.Context, n MetadataNode, key string, acquireLock bool) error
@@ -94,12 +93,6 @@ func (NullBackend) GetInt64(ctx context.Context, n MetadataNode, key string) (in
return 0, errUnconfiguredError
}
// List retrieves a list of names of extended attributes associated with the
// given path in the file system.
func (NullBackend) List(ctx context.Context, n MetadataNode) ([]string, error) {
return nil, errUnconfiguredError
}
// Set sets one attribute for the given path
func (NullBackend) Set(ctx context.Context, n MetadataNode, key string, val []byte) error {
return errUnconfiguredError
@@ -87,12 +87,6 @@ func (b XattrsBackend) GetInt64(ctx context.Context, n MetadataNode, key string)
return v, nil
}
// List retrieves a list of names of extended attributes associated with the
// given path in the file system.
func (b XattrsBackend) List(ctx context.Context, n MetadataNode) (attribs []string, err error) {
return b.list(ctx, n, true)
}
func (b XattrsBackend) list(ctx context.Context, n MetadataNode, acquireLock bool) (attribs []string, err error) {
filePath := n.InternalPath()
attrs, err := xattr.List(filePath)
@@ -101,8 +101,8 @@ func (tb *DecomposedfsTrashbin) ListRecycle(ctx context.Context, ref *provider.R
items := make([]*provider.RecycleItem, 0)
trashRootPath := filepath.Join(tb.getRecycleRoot(spaceID), lookup.Pathify(key, 4, 2))
originalPath, _, timeSuffix, err := readTrashLink(trashRootPath)
originalNode := node.NewBaseNode(spaceID, key, tb.fs.lu)
originalPath, id, timeSuffix, err := readTrashLink(trashRootPath)
originalNode := node.NewBaseNode(spaceID, id+node.TrashIDDelimiter+timeSuffix, tb.fs.lu)
if err != nil {
sublog.Error().Err(err).Str("trashRoot", trashRootPath).Msg("error reading trash link")
return nil, err
@@ -884,6 +884,9 @@ func (t *Tree) readRecycleItem(ctx context.Context, spaceID, key, path string) (
trashItem = filepath.Join(t.lookup.InternalRoot(), "spaces", lookup.Pathify(spaceID, 1, 2), "trash", lookup.Pathify(key, 4, 2))
resolvedTrashRootNodePath, err := filepath.EvalSymlinks(trashItem)
trashedNodeId := nodeFullIDRegep.ReplaceAllString(resolvedTrashRootNodePath, "$1")
trashedNodeId = strings.ReplaceAll(trashedNodeId, "/", "")
if err != nil {
return
}
@@ -936,7 +939,7 @@ func (t *Tree) readRecycleItem(ctx context.Context, spaceID, key, path string) (
origin = "/"
// lookup origin path in extended attributes
rootNode := node.NewBaseNode(spaceID, nodeID, t.lookup)
rootNode := node.NewBaseNode(spaceID, trashedNodeId, t.lookup)
if attrBytes, err = backend.Get(ctx, rootNode, prefixes.TrashOriginAttr); err == nil {
origin = filepath.Join(string(attrBytes), path)
} else {