github.com/mika/distribution@v2.2.2-0.20160108133430-a75790e3d8e0+incompatible/registry/storage/walk.go (about) 1 package storage 2 3 import ( 4 "errors" 5 "fmt" 6 "sort" 7 8 "github.com/docker/distribution/context" 9 storageDriver "github.com/docker/distribution/registry/storage/driver" 10 ) 11 12 // ErrSkipDir is used as a return value from onFileFunc to indicate that 13 // the directory named in the call is to be skipped. It is not returned 14 // as an error by any function. 15 var ErrSkipDir = errors.New("skip this directory") 16 17 // WalkFn is called once per file by Walk 18 // If the returned error is ErrSkipDir and fileInfo refers 19 // to a directory, the directory will not be entered and Walk 20 // will continue the traversal. Otherwise Walk will return 21 type WalkFn func(fileInfo storageDriver.FileInfo) error 22 23 // Walk traverses a filesystem defined within driver, starting 24 // from the given path, calling f on each file 25 func Walk(ctx context.Context, driver storageDriver.StorageDriver, from string, f WalkFn) error { 26 children, err := driver.List(ctx, from) 27 if err != nil { 28 return err 29 } 30 sort.Stable(sort.StringSlice(children)) 31 for _, child := range children { 32 // TODO(stevvooe): Calling driver.Stat for every entry is quite 33 // expensive when running against backends with a slow Stat 34 // implementation, such as s3. This is very likely a serious 35 // performance bottleneck. 36 fileInfo, err := driver.Stat(ctx, child) 37 if err != nil { 38 return err 39 } 40 err = f(fileInfo) 41 skipDir := (err == ErrSkipDir) 42 if err != nil && !skipDir { 43 return err 44 } 45 46 if fileInfo.IsDir() && !skipDir { 47 if err := Walk(ctx, driver, child, f); err != nil { 48 return err 49 } 50 } 51 } 52 return nil 53 } 54 55 // pushError formats an error type given a path and an error 56 // and pushes it to a slice of errors 57 func pushError(errors []error, path string, err error) []error { 58 return append(errors, fmt.Errorf("%s: %s", path, err)) 59 }