Skip to content

Commit

Permalink
Restoring Spaces (#2458)
Browse files Browse the repository at this point in the history
* add restore functionality to decomposedfs

Signed-off-by: jkoberg <jkoberg@owncloud.com>

* add changelog item

Signed-off-by: jkoberg <jkoberg@owncloud.com>

* find trashed spaces

Signed-off-by: jkoberg <jkoberg@owncloud.com>

* add includeTrashed paramter to ListSpaces call

Signed-off-by: jkoberg <jkoberg@owncloud.com>

* only add * if neccessary

Signed-off-by: jkoberg <jkoberg@owncloud.com>

* move instead delete to make restore possible

Signed-off-by: jkoberg <jkoberg@owncloud.com>

* pass trashed information via Opaque

Signed-off-by: jkoberg <jkoberg@owncloud.com>

* don't return trashed space for non owners

Signed-off-by: jkoberg <jkoberg@owncloud.com>

* revert includeTrashed hack

Signed-off-by: jkoberg <jkoberg@owncloud.com>

* no need to symlink trashed spaces any more

Signed-off-by: jkoberg <jkoberg@owncloud.com>

* bad solution - just wanna paint it green

Signed-off-by: jkoberg <jkoberg@owncloud.com>

* allow listing of deleted spaces

Signed-off-by: jkoberg <jkoberg@owncloud.com>
  • Loading branch information
kobergj authored Feb 4, 2022
1 parent c3bb73f commit fa24ac3
Show file tree
Hide file tree
Showing 3 changed files with 116 additions and 39 deletions.
7 changes: 7 additions & 0 deletions changelog/unreleased/restore-spaces.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Enhancement: Restore spaces that were previously deleted

After the first step of the two step delete process an admin can decide to restore
the space instead of deleting it. This will undo the deletion and
all files and shares are accessible again

https://github.com/cs3org/reva/pull/2457
11 changes: 7 additions & 4 deletions internal/grpc/services/gateway/storageprovider.go
Original file line number Diff line number Diff line change
Expand Up @@ -286,9 +286,11 @@ func (s *svc) UpdateStorageSpace(ctx context.Context, req *provider.UpdateStorag
}, nil
}

id := res.StorageSpace.Root
s.cache.RemoveStat(ctxpkg.ContextMustGetUser(ctx), id)
s.cache.RemoveListStorageProviders(id)
if res.Status.Code == rpc.Code_CODE_OK {
id := res.StorageSpace.Root
s.cache.RemoveStat(ctxpkg.ContextMustGetUser(ctx), id)
s.cache.RemoveListStorageProviders(id)
}
return res, nil
}

Expand Down Expand Up @@ -1089,8 +1091,9 @@ func (s *svc) findSpaces(ctx context.Context, ref *provider.Reference) ([]*regis
}

listReq := &registry.ListStorageProvidersRequest{
Opaque: &typesv1beta1.Opaque{},
Opaque: &typesv1beta1.Opaque{Map: make(map[string]*typesv1beta1.OpaqueEntry)},
}

sdk.EncodeOpaqueMap(listReq.Opaque, filters)

return s.findProvider(ctx, listReq)
Expand Down
137 changes: 102 additions & 35 deletions pkg/storage/utils/decomposedfs/spaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
"path/filepath"
"strconv"
"strings"
"time"

