Skip to content

Commit b8a1292

Browse files
dsnetbradfitz
authored andcommitted
archive/tar: convert Reader.Next to be loop based
Motivation for change: * Recursive logic is hard to follow, since it tends to apply things in reverse. On the other hand, the tar formats tend to describe meta headers as affecting the next entry. * Recursion also applies changes in the wrong order. Two test files are attached that use multiple headers. The previous Go behavior differs from what GNU and BSD tar do. Change-Id: Ic1557256fc1363c5cb26570e5d0b9f65a9e57341 Reviewed-on: https://go-review.googlesource.com/14624 Run-TryBot: Joe Tsai <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Brad Fitzpatrick <[email protected]>
1 parent 9bad995 commit b8a1292

File tree

4 files changed

+84
-70
lines changed

4 files changed

+84
-70
lines changed

src/archive/tar/reader.go

+60-70
Original file line numberDiff line numberDiff line change
@@ -117,92 +117,82 @@ func NewReader(r io.Reader) *Reader { return &Reader{r: r} }
117117
//
118118
// io.EOF is returned at the end of the input.
119119
func (tr *Reader) Next() (*Header, error) {
120-
var p parser
121-
var hdr *Header
122-
if tr.err == nil {
123-
tr.skipUnread()
124-
}
125120
if tr.err != nil {
126-
return hdr, tr.err
121+
return nil, tr.err
127122
}
128-
hdr = tr.readHeader()
129-
if hdr == nil {
130-
return hdr, tr.err
131-
}
132-
// Check for PAX/GNU header.
133-
switch hdr.Typeflag {
134-
case TypeXHeader:
135-
// PAX extended header
136-
headers, err := parsePAX(tr)
137-
if err != nil {
138-
return nil, err
139-
}
140-
// We actually read the whole file,
141-
// but this skips alignment padding
142-
tr.skipUnread()
123+
124+
var hdr *Header
125+
var extHdrs map[string]string
126+
127+
// Externally, Next iterates through the tar archive as if it is a series of
128+
// files. Internally, the tar format often uses fake "files" to add meta
129+
// data that describes the next file. These meta data "files" should not
130+
// normally be visible to the outside. As such, this loop iterates through
131+
// one or more "header files" until it finds a "normal file".
132+
loop:
133+
for {
134+
tr.err = tr.skipUnread()
143135
if tr.err != nil {
144136
return nil, tr.err
145137
}
138+
146139
hdr = tr.readHeader()
147-
if hdr == nil {
140+
if tr.err != nil {
148141
return nil, tr.err
149142
}
150-
mergePAX(hdr, headers)
151143

152-
// Check for a PAX format sparse file
153-
sp, err := tr.checkForGNUSparsePAXHeaders(hdr, headers)
154-
if err != nil {
155-
tr.err = err
156-
return nil, err
157-
}
158-
if sp != nil {
159-
// Sparse files do not make sense when applied to the special header
160-
// types that never have a data section.
161-
if isHeaderOnlyType(hdr.Typeflag) {
162-
tr.err = ErrHeader
144+
// Check for PAX/GNU special headers and files.
145+
switch hdr.Typeflag {
146+
case TypeXHeader:
147+
extHdrs, tr.err = parsePAX(tr)
148+
if tr.err != nil {
163149
return nil, tr.err
164150
}
165-
166-
// Current file is a PAX format GNU sparse file.
167-
// Set the current file reader to a sparse file reader.
168-
tr.curr, tr.err = newSparseFileReader(tr.curr, sp, hdr.Size)
151+
continue loop // This is a meta header affecting the next header
152+
case TypeGNULongName, TypeGNULongLink:
153+
var realname []byte
154+
realname, tr.err = ioutil.ReadAll(tr)
169155
if tr.err != nil {
170156
return nil, tr.err
171157
}
158+
159+
// Convert GNU extensions to use PAX headers.
160+
if extHdrs == nil {
161+
extHdrs = make(map[string]string)
162+
}
163+
var p parser
164+
switch hdr.Typeflag {
165+
case TypeGNULongName:
166+
extHdrs[paxPath] = p.parseString(realname)
167+
case TypeGNULongLink:
168+
extHdrs[paxLinkpath] = p.parseString(realname)
169+
}
170+
if p.err != nil {
171+
tr.err = p.err
172+
return nil, tr.err
173+
}
174+
continue loop // This is a meta header affecting the next header
175+
default:
176+
mergePAX(hdr, extHdrs)
177+
178+
// Check for a PAX format sparse file
179+
sp, err := tr.checkForGNUSparsePAXHeaders(hdr, extHdrs)
180+
if err != nil {
181+
tr.err = err
182+
return nil, err
183+
}
184+
if sp != nil {
185+
// Current file is a PAX format GNU sparse file.
186+
// Set the current file reader to a sparse file reader.
187+
tr.curr, tr.err = newSparseFileReader(tr.curr, sp, hdr.Size)
188+
if tr.err != nil {
189+
return nil, tr.err
190+
}
191+
}
192+
break loop // This is a file, so stop
172193
}
173-
return hdr, nil
174-
case TypeGNULongName:
175-
// We have a GNU long name header. Its contents are the real file name.
176-
realname, err := ioutil.ReadAll(tr)
177-
if err != nil {
178-
return nil, err
179-
}
180-
hdr, tr.err = tr.Next()
181-
if tr.err != nil {
182-
return nil, tr.err
183-
}
184-
hdr.Name = p.parseString(realname)
185-
if p.err != nil {
186-
return nil, p.err
187-
}
188-
return hdr, nil
189-
case TypeGNULongLink:
190-
// We have a GNU long link header.
191-
realname, err := ioutil.ReadAll(tr)
192-
if err != nil {
193-
return nil, err
194-
}
195-
hdr, tr.err = tr.Next()
196-
if tr.err != nil {
197-
return nil, tr.err
198-
}
199-
hdr.Linkname = p.parseString(realname)
200-
if p.err != nil {
201-
return nil, p.err
202-
}
203-
return hdr, nil
204194
}
205-
return hdr, tr.err
195+
return hdr, nil
206196
}
207197

208198
// checkForGNUSparsePAXHeaders checks the PAX headers for GNU sparse headers. If they are found, then

src/archive/tar/reader_test.go

+24
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,30 @@ var untarTests = []*untarTest{
288288
},
289289
},
290290
},
291+
{
292+
// Matches the behavior of GNU, BSD, and STAR tar utilities.
293+
file: "testdata/gnu-multi-hdrs.tar",
294+
headers: []*Header{
295+
{
296+
Name: "GNU2/GNU2/long-path-name",
297+
Linkname: "GNU4/GNU4/long-linkpath-name",
298+
ModTime: time.Unix(0, 0),
299+
Typeflag: '2',
300+
},
301+
},
302+
},
303+
{
304+
// Matches the behavior of GNU and BSD tar utilities.
305+
file: "testdata/pax-multi-hdrs.tar",
306+
headers: []*Header{
307+
{
308+
Name: "bar",
309+
Linkname: "PAX4/PAX4/long-linkpath-name",
310+
ModTime: time.Unix(0, 0),
311+
Typeflag: '2',
312+
},
313+
},
314+
},
291315
{
292316
file: "testdata/neg-size.tar",
293317
err: ErrHeader,
4.5 KB
Binary file not shown.
4.5 KB
Binary file not shown.

0 commit comments

Comments
 (0)