mirror of
				https://github.com/keepassxreboot/keepassxc.git
				synced 2025-11-03 15:18:08 -05:00 
			
		
		
		
	Remove QSaveFile and QLockFile.
They are part of Qt >= 5.1.
This commit is contained in:
		
							parent
							
								
									813c64a055
								
							
						
					
					
						commit
						531018e58d
					
				
					 14 changed files with 3 additions and 1886 deletions
				
			
		| 
						 | 
				
			
			@ -54,9 +54,6 @@ set(keepassx_SOURCES
 | 
			
		|||
    core/ListDeleter.h
 | 
			
		||||
    core/Metadata.cpp
 | 
			
		||||
    core/PasswordGenerator.cpp
 | 
			
		||||
    core/qlockfile.cpp
 | 
			
		||||
    core/qsavefile.cpp
 | 
			
		||||
    core/qsavefile_p.h
 | 
			
		||||
    core/SignalMultiplexer.cpp
 | 
			
		||||
    core/TimeDelta.cpp
 | 
			
		||||
    core/TimeInfo.cpp
 | 
			
		||||
| 
						 | 
				
			
			@ -143,18 +140,6 @@ if(NOT GCRYPT_HAS_SALSA20)
 | 
			
		|||
  )
 | 
			
		||||
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
 | 
			
		||||
    main.cpp
 | 
			
		||||
)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,344 +0,0 @@
 | 
			
		|||
/****************************************************************************
 | 
			
		||||
**
 | 
			
		||||
** 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"
 | 
			
		||||
#if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)
 | 
			
		||||
#    include <QElapsedTimer>
 | 
			
		||||
#else
 | 
			
		||||
#    include <QTime>
 | 
			
		||||
#endif
 | 
			
		||||
#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);
 | 
			
		||||
#if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)
 | 
			
		||||
    QElapsedTimer timer;
 | 
			
		||||
#else
 | 
			
		||||
    QTime timer;
 | 
			
		||||
#endif
 | 
			
		||||
    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.elapsed() > 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
 | 
			
		||||
| 
						 | 
				
			
			@ -1,79 +0,0 @@
 | 
			
		|||
/****************************************************************************
 | 
			
		||||
**
 | 
			
		||||
** 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
 | 
			
		||||
| 
						 | 
				
			
			@ -1,101 +0,0 @@
 | 
			
		|||
/****************************************************************************
 | 
			
		||||
**
 | 
			
		||||
** 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;
 | 
			
		||||
    static QString processNameByPid(qint64 pid);
 | 
			
		||||
 | 
			
		||||
    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 */
 | 
			
		||||
| 
						 | 
				
			
			@ -1,244 +0,0 @@
 | 
			
		|||
