KIO

kmountpoint.cpp
1 /*
2  This file is part of the KDE libraries
3  SPDX-FileCopyrightText: 2003 Waldo Bastian <[email protected]>
4  SPDX-FileCopyrightText: 2007 David Faure <[email protected]>
5 
6  SPDX-License-Identifier: LGPL-2.0-only
7 */
8 
9 #include "kmountpoint.h"
10 
11 #include <stdlib.h>
12 
13 #include <config-kmountpoint.h>
14 
15 #include <QDir>
16 #include <QFile>
17 #include <QTextStream>
18 #include <QFileInfo>
19 
20 #ifdef Q_OS_WIN
21 #include <qt_windows.h>
22 #endif
23 
24 #ifdef Q_OS_WIN
26 #else
27 static const Qt::CaseSensitivity cs = Qt::CaseSensitive;
28 #endif
29 
30 #if HAVE_VOLMGT
31 #include <volmgt.h>
32 #endif
33 #if HAVE_SYS_MNTTAB_H
34 #include <sys/mnttab.h>
35 #endif
36 #if HAVE_MNTENT_H
37 #include <mntent.h>
38 #elif HAVE_SYS_MNTENT_H
39 #include <sys/mntent.h>
40 #endif
41 
42 // This is the *BSD branch
43 #if HAVE_SYS_MOUNT_H
44 #if HAVE_SYS_TYPES_H
45 #include <sys/types.h>
46 #endif
47 #if HAVE_SYS_PARAM_H
48 #include <sys/param.h>
49 #endif
50 #include <sys/mount.h>
51 #endif
52 
53 #if HAVE_FSTAB_H
54 #include <fstab.h>
55 #endif
56 
57 #if ! HAVE_GETMNTINFO
58 # ifdef _PATH_MOUNTED
59 // On some Linux, MNTTAB points to /etc/fstab !
60 # undef MNTTAB
61 # define MNTTAB _PATH_MOUNTED
62 # else
63 # ifndef MNTTAB
64 # ifdef MTAB_FILE
65 # define MNTTAB MTAB_FILE
66 # else
67 # define MNTTAB "/etc/mnttab"
68 # endif
69 # endif
70 # endif
71 #endif
72 
73 #ifdef _OS_SOLARIS_
74 #define FSTAB "/etc/vfstab"
75 #else
76 #define FSTAB "/etc/fstab"
77 #endif
78 
79 class Q_DECL_HIDDEN KMountPoint::Private
80 {
81 public:
82  void finalizePossibleMountPoint(DetailsNeededFlags infoNeeded);
83  void finalizeCurrentMountPoint(DetailsNeededFlags infoNeeded);
84 
85  QString m_mountedFrom;
86  QString m_device; // Only available when the NeedRealDeviceName flag was set.
87  QString m_mountPoint;
88  QString m_mountType;
89  QStringList m_mountOptions;
90 };
91 
92 KMountPoint::KMountPoint()
93  : d(new Private)
94 {
95 }
96 
98 {
99  delete d;
100 }
101 
102 // There are (at least) four kind of APIs:
103 // setmntent + getmntent + struct mntent (linux...)
104 // getmntent + struct mnttab
105 // getmntinfo + struct statfs&flags (BSD 4.4 and friends)
106 // getfsent + char* (BSD 4.3 and friends)
107 
108 #if HAVE_SETMNTENT
109 #define SETMNTENT setmntent
110 #define ENDMNTENT endmntent
111 #define STRUCT_MNTENT struct mntent *
112 #define STRUCT_SETMNTENT FILE *
113 #define GETMNTENT(file, var) ((var = getmntent(file)) != nullptr)
114 #define MOUNTPOINT(var) var->mnt_dir
115 #define MOUNTTYPE(var) var->mnt_type
116 #define MOUNTOPTIONS(var) var->mnt_opts
117 #define FSNAME(var) var->mnt_fsname
118 #else
119 #define SETMNTENT fopen
120 #define ENDMNTENT fclose
121 #define STRUCT_MNTENT struct mnttab
122 #define STRUCT_SETMNTENT FILE *
123 #define GETMNTENT(file, var) (getmntent(file, &var) == nullptr)
124 #define MOUNTPOINT(var) var.mnt_mountp
125 #define MOUNTTYPE(var) var.mnt_fstype
126 #define MOUNTOPTIONS(var) var.mnt_mntopts
127 #define FSNAME(var) var.mnt_special
128 #endif
129 
130 void KMountPoint::Private::finalizePossibleMountPoint(DetailsNeededFlags infoNeeded)
131 {
132  if (m_mountedFrom.startsWith(QLatin1String("UUID="))) {
133  const QStringRef uuid = m_mountedFrom.midRef(5);
134  const QString potentialDevice = QFile::symLinkTarget(QLatin1String("/dev/disk/by-uuid/") + uuid);
135  if (QFile::exists(potentialDevice)) {
136  m_mountedFrom = potentialDevice;
137  }
138  }
139  if (m_mountedFrom.startsWith(QLatin1String("LABEL="))) {
140  const QStringRef label = m_mountedFrom.midRef(6);
141  const QString potentialDevice = QFile::symLinkTarget(QLatin1String("/dev/disk/by-label/") + label);
142  if (QFile::exists(potentialDevice)) {
143  m_mountedFrom = potentialDevice;
144  }
145  }
146 
147  if (infoNeeded & NeedRealDeviceName) {
148  if (m_mountedFrom.startsWith(QLatin1Char('/'))) {
149  m_device = QFileInfo(m_mountedFrom).canonicalFilePath();
150  }
151  }
152 
153  // Chop trailing slash
154  if (m_mountedFrom.endsWith(QLatin1Char('/'))) {
155  m_mountedFrom.chop(1);
156  }
157 }
158 
159 void KMountPoint::Private::finalizeCurrentMountPoint(DetailsNeededFlags infoNeeded)
160 {
161  if (infoNeeded & NeedRealDeviceName) {
162  if (m_mountedFrom.startsWith(QLatin1Char('/'))) {
163  m_device = QFileInfo(m_mountedFrom).canonicalFilePath();
164  }
165  }
166 }
167 
169 {
170 #ifdef Q_OS_WIN
171  return KMountPoint::currentMountPoints(infoNeeded);
172 #endif
173 
174  KMountPoint::List result;
175 
176 #if HAVE_SETMNTENT
177  STRUCT_SETMNTENT fstab;
178  if ((fstab = SETMNTENT(FSTAB, "r")) == nullptr) {
179  return result;
180  }
181 
182  STRUCT_MNTENT fe;
183  while (GETMNTENT(fstab, fe)) {
184  if (QByteArray(MOUNTTYPE(fe)) == "swap") {
185  continue;
186  }
187  Ptr mp(new KMountPoint);
188  mp->d->m_mountedFrom = QFile::decodeName(FSNAME(fe));
189 
190  mp->d->m_mountPoint = QFile::decodeName(MOUNTPOINT(fe));
191  mp->d->m_mountType = QFile::decodeName(MOUNTTYPE(fe));
192 
193  if (infoNeeded & NeedMountOptions) {
194  QString options = QFile::decodeName(MOUNTOPTIONS(fe));
195  mp->d->m_mountOptions = options.split(QLatin1Char(','));
196  }
197 
198  mp->d->finalizePossibleMountPoint(infoNeeded);
199 
200  result.append(mp);
201  }
202  ENDMNTENT(fstab);
203 #else
204  QFile f(QLatin1String(FSTAB));
205  if (!f.open(QIODevice::ReadOnly)) {
206  return result;
207  }
208 
209  QTextStream t(&f);
210  QString s;
211 
212  while (! t.atEnd()) {
213  s = t.readLine().simplified();
214  if (s.isEmpty() || (s[0] == QLatin1Char('#'))) {
215  continue;
216  }
217 
218  // not empty or commented out by '#'
219  const QStringList item = s.split(QLatin1Char(' '));
220 
221 #ifdef _OS_SOLARIS_
222  if (item.count() < 5) {
223  continue;
224  }
225 #else
226  if (item.count() < 4) {
227  continue;
228  }
229 #endif
230 
231  Ptr mp(new KMountPoint);
232 
233  int i = 0;
234  mp->d->m_mountedFrom = item[i++];
235 #ifdef _OS_SOLARIS_
236  //device to fsck
237  i++;
238 #endif
239  mp->d->m_mountPoint = item[i++];
240  mp->d->m_mountType = item[i++];
241  if (mp->d->m_mountType == QLatin1String("swap")) {
242  continue;
243  }
244  QString options = item[i++];
245 
246  if (infoNeeded & NeedMountOptions) {
247  mp->d->m_mountOptions = options.split(QLatin1Char(','));
248  }
249 
250  mp->d->finalizePossibleMountPoint(infoNeeded);
251 
252  result.append(mp);
253  } //while
254 
255  f.close();
256 #endif
257  return result;
258 }
259 
261 {
262  KMountPoint::List result;
263 
264 #if HAVE_GETMNTINFO
265 
266 #if GETMNTINFO_USES_STATVFS
267  struct statvfs *mounted;
268 #else
269  struct statfs *mounted;
270 #endif
271 
272  int num_fs = getmntinfo(&mounted, MNT_NOWAIT);
273 
274  for (int i = 0; i < num_fs; i++) {
275  Ptr mp(new KMountPoint);
276  mp->d->m_mountedFrom = QFile::decodeName(mounted[i].f_mntfromname);
277  mp->d->m_mountPoint = QFile::decodeName(mounted[i].f_mntonname);
278 
279 #ifdef __osf__
280  mp->d->m_mountType = QFile::decodeName(mnt_names[mounted[i].f_type]);
281 #else
282  mp->d->m_mountType = QFile::decodeName(mounted[i].f_fstypename);
283 #endif
284 
285  if (infoNeeded & NeedMountOptions) {
286  struct fstab *ft = getfsfile(mounted[i].f_mntonname);
287  if (ft != nullptr) {
288  QString options = QFile::decodeName(ft->fs_mntops);
289  mp->d->m_mountOptions = options.split(QLatin1Char(','));
290  } else {
291  // TODO: get mount options if not mounted via fstab, see mounted[i].f_flags
292  }
293  }
294 
295  mp->d->finalizeCurrentMountPoint(infoNeeded);
296  // TODO: Strip trailing '/' ?
297  result.append(mp);
298  }
299 
300 #elif defined(Q_OS_WIN)
301  //nothing fancy with infoNeeded but it gets the job done
302  DWORD bits = GetLogicalDrives();
303  if (!bits) {
304  return result;
305  }
306 
307  for (int i = 0; i < 26; i++) {
308  if (bits & (1 << i)) {
309  Ptr mp(new KMountPoint);
310  mp->d->m_mountPoint = QString(QLatin1Char('A' + i) + QLatin1String(":/"));
311  result.append(mp);
312  }
313  }
314 
315 #elif !defined(Q_OS_ANDROID)
316  STRUCT_SETMNTENT mnttab;
317  if ((mnttab = SETMNTENT(MNTTAB, "r")) == nullptr) {
318  return result;
319  }
320 
321  STRUCT_MNTENT fe;
322  while (GETMNTENT(mnttab, fe)) {
323  Ptr mp(new KMountPoint);
324  mp->d->m_mountedFrom = QFile::decodeName(FSNAME(fe));
325 
326  mp->d->m_mountPoint = QFile::decodeName(MOUNTPOINT(fe));
327  mp->d->m_mountType = QFile::decodeName(MOUNTTYPE(fe));
328 
329  if (infoNeeded & NeedMountOptions) {
330  QString options = QFile::decodeName(MOUNTOPTIONS(fe));
331  mp->d->m_mountOptions = options.split(QLatin1Char(','));
332  }
333 
334  // Resolve gvfs mountpoints
335  if (mp->d->m_mountedFrom == QLatin1String("gvfsd-fuse")) {
336  const QDir gvfsDir(mp->d->m_mountPoint);
337  const QStringList mountDirs = gvfsDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
338  for (const QString &mountDir : mountDirs) {
339  const QString type = mountDir.section(QLatin1Char(':'), 0, 0);
340  if (type.isEmpty()) {
341  continue;
342  }
343 
344  Ptr gvfsmp(new KMountPoint);
345  gvfsmp->d->m_mountedFrom = mp->d->m_mountedFrom;
346  gvfsmp->d->m_mountPoint = mp->d->m_mountPoint + QLatin1Char('/') + mountDir;
347  gvfsmp->d->m_mountType = type;
348  result.append(gvfsmp);
349  }
350  }
351 
352  mp->d->finalizeCurrentMountPoint(infoNeeded);
353 
354  result.append(mp);
355  }
356  ENDMNTENT(mnttab);
357 #endif
358  return result;
359 }
360 
362 {
363  return d->m_mountedFrom;
364 }
365 
367 {
368  return d->m_device;
369 }
370 
372 {
373  return d->m_mountPoint;
374 }
375 
377 {
378  return d->m_mountType;
379 }
380 
382 {
383  return d->m_mountOptions;
384 }
385 
386 KMountPoint::List::List()
387  : QList<Ptr>()
388 {
389 }
390 
391 static bool pathsAreParentAndChildOrEqual(const QString &parent, const QString &child)
392 {
393  const QLatin1Char slash('/');
394  if (child.startsWith(parent, cs)) {
395  // Check if either
396  // (a) both paths are equal, or
397  // (b) parent ends with '/', or
398  // (c) the first character of child that is not shared with parent is '/'.
399  // Note that child is guaranteed to be longer than parent if (a) is false.
400  //
401  // This prevents that we incorrectly consider "/books" a child of "/book".
402  return parent.compare(child, cs) == 0 || parent.endsWith(slash) || child.at(parent.length()) == slash;
403  } else {
404  // Note that "/books" is a child of "/books/".
405  return parent.endsWith(slash) && (parent.length() == child.length() + 1) && parent.startsWith(child, cs);
406  }
407 }
408 
410 {
411 #ifndef Q_OS_WIN
412  /* If the path contains symlinks, get the real name */
413  QFileInfo fileinfo(path);
414  const QString realname = fileinfo.exists()
415  ? fileinfo.canonicalFilePath()
416  : fileinfo.absolutePath(); //canonicalFilePath won't work unless file exists
417 #else
418  const QString realname = QDir::fromNativeSeparators(QDir(path).absolutePath());
419 #endif
420 
421  int max = 0;
422  KMountPoint::Ptr result;
423  for (const KMountPoint::Ptr &mp : *this) {
424  const QString mountpoint = mp->d->m_mountPoint;
425  const int length = mountpoint.length();
426  if (length > max && pathsAreParentAndChildOrEqual(mountpoint, realname)) {
427  max = length;
428  result = mp;
429  // keep iterating to check for a better match (bigger max)
430  }
431  }
432  return result;
433 }
434 
436 {
437  const QString realDevice = QFileInfo(device).canonicalFilePath();
438  if (realDevice.isEmpty()) { // d->m_device can be empty in the loop below, don't match empty with it
439  return Ptr();
440  }
441  for (const KMountPoint::Ptr &mountPoint : *this) {
442  if (realDevice.compare(mountPoint->d->m_device, cs) == 0 ||
443  realDevice.compare(mountPoint->d->m_mountedFrom, cs) == 0) {
444  return mountPoint;
445  }
446  }
447  return Ptr();
448 }
449 
451 {
452  return d->m_mountType == QLatin1String("nfs")
453  || d->m_mountType == QLatin1String("nfs4")
454  || d->m_mountType == QLatin1String("cifs")
455  || d->m_mountType == QLatin1String("autofs")
456  || d->m_mountType == QLatin1String("subfs")
457  // Technically KIOFUSe mounts local slaves as well,
458  // such as recents:/, but better safe than sorry...
459  || d->m_mountType == QLatin1String("fuse.kio-fuse");
460 }
461 
462 bool KMountPoint::testFileSystemFlag(FileSystemFlag flag) const
463 {
464  const bool isMsDos = (d->m_mountType == QLatin1String("msdos") || d->m_mountType == QLatin1String("fat") || d->m_mountType == QLatin1String("vfat"));
465  const bool isNtfs = d->m_mountType.contains(QLatin1String("fuse.ntfs")) || d->m_mountType.contains(QLatin1String("fuseblk.ntfs"))
466  // fuseblk could really be anything. But its most common use is for NTFS mounts, these days.
467  || d->m_mountType == QLatin1String("fuseblk");
468  const bool isSmb = d->m_mountType == QLatin1String("cifs")
469  || d->m_mountType == QLatin1String("smbfs")
470  // gvfs-fuse mounted SMB share
471  || d->m_mountType == QLatin1String("smb-share");
472 
473  switch (flag) {
474  case SupportsChmod:
475  case SupportsChown:
476  case SupportsUTime:
477  case SupportsSymlinks:
478  return !isMsDos && !isNtfs && !isSmb; // it's amazing the number of things Microsoft filesystems don't support :)
479  case CaseInsensitive:
480  return isMsDos;
481  }
482  return false;
483 }
484 
bool probablySlow() const
Checks if the filesystem that is probably slow (network mounts).
The KMountPoint class provides information about mounted and unmounted disks.
Definition: kmountpoint.h:25
QString fromNativeSeparators(const QString &pathName)
QString readLine(qint64 maxlen)
static List currentMountPoints(DetailsNeededFlags infoNeeded=BasicInfoNeeded)
This function gives a list of all currently used mountpoints.
QString mountedFrom() const
Where this filesystem gets mounted from.
~KMountPoint()
Destructor.
Definition: kmountpoint.cpp:97
QString realDeviceName() const
Canonical name of the device where the filesystem got mounted from.
Ptr findByDevice(const QString &device) const
Returns the mount point associated with device, i.e.
QString simplified() const const
bool exists() const const
void chop(int n)
QString canonicalFilePath() const const
int count(const T &value) const const
void append(const T &value)
bool atEnd() const const
CaseSensitivity
QString symLinkTarget() const const
QString mountType() const
Type of filesystem.
bool isEmpty() const const
bool startsWith(const QString &s, Qt::CaseSensitivity cs) const const
QStringList split(const QString &sep, QString::SplitBehavior behavior, Qt::CaseSensitivity cs) const const
bool endsWith(const QString &s, Qt::CaseSensitivity cs) const const
virtual bool open(QIODevice::OpenMode mode) override
bool testFileSystemFlag(FileSystemFlag flag) const
Checks the capabilities of the filesystem.
bool exists() const const
QStringRef midRef(int position, int n) const const
QString mountPoint() const
Path where the filesystem is mounted or can be mounted.
QStringList mountOptions() const
Options used to mount the filesystem.
static List possibleMountPoints(DetailsNeededFlags infoNeeded=BasicInfoNeeded)
This function gives a list of all possible mountpoints.
virtual void close() override
Ptr findByPath(const QString &path) const
Find the mountpoint on which resides path For instance if /home is a separate partition, findByPath("/home/user/blah") will return /home.
QStringList entryList(QDir::Filters filters, QDir::SortFlags sort) const const
const QChar at(int position) const const
int length() const const
List of mount points.
Definition: kmountpoint.h:32
QString absolutePath() const const
QObject * parent() const const
int compare(const QString &other, Qt::CaseSensitivity cs) const const
QString decodeName(const QByteArray &localFileName)
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Fri Dec 4 2020 23:01:36 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.