1
2
3
4
5 package tar
6
7 import (
8 "bytes"
9 "fmt"
10 "strconv"
11 "strings"
12 "time"
13 )
14
15
16 func hasNUL(s string) bool {
17 return strings.Contains(s, "\x00")
18 }
19
20
21 func isASCII(s string) bool {
22 for _, c := range s {
23 if c >= 0x80 || c == 0x00 {
24 return false
25 }
26 }
27 return true
28 }
29
30
31
32 func toASCII(s string) string {
33 if isASCII(s) {
34 return s
35 }
36 b := make([]byte, 0, len(s))
37 for _, c := range s {
38 if c < 0x80 && c != 0x00 {
39 b = append(b, byte(c))
40 }
41 }
42 return string(b)
43 }
44
45 type parser struct {
46 err error
47 }
48
49 type formatter struct {
50 err error
51 }
52
53
54
55 func (*parser) parseString(b []byte) string {
56 if i := bytes.IndexByte(b, 0); i >= 0 {
57 return string(b[:i])
58 }
59 return string(b)
60 }
61
62
63 func (f *formatter) formatString(b []byte, s string) {
64 if len(s) > len(b) {
65 f.err = ErrFieldTooLong
66 }
67 copy(b, s)
68 if len(s) < len(b) {
69 b[len(s)] = 0
70 }
71
72
73
74
75 if len(s) > len(b) && b[len(b)-1] == '/' {
76 n := len(strings.TrimRight(s[:len(b)-1], "/"))
77 b[n] = 0
78 }
79 }
80
81
82
83
84
85
86
87
88 func fitsInBase256(n int, x int64) bool {
89 binBits := uint(n-1) * 8
90 return n >= 9 || (x >= -1<<binBits && x < 1<<binBits)
91 }
92
93
94
95
96 func (p *parser) parseNumeric(b []byte) int64 {
97
98
99
100 if len(b) > 0 && b[0]&0x80 != 0 {
101
102
103
104
105
106 var inv byte
107 if b[0]&0x40 != 0 {
108 inv = 0xff
109 }
110
111 var x uint64
112 for i, c := range b {
113 c ^= inv
114 if i == 0 {
115 c &= 0x7f
116 }
117 if (x >> 56) > 0 {
118 p.err = ErrHeader
119 return 0
120 }
121 x = x<<8 | uint64(c)
122 }
123 if (x >> 63) > 0 {
124 p.err = ErrHeader
125 return 0
126 }
127 if inv == 0xff {
128 return ^int64(x)
129 }
130 return int64(x)
131 }
132
133
134 return p.parseOctal(b)
135 }
136
137
138
139 func (f *formatter) formatNumeric(b []byte, x int64) {
140 if fitsInOctal(len(b), x) {
141 f.formatOctal(b, x)
142 return
143 }
144
145 if fitsInBase256(len(b), x) {
146 for i := len(b) - 1; i >= 0; i-- {
147 b[i] = byte(x)
148 x >>= 8
149 }
150 b[0] |= 0x80
151 return
152 }
153
154 f.formatOctal(b, 0)
155 f.err = ErrFieldTooLong
156 }
157
158 func (p *parser) parseOctal(b []byte) int64 {
159
160
161
162
163
164 b = bytes.Trim(b, " \x00")
165
166 if len(b) == 0 {
167 return 0
168 }
169 x, perr := strconv.ParseUint(p.parseString(b), 8, 64)
170 if perr != nil {
171 p.err = ErrHeader
172 }
173 return int64(x)
174 }
175
176 func (f *formatter) formatOctal(b []byte, x int64) {
177 if !fitsInOctal(len(b), x) {
178 x = 0
179 f.err = ErrFieldTooLong
180 }
181
182 s := strconv.FormatInt(x, 8)
183
184 if n := len(b) - len(s) - 1; n > 0 {
185 s = strings.Repeat("0", n) + s
186 }
187 f.formatString(b, s)
188 }
189
190
191
192 func fitsInOctal(n int, x int64) bool {
193 octBits := uint(n-1) * 3
194 return x >= 0 && (n >= 22 || x < 1<<octBits)
195 }
196
197
198
199
200 func parsePAXTime(s string) (time.Time, error) {
201 const maxNanoSecondDigits = 9
202
203
204 ss, sn, _ := strings.Cut(s, ".")
205
206
207 secs, err := strconv.ParseInt(ss, 10, 64)
208 if err != nil {
209 return time.Time{}, ErrHeader
210 }
211 if len(sn) == 0 {
212 return time.Unix(secs, 0), nil
213 }
214
215
216
217 nanoDigits := [maxNanoSecondDigits]byte{'0', '0', '0', '0', '0', '0', '0', '0', '0'}
218 for i := range len(sn) {
219 switch c := sn[i]; {
220 case c < '0' || c > '9':
221 return time.Time{}, ErrHeader
222 case i < len(nanoDigits):
223 nanoDigits[i] = c
224 }
225 }
226 nsecs, _ := strconv.ParseInt(string(nanoDigits[:]), 10, 64)
227 if len(ss) > 0 && ss[0] == '-' {
228 return time.Unix(secs, -1*nsecs), nil
229 }
230 return time.Unix(secs, nsecs), nil
231 }
232
233
234
235 func formatPAXTime(ts time.Time) (s string) {
236 secs, nsecs := ts.Unix(), ts.Nanosecond()
237 if nsecs == 0 {
238 return strconv.FormatInt(secs, 10)
239 }
240
241
242 sign := ""
243 if secs < 0 {
244 sign = "-"
245 secs = -(secs + 1)
246 nsecs = -(nsecs - 1e9)
247 }
248 return strings.TrimRight(fmt.Sprintf("%s%d.%09d", sign, secs, nsecs), "0")
249 }
250
251
252
253
254 func parsePAXRecord(s string) (k, v, r string, err error) {
255
256 nStr, rest, ok := strings.Cut(s, " ")
257 if !ok {
258 return "", "", s, ErrHeader
259 }
260
261
262 n, perr := strconv.ParseInt(nStr, 10, 0)
263 if perr != nil || n < 5 || n > int64(len(s)) {
264 return "", "", s, ErrHeader
265 }
266 n -= int64(len(nStr) + 1)
267 if n <= 0 {
268 return "", "", s, ErrHeader
269 }
270
271
272 rec, nl, rem := rest[:n-1], rest[n-1:n], rest[n:]
273 if nl != "\n" {
274 return "", "", s, ErrHeader
275 }
276
277
278 k, v, ok = strings.Cut(rec, "=")
279 if !ok {
280 return "", "", s, ErrHeader
281 }
282
283 if !validPAXRecord(k, v) {
284 return "", "", s, ErrHeader
285 }
286 return k, v, rem, nil
287 }
288
289
290
291 func formatPAXRecord(k, v string) (string, error) {
292 if !validPAXRecord(k, v) {
293 return "", ErrHeader
294 }
295
296 const padding = 3
297 size := len(k) + len(v) + padding
298 size += len(strconv.Itoa(size))
299 record := strconv.Itoa(size) + " " + k + "=" + v + "\n"
300
301
302 if len(record) != size {
303 size = len(record)
304 record = strconv.Itoa(size) + " " + k + "=" + v + "\n"
305 }
306 return record, nil
307 }
308
309
310
311
312
313
314
315
316
317
318
319 func validPAXRecord(k, v string) bool {
320 if k == "" || strings.Contains(k, "=") {
321 return false
322 }
323 switch k {
324 case paxPath, paxLinkpath, paxUname, paxGname:
325 return !hasNUL(v)
326 default:
327 return !hasNUL(k)
328 }
329 }
330
View as plain text