github.com/cs3org/reva/v2@v2.27.7/internal/grpc/services/sharesstorageprovider/sharesstorageprovider.go (about) 1 // Copyright 2018-2021 CERN 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 // 15 // In applying this license, CERN does not waive the privileges and immunities 16 // granted to it by virtue of its status as an Intergovernmental Organization 17 // or submit itself to any jurisdiction. 18 19 package sharesstorageprovider 20 21 import ( 22 "context" 23 "fmt" 24 "path/filepath" 25 "strings" 26 27 "github.com/cs3org/reva/v2/pkg/storagespace" 28 "github.com/rs/zerolog" 29 "golang.org/x/sync/errgroup" 30 "google.golang.org/genproto/protobuf/field_mask" 31 "google.golang.org/grpc" 32 codes "google.golang.org/grpc/codes" 33 gstatus "google.golang.org/grpc/status" 34 "google.golang.org/protobuf/types/known/fieldmaskpb" 35 36 gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1" 37 userv1beta1 "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" 38 rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1" 39 collaboration "github.com/cs3org/go-cs3apis/cs3/sharing/collaboration/v1beta1" 40 provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" 41 typesv1beta1 "github.com/cs3org/go-cs3apis/cs3/types/v1beta1" 42 "github.com/cs3org/reva/v2/pkg/appctx" 43 ctxpkg "github.com/cs3org/reva/v2/pkg/ctx" 44 "github.com/cs3org/reva/v2/pkg/errtypes" 45 "github.com/cs3org/reva/v2/pkg/rgrpc" 46 "github.com/cs3org/reva/v2/pkg/rgrpc/status" 47 "github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool" 48 "github.com/cs3org/reva/v2/pkg/sharedconf" 49 "github.com/cs3org/reva/v2/pkg/utils" 50 "github.com/mitchellh/mapstructure" 51 "github.com/pkg/errors" 52 ) 53 54 const ( 55 _defaultSharesJailEtag = "DECAFC00FEE" 56 ) 57 58 func init() { 59 rgrpc.Register("sharesstorageprovider", NewDefault) 60 } 61 62 type config struct { 63 GatewayAddr string `mapstructure:"gateway_addr"` 64 UserShareProviderEndpoint string `mapstructure:"usershareprovidersvc"` 65 MaxConcurrency int `mapstructure:"max_concurrency"` 66 } 67 68 type service struct { 69 gatewaySelector pool.Selectable[gateway.GatewayAPIClient] 70 sharingCollaborationSelector pool.Selectable[collaboration.CollaborationAPIClient] 71 maxConcurrency int 72 } 73 74 func (s *service) Close() error { 75 return nil 76 } 77 78 func (s *service) UnprotectedEndpoints() []string { 79 return []string{} 80 } 81 82 func (s *service) Register(ss *grpc.Server) { 83 provider.RegisterProviderAPIServer(ss, s) 84 provider.RegisterSpacesAPIServer(ss, s) 85 } 86 87 // NewDefault returns a new instance of the SharesStorageProvider service with default dependencies 88 func NewDefault(m map[string]interface{}, _ *grpc.Server, _ *zerolog.Logger) (rgrpc.Service, error) { 89 c := &config{} 90 if err := mapstructure.Decode(m, c); err != nil { 91 err = errors.Wrap(err, "error decoding conf") 92 return nil, err 93 } 94 95 gatewaySelector, err := pool.GatewaySelector(sharedconf.GetGatewaySVC(c.GatewayAddr)) 96 if err != nil { 97 return nil, err 98 } 99 100 sharingCollaborationSelector, err := pool.SharingCollaborationSelector(sharedconf.GetGatewaySVC(c.UserShareProviderEndpoint)) 101 if err != nil { 102 return nil, errors.Wrap(err, "sharesstorageprovider: error getting UserShareProvider client") 103 } 104 105 if c.MaxConcurrency <= 0 { 106 c.MaxConcurrency = 5 107 } 108 109 return New(gatewaySelector, sharingCollaborationSelector, c.MaxConcurrency) 110 } 111 112 // New returns a new instance of the SharesStorageProvider service 113 func New(gatewaySelector pool.Selectable[gateway.GatewayAPIClient], sharingCollaborationSelector pool.Selectable[collaboration.CollaborationAPIClient], maxConcurrency int) (rgrpc.Service, error) { 114 s := &service{ 115 gatewaySelector: gatewaySelector, 116 sharingCollaborationSelector: sharingCollaborationSelector, 117 maxConcurrency: maxConcurrency, 118 } 119 return s, nil 120 } 121 122 func (s *service) SetArbitraryMetadata(ctx context.Context, req *provider.SetArbitraryMetadataRequest) (*provider.SetArbitraryMetadataResponse, error) { 123 receivedShare, rpcStatus, err := s.resolveAcceptedShare(ctx, req.Ref) 124 appctx.GetLogger(ctx).Debug(). 125 Interface("ref", req.Ref). 126 Interface("received_share", receivedShare). 127 Msg("sharesstorageprovider: Got SetArbitraryMetadata request") 128 if err != nil { 129 return nil, err 130 } 131 if rpcStatus.Code != rpc.Code_CODE_OK { 132 return &provider.SetArbitraryMetadataResponse{ 133 Status: rpcStatus, 134 }, nil 135 } 136 137 gatewayClient, err := s.gatewaySelector.Next() 138 if err != nil { 139 return nil, err 140 } 141 142 return gatewayClient.SetArbitraryMetadata(ctx, &provider.SetArbitraryMetadataRequest{ 143 Opaque: req.Opaque, 144 Ref: buildReferenceInShare(req.Ref, receivedShare), 145 ArbitraryMetadata: req.ArbitraryMetadata, 146 }) 147 } 148 149 func (s *service) UnsetArbitraryMetadata(ctx context.Context, req *provider.UnsetArbitraryMetadataRequest) (*provider.UnsetArbitraryMetadataResponse, error) { 150 receivedShare, rpcStatus, err := s.resolveAcceptedShare(ctx, req.Ref) 151 appctx.GetLogger(ctx).Debug(). 152 Interface("ref", req.Ref). 153 Interface("received_share", receivedShare). 154 Msg("sharesstorageprovider: Got UnsetArbitraryMetadata request") 155 if err != nil { 156 return nil, err 157 } 158 if rpcStatus.Code != rpc.Code_CODE_OK { 159 return &provider.UnsetArbitraryMetadataResponse{ 160 Status: rpcStatus, 161 }, nil 162 } 163 164 gatewayClient, err := s.gatewaySelector.Next() 165 if err != nil { 166 return nil, err 167 } 168 169 return gatewayClient.UnsetArbitraryMetadata(ctx, &provider.UnsetArbitraryMetadataRequest{ 170 Opaque: req.Opaque, 171 Ref: buildReferenceInShare(req.Ref, receivedShare), 172 ArbitraryMetadataKeys: req.ArbitraryMetadataKeys, 173 }) 174 } 175 176 func (s *service) InitiateFileDownload(ctx context.Context, req *provider.InitiateFileDownloadRequest) (*provider.InitiateFileDownloadResponse, error) { 177 receivedShare, rpcStatus, err := s.resolveAcceptedShare(ctx, req.Ref) 178 appctx.GetLogger(ctx).Debug(). 179 Interface("ref", req.Ref). 180 Interface("received_share", receivedShare). 181 Msg("sharesstorageprovider: Got InitiateFileDownload request") 182 if err != nil { 183 return nil, err 184 } 185 if rpcStatus.Code != rpc.Code_CODE_OK { 186 return &provider.InitiateFileDownloadResponse{ 187 Status: rpcStatus, 188 }, nil 189 } 190 191 gatewayClient, err := s.gatewaySelector.Next() 192 if err != nil { 193 return nil, err 194 } 195 196 gwres, err := gatewayClient.InitiateFileDownload(ctx, &provider.InitiateFileDownloadRequest{ 197 Opaque: req.Opaque, 198 Ref: buildReferenceInShare(req.Ref, receivedShare), 199 LockId: req.LockId, 200 }) 201 if err != nil { 202 return nil, err 203 } 204 if gwres.Status.Code != rpc.Code_CODE_OK { 205 return &provider.InitiateFileDownloadResponse{ 206 Status: gwres.Status, 207 }, nil 208 } 209 210 protocols := []*provider.FileDownloadProtocol{} 211 for p := range gwres.Protocols { 212 if !strings.HasSuffix(gwres.Protocols[p].DownloadEndpoint, "/") { 213 gwres.Protocols[p].DownloadEndpoint += "/" 214 } 215 gwres.Protocols[p].DownloadEndpoint += gwres.Protocols[p].Token 216 217 protocols = append(protocols, &provider.FileDownloadProtocol{ 218 Opaque: gwres.Protocols[p].Opaque, 219 Protocol: gwres.Protocols[p].Protocol, 220 DownloadEndpoint: gwres.Protocols[p].DownloadEndpoint, 221 Expose: true, // the gateway already has encoded the upload endpoint 222 }) 223 } 224 225 return &provider.InitiateFileDownloadResponse{ 226 Opaque: gwres.GetOpaque(), 227 Status: gwres.Status, 228 Protocols: protocols, 229 }, nil 230 231 } 232 233 func (s *service) InitiateFileUpload(ctx context.Context, req *provider.InitiateFileUploadRequest) (*provider.InitiateFileUploadResponse, error) { 234 receivedShare, rpcStatus, err := s.resolveAcceptedShare(ctx, req.Ref) 235 appctx.GetLogger(ctx).Debug(). 236 Interface("ref", req.Ref). 237 Interface("received_share", receivedShare). 238 Msg("sharesstorageprovider: Got InitiateFileUpload request") 239 switch { 240 case err != nil: 241 return nil, err 242 case rpcStatus.Code == rpc.Code_CODE_NOT_FOUND: 243 // the user has access (it showed up in the clist of shares), but we cannot write here 244 return &provider.InitiateFileUploadResponse{ 245 Status: status.NewFailedPrecondition(ctx, nil, rpcStatus.GetMessage()), 246 }, nil 247 case rpcStatus.Code != rpc.Code_CODE_OK: 248 return &provider.InitiateFileUploadResponse{ 249 Status: rpcStatus, 250 }, nil 251 } 252 253 if !receivedShare.GetShare().GetPermissions().GetPermissions().GetInitiateFileUpload() { 254 return &provider.InitiateFileUploadResponse{ 255 Status: status.NewPermissionDenied(ctx, nil, "share does not grant InitiateFileDownload permission"), 256 }, nil 257 } 258 259 gatewayClient, err := s.gatewaySelector.Next() 260 if err != nil { 261 return nil, err 262 } 263 264 gwres, err := gatewayClient.InitiateFileUpload(ctx, &provider.InitiateFileUploadRequest{ 265 Opaque: req.Opaque, 266 Ref: buildReferenceInShare(req.Ref, receivedShare), 267 LockId: req.LockId, 268 Options: req.Options, 269 }) 270 if err != nil { 271 return nil, err 272 } 273 if gwres.Status.Code != rpc.Code_CODE_OK { 274 return &provider.InitiateFileUploadResponse{ 275 Status: gwres.Status, 276 }, nil 277 } 278 279 protocols := []*provider.FileUploadProtocol{} 280 for p := range gwres.Protocols { 281 if !strings.HasSuffix(gwres.Protocols[p].UploadEndpoint, "/") { 282 gwres.Protocols[p].UploadEndpoint += "/" 283 } 284 gwres.Protocols[p].UploadEndpoint += gwres.Protocols[p].Token 285 286 protocols = append(protocols, &provider.FileUploadProtocol{ 287 Opaque: gwres.Protocols[p].Opaque, 288 Protocol: gwres.Protocols[p].Protocol, 289 UploadEndpoint: gwres.Protocols[p].UploadEndpoint, 290 AvailableChecksums: gwres.Protocols[p].AvailableChecksums, 291 Expose: true, // the gateway already has encoded the upload endpoint 292 }) 293 } 294 return &provider.InitiateFileUploadResponse{ 295 Opaque: gwres.GetOpaque(), 296 Status: gwres.Status, 297 Protocols: protocols, 298 }, nil 299 } 300 301 func (s *service) GetPath(ctx context.Context, req *provider.GetPathRequest) (*provider.GetPathResponse, error) { 302 // TODO: Needs to find a path for a given resourceID 303 // It should 304 // - getPath of the resourceID - probably requires owner permissions -> needs machine auth 305 // - getPath of every received share on the same space - needs also owner permissions -> needs machine auth 306 // - find the shortest root path that is a prefix of the resource path 307 // alternatively implement this on storageprovider - it needs to know about grants to do so 308 309 if isShareJailRoot(req.ResourceId) { 310 return &provider.GetPathResponse{ 311 Status: status.NewOK(ctx), 312 Path: "/", 313 }, nil 314 } 315 316 receivedShare, rpcStatus, err := s.resolveAcceptedShare(ctx, &provider.Reference{ 317 ResourceId: req.ResourceId, 318 }) 319 appctx.GetLogger(ctx).Debug(). 320 Interface("resourceId", req.ResourceId). 321 Interface("received_share", receivedShare). 322 Msg("sharesstorageprovider: Got GetPath request") 323 if err != nil { 324 return nil, err 325 } 326 if rpcStatus.Code != rpc.Code_CODE_OK { 327 return &provider.GetPathResponse{ 328 Status: rpcStatus, 329 }, nil 330 } 331 332 return &provider.GetPathResponse{ 333 Status: status.NewOK(ctx), 334 Path: filepath.Clean("/" + receivedShare.MountPoint.Path), 335 }, nil 336 337 } 338 339 func (s *service) GetHome(ctx context.Context, req *provider.GetHomeRequest) (*provider.GetHomeResponse, error) { 340 return nil, gstatus.Errorf(codes.Unimplemented, "method not implemented") 341 } 342 343 func (s *service) CreateHome(ctx context.Context, req *provider.CreateHomeRequest) (*provider.CreateHomeResponse, error) { 344 return nil, gstatus.Errorf(codes.Unimplemented, "method not implemented") 345 } 346 347 func (s *service) CreateStorageSpace(ctx context.Context, req *provider.CreateStorageSpaceRequest) (*provider.CreateStorageSpaceResponse, error) { 348 return nil, gstatus.Errorf(codes.Unimplemented, "method not implemented") 349 } 350 351 // ListStorageSpaces returns a list storage spaces with type "share" the current user has acces to. 352 // Do owners of shares see type "shared"? Do they see andyhing? They need to if the want a fast lookup of shared with others 353 // -> but then a storage sprovider has to do everything? not everything but permissions (= shares) related operations, yes 354 // The root node of every storag space is the (spaceid, nodeid) of the shared node. 355 // Since real space roots have (spaceid=nodeid) shares can be correlated with the space using the (spaceid, ) part of the reference. 356 357 // However, when the space registry tries 358 // to find a storage provider for a specific space it returns an empty list, so the actual storage provider 359 // should be found. 360 361 func (s *service) ListStorageSpaces(ctx context.Context, req *provider.ListStorageSpacesRequest) (*provider.ListStorageSpacesResponse, error) { 362 spaceTypes := map[string]struct{}{} 363 var exists = struct{}{} 364 var fetchShares bool 365 appendTypes := []string{} 366 var spaceID *provider.ResourceId 367 for _, f := range req.Filters { 368 switch f.Type { 369 case provider.ListStorageSpacesRequest_Filter_TYPE_SPACE_TYPE: 370 spaceType := f.GetSpaceType() 371 // do we need to fetch the shares? 372 if spaceType == "+mountpoint" || spaceType == "+grant" { 373 appendTypes = append(appendTypes, strings.TrimPrefix(spaceType, "+")) 374 fetchShares = true 375 continue 376 } 377 if spaceType == "mountpoint" || spaceType == "grant" { 378 fetchShares = true 379 } 380 spaceTypes[spaceType] = exists 381 case provider.ListStorageSpacesRequest_Filter_TYPE_ID: 382 storageid, spaceid, shareid, err := storagespace.SplitID(f.GetId().OpaqueId) 383 if err != nil { 384 continue 385 } 386 if spaceid != utils.ShareStorageSpaceID { 387 return &provider.ListStorageSpacesResponse{ 388 // a specific id was requested, return not found instead of empty list 389 Status: &rpc.Status{Code: rpc.Code_CODE_NOT_FOUND}, 390 }, nil 391 } 392 393 spaceID = &provider.ResourceId{StorageId: storageid, SpaceId: spaceid, OpaqueId: shareid} 394 } 395 } 396 397 if len(spaceTypes) == 0 { 398 spaceTypes["virtual"] = exists 399 spaceTypes["mountpoint"] = exists 400 fetchShares = true 401 } 402 403 for _, s := range appendTypes { 404 spaceTypes[s] = exists 405 } 406 407 var receivedShares []*collaboration.ReceivedShare 408 var shareInfo map[string]*provider.ResourceInfo 409 var err error 410 if fetchShares { 411 receivedShares, shareInfo, err = s.fetchAcceptedShares(ctx, req.Opaque, []string{}, &fieldmaskpb.FieldMask{ /*TODO mtime and etag only?*/ }) 412 if err != nil { 413 return nil, errors.Wrap(err, "sharesstorageprovider: error calling ListReceivedSharesRequest") 414 } 415 } 416 417 res := &provider.ListStorageSpacesResponse{ 418 Status: status.NewOK(ctx), 419 } 420 for k := range spaceTypes { 421 switch k { 422 case "virtual": 423 virtualRootID := &provider.ResourceId{ 424 StorageId: utils.ShareStorageProviderID, 425 SpaceId: utils.ShareStorageSpaceID, 426 OpaqueId: utils.ShareStorageSpaceID, 427 } 428 if spaceID == nil || isShareJailRoot(spaceID) { 429 earliestShare := findEarliestShare(receivedShares, shareInfo) 430 var opaque *typesv1beta1.Opaque 431 var mtime *typesv1beta1.Timestamp 432 if earliestShare != nil { 433 if info, ok := shareInfo[earliestShare.GetId().GetOpaqueId()]; ok { 434 mtime = info.Mtime 435 opaque = utils.AppendPlainToOpaque(opaque, "etag", info.Etag) 436 } 437 } else { 438 opaque = utils.AppendPlainToOpaque(opaque, "etag", _defaultSharesJailEtag) 439 } 440 // only display the shares jail if we have accepted shares 441 opaque = utils.AppendPlainToOpaque(opaque, "spaceAlias", "virtual/shares") 442 space := &provider.StorageSpace{ 443 Opaque: opaque, 444 Id: &provider.StorageSpaceId{ 445 OpaqueId: storagespace.FormatResourceID(virtualRootID), 446 }, 447 SpaceType: "virtual", 448 //Owner: &userv1beta1.User{Id: receivedShare.Share.Owner}, // FIXME actually, the mount point belongs to the recipient 449 // the sharesstorageprovider keeps track of mount points 450 Root: virtualRootID, 451 Name: "Shares", 452 Mtime: mtime, 453 } 454 res.StorageSpaces = append(res.StorageSpaces, space) 455 } 456 case "grant": 457 for _, receivedShare := range receivedShares { 458 root := receivedShare.Share.ResourceId 459 // do we filter by id? 460 if spaceID != nil && !utils.ResourceIDEqual(spaceID, root) { 461 // none of our business 462 continue 463 } 464 // we know a grant for this resource 465 space := &provider.StorageSpace{ 466 Id: &provider.StorageSpaceId{ 467 OpaqueId: storagespace.FormatResourceID(root), 468 }, 469 SpaceType: "grant", 470 Owner: &userv1beta1.User{Id: receivedShare.Share.Owner}, 471 // the sharesstorageprovider keeps track of mount points 472 Root: root, 473 RootInfo: shareInfo[receivedShare.Share.Id.OpaqueId], 474 } 475 476 res.StorageSpaces = append(res.StorageSpaces, space) 477 } 478 case "mountpoint": 479 for _, receivedShare := range receivedShares { 480 if receivedShare.State != collaboration.ShareState_SHARE_STATE_ACCEPTED { 481 continue 482 } 483 root := &provider.ResourceId{ 484 StorageId: utils.ShareStorageProviderID, 485 SpaceId: utils.ShareStorageSpaceID, 486 OpaqueId: receivedShare.Share.Id.OpaqueId, 487 } 488 // do we filter by id 489 if spaceID != nil { 490 switch { 491 case utils.ResourceIDEqual(spaceID, root): 492 // we have a virtual node 493 case utils.ResourceIDEqual(spaceID, receivedShare.Share.ResourceId): 494 // we have a mount point 495 root = receivedShare.Share.ResourceId 496 default: 497 // none of our business 498 continue 499 } 500 } 501 var opaque *typesv1beta1.Opaque 502 if _, ok := shareInfo[receivedShare.Share.Id.OpaqueId]; !ok { 503 // we could not stat the share, skip it 504 continue 505 } 506 // add the resourceID for the grant 507 if receivedShare.Share.ResourceId != nil { 508 opaque = utils.AppendPlainToOpaque(opaque, "grantStorageID", receivedShare.Share.ResourceId.StorageId) 509 opaque = utils.AppendPlainToOpaque(opaque, "grantSpaceID", receivedShare.Share.ResourceId.SpaceId) 510 opaque = utils.AppendPlainToOpaque(opaque, "grantOpaqueID", receivedShare.Share.ResourceId.OpaqueId) 511 } 512 513 // prefix storageid if we are responsible 514 if root.SpaceId == utils.ShareStorageSpaceID { 515 root.StorageId = utils.ShareStorageProviderID 516 } 517 518 space := &provider.StorageSpace{ 519 Opaque: opaque, 520 Id: &provider.StorageSpaceId{ 521 OpaqueId: storagespace.FormatResourceID(root), 522 }, 523 SpaceType: "mountpoint", 524 Owner: &userv1beta1.User{Id: receivedShare.Share.Owner}, // FIXME actually, the mount point belongs to the recipient 525 // the sharesstorageprovider keeps track of mount points 526 Root: root, 527 RootInfo: shareInfo[receivedShare.Share.Id.OpaqueId], 528 } 529 530 // TODO in the future the spaces registry will handle the alias for share spaces. 531 // for now use the name from the share to override the name determined by stat 532 if receivedShare.MountPoint != nil { 533 space.Name = receivedShare.MountPoint.Path 534 space.Opaque = utils.AppendPlainToOpaque(space.Opaque, "spaceAlias", space.SpaceType+"/"+strings.ReplaceAll(strings.ToLower(space.Name), " ", "-")) 535 } 536 537 // what if we don't have a name? 538 res.StorageSpaces = append(res.StorageSpaces, space) 539 } 540 } 541 } 542 return res, nil 543 } 544 545 func (s *service) UpdateStorageSpace(ctx context.Context, req *provider.UpdateStorageSpaceRequest) (*provider.UpdateStorageSpaceResponse, error) { 546 return nil, gstatus.Errorf(codes.Unimplemented, "method not implemented") 547 } 548 549 func (s *service) DeleteStorageSpace(ctx context.Context, req *provider.DeleteStorageSpaceRequest) (*provider.DeleteStorageSpaceResponse, error) { 550 return nil, gstatus.Errorf(codes.Unimplemented, "method not implemented") 551 } 552 553 func (s *service) CreateContainer(ctx context.Context, req *provider.CreateContainerRequest) (*provider.CreateContainerResponse, error) { 554 receivedShare, rpcStatus, err := s.resolveAcceptedShare(ctx, req.Ref) 555 appctx.GetLogger(ctx).Debug(). 556 Interface("ref", req.Ref). 557 Interface("received_share", receivedShare). 558 Msg("sharesstorageprovider: Got CreateContainer request") 559 if err != nil { 560 return nil, err 561 } 562 if rpcStatus.Code != rpc.Code_CODE_OK { 563 return &provider.CreateContainerResponse{ 564 Status: rpcStatus, 565 }, nil 566 } 567 568 gatewayClient, err := s.gatewaySelector.Next() 569 if err != nil { 570 return nil, err 571 } 572 573 return gatewayClient.CreateContainer(ctx, &provider.CreateContainerRequest{ 574 Opaque: req.Opaque, 575 Ref: buildReferenceInShare(req.Ref, receivedShare), 576 }) 577 } 578 579 func (s *service) Delete(ctx context.Context, req *provider.DeleteRequest) (*provider.DeleteResponse, error) { 580 receivedShare, rpcStatus, err := s.resolveAcceptedShare(ctx, req.Ref) 581 appctx.GetLogger(ctx).Debug(). 582 Interface("ref", req.Ref). 583 Interface("received_share", receivedShare). 584 Err(err). 585 Msg("sharesstorageprovider: Got Delete request") 586 if err != nil { 587 return nil, err 588 } 589 if rpcStatus.Code != rpc.Code_CODE_OK { 590 return &provider.DeleteResponse{ 591 Status: rpcStatus, 592 }, nil 593 } 594 595 // the root of a share always has the path "." 596 if req.Ref.ResourceId.StorageId == utils.ShareStorageProviderID && req.Ref.ResourceId.SpaceId == utils.ShareStorageSpaceID && req.Ref.Path == "." { 597 err := s.rejectReceivedShare(ctx, receivedShare) 598 if err != nil { 599 return &provider.DeleteResponse{ 600 Status: status.NewInternal(ctx, "sharesstorageprovider: error rejecting share"), 601 }, nil 602 } 603 return &provider.DeleteResponse{ 604 Status: status.NewOK(ctx), 605 }, nil 606 } 607 608 gatewayClient, err := s.gatewaySelector.Next() 609 if err != nil { 610 return nil, err 611 } 612 613 return gatewayClient.Delete(ctx, &provider.DeleteRequest{ 614 Opaque: req.Opaque, 615 Ref: buildReferenceInShare(req.Ref, receivedShare), 616 }) 617 } 618 619 func (s *service) Move(ctx context.Context, req *provider.MoveRequest) (*provider.MoveResponse, error) { 620 appctx.GetLogger(ctx).Debug(). 621 Interface("source", req.Source). 622 Interface("destination", req.Destination). 623 Msg("sharesstorageprovider: Got Move request") 624 625 // TODO moving inside a shared tree should just be a forward of the move 626 // but when do we rename a mounted share? Does that request even hit us? 627 // - the registry needs to invalidate the alias 628 // - the rhe share manager needs to change the name 629 // ... but which storageprovider will receive the move request??? 630 srcReceivedShare, rpcStatus, err := s.resolveAcceptedShare(ctx, req.Source) 631 if err != nil { 632 return nil, err 633 } 634 if rpcStatus.Code != rpc.Code_CODE_OK { 635 return &provider.MoveResponse{ 636 Status: rpcStatus, 637 }, nil 638 } 639 640 // we can do a rename 641 if isRename(req.Source, req.Destination) { 642 643 // Change the MountPoint of the share, it has no relative prefix 644 srcReceivedShare.MountPoint = &provider.Reference{ 645 // FIXME actually it does have a resource id: the one of the sharesstorageprovider 646 Path: filepath.Base(req.Destination.Path), 647 } 648 649 sharingCollaborationClient, err := s.sharingCollaborationSelector.Next() 650 if err != nil { 651 return nil, err 652 } 653 654 _, err = sharingCollaborationClient.UpdateReceivedShare(ctx, &collaboration.UpdateReceivedShareRequest{ 655 Share: srcReceivedShare, 656 UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"state", "mount_point"}}, 657 }) 658 if err != nil { 659 return &provider.MoveResponse{ 660 Status: status.NewInternal(ctx, "sharesstorageprovider: can not change mountpoint of share"), 661 }, nil 662 } 663 return &provider.MoveResponse{ 664 Status: status.NewOK(ctx), 665 }, nil 666 } 667 668 dstReceivedShare, rpcStatus, err2 := s.resolveAcceptedShare(ctx, req.Destination) 669 if err2 != nil { 670 return nil, err2 671 } 672 if rpcStatus.Code != rpc.Code_CODE_OK { 673 return &provider.MoveResponse{ 674 Status: rpcStatus, 675 }, nil 676 } 677 678 if dstReceivedShare.Share.Id.OpaqueId != srcReceivedShare.Share.Id.OpaqueId { 679 return &provider.MoveResponse{ 680 Status: status.NewUnimplemented(ctx, nil, "cross storage moves are not supported, use copy and delete"), 681 }, nil 682 } 683 684 gatewayClient, err := s.gatewaySelector.Next() 685 if err != nil { 686 return nil, err 687 } 688 689 return gatewayClient.Move(ctx, &provider.MoveRequest{ 690 Opaque: req.Opaque, 691 Source: buildReferenceInShare(req.Source, srcReceivedShare), 692 Destination: buildReferenceInShare(req.Destination, dstReceivedShare), 693 }) 694 } 695 696 // SetLock puts a lock on the given reference 697 func (s *service) SetLock(ctx context.Context, req *provider.SetLockRequest) (*provider.SetLockResponse, error) { 698 return nil, gstatus.Errorf(codes.Unimplemented, "method not implemented") 699 } 700 701 // GetLock returns an existing lock on the given reference 702 func (s *service) GetLock(ctx context.Context, req *provider.GetLockRequest) (*provider.GetLockResponse, error) { 703 return nil, gstatus.Errorf(codes.Unimplemented, "method not implemented") 704 } 705 706 // RefreshLock refreshes an existing lock on the given reference 707 func (s *service) RefreshLock(ctx context.Context, req *provider.RefreshLockRequest) (*provider.RefreshLockResponse, error) { 708 return nil, gstatus.Errorf(codes.Unimplemented, "method not implemented") 709 } 710 711 // Unlock removes an existing lock from the given reference 712 func (s *service) Unlock(ctx context.Context, req *provider.UnlockRequest) (*provider.UnlockResponse, error) { 713 return nil, gstatus.Errorf(codes.Unimplemented, "method not implemented") 714 } 715 716 func (s *service) Stat(ctx context.Context, req *provider.StatRequest) (*provider.StatResponse, error) { 717 if isVirtualRoot(req.Ref) { 718 owner, ok := ctxpkg.ContextGetUser(ctx) 719 if !ok { 720 return nil, fmt.Errorf("missing user in context") 721 } 722 receivedShares, shareMd, err := s.fetchAcceptedShares(ctx, req.Opaque, req.ArbitraryMetadataKeys, req.FieldMask) 723 if err != nil { 724 return nil, err 725 } 726 earliestShare := findEarliestShare(receivedShares, shareMd) 727 var mtime *typesv1beta1.Timestamp 728 etag := _defaultSharesJailEtag 729 if earliestShare != nil { 730 if info, ok := shareMd[earliestShare.GetId().GetOpaqueId()]; ok { 731 mtime = info.Mtime 732 etag = info.Etag 733 } 734 } 735 return &provider.StatResponse{ 736 Status: status.NewOK(ctx), 737 Info: &provider.ResourceInfo{ 738 Opaque: &typesv1beta1.Opaque{ 739 Map: map[string]*typesv1beta1.OpaqueEntry{ 740 "root": { 741 Decoder: "plain", 742 Value: []byte(utils.ShareStorageProviderID), 743 }, 744 }, 745 }, 746 Id: &provider.ResourceId{ 747 StorageId: utils.ShareStorageProviderID, 748 SpaceId: utils.ShareStorageSpaceID, 749 OpaqueId: utils.ShareStorageSpaceID, 750 }, 751 Type: provider.ResourceType_RESOURCE_TYPE_CONTAINER, 752 Mtime: mtime, 753 Path: "/", 754 MimeType: "httpd/unix-directory", 755 Size: 0, 756 PermissionSet: &provider.ResourcePermissions{ 757 // TODO 758 }, 759 Space: &provider.StorageSpace{ 760 SpaceType: "virtual", 761 }, 762 Etag: etag, 763 Owner: owner.Id, 764 }, 765 }, nil 766 } 767 receivedShare, rpcStatus, err := s.resolveAcceptedShare(ctx, req.Ref) 768 appctx.GetLogger(ctx).Debug(). 769 Interface("ref", req.Ref). 770 Interface("received_share", receivedShare). 771 Err(err). 772 Msg("sharesstorageprovider: Got Stat request") 773 if err != nil { 774 return nil, err 775 } 776 if rpcStatus.Code != rpc.Code_CODE_OK { 777 return &provider.StatResponse{ 778 Status: rpcStatus, 779 }, nil 780 } 781 if receivedShare.State != collaboration.ShareState_SHARE_STATE_ACCEPTED { 782 return &provider.StatResponse{ 783 Status: &rpc.Status{Code: rpc.Code_CODE_NOT_FOUND}, 784 // not mounted yet 785 }, nil 786 } 787 788 gatewayClient, err := s.gatewaySelector.Next() 789 if err != nil { 790 return nil, err 791 } 792 793 statRes, err := gatewayClient.Stat(ctx, &provider.StatRequest{ 794 Opaque: req.Opaque, 795 Ref: buildReferenceInShare(req.Ref, receivedShare), 796 ArbitraryMetadataKeys: req.ArbitraryMetadataKeys, 797 }) 798 if err != nil { 799 return nil, err 800 } 801 802 // when stating a share jail mountpoint we need to rewrite the ids 803 if statRes.GetStatus().GetCode() == rpc.Code_CODE_OK && receivedShare.MountPoint.Path == strings.TrimPrefix(req.Ref.Path, "./") && statRes.Info != nil { 804 // overwrite id with the share jail mountpoint id 805 statRes.Info.Id = &provider.ResourceId{ 806 StorageId: utils.ShareStorageProviderID, 807 SpaceId: utils.ShareStorageSpaceID, 808 OpaqueId: receivedShare.GetShare().GetId().GetOpaqueId(), 809 } 810 // overwrite parent id with the share jail root 811 statRes.Info.ParentId = &provider.ResourceId{ 812 StorageId: utils.ShareStorageProviderID, 813 SpaceId: utils.ShareStorageSpaceID, 814 OpaqueId: utils.ShareStorageSpaceID, 815 } 816 } 817 818 return statRes, nil 819 } 820 821 func (s *service) ListContainerStream(req *provider.ListContainerStreamRequest, ss provider.ProviderAPI_ListContainerStreamServer) error { 822 return gstatus.Errorf(codes.Unimplemented, "method not implemented") 823 } 824 func (s *service) ListContainer(ctx context.Context, req *provider.ListContainerRequest) (*provider.ListContainerResponse, error) { 825 if isVirtualRoot(req.Ref) { 826 // The root is empty, it is filled by mountpoints 827 // so, when accessing the root via /dav/spaces, we need to list the accepted shares with their mountpoint 828 829 receivedShares, shareMd, err := s.fetchAcceptedShares(ctx, req.Opaque, req.ArbitraryMetadataKeys, req.FieldMask) 830 if err != nil { 831 return nil, errors.Wrap(err, "sharesstorageprovider: error calling ListReceivedSharesRequest") 832 } 833 834 // Create map of shares that contains only the oldest share per shared resource. This is to avoid 835 // returning multiple resourceInfos for the same resource. But still be able to maintain a 836 // "somewhat" stable resourceID 837 oldestReceivedSharesByResourceID := make(map[string]*collaboration.ReceivedShare, len(receivedShares)) 838 for _, receivedShare := range receivedShares { 839 if receivedShare.GetState() != collaboration.ShareState_SHARE_STATE_ACCEPTED { 840 continue 841 } 842 rIDStr := storagespace.FormatResourceID(receivedShare.GetShare().GetResourceId()) 843 if oldest, ok := oldestReceivedSharesByResourceID[rIDStr]; ok { 844 // replace if older than current oldest 845 if utils.TSToTime(receivedShare.GetShare().GetCtime()).Before(utils.TSToTime(oldest.GetShare().GetCtime())) { 846 oldestReceivedSharesByResourceID[rIDStr] = receivedShare 847 } 848 } else { 849 oldestReceivedSharesByResourceID[rIDStr] = receivedShare 850 } 851 } 852 853 // now compose the resourceInfos for the unified list of shares 854 infos := []*provider.ResourceInfo{} 855 for _, share := range oldestReceivedSharesByResourceID { 856 info := shareMd[share.GetShare().GetId().GetOpaqueId()] 857 if info == nil { 858 appctx.GetLogger(ctx).Debug(). 859 Interface("share", share). 860 Msg("sharesstorageprovider: no resource info for share") 861 continue 862 } 863 864 // override resource id info 865 info.Id = &provider.ResourceId{ 866 StorageId: utils.ShareStorageProviderID, 867 SpaceId: utils.ShareStorageSpaceID, 868 OpaqueId: share.GetShare().GetId().GetOpaqueId(), 869 } 870 info.Path = filepath.Base(share.MountPoint.Path) 871 info.Name = info.Path 872 873 infos = append(infos, info) 874 } 875 return &provider.ListContainerResponse{ 876 Status: status.NewOK(ctx), 877 Infos: infos, 878 }, nil 879 } 880 receivedShare, rpcStatus, err := s.resolveAcceptedShare(ctx, req.Ref) 881 appctx.GetLogger(ctx).Debug(). 882 Interface("ref", req.Ref). 883 Interface("received_share", receivedShare). 884 Err(err). 885 Msg("sharesstorageprovider: Got ListContainer request") 886 if err != nil { 887 return nil, err 888 } 889 if rpcStatus.Code != rpc.Code_CODE_OK { 890 return &provider.ListContainerResponse{ 891 Status: rpcStatus, 892 }, nil 893 } 894 895 gatewayClient, err := s.gatewaySelector.Next() 896 if err != nil { 897 return nil, err 898 } 899 900 return gatewayClient.ListContainer(ctx, &provider.ListContainerRequest{ 901 Opaque: req.Opaque, 902 Ref: buildReferenceInShare(req.Ref, receivedShare), 903 ArbitraryMetadataKeys: req.ArbitraryMetadataKeys, 904 }) 905 } 906 func (s *service) ListFileVersions(ctx context.Context, req *provider.ListFileVersionsRequest) (*provider.ListFileVersionsResponse, error) { 907 receivedShare, rpcStatus, err := s.resolveAcceptedShare(ctx, req.Ref) 908 appctx.GetLogger(ctx).Debug(). 909 Interface("ref", req.Ref). 910 Interface("received_share", receivedShare). 911 Err(err). 912 Msg("sharesstorageprovider: Got ListFileVersions request") 913 if err != nil { 914 return nil, err 915 } 916 if rpcStatus.Code != rpc.Code_CODE_OK { 917 return &provider.ListFileVersionsResponse{ 918 Status: rpcStatus, 919 }, nil 920 } 921 922 gatewayClient, err := s.gatewaySelector.Next() 923 if err != nil { 924 return nil, err 925 } 926 927 return gatewayClient.ListFileVersions(ctx, &provider.ListFileVersionsRequest{ 928 Opaque: req.Opaque, 929 Ref: buildReferenceInShare(req.Ref, receivedShare), 930 }) 931 } 932 933 func (s *service) RestoreFileVersion(ctx context.Context, req *provider.RestoreFileVersionRequest) (*provider.RestoreFileVersionResponse, error) { 934 receivedShare, rpcStatus, err := s.resolveAcceptedShare(ctx, req.Ref) 935 appctx.GetLogger(ctx).Debug(). 936 Interface("ref", req.Ref). 937 Interface("received_share", receivedShare). 938 Err(err). 939 Msg("sharesstorageprovider: Got RestoreFileVersion request") 940 if err != nil { 941 return nil, err 942 } 943 if rpcStatus.Code != rpc.Code_CODE_OK { 944 return &provider.RestoreFileVersionResponse{ 945 Status: rpcStatus, 946 }, nil 947 } 948 949 gatewayClient, err := s.gatewaySelector.Next() 950 if err != nil { 951 return nil, err 952 } 953 954 return gatewayClient.RestoreFileVersion(ctx, &provider.RestoreFileVersionRequest{ 955 Opaque: req.Opaque, 956 Ref: buildReferenceInShare(req.Ref, receivedShare), 957 }) 958 } 959 960 func (s *service) ListRecycleStream(req *provider.ListRecycleStreamRequest, ss provider.ProviderAPI_ListRecycleStreamServer) error { 961 return gstatus.Errorf(codes.Unimplemented, "method not implemented") 962 } 963 964 func (s *service) ListRecycle(ctx context.Context, req *provider.ListRecycleRequest) (*provider.ListRecycleResponse, error) { 965 return nil, gstatus.Errorf(codes.Unimplemented, "method not implemented") 966 } 967 968 func (s *service) RestoreRecycleItem(ctx context.Context, req *provider.RestoreRecycleItemRequest) (*provider.RestoreRecycleItemResponse, error) { 969 return nil, gstatus.Errorf(codes.Unimplemented, "method not implemented") 970 } 971 972 func (s *service) PurgeRecycle(ctx context.Context, req *provider.PurgeRecycleRequest) (*provider.PurgeRecycleResponse, error) { 973 return nil, gstatus.Errorf(codes.Unimplemented, "method not implemented") 974 } 975 976 func (s *service) ListGrants(ctx context.Context, req *provider.ListGrantsRequest) (*provider.ListGrantsResponse, error) { 977 return nil, gstatus.Errorf(codes.Unimplemented, "method not implemented") 978 } 979 980 func (s *service) AddGrant(ctx context.Context, req *provider.AddGrantRequest) (*provider.AddGrantResponse, error) { 981 return nil, gstatus.Errorf(codes.Unimplemented, "method not implemented") 982 } 983 984 func (s *service) DenyGrant(ctx context.Context, ref *provider.DenyGrantRequest) (*provider.DenyGrantResponse, error) { 985 return nil, gstatus.Errorf(codes.Unimplemented, "method not implemented") 986 } 987 988 func (s *service) CreateReference(ctx context.Context, req *provider.CreateReferenceRequest) (*provider.CreateReferenceResponse, error) { 989 return nil, gstatus.Errorf(codes.Unimplemented, "method not implemented") 990 } 991 992 func (s *service) CreateSymlink(ctx context.Context, req *provider.CreateSymlinkRequest) (*provider.CreateSymlinkResponse, error) { 993 return nil, gstatus.Errorf(codes.Unimplemented, "method not implemented") 994 } 995 996 func (s *service) UpdateGrant(ctx context.Context, req *provider.UpdateGrantRequest) (*provider.UpdateGrantResponse, error) { 997 return nil, gstatus.Errorf(codes.Unimplemented, "method not implemented") 998 } 999 1000 func (s *service) RemoveGrant(ctx context.Context, req *provider.RemoveGrantRequest) (*provider.RemoveGrantResponse, error) { 1001 return nil, gstatus.Errorf(codes.Unimplemented, "method not implemented") 1002 } 1003 1004 func (s *service) TouchFile(ctx context.Context, req *provider.TouchFileRequest) (*provider.TouchFileResponse, error) { 1005 receivedShare, rpcStatus, err := s.resolveAcceptedShare(ctx, req.Ref) 1006 appctx.GetLogger(ctx).Debug(). 1007 Interface("ref", req.Ref). 1008 Interface("received_share", receivedShare). 1009 Msg("sharesstorageprovider: Got TouchFile request") 1010 if err != nil { 1011 return nil, err 1012 } 1013 if rpcStatus.Code != rpc.Code_CODE_OK { 1014 return &provider.TouchFileResponse{ 1015 Status: rpcStatus, 1016 }, nil 1017 } 1018 1019 gatewayClient, err := s.gatewaySelector.Next() 1020 if err != nil { 1021 return nil, err 1022 } 1023 1024 return gatewayClient.TouchFile(ctx, &provider.TouchFileRequest{ 1025 Opaque: req.Opaque, 1026 Ref: buildReferenceInShare(req.Ref, receivedShare), 1027 }) 1028 } 1029 1030 // GetQuota returns 0 free quota. It is virtual ... the shares may have a different quota ... 1031 func (s *service) GetQuota(ctx context.Context, req *provider.GetQuotaRequest) (*provider.GetQuotaResponse, error) { 1032 // FIXME use req.Ref to get real quota 1033 return &provider.GetQuotaResponse{ 1034 Status: status.NewOK(ctx), 1035 }, nil 1036 } 1037 1038 func (s *service) resolveAcceptedShare(ctx context.Context, ref *provider.Reference) (*collaboration.ReceivedShare, *rpc.Status, error) { 1039 // treat absolute id based references as relative ones 1040 if ref.Path == "" { 1041 ref.Path = "." 1042 } 1043 if !utils.IsRelativeReference(ref) { 1044 return nil, status.NewInvalid(ctx, "sharesstorageprovider: can only handle relative references"), nil 1045 } 1046 1047 if ref.ResourceId.SpaceId != utils.ShareStorageSpaceID { 1048 return nil, status.NewNotFound(ctx, "sharesstorageprovider: not found "+ref.String()), nil 1049 } 1050 1051 sharingCollaborationClient, err := s.sharingCollaborationSelector.Next() 1052 if err != nil { 1053 return nil, nil, err 1054 } 1055 1056 // we can get the share if the reference carries a share id 1057 if ref.ResourceId.OpaqueId != utils.ShareStorageProviderID { 1058 // look up share for this resourceid 1059 lsRes, err := sharingCollaborationClient.GetReceivedShare(ctx, &collaboration.GetReceivedShareRequest{ 1060 Ref: &collaboration.ShareReference{ 1061 Spec: &collaboration.ShareReference_Id{ 1062 Id: &collaboration.ShareId{ 1063 OpaqueId: ref.ResourceId.OpaqueId, 1064 }, 1065 }, 1066 }, 1067 }) 1068 1069 if err != nil { 1070 return nil, nil, errors.Wrap(err, "sharesstorageprovider: error calling GetReceivedShare") 1071 } 1072 if lsRes.Status.Code != rpc.Code_CODE_OK { 1073 return nil, lsRes.Status, nil 1074 } 1075 if lsRes.Share.State != collaboration.ShareState_SHARE_STATE_ACCEPTED { 1076 return nil, status.NewNotFound(ctx, "sharesstorageprovider: not found "+ref.String()), nil 1077 } 1078 return lsRes.Share, lsRes.Status, nil 1079 } 1080 1081 // we currently need to list all accepted shares and match the path if the 1082 // request is relative to the share jail root. Also we need to Stat() the 1083 // shared resource's id to check whether that still exist. There might be 1084 // old shares using the same path but for an already vanished resource id. 1085 if ref.ResourceId.OpaqueId == utils.ShareStorageProviderID && ref.Path != "." { 1086 lsRes, err := sharingCollaborationClient.ListReceivedShares(ctx, &collaboration.ListReceivedSharesRequest{ 1087 Filters: []*collaboration.Filter{ 1088 { 1089 Type: collaboration.Filter_TYPE_STATE, 1090 Term: &collaboration.Filter_State{ 1091 State: collaboration.ShareState_SHARE_STATE_ACCEPTED, 1092 }, 1093 }, 1094 // TODO filter by mountpoint? 1095 }, 1096 }) 1097 if err != nil { 1098 return nil, nil, errors.Wrap(err, "sharesstorageprovider: error calling GetReceivedShare") 1099 } 1100 if lsRes.Status.Code != rpc.Code_CODE_OK { 1101 return nil, lsRes.Status, nil 1102 } 1103 for _, receivedShare := range lsRes.Shares { 1104 if isMountPointForPath(receivedShare.MountPoint.Path, ref.Path) { 1105 // Only return this share if the resource still exists. 1106 gatewayClient, err := s.gatewaySelector.Next() 1107 if err != nil { 1108 return nil, nil, err 1109 } 1110 sRes, err := gatewayClient.Stat(ctx, &provider.StatRequest{ 1111 Ref: &provider.Reference{ResourceId: receivedShare.GetShare().GetResourceId()}, 1112 }) 1113 if err != nil { 1114 appctx.GetLogger(ctx).Debug(). 1115 Err(err). 1116 Interface("resourceID", receivedShare.GetShare().GetResourceId()). 1117 Msg("resolveAcceptedShare: failed to stat shared resource") 1118 continue 1119 } 1120 if sRes.Status.Code != rpc.Code_CODE_OK { 1121 appctx.GetLogger(ctx).Debug(). 1122 Interface("resourceID", receivedShare.GetShare().GetResourceId()). 1123 Interface("status", sRes.Status). 1124 Msg("resolveAcceptedShare: failed to stat shared resource") 1125 continue 1126 } 1127 return receivedShare, lsRes.Status, nil 1128 } 1129 } 1130 } 1131 1132 return nil, status.NewNotFound(ctx, "sharesstorageprovider: not found "+ref.String()), nil 1133 } 1134 1135 func isMountPointForPath(mountpoint, path string) bool { 1136 requiredSegments := strings.Split(strings.TrimPrefix(mountpoint, "./"), "/") 1137 pathSegments := strings.Split(strings.TrimPrefix(path, "./"), "/") 1138 for i := range requiredSegments { 1139 if pathSegments[i] != requiredSegments[i] { 1140 return false 1141 } 1142 } 1143 return true 1144 } 1145 1146 func (s *service) rejectReceivedShare(ctx context.Context, receivedShare *collaboration.ReceivedShare) error { 1147 receivedShare.State = collaboration.ShareState_SHARE_STATE_REJECTED 1148 receivedShare.MountPoint = nil 1149 1150 sharingCollaborationClient, err := s.sharingCollaborationSelector.Next() 1151 if err != nil { 1152 return err 1153 } 1154 1155 res, err := sharingCollaborationClient.UpdateReceivedShare(ctx, &collaboration.UpdateReceivedShareRequest{ 1156 Share: receivedShare, 1157 UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"state", "mount_point"}}, 1158 }) 1159 if err != nil { 1160 return err 1161 } 1162 1163 return errtypes.NewErrtypeFromStatus(res.Status) 1164 } 1165 1166 func (s *service) fetchAcceptedShares(ctx context.Context, opaque *typesv1beta1.Opaque, arbitraryMetadataKeys []string, fieldMask *field_mask.FieldMask) ([]*collaboration.ReceivedShare, map[string]*provider.ResourceInfo, error) { 1167 sharingCollaborationClient, err := s.sharingCollaborationSelector.Next() 1168 if err != nil { 1169 return nil, nil, err 1170 } 1171 1172 lsRes, err := sharingCollaborationClient.ListReceivedShares(ctx, &collaboration.ListReceivedSharesRequest{ 1173 Filters: []*collaboration.Filter{ 1174 { 1175 Type: collaboration.Filter_TYPE_STATE, 1176 Term: &collaboration.Filter_State{ 1177 State: collaboration.ShareState_SHARE_STATE_ACCEPTED, 1178 }, 1179 }, 1180 }, 1181 }) 1182 if err != nil { 1183 return nil, nil, errors.Wrap(err, "sharesstorageprovider: error calling ListReceivedSharesRequest") 1184 } 1185 if lsRes.Status.Code != rpc.Code_CODE_OK { 1186 return nil, nil, fmt.Errorf("sharesstorageprovider: error calling ListReceivedSharesRequest") 1187 } 1188 1189 numWorkers := s.maxConcurrency 1190 if len(lsRes.Shares) < numWorkers { 1191 numWorkers = len(lsRes.Shares) 1192 } 1193 type res struct { 1194 shareid string 1195 info *provider.ResourceInfo 1196 } 1197 work := make(chan *collaboration.ReceivedShare, len(lsRes.Shares)) 1198 results := make(chan res, len(lsRes.Shares)) 1199 1200 g, ctx := errgroup.WithContext(ctx) 1201 1202 // Distribute work 1203 g.Go(func() error { 1204 defer close(work) 1205 for _, share := range lsRes.Shares { 1206 select { 1207 case work <- share: 1208 case <-ctx.Done(): 1209 return ctx.Err() 1210 } 1211 } 1212 return nil 1213 }) 1214 1215 // Spawn workers that'll concurrently work the queue 1216 for i := 0; i < numWorkers; i++ { 1217 g.Go(func() error { 1218 for rs := range work { 1219 1220 // only stat accepted shares 1221 if rs.State != collaboration.ShareState_SHARE_STATE_ACCEPTED { 1222 continue 1223 } 1224 if rs.Share.ResourceId.SpaceId == "" { 1225 // convert backwards compatible share id 1226 rs.Share.ResourceId.StorageId, rs.Share.ResourceId.SpaceId = storagespace.SplitStorageID(rs.Share.ResourceId.StorageId) 1227 } 1228 1229 gatewayClient, err := s.gatewaySelector.Next() 1230 if err != nil { 1231 appctx.GetLogger(ctx).Error(). 1232 Err(err). 1233 Interface("resourceID", rs.Share.ResourceId). 1234 Msg("ListRecievedShares: failed to select next gateway client") 1235 return err 1236 } 1237 sRes, err := gatewayClient.Stat(ctx, &provider.StatRequest{ 1238 Opaque: opaque, 1239 Ref: &provider.Reference{ResourceId: rs.Share.ResourceId}, 1240 ArbitraryMetadataKeys: arbitraryMetadataKeys, 1241 FieldMask: fieldMask, 1242 }) 1243 if err != nil { 1244 appctx.GetLogger(ctx).Error(). 1245 Err(err). 1246 Interface("resourceID", rs.Share.ResourceId). 1247 Msg("ListRecievedShares: failed to make stat call") 1248 return err 1249 } 1250 if sRes.Status.Code != rpc.Code_CODE_OK { 1251 appctx.GetLogger(ctx).Debug(). 1252 Interface("resourceID", rs.Share.ResourceId). 1253 Interface("status", sRes.Status). 1254 Msg("ListRecievedShares: failed to stat the resource") 1255 continue 1256 } 1257 select { 1258 case results <- res{shareid: rs.Share.Id.OpaqueId, info: sRes.Info}: 1259 case <-ctx.Done(): 1260 return ctx.Err() 1261 } 1262 } 1263 return nil 1264 }) 1265 } 1266 1267 // Wait for things to settle down, then close results chan 1268 go func() { 1269 _ = g.Wait() // error is checked later 1270 close(results) 1271 }() 1272 1273 // some results might have been skipped, so we cannot preallocate the map 1274 shareMetaData := make(map[string]*provider.ResourceInfo) 1275 for r := range results { 1276 shareMetaData[r.shareid] = r.info 1277 } 1278 1279 if err := g.Wait(); err != nil { 1280 return nil, nil, err 1281 } 1282 1283 return lsRes.Shares, shareMetaData, nil 1284 } 1285 1286 func findEarliestShare(receivedShares []*collaboration.ReceivedShare, shareInfo map[string]*provider.ResourceInfo) (earliestShare *collaboration.Share) { 1287 for _, rs := range receivedShares { 1288 var hasCurrentMd bool 1289 var hasEarliestMd bool 1290 1291 current := rs.Share 1292 // We cannot assume that every share has metadata 1293 if current.Id != nil { 1294 _, hasCurrentMd = shareInfo[current.Id.OpaqueId] 1295 } 1296 if earliestShare != nil && earliestShare.Id != nil { 1297 _, hasEarliestMd = shareInfo[earliestShare.Id.OpaqueId] 1298 } 1299 1300 switch { 1301 case earliestShare == nil && hasCurrentMd: 1302 earliestShare = current 1303 // ignore if one of the shares has no metadata 1304 case !hasEarliestMd || !hasCurrentMd: 1305 continue 1306 case shareInfo[current.Id.OpaqueId].Mtime.Seconds > shareInfo[earliestShare.Id.OpaqueId].Mtime.Seconds: 1307 earliestShare = current 1308 case shareInfo[current.Id.OpaqueId].Mtime.Seconds == shareInfo[earliestShare.Id.OpaqueId].Mtime.Seconds && 1309 shareInfo[current.Id.OpaqueId].Mtime.Nanos > shareInfo[earliestShare.Id.OpaqueId].Mtime.Nanos: 1310 earliestShare = current 1311 } 1312 } 1313 return earliestShare 1314 } 1315 1316 func buildReferenceInShare(ref *provider.Reference, s *collaboration.ReceivedShare) *provider.Reference { 1317 path := ref.Path 1318 if isShareJailRoot(ref.ResourceId) { 1319 // we need to cut off the mountpoint from the path in the request reference 1320 path = utils.MakeRelativePath(strings.TrimPrefix(strings.TrimPrefix(path, "./"), s.MountPoint.Path)) 1321 } 1322 return &provider.Reference{ 1323 ResourceId: s.Share.ResourceId, 1324 Path: path, 1325 } 1326 } 1327 1328 // isRename checks if the two references lie in the responsibility of the sharesstorageprovider and if a rename occurs 1329 func isRename(s, d *provider.Reference) bool { 1330 // if the source is a share jail child where the path is . 1331 return ((isShareJailChild(s.ResourceId) && s.Path == ".") || 1332 // or if the source is the share jail with a single path segment, e.g. './old' 1333 (isShareJailRoot(s.ResourceId) && len(strings.SplitN(s.Path, "/", 3)) == 2)) && 1334 // and if the destination is the share jail a single path segment, e.g. './new' 1335 isShareJailRoot(d.ResourceId) && len(strings.SplitN(d.Path, "/", 3)) == 2 1336 } 1337 1338 func isShareJailChild(id *provider.ResourceId) bool { 1339 return id.SpaceId == utils.ShareStorageSpaceID && id.OpaqueId != utils.ShareStorageSpaceID 1340 } 1341 1342 func isShareJailRoot(id *provider.ResourceId) bool { 1343 return id.SpaceId == utils.ShareStorageSpaceID && id.OpaqueId == utils.ShareStorageSpaceID 1344 } 1345 1346 func isVirtualRoot(ref *provider.Reference) bool { 1347 return isShareJailRoot(ref.ResourceId) && (ref.Path == "" || ref.Path == "." || ref.Path == "./") 1348 }