7#include "qplatformdefs.h"
13#include <QtCore/qoperatingsystemversion.h>
14#include <QtCore/private/qcore_unix_p.h>
15#include <QtCore/private/qfiledevice_p.h>
16#include <QtCore/private/qfunctions_p.h>
17#include <QtCore/qvarlengtharray.h>
18#ifndef QT_BOOTSTRAPPED
19# include <QtCore/qstandardpaths.h>
20# include <QtCore/private/qtemporaryfile_p.h>
38# define _PATH_TMP "/tmp"
46#if defined(Q_OS_DARWIN)
47# include <QtCore/private/qcore_mac_p.h>
48# include <CoreFoundation/CFBundle.h>
49# include <UniformTypeIdentifiers/UTType.h>
50# include <UniformTypeIdentifiers/UTCoreTypes.h>
51# include <Foundation/Foundation.h>
56#include <CoreServices/CoreServices.h>
59#if defined(QT_PLATFORM_UIKIT)
60#include <MobileCoreServices/MobileCoreServices.h>
63#if defined(Q_OS_LINUX)
64# include <sys/ioctl.h>
65# include <sys/sendfile.h>
70# define FICLONE _IOW(0x94
, 9
, int)
74#if defined(Q_OS_VXWORKS)
75# include <sys/statfs.h>
76# if __has_include(<dosFsLib.h>)
81#if defined(Q_OS_ANDROID)
85# undef STATX_BASIC_STATS
101using namespace Qt::StringLiterals;
106 while (path.size() > 1 && path.endsWith(
'/'))
113#if defined(Q_OS_ANDROID) || defined(Q_OS_HARMONY)
116 SupportsHardlinking =
false
122#if defined(Q_OS_DARWIN)
123static inline bool hasResourcePropertyFlag(
const QFileSystemMetaData &data,
124 const QFileSystemEntry &entry,
125 CFStringRef key, QCFType<CFURLRef> &url)
128 QCFString path = CFStringCreateWithFileSystemRepresentation(0,
129 entry.nativeFilePath().constData());
133 url = CFURLCreateWithFileSystemPath(0, path, kCFURLPOSIXPathStyle,
134 data.hasFlags(QFileSystemMetaData::DirectoryType));
140 if (CFURLCopyResourcePropertyForKey(url, key, &value, NULL)) {
141 if (value == kCFBooleanTrue)
148static bool isPackage(
const QFileSystemMetaData &data,
const QFileSystemEntry &entry,
149 QCFType<CFURLRef> &cachedUrl)
151 if (!data.isDirectory())
154 QFileInfo info(entry.filePath());
155 QString suffix = info.suffix();
157 if (suffix.length() > 0) {
159 const auto *utType = [UTType typeWithFilenameExtension:suffix.toNSString()];
160 if ([utType conformsToType:UTTypeBundle])
164 QCFType<CFStringRef> path = entry.filePath().toCFString();
165 QCFType<CFURLRef> url = CFURLCreateWithFileSystemPath(0, path, kCFURLPOSIXPathStyle,
true);
167 UInt32 type, creator;
169 if (CFBundleGetPackageInfoInDirectory(url, &type, &creator))
174 QCFType<CFURLRef> application = LSCopyDefaultApplicationURLForURL(url,
175 kLSRolesEditor | kLSRolesViewer,
nullptr);
178 QCFType<CFBundleRef> bundle = CFBundleCreate(kCFAllocatorDefault, application);
179 CFStringRef identifier = CFBundleGetIdentifier(bundle);
180 QString applicationId = QString::fromCFString(identifier);
181 if (applicationId !=
"com.apple.finder"_L1)
188 return hasResourcePropertyFlag(data, entry, kCFURLIsPackageKey, cachedUrl);
193static inline void forceRequestedPermissionsOnVxWorks(QByteArray dirName, mode_t mode)
203qint64 time_t_toMsecs(time_t t)
205 using namespace std::chrono;
206 return milliseconds{seconds{t}}.count();
210[[
maybe_unused]] qint64 atime(
const QT_STATBUF &statBuffer, ulong)
212 return time_t_toMsecs(statBuffer.st_atime);
214[[
maybe_unused]] qint64 birthtime(
const QT_STATBUF &, ulong)
218[[
maybe_unused]] qint64 ctime(
const QT_STATBUF &statBuffer, ulong)
220 return time_t_toMsecs(statBuffer.st_ctime);
222[[
maybe_unused]] qint64 mtime(
const QT_STATBUF &statBuffer, ulong)
224 return time_t_toMsecs(statBuffer.st_mtime);
230qint64 timespecToMSecs(
const T &spec)
232 using namespace std::chrono;
233 const nanoseconds nsecs = seconds{spec.tv_sec} + nanoseconds{spec.tv_nsec};
234 return duration_cast<milliseconds>(nsecs).count();
239[[maybe_unused]]
static typename std::enable_if<(&T::st_atim,
true), qint64>::type
240atime(
const T &statBuffer,
int)
241{
return timespecToMSecs(statBuffer.st_atim); }
244[[maybe_unused]]
static typename std::enable_if<(&T::st_birthtim,
true), qint64>::type
245birthtime(
const T &statBuffer,
int)
246{
return timespecToMSecs(statBuffer.st_birthtim); }
249[[maybe_unused]]
static typename std::enable_if<(&T::st_ctim,
true), qint64>::type
250ctime(
const T &statBuffer,
int)
251{
return timespecToMSecs(statBuffer.st_ctim); }
254[[maybe_unused]]
static typename std::enable_if<(&T::st_mtim,
true), qint64>::type
255mtime(
const T &statBuffer,
int)
256{
return timespecToMSecs(statBuffer.st_mtim); }
261[[maybe_unused]]
static typename std::enable_if<(&T::st_atimespec,
true), qint64>::type
262atime(
const T &statBuffer,
int)
263{
return timespecToMSecs(statBuffer.st_atimespec); }
266[[maybe_unused]]
static typename std::enable_if<(&T::st_birthtimespec,
true), qint64>::type
267birthtime(
const T &statBuffer,
int)
268{
return timespecToMSecs(statBuffer.st_birthtimespec); }
271[[maybe_unused]]
static typename std::enable_if<(&T::st_ctimespec,
true), qint64>::type
272ctime(
const T &statBuffer,
int)
273{
return timespecToMSecs(statBuffer.st_ctimespec); }
276[[maybe_unused]]
static typename std::enable_if<(&T::st_mtimespec,
true), qint64>::type
277mtime(
const T &statBuffer,
int)
278{
return timespecToMSecs(statBuffer.st_mtimespec); }
281#if !defined(st_mtimensec) && !defined(__alpha__)
284[[maybe_unused]]
static typename std::enable_if<(&T::st_atimensec,
true), qint64>::type
285atime(
const T &statBuffer,
int)
286{
return statBuffer.st_atime * Q_INT64_C(1000) + statBuffer.st_atimensec / 1000000; }
289[[maybe_unused]]
static typename std::enable_if<(&T::st_birthtimensec,
true), qint64>::type
290birthtime(
const T &statBuffer,
int)
291{
return statBuffer.st_birthtime * Q_INT64_C(1000) + statBuffer.st_birthtimensec / 1000000; }
294[[maybe_unused]]
static typename std::enable_if<(&T::st_ctimensec,
true), qint64>::type
295ctime(
const T &statBuffer,
int)
296{
return statBuffer.st_ctime * Q_INT64_C(1000) + statBuffer.st_ctimensec / 1000000; }
299[[maybe_unused]]
static typename std::enable_if<(&T::st_mtimensec,
true), qint64>::type
300mtime(
const T &statBuffer,
int)
301{
return statBuffer.st_mtime * Q_INT64_C(1000) + statBuffer.st_mtimensec / 1000000; }
312 QFileSystemMetaData::MetaDataFlags entryFlags = QFileSystemMetaData::ExistsAttribute;
315 entryFlags |= QFileSystemMetaData::OwnerReadPermission;
317 entryFlags |= QFileSystemMetaData::OwnerWritePermission;
319 entryFlags |= QFileSystemMetaData::OwnerExecutePermission;
322 entryFlags |= QFileSystemMetaData::GroupReadPermission;
324 entryFlags |= QFileSystemMetaData::GroupWritePermission;
326 entryFlags |= QFileSystemMetaData::GroupExecutePermission;
329 entryFlags |= QFileSystemMetaData::OtherReadPermission;
331 entryFlags |= QFileSystemMetaData::OtherWritePermission;
333 entryFlags |= QFileSystemMetaData::OtherExecutePermission;
336 Q_ASSERT(!S_ISLNK(mode));
337 if ((mode & S_IFMT) == S_IFREG)
338 entryFlags |= QFileSystemMetaData::FileType;
339 else if ((mode & S_IFMT) == S_IFDIR)
340 entryFlags |= QFileSystemMetaData::DirectoryType;
341 else if ((mode & S_IFMT) != S_IFBLK)
342 entryFlags |= QFileSystemMetaData::SequentialType;
352 if (attributes & DOS_ATTR_RDONLY) {
355 entryFlags &= ~QFileSystemMetaData::OwnerWritePermission;
356 entryFlags &= ~QFileSystemMetaData::GroupWritePermission;
357 entryFlags &= ~QFileSystemMetaData::OtherWritePermission;
363#ifdef STATX_BASIC_STATS
364static int qt_real_statx(
int fd,
const char *pathname,
int flags,
struct statx *statxBuffer)
366 unsigned mask = STATX_BASIC_STATS | STATX_BTIME;
367 int ret = statx(fd, pathname, flags | AT_NO_AUTOMOUNT, mask, statxBuffer);
368 return ret == -1 ? -errno : 0;
371static int qt_statx(
const char *pathname,
struct statx *statxBuffer)
373 return qt_real_statx(AT_FDCWD, pathname, 0, statxBuffer);
376static int qt_lstatx(
const char *pathname,
struct statx *statxBuffer)
378 return qt_real_statx(AT_FDCWD, pathname, AT_SYMLINK_NOFOLLOW, statxBuffer);
381static int qt_fstatx(
int fd,
struct statx *statxBuffer)
383 return qt_real_statx(fd,
"", AT_EMPTY_PATH, statxBuffer);
386inline void QFileSystemMetaData::fillFromStatxBuf(
const struct statx &statxBuffer)
389 MetaDataFlags flags = flagsFromStMode(statxBuffer.stx_mode, statxBuffer.stx_attributes);
391 knownFlagsMask |= flags | PosixStatFlags;
394 if (statxBuffer.stx_nlink == 0)
395 entryFlags |= QFileSystemMetaData::WasDeletedAttribute;
396 size_ = qint64(statxBuffer.stx_size);
399 using namespace GetFileTimes;
400 accessTime_ = timespecToMSecs(statxBuffer.stx_atime);
401 metadataChangeTime_ = timespecToMSecs(statxBuffer.stx_ctime);
402 modificationTime_ = timespecToMSecs(statxBuffer.stx_mtime);
403 const bool birthMask = statxBuffer.stx_mask & STATX_BTIME;
404 birthTime_ = birthMask ? timespecToMSecs(statxBuffer.stx_btime) : 0;
406 userId_ = statxBuffer.stx_uid;
407 groupId_ = statxBuffer.stx_gid;
419inline void QFileSystemMetaData::fillFromStatxBuf(
const struct statx &)
424bool QFileSystemEngine::fillMetaData(
int fd, QFileSystemMetaData &data)
426 auto getSizeForBlockDev = [&](mode_t st_mode) {
429 if (quint64 sz; (st_mode & S_IFMT) == S_IFBLK && ioctl(fd, BLKGETSIZE64, &sz) == 0)
431#elif defined(BLKGETSIZE)
433 if (ulong sz; (st_mode & S_IFMT) == S_IFBLK && ioctl(fd, BLKGETSIZE, &sz) == 0)
434 data.size_ = sz * 512;
435#elif defined(DKIOCGETBLOCKCOUNT)
438 if (quint64 count; (st_mode & S_IFMT) == S_IFBLK
439 && ioctl(fd, DKIOCGETBLOCKCOUNT, &count) == 0
440 && ioctl(fd, DKIOCGETBLOCKSIZE, &blksz) == 0)
441 data.size_ = count * blksz;
442#elif defined(DIOCGMEDIASIZE)
447 if (QT_OFF_T sz; (st_mode & S_IFMT) == S_IFCHR && ioctl(fd, DIOCGMEDIASIZE, &sz) == 0)
453 data.entryFlags &= ~QFileSystemMetaData::PosixStatFlags;
454 data.knownFlagsMask |= QFileSystemMetaData::PosixStatFlags;
456 struct statx statxBuffer;
458 int ret = qt_fstatx(fd, &statxBuffer);
459 if (ret != -ENOSYS) {
461 data.fillFromStatxBuf(statxBuffer);
462 getSizeForBlockDev(statxBuffer.stx_mode);
468 QT_STATBUF statBuffer;
470 if (QT_FSTAT(fd, &statBuffer) == 0) {
471 data.fillFromStatBuf(statBuffer);
472 getSizeForBlockDev(statBuffer.st_mode);
479#if defined(_DEXTRA_FIRST)
480static void fillStat64fromStat32(
struct stat64 *statBuf64,
const struct stat &statBuf32)
482 statBuf64->st_mode = statBuf32.st_mode;
483 statBuf64->st_size = statBuf32.st_size;
484#if _POSIX_VERSION >= 200809L
485 statBuf64->st_ctim = statBuf32.st_ctim;
486 statBuf64->st_mtim = statBuf32.st_mtim;
487 statBuf64->st_atim = statBuf32.st_atim;
489 statBuf64->st_ctime = statBuf32.st_ctime;
490 statBuf64->st_mtime = statBuf32.st_mtime;
491 statBuf64->st_atime = statBuf32.st_atime;
493 statBuf64->st_uid = statBuf32.st_uid;
494 statBuf64->st_gid = statBuf32.st_gid;
498void QFileSystemMetaData::fillFromStatBuf(
const QT_STATBUF &statBuffer)
500 quint64 attributes = 0;
501#if defined(UF_SETTABLE)
502 attributes = statBuffer.st_flags;
504 attributes = statBuffer.st_attrib;
507 MetaDataFlags flags = flagsFromStMode(statBuffer.st_mode, attributes);
509 knownFlagsMask |= flags | PosixStatFlags;
512 if (statBuffer.st_nlink == 0)
513 entryFlags |= QFileSystemMetaData::WasDeletedAttribute;
514 size_ = statBuffer.st_size;
517 accessTime_ = GetFileTimes::atime(statBuffer, 0);
518 birthTime_ = GetFileTimes::birthtime(statBuffer, 0);
519 metadataChangeTime_ = GetFileTimes::ctime(statBuffer, 0);
520 modificationTime_ = GetFileTimes::mtime(statBuffer, 0);
522 userId_ = statBuffer.st_uid;
523 groupId_ = statBuffer.st_gid;
526void QFileSystemMetaData::fillFromDirEnt(
const QT_DIRENT &entry)
528#if defined(_DEXTRA_FIRST)
531 for (dirent_extra *extra = _DEXTRA_FIRST(&entry); _DEXTRA_VALID(extra, &entry);
532 extra = _DEXTRA_NEXT(extra)) {
533 if (extra->d_type == _DTYPE_STAT || extra->d_type == _DTYPE_LSTAT) {
535 const struct dirent_extra_stat *
const extra_stat =
536 reinterpret_cast<
struct dirent_extra_stat *>(extra);
539 if (extra->d_type == _DTYPE_LSTAT) {
540 knownFlagsMask |= QFileSystemMetaData::LinkType;
541 if (S_ISLNK(extra_stat->d_stat.st_mode))
542 entryFlags |= QFileSystemMetaData::LinkType;
550 if (S_ISLNK(extra_stat->d_stat.st_mode) && extra->d_type == _DTYPE_LSTAT)
553#if defined(QT_USE_XOPEN_LFS_EXTENSIONS) && defined(QT_LARGEFILE_SUPPORT)
556 struct stat64 statBuf;
557 fillStat64fromStat32(&statBuf, extra_stat->d_stat);
558 fillFromStatBuf(statBuf);
560 fillFromStatBuf(extra_stat->d_stat);
562 knownFlagsMask |= QFileSystemMetaData::PosixStatFlags;
563 if (!S_ISLNK(extra_stat->d_stat.st_mode)) {
564 knownFlagsMask |= QFileSystemMetaData::ExistsAttribute;
565 entryFlags |= QFileSystemMetaData::ExistsAttribute;
569#elif defined(_DIRENT_HAVE_D_TYPE) || defined(Q_OS_BSD4)
573 switch (entry.d_type)
576 knownFlagsMask = QFileSystemMetaData::LinkType
577 | QFileSystemMetaData::FileType
578 | QFileSystemMetaData::DirectoryType
579 | QFileSystemMetaData::SequentialType
580 | QFileSystemMetaData::ExistsAttribute;
582 entryFlags = QFileSystemMetaData::DirectoryType
583 | QFileSystemMetaData::ExistsAttribute;
588 knownFlagsMask = QFileSystemMetaData::LinkType
589 | QFileSystemMetaData::FileType
590 | QFileSystemMetaData::DirectoryType
591 | QFileSystemMetaData::BundleType
592 | QFileSystemMetaData::AliasType
593 | QFileSystemMetaData::SequentialType
594 | QFileSystemMetaData::ExistsAttribute;
596 entryFlags = QFileSystemMetaData::ExistsAttribute;
604 knownFlagsMask = QFileSystemMetaData::LinkType
605 | QFileSystemMetaData::FileType
606 | QFileSystemMetaData::DirectoryType
607 | QFileSystemMetaData::BundleType
608 | QFileSystemMetaData::AliasType
609 | QFileSystemMetaData::SequentialType
610 | QFileSystemMetaData::ExistsAttribute;
612 entryFlags = QFileSystemMetaData::SequentialType
613 | QFileSystemMetaData::ExistsAttribute;
618 knownFlagsMask = QFileSystemMetaData::LinkType;
619 entryFlags = QFileSystemMetaData::LinkType;
623 knownFlagsMask = QFileSystemMetaData::LinkType
624 | QFileSystemMetaData::FileType
625 | QFileSystemMetaData::DirectoryType
626 | QFileSystemMetaData::BundleType
627 | QFileSystemMetaData::SequentialType
628 | QFileSystemMetaData::ExistsAttribute;
630 entryFlags = QFileSystemMetaData::FileType
631 | QFileSystemMetaData::ExistsAttribute;
645QFileSystemEntry QFileSystemEngine::getLinkTarget(
const QFileSystemEntry &link, QFileSystemMetaData &data)
649 QByteArray s = qt_readlink(link.nativeFilePath().constData());
652 if (!data.hasFlags(QFileSystemMetaData::DirectoryType))
653 fillMetaData(link, data, QFileSystemMetaData::DirectoryType);
654 if (data.isDirectory() && s[0] !=
'/') {
655 QDir parent(link.filePath());
658 if (!ret.isEmpty() && !ret.endsWith(u'/'))
661 ret += QFile::decodeName(s);
663 if (!ret.startsWith(u'/'))
664 ret.prepend(absoluteName(link).path() + u'/');
665 ret = QDir::cleanPath(ret);
666 if (ret.size() > 1 && ret.endsWith(u'/'))
668 return QFileSystemEntry(ret);
670#if defined(Q_OS_DARWIN)
672 QCFString path = CFStringCreateWithFileSystemRepresentation(0,
673 QFile::encodeName(QDir::cleanPath(link.filePath())).data());
675 return QFileSystemEntry();
677 QCFType<CFURLRef> url = CFURLCreateWithFileSystemPath(0, path, kCFURLPOSIXPathStyle,
678 data.hasFlags(QFileSystemMetaData::DirectoryType));
680 return QFileSystemEntry();
682 QCFType<CFDataRef> bookmarkData = CFURLCreateBookmarkDataFromFile(0, url, NULL);
684 return QFileSystemEntry();
686 QCFType<CFURLRef> resolvedUrl = CFURLCreateByResolvingBookmarkData(0,
688 (CFURLBookmarkResolutionOptions)(kCFBookmarkResolutionWithoutUIMask
689 | kCFBookmarkResolutionWithoutMountingMask), NULL, NULL, NULL, NULL);
691 return QFileSystemEntry();
693 QCFString cfstr(CFURLCopyFileSystemPath(resolvedUrl, kCFURLPOSIXPathStyle));
695 return QFileSystemEntry();
697 return QFileSystemEntry(QString::fromCFString(cfstr));
700 return QFileSystemEntry();
704QFileSystemEntry QFileSystemEngine::getRawLinkPath(
const QFileSystemEntry &link,
705 QFileSystemMetaData &data)
708 const QByteArray path = qt_readlink(link.nativeFilePath().constData());
709 const QString ret = QFile::decodeName(path);
710 return QFileSystemEntry(ret);
714QFileSystemEntry QFileSystemEngine::canonicalName(
const QFileSystemEntry &entry, QFileSystemMetaData &data)
717 char *resolved_name =
nullptr;
721 char stack_result[PATH_MAX + 1];
724 std::nullptr_t stack_result =
nullptr;
725 auto freer = qScopeGuard([&] { free(resolved_name); });
728# if defined(Q_OS_DARWIN) || defined(Q_OS_ANDROID)
731 if (!data.hasFlags(QFileSystemMetaData::ExistsAttribute))
732 fillMetaData(entry, data, QFileSystemMetaData::ExistsAttribute);
737 resolved_name = realpath(entry.nativeFilePath().constData(), stack_result);
739 resolved_name = realpath(entry.nativeFilePath().constData(), stack_result);
742 data.knownFlagsMask |= QFileSystemMetaData::ExistsAttribute;
743 data.entryFlags |= QFileSystemMetaData::ExistsAttribute;
744 return QFileSystemEntry(resolved_name, QFileSystemEntry::FromNativePath{});
745 }
else if (errno == ENOENT || errno == ENOTDIR) {
746 data.knownFlagsMask |= QFileSystemMetaData::ExistsAttribute;
747 data.entryFlags &= ~(QFileSystemMetaData::ExistsAttribute);
748 return QFileSystemEntry();
754QFileSystemEntry QFileSystemEngine::absoluteName(
const QFileSystemEntry &entry)
758 if (entry.isAbsolute() && entry.isClean())
761 QByteArray orig = entry.nativeFilePath();
763 if (orig.isEmpty() || !orig.startsWith(
'/')) {
764 QFileSystemEntry cur(currentPath());
765 result = cur.nativeFilePath();
767 if (!orig.isEmpty() && !(orig.size() == 1 && orig[0] ==
'.')) {
768 if (!result.isEmpty() && !result.endsWith(
'/'))
773 if (result.size() == 1 && result[0] ==
'/')
774 return QFileSystemEntry(result, QFileSystemEntry::FromNativePath());
775 const bool isDir = result.endsWith(
'/');
778
779
780
781 QFileSystemEntry resultingEntry(result, QFileSystemEntry::FromNativePath());
782 QString stringVersion = QDir::cleanPath(resultingEntry.filePath());
784 stringVersion.append(u'/');
785 return QFileSystemEntry(stringVersion);
789QByteArray QFileSystemEngine::id(
const QFileSystemEntry &entry)
793 QT_STATBUF statResult;
794 if (QT_STAT(entry.nativeFilePath().constData(), &statResult)) {
796 qErrnoWarning(
"stat() failed for '%s'", entry.nativeFilePath().constData());
799 QByteArray result = QByteArray::number(quint64(statResult.st_dev), 16);
801 result += QByteArray::number(quint64(statResult.st_ino));
806QByteArray QFileSystemEngine::id(
int fd)
808 QT_STATBUF statResult;
809 if (QT_FSTAT(fd, &statResult)) {
810 qErrnoWarning(
"fstat() failed for fd %d", fd);
813 QByteArray result = QByteArray::number(quint64(statResult.st_dev), 16);
815 result += QByteArray::number(quint64(statResult.st_ino));
820QString QFileSystemEngine::resolveUserName(uint userId)
822#if QT_CONFIG(thread) && defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_OPENBSD)
823 long size_max = sysconf(_SC_GETPW_R_SIZE_MAX);
826 QVarLengthArray<
char, 1024> buf(size_max);
829#if !defined(Q_OS_INTEGRITY) && !defined(Q_OS_WASM)
830 struct passwd *pw =
nullptr;
831#if QT_CONFIG(thread) && defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_OPENBSD) && !defined(Q_OS_VXWORKS)
833 getpwuid_r(userId, &entry, buf.data(), buf.size(), &pw);
835 pw = getpwuid(userId);
838 return QFile::decodeName(QByteArray(pw->pw_name));
846QString QFileSystemEngine::resolveGroupName(uint groupId)
848#if QT_CONFIG(thread) && defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_OPENBSD)
849 long size_max = sysconf(_SC_GETPW_R_SIZE_MAX);
852 QVarLengthArray<
char, 1024> buf(size_max);
855#if !defined(Q_OS_INTEGRITY) && !defined(Q_OS_WASM)
856 struct group *gr =
nullptr;
857#if QT_CONFIG(thread) && defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_OPENBSD) && !defined(Q_OS_VXWORKS) && (!defined(Q_OS_ANDROID) || defined(Q_OS_ANDROID) && (__ANDROID_API__ >= 24
))
858 size_max = sysconf(_SC_GETGR_R_SIZE_MAX);
861 buf.resize(size_max);
865 for (
long size = size_max; size < 256000; size += size)
869 if (!getgrgid_r(groupId, &entry, buf.data(), buf.size(), &gr)
874 gr = getgrgid(groupId);
877 return QFile::decodeName(QByteArray(gr->gr_name));
884#if defined(Q_OS_DARWIN)
886QString QFileSystemEngine::bundleName(
const QFileSystemEntry &entry)
888 QCFType<CFURLRef> url = CFURLCreateWithFileSystemPath(0, QCFString(entry.filePath()),
889 kCFURLPOSIXPathStyle,
true);
890 if (QCFType<CFDictionaryRef> dict = CFBundleCopyInfoDictionaryForURL(url)) {
891 if (CFTypeRef name = (CFTypeRef)CFDictionaryGetValue(dict, kCFBundleNameKey)) {
892 if (CFGetTypeID(name) == CFStringGetTypeID())
893 return QString::fromCFString((CFStringRef)name);
901bool QFileSystemEngine::fillMetaData(
const QFileSystemEntry &entry, QFileSystemMetaData &data,
902 QFileSystemMetaData::MetaDataFlags what)
910 auto hadBeenDeleted = data.entryFlags & QFileSystemMetaData::WasDeletedAttribute;
912#if defined(Q_OS_DARWIN)
913 if (what & (QFileSystemMetaData::BundleType | QFileSystemMetaData::CaseSensitive)) {
914 if (!data.hasFlags(QFileSystemMetaData::DirectoryType))
915 what |= QFileSystemMetaData::DirectoryType;
917 if (what & QFileSystemMetaData::AliasType)
918 what |= QFileSystemMetaData::LinkType;
921 bool needLstat = what.testAnyFlag(QFileSystemMetaData::LinkType);
924 if (what & QFileSystemMetaData::HiddenAttribute) {
934 if (what & QFileSystemMetaData::PosixStatFlags)
935 what |= QFileSystemMetaData::PosixStatFlags;
937 data.entryFlags &= ~what;
939 const QByteArray nativeFilePath = entry.nativeFilePath();
956 QT_STATBUF statBuffer;
957 struct statx statxBuffer;
962 statResult = qt_lstatx(nativeFilePath, &statxBuffer);
963 if (statResult == -ENOSYS) {
965 statResult = QT_LSTAT(nativeFilePath, &statBuffer);
967 mode = statBuffer.st_mode;
968 }
else if (statResult == 0) {
970 mode = statxBuffer.stx_mode;
973 if (statResult >= 0) {
976 Q_ASSERT(statResult == 0);
977 if (statBuffer.st_flags & UF_HIDDEN)
978 data.entryFlags |= QFileSystemMetaData::HiddenAttribute;
979 data.knownFlagsMask |= QFileSystemMetaData::HiddenAttribute;
984 data.entryFlags |= QFileSystemMetaData::LinkType;
989 data.fillFromStatxBuf(statxBuffer);
991 data.fillFromStatBuf(statBuffer);
997 data.knownFlagsMask |= QFileSystemMetaData::ExistsAttribute;
1000 data.knownFlagsMask |= QFileSystemMetaData::LinkType;
1004 if (statResult == -1 && (what & QFileSystemMetaData::PosixStatFlags)) {
1005 if (entryErrno == 0) {
1006 data.entryFlags &= ~QFileSystemMetaData::PosixStatFlags;
1007 statResult = qt_statx(nativeFilePath, &statxBuffer);
1008 if (statResult == -ENOSYS) {
1010 statResult = QT_STAT(nativeFilePath, &statBuffer);
1011 if (statResult == 0)
1012 data.fillFromStatBuf(statBuffer);
1013 }
else if (statResult == 0) {
1014 data.fillFromStatxBuf(statxBuffer);
1018 if (statResult != 0) {
1020 data.birthTime_ = 0;
1021 data.metadataChangeTime_ = 0;
1022 data.modificationTime_ = 0;
1023 data.accessTime_ = 0;
1025 data.userId_ = (uint) -2;
1026 data.groupId_ = (uint) -2;
1029 data.knownFlagsMask |= QFileSystemMetaData::PosixStatFlags
1030 | QFileSystemMetaData::ExistsAttribute;
1035 if (what & (QFileSystemMetaData::UserPermissions | QFileSystemMetaData::ExistsAttribute)) {
1036#if defined(Q_OS_VXWORKS)
1037 struct statfs statBuf;
1038 if (statfs(nativeFilePath, &statBuf) == 0) {
1039 if (statBuf.f_type != NFSV2_MAGIC && statBuf.f_type != NFSV3_MAGIC &&
1040 statBuf.f_type != HRFS_MAGIC) {
1041#if __has_include(<dosFsLib.h>)
1042 if (data.entryFlags & QFileSystemMetaData::OwnerWritePermission) {
1043 data.entryFlags |= QFileSystemMetaData::UserWritePermission;
1045 if (data.entryFlags & QFileSystemMetaData::OwnerExecutePermission) {
1046 data.entryFlags |= QFileSystemMetaData::UserExecutePermission;
1049 data.entryFlags |= QFileSystemMetaData::UserReadPermission |
1050 QFileSystemMetaData::ExistsAttribute;
1054#if defined(QT_DEBUG)
1058 statResult = QT_STAT(nativeFilePath, &statBuffer);
1059 if (statResult == 0) {
1060 data.entryFlags |= QFileSystemMetaData::UserReadPermission |
1061 QFileSystemMetaData::ExistsAttribute;
1062 data.entryFlags &= ~QFileSystemMetaData::WasDeletedAttribute;
1069 auto checkAccess = [&](QFileSystemMetaData::MetaDataFlag flag,
int mode) {
1070 if (entryErrno != 0 || (what & flag) == 0)
1072 if (QT_ACCESS(nativeFilePath, mode) == 0) {
1074 data.entryFlags |= flag | QFileSystemMetaData::ExistsAttribute;
1075 }
else if (errno != EACCES && errno != EROFS) {
1079 checkAccess(QFileSystemMetaData::UserReadPermission, R_OK);
1080 checkAccess(QFileSystemMetaData::UserWritePermission, W_OK);
1081 checkAccess(QFileSystemMetaData::UserExecutePermission, X_OK);
1084 if (entryErrno == 0 && (data.entryFlags & QFileSystemMetaData::ExistsAttribute) == 0) {
1085 if (QT_ACCESS(nativeFilePath, F_OK) == -1)
1088 data.entryFlags |= QFileSystemMetaData::ExistsAttribute;
1091 data.knownFlagsMask |= (what & QFileSystemMetaData::UserPermissions) |
1092 QFileSystemMetaData::ExistsAttribute;
1095#if defined(Q_OS_DARWIN)
1096 QCFType<CFURLRef> cachedUrl;
1097 if (what & QFileSystemMetaData::AliasType) {
1098 if (entryErrno == 0 && hasResourcePropertyFlag(data, entry, kCFURLIsAliasFileKey, cachedUrl)) {
1100 if (!(data.entryFlags & QFileSystemMetaData::LinkType))
1101 data.entryFlags |= QFileSystemMetaData::AliasType;
1103 data.knownFlagsMask |= QFileSystemMetaData::AliasType;
1106 if (what & QFileSystemMetaData::BundleType) {
1107 if (entryErrno == 0 && isPackage(data, entry, cachedUrl))
1108 data.entryFlags |= QFileSystemMetaData::BundleType;
1110 data.knownFlagsMask |= QFileSystemMetaData::BundleType;
1113 if (what & QFileSystemMetaData::CaseSensitive) {
1114 if (entryErrno == 0 && hasResourcePropertyFlag(data, entry,
1115 kCFURLVolumeSupportsCaseSensitiveNamesKey, cachedUrl))
1116 data.entryFlags |= QFileSystemMetaData::CaseSensitive;
1117 data.knownFlagsMask |= QFileSystemMetaData::CaseSensitive;
1121 if (what & QFileSystemMetaData::HiddenAttribute
1122 && !data.isHidden()) {
1125 qsizetype lastSlash = nativeFilePath.size();
1127 while (lastSlash && nativeFilePath.at(lastSlash - 1) ==
'/')
1129 while (lastSlash && nativeFilePath.at(lastSlash - 1) !=
'/')
1133 if (nativeFilePath.at(lastSlash + 1) ==
'.')
1134 data.entryFlags |= QFileSystemMetaData::HiddenAttribute;
1135 data.knownFlagsMask |= QFileSystemMetaData::HiddenAttribute;
1138 if (entryErrno != 0) {
1139 what &= ~QFileSystemMetaData::LinkType;
1140 data.clearFlags(what);
1143 data.entryFlags |= hadBeenDeleted;
1144 data.knownFlagsMask |= hadBeenDeleted;
1152auto QFileSystemEngine::cloneFile(
int srcfd,
int dstfd,
const QFileSystemMetaData &knownData) -> TriStateResult
1154 QT_STATBUF statBuffer;
1155 if (knownData.hasFlags(QFileSystemMetaData::PosixStatFlags) &&
1156 knownData.isFile()) {
1157 statBuffer.st_mode = S_IFREG;
1158 }
else if (knownData.hasFlags(QFileSystemMetaData::PosixStatFlags) &&
1159 knownData.isDirectory()) {
1161 return TriStateResult::Failed;
1162 }
else if (QT_FSTAT(srcfd, &statBuffer) == -1) {
1164 return TriStateResult::Failed;
1165 }
else if (!S_ISREG((statBuffer.st_mode))) {
1167 return TriStateResult::NotSupported;
1170 [[maybe_unused]]
auto destinationIsEmpty = [dstfd]() {
1171 QT_STATBUF statBuffer;
1172 return QT_FSTAT(dstfd, &statBuffer) == 0 && statBuffer.st_size == 0;
1174 Q_ASSERT(destinationIsEmpty());
1176#if defined(Q_OS_LINUX)
1178 if (::ioctl(dstfd, FICLONE, srcfd) == 0)
1179 return TriStateResult::Success;
1180#elif defined(Q_OS_DARWIN)
1182 if (fcopyfile(srcfd, dstfd,
nullptr, COPYFILE_DATA | COPYFILE_STAT) == 0)
1183 return TriStateResult::Success;
1187 return TriStateResult::NotSupported;
1189 return TriStateResult::Failed;
1192#if QT_CONFIG(copy_file_range)
1196 QT_OFF_T srcoffset = 0;
1199 copied = ::copy_file_range(srcfd, &srcoffset, dstfd,
nullptr, SSIZE_MAX, 0);
1200 }
while (copied > 0 || (copied < 0 && errno == EINTR));
1202 return TriStateResult::Success;
1205 copied = ftruncate(dstfd, 0);
1206 return TriStateResult::Failed;
1219 return TriStateResult::Failed;
1223#if defined(Q_OS_LINUX)
1226 const size_t SendfileSize = 0x7ffff000;
1228 ssize_t n = ::sendfile(dstfd, srcfd,
nullptr, SendfileSize);
1233 return TriStateResult::Failed;
1237 return TriStateResult::NotSupported;
1241 n = ::sendfile(dstfd, srcfd,
nullptr, SendfileSize);
1244 n = ftruncate(dstfd, 0);
1245 n = lseek(srcfd, 0, SEEK_SET);
1246 n = lseek(dstfd, 0, SEEK_SET);
1247 return TriStateResult::Failed;
1251 return TriStateResult::Success;
1254 return TriStateResult::NotSupported;
1265 auto tryMkDir = [&path, mode]() -> QSystemError {
1266 if (QT_MKDIR(path, mode) == 0) {
1268 forceRequestedPermissionsOnVxWorks(path, mode);
1273 if (errno == EISDIR)
1275 if (errno == EEXIST || errno == EROFS) {
1279 if (QT_STAT(path.constData(), &st) != 0)
1280 return QSystemError::stdError(errno);
1281 const bool isDir = (st.st_mode & S_IFMT) == S_IFDIR;
1282 return isDir ? QSystemError{} : QSystemError::stdError(EEXIST);
1284 return QSystemError::stdError(errno);
1287 QSystemError result = tryMkDir();
1292 if (result.errorCode != ENOENT)
1295 qsizetype slash = path.lastIndexOf(
'/');
1296 while (slash > 0 && path[slash - 1] ==
'/')
1303 QByteArray parentPath = path.first(slash);
1304 if (result = createDirectoryWithParents(parentPath, mode); !result.ok())
1311bool QFileSystemEngine::mkpath(
const QFileSystemEntry &entry,
1312 std::optional<QFile::Permissions> permissions)
1314 QByteArray path = entry.nativeFilePath();
1317 mode_t mode = permissions ? QtPrivate::toMode_t(*permissions) : 0777;
1318 return createDirectoryWithParents(removeTrailingSlashes(path), mode).ok();
1321bool QFileSystemEngine::mkdir(
const QFileSystemEntry &entry,
1322 std::optional<QFile::Permissions> permissions)
1324 QByteArray path = entry.nativeFilePath();
1327 mode_t mode = permissions ? QtPrivate::toMode_t(*permissions) : 0777;
1328 auto result = QT_MKDIR(removeTrailingSlashes(path), mode) == 0;
1329#if defined(Q_OS_VXWORKS)
1331 forceRequestedPermissionsOnVxWorks(path, mode);
1336bool QFileSystemEngine::rmdir(
const QFileSystemEntry &entry)
1338 const QByteArray path = entry.nativeFilePath();
1340 return ::rmdir(path.constData()) == 0;
1343bool QFileSystemEngine::rmpath(
const QFileSystemEntry &entry)
1345 QByteArray path = QFile::encodeName(QDir::cleanPath(entry.filePath()));
1348 if (::rmdir(path.constData()) != 0)
1351 const char sep = QDir::separator().toLatin1();
1352 qsizetype slash = path.lastIndexOf(sep);
1354 for (; slash > 0; slash = path.lastIndexOf(sep)) {
1355 path.truncate(slash);
1356 if (::rmdir(path.constData()) != 0)
1364bool QFileSystemEngine::createLink(
const QFileSystemEntry &source,
const QFileSystemEntry &target, QSystemError &error)
1369 if (::symlink(source.nativeFilePath().constData(), target.nativeFilePath().constData()) == 0)
1371 error = QSystemError(errno, QSystemError::StandardLibraryError);
1375#if defined(QT_BOOTSTRAPPED) || !defined(AT_FDCWD) || defined(Q_OS_ANDROID) || !QT_CONFIG(datestring) || defined(Q_OS_VXWORKS)
1378bool QFileSystemEngine::supportsMoveFileToTrash()
1384bool QFileSystemEngine::moveFileToTrash(
const QFileSystemEntry &, QFileSystemEntry &,
1385 QSystemError &error)
1387 error = QSystemError(ENOSYS, QSystemError::StandardLibraryError);
1390#elif defined(Q_OS_DARWIN)
1392#elif defined(Q_OS_HARMONY)
1396
1397
1399bool QFileSystemEngine::supportsMoveFileToTrash()
1405struct FreeDesktopTrashOperation
1408
1409
1411 int filesDirFd = -1;
1413 qsizetype volumePrefixLength = 0;
1416 QByteArray tempTrashFileName;
1417 QByteArray infoFilePath;
1419 int infoFileFd = -1;
1420 ~FreeDesktopTrashOperation()
1425 constexpr bool isTrashDirOpen()
const {
return filesDirFd != -1 && infoDirFd != -1; }
1429 int savedErrno = errno;
1430 if (infoFileFd != -1) {
1431 Q_ASSERT(infoDirFd != -1);
1432 Q_ASSERT(!infoFilePath.isEmpty());
1433 Q_ASSERT(!trashPath.isEmpty());
1435 QT_CLOSE(infoFileFd);
1436 unlinkat(infoDirFd, infoFilePath, 0);
1439 if (!tempTrashFileName.isEmpty()) {
1440 Q_ASSERT(filesDirFd != -1);
1441 unlinkat(filesDirFd, tempTrashFileName, 0);
1443 if (filesDirFd >= 0)
1444 QT_CLOSE(filesDirFd);
1446 QT_CLOSE(infoDirFd);
1447 filesDirFd = infoDirFd = -1;
1451 bool tryCreateInfoFile(
const QString &filePath, QSystemError &error)
1453 QByteArray p = QFile::encodeName(filePath) +
".trashinfo";
1454 infoFileFd = qt_safe_openat(infoDirFd, p, QT_OPEN_RDWR | QT_OPEN_CREAT | QT_OPEN_EXCL, 0666);
1455 if (infoFileFd < 0) {
1456 error = QSystemError(errno, QSystemError::StandardLibraryError);
1459 infoFilePath = std::move(p);
1465 QT_CLOSE(infoFileFd);
1467 tempTrashFileName = {};
1471 static int openDirFd(
int dfd,
const char *path,
int mode)
1474 return qt_safe_openat(dfd, path, mode);
1478 static int openOrCreateDir(
int dfd,
const char *path,
int openmode = 0)
1481 int fd = openDirFd(dfd, path, openmode);
1482 if (fd >= 0 || errno != ENOENT)
1486 if (mkdirat(dfd, path, 0700) < 0)
1490 return openDirFd(dfd, path, openmode);
1494 QSystemError getTrashDir(
int parentfd, QString targetDir,
const QFileSystemEntry &source,
1497 if (parentfd == AT_FDCWD)
1498 trashPath = targetDir;
1499 QByteArray nativePath = QFile::encodeName(targetDir);
1502 int trashfd = openOrCreateDir(parentfd, nativePath, openmode);
1503 if (trashfd < 0 && errno != ENOENT)
1504 return QSystemError::stdError(errno);
1507 if (QT_STATBUF st; QT_FSTAT(trashfd, &st) < 0)
1508 return QSystemError::stdError(errno);
1509 else if (st.st_uid != getuid())
1510 return QSystemError::stdError(errno);
1512 filesDirFd = openOrCreateDir(trashfd,
"files");
1513 if (filesDirFd >= 0) {
1515 QTemporaryFileName tfn(
"XXXXXX"_L1);
1516 for (
int i = 0; i < 16; ++i) {
1517 QByteArray attempt = tfn.generateNext();
1518 if (linkat(AT_FDCWD, source.nativeFilePath(), filesDirFd, attempt, 0) == 0) {
1519 tempTrashFileName = std::move(attempt);
1522 if (errno != EEXIST)
1533 if (!tempTrashFileName.isEmpty() || errno == EPERM || errno == EMLINK)
1534 infoDirFd = openOrCreateDir(trashfd,
"info");
1537 if (infoDirFd < 0) {
1538 int saved_errno = errno;
1541 return QSystemError::stdError(saved_errno);
1548 QSystemError openMountPointTrashLocation(
const QFileSystemEntry &source,
1549 const QStorageInfo &sourceStorage)
1552
1553
1554
1555
1556
1557
1558
1559
1560
1563 const auto dotTrash =
"/.Trash"_L1;
1564 const QString userID = QString::number(::getuid());
1565 QFileSystemEntry dotTrashDir(sourceStorage.rootPath() + dotTrash);
1568 int genericTrashFd = openDirFd(AT_FDCWD, dotTrashDir.nativeFilePath(),
O_NOFOLLOW);
1570 if (genericTrashFd < 0 && errno != ENOENT && errno != EACCES) {
1572 if (QT_LSTAT(dotTrashDir.nativeFilePath(), &st) == 0 && S_ISLNK(st.st_mode)) {
1574 qCritical(
"Warning: '%s' is a symlink to '%s'",
1575 dotTrashDir.nativeFilePath().constData(),
1576 qt_readlink(dotTrashDir.nativeFilePath()).constData());
1577 error = QSystemError(ELOOP, QSystemError::StandardLibraryError);
1579 }
else if (genericTrashFd >= 0) {
1580 QT_FSTAT(genericTrashFd, &st);
1581 if ((st.st_mode & S_ISVTX) == 0) {
1583 qCritical(
"Warning: '%s' doesn't have sticky bit set!",
1584 dotTrashDir.nativeFilePath().constData());
1585 error = QSystemError(EPERM, QSystemError::StandardLibraryError);
1588
1589
1590
1591
1592
1593
1594
1595
1596 if (error = getTrashDir(genericTrashFd, userID, source,
O_NOFOLLOW); error.ok()) {
1598 trashPath = dotTrashDir.filePath() + u'/' + userID;
1601 QT_CLOSE(genericTrashFd);
1605
1606
1607
1608
1609
1610
1611 if (!isTrashDirOpen())
1612 error = getTrashDir(AT_FDCWD, sourceStorage.rootPath() + dotTrash + u'-' + userID, source,
1615 if (isTrashDirOpen()) {
1616 volumePrefixLength = sourceStorage.rootPath().size();
1617 if (volumePrefixLength == 1)
1618 volumePrefixLength = 0;
1620 ++volumePrefixLength;
1622 return isTrashDirOpen() ? QSystemError() : error;
1625 QSystemError openHomeTrashLocation(
const QFileSystemEntry &source)
1627 QString topDir = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation);
1629 return getTrashDir(AT_FDCWD, topDir +
"/Trash"_L1, source, openmode);
1632 QSystemError findTrashFor(
const QFileSystemEntry &source)
1635
1636
1637
1638
1639
1640 if (QSystemError error = openHomeTrashLocation(source); error.ok())
1641 return QSystemError();
1642 else if (error.errorCode != EXDEV)
1646 const QStorageInfo sourceStorage(source.filePath());
1647 if (!sourceStorage.isValid())
1648 return QSystemError::stdError(ENODEV);
1649 return openMountPointTrashLocation(source, sourceStorage);
1655bool QFileSystemEngine::moveFileToTrash(
const QFileSystemEntry &source,
1656 QFileSystemEntry &newLocation, QSystemError &error)
1658 const QFileSystemEntry sourcePath = [&] {
1659 if (QString path = source.filePath(); path.size() > 1 && path.endsWith(u'/')) {
1661 return absoluteName(QFileSystemEntry(path));
1663 return absoluteName(source);
1665 FreeDesktopTrashOperation op;
1666 if (error = op.findTrashFor(sourcePath); !error.ok())
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689 QString uniqueTrashedName = sourcePath.fileName();
1690 if (!op.tryCreateInfoFile(uniqueTrashedName, error) && error.errorCode == EEXIST) {
1694 if (QT_STATBUF st; Q_LIKELY(QT_STAT(source.nativeFilePath(), &st) == 0)) {
1695 counter = st.st_ino;
1697 error = QSystemError(errno, QSystemError::StandardLibraryError);
1701 QString uniqueTrashBase = std::move(uniqueTrashedName);
1703 uniqueTrashedName = QString::asprintf(
"%ls-%llu", qUtf16Printable(uniqueTrashBase),
1705 if (op.tryCreateInfoFile(uniqueTrashedName, error))
1707 if (error.errorCode != EEXIST)
1714 "Path=" + QUrl::toPercentEncoding(source.filePath().mid(op.volumePrefixLength),
"/") +
"\n"
1715 "DeletionDate=" + QDateTime::currentDateTime().toString(Qt::ISODate).toUtf8()
1717 if (QT_WRITE(op.infoFileFd, info.data(), info.size()) < 0) {
1718 error = QSystemError(errno, QSystemError::StandardLibraryError);
1723
1724
1725
1726
1728 if (op.tempTrashFileName.isEmpty()) {
1730
1731
1732
1733
1734
1735 renamed = renameat(AT_FDCWD, source.nativeFilePath(), op.filesDirFd,
1736 QFile::encodeName(uniqueTrashedName)) == 0;
1738 renamed = renameat(op.filesDirFd, op.tempTrashFileName, op.filesDirFd,
1739 QFile::encodeName(uniqueTrashedName)) == 0;
1741 removeFile(sourcePath, error);
1744 error = QSystemError(errno, QSystemError::StandardLibraryError);
1749 newLocation = QFileSystemEntry(op.trashPath +
"/files/"_L1 + uniqueTrashedName);
1755#if !defined(AT_FDCWD) || defined(Q_OS_QNX) || defined(Q_OS_VXWORKS)
1763bool QFileSystemEngine::supportsRmdirRecursively()
noexcept
1769bool QFileSystemEngine::rmdirRecursively(
const QFileSystemEntry &, QSystemError &error)
1771 error = QSystemError(ENOSYS, QSystemError::StandardLibraryError);
1777bool QFileSystemEngine::supportsRmdirRecursively()
noexcept
1783bool QFileSystemEngine::rmdirRecursively(
const QFileSystemEntry &entry, QSystemError &error)
1785 Q_CHECK_FILE_NAME(entry,
false);
1787# if defined(Q_OS_ANDROID) || defined(Q_OS_BSD4) || defined(Q_OS_LINUX)
1794 constexpr bool NeedsSecondFileDescriptor =
false;
1796 constexpr bool NeedsSecondFileDescriptor =
true;
1804 auto openDirectory = [](
int parentFd, std::string name) {
1805 constexpr int OpenMode = QT_OPEN_RDONLY | O_DIRECTORY | O_NOFOLLOW;
1806 DirInfo di = {
nullptr, -1, parentFd, std::move(name) };
1807 di.dirfd = qt_safe_openat(parentFd, di.name.c_str(), OpenMode);
1812 di.dir = fdopendir(di.dirfd);
1818 if constexpr (NeedsSecondFileDescriptor) {
1819 di.dirfd = qt_safe_openat(parentFd, di.name.c_str(), OpenMode);
1820 if (Q_UNLIKELY(di.dirfd < 0))
1821 closedir(std::exchange(di.dir,
nullptr));
1827 QVarLengthArray<DirInfo, 16> stack;
1828 if (DirInfo di = openDirectory(AT_FDCWD, entry.nativeFilePath().toStdString()); di.dir) {
1829 stack.emplace_back(std::move(di));
1831 if (errno == ENOENT)
1833 error = QSystemError(errno, QSystemError::StandardLibraryError);
1837 while (!stack.isEmpty()) {
1838 DirInfo &top = stack.last();
1839 if (QT_DIRENT *de = QT_READDIR(top.dir)) {
1840 if (de->d_name[0] ==
'.') {
1841 if (de->d_name[1] ==
'\0' || (de->d_name[1] ==
'.' && de->d_name[2] ==
'\0'))
1845 QFileSystemMetaData meta;
1846 meta.fillFromDirEnt(*de);
1849 if (!meta.hasFlags(QFileSystemMetaData::DirectoryType) || !meta.isDirectory()) {
1850 if (unlinkat(top.dirfd, de->d_name, 0) == 0)
1856 if (DirInfo di = openDirectory(top.dirfd, de->d_name); di.dir) {
1858 stack.emplace_back(std::move(di));
1866 if constexpr (NeedsSecondFileDescriptor)
1867 QT_CLOSE(top.dirfd);
1870 int r = unlinkat(top.parentFd, top.name.c_str(), AT_REMOVEDIR);
1871 if (r < 0 && top.parentFd == AT_FDCWD && errno != ENOENT)
1872 error = QSystemError(errno, QSystemError::StandardLibraryError);
1884bool QFileSystemEngine::copyFile(
const QFileSystemEntry &source,
const QFileSystemEntry &target, QSystemError &error)
1888 error = QSystemError(ENOSYS, QSystemError::StandardLibraryError);
1893bool QFileSystemEngine::renameFile(
const QFileSystemEntry &source,
const QFileSystemEntry &target, QSystemError &error)
1895 QFileSystemEntry::NativePath srcPath = source.nativeFilePath();
1896 QFileSystemEntry::NativePath tgtPath = target.nativeFilePath();
1901#if defined(RENAME_NOREPLACE
) && QT_CONFIG(renameat2)
1902 if (renameat2(AT_FDCWD, srcPath, AT_FDCWD, tgtPath, RENAME_NOREPLACE) == 0)
1906 if (errno != EINVAL) {
1907 error = QSystemError(errno, QSystemError::StandardLibraryError);
1911#if defined(Q_OS_DARWIN) && defined(RENAME_EXCL)
1912 if (renameatx_np(AT_FDCWD, srcPath, AT_FDCWD, tgtPath, RENAME_EXCL) == 0)
1914 if (errno != ENOTSUP) {
1915 error = QSystemError(errno, QSystemError::StandardLibraryError);
1920 if (SupportsHardlinking && ::link(srcPath, tgtPath) == 0) {
1921 if (::unlink(srcPath) == 0)
1927 int savedErrno = errno;
1932 error = QSystemError(savedErrno, QSystemError::StandardLibraryError);
1934 }
else if (!SupportsHardlinking) {
1938 if (::access(tgtPath, F_OK) == 0) {
1963 if (::rename(srcPath, tgtPath) == 0)
1967 error = QSystemError(errno, QSystemError::StandardLibraryError);
1972bool QFileSystemEngine::renameOverwriteFile(
const QFileSystemEntry &source,
const QFileSystemEntry &target, QSystemError &error)
1977 if (::rename(source.nativeFilePath().constData(), target.nativeFilePath().constData()) == 0)
1979 error = QSystemError(errno, QSystemError::StandardLibraryError);
1984bool QFileSystemEngine::removeFile(
const QFileSystemEntry &entry, QSystemError &error)
1987 if (unlink(entry.nativeFilePath().constData()) == 0)
1989 error = QSystemError(errno, QSystemError::StandardLibraryError);
1995bool QFileSystemEngine::setPermissions(
const QFileSystemEntry &entry,
1996 QFile::Permissions permissions, QSystemError &error)
2000 mode_t mode = QtPrivate::toMode_t(permissions);
2001 bool success = ::chmod(entry.nativeFilePath().constData(), mode) == 0;
2003 error = QSystemError(errno, QSystemError::StandardLibraryError);
2008bool QFileSystemEngine::setPermissions(
int fd, QFile::Permissions permissions, QSystemError &error)
2010 mode_t mode = QtPrivate::toMode_t(permissions);
2011 bool success = ::fchmod(fd, mode) == 0;
2013 error = QSystemError(errno, QSystemError::StandardLibraryError);
2018bool QFileSystemEngine::setFileTime(
int fd,
const QDateTime &newDate, QFile::FileTime time, QSystemError &error)
2020 if (!newDate.isValid()
2021 || time == QFile::FileBirthTime || time == QFile::FileMetadataChangeTime) {
2022 error = QSystemError(EINVAL, QSystemError::StandardLibraryError);
2026#if QT_CONFIG(futimens)
2028 struct timespec ts[2] = {{0, UTIME_OMIT}, {0, UTIME_OMIT}};
2030 if (time == QFile::FileAccessTime || time == QFile::FileModificationTime) {
2031 const int idx = time == QFile::FileAccessTime ? 0 : 1;
2032 const std::chrono::milliseconds msecs{newDate.toMSecsSinceEpoch()};
2033 ts[idx] = durationToTimespec(msecs);
2036 if (futimens(fd, ts) == -1) {
2037 error = QSystemError(errno, QSystemError::StandardLibraryError);
2044 error = QSystemError(ENOSYS, QSystemError::StandardLibraryError);
2049QString QFileSystemEngine::homePath()
2051 QString home = qEnvironmentVariable(
"HOME");
2054 return QDir::cleanPath(home);
2057QString QFileSystemEngine::rootPath()
2065 QLatin1StringView temp =
_PATH_TMP ""_L1;
2066 static_assert(
_PATH_TMP[0] ==
'/',
"_PATH_TMP needs to be absolute");
2067 static_assert(
_PATH_TMP[1] !=
'\0',
"Are you really sure _PATH_TMP should be the root dir??");
2068 if (temp.endsWith(u'/'))
2073QString QFileSystemEngine::tempPath()
2075#ifdef QT_UNIX_TEMP_PATH_OVERRIDE
2076 return QT_UNIX_TEMP_PATH_OVERRIDE
""_L1;
2078 QString temp = qEnvironmentVariable(
"TMPDIR");
2079# if defined(Q_OS_DARWIN) && !defined(QT_BOOTSTRAPPED)
2080 if (NSString *nsPath; temp.isEmpty() && (nsPath = NSTemporaryDirectory()))
2081 temp = QString::fromCFString((CFStringRef)nsPath);
2084 return nativeTempPath();
2087 if (temp.size() > 1 && temp.endsWith(u'/'))
2090 QFileSystemEntry e(temp, QFileSystemEntry::FromInternalPath{});
2091 return QFileSystemEngine::absoluteName(e).filePath();
2095bool QFileSystemEngine::setCurrentPath(
const QFileSystemEntry &path)
2098 r = QT_CHDIR(path.nativeFilePath().constData());
2102QFileSystemEntry QFileSystemEngine::currentPath()
2104 QFileSystemEntry result;
2105#if defined(__GLIBC__
) && !defined(PATH_MAX)
2106 char *currentName = ::get_current_dir_name();
2108 result = QFileSystemEntry(QByteArray(currentName), QFileSystemEntry::FromNativePath());
2109 ::free(currentName);
2112 char currentName[PATH_MAX+1];
2113 if (::getcwd(currentName, PATH_MAX)) {
2114#if defined(Q_OS_VXWORKS) && defined(VXWORKS_VXSIM)
2115 QByteArray dir(currentName);
2116 if (dir.indexOf(
':') < dir.indexOf(
'/'))
2117 dir.remove(0, dir.indexOf(
':')+1);
2119 qstrncpy(currentName, dir.constData(), PATH_MAX);
2121 result = QFileSystemEntry(QByteArray(currentName), QFileSystemEntry::FromNativePath());
2123# if defined(QT_DEBUG)
2124 if (result.isEmpty())
2125 qWarning(
"QFileSystemEngine::currentPath: getcwd() failed");
2131bool QFileSystemEngine::isCaseSensitive(
const QFileSystemEntry &entry, QFileSystemMetaData &metaData)
2133#if defined(Q_OS_DARWIN)
2134 if (!metaData.hasFlags(QFileSystemMetaData::CaseSensitive))
2135 fillMetaData(entry, metaData, QFileSystemMetaData::CaseSensitive);
2136 return metaData.entryFlags.testFlag(QFileSystemMetaData::CaseSensitive);
#define Q_CHECK_FILE_NAME(name, result)
static int qt_lstatx(const char *, struct statx *)
static int qt_fstatx(int, struct statx *)
static QFileSystemMetaData::MetaDataFlags flagsFromStMode(mode_t mode, quint64 attributes)
static QByteArray & removeTrailingSlashes(QByteArray &path)
static QSystemError createDirectoryWithParents(const QByteArray &path, mode_t mode)
static constexpr QLatin1StringView nativeTempPath() noexcept
static int qt_statx(const char *, struct statx *)