Skip to content

Commit

Permalink
gunzip and untar passthrough writers (oras-project#191)
Browse files Browse the repository at this point in the history
Signed-off-by: Avi Deitcher <[email protected]>
  • Loading branch information
deitch authored Oct 28, 2020
1 parent 79a6cf6 commit 4cae1db
Show file tree
Hide file tree
Showing 3 changed files with 116 additions and 0 deletions.
6 changes: 6 additions & 0 deletions pkg/content/consts.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,9 @@ const (
// Reference: https://github.com/opencontainers/image-spec/blob/master/image-layout.md#indexjson-file
OCIImageIndexFile = "index.json"
)

const (
// DefaultBlocksize default size of each slice of bytes read in each write through in gunzipand untar.
// Simply uses the same size as io.Copy()
DefaultBlocksize = 32768
)
48 changes: 48 additions & 0 deletions pkg/content/gunzip.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package content

import (
"compress/gzip"
"fmt"
"io"

"github.com/containerd/containerd/content"
)

// NewGunzipWriter wrap a writer with a gunzip, so that the stream is gunzipped
func NewGunzipWriter(writer content.Writer, blocksize int) content.Writer {
if blocksize == 0 {
blocksize = DefaultBlocksize
}
return NewPassthroughWriter(writer, func(r io.Reader, w io.Writer, done chan<- error) {
gr, err := gzip.NewReader(r)
if err != nil {
done <- fmt.Errorf("error creating gzip reader: %v", err)
return
}
// write out the uncompressed data
b := make([]byte, blocksize, blocksize)
for {
var n int
n, err = gr.Read(b)
if err != nil && err != io.EOF {
err = fmt.Errorf("GunzipWriter data read error: %v\n", err)
break
}
l := n
if n > len(b) {
l = len(b)
}
if _, err2 := w.Write(b[:l]); err2 != nil {
err = fmt.Errorf("GunzipWriter: error writing to underlying writer: %v", err2)
break
}
if err == io.EOF {
// clear the error
err = nil
break
}
}
gr.Close()
done <- err
})
}
62 changes: 62 additions & 0 deletions pkg/content/untar.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package content

import (
"archive/tar"
"fmt"
"io"

"github.com/containerd/containerd/content"
)

// NewUntarWriter wrap a writer with an untar, so that the stream is untarred
func NewUntarWriter(writer content.Writer, blocksize int) content.Writer {
if blocksize == 0 {
blocksize = DefaultBlocksize
}
return NewPassthroughWriter(writer, func(r io.Reader, w io.Writer, done chan<- error) {
tr := tar.NewReader(r)
var err error
for {
_, err := tr.Next()
if err == io.EOF {
// clear the error, since we do not pass an io.EOF
err = nil
break // End of archive
}
if err != nil {
// pass the error on
err = fmt.Errorf("UntarWriter tar file header read error: %v", err)
break
}
// write out the untarred data
// we can handle io.EOF, just go to the next file
// any other errors should stop and get reported
b := make([]byte, blocksize, blocksize)
for {
var n int
n, err = tr.Read(b)
if err != nil && err != io.EOF {
err = fmt.Errorf("UntarWriter file data read error: %v\n", err)
break
}
l := n
if n > len(b) {
l = len(b)
}
if _, err2 := w.Write(b[:l]); err2 != nil {
err = fmt.Errorf("UntarWriter error writing to underlying writer: %v", err2)
break
}
if err == io.EOF {
// go to the next file
break
}
}
// did we break with a non-nil and non-EOF error?
if err != nil && err != io.EOF {
break
}
}
done <- err
})
}

0 comments on commit 4cae1db

Please sign in to comment.