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);
792 char buffer[
sizeof "0123456789abcdef:18446744073709551615"];
793 std::snprintf(buffer,
sizeof(buffer),
"%llx:%llu",
794 qulonglong(volumeId), qulonglong(fileId[0]));
801 id.volumeId = quint64(statResult.st_dev);
802 id.fileId[0] = quint64(statResult.st_ino);
807QFileSystemNativeId QFileSystemEngine::nativeId(
const QFileSystemEntry &entry)
811 QT_STATBUF statResult;
812 if (QT_STAT(entry.nativeFilePath().constData(), &statResult) != 0)
814 return nativeIdFromStatBuf(statResult);
818QFileSystemNativeId QFileSystemEngine::nativeId(
int fd)
820 QT_STATBUF statResult;
821 if (QT_FSTAT(fd, &statResult) != 0) {
822 qErrnoWarning(
"fstat() failed for fd %d", fd);
825 return nativeIdFromStatBuf(statResult);
829QString QFileSystemEngine::resolveUserName(uint userId)
831#if QT_CONFIG(thread) && defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_OPENBSD)
832 long size_max = sysconf(_SC_GETPW_R_SIZE_MAX);
835 QVarLengthArray<
char, 1024> buf(size_max);
838#if !defined(Q_OS_INTEGRITY) && !defined(Q_OS_WASM)
839 struct passwd *pw =
nullptr;
840#if QT_CONFIG(thread) && defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_OPENBSD) && !defined(Q_OS_VXWORKS)
842 getpwuid_r(userId, &entry, buf.data(), buf.size(), &pw);
844 pw = getpwuid(userId);
847 return QFile::decodeName(QByteArray(pw->pw_name));
855QString QFileSystemEngine::resolveGroupName(uint groupId)
857#if QT_CONFIG(thread) && defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_OPENBSD)
858 long size_max = sysconf(_SC_GETPW_R_SIZE_MAX);
861 QVarLengthArray<
char, 1024> buf(size_max);
864#if !defined(Q_OS_INTEGRITY) && !defined(Q_OS_WASM)
865 struct group *gr =
nullptr;
866#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
))
867 size_max = sysconf(_SC_GETGR_R_SIZE_MAX);
870 buf.resize(size_max);
874 for (
long size = size_max; size < 256000; size += size)
878 if (!getgrgid_r(groupId, &entry, buf.data(), buf.size(), &gr)
883 gr = getgrgid(groupId);
886 return QFile::decodeName(QByteArray(gr->gr_name));
893#if defined(Q_OS_DARWIN)
895QString QFileSystemEngine::bundleName(
const QFileSystemEntry &entry)
897 QCFType<CFURLRef> url = CFURLCreateWithFileSystemPath(0, QCFString(entry.filePath()),
898 kCFURLPOSIXPathStyle,
true);
899 if (QCFType<CFDictionaryRef> dict = CFBundleCopyInfoDictionaryForURL(url)) {
900 if (CFTypeRef name = (CFTypeRef)CFDictionaryGetValue(dict, kCFBundleNameKey)) {
901 if (CFGetTypeID(name) == CFStringGetTypeID())
902 return QString::fromCFString((CFStringRef)name);
910bool QFileSystemEngine::fillMetaData(
const QFileSystemEntry &entry, QFileSystemMetaData &data,
911 QFileSystemMetaData::MetaDataFlags what)
919 auto hadBeenDeleted = data.entryFlags & QFileSystemMetaData::WasDeletedAttribute;
921#if defined(Q_OS_DARWIN)
922 if (what & (QFileSystemMetaData::BundleType | QFileSystemMetaData::CaseSensitive)) {
923 if (!data.hasFlags(QFileSystemMetaData::DirectoryType))
924 what |= QFileSystemMetaData::DirectoryType;
926 if (what & QFileSystemMetaData::AliasType)
927 what |= QFileSystemMetaData::LinkType;
930 bool needLstat = what.testAnyFlag(QFileSystemMetaData::LinkType);
933 if (what & QFileSystemMetaData::HiddenAttribute) {
943 if (what & QFileSystemMetaData::PosixStatFlags)
944 what |= QFileSystemMetaData::PosixStatFlags;
946 data.entryFlags &= ~what;
948 const QByteArray nativeFilePath = entry.nativeFilePath();
965 QT_STATBUF statBuffer;
966 struct statx statxBuffer;
971 statResult = qt_lstatx(nativeFilePath, &statxBuffer);
972 if (statResult == -ENOSYS) {
974 statResult = QT_LSTAT(nativeFilePath, &statBuffer);
976 mode = statBuffer.st_mode;
977 }
else if (statResult == 0) {
979 mode = statxBuffer.stx_mode;
982 if (statResult >= 0) {
985 Q_ASSERT(statResult == 0);
986 if (statBuffer.st_flags & UF_HIDDEN)
987 data.entryFlags |= QFileSystemMetaData::HiddenAttribute;
988 data.knownFlagsMask |= QFileSystemMetaData::HiddenAttribute;
993 data.entryFlags |= QFileSystemMetaData::LinkType;
998 data.fillFromStatxBuf(statxBuffer);
1000 data.fillFromStatBuf(statBuffer);
1006 data.knownFlagsMask |= QFileSystemMetaData::ExistsAttribute;
1009 data.knownFlagsMask |= QFileSystemMetaData::LinkType;
1013 if (statResult == -1 && (what & QFileSystemMetaData::PosixStatFlags)) {
1014 if (entryErrno == 0) {
1015 data.entryFlags &= ~QFileSystemMetaData::PosixStatFlags;
1016 statResult = qt_statx(nativeFilePath, &statxBuffer);
1017 if (statResult == -ENOSYS) {
1019 statResult = QT_STAT(nativeFilePath, &statBuffer);
1020 if (statResult == 0)
1021 data.fillFromStatBuf(statBuffer);
1022 }
else if (statResult == 0) {
1023 data.fillFromStatxBuf(statxBuffer);
1027 if (statResult != 0) {
1029 data.birthTime_ = 0;
1030 data.metadataChangeTime_ = 0;
1031 data.modificationTime_ = 0;
1032 data.accessTime_ = 0;
1034 data.userId_ = (uint) -2;
1035 data.groupId_ = (uint) -2;
1038 data.knownFlagsMask |= QFileSystemMetaData::PosixStatFlags
1039 | QFileSystemMetaData::ExistsAttribute;
1044 if (what & (QFileSystemMetaData::UserPermissions | QFileSystemMetaData::ExistsAttribute)) {
1045#if defined(Q_OS_VXWORKS)
1046 struct statfs statBuf;
1047 if (statfs(nativeFilePath, &statBuf) == 0) {
1048 if (statBuf.f_type != NFSV2_MAGIC && statBuf.f_type != NFSV3_MAGIC &&
1049 statBuf.f_type != HRFS_MAGIC) {
1050#if __has_include(<dosFsLib.h>)
1051 if (data.entryFlags & QFileSystemMetaData::OwnerWritePermission) {
1052 data.entryFlags |= QFileSystemMetaData::UserWritePermission;
1054 if (data.entryFlags & QFileSystemMetaData::OwnerExecutePermission) {
1055 data.entryFlags |= QFileSystemMetaData::UserExecutePermission;
1058 data.entryFlags |= QFileSystemMetaData::UserReadPermission |
1059 QFileSystemMetaData::ExistsAttribute;
1063#if defined(QT_DEBUG)
1067 statResult = QT_STAT(nativeFilePath, &statBuffer);
1068 if (statResult == 0) {
1069 data.entryFlags |= QFileSystemMetaData::UserReadPermission |
1070 QFileSystemMetaData::ExistsAttribute;
1071 data.entryFlags &= ~QFileSystemMetaData::WasDeletedAttribute;
1078 auto checkAccess = [&](QFileSystemMetaData::MetaDataFlag flag,
int mode) {
1079 if (entryErrno != 0 || (what & flag) == 0)
1081 if (QT_ACCESS(nativeFilePath, mode) == 0) {
1083 data.entryFlags |= flag | QFileSystemMetaData::ExistsAttribute;
1084 }
else if (errno != EACCES && errno != EROFS) {
1088 checkAccess(QFileSystemMetaData::UserReadPermission, R_OK);
1089 checkAccess(QFileSystemMetaData::UserWritePermission, W_OK);
1090 checkAccess(QFileSystemMetaData::UserExecutePermission, X_OK);
1093 if (entryErrno == 0 && (data.entryFlags & QFileSystemMetaData::ExistsAttribute) == 0) {
1094 if (QT_ACCESS(nativeFilePath, F_OK) == -1)
1097 data.entryFlags |= QFileSystemMetaData::ExistsAttribute;
1100 data.knownFlagsMask |= (what & QFileSystemMetaData::UserPermissions) |
1101 QFileSystemMetaData::ExistsAttribute;
1104#if defined(Q_OS_DARWIN)
1105 QCFType<CFURLRef> cachedUrl;
1106 if (what & QFileSystemMetaData::AliasType) {
1107 if (entryErrno == 0 && hasResourcePropertyFlag(data, entry, kCFURLIsAliasFileKey, cachedUrl)) {
1109 if (!(data.entryFlags & QFileSystemMetaData::LinkType))
1110 data.entryFlags |= QFileSystemMetaData::AliasType;
1112 data.knownFlagsMask |= QFileSystemMetaData::AliasType;
1115 if (what & QFileSystemMetaData::BundleType) {
1116 if (entryErrno == 0 && isPackage(data, entry, cachedUrl))
1117 data.entryFlags |= QFileSystemMetaData::BundleType;
1119 data.knownFlagsMask |= QFileSystemMetaData::BundleType;
1122 if (what & QFileSystemMetaData::CaseSensitive) {
1123 if (entryErrno == 0 && hasResourcePropertyFlag(data, entry,
1124 kCFURLVolumeSupportsCaseSensitiveNamesKey, cachedUrl))
1125 data.entryFlags |= QFileSystemMetaData::CaseSensitive;
1126 data.knownFlagsMask |= QFileSystemMetaData::CaseSensitive;
1130 if (what & QFileSystemMetaData::HiddenAttribute
1131 && !data.isHidden()) {
1134 qsizetype lastSlash = nativeFilePath.size();
1136 while (lastSlash && nativeFilePath.at(lastSlash - 1) ==
'/')
1138 while (lastSlash && nativeFilePath.at(lastSlash - 1) !=
'/')
1142 if (nativeFilePath.at(lastSlash + 1) ==
'.')
1143 data.entryFlags |= QFileSystemMetaData::HiddenAttribute;
1144 data.knownFlagsMask |= QFileSystemMetaData::HiddenAttribute;
1147 if (entryErrno != 0) {
1148 what &= ~QFileSystemMetaData::LinkType;
1149 data.clearFlags(what);
1152 data.entryFlags |= hadBeenDeleted;
1153 data.knownFlagsMask |= hadBeenDeleted;
1161auto QFileSystemEngine::cloneFile(
int srcfd,
int dstfd,
const QFileSystemMetaData &knownData) -> TriStateResult
1163 QT_STATBUF statBuffer;
1164 if (knownData.hasFlags(QFileSystemMetaData::PosixStatFlags) &&
1165 knownData.isFile()) {
1166 statBuffer.st_mode = S_IFREG;
1167 }
else if (knownData.hasFlags(QFileSystemMetaData::PosixStatFlags) &&
1168 knownData.isDirectory()) {
1170 return TriStateResult::Failed;
1171 }
else if (QT_FSTAT(srcfd, &statBuffer) == -1) {
1173 return TriStateResult::Failed;
1174 }
else if (!S_ISREG((statBuffer.st_mode))) {
1176 return TriStateResult::NotSupported;
1179 [[maybe_unused]]
auto destinationIsEmpty = [dstfd]() {
1180 QT_STATBUF statBuffer;
1181 return QT_FSTAT(dstfd, &statBuffer) == 0 && statBuffer.st_size == 0;
1183 Q_ASSERT(destinationIsEmpty());
1185#if defined(Q_OS_LINUX)
1187 if (::ioctl(dstfd, FICLONE, srcfd) == 0)
1188 return TriStateResult::Success;
1189#elif defined(Q_OS_DARWIN)
1191 if (fcopyfile(srcfd, dstfd,
nullptr, COPYFILE_DATA | COPYFILE_STAT) == 0)
1192 return TriStateResult::Success;
1196 return TriStateResult::NotSupported;
1198 return TriStateResult::Failed;
1201#if QT_CONFIG(copy_file_range)
1205 QT_OFF_T srcoffset = 0;
1208 copied = ::copy_file_range(srcfd, &srcoffset, dstfd,
nullptr, SSIZE_MAX, 0);
1209 }
while (copied > 0 || (copied < 0 && errno == EINTR));
1211 return TriStateResult::Success;
1214 copied = ftruncate(dstfd, 0);
1215 return TriStateResult::Failed;
1228 return TriStateResult::Failed;
1232#if defined(Q_OS_LINUX)
1235 const size_t SendfileSize = 0x7ffff000;
1237 ssize_t n = ::sendfile(dstfd, srcfd,
nullptr, SendfileSize);
1242 return TriStateResult::Failed;
1246 return TriStateResult::NotSupported;
1250 n = ::sendfile(dstfd, srcfd,
nullptr, SendfileSize);
1253 n = ftruncate(dstfd, 0);
1254 n = lseek(srcfd, 0, SEEK_SET);
1255 n = lseek(dstfd, 0, SEEK_SET);
1256 return TriStateResult::Failed;
1260 return TriStateResult::Success;
1263 return TriStateResult::NotSupported;
1274 auto tryMkDir = [&path, mode]() -> QSystemError {
1275 if (QT_MKDIR(path, mode) == 0) {
1277 forceRequestedPermissionsOnVxWorks(path, mode);
1282 if (errno == EISDIR)
1284 if (errno == EEXIST || errno == EROFS) {
1288 if (QT_STAT(path.constData(), &st) != 0)
1289 return QSystemError::stdError(errno);
1290 const bool isDir = (st.st_mode & S_IFMT) == S_IFDIR;
1291 return isDir ? QSystemError{} : QSystemError::stdError(EEXIST);
1293 return QSystemError::stdError(errno);
1296 QSystemError result = tryMkDir();
1301 if (result.errorCode != ENOENT)
1304 qsizetype slash = path.lastIndexOf(
'/');
1305 while (slash > 0 && path[slash - 1] ==
'/')
1312 QByteArray parentPath = path.first(slash);
1313 if (result = createDirectoryWithParents(parentPath, mode); !result.ok())
1320bool QFileSystemEngine::mkpath(
const QFileSystemEntry &entry,
1321 std::optional<QFile::Permissions> permissions)
1323 QByteArray path = entry.nativeFilePath();
1326 mode_t mode = permissions ? QtPrivate::toMode_t(*permissions) : 0777;
1327 return createDirectoryWithParents(removeTrailingSlashes(path), mode).ok();
1330bool QFileSystemEngine::mkdir(
const QFileSystemEntry &entry,
1331 std::optional<QFile::Permissions> permissions)
1333 QByteArray path = entry.nativeFilePath();
1336 mode_t mode = permissions ? QtPrivate::toMode_t(*permissions) : 0777;
1337 auto result = QT_MKDIR(removeTrailingSlashes(path), mode) == 0;
1338#if defined(Q_OS_VXWORKS)
1340 forceRequestedPermissionsOnVxWorks(path, mode);
1345bool QFileSystemEngine::rmdir(
const QFileSystemEntry &entry)
1347 const QByteArray path = entry.nativeFilePath();
1349 return ::rmdir(path.constData()) == 0;
1352bool QFileSystemEngine::rmpath(
const QFileSystemEntry &entry)
1354 QByteArray path = QFile::encodeName(QDir::cleanPath(entry.filePath()));
1357 if (::rmdir(path.constData()) != 0)
1360 const char sep = QDir::separator().toLatin1();
1361 qsizetype slash = path.lastIndexOf(sep);
1363 for (; slash > 0; slash = path.lastIndexOf(sep)) {
1364 path.truncate(slash);
1365 if (::rmdir(path.constData()) != 0)
1373bool QFileSystemEngine::createLink(
const QFileSystemEntry &source,
const QFileSystemEntry &target, QSystemError &error)
1378 if (::symlink(source.nativeFilePath().constData(), target.nativeFilePath().constData()) == 0)
1380 error = QSystemError(errno, QSystemError::StandardLibraryError);
1384#if defined(QT_BOOTSTRAPPED) || !defined(AT_FDCWD) || defined(Q_OS_ANDROID) || !QT_CONFIG(datestring) || defined(Q_OS_VXWORKS)
1387bool QFileSystemEngine::supportsMoveFileToTrash()
1393bool QFileSystemEngine::moveFileToTrash(
const QFileSystemEntry &, QFileSystemEntry &,
1394 QSystemError &error)
1396 error = QSystemError(ENOSYS, QSystemError::StandardLibraryError);
1399#elif defined(Q_OS_DARWIN)
1401#elif defined(Q_OS_HARMONY)
1405
1406
1408bool QFileSystemEngine::supportsMoveFileToTrash()
1414struct FreeDesktopTrashOperation
1417
1418
1420 int filesDirFd = -1;
1422 qsizetype volumePrefixLength = 0;
1425 QByteArray tempTrashFileName;
1426 QByteArray infoFilePath;
1428 int infoFileFd = -1;
1429 ~FreeDesktopTrashOperation()
1434 constexpr bool isTrashDirOpen()
const {
return filesDirFd != -1 && infoDirFd != -1; }
1438 int savedErrno = errno;
1439 if (infoFileFd != -1) {
1440 Q_ASSERT(infoDirFd != -1);
1441 Q_ASSERT(!infoFilePath.isEmpty());
1442 Q_ASSERT(!trashPath.isEmpty());
1444 QT_CLOSE(infoFileFd);
1445 unlinkat(infoDirFd, infoFilePath, 0);
1448 if (!tempTrashFileName.isEmpty()) {
1449 Q_ASSERT(filesDirFd != -1);
1450 unlinkat(filesDirFd, tempTrashFileName, 0);
1452 if (filesDirFd >= 0)
1453 QT_CLOSE(filesDirFd);
1455 QT_CLOSE(infoDirFd);
1456 filesDirFd = infoDirFd = -1;
1460 bool tryCreateInfoFile(
const QString &filePath, QSystemError &error)
1462 QByteArray p = QFile::encodeName(filePath) +
".trashinfo";
1463 infoFileFd = qt_safe_openat(infoDirFd, p, QT_OPEN_RDWR | QT_OPEN_CREAT | QT_OPEN_EXCL, 0666);
1464 if (infoFileFd < 0) {
1465 error = QSystemError(errno, QSystemError::StandardLibraryError);
1468 infoFilePath = std::move(p);
1474 QT_CLOSE(infoFileFd);
1476 tempTrashFileName = {};
1480 static int openDirFd(
int dfd,
const char *path,
int mode)
1483 return qt_safe_openat(dfd, path, mode);
1487 static int openOrCreateDir(
int dfd,
const char *path,
int openmode = 0)
1490 int fd = openDirFd(dfd, path, openmode);
1491 if (fd >= 0 || errno != ENOENT)
1495 if (mkdirat(dfd, path, 0700) < 0)
1499 return openDirFd(dfd, path, openmode);
1503 QSystemError getTrashDir(
int parentfd, QString targetDir,
const QFileSystemEntry &source,
1506 if (parentfd == AT_FDCWD)
1507 trashPath = targetDir;
1508 QByteArray nativePath = QFile::encodeName(targetDir);
1511 int trashfd = openOrCreateDir(parentfd, nativePath, openmode);
1512 if (trashfd < 0 && errno != ENOENT)
1513 return QSystemError::stdError(errno);
1516 if (QT_STATBUF st; QT_FSTAT(trashfd, &st) < 0)
1517 return QSystemError::stdError(errno);
1518 else if (st.st_uid != getuid())
1519 return QSystemError::stdError(errno);
1521 filesDirFd = openOrCreateDir(trashfd,
"files");
1522 if (filesDirFd >= 0) {
1524 QTemporaryFileName tfn(
"XXXXXX"_L1);
1525 for (
int i = 0; i < 16; ++i) {
1526 QByteArray attempt = tfn.generateNext();
1527 if (linkat(AT_FDCWD, source.nativeFilePath(), filesDirFd, attempt, 0) == 0) {
1528 tempTrashFileName = std::move(attempt);
1531 if (errno != EEXIST)
1542 if (!tempTrashFileName.isEmpty() || errno == EPERM || errno == EMLINK)
1543 infoDirFd = openOrCreateDir(trashfd,
"info");
1546 if (infoDirFd < 0) {
1547 int saved_errno = errno;
1550 return QSystemError::stdError(saved_errno);
1557 QSystemError openMountPointTrashLocation(
const QFileSystemEntry &source,
1558 const QStorageInfo &sourceStorage)
1561
1562
1563
1564
1565
1566
1567
1568
1569
1572 const auto dotTrash =
"/.Trash"_L1;
1573 const QString userID = QString::number(::getuid());
1574 QFileSystemEntry dotTrashDir(sourceStorage.rootPath() + dotTrash);
1577 int genericTrashFd = openDirFd(AT_FDCWD, dotTrashDir.nativeFilePath(),
O_NOFOLLOW);
1579 if (genericTrashFd < 0 && errno != ENOENT && errno != EACCES) {
1581 if (QT_LSTAT(dotTrashDir.nativeFilePath(), &st) == 0 && S_ISLNK(st.st_mode)) {
1583 qCritical(
"Warning: '%s' is a symlink to '%s'",
1584 dotTrashDir.nativeFilePath().constData(),
1585 qt_readlink(dotTrashDir.nativeFilePath()).constData());
1586 error = QSystemError(ELOOP, QSystemError::StandardLibraryError);
1588 }
else if (genericTrashFd >= 0) {
1589 QT_FSTAT(genericTrashFd, &st);
1590 if ((st.st_mode & S_ISVTX) == 0) {
1592 qCritical(
"Warning: '%s' doesn't have sticky bit set!",
1593 dotTrashDir.nativeFilePath().constData());
1594 error = QSystemError(EPERM, QSystemError::StandardLibraryError);
1597
1598
1599
1600
1601
1602
1603
1604
1605 if (error = getTrashDir(genericTrashFd, userID, source,
O_NOFOLLOW); error.ok()) {
1607 trashPath = dotTrashDir.filePath() + u'/' + userID;
1610 QT_CLOSE(genericTrashFd);
1614
1615
1616
1617
1618
1619
1620 if (!isTrashDirOpen())
1621 error = getTrashDir(AT_FDCWD, sourceStorage.rootPath() + dotTrash + u'-' + userID, source,
1624 if (isTrashDirOpen()) {
1625 volumePrefixLength = sourceStorage.rootPath().size();
1626 if (volumePrefixLength == 1)
1627 volumePrefixLength = 0;
1629 ++volumePrefixLength;
1631 return isTrashDirOpen() ? QSystemError() : error;
1634 QSystemError openHomeTrashLocation(
const QFileSystemEntry &source)
1636 QString topDir = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation);
1638 return getTrashDir(AT_FDCWD, topDir +
"/Trash"_L1, source, openmode);
1641 QSystemError findTrashFor(
const QFileSystemEntry &source)
1644
1645
1646
1647
1648
1649 if (QSystemError error = openHomeTrashLocation(source); error.ok())
1650 return QSystemError();
1651 else if (error.errorCode != EXDEV)
1655 const QStorageInfo sourceStorage(source.filePath());
1656 if (!sourceStorage.isValid())
1657 return QSystemError::stdError(ENODEV);
1658 return openMountPointTrashLocation(source, sourceStorage);
1664bool QFileSystemEngine::moveFileToTrash(
const QFileSystemEntry &source,
1665 QFileSystemEntry &newLocation, QSystemError &error)
1667 const QFileSystemEntry sourcePath = [&] {
1668 if (QString path = source.filePath(); path.size() > 1 && path.endsWith(u'/')) {
1670 return absoluteName(QFileSystemEntry(path));
1672 return absoluteName(source);
1674 FreeDesktopTrashOperation op;
1675 if (error = op.findTrashFor(sourcePath); !error.ok())
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698 QString uniqueTrashedName = sourcePath.fileName();
1699 if (!op.tryCreateInfoFile(uniqueTrashedName, error) && error.errorCode == EEXIST) {
1703 if (QT_STATBUF st; Q_LIKELY(QT_STAT(source.nativeFilePath(), &st) == 0)) {
1704 counter = st.st_ino;
1706 error = QSystemError(errno, QSystemError::StandardLibraryError);
1710 QString uniqueTrashBase = std::move(uniqueTrashedName);
1712 uniqueTrashedName = QString::asprintf(
"%ls-%llu", qUtf16Printable(uniqueTrashBase),
1714 if (op.tryCreateInfoFile(uniqueTrashedName, error))
1716 if (error.errorCode != EEXIST)
1723 "Path=" + QUrl::toPercentEncoding(source.filePath().mid(op.volumePrefixLength),
"/") +
"\n"
1724 "DeletionDate=" + QDateTime::currentDateTime().toString(Qt::ISODate).toUtf8()
1726 if (QT_WRITE(op.infoFileFd, info.data(), info.size()) < 0) {
1727 error = QSystemError(errno, QSystemError::StandardLibraryError);
1732
1733
1734
1735
1737 if (op.tempTrashFileName.isEmpty()) {
1739
1740
1741
1742
1743
1744 renamed = renameat(AT_FDCWD, source.nativeFilePath(), op.filesDirFd,
1745 QFile::encodeName(uniqueTrashedName)) == 0;
1747 renamed = renameat(op.filesDirFd, op.tempTrashFileName, op.filesDirFd,
1748 QFile::encodeName(uniqueTrashedName)) == 0;
1750 removeFile(sourcePath, error);
1753 error = QSystemError(errno, QSystemError::StandardLibraryError);
1758 newLocation = QFileSystemEntry(op.trashPath +
"/files/"_L1 + uniqueTrashedName);
1764#if !defined(AT_FDCWD) || defined(Q_OS_QNX) || defined(Q_OS_VXWORKS)
1772bool QFileSystemEngine::supportsRmdirRecursively()
noexcept
1778bool QFileSystemEngine::rmdirRecursively(
const QFileSystemEntry &, QSystemError &error)
1780 error = QSystemError(ENOSYS, QSystemError::StandardLibraryError);
1786bool QFileSystemEngine::supportsRmdirRecursively()
noexcept
1792bool QFileSystemEngine::rmdirRecursively(
const QFileSystemEntry &entry, QSystemError &error)
1794 Q_CHECK_FILE_NAME(entry,
false);
1796# if defined(Q_OS_ANDROID) || defined(Q_OS_BSD4) || defined(Q_OS_LINUX)
1803 constexpr bool NeedsSecondFileDescriptor =
false;
1805 constexpr bool NeedsSecondFileDescriptor =
true;
1813 auto openDirectory = [](
int parentFd, std::string name) {
1814 constexpr int OpenMode = QT_OPEN_RDONLY | O_DIRECTORY | O_NOFOLLOW;
1815 DirInfo di = {
nullptr, -1, parentFd, std::move(name) };
1816 di.dirfd = qt_safe_openat(parentFd, di.name.c_str(), OpenMode);
1821 di.dir = fdopendir(di.dirfd);
1827 if constexpr (NeedsSecondFileDescriptor) {
1828 di.dirfd = qt_safe_openat(parentFd, di.name.c_str(), OpenMode);
1829 if (Q_UNLIKELY(di.dirfd < 0))
1830 closedir(std::exchange(di.dir,
nullptr));
1836 QVarLengthArray<DirInfo, 16> stack;
1837 if (DirInfo di = openDirectory(AT_FDCWD, entry.nativeFilePath().toStdString()); di.dir) {
1838 stack.emplace_back(std::move(di));
1840 if (errno == ENOENT)
1842 error = QSystemError(errno, QSystemError::StandardLibraryError);
1846 while (!stack.isEmpty()) {
1847 DirInfo &top = stack.last();
1848 if (QT_DIRENT *de = QT_READDIR(top.dir)) {
1849 if (de->d_name[0] ==
'.') {
1850 if (de->d_name[1] ==
'\0' || (de->d_name[1] ==
'.' && de->d_name[2] ==
'\0'))
1854 QFileSystemMetaData meta;
1855 meta.fillFromDirEnt(*de);
1858 if (!meta.hasFlags(QFileSystemMetaData::DirectoryType) || !meta.isDirectory()) {
1859 if (unlinkat(top.dirfd, de->d_name, 0) == 0)
1865 if (DirInfo di = openDirectory(top.dirfd, de->d_name); di.dir) {
1867 stack.emplace_back(std::move(di));
1875 if constexpr (NeedsSecondFileDescriptor)
1876 QT_CLOSE(top.dirfd);
1879 int r = unlinkat(top.parentFd, top.name.c_str(), AT_REMOVEDIR);
1880 if (r < 0 && top.parentFd == AT_FDCWD && errno != ENOENT)
1881 error = QSystemError(errno, QSystemError::StandardLibraryError);
1893bool QFileSystemEngine::copyFile(
const QFileSystemEntry &source,
const QFileSystemEntry &target, QSystemError &error)
1897 error = QSystemError(ENOSYS, QSystemError::StandardLibraryError);
1902bool QFileSystemEngine::renameFile(
const QFileSystemEntry &source,
const QFileSystemEntry &target, QSystemError &error)
1904 QFileSystemEntry::NativePath srcPath = source.nativeFilePath();
1905 QFileSystemEntry::NativePath tgtPath = target.nativeFilePath();
1910#if defined(RENAME_NOREPLACE
) && QT_CONFIG(renameat2)
1911 if (renameat2(AT_FDCWD, srcPath, AT_FDCWD, tgtPath, RENAME_NOREPLACE) == 0)
1915 if (errno != EINVAL) {
1916 error = QSystemError(errno, QSystemError::StandardLibraryError);
1920#if defined(Q_OS_DARWIN) && defined(RENAME_EXCL)
1921 if (renameatx_np(AT_FDCWD, srcPath, AT_FDCWD, tgtPath, RENAME_EXCL) == 0)
1923 if (errno != ENOTSUP) {
1924 error = QSystemError(errno, QSystemError::StandardLibraryError);
1929 if (SupportsHardlinking && ::link(srcPath, tgtPath) == 0) {
1930 if (::unlink(srcPath) == 0)
1936 int savedErrno = errno;
1941 error = QSystemError(savedErrno, QSystemError::StandardLibraryError);
1943 }
else if (!SupportsHardlinking) {
1947 if (::access(tgtPath, F_OK) == 0) {
1972 if (::rename(srcPath, tgtPath) == 0)
1976 error = QSystemError(errno, QSystemError::StandardLibraryError);
1981bool QFileSystemEngine::renameOverwriteFile(
const QFileSystemEntry &source,
const QFileSystemEntry &target, QSystemError &error)
1986 if (::rename(source.nativeFilePath().constData(), target.nativeFilePath().constData()) == 0)
1988 error = QSystemError(errno, QSystemError::StandardLibraryError);
1993bool QFileSystemEngine::removeFile(
const QFileSystemEntry &entry, QSystemError &error)
1996 if (unlink(entry.nativeFilePath().constData()) == 0)
1998 error = QSystemError(errno, QSystemError::StandardLibraryError);
2004bool QFileSystemEngine::setPermissions(
const QFileSystemEntry &entry,
2005 QFile::Permissions permissions, QSystemError &error)
2009 mode_t mode = QtPrivate::toMode_t(permissions);
2010 bool success = ::chmod(entry.nativeFilePath().constData(), mode) == 0;
2012 error = QSystemError(errno, QSystemError::StandardLibraryError);
2017bool QFileSystemEngine::setPermissions(
int fd, QFile::Permissions permissions, QSystemError &error)
2019 mode_t mode = QtPrivate::toMode_t(permissions);
2020 bool success = ::fchmod(fd, mode) == 0;
2022 error = QSystemError(errno, QSystemError::StandardLibraryError);
2027bool QFileSystemEngine::setFileTime(
int fd,
const QDateTime &newDate, QFile::FileTime time, QSystemError &error)
2029 if (!newDate.isValid()
2030 || time == QFile::FileBirthTime || time == QFile::FileMetadataChangeTime) {
2031 error = QSystemError(EINVAL, QSystemError::StandardLibraryError);
2035#if QT_CONFIG(futimens)
2037 struct timespec ts[2] = {{0, UTIME_OMIT}, {0, UTIME_OMIT}};
2039 if (time == QFile::FileAccessTime || time == QFile::FileModificationTime) {
2040 const int idx = time == QFile::FileAccessTime ? 0 : 1;
2041 const std::chrono::milliseconds msecs{newDate.toMSecsSinceEpoch()};
2042 ts[idx] = durationToTimespec(msecs);
2045 if (futimens(fd, ts) == -1) {
2046 error = QSystemError(errno, QSystemError::StandardLibraryError);
2053 error = QSystemError(ENOSYS, QSystemError::StandardLibraryError);
2058QString QFileSystemEngine::homePath()
2060 QString home = qEnvironmentVariable(
"HOME");
2063 return QDir::cleanPath(home);
2066QString QFileSystemEngine::rootPath()
2074 QLatin1StringView temp =
_PATH_TMP ""_L1;
2075 static_assert(
_PATH_TMP[0] ==
'/',
"_PATH_TMP needs to be absolute");
2076 static_assert(
_PATH_TMP[1] !=
'\0',
"Are you really sure _PATH_TMP should be the root dir??");
2077 if (temp.endsWith(u'/'))
2082QString QFileSystemEngine::tempPath()
2084#ifdef QT_UNIX_TEMP_PATH_OVERRIDE
2085 return QT_UNIX_TEMP_PATH_OVERRIDE
""_L1;
2087 QString temp = qEnvironmentVariable(
"TMPDIR");
2088# if defined(Q_OS_DARWIN) && !defined(QT_BOOTSTRAPPED)
2089 if (NSString *nsPath; temp.isEmpty() && (nsPath = NSTemporaryDirectory()))
2090 temp = QString::fromCFString((CFStringRef)nsPath);
2093 return nativeTempPath();
2096 if (temp.size() > 1 && temp.endsWith(u'/'))
2099 QFileSystemEntry e(temp, QFileSystemEntry::FromInternalPath{});
2100 return QFileSystemEngine::absoluteName(e).filePath();
2104bool QFileSystemEngine::setCurrentPath(
const QFileSystemEntry &path)
2107 r = QT_CHDIR(path.nativeFilePath().constData());
2111QFileSystemEntry QFileSystemEngine::currentPath()
2113 QFileSystemEntry result;
2114#if defined(__GLIBC__
) && !defined(PATH_MAX)
2115 char *currentName = ::get_current_dir_name();
2117 result = QFileSystemEntry(QByteArray(currentName), QFileSystemEntry::FromNativePath());
2118 ::free(currentName);
2121 char currentName[PATH_MAX+1];
2122 if (::getcwd(currentName, PATH_MAX)) {
2123#if defined(Q_OS_VXWORKS) && defined(VXWORKS_VXSIM)
2124 QByteArray dir(currentName);
2125 if (dir.indexOf(
':') < dir.indexOf(
'/'))
2126 dir.remove(0, dir.indexOf(
':')+1);
2128 qstrncpy(currentName, dir.constData(), PATH_MAX);
2130 result = QFileSystemEntry(QByteArray(currentName), QFileSystemEntry::FromNativePath());
2132# if defined(QT_DEBUG)
2133 if (result.isEmpty())
2134 qWarning(
"QFileSystemEngine::currentPath: getcwd() failed");
2140bool QFileSystemEngine::isCaseSensitive(
const QFileSystemEntry &entry, QFileSystemMetaData &metaData)
2142#if defined(Q_OS_DARWIN)
2143 if (!metaData.hasFlags(QFileSystemMetaData::CaseSensitive))
2144 fillMetaData(entry, metaData, QFileSystemMetaData::CaseSensitive);
2145 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 QFileSystemNativeId nativeIdFromStatBuf(const QT_STATBUF &statResult)
static int qt_statx(const char *, struct statx *)
bool isValid() const noexcept