/****************************************************************************
 | 
			
		||||
**
 | 
			
		||||
** Copyright (C) 2013 David Faure <faure+bluesystems@kde.org>
 | 
			
		||||
** Copyright (C) 2015 The Qt Company Ltd.
 | 
			
		||||
** 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>    // gethostname
 | 
			
		||||
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
 | 
			
		||||
#if defined(Q_OS_MAC)
 | 
			
		||||
#   include <libproc.h>
 | 
			
		||||
#elif defined(Q_OS_LINUX)
 | 
			
		||||
#   include <unistd.h>
 | 
			
		||||
#   include <cstdio>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
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 QByteArray localHostName() // from QHostInfo::localHostName(), modified to return a QByteArray
 | 
			
		||||
{
 | 
			
		||||
    QByteArray hostName(512, Qt::Uninitialized);
 | 
			
		||||
    if (gethostname(hostName.data(), hostName.size()) == -1)
 | 
			
		||||
        return QByteArray();
 | 
			
		||||
    hostName.truncate(strlen(hostName.data()));
 | 
			
		||||
    return 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'
 | 
			
		||||
                          + QCoreApplication::applicationName().toUtf8() + '\n'
 | 
			
		||||
                          + localHostName() + '\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;
 | 
			
		||||
#ifdef Q_OS_MAC
 | 
			
		||||
    // ugly workaround: ignore setNativeLocks() result on Mac since it's broken there
 | 
			
		||||
    setNativeLocks(fd);
 | 
			
		||||
    bool success = (::unlink(lockFileName) == 0);
 | 
			
		||||
#else
 | 
			
		||||
    bool success = setNativeLocks(fd) && (::unlink(lockFileName) == 0);
 | 
			
		||||
#endif
 | 
			
		||||
    close(fd);
 | 
			
		||||
    return success;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool QLockFilePrivate::isApparentlyStale() const
 | 
			
		||||
{
 | 
			
		||||
    qint64 pid;
 | 
			
		||||
    QString hostname, appname;
 | 
			
		||||
    if (getLockInfo(&pid, &hostname, &appname)) {
 | 
			
		||||
        if (hostname.isEmpty() || hostname == QString::fromLocal8Bit(localHostName())) {
 | 
			
		||||
            if (::kill(pid, 0) == -1 && errno == ESRCH)
 | 
			
		||||
                return true; // PID doesn't exist anymore
 | 
			
		||||
            const QString processName = processNameByPid(pid);
 | 
			
		||||
            if (!processName.isEmpty()) {
 | 
			
		||||
                QFileInfo fi(appname);
 | 
			
		||||
                if (fi.isSymLink())
 | 
			
		||||
                    fi.setFile(fi.symLinkTarget());
 | 
			
		||||
                if (processName.toLower() != fi.fileName().toLower())
 | 
			
		||||
                    return true; // PID got reused by a different application.
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    const qint64 age = QFileInfo(fileName).lastModified().secsTo(QDateTime::currentDateTime()) * 1000;
 | 
			
		||||
    return staleLockTime > 0 && age > staleLockTime;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
QString QLockFilePrivate::processNameByPid(qint64 pid)
 | 
			
		||||
{
 | 
			
		||||
#if defined(Q_OS_MAC)
 | 
			
		||||
    char name[1024];
 | 
			
		||||
    proc_name(pid, name, sizeof(name) / sizeof(char));
 | 
			
		||||
    return QFile::decodeName(name);
 | 
			
		||||
#elif defined(Q_OS_LINUX)
 | 
			
		||||
    if (!QFile::exists(QString("/proc/version")))
 | 
			
		||||
        return QString();
 | 
			
		||||
    char exePath[64];
 | 
			
		||||
    char buf[PATH_MAX + 1];
 | 
			
		||||
    sprintf(exePath, "/proc/%lld/exe", pid);
 | 
			
		||||
    size_t len = static_cast<size_t>(readlink(exePath, buf, sizeof(buf)));
 | 
			
		||||
    if (len >= sizeof(buf)) {
 | 
			
		||||
        // The pid is gone. Return some invalid process name to fail the test.
 | 
			
		||||
        return QString("/ERROR/");
 | 
			
		||||
    }
 | 
			
		||||
    buf[len] = 0;
 | 
			
		||||
    return QFileInfo(QFile::decodeName(buf)).fileName();
 | 
			
		||||
#else
 | 
			
		||||
    return QString();
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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...
 | 
			
		||||
    }
 | 
			
		||||
    d->lockError = QLockFile::NoError;
 | 
			
		||||
    d->isLocked = false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
QT_END_NAMESPACE
 | 
			
		||||
| 
						 | 
				
			
			@ -1,227 +0,0 @@
 | 
			
		|||
/****************************************************************************
 | 
			
		||||
**
 | 
			
		||||
** Copyright (C) 2013 David Faure <faure+bluesystems@kde.org>
 | 
			
		||||
** Copyright (C) 2015 The Qt Company Ltd.
 | 
			
		||||
** 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;
 | 
			
		||||
 | 
			
		||||
    // On WinRT there seems to be no way of obtaining information about other
 | 
			
		||||
    // processes due to sandboxing
 | 
			
		||||
#ifndef Q_OS_WINRT
 | 
			
		||||
    if (getLockInfo(&pid, &hostname, &appname)) {
 | 
			
		||||
        if (hostname.isEmpty() || 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;
 | 
			
		||||
            const QString processName = processNameByPid(pid);
 | 
			
		||||
            if (!processName.isEmpty() && processName != appname)
 | 
			
		||||
                return true; // PID got reused by a different application.
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
#else // !Q_OS_WINRT
 | 
			
		||||
    Q_UNUSED(pid);
 | 
			
		||||
    Q_UNUSED(hostname);
 | 
			
		||||
    Q_UNUSED(appname);
 | 
			
		||||
#endif // Q_OS_WINRT
 | 
			
		||||
    const qint64 age = QFileInfo(fileName).lastModified().msecsTo(QDateTime::currentDateTime());
 | 
			
		||||
    return staleLockTime > 0 && age > staleLockTime;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
QString QLockFilePrivate::processNameByPid(qint64 pid)
 | 
			
		||||
{
 | 
			
		||||
#if !defined(Q_OS_WINRT) && !defined(Q_OS_WINCE)
 | 
			
		||||
    typedef DWORD (WINAPI *GetModuleFileNameExFunc)(HANDLE, HMODULE, LPTSTR, DWORD);
 | 
			
		||||
 | 
			
		||||
    HMODULE hPsapi = LoadLibraryA("psapi");
 | 
			
		||||
    if (!hPsapi)
 | 
			
		||||
        return QString();
 | 
			
		||||
 | 
			
		||||
    GetModuleFileNameExFunc qGetModuleFileNameEx
 | 
			
		||||
            = (GetModuleFileNameExFunc)GetProcAddress(hPsapi, "GetModuleFileNameExW");
 | 
			
		||||
    if (!qGetModuleFileNameEx) {
 | 
			
		||||
        FreeLibrary(hPsapi);
 | 
			
		||||
        return QString();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, DWORD(pid));
 | 
			
		||||
    if (!hProcess) {
 | 
			
		||||
        FreeLibrary(hPsapi);
 | 
			
		||||
        return QString();
 | 
			
		||||
    }
 | 
			
		||||
    wchar_t buf[MAX_PATH];
 | 
			
		||||
    const DWORD length = qGetModuleFileNameEx(hProcess, NULL, buf, sizeof(buf) / sizeof(wchar_t));
 | 
			
		||||
    CloseHandle(hProcess);
 | 
			
		||||
    FreeLibrary(hPsapi);
 | 
			
		||||
    if (!length)
 | 
			
		||||
        return QString();
 | 
			
		||||
    QString name = QString::fromWCharArray(buf, length);
 | 
			
		||||
    int i = name.lastIndexOf(QLatin1Char('\\'));
 | 
			
		||||
    if (i >= 0)
 | 
			
		||||
        name.remove(0, i + 1);
 | 
			
		||||
    i = name.lastIndexOf(QLatin1Char('.'));
 | 
			
		||||
    if (i >= 0)
 | 
			
		||||
        name.truncate(i);
 | 
			
		||||
    return name;
 | 
			
		||||
#else
 | 
			
		||||
    Q_UNUSED(pid);
 | 
			
		||||
    return QString();
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
| 
						 | 
				
			
			@ -1,452 +0,0 @@
 | 
			
		|||
/****************************************************************************
 | 
			
		||||
**
 | 
			
		||||
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
 | 
			
		||||
** Contact: http://www.qt-project.org/
 | 
			
		||||
**
 | 
			
		||||
** This file is part of the QtCore module of the Qt Toolkit.
 | 
			
		||||
**
 | 
			
		||||
** $QT_BEGIN_LICENSE:LGPL$
 | 
			
		||||
** GNU Lesser General Public License Usage
 | 
			
		||||
** This file may be used under the terms of the GNU Lesser General Public
 | 
			
		||||
** License version 2.1 as published by the Free Software Foundation and
 | 
			
		||||
** appearing in the file LICENSE.LGPL included in the packaging of this
 | 
			
		||||
** file. Please review the following information to ensure the GNU Lesser
 | 
			
		||||
** General Public License version 2.1 requirements will be met:
 | 
			
		||||
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
 | 
			
		||||
**
 | 
			
		||||
** In addition, as a special exception, Nokia gives you certain additional
 | 
			
		||||
** rights. These rights are described in the Nokia Qt LGPL Exception
 | 
			
		||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
 | 
			
		||||
**
 | 
			
		||||
** GNU General Public License Usage
 | 
			
		||||
** Alternatively, this file may be used under the terms of the GNU General
 | 
			
		||||
** Public License version 3.0 as published by the Free Software Foundation
 | 
			
		||||
** and appearing in the file LICENSE.GPL included in the packaging of this
 | 
			
		||||
** file. Please review the following information to ensure the GNU General
 | 
			
		||||
** Public License version 3.0 requirements will be met:
 | 
			
		||||
** http://www.gnu.org/copyleft/gpl.html.
 | 
			
		||||
**
 | 
			
		||||
** Other Usage
 | 
			
		||||
** Alternatively, this file may be used in accordance with the terms and
 | 
			
		||||
** conditions contained in a signed written agreement between you and Nokia.
 | 
			
		||||
**
 | 
			
		||||
**
 | 
			
		||||
**
 | 
			
		||||
**
 | 
			
		||||
**
 | 
			
		||||
**
 | 
			
		||||
** $QT_END_LICENSE$
 | 
			
		||||
**
 | 
			
		||||
****************************************************************************/
 | 
			
		||||
 | 
			
		||||
