File indexing completed on 2024-12-01 13:48:42

0001 /*
0002    SPDX-FileCopyrightText: 2019-2020 Fabian Vogt <fabian@ritter-vogt.de>
0003    SPDX-FileCopyrightText: 2019-2020 Alexander Saoutkin <a.saoutkin@gmail.com>
0004    SPDX-License-Identifier: GPL-3.0-or-later
0005 */
0006 
0007 #include <unistd.h>
0008 #include <string.h>
0009 #include <sys/wait.h>
0010 
0011 #include <QDBusConnection>
0012 #include <QStandardPaths>
0013 #include <QDir>
0014 
0015 #include "debug.h"
0016 #include "kiofuseservice.h"
0017 #include "kiofusevfs.h"
0018 
0019 const QStringList KIOFuseService::m_blacklist {
0020     QStringLiteral("gdrive"),  // @see #1
0021     QStringLiteral("mtp"), // @see #2
0022     // http(s) is buggy and gives back invalid sizes (similar to gdrive).
0023     QStringLiteral("https"),
0024     QStringLiteral("http"),
0025     QStringLiteral("admin"),
0026 };
0027 
0028 KIOFuseService::~KIOFuseService()
0029 {
0030     // Make sure the VFS is unmounted before the member destructors run.
0031     // Any access to the mountpoint would deadlock.
0032     kiofusevfs.stop();
0033 }
0034 
0035 bool KIOFuseService::start(struct fuse_args &args, const QString &mountpoint, bool foreground)
0036 {
0037     if(!m_mountpoint.isEmpty())
0038     {
0039         qWarning(KIOFUSE_LOG) << "Refusing to start already running KIOFuseService";
0040         return false;
0041     }
0042 
0043     if(mountpoint.isEmpty())
0044     {
0045         const QString runtimeloc = QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation);
0046         if(runtimeloc.isEmpty())
0047             return false;
0048 
0049         m_tempDir.emplace(runtimeloc + QStringLiteral("/kio-fuse-XXXXXX"));
0050         if(!m_tempDir.value().isValid())
0051             return false; // Abort if can't mkdir for some reason
0052 
0053         m_mountpoint = m_tempDir.value().path();
0054     }
0055     else
0056         // Don't do a mkdir here, we assume that any given mountpoint dir already exists.
0057         m_mountpoint = mountpoint;
0058 
0059     if(!kiofusevfs.start(args, m_mountpoint))
0060         return false;
0061 
0062     if(foreground)
0063         return registerService();
0064     else
0065         return registerServiceDaemonized();
0066 }
0067 
0068 void KIOFuseServicePrivate::forceNodeTimeout()
0069 {
0070     g_timeoutEpoch = std::chrono::steady_clock::now();
0071 }
0072 
0073 QString KIOFuseService::remoteUrl(const QString& localPath)
0074 {
0075     // Massage URL into something KIOFuseVFS may understand.
0076     QDir mountpoint(m_mountpoint);
0077     QString relativePath = mountpoint.relativeFilePath(localPath);
0078     // If relativePath is empty or starts with ../, this would get error out
0079     QUrl remoteUrl = kiofusevfs.localPathToRemoteUrl(relativePath);
0080 
0081     if(remoteUrl.isEmpty())
0082     {
0083         sendErrorReply(
0084             QStringLiteral("org.kde.KIOFuse.VFS.Error.RemoteURLNotFound"),
0085             QStringLiteral("The given path does not have a remote URL equivalent: %1").arg(localPath)
0086         );
0087         return QString();
0088     }
0089 
0090     return remoteUrl.toString(QUrl::RemovePassword);
0091 }
0092 
0093 void KIOFuseService::dbusDisconnected()
0094 {
0095     qInfo(KIOFUSE_LOG) << "DBus disconnected - stopping.";
0096     kiofusevfs.stop();
0097 }
0098 
0099 QString KIOFuseService::mountUrl(const QString& remoteUrl, const QDBusMessage& message)
0100 {
0101     message.setDelayedReply(true);
0102     QUrl url = QUrl::fromUserInput(remoteUrl);
0103     if(m_blacklist.contains(url.scheme()))
0104     {
0105         url.setPassword({}); // Lets not give back passwords in plaintext...
0106         auto errorReply = message.createErrorReply(
0107             QStringLiteral("org.kde.KIOFuse.VFS.Error.SchemeNotSupported"),
0108             QStringLiteral("KIOFuse does not suport mounting of URLs with a scheme of %1").arg(url.scheme())
0109         );
0110         QDBusConnection::sessionBus().send(errorReply);
0111         return QString();
0112     }
0113     kiofusevfs.mountUrl(url, [=] (auto path, int error) {
0114         if(error)
0115         {
0116             QUrl displayUrl = url;
0117             displayUrl.setPassword({}); // Lets not give back passwords in plaintext...
0118             auto errorReply = message.createErrorReply(
0119                 QStringLiteral("org.kde.KIOFuse.VFS.Error.CannotMount"),
0120                 QStringLiteral("KIOFuse failed to mount %1: %2").arg(displayUrl.toString(), QLatin1String(strerror(error)))
0121             );
0122             QDBusConnection::sessionBus().send(errorReply);
0123             return;
0124         }
0125 
0126         QString localPath = {m_mountpoint + QLatin1Char('/') + path};
0127         QDBusConnection::sessionBus().send(message.createReply() << localPath);
0128     });
0129     return QString();
0130 }
0131 
0132 bool KIOFuseService::registerService()
0133 {
0134     if(QDBusConnection::sessionBus().registerObject(QStringLiteral("/org/kde/KIOFuse"), this,
0135                                                         QDBusConnection::ExportAllSlots | QDBusConnection::ExportAdaptors)
0136         && QDBusConnection::sessionBus().registerService(QStringLiteral("org.kde.KIOFuse")))
0137     {
0138         QDBusConnection::sessionBus().connect({}, QStringLiteral("/org/freedesktop/DBus/Local"), QStringLiteral("org.freedesktop.DBus.Local"), QStringLiteral("Disconnected"), this, SLOT(dbusDisconnected()));
0139         return true;
0140     }
0141 
0142     return false;
0143 }
0144 
0145 bool KIOFuseService::registerServiceDaemonized()
0146 {
0147     int waiter[2];
0148     int result = 1;
0149 
0150     if(pipe(waiter)) {
0151         perror("kiofuse_daemonize: pipe");
0152         return false;
0153     }
0154 
0155     /*
0156     * demonize current process by forking it and killing the
0157     * parent.  This makes current process as a child of 'init'.
0158     */
0159     pid_t cpid = fork();
0160     switch(cpid) {
0161     case -1: // fork failed
0162         perror("kiofuse_daemonize: fork");
0163         return false;
0164     default: // Parent
0165         (void) read(waiter[0], &result, sizeof(result));
0166         if(result)
0167             waitpid(cpid, nullptr, 0);
0168         _exit(result);
0169     case 0: // Child
0170         break;
0171     }
0172 
0173     result = registerService() ? 0 : 1;
0174 
0175     if(setsid() == -1) {
0176         perror("kiofuse_daemonize: setsid");
0177         result = 1;
0178     }
0179 
0180     (void) chdir("/");
0181 
0182     /* Propagate completion of daemon initialization */
0183     (void) write(waiter[1], &result, sizeof(result));
0184     close(waiter[0]);
0185     close(waiter[1]);
0186 
0187     return result == 0;
0188 }