abuild-fetch: simplify and fix locking

Simplify locking by using lockf(3). It is POSIX compatible and should
work over NFS.

Fix download race condition when:
1) host A creates lockfile and aquire lock to fetch from distfiles
   mirror
2) host B opens the lockfile and waits for lock
3) host A gets 404 from distfiles, releases lock and deletes the
   lockfile, which host A has an open file handle for
4) host B gets lock of the deleted file and downloads file
5) host A retries download and creates a new lockfile, but is not
   blocked by host B, even if it should

Solve this by releaseing the lock, give the other processes a chance
to aquire it (using sleep(0)), and then only delete the lockfile if:
a) download was successful (no 404) or b) no-one else has a lock.

This reverts commit 281720ec39 (abuild-fetch: aquire a second lock
using flock(2))

fixes #10026
This commit is contained in:
Natanael Copa 2021-05-06 12:22:55 +02:00
parent 6a6145b9b3
commit 3da770ce35

View File

@ -80,45 +80,26 @@ int fork_exec(char *argv[], int showerr)
static int aquire_lock(const char *lockfile)
{
struct flock fl = {
.l_type = F_WRLCK,
.l_whence = SEEK_SET,
.l_start = 1,
.l_len = 0,
};
int lockfd = open(lockfile, O_WRONLY|O_CREAT, 0660);
if (lockfd < 0)
err(1, "%s", lockfile);
/* create NFS-safe lock */
if (fcntl(lockfd, F_SETLK, &fl) < 0) {
int i;
printf("Waiting for %s ...\n", lockfile);
for (i=0; i<10; i++) {
int r = fcntl(lockfd, F_SETLKW, &fl);
if (r == 0)
break;
if (r == -1 && errno != ESTALE)
err(1, "fcntl(F_SETLKW)");
sleep(1);
}
}
int r = flock(lockfd, LOCK_EX);
if (r == -1)
err(1, "flock: %s", lockfile);
if (lockf(lockfd, F_LOCK, 0) == -1)
err(1, "failed to aquire lock: %s", lockfile);
return lockfd;
}
static void release_lock(int lockfd)
{
int r = flock(lockfd, LOCK_UN);
if (r == -1)
err(1, "flock");
if (lockf(lockfd, F_ULOCK, 0) == -1)
err(1, "failed to release lock");
close(lockfd);
}
static int try_lock(int lockfd)
{
return lockf(lockfd, F_TLOCK, 0) == 0;
}
/* create or wait for an NFS-safe lockfile and fetch url with curl or wget */
@ -197,9 +178,14 @@ int fetch(char *url, const char *destdir, bool insecure)
rename(partfile, outfile);
fetch_done:
unlink(lockfile);
release_lock(lockfd);
lockfile[0] = '\0';
// give other processes the chance to aquire the lock if they have the file open
sleep(0);
if (status == 0 || try_lock(lockfd))
unlink(lockfile);
close(lockfd);
return status;
}