Skip to content

Commit

Permalink
implement the UpdateStorageSpace method
Browse files Browse the repository at this point in the history
  • Loading branch information
David Christofas committed Oct 14, 2021
1 parent 4885697 commit 64d0de0
Show file tree
Hide file tree
Showing 10 changed files with 199 additions and 84 deletions.
5 changes: 5 additions & 0 deletions changelog/unreleased/update-storage-spaces.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Enhancement: Implement the UpdateStorageSpace method

Added the UpdateStorageSpace method to the decomposedfs.

https://github.com/cs3org/reva/pull/2162
4 changes: 1 addition & 3 deletions internal/grpc/services/storageprovider/storageprovider.go
Original file line number Diff line number Diff line change
Expand Up @@ -489,9 +489,7 @@ func (s *service) ListStorageSpaces(ctx context.Context, req *provider.ListStora
}

func (s *service) UpdateStorageSpace(ctx context.Context, req *provider.UpdateStorageSpaceRequest) (*provider.UpdateStorageSpaceResponse, error) {
return &provider.UpdateStorageSpaceResponse{
Status: status.NewUnimplemented(ctx, errtypes.NotSupported("UpdateStorageSpace not implemented"), "UpdateStorageSpace not implemented"),
}, nil
return s.storage.UpdateStorageSpace(ctx, req)
}

func (s *service) DeleteStorageSpace(ctx context.Context, req *provider.DeleteStorageSpaceRequest) (*provider.DeleteStorageSpaceResponse, error) {
Expand Down
16 changes: 16 additions & 0 deletions pkg/storage/fs/nextcloud/nextcloud.go
Original file line number Diff line number Diff line change
Expand Up @@ -784,3 +784,19 @@ func (nc *StorageDriver) CreateStorageSpace(ctx context.Context, req *provider.C
}
return &respObj, nil
}

// UpdateStorageSpace updates a storage space
func (nc *StorageDriver) UpdateStorageSpace(ctx context.Context, req *provider.UpdateStorageSpaceRequest) (*provider.UpdateStorageSpaceResponse, error) {
bodyStr, _ := json.Marshal(req)
_, respBody, err := nc.do(ctx, Action{"UpdateStorageSpace", string(bodyStr)})
if err != nil {
return nil, err
}
var respObj provider.UpdateStorageSpaceResponse
fmt.Println(string(respBody))
err = json.Unmarshal(respBody, &respObj)
if err != nil {
return nil, err
}
return &respObj, nil
}
5 changes: 5 additions & 0 deletions pkg/storage/fs/owncloud/owncloud.go
Original file line number Diff line number Diff line change
Expand Up @@ -2234,6 +2234,11 @@ func (fs *ocfs) ListStorageSpaces(ctx context.Context, filter []*provider.ListSt
return nil, errtypes.NotSupported("list storage spaces")
}

// UpdateStorageSpace updates a storage space
func (fs *ocfs) UpdateStorageSpace(ctx context.Context, req *provider.UpdateStorageSpaceRequest) (*provider.UpdateStorageSpaceResponse, error) {
return nil, errtypes.NotSupported("update storage space")
}

