diff --git a/image/config.go b/image/config.go index 71d98a7..4571199 100644 --- a/image/config.go +++ b/image/config.go @@ -20,7 +20,6 @@ import ( "fmt" "io" "io/ioutil" - "os" "path/filepath" "strconv" "strings" @@ -35,10 +34,7 @@ func findConfig(w walker, d *v1.Descriptor) (*v1.Image, error) { var c v1.Image cpath := filepath.Join("blobs", string(d.Digest.Algorithm()), d.Digest.Hex()) - switch err := w.walk(func(path string, info os.FileInfo, r io.Reader) error { - if info.IsDir() || filepath.Clean(path) != cpath { - return nil - } + switch err := w.find(cpath, func(path string, r io.Reader) error { buf, err := ioutil.ReadAll(r) if err != nil { return errors.Wrapf(err, "%s: error reading config", path) diff --git a/image/descriptor.go b/image/descriptor.go index a62f6fe..5bf19ac 100644 --- a/image/descriptor.go +++ b/image/descriptor.go @@ -53,12 +53,9 @@ func listReferences(w walker) ([]v1.Descriptor, error) { func findDescriptor(w walker, names []string) ([]v1.Descriptor, error) { var descs []v1.Descriptor var index v1.Index + dpath := "index.json" - if err := w.walk(func(path string, info os.FileInfo, r io.Reader) error { - if info.IsDir() || filepath.Clean(path) != indexPath { - return nil - } - + if err := w.find(dpath, func(path string, r io.Reader) error { if err := json.NewDecoder(r).Decode(&index); err != nil { return err } diff --git a/image/manifest.go b/image/manifest.go index 833be7b..83bce0f 100644 --- a/image/manifest.go +++ b/image/manifest.go @@ -39,11 +39,7 @@ func findManifest(w walker, d *v1.Descriptor) (*v1.Manifest, error) { var m v1.Manifest mpath := filepath.Join("blobs", string(d.Digest.Algorithm()), d.Digest.Hex()) - switch err := w.walk(func(path string, info os.FileInfo, r io.Reader) error { - if info.IsDir() || filepath.Clean(path) != mpath { - return nil - } - + switch err := w.find(mpath, func(path string, r io.Reader) error { buf, err := ioutil.ReadAll(r) if err != nil { return errors.Wrapf(err, "%s: error reading manifest", path) @@ -108,18 +104,10 @@ func unpackManifest(m *v1.Manifest, w walker, dest string) (retErr error) { } }() for _, d := range m.Layers { - switch err := w.walk(func(path string, info os.FileInfo, r io.Reader) error { - if info.IsDir() { - return nil - } - - dd, err := filepath.Rel(filepath.Join("blobs", string(d.Digest.Algorithm())), filepath.Clean(path)) - if err != nil || d.Digest.Hex() != dd { - return nil - } - + lpath := filepath.Join("blobs", string(d.Digest.Algorithm()), d.Digest.Hex()) + switch err := w.find(lpath, func(path string, r io.Reader) error { if err := unpackLayer(d.MediaType, path, dest, r); err != nil { - return errors.Wrap(err, "error unpack: extracting layer") + return errors.Wrap(err, "unpack: error extracting layer") } return errEOW diff --git a/image/walker.go b/image/walker.go index 04a77f0..88b01df 100644 --- a/image/walker.go +++ b/image/walker.go @@ -34,6 +34,8 @@ var ( // walkFunc is a function type that gets called for each file or directory visited by the Walker. type walkFunc func(path string, _ os.FileInfo, _ io.Reader) error +type findFunc func(path string, r io.Reader) error + // walker is the interface that defines how to access a given archival format type walker interface { @@ -43,6 +45,9 @@ type walker interface { // get will copy an arbitrary blob, defined by desc, in to dst. returns // the number of bytes copied on success. get(desc v1.Descriptor, dst io.Writer) (int64, error) + + // find calls findFunc for handling content of path + find(path string, ff findFunc) error } // tarWalker exposes access to image layouts in a tar file. @@ -120,6 +125,34 @@ func (w *tarWalker) get(desc v1.Descriptor, dst io.Writer) (int64, error) { return bytes, nil } +func (w *tarWalker) find(path string, ff findFunc) error { + done := false + + f := func(relpath string, info os.FileInfo, rdr io.Reader) error { + var err error + if done { + return nil + } + + if filepath.Clean(relpath) == path && !info.IsDir() { + if err = ff(relpath, rdr); err != nil { + return err + } + done = true + } + return nil + } + + if err := w.walk(f); err != nil { + return errors.Wrapf(err, "find failed: unable to walk") + } + if !done { + return os.ErrNotExist + } + + return nil +} + type eofReader struct{} func (eofReader) Read(_ []byte) (int, error) { @@ -188,6 +221,27 @@ func (w *pathWalker) get(desc v1.Descriptor, dst io.Writer) (int64, error) { return nbytes, nil } +func (w *pathWalker) find(path string, ff findFunc) error { + name := filepath.Join(w.root, path) + + info, err := os.Stat(name) + if err != nil { + return err + } + + if info.IsDir() { + return fmt.Errorf("object is dir") + } + + file, err := os.Open(name) + if err != nil { + return errors.Wrap(err, "unable to open file") // os.Open includes the path + } + defer file.Close() + + return ff(name, file) +} + type zipWalker struct { fileName string } @@ -249,3 +303,31 @@ func (w *zipWalker) get(desc v1.Descriptor, dst io.Writer) (int64, error) { return bytes, nil } + +func (w *zipWalker) find(path string, ff findFunc) error { + done := false + + f := func(relpath string, info os.FileInfo, rdr io.Reader) error { + var err error + if done { + return nil + } + + if filepath.Clean(relpath) == path && !info.IsDir() { + if err = ff(relpath, rdr); err != nil { + return err + } + done = true + } + return nil + } + + if err := w.walk(f); err != nil { + return errors.Wrapf(err, "find failed: unable to walk") + } + if !done { + return os.ErrNotExist + } + + return nil +}