mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2025-07-26 08:15:32 -04:00
parent
ade684d501
commit
05b5446e94
8 changed files with 946 additions and 0 deletions
|
@ -43,6 +43,7 @@ set(keepassx_SOURCES
|
||||||
core/ListDeleter.h
|
core/ListDeleter.h
|
||||||
core/Metadata.cpp
|
core/Metadata.cpp
|
||||||
core/PasswordGenerator.cpp
|
core/PasswordGenerator.cpp
|
||||||
|
core/qlockfile.cpp
|
||||||
core/qsavefile.cpp
|
core/qsavefile.cpp
|
||||||
core/qsavefile_p.h
|
core/qsavefile_p.h
|
||||||
core/SignalMultiplexer.cpp
|
core/SignalMultiplexer.cpp
|
||||||
|
@ -130,6 +131,18 @@ if(NOT GCRYPT_HAS_SALSA20)
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(UNIX)
|
||||||
|
set(keepassx_SOURCES
|
||||||
|
${keepassx_SOURCES}
|
||||||
|
core/qlockfile_unix.cpp
|
||||||
|
)
|
||||||
|
elseif(MINGW)
|
||||||
|
set(keepassx_SOURCES
|
||||||
|
${keepassx_SOURCES}
|
||||||
|
core/qlockfile_win.cpp
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
set(keepassx_SOURCES_MAINEXE
|
set(keepassx_SOURCES_MAINEXE
|
||||||
main.cpp
|
main.cpp
|
||||||
)
|
)
|
||||||
|
|
337
src/core/qlockfile.cpp
Normal file
337
src/core/qlockfile.cpp
Normal file
|
@ -0,0 +1,337 @@
|
||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2013 David Faure <faure+bluesystems@kde.org>
|
||||||
|
** Contact: http://www.qt-project.org/legal
|
||||||
|
**
|
||||||
|
** This file is part of the QtCore module of the Qt Toolkit.
|
||||||
|
**
|
||||||
|
** $QT_BEGIN_LICENSE:LGPL21$
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and Digia. For licensing terms and
|
||||||
|
** conditions see http://qt.digia.com/licensing. For further information
|
||||||
|
** use the contact form at http://qt.digia.com/contact-us.
|
||||||
|
**
|
||||||
|
** GNU Lesser General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||||
|
** General Public License version 2.1 or version 3 as published by the Free
|
||||||
|
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
|
||||||
|
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
|
||||||
|
** following information to ensure the GNU Lesser General Public License
|
||||||
|
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
|
||||||
|
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||||
|
**
|
||||||
|
** In addition, as a special exception, Digia gives you certain additional
|
||||||
|
** rights. These rights are described in the Digia Qt LGPL Exception
|
||||||
|
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||||
|
**
|
||||||
|
** $QT_END_LICENSE$
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include "qlockfile.h"
|
||||||
|
#include "qlockfile_p.h"
|
||||||
|
|
||||||
|
#include <QElapsedTimer>
|
||||||
|
#include <QDateTime>
|
||||||
|
|
||||||
|
QT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\class QLockFile
|
||||||
|
\inmodule QtCore
|
||||||
|
\brief The QLockFile class provides locking between processes using a file.
|
||||||
|
\since 5.1
|
||||||
|
|
||||||
|
A lock file can be used to prevent multiple processes from accessing concurrently
|
||||||
|
the same resource. For instance, a configuration file on disk, or a socket, a port,
|
||||||
|
a region of shared memory...
|
||||||
|
|
||||||
|
Serialization is only guaranteed if all processes that access the shared resource
|
||||||
|
use QLockFile, with the same file path.
|
||||||
|
|
||||||
|
QLockFile supports two use cases:
|
||||||
|
to protect a resource for a short-term operation (e.g. verifying if a configuration
|
||||||
|
file has changed before saving new settings), and for long-lived protection of a
|
||||||
|
resource (e.g. a document opened by a user in an editor) for an indefinite amount of time.
|
||||||
|
|
||||||
|
When protecting for a short-term operation, it is acceptable to call lock() and wait
|
||||||
|
until any running operation finishes.
|
||||||
|
When protecting a resource over a long time, however, the application should always
|
||||||
|
call setStaleLockTime(0) and then tryLock() with a short timeout, in order to
|
||||||
|
warn the user that the resource is locked.
|
||||||
|
|
||||||
|
If the process holding the lock crashes, the lock file stays on disk and can prevent
|
||||||
|
any other process from accessing the shared resource, ever. For this reason, QLockFile
|
||||||
|
tries to detect such a "stale" lock file, based on the process ID written into the file,
|
||||||
|
and (in case that process ID got reused meanwhile), on the last modification time of
|
||||||
|
the lock file (30s by default, for the use case of a short-lived operation).
|
||||||
|
If the lock file is found to be stale, it will be deleted.
|
||||||
|
|
||||||
|
For the use case of protecting a resource over a long time, you should therefore call
|
||||||
|
setStaleLockTime(0), and when tryLock() returns LockFailedError, inform the user
|
||||||
|
that the document is locked, possibly using getLockInfo() for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\enum QLockFile::LockError
|
||||||
|
|
||||||
|
This enum describes the result of the last call to lock() or tryLock().
|
||||||
|
|
||||||
|
\value NoError The lock was acquired successfully.
|
||||||
|
\value LockFailedError The lock could not be acquired because another process holds it.
|
||||||
|
\value PermissionError The lock file could not be created, for lack of permissions
|
||||||
|
in the parent directory.
|
||||||
|
\value UnknownError Another error happened, for instance a full partition
|
||||||
|
prevented writing out the lock file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Constructs a new lock file object.
|
||||||
|
The object is created in an unlocked state.
|
||||||
|
When calling lock() or tryLock(), a lock file named \a fileName will be created,
|
||||||
|
if it doesn't already exist.
|
||||||
|
|
||||||
|
\sa lock(), unlock()
|
||||||
|
*/
|
||||||
|
QLockFile::QLockFile(const QString &fileName)
|
||||||
|
: d_ptr(new QLockFilePrivate(fileName))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Destroys the lock file object.
|
||||||
|
If the lock was acquired, this will release the lock, by deleting the lock file.
|
||||||
|
*/
|
||||||
|
QLockFile::~QLockFile()
|
||||||
|
{
|
||||||
|
unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Sets \a staleLockTime to be the time in milliseconds after which
|
||||||
|
a lock file is considered stale.
|
||||||
|
The default value is 30000, i.e. 30 seconds.
|
||||||
|
If your application typically keeps the file locked for more than 30 seconds
|
||||||
|
(for instance while saving megabytes of data for 2 minutes), you should set
|
||||||
|
a bigger value using setStaleLockTime().
|
||||||
|
|
||||||
|
The value of \a staleLockTime is used by lock() and tryLock() in order
|
||||||
|
to determine when an existing lock file is considered stale, i.e. left over
|
||||||
|
by a crashed process. This is useful for the case where the PID got reused
|
||||||
|
meanwhile, so the only way to detect a stale lock file is by the fact that
|
||||||
|
it has been around for a long time.
|
||||||
|
|
||||||
|
\sa staleLockTime()
|
||||||
|
*/
|
||||||
|
void QLockFile::setStaleLockTime(int staleLockTime)
|
||||||
|
{
|
||||||
|
Q_D(QLockFile);
|
||||||
|
d->staleLockTime = staleLockTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Returns the time in milliseconds after which
|
||||||
|
a lock file is considered stale.
|
||||||
|
|
||||||
|
\sa setStaleLockTime()
|
||||||
|
*/
|
||||||
|
int QLockFile::staleLockTime() const
|
||||||
|
{
|
||||||
|
Q_D(const QLockFile);
|
||||||
|
return d->staleLockTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Returns \c true if the lock was acquired by this QLockFile instance,
|
||||||
|
otherwise returns \c false.
|
||||||
|
|
||||||
|
\sa lock(), unlock(), tryLock()
|
||||||
|
*/
|
||||||
|
bool QLockFile::isLocked() const
|
||||||
|
{
|
||||||
|
Q_D(const QLockFile);
|
||||||
|
return d->isLocked;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Creates the lock file.
|
||||||
|
|
||||||
|
If another process (or another thread) has created the lock file already,
|
||||||
|
this function will block until that process (or thread) releases it.
|
||||||
|
|
||||||
|
Calling this function multiple times on the same lock from the same
|
||||||
|
thread without unlocking first is not allowed. This function will
|
||||||
|
\e dead-lock when the file is locked recursively.
|
||||||
|
|
||||||
|
Returns \c true if the lock was acquired, false if it could not be acquired
|
||||||
|
due to an unrecoverable error, such as no permissions in the parent directory.
|
||||||
|
|
||||||
|
\sa unlock(), tryLock()
|
||||||
|
*/
|
||||||
|
bool QLockFile::lock()
|
||||||
|
{
|
||||||
|
return tryLock(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Attempts to create the lock file. This function returns \c true if the
|
||||||
|
lock was obtained; otherwise it returns \c false. If another process (or
|
||||||
|
another thread) has created the lock file already, this function will
|
||||||
|
wait for at most \a timeout milliseconds for the lock file to become
|
||||||
|
available.
|
||||||
|
|
||||||
|
Note: Passing a negative number as the \a timeout is equivalent to
|
||||||
|
calling lock(), i.e. this function will wait forever until the lock
|
||||||
|
file can be locked if \a timeout is negative.
|
||||||
|
|
||||||
|
If the lock was obtained, it must be released with unlock()
|
||||||
|
before another process (or thread) can successfully lock it.
|
||||||
|
|
||||||
|
Calling this function multiple times on the same lock from the same
|
||||||
|
thread without unlocking first is not allowed, this function will
|
||||||
|
\e always return false when attempting to lock the file recursively.
|
||||||
|
|
||||||
|
\sa lock(), unlock()
|
||||||
|
*/
|
||||||
|
bool QLockFile::tryLock(int timeout)
|
||||||
|
{
|
||||||
|
Q_D(QLockFile);
|
||||||
|
QElapsedTimer timer;
|
||||||
|
if (timeout > 0)
|
||||||
|
timer.start();
|
||||||
|
int sleepTime = 100;
|
||||||
|
Q_FOREVER {
|
||||||
|
d->lockError = d->tryLock_sys();
|
||||||
|
switch (d->lockError) {
|
||||||
|
case NoError:
|
||||||
|
d->isLocked = true;
|
||||||
|
return true;
|
||||||
|
case PermissionError:
|
||||||
|
case UnknownError:
|
||||||
|
return false;
|
||||||
|
case LockFailedError:
|
||||||
|
if (!d->isLocked && d->isApparentlyStale()) {
|
||||||
|
// Stale lock from another thread/process
|
||||||
|
// Ensure two processes don't remove it at the same time
|
||||||
|
QLockFile rmlock(d->fileName + QLatin1String(".rmlock"));
|
||||||
|
if (rmlock.tryLock()) {
|
||||||
|
if (d->isApparentlyStale() && d->removeStaleLock())
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (timeout == 0 || (timeout > 0 && timer.hasExpired(timeout)))
|
||||||
|
return false;
|
||||||
|
QLockFileThread::msleep(sleepTime);
|
||||||
|
if (sleepTime < 5 * 1000)
|
||||||
|
sleepTime *= 2;
|
||||||
|
}
|
||||||
|
// not reached
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\fn void QLockFile::unlock()
|
||||||
|
Releases the lock, by deleting the lock file.
|
||||||
|
|
||||||
|
Calling unlock() without locking the file first, does nothing.
|
||||||
|
|
||||||
|
\sa lock(), tryLock()
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Retrieves information about the current owner of the lock file.
|
||||||
|
|
||||||
|
If tryLock() returns \c false, and error() returns LockFailedError,
|
||||||
|
this function can be called to find out more information about the existing
|
||||||
|
lock file:
|
||||||
|
\list
|
||||||
|
\li the PID of the application (returned in \a pid)
|
||||||
|
\li the \a hostname it's running on (useful in case of networked filesystems),
|
||||||
|
\li the name of the application which created it (returned in \a appname),
|
||||||
|
\endlist
|
||||||
|
|
||||||
|
Note that tryLock() automatically deleted the file if there is no
|
||||||
|
running application with this PID, so LockFailedError can only happen if there is
|
||||||
|
an application with this PID (it could be unrelated though).
|
||||||
|
|
||||||
|
This can be used to inform users about the existing lock file and give them
|
||||||
|
the choice to delete it. After removing the file using removeStaleLockFile(),
|
||||||
|
the application can call tryLock() again.
|
||||||
|
|
||||||
|
This function returns \c true if the information could be successfully retrieved, false
|
||||||
|
if the lock file doesn't exist or doesn't contain the expected data.
|
||||||
|
This can happen if the lock file was deleted between the time where tryLock() failed
|
||||||
|
and the call to this function. Simply call tryLock() again if this happens.
|
||||||
|
*/
|
||||||
|
bool QLockFile::getLockInfo(qint64 *pid, QString *hostname, QString *appname) const
|
||||||
|
{
|
||||||
|
Q_D(const QLockFile);
|
||||||
|
return d->getLockInfo(pid, hostname, appname);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool QLockFilePrivate::getLockInfo(qint64 *pid, QString *hostname, QString *appname) const
|
||||||
|
{
|
||||||
|
QFile reader(fileName);
|
||||||
|
if (!reader.open(QIODevice::ReadOnly))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
QByteArray pidLine = reader.readLine();
|
||||||
|
pidLine.chop(1);
|
||||||
|
QByteArray appNameLine = reader.readLine();
|
||||||
|
appNameLine.chop(1);
|
||||||
|
QByteArray hostNameLine = reader.readLine();
|
||||||
|
hostNameLine.chop(1);
|
||||||
|
if (pidLine.isEmpty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
qint64 thePid = pidLine.toLongLong();
|
||||||
|
if (pid)
|
||||||
|
*pid = thePid;
|
||||||
|
if (appname)
|
||||||
|
*appname = QString::fromUtf8(appNameLine);
|
||||||
|
if (hostname)
|
||||||
|
*hostname = QString::fromUtf8(hostNameLine);
|
||||||
|
return thePid > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Attempts to forcefully remove an existing lock file.
|
||||||
|
|
||||||
|
Calling this is not recommended when protecting a short-lived operation: QLockFile
|
||||||
|
already takes care of removing lock files after they are older than staleLockTime().
|
||||||
|
|
||||||
|
This method should only be called when protecting a resource for a long time, i.e.
|
||||||
|
with staleLockTime(0), and after tryLock() returned LockFailedError, and the user
|
||||||
|
agreed on removing the lock file.
|
||||||
|
|
||||||
|
Returns \c true on success, false if the lock file couldn't be removed. This happens
|
||||||
|
on Windows, when the application owning the lock is still running.
|
||||||
|
*/
|
||||||
|
bool QLockFile::removeStaleLockFile()
|
||||||
|
{
|
||||||
|
Q_D(QLockFile);
|
||||||
|
if (d->isLocked) {
|
||||||
|
qWarning("removeStaleLockFile can only be called when not holding the lock");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return d->removeStaleLock();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Returns the lock file error status.
|
||||||
|
|
||||||
|
If tryLock() returns \c false, this function can be called to find out
|
||||||
|
the reason why the locking failed.
|
||||||
|
*/
|
||||||
|
QLockFile::LockError QLockFile::error() const
|
||||||
|
{
|
||||||
|
Q_D(const QLockFile);
|
||||||
|
return d->lockError;
|
||||||
|
}
|
||||||
|
|
||||||
|
QT_END_NAMESPACE
|
79
src/core/qlockfile.h
Normal file
79
src/core/qlockfile.h
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2013 David Faure <faure+bluesystems@kde.org>
|
||||||
|
** Contact: http://www.qt-project.org/legal
|
||||||
|
**
|
||||||
|
** This file is part of the QtCore module of the Qt Toolkit.
|
||||||
|
**
|
||||||
|
** $QT_BEGIN_LICENSE:LGPL21$
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and Digia. For licensing terms and
|
||||||
|
** conditions see http://qt.digia.com/licensing. For further information
|
||||||
|
** use the contact form at http://qt.digia.com/contact-us.
|
||||||
|
**
|
||||||
|
** GNU Lesser General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||||
|
** General Public License version 2.1 or version 3 as published by the Free
|
||||||
|
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
|
||||||
|
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
|
||||||
|
** following information to ensure the GNU Lesser General Public License
|
||||||
|
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
|
||||||
|
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||||
|
**
|
||||||
|
** In addition, as a special exception, Digia gives you certain additional
|
||||||
|
** rights. These rights are described in the Digia Qt LGPL Exception
|
||||||
|
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||||
|
**
|
||||||
|
** $QT_END_LICENSE$
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#ifndef QLOCKFILE_H
|
||||||
|
#define QLOCKFILE_H
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
#include <QScopedPointer>
|
||||||
|
|
||||||
|
QT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
class QLockFilePrivate;
|
||||||
|
|
||||||
|
class QLockFile
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
QLockFile(const QString &fileName);
|
||||||
|
~QLockFile();
|
||||||
|
|
||||||
|
bool lock();
|
||||||
|
bool tryLock(int timeout = 0);
|
||||||
|
void unlock();
|
||||||
|
|
||||||
|
void setStaleLockTime(int);
|
||||||
|
int staleLockTime() const;
|
||||||
|
|
||||||
|
bool isLocked() const;
|
||||||
|
bool getLockInfo(qint64 *pid, QString *hostname, QString *appname) const;
|
||||||
|
bool removeStaleLockFile();
|
||||||
|
|
||||||
|
enum LockError {
|
||||||
|
NoError = 0,
|
||||||
|
LockFailedError = 1,
|
||||||
|
PermissionError = 2,
|
||||||
|
UnknownError = 3
|
||||||
|
};
|
||||||
|
LockError error() const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
QScopedPointer<QLockFilePrivate> d_ptr;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Q_DECLARE_PRIVATE(QLockFile)
|
||||||
|
Q_DISABLE_COPY(QLockFile)
|
||||||
|
};
|
||||||
|
|
||||||
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
|
#endif // QLOCKFILE_H
|
104
src/core/qlockfile_p.h
Normal file
104
src/core/qlockfile_p.h
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2013 David Faure <faure+bluesystems@kde.org>
|
||||||
|
** Contact: http://www.qt-project.org/legal
|
||||||
|
**
|
||||||
|
** This file is part of the QtCore module of the Qt Toolkit.
|
||||||
|
**
|
||||||
|
** $QT_BEGIN_LICENSE:LGPL21$
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and Digia. For licensing terms and
|
||||||
|
** conditions see http://qt.digia.com/licensing. For further information
|
||||||
|
** use the contact form at http://qt.digia.com/contact-us.
|
||||||
|
**
|
||||||
|
** GNU Lesser General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||||
|
** General Public License version 2.1 or version 3 as published by the Free
|
||||||
|
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
|
||||||
|
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
|
||||||
|
** following information to ensure the GNU Lesser General Public License
|
||||||
|
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
|
||||||
|
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||||
|
**
|
||||||
|
** In addition, as a special exception, Digia gives you certain additional
|
||||||
|
** rights. These rights are described in the Digia Qt LGPL Exception
|
||||||
|
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||||
|
**
|
||||||
|
** $QT_END_LICENSE$
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#ifndef QLOCKFILE_P_H
|
||||||
|
#define QLOCKFILE_P_H
|
||||||
|
|
||||||
|
//
|
||||||
|
// W A R N I N G
|
||||||
|
// -------------
|
||||||
|
//
|
||||||
|
// This file is not part of the Qt API. It exists purely as an
|
||||||
|
// implementation detail. This header file may change from version to
|
||||||
|
// version without notice, or even be removed.
|
||||||
|
//
|
||||||
|
// We mean it.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "qlockfile.h"
|
||||||
|
|
||||||
|
#include <QFile>
|
||||||
|
#include <QThread>
|
||||||
|
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
#include <qt_windows.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
QT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
class QLockFileThread : public QThread
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static void msleep(unsigned long msecs) { QThread::msleep(msecs); }
|
||||||
|
};
|
||||||
|
|
||||||
|
class QLockFilePrivate
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
QLockFilePrivate(const QString &fn)
|
||||||
|
: fileName(fn),
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
fileHandle(INVALID_HANDLE_VALUE),
|
||||||
|
#else
|
||||||
|
fileHandle(-1),
|
||||||
|
#endif
|
||||||
|
staleLockTime(30 * 1000), // 30 seconds
|
||||||
|
lockError(QLockFile::NoError),
|
||||||
|
isLocked(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
QLockFile::LockError tryLock_sys();
|
||||||
|
bool removeStaleLock();
|
||||||
|
bool getLockInfo(qint64 *pid, QString *hostname, QString *appname) const;
|
||||||
|
// Returns \c true if the lock belongs to dead PID, or is old.
|
||||||
|
// The attempt to delete it will tell us if it was really stale or not, though.
|
||||||
|
bool isApparentlyStale() const;
|
||||||
|
|
||||||
|
#ifdef Q_OS_UNIX
|
||||||
|
static int checkFcntlWorksAfterFlock();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
QString fileName;
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
Qt::HANDLE fileHandle;
|
||||||
|
#else
|
||||||
|
int fileHandle;
|
||||||
|
#endif
|
||||||
|
int staleLockTime; // "int milliseconds" is big enough for 24 days
|
||||||
|
QLockFile::LockError lockError;
|
||||||
|
bool isLocked;
|
||||||
|
};
|
||||||
|
|
||||||
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
|
#endif /* QLOCKFILE_P_H */
|
199
src/core/qlockfile_unix.cpp
Normal file
199
src/core/qlockfile_unix.cpp
Normal file
|
@ -0,0 +1,199 @@
|
||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2013 David Faure <faure+bluesystems@kde.org>
|
||||||
|
** Contact: http://www.qt-project.org/legal
|
||||||
|
**
|
||||||
|
** This file is part of the QtCore module of the Qt Toolkit.
|
||||||
|
**
|
||||||
|
** $QT_BEGIN_LICENSE:LGPL21$
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and Digia. For licensing terms and
|
||||||
|
** conditions see http://qt.digia.com/licensing. For further information
|
||||||
|
** use the contact form at http://qt.digia.com/contact-us.
|
||||||
|
**
|
||||||
|
** GNU Lesser General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||||
|
** General Public License version 2.1 or version 3 as published by the Free
|
||||||
|
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
|
||||||
|
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
|
||||||
|
** following information to ensure the GNU Lesser General Public License
|
||||||
|
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
|
||||||
|
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||||
|
**
|
||||||
|
** In addition, as a special exception, Digia gives you certain additional
|
||||||
|
** rights. These rights are described in the Digia Qt LGPL Exception
|
||||||
|
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||||
|
**
|
||||||
|
** $QT_END_LICENSE$
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include "qlockfile_p.h"
|
||||||
|
|
||||||
|
#include <QTemporaryFile>
|
||||||
|
#include <QCoreApplication>
|
||||||
|
#include <QFileInfo>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QDateTime>
|
||||||
|
|
||||||
|
#include <sys/file.h> // flock
|
||||||
|
#include <sys/types.h> // kill
|
||||||
|
#include <signal.h> // kill
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
QT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
#define EINTR_LOOP(var, cmd) \
|
||||||
|
do { \
|
||||||
|
var = cmd; \
|
||||||
|
} while (var == -1 && errno == EINTR)
|
||||||
|
|
||||||
|
// don't call QT_OPEN or ::open
|
||||||
|
// call qt_safe_open
|
||||||
|
static inline int qt_safe_open(const char *pathname, int flags, mode_t mode = 0777)
|
||||||
|
{
|
||||||
|
#ifdef O_CLOEXEC
|
||||||
|
flags |= O_CLOEXEC;
|
||||||
|
#endif
|
||||||
|
int fd;
|
||||||
|
EINTR_LOOP(fd, ::open(pathname, flags, mode));
|
||||||
|
|
||||||
|
// unknown flags are ignored, so we have no way of verifying if
|
||||||
|
// O_CLOEXEC was accepted
|
||||||
|
if (fd != -1)
|
||||||
|
::fcntl(fd, F_SETFD, FD_CLOEXEC);
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline qint64 qt_safe_write(int fd, const void *data, qint64 len)
|
||||||
|
{
|
||||||
|
qint64 ret = 0;
|
||||||
|
EINTR_LOOP(ret, ::write(fd, data, len));
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static QString localHostName() // from QHostInfo::localHostName()
|
||||||
|
{
|
||||||
|
char hostName[512];
|
||||||
|
if (gethostname(hostName, sizeof(hostName)) == -1)
|
||||||
|
return QString();
|
||||||
|
hostName[sizeof(hostName) - 1] = '\0';
|
||||||
|
return QString::fromLocal8Bit(hostName);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ### merge into qt_safe_write?
|
||||||
|
static qint64 qt_write_loop(int fd, const char *data, qint64 len)
|
||||||
|
{
|
||||||
|
qint64 pos = 0;
|
||||||
|
while (pos < len) {
|
||||||
|
const qint64 ret = qt_safe_write(fd, data + pos, len - pos);
|
||||||
|
if (ret == -1) // e.g. partition full
|
||||||
|
return pos;
|
||||||
|
pos += ret;
|
||||||
|
}
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool setNativeLocks(int fd)
|
||||||
|
{
|
||||||
|
#if defined(LOCK_EX) && defined(LOCK_NB)
|
||||||
|
if (flock(fd, LOCK_EX | LOCK_NB) == -1) // other threads, and other processes on a local fs
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
struct flock flockData;
|
||||||
|
flockData.l_type = F_WRLCK;
|
||||||
|
flockData.l_whence = SEEK_SET;
|
||||||
|
flockData.l_start = 0;
|
||||||
|
flockData.l_len = 0; // 0 = entire file
|
||||||
|
flockData.l_pid = getpid();
|
||||||
|
if (fcntl(fd, F_SETLK, &flockData) == -1) // for networked filesystems
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
QLockFile::LockError QLockFilePrivate::tryLock_sys()
|
||||||
|
{
|
||||||
|
// Assemble data, to write in a single call to write
|
||||||
|
// (otherwise we'd have to check every write call)
|
||||||
|
// Use operator% from the fast builder to avoid multiple memory allocations.
|
||||||
|
QByteArray fileData = QByteArray::number(QCoreApplication::applicationPid()) + '\n'
|
||||||
|
+ qAppName().toUtf8() + '\n'
|
||||||
|
+ localHostName().toUtf8() + '\n';
|
||||||
|
|
||||||
|
const QByteArray lockFileName = QFile::encodeName(fileName);
|
||||||
|
const int fd = qt_safe_open(lockFileName.constData(), O_WRONLY | O_CREAT | O_EXCL, 0644);
|
||||||
|
if (fd < 0) {
|
||||||
|
switch (errno) {
|
||||||
|
case EEXIST:
|
||||||
|
return QLockFile::LockFailedError;
|
||||||
|
case EACCES:
|
||||||
|
case EROFS:
|
||||||
|
return QLockFile::PermissionError;
|
||||||
|
default:
|
||||||
|
return QLockFile::UnknownError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Ensure nobody else can delete the file while we have it
|
||||||
|
if (!setNativeLocks(fd))
|
||||||
|
qWarning() << "setNativeLocks failed:" << strerror(errno);
|
||||||
|
|
||||||
|
if (qt_write_loop(fd, fileData.constData(), fileData.size()) < fileData.size()) {
|
||||||
|
close(fd);
|
||||||
|
if (!QFile::remove(fileName))
|
||||||
|
qWarning("QLockFile: Could not remove our own lock file %s.", qPrintable(fileName));
|
||||||
|
return QLockFile::UnknownError; // partition full
|
||||||
|
}
|
||||||
|
|
||||||
|
// We hold the lock, continue.
|
||||||
|
fileHandle = fd;
|
||||||
|
|
||||||
|
return QLockFile::NoError;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool QLockFilePrivate::removeStaleLock()
|
||||||
|
{
|
||||||
|
const QByteArray lockFileName = QFile::encodeName(fileName);
|
||||||
|
const int fd = qt_safe_open(lockFileName.constData(), O_WRONLY, 0644);
|
||||||
|
if (fd < 0) // gone already?
|
||||||
|
return false;
|
||||||
|
bool success = setNativeLocks(fd) && (::unlink(lockFileName) == 0);
|
||||||
|
close(fd);
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool QLockFilePrivate::isApparentlyStale() const
|
||||||
|
{
|
||||||
|
qint64 pid;
|
||||||
|
QString hostname, appname;
|
||||||
|
if (!getLockInfo(&pid, &hostname, &appname))
|
||||||
|
return false;
|
||||||
|
if (hostname.isEmpty() || hostname == localHostName()) {
|
||||||
|
if (::kill(pid, 0) == -1 && errno == ESRCH)
|
||||||
|
return true; // PID doesn't exist anymore
|
||||||
|
}
|
||||||
|
const qint64 age = QFileInfo(fileName).lastModified().msecsTo(QDateTime::currentDateTime());
|
||||||
|
return staleLockTime > 0 && age > staleLockTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QLockFile::unlock()
|
||||||
|
{
|
||||||
|
Q_D(QLockFile);
|
||||||
|
if (!d->isLocked)
|
||||||
|
return;
|
||||||
|
close(d->fileHandle);
|
||||||
|
d->fileHandle = -1;
|
||||||
|
if (!QFile::remove(d->fileName)) {
|
||||||
|
qWarning() << "Could not remove our own lock file" << d->fileName << "maybe permissions changed meanwhile?";
|
||||||
|
// This is bad because other users of this lock file will now have to wait for the stale-lock-timeout...
|
||||||
|
}
|
||||||
|
QFile::remove(d->fileName);
|
||||||
|
d->lockError = QLockFile::NoError;
|
||||||
|
d->isLocked = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QT_END_NAMESPACE
|
178
src/core/qlockfile_win.cpp
Normal file
178
src/core/qlockfile_win.cpp
Normal file
|
@ -0,0 +1,178 @@
|
||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2013 David Faure <faure+bluesystems@kde.org>
|
||||||
|
** Contact: http://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of the QtCore module of the Qt Toolkit.
|
||||||
|
**
|
||||||
|
** $QT_BEGIN_LICENSE:LGPL21$
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see http://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at http://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU Lesser General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||||
|
** General Public License version 2.1 or version 3 as published by the Free
|
||||||
|
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
|
||||||
|
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
|
||||||
|
** following information to ensure the GNU Lesser General Public License
|
||||||
|
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
|
||||||
|
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||||
|
**
|
||||||
|
** As a special exception, The Qt Company gives you certain additional
|
||||||
|
** rights. These rights are described in The Qt Company LGPL Exception
|
||||||
|
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||||
|
**
|
||||||
|
** $QT_END_LICENSE$
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#ifndef _UNICODE
|
||||||
|
#define _UNICODE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef UNICODE
|
||||||
|
#define UNICODE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "qlockfile_p.h"
|
||||||
|
|
||||||
|
#include <qt_windows.h>
|
||||||
|
|
||||||
|
#include <QCoreApplication>
|
||||||
|
#include <QDir>
|
||||||
|
#include <QFileInfo>
|
||||||
|
#include <QDateTime>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
QT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
static inline QByteArray localHostName()
|
||||||
|
{
|
||||||
|
return qgetenv("COMPUTERNAME");
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool fileExists(const wchar_t *fileName)
|
||||||
|
{
|
||||||
|
WIN32_FILE_ATTRIBUTE_DATA data;
|
||||||
|
return GetFileAttributesEx(fileName, GetFileExInfoStandard, &data);
|
||||||
|
}
|
||||||
|
|
||||||
|
QLockFile::LockError QLockFilePrivate::tryLock_sys()
|
||||||
|
{
|
||||||
|
const ushort* nativePath = QDir::toNativeSeparators(fileName).utf16();
|
||||||
|
// When writing, allow others to read.
|
||||||
|
// When reading, QFile will allow others to read and write, all good.
|
||||||
|
// Adding FILE_SHARE_DELETE would allow forceful deletion of stale files,
|
||||||
|
// but Windows doesn't allow recreating it while this handle is open anyway,
|
||||||
|
// so this would only create confusion (can't lock, but no lock file to read from).
|
||||||
|
const DWORD dwShareMode = FILE_SHARE_READ;
|
||||||
|
#ifndef Q_OS_WINRT
|
||||||
|
SECURITY_ATTRIBUTES securityAtts = { sizeof(SECURITY_ATTRIBUTES), NULL, FALSE };
|
||||||
|
HANDLE fh = CreateFile((const wchar_t*)nativePath,
|
||||||
|
GENERIC_WRITE,
|
||||||
|
dwShareMode,
|
||||||
|
&securityAtts,
|
||||||
|
CREATE_NEW, // error if already exists
|
||||||
|
FILE_ATTRIBUTE_NORMAL,
|
||||||
|
NULL);
|
||||||
|
#else // !Q_OS_WINRT
|
||||||
|
HANDLE fh = CreateFile2((const wchar_t*)nativePath,
|
||||||
|
GENERIC_WRITE,
|
||||||
|
dwShareMode,
|
||||||
|
CREATE_NEW, // error if already exists
|
||||||
|
NULL);
|
||||||
|
#endif // Q_OS_WINRT
|
||||||
|
if (fh == INVALID_HANDLE_VALUE) {
|
||||||
|
const DWORD lastError = GetLastError();
|
||||||
|
switch (lastError) {
|
||||||
|
case ERROR_SHARING_VIOLATION:
|
||||||
|
case ERROR_ALREADY_EXISTS:
|
||||||
|
case ERROR_FILE_EXISTS:
|
||||||
|
return QLockFile::LockFailedError;
|
||||||
|
case ERROR_ACCESS_DENIED:
|
||||||
|
// readonly file, or file still in use by another process.
|
||||||
|
// Assume the latter if the file exists, since we don't create it readonly.
|
||||||
|
return fileExists((const wchar_t*)nativePath)
|
||||||
|
? QLockFile::LockFailedError
|
||||||
|
: QLockFile::PermissionError;
|
||||||
|
default:
|
||||||
|
qWarning() << "Got unexpected locking error" << lastError;
|
||||||
|
return QLockFile::UnknownError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We hold the lock, continue.
|
||||||
|
fileHandle = fh;
|
||||||
|
// Assemble data, to write in a single call to write
|
||||||
|
// (otherwise we'd have to check every write call)
|
||||||
|
QByteArray fileData;
|
||||||
|
fileData += QByteArray::number(QCoreApplication::applicationPid());
|
||||||
|
fileData += '\n';
|
||||||
|
fileData += QCoreApplication::applicationName().toUtf8();
|
||||||
|
fileData += '\n';
|
||||||
|
fileData += localHostName();
|
||||||
|
fileData += '\n';
|
||||||
|
DWORD bytesWritten = 0;
|
||||||
|
QLockFile::LockError error = QLockFile::NoError;
|
||||||
|
if (!WriteFile(fh, fileData.constData(), fileData.size(), &bytesWritten, NULL) || !FlushFileBuffers(fh))
|
||||||
|
error = QLockFile::UnknownError; // partition full
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool QLockFilePrivate::removeStaleLock()
|
||||||
|
{
|
||||||
|
// QFile::remove fails on Windows if the other process is still using the file, so it's not stale.
|
||||||
|
return QFile::remove(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool QLockFilePrivate::isApparentlyStale() const
|
||||||
|
{
|
||||||
|
qint64 pid;
|
||||||
|
QString hostname, appname;
|
||||||
|
if (!getLockInfo(&pid, &hostname, &appname))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// On WinRT there seems to be no way of obtaining information about other
|
||||||
|
// processes due to sandboxing
|
||||||
|
#ifndef Q_OS_WINRT
|
||||||
|
if (hostname == QString::fromLocal8Bit(localHostName())) {
|
||||||
|
HANDLE procHandle = ::OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid);
|
||||||
|
if (!procHandle)
|
||||||
|
return true;
|
||||||
|
// We got a handle but check if process is still alive
|
||||||
|
DWORD dwR = ::WaitForSingleObject(procHandle, 0);
|
||||||
|
::CloseHandle(procHandle);
|
||||||
|
if (dwR == WAIT_TIMEOUT)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif // !Q_OS_WINRT
|
||||||
|
const qint64 age = QFileInfo(fileName).lastModified().msecsTo(QDateTime::currentDateTime());
|
||||||
|
return staleLockTime > 0 && age > staleLockTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QLockFile::unlock()
|
||||||
|
{
|
||||||
|
Q_D(QLockFile);
|
||||||
|
if (!d->isLocked)
|
||||||
|
return;
|
||||||
|
CloseHandle(d->fileHandle);
|
||||||
|
int attempts = 0;
|
||||||
|
static const int maxAttempts = 500; // 500ms
|
||||||
|
while (!QFile::remove(d->fileName) && ++attempts < maxAttempts) {
|
||||||
|
// Someone is reading the lock file right now (on Windows this prevents deleting it).
|
||||||
|
QLockFileThread::msleep(1);
|
||||||
|
}
|
||||||
|
if (attempts == maxAttempts) {
|
||||||
|
qWarning() << "Could not remove our own lock file" << d->fileName << ". Either other users of the lock file are reading it constantly for 500 ms, or we (no longer) have permissions to delete the file";
|
||||||
|
// This is bad because other users of this lock file will now have to wait for the stale-lock-timeout...
|
||||||
|
}
|
||||||
|
d->lockError = QLockFile::NoError;
|
||||||
|
d->isLocked = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QT_END_NAMESPACE
|
|
@ -36,6 +36,7 @@
|
||||||
|
|
||||||
DatabaseManagerStruct::DatabaseManagerStruct()
|
DatabaseManagerStruct::DatabaseManagerStruct()
|
||||||
: dbWidget(Q_NULLPTR)
|
: dbWidget(Q_NULLPTR)
|
||||||
|
, lockFile(Q_NULLPTR)
|
||||||
, saveToFilename(false)
|
, saveToFilename(false)
|
||||||
, modified(false)
|
, modified(false)
|
||||||
, readOnly(false)
|
, readOnly(false)
|
||||||
|
@ -142,8 +143,35 @@ void DatabaseTabWidget::openDatabase(const QString& fileName, const QString& pw,
|
||||||
}
|
}
|
||||||
file.close();
|
file.close();
|
||||||
|
|
||||||
|
QLockFile* lockFile = new QLockFile(QString("%1/.%2.lock").arg(fileInfo.canonicalPath(), fileInfo.fileName()));
|
||||||
|
lockFile->setStaleLockTime(0);
|
||||||
|
|
||||||
|
if (!dbStruct.readOnly && !lockFile->tryLock()) {
|
||||||
|
// for now silently ignore if we can't create a lock file
|
||||||
|
// due to lack of permissions
|
||||||
|
if (lockFile->error() != QLockFile::PermissionError) {
|
||||||
|
QMessageBox::StandardButton result = MessageBox::question(this, tr("Open database"),
|
||||||
|
tr("The database you are trying to open is locked by another instance of KeePassX.\n"
|
||||||
|
"Do you want to open it anyway? Alternatively the database is opened read-only."),
|
||||||
|
QMessageBox::Yes | QMessageBox::No);
|
||||||
|
|
||||||
|
if (result == QMessageBox::No) {
|
||||||
|
dbStruct.readOnly = true;
|
||||||
|
delete lockFile;
|
||||||
|
lockFile = Q_NULLPTR;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// take over the lock file if possible
|
||||||
|
if (lockFile->removeStaleLockFile()) {
|
||||||
|
lockFile->tryLock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Database* db = new Database();
|
Database* db = new Database();
|
||||||
dbStruct.dbWidget = new DatabaseWidget(db, this);
|
dbStruct.dbWidget = new DatabaseWidget(db, this);
|
||||||
|
dbStruct.lockFile = lockFile;
|
||||||
dbStruct.saveToFilename = !dbStruct.readOnly;
|
dbStruct.saveToFilename = !dbStruct.readOnly;
|
||||||
|
|
||||||
dbStruct.filePath = fileInfo.absoluteFilePath();
|
dbStruct.filePath = fileInfo.absoluteFilePath();
|
||||||
|
@ -238,6 +266,7 @@ void DatabaseTabWidget::deleteDatabase(Database* db)
|
||||||
removeTab(index);
|
removeTab(index);
|
||||||
toggleTabbar();
|
toggleTabbar();
|
||||||
m_dbList.remove(db);
|
m_dbList.remove(db);
|
||||||
|
delete dbStruct.lockFile;
|
||||||
delete dbStruct.dbWidget;
|
delete dbStruct.dbWidget;
|
||||||
delete db;
|
delete db;
|
||||||
|
|
||||||
|
@ -311,6 +340,11 @@ bool DatabaseTabWidget::saveDatabaseAs(Database* db)
|
||||||
dbStruct.canonicalFilePath = fileInfo.canonicalFilePath();
|
dbStruct.canonicalFilePath = fileInfo.canonicalFilePath();
|
||||||
dbStruct.fileName = fileInfo.fileName();
|
dbStruct.fileName = fileInfo.fileName();
|
||||||
dbStruct.dbWidget->updateFilename(dbStruct.filePath);
|
dbStruct.dbWidget->updateFilename(dbStruct.filePath);
|
||||||
|
QString lockFileName = QString("%1/.%2.lock")
|
||||||
|
.arg(fileInfo.canonicalPath(), fileInfo.fileName());
|
||||||
|
dbStruct.lockFile = new QLockFile(lockFileName);
|
||||||
|
dbStruct.lockFile->setStaleLockTime(0);
|
||||||
|
dbStruct.lockFile->tryLock();
|
||||||
updateTabName(db);
|
updateTabName(db);
|
||||||
updateLastDatabases(dbStruct.filePath);
|
updateLastDatabases(dbStruct.filePath);
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
#include <QHash>
|
#include <QHash>
|
||||||
#include <QTabWidget>
|
#include <QTabWidget>
|
||||||
|
|
||||||
|
#include "core/qlockfile.h"
|
||||||
#include "format/KeePass2Writer.h"
|
#include "format/KeePass2Writer.h"
|
||||||
#include "gui/DatabaseWidget.h"
|
#include "gui/DatabaseWidget.h"
|
||||||
|
|
||||||
|
@ -34,6 +35,7 @@ struct DatabaseManagerStruct
|
||||||
DatabaseManagerStruct();
|
DatabaseManagerStruct();
|
||||||
|
|
||||||
DatabaseWidget* dbWidget;
|
DatabaseWidget* dbWidget;
|
||||||
|
QLockFile* lockFile;
|
||||||
QString filePath;
|
QString filePath;
|
||||||
QString canonicalFilePath;
|
QString canonicalFilePath;
|
||||||
QString fileName;
|
QString fileName;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue