Skip to content

Commit

Permalink
Merge pull request #4533 from dragonchaser/fix-recursive-trashcan-purge
Browse files Browse the repository at this point in the history
Fix recursive trashcan purge
  • Loading branch information
dragonchaser authored Feb 21, 2024
2 parents 3c387be + 3cf3e5c commit 6d5a4d7
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 15 deletions.
6 changes: 6 additions & 0 deletions changelog/unreleased/fix-recusrive-trashcan-purge
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Bugfix: Fix recursive trashcan purge

We have fixed a bug in the trashcan purge process that did not delete folder structures recursively.

https://github.com/cs3org/reva/pull/4533
https://github.com/owncloud/ocis/issues/8473
60 changes: 45 additions & 15 deletions pkg/storage/utils/decomposedfs/tree/tree.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import (
"fmt"
"io"
"io/fs"
iofs "io/fs"
"os"
"path/filepath"
"regexp"
Expand All @@ -42,7 +41,6 @@ import (
"github.com/cs3org/reva/v2/pkg/utils"
"github.com/google/uuid"
"github.com/pkg/errors"
"github.com/rs/zerolog/log"
"go-micro.dev/v4/store"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/trace"
Expand Down Expand Up @@ -172,7 +170,7 @@ func (t *Tree) TouchFile(ctx context.Context, n *node.Node, markprocessing bool,
return errors.Wrap(err, "Decomposedfs: could not remove symlink child entry")
}
}
if errors.Is(err, iofs.ErrNotExist) || link != "../"+n.ID {
if errors.Is(err, fs.ErrNotExist) || link != "../"+n.ID {
relativeNodePath := filepath.Join("../../../../../", lookup.Pathify(n.ID, 4, 2))
if err = os.Symlink(relativeNodePath, childNameLink); err != nil {
return errors.Wrap(err, "Decomposedfs: could not symlink child entry")
Expand Down Expand Up @@ -529,6 +527,8 @@ 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, spaceid, key, trashPath string, targetNode *node.Node) (*node.Node, *node.Node, func() error, error) {
logger := appctx.GetLogger(ctx)

recycleNode, trashItem, deletedNodePath, origin, err := t.readRecycleItem(ctx, spaceid, key, trashPath)
if err != nil {
return nil, nil, nil, err
Expand Down Expand Up @@ -604,7 +604,7 @@ func (t *Tree) RestoreRecycleItemFunc(ctx context.Context, spaceid, key, trashPa
deletePath = filepath.Join(resolvedTrashRoot, trashPath)
}
if err = os.Remove(deletePath); err != nil {
log.Error().Err(err).Str("trashItem", trashItem).Msg("error deleting trash item")
logger.Error().Err(err).Str("trashItem", trashItem).Msg("error deleting trash item")
}

var sizeDiff int64
Expand All @@ -624,13 +624,22 @@ func (t *Tree) RestoreRecycleItemFunc(ctx context.Context, spaceid, key, trashPa

// PurgeRecycleItemFunc returns a node and a function to purge it from the trash
func (t *Tree) PurgeRecycleItemFunc(ctx context.Context, spaceid, key string, path string) (*node.Node, func() error, error) {
logger := appctx.GetLogger(ctx)

rn, trashItem, deletedNodePath, _, err := t.readRecycleItem(ctx, spaceid, key, path)
if err != nil {
return nil, nil, err
}

ts := ""
timeSuffix := strings.SplitN(filepath.Base(deletedNodePath), node.TrashIDDelimiter, 2)
if len(timeSuffix) == 2 {
ts = timeSuffix[1]
}

fn := func() error {
if err := t.removeNode(ctx, deletedNodePath, rn); err != nil {

if err := t.removeNode(ctx, deletedNodePath, ts, rn); err != nil {
return err
}

Expand All @@ -644,7 +653,7 @@ func (t *Tree) PurgeRecycleItemFunc(ctx context.Context, spaceid, key string, pa
deletePath = filepath.Join(resolvedTrashRoot, path)
}
if err = os.Remove(deletePath); err != nil {
log.Error().Err(err).Str("deletePath", deletePath).Msg("error deleting trash item")
logger.Error().Err(err).Str("deletePath", deletePath).Msg("error deleting trash item")
return err
}

Expand All @@ -654,30 +663,49 @@ func (t *Tree) PurgeRecycleItemFunc(ctx context.Context, spaceid, key string, pa
return rn, fn, nil
}

func (t *Tree) removeNode(ctx context.Context, path string, n *node.Node) error {
func (t *Tree) removeNode(ctx context.Context, path, timeSuffix string, n *node.Node) error {
logger := appctx.GetLogger(ctx)

if timeSuffix != "" {
n.ID = n.ID + node.TrashIDDelimiter + timeSuffix
}

if n.IsDir(ctx) {
item, err := t.ListFolder(ctx, n)
if err != nil {
logger.Error().Err(err).Str("path", path).Msg("error listing folder")
} else {
for _, child := range item {
if err := t.removeNode(ctx, child.InternalPath(), "", child); err != nil {
return err
}
}
}
}

// delete the actual node
if err := utils.RemoveItem(path); err != nil {
log.Error().Err(err).Str("path", path).Msg("error purging node")
logger.Error().Err(err).Str("path", path).Msg("error purging node")
return err
}

if err := t.lookup.MetadataBackend().Purge(path); err != nil {
log.Error().Err(err).Str("path", t.lookup.MetadataBackend().MetadataPath(path)).Msg("error purging node metadata")
logger.Error().Err(err).Str("path", t.lookup.MetadataBackend().MetadataPath(path)).Msg("error purging node metadata")
return err
}

// delete blob from blobstore
if n.BlobID != "" {
if err := t.DeleteBlob(n); err != nil {
log.Error().Err(err).Str("blobID", n.BlobID).Msg("error purging nodes blob")
logger.Error().Err(err).Str("blobID", n.BlobID).Msg("error purging nodes blob")
return err
}
}

// delete revisions
revs, err := filepath.Glob(n.InternalPath() + node.RevisionIDDelimiter + "*")
if err != nil {
log.Error().Err(err).Str("path", n.InternalPath()+node.RevisionIDDelimiter+"*").Msg("glob failed badly")
logger.Error().Err(err).Str("path", n.InternalPath()+node.RevisionIDDelimiter+"*").Msg("glob failed badly")
return err
}
for _, rev := range revs {
Expand All @@ -687,18 +715,18 @@ func (t *Tree) removeNode(ctx context.Context, path string, n *node.Node) error

bID, err := t.lookup.ReadBlobIDAttr(ctx, rev)
if err != nil {
log.Error().Err(err).Str("revision", rev).Msg("error reading blobid attribute")
logger.Error().Err(err).Str("revision", rev).Msg("error reading blobid attribute")
return err
}

if err := utils.RemoveItem(rev); err != nil {
log.Error().Err(err).Str("revision", rev).Msg("error removing revision node")
logger.Error().Err(err).Str("revision", rev).Msg("error removing revision node")
return err
}

if bID != "" {
if err := t.DeleteBlob(&node.Node{SpaceID: n.SpaceID, BlobID: bID}); err != nil {
log.Error().Err(err).Str("revision", rev).Str("blobID", bID).Msg("error removing revision node blob")
logger.Error().Err(err).Str("revision", rev).Str("blobID", bID).Msg("error removing revision node blob")
return err
}
}
Expand Down Expand Up @@ -761,6 +789,8 @@ var nodeIDRegep = regexp.MustCompile(`.*/nodes/([^.]*).*`)

// TODO refactor the returned params into Node properties? would make all the path transformations go away...
func (t *Tree) readRecycleItem(ctx context.Context, spaceID, key, path string) (recycleNode *node.Node, trashItem string, deletedNodePath string, origin string, err error) {
logger := appctx.GetLogger(ctx)

if key == "" {
return nil, "", "", "", errtypes.InternalError("key is empty")
}
Expand Down Expand Up @@ -823,7 +853,7 @@ func (t *Tree) readRecycleItem(ctx context.Context, spaceID, key, path string) (
if attrBytes, err = backend.Get(ctx, resolvedTrashItem, prefixes.TrashOriginAttr); err == nil {
origin = filepath.Join(string(attrBytes), path)
} else {
log.Error().Err(err).Str("trashItem", trashItem).Str("deletedNodePath", deletedNodePath).Msg("could not read origin path, restoring to /")
logger.Error().Err(err).Str("trashItem", trashItem).Str("deletedNodePath", deletedNodePath).Msg("could not read origin path, restoring to /")
}

return
Expand Down

0 comments on commit 6d5a4d7

Please sign in to comment.