Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Grant id in mountpoint #2664

Merged
merged 8 commits into from
Mar 29, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions changelog/unreleased/sharing-fix-add-grantID.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Enhancement: Add grantID to mountpoint

We distinguish between the mountpoint of a share and the grant where the original file is located on the storage.

https://github.com/cs3org/reva/pull/2664
1 change: 0 additions & 1 deletion internal/grpc/services/gateway/gateway.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import (
"strings"

gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"

"github.com/cs3org/reva/v2/pkg/errtypes"
"github.com/cs3org/reva/v2/pkg/rgrpc"
"github.com/cs3org/reva/v2/pkg/sharedconf"
Expand Down
9 changes: 5 additions & 4 deletions internal/grpc/services/gateway/usershareprovider.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import (
"github.com/cs3org/reva/v2/pkg/errtypes"
"github.com/cs3org/reva/v2/pkg/rgrpc/status"
"github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool"
"github.com/cs3org/reva/v2/pkg/share"
"github.com/cs3org/reva/v2/pkg/storage/utils/grants"
rtrace "github.com/cs3org/reva/v2/pkg/trace"
"github.com/pkg/errors"
Expand Down Expand Up @@ -166,7 +167,7 @@ func (s *svc) ListReceivedShares(ctx context.Context, req *collaboration.ListRec
// TODO: This is a hack for now.
// Can we do that cleaner somehow?
// The `ListStorageSpaces` method in sharesstorageprovider/sharesstorageprovider.go needs the etags.
shareEtags := make(map[string]string, len(res.Shares))
shareMetaData := make(map[string]share.Metadata, len(res.Shares))
for _, rs := range res.Shares {
sRes, err := s.Stat(ctx, &provider.StatRequest{Ref: &provider.Reference{ResourceId: rs.Share.ResourceId}})
if err != nil {
Expand All @@ -182,10 +183,10 @@ func (s *svc) ListReceivedShares(ctx context.Context, req *collaboration.ListRec
Msg("ListRecievedShares: failed to stat the resource")
continue
}
shareEtags[rs.Share.Id.OpaqueId] = sRes.Info.Etag
shareMetaData[rs.Share.Id.OpaqueId] = share.Metadata{ETag: sRes.Info.Etag, Mtime: sRes.Info.Mtime}
}

marshalled, err := json.Marshal(shareEtags)
marshalled, err := json.Marshal(shareMetaData)
if err != nil {
logger.Error().
Err(err).
Expand All @@ -197,7 +198,7 @@ func (s *svc) ListReceivedShares(ctx context.Context, req *collaboration.ListRec
Map: map[string]*typesv1beta1.OpaqueEntry{},
}
}
opaque.Map["etags"] = &typesv1beta1.OpaqueEntry{
opaque.Map["shareMetadata"] = &typesv1beta1.OpaqueEntry{
Decoder: "json",
Value: marshalled,
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
"path/filepath"
"strings"

"github.com/cs3org/reva/v2/pkg/share"
"google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
gstatus "google.golang.org/grpc/status"
Expand Down Expand Up @@ -359,24 +360,13 @@ func (s *service) ListStorageSpaces(ctx context.Context, req *provider.ListStora
}

var receivedShares []*collaboration.ReceivedShare
var shareEtags map[string]string
var shareMd map[string]share.Metadata
var err error
if fetchShares {
lsRes, err := s.gateway.ListReceivedShares(ctx, &collaboration.ListReceivedSharesRequest{
// FIXME filter by received shares for resource id - listing all shares is tooo expensive!
})
receivedShares, shareMd, err = s.fetchShares(ctx)
if err != nil {
return nil, errors.Wrap(err, "sharesstorageprovider: error calling ListReceivedSharesRequest")
}
if lsRes.Status.Code != rpc.Code_CODE_OK {
return nil, fmt.Errorf("sharesstorageprovider: error calling ListReceivedSharesRequest")
}
receivedShares = lsRes.Shares
if lsRes.Opaque != nil {
if entry, ok := lsRes.Opaque.Map["etags"]; ok {
// If we can't get the etags thats fine, just continue.
_ = json.Unmarshal(entry.Value, &shareEtags)
}
}
}

res := &provider.ListStorageSpacesResponse{
Expand All @@ -390,39 +380,29 @@ func (s *service) ListStorageSpaces(ctx context.Context, req *provider.ListStora
OpaqueId: utils.ShareStorageProviderID,
}
if spaceID == nil || utils.ResourceIDEqual(virtualRootID, spaceID) {
var earliestShare *collaboration.Share
// Lookup the last changed received share and use its etag for the share jail.
for _, rs := range receivedShares {
current := rs.Share
switch {
case earliestShare == nil:
earliestShare = current
case current.Mtime.Seconds > earliestShare.Mtime.Seconds:
earliestShare = current
case current.Mtime.Seconds == earliestShare.Mtime.Seconds &&
current.Mtime.Nanos > earliestShare.Mtime.Nanos:
earliestShare = current
}
}

earliestShare, atLeastOneAccepted := findEarliestShare(receivedShares, shareMd)
var opaque *typesv1beta1.Opaque
if earliestShare != nil {
if etag, ok := shareEtags[earliestShare.Id.OpaqueId]; ok {
opaque = utils.AppendPlainToOpaque(opaque, "etag", etag)
if md, ok := shareMd[earliestShare.Id.OpaqueId]; ok {
opaque = utils.AppendPlainToOpaque(opaque, "etag", md.ETag)
}
}
space := &provider.StorageSpace{
Opaque: opaque,
Id: &provider.StorageSpaceId{
OpaqueId: virtualRootID.StorageId + "!" + virtualRootID.OpaqueId,
},
SpaceType: "virtual",
//Owner: &userv1beta1.User{Id: receivedShare.Share.Owner}, // FIXME actually, the mount point belongs to the recipient
// the sharesstorageprovider keeps track of mount points
Root: virtualRootID,
Name: "Shares Jail",
// only display the shares jail if we have accepted shares
if atLeastOneAccepted {
opaque = utils.AppendPlainToOpaque(opaque, "spaceAlias", "virtual/shares")
space := &provider.StorageSpace{
Opaque: opaque,
Id: &provider.StorageSpaceId{
OpaqueId: virtualRootID.StorageId + "!" + virtualRootID.OpaqueId,
},
SpaceType: "virtual",
//Owner: &userv1beta1.User{Id: receivedShare.Share.Owner}, // FIXME actually, the mount point belongs to the recipient
// the sharesstorageprovider keeps track of mount points
Root: virtualRootID,
Name: "Shares Jail",
}
res.StorageSpaces = append(res.StorageSpaces, space)
}
res.StorageSpaces = append(res.StorageSpaces, space)
}
case "grant":
for _, receivedShare := range receivedShares {
Expand All @@ -433,8 +413,8 @@ func (s *service) ListStorageSpaces(ctx context.Context, req *provider.ListStora
continue
}
var opaque *typesv1beta1.Opaque
if etag, ok := shareEtags[receivedShare.Share.Id.OpaqueId]; ok {
opaque = utils.AppendPlainToOpaque(opaque, "etag", etag)
if md, ok := shareMd[receivedShare.Share.Id.OpaqueId]; ok {
opaque = utils.AppendPlainToOpaque(opaque, "etag", md.ETag)
}
// we know a grant for this resource
space := &provider.StorageSpace{
Expand Down Expand Up @@ -474,9 +454,15 @@ func (s *service) ListStorageSpaces(ctx context.Context, req *provider.ListStora
}
}
var opaque *typesv1beta1.Opaque
if etag, ok := shareEtags[receivedShare.Share.Id.OpaqueId]; ok {
opaque = utils.AppendPlainToOpaque(opaque, "etag", etag)
if md, ok := shareMd[receivedShare.Share.Id.OpaqueId]; ok {
opaque = utils.AppendPlainToOpaque(opaque, "etag", md.ETag)
}
// add the resourceID for the grant
if receivedShare.Share.ResourceId != nil {
opaque = utils.AppendPlainToOpaque(opaque, "grantStorageID", receivedShare.Share.ResourceId.StorageId)
opaque = utils.AppendPlainToOpaque(opaque, "grantOpaqueID", receivedShare.Share.ResourceId.OpaqueId)
}

space := &provider.StorageSpace{
Opaque: opaque,
Id: &provider.StorageSpaceId{
Expand All @@ -492,6 +478,7 @@ func (s *service) ListStorageSpaces(ctx context.Context, req *provider.ListStora
// for now use the name from the share to override the name determined by stat
if receivedShare.MountPoint != nil {
space.Name = receivedShare.MountPoint.Path
space.Opaque = utils.AppendPlainToOpaque(space.Opaque, "spaceAlias", space.SpaceType+"/"+strings.ReplaceAll(strings.ToLower(space.Name), " ", "-"))
}

// what if we don't have a name?
Expand Down Expand Up @@ -670,7 +657,11 @@ func (s *service) Unlock(ctx context.Context, req *provider.UnlockRequest) (*pro

func (s *service) Stat(ctx context.Context, req *provider.StatRequest) (*provider.StatResponse, error) {
if isVirtualRoot(req.Ref.ResourceId) && (req.Ref.Path == "" || req.Ref.Path == ".") {
// The root is empty, it is filled by mountpoints
receivedShares, shareMd, err := s.fetchShares(ctx)
if err != nil {
return nil, err
}
earliestShare, _ := findEarliestShare(receivedShares, shareMd)
return &provider.StatResponse{
Status: status.NewOK(ctx),
Info: &provider.ResourceInfo{
Expand All @@ -687,13 +678,14 @@ func (s *service) Stat(ctx context.Context, req *provider.StatRequest) (*provide
OpaqueId: utils.ShareStorageProviderID,
},
Type: provider.ResourceType_RESOURCE_TYPE_CONTAINER,
Mtime: &typesv1beta1.Timestamp{},
Mtime: shareMd[earliestShare.Id.OpaqueId].Mtime,
Path: "/",
MimeType: "httpd/unix-directory",
Size: 0,
PermissionSet: &provider.ResourcePermissions{
// TODO
},
Etag: shareMd[earliestShare.Id.OpaqueId].ETag,
},
}, nil
}
Expand Down Expand Up @@ -939,3 +931,59 @@ func (s *service) rejectReceivedShare(ctx context.Context, receivedShare *collab

return errtypes.NewErrtypeFromStatus(res.Status)
}

func (s *service) fetchShares(ctx context.Context) ([]*collaboration.ReceivedShare, map[string]share.Metadata, error) {
lsRes, err := s.gateway.ListReceivedShares(ctx, &collaboration.ListReceivedSharesRequest{
// FIXME filter by received shares for resource id - listing all shares is tooo expensive!
})
if err != nil {
return nil, nil, errors.Wrap(err, "sharesstorageprovider: error calling ListReceivedSharesRequest")
}
if lsRes.Status.Code != rpc.Code_CODE_OK {
return nil, nil, fmt.Errorf("sharesstorageprovider: error calling ListReceivedSharesRequest")
}
receivedShares := lsRes.Shares

var shareMd map[string]share.Metadata
if lsRes.Opaque != nil {
if entry, ok := lsRes.Opaque.Map["shareMetadata"]; ok {
// If we can't get the etags thats fine, just continue.
_ = json.Unmarshal(entry.Value, &shareMd)
}
}
return receivedShares, shareMd, nil
}

func findEarliestShare(receivedShares []*collaboration.ReceivedShare, shareMd map[string]share.Metadata) (earliestShare *collaboration.Share, atLeastOneAccepted bool) {
for _, rs := range receivedShares {
var hasCurrentMd bool
var hasEarliestMd bool

current := rs.Share
if rs.State == collaboration.ShareState_SHARE_STATE_ACCEPTED {
atLeastOneAccepted = true
}

// We cannot assume that every share has metadata
if current.Id != nil {
_, hasCurrentMd = shareMd[current.Id.OpaqueId]
}
if earliestShare != nil && earliestShare.Id != nil {
_, hasEarliestMd = shareMd[earliestShare.Id.OpaqueId]
}

switch {
case earliestShare == nil:
earliestShare = current
// ignore if one of the shares has no metadata
case !hasEarliestMd || !hasCurrentMd:
continue
case shareMd[current.Id.OpaqueId].Mtime.Seconds > shareMd[earliestShare.Id.OpaqueId].Mtime.Seconds:
earliestShare = current
case shareMd[current.Id.OpaqueId].Mtime.Seconds == shareMd[earliestShare.Id.OpaqueId].Mtime.Seconds &&
shareMd[current.Id.OpaqueId].Mtime.Nanos > shareMd[earliestShare.Id.OpaqueId].Mtime.Nanos:
earliestShare = current
}
}
return earliestShare, atLeastOneAccepted
}
7 changes: 7 additions & 0 deletions pkg/share/share.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
userv1beta1 "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
collaboration "github.com/cs3org/go-cs3apis/cs3/sharing/collaboration/v1beta1"
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
types "github.com/cs3org/go-cs3apis/cs3/types/v1beta1"
"github.com/cs3org/reva/v2/internal/http/services/owncloud/ocs/conversions"
"github.com/cs3org/reva/v2/pkg/utils"
"google.golang.org/genproto/protobuf/field_mask"
Expand All @@ -37,6 +38,12 @@ const (

//go:generate mockery -name Manager

// Metadata contains Metadata for a share
type Metadata struct {
ETag string
Mtime *types.Timestamp
}

// Manager is the interface that manipulates shares.
type Manager interface {
// Create a new share in fn with the given acl.
Expand Down