Akonadi

utils.cpp
1/*
2 * SPDX-FileCopyrightText: 2010 Tobias Koenig <tokoe@kde.org>
3 * SPDX-FileCopyrightText: 2014 Daniel Vrátil <dvratil@redhat.com>
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
27static QString akonadiSocketDirectory();
28static bool checkSocketDirectory(const QString &path);
29static 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
39using namespace Akonadi;
40using namespace Akonadi::Server;
41
42QString 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(QLatin1StringView("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(QLatin1StringView("$USER"))) {
63 const QString userName = QString::fromLocal8Bit(qgetenv("USER"));
64 if (!userName.isEmpty()) {
65 socketDir.replace(QLatin1StringView("$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)
89QString 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
113static 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
136static 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
156QString 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
189void 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}
Helper integration between Akonadi and Qt.
KIOCORE_EXPORT CopyJob * link(const QList< QUrl > &src, const QUrl &destDir, JobFlags flags=DefaultFlags)
KIOCORE_EXPORT MkpathJob * mkpath(const QUrl &url, const QUrl &baseUrl=QUrl(), JobFlags flags=DefaultFlags)
QString path(const QString &relativePath)
KIOCORE_EXPORT QString dir(const QString &fileClass)
const QList< QKeySequence > & close()
const QList< QKeySequence > & open()
NETWORKMANAGERQT_EXPORT QString hostname()
QDir home()
QString homePath()
bool mkdir(const QString &dirName) const const
bool mkpath(const QString &dirPath) const const
bool remove()
QString symLinkTarget() const const
QString localHostName()
bool contains(QChar ch, Qt::CaseSensitivity cs) const const
QString fromLocal8Bit(QByteArrayView str)
bool isEmpty() const const
qsizetype length() const const
QString & replace(QChar before, QChar after, Qt::CaseSensitivity cs)
bool startsWith(QChar c, Qt::CaseSensitivity cs) const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:13:38 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.