Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qfilesystemengine.cpp
Go to the documentation of this file.
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// Qt-Security score:critical reason:data-parser
4
6#include <QtCore/qdir.h>
7#include <QtCore/qset.h>
8#include <QtCore/qstringbuilder.h>
9#include <QtCore/private/qabstractfileengine_p.h>
10#ifdef QT_BUILD_CORE_LIB
11#include <QtCore/private/qresource_p.h>
12#endif
13#include <QtCore/private/qduplicatetracker_p.h>
14
16
17/*! \class QFileSystemEngine
18 \internal
19
20 QFileSystemEngine offers OS-independent API for native system library
21 methods, which work with files on physical disk drives; using such methods
22 directly is faster than using a custom file engine (see QAbstractFileEngine
23 and its sub-classes). Typically, you need a custom file engine when working
24 with virtual file systems (for example QResource). Various Qt classes,
25 for example QDir, QFile, and QFileInfo, can handle both types of files by
26 detecting the file path scheme, for example, \c file:///, \c :/someresource
27 (QResource).
28
29 \sa QAbstractFileEngine, QAbstractFileEngineHandler, QFSFileEngine, QResourceFileEngine
30*/
31
32/*!
33 \internal
34
35 Returns the canonicalized form of \a path (i.e., with all symlinks
36 resolved, and all redundant path elements removed.
37*/
38QString QFileSystemEngine::slowCanonicalized(const QString &path)
39{
40 if (path.isEmpty())
41 return path;
42
43 QFileInfo fi;
44 const QChar slash(u'/');
45 QString tmpPath = path;
46 qsizetype separatorPos = 0;
47 QSet<QString> nonSymlinks;
48 QDuplicateTracker<QString> known;
49
50 (void)known.hasSeen(path);
51 do {
52#ifdef Q_OS_WIN
53 if (separatorPos == 0) {
54 if (tmpPath.size() >= 2 && tmpPath.at(0) == slash && tmpPath.at(1) == slash) {
55 // UNC, skip past the first two elements
56 separatorPos = tmpPath.indexOf(slash, 2);
57 } else if (tmpPath.size() >= 3 && tmpPath.at(1) == u':' && tmpPath.at(2) == slash) {
58 // volume root, skip since it can not be a symlink
59 separatorPos = 2;
60 }
61 }
62 if (separatorPos != -1)
63#endif
64 separatorPos = tmpPath.indexOf(slash, separatorPos + 1);
65 QString prefix = separatorPos == -1 ? tmpPath : tmpPath.left(separatorPos);
66 if (!nonSymlinks.contains(prefix)) {
67 fi.setFile(prefix);
68 if (fi.isSymLink()) {
69 QString target = fi.symLinkTarget();
70 if (separatorPos != -1) {
71 if (fi.isDir() && !target.endsWith(slash))
72 target.append(slash);
73 target.append(QStringView{tmpPath}.mid(separatorPos));
74 }
75 tmpPath = QDir::cleanPath(target);
76 separatorPos = 0;
77
78 if (known.hasSeen(tmpPath))
79 return QString();
80 } else {
81 nonSymlinks.insert(prefix);
82 }
83 }
84 } while (separatorPos != -1);
85
86 return QDir::cleanPath(tmpPath);
87}
88
89static inline bool _q_checkEntry(QFileSystemEntry &entry, QFileSystemMetaData &data, bool resolvingEntry)
90{
91 if (resolvingEntry) {
92 if (!QFileSystemEngine::fillMetaData(entry, data, QFileSystemMetaData::ExistsAttribute)
93 || !data.exists()) {
94 data.clear();
95 return false;
96 }
97 }
98
99 return true;
100}
101
102static inline bool _q_checkEntry(std::unique_ptr<QAbstractFileEngine> &engine, bool resolvingEntry)
103{
104 if (resolvingEntry) {
105 if (!(engine->fileFlags(QAbstractFileEngine::FlagsMask) & QAbstractFileEngine::ExistsFlag)) {
106 engine.reset();
107 return false;
108 }
109 }
110
111 return true;
112}
113
114static bool _q_createLegacyEngine_recursive(QFileSystemEntry &entry, QFileSystemMetaData &data,
115 std::unique_ptr<QAbstractFileEngine> &engine,
116 bool resolvingEntry = false)
117{
118 QString const &filePath = entry.filePath();
119 if ((engine = qt_custom_file_engine_handler_create(filePath)))
120 return _q_checkEntry(engine, resolvingEntry);
121
122#if defined(QT_BUILD_CORE_LIB)
123 for (qsizetype prefixSeparator = 0; prefixSeparator < filePath.size(); ++prefixSeparator) {
124 QChar const ch = filePath[prefixSeparator];
125 if (ch == u'/')
126 break;
127
128 if (ch == u':') {
129 if (prefixSeparator == 0) {
130 engine = std::make_unique<QResourceFileEngine>(filePath);
131 return _q_checkEntry(engine, resolvingEntry);
132 }
133
134 if (prefixSeparator == 1)
135 break;
136
137 const QStringList &paths = QDir::searchPaths(filePath.left(prefixSeparator));
138 for (int i = 0; i < paths.size(); i++) {
139 entry = QFileSystemEntry(QDir::cleanPath(
140 paths.at(i) % u'/' % QStringView{filePath}.mid(prefixSeparator + 1)));
141 // Recurse!
142 if (_q_createLegacyEngine_recursive(entry, data, engine, true))
143 return true;
144 }
145
146 // entry may have been clobbered at this point.
147 return false;
148 }
149
150 // There's no need to fully validate the prefix here. Consulting the
151 // unicode tables could be expensive and validation is already
152 // performed in QDir::setSearchPaths.
153 //
154 // if (!ch.isLetterOrNumber())
155 // break;
156 }
157#endif // defined(QT_BUILD_CORE_LIB)
158
159 return _q_checkEntry(entry, data, resolvingEntry);
160}
161
162Q_CORE_EXPORT bool qt_isCaseSensitive(const QFileSystemEntry &entry, QFileSystemMetaData &data)
163{
164 // called from QtGui (QFileSystemModel, QFileInfoGatherer)
165 return QFileSystemEngine::isCaseSensitive(entry, data);
166}
167
168/*!
169 \internal
170
171 Resolves the \a entry (see QDir::searchPaths) and returns an engine for
172 it, but never a QFSFileEngine.
173
174 Returns a file engine that can be used to access the entry. Returns 0 if
175 QFileSystemEngine API should be used to query and interact with the file
176 system object.
177*/
178std::unique_ptr<QAbstractFileEngine>
179QFileSystemEngine::createLegacyEngine(QFileSystemEntry &entry, QFileSystemMetaData &data)
180{
181 QFileSystemEntry copy = entry;
182 std::unique_ptr<QAbstractFileEngine> engine;
183
184 if (_q_createLegacyEngine_recursive(copy, data, engine))
185 // Reset entry to resolved copy.
186 entry = copy;
187 else
188 data.clear();
189
190 return engine;
191}
192
193//static
194QString QFileSystemEngine::resolveUserName(const QFileSystemEntry &entry, QFileSystemMetaData &metaData)
195{
196#if defined(Q_OS_WIN)
197 Q_UNUSED(metaData);
198 return QFileSystemEngine::owner(entry, QAbstractFileEngine::OwnerUser);
199#else //(Q_OS_UNIX)
200 if (!metaData.hasFlags(QFileSystemMetaData::UserId))
201 QFileSystemEngine::fillMetaData(entry, metaData, QFileSystemMetaData::UserId);
202 if (!metaData.exists())
203 return QString();
204 return resolveUserName(metaData.userId());
205#endif
206}
207
208//static
209QString QFileSystemEngine::resolveGroupName(const QFileSystemEntry &entry, QFileSystemMetaData &metaData)
210{
211#if defined(Q_OS_WIN)
212 Q_UNUSED(metaData);
213 return QFileSystemEngine::owner(entry, QAbstractFileEngine::OwnerGroup);
214#else //(Q_OS_UNIX)
215 if (!metaData.hasFlags(QFileSystemMetaData::GroupId))
216 QFileSystemEngine::fillMetaData(entry, metaData, QFileSystemMetaData::GroupId);
217 if (!metaData.exists())
218 return QString();
219 return resolveGroupName(metaData.groupId());
220#endif
221}
222
223//static
224QFileSystemEntry QFileSystemEngine::getJunctionTarget(const QFileSystemEntry &link,
225 QFileSystemMetaData &data)
226{
227#if defined(Q_OS_WIN)
228 return junctionTarget(link, data);
229#else
230 Q_UNUSED(link);
231 Q_UNUSED(data);
232 return {};
233#endif
234}
235
236QT_END_NAMESPACE
static bool _q_createLegacyEngine_recursive(QFileSystemEntry &entry, QFileSystemMetaData &data, std::unique_ptr< QAbstractFileEngine > &engine, bool resolvingEntry=false)
static bool _q_checkEntry(QFileSystemEntry &entry, QFileSystemMetaData &data, bool resolvingEntry)
static bool _q_checkEntry(std::unique_ptr< QAbstractFileEngine > &engine, bool resolvingEntry)
Q_CORE_EXPORT bool qt_isCaseSensitive(const QFileSystemEntry &entry, QFileSystemMetaData &data)