mirror of
https://bitbucket.org/smil3y/kdelibs.git
synced 2025-02-23 10:22:48 +00:00
454 lines
13 KiB
C++
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;
|
|
}
|
|
|