daemon: Gracefully handle target mount point already existing.

Fixes guix/guix#903.

* nix/libutil/spawn.cc (bindMount): in the "regular file" case, only create a
  placeholder file if one doesn't already exist.

Change-Id: Ie46b2fef2cea5b2a052c4ec48d00e97bfc1ee506
Reported-by: Hilton Chain <hako@ultrarare.space>
Signed-off-by: Ludovic Courtès <ludo@gnu.org>
This commit is contained in:
Reepca Russelstein 2025-07-01 12:50:06 -05:00 committed by Ludovic Courtès
parent 6b42df3ad6
commit 05a669efa5
No known key found for this signature in database
GPG key ID: 090B11993D9AEBB5

View file

@ -538,8 +538,31 @@ void bindMount(Path source, Path target, bool readOnly)
return; return;
} }
else { else {
struct stat st2;
createDirs(dirOf(target)); createDirs(dirOf(target));
writeFile(target, ""); /* Alternate between trying to create placeholder file at target and
* checking for its existence and type */
while(true){
if(lstat(target.c_str(), &st2) != -1) {
if(!S_ISREG(st2.st_mode))
throw Error(format("mount target `%1%' exists but is not a regular file") % target);
break;
}
if(errno != ENOENT) {
throw SysError(format("stat'ing path `%1%'") % target);
}
AutoCloseFD fd = open(target.c_str(),
O_WRONLY | O_NOFOLLOW | O_CREAT | O_EXCL,
0600);
if(fd != -1) {
fd.close(); /* Now exists and is a fresh regular file */
break;
}
/* Note: because of O_CREAT | O_EXCL, EACCES can only mean a
* permission issue with the parent directory */
if(errno != EEXIST)
throw SysError(format("Creating placeholder regular file target mount `%1%'") % target);
}
} }
/* This may fail with EINVAL unless we specify MS_REC, specifically if we /* This may fail with EINVAL unless we specify MS_REC, specifically if we