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 "qplatformdefs.h" |
41 | #include "private/qabstractfileengine_p.h" |
42 | #include "private/qfsfileengine_p.h" |
43 | #include "private/qcore_unix_p.h" |
44 | #include "qfilesystementry_p.h" |
45 | #include "qfilesystemengine_p.h" |
46 | #include "qcoreapplication.h" |
47 | |
48 | #ifndef QT_NO_FSFILEENGINE |
49 | |
50 | #include "qfile.h" |
51 | #include "qdir.h" |
52 | #include "qdatetime.h" |
53 | #include "qvarlengtharray.h" |
54 | |
55 | #include <sys/mman.h> |
56 | #include <stdlib.h> |
57 | #include <limits.h> |
58 | #include <errno.h> |
59 | #if !defined(QWS) && defined(Q_OS_MAC) |
60 | # include <private/qcore_mac_p.h> |
61 | #endif |
62 | |
63 | QT_BEGIN_NAMESPACE |
64 | |
65 | /*! |
66 | \internal |
67 | |
68 | Returns the stdio open flags corresponding to a QIODevice::OpenMode. |
69 | */ |
70 | static inline int openModeToOpenFlags(QIODevice::OpenMode mode) |
71 | { |
72 | int oflags = QT_OPEN_RDONLY; |
73 | #ifdef QT_LARGEFILE_SUPPORT |
74 | oflags |= QT_OPEN_LARGEFILE; |
75 | #endif |
76 | |
77 | if ((mode & QFile::ReadWrite) == QFile::ReadWrite) |
78 | oflags = QT_OPEN_RDWR; |
79 | else if (mode & QFile::WriteOnly) |
80 | oflags = QT_OPEN_WRONLY; |
81 | |
82 | if (QFSFileEnginePrivate::openModeCanCreate(openMode: mode)) |
83 | oflags |= QT_OPEN_CREAT; |
84 | |
85 | if (mode & QFile::Truncate) |
86 | oflags |= QT_OPEN_TRUNC; |
87 | |
88 | if (mode & QFile::Append) |
89 | oflags |= QT_OPEN_APPEND; |
90 | |
91 | if (mode & QFile::NewOnly) |
92 | oflags |= QT_OPEN_EXCL; |
93 | |
94 | return oflags; |
95 | } |
96 | |
97 | static inline QString msgOpenDirectory() |
98 | { |
99 | const char message[] = QT_TRANSLATE_NOOP("QIODevice" , "file to open is a directory" ); |
100 | #if QT_CONFIG(translation) |
101 | return QIODevice::tr(s: message); |
102 | #else |
103 | return QLatin1String(message); |
104 | #endif |
105 | } |
106 | |
107 | /*! |
108 | \internal |
109 | */ |
110 | bool QFSFileEnginePrivate::nativeOpen(QIODevice::OpenMode openMode) |
111 | { |
112 | Q_Q(QFSFileEngine); |
113 | |
114 | Q_ASSERT_X(openMode & QIODevice::Unbuffered, "QFSFileEngine::open" , |
115 | "QFSFileEngine no longer supports buffered mode; upper layer must buffer" ); |
116 | if (openMode & QIODevice::Unbuffered) { |
117 | int flags = openModeToOpenFlags(mode: openMode); |
118 | |
119 | // Try to open the file in unbuffered mode. |
120 | do { |
121 | fd = QT_OPEN(pathname: fileEntry.nativeFilePath().constData(), flags, mode: 0666); |
122 | } while (fd == -1 && errno == EINTR); |
123 | |
124 | // On failure, return and report the error. |
125 | if (fd == -1) { |
126 | q->setError(errno == EMFILE ? QFile::ResourceError : QFile::OpenError, |
127 | str: qt_error_string(errno)); |
128 | return false; |
129 | } |
130 | |
131 | if (!(openMode & QIODevice::WriteOnly)) { |
132 | // we don't need this check if we tried to open for writing because then |
133 | // we had received EISDIR anyway. |
134 | if (QFileSystemEngine::fillMetaData(fd, data&: metaData) |
135 | && metaData.isDirectory()) { |
136 | q->setError(error: QFile::OpenError, str: msgOpenDirectory()); |
137 | QT_CLOSE(fd); |
138 | return false; |
139 | } |
140 | } |
141 | |
142 | // Seek to the end when in Append mode. |
143 | if (flags & QFile::Append) { |
144 | int ret; |
145 | do { |
146 | ret = QT_LSEEK(fd: fd, offset: 0, SEEK_END); |
147 | } while (ret == -1 && errno == EINTR); |
148 | |
149 | if (ret == -1) { |
150 | q->setError(errno == EMFILE ? QFile::ResourceError : QFile::OpenError, |
151 | str: qt_error_string(errorCode: int(errno))); |
152 | return false; |
153 | } |
154 | } |
155 | |
156 | fh = nullptr; |
157 | } |
158 | |
159 | closeFileHandle = true; |
160 | return true; |
161 | } |
162 | |
163 | /*! |
164 | \internal |
165 | */ |
166 | bool QFSFileEnginePrivate::nativeClose() |
167 | { |
168 | return closeFdFh(); |
169 | } |
170 | |
171 | /*! |
172 | \internal |
173 | |
174 | */ |
175 | bool QFSFileEnginePrivate::nativeFlush() |
176 | { |
177 | return fh ? flushFh() : fd != -1; |
178 | } |
179 | |
180 | /*! |
181 | \internal |
182 | \since 5.1 |
183 | */ |
184 | bool QFSFileEnginePrivate::nativeSyncToDisk() |
185 | { |
186 | Q_Q(QFSFileEngine); |
187 | int ret; |
188 | #if defined(_POSIX_SYNCHRONIZED_IO) && _POSIX_SYNCHRONIZED_IO > 0 |
189 | EINTR_LOOP(ret, fdatasync(nativeHandle())); |
190 | #else |
191 | EINTR_LOOP(ret, fsync(nativeHandle())); |
192 | #endif |
193 | if (ret != 0) |
194 | q->setError(error: QFile::WriteError, str: qt_error_string(errno)); |
195 | return ret == 0; |
196 | } |
197 | |
198 | /*! |
199 | \internal |
200 | */ |
201 | qint64 QFSFileEnginePrivate::nativeRead(char *data, qint64 len) |
202 | { |
203 | Q_Q(QFSFileEngine); |
204 | |
205 | if (fh && nativeIsSequential()) { |
206 | size_t readBytes = 0; |
207 | int oldFlags = fcntl(QT_FILENO(stream: fh), F_GETFL); |
208 | for (int i = 0; i < 2; ++i) { |
209 | // Unix: Make the underlying file descriptor non-blocking |
210 | if ((oldFlags & O_NONBLOCK) == 0) |
211 | fcntl(QT_FILENO(stream: fh), F_SETFL, oldFlags | O_NONBLOCK); |
212 | |
213 | // Cross platform stdlib read |
214 | size_t read = 0; |
215 | do { |
216 | read = fread(ptr: data + readBytes, size: 1, n: size_t(len - readBytes), stream: fh); |
217 | } while (read == 0 && !feof(stream: fh) && errno == EINTR); |
218 | if (read > 0) { |
219 | readBytes += read; |
220 | break; |
221 | } else { |
222 | if (readBytes) |
223 | break; |
224 | readBytes = read; |
225 | } |
226 | |
227 | // Unix: Restore the blocking state of the underlying socket |
228 | if ((oldFlags & O_NONBLOCK) == 0) { |
229 | fcntl(QT_FILENO(stream: fh), F_SETFL, oldFlags); |
230 | if (readBytes == 0) { |
231 | int readByte = 0; |
232 | do { |
233 | readByte = fgetc(stream: fh); |
234 | } while (readByte == -1 && errno == EINTR); |
235 | if (readByte != -1) { |
236 | *data = uchar(readByte); |
237 | readBytes += 1; |
238 | } else { |
239 | break; |
240 | } |
241 | } |
242 | } |
243 | } |
244 | // Unix: Restore the blocking state of the underlying socket |
245 | if ((oldFlags & O_NONBLOCK) == 0) { |
246 | fcntl(QT_FILENO(stream: fh), F_SETFL, oldFlags); |
247 | } |
248 | if (readBytes == 0 && !feof(stream: fh)) { |
249 | // if we didn't read anything and we're not at EOF, it must be an error |
250 | q->setError(error: QFile::ReadError, str: qt_error_string(errorCode: int(errno))); |
251 | return -1; |
252 | } |
253 | return readBytes; |
254 | } |
255 | |
256 | return readFdFh(data, maxlen: len); |
257 | } |
258 | |
259 | /*! |
260 | \internal |
261 | */ |
262 | qint64 QFSFileEnginePrivate::nativeReadLine(char *data, qint64 maxlen) |
263 | { |
264 | return readLineFdFh(data, maxlen); |
265 | } |
266 | |
267 | /*! |
268 | \internal |
269 | */ |
270 | qint64 QFSFileEnginePrivate::nativeWrite(const char *data, qint64 len) |
271 | { |
272 | return writeFdFh(data, len); |
273 | } |
274 | |
275 | /*! |
276 | \internal |
277 | */ |
278 | qint64 QFSFileEnginePrivate::nativePos() const |
279 | { |
280 | return posFdFh(); |
281 | } |
282 | |
283 | /*! |
284 | \internal |
285 | */ |
286 | bool QFSFileEnginePrivate::nativeSeek(qint64 pos) |
287 | { |
288 | return seekFdFh(pos); |
289 | } |
290 | |
291 | /*! |
292 | \internal |
293 | */ |
294 | int QFSFileEnginePrivate::nativeHandle() const |
295 | { |
296 | return fh ? fileno(stream: fh) : fd; |
297 | } |
298 | |
299 | /*! |
300 | \internal |
301 | */ |
302 | bool QFSFileEnginePrivate::nativeIsSequential() const |
303 | { |
304 | return isSequentialFdFh(); |
305 | } |
306 | |
307 | bool QFSFileEngine::link(const QString &newName) |
308 | { |
309 | Q_D(QFSFileEngine); |
310 | QSystemError error; |
311 | bool ret = QFileSystemEngine::createLink(source: d->fileEntry, target: QFileSystemEntry(newName), error); |
312 | if (!ret) { |
313 | setError(error: QFile::RenameError, str: error.toString()); |
314 | } |
315 | return ret; |
316 | } |
317 | |
318 | qint64 QFSFileEnginePrivate::nativeSize() const |
319 | { |
320 | return sizeFdFh(); |
321 | } |
322 | |
323 | bool QFSFileEngine::caseSensitive() const |
324 | { |
325 | return true; |
326 | } |
327 | |
328 | QString QFSFileEngine::currentPath(const QString &) |
329 | { |
330 | return QFileSystemEngine::currentPath().filePath(); |
331 | } |
332 | |
333 | |
334 | QFileInfoList QFSFileEngine::drives() |
335 | { |
336 | QFileInfoList ret; |
337 | ret.append(t: QFileInfo(rootPath())); |
338 | return ret; |
339 | } |
340 | |
341 | bool QFSFileEnginePrivate::doStat(QFileSystemMetaData::MetaDataFlags flags) const |
342 | { |
343 | if (!tried_stat || !metaData.hasFlags(flags)) { |
344 | tried_stat = 1; |
345 | |
346 | int localFd = fd; |
347 | if (fh && fileEntry.isEmpty()) |
348 | localFd = QT_FILENO(stream: fh); |
349 | if (localFd != -1) |
350 | QFileSystemEngine::fillMetaData(fd: localFd, data&: metaData); |
351 | |
352 | if (metaData.missingFlags(flags) && !fileEntry.isEmpty()) |
353 | QFileSystemEngine::fillMetaData(entry: fileEntry, data&: metaData, what: metaData.missingFlags(flags)); |
354 | } |
355 | |
356 | return metaData.exists(); |
357 | } |
358 | |
359 | bool QFSFileEnginePrivate::isSymlink() const |
360 | { |
361 | if (!metaData.hasFlags(flags: QFileSystemMetaData::LinkType)) |
362 | QFileSystemEngine::fillMetaData(entry: fileEntry, data&: metaData, what: QFileSystemMetaData::LinkType); |
363 | |
364 | return metaData.isLink(); |
365 | } |
366 | |
367 | /*! |
368 | \reimp |
369 | */ |
370 | QAbstractFileEngine::FileFlags QFSFileEngine::fileFlags(FileFlags type) const |
371 | { |
372 | Q_D(const QFSFileEngine); |
373 | |
374 | if (type & Refresh) |
375 | d->metaData.clear(); |
376 | |
377 | QAbstractFileEngine::FileFlags ret = { }; |
378 | |
379 | if (type & FlagsMask) |
380 | ret |= LocalDiskFlag; |
381 | |
382 | bool exists; |
383 | { |
384 | QFileSystemMetaData::MetaDataFlags queryFlags = { }; |
385 | |
386 | queryFlags |= QFileSystemMetaData::MetaDataFlags(uint(type)) |
387 | & QFileSystemMetaData::Permissions; |
388 | |
389 | if (type & TypesMask) |
390 | queryFlags |= QFileSystemMetaData::AliasType |
391 | | QFileSystemMetaData::LinkType |
392 | | QFileSystemMetaData::FileType |
393 | | QFileSystemMetaData::DirectoryType |
394 | | QFileSystemMetaData::BundleType |
395 | | QFileSystemMetaData::WasDeletedAttribute; |
396 | |
397 | if (type & FlagsMask) |
398 | queryFlags |= QFileSystemMetaData::HiddenAttribute |
399 | | QFileSystemMetaData::ExistsAttribute; |
400 | else if (type & ExistsFlag) |
401 | queryFlags |= QFileSystemMetaData::WasDeletedAttribute; |
402 | |
403 | queryFlags |= QFileSystemMetaData::LinkType; |
404 | |
405 | exists = d->doStat(flags: queryFlags); |
406 | } |
407 | |
408 | if (!exists && !d->metaData.isLink()) |
409 | return ret; |
410 | |
411 | if (exists && (type & PermsMask)) |
412 | ret |= FileFlags(uint(d->metaData.permissions())); |
413 | |
414 | if (type & TypesMask) { |
415 | if (d->metaData.isAlias()) { |
416 | ret |= LinkType; |
417 | } else { |
418 | if ((type & LinkType) && d->metaData.isLink()) |
419 | ret |= LinkType; |
420 | if (exists) { |
421 | if (d->metaData.isFile()) { |
422 | ret |= FileType; |
423 | } else if (d->metaData.isDirectory()) { |
424 | ret |= DirectoryType; |
425 | if ((type & BundleType) && d->metaData.isBundle()) |
426 | ret |= BundleType; |
427 | } |
428 | } |
429 | } |
430 | } |
431 | |
432 | if (type & FlagsMask) { |
433 | // the inode existing does not mean the file exists |
434 | if (!d->metaData.wasDeleted()) |
435 | ret |= ExistsFlag; |
436 | if (d->fileEntry.isRoot()) |
437 | ret |= RootFlag; |
438 | else if (d->metaData.isHidden()) |
439 | ret |= HiddenFlag; |
440 | } |
441 | |
442 | return ret; |
443 | } |
444 | |
445 | QByteArray QFSFileEngine::id() const |
446 | { |
447 | Q_D(const QFSFileEngine); |
448 | if (d->fd != -1) |
449 | return QFileSystemEngine::id(fd: d->fd); |
450 | return QFileSystemEngine::id(entry: d->fileEntry); |
451 | } |
452 | |
453 | QString QFSFileEngine::fileName(FileName file) const |
454 | { |
455 | Q_D(const QFSFileEngine); |
456 | switch (file) { |
457 | case BundleName: |
458 | return QFileSystemEngine::bundleName(entry: d->fileEntry); |
459 | case BaseName: |
460 | return d->fileEntry.fileName(); |
461 | case PathName: |
462 | return d->fileEntry.path(); |
463 | case AbsoluteName: |
464 | case AbsolutePathName: { |
465 | QFileSystemEntry entry(QFileSystemEngine::absoluteName(entry: d->fileEntry)); |
466 | return file == AbsolutePathName ? entry.path() : entry.filePath(); |
467 | } |
468 | case CanonicalName: |
469 | case CanonicalPathName: { |
470 | QFileSystemEntry entry(QFileSystemEngine::canonicalName(entry: d->fileEntry, data&: d->metaData)); |
471 | return file == CanonicalPathName ? entry.path() : entry.filePath(); |
472 | } |
473 | case LinkName: |
474 | if (d->isSymlink()) { |
475 | QFileSystemEntry entry = QFileSystemEngine::getLinkTarget(link: d->fileEntry, data&: d->metaData); |
476 | return entry.filePath(); |
477 | } |
478 | return QString(); |
479 | case DefaultName: |
480 | case NFileNames: |
481 | break; |
482 | } |
483 | return d->fileEntry.filePath(); |
484 | } |
485 | |
486 | bool QFSFileEngine::isRelativePath() const |
487 | { |
488 | Q_D(const QFSFileEngine); |
489 | return d->fileEntry.filePath().length() ? d->fileEntry.filePath().at(i: 0) != QLatin1Char('/') : true; |
490 | } |
491 | |
492 | uint QFSFileEngine::ownerId(FileOwner own) const |
493 | { |
494 | Q_D(const QFSFileEngine); |
495 | static const uint nobodyID = (uint) -2; |
496 | |
497 | if (d->doStat(flags: QFileSystemMetaData::OwnerIds)) |
498 | return d->metaData.ownerId(owner: own); |
499 | |
500 | return nobodyID; |
501 | } |
502 | |
503 | QString QFSFileEngine::owner(FileOwner own) const |
504 | { |
505 | if (own == OwnerUser) |
506 | return QFileSystemEngine::resolveUserName(userId: ownerId(own)); |
507 | return QFileSystemEngine::resolveGroupName(groupId: ownerId(own)); |
508 | } |
509 | |
510 | bool QFSFileEngine::setPermissions(uint perms) |
511 | { |
512 | Q_D(QFSFileEngine); |
513 | QSystemError error; |
514 | bool ok; |
515 | if (d->fd != -1) |
516 | ok = QFileSystemEngine::setPermissions(fd: d->fd, permissions: QFile::Permissions(perms), error); |
517 | else |
518 | ok = QFileSystemEngine::setPermissions(entry: d->fileEntry, permissions: QFile::Permissions(perms), error); |
519 | if (!ok) { |
520 | setError(error: QFile::PermissionsError, str: error.toString()); |
521 | return false; |
522 | } |
523 | return true; |
524 | } |
525 | |
526 | bool QFSFileEngine::setSize(qint64 size) |
527 | { |
528 | Q_D(QFSFileEngine); |
529 | bool ret = false; |
530 | if (d->fd != -1) |
531 | ret = QT_FTRUNCATE(fd: d->fd, length: size) == 0; |
532 | else if (d->fh) |
533 | ret = QT_FTRUNCATE(QT_FILENO(stream: d->fh), length: size) == 0; |
534 | else |
535 | ret = QT_TRUNCATE(file: d->fileEntry.nativeFilePath().constData(), length: size) == 0; |
536 | if (!ret) |
537 | setError(error: QFile::ResizeError, str: qt_error_string(errno)); |
538 | return ret; |
539 | } |
540 | |
541 | bool QFSFileEngine::setFileTime(const QDateTime &newDate, FileTime time) |
542 | { |
543 | Q_D(QFSFileEngine); |
544 | |
545 | if (d->openMode == QIODevice::NotOpen) { |
546 | setError(error: QFile::PermissionsError, str: qt_error_string(EACCES)); |
547 | return false; |
548 | } |
549 | |
550 | QSystemError error; |
551 | if (!QFileSystemEngine::setFileTime(fd: d->nativeHandle(), newDate, whatTime: time, error)) { |
552 | setError(error: QFile::PermissionsError, str: error.toString()); |
553 | return false; |
554 | } |
555 | |
556 | d->metaData.clearFlags(flags: QFileSystemMetaData::Times); |
557 | return true; |
558 | } |
559 | |
560 | uchar *QFSFileEnginePrivate::map(qint64 offset, qint64 size, QFile::MemoryMapFlags flags) |
561 | { |
562 | qint64 maxFileOffset = std::numeric_limits<QT_OFF_T>::max(); |
563 | #if (defined(Q_OS_LINUX) || defined(Q_OS_ANDROID)) && Q_PROCESSOR_WORDSIZE == 4 |
564 | // The Linux mmap2 system call on 32-bit takes a page-shifted 32-bit |
565 | // integer so the maximum offset is 1 << (32+12) (the shift is always 12, |
566 | // regardless of the actual page size). Unfortunately, the mmap64() |
567 | // function is known to be broken in all Linux libcs (glibc, uclibc, musl |
568 | // and Bionic): all of them do the right shift, but don't confirm that the |
569 | // result fits into the 32-bit parameter to the kernel. |
570 | |
571 | maxFileOffset = qMin((Q_INT64_C(1) << (32+12)) - 1, maxFileOffset); |
572 | #endif |
573 | |
574 | Q_Q(QFSFileEngine); |
575 | if (openMode == QIODevice::NotOpen) { |
576 | q->setError(error: QFile::PermissionsError, str: qt_error_string(errorCode: int(EACCES))); |
577 | return nullptr; |
578 | } |
579 | |
580 | if (offset < 0 || offset > maxFileOffset |
581 | || size < 0 || quint64(size) > quint64(size_t(-1))) { |
582 | q->setError(error: QFile::UnspecifiedError, str: qt_error_string(errorCode: int(EINVAL))); |
583 | return nullptr; |
584 | } |
585 | |
586 | // If we know the mapping will extend beyond EOF, fail early to avoid |
587 | // undefined behavior. Otherwise, let mmap have its say. |
588 | if (doStat(flags: QFileSystemMetaData::SizeAttribute) |
589 | && (QT_OFF_T(size) > metaData.size() - QT_OFF_T(offset))) |
590 | qWarning(msg: "QFSFileEngine::map: Mapping a file beyond its size is not portable" ); |
591 | |
592 | int access = 0; |
593 | if (openMode & QIODevice::ReadOnly) access |= PROT_READ; |
594 | if (openMode & QIODevice::WriteOnly) access |= PROT_WRITE; |
595 | |
596 | int sharemode = MAP_SHARED; |
597 | if (flags & QFileDevice::MapPrivateOption) { |
598 | sharemode = MAP_PRIVATE; |
599 | access |= PROT_WRITE; |
600 | } |
601 | |
602 | #if defined(Q_OS_INTEGRITY) |
603 | int pageSize = sysconf(_SC_PAGESIZE); |
604 | #else |
605 | int pageSize = getpagesize(); |
606 | #endif |
607 | int = offset % pageSize; |
608 | |
609 | if (quint64(size + extra) > quint64((size_t)-1)) { |
610 | q->setError(error: QFile::UnspecifiedError, str: qt_error_string(errorCode: int(EINVAL))); |
611 | return nullptr; |
612 | } |
613 | |
614 | size_t realSize = (size_t)size + extra; |
615 | QT_OFF_T realOffset = QT_OFF_T(offset); |
616 | realOffset &= ~(QT_OFF_T(pageSize - 1)); |
617 | |
618 | void *mapAddress = QT_MMAP(addr: (void*)nullptr, len: realSize, |
619 | prot: access, flags: sharemode, fd: nativeHandle(), offset: realOffset); |
620 | if (MAP_FAILED != mapAddress) { |
621 | uchar *address = extra + static_cast<uchar*>(mapAddress); |
622 | maps[address] = QPair<int,size_t>(extra, realSize); |
623 | return address; |
624 | } |
625 | |
626 | switch(errno) { |
627 | case EBADF: |
628 | q->setError(error: QFile::PermissionsError, str: qt_error_string(errorCode: int(EACCES))); |
629 | break; |
630 | case ENFILE: |
631 | case ENOMEM: |
632 | q->setError(error: QFile::ResourceError, str: qt_error_string(errorCode: int(errno))); |
633 | break; |
634 | case EINVAL: |
635 | // size are out of bounds |
636 | default: |
637 | q->setError(error: QFile::UnspecifiedError, str: qt_error_string(errorCode: int(errno))); |
638 | break; |
639 | } |
640 | return nullptr; |
641 | } |
642 | |
643 | bool QFSFileEnginePrivate::unmap(uchar *ptr) |
644 | { |
645 | #if !defined(Q_OS_INTEGRITY) |
646 | Q_Q(QFSFileEngine); |
647 | if (!maps.contains(akey: ptr)) { |
648 | q->setError(error: QFile::PermissionsError, str: qt_error_string(EACCES)); |
649 | return false; |
650 | } |
651 | |
652 | uchar *start = ptr - maps[ptr].first; |
653 | size_t len = maps[ptr].second; |
654 | if (-1 == munmap(addr: start, len: len)) { |
655 | q->setError(error: QFile::UnspecifiedError, str: qt_error_string(errno)); |
656 | return false; |
657 | } |
658 | maps.remove(akey: ptr); |
659 | return true; |
660 | #else |
661 | return false; |
662 | #endif |
663 | } |
664 | |
665 | /*! |
666 | \reimp |
667 | */ |
668 | bool QFSFileEngine::cloneTo(QAbstractFileEngine *target) |
669 | { |
670 | Q_D(QFSFileEngine); |
671 | if ((target->fileFlags(type: LocalDiskFlag) & LocalDiskFlag) == 0) |
672 | return false; |
673 | |
674 | int srcfd = d->nativeHandle(); |
675 | int dstfd = target->handle(); |
676 | return QFileSystemEngine::cloneFile(srcfd, dstfd, knownData: d->metaData); |
677 | } |
678 | |
679 | QT_END_NAMESPACE |
680 | |
681 | #endif // QT_NO_FSFILEENGINE |
682 | |