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

ocdav: skip space lookup on spaces propfind #2977

Merged
merged 24 commits into from
Jul 6, 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
8 changes: 8 additions & 0 deletions changelog/unreleased/skip-space-lookup-on-space-propfind.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
Enhancement: skip space lookup on space propfind

We now construct the space id from the /dav/spaces URL intead of making a request to the registry.

https://github.com/cs3org/reva/pull/2977
https://github.com/owncloud/ocis/issues/1277
https://github.com/owncloud/ocis/issues/2144
https://github.com/owncloud/ocis/issues/3073
8 changes: 7 additions & 1 deletion internal/grpc/services/gateway/storageprovider.go
Original file line number Diff line number Diff line change
Expand Up @@ -837,7 +837,12 @@ func (s *svc) Stat(ctx context.Context, req *provider.StatRequest) (*provider.St
}, nil
}

return c.Stat(ctx, &provider.StatRequest{Opaque: req.Opaque, Ref: ref, ArbitraryMetadataKeys: req.ArbitraryMetadataKeys})
return c.Stat(ctx, &provider.StatRequest{
Opaque: req.Opaque,
Ref: ref,
ArbitraryMetadataKeys: req.ArbitraryMetadataKeys,
FieldMask: req.FieldMask,
})
}

func (s *svc) ListContainerStream(_ *provider.ListContainerStreamRequest, _ gateway.GatewayAPI_ListContainerStreamServer) error {
Expand All @@ -858,6 +863,7 @@ func (s *svc) ListContainer(ctx context.Context, req *provider.ListContainerRequ
Opaque: req.Opaque,
Ref: ref,
ArbitraryMetadataKeys: req.ArbitraryMetadataKeys,
FieldMask: req.FieldMask,
})
}

