Skip to content

Commit 0d1a8f6

Browse files
committed
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 <[email protected]>
1 parent 812124a commit 0d1a8f6

File tree

2 files changed

+73
-5
lines changed

2 files changed

+73
-5
lines changed

src/archive/tar/format.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,11 @@ func (b *block) ComputeChecksum() (unsigned, signed int64) {
134134
return unsigned, signed
135135
}
136136

137+
// Reset clears the block with all zeros.
138+
func (b *block) Reset() {
139+
*b = block{}
140+
}
141+
137142
type headerV7 [blockSize]byte
138143

139144
func (h *headerV7) Name() []byte { return h[000:][:100] }

src/archive/tar/writer.go

Lines changed: 68 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ type Writer struct {
4040
preferPax bool // use PAX header instead of binary numeric header
4141
hdrBuff block // buffer to use in writeHeader when writing a regular header
4242
paxHdrBuff block // buffer to use in writeHeader when writing a PAX header
43+
44+
blk block // Buffer to use as temporary local storage
4345
}
4446

4547
// NewWriter creates a new Writer writing to w.
@@ -82,20 +84,81 @@ func (tw *Writer) WriteHeader(hdr *Header) error {
8284
hdrCpy.ModTime = hdrCpy.ModTime.Truncate(time.Second)
8385

8486
switch allowedFormats, _ := hdrCpy.allowedFormats(); {
85-
case allowedFormats&formatUSTAR > 0:
86-
// TODO(dsnet): Implement and call specialized writeUSTARHeader.
87-
return tw.writeHeader(&hdrCpy, true)
88-
case allowedFormats&formatPAX > 0:
87+
case allowedFormats&formatUSTAR != 0:
88+
return tw.writeUSTARHeader(&hdrCpy)
89+
case allowedFormats&formatPAX != 0:
8990
// TODO(dsnet): Implement and call specialized writePAXHeader.
9091
return tw.writeHeader(&hdrCpy, true)
91-
case allowedFormats&formatGNU > 0:
92+
case allowedFormats&formatGNU != 0:
9293
// TODO(dsnet): Implement and call specialized writeGNUHeader.
9394
return tw.writeHeader(&hdrCpy, true)
9495
default:
9596
return ErrHeader
9697
}
9798
}
9899

100+
func (tw *Writer) writeUSTARHeader(hdr *Header) error {
101+
// TODO(dsnet): Support USTAR prefix/suffix path splitting.
102+
// See https://golang.org/issue/12594
103+
104+
// Pack the main header.
105+
var f formatter
106+
blk := tw.templateV7Plus(hdr, &f)
107+
blk.SetFormat(formatUSTAR)
108+
if f.err != nil {
109+
return f.err // Should never happen since header is validated
110+
}
111+
return tw.writeRawHeader(blk, hdr.Size)
112+
}
113+
114+
// templateV7Plus fills out the V7 fields of a block using values from hdr.
115+
// It also fills out fields (uname, gname, devmajor, devminor) that are
116+
// shared in the USTAR, PAX, and GNU formats.
117+
//
118+
// The block returned is only valid until the next call to templateV7Plus.
119+
func (tw *Writer) templateV7Plus(hdr *Header, f *formatter) *block {
120+
tw.blk.Reset()
121+
122+
modTime := hdr.ModTime
123+
if modTime.IsZero() {
124+
modTime = time.Unix(0, 0)
125+
}
126+
127+
v7 := tw.blk.V7()
128+
v7.TypeFlag()[0] = hdr.Typeflag
129+
f.formatString(v7.Name(), hdr.Name)
130+
f.formatString(v7.LinkName(), hdr.Linkname)
131+
f.formatOctal(v7.Mode(), hdr.Mode)
132+
f.formatOctal(v7.UID(), int64(hdr.Uid))
133+
f.formatOctal(v7.GID(), int64(hdr.Gid))
134+
f.formatOctal(v7.Size(), hdr.Size)
135+
f.formatOctal(v7.ModTime(), modTime.Unix())
136+
137+
ustar := tw.blk.USTAR()
138+
f.formatString(ustar.UserName(), hdr.Uname)
139+
f.formatString(ustar.GroupName(), hdr.Gname)
140+
f.formatOctal(ustar.DevMajor(), hdr.Devmajor)
141+
f.formatOctal(ustar.DevMinor(), hdr.Devminor)
142+
143+
return &tw.blk
144+
}
145+
146+
// writeRawHeader writes the value of blk, regardless of its value.
147+
// It sets up the Writer such that it can accept a file of the given size.
148+
func (tw *Writer) writeRawHeader(blk *block, size int64) error {
149+
if err := tw.Flush(); err != nil {
150+
return err
151+
}
152+
if _, err := tw.w.Write(blk[:]); err != nil {
153+
return err
154+
}
155+
// TODO(dsnet): Set Size implicitly to zero for header-only entries.
156+
// See https://golang.org/issue/15565
157+
tw.nb = size
158+
tw.pad = -size & (blockSize - 1) // blockSize is a power of two
159+
return nil
160+
}
161+
99162
// WriteHeader writes hdr and prepares to accept the file's contents.
100163
// WriteHeader calls Flush if it is not the first header.
101164
// Calling after a Close will return ErrWriteAfterClose.

0 commit comments

Comments
 (0)