@@ -213,6 +213,8 @@ zend_result phar_parse_tarfile(php_stream* fp, char *fname, size_t fname_len, ch
213
213
int last_was_longlink = 0 ;
214
214
size_t linkname_len ;
215
215
216
+ zend_string * filename_pax_override = NULL ;
217
+
216
218
if (error ) {
217
219
* error = NULL ;
218
220
}
@@ -275,11 +277,77 @@ zend_result phar_parse_tarfile(php_stream* fp, char *fname, size_t fname_len, ch
275
277
phar_tar_oct_number (hdr -> size , sizeof (hdr -> size ));
276
278
277
279
/* 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 ) {
279
281
size = (size + 511 )& ~511 ;
280
282
goto next ;
281
283
}
282
284
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 ??? */
283
351
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 )) {
284
352
zend_off_t curloc ;
285
353
size_t sig_len ;
@@ -289,6 +357,7 @@ zend_result phar_parse_tarfile(php_stream* fp, char *fname, size_t fname_len, ch
289
357
spprintf (error , 4096 , "phar error: tar-based phar \"%s\" has signature that is larger than 511 bytes, cannot process" , fname );
290
358
}
291
359
bail :
360
+ zend_string_release (filename_pax_override );
292
361
php_stream_close (fp );
293
362
phar_destroy_phar_data (myphar );
294
363
return FAILURE ;
@@ -356,7 +425,10 @@ zend_result phar_parse_tarfile(php_stream* fp, char *fname, size_t fname_len, ch
356
425
goto bail ;
357
426
}
358
427
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' ) {
360
432
last_was_longlink = 1 ;
361
433
/* support the ././@LongLink system for storing long filenames */
362
434
@@ -564,6 +636,7 @@ zend_result phar_parse_tarfile(php_stream* fp, char *fname, size_t fname_len, ch
564
636
next :
565
637
/* this is not good enough - seek succeeds even on truncated tars */
566
638
php_stream_seek (fp , size , SEEK_CUR );
639
+ next_no_seek :
567
640
if ((uint32_t )php_stream_tell (fp ) > totalsize ) {
568
641
if (error ) {
569
642
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
580
653
read = php_stream_read (fp , buf , sizeof (buf ));
581
654
582
655
if (read != sizeof (buf )) {
656
+ truncated :
583
657
if (error ) {
584
658
spprintf (error , 4096 , "phar error: \"%s\" is a corrupted tar file (truncated)" , fname );
585
659
}
@@ -674,6 +748,8 @@ zend_result phar_parse_tarfile(php_stream* fp, char *fname, size_t fname_len, ch
674
748
* pphar = myphar ;
675
749
}
676
750
751
+ pefree (filename_pax_override , myphar -> is_persistent );
752
+
677
753
return SUCCESS ;
678
754
}
679
755
/* }}} */
0 commit comments