Expand Down
23 changes: 18 additions & 5 deletions internal/grpc/services/gateway/storageprovidercache.go
Original file line number Diff line number Diff line change
Expand Up @@ -229,24 +229,37 @@ type cachedAPIClient struct {
// generates a user specific key pointing to ref - used for statcache
// a key looks like: uid:1234-1233!sid:5678-5677!oid:9923-9934!path:/path/to/source
// as you see it adds "uid:"/"sid:"/"oid:" prefixes to the uuids so they can be differentiated
func statKey(user *userpb.User, ref *provider.Reference, metaDataKeys []string) string {
func statKey(user *userpb.User, ref *provider.Reference, metaDataKeys, fieldMaskPaths []string) string {
if ref == nil || ref.ResourceId == nil || ref.ResourceId.StorageId == "" {
return ""
}

key := "uid:" + user.Id.OpaqueId + "!sid:" + ref.ResourceId.StorageId + "!oid:" + ref.ResourceId.OpaqueId + "!path:" + ref.Path
key := strings.Builder{}
key.WriteString("uid:")
key.WriteString(user.Id.OpaqueId)
key.WriteString("!sid:")
key.WriteString(ref.ResourceId.StorageId)
key.WriteString("!oid:")
key.WriteString(ref.ResourceId.OpaqueId)
key.WriteString("!path:")
key.WriteString(ref.Path)
for _, k := range metaDataKeys {
key += "!mdk:" + k
key.WriteString("!mdk:")
key.WriteString(k)
}
for _, p := range fieldMaskPaths {
key.WriteString("!fmp:")
key.WriteString(p)
}

return key
return key.String()
}

// Stat looks in cache first before forwarding to storage provider
func (c *cachedAPIClient) Stat(ctx context.Context, in *provider.StatRequest, opts ...grpc.CallOption) (*provider.StatResponse, error) {
cache := c.caches[stat]

key := statKey(ctxpkg.ContextMustGetUser(ctx), in.Ref, in.ArbitraryMetadataKeys)
key := statKey(ctxpkg.ContextMustGetUser(ctx), in.GetRef(), in.GetArbitraryMetadataKeys(), in.GetFieldMask().GetPaths())
if key != "" {
s := &provider.StatResponse{}
if err := pullFromCache(cache, key, s); err == nil {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import (
"path/filepath"
"strings"

"github.com/cs3org/reva/v2/pkg/share"
"github.com/cs3org/reva/v2/pkg/storagespace"
"google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
Expand Down Expand Up @@ -369,10 +368,10 @@ func (s *service) ListStorageSpaces(ctx context.Context, req *provider.ListStora
}

var receivedShares []*collaboration.ReceivedShare
var shareMd map[string]share.Metadata
var shareInfo map[string]*provider.ResourceInfo
var err error
if fetchShares {
receivedShares, shareMd, err = s.fetchShares(ctx)
receivedShares, shareInfo, err = s.fetchShares(ctx)
if err != nil {
return nil, errors.Wrap(err, "sharesstorageprovider: error calling ListReceivedSharesRequest")
}
Expand All @@ -389,13 +388,13 @@ func (s *service) ListStorageSpaces(ctx context.Context, req *provider.ListStora
OpaqueId: utils.ShareStorageProviderID,
}
if spaceID == nil || isShareJailRoot(spaceID) {
earliestShare, atLeastOneAccepted := findEarliestShare(receivedShares, shareMd)
earliestShare, atLeastOneAccepted := findEarliestShare(receivedShares, shareInfo)
var opaque *typesv1beta1.Opaque
var mtime *typesv1beta1.Timestamp
if earliestShare != nil {
if md, ok := shareMd[earliestShare.Id.OpaqueId]; ok {
mtime = md.Mtime
opaque = utils.AppendPlainToOpaque(opaque, "etag", md.ETag)
if info, ok := shareInfo[earliestShare.Id.OpaqueId]; ok {
mtime = info.Mtime
opaque = utils.AppendPlainToOpaque(opaque, "etag", info.Etag)
}
}
// only display the shares jail if we have accepted shares
Expand Down Expand Up @@ -424,20 +423,16 @@ func (s *service) ListStorageSpaces(ctx context.Context, req *provider.ListStora
// none of our business
continue
}
var opaque *typesv1beta1.Opaque
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{
Opaque: opaque,
Id: &provider.StorageSpaceId{
OpaqueId: storagespace.FormatResourceID(*root),
},
SpaceType: "grant",
Owner: &userv1beta1.User{Id: receivedShare.Share.Owner},
// the sharesstorageprovider keeps track of mount points
Root: root,
Root: root,
RootInfo: shareInfo[receivedShare.Share.Id.OpaqueId],
}

res.StorageSpaces = append(res.StorageSpaces, space)
Expand Down Expand Up @@ -465,9 +460,7 @@ func (s *service) ListStorageSpaces(ctx context.Context, req *provider.ListStora
}
}
var opaque *typesv1beta1.Opaque
if md, ok := shareMd[receivedShare.Share.Id.OpaqueId]; ok {
opaque = utils.AppendPlainToOpaque(opaque, "etag", md.ETag)
} else {
if _, ok := shareInfo[receivedShare.Share.Id.OpaqueId]; !ok {
// we could not stat the share, skip it
continue
}
Expand All @@ -490,7 +483,8 @@ func (s *service) ListStorageSpaces(ctx context.Context, req *provider.ListStora
SpaceType: "mountpoint",
Owner: &userv1beta1.User{Id: receivedShare.Share.Owner}, // FIXME actually, the mount point belongs to the recipient
// the sharesstorageprovider keeps track of mount points
Root: root,
Root: root,
RootInfo: shareInfo[receivedShare.Share.Id.OpaqueId],
}

// TODO in the future the spaces registry will handle the alias for share spaces.
Expand Down Expand Up @@ -711,7 +705,7 @@ func (s *service) Stat(ctx context.Context, req *provider.StatRequest) (*provide
PermissionSet: &provider.ResourcePermissions{
// TODO
},
Etag: shareMd[earliestShare.Id.OpaqueId].ETag,
Etag: shareMd[earliestShare.Id.OpaqueId].Etag,
Owner: owner.Id,
},
}, nil
Expand Down Expand Up @@ -1031,7 +1025,7 @@ 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) {
func (s *service) fetchShares(ctx context.Context) ([]*collaboration.ReceivedShare, map[string]*provider.ResourceInfo, error) {
lsRes, err := s.sharesProviderClient.ListReceivedShares(ctx, &collaboration.ListReceivedSharesRequest{
// FIXME filter by received shares for resource id - listing all shares is tooo expensive!
})
Expand All @@ -1042,7 +1036,7 @@ func (s *service) fetchShares(ctx context.Context) ([]*collaboration.ReceivedSha
return nil, nil, fmt.Errorf("sharesstorageprovider: error calling ListReceivedSharesRequest")
}

shareMetaData := make(map[string]share.Metadata, len(lsRes.Shares))
shareMetaData := make(map[string]*provider.ResourceInfo, len(lsRes.Shares))
for _, rs := range lsRes.Shares {
// only stat accepted shares
if rs.State != collaboration.ShareState_SHARE_STATE_ACCEPTED {
Expand All @@ -1063,13 +1057,13 @@ func (s *service) fetchShares(ctx context.Context) ([]*collaboration.ReceivedSha
Msg("ListRecievedShares: failed to stat the resource")
continue
}
shareMetaData[rs.Share.Id.OpaqueId] = share.Metadata{ETag: sRes.Info.Etag, Mtime: sRes.Info.Mtime}
shareMetaData[rs.Share.Id.OpaqueId] = sRes.Info
}

return lsRes.Shares, shareMetaData, nil
}

func findEarliestShare(receivedShares []*collaboration.ReceivedShare, shareMd map[string]share.Metadata) (earliestShare *collaboration.Share, atLeastOneAccepted bool) {
func findEarliestShare(receivedShares []*collaboration.ReceivedShare, shareInfo map[string]*provider.ResourceInfo) (earliestShare *collaboration.Share, atLeastOneAccepted bool) {
for _, rs := range receivedShares {
var hasCurrentMd bool
var hasEarliestMd bool
Expand All @@ -1081,10 +1075,10 @@ func findEarliestShare(receivedShares []*collaboration.ReceivedShare, shareMd ma

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

switch {
Expand All @@ -1093,10 +1087,10 @@ func findEarliestShare(receivedShares []*collaboration.ReceivedShare, shareMd ma
// 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:
case shareInfo[current.Id.OpaqueId].Mtime.Seconds > shareInfo[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:
case shareInfo[current.Id.OpaqueId].Mtime.Seconds == shareInfo[earliestShare.Id.OpaqueId].Mtime.Seconds &&
shareInfo[current.Id.OpaqueId].Mtime.Nanos > shareInfo[earliestShare.Id.OpaqueId].Mtime.Nanos:
earliestShare = current
}
}
Expand Down
8 changes: 4 additions & 4 deletions internal/grpc/services/storageprovider/storageprovider.go
Original file line number Diff line number Diff line change
Expand Up @@ -672,7 +672,7 @@ func (s *service) Delete(ctx context.Context, req *provider.DeleteRequest) (*pro
}
}

md, err := s.storage.GetMD(ctx, req.Ref, []string{})
md, err := s.storage.GetMD(ctx, req.Ref, []string{}, []string{"id"})
if err != nil {
return &provider.DeleteResponse{
Status: status.NewStatusFromErrType(ctx, "can't stat resource to delete", err),
Expand Down Expand Up @@ -718,7 +718,7 @@ func (s *service) Stat(ctx context.Context, req *provider.StatRequest) (*provide
Value: attribute.StringValue(req.Ref.String()),
})

md, err := s.storage.GetMD(ctx, req.Ref, req.ArbitraryMetadataKeys)
md, err := s.storage.GetMD(ctx, req.GetRef(), req.GetArbitraryMetadataKeys(), req.GetFieldMask().GetPaths())
if err != nil {
return &provider.StatResponse{
Status: status.NewStatusFromErrType(ctx, "stat", err),
Expand Down Expand Up @@ -747,7 +747,7 @@ func (s *service) ListContainerStream(req *provider.ListContainerStreamRequest,
ctx := ss.Context()
log := appctx.GetLogger(ctx)

mds, err := s.storage.ListFolder(ctx, req.Ref, req.ArbitraryMetadataKeys)
mds, err := s.storage.ListFolder(ctx, req.GetRef(), req.GetArbitraryMetadataKeys(), req.GetFieldMask().GetPaths())
if err != nil {
var st *rpc.Status
switch err.(type) {
Expand Down Expand Up @@ -792,7 +792,7 @@ func (s *service) ListContainer(ctx context.Context, req *provider.ListContainer
providerID := unwrapProviderID(req.Ref.GetResourceId())
defer rewrapProviderID(req.Ref.GetResourceId(), providerID)

mds, err := s.storage.ListFolder(ctx, req.Ref, req.ArbitraryMetadataKeys)
mds, err := s.storage.ListFolder(ctx, req.GetRef(), req.GetArbitraryMetadataKeys(), req.GetFieldMask().GetPaths())
res := &provider.ListContainerResponse{
Status: status.NewStatusFromErrType(ctx, "list container", err),
Infos: mds,
Expand Down
8 changes: 3 additions & 5 deletions internal/http/services/owncloud/ocdav/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,7 @@ func (s *svc) handleDelete(ctx context.Context, w http.ResponseWriter, r *http.R
w.WriteHeader(http.StatusNoContent)
case rpc.Code_CODE_NOT_FOUND:
w.WriteHeader(http.StatusNotFound)
// TODO path might be empty or relative...
m := fmt.Sprintf("Resource %v not found", ref.Path)
m := "Resource not found" // mimic the oc10 error message
b, err := errors.Marshal(http.StatusNotFound, m, "")
errors.HandleWebdavError(&log, w, b, err)
case rpc.Code_CODE_PERMISSION_DENIED:
Expand All @@ -119,12 +118,11 @@ func (s *svc) handleDelete(ctx context.Context, w http.ResponseWriter, r *http.R
return
}
if sRes.Status.Code != rpc.Code_CODE_OK {
// return not found error so we dont leak existence of a file
// return not found error so we do not leak existence of a file
// TODO hide permission failed for users without access in every kind of request
// TODO should this be done in the driver?
status = http.StatusNotFound
// TODO path might be empty or relative...
m = fmt.Sprintf("%s not fount", ref.Path)
m = "Resource not found" // mimic the oc10 error message
}
w.WriteHeader(status)
b, err := errors.Marshal(status, m, "")
Expand Down
17 changes: 3 additions & 14 deletions internal/http/services/owncloud/ocdav/locks.go
Original file line number Diff line number Diff line change
Expand Up @@ -400,23 +400,12 @@ func (s *svc) handleSpacesLock(w http.ResponseWriter, r *http.Request, spaceID s

span.SetAttributes(attribute.String("component", "ocdav"))

client, err := s.getClient()
ref, err := spacelookup.MakeStorageSpaceReference(spaceID, r.URL.Path)
if err != nil {
return http.StatusInternalServerError, err
return http.StatusBadRequest, fmt.Errorf("invalid space id")
}

// retrieve a specific storage space
space, cs3Status, err := spacelookup.LookUpStorageSpaceByID(ctx, client, spaceID)
if err != nil {
return http.StatusInternalServerError, err
}
if cs3Status.Code != rpc.Code_CODE_OK {
return http.StatusInternalServerError, errtypes.NewErrtypeFromStatus(cs3Status)
}

ref := spacelookup.MakeRelativeReference(space, r.URL.Path, true)

return s.lockReference(ctx, w, r, ref)
return s.lockReference(ctx, w, r, &ref)
}

func (s *svc) lockReference(ctx context.Context, w http.ResponseWriter, r *http.Request, ref *provider.Reference) (retStatus int, retErr error) {
Expand Down
4 changes: 2 additions & 2 deletions internal/http/services/owncloud/ocdav/mkcol.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,10 +136,10 @@ func (s *svc) handleMkcol(ctx context.Context, w http.ResponseWriter, r *http.Re
return http.StatusInternalServerError, err
}
if sRes.Status.Code != rpc.Code_CODE_OK {
// return not found error so we dont leak existence of a file
// return not found error so we do not leak existence of a file
// TODO hide permission failed for users without access in every kind of request
// TODO should this be done in the driver?
return http.StatusNotFound, fmt.Errorf(sRes.Status.Message)
return http.StatusNotFound, fmt.Errorf("Resource not found")
}
return http.StatusForbidden, fmt.Errorf(sRes.Status.Message)
case res.Status.Code == rpc.Code_CODE_ABORTED:
Expand Down
Loading