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(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(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(openMode); |
118 | |
119 | // Try to open the file in unbuffered mode. |
120 | do { |
121 | fd = QT_OPEN(fileEntry.nativeFilePath().constData(), flags, 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 | 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, metaData) |
135 | && metaData.isDirectory()) { |
136 | q->setError(QFile::OpenError, 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, 0, SEEK_END); |
147 | } while (ret == -1 && errno == EINTR); |
148 | |
149 | if (ret == -1) { |
150 | q->setError(errno == EMFILE ? QFile::ResourceError : QFile::OpenError, |
151 | qt_error_string(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(QFile::WriteError, 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(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(fh), F_SETFL, oldFlags | O_NONBLOCK); |
212 | |
213 | // Cross platform stdlib read |
214 | size_t read = 0; |
215 | do { |
216 | read = fread(data + readBytes, 1, size_t(len - readBytes), fh); |
217 | } while (read == 0 && !feof(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(fh), F_SETFL, oldFlags); |
230 | if (readBytes == 0) { |
231 | int readByte = 0; |
232 | do { |
233 | readByte = fgetc(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(fh), F_SETFL, oldFlags); |
247 | } |
248 | if (readBytes == 0 && !feof(fh)) { |
249 | // if we didn't read anything and we're not at EOF, it must be an error |
250 | q->setError(QFile::ReadError, qt_error_string(int(errno))); |
251 | return -1; |
252 | } |
253 | return readBytes; |
254 | } |
255 | |
256 | return readFdFh(data, 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(fh) : fd; |
297 | } |
298 | |
299 | /*! |
300 | \internal |
301 | */ |
302 | bool QFSFileEnginePrivate::nativeIsSequential() const |
303 | { |
304 | return isSequentialFdFh(); |
305 | } |
306 | |
307 | bool QFSFileEngine::remove() |
308 | { |
309 | Q_D(QFSFileEngine); |
310 | QSystemError error; |
311 | bool ret = QFileSystemEngine::removeFile(d->fileEntry, error); |
312 | d->metaData.clear(); |
313 | if (!ret) { |
314 | setError(QFile::RemoveError, error.toString()); |
315 | } |
316 | return ret; |
317 | } |
318 | |
319 | bool QFSFileEngine::copy(const QString &newName) |
320 | { |
321 | Q_D(QFSFileEngine); |
322 | QSystemError error; |
323 | bool ret = QFileSystemEngine::copyFile(d->fileEntry, QFileSystemEntry(newName), error); |
324 | if (!ret) { |
325 | setError(QFile::CopyError, error.toString()); |
326 | } |
327 | return ret; |
328 | } |
329 | |
330 | bool QFSFileEngine::renameOverwrite(const QString &newName) |
331 | { |
332 | Q_D(QFSFileEngine); |
333 | QSystemError error; |
334 | bool ret = QFileSystemEngine::renameOverwriteFile(d->fileEntry, QFileSystemEntry(newName), error); |
335 | |
336 | if (!ret) |
337 | setError(QFile::RenameError, error.toString()); |
338 | |
339 | return ret; |
340 | } |
341 | |
342 | bool QFSFileEngine::rename(const QString &newName) |
343 | { |
344 | Q_D(QFSFileEngine); |
345 | QSystemError error; |
346 | bool ret = QFileSystemEngine::renameFile(d->fileEntry, QFileSystemEntry(newName), error); |
347 | |
348 | if (!ret) { |
349 | setError(QFile::RenameError, error.toString()); |
350 | } |
351 | |
352 | return ret; |
353 | } |
354 | |
355 | bool QFSFileEngine::link(const QString &newName) |
356 | { |
357 | Q_D(QFSFileEngine); |
358 | QSystemError error; |
359 | bool ret = QFileSystemEngine::createLink(d->fileEntry, QFileSystemEntry(newName), error); |
360 | if (!ret) { |
361 | setError(QFile::RenameError, error.toString()); |
362 | } |
363 | return ret; |
364 | } |
365 | |
366 | qint64 QFSFileEnginePrivate::nativeSize() const |
367 | { |
368 | return sizeFdFh(); |
369 | } |
370 | |
371 | bool QFSFileEngine::mkdir(const QString &name, bool createParentDirectories) const |
372 | { |
373 | return QFileSystemEngine::createDirectory(QFileSystemEntry(name), createParentDirectories); |
374 | } |
375 | |
376 | bool QFSFileEngine::rmdir(const QString &name, bool recurseParentDirectories) const |
377 | { |
378 | return QFileSystemEngine::removeDirectory(QFileSystemEntry(name), recurseParentDirectories); |
379 | } |
380 | |
381 | bool QFSFileEngine::caseSensitive() const |
382 | { |
383 | return true; |
384 | } |
385 | |
386 | bool QFSFileEngine::setCurrentPath(const QString &path) |
387 | { |
388 | return QFileSystemEngine::setCurrentPath(QFileSystemEntry(path)); |
389 | } |
390 | |
391 | QString QFSFileEngine::currentPath(const QString &) |
392 | { |
393 | return QFileSystemEngine::currentPath().filePath(); |
394 | } |
395 | |
396 | QString QFSFileEngine::homePath() |
397 | { |
398 | return QFileSystemEngine::homePath(); |
399 | } |
400 | |
401 | QString QFSFileEngine::rootPath() |
402 | { |
403 | return QFileSystemEngine::rootPath(); |
404 | } |
405 | |
406 | QString QFSFileEngine::tempPath() |
407 | { |
408 | return QFileSystemEngine::tempPath(); |
409 | } |
410 | |
411 | QFileInfoList QFSFileEngine::drives() |
412 | { |
413 | QFileInfoList ret; |
414 | ret.append(QFileInfo(rootPath())); |
415 | return ret; |
416 | } |
417 | |
418 | bool QFSFileEnginePrivate::doStat(QFileSystemMetaData::MetaDataFlags flags) const |
419 | { |
420 | if (!tried_stat || !metaData.hasFlags(flags)) { |
421 | tried_stat = 1; |
422 | |
423 | int localFd = fd; |
424 | if (fh && fileEntry.isEmpty()) |
425 | localFd = QT_FILENO(fh); |
426 | if (localFd != -1) |
427 | QFileSystemEngine::fillMetaData(localFd, metaData); |
428 | |
429 | if (metaData.missingFlags(flags) && !fileEntry.isEmpty()) |
430 | QFileSystemEngine::fillMetaData(fileEntry, metaData, metaData.missingFlags(flags)); |
431 | } |
432 | |
433 | return metaData.exists(); |
434 | } |
435 | |
436 | bool QFSFileEnginePrivate::isSymlink() const |
437 | { |
438 | if (!metaData.hasFlags(QFileSystemMetaData::LinkType)) |
439 | QFileSystemEngine::fillMetaData(fileEntry, metaData, QFileSystemMetaData::LinkType); |
440 | |
441 | return metaData.isLink(); |
442 | } |
443 | |
444 | /*! |
445 | \reimp |
446 | */ |
447 | QAbstractFileEngine::FileFlags QFSFileEngine::fileFlags(FileFlags type) const |
448 | { |
449 | Q_D(const QFSFileEngine); |
450 | |
451 | if (type & Refresh) |
452 | d->metaData.clear(); |
453 | |
454 | QAbstractFileEngine::FileFlags ret = { }; |
455 | |
456 | if (type & FlagsMask) |
457 | ret |= LocalDiskFlag; |
458 | |
459 | bool exists; |
460 | { |
461 | QFileSystemMetaData::MetaDataFlags queryFlags = { }; |
462 | |
463 | queryFlags |= QFileSystemMetaData::MetaDataFlags(uint(type)) |
464 | & QFileSystemMetaData::Permissions; |
465 | |
466 | if (type & TypesMask) |
467 | queryFlags |= QFileSystemMetaData::AliasType |
468 | | QFileSystemMetaData::LinkType |
469 | | QFileSystemMetaData::FileType |
470 | | QFileSystemMetaData::DirectoryType |
471 | | QFileSystemMetaData::BundleType |
472 | | QFileSystemMetaData::WasDeletedAttribute; |
473 | |
474 | if (type & FlagsMask) |
475 | queryFlags |= QFileSystemMetaData::HiddenAttribute |
476 | | QFileSystemMetaData::ExistsAttribute; |
477 | else if (type & ExistsFlag) |
478 | queryFlags |= QFileSystemMetaData::WasDeletedAttribute; |
479 | |
480 | queryFlags |= QFileSystemMetaData::LinkType; |
481 | |
482 | exists = d->doStat(queryFlags); |
483 | } |
484 | |
485 | if (!exists && !d->metaData.isLink()) |
486 | return ret; |
487 | |
488 | if (exists && (type & PermsMask)) |
489 | ret |= FileFlags(uint(d->metaData.permissions())); |
490 | |
491 | if (type & TypesMask) { |
492 | if (d->metaData.isAlias()) { |
493 | ret |= LinkType; |
494 | } else { |
495 | if ((type & LinkType) && d->metaData.isLink()) |
496 | ret |= LinkType; |
497 | if (exists) { |
498 | if (d->metaData.isFile()) { |
499 | ret |= FileType; |
500 | } else if (d->metaData.isDirectory()) { |
501 | ret |= DirectoryType; |
502 | if ((type & BundleType) && d->metaData.isBundle()) |
503 | ret |= BundleType; |
504 | } |
505 | } |
506 | } |
507 | } |
508 | |
509 | if (type & FlagsMask) { |
510 | // the inode existing does not mean the file exists |
511 | if (!d->metaData.wasDeleted()) |
512 | ret |= ExistsFlag; |
513 | if (d->fileEntry.isRoot()) |
514 | ret |= RootFlag; |
515 | else if (d->metaData.isHidden()) |
516 | ret |= HiddenFlag; |
517 | } |
518 | |
519 | return ret; |
520 | } |
521 | |
522 | QByteArray QFSFileEngine::id() const |
523 | { |
524 | Q_D(const QFSFileEngine); |
525 | if (d->fd != -1) |
526 | return QFileSystemEngine::id(d->fd); |
527 | return QFileSystemEngine::id(d->fileEntry); |
528 | } |
529 | |
530 | QString QFSFileEngine::fileName(FileName file) const |
531 | { |
532 | Q_D(const QFSFileEngine); |
533 | switch (file) { |
534 | case BundleName: |
535 | return QFileSystemEngine::bundleName(d->fileEntry); |
536 | case BaseName: |
537 | return d->fileEntry.fileName(); |
538 | case PathName: |
539 | return d->fileEntry.path(); |
540 | case AbsoluteName: |
541 | case AbsolutePathName: { |
542 | QFileSystemEntry entry(QFileSystemEngine::absoluteName(d->fileEntry)); |
543 | return file == AbsolutePathName ? entry.path() : entry.filePath(); |
544 | } |
545 | case CanonicalName: |
546 | case CanonicalPathName: { |
547 | QFileSystemEntry entry(QFileSystemEngine::canonicalName(d->fileEntry, d->metaData)); |
548 | return file == CanonicalPathName ? entry.path() : entry.filePath(); |
549 | } |
550 | case LinkName: |
551 | if (d->isSymlink()) { |
552 | QFileSystemEntry entry = QFileSystemEngine::getLinkTarget(d->fileEntry, d->metaData); |
553 | return entry.filePath(); |
554 | } |
555 | return QString(); |
556 | case DefaultName: |
557 | case NFileNames: |
558 | break; |
559 | } |
560 | return d->fileEntry.filePath(); |
561 | } |
562 | |
563 | bool QFSFileEngine::isRelativePath() const |
564 | { |
565 | Q_D(const QFSFileEngine); |
566 | return d->fileEntry.filePath().length() ? d->fileEntry.filePath().at(0) != QLatin1Char('/') : true; |
567 | } |
568 | |
569 | uint QFSFileEngine::ownerId(FileOwner own) const |
570 | { |
571 | Q_D(const QFSFileEngine); |
572 | static const uint nobodyID = (uint) -2; |
573 | |
574 | if (d->doStat(QFileSystemMetaData::OwnerIds)) |
575 | return d->metaData.ownerId(own); |
576 | |
577 | return nobodyID; |
578 | } |
579 | |
580 | QString QFSFileEngine::owner(FileOwner own) const |
581 | { |
582 | if (own == OwnerUser) |
583 | return QFileSystemEngine::resolveUserName(ownerId(own)); |
584 | return QFileSystemEngine::resolveGroupName(ownerId(own)); |
585 | } |
586 | |
587 | bool QFSFileEngine::setPermissions(uint perms) |
588 | { |
589 | Q_D(QFSFileEngine); |
590 | QSystemError error; |
591 | bool ok; |
592 | if (d->fd != -1) |
593 | ok = QFileSystemEngine::setPermissions(d->fd, QFile::Permissions(perms), error); |
594 | else |
595 | ok = QFileSystemEngine::setPermissions(d->fileEntry, QFile::Permissions(perms), error); |
596 | if (!ok) { |
597 | setError(QFile::PermissionsError, error.toString()); |
598 | return false; |
599 | } |
600 | return true; |
601 | } |
602 | |
603 | bool QFSFileEngine::setSize(qint64 size) |
604 | { |
605 | Q_D(QFSFileEngine); |
606 | bool ret = false; |
607 | if (d->fd != -1) |
608 | ret = QT_FTRUNCATE(d->fd, size) == 0; |
609 | else if (d->fh) |
610 | ret = QT_FTRUNCATE(QT_FILENO(d->fh), size) == 0; |
611 | else |
612 | ret = QT_TRUNCATE(d->fileEntry.nativeFilePath().constData(), size) == 0; |
613 | if (!ret) |
614 | setError(QFile::ResizeError, qt_error_string(errno)); |
615 | return ret; |
616 | } |
617 | |
618 | bool QFSFileEngine::setFileTime(const QDateTime &newDate, FileTime time) |
619 | { |
620 | Q_D(QFSFileEngine); |
621 | |
622 | if (d->openMode == QIODevice::NotOpen) { |
623 | setError(QFile::PermissionsError, qt_error_string(EACCES)); |
624 | return false; |
625 | } |
626 | |
627 | QSystemError error; |
628 | if (!QFileSystemEngine::setFileTime(d->nativeHandle(), newDate, time, error)) { |
629 | setError(QFile::PermissionsError, error.toString()); |
630 | return false; |
631 | } |
632 | |
633 | d->metaData.clearFlags(QFileSystemMetaData::Times); |
634 | return true; |
635 | } |
636 | |
637 | uchar *QFSFileEnginePrivate::map(qint64 offset, qint64 size, QFile::MemoryMapFlags flags) |
638 | { |
639 | qint64 maxFileOffset = std::numeric_limits<QT_OFF_T>::max(); |
640 | #if (defined(Q_OS_LINUX) || defined(Q_OS_ANDROID)) && Q_PROCESSOR_WORDSIZE == 4 |
641 | // The Linux mmap2 system call on 32-bit takes a page-shifted 32-bit |
642 | // integer so the maximum offset is 1 << (32+12) (the shift is always 12, |
643 | // regardless of the actual page size). Unfortunately, the mmap64() |
644 | // function is known to be broken in all Linux libcs (glibc, uclibc, musl |
645 | // and Bionic): all of them do the right shift, but don't confirm that the |
646 | // result fits into the 32-bit parameter to the kernel. |
647 | |
648 | maxFileOffset = qMin((Q_INT64_C(1) << (32+12)) - 1, maxFileOffset); |
649 | #endif |
650 | |
651 | Q_Q(QFSFileEngine); |
652 | if (openMode == QIODevice::NotOpen) { |
653 | q->setError(QFile::PermissionsError, qt_error_string(int(EACCES))); |
654 | return nullptr; |
655 | } |
656 | |
657 | if (offset < 0 || offset > maxFileOffset |
658 | || size < 0 || quint64(size) > quint64(size_t(-1))) { |
659 | q->setError(QFile::UnspecifiedError, qt_error_string(int(EINVAL))); |
660 | return nullptr; |
661 | } |
662 | |
663 | // If we know the mapping will extend beyond EOF, fail early to avoid |
664 | // undefined behavior. Otherwise, let mmap have its say. |
665 | if (doStat(QFileSystemMetaData::SizeAttribute) |
666 | && (QT_OFF_T(size) > metaData.size() - QT_OFF_T(offset))) |
667 | qWarning("QFSFileEngine::map: Mapping a file beyond its size is not portable" ); |
668 | |
669 | int access = 0; |
670 | if (openMode & QIODevice::ReadOnly) access |= PROT_READ; |
671 | if (openMode & QIODevice::WriteOnly) access |= PROT_WRITE; |
672 | |
673 | int sharemode = MAP_SHARED; |
674 | if (flags & QFileDevice::MapPrivateOption) { |
675 | sharemode = MAP_PRIVATE; |
676 | access |= PROT_WRITE; |
677 | } |
678 | |
679 | #if defined(Q_OS_INTEGRITY) |
680 | int pageSize = sysconf(_SC_PAGESIZE); |
681 | #else |
682 | int pageSize = getpagesize(); |
683 | #endif |
684 | int = offset % pageSize; |
685 | |
686 | if (quint64(size + extra) > quint64((size_t)-1)) { |
687 | q->setError(QFile::UnspecifiedError, qt_error_string(int(EINVAL))); |
688 | return nullptr; |
689 | } |
690 | |
691 | size_t realSize = (size_t)size + extra; |
692 | QT_OFF_T realOffset = QT_OFF_T(offset); |
693 | realOffset &= ~(QT_OFF_T(pageSize - 1)); |
694 | |
695 | void *mapAddress = QT_MMAP((void*)nullptr, realSize, |
696 | access, sharemode, nativeHandle(), realOffset); |
697 | if (MAP_FAILED != mapAddress) { |
698 | uchar *address = extra + static_cast<uchar*>(mapAddress); |
699 | maps[address] = QPair<int,size_t>(extra, realSize); |
700 | return address; |
701 | } |
702 | |
703 | switch(errno) { |
704 | case EBADF: |
705 | q->setError(QFile::PermissionsError, qt_error_string(int(EACCES))); |
706 | break; |
707 | case ENFILE: |
708 | case ENOMEM: |
709 | q->setError(QFile::ResourceError, qt_error_string(int(errno))); |
710 | break; |
711 | case EINVAL: |
712 | // size are out of bounds |
713 | default: |
714 | q->setError(QFile::UnspecifiedError, qt_error_string(int(errno))); |
715 | break; |
716 | } |
717 | return nullptr; |
718 | } |
719 | |
720 | bool QFSFileEnginePrivate::unmap(uchar *ptr) |
721 | { |
722 | #if !defined(Q_OS_INTEGRITY) |
723 | Q_Q(QFSFileEngine); |
724 | if (!maps.contains(ptr)) { |
725 | q->setError(QFile::PermissionsError, qt_error_string(EACCES)); |
726 | return false; |
727 | } |
728 | |
729 | uchar *start = ptr - maps[ptr].first; |
730 | size_t len = maps[ptr].second; |
731 | if (-1 == munmap(start, len)) { |
732 | q->setError(QFile::UnspecifiedError, qt_error_string(errno)); |
733 | return false; |
734 | } |
735 | maps.remove(ptr); |
736 | return true; |
737 | #else |
738 | return false; |
739 | #endif |
740 | } |
741 | |
742 | /*! |
743 | \reimp |
744 | */ |
745 | bool QFSFileEngine::cloneTo(QAbstractFileEngine *target) |
746 | { |
747 | Q_D(QFSFileEngine); |
748 | if ((target->fileFlags(LocalDiskFlag) & LocalDiskFlag) == 0) |
749 | return false; |
750 | |
751 | int srcfd = d->nativeHandle(); |
752 | int dstfd = target->handle(); |
753 | return QFileSystemEngine::cloneFile(srcfd, dstfd, d->metaData); |
754 | } |
755 | |
756 | QT_END_NAMESPACE |
757 | |
758 | #endif // QT_NO_FSFILEENGINE |
759 | |