From f4f514f696e7a6579f287869a1e264134aa50b3b Mon Sep 17 00:00:00 2001 From: Ian Jackson Date: Sat, 21 Mar 2015 17:32:10 +0000 Subject: [PATCH] dgit-repos-server: Sort out locking hierarchy (and some comments) --- infra/dgit-repos-server | 60 +++++++++++++++++++++++++++++++++-------- 1 file changed, 49 insertions(+), 11 deletions(-) diff --git a/infra/dgit-repos-server b/infra/dgit-repos-server index 551efff9..b0064e79 100755 --- a/infra/dgit-repos-server +++ b/infra/dgit-repos-server @@ -22,7 +22,29 @@ use strict; -# What we do is this: +# DGIT-REPOS-DIR contains: +# git tree (or other object) lock (in acquisition order, outer first) +# +# _tmp/PACKAGE_prospective ! } SAME.lock, held during receive-pack +# +# _tmp/PACKAGE_incoming$$ ! } SAME.lock, held during receive-pack +# _tmp/PACKAGE_incoming$$_fresh ! } +# +# PACKAGE.git } PACKAGE.git.lock +# PACKAGE_garbage } (also covers executions of +# PACKAGE_garbage-old } policy hook script for PACKAGE) +# PACKAGE_garbage-tmp } +# +# leaf locks, held during brief operaton only: +# +# _empty } SAME.lock +# _empty.new } +# +# _template } SAME.lock +# +# locks marked ! may be held during client data transfer + +# What we do on push is this: # - extract the destination repo name # - make a hardlink clone of the destination repo # - provide the destination with a stunt pre-receive hook @@ -44,13 +66,16 @@ use strict; # + 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 suite is one of those permitted # * the signed tag has a suitable name -# * the commit is a fast forward +# * run the "push" policy hook +# * replay prevention for --deliberately-not-fast-forward +# * check the commit is a fast forward +# * handle a request from the policy hook for a fresh repo # + push the signed tag and new dgit branch to the actual repo # # If the destination repo does not already exist, we need to make @@ -63,8 +88,6 @@ use strict; # 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 -# + touches a stamp file # - after git-receive-pack exits, we # + check that the prospective repo contains a tag and head # + rename the prospective destination repo into place @@ -206,14 +229,15 @@ sub mkemptyrepo ($$) { sub mkrepo_fromtemplate ($) { my ($dir) = @_; my $template = "$dgitrepos/_template"; - debug "copy tempalate $template -> $dir"; + acquiretree($template,1); + debug "copy template $template -> $dir"; my $r = system qw(cp -a --), $template, $dir; !$r or die "create new repo $dir failed: $r $!"; } sub movetogarbage () { + # $realdestrepo must have been locked my $garbagerepo = "$dgitrepos/${package}_garbage"; - my $lfh =acquiretree($garbagerepo,1); # We arrange to always keep at least one old tree, for anti-rewind # purposes (and, I guess, recovery from mistakes). This is either # $garbage or $garbage-old. @@ -229,7 +253,6 @@ sub movetogarbage () { rename $realdestrepo, $garbagerepo or $! == ENOENT or die "$garbagerepo $!"; - close $lfh; } sub onwardpush () { @@ -256,7 +279,9 @@ sub makeworkingclone () { mkrepotmp(); $workrepo = "$dgitrepos/_tmp/${package}_incoming$$"; acquiretree($workrepo, 1); + my $lfh = acquiretree($realdestrepo, 1); runcmd qw(git clone -l -q --mirror), $destrepo, $workrepo; + close $lfh; rmtree "${workrepo}_fresh"; } @@ -320,7 +345,11 @@ sub maybeinstallprospective () { die Dumper(\%got)." -- missing refs in new repo" if grep { !$_ } values %got; - movetogarbage; # in case of FRESHREPO + acquiretree($realdestrepo,1); + + if ($destrepo eq "${workrepo}_fresh") { + movetogarbage; + } debug "install $destrepo => $realdestrepo"; rename $destrepo, $realdestrepo or die $!; @@ -545,7 +574,7 @@ sub checktagnoreplay () { return unless $policy & (FRESHREPO|NOFFCHECK); my $garbagerepo = "$dgitrepos/${package}_garbage"; - acquiretree($garbagerepo,1); + acquiretree($realdestrepo,1); local $ENV{GIT_DIR}; foreach my $garb ("$garbagerepo", "$garbagerepo-old") { @@ -625,6 +654,8 @@ sub checks () { debug "translated version $v"; $tagname eq "debian/$v" or die; + acquiretree($realdestrepo,1); + $policy = policyhook(NOFFCHECK|FRESHREPO, 'push',$package, $version,$suite,$tagname, join(",",@delberatelies)); @@ -684,7 +715,10 @@ sub fixmissing__git_upload_pack () { } sub main__git_upload_pack () { - runcmd qw(git upload-pack), $destrepo; + my $lfh = acquiretree($destrepo,1); + chdir $destrepo or die "$destrepo: $!"; + close $lfh; + runcmd qw(git upload-pack), "."; } #----- arg parsing and main program ----- @@ -758,12 +792,16 @@ sub parseargsdispatch () { reject "unknown method" unless $mainfunc; + my $lfh = acquiretree($realdestrepo,1); + my ($policy, $pollock) = policyhook(FRESHREPO,'check-package',$package); if ($policy & FRESHREPO) { movetogarbage; } close $pollock or die $!; + close $lfh; + if (stat_exists $realdestrepo) { $destrepo = $realdestrepo; } else { -- 2.30.2