github.com/cs3org/reva/v2@v2.27.7/internal/grpc/services/storageprovider/storageprovider.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 storageprovider
    20  
    21  import (
    22  	"context"
    23  	"encoding/json"
    24  	"fmt"
    25  	"net/url"
    26  	"os"
    27  	"path"
    28  	"sort"
    29  	"strconv"
    30  	"strings"
    31  	"time"
    32  
    33  	rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
    34  	provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
    35  	typesv1beta1 "github.com/cs3org/go-cs3apis/cs3/types/v1beta1"
    36  	"github.com/cs3org/reva/v2/pkg/appctx"
    37  	"github.com/cs3org/reva/v2/pkg/conversions"
    38  	ctxpkg "github.com/cs3org/reva/v2/pkg/ctx"
    39  	"github.com/cs3org/reva/v2/pkg/errtypes"
    40  	"github.com/cs3org/reva/v2/pkg/events"
    41  	"github.com/cs3org/reva/v2/pkg/events/stream"
    42  	"github.com/cs3org/reva/v2/pkg/mime"
    43  	"github.com/cs3org/reva/v2/pkg/rgrpc"
    44  	"github.com/cs3org/reva/v2/pkg/rgrpc/status"
    45  	"github.com/cs3org/reva/v2/pkg/rhttp/router"
    46  	"github.com/cs3org/reva/v2/pkg/storage"
    47  	"github.com/cs3org/reva/v2/pkg/storage/fs/registry"
    48  	"github.com/cs3org/reva/v2/pkg/storagespace"
    49  	"github.com/cs3org/reva/v2/pkg/utils"
    50  	"github.com/mitchellh/mapstructure"
    51  	"github.com/pkg/errors"
    52  	"github.com/rs/zerolog"
    53  	"go.opentelemetry.io/otel/attribute"
    54  	"google.golang.org/grpc"
    55  )
    56  
    57  // name is the Tracer name used to identify this instrumentation library.
    58  const tracerName = "storageprovider"
    59  
    60  func init() {
    61  	rgrpc.Register("storageprovider", New)
    62  }
    63  
    64  type config struct {
    65  	Driver              string                            `mapstructure:"driver" docs:"localhome;The storage driver to be used."`
    66  	Drivers             map[string]map[string]interface{} `mapstructure:"drivers" docs:"url:pkg/storage/fs/localhome/localhome.go"`
    67  	DataServerURL       string                            `mapstructure:"data_server_url" docs:"http://localhost/data;The URL for the data server."`
    68  	ExposeDataServer    bool                              `mapstructure:"expose_data_server" docs:"false;Whether to expose data server."` // if true the client will be able to upload/download directly to it
    69  	AvailableXS         map[string]uint32                 `mapstructure:"available_checksums" docs:"nil;List of available checksums."`
    70  	CustomMimeTypesJSON string                            `mapstructure:"custom_mimetypes_json" docs:"nil;An optional mapping file with the list of supported custom file extensions and corresponding mime types."`
    71  	MountID             string                            `mapstructure:"mount_id"`
    72  	UploadExpiration    int64                             `mapstructure:"upload_expiration" docs:"0;Duration for how long uploads will be valid."`
    73  	Events              eventconfig                       `mapstructure:"events" docs:"0;Event stream configuration"`
    74  }
    75  
    76  type eventconfig struct {
    77  	Endpoint             string `mapstructure:"nats_address" docs:"address of the nats server"`
    78  	Cluster              string `mapstructure:"nats_clusterid" docs:"clusterid of the nats server"`
    79  	TLSInsecure          bool   `mapstructure:"tls_insecure"  docs:"Whether to verify the server TLS certificates."`
    80  	TLSRootCACertificate string `mapstructure:"tls_root_ca_cert"  docs:"The root CA certificate used to validate the server's TLS certificate."`
    81  	EnableTLS            bool   `mapstructure:"nats_enable_tls" docs:"events tls switch"`
    82  	AuthUsername         string `mapstructure:"nats_username" docs:"event stream username"`
    83  	AuthPassword         string `mapstructure:"nats_password" docs:"event stream password"`
    84  }
    85  
    86  func (c *config) init() {
    87  	if c.Driver == "" {
    88  		c.Driver = "localhome"
    89  	}
    90  
    91  	if c.DataServerURL == "" {
    92  		host, err := os.Hostname()
    93  		if err != nil || host == "" {
    94  			c.DataServerURL = "http://0.0.0.0:19001/data"
    95  		} else {
    96  			c.DataServerURL = fmt.Sprintf("http://%s:19001/data", host)
    97  		}
    98  	}
    99  
   100  	// set sane defaults
   101  	if len(c.AvailableXS) == 0 {
   102  		c.AvailableXS = map[string]uint32{"md5": 100, "unset": 1000}
   103  	}
   104  }
   105  
   106  type Service struct {
   107  	conf          *config
   108  	Storage       storage.FS
   109  	dataServerURL *url.URL
   110  	availableXS   []*provider.ResourceChecksumPriority
   111  }
   112  
   113  func (s *Service) Close() error {
   114  	return s.Storage.Shutdown(context.Background())
   115  }
   116  
   117  func (s *Service) UnprotectedEndpoints() []string { return []string{} }
   118  
   119  func (s *Service) Register(ss *grpc.Server) {
   120  	provider.RegisterProviderAPIServer(ss, s)
   121  	provider.RegisterSpacesAPIServer(ss, s)
   122  }
   123  
   124  func parseXSTypes(xsTypes map[string]uint32) ([]*provider.ResourceChecksumPriority, error) {
   125  	var types = make([]*provider.ResourceChecksumPriority, 0, len(xsTypes))
   126  	for xs, prio := range xsTypes {
   127  		t := PKG2GRPCXS(xs)
   128  		if t == provider.ResourceChecksumType_RESOURCE_CHECKSUM_TYPE_INVALID {
   129  			return nil, errtypes.BadRequest("checksum type is invalid: " + xs)
   130  		}
   131  		xsPrio := &provider.ResourceChecksumPriority{
   132  			Priority: prio,
   133  			Type:     t,
   134  		}
   135  		types = append(types, xsPrio)
   136  	}
   137  	return types, nil
   138  }
   139  
   140  func parseConfig(m map[string]interface{}) (*config, error) {
   141  	c := &config{}
   142  	if err := mapstructure.Decode(m, c); err != nil {
   143  		err = errors.Wrap(err, "error decoding conf")
   144  		return nil, err
   145  	}
   146  	return c, nil
   147  }
   148  
   149  func registerMimeTypes(mappingFile string) error {
   150  	if mappingFile != "" {
   151  		f, err := os.ReadFile(mappingFile)
   152  		if err != nil {
   153  			return fmt.Errorf("storageprovider: error reading the custom mime types file: +%v", err)
   154  		}
   155  		mimeTypes := map[string]string{}
   156  		err = json.Unmarshal(f, &mimeTypes)
   157  		if err != nil {
   158  			return fmt.Errorf("storageprovider: error unmarshalling the custom mime types file: +%v", err)
   159  		}
   160  		// register all mime types that were read
   161  		for e, m := range mimeTypes {
   162  			mime.RegisterMime(e, m)
   163  		}
   164  	}
   165  	return nil
   166  }
   167  
   168  // New creates a new storage provider svc
   169  func New(m map[string]interface{}, ss *grpc.Server, log *zerolog.Logger) (rgrpc.Service, error) {
   170  
   171  	c, err := parseConfig(m)
   172  	if err != nil {
   173  		return nil, err
   174  	}
   175  
   176  	c.init()
   177  
   178  	fs, err := getFS(c, log)
   179  	if err != nil {
   180  		return nil, err
   181  	}
   182  
   183  	// parse data server url
   184  	u, err := url.Parse(c.DataServerURL)
   185  	if err != nil {
   186  		return nil, err
   187  	}
   188  
   189  	// validate available checksums
   190  	xsTypes, err := parseXSTypes(c.AvailableXS)
   191  	if err != nil {
   192  		return nil, err
   193  	}
   194  
   195  	if len(xsTypes) == 0 {
   196  		return nil, errtypes.NotFound("no available checksum, please set in config")
   197  	}
   198  
   199  	// read and register custom mime types if configured
   200  	err = registerMimeTypes(c.CustomMimeTypesJSON)
   201  	if err != nil {
   202  		return nil, err
   203  	}
   204  
   205  	service := &Service{
   206  		conf:          c,
   207  		Storage:       fs,
   208  		dataServerURL: u,
   209  		availableXS:   xsTypes,
   210  	}
   211  
   212  	return service, nil
   213  }
   214  
   215  func (s *Service) SetArbitraryMetadata(ctx context.Context, req *provider.SetArbitraryMetadataRequest) (*provider.SetArbitraryMetadataResponse, error) {
   216  	ctx = ctxpkg.ContextSetLockID(ctx, req.LockId)
   217  
   218  	err := s.Storage.SetArbitraryMetadata(ctx, req.Ref, req.ArbitraryMetadata)
   219  
   220  	return &provider.SetArbitraryMetadataResponse{
   221  		Status: status.NewStatusFromErrType(ctx, "set arbitrary metadata", err),
   222  	}, nil
   223  }
   224  
   225  func (s *Service) UnsetArbitraryMetadata(ctx context.Context, req *provider.UnsetArbitraryMetadataRequest) (*provider.UnsetArbitraryMetadataResponse, error) {
   226  	ctx = ctxpkg.ContextSetLockID(ctx, req.LockId)
   227  
   228  	err := s.Storage.UnsetArbitraryMetadata(ctx, req.Ref, req.ArbitraryMetadataKeys)
   229  
   230  	return &provider.UnsetArbitraryMetadataResponse{
   231  		Status: status.NewStatusFromErrType(ctx, "unset arbitrary metadata", err),
   232  	}, nil
   233  }
   234  
   235  // SetLock puts a lock on the given reference
   236  func (s *Service) SetLock(ctx context.Context, req *provider.SetLockRequest) (*provider.SetLockResponse, error) {
   237  	if !canLockPublicShare(ctx) {
   238  		return &provider.SetLockResponse{
   239  			Status: status.NewPermissionDenied(ctx, nil, "no permission to lock the share"),
   240  		}, nil
   241  	}
   242  	err := s.Storage.SetLock(ctx, req.Ref, req.Lock)
   243  
   244  	return &provider.SetLockResponse{
   245  		Status: status.NewStatusFromErrType(ctx, "set lock", err),
   246  	}, nil
   247  }
   248  
   249  // GetLock returns an existing lock on the given reference
   250  func (s *Service) GetLock(ctx context.Context, req *provider.GetLockRequest) (*provider.GetLockResponse, error) {
   251  	lock, err := s.Storage.GetLock(ctx, req.Ref)
   252  
   253  	return &provider.GetLockResponse{
   254  		Status: status.NewStatusFromErrType(ctx, "get lock", err),
   255  		Lock:   lock,
   256  	}, nil
   257  }
   258  
   259  // RefreshLock refreshes an existing lock on the given reference
   260  func (s *Service) RefreshLock(ctx context.Context, req *provider.RefreshLockRequest) (*provider.RefreshLockResponse, error) {
   261  	if !canLockPublicShare(ctx) {
   262  		return &provider.RefreshLockResponse{
   263  			Status: status.NewPermissionDenied(ctx, nil, "no permission to refresh the share lock"),
   264  		}, nil
   265  	}
   266  
   267  	err := s.Storage.RefreshLock(ctx, req.Ref, req.Lock, req.ExistingLockId)
   268  
   269  	return &provider.RefreshLockResponse{
   270  		Status: status.NewStatusFromErrType(ctx, "refresh lock", err),
   271  	}, nil
   272  }
   273  
   274  // Unlock removes an existing lock from the given reference
   275  func (s *Service) Unlock(ctx context.Context, req *provider.UnlockRequest) (*provider.UnlockResponse, error) {
   276  	if !canLockPublicShare(ctx) {
   277  		return &provider.UnlockResponse{
   278  			Status: status.NewPermissionDenied(ctx, nil, "no permission to unlock the share"),
   279  		}, nil
   280  	}
   281  
   282  	err := s.Storage.Unlock(ctx, req.Ref, req.Lock)
   283  
   284  	return &provider.UnlockResponse{
   285  		Status: status.NewStatusFromErrType(ctx, "unlock", err),
   286  	}, nil
   287  }
   288  
   289  func (s *Service) InitiateFileDownload(ctx context.Context, req *provider.InitiateFileDownloadRequest) (*provider.InitiateFileDownloadResponse, error) {
   290  	// TODO(labkode): maybe add some checks before download starts? eg. check permissions?
   291  	// TODO(labkode): maybe add short-lived token?
   292  	// We now simply point the client to the data server.
   293  	// For example, https://data-server.example.org/home/docs/myfile.txt
   294  	// or ownclouds://data-server.example.org/home/docs/myfile.txt
   295  	log := appctx.GetLogger(ctx)
   296  	u := *s.dataServerURL
   297  	log.Info().Str("data-server", u.String()).Interface("ref", req.Ref).Msg("file download")
   298  
   299  	protocol := &provider.FileDownloadProtocol{Expose: s.conf.ExposeDataServer}
   300  
   301  	if utils.IsRelativeReference(req.Ref) {
   302  		s.addMissingStorageProviderID(req.GetRef().GetResourceId(), nil)
   303  		protocol.Protocol = "spaces"
   304  		u.Path = path.Join(u.Path, "spaces", storagespace.FormatResourceID(req.Ref.ResourceId), req.Ref.Path)
   305  	} else {
   306  		// Currently, we only support the simple protocol for GET requests
   307  		// Once we have multiple protocols, this would be moved to the fs layer
   308  		protocol.Protocol = "simple"
   309  		u.Path = path.Join(u.Path, "simple", req.Ref.GetPath())
   310  	}
   311  
   312  	protocol.DownloadEndpoint = u.String()
   313  
   314  	return &provider.InitiateFileDownloadResponse{
   315  		Protocols: []*provider.FileDownloadProtocol{protocol},
   316  		Status:    status.NewOK(ctx),
   317  	}, nil
   318  }
   319  
   320  func validateIfMatch(ifMatch string, info *provider.ResourceInfo) bool {
   321  	return ifMatch == info.GetEtag()
   322  }
   323  
   324  func validateIfUnmodifiedSince(ifUnmodifiedSince *typesv1beta1.Timestamp, info *provider.ResourceInfo) bool {
   325  	switch {
   326  	case ifUnmodifiedSince == nil || info.GetMtime() == nil:
   327  		return true
   328  	case utils.LaterTS(info.GetMtime(), ifUnmodifiedSince) == info.GetMtime():
   329  		return false
   330  	default:
   331  		return true
   332  	}
   333  }
   334  
   335  func (s *Service) InitiateFileUpload(ctx context.Context, req *provider.InitiateFileUploadRequest) (*provider.InitiateFileUploadResponse, error) {
   336  	// TODO(labkode): same considerations as download
   337  	log := appctx.GetLogger(ctx)
   338  	if req.Ref.GetPath() == "/" {
   339  		return &provider.InitiateFileUploadResponse{
   340  			Status: status.NewInternal(ctx, "can't upload to mount path"),
   341  		}, nil
   342  	}
   343  
   344  	// FIXME move the etag check into the InitiateUpload call instead of making a Stat call here
   345  	sRes, err := s.Stat(ctx, &provider.StatRequest{Ref: req.Ref})
   346  	if err != nil {
   347  		return nil, err
   348  	}
   349  	switch sRes.Status.Code {
   350  	case rpc.Code_CODE_OK:
   351  		if req.GetIfNotExist() {
   352  			return &provider.InitiateFileUploadResponse{
   353  				Status: status.NewAlreadyExists(ctx, errors.New("already exists"), "already exists"),
   354  			}, nil
   355  		}
   356  	case rpc.Code_CODE_NOT_FOUND:
   357  		// Just continue with a normal upload
   358  	default:
   359  		return &provider.InitiateFileUploadResponse{
   360  			Status: sRes.Status,
   361  		}, nil
   362  	}
   363  
   364  	metadata := map[string]string{}
   365  	ifMatch := req.GetIfMatch()
   366  	if ifMatch != "" {
   367  		if !validateIfMatch(ifMatch, sRes.GetInfo()) {
   368  			return &provider.InitiateFileUploadResponse{
   369  				Status: status.NewFailedPrecondition(ctx, errors.New("etag mismatch"), "etag mismatch"),
   370  			}, nil
   371  		}
   372  		metadata["if-match"] = ifMatch
   373  	}
   374  	ifUnmodifiedSince := req.GetIfUnmodifiedSince()
   375  	if ifUnmodifiedSince != nil {
   376  		metadata["if-unmodified-since"] = utils.TSToTime(ifUnmodifiedSince).Format(time.RFC3339Nano)
   377  		if !validateIfUnmodifiedSince(ifUnmodifiedSince, sRes.GetInfo()) {
   378  			return &provider.InitiateFileUploadResponse{
   379  				Status: status.NewFailedPrecondition(ctx, errors.New("resource has been modified"), "resource has been modified"),
   380  			}, nil
   381  		}
   382  	}
   383  
   384  	ctx = ctxpkg.ContextSetLockID(ctx, req.LockId)
   385  
   386  	var uploadLength int64
   387  	if req.Opaque != nil && req.Opaque.Map != nil {
   388  		if req.Opaque.Map["Upload-Length"] != nil {
   389  			var err error
   390  			uploadLength, err = strconv.ParseInt(string(req.Opaque.Map["Upload-Length"].Value), 10, 64)
   391  			if err != nil {
   392  				log.Error().Err(err).Msg("error parsing upload length")
   393  				return &provider.InitiateFileUploadResponse{
   394  					Status: status.NewInternal(ctx, "error parsing upload length"),
   395  				}, nil
   396  			}
   397  		}
   398  		// TUS forward Upload-Checksum header as checksum, uses '[type] [hash]' format
   399  		if req.Opaque.Map["Upload-Checksum"] != nil {
   400  			metadata["checksum"] = string(req.Opaque.Map["Upload-Checksum"].Value)
   401  		}
   402  		// ownCloud mtime to set for the uploaded file
   403  		if req.Opaque.Map["X-OC-Mtime"] != nil {
   404  			metadata["mtime"] = string(req.Opaque.Map["X-OC-Mtime"].Value)
   405  		}
   406  	}
   407  
   408  	// pass on the provider it to be persisted with the upload info. that is required to correlate the upload with the proper provider later on
   409  	metadata["providerID"] = s.conf.MountID
   410  	var expirationTimestamp *typesv1beta1.Timestamp
   411  	if s.conf.UploadExpiration > 0 {
   412  		expirationTimestamp = &typesv1beta1.Timestamp{
   413  			Seconds: uint64(time.Now().UTC().Add(time.Duration(s.conf.UploadExpiration) * time.Second).Unix()),
   414  		}
   415  		metadata["expires"] = strconv.Itoa(int(expirationTimestamp.Seconds))
   416  	}
   417  
   418  	uploadIDs, err := s.Storage.InitiateUpload(ctx, req.Ref, uploadLength, metadata)
   419  	if err != nil {
   420  		var st *rpc.Status
   421  		switch err.(type) {
   422  		case errtypes.IsNotFound:
   423  			st = status.NewNotFound(ctx, "path not found when initiating upload")
   424  		case errtypes.IsBadRequest, errtypes.IsChecksumMismatch:
   425  			st = status.NewInvalid(ctx, err.Error())
   426  			// TODO TUS uses a custom ChecksumMismatch 460 http status which is in an unassigned range in
   427  			// https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
   428  			// maybe 409 conflict is good enough
   429  			// someone is proposing `419 Checksum Error`, see https://stackoverflow.com/a/35665694
   430  			// - it is also unassigned
   431  			// - ends in 9 as the 409 conflict
   432  			// - is near the 4xx errors about conditions: 415 Unsupported Media Type, 416 Range Not Satisfiable or 417 Expectation Failed
   433  			// owncloud only expects a 400 Bad request so InvalidArg is good enough for now
   434  			// seealso errtypes.StatusChecksumMismatch
   435  		case errtypes.PermissionDenied:
   436  			st = status.NewPermissionDenied(ctx, err, "permission denied")
   437  		case errtypes.InsufficientStorage:
   438  			st = status.NewInsufficientStorage(ctx, err, "insufficient storage")
   439  		case errtypes.PreconditionFailed:
   440  			st = status.NewFailedPrecondition(ctx, err, "failed precondition")
   441  		case errtypes.Locked:
   442  			st = status.NewLocked(ctx, "locked")
   443  		default:
   444  			st = status.NewInternal(ctx, "error getting upload id: "+err.Error())
   445  		}
   446  		log.Error().
   447  			Err(err).
   448  			Interface("status", st).
   449  			Msg("failed to initiate upload")
   450  		return &provider.InitiateFileUploadResponse{
   451  			Status: st,
   452  		}, nil
   453  	}
   454  
   455  	protocols := make([]*provider.FileUploadProtocol, len(uploadIDs))
   456  	var i int
   457  	for protocol, ID := range uploadIDs {
   458  		u := *s.dataServerURL
   459  		u.Path = path.Join(u.Path, protocol, ID)
   460  		protocols[i] = &provider.FileUploadProtocol{
   461  			Protocol:           protocol,
   462  			UploadEndpoint:     u.String(),
   463  			AvailableChecksums: s.availableXS,
   464  			Expose:             s.conf.ExposeDataServer,
   465  			Expiration:         expirationTimestamp,
   466  		}
   467  		i++
   468  		log.Info().Str("data-server", u.String()).
   469  			Str("fn", req.Ref.GetPath()).
   470  			Str("xs", fmt.Sprintf("%+v", s.conf.AvailableXS)).
   471  			Msg("file upload")
   472  	}
   473  
   474  	res := &provider.InitiateFileUploadResponse{
   475  		Protocols: protocols,
   476  		Status:    status.NewOK(ctx),
   477  	}
   478  	// FIXME make created flag a property on the InitiateFileUploadResponse
   479  	if sRes.Status.Code == rpc.Code_CODE_NOT_FOUND {
   480  		res.Opaque = utils.AppendPlainToOpaque(res.Opaque, "created", "true")
   481  	}
   482  	return res, nil
   483  }
   484  
   485  func (s *Service) GetPath(ctx context.Context, req *provider.GetPathRequest) (*provider.GetPathResponse, error) {
   486  	// TODO(labkode): check that the storage ID is the same as the storage provider id.
   487  	fn, err := s.Storage.GetPathByID(ctx, req.ResourceId)
   488  	if err != nil {
   489  		return &provider.GetPathResponse{
   490  			Status: status.NewStatusFromErrType(ctx, "get path", err),
   491  		}, nil
   492  	}
   493  	res := &provider.GetPathResponse{
   494  		Path:   fn,
   495  		Status: status.NewOK(ctx),
   496  	}
   497  	return res, nil
   498  }
   499  
   500  func (s *Service) GetHome(ctx context.Context, req *provider.GetHomeRequest) (*provider.GetHomeResponse, error) {
   501  	return nil, errtypes.NotSupported("unused, use the gateway to look up the user home")
   502  }
   503  
   504  func (s *Service) CreateHome(ctx context.Context, req *provider.CreateHomeRequest) (*provider.CreateHomeResponse, error) {
   505  	return nil, errtypes.NotSupported("use CreateStorageSpace with type personal")
   506  }
   507  
   508  // CreateStorageSpace creates a storage space
   509  func (s *Service) CreateStorageSpace(ctx context.Context, req *provider.CreateStorageSpaceRequest) (*provider.CreateStorageSpaceResponse, error) {
   510  	resp, err := s.Storage.CreateStorageSpace(ctx, req)
   511  	if err != nil {
   512  		var st *rpc.Status
   513  		switch err.(type) {
   514  		case errtypes.IsNotFound:
   515  			st = status.NewNotFound(ctx, "not found when creating space")
   516  		case errtypes.PermissionDenied:
   517  			st = status.NewPermissionDenied(ctx, err, "permission denied")
   518  		case errtypes.NotSupported:
   519  			// if trying to create a user home fall back to CreateHome
   520  			if u, ok := ctxpkg.ContextGetUser(ctx); ok && req.Type == "personal" && utils.UserEqual(req.GetOwner().Id, u.Id) {
   521  				if err := s.Storage.CreateHome(ctx); err != nil {
   522  					st = status.NewInternal(ctx, "error creating home")
   523  				} else {
   524  					st = status.NewOK(ctx)
   525  					// TODO we cannot return a space, but the gateway currently does not expect one...
   526  				}
   527  			} else {
   528  				st = status.NewUnimplemented(ctx, err, "not implemented")
   529  			}
   530  		case errtypes.AlreadyExists:
   531  			st = status.NewAlreadyExists(ctx, err, "already exists")
   532  		case errtypes.BadRequest:
   533  			st = status.NewInvalid(ctx, err.Error())
   534  		default:
   535  			st = status.NewInternal(ctx, "error creating space")
   536  			appctx.GetLogger(ctx).
   537  				Error().
   538  				Err(err).
   539  				Interface("status", st).
   540  				Interface("request", req).
   541  				Msg("failed to create storage space")
   542  		}
   543  		return &provider.CreateStorageSpaceResponse{
   544  			Status: st,
   545  		}, nil
   546  	}
   547  
   548  	s.addMissingStorageProviderID(resp.GetStorageSpace().GetRoot(), resp.GetStorageSpace().GetId())
   549  	return resp, nil
   550  }
   551  
   552  func (s *Service) ListStorageSpaces(ctx context.Context, req *provider.ListStorageSpacesRequest) (*provider.ListStorageSpacesResponse, error) {
   553  	log := appctx.GetLogger(ctx)
   554  
   555  	// TODO this is just temporary. Update the API to include this flag.
   556  	unrestricted := false
   557  	if req.Opaque != nil {
   558  		if entry, ok := req.Opaque.Map["unrestricted"]; ok {
   559  			unrestricted, _ = strconv.ParseBool(string(entry.Value))
   560  		}
   561  	}
   562  
   563  	spaces, err := s.Storage.ListStorageSpaces(ctx, req.Filters, unrestricted)
   564  	if err != nil {
   565  		var st *rpc.Status
   566  		switch err.(type) {
   567  		case errtypes.IsNotFound:
   568  			st = status.NewNotFound(ctx, "not found when listing spaces")
   569  		case errtypes.PermissionDenied:
   570  			st = status.NewPermissionDenied(ctx, err, "permission denied")
   571  		case errtypes.NotSupported:
   572  			st = status.NewUnimplemented(ctx, err, "not implemented")
   573  		default:
   574  			st = status.NewInternal(ctx, "error listing spaces")
   575  		}
   576  		log.Error().
   577  			Err(err).
   578  			Interface("status", st).
   579  			Interface("filters", req.Filters).
   580  			Msg("failed to list storage spaces")
   581  		return &provider.ListStorageSpacesResponse{
   582  			Status: st,
   583  		}, nil
   584  	}
   585  
   586  	for _, sp := range spaces {
   587  		if sp.Id == nil || sp.Id.OpaqueId == "" {
   588  			log.Error().Str("service", "storageprovider").Str("driver", s.conf.Driver).Interface("space", sp).Msg("space is missing space id and root id")
   589  			continue
   590  		}
   591  
   592  		s.addMissingStorageProviderID(sp.GetRoot(), sp.GetId())
   593  	}
   594  
   595  	return &provider.ListStorageSpacesResponse{
   596  		Status:        status.NewOK(ctx),
   597  		StorageSpaces: spaces,
   598  	}, nil
   599  }
   600  
   601  func (s *Service) UpdateStorageSpace(ctx context.Context, req *provider.UpdateStorageSpaceRequest) (*provider.UpdateStorageSpaceResponse, error) {
   602  	res, err := s.Storage.UpdateStorageSpace(ctx, req)
   603  	if err != nil {
   604  		appctx.GetLogger(ctx).
   605  			Error().
   606  			Err(err).
   607  			Interface("req", req).
   608  			Msg("failed to update storage space")
   609  		return nil, err
   610  	}
   611  	s.addMissingStorageProviderID(res.GetStorageSpace().GetRoot(), res.GetStorageSpace().GetId())
   612  	return res, nil
   613  }
   614  
   615  func (s *Service) DeleteStorageSpace(ctx context.Context, req *provider.DeleteStorageSpaceRequest) (*provider.DeleteStorageSpaceResponse, error) {
   616  	// we need to get the space before so we can return critical information
   617  	// FIXME: why is this string parsing necessary?
   618  	idraw, _ := storagespace.ParseID(req.Id.GetOpaqueId())
   619  	idraw.OpaqueId = idraw.GetSpaceId()
   620  	id := &provider.StorageSpaceId{OpaqueId: storagespace.FormatResourceID(&idraw)}
   621  
   622  	spaces, err := s.Storage.ListStorageSpaces(ctx, []*provider.ListStorageSpacesRequest_Filter{{Type: provider.ListStorageSpacesRequest_Filter_TYPE_ID, Term: &provider.ListStorageSpacesRequest_Filter_Id{Id: id}}}, true)
   623  	if err != nil {
   624  		var st *rpc.Status
   625  		switch err.(type) {
   626  		case errtypes.IsNotFound:
   627  			st = status.NewNotFound(ctx, "space not found")
   628  		case errtypes.PermissionDenied:
   629  			st = status.NewPermissionDenied(ctx, err, "permission denied")
   630  		case errtypes.BadRequest:
   631  			st = status.NewInvalid(ctx, err.Error())
   632  		default:
   633  			st = status.NewInternal(ctx, "error deleting space: "+req.Id.String())
   634  		}
   635  		return &provider.DeleteStorageSpaceResponse{
   636  			Status: st,
   637  		}, nil
   638  	} else if len(spaces) != 1 {
   639  		return &provider.DeleteStorageSpaceResponse{
   640  			Status: status.NewNotFound(ctx, "space not found"),
   641  		}, nil
   642  	}
   643  
   644  	if err := s.Storage.DeleteStorageSpace(ctx, req); err != nil {
   645  		var st *rpc.Status
   646  		switch err.(type) {
   647  		case errtypes.IsNotFound:
   648  			st = status.NewNotFound(ctx, "space not found")
   649  		case errtypes.PermissionDenied:
   650  			st = status.NewPermissionDenied(ctx, err, "permission denied")
   651  		case errtypes.BadRequest:
   652  			st = status.NewInvalid(ctx, err.Error())
   653  		default:
   654  			st = status.NewInternal(ctx, "error deleting space: "+req.Id.String())
   655  		}
   656  		appctx.GetLogger(ctx).
   657  			Error().
   658  			Err(err).
   659  			Interface("status", st).
   660  			Interface("storage_space_id", req.Id).
   661  			Msg("failed to delete storage space")
   662  		return &provider.DeleteStorageSpaceResponse{
   663  			Status: st,
   664  		}, nil
   665  	}
   666  
   667  	// TODO: update cs3api
   668  	o := utils.AppendPlainToOpaque(nil, "spacename", spaces[0].GetName())
   669  	o.Map["grants"] = spaces[0].GetOpaque().GetMap()["grants"]
   670  
   671  	res := &provider.DeleteStorageSpaceResponse{
   672  		Opaque: o,
   673  		Status: status.NewOK(ctx),
   674  	}
   675  	return res, nil
   676  }
   677  
   678  func (s *Service) CreateContainer(ctx context.Context, req *provider.CreateContainerRequest) (*provider.CreateContainerResponse, error) {
   679  	// FIXME these should be part of the CreateContainerRequest object
   680  	if req.Opaque != nil {
   681  		if e, ok := req.Opaque.Map["lockid"]; ok && e.Decoder == "plain" {
   682  			ctx = ctxpkg.ContextSetLockID(ctx, string(e.Value))
   683  		}
   684  	}
   685  
   686  	err := s.Storage.CreateDir(ctx, req.Ref)
   687  
   688  	return &provider.CreateContainerResponse{
   689  		Status: status.NewStatusFromErrType(ctx, "create container", err),
   690  	}, nil
   691  }
   692  
   693  func (s *Service) TouchFile(ctx context.Context, req *provider.TouchFileRequest) (*provider.TouchFileResponse, error) {
   694  	// FIXME these should be part of the TouchFileRequest object
   695  	var mtime string
   696  	if req.Opaque != nil {
   697  		if e, ok := req.Opaque.Map["lockid"]; ok && e.Decoder == "plain" {
   698  			ctx = ctxpkg.ContextSetLockID(ctx, string(e.Value))
   699  		}
   700  		mtime = utils.ReadPlainFromOpaque(req.Opaque, "X-OC-Mtime")
   701  	}
   702  
   703  	err := s.Storage.TouchFile(ctx, req.Ref, utils.ExistsInOpaque(req.Opaque, "markprocessing"), mtime)
   704  
   705  	return &provider.TouchFileResponse{
   706  		Status: status.NewStatusFromErrType(ctx, "touch file", err),
   707  	}, nil
   708  }
   709  
   710  func (s *Service) Delete(ctx context.Context, req *provider.DeleteRequest) (*provider.DeleteResponse, error) {
   711  	if req.Ref.GetPath() == "/" {
   712  		return &provider.DeleteResponse{
   713  			Status: status.NewInternal(ctx, "can't delete mount path"),
   714  		}, nil
   715  	}
   716  
   717  	ctx = ctxpkg.ContextSetLockID(ctx, req.LockId)
   718  
   719  	// check DeleteRequest for any known opaque properties.
   720  	// FIXME these should be part of the DeleteRequest object
   721  	if req.Opaque != nil {
   722  		if _, ok := req.Opaque.Map["deleting_shared_resource"]; ok {
   723  			// it is a binary key; its existence signals true. Although, do not assume.
   724  			ctx = appctx.WithDeletingSharedResource(ctx)
   725  		}
   726  	}
   727  
   728  	md, err := s.Storage.GetMD(ctx, req.Ref, []string{}, []string{"id", "status"})
   729  	if err != nil {
   730  		return &provider.DeleteResponse{
   731  			Status: status.NewStatusFromErrType(ctx, "can't stat resource to delete", err),
   732  		}, nil
   733  	}
   734  
   735  	if utils.ReadPlainFromOpaque(md.GetOpaque(), "status") == "processing" {
   736  		return &provider.DeleteResponse{
   737  			Status: &rpc.Status{
   738  				Code:    rpc.Code_CODE_TOO_EARLY,
   739  				Message: "file is processing",
   740  			},
   741  			Opaque: &typesv1beta1.Opaque{
   742  				Map: map[string]*typesv1beta1.OpaqueEntry{
   743  					"status": {Decoder: "plain", Value: []byte("processing")},
   744  				},
   745  			},
   746  		}, nil
   747  	}
   748  
   749  	err = s.Storage.Delete(ctx, req.Ref)
   750  
   751  	return &provider.DeleteResponse{
   752  		Status: status.NewStatusFromErrType(ctx, "delete", err),
   753  		Opaque: &typesv1beta1.Opaque{
   754  			Map: map[string]*typesv1beta1.OpaqueEntry{
   755  				"opaque_id": {Decoder: "plain", Value: []byte(md.Id.OpaqueId)},
   756  			},
   757  		},
   758  	}, nil
   759  }
   760  
   761  func (s *Service) Move(ctx context.Context, req *provider.MoveRequest) (*provider.MoveResponse, error) {
   762  	ctx = ctxpkg.ContextSetLockID(ctx, req.LockId)
   763  
   764  	err := s.Storage.Move(ctx, req.Source, req.Destination)
   765  
   766  	return &provider.MoveResponse{
   767  		Status: status.NewStatusFromErrType(ctx, "move", err),
   768  	}, nil
   769  }
   770  
   771  func (s *Service) Stat(ctx context.Context, req *provider.StatRequest) (*provider.StatResponse, error) {
   772  	ctx, span := appctx.GetTracerProvider(ctx).Tracer(tracerName).Start(ctx, "stat")
   773  	defer span.End()
   774  
   775  	span.SetAttributes(attribute.KeyValue{
   776  		Key:   "reference",
   777  		Value: attribute.StringValue(req.GetRef().String()),
   778  	})
   779  
   780  	md, err := s.Storage.GetMD(ctx, req.GetRef(), req.GetArbitraryMetadataKeys(), req.GetFieldMask().GetPaths())
   781  	if err != nil {
   782  		return &provider.StatResponse{
   783  			Status: status.NewStatusFromErrType(ctx, "stat", err),
   784  		}, nil
   785  	}
   786  
   787  	s.addMissingStorageProviderID(md.GetId(), nil)
   788  	s.addMissingStorageProviderID(md.GetParentId(), nil)
   789  	s.addMissingStorageProviderID(md.GetSpace().GetRoot(), nil)
   790  
   791  	return &provider.StatResponse{
   792  		Status: status.NewOK(ctx),
   793  		Info:   md,
   794  	}, nil
   795  }
   796  
   797  func (s *Service) ListContainerStream(req *provider.ListContainerStreamRequest, ss provider.ProviderAPI_ListContainerStreamServer) error {
   798  	ctx := ss.Context()
   799  	log := appctx.GetLogger(ctx)
   800  
   801  	mds, err := s.Storage.ListFolder(ctx, req.GetRef(), req.GetArbitraryMetadataKeys(), req.GetFieldMask().GetPaths())
   802  	if err != nil {
   803  		var st *rpc.Status
   804  		switch err.(type) {
   805  		case errtypes.IsNotFound:
   806  			st = status.NewNotFound(ctx, "path not found when listing container")
   807  		case errtypes.PermissionDenied:
   808  			st = status.NewPermissionDenied(ctx, err, "permission denied")
   809  		default:
   810  			st = status.NewInternal(ctx, "error listing container: "+req.Ref.String())
   811  		}
   812  		log.Error().
   813  			Err(err).
   814  			Interface("status", st).
   815  			Interface("reference", req.Ref).
   816  			Msg("failed to list folder (stream)")
   817  		res := &provider.ListContainerStreamResponse{
   818  			Status: st,
   819  		}
   820  		if err := ss.Send(res); err != nil {
   821  			log.Error().Err(err).Msg("ListContainerStream: error sending response")
   822  			return err
   823  		}
   824  		return nil
   825  	}
   826  
   827  	for _, md := range mds {
   828  		s.addMissingStorageProviderID(md.GetId(), nil)
   829  		s.addMissingStorageProviderID(md.GetParentId(), nil)
   830  		s.addMissingStorageProviderID(md.GetSpace().GetRoot(), nil)
   831  		res := &provider.ListContainerStreamResponse{
   832  			Info:   md,
   833  			Status: status.NewOK(ctx),
   834  		}
   835  
   836  		if err := ss.Send(res); err != nil {
   837  			log.Error().Err(err).Msg("ListContainerStream: error sending response")
   838  			return err
   839  		}
   840  	}
   841  	return nil
   842  }
   843  
   844  func (s *Service) ListContainer(ctx context.Context, req *provider.ListContainerRequest) (*provider.ListContainerResponse, error) {
   845  	mds, err := s.Storage.ListFolder(ctx, req.GetRef(), req.GetArbitraryMetadataKeys(), req.GetFieldMask().GetPaths())
   846  	res := &provider.ListContainerResponse{
   847  		Status: status.NewStatusFromErrType(ctx, "list container", err),
   848  		Infos:  mds,
   849  	}
   850  	if err != nil {
   851  		return res, nil
   852  	}
   853  
   854  	for _, i := range res.Infos {
   855  		s.addMissingStorageProviderID(i.GetId(), nil)
   856  		s.addMissingStorageProviderID(i.GetParentId(), nil)
   857  		s.addMissingStorageProviderID(i.GetSpace().GetRoot(), nil)
   858  	}
   859  	return res, nil
   860  }
   861  
   862  func (s *Service) ListFileVersions(ctx context.Context, req *provider.ListFileVersionsRequest) (*provider.ListFileVersionsResponse, error) {
   863  	revs, err := s.Storage.ListRevisions(ctx, req.Ref)
   864  
   865  	sort.Sort(descendingMtime(revs))
   866  
   867  	return &provider.ListFileVersionsResponse{
   868  		Status:   status.NewStatusFromErrType(ctx, "list file versions", err),
   869  		Versions: revs,
   870  	}, nil
   871  }
   872  
   873  func (s *Service) RestoreFileVersion(ctx context.Context, req *provider.RestoreFileVersionRequest) (*provider.RestoreFileVersionResponse, error) {
   874  	ctx = ctxpkg.ContextSetLockID(ctx, req.LockId)
   875  
   876  	err := s.Storage.RestoreRevision(ctx, req.Ref, req.Key)
   877  
   878  	return &provider.RestoreFileVersionResponse{
   879  		Status: status.NewStatusFromErrType(ctx, "restore file version", err),
   880  	}, nil
   881  }
   882  
   883  func (s *Service) ListRecycleStream(req *provider.ListRecycleStreamRequest, ss provider.ProviderAPI_ListRecycleStreamServer) error {
   884  	ctx := ss.Context()
   885  	log := appctx.GetLogger(ctx)
   886  
   887  	// if no slash is present in the key, do not pass a relative path to the storage
   888  	// when a path is passed to the storage, it will list the contents of the directory
   889  	key, relativePath := splitKeyAndPath(req.GetKey())
   890  	items, err := s.Storage.ListRecycle(ctx, req.Ref, key, relativePath)
   891  
   892  	if err != nil {
   893  		var st *rpc.Status
   894  		switch err.(type) {
   895  		case errtypes.IsNotFound:
   896  			st = status.NewNotFound(ctx, "resource not found when listing recycle stream")
   897  		case errtypes.PermissionDenied:
   898  			st = status.NewPermissionDenied(ctx, err, "permission denied")
   899  		default:
   900  			st = status.NewInternal(ctx, "error listing recycle stream")
   901  		}
   902  		log.Error().
   903  			Err(err).
   904  			Interface("status", st).
   905  			Interface("reference", req.Ref).
   906  			Str("key", req.Key).
   907  			Msg("failed to list recycle (stream)")
   908  		res := &provider.ListRecycleStreamResponse{
   909  			Status: st,
   910  		}
   911  		if err := ss.Send(res); err != nil {
   912  			log.Error().Err(err).Msg("ListRecycleStream: error sending response")
   913  			return err
   914  		}
   915  		return nil
   916  	}
   917  
   918  	// TODO(labkode): CRITICAL: fill recycle info with storage provider.
   919  	for _, item := range items {
   920  		s.addMissingStorageProviderID(item.GetRef().GetResourceId(), nil)
   921  		res := &provider.ListRecycleStreamResponse{
   922  			RecycleItem: item,
   923  			Status:      status.NewOK(ctx),
   924  		}
   925  		if err := ss.Send(res); err != nil {
   926  			log.Error().Err(err).Msg("ListRecycleStream: error sending response")
   927  			return err
   928  		}
   929  	}
   930  	return nil
   931  }
   932  
   933  func (s *Service) ListRecycle(ctx context.Context, req *provider.ListRecycleRequest) (*provider.ListRecycleResponse, error) {
   934  	// if no slash is present in the key, do not pass a relative path to the storage
   935  	// when a path is passed to the storage, it will list the contents of the directory
   936  	key, relativePath := splitKeyAndPath(req.GetKey())
   937  	items, err := s.Storage.ListRecycle(ctx, req.Ref, key, relativePath)
   938  	if err != nil {
   939  		var st *rpc.Status
   940  		switch err.(type) {
   941  		case errtypes.IsNotFound:
   942  			st = status.NewNotFound(ctx, "resource not found when listing recycle")
   943  		case errtypes.PermissionDenied:
   944  			st = status.NewPermissionDenied(ctx, err, "permission denied")
   945  		default:
   946  			st = status.NewInternal(ctx, "error listing recycle")
   947  		}
   948  		appctx.GetLogger(ctx).
   949  			Error().
   950  			Err(err).
   951  			Interface("status", st).
   952  			Interface("reference", req.Ref).
   953  			Str("key", req.Key).
   954  			Msg("failed to list recycle")
   955  		return &provider.ListRecycleResponse{
   956  			Status: st,
   957  		}, nil
   958  	}
   959  
   960  	for _, i := range items {
   961  		s.addMissingStorageProviderID(i.GetRef().GetResourceId(), nil)
   962  	}
   963  	res := &provider.ListRecycleResponse{
   964  		Status:       status.NewOK(ctx),
   965  		RecycleItems: items,
   966  	}
   967  	return res, nil
   968  }
   969  
   970  func (s *Service) RestoreRecycleItem(ctx context.Context, req *provider.RestoreRecycleItemRequest) (*provider.RestoreRecycleItemResponse, error) {
   971  	ctx = ctxpkg.ContextSetLockID(ctx, req.LockId)
   972  
   973  	// TODO(labkode): CRITICAL: fill recycle info with storage provider.
   974  	key, relativePath := splitKeyAndPath(req.GetKey())
   975  	err := s.Storage.RestoreRecycleItem(ctx, req.Ref, key, relativePath, req.RestoreRef)
   976  
   977  	res := &provider.RestoreRecycleItemResponse{
   978  		Status: status.NewStatusFromErrType(ctx, "restore recycle item", err),
   979  	}
   980  	return res, nil
   981  }
   982  
   983  func (s *Service) PurgeRecycle(ctx context.Context, req *provider.PurgeRecycleRequest) (*provider.PurgeRecycleResponse, error) {
   984  	// FIXME these should be part of the PurgeRecycleRequest object
   985  	if req.Opaque != nil {
   986  		if e, ok := req.Opaque.Map["lockid"]; ok && e.Decoder == "plain" {
   987  			ctx = ctxpkg.ContextSetLockID(ctx, string(e.Value))
   988  		}
   989  	}
   990  
   991  	// if a key was sent as opaque id purge only that item
   992  	key, relativePath := splitKeyAndPath(req.GetKey())
   993  	if key != "" {
   994  		if err := s.Storage.PurgeRecycleItem(ctx, req.Ref, key, relativePath); err != nil {
   995  			st := status.NewStatusFromErrType(ctx, "error purging recycle item", err)
   996  			appctx.GetLogger(ctx).
   997  				Error().
   998  				Err(err).
   999  				Interface("status", st).
  1000  				Interface("reference", req.Ref).
  1001  				Str("key", req.Key).
  1002  				Msg("failed to purge recycle item")
  1003  			return &provider.PurgeRecycleResponse{
  1004  				Status: st,
  1005  			}, nil
  1006  		}
  1007  	} else if err := s.Storage.EmptyRecycle(ctx, req.Ref); err != nil {
  1008  		// otherwise try emptying the whole recycle bin
  1009  		st := status.NewStatusFromErrType(ctx, "error emptying recycle", err)
  1010  		appctx.GetLogger(ctx).
  1011  			Error().
  1012  			Err(err).
  1013  			Interface("status", st).
  1014  			Interface("reference", req.Ref).
  1015  			Str("key", req.Key).
  1016  			Msg("failed to empty recycle")
  1017  		return &provider.PurgeRecycleResponse{
  1018  			Status: st,
  1019  		}, nil
  1020  	}
  1021  
  1022  	res := &provider.PurgeRecycleResponse{
  1023  		Status: status.NewOK(ctx),
  1024  	}
  1025  	return res, nil
  1026  }
  1027  
  1028  func (s *Service) ListGrants(ctx context.Context, req *provider.ListGrantsRequest) (*provider.ListGrantsResponse, error) {
  1029  	grants, err := s.Storage.ListGrants(ctx, req.Ref)
  1030  	if err != nil {
  1031  		var st *rpc.Status
  1032  		switch err.(type) {
  1033  		case errtypes.IsNotFound:
  1034  			st = status.NewNotFound(ctx, "path not found when listing grants")
  1035  		case errtypes.PermissionDenied:
  1036  			st = status.NewPermissionDenied(ctx, err, "permission denied")
  1037  		default:
  1038  			st = status.NewInternal(ctx, "error listing grants")
  1039  		}
  1040  		appctx.GetLogger(ctx).
  1041  			Error().
  1042  			Err(err).
  1043  			Interface("status", st).
  1044  			Interface("reference", req.Ref).
  1045  			Msg("failed to list grants")
  1046  		return &provider.ListGrantsResponse{
  1047  			Status: st,
  1048  		}, nil
  1049  	}
  1050  
  1051  	res := &provider.ListGrantsResponse{
  1052  		Status: status.NewOK(ctx),
  1053  		Grants: grants,
  1054  	}
  1055  	return res, nil
  1056  }
  1057  
  1058  func (s *Service) DenyGrant(ctx context.Context, req *provider.DenyGrantRequest) (*provider.DenyGrantResponse, error) {
  1059  	// check grantee type is valid
  1060  	if req.Grantee.Type == provider.GranteeType_GRANTEE_TYPE_INVALID {
  1061  		return &provider.DenyGrantResponse{
  1062  			Status: status.NewInvalid(ctx, "grantee type is invalid"),
  1063  		}, nil
  1064  	}
  1065  
  1066  	err := s.Storage.DenyGrant(ctx, req.Ref, req.Grantee)
  1067  	if err != nil {
  1068  		var st *rpc.Status
  1069  		switch err.(type) {
  1070  		case errtypes.NotSupported:
  1071  			// ignore - setting storage grants is optional
  1072  			return &provider.DenyGrantResponse{
  1073  				Status: status.NewOK(ctx),
  1074  			}, nil
  1075  		case errtypes.IsNotFound:
  1076  			st = status.NewNotFound(ctx, "path not found when setting grants")
  1077  		case errtypes.PermissionDenied:
  1078  			st = status.NewPermissionDenied(ctx, err, "permission denied")
  1079  		default:
  1080  			st = status.NewInternal(ctx, "error setting grants")
  1081  		}
  1082  		appctx.GetLogger(ctx).
  1083  			Error().
  1084  			Err(err).
  1085  			Interface("status", st).
  1086  			Interface("reference", req.Ref).
  1087  			Msg("failed to deny grant")
  1088  		return &provider.DenyGrantResponse{
  1089  			Status: st,
  1090  		}, nil
  1091  	}
  1092  
  1093  	res := &provider.DenyGrantResponse{
  1094  		Status: status.NewOK(ctx),
  1095  	}
  1096  	return res, nil
  1097  }
  1098  
  1099  type spaceTypeKey struct{}
  1100  
  1101  func WithSpaceType(ctx context.Context, spaceType string) context.Context {
  1102  	return context.WithValue(ctx, spaceTypeKey{}, spaceType)
  1103  }
  1104  func SpaceTypeFromContext(ctx context.Context) (string, bool) {
  1105  	spaceType, ok := ctx.Value(spaceTypeKey{}).(string)
  1106  	return spaceType, ok
  1107  }
  1108  
  1109  func (s *Service) AddGrant(ctx context.Context, req *provider.AddGrantRequest) (*provider.AddGrantResponse, error) {
  1110  	ctx = ctxpkg.ContextSetLockID(ctx, req.LockId)
  1111  
  1112  	// TODO: update CS3 APIs
  1113  	// FIXME these should be part of the AddGrantRequest object
  1114  	// https://github.com/owncloud/ocis/issues/4312
  1115  	if utils.ExistsInOpaque(req.Opaque, "spacegrant") {
  1116  		ctx = WithSpaceType(ctx, utils.ReadPlainFromOpaque(req.Opaque, "spacetype"))
  1117  	}
  1118  
  1119  	// check grantee type is valid
  1120  	if req.Grant.Grantee.Type == provider.GranteeType_GRANTEE_TYPE_INVALID {
  1121  		return &provider.AddGrantResponse{
  1122  			Status: status.NewInvalid(ctx, "grantee type is invalid"),
  1123  		}, nil
  1124  	}
  1125  
  1126  	err := s.Storage.AddGrant(ctx, req.Ref, req.Grant)
  1127  
  1128  	return &provider.AddGrantResponse{
  1129  		Status: status.NewStatusFromErrType(ctx, "add grant", err),
  1130  	}, nil
  1131  }
  1132  
  1133  func (s *Service) UpdateGrant(ctx context.Context, req *provider.UpdateGrantRequest) (*provider.UpdateGrantResponse, error) {
  1134  	// FIXME these should be part of the UpdateGrantRequest object
  1135  	if req.Opaque != nil {
  1136  		if e, ok := req.Opaque.Map["lockid"]; ok && e.Decoder == "plain" {
  1137  			ctx = ctxpkg.ContextSetLockID(ctx, string(e.Value))
  1138  		}
  1139  	}
  1140  
  1141  	// TODO: update CS3 APIs
  1142  	// FIXME these should be part of the AddGrantRequest object
  1143  	// https://github.com/owncloud/ocis/issues/4312
  1144  	if utils.ExistsInOpaque(req.Opaque, "spacegrant") {
  1145  		ctx = WithSpaceType(ctx, utils.ReadPlainFromOpaque(req.Opaque, "spacetype"))
  1146  	}
  1147  
  1148  	// check grantee type is valid
  1149  	if req.Grant.Grantee.Type == provider.GranteeType_GRANTEE_TYPE_INVALID {
  1150  		return &provider.UpdateGrantResponse{
  1151  			Status: status.NewInvalid(ctx, "grantee type is invalid"),
  1152  		}, nil
  1153  	}
  1154  
  1155  	err := s.Storage.UpdateGrant(ctx, req.Ref, req.Grant)
  1156  
  1157  	return &provider.UpdateGrantResponse{
  1158  		Status: status.NewStatusFromErrType(ctx, "update grant", err),
  1159  	}, nil
  1160  }
  1161  
  1162  func (s *Service) RemoveGrant(ctx context.Context, req *provider.RemoveGrantRequest) (*provider.RemoveGrantResponse, error) {
  1163  	ctx = ctxpkg.ContextSetLockID(ctx, req.LockId)
  1164  
  1165  	// check targetType is valid
  1166  	if req.Grant.Grantee.Type == provider.GranteeType_GRANTEE_TYPE_INVALID {
  1167  		return &provider.RemoveGrantResponse{
  1168  			Status: status.NewInvalid(ctx, "grantee type is invalid"),
  1169  		}, nil
  1170  	}
  1171  
  1172  	// TODO: update CS3 APIs
  1173  	// FIXME these should be part of the RemoveGrantRequest object
  1174  	// https://github.com/owncloud/ocis/issues/4312
  1175  	if utils.ExistsInOpaque(req.Opaque, "spacegrant") {
  1176  		ctx = WithSpaceType(ctx, "")
  1177  	}
  1178  
  1179  	err := s.Storage.RemoveGrant(ctx, req.Ref, req.Grant)
  1180  
  1181  	return &provider.RemoveGrantResponse{
  1182  		Status: status.NewStatusFromErrType(ctx, "remove grant", err),
  1183  	}, nil
  1184  }
  1185  
  1186  func (s *Service) CreateReference(ctx context.Context, req *provider.CreateReferenceRequest) (*provider.CreateReferenceResponse, error) {
  1187  	log := appctx.GetLogger(ctx)
  1188  
  1189  	// parse uri is valid
  1190  	u, err := url.Parse(req.TargetUri)
  1191  	if err != nil {
  1192  		log.Error().Err(err).Msg("invalid target uri")
  1193  		return &provider.CreateReferenceResponse{
  1194  			Status: status.NewInvalid(ctx, "target uri is invalid: "+err.Error()),
  1195  		}, nil
  1196  	}
  1197  
  1198  	if err := s.Storage.CreateReference(ctx, req.Ref.GetPath(), u); err != nil {
  1199  		var st *rpc.Status
  1200  		switch err.(type) {
  1201  		case errtypes.IsNotFound:
  1202  			st = status.NewNotFound(ctx, "path not found when creating reference")
  1203  		case errtypes.PermissionDenied:
  1204  			st = status.NewPermissionDenied(ctx, err, "permission denied")
  1205  		default:
  1206  			st = status.NewInternal(ctx, "error creating reference")
  1207  		}
  1208  		log.Error().
  1209  			Err(err).
  1210  			Interface("status", st).
  1211  			Interface("reference", req.Ref).
  1212  			Msg("failed to create reference")
  1213  		return &provider.CreateReferenceResponse{
  1214  			Status: st,
  1215  		}, nil
  1216  	}
  1217  
  1218  	return &provider.CreateReferenceResponse{
  1219  		Status: status.NewOK(ctx),
  1220  	}, nil
  1221  }
  1222  
  1223  func (s *Service) CreateSymlink(ctx context.Context, req *provider.CreateSymlinkRequest) (*provider.CreateSymlinkResponse, error) {
  1224  	return &provider.CreateSymlinkResponse{
  1225  		Status: status.NewUnimplemented(ctx, errtypes.NotSupported("CreateSymlink not implemented"), "CreateSymlink not implemented"),
  1226  	}, nil
  1227  }
  1228  
  1229  func (s *Service) GetQuota(ctx context.Context, req *provider.GetQuotaRequest) (*provider.GetQuotaResponse, error) {
  1230  	total, used, remaining, err := s.Storage.GetQuota(ctx, req.Ref)
  1231  	if err != nil {
  1232  		var st *rpc.Status
  1233  		switch err.(type) {
  1234  		case errtypes.IsNotFound:
  1235  			st = status.NewNotFound(ctx, "path not found when getting quota")
  1236  		case errtypes.PermissionDenied:
  1237  			st = status.NewPermissionDenied(ctx, err, "permission denied")
  1238  		default:
  1239  			st = status.NewInternal(ctx, "error getting quota")
  1240  		}
  1241  		appctx.GetLogger(ctx).
  1242  			Error().
  1243  			Err(err).
  1244  			Interface("status", st).
  1245  			Interface("reference", req.Ref).
  1246  			Msg("failed to get quota")
  1247  		return &provider.GetQuotaResponse{
  1248  			Status: st,
  1249  		}, nil
  1250  	}
  1251  
  1252  	res := &provider.GetQuotaResponse{
  1253  		Opaque: &typesv1beta1.Opaque{
  1254  			Map: map[string]*typesv1beta1.OpaqueEntry{
  1255  				"remaining": {
  1256  					Decoder: "plain",
  1257  					Value:   []byte(strconv.FormatUint(remaining, 10)),
  1258  				},
  1259  			},
  1260  		},
  1261  		Status:     status.NewOK(ctx),
  1262  		TotalBytes: total,
  1263  		UsedBytes:  used,
  1264  	}
  1265  	return res, nil
  1266  }
  1267  
  1268  func (s *Service) addMissingStorageProviderID(resourceID *provider.ResourceId, spaceID *provider.StorageSpaceId) {
  1269  	// The storage driver might set the mount ID by itself, in which case skip this step
  1270  	if resourceID != nil && resourceID.GetStorageId() == "" {
  1271  		resourceID.StorageId = s.conf.MountID
  1272  		if spaceID != nil {
  1273  			rid, _ := storagespace.ParseID(spaceID.GetOpaqueId())
  1274  			rid.StorageId = s.conf.MountID
  1275  			spaceID.OpaqueId, _ = storagespace.FormatReference(&provider.Reference{ResourceId: &rid})
  1276  		}
  1277  	}
  1278  }
  1279  
  1280  func getFS(c *config, log *zerolog.Logger) (storage.FS, error) {
  1281  	evstream, err := estreamFromConfig(c.Events)
  1282  	if err != nil {
  1283  		return nil, err
  1284  	}
  1285  
  1286  	if f, ok := registry.NewFuncs[c.Driver]; ok {
  1287  		driverConf := c.Drivers[c.Driver]
  1288  		driverConf["mount_id"] = c.MountID // pass the mount id to the driver
  1289  
  1290  		return f(driverConf, evstream, log)
  1291  	}
  1292  
  1293  	return nil, errtypes.NotFound("driver not found: " + c.Driver)
  1294  }
  1295  
  1296  type descendingMtime []*provider.FileVersion
  1297  
  1298  func (v descendingMtime) Len() int {
  1299  	return len(v)
  1300  }
  1301  
  1302  func (v descendingMtime) Less(i, j int) bool {
  1303  	return v[i].Mtime >= v[j].Mtime
  1304  }
  1305  
  1306  func (v descendingMtime) Swap(i, j int) {
  1307  	v[i], v[j] = v[j], v[i]
  1308  }
  1309  
  1310  func estreamFromConfig(c eventconfig) (events.Stream, error) {
  1311  	if c.Endpoint == "" {
  1312  		return nil, nil
  1313  	}
  1314  
  1315  	return stream.NatsFromConfig("storageprovider", false, stream.NatsConfig(c))
  1316  }
  1317  
  1318  func canLockPublicShare(ctx context.Context) bool {
  1319  	u := ctxpkg.ContextMustGetUser(ctx)
  1320  	psr := utils.ReadPlainFromOpaque(u.Opaque, "public-share-role")
  1321  	return psr == "" || psr == conversions.RoleEditor
  1322  }
  1323  
  1324  // splitKeyAndPath splits a key into a root and a relative path
  1325  func splitKeyAndPath(key string) (string, string) {
  1326  	root, relativePath := router.ShiftPath(key)
  1327  	if relativePath == "/" && !strings.HasSuffix(key, "/") {
  1328  		relativePath = ""
  1329  	}
  1330  	return root, relativePath
  1331  }