From 602d1ca4c27af5c93b0e13e0c402477a3026fec3 Mon Sep 17 00:00:00 2001 From: David Christofas Date: Fri, 25 Jun 2021 14:37:03 +0200 Subject: [PATCH] implement purging of specific files in the trash-bin --- .../storageprovider/storageprovider.go | 2 +- .../http/services/owncloud/ocdav/trashbin.go | 5 +-- pkg/storage/fs/owncloud/owncloud.go | 8 ++--- pkg/storage/fs/owncloudsql/owncloudsql.go | 8 ++--- pkg/storage/fs/s3/s3.go | 2 +- pkg/storage/storage.go | 2 +- .../utils/decomposedfs/decomposedfs.go | 2 +- pkg/storage/utils/decomposedfs/recycle.go | 6 ++-- pkg/storage/utils/decomposedfs/tree/tree.go | 32 +++++++++++-------- .../utils/decomposedfs/tree/tree_test.go | 4 +-- pkg/storage/utils/eosfs/eosfs.go | 2 +- pkg/storage/utils/localfs/localfs.go | 4 +-- 12 files changed, 41 insertions(+), 36 deletions(-) diff --git a/internal/grpc/services/storageprovider/storageprovider.go b/internal/grpc/services/storageprovider/storageprovider.go index 013fed44cf8..b6622d93f8a 100644 --- a/internal/grpc/services/storageprovider/storageprovider.go +++ b/internal/grpc/services/storageprovider/storageprovider.go @@ -848,7 +848,7 @@ func (s *service) RestoreRecycleItem(ctx context.Context, req *provider.RestoreR func (s *service) PurgeRecycle(ctx context.Context, req *provider.PurgeRecycleRequest) (*provider.PurgeRecycleResponse, error) { // if a key was sent as opaque id purge only that item if req.GetRef().GetResourceId() != nil && req.GetRef().GetResourceId().OpaqueId != "" { - if err := s.storage.PurgeRecycleItem(ctx, req.GetRef().GetResourceId().OpaqueId); err != nil { + if err := s.storage.PurgeRecycleItem(ctx, req.GetRef()); err != nil { var st *rpc.Status switch err.(type) { case errtypes.IsNotFound: diff --git a/internal/http/services/owncloud/ocdav/trashbin.go b/internal/http/services/owncloud/ocdav/trashbin.go index 24cf6b21ce2..a6a99f40132 100644 --- a/internal/http/services/owncloud/ocdav/trashbin.go +++ b/internal/http/services/owncloud/ocdav/trashbin.go @@ -134,7 +134,7 @@ func (h *TrashbinHandler) Handler(s *svc) http.Handler { } if r.Method == "DELETE" { - h.delete(w, r, s, u, key) + h.delete(w, r, s, u, key, r.URL.Path) return } @@ -555,7 +555,7 @@ func (h *TrashbinHandler) restore(w http.ResponseWriter, r *http.Request, s *svc } // delete has only a key -func (h *TrashbinHandler) delete(w http.ResponseWriter, r *http.Request, s *svc, u *userpb.User, key string) { +func (h *TrashbinHandler) delete(w http.ResponseWriter, r *http.Request, s *svc, u *userpb.User, key, path string) { ctx := r.Context() ctx, span := trace.StartSpan(ctx, "erase") defer span.End() @@ -599,6 +599,7 @@ func (h *TrashbinHandler) delete(w http.ResponseWriter, r *http.Request, s *svc, StorageId: sRes.Info.Id.StorageId, OpaqueId: key, }, + Path: utils.MakeRelativePath(path), }, } diff --git a/pkg/storage/fs/owncloud/owncloud.go b/pkg/storage/fs/owncloud/owncloud.go index 3ba6423a358..3566f15edd6 100644 --- a/pkg/storage/fs/owncloud/owncloud.go +++ b/pkg/storage/fs/owncloud/owncloud.go @@ -2056,12 +2056,12 @@ func (fs *ocfs) RestoreRevision(ctx context.Context, ref *provider.Reference, re return fs.propagate(ctx, ip) } -func (fs *ocfs) PurgeRecycleItem(ctx context.Context, key string) error { +func (fs *ocfs) PurgeRecycleItem(ctx context.Context, ref *provider.Reference) error { rp, err := fs.getRecyclePath(ctx) if err != nil { return errors.Wrap(err, "ocfs: error resolving recycle path") } - ip := filepath.Join(rp, filepath.Clean(key)) + ip := filepath.Join(rp, filepath.Clean(ref.ResourceId.OpaqueId)) // TODO check permission? // check permissions @@ -2082,7 +2082,7 @@ func (fs *ocfs) PurgeRecycleItem(ctx context.Context, key string) error { if err != nil { return errors.Wrap(err, "ocfs: error deleting recycle item") } - err = os.RemoveAll(filepath.Join(filepath.Dir(rp), "versions", filepath.Clean(key))) + err = os.RemoveAll(filepath.Join(filepath.Dir(rp), "versions", filepath.Clean(ref.ResourceId.OpaqueId))) if err != nil { return errors.Wrap(err, "ocfs: error deleting recycle item versions") } @@ -2157,7 +2157,7 @@ func (fs *ocfs) ListRecycle(ctx context.Context, ref *provider.Reference) ([]*pr } // list files folder - mds, err := ioutil.ReadDir(rp) + mds, err := ioutil.ReadDir(filepath.Join(rp, ref.Path)) if err != nil { log := appctx.GetLogger(ctx) log.Debug().Err(err).Str("path", rp).Msg("trash not readable") diff --git a/pkg/storage/fs/owncloudsql/owncloudsql.go b/pkg/storage/fs/owncloudsql/owncloudsql.go index b0773e15450..e44bb4a1c3c 100644 --- a/pkg/storage/fs/owncloudsql/owncloudsql.go +++ b/pkg/storage/fs/owncloudsql/owncloudsql.go @@ -1892,12 +1892,12 @@ func (fs *ocfs) RestoreRevision(ctx context.Context, ref *provider.Reference, re return fs.propagate(ctx, ip) } -func (fs *ocfs) PurgeRecycleItem(ctx context.Context, key string) error { +func (fs *ocfs) PurgeRecycleItem(ctx context.Context, ref *provider.Reference) error { rp, err := fs.getRecyclePath(ctx) if err != nil { return errors.Wrap(err, "ocfs: error resolving recycle path") } - ip := filepath.Join(rp, filepath.Clean(key)) + ip := filepath.Join(rp, filepath.Clean(ref.ResourceId.OpaqueId)) // TODO check permission? // check permissions @@ -1918,12 +1918,12 @@ func (fs *ocfs) PurgeRecycleItem(ctx context.Context, key string) error { if err != nil { return errors.Wrap(err, "ocfs: error deleting recycle item") } - err = os.RemoveAll(filepath.Join(filepath.Dir(rp), "versions", filepath.Clean(key))) + err = os.RemoveAll(filepath.Join(filepath.Dir(rp), "versions", filepath.Clean(ref.ResourceId.OpaqueId))) if err != nil { return errors.Wrap(err, "ocfs: error deleting recycle item versions") } - base, ttime, err := splitTrashKey(key) + base, ttime, err := splitTrashKey(ref.ResourceId.OpaqueId) if err != nil { return err } diff --git a/pkg/storage/fs/s3/s3.go b/pkg/storage/fs/s3/s3.go index 1ed0a0ea68d..2e6800f0594 100644 --- a/pkg/storage/fs/s3/s3.go +++ b/pkg/storage/fs/s3/s3.go @@ -646,7 +646,7 @@ func (fs *s3FS) RestoreRevision(ctx context.Context, ref *provider.Reference, re return errtypes.NotSupported("restore revision") } -func (fs *s3FS) PurgeRecycleItem(ctx context.Context, key string) error { +func (fs *s3FS) PurgeRecycleItem(ctx context.Context, ref *provider.Reference) error { return errtypes.NotSupported("purge recycle item") } diff --git a/pkg/storage/storage.go b/pkg/storage/storage.go index 687fe9eab35..d3bedd925b6 100644 --- a/pkg/storage/storage.go +++ b/pkg/storage/storage.go @@ -44,7 +44,7 @@ type FS interface { RestoreRevision(ctx context.Context, ref *provider.Reference, key string) error ListRecycle(ctx context.Context, ref *provider.Reference) ([]*provider.RecycleItem, error) RestoreRecycleItem(ctx context.Context, key string, restoreRef *provider.Reference) error - PurgeRecycleItem(ctx context.Context, key string) error + PurgeRecycleItem(ctx context.Context, ref *provider.Reference) error EmptyRecycle(ctx context.Context) error GetPathByID(ctx context.Context, id *provider.ResourceId) (string, error) AddGrant(ctx context.Context, ref *provider.Reference, g *provider.Grant) error diff --git a/pkg/storage/utils/decomposedfs/decomposedfs.go b/pkg/storage/utils/decomposedfs/decomposedfs.go index 3d55a99a194..105429d966e 100644 --- a/pkg/storage/utils/decomposedfs/decomposedfs.go +++ b/pkg/storage/utils/decomposedfs/decomposedfs.go @@ -64,7 +64,7 @@ type Tree interface { Move(ctx context.Context, oldNode *node.Node, newNode *node.Node) (err error) Delete(ctx context.Context, node *node.Node) (err error) RestoreRecycleItemFunc(ctx context.Context, key, restorePath string) (*node.Node, func() error, error) // FIXME REFERENCE use ref instead of path - PurgeRecycleItemFunc(ctx context.Context, key string) (*node.Node, func() error, error) + PurgeRecycleItemFunc(ctx context.Context, key, purgePath string) (*node.Node, func() error, error) WriteBlob(key string, reader io.Reader) error ReadBlob(key string) (io.ReadCloser, error) diff --git a/pkg/storage/utils/decomposedfs/recycle.go b/pkg/storage/utils/decomposedfs/recycle.go index a6cc999aa9f..d3ed9938bb4 100644 --- a/pkg/storage/utils/decomposedfs/recycle.go +++ b/pkg/storage/utils/decomposedfs/recycle.go @@ -284,8 +284,8 @@ func (fs *Decomposedfs) RestoreRecycleItem(ctx context.Context, key string, rest } // PurgeRecycleItem purges the specified item -func (fs *Decomposedfs) PurgeRecycleItem(ctx context.Context, key string) error { - rn, purgeFunc, err := fs.tp.PurgeRecycleItemFunc(ctx, key) +func (fs *Decomposedfs) PurgeRecycleItem(ctx context.Context, ref *provider.Reference) error { + rn, purgeFunc, err := fs.tp.PurgeRecycleItemFunc(ctx, ref.ResourceId.OpaqueId, ref.Path) if err != nil { return err } @@ -298,7 +298,7 @@ func (fs *Decomposedfs) PurgeRecycleItem(ctx context.Context, key string) error case err != nil: return errtypes.InternalError(err.Error()) case !ok: - return errtypes.PermissionDenied(key) + return errtypes.PermissionDenied(ref.ResourceId.OpaqueId) } // Run the purge func diff --git a/pkg/storage/utils/decomposedfs/tree/tree.go b/pkg/storage/utils/decomposedfs/tree/tree.go index e9ec02d33bf..378ececf222 100644 --- a/pkg/storage/utils/decomposedfs/tree/tree.go +++ b/pkg/storage/utils/decomposedfs/tree/tree.go @@ -25,7 +25,6 @@ import ( "os" "path/filepath" "strconv" - "strings" "time" userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" @@ -347,7 +346,7 @@ func (t *Tree) Delete(ctx context.Context, n *node.Node) (err error) { // RestoreRecycleItemFunc returns a node and a function to restore it from the trash func (t *Tree) RestoreRecycleItemFunc(ctx context.Context, key, restorePath string) (*node.Node, func() error, error) { - rn, trashItem, deletedNodePath, origin, err := t.readRecycleItem(ctx, key) + rn, trashItem, deletedNodePath, origin, err := t.readRecycleItem(ctx, key, "") if err != nil { return nil, nil, err } @@ -397,8 +396,8 @@ func (t *Tree) RestoreRecycleItemFunc(ctx context.Context, key, restorePath stri } // PurgeRecycleItemFunc returns a node and a function to purge it from the trash -func (t *Tree) PurgeRecycleItemFunc(ctx context.Context, key string) (*node.Node, func() error, error) { - rn, trashItem, deletedNodePath, _, err := t.readRecycleItem(ctx, key) +func (t *Tree) PurgeRecycleItemFunc(ctx context.Context, key string, path string) (*node.Node, func() error, error) { + rn, trashItem, deletedNodePath, _, err := t.readRecycleItem(ctx, key, path) if err != nil { return nil, nil, err } @@ -632,13 +631,13 @@ func (t *Tree) createNode(n *node.Node, owner *userpb.UserId) (err error) { } // TODO refactor the returned params into Node properties? would make all the path transformations go away... -func (t *Tree) readRecycleItem(ctx context.Context, key string) (n *node.Node, trashItem string, deletedNodePath string, origin string, err error) { +func (t *Tree) readRecycleItem(ctx context.Context, key, path string) (n *node.Node, trashItem string, deletedNodePath string, origin string, err error) { if key == "" { return nil, "", "", "", errtypes.InternalError("key is empty") } u := user.ContextMustGetUser(ctx) - trashItem = filepath.Join(t.lookup.InternalRoot(), "trash", u.Id.OpaqueId, key) + trashItem = filepath.Join(t.lookup.InternalRoot(), "trash", u.Id.OpaqueId, key, path) var link string link, err = os.Readlink(trashItem) @@ -646,11 +645,8 @@ func (t *Tree) readRecycleItem(ctx context.Context, key string) (n *node.Node, t appctx.GetLogger(ctx).Error().Err(err).Str("trashItem", trashItem).Msg("error reading trash link") return } - parts := strings.SplitN(filepath.Base(link), ".T.", 2) - if len(parts) != 2 { - appctx.GetLogger(ctx).Error().Err(err).Str("trashItem", trashItem).Interface("parts", parts).Msg("malformed trash link") - return - } + + nodeID := filepath.Base(link) var attrBytes []byte deletedNodePath = t.lookup.InternalPath(filepath.Base(link)) @@ -669,7 +665,7 @@ func (t *Tree) readRecycleItem(ctx context.Context, key string) (n *node.Node, t return } - n = node.New(parts[0], "", "", 0, "", owner, t.lookup) + n = node.New(nodeID, "", "", 0, "", owner, t.lookup) // lookup blobID in extended attributes if attrBytes, err = xattr.Get(deletedNodePath, xattrs.BlobIDAttr); err == nil { n.BlobID = string(attrBytes) @@ -693,9 +689,17 @@ func (t *Tree) readRecycleItem(ctx context.Context, key string) (n *node.Node, t // get origin node origin = "/" + trashItemRoot := filepath.Join(t.lookup.InternalRoot(), "trash", u.Id.OpaqueId, key) + rootLink, err := os.Readlink(trashItemRoot) + if err != nil { + appctx.GetLogger(ctx).Error().Err(err).Str("trashItem", trashItem).Msg("error reading trash link") + return + } + + deletedNodeRootPath := t.lookup.InternalPath(filepath.Base(rootLink)) // lookup origin path in extended attributes - if attrBytes, err = xattr.Get(deletedNodePath, xattrs.TrashOriginAttr); err == nil { - origin = string(attrBytes) + if attrBytes, err = xattr.Get(deletedNodeRootPath, xattrs.TrashOriginAttr); err == nil { + origin = filepath.Join(string(attrBytes), path) } else { log.Error().Err(err).Str("trashItem", trashItem).Str("link", link).Str("deletedNodePath", deletedNodePath).Msg("could not read origin path, restoring to /") } diff --git a/pkg/storage/utils/decomposedfs/tree/tree_test.go b/pkg/storage/utils/decomposedfs/tree/tree_test.go index ad83131c3ad..d41d2f530a5 100644 --- a/pkg/storage/utils/decomposedfs/tree/tree_test.go +++ b/pkg/storage/utils/decomposedfs/tree/tree_test.go @@ -115,7 +115,7 @@ var _ = Describe("Tree", func() { _, err := os.Stat(trashPath) Expect(err).ToNot(HaveOccurred()) - _, purgeFunc, err := t.PurgeRecycleItemFunc(env.Ctx, n.ID) + _, purgeFunc, err := t.PurgeRecycleItemFunc(env.Ctx, n.ID, "") Expect(err).ToNot(HaveOccurred()) Expect(purgeFunc()).To(Succeed()) }) @@ -203,7 +203,7 @@ var _ = Describe("Tree", func() { _, err := os.Stat(trashPath) Expect(err).ToNot(HaveOccurred()) - _, purgeFunc, err := t.PurgeRecycleItemFunc(env.Ctx, n.ID) + _, purgeFunc, err := t.PurgeRecycleItemFunc(env.Ctx, n.ID, "") Expect(err).ToNot(HaveOccurred()) Expect(purgeFunc()).To(Succeed()) }) diff --git a/pkg/storage/utils/eosfs/eosfs.go b/pkg/storage/utils/eosfs/eosfs.go index abac5bce973..11f8264b379 100644 --- a/pkg/storage/utils/eosfs/eosfs.go +++ b/pkg/storage/utils/eosfs/eosfs.go @@ -1362,7 +1362,7 @@ func (fs *eosfs) RestoreRevision(ctx context.Context, ref *provider.Reference, r return fs.c.RollbackToVersion(ctx, uid, gid, fn, revisionKey) } -func (fs *eosfs) PurgeRecycleItem(ctx context.Context, key string) error { +func (fs *eosfs) PurgeRecycleItem(ctx context.Context, ref *provider.Reference) error { return errtypes.NotSupported("eosfs: operation not supported") } diff --git a/pkg/storage/utils/localfs/localfs.go b/pkg/storage/utils/localfs/localfs.go index 6d27e6557ff..f53881f8669 100644 --- a/pkg/storage/utils/localfs/localfs.go +++ b/pkg/storage/utils/localfs/localfs.go @@ -1131,8 +1131,8 @@ func (fs *localfs) RestoreRevision(ctx context.Context, ref *provider.Reference, return fs.propagate(ctx, np) } -func (fs *localfs) PurgeRecycleItem(ctx context.Context, key string) error { - rp := fs.wrapRecycleBin(ctx, key) +func (fs *localfs) PurgeRecycleItem(ctx context.Context, ref *provider.Reference) error { + rp := fs.wrapRecycleBin(ctx, ref.ResourceId.OpaqueId) if err := os.Remove(rp); err != nil { return errors.Wrap(err, "localfs: error deleting recycle item")