1 // Copyright 2009 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
20 HeaderError = errors.New("invalid tar header")
23 // A Reader provides sequential access to the contents of a tar archive.
24 // A tar archive consists of a sequence of files.
25 // The Next method advances to the next file in the archive (including the first),
26 // and then it can be treated as an io.Reader to access the file's data.
29 // tr := tar.NewReader(r)
31 // hdr, err := tr.Next()
33 // // end of tar archive
44 nb int64 // number of unread bytes for current file entry
45 pad int64 // amount of padding (ignored) after current file entry
48 // NewReader creates a new Reader reading from r.
49 func NewReader(r io.Reader) *Reader { return &Reader{r: r} }
51 // Next advances to the next entry in the tar archive.
52 func (tr *Reader) Next() (*Header, error) {
63 // Parse bytes as a NUL-terminated C-style string.
64 // If a NUL byte is not found then the whole slice is returned as a string.
65 func cString(b []byte) string {
67 for n < len(b) && b[n] != 0 {
73 func (tr *Reader) octal(b []byte) int64 {
74 // Removing leading spaces.
75 for len(b) > 0 && b[0] == ' ' {
78 // Removing trailing NULs and spaces.
79 for len(b) > 0 && (b[len(b)-1] == ' ' || b[len(b)-1] == '\x00') {
82 x, err := strconv.Btoui64(cString(b), 8)
89 // Skip any unread bytes in the existing file entry, as well as any alignment padding.
90 func (tr *Reader) skipUnread() {
91 nr := tr.nb + tr.pad // number of bytes to skip
93 if sr, ok := tr.r.(io.Seeker); ok {
94 if _, err := sr.Seek(nr, os.SEEK_CUR); err == nil {
98 _, tr.err = io.CopyN(ioutil.Discard, tr.r, nr)
101 func (tr *Reader) verifyChecksum(header []byte) bool {
106 given := tr.octal(header[148:156])
107 unsigned, signed := checksum(header)
108 return given == unsigned || given == signed
111 func (tr *Reader) readHeader() *Header {
112 header := make([]byte, blockSize)
113 if _, tr.err = io.ReadFull(tr.r, header); tr.err != nil {
117 // Two blocks of zero bytes marks the end of the archive.
118 if bytes.Equal(header, zeroBlock[0:blockSize]) {
119 if _, tr.err = io.ReadFull(tr.r, header); tr.err != nil {
122 if bytes.Equal(header, zeroBlock[0:blockSize]) {
125 tr.err = HeaderError // zero block and then non-zero block
130 if !tr.verifyChecksum(header) {
139 hdr.Name = cString(s.next(100))
140 hdr.Mode = tr.octal(s.next(8))
141 hdr.Uid = int(tr.octal(s.next(8)))
142 hdr.Gid = int(tr.octal(s.next(8)))
143 hdr.Size = tr.octal(s.next(12))
144 hdr.Mtime = tr.octal(s.next(12))
146 hdr.Typeflag = s.next(1)[0]
147 hdr.Linkname = cString(s.next(100))
149 // The remainder of the header depends on the value of magic.
150 // The original (v7) version of tar had no explicit magic field,
151 // so its magic bytes, like the rest of the block, are NULs.
152 magic := string(s.next(8)) // contains version field as well.
155 case "ustar\x0000": // POSIX tar (1003.1-1988)
156 if string(header[508:512]) == "tar\x00" {
161 case "ustar \x00": // old GNU tar
166 case "posix", "gnu", "star":
167 hdr.Uname = cString(s.next(32))
168 hdr.Gname = cString(s.next(32))
169 devmajor := s.next(8)
170 devminor := s.next(8)
171 if hdr.Typeflag == TypeChar || hdr.Typeflag == TypeBlock {
172 hdr.Devmajor = tr.octal(devmajor)
173 hdr.Devminor = tr.octal(devminor)
178 prefix = cString(s.next(155))
180 prefix = cString(s.next(131))
181 hdr.Atime = tr.octal(s.next(12))
182 hdr.Ctime = tr.octal(s.next(12))
185 hdr.Name = prefix + "/" + hdr.Name
194 // Maximum value of hdr.Size is 64 GB (12 octal digits),
195 // so there's no risk of int64 overflowing.
196 tr.nb = int64(hdr.Size)
197 tr.pad = -tr.nb & (blockSize - 1) // blockSize is a power of two
202 // Read reads from the current entry in the tar archive.
203 // It returns 0, io.EOF when it reaches the end of that entry,
204 // until Next is called to advance to the next entry.
205 func (tr *Reader) Read(b []byte) (n int, err error) {
211 if int64(len(b)) > tr.nb {
214 n, err = tr.r.Read(b)
217 if err == io.EOF && tr.nb > 0 {
218 err = io.ErrUnexpectedEOF