Solid

fstabhandling.cpp
1/*
2 SPDX-FileCopyrightText: 2006-2010 Kevin Ottens <ervin@kde.org>
3 SPDX-FileCopyrightText: 2010 Mario Bensi <mbensi@ipsquad.net>
4
5 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
6*/
7
8#include "fstabhandling.h"
9#include "fstab_debug.h"
10
11#include <QFile>
12#include <QObject>
13#include <QProcess>
14#include <QRegularExpression>
15#include <QStandardPaths>
16#include <QTextStream>
17#include <QThreadStorage>
18
19#include <solid/devices/soliddefs_p.h>
20
21#include <solid/config-solid.h>
22#include <stdlib.h>
23
24#if HAVE_MNTENT_H
25#include <mntent.h>
26#endif
27
28// This is the *BSD branch
29#if HAVE_SYS_MOUNT_H
30#if HAVE_SYS_TYPES_H
31#include <sys/types.h>
32#endif
33#if HAVE_SYS_PARAM_H
34#include <sys/param.h>
35#endif
36#include <sys/mount.h>
37#endif
38
39#define FSTAB "/etc/fstab"
40
41// There are currently two APIs implemented:
42// setmntent + getmntent + struct mntent (linux...)
43// getmntinfo + struct statfs&flags (BSD 4.4 and friends)
44
45Q_GLOBAL_STATIC(QThreadStorage<Solid::Backends::Fstab::FstabHandling>, globalFstabCache)
46
47Solid::Backends::Fstab::FstabHandling::FstabHandling()
48 : m_fstabCacheValid(false)
49 , m_mtabCacheValid(false)
50{
51}
52
53bool _k_isFstabNetworkFileSystem(const QString &fstype, const QString &devName)
54{
55 if (fstype == QLatin1String("nfs") //
56 || fstype == QLatin1String("nfs4") //
57 || fstype == QLatin1String("smbfs") //
58 || fstype == QLatin1String("cifs") //
59 || fstype == QLatin1String("smb3") //
60 || fstype == QLatin1String("fuse.sshfs") //
61 || devName.startsWith(QLatin1String("//"))) {
62 return true;
63 }
64 return false;
65}
66
67bool _k_isFstabSupportedLocalFileSystem(const QString &fstype)
68{
69 if (fstype == QLatin1String("fuse.encfs") //
70 || fstype == QLatin1String("fuse.cryfs") //
71 || fstype == QLatin1String("fuse.gocryptfs") //
72 || fstype == QLatin1String("overlay")) {
73 return true;
74 }
75 return false;
76}
77
78QString _k_deviceNameForMountpoint(const QString &source, const QString &fstype, const QString &mountpoint)
79{
80 if (fstype.startsWith(QLatin1String("fuse.")) || fstype == QLatin1String("overlay")) {
81 return fstype + mountpoint;
82 }
83 // A source may be mounted several times, e.g. with different
84 // options, often a network share with different credentials
85 // for different users. Make sure it is unique by appending the
86 // mountpoint (which is unique).
87
88 auto _mountpoint = mountpoint;
89 if (fstype == QLatin1String("nfs") || fstype == QLatin1String("nfs4")) {
90 if (!mountpoint.startsWith(QLatin1Char('/'))) {
91 // making sure mount point starts with /
92 _mountpoint.prepend(QLatin1Char('/'));
93 }
94 }
95 return source + QLatin1Char(':') + _mountpoint;
96}
97
98void Solid::Backends::Fstab::FstabHandling::_k_updateFstabMountPointsCache()
99{
100 if (globalFstabCache->localData().m_fstabCacheValid) {
101 return;
102 }
103
104 globalFstabCache->localData().m_fstabCache.clear();
105 globalFstabCache->localData().m_fstabOptionsCache.clear();
106
107#if HAVE_SETMNTENT
108
109 FILE *fstab;
110 if ((fstab = setmntent(FSTAB, "r")) == nullptr) {
111 return;
112 }
113
114 struct mntent *fe;
115 while ((fe = getmntent(fstab)) != nullptr) {
116 const QString fsname = QFile::decodeName(fe->mnt_fsname);
117 const QString fstype = QFile::decodeName(fe->mnt_type);
118 if (_k_isFstabNetworkFileSystem(fstype, fsname) || _k_isFstabSupportedLocalFileSystem(fstype)) {
119 const QString mountpoint = QFile::decodeName(fe->mnt_dir);
120 const QString device = _k_deviceNameForMountpoint(fsname, fstype, mountpoint);
121 QStringList options = QFile::decodeName(fe->mnt_opts).split(QLatin1Char(','));
122
123 globalFstabCache->localData().m_fstabCache.insert(device, mountpoint);
124 globalFstabCache->localData().m_fstabFstypeCache.insert(device, fstype);
125 while (!options.isEmpty()) {
126 globalFstabCache->localData().m_fstabOptionsCache.insert(device, options.takeFirst());
127 }
128 }
129 }
130
131 endmntent(fstab);
132
133#else
134
135 QFile fstab(QStringLiteral(FSTAB));
136 if (!fstab.open(QIODevice::ReadOnly)) {
137 return;
138 }
139
140 QTextStream stream(&fstab);
141 QString line;
142
143 while (!stream.atEnd()) {
144 line = stream.readLine().simplified();
145 if (line.isEmpty() || line.startsWith(QLatin1Char('#'))) {
146 continue;
147 }
148
149 // not empty or commented out by '#'
150 const QStringList items = line.split(QLatin1Char(' '));
151 if (items.count() < 4) {
152 continue;
153 }
154
155 // prevent accessing a blocking directory
156 if (_k_isFstabNetworkFileSystem(items.at(2), items.at(0)) || _k_isFstabSupportedLocalFileSystem(items.at(2))) {
157 const QString device = items.at(0);
158 const QString fsType = items.at(2);
159 QString mountpoint = items.at(1);
160
161 if (fsType == QLatin1String("nfs") || fsType == QLatin1String("nfs4")) {
162 if (!mountpoint.startsWith(QLatin1Char('/'))) {
163 // making sure mount point starts with /
164 mountpoint.prepend(QLatin1Char('/'));
165 }
166 }
167
168 globalFstabCache->localData().m_fstabCache.insert(device, mountpoint);
169 }
170 }
171
172 fstab.close();
173#endif
174 globalFstabCache->localData().m_fstabCacheValid = true;
175}
176
177QStringList Solid::Backends::Fstab::FstabHandling::deviceList()
178{
179 _k_updateFstabMountPointsCache();
180 _k_updateMtabMountPointsCache();
181
182 QStringList devices = globalFstabCache->localData().m_mtabCache.keys();
183
184 // Ensure that regardless an fstab device ends with a slash
185 // it will match its eventual mounted device regardless whether or not its path
186 // ends with a slash
187 for (auto it = globalFstabCache->localData().m_fstabCache.constBegin(), end = globalFstabCache->localData().m_fstabCache.constEnd(); it != end; ++it) {
188 auto device = it.key();
189 // the device is already known
190 if (devices.contains(device)) {
191 continue;
192 }
193
194 // deviceName will or won't end with / depending if device ended with one
195 QString deviceName = device;
196 if (deviceName.endsWith(QLatin1Char('/'))) {
197 deviceName.chop(1);
198 } else {
199 deviceName.append(QLatin1Char('/'));
200 }
201 if (!devices.contains(deviceName)) {
202 devices.append(device);
203 }
204 }
205 return devices;
206}
207
208QStringList Solid::Backends::Fstab::FstabHandling::mountPoints(const QString &device)
209{
210 _k_updateFstabMountPointsCache();
211 _k_updateMtabMountPointsCache();
212
213 QStringList mountpoints = globalFstabCache->localData().m_fstabCache.values(device);
214 mountpoints += globalFstabCache->localData().m_mtabCache.values(device);
215 mountpoints.removeDuplicates();
216 return mountpoints;
217}
218
219QStringList Solid::Backends::Fstab::FstabHandling::options(const QString &device)
220{
221 _k_updateFstabMountPointsCache();
222
223 QStringList options = globalFstabCache->localData().m_fstabOptionsCache.values(device);
224 return options;
225}
226
227QString Solid::Backends::Fstab::FstabHandling::fstype(const QString &device)
228{
229 _k_updateFstabMountPointsCache();
230
231 return globalFstabCache->localData().m_fstabFstypeCache.value(device);
232}
233
234bool Solid::Backends::Fstab::FstabHandling::callSystemCommand(const QString &commandName,
235 const QStringList &args,
236 const QObject *receiver,
237 std::function<void(QProcess *)> callback)
238{
239 static const QStringList searchPaths{QStringLiteral("/sbin"), QStringLiteral("/bin"), QStringLiteral("/usr/sbin"), QStringLiteral("/usr/bin")};
240 static const QString joinedPaths = searchPaths.join(QLatin1Char(':'));
241 const QString exec = QStandardPaths::findExecutable(commandName, searchPaths);
242 if (exec.isEmpty()) {
243 qCWarning(FSTAB_LOG) << "Couldn't find executable" << commandName << "in" << joinedPaths;
244 return false;
245 }
246
247 QProcess *process = new QProcess();
248
249 QObject::connect(process,
250 static_cast<void (QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished),
251 receiver,
252 [process, callback](int exitCode, QProcess::ExitStatus exitStatus) {
253 Q_UNUSED(exitCode);
254 Q_UNUSED(exitStatus);
255 callback(process);
256 process->deleteLater();
257 });
258
259 static const QRegularExpression re(QStringLiteral("^PATH=.*"), QRegularExpression::CaseInsensitiveOption);
261 env.replaceInStrings(re, QLatin1String("PATH=") + joinedPaths);
262 process->setEnvironment(env);
263 process->start(exec, args);
264
265 if (process->waitForStarted()) {
266 return true;
267 }
268
269 delete process;
270 return false;
271}
272
273void Solid::Backends::Fstab::FstabHandling::_k_updateMtabMountPointsCache()
274{
275 if (globalFstabCache->localData().m_mtabCacheValid) {
276 return;
277 }
278
279 globalFstabCache->localData().m_mtabCache.clear();
280
281#if HAVE_GETMNTINFO
282
283#if GETMNTINFO_USES_STATVFS
284 struct statvfs *mounted;
285#else
286 struct statfs *mounted;
287#endif
288
289 int num_fs = getmntinfo(&mounted, MNT_NOWAIT);
290
291 for (int i = 0; i < num_fs; i++) {
292 QString type = QFile::decodeName(mounted[i].f_fstypename);
293 if (_k_isFstabNetworkFileSystem(type, QString()) || _k_isFstabSupportedLocalFileSystem(type)) {
294 const QString fsname = QFile::decodeName(mounted[i].f_mntfromname);
295 const QString mountpoint = QFile::decodeName(mounted[i].f_mntonname);
296 const QString device = _k_deviceNameForMountpoint(fsname, type, mountpoint);
297 globalFstabCache->localData().m_mtabCache.insert(device, mountpoint);
298 globalFstabCache->localData().m_fstabFstypeCache.insert(device, type);
299 }
300 }
301
302#else
303 FILE *mnttab;
304 if ((mnttab = setmntent("/etc/mtab", "r")) == nullptr) {
305 return;
306 }
307
308 struct mntent *fe;
309 while ((fe = getmntent(mnttab)) != nullptr) {
310 const QString type = QFile::decodeName(fe->mnt_type);
311 if (_k_isFstabNetworkFileSystem(type, QString()) || _k_isFstabSupportedLocalFileSystem(type)) {
312 const QString fsname = QFile::decodeName(fe->mnt_fsname);
313 const QString mountpoint = QFile::decodeName(fe->mnt_dir);
314 const QString device = _k_deviceNameForMountpoint(fsname, type, mountpoint);
315 globalFstabCache->localData().m_mtabCache.insert(device, mountpoint);
316 globalFstabCache->localData().m_fstabFstypeCache.insert(device, type);
317 }
318 }
319 endmntent(mnttab);
320#endif
321
322 globalFstabCache->localData().m_mtabCacheValid = true;
323}
324
325QStringList Solid::Backends::Fstab::FstabHandling::currentMountPoints(const QString &device)
326{
327 _k_updateMtabMountPointsCache();
328 return globalFstabCache->localData().m_mtabCache.values(device);
329}
330
331void Solid::Backends::Fstab::FstabHandling::flushMtabCache()
332{
333 globalFstabCache->localData().m_mtabCacheValid = false;
334}
335
336void Solid::Backends::Fstab::FstabHandling::flushFstabCache()
337{
338 globalFstabCache->localData().m_fstabCacheValid = false;
339}
VehicleSection::Type type(QStringView coachNumber, QStringView coachClassification)
QString decodeName(const QByteArray &localFileName)
void append(QList< T > &&value)
const_reference at(qsizetype i) const const
qsizetype count() const const
iterator insert(const_iterator before, parameter_type value)
bool isEmpty() const const
value_type takeFirst()
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
void deleteLater()
void setEnvironment(const QStringList &environment)
void finished(int exitCode, QProcess::ExitStatus exitStatus)
void start(OpenMode mode)
QStringList systemEnvironment()
bool waitForStarted(int msecs)
QString findExecutable(const QString &executableName, const QStringList &paths)
QString & append(QChar ch)
void chop(qsizetype n)
bool endsWith(QChar c, Qt::CaseSensitivity cs) const const
bool isEmpty() const const
QString & prepend(QChar ch)
QString simplified() const const
QStringList split(QChar sep, Qt::SplitBehavior behavior, Qt::CaseSensitivity cs) const const
bool startsWith(QChar c, Qt::CaseSensitivity cs) const const
bool contains(QLatin1StringView str, Qt::CaseSensitivity cs) const const
qsizetype removeDuplicates()
QStringList & replaceInStrings(QStringView before, QStringView after, Qt::CaseSensitivity cs)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Sep 13 2024 11:48:20 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.