github.com/cs3org/reva/v2@v2.27.7/pkg/storage/utils/walker/walker.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 walker 20 21 import ( 22 "context" 23 "path/filepath" 24 25 provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" 26 27 gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1" 28 rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1" 29 "github.com/cs3org/reva/v2/pkg/errtypes" 30 "github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool" 31 ) 32 33 // WalkFunc is the type of function called by Walk to visit each file or directory 34 // 35 // Each time the Walk function meet a file/folder path is set to the full path of this. 36 // The err argument reports an error related to the path, and the function can decide the action to 37 // do with this. 38 // 39 // The error result returned by the function controls how Walk continues. If the function returns the special value SkipDir, Walk skips the current directory. 40 // Otherwise, if the function returns a non-nil error, Walk stops entirely and returns that error. 41 type WalkFunc func(wd string, info *provider.ResourceInfo, err error) error 42 43 // Walker is an interface implemented by objects that are able to walk from a dir rooted into the passed path 44 type Walker interface { 45 // Walk walks the file tree rooted at root, calling fn for each file or folder in the tree, including the root. 46 Walk(ctx context.Context, root *provider.ResourceId, fn WalkFunc) error 47 } 48 49 type revaWalker struct { 50 gatewaySelector pool.Selectable[gateway.GatewayAPIClient] 51 } 52 53 // NewWalker creates a Walker object that uses the reva gateway 54 func NewWalker(gatewaySelector pool.Selectable[gateway.GatewayAPIClient]) Walker { 55 return &revaWalker{gatewaySelector: gatewaySelector} 56 } 57 58 // Walk walks the file tree rooted at root, calling fn for each file or folder in the tree, including the root. 59 func (r *revaWalker) Walk(ctx context.Context, root *provider.ResourceId, fn WalkFunc) error { 60 info, err := r.stat(ctx, root) 61 62 if err != nil { 63 return fn("", nil, err) 64 } 65 66 err = r.walkRecursively(ctx, "", info, fn) 67 68 if err == filepath.SkipDir { 69 return nil 70 } 71 72 return err 73 } 74 75 func (r *revaWalker) walkRecursively(ctx context.Context, wd string, info *provider.ResourceInfo, fn WalkFunc) error { 76 77 if info.Type != provider.ResourceType_RESOURCE_TYPE_CONTAINER { 78 return fn(wd, info, nil) 79 } 80 81 list, err := r.readDir(ctx, info.Id) 82 errFn := fn(wd, info, err) 83 84 if err != nil || errFn != nil { 85 return errFn 86 } 87 88 for _, file := range list { 89 err = r.walkRecursively(ctx, filepath.Join(wd, info.Path), file, fn) 90 if err != nil && (file.Type != provider.ResourceType_RESOURCE_TYPE_CONTAINER || err != filepath.SkipDir) { 91 return err 92 } 93 } 94 95 return nil 96 } 97 98 func (r *revaWalker) readDir(ctx context.Context, id *provider.ResourceId) ([]*provider.ResourceInfo, error) { 99 gatewayClient, err := r.gatewaySelector.Next() 100 if err != nil { 101 return nil, err 102 } 103 resp, err := gatewayClient.ListContainer(ctx, &provider.ListContainerRequest{Ref: &provider.Reference{ResourceId: id, Path: "."}}) 104 105 switch { 106 case err != nil: 107 return nil, err 108 case resp.Status.Code != rpc.Code_CODE_OK: 109 return nil, errtypes.NewErrtypeFromStatus(resp.Status) 110 } 111 112 return resp.Infos, nil 113 } 114 115 func (r *revaWalker) stat(ctx context.Context, id *provider.ResourceId) (*provider.ResourceInfo, error) { 116 gatewayClient, err := r.gatewaySelector.Next() 117 if err != nil { 118 return nil, err 119 } 120 resp, err := gatewayClient.Stat(ctx, &provider.StatRequest{Ref: &provider.Reference{ResourceId: id, Path: "."}}) 121 122 switch { 123 case err != nil: 124 return nil, err 125 case resp.Status.Code != rpc.Code_CODE_OK: 126 return nil, errtypes.NewErrtypeFromStatus(resp.Status) 127 } 128 129 return resp.Info, nil 130 }