diff options
Diffstat (limited to 'src/corelib/io')
-rw-r--r-- | src/corelib/io/qfilesystemengine.cpp | 2 | ||||
-rw-r--r-- | src/corelib/io/qfilesystemengine_unix.cpp | 28 | ||||
-rw-r--r-- | src/corelib/io/qlockfile.cpp | 71 | ||||
-rw-r--r-- | src/corelib/io/qlockfile_p.h | 8 | ||||
-rw-r--r-- | src/corelib/io/qlockfile_unix.cpp | 155 | ||||
-rw-r--r-- | src/corelib/io/qlockfile_win.cpp | 60 |
6 files changed, 153 insertions, 171 deletions
diff --git a/src/corelib/io/qfilesystemengine.cpp b/src/corelib/io/qfilesystemengine.cpp index 73a2e37a38..7abdf90bf5 100644 --- a/src/corelib/io/qfilesystemengine.cpp +++ b/src/corelib/io/qfilesystemengine.cpp @@ -86,8 +86,6 @@ QString QFileSystemEngine::slowCanonicalized(const QString &path) fi.setFile(prefix); if (fi.isSymLink()) { QString target = fi.symLinkTarget(); - if(QFileInfo(target).isRelative()) - target = fi.absolutePath() + slash + target; if (separatorPos != -1) { if (fi.isDir() && !target.endsWith(slash)) target.append(slash); diff --git a/src/corelib/io/qfilesystemengine_unix.cpp b/src/corelib/io/qfilesystemengine_unix.cpp index 9fb3855472..52512c5e13 100644 --- a/src/corelib/io/qfilesystemengine_unix.cpp +++ b/src/corelib/io/qfilesystemengine_unix.cpp @@ -412,8 +412,11 @@ bool QFileSystemEngine::fillMetaData(int fd, QFileSystemMetaData &data) int ret = qt_fstatx(fd, &statxBuffer); if (ret != -ENOSYS) { - data.fillFromStatxBuf(statxBuffer); - return ret == 0; + if (ret == 0) { + data.fillFromStatxBuf(statxBuffer); + return true; + } + return false; } if (QT_FSTAT(fd, &statBuffer) == 0) { @@ -635,13 +638,8 @@ QFileSystemEntry QFileSystemEngine::getLinkTarget(const QFileSystemEntry &link, } ret += QFile::decodeName(s); - if (!ret.startsWith(QLatin1Char('/'))) { - const QString linkPath = link.path(); - if (linkPath.startsWith(QLatin1Char('/'))) - ret.prepend(linkPath + QLatin1Char('/')); - else - ret.prepend(QDir::currentPath() + QLatin1Char('/') + linkPath + QLatin1Char('/')); - } + if (!ret.startsWith(QLatin1Char('/'))) + ret.prepend(absoluteName(link).path() + QLatin1Char('/')); ret = QDir::cleanPath(ret); if (ret.size() > 1 && ret.endsWith(QLatin1Char('/'))) ret.chop(1); @@ -1234,11 +1232,7 @@ bool QFileSystemEngine::createLink(const QFileSystemEntry &source, const QFileSy bool QFileSystemEngine::copyFile(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error) { #if QT_DARWIN_PLATFORM_SDK_EQUAL_OR_ABOVE(101200, 100000, 100000, 30000) - const auto current = QOperatingSystemVersion::current(); - if (current >= QOperatingSystemVersion::MacOSSierra || - current >= QOperatingSystemVersion(QOperatingSystemVersion::IOS, 10) || - current >= QOperatingSystemVersion(QOperatingSystemVersion::TvOS, 10) || - current >= QOperatingSystemVersion(QOperatingSystemVersion::WatchOS, 3)) { + if (__builtin_available(macOS 10.12, iOS 10, tvOS 10, watchOS 3, *)) { if (::clonefile(source.nativeFilePath().constData(), target.nativeFilePath().constData(), 0) == 0) return true; @@ -1273,11 +1267,7 @@ bool QFileSystemEngine::renameFile(const QFileSystemEntry &source, const QFileSy } #endif #if defined(Q_OS_DARWIN) && defined(RENAME_EXCL) - const auto current = QOperatingSystemVersion::current(); - if (current >= QOperatingSystemVersion::MacOSSierra || - current >= QOperatingSystemVersion(QOperatingSystemVersion::IOS, 10) || - current >= QOperatingSystemVersion(QOperatingSystemVersion::TvOS, 10) || - current >= QOperatingSystemVersion(QOperatingSystemVersion::WatchOS, 3)) { + if (__builtin_available(macOS 10.12, iOS 10, tvOS 10, watchOS 3, *)) { if (renameatx_np(AT_FDCWD, srcPath, AT_FDCWD, tgtPath, RENAME_EXCL) == 0) return true; if (errno != ENOTSUP) { diff --git a/src/corelib/io/qlockfile.cpp b/src/corelib/io/qlockfile.cpp index 48317d07e0..129cf01b63 100644 --- a/src/corelib/io/qlockfile.cpp +++ b/src/corelib/io/qlockfile.cpp @@ -2,6 +2,7 @@ ** ** Copyright (C) 2013 David Faure <faure+bluesystems@kde.org> ** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2017 Intel Corporation. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtCore module of the Qt Toolkit. @@ -42,12 +43,34 @@ #include "qlockfile_p.h" #include <QtCore/qthread.h> +#include <QtCore/qcoreapplication.h> #include <QtCore/qdeadlinetimer.h> #include <QtCore/qdatetime.h> #include <QtCore/qfileinfo.h> QT_BEGIN_NAMESPACE +namespace { +struct LockFileInfo +{ + qint64 pid; + QString appname; + QString hostname; +}; +} + +static bool getLockInfo_helper(const QString &fileName, LockFileInfo *info); + +static QString machineName() +{ +#ifdef Q_OS_WIN + // we don't use QSysInfo because it tries to do name resolution + return qEnvironmentVariable("COMPUTERNAME"); +#else + return QSysInfo::machineHostName(); +#endif +} + /*! \class QLockFile \inmodule QtCore @@ -291,10 +314,27 @@ bool QLockFile::tryLock(int timeout) bool QLockFile::getLockInfo(qint64 *pid, QString *hostname, QString *appname) const { Q_D(const QLockFile); - return d->getLockInfo(pid, hostname, appname); + LockFileInfo info; + if (!getLockInfo_helper(d->fileName, &info)) + return false; + if (pid) + *pid = info.pid; + if (hostname) + *hostname = info.hostname; + if (appname) + *appname = info.appname; + return true; } -bool QLockFilePrivate::getLockInfo(qint64 *pid, QString *hostname, QString *appname) const +QByteArray QLockFilePrivate::lockFileContents() const +{ + // Use operator% from the fast builder to avoid multiple memory allocations. + return QByteArray::number(QCoreApplication::applicationPid()) % '\n' + % processNameByPid(QCoreApplication::applicationPid()).toUtf8() % '\n' + % machineName().toUtf8() % '\n'; +} + +static bool getLockInfo_helper(const QString &fileName, LockFileInfo *info) { QFile reader(fileName); if (!reader.open(QIODevice::ReadOnly)) @@ -309,14 +349,25 @@ bool QLockFilePrivate::getLockInfo(qint64 *pid, QString *hostname, QString *appn QByteArray hostNameLine = reader.readLine(); hostNameLine.chop(1); - qint64 thePid = pidLine.toLongLong(); - if (pid) - *pid = thePid; - if (appname) - *appname = QString::fromUtf8(appNameLine); - if (hostname) - *hostname = QString::fromUtf8(hostNameLine); - return thePid > 0; + bool ok; + info->appname = QString::fromUtf8(appNameLine); + info->hostname = QString::fromUtf8(hostNameLine); + info->pid = pidLine.toLongLong(&ok); + return ok && info->pid > 0; +} + +bool QLockFilePrivate::isApparentlyStale() const +{ + LockFileInfo info; + if (getLockInfo_helper(fileName, &info)) { + if (info.hostname.isEmpty() || info.hostname == machineName()) { + if (!isProcessRunning(info.pid, info.appname)) + return true; + } + } + + const qint64 age = QFileInfo(fileName).lastModified().msecsTo(QDateTime::currentDateTimeUtc()); + return staleLockTime > 0 && qAbs(age) > staleLockTime; } /*! diff --git a/src/corelib/io/qlockfile_p.h b/src/corelib/io/qlockfile_p.h index 86a606ec00..b41dfb38ad 100644 --- a/src/corelib/io/qlockfile_p.h +++ b/src/corelib/io/qlockfile_p.h @@ -78,16 +78,14 @@ public: } QLockFile::LockError tryLock_sys(); bool removeStaleLock(); - bool getLockInfo(qint64 *pid, QString *hostname, QString *appname) const; + QByteArray lockFileContents() 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; + // used in dbusmenu Q_CORE_EXPORT static QString processNameByPid(qint64 pid); - -#ifdef Q_OS_UNIX - static int checkFcntlWorksAfterFlock(const QString &fn); -#endif + static bool isProcessRunning(qint64 pid, const QString &appname); QString fileName; #ifdef Q_OS_WIN diff --git a/src/corelib/io/qlockfile_unix.cpp b/src/corelib/io/qlockfile_unix.cpp index 1ee8ce889c..fc01f83e80 100644 --- a/src/corelib/io/qlockfile_unix.cpp +++ b/src/corelib/io/qlockfile_unix.cpp @@ -1,7 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2013 David Faure <faure+bluesystems@kde.org> -** Copyright (C) 2016 Intel Corporation. +** Copyright (C) 2017 Intel Corporation. ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** @@ -42,7 +42,6 @@ #include "private/qlockfile_p.h" #include "QtCore/qtemporaryfile.h" -#include "QtCore/qcoreapplication.h" #include "QtCore/qfileinfo.h" #include "QtCore/qdebug.h" #include "QtCore/qdatetime.h" @@ -94,91 +93,59 @@ static qint64 qt_write_loop(int fd, const char *data, qint64 len) return pos; } -int QLockFilePrivate::checkFcntlWorksAfterFlock(const QString &fn) -{ -#ifndef QT_NO_TEMPORARYFILE - QTemporaryFile file(fn); - if (!file.open()) - return 0; - const int fd = file.d_func()->engine()->handle(); -#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 0; -#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 0; - return 1; -#else - Q_UNUSED(fn); - return 0; -#endif -} - -// Cache the result of checkFcntlWorksAfterFlock for each directory a lock -// file is created in because in some filesystems, like NFS, both locks -// are the same. This does not take into account a filesystem changing. -// QCache is set to hold a maximum of 10 entries, this is to avoid unbounded -// growth, this is caching directories of files and it is assumed a low number -// will be sufficient. -typedef QCache<QString, bool> CacheType; -Q_GLOBAL_STATIC_WITH_ARGS(CacheType, fcntlOK, (10)); -static QBasicMutex fcntlLock; +/* + * Details about file locking on Unix. + * + * There are three types of advisory locks on Unix systems: + * 1) POSIX process-wide locks using fcntl(F_SETLK) + * 2) BSD flock(2) system call + * 3) Linux-specific file descriptor locks using fcntl(F_OFD_SETLK) + * There's also a mandatory locking feature by POSIX, which is deprecated on + * Linux and users are advised not to use it. + * + * The first problem is that the POSIX API is braindead. POSIX.1-2008 says: + * + * All locks associated with a file for a given process shall be removed when + * a file descriptor for that file is closed by that process or the process + * holding that file descriptor terminates. + * + * The Linux manpage is clearer: + * + * * If a process closes _any_ file descriptor referring to a file, then all + * of the process's locks on that file are released, regardless of the file + * descriptor(s) on which the locks were obtained. This is bad: [...] + * + * * The threads in a process share locks. In other words, a multithreaded + * program can't use record locking to ensure that threads don't + * simultaneously access the same region of a file. + * + * So in order to use POSIX locks, we'd need a global mutex that stays locked + * while the QLockFile is locked. For that reason, Qt does not use POSIX + * advisory locks anymore. + * + * The next problem is that POSIX leaves undefined the relationship between + * locks with fcntl(), flock() and lockf(). In some systems (like the BSDs), + * all three use the same record set, while on others (like Linux) the locks + * are independent, except if locking over NFS mounts, in which case they're + * actually the same. Therefore, it's a very bad idea to mix them in the same + * process. + * + * We therefore use only flock(2). + */ -/*! - \internal - Checks that the OS isn't using POSIX locks to emulate flock(). - \macos is one of those. -*/ -static bool fcntlWorksAfterFlock(const QString &fn) -{ - QMutexLocker lock(&fcntlLock); - if (fcntlOK.isDestroyed()) - return QLockFilePrivate::checkFcntlWorksAfterFlock(fn); - bool *worksPtr = fcntlOK->object(fn); - if (worksPtr) - return *worksPtr; - - const bool val = QLockFilePrivate::checkFcntlWorksAfterFlock(fn); - worksPtr = new bool(val); - fcntlOK->insert(fn, worksPtr); - - return val; -} - -static bool setNativeLocks(const QString &fileName, int fd) +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; +#else + Q_UNUSED(fd); #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 (fcntlWorksAfterFlock(QDir::cleanPath(QFileInfo(fileName).absolutePath()) + QString('/')) - && 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' - % QSysInfo::machineHostName().toUtf8() % '\n'; - const QByteArray lockFileName = QFile::encodeName(fileName); const int fd = qt_safe_open(lockFileName.constData(), O_WRONLY | O_CREAT | O_EXCL, 0666); if (fd < 0) { @@ -193,11 +160,12 @@ QLockFile::LockError QLockFilePrivate::tryLock_sys() } } // Ensure nobody else can delete the file while we have it - if (!setNativeLocks(fileName, fd)) { + if (!setNativeLocks(fd)) { const int errnoSaved = errno; qWarning() << "setNativeLocks failed:" << qt_error_string(errnoSaved); } + QByteArray fileData = lockFileContents(); if (qt_write_loop(fd, fileData.constData(), fileData.size()) < fileData.size()) { qt_safe_close(fd); if (!QFile::remove(fileName)) @@ -224,31 +192,26 @@ bool QLockFilePrivate::removeStaleLock() const int fd = qt_safe_open(lockFileName.constData(), O_WRONLY, 0666); if (fd < 0) // gone already? return false; - bool success = setNativeLocks(fileName, fd) && (::unlink(lockFileName) == 0); + bool success = setNativeLocks(fd) && (::unlink(lockFileName) == 0); close(fd); return success; } -bool QLockFilePrivate::isApparentlyStale() const +bool QLockFilePrivate::isProcessRunning(qint64 pid, const QString &appname) { - qint64 pid; - QString hostname, appname; - if (getLockInfo(&pid, &hostname, &appname)) { - if (hostname.isEmpty() || hostname == QSysInfo::machineHostName()) { - 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 != fi.fileName()) - return true; // PID got reused by a different application. - } - } + if (::kill(pid, 0) == -1 && errno == ESRCH) + return false; // 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 != fi.fileName()) + return false; // PID got reused by a different application. } - const qint64 age = QFileInfo(fileName).lastModified().msecsTo(QDateTime::currentDateTime()); - return staleLockTime > 0 && qAbs(age) > staleLockTime; + + return true; } QString QLockFilePrivate::processNameByPid(qint64 pid) diff --git a/src/corelib/io/qlockfile_win.cpp b/src/corelib/io/qlockfile_win.cpp index 4b43181686..de64ec0432 100644 --- a/src/corelib/io/qlockfile_win.cpp +++ b/src/corelib/io/qlockfile_win.cpp @@ -2,6 +2,7 @@ ** ** Copyright (C) 2013 David Faure <faure+bluesystems@kde.org> ** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2017 Intel Corporation. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtCore module of the Qt Toolkit. @@ -42,7 +43,6 @@ #include "private/qfilesystementry_p.h" #include <qt_windows.h> -#include "QtCore/qcoreapplication.h" #include "QtCore/qfileinfo.h" #include "QtCore/qdatetime.h" #include "QtCore/qdebug.h" @@ -50,11 +50,6 @@ QT_BEGIN_NAMESPACE -static inline QByteArray localHostName() -{ - return qgetenv("COMPUTERNAME"); -} - static inline bool fileExists(const wchar_t *fileName) { WIN32_FILE_ATTRIBUTE_DATA data; @@ -107,15 +102,7 @@ QLockFile::LockError QLockFilePrivate::tryLock_sys() // 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'; + QByteArray fileData = lockFileContents(); DWORD bytesWritten = 0; QLockFile::LockError error = QLockFile::NoError; if (!WriteFile(fh, fileData.constData(), fileData.size(), &bytesWritten, NULL) || !FlushFileBuffers(fh)) @@ -129,38 +116,33 @@ bool QLockFilePrivate::removeStaleLock() return QFile::remove(fileName); } -bool QLockFilePrivate::isApparentlyStale() const +bool QLockFilePrivate::isProcessRunning(qint64 pid, const QString &appname) { - 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 exitCode = 0; - if (!::GetExitCodeProcess(procHandle, &exitCode)) - exitCode = 0; - ::CloseHandle(procHandle); - if (exitCode != STILL_ACTIVE) - return true; - const QString processName = processNameByPid(pid); - if (!processName.isEmpty() && processName != appname) - return true; // PID got reused by a different application. - } - } + HANDLE procHandle = ::OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid); + if (!procHandle) + return false; + + // We got a handle but check if process is still alive + DWORD exitCode = 0; + if (!::GetExitCodeProcess(procHandle, &exitCode)) + exitCode = 0; + ::CloseHandle(procHandle); + if (exitCode != STILL_ACTIVE) + return false; + + const QString processName = processNameByPid(pid); + if (!processName.isEmpty() && processName != appname) + return false; // 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 && qAbs(age) > staleLockTime; + + return true; } QString QLockFilePrivate::processNameByPid(qint64 pid) |