1/*
2 Copyright 2006-2010 Kevin Ottens <ervin@kde.org>
3 Copyright 2010 Mario Bensi <mbensi@ipsquad.net>
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) version 3, or any
9 later version accepted by the membership of KDE e.V. (or its
10 successor approved by the membership of KDE e.V.), which shall
11 act as a proxy defined in Section 6 of version 3 of the license.
12
13 This library is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public
19 License along with this library. If not, see <http://www.gnu.org/licenses/>.
20*/
21
22#include "fstabhandling.h"
23
24#include <QFile>
25#include <QObject>
26#include <QProcess>
27#include <QTextStream>
28#include <QTime>
29
30#include <solid/devices/soliddefs_p.h>
31
32#include <solid/config-solid.h>
33#include <stdlib.h>
34
35#if HAVE_SYS_MNTTAB_H
36#include <sys/mnttab.h>
37#endif
38#if HAVE_MNTENT_H
39#include <mntent.h>
40#elif defined(HAVE_SYS_MNTENT_H)
41#include <sys/mntent.h>
42#endif
43
44// This is the *BSD branch
45#if HAVE_SYS_MOUNT_H
46#if HAVE_SYS_TYPES_H
47#include <sys/types.h>
48#endif
49#if HAVE_SYS_PARAM_H
50#include <sys/param.h>
51#endif
52#include <sys/mount.h>
53#include <QThreadStorage>
54#endif
55
56#ifdef Q_OS_SOLARIS
57#define FSTAB "/etc/vfstab"
58#else
59#define FSTAB "/etc/fstab"
60#endif
61
62#if ! HAVE_GETMNTINFO
63# ifdef _PATH_MOUNTED
64// On some Linux, MNTTAB points to /etc/fstab !
65# undef MNTTAB
66# define MNTTAB _PATH_MOUNTED
67# else
68# ifndef MNTTAB
69# ifdef MTAB_FILE
70# define MNTTAB MTAB_FILE
71# else
72# define MNTTAB "/etc/mnttab"
73# endif
74# endif
75# endif
76#endif
77
78// There are (at least) four kind of APIs:
79// setmntent + getmntent + struct mntent (linux...)
80// getmntent + struct mnttab
81// getmntinfo + struct statfs&flags (BSD 4.4 and friends)
82// getfsent + char* (BSD 4.3 and friends)
83
84#if HAVE_SETMNTENT
85#define SETMNTENT setmntent
86#define ENDMNTENT endmntent
87#define STRUCT_MNTENT struct mntent *
88#define STRUCT_SETMNTENT FILE *
89#define GETMNTENT(file, var) ((var = getmntent(file)) != nullptr)
90#define MOUNTPOINT(var) var->mnt_dir
91#define MOUNTTYPE(var) var->mnt_type
92#define MOUNTOPTIONS(var) var->mnt_opts
93#define FSNAME(var) var->mnt_fsname
94#else
95#define SETMNTENT fopen
96#define ENDMNTENT fclose
97#define STRUCT_MNTENT struct mnttab
98#define STRUCT_SETMNTENT FILE *
99#define GETMNTENT(file, var) (getmntent(file, &var) == nullptr)
100#define MOUNTPOINT(var) var.mnt_mountp
101#define MOUNTTYPE(var) var.mnt_fstype
102#define MOUNTOPTIONS(var) var.mnt_mntopts
103#define FSNAME(var) var.mnt_special
104#endif
105
106Q_GLOBAL_STATIC(QThreadStorage<Solid::Backends::Fstab::FstabHandling>, globalFstabCache)
107
108Solid::Backends::Fstab::FstabHandling::FstabHandling()
109 : m_fstabCacheValid(false),
110 m_mtabCacheValid(false)
111{ }
112
113bool _k_isFstabNetworkFileSystem(const QString &fstype, const QString &devName)
114{
115 if (fstype == "nfs"
116 || fstype == "nfs4"
117 || fstype == "smbfs"
118 || fstype == "cifs"
119 || devName.startsWith(QLatin1String("//"))) {
120 return true;
121 }
122 return false;
123}
124
125bool _k_isFstabSupportedLocalFileSystem(const QString &fstype)
126{
127 if (fstype == "fuse.encfs" ||
128 fstype == "fuse.cryfs" ||
129 fstype == "overlay") {
130 return true;
131 }
132 return false;
133}
134
135QString _k_deviceNameForMountpoint(const QString &source, const QString &fstype,
136 const QString &mountpoint)
137{
138 if (fstype.startsWith("fuse.") ||
139 fstype == QLatin1String("overlay")) {
140 return fstype + mountpoint;
141 }
142 return source;
143}
144
145void Solid::Backends::Fstab::FstabHandling::_k_updateFstabMountPointsCache()
146{
147 if (globalFstabCache->localData().m_fstabCacheValid) {
148 return;
149 }
150
151 globalFstabCache->localData().m_fstabCache.clear();
152 globalFstabCache->localData().m_fstabOptionsCache.clear();
153
154#if HAVE_SETMNTENT
155
156 FILE *fstab;
157 if ((fstab = setmntent(FSTAB, "r")) == nullptr) {
158 return;
159 }
160
161 struct mntent *fe;
162 while ((fe = getmntent(fstab)) != nullptr) {
163 const QString fsname = QFile::decodeName(fe->mnt_fsname);
164 const QString fstype = QFile::decodeName(fe->mnt_type);
165 if (_k_isFstabNetworkFileSystem(fstype, fsname) ||
166 _k_isFstabSupportedLocalFileSystem(fstype)) {
167 const QString mountpoint = QFile::decodeName(fe->mnt_dir);
168 const QString device = _k_deviceNameForMountpoint(fsname, fstype, mountpoint);
169 QStringList options = QFile::decodeName(fe->mnt_opts).split(QLatin1Char(','));
170
171 globalFstabCache->localData().m_fstabCache.insert(device, mountpoint);
172 globalFstabCache->localData().m_fstabFstypeCache.insert(device, fstype);
173 while (!options.isEmpty()) {
174 globalFstabCache->localData().m_fstabOptionsCache.insert(device, options.takeFirst());
175 }
176 }
177 }
178
179 endmntent(fstab);
180
181#else
182
183 QFile fstab(FSTAB);
184 if (!fstab.open(QIODevice::ReadOnly)) {
185 return;
186 }
187
188 QTextStream stream(&fstab);
189 QString line;
190
191 while (!stream.atEnd()) {
192 line = stream.readLine().simplified();
193 if (line.isEmpty() || line.startsWith('#')) {
194 continue;
195 }
196
197 // not empty or commented out by '#'
198 const QStringList items = line.split(' ');
199
200#ifdef Q_OS_SOLARIS
201 if (items.count() < 5) {
202 continue;
203 }
204#else
205 if (items.count() < 4) {
206 continue;
207 }
208#endif
209 //prevent accessing a blocking directory
210 if (_k_isFstabNetworkFileSystem(items.at(2), items.at(0)) ||
211 _k_isFstabSupportedLocalFileSystem(items.at(2))) {
212 const QString device = items.at(0);
213 const QString mountpoint = items.at(1);
214
215 globalFstabCache->localData().m_fstabCache.insert(device, mountpoint);
216 }
217 }
218
219 fstab.close();
220#endif
221 globalFstabCache->localData().m_fstabCacheValid = true;
222}
223
224QStringList Solid::Backends::Fstab::FstabHandling::deviceList()
225{
226 _k_updateFstabMountPointsCache();
227 _k_updateMtabMountPointsCache();
228
229 QStringList devices = globalFstabCache->localData().m_fstabCache.keys();
230 devices += globalFstabCache->localData().m_mtabCache.keys();
231 devices.removeDuplicates();
232 return devices;
233}
234
235QStringList Solid::Backends::Fstab::FstabHandling::mountPoints(const QString &device)
236{
237 _k_updateFstabMountPointsCache();
238 _k_updateMtabMountPointsCache();
239
240 QStringList mountpoints = globalFstabCache->localData().m_fstabCache.values(device);
241 mountpoints += globalFstabCache->localData().m_mtabCache.values(device);
242 mountpoints.removeDuplicates();
243 return mountpoints;
244}
245
246QStringList Solid::Backends::Fstab::FstabHandling::options(const QString &device)
247{
248 _k_updateFstabMountPointsCache();
249
250 QStringList options = globalFstabCache->localData().m_fstabOptionsCache.values(device);
251 return options;
252}
253
254QString Solid::Backends::Fstab::FstabHandling::fstype(const QString &device)
255{
256 _k_updateFstabMountPointsCache();
257
258 return globalFstabCache->localData().m_fstabFstypeCache.value(device);
259}
260
261bool Solid::Backends::Fstab::FstabHandling::callSystemCommand(const QString &commandName, const QStringList &args,
262 const QObject *receiver, std::function<void(QProcess *)> callback)
263{
264 QStringList env = QProcess::systemEnvironment();
265 env.replaceInStrings(QRegExp("^PATH=(.*)", Qt::CaseInsensitive), "PATH=/sbin:/bin:/usr/sbin/:/usr/bin");
266
267 QProcess *process = new QProcess();
268
269 QObject::connect(process, static_cast<void(QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished), receiver,
270 [process, callback](int exitCode, QProcess::ExitStatus exitStatus) {
271 Q_UNUSED(exitCode);
272 Q_UNUSED(exitStatus);
273 callback(process);
274 process->deleteLater();
275 });
276
277 process->setEnvironment(env);
278 process->start(commandName, args);
279
280 if (process->waitForStarted()) {
281 return true;
282 }
283
284 delete process;
285 return false;
286}
287
288void Solid::Backends::Fstab::FstabHandling::_k_updateMtabMountPointsCache()
289{
290 if (globalFstabCache->localData().m_mtabCacheValid) {
291 return;
292 }
293
294 globalFstabCache->localData().m_mtabCache.clear();
295
296#if HAVE_GETMNTINFO
297
298#if GETMNTINFO_USES_STATVFS
299 struct statvfs *mounted;
300#else
301 struct statfs *mounted;
302#endif
303
304 int num_fs = getmntinfo(&mounted, MNT_NOWAIT);
305
306 for (int i = 0; i < num_fs; i++) {
307 QString type = QFile::decodeName(mounted[i].f_fstypename);
308 if (_k_isFstabNetworkFileSystem(type, QString()) ||
309 _k_isFstabSupportedLocalFileSystem(type)) {
310 const QString fsname = QFile::decodeName(mounted[i].f_mntfromname);
311 const QString mountpoint = QFile::decodeName(mounted[i].f_mntonname);
312 const QString device = _k_deviceNameForMountpoint(fsname, type, mountpoint);
313 globalFstabCache->localData().m_mtabCache.insert(device, mountpoint);
314 globalFstabCache->localData().m_fstabFstypeCache.insert(device, type);
315 }
316 }
317
318#else
319 STRUCT_SETMNTENT mnttab;
320 if ((mnttab = SETMNTENT(MNTTAB, "r")) == nullptr) {
321 return;
322 }
323
324 STRUCT_MNTENT fe;
325 while (GETMNTENT(mnttab, fe)) {
326 QString type = QFile::decodeName(MOUNTTYPE(fe));
327 if (_k_isFstabNetworkFileSystem(type, QString()) ||
328 _k_isFstabSupportedLocalFileSystem(type)) {
329 const QString fsname = QFile::decodeName(FSNAME(fe));
330 const QString mountpoint = QFile::decodeName(MOUNTPOINT(fe));
331 const QString device = _k_deviceNameForMountpoint(fsname, type, mountpoint);
332 globalFstabCache->localData().m_mtabCache.insert(device, mountpoint);
333 globalFstabCache->localData().m_fstabFstypeCache.insert(device, type);
334 }
335 }
336 ENDMNTENT(mnttab);
337#endif
338
339 globalFstabCache->localData().m_mtabCacheValid = true;
340}
341
342QStringList Solid::Backends::Fstab::FstabHandling::currentMountPoints(const QString &device)
343{
344 _k_updateMtabMountPointsCache();
345 return globalFstabCache->localData().m_mtabCache.values(device);
346}
347
348void Solid::Backends::Fstab::FstabHandling::flushMtabCache()
349{
350 globalFstabCache->localData().m_mtabCacheValid = false;
351}
352
353void Solid::Backends::Fstab::FstabHandling::flushFstabCache()
354{
355 globalFstabCache->localData().m_fstabCacheValid = false;
356}
357