diff --git a/krusader/MountMan/kmountman.cpp b/krusader/MountMan/kmountman.cpp index c227ccdf..7616ceb7 100644 --- a/krusader/MountMan/kmountman.cpp +++ b/krusader/MountMan/kmountman.cpp @@ -1,500 +1,465 @@ /***************************************************************************** * Copyright (C) 2000 Shie Erlich * * Copyright (C) 2000 Rafi Yanai * * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 2 of the License, or * * (at your option) any later version. * * * * Krusader 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 General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #include "kmountman.h" // QtCore #include // QtWidgets #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../krglobal.h" #include "../icon.h" #include "../kractions.h" #include "../defaults.h" #include "../Dialogs/krdialogs.h" #include "../krservices.h" #include "kmountmangui.h" #include "../FileSystem/krpermhandler.h" #ifdef _OS_SOLARIS_ #define FSTAB "/etc/filesystemtab" #else #define FSTAB "/etc/fstab" #endif KMountMan::KMountMan(QWidget *parent) : QObject(), _operational(false), waiting(false), mountManGui(0), parentWindow(parent) { _action = new KToolBarPopupAction(Icon("kr_mountman"), i18n("&MountMan..."), this); connect(_action, &QAction::triggered, this, &KMountMan::mainWindow); connect(_action->menu(), &QMenu::aboutToShow, this, &KMountMan::quickList); _manageAction = _action->menu()->addAction(i18n("Open &MountMan")); connect(_manageAction, &QAction::triggered, this, &KMountMan::mainWindow); _action->menu()->addSeparator(); // added as a precaution, although we use kde services now _operational = KrServices::cmdExist("mount"); network_fs << "nfs" << "smbfs" << "fuse.fusesmb" << "fuse.sshfs"; //TODO: is this list complete ? // list of FS that we don't manage at all invalid_fs << "swap" << "/dev/pts" << "tmpfs" << "devpts" << "sysfs" << "rpc_pipefs" << "usbfs" << "binfmt_misc"; #ifdef BSD invalid_fs << "procfs"; #else invalid_fs << "proc"; #endif // list of FS that we don't allow to mount/unmount nonmount_fs << "supermount"; { KConfigGroup group(krConfig, "Advanced"); QStringList nonmount = group.readEntry("Nonmount Points", _NonMountPoints).split(','); nonmount_fs_mntpoint += nonmount; // simplify the white space for (QStringList::Iterator it = nonmount_fs_mntpoint.begin(); it != nonmount_fs_mntpoint.end(); ++it) { *it = (*it).simplified(); } } } KMountMan::~KMountMan() {} bool KMountMan::invalidFilesystem(QString type) { return (invalid_fs.contains(type) > 0); } // this is an ugly hack, but type can actually be a mountpoint. oh well... bool KMountMan::nonmountFilesystem(QString type, QString mntPoint) { return((nonmount_fs.contains(type) > 0) || (nonmount_fs_mntpoint.contains(mntPoint) > 0)); } bool KMountMan::networkFilesystem(QString type) { return (network_fs.contains(type) > 0); } void KMountMan::mainWindow() { // left as a precaution, although we use kde's services now if (!KrServices::cmdExist("mount")) { KMessageBox::error(0, i18n("Cannot start 'mount'. Check the 'Dependencies' page in konfigurator.")); return; } mountManGui = new KMountManGUI(this); delete mountManGui; /* as KMountManGUI is modal, we can now delete it */ mountManGui = nullptr; /* for sanity */ } QExplicitlySharedDataPointer KMountMan::findInListByMntPoint(KMountPoint::List &lst, QString value) { if (value.length() > 1 && value.endsWith('/')) value = value.left(value.length() - 1); QExplicitlySharedDataPointer m; for (KMountPoint::List::iterator it = lst.begin(); it != lst.end(); ++it) { m = it->data(); QString mntPnt = m->mountPoint(); if (mntPnt.length() > 1 && mntPnt.endsWith('/')) mntPnt = mntPnt.left(mntPnt.length() - 1); if (mntPnt == value) return m; } return QExplicitlySharedDataPointer(); } void KMountMan::jobResult(KJob *job) { waiting = false; if (job->error()) job->uiDelegate()->showErrorMessage(); } void KMountMan::mount(QString mntPoint, bool blocking) { QString udi = findUdiForPath(mntPoint, Solid::DeviceInterface::StorageAccess); if (!udi.isNull()) { Solid::Device device(udi); Solid::StorageAccess *access = device.as(); if (access && !access->isAccessible()) { connect(access, &Solid::StorageAccess::setupDone, this, &KMountMan::slotSetupDone, Qt::UniqueConnection); if (blocking) waiting = true; // prepare to block access->setup(); } } else { KMountPoint::List possible = KMountPoint::possibleMountPoints(KMountPoint::NeedMountOptions); QExplicitlySharedDataPointer m = findInListByMntPoint(possible, mntPoint); if (!((bool)m)) return; if (blocking) waiting = true; // prepare to block // KDE4 doesn't allow mounting devices as user, because they think it's the right behaviour. // I add this patch, as I don't think so. if (geteuid()) { // tries to mount as an user? KProcess proc; proc << KrServices::fullPathName("mount") << mntPoint; proc.start(); if (!blocking) return; proc.waitForFinished(-1); // -1 msec blocks without timeout if (proc.exitStatus() == QProcess::NormalExit && proc.exitCode() == 0) return; } KIO::SimpleJob *job = KIO::mount(false, m->mountType().toLocal8Bit(), m->mountedFrom(), m->mountPoint(), KIO::DefaultFlags); job->setUiDelegate(new KIO::JobUiDelegate()); KIO::getJobTracker()->registerJob(job); connect(job, SIGNAL(result(KJob*)), this, SLOT(jobResult(KJob*))); } while (blocking && waiting) { qApp->processEvents(); usleep(1000); } } void KMountMan::unmount(QString mntPoint, bool blocking) { //if working dir is below mountpoint cd to ~ first if(QUrl::fromLocalFile(QDir(mntPoint).canonicalPath()).isParentOf(QUrl::fromLocalFile(QDir::current().canonicalPath()))) QDir::setCurrent(QDir::homePath()); QString udi = findUdiForPath(mntPoint, Solid::DeviceInterface::StorageAccess); if (!udi.isNull()) { Solid::Device device(udi); Solid::StorageAccess *access = device.as(); if (access && access->isAccessible()) { connect(access, &Solid::StorageAccess::teardownDone, this, &KMountMan::slotTeardownDone, Qt::UniqueConnection); access->teardown(); } } else { if (blocking) waiting = true; // prepare to block // KDE4 doesn't allow unmounting devices as user, because they think it's the right behaviour. // I add this patch, as I don't think so. if (geteuid()) { // tries to mount as an user? KProcess proc; proc << KrServices::fullPathName("umount") << mntPoint; proc.start(); if (!blocking) return; proc.waitForFinished(-1); // -1 msec blocks without timeout if (proc.exitStatus() == QProcess::NormalExit && proc.exitCode() == 0) return; } KIO::SimpleJob *job = KIO::unmount(mntPoint, KIO::DefaultFlags); job->setUiDelegate(new KIO::JobUiDelegate()); KIO::getJobTracker()->registerJob(job); connect(job, SIGNAL(result(KJob*)), this, SLOT(jobResult(KJob*))); } while (blocking && waiting) { qApp->processEvents(); usleep(1000); } } KMountMan::mntStatus KMountMan::getStatus(QString mntPoint) { QExplicitlySharedDataPointer mountPoint; // 1: is it already mounted KMountPoint::List current = KMountPoint::currentMountPoints(); mountPoint = findInListByMntPoint(current, mntPoint); if ((bool) mountPoint) return MOUNTED; // 2: is it a mount point but not mounted? KMountPoint::List possible = KMountPoint::possibleMountPoints(); mountPoint = findInListByMntPoint(possible, mntPoint); if ((bool) mountPoint) return NOT_MOUNTED; // 3: unknown return DOESNT_EXIST; } void KMountMan::toggleMount(QString mntPoint) { mntStatus status = getStatus(mntPoint); switch (status) { case MOUNTED: unmount(mntPoint); break; case NOT_MOUNTED: mount(mntPoint); break; case DOESNT_EXIST: // do nothing: no-op to make the compiler quiet ;-) break; } } void KMountMan::autoMount(QString path) { KConfigGroup group(krConfig, "Advanced"); if (!group.readEntry("AutoMount", _AutoMount)) return; // auto mount disabled if (getStatus(path) == NOT_MOUNTED) mount(path); } void KMountMan::eject(QString mntPoint) { QString udi = findUdiForPath(mntPoint, Solid::DeviceInterface::OpticalDrive); if (udi.isNull()) return; Solid::Device dev(udi); Solid::OpticalDrive *drive = dev.as(); if (drive == 0) return; //if working dir is below mountpoint cd to ~ first if(QUrl::fromLocalFile(QDir(mntPoint).canonicalPath()).isParentOf(QUrl::fromLocalFile(QDir::current().canonicalPath()))) QDir::setCurrent(QDir::homePath()); connect(drive, SIGNAL(ejectDone(Solid::ErrorType,QVariant,QString)), this, SLOT(slotTeardownDone(Solid::ErrorType,QVariant,QString))); drive->eject(); } // returns true if the path is an ejectable mount point (at the moment CDROM and DVD) bool KMountMan::ejectable(QString path) { QString udi = findUdiForPath(path, Solid::DeviceInterface::OpticalDisc); if (udi.isNull()) return false; Solid::Device dev(udi); return dev.as() != 0; } bool KMountMan::removable(QString path) { QString udi = findUdiForPath(path, Solid::DeviceInterface::StorageAccess); if (udi.isNull()) return false; return removable(Solid::Device(udi)); } bool KMountMan::removable(Solid::Device d) { if(!d.isValid()) return false; Solid::StorageDrive *drive = d.as(); if(drive) return drive->isRemovable(); else return(removable(d.parent())); } -// a mountMan special version of KIO::convertSize, which deals -// with large filesystems ==> > 4GB, it actually receives size in -// a minimum block of 1024 ==> data is KB not bytes -QString KMountMan::convertSize(KIO::filesize_t size) -{ - float fsize; - QString s; - QLocale loc; - // Tera-byte - if (size >= 1073741824) { - fsize = (float) size / (float) 1073741824; - if (fsize > 1024) // no name for something bigger than tera byte - // let's call it Zega-Byte, who'll ever find out? :-) - s = i18n("%1 ZB", loc.toString(fsize / (float) 1024, 'f', 1)); - else - s = i18n("%1 TB", loc.toString(fsize, 'f', 1)); - } - // Giga-byte - else if (size >= 1048576) { - fsize = (float) size / (float) 1048576; - s = i18n("%1 GB", loc.toString(fsize, 'f', 1)); - } - // Mega-byte - else if (size > 1024) { - fsize = (float) size / (float) 1024; - s = i18n("%1 MB", loc.toString(fsize, 'f', 1)); - } - // Kilo-byte - else { - fsize = (float) size; - s = i18n("%1 KB", loc.toString(fsize, 'f', 0)); - } - return s; -} - // populate the pop-up menu of the mountman tool-button with actions void KMountMan::quickList() { if (!_operational) { KMessageBox::error(0, i18n("MountMan is not operational. Sorry")); return; } // clear mount / unmount actions for (QAction *action : _action->menu()->actions()) { if (action == _manageAction || action->isSeparator()) { continue; } _action->menu()->removeAction(action); } // create lists of current and possible mount points const KMountPoint::List currentMountPoints = KMountPoint::currentMountPoints(); // create a menu, displaying mountpoints with possible actions for (QExplicitlySharedDataPointer possibleMountPoint : KMountPoint::possibleMountPoints()) { // skip nonmountable file systems if (nonmountFilesystem(possibleMountPoint->mountType(), possibleMountPoint->mountPoint()) || invalidFilesystem(possibleMountPoint->mountType())) { continue; } // does the mountpoint exist in current list? // if so, it can only be umounted, otherwise, it can be mounted bool needUmount = false; for (QExplicitlySharedDataPointer currentMountPoint : currentMountPoints) { if (currentMountPoint->mountPoint() == possibleMountPoint->mountPoint()) { // found -> needs umount needUmount = true; break; } } // add the item to the menu const QString text = QString("%1 %2 (%3)").arg(needUmount ? i18n("Unmount") : i18n("Mount"), possibleMountPoint->mountPoint(), possibleMountPoint->mountedFrom()); QAction *act = _action->menu()->addAction(text); act->setData(QList({ QVariant(needUmount ? KMountMan::ActionType::Unmount : KMountMan::ActionType::Mount), QVariant(possibleMountPoint->mountPoint()) })); } connect(_action->menu(), &QMenu::triggered, this, &KMountMan::delayedPerformAction); } void KMountMan::delayedPerformAction(const QAction *action) { if (!action || !action->data().canConvert>()) { return; } disconnect(_action->menu(), &QMenu::triggered, 0, 0); const QList actData = action->data().toList(); const int actionType = actData[0].toInt(); const QString mountPoint = actData[1].toString(); QTimer::singleShot(0, this, [=] { if (actionType == KMountMan::ActionType::Mount) { mount(mountPoint); } else { unmount(mountPoint); } }); } QString KMountMan::findUdiForPath(QString path, const Solid::DeviceInterface::Type &expType) { KMountPoint::List current = KMountPoint::currentMountPoints(); KMountPoint::List possible = KMountPoint::possibleMountPoints(); QExplicitlySharedDataPointer mp = findInListByMntPoint(current, path); if (!(bool)mp) { mp = findInListByMntPoint(possible, path); if (!(bool)mp) return QString(); } QString dev = QDir(mp->mountedFrom()).canonicalPath(); QList storageDevices = Solid::Device::listFromType(Solid::DeviceInterface::Block); for (int p = storageDevices.count() - 1 ; p >= 0; p--) { Solid::Device device = storageDevices[ p ]; QString udi = device.udi(); Solid::Block * sb = device.as(); if (sb) { QString devb = QDir(sb->device()).canonicalPath(); if (expType != Solid::DeviceInterface::Unknown && !device.isDeviceInterface(expType)) continue; if (devb == dev) return udi; } } return QString(); } QString KMountMan::pathForUdi(QString udi) { Solid::Device device(udi); Solid::StorageAccess *access = device.as(); if(access) return access->filePath(); else return QString(); } void KMountMan::slotTeardownDone(Solid::ErrorType error, QVariant errorData, const QString& /*udi*/) { waiting = false; if (error != Solid::NoError && errorData.isValid()) { KMessageBox::sorry(parentWindow, errorData.toString()); } } void KMountMan::slotSetupDone(Solid::ErrorType error, QVariant errorData, const QString& /*udi*/) { waiting = false; if (error != Solid::NoError && errorData.isValid()) { KMessageBox::sorry(parentWindow, errorData.toString()); } } diff --git a/krusader/MountMan/kmountman.h b/krusader/MountMan/kmountman.h index 559589ac..63744100 100644 --- a/krusader/MountMan/kmountman.h +++ b/krusader/MountMan/kmountman.h @@ -1,118 +1,117 @@ /***************************************************************************** * Copyright (C) 2000 Shie Erlich * * Copyright (C) 2000 Rafi Yanai * * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 2 of the License, or * * (at your option) any later version. * * * * Krusader 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 General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #ifndef KMOUNTMAN_H #define KMOUNTMAN_H // QtCore #include #include #include #include #include // QtWidgets #include #include #include #include #include #include #include class KMountManGUI; class KToolBarPopupAction; class KMountMan : public QObject { Q_OBJECT friend class KMountManGUI; public: enum mntStatus {DOESNT_EXIST, NOT_MOUNTED, MOUNTED}; inline bool operational() { return _operational; } // check this 1st void mount(QString mntPoint, bool blocking = true); // this is probably what you need for mount void unmount(QString mntPoint, bool blocking = true); // this is probably what you need for unmount mntStatus getStatus(QString mntPoint); // return the status of a mntPoint (if any) void eject(QString mntPoint); bool ejectable(QString path); bool removable(QString path); bool removable(Solid::Device d); - QString convertSize(KIO::filesize_t size); bool invalidFilesystem(QString type); bool networkFilesystem(QString type); bool nonmountFilesystem(QString type, QString mntPoint); QAction *action() { return (QAction *) _action; } explicit KMountMan(QWidget *parent); ~KMountMan(); // NOTE: this function needs some time (~50msec) QString findUdiForPath(QString path, const Solid::DeviceInterface::Type &expType = Solid::DeviceInterface::Unknown); QString pathForUdi(QString udi); public slots: void mainWindow(); // opens up the GUI void autoMount(QString path); // just call it before refreshing into a dir void delayedPerformAction(const QAction *action); void quickList(); protected slots: void jobResult(KJob *job); void slotTeardownDone(Solid::ErrorType error, QVariant errorData, const QString &udi); void slotSetupDone(Solid::ErrorType error, QVariant errorData, const QString &udi); protected: // used internally static QExplicitlySharedDataPointer findInListByMntPoint(KMountPoint::List &lst, QString value); void toggleMount(QString mntPoint); void emitRefreshPanel(const QUrl &url) { emit refreshPanel(url); } signals: void refreshPanel(const QUrl &); private: enum ActionType { Mount, Unmount }; KToolBarPopupAction *_action; QAction *_manageAction; bool _operational; // if false, something went terribly wrong on startup bool waiting; // used to block krusader while waiting for (un)mount operation KMountManGUI *mountManGui; // the following is the FS type QStringList invalid_fs; QStringList nonmount_fs; QStringList network_fs; // the following is the FS name QStringList nonmount_fs_mntpoint; QPointer parentWindow; }; #endif