forked from TrueCloudLab/distribution
9c88801a12
Back in the before time, the best practices surrounding usage of Context weren't quite worked out. We defined our own type to make usage easier. As this packaged was used elsewhere, it make it more and more challenging to integrate with the forked `Context` type. Now that it is available in the standard library, we can just use that one directly. To make usage more consistent, we now use `dcontext` when referring to the distribution context package. Signed-off-by: Stephen J Day <stephen.day@docker.com>
59 lines
1.7 KiB
Go
59 lines
1.7 KiB
Go
package storage
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"sort"
|
|
|
|
storageDriver "github.com/docker/distribution/registry/storage/driver"
|
|
)
|
|
|
|
// ErrSkipDir is used as a return value from onFileFunc to indicate that
|
|
// the directory named in the call is to be skipped. It is not returned
|
|
// as an error by any function.
|
|
var ErrSkipDir = errors.New("skip this directory")
|
|
|
|
// WalkFn is called once per file by Walk
|
|
// If the returned error is ErrSkipDir and fileInfo refers
|
|
// to a directory, the directory will not be entered and Walk
|
|
// will continue the traversal. Otherwise Walk will return
|
|
type WalkFn func(fileInfo storageDriver.FileInfo) error
|
|
|
|
// Walk traverses a filesystem defined within driver, starting
|
|
// from the given path, calling f on each file
|
|
func Walk(ctx context.Context, driver storageDriver.StorageDriver, from string, f WalkFn) error {
|
|
children, err := driver.List(ctx, from)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
sort.Stable(sort.StringSlice(children))
|
|
for _, child := range children {
|
|
// TODO(stevvooe): Calling driver.Stat for every entry is quite
|
|
// expensive when running against backends with a slow Stat
|
|
// implementation, such as s3. This is very likely a serious
|
|
// performance bottleneck.
|
|
fileInfo, err := driver.Stat(ctx, child)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = f(fileInfo)
|
|
skipDir := (err == ErrSkipDir)
|
|
if err != nil && !skipDir {
|
|
return err
|
|
}
|
|
|
|
if fileInfo.IsDir() && !skipDir {
|
|
if err := Walk(ctx, driver, child, f); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// pushError formats an error type given a path and an error
|
|
// and pushes it to a slice of errors
|
|
func pushError(errors []error, path string, err error) []error {
|
|
return append(errors, fmt.Errorf("%s: %s", path, err))
|
|
}
|