nix-super/src/libstore/pathlocks.cc

194 lines
4.7 KiB
C++
Raw Normal View History

#include "pathlocks.hh"
#include "util.hh"
2016-12-09 14:26:43 +02:00
#include "sync.hh"
#include "signals.hh"
2003-09-11 11:31:29 +03:00
#include <cerrno>
#include <cstdlib>
2003-09-11 11:31:29 +03:00
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/file.h>
namespace nix {
2017-01-25 13:51:35 +02:00
AutoCloseFD openLockFile(const Path & path, bool create)
2006-06-20 20:48:10 +03:00
{
AutoCloseFD fd;
2016-06-09 17:15:58 +03:00
fd = open(path.c_str(), O_CLOEXEC | O_RDWR | (create ? O_CREAT : 0), 0600);
2016-07-11 22:44:44 +03:00
if (!fd && (create || errno != ENOENT))
throw SysError("opening lock file '%1%'", path);
2006-06-20 20:48:10 +03:00
2017-01-25 13:51:35 +02:00
return fd;
2006-06-20 20:48:10 +03:00
}
void deleteLockFile(const Path & path, int fd)
2006-06-20 20:48:10 +03:00
{
/* Get rid of the lock file. Have to be careful not to introduce
races. Write a (meaningless) token to the file to indicate to
2006-06-20 20:48:10 +03:00
other processes waiting on this lock that the lock is stale
(deleted). */
unlink(path.c_str());
writeFull(fd, "d");
2006-06-20 20:48:10 +03:00
/* Note that the result of unlink() is ignored; removing the lock
file is an optimisation, not a necessity. */
}
bool lockFile(int fd, LockType lockType, bool wait)
{
int type;
if (lockType == ltRead) type = LOCK_SH;
else if (lockType == ltWrite) type = LOCK_EX;
else if (lockType == ltNone) type = LOCK_UN;
else abort();
if (wait) {
while (flock(fd, type) != 0) {
checkInterrupt();
if (errno != EINTR)
throw SysError("acquiring/releasing lock");
2016-07-19 01:50:27 +03:00
else
return false;
}
} else {
while (flock(fd, type | LOCK_NB) != 0) {
checkInterrupt();
if (errno == EWOULDBLOCK) return false;
2015-07-17 20:24:28 +03:00
if (errno != EINTR)
throw SysError("acquiring/releasing lock");
}
}
return true;
}
PathLocks::PathLocks()
2003-11-21 18:05:19 +02:00
: deletePaths(false)
{
}
PathLocks::PathLocks(const PathSet & paths, const std::string & waitMsg)
: deletePaths(false)
{
lockPaths(paths, waitMsg);
}
bool PathLocks::lockPaths(const PathSet & paths,
const std::string & waitMsg, bool wait)
{
assert(fds.empty());
2015-07-17 20:24:28 +03:00
/* Note that `fds' is built incrementally so that the destructor
will only release those locks that we have already acquired. */
/* Acquire the lock for each path in sorted order. This ensures
that locks are always acquired in the same order, thus
preventing deadlocks. */
2015-07-17 20:24:28 +03:00
for (auto & path : paths) {
checkInterrupt();
Path lockPath = path + ".lock";
debug("locking path '%1%'", path);
AutoCloseFD fd;
while (1) {
2015-07-17 20:24:28 +03:00
/* Open/create the lock file. */
fd = openLockFile(lockPath, true);
/* Acquire an exclusive lock. */
if (!lockFile(fd.get(), ltWrite, false)) {
if (wait) {
if (waitMsg != "") printError(waitMsg);
lockFile(fd.get(), ltWrite, true);
} else {
/* Failed to lock this path; release all other
locks. */
unlock();
return false;
}
}
debug("lock acquired on '%1%'", lockPath);
/* Check that the lock file hasn't become stale (i.e.,
hasn't been unlinked). */
struct stat st;
if (fstat(fd.get(), &st) == -1)
throw SysError("statting lock file '%1%'", lockPath);
if (st.st_size != 0)
/* This lock file has been unlinked, so we're holding
a lock on a deleted file. This means that other
processes may create and acquire a lock on
`lockPath', and proceed. So we must retry. */
debug("open lock file '%1%' has become stale", lockPath);
else
break;
}
/* Use borrow so that the descriptor isn't closed. */
fds.push_back(FDPair(fd.release(), lockPath));
}
return true;
}
PathLocks::~PathLocks()
{
try {
unlock();
} catch (...) {
ignoreException();
}
}
void PathLocks::unlock()
{
2015-07-17 20:24:28 +03:00
for (auto & i : fds) {
if (deletePaths) deleteLockFile(i.second, i.first);
2006-06-20 20:48:10 +03:00
2015-07-17 20:24:28 +03:00
if (close(i.first) == -1)
printError(
"error (ignored): cannot close lock file on '%1%'",
i.second);
2006-06-20 20:48:10 +03:00
debug("lock released on '%1%'", i.second);
2003-11-21 18:05:19 +02:00
}
fds.clear();
2003-11-21 18:05:19 +02:00
}
void PathLocks::setDeletion(bool deletePaths)
{
this->deletePaths = deletePaths;
}
FdLock::FdLock(int fd, LockType lockType, bool wait, std::string_view waitMsg)
: fd(fd)
{
if (wait) {
if (!lockFile(fd, lockType, false)) {
printInfo("%s", waitMsg);
acquired = lockFile(fd, lockType, true);
}
} else
acquired = lockFile(fd, lockType, false);
}
}