#include "qsavefile.h"
 | 
			
		||||
#include "qsavefile_p.h"
 | 
			
		||||
 | 
			
		||||
#include <QAbstractFileEngine>
 | 
			
		||||
#include <QFileInfo>
 | 
			
		||||
#include <QTemporaryFile>
 | 
			
		||||
 | 
			
		||||
#ifdef Q_OS_WIN
 | 
			
		||||
#  include <windows.h>
 | 
			
		||||
#else
 | 
			
		||||
#  include <unistd.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
QSaveFilePrivate::QSaveFilePrivate()
 | 
			
		||||
    : tempFile(0), error(QFile::NoError)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
QSaveFilePrivate::~QSaveFilePrivate()
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*!
 | 
			
		||||
    \class QSaveFile
 | 
			
		||||
    \brief The QSaveFile class provides an interface for safely writing to files.
 | 
			
		||||
 | 
			
		||||
    \ingroup io
 | 
			
		||||
 | 
			
		||||
    \reentrant
 | 
			
		||||
 | 
			
		||||
    QSaveFile is an I/O device for writing text and binary files, without losing
 | 
			
		||||
    existing data if the writing operation fails.
 | 
			
		||||
 | 
			
		||||
    While writing, the contents will be written to a temporary file, and if
 | 
			
		||||
    no error happened, commit() will move it to the final file. This ensures that
 | 
			
		||||
    no data at the final file is lost in case an error happens while writing,
 | 
			
		||||
    and no partially-written file is ever present at the final location. Always
 | 
			
		||||
    use QSaveFile when saving entire documents to disk.
 | 
			
		||||
 | 
			
		||||
    QSaveFile automatically detects errors while writing, such as the full partition
 | 
			
		||||
    situation, where write() cannot write all the bytes. It will remember that
 | 
			
		||||
    an error happened, and will discard the temporary file in commit().
 | 
			
		||||
 | 
			
		||||
    Much like with QFile, the file is opened with open(). Data is usually read
 | 
			
		||||
    and written using QDataStream or QTextStream, but you can also call the
 | 
			
		||||
    QIODevice-inherited functions read(), readLine(), readAll(), write().
 | 
			
		||||
 | 
			
		||||
    Unlike QFile, calling close() is not allowed. commit() replaces it. If commit()
 | 
			
		||||
    was not called and the QSaveFile instance is destroyed, the temporary file is
 | 
			
		||||
    discarded.
 | 
			
		||||
 | 
			
		||||
    \sa QTextStream, QDataStream, QFileInfo, QDir, QFile, QTemporaryFile
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
/*!
 | 
			
		||||
    \internal
 | 
			
		||||
*/
 | 
			
		||||
QSaveFile::QSaveFile()
 | 
			
		||||
    : QIODevice(), d_ptr(new QSaveFilePrivate)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
/*!
 | 
			
		||||
    Constructs a new file object with the given \a parent.
 | 
			
		||||
*/
 | 
			
		||||
QSaveFile::QSaveFile(QObject *parent)
 | 
			
		||||
    : QIODevice(parent), d_ptr(new QSaveFilePrivate)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
/*!
 | 
			
		||||
    Constructs a new file object to represent the file with the given \a name.
 | 
			
		||||
*/
 | 
			
		||||
QSaveFile::QSaveFile(const QString &name)
 | 
			
		||||
    : QIODevice(0), d_ptr(new QSaveFilePrivate)
 | 
			
		||||
{
 | 
			
		||||
    Q_D(QSaveFile);
 | 
			
		||||
    d->fileName = name;
 | 
			
		||||
}
 | 
			
		||||
/*!
 | 
			
		||||
    Constructs a new file object with the given \a parent to represent the
 | 
			
		||||
    file with the specified \a name.
 | 
			
		||||
*/
 | 
			
		||||
