@@ -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,11 +277,77 @@ 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
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,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 }
291359bail :
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
564638next :
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