import-tars: read overlong names from pax extended header

Importing gcc tarballs[1] with import-tars script (in contrib) fails when hitting a pax extended header. Make sure we always read the extended attributes from the pax entries, and store the 'path' value if found to be used in the next ustar entry. The code to parse pax extended headers was written consulting the Pax Pax Interchange Format documentation [2]. [1] http://ftp.gnu.org/gnu/gcc/gcc-7.3.0/gcc-7.3.0.tar.xz [2] https://www.freebsd.org/cgi/man.cgi?manpath=FreeBSD+8-current&query=tar&sektion=5 Signed-off-by: Pedro Alvarez <palvarez89@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Pedro Alvarez Piedehierro committed May 23, 2018 at 23:54 UTC 12ecea46e3a7542918efaf595fd866e74d3dba3d
1 file changed +29 -2
contrib/fast-import/import-tars.perl
+29 -2
@@ -63,6 +63,8 @@ foreach my $tar_file (@ARGV)
63 my $have_top_dir = 1;
64 my ($top_dir, %files);
65
66 + my $next_path = '';
67 +
68 while (read(I, $_, 512) == 512) {
69 my ($name, $mode, $uid, $gid, $size, $mtime,
70 $chksum, $typeflag, $linkname, $magic,
@@ -70,6 +72,13 @@ foreach my $tar_file (@ARGV)
72 $prefix) = unpack 'Z100 Z8 Z8 Z8 Z12 Z12
73 Z8 Z1 Z100 Z6
74 Z2 Z32 Z32 Z8 Z8 Z*', $_;
75 +
76 + unless ($next_path eq '') {
77 + # Recover name from previous extended header
78 + $name = $next_path;
79 + $next_path = '';
80 + }
81 +
82 last unless length($name);
83 if ($name eq '././@LongLink') {
84 # GNU tar extension
@@ -90,13 +99,31 @@ foreach my $tar_file (@ARGV)
99 Z8 Z1 Z100 Z6
100 Z2 Z32 Z32 Z8 Z8 Z*', $_;
101 }
93 - next if $name =~ m{/\z};
102 $mode = oct $mode;
103 $size = oct $size;
104 $mtime = oct $mtime;
105 next if $typeflag == 5; # directory
106
99 - if ($typeflag != 1) { # handle hard links later
107 + if ($typeflag eq 'x') { # extended header
108 + # If extended header, check for path
109 + my $pax_header = '';
110 + while ($size > 0 && read(I, $_, 512) == 512) {
111 + $pax_header = $pax_header . substr($_, 0, $size);
112 + $size -= 512;
113 + }
114 +
115 + my @lines = split /\n/, $pax_header;
116 + foreach my $line (@lines) {
117 + my ($len, $entry) = split / /, $line;
118 + my ($key, $value) = split /=/, $entry;
119 + if ($key eq 'path') {
120 + $next_path = $value;
121 + }
122 + }
123 + next;
124 + } elsif ($name =~ m{/\z}) { # directory
125 + next;
126 + } elsif ($typeflag != 1) { # handle hard links later
127 print FI "blob\n", "mark :$next_mark\n";
128 if ($typeflag == 2) { # symbolic link
129 print FI "data ", length($linkname), "\n",