From 972202563e62f7be43431e31497f1d2a19ada0d8 Mon Sep 17 00:00:00 2001 From: Ian Jackson Date: Sun, 31 May 2015 11:49:42 +0100 Subject: infra: Finish replay prevention --- infra/dgit-repos-server | 90 +++++++++++++++++++++++++++++++------------------ 1 file changed, 57 insertions(+), 33 deletions(-) (limited to 'infra/dgit-repos-server') diff --git a/infra/dgit-repos-server b/infra/dgit-repos-server index 1fcc1fd..d990aee 100755 --- a/infra/dgit-repos-server +++ b/infra/dgit-repos-server @@ -137,18 +137,19 @@ $SIG{__WARN__} = sub { die $_[0]; }; # of any expected bits set). So, eg, exit 0 means "continue normally" # and would be appropriate for an unknown action. # -# cwd for push and push-confirm is a temporary repo where the -# to-be-pushed objects have been received; TAGNAME is the -# version-based tag +# cwd for push and push-confirm is a temporary repo where the incoming +# objects have been received; TAGNAME is the version-based tag. # # FRESH-REPO is '' iff the repo for this package already existed, or # the pathname of the newly-created repo which will be renamed into # place if everything goes well. (NB that this is generally not the # same repo as the cwd, because the objects are first received into a -# temporary repo so they can be examined.) +# temporary repo so they can be examined.) In this case FRESH-REPO +# contains exactly the objects and refs that will appear in the +# destination if push-confirm approves. # -# if push requested FRESHREPO, push-confirm happens in said fresh repo -# and FRESH-REPO is guaranteed not to be ''. +# if push requested FRESHREPO, push-confirm happens in the old working +# repo and FRESH-REPO is guaranteed not to be ''. # # policy hook for a particular package will be invoked only once at # a time - (see comments about DGIT-REPOS-DIR, above) @@ -188,6 +189,7 @@ our @lockfhs; our @deliberatelies; our %supersedes; our $policy; +our @policy_args; #----- utilities ----- @@ -295,13 +297,8 @@ sub mkrepo_fromtemplate ($) { sub movetogarbage () { # realdestrepo must have been locked - ensuredir "$dgitrepos/_removed-tags"; - open PREVIOUS, ">>", removedtagsfile or die removedtagsfile." $!"; - git_for_each_ref(debiantag('*'), sub { - my ($objid,$objtype,$fullrefname,$reftail) = @_; - print PREVIOUS "\n$objid $reftail .\n" or die $!; - }); - close PREVIOUS or die $!; + my $real = realdestrepo; + return unless stat_exists $real; my $garbagerepo = "$dgitrepos/${package}_garbage"; # We arrange to always keep at least one old tree, for recovery @@ -319,7 +316,15 @@ sub movetogarbage () { printdebug "movetogarbage: $garbagerepo -> -old\n"; rename "$garbagerepo", "$garbagerepo-old" or die "$garbagerepo $!"; } - my $real = realdestrepo; + + ensuredir "$dgitrepos/_removed-tags"; + open PREVIOUS, ">>", removedtagsfile or die removedtagsfile." $!"; + git_for_each_ref(debiantag('*'), sub { + my ($objid,$objtype,$fullrefname,$reftail) = @_; + print PREVIOUS "\n$objid $reftail .\n" or die $!; + }, $real); + close PREVIOUS or die $!; + printdebug "movetogarbage: $real -> $garbagerepo\n"; rename $real, $garbagerepo or $! == ENOENT @@ -424,7 +429,7 @@ sub maybeinstallprospective () { printdebug "install $destrepo => ".realdestrepo."\n"; rename $destrepo, realdestrepo or die $!; - remove "$destrepo.lock" or die $!; + remove realdestrepo.".lock" or die $!; } sub main__git_receive_pack () { @@ -770,8 +775,8 @@ sub checks () { lockrealtree(); - my @policy_args = ($package,$version,$suite,$tagname, - join(",",@deliberatelies)); + @policy_args = ($package,$version,$suite,$tagname, + join(",",@deliberatelies)); $policy = policyhook(NOFFCHECK|FRESHREPO, 'push', @policy_args); checktagnoreplay(); @@ -784,26 +789,23 @@ sub checks () { chomp $mb; $mb eq $oldcommit or reject "not fast forward on dgit branch"; } - if ($policy & FRESHREPO) { - # This is troublesome. We have been asked by the policy hook - # to receive the push into a fresh repo. But of course we - # have actually already mostly received the push into the working - # repo. (This is unavoidable because the instruction to use a new - # repo comes ultimately from the signed tag for the dgit push, - # which has to have been received into some repo.) + # It's a bit late to be discovering this here, isn't it ? + # + # What we do is: Generate a fresh destination repo right now, + # and arrange to treat it from now on as if it were a + # prospective repo. + # + # The presence of this fresh destination repo is detected by + # the parent, which responds by making a fresh master repo + # from the template. (If the repo didn't already exist then + # $destrepo was _prospective, and we change it here. This is + # OK because the parent's check for _fresh persuades it not to + # use _prospective.) # - # So what we do is generate a fresh working repo right now and - # push the head and tag into it. The presence of this fresh - # working repo is detected by the parent, which responds by - # making a fresh master repo from the template. - $destrepo = "${workrepo}_fresh"; # workrepo lock covers mkrepo_fromtemplate $destrepo; } - - my $willinstall = ($destrepo eq realdestrepo ? '' : $destrepo); - policyhook(0, 'push-confirm', @policy_args, $willinstall); } sub onwardpush () { @@ -817,6 +819,28 @@ sub onwardpush () { !$r or die "onward push to $destrepo failed: $r $!"; } +sub finalisepush () { + if ($destrepo eq realdestrepo) { + policyhook(0, 'push-confirm', @policy_args, ''); + onwardpush(); + } else { + # We are to receive the push into a new repo (perhaps + # because the policy push hook asked us to with FRESHREPO, or + # perhaps because the repo didn't exist before). + # + # We want to provide the policy push-confirm hook with a repo + # which looks like the one which is going to be installed. + # The working repo is no good because it might contain + # previous history. + # + # So we push the objects into the prospective new repo right + # away. If the hook declines, we decline, and the prospective + # repo is never installed. + onwardpush(); + policyhook(0, 'push-confirm', @policy_args, $destrepo); + } +} + sub stunthook () { printdebug "stunthook in $workrepo\n"; chdir $workrepo or die "chdir $workrepo: $!"; @@ -825,7 +849,7 @@ sub stunthook () { parsetag(); verifytag(); checks(); - onwardpush(); + finalisepush(); printdebug "stunthook done.\n"; } -- cgit v1.2.3