Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qfsfileengine_unix.cpp
Go to the documentation of this file.
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"
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
29
35static 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
43 oflags = QT_OPEN_RDWR;
44 else if (mode & QFile::WriteOnly)
45 oflags = QT_OPEN_WRONLY;
46
48 oflags |= QT_OPEN_CREAT;
49
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
63{
64 const char message[] = QT_TRANSLATE_NOOP("QIODevice", "file to open is a directory");
65#if QT_CONFIG(translation)
66 return QIODevice::tr(message);
67#else
69#endif
70}
71
75bool QFSFileEnginePrivate::nativeOpen(QIODevice::OpenMode openMode,
76 std::optional<QFile::Permissions> permissions)
77{
78 return nativeOpenImpl(openMode, permissions ? QtPrivate::toMode_t(*permissions) : 0666);
79}
80
84bool 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");
92
93 // Try to open the file in unbuffered mode.
94 do {
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 qt_error_string(errno));
102 return false;
103 }
104
106 // we don't need this check if we tried to open for writing because then
107 // we had received EISDIR anyway.
109 && metaData.isDirectory()) {
110 q->setError(QFile::OpenError, 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, 0, SEEK_END);
121 } while (ret == -1 && errno == EINTR);
122
123 if (ret == -1) {
124 q->setError(errno == EMFILE ? QFile::ResourceError : QFile::OpenError,
125 qt_error_string(errno));
126 return false;
127 }
128 }
129
130 fh = nullptr;
131 }
132
133 closeFileHandle = true;
134 return true;
135}
136
141{
142 return closeFdFh();
143}
144
150{
151 return fh ? flushFh() : fd != -1;
152}
153
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(QFile::WriteError, qt_error_string(errno));
169 return ret == 0;
170}
171
176{
177 Q_Q(QFSFileEngine);
178
179 if (fh && nativeIsSequential()) {
180 size_t readBytes = 0;
181 int oldFlags = fcntl(QT_FILENO(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(fh), F_SETFL, oldFlags | O_NONBLOCK);
186
187 // Cross platform stdlib read
188 size_t read = 0;
189 do {
190 read = fread(data + readBytes, 1, size_t(len - readBytes), fh);
191 } while (read == 0 && !feof(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(fh), F_SETFL, oldFlags);
204 if (readBytes == 0) {
205 int readByte = 0;
206 do {
207 readByte = fgetc(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(fh), F_SETFL, oldFlags);
221 }
222 if (readBytes == 0 && !feof(fh)) {
223 // if we didn't read anything and we're not at EOF, it must be an error
224 q->setError(QFile::ReadError, qt_error_string(errno));
225 return -1;
226 }
227 return readBytes;
228 }
229
230 return readFdFh(data, len);
231}
232
237{
238 return readLineFdFh(data, maxlen);
239}
240
248
253{
254 return posFdFh();
255}
256
264
269{
270 return fh ? fileno(fh) : fd;
271}
272
280
281bool QFSFileEngine::link(const QString &newName)
282{
283 Q_D(QFSFileEngine);
285 bool ret = QFileSystemEngine::createLink(d->fileEntry, QFileSystemEntry(newName), error);
286 if (!ret) {
287 setError(QFile::RenameError, error.toString());
288 }
289 return ret;
290}
291
293{
294 return sizeFdFh();
295}
296
298{
299 return true;
300}
301
306
307
314
315bool 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(fh);
323 if (localFd != -1)
325
328 }
329
330 return metaData.exists();
331}
332
340
344QAbstractFileEngine::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)
355
356 bool exists;
357 {
358 QFileSystemMetaData::MetaDataFlags queryFlags = { };
359
360 queryFlags |= QFileSystemMetaData::MetaDataFlags(uint(type.toInt()))
362
363 if (type & TypesMask)
370
371 if (type & FlagsMask)
374 else if (type & ExistsFlag)
376
377 queryFlags |= QFileSystemMetaData::LinkType;
378
379 exists = d->doStat(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()) {
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
420{
421 Q_D(const QFSFileEngine);
422 if (d->fd != -1)
423 return QFileSystemEngine::id(d->fd);
424 return QFileSystemEngine::id(d->fileEntry);
425}
426
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: {
440 return file == AbsolutePathName ? entry.path() : entry.filePath();
441 }
442 case CanonicalName:
443 case CanonicalPathName: {
445 return file == CanonicalPathName ? entry.path() : entry.filePath();
446 }
448 if (d->isSymlink()) {
450 return entry.filePath();
451 }
452 return QString();
453 case RawLinkPath:
454 if (d->isSymlink()) {
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
469{
470 Q_D(const QFSFileEngine);
471 const QString fp = d->fileEntry.filePath();
472 return fp.isEmpty() || fp.at(0) != u'/';
473}
474
476{
477 Q_D(const QFSFileEngine);
478 static const uint nobodyID = (uint) -2;
479
480 if (d->doStat(QFileSystemMetaData::OwnerIds))
481 return d->metaData.ownerId(own);
482
483 return nobodyID;
484}
485
492
494{
495 Q_D(QFSFileEngine);
497 bool ok;
498
499 // clear cached state (if any)
500 d->metaData.clearFlags(QFileSystemMetaData::Permissions);
501
502 if (d->fd != -1)
503 ok = QFileSystemEngine::setPermissions(d->fd, QFile::Permissions(perms), error);
504 else
505 ok = QFileSystemEngine::setPermissions(d->fileEntry, QFile::Permissions(perms), error);
506 if (!ok) {
508 return false;
509 }
510 return true;
511}
512
514{
515 Q_D(QFSFileEngine);
516 bool ret = false;
517 if (d->fd != -1)
518 ret = QT_FTRUNCATE(d->fd, size) == 0;
519 else if (d->fh)
520 ret = QT_FTRUNCATE(QT_FILENO(d->fh), size) == 0;
521 else
522 ret = QT_TRUNCATE(d->fileEntry.nativeFilePath().constData(), size) == 0;
523 if (!ret)
525 return ret;
526}
527
529{
530 Q_D(QFSFileEngine);
531
532 if (d->openMode == QIODevice::NotOpen) {
534 return false;
535 }
536
538 if (!QFileSystemEngine::setFileTime(d->nativeHandle(), newDate, time, error)) {
540 return false;
541 }
542
543 d->metaData.clearFlags(QFileSystemMetaData::Times);
544 return true;
545}
546
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);
563 q->setError(QFile::PermissionsError, qt_error_string(EACCES));
564 return nullptr;
565 }
566
567 if (offset < 0 || offset > maxFileOffset
568 || size <= 0
569 || quint64(size) > quint64(size_t(-1))) {
570 q->setError(QFile::UnspecifiedError, qt_error_string(EINVAL));
571 return nullptr;
572 }
573 // If we know the mapping will extend beyond EOF, fail early to avoid
574 // undefined behavior. Otherwise, let mmap have its say.
576 && (QT_OFF_T(size) > metaData.size() - QT_OFF_T(offset)))
577 qWarning("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;
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 extra = offset % pageSize;
595
596 if (quint64(size + extra) > quint64((size_t)-1)) {
597 q->setError(QFile::UnspecifiedError, 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((void*)nullptr, realSize,
606 access, sharemode, nativeHandle(), realOffset);
607 if (MAP_FAILED != mapAddress) {
608 uchar *address = extra + static_cast<uchar*>(mapAddress);
609 maps[address] = {extra, realSize};
610 return address;
611 }
612
613 switch(errno) {
614 case EBADF:
615 q->setError(QFile::PermissionsError, qt_error_string(EACCES));
616 break;
617 case ENFILE:
618 case ENOMEM:
619 q->setError(QFile::ResourceError, qt_error_string(errno));
620 break;
621 case EINVAL:
622 // size are out of bounds
623 default:
624 q->setError(QFile::UnspecifiedError, qt_error_string(errno));
625 break;
626 }
627 return nullptr;
628}
629
631{
632#if !defined(Q_OS_INTEGRITY)
633 Q_Q(QFSFileEngine);
634 const auto it = std::as_const(maps).find(ptr);
635 if (it == maps.cend()) {
636 q->setError(QFile::PermissionsError, 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(start, len)) {
643 q->setError(QFile::UnspecifiedError, qt_error_string(errno));
644 return false;
645 }
646 maps.erase(it);
647 return true;
648#else
649 return false;
650#endif
651}
652
657{
658 Q_D(QFSFileEngine);
659 if ((target->fileFlags(LocalDiskFlag) & LocalDiskFlag) == 0)
660 return false;
661
662 int srcfd = d->nativeHandle();
663 int dstfd = target->handle();
664 return QFileSystemEngine::cloneFile(srcfd, dstfd, d->metaData);
665}
666
668
669#endif // QT_NO_FSFILEENGINE
\inmodule QtCore \reentrant
FileOwner
\value OwnerUser The user who owns the file.
void setError(QFile::FileError error, const QString &str)
Sets the error type to error, and the error string to errorString.
FileName
These values are used to request a file name in a particular format.
QFile::FileError error() const
Returns the QFile::FileError that resulted from the last failed operation.
\inmodule QtCore
Definition qbytearray.h:57
const char * constData() const noexcept
Returns a pointer to the const data stored in the byte array.
Definition qbytearray.h:124
\inmodule QtCore\reentrant
Definition qdatetime.h:283
qint64 nativeRead(char *data, qint64 maxlen)
qint64 readFdFh(char *data, qint64 maxlen)
QHash< uchar *, StartAndLength > maps
qint64 writeFdFh(const char *data, qint64 len)
QIODevice::OpenMode openMode
QFileSystemEntry fileEntry
qint64 sizeFdFh() const
bool doStat(QFileSystemMetaData::MetaDataFlags flags=QFileSystemMetaData::PosixStatFlags) const
bool nativeOpen(QIODevice::OpenMode openMode, std::optional< QFile::Permissions > permissions)
QFileSystemMetaData metaData
static bool openModeCanCreate(QIODevice::OpenMode openMode)
bool isSequentialFdFh() const
qint64 readLineFdFh(char *data, qint64 maxlen)
qint64 nativeWrite(const char *data, qint64 len)
uchar * map(qint64 offset, qint64 size, QFile::MemoryMapFlags flags)
qint64 nativeReadLine(char *data, qint64 maxlen)
\inmodule QtCore
static QFileInfoList drives()
For Windows, returns the list of drives in the file system as a list of QFileInfo objects.
QString fileName(FileName file) const override
\reimp
uint ownerId(FileOwner) const override
In Unix, if stat() is successful, the uid is returned if own is the owner.
bool setFileTime(const QDateTime &newDate, QFile::FileTime time) override
\reimp
static QString currentPath(const QString &path=QString())
For Unix, returns the current working directory for the file engine.
bool cloneTo(QAbstractFileEngine *target) override
\reimp
bool setSize(qint64 size) override
\reimp
bool setPermissions(uint perms) override
\reimp
bool caseSensitive() const override
Returns false for Windows, true for Unix.
static QString rootPath()
Returns the root path.
QByteArray id() const override
bool link(const QString &newName) override
Creates a link from the file currently specified by fileName() to newName.
bool isRelativePath() const override
\reimp
FileFlags fileFlags(FileFlags type) const override
\reimp
QString owner(FileOwner) const override
\reimp
static QFileSystemEntry getLinkTarget(const QFileSystemEntry &link, QFileSystemMetaData &data)
static QFileSystemEntry canonicalName(const QFileSystemEntry &entry, QFileSystemMetaData &data)
static QByteArray id(const QFileSystemEntry &entry)
static QFileSystemEntry getRawLinkPath(const QFileSystemEntry &link, QFileSystemMetaData &data)
static bool fillMetaData(const QFileSystemEntry &entry, QFileSystemMetaData &data, QFileSystemMetaData::MetaDataFlags what)
static QString bundleName(const QFileSystemEntry &)
static bool createLink(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error)
static bool setPermissions(const QFileSystemEntry &entry, QFile::Permissions permissions, QSystemError &error, QFileSystemMetaData *data=nullptr)
static QFileSystemEntry absoluteName(const QFileSystemEntry &entry)
static bool setFileTime(const QFileSystemEntry &entry, const QDateTime &newDate, QFile::FileTime whatTime, QSystemError &error)
static QString resolveUserName(const QFileSystemEntry &entry, QFileSystemMetaData &data)
static QString resolveGroupName(const QFileSystemEntry &entry, QFileSystemMetaData &data)
static QFileSystemEntry currentPath()
Q_AUTOTEST_EXPORT NativePath nativeFilePath() const
Q_AUTOTEST_EXPORT bool isEmpty() const
bool hasFlags(MetaDataFlags flags) const
MetaDataFlags missingFlags(MetaDataFlags flags)
\inmodule QtCore
Definition qfile.h:93
void append(parameter_type t)
Definition qlist.h:458
iterator find(const T &value)
Definition qset.h:159
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
QSet< QString >::iterator it
@ OpenError
Definition qaudio.h:28
Combined button and popup list for selecting options.
#define QT_EINTR_LOOP(var, cmd)
#define QT_OPEN
#define QT_CLOSE
static QT_BEGIN_NAMESPACE int openModeToOpenFlags(QIODevice::OpenMode mode)
static QString msgOpenDirectory()
Q_DECL_COLD_FUNCTION Q_CORE_EXPORT QString qt_error_string(int errorCode=-1)
#define qWarning
Definition qlogging.h:166
return ret
static ControlElement< T > * ptr(QWidget *widget)
constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:40
GLenum mode
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLenum type
GLenum access
GLenum target
GLbitfield flags
GLuint GLsizei const GLchar * message
GLuint start
GLenum GLuint GLintptr offset
GLuint64 GLenum GLint fd
GLuint entry
GLuint GLuint64EXT address
GLdouble GLdouble GLdouble GLdouble q
Definition qopenglext.h:259
GLenum GLsizei len
#define Q_ASSERT_X(cond, x, msg)
Definition qrandom.cpp:48
#define MAP_FAILED
#define fp
#define QT_TRANSLATE_NOOP(scope, x)
unsigned char uchar
Definition qtypes.h:32
unsigned long long quint64
Definition qtypes.h:61
unsigned int uint
Definition qtypes.h:34
long long qint64
Definition qtypes.h:60
#define Q_INT64_C(c)
Definition qtypes.h:57
ReturnedValue read(const char *data)
QFile file
[0]