kdelibs/kdecore/io/kmountpoint.cpp
Ivailo Monev 29a8459798 generic: remove checks for system headers that should be present
Signed-off-by: Ivailo Monev <xakepa10@gmail.com>
2022-09-23 15:42:09 +03:00

454 lines
13 KiB
C++

/*
* This file is part of the KDE libraries
* Copyright (c) 2003 Waldo Bastian <bastian@kde.org>
* 2007 David Faure <faure@kde.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License version 2 as published by the Free Software Foundation.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "kmountpoint.h"
#include <config.h>
#include <stdlib.h>
#include <QtCore/QFileInfo>
#include <QtCore/QTextStream>
#include "kstandarddirs.h"
static Qt::CaseSensitivity cs = Qt::CaseSensitive;
#ifdef HAVE_PATHS_H
#include <paths.h> // for _PATH_MOUNTED
#endif
#ifdef HAVE_SYS_MNTTAB_H
#include <sys/mnttab.h>
#endif
#ifdef HAVE_MNTENT_H
#include <mntent.h>
#elif defined(HAVE_SYS_MNTENT_H)
#include <sys/mntent.h>
#endif
// This is the *BSD branch
#ifdef HAVE_SYS_MOUNT_H
#include <sys/types.h>
#ifdef HAVE_SYS_PARAM_H
#include <sys/param.h>
#endif
#include <sys/mount.h>
#endif
#ifdef HAVE_FSTAB_H
#include <fstab.h>
#endif
#ifndef HAVE_GETMNTINFO
# ifdef _PATH_MOUNTED
// On some Linux, MNTTAB points to /etc/fstab !
# undef MNTTAB
# define MNTTAB _PATH_MOUNTED
# else
# ifndef MNTTAB
# ifdef MTAB_FILE
# define MNTTAB MTAB_FILE
# else
# define MNTTAB "/etc/mnttab"
# endif
# endif
# endif
#endif
#include "kdebug.h"
#ifdef Q_OS_SOLARIS
#define FSTAB "/etc/vfstab"
#else
#define FSTAB "/etc/fstab"
#endif
class KMountPoint::Private {
public:
void finalizePossibleMountPoint(DetailsNeededFlags infoNeeded);
void finalizeCurrentMountPoint(DetailsNeededFlags infoNeeded);
QString mountedFrom;
QString device; // Only available when the NeedRealDeviceName flag was set.
QString mountPoint;
QString mountType;
QStringList mountOptions;
};
KMountPoint::KMountPoint()
:d( new Private )
{
}
KMountPoint::~KMountPoint()
{
delete d;
}
// There are (at least) four kind of APIs:
// setmntent + getmntent + struct mntent (linux...)
// getmntent + struct mnttab
// mntctl + struct vmount (AIX)
// getmntinfo + struct statfs&flags (BSD 4.4 and friends)
// getfsent + char* (BSD 4.3 and friends)
#ifdef HAVE_SETMNTENT
#define SETMNTENT setmntent
#define ENDMNTENT endmntent
#define STRUCT_MNTENT struct mntent *
#define STRUCT_SETMNTENT FILE *
#define GETMNTENT(file, var) ((var = getmntent(file)) != 0)
#define MOUNTPOINT(var) var->mnt_dir
#define MOUNTTYPE(var) var->mnt_type
#define MOUNTOPTIONS(var) var->mnt_opts
#define FSNAME(var) var->mnt_fsname
#else
#define SETMNTENT fopen
#define ENDMNTENT fclose
#define STRUCT_MNTENT struct mnttab
#define STRUCT_SETMNTENT FILE *
#define GETMNTENT(file, var) (getmntent(file, &var) == 0)
#define MOUNTPOINT(var) var.mnt_mountp
#define MOUNTTYPE(var) var.mnt_fstype
#define MOUNTOPTIONS(var) var.mnt_mntopts
#define FSNAME(var) var.mnt_special
#endif
/**
* When using supermount, the device name is in the options field
* as dev=/my/device
*/
static QString devNameFromOptions(const QStringList &options)
{
// Search options to find the device name
for ( QStringList::ConstIterator it = options.begin(); it != options.end(); ++it)
{
if( (*it).startsWith(QLatin1String("dev=")))
return (*it).mid(4);
}
return QString::fromLatin1("none");
}
void KMountPoint::Private::finalizePossibleMountPoint(DetailsNeededFlags infoNeeded)
{
if (mountType == QLatin1String("supermount")) {
mountedFrom = devNameFromOptions(mountOptions);
}
if (mountedFrom.startsWith(QLatin1String("UUID="))) {
const QString uuid = mountedFrom.mid(5);
const QString potentialDevice = QFile::readLink(QString::fromLatin1("/dev/disk/by-uuid/") + uuid);
if (QFileInfo(potentialDevice).exists()) {
mountedFrom = potentialDevice;
}
}
if (mountedFrom.startsWith(QLatin1String("LABEL="))) {
const QString label = mountedFrom.mid(6);
const QString potentialDevice = QFile::readLink(QString::fromLatin1("/dev/disk/by-label/") + label);
if (QFileInfo(potentialDevice).exists()) {
mountedFrom = potentialDevice;
}
}
if (infoNeeded & NeedRealDeviceName) {
if (mountedFrom.startsWith(QLatin1Char('/')))
device = KStandardDirs::realFilePath(mountedFrom);
}
// TODO: Strip trailing '/' ?
}
void KMountPoint::Private::finalizeCurrentMountPoint(DetailsNeededFlags infoNeeded)
{
if (infoNeeded & NeedRealDeviceName) {
if (mountedFrom.startsWith(QLatin1Char('/')))
device = KStandardDirs::realFilePath(mountedFrom);
}
}
KMountPoint::List KMountPoint::possibleMountPoints(DetailsNeededFlags infoNeeded)
{
KMountPoint::List result;
#ifdef HAVE_SETMNTENT
STRUCT_SETMNTENT fstab;
if ((fstab = SETMNTENT(FSTAB, "r")) == 0)
return result;
STRUCT_MNTENT fe;
while (GETMNTENT(fstab, fe))
{
Ptr mp(new KMountPoint);
mp->d->mountedFrom = QFile::decodeName(FSNAME(fe));
mp->d->mountPoint = QFile::decodeName(MOUNTPOINT(fe));
mp->d->mountType = QFile::decodeName(MOUNTTYPE(fe));
//Devices using supermount have their device names in the mount options
//instead of the device field. That's why we need to read the mount options
if (infoNeeded & NeedMountOptions || (mp->d->mountType == QLatin1String("supermount")))
{
QString options = QFile::decodeName(MOUNTOPTIONS(fe));
mp->d->mountOptions = options.split( QLatin1Char(',') );
}
mp->d->finalizePossibleMountPoint(infoNeeded);
result.append(mp);
}
ENDMNTENT(fstab);
#else
QFile f(QLatin1String(FSTAB));
if ( !f.open(QIODevice::ReadOnly) )
return result;
QTextStream t (&f);
QString s;
while (! t.atEnd())
{
s=t.readLine().simplified();
if ( s.isEmpty() || (s[0] == QLatin1Char('#')))
continue;
// not empty or commented out by '#'
const QStringList item = s.split(QLatin1Char(' '));
#ifdef Q_OS_SOLARIS
if (item.count() < 5)
continue;
#else
if (item.count() < 4)
continue;
#endif
Ptr mp(new KMountPoint);
int i = 0;
mp->d->mountedFrom = item[i++];
#ifdef Q_OS_SOLARIS
//device to fsck
i++;
#endif
mp->d->mountPoint = item[i++];
mp->d->mountType = item[i++];
QString options = item[i++];
if (infoNeeded & NeedMountOptions)
{
mp->d->mountOptions = options.split(QLatin1Char(','));
}
mp->d->finalizePossibleMountPoint(infoNeeded);
result.append(mp);
} //while
f.close();
#endif
return result;
}
KMountPoint::List KMountPoint::currentMountPoints(DetailsNeededFlags infoNeeded)
{
KMountPoint::List result;
#ifdef HAVE_GETMNTINFO
#ifdef GETMNTINFO_USES_STATVFS
struct statvfs *mounted;
#else
struct statfs *mounted;
#endif
int num_fs = getmntinfo(&mounted, MNT_NOWAIT);
for (int i=0;i< num_fs;i++)
{
Ptr mp(new KMountPoint);
mp->d->mountedFrom = QFile::decodeName(mounted[i].f_mntfromname);
mp->d->mountPoint = QFile::decodeName(mounted[i].f_mntonname);
mp->d->mountType = QFile::decodeName(mounted[i].f_fstypename);
if (infoNeeded & NeedMountOptions)
{
struct fstab *ft = getfsfile(mounted[i].f_mntonname);
if (ft != 0) {
QString options = QFile::decodeName(ft->fs_mntops);
mp->d->mountOptions = options.split(QLatin1Char(','));
} else {
// TODO: get mount options if not mounted via fstab, see mounted[i].f_flags
}
}
mp->d->finalizeCurrentMountPoint(infoNeeded);
// TODO: Strip trailing '/' ?
result.append(mp);
}
#else
STRUCT_SETMNTENT mnttab;
if ((mnttab = SETMNTENT(MNTTAB, "r")) == 0)
return result;
STRUCT_MNTENT fe;
while (GETMNTENT(mnttab, fe))
{
Ptr mp(new KMountPoint);
mp->d->mountedFrom = QFile::decodeName(FSNAME(fe));
mp->d->mountPoint = QFile::decodeName(MOUNTPOINT(fe));
mp->d->mountType = QFile::decodeName(MOUNTTYPE(fe));
//Devices using supermount have their device names in the mount options
//instead of the device field. That's why we need to read the mount options
if (infoNeeded & NeedMountOptions || (mp->d->mountType == QLatin1String("supermount")))
{
QString options = QFile::decodeName(MOUNTOPTIONS(fe));
mp->d->mountOptions = options.split( QLatin1Char(',') );
}
mp->d->finalizeCurrentMountPoint(infoNeeded);
result.append(mp);
}
ENDMNTENT(mnttab);
#endif
return result;
}
QString KMountPoint::mountedFrom() const
{
return d->mountedFrom;
}
QString KMountPoint::realDeviceName() const
{
return d->device;
}
QString KMountPoint::mountPoint() const
{
return d->mountPoint;
}
QString KMountPoint::mountType() const
{
return d->mountType;
}
QStringList KMountPoint::mountOptions() const
{
return d->mountOptions;
}
KMountPoint::List::List()
: QList<Ptr>()
{
}
static bool pathsAreParentAndChildOrEqual(const QString& parent, const QString& child)
{
const QLatin1Char slash('/');
if (child.startsWith(parent, cs)) {
// Check if either
// (a) both paths are equal, or
// (b) parent ends with '/', or
// (c) the first character of child that is not shared with parent is '/'.
// Note that child is guaranteed to be longer than parent if (a) is false.
//
// This prevents that we incorrectly consider "/books" a child of "/book".
return parent.compare(child, cs) == 0 || parent.endsWith(slash) || child.at(parent.length()) == slash;
} else {
// Note that "/books" is a child of "/books/".
return parent.endsWith(slash) && (parent.length() == child.length() + 1) && parent.startsWith(child, cs);
}
}
KMountPoint::Ptr KMountPoint::List::findByPath(const QString& path) const
{
/* If the path contains symlinks, get the real name */
const QString realname = KStandardDirs::realFilePath(path);
int max = 0;
KMountPoint::Ptr result;
for (const_iterator it = begin(); it != end(); ++it) {
const QString mountpoint = (*it)->d->mountPoint;
const int length = mountpoint.length();
if (length > max && pathsAreParentAndChildOrEqual(mountpoint, realname)) {
max = length;
result = *it;
// keep iterating to check for a better match (bigger max)
}
}
return result;
}
KMountPoint::Ptr KMountPoint::List::findByDevice(const QString& device) const
{
const QString realDevice = KStandardDirs::realFilePath(device);
if (realDevice.isEmpty()) // d->device can be empty in the loop below, don't match empty with it
return Ptr();
for (const_iterator it = begin(); it != end(); ++it) {
if (realDevice.compare((*it)->d->device, cs) == 0 ||
realDevice.compare((*it)->d->mountedFrom, cs) == 0)
return *it;
}
return Ptr();
}
bool KMountPoint::probablySlow() const
{
bool nfs = d->mountType == QLatin1String("nfs");
bool cifs = d->mountType == QLatin1String("cifs");
bool autofs = d->mountType == QLatin1String("autofs") || d->mountType == QLatin1String("subfs");
//bool pid = d->mountPoint.contains(":(pid");
// The "pid" thing was in kde3's KIO::probably_slow_mounted, with obscure logic
// (looks like it used state from the previous line or something...)
// This needs to be revised once we have a testcase or explanation about it.
// But autofs works already, it shows nfs as mountType in mtab.
if (nfs || autofs || cifs) {
return true;
}
return false;
}
bool KMountPoint::testFileSystemFlag(FileSystemFlag flag) const
{
const bool isMsDos = ( d->mountType == QLatin1String("msdos") || d->mountType == QLatin1String("fat") || d->mountType == QLatin1String("vfat") );
const bool isNtfs = d->mountType.contains(QLatin1String("fuse.ntfs")) || d->mountType.contains(QLatin1String("fuseblk.ntfs"))
// fuseblk could really be anything. But its most common use is for NTFS mounts, these days.
|| d->mountType == QLatin1String("fuseblk");
const bool isSmb = d->mountType == QLatin1String("cifs") || d->mountType == QLatin1String("smbfs");
switch (flag) {
case SupportsChmod:
case SupportsChown:
case SupportsUTime:
case SupportsSymlinks:
return !isMsDos && !isNtfs && !isSmb; // it's amazing the number of things Microsoft filesystems don't support :)
case CaseInsensitive:
return isMsDos;
}
return false;
}