userv1beta1 "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
permissionsv1beta1 "github.com/cs3org/go-cs3apis/cs3/permissions/v1beta1"
Expand Down Expand Up @@ -217,7 +218,8 @@ func (fs *Decomposedfs) ListStorageSpaces(ctx context.Context, filter []*provide

matches := []string{}
for _, spaceType := range spaceTypes {
m, err := filepath.Glob(filepath.Join(fs.o.Root, "spaces", spaceType, nodeID))
path := filepath.Join(fs.o.Root, "spaces", spaceType, nodeID)
m, err := filepath.Glob(path)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -317,10 +319,50 @@ func (fs *Decomposedfs) ListStorageSpaces(ctx context.Context, filter []*provide

// UpdateStorageSpace updates a storage space
func (fs *Decomposedfs) UpdateStorageSpace(ctx context.Context, req *provider.UpdateStorageSpaceRequest) (*provider.UpdateStorageSpaceResponse, error) {
space := req.StorageSpace
var restore bool
if req.Opaque != nil {
_, restore = req.Opaque.Map["restore"]
}

space := req.StorageSpace
_, spaceID, _ := utils.SplitStorageSpaceID(space.Id.OpaqueId)

if restore {
matches, err := filepath.Glob(filepath.Join(fs.o.Root, "spaces", spaceTypeAny, spaceID))
if err != nil {
return nil, err
}

if len(matches) != 1 {
return &provider.UpdateStorageSpaceResponse{
Status: &v1beta11.Status{
Code: v1beta11.Code_CODE_NOT_FOUND,
Message: fmt.Sprintf("restoring space failed: found %d matching spaces", len(matches)),
},
}, nil

}

target, err := os.Readlink(matches[0])
if err != nil {
appctx.GetLogger(ctx).Error().Err(err).Str("match", matches[0]).Msg("could not read link, skipping")
}

n, err := node.ReadNode(ctx, fs.lu, filepath.Base(target))
if err != nil {
return nil, err
}

newnode := *n
newnode.Name = strings.Split(n.Name, node.TrashIDDelimiter)[0]
newnode.Exists = false

err = fs.tp.Move(ctx, n, &newnode)
if err != nil {
return nil, err
}
}

matches, err := filepath.Glob(filepath.Join(fs.o.Root, "spaces", spaceTypeAny, spaceID))
if err != nil {
return nil, err
Expand Down Expand Up @@ -377,8 +419,29 @@ func (fs *Decomposedfs) DeleteStorageSpace(ctx context.Context, req *provider.De
_, purge = opaque.Map["purge"]
}

spaceID := req.Id.OpaqueId

matches, err := filepath.Glob(filepath.Join(fs.o.Root, "spaces", spaceTypeAny, spaceID))
if err != nil {
return err
}

if len(matches) != 1 {
return fmt.Errorf("delete space failed: found %d matching spaces", len(matches))
}

target, err := os.Readlink(matches[0])
if err != nil {
appctx.GetLogger(ctx).Error().Err(err).Str("match", matches[0]).Msg("could not read link, skipping")
}

n, err := node.ReadNode(ctx, fs.lu, filepath.Base(target))
if err != nil {
return err
}

if purge {
if !strings.Contains(req.Id.OpaqueId, node.TrashIDDelimiter) {
if !strings.Contains(n.Name, node.TrashIDDelimiter) {
return errtypes.NewErrtypeFromStatus(status.NewInvalidArg(ctx, "can't purge enabled space"))
}
ip := fs.lu.InternalPath(req.Id.OpaqueId)
Expand All @@ -400,31 +463,27 @@ func (fs *Decomposedfs) DeleteStorageSpace(ctx context.Context, req *provider.De
return fmt.Errorf("delete space failed: found %d matching spaces", len(matches))
}

return os.RemoveAll(matches[0])
}

spaceID := req.Id.OpaqueId

matches, err := filepath.Glob(filepath.Join(fs.o.Root, "spaces", spaceTypeAny, spaceID))
if err != nil {
return err
}
if err := os.RemoveAll(matches[0]); err != nil {
return err
}

if len(matches) != 1 {
return fmt.Errorf("delete space failed: found %d matching spaces", len(matches))
}
matches, err = filepath.Glob(filepath.Join(fs.o.Root, "nodes", "root", req.Id.OpaqueId+node.TrashIDDelimiter+"*"))
if err != nil {
return err
}

target, err := os.Readlink(matches[0])
if err != nil {
appctx.GetLogger(ctx).Error().Err(err).Str("match", matches[0]).Msg("could not read link, skipping")
}
if len(matches) != 1 {
return fmt.Errorf("delete root node failed: found %d matching root nodes", len(matches))
}

n, err := node.ReadNode(ctx, fs.lu, filepath.Base(target))
if err != nil {
return err
return os.RemoveAll(matches[0])
}

err = fs.tp.Delete(ctx, n)
// don't delete - just rename
dn := *n
deletionTime := time.Now().UTC().Format(time.RFC3339Nano)
dn.Name = n.Name + node.TrashIDDelimiter + deletionTime
dn.Exists = false
err = fs.tp.Move(ctx, n, &dn)
if err != nil {
return err
}
Expand All @@ -434,15 +493,9 @@ func (fs *Decomposedfs) DeleteStorageSpace(ctx context.Context, req *provider.De
return err
}

trashPathMatches, err := filepath.Glob(n.InternalPath() + node.TrashIDDelimiter + "*")
if err != nil {
return err
}
if len(trashPathMatches) != 1 {
return fmt.Errorf("delete space failed: found %d matching trashed spaces", len(trashPathMatches))
}
trashPath := trashPathMatches[0]
return os.Symlink(trashPath, filepath.Join(filepath.Dir(matches[0]), filepath.Base(trashPath)))
trashPath := dn.InternalPath()
np := filepath.Join(filepath.Dir(matches[0]), filepath.Base(trashPath))
return os.Symlink(trashPath, np)
}

// createHiddenSpaceFolder bootstraps a storage space root with a hidden ".space" folder used to store space related
Expand Down Expand Up @@ -487,8 +540,15 @@ func (fs *Decomposedfs) storageSpaceFromNode(ctx context.Context, n *node.Node,
if err != nil || !ok {
return nil, errtypes.PermissionDenied(fmt.Sprintf("user %s is not allowed to Stat the space %s", user.Username, n.SpaceRoot.ID))
}
if n.SpaceRoot != nil && strings.Contains(n.SpaceRoot.ID, node.TrashIDDelimiter) {
return nil, errtypes.PermissionDenied(fmt.Sprintf("user %s is not allowed to list deleted spaces %s", user.Username, n.SpaceRoot.ID))

if strings.Contains(n.Name, node.TrashIDDelimiter) {
ok, err := node.NewPermissions(fs.lu).HasPermission(ctx, n, func(p *provider.ResourcePermissions) bool {
// TODO: Which permission do I need to see the space?
return p.AddGrant
})
if err != nil || !ok {
return nil, errtypes.PermissionDenied(fmt.Sprintf("user %s is not allowed to list deleted spaces %s", user.Username, n.SpaceRoot.ID))
}
}
}

Expand Down Expand Up @@ -563,6 +623,13 @@ func (fs *Decomposedfs) storageSpaceFromNode(ctx context.Context, n *node.Node,
// Mtime is set either as node.tmtime or as fi.mtime below
}

if strings.Contains(n.Name, node.TrashIDDelimiter) {
space.Opaque.Map["trashed"] = &types.OpaqueEntry{
Decoder: "plain",
Value: []byte("trashed"),
}
}

space.Owner = &userv1beta1.User{ // FIXME only return a UserID, not a full blown user object
Id: owner,
}
Expand Down

0 comments on commit fa24ac3

Please sign in to comment.