summaryrefslogtreecommitdiff
path: root/library/dir_lock.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'library/dir_lock.cpp')
-rw-r--r--library/dir_lock.cpp126
1 files changed, 64 insertions, 62 deletions
diff --git a/library/dir_lock.cpp b/library/dir_lock.cpp
index f653fa7c..7feb51ff 100644
--- a/library/dir_lock.cpp
+++ b/library/dir_lock.cpp
@@ -17,6 +17,7 @@
#include "../shared/serialize.h"
#include "../shared/build_info.h"
#include "../shared/int64.h"
+#include "../shared/file_handling.h"
#ifdef FFS_WIN
#include <tlhelp32.h>
@@ -24,7 +25,6 @@
#include "../shared/long_path_prefix.h"
#elif defined FFS_LINUX
-#include "../shared/file_handling.h"
#include <sys/stat.h>
#include <cerrno>
#include <unistd.h>
@@ -78,17 +78,15 @@ public:
//although CreateFile/FILE_APPEND_DATA without SetFilePointerEx works locally, it MAY NOT work on some network shares creating a 4 gig file!!!
const HANDLE fileHandle = ::CreateFile(applyLongPathPrefix(lockfilename_.c_str()).c_str(),
- FILE_GENERIC_WRITE,
+ GENERIC_READ | GENERIC_WRITE, //use both when writing over network, see comment in file_io.cpp
FILE_SHARE_READ,
- NULL,
+ 0,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (fileHandle == INVALID_HANDLE_VALUE)
return;
-
- Loki::ScopeGuard dummy = Loki::MakeGuard(::CloseHandle, fileHandle);
- (void)dummy; //silence warning "unused variable"
+ LOKI_ON_BLOCK_EXIT2(::CloseHandle(fileHandle));
const LARGE_INTEGER moveDist = {};
if (!::SetFilePointerEx(fileHandle, //__in HANDLE hFile,
@@ -108,9 +106,7 @@ public:
const int fileHandle = ::open(lockfilename_.c_str(), O_WRONLY | O_APPEND); //O_EXCL contains a race condition on NFS file systems: http://linux.die.net/man/2/open
if (fileHandle == -1)
return;
-
- Loki::ScopeGuard dummy = Loki::MakeGuard(::close, fileHandle);
- (void)dummy; //silence warning "unused variable"
+ LOKI_ON_BLOCK_EXIT2(::close(fileHandle));
const ssize_t bytesWritten = ::write(fileHandle, buffer, 1);
(void)bytesWritten;
@@ -124,20 +120,7 @@ private:
namespace
{
-void deleteLockFile(const Zstring& filename) //throw (FileError)
-{
-#ifdef FFS_WIN
- if (!::DeleteFile(applyLongPathPrefix(filename).c_str()))
-#elif defined FFS_LINUX
- if (::unlink(filename.c_str()) != 0)
-#endif
- {
- throw FileError(_("Error deleting file:") + "\n\"" + filename + "\"" + "\n\n" + getLastErrorFormatted());
- }
-}
-
-
-zen::UInt64 getLockFileSize(const Zstring& filename) //throw (FileError, ErrorNotExisting)
+UInt64 getLockFileSize(const Zstring& filename) //throw FileError, ErrorNotExisting
{
#ifdef FFS_WIN
WIN32_FIND_DATA fileInfo = {};
@@ -192,13 +175,13 @@ namespace
{
//read string from file stream
inline
-std::string readString(wxInputStream& stream) //throw (std::exception)
+std::string readString(wxInputStream& stream) //throw std::exception
{
- const boost::uint32_t strLength = readPOD<boost::uint32_t>(stream);
+ const auto strLength = readPOD<boost::uint32_t>(stream);
std::string output;
if (strLength > 0)
{
- output.resize(strLength); //may throw for corrupted data
+ output.resize(strLength); //throw std::bad_alloc
stream.Read(&output[0], strLength);
}
return output;
@@ -208,9 +191,8 @@ std::string readString(wxInputStream& stream) //throw (std::exception)
inline
void writeString(wxOutputStream& stream, const std::string& str) //write string to filestream
{
- const boost::uint32_t strLength = static_cast<boost::uint32_t>(str.length());
- writePOD<boost::uint32_t>(stream, strLength);
- stream.Write(str.c_str(), strLength);
+ writePOD(stream, static_cast<boost::uint32_t>(str.length()));
+ stream.Write(str.c_str(), str.length());
}
@@ -242,7 +224,7 @@ struct LockInformation
LockInformation(wxInputStream& stream) //read
{
char formatDescr[sizeof(LOCK_FORMAT_DESCR)] = {};
- stream.Read(formatDescr, sizeof(LOCK_FORMAT_DESCR)); //file format header
+ stream.Read(formatDescr, sizeof(LOCK_FORMAT_DESCR)); //file format header
const int lockFileVersion = readPOD<boost::int32_t>(stream); //
(void)lockFileVersion;
@@ -286,6 +268,7 @@ enum ProcessStatus
{
PROC_STATUS_NOT_RUNNING,
PROC_STATUS_RUNNING,
+ PROC_STATUS_ITS_US,
PROC_STATUS_NO_IDEA
};
ProcessStatus getProcessStatus(const LockInformation::ProcessDescription& procDescr)
@@ -295,15 +278,16 @@ ProcessStatus getProcessStatus(const LockInformation::ProcessDescription& procDe
return PROC_STATUS_NO_IDEA; //lock owned by different computer
#ifdef FFS_WIN
+ if (procDescr.processId == ::GetCurrentProcessId()) //may seem obscure, but it's possible: a lock file is "stolen" and put back while the program is running
+ return PROC_STATUS_ITS_US;
+
//note: ::OpenProcess() is no option as it may successfully return for crashed processes!
HANDLE snapshot = ::CreateToolhelp32Snapshot(
TH32CS_SNAPPROCESS, //__in DWORD dwFlags,
0); //__in DWORD th32ProcessID
if (snapshot == INVALID_HANDLE_VALUE)
return PROC_STATUS_NO_IDEA;
-
- Loki::ScopeGuard dummy = Loki::MakeGuard(::CloseHandle, snapshot);
- (void)dummy; //silence warning "unused variable"
+ LOKI_ON_BLOCK_EXIT2(::CloseHandle(snapshot));
PROCESSENTRY32 processEntry = {};
processEntry.dwSize = sizeof(processEntry);
@@ -316,11 +300,14 @@ ProcessStatus getProcessStatus(const LockInformation::ProcessDescription& procDe
if (processEntry.th32ProcessID == procDescr.processId)
return PROC_STATUS_RUNNING; //process still running
}
- while(::Process32Next(snapshot, &processEntry));
+ while (::Process32Next(snapshot, &processEntry));
return PROC_STATUS_NOT_RUNNING;
#elif defined FFS_LINUX
+ if (procDescr.processId == ::getpid()) //may seem obscure, but it's possible: a lock file is "stolen" and put back while the program is running
+ return PROC_STATUS_ITS_US;
+
if (procDescr.processId <= 0 || procDescr.processId >= 65536)
return PROC_STATUS_NO_IDEA; //invalid process id
@@ -329,30 +316,30 @@ ProcessStatus getProcessStatus(const LockInformation::ProcessDescription& procDe
}
-void writeLockInfo(const Zstring& lockfilename) //throw (FileError)
+void writeLockInfo(const Zstring& lockfilename) //throw FileError
{
//write GUID at the beginning of the file: this ID is a universal identifier for this lock (no matter what the path is, considering symlinks, distributed network, etc.)
- FileOutputStream lockFile(lockfilename); //throw (FileError)
+ FileOutputStream lockFile(lockfilename); //throw FileError
LockInformation().toStream(lockFile);
}
-LockInformation retrieveLockInfo(const Zstring& lockfilename) //throw (FileError, ErrorNotExisting)
+LockInformation retrieveLockInfo(const Zstring& lockfilename) //throw FileError, ErrorNotExisting
{
//read GUID from beginning of file
- FileInputStream lockFile(lockfilename); //throw (FileError, ErrorNotExisting)
+ FileInputStream lockFile(lockfilename); //throw FileError, ErrorNotExisting
return LockInformation(lockFile);
}
-std::string retrieveLockId(const Zstring& lockfilename) //throw (FileError, ErrorNotExisting)
+std::string retrieveLockId(const Zstring& lockfilename) //throw FileError, ErrorNotExisting
{
return retrieveLockInfo(lockfilename).lockId;
}
}
-void waitOnDirLock(const Zstring& lockfilename, DirLockCallback* callback) //throw (FileError)
+void waitOnDirLock(const Zstring& lockfilename, DirLockCallback* callback) //throw FileError
{
std::wstring infoMsg = _("Waiting while directory is locked (%x)...");
replace(infoMsg, L"%x", std::wstring(L"\"") + lockfilename + "\"");
@@ -361,15 +348,26 @@ void waitOnDirLock(const Zstring& lockfilename, DirLockCallback* callback) //thr
//---------------------------------------------------------------
try
{
- const LockInformation lockInfo = retrieveLockInfo(lockfilename); //throw (FileError, ErrorNotExisting)
- const bool lockOwnderDead = getProcessStatus(lockInfo.procDescr) == PROC_STATUS_NOT_RUNNING; //convenience optimization: if we know the owning process crashed, we needn't wait DETECT_EXITUS_INTERVAL sec
+ const LockInformation lockInfo = retrieveLockInfo(lockfilename); //throw FileError, ErrorNotExisting
+
+ bool lockOwnderDead = false; //convenience optimization: if we know the owning process crashed, we needn't wait DETECT_EXITUS_INTERVAL sec
+ switch (getProcessStatus(lockInfo.procDescr))
+ {
+ case PROC_STATUS_ITS_US: //since we've already passed LockAdmin, the lock file seems abandoned ("stolen"?) although it's from this process
+ case PROC_STATUS_NOT_RUNNING:
+ lockOwnderDead = true;
+ break;
+ case PROC_STATUS_RUNNING:
+ case PROC_STATUS_NO_IDEA:
+ break;
+ }
zen::UInt64 fileSizeOld;
wxLongLong lockSilentStart = wxGetLocalTimeMillis();
while (true)
{
- const zen::UInt64 fileSizeNew = ::getLockFileSize(lockfilename); //throw (FileError, ErrorNotExisting)
+ const zen::UInt64 fileSizeNew = ::getLockFileSize(lockfilename); //throw FileError, ErrorNotExisting
wxLongLong currentTime = wxGetLocalTimeMillis();
if (fileSizeNew != fileSizeOld)
@@ -382,17 +380,17 @@ void waitOnDirLock(const Zstring& lockfilename, DirLockCallback* callback) //thr
if (lockOwnderDead || //no need to wait any longer...
currentTime - lockSilentStart > DETECT_EXITUS_INTERVAL)
{
- DirLock dummy(deleteAbandonedLockName(lockfilename), callback); //throw (FileError)
+ DirLock dummy(deleteAbandonedLockName(lockfilename), callback); //throw FileError
//now that the lock is in place check existence again: meanwhile another process may have deleted and created a new lock!
- if (retrieveLockId(lockfilename) != lockInfo.lockId) //throw (FileError, ErrorNotExisting)
+ if (retrieveLockId(lockfilename) != lockInfo.lockId) //throw FileError, ErrorNotExisting
return; //another process has placed a new lock, leave scope: the wait for the old lock is technically over...
- if (getLockFileSize(lockfilename) != fileSizeOld) //throw (FileError, ErrorNotExisting)
+ if (getLockFileSize(lockfilename) != fileSizeOld) //throw FileError, ErrorNotExisting
continue; //belated lifesign
- ::deleteLockFile(lockfilename); //throw (FileError)
+ removeFile(lockfilename); //throw FileError
return;
}
@@ -432,24 +430,28 @@ void releaseLock(const Zstring& lockfilename) //throw ()
{
try
{
- ::deleteLockFile(lockfilename);
+ removeFile(lockfilename);
}
- catch(...) {}
+ catch (...) {}
}
-bool tryLock(const Zstring& lockfilename) //throw (FileError)
+bool tryLock(const Zstring& lockfilename) //throw FileError
{
#ifdef FFS_WIN
const HANDLE fileHandle = ::CreateFile(applyLongPathPrefix(lockfilename).c_str(),
- GENERIC_WRITE,
+ GENERIC_READ | GENERIC_WRITE, //use both when writing over network, see comment in file_io.cpp
FILE_SHARE_READ,
- NULL,
+ 0,
CREATE_NEW,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (fileHandle == INVALID_HANDLE_VALUE)
{
+#ifndef _MSC_VER
+#warning fix this problem!
+ //read-only FTP may return: ERROR_FILE_EXISTS (NetDrive @ GNU)
+#endif
if (::GetLastError() == ERROR_FILE_EXISTS)
return false;
else
@@ -471,10 +473,10 @@ bool tryLock(const Zstring& lockfilename) //throw (FileError)
::close(fileHandle);
#endif
- Loki::ScopeGuard guardLockFile = Loki::MakeGuard(::releaseLock, lockfilename);
+ Loki::ScopeGuard guardLockFile = Loki::MakeGuard(zen::removeFile, lockfilename);
//write UUID at the beginning of the file: this ID is a universal identifier for this lock (no matter what the path is, considering symlinks, etc.)
- writeLockInfo(lockfilename); //throw (FileError)
+ writeLockInfo(lockfilename); //throw FileError
guardLockFile.Dismiss(); //lockfile created successfully
return true;
@@ -485,10 +487,10 @@ bool tryLock(const Zstring& lockfilename) //throw (FileError)
class DirLock::SharedDirLock
{
public:
- SharedDirLock(const Zstring& lockfilename, DirLockCallback* callback = NULL) : //throw (FileError)
+ SharedDirLock(const Zstring& lockfilename, DirLockCallback* callback = NULL) : //throw FileError
lockfilename_(lockfilename)
{
- while (!::tryLock(lockfilename)) //throw (FileError)
+ while (!::tryLock(lockfilename)) //throw FileError
::waitOnDirLock(lockfilename, callback); //
threadObj = boost::thread(LifeSigns(lockfilename));
@@ -522,7 +524,7 @@ public:
}
//create or retrieve a SharedDirLock
- std::shared_ptr<SharedDirLock> retrieve(const Zstring& lockfilename, DirLockCallback* callback) //throw (FileError)
+ std::shared_ptr<SharedDirLock> retrieve(const Zstring& lockfilename, DirLockCallback* callback) //throw FileError
{
//optimization: check if there is an active(!) lock for "lockfilename"
FileToUuidMap::const_iterator iterUuid = fileToUuid.find(lockfilename);
@@ -535,7 +537,7 @@ public:
try //actual check based on lock UUID, deadlock prevention: "lockfilename" may be an alternative name for an already active lock
{
- const std::string lockId = retrieveLockId(lockfilename); //throw (FileError, ErrorNotExisting)
+ const std::string lockId = retrieveLockId(lockfilename); //throw FileError, ErrorNotExisting
const std::shared_ptr<SharedDirLock>& activeLock = findActive(lockId); //returns null-lock if not found
if (activeLock)
@@ -544,11 +546,11 @@ public:
return activeLock;
}
}
- catch (const ErrorNotExisting&) {} //let other FileError(s) propagate!
+ catch (...) {} //catch everything, let SharedDirLock constructor deal with errors, e.g. 0-sized/corrupted lock file
//not yet in buffer, so create a new directory lock
- std::shared_ptr<SharedDirLock> newLock(new SharedDirLock(lockfilename, callback)); //throw (FileError)
- const std::string newLockId = retrieveLockId(lockfilename); //throw (FileError, ErrorNotExisting)
+ std::shared_ptr<SharedDirLock> newLock(new SharedDirLock(lockfilename, callback)); //throw FileError
+ const std::string newLockId = retrieveLockId(lockfilename); //throw FileError, ErrorNotExisting
//update registry
fileToUuid[lockfilename] = newLockId; //throw()
@@ -579,7 +581,7 @@ private:
};
-DirLock::DirLock(const Zstring& lockfilename, DirLockCallback* callback) //throw (FileError)
+DirLock::DirLock(const Zstring& lockfilename, DirLockCallback* callback) //throw FileError
{
#ifdef FFS_WIN
std::vector<wchar_t> volName(std::max(lockfilename.size(), static_cast<size_t>(10000)));
bgstack15