]> Cypherpunks repositories - gostls13.git/commitdiff
archive/tar: implement specialized logic for USTAR format
authorJoe Tsai <joetsai@digital-static.net>
Fri, 11 Aug 2017 05:34:51 +0000 (22:34 -0700)
committerJoe Tsai <thebrokentoaster@gmail.com>
Sat, 12 Aug 2017 01:48:06 +0000 (01:48 +0000)
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>
src/archive/tar/format.go
src/archive/tar/writer.go

index c2c9910d00281ffd918258dcee2a5004daf4c554..1e9a006a6fedb8c7c7d309e5b73f25ed74b8842b 100644 (file)
@@ -134,6 +134,11 @@ func (b *block) ComputeChecksum() (unsigned, signed int64) {
        return unsigned, signed
 }
 
+// Reset clears the block with all zeros.
+func (b *block) Reset() {
+       *b = block{}
+}
+
 type headerV7 [blockSize]byte
 
 func (h *headerV7) Name() []byte     { return h[000:][:100] }
index 8efd149fbaf80c52df55a1c9be1e6516c910b4b8..28ce13ea728c55591f59906d471487f64cfac1c6 100644 (file)
@@ -40,6 +40,8 @@ type Writer struct {
        preferPax  bool  // use PAX header instead of binary numeric 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
+
+       blk block // Buffer to use as temporary local storage
 }
 
 // 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)
 
        switch allowedFormats, _ := hdrCpy.allowedFormats(); {
-       case allowedFormats&formatUSTAR > 0:
-               // TODO(dsnet): Implement and call specialized writeUSTARHeader.
-               return tw.writeHeader(&hdrCpy, true)
-       case allowedFormats&formatPAX > 0:
+       case allowedFormats&formatUSTAR != 0:
+               return tw.writeUSTARHeader(&hdrCpy)
+       case allowedFormats&formatPAX != 0:
                // TODO(dsnet): Implement and call specialized writePAXHeader.
                return tw.writeHeader(&hdrCpy, true)
-       case allowedFormats&formatGNU > 0:
+       case allowedFormats&formatGNU != 0:
                // TODO(dsnet): Implement and call specialized writeGNUHeader.
                return tw.writeHeader(&hdrCpy, true)
        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 calls Flush if it is not the first header.
 // Calling after a Close will return ErrWriteAfterClose.