1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qfilesystemengine_p.h"
5#include <QtCore/qdir.h>
6#include <QtCore/qset.h>
7#include <QtCore/qstringbuilder.h>
8#include <QtCore/private/qabstractfileengine_p.h>
9#ifdef QT_BUILD_CORE_LIB
10#include <QtCore/private/qresource_p.h>
11#endif
12#include <QtCore/private/qduplicatetracker_p.h>
13
14QT_BEGIN_NAMESPACE
15
16/*!
17 \internal
18
19 Returns the canonicalized form of \a path (i.e., with all symlinks
20 resolved, and all redundant path elements removed.
21*/
22QString QFileSystemEngine::slowCanonicalized(const QString &path)
23{
24 if (path.isEmpty())
25 return path;
26
27 QFileInfo fi;
28 const QChar slash(u'/');
29 QString tmpPath = path;
30 qsizetype separatorPos = 0;
31 QSet<QString> nonSymlinks;
32 QDuplicateTracker<QString> known;
33
34 (void)known.hasSeen(s: path);
35 do {
36#ifdef Q_OS_WIN
37 if (separatorPos == 0) {
38 if (tmpPath.size() >= 2 && tmpPath.at(0) == slash && tmpPath.at(1) == slash) {
39 // UNC, skip past the first two elements
40 separatorPos = tmpPath.indexOf(slash, 2);
41 } else if (tmpPath.size() >= 3 && tmpPath.at(1) == u':' && tmpPath.at(2) == slash) {
42 // volume root, skip since it can not be a symlink
43 separatorPos = 2;
44 }
45 }
46 if (separatorPos != -1)
47#endif
48 separatorPos = tmpPath.indexOf(c: slash, from: separatorPos + 1);
49 QString prefix = separatorPos == -1 ? tmpPath : tmpPath.left(n: separatorPos);
50 if (!nonSymlinks.contains(value: prefix)) {
51 fi.setFile(prefix);
52 if (fi.isSymLink()) {
53 QString target = fi.symLinkTarget();
54 if (separatorPos != -1) {
55 if (fi.isDir() && !target.endsWith(c: slash))
56 target.append(c: slash);
57 target.append(v: QStringView{tmpPath}.mid(pos: separatorPos));
58 }
59 tmpPath = QDir::cleanPath(path: target);
60 separatorPos = 0;
61
62 if (known.hasSeen(s: tmpPath))
63 return QString();
64 } else {
65 nonSymlinks.insert(value: prefix);
66 }
67 }
68 } while (separatorPos != -1);
69
70 return QDir::cleanPath(path: tmpPath);
71}
72
73static inline bool _q_checkEntry(QFileSystemEntry &entry, QFileSystemMetaData &data, bool resolvingEntry)
74{
75 if (resolvingEntry) {
76 if (!QFileSystemEngine::fillMetaData(entry, data, what: QFileSystemMetaData::ExistsAttribute)
77 || !data.exists()) {
78 data.clear();
79 return false;
80 }
81 }
82
83 return true;
84}
85
86static inline bool _q_checkEntry(std::unique_ptr<QAbstractFileEngine> &engine, bool resolvingEntry)
87{
88 if (resolvingEntry) {
89 if (!(engine->fileFlags(type: QAbstractFileEngine::FlagsMask) & QAbstractFileEngine::ExistsFlag)) {
90 engine.reset();
91 return false;
92 }
93 }
94
95 return true;
96}
97
98static bool _q_createLegacyEngine_recursive(QFileSystemEntry &entry, QFileSystemMetaData &data,
99 std::unique_ptr<QAbstractFileEngine> &engine,
100 bool resolvingEntry = false)
101{
102 QString const &filePath = entry.filePath();
103 if ((engine = qt_custom_file_engine_handler_create(path: filePath)))
104 return _q_checkEntry(engine, resolvingEntry);
105
106#if defined(QT_BUILD_CORE_LIB)
107 for (qsizetype prefixSeparator = 0; prefixSeparator < filePath.size(); ++prefixSeparator) {
108 QChar const ch = filePath[prefixSeparator];
109 if (ch == u'/')
110 break;
111
112 if (ch == u':') {
113 if (prefixSeparator == 0) {
114 engine = std::make_unique<QResourceFileEngine>(args: filePath);
115 return _q_checkEntry(engine, resolvingEntry);
116 }
117
118 if (prefixSeparator == 1)
119 break;
120
121 const QStringList &paths = QDir::searchPaths(prefix: filePath.left(n: prefixSeparator));
122 for (int i = 0; i < paths.size(); i++) {
123 entry = QFileSystemEntry(QDir::cleanPath(
124 path: paths.at(i) % u'/' % QStringView{filePath}.mid(pos: prefixSeparator + 1)));
125 // Recurse!
126 if (_q_createLegacyEngine_recursive(entry, data, engine, resolvingEntry: true))
127 return true;
128 }
129
130 // entry may have been clobbered at this point.
131 return false;
132 }
133
134 // There's no need to fully validate the prefix here. Consulting the
135 // unicode tables could be expensive and validation is already
136 // performed in QDir::setSearchPaths.
137 //
138 // if (!ch.isLetterOrNumber())
139 // break;
140 }
141#endif // defined(QT_BUILD_CORE_LIB)
142
143 return _q_checkEntry(entry, data, resolvingEntry);
144}
145
146Q_CORE_EXPORT bool qt_isCaseSensitive(const QFileSystemEntry &entry, QFileSystemMetaData &data)
147{
148 // called from QtGui (QFileSystemModel, QFileInfoGatherer)
149 return QFileSystemEngine::isCaseSensitive(entry, data);
150}
151
152/*!
153 \internal
154
155 Resolves the \a entry (see QDir::searchPaths) and returns an engine for
156 it, but never a QFSFileEngine.
157
158 Returns a file engine that can be used to access the entry. Returns 0 if
159 QFileSystemEngine API should be used to query and interact with the file
160 system object.
161*/
162std::unique_ptr<QAbstractFileEngine>
163QFileSystemEngine::createLegacyEngine(QFileSystemEntry &entry, QFileSystemMetaData &data)
164{
165 QFileSystemEntry copy = entry;
166 std::unique_ptr<QAbstractFileEngine> engine;
167
168 if (_q_createLegacyEngine_recursive(entry&: copy, data, engine))
169 // Reset entry to resolved copy.
170 entry = copy;
171 else
172 data.clear();
173
174 return engine;
175}
176
177//static
178QString QFileSystemEngine::resolveUserName(const QFileSystemEntry &entry, QFileSystemMetaData &metaData)
179{
180#if defined(Q_OS_WIN)
181 Q_UNUSED(metaData);
182 return QFileSystemEngine::owner(entry, QAbstractFileEngine::OwnerUser);
183#else //(Q_OS_UNIX)
184 if (!metaData.hasFlags(flags: QFileSystemMetaData::UserId))
185 QFileSystemEngine::fillMetaData(entry, data&: metaData, what: QFileSystemMetaData::UserId);
186 if (!metaData.exists())
187 return QString();
188 return resolveUserName(userId: metaData.userId());
189#endif
190}
191
192//static
193QString QFileSystemEngine::resolveGroupName(const QFileSystemEntry &entry, QFileSystemMetaData &metaData)
194{
195#if defined(Q_OS_WIN)
196 Q_UNUSED(metaData);
197 return QFileSystemEngine::owner(entry, QAbstractFileEngine::OwnerGroup);
198#else //(Q_OS_UNIX)
199 if (!metaData.hasFlags(flags: QFileSystemMetaData::GroupId))
200 QFileSystemEngine::fillMetaData(entry, data&: metaData, what: QFileSystemMetaData::GroupId);
201 if (!metaData.exists())
202 return QString();
203 return resolveGroupName(groupId: metaData.groupId());
204#endif
205}
206
207//static
208QFileSystemEntry QFileSystemEngine::getJunctionTarget(const QFileSystemEntry &link,
209 QFileSystemMetaData &data)
210{
211#if defined(Q_OS_WIN)
212 return junctionTarget(link, data);
213#else
214 Q_UNUSED(link);
215 Q_UNUSED(data);
216 return {};
217#endif
218}
219
220QT_END_NAMESPACE
221

source code of qtbase/src/corelib/io/qfilesystemengine.cpp