mirror of
https://github.com/privatevoid-net/nix-super.git
synced 2024-11-24 06:56:14 +02:00
Create no-op Window pathlocks implementation
This keeps the call sites simple, eventually this should be filled in.
This commit is contained in:
parent
05b9dac754
commit
2248a3f545
11 changed files with 232 additions and 182 deletions
|
@ -22,6 +22,7 @@
|
||||||
#include "experimental-features.hh"
|
#include "experimental-features.hh"
|
||||||
|
|
||||||
using namespace nix;
|
using namespace nix;
|
||||||
|
using namespace nix::unix;
|
||||||
using std::cin;
|
using std::cin;
|
||||||
|
|
||||||
static void handleAlarm(int sig) {
|
static void handleAlarm(int sig) {
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
using namespace nix::unix;
|
||||||
|
|
||||||
static std::string gcSocketPath = "/gc-socket/socket";
|
static std::string gcSocketPath = "/gc-socket/socket";
|
||||||
static std::string gcRootsDir = "gcroots";
|
static std::string gcRootsDir = "gcroots";
|
||||||
|
|
|
@ -52,6 +52,8 @@
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
using namespace nix::unix;
|
||||||
|
|
||||||
std::string LocalStoreConfig::doc()
|
std::string LocalStoreConfig::doc()
|
||||||
{
|
{
|
||||||
return
|
return
|
||||||
|
|
|
@ -36,6 +36,9 @@ INCLUDE_libstore := -I $(d) -I $(d)/build
|
||||||
ifdef HOST_UNIX
|
ifdef HOST_UNIX
|
||||||
INCLUDE_libstore += -I $(d)/unix
|
INCLUDE_libstore += -I $(d)/unix
|
||||||
endif
|
endif
|
||||||
|
ifdef HOST_WINDOWS
|
||||||
|
INCLUDE_libstore += -I $(d)/windows
|
||||||
|
endif
|
||||||
|
|
||||||
libstore_CXXFLAGS += \
|
libstore_CXXFLAGS += \
|
||||||
$(INCLUDE_libutil) $(INCLUDE_libstore) $(INCLUDE_libstore) \
|
$(INCLUDE_libutil) $(INCLUDE_libstore) $(INCLUDE_libstore) \
|
||||||
|
|
|
@ -9,6 +9,8 @@
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
using namespace nix::unix;
|
||||||
|
|
||||||
#if __linux__
|
#if __linux__
|
||||||
|
|
||||||
static std::vector<gid_t> get_group_list(const char *username, gid_t group_id)
|
static std::vector<gid_t> get_group_list(const char *username, gid_t group_id)
|
||||||
|
|
|
@ -6,69 +6,9 @@
|
||||||
#include <cerrno>
|
#include <cerrno>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <sys/file.h>
|
|
||||||
|
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
|
||||||
AutoCloseFD openLockFile(const Path & path, bool create)
|
|
||||||
{
|
|
||||||
AutoCloseFD fd;
|
|
||||||
|
|
||||||
fd = open(path.c_str(), O_CLOEXEC | O_RDWR | (create ? O_CREAT : 0), 0600);
|
|
||||||
if (!fd && (create || errno != ENOENT))
|
|
||||||
throw SysError("opening lock file '%1%'", path);
|
|
||||||
|
|
||||||
return fd;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void deleteLockFile(const Path & path, int fd)
|
|
||||||
{
|
|
||||||
/* Get rid of the lock file. Have to be careful not to introduce
|
|
||||||
races. Write a (meaningless) token to the file to indicate to
|
|
||||||
other processes waiting on this lock that the lock is stale
|
|
||||||
(deleted). */
|
|
||||||
unlink(path.c_str());
|
|
||||||
writeFull(fd, "d");
|
|
||||||
/* 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");
|
|
||||||
else
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
while (flock(fd, type | LOCK_NB) != 0) {
|
|
||||||
checkInterrupt();
|
|
||||||
if (errno == EWOULDBLOCK) return false;
|
|
||||||
if (errno != EINTR)
|
|
||||||
throw SysError("acquiring/releasing lock");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
PathLocks::PathLocks()
|
PathLocks::PathLocks()
|
||||||
: deletePaths(false)
|
: deletePaths(false)
|
||||||
{
|
{
|
||||||
|
@ -82,68 +22,6 @@ PathLocks::PathLocks(const PathSet & paths, const std::string & waitMsg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool PathLocks::lockPaths(const PathSet & paths,
|
|
||||||
const std::string & waitMsg, bool wait)
|
|
||||||
{
|
|
||||||
assert(fds.empty());
|
|
||||||
|
|
||||||
/* 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. */
|
|
||||||
for (auto & path : paths) {
|
|
||||||
checkInterrupt();
|
|
||||||
Path lockPath = path + ".lock";
|
|
||||||
|
|
||||||
debug("locking path '%1%'", path);
|
|
||||||
|
|
||||||
AutoCloseFD fd;
|
|
||||||
|
|
||||||
while (1) {
|
|
||||||
|
|
||||||
/* 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()
|
PathLocks::~PathLocks()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
@ -154,40 +32,10 @@ PathLocks::~PathLocks()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void PathLocks::unlock()
|
|
||||||
{
|
|
||||||
for (auto & i : fds) {
|
|
||||||
if (deletePaths) deleteLockFile(i.second, i.first);
|
|
||||||
|
|
||||||
if (close(i.first) == -1)
|
|
||||||
printError(
|
|
||||||
"error (ignored): cannot close lock file on '%1%'",
|
|
||||||
i.second);
|
|
||||||
|
|
||||||
debug("lock released on '%1%'", i.second);
|
|
||||||
}
|
|
||||||
|
|
||||||
fds.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void PathLocks::setDeletion(bool deletePaths)
|
void PathLocks::setDeletion(bool deletePaths)
|
||||||
{
|
{
|
||||||
this->deletePaths = 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,22 +5,6 @@
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
/**
|
|
||||||
* Open (possibly create) a lock file and return the file descriptor.
|
|
||||||
* -1 is returned if create is false and the lock could not be opened
|
|
||||||
* because it doesn't exist. Any other error throws an exception.
|
|
||||||
*/
|
|
||||||
AutoCloseFD openLockFile(const Path & path, bool create);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Delete an open lock file.
|
|
||||||
*/
|
|
||||||
void deleteLockFile(const Path & path, int fd);
|
|
||||||
|
|
||||||
enum LockType { ltRead, ltWrite, ltNone };
|
|
||||||
|
|
||||||
bool lockFile(int fd, LockType lockType, bool wait);
|
|
||||||
|
|
||||||
class PathLocks
|
class PathLocks
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
|
@ -40,18 +24,6 @@ public:
|
||||||
void setDeletion(bool deletePaths);
|
void setDeletion(bool deletePaths);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct FdLock
|
|
||||||
{
|
|
||||||
int fd;
|
|
||||||
bool acquired = false;
|
|
||||||
|
|
||||||
FdLock(int fd, LockType lockType, bool wait, std::string_view waitMsg);
|
|
||||||
|
|
||||||
~FdLock()
|
|
||||||
{
|
|
||||||
if (acquired)
|
|
||||||
lockFile(fd, ltNone, false);
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
}
|
#include "pathlocks-impl.hh"
|
||||||
|
|
38
src/libstore/unix/pathlocks-impl.hh
Normal file
38
src/libstore/unix/pathlocks-impl.hh
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
#pragma once
|
||||||
|
///@file
|
||||||
|
|
||||||
|
#include "file-descriptor.hh"
|
||||||
|
|
||||||
|
namespace nix::unix {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open (possibly create) a lock file and return the file descriptor.
|
||||||
|
* -1 is returned if create is false and the lock could not be opened
|
||||||
|
* because it doesn't exist. Any other error throws an exception.
|
||||||
|
*/
|
||||||
|
AutoCloseFD openLockFile(const Path & path, bool create);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete an open lock file.
|
||||||
|
*/
|
||||||
|
void deleteLockFile(const Path & path, int fd);
|
||||||
|
|
||||||
|
enum LockType { ltRead, ltWrite, ltNone };
|
||||||
|
|
||||||
|
bool lockFile(int fd, LockType lockType, bool wait);
|
||||||
|
|
||||||
|
struct FdLock
|
||||||
|
{
|
||||||
|
int fd;
|
||||||
|
bool acquired = false;
|
||||||
|
|
||||||
|
FdLock(int fd, LockType lockType, bool wait, std::string_view waitMsg);
|
||||||
|
|
||||||
|
~FdLock()
|
||||||
|
{
|
||||||
|
if (acquired)
|
||||||
|
lockFile(fd, ltNone, false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
165
src/libstore/unix/pathlocks.cc
Normal file
165
src/libstore/unix/pathlocks.cc
Normal file
|
@ -0,0 +1,165 @@
|
||||||
|
#include "pathlocks.hh"
|
||||||
|
#include "util.hh"
|
||||||
|
#include "sync.hh"
|
||||||
|
#include "signals.hh"
|
||||||
|
|
||||||
|
#include <cerrno>
|
||||||
|
#include <cstdlib>
|
||||||
|
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/file.h>
|
||||||
|
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
using namespace nix::unix;
|
||||||
|
|
||||||
|
AutoCloseFD unix::openLockFile(const Path & path, bool create)
|
||||||
|
{
|
||||||
|
AutoCloseFD fd;
|
||||||
|
|
||||||
|
fd = open(path.c_str(), O_CLOEXEC | O_RDWR | (create ? O_CREAT : 0), 0600);
|
||||||
|
if (!fd && (create || errno != ENOENT))
|
||||||
|
throw SysError("opening lock file '%1%'", path);
|
||||||
|
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void unix::deleteLockFile(const Path & path, int fd)
|
||||||
|
{
|
||||||
|
/* Get rid of the lock file. Have to be careful not to introduce
|
||||||
|
races. Write a (meaningless) token to the file to indicate to
|
||||||
|
other processes waiting on this lock that the lock is stale
|
||||||
|
(deleted). */
|
||||||
|
unlink(path.c_str());
|
||||||
|
writeFull(fd, "d");
|
||||||
|
/* Note that the result of unlink() is ignored; removing the lock
|
||||||
|
file is an optimisation, not a necessity. */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool unix::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");
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
while (flock(fd, type | LOCK_NB) != 0) {
|
||||||
|
checkInterrupt();
|
||||||
|
if (errno == EWOULDBLOCK) return false;
|
||||||
|
if (errno != EINTR)
|
||||||
|
throw SysError("acquiring/releasing lock");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool PathLocks::lockPaths(const PathSet & paths,
|
||||||
|
const std::string & waitMsg, bool wait)
|
||||||
|
{
|
||||||
|
assert(fds.empty());
|
||||||
|
|
||||||
|
/* 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. */
|
||||||
|
for (auto & path : paths) {
|
||||||
|
checkInterrupt();
|
||||||
|
Path lockPath = path + ".lock";
|
||||||
|
|
||||||
|
debug("locking path '%1%'", path);
|
||||||
|
|
||||||
|
AutoCloseFD fd;
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void PathLocks::unlock()
|
||||||
|
{
|
||||||
|
for (auto & i : fds) {
|
||||||
|
if (deletePaths) deleteLockFile(i.second, i.first);
|
||||||
|
|
||||||
|
if (close(i.first) == -1)
|
||||||
|
printError(
|
||||||
|
"error (ignored): cannot close lock file on '%1%'",
|
||||||
|
i.second);
|
||||||
|
|
||||||
|
debug("lock released on '%1%'", i.second);
|
||||||
|
}
|
||||||
|
|
||||||
|
fds.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
2
src/libstore/windows/pathlocks-impl.hh
Normal file
2
src/libstore/windows/pathlocks-impl.hh
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
#pragma once
|
||||||
|
///@file Needed because Unix-specific counterpart
|
16
src/libstore/windows/pathlocks.cc
Normal file
16
src/libstore/windows/pathlocks.cc
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
#include "logging.hh"
|
||||||
|
#include "pathlocks.hh"
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
bool PathLocks::lockPaths(const PathSet & _paths, const std::string & waitMsg, bool wait)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PathLocks::unlock()
|
||||||
|
{
|
||||||
|
warn("PathLocks::unlock: not yet implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in a new issue