func (fs *ocfs) propagate(ctx context.Context, leafPath string) error {
var root string
if fs.c.EnableHome {
Expand Down
5 changes: 5 additions & 0 deletions pkg/storage/fs/owncloudsql/owncloudsql.go
Original file line number Diff line number Diff line change
Expand Up @@ -1930,6 +1930,11 @@ func (fs *owncloudsqlfs) ListStorageSpaces(ctx context.Context, filter []*provid
return nil, errtypes.NotSupported("list storage spaces")
}

// UpdateStorageSpace updates a storage space
func (fs *owncloudsqlfs) UpdateStorageSpace(ctx context.Context, req *provider.UpdateStorageSpaceRequest) (*provider.UpdateStorageSpaceResponse, error) {
return nil, errtypes.NotSupported("update storage space")
}

func readChecksumIntoResourceChecksum(ctx context.Context, checksums, algo string, ri *provider.ResourceInfo) {
re := regexp.MustCompile(strings.ToUpper(algo) + `:(.*)`)
matches := re.FindStringSubmatch(checksums)
Expand Down
5 changes: 5 additions & 0 deletions pkg/storage/fs/s3/s3.go
Original file line number Diff line number Diff line change
Expand Up @@ -680,3 +680,8 @@ func (fs *s3FS) RestoreRecycleItem(ctx context.Context, key, path string, restor
func (fs *s3FS) ListStorageSpaces(ctx context.Context, filter []*provider.ListStorageSpacesRequest_Filter) ([]*provider.StorageSpace, error) {
return nil, errtypes.NotSupported("list storage spaces")
}

// UpdateStorageSpace updates a storage space
func (fs *s3FS) UpdateStorageSpace(ctx context.Context, req *provider.UpdateStorageSpaceRequest) (*provider.UpdateStorageSpaceResponse, error) {
return nil, errtypes.NotSupported("update storage space")
}
1 change: 1 addition & 0 deletions pkg/storage/storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ type FS interface {
UnsetArbitraryMetadata(ctx context.Context, ref *provider.Reference, keys []string) error
ListStorageSpaces(ctx context.Context, filter []*provider.ListStorageSpacesRequest_Filter) ([]*provider.StorageSpace, error)
CreateStorageSpace(ctx context.Context, req *provider.CreateStorageSpaceRequest) (*provider.CreateStorageSpaceResponse, error)
UpdateStorageSpace(ctx context.Context, req *provider.UpdateStorageSpaceRequest) (*provider.UpdateStorageSpaceResponse, error)
}

// Registry is the interface that storage registries implement
Expand Down
232 changes: 151 additions & 81 deletions pkg/storage/utils/decomposedfs/spaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ import (
"github.com/pkg/xattr"
)

const (
spaceTypeAny = "*"
spaceIDAny = "*"
)

// CreateStorageSpace creates a storage space
func (fs *Decomposedfs) CreateStorageSpace(ctx context.Context, req *provider.CreateStorageSpaceRequest) (*provider.CreateStorageSpaceResponse, error) {
// spaces will be located by default in the root of the storage.
Expand Down Expand Up @@ -153,8 +158,8 @@ func (fs *Decomposedfs) ListStorageSpaces(ctx context.Context, filter []*provide
// we would not need /nodes/root if access always happened via spaceid+relative path

var (
spaceType = "*"
spaceID = "*"
spaceType = spaceTypeAny
spaceID = spaceIDAny
)

for i := range filter {
Expand Down Expand Up @@ -196,104 +201,84 @@ func (fs *Decomposedfs) ListStorageSpaces(ctx context.Context, filter []*provide
appctx.GetLogger(ctx).Error().Err(err).Str("id", filepath.Base(target)).Msg("could not read node, skipping")
continue
}

spaceType := filepath.Base(filepath.Dir(matches[i]))

owner, err := n.Owner()
if err != nil {
appctx.GetLogger(ctx).Error().Err(err).Interface("node", n).Msg("could not read owner, skipping")
continue
}

// TODO apply more filters

space := &provider.StorageSpace{
// FIXME the driver should know its id move setting the spaceid from the storage provider to the drivers
//Id: &provider.StorageSpaceId{OpaqueId: "1284d238-aa92-42ce-bdc4-0b0000009157!" + n.ID},
Root: &provider.ResourceId{
// FIXME the driver should know its id move setting the spaceid from the storage provider to the drivers
//StorageId: "1284d238-aa92-42ce-bdc4-0b0000009157",
OpaqueId: n.ID,
},
Name: n.Name,
SpaceType: filepath.Base(filepath.Dir(matches[i])),
// Mtime is set either as node.tmtime or as fi.mtime below
}

switch space.SpaceType {
case "share":
if utils.UserEqual(u.Id, owner) {
// do not list shares as spaces for the owner
continue
}
case "project":
sname, err := xattr.Get(n.InternalPath(), xattrs.SpaceNameAttr)
if err != nil {
appctx.GetLogger(ctx).Error().Err(err).Interface("node", n).Msg("could not read space name, attribute not found")
continue
}
space.Name = string(sname)
default:
space.Name = "root"
if spaceType == "share" && utils.UserEqual(u.Id, owner) {
// do not list shares as spaces for the owner
continue
}

// filter out spaces user cannot access (currently based on stat permission)
p, err := n.ReadUserPermissions(ctx, u)
// TODO apply more filters
space, err := fs.storageSpaceFromNode(ctx, n, matches[i], spaceType)
if err != nil {
appctx.GetLogger(ctx).Error().Err(err).Interface("node", n).Msg("could not read permissions, skipping")
continue
}
if !p.Stat {
appctx.GetLogger(ctx).Error().Err(err).Interface("node", n).Msg("could not convert to storage space")
continue
}
spaces = append(spaces, space)
}
}

// fill in user object if the current user is the owner
if utils.UserEqual(u.Id, owner) {
space.Owner = u
} else {
space.Owner = &userv1beta1.User{ // FIXME only return a UserID, not a full blown user object
Id: owner,
}
}
return spaces, nil

// we set the space mtime to the root item mtime
// override the stat mtime with a tmtime if it is present
if tmt, err := n.GetTMTime(); err == nil {
un := tmt.UnixNano()
space.Mtime = &types.Timestamp{
Seconds: uint64(un / 1000000000),
Nanos: uint32(un % 1000000000),
}
} else if fi, err := os.Stat(matches[i]); err == nil {
// fall back to stat mtime
un := fi.ModTime().UnixNano()
space.Mtime = &types.Timestamp{
Seconds: uint64(un / 1000000000),
Nanos: uint32(un % 1000000000),
}
}
}

// quota
v, err := xattr.Get(matches[i], xattrs.QuotaAttr)
if err == nil {
// make sure we have a proper signed int
// we use the same magic numbers to indicate:
// -1 = uncalculated
// -2 = unknown
// -3 = unlimited
if quota, err := strconv.ParseUint(string(v), 10, 64); err == nil {
space.Quota = &provider.Quota{
QuotaMaxBytes: quota,
QuotaMaxFiles: math.MaxUint64, // TODO MaxUInt64? = unlimited? why even max files? 0 = unlimited?
}
} else {
appctx.GetLogger(ctx).Debug().Err(err).Str("nodepath", matches[i]).Msg("could not read quota")
}
}
// UpdateStorageSpace updates a storage space
func (fs *Decomposedfs) UpdateStorageSpace(ctx context.Context, req *provider.UpdateStorageSpaceRequest) (*provider.UpdateStorageSpaceResponse, error) {
space := req.StorageSpace

spaces = append(spaces, space)
_, spaceID, err := utils.SplitStorageSpaceID(space.Id.OpaqueId)
if err != nil {
return nil, err
}

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

if len(matches) != 1 {
return nil, errors.New("multiple spaces found")
}

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")
}

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

if space.Name != "" {
if err := node.SetMetadata(xattrs.SpaceNameAttr, space.Name); err != nil {
return nil, err
}
}

return spaces, nil
if space.Quota != nil {
if err := node.SetMetadata(xattrs.QuotaAttr, strconv.FormatUint(space.Quota.QuotaMaxBytes, 10)); err != nil {
return nil, err
}
}

spaceType := filepath.Base(filepath.Dir(matches[0]))
updated, err := fs.storageSpaceFromNode(ctx, node, matches[0], spaceType)
if err != nil {
return nil, err
}

return &provider.UpdateStorageSpaceResponse{
Status: &v1beta11.Status{Code: v1beta11.Code_CODE_OK},
StorageSpace: updated,
}, nil
}

// createHiddenSpaceFolder bootstraps a storage space root with a hidden ".space" folder used to store space related
Expand Down Expand Up @@ -328,3 +313,88 @@ func (fs *Decomposedfs) createStorageSpace(ctx context.Context, spaceType, nodeI

return nil
}

func (fs *Decomposedfs) storageSpaceFromNode(ctx context.Context, node *node.Node, nodePath, spaceType string) (*provider.StorageSpace, error) {
owner, err := node.Owner()
if err != nil {
return nil, err
}

// TODO apply more filters

space := &provider.StorageSpace{
// FIXME the driver should know its id move setting the spaceid from the storage provider to the drivers
//Id: &provider.StorageSpaceId{OpaqueId: "1284d238-aa92-42ce-bdc4-0b0000009157!" + n.ID},
Root: &provider.ResourceId{
// FIXME the driver should know its id move setting the spaceid from the storage provider to the drivers
//StorageId: "1284d238-aa92-42ce-bdc4-0b0000009157",
OpaqueId: node.ID,
},
Name: node.Name,
SpaceType: spaceType,
// Mtime is set either as node.tmtime or as fi.mtime below
}

switch space.SpaceType {
case "project":
sname, err := xattr.Get(node.InternalPath(), xattrs.SpaceNameAttr)
if err != nil {
return nil, err
}
space.Name = string(sname)
default:
space.Name = "root"
}

user := ctxpkg.ContextMustGetUser(ctx)

// filter out spaces user cannot access (currently based on stat permission)
p, err := node.ReadUserPermissions(ctx, user)
if err != nil {
return nil, err
}
if !p.Stat {
return nil, errors.New("user is not allowed to Stat the space")
}

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

// we set the space mtime to the root item mtime
// override the stat mtime with a tmtime if it is present
if tmt, err := node.GetTMTime(); err == nil {
un := tmt.UnixNano()
space.Mtime = &types.Timestamp{
Seconds: uint64(un / 1000000000),
Nanos: uint32(un % 1000000000),
}
} else if fi, err := os.Stat(nodePath); err == nil {
// fall back to stat mtime
un := fi.ModTime().UnixNano()
space.Mtime = &types.Timestamp{
Seconds: uint64(un / 1000000000),
Nanos: uint32(un % 1000000000),
}
}

// quota
v, err := xattr.Get(nodePath, xattrs.QuotaAttr)
if err == nil {
// make sure we have a proper signed int
// we use the same magic numbers to indicate:
// -1 = uncalculated
// -2 = unknown
// -3 = unlimited
if quota, err := strconv.ParseUint(string(v), 10, 64); err == nil {
space.Quota = &provider.Quota{
QuotaMaxBytes: quota,
QuotaMaxFiles: math.MaxUint64, // TODO MaxUInt64? = unlimited? why even max files? 0 = unlimited?
}
} else {
return nil, err
}
}

return space, nil
}
5 changes: 5 additions & 0 deletions pkg/storage/utils/eosfs/eosfs.go
Original file line number Diff line number Diff line change
Expand Up @@ -1513,6 +1513,11 @@ func (fs *eosfs) ListStorageSpaces(ctx context.Context, filter []*provider.ListS
return nil, errtypes.NotSupported("list storage spaces")
}

// UpdateStorageSpace updates a storage space
func (fs *eosfs) UpdateStorageSpace(ctx context.Context, req *provider.UpdateStorageSpaceRequest) (*provider.UpdateStorageSpaceResponse, error) {
return nil, errtypes.NotSupported("update storage space")
}

func (fs *eosfs) convertToRecycleItem(ctx context.Context, eosDeletedItem *eosclient.DeletedEntry) (*provider.RecycleItem, error) {
path, err := fs.unwrap(ctx, eosDeletedItem.RestorePath)
if err != nil {
Expand Down
5 changes: 5 additions & 0 deletions pkg/storage/utils/localfs/localfs.go
Original file line number Diff line number Diff line change
Expand Up @@ -1264,6 +1264,11 @@ func (fs *localfs) ListStorageSpaces(ctx context.Context, filter []*provider.Lis
return nil, errtypes.NotSupported("list storage spaces")
}

// UpdateStorageSpace updates a storage space
func (fs *localfs) UpdateStorageSpace(ctx context.Context, req *provider.UpdateStorageSpaceRequest) (*provider.UpdateStorageSpaceResponse, error) {
return nil, errtypes.NotSupported("update storage space")
}

func (fs *localfs) propagate(ctx context.Context, leafPath string) error {

var root string
Expand Down

0 comments on commit 64d0de0

Please sign in to comment.