1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtCore module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 #include "qfilesystemengine_p.h"
41 #include <QtCore/qdir.h>
42 #include <QtCore/qset.h>
43 #include <QtCore/qstringbuilder.h>
44 #include <QtCore/private/qabstractfileengine_p.h>
45 #ifdef QT_BUILD_CORE_LIB
46 #include <QtCore/private/qresource_p.h>
47 #endif
48 
49 QT_BEGIN_NAMESPACE
50 
51 /*!
52     \internal
53 
54     Returns the canonicalized form of \a path (i.e., with all symlinks
55     resolved, and all redundant path elements removed.
56 */
slowCanonicalized(const QString & path)57 QString QFileSystemEngine::slowCanonicalized(const QString &path)
58 {
59     if (path.isEmpty())
60         return path;
61 
62     QFileInfo fi;
63     const QChar slash(QLatin1Char('/'));
64     QString tmpPath = path;
65     int separatorPos = 0;
66     QSet<QString> nonSymlinks;
67     QSet<QString> known;
68 
69     known.insert(path);
70     do {
71 #ifdef Q_OS_WIN
72         if (separatorPos == 0) {
73             if (tmpPath.size() >= 2 && tmpPath.at(0) == slash && tmpPath.at(1) == slash) {
74                 // UNC, skip past the first two elements
75                 separatorPos = tmpPath.indexOf(slash, 2);
76             } else if (tmpPath.size() >= 3 && tmpPath.at(1) == QLatin1Char(':') && tmpPath.at(2) == slash) {
77                 // volume root, skip since it can not be a symlink
78                 separatorPos = 2;
79             }
80         }
81         if (separatorPos != -1)
82 #endif
83         separatorPos = tmpPath.indexOf(slash, separatorPos + 1);
84         QString prefix = separatorPos == -1 ? tmpPath : tmpPath.left(separatorPos);
85         if (!nonSymlinks.contains(prefix)) {
86             fi.setFile(prefix);
87             if (fi.isSymLink()) {
88                 QString target = fi.symLinkTarget();
89                 if (separatorPos != -1) {
90                     if (fi.isDir() && !target.endsWith(slash))
91                         target.append(slash);
92                     target.append(tmpPath.midRef(separatorPos));
93                 }
94                 tmpPath = QDir::cleanPath(target);
95                 separatorPos = 0;
96 
97                 if (known.contains(tmpPath))
98                     return QString();
99                 known.insert(tmpPath);
100             } else {
101                 nonSymlinks.insert(prefix);
102             }
103         }
104     } while (separatorPos != -1);
105 
106     return QDir::cleanPath(tmpPath);
107 }
108 
_q_checkEntry(QFileSystemEntry & entry,QFileSystemMetaData & data,bool resolvingEntry)109 static inline bool _q_checkEntry(QFileSystemEntry &entry, QFileSystemMetaData &data, bool resolvingEntry)
110 {
111     if (resolvingEntry) {
112         if (!QFileSystemEngine::fillMetaData(entry, data, QFileSystemMetaData::ExistsAttribute)
113                 || !data.exists()) {
114             data.clear();
115             return false;
116         }
117     }
118 
119     return true;
120 }
121 
_q_checkEntry(QAbstractFileEngine * & engine,bool resolvingEntry)122 static inline bool _q_checkEntry(QAbstractFileEngine *&engine, bool resolvingEntry)
123 {
124     if (resolvingEntry) {
125         if (!(engine->fileFlags(QAbstractFileEngine::FlagsMask) & QAbstractFileEngine::ExistsFlag)) {
126             delete engine;
127             engine = nullptr;
128             return false;
129         }
130     }
131 
132     return true;
133 }
134 
_q_resolveEntryAndCreateLegacyEngine_recursive(QFileSystemEntry & entry,QFileSystemMetaData & data,QAbstractFileEngine * & engine,bool resolvingEntry=false)135 static bool _q_resolveEntryAndCreateLegacyEngine_recursive(QFileSystemEntry &entry, QFileSystemMetaData &data,
136         QAbstractFileEngine *&engine, bool resolvingEntry = false)
137 {
138     QString const &filePath = entry.filePath();
139     if ((engine = qt_custom_file_engine_handler_create(filePath)))
140         return _q_checkEntry(engine, resolvingEntry);
141 
142 #if defined(QT_BUILD_CORE_LIB)
143     for (int prefixSeparator = 0; prefixSeparator < filePath.size(); ++prefixSeparator) {
144         QChar const ch = filePath[prefixSeparator];
145         if (ch == QLatin1Char('/'))
146             break;
147 
148         if (ch == QLatin1Char(':')) {
149             if (prefixSeparator == 0) {
150                 engine = new QResourceFileEngine(filePath);
151                 return _q_checkEntry(engine, resolvingEntry);
152             }
153 
154             if (prefixSeparator == 1)
155                 break;
156 
157             const QStringList &paths = QDir::searchPaths(filePath.left(prefixSeparator));
158             for (int i = 0; i < paths.count(); i++) {
159                 entry = QFileSystemEntry(QDir::cleanPath(paths.at(i) % QLatin1Char('/') % filePath.midRef(prefixSeparator + 1)));
160                 // Recurse!
161                 if (_q_resolveEntryAndCreateLegacyEngine_recursive(entry, data, engine, true))
162                     return true;
163             }
164 
165             // entry may have been clobbered at this point.
166             return false;
167         }
168 
169         //  There's no need to fully validate the prefix here. Consulting the
170         //  unicode tables could be expensive and validation is already
171         //  performed in QDir::setSearchPaths.
172         //
173         //  if (!ch.isLetterOrNumber())
174         //      break;
175     }
176 #endif // defined(QT_BUILD_CORE_LIB)
177 
178     return _q_checkEntry(entry, data, resolvingEntry);
179 }
180 
181 /*!
182     \internal
183 
184     Resolves the \a entry (see QDir::searchPaths) and returns an engine for
185     it, but never a QFSFileEngine.
186 
187     Returns a file engine that can be used to access the entry. Returns 0 if
188     QFileSystemEngine API should be used to query and interact with the file
189     system object.
190 */
resolveEntryAndCreateLegacyEngine(QFileSystemEntry & entry,QFileSystemMetaData & data)191 QAbstractFileEngine *QFileSystemEngine::resolveEntryAndCreateLegacyEngine(
192         QFileSystemEntry &entry, QFileSystemMetaData &data) {
193     QFileSystemEntry copy = entry;
194     QAbstractFileEngine *engine = nullptr;
195 
196     if (_q_resolveEntryAndCreateLegacyEngine_recursive(copy, data, engine))
197         // Reset entry to resolved copy.
198         entry = copy;
199     else
200         data.clear();
201 
202     return engine;
203 }
204 
205 //static
resolveUserName(const QFileSystemEntry & entry,QFileSystemMetaData & metaData)206 QString QFileSystemEngine::resolveUserName(const QFileSystemEntry &entry, QFileSystemMetaData &metaData)
207 {
208 #if defined(Q_OS_WIN)
209     Q_UNUSED(metaData);
210     return QFileSystemEngine::owner(entry, QAbstractFileEngine::OwnerUser);
211 #else //(Q_OS_UNIX)
212     if (!metaData.hasFlags(QFileSystemMetaData::UserId))
213         QFileSystemEngine::fillMetaData(entry, metaData, QFileSystemMetaData::UserId);
214     if (!metaData.exists())
215         return QString();
216     return resolveUserName(metaData.userId());
217 #endif
218 }
219 
220 //static
resolveGroupName(const QFileSystemEntry & entry,QFileSystemMetaData & metaData)221 QString QFileSystemEngine::resolveGroupName(const QFileSystemEntry &entry, QFileSystemMetaData &metaData)
222 {
223 #if defined(Q_OS_WIN)
224     Q_UNUSED(metaData);
225     return QFileSystemEngine::owner(entry, QAbstractFileEngine::OwnerGroup);
226 #else //(Q_OS_UNIX)
227     if (!metaData.hasFlags(QFileSystemMetaData::GroupId))
228         QFileSystemEngine::fillMetaData(entry, metaData, QFileSystemMetaData::GroupId);
229     if (!metaData.exists())
230         return QString();
231     return resolveGroupName(metaData.groupId());
232 #endif
233 }
234 
235 QT_END_NAMESPACE
236