pathlocks.cc 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. #include "pathlocks.hh"
  2. #include "util.hh"
  3. #include <cerrno>
  4. #include <cstdlib>
  5. #include <sys/types.h>
  6. #include <sys/stat.h>
  7. #include <fcntl.h>
  8. namespace nix {
  9. int openLockFile(const Path & path, bool create)
  10. {
  11. AutoCloseFD fd;
  12. fd = open(path.c_str(), O_RDWR | (create ? O_CREAT : 0), 0600);
  13. if (fd == -1 && (create || errno != ENOENT))
  14. throw SysError(format("opening lock file `%1%'") % path);
  15. closeOnExec(fd);
  16. return fd.borrow();
  17. }
  18. void deleteLockFile(const Path & path, int fd)
  19. {
  20. /* Get rid of the lock file. Have to be careful not to introduce
  21. races. Write a (meaningless) token to the file to indicate to
  22. other processes waiting on this lock that the lock is stale
  23. (deleted). */
  24. unlink(path.c_str());
  25. writeFull(fd, "d");
  26. /* Note that the result of unlink() is ignored; removing the lock
  27. file is an optimisation, not a necessity. */
  28. }
  29. bool lockFile(int fd, LockType lockType, bool wait)
  30. {
  31. struct flock lock;
  32. if (lockType == ltRead) lock.l_type = F_RDLCK;
  33. else if (lockType == ltWrite) lock.l_type = F_WRLCK;
  34. else if (lockType == ltNone) lock.l_type = F_UNLCK;
  35. else abort();
  36. lock.l_whence = SEEK_SET;
  37. lock.l_start = 0;
  38. lock.l_len = 0; /* entire file */
  39. if (wait) {
  40. while (fcntl(fd, F_SETLKW, &lock) != 0) {
  41. checkInterrupt();
  42. if (errno != EINTR)
  43. throw SysError(format("acquiring/releasing lock"));
  44. }
  45. } else {
  46. while (fcntl(fd, F_SETLK, &lock) != 0) {
  47. checkInterrupt();
  48. if (errno == EACCES || errno == EAGAIN) return false;
  49. if (errno != EINTR)
  50. throw SysError(format("acquiring/releasing lock"));
  51. }
  52. }
  53. return true;
  54. }
  55. /* This enables us to check whether are not already holding a lock on
  56. a file ourselves. POSIX locks (fcntl) suck in this respect: if we
  57. close a descriptor, the previous lock will be closed as well. And
  58. there is no way to query whether we already have a lock (F_GETLK
  59. only works on locks held by other processes). */
  60. static StringSet lockedPaths; /* !!! not thread-safe */
  61. PathLocks::PathLocks()
  62. : deletePaths(false)
  63. {
  64. }
  65. PathLocks::PathLocks(const PathSet & paths, const string & waitMsg)
  66. : deletePaths(false)
  67. {
  68. lockPaths(paths, waitMsg);
  69. }
  70. bool PathLocks::lockPaths(const PathSet & _paths,
  71. const string & waitMsg, bool wait)
  72. {
  73. assert(fds.empty());
  74. /* Note that `fds' is built incrementally so that the destructor
  75. will only release those locks that we have already acquired. */
  76. /* Sort the paths. This assures that locks are always acquired in
  77. the same order, thus preventing deadlocks. */
  78. Paths paths(_paths.begin(), _paths.end());
  79. paths.sort();
  80. /* Acquire the lock for each path. */
  81. foreach (Paths::iterator, i, paths) {
  82. checkInterrupt();
  83. Path path = *i;
  84. Path lockPath = path + ".lock";
  85. debug(format("locking path `%1%'") % path);
  86. if (lockedPaths.find(lockPath) != lockedPaths.end())
  87. throw Error("deadlock: trying to re-acquire self-held lock");
  88. AutoCloseFD fd;
  89. while (1) {
  90. /* Open/create the lock file. */
  91. fd = openLockFile(lockPath, true);
  92. /* Acquire an exclusive lock. */
  93. if (!lockFile(fd, ltWrite, false)) {
  94. if (wait) {
  95. if (waitMsg != "") printMsg(lvlError, waitMsg);
  96. lockFile(fd, ltWrite, true);
  97. } else {
  98. /* Failed to lock this path; release all other
  99. locks. */
  100. unlock();
  101. return false;
  102. }
  103. }
  104. debug(format("lock acquired on `%1%'") % lockPath);
  105. /* Check that the lock file hasn't become stale (i.e.,
  106. hasn't been unlinked). */
  107. struct stat st;
  108. if (fstat(fd, &st) == -1)
  109. throw SysError(format("statting lock file `%1%'") % lockPath);
  110. if (st.st_size != 0)
  111. /* This lock file has been unlinked, so we're holding
  112. a lock on a deleted file. This means that other
  113. processes may create and acquire a lock on
  114. `lockPath', and proceed. So we must retry. */
  115. debug(format("open lock file `%1%' has become stale") % lockPath);
  116. else
  117. break;
  118. }
  119. /* Use borrow so that the descriptor isn't closed. */
  120. fds.push_back(FDPair(fd.borrow(), lockPath));
  121. lockedPaths.insert(lockPath);
  122. }
  123. return true;
  124. }
  125. PathLocks::~PathLocks()
  126. {
  127. try {
  128. unlock();
  129. } catch (...) {
  130. ignoreException();
  131. }
  132. }
  133. void PathLocks::unlock()
  134. {
  135. foreach (list<FDPair>::iterator, i, fds) {
  136. if (deletePaths) deleteLockFile(i->second, i->first);
  137. lockedPaths.erase(i->second);
  138. if (close(i->first) == -1)
  139. printMsg(lvlError,
  140. format("error (ignored): cannot close lock file on `%1%'") % i->second);
  141. debug(format("lock released on `%1%'") % i->second);
  142. }
  143. fds.clear();
  144. }
  145. void PathLocks::setDeletion(bool deletePaths)
  146. {
  147. this->deletePaths = deletePaths;
  148. }
  149. bool pathIsLockedByMe(const Path & path)
  150. {
  151. Path lockPath = path + ".lock";
  152. return lockedPaths.find(lockPath) != lockedPaths.end();
  153. }
  154. }