1 # Copyright (C) 2014, Eric Wong <normalperson@yhbt.net> and all contributors
2 # License: GPLv2 or later (https://www.gnu.org/licenses/gpl-2.0.txt)
3 # This is based on code in Git.pm which is GPLv2, but modified to avoid
4 # dependence on environment variables for compatibility with mod_perl.
5 # There are also API changes to simplify our usage and data set.
6 package PublicInbox::GitCatFile;
9 use Fcntl qw(F_GETFD F_SETFD FD_CLOEXEC);
13 my ($class, $git_dir) = @_;
14 bless { git_dir => $git_dir }, $class;
19 my $flags = fcntl($fh, F_GETFD, 0) or die "fcntl(F_GETFD): $!\n";
20 fcntl($fh, F_SETFD, $flags | FD_CLOEXEC) or die "fcntl(F_SETFD): $!\n";
25 return if $self->{pid};
26 my ($in_r, $in_w, $out_r, $out_w);
28 pipe($in_r, $in_w) or die "pipe failed: $!\n";
29 set_cloexec($_) foreach ($in_r, $in_w);
30 pipe($out_r, $out_w) or die "pipe failed: $!\n";
31 set_cloexec($_) foreach ($out_r, $out_w);
33 my @cmd = ('git', "--git-dir=$self->{git_dir}", qw(cat-file --batch));
35 defined $pid or die "fork failed: $!\n";
37 dup2(fileno($out_r), 0) or die "redirect stdin failed: $!\n";
38 dup2(fileno($in_w), 1) or die "redirect stdout failed: $!\n";
39 exec(@cmd) or die 'exec `' . join(' '). "' failed: $!\n";
41 close $out_r or die "close failed: $!\n";
42 close $in_w or die "close failed: $!\n";
45 $self->{out} = $out_w;
50 my ($self, $object) = @_;
53 my $len = bytes::length($object);
55 $self->_cat_file_begin;
56 my $written = syswrite($self->{out}, $object);
57 if (!defined $written) {
58 die "pipe write error: $!\n";
59 } elsif ($written != $len) {
60 die "wrote too little to pipe ($written < $len)\n";
65 $head =~ / missing$/ and return undef;
66 $head =~ /^[0-9a-f]{40} \S+ (\d+)$/ or
67 die "Unexpected result from git cat-file: $head\n";
70 my $bytes_left = $size;
75 my $read = read($in, $buf, $bytes_left);
76 defined($read) or die "read pipe failed: $!\n";
81 my $read = read($in, $buf, 1);
82 defined($read) or die "read pipe failed: $!\n";
83 if ($read != 1 || $buf ne "\n") {
84 die "newline missing after blob\n";
91 my $pid = $self->{pid} or return;
93 foreach my $f (qw(in out)) {