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

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 #pragma once
0008 
0009 #include <fuse_lowlevel.h>
0010 
0011 #include <functional>
0012 #include <memory>
0013 #include <set>
0014 #include <unordered_map>
0015 
0016 #include <QEventLoopLocker>
0017 #include <QObject>
0018 #include <QSocketNotifier>
0019 
0020 #include "kiofusenode.h"
0021 
0022 // Forward declarations
0023 namespace KIO { class UDSEntry; }
0024 
0025 enum KIOFuseIno : fuse_ino_t {
0026     /** Not reserved by the kernel, so used as a marker. */
0027     Invalid = 0,
0028     /** Defined by the kernel */
0029     Root = 1,
0030 
0031     /** The inode number of the parent of deleted nodes. */
0032     DeletedRoot,
0033 
0034     /** Dynamic allocation by insertNode starts here. */
0035     DynamicStart,
0036 };
0037 
0038 class KIOFuseVFS : public QObject
0039 {
0040     Q_OBJECT
0041 
0042 public:
0043     explicit KIOFuseVFS(QObject *parent = nullptr);
0044     ~KIOFuseVFS();
0045 
0046     /** Mounts the filesystem at mountpoint. Returns true on success. */
0047     bool start(fuse_args &args, const QString& mountpoint);
0048     /** Umounts the filesystem (if necessary) and flushes dirty nodes. */
0049     void stop();
0050     /** Designates whether KIOFuse should perform FileJob-based (KIO::open) I/O where possible. */
0051     void setUseFileJob(bool useFileJob);
0052     /** Runs KIO::stat on url and (if successful) creates an origin node at the lowest possible level.
0053       * Returns the relative path to where url is reachable in the callback. */
0054     void mountUrl(const QUrl &url, const std::function<void(const QString&, int)> &callback);
0055     /** Converts a local path into a remote URL if it is mounted within the VFS */
0056     QUrl localPathToRemoteUrl(const QString &localPath) const;
0057     /** Returns the path upwards until a root node. */
0058     QString virtualPath(const std::shared_ptr<KIOFuseNode> &node) const;
0059 
0060 private Q_SLOTS:
0061     void fuseRequestPending();
0062     void exitHandler();
0063 
0064 private:
0065     // Functions used by fuse_lowlevel_ops
0066     static void init(void *userdata, struct fuse_conn_info *conn);
0067     static void lookup(fuse_req_t req, fuse_ino_t parent, const char *name);
0068     static void forget(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup);
0069     static void getattr(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi);
0070     static void setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr, int to_set, struct fuse_file_info *fi);
0071     static void readlink(fuse_req_t req, fuse_ino_t ino);
0072     static void mknod(fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode, dev_t rdev);
0073     static void mkdir(fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode);
0074     static void unlinkHelper(fuse_req_t req, fuse_ino_t parent, const char *name, bool isDirectory=false);
0075     static void unlink(fuse_req_t req, fuse_ino_t parent, const char *name); // Just calls unlinkHelper
0076     static void rmdir(fuse_req_t req, fuse_ino_t parent, const char *name); // Just calls unlinkHelper
0077     static void symlink(fuse_req_t req, const char *link, fuse_ino_t parent, const char *name);
0078     static void open(fuse_req_t req, fuse_ino_t ino, fuse_file_info *fi);
0079     static void rename(fuse_req_t req, fuse_ino_t parent, const char *name, fuse_ino_t newparent,
0080                        const char *newname, unsigned int flags);
0081     static void opendir(fuse_req_t req, fuse_ino_t ino, fuse_file_info *fi);
0082     static void readdir(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,
0083                         struct fuse_file_info *fi);
0084     static void releasedir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi);
0085     static void read(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,
0086                      struct fuse_file_info *fi);
0087     static void write(fuse_req_t req, fuse_ino_t ino, const char *buf,
0088                       size_t size, off_t off, struct fuse_file_info *fi);
0089     static void flush(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi);
0090     static void release(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi);
0091     static void fsync(fuse_req_t req, fuse_ino_t ino, int datasync, struct fuse_file_info *fi);
0092 
0093     /** Does some checks of the environment. Returns false if a critical issue was found. */
0094     bool isEnvironmentValid();
0095 
0096     /** Setups signal handlers. Returns true if successful, false otherwise **/
0097     bool setupSignalHandlers();
0098     /** Reverts to default signal handlers. Returns true if successful, false otherwise. **/
0099     bool removeSignalHandlers();
0100     /** Notifies m_signalNotifier of a signal **/
0101     static void signalHandler(int signal);
0102 
0103     /** Returns a pointer to a child node of parent with m_nodeName == name or nullptr. */
0104     std::shared_ptr<KIOFuseNode> nodeByName(const std::shared_ptr<KIOFuseDirNode> &parent, const QString &name) const;
0105     /** Returns a pointer to the KIOFuseNode with inode number ino or nullptr. */
0106     std::shared_ptr<KIOFuseNode> nodeForIno(const fuse_ino_t ino) const;
0107     /** Removes the node from the old parent's children list (if any) and adds it to the new parent's list.*/
0108     void reparentNode(const std::shared_ptr<KIOFuseNode> &node, fuse_ino_t newParentIno);
0109     /** Allocates a new inode number if not given, adds node into m_nodes
0110       * and adds it to the node parent's children list. */
0111     fuse_ino_t insertNode(const std::shared_ptr<KIOFuseNode> &node, fuse_ino_t ino=KIOFuseIno::Invalid);
0112 
0113     /** Returns the full url upwards until a OriginNode is hit.
0114       * If no OriginNode is found, an empty QUrl is returned. */
0115     QUrl remoteUrl(const std::shared_ptr<const KIOFuseNode> &node) const;
0116 
0117     /** Fills a (previously zeroed out) struct stat with minimal information about a fake file. */
0118     void fillStatForFile(struct stat &attr);
0119     /** Increments the lookup count of node by delta. */
0120     void incrementLookupCount(const std::shared_ptr<KIOFuseNode> &node, uint64_t delta=1);
0121     /** Adjusts the lookup count and deletes the node if it is now zero and a child of DeletedRoot. */
0122     void decrementLookupCount(const std::shared_ptr<KIOFuseNode> node, uint64_t delta=1);
0123     /** Depending on the lookup count, it makes the node a child of DeletedRoot or deletes it directly. */
0124     void markNodeDeleted(const std::shared_ptr<KIOFuseNode> &node);
0125     /** Creates a new node with the matching type and fills m_stat fields. */
0126     std::shared_ptr<KIOFuseNode> createNodeFromUDSEntry(const KIO::UDSEntry &entry, const fuse_ino_t parentIno, const QString &nameOverride);
0127     /** Applies a fresh KIO::UDSEntry to an existing node. If the type needs changing,
0128      * The old node is deleted and a new one inserted instead. The now fresh node is returned. */
0129     std::shared_ptr<KIOFuseNode> updateNodeFromUDSEntry(const std::shared_ptr<KIOFuseNode> &node, const KIO::UDSEntry &entry);
0130 
0131     /** Sends the node's attributes with fuse_reply_attr. */
0132     static void replyAttr(fuse_req_t req, std::shared_ptr<KIOFuseNode> node);
0133     /** Sends the node entry with fuse_reply_entry and increments the lookup count.
0134       * Sends an empty entry if node is null.*/
0135     void replyEntry(fuse_req_t req, std::shared_ptr<KIOFuseNode> node);
0136 
0137     /** Invokes callback on error or when the bytes are available for reading/writing.
0138       * If the file is smaller than bytes, it sets error = ESPIPE. */
0139     void awaitBytesAvailable(const std::shared_ptr<KIOFuseRemoteCacheBasedFileNode> &node, off_t bytes, const std::function<void(int error)> &callback);
0140     /** Invokes callback on error or when the cache is marked as complete. */
0141     void awaitCacheComplete(const std::shared_ptr<KIOFuseRemoteCacheBasedFileNode> &node, const std::function<void(int error)> &callback);
0142     /** Invokes callback on error or when all children nodes are available */
0143     void awaitChildrenComplete(const std::shared_ptr<KIOFuseDirNode> &node, const std::function<void(int error)> &callback);
0144     /** Marks a node's cache as dirty and add it to m_dirtyNodes. */
0145     void markCacheDirty(const std::shared_ptr<KIOFuseRemoteCacheBasedFileNode> &node);
0146     /** Calls the callback once the cache is not dirty anymore (no cache counts as clean as well).
0147       * If writes happen while a flush is sending data, a flush will be retriggered. */
0148     void awaitNodeFlushed(const std::shared_ptr<KIOFuseRemoteCacheBasedFileNode> &node, const std::function<void(int error)> &callback);
0149     /** Invokes callback on error or when a node has been refreshed (if its stat timed out) */
0150     void awaitAttrRefreshed(const std::shared_ptr<KIOFuseNode> &node, const std::function<void(int error)> &callback);
0151     /** Invokes callback on error on when the child node was fetched and created/updated. */
0152     void awaitChildMounted(const std::shared_ptr<KIOFuseRemoteDirNode> &node, const QString &name, const std::function<void(const std::shared_ptr<KIOFuseNode>&, int)> &callback);
0153 
0154     /** Returns the URL pointing to the origin of the linked resource, i.e. path set to / or empty. */
0155     QUrl originOfUrl(const QUrl &url);
0156     /** Returns the path elements where the URL url gets mapped to in this VFS. */
0157     QStringList mapUrlToVfs(const QUrl &url);
0158     /** Stats url. If successful, returns the path where url + pathElements is reachable in callback.
0159       * If it failed, it moves one part of pathElements to url and tries again, recursively. */
0160     void findAndCreateOrigin(const QUrl &url, const QStringList &pathElements, const std::function<void(const QString&, int)> &callback);
0161     
0162     /** Returns the corresponding FUSE error to the given KIO Job error */
0163     static int kioErrorToFuseError(const int kioError);
0164 
0165     /** Prevent the Application from quitting. */
0166     std::unique_ptr<QEventLoopLocker> m_eventLoopLocker;
0167 
0168     /** Struct of implemented fuse operations. */
0169     struct FuseLLOps;
0170     static const FuseLLOps fuse_ll_ops;
0171 
0172     /** Fuse bookkeeping. */
0173     struct fuse_session *m_fuseSession = nullptr;
0174     /** Allow parsing of options in init() */
0175     std::unique_ptr<struct fuse_conn_info_opts, decltype(&free)> m_fuseConnInfoOpts{nullptr, &free};
0176     /** Fuse bookkeeping. */
0177     std::unique_ptr<QSocketNotifier> m_fuseNotifier;
0178     /** Path where this VFS is currently mounted at, with trailing '/'. */
0179     QString m_mountpoint;
0180 
0181     /** Fds of paired sockets. 
0182      * Used in conjunction with socket notifier to allow handling signals with the Qt event loop. **/
0183     static int signalFd[2];
0184     /** Activated if there is data to read from the fd.
0185      * This is the case when a signal handler is activated.**/
0186     std::unique_ptr<QSocketNotifier> m_signalNotifier;
0187 
0188     /** Used by insertNode for accelerating the search for the next free inode number. */
0189     fuse_ino_t m_nextIno = KIOFuseIno::DynamicStart;
0190     /** Map of all known inode numbers to KIOFuseNodes. */
0191     std::unordered_map<fuse_ino_t, std::shared_ptr<KIOFuseNode>> m_nodes;
0192     /** Set of all nodes with a dirty cache. */
0193     std::set<fuse_ino_t> m_dirtyNodes;
0194 
0195     /** @see setUseFileJob() */
0196     bool m_useFileJob;
0197 };