1
0
mirror of https://github.com/golang/go synced 2024-11-24 07:30:10 -07:00

archive/tar: implement specialized logic for USTAR format

Rather than going through the complicated logic of writeHeader,
implement a writeUSTARHeader that only knows about the USTAR format.
This makes the logic much easier to reason about since you only
need to be concerned about USTAR and not all the subtle
differences between USTAR, PAX, and GNU.

We seperate out the logic in writeUSTARHeader into templateV7Plus
and writeRawHeader since the planned implementations of
writePAXHeader and writeGNUHeader will use them.

Change-Id: Ie75a54ac998420ece82686159ae6fa39f8b128e9
Reviewed-on: https://go-review.googlesource.com/54970
Reviewed-by: Ian Lance Taylor <iant@golang.org>
This commit is contained in:
Joe Tsai 2017-08-10 22:34:51 -07:00 committed by Joe Tsai
parent 812124a567
commit 0d1a8f6e12
2 changed files with 73 additions and 5 deletions

View File

@ -134,6 +134,11 @@ func (b *block) ComputeChecksum() (unsigned, signed int64) {
return unsigned, signed return unsigned, signed
} }
// Reset clears the block with all zeros.
func (b *block) Reset() {
*b = block{}
}
type headerV7 [blockSize]byte type headerV7 [blockSize]byte
func (h *headerV7) Name() []byte { return h[000:][:100] } func (h *headerV7) Name() []byte { return h[000:][:100] }

View File

@ -40,6 +40,8 @@ type Writer struct {
preferPax bool // use PAX header instead of binary numeric header preferPax bool // use PAX header instead of binary numeric header
hdrBuff block // buffer to use in writeHeader when writing a regular header hdrBuff block // buffer to use in writeHeader when writing a regular header
paxHdrBuff block // buffer to use in writeHeader when writing a PAX header paxHdrBuff block // buffer to use in writeHeader when writing a PAX header
blk block // Buffer to use as temporary local storage
} }
// NewWriter creates a new Writer writing to w. // NewWriter creates a new Writer writing to w.
@ -82,13 +84,12 @@ func (tw *Writer) WriteHeader(hdr *Header) error {
hdrCpy.ModTime = hdrCpy.ModTime.Truncate(time.Second) hdrCpy.ModTime = hdrCpy.ModTime.Truncate(time.Second)
switch allowedFormats, _ := hdrCpy.allowedFormats(); { switch allowedFormats, _ := hdrCpy.allowedFormats(); {
case allowedFormats&formatUSTAR > 0: case allowedFormats&formatUSTAR != 0:
// TODO(dsnet): Implement and call specialized writeUSTARHeader. return tw.writeUSTARHeader(&hdrCpy)
return tw.writeHeader(&hdrCpy, true) case allowedFormats&formatPAX != 0:
case allowedFormats&formatPAX > 0:
// TODO(dsnet): Implement and call specialized writePAXHeader. // TODO(dsnet): Implement and call specialized writePAXHeader.
return tw.writeHeader(&hdrCpy, true) return tw.writeHeader(&hdrCpy, true)
case allowedFormats&formatGNU > 0: case allowedFormats&formatGNU != 0:
// TODO(dsnet): Implement and call specialized writeGNUHeader. // TODO(dsnet): Implement and call specialized writeGNUHeader.
return tw.writeHeader(&hdrCpy, true) return tw.writeHeader(&hdrCpy, true)
default: default:
@ -96,6 +97,68 @@ func (tw *Writer) WriteHeader(hdr *Header) error {
} }
} }
func (tw *Writer) writeUSTARHeader(hdr *Header) error {
// TODO(dsnet): Support USTAR prefix/suffix path splitting.
// See https://golang.org/issue/12594
// Pack the main header.
var f formatter
blk := tw.templateV7Plus(hdr, &f)
blk.SetFormat(formatUSTAR)
if f.err != nil {
return f.err // Should never happen since header is validated
}
return tw.writeRawHeader(blk, hdr.Size)
}
// templateV7Plus fills out the V7 fields of a block using values from hdr.
// It also fills out fields (uname, gname, devmajor, devminor) that are
// shared in the USTAR, PAX, and GNU formats.
//
// The block returned is only valid until the next call to templateV7Plus.
func (tw *Writer) templateV7Plus(hdr *Header, f *formatter) *block {
tw.blk.Reset()
modTime := hdr.ModTime
if modTime.IsZero() {
modTime = time.Unix(0, 0)
}
v7 := tw.blk.V7()
v7.TypeFlag()[0] = hdr.Typeflag
f.formatString(v7.Name(), hdr.Name)
f.formatString(v7.LinkName(), hdr.Linkname)
f.formatOctal(v7.Mode(), hdr.Mode)
f.formatOctal(v7.UID(), int64(hdr.Uid))
f.formatOctal(v7.GID(), int64(hdr.Gid))
f.formatOctal(v7.Size(), hdr.Size)
f.formatOctal(v7.ModTime(), modTime.Unix())
ustar := tw.blk.USTAR()
f.formatString(ustar.UserName(), hdr.Uname)
f.formatString(ustar.GroupName(), hdr.Gname)
f.formatOctal(ustar.DevMajor(), hdr.Devmajor)
f.formatOctal(ustar.DevMinor(), hdr.Devminor)
return &tw.blk
}
// writeRawHeader writes the value of blk, regardless of its value.
// It sets up the Writer such that it can accept a file of the given size.
func (tw *Writer) writeRawHeader(blk *block, size int64) error {
if err := tw.Flush(); err != nil {
return err
}
if _, err := tw.w.Write(blk[:]); err != nil {
return err
}
// TODO(dsnet): Set Size implicitly to zero for header-only entries.
// See https://golang.org/issue/15565
tw.nb = size
tw.pad = -size & (blockSize - 1) // blockSize is a power of two
return nil
}
// WriteHeader writes hdr and prepares to accept the file's contents. // WriteHeader writes hdr and prepares to accept the file's contents.
// WriteHeader calls Flush if it is not the first header. // WriteHeader calls Flush if it is not the first header.
// Calling after a Close will return ErrWriteAfterClose. // Calling after a Close will return ErrWriteAfterClose.