QSaveFile::QSaveFile(const QString &name, QObject *parent)
 | 
			
		||||
    : QIODevice(parent), d_ptr(new QSaveFilePrivate)
 | 
			
		||||
{
 | 
			
		||||
    Q_D(QSaveFile);
 | 
			
		||||
    d->fileName = name;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*!
 | 
			
		||||
    Destroys the file object, discarding the saved contents unless commit() was called.
 | 
			
		||||
*/
 | 
			
		||||
QSaveFile::~QSaveFile()
 | 
			
		||||
{
 | 
			
		||||
    Q_D(QSaveFile);
 | 
			
		||||
    if (d->tempFile) {
 | 
			
		||||
        d->tempFile->setAutoRemove(true);
 | 
			
		||||
        delete d->tempFile;
 | 
			
		||||
    }
 | 
			
		||||
    QIODevice::close();
 | 
			
		||||
    delete d;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*!
 | 
			
		||||
    Returns false since temporary files support random access.
 | 
			
		||||
 | 
			
		||||
    \sa QIODevice::isSequential()
 | 
			
		||||
*/
 | 
			
		||||
bool QSaveFile::isSequential() const
 | 
			
		||||
{
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*!
 | 
			
		||||
    Returns the file error status.
 | 
			
		||||
 | 
			
		||||
    The I/O device status returns an error code. For example, if open()
 | 
			
		||||
    returns false, or a read/write operation returns -1, this function can
 | 
			
		||||
    be called to find out the reason why the operation failed.
 | 
			
		||||
 | 
			
		||||
    Unlike QFile which clears the error on the next operation, QSaveFile remembers
 | 
			
		||||
    the error until the file is closed, in order to discard the file contents in close().
 | 
			
		||||
 | 
			
		||||
    \sa unsetError()
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
QFile::FileError QSaveFile::error() const
 | 
			
		||||
{
 | 
			
		||||
    return d_func()->error;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*!
 | 
			
		||||
    Sets the file's error to QFile::NoError.
 | 
			
		||||
 | 
			
		||||
    This will make QSaveFile forget that an error happened during saving, so you
 | 
			
		||||
    probably don't want to call this, unless you're really sure that you want to
 | 
			
		||||
    save the file anyway.
 | 
			
		||||
 | 
			
		||||
    \sa error()
 | 
			
		||||
*/
 | 
			
		||||
void QSaveFile::unsetError()
 | 
			
		||||
{
 | 
			
		||||
    d_func()->error = QFile::NoError;
 | 
			
		||||
    setErrorString(QString());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*!
 | 
			
		||||
    Returns the name set by setFileName() or to the QSaveFile
 | 
			
		||||
    constructor.
 | 
			
		||||
 | 
			
		||||
    \sa setFileName()
 | 
			
		||||
*/
 | 
			
		||||
QString QSaveFile::fileName() const
 | 
			
		||||
{
 | 
			
		||||
    return d_func()->fileName;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*!
 | 
			
		||||
    Sets the \a name of the file. The name can have no path, a
 | 
			
		||||
    relative path, or an absolute path.
 | 
			
		||||
 | 
			
		||||
    \sa QFile::setFileName(), fileName()
 | 
			
		||||
*/
 | 
			
		||||
void QSaveFile::setFileName(const QString &name)
 | 
			
		||||
{
 | 
			
		||||
    d_func()->fileName = name;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*!
 | 
			
		||||
    Opens the file using OpenMode \a mode, returning true if successful;
 | 
			
		||||
    otherwise false.
 | 
			
		||||
 | 
			
		||||
    Important: the \a mode must be QIODevice::WriteOnly.
 | 
			
		||||
    It may also have additional flags, such as QIODevice::Text and QIODevice::Unbuffered.
 | 
			
		||||
 | 
			
		||||
    QIODevice::ReadWrite and QIODevice::Append are not supported at the moment.
 | 
			
		||||
 | 
			
		||||
    \sa QIODevice::OpenMode, setFileName()
 | 
			
		||||
*/
 | 
			
		||||
bool QSaveFile::open(OpenMode mode)
 | 
			
		||||
{
 | 
			
		||||
    Q_D(QSaveFile);
 | 
			
		||||
    if (isOpen()) {
 | 
			
		||||
        qWarning("QSaveFile::open: File (%s) already open", qPrintable(fileName()));
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
    unsetError();
 | 
			
		||||
    if ((mode & (ReadOnly | WriteOnly)) == 0) {
 | 
			
		||||
        qWarning("QSaveFile::open: Open mode not specified");
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
    // In the future we could implement Append and ReadWrite by copying from the existing file to the temp file...
 | 
			
		||||
    if ((mode & ReadOnly) || (mode & Append)) {
 | 
			
		||||
        qWarning("QSaveFile::open: Unsupported open mode %d", int(mode));
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // check if existing file is writable
 | 
			
		||||
    QFileInfo existingFile(d->fileName);
 | 
			
		||||
    if (existingFile.exists() && !existingFile.isWritable()) {
 | 
			
		||||
        d->error = QFile::WriteError;
 | 
			
		||||
        setErrorString(QSaveFile::tr("Existing file %1 is not writable").arg(d->fileName));
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
    d->tempFile = new QTemporaryFile;
 | 
			
		||||
    d->tempFile->setAutoRemove(false);
 | 
			
		||||
    d->tempFile->setFileTemplate(d->fileName);
 | 
			
		||||
    if (!d->tempFile->open()) {
 | 
			
		||||
        d->error = d->tempFile->error();
 | 
			
		||||
        setErrorString(d->tempFile->errorString());
 | 
			
		||||
        delete d->tempFile;
 | 
			
		||||
        d->tempFile = 0;
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
    QIODevice::open(mode);
 | 
			
		||||
    if (existingFile.exists())
 | 
			
		||||
        d->tempFile->setPermissions(existingFile.permissions());
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*!
 | 
			
		||||
  \reimp
 | 
			
		||||
  Cannot be called.
 | 
			
		||||
  Call commit() instead.
 | 
			
		||||
*/
 | 
			
		||||
void QSaveFile::close()
 | 
			
		||||
{
 | 
			
		||||
    qFatal("QSaveFile::close called");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
  Commits the changes to disk, if all previous writes were successful.
 | 
			
		||||
 | 
			
		||||
  It is mandatory to call this at the end of the saving operation, otherwise the file will be
 | 
			
		||||
  discarded.
 | 
			
		||||
 | 
			
		||||
  If an error happened during writing, deletes the temporary file and returns false.
 | 
			
		||||
  Otherwise, renames it to the final fileName and returns true on success.
 | 
			
		||||
  Finally, closes the device.
 | 
			
		||||
 | 
			
		||||
  \sa cancelWriting()
 | 
			
		||||
*/
 | 
			
		||||
bool QSaveFile::commit()
 | 
			
		||||
{
 | 
			
		||||
    Q_D(QSaveFile);
 | 
			
		||||
    if (!d->tempFile)
 | 
			
		||||
        return false;
 | 
			
		||||
    if (!isOpen()) {
 | 
			
		||||
        qWarning("QSaveFile::commit: File (%s) is not open", qPrintable(fileName()));
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
    flush();
 | 
			
		||||
#ifdef Q_OS_WIN
 | 
			
		||||
    FlushFileBuffers(reinterpret_cast<HANDLE>(handle()));
 | 
			
		||||
#elif defined(_POSIX_SYNCHRONIZED_IO) && _POSIX_SYNCHRONIZED_IO > 0
 | 
			
		||||
    fdatasync(d->tempFile->handle());
 | 
			
		||||
#else
 | 
			
		||||
    fsync(d->tempFile->handle());
 | 
			
		||||
#endif
 | 
			
		||||
    QIODevice::close();
 | 
			
		||||
    if (d->error != QFile::NoError) {
 | 
			
		||||
        d->tempFile->remove();
 | 
			
		||||
        unsetError();
 | 
			
		||||
        delete d->tempFile;
 | 
			
		||||
        d->tempFile = 0;
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
    d->tempFile->close();
 | 
			
		||||
#ifdef Q_OS_WIN
 | 
			
		||||
    // On Windows QAbstractFileEngine::rename() fails if the the target exists,
 | 
			
		||||
    // so we have to rename the target.
 | 
			
		||||
    // Ideally the winapi ReplaceFile() method should be used.
 | 
			
		||||
    QString bakname = d->fileName + "~";
 | 
			
		||||
    QFile::remove(bakname);
 | 
			
		||||
    QFile::rename(d->fileName, bakname);
 | 
			
		||||
#endif
 | 
			
		||||
    QAbstractFileEngine* fileEngine = d->tempFile->fileEngine();
 | 
			
		||||
    Q_ASSERT(fileEngine);
 | 
			
		||||
    if (!fileEngine->rename(d->fileName)) {
 | 
			
		||||
        d->error = fileEngine->error();
 | 
			
		||||
        setErrorString(fileEngine->errorString());
 | 
			
		||||
        d->tempFile->remove();
 | 
			
		||||
        delete d->tempFile;
 | 
			
		||||
        d->tempFile = 0;
 | 
			
		||||
#ifdef Q_OS_WIN
 | 
			
		||||
        QFile::rename(bakname, d->fileName);
 | 
			
		||||
#endif
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
    delete d->tempFile;
 | 
			
		||||
    d->tempFile = 0;
 | 
			
		||||
#ifdef Q_OS_WIN
 | 
			
		||||
    QFile::remove(bakname);
 | 
			
		||||
#endif
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*!
 | 
			
		||||
  Sets an error code so that commit() discards the temporary file.
 | 
			
		||||
 | 
			
		||||
  Further write operations are possible after calling this method, but none
 | 
			
		||||
  of it will have any effect, the written file will be discarded.
 | 
			
		||||
 | 
			
		||||
  \sa commit()
 | 
			
		||||
*/
 | 
			
		||||
void QSaveFile::cancelWriting()
 | 
			
		||||
{
 | 
			
		||||
    if (!isOpen())
 | 
			
		||||
        return;
 | 
			
		||||
    d_func()->error = QFile::WriteError;
 | 
			
		||||
    setErrorString(QSaveFile::tr("Writing canceled by application"));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*!
 | 
			
		||||
  Returns the size of the file.
 | 
			
		||||
  \sa QFile::size()
 | 
			
		||||
*/
 | 
			
		||||
qint64 QSaveFile::size() const
 | 
			
		||||
{
 | 
			
		||||
    Q_D(const QSaveFile);
 | 
			
		||||
    return d->tempFile ? d->tempFile->size() : qint64(-1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*!
 | 
			
		||||
  \reimp
 | 
			
		||||
*/
 | 
			
		||||
qint64 QSaveFile::pos() const
 | 
			
		||||
{
 | 
			
		||||
    Q_D(const QSaveFile);
 | 
			
		||||
    return d->tempFile ? d->tempFile->pos() : qint64(-1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*!
 | 
			
		||||
  \reimp
 | 
			
		||||
*/
 | 
			
		||||
bool QSaveFile::seek(qint64 offset)
 | 
			
		||||
{
 | 
			
		||||
    Q_D(QSaveFile);
 | 
			
		||||
    return d->tempFile ? d->tempFile->seek(offset) : false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*!
 | 
			
		||||
  \reimp
 | 
			
		||||
*/
 | 
			
		||||
bool QSaveFile::atEnd() const
 | 
			
		||||
{
 | 
			
		||||
    Q_D(const QSaveFile);
 | 
			
		||||
    return d->tempFile ? d->tempFile->atEnd() : true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*!
 | 
			
		||||
    Flushes any buffered data to the file. Returns true if successful;
 | 
			
		||||
    otherwise returns false.
 | 
			
		||||
*/
 | 
			
		||||
bool QSaveFile::flush()
 | 
			
		||||
{
 | 
			
		||||
    Q_D(QSaveFile);
 | 
			
		||||
    if (d->tempFile) {
 | 
			
		||||
        if (!d->tempFile->flush()) {
 | 
			
		||||
            d->error = d->tempFile->error();
 | 
			
		||||
            setErrorString(d->tempFile->errorString());
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*!
 | 
			
		||||
  Returns the file handle of the temporary file.
 | 
			
		||||
 | 
			
		||||
  \sa QFile::handle()
 | 
			
		||||
*/
 | 
			
		||||
int QSaveFile::handle() const
 | 
			
		||||
{
 | 
			
		||||
    Q_D(const QSaveFile);
 | 
			
		||||
    return d->tempFile ? d->tempFile->handle() : -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*!
 | 
			
		||||
  \reimp
 | 
			
		||||
*/
 | 
			
		||||
qint64 QSaveFile::readData(char *data, qint64 maxlen)
 | 
			
		||||
{
 | 
			
		||||
    Q_D(QSaveFile);
 | 
			
		||||
    return d->tempFile ? d->tempFile->read(data, maxlen) : -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*!
 | 
			
		||||
  \reimp
 | 
			
		||||
*/
 | 
			
		||||
qint64 QSaveFile::writeData(const char *data, qint64 len)
 | 
			
		||||
{
 | 
			
		||||
    Q_D(QSaveFile);
 | 
			
		||||
    if (!d->tempFile)
 | 
			
		||||
        return -1;
 | 
			
		||||
    const qint64 written = d->tempFile->write(data, len);
 | 
			
		||||
    if (written != len) {
 | 
			
		||||
        d->error = QFile::WriteError;
 | 
			
		||||
        setErrorString(QSaveFile::tr("Partial write. Partition full?"));
 | 
			
		||||
    }
 | 
			
		||||
    return written;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*!
 | 
			
		||||
  \reimp
 | 
			
		||||
*/
 | 
			
		||||
qint64 QSaveFile::readLineData(char *data, qint64 maxlen)
 | 
			
		||||
{
 | 
			
		||||
    Q_D(QSaveFile);
 | 
			
		||||
    return d->tempFile ? d->tempFile->readLine(data, maxlen) : -1;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,105 +0,0 @@
 | 
			
		|||
/****************************************************************************
 | 
			
		||||
**
 | 
			
		||||
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
 | 
			
		||||
** Contact: http://www.qt-project.org/
 | 
			
		||||
**
 | 
			
		||||
** This file is part of the QtCore module of the Qt Toolkit.
 | 
			
		||||
**
 | 
			
		||||
** $QT_BEGIN_LICENSE:LGPL$
 | 
			
		||||
** GNU Lesser General Public License Usage
 | 
			
		||||
** This file may be used under the terms of the GNU Lesser General Public
 | 
			
		||||
** License version 2.1 as published by the Free Software Foundation and
 | 
			
		||||
** appearing in the file LICENSE.LGPL included in the packaging of this
 | 
			
		||||
** file. Please review the following information to ensure the GNU Lesser
 | 
			
		||||
** General Public License version 2.1 requirements will be met:
 | 
			
		||||
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
 | 
			
		||||
**
 | 
			
		||||
** In addition, as a special exception, Nokia gives you certain additional
 | 
			
		||||
** rights. These rights are described in the Nokia Qt LGPL Exception
 | 
			
		||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
 | 
			
		||||
**
 | 
			
		||||
** GNU General Public License Usage
 | 
			
		||||
** Alternatively, this file may be used under the terms of the GNU General
 | 
			
		||||
** Public License version 3.0 as published by the Free Software Foundation
 | 
			
		||||
** and appearing in the file LICENSE.GPL included in the packaging of this
 | 
			
		||||
** file. Please review the following information to ensure the GNU General
 | 
			
		||||
** Public License version 3.0 requirements will be met:
 | 
			
		||||
** http://www.gnu.org/copyleft/gpl.html.
 | 
			
		||||
**
 | 
			
		||||
** Other Usage
 | 
			
		||||
** Alternatively, this file may be used in accordance with the terms and
 | 
			
		||||
** conditions contained in a signed written agreement between you and Nokia.
 | 
			
		||||
**
 | 
			
		||||
**
 | 
			
		||||
**
 | 
			
		||||
**
 | 
			
		||||
**
 | 
			
		||||
**
 | 
			
		||||
** $QT_END_LICENSE$
 | 
			
		||||
**
 | 
			
		||||
****************************************************************************/
 | 
			
		||||
 | 
			
		||||
#ifndef QSAVEFILE_H
 | 
			
		||||
#define QSAVEFILE_H
 | 
			
		||||
 | 
			
		||||
#include <QFile>
 | 
			
		||||
#include <QString>
 | 
			
		||||
 | 
			
		||||
#ifdef open
 | 
			
		||||
#error qsavefile.h must be included before any header file that defines open
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
class QAbstractFileEngine;
 | 
			
		||||
class QSaveFilePrivate;
 | 
			
		||||
 | 
			
		||||
class QSaveFile : public QIODevice
 | 
			
		||||
{
 | 
			
		||||
    Q_OBJECT
 | 
			
		||||
    Q_DECLARE_PRIVATE(QSaveFile)
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
 | 
			
		||||
    QSaveFile();
 | 
			
		||||
    explicit QSaveFile(const QString &name);
 | 
			
		||||
    explicit QSaveFile(QObject *parent);
 | 
			
		||||
    QSaveFile(const QString &name, QObject *parent);
 | 
			
		||||
    ~QSaveFile();
 | 
			
		||||
 | 
			
		||||
    QFile::FileError error() const;
 | 
			
		||||
    void unsetError();
 | 
			
		||||
 | 
			
		||||
    QString fileName() const;
 | 
			
		||||
    void setFileName(const QString &name);
 | 
			
		||||
 | 
			
		||||
    bool isSequential() const;
 | 
			
		||||
 | 
			
		||||
    virtual bool open(OpenMode flags);
 | 
			
		||||
    bool commit();
 | 
			
		||||
 | 
			
		||||
    void cancelWriting();
 | 
			
		||||
 | 
			
		||||
    qint64 size() const;
 | 
			
		||||
    qint64 pos() const;
 | 
			
		||||
    bool seek(qint64 offset);
 | 
			
		||||
    bool atEnd() const;
 | 
			
		||||
    bool flush();
 | 
			
		||||
 | 
			
		||||
    bool resize(qint64 sz);
 | 
			
		||||
 | 
			
		||||
    int handle() const;
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
    qint64 readData(char *data, qint64 maxlen);
 | 
			
		||||
    qint64 writeData(const char *data, qint64 len);
 | 
			
		||||
    qint64 readLineData(char *data, qint64 maxlen);
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    virtual void close();
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    Q_DISABLE_COPY(QSaveFile)
 | 
			
		||||
 | 
			
		||||
    QSaveFilePrivate* const d_ptr;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif // QSAVEFILE_H
 | 
			
		||||
| 
						 | 
				
			
			@ -1,71 +0,0 @@
 | 
			
		|||
/****************************************************************************
 | 
			
		||||
**
 | 
			
		||||
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
 | 
			
		||||
** Contact: http://www.qt-project.org/
 | 
			
		||||
**
 | 
			
		||||
** This file is part of the QtCore module of the Qt Toolkit.
 | 
			
		||||
**
 | 
			
		||||
** $QT_BEGIN_LICENSE:LGPL$
 | 
			
		||||
** GNU Lesser General Public License Usage
 | 
			
		||||
** This file may be used under the terms of the GNU Lesser General Public
 | 
			
		||||
** License version 2.1 as published by the Free Software Foundation and
 | 
			
		||||
** appearing in the file LICENSE.LGPL included in the packaging of this
 | 
			
		||||
** file. Please review the following information to ensure the GNU Lesser
 | 
			
		||||
** General Public License version 2.1 requirements will be met:
 | 
			
		||||
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
 | 
			
		||||
**
 | 
			
		||||
** In addition, as a special exception, Nokia gives you certain additional
 | 
			
		||||
** rights. These rights are described in the Nokia Qt LGPL Exception
 | 
			
		||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
 | 
			
		||||
**
 | 
			
		||||
** GNU General Public License Usage
 | 
			
		||||
** Alternatively, this file may be used under the terms of the GNU General
 | 
			
		||||
** Public License version 3.0 as published by the Free Software Foundation
 | 
			
		||||
** and appearing in the file LICENSE.GPL included in the packaging of this
 | 
			
		||||
** file. Please review the following information to ensure the GNU General
 | 
			
		||||
** Public License version 3.0 requirements will be met:
 | 
			
		||||
** http://www.gnu.org/copyleft/gpl.html.
 | 
			
		||||
**
 | 
			
		||||
** Other Usage
 | 
			
		||||
** Alternatively, this file may be used in accordance with the terms and
 | 
			
		||||
** conditions contained in a signed written agreement between you and Nokia.
 | 
			
		||||
**
 | 
			
		||||
**
 | 
			
		||||
**
 | 
			
		||||
**
 | 
			
		||||
**
 | 
			
		||||
**
 | 
			
		||||
** $QT_END_LICENSE$
 | 
			
		||||
**
 | 
			
		||||
****************************************************************************/
 | 
			
		||||
 | 
			
		||||
#ifndef QSAVEFILE_P_H
 | 
			
		||||
#define QSAVEFILE_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 <QTemporaryFile>
 | 
			
		||||
 | 
			
		||||
class QSaveFilePrivate
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    QSaveFilePrivate();
 | 
			
		||||
    ~QSaveFilePrivate();
 | 
			
		||||
 | 
			
		||||
    QString fileName;
 | 
			
		||||
    QTemporaryFile *tempFile;
 | 
			
		||||
 | 
			
		||||
    QFile::FileError error;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif // QSAVEFILE_P_H
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -18,6 +18,8 @@
 | 
			
		|||
#include "DatabaseTabWidget.h"
 | 
			
		||||
 | 
			
		||||
#include <QFileInfo>
 | 
			
		||||
#include <QLockFile>
 | 
			
		||||
#include <QSaveFile>
 | 
			
		||||
#include <QTabWidget>
 | 
			
		||||
 | 
			
		||||
#include "autotype/AutoType.h"
 | 
			
		||||
| 
						 | 
				
			
			@ -25,7 +27,6 @@
 | 
			
		|||
#include "core/Database.h"
 | 
			
		||||
#include "core/Group.h"
 | 
			
		||||
#include "core/Metadata.h"
 | 
			
		||||
#include "core/qsavefile.h"
 | 
			
		||||
#include "format/CsvExporter.h"
 | 
			
		||||
#include "gui/DatabaseWidget.h"
 | 
			
		||||
#include "gui/DatabaseWidgetStateSync.h"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -21,7 +21,6 @@
 | 
			
		|||
#include <QHash>
 | 
			
		||||
#include <QTabWidget>
 | 
			
		||||
 | 
			
		||||
#include "core/qlockfile.h"
 | 
			
		||||
#include "format/KeePass2Writer.h"
 | 
			
		||||
#include "gui/DatabaseWidget.h"
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -29,6 +28,7 @@ class DatabaseWidget;
 | 
			
		|||
class DatabaseWidgetStateSync;
 | 
			
		||||
class DatabaseOpenWidget;
 | 
			
		||||
class QFile;
 | 
			
		||||
class QLockFile;
 | 
			
		||||
 | 
			
		||||
struct DatabaseManagerStruct
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -138,9 +138,6 @@ add_unit_test(NAME testdeletedobjects SOURCES TestDeletedObjects.cpp
 | 
			
		|||
add_unit_test(NAME testkeepass1reader SOURCES TestKeePass1Reader.cpp
 | 
			
		||||
              LIBS ${TEST_LIBRARIES})
 | 
			
		||||
 | 
			
		||||
add_unit_test(NAME testqsavefile SOURCES TestQSaveFile.cpp
 | 
			
		||||
              LIBS ${TEST_LIBRARIES})
 | 
			
		||||
 | 
			
		||||
add_unit_test(NAME testwildcardmatcher SOURCES TestWildcardMatcher.cpp
 | 
			
		||||
              LIBS ${TEST_LIBRARIES})
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,204 +0,0 @@
 | 
			
		|||
/*
 | 
			
		||||
 *  Copyright (C) 2012 Felix Geyer <debfx@fobos.de>
 | 
			
		||||
 *  Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies)
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software: you can redistribute it and/or modify
 | 
			
		||||
 *  it under the terms of the GNU General Public License as published by
 | 
			
		||||
 *  the Free Software Foundation, either version 2 or (at your option)
 | 
			
		||||
 *  version 3 of the License.
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is distributed in the hope that it will be useful,
 | 
			
		||||
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 *  GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 *  You should have received a copy of the GNU General Public License
 | 
			
		||||
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "TestQSaveFile.h"
 | 
			
		||||
 | 
			
		||||
#include <QtTest>
 | 
			
		||||
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
 | 
			
		||||
#if defined(Q_OS_WIN)
 | 
			
		||||
# include <windows.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include "tests.h"
 | 
			
		||||
#include "core/qsavefile.h"
 | 
			
		||||
 | 
			
		||||
QTEST_GUILESS_MAIN(TestQSaveFile)
 | 
			
		||||
 | 
			
		||||
class DirCleanup
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    DirCleanup(const QString& dir, const QString& filePrefix) : m_dir(dir), m_filePrefix(filePrefix) {}
 | 
			
		||||
    ~DirCleanup() {
 | 
			
		||||
        QDir dir(m_dir);
 | 
			
		||||
        QStringList files = dir.entryList(QStringList() << (m_filePrefix + "*"), QDir::Files);
 | 
			
		||||
        Q_FOREACH (const QString& file, files) {
 | 
			
		||||
            QFile::remove(m_dir + "/" + file);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        QDir().rmdir(m_dir);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    QString m_dir;
 | 
			
		||||
    QString m_filePrefix;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void TestQSaveFile::transactionalWrite()
 | 
			
		||||
{
 | 
			
		||||
    const QString dir = tmpDir();
 | 
			
		||||
    QVERIFY(!dir.isEmpty());
 | 
			
		||||
    const QString targetFile = dir + QString::fromLatin1("/outfile");
 | 
			
		||||
    DirCleanup dirCleanup(dir, "outfile");
 | 
			
		||||
    QFile::remove(targetFile);
 | 
			
		||||
    QSaveFile file(targetFile);
 | 
			
		||||
    QVERIFY(file.open(QIODevice::WriteOnly));
 | 
			
		||||
    QVERIFY(file.isOpen());
 | 
			
		||||
    QCOMPARE(file.fileName(), targetFile);
 | 
			
		||||
    QVERIFY(!QFile::exists(targetFile));
 | 
			
		||||
 | 
			
		||||
    QTextStream ts(&file);
 | 
			
		||||
    ts << "This is test data one.\n";
 | 
			
		||||
    ts.flush();
 | 
			
		||||
    QCOMPARE(file.error(), QFile::NoError);
 | 
			
		||||
    QVERIFY(!QFile::exists(targetFile));
 | 
			
		||||
 | 
			
		||||
    QVERIFY(file.commit());
 | 
			
		||||
    QVERIFY(QFile::exists(targetFile));
 | 
			
		||||
    QCOMPARE(file.fileName(), targetFile);
 | 
			
		||||
 | 
			
		||||
    // Check that we can reuse a QSaveFile object
 | 
			
		||||
    // (and test the case of an existing target file)
 | 
			
		||||
    QVERIFY(file.open(QIODevice::WriteOnly));
 | 
			
		||||
    QCOMPARE(file.write("Hello"), 5LL);
 | 
			
		||||
    QVERIFY(file.commit());
 | 
			
		||||
 | 
			
		||||
    QFile reader(targetFile);
 | 
			
		||||
    QVERIFY(reader.open(QIODevice::ReadOnly));
 | 
			
		||||
    QCOMPARE(QString::fromLatin1(reader.readAll().constData()), QString::fromLatin1("Hello"));
 | 
			
		||||
    reader.close();
 | 
			
		||||
 | 
			
		||||
    QVERIFY(QFile::remove(targetFile));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TestQSaveFile::autoFlush()
 | 
			
		||||
{
 | 
			
		||||
    const QString dir = tmpDir();
 | 
			
		||||
    QVERIFY(!dir.isEmpty());
 | 
			
		||||
    const QString targetFile = dir + QString::fromLatin1("/outfile");
 | 
			
		||||
    DirCleanup dirCleanup(dir, "outfile");
 | 
			
		||||
    QFile::remove(targetFile);
 | 
			
		||||
    QSaveFile file(targetFile);
 | 
			
		||||
    QVERIFY(file.open(QIODevice::WriteOnly));
 | 
			
		||||
 | 
			
		||||
    QTextStream ts(&file);
 | 
			
		||||
    ts << "Auto-flush.";
 | 
			
		||||
    // no flush
 | 
			
		||||
    QVERIFY(file.commit()); // close will emit aboutToClose, which will flush the stream
 | 
			
		||||
    QFile reader(targetFile);
 | 
			
		||||
    QVERIFY(reader.open(QIODevice::ReadOnly));
 | 
			
		||||
    QCOMPARE(QString::fromLatin1(reader.readAll().constData()), QString::fromLatin1("Auto-flush."));
 | 
			
		||||
    reader.close();
 | 
			
		||||
 | 
			
		||||
    QVERIFY(QFile::remove(targetFile));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TestQSaveFile::transactionalWriteNoPermissions()
 | 
			
		||||
{
 | 
			
		||||
#ifdef Q_OS_UNIX
 | 
			
		||||
    if (::geteuid() == 0) {
 | 
			
		||||
        QSKIP("not valid running this test as root", SkipAll);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // You can write into /dev/zero, but you can't create a /dev/zero.XXXXXX temp file.
 | 
			
		||||
    QSaveFile file("/dev/zero");
 | 
			
		||||
    if (!QDir("/dev").exists()) {
 | 
			
		||||
        QSKIP("/dev doesn't exist on this system", SkipAll);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    QVERIFY(!file.open(QIODevice::WriteOnly));
 | 
			
		||||
    QCOMPARE(static_cast<int>(file.error()), static_cast<int>(QFile::OpenError));
 | 
			
		||||
    QVERIFY(!file.commit());
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TestQSaveFile::transactionalWriteCanceled()
 | 
			
		||||
{
 | 
			
		||||
    const QString dir = tmpDir();
 | 
			
		||||
    QVERIFY(!dir.isEmpty());
 | 
			
		||||
    const QString targetFile = dir + QString::fromLatin1("/outfile");
 | 
			
		||||
    DirCleanup dirCleanup(dir, "outfile");
 | 
			
		||||
    QFile::remove(targetFile);
 | 
			
		||||
    QSaveFile file(targetFile);
 | 
			
		||||
    QVERIFY(file.open(QIODevice::WriteOnly));
 | 
			
		||||
 | 
			
		||||
    QTextStream ts(&file);
 | 
			
		||||
    ts << "This writing operation will soon be canceled.\n";
 | 
			
		||||
    ts.flush();
 | 
			
		||||
    QCOMPARE(file.error(), QFile::NoError);
 | 
			
		||||
    QVERIFY(!QFile::exists(targetFile));
 | 
			
		||||
 | 
			
		||||
    // We change our mind, let's abort writing
 | 
			
		||||
    file.cancelWriting();
 | 
			
		||||
 | 
			
		||||
    QVERIFY(!file.commit());
 | 
			
		||||
 | 
			
		||||
    QVERIFY(!QFile::exists(targetFile)); // temp file was discarded
 | 
			
		||||
    QCOMPARE(file.fileName(), targetFile);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TestQSaveFile::transactionalWriteErrorRenaming()
 | 
			
		||||
{
 | 
			
		||||
#ifndef Q_OS_WIN
 | 
			
		||||
    if (::geteuid() == 0) {
 | 
			
		||||
        QSKIP("not valid running this test as root", SkipAll);
 | 
			
		||||
    }
 | 
			
		||||
    const QString dir = tmpDir();
 | 
			
		||||
    QVERIFY(!dir.isEmpty());
 | 
			
		||||
    const QString targetFile = dir + QString::fromLatin1("/outfile");
 | 
			
		||||
    DirCleanup dirCleanup(dir, "outfile");
 | 
			
		||||
    QSaveFile file(targetFile);
 | 
			
		||||
    QVERIFY(file.open(QIODevice::WriteOnly));
 | 
			
		||||
    QCOMPARE(file.write("Hello"), qint64(5));
 | 
			
		||||
    QVERIFY(!QFile::exists(targetFile));
 | 
			
		||||
 | 
			
		||||
#ifdef Q_OS_UNIX
 | 
			
		||||
    QFile dirAsFile(dir); // yay, I have to use QFile to change a dir's permissions...
 | 
			
		||||
    QVERIFY(dirAsFile.setPermissions(QFile::Permissions(0))); // no permissions
 | 
			
		||||
#else
 | 
			
		||||
    QVERIFY(file.setPermissions(QFile::ReadOwner));
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    QVERIFY(!file.commit());
 | 
			
		||||
    QVERIFY(!QFile::exists(targetFile)); // renaming failed
 | 
			
		||||
    QCOMPARE(file.error(), QFile::RenameError);
 | 
			
		||||
 | 
			
		||||
    // Restore permissions so that the cleanup can happen
 | 
			
		||||
#ifdef Q_OS_UNIX
 | 
			
		||||
    QVERIFY(dirAsFile.setPermissions(QFile::Permissions(QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner)));
 | 
			
		||||
#else
 | 
			
		||||
    QVERIFY(file.setPermissions(QFile::ReadOwner | QFile::WriteOwner));
 | 
			
		||||
#endif
 | 
			
		||||
#endif // !Q_OS_WIN
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
QString TestQSaveFile::tmpDir()
 | 
			
		||||
{
 | 
			
		||||
    QTemporaryFile* tmpFile = new QTemporaryFile(QDir::tempPath() + "/qttest_temp.XXXXXX");
 | 
			
		||||
    if (!tmpFile->open()) {
 | 
			
		||||
        return QString();
 | 
			
		||||
    }
 | 
			
		||||
    QString dirName = tmpFile->fileName();
 | 
			
		||||
    delete tmpFile;
 | 
			
		||||
    if (!QDir().mkdir(dirName)) {
 | 
			
		||||
        return QString();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return dirName;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,39 +0,0 @@
 | 
			
		|||
/*
 | 
			
		||||
 *  Copyright (C) 2012 Felix Geyer <debfx@fobos.de>
 | 
			
		||||
 *  Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies)
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software: you can redistribute it and/or modify
 | 
			
		||||
 *  it under the terms of the GNU General Public License as published by
 | 
			
		||||
 *  the Free Software Foundation, either version 2 or (at your option)
 | 
			
		||||
 *  version 3 of the License.
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is distributed in the hope that it will be useful,
 | 
			
		||||
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 *  GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 *  You should have received a copy of the GNU General Public License
 | 
			
		||||
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef KEEPASSX_TESTQSAVEFILE_H
 | 
			
		||||
#define KEEPASSX_TESTQSAVEFILE_H
 | 
			
		||||
 | 
			
		||||
#include <QObject>
 | 
			
		||||
 | 
			
		||||
class TestQSaveFile : public QObject
 | 
			
		||||
{
 | 
			
		||||
    Q_OBJECT
 | 
			
		||||
 | 
			
		||||
private Q_SLOTS:
 | 
			
		||||
    void transactionalWrite();
 | 
			
		||||
    void autoFlush();
 | 
			
		||||
    void transactionalWriteNoPermissions();
 | 
			
		||||
    void transactionalWriteCanceled();
 | 
			
		||||
    void transactionalWriteErrorRenaming();
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    QString tmpDir();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif // KEEPASSX_TESTQSAVEFILE_H
 | 
			
		||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue