Skip to content

Commit 5d4f550

Browse files
committed
tarfs: implement pax size extension
Signed-off-by: Hank Donnay <hdonnay@redhat.com> Change-Id: I6c2d6cfae9f6e742056c157e38b8b6156a6a6964
1 parent 1974408 commit 5d4f550

1 file changed

Lines changed: 47 additions & 2 deletions

File tree

pkg/tarfs/parse.go

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,13 @@ package tarfs
22

33
import (
44
"archive/tar"
5+
"bufio"
56
"bytes"
67
"errors"
78
"fmt"
89
"io"
910
"strconv"
11+
"strings"
1012
)
1113

1214
// The value we should find in the "magic" position of the tar header.
@@ -52,6 +54,9 @@ func findSegments(r io.ReaderAt) ([]segment, error) {
5254
var blk int64
5355
// Has the parser seen a zeroes block.
5456
var zeroes bool
57+
// Size carried over from pax headers.
58+
// Would someone set a global size? Who knows.
59+
var gSize, xSize int64
5560

5661
Scan:
5762
for {
@@ -112,20 +117,60 @@ Scan:
112117
case !bytes.Equal(b[versionOff:][:2], []byte("00")):
113118
return nil, parseErr("bad block at %d: got version %+q", off, b[versionOff:][:2])
114119
}
120+
typ := b[typeflag]
115121
encSz := b[sizeOff:][:12]
116122
sz, err := parseNumber(encSz)
117123
if err != nil {
118124
return nil, parseErr("invalid number: %024x: %v", encSz, err)
119125
}
126+
// If this this is "real" file and there's a size from a previous
127+
// extended header, adjust the size value.
128+
if typ < tar.TypeCont && xSize != 0 {
129+
sz, xSize = xSize, gSize
130+
}
120131
nBlk := sz / blockSz
121132
if sz%blockSz != 0 {
122133
nBlk++
123134
}
124135
blk++ // Current header block
125136
blk += nBlk // File contents
126-
switch b[typeflag] {
127-
case tar.TypeXHeader, tar.TypeGNULongLink, tar.TypeGNULongName, tar.TypeGNUSparse:
137+
switch typ {
138+
case tar.TypeGNULongLink, tar.TypeGNULongName, tar.TypeGNUSparse:
128139
// All these are prepended to a "real" entry.
140+
case tar.TypeXHeader, tar.TypeXGlobalHeader:
141+
// Handle pax headers to keep the ultimate file size correct.
142+
var p *int64
143+
switch typ {
144+
case tar.TypeXHeader:
145+
p = &xSize
146+
case tar.TypeXGlobalHeader:
147+
p = &gSize
148+
default:
149+
panic("unreachable")
150+
}
151+
// This is just the "file" data, which is why this is different from
152+
// the [segment] math below.
153+
sr := io.NewSectionReader(r, (blk-nBlk)*blockSz, sz)
154+
s := bufio.NewScanner(sr)
155+
for s.Scan() {
156+
fs := strings.FieldsFunc(s.Text(), func(r rune) bool { return r == ' ' || r == '=' })
157+
if fs[1] == "size" {
158+
// This is *not* the stringified octal madness of a real header.
159+
*p, err = strconv.ParseInt(fs[2], 10, 64)
160+
if err != nil {
161+
return nil, parseErr("bad block at %d: weird PAX header: bad size: %v", off, err)
162+
}
163+
}
164+
}
165+
if err := s.Err(); err != nil {
166+
return nil, parseErr("bad block at %d: weird PAX header: %v", off, err)
167+
}
168+
if typ == tar.TypeXGlobalHeader {
169+
// Make sure to immediately "promote" the global size.
170+
// Without this, the global size would only take effect after
171+
// the next regular file.
172+
xSize = gSize
173+
}
129174
case tar.TypeBlock, tar.TypeChar, tar.TypeCont, tar.TypeDir, tar.TypeFifo, tar.TypeLink, tar.TypeReg, tar.TypeRegA, tar.TypeSymlink:
130175
// Found a data block, emit it:
131176
ret = append(ret, segment{start: cur * blockSz, size: (blk - cur) * blockSz})

0 commit comments

Comments
 (0)