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 | EINTR_LOOP(ret, fdatasync(nativeHandle())); |
164 | #else |
165 | 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 | bool QFSFileEngine::caseSensitive() const |
298 | { |
299 | return true; |
300 | } |
301 | |
302 | QString QFSFileEngine::currentPath(const QString &) |
303 | { |
304 | return QFileSystemEngine::currentPath().filePath(); |
305 | } |
306 | |
307 | |
308 | QFileInfoList QFSFileEngine::drives() |
309 | { |
310 | QFileInfoList ret; |
311 | ret.append(t: QFileInfo(rootPath())); |
312 | return ret; |
313 | } |
314 | |
315 | bool QFSFileEnginePrivate::doStat(QFileSystemMetaData::MetaDataFlags flags) const |
316 | { |
317 | if (!tried_stat || !metaData.hasFlags(flags)) { |
318 | tried_stat = 1; |
319 | |
320 | int localFd = fd; |
321 | if (fh && fileEntry.isEmpty()) |
322 | localFd = QT_FILENO(stream: fh); |
323 | if (localFd != -1) |
324 | QFileSystemEngine::fillMetaData(fd: localFd, data&: metaData); |
325 | |
326 | if (metaData.missingFlags(flags) && !fileEntry.isEmpty()) |
327 | QFileSystemEngine::fillMetaData(entry: fileEntry, data&: metaData, what: metaData.missingFlags(flags)); |
328 | } |
329 | |
330 | return metaData.exists(); |
331 | } |
332 | |
333 | bool QFSFileEnginePrivate::isSymlink() const |
334 | { |
335 | if (!metaData.hasFlags(flags: QFileSystemMetaData::LinkType)) |
336 | QFileSystemEngine::fillMetaData(entry: fileEntry, data&: metaData, what: QFileSystemMetaData::LinkType); |
337 | |
338 | return metaData.isLink(); |
339 | } |
340 | |
341 | /*! |
342 | \reimp |
343 | */ |
344 | QAbstractFileEngine::FileFlags QFSFileEngine::fileFlags(FileFlags type) const |
345 | { |
346 | Q_D(const QFSFileEngine); |
347 | |
348 | if (type & Refresh) |
349 | d->metaData.clear(); |
350 | |
351 | QAbstractFileEngine::FileFlags ret = { }; |
352 | |
353 | if (type & FlagsMask) |
354 | ret |= LocalDiskFlag; |
355 | |
356 | bool exists; |
357 | { |
358 | QFileSystemMetaData::MetaDataFlags queryFlags = { }; |
359 | |
360 | queryFlags |= QFileSystemMetaData::MetaDataFlags(uint(type.toInt())) |
361 | & QFileSystemMetaData::Permissions; |
362 | |
363 | if (type & TypesMask) |
364 | queryFlags |= QFileSystemMetaData::AliasType |
365 | | QFileSystemMetaData::LinkType |
366 | | QFileSystemMetaData::FileType |
367 | | QFileSystemMetaData::DirectoryType |
368 | | QFileSystemMetaData::BundleType |
369 | | QFileSystemMetaData::WasDeletedAttribute; |
370 | |
371 | if (type & FlagsMask) |
372 | queryFlags |= QFileSystemMetaData::HiddenAttribute |
373 | | QFileSystemMetaData::ExistsAttribute; |
374 | else if (type & ExistsFlag) |
375 | queryFlags |= QFileSystemMetaData::WasDeletedAttribute; |
376 | |
377 | queryFlags |= QFileSystemMetaData::LinkType; |
378 | |
379 | exists = d->doStat(flags: queryFlags); |
380 | } |
381 | |
382 | if (!exists && !d->metaData.isLink()) |
383 | return ret; |
384 | |
385 | if (exists && (type & PermsMask)) |
386 | ret |= FileFlags(uint(d->metaData.permissions().toInt())); |
387 | |
388 | if (type & TypesMask) { |
389 | if (d->metaData.isAlias()) { |
390 | ret |= LinkType; |
391 | } else { |
392 | if ((type & LinkType) && d->metaData.isLink()) |
393 | ret |= LinkType; |
394 | if (exists) { |
395 | if (d->metaData.isFile()) { |
396 | ret |= FileType; |
397 | } else if (d->metaData.isDirectory()) { |
398 | ret |= DirectoryType; |
399 | if ((type & BundleType) && d->metaData.isBundle()) |
400 | ret |= BundleType; |
401 | } |
402 | } |
403 | } |
404 | } |
405 | |
406 | if (type & FlagsMask) { |
407 | // the inode existing does not mean the file exists |
408 | if (!d->metaData.wasDeleted()) |
409 | ret |= ExistsFlag; |
410 | if (d->fileEntry.isRoot()) |
411 | ret |= RootFlag; |
412 | else if (d->metaData.isHidden()) |
413 | ret |= HiddenFlag; |
414 | } |
415 | |
416 | return ret; |
417 | } |
418 | |
419 | QByteArray QFSFileEngine::id() const |
420 | { |
421 | Q_D(const QFSFileEngine); |
422 | if (d->fd != -1) |
423 | return QFileSystemEngine::id(fd: d->fd); |
424 | return QFileSystemEngine::id(entry: d->fileEntry); |
425 | } |
426 | |
427 | QString QFSFileEngine::fileName(FileName file) const |
428 | { |
429 | Q_D(const QFSFileEngine); |
430 | switch (file) { |
431 | case BundleName: |
432 | return QFileSystemEngine::bundleName(d->fileEntry); |
433 | case BaseName: |
434 | return d->fileEntry.fileName(); |
435 | case PathName: |
436 | return d->fileEntry.path(); |
437 | case AbsoluteName: |
438 | case AbsolutePathName: { |
439 | QFileSystemEntry entry(QFileSystemEngine::absoluteName(entry: d->fileEntry)); |
440 | return file == AbsolutePathName ? entry.path() : entry.filePath(); |
441 | } |
442 | case CanonicalName: |
443 | case CanonicalPathName: { |
444 | QFileSystemEntry entry(QFileSystemEngine::canonicalName(entry: d->fileEntry, data&: d->metaData)); |
445 | return file == CanonicalPathName ? entry.path() : entry.filePath(); |
446 | } |
447 | case AbsoluteLinkTarget: |
448 | if (d->isSymlink()) { |
449 | QFileSystemEntry entry = QFileSystemEngine::getLinkTarget(link: d->fileEntry, data&: d->metaData); |
450 | return entry.filePath(); |
451 | } |
452 | return QString(); |
453 | case RawLinkPath: |
454 | if (d->isSymlink()) { |
455 | QFileSystemEntry entry = QFileSystemEngine::getRawLinkPath(link: d->fileEntry, data&: d->metaData); |
456 | return entry.filePath(); |
457 | } |
458 | return QString(); |
459 | case JunctionName: |
460 | return QString(); |
461 | case DefaultName: |
462 | case NFileNames: |
463 | break; |
464 | } |
465 | return d->fileEntry.filePath(); |
466 | } |
467 | |
468 | bool QFSFileEngine::isRelativePath() const |
469 | { |
470 | Q_D(const QFSFileEngine); |
471 | const QString fp = d->fileEntry.filePath(); |
472 | return fp.isEmpty() || fp.at(i: 0) != u'/'; |
473 | } |
474 | |
475 | uint QFSFileEngine::ownerId(FileOwner own) const |
476 | { |
477 | Q_D(const QFSFileEngine); |
478 | static const uint nobodyID = (uint) -2; |
479 | |
480 | if (d->doStat(flags: QFileSystemMetaData::OwnerIds)) |
481 | return d->metaData.ownerId(owner: own); |
482 | |
483 | return nobodyID; |
484 | } |
485 | |
486 | QString QFSFileEngine::owner(FileOwner own) const |
487 | { |
488 | if (own == OwnerUser) |
489 | return QFileSystemEngine::resolveUserName(userId: ownerId(own)); |
490 | return QFileSystemEngine::resolveGroupName(groupId: ownerId(own)); |
491 | } |
492 | |
493 | bool QFSFileEngine::setPermissions(uint perms) |
494 | { |
495 | Q_D(QFSFileEngine); |
496 | QSystemError error; |
497 | bool ok; |
498 | |
499 | // clear cached state (if any) |
500 | d->metaData.clearFlags(flags: QFileSystemMetaData::Permissions); |
501 | |
502 | if (d->fd != -1) |
503 | ok = QFileSystemEngine::setPermissions(fd: d->fd, permissions: QFile::Permissions(perms), error); |
504 | else |
505 | ok = QFileSystemEngine::setPermissions(entry: d->fileEntry, permissions: QFile::Permissions(perms), error); |
506 | if (!ok) { |
507 | setError(error: QFile::PermissionsError, str: error.toString()); |
508 | return false; |
509 | } |
510 | return true; |
511 | } |
512 | |
513 | bool QFSFileEngine::setSize(qint64 size) |
514 | { |
515 | Q_D(QFSFileEngine); |
516 | bool ret = false; |
517 | if (d->fd != -1) |
518 | ret = QT_FTRUNCATE(fd: d->fd, length: size) == 0; |
519 | else if (d->fh) |
520 | ret = QT_FTRUNCATE(QT_FILENO(stream: d->fh), length: size) == 0; |
521 | else |
522 | ret = QT_TRUNCATE(file: d->fileEntry.nativeFilePath().constData(), length: size) == 0; |
523 | if (!ret) |
524 | setError(error: QFile::ResizeError, str: qt_error_string(errno)); |
525 | return ret; |
526 | } |
527 | |
528 | bool QFSFileEngine::setFileTime(const QDateTime &newDate, FileTime time) |
529 | { |
530 | Q_D(QFSFileEngine); |
531 | |
532 | if (d->openMode == QIODevice::NotOpen) { |
533 | setError(error: QFile::PermissionsError, str: qt_error_string(EACCES)); |
534 | return false; |
535 | } |
536 | |
537 | QSystemError error; |
538 | if (!QFileSystemEngine::setFileTime(fd: d->nativeHandle(), newDate, whatTime: time, error)) { |
539 | setError(error: QFile::PermissionsError, str: error.toString()); |
540 | return false; |
541 | } |
542 | |
543 | d->metaData.clearFlags(flags: QFileSystemMetaData::Times); |
544 | return true; |
545 | } |
546 | |
547 | uchar *QFSFileEnginePrivate::map(qint64 offset, qint64 size, QFile::MemoryMapFlags flags) |
548 | { |
549 | qint64 maxFileOffset = std::numeric_limits<QT_OFF_T>::max(); |
550 | #if (defined(Q_OS_LINUX) || defined(Q_OS_ANDROID)) && Q_PROCESSOR_WORDSIZE == 4 |
551 | // The Linux mmap2 system call on 32-bit takes a page-shifted 32-bit |
552 | // integer so the maximum offset is 1 << (32+12) (the shift is always 12, |
553 | // regardless of the actual page size). Unfortunately, the mmap64() |
554 | // function is known to be broken in all Linux libcs (glibc, uclibc, musl |
555 | // and Bionic): all of them do the right shift, but don't confirm that the |
556 | // result fits into the 32-bit parameter to the kernel. |
557 | |
558 | maxFileOffset = qMin((Q_INT64_C(1) << (32+12)) - 1, maxFileOffset); |
559 | #endif |
560 | |
561 | Q_Q(QFSFileEngine); |
562 | if (openMode == QIODevice::NotOpen) { |
563 | q->setError(error: QFile::PermissionsError, str: qt_error_string(EACCES)); |
564 | return nullptr; |
565 | } |
566 | |
567 | if (offset < 0 || offset > maxFileOffset |
568 | || size < 0 || quint64(size) > quint64(size_t(-1))) { |
569 | q->setError(error: QFile::UnspecifiedError, str: qt_error_string(EINVAL)); |
570 | return nullptr; |
571 | } |
572 | |
573 | // If we know the mapping will extend beyond EOF, fail early to avoid |
574 | // undefined behavior. Otherwise, let mmap have its say. |
575 | if (doStat(flags: QFileSystemMetaData::SizeAttribute) |
576 | && (QT_OFF_T(size) > metaData.size() - QT_OFF_T(offset))) |
577 | qWarning(msg: "QFSFileEngine::map: Mapping a file beyond its size is not portable" ); |
578 | |
579 | int access = 0; |
580 | if (openMode & QIODevice::ReadOnly) access |= PROT_READ; |
581 | if (openMode & QIODevice::WriteOnly) access |= PROT_WRITE; |
582 | |
583 | int sharemode = MAP_SHARED; |
584 | if (flags & QFileDevice::MapPrivateOption) { |
585 | sharemode = MAP_PRIVATE; |
586 | access |= PROT_WRITE; |
587 | } |
588 | |
589 | #if defined(Q_OS_INTEGRITY) |
590 | int pageSize = sysconf(_SC_PAGESIZE); |
591 | #else |
592 | int pageSize = getpagesize(); |
593 | #endif |
594 | int = offset % pageSize; |
595 | |
596 | if (quint64(size + extra) > quint64((size_t)-1)) { |
597 | q->setError(error: QFile::UnspecifiedError, str: qt_error_string(EINVAL)); |
598 | return nullptr; |
599 | } |
600 | |
601 | size_t realSize = (size_t)size + extra; |
602 | QT_OFF_T realOffset = QT_OFF_T(offset); |
603 | realOffset &= ~(QT_OFF_T(pageSize - 1)); |
604 | |
605 | void *mapAddress = QT_MMAP(addr: (void*)nullptr, len: realSize, |
606 | prot: access, flags: sharemode, fd: nativeHandle(), offset: realOffset); |
607 | if (MAP_FAILED != mapAddress) { |
608 | uchar *address = extra + static_cast<uchar*>(mapAddress); |
609 | maps[address] = {.start: extra, .length: realSize}; |
610 | return address; |
611 | } |
612 | |
613 | switch(errno) { |
614 | case EBADF: |
615 | q->setError(error: QFile::PermissionsError, str: qt_error_string(EACCES)); |
616 | break; |
617 | case ENFILE: |
618 | case ENOMEM: |
619 | q->setError(error: QFile::ResourceError, str: qt_error_string(errno)); |
620 | break; |
621 | case EINVAL: |
622 | // size are out of bounds |
623 | default: |
624 | q->setError(error: QFile::UnspecifiedError, str: qt_error_string(errno)); |
625 | break; |
626 | } |
627 | return nullptr; |
628 | } |
629 | |
630 | bool QFSFileEnginePrivate::unmap(uchar *ptr) |
631 | { |
632 | #if !defined(Q_OS_INTEGRITY) |
633 | Q_Q(QFSFileEngine); |
634 | const auto it = std::as_const(t&: maps).find(key: ptr); |
635 | if (it == maps.cend()) { |
636 | q->setError(error: QFile::PermissionsError, str: qt_error_string(EACCES)); |
637 | return false; |
638 | } |
639 | |
640 | uchar *start = ptr - it->start; |
641 | size_t len = it->length; |
642 | if (-1 == munmap(addr: start, len: len)) { |
643 | q->setError(error: QFile::UnspecifiedError, str: qt_error_string(errno)); |
644 | return false; |
645 | } |
646 | maps.erase(it); |
647 | return true; |
648 | #else |
649 | return false; |
650 | #endif |
651 | } |
652 | |
653 | /*! |
654 | \reimp |
655 | */ |
656 | bool QFSFileEngine::cloneTo(QAbstractFileEngine *target) |
657 | { |
658 | Q_D(QFSFileEngine); |
659 | if ((target->fileFlags(type: LocalDiskFlag) & LocalDiskFlag) == 0) |
660 | return false; |
661 | |
662 | int srcfd = d->nativeHandle(); |
663 | int dstfd = target->handle(); |
664 | return QFileSystemEngine::cloneFile(srcfd, dstfd, knownData: d->metaData); |
665 | } |
666 | |
667 | QT_END_NAMESPACE |
668 | |
669 | #endif // QT_NO_FSFILEENGINE |
670 | |