Skip to content

Commit 98df7e9

Browse files
committed
Parse PAX header and extract path
1 parent 6e1ee35 commit 98df7e9

File tree

1 file changed

+81
-3
lines changed

1 file changed

+81
-3
lines changed

ext/phar/tar.c

Lines changed: 81 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,8 @@ zend_result phar_parse_tarfile(php_stream* fp, char *fname, size_t fname_len, ch
213213
int last_was_longlink = 0;
214214
size_t linkname_len;
215215

216+
zend_string *filename_pax_override = NULL;
217+
216218
if (error) {
217219
*error = NULL;
218220
}
@@ -275,12 +277,78 @@ zend_result phar_parse_tarfile(php_stream* fp, char *fname, size_t fname_len, ch
275277
phar_tar_oct_number(hdr->size, sizeof(hdr->size));
276278

277279
/* skip global/file headers (pax) */
278-
if (!old && (hdr->typeflag == TAR_GLOBAL_HDR || hdr->typeflag == TAR_FILE_HDR)) {
280+
if (!old && hdr->typeflag == TAR_GLOBAL_HDR) {
279281
size = (size+511)&~511;
280282
goto next;
281283
}
282284

283-
if (((!old && hdr->prefix[0] == 0) || old) && zend_strnlen(hdr->name, 100) == sizeof(".phar/signature.bin")-1 && !strncmp(hdr->name, ".phar/signature.bin", sizeof(".phar/signature.bin")-1)) {
285+
/* Process file pax header */
286+
if (!old && hdr->typeflag == TAR_FILE_HDR) {
287+
size = (size + 511) & ~511;
288+
289+
char *pax_data = emalloc(size);
290+
291+
if (UNEXPECTED(php_stream_read(fp, pax_data, size) != size)) {
292+
efree(pax_data);
293+
goto truncated;
294+
}
295+
296+
/* TODO: should this be usable multiple times? */
297+
if (filename_pax_override) {
298+
zend_string_release(filename_pax_override);
299+
filename_pax_override = NULL;
300+
}
301+
302+
char *ptr = pax_data;
303+
const char *pax_data_end = pax_data + size;
304+
while (ptr < pax_data_end) {
305+
/* Format: "%d %s=%s\n" */
306+
307+
char *line_end = memchr(ptr, '\n', pax_data_end - ptr);
308+
if (!line_end) {
309+
break;
310+
}
311+
312+
char *blank = memchr(ptr, ' ', line_end - ptr);
313+
if (!blank) {
314+
break;
315+
}
316+
*blank = '\0';
317+
size_t kv_size = strtoull(ptr, NULL, 10);
318+
if (kv_size != line_end - ptr + 1) {
319+
break;
320+
}
321+
322+
/* Check for known keys */
323+
const char *key = blank + 1;
324+
const char *equals = memchr(key, '=', line_end - key);
325+
if (!equals) {
326+
break;
327+
}
328+
size_t len = equals - key;
329+
if (len == strlen("path") && memcmp(key, "path", strlen("path")) == 0) {
330+
const char *filename_start = equals + 1;
331+
size_t filename_pax_override_len = line_end - filename_start;
332+
/* Ending '/' stripping */
333+
if (filename_pax_override_len > 0 && filename_start[filename_pax_override_len - 1] == '/') {
334+
filename_pax_override_len--;
335+
}
336+
filename_pax_override = zend_string_init(filename_start, filename_pax_override_len, myphar->is_persistent);
337+
if (myphar->is_persistent) {
338+
GC_MAKE_PERSISTENT_LOCAL(filename_pax_override);
339+
}
340+
}
341+
342+
ptr = line_end + 1;
343+
}
344+
345+
efree(pax_data);
346+
347+
goto next_no_seek;
348+
}
349+
350+
if ((filename_pax_override && zend_string_equals_literal(filename_pax_override, ".phar/signature.bin"))
351+
|| (((!old && hdr->prefix[0] == 0) || old) && !strcmp(hdr->name, ".phar/signature.bin"))) {
284352
zend_off_t curloc;
285353
size_t sig_len;
286354

@@ -289,6 +357,9 @@ zend_result phar_parse_tarfile(php_stream* fp, char *fname, size_t fname_len, ch
289357
spprintf(error, 4096, "phar error: tar-based phar \"%s\" has signature that is larger than 511 bytes, cannot process", fname);
290358
}
291359
bail:
360+
if (filename_pax_override) {
361+
zend_string_release(filename_pax_override);
362+
}
292363
php_stream_close(fp);
293364
phar_destroy_phar_data(myphar);
294365
return FAILURE;
@@ -356,7 +427,10 @@ zend_result phar_parse_tarfile(php_stream* fp, char *fname, size_t fname_len, ch
356427
goto bail;
357428
}
358429

359-
if (!last_was_longlink && hdr->typeflag == 'L') {
430+
if (filename_pax_override) {
431+
entry.filename = filename_pax_override;
432+
filename_pax_override = NULL;
433+
} else if (!last_was_longlink && hdr->typeflag == 'L') {
360434
last_was_longlink = 1;
361435
/* support the ././@LongLink system for storing long filenames */
362436

@@ -564,6 +638,7 @@ zend_result phar_parse_tarfile(php_stream* fp, char *fname, size_t fname_len, ch
564638
next:
565639
/* this is not good enough - seek succeeds even on truncated tars */
566640
php_stream_seek(fp, size, SEEK_CUR);
641+
next_no_seek:
567642
if ((uint32_t)php_stream_tell(fp) > totalsize) {
568643
if (error) {
569644
spprintf(error, 4096, "phar error: \"%s\" is a corrupted tar file (truncated)", fname);
@@ -580,6 +655,7 @@ zend_result phar_parse_tarfile(php_stream* fp, char *fname, size_t fname_len, ch
580655
read = php_stream_read(fp, buf, sizeof(buf));
581656

582657
if (read != sizeof(buf)) {
658+
truncated:
583659
if (error) {
584660
spprintf(error, 4096, "phar error: \"%s\" is a corrupted tar file (truncated)", fname);
585661
}
@@ -674,6 +750,8 @@ zend_result phar_parse_tarfile(php_stream* fp, char *fname, size_t fname_len, ch
674750
*pphar = myphar;
675751
}
676752

753+
pefree(filename_pax_override, myphar->is_persistent);
754+
677755
return SUCCESS;
678756
}
679757
/* }}} */

0 commit comments

Comments
 (0)