github.com/cs3org/reva/v2@v2.27.7/internal/http/services/owncloud/ocdav/spacelookup/spacelookup.go (about)

     1  // Copyright 2018-2022 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 spacelookup
    20  
    21  import (
    22  	"context"
    23  	"fmt"
    24  	"strconv"
    25  	"strings"
    26  
    27  	gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
    28  	rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
    29  	storageProvider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
    30  	typesv1beta1 "github.com/cs3org/go-cs3apis/cs3/types/v1beta1"
    31  	"github.com/cs3org/reva/v2/pkg/rgrpc/status"
    32  	"github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool"
    33  	"github.com/cs3org/reva/v2/pkg/storagespace"
    34  	"github.com/cs3org/reva/v2/pkg/utils"
    35  	"google.golang.org/protobuf/types/known/fieldmaskpb"
    36  )
    37  
    38  // LookupReferenceForPath returns:
    39  // a reference with root and relative path
    40  // the status and error for the lookup
    41  func LookupReferenceForPath(ctx context.Context, selector pool.Selectable[gateway.GatewayAPIClient], path string) (*storageProvider.Reference, *rpc.Status, error) {
    42  	space, cs3Status, err := LookUpStorageSpaceForPath(ctx, selector, path)
    43  	if err != nil || cs3Status.Code != rpc.Code_CODE_OK {
    44  		return nil, cs3Status, err
    45  	}
    46  	spacePath := string(space.Opaque.Map["path"].Value) // FIXME error checks
    47  	return &storageProvider.Reference{
    48  		ResourceId: space.Root,
    49  		Path:       utils.MakeRelativePath(strings.TrimPrefix(path, spacePath)),
    50  	}, cs3Status, nil
    51  }
    52  
    53  // LookUpStorageSpaceForPath returns:
    54  // the storage spaces responsible for a path
    55  // the status and error for the lookup
    56  func LookUpStorageSpaceForPath(ctx context.Context, selector pool.Selectable[gateway.GatewayAPIClient], path string) (*storageProvider.StorageSpace, *rpc.Status, error) {
    57  	// TODO add filter to only fetch spaces changed in the last 30 sec?
    58  	// TODO cache space information, invalidate after ... 5min? so we do not need to fetch all spaces?
    59  	// TODO use ListContainerStream to listen for changes
    60  	// retrieve a specific storage space
    61  	lSSReq := &storageProvider.ListStorageSpacesRequest{
    62  		Opaque: &typesv1beta1.Opaque{
    63  			Map: map[string]*typesv1beta1.OpaqueEntry{
    64  				"path": {
    65  					Decoder: "plain",
    66  					Value:   []byte(path),
    67  				},
    68  				"unique": {
    69  					Decoder: "plain",
    70  					Value:   []byte(strconv.FormatBool(true)),
    71  				},
    72  			},
    73  		},
    74  	}
    75  
    76  	client, err := selector.Next()
    77  	if err != nil {
    78  		return nil, status.NewInternal(ctx, "could not select next client"), err
    79  	}
    80  
    81  	lSSRes, err := client.ListStorageSpaces(ctx, lSSReq)
    82  	if err != nil || lSSRes.Status.Code != rpc.Code_CODE_OK {
    83  		status := status.NewStatusFromErrType(ctx, "failed to lookup storage spaces", err)
    84  		if lSSRes != nil {
    85  			status = lSSRes.Status
    86  		}
    87  		return nil, status, err
    88  	}
    89  	switch len(lSSRes.StorageSpaces) {
    90  	case 0:
    91  		return nil, status.NewNotFound(ctx, "no space found"), nil
    92  	case 1:
    93  		return lSSRes.StorageSpaces[0], lSSRes.Status, nil
    94  	}
    95  
    96  	return nil, status.NewInternal(ctx, "too many spaces returned"), nil
    97  }
    98  
    99  // LookUpStorageSpacesForPathWithChildren returns:
   100  // the list of storage spaces responsible for a path
   101  // the status and error for the lookup
   102  func LookUpStorageSpacesForPathWithChildren(ctx context.Context, client gateway.GatewayAPIClient, path string) ([]*storageProvider.StorageSpace, *rpc.Status, error) {
   103  	// TODO add filter to only fetch spaces changed in the last 30 sec?
   104  	// TODO cache space information, invalidate after ... 5min? so we do not need to fetch all spaces?
   105  	// TODO use ListContainerStream to listen for changes
   106  	// retrieve a specific storage space
   107  	lSSReq := &storageProvider.ListStorageSpacesRequest{
   108  		// get all fields, including root_info
   109  		FieldMask: &fieldmaskpb.FieldMask{Paths: []string{"*"}},
   110  	}
   111  	// list all providers at or below the given path
   112  	lSSReq.Opaque = utils.AppendPlainToOpaque(lSSReq.Opaque, "path", path)
   113  	// we want to get all metadata? really? when looking up the space roots we actually only want etag, mtime and type so we can construct a child ...
   114  	lSSReq.Opaque = utils.AppendPlainToOpaque(lSSReq.Opaque, "metadata", "*")
   115  
   116  	lSSRes, err := client.ListStorageSpaces(ctx, lSSReq)
   117  	if err != nil {
   118  		return nil, nil, err
   119  	}
   120  	if lSSRes.Status.GetCode() != rpc.Code_CODE_OK {
   121  		return nil, lSSRes.Status, err
   122  	}
   123  
   124  	return lSSRes.StorageSpaces, lSSRes.Status, nil
   125  }
   126  
   127  // LookUpStorageSpaceByID find a space by ID
   128  func LookUpStorageSpaceByID(ctx context.Context, client gateway.GatewayAPIClient, spaceID string) (*storageProvider.StorageSpace, *rpc.Status, error) {
   129  	// retrieve a specific storage space
   130  	lSSReq := &storageProvider.ListStorageSpacesRequest{
   131  		Opaque: &typesv1beta1.Opaque{},
   132  		Filters: []*storageProvider.ListStorageSpacesRequest_Filter{
   133  			{
   134  				Type: storageProvider.ListStorageSpacesRequest_Filter_TYPE_ID,
   135  				Term: &storageProvider.ListStorageSpacesRequest_Filter_Id{
   136  					Id: &storageProvider.StorageSpaceId{
   137  						OpaqueId: spaceID,
   138  					},
   139  				},
   140  			},
   141  		},
   142  	}
   143  
   144  	lSSRes, err := client.ListStorageSpaces(ctx, lSSReq)
   145  	if err != nil || lSSRes.Status.Code != rpc.Code_CODE_OK {
   146  		return nil, lSSRes.Status, err
   147  	}
   148  
   149  	switch len(lSSRes.StorageSpaces) {
   150  	case 0:
   151  		return nil, &rpc.Status{Code: rpc.Code_CODE_NOT_FOUND}, nil // since the caller only expects a single space return not found status
   152  	case 1:
   153  		return lSSRes.StorageSpaces[0], lSSRes.Status, nil
   154  	default:
   155  		return nil, nil, fmt.Errorf("unexpected number of spaces %d", len(lSSRes.StorageSpaces))
   156  	}
   157  }
   158  
   159  // MakeStorageSpaceReference find a space by id and returns a relative reference
   160  func MakeStorageSpaceReference(spaceID string, relativePath string) (storageProvider.Reference, error) {
   161  	resourceID, err := storagespace.ParseID(spaceID)
   162  	if err != nil {
   163  		return storageProvider.Reference{}, err
   164  	}
   165  	// be tolerant about missing sharesstorageprovider id
   166  	if resourceID.StorageId == "" && resourceID.SpaceId == utils.ShareStorageSpaceID {
   167  		resourceID.StorageId = utils.ShareStorageProviderID
   168  	}
   169  	return storageProvider.Reference{
   170  		ResourceId: &resourceID,
   171  		Path:       utils.MakeRelativePath(relativePath),
   172  	}, nil
   173  }
   174  
   175  // MakeRelativeReference returns a relative reference for the given space and path
   176  func MakeRelativeReference(space *storageProvider.StorageSpace, relativePath string, spacesDavRequest bool) *storageProvider.Reference {
   177  	if space.Opaque == nil || space.Opaque.Map == nil || space.Opaque.Map["path"] == nil || space.Opaque.Map["path"].Decoder != "plain" {
   178  		return nil // not mounted
   179  	}
   180  	spacePath := string(space.Opaque.Map["path"].Value)
   181  	relativeSpacePath := "."
   182  	if strings.HasPrefix(relativePath, spacePath) {
   183  		relativeSpacePath = utils.MakeRelativePath(strings.TrimPrefix(relativePath, spacePath))
   184  	} else if spacesDavRequest {
   185  		relativeSpacePath = utils.MakeRelativePath(relativePath)
   186  	}
   187  	return &storageProvider.Reference{
   188  		ResourceId: space.Root,
   189  		Path:       relativeSpacePath,
   190  	}
   191  }