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 };