diff --git a/image/descriptor.go b/image/descriptor.go index 341b65c..1e760a7 100644 --- a/image/descriptor.go +++ b/image/descriptor.go @@ -105,28 +105,14 @@ func (d *descriptor) validate(w walker, mts []string) error { if !found { return fmt.Errorf("invalid descriptor MediaType %q", d.MediaType) } - switch err := w.walk(func(path string, info os.FileInfo, r io.Reader) error { - if info.IsDir() { - return nil - } - - filename, err := filepath.Rel(filepath.Join("blobs", d.algo()), filepath.Clean(path)) - if err != nil || d.hash() != filename { - return nil - } - if err := d.validateContent(r); err != nil { - return err - } - return errEOW - }); err { - case nil: - return fmt.Errorf("%s: not found", d.Digest) - case errEOW: - return nil - default: - return errors.Wrapf(err, "%s: validation failed", d.Digest) + rc, err := w.Get(*d) + if err != nil { + return err } + defer rc.Close() + + return d.validateContent(rc) } func (d *descriptor) validateContent(r io.Reader) error { diff --git a/image/image.go b/image/image.go index 095f48f..1551c1c 100644 --- a/image/image.go +++ b/image/image.go @@ -40,7 +40,7 @@ func Validate(tarFile string, refs []string, out *log.Logger) error { } defer f.Close() - return validate(newTarWalker(f), refs, out) + return validate(newTarWalker(tarFile, f), refs, out) } var validRefMediaTypes = []string{ @@ -111,7 +111,7 @@ func Unpack(tarFile, dest, ref string) error { } defer f.Close() - return unpack(newTarWalker(f), dest, ref) + return unpack(newTarWalker(tarFile, f), dest, ref) } func unpack(w walker, dest, refName string) error { @@ -153,7 +153,7 @@ func CreateRuntimeBundle(tarFile, dest, ref, root string) error { } defer f.Close() - return createRuntimeBundle(newTarWalker(f), dest, ref, root) + return createRuntimeBundle(newTarWalker(tarFile, f), dest, ref, root) } func createRuntimeBundle(w walker, dest, refName, rootfs string) error { diff --git a/image/reader.go b/image/reader.go new file mode 100644 index 0000000..078db5a --- /dev/null +++ b/image/reader.go @@ -0,0 +1,84 @@ +// Copyright 2016 The Linux Foundation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package image + +import ( + "archive/tar" + "bytes" + "fmt" + "io" + "io/ioutil" + "os" + "path/filepath" +) + +type reader interface { + Get(desc descriptor) (io.ReadCloser, error) +} + +type tarReader struct { + name string +} + +func (r *tarReader) Get(desc descriptor) (io.ReadCloser, error) { + f, err := os.Open(r.name) + if err != nil { + return nil, err + } + defer f.Close() + + tr := tar.NewReader(f) +loop: + for { + hdr, err := tr.Next() + switch err { + case io.EOF: + break loop + case nil: + // success, continue below + default: + return nil, err + } + if hdr.Name == filepath.Join("blobs", desc.algo(), desc.hash()) && + !hdr.FileInfo().IsDir() { + buf, err := ioutil.ReadAll(tr) + if err != nil { + return nil, err + } + return ioutil.NopCloser(bytes.NewReader(buf)), nil + } + } + + return nil, fmt.Errorf("object not found") +} + +type layoutReader struct { + root string +} + +func (r *layoutReader) Get(desc descriptor) (io.ReadCloser, error) { + name := filepath.Join(r.root, "blobs", desc.algo(), desc.hash()) + + info, err := os.Stat(name) + if err != nil { + return nil, err + } + + if info.IsDir() { + return nil, fmt.Errorf("object is dir") + } + + return os.Open(name) +} diff --git a/image/walker.go b/image/walker.go index 33f9674..27acfac 100644 --- a/image/walker.go +++ b/image/walker.go @@ -35,15 +35,17 @@ type walkFunc func(path string, _ os.FileInfo, _ io.Reader) error // calling walk for each file or directory in the tree. type walker interface { walk(walkFunc) error + reader } type tarWalker struct { r io.ReadSeeker + tarReader } // newTarWalker returns a Walker that walks through .tar files. -func newTarWalker(r io.ReadSeeker) walker { - return &tarWalker{r} +func newTarWalker(tarFile string, r io.ReadSeeker) walker { + return &tarWalker{r, tarReader{name: tarFile}} } func (w *tarWalker) walk(f walkFunc) error { @@ -82,12 +84,13 @@ func (eofReader) Read(_ []byte) (int, error) { type pathWalker struct { root string + layoutReader } // newPathWalker returns a Walker that walks through directories // starting at the given root path. It does not follow symlinks. func newPathWalker(root string) walker { - return &pathWalker{root} + return &pathWalker{root, layoutReader{root: root}} } func (w *pathWalker) walk(f walkFunc) error {