File Coverage

tar.c
Criterion Covered Total %
statement 614 713 86.1
branch 368 554 66.4
condition n/a
subroutine n/a
pod n/a
total 982 1267 77.5


line stmt bran cond sub pod time code
1             /*
2             * tar.c - built-in tar plugin for File::Raw::Archive.
3             *
4             * Reads ustar (POSIX 1988), GNU `././@LongLink` extension for long
5             * filenames / symlink targets, and PAX extended headers (POSIX
6             * 1003.1-2001 typeflags 'x' and 'g'). Writes ustar + GNU @LongLink +
7             * PAX 'x' + optional PAX 'g' header at archive start, with a `format`
8             * option selecting auto / pax / gnu / ustar emission strategy.
9             *
10             * PAX keys handled on read AND write: path, linkpath, size, mtime
11             * (with nanoseconds), atime, uid, gid, uname, gname, plus
12             * SCHILY.xattr.* and LIBARCHIVE.xattr.* (optionally .b64 suffix for
13             * binary values). Sparse-file PAX keys flag the entry but don't
14             * reconstruct.
15             */
16              
17             #define PERL_NO_GET_CONTEXT
18             #include "EXTERN.h"
19             #include "perl.h"
20             #include "XSUB.h"
21              
22             #include "archive_plugin.h"
23             #include "arch_io.h"
24             #include "tar.h"
25              
26             #include
27             #include
28             #include
29             #include
30             #include
31             #include
32              
33             /* ============================================================
34             * Octal / numeric helpers
35             * ============================================================ */
36              
37             /* Parse a NUL- or space-terminated octal field of `n` bytes. Honours
38             * the GNU base-256 extension when bit 7 of the first byte is set
39             * (used for size > 8 GiB, mtime, uid, etc.). */
40             static uint64_t
41 5033           tar_parse_numeric(const char *field, size_t n)
42             {
43 5033 50         if (n == 0) return 0;
44 5033 100         if ((unsigned char)field[0] & 0x80) {
45             /* Base-256: first byte's bit 7 is the marker (and bit 6 is
46             * the sign bit; we treat negative as 0). */
47 4           uint64_t v = 0;
48             size_t i;
49 4 50         if ((unsigned char)field[0] & 0x40) return 0; /* negative */
50 4           v = (unsigned char)field[0] & 0x3f;
51 32 100         for (i = 1; i < n; i++) {
52 28           v = (v << 8) | (unsigned char)field[i];
53             }
54 4           return v;
55             }
56             /* Octal ASCII, ignoring leading spaces and trailing spaces/NUL. */
57 5029           uint64_t v = 0;
58 5029           size_t i = 0;
59 37567 50         while (i < n && (field[i] == ' ' || field[i] == '0')) i++;
    100          
    100          
60             /* If we ate everything as zeros/spaces, value is 0 (handles all-zero fields). */
61 13554 50         while (i < n && field[i] >= '0' && field[i] <= '7') {
    100          
    100          
62 8525           v = (v << 3) | (uint64_t)(field[i] - '0');
63 8525           i++;
64             }
65 5029           return v;
66             }
67              
68             /* Emit an octal value into a fixed-width field, NUL-terminated.
69             * For values that don't fit, emit base-256 with the high bit set on
70             * the first byte. */
71             static void
72 3410           tar_emit_numeric(char *field, size_t n, uint64_t v)
73             {
74             /* Try octal first. Field width n means n-1 digits (last byte NUL). */
75 3410           int fits_octal = 1;
76 3410           uint64_t check = v;
77 3410           size_t digits = 0;
78 3410 100         if (check == 0) digits = 1;
79 6998 100         while (check) { digits++; check >>= 3; }
80 3410 100         if (digits > n - 1) fits_octal = 0;
81              
82 3410 100         if (fits_octal) {
83             size_t i;
84 3406           field[n - 1] = '\0';
85 32752 100         for (i = n - 1; i > 0; i--) {
86 29346           field[i - 1] = (char)('0' + (v & 7));
87 29346           v >>= 3;
88             }
89             } else {
90             /* Base-256, big-endian, with high bit set on first byte. */
91             size_t i;
92 36 100         for (i = n; i > 0; i--) {
93 32           field[i - 1] = (char)(v & 0xff);
94 32           v >>= 8;
95             }
96 4           field[0] |= 0x80;
97             }
98 3410           }
99              
100             /* Compute the standard tar header checksum: sum of all bytes treating
101             * the chksum field itself as eight spaces. */
102             static uint32_t
103 1539           tar_compute_checksum(const tar_header_t *h)
104             {
105 1539           const unsigned char *p = (const unsigned char *)h;
106 1539           uint32_t sum = 0;
107             size_t i;
108 789507 100         for (i = 0; i < TAR_BLOCK_SIZE; i++) {
109 787968 100         if (i >= offsetof(tar_header_t, chksum) &&
    100          
110             i < offsetof(tar_header_t, chksum) + sizeof h->chksum) {
111 12312           sum += ' ';
112             } else {
113 775656           sum += p[i];
114             }
115             }
116 1539           return sum;
117             }
118              
119             static int
120 887           tar_block_is_zero(const char *block)
121             {
122             size_t i;
123 19319 100         for (i = 0; i < TAR_BLOCK_SIZE; i++) if (block[i]) return 0;
    100          
124 36           return 1;
125             }
126              
127             /* ============================================================
128             * PAX record parsing / emission
129             * ============================================================ */
130              
131             typedef struct pax_kv {
132             char *key;
133             char *value;
134             size_t value_len;
135             struct pax_kv *next;
136             } pax_kv_t;
137              
138             static void
139 980           pax_kv_free(pax_kv_t *list)
140             {
141 1015 100         while (list) {
142 35           pax_kv_t *n = list->next;
143 35           free(list->key);
144 35           free(list->value);
145 35           free(list);
146 35           list = n;
147             }
148 980           }
149              
150             static void
151 35           pax_kv_set(pax_kv_t **list, const char *key, const char *value, size_t value_len)
152             {
153             pax_kv_t *cur;
154 83 100         for (cur = *list; cur; cur = cur->next) {
155 48 50         if (strcmp(cur->key, key) == 0) {
156 0           free(cur->value);
157 0           cur->value = (char *)malloc(value_len + 1);
158 0           memcpy(cur->value, value, value_len);
159 0           cur->value[value_len] = '\0';
160 0           cur->value_len = value_len;
161 0           return;
162             }
163             }
164 35           pax_kv_t *fresh = (pax_kv_t *)calloc(1, sizeof *fresh);
165 35           fresh->key = strdup(key);
166 35           fresh->value = (char *)malloc(value_len + 1);
167 35           memcpy(fresh->value, value, value_len);
168 35           fresh->value[value_len] = '\0';
169 35           fresh->value_len = value_len;
170 35           fresh->next = *list;
171 35           *list = fresh;
172             }
173              
174             static const pax_kv_t *
175 0           pax_kv_find(const pax_kv_t *list, const char *key)
176             {
177 0 0         while (list) {
178 0 0         if (strcmp(list->key, key) == 0) return list;
179 0           list = list->next;
180             }
181 0           return NULL;
182             }
183              
184             /* Parse a buffer of length `len` as a sequence of PAX records:
185             * " =\n"
186             * where is the ASCII byte length of the whole record. */
187             static int
188 14           pax_parse_records(const char *buf, size_t len, pax_kv_t **out)
189             {
190 14           size_t pos = 0;
191 49 100         while (pos < len) {
192 35           size_t L = 0;
193 35           size_t lstart = pos;
194 101 50         while (pos < len && buf[pos] >= '0' && buf[pos] <= '9') {
    100          
    50          
195 66           L = L * 10 + (buf[pos] - '0');
196 66           pos++;
197             }
198 35 50         if (pos == lstart || pos >= len || buf[pos] != ' ') return -1;
    50          
    50          
199 35           pos++; /* skip space */
200 35 50         if (lstart + L > len) return -1;
201             /* The record minus the digits + space is "kw=value\n" */
202 35           size_t rec_inner_end = lstart + L - 1; /* position of '\n' */
203 35 50         if (rec_inner_end >= len || buf[rec_inner_end] != '\n') return -1;
    50          
204 35           const char *eq = (const char *)memchr(buf + pos, '=', rec_inner_end - pos);
205 35 50         if (!eq) return -1;
206 35           size_t key_len = eq - (buf + pos);
207 35           size_t value_len = rec_inner_end - (eq - buf) - 1;
208 35           char *key = (char *)malloc(key_len + 1);
209 35           memcpy(key, buf + pos, key_len);
210 35           key[key_len] = '\0';
211             /* pax_kv_set duplicates value; we'll free key after. */
212 35           pax_kv_set(out, key, eq + 1, value_len);
213 35           free(key);
214 35           pos = lstart + L;
215             }
216 14           return 0;
217             }
218              
219             /* Build one PAX record into `out_buf` (caller-provided, must be big
220             * enough). Returns the byte length. The fixed-point challenge: the
221             * leading L includes its own digit count. */
222             static size_t
223 33           pax_build_record(char *out_buf, const char *key, const char *value, size_t value_len)
224             {
225 33           size_t key_len = strlen(key);
226             /* Inner = key + '=' + value + '\n' */
227 33           size_t inner = key_len + 1 + value_len + 1;
228             /* L is a positive integer; its digit count + 1 (for the space) + inner = total. */
229 33           size_t L = inner + 2; /* start with 1-digit guess */
230 26           for (;;) {
231             char tmp[32];
232 59           int dlen = snprintf(tmp, sizeof tmp, "%zu", L);
233 59           size_t total = (size_t)dlen + 1 + inner;
234 59 100         if (total == L) break;
235 26           L = total;
236             }
237 33           int dlen = snprintf(out_buf, 32, "%zu ", L);
238 33           memcpy(out_buf + dlen, key, key_len);
239 33           out_buf[dlen + key_len] = '=';
240 33           memcpy(out_buf + dlen + key_len + 1, value, value_len);
241 33           out_buf[L - 1] = '\n';
242 33           return L;
243             }
244              
245             /* ============================================================
246             * Base64 (for SCHILY.xattr.*.b64 binary values)
247             * ============================================================ */
248              
249             static const char b64_alphabet[] =
250             "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
251              
252             static int
253 24           b64_decode_byte(int c)
254             {
255 24 100         if (c >= 'A' && c <= 'Z') return c - 'A';
    100          
256 10 100         if (c >= 'a' && c <= 'z') return c - 'a' + 26;
    50          
257 4 50         if (c >= '0' && c <= '9') return c - '0' + 52;
    50          
258 0 0         if (c == '+') return 62;
259 0 0         if (c == '/') return 63;
260 0           return -1;
261             }
262              
263             /* malloc-allocates *out; caller frees. Returns 0 on success, -1 on
264             * malformed input. */
265             static int
266 2           b64_decode(const char *in, size_t in_len, char **out, size_t *out_len)
267             {
268 2           size_t cap = (in_len / 4) * 3 + 4;
269 2           char *buf = (char *)malloc(cap);
270 2 50         if (!buf) return -1;
271 2           size_t bp = 0;
272 2           int quad[4] = { -1, -1, -1, -1 };
273 2           int qi = 0;
274             size_t i;
275 26 100         for (i = 0; i < in_len; i++) {
276 24           int c = (unsigned char)in[i];
277 24 50         if (c == '=' || c == '\r' || c == '\n' || c == ' ' || c == '\t') {
    50          
    50          
    50          
    50          
278 0 0         if (c == '=') {
279 0           quad[qi++] = -2;
280 0 0         if (qi == 4) goto flush;
281             }
282 0           continue;
283             }
284 24           int v = b64_decode_byte(c);
285 24 50         if (v < 0) { free(buf); return -1; }
286 24           quad[qi++] = v;
287 24 100         if (qi == 4) {
288 6           flush:
289 6 50         if (quad[0] < 0 || quad[1] < 0) { free(buf); return -1; }
    50          
290 6           buf[bp++] = (char)(((quad[0] & 0x3f) << 2) | ((quad[1] & 0x30) >> 4));
291 6 50         if (quad[2] >= 0) {
292 6           buf[bp++] = (char)(((quad[1] & 0x0f) << 4) | ((quad[2] & 0x3c) >> 2));
293 6 50         if (quad[3] >= 0) {
294 6           buf[bp++] = (char)(((quad[2] & 0x03) << 6) | (quad[3] & 0x3f));
295             }
296             }
297 6           qi = 0;
298 6           quad[0] = quad[1] = quad[2] = quad[3] = -1;
299             }
300             }
301 2           *out = buf;
302 2           *out_len = bp;
303 2           return 0;
304             }
305              
306             static char *
307 2           b64_encode(const char *in, size_t in_len)
308             {
309 2           size_t out_len = 4 * ((in_len + 2) / 3);
310 2           char *out = (char *)malloc(out_len + 1);
311 2 50         if (!out) return NULL;
312 2           size_t i, op = 0;
313 8 100         for (i = 0; i + 2 < in_len; i += 3) {
314 6           unsigned a = (unsigned char)in[i];
315 6           unsigned b = (unsigned char)in[i+1];
316 6           unsigned c = (unsigned char)in[i+2];
317 6           out[op++] = b64_alphabet[a >> 2];
318 6           out[op++] = b64_alphabet[((a & 3) << 4) | (b >> 4)];
319 6           out[op++] = b64_alphabet[((b & 0xf) << 2) | (c >> 6)];
320 6           out[op++] = b64_alphabet[c & 0x3f];
321             }
322 2 50         if (i < in_len) {
323 0           unsigned a = (unsigned char)in[i];
324 0 0         unsigned b = (i+1 < in_len) ? (unsigned char)in[i+1] : 0;
325 0           out[op++] = b64_alphabet[a >> 2];
326 0           out[op++] = b64_alphabet[((a & 3) << 4) | (b >> 4)];
327 0 0         if (i + 1 < in_len) {
328 0           out[op++] = b64_alphabet[(b & 0xf) << 2];
329             } else {
330 0           out[op++] = '=';
331             }
332 0           out[op++] = '=';
333             }
334 2           out[op] = '\0';
335 2           return out;
336             }
337              
338             static int
339 5           needs_b64(const char *value, size_t value_len)
340             {
341             size_t i;
342 20 100         for (i = 0; i < value_len; i++) {
343 17           unsigned c = (unsigned char)value[i];
344 17 100         if (c == 0 || c == '\n' || c < 0x20 || c > 0x7e) return 1;
    50          
    50          
    50          
345             }
346 3           return 0;
347             }
348              
349             /* ============================================================
350             * Cursor state
351             * ============================================================ */
352              
353             typedef struct {
354             archive_pull_fn pull;
355             void *src;
356             archive_push_fn push;
357             void *sink;
358              
359             pax_kv_t *global_kv;
360             pax_kv_t *next_kv; /* per-entry 'x' header, applied to next regular entry */
361              
362             /* read-side: pending bytes of the current entry payload */
363             uint64_t entry_remaining;
364             uint64_t entry_pad; /* bytes of zero padding still to consume after entry */
365              
366             /* read-side: storage for the current entry's strings/xattrs.
367             * Lifetime: until the next read_next call. */
368             char *cur_name;
369             char *cur_link;
370             ArchiveXattr *cur_xattrs;
371             size_t cur_xattr_count;
372             size_t cur_xattr_cap;
373             int sparse_warning_emitted;
374              
375             /* write-side: chosen format and counters */
376             int format; /* 0=auto 1=pax 2=gnu 3=ustar */
377             int level; /* gzip level for gz sink (informational) */
378             /* PAX header sequence number for the conventional name. */
379             int pax_header_seq;
380             } tar_cursor_t;
381              
382             #define FMT_AUTO 0
383             #define FMT_PAX 1
384             #define FMT_GNU 2
385             #define FMT_USTAR 3
386              
387             static int
388 2889           read_full(archive_pull_fn pull, void *src, char *buf, size_t len)
389             {
390 2889           size_t got = 0;
391 5777 100         while (got < len) {
392 2890           int n = pull(src, buf + got, len - got);
393 2890 100         if (n < 0) return -1;
394 2889 100         if (n == 0) return (int)got;
395 2888           got += (size_t)n;
396             }
397 2887           return (int)got;
398             }
399              
400             static int
401 2081           write_full(archive_push_fn push, void *sink, const char *buf, size_t len)
402             {
403 2081           size_t put = 0;
404 4162 100         while (put < len) {
405 2081           int n = push(sink, buf + put, len - put);
406 2081 50         if (n < 0) return -1;
407 2081 50         if (n == 0) return -1;
408 2081           put += (size_t)n;
409             }
410 2081           return 0;
411             }
412              
413             static void
414 941           free_cur_buffers(tar_cursor_t *c)
415             {
416 941           free(c->cur_name); c->cur_name = NULL;
417 941           free(c->cur_link); c->cur_link = NULL;
418 941 100         if (c->cur_xattrs) {
419             size_t i;
420 8 100         for (i = 0; i < c->cur_xattr_count; i++) {
421 5           free((void *)c->cur_xattrs[i].key);
422 5           free((void *)c->cur_xattrs[i].value);
423             }
424 3           free(c->cur_xattrs);
425             }
426 941           c->cur_xattrs = NULL;
427 941           c->cur_xattr_count = 0;
428 941           c->cur_xattr_cap = 0;
429 941           }
430              
431             /* ============================================================
432             * Read side
433             * ============================================================ */
434              
435             static int
436 69           tar_read_open(pTHX_ const ArchivePlugin *self, archive_pull_fn pull,
437             void *src, HV *opts, void **out_cursor)
438             {
439             PERL_UNUSED_ARG(self);
440             PERL_UNUSED_ARG(opts);
441 69           tar_cursor_t *c = (tar_cursor_t *)calloc(1, sizeof *c);
442 69 50         if (!c) return -1;
443 69           c->pull = pull;
444 69           c->src = src;
445 69           *out_cursor = c;
446 69           return 0;
447             }
448              
449             /* Skip the trailing zero padding of the previous entry, if any. */
450             static int
451 872           consume_pending_padding(tar_cursor_t *c)
452             {
453             char buf[TAR_BLOCK_SIZE];
454 872 50         while (c->entry_remaining > 0 || c->entry_pad > 0) {
    50          
455 0 0         if (c->entry_remaining > 0) {
456 0           size_t take = c->entry_remaining > sizeof buf ? sizeof buf : (size_t)c->entry_remaining;
457 0           int n = read_full(c->pull, c->src, buf, take);
458 0 0         if (n < (int)take) return -1;
459 0           c->entry_remaining -= take;
460             }
461 0 0         if (c->entry_remaining == 0 && c->entry_pad > 0) {
    0          
462 0           size_t take = c->entry_pad > sizeof buf ? sizeof buf : (size_t)c->entry_pad;
463 0           int n = read_full(c->pull, c->src, buf, take);
464 0 0         if (n < (int)take) return -1;
465 0           c->entry_pad -= take;
466             }
467             }
468 872           return 0;
469             }
470              
471             /* Read a tar header block, return:
472             * 1 = got a valid header (in *h), 0 = end of archive, -1 = error */
473             static int
474 889           read_one_header(tar_cursor_t *c, tar_header_t *h)
475             {
476             char buf[TAR_BLOCK_SIZE];
477 889           int n = read_full(c->pull, c->src, buf, TAR_BLOCK_SIZE);
478 889 100         if (n < 0) return -1;
479 888 50         if (n == 0) return 0; /* clean EOF */
480 888 100         if (n < TAR_BLOCK_SIZE) return -1;
481 887 100         if (tar_block_is_zero(buf)) {
482             /* First zero block: try one more. Two zero blocks => end-of-archive. */
483             char buf2[TAR_BLOCK_SIZE];
484 36           int n2 = read_full(c->pull, c->src, buf2, TAR_BLOCK_SIZE);
485             (void)n2; /* Either way: end. */
486 36           return 0;
487             }
488 851           memcpy(h, buf, sizeof *h);
489              
490             /* Verify checksum. */
491 851           uint32_t stored = (uint32_t)tar_parse_numeric(h->chksum, sizeof h->chksum);
492 851           uint32_t actual = tar_compute_checksum(h);
493 851 100         if (stored != actual) return -1;
494              
495 850           return 1;
496             }
497              
498             /* Read an entry payload of `size` bytes followed by zero padding into
499             * a malloced buffer. Caller frees. */
500             static int
501 17           read_payload(tar_cursor_t *c, uint64_t size, char **out)
502             {
503 17           *out = NULL;
504 17 50         if (size == 0) {
505 0           *out = (char *)malloc(1);
506 0 0         return *out ? 0 : -1;
507             }
508 17           char *buf = (char *)malloc((size_t)size + 1);
509 17 50         if (!buf) return -1;
510 17           int n = read_full(c->pull, c->src, buf, (size_t)size);
511 17 50         if (n < (int)size) { free(buf); return -1; }
512 17           buf[size] = '\0';
513              
514             /* Skip padding. */
515 17           size_t pad = (TAR_BLOCK_SIZE - ((size_t)size % TAR_BLOCK_SIZE)) % TAR_BLOCK_SIZE;
516 17 50         if (pad > 0) {
517             char pbuf[TAR_BLOCK_SIZE];
518 17           int pn = read_full(c->pull, c->src, pbuf, pad);
519 17 50         if (pn < (int)pad) { free(buf); return -1; }
520             }
521 17           *out = buf;
522 17           return 0;
523             }
524              
525             /* Apply a per-entry PAX kv list onto entry `out`, parsing typed
526             * values. Xattr keys (SCHILY.xattr.* and LIBARCHIVE.xattr.*) accumulate
527             * into c->cur_xattrs[]. */
528             static void
529 16           apply_pax_to_entry(tar_cursor_t *c, const pax_kv_t *list, ArchiveEntry *out)
530             {
531 55 100         while (list) {
532 39           const char *k = list->key;
533 39           const char *v = list->value;
534 39           size_t vlen = list->value_len;
535 39 100         if (strcmp(k, "path") == 0) {
536 5           free(c->cur_name);
537 5           c->cur_name = (char *)malloc(vlen + 1);
538 5           memcpy(c->cur_name, v, vlen);
539 5           c->cur_name[vlen] = '\0';
540 5           out->name = c->cur_name;
541 5           out->name_len = vlen;
542 34 50         } else if (strcmp(k, "linkpath") == 0) {
543 0           free(c->cur_link);
544 0           c->cur_link = (char *)malloc(vlen + 1);
545 0           memcpy(c->cur_link, v, vlen);
546 0           c->cur_link[vlen] = '\0';
547 0           out->link_target = c->cur_link;
548 0           out->link_target_len = vlen;
549 34 100         } else if (strcmp(k, "size") == 0) {
550 5           out->size = strtoull(v, NULL, 10);
551 29 100         } else if (strcmp(k, "mtime") == 0) {
552 6           const char *dot = (const char *)memchr(v, '.', vlen);
553 6           out->mtime = strtoull(v, NULL, 10);
554 6 50         if (dot && (size_t)(dot - v) < vlen - 1) {
    50          
555             /* Parse fractional part into nanoseconds (truncate/pad to 9 digits). */
556 6           uint32_t ns = 0;
557 6           int seen = 0;
558             size_t i;
559 60 100         for (i = (size_t)(dot - v + 1); i < vlen && seen < 9; i++) {
    50          
560 54 50         if (v[i] < '0' || v[i] > '9') break;
    50          
561 54           ns = ns * 10 + (uint32_t)(v[i] - '0');
562 54           seen++;
563             }
564 6 50         while (seen < 9) { ns *= 10; seen++; }
565 6           out->mtime_ns = ns;
566             }
567 23 100         } else if (strcmp(k, "uid") == 0) {
568 5           out->uid = (uint32_t)strtoul(v, NULL, 10);
569 18 100         } else if (strcmp(k, "gid") == 0) {
570 5           out->gid = (uint32_t)strtoul(v, NULL, 10);
571 13 100         } else if (strncmp(k, "SCHILY.xattr.", 13) == 0
572 8 50         || strncmp(k, "LIBARCHIVE.xattr.", 17) == 0) {
573 5 50         size_t prefix_len = (k[0] == 'S') ? 13 : 17;
574 5           const char *xname = k + prefix_len;
575 5           size_t xname_len = strlen(xname);
576 5           int is_b64 = 0;
577 5 50         if (xname_len > 4 && strcmp(xname + xname_len - 4, ".b64") == 0) {
    100          
578 2           is_b64 = 1;
579 2           xname_len -= 4;
580             }
581 5           char *decoded = NULL;
582 5           size_t dec_len = 0;
583 5 100         if (is_b64) {
584 2 50         if (b64_decode(v, vlen, &decoded, &dec_len) < 0) {
585 0           list = list->next;
586 0           continue;
587             }
588             } else {
589 3           decoded = (char *)malloc(vlen + 1);
590 3           memcpy(decoded, v, vlen);
591 3           decoded[vlen] = '\0';
592 3           dec_len = vlen;
593             }
594 5 100         if (c->cur_xattr_count >= c->cur_xattr_cap) {
595 3 50         size_t ncap = c->cur_xattr_cap ? c->cur_xattr_cap * 2 : 4;
596 3           ArchiveXattr *na = (ArchiveXattr *)realloc(c->cur_xattrs, ncap * sizeof(*na));
597 3 50         if (!na) { free(decoded); list = list->next; continue; }
598 3           c->cur_xattrs = na;
599 3           c->cur_xattr_cap = ncap;
600             }
601 5           char *kdup = (char *)malloc(xname_len + 1);
602 5           memcpy(kdup, xname, xname_len);
603 5           kdup[xname_len] = '\0';
604 5           c->cur_xattrs[c->cur_xattr_count].key = kdup;
605 5           c->cur_xattrs[c->cur_xattr_count].key_len = xname_len;
606 5           c->cur_xattrs[c->cur_xattr_count].value = decoded;
607 5           c->cur_xattrs[c->cur_xattr_count].value_len = dec_len;
608 5           c->cur_xattr_count++;
609 8 50         } else if (strncmp(k, "GNU.sparse.", 11) == 0) {
610 0           out->is_sparse = 1;
611 0 0         if (!c->sparse_warning_emitted) {
612             /* Single warning per archive. */
613 0           fprintf(stderr,
614             "File::Raw::Archive: sparse-encoded entries present; "
615             "dense bytes returned verbatim\n");
616 0           c->sparse_warning_emitted = 1;
617             }
618             }
619             /* All other vendor keys silently ignored. */
620 39           list = list->next;
621             }
622 16           }
623              
624             static int
625 833           typeflag_to_ae(char tf)
626             {
627 833           switch (tf) {
628 820           case TF_REGULAR:
629 820           case TF_AREGULAR: return AE_FILE;
630 0           case TF_HARDLINK: return AE_HARDLINK;
631 6           case TF_SYMLINK: return AE_SYMLINK;
632 0           case TF_CHAR: return AE_CHAR;
633 0           case TF_BLOCK: return AE_BLOCK;
634 7           case TF_DIRECTORY: return AE_DIR;
635 0           case TF_FIFO: return AE_FIFO;
636 0           default: return AE_OTHER;
637             }
638             }
639              
640             static int
641 872           tar_read_next(pTHX_ const ArchivePlugin *self, void *cursor, ArchiveEntry *out)
642             {
643             PERL_UNUSED_ARG(self);
644 872           tar_cursor_t *c = (tar_cursor_t *)cursor;
645              
646 872 50         if (consume_pending_padding(c) < 0) return -1;
647 872           free_cur_buffers(c);
648 872           memset(out, 0, sizeof *out);
649              
650             /* Inner loop: peel off PAX / @LongLink prefix blocks until we see
651             * a regular entry. */
652 872           char *long_name = NULL;
653 872           char *long_link = NULL;
654 872           pax_kv_t *per_entry = NULL;
655              
656 17           for (;;) {
657             tar_header_t h;
658 889           int rc = read_one_header(c, &h);
659 1758 100         if (rc < 0) { pax_kv_free(per_entry); free(long_name); free(long_link); return -1; }
660 886 100         if (rc == 0) {
661 36           pax_kv_free(per_entry); free(long_name); free(long_link);
662 36           return 0; /* end of archive */
663             }
664              
665 850           char tf = h.typeflag;
666 850           uint64_t size = tar_parse_numeric(h.size, sizeof h.size);
667              
668 850 100         if (tf == TF_GNU_LONGNAME || tf == TF_GNU_LONGLINK) {
    50          
669             char *payload;
670 3 50         if (read_payload(c, size, &payload) < 0) {
671 0           pax_kv_free(per_entry); free(long_name); free(long_link); return -1;
672             }
673 3 50         if (tf == TF_GNU_LONGNAME) { free(long_name); long_name = payload; }
674 0           else { free(long_link); long_link = payload; }
675 3           continue;
676             }
677 847 100         if (tf == TF_PAX_FILE) {
678             char *payload;
679 12 50         if (read_payload(c, size, &payload) < 0) {
680 0           pax_kv_free(per_entry); free(long_name); free(long_link); return -1;
681             }
682 12           pax_parse_records(payload, (size_t)size, &per_entry);
683 12           free(payload);
684 12           continue;
685             }
686 835 100         if (tf == TF_PAX_GLOBAL) {
687             char *payload;
688 2 50         if (read_payload(c, size, &payload) < 0) {
689 0           pax_kv_free(per_entry); free(long_name); free(long_link); return -1;
690             }
691 2           pax_parse_records(payload, (size_t)size, &c->global_kv);
692 2           free(payload);
693 2           continue;
694             }
695              
696             /* Regular entry. Populate from ustar header first. */
697 833 100         if (long_name) {
698 3           c->cur_name = long_name;
699 3           long_name = NULL;
700 3           out->name = c->cur_name;
701 3           out->name_len = strlen(c->cur_name);
702 830 50         } else if (h.prefix[0]) {
703 0           size_t pl = strnlen(h.prefix, sizeof h.prefix);
704 0           size_t nl = strnlen(h.name, sizeof h.name);
705 0           c->cur_name = (char *)malloc(pl + 1 + nl + 1);
706 0           memcpy(c->cur_name, h.prefix, pl);
707 0           c->cur_name[pl] = '/';
708 0           memcpy(c->cur_name + pl + 1, h.name, nl);
709 0           c->cur_name[pl + 1 + nl] = '\0';
710 0           out->name = c->cur_name;
711 0           out->name_len = pl + 1 + nl;
712             } else {
713 830           size_t nl = strnlen(h.name, sizeof h.name);
714 830           c->cur_name = (char *)malloc(nl + 1);
715 830           memcpy(c->cur_name, h.name, nl);
716 830           c->cur_name[nl] = '\0';
717 830           out->name = c->cur_name;
718 830           out->name_len = nl;
719             }
720              
721 833 50         if (long_link) {
722 0           c->cur_link = long_link;
723 0           long_link = NULL;
724 0           out->link_target = c->cur_link;
725 0           out->link_target_len = strlen(c->cur_link);
726 833 100         } else if (h.linkname[0]) {
727 6           size_t ll = strnlen(h.linkname, sizeof h.linkname);
728 6           c->cur_link = (char *)malloc(ll + 1);
729 6           memcpy(c->cur_link, h.linkname, ll);
730 6           c->cur_link[ll] = '\0';
731 6           out->link_target = c->cur_link;
732 6           out->link_target_len = ll;
733             }
734              
735 833           out->size = size;
736 833           out->mode = (uint32_t)tar_parse_numeric(h.mode, sizeof h.mode);
737 833           out->uid = (uint32_t)tar_parse_numeric(h.uid, sizeof h.uid);
738 833           out->gid = (uint32_t)tar_parse_numeric(h.gid, sizeof h.gid);
739 833           out->mtime = tar_parse_numeric(h.mtime, sizeof h.mtime);
740 833           out->mtime_ns = 0;
741 833           out->type = typeflag_to_ae(tf);
742              
743             /* Apply globals first, then per-entry PAX overrides. */
744 833 100         if (c->global_kv) apply_pax_to_entry(c, c->global_kv, out);
745 833 100         if (per_entry) apply_pax_to_entry(c, per_entry, out);
746 833           pax_kv_free(per_entry);
747              
748 833 100         if (c->cur_xattr_count) {
749 3           out->xattrs = c->cur_xattrs;
750 3           out->xattr_count = c->cur_xattr_count;
751             }
752              
753             /* Set up payload bookkeeping. */
754 833 100         c->entry_remaining = (out->type == AE_FILE) ? out->size : 0;
755 833 100         if (out->type != AE_FILE) {
756             /* Some producers emit a non-zero size for non-files (rare); skip the bytes. */
757 13           c->entry_remaining = out->size;
758             }
759 1666           c->entry_pad = c->entry_remaining
760 820           ? (TAR_BLOCK_SIZE - (c->entry_remaining % TAR_BLOCK_SIZE)) % TAR_BLOCK_SIZE
761 833 100         : 0;
762              
763 833           return 1;
764             }
765             }
766              
767             static int
768 1945           tar_read_data(pTHX_ const ArchivePlugin *self, void *cursor, char *buf, size_t len)
769             {
770             PERL_UNUSED_ARG(self);
771 1945           tar_cursor_t *c = (tar_cursor_t *)cursor;
772 1945 100         if (c->entry_remaining == 0) return 0;
773 1127           size_t take = (c->entry_remaining < len) ? (size_t)c->entry_remaining : len;
774 1127           int n = read_full(c->pull, c->src, buf, take);
775 1127 50         if (n < (int)take) return -1;
776 1127           c->entry_remaining -= take;
777             /* When the entry is exhausted, consume padding so next read_next
778             * starts on a header boundary. */
779 1127 100         if (c->entry_remaining == 0 && c->entry_pad > 0) {
    100          
780             char pad[TAR_BLOCK_SIZE];
781 803           int pn = read_full(c->pull, c->src, pad, (size_t)c->entry_pad);
782 803 50         if (pn < (int)c->entry_pad) return -1;
783 803           c->entry_pad = 0;
784             }
785 1127           return (int)take;
786             }
787              
788             static void
789 69           tar_read_close(pTHX_ const ArchivePlugin *self, void *cursor)
790             {
791             PERL_UNUSED_ARG(self);
792 69 50         if (!cursor) return;
793 69           tar_cursor_t *c = (tar_cursor_t *)cursor;
794 69           pax_kv_free(c->global_kv);
795 69           free_cur_buffers(c);
796 69           free(c);
797             }
798              
799             /* ============================================================
800             * Write side
801             * ============================================================ */
802              
803             static int
804 39           parse_format_opt(pTHX_ HV *opts)
805             {
806 39 50         if (!opts) return FMT_AUTO;
807 39           SV **sv = hv_fetchs(opts, "format", 0);
808 39 100         if (!sv || !*sv || !SvOK(*sv)) return FMT_AUTO;
    50          
    50          
809             STRLEN n;
810 10           const char *p = SvPV(*sv, n);
811 10 100         if (n == 4 && memcmp(p, "auto", 4) == 0) return FMT_AUTO;
    50          
812 9 100         if (n == 3 && memcmp(p, "pax", 3) == 0) return FMT_PAX;
    100          
813 5 100         if (n == 3 && memcmp(p, "gnu", 3) == 0) return FMT_GNU;
    50          
814 4 50         if (n == 5 && memcmp(p, "ustar", 5) == 0) return FMT_USTAR;
    50          
815 0           return -1;
816             }
817              
818             /* Returns 1 if this entry's name fits the ustar 100/256-byte limit. */
819             static int
820 678           ustar_name_fits(const ArchiveEntry *e)
821             {
822 678           return e->name_len <= 100;
823             /* (We could also try a prefix split; we leave that to PAX/@LongLink path.) */
824             }
825              
826             static int
827 4           ustar_link_fits(const ArchiveEntry *e)
828             {
829 4           return e->link_target_len <= 100;
830             }
831              
832             static int
833 2764           ustar_numeric_fits(uint64_t v, size_t field_n)
834             {
835             /* field_n includes the trailing NUL: usable digits = field_n - 1. */
836 2764           uint64_t cap = 1;
837             size_t i;
838 27600 100         for (i = 0; i < field_n - 1; i++) {
839 24836 50         if (cap > (UINT64_MAX >> 3)) return 1;
840 24836           cap <<= 3;
841             }
842 2764           return v < cap;
843             }
844              
845             static int
846 688           emit_block(tar_cursor_t *c, const char *block)
847             {
848 688           return write_full(c->push, c->sink, block, TAR_BLOCK_SIZE);
849             }
850              
851             static int
852 679           emit_padding_for(tar_cursor_t *c, uint64_t size)
853             {
854 679 50         if (size == 0) return 0;
855 679           size_t pad = (TAR_BLOCK_SIZE - (size_t)(size % TAR_BLOCK_SIZE)) % TAR_BLOCK_SIZE;
856 679 100         if (!pad) return 0;
857 675           char zeros[TAR_BLOCK_SIZE] = {0};
858 675           return write_full(c->push, c->sink, zeros, pad);
859             }
860              
861             /* Build a base ustar header into `h` (caller pre-zeroed). Truncates
862             * fields to ustar limits; PAX/@LongLink override paths handled by the
863             * caller. */
864             static void
865 673           build_ustar_header(tar_header_t *h, const ArchiveEntry *e, char typeflag, const char *override_name)
866             {
867 673 50         const char *name = override_name ? override_name : e->name;
868 673 50         size_t name_len = override_name ? strlen(override_name) : e->name_len;
869 673           size_t cpy = name_len > sizeof h->name ? sizeof h->name : name_len;
870 673           memcpy(h->name, name, cpy);
871              
872 673 100         if (e->link_target && e->link_target_len) {
    50          
873 4           size_t llen = e->link_target_len;
874 4 50         if (llen > sizeof h->linkname) llen = sizeof h->linkname;
875 4           memcpy(h->linkname, e->link_target, llen);
876             }
877              
878 673 50         tar_emit_numeric(h->mode, sizeof h->mode, (uint64_t)(e->mode ? e->mode : 0644));
879 673           tar_emit_numeric(h->uid, sizeof h->uid, (uint64_t)e->uid);
880 673           tar_emit_numeric(h->gid, sizeof h->gid, (uint64_t)e->gid);
881 673           tar_emit_numeric(h->size, sizeof h->size, e->size);
882 673           tar_emit_numeric(h->mtime, sizeof h->mtime, e->mtime);
883              
884 673           h->typeflag = typeflag;
885 673           memcpy(h->magic, "ustar", 5);
886 673           h->version[0] = '0';
887 673           h->version[1] = '0';
888              
889             /* checksum: fill chksum field with spaces, compute, then write. */
890 673           memset(h->chksum, ' ', sizeof h->chksum);
891 673           uint32_t sum = tar_compute_checksum(h);
892             /* chksum is 6 octal digits + NUL + space per convention. */
893             char tmp[8];
894 673           snprintf(tmp, sizeof tmp, "%06o", (unsigned)sum);
895 673           memcpy(h->chksum, tmp, 6);
896 673           h->chksum[6] = '\0';
897 673           h->chksum[7] = ' ';
898 673           }
899              
900             /* Emit a GNU @LongLink block carrying `payload` of `payload_len` bytes
901             * with the given flag (TF_GNU_LONGNAME or TF_GNU_LONGLINK). */
902             static int
903 3           emit_gnu_longlink(tar_cursor_t *c, char flag, const char *payload, size_t payload_len)
904             {
905             tar_header_t h;
906 3           memset(&h, 0, sizeof h);
907             /* @LongLink "filename" is the literal `././@LongLink`. */
908 3           strcpy(h.name, "././@LongLink");
909 3           tar_emit_numeric(h.mode, sizeof h.mode, 0644);
910 3           tar_emit_numeric(h.size, sizeof h.size, (uint64_t)payload_len);
911 3           tar_emit_numeric(h.mtime, sizeof h.mtime, 0);
912 3           h.typeflag = flag;
913             /* GNU magic is "ustar " (5 + space + space). */
914 3           memcpy(h.magic, "ustar ", 6);
915 3           h.version[0] = ' ';
916 3           h.version[1] = '\0';
917 3           memset(h.chksum, ' ', sizeof h.chksum);
918 3           uint32_t sum = tar_compute_checksum(&h);
919             char tmp[8];
920 3           snprintf(tmp, sizeof tmp, "%06o", (unsigned)sum);
921 3           memcpy(h.chksum, tmp, 6);
922 3           h.chksum[6] = '\0';
923 3           h.chksum[7] = ' ';
924 3 50         if (emit_block(c, (char *)&h) < 0) return -1;
925             /* Payload with trailing NUL, padded to block boundary. */
926 3 50         if (write_full(c->push, c->sink, payload, payload_len) < 0) return -1;
927 3 50         if (emit_padding_for(c, payload_len) < 0) return -1;
928 3           return 0;
929             }
930              
931             /* Emit a PAX 'x' header carrying the given pre-built records buffer. */
932             static int
933 12           emit_pax_header(tar_cursor_t *c, char flag, const ArchiveEntry *e,
934             const char *records, size_t records_len)
935             {
936             tar_header_t h;
937 12           memset(&h, 0, sizeof h);
938             /* Conventional PAX header name: /PaxHeaders/. */
939             char name[100];
940 12 100         const char *base = e ? e->name : "";
941 12 100         size_t base_len = e ? e->name_len : 0;
942 12 50         const char *slash = base ? (const char *)memchr(base, '/', base_len) : NULL;
943             /* Find LAST slash. */
944 12 50         if (base) {
945             const char *p;
946 12           slash = NULL;
947 825 100         for (p = base; p < base + base_len; p++) if (*p == '/') slash = p;
    100          
948             }
949 12 100         if (slash) {
950 3           size_t dirlen = slash - base;
951 3           size_t baselen = base_len - dirlen - 1;
952 3           snprintf(name, sizeof name, "%.*s/PaxHeaders/%.*s",
953             (int)dirlen, base, (int)baselen, slash + 1);
954 9 100         } else if (e) {
955 7           snprintf(name, sizeof name, "PaxHeaders/%.*s", (int)base_len, base);
956             } else {
957 2           snprintf(name, sizeof name, "PaxHeaders/global.%d", c->pax_header_seq++);
958             }
959 12           name[sizeof name - 1] = '\0';
960 12           strncpy(h.name, name, sizeof h.name);
961              
962 12           tar_emit_numeric(h.mode, sizeof h.mode, 0644);
963 12           tar_emit_numeric(h.size, sizeof h.size, (uint64_t)records_len);
964 12           tar_emit_numeric(h.mtime, sizeof h.mtime, 0);
965 12           h.typeflag = flag;
966 12           memcpy(h.magic, "ustar", 5);
967 12           h.version[0] = '0';
968 12           h.version[1] = '0';
969 12           memset(h.chksum, ' ', sizeof h.chksum);
970 12           uint32_t sum = tar_compute_checksum(&h);
971             char tmp[8];
972 12           snprintf(tmp, sizeof tmp, "%06o", (unsigned)sum);
973 12           memcpy(h.chksum, tmp, 6);
974 12           h.chksum[6] = '\0';
975 12           h.chksum[7] = ' ';
976 12 50         if (emit_block(c, (char *)&h) < 0) return -1;
977 12 50         if (write_full(c->push, c->sink, records, records_len) < 0) return -1;
978 12 50         if (emit_padding_for(c, records_len) < 0) return -1;
979 12           return 0;
980             }
981              
982             /* Build PAX records from an entry. Returns malloced buffer; *out_len
983             * set. Caller frees. The `force_all` flag: if 1, emit records for
984             * every applicable field regardless of whether ustar would have fit
985             * (used for format=pax). */
986             static int
987 10           build_pax_for_entry(const ArchiveEntry *e, char **out, size_t *out_len, int force_all)
988             {
989             /* Worst-case size: each record at most 32 + key + value + a few bytes.
990             * Aggregate xattr sizes and pad. */
991 10           size_t cap = 256;
992             size_t i;
993 10 100         if (e->name_len > 100 || force_all) cap += 32 + 8 + e->name_len;
    100          
994 10 50         if (e->link_target_len > 100 || force_all) cap += 32 + 12 + e->link_target_len;
    100          
995 10 50         if (!ustar_numeric_fits(e->size, sizeof ((tar_header_t *)0)->size) || force_all) cap += 64;
    100          
996 10 100         if (e->mtime_ns) cap += 64;
997 10 100         if (!ustar_numeric_fits(e->uid, sizeof ((tar_header_t *)0)->uid) || force_all) cap += 32;
    100          
998 10 100         if (!ustar_numeric_fits(e->gid, sizeof ((tar_header_t *)0)->gid) || force_all) cap += 32;
    100          
999 15 100         for (i = 0; i < e->xattr_count; i++) {
1000 5           cap += 64 + 32 + e->xattrs[i].key_len + e->xattrs[i].value_len * 2 + 16;
1001             }
1002 10 50         if (cap < 4096) cap = 4096;
1003 10           char *buf = (char *)malloc(cap);
1004 10 50         if (!buf) return -1;
1005 10           size_t pos = 0;
1006             char tmp[64];
1007              
1008 10 100         if (e->name_len > 100 || force_all) {
    100          
1009 5           char *rec = buf + pos;
1010 5           pos += pax_build_record(rec, "path", e->name, e->name_len);
1011             }
1012 10 50         if ((e->link_target_len > 100 && e->link_target_len) || (force_all && e->link_target_len)) {
    0          
    100          
    50          
1013 0           pos += pax_build_record(buf + pos, "linkpath", e->link_target, e->link_target_len);
1014             }
1015 10 50         if (!ustar_numeric_fits(e->size, sizeof ((tar_header_t *)0)->size) || force_all) {
    100          
1016 5           int n = snprintf(tmp, sizeof tmp, "%llu", (unsigned long long)e->size);
1017 5           pos += pax_build_record(buf + pos, "size", tmp, (size_t)n);
1018             }
1019 10 100         if (e->mtime_ns) {
1020 4           int n = snprintf(tmp, sizeof tmp, "%llu.%09u",
1021 4           (unsigned long long)e->mtime, (unsigned)e->mtime_ns);
1022 4           pos += pax_build_record(buf + pos, "mtime", tmp, (size_t)n);
1023             }
1024 10 100         if (!ustar_numeric_fits(e->uid, sizeof ((tar_header_t *)0)->uid) || force_all) {
    100          
1025 5           int n = snprintf(tmp, sizeof tmp, "%u", (unsigned)e->uid);
1026 5           pos += pax_build_record(buf + pos, "uid", tmp, (size_t)n);
1027             }
1028 10 100         if (!ustar_numeric_fits(e->gid, sizeof ((tar_header_t *)0)->gid) || force_all) {
    100          
1029 5           int n = snprintf(tmp, sizeof tmp, "%u", (unsigned)e->gid);
1030 5           pos += pax_build_record(buf + pos, "gid", tmp, (size_t)n);
1031             }
1032             /* xattrs */
1033 15 100         for (i = 0; i < e->xattr_count; i++) {
1034 5           const ArchiveXattr *x = &e->xattrs[i];
1035 5           int b64 = needs_b64(x->value, x->value_len);
1036             char keybuf[300];
1037 5 100         snprintf(keybuf, sizeof keybuf, "SCHILY.xattr.%.*s%s",
1038 5           (int)x->key_len, x->key, b64 ? ".b64" : "");
1039 5 100         if (b64) {
1040 2           char *enc = b64_encode(x->value, x->value_len);
1041 2 50         if (enc) {
1042 2           pos += pax_build_record(buf + pos, keybuf, enc, strlen(enc));
1043 2           free(enc);
1044             }
1045             } else {
1046 3           pos += pax_build_record(buf + pos, keybuf, x->value, x->value_len);
1047             }
1048             }
1049              
1050 10           *out = buf;
1051 10           *out_len = pos;
1052 10           return 0;
1053             }
1054              
1055             static int
1056 39           tar_write_open(pTHX_ const ArchivePlugin *self, archive_push_fn push,
1057             void *sink, HV *opts, void **out_cursor)
1058             {
1059             PERL_UNUSED_ARG(self);
1060 39           int fmt = parse_format_opt(aTHX_ opts);
1061 39 50         if (fmt < 0) return -1;
1062 39           tar_cursor_t *c = (tar_cursor_t *)calloc(1, sizeof *c);
1063 39 50         if (!c) return -1;
1064 39           c->push = push;
1065 39           c->sink = sink;
1066 39           c->format = fmt;
1067              
1068             /* PAX 'g' global header from opts.global_meta. */
1069 39 50         if (opts) {
1070 39           SV **gm = hv_fetchs(opts, "global_meta", 0);
1071 39 100         if (gm && *gm && SvROK(*gm) && SvTYPE(SvRV(*gm)) == SVt_PVHV) {
    50          
    50          
    50          
1072 2           HV *gh = (HV *)SvRV(*gm);
1073 2           char *records = (char *)malloc(4096);
1074 2           size_t rcap = 4096, rpos = 0;
1075 2           hv_iterinit(gh);
1076             HE *he;
1077 6 100         while ((he = hv_iternext(gh))) {
1078             I32 klen_i;
1079 4           const char *k = hv_iterkey(he, &klen_i);
1080 4           SV *v = hv_iterval(gh, he);
1081             STRLEN vlen;
1082 4           const char *vp = SvPV(v, vlen);
1083 4           size_t need = (size_t)klen_i + vlen + 64;
1084 4 50         if (rpos + need > rcap) {
1085 0 0         while (rpos + need > rcap) rcap *= 2;
1086 0           records = (char *)realloc(records, rcap);
1087             }
1088             /* Build using a NUL-terminated key (pax_build_record needs strlen). */
1089             char keybuf[256];
1090 4           memcpy(keybuf, k, klen_i);
1091 4           keybuf[klen_i] = '\0';
1092 4           rpos += pax_build_record(records + rpos, keybuf, vp, vlen);
1093             }
1094 2 50         if (rpos > 0) {
1095 2 50         if (emit_pax_header(c, TF_PAX_GLOBAL, NULL, records, rpos) < 0) {
1096 0           free(records);
1097 0           free(c);
1098 0           return -1;
1099             }
1100             }
1101 2           free(records);
1102             }
1103             }
1104              
1105 39           *out_cursor = c;
1106 39           return 0;
1107             }
1108              
1109             static int
1110 678           tar_write_add(pTHX_ const ArchivePlugin *self, void *cursor,
1111             const ArchiveEntry *entry, const char *content, size_t len)
1112             {
1113             PERL_UNUSED_ARG(self);
1114 678           tar_cursor_t *c = (tar_cursor_t *)cursor;
1115              
1116 678           int needs_pax = 0;
1117 678           int needs_gnu = 0;
1118 678           int needs_long_name = !ustar_name_fits(entry);
1119 678 100         int needs_long_link = entry->link_target && !ustar_link_fits(entry);
    50          
1120              
1121 678 50         if (!ustar_numeric_fits(entry->size, sizeof ((tar_header_t *)0)->size)
1122 678 100         || !ustar_numeric_fits(entry->uid, sizeof ((tar_header_t *)0)->uid)
1123 674 50         || !ustar_numeric_fits(entry->gid, sizeof ((tar_header_t *)0)->gid)
1124 674 50         || !ustar_numeric_fits(entry->mtime, sizeof ((tar_header_t *)0)->mtime)
1125 674 100         || entry->mtime_ns
1126 671 100         || entry->xattr_count) {
1127 9           needs_pax = 1;
1128             }
1129              
1130 678 100         if (c->format == FMT_USTAR) {
1131 4 100         if (needs_pax || needs_long_name || needs_long_link) return -1;
    50          
    0          
1132 674 100         } else if (c->format == FMT_GNU) {
1133 2 100         if (needs_pax) return -1;
1134 1 50         if (needs_long_name || needs_long_link) needs_gnu = 1;
    0          
1135 672 100         } else if (c->format == FMT_PAX) {
1136 5           needs_pax = 1; /* force */
1137             } else { /* FMT_AUTO */
1138 667 100         if (needs_pax) needs_pax = 1;
1139 662 100         else if (needs_long_name || needs_long_link) needs_gnu = 1;
    50          
1140             }
1141              
1142             /* Emit GNU @LongLink prefix blocks (for name and/or linkpath). */
1143 673 100         if (needs_gnu) {
1144 3 50         if (needs_long_name && entry->name_len) {
    50          
1145 3 50         if (emit_gnu_longlink(c, TF_GNU_LONGNAME, entry->name, entry->name_len + 1) < 0) return -1;
1146             }
1147 3 50         if (needs_long_link && entry->link_target_len) {
    0          
1148 0 0         if (emit_gnu_longlink(c, TF_GNU_LONGLINK, entry->link_target, entry->link_target_len + 1) < 0) return -1;
1149             }
1150             }
1151              
1152             /* Emit PAX 'x' header. */
1153 673 100         if (needs_pax) {
1154             char *records;
1155             size_t rlen;
1156 10 50         if (build_pax_for_entry(entry, &records, &rlen, c->format == FMT_PAX) < 0) return -1;
1157 10 50         if (rlen > 0) {
1158 10 50         if (emit_pax_header(c, TF_PAX_FILE, entry, records, rlen) < 0) {
1159 0           free(records); return -1;
1160             }
1161             }
1162 10           free(records);
1163             }
1164              
1165             /* Emit ustar header. */
1166             tar_header_t h;
1167 673           memset(&h, 0, sizeof h);
1168             char typeflag;
1169 673           switch (entry->type) {
1170 5           case AE_DIR: typeflag = TF_DIRECTORY; break;
1171 4           case AE_SYMLINK: typeflag = TF_SYMLINK; break;
1172 0           case AE_HARDLINK: typeflag = TF_HARDLINK; break;
1173 0           case AE_FIFO: typeflag = TF_FIFO; break;
1174 0           case AE_CHAR: typeflag = TF_CHAR; break;
1175 0           case AE_BLOCK: typeflag = TF_BLOCK; break;
1176 664           default: typeflag = TF_REGULAR; break;
1177             }
1178             /* If name overflows ustar AND we're not using PAX/@LongLink, that's
1179             * an error. (Already handled above in format branches.) */
1180 673           const char *name_for_header = entry->name;
1181             char tnbuf[101];
1182 673 100         if (entry->name_len > 100) {
1183             /* Use a truncated stand-in; the real name is in PAX/@LongLink. */
1184 6           size_t cpy = 100;
1185 6           memcpy(tnbuf, entry->name, cpy);
1186 6           tnbuf[cpy] = '\0';
1187 6           name_for_header = tnbuf;
1188             }
1189 673           build_ustar_header(&h, entry, typeflag, name_for_header);
1190 673 50         if (emit_block(c, (char *)&h) < 0) return -1;
1191              
1192             /* Emit payload. */
1193 673 100         if (entry->size > 0 && content) {
    50          
1194 664 50         if (write_full(c->push, c->sink, content, (size_t)entry->size) < 0) return -1;
1195 664 50         if (emit_padding_for(c, entry->size) < 0) return -1;
1196 9 50         } else if (len > 0 && content) {
    0          
1197 0 0         if (write_full(c->push, c->sink, content, len) < 0) return -1;
1198 0 0         if (emit_padding_for(c, len) < 0) return -1;
1199             }
1200              
1201 673           return 0;
1202             }
1203              
1204             static int
1205 39           tar_write_close(pTHX_ const ArchivePlugin *self, void *cursor)
1206             {
1207             PERL_UNUSED_ARG(self);
1208 39 50         if (!cursor) return 0;
1209 39           tar_cursor_t *c = (tar_cursor_t *)cursor;
1210 39           char zeros[TAR_BLOCK_SIZE * 2] = {0};
1211 39           int rc = write_full(c->push, c->sink, zeros, sizeof zeros);
1212 39           pax_kv_free(c->global_kv);
1213 39           free(c);
1214 39           return rc;
1215             }
1216              
1217             /* ============================================================
1218             * Probe
1219             * ============================================================ */
1220              
1221             static int
1222 0           tar_probe(const char *bytes, size_t len)
1223             {
1224 0 0         if (len < TAR_BLOCK_SIZE) return 0;
1225 0           const tar_header_t *h = (const tar_header_t *)bytes;
1226 0 0         if (memcmp(h->magic, "ustar", 5) == 0) {
1227             /* Verify checksum to weed out false positives. */
1228 0           uint32_t stored = (uint32_t)tar_parse_numeric(h->chksum, sizeof h->chksum);
1229 0           uint32_t actual = tar_compute_checksum(h);
1230 0 0         if (stored == actual) return 90;
1231 0           return 60;
1232             }
1233             /* Old-style: maybe still tar if checksum verifies. */
1234 0           uint32_t stored = (uint32_t)tar_parse_numeric(h->chksum, sizeof h->chksum);
1235 0           uint32_t actual = tar_compute_checksum(h);
1236 0 0         if (stored == actual && stored != 0) return 50;
    0          
1237 0           return 0;
1238             }
1239              
1240             /* ============================================================
1241             * Plugin descriptor
1242             * ============================================================ */
1243              
1244             ArchivePlugin tar_plugin = {
1245             .name = "tar",
1246             .probe = tar_probe,
1247             .read_open = tar_read_open,
1248             .read_next = tar_read_next,
1249             .read_data = tar_read_data,
1250             .read_close = tar_read_close,
1251             .read_seek_to = NULL,
1252             .write_open = tar_write_open,
1253             .write_add = tar_write_add,
1254             .write_close = tar_write_close,
1255             .state = NULL,
1256             };