1
/****************************************************************************
3
** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4
** Contact: http://www.qt-project.org/legal
6
** This file is part of the QtCore module of the Qt Toolkit.
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 Digia. For licensing terms and
14
** conditions see http://qt.digia.com/licensing. For further information
15
** use the contact form at http://qt.digia.com/contact-us.
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 2.1 as published by the Free Software
20
** Foundation and appearing in the file LICENSE.LGPL included in the
21
** packaging of this file. Please review the following information to
22
** ensure the GNU Lesser General Public License version 2.1 requirements
23
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25
** In addition, as a special exception, Digia gives you certain additional
26
** rights. These rights are described in the Digia Qt LGPL Exception
27
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29
** GNU General Public License Usage
30
** Alternatively, this file may be used under the terms of the GNU
31
** General Public License version 3.0 as published by the Free Software
32
** Foundation and appearing in the file LICENSE.GPL included in the
33
** packaging of this file. Please review the following information to
34
** ensure the GNU General Public License version 3.0 requirements will be
35
** met: http://www.gnu.org/copyleft/gpl.html.
40
****************************************************************************/
42
#include "qplatformdefs.h"
43
#include "private/qabstractfileengine_p.h"
44
#include "private/qfsfileengine_p.h"
45
#include "private/qcore_unix_p.h"
46
#include "qfilesystementry_p.h"
47
#include "qfilesystemengine_p.h"
49
#ifndef QT_NO_FSFILEENGINE
53
#include "qdatetime.h"
54
#include "qvarlengtharray.h"
60
#if !defined(QWS) && defined(Q_OS_MAC)
61
# include <private/qcore_mac_p.h>
69
Returns the stdlib open string corresponding to a QIODevice::OpenMode.
71
static inline QByteArray openModeToFopenMode(QIODevice::OpenMode flags, const QFileSystemEntry &fileEntry,
72
QFileSystemMetaData &metaData)
75
if ((flags & QIODevice::ReadOnly) && !(flags & QIODevice::Truncate)) {
77
if (flags & QIODevice::WriteOnly) {
78
metaData.clearFlags(QFileSystemMetaData::FileType);
79
if (!fileEntry.isEmpty()
80
&& QFileSystemEngine::fillMetaData(fileEntry, metaData, QFileSystemMetaData::FileType)
81
&& metaData.isFile()) {
87
} else if (flags & QIODevice::WriteOnly) {
89
if (flags & QIODevice::ReadOnly)
92
if (flags & QIODevice::Append) {
94
if (flags & QIODevice::ReadOnly)
98
#if defined(__GLIBC__) && (__GLIBC__ * 0x100 + __GLIBC_MINOR__) >= 0x0207
99
// must be glibc >= 2.7
109
Returns the stdio open flags corresponding to a QIODevice::OpenMode.
111
static inline int openModeToOpenFlags(QIODevice::OpenMode mode)
113
int oflags = QT_OPEN_RDONLY;
114
#ifdef QT_LARGEFILE_SUPPORT
115
oflags |= QT_OPEN_LARGEFILE;
118
if ((mode & QFile::ReadWrite) == QFile::ReadWrite) {
119
oflags = QT_OPEN_RDWR | QT_OPEN_CREAT;
120
} else if (mode & QFile::WriteOnly) {
121
oflags = QT_OPEN_WRONLY | QT_OPEN_CREAT;
124
if (mode & QFile::Append) {
125
oflags |= QT_OPEN_APPEND;
126
} else if (mode & QFile::WriteOnly) {
127
if ((mode & QFile::Truncate) || !(mode & QFile::ReadOnly))
128
oflags |= QT_OPEN_TRUNC;
137
Sets the file descriptor to close on exec. That is, the file
138
descriptor is not inherited by child processes.
140
static inline bool setCloseOnExec(int fd)
142
return fd != -1 && fcntl(fd, F_SETFD, FD_CLOEXEC) != -1;
148
bool QFSFileEnginePrivate::nativeOpen(QIODevice::OpenMode openMode)
152
if (openMode & QIODevice::Unbuffered) {
153
int flags = openModeToOpenFlags(openMode);
155
// Try to open the file in unbuffered mode.
157
fd = QT_OPEN(fileEntry.nativeFilePath().constData(), flags, 0666);
158
} while (fd == -1 && errno == EINTR);
160
// On failure, return and report the error.
162
q->setError(errno == EMFILE ? QFile::ResourceError : QFile::OpenError,
163
qt_error_string(errno));
167
if (!(openMode & QIODevice::WriteOnly)) {
168
// we don't need this check if we tried to open for writing because then
169
// we had received EISDIR anyway.
170
if (QFileSystemEngine::fillMetaData(fd, metaData)
171
&& metaData.isDirectory()) {
172
q->setError(QFile::OpenError, QLatin1String("file to open is a directory"));
178
// Seek to the end when in Append mode.
179
if (flags & QFile::Append) {
182
ret = QT_LSEEK(fd, 0, SEEK_END);
183
} while (ret == -1 && errno == EINTR);
186
q->setError(errno == EMFILE ? QFile::ResourceError : QFile::OpenError,
187
qt_error_string(int(errno)));
194
QByteArray fopenMode = openModeToFopenMode(openMode, fileEntry, metaData);
196
// Try to open the file in buffered mode.
198
fh = QT_FOPEN(fileEntry.nativeFilePath().constData(), fopenMode.constData());
199
} while (!fh && errno == EINTR);
201
// On failure, return and report the error.
203
q->setError(errno == EMFILE ? QFile::ResourceError : QFile::OpenError,
204
qt_error_string(int(errno)));
208
if (!(openMode & QIODevice::WriteOnly)) {
209
// we don't need this check if we tried to open for writing because then
210
// we had received EISDIR anyway.
211
if (QFileSystemEngine::fillMetaData(QT_FILENO(fh), metaData)
212
&& metaData.isDirectory()) {
213
q->setError(QFile::OpenError, QLatin1String("file to open is a directory"));
219
setCloseOnExec(fileno(fh)); // ignore failure
221
// Seek to the end when in Append mode.
222
if (openMode & QIODevice::Append) {
225
ret = QT_FSEEK(fh, 0, SEEK_END);
226
} while (ret == -1 && errno == EINTR);
229
q->setError(errno == EMFILE ? QFile::ResourceError : QFile::OpenError,
230
qt_error_string(int(errno)));
238
closeFileHandle = true;
245
bool QFSFileEnginePrivate::nativeClose()
254
bool QFSFileEnginePrivate::nativeFlush()
256
return fh ? flushFh() : fd != -1;
262
qint64 QFSFileEnginePrivate::nativeRead(char *data, qint64 len)
266
if (fh && nativeIsSequential()) {
267
size_t readBytes = 0;
268
int oldFlags = fcntl(QT_FILENO(fh), F_GETFL);
269
for (int i = 0; i < 2; ++i) {
270
// Unix: Make the underlying file descriptor non-blocking
271
if ((oldFlags & O_NONBLOCK) == 0)
272
fcntl(QT_FILENO(fh), F_SETFL, oldFlags | O_NONBLOCK);
274
// Cross platform stdlib read
277
read = fread(data + readBytes, 1, size_t(len - readBytes), fh);
278
} while (read == 0 && !feof(fh) && errno == EINTR);
288
// Unix: Restore the blocking state of the underlying socket
289
if ((oldFlags & O_NONBLOCK) == 0) {
290
fcntl(QT_FILENO(fh), F_SETFL, oldFlags);
291
if (readBytes == 0) {
294
readByte = fgetc(fh);
295
} while (readByte == -1 && errno == EINTR);
296
if (readByte != -1) {
297
*data = uchar(readByte);
305
// Unix: Restore the blocking state of the underlying socket
306
if ((oldFlags & O_NONBLOCK) == 0) {
307
fcntl(QT_FILENO(fh), F_SETFL, oldFlags);
309
if (readBytes == 0 && !feof(fh)) {
310
// if we didn't read anything and we're not at EOF, it must be an error
311
q->setError(QFile::ReadError, qt_error_string(int(errno)));
317
return readFdFh(data, len);
323
qint64 QFSFileEnginePrivate::nativeReadLine(char *data, qint64 maxlen)
325
return readLineFdFh(data, maxlen);
331
qint64 QFSFileEnginePrivate::nativeWrite(const char *data, qint64 len)
333
return writeFdFh(data, len);
339
qint64 QFSFileEnginePrivate::nativePos() const
347
bool QFSFileEnginePrivate::nativeSeek(qint64 pos)
349
return seekFdFh(pos);
355
int QFSFileEnginePrivate::nativeHandle() const
357
return fh ? fileno(fh) : fd;
363
bool QFSFileEnginePrivate::nativeIsSequential() const
365
return isSequentialFdFh();
368
bool QFSFileEngine::remove()
372
bool ret = QFileSystemEngine::removeFile(d->fileEntry, error);
375
setError(QFile::RemoveError, error.toString());
380
bool QFSFileEngine::copy(const QString &newName)
384
bool ret = QFileSystemEngine::copyFile(d->fileEntry, QFileSystemEntry(newName), error);
386
setError(QFile::CopyError, error.toString());
391
bool QFSFileEngine::rename(const QString &newName)
395
bool ret = QFileSystemEngine::renameFile(d->fileEntry, QFileSystemEntry(newName), error);
398
setError(QFile::RenameError, error.toString());
404
bool QFSFileEngine::link(const QString &newName)
408
bool ret = QFileSystemEngine::createLink(d->fileEntry, QFileSystemEntry(newName), error);
410
setError(QFile::RenameError, error.toString());
415
qint64 QFSFileEnginePrivate::nativeSize() const
420
bool QFSFileEngine::mkdir(const QString &name, bool createParentDirectories) const
422
return QFileSystemEngine::createDirectory(QFileSystemEntry(name), createParentDirectories);
425
bool QFSFileEngine::rmdir(const QString &name, bool recurseParentDirectories) const
427
return QFileSystemEngine::removeDirectory(QFileSystemEntry(name), recurseParentDirectories);
430
bool QFSFileEngine::caseSensitive() const
435
bool QFSFileEngine::setCurrentPath(const QString &path)
437
return QFileSystemEngine::setCurrentPath(QFileSystemEntry(path));
440
QString QFSFileEngine::currentPath(const QString &)
442
return QFileSystemEngine::currentPath().filePath();
445
QString QFSFileEngine::homePath()
447
return QFileSystemEngine::homePath();
450
QString QFSFileEngine::rootPath()
452
return QFileSystemEngine::rootPath();
455
QString QFSFileEngine::tempPath()
457
return QFileSystemEngine::tempPath();
460
QFileInfoList QFSFileEngine::drives()
463
ret.append(QFileInfo(rootPath()));
467
bool QFSFileEnginePrivate::doStat(QFileSystemMetaData::MetaDataFlags flags) const
469
if (!tried_stat || !metaData.hasFlags(flags)) {
473
if (fh && fileEntry.isEmpty())
474
localFd = QT_FILENO(fh);
476
QFileSystemEngine::fillMetaData(localFd, metaData);
478
if (metaData.missingFlags(flags) && !fileEntry.isEmpty())
479
QFileSystemEngine::fillMetaData(fileEntry, metaData, metaData.missingFlags(flags));
482
return metaData.exists();
485
bool QFSFileEnginePrivate::isSymlink() const
487
if (!metaData.hasFlags(QFileSystemMetaData::LinkType))
488
QFileSystemEngine::fillMetaData(fileEntry, metaData, QFileSystemMetaData::LinkType);
490
return metaData.isLink();
496
QAbstractFileEngine::FileFlags QFSFileEngine::fileFlags(FileFlags type) const
498
Q_D(const QFSFileEngine);
503
QAbstractFileEngine::FileFlags ret = 0;
505
if (type & FlagsMask)
506
ret |= LocalDiskFlag;
510
QFileSystemMetaData::MetaDataFlags queryFlags = 0;
512
queryFlags |= QFileSystemMetaData::MetaDataFlags(uint(type))
513
& QFileSystemMetaData::Permissions;
515
if (type & TypesMask)
516
queryFlags |= QFileSystemMetaData::AliasType
517
| QFileSystemMetaData::LinkType
518
| QFileSystemMetaData::FileType
519
| QFileSystemMetaData::DirectoryType
520
| QFileSystemMetaData::BundleType;
522
if (type & FlagsMask)
523
queryFlags |= QFileSystemMetaData::HiddenAttribute
524
| QFileSystemMetaData::ExistsAttribute;
526
queryFlags |= QFileSystemMetaData::LinkType;
528
exists = d->doStat(queryFlags);
531
if (!exists && !d->metaData.isLink())
534
if (exists && (type & PermsMask))
535
ret |= FileFlags(uint(d->metaData.permissions()));
537
if (type & TypesMask) {
538
if (d->metaData.isAlias()) {
541
if ((type & LinkType) && d->metaData.isLink())
544
if (d->metaData.isFile()) {
546
} else if (d->metaData.isDirectory()) {
547
ret |= DirectoryType;
548
if ((type & BundleType) && d->metaData.isBundle())
555
if (type & FlagsMask) {
558
if (d->fileEntry.isRoot())
560
else if (d->metaData.isHidden())
567
QString QFSFileEngine::fileName(FileName file) const
569
Q_D(const QFSFileEngine);
570
if (file == BundleName) {
571
return QFileSystemEngine::bundleName(d->fileEntry);
572
} else if (file == BaseName) {
573
return d->fileEntry.fileName();
574
} else if (file == PathName) {
575
return d->fileEntry.path();
576
} else if (file == AbsoluteName || file == AbsolutePathName) {
577
QFileSystemEntry entry(QFileSystemEngine::absoluteName(d->fileEntry));
578
if (file == AbsolutePathName) {
581
return entry.filePath();
582
} else if (file == CanonicalName || file == CanonicalPathName) {
583
QFileSystemEntry entry(QFileSystemEngine::canonicalName(d->fileEntry, d->metaData));
584
if (file == CanonicalPathName)
586
return entry.filePath();
587
} else if (file == LinkName) {
588
if (d->isSymlink()) {
589
QFileSystemEntry entry = QFileSystemEngine::getLinkTarget(d->fileEntry, d->metaData);
590
return entry.filePath();
594
return d->fileEntry.filePath();
597
bool QFSFileEngine::isRelativePath() const
599
Q_D(const QFSFileEngine);
600
return d->fileEntry.filePath().length() ? d->fileEntry.filePath()[0] != QLatin1Char('/') : true;
603
uint QFSFileEngine::ownerId(FileOwner own) const
605
Q_D(const QFSFileEngine);
606
static const uint nobodyID = (uint) -2;
608
if (d->doStat(QFileSystemMetaData::OwnerIds))
609
return d->metaData.ownerId(own);
614
QString QFSFileEngine::owner(FileOwner own) const
616
if (own == OwnerUser)
617
return QFileSystemEngine::resolveUserName(ownerId(own));
618
return QFileSystemEngine::resolveGroupName(ownerId(own));
621
bool QFSFileEngine::setPermissions(uint perms)
625
if (!QFileSystemEngine::setPermissions(d->fileEntry, QFile::Permissions(perms), error, 0)) {
626
setError(QFile::PermissionsError, error.toString());
632
bool QFSFileEngine::setSize(qint64 size)
637
ret = QT_FTRUNCATE(d->fd, size) == 0;
639
ret = QT_FTRUNCATE(QT_FILENO(d->fh), size) == 0;
641
ret = QT_TRUNCATE(d->fileEntry.nativeFilePath().constData(), size) == 0;
643
setError(QFile::ResizeError, qt_error_string(errno));
647
QDateTime QFSFileEngine::fileTime(FileTime time) const
649
Q_D(const QFSFileEngine);
651
if (d->doStat(QFileSystemMetaData::Times))
652
return d->metaData.fileTime(time);
657
uchar *QFSFileEnginePrivate::map(qint64 offset, qint64 size, QFile::MemoryMapFlags flags)
661
if (openMode == QIODevice::NotOpen) {
662
q->setError(QFile::PermissionsError, qt_error_string(int(EACCES)));
666
if (offset < 0 || offset != qint64(QT_OFF_T(offset))
667
|| size < 0 || quint64(size) > quint64(size_t(-1))) {
668
q->setError(QFile::UnspecifiedError, qt_error_string(int(EINVAL)));
672
// If we know the mapping will extend beyond EOF, fail early to avoid
673
// undefined behavior. Otherwise, let mmap have its say.
674
if (doStat(QFileSystemMetaData::SizeAttribute)
675
&& (QT_OFF_T(size) > metaData.size() - QT_OFF_T(offset)))
676
qWarning("QFSFileEngine::map: Mapping a file beyond its size is not portable");
679
if (openMode & QIODevice::ReadOnly) access |= PROT_READ;
680
if (openMode & QIODevice::WriteOnly) access |= PROT_WRITE;
682
#if defined(Q_OS_INTEGRITY)
683
int pageSize = sysconf(_SC_PAGESIZE);
685
int pageSize = getpagesize();
687
int extra = offset % pageSize;
689
if (quint64(size + extra) > quint64((size_t)-1)) {
690
q->setError(QFile::UnspecifiedError, qt_error_string(int(EINVAL)));
694
size_t realSize = (size_t)size + extra;
695
QT_OFF_T realOffset = QT_OFF_T(offset);
696
realOffset &= ~(QT_OFF_T(pageSize - 1));
698
void *mapAddress = QT_MMAP((void*)0, realSize,
699
access, MAP_SHARED, nativeHandle(), realOffset);
700
if (MAP_FAILED != mapAddress) {
701
uchar *address = extra + static_cast<uchar*>(mapAddress);
702
maps[address] = QPair<int,size_t>(extra, realSize);
708
q->setError(QFile::PermissionsError, qt_error_string(int(EACCES)));
712
q->setError(QFile::ResourceError, qt_error_string(int(errno)));
715
// size are out of bounds
717
q->setError(QFile::UnspecifiedError, qt_error_string(int(errno)));
723
bool QFSFileEnginePrivate::unmap(uchar *ptr)
725
#if !defined(Q_OS_INTEGRITY)
727
if (!maps.contains(ptr)) {
728
q->setError(QFile::PermissionsError, qt_error_string(EACCES));
732
uchar *start = ptr - maps[ptr].first;
733
size_t len = maps[ptr].second;
734
if (-1 == munmap(start, len)) {
735
q->setError(QFile::UnspecifiedError, qt_error_string(errno));
747
#endif // QT_NO_FSFILEENGINE