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 if strings.Trim(sn, "0123456789") != "" {
217 return time.Time{}, ErrHeader
218 }
219 if len(sn) < maxNanoSecondDigits {
220 sn += strings.Repeat("0", maxNanoSecondDigits-len(sn))
221 } else {
222 sn = sn[:maxNanoSecondDigits]
223 }
224 nsecs, _ := strconv.ParseInt(sn, 10, 64)
225 if len(ss) > 0 && ss[0] == '-' {
226 return time.Unix(secs, -1*nsecs), nil
227 }
228 return time.Unix(secs, nsecs), nil
229 }
230
231
232
233 func formatPAXTime(ts time.Time) (s string) {
234 secs, nsecs := ts.Unix(), ts.Nanosecond()
235 if nsecs == 0 {
236 return strconv.FormatInt(secs, 10)
237 }
238
239
240 sign := ""
241 if secs < 0 {
242 sign = "-"
243 secs = -(secs + 1)
244 nsecs = -(nsecs - 1e9)
245 }
246 return strings.TrimRight(fmt.Sprintf("%s%d.%09d", sign, secs, nsecs), "0")
247 }
248
249
250
251
252 func parsePAXRecord(s string) (k, v, r string, err error) {
253
254 nStr, rest, ok := strings.Cut(s, " ")
255 if !ok {
256 return "", "", s, ErrHeader
257 }
258
259
260 n, perr := strconv.ParseInt(nStr, 10, 0)
261 if perr != nil || n < 5 || n > int64(len(s)) {
262 return "", "", s, ErrHeader
263 }
264 n -= int64(len(nStr) + 1)
265 if n <= 0 {
266 return "", "", s, ErrHeader
267 }
268
269
270 rec, nl, rem := rest[:n-1], rest[n-1:n], rest[n:]
271 if nl != "\n" {
272 return "", "", s, ErrHeader
273 }
274
275
276 k, v, ok = strings.Cut(rec, "=")
277 if !ok {
278 return "", "", s, ErrHeader
279 }
280
281 if !validPAXRecord(k, v) {
282 return "", "", s, ErrHeader
283 }
284 return k, v, rem, nil
285 }
286
287
288
289 func formatPAXRecord(k, v string) (string, error) {
290 if !validPAXRecord(k, v) {
291 return "", ErrHeader
292 }
293
294 const padding = 3
295 size := len(k) + len(v) + padding
296 size += len(strconv.Itoa(size))
297 record := strconv.Itoa(size) + " " + k + "=" + v + "\n"
298
299
300 if len(record) != size {
301 size = len(record)
302 record = strconv.Itoa(size) + " " + k + "=" + v + "\n"
303 }
304 return record, nil
305 }
306
307
308
309
310
311
312
313
314
315
316
317 func validPAXRecord(k, v string) bool {
318 if k == "" || strings.Contains(k, "=") {
319 return false
320 }
321 switch k {
322 case paxPath, paxLinkpath, paxUname, paxGname:
323 return !hasNUL(v)
324 default:
325 return !hasNUL(k)
326 }
327 }
328
View as plain text