@@ -553,8 +553,8 @@ protected function writeRawFileHeader($name, $uid, $gid, $perm, $size, $mtime, $
553553 $ uid = sprintf ("%6s " , decoct ($ uid ));
554554 $ gid = sprintf ("%6s " , decoct ($ gid ));
555555 $ perm = sprintf ("%6s " , decoct ($ perm ));
556- $ size = sprintf ( " %11s " , decoct ( $ size) );
557- $ mtime = sprintf ( " %11s " , decoct ( $ mtime ) );
556+ $ size = self :: numberEncode ( $ size, 12 );
557+ $ mtime = self :: numberEncode ( $ size , 12 );
558558
559559 $ data_first = pack ("a100a8a8a8a12A12 " , $ name , $ perm , $ uid , $ gid , $ size , $ mtime );
560560 $ data_last = pack ("a1a100a6a2a32a32a8a8a155a12 " , $ typeflag , '' , 'ustar ' , '' , '' , '' , '' , '' , $ prefix , "" );
@@ -614,8 +614,8 @@ protected function parseHeader($block)
614614 $ return ['perm ' ] = OctDec (trim ($ header ['perm ' ]));
615615 $ return ['uid ' ] = OctDec (trim ($ header ['uid ' ]));
616616 $ return ['gid ' ] = OctDec (trim ($ header ['gid ' ]));
617- $ return ['size ' ] = OctDec ( trim ( $ header ['size ' ]) );
618- $ return ['mtime ' ] = OctDec ( trim ( $ header ['mtime ' ]) );
617+ $ return ['size ' ] = self :: numberDecode ( $ header ['size ' ]);
618+ $ return ['mtime ' ] = self :: numberDecode ( $ header ['mtime ' ]);
619619 $ return ['typeflag ' ] = $ header ['typeflag ' ];
620620 $ return ['link ' ] = trim ($ header ['link ' ]);
621621 $ return ['uname ' ] = trim ($ header ['uname ' ]);
@@ -713,4 +713,63 @@ public function filetype($file)
713713 return Archive::COMPRESS_NONE ;
714714 }
715715
716+ /**
717+ * Decodes numeric values according to the
718+ * https://www.gnu.org/software/tar/manual/html_node/Extensions.html#Extensions
719+ * (basically with support for big numbers)
720+ *
721+ * @param string $field
722+ * $return int
723+ */
724+ static public function numberDecode ($ field )
725+ {
726+ $ firstByte = ord (substr ($ field , 0 , 1 ));
727+ if ($ firstByte === 255 ) {
728+ $ value = -1 << (8 * strlen ($ field ));
729+ $ shift = 0 ;
730+ for ($ i = strlen ($ field ) - 1 ; $ i >= 0 ; $ i --) {
731+ $ value += ord (substr ($ field , $ i , 1 )) << $ shift ;
732+ $ shift += 8 ;
733+ }
734+ } elseif ($ firstByte === 128 ) {
735+ $ value = 0 ;
736+ $ shift = 0 ;
737+ for ($ i = strlen ($ field ) - 1 ; $ i > 0 ; $ i --) {
738+ $ value += ord (substr ($ field , $ i , 1 )) << $ shift ;
739+ $ shift += 8 ;
740+ }
741+ } else {
742+ $ value = octdec (trim ($ field ));
743+ }
744+ return $ value ;
745+ }
746+
747+ /**
748+ * Encodes numeric values according to the
749+ * https://www.gnu.org/software/tar/manual/html_node/Extensions.html#Extensions
750+ * (basically with support for big numbers)
751+ *
752+ * @param int $value
753+ * @param int $length field length
754+ * @return string
755+ */
756+ static public function numberEncode ($ value , $ length )
757+ {
758+ // old implementations leave last byte empty
759+ // octal encoding encodes three bits per byte
760+ $ maxValue = 1 << (($ length - 1 ) * 3 );
761+ if ($ value < 0 ) {
762+ // PHP already stores integers as 2's complement
763+ $ value = pack (PHP_INT_SIZE === 8 ? 'J ' : 'N ' , (int ) $ value );
764+ $ encoded = str_repeat (chr (255 ), max (1 , $ length - PHP_INT_SIZE ));
765+ $ encoded .= substr ($ value , max (0 , PHP_INT_SIZE - $ length + 1 ));
766+ } elseif ($ value >= $ maxValue ) {
767+ $ value = pack (PHP_INT_SIZE === 8 ? 'J ' : 'N ' , (int ) $ value );
768+ $ encoded = chr (128 ) . str_repeat (chr (0 ), max (0 , $ length - PHP_INT_SIZE - 1 ));
769+ $ encoded .= substr ($ value , max (0 , PHP_INT_SIZE - $ length + 1 ));
770+ } else {
771+ $ encoded = sprintf ("% " . ($ length - 1 ) . "s " , decoct ($ value ));
772+ }
773+ return $ encoded ;
774+ }
716775}
0 commit comments