diff --git a/go.mod b/go.mod index 4b0048bab..3538cf06d 100644 --- a/go.mod +++ b/go.mod @@ -15,7 +15,7 @@ require ( github.com/cenkalti/backoff v2.2.1+incompatible github.com/coreos/go-oidc/v3 v3.10.0 github.com/cs3org/go-cs3apis v0.0.0-20231023073225-7748710e0781 - github.com/cs3org/reva/v2 v2.19.2-0.20240613123928-7fb5f11a24cf + github.com/cs3org/reva/v2 v2.19.2-0.20240618080316-ed0273c9db9b github.com/dhowden/tag v0.0.0-20230630033851-978a0926ee25 github.com/dutchcoders/go-clamd v0.0.0-20170520113014-b970184f4d9e github.com/egirna/icap-client v0.1.1 diff --git a/go.sum b/go.sum index 314807368..3886058b6 100644 --- a/go.sum +++ b/go.sum @@ -1025,8 +1025,8 @@ github.com/crewjam/saml v0.4.14 h1:g9FBNx62osKusnFzs3QTN5L9CVA/Egfgm+stJShzw/c= github.com/crewjam/saml v0.4.14/go.mod h1:UVSZCf18jJkk6GpWNVqcyQJMD5HsRugBPf4I1nl2mME= github.com/cs3org/go-cs3apis v0.0.0-20231023073225-7748710e0781 h1:BUdwkIlf8IS2FasrrPg8gGPHQPOrQ18MS1Oew2tmGtY= github.com/cs3org/go-cs3apis v0.0.0-20231023073225-7748710e0781/go.mod h1:UXha4TguuB52H14EMoSsCqDj7k8a/t7g4gVP+bgY5LY= -github.com/cs3org/reva/v2 v2.19.2-0.20240613123928-7fb5f11a24cf h1:AQLf+bZfcEn4zKQiH5vLtBpOx7onvbCQ4Z9X4i6SKNM= -github.com/cs3org/reva/v2 v2.19.2-0.20240613123928-7fb5f11a24cf/go.mod h1:Rb2XnhpGKnH7k6WBFZlMygbyBxW6ma09Z4Uk+ro0v+A= +github.com/cs3org/reva/v2 v2.19.2-0.20240618080316-ed0273c9db9b h1:IPFiNd8Xev9WxAt+LkJUxnbyU8Y1rGi5Ha0S+ZNObr8= +github.com/cs3org/reva/v2 v2.19.2-0.20240618080316-ed0273c9db9b/go.mod h1:Rb2XnhpGKnH7k6WBFZlMygbyBxW6ma09Z4Uk+ro0v+A= github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4= github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg= github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= diff --git a/services/frontend/pkg/command/events.go b/services/frontend/pkg/command/events.go index 430b90dd1..248716553 100644 --- a/services/frontend/pkg/command/events.go +++ b/services/frontend/pkg/command/events.go @@ -3,7 +3,6 @@ package command import ( "context" "errors" - "fmt" "github.com/cs3org/reva/v2/pkg/events" "github.com/cs3org/reva/v2/pkg/events/stream" @@ -187,25 +186,3 @@ func updateShareRequest(shareID *collaboration.ShareId, uid *user.UserId) *colla UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"state"}}, } } - -// getSharesList gets the list of all shares for the given user. -func getSharesList(ctx context.Context, gatewaySelector pool.Selectable[gateway.GatewayAPIClient], uid *user.UserId) (*collaboration.ListReceivedSharesResponse, error) { - gwc, err := gatewaySelector.Next() - if err != nil { - return nil, err - } - shares, err := gwc.ListReceivedShares(ctx, &collaboration.ListReceivedSharesRequest{ - Opaque: utils.AppendJSONToOpaque(nil, "userid", uid), - }) - if err != nil { - return nil, err - } - - if shares.Status.Code != rpc.Code_CODE_OK { - if shares.Status.Code == rpc.Code_CODE_NOT_FOUND { - return nil, fmt.Errorf("not found") - } - return nil, fmt.Errorf(shares.GetStatus().GetMessage()) - } - return shares, nil -} diff --git a/services/graph/pkg/errorcode/cs3.go b/services/graph/pkg/errorcode/cs3.go index 9367d93fc..37524bacd 100644 --- a/services/graph/pkg/errorcode/cs3.go +++ b/services/graph/pkg/errorcode/cs3.go @@ -5,6 +5,7 @@ import ( cs3rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1" provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" + "github.com/cs3org/reva/v2/pkg/utils" ) @@ -19,11 +20,12 @@ import ( // This function is particularly useful when dealing with CS3 responses, // and a unified error handling within the application is necessary. func FromCS3Status(status *cs3rpc.Status, inerr error, ignore ...cs3rpc.Code) error { - if inerr != nil { - return Error{msg: inerr.Error(), errorCode: GeneralException} - } + err := Error{errorCode: GeneralException, msg: "unspecified error has occurred", origin: ErrorOriginCS3} - err := Error{errorCode: GeneralException, msg: "unspecified error has occurred"} + if inerr != nil { + err.msg = inerr.Error() + return err + } if status != nil { err.msg = status.GetMessage() diff --git a/services/graph/pkg/errorcode/cs3_test.go b/services/graph/pkg/errorcode/cs3_test.go index 5b160e7ad..45ec797c8 100644 --- a/services/graph/pkg/errorcode/cs3_test.go +++ b/services/graph/pkg/errorcode/cs3_test.go @@ -44,6 +44,11 @@ func TestFromCS3Status(t *testing.T) { } for _, test := range tests { + var e errorcode.Error + if errors.As(test.expected, &e) { + test.expected = e.WithOrigin(errorcode.ErrorOriginCS3) + } + if got := errorcode.FromCS3Status(test.status, test.err, test.ignore...); !reflect.DeepEqual(got, test.expected) { t.Error("Test Failed: {} expected, received: {}", test.expected, got) } @@ -56,7 +61,7 @@ func TestFromStat(t *testing.T) { err error result error }{ - {nil, errors.New("some error"), errorcode.New(errorcode.GeneralException, "some error")}, + {nil, errors.New("some error"), errorcode.New(errorcode.GeneralException, "some error").WithOrigin(errorcode.ErrorOriginCS3)}, {&provider.StatResponse{Status: &cs3rpc.Status{Code: cs3rpc.Code_CODE_OK}}, nil, nil}, } diff --git a/services/graph/pkg/errorcode/errorcode.go b/services/graph/pkg/errorcode/errorcode.go index f4a9bda41..297c334e1 100644 --- a/services/graph/pkg/errorcode/errorcode.go +++ b/services/graph/pkg/errorcode/errorcode.go @@ -12,15 +12,28 @@ import ( libregraph "github.com/owncloud/libre-graph-api-go" ) -// ErrorCode defines code as used in MS Graph - see https://docs.microsoft.com/en-us/graph/errors?context=graph%2Fapi%2F1.0&view=graph-rest-1.0 -type ErrorCode int - // Error defines a custom error struct, containing and MS Graph error code and a textual error message type Error struct { errorCode ErrorCode msg string + origin ErrorOrigin } +// ErrorOrigin gives information about where the error originated +type ErrorOrigin int + +const ( + // ErrorOriginUnknown is the default error source + // and indicates that the error does not have any information about its origin + ErrorOriginUnknown ErrorOrigin = iota + + // ErrorOriginCS3 indicates that the error originated from a CS3 service + ErrorOriginCS3 +) + +// ErrorCode defines code as used in MS Graph - see https://docs.microsoft.com/en-us/graph/errors?context=graph%2Fapi%2F1.0&view=graph-rest-1.0 +type ErrorCode int + // List taken from https://github.com/microsoft/microsoft-graph-docs-1/blob/main/concepts/errors.md#code-property const ( // AccessDenied defines the error if the caller doesn't have permission to perform the action. @@ -148,7 +161,7 @@ func (e ErrorCode) String() string { return errorCodes[e] } -// Error return the concatenation of the error string and optional message +// Error returns the concatenation of the error string and optional message func (e Error) Error() string { errString := errorCodes[e.errorCode] if e.msg != "" { @@ -161,13 +174,35 @@ func (e Error) GetCode() ErrorCode { return e.errorCode } +// GetOrigin returns the source of the error +func (e Error) GetOrigin() ErrorOrigin { + return e.origin +} + +// WithOrigin returns a new Error with the provided origin +func (e Error) WithOrigin(o ErrorOrigin) Error { + e.origin = o + return e +} + // RenderError render the Graph Error based on a code or default one func RenderError(w http.ResponseWriter, r *http.Request, err error) { - var errcode Error - if errors.As(err, &errcode) { - errcode.Render(w, r) + e, ok := ToError(err) + if !ok { + GeneralException.Render(w, r, http.StatusInternalServerError, err.Error()) return } - GeneralException.Render(w, r, http.StatusInternalServerError, err.Error()) + e.Render(w, r) +} + +// ToError checks if the error is of type Error and returns it, +// the second parameter indicates if the error conversion was successful +func ToError(err error) (Error, bool) { + var e Error + if errors.As(err, &e) { + return e, true + } + + return Error{}, false } diff --git a/services/graph/pkg/service/v0/api_driveitem_permissions_test.go b/services/graph/pkg/service/v0/api_driveitem_permissions_test.go index 20ee36a42..3d9dd889f 100644 --- a/services/graph/pkg/service/v0/api_driveitem_permissions_test.go +++ b/services/graph/pkg/service/v0/api_driveitem_permissions_test.go @@ -17,12 +17,6 @@ import ( provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" storageprovider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" types "github.com/cs3org/go-cs3apis/cs3/types/v1beta1" - roleconversions "github.com/cs3org/reva/v2/pkg/conversions" - revactx "github.com/cs3org/reva/v2/pkg/ctx" - "github.com/cs3org/reva/v2/pkg/rgrpc/status" - "github.com/cs3org/reva/v2/pkg/storagespace" - "github.com/cs3org/reva/v2/pkg/utils" - cs3mocks "github.com/cs3org/reva/v2/tests/cs3mocks/mocks" "github.com/go-chi/chi/v5" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" @@ -31,6 +25,13 @@ import ( "github.com/tidwall/gjson" "google.golang.org/grpc" + roleconversions "github.com/cs3org/reva/v2/pkg/conversions" + revactx "github.com/cs3org/reva/v2/pkg/ctx" + "github.com/cs3org/reva/v2/pkg/rgrpc/status" + "github.com/cs3org/reva/v2/pkg/storagespace" + "github.com/cs3org/reva/v2/pkg/utils" + cs3mocks "github.com/cs3org/reva/v2/tests/cs3mocks/mocks" + "github.com/owncloud/ocis/v2/ocis-pkg/log" "github.com/owncloud/ocis/v2/services/graph/mocks" "github.com/owncloud/ocis/v2/services/graph/pkg/config/defaults" @@ -249,7 +250,7 @@ var _ = Describe("DriveItemPermissionsService", func() { statResponse.Status = status.NewNotFound(context.Background(), "not found") permission, err := driveItemPermissionsService.Invite(context.Background(), driveItemId, driveItemInvite) Expect(err).To(HaveOccurred()) - Expect(err).To(MatchError(errorcode.New(errorcode.ItemNotFound, "not found"))) + Expect(err).To(MatchError(errorcode.New(errorcode.ItemNotFound, "not found").WithOrigin(errorcode.ErrorOriginCS3))) Expect(permission).To(BeZero()) }) }) @@ -999,7 +1000,7 @@ var _ = Describe("DriveItemPermissionsService", func() { driveItemPermission.SetExpirationDateTime(expiration) res, err := driveItemPermissionsService.UpdatePermission(context.Background(), driveItemId, "permissionid", driveItemPermission) - Expect(err).To(MatchError(errorcode.New(errorcode.InvalidRequest, "expiration date is in the past"))) + Expect(err).To(MatchError(errorcode.New(errorcode.InvalidRequest, "expiration date is in the past").WithOrigin(errorcode.ErrorOriginCS3))) Expect(res).To(BeZero()) }) }) diff --git a/services/graph/pkg/service/v0/api_drives_drive_item.go b/services/graph/pkg/service/v0/api_drives_drive_item.go index 962d42776..2712b99ec 100644 --- a/services/graph/pkg/service/v0/api_drives_drive_item.go +++ b/services/graph/pkg/service/v0/api_drives_drive_item.go @@ -360,7 +360,7 @@ func (api DrivesDriveItemApi) DeleteDriveItem(w http.ResponseWriter, r *http.Req shareID := ExtractShareIdFromResourceId(itemID) if err := api.drivesDriveItemService.UnmountShare(ctx, shareID); err != nil { - api.logger.Debug().Err(err).Msg(err.Error()) + api.logger.Debug().Err(err).Msg(ErrUnmountShare.Error()) errorcode.RenderError(w, r, err) return } @@ -518,8 +518,15 @@ func (api DrivesDriveItemApi) CreateDriveItem(w http.ResponseWriter, r *http.Req mountedShares, err := api.drivesDriveItemService.MountShare(ctx, &resourceId, requestDriveItem.GetName()) if err != nil { - api.logger.Debug().Err(err).Msg(err.Error()) - errorcode.RenderError(w, r, err) + api.logger.Debug().Err(err).Msg(ErrMountShare.Error()) + + switch e, ok := errorcode.ToError(err); { + case ok && e.GetOrigin() == errorcode.ErrorOriginCS3 && e.GetCode() == errorcode.ItemNotFound: + ErrDriveItemConversion.Render(w, r) + default: + errorcode.RenderError(w, r, err) + } + return } diff --git a/services/graph/pkg/service/v0/api_drives_drive_item_test.go b/services/graph/pkg/service/v0/api_drives_drive_item_test.go index 1d2a3ae34..7cdc245fb 100644 --- a/services/graph/pkg/service/v0/api_drives_drive_item_test.go +++ b/services/graph/pkg/service/v0/api_drives_drive_item_test.go @@ -152,7 +152,7 @@ var _ = Describe("DrivesDriveItemService", func() { Once() _, err := drivesDriveItemService.GetShare(context.Background(), &collaborationv1beta1.ShareId{}) - Expect(err).To(MatchError(errorcode.New(errorcode.GeneralException, someErr.Error()))) + Expect(err).To(MatchError(errorcode.New(errorcode.GeneralException, someErr.Error()).WithOrigin(errorcode.ErrorOriginCS3))) }) It("fails if share lookup does not report an error but the status is off", func() { @@ -161,12 +161,12 @@ var _ = Describe("DrivesDriveItemService", func() { EXPECT(). GetReceivedShare(context.Background(), mock.Anything, mock.Anything). Return(&collaborationv1beta1.GetReceivedShareResponse{ - Status: status.NewNotFound(context.Background(), someErr.Error()), + Status: status.NewInvalid(context.Background(), someErr.Error()), }, nil). Once() _, err := drivesDriveItemService.GetShare(context.Background(), &collaborationv1beta1.ShareId{}) - Expect(err).To(MatchError(errorcode.New(errorcode.ItemNotFound, someErr.Error()))) + Expect(err).To(MatchError(errorcode.New(errorcode.InvalidRequest, someErr.Error()).WithOrigin(errorcode.ErrorOriginCS3))) }) It("successfully returns a share", func() { @@ -219,7 +219,7 @@ var _ = Describe("DrivesDriveItemService", func() { request.Share.State = collaborationv1beta1.ShareState_SHARE_STATE_ACCEPTED request.UpdateMask.Paths = append(request.UpdateMask.Paths, "state") }) - Expect(err).To(MatchError(errorcode.New(errorcode.GeneralException, someErr.Error()))) + Expect(err).To(MatchError(errorcode.New(errorcode.GeneralException, someErr.Error()).WithOrigin(errorcode.ErrorOriginCS3))) }) It("fails if share update does not report an error but the status is off", func() { @@ -236,7 +236,7 @@ var _ = Describe("DrivesDriveItemService", func() { request.Share.State = collaborationv1beta1.ShareState_SHARE_STATE_ACCEPTED request.UpdateMask.Paths = append(request.UpdateMask.Paths, "state") }) - Expect(err).To(MatchError(errorcode.New(errorcode.ItemNotFound, someErr.Error()))) + Expect(err).To(MatchError(errorcode.New(errorcode.ItemNotFound, someErr.Error()).WithOrigin(errorcode.ErrorOriginCS3))) }) }) @@ -265,7 +265,7 @@ var _ = Describe("DrivesDriveItemService", func() { request.Share.State = collaborationv1beta1.ShareState_SHARE_STATE_ACCEPTED request.UpdateMask.Paths = append(request.UpdateMask.Paths, "state") }) - Expect(err).To(MatchError(errorcode.New(errorcode.GeneralException, someErr.Error()))) + Expect(err).To(MatchError(errorcode.New(errorcode.GeneralException, someErr.Error()).WithOrigin(errorcode.ErrorOriginCS3))) Expect(err.(interface{ Unwrap() []error }).Unwrap()).To(HaveLen(2)) Expect(shares).To(HaveLen(1)) }) @@ -281,7 +281,7 @@ var _ = Describe("DrivesDriveItemService", func() { Once() err := drivesDriveItemService.UnmountShare(context.Background(), &collaborationv1beta1.ShareId{}) - Expect(err).To(MatchError(errorcode.New(errorcode.GeneralException, someErr.Error()))) + Expect(err).To(MatchError(errorcode.New(errorcode.GeneralException, someErr.Error()).WithOrigin(errorcode.ErrorOriginCS3))) }) It("requests only accepted shares to be unmounted", func() { @@ -358,7 +358,7 @@ var _ = Describe("DrivesDriveItemService", func() { Times(1) err := drivesDriveItemService.UnmountShare(context.Background(), &collaborationv1beta1.ShareId{}) - Expect(err).To(MatchError(errorcode.New(errorcode.GeneralException, someErr.Error()))) + Expect(err).To(MatchError(errorcode.New(errorcode.GeneralException, someErr.Error()).WithOrigin(errorcode.ErrorOriginCS3))) Expect(err.(interface{ Unwrap() []error }).Unwrap()).To(HaveLen(1)) }) }) @@ -436,7 +436,7 @@ var _ = Describe("DrivesDriveItemService", func() { Times(3) shares, err := drivesDriveItemService.MountShare(context.Background(), nil, "some") - Expect(err).To(MatchError(errorcode.New(errorcode.GeneralException, someErr.Error()))) + Expect(err).To(MatchError(errorcode.New(errorcode.GeneralException, someErr.Error()).WithOrigin(errorcode.ErrorOriginCS3))) Expect(err.(interface{ Unwrap() []error }).Unwrap()).To(HaveLen(3)) Expect(shares).To(HaveLen(0)) }) diff --git a/vendor/github.com/cs3org/reva/v2/internal/grpc/services/usershareprovider/usershareprovider.go b/vendor/github.com/cs3org/reva/v2/internal/grpc/services/usershareprovider/usershareprovider.go index 178c6f78d..4bec8dd75 100644 --- a/vendor/github.com/cs3org/reva/v2/internal/grpc/services/usershareprovider/usershareprovider.go +++ b/vendor/github.com/cs3org/reva/v2/internal/grpc/services/usershareprovider/usershareprovider.go @@ -547,75 +547,6 @@ func (s *service) UpdateReceivedShare(ctx context.Context, req *collaboration.Up }, nil } -// GetAvailableMountpoint returns a new or existing mountpoint -func GetAvailableMountpoint(ctx context.Context, gwc gateway.GatewayAPIClient, id *provider.ResourceId, name string, userId *userpb.UserId) (string, error) { - listReceivedSharesReq := &collaboration.ListReceivedSharesRequest{} - if userId != nil { - listReceivedSharesReq.Opaque = utils.AppendJSONToOpaque(nil, "userid", userId) - } - - listReceivedSharesRes, err := gwc.ListReceivedShares(ctx, listReceivedSharesReq) - if err != nil { - return "", errtypes.InternalError("grpc list received shares request failed") - } - - if err := errtypes.NewErrtypeFromStatus(listReceivedSharesRes.GetStatus()); err != nil { - return "", err - } - - base := filepath.Clean(name) - mount := base - existingMountpoint := "" - mountedShares := make([]string, 0, len(listReceivedSharesRes.GetShares())) - var pathExists bool - - for _, s := range listReceivedSharesRes.GetShares() { - resourceIDEqual := utils.ResourceIDEqual(s.GetShare().GetResourceId(), id) - - if resourceIDEqual && s.State == collaboration.ShareState_SHARE_STATE_ACCEPTED { - // a share to the resource already exists and is mounted, remembers the mount point - _, err := utils.GetResourceByID(ctx, s.GetShare().GetResourceId(), gwc) - if err == nil { - existingMountpoint = s.GetMountPoint().GetPath() - } - } - - if s.State == collaboration.ShareState_SHARE_STATE_ACCEPTED { - // collect all accepted mount points - mountedShares = append(mountedShares, s.GetMountPoint().GetPath()) - if s.GetMountPoint().GetPath() == mount { - // does the shared resource still exist? - _, err := utils.GetResourceByID(ctx, s.GetShare().GetResourceId(), gwc) - if err == nil { - pathExists = true - } - // TODO we could delete shares here if the stat returns code NOT FOUND ... but listening for file deletes would be better - } - } - } - - if existingMountpoint != "" { - // we want to reuse the same mountpoint for all unmounted shares to the same resource - return existingMountpoint, nil - } - - // If the mount point really already exists, we need to insert a number into the filename - if pathExists { - // now we have a list of shares, we want to iterate over all of them and check for name collisions agents a mount points list - for i := 1; i <= len(mountedShares)+1; i++ { - ext := filepath.Ext(base) - name := strings.TrimSuffix(base, ext) - - mount = name + " (" + strconv.Itoa(i) + ")" + ext - if !slices.Contains(mountedShares, mount) { - return mount, nil - } - } - } - - return mount, nil -} - func setReceivedShareMountPoint(ctx context.Context, gwc gateway.GatewayAPIClient, req *collaboration.UpdateReceivedShareRequest) (*rpc.Status, error) { receivedShare, err := gwc.GetReceivedShare(ctx, &collaboration.GetReceivedShareRequest{ Ref: &collaboration.ShareReference{ @@ -653,7 +584,7 @@ func setReceivedShareMountPoint(ctx context.Context, gwc gateway.GatewayAPIClien _ = utils.ReadJSONFromOpaque(req.Opaque, "userid", &userID) // check if the requested mount point is available and if not, find a suitable one - availableMountpoint, err := GetAvailableMountpoint(ctx, gwc, + availableMountpoint, _, err := GetMountpointAndUnmountedShares(ctx, gwc, resourceStat.GetInfo().GetId(), resourceStat.GetInfo().GetName(), userID, @@ -673,3 +604,78 @@ func setReceivedShareMountPoint(ctx context.Context, gwc gateway.GatewayAPIClien return status.NewOK(ctx), nil } + +// GetMountpointAndUnmountedShares returns a new or existing mountpoint for the given info and produces a list of unmounted received shares for the same resource +func GetMountpointAndUnmountedShares(ctx context.Context, gwc gateway.GatewayAPIClient, id *provider.ResourceId, name string, userId *userpb.UserId) (string, []*collaboration.ReceivedShare, error) { + listReceivedSharesReq := &collaboration.ListReceivedSharesRequest{} + if userId != nil { + listReceivedSharesReq.Opaque = utils.AppendJSONToOpaque(nil, "userid", userId) + } + + listReceivedSharesRes, err := gwc.ListReceivedShares(ctx, listReceivedSharesReq) + if err != nil { + return "", nil, errtypes.InternalError("grpc list received shares request failed") + } + + if err := errtypes.NewErrtypeFromStatus(listReceivedSharesRes.GetStatus()); err != nil { + return "", nil, err + } + + unmountedShares := []*collaboration.ReceivedShare{} + base := filepath.Clean(name) + mount := base + existingMountpoint := "" + mountedShares := make([]string, 0, len(listReceivedSharesRes.GetShares())) + var pathExists bool + + for _, s := range listReceivedSharesRes.GetShares() { + resourceIDEqual := utils.ResourceIDEqual(s.GetShare().GetResourceId(), id) + + if resourceIDEqual && s.State == collaboration.ShareState_SHARE_STATE_ACCEPTED { + // a share to the resource already exists and is mounted, remembers the mount point + _, err := utils.GetResourceByID(ctx, s.GetShare().GetResourceId(), gwc) + if err == nil { + existingMountpoint = s.GetMountPoint().GetPath() + } + } + + if resourceIDEqual && s.State != collaboration.ShareState_SHARE_STATE_ACCEPTED { + // a share to the resource already exists but is not mounted, collect the unmounted share + unmountedShares = append(unmountedShares, s) + } + + if s.State == collaboration.ShareState_SHARE_STATE_ACCEPTED { + // collect all accepted mount points + mountedShares = append(mountedShares, s.GetMountPoint().GetPath()) + if s.GetMountPoint().GetPath() == mount { + // does the shared resource still exist? + _, err := utils.GetResourceByID(ctx, s.GetShare().GetResourceId(), gwc) + if err == nil { + pathExists = true + } + // TODO we could delete shares here if the stat returns code NOT FOUND ... but listening for file deletes would be better + } + } + } + + if existingMountpoint != "" { + // we want to reuse the same mountpoint for all unmounted shares to the same resource + return existingMountpoint, unmountedShares, nil + } + + // If the mount point really already exists, we need to insert a number into the filename + if pathExists { + // now we have a list of shares, we want to iterate over all of them and check for name collisions agents a mount points list + for i := 1; i <= len(mountedShares)+1; i++ { + ext := filepath.Ext(base) + name := strings.TrimSuffix(base, ext) + + mount = name + " (" + strconv.Itoa(i) + ")" + ext + if !slices.Contains(mountedShares, mount) { + return mount, unmountedShares, nil + } + } + } + + return mount, unmountedShares, nil +} diff --git a/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocs/handlers/apps/sharing/shares/pending.go b/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocs/handlers/apps/sharing/shares/pending.go index 5bfd78cd2..950286244 100644 --- a/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocs/handlers/apps/sharing/shares/pending.go +++ b/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocs/handlers/apps/sharing/shares/pending.go @@ -34,11 +34,11 @@ import ( "github.com/pkg/errors" "google.golang.org/protobuf/types/known/fieldmaskpb" + "github.com/cs3org/reva/v2/internal/grpc/services/usershareprovider" "github.com/cs3org/reva/v2/internal/http/services/owncloud/ocs/response" "github.com/cs3org/reva/v2/pkg/appctx" "github.com/cs3org/reva/v2/pkg/conversions" "github.com/cs3org/reva/v2/pkg/errtypes" - "github.com/cs3org/reva/v2/pkg/utils" ) const ( @@ -73,8 +73,13 @@ func (h *Handler) AcceptReceivedShare(w http.ResponseWriter, r *http.Request) { response.WriteOCSResponse(w, r, *ocsResponse, nil) return } - - unmountedShares, err := getUnmountedShares(ctx, client, sharedResource.GetInfo().GetId()) + mount, unmountedShares, err := usershareprovider.GetMountpointAndUnmountedShares( + ctx, + client, + sharedResource.GetInfo().GetId(), + sharedResource.GetInfo().GetName(), + nil, + ) if err != nil { response.WriteOCSError(w, r, response.MetaServerError.StatusCode, "could not determine mountpoint", err) return @@ -82,8 +87,12 @@ func (h *Handler) AcceptReceivedShare(w http.ResponseWriter, r *http.Request) { // first update the requested share receivedShare.State = collaboration.ShareState_SHARE_STATE_ACCEPTED + // we need to add a path to the share + receivedShare.MountPoint = &provider.Reference{ + Path: mount, + } - updateMask := &fieldmaskpb.FieldMask{Paths: []string{"state"}} + updateMask := &fieldmaskpb.FieldMask{Paths: []string{"state", "mount_point"}} data, meta, err := h.updateReceivedShare(r.Context(), receivedShare, updateMask) if err != nil { // we log an error for affected shares, for the actual share we return an error @@ -100,6 +109,10 @@ func (h *Handler) AcceptReceivedShare(w http.ResponseWriter, r *http.Request) { } rs.State = collaboration.ShareState_SHARE_STATE_ACCEPTED + // set the same mountpoint as for the requested received share + rs.MountPoint = &provider.Reference{ + Path: mount, + } _, _, err := h.updateReceivedShare(r.Context(), rs, updateMask) if err != nil { @@ -314,25 +327,6 @@ func getReceivedShareFromID(ctx context.Context, client gateway.GatewayAPIClient return s.Share, nil } -func getUnmountedShares(ctx context.Context, gwc gateway.GatewayAPIClient, id *provider.ResourceId) ([]*collaboration.ReceivedShare, error) { - var unmountedShares []*collaboration.ReceivedShare - receivedShares, err := listReceivedShares(ctx, gwc) - if err != nil { - return unmountedShares, err - } - - for _, s := range receivedShares { - resourceIDEqual := utils.ResourceIDEqual(s.GetShare().GetResourceId(), id) - - if resourceIDEqual && s.State != collaboration.ShareState_SHARE_STATE_ACCEPTED { - // a share to the resource already exists but is not mounted, collect the unmounted share - unmountedShares = append(unmountedShares, s) - } - } - - return unmountedShares, err -} - // getSharedResource attempts to get a shared resource from the storage from the resource reference. func getSharedResource(ctx context.Context, client gateway.GatewayAPIClient, resID *provider.ResourceId) (*provider.StatResponse, *response.Response) { res, err := client.Stat(ctx, &provider.StatRequest{ diff --git a/vendor/modules.txt b/vendor/modules.txt index 7a844eef3..b87ee9e60 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -366,7 +366,7 @@ github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1 github.com/cs3org/go-cs3apis/cs3/storage/registry/v1beta1 github.com/cs3org/go-cs3apis/cs3/tx/v1beta1 github.com/cs3org/go-cs3apis/cs3/types/v1beta1 -# github.com/cs3org/reva/v2 v2.19.2-0.20240613123928-7fb5f11a24cf +# github.com/cs3org/reva/v2 v2.19.2-0.20240618080316-ed0273c9db9b ## explicit; go 1.21 github.com/cs3org/reva/v2/cmd/revad/internal/grace github.com/cs3org/reva/v2/cmd/revad/runtime