X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=dgit.git;a=blobdiff_plain;f=infra%2Fdgit-repos-server;h=f2f30884854a83e2403b4142235f8fe4617ecd4c;hp=ae30eeff20d958693d911d6b5f6a931e5decd1b0;hb=9a69602bffdc82084152fd322862197c51ea7d1b;hpb=f6cd040652129d18f3d7fe53b6bb62e5d0c79ec1 diff --git a/infra/dgit-repos-server b/infra/dgit-repos-server index ae30eeff..f2f30884 100755 --- a/infra/dgit-repos-server +++ b/infra/dgit-repos-server @@ -9,7 +9,7 @@ # --suites=SUITES-FILE default DISTRO-DIR/suites # --policy-hook=POLICY-HOOK default DISTRO-DIR/policy-hook # --dgit-live=DGIT-LIVE-DIR default DISTRO-DIR/dgit-live -# (DISTRO-DIR is not used other than as default) +# (DISTRO-DIR is not used other than as default and to pass to policy hook) # internal usage: # .../dgit-repos-server --pre-receive-hook PACKAGE # @@ -117,7 +117,7 @@ $SIG{__WARN__} = sub { die $_[0]; }; # cleanup to do # # Policy hook script is invoked like this: -# POLICY-HOOK-SCRIPT DISTRO DGIT-REPOS-DIR DGIT-LIVE-DIR ACTION... +# POLICY-HOOK-SCRIPT DISTRO DGIT-REPOS-DIR DGIT-LIVE-DIR DISTRO-DIR ACTION... # ie. # POLICY-HOOK-SCRIPT ... check-list [...] # POLICY-HOOK-SCRIPT ... check-package PACKAGE [...] @@ -179,6 +179,7 @@ our $distro; our $suitesfile; our $policyhook; our $dgitlive; +our $distrodir; our $destrepo; our $workrepo; our $keyrings; @@ -233,12 +234,9 @@ sub lockrealtree () { locksometree(realdestrepo); } -sub mkrepotmp () { - my $tmpdir = "$dgitrepos/_tmp"; - return if mkdir $tmpdir; - return if $! == EEXIST; - die $!; -} +sub mkrepotmp () { ensuredir "$dgitrepos/_tmp" }; + +sub removedtagsfile () { "$dgitrepos/_removed-tags/$package"; } sub recorderror ($) { my ($why) = @_; @@ -270,11 +268,11 @@ sub policyhook { my ($policyallowbits, @polargs) = @_; # => ($exitstatuspolicybitmap); die if $policyallowbits & ~0x3e; - my @cmd = ($policyhook,$distro,$dgitrepos,$dgitlive,@polargs); + my @cmd = ($policyhook,$distro,$dgitrepos,$dgitlive,$distrodir,@polargs); debugcmd '+',@cmd; my $r = system @cmd; die "system: $!" if $r < 0; - die "hook (".(shellquote @cmd).") failed ($?)" + die "dgit-repos-server: policy hook failed (or rejected) ($?)\n" if $r & ~($policyallowbits << 8); printdebug sprintf "hook => %#x\n", $r; return $r >> 8; @@ -301,15 +299,21 @@ sub movetogarbage () { # purposes (and, I guess, recovery from mistakes). This is either # $garbage or $garbage-old. if (stat_exists "$garbagerepo") { + printdebug "movetogarbage: rmtree $garbagerepo-tmp\n"; rmtree "$garbagerepo-tmp"; if (rename "$garbagerepo-old", "$garbagerepo-tmp") { + printdebug "movetogarbage: $garbagerepo-old -> -tmp, rmtree\n"; rmtree "$garbagerepo-tmp"; } else { die "$garbagerepo $!" unless $!==ENOENT; + printdebug "movetogarbage: $garbagerepo-old -> -tmp\n"; } + printdebug "movetogarbage: $garbagerepo -> -old\n"; rename "$garbagerepo", "$garbagerepo-old" or die "$garbagerepo $!"; } - rename realdestrepo, $garbagerepo + my $real = realdestrepo; + printdebug "movetogarbage: $real -> $garbagerepo\n"; + rename $real, $garbagerepo or $! == ENOENT or die "$garbagerepo $!"; } @@ -492,7 +496,7 @@ sub parsetag () { while (length) { if (s/^distro\=(\S+) //) { die "$1 != $distro" unless $1 eq $distro; - } elsif (s/^(--deliberately-$package_re) //) { + } elsif (s/^(--deliberately-$deliberately_re) //) { push @deliberatelies, $1; } elsif (s/^supersede:(\S+)=(\w+) //) { die "supersede $1 twice" if defined $supersedes{$1}; @@ -628,11 +632,21 @@ sub checksuite () { } sub checktagnoreplay () { - # We check that the signed tag mentions the name and value of - # (a) in the case of FRESHREPO all tags in the repo; - # (b) in the case of just NOFFCHECK all tags referring to - # the current head for the suite (there must be at least one). - # This prevents a replay attack using an earlier signed tag. + # We need to prevent a replay attack using an earlier signed tag. + # We also want to archive in the history anything + # + # We check that the signed tag mentions the name and tag object id of + # + # (a) In the case of FRESHREPO all tags and refs/heads/heads in the + # repo. That is, effectively, all the things we are deleting. + # This prevents any tag implying a FRESHREPO push being replayed + # into a different state of the repo. + # + # (b) In the case of just NOFFCHECK all tags referring to + # the current head for the suite (there must be at least one). + # This guarantees that the + # + # return unless $policy & (FRESHREPO|NOFFCHECK); my $garbagerepo = "$dgitrepos/${package}_garbage"; @@ -673,14 +687,29 @@ sub checktagnoreplay () { my @problems; git_for_each_tag_referring($onlyreferring, sub { - my ($objid,$fullrefname,$tagname) = @_; - printdebug "checktagnoreplay - overwriting $fullrefname=$objid\n"; + my ($tagobjid,$refobjid,$fullrefname,$tagname) = @_; + printdebug "checktagnoreplay - overwriting". + " $fullrefname=$tagobjid->$refobjid\n"; my $supers = $supersedes{$fullrefname}; if (!defined $supers) { - push @problems, "does not supersede $fullrefname"; - } elsif ($supers ne $objid) { + printdebug "checktagnoreply - fallbacks\n"; + my $super_fallback = 0; + foreach my $didsuper (sort keys %supersedes) { + my $didsuper_tagobjid = $supersedes{$didsuper}; + my $didsuper_refobjid = git_rev_parse $didsuper_tagobjid; + printdebug "checktagnoreply - fallback". + " $didsuper=$didsuper_refobjid->$didsuper_tagobjid\n"; + last if + $refobjid ne $didsuper_refobjid + and is_fast_fwd($refobjid, $didsuper_refobjid); + printdebug "checktagnoreply - fallback $didsuper OK\n"; + $super_fallback = 1; + } + push @problems, "does not supersede $fullrefname" + unless $super_fallback; + } elsif ($supers ne $tagobjid) { push @problems, - "supersedes $fullrefname=$supers but previously $fullrefname=$objid"; + "supersedes $fullrefname=$supers but previously $fullrefname=$tagobjid"; } else { # ok; } @@ -815,7 +844,8 @@ our %indistrodir = ( 'dgit-live' => \$dgitlive, ); -our @hookenvs = qw(distro suitesfile policyhook dgitlive keyrings dgitrepos); +our @hookenvs = qw(distro suitesfile policyhook + dgitlive keyrings dgitrepos distrodir); # workrepo and destrepo handled ad-hoc @@ -912,9 +942,9 @@ sub parseargsdispatch () { exit 0; } - $distro = $ENV{'DGIT_DRS_DISTRO'} = argval(); - my $distrodir = argval(); - $keyrings = $ENV{'DGIT_DRS_KEYRINGS'} = argval(); + $distro = argval(); + $distrodir = argval(); + $keyrings = argval(); foreach my $dk (keys %indistrodir) { ${ $indistrodir{$dk} } = "$distrodir/$dk";