LTP GCOV extension - code coverage report
Current view: directory - src/libstore - pathlocks.cc
Test: app.info
Date: 2008-11-20 Instrumented lines: 84
Code covered: 85.7 % Executed lines: 72

       1                 : #include "pathlocks.hh"
       2                 : #include "util.hh"
       3                 : 
       4                 : #include <cerrno>
       5                 : #include <cstdlib>
       6                 : 
       7                 : #include <sys/types.h>
       8                 : #include <sys/stat.h>
       9                 : #include <fcntl.h>
      10                 : 
      11                 : #ifdef __CYGWIN__
      12                 : #include <windows.h>
      13                 : #include <sys/cygwin.h>
      14                 : #endif
      15                 : 
      16                 : 
      17                 : namespace nix {
      18                 : 
      19                 : 
      20           14029 : int openLockFile(const Path & path, bool create)
      21                 : {
      22           14029 :     AutoCloseFD fd;
      23                 : 
      24                 : #ifdef __CYGWIN__
      25                 :     /* On Cygwin we have to open the lock file without "DELETE"
      26                 :        sharing mode; otherwise Windows will allow open lock files to
      27                 :        be deleted (which is almost but not quite what Unix does). */
      28                 :     char win32Path[MAX_PATH + 1];
      29                 :     cygwin_conv_to_full_win32_path(path.c_str(), win32Path);
      30                 : 
      31                 :     SECURITY_ATTRIBUTES sa; /* required, otherwise inexplicably bad shit happens */
      32                 :     sa.nLength = sizeof sa;
      33                 :     sa.lpSecurityDescriptor = 0;
      34                 :     sa.bInheritHandle = TRUE;
      35                 :     HANDLE h = CreateFile(win32Path, GENERIC_READ | GENERIC_WRITE,
      36                 :         FILE_SHARE_READ | FILE_SHARE_WRITE, &sa, 
      37                 :         (create ? OPEN_ALWAYS : OPEN_EXISTING), 
      38                 :         FILE_ATTRIBUTE_NORMAL, 0);
      39                 :     if (h == INVALID_HANDLE_VALUE) {
      40                 :         if (create || GetLastError() != ERROR_FILE_NOT_FOUND)
      41                 :             throw Error(format("opening lock file `%1%'") % path);
      42                 :         fd = -1;
      43                 :     }
      44                 :     else
      45                 :         fd = cygwin_attach_handle_to_fd((char *) path.c_str(), -1, h, 1, O_RDWR);
      46                 : #else        
      47           14029 :     fd = open(path.c_str(), O_RDWR | (create ? O_CREAT : 0), 0666);
      48           14029 :     if (fd == -1 && (create || errno != ENOENT))
      49               0 :         throw SysError(format("opening lock file `%1%'") % path);
      50                 : #endif
      51                 : 
      52           14029 :     return fd.borrow();
      53                 : }
      54                 : 
      55                 : 
      56           13482 : void deleteLockFilePreClose(const Path & path, int fd)
      57                 : {
      58                 : #ifndef __CYGWIN__
      59                 :     /* Get rid of the lock file.  Have to be careful not to introduce
      60                 :        races. */
      61                 :     /* On Unix, write a (meaningless) token to the file to indicate to
      62                 :        other processes waiting on this lock that the lock is stale
      63                 :        (deleted). */
      64           13482 :     unlink(path.c_str());
      65           13482 :     writeFull(fd, (const unsigned char *) "d", 1);
      66                 :     /* Note that the result of unlink() is ignored; removing the lock
      67                 :        file is an optimisation, not a necessity. */
      68                 : #endif
      69           13482 : }
      70                 : 
      71                 : 
      72           13482 : void deleteLockFilePostClose(const Path & path)
      73                 : {
      74                 : #ifdef __CYGWIN__
      75                 :     /* On Windows, just try to delete the lock file.  This will fail
      76                 :        if anybody still has the file open.  We cannot use unlink()
      77                 :        here, because Cygwin emulates Unix semantics of allowing an
      78                 :        open file to be deleted (but fakes it - the file isn't actually
      79                 :        deleted until later, so a file with the same name cannot be
      80                 :        created in the meantime). */
      81                 :     char win32Path[MAX_PATH + 1];
      82                 :     cygwin_conv_to_full_win32_path(path.c_str(), win32Path);
      83                 :     if (DeleteFile(win32Path))
      84                 :         debug(format("delete of `%1%' succeeded") % path.c_str());
      85                 :     else
      86                 :         /* Not an error: probably means that the lock is still opened
      87                 :            by someone else. */
      88                 :         debug(format("delete of `%1%' failed: %2%") % path.c_str() % GetLastError());
      89                 : #endif
      90           13482 : }
      91                 : 
      92                 : 
      93           16405 : bool lockFile(int fd, LockType lockType, bool wait)
      94                 : {
      95                 :     struct flock lock;
      96           16405 :     if (lockType == ltRead) lock.l_type = F_RDLCK;
      97           14630 :     else if (lockType == ltWrite) lock.l_type = F_WRLCK;
      98               0 :     else if (lockType == ltNone) lock.l_type = F_UNLCK;
      99               0 :     else abort();
     100           16405 :     lock.l_whence = SEEK_SET;
     101           16405 :     lock.l_start = 0;
     102           16405 :     lock.l_len = 0; /* entire file */
     103                 : 
     104           16405 :     if (wait) {
     105            4586 :         while (fcntl(fd, F_SETLKW, &lock) != 0) {
     106               0 :             checkInterrupt();
     107               0 :             if (errno != EINTR)
     108               0 :                 throw SysError(format("acquiring/releasing lock"));
     109                 :         }
     110                 :     } else {
     111           28224 :         while (fcntl(fd, F_SETLK, &lock) != 0) {
     112              29 :             checkInterrupt();
     113              29 :             if (errno == EACCES || errno == EAGAIN) return false;
     114               0 :             if (errno != EINTR) 
     115               0 :                 throw SysError(format("acquiring/releasing lock"));
     116                 :         }
     117                 :     }
     118                 : 
     119           16376 :     return true;
     120                 : }
     121                 : 
     122                 : 
     123                 : /* This enables us to check whether are not already holding a lock on
     124                 :    a file ourselves.  POSIX locks (fcntl) suck in this respect: if we
     125                 :    close a descriptor, the previous lock will be closed as well.  And
     126                 :    there is no way to query whether we already have a lock (F_GETLK
     127                 :    only works on locks held by other processes). */
     128            1151 : static StringSet lockedPaths; /* !!! not thread-safe */
     129                 : 
     130                 : 
     131            5648 : PathLocks::PathLocks()
     132            5648 :     : deletePaths(false)
     133                 : {
     134            5648 : }
     135                 : 
     136                 : 
     137            8309 : PathLocks::PathLocks(const PathSet & paths, const string & waitMsg)
     138            8309 :     : deletePaths(false)
     139                 : {
     140            8309 :     lockPaths(paths, waitMsg);
     141            8309 : }
     142                 : 
     143                 : 
     144            8523 : void PathLocks::lockPaths(const PathSet & _paths, const string & waitMsg)
     145                 : {
     146                 :     /* May be called only once! */
     147            8523 :     assert(fds.empty());
     148                 :     
     149                 :     /* Note that `fds' is built incrementally so that the destructor
     150                 :        will only release those locks that we have already acquired. */
     151                 : 
     152                 :     /* Sort the paths.  This assures that locks are always acquired in
     153                 :        the same order, thus preventing deadlocks. */
     154            8523 :     Paths paths(_paths.begin(), _paths.end());
     155            8523 :     paths.sort();
     156                 :     
     157                 :     /* Acquire the lock for each path. */
     158           22015 :     for (Paths::iterator i = paths.begin(); i != paths.end(); i++) {
     159           13492 :         checkInterrupt();
     160           13492 :         Path path = *i;
     161           13492 :         Path lockPath = path + ".lock";
     162                 : 
     163           13492 :         debug(format("locking path `%1%'") % path);
     164                 : 
     165           13492 :         if (lockedPaths.find(lockPath) != lockedPaths.end())
     166               0 :             throw Error("deadlock: trying to re-acquire self-held lock");
     167                 : 
     168           13492 :         AutoCloseFD fd;
     169                 :         
     170              27 :         while (1) {
     171                 : 
     172                 :             /* Open/create the lock file. */
     173           13519 :             fd = openLockFile(lockPath, true);
     174                 : 
     175                 :             /* Acquire an exclusive lock. */
     176           13519 :             if (!lockFile(fd, ltWrite, false)) {
     177              27 :                 if (waitMsg != "") printMsg(lvlError, waitMsg);
     178              27 :                 lockFile(fd, ltWrite, true);
     179                 :             }
     180                 : 
     181           13519 :             debug(format("lock acquired on `%1%'") % lockPath);
     182                 : 
     183                 :             /* Check that the lock file hasn't become stale (i.e.,
     184                 :                hasn't been unlinked). */
     185                 :             struct stat st;
     186           13519 :             if (fstat(fd, &st) == -1)
     187               0 :                 throw SysError(format("statting lock file `%1%'") % lockPath);
     188           13519 :             if (st.st_size != 0)
     189                 :                 /* This lock file has been unlinked, so we're holding
     190                 :                    a lock on a deleted file.  This means that other
     191                 :                    processes may create and acquire a lock on
     192                 :                    `lockPath', and proceed.  So we must retry. */
     193              27 :                 debug(format("open lock file `%1%' has become stale") % lockPath);
     194                 :             else
     195           13492 :                 break;
     196                 :         }
     197                 : 
     198                 :         /* Use borrow so that the descriptor isn't closed. */
     199           13492 :         fds.push_back(FDPair(fd.borrow(), lockPath));
     200           13492 :         lockedPaths.insert(lockPath);
     201            8523 :     }
     202            8523 : }
     203                 : 
     204                 : 
     205           13957 : PathLocks::~PathLocks()
     206                 : {
     207           27449 :     for (list<FDPair>::iterator i = fds.begin(); i != fds.end(); i++) {
     208           13492 :         if (deletePaths) deleteLockFilePreClose(i->second, i->first);
     209                 : 
     210           13492 :         lockedPaths.erase(i->second);
     211           13492 :         if (close(i->first) == -1)
     212               0 :             printMsg(lvlError,
     213                 :                 format("error (ignored): cannot close lock file on `%1%'") % i->second);
     214                 : 
     215           13492 :         if (deletePaths) deleteLockFilePostClose(i->second);
     216                 : 
     217           13492 :         debug(format("lock released on `%1%'") % i->second);
     218                 :     }
     219           13957 : }
     220                 : 
     221                 : 
     222            8513 : void PathLocks::setDeletion(bool deletePaths)
     223                 : {
     224            8513 :     this->deletePaths = deletePaths;
     225            8513 : }
     226                 : 
     227                 : 
     228             299 : bool pathIsLockedByMe(const Path & path)
     229                 : {
     230             299 :     Path lockPath = path + ".lock";
     231             299 :     return lockedPaths.find(lockPath) != lockedPaths.end();
     232                 : }
     233                 : 
     234               0 :  
     235            1106 : }

Generated by: LTP GCOV extension version 1.6