Akonadi

utils.cpp
1 /*
2  * SPDX-FileCopyrightText: 2010 Tobias Koenig <[email protected]>
3  * SPDX-FileCopyrightText: 2014 Daniel Vr├ítil <[email protected]>
4  *
5  * SPDX-License-Identifier: LGPL-2.0-or-later
6  *
7  */
8 
9 #include "utils.h"
10 #include "akonadiserver_debug.h"
11 #include "instance_p.h"
12 
13 #include <private/standarddirs_p.h>
14 
15 #include <QDir>
16 #include <QFileInfo>
17 #include <QHostInfo>
18 #include <QSettings>
19 
20 #if !defined(Q_OS_WIN)
21 #include <cerrno>
22 #include <cstdlib>
23 #include <sys/types.h>
24 #include <sys/un.h>
25 #include <unistd.h>
26 
27 static QString akonadiSocketDirectory();
28 static bool checkSocketDirectory(const QString &path);
29 static bool createSocketDirectory(const QString &link);
30 #endif
31 
32 #ifdef Q_OS_LINUX
33 #include <fcntl.h>
34 #include <mntent.h>
35 #include <stdio.h>
36 #include <sys/ioctl.h>
37 #endif
38 
39 using namespace Akonadi;
40 using namespace Akonadi::Server;
41 
42 QString Utils::preferredSocketDirectory(const QString &defaultDirectory, int fnLengthHint)
43 {
44  const QString serverConfigFile = StandardDirs::serverConfigFile(StandardDirs::ReadWrite);
45  const QSettings serverSettings(serverConfigFile, QSettings::IniFormat);
46 
47 #if defined(Q_OS_WIN)
48  const QString socketDir = serverSettings.value(QLatin1String("Connection/SocketDirectory"), defaultDirectory).toString();
49 #else
50  QString socketDir = defaultDirectory;
51  if (!serverSettings.contains(QStringLiteral("Connection/SocketDirectory"))) {
52  // if no socket directory is defined, use the symlinked from /tmp
53  socketDir = akonadiSocketDirectory();
54 
55  if (socketDir.isEmpty()) { // if that does not work, fall back on default
56  socketDir = defaultDirectory;
57  }
58  } else {
59  socketDir = serverSettings.value(QStringLiteral("Connection/SocketDirectory"), defaultDirectory).toString();
60  }
61 
62  if (socketDir.contains(QLatin1String("$USER"))) {
63  const QString userName = QString::fromLocal8Bit(qgetenv("USER"));
64  if (!userName.isEmpty()) {
65  socketDir.replace(QLatin1String("$USER"), userName);
66  }
67  }
68 
69  if (socketDir[0] != QLatin1Char('/')) {
70  QDir::home().mkdir(socketDir);
71  socketDir = QDir::homePath() + QLatin1Char('/') + socketDir;
72  }
73 
74  QFileInfo dirInfo(socketDir);
75  if (!dirInfo.exists()) {
76  QDir::home().mkpath(dirInfo.absoluteFilePath());
77  }
78 
79  const std::size_t totalLength = socketDir.length() + 1 + fnLengthHint;
80  const std::size_t maxLen = sizeof(sockaddr_un::sun_path);
81  if (totalLength >= maxLen) {
82  qCCritical(AKONADISERVER_LOG) << "akonadiSocketDirectory() length of" << totalLength << "is longer than the system limit" << maxLen;
83  }
84 #endif
85  return socketDir;
86 }
87 
88 #if !defined(Q_OS_WIN)
89 QString akonadiSocketDirectory()
90 {
92 
93  if (hostname.isEmpty()) {
94  qCCritical(AKONADISERVER_LOG) << "QHostInfo::localHostName() failed";
95  return QString();
96  }
97 
98  const QString identifier = Instance::hasIdentifier() ? Instance::identifier() : QStringLiteral("default");
99  const QString link = StandardDirs::saveDir("data") + QStringLiteral("/socket-%1-%2").arg(hostname, identifier);
100 
101  if (checkSocketDirectory(link)) {
102  return QFileInfo(link).symLinkTarget();
103  }
104 
105  if (createSocketDirectory(link)) {
106  return QFileInfo(link).symLinkTarget();
107  }
108 
109  qCCritical(AKONADISERVER_LOG) << "Could not create socket directory for Akonadi.";
110  return QString();
111 }
112 
113 static bool checkSocketDirectory(const QString &path)
114 {
115  QFileInfo info(path);
116 
117  if (!info.exists()) {
118  return false;
119  }
120 
121  if (info.isSymLink()) {
122  info = QFileInfo(info.symLinkTarget());
123  }
124 
125  if (!info.isDir()) {
126  return false;
127  }
128 
129  if (info.ownerId() != getuid()) {
130  return false;
131  }
132 
133  return true;
134 }
135 
136 static bool createSocketDirectory(const QString &link)
137 {
138  const QString directory = StandardDirs::saveDir("runtime");
139 
140  if (!QDir().mkpath(directory)) {
141  qCCritical(AKONADISERVER_LOG) << "Creating socket directory with name" << directory << "failed:" << strerror(errno);
142  return false;
143  }
144 
145  QFile::remove(link);
146 
147  if (!QFile::link(directory, link)) {
148  qCCritical(AKONADISERVER_LOG) << "Creating symlink from" << directory << "to" << link << "failed";
149  return false;
150  }
151 
152  return true;
153 }
154 #endif
155 
156 QString Utils::getDirectoryFileSystem(const QString &directory)
157 {
158 #ifndef Q_OS_LINUX
159  Q_UNUSED(directory)
160  return QString();
161 #else
162  QString bestMatchPath;
163  QString bestMatchFS;
164 
165  FILE *mtab = setmntent("/etc/mtab", "r");
166  if (!mtab) {
167  return QString();
168  }
169  while (mntent *mnt = getmntent(mtab)) {
170  if (qstrcmp(mnt->mnt_type, MNTTYPE_IGNORE) == 0) {
171  continue;
172  }
173 
174  const QString dir = QString::fromLocal8Bit(mnt->mnt_dir);
175  if (!directory.startsWith(dir) || dir.length() < bestMatchPath.length()) {
176  continue;
177  }
178 
179  bestMatchPath = dir;
180  bestMatchFS = QString::fromLocal8Bit(mnt->mnt_type);
181  }
182 
183  endmntent(mtab);
184 
185  return bestMatchFS;
186 #endif
187 }
188 
189 void Utils::disableCoW(const QString &path)
190 {
191 #ifndef Q_OS_LINUX
192  Q_UNUSED(path)
193 #else
194  qCDebug(AKONADISERVER_LOG) << "Detected Btrfs, disabling copy-on-write on database files";
195 
196  // from linux/fs.h, so that Akonadi does not depend on Linux header files
197 #ifndef FS_IOC_GETFLAGS
198 #define FS_IOC_GETFLAGS _IOR('f', 1, long)
199 #endif
200 #ifndef FS_IOC_SETFLAGS
201 #define FS_IOC_SETFLAGS _IOW('f', 2, long)
202 #endif
203 
204  // Disable COW on file
205 #ifndef FS_NOCOW_FL
206 #define FS_NOCOW_FL 0x00800000
207 #endif
208 
209  ulong flags = 0;
210  const int fd = open(qPrintable(path), O_RDONLY);
211  if (fd == -1) {
212  qCWarning(AKONADISERVER_LOG) << "Failed to open" << path << "to modify flags (" << errno << ")";
213  return;
214  }
215 
216  if (ioctl(fd, FS_IOC_GETFLAGS, &flags) == -1) {
217  qCWarning(AKONADISERVER_LOG) << "ioctl error: failed to get file flags (" << errno << ")";
218  close(fd);
219  return;
220  }
221  if (!(flags & FS_NOCOW_FL)) {
222  flags |= FS_NOCOW_FL;
223  if (ioctl(fd, FS_IOC_SETFLAGS, &flags) == -1) {
224  qCWarning(AKONADISERVER_LOG) << "ioctl error: failed to set file flags (" << errno << ")";
225  close(fd);
226  return;
227  }
228  }
229  close(fd);
230 #endif
231 }
bool remove()
bool mkdir(const QString &dirName) const const
QString homePath()
const QList< QKeySequence > & close()
KIOCORE_EXPORT CopyJob * link(const QList< QUrl > &src, const QUrl &destDir, JobFlags flags=DefaultFlags)
NETWORKMANAGERQT_EXPORT QString hostname()
QString fromLocal8Bit(const char *str, int size)
QDir home()
bool isEmpty() const const
int length() const const
bool mkpath(const QString &dirPath) const const
KIOCORE_EXPORT MkpathJob * mkpath(const QUrl &url, const QUrl &baseUrl=QUrl(), JobFlags flags=DefaultFlags)
QString & replace(int position, int n, QChar after)
bool startsWith(const QString &s, Qt::CaseSensitivity cs) const const
KIOFILEWIDGETS_EXPORT QString dir(const QString &fileClass)
QString localHostName()
QString symLinkTarget() const const
bool contains(QChar ch, Qt::CaseSensitivity cs) const const
const QList< QKeySequence > & open()
Helper integration between Akonadi and Qt.
This file is part of the KDE documentation.
Documentation copyright © 1996-2022 The KDE developers.
Generated on Sat Jun 25 2022 06:00:33 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.