1 /*
2     This software is a contribution of the LiMux project of the city of Munich.
3     SPDX-FileCopyrightText: 2021 Robert Hoffmann <robert@roberthoffmann.de>
4 
5     SPDX-License-Identifier: LGPL-2.0-or-later
6 */
7 #ifndef KNETWORKMOUNTS_H
8 #define KNETWORKMOUNTS_H
9 
10 #include <memory>
11 
12 #include <QObject>
13 #include <kcoreaddons_export.h>
14 
15 /**
16  * \class KNetworkMounts knetworkmounts.h <KCoreAddons/KNetworkMounts>
17  *
18  * Performance control on network mounts.
19  *
20  * This class provides methods for deciding whether operations
21  * on slow network mounts should be performed or not.
22  *
23  * Configuration is read from a configuration file network_mounts in
24  * the user's QStandardPaths::ConfigLocation. This file can be filled by using
25  * the network mounts performance configuration module or directly via @ref setEnabled,
26  * @ref setPaths, @ref addPath and @ref setOption
27  * @code
28  *   KNetworkMounts::self()->setEnabled(true);
29  *   KNetworkMounts::self()->setOption(KNetworkMounts::LowSideEffectsOptimizations, true);
30  *   KNetworkMounts::self()->addPath(path1, KNetworkMounts::NfsPaths);
31  *   KNetworkMounts::self()->addPath(path2, KNetworkMounts::NfsPaths);
32  *   KNetworkMounts::self()->setPaths(listOfPaths, KNetworkMounts::SmbPaths);
33  * @endcode
34  *
35  * Use KNetworkMounts like this to check if the given url is on a
36  * configured slow path and the KNetworkMountOption LowSideEffectsOptimizations
37  * is enabled:
38  * @code
39  *   if (KNetworkMounts::self()->isOptionEnabledForPath(url.toLocalFile(),
40  *       KNetworkMounts::LowSideEffectsOptimizations))
41  *   {
42  *       // skip operations which are slow on the given url if
43  *       // KNetworkMountOption LowSideEffectsOptimizations is enabled
44  *   } else {
45  *       // given url is not configured being slow or the KNetworkMountOption
46  *       // LowSideEffectsOptimizations is not enabled
47  *   }
48  * @endcode
49  *
50  * If called for the first time, this creates a singleton instance and reads
51  * the config file. Subsequent calls just use this instance without reading
52  * the config file again.
53  *
54  * @author Robert Hoffmann <robert@roberthoffmann.de>
55  *
56  * @since 5.85
57  **/
58 class KCOREADDONS_EXPORT KNetworkMounts : public QObject
59 {
60     Q_OBJECT
61 
62 public:
63     /**
64      * Returns (and creates if necessary) the singleton instance
65      *
66      * @return the singleton instance
67      */
68     static KNetworkMounts *self();
69 
70     /**
71      * The KNetworkMountOption enum
72      *
73      * Uses are:
74      */
75     enum KNetworkMountOption {
76         LowSideEffectsOptimizations, ///< Don't run KDiskFreeSpaceInfo if slow path.<br>
77                                      ///< Don't check for manually mounted drives.<br>
78                                      ///< Don't check with QFileInfo::isWritable if it is writable, if not yet known, return true.<br>
79                                      ///< Don't check with QFileInfo::isReadable if it is readable, return false.<br>
80                                      ///< Don't check for desktop files just return false.<br>
81                                      ///< Ignore .hidden files on slow paths.<br>
82                                      ///< Don't read mime comment from .desktop or .directory files.<br>
83                                      ///< Don't get the size with QFileInfo::size, just return 0, if not yet known.<br>
84                                      ///< Don't determine mime type from file content, use file extension.<br>
85                                      ///< Don't check for desktop files just return false.<br>
86                                      ///< Don't call KFileSystemType::fileSystemType to check if the filesystem is slow, just return true.<br>
87                                      ///< Don't count files/directories in subdirectories.<br>
88                                      ///< Don't calculate sizes of subdirectories.<br>
89                                      ///< Avoid check for dir at Kate startup
90         MediumSideEffectsOptimizations, ///< Don't return project for dir, avoid QFileInfo().absoluteDir()<br>
91                                         ///< Don't search for .kateconfig recursively<br>
92                                         ///< Ignore recent files on slow paths
93         StrongSideEffectsOptimizations, ///< Turn off symbolic link resolution
94         KDirWatchUseINotify, ///< Use Inotify on the path (instead of FAM on NFS mounts). Inotify detects changes only locally, FAM works also remotely with NFS
95         KDirWatchDontAddWatches, ///< Disables dir watching completely for slow paths, avoids stat() calls on added dirs and subdirs
96         SymlinkPathsUseCache ///< Cache resolved symlink paths
97     };
98     Q_ENUM(KNetworkMountOption)
99 
100     /**
101      * The KNetworkMountsType enum
102      */
103     enum KNetworkMountsType {
104         NfsPaths, ///< NFS paths
105         SmbPaths, ///< SMB paths
106         SymlinkDirectory, ///< Paths to directories which contain symbolic links to network mounts
107         SymlinkToNetworkMount, ///< Paths which are symbolic links to network mounts
108         Any ///< Any slow path type. Do not use with @ref setPaths or @ref addPath
109     };
110     Q_ENUM(KNetworkMountsType)
111 
112     /**
113      * Query if @p path is configured to be a slow path of type @p type
114      *
115      * @param path the path to query
116      * @param type the type to query. If omitted, any type matches
117      * @return @c true if @p path is a configured slow path of type @p type
118      *
119      * This function is also used to determine the filesystem type in @ref KFileSystemType::fileSystemType
120      * (KFileSystemType::Smb or KFileSystemType::Nfs) without an expensive call to stafs(). For this
121      * to work the types of paths need to be correctly assigned in @ref setPath or @ref addPath
122      */
123     bool isSlowPath(const QString &path, KNetworkMountsType type = Any);
124 
125     /**
126      * Query if @p path is configured to be a slow path and @p option is enabled
127      *
128      * @param path the path to query
129      * @param option the option to query
130      * @return @c true if @p path is a configured slow path and option @p option is enabled
131      */
132     bool isOptionEnabledForPath(const QString &path, KNetworkMountOption option);
133 
134     /**
135      * Query if the performance optimizations are switched on
136      *
137      * @return @c true if on, @c false otherwise
138      */
139     bool isEnabled() const;
140 
141     /**
142      * Switch the performance optimizations on or off
143      *
144      * @param value the value to set
145      */
146     void setEnabled(bool value);
147 
148     /**
149      * Query a performance option
150      *
151      * @param option the option to query
152      * @param defaultValue the value to return if the option is not configured
153      * @return @c true if option is on, @c false if not
154      * @see KNetworkMountOption
155      */
156     bool isOptionEnabled(const KNetworkMountOption option, const bool defaultValue = false) const;
157 
158     /**
159      * Switch a performance option on or off
160      *
161      * @param option the option to change
162      * @param value the value to set
163      * @see KNetworkMountOption
164      */
165     void setOption(const KNetworkMountOption option, const bool value);
166 
167     /**
168      * Query the configured paths for which optimizations are to take place
169      *
170      * @return a list of paths
171      */
172     QStringList paths(KNetworkMountsType type = Any) const;
173 
174     /**
175      * Set the paths for which optimizations are to take place
176      *
177      * @param paths the paths to set
178      * @param type the type of paths. Do not use @ref Any
179      * @see KNetworkMountsType
180      */
181     void setPaths(const QStringList &paths, KNetworkMountsType type);
182 
183     /**
184      * Add a path for which optimizations are to take place
185      *
186      * @param path the path to add
187      * @param type the type of the path. Do not use @ref Any
188      * @see KNetworkMountsType
189      */
190     void addPath(const QString &path, KNetworkMountsType type);
191 
192     /**
193      * Resolves a @p path that may contain symbolic links to mounted network shares.
194      *
195      * A symlink path is either a directory which contains symbolic links to slow network mounts
196      * (@ref SymlinkDirectory) or a direct symbolic link to a slow network mount (@ref
197      * SymlinkToNfsOrSmbPaths).
198      *
199      * Example:
200      * There are some Samba shares mounted below /mnt. These are @ref paths of type @ref SmbPaths
201      * @code
202      * /mnt/server1/share1
203      * /mnt/server1/share2
204      * /mnt/server2/share3
205      * @endcode
206      *
207      * A (logged in) user may have symbolic links to them in his home directory below netshares. The
208      * directory /home/user/netshares is a @ref SymlinkDirectory:
209      * @code
210      * /home/user/netshares/share1 -> /mnt/server1/share1
211      * /home/user/netshares/share2 -> /mnt/server1/share2
212      * /home/user/netshares/share3 -> /mnt/server2/share3
213      * @endcode
214      *
215      * There is a direct symbolic link from /home/user/share1 to /mnt/server1/share1. This is of type
216      * @ref SymlinkToNfsOrSmbPaths:
217      * @code
218      * /home/user/share1 -> /mnt/server1/share1
219      * @endcode
220      *
221      * Both types of symbolic links from symlink paths to the real mounted shares are resolved even if
222      * KNetworkMountOption @ref StrongSideEffectsOptimizations is enabled.
223 
224      * If the setup is like above a @p path
225      * @code
226      * /home/user/netshares/share1/Documents/file.txt
227      * @endcode
228      *
229      * would be resolved to
230      * @code
231      * /mnt/server1/share1/Documents/file.txt
232      * @endcode
233      *
234      * and a @p path
235      * @code
236      * /home/user/share1/Documents/file.txt
237      * @endcode
238      *
239      * would also be resolved to
240      * @code
241      * /mnt/server1/share1/Documents/file.txt
242      * @endcode
243      *
244      * Resolved paths are cached in a hash.
245      *
246      * @param path the path to resolve
247      * @return the resolved path or @p path if @p path is not a symlink path or no symlink found
248      * @see KNetworkMountsType
249      * @see clearCache
250      * @see isSlowPath
251      */
252     QString canonicalSymlinkPath(const QString &path);
253 
254     /**
255      * Clears the canonical symlink path cache
256      *
257      * Call this if directory structures on mounted network drives changed. Don't enable the
258      * cache (@ref SymlinkPathsUseCache) if this happens often and the drives are usually accessed
259      * via the symlinks. This method exists mainly for the KCM.
260      * @see canonicalSymlinkPath
261      */
262     void clearCache();
263 
264     /**
265      * Synchronizes to config file
266      *
267      * QSettings synchronization also takes place automatically at regular intervals and from
268      * QSettings destructor, see QSettings::sync() documentation.
269      *
270      * Calls QSettings::sync()
271      */
272     void sync();
273 
274 private:
275     /// Creates a new KNetworkMounts object
276     KNetworkMounts();
277 
278     /// Destructor
279     ~KNetworkMounts() override;
280 
281     std::unique_ptr<class KNetworkMountsPrivate> const d;
282 };
283 
284 #endif // KNETWORKMOUNTS_H
285