diff options
Diffstat (limited to 'src/corelib/io/qsettings.cpp')
-rw-r--r-- | src/corelib/io/qsettings.cpp | 297 |
1 files changed, 62 insertions, 235 deletions
diff --git a/src/corelib/io/qsettings.cpp b/src/corelib/io/qsettings.cpp index 1748170324..663ad3cc1b 100644 --- a/src/corelib/io/qsettings.cpp +++ b/src/corelib/io/qsettings.cpp @@ -53,6 +53,7 @@ #include "qmutex.h" #include "qlibraryinfo.h" #include "qtemporaryfile.h" +#include "qstandardpaths.h" #ifndef QT_NO_TEXTCODEC # include "qtextcodec.h" @@ -68,6 +69,11 @@ #include "qcoreapplication.h" #endif +#ifndef QT_BOOTSTRAPPED +#include "qsavefile.h" +#include "qlockfile.h" +#endif + #ifdef Q_OS_VXWORKS # include <ioLib.h> #endif @@ -101,12 +107,6 @@ using namespace ABI::Windows::Storage; #define CSIDL_APPDATA 0x001a // <username>\Application Data #endif -#ifdef Q_AUTOTEST_EXPORT -# define Q_AUTOTEST_EXPORT_HELPER Q_AUTOTEST_EXPORT -#else -# define Q_AUTOTEST_EXPORT_HELPER static -#endif - // ************************************************************************ // QConfFile @@ -141,101 +141,6 @@ static QBasicMutex settingsGlobalMutex; static QSettings::Format globalDefaultFormat = QSettings::NativeFormat; -#ifndef Q_OS_WIN -inline bool qt_isEvilFsTypeName(const char *name) -{ - return (qstrncmp(name, "nfs", 3) == 0 - || qstrncmp(name, "autofs", 6) == 0 - || qstrncmp(name, "cachefs", 7) == 0); -} - -#if defined(Q_OS_BSD4) && !defined(Q_OS_NETBSD) -QT_BEGIN_INCLUDE_NAMESPACE -# include <sys/param.h> -# include <sys/mount.h> -QT_END_INCLUDE_NAMESPACE - -Q_AUTOTEST_EXPORT_HELPER bool qIsLikelyToBeNfs(int handle) -{ - struct statfs buf; - if (fstatfs(handle, &buf) != 0) - return false; - return qt_isEvilFsTypeName(buf.f_fstypename); -} - -#elif defined(Q_OS_LINUX) || defined(Q_OS_HURD) -QT_BEGIN_INCLUDE_NAMESPACE -# include <sys/vfs.h> -# ifdef QT_LINUXBASE - // LSB 3.2 has fstatfs in sys/statfs.h, sys/vfs.h is just an empty dummy header -# include <sys/statfs.h> -# endif -QT_END_INCLUDE_NAMESPACE -# ifndef NFS_SUPER_MAGIC -# define NFS_SUPER_MAGIC 0x00006969 -# endif -# ifndef AUTOFS_SUPER_MAGIC -# define AUTOFS_SUPER_MAGIC 0x00000187 -# endif -# ifndef AUTOFSNG_SUPER_MAGIC -# define AUTOFSNG_SUPER_MAGIC 0x7d92b1a0 -# endif - -Q_AUTOTEST_EXPORT_HELPER bool qIsLikelyToBeNfs(int handle) -{ - struct statfs buf; - if (fstatfs(handle, &buf) != 0) - return false; - return buf.f_type == NFS_SUPER_MAGIC - || buf.f_type == AUTOFS_SUPER_MAGIC - || buf.f_type == AUTOFSNG_SUPER_MAGIC; -} - -#elif defined(Q_OS_SOLARIS) || defined(Q_OS_IRIX) || defined(Q_OS_AIX) || defined(Q_OS_HPUX) \ - || defined(Q_OS_OSF) || defined(Q_OS_QNX) || defined(Q_OS_SCO) \ - || defined(Q_OS_UNIXWARE) || defined(Q_OS_RELIANT) || defined(Q_OS_NETBSD) -QT_BEGIN_INCLUDE_NAMESPACE -# include <sys/statvfs.h> -QT_END_INCLUDE_NAMESPACE - -Q_AUTOTEST_EXPORT_HELPER bool qIsLikelyToBeNfs(int handle) -{ - struct statvfs buf; - if (fstatvfs(handle, &buf) != 0) - return false; -#if defined(Q_OS_NETBSD) - return qt_isEvilFsTypeName(buf.f_fstypename); -#else - return qt_isEvilFsTypeName(buf.f_basetype); -#endif -} -#else -Q_AUTOTEST_EXPORT_HELPER inline bool qIsLikelyToBeNfs(int /* handle */) -{ - return true; -} -#endif - -static bool unixLock(int handle, int lockType) -{ - /* - NFS hangs on the fcntl() call below when statd or lockd isn't - running. There's no way to detect this. Our work-around for - now is to disable locking when we detect NFS (or AutoFS or - CacheFS, which are probably wrapping NFS). - */ - if (qIsLikelyToBeNfs(handle)) - return false; - - struct flock fl; - fl.l_whence = SEEK_SET; - fl.l_start = 0; - fl.l_len = 0; - fl.l_type = lockType; - return fcntl(handle, F_SETLKW, &fl) == 0; -} -#endif - QConfFile::QConfFile(const QString &fileName, bool _userPerms) : name(fileName), size(0), ref(1), userPerms(_userPerms) { @@ -1140,6 +1045,8 @@ static void initDefaultPaths(QMutexLocker *locker) pathHash->insert(pathHashKey(QSettings::IniFormat, QSettings::SystemScope), windowsConfigPath(CSIDL_COMMON_APPDATA) + QDir::separator()); #else + +#ifdef QT_NO_STANDARDPATHS QString userPath; char *env = getenv("XDG_CONFIG_HOME"); if (env == 0) { @@ -1153,6 +1060,9 @@ static void initDefaultPaths(QMutexLocker *locker) userPath += QLatin1Char('/'); userPath += QFile::decodeName(env); } +#else + QString userPath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation); +#endif userPath += QLatin1Char('/'); pathHash->insert(pathHashKey(QSettings::IniFormat, QSettings::UserScope), userPath); @@ -1457,105 +1367,30 @@ void QConfFileSettingsPrivate::syncConfFile(int confFileNo) return; } +#ifndef QT_BOOTSTRAPPED /* - Open the configuration file and try to use it using a named - semaphore on Windows and an advisory lock on Unix-based - systems. This protect us against other QSettings instances - trying to access the same file from other threads or - processes. - - As it stands now, the locking mechanism doesn't work for - .plist files. - */ - QFile file(confFile->name); - bool createFile = !file.exists(); - if (!readOnly && confFile->isWritable()) - file.open(QFile::ReadWrite); - if (!file.isOpen()) - file.open(QFile::ReadOnly); - - if (!createFile && !file.isOpen()) - setStatus(QSettings::AccessError); - -#ifdef Q_OS_WIN - HANDLE readSemaphore = 0; - HANDLE writeSemaphore = 0; - static const int FileLockSemMax = 50; - int numReadLocks = readOnly ? 1 : FileLockSemMax; - - if (file.isOpen()) { - // Acquire the write lock if we will be writing - if (!readOnly) { - QString writeSemName = QLatin1String("QSettingsWriteSem "); - writeSemName.append(file.fileName()); - -#ifndef Q_OS_WINRT - writeSemaphore = CreateSemaphore(0, 1, 1, reinterpret_cast<const wchar_t *>(writeSemName.utf16())); -#else - writeSemaphore = CreateSemaphoreEx(0, 1, 1, reinterpret_cast<const wchar_t *>(writeSemName.utf16()), 0, SEMAPHORE_ALL_ACCESS); -#endif + Use a lockfile in order to protect us against other QSettings instances + trying to write the same settings at the same time. - if (writeSemaphore) { -#ifndef Q_OS_WINRT - WaitForSingleObject(writeSemaphore, INFINITE); -#else - WaitForSingleObjectEx(writeSemaphore, INFINITE, FALSE); -#endif - } else { - setStatus(QSettings::AccessError); - return; - } - } - - // Acquire all the read locks if we will be writing, to make sure nobody - // reads while we're writing. If we are only reading, acquire a single - // read lock. - QString readSemName(QLatin1String("QSettingsReadSem ")); - readSemName.append(file.fileName()); - -#ifndef Q_OS_WINRT - readSemaphore = CreateSemaphore(0, FileLockSemMax, FileLockSemMax, reinterpret_cast<const wchar_t *>(readSemName.utf16())); -#else - readSemaphore = CreateSemaphoreEx(0, FileLockSemMax, FileLockSemMax, reinterpret_cast<const wchar_t *>(readSemName.utf16()), 0, SEMAPHORE_ALL_ACCESS); -#endif - - if (readSemaphore) { - for (int i = 0; i < numReadLocks; ++i) -#ifndef Q_OS_WINRT - WaitForSingleObject(readSemaphore, INFINITE); -#else - WaitForSingleObjectEx(readSemaphore, INFINITE, FALSE); -#endif - } else { + We only need to lock if we are actually writing as only concurrent writes are a problem. + Concurrent read and write are not a problem because the writing operation is atomic. + */ + QLockFile lockFile(confFile->name + QLatin1String(".lock")); + if (!readOnly) { + if (!confFile->isWritable() || !lockFile.lock() ) { setStatus(QSettings::AccessError); - if (writeSemaphore != 0) { - ReleaseSemaphore(writeSemaphore, 1, 0); - CloseHandle(writeSemaphore); - } return; } } -#else - if (file.isOpen()) - unixLock(file.handle(), readOnly ? F_RDLCK : F_WRLCK); #endif - // If we have created the file, apply the file perms - if (file.isOpen()) { - if (createFile) { - QFile::Permissions perms = file.permissions() | QFile::ReadOwner | QFile::WriteOwner; - if (!confFile->userPerms) - perms |= QFile::ReadGroup | QFile::ReadOther; - file.setPermissions(perms); - } - } - /* We hold the lock. Let's reread the file if it has changed since last time we read it. */ QFileInfo fileInfo(confFile->name); bool mustReadFile = true; + bool createFile = !fileInfo.exists(); if (!readOnly) mustReadFile = (confFile->size != fileInfo.size() @@ -1565,6 +1400,12 @@ void QConfFileSettingsPrivate::syncConfFile(int confFileNo) confFile->unparsedIniSections.clear(); confFile->originalKeys.clear(); + QFile file(confFile->name); + if (!createFile && !file.open(QFile::ReadOnly)) { + setStatus(QSettings::AccessError); + return; + } + /* Files that we can't read (because of permissions or because they don't exist) are treated as empty files. @@ -1615,42 +1456,40 @@ void QConfFileSettingsPrivate::syncConfFile(int confFileNo) ensureAllSectionsParsed(confFile); ParsedSettingsMap mergedKeys = confFile->mergedKeyMap(); - if (file.isWritable()) { #ifdef Q_OS_MAC - if (format == QSettings::NativeFormat) { - ok = writePlistFile(confFile->name, mergedKeys); - } else + if (format == QSettings::NativeFormat) { + ok = writePlistFile(confFile->name, mergedKeys); + } else #endif - { - file.seek(0); - file.resize(0); + { +#ifndef QT_BOOTSTRAPPED + QSaveFile sf(confFile->name); +#else + QFile sf(confFile->name); +#endif + if (!sf.open(QIODevice::WriteOnly)) { + setStatus(QSettings::AccessError); + ok = false; + } else if (format <= QSettings::IniFormat) { + ok = writeIniFile(sf, mergedKeys); + } else { + if (writeFunc) { + QSettings::SettingsMap tempOriginalKeys; - if (format <= QSettings::IniFormat) { - ok = writeIniFile(file, mergedKeys); - if (!ok) { - // try to restore old data; might work if the disk was full and the new data - // was larger than the old data - file.seek(0); - file.resize(0); - writeIniFile(file, confFile->originalKeys); + ParsedSettingsMap::const_iterator i = mergedKeys.constBegin(); + while (i != mergedKeys.constEnd()) { + tempOriginalKeys.insert(i.key(), i.value()); + ++i; } + ok = writeFunc(sf, tempOriginalKeys); } else { - if (writeFunc) { - QSettings::SettingsMap tempOriginalKeys; - - ParsedSettingsMap::const_iterator i = mergedKeys.constBegin(); - while (i != mergedKeys.constEnd()) { - tempOriginalKeys.insert(i.key(), i.value()); - ++i; - } - ok = writeFunc(file, tempOriginalKeys); - } else { - ok = false; - } + ok = false; } } - } else { - ok = false; +#ifndef QT_BOOTSTRAPPED + if (ok) + ok = sf.commit(); +#endif } if (ok) { @@ -1662,24 +1501,18 @@ void QConfFileSettingsPrivate::syncConfFile(int confFileNo) QFileInfo fileInfo(confFile->name); confFile->size = fileInfo.size(); confFile->timeStamp = fileInfo.lastModified(); + + // If we have created the file, apply the file perms + if (createFile) { + QFile::Permissions perms = fileInfo.permissions() | QFile::ReadOwner | QFile::WriteOwner; + if (!confFile->userPerms) + perms |= QFile::ReadGroup | QFile::ReadOther; + QFile(confFile->name).setPermissions(perms); + } } else { setStatus(QSettings::AccessError); } } - - /* - Release the file lock. - */ -#ifdef Q_OS_WIN - if (readSemaphore != 0) { - ReleaseSemaphore(readSemaphore, numReadLocks, 0); - CloseHandle(readSemaphore); - } - if (writeSemaphore != 0) { - ReleaseSemaphore(writeSemaphore, 1, 0); - CloseHandle(writeSemaphore); - } -#endif } enum { Space = 0x1, Special = 0x2 }; @@ -2505,12 +2338,6 @@ void QConfFileSettingsPrivate::ensureSectionParsed(QConfFile *confFile, 10.8 (Mountain Lion), only root can. However, 10.9 (Mavericks) changes that rule again but only for the native format (plist files). - \li On Unix and Mac OS X systems, the advisory file locking is disabled - if NFS (or AutoFS or CacheFS) is detected to work around a bug in the - NFS fcntl() implementation, which hangs forever if statd or lockd aren't - running. Also, the locking isn't performed when accessing \c .plist - files. - \li On the BlackBerry platform, applications run in a sandbox. They are not allowed to read or write outside of this sandbox. This involves the following limitations: |