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