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
18KNetworkMountsPrivate::KNetworkMountsPrivate(KNetworkMounts *qq)
19 : q(qq)
20{
21}
22
23KNetworkMounts *KNetworkMounts::self()
24{
25 static KNetworkMounts s_self;
26 return &s_self;
27}
28
29KNetworkMounts::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
45KNetworkMounts::~KNetworkMounts()
46{
47}
48
49bool KNetworkMounts::isSlowPath(const QString &path, KNetworkMountsType type)
50{
51 return !getMatchingPath(path: path, slowPaths: paths(type)).isEmpty();
52}
53
54bool 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
67bool KNetworkMounts::isEnabled() const
68{
69 return d->m_settings->value(QStringLiteral("EnableOptimizations"), defaultValue: false).toBool();
70}
71
72void KNetworkMounts::setEnabled(const bool value)
73{
74 d->m_settings->setValue(QStringLiteral("EnableOptimizations"), value);
75}
76
77bool KNetworkMounts::isOptionEnabled(const KNetworkMountOption option, const bool defaultValue) const
78{
79 return d->m_settings->value(key: enumToString(type: option), defaultValue).toBool();
80}
81
82void KNetworkMounts::setOption(const KNetworkMountOption option, const bool value)
83{
84 d->m_settings->setValue(key: enumToString(type: option), value);
85}
86
87QStringList 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
102void 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
109void 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
118typedef QHash<QString /*symlink*/, QString /*canonical path*/> symlinkCanonicalPathHash;
119Q_GLOBAL_STATIC(symlinkCanonicalPathHash, s_canonicalLinkSpacePaths)
120
121QString 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
207void KNetworkMounts::clearCache()
208{
209 if (s_canonicalLinkSpacePaths.exists()) {
210 s_canonicalLinkSpacePaths->clear();
211 }
212}
213
214void KNetworkMounts::sync()
215{
216 d->m_settings->sync();
217}
218
219#include "moc_knetworkmounts.cpp"
220

source code of kcoreaddons/src/lib/io/knetworkmounts.cpp