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 | |
8 | #include "knetworkmounts.h" |
9 | #include "knetworkmounts_p.h" |
10 | |
11 | #include <QCoreApplication> |
12 | #include <QGlobalStatic> |
13 | |
14 | #include <QDebug> |
15 | #include <QDir> |
16 | #include <QStandardPaths> |
17 | |
18 | KNetworkMountsPrivate::KNetworkMountsPrivate(KNetworkMounts *qq) |
19 | : q(qq) |
20 | { |
21 | } |
22 | |
23 | KNetworkMounts *KNetworkMounts::self() |
24 | { |
25 | static KNetworkMounts s_self; |
26 | return &s_self; |
27 | } |
28 | |
29 | KNetworkMounts::KNetworkMounts() |
30 | : d(new KNetworkMountsPrivate(this)) |
31 | { |
32 | const QString configFileName = QStringLiteral("%1/network_mounts" ).arg(a: QStandardPaths::writableLocation(type: QStandardPaths::ConfigLocation)); |
33 | d->m_settings = new QSettings(configFileName, QSettings::Format::IniFormat, this); |
34 | |
35 | for (const auto type : {KNetworkMounts::NfsPaths, KNetworkMounts::SmbPaths, KNetworkMounts::SymlinkDirectory, KNetworkMounts::SymlinkToNetworkMount}) { |
36 | QString typeStr = enumToString(type); |
37 | QStringList slowPaths = d->m_settings->value(key: typeStr, defaultValue: QStringList()).toStringList(); |
38 | |
39 | if (ensureTrailingSlashes(paths: &slowPaths)) { |
40 | d->m_settings->setValue(key: typeStr, value: slowPaths); |
41 | } |
42 | } |
43 | } |
44 | |
45 | KNetworkMounts::~KNetworkMounts() |
46 | { |
47 | } |
48 | |
49 | bool KNetworkMounts::isSlowPath(const QString &path, KNetworkMountsType type) |
50 | { |
51 | return !getMatchingPath(path: path, slowPaths: paths(type)).isEmpty(); |
52 | } |
53 | |
54 | bool KNetworkMounts::isOptionEnabledForPath(const QString &path, KNetworkMountOption option) |
55 | { |
56 | if (!isEnabled()) { |
57 | return false; |
58 | } |
59 | |
60 | if (!isSlowPath(path)) { |
61 | return false; |
62 | } |
63 | |
64 | return isOptionEnabled(option, defaultValue: true); |
65 | } |
66 | |
67 | bool KNetworkMounts::isEnabled() const |
68 | { |
69 | return d->m_settings->value(QStringLiteral("EnableOptimizations" ), defaultValue: false).toBool(); |
70 | } |
71 | |
72 | void KNetworkMounts::setEnabled(const bool value) |
73 | { |
74 | d->m_settings->setValue(QStringLiteral("EnableOptimizations" ), value); |
75 | } |
76 | |
77 | bool KNetworkMounts::isOptionEnabled(const KNetworkMountOption option, const bool defaultValue) const |
78 | { |
79 | return d->m_settings->value(key: enumToString(type: option), defaultValue).toBool(); |
80 | } |
81 | |
82 | void KNetworkMounts::setOption(const KNetworkMountOption option, const bool value) |
83 | { |
84 | d->m_settings->setValue(key: enumToString(type: option), value); |
85 | } |
86 | |
87 | QStringList KNetworkMounts::paths(KNetworkMountsType type) const |
88 | { |
89 | if (type == Any) { |
90 | QStringList paths; |
91 | paths.reserve(asize: 4); |
92 | for (const auto networkMountType : |
93 | {KNetworkMounts::NfsPaths, KNetworkMounts::SmbPaths, KNetworkMounts::SymlinkDirectory, KNetworkMounts::SymlinkToNetworkMount}) { |
94 | paths.append(other: d->m_settings->value(key: enumToString(type: networkMountType), defaultValue: QStringList()).toStringList()); |
95 | } |
96 | return paths; |
97 | } else { |
98 | return d->m_settings->value(key: enumToString(type), defaultValue: QStringList()).toStringList(); |
99 | } |
100 | } |
101 | |
102 | void KNetworkMounts::setPaths(const QStringList &paths, KNetworkMountsType type) |
103 | { |
104 | QStringList _paths(paths); |
105 | ensureTrailingSlashes(paths: &_paths); |
106 | d->m_settings->setValue(key: enumToString(type), value: _paths); |
107 | } |
108 | |
109 | void KNetworkMounts::addPath(const QString &path, KNetworkMountsType type) |
110 | { |
111 | QString _path(path); |
112 | ensureTrailingSlash(path: &_path); |
113 | QStringList newPaths = paths(type); |
114 | newPaths.append(t: _path); |
115 | d->m_settings->setValue(key: enumToString(type), value: newPaths); |
116 | } |
117 | |
118 | typedef QHash<QString /*symlink*/, QString /*canonical path*/> symlinkCanonicalPathHash; |
119 | Q_GLOBAL_STATIC(symlinkCanonicalPathHash, s_canonicalLinkSpacePaths) |
120 | |
121 | QString KNetworkMounts::canonicalSymlinkPath(const QString &path) |
122 | { |
123 | bool useCache = isOptionEnabled(option: KNetworkMountOption::SymlinkPathsUseCache, defaultValue: true); |
124 | if (useCache) { |
125 | const QString resolved = s_canonicalLinkSpacePaths->value(key: path); |
126 | |
127 | if (!resolved.isEmpty()) { |
128 | return resolved; |
129 | } |
130 | } |
131 | |
132 | QString symlinkPath = getMatchingPath(path: path, slowPaths: paths(type: KNetworkMountsType::SymlinkToNetworkMount)); |
133 | if (!symlinkPath.isEmpty()) { |
134 | // remove trailing slash |
135 | symlinkPath.chop(n: 1); |
136 | |
137 | QFileInfo link(symlinkPath); |
138 | QString linkPath(path); |
139 | QString target = link.symLinkTarget(); |
140 | |
141 | if (target.isEmpty()) { |
142 | // not a symlink |
143 | if (useCache) { |
144 | s_canonicalLinkSpacePaths->insert(key: path, value: path); |
145 | } |
146 | return path; |
147 | } else { |
148 | // symlink |
149 | // replace only the first occurence of symlinkPath in linkPath with the link target |
150 | // linkPath.startsWith(symlinkPath) because of getMatchingPath |
151 | linkPath.replace(i: 0, len: symlinkPath.size(), after: target); |
152 | |
153 | if (useCache) { |
154 | s_canonicalLinkSpacePaths->insert(key: path, value: linkPath); |
155 | } |
156 | return linkPath; |
157 | } |
158 | } |
159 | |
160 | QString linkSpacePath = getMatchingPath(path: path, slowPaths: paths(type: KNetworkMountsType::SymlinkDirectory)); |
161 | if (!linkSpacePath.isEmpty()) { |
162 | QString _path = path; |
163 | if (!_path.endsWith(c: QLatin1Char('/'))) { |
164 | _path.append(c: QLatin1Char('/')); |
165 | } |
166 | |
167 | if (_path == linkSpacePath) { |
168 | if (useCache) { |
169 | s_canonicalLinkSpacePaths->insert(key: path, value: path); |
170 | } |
171 | return path; |
172 | } |
173 | |
174 | // search for symlink, linkSpacePath always ends with '/' |
175 | int linkIndex = path.indexOf(c: QLatin1Char('/'), from: linkSpacePath.length()); |
176 | const QString symlink = path.left(n: linkIndex); |
177 | |
178 | if (useCache && s_canonicalLinkSpacePaths->contains(key: symlink)) { |
179 | QString linkPath(path); |
180 | // replace only the first occurence of symlink in linkPath |
181 | linkPath.replace(i: 0, len: symlink.size(), after: s_canonicalLinkSpacePaths->value(key: symlink)); |
182 | s_canonicalLinkSpacePaths->insert(key: path, value: linkPath); |
183 | return linkPath; |
184 | } else { |
185 | QFileInfo link(symlink); |
186 | |
187 | if (link.isSymLink()) { |
188 | QString linkPath(path); |
189 | // replace only the first occurence of symlink in linkPath |
190 | linkPath.replace(i: 0, len: symlink.size(), after: link.symLinkTarget()); |
191 | |
192 | if (useCache) { |
193 | s_canonicalLinkSpacePaths->insert(key: path, value: linkPath); |
194 | } |
195 | return linkPath; |
196 | } else { |
197 | if (useCache) { |
198 | s_canonicalLinkSpacePaths->insert(key: path, value: path); |
199 | } |
200 | } |
201 | } |
202 | } |
203 | |
204 | return path; |
205 | } |
206 | |
207 | void KNetworkMounts::clearCache() |
208 | { |
209 | if (s_canonicalLinkSpacePaths.exists()) { |
210 | s_canonicalLinkSpacePaths->clear(); |
211 | } |
212 | } |
213 | |
214 | void KNetworkMounts::sync() |
215 | { |
216 | d->m_settings->sync(); |
217 | } |
218 | |
219 | #include "moc_knetworkmounts.cpp" |
220 | |