1 /****************************************************************************
2 **
3 ** Copyright (C) 2019 The Qt Company Ltd.
4 ** Copyright (C) 2020 Intel Corporation.
5 ** Contact: https://www.qt.io/licensing/
6 **
7 ** This file is part of the QtCore module of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** Commercial License Usage
11 ** Licensees holding valid commercial Qt licenses may use this file in
12 ** accordance with the commercial license agreement provided with the
13 ** Software or, alternatively, in accordance with the terms contained in
14 ** a written agreement between you and The Qt Company. For licensing terms
15 ** and conditions see https://www.qt.io/terms-conditions. For further
16 ** information use the contact form at https://www.qt.io/contact-us.
17 **
18 ** GNU Lesser General Public License Usage
19 ** Alternatively, this file may be used under the terms of the GNU Lesser
20 ** General Public License version 3 as published by the Free Software
21 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
22 ** packaging of this file. Please review the following information to
23 ** ensure the GNU Lesser General Public License version 3 requirements
24 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
25 **
26 ** GNU General Public License Usage
27 ** Alternatively, this file may be used under the terms of the GNU
28 ** General Public License version 2.0 or (at your option) the GNU General
29 ** Public license version 3 or any later version approved by the KDE Free
30 ** Qt Foundation. The licenses are as published by the Free Software
31 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
32 ** included in the packaging of this file. Please review the following
33 ** information to ensure the GNU General Public License requirements will
34 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
35 ** https://www.gnu.org/licenses/gpl-3.0.html.
36 **
37 ** $QT_END_LICENSE$
38 **
39 ****************************************************************************/
40 
41 #include "qresource.h"
42 #include "qresource_p.h"
43 #include "qresource_iterator_p.h"
44 #include "qset.h"
45 #include <private/qlocking_p.h>
46 #include "qdebug.h"
47 #include "qlocale.h"
48 #include "qglobal.h"
49 #include "qvector.h"
50 #include "qdatetime.h"
51 #include "qbytearray.h"
52 #include "qstringlist.h"
53 #include "qendian.h"
54 #include <qshareddata.h>
55 #include <qplatformdefs.h>
56 #include <qendian.h>
57 #include "private/qabstractfileengine_p.h"
58 #include "private/qnumeric_p.h"
59 #include "private/qsimd_p.h"
60 #include "private/qtools_p.h"
61 #include "private/qsystemerror_p.h"
62 
63 #ifndef QT_NO_COMPRESS
64 #  include <zconf.h>
65 #  include <zlib.h>
66 #endif
67 #if QT_CONFIG(zstd)
68 #  include <zstd.h>
69 #endif
70 
71 #if defined(Q_OS_UNIX) && !defined(Q_OS_NACL) && !defined(Q_OS_INTEGRITY)
72 #  define QT_USE_MMAP
73 #  include <sys/mman.h>
74 #endif
75 
76 //#define DEBUG_RESOURCE_MATCH
77 
78 QT_BEGIN_NAMESPACE
79 
80 // Symbols used by code generated by RCC.
81 // They cause compilation errors if the RCC content couldn't
82 // be interpreted by this QtCore version.
83 #if defined(__ELF__) || defined(__APPLE__)  // same as RCC generates
84 #  define RCC_FEATURE_SYMBOL(feature)   \
85     extern Q_CORE_EXPORT const quint8 qt_resourceFeature ## feature; \
86     const quint8 qt_resourceFeature ## feature = 0;
87 #else
88 #  define RCC_FEATURE_SYMBOL(feature)   \
89     Q_CORE_EXPORT quint8 qResourceFeature ## feature() { return 0; }
90 #endif
91 
92 #ifndef QT_NO_COMPRESS
93 RCC_FEATURE_SYMBOL(Zlib)
94 #endif
95 #if QT_CONFIG(zstd)
96 RCC_FEATURE_SYMBOL(Zstd)
97 #endif
98 
99 #undef RCC_FEATURE_SYMBOL
100 
101 class QStringSplitter
102 {
103 public:
QStringSplitter(QStringView sv)104     explicit QStringSplitter(QStringView sv)
105         : m_data(sv.data()), m_len(sv.size())
106     {
107     }
108 
hasNext()109     inline bool hasNext() {
110         while (m_pos < m_len && m_data[m_pos] == m_splitChar)
111             ++m_pos;
112         return m_pos < m_len;
113     }
114 
next()115     inline QStringView next() {
116         int start = m_pos;
117         while (m_pos < m_len && m_data[m_pos] != m_splitChar)
118             ++m_pos;
119         return QStringView(m_data + start, m_pos - start);
120     }
121 
122     const QChar *m_data;
123     qsizetype m_len;
124     qsizetype m_pos = 0;
125     QChar m_splitChar = QLatin1Char('/');
126 };
127 
128 
129 //resource glue
130 class QResourceRoot
131 {
132 public:
133     enum Flags
134     {
135         // must match rcc.h
136         Compressed = 0x01,
137         Directory = 0x02,
138         CompressedZstd = 0x04
139     };
140 private:
141     const uchar *tree, *names, *payloads;
142     int version;
findOffset(int node) const143     inline int findOffset(int node) const { return node * (14 + (version >= 0x02 ? 8 : 0)); } //sizeof each tree element
144     uint hash(int node) const;
145     QString name(int node) const;
146     short flags(int node) const;
147 public:
148     mutable QAtomicInt ref;
149 
QResourceRoot()150     inline QResourceRoot(): tree(nullptr), names(nullptr), payloads(nullptr), version(0) {}
QResourceRoot(int version,const uchar * t,const uchar * n,const uchar * d)151     inline QResourceRoot(int version, const uchar *t, const uchar *n, const uchar *d) { setSource(version, t, n, d); }
~QResourceRoot()152     virtual ~QResourceRoot() { }
153     int findNode(const QString &path, const QLocale &locale=QLocale()) const;
isContainer(int node) const154     inline bool isContainer(int node) const { return flags(node) & Directory; }
compressionAlgo(int node)155     QResource::Compression compressionAlgo(int node)
156     {
157         uint compressionFlags = flags(node) & (Compressed | CompressedZstd);
158         if (compressionFlags == Compressed)
159             return QResource::ZlibCompression;
160         if (compressionFlags == CompressedZstd)
161             return QResource::ZstdCompression;
162         return QResource::NoCompression;
163     }
164     const uchar *data(int node, qint64 *size) const;
165     quint64 lastModified(int node) const;
166     QStringList children(int node) const;
mappingRoot() const167     virtual QString mappingRoot() const { return QString(); }
168     bool mappingRootSubdir(const QString &path, QString *match = nullptr) const;
operator ==(const QResourceRoot & other) const169     inline bool operator==(const QResourceRoot &other) const
170     { return tree == other.tree && names == other.names && payloads == other.payloads && version == other.version; }
operator !=(const QResourceRoot & other) const171     inline bool operator!=(const QResourceRoot &other) const
172     { return !operator==(other); }
173     enum ResourceRootType { Resource_Builtin, Resource_File, Resource_Buffer };
type() const174     virtual ResourceRootType type() const { return Resource_Builtin; }
175 
176 protected:
setSource(int v,const uchar * t,const uchar * n,const uchar * d)177     inline void setSource(int v, const uchar *t, const uchar *n, const uchar *d) {
178         tree = t;
179         names = n;
180         payloads = d;
181         version = v;
182     }
183 };
184 
cleanPath(const QString & _path)185 static QString cleanPath(const QString &_path)
186 {
187     QString path = QDir::cleanPath(_path);
188     // QDir::cleanPath does not remove two trailing slashes under _Windows_
189     // due to support for UNC paths. Remove those manually.
190     if (path.startsWith(QLatin1String("//")))
191         path.remove(0, 1);
192     return path;
193 }
194 
195 Q_DECLARE_TYPEINFO(QResourceRoot, Q_MOVABLE_TYPE);
196 
197 typedef QList<QResourceRoot*> ResourceList;
198 struct QResourceGlobalData
199 {
200     QRecursiveMutex resourceMutex;
201     ResourceList resourceList;
202     QStringList resourceSearchPaths;
203 };
Q_GLOBAL_STATIC(QResourceGlobalData,resourceGlobalData)204 Q_GLOBAL_STATIC(QResourceGlobalData, resourceGlobalData)
205 
206 static inline QRecursiveMutex &resourceMutex()
207 { return resourceGlobalData->resourceMutex; }
208 
resourceList()209 static inline ResourceList *resourceList()
210 { return &resourceGlobalData->resourceList; }
211 
resourceSearchPaths()212 static inline QStringList *resourceSearchPaths()
213 { return &resourceGlobalData->resourceSearchPaths; }
214 
215 /*!
216     \class QResource
217     \inmodule QtCore
218     \brief The QResource class provides an interface for reading directly from resources.
219 
220     \ingroup io
221 
222     \reentrant
223     \since 4.2
224 
225     QResource is an object that represents a set of data (and possibly
226     children) relating to a single resource entity. QResource gives direct
227     access to the bytes in their raw format. In this way direct access
228     allows reading data without buffer copying or indirection. Indirection
229     is often useful when interacting with the resource entity as if it is a
230     file, this can be achieved with QFile. The data and children behind a
231     QResource are normally compiled into an application/library, but it is
232     also possible to load a resource at runtime. When loaded at run time
233     the resource file will be loaded as one big set of data and then given
234     out in pieces via references into the resource tree.
235 
236     A QResource can either be loaded with an absolute path, either treated
237     as a file system rooted with a \c{/} character, or in resource notation
238     rooted with a \c{:} character. A relative resource can also be opened
239     which will be found in the list of paths returned by QDir::searchPaths().
240 
241     A QResource that is representing a file will have data backing it, this
242     data can possibly be compressed, in which case qUncompress() must be
243     used to access the real data; this happens implicitly when accessed
244     through a QFile. A QResource that is representing a directory will have
245     only children and no data.
246 
247     \section1 Dynamic Resource Loading
248 
249     A resource can be left out of an application's binary and loaded when
250     it is needed at run-time by using the registerResource() function. The
251     resource file passed into registerResource() must be a binary resource
252     as created by rcc. Further information about binary resources can be
253     found in \l{The Qt Resource System} documentation.
254 
255     This can often be useful when loading a large set of application icons
256     that may change based on a setting, or that can be edited by a user and
257     later recreated. The resource is immediately loaded into memory, either
258     as a result of a single file read operation, or as a memory mapped file.
259 
260     This approach can prove to be a significant performance gain as only a
261     single file will be loaded, and pieces of data will be given out via the
262     path requested in setFileName().
263 
264     The unregisterResource() function removes a reference to a particular
265     file. If there are QResource objects that currently reference resources related
266     to the unregistered file, they will continue to be valid but the resource
267     file itself will be removed from the resource roots, and thus no further
268     QResource can be created pointing into this resource data. The resource
269     itself will be unmapped from memory when the last QResource that points
270     to it is destroyed.
271 
272     \sa {The Qt Resource System}, QFile, QDir, QFileInfo
273 */
274 
275 /*!
276     \enum QResource::Compression
277     \since 5.13
278 
279     This enum is used by compressionAlgorithm() to indicate which algorithm the
280     RCC tool used to compress the payload.
281 
282     \value NoCompression    Contents are not compressed
283     \value ZlibCompression  Contents are compressed using \l{https://zlib.net}{zlib} and can
284                             be decompressed using the qUncompress() function.
285     \value ZstdCompression  Contents are compressed using \l{https://zstd.net}{zstd}. To
286                             decompress, use the \c{ZSTD_decompress} function from the zstd
287                             library.
288 
289     \sa compressionAlgorithm()
290 */
291 
292 class QResourcePrivate {
293 public:
QResourcePrivate(QResource * _q)294     inline QResourcePrivate(QResource *_q) : q_ptr(_q) { clear(); }
~QResourcePrivate()295     inline ~QResourcePrivate() { clear(); }
296 
297     void ensureInitialized() const;
298     void ensureChildren() const;
299     qint64 uncompressedSize() const Q_DECL_PURE_FUNCTION;
300     qsizetype decompress(char *buffer, qsizetype bufferSize) const;
301 
302     bool load(const QString &file);
303     void clear();
304 
305     QLocale locale;
306     QString fileName, absoluteFilePath;
307     QList<QResourceRoot*> related;
308     mutable qint64 size;
309     mutable quint64 lastModified;
310     mutable const uchar *data;
311     mutable QStringList children;
312     mutable quint8 compressionAlgo;
313     bool container;
314     /* 2 or 6 padding bytes */
315 
316     QResource *q_ptr;
317     Q_DECLARE_PUBLIC(QResource)
318 };
319 
320 void
clear()321 QResourcePrivate::clear()
322 {
323     absoluteFilePath.clear();
324     compressionAlgo = QResource::NoCompression;
325     data = nullptr;
326     size = 0;
327     children.clear();
328     lastModified = 0;
329     container = 0;
330     for(int i = 0; i < related.size(); ++i) {
331         QResourceRoot *root = related.at(i);
332         if(!root->ref.deref())
333             delete root;
334     }
335     related.clear();
336 }
337 
338 bool
load(const QString & file)339 QResourcePrivate::load(const QString &file)
340 {
341     related.clear();
342     const auto locker = qt_scoped_lock(resourceMutex());
343     const ResourceList *list = resourceList();
344     QString cleaned = cleanPath(file);
345     for(int i = 0; i < list->size(); ++i) {
346         QResourceRoot *res = list->at(i);
347         const int node = res->findNode(cleaned, locale);
348         if(node != -1) {
349             if(related.isEmpty()) {
350                 container = res->isContainer(node);
351                 if(!container) {
352                     data = res->data(node, &size);
353                     compressionAlgo = res->compressionAlgo(node);
354                 } else {
355                     data = nullptr;
356                     size = 0;
357                     compressionAlgo = QResource::NoCompression;
358                 }
359                 lastModified = res->lastModified(node);
360             } else if(res->isContainer(node) != container) {
361                 qWarning("QResourceInfo: Resource [%s] has both data and children!", file.toLatin1().constData());
362             }
363             res->ref.ref();
364             related.append(res);
365         } else if(res->mappingRootSubdir(file)) {
366             container = true;
367             data = nullptr;
368             size = 0;
369             compressionAlgo = QResource::NoCompression;
370             lastModified = 0;
371             res->ref.ref();
372             related.append(res);
373         }
374     }
375     return !related.isEmpty();
376 }
377 
378 void
ensureInitialized() const379 QResourcePrivate::ensureInitialized() const
380 {
381     if(!related.isEmpty())
382         return;
383     QResourcePrivate *that = const_cast<QResourcePrivate *>(this);
384     if(fileName == QLatin1String(":"))
385         that->fileName += QLatin1Char('/');
386     that->absoluteFilePath = fileName;
387     if(!that->absoluteFilePath.startsWith(QLatin1Char(':')))
388         that->absoluteFilePath.prepend(QLatin1Char(':'));
389 
390     QStringRef path(&fileName);
391     if(path.startsWith(QLatin1Char(':')))
392         path = path.mid(1);
393 
394     if(path.startsWith(QLatin1Char('/'))) {
395         that->load(path.toString());
396     } else {
397         const auto locker = qt_scoped_lock(resourceMutex());
398         QStringList searchPaths = *resourceSearchPaths();
399         searchPaths << QLatin1String("");
400         for(int i = 0; i < searchPaths.size(); ++i) {
401             const QString searchPath(searchPaths.at(i) + QLatin1Char('/') + path);
402             if(that->load(searchPath)) {
403                 that->absoluteFilePath = QLatin1Char(':') + searchPath;
404                 break;
405             }
406         }
407     }
408 }
409 
410 void
ensureChildren() const411 QResourcePrivate::ensureChildren() const
412 {
413     ensureInitialized();
414     if(!children.isEmpty() || !container || related.isEmpty())
415         return;
416 
417     QString path = absoluteFilePath, k;
418     if(path.startsWith(QLatin1Char(':')))
419         path = path.mid(1);
420     QSet<QString> kids;
421     QString cleaned = cleanPath(path);
422     for(int i = 0; i < related.size(); ++i) {
423         QResourceRoot *res = related.at(i);
424         if(res->mappingRootSubdir(path, &k) && !k.isEmpty()) {
425             if(!kids.contains(k)) {
426                 children += k;
427                 kids.insert(k);
428             }
429         } else {
430             const int node = res->findNode(cleaned);
431             if(node != -1) {
432                 QStringList related_children = res->children(node);
433                 for(int kid = 0; kid < related_children.size(); ++kid) {
434                     k = related_children.at(kid);
435                     if(!kids.contains(k)) {
436                         children += k;
437                         kids.insert(k);
438                     }
439                 }
440             }
441         }
442     }
443 }
444 
uncompressedSize() const445 qint64 QResourcePrivate::uncompressedSize() const
446 {
447     switch (compressionAlgo) {
448     case QResource::NoCompression:
449         return size;
450 
451     case QResource::ZlibCompression:
452 #ifndef QT_NO_COMPRESS
453         if (size_t(size) >= sizeof(quint32))
454             return qFromBigEndian<quint32>(data);
455 #else
456         Q_ASSERT(!"QResource: Qt built without support for Zlib compression");
457         Q_UNREACHABLE();
458 #endif
459         break;
460 
461     case QResource::ZstdCompression: {
462 #if QT_CONFIG(zstd)
463         size_t n = ZSTD_getFrameContentSize(data, size);
464         return ZSTD_isError(n) ? -1 : qint64(n);
465 #else
466         // This should not happen because we've refused to load such resource
467         Q_ASSERT(!"QResource: Qt built without support for Zstd compression");
468         Q_UNREACHABLE();
469 #endif
470     }
471 
472     }
473     return -1;
474 }
475 
decompress(char * buffer,qsizetype bufferSize) const476 qsizetype QResourcePrivate::decompress(char *buffer, qsizetype bufferSize) const
477 {
478     Q_ASSERT(data);
479 
480     switch (compressionAlgo) {
481     case QResource::NoCompression:
482         Q_UNREACHABLE();
483         break;
484 
485     case QResource::ZlibCompression: {
486 #ifndef QT_NO_COMPRESS
487         uLong len = uLong(bufferSize);
488         int res = ::uncompress(reinterpret_cast<Bytef *>(buffer), &len,
489                                data + sizeof(quint32), uLong(size - sizeof(quint32)));
490         if (res != Z_OK) {
491             qWarning("QResource: error decompressing zlib content (%d)", res);
492             return -1;
493         }
494         return len;
495 #else
496         Q_UNREACHABLE();
497 #endif
498     }
499 
500     case QResource::ZstdCompression: {
501 #if QT_CONFIG(zstd)
502         size_t usize = ZSTD_decompress(buffer, bufferSize, data, size);
503         if (ZSTD_isError(usize)) {
504             qWarning("QResource: error decompressing zstd content: %s", ZSTD_getErrorName(usize));
505             return -1;
506         }
507         return usize;
508 #else
509         Q_UNREACHABLE();
510 #endif
511     }
512     }
513 
514     return -1;
515 }
516 
517 /*!
518     Constructs a QResource pointing to \a file. \a locale is used to
519     load a specific localization of a resource data.
520 
521     \sa QFileInfo, QDir::searchPaths(), setFileName(), setLocale()
522 */
523 
QResource(const QString & file,const QLocale & locale)524 QResource::QResource(const QString &file, const QLocale &locale) : d_ptr(new QResourcePrivate(this))
525 {
526     Q_D(QResource);
527     d->fileName = file;
528     d->locale = locale;
529 }
530 
531 /*!
532     Releases the resources of the QResource object.
533 */
~QResource()534 QResource::~QResource()
535 {
536 }
537 
538 /*!
539     Sets a QResource to only load the localization of resource to for \a
540     locale. If a resource for the specific locale is not found then the
541     C locale is used.
542 
543     \sa setFileName()
544 */
545 
setLocale(const QLocale & locale)546 void QResource::setLocale(const QLocale &locale)
547 {
548     Q_D(QResource);
549     d->clear();
550     d->locale = locale;
551 }
552 
553 /*!
554     Returns the locale used to locate the data for the QResource.
555 */
556 
locale() const557 QLocale QResource::locale() const
558 {
559     Q_D(const QResource);
560     return d->locale;
561 }
562 
563 /*!
564     Sets a QResource to point to \a file. \a file can either be absolute,
565     in which case it is opened directly, if relative then the file will be
566     tried to be found in QDir::searchPaths().
567 
568     \sa absoluteFilePath()
569 */
570 
setFileName(const QString & file)571 void QResource::setFileName(const QString &file)
572 {
573     Q_D(QResource);
574     d->clear();
575     d->fileName = file;
576 }
577 
578 /*!
579     Returns the full path to the file that this QResource represents as it
580     was passed.
581 
582     \sa absoluteFilePath()
583 */
584 
fileName() const585 QString QResource::fileName() const
586 {
587     Q_D(const QResource);
588     d->ensureInitialized();
589     return d->fileName;
590 }
591 
592 /*!
593     Returns the real path that this QResource represents, if the resource
594     was found via the QDir::searchPaths() it will be indicated in the path.
595 
596     \sa fileName()
597 */
598 
absoluteFilePath() const599 QString QResource::absoluteFilePath() const
600 {
601     Q_D(const QResource);
602     d->ensureInitialized();
603     return d->absoluteFilePath;
604 }
605 
606 /*!
607     Returns \c true if the resource really exists in the resource hierarchy,
608     false otherwise.
609 
610 */
611 
isValid() const612 bool QResource::isValid() const
613 {
614     Q_D(const QResource);
615     d->ensureInitialized();
616     return !d->related.isEmpty();
617 }
618 
619 /*!
620     \fn bool QResource::isFile() const
621 
622     Returns \c true if the resource represents a file and thus has data
623     backing it, false if it represents a directory.
624 
625     \sa isDir()
626 */
627 
628 #if QT_DEPRECATED_SINCE(5, 13)
629 /*!
630     \obsolete
631 
632     Returns \c true if the resource represents a file and the data backing it
633     is in a compressed format, false otherwise. If the data is compressed,
634     check compressionAlgorithm() to verify what algorithm to use to decompress
635     the data.
636 
637     \note This function is deprecated and can be replaced with
638     \code
639       compressionAlgorithm() != NoCompression
640     \endcode
641 
642     \sa data(), compressionAlgorithm(), isFile()
643 */
644 
isCompressed() const645 bool QResource::isCompressed() const
646 {
647     return compressionAlgorithm() != NoCompression;
648 }
649 #endif
650 
651 /*!
652     \since 5.13
653 
654     Returns the compression type that this resource is compressed with, if any.
655     If it is not compressed, this function returns QResource::NoCompression.
656 
657     If this function returns QResource::ZlibCompression, you may decompress the
658     data using the qUncompress() function. Up until Qt 5.13, this was the only
659     possible compression algorithm.
660 
661     If this function returns QResource::ZstdCompression, you need to use the
662     Zstandard library functios (\c{<zstd.h> header). Qt does not provide a
663     wrapper.
664 
665     See \l{http://facebook.github.io/zstd/zstd_manual.html}{Zstandard manual}.
666 
667     \sa data(), isFile()
668 */
compressionAlgorithm() const669 QResource::Compression QResource::compressionAlgorithm() const
670 {
671     Q_D(const QResource);
672     d->ensureInitialized();
673     return Compression(d->compressionAlgo);
674 }
675 
676 /*!
677     Returns the size of the stored data backing the resource.
678 
679     If the resource is compressed, this function returns the size of the
680     compressed data. See uncompressedSize() for the uncompressed size.
681 
682     \sa data(), uncompressedSize(), isFile()
683 */
684 
size() const685 qint64 QResource::size() const
686 {
687     Q_D(const QResource);
688     d->ensureInitialized();
689     return d->size;
690 }
691 
692 /*!
693     \since 5.15
694 
695     Returns the size of the data in this resource. If the data was not
696     compressed, this function returns the same as size(). If it was, then this
697     function extracts the size of the original uncompressed data from the
698     stored stream.
699 
700     \sa size(), uncompressedData(), isFile()
701 */
uncompressedSize() const702 qint64 QResource::uncompressedSize() const
703 {
704     Q_D(const QResource);
705     d->ensureInitialized();
706     return d->uncompressedSize();
707 }
708 
709 /*!
710     Returns direct access to a segment of read-only data, that this resource
711     represents. If the resource is compressed, the data returned is also
712     compressed. The caller must then decompress the data or use
713     uncompressedData(). If the resource is a directory, \c nullptr is returned.
714 
715     \sa uncompressedData(), size(), isFile()
716 */
717 
data() const718 const uchar *QResource::data() const
719 {
720     Q_D(const QResource);
721     d->ensureInitialized();
722     return d->data;
723 }
724 
725 /*!
726     \since 5.15
727 
728     Returns the resource data, decompressing it first, if the data was stored
729     compressed. If the resource is a directory or an error occurs while
730     decompressing, a null QByteArray is returned.
731 
732     \note If the data was compressed, this function will decompress every time
733     it is called. The result is not cached between calls.
734 
735     \sa uncompressedSize(), size(), isCompressed(), isFile()
736 */
737 
uncompressedData() const738 QByteArray QResource::uncompressedData() const
739 {
740     Q_D(const QResource);
741     qint64 n = uncompressedSize();
742     if (n < 0)
743         return QByteArray();
744     if (n > std::numeric_limits<QByteArray::size_type>::max()) {
745         qWarning("QResource: compressed content does not fit into a QByteArray; use QFile instead");
746         return QByteArray();
747     }
748     if (d->compressionAlgo == NoCompression)
749         return QByteArray::fromRawData(reinterpret_cast<const char *>(d->data), n);
750 
751     // decompress
752     QByteArray result(n, Qt::Uninitialized);
753     n = d->decompress(result.data(), n);
754     if (n < 0)
755         result.clear();
756     else
757         result.truncate(n);
758     return result;
759 }
760 
761 /*!
762     \since 5.8
763 
764     Returns the date and time when the file was last modified before
765     packaging into a resource.
766 */
lastModified() const767 QDateTime QResource::lastModified() const
768 {
769     Q_D(const QResource);
770     d->ensureInitialized();
771     return d->lastModified ? QDateTime::fromMSecsSinceEpoch(d->lastModified) : QDateTime();
772 }
773 
774 /*!
775     Returns \c true if the resource represents a directory and thus may have
776     children() in it, false if it represents a file.
777 
778     \sa isFile()
779 */
780 
isDir() const781 bool QResource::isDir() const
782 {
783     Q_D(const QResource);
784     d->ensureInitialized();
785     return d->container;
786 }
787 
788 /*!
789     Returns a list of all resources in this directory, if the resource
790     represents a file the list will be empty.
791 
792     \sa isDir()
793 */
794 
children() const795 QStringList QResource::children() const
796 {
797     Q_D(const QResource);
798     d->ensureChildren();
799     return d->children;
800 }
801 
802 #if QT_DEPRECATED_SINCE(5, 13)
803 /*!
804   \obsolete
805 
806   Use QDir::addSearchPath() with a prefix instead.
807 
808   Adds \a path to the search paths searched in to find resources that are
809   not specified with an absolute path. The \a path must be an absolute
810   path (start with \c{/}).
811 
812   The default search path is to search only in the root (\c{:/}). The last
813   path added will be consulted first upon next QResource creation.
814 */
815 void
addSearchPath(const QString & path)816 QResource::addSearchPath(const QString &path)
817 {
818     if (!path.startsWith(QLatin1Char('/'))) {
819         qWarning("QResource::addResourceSearchPath: Search paths must be absolute (start with /) [%s]",
820                  path.toLocal8Bit().data());
821         return;
822     }
823     const auto locker = qt_scoped_lock(resourceMutex());
824     resourceSearchPaths()->prepend(path);
825 }
826 
827 /*!
828   \obsolete
829 
830   Use QDir::searchPaths() instead.
831 
832   Returns the current search path list. This list is consulted when
833   creating a relative resource.
834 
835   \sa QDir::addSearchPath(), QDir::setSearchPaths()
836 */
837 
838 QStringList
searchPaths()839 QResource::searchPaths()
840 {
841     const auto locker = qt_scoped_lock(resourceMutex());
842     return *resourceSearchPaths();
843 }
844 #endif
845 
hash(int node) const846 inline uint QResourceRoot::hash(int node) const
847 {
848     if(!node) //root
849         return 0;
850     const int offset = findOffset(node);
851     qint32 name_offset = qFromBigEndian<qint32>(tree + offset);
852     name_offset += 2; //jump past name length
853     return qFromBigEndian<quint32>(names + name_offset);
854 }
name(int node) const855 inline QString QResourceRoot::name(int node) const
856 {
857     if(!node) // root
858         return QString();
859     const int offset = findOffset(node);
860 
861     QString ret;
862     qint32 name_offset = qFromBigEndian<qint32>(tree + offset);
863     quint16 name_length = qFromBigEndian<qint16>(names + name_offset);
864     name_offset += 2;
865     name_offset += 4; //jump past hash
866 
867     ret.resize(name_length);
868     QChar *strData = ret.data();
869     qFromBigEndian<ushort>(names + name_offset, name_length, strData);
870     return ret;
871 }
872 
findNode(const QString & _path,const QLocale & locale) const873 int QResourceRoot::findNode(const QString &_path, const QLocale &locale) const
874 {
875     QString path = _path;
876     {
877         QString root = mappingRoot();
878         if(!root.isEmpty()) {
879             if(root == path) {
880                 path = QLatin1Char('/');
881             } else {
882                 if(!root.endsWith(QLatin1Char('/')))
883                     root += QLatin1Char('/');
884                 if(path.size() >= root.size() && path.startsWith(root))
885                     path = path.mid(root.length()-1);
886                 if(path.isEmpty())
887                     path = QLatin1Char('/');
888             }
889         }
890     }
891 #ifdef DEBUG_RESOURCE_MATCH
892     qDebug() << "!!!!" << "START" << path << locale.country() << locale.language();
893 #endif
894 
895     if(path == QLatin1String("/"))
896         return 0;
897 
898     //the root node is always first
899     qint32 child_count = qFromBigEndian<qint32>(tree + 6);
900     qint32 child       = qFromBigEndian<qint32>(tree + 10);
901 
902     //now iterate up the tree
903     int node = -1;
904 
905     QStringSplitter splitter(path);
906     while (child_count && splitter.hasNext()) {
907         QStringView segment = splitter.next();
908 
909 #ifdef DEBUG_RESOURCE_MATCH
910         qDebug() << "  CHILDREN" << segment;
911         for(int j = 0; j < child_count; ++j) {
912             qDebug() << "   " << child+j << " :: " << name(child+j);
913         }
914 #endif
915         const uint h = qt_hash(segment);
916 
917         //do the binary search for the hash
918         int l = 0, r = child_count-1;
919         int sub_node = (l+r+1)/2;
920         while(r != l) {
921             const uint sub_node_hash = hash(child+sub_node);
922             if(h == sub_node_hash)
923                 break;
924             else if(h < sub_node_hash)
925                 r = sub_node - 1;
926             else
927                 l = sub_node;
928             sub_node = (l + r + 1) / 2;
929         }
930         sub_node += child;
931 
932         //now do the "harder" compares
933         bool found = false;
934         if(hash(sub_node) == h) {
935             while(sub_node > child && hash(sub_node-1) == h) //backup for collisions
936                 --sub_node;
937             for(; sub_node < child+child_count && hash(sub_node) == h; ++sub_node) { //here we go...
938                 if(name(sub_node) == segment) {
939                     found = true;
940                     int offset = findOffset(sub_node);
941 #ifdef DEBUG_RESOURCE_MATCH
942                     qDebug() << "  TRY" << sub_node << name(sub_node) << offset;
943 #endif
944                     offset += 4;  //jump past name
945 
946                     const qint16 flags = qFromBigEndian<qint16>(tree + offset);
947                     offset += 2;
948 
949                     if(!splitter.hasNext()) {
950                         if(!(flags & Directory)) {
951                             const qint16 country = qFromBigEndian<qint16>(tree + offset);
952                             offset += 2;
953 
954                             const qint16 language = qFromBigEndian<qint16>(tree + offset);
955                             offset += 2;
956 #ifdef DEBUG_RESOURCE_MATCH
957                             qDebug() << "    " << "LOCALE" << country << language;
958 #endif
959                             if(country == locale.country() && language == locale.language()) {
960 #ifdef DEBUG_RESOURCE_MATCH
961                                 qDebug() << "!!!!" << "FINISHED" << __LINE__ << sub_node;
962 #endif
963                                 return sub_node;
964                             } else if((country == QLocale::AnyCountry && language == locale.language()) ||
965                                       (country == QLocale::AnyCountry && language == QLocale::C && node == -1)) {
966                                 node = sub_node;
967                             }
968                             continue;
969                         } else {
970 #ifdef DEBUG_RESOURCE_MATCH
971                             qDebug() << "!!!!" << "FINISHED" << __LINE__ << sub_node;
972 #endif
973 
974                             return sub_node;
975                         }
976                     }
977 
978                     if(!(flags & Directory))
979                         return -1;
980 
981                     child_count = qFromBigEndian<qint32>(tree + offset);
982                     offset += 4;
983                     child = qFromBigEndian<qint32>(tree + offset);
984                     break;
985                 }
986             }
987         }
988         if(!found)
989             break;
990     }
991 #ifdef DEBUG_RESOURCE_MATCH
992     qDebug() << "!!!!" << "FINISHED" << __LINE__ << node;
993 #endif
994     return node;
995 }
flags(int node) const996 short QResourceRoot::flags(int node) const
997 {
998     if(node == -1)
999         return 0;
1000     const int offset = findOffset(node) + 4; //jump past name
1001     return qFromBigEndian<qint16>(tree + offset);
1002 }
data(int node,qint64 * size) const1003 const uchar *QResourceRoot::data(int node, qint64 *size) const
1004 {
1005     if(node == -1) {
1006         *size = 0;
1007         return nullptr;
1008     }
1009     int offset = findOffset(node) + 4; //jump past name
1010 
1011     const qint16 flags = qFromBigEndian<qint16>(tree + offset);
1012     offset += 2;
1013 
1014     offset += 4; //jump past locale
1015 
1016     if(!(flags & Directory)) {
1017         const qint32 data_offset = qFromBigEndian<qint32>(tree + offset);
1018         const quint32 data_length = qFromBigEndian<quint32>(payloads + data_offset);
1019         const uchar *ret = payloads+data_offset+4;
1020         *size = data_length;
1021         return ret;
1022     }
1023     *size = 0;
1024     return nullptr;
1025 }
1026 
lastModified(int node) const1027 quint64 QResourceRoot::lastModified(int node) const
1028 {
1029     if (node == -1 || version < 0x02)
1030         return 0;
1031 
1032     const int offset = findOffset(node) + 14;
1033 
1034     return qFromBigEndian<quint64>(tree + offset);
1035 }
1036 
children(int node) const1037 QStringList QResourceRoot::children(int node) const
1038 {
1039     if(node == -1)
1040         return QStringList();
1041     int offset = findOffset(node) + 4; //jump past name
1042 
1043     const qint16 flags = qFromBigEndian<qint16>(tree + offset);
1044     offset += 2;
1045 
1046     QStringList ret;
1047     if(flags & Directory) {
1048         const qint32 child_count = qFromBigEndian<qint32>(tree + offset);
1049         offset += 4;
1050         const qint32 child_off = qFromBigEndian<qint32>(tree + offset);
1051         ret.reserve(child_count);
1052         for(int i = child_off; i < child_off+child_count; ++i)
1053             ret << name(i);
1054     }
1055     return ret;
1056 }
mappingRootSubdir(const QString & path,QString * match) const1057 bool QResourceRoot::mappingRootSubdir(const QString &path, QString *match) const
1058 {
1059     const QString root = mappingRoot();
1060     if (root.isEmpty())
1061         return false;
1062 
1063     QStringSplitter rootIt(root);
1064     QStringSplitter pathIt(path);
1065     while (rootIt.hasNext()) {
1066         if (pathIt.hasNext()) {
1067             if (rootIt.next() != pathIt.next()) // mismatch
1068                 return false;
1069         } else {
1070             // end of path, but not of root:
1071             if (match)
1072                 *match = rootIt.next().toString();
1073             return true;
1074         }
1075     }
1076     // end of root
1077     return !pathIt.hasNext();
1078 }
1079 
qRegisterResourceData(int version,const unsigned char * tree,const unsigned char * name,const unsigned char * data)1080 Q_CORE_EXPORT bool qRegisterResourceData(int version, const unsigned char *tree,
1081                                          const unsigned char *name, const unsigned char *data)
1082 {
1083     if (resourceGlobalData.isDestroyed())
1084         return false;
1085     const auto locker = qt_scoped_lock(resourceMutex());
1086     ResourceList *list = resourceList();
1087     if (version >= 0x01 && version <= 0x3) {
1088         bool found = false;
1089         QResourceRoot res(version, tree, name, data);
1090         for (int i = 0; i < list->size(); ++i) {
1091             if (*list->at(i) == res) {
1092                 found = true;
1093                 break;
1094             }
1095         }
1096         if(!found) {
1097             QResourceRoot *root = new QResourceRoot(version, tree, name, data);
1098             root->ref.ref();
1099             list->append(root);
1100         }
1101         return true;
1102     }
1103     return false;
1104 }
1105 
qUnregisterResourceData(int version,const unsigned char * tree,const unsigned char * name,const unsigned char * data)1106 Q_CORE_EXPORT bool qUnregisterResourceData(int version, const unsigned char *tree,
1107                                            const unsigned char *name, const unsigned char *data)
1108 {
1109     if (resourceGlobalData.isDestroyed())
1110         return false;
1111 
1112     const auto locker = qt_scoped_lock(resourceMutex());
1113     if (version >= 0x01 && version <= 0x3) {
1114         QResourceRoot res(version, tree, name, data);
1115         ResourceList *list = resourceList();
1116         for (int i = 0; i < list->size(); ) {
1117             if (*list->at(i) == res) {
1118                 QResourceRoot *root = list->takeAt(i);
1119                 if(!root->ref.deref())
1120                     delete root;
1121             } else {
1122                 ++i;
1123             }
1124         }
1125         return true;
1126     }
1127     return false;
1128 }
1129 
1130 //run time resource creation
1131 
1132 class QDynamicBufferResourceRoot: public QResourceRoot
1133 {
1134     QString root;
1135     const uchar *buffer;
1136 
1137 public:
QDynamicBufferResourceRoot(const QString & _root)1138     inline QDynamicBufferResourceRoot(const QString &_root) : root(_root), buffer(nullptr) { }
~QDynamicBufferResourceRoot()1139     inline ~QDynamicBufferResourceRoot() { }
mappingBuffer() const1140     inline const uchar *mappingBuffer() const { return buffer; }
mappingRoot() const1141     QString mappingRoot() const override { return root; }
type() const1142     ResourceRootType type() const override { return Resource_Buffer; }
1143 
1144     // size == -1 means "unknown"
registerSelf(const uchar * b,qsizetype size)1145     bool registerSelf(const uchar *b, qsizetype size)
1146     {
1147         // 5 int "pointers"
1148         if (size >= 0 && size < 20)
1149             return false;
1150 
1151         //setup the data now
1152         int offset = 0;
1153 
1154         //magic number
1155         if(b[offset+0] != 'q' || b[offset+1] != 'r' ||
1156            b[offset+2] != 'e' || b[offset+3] != 's') {
1157             return false;
1158         }
1159         offset += 4;
1160 
1161         const int version = qFromBigEndian<qint32>(b + offset);
1162         offset += 4;
1163 
1164         const int tree_offset = qFromBigEndian<qint32>(b + offset);
1165         offset += 4;
1166 
1167         const int data_offset = qFromBigEndian<qint32>(b + offset);
1168         offset += 4;
1169 
1170         const int name_offset = qFromBigEndian<qint32>(b + offset);
1171         offset += 4;
1172 
1173         quint32 file_flags = 0;
1174         if (version >= 3) {
1175             file_flags = qFromBigEndian<qint32>(b + offset);
1176             offset += 4;
1177         }
1178 
1179         // Some sanity checking for sizes. This is _not_ a security measure.
1180         if (size >= 0 && (tree_offset >= size || data_offset >= size || name_offset >= size))
1181             return false;
1182 
1183         // And some sanity checking for features
1184         quint32 acceptableFlags = 0;
1185 #ifndef QT_NO_COMPRESS
1186         acceptableFlags |= Compressed;
1187 #endif
1188         if (QT_CONFIG(zstd))
1189             acceptableFlags |= CompressedZstd;
1190         if (file_flags & ~acceptableFlags)
1191             return false;
1192 
1193         if (version >= 0x01 && version <= 0x03) {
1194             buffer = b;
1195             setSource(version, b+tree_offset, b+name_offset, b+data_offset);
1196             return true;
1197         }
1198         return false;
1199     }
1200 };
1201 
1202 class QDynamicFileResourceRoot: public QDynamicBufferResourceRoot
1203 {
1204     QString fileName;
1205     // for mmap'ed files, this is what needs to be unmapped.
1206     uchar *unmapPointer;
1207     qsizetype unmapLength;
1208 
1209 public:
QDynamicFileResourceRoot(const QString & _root)1210     QDynamicFileResourceRoot(const QString &_root)
1211         : QDynamicBufferResourceRoot(_root), unmapPointer(nullptr), unmapLength(0)
1212     { }
~QDynamicFileResourceRoot()1213     ~QDynamicFileResourceRoot() {
1214 #if defined(QT_USE_MMAP)
1215         if (unmapPointer) {
1216             munmap((char*)unmapPointer, unmapLength);
1217             unmapPointer = nullptr;
1218             unmapLength = 0;
1219         } else
1220 #endif
1221         {
1222             delete [] mappingBuffer();
1223         }
1224     }
mappingFile() const1225     QString mappingFile() const { return fileName; }
type() const1226     ResourceRootType type() const override { return Resource_File; }
1227 
1228     bool registerSelf(const QString &f);
1229 };
1230 
1231 #ifndef MAP_FILE
1232 #  define MAP_FILE 0
1233 #endif
1234 #ifndef MAP_FAILED
1235 #  define MAP_FAILED reinterpret_cast<void *>(-1)
1236 #endif
1237 
registerSelf(const QString & f)1238 bool QDynamicFileResourceRoot::registerSelf(const QString &f)
1239 {
1240     bool fromMM = false;
1241     uchar *data = nullptr;
1242     qsizetype data_len = 0;
1243 
1244 #ifdef QT_USE_MMAP
1245     int fd = QT_OPEN(QFile::encodeName(f), O_RDONLY,
1246 #if defined(Q_OS_WIN)
1247                      _S_IREAD | _S_IWRITE
1248 #else
1249                      0666
1250 #endif
1251                      );
1252     if (fd >= 0) {
1253         QT_STATBUF st;
1254         if (!QT_FSTAT(fd, &st) && st.st_size <= std::numeric_limits<qsizetype>::max()) {
1255             int protection = PROT_READ;                 // read-only memory
1256             int flags = MAP_FILE | MAP_PRIVATE;         // swap-backed map from file
1257             void *ptr = QT_MMAP(nullptr, st.st_size,    // any address, whole file
1258                                 protection, flags,
1259                                 fd, 0);                 // from offset 0 of fd
1260             if (ptr != MAP_FAILED) {
1261                 data = static_cast<uchar *>(ptr);
1262                 data_len = st.st_size;
1263                 fromMM = true;
1264             }
1265         }
1266         QT_CLOSE(fd);
1267     }
1268 #endif // QT_USE_MMAP
1269     if (!data) {
1270         QFile file(f);
1271         bool ok = false;
1272         if (file.open(QIODevice::ReadOnly)) {
1273             qint64 fsize = file.size();
1274             if (fsize <= std::numeric_limits<qsizetype>::max()) {
1275                 data_len = file.size();
1276                 data = new uchar[data_len];
1277                 ok = (data_len == file.read((char*)data, data_len));
1278             }
1279         }
1280         if (!ok) {
1281             delete [] data;
1282             data = nullptr;
1283             data_len = 0;
1284             return false;
1285         }
1286         fromMM = false;
1287     }
1288     if (data && QDynamicBufferResourceRoot::registerSelf(data, data_len)) {
1289         if (fromMM) {
1290             unmapPointer = data;
1291             unmapLength = data_len;
1292         }
1293         fileName = f;
1294         return true;
1295     }
1296     return false;
1297 }
1298 
qt_resource_fixResourceRoot(QString r)1299 static QString qt_resource_fixResourceRoot(QString r) {
1300     if(!r.isEmpty()) {
1301         if(r.startsWith(QLatin1Char(':')))
1302             r = r.mid(1);
1303         if(!r.isEmpty())
1304             r = QDir::cleanPath(r);
1305     }
1306     return r;
1307 }
1308 
1309 
1310 /*!
1311    \fn bool QResource::registerResource(const QString &rccFileName, const QString &mapRoot)
1312 
1313    Registers the resource with the given \a rccFileName at the location in the
1314    resource tree specified by \a mapRoot, and returns \c true if the file is
1315    successfully opened; otherwise returns \c false.
1316 
1317    \sa unregisterResource()
1318 */
1319 
1320 bool
registerResource(const QString & rccFilename,const QString & resourceRoot)1321 QResource::registerResource(const QString &rccFilename, const QString &resourceRoot)
1322 {
1323     QString r = qt_resource_fixResourceRoot(resourceRoot);
1324     if(!r.isEmpty() && r[0] != QLatin1Char('/')) {
1325         qWarning("QDir::registerResource: Registering a resource [%s] must be rooted in an absolute path (start with /) [%s]",
1326                  rccFilename.toLocal8Bit().data(), resourceRoot.toLocal8Bit().data());
1327         return false;
1328     }
1329 
1330     QDynamicFileResourceRoot *root = new QDynamicFileResourceRoot(r);
1331     if(root->registerSelf(rccFilename)) {
1332         root->ref.ref();
1333         const auto locker = qt_scoped_lock(resourceMutex());
1334         resourceList()->append(root);
1335         return true;
1336     }
1337     delete root;
1338     return false;
1339 }
1340 
1341 /*!
1342   \fn bool QResource::unregisterResource(const QString &rccFileName, const QString &mapRoot)
1343 
1344   Unregisters the resource with the given \a rccFileName at the location in
1345   the resource tree specified by \a mapRoot, and returns \c true if the
1346   resource is successfully unloaded and no references exist for the
1347   resource; otherwise returns \c false.
1348 
1349   \sa registerResource()
1350 */
1351 
1352 bool
unregisterResource(const QString & rccFilename,const QString & resourceRoot)1353 QResource::unregisterResource(const QString &rccFilename, const QString &resourceRoot)
1354 {
1355     QString r = qt_resource_fixResourceRoot(resourceRoot);
1356 
1357     const auto locker = qt_scoped_lock(resourceMutex());
1358     ResourceList *list = resourceList();
1359     for(int i = 0; i < list->size(); ++i) {
1360         QResourceRoot *res = list->at(i);
1361         if(res->type() == QResourceRoot::Resource_File) {
1362             QDynamicFileResourceRoot *root = reinterpret_cast<QDynamicFileResourceRoot*>(res);
1363             if (root->mappingFile() == rccFilename && root->mappingRoot() == r) {
1364                 list->removeAt(i);
1365                 if(!root->ref.deref()) {
1366                     delete root;
1367                     return true;
1368                 }
1369                 return false;
1370             }
1371         }
1372     }
1373     return false;
1374 }
1375 
1376 
1377 /*!
1378    \fn bool QResource::registerResource(const uchar *rccData, const QString &mapRoot)
1379    \since 4.3
1380 
1381    Registers the resource with the given \a rccData at the location in the
1382    resource tree specified by \a mapRoot, and returns \c true if the file is
1383    successfully opened; otherwise returns \c false.
1384 
1385    \warning The data must remain valid throughout the life of any QFile
1386    that may reference the resource data.
1387 
1388    \sa unregisterResource()
1389 */
1390 
1391 bool
registerResource(const uchar * rccData,const QString & resourceRoot)1392 QResource::registerResource(const uchar *rccData, const QString &resourceRoot)
1393 {
1394     QString r = qt_resource_fixResourceRoot(resourceRoot);
1395     if(!r.isEmpty() && r[0] != QLatin1Char('/')) {
1396         qWarning("QDir::registerResource: Registering a resource [%p] must be rooted in an absolute path (start with /) [%s]",
1397                  rccData, resourceRoot.toLocal8Bit().data());
1398         return false;
1399     }
1400 
1401     QDynamicBufferResourceRoot *root = new QDynamicBufferResourceRoot(r);
1402     if (root->registerSelf(rccData, -1)) {
1403         root->ref.ref();
1404         const auto locker = qt_scoped_lock(resourceMutex());
1405         resourceList()->append(root);
1406         return true;
1407     }
1408     delete root;
1409     return false;
1410 }
1411 
1412 /*!
1413   \fn bool QResource::unregisterResource(const uchar *rccData, const QString &mapRoot)
1414   \since 4.3
1415 
1416   Unregisters the resource with the given \a rccData at the location in the
1417   resource tree specified by \a mapRoot, and returns \c true if the resource is
1418   successfully unloaded and no references exist into the resource; otherwise returns \c false.
1419 
1420   \sa registerResource()
1421 */
1422 
1423 bool
unregisterResource(const uchar * rccData,const QString & resourceRoot)1424 QResource::unregisterResource(const uchar *rccData, const QString &resourceRoot)
1425 {
1426     QString r = qt_resource_fixResourceRoot(resourceRoot);
1427 
1428     const auto locker = qt_scoped_lock(resourceMutex());
1429     ResourceList *list = resourceList();
1430     for(int i = 0; i < list->size(); ++i) {
1431         QResourceRoot *res = list->at(i);
1432         if(res->type() == QResourceRoot::Resource_Buffer) {
1433             QDynamicBufferResourceRoot *root = reinterpret_cast<QDynamicBufferResourceRoot*>(res);
1434             if (root->mappingBuffer() == rccData && root->mappingRoot() == r) {
1435                 list->removeAt(i);
1436                 if(!root->ref.deref()) {
1437                     delete root;
1438                     return true;
1439                 }
1440                 return false;
1441             }
1442         }
1443     }
1444     return false;
1445 }
1446 
1447 #if !defined(QT_BOOTSTRAPPED)
1448 //resource engine
1449 class QResourceFileEnginePrivate : public QAbstractFileEnginePrivate
1450 {
1451 protected:
1452     Q_DECLARE_PUBLIC(QResourceFileEngine)
1453 private:
1454     uchar *map(qint64 offset, qint64 size, QFile::MemoryMapFlags flags);
1455     bool unmap(uchar *ptr);
1456     void uncompress() const;
1457     qint64 offset;
1458     QResource resource;
1459     mutable QByteArray uncompressed;
1460 protected:
QResourceFileEnginePrivate()1461     QResourceFileEnginePrivate() : offset(0) { }
1462 };
1463 
mkdir(const QString &,bool) const1464 bool QResourceFileEngine::mkdir(const QString &, bool) const
1465 {
1466     return false;
1467 }
1468 
rmdir(const QString &,bool) const1469 bool QResourceFileEngine::rmdir(const QString &, bool) const
1470 {
1471     return false;
1472 }
1473 
setSize(qint64)1474 bool QResourceFileEngine::setSize(qint64)
1475 {
1476     return false;
1477 }
1478 
entryList(QDir::Filters filters,const QStringList & filterNames) const1479 QStringList QResourceFileEngine::entryList(QDir::Filters filters, const QStringList &filterNames) const
1480 {
1481     return QAbstractFileEngine::entryList(filters, filterNames);
1482 }
1483 
caseSensitive() const1484 bool QResourceFileEngine::caseSensitive() const
1485 {
1486     return true;
1487 }
1488 
QResourceFileEngine(const QString & file)1489 QResourceFileEngine::QResourceFileEngine(const QString &file) :
1490     QAbstractFileEngine(*new QResourceFileEnginePrivate)
1491 {
1492     Q_D(QResourceFileEngine);
1493     d->resource.setFileName(file);
1494 }
1495 
~QResourceFileEngine()1496 QResourceFileEngine::~QResourceFileEngine()
1497 {
1498 }
1499 
setFileName(const QString & file)1500 void QResourceFileEngine::setFileName(const QString &file)
1501 {
1502     Q_D(QResourceFileEngine);
1503     d->resource.setFileName(file);
1504 }
1505 
open(QIODevice::OpenMode flags)1506 bool QResourceFileEngine::open(QIODevice::OpenMode flags)
1507 {
1508     Q_D(QResourceFileEngine);
1509     if (d->resource.fileName().isEmpty()) {
1510         qWarning("QResourceFileEngine::open: Missing file name");
1511         return false;
1512     }
1513     if (flags & QIODevice::WriteOnly)
1514         return false;
1515     if (d->resource.compressionAlgorithm() != QResource::NoCompression) {
1516         d->uncompress();
1517         if (d->uncompressed.isNull()) {
1518             d->errorString = QSystemError::stdString(EIO);
1519             return false;
1520         }
1521     }
1522     if (!d->resource.isValid()) {
1523         d->errorString = QSystemError::stdString(ENOENT);
1524         return false;
1525     }
1526     return true;
1527 }
1528 
close()1529 bool QResourceFileEngine::close()
1530 {
1531     Q_D(QResourceFileEngine);
1532     d->offset = 0;
1533     return true;
1534 }
1535 
flush()1536 bool QResourceFileEngine::flush()
1537 {
1538     return true;
1539 }
1540 
read(char * data,qint64 len)1541 qint64 QResourceFileEngine::read(char *data, qint64 len)
1542 {
1543     Q_D(QResourceFileEngine);
1544     if(len > size()-d->offset)
1545         len = size()-d->offset;
1546     if(len <= 0)
1547         return 0;
1548     if (!d->uncompressed.isNull())
1549         memcpy(data, d->uncompressed.constData()+d->offset, len);
1550     else
1551         memcpy(data, d->resource.data()+d->offset, len);
1552     d->offset += len;
1553     return len;
1554 }
1555 
write(const char *,qint64)1556 qint64 QResourceFileEngine::write(const char *, qint64)
1557 {
1558     return -1;
1559 }
1560 
remove()1561 bool QResourceFileEngine::remove()
1562 {
1563     return false;
1564 }
1565 
copy(const QString &)1566 bool QResourceFileEngine::copy(const QString &)
1567 {
1568     return false;
1569 }
1570 
rename(const QString &)1571 bool QResourceFileEngine::rename(const QString &)
1572 {
1573     return false;
1574 }
1575 
link(const QString &)1576 bool QResourceFileEngine::link(const QString &)
1577 {
1578     return false;
1579 }
1580 
size() const1581 qint64 QResourceFileEngine::size() const
1582 {
1583     Q_D(const QResourceFileEngine);
1584     return d->resource.isValid() ? d->resource.uncompressedSize() : 0;
1585 }
1586 
pos() const1587 qint64 QResourceFileEngine::pos() const
1588 {
1589     Q_D(const QResourceFileEngine);
1590     return d->offset;
1591 }
1592 
atEnd() const1593 bool QResourceFileEngine::atEnd() const
1594 {
1595     Q_D(const QResourceFileEngine);
1596     if(!d->resource.isValid())
1597         return true;
1598     return d->offset == size();
1599 }
1600 
seek(qint64 pos)1601 bool QResourceFileEngine::seek(qint64 pos)
1602 {
1603     Q_D(QResourceFileEngine);
1604     if(!d->resource.isValid())
1605         return false;
1606 
1607     if(d->offset > size())
1608         return false;
1609     d->offset = pos;
1610     return true;
1611 }
1612 
isSequential() const1613 bool QResourceFileEngine::isSequential() const
1614 {
1615     return false;
1616 }
1617 
fileFlags(QAbstractFileEngine::FileFlags type) const1618 QAbstractFileEngine::FileFlags QResourceFileEngine::fileFlags(QAbstractFileEngine::FileFlags type) const
1619 {
1620     Q_D(const QResourceFileEngine);
1621     QAbstractFileEngine::FileFlags ret;
1622     if(!d->resource.isValid())
1623         return ret;
1624 
1625     if(type & PermsMask)
1626         ret |= QAbstractFileEngine::FileFlags(ReadOwnerPerm|ReadUserPerm|ReadGroupPerm|ReadOtherPerm);
1627     if(type & TypesMask) {
1628         if(d->resource.isDir())
1629             ret |= DirectoryType;
1630         else
1631             ret |= FileType;
1632     }
1633     if(type & FlagsMask) {
1634         ret |= ExistsFlag;
1635         if(d->resource.absoluteFilePath() == QLatin1String(":/"))
1636             ret |= RootFlag;
1637     }
1638     return ret;
1639 }
1640 
setPermissions(uint)1641 bool QResourceFileEngine::setPermissions(uint)
1642 {
1643     return false;
1644 }
1645 
fileName(FileName file) const1646 QString QResourceFileEngine::fileName(FileName file) const
1647 {
1648     Q_D(const QResourceFileEngine);
1649     if(file == BaseName) {
1650         int slash = d->resource.fileName().lastIndexOf(QLatin1Char('/'));
1651         if (slash == -1)
1652             return d->resource.fileName();
1653         return d->resource.fileName().mid(slash + 1);
1654     } else if(file == PathName || file == AbsolutePathName) {
1655         const QString path = (file == AbsolutePathName) ? d->resource.absoluteFilePath() : d->resource.fileName();
1656         const int slash = path.lastIndexOf(QLatin1Char('/'));
1657         if (slash == -1)
1658             return QLatin1String(":");
1659         else if (slash <= 1)
1660             return QLatin1String(":/");
1661         return path.left(slash);
1662 
1663     } else if(file == CanonicalName || file == CanonicalPathName) {
1664         const QString absoluteFilePath = d->resource.absoluteFilePath();
1665         if(file == CanonicalPathName) {
1666             const int slash = absoluteFilePath.lastIndexOf(QLatin1Char('/'));
1667             if (slash != -1)
1668                 return absoluteFilePath.left(slash);
1669         }
1670         return absoluteFilePath;
1671     }
1672     return d->resource.fileName();
1673 }
1674 
isRelativePath() const1675 bool QResourceFileEngine::isRelativePath() const
1676 {
1677     return false;
1678 }
1679 
ownerId(FileOwner) const1680 uint QResourceFileEngine::ownerId(FileOwner) const
1681 {
1682     static const uint nobodyID = (uint) -2;
1683     return nobodyID;
1684 }
1685 
owner(FileOwner) const1686 QString QResourceFileEngine::owner(FileOwner) const
1687 {
1688     return QString();
1689 }
1690 
fileTime(FileTime time) const1691 QDateTime QResourceFileEngine::fileTime(FileTime time) const
1692 {
1693     Q_D(const QResourceFileEngine);
1694     if (time == ModificationTime)
1695         return d->resource.lastModified();
1696     return QDateTime();
1697 }
1698 
1699 /*!
1700     \internal
1701 */
beginEntryList(QDir::Filters filters,const QStringList & filterNames)1702 QAbstractFileEngine::Iterator *QResourceFileEngine::beginEntryList(QDir::Filters filters,
1703                                                                    const QStringList &filterNames)
1704 {
1705     return new QResourceFileEngineIterator(filters, filterNames);
1706 }
1707 
1708 /*!
1709     \internal
1710 */
endEntryList()1711 QAbstractFileEngine::Iterator *QResourceFileEngine::endEntryList()
1712 {
1713     return nullptr;
1714 }
1715 
extension(Extension extension,const ExtensionOption * option,ExtensionReturn * output)1716 bool QResourceFileEngine::extension(Extension extension, const ExtensionOption *option, ExtensionReturn *output)
1717 {
1718     Q_D(QResourceFileEngine);
1719     if (extension == MapExtension) {
1720         const MapExtensionOption *options = (const MapExtensionOption*)(option);
1721         MapExtensionReturn *returnValue = static_cast<MapExtensionReturn*>(output);
1722         returnValue->address = d->map(options->offset, options->size, options->flags);
1723         return (returnValue->address != nullptr);
1724     }
1725     if (extension == UnMapExtension) {
1726         const UnMapExtensionOption *options = (const UnMapExtensionOption*)option;
1727         return d->unmap(options->address);
1728     }
1729     return false;
1730 }
1731 
supportsExtension(Extension extension) const1732 bool QResourceFileEngine::supportsExtension(Extension extension) const
1733 {
1734     return (extension == UnMapExtension || extension == MapExtension);
1735 }
1736 
map(qint64 offset,qint64 size,QFile::MemoryMapFlags flags)1737 uchar *QResourceFileEnginePrivate::map(qint64 offset, qint64 size, QFile::MemoryMapFlags flags)
1738 {
1739     Q_Q(QResourceFileEngine);
1740     Q_UNUSED(flags);
1741 
1742     qint64 max = resource.uncompressedSize();
1743     qint64 end;
1744     if (offset < 0 || size <= 0 || !resource.isValid() ||
1745             add_overflow(offset, size, &end) || end > max) {
1746         q->setError(QFile::UnspecifiedError, QString());
1747         return nullptr;
1748     }
1749 
1750     const uchar *address = resource.data();
1751     if (resource.compressionAlgorithm() != QResource::NoCompression) {
1752         uncompress();
1753         if (uncompressed.isNull())
1754             return nullptr;
1755         address = reinterpret_cast<const uchar *>(uncompressed.constData());
1756     }
1757 
1758     return const_cast<uchar *>(address) + offset;
1759 }
1760 
unmap(uchar * ptr)1761 bool QResourceFileEnginePrivate::unmap(uchar *ptr)
1762 {
1763     Q_UNUSED(ptr);
1764     return true;
1765 }
1766 
uncompress() const1767 void QResourceFileEnginePrivate::uncompress() const
1768 {
1769     if (resource.compressionAlgorithm() == QResource::NoCompression
1770             || !uncompressed.isEmpty() || resource.size() == 0)
1771         return;     // nothing to do
1772     uncompressed = resource.uncompressedData();
1773 }
1774 
1775 #endif // !defined(QT_BOOTSTRAPPED)
1776 
1777 QT_END_NAMESPACE
1778