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

Side by Side Diff: src/pkg/archive/tar/reader.go

Issue 6700047: code review 6700047: archive/tar: read/write extended pax/gnu tar archives (Closed)
Patch Set: diff -r 39b31d81b947 https://code.google.com/p/go Created 11 years, 5 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:
View unified diff | Download patch
« no previous file with comments | « src/pkg/archive/tar/common.go ('k') | src/pkg/archive/tar/reader_test.go » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 )
23 24
25 const maxNanoSecondIntSize = 9
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()
33 // if err == io.EOF { 36 // if err == io.EOF {
(...skipping 14 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 » » // We have a PAX extended header. See http://pubs.opengroup.org /onlinepubs/009604599/utilities/pax.html
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
104 // Merge well known headers according to PAX standard.
rog 2012/10/16 10:52:03 s/Merge/mergePAX merges/
shanemhansen 2012/10/16 17:09:54 Done.
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 := parseFloatToTime(v)
134 if err != nil {
135 return err
136 }
137 hdr.AccessTime = t
138 case "mtime":
139 t, err := parseFloatToTime(v)
140 if err != nil {
141 return err
142 }
143 hdr.ModTime = t
144 case "ctime":
145 t, err := parseFloatToTime(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 // Take a string of the form %d.%d and convert it to unix standard time. We
rog 2012/10/16 10:52:03 // parseFloatToTime converts a string of the form
shanemhansen 2012/10/16 17:09:54 Done.
163 // try to avoid floating point math by splitting the string at '.' and handling
164 // seconds and nanoseconds as integers. This function exists to avoid
165 // rounding errors when dealing with the formatted output of clock_gettime(3)
166 func parseFloatToTime(t string) (time.Time, error) {
dfc 2012/10/16 04:24:56 http://pubs.opengroup.org/onlinepubs/009604599/uti
shanemhansen 2012/10/16 17:09:54 Done.
167 buf := []byte(t)
168 pos := bytes.IndexByte(buf, '.')
169 var seconds, nanoseconds int64
170 var err error
171 if pos == -1 {
172 seconds, err = strconv.ParseInt(t, 10, 0)
173 if err != nil {
174 return time.Time{}, err
175 }
176 } else {
177 seconds, err = strconv.ParseInt(string(buf[:pos]), 10, 0)
178 if err != nil {
179 return time.Time{}, err
180 }
181 nano_buf := string(buf[pos+1:])
182 // Pad as needed before converting to a decimal.
183 // For example .030 -> .030000000 -> 30000000 nanoseconds
184 if len(nano_buf) < maxNanoSecondIntSize {
185 // Right pad
186 nano_buf += strings.Repeat("0", maxNanoSecondIntSize-len (nano_buf))
187 } else if len(nano_buf) > maxNanoSecondIntSize {
188 // Right truncate
189 nano_buf = nano_buf[:maxNanoSecondIntSize]
190 }
191 nanoseconds, err = strconv.ParseInt(string(buf[pos+1:]), 10, 0)
192 if err != nil {
193 return time.Time{}, err
194 }
195 }
196 ts := time.Unix(seconds, nanoseconds)
197 return ts, nil
198 }
199
200 // Parse PAX headers from tr following the rules outlined
rog 2012/10/16 10:52:03 // parsePax parses PAX headers from tr ... etc
shanemhansen 2012/10/16 17:09:54 Done.
201 // in http://pubs.opengroup.org/onlinepubs/9699919799/utilities/pax.html#tag_20_ 92_13_03
202 // If an extended header (type 'x') is invalid, ErrHeader is returned
203 func parsePAX(tr io.Reader) (map[string]string, error) {
204 buf, err := ioutil.ReadAll(tr)
205 if err != nil {
206 return nil, err
207 }
208 headers := make(map[string]string)
209 // We are looking for records of the type:
210 // 1024 foo=bar such that len('1024 foo=bar')==1024
211 for {
212 // Bail early if we've consumed the whole buffer
213 // or the header was empty to start with.
214 if len(buf) == 0 {
215 break
216 }
217 var pos int
218 // The size field ends at the first space.
219 pos = bytes.IndexByte(buf, ' ')
220 if pos == -1 {
221 return nil, ErrHeader
222 }
223 // Parse the first token as a decimal integer.
224 length, err := strconv.ParseInt(string(buf[:pos]), 10, 0)
225 if err != nil {
226 return nil, ErrHeader
227 }
228 // Extract everything between the decimal and the length -1 on t he
229 // beginning to to eat the ' ', -1 on the end to skip the newlin e.
230 record := buf[pos+1 : int(length)-1]
231 // The first equals is guaranteed to mark the end of the key.
232 // Everything else is value.
233 pos = bytes.IndexByte(record, '=')
234 if pos == -1 {
235 return nil, ErrHeader
236 }
237 key := record[:pos]
238 value := record[pos+1:]
239 headers[string(key)] = string(value)
240 buf = buf[length:]
241 }
242 return headers, nil
243 }
244
64 // Parse bytes as a NUL-terminated C-style string. 245 // Parse bytes as a NUL-terminated C-style string.
rog 2012/10/16 10:52:03 // cString parses ...
shanemhansen 2012/10/16 17:09:54 Done.
65 // If a NUL byte is not found then the whole slice is returned as a string. 246 // If a NUL byte is not found then the whole slice is returned as a string.
66 func cString(b []byte) string { 247 func cString(b []byte) string {
67 n := 0 248 n := 0
68 for n < len(b) && b[n] != 0 { 249 for n < len(b) && b[n] != 0 {
69 n++ 250 n++
70 } 251 }
71 return string(b[0:n]) 252 return string(b[0:n])
72 } 253 }
73 254
74 func (tr *Reader) octal(b []byte) int64 { 255 func (tr *Reader) octal(b []byte) int64 {
(...skipping 139 matching lines...) Expand 10 before | Expand all | Expand 10 after
214 } 395 }
215 n, err = tr.r.Read(b) 396 n, err = tr.r.Read(b)
216 tr.nb -= int64(n) 397 tr.nb -= int64(n)
217 398
218 if err == io.EOF && tr.nb > 0 { 399 if err == io.EOF && tr.nb > 0 {
219 err = io.ErrUnexpectedEOF 400 err = io.ErrUnexpectedEOF
220 } 401 }
221 tr.err = err 402 tr.err = err
222 return 403 return
223 } 404 }
OLDNEW
« no previous file with comments | « src/pkg/archive/tar/common.go ('k') | src/pkg/archive/tar/reader_test.go » ('j') | no next file with comments »

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