Left: | ||
Right: |
OLD | NEW |
---|---|
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 Loading... | |
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 Loading... | |
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 } |
OLD | NEW |