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>
parent
5f30fd6426
commit
4ebac33644
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue