Skip to content

Commit a1cf23e

Browse files
committed
Parse PAX header and extract path
1 parent ec7899c commit a1cf23e

File tree

1 file changed

+79
-3
lines changed

1 file changed

+79
-3
lines changed

ext/phar/tar.c

Lines changed: 79 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
}
@@ -272,14 +274,80 @@ zend_result phar_parse_tarfile(php_stream* fp, char *fname, size_t fname_len, ch
272274
}
273275

274276
size = entry.uncompressed_filesize = entry.compressed_filesize =
275-
phar_tar_number(hdr->size, sizeof(hdr->size));
277+
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

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+
/* TODO: override from PAX ??? */
283351
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)) {
284352
zend_off_t curloc;
285353
size_t sig_len;
@@ -289,6 +357,7 @@ 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+
zend_string_release(filename_pax_override);
292361
php_stream_close(fp);
293362
phar_destroy_phar_data(myphar);
294363
return FAILURE;
@@ -356,7 +425,10 @@ zend_result phar_parse_tarfile(php_stream* fp, char *fname, size_t fname_len, ch
356425
goto bail;
357426
}
358427

359-
if (!last_was_longlink && hdr->typeflag == 'L') {
428+
if (filename_pax_override) {
429+
entry.filename = filename_pax_override;
430+
filename_pax_override = NULL;
431+
} else if (!last_was_longlink && hdr->typeflag == 'L') {
360432
last_was_longlink = 1;
361433
/* support the ././@LongLink system for storing long filenames */
362434

@@ -564,6 +636,7 @@ zend_result phar_parse_tarfile(php_stream* fp, char *fname, size_t fname_len, ch
564636
next:
565637
/* this is not good enough - seek succeeds even on truncated tars */
566638
php_stream_seek(fp, size, SEEK_CUR);
639+
next_no_seek:
567640
if ((uint32_t)php_stream_tell(fp) > totalsize) {
568641
if (error) {
569642
spprintf(error, 4096, "phar error: \"%s\" is a corrupted tar file (truncated)", fname);
@@ -580,6 +653,7 @@ zend_result phar_parse_tarfile(php_stream* fp, char *fname, size_t fname_len, ch
580653
read = php_stream_read(fp, buf, sizeof(buf));
581654

582655
if (read != sizeof(buf)) {
656+
truncated:
583657
if (error) {
584658
spprintf(error, 4096, "phar error: \"%s\" is a corrupted tar file (truncated)", fname);
585659
}
@@ -674,6 +748,8 @@ zend_result phar_parse_tarfile(php_stream* fp, char *fname, size_t fname_len, ch
674748
*pphar = myphar;
675749
}
676750

751+
pefree(filename_pax_override, myphar->is_persistent);
752+
677753
return SUCCESS;
678754
}
679755
/* }}} */

0 commit comments

Comments
 (0)