Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(29)

Delta Between Two Patch Sets: src/pkg/archive/tar/reader.go

Issue 7229066: archive/tar: merge 6700047 and 6814084
Left Patch Set: Created 12 years, 2 months ago
Right Patch Set: diff -r 9ff35ff05de6 https://code.google.com/p/go Created 12 years, 2 months ago
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
Right: Side by side diff | Download
« no previous file with change/comment | « src/pkg/archive/tar/common.go ('k') | src/pkg/archive/tar/reader_test.go » ('j') | no next file with change/comment »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
LEFTRIGHT
(no file at all)
1 // Copyright 2009 The Go Authors. All rights reserved. 1 // Copyright 2009 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style 2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file. 3 // license that can be found in the LICENSE file.
4 4
5 package tar 5 package tar
6 6
7 // TODO(dsymonds): 7 // TODO(dsymonds):
8 // - pax extensions 8 // - pax extensions
9 9
10 import ( 10 import (
11 "bytes" 11 "bytes"
12 "errors" 12 "errors"
13 "io" 13 "io"
14 "io/ioutil" 14 "io/ioutil"
15 "os" 15 "os"
16 "strconv" 16 "strconv"
17 "strings"
17 "time" 18 "time"
18 ) 19 )
19 20
20 var ( 21 var (
21 ErrHeader = errors.New("archive/tar: invalid tar header") 22 ErrHeader = errors.New("archive/tar: invalid tar header")
22 ) 23 )
24
25 const maxNanoSecondIntSize = 9
23 26
24 // A Reader provides sequential access to the contents of a tar archive. 27 // A Reader provides sequential access to the contents of a tar archive.
25 // A tar archive consists of a sequence of files. 28 // A tar archive consists of a sequence of files.
26 // The Next method advances to the next file in the archive (including the first ), 29 // The Next method advances to the next file in the archive (including the first ),
27 // and then it can be treated as an io.Reader to access the file's data. 30 // and then it can be treated as an io.Reader to access the file's data.
28 // 31 //
29 // Example: 32 // Example:
30 // tr := tar.NewReader(r) 33 // tr := tar.NewReader(r)
31 // for { 34 // for {
32 // hdr, err := tr.Next() 35 // hdr, err := tr.Next()
(...skipping 15 matching lines...) Expand all
48 51
49 // NewReader creates a new Reader reading from r. 52 // NewReader creates a new Reader reading from r.
50 func NewReader(r io.Reader) *Reader { return &Reader{r: r} } 53 func NewReader(r io.Reader) *Reader { return &Reader{r: r} }
51 54
52 // Next advances to the next entry in the tar archive. 55 // Next advances to the next entry in the tar archive.
53 func (tr *Reader) Next() (*Header, error) { 56 func (tr *Reader) Next() (*Header, error) {
54 var hdr *Header 57 var hdr *Header
55 if tr.err == nil { 58 if tr.err == nil {
56 tr.skipUnread() 59 tr.skipUnread()
57 } 60 }
58 » if tr.err == nil { 61 » if tr.err != nil {
62 » » return hdr, tr.err
63 » }
64 » hdr = tr.readHeader()
65 » if hdr == nil {
66 » » return hdr, tr.err
67 » }
68 » // Check for PAX/GNU header.
69 » switch hdr.Typeflag {
70 » case TypeXHeader:
71 » » // PAX extended header
72 » » headers, err := parsePAX(tr)
73 » » if err != nil {
74 » » » return nil, err
75 » » }
76 » » // We actually read the whole file,
77 » » // but this skips alignment padding
78 » » tr.skipUnread()
59 hdr = tr.readHeader() 79 hdr = tr.readHeader()
80 mergePAX(hdr, headers)
81 return hdr, nil
82 case TypeGNULongName:
83 // We have a GNU long name header. Its contents are the real fil e name.
84 realname, err := ioutil.ReadAll(tr)
85 if err != nil {
86 return nil, err
87 }
88 hdr, err := tr.Next()
89 hdr.Name = cString(realname)
90 return hdr, err
91 case TypeGNULongLink:
92 // We have a GNU long link header.
93 realname, err := ioutil.ReadAll(tr)
94 if err != nil {
95 return nil, err
96 }
97 hdr, err := tr.Next()
98 hdr.Linkname = cString(realname)
99 return hdr, err
60 } 100 }
61 return hdr, tr.err 101 return hdr, tr.err
62 } 102 }
63 103
64 // Parse bytes as a NUL-terminated C-style string. 104 // mergePAX merges well known headers according to PAX standard.
105 // In general headers with the same name as those found
106 // in the header struct overwrite those found in the header
107 // struct with higher precision or longer values. Esp. useful
108 // for name and linkname fields.
109 func mergePAX(hdr *Header, headers map[string]string) error {
110 » for k, v := range headers {
111 » » switch k {
112 » » case "path":
113 » » » hdr.Name = v
114 » » case "linkpath":
115 » » » hdr.Linkname = v
116 » » case "gname":
117 » » » hdr.Gname = v
118 » » case "uname":
119 » » » hdr.Uname = v
120 » » case "uid":
121 » » » uid, err := strconv.ParseInt(v, 10, 0)
122 » » » if err != nil {
123 » » » » return err
124 » » » }
125 » » » hdr.Uid = int(uid)
126 » » case "gid":
127 » » » gid, err := strconv.ParseInt(v, 10, 0)
128 » » » if err != nil {
129 » » » » return err
130 » » » }
131 » » » hdr.Gid = int(gid)
132 » » case "atime":
133 » » » t, err := parsePAXTime(v)
134 » » » if err != nil {
135 » » » » return err
136 » » » }
137 » » » hdr.AccessTime = t
138 » » case "mtime":
139 » » » t, err := parsePAXTime(v)
140 » » » if err != nil {
141 » » » » return err
142 » » » }
143 » » » hdr.ModTime = t
144 » » case "ctime":
145 » » » t, err := parsePAXTime(v)
146 » » » if err != nil {
147 » » » » return err
148 » » » }
149 » » » hdr.ChangeTime = t
150 » » case "size":
151 » » » size, err := strconv.ParseInt(v, 10, 0)
152 » » » if err != nil {
153 » » » » return err
154 » » » }
155 » » » hdr.Size = int64(size)
156 » » }
157
158 » }
159 » return nil
160 }
161
162 // parsePAXTime takes a string of the form %d.%d as described in
163 // the PAX specification.
164 func parsePAXTime(t string) (time.Time, error) {
165 » buf := []byte(t)
166 » pos := bytes.IndexByte(buf, '.')
167 » var seconds, nanoseconds int64
168 » var err error
169 » if pos == -1 {
170 » » seconds, err = strconv.ParseInt(t, 10, 0)
171 » » if err != nil {
172 » » » return time.Time{}, err
173 » » }
174 » } else {
175 » » seconds, err = strconv.ParseInt(string(buf[:pos]), 10, 0)
176 » » if err != nil {
177 » » » return time.Time{}, err
178 » » }
179 » » nano_buf := string(buf[pos+1:])
180 » » // Pad as needed before converting to a decimal.
181 » » // For example .030 -> .030000000 -> 30000000 nanoseconds
182 » » if len(nano_buf) < maxNanoSecondIntSize {
183 » » » // Right pad
184 » » » nano_buf += strings.Repeat("0", maxNanoSecondIntSize-len (nano_buf))
185 » » } else if len(nano_buf) > maxNanoSecondIntSize {
186 » » » // Right truncate
187 » » » nano_buf = nano_buf[:maxNanoSecondIntSize]
188 » » }
189 » » nanoseconds, err = strconv.ParseInt(string(nano_buf), 10, 0)
190 » » if err != nil {
191 » » » return time.Time{}, err
192 » » }
193 » }
194 » ts := time.Unix(seconds, nanoseconds)
195 » return ts, nil
196 }
197
198 // parsePAX parses PAX headers.
199 // If an extended header (type 'x') is invalid, ErrHeader is returned
200 func parsePAX(r io.Reader) (map[string]string, error) {
201 » buf, err := ioutil.ReadAll(r)
202 » if err != nil {
203 » » return nil, err
204 » }
205 » headers := make(map[string]string)
206 » // Each record is constructed as
207 » // "%d %s=%s\n", length, keyword, value
208 » for len(buf) > 0 {
209 » » // or the header was empty to start with.
210 » » var sp int
211 » » // The size field ends at the first space.
212 » » sp = bytes.IndexByte(buf, ' ')
213 » » if sp == -1 {
214 » » » return nil, ErrHeader
215 » » }
216 » » // Parse the first token as a decimal integer.
217 » » n, err := strconv.ParseInt(string(buf[:sp]), 10, 0)
218 » » if err != nil {
219 » » » return nil, ErrHeader
220 » » }
221 » » // Extract everything between the decimal and the n -1 on the
222 » » // beginning to to eat the ' ', -1 on the end to skip the newlin e.
223 » » var record []byte
224 » » record, buf = buf[sp+1:n-1], buf[n:]
225 » » // The first equals is guaranteed to mark the end of the key.
226 » » // Everything else is value.
227 » » eq := bytes.IndexByte(record, '=')
228 » » if eq == -1 {
229 » » » return nil, ErrHeader
230 » » }
231 » » key, value := record[:eq], record[eq+1:]
232 » » headers[string(key)] = string(value)
233 » }
234 » return headers, nil
235 }
236
237 // cString parses bytes as a NUL-terminated C-style string.
65 // If a NUL byte is not found then the whole slice is returned as a string. 238 // If a NUL byte is not found then the whole slice is returned as a string.
66 func cString(b []byte) string { 239 func cString(b []byte) string {
67 n := 0 240 n := 0
68 for n < len(b) && b[n] != 0 { 241 for n < len(b) && b[n] != 0 {
69 n++ 242 n++
70 } 243 }
71 return string(b[0:n]) 244 return string(b[0:n])
72 } 245 }
73 246
74 func (tr *Reader) octal(b []byte) int64 { 247 func (tr *Reader) octal(b []byte) int64 {
(...skipping 17 matching lines...) Expand all
92 for len(b) > 0 && (b[len(b)-1] == ' ' || b[len(b)-1] == '\x00') { 265 for len(b) > 0 && (b[len(b)-1] == ' ' || b[len(b)-1] == '\x00') {
93 b = b[0 : len(b)-1] 266 b = b[0 : len(b)-1]
94 } 267 }
95 x, err := strconv.ParseUint(cString(b), 8, 64) 268 x, err := strconv.ParseUint(cString(b), 8, 64)
96 if err != nil { 269 if err != nil {
97 tr.err = err 270 tr.err = err
98 } 271 }
99 return int64(x) 272 return int64(x)
100 } 273 }
101 274
102 // Skip any unread bytes in the existing file entry, as well as any alignment pa dding. 275 // skipUnread skips any unread bytes in the existing file entry, as well as any alignment padding.
103 func (tr *Reader) skipUnread() { 276 func (tr *Reader) skipUnread() {
104 nr := tr.nb + tr.pad // number of bytes to skip 277 nr := tr.nb + tr.pad // number of bytes to skip
105 tr.nb, tr.pad = 0, 0 278 tr.nb, tr.pad = 0, 0
106 if sr, ok := tr.r.(io.Seeker); ok { 279 if sr, ok := tr.r.(io.Seeker); ok {
107 if _, err := sr.Seek(nr, os.SEEK_CUR); err == nil { 280 if _, err := sr.Seek(nr, os.SEEK_CUR); err == nil {
108 return 281 return
109 } 282 }
110 } 283 }
111 _, tr.err = io.CopyN(ioutil.Discard, tr.r, nr) 284 _, tr.err = io.CopyN(ioutil.Discard, tr.r, nr)
112 } 285 }
(...skipping 113 matching lines...) Expand 10 before | Expand all | Expand 10 after
226 } 399 }
227 n, err = tr.r.Read(b) 400 n, err = tr.r.Read(b)
228 tr.nb -= int64(n) 401 tr.nb -= int64(n)
229 402
230 if err == io.EOF && tr.nb > 0 { 403 if err == io.EOF && tr.nb > 0 {
231 err = io.ErrUnexpectedEOF 404 err = io.ErrUnexpectedEOF
232 } 405 }
233 tr.err = err 406 tr.err = err
234 return 407 return
235 } 408 }
LEFTRIGHT

Powered by Google App Engine
RSS Feeds Recent Issues | This issue
This is Rietveld f62528b