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) 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); int lockfd = open(lockfile, O_WRONLY|O_CREAT, 0660);
if (lockfd < 0) if (lockfd < 0)
err(1, "%s", lockfile); err(1, "%s", lockfile);
/* create NFS-safe lock */ if (lockf(lockfd, F_LOCK, 0) == -1)
if (fcntl(lockfd, F_SETLK, &fl) < 0) { err(1, "failed to aquire lock: %s", lockfile);
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);
return lockfd; return lockfd;
} }
static void release_lock(int lockfd) static void release_lock(int lockfd)
{ {
int r = flock(lockfd, LOCK_UN); if (lockf(lockfd, F_ULOCK, 0) == -1)
if (r == -1) err(1, "failed to release lock");
err(1, "flock");
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 */ /* 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); rename(partfile, outfile);
fetch_done: fetch_done:
unlink(lockfile);
release_lock(lockfd); 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; return status;
} }