X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=dgit-repos-server;h=51160be419e3941a608c20f382a3f1861fd31acc;hb=48fdf45673b426357414c73c09688a275983a7bf;hp=0f7c4b0ddc7376c7aff715a105f5e194c6e2442c;hpb=4c3ff1c38c7db68f382041fb1068d092eaabd875;p=dgit.git diff --git a/dgit-repos-server b/dgit-repos-server index 0f7c4b0d..51160be4 100644 --- a/dgit-repos-server +++ b/dgit-repos-server @@ -2,8 +2,8 @@ # dgit-repos-push-receiver # # usages: -# .../dgit-repos-push-receiver KEYRING-AUTH-SPEC DGIT-REPOS-DIR --ssh -# .../dgit-repos-push-receiver KEYRING-AUTH-SPEC DGIT-REPOS-DIR PACKAGE +# .../dgit-repos-push-receiver SUITES KEYRING-AUTH-SPEC DGIT-REPOS-DIR --ssh +# .../dgit-repos-push-receiver SUITES KEYRING-AUTH-SPEC DGIT-REPOS-DIR PACKAGE # internal usage: # .../dgit-repos-push-receiver --pre-receive-hook PACKAGE # @@ -11,6 +11,9 @@ # # Works like git-receive-pack # +# SUITES is the name of a file which lists the permissible suites +# one per line (#-comments and blank lines ignored) +# # KEYRING-AUTH-SPEC is a :-separated list of # KEYRING.GPG,AUTH-SPEC # where AUTH-SPEC is one of @@ -20,24 +23,65 @@ use strict; # What we do is this: -# - extract the destination repo name somehow +# - extract the destination repo name # - make a hardlink clone of the destination repo # - provide the destination with a stunt pre-receive hook # - run actual git-receive-pack with that new destination # as a result of this the stunt pre-receive hook runs; it does this -# find the keyring(s) to use for verification -# verify the signed tag -# check that the signed tag has a suitable name -# parse the signed tag body to extract the intended -# distro and suite -# check that the distro is right -# check that the suite is the same as the branch we are -# supposed to update -# check that the signed tag refers to the same commit -# as the new suite -# check that the signer was correct -# push the signed tag to the actual repo -# push the new dgit branch head to the actual repo +# + understand what refs we are allegedly updating and +# check some correspondences: +# * we are updating only refs/tags/debian/* and refs/dgit/* +# * and only one of each +# * and the tag does not already exist +# and +# * recovering the suite name from the destination refs/dgit/ ref +# + disassemble the signed tag into its various fields and signature +# including: +# * parsing the first line of the tag message to recover +# the package name, version and suite +# * checking that the package name corresponds to the dest repo name +# * checking that the suite name is as recovered above +# + verify the signature on the signed tag +# and if necessary check that the keyid and package are listed in dm.txt +# + check various correspondences: +# * the suite is one of those permitted +# * the signed tag must refer to a commit +# * the signed tag commit must be the refs/dgit value +# * the name in the signed tag must correspond to its ref name +# * the tag name must be debian/ (massaged as needed) +# * the signed tag has a suitable name +# * the commit is a fast forward +# + push the signed tag and new dgit branch to the actual repo +# +# If the destination repo does not already exist, we need to make +# sure that we create it reasonably atomically, and also that +# we don't every have a destination repo containing no refs at all +# (because such a thing causes git-fetch-pack to barf). So then we +# do as above, except: +# - before starting, we take out our own lock for the destination repo +# - we create a prospective new destination repo by making a copy +# of _template +# - we use the prospective new destination repo instead of the +# actual new destination repo (since the latter doesn't exist) +# - we set up a post-receive hook as well, which +# + checks that exactly two refs were updated +# + touches a stamp file +# - after git-receive-pack exits, we rename the prospective +# destination repo into place +# +# Cleanup strategy: +# - We are crash-only +# - Temporary working trees and their locks are cleaned up +# opportunistically by a program which tries to take each lock and +# if successful deletes both the tree and the lockfile +# - Prospective working trees and their locks are cleaned up by +# a program which tries to take each lock and if successful +# deletes any prospective working tree and the lock (but not +# of course any actual tree) +# - It is forbidden to _remove_ the lockfile without removing +# the corresponding temporary tree, as the lockfile is also +# a stampfile whose presence indicates that there may be +# cleanup to do use POSIX; use Fcntl qw(:flock); @@ -132,7 +176,6 @@ sub parsetag () { open PT, ">dgit-tmp/plaintext" or die $!; open DS, ">dgit-tmp/plaintext.asc" or die $!; open T, "-|", qw(git cat-file tag), $tagval or die $!; - my %tagh; for (;;) { $!=0; $_=; defined or die $!; print PT or die $!; @@ -241,18 +284,32 @@ sub verifytag () { reject "key not found in keyrings"; } -sub checktag () { - tagh1('object') eq $branchval or die; +sub checks () { +fixme check the suite against the approved list tagh1('type') eq 'commit' or die; + tagh1('object') eq $commit or die; tagh1('tag') eq $tagname or die; my $v = $version; $v =~ y/~:/_%/; $tagname eq "debian/$v" or die; - check fast forward; + # check that our ref is being fast-forwarded + if ($oldcommit =~ m/[^0]/) { + $?=0; $!=0; my $mb = `git merge-base $commit $oldcommit`; + chomp $mb; + $mb eq $oldcommit or reject "not fast forward on dgit branch"; + } } +sub onwardpush () { + $!=0; + my $r = system (qw(git send-pack), + $destrepo, + "$commit:refs/dgit/$suite", + "$tagval:refs/tags/$tagname"); + !$r or die "onward push failed: $r $!"; +} sub stunthook () { chdir $workrepo or die "chdir $workrepo: $!"; @@ -260,8 +317,8 @@ sub stunthook () { readupdates(); parsetag(); verifytag(); - checktag(); -... ... + checks(); + onwardpush(); } #----- arg parsing and main program ----- @@ -281,10 +338,14 @@ sub parseargs () { exit 0; } - die unless @ARGV>=2; + die unless @ARGV>=3; + + die if $ARGV[0] =~ m/^-/; + $suitesfile = shift @ARGV; die if $ARGV[0] =~ m/^-/; $ENV{'DGIT_RPR_KEYRINGS'} = shift @ARGV; + die if $ARGV[0] =~ m/^-/; $dgitrepos = shift @ARGV; @@ -308,6 +369,10 @@ sub parseargs () { or die "requested command $cmd not understood"; $method = $1; $pkg = $2; + my $func = $method; + $func =~ y/-/_/; + $func = $main::{"main__$func"}; + &$func; } else { die; } @@ -315,7 +380,7 @@ sub parseargs () { $destrepo = "$dgitrepos/$pkg.git"; } -sub main () { +sub main__git_receive_pack () { parseargs(); fixme check method; makeworkingclone();