2015-02-24 22:07:00 +00:00
|
|
|
package asm
|
|
|
|
|
|
|
|
import (
|
2015-03-03 19:23:04 +00:00
|
|
|
"bytes"
|
|
|
|
"fmt"
|
|
|
|
"hash/crc64"
|
2015-02-24 22:07:00 +00:00
|
|
|
"io"
|
|
|
|
|
|
|
|
"github.com/vbatts/tar-split/tar/storage"
|
|
|
|
)
|
|
|
|
|
2015-06-23 20:13:29 +00:00
|
|
|
// NewOutputTarStream returns an io.ReadCloser that is an assembled tar archive
|
2015-03-02 20:25:03 +00:00
|
|
|
// stream.
|
|
|
|
//
|
2015-03-09 17:20:26 +00:00
|
|
|
// It takes a storage.FileGetter, for mapping the file payloads that are to be read in,
|
2015-03-02 20:25:03 +00:00
|
|
|
// and a storage.Unpacker, which has access to the rawbytes and file order
|
|
|
|
// metadata. With the combination of these two items, a precise assembled Tar
|
|
|
|
// archive is possible.
|
2015-03-09 17:20:26 +00:00
|
|
|
func NewOutputTarStream(fg storage.FileGetter, up storage.Unpacker) io.ReadCloser {
|
2015-03-02 20:25:03 +00:00
|
|
|
// ... Since these are interfaces, this is possible, so let's not have a nil pointer
|
|
|
|
if fg == nil || up == nil {
|
|
|
|
return nil
|
|
|
|
}
|
2015-02-24 22:07:00 +00:00
|
|
|
pr, pw := io.Pipe()
|
|
|
|
go func() {
|
|
|
|
for {
|
|
|
|
entry, err := up.Next()
|
|
|
|
if err != nil {
|
|
|
|
pw.CloseWithError(err)
|
2015-07-22 15:32:18 +00:00
|
|
|
return
|
2015-02-24 22:07:00 +00:00
|
|
|
}
|
|
|
|
switch entry.Type {
|
|
|
|
case storage.SegmentType:
|
|
|
|
if _, err := pw.Write(entry.Payload); err != nil {
|
|
|
|
pw.CloseWithError(err)
|
2015-07-22 15:32:18 +00:00
|
|
|
return
|
2015-02-24 22:07:00 +00:00
|
|
|
}
|
|
|
|
case storage.FileType:
|
2015-02-25 17:56:40 +00:00
|
|
|
if entry.Size == 0 {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
fh, err := fg.Get(entry.Name)
|
|
|
|
if err != nil {
|
|
|
|
pw.CloseWithError(err)
|
2015-07-22 15:32:18 +00:00
|
|
|
return
|
2015-02-25 17:56:40 +00:00
|
|
|
}
|
2015-03-09 17:20:26 +00:00
|
|
|
c := crc64.New(storage.CRCTable)
|
2015-03-03 19:23:04 +00:00
|
|
|
tRdr := io.TeeReader(fh, c)
|
|
|
|
if _, err := io.Copy(pw, tRdr); err != nil {
|
2015-07-15 17:43:48 +00:00
|
|
|
fh.Close()
|
2015-02-24 22:07:00 +00:00
|
|
|
pw.CloseWithError(err)
|
2015-07-22 15:32:18 +00:00
|
|
|
return
|
2015-02-24 22:07:00 +00:00
|
|
|
}
|
2015-03-03 19:23:04 +00:00
|
|
|
if !bytes.Equal(c.Sum(nil), entry.Payload) {
|
2015-03-03 19:27:37 +00:00
|
|
|
// I would rather this be a comparable ErrInvalidChecksum or such,
|
|
|
|
// but since it's coming through the PipeReader, the context of
|
|
|
|
// _which_ file would be lost...
|
2015-07-15 17:43:48 +00:00
|
|
|
fh.Close()
|
2015-03-03 19:23:04 +00:00
|
|
|
pw.CloseWithError(fmt.Errorf("file integrity checksum failed for %q", entry.Name))
|
2015-07-22 15:32:18 +00:00
|
|
|
return
|
2015-03-03 19:23:04 +00:00
|
|
|
}
|
2015-07-15 17:43:48 +00:00
|
|
|
fh.Close()
|
2015-02-24 22:07:00 +00:00
|
|
|
}
|
|
|
|
}
|
2015-03-05 19:09:17 +00:00
|
|
|
pw.Close()
|
2015-02-24 22:07:00 +00:00
|
|
|
}()
|
|
|
|
return pr
|
|
|
|
}
|