LEFT | RIGHT |
(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 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 » » // 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 Loading... |
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 Loading... |
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 } |
LEFT | RIGHT |