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 }