@@ -2,11 +2,13 @@ package tarfs
22
33import (
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
5661Scan:
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