diff options
author | Damien Caliste <dcaliste@free.fr> | 2019-05-20 15:44:18 +0200 |
---|---|---|
committer | Damien Caliste <dcaliste@free.fr> | 2019-06-04 13:27:02 +0200 |
commit | 4ebac33644d5db0b727680f1dacb18616daafe86 (patch) | |
tree | 0229f943260a85d3a6673fb30e351b4082efe79a /src/corelib/tools | |
parent | 5f30fd64269822d4696ac071e17a39ae62a7b50f (diff) |
Detect system time zone from linked symlinks
[ChangeLog][QtCore][QTimeZone] The IANA timezone database backend
now properly follows symlinks even when they point to variable
locations like /run or /var (useful when /etc is mounted read-only).
Fixes: QTBUG-75936
Fixes: QTBUG-75527
Change-Id: If0dc2bfa20659e76c3bd062c75597a9ad01ad954
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
Diffstat (limited to 'src/corelib/tools')
-rw-r--r-- | src/corelib/tools/qtimezoneprivate_tz.cpp | 43 |
1 files changed, 38 insertions, 5 deletions
diff --git a/src/corelib/tools/qtimezoneprivate_tz.cpp b/src/corelib/tools/qtimezoneprivate_tz.cpp index 7d85bc077d..57bc000af5 100644 --- a/src/corelib/tools/qtimezoneprivate_tz.cpp +++ b/src/corelib/tools/qtimezoneprivate_tz.cpp @@ -51,6 +51,12 @@ #include "qlocale_tools_p.h" #include <algorithm> +#include <errno.h> +#include <limits.h> +#if !defined(Q_OS_INTEGRITY) +#include <sys/param.h> // to use MAXSYMLINKS constant +#endif +#include <unistd.h> // to use _SC_SYMLOOP_MAX constant QT_BEGIN_NAMESPACE @@ -1045,6 +1051,27 @@ QTimeZonePrivate::Data QTzTimeZonePrivate::previousTransition(qint64 beforeMSecs return last > m_tranTimes.cbegin() ? dataForTzTransition(*--last) : invalidData(); } +static long getSymloopMax() +{ +#if defined(SYMLOOP_MAX) + return SYMLOOP_MAX; // if defined, at runtime it can only be greater than this, so this is a safe bet +#else + errno = 0; + long result = sysconf(_SC_SYMLOOP_MAX); + if (result >= 0) + return result; + // result is -1, meaning either error or no limit + Q_ASSERT(!errno); // ... but it can't be an error, POSIX mandates _SC_SYMLOOP_MAX + + // therefore we can make up our own limit +# if defined(MAXSYMLINKS) + return MAXSYMLINKS; +# else + return 8; +# endif +#endif +} + // TODO Could cache the value and monitor the required files for any changes QByteArray QTzTimeZonePrivate::systemTimeZoneId() const { @@ -1062,12 +1089,18 @@ QByteArray QTzTimeZonePrivate::systemTimeZoneId() const // On most distros /etc/localtime is a symlink to a real file so extract name from the path if (ianaId.isEmpty()) { - const QString path = QFile::symLinkTarget(QStringLiteral("/etc/localtime")); - if (!path.isEmpty()) { + const QLatin1String zoneinfo("/zoneinfo/"); + QString path = QFile::symLinkTarget(QStringLiteral("/etc/localtime")); + int index = -1; + long iteration = getSymloopMax(); + // Symlink may point to another symlink etc. before being under zoneinfo/ + // We stop on the first path under /zoneinfo/, even if it is itself a + // symlink, like America/Montreal pointing to America/Toronto + while (iteration-- > 0 && !path.isEmpty() && (index = path.indexOf(zoneinfo)) < 0) + path = QFile::symLinkTarget(path); + if (index >= 0) { // /etc/localtime is a symlink to the current TZ file, so extract from path - int index = path.indexOf(QLatin1String("/zoneinfo/")); - if (index != -1) - ianaId = path.mid(index + 10).toUtf8(); + ianaId = path.mid(index + zoneinfo.size()).toUtf8(); } } |