summaryrefslogtreecommitdiffstats
path: root/src/corelib/io/qfilesystemengine_unix.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/corelib/io/qfilesystemengine_unix.cpp')
-rw-r--r--src/corelib/io/qfilesystemengine_unix.cpp948
1 files changed, 856 insertions, 92 deletions
diff --git a/src/corelib/io/qfilesystemengine_unix.cpp b/src/corelib/io/qfilesystemengine_unix.cpp
index 7fed54f733..b974af80dc 100644
--- a/src/corelib/io/qfilesystemengine_unix.cpp
+++ b/src/corelib/io/qfilesystemengine_unix.cpp
@@ -43,6 +43,8 @@
#include "qfilesystemengine_p.h"
#include "qfile.h"
+#include <QtCore/qoperatingsystemversion.h>
+#include <QtCore/private/qcore_unix_p.h>
#include <QtCore/qvarlengtharray.h>
#include <pwd.h>
@@ -53,6 +55,12 @@
#include <stdio.h>
#include <errno.h>
+#if QT_HAS_INCLUDE(<paths.h>)
+# include <paths.h>
+#endif
+#ifndef _PATH_TMP // from <paths.h>
+# define _PATH_TMP "/tmp"
+#endif
#if defined(Q_OS_MAC)
# include <QtCore/private/qcore_mac_p.h>
@@ -68,14 +76,62 @@
#endif
#if defined(Q_OS_DARWIN)
+# if QT_DARWIN_PLATFORM_SDK_EQUAL_OR_ABOVE(101200, 100000, 100000, 30000)
+# include <sys/clonefile.h>
+# endif
+# include <copyfile.h>
// We cannot include <Foundation/Foundation.h> (it's an Objective-C header), but
// we need these declarations:
Q_FORWARD_DECLARE_OBJC_CLASS(NSString);
extern "C" NSString *NSTemporaryDirectory();
#endif
+#if defined(Q_OS_LINUX)
+# include <sys/ioctl.h>
+# include <sys/syscall.h>
+# include <sys/sendfile.h>
+# include <linux/fs.h>
+
+// in case linux/fs.h is too old and doesn't define it:
+#ifndef FICLONE
+# define FICLONE _IOW(0x94, 9, int)
+#endif
+
+# if !QT_CONFIG(renameat2) && defined(SYS_renameat2)
+static int renameat2(int oldfd, const char *oldpath, int newfd, const char *newpath, unsigned flags)
+{ return syscall(SYS_renameat2, oldfd, oldpath, newfd, newpath, flags); }
+# endif
+
+# if !QT_CONFIG(statx) && defined(SYS_statx) && QT_HAS_INCLUDE(<linux/stat.h>)
+# include <linux/stat.h>
+static int statx(int dirfd, const char *pathname, int flag, unsigned mask, struct statx *statxbuf)
+{ return syscall(SYS_statx, dirfd, pathname, flag, mask, statxbuf); }
+# endif
+#endif
+
+#ifndef STATX_BASIC_STATS
+struct statx { mode_t stx_mode; };
+#endif
+
QT_BEGIN_NAMESPACE
+enum {
+#ifdef Q_OS_ANDROID
+ // On Android, the link(2) system call has been observed to always fail
+ // with EACCES, regardless of whether there are permission problems or not.
+ SupportsHardlinking = false
+#else
+ SupportsHardlinking = true
+#endif
+};
+
+#define emptyFileEntryWarning() emptyFileEntryWarning_(QT_MESSAGELOG_FILE, QT_MESSAGELOG_LINE, QT_MESSAGELOG_FUNC)
+static void emptyFileEntryWarning_(const char *file, int line, const char *function)
+{
+ QMessageLogger(file, line, function).warning("Empty filename passed to function");
+ errno = EINVAL;
+}
+
#if defined(Q_OS_DARWIN)
static inline bool hasResourcePropertyFlag(const QFileSystemMetaData &data,
const QFileSystemEntry &entry,
@@ -144,33 +200,457 @@ static bool isPackage(const QFileSystemMetaData &data, const QFileSystemEntry &e
}
#endif
+namespace {
+namespace GetFileTimes {
+#if !QT_CONFIG(futimens) && (QT_CONFIG(futimes))
+template <typename T>
+static inline typename QtPrivate::QEnableIf<(&T::st_atim, &T::st_mtim, true)>::Type get(const T *p, struct timeval *access, struct timeval *modification)
+{
+ access->tv_sec = p->st_atim.tv_sec;
+ access->tv_usec = p->st_atim.tv_nsec / 1000;
+
+ modification->tv_sec = p->st_mtim.tv_sec;
+ modification->tv_usec = p->st_mtim.tv_nsec / 1000;
+}
+
+template <typename T>
+static inline typename QtPrivate::QEnableIf<(&T::st_atimespec, &T::st_mtimespec, true)>::Type get(const T *p, struct timeval *access, struct timeval *modification)
+{
+ access->tv_sec = p->st_atimespec.tv_sec;
+ access->tv_usec = p->st_atimespec.tv_nsec / 1000;
+
+ modification->tv_sec = p->st_mtimespec.tv_sec;
+ modification->tv_usec = p->st_mtimespec.tv_nsec / 1000;
+}
+
+# ifndef st_atimensec
+// if "st_atimensec" is defined, this would expand to invalid C++
+template <typename T>
+static inline typename QtPrivate::QEnableIf<(&T::st_atimensec, &T::st_mtimensec, true)>::Type get(const T *p, struct timeval *access, struct timeval *modification)
+{
+ access->tv_sec = p->st_atime;
+ access->tv_usec = p->st_atimensec / 1000;
+
+ modification->tv_sec = p->st_mtime;
+ modification->tv_usec = p->st_mtimensec / 1000;
+}
+# endif
+#endif
+
+qint64 timespecToMSecs(const timespec &spec)
+{
+ return (qint64(spec.tv_sec) * 1000) + (spec.tv_nsec / 1000000);
+}
+
+// fallback set
+Q_DECL_UNUSED qint64 atime(const QT_STATBUF &statBuffer, ulong) { return qint64(statBuffer.st_atime) * 1000; }
+Q_DECL_UNUSED qint64 birthtime(const QT_STATBUF &, ulong) { return Q_INT64_C(0); }
+Q_DECL_UNUSED qint64 ctime(const QT_STATBUF &statBuffer, ulong) { return qint64(statBuffer.st_ctime) * 1000; }
+Q_DECL_UNUSED qint64 mtime(const QT_STATBUF &statBuffer, ulong) { return qint64(statBuffer.st_mtime) * 1000; }
+
+// Xtim, POSIX.1-2008
+template <typename T>
+Q_DECL_UNUSED static typename std::enable_if<(&T::st_atim, true), qint64>::type
+atime(const T &statBuffer, int)
+{ return timespecToMSecs(statBuffer.st_atim); }
+
+template <typename T>
+Q_DECL_UNUSED static typename std::enable_if<(&T::st_birthtim, true), qint64>::type
+birthtime(const T &statBuffer, int)
+{ return timespecToMSecs(statBuffer.st_birthtim); }
+
+template <typename T>
+Q_DECL_UNUSED static typename std::enable_if<(&T::st_ctim, true), qint64>::type
+ctime(const T &statBuffer, int)
+{ return timespecToMSecs(statBuffer.st_ctim); }
+
+template <typename T>
+Q_DECL_UNUSED static typename std::enable_if<(&T::st_mtim, true), qint64>::type
+mtime(const T &statBuffer, int)
+{ return timespecToMSecs(statBuffer.st_mtim); }
+
+#ifndef st_mtimespec
+// Xtimespec
+template <typename T>
+Q_DECL_UNUSED static typename std::enable_if<(&T::st_atimespec, true), qint64>::type
+atime(const T &statBuffer, int)
+{ return timespecToMSecs(statBuffer.st_atimespec); }
+
+template <typename T>
+Q_DECL_UNUSED static typename std::enable_if<(&T::st_birthtimespec, true), qint64>::type
+birthtime(const T &statBuffer, int)
+{ return timespecToMSecs(statBuffer.st_birthtimespec); }
+
+template <typename T>
+Q_DECL_UNUSED static typename std::enable_if<(&T::st_ctimespec, true), qint64>::type
+ctime(const T &statBuffer, int)
+{ return timespecToMSecs(statBuffer.st_ctimespec); }
+
+template <typename T>
+Q_DECL_UNUSED static typename std::enable_if<(&T::st_mtimespec, true), qint64>::type
+mtime(const T &statBuffer, int)
+{ return timespecToMSecs(statBuffer.st_mtimespec); }
+#endif
+
+#ifndef st_mtimensec
+// Xtimensec
+template <typename T>
+Q_DECL_UNUSED static typename std::enable_if<(&T::st_atimensec, true), qint64>::type
+atime(const T &statBuffer, int)
+{ return statBuffer.st_atime * Q_INT64_C(1000) + statBuffer.st_atimensec / 1000000; }
+
+template <typename T>
+Q_DECL_UNUSED static typename std::enable_if<(&T::st_birthtimensec, true), qint64>::type
+birthtime(const T &statBuffer, int)
+{ return statBuffer.st_birthtime * Q_INT64_C(1000) + statBuffer.st_birthtimensec / 1000000; }
+
+template <typename T>
+Q_DECL_UNUSED static typename std::enable_if<(&T::st_ctimensec, true), qint64>::type
+ctime(const T &statBuffer, int)
+{ return statBuffer.st_ctime * Q_INT64_C(1000) + statBuffer.st_ctimensec / 1000000; }
+
+template <typename T>
+Q_DECL_UNUSED static typename std::enable_if<(&T::st_mtimensec, true), qint64>::type
+mtime(const T &statBuffer, int)
+{ return statBuffer.st_mtime * Q_INT64_C(1000) + statBuffer.st_mtimensec / 1000000; }
+#endif
+} // namespace GetFileTimes
+} // unnamed namespace
+
+#ifdef STATX_BASIC_STATS
+static int qt_real_statx(int fd, const char *pathname, int flags, struct statx *statxBuffer)
+{
+#ifdef Q_ATOMIC_INT8_IS_SUPPORTED
+ static QBasicAtomicInteger<qint8> statxTested = Q_BASIC_ATOMIC_INITIALIZER(0);
+#else
+ static QBasicAtomicInt statxTested = Q_BASIC_ATOMIC_INITIALIZER(0);
+#endif
+
+ if (statxTested.load() == -1)
+ return -ENOSYS;
+
+ unsigned mask = STATX_BASIC_STATS | STATX_BTIME;
+ int ret = statx(fd, pathname, flags, mask, statxBuffer);
+ if (ret == -1 && errno == ENOSYS) {
+ statxTested.store(-1);
+ return -ENOSYS;
+ }
+ statxTested.store(1);
+ return ret == -1 ? -errno : 0;
+}
+
+static int qt_statx(const char *pathname, struct statx *statxBuffer)
+{
+ return qt_real_statx(AT_FDCWD, pathname, 0, statxBuffer);
+}
+
+static int qt_lstatx(const char *pathname, struct statx *statxBuffer)
+{
+ return qt_real_statx(AT_FDCWD, pathname, AT_SYMLINK_NOFOLLOW, statxBuffer);
+}
+
+static int qt_fstatx(int fd, struct statx *statxBuffer)
+{
+ return qt_real_statx(fd, "", AT_EMPTY_PATH, statxBuffer);
+}
+
+inline void QFileSystemMetaData::fillFromStatxBuf(const struct statx &statxBuffer)
+{
+ // Permissions
+ if (statxBuffer.stx_mode & S_IRUSR)
+ entryFlags |= QFileSystemMetaData::OwnerReadPermission;
+ if (statxBuffer.stx_mode & S_IWUSR)
+ entryFlags |= QFileSystemMetaData::OwnerWritePermission;
+ if (statxBuffer.stx_mode & S_IXUSR)
+ entryFlags |= QFileSystemMetaData::OwnerExecutePermission;
+
+ if (statxBuffer.stx_mode & S_IRGRP)
+ entryFlags |= QFileSystemMetaData::GroupReadPermission;
+ if (statxBuffer.stx_mode & S_IWGRP)
+ entryFlags |= QFileSystemMetaData::GroupWritePermission;
+ if (statxBuffer.stx_mode & S_IXGRP)
+ entryFlags |= QFileSystemMetaData::GroupExecutePermission;
+
+ if (statxBuffer.stx_mode & S_IROTH)
+ entryFlags |= QFileSystemMetaData::OtherReadPermission;
+ if (statxBuffer.stx_mode & S_IWOTH)
+ entryFlags |= QFileSystemMetaData::OtherWritePermission;
+ if (statxBuffer.stx_mode & S_IXOTH)
+ entryFlags |= QFileSystemMetaData::OtherExecutePermission;
+
+ // Type
+ if (S_ISLNK(statxBuffer.stx_mode))
+ entryFlags |= QFileSystemMetaData::LinkType;
+ if ((statxBuffer.stx_mode & S_IFMT) == S_IFREG)
+ entryFlags |= QFileSystemMetaData::FileType;
+ else if ((statxBuffer.stx_mode & S_IFMT) == S_IFDIR)
+ entryFlags |= QFileSystemMetaData::DirectoryType;
+ else if ((statxBuffer.stx_mode & S_IFMT) != S_IFBLK)
+ entryFlags |= QFileSystemMetaData::SequentialType;
+
+ // Attributes
+ entryFlags |= QFileSystemMetaData::ExistsAttribute; // inode exists
+ if (statxBuffer.stx_nlink == 0)
+ entryFlags |= QFileSystemMetaData::WasDeletedAttribute;
+ size_ = qint64(statxBuffer.stx_size);
+
+ // Times
+ auto toMSecs = [](struct statx_timestamp ts)
+ {
+ return qint64(ts.tv_sec) * 1000 + (ts.tv_nsec / 1000000);
+ };
+ accessTime_ = toMSecs(statxBuffer.stx_atime);
+ metadataChangeTime_ = toMSecs(statxBuffer.stx_ctime);
+ modificationTime_ = toMSecs(statxBuffer.stx_mtime);
+ if (statxBuffer.stx_mask & STATX_BTIME)
+ birthTime_ = toMSecs(statxBuffer.stx_btime);
+ else
+ birthTime_ = 0;
+
+ userId_ = statxBuffer.stx_uid;
+ groupId_ = statxBuffer.stx_gid;
+}
+#else
+static int qt_statx(const char *, struct statx *)
+{ return -ENOSYS; }
+
+static int qt_lstatx(const char *, struct statx *)
+{ return -ENOSYS; }
+
+static int qt_fstatx(int, struct statx *)
+{ return -ENOSYS; }
+
+inline void QFileSystemMetaData::fillFromStatxBuf(const struct statx &)
+{ }
+#endif
+
//static
-QFileSystemEntry QFileSystemEngine::getLinkTarget(const QFileSystemEntry &link, QFileSystemMetaData &data)
+bool QFileSystemEngine::fillMetaData(int fd, QFileSystemMetaData &data)
{
-#if defined(__GLIBC__) && !defined(PATH_MAX)
-#define PATH_CHUNK_SIZE 256
- char *s = 0;
- int len = -1;
- int size = PATH_CHUNK_SIZE;
-
- while (1) {
- s = (char *) ::realloc(s, size);
- Q_CHECK_PTR(s);
- len = ::readlink(link.nativeFilePath().constData(), s, size);
- if (len < 0) {
- ::free(s);
- break;
+ data.entryFlags &= ~QFileSystemMetaData::PosixStatFlags;
+ data.knownFlagsMask |= QFileSystemMetaData::PosixStatFlags;
+
+ union {
+ struct statx statxBuffer;
+ QT_STATBUF statBuffer;
+ };
+
+ int ret = qt_fstatx(fd, &statxBuffer);
+ if (ret != -ENOSYS) {
+ if (ret == 0) {
+ data.fillFromStatxBuf(statxBuffer);
+ return true;
}
- if (len < size) {
- break;
+ return false;
+ }
+
+ if (QT_FSTAT(fd, &statBuffer) == 0) {
+ data.fillFromStatBuf(statBuffer);
+ return true;
+ }
+
+ return false;
+}
+
+#if defined(_DEXTRA_FIRST)
+static void fillStat64fromStat32(struct stat64 *statBuf64, const struct stat &statBuf32)
+{
+ statBuf64->st_mode = statBuf32.st_mode;
+ statBuf64->st_size = statBuf32.st_size;
+#if _POSIX_VERSION >= 200809L
+ statBuf64->st_ctim = statBuf32.st_ctim;
+ statBuf64->st_mtim = statBuf32.st_mtim;
+ statBuf64->st_atim = statBuf32.st_atim;
+#else
+ statBuf64->st_ctime = statBuf32.st_ctime;
+ statBuf64->st_mtime = statBuf32.st_mtime;
+ statBuf64->st_atime = statBuf32.st_atime;
+#endif
+ statBuf64->st_uid = statBuf32.st_uid;
+ statBuf64->st_gid = statBuf32.st_gid;
+}
+#endif
+
+void QFileSystemMetaData::fillFromStatBuf(const QT_STATBUF &statBuffer)
+{
+ // Permissions
+ if (statBuffer.st_mode & S_IRUSR)
+ entryFlags |= QFileSystemMetaData::OwnerReadPermission;
+ if (statBuffer.st_mode & S_IWUSR)
+ entryFlags |= QFileSystemMetaData::OwnerWritePermission;
+ if (statBuffer.st_mode & S_IXUSR)
+ entryFlags |= QFileSystemMetaData::OwnerExecutePermission;
+
+ if (statBuffer.st_mode & S_IRGRP)
+ entryFlags |= QFileSystemMetaData::GroupReadPermission;
+ if (statBuffer.st_mode & S_IWGRP)
+ entryFlags |= QFileSystemMetaData::GroupWritePermission;
+ if (statBuffer.st_mode & S_IXGRP)
+ entryFlags |= QFileSystemMetaData::GroupExecutePermission;
+
+ if (statBuffer.st_mode & S_IROTH)
+ entryFlags |= QFileSystemMetaData::OtherReadPermission;
+ if (statBuffer.st_mode & S_IWOTH)
+ entryFlags |= QFileSystemMetaData::OtherWritePermission;
+ if (statBuffer.st_mode & S_IXOTH)
+ entryFlags |= QFileSystemMetaData::OtherExecutePermission;
+
+ // Type
+ if ((statBuffer.st_mode & S_IFMT) == S_IFREG)
+ entryFlags |= QFileSystemMetaData::FileType;
+ else if ((statBuffer.st_mode & S_IFMT) == S_IFDIR)
+ entryFlags |= QFileSystemMetaData::DirectoryType;
+ else if ((statBuffer.st_mode & S_IFMT) != S_IFBLK)
+ entryFlags |= QFileSystemMetaData::SequentialType;
+
+ // Attributes
+ entryFlags |= QFileSystemMetaData::ExistsAttribute; // inode exists
+ if (statBuffer.st_nlink == 0)
+ entryFlags |= QFileSystemMetaData::WasDeletedAttribute;
+ size_ = statBuffer.st_size;
+#ifdef UF_HIDDEN
+ if (statBuffer.st_flags & UF_HIDDEN) {
+ entryFlags |= QFileSystemMetaData::HiddenAttribute;
+ knownFlagsMask |= QFileSystemMetaData::HiddenAttribute;
+ }
+#endif
+
+ // Times
+ accessTime_ = GetFileTimes::atime(statBuffer, 0);
+ birthTime_ = GetFileTimes::birthtime(statBuffer, 0);
+ metadataChangeTime_ = GetFileTimes::ctime(statBuffer, 0);
+ modificationTime_ = GetFileTimes::mtime(statBuffer, 0);
+
+ userId_ = statBuffer.st_uid;
+ groupId_ = statBuffer.st_gid;
+}
+
+void QFileSystemMetaData::fillFromDirEnt(const QT_DIRENT &entry)
+{
+#if defined(_DEXTRA_FIRST)
+ knownFlagsMask = 0;
+ entryFlags = 0;
+ for (dirent_extra *extra = _DEXTRA_FIRST(&entry); _DEXTRA_VALID(extra, &entry);
+ extra = _DEXTRA_NEXT(extra)) {
+ if (extra->d_type == _DTYPE_STAT || extra->d_type == _DTYPE_LSTAT) {
+
+ const struct dirent_extra_stat * const extra_stat =
+ reinterpret_cast<struct dirent_extra_stat *>(extra);
+
+ // Remember whether this was a link or not, this saves an lstat() call later.
+ if (extra->d_type == _DTYPE_LSTAT) {
+ knownFlagsMask |= QFileSystemMetaData::LinkType;
+ if (S_ISLNK(extra_stat->d_stat.st_mode))
+ entryFlags |= QFileSystemMetaData::LinkType;
+ }
+
+ // For symlinks, the extra type _DTYPE_LSTAT doesn't work for filling out the meta data,
+ // as we need the stat() information there, not the lstat() information.
+ // In this case, don't use the extra information.
+ // Unfortunately, readdir() never seems to return extra info of type _DTYPE_STAT, so for
+ // symlinks, we always incur the cost of an extra stat() call later.
+ if (S_ISLNK(extra_stat->d_stat.st_mode) && extra->d_type == _DTYPE_LSTAT)
+ continue;
+
+#if defined(QT_USE_XOPEN_LFS_EXTENSIONS) && defined(QT_LARGEFILE_SUPPORT)
+ // Even with large file support, d_stat is always of type struct stat, not struct stat64,
+ // so it needs to be converted
+ struct stat64 statBuf;
+ fillStat64fromStat32(&statBuf, extra_stat->d_stat);
+ fillFromStatBuf(statBuf);
+#else
+ fillFromStatBuf(extra_stat->d_stat);
+#endif
+ knownFlagsMask |= QFileSystemMetaData::PosixStatFlags;
+ if (!S_ISLNK(extra_stat->d_stat.st_mode)) {
+ knownFlagsMask |= QFileSystemMetaData::ExistsAttribute;
+ entryFlags |= QFileSystemMetaData::ExistsAttribute;
+ }
}
- size *= 2;
+ }
+#elif defined(_DIRENT_HAVE_D_TYPE) || defined(Q_OS_BSD4)
+ // BSD4 includes OS X and iOS
+
+ // ### This will clear all entry flags and knownFlagsMask
+ switch (entry.d_type)
+ {
+ case DT_DIR:
+ knownFlagsMask = QFileSystemMetaData::LinkType
+ | QFileSystemMetaData::FileType
+ | QFileSystemMetaData::DirectoryType
+ | QFileSystemMetaData::SequentialType
+ | QFileSystemMetaData::ExistsAttribute;
+
+ entryFlags = QFileSystemMetaData::DirectoryType
+ | QFileSystemMetaData::ExistsAttribute;
+
+ break;
+
+ case DT_BLK:
+ knownFlagsMask = QFileSystemMetaData::LinkType
+ | QFileSystemMetaData::FileType
+ | QFileSystemMetaData::DirectoryType
+ | QFileSystemMetaData::BundleType
+ | QFileSystemMetaData::AliasType
+ | QFileSystemMetaData::SequentialType
+ | QFileSystemMetaData::ExistsAttribute;
+
+ entryFlags = QFileSystemMetaData::ExistsAttribute;
+
+ break;
+
+ case DT_CHR:
+ case DT_FIFO:
+ case DT_SOCK:
+ // ### System attribute
+ knownFlagsMask = QFileSystemMetaData::LinkType
+ | QFileSystemMetaData::FileType
+ | QFileSystemMetaData::DirectoryType
+ | QFileSystemMetaData::BundleType
+ | QFileSystemMetaData::AliasType
+ | QFileSystemMetaData::SequentialType
+ | QFileSystemMetaData::ExistsAttribute;
+
+ entryFlags = QFileSystemMetaData::SequentialType
+ | QFileSystemMetaData::ExistsAttribute;
+
+ break;
+
+ case DT_LNK:
+ knownFlagsMask = QFileSystemMetaData::LinkType;
+ entryFlags = QFileSystemMetaData::LinkType;
+ break;
+
+ case DT_REG:
+ knownFlagsMask = QFileSystemMetaData::LinkType
+ | QFileSystemMetaData::FileType
+ | QFileSystemMetaData::DirectoryType
+ | QFileSystemMetaData::BundleType
+ | QFileSystemMetaData::SequentialType
+ | QFileSystemMetaData::ExistsAttribute;
+
+ entryFlags = QFileSystemMetaData::FileType
+ | QFileSystemMetaData::ExistsAttribute;
+
+ break;
+
+ case DT_UNKNOWN:
+ default:
+ clear();
}
#else
- char s[PATH_MAX+1];
- int len = readlink(link.nativeFilePath().constData(), s, PATH_MAX);
+ Q_UNUSED(entry)
#endif
- if (len > 0) {
+}
+
+//static
+QFileSystemEntry QFileSystemEngine::getLinkTarget(const QFileSystemEntry &link, QFileSystemMetaData &data)
+{
+ if (Q_UNLIKELY(link.isEmpty()))
+ return emptyFileEntryWarning(), link;
+
+ QByteArray s = qt_readlink(link.nativeFilePath().constData());
+ if (s.length() > 0) {
QString ret;
if (!data.hasFlags(QFileSystemMetaData::DirectoryType))
fillMetaData(link, data, QFileSystemMetaData::DirectoryType);
@@ -181,19 +661,10 @@ QFileSystemEntry QFileSystemEngine::getLinkTarget(const QFileSystemEntry &link,
if (!ret.isEmpty() && !ret.endsWith(QLatin1Char('/')))
ret += QLatin1Char('/');
}
- s[len] = '\0';
- ret += QFile::decodeName(QByteArray(s));
-#if defined(__GLIBC__) && !defined(PATH_MAX)
- ::free(s);
-#endif
+ ret += QFile::decodeName(s);
- if (!ret.startsWith(QLatin1Char('/'))) {
- const QString linkPath = link.path();
- if (linkPath.startsWith(QLatin1Char('/')))
- ret.prepend(linkPath + QLatin1Char('/'));
- else
- ret.prepend(QDir::currentPath() + QLatin1Char('/') + linkPath + QLatin1Char('/'));
- }
+ if (!ret.startsWith(QLatin1Char('/')))
+ ret.prepend(absoluteName(link).path() + QLatin1Char('/'));
ret = QDir::cleanPath(ret);
if (ret.size() > 1 && ret.endsWith(QLatin1Char('/')))
ret.chop(1);
@@ -235,7 +706,9 @@ QFileSystemEntry QFileSystemEngine::getLinkTarget(const QFileSystemEntry &link,
//static
QFileSystemEntry QFileSystemEngine::canonicalName(const QFileSystemEntry &entry, QFileSystemMetaData &data)
{
- if (entry.isEmpty() || entry.isRoot())
+ if (Q_UNLIKELY(entry.isEmpty()))
+ return emptyFileEntryWarning(), entry;
+ if (entry.isRoot())
return entry;
#if !defined(Q_OS_MAC) && !defined(Q_OS_QNX) && !defined(Q_OS_ANDROID) && !defined(Q_OS_HAIKU) && _POSIX_VERSION < 200809L
@@ -302,6 +775,8 @@ QFileSystemEntry QFileSystemEngine::canonicalName(const QFileSystemEntry &entry,
//static
QFileSystemEntry QFileSystemEngine::absoluteName(const QFileSystemEntry &entry)
{
+ if (Q_UNLIKELY(entry.isEmpty()))
+ return emptyFileEntryWarning(), entry;
if (entry.isAbsolute() && entry.isClean())
return entry;
@@ -335,6 +810,9 @@ QFileSystemEntry QFileSystemEngine::absoluteName(const QFileSystemEntry &entry)
//static
QByteArray QFileSystemEngine::id(const QFileSystemEntry &entry)
{
+ if (Q_UNLIKELY(entry.isEmpty()))
+ return emptyFileEntryWarning(), QByteArray();
+
QT_STATBUF statResult;
if (QT_STAT(entry.nativeFilePath().constData(), &statResult)) {
if (errno != ENOENT)
@@ -442,56 +920,105 @@ QString QFileSystemEngine::bundleName(const QFileSystemEntry &entry)
bool QFileSystemEngine::fillMetaData(const QFileSystemEntry &entry, QFileSystemMetaData &data,
QFileSystemMetaData::MetaDataFlags what)
{
+ if (Q_UNLIKELY(entry.isEmpty()))
+ return emptyFileEntryWarning(), false;
+
#if defined(Q_OS_DARWIN)
if (what & QFileSystemMetaData::BundleType) {
if (!data.hasFlags(QFileSystemMetaData::DirectoryType))
what |= QFileSystemMetaData::DirectoryType;
}
+#endif
+#ifdef UF_HIDDEN
if (what & QFileSystemMetaData::HiddenAttribute) {
// OS X >= 10.5: st_flags & UF_HIDDEN
what |= QFileSystemMetaData::PosixStatFlags;
}
#endif // defined(Q_OS_DARWIN)
+ // if we're asking for any of the stat(2) flags, then we're getting them all
if (what & QFileSystemMetaData::PosixStatFlags)
what |= QFileSystemMetaData::PosixStatFlags;
- if (what & QFileSystemMetaData::ExistsAttribute) {
- // FIXME: Would other queries being performed provide this bit?
- what |= QFileSystemMetaData::PosixStatFlags;
- }
-
data.entryFlags &= ~what;
const QByteArray nativeFilePath = entry.nativeFilePath();
- bool entryExists = true; // innocent until proven otherwise
-
- QT_STATBUF statBuffer;
- bool statBufferValid = false;
+ int entryErrno = 0; // innocent until proven otherwise
+
+ // first, we may try lstat(2). Possible outcomes:
+ // - success and is a symlink: filesystem entry exists, but we need stat(2)
+ // -> statResult = -1;
+ // - success and is not a symlink: filesystem entry exists and we're done
+ // -> statResult = 0
+ // - failure: really non-existent filesystem entry
+ // -> entryExists = false; statResult = 0;
+ // both stat(2) and lstat(2) may generate a number of different errno
+ // conditions, but of those, the only ones that could happen and the
+ // entry still exist are EACCES, EFAULT, ENOMEM and EOVERFLOW. If we get
+ // EACCES or ENOMEM, then we have no choice on how to proceed, so we may
+ // as well conclude it doesn't exist; EFAULT can't happen and EOVERFLOW
+ // shouldn't happen because we build in _LARGEFIE64.
+ union {
+ QT_STATBUF statBuffer;
+ struct statx statxBuffer;
+ };
+ int statResult = -1;
if (what & QFileSystemMetaData::LinkType) {
- if (QT_LSTAT(nativeFilePath, &statBuffer) == 0) {
- if (S_ISLNK(statBuffer.st_mode)) {
+ mode_t mode = 0;
+ statResult = qt_lstatx(nativeFilePath, &statxBuffer);
+ if (statResult == -ENOSYS) {
+ // use lstst(2)
+ statResult = QT_LSTAT(nativeFilePath, &statBuffer);
+ if (statResult == 0)
+ mode = statBuffer.st_mode;
+ } else if (statResult == 0) {
+ statResult = 1; // record it was statx(2) that succeeded
+ mode = statxBuffer.stx_mode;
+ }
+
+ if (statResult >= 0) {
+ if (S_ISLNK(mode)) {
+ // it's a symlink, we don't know if the file "exists"
data.entryFlags |= QFileSystemMetaData::LinkType;
+ statResult = -1; // force stat(2) below
} else {
- statBufferValid = true;
- data.entryFlags &= ~QFileSystemMetaData::PosixStatFlags;
+ // it's a reagular file and it exists
+ if (statResult)
+ data.fillFromStatxBuf(statxBuffer);
+ else
+ data.fillFromStatBuf(statBuffer);
+ data.knownFlagsMask |= QFileSystemMetaData::PosixStatFlags
+ | QFileSystemMetaData::ExistsAttribute;
+ data.entryFlags |= QFileSystemMetaData::ExistsAttribute;
}
} else {
- entryExists = false;
+ // it doesn't exist
+ entryErrno = errno;
+ data.knownFlagsMask |= QFileSystemMetaData::ExistsAttribute;
}
data.knownFlagsMask |= QFileSystemMetaData::LinkType;
}
- if (statBufferValid || (what & QFileSystemMetaData::PosixStatFlags)) {
- if (entryExists && !statBufferValid)
- statBufferValid = (QT_STAT(nativeFilePath, &statBuffer) == 0);
+ // second, we try a regular stat(2)
+ if (statResult == -1 && (what & QFileSystemMetaData::PosixStatFlags)) {
+ if (entryErrno == 0 && statResult == -1) {
+ data.entryFlags &= ~QFileSystemMetaData::PosixStatFlags;
+ statResult = qt_statx(nativeFilePath, &statxBuffer);
+ if (statResult == -ENOSYS) {
+ // use stat(2)
+ statResult = QT_STAT(nativeFilePath, &statBuffer);
+ if (statResult == 0)
+ data.fillFromStatBuf(statBuffer);
+ } else if (statResult == 0) {
+ data.fillFromStatxBuf(statxBuffer);
+ }
+ }
- if (statBufferValid)
- data.fillFromStatBuf(statBuffer);
- else {
- entryExists = false;
- data.creationTime_ = 0;
+ if (statResult != 0) {
+ entryErrno = errno;
+ data.birthTime_ = 0;
+ data.metadataChangeTime_ = 0;
data.modificationTime_ = 0;
data.accessTime_ = 0;
data.size_ = 0;
@@ -504,60 +1031,135 @@ bool QFileSystemEngine::fillMetaData(const QFileSystemEntry &entry, QFileSystemM
| QFileSystemMetaData::ExistsAttribute;
}
+ // third, we try access(2)
+ if (what & (QFileSystemMetaData::UserPermissions | QFileSystemMetaData::ExistsAttribute)) {
+ // calculate user permissions
+ auto checkAccess = [&](QFileSystemMetaData::MetaDataFlag flag, int mode) {
+ if (entryErrno != 0 || (what & flag) == 0)
+ return;
+ if (QT_ACCESS(nativeFilePath, mode) == 0) {
+ // access ok (and file exists)
+ data.entryFlags |= flag | QFileSystemMetaData::ExistsAttribute;
+ } else if (errno != EACCES && errno != EROFS) {
+ entryErrno = errno;
+ }
+ };
+
+ checkAccess(QFileSystemMetaData::UserReadPermission, R_OK);
+ checkAccess(QFileSystemMetaData::UserWritePermission, W_OK);
+ checkAccess(QFileSystemMetaData::UserExecutePermission, X_OK);
+
+ // if we still haven't found out if the file exists, try F_OK
+ if (entryErrno == 0 && (data.entryFlags & QFileSystemMetaData::ExistsAttribute) == 0) {
+ if (QT_ACCESS(nativeFilePath, F_OK) == -1)
+ entryErrno = errno;
+ else
+ data.entryFlags |= QFileSystemMetaData::ExistsAttribute;
+ }
+
+ data.knownFlagsMask |= (what & QFileSystemMetaData::UserPermissions) |
+ QFileSystemMetaData::ExistsAttribute;
+ }
+
#if defined(Q_OS_DARWIN)
- if (what & QFileSystemMetaData::AliasType)
- {
- if (entryExists && hasResourcePropertyFlag(data, entry, kCFURLIsAliasFileKey))
+ if (what & QFileSystemMetaData::AliasType) {
+ if (entryErrno == 0 && hasResourcePropertyFlag(data, entry, kCFURLIsAliasFileKey))
data.entryFlags |= QFileSystemMetaData::AliasType;
data.knownFlagsMask |= QFileSystemMetaData::AliasType;
}
-#endif
- if (what & QFileSystemMetaData::UserPermissions) {
- // calculate user permissions
+ if (what & QFileSystemMetaData::BundleType) {
+ if (entryErrno == 0 && isPackage(data, entry))
+ data.entryFlags |= QFileSystemMetaData::BundleType;
- if (entryExists) {
- if (what & QFileSystemMetaData::UserReadPermission) {
- if (QT_ACCESS(nativeFilePath, R_OK) == 0)
- data.entryFlags |= QFileSystemMetaData::UserReadPermission;
- }
- if (what & QFileSystemMetaData::UserWritePermission) {
- if (QT_ACCESS(nativeFilePath, W_OK) == 0)
- data.entryFlags |= QFileSystemMetaData::UserWritePermission;
- }
- if (what & QFileSystemMetaData::UserExecutePermission) {
- if (QT_ACCESS(nativeFilePath, X_OK) == 0)
- data.entryFlags |= QFileSystemMetaData::UserExecutePermission;
- }
- }
- data.knownFlagsMask |= (what & QFileSystemMetaData::UserPermissions);
+ data.knownFlagsMask |= QFileSystemMetaData::BundleType;
}
+#endif
if (what & QFileSystemMetaData::HiddenAttribute
&& !data.isHidden()) {
QString fileName = entry.fileName();
if ((fileName.size() > 0 && fileName.at(0) == QLatin1Char('.'))
#if defined(Q_OS_DARWIN)
- || (entryExists && hasResourcePropertyFlag(data, entry, kCFURLIsHiddenKey))
+ || (entryErrno == 0 && hasResourcePropertyFlag(data, entry, kCFURLIsHiddenKey))
#endif
)
data.entryFlags |= QFileSystemMetaData::HiddenAttribute;
data.knownFlagsMask |= QFileSystemMetaData::HiddenAttribute;
}
-#if defined(Q_OS_DARWIN)
- if (what & QFileSystemMetaData::BundleType) {
- if (entryExists && isPackage(data, entry))
- data.entryFlags |= QFileSystemMetaData::BundleType;
+ if (entryErrno != 0) {
+ what &= ~QFileSystemMetaData::LinkType; // don't clear link: could be broken symlink
+ data.clearFlags(what);
+ return false;
+ }
+ return true;
+}
- data.knownFlagsMask |= QFileSystemMetaData::BundleType;
+// static
+bool QFileSystemEngine::cloneFile(int srcfd, int dstfd, const QFileSystemMetaData &knownData)
+{
+ QT_STATBUF statBuffer;
+ if (knownData.hasFlags(QFileSystemMetaData::PosixStatFlags) &&
+ knownData.isFile()) {
+ statBuffer.st_size = knownData.size();
+ statBuffer.st_mode = S_IFREG;
+ } else if (knownData.hasFlags(QFileSystemMetaData::PosixStatFlags) &&
+ knownData.isDirectory()) {
+ return false; // fcopyfile(3) returns success on directories
+ } else if (QT_FSTAT(srcfd, &statBuffer) == -1) {
+ return false;
+ } else if (!S_ISREG((statBuffer.st_mode))) {
+ // not a regular file, let QFile do the copy
+ return false;
}
-#endif
- if (!entryExists) {
- data.clearFlags(what);
+
+#if defined(Q_OS_LINUX)
+ if (statBuffer.st_size == 0) {
+ // empty file? we're done.
+ return true;
+ }
+
+ // first, try FICLONE (only works on regular files and only on certain fs)
+ if (::ioctl(dstfd, FICLONE, srcfd) == 0)
+ return true;
+
+ // Second, try sendfile (it can send to some special types too).
+ // sendfile(2) is limited in the kernel to 2G - 4k
+ auto sendfileSize = [](QT_OFF_T size) { return size_t(qMin<qint64>(0x7ffff000, size)); };
+
+ ssize_t n = ::sendfile(dstfd, srcfd, NULL, sendfileSize(statBuffer.st_size));
+ if (n == -1) {
+ // if we got an error here, give up and try at an upper layer
return false;
}
- return data.hasFlags(what);
+
+ statBuffer.st_size -= n;
+ while (statBuffer.st_size) {
+ n = ::sendfile(dstfd, srcfd, NULL, sendfileSize(statBuffer.st_size));
+ if (n == 0) {
+ // uh oh, this is probably a real error (like ENOSPC), but we have
+ // no way to notify QFile of partial success, so just erase any work
+ // done (hopefully we won't get any errors, because there's nothing
+ // we can do about them)
+ n = ftruncate(dstfd, 0);
+ n = lseek(srcfd, 0, SEEK_SET);
+ n = lseek(dstfd, 0, SEEK_SET);
+ return false;
+ }
+ if (n == 0)
+ return true;
+ statBuffer.st_size -= n;
+ }
+
+ return true;
+#elif defined(Q_OS_DARWIN)
+ // try fcopyfile
+ return fcopyfile(srcfd, dstfd, nullptr, COPYFILE_DATA | COPYFILE_STAT) == 0;
+#else
+ Q_UNUSED(dstfd);
+ return false;
+#endif
}
// Note: if \a shouldMkdirFirst is false, we assume the caller did try to mkdir
@@ -598,6 +1200,8 @@ static bool createDirectoryWithParents(const QByteArray &nativeName, bool should
bool QFileSystemEngine::createDirectory(const QFileSystemEntry &entry, bool createParents)
{
QString dirName = entry.filePath();
+ if (Q_UNLIKELY(dirName.isEmpty()))
+ return emptyFileEntryWarning(), false;
// Darwin doesn't support trailing /'s, so remove for everyone
while (dirName.size() > 1 && dirName.endsWith(QLatin1Char('/')))
@@ -616,6 +1220,9 @@ bool QFileSystemEngine::createDirectory(const QFileSystemEntry &entry, bool crea
//static
bool QFileSystemEngine::removeDirectory(const QFileSystemEntry &entry, bool removeEmptyParents)
{
+ if (Q_UNLIKELY(entry.isEmpty()))
+ return emptyFileEntryWarning(), false;
+
if (removeEmptyParents) {
QString dirName = QDir::cleanPath(entry.filePath());
for (int oldslash = 0, slash=dirName.length(); slash > 0; oldslash = slash) {
@@ -639,6 +1246,8 @@ bool QFileSystemEngine::removeDirectory(const QFileSystemEntry &entry, bool remo
//static
bool QFileSystemEngine::createLink(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error)
{
+ if (Q_UNLIKELY(source.isEmpty() || target.isEmpty()))
+ return emptyFileEntryWarning(), false;
if (::symlink(source.nativeFilePath().constData(), target.nativeFilePath().constData()) == 0)
return true;
error = QSystemError(errno, QSystemError::StandardLibraryError);
@@ -648,8 +1257,18 @@ bool QFileSystemEngine::createLink(const QFileSystemEntry &source, const QFileSy
//static
bool QFileSystemEngine::copyFile(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error)
{
+#if QT_DARWIN_PLATFORM_SDK_EQUAL_OR_ABOVE(101200, 100000, 100000, 30000)
+ if (__builtin_available(macOS 10.12, iOS 10, tvOS 10, watchOS 3, *)) {
+ if (::clonefile(source.nativeFilePath().constData(),
+ target.nativeFilePath().constData(), 0) == 0)
+ return true;
+ error = QSystemError(errno, QSystemError::StandardLibraryError);
+ return false;
+ }
+#else
Q_UNUSED(source);
Q_UNUSED(target);
+#endif
error = QSystemError(ENOSYS, QSystemError::StandardLibraryError); //Function not implemented
return false;
}
@@ -657,6 +1276,83 @@ bool QFileSystemEngine::copyFile(const QFileSystemEntry &source, const QFileSyst
//static
bool QFileSystemEngine::renameFile(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error)
{
+ QFileSystemEntry::NativePath srcPath = source.nativeFilePath();
+ QFileSystemEntry::NativePath tgtPath = target.nativeFilePath();
+ if (Q_UNLIKELY(srcPath.isEmpty() || tgtPath.isEmpty()))
+ return emptyFileEntryWarning(), false;
+
+#if defined(RENAME_NOREPLACE) && (QT_CONFIG(renameat2) || defined(SYS_renameat2))
+ if (renameat2(AT_FDCWD, srcPath, AT_FDCWD, tgtPath, RENAME_NOREPLACE) == 0)
+ return true;
+
+ // If we're using syscall(), check for ENOSYS;
+ // if renameat2 came from libc, we don't accept ENOSYS.
+ // We can also get EINVAL for some non-local filesystems.
+ if ((QT_CONFIG(renameat2) || errno != ENOSYS) && errno != EINVAL) {
+ error = QSystemError(errno, QSystemError::StandardLibraryError);
+ return false;
+ }
+#endif
+#if defined(Q_OS_DARWIN) && defined(RENAME_EXCL)
+ if (__builtin_available(macOS 10.12, iOS 10, tvOS 10, watchOS 3, *)) {
+ if (renameatx_np(AT_FDCWD, srcPath, AT_FDCWD, tgtPath, RENAME_EXCL) == 0)
+ return true;
+ if (errno != ENOTSUP) {
+ error = QSystemError(errno, QSystemError::StandardLibraryError);
+ return false;
+ }
+ }
+#endif
+
+ if (SupportsHardlinking && ::link(srcPath, tgtPath) == 0) {
+ if (::unlink(srcPath) == 0)
+ return true;
+
+ // if we managed to link but can't unlink the source, it's likely
+ // it's in a directory we don't have write access to; fail the
+ // renaming instead
+ int savedErrno = errno;
+
+ // this could fail too, but there's nothing we can do about it now
+ ::unlink(tgtPath);
+
+ error = QSystemError(savedErrno, QSystemError::StandardLibraryError);
+ return false;
+ } else if (!SupportsHardlinking) {
+ // man 2 link on Linux has:
+ // EPERM The filesystem containing oldpath and newpath does not
+ // support the creation of hard links.
+ errno = EPERM;
+ }
+
+ switch (errno) {
+ case EACCES:
+ case EEXIST:
+ case ENAMETOOLONG:
+ case ENOENT:
+ case ENOTDIR:
+ case EROFS:
+ case EXDEV:
+ // accept the error from link(2) (especially EEXIST) and don't retry
+ break;
+
+ default:
+ // fall back to rename()
+ // ### Race condition. If a file is moved in after this, it /will/ be
+ // overwritten.
+ if (::rename(srcPath, tgtPath) == 0)
+ return true;
+ }
+
+ error = QSystemError(errno, QSystemError::StandardLibraryError);
+ return false;
+}
+
+//static
+bool QFileSystemEngine::renameOverwriteFile(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error)
+{
+ if (Q_UNLIKELY(source.isEmpty() || target.isEmpty()))
+ return emptyFileEntryWarning(), false;
if (::rename(source.nativeFilePath().constData(), target.nativeFilePath().constData()) == 0)
return true;
error = QSystemError(errno, QSystemError::StandardLibraryError);
@@ -666,6 +1362,8 @@ bool QFileSystemEngine::renameFile(const QFileSystemEntry &source, const QFileSy
//static
bool QFileSystemEngine::removeFile(const QFileSystemEntry &entry, QSystemError &error)
{
+ if (Q_UNLIKELY(entry.isEmpty()))
+ return emptyFileEntryWarning(), false;
if (unlink(entry.nativeFilePath().constData()) == 0)
return true;
error = QSystemError(errno, QSystemError::StandardLibraryError);
@@ -700,8 +1398,10 @@ static mode_t toMode_t(QFile::Permissions permissions)
//static
bool QFileSystemEngine::setPermissions(const QFileSystemEntry &entry, QFile::Permissions permissions, QSystemError &error, QFileSystemMetaData *data)
{
- mode_t mode = toMode_t(permissions);
+ if (Q_UNLIKELY(entry.isEmpty()))
+ return emptyFileEntryWarning(), false;
+ mode_t mode = toMode_t(permissions);
bool success = ::chmod(entry.nativeFilePath().constData(), mode) == 0;
if (success && data) {
data->entryFlags &= ~QFileSystemMetaData::Permissions;
@@ -729,6 +1429,71 @@ bool QFileSystemEngine::setPermissions(int fd, QFile::Permissions permissions, Q
return success;
}
+//static
+bool QFileSystemEngine::setFileTime(int fd, const QDateTime &newDate, QAbstractFileEngine::FileTime time, QSystemError &error)
+{
+ if (!newDate.isValid() || time == QAbstractFileEngine::BirthTime ||
+ time == QAbstractFileEngine::MetadataChangeTime) {
+ error = QSystemError(EINVAL, QSystemError::StandardLibraryError);
+ return false;
+ }
+
+#if QT_CONFIG(futimens)
+ struct timespec ts[2];
+
+ ts[0].tv_sec = ts[1].tv_sec = 0;
+ ts[0].tv_nsec = ts[1].tv_nsec = UTIME_OMIT;
+
+ const qint64 msecs = newDate.toMSecsSinceEpoch();
+
+ if (time == QAbstractFileEngine::AccessTime) {
+ ts[0].tv_sec = msecs / 1000;
+ ts[0].tv_nsec = (msecs % 1000) * 1000000;
+ } else if (time == QAbstractFileEngine::ModificationTime) {
+ ts[1].tv_sec = msecs / 1000;
+ ts[1].tv_nsec = (msecs % 1000) * 1000000;
+ }
+
+ if (futimens(fd, ts) == -1) {
+ error = QSystemError(errno, QSystemError::StandardLibraryError);
+ return false;
+ }
+
+ return true;
+#elif QT_CONFIG(futimes)
+ struct timeval tv[2];
+ QT_STATBUF st;
+
+ if (QT_FSTAT(fd, &st) == -1) {
+ error = QSystemError(errno, QSystemError::StandardLibraryError);
+ return false;
+ }
+
+ GetFileTimes::get(&st, &tv[0], &tv[1]);
+
+ const qint64 msecs = newDate.toMSecsSinceEpoch();
+
+ if (time == QAbstractFileEngine::AccessTime) {
+ tv[0].tv_sec = msecs / 1000;
+ tv[0].tv_usec = (msecs % 1000) * 1000;
+ } else if (time == QAbstractFileEngine::ModificationTime) {
+ tv[1].tv_sec = msecs / 1000;
+ tv[1].tv_usec = (msecs % 1000) * 1000;
+ }
+
+ if (futimes(fd, tv) == -1) {
+ error = QSystemError(errno, QSystemError::StandardLibraryError);
+ return false;
+ }
+
+ return true;
+#else
+ Q_UNUSED(fd);
+ error = QSystemError(ENOSYS, QSystemError::StandardLibraryError);
+ return false;
+#endif
+}
+
QString QFileSystemEngine::homePath()
{
QString home = QFile::decodeName(qgetenv("HOME"));
@@ -749,14 +1514,13 @@ QString QFileSystemEngine::tempPath()
#else
QString temp = QFile::decodeName(qgetenv("TMPDIR"));
if (temp.isEmpty()) {
+ if (false) {
#if defined(Q_OS_DARWIN) && !defined(QT_BOOTSTRAPPED)
- if (NSString *nsPath = NSTemporaryDirectory()) {
+ } else if (NSString *nsPath = NSTemporaryDirectory()) {
temp = QString::fromCFString((CFStringRef)nsPath);
- } else {
-#else
- {
#endif
- temp = QLatin1String("/tmp");
+ } else {
+ temp = QLatin1String(_PATH_TMP);
}
}
return QDir::cleanPath(temp);