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 }