File indexing completed on 2021-12-23 10:45:08

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 <qglobal.h>
0008 
0009 #include <sys/types.h>
0010 #include <errno.h>
0011 #include <unistd.h>
0012 #include <signal.h>
0013 #include <chrono>
0014 
0015 #ifdef Q_OS_LINUX
0016 #include <linux/fs.h>
0017 #include <sys/utsname.h>
0018 #endif
0019 
0020 #include <QDateTime>
0021 #include <QDebug>
0022 #include <QDir>
0023 #include <QVersionNumber>
0024 
0025 #include <KIO/ListJob>
0026 #include <KIO/MkdirJob>
0027 #include <KIO/StatJob>
0028 #include <KIO/TransferJob>
0029 #include <KIO/DeleteJob>
0030 #include <KIO/FileJob>
0031 #include <KProtocolManager>
0032 
0033 #include "debug.h"
0034 #include "kiofusevfs.h"
0035 
0036 // Flags that don't exist on FreeBSD; since these are used as
0037 // bit(masks), setting them to 0 effectively means they're always unset.
0038 #ifndef O_NOATIME
0039 #define O_NOATIME 0
0040 #endif
0041 #ifndef RENAME_NOREPLACE
0042 #define RENAME_NOREPLACE 0
0043 #endif
0044 
0045 // The libfuse macros make this necessary
0046 #pragma GCC diagnostic ignored "-Wpedantic"
0047 #pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant"
0048 
0049 struct KIOFuseVFS::FuseLLOps : public fuse_lowlevel_ops
0050 {
0051     FuseLLOps()
0052     {
0053         init = &KIOFuseVFS::init;
0054         lookup = &KIOFuseVFS::lookup;
0055         forget = &KIOFuseVFS::forget;
0056         getattr = &KIOFuseVFS::getattr;
0057         setattr = &KIOFuseVFS::setattr;
0058         readlink = &KIOFuseVFS::readlink;
0059         mknod = &KIOFuseVFS::mknod;
0060         mkdir = &KIOFuseVFS::mkdir;
0061         unlink = &KIOFuseVFS::unlink;
0062         rmdir = &KIOFuseVFS::rmdir;
0063         symlink = &KIOFuseVFS::symlink;
0064         rename = &KIOFuseVFS::rename;
0065         open = &KIOFuseVFS::open;
0066         read = &KIOFuseVFS::read;
0067         write = &KIOFuseVFS::write;
0068         flush = &KIOFuseVFS::flush;
0069         release = &KIOFuseVFS::release;
0070         fsync = &KIOFuseVFS::fsync;
0071         opendir = &KIOFuseVFS::opendir;
0072         readdir = &KIOFuseVFS::readdir;
0073         releasedir = &KIOFuseVFS::releasedir;
0074     }
0075 };
0076 
0077 const struct KIOFuseVFS::FuseLLOps KIOFuseVFS::fuse_ll_ops;
0078 
0079 const std::chrono::steady_clock::duration KIOFuseRemoteNodeInfo::ATTR_TIMEOUT = std::chrono::seconds(30);
0080 std::chrono::steady_clock::time_point g_timeoutEpoch = {};
0081 
0082 /* Handles partial writes and EINTR.
0083  * Returns true only if count bytes were written successfully. */
0084 static bool sane_write(int fd, const void *buf, size_t count)
0085 {
0086     size_t bytes_left = count;
0087     const char *buf_left = (const char*)buf;
0088     while(bytes_left)
0089     {
0090         ssize_t step = write(fd, buf_left, bytes_left);
0091         if(step == -1)
0092         {
0093             if(errno == EINTR)
0094                 continue;
0095             else
0096                 return false;
0097         }
0098         else if(step == 0)
0099             return false;
0100 
0101         bytes_left -= step;
0102         buf_left += step;
0103     }
0104 
0105     return true;
0106 }
0107 
0108 /* Handles partial reads and EINTR.
0109  * Returns true only if count bytes were read successfully. */
0110 static bool sane_read(int fd, void *buf, size_t count)
0111 {
0112     size_t bytes_left = count;
0113     char *buf_left = (char*)buf;
0114     while(bytes_left)
0115     {
0116         ssize_t step = read(fd, buf_left, bytes_left);
0117         if(step == -1)
0118         {
0119             if(errno == EINTR)
0120                 continue;
0121             else
0122                 return false;
0123         }
0124         else if(step == 0)
0125             return false;
0126 
0127         bytes_left -= step;
0128         buf_left += step;
0129     }
0130 
0131     return true;
0132 }
0133 
0134 static bool operator <(const struct timespec &a, const struct timespec &b)
0135 {
0136     return (a.tv_sec == b.tv_sec) ? (a.tv_nsec < b.tv_nsec) : (a.tv_sec < b.tv_sec);
0137 }
0138 
0139 /** Returns whether the given name is valid for a file. */
0140 static bool isValidFilename(const QString &name)
0141 {
0142     return !name.isEmpty() && !name.contains(QLatin1Char('/'))
0143        && name != QStringLiteral(".") && name != QStringLiteral("..");
0144 }
0145 
0146 /** Returns url with members of pathElements appended to its path,
0147   * unless url is empty, then it's returned as-is. */
0148 static QUrl addPathElements(QUrl url, QStringList pathElements)
0149 {
0150     if(url.isEmpty())
0151         return url;
0152 
0153     if(!url.path().endsWith(QLatin1Char('/')) && !pathElements.isEmpty())
0154         pathElements.prepend({});
0155 
0156     url.setPath(url.path() + pathElements.join(QLatin1Char('/')));
0157     return url;
0158 }
0159 
0160 int KIOFuseVFS::signalFd[2];
0161 
0162 KIOFuseVFS::KIOFuseVFS(QObject *parent)
0163     : QObject(parent)
0164 {
0165     struct stat attr = {};
0166     fillStatForFile(attr);
0167     attr.st_mode = S_IFDIR | 0755;
0168 
0169     auto root = std::make_shared<KIOFuseDirNode>(KIOFuseIno::Invalid, QString(), attr);
0170     insertNode(root, KIOFuseIno::Root);
0171     incrementLookupCount(root, 1); // Implicitly referenced by mounting
0172 
0173     auto deletedRoot = std::make_shared<KIOFuseDirNode>(KIOFuseIno::Invalid, QString(), attr);
0174     insertNode(deletedRoot, KIOFuseIno::DeletedRoot);
0175 }
0176 
0177 KIOFuseVFS::~KIOFuseVFS()
0178 {
0179     stop();
0180 }
0181 
0182 bool KIOFuseVFS::start(struct fuse_args &args, const QString& mountpoint)
0183 {
0184     if(!isEnvironmentValid())
0185        return false;
0186 
0187     stop();
0188 
0189     m_fuseConnInfoOpts.reset(fuse_parse_conn_info_opts(&args));
0190     m_fuseSession = fuse_session_new(&args, &fuse_ll_ops, sizeof(fuse_ll_ops), this);
0191     m_mountpoint = QDir::cleanPath(mountpoint); // Remove redundant slashes
0192     if(!m_mountpoint.endsWith(QLatin1Char('/')))
0193         m_mountpoint += QLatin1Char('/');
0194 
0195     if(!m_fuseSession)
0196         return false;
0197 
0198     if(!setupSignalHandlers()
0199        || fuse_session_mount(m_fuseSession, m_mountpoint.toUtf8().data()) != 0)
0200     {
0201         stop();
0202         return false;
0203     }
0204 
0205     // Setup a notifier on the FUSE FD
0206     int fusefd = fuse_session_fd(m_fuseSession);
0207 
0208     // Set the FD to O_NONBLOCK so that it can be read in a loop until empty
0209     int flags = fcntl(fusefd, F_GETFL);
0210     fcntl(fusefd, F_SETFL, flags | O_NONBLOCK);
0211 
0212     m_fuseNotifier = std::make_unique<QSocketNotifier>(fusefd, QSocketNotifier::Read, this);
0213     m_fuseNotifier->connect(m_fuseNotifier.get(), &QSocketNotifier::activated, this, &KIOFuseVFS::fuseRequestPending);
0214 
0215     // Arm the QEventLoopLocker
0216     m_eventLoopLocker = std::make_unique<QEventLoopLocker>();
0217 
0218     return true;
0219 }
0220 
0221 void KIOFuseVFS::stop()
0222 {
0223     if(!m_fuseSession) // Not started or already (being) stopped. Avoids reentrancy.
0224         return;
0225 
0226     // Disable the QSocketNotifier
0227     m_fuseNotifier.reset();
0228 
0229     // Disarm the QEventLoopLocker
0230     m_eventLoopLocker.reset();
0231 
0232     fuse_session_unmount(m_fuseSession);
0233     fuse_session_destroy(m_fuseSession);
0234     m_fuseSession = nullptr;
0235 
0236     // Flush all dirty nodes
0237     QEventLoop loop;
0238     bool needEventLoop = false;
0239 
0240     for(auto it = m_dirtyNodes.begin(); it != m_dirtyNodes.end();)
0241     {
0242         auto node = std::dynamic_pointer_cast<KIOFuseRemoteCacheBasedFileNode>(nodeForIno(*it));
0243 
0244         ++it; // Increment now as awaitNodeFlushed invalidates the iterator
0245 
0246         if(!node || (!node->m_cacheDirty && !node->m_flushRunning))
0247         {
0248             qWarning(KIOFUSE_LOG) << "Broken inode in dirty set";
0249             continue;
0250         }
0251 
0252         auto lockerPointer = std::make_shared<QEventLoopLocker>(&loop);
0253         // Trigger or wait until flush done.
0254         awaitNodeFlushed(node, [lp = std::move(lockerPointer)](int) {});
0255 
0256         needEventLoop = true;
0257     }
0258 
0259     if(needEventLoop)
0260         loop.exec(); // Wait until all QEventLoopLockers got destroyed
0261 
0262     // FIXME: If a signal arrives after this, it would be fatal.
0263     removeSignalHandlers();
0264 }
0265 
0266 void KIOFuseVFS::fuseRequestPending()
0267 {
0268     // Never deallocated, just reused
0269     static struct fuse_buf fbuf = {};
0270 
0271     // Read requests until empty (-EAGAIN) or error
0272     for(;;)
0273     {
0274         int res = fuse_session_receive_buf(m_fuseSession, &fbuf);
0275 
0276         if (res == -EINTR || res == -EAGAIN)
0277             break;
0278 
0279         if (res <= 0)
0280         {
0281             if(res < 0) // Error
0282                 qWarning(KIOFUSE_LOG) << "Error reading FUSE request:" << strerror(errno);
0283 
0284             // Error or umounted -> quit
0285             stop();
0286             break;
0287         }
0288 
0289         fuse_session_process_buf(m_fuseSession, &fbuf);
0290     }
0291 }
0292 
0293 void KIOFuseVFS::setUseFileJob(bool useFileJob)
0294 {
0295     m_useFileJob = useFileJob;
0296 }
0297 
0298 void KIOFuseVFS::mountUrl(const QUrl &url, const std::function<void (const QString &, int)> &callback)
0299 {
0300     // First make sure it actually exists
0301     qDebug(KIOFUSE_LOG) << "Stating" << url.toDisplayString() << "for mount";
0302     auto statJob = KIO::stat(url);
0303     statJob->setSide(KIO::StatJob::SourceSide); // Be "optimistic" to allow accessing
0304                                                 // files over plain HTTP
0305     connect(statJob, &KIO::StatJob::result, [=] {
0306         if(statJob->error())
0307         {
0308             qDebug(KIOFUSE_LOG) << statJob->errorString();
0309             return callback({}, kioErrorToFuseError(statJob->error()));
0310         }
0311 
0312         // The file exists, try to mount it
0313         auto pathElements = url.path().split(QLatin1Char('/'));
0314         pathElements.removeAll({});
0315 
0316         return findAndCreateOrigin(originOfUrl(url), pathElements, callback);
0317     });
0318 }
0319 
0320 QStringList KIOFuseVFS::mapUrlToVfs(const QUrl &url)
0321 {
0322     // Build the path where it will appear in the VFS
0323     auto targetPathComponents = QStringList{url.scheme(), url.authority()};
0324     targetPathComponents.append(url.path().split(QLatin1Char('/')));
0325 
0326     // Strip empty path elements, for instance in
0327     // "file:///home/foo"
0328     //         ^                   V
0329     // "ftp://user@host/dir/ectory/"
0330     targetPathComponents.removeAll({});
0331 
0332     return targetPathComponents;
0333 }
0334 
0335 void KIOFuseVFS::findAndCreateOrigin(const QUrl &url, const QStringList &pathElements, const std::function<void (const QString &, int)> &callback)
0336 {
0337     qDebug(KIOFUSE_LOG) << "Trying origin" << url.toDisplayString();
0338     auto statJob = KIO::stat(url);
0339     statJob->setSide(KIO::StatJob::SourceSide); // Be "optimistic" to allow accessing
0340                                                 // files over plain HTTP
0341     connect(statJob, &KIO::StatJob::result, [=] {
0342         if(statJob->error())
0343         {
0344             qDebug(KIOFUSE_LOG) << statJob->errorString();
0345 
0346             if(!pathElements.isEmpty())
0347                 return findAndCreateOrigin(addPathElements(url, pathElements.mid(0, 1)), pathElements.mid(1), callback);
0348 
0349             qDebug(KIOFUSE_LOG) << "Creating origin failed";
0350             return callback({}, kioErrorToFuseError(statJob->error()));
0351         }
0352 
0353         qDebug(KIOFUSE_LOG) << "Origin found at" << url.toDisplayString();
0354 
0355         auto targetPathComponents = mapUrlToVfs(url);
0356 
0357         if(std::any_of(targetPathComponents.begin(), targetPathComponents.end(),
0358                        [](const QString &s) { return !isValidFilename(s); }))
0359             return callback({}, EINVAL); // Invalid path (contains '.' or '..')
0360 
0361         auto currentNode = std::dynamic_pointer_cast<KIOFuseDirNode>(nodeForIno(KIOFuseIno::Root));
0362 
0363         // Traverse/create all components until the last
0364         for(int pathIdx = 0; pathIdx < targetPathComponents.size() - 1; ++pathIdx)
0365         {
0366             auto name = targetPathComponents[pathIdx];
0367             auto nextNode = nodeByName(currentNode, name);
0368             auto nextDirNode = std::dynamic_pointer_cast<KIOFuseDirNode>(nextNode);
0369 
0370             if(!nextNode)
0371             {
0372                 struct stat attr = {};
0373                 fillStatForFile(attr);
0374                 attr.st_mode = S_IFDIR | 0755;
0375 
0376                 nextDirNode = std::make_shared<KIOFuseDirNode>(currentNode->m_stat.st_ino, name, attr);
0377                 insertNode(nextDirNode);
0378             }
0379             else if(!nextDirNode)
0380             {
0381                 qWarning(KIOFUSE_LOG) << "Node" << nextNode->m_nodeName << "not a dir?";
0382                 return callback({}, EIO);
0383             }
0384 
0385             currentNode = nextDirNode;
0386         }
0387 
0388         auto finalNode = nodeByName(currentNode, targetPathComponents.last());
0389         if(!finalNode)
0390         {
0391             finalNode = createNodeFromUDSEntry(statJob->statResult(), currentNode->m_stat.st_ino, targetPathComponents.last());
0392             if(!finalNode)
0393             {
0394                 qWarning(KIOFUSE_LOG) << "Unable to create a valid final node for" << url.toDisplayString() << "from its UDS Entry";
0395                 return callback({}, EIO);
0396             }
0397 
0398             // Some ioslaves like man:/ implement "index files" for folders (including /) by making
0399             // them look as regular file when stating, but they also support listDir for directory
0400             // functionality. This behaviour is not compatible, so just reject it outright.
0401             if((url.path().isEmpty() || url.path() == QStringLiteral("/"))
0402                && !S_ISDIR(finalNode->m_stat.st_mode))
0403             {
0404                 qWarning(KIOFUSE_LOG) << "Root of mount at" << url.toDisplayString() << "not a directory";
0405                 return callback({}, ENOTDIR);
0406             }
0407 
0408             insertNode(finalNode);
0409         }
0410 
0411         auto originNode = std::dynamic_pointer_cast<KIOFuseRemoteNodeInfo>(finalNode);
0412         if(!originNode)
0413         {
0414             qWarning(KIOFUSE_LOG) << "Origin" << finalNode->m_nodeName << "exists but not a remote node?";
0415             return callback({}, EIO);
0416         }
0417 
0418         originNode->m_overrideUrl = url; // Allow the user to change the password
0419         return callback((targetPathComponents + pathElements).join(QLatin1Char('/')), 0);
0420     });
0421 }
0422 
0423 void KIOFuseVFS::init(void *userdata, fuse_conn_info *conn)
0424 {
0425     KIOFuseVFS *that = reinterpret_cast<KIOFuseVFS*>(userdata);
0426 
0427     if(that->m_fuseConnInfoOpts)
0428     {
0429         fuse_apply_conn_info_opts(that->m_fuseConnInfoOpts.get(), conn);
0430         that->m_fuseConnInfoOpts.reset();
0431     }
0432 
0433     conn->want &= ~FUSE_CAP_HANDLE_KILLPRIV; // Don't care about resetting setuid/setgid flags
0434     conn->want &= ~FUSE_CAP_ATOMIC_O_TRUNC; // Use setattr with st_size = 0 instead of open with O_TRUNC
0435     // Writeback caching needs fuse_notify calls for shared filesystems, but those are broken by design
0436     conn->want &= ~FUSE_CAP_WRITEBACK_CACHE;
0437     conn->time_gran = 1000000000; // Only second resolution for mtime
0438 }
0439 
0440 void KIOFuseVFS::getattr(fuse_req_t req, fuse_ino_t ino, fuse_file_info *fi)
0441 {
0442     Q_UNUSED(fi);
0443     KIOFuseVFS *that = reinterpret_cast<KIOFuseVFS*>(fuse_req_userdata(req));
0444     auto node = that->nodeForIno(ino);
0445     if(!node)
0446     {
0447         fuse_reply_err(req, EIO);
0448         return;
0449     }
0450 
0451     return that->awaitAttrRefreshed(node, [=] (int error) {
0452         Q_UNUSED(error); // Just send the old attr...
0453         replyAttr(req, node);
0454     });
0455 }
0456 
0457 void KIOFuseVFS::setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr, int to_set, fuse_file_info *fi)
0458 {
0459     Q_UNUSED(fi);
0460     KIOFuseVFS *that = reinterpret_cast<KIOFuseVFS*>(fuse_req_userdata(req));
0461     auto node = that->nodeForIno(ino);
0462     if(!node)
0463     {
0464         fuse_reply_err(req, EIO);
0465         return;
0466     }
0467 
0468     auto remoteDirNode = std::dynamic_pointer_cast<KIOFuseRemoteDirNode>(node);
0469     auto remoteFileNode = std::dynamic_pointer_cast<KIOFuseRemoteFileNode>(node);
0470 
0471     if(!remoteDirNode && !remoteFileNode)
0472     {
0473         fuse_reply_err(req, EOPNOTSUPP);
0474         return;
0475     }
0476 
0477     if((to_set & ~(FUSE_SET_ATTR_SIZE | FUSE_SET_ATTR_UID | FUSE_SET_ATTR_GID
0478                   | FUSE_SET_ATTR_MODE
0479                   | FUSE_SET_ATTR_MTIME | FUSE_SET_ATTR_MTIME_NOW
0480                   | FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_ATIME_NOW
0481                   | FUSE_SET_ATTR_CTIME))
0482        || (!remoteFileNode && (to_set & FUSE_SET_ATTR_SIZE))) // Unsupported operation requested?
0483     {
0484         // Don't do anything
0485         fuse_reply_err(req, EOPNOTSUPP);
0486         return;
0487     }
0488 
0489     auto cacheBasedFileNode = std::dynamic_pointer_cast<KIOFuseRemoteCacheBasedFileNode>(node);
0490     auto fileJobBasedFileNode = std::dynamic_pointer_cast<KIOFuseRemoteFileJobBasedFileNode>(node);
0491 
0492     // To have equal atim and mtim
0493     struct timespec tsNow;
0494     clock_gettime(CLOCK_REALTIME, &tsNow);
0495 
0496     // Can anything be done directly?
0497 
0498     // This is a hack: Access and change time are not actually passed through to KIO.
0499     // The kernel sends request for those if writeback caching is enabled, so it's not
0500     // possible to ignore them. So just save them in the local cache.
0501     if(to_set & (FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_ATIME_NOW))
0502     {
0503         if(to_set & FUSE_SET_ATTR_ATIME_NOW)
0504             attr->st_atim = tsNow;
0505 
0506         node->m_stat.st_atim = attr->st_atim;
0507         to_set &= ~(FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_ATIME_NOW);
0508     }
0509     if(to_set & FUSE_SET_ATTR_CTIME)
0510     {
0511         node->m_stat.st_ctim = attr->st_ctim;
0512         to_set &= ~FUSE_SET_ATTR_CTIME;
0513     }
0514     if((to_set & FUSE_SET_ATTR_SIZE) && cacheBasedFileNode)
0515     {
0516         // Can be done directly if the new size is zero (and there is no get going on).
0517         // This is an optimization to avoid fetching the entire file just to ignore its content.
0518         if(!cacheBasedFileNode->m_localCache && attr->st_size == 0)
0519         {
0520             // Just create an empty file
0521             cacheBasedFileNode->m_localCache = tmpfile();
0522             if(!cacheBasedFileNode->m_localCache)
0523             {
0524                 fuse_reply_err(req, EIO);
0525                 return;
0526             }
0527 
0528             cacheBasedFileNode->m_cacheComplete = true;
0529             cacheBasedFileNode->m_cacheSize = cacheBasedFileNode->m_stat.st_size = 0;
0530             cacheBasedFileNode->m_stat.st_mtim = cacheBasedFileNode->m_stat.st_ctim = tsNow;
0531             that->markCacheDirty(cacheBasedFileNode);
0532 
0533             to_set &= ~FUSE_SET_ATTR_SIZE; // Done already!
0534         }
0535     }
0536 
0537     if(!to_set) // Done already?
0538     {
0539         replyAttr(req, node);
0540         return;
0541     }
0542 
0543     // Everything else has to be done async - but there might be multiple ops that
0544     // need to be coordinated. If an operation completes and clearing its value(s)
0545     // in to_set_remaining leaves a zero value, it replies with fuse_reply_attr if
0546     // error is zero and fuse_reply_err(error) otherwise.
0547     struct SetattrState {
0548         int to_set_remaining;
0549         int error;
0550         struct stat value;
0551     };
0552 
0553     auto sharedState = std::make_shared<SetattrState>((SetattrState){to_set, 0, *attr});
0554 
0555     auto markOperationCompleted = [=] (int to_set_done) {
0556         sharedState->to_set_remaining &= ~to_set_done;
0557         if(!sharedState->to_set_remaining)
0558         {
0559             if(sharedState->error)
0560                 fuse_reply_err(req, sharedState->error);
0561             else
0562                 replyAttr(req, node);
0563         }
0564     };
0565 
0566     if((to_set & FUSE_SET_ATTR_SIZE) && cacheBasedFileNode)
0567     {
0568         // Have to wait until the cache is complete to truncate.
0569         // Waiting until all bytes up to the truncation point are available won't work,
0570         // as the fetch function would just overwrite the cache.
0571         that->awaitCacheComplete(cacheBasedFileNode, [=] (int error) {
0572             if(error)
0573                 sharedState->error = error;
0574             else // Cache complete!
0575             {
0576                 // Truncate the cache file
0577                 if(fflush(cacheBasedFileNode->m_localCache) != 0
0578                     || ftruncate(fileno(cacheBasedFileNode->m_localCache), sharedState->value.st_size) == -1)
0579                     sharedState->error = errno;
0580                 else
0581                 {
0582                     cacheBasedFileNode->m_cacheSize = cacheBasedFileNode->m_stat.st_size = sharedState->value.st_size;
0583                     cacheBasedFileNode->m_stat.st_mtim = cacheBasedFileNode->m_stat.st_ctim = tsNow;
0584                     that->markCacheDirty(cacheBasedFileNode);
0585                 }
0586             }
0587             markOperationCompleted(FUSE_SET_ATTR_SIZE);
0588         });
0589     }
0590     else if ((to_set & FUSE_SET_ATTR_SIZE) && fileJobBasedFileNode)
0591     {
0592         auto *fileJob = KIO::open(that->remoteUrl(fileJobBasedFileNode), QIODevice::ReadWrite);
0593         connect(fileJob, &KIO::FileJob::result, [=] (auto *job) {
0594             // All errors come through this signal, so error-handling is done here
0595             if(job->error())
0596             {
0597                 sharedState->error = kioErrorToFuseError(job->error());
0598                 markOperationCompleted(FUSE_SET_ATTR_SIZE);
0599             }
0600         });
0601         connect(fileJob, &KIO::FileJob::open, [=] {
0602             fileJob->truncate(sharedState->value.st_size);
0603             connect(fileJob, &KIO::FileJob::truncated, [=] {
0604                 fileJob->close();
0605                 connect(fileJob, qOverload<KIO::Job*>(&KIO::FileJob::close), [=] {
0606                     fileJobBasedFileNode->m_stat.st_size = sharedState->value.st_size;
0607                     markOperationCompleted(FUSE_SET_ATTR_SIZE);
0608                 });
0609             });
0610         });
0611     }
0612 
0613     if(to_set & (FUSE_SET_ATTR_UID | FUSE_SET_ATTR_GID))
0614     {
0615         // KIO uses strings for passing user and group, but the VFS uses IDs exclusively.
0616         // So this needs a roundtrip.
0617 
0618         uid_t newUid = (to_set & FUSE_SET_ATTR_UID) ? attr->st_uid : node->m_stat.st_uid;
0619         gid_t newGid = (to_set & FUSE_SET_ATTR_GID) ? attr->st_gid : node->m_stat.st_gid;
0620         auto *pw = getpwuid(newUid);
0621         auto *gr = getgrgid(newGid);
0622 
0623         if(!pw || !gr)
0624         {
0625             sharedState->error = ENOENT;
0626             markOperationCompleted(FUSE_SET_ATTR_UID | FUSE_SET_ATTR_GID);
0627         }
0628         else
0629         {
0630             QString newOwner = QString::fromUtf8(pw->pw_name),
0631                     newGroup = QString::fromUtf8(gr->gr_name);
0632 
0633             auto *job = KIO::chown(that->remoteUrl(node), newOwner, newGroup);
0634             that->connect(job, &KIO::SimpleJob::finished, [=] {
0635                 if(job->error())
0636                     sharedState->error = kioErrorToFuseError(job->error());
0637                 else
0638                 {
0639                     node->m_stat.st_uid = newUid;
0640                     node->m_stat.st_gid = newGid;
0641                     node->m_stat.st_ctim = tsNow;
0642                 }
0643 
0644                 markOperationCompleted(FUSE_SET_ATTR_UID | FUSE_SET_ATTR_GID);
0645             });
0646         }
0647     }
0648 
0649     if(to_set & (FUSE_SET_ATTR_MODE))
0650     {
0651         auto newMode = attr->st_mode & ~S_IFMT;
0652         auto *job = KIO::chmod(that->remoteUrl(node), newMode);
0653         that->connect(job, &KIO::SimpleJob::finished, [=] {
0654             if(job->error())
0655                 sharedState->error = kioErrorToFuseError(job->error());
0656             else
0657             {
0658                 node->m_stat.st_mode = (node->m_stat.st_mode & S_IFMT) | newMode;
0659                 node->m_stat.st_ctim = tsNow;
0660             }
0661 
0662             markOperationCompleted(FUSE_SET_ATTR_MODE);
0663         });
0664     }
0665 
0666     if(to_set & (FUSE_SET_ATTR_MTIME | FUSE_SET_ATTR_MTIME_NOW))
0667     {
0668         if(to_set & FUSE_SET_ATTR_MTIME_NOW)
0669             sharedState->value.st_mtim = tsNow;
0670 
0671         auto time = QDateTime::fromMSecsSinceEpoch(qint64(sharedState->value.st_mtim.tv_sec) * 1000
0672                                                    + sharedState->value.st_mtim.tv_nsec / 1000000);
0673         auto *job = KIO::setModificationTime(that->remoteUrl(node), time);
0674         that->connect(job, &KIO::SimpleJob::finished, [=] {
0675             if(job->error())
0676                 sharedState->error = kioErrorToFuseError(job->error());
0677             else // This is not quite correct, as KIO rounded the value down to a second
0678                 node->m_stat.st_mtim = sharedState->value.st_mtim;
0679 
0680             markOperationCompleted(FUSE_SET_ATTR_MTIME | FUSE_SET_ATTR_MTIME_NOW);
0681         });
0682     }
0683 }
0684 
0685 void KIOFuseVFS::readlink(fuse_req_t req, fuse_ino_t ino)
0686 {
0687     KIOFuseVFS *that = reinterpret_cast<KIOFuseVFS*>(fuse_req_userdata(req));
0688     auto node = that->nodeForIno(ino);
0689     if(!node)
0690     {
0691         fuse_reply_err(req, EIO);
0692         return;
0693     }
0694 
0695     auto symlinkNode = std::dynamic_pointer_cast<KIOFuseSymLinkNode>(node);
0696     if(!symlinkNode)
0697     {
0698         fuse_reply_err(req, EINVAL);
0699         return;
0700     }
0701 
0702     that->awaitAttrRefreshed(node, [=](int error) {
0703         Q_UNUSED(error); // Just send the old target...
0704 
0705         QString target = symlinkNode->m_target;
0706 
0707         // Convert an absolute link to be absolute within its origin
0708         if(QDir::isAbsolutePath(target))
0709         {
0710             target = target.mid(1); // Strip the initial /
0711             QUrl origin = that->originOfUrl(that->remoteUrl(symlinkNode));
0712             origin = addPathElements(origin, target.split(QLatin1Char('/')));
0713             target = that->m_mountpoint + that->mapUrlToVfs(origin).join(QLatin1Char('/'));
0714             qCDebug(KIOFUSE_LOG) << "Detected reading of absolute symlink" << symlinkNode->m_target << "at" << that->virtualPath(symlinkNode) << ", rewritten to" << target;
0715         }
0716 
0717         fuse_reply_readlink(req, target.toUtf8().data());
0718     });
0719 }
0720 
0721 void KIOFuseVFS::mknod(fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode, dev_t rdev)
0722 {
0723     Q_UNUSED(rdev);
0724 
0725     KIOFuseVFS *that = reinterpret_cast<KIOFuseVFS*>(fuse_req_userdata(req));
0726     auto node = that->nodeForIno(parent);
0727     if(!node)
0728     {
0729         fuse_reply_err(req, EIO);
0730         return;
0731     }
0732 
0733     auto remote = std::dynamic_pointer_cast<KIOFuseRemoteDirNode>(node);
0734     if(!remote)
0735     {
0736         fuse_reply_err(req, EINVAL);
0737         return;
0738     }
0739 
0740     // No type means regular file as well
0741     if((mode & S_IFMT) != S_IFREG && (mode & S_IFMT) != 0)
0742     {
0743         fuse_reply_err(req, EOPNOTSUPP);
0744         return;
0745     }
0746 
0747     auto nameStr = QString::fromUtf8(name);
0748     auto url = addPathElements(that->remoteUrl(node), {nameStr});
0749     auto *job = KIO::put(url, mode & ~S_IFMT);
0750     // Not connecting to the dataReq signal at all results in an empty file
0751     that->connect(job, &KIO::SimpleJob::finished, [=] {
0752         if(job->error())
0753         {
0754             fuse_reply_err(req, kioErrorToFuseError(job->error()));
0755             return;
0756         }
0757 
0758         that->awaitChildMounted(remote, nameStr, [=](auto node, int error) {
0759             if(error)
0760                 fuse_reply_err(req, error);
0761             else
0762                 that->replyEntry(req, node);
0763         });
0764     });
0765 }
0766 
0767 void KIOFuseVFS::mkdir(fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode)
0768 {
0769     KIOFuseVFS *that = reinterpret_cast<KIOFuseVFS*>(fuse_req_userdata(req));
0770     auto node = that->nodeForIno(parent);
0771     if(!node)
0772     {
0773         fuse_reply_err(req, EIO);
0774         return;
0775     }
0776 
0777     auto remote = std::dynamic_pointer_cast<KIOFuseRemoteDirNode>(node);
0778     if(!remote)
0779     {
0780         fuse_reply_err(req, EINVAL);
0781         return;
0782     }
0783 
0784     auto namestr = QString::fromUtf8(name);
0785     auto url = addPathElements(that->remoteUrl(node), {namestr});
0786     auto *job = KIO::mkdir(url, mode & ~S_IFMT);
0787     that->connect(job, &KIO::SimpleJob::finished, [=] {
0788         if(job->error())
0789         {
0790             fuse_reply_err(req, kioErrorToFuseError(job->error()));
0791             return;
0792         }
0793 
0794         that->awaitChildMounted(remote, namestr, [=](auto node, int error) {
0795             if(error)
0796                 fuse_reply_err(req, error);
0797             else
0798                 that->replyEntry(req, node);
0799         });
0800     });
0801 }
0802 
0803 void KIOFuseVFS::unlinkHelper(fuse_req_t req, fuse_ino_t parent, const char *name, bool isDirectory)
0804 {
0805     KIOFuseVFS *that = reinterpret_cast<KIOFuseVFS*>(fuse_req_userdata(req));
0806     auto parentNode = that->nodeForIno(parent);
0807     if(!parentNode)
0808     {
0809         fuse_reply_err(req, EIO);
0810         return;
0811     }
0812 
0813     // Make sure the to-be deleted node is in a remote dir
0814     auto parentDirNode = std::dynamic_pointer_cast<KIOFuseRemoteDirNode>(parentNode);
0815     if(!parentDirNode)
0816     {
0817         fuse_reply_err(req, EINVAL);
0818         return;
0819     }
0820 
0821     auto node = that->nodeByName(parentDirNode, QString::fromUtf8(name));
0822     if(!node)
0823     {
0824         fuse_reply_err(req, ENOENT);
0825         return;
0826     }
0827 
0828     auto dirNode = std::dynamic_pointer_cast<KIOFuseDirNode>(node);
0829 
0830     if(!isDirectory && dirNode != nullptr)
0831     {
0832         fuse_reply_err(req, EISDIR);
0833         return;
0834     }
0835 
0836     if(isDirectory && dirNode == nullptr)
0837     {
0838         fuse_reply_err(req, ENOTDIR);
0839         return;
0840     }
0841     else if(dirNode && !dirNode->m_childrenInos.empty())
0842     {
0843         // If node is a dir, it must be empty
0844         fuse_reply_err(req, ENOTEMPTY);
0845         return;
0846     }
0847 
0848     if(auto fileJobNode = std::dynamic_pointer_cast<KIOFuseRemoteFileJobBasedFileNode>(node))
0849     {
0850         // After deleting a file, the contents become inaccessible immediately,
0851         // so avoid creating nameless inodes. tmpfile() semantics aren't possible with FileJob.
0852         if(fileJobNode->m_openCount)
0853         {
0854             fuse_reply_err(req, EBUSY);
0855             return;
0856         }
0857     }
0858 
0859     auto *job = KIO::del(that->remoteUrl(node));
0860     that->connect(job, &KIO::SimpleJob::finished, [=] {
0861         if(job->error())
0862         {
0863             fuse_reply_err(req, kioErrorToFuseError(job->error()));
0864             return;
0865         }
0866 
0867         that->markNodeDeleted(node);
0868         fuse_reply_err(req, 0);
0869     });
0870 }
0871 
0872 void KIOFuseVFS::unlink(fuse_req_t req, fuse_ino_t parent, const char *name)
0873 {
0874     unlinkHelper(req, parent, name, false);
0875 }
0876 
0877 void KIOFuseVFS::rmdir(fuse_req_t req, fuse_ino_t parent, const char *name)
0878 {
0879     unlinkHelper(req, parent, name, true);
0880 }
0881 
0882 void KIOFuseVFS::symlink(fuse_req_t req, const char *link, fuse_ino_t parent, const char *name)
0883 {
0884     KIOFuseVFS *that = reinterpret_cast<KIOFuseVFS*>(fuse_req_userdata(req));
0885     auto node = that->nodeForIno(parent);
0886     if(!node)
0887     {
0888         fuse_reply_err(req, EIO);
0889         return;
0890     }
0891 
0892     auto remote = std::dynamic_pointer_cast<KIOFuseRemoteDirNode>(node);
0893     if(!remote)
0894     {
0895         fuse_reply_err(req, EINVAL);
0896         return;
0897     }
0898 
0899     QString target = QString::fromUtf8(link);
0900 
0901     // Convert an absolute link within the VFS to an absolute link on the origin
0902     // (inverse of readlink)
0903     if(QDir::isAbsolutePath(target) && target.startsWith(that->m_mountpoint))
0904     {
0905         QUrl remoteSourceUrl = that->remoteUrl(remote),
0906              // QDir::relativePath would mangle the path, we want to keep it as-is
0907              remoteTargetUrl = that->localPathToRemoteUrl(target.mid(that->m_mountpoint.length()));
0908 
0909         if(remoteTargetUrl.isValid()
0910            && remoteSourceUrl.scheme() == remoteTargetUrl.scheme()
0911            && remoteSourceUrl.authority() == remoteTargetUrl.authority())
0912         {
0913             target = remoteTargetUrl.path();
0914             if(!target.startsWith(QLatin1Char('/')))
0915                 target.prepend(QLatin1Char('/'));
0916 
0917             qCDebug(KIOFUSE_LOG) << "Detected creation of absolute symlink, rewritten to" << target;
0918         }
0919         else
0920             qCWarning(KIOFUSE_LOG) << "Creation of absolute symlink to other origin";
0921     }
0922 
0923     auto namestr = QString::fromUtf8(name);
0924     auto url = addPathElements(that->remoteUrl(node), {namestr});
0925     auto *job = KIO::symlink(target, url);
0926     that->connect(job, &KIO::SimpleJob::finished, [=] {
0927         if(job->error())
0928         {
0929             fuse_reply_err(req, kioErrorToFuseError(job->error()));
0930             return;
0931         }
0932 
0933         that->awaitChildMounted(remote, namestr, [=](auto node, int error) {
0934             if(error)
0935                 fuse_reply_err(req, error);
0936             else
0937                 that->replyEntry(req, node);
0938         });
0939     });
0940 }
0941 
0942 void KIOFuseVFS::open(fuse_req_t req, fuse_ino_t ino, fuse_file_info *fi)
0943 {
0944     KIOFuseVFS *that = reinterpret_cast<KIOFuseVFS*>(fuse_req_userdata(req));
0945     auto node = that->nodeForIno(ino);
0946     if(!node)
0947     {
0948         fuse_reply_err(req, EIO);
0949         return;
0950     }
0951 
0952     node->m_openCount += 1;
0953 
0954     if (!(fi->flags & O_NOATIME))
0955         clock_gettime(CLOCK_REALTIME, &node->m_stat.st_atim);
0956 
0957     fuse_reply_open(req, fi);
0958 }
0959 
0960 void KIOFuseVFS::rename(fuse_req_t req, fuse_ino_t parent, const char *name, fuse_ino_t newparent, const char *newname, unsigned int flags)
0961 {
0962     if(flags & ~(RENAME_NOREPLACE))
0963     {
0964         // RENAME_EXCHANGE could be emulated locally, but not with the same guarantees
0965         fuse_reply_err(req, EOPNOTSUPP);
0966         return;
0967     }
0968 
0969     KIOFuseVFS *that = reinterpret_cast<KIOFuseVFS*>(fuse_req_userdata(req));
0970     auto parentNode = that->nodeForIno(parent), newParentNode = that->nodeForIno(newparent);
0971     if(!parentNode || !newParentNode)
0972     {
0973         fuse_reply_err(req, EIO);
0974         return;
0975     }
0976 
0977     auto remoteParent = std::dynamic_pointer_cast<KIOFuseRemoteDirNode>(parentNode),
0978          remoteNewParent = std::dynamic_pointer_cast<KIOFuseRemoteDirNode>(newParentNode);
0979     if(!remoteParent || !remoteNewParent)
0980     {
0981         fuse_reply_err(req, EINVAL);
0982         return;
0983     }
0984 
0985     auto node = that->nodeByName(remoteParent, QString::fromUtf8(name));
0986     if(!node)
0987     {
0988         fuse_reply_err(req, ENOENT);
0989         return;
0990     }
0991 
0992     QString newNameStr = QString::fromUtf8(newname);
0993 
0994     auto replacedNode = that->nodeByName(remoteNewParent, newNameStr);
0995 
0996     // Ensure that if node is a directory, replacedNode either does not exist or is an empty directory.
0997     if(std::dynamic_pointer_cast<KIOFuseDirNode>(node) && replacedNode)
0998     {
0999         auto replacedDir = std::dynamic_pointer_cast<KIOFuseDirNode>(replacedNode);
1000         if(!replacedDir)
1001         {
1002             fuse_reply_err(req, ENOTDIR);
1003             return;
1004         }
1005         if(replacedDir && !replacedDir->m_childrenInos.empty())
1006         {
1007             fuse_reply_err(req, ENOTEMPTY);
1008             return;
1009         }
1010     }
1011 
1012     auto url = addPathElements(that->remoteUrl(remoteParent), {QString::fromUtf8(name)}),
1013          newUrl = addPathElements(that->remoteUrl(remoteNewParent), {newNameStr});
1014 
1015     auto *job = KIO::rename(url, newUrl, (flags & RENAME_NOREPLACE) ? KIO::DefaultFlags : KIO::Overwrite);
1016     that->connect(job, &KIO::SimpleJob::finished, [=] {
1017         if(job->error())
1018             fuse_reply_err(req, kioErrorToFuseError(job->error()));
1019         else
1020         {
1021             if(replacedNode)
1022                 that->markNodeDeleted(replacedNode);
1023 
1024             that->reparentNode(node, newParentNode->m_stat.st_ino);
1025             node->m_nodeName = newNameStr;
1026 
1027             clock_gettime(CLOCK_REALTIME, &node->m_stat.st_ctim);
1028             fuse_reply_err(req, 0);
1029         }
1030     });
1031 }
1032 
1033 static void appendDirentry(std::vector<char> &dirbuf, fuse_req_t req, const char *name, const struct stat *stbuf)
1034 {
1035     size_t oldsize = dirbuf.size();
1036     dirbuf.resize(oldsize + fuse_add_direntry(req, nullptr, 0, name, nullptr, 0));
1037     fuse_add_direntry(req, dirbuf.data() + oldsize, dirbuf.size() + oldsize, name, stbuf, dirbuf.size());
1038 }
1039 
1040 void KIOFuseVFS::opendir(fuse_req_t req, fuse_ino_t ino, fuse_file_info *fi)
1041 {
1042     KIOFuseVFS *that = reinterpret_cast<KIOFuseVFS*>(fuse_req_userdata(req));
1043     auto node = that->nodeForIno(ino);
1044     if(!node)
1045     {
1046         fuse_reply_err(req, EIO);
1047         return;
1048     }
1049 
1050     auto dirNode = std::dynamic_pointer_cast<KIOFuseDirNode>(node);
1051 
1052     if(!dirNode)
1053     {
1054         fuse_reply_err(req, ENOTDIR);
1055         return;
1056     }
1057 
1058     node->m_openCount += 1;
1059 
1060     that->awaitChildrenComplete(dirNode, [=, myfi=*fi](int error) mutable {
1061         if(error)
1062         {
1063             fuse_reply_err(req, error);
1064             return;
1065         }
1066 
1067         auto dirbuf = std::make_unique<std::vector<char>>();
1068 
1069         appendDirentry(*dirbuf, req, ".", &node->m_stat);
1070 
1071         std::shared_ptr<KIOFuseNode> parentNode;
1072         if(node->m_parentIno != KIOFuseIno::DeletedRoot)
1073             parentNode = that->nodeForIno(node->m_parentIno);
1074         if(!parentNode)
1075             parentNode = that->nodeForIno(KIOFuseIno::Root);
1076         if(parentNode)
1077             appendDirentry(*dirbuf, req, "..", &parentNode->m_stat);
1078 
1079         for(auto ino : dirNode->m_childrenInos)
1080         {
1081             auto child = that->nodeForIno(ino);
1082             if(!child)
1083             {
1084                 qWarning(KIOFUSE_LOG) << "Node" << node->m_nodeName << "references nonexistent child";
1085                 continue;
1086             }
1087 
1088             appendDirentry(*dirbuf, req, qPrintable(child->m_nodeName), &child->m_stat);
1089         }
1090 
1091         myfi.fh = reinterpret_cast<uint64_t>(dirbuf.release());
1092 
1093         if (!(myfi.flags & O_NOATIME))
1094             clock_gettime(CLOCK_REALTIME, &node->m_stat.st_atim);
1095 
1096         fuse_reply_open(req, &myfi);
1097     });
1098 }
1099 
1100 void KIOFuseVFS::readdir(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi)
1101 {
1102     KIOFuseVFS *that = reinterpret_cast<KIOFuseVFS*>(fuse_req_userdata(req));
1103     auto node = that->nodeForIno(ino);
1104     if(!node)
1105     {
1106         fuse_reply_err(req, EIO);
1107         return;
1108     }
1109 
1110     auto dirNode = std::dynamic_pointer_cast<KIOFuseDirNode>(node);
1111     if(!dirNode)
1112     {
1113         fuse_reply_err(req, ENOTDIR);
1114         return;
1115     }
1116 
1117     std::vector<char>* dirbuf = reinterpret_cast<std::vector<char>*>(fi->fh);
1118     if(!dirbuf)
1119     {
1120         fuse_reply_err(req, EIO);
1121         return;
1122     }
1123 
1124     if(off < off_t(dirbuf->size()))
1125         fuse_reply_buf(req, dirbuf->data() + off, std::min(off_t(size), off_t(dirbuf->size()) - off));
1126     else
1127         fuse_reply_buf(req, nullptr, 0);
1128 }
1129 
1130 void KIOFuseVFS::releasedir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
1131 {
1132     KIOFuseVFS *that = reinterpret_cast<KIOFuseVFS*>(fuse_req_userdata(req));
1133     auto node = that->nodeForIno(ino);
1134     if(!node)
1135     {
1136         fuse_reply_err(req, EIO);
1137         return;
1138     }
1139 
1140     auto dirNode = std::dynamic_pointer_cast<KIOFuseDirNode>(node);
1141     if(!dirNode)
1142     {
1143         fuse_reply_err(req, ENOTDIR);
1144         return;
1145     }
1146 
1147     node->m_openCount -= 1;
1148     if(std::vector<char> *ptr = reinterpret_cast<std::vector<char>*>(fi->fh))
1149     {
1150         delete ptr;
1151     }
1152     else{
1153         qWarning(KIOFUSE_LOG) << "File handler of node" << node->m_nodeName << "already null";
1154     }
1155       
1156     fuse_reply_err(req, 0);
1157 }
1158 
1159 void KIOFuseVFS::read(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, fuse_file_info *fi)
1160 {
1161     Q_UNUSED(fi);
1162     KIOFuseVFS *that = reinterpret_cast<KIOFuseVFS*>(fuse_req_userdata(req));
1163     auto node = that->nodeForIno(ino);
1164     if(!node)
1165     {
1166         fuse_reply_err(req, EIO);
1167         return;
1168     }
1169 
1170     if(std::dynamic_pointer_cast<KIOFuseDirNode>(node))
1171     {
1172         fuse_reply_err(req, EISDIR);
1173         return;
1174     }
1175 
1176     if(auto remoteNode = std::dynamic_pointer_cast<KIOFuseRemoteCacheBasedFileNode>(node))
1177     {
1178         qDebug(KIOFUSE_LOG) << "Reading" << size << "byte(s) at offset" << off << "of (cache-based) node" << node->m_nodeName;
1179         that->awaitBytesAvailable(remoteNode, off + size, [=] (int error) {
1180             if(error != 0 && error != ESPIPE)
1181             {
1182                 fuse_reply_err(req, error);
1183                 return;
1184             }
1185 
1186             auto actualSize = size;
1187 
1188             if(error == ESPIPE)
1189             {
1190                 // Reading over the end
1191                 if(off >= remoteNode->m_cacheSize)
1192                     actualSize = 0;
1193                 else
1194                     actualSize = std::min(remoteNode->m_cacheSize - off, off_t(size));
1195             }
1196 
1197             // Make sure that the kernel has the data
1198             fflush(remoteNode->m_localCache);
1199 
1200             // Construct a buf pointing to the cache file
1201             fuse_bufvec buf = FUSE_BUFVEC_INIT(actualSize);
1202             buf.buf[0].fd = fileno(remoteNode->m_localCache);
1203             buf.buf[0].flags = static_cast<fuse_buf_flags>(FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK);
1204             buf.buf[0].pos = off;
1205 
1206             fuse_reply_data(req, &buf, fuse_buf_copy_flags{});
1207         });
1208 
1209         return;
1210     }
1211     else if(auto remoteNode = std::dynamic_pointer_cast<KIOFuseRemoteFileJobBasedFileNode>(node))
1212     {
1213         qDebug(KIOFUSE_LOG) << "Reading" << size << "byte(s) at offset" << off << "of (FileJob-based) node" << node->m_nodeName;
1214 
1215         if(node->m_parentIno == KIOFuseIno::DeletedRoot)
1216         {
1217             fuse_reply_err(req, ENOENT);
1218             return;
1219         }
1220 
1221         auto *fileJob = KIO::open(that->remoteUrl(remoteNode), QIODevice::ReadOnly);
1222         connect(fileJob, &KIO::FileJob::result, [=] (auto *job) {
1223             // All errors come through this signal, so error-handling is done here
1224             if(job->error())
1225                 fuse_reply_err(req, kioErrorToFuseError(job->error()));
1226         });
1227         connect(fileJob, &KIO::FileJob::open, [=] {
1228             fileJob->seek(off);
1229             connect(fileJob, &KIO::FileJob::position, [=] (auto *job, KIO::filesize_t offset) {
1230                 Q_UNUSED(job);
1231                 if(off_t(offset) != off)
1232                 {
1233                     fileJob->close();
1234                     fileJob->connect(fileJob, qOverload<KIO::Job*>(&KIO::FileJob::close), [=] {
1235                         fuse_reply_err(req, EIO);
1236                     });
1237                     return;
1238                 }
1239                 auto actualSize = remoteNode->m_stat.st_size = fileJob->size();
1240                 // Reading over the end
1241                 if(off >= off_t(actualSize))
1242                     actualSize = 0;
1243                 else
1244                     actualSize = std::min(off_t(actualSize) - off, off_t(size));
1245                 fileJob->read(actualSize);
1246                 QByteArray buffer;
1247                 fileJob->connect(fileJob, &KIO::FileJob::data, [=] (auto *readJob, const QByteArray &data) mutable {
1248                     Q_UNUSED(readJob);
1249                     QByteArray truncatedData = data.left(actualSize);
1250                     buffer.append(truncatedData);
1251                     actualSize -= truncatedData.size();
1252 
1253                     if(actualSize > 0)
1254                     {
1255                         // Keep reading until we get all the data we need.
1256                         fileJob->read(actualSize);
1257                         return;
1258                     }
1259                     fileJob->close();
1260                     fileJob->connect(fileJob, qOverload<KIO::Job*>(&KIO::FileJob::close), [=] {
1261                         fuse_reply_buf(req, buffer.constData(), buffer.size());
1262                     });
1263                 });
1264             });
1265         });
1266 
1267         return;
1268     }
1269 
1270     fuse_reply_err(req, EIO);
1271 }
1272 
1273 void KIOFuseVFS::write(fuse_req_t req, fuse_ino_t ino, const char *buf, size_t size, off_t off, fuse_file_info *fi)
1274 {
1275     Q_UNUSED(fi);
1276     KIOFuseVFS *that = reinterpret_cast<KIOFuseVFS*>(fuse_req_userdata(req));
1277     auto node = that->nodeForIno(ino);
1278     if(!node)
1279     {
1280         fuse_reply_err(req, EIO);
1281         return;
1282     }
1283 
1284     if(std::dynamic_pointer_cast<KIOFuseDirNode>(node))
1285     {
1286         fuse_reply_err(req, EISDIR);
1287         return;
1288     }
1289 
1290     if(auto remoteNode = std::dynamic_pointer_cast<KIOFuseRemoteCacheBasedFileNode>(node))
1291     {
1292         qDebug(KIOFUSE_LOG) << "Writing" << size << "byte(s) at offset" << off << "of (cache-based) node" << node->m_nodeName;
1293         QByteArray data(buf, size); // Copy data
1294         // fi lives on the caller's stack make a copy.
1295         auto cacheBasedWriteCallback = [=, fi_flags=fi->flags] (int error) {
1296             if(error && error != ESPIPE)
1297             {
1298                 fuse_reply_err(req, error);
1299                 return;
1300             }
1301 
1302             off_t offset = (fi_flags & O_APPEND) ? remoteNode->m_cacheSize : off;
1303 
1304             int cacheFd = fileno(remoteNode->m_localCache);
1305             if(lseek(cacheFd, offset, SEEK_SET) == -1
1306                || !sane_write(cacheFd, data.data(), data.size()))
1307             {
1308                 fuse_reply_err(req, errno);
1309                 return;
1310             }
1311 
1312             remoteNode->m_cacheSize = std::max(remoteNode->m_cacheSize, off_t(offset + size));
1313             // Don't set it to a lower value, in case the cache is incomplete
1314             remoteNode->m_stat.st_size = std::max(remoteNode->m_stat.st_size, remoteNode->m_cacheSize);
1315             // Update [cm] time as without writeback caching,
1316             // the kernel doesn't do this for us.
1317             clock_gettime(CLOCK_REALTIME, &remoteNode->m_stat.st_mtim);
1318             remoteNode->m_stat.st_ctim = remoteNode->m_stat.st_mtim;
1319             that->markCacheDirty(remoteNode);
1320 
1321             fuse_reply_write(req, data.size());
1322         };
1323 
1324         if(fi->flags & O_APPEND)
1325             // Wait for cache to be complete to ensure valid m_cacheSize
1326             that->awaitCacheComplete(remoteNode, cacheBasedWriteCallback);
1327         else
1328             that->awaitBytesAvailable(remoteNode, off + size, cacheBasedWriteCallback);
1329 
1330         return;
1331     }
1332     else if(auto remoteNode = std::dynamic_pointer_cast<KIOFuseRemoteFileJobBasedFileNode>(node))
1333     {
1334         qDebug(KIOFUSE_LOG) << "Writing" << size << "byte(s) at offset" << off << "of (FileJob-based) node" << node->m_nodeName;
1335 
1336         if(node->m_parentIno == KIOFuseIno::DeletedRoot)
1337         {
1338             fuse_reply_err(req, ENOENT);
1339             return;
1340         }
1341 
1342         QByteArray data(buf, size); // Copy data
1343         auto *fileJob = KIO::open(that->remoteUrl(remoteNode), QIODevice::ReadWrite);
1344         connect(fileJob, &KIO::FileJob::result, [=] (auto *job) {
1345             // All errors come through this signal, so error-handling is done here
1346             if(job->error())
1347                 fuse_reply_err(req, kioErrorToFuseError(job->error()));
1348         });
1349         connect(fileJob, &KIO::FileJob::open, [=, fi_flags=fi->flags] {
1350             off_t offset = (fi_flags & O_APPEND) ? fileJob->size() : off;
1351             fileJob->seek(offset);
1352             connect(fileJob, &KIO::FileJob::position, [=] (auto *job, KIO::filesize_t offset) {
1353                 Q_UNUSED(job);
1354                 if (off_t(offset) != off) {
1355                     fileJob->close();
1356                     fileJob->connect(fileJob, qOverload<KIO::Job*>(&KIO::FileJob::close), [=] {
1357                         fuse_reply_err(req, EIO);
1358                     });
1359                     return;
1360                 }
1361                 // Limit write to avoid killing the slave.
1362                 // @see https://phabricator.kde.org/D15448
1363                 fileJob->write(data.left(0xFFFFFF));
1364                 off_t bytesLeft = size;
1365                 fileJob->connect(fileJob, &KIO::FileJob::written, [=] (auto *writeJob, KIO::filesize_t written) mutable {
1366                     Q_UNUSED(writeJob);
1367                     bytesLeft -= written;
1368                     if (bytesLeft > 0)
1369                     {
1370                         // Keep writing until we write all the data we need.
1371                         fileJob->write(data.mid(size - bytesLeft, 0xFFFFFF));
1372                         return;
1373                     }
1374                     fileJob->close();
1375                     fileJob->connect(fileJob, qOverload<KIO::Job*>(&KIO::FileJob::close), [=] {
1376                         // Wait till we've flushed first...
1377                         remoteNode->m_stat.st_size = std::max(off_t(offset + data.size()), remoteNode->m_stat.st_size);
1378                         fuse_reply_write(req, data.size());
1379                     });
1380                 });
1381             });
1382         });
1383 
1384         return;
1385     }
1386 
1387     fuse_reply_err(req, EIO);
1388 }
1389 
1390 void KIOFuseVFS::flush(fuse_req_t req, fuse_ino_t ino, fuse_file_info *fi)
1391 {
1392     // This is called on each close of a FD, so it might be a bit overzealous
1393     // to do writeback here. I can't think of a better alternative though -
1394     // doing it only on fsync and the final forget seems like a bit too late.
1395 
1396     return KIOFuseVFS::fsync(req, ino, 1, fi);
1397 }
1398 
1399 void KIOFuseVFS::release(fuse_req_t req, fuse_ino_t ino, fuse_file_info *fi)
1400 {
1401     Q_UNUSED(fi);
1402     KIOFuseVFS *that = reinterpret_cast<KIOFuseVFS*>(fuse_req_userdata(req));
1403     auto node = that->nodeForIno(ino);
1404     if(!node)
1405     {
1406         fuse_reply_err(req, EIO);
1407         return;
1408     }
1409 
1410     node->m_openCount -= 1;
1411 
1412     fuse_reply_err(req, 0); // Ignored anyway
1413 
1414     auto remoteFileNode = std::dynamic_pointer_cast<KIOFuseRemoteCacheBasedFileNode>(node);
1415     if(node->m_openCount || !remoteFileNode || !remoteFileNode->m_localCache)
1416         return; // Nothing to do
1417 
1418     // When the cache is not dirty, remove the cache file.
1419     that->awaitNodeFlushed(remoteFileNode, [=](int error) {
1420         if(error != 0 || node->m_openCount != 0)
1421             return; // Better not remove the cache
1422         if(remoteFileNode->m_localCache == nullptr)
1423             return; // Already removed (happens if the file was reopened and closed while flushing)
1424         if(!remoteFileNode->cacheIsComplete())
1425             return; // Currently filling
1426 
1427         if(remoteFileNode->m_cacheDirty || remoteFileNode->m_flushRunning)
1428         {
1429             if(remoteFileNode->m_parentIno == KIOFuseIno::DeletedRoot)
1430                 return; // Closed a deleted dirty file, keep the cache as it could be reopened
1431 
1432             // Can't happen, but if it does, avoid data loss and potential crashing later by keeping
1433             // the cache.
1434             qWarning(KIOFUSE_LOG) << "Node" << remoteFileNode->m_nodeName << "turned dirty in flush callback";
1435             return;
1436         }
1437 
1438         qDebug(KIOFUSE_LOG) << "Removing cache of" << remoteFileNode->m_nodeName;
1439         fclose(remoteFileNode->m_localCache);
1440         remoteFileNode->m_cacheSize = 0;
1441         remoteFileNode->m_localCache = nullptr;
1442         remoteFileNode->m_cacheComplete = false;
1443     });
1444 }
1445 
1446 void KIOFuseVFS::fsync(fuse_req_t req, fuse_ino_t ino, int datasync, fuse_file_info *fi)
1447 {
1448     Q_UNUSED(datasync); Q_UNUSED(fi);
1449     KIOFuseVFS *that = reinterpret_cast<KIOFuseVFS*>(fuse_req_userdata(req));
1450     auto node = that->nodeForIno(ino);
1451     if(!node)
1452     {
1453         fuse_reply_err(req, EIO);
1454         return;
1455     }
1456 
1457     if(auto cacheBasedFileNode = std::dynamic_pointer_cast<KIOFuseRemoteCacheBasedFileNode>(node))
1458         that->awaitNodeFlushed(cacheBasedFileNode, [=](int error) {
1459             fuse_reply_err(req, error);
1460         });
1461     else
1462         fuse_reply_err(req, 0);
1463 }
1464 
1465 bool KIOFuseVFS::isEnvironmentValid()
1466 {
1467     static_assert(sizeof(off_t) >= 8, "Please compile with -D_FILE_OFFSET_BITS=64 to allow working with large (>4GB) files");
1468 
1469 #ifdef Q_OS_LINUX
1470     // On 32bit Linux before "fuse: fix writepages on 32bit", writes past 4GiB were silently discarded.
1471     // Technically this would have to check the kernel's bitness, but that's not easily possible.
1472     if(sizeof(size_t) != sizeof(off_t))
1473     {
1474         struct utsname uts;
1475         if(uname(&uts) != 0)
1476             return false;
1477 
1478         auto kernelversion = QVersionNumber::fromString(QLatin1String(uts.release));
1479         if(kernelversion < QVersionNumber(5, 2))
1480         {
1481             qCritical(KIOFUSE_LOG) << "You're running kio-fuse on an older 32-bit kernel, which can lead to data loss.\n"
1482                                       "Please use a newer one or make sure that the 'fuse: fix writepages on 32bit' commit "
1483                                       "is part of the kernel and then build kio-fuse with this check adjusted.\n"
1484                                       "If you don't know how to do that, please file a bug at your distro.";
1485             return false;
1486         }
1487     }
1488 #endif
1489 
1490     return true;
1491 }
1492 
1493 std::shared_ptr<KIOFuseNode> KIOFuseVFS::nodeByName(const std::shared_ptr<KIOFuseDirNode> &parent, const QString &name) const
1494 {
1495     for(auto ino : parent->m_childrenInos)
1496     {
1497         auto child = nodeForIno(ino);
1498         if(!child)
1499         {
1500             qWarning(KIOFUSE_LOG) << "Node" << parent->m_nodeName << "references nonexistent child";
1501             continue;
1502         }
1503 
1504         if(child->m_nodeName == name)
1505             return child;
1506     }
1507 
1508     return nullptr;
1509 }
1510 
1511 void KIOFuseVFS::lookup(fuse_req_t req, fuse_ino_t parent, const char *name)
1512 {
1513     KIOFuseVFS *that = reinterpret_cast<KIOFuseVFS*>(fuse_req_userdata(req));
1514     auto parentNode = that->nodeForIno(parent);
1515     if(!parentNode)
1516     {
1517         fuse_reply_err(req, EIO);
1518         return;
1519     }
1520 
1521     auto parentDirNode = std::dynamic_pointer_cast<KIOFuseDirNode>(parentNode);
1522     if(!parentDirNode)
1523     {
1524         fuse_reply_err(req, ENOTDIR);
1525         return;
1526     }
1527 
1528     QString nodeName = QString::fromUtf8(name);
1529 
1530     if(auto child = that->nodeByName(parentDirNode, nodeName)) // Part of the tree?
1531     {
1532         return that->awaitAttrRefreshed(child, [=](int error) {
1533             Q_UNUSED(error);
1534             // Lookup again, node might've been replaced or deleted
1535             auto child = that->nodeByName(parentDirNode, nodeName);
1536             that->replyEntry(req, child);
1537         });
1538     }
1539 
1540     auto remoteDirNode = std::dynamic_pointer_cast<KIOFuseRemoteDirNode>(parentDirNode);
1541     if(!remoteDirNode)
1542     {
1543         // Directory not remote, so definitely does not exist
1544         fuse_reply_err(req, ENOENT);
1545         return;
1546     }
1547 
1548     // Not in the local tree, but remote - try again
1549     that->awaitChildMounted(remoteDirNode, nodeName, [=](auto node, int error) {
1550         if(error && error != ENOENT)
1551             fuse_reply_err(req, error);
1552         else
1553             that->replyEntry(req, node);
1554     });
1555 }
1556 
1557 void KIOFuseVFS::forget(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup)
1558 {
1559     KIOFuseVFS *that = reinterpret_cast<KIOFuseVFS*>(fuse_req_userdata(req));
1560     if(auto node = that->nodeForIno(ino))
1561         that->decrementLookupCount(node, nlookup);
1562 
1563     fuse_reply_none(req);
1564 }
1565 
1566 std::shared_ptr<KIOFuseNode> KIOFuseVFS::nodeForIno(const fuse_ino_t ino) const
1567 {
1568     auto it = m_nodes.find(ino);
1569     if(it == m_nodes.end())
1570         return nullptr;
1571 
1572     return it->second;
1573 }
1574 
1575 void KIOFuseVFS::reparentNode(const std::shared_ptr<KIOFuseNode> &node, fuse_ino_t newParentIno)
1576 {
1577     if(node->m_parentIno == newParentIno)
1578         return;
1579 
1580     if(node->m_parentIno != KIOFuseIno::Invalid)
1581     {
1582         // Remove from old parent's children list
1583         if(auto parentDir = std::dynamic_pointer_cast<KIOFuseDirNode>(nodeForIno(node->m_parentIno)))
1584         {
1585             auto &childrenList = parentDir->m_childrenInos;
1586             auto it = std::find(begin(childrenList), end(childrenList), node->m_stat.st_ino);
1587             if(it != childrenList.end())
1588                 childrenList.erase(it);
1589             else
1590                 qWarning(KIOFUSE_LOG) << "Tried to reparent node with broken parent link";
1591         }
1592         else
1593             qWarning(KIOFUSE_LOG) << "Tried to reparent node with invalid parent";
1594     }
1595 
1596     node->m_parentIno = newParentIno;
1597 
1598     if(node->m_parentIno != KIOFuseIno::Invalid)
1599     {
1600         // Add to new parent's children list
1601         if(auto parentDir = std::dynamic_pointer_cast<KIOFuseDirNode>(nodeForIno(node->m_parentIno)))
1602             parentDir->m_childrenInos.push_back(node->m_stat.st_ino);
1603         else
1604             qWarning(KIOFUSE_LOG) << "Tried to insert node with invalid parent";
1605     }
1606 }
1607 
1608 fuse_ino_t KIOFuseVFS::insertNode(const std::shared_ptr<KIOFuseNode> &node, fuse_ino_t ino)
1609 {
1610     if(ino == KIOFuseIno::Invalid)
1611     {
1612         // Allocate a free inode number
1613         ino = m_nextIno;
1614         while(ino < KIOFuseIno::DynamicStart || m_nodes.find(ino) != m_nodes.end())
1615             ino++;
1616 
1617         m_nextIno = ino + 1;
1618     }
1619 
1620     m_nodes[ino] = node;
1621 
1622     // Adjust internal ino
1623     node->m_stat.st_ino = ino;
1624 
1625     if(node->m_parentIno != KIOFuseIno::Invalid)
1626     {
1627         // Add to parent's child
1628         if(auto parentDir = std::dynamic_pointer_cast<KIOFuseDirNode>(nodeForIno(node->m_parentIno)))
1629             parentDir->m_childrenInos.push_back(ino);
1630         else
1631             qWarning(KIOFUSE_LOG) << "Tried to insert node with invalid parent";
1632     }
1633 
1634     return ino;
1635 }
1636 
1637 QUrl KIOFuseVFS::localPathToRemoteUrl(const QString& localPath) const
1638 {
1639     auto node = nodeForIno(KIOFuseIno::Root);
1640     auto pathSegments = localPath.split(QStringLiteral("/"));
1641     for(int i = 0; i < pathSegments.size(); ++i)
1642     {
1643         auto dirNode = std::dynamic_pointer_cast<KIOFuseDirNode>(node);
1644         if(!dirNode || !(node = nodeByName(dirNode, pathSegments[i])))
1645             break;
1646 
1647         auto url = remoteUrl(node);
1648         if(!url.isEmpty())
1649             return addPathElements(url, pathSegments.mid(i + 1));
1650     }
1651     return {};
1652 }
1653 
1654 QUrl KIOFuseVFS::remoteUrl(const std::shared_ptr<const KIOFuseNode> &node) const
1655 {
1656     QStringList path;
1657     for(const KIOFuseNode *currentNode = node.get(); currentNode != nullptr; currentNode = nodeForIno(currentNode->m_parentIno).get())
1658     {
1659         auto remoteDirNode = dynamic_cast<const KIOFuseRemoteNodeInfo*>(currentNode);
1660         if(remoteDirNode && !remoteDirNode->m_overrideUrl.isEmpty())
1661             // Origin found - add path and return
1662             return addPathElements(remoteDirNode->m_overrideUrl, path);
1663 
1664         path.prepend(currentNode->m_nodeName);
1665     }
1666 
1667     // No origin found until the root - return an invalid URL
1668     return {};
1669 }
1670 
1671 QString KIOFuseVFS::virtualPath(const std::shared_ptr<KIOFuseNode> &node) const
1672 {
1673     QStringList path;
1674     for(const KIOFuseNode *currentNode = node.get(); currentNode != nullptr; currentNode = nodeForIno(currentNode->m_parentIno).get())
1675         path.prepend(currentNode->m_nodeName);
1676 
1677     return path.join(QLatin1Char('/'));
1678 }
1679 
1680 void KIOFuseVFS::fillStatForFile(struct stat &attr)
1681 {
1682     static uid_t uid = getuid();
1683     static gid_t gid = getgid();
1684 
1685     attr.st_nlink = 1;
1686     attr.st_mode = S_IFREG | 0755;
1687     attr.st_uid = uid;
1688     attr.st_gid = gid;
1689     attr.st_size = 0;
1690     attr.st_blksize = 512;
1691     // This is set to match st_size by replyAttr
1692     attr.st_blocks = 0;
1693 
1694     clock_gettime(CLOCK_REALTIME, &attr.st_atim);
1695     attr.st_mtim = attr.st_atim;
1696     attr.st_ctim = attr.st_atim;
1697 }
1698 
1699 void KIOFuseVFS::incrementLookupCount(const std::shared_ptr<KIOFuseNode> &node, uint64_t delta)
1700 {
1701     if(node->m_lookupCount + delta < node->m_lookupCount)
1702         qWarning(KIOFUSE_LOG) << "Lookup count overflow!";
1703     else
1704         node->m_lookupCount += delta;
1705 }
1706 
1707 void KIOFuseVFS::decrementLookupCount(const std::shared_ptr<KIOFuseNode> node, uint64_t delta)
1708 {
1709     if(node->m_lookupCount < delta)
1710         qWarning(KIOFUSE_LOG) << "Tried to set lookup count negative!";
1711     else
1712         node->m_lookupCount -= delta;
1713 
1714     if(node->m_parentIno == KIOFuseIno::DeletedRoot && node->m_lookupCount == 0)
1715     {
1716         // Delete the node
1717         m_dirtyNodes.extract(node->m_stat.st_ino);
1718         reparentNode(node, KIOFuseIno::Invalid);
1719         m_nodes.erase(m_nodes.find(node->m_stat.st_ino));
1720     }
1721 }
1722 
1723 void KIOFuseVFS::markNodeDeleted(const std::shared_ptr<KIOFuseNode> &node)
1724 {
1725     if(auto dirNode = std::dynamic_pointer_cast<KIOFuseDirNode>(node))
1726     {
1727         // Mark all children as deleted first (flatten the hierarchy)
1728         for (auto childIno : std::vector<fuse_ino_t>(dirNode->m_childrenInos))
1729             if(auto childNode = nodeForIno(childIno))
1730                 markNodeDeleted(childNode);
1731     }
1732 
1733     qDebug(KIOFUSE_LOG) << "Marking node" << node->m_nodeName << "as deleted";
1734 
1735     reparentNode(node, KIOFuseIno::DeletedRoot);
1736     decrementLookupCount(node, 0); // Trigger reevaluation
1737 }
1738 
1739 void KIOFuseVFS::replyAttr(fuse_req_t req, std::shared_ptr<KIOFuseNode> node)
1740 {
1741     // Set st_blocks accordingly
1742     node->m_stat.st_blocks = (node->m_stat.st_size + 512 - 1) / 512;
1743 
1744     // TODO: Validity timeout?
1745     fuse_reply_attr(req, &node->m_stat, 0);
1746 }
1747 
1748 void KIOFuseVFS::replyEntry(fuse_req_t req, std::shared_ptr<KIOFuseNode> node)
1749 {
1750     // Zero means invalid entry. Compared to an ENOENT reply, the kernel can cache this.
1751     struct fuse_entry_param entry {};
1752 
1753     if(node)
1754     {
1755         incrementLookupCount(node);
1756 
1757         entry.ino = node->m_stat.st_ino;
1758         entry.attr_timeout = 0.0;
1759         entry.entry_timeout = 0.0;
1760         entry.attr = node->m_stat;
1761     }
1762 
1763     fuse_reply_entry(req, &entry);
1764 }
1765 
1766 std::shared_ptr<KIOFuseNode> KIOFuseVFS::createNodeFromUDSEntry(const KIO::UDSEntry &entry, const fuse_ino_t parentIno, const QString &nameOverride)
1767 {
1768     QString name = nameOverride;
1769     if(name.isEmpty())
1770         name = entry.stringValue(KIO::UDSEntry::UDS_NAME);
1771     if(!isValidFilename(name))
1772         return nullptr; // Reject invalid names
1773 
1774     // Create a stat struct with default values
1775     struct stat attr = {};
1776     fillStatForFile(attr);
1777     attr.st_size = entry.numberValue(KIO::UDSEntry::UDS_SIZE, 1);
1778     attr.st_mode = entry.numberValue(KIO::UDSEntry::UDS_ACCESS, entry.isDir() ? 0755 : 0644);
1779     attr.st_mode &= ~S_IFMT; // Set by us further down
1780     if(entry.contains(KIO::UDSEntry::UDS_MODIFICATION_TIME))
1781     {
1782         attr.st_mtim.tv_sec = entry.numberValue(KIO::UDSEntry::UDS_MODIFICATION_TIME);
1783         attr.st_mtim.tv_nsec = 0;
1784     }
1785     if(entry.contains(KIO::UDSEntry::UDS_ACCESS_TIME))
1786     {
1787         attr.st_atim.tv_sec = entry.numberValue(KIO::UDSEntry::UDS_ACCESS_TIME);
1788         attr.st_atim.tv_nsec = 0;
1789     }
1790     // No support for ctim/btim in KIO...
1791 
1792     // Setting UID and GID here to UDS_USER/UDS_GROUP respectively does not lead to the expected
1793     // results as those values might only be meaningful on the remote side.
1794     // As access checks are only performed by the remote side, it shouldn't matter much though.
1795     // It's necessary to make chown/chmod meaningful.
1796     if(entry.contains(KIO::UDSEntry::UDS_USER))
1797     {
1798         QString user = entry.stringValue(KIO::UDSEntry::UDS_USER);
1799         if(auto *pw = getpwnam(user.toUtf8().data()))
1800             attr.st_uid = pw->pw_uid;
1801     }
1802     if(entry.contains(KIO::UDSEntry::UDS_GROUP))
1803     {
1804         QString group = entry.stringValue(KIO::UDSEntry::UDS_GROUP);
1805         if(auto *gr = getgrnam(group.toUtf8().data()))
1806             attr.st_gid = gr->gr_gid;
1807     }
1808 
1809     if(entry.contains(KIO::UDSEntry::UDS_LOCAL_PATH) || entry.contains(KIO::UDSEntry::UDS_TARGET_URL))
1810     {
1811         // Create as symlink if possible
1812         QString target = entry.stringValue(KIO::UDSEntry::UDS_LOCAL_PATH);
1813         if(target.isEmpty())
1814             target = QUrl(entry.stringValue(KIO::UDSEntry::UDS_TARGET_URL)).toLocalFile();
1815 
1816         if(!target.isEmpty())
1817         {
1818             // Symlink to local file/folder
1819             attr.st_mode |= S_IFLNK;
1820             auto ret = std::make_shared<KIOFuseSymLinkNode>(parentIno, name, attr);
1821             ret->m_target = target;
1822             ret->m_stat.st_size = ret->m_target.toUtf8().length();
1823             return ret;
1824         }
1825         else if(entry.isLink())
1826             return nullptr; // Does this even happen?
1827         else if(entry.isDir())
1828             return nullptr; // Maybe create a mountpoint (remote dir with override URL) here?
1829         else // Regular file pointing to URL
1830         {
1831             attr.st_mode |= S_IFREG;
1832             std::shared_ptr<KIOFuseRemoteFileNode> ret = nullptr;
1833             const QUrl nodeUrl = QUrl{entry.stringValue(KIO::UDSEntry::UDS_TARGET_URL)};
1834             if(nodeUrl.isEmpty())
1835                 return nullptr;
1836             if(m_useFileJob && KProtocolManager::supportsOpening(nodeUrl) && KProtocolManager::supportsTruncating(nodeUrl))
1837                 ret = std::make_shared<KIOFuseRemoteFileJobBasedFileNode>(parentIno, name, attr);
1838             else
1839                 ret = std::make_shared<KIOFuseRemoteCacheBasedFileNode>(parentIno, name, attr);
1840             ret->m_overrideUrl = nodeUrl;
1841             return ret;
1842         }
1843     }
1844     else if(entry.isLink()) // Check for link first as isDir can also be a link
1845     {
1846         attr.st_mode |= S_IFLNK;
1847         auto ret = std::make_shared<KIOFuseSymLinkNode>(parentIno, name, attr);
1848         ret->m_target = entry.stringValue(KIO::UDSEntry::UDS_LINK_DEST);
1849         attr.st_size = ret->m_target.size();
1850         return ret;
1851     }
1852     else if(entry.isDir())
1853     {
1854         attr.st_mode |= S_IFDIR;
1855         return std::make_shared<KIOFuseRemoteDirNode>(parentIno, name, attr);
1856     }
1857     else // it's a regular file
1858     {
1859         attr.st_mode |= S_IFREG;
1860         const QUrl nodeUrl = remoteUrl(nodeForIno(parentIno));
1861         if(m_useFileJob && KProtocolManager::supportsOpening(nodeUrl) && KProtocolManager::supportsTruncating(nodeUrl))
1862             return std::make_shared<KIOFuseRemoteFileJobBasedFileNode>(parentIno, name, attr);
1863         else
1864             return std::make_shared<KIOFuseRemoteCacheBasedFileNode>(parentIno, name, attr);
1865     }
1866 }
1867 
1868 std::shared_ptr<KIOFuseNode> KIOFuseVFS::updateNodeFromUDSEntry(const std::shared_ptr<KIOFuseNode> &node, const KIO::UDSEntry &entry)
1869 {
1870     qDebug(KIOFUSE_LOG) << "Updating attributes of" << node->m_nodeName;
1871 
1872     // Updating a node works by creating a new node and merging their attributes
1873     // (both m_stat and certain other class attributes) together.
1874     // This is broadly done with node->m_stat = newNode->m_stat;
1875     // However, there are some things we may not want to copy straight from the
1876     // new node into the node to be updated and so we update newNode as
1877     // appropriate before merging.
1878     auto newNode = createNodeFromUDSEntry(entry, node->m_parentIno, node->m_nodeName);
1879     if (!newNode)
1880     {
1881         qWarning(KIOFUSE_LOG) << "Could not create new node for" << node->m_nodeName;
1882         return node;
1883     }
1884 
1885 #pragma GCC diagnostic push
1886 #pragma GCC diagnostic ignored "-Wpragmas" // GCC does not know the diagnostic below and warns m(
1887 #pragma GCC diagnostic ignored "-Wpotentially-evaluated-expression"
1888     if (typeid(*newNode.get()) != typeid(*node.get()))
1889 #pragma GCC diagnostic pop
1890     {
1891         if(node->m_parentIno != KIOFuseIno::DeletedRoot)
1892         {
1893             markNodeDeleted(node);
1894             insertNode(newNode);
1895             return newNode;
1896         }
1897         return node;
1898     }
1899 
1900     const bool newMtimNewer = node->m_stat.st_mtim < newNode->m_stat.st_mtim;
1901     // Take the newer time value
1902     if (!newMtimNewer)
1903         newNode->m_stat.st_mtim = node->m_stat.st_mtim;
1904 
1905     if (newNode->m_stat.st_atim < node->m_stat.st_atim)
1906         newNode->m_stat.st_atim = node->m_stat.st_atim;
1907 
1908     if (auto cacheBasedFileNode = std::dynamic_pointer_cast<KIOFuseRemoteCacheBasedFileNode>(node))
1909     {
1910         if(newMtimNewer || newNode->m_stat.st_size != node->m_stat.st_size)
1911         {
1912             // Someone has changed something server side, lets get those changes.
1913             if(cacheBasedFileNode->m_cacheDirty || cacheBasedFileNode->m_flushRunning)
1914             {
1915                 if(newMtimNewer)
1916                     qWarning(KIOFUSE_LOG) << cacheBasedFileNode->m_nodeName << "cache is dirty but file has also been changed by something else";
1917 
1918                 newNode->m_stat.st_size = cacheBasedFileNode->m_stat.st_size;
1919             }
1920             else if(cacheBasedFileNode->m_localCache && cacheBasedFileNode->cacheIsComplete())
1921             {
1922                 // Our cache isn't dirty but the file has changed, our current cache is invalid
1923                 // and we can safely get rid of it.
1924                 fclose(cacheBasedFileNode->m_localCache);
1925                 cacheBasedFileNode->m_cacheSize = 0;
1926                 cacheBasedFileNode->m_cacheComplete = false;
1927                 cacheBasedFileNode->m_localCache = nullptr;
1928             }
1929         }
1930     }
1931     else if (auto oldSymlinkNode = std::dynamic_pointer_cast<KIOFuseSymLinkNode>(node))
1932     {
1933         auto newSymlinkNode = std::dynamic_pointer_cast<KIOFuseSymLinkNode>(newNode);
1934         oldSymlinkNode->m_target = newSymlinkNode->m_target;
1935     }
1936 
1937     auto oldRemoteNode = std::dynamic_pointer_cast<KIOFuseRemoteNodeInfo>(node);
1938     auto newRemoteNode = std::dynamic_pointer_cast<KIOFuseRemoteNodeInfo>(newNode);
1939 
1940     oldRemoteNode->m_lastStatRefresh = std::chrono::steady_clock::now();
1941 
1942     // oldRemoteNode->m_overrideUrl could've been set by mountUrl, don't clear it
1943     if (!newRemoteNode->m_overrideUrl.isEmpty())
1944         oldRemoteNode->m_overrideUrl = newRemoteNode->m_overrideUrl;
1945 
1946     // Preserve the inode number
1947     newNode->m_stat.st_ino = node->m_stat.st_ino;
1948     node->m_stat = newNode->m_stat;
1949     return node;
1950 }
1951 
1952 void KIOFuseVFS::awaitBytesAvailable(const std::shared_ptr<KIOFuseRemoteCacheBasedFileNode> &node, off_t bytes, const std::function<void(int error)> &callback)
1953 {
1954     if(bytes < 0)
1955     {
1956         qWarning(KIOFUSE_LOG) << "Negative size passed to awaitBytesAvailable";
1957         return callback(EINVAL);
1958     }
1959 
1960     if(node->m_localCache && node->m_cacheSize >= bytes)
1961         return callback(0); // Already available
1962     else if(node->cacheIsComplete()) // Full cache is available...
1963         return callback(ESPIPE); // ...but less than requested.
1964 
1965     if(!node->m_localCache)
1966     {
1967         if(node->m_parentIno == KIOFuseIno::DeletedRoot)
1968             return callback(ENOENT);
1969 
1970         // Create a temporary file
1971         node->m_localCache = tmpfile();
1972 
1973         if(!node->m_localCache)
1974             return callback(errno);
1975 
1976         // Request the file
1977         qDebug(KIOFUSE_LOG) << "Fetching cache for" << node->m_nodeName;
1978         auto *job = KIO::get(remoteUrl(node));
1979         connect(job, &KIO::TransferJob::data, [=](auto *job, const QByteArray &data) {
1980             // Nobody needs the data anymore? Drop the cache.
1981             if(node->m_openCount == 0 && !node->m_cacheDirty && !node->m_flushRunning)
1982             {
1983                 // KJob::Quietly would break the cache in the result handler while
1984                 // the error handler sets up the node state just right.
1985                 job->kill(KJob::EmitResult);
1986                 qDebug(KIOFUSE_LOG) << "Stopped filling the cache of" << node->m_nodeName;
1987                 return;
1988             }
1989 
1990             int cacheFd = fileno(node->m_localCache);
1991             if(lseek(cacheFd, 0, SEEK_END) == -1
1992                || !sane_write(cacheFd, data.data(), data.size()))
1993                 Q_EMIT node->localCacheChanged(errno);
1994             else
1995             {
1996                 node->m_cacheSize += data.size();
1997                 Q_EMIT node->localCacheChanged(0);
1998             }
1999         });
2000         connect(job, &KIO::TransferJob::result, [=] {
2001             if(job->error())
2002             {
2003                 // It's possible that the cache was written to meanwhile - that's bad.
2004                 // TODO: Is it possible to recover?
2005                 node->m_cacheDirty = false;
2006 
2007                 fclose(node->m_localCache);
2008                 node->m_cacheSize = 0;
2009                 node->m_cacheComplete = false;
2010                 node->m_localCache = nullptr;
2011                 Q_EMIT node->localCacheChanged(kioErrorToFuseError(job->error()));
2012             }
2013             else
2014             {
2015                 // Might be different from the attr size meanwhile, use the more recent value.
2016                 // This also ensures that the cache is seen as complete.
2017                 node->m_stat.st_size = node->m_cacheSize;
2018                 node->m_cacheComplete = true;
2019                 Q_EMIT node->localCacheChanged(0);
2020             }
2021         });
2022     }
2023 
2024     // Using a unique_ptr here to let the lambda disconnect the connection itself
2025     auto connection = std::make_unique<QMetaObject::Connection>();
2026     auto &conn = *connection;
2027     conn = connect(node.get(), &KIOFuseRemoteCacheBasedFileNode::localCacheChanged,
2028                    [=, connection = std::move(connection)](int error) {
2029         if(error)
2030         {
2031             callback(error);
2032             node->disconnect(*connection);
2033         }
2034         else if(node->m_cacheSize >= bytes) // Requested data available
2035         {
2036             callback(0);
2037             node->disconnect(*connection);
2038         }
2039         else if(node->cacheIsComplete()) // Full cache is available...
2040         {
2041             // ...but less than requested.
2042             callback(ESPIPE);
2043             node->disconnect(*connection);
2044         }
2045         // else continue waiting until the above happens
2046     }
2047     );
2048 }
2049 
2050 void KIOFuseVFS::awaitCacheComplete(const std::shared_ptr<KIOFuseRemoteCacheBasedFileNode> &node, const std::function<void (int)> &callback)
2051 {
2052     return awaitBytesAvailable(node, std::numeric_limits<off_t>::max(), [callback](int error) {
2053         // ESPIPE == cache complete, but less than the requested size, which is expected.
2054         return callback(error == ESPIPE ? 0 : error);
2055     });
2056 }
2057 
2058 void KIOFuseVFS::awaitChildrenComplete(const std::shared_ptr<KIOFuseDirNode> &node, const std::function<void (int)> &callback)
2059 {
2060     auto remoteNode = std::dynamic_pointer_cast<KIOFuseRemoteDirNode>(node);
2061     if(!remoteNode)
2062         return callback(0); // Not a remote node
2063 
2064     if(!remoteNode->haveChildrenTimedOut())
2065         return callback(0); // Children complete and up to date
2066 
2067     if(!remoteNode->m_childrenRequested)
2068     {
2069         if(node->m_parentIno == KIOFuseIno::DeletedRoot)
2070             return callback(0);
2071 
2072         auto childrenNotSeen = std::make_shared<std::vector<fuse_ino_t>>(remoteNode->m_childrenInos);
2073 
2074         // List the remote dir
2075         auto refreshTime = std::chrono::steady_clock::now();
2076         auto *job = KIO::listDir(remoteUrl(remoteNode));
2077         connect(job, &KIO::ListJob::entries, [=](auto *job, const KIO::UDSEntryList &entries) {
2078             for(auto &entry : entries)
2079             {
2080                 // Inside the loop because refreshing "." might drop it
2081                 if(remoteNode->m_parentIno == KIOFuseIno::DeletedRoot)
2082                 {
2083                     job->kill(KJob::EmitResult);
2084                     return;
2085                 }
2086 
2087                 QString name = entry.stringValue(KIO::UDSEntry::UDS_NAME);
2088 
2089                 // Refresh "." and ignore ".."
2090                 if(name == QStringLiteral("."))
2091                 {
2092                     updateNodeFromUDSEntry(remoteNode, entry);
2093                     continue;
2094                 }
2095                 else if(name == QStringLiteral(".."))
2096                    continue;
2097 
2098                 auto childrenNode = nodeByName(remoteNode, name);
2099                 if(childrenNode)
2100                 {
2101                     auto it = std::find(begin(*childrenNotSeen), end(*childrenNotSeen),
2102                                         childrenNode->m_stat.st_ino);
2103                     if(it != end(*childrenNotSeen))
2104                         childrenNotSeen->erase(it);
2105 
2106                     // Try to update existing node
2107                     updateNodeFromUDSEntry(childrenNode, entry);
2108                     continue;
2109                 }
2110 
2111                 childrenNode = createNodeFromUDSEntry(entry, remoteNode->m_stat.st_ino, name);
2112                 if(!childrenNode)
2113                 {
2114                     qWarning(KIOFUSE_LOG) << "Could not create node for" << name;
2115                     continue;
2116                 }
2117 
2118                 insertNode(childrenNode);
2119             }
2120         });
2121         connect(job, &KIO::ListJob::result, [=] {
2122             remoteNode->m_childrenRequested = false;
2123 
2124             if(job->error() && job->error() != KJob::KilledJobError)
2125             {
2126                 Q_EMIT remoteNode->gotChildren(kioErrorToFuseError(job->error()));
2127                 return;
2128             }
2129 
2130             for(auto ino : *childrenNotSeen)
2131             {
2132                 auto childNode = nodeForIno(ino);
2133                 // Was not refreshed as part of this listDir operation, drop it
2134                 if(childNode && childNode->m_parentIno == node->m_stat.st_ino)
2135                     markNodeDeleted(childNode);
2136             }
2137 
2138             remoteNode->m_lastChildrenRefresh = refreshTime;
2139             Q_EMIT remoteNode->gotChildren(0);
2140         });
2141 
2142         remoteNode->m_childrenRequested = true;
2143     }
2144 
2145     // Using a unique_ptr here to let the lambda disconnect the connection itself
2146     auto connection = std::make_unique<QMetaObject::Connection>();
2147     auto &conn = *connection;
2148     conn = connect(remoteNode.get(), &KIOFuseRemoteDirNode::gotChildren,
2149                    [=, connection = std::move(connection)](int error) {
2150         callback(error);
2151         remoteNode->disconnect(*connection);
2152     }
2153     );
2154 }
2155 
2156 void KIOFuseVFS::markCacheDirty(const std::shared_ptr<KIOFuseRemoteCacheBasedFileNode> &node)
2157 {
2158     if(node->m_cacheDirty)
2159         return; // Already dirty, nothing to do
2160 
2161     node->m_cacheDirty = true;
2162     m_dirtyNodes.insert(node->m_stat.st_ino);
2163 }
2164 
2165 void KIOFuseVFS::awaitNodeFlushed(const std::shared_ptr<KIOFuseRemoteCacheBasedFileNode> &node, const std::function<void (int)> &callback)
2166 {
2167     if(!node->m_cacheDirty && !node->m_flushRunning)
2168         return callback(0); // Nothing to flush/wait for
2169 
2170     if(node->m_parentIno == KIOFuseIno::DeletedRoot)
2171     {
2172         // Important: This is before marking it as flushed as it can be linked back.
2173         qDebug(KIOFUSE_LOG) << "Not flushing unlinked node" << node->m_nodeName;
2174         return callback(0);
2175     }
2176 
2177     // Don't send incomplete data
2178     if(!node->cacheIsComplete())
2179     {
2180         qDebug(KIOFUSE_LOG) << "Deferring flushing of node" << node->m_nodeName << "until cache complete";
2181         return awaitCacheComplete(node, [=](int error) {
2182             if(error)
2183                 callback(error);
2184             else
2185                 awaitNodeFlushed(node, callback);
2186         });
2187     }
2188 
2189     if(!node->m_flushRunning)
2190     {
2191         qDebug(KIOFUSE_LOG) << "Flushing node" << node->m_nodeName;
2192 
2193         // Clear the flag now to not lose any writes that happen while sending data
2194         node->m_cacheDirty = false;
2195         node->m_flushRunning = true;
2196 
2197         auto *job = KIO::put(remoteUrl(node), -1, KIO::Overwrite);
2198         job->setTotalSize(node->m_cacheSize);
2199 
2200         off_t bytesSent = 0; // Modified inside the lambda
2201         connect(job, &KIO::TransferJob::dataReq, [=](auto *job, QByteArray &data) mutable {
2202             Q_UNUSED(job);
2203 
2204             // Someone truncated the file?
2205             if(node->m_cacheSize <= bytesSent)
2206                 return;
2207 
2208             // Somebody wrote to the cache whilst sending data.
2209             // Kill the job to save time and try again.
2210             // However, set a limit to how many times we do this consecutively.
2211             if(node->m_cacheDirty && node->m_numKilledJobs < 2 && job->percent() < 85)
2212             {
2213                 job->kill(KJob::Quietly);
2214                 node->m_numKilledJobs++;
2215                 node->m_flushRunning = false;
2216                 awaitNodeFlushed(node, [](int){});
2217                 return;
2218             }
2219 
2220             off_t toSend = std::min(node->m_cacheSize - bytesSent, off_t(14*1024*1024ul)); // 14MiB max
2221             data.resize(toSend);
2222 
2223             // Read the cache file into the buffer
2224             int cacheFd = fileno(node->m_localCache);
2225             if(lseek(cacheFd, bytesSent, SEEK_SET) == -1
2226                || !sane_read(cacheFd, data.data(), toSend))
2227             {
2228                 qWarning(KIOFUSE_LOG) << "Failed to read cache:" << strerror(errno);
2229                 job->kill(KJob::EmitResult);
2230                 return;
2231             }
2232 
2233             bytesSent += toSend;
2234         });
2235         connect(job, &KIO::TransferJob::result, [=] {
2236             node->m_flushRunning = false;
2237 
2238             if(job->error())
2239             {
2240                 qWarning(KIOFUSE_LOG) << "Failed to send data:" << job->errorString();
2241                 markCacheDirty(node); // Try again
2242                 Q_EMIT node->cacheFlushed(kioErrorToFuseError(job->error()));
2243                 return;
2244             }
2245 
2246             if(!node->m_cacheDirty)
2247             {
2248                 // Nobody wrote to the cache while sending data
2249                 m_dirtyNodes.extract(node->m_stat.st_ino);
2250                 node->m_numKilledJobs = 0;
2251                 Q_EMIT node->cacheFlushed(0);
2252             }
2253             else
2254                 awaitNodeFlushed(node, [](int){});
2255         });
2256     }
2257 
2258     // Using a unique_ptr here to let the lambda disconnect the connection itself
2259     auto connection = std::make_unique<QMetaObject::Connection>();
2260     auto &conn = *connection;
2261     conn = connect(node.get(), &KIOFuseRemoteCacheBasedFileNode::cacheFlushed,
2262                    [=, connection = std::move(connection)](int error) {
2263         callback(error);
2264         node->disconnect(*connection);
2265     }
2266     );
2267 }
2268 
2269 void KIOFuseVFS::awaitAttrRefreshed(const std::shared_ptr<KIOFuseNode> &node, const std::function<void (int)> &callback)
2270 {
2271     auto remoteNode = std::dynamic_pointer_cast<KIOFuseRemoteNodeInfo>(node);
2272     if(!remoteNode || !remoteNode->hasStatTimedOut())
2273         return callback(0); // Node not remote, or it hasn't timed out yet
2274 
2275     // To do the same request as the initial mount or lookup, build the URL from the parent
2276     auto parentNode = nodeForIno(node->m_parentIno);
2277     auto remoteParentNode = std::dynamic_pointer_cast<KIOFuseRemoteDirNode>(parentNode);
2278     QUrl url;
2279     if(!remoteParentNode || (url = remoteUrl(parentNode)).isEmpty())
2280         return callback(0); // Parent not remote
2281 
2282     if(!remoteNode->m_statRequested)
2283     {
2284         qDebug(KIOFUSE_LOG) << "Refreshing attributes of node" << node->m_nodeName;
2285         remoteNode->m_statRequested = true;
2286         awaitChildMounted(remoteParentNode, node->m_nodeName, [=] (auto mountedNode, int error) {
2287             if(error == ENOENT)
2288                 markNodeDeleted(node);
2289 
2290             Q_UNUSED(mountedNode);
2291             remoteNode->m_statRequested = false;
2292             Q_EMIT remoteNode->statRefreshed(error);
2293         });
2294     }
2295 
2296     // Using a unique_ptr here to let the lambda disconnect the connection itself
2297     auto connection = std::make_unique<QMetaObject::Connection>();
2298     auto &conn = *connection;
2299     conn = connect(remoteNode.get(), &KIOFuseRemoteNodeInfo::statRefreshed,
2300                    [=, connection = std::move(connection)](int error) {
2301         callback(error);
2302         remoteNode->disconnect(*connection);
2303     }
2304     );
2305 }
2306 
2307 void KIOFuseVFS::awaitChildMounted(const std::shared_ptr<KIOFuseRemoteDirNode> &parent, const QString &name, const std::function<void (const std::shared_ptr<KIOFuseNode> &, int)> &callback)
2308 {
2309     auto url = addPathElements(remoteUrl(parent), {name});
2310     if(url.isEmpty()) // Not remote?
2311     {
2312         if(auto node = nodeByName(parent, name))
2313             return callback(node, 0);
2314         else
2315             return callback({}, ENOENT);
2316     }
2317 
2318     qDebug(KIOFUSE_LOG) << "Mounting" << url.toDisplayString();
2319     auto statJob = KIO::stat(url);
2320     statJob->setSide(KIO::StatJob::SourceSide); // Be "optimistic" to allow accessing
2321                                                 // files over plain HTTP
2322     connect(statJob, &KIO::StatJob::result, [=] {
2323         if(statJob->error())
2324         {
2325             qDebug(KIOFUSE_LOG) << statJob->errorString();
2326             callback(nullptr, kioErrorToFuseError(statJob->error()));
2327             return;
2328         }
2329 
2330         // Finally create the last component
2331         auto finalNode = nodeByName(parent, name);
2332         if(finalNode)
2333             // Note that node may fail to update, but this type of error is ignored.
2334             finalNode = updateNodeFromUDSEntry(finalNode, statJob->statResult());
2335         else
2336         {
2337             // The remote name (statJob->statResult().stringValue(KIO::UDSEntry::UDS_NAME)) has to be
2338             // ignored as it can be different from the path. e.g. tar:/foo.tar/ is "/"
2339             finalNode = createNodeFromUDSEntry(statJob->statResult(), parent->m_stat.st_ino, name);
2340             if(!finalNode)
2341                 return callback(nullptr, EIO);
2342 
2343             insertNode(finalNode);
2344         }
2345 
2346         callback(finalNode, 0);
2347     });
2348 }
2349 
2350 QUrl KIOFuseVFS::originOfUrl(const QUrl &url)
2351 {
2352     QUrl originUrl = url;
2353     if(originUrl.path().startsWith(QLatin1Char('/')))
2354         originUrl.setPath(QStringLiteral("/"));
2355     else
2356         originUrl.setPath({});
2357 
2358     return originUrl;
2359 }
2360 
2361 bool KIOFuseVFS::setupSignalHandlers()
2362 {
2363     // Create required socketpair for custom signal handling
2364     if (socketpair(AF_UNIX, SOCK_STREAM, 0, signalFd)) {
2365         return false;
2366     }
2367     m_signalNotifier = std::make_unique<QSocketNotifier>(signalFd[1], QSocketNotifier::Read, this);
2368     m_signalNotifier->connect(m_signalNotifier.get(), &QSocketNotifier::activated, this, &KIOFuseVFS::exitHandler);
2369     
2370     struct sigaction sig;
2371 
2372     sig.sa_handler = KIOFuseVFS::signalHandler;
2373     sigemptyset(&sig.sa_mask);
2374     sig.sa_flags = SA_RESTART;
2375 
2376     if (sigaction(SIGHUP, &sig, 0))
2377         return false;
2378     if (sigaction(SIGTERM, &sig, 0))
2379         return false;
2380     if (sigaction(SIGINT, &sig, 0))
2381         return false;
2382 
2383     return true;
2384 }
2385 
2386 bool KIOFuseVFS::removeSignalHandlers() 
2387 {
2388     m_signalNotifier.reset();
2389     ::close(signalFd[0]);
2390     ::close(signalFd[1]);
2391 
2392     struct sigaction sig;
2393 
2394     sig.sa_handler = SIG_DFL;
2395     sigemptyset(&sig.sa_mask);
2396     sig.sa_flags = SA_RESTART;
2397 
2398     if (sigaction(SIGHUP, &sig, 0))
2399         return false;
2400     if (sigaction(SIGTERM, &sig, 0))
2401         return false;
2402     if (sigaction(SIGINT, &sig, 0))
2403         return false;
2404 
2405     return true;
2406 }
2407 
2408 void KIOFuseVFS::exitHandler() 
2409 {
2410     m_signalNotifier->setEnabled(false);
2411     int tmp;
2412     ::read(signalFd[1], &tmp, sizeof(tmp));
2413     stop();
2414 }
2415 
2416 void KIOFuseVFS::signalHandler(int signal) 
2417 {
2418     ::write(signalFd[0], &signal, sizeof(signal));
2419 }
2420 
2421 int KIOFuseVFS::kioErrorToFuseError(const int kioError) {
2422     switch (kioError) {
2423         case 0                                     : return 0; // No error
2424         case KIO::ERR_CANNOT_OPEN_FOR_READING      : return EIO;
2425         case KIO::ERR_CANNOT_OPEN_FOR_WRITING      : return EIO;
2426         case KIO::ERR_CANNOT_LAUNCH_PROCESS        : return EIO;
2427         case KIO::ERR_INTERNAL                     : return EIO;
2428         case KIO::ERR_MALFORMED_URL                : return EIO;
2429         case KIO::ERR_UNSUPPORTED_PROTOCOL         : return ENOPROTOOPT;
2430         case KIO::ERR_NO_SOURCE_PROTOCOL           : return ENOPROTOOPT;
2431         case KIO::ERR_UNSUPPORTED_ACTION           : return ENOTTY;
2432         case KIO::ERR_IS_DIRECTORY                 : return EISDIR;
2433         case KIO::ERR_IS_FILE                      : return EEXIST;
2434         case KIO::ERR_DOES_NOT_EXIST               : return ENOENT;
2435         case KIO::ERR_FILE_ALREADY_EXIST           : return EEXIST;
2436         case KIO::ERR_DIR_ALREADY_EXIST            : return EEXIST;
2437         case KIO::ERR_UNKNOWN_HOST                 : return EHOSTUNREACH;
2438         case KIO::ERR_ACCESS_DENIED                : return EPERM;
2439         case KIO::ERR_WRITE_ACCESS_DENIED          : return EPERM;
2440         case KIO::ERR_CANNOT_ENTER_DIRECTORY       : return EIO;
2441         case KIO::ERR_PROTOCOL_IS_NOT_A_FILESYSTEM : return EPROTOTYPE;
2442         case KIO::ERR_CYCLIC_LINK                  : return ELOOP;
2443         case KIO::ERR_USER_CANCELED                : return ECANCELED;
2444         case KIO::ERR_CYCLIC_COPY                  : return ELOOP;
2445         case KIO::ERR_CANNOT_CREATE_SOCKET         : return ENOTCONN;
2446         case KIO::ERR_CANNOT_CONNECT               : return ENOTCONN;
2447         case KIO::ERR_CONNECTION_BROKEN            : return ENOTCONN;
2448         case KIO::ERR_NOT_FILTER_PROTOCOL          : return EPROTOTYPE;
2449         case KIO::ERR_CANNOT_MOUNT                 : return EIO;
2450         case KIO::ERR_CANNOT_READ                  : return EIO;
2451         case KIO::ERR_CANNOT_WRITE                 : return EIO;
2452         case KIO::ERR_CANNOT_BIND                  : return EPERM;
2453         case KIO::ERR_CANNOT_LISTEN                : return EPERM;
2454         case KIO::ERR_CANNOT_ACCEPT                : return EPERM;
2455         case KIO::ERR_CANNOT_LOGIN                 : return ECONNREFUSED;
2456         case KIO::ERR_CANNOT_STAT                  : return EIO;
2457         case KIO::ERR_CANNOT_CLOSEDIR              : return EIO;
2458         case KIO::ERR_CANNOT_MKDIR                 : return EIO;
2459         case KIO::ERR_CANNOT_RMDIR                 : return EIO;
2460         case KIO::ERR_CANNOT_RESUME                : return ECONNABORTED;
2461         case KIO::ERR_CANNOT_RENAME                : return EIO;
2462         case KIO::ERR_CANNOT_CHMOD                 : return EIO;
2463         case KIO::ERR_CANNOT_DELETE                : return EIO;
2464         case KIO::ERR_SLAVE_DIED                   : return EIO;
2465         case KIO::ERR_OUT_OF_MEMORY                : return ENOMEM;
2466         case KIO::ERR_UNKNOWN_PROXY_HOST           : return EHOSTUNREACH;
2467         case KIO::ERR_CANNOT_AUTHENTICATE          : return EACCES;
2468         case KIO::ERR_ABORTED                      : return ECONNABORTED;
2469         case KIO::ERR_INTERNAL_SERVER              : return EPROTO;
2470         case KIO::ERR_SERVER_TIMEOUT               : return ETIMEDOUT;
2471         case KIO::ERR_SERVICE_NOT_AVAILABLE        : return ENOPROTOOPT;
2472         case KIO::ERR_UNKNOWN                      : return EIO;
2473         case KIO::ERR_UNKNOWN_INTERRUPT            : return EIO;
2474         case KIO::ERR_CANNOT_DELETE_ORIGINAL       : return EIO;
2475         case KIO::ERR_CANNOT_DELETE_PARTIAL        : return EIO;
2476         case KIO::ERR_CANNOT_RENAME_ORIGINAL       : return EIO;
2477         case KIO::ERR_CANNOT_RENAME_PARTIAL        : return EIO;
2478         case KIO::ERR_NEED_PASSWD                  : return EACCES;
2479         case KIO::ERR_CANNOT_SYMLINK               : return EIO;
2480         case KIO::ERR_NO_CONTENT                   : return EIO;
2481         case KIO::ERR_DISK_FULL                    : return ENOSPC;
2482         case KIO::ERR_IDENTICAL_FILES              : return EEXIST;
2483         case KIO::ERR_SLAVE_DEFINED                : return EIO;
2484         case KIO::ERR_UPGRADE_REQUIRED             : return EPROTOTYPE;
2485         case KIO::ERR_POST_DENIED                  : return EACCES;
2486         case KIO::ERR_CANNOT_SEEK                  : return EIO;
2487         case KIO::ERR_CANNOT_SETTIME               : return EIO;
2488         case KIO::ERR_CANNOT_CHOWN                 : return EIO;
2489         case KIO::ERR_POST_NO_SIZE                 : return EIO;
2490         case KIO::ERR_DROP_ON_ITSELF               : return EINVAL;
2491         case KIO::ERR_CANNOT_MOVE_INTO_ITSELF      : return EINVAL;
2492         case KIO::ERR_PASSWD_SERVER                : return EIO;
2493         case KIO::ERR_CANNOT_CREATE_SLAVE          : return EIO;
2494         case KIO::ERR_FILE_TOO_LARGE_FOR_FAT32     : return EFBIG;
2495         case KIO::ERR_OWNER_DIED                   : return EIO;
2496         default                                    : return EIO;
2497     }
2498 }