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);
749 return QFileSystemEntry();
753QFileSystemEntry QFileSystemEngine::absoluteName(
const QFileSystemEntry &entry)
757 if (entry.isAbsolute() && entry.isClean())
760 QByteArray orig = entry.nativeFilePath();
762 if (orig.isEmpty() || !orig.startsWith(
'/')) {
763 QFileSystemEntry cur(currentPath());
764 result = cur.nativeFilePath();
766 if (!orig.isEmpty() && !(orig.size() == 1 && orig[0] ==
'.')) {
767 if (!result.isEmpty() && !result.endsWith(
'/'))
772 if (result.size() == 1 && result[0] ==
'/')
773 return QFileSystemEntry(result, QFileSystemEntry::FromNativePath());
774 const bool isDir = result.endsWith(
'/');
777
778
779
780 QFileSystemEntry resultingEntry(result, QFileSystemEntry::FromNativePath());
781 QString stringVersion = QDir::cleanPath(resultingEntry.filePath());
783 stringVersion.append(u'/');
784 return QFileSystemEntry(stringVersion);
791 char buffer[
sizeof "0123456789abcdef:18446744073709551615"];
792 std::snprintf(buffer,
sizeof(buffer),
"%llx:%llu",
793 qulonglong(volumeId), qulonglong(fileId[0]));
800 id.volumeId = quint64(statResult.st_dev);
801 id.fileId[0] = quint64(statResult.st_ino);
806QFileSystemNativeId QFileSystemEngine::nativeId(
const QFileSystemEntry &entry)
810 QT_STATBUF statResult;
811 if (QT_STAT(entry.nativeFilePath().constData(), &statResult) != 0)
813 return nativeIdFromStatBuf(statResult);
817QFileSystemNativeId QFileSystemEngine::nativeId(
int fd)
819 QT_STATBUF statResult;
820 if (QT_FSTAT(fd, &statResult) != 0) {
821 qErrnoWarning(
"fstat() failed for fd %d", fd);
824 return nativeIdFromStatBuf(statResult);
828QString QFileSystemEngine::resolveUserName(uint userId)
830#if QT_CONFIG(thread) && defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_OPENBSD)
831 long size_max = sysconf(_SC_GETPW_R_SIZE_MAX);
834 QVarLengthArray<
char, 1024> buf(size_max);
837#if !defined(Q_OS_INTEGRITY) && !defined(Q_OS_WASM)
838 struct passwd *pw =
nullptr;
839#if QT_CONFIG(thread) && defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_OPENBSD) && !defined(Q_OS_VXWORKS)
841 getpwuid_r(userId, &entry, buf.data(), buf.size(), &pw);
843 pw = getpwuid(userId);
846 return QFile::decodeName(QByteArray(pw->pw_name));
854QString QFileSystemEngine::resolveGroupName(uint groupId)
856#if QT_CONFIG(thread) && defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_OPENBSD)
857 long size_max = sysconf(_SC_GETPW_R_SIZE_MAX);
860 QVarLengthArray<
char, 1024> buf(size_max);
863#if !defined(Q_OS_INTEGRITY) && !defined(Q_OS_WASM)
864 struct group *gr =
nullptr;
865#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
))
866 size_max = sysconf(_SC_GETGR_R_SIZE_MAX);
869 buf.resize(size_max);
873 for (
long size = size_max; size < 256000; size += size)
877 if (!getgrgid_r(groupId, &entry, buf.data(), buf.size(), &gr)
882 gr = getgrgid(groupId);
885 return QFile::decodeName(QByteArray(gr->gr_name));
892#if defined(Q_OS_DARWIN)
894QString QFileSystemEngine::bundleName(
const QFileSystemEntry &entry)
896 QCFType<CFURLRef> url = CFURLCreateWithFileSystemPath(0, QCFString(entry.filePath()),
897 kCFURLPOSIXPathStyle,
true);
898 if (QCFType<CFDictionaryRef> dict = CFBundleCopyInfoDictionaryForURL(url)) {
899 if (CFTypeRef name = (CFTypeRef)CFDictionaryGetValue(dict, kCFBundleNameKey)) {
900 if (CFGetTypeID(name) == CFStringGetTypeID())
901 return QString::fromCFString((CFStringRef)name);
909bool QFileSystemEngine::fillMetaData(
const QFileSystemEntry &entry, QFileSystemMetaData &data,
910 QFileSystemMetaData::MetaDataFlags what)
918 auto hadBeenDeleted = data.entryFlags & QFileSystemMetaData::WasDeletedAttribute;
920#if defined(Q_OS_DARWIN)
921 if (what & (QFileSystemMetaData::BundleType | QFileSystemMetaData::CaseSensitive)) {
922 if (!data.hasFlags(QFileSystemMetaData::DirectoryType))
923 what |= QFileSystemMetaData::DirectoryType;
925 if (what & QFileSystemMetaData::AliasType)
926 what |= QFileSystemMetaData::LinkType;
929 bool needLstat = what.testAnyFlag(QFileSystemMetaData::LinkType);
932 if (what & QFileSystemMetaData::HiddenAttribute) {
942 if (what & QFileSystemMetaData::PosixStatFlags)
943 what |= QFileSystemMetaData::PosixStatFlags;
945 data.entryFlags &= ~what;
947 const QByteArray nativeFilePath = entry.nativeFilePath();
964 QT_STATBUF statBuffer;
965 struct statx statxBuffer;
970 statResult = qt_lstatx(nativeFilePath, &statxBuffer);
971 if (statResult == -ENOSYS) {
973 statResult = QT_LSTAT(nativeFilePath, &statBuffer);
975 mode = statBuffer.st_mode;
976 }
else if (statResult == 0) {
978 mode = statxBuffer.stx_mode;
981 if (statResult >= 0) {
984 Q_ASSERT(statResult == 0);
985 if (statBuffer.st_flags & UF_HIDDEN)
986 data.entryFlags |= QFileSystemMetaData::HiddenAttribute;
987 data.knownFlagsMask |= QFileSystemMetaData::HiddenAttribute;
992 data.entryFlags |= QFileSystemMetaData::LinkType;
997 data.fillFromStatxBuf(statxBuffer);
999 data.fillFromStatBuf(statBuffer);
1005 data.knownFlagsMask |= QFileSystemMetaData::ExistsAttribute;
1008 data.knownFlagsMask |= QFileSystemMetaData::LinkType;
1012 if (statResult == -1 && (what & QFileSystemMetaData::PosixStatFlags)) {
1013 if (entryErrno == 0) {
1014 data.entryFlags &= ~QFileSystemMetaData::PosixStatFlags;
1015 statResult = qt_statx(nativeFilePath, &statxBuffer);
1016 if (statResult == -ENOSYS) {
1018 statResult = QT_STAT(nativeFilePath, &statBuffer);
1019 if (statResult == 0)
1020 data.fillFromStatBuf(statBuffer);
1021 }
else if (statResult == 0) {
1022 data.fillFromStatxBuf(statxBuffer);
1026 if (statResult != 0) {
1028 data.birthTime_ = 0;
1029 data.metadataChangeTime_ = 0;
1030 data.modificationTime_ = 0;
1031 data.accessTime_ = 0;
1033 data.userId_ = (uint) -2;
1034 data.groupId_ = (uint) -2;
1037 data.knownFlagsMask |= QFileSystemMetaData::PosixStatFlags
1038 | QFileSystemMetaData::ExistsAttribute;
1043 if (what & (QFileSystemMetaData::UserPermissions | QFileSystemMetaData::ExistsAttribute)) {
1044#if defined(Q_OS_VXWORKS)
1045 struct statfs statBuf;
1046 if (statfs(nativeFilePath, &statBuf) == 0) {
1047 if (statBuf.f_type != NFSV2_MAGIC && statBuf.f_type != NFSV3_MAGIC &&
1048 statBuf.f_type != HRFS_MAGIC) {
1049#if __has_include(<dosFsLib.h>)
1050 if (data.entryFlags & QFileSystemMetaData::OwnerWritePermission) {
1051 data.entryFlags |= QFileSystemMetaData::UserWritePermission;
1053 if (data.entryFlags & QFileSystemMetaData::OwnerExecutePermission) {
1054 data.entryFlags |= QFileSystemMetaData::UserExecutePermission;
1057 data.entryFlags |= QFileSystemMetaData::UserReadPermission |
1058 QFileSystemMetaData::ExistsAttribute;
1062#if defined(QT_DEBUG)
1066 statResult = QT_STAT(nativeFilePath, &statBuffer);
1067 if (statResult == 0) {
1068 data.entryFlags |= QFileSystemMetaData::UserReadPermission |
1069 QFileSystemMetaData::ExistsAttribute;
1070 data.entryFlags &= ~QFileSystemMetaData::WasDeletedAttribute;
1077 auto checkAccess = [&](QFileSystemMetaData::MetaDataFlag flag,
int mode) {
1078 if (entryErrno != 0 || (what & flag) == 0)
1080 if (QT_ACCESS(nativeFilePath, mode) == 0) {
1082 data.entryFlags |= flag | QFileSystemMetaData::ExistsAttribute;
1083 }
else if (errno != EACCES && errno != EROFS) {
1087 checkAccess(QFileSystemMetaData::UserReadPermission, R_OK);
1088 checkAccess(QFileSystemMetaData::UserWritePermission, W_OK);
1089 checkAccess(QFileSystemMetaData::UserExecutePermission, X_OK);
1092 if (entryErrno == 0 && (data.entryFlags & QFileSystemMetaData::ExistsAttribute) == 0) {
1093 if (QT_ACCESS(nativeFilePath, F_OK) == -1)
1096 data.entryFlags |= QFileSystemMetaData::ExistsAttribute;
1099 data.knownFlagsMask |= (what & QFileSystemMetaData::UserPermissions) |
1100 QFileSystemMetaData::ExistsAttribute;
1103#if defined(Q_OS_DARWIN)
1104 QCFType<CFURLRef> cachedUrl;
1105 if (what & QFileSystemMetaData::AliasType) {
1106 if (entryErrno == 0 && hasResourcePropertyFlag(data, entry, kCFURLIsAliasFileKey, cachedUrl)) {
1108 if (!(data.entryFlags & QFileSystemMetaData::LinkType))
1109 data.entryFlags |= QFileSystemMetaData::AliasType;
1111 data.knownFlagsMask |= QFileSystemMetaData::AliasType;
1114 if (what & QFileSystemMetaData::BundleType) {
1115 if (entryErrno == 0 && isPackage(data, entry, cachedUrl))
1116 data.entryFlags |= QFileSystemMetaData::BundleType;
1118 data.knownFlagsMask |= QFileSystemMetaData::BundleType;
1121 if (what & QFileSystemMetaData::CaseSensitive) {
1122 if (entryErrno == 0 && hasResourcePropertyFlag(data, entry,
1123 kCFURLVolumeSupportsCaseSensitiveNamesKey, cachedUrl))
1124 data.entryFlags |= QFileSystemMetaData::CaseSensitive;
1125 data.knownFlagsMask |= QFileSystemMetaData::CaseSensitive;
1129 if (what & QFileSystemMetaData::HiddenAttribute
1130 && !data.isHidden()) {
1133 qsizetype lastSlash = nativeFilePath.size();
1135 while (lastSlash && nativeFilePath.at(lastSlash - 1) ==
'/')
1137 while (lastSlash && nativeFilePath.at(lastSlash - 1) !=
'/')
1141 if (nativeFilePath.at(lastSlash + 1) ==
'.')
1142 data.entryFlags |= QFileSystemMetaData::HiddenAttribute;
1143 data.knownFlagsMask |= QFileSystemMetaData::HiddenAttribute;
1146 if (entryErrno != 0) {
1147 what &= ~QFileSystemMetaData::LinkType;
1148 data.clearFlags(what);
1151 data.entryFlags |= hadBeenDeleted;
1152 data.knownFlagsMask |= hadBeenDeleted;
1160auto QFileSystemEngine::cloneFile(
int srcfd,
int dstfd,
const QFileSystemMetaData &knownData) -> TriStateResult
1162 QT_STATBUF statBuffer;
1163 if (knownData.hasFlags(QFileSystemMetaData::PosixStatFlags) &&
1164 knownData.isFile()) {
1165 statBuffer.st_mode = S_IFREG;
1166 }
else if (knownData.hasFlags(QFileSystemMetaData::PosixStatFlags) &&
1167 knownData.isDirectory()) {
1169 return TriStateResult::Failed;
1170 }
else if (QT_FSTAT(srcfd, &statBuffer) == -1) {
1172 return TriStateResult::Failed;
1173 }
else if (!S_ISREG((statBuffer.st_mode))) {
1175 return TriStateResult::NotSupported;
1178 [[maybe_unused]]
auto destinationIsEmpty = [dstfd]() {
1179 QT_STATBUF statBuffer;
1180 return QT_FSTAT(dstfd, &statBuffer) == 0 && statBuffer.st_size == 0;
1182 Q_ASSERT(destinationIsEmpty());
1184#if defined(Q_OS_LINUX)
1186 if (::ioctl(dstfd, FICLONE, srcfd) == 0)
1187 return TriStateResult::Success;
1188#elif defined(Q_OS_DARWIN)
1190 if (fcopyfile(srcfd, dstfd,
nullptr, COPYFILE_DATA | COPYFILE_STAT) == 0)
1191 return TriStateResult::Success;
1195 return TriStateResult::NotSupported;
1197 return TriStateResult::Failed;
1200#if QT_CONFIG(copy_file_range)
1204 QT_OFF_T srcoffset = 0;
1207 copied = ::copy_file_range(srcfd, &srcoffset, dstfd,
nullptr, SSIZE_MAX, 0);
1208 }
while (copied > 0 || (copied < 0 && errno == EINTR));
1210 return TriStateResult::Success;
1213 copied = ftruncate(dstfd, 0);
1214 return TriStateResult::Failed;
1227 return TriStateResult::Failed;
1231#if defined(Q_OS_LINUX)
1234 const size_t SendfileSize = 0x7ffff000;
1236 ssize_t n = ::sendfile(dstfd, srcfd,
nullptr, SendfileSize);
1241 return TriStateResult::Failed;
1245 return TriStateResult::NotSupported;
1249 n = ::sendfile(dstfd, srcfd,
nullptr, SendfileSize);
1252 n = ftruncate(dstfd, 0);
1253 n = lseek(srcfd, 0, SEEK_SET);
1254 n = lseek(dstfd, 0, SEEK_SET);
1255 return TriStateResult::Failed;
1259 return TriStateResult::Success;
1262 return TriStateResult::NotSupported;
1273 auto tryMkDir = [&path, mode]() -> QSystemError {
1274 if (QT_MKDIR(path, mode) == 0) {
1276 forceRequestedPermissionsOnVxWorks(path, mode);
1281 if (errno == EISDIR)
1283 if (errno == EEXIST || errno == EROFS) {
1287 if (QT_STAT(path.constData(), &st) != 0)
1288 return QSystemError::stdError(errno);
1289 const bool isDir = (st.st_mode & S_IFMT) == S_IFDIR;
1290 return isDir ? QSystemError{} : QSystemError::stdError(EEXIST);
1292 return QSystemError::stdError(errno);
1295 QSystemError result = tryMkDir();
1300 if (result.errorCode != ENOENT)
1303 qsizetype slash = path.lastIndexOf(
'/');
1304 while (slash > 0 && path[slash - 1] ==
'/')
1311 QByteArray parentPath = path.first(slash);
1312 if (result = createDirectoryWithParents(parentPath, mode); !result.ok())
1319bool QFileSystemEngine::mkpath(
const QFileSystemEntry &entry,
1320 std::optional<QFile::Permissions> permissions)
1322 QByteArray path = entry.nativeFilePath();
1325 mode_t mode = permissions ? QtPrivate::toMode_t(*permissions) : 0777;
1326 return createDirectoryWithParents(removeTrailingSlashes(path), mode).ok();
1329bool QFileSystemEngine::mkdir(
const QFileSystemEntry &entry,
1330 std::optional<QFile::Permissions> permissions)
1332 QByteArray path = entry.nativeFilePath();
1335 mode_t mode = permissions ? QtPrivate::toMode_t(*permissions) : 0777;
1336 auto result = QT_MKDIR(removeTrailingSlashes(path), mode) == 0;
1337#if defined(Q_OS_VXWORKS)
1339 forceRequestedPermissionsOnVxWorks(path, mode);
1344bool QFileSystemEngine::rmdir(
const QFileSystemEntry &entry)
1346 const QByteArray path = entry.nativeFilePath();
1348 return ::rmdir(path.constData()) == 0;
1351bool QFileSystemEngine::rmpath(
const QFileSystemEntry &entry)
1353 QByteArray path = QFile::encodeName(QDir::cleanPath(entry.filePath()));
1356 if (::rmdir(path.constData()) != 0)
1359 const char sep = QDir::separator().toLatin1();
1360 qsizetype slash = path.lastIndexOf(sep);
1362 for (; slash > 0; slash = path.lastIndexOf(sep)) {
1363 path.truncate(slash);
1364 if (::rmdir(path.constData()) != 0)
1372bool QFileSystemEngine::createLink(
const QFileSystemEntry &source,
const QFileSystemEntry &target, QSystemError &error)
1377 if (::symlink(source.nativeFilePath().constData(), target.nativeFilePath().constData()) == 0)
1379 error = QSystemError(errno, QSystemError::StandardLibraryError);
1383#if defined(QT_BOOTSTRAPPED) || !defined(AT_FDCWD) || defined(Q_OS_ANDROID) || !QT_CONFIG(datestring) || defined(Q_OS_VXWORKS)
1386bool QFileSystemEngine::supportsMoveFileToTrash()
1392bool QFileSystemEngine::moveFileToTrash(
const QFileSystemEntry &, QFileSystemEntry &,
1393 QSystemError &error)
1395 error = QSystemError(ENOSYS, QSystemError::StandardLibraryError);
1398#elif defined(Q_OS_DARWIN)
1400#elif defined(Q_OS_HARMONY)
1404
1405
1407bool QFileSystemEngine::supportsMoveFileToTrash()
1413struct FreeDesktopTrashOperation
1416
1417
1419 int filesDirFd = -1;
1421 qsizetype volumePrefixLength = 0;
1424 QByteArray tempTrashFileName;
1425 QByteArray infoFilePath;
1427 int infoFileFd = -1;
1428 ~FreeDesktopTrashOperation()
1433 constexpr bool isTrashDirOpen()
const {
return filesDirFd != -1 && infoDirFd != -1; }
1437 int savedErrno = errno;
1438 if (infoFileFd != -1) {
1439 Q_ASSERT(infoDirFd != -1);
1440 Q_ASSERT(!infoFilePath.isEmpty());
1441 Q_ASSERT(!trashPath.isEmpty());
1443 QT_CLOSE(infoFileFd);
1444 unlinkat(infoDirFd, infoFilePath, 0);
1447 if (!tempTrashFileName.isEmpty()) {
1448 Q_ASSERT(filesDirFd != -1);
1449 unlinkat(filesDirFd, tempTrashFileName, 0);
1451 if (filesDirFd >= 0)
1452 QT_CLOSE(filesDirFd);
1454 QT_CLOSE(infoDirFd);
1455 filesDirFd = infoDirFd = -1;
1459 bool tryCreateInfoFile(
const QString &filePath, QSystemError &error)
1461 QByteArray p = QFile::encodeName(filePath) +
".trashinfo";
1462 infoFileFd = qt_safe_openat(infoDirFd, p, QT_OPEN_RDWR | QT_OPEN_CREAT | QT_OPEN_EXCL, 0666);
1463 if (infoFileFd < 0) {
1464 error = QSystemError(errno, QSystemError::StandardLibraryError);
1467 infoFilePath = std::move(p);
1473 QT_CLOSE(infoFileFd);
1475 tempTrashFileName = {};
1479 static int openDirFd(
int dfd,
const char *path,
int mode)
1482 return qt_safe_openat(dfd, path, mode);
1486 static int openOrCreateDir(
int dfd,
const char *path,
int openmode = 0)
1489 int fd = openDirFd(dfd, path, openmode);
1490 if (fd >= 0 || errno != ENOENT)
1494 if (mkdirat(dfd, path, 0700) < 0)
1498 return openDirFd(dfd, path, openmode);
1502 QSystemError getTrashDir(
int parentfd, QString targetDir,
const QFileSystemEntry &source,
1505 if (parentfd == AT_FDCWD)
1506 trashPath = targetDir;
1507 QByteArray nativePath = QFile::encodeName(targetDir);
1510 int trashfd = openOrCreateDir(parentfd, nativePath, openmode);
1511 if (trashfd < 0 && errno != ENOENT)
1512 return QSystemError::stdError(errno);
1515 if (QT_STATBUF st; QT_FSTAT(trashfd, &st) < 0)
1516 return QSystemError::stdError(errno);
1517 else if (st.st_uid != getuid())
1518 return QSystemError::stdError(errno);
1520 filesDirFd = openOrCreateDir(trashfd,
"files");
1521 if (filesDirFd >= 0) {
1523 QTemporaryFileName tfn(
"XXXXXX"_L1);
1524 for (
int i = 0; i < 16; ++i) {
1525 QByteArray attempt = tfn.generateNext();
1526 if (linkat(AT_FDCWD, source.nativeFilePath(), filesDirFd, attempt, 0) == 0) {
1527 tempTrashFileName = std::move(attempt);
1530 if (errno != EEXIST)
1541 if (!tempTrashFileName.isEmpty() || errno == EPERM || errno == EMLINK)
1542 infoDirFd = openOrCreateDir(trashfd,
"info");
1545 if (infoDirFd < 0) {
1546 int saved_errno = errno;
1549 return QSystemError::stdError(saved_errno);
1556 QSystemError openMountPointTrashLocation(
const QFileSystemEntry &source,
1557 const QStorageInfo &sourceStorage)
1560
1561
1562
1563
1564
1565
1566
1567
1568
1571 const auto dotTrash =
"/.Trash"_L1;
1572 const QString userID = QString::number(::getuid());
1573 QFileSystemEntry dotTrashDir(sourceStorage.rootPath() + dotTrash);
1576 int genericTrashFd = openDirFd(AT_FDCWD, dotTrashDir.nativeFilePath(),
O_NOFOLLOW);
1578 if (genericTrashFd < 0 && errno != ENOENT && errno != EACCES) {
1580 if (QT_LSTAT(dotTrashDir.nativeFilePath(), &st) == 0 && S_ISLNK(st.st_mode)) {
1582 qCritical(
"Warning: '%s' is a symlink to '%s'",
1583 dotTrashDir.nativeFilePath().constData(),
1584 qt_readlink(dotTrashDir.nativeFilePath()).constData());
1585 error = QSystemError(ELOOP, QSystemError::StandardLibraryError);
1587 }
else if (genericTrashFd >= 0) {
1588 QT_FSTAT(genericTrashFd, &st);
1589 if ((st.st_mode & S_ISVTX) == 0) {
1591 qCritical(
"Warning: '%s' doesn't have sticky bit set!",
1592 dotTrashDir.nativeFilePath().constData());
1593 error = QSystemError(EPERM, QSystemError::StandardLibraryError);
1596
1597
1598
1599
1600
1601
1602
1603
1604 if (error = getTrashDir(genericTrashFd, userID, source,
O_NOFOLLOW); error.ok()) {
1606 trashPath = dotTrashDir.filePath() + u'/' + userID;
1609 QT_CLOSE(genericTrashFd);
1613
1614
1615
1616
1617
1618
1619 if (!isTrashDirOpen())
1620 error = getTrashDir(AT_FDCWD, sourceStorage.rootPath() + dotTrash + u'-' + userID, source,
1623 if (isTrashDirOpen()) {
1624 volumePrefixLength = sourceStorage.rootPath().size();
1625 if (volumePrefixLength == 1)
1626 volumePrefixLength = 0;
1628 ++volumePrefixLength;
1630 return isTrashDirOpen() ? QSystemError() : error;
1633 QSystemError openHomeTrashLocation(
const QFileSystemEntry &source)
1635 QString topDir = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation);
1637 return getTrashDir(AT_FDCWD, topDir +
"/Trash"_L1, source, openmode);
1640 QSystemError findTrashFor(
const QFileSystemEntry &source)
1643
1644
1645
1646
1647
1648 if (QSystemError error = openHomeTrashLocation(source); error.ok())
1649 return QSystemError();
1650 else if (error.errorCode != EXDEV)
1654 const QStorageInfo sourceStorage(source.filePath());
1655 if (!sourceStorage.isValid())
1656 return QSystemError::stdError(ENODEV);
1657 return openMountPointTrashLocation(source, sourceStorage);
1663bool QFileSystemEngine::moveFileToTrash(
const QFileSystemEntry &source,
1664 QFileSystemEntry &newLocation, QSystemError &error)
1666 const QFileSystemEntry sourcePath = [&] {
1667 if (QString path = source.filePath(); path.size() > 1 && path.endsWith(u'/')) {
1669 return absoluteName(QFileSystemEntry(path));
1671 return absoluteName(source);
1673 FreeDesktopTrashOperation op;
1674 if (error = op.findTrashFor(sourcePath); !error.ok())
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697 QString uniqueTrashedName = sourcePath.fileName();
1698 if (!op.tryCreateInfoFile(uniqueTrashedName, error) && error.errorCode == EEXIST) {
1702 if (QT_STATBUF st; Q_LIKELY(QT_STAT(source.nativeFilePath(), &st) == 0)) {
1703 counter = st.st_ino;
1705 error = QSystemError(errno, QSystemError::StandardLibraryError);
1709 QString uniqueTrashBase = std::move(uniqueTrashedName);
1711 uniqueTrashedName = QString::asprintf(
"%ls-%llu", qUtf16Printable(uniqueTrashBase),
1713 if (op.tryCreateInfoFile(uniqueTrashedName, error))
1715 if (error.errorCode != EEXIST)
1722 "Path=" + QUrl::toPercentEncoding(source.filePath().mid(op.volumePrefixLength),
"/") +
"\n"
1723 "DeletionDate=" + QDateTime::currentDateTime().toString(Qt::ISODate).toUtf8()
1725 if (QT_WRITE(op.infoFileFd, info.data(), info.size()) < 0) {
1726 error = QSystemError(errno, QSystemError::StandardLibraryError);
1731
1732
1733
1734
1736 if (op.tempTrashFileName.isEmpty()) {
1738
1739
1740
1741
1742
1743 renamed = renameat(AT_FDCWD, source.nativeFilePath(), op.filesDirFd,
1744 QFile::encodeName(uniqueTrashedName)) == 0;
1746 renamed = renameat(op.filesDirFd, op.tempTrashFileName, op.filesDirFd,
1747 QFile::encodeName(uniqueTrashedName)) == 0;
1749 removeFile(sourcePath, error);
1752 error = QSystemError(errno, QSystemError::StandardLibraryError);
1757 newLocation = QFileSystemEntry(op.trashPath +
"/files/"_L1 + uniqueTrashedName);
1763#if !defined(AT_FDCWD) || defined(Q_OS_QNX) || defined(Q_OS_VXWORKS)
1771bool QFileSystemEngine::supportsRmdirRecursively()
noexcept
1777bool QFileSystemEngine::rmdirRecursively(
const QFileSystemEntry &, QSystemError &error)
1779 error = QSystemError(ENOSYS, QSystemError::StandardLibraryError);
1785bool QFileSystemEngine::supportsRmdirRecursively()
noexcept
1791bool QFileSystemEngine::rmdirRecursively(
const QFileSystemEntry &entry, QSystemError &error)
1793 Q_CHECK_FILE_NAME(entry,
false);
1795# if defined(Q_OS_ANDROID) || defined(Q_OS_BSD4) || defined(Q_OS_LINUX)
1802 constexpr bool NeedsSecondFileDescriptor =
false;
1804 constexpr bool NeedsSecondFileDescriptor =
true;
1812 auto openDirectory = [](
int parentFd, std::string name) {
1813 constexpr int OpenMode = QT_OPEN_RDONLY | O_DIRECTORY | O_NOFOLLOW;
1814 DirInfo di = {
nullptr, -1, parentFd, std::move(name) };
1815 di.dirfd = qt_safe_openat(parentFd, di.name.c_str(), OpenMode);
1820 di.dir = fdopendir(di.dirfd);
1826 if constexpr (NeedsSecondFileDescriptor) {
1827 di.dirfd = qt_safe_openat(parentFd, di.name.c_str(), OpenMode);
1828 if (Q_UNLIKELY(di.dirfd < 0))
1829 closedir(std::exchange(di.dir,
nullptr));
1835 QVarLengthArray<DirInfo, 16> stack;
1836 if (DirInfo di = openDirectory(AT_FDCWD, entry.nativeFilePath().toStdString()); di.dir) {
1837 stack.emplace_back(std::move(di));
1839 if (errno == ENOENT)
1841 error = QSystemError(errno, QSystemError::StandardLibraryError);
1845 while (!stack.isEmpty()) {
1846 DirInfo &top = stack.last();
1847 if (QT_DIRENT *de = QT_READDIR(top.dir)) {
1848 if (de->d_name[0] ==
'.') {
1849 if (de->d_name[1] ==
'\0' || (de->d_name[1] ==
'.' && de->d_name[2] ==
'\0'))
1853 QFileSystemMetaData meta;
1854 meta.fillFromDirEnt(*de);
1857 if (!meta.hasFlags(QFileSystemMetaData::DirectoryType) || !meta.isDirectory()) {
1858 if (unlinkat(top.dirfd, de->d_name, 0) == 0)
1864 if (DirInfo di = openDirectory(top.dirfd, de->d_name); di.dir) {
1866 stack.emplace_back(std::move(di));
1874 if constexpr (NeedsSecondFileDescriptor)
1875 QT_CLOSE(top.dirfd);
1878 int r = unlinkat(top.parentFd, top.name.c_str(), AT_REMOVEDIR);
1879 if (r < 0 && top.parentFd == AT_FDCWD && errno != ENOENT)
1880 error = QSystemError(errno, QSystemError::StandardLibraryError);
1892bool QFileSystemEngine::copyFile(
const QFileSystemEntry &source,
const QFileSystemEntry &target, QSystemError &error)
1896 error = QSystemError(ENOSYS, QSystemError::StandardLibraryError);
1901bool QFileSystemEngine::renameFile(
const QFileSystemEntry &source,
const QFileSystemEntry &target, QSystemError &error)
1903 QFileSystemEntry::NativePath srcPath = source.nativeFilePath();
1904 QFileSystemEntry::NativePath tgtPath = target.nativeFilePath();
1909#if defined(RENAME_NOREPLACE
) && QT_CONFIG(renameat2)
1910 if (renameat2(AT_FDCWD, srcPath, AT_FDCWD, tgtPath, RENAME_NOREPLACE) == 0)
1914 if (errno != EINVAL) {
1915 error = QSystemError(errno, QSystemError::StandardLibraryError);
1919#if defined(Q_OS_DARWIN) && defined(RENAME_EXCL)
1920 if (renameatx_np(AT_FDCWD, srcPath, AT_FDCWD, tgtPath, RENAME_EXCL) == 0)
1922 if (errno != ENOTSUP) {
1923 error = QSystemError(errno, QSystemError::StandardLibraryError);
1928 if (SupportsHardlinking && ::link(srcPath, tgtPath) == 0) {
1929 if (::unlink(srcPath) == 0)
1935 int savedErrno = errno;
1940 error = QSystemError(savedErrno, QSystemError::StandardLibraryError);
1942 }
else if (!SupportsHardlinking) {
1946 if (::access(tgtPath, F_OK) == 0) {
1971 if (::rename(srcPath, tgtPath) == 0)
1975 error = QSystemError(errno, QSystemError::StandardLibraryError);
1980bool QFileSystemEngine::renameOverwriteFile(
const QFileSystemEntry &source,
const QFileSystemEntry &target, QSystemError &error)
1985 if (::rename(source.nativeFilePath().constData(), target.nativeFilePath().constData()) == 0)
1987 error = QSystemError(errno, QSystemError::StandardLibraryError);
1992bool QFileSystemEngine::removeFile(
const QFileSystemEntry &entry, QSystemError &error)
1995 if (unlink(entry.nativeFilePath().constData()) == 0)
1997 error = QSystemError(errno, QSystemError::StandardLibraryError);
2003bool QFileSystemEngine::setPermissions(
const QFileSystemEntry &entry,
2004 QFile::Permissions permissions, QSystemError &error)
2008 mode_t mode = QtPrivate::toMode_t(permissions);
2009 bool success = ::chmod(entry.nativeFilePath().constData(), mode) == 0;
2011 error = QSystemError(errno, QSystemError::StandardLibraryError);
2016bool QFileSystemEngine::setPermissions(
int fd, QFile::Permissions permissions, QSystemError &error)
2018 mode_t mode = QtPrivate::toMode_t(permissions);
2019 bool success = ::fchmod(fd, mode) == 0;
2021 error = QSystemError(errno, QSystemError::StandardLibraryError);
2026bool QFileSystemEngine::setFileTime(
int fd,
const QDateTime &newDate, QFile::FileTime time, QSystemError &error)
2028 if (!newDate.isValid()
2029 || time == QFile::FileBirthTime || time == QFile::FileMetadataChangeTime) {
2030 error = QSystemError(EINVAL, QSystemError::StandardLibraryError);
2034#if QT_CONFIG(futimens)
2036 struct timespec ts[2] = {{0, UTIME_OMIT}, {0, UTIME_OMIT}};
2038 if (time == QFile::FileAccessTime || time == QFile::FileModificationTime) {
2039 const int idx = time == QFile::FileAccessTime ? 0 : 1;
2040 const std::chrono::milliseconds msecs{newDate.toMSecsSinceEpoch()};
2041 ts[idx] = durationToTimespec(msecs);
2044 if (futimens(fd, ts) == -1) {
2045 error = QSystemError(errno, QSystemError::StandardLibraryError);
2052 error = QSystemError(ENOSYS, QSystemError::StandardLibraryError);
2057QString QFileSystemEngine::homePath()
2059 QString home = qEnvironmentVariable(
"HOME");
2062 return QDir::cleanPath(home);
2065QString QFileSystemEngine::rootPath()
2073 QLatin1StringView temp =
_PATH_TMP ""_L1;
2074 static_assert(
_PATH_TMP[0] ==
'/',
"_PATH_TMP needs to be absolute");
2075 static_assert(
_PATH_TMP[1] !=
'\0',
"Are you really sure _PATH_TMP should be the root dir??");
2076 if (temp.endsWith(u'/'))
2081QString QFileSystemEngine::tempPath()
2083#ifdef QT_UNIX_TEMP_PATH_OVERRIDE
2084 return QT_UNIX_TEMP_PATH_OVERRIDE
""_L1;
2086 QString temp = qEnvironmentVariable(
"TMPDIR");
2087# if defined(Q_OS_DARWIN) && !defined(QT_BOOTSTRAPPED)
2088 if (NSString *nsPath; temp.isEmpty() && (nsPath = NSTemporaryDirectory()))
2089 temp = QString::fromCFString((CFStringRef)nsPath);
2092 return nativeTempPath();
2095 if (temp.size() > 1 && temp.endsWith(u'/'))
2098 QFileSystemEntry e(temp, QFileSystemEntry::FromInternalPath{});
2099 return QFileSystemEngine::absoluteName(e).filePath();
2103bool QFileSystemEngine::setCurrentPath(
const QFileSystemEntry &path)
2106 r = QT_CHDIR(path.nativeFilePath().constData());
2110QFileSystemEntry QFileSystemEngine::currentPath()
2112 QFileSystemEntry result;
2113#if defined(__GLIBC__
) && !defined(PATH_MAX)
2114 char *currentName = ::get_current_dir_name();
2116 result = QFileSystemEntry(QByteArray(currentName), QFileSystemEntry::FromNativePath());
2117 ::free(currentName);
2120 char currentName[PATH_MAX+1];
2121 if (::getcwd(currentName, PATH_MAX)) {
2122#if defined(Q_OS_VXWORKS) && defined(VXWORKS_VXSIM)
2123 QByteArray dir(currentName);
2124 if (dir.indexOf(
':') < dir.indexOf(
'/'))
2125 dir.remove(0, dir.indexOf(
':')+1);
2127 qstrncpy(currentName, dir.constData(), PATH_MAX);
2129 result = QFileSystemEntry(QByteArray(currentName), QFileSystemEntry::FromNativePath());
2131# if defined(QT_DEBUG)
2132 if (result.isEmpty())
2133 qWarning(
"QFileSystemEngine::currentPath: getcwd() failed");
2139bool QFileSystemEngine::isCaseSensitive(
const QFileSystemEntry &entry, QFileSystemMetaData &metaData)
2141#if defined(Q_OS_DARWIN)
2142 if (!metaData.hasFlags(QFileSystemMetaData::CaseSensitive))
2143 fillMetaData(entry, metaData, QFileSystemMetaData::CaseSensitive);
2144 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