mirror of
https://github.com/NixOS/nix
synced 2025-06-25 10:41:16 +02:00
optimise-store: Fix race condition in temporary link creation
Replace rand() with atomic counter for unique temp link names and add retry loop to handle EEXIST errors when multiple processes optimize the store simultaneously.
This commit is contained in:
parent
371fcf91c3
commit
ef05a2fa0f
1 changed files with 43 additions and 28 deletions
|
@ -223,7 +223,9 @@ void LocalStore::optimisePath_(Activity * act, OptimiseStats & stats,
|
||||||
its timestamp back to 0. */
|
its timestamp back to 0. */
|
||||||
MakeReadOnly makeReadOnly(mustToggle ? dirOfPath : "");
|
MakeReadOnly makeReadOnly(mustToggle ? dirOfPath : "");
|
||||||
|
|
||||||
std::filesystem::path tempLink = fmt("%1%/.tmp-link-%2%-%3%", config->realStoreDir, getpid(), rand());
|
/* Retry loop for temporary link creation to handle race conditions */
|
||||||
|
while (true) {
|
||||||
|
Path tempLink = makeTempPath(config->realStoreDir.get(), ".tmp-link");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
std::filesystem::create_hard_link(linkPath, tempLink);
|
std::filesystem::create_hard_link(linkPath, tempLink);
|
||||||
|
@ -237,24 +239,37 @@ void LocalStore::optimisePath_(Activity * act, OptimiseStats & stats,
|
||||||
printInfo("'%1%' has maximum number of links", linkPath);
|
printInfo("'%1%' has maximum number of links", linkPath);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
throw;
|
throw SysError("creating temporary link '%1%'", tempLink);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Atomically replace the old file with the new hard link. */
|
/* Atomically replace the old file with the new hard link. */
|
||||||
try {
|
try {
|
||||||
std::filesystem::rename(tempLink, path);
|
std::filesystem::rename(tempLink, path);
|
||||||
|
break; /* Success! */
|
||||||
} catch (std::filesystem::filesystem_error & e) {
|
} catch (std::filesystem::filesystem_error & e) {
|
||||||
|
try {
|
||||||
std::filesystem::remove(tempLink);
|
std::filesystem::remove(tempLink);
|
||||||
printError("unable to unlink '%1%'", tempLink);
|
} catch (...) {
|
||||||
|
/* Ignore errors removing the temp link */
|
||||||
|
}
|
||||||
|
|
||||||
if (e.code() == std::errc::too_many_links) {
|
if (e.code() == std::errc::too_many_links) {
|
||||||
/* Some filesystems generate too many links on the rename,
|
/* Some filesystems generate too many links on the rename,
|
||||||
rather than on the original link. (Probably it
|
rather than on the original link. (Probably it
|
||||||
temporarily increases the st_nlink field before
|
temporarily increases the st_nlink field before
|
||||||
decreasing it again.) */
|
decreasing it again.) */
|
||||||
debug("'%s' has reached maximum number of links", linkPath);
|
debug("%s has reached maximum number of links", linkPath);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
throw;
|
|
||||||
|
if (e.code() == std::errc::file_exists) {
|
||||||
|
/* Race condition: another process created the same temp link.
|
||||||
|
Try again with a different name. */
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw SysError("renaming temporary link '%1%' to '%2%'", tempLink, path);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
stats.filesLinked++;
|
stats.filesLinked++;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue