Akonadi

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

KDE's Doxygen guidelines are available online.