Bump reva for latest fixes
This commit is contained in:
+5
@@ -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
@@ -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)
|
||||
|
||||
+23
-21
@@ -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
@@ -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 {
|
||||
|
||||
Generated
Vendored
+515
@@ -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)
|
||||
}
|
||||
Generated
Vendored
-14
@@ -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)
|
||||
|
||||
Generated
Vendored
-7
@@ -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
|
||||
|
||||
Generated
Vendored
-6
@@ -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)
|
||||
|
||||
+2
-2
@@ -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
|
||||
|
||||
+4
-1
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user