From d4dbd0c127b292fcac50afcd708699d5048a8e34 Mon Sep 17 00:00:00 2001
From: Levente Polyak <anthraxx@archlinux.org>
Date: Tue, 14 Nov 2023 19:23:28 +0100
Subject: [PATCH] fix(db-functions): acquire a lock around git clone and fetch
 operations

An initial git clone and especially a git fetch operation can occur
concurrently through multiple packagers and processes. Guard these git
operations with a fetch lock inside the actual git repository as well as
a special lock stored in the shared global lock directory before
starting an initial clone.

Fixes #49

Signed-off-by: Levente Polyak <anthraxx@archlinux.org>
---
 config               |  1 +
 config.local.git     |  1 +
 db-functions-git     | 18 +++++++++++++++---
 test/lib/common.bash |  1 +
 4 files changed, 18 insertions(+), 3 deletions(-)

diff --git a/config b/config
index e4af6b8..d8b8458 100644
--- a/config
+++ b/config
@@ -32,6 +32,7 @@ SOURCE_CLEANUP_KEEP=14
 
 LOCK_DELAY=10
 LOCK_TIMEOUT=300
+LOCK_DIR="/var/tmp"
 
 STAGING="$HOME/staging"
 export TMPDIR="/var/tmp"
diff --git a/config.local.git b/config.local.git
index a107a60..a17a8ee 100644
--- a/config.local.git
+++ b/config.local.git
@@ -68,3 +68,4 @@ GIT_STATE_REPO="/srv/repos/state"
 GITUSER=""
 
 GIT_PACKAGES_CACHE="/srv/repos/pkg-cache"
+LOCK_DIR="/srv/repos/lock"
diff --git a/db-functions-git b/db-functions-git
index 3cb138c..1bf6340 100644
--- a/db-functions-git
+++ b/db-functions-git
@@ -66,15 +66,27 @@ fetch_pkgbuild() {
 	# avoid git operations asking for terminal input
 	export GIT_TERMINAL_PROMPT=0
 
+	# double checked locking for fresh clones
 	if [[ ! -d $target ]]; then
-		if ! arch_git -c core.sharedRepository=group clone --origin origin --bare --mirror "${src}" "${target}"; then
-			return 1
+		lock 8 "${LOCK_DIR}/repo-clone-${pkgbase}.lock" "Locking git clone for ${pkgbase}"
+		if [[ ! -d $target ]]; then
+			if ! arch_git -c core.sharedRepository=group clone --origin origin --bare --mirror "${src}" "${target}"; then
+				lock_close 8
+				return 1
+			fi
+			lock_close 8
+			return 0
 		fi
-		return 0
+		lock_close 8
 	fi
+
+	lock 8 "${target}/.git/dbscripts.lock" "Locking git repo ${pkgbase}"
 	if ! arch_git -C "${target}" fetch --prune --prune-tags; then
+		lock_close 8
 		return 1
 	fi
+	lock_close 8
+
 	return 0
 }
 
diff --git a/test/lib/common.bash b/test/lib/common.bash
index aa5552e..40b134e 100644
--- a/test/lib/common.bash
+++ b/test/lib/common.bash
@@ -205,6 +205,7 @@ setup() {
 	GIT_PACKAGING_REPOS_URL="${TMP}/git-packages"
 	GIT_STATE_REPO="${TMP}/repository"
 	GIT_PACKAGES_CACHE="${TMP}/git-pkg-repos"
+	LOCK_DIR="${TMP}/lock"
 	GITUSER=""
 	AUTHORS="${TMP}/authors.conf"
 	PACKAGER_DOMAIN=localhost
-- 
GitLab