+ getpackage();
+ getpushinfo();
+
+ check_package(); # might make package public, or might add taints
+
+ return 0 unless $pkg_exists;
+ return 0 unless $pkg_secret;
+
+ # we suppose that NEW has a version which is already in our
+ # history, as otherwise the repo would have been blown away
+
+ if (deliberately('not-fast-forward')) {
+ add_taint(server_ref($suite),
+ "rewound suite $suite; --deliberately-not-fast-forward".
+ " specified in signed tag $tagname for upload of".
+ " version $version");
+ return NOFFCHECK|FRESHREPO;
+ }
+ if (deliberately('include-questionable-history')) {
+ return 0;
+ }
+ die <<END;
+
+Package is in NEW and has not been accepted or rejected yet.
+Unfortunately, we cannot determine automatically what should happen.
+You will have to pass either --deliberately-not-fast-forward or
+--deliberately-include-questionable-history to specify whether you are
+keeping or discarding the previously pushed history.
+
+The choice is important, to ensure that your git history is both
+suitable for public distribution and as useful as possible. Please
+see the descriptions of these options in dgit(1).
+
+END
+}
+
+sub action_push_confirm () {
+ getpackage();
+ getpushinfo();
+ die unless @ARGV >= 1;
+ my $freshrepo = shift @ARGV;
+
+ my $initq = $poldbh->prepare(<<END);
+ SELECT taint_id, gitobjid FROM taints t
+ WHERE (package = ? OR package = '')
+END
+ $initq->execute($pkg);
+
+ my @objscatcmd = qw(git);
+ push @objscatcmd, qw(--git-dir), $freshrepo if length $freshrepo;
+ push @objscatcmd, qw(cat-file --batch);
+ debugcmd '|',@objscatcmd if $debuglevel>=2;
+
+ my @taintids;
+ my $chkinput = tempfile();
+ while (my $taint = $initq->fetchrow_hashref()) {
+ push @taintids, $taint->{taint_id};
+ print $chkinput $taint->{gitobjid}, "\n" or die $!;
+ printdebug '|> ', $taint->{gitobjid}, "\n" if $debuglevel>=2;
+ }
+ flush $chkinput or die $!;
+ seek $chkinput,0,0 or die $!;
+
+ my $checkpid = open CHKOUT, "-|" // die $!;
+ if (!$checkpid) {
+ open STDIN, "<&", $chkinput or die $!;
+ delete $ENV{GIT_ALTERNATE_OBJECT_DIRECTORIES};
+ # ^ recent versions of git set this in the environment of
+ # receive hooks. This can cause us to see things which
+ # the user is trying to abolish.
+ exec @objscatcmd or die $!;
+ }
+
+ my ($taintinfoq,$overridesanyq,$untaintq,$overridesq);
+
+ my $overridesstmt = <<END;
+ SELECT deliberately FROM taintoverrides WHERE (
+ 1=0
+END
+ my @overridesv = sort keys %deliberately;
+ $overridesstmt .= <<END foreach @overridesv;
+ OR deliberately = ?
+END
+ $overridesstmt .= <<END;
+ ) AND taint_id = ?
+ ORDER BY deliberately ASC
+END
+
+ my $mustreject=0;
+
+ while (my $taintid = shift @taintids) {
+ $!=0; $_ = <CHKOUT>;
+ die "($taintid @objscatcmd) $!" unless defined $_;
+ printdebug "|< ", $_ if $debuglevel>=2;
+
+ next if m/^\w+ missing$/;
+ die "($taintid @objscatcmd) $_ ?" unless m/^(\w+) (\w+) (\d+)\s/;
+ my ($objid,$objtype,$nbytes) = ($1,$2,$3);
+
+ my $drop;
+ (read CHKOUT, $drop, $nbytes) == $nbytes
+ or die "($taintid @objscatcmd) $!";
+
+ $!=0; $_ = <CHKOUT>;
+ die "($taintid @objscatcmd) $!" unless defined $_;
+ die "($taintid @objscatcmd) $_ ?" if m/\S/;
+
+ $taintinfoq ||= $poldbh->prepare(<<END);
+ SELECT package, time, comment FROM taints WHERE taint_id = ?
+END
+ $taintinfoq->execute($taintid);
+
+ my $ti = $taintinfoq->fetchrow_hashref();
+ die "($taintid)" unless $ti;
+
+ my $timeshow = defined $ti->{time}
+ ? " at time ".strftime("%Y-%m-%d %H:%M:%S Z", gmtime $ti->{time})
+ : "";
+ my $pkgshow = length $ti->{package}
+ ? "package $ti->{package}"
+ : "any package";
+
+ $stderr .= <<END;
+
+History contains tainted $objtype $objid
+Taint recorded$timeshow for $pkgshow
+Reason: $ti->{comment}
+END
+
+ printdebug "SQL overrides: @overridesv $taintid /\n$overridesstmt\n";
+
+ $overridesq ||= $poldbh->prepare($overridesstmt);
+ $overridesq->execute(@overridesv, $taintid);
+ my ($ovwhy) = $overridesq->fetchrow_array();
+ if (!defined $ovwhy) {
+ $overridesanyq ||= $poldbh->prepare(<<END);
+ SELECT 1 FROM taintoverrides WHERE taint_id = ? LIMIT 1
+END
+ $overridesanyq->execute($taintid);
+ my ($ovany) = $overridesanyq->fetchrow_array();
+ $stderr .= $ovany ? <<END : <<END;
+Could be forced using --deliberately. Consult documentation.
+END
+Uncorrectable error. If confused, consult administrator.
+END
+ $mustreject = 1;
+ } else {
+ $stderr .= <<END;
+Forcing due to --deliberately-$ovwhy
+END
+ $untaintq ||= $poldbh->prepare(<<END);
+ DELETE FROM taints WHERE taint_id = ?
+END
+ $untaintq->execute($taintid);
+ }
+ }
+ close CHKOUT;
+
+ if ($mustreject) {
+ $stderr .= <<END;
+
+Rejecting push due to questionable history.
+END
+ return 1;
+ }
+
+ if (length $freshrepo) {
+ if (!good_suite_has_suitable_vsn(sub { 1; })) {
+ stat $freshrepo or die "$freshrepo $!";
+ my $oldmode = ((stat _)[2]);
+ my $oldwrites = $oldmode & 0222;
+ # remove r and x bits which have corresponding w bits clear
+ my $newmode = $oldmode &
+ (~0555 | ($oldwrites << 1) | ($oldwrites >> 1));
+ printdebug sprintf "chmod %#o (was %#o) %s\n",
+ $newmode, $oldmode, $freshrepo;
+ chmod $newmode, $freshrepo or die $!;
+ utime undef, undef, $freshrepo or die $!;
+ }
+ }
+
+ return 0;
+}
+
+sub action_check_list () {
+ opendir L, "$repos" or die "$repos $!";
+ while (defined (my $dent = readdir L)) {
+ next unless $dent =~ m/^($package_re)\.git$/;
+ $pkg = $1;
+ statpackage();
+ next unless $pkg_exists;
+ next unless $pkg_secret;
+ print "$pkg\n" or die $!;
+ }
+ closedir L or die $!;
+ close STDOUT or die $!;
+ return 0;
+}
+
+$action =~ y/-/_/;
+my $fn = ${*::}{"action_$action"};
+if (!$fn) {
+ printdebug "dgit-repos-policy-debian: unknown action $action\n";
+ exit 0;
+}
+
+my $sleepy=0;
+my $rcode;
+
+my $db_busy_exception= 'Debian::Dgit::Policy::Debian::DB_BUSY';
+
+my @orgargv = @ARGV;
+
+for (;;) {
+ @ARGV = @orgargv;
+ eval {
+ poldb_setup(poldb_path($repos), sub {
+ $poldbh->{HandleError} = sub {
+ return 0 unless $poldbh->err == 5; # SQLITE_BUSY, not in .pm :-(
+ die bless { }, $db_busy_exception;
+ };
+
+ eval ($ENV{'DGIT_RPD_TEST_DBLOOP_HOOK'}//'');
+ die $@ if length $@;
+ # used by tests/tests/debpolicy-dbretry
+ });
+
+ $stderr = '';
+
+ $rcode = $fn->();
+ die unless defined $rcode;
+
+ $poldbh->commit;
+ };
+ last unless length $@;
+ die $@ unless ref $@ eq $db_busy_exception;
+
+ die if $sleepy >= 20;
+ $sleepy++;
+ print STDERR "[policy database busy, retrying (${sleepy}s)]\n";
+
+ eval { $poldbh->rollback; };