7#include "qplatformdefs.h"
12#include <QtCore/qoperatingsystemversion.h>
13#include <QtCore/private/qcore_unix_p.h>
14#include <QtCore/private/qfiledevice_p.h>
15#include <QtCore/private/qfunctions_p.h>
16#include <QtCore/qvarlengtharray.h>
17#ifndef QT_BOOTSTRAPPED
18# include <QtCore/qscopedvaluerollback.h>
19# include <QtCore/qstandardpaths.h>
20# include <QtCore/private/qtemporaryfile_p.h>
38# define _PATH_TMP "/tmp"
50#if defined(Q_OS_DARWIN)
51# include <QtCore/private/qcore_mac_p.h>
52# include <CoreFoundation/CFBundle.h>
53# include <UniformTypeIdentifiers/UTType.h>
54# include <UniformTypeIdentifiers/UTCoreTypes.h>
55# include <Foundation/Foundation.h>
60#include <CoreServices/CoreServices.h>
63#if defined(QT_PLATFORM_UIKIT)
64#include <MobileCoreServices/MobileCoreServices.h>
67#if defined(Q_OS_LINUX)
68# include <sys/ioctl.h>
69# include <sys/sendfile.h>
74# define FICLONE _IOW(0x94
, 9
, int)
78#if defined(Q_OS_VXWORKS)
79# include <sys/statfs.h>
80# if __has_include(<dosFsLib.h>)
85#if defined(Q_OS_ANDROID)
89# undef STATX_BASIC_STATS
108using namespace Qt::StringLiterals;
113 while (path.size() > 1 && path.endsWith(
'/'))
120#if defined(Q_OS_ANDROID) || defined(Q_OS_HARMONY)
123 SupportsHardlinking =
false
129#if defined(Q_OS_DARWIN)
130static inline bool hasResourcePropertyFlag(
const QFileSystemMetaData &data,
131 const QFileSystemEntry &entry,
132 CFStringRef key, QCFType<CFURLRef> &url)
135 QCFString path = CFStringCreateWithFileSystemRepresentation(0,
136 entry.nativeFilePath().constData());
140 url = CFURLCreateWithFileSystemPath(0, path, kCFURLPOSIXPathStyle,
141 data.hasFlags(QFileSystemMetaData::DirectoryType));
147 if (CFURLCopyResourcePropertyForKey(url, key, &value, NULL)) {
148 if (value == kCFBooleanTrue)
155static bool isPackage(
const QFileSystemMetaData &data,
const QFileSystemEntry &entry,
156 QCFType<CFURLRef> &cachedUrl)
158 if (!data.isDirectory())
161 QFileInfo info(entry.filePath());
162 QString suffix = info.suffix();
164 if (suffix.length() > 0) {
166 const auto *utType = [UTType typeWithFilenameExtension:suffix.toNSString()];
167 if ([utType conformsToType:UTTypeBundle])
171 QCFType<CFStringRef> path = entry.filePath().toCFString();
172 QCFType<CFURLRef> url = CFURLCreateWithFileSystemPath(0, path, kCFURLPOSIXPathStyle,
true);
174 UInt32 type, creator;
176 if (CFBundleGetPackageInfoInDirectory(url, &type, &creator))
181 QCFType<CFURLRef> application = LSCopyDefaultApplicationURLForURL(url,
182 kLSRolesEditor | kLSRolesViewer,
nullptr);
185 QCFType<CFBundleRef> bundle = CFBundleCreate(kCFAllocatorDefault, application);
186 CFStringRef identifier = CFBundleGetIdentifier(bundle);
187 QString applicationId = QString::fromCFString(identifier);
188 if (applicationId !=
"com.apple.finder"_L1)
195 return hasResourcePropertyFlag(data, entry, kCFURLIsPackageKey, cachedUrl);
200static inline void forceRequestedPermissionsOnVxWorks(QByteArray dirName, mode_t mode)
210qint64 time_t_toMsecs(time_t t)
212 using namespace std::chrono;
213 return milliseconds{seconds{t}}.count();
217[[
maybe_unused]] qint64 atime(
const QT_STATBUF &statBuffer, ulong)
219 return time_t_toMsecs(statBuffer.st_atime);
221[[
maybe_unused]] qint64 birthtime(
const QT_STATBUF &, ulong)
225[[
maybe_unused]] qint64 ctime(
const QT_STATBUF &statBuffer, ulong)
227 return time_t_toMsecs(statBuffer.st_ctime);
229[[
maybe_unused]] qint64 mtime(
const QT_STATBUF &statBuffer, ulong)
231 return time_t_toMsecs(statBuffer.st_mtime);
237qint64 timespecToMSecs(
const T &spec)
239 using namespace std::chrono;
240 const nanoseconds nsecs = seconds{spec.tv_sec} + nanoseconds{spec.tv_nsec};
241 return duration_cast<milliseconds>(nsecs).count();
246[[maybe_unused]]
static typename std::enable_if<(&T::st_atim,
true), qint64>::type
247atime(
const T &statBuffer,
int)
248{
return timespecToMSecs(statBuffer.st_atim); }
251[[maybe_unused]]
static typename std::enable_if<(&T::st_birthtim,
true), qint64>::type
252birthtime(
const T &statBuffer,
int)
253{
return timespecToMSecs(statBuffer.st_birthtim); }
256[[maybe_unused]]
static typename std::enable_if<(&T::st_ctim,
true), qint64>::type
257ctime(
const T &statBuffer,
int)
258{
return timespecToMSecs(statBuffer.st_ctim); }
261[[maybe_unused]]
static typename std::enable_if<(&T::st_mtim,
true), qint64>::type
262mtime(
const T &statBuffer,
int)
263{
return timespecToMSecs(statBuffer.st_mtim); }
268[[maybe_unused]]
static typename std::enable_if<(&T::st_atimespec,
true), qint64>::type
269atime(
const T &statBuffer,
int)
270{
return timespecToMSecs(statBuffer.st_atimespec); }
273[[maybe_unused]]
static typename std::enable_if<(&T::st_birthtimespec,
true), qint64>::type
274birthtime(
const T &statBuffer,
int)
275{
return timespecToMSecs(statBuffer.st_birthtimespec); }
278[[maybe_unused]]
static typename std::enable_if<(&T::st_ctimespec,
true), qint64>::type
279ctime(
const T &statBuffer,
int)
280{
return timespecToMSecs(statBuffer.st_ctimespec); }
283[[maybe_unused]]
static typename std::enable_if<(&T::st_mtimespec,
true), qint64>::type
284mtime(
const T &statBuffer,
int)
285{
return timespecToMSecs(statBuffer.st_mtimespec); }
288#if !defined(st_mtimensec) && !defined(__alpha__)
291[[maybe_unused]]
static typename std::enable_if<(&T::st_atimensec,
true), qint64>::type
292atime(
const T &statBuffer,
int)
293{
return statBuffer.st_atime * Q_INT64_C(1000) + statBuffer.st_atimensec / 1000000; }
296[[maybe_unused]]
static typename std::enable_if<(&T::st_birthtimensec,
true), qint64>::type
297birthtime(
const T &statBuffer,
int)
298{
return statBuffer.st_birthtime * Q_INT64_C(1000) + statBuffer.st_birthtimensec / 1000000; }
301[[maybe_unused]]
static typename std::enable_if<(&T::st_ctimensec,
true), qint64>::type
302ctime(
const T &statBuffer,
int)
303{
return statBuffer.st_ctime * Q_INT64_C(1000) + statBuffer.st_ctimensec / 1000000; }
306[[maybe_unused]]
static typename std::enable_if<(&T::st_mtimensec,
true), qint64>::type
307mtime(
const T &statBuffer,
int)
308{
return statBuffer.st_mtime * Q_INT64_C(1000) + statBuffer.st_mtimensec / 1000000; }
319 QFileSystemMetaData::MetaDataFlags entryFlags = QFileSystemMetaData::ExistsAttribute;
322 entryFlags |= QFileSystemMetaData::OwnerReadPermission;
324 entryFlags |= QFileSystemMetaData::OwnerWritePermission;
326 entryFlags |= QFileSystemMetaData::OwnerExecutePermission;
329 entryFlags |= QFileSystemMetaData::GroupReadPermission;
331 entryFlags |= QFileSystemMetaData::GroupWritePermission;
333 entryFlags |= QFileSystemMetaData::GroupExecutePermission;
336 entryFlags |= QFileSystemMetaData::OtherReadPermission;
338 entryFlags |= QFileSystemMetaData::OtherWritePermission;
340 entryFlags |= QFileSystemMetaData::OtherExecutePermission;
343 Q_ASSERT(!S_ISLNK(mode));
344 if ((mode & S_IFMT) == S_IFREG)
345 entryFlags |= QFileSystemMetaData::FileType;
346 else if ((mode & S_IFMT) == S_IFDIR)
347 entryFlags |= QFileSystemMetaData::DirectoryType;
348 else if ((mode & S_IFMT) != S_IFBLK)
349 entryFlags |= QFileSystemMetaData::SequentialType;
359 if (attributes & DOS_ATTR_RDONLY) {
362 entryFlags &= ~QFileSystemMetaData::OwnerWritePermission;
363 entryFlags &= ~QFileSystemMetaData::GroupWritePermission;
364 entryFlags &= ~QFileSystemMetaData::OtherWritePermission;
370#ifdef STATX_BASIC_STATS
371static int qt_real_statx(
int fd,
const char *pathname,
int flags,
struct statx *statxBuffer)
373 unsigned mask = STATX_BASIC_STATS | STATX_BTIME;
374 int ret = statx(fd, pathname, flags | AT_NO_AUTOMOUNT, mask, statxBuffer);
375 return ret == -1 ? -errno : 0;
378static int qt_statx(
const char *pathname,
struct statx *statxBuffer)
380 return qt_real_statx(AT_FDCWD, pathname, 0, statxBuffer);
383static int qt_lstatx(
const char *pathname,
struct statx *statxBuffer)
385 return qt_real_statx(AT_FDCWD, pathname, AT_SYMLINK_NOFOLLOW, statxBuffer);
388static int qt_fstatx(
int fd,
struct statx *statxBuffer)
390 return qt_real_statx(fd,
"", AT_EMPTY_PATH, statxBuffer);
393inline void QFileSystemMetaData::fillFromStatxBuf(
const struct statx &statxBuffer)
396 MetaDataFlags flags = flagsFromStMode(statxBuffer.stx_mode, statxBuffer.stx_attributes);
398 knownFlagsMask |= flags | PosixStatFlags;
401 if (statxBuffer.stx_nlink == 0)
402 entryFlags |= QFileSystemMetaData::WasDeletedAttribute;
403 size_ = qint64(statxBuffer.stx_size);
406 using namespace GetFileTimes;
407 accessTime_ = timespecToMSecs(statxBuffer.stx_atime);
408 metadataChangeTime_ = timespecToMSecs(statxBuffer.stx_ctime);
409 modificationTime_ = timespecToMSecs(statxBuffer.stx_mtime);
410 const bool birthMask = statxBuffer.stx_mask & STATX_BTIME;
411 birthTime_ = birthMask ? timespecToMSecs(statxBuffer.stx_btime) : 0;
413 userId_ = statxBuffer.stx_uid;
414 groupId_ = statxBuffer.stx_gid;
426inline void QFileSystemMetaData::fillFromStatxBuf(
const struct statx &)
431bool QFileSystemEngine::fillMetaData(
int fd, QFileSystemMetaData &data)
433 auto getSizeForBlockDev = [&](mode_t st_mode) {
436 if (quint64 sz; (st_mode & S_IFMT) == S_IFBLK && ioctl(fd, BLKGETSIZE64, &sz) == 0)
438#elif defined(BLKGETSIZE)
440 if (ulong sz; (st_mode & S_IFMT) == S_IFBLK && ioctl(fd, BLKGETSIZE, &sz) == 0)
441 data.size_ = sz * 512;
442#elif defined(DKIOCGETBLOCKCOUNT)
445 if (quint64 count; (st_mode & S_IFMT) == S_IFBLK
446 && ioctl(fd, DKIOCGETBLOCKCOUNT, &count) == 0
447 && ioctl(fd, DKIOCGETBLOCKSIZE, &blksz) == 0)
448 data.size_ = count * blksz;
449#elif defined(DIOCGMEDIASIZE)
454 if (QT_OFF_T sz; (st_mode & S_IFMT) == S_IFCHR && ioctl(fd, DIOCGMEDIASIZE, &sz) == 0)
460 data.entryFlags &= ~QFileSystemMetaData::PosixStatFlags;
461 data.knownFlagsMask |= QFileSystemMetaData::PosixStatFlags;
463 struct statx statxBuffer;
465 int ret = qt_fstatx(fd, &statxBuffer);
466 if (ret != -ENOSYS) {
468 data.fillFromStatxBuf(statxBuffer);
469 getSizeForBlockDev(statxBuffer.stx_mode);
475 QT_STATBUF statBuffer;
477 if (QT_FSTAT(fd, &statBuffer) == 0) {
478 data.fillFromStatBuf(statBuffer);
479 getSizeForBlockDev(statBuffer.st_mode);
486#if defined(_DEXTRA_FIRST)
487static void fillStat64fromStat32(
struct stat64 *statBuf64,
const struct stat &statBuf32)
489 statBuf64->st_mode = statBuf32.st_mode;
490 statBuf64->st_size = statBuf32.st_size;
491#if _POSIX_VERSION >= 200809L
492 statBuf64->st_ctim = statBuf32.st_ctim;
493 statBuf64->st_mtim = statBuf32.st_mtim;
494 statBuf64->st_atim = statBuf32.st_atim;
496 statBuf64->st_ctime = statBuf32.st_ctime;
497 statBuf64->st_mtime = statBuf32.st_mtime;
498 statBuf64->st_atime = statBuf32.st_atime;
500 statBuf64->st_uid = statBuf32.st_uid;
501 statBuf64->st_gid = statBuf32.st_gid;
505void QFileSystemMetaData::fillFromStatBuf(
const QT_STATBUF &statBuffer)
507 quint64 attributes = 0;
508#if defined(UF_SETTABLE)
509 attributes = statBuffer.st_flags;
511 attributes = statBuffer.st_attrib;
514 MetaDataFlags flags = flagsFromStMode(statBuffer.st_mode, attributes);
516 knownFlagsMask |= flags | PosixStatFlags;
519 if (statBuffer.st_nlink == 0)
520 entryFlags |= QFileSystemMetaData::WasDeletedAttribute;
521 size_ = statBuffer.st_size;
524 accessTime_ = GetFileTimes::atime(statBuffer, 0);
525 birthTime_ = GetFileTimes::birthtime(statBuffer, 0);
526 metadataChangeTime_ = GetFileTimes::ctime(statBuffer, 0);
527 modificationTime_ = GetFileTimes::mtime(statBuffer, 0);
529 userId_ = statBuffer.st_uid;
530 groupId_ = statBuffer.st_gid;
533void QFileSystemMetaData::fillFromDirEnt(
const QT_DIRENT &entry)
535#if defined(_DEXTRA_FIRST)
538 for (dirent_extra *extra = _DEXTRA_FIRST(&entry); _DEXTRA_VALID(extra, &entry);
539 extra = _DEXTRA_NEXT(extra)) {
540 if (extra->d_type == _DTYPE_STAT || extra->d_type == _DTYPE_LSTAT) {
542 const struct dirent_extra_stat *
const extra_stat =
543 reinterpret_cast<
struct dirent_extra_stat *>(extra);
546 if (extra->d_type == _DTYPE_LSTAT) {
547 knownFlagsMask |= QFileSystemMetaData::LinkType;
548 if (S_ISLNK(extra_stat->d_stat.st_mode))
549 entryFlags |= QFileSystemMetaData::LinkType;
557 if (S_ISLNK(extra_stat->d_stat.st_mode) && extra->d_type == _DTYPE_LSTAT)
560#if defined(QT_USE_XOPEN_LFS_EXTENSIONS) && defined(QT_LARGEFILE_SUPPORT)
563 struct stat64 statBuf;
564 fillStat64fromStat32(&statBuf, extra_stat->d_stat);
565 fillFromStatBuf(statBuf);
567 fillFromStatBuf(extra_stat->d_stat);
569 knownFlagsMask |= QFileSystemMetaData::PosixStatFlags;
570 if (!S_ISLNK(extra_stat->d_stat.st_mode)) {
571 knownFlagsMask |= QFileSystemMetaData::ExistsAttribute;
572 entryFlags |= QFileSystemMetaData::ExistsAttribute;
576#elif defined(_DIRENT_HAVE_D_TYPE) || defined(Q_OS_BSD4)
580 switch (entry.d_type)
583 knownFlagsMask = QFileSystemMetaData::LinkType
584 | QFileSystemMetaData::FileType
585 | QFileSystemMetaData::DirectoryType
586 | QFileSystemMetaData::SequentialType
587 | QFileSystemMetaData::ExistsAttribute;
589 entryFlags = QFileSystemMetaData::DirectoryType
590 | QFileSystemMetaData::ExistsAttribute;
595 knownFlagsMask = QFileSystemMetaData::LinkType
596 | QFileSystemMetaData::FileType
597 | QFileSystemMetaData::DirectoryType
598 | QFileSystemMetaData::BundleType
599 | QFileSystemMetaData::AliasType
600 | QFileSystemMetaData::SequentialType
601 | QFileSystemMetaData::ExistsAttribute;
603 entryFlags = QFileSystemMetaData::ExistsAttribute;
611 knownFlagsMask = QFileSystemMetaData::LinkType
612 | QFileSystemMetaData::FileType
613 | QFileSystemMetaData::DirectoryType
614 | QFileSystemMetaData::BundleType
615 | QFileSystemMetaData::AliasType
616 | QFileSystemMetaData::SequentialType
617 | QFileSystemMetaData::ExistsAttribute;
619 entryFlags = QFileSystemMetaData::SequentialType
620 | QFileSystemMetaData::ExistsAttribute;
625 knownFlagsMask = QFileSystemMetaData::LinkType;
626 entryFlags = QFileSystemMetaData::LinkType;
630 knownFlagsMask = QFileSystemMetaData::LinkType
631 | QFileSystemMetaData::FileType
632 | QFileSystemMetaData::DirectoryType
633 | QFileSystemMetaData::BundleType
634 | QFileSystemMetaData::SequentialType
635 | QFileSystemMetaData::ExistsAttribute;
637 entryFlags = QFileSystemMetaData::FileType
638 | QFileSystemMetaData::ExistsAttribute;
652QFileSystemEntry QFileSystemEngine::getLinkTarget(
const QFileSystemEntry &link, QFileSystemMetaData &data)
656 QByteArray s = qt_readlink(link.nativeFilePath().constData());
659 if (!data.hasFlags(QFileSystemMetaData::DirectoryType))
660 fillMetaData(link, data, QFileSystemMetaData::DirectoryType);
661 if (data.isDirectory() && s[0] !=
'/') {
662 QDir parent(link.filePath());
665 if (!ret.isEmpty() && !ret.endsWith(u'/'))
668 ret += QFile::decodeName(s);
670 if (!ret.startsWith(u'/'))
671 ret.prepend(absoluteName(link).path() + u'/');
672 ret = QDir::cleanPath(ret);
673 if (ret.size() > 1 && ret.endsWith(u'/'))
675 return QFileSystemEntry(ret);
677#if defined(Q_OS_DARWIN)
679 QCFString path = CFStringCreateWithFileSystemRepresentation(0,
680 QFile::encodeName(QDir::cleanPath(link.filePath())).data());
682 return QFileSystemEntry();
684 QCFType<CFURLRef> url = CFURLCreateWithFileSystemPath(0, path, kCFURLPOSIXPathStyle,
685 data.hasFlags(QFileSystemMetaData::DirectoryType));
687 return QFileSystemEntry();
689 QCFType<CFDataRef> bookmarkData = CFURLCreateBookmarkDataFromFile(0, url, NULL);
691 return QFileSystemEntry();
693 QCFType<CFURLRef> resolvedUrl = CFURLCreateByResolvingBookmarkData(0,
695 (CFURLBookmarkResolutionOptions)(kCFBookmarkResolutionWithoutUIMask
696 | kCFBookmarkResolutionWithoutMountingMask), NULL, NULL, NULL, NULL);
698 return QFileSystemEntry();
700 QCFString cfstr(CFURLCopyFileSystemPath(resolvedUrl, kCFURLPOSIXPathStyle));
702 return QFileSystemEntry();
704 return QFileSystemEntry(QString::fromCFString(cfstr));
707 return QFileSystemEntry();
711QFileSystemEntry QFileSystemEngine::getRawLinkPath(
const QFileSystemEntry &link,
712 QFileSystemMetaData &data)
715 const QByteArray path = qt_readlink(link.nativeFilePath().constData());
716 const QString ret = QFile::decodeName(path);
717 return QFileSystemEntry(ret);
721QFileSystemEntry QFileSystemEngine::canonicalName(
const QFileSystemEntry &entry, QFileSystemMetaData &data)
729#if defined(Q_OS_LINUX) || defined(Q_OS_ANDROID) || defined(F_KINFO) || defined(F_GETPATH)
730 auto pathForFd = [](
int fd) -> QByteArray {
731# if defined(Q_OS_LINUX) || defined(Q_OS_ANDROID)
732 return qt_readlink(QByteArray::number(fd).prepend(
"/proc/self/fd/"));
733# elif defined(F_KINFO)
734 struct kinfo_file kif;
735 kif.kf_structsize =
sizeof(kif);
736 if (fcntl(fd, F_KINFO, &kif) == 0)
738# elif defined(F_GETPATH)
740 if (fcntl(fd, F_GETPATH, buf) == 0)
745 if (
int fd = QT_OPEN(entry.nativeFilePath(), O_RDONLY | O_PATH | O_NONBLOCK); fd >= 0) {
747 data.knownFlagsMask |= QFileSystemMetaData::ExistsAttribute;
748 data.entryFlags |= QFileSystemMetaData::ExistsAttribute;
749 QByteArray ret = pathForFd(fd);
755 if (ret.startsWith(
'/'))
756 return QFileSystemEntry(ret, QFileSystemEntry::FromNativePath{});
758 }
else if (errno == ENOENT || errno == ENOTDIR) {
759 data.knownFlagsMask |= QFileSystemMetaData::ExistsAttribute;
760 data.entryFlags &= ~(QFileSystemMetaData::ExistsAttribute);
762 }
else if constexpr (O_PATH != 0) {
768# define REALPATH_FALLBACK_INLINE Q_NEVER_INLINE
770# define REALPATH_FALLBACK_INLINE inline
773 return canonicalNameViaRealpath(entry, data);
777QFileSystemEntry QFileSystemEngine::canonicalNameViaRealpath(
const QFileSystemEntry &entry,
778 QFileSystemMetaData &data)
780 char *resolved_name =
nullptr;
783 char stack_result[PATH_MAX + 1];
786 std::nullptr_t stack_result =
nullptr;
787 auto freer = qScopeGuard([&] { free(resolved_name); });
790# if defined(Q_OS_DARWIN) || defined(Q_OS_ANDROID)
793 if (!data.hasFlags(QFileSystemMetaData::ExistsAttribute))
794 fillMetaData(entry, data, QFileSystemMetaData::ExistsAttribute);
799 resolved_name = realpath(entry.nativeFilePath().constData(), stack_result);
801 resolved_name = realpath(entry.nativeFilePath().constData(), stack_result);
804 data.knownFlagsMask |= QFileSystemMetaData::ExistsAttribute;
805 data.entryFlags |= QFileSystemMetaData::ExistsAttribute;
806 return QFileSystemEntry(resolved_name, QFileSystemEntry::FromNativePath{});
807 }
else if (errno == ENOENT || errno == ENOTDIR) {
808 data.knownFlagsMask |= QFileSystemMetaData::ExistsAttribute;
809 data.entryFlags &= ~(QFileSystemMetaData::ExistsAttribute);
811 return QFileSystemEntry();
813#undef REALPATH_FALLBACK_INLINE
816QFileSystemEntry QFileSystemEngine::absoluteName(
const QFileSystemEntry &entry)
820 if (entry.isAbsolute() && entry.isClean())
823 QByteArray orig = entry.nativeFilePath();
825 if (orig.isEmpty() || !orig.startsWith(
'/')) {
826 QFileSystemEntry cur(currentPath());
827 result = cur.nativeFilePath();
829 if (!orig.isEmpty() && !(orig.size() == 1 && orig[0] ==
'.')) {
830 if (!result.isEmpty() && !result.endsWith(
'/'))
835 if (result.size() == 1 && result[0] ==
'/')
836 return QFileSystemEntry(result, QFileSystemEntry::FromNativePath());
837 const bool isDir = result.endsWith(
'/');
840
841
842
843 QFileSystemEntry resultingEntry(result, QFileSystemEntry::FromNativePath());
844 QString stringVersion = QDir::cleanPath(resultingEntry.filePath());
846 stringVersion.append(u'/');
847 return QFileSystemEntry(stringVersion);
854 char buffer[
sizeof "0123456789abcdef:18446744073709551615"];
855 std::snprintf(buffer,
sizeof(buffer),
"%llx:%llu",
856 qulonglong(volumeId), qulonglong(fileId[0]));
863 id.volumeId = quint64(statResult.st_dev);
864 id.fileId[0] = quint64(statResult.st_ino);
869QFileSystemNativeId QFileSystemEngine::nativeId(
const QFileSystemEntry &entry)
873 QT_STATBUF statResult;
874 if (QT_STAT(entry.nativeFilePath().constData(), &statResult) != 0)
876 return nativeIdFromStatBuf(statResult);
880QFileSystemNativeId QFileSystemEngine::nativeId(
int fd)
882 QT_STATBUF statResult;
883 if (QT_FSTAT(fd, &statResult) != 0) {
884 qErrnoWarning(
"fstat() failed for fd %d", fd);
887 return nativeIdFromStatBuf(statResult);
891QString QFileSystemEngine::resolveUserName(uint userId)
893#if QT_CONFIG(thread) && defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_OPENBSD)
894 long size_max = sysconf(_SC_GETPW_R_SIZE_MAX);
897 QVarLengthArray<
char, 1024> buf(size_max);
900#if !defined(Q_OS_INTEGRITY) && !defined(Q_OS_WASM)
901 struct passwd *pw =
nullptr;
902#if QT_CONFIG(thread) && defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_OPENBSD) && !defined(Q_OS_VXWORKS)
904 getpwuid_r(userId, &entry, buf.data(), buf.size(), &pw);
906 pw = getpwuid(userId);
909 return QFile::decodeName(QByteArray(pw->pw_name));
917QString QFileSystemEngine::resolveGroupName(uint groupId)
919#if QT_CONFIG(thread) && defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_OPENBSD)
920 long size_max = sysconf(_SC_GETPW_R_SIZE_MAX);
923 QVarLengthArray<
char, 1024> buf(size_max);
926#if !defined(Q_OS_INTEGRITY) && !defined(Q_OS_WASM)
927 struct group *gr =
nullptr;
928#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
))
929 size_max = sysconf(_SC_GETGR_R_SIZE_MAX);
932 buf.resize(size_max);
936 for (
long size = size_max; size < 256000; size += size)
940 if (!getgrgid_r(groupId, &entry, buf.data(), buf.size(), &gr)
945 gr = getgrgid(groupId);
948 return QFile::decodeName(QByteArray(gr->gr_name));
955#if defined(Q_OS_DARWIN)
957QString QFileSystemEngine::bundleName(
const QFileSystemEntry &entry)
959 QCFType<CFURLRef> url = CFURLCreateWithFileSystemPath(0, QCFString(entry.filePath()),
960 kCFURLPOSIXPathStyle,
true);
961 if (QCFType<CFDictionaryRef> dict = CFBundleCopyInfoDictionaryForURL(url)) {
962 if (CFTypeRef name = (CFTypeRef)CFDictionaryGetValue(dict, kCFBundleNameKey)) {
963 if (CFGetTypeID(name) == CFStringGetTypeID())
964 return QString::fromCFString((CFStringRef)name);
972bool QFileSystemEngine::fillMetaData(
const QFileSystemEntry &entry, QFileSystemMetaData &data,
973 QFileSystemMetaData::MetaDataFlags what)
981 auto hadBeenDeleted = data.entryFlags & QFileSystemMetaData::WasDeletedAttribute;
983#if defined(Q_OS_DARWIN)
984 if (what & (QFileSystemMetaData::BundleType | QFileSystemMetaData::CaseSensitive)) {
985 if (!data.hasFlags(QFileSystemMetaData::DirectoryType))
986 what |= QFileSystemMetaData::DirectoryType;
988 if (what & QFileSystemMetaData::AliasType)
989 what |= QFileSystemMetaData::LinkType;
992 bool needLstat = what.testAnyFlag(QFileSystemMetaData::LinkType);
995 if (what & QFileSystemMetaData::HiddenAttribute) {
1005 if (what & QFileSystemMetaData::PosixStatFlags)
1006 what |= QFileSystemMetaData::PosixStatFlags;
1008 data.entryFlags &= ~what;
1010 const QByteArray nativeFilePath = entry.nativeFilePath();
1027 QT_STATBUF statBuffer;
1028 struct statx statxBuffer;
1030 int statResult = -1;
1033 statResult = qt_lstatx(nativeFilePath, &statxBuffer);
1034 if (statResult == -ENOSYS) {
1036 statResult = QT_LSTAT(nativeFilePath, &statBuffer);
1037 if (statResult == 0)
1038 mode = statBuffer.st_mode;
1039 }
else if (statResult == 0) {
1041 mode = statxBuffer.stx_mode;
1044 if (statResult >= 0) {
1047 Q_ASSERT(statResult == 0);
1048 if (statBuffer.st_flags & UF_HIDDEN)
1049 data.entryFlags |= QFileSystemMetaData::HiddenAttribute;
1050 data.knownFlagsMask |= QFileSystemMetaData::HiddenAttribute;
1053 if (S_ISLNK(mode)) {
1055 data.entryFlags |= QFileSystemMetaData::LinkType;
1060 data.fillFromStatxBuf(statxBuffer);
1062 data.fillFromStatBuf(statBuffer);
1068 data.knownFlagsMask |= QFileSystemMetaData::ExistsAttribute;
1071 data.knownFlagsMask |= QFileSystemMetaData::LinkType;
1075 if (statResult == -1 && (what & QFileSystemMetaData::PosixStatFlags)) {
1076 if (entryErrno == 0) {
1077 data.entryFlags &= ~QFileSystemMetaData::PosixStatFlags;
1078 statResult = qt_statx(nativeFilePath, &statxBuffer);
1079 if (statResult == -ENOSYS) {
1081 statResult = QT_STAT(nativeFilePath, &statBuffer);
1082 if (statResult == 0)
1083 data.fillFromStatBuf(statBuffer);
1084 }
else if (statResult == 0) {
1085 data.fillFromStatxBuf(statxBuffer);
1089 if (statResult != 0) {
1091 data.birthTime_ = 0;
1092 data.metadataChangeTime_ = 0;
1093 data.modificationTime_ = 0;
1094 data.accessTime_ = 0;
1096 data.userId_ = (uint) -2;
1097 data.groupId_ = (uint) -2;
1100 data.knownFlagsMask |= QFileSystemMetaData::PosixStatFlags
1101 | QFileSystemMetaData::ExistsAttribute;
1106 if (what & (QFileSystemMetaData::UserPermissions | QFileSystemMetaData::ExistsAttribute)) {
1107#if defined(Q_OS_VXWORKS)
1108 struct statfs statBuf;
1109 if (statfs(nativeFilePath, &statBuf) == 0) {
1110 if (statBuf.f_type != NFSV2_MAGIC && statBuf.f_type != NFSV3_MAGIC &&
1111 statBuf.f_type != HRFS_MAGIC) {
1112#if __has_include(<dosFsLib.h>)
1113 if (data.entryFlags & QFileSystemMetaData::OwnerWritePermission) {
1114 data.entryFlags |= QFileSystemMetaData::UserWritePermission;
1116 if (data.entryFlags & QFileSystemMetaData::OwnerExecutePermission) {
1117 data.entryFlags |= QFileSystemMetaData::UserExecutePermission;
1120 data.entryFlags |= QFileSystemMetaData::UserReadPermission |
1121 QFileSystemMetaData::ExistsAttribute;
1125#if defined(QT_DEBUG)
1129 statResult = QT_STAT(nativeFilePath, &statBuffer);
1130 if (statResult == 0) {
1131 data.entryFlags |= QFileSystemMetaData::UserReadPermission |
1132 QFileSystemMetaData::ExistsAttribute;
1133 data.entryFlags &= ~QFileSystemMetaData::WasDeletedAttribute;
1140 auto checkAccess = [&](QFileSystemMetaData::MetaDataFlag flag,
int mode) {
1141 if (entryErrno != 0 || (what & flag) == 0)
1143 if (QT_ACCESS(nativeFilePath, mode) == 0) {
1145 data.entryFlags |= flag | QFileSystemMetaData::ExistsAttribute;
1146 }
else if (errno != EACCES && errno != EROFS) {
1150 checkAccess(QFileSystemMetaData::UserReadPermission, R_OK);
1151 checkAccess(QFileSystemMetaData::UserWritePermission, W_OK);
1152 checkAccess(QFileSystemMetaData::UserExecutePermission, X_OK);
1155 if (entryErrno == 0 && (data.entryFlags & QFileSystemMetaData::ExistsAttribute) == 0) {
1156 if (QT_ACCESS(nativeFilePath, F_OK) == -1)
1159 data.entryFlags |= QFileSystemMetaData::ExistsAttribute;
1162 data.knownFlagsMask |= (what & QFileSystemMetaData::UserPermissions) |
1163 QFileSystemMetaData::ExistsAttribute;
1166#if defined(Q_OS_DARWIN)
1167 QCFType<CFURLRef> cachedUrl;
1168 if (what & QFileSystemMetaData::AliasType) {
1169 if (entryErrno == 0 && hasResourcePropertyFlag(data, entry, kCFURLIsAliasFileKey, cachedUrl)) {
1171 if (!(data.entryFlags & QFileSystemMetaData::LinkType))
1172 data.entryFlags |= QFileSystemMetaData::AliasType;
1174 data.knownFlagsMask |= QFileSystemMetaData::AliasType;
1177 if (what & QFileSystemMetaData::BundleType) {
1178 if (entryErrno == 0 && isPackage(data, entry, cachedUrl))
1179 data.entryFlags |= QFileSystemMetaData::BundleType;
1181 data.knownFlagsMask |= QFileSystemMetaData::BundleType;
1184 if (what & QFileSystemMetaData::CaseSensitive) {
1185 if (entryErrno == 0 && hasResourcePropertyFlag(data, entry,
1186 kCFURLVolumeSupportsCaseSensitiveNamesKey, cachedUrl))
1187 data.entryFlags |= QFileSystemMetaData::CaseSensitive;
1188 data.knownFlagsMask |= QFileSystemMetaData::CaseSensitive;
1192 if (what & QFileSystemMetaData::HiddenAttribute
1193 && !data.isHidden()) {
1196 qsizetype lastSlash = nativeFilePath.size();
1198 while (lastSlash && nativeFilePath.at(lastSlash - 1) ==
'/')
1200 while (lastSlash && nativeFilePath.at(lastSlash - 1) !=
'/')
1204 if (nativeFilePath.at(lastSlash + 1) ==
'.')
1205 data.entryFlags |= QFileSystemMetaData::HiddenAttribute;
1206 data.knownFlagsMask |= QFileSystemMetaData::HiddenAttribute;
1209 if (entryErrno != 0) {
1210 what &= ~QFileSystemMetaData::LinkType;
1211 data.clearFlags(what);
1214 data.entryFlags |= hadBeenDeleted;
1215 data.knownFlagsMask |= hadBeenDeleted;
1223auto QFileSystemEngine::cloneFile(
int srcfd,
int dstfd,
const QFileSystemMetaData &knownData) -> TriStateResult
1225 QT_STATBUF statBuffer;
1226 if (knownData.hasFlags(QFileSystemMetaData::PosixStatFlags) &&
1227 knownData.isFile()) {
1228 statBuffer.st_mode = S_IFREG;
1229 }
else if (knownData.hasFlags(QFileSystemMetaData::PosixStatFlags) &&
1230 knownData.isDirectory()) {
1232 return TriStateResult::Failed;
1233 }
else if (QT_FSTAT(srcfd, &statBuffer) == -1) {
1235 return TriStateResult::Failed;
1236 }
else if (!S_ISREG((statBuffer.st_mode))) {
1238 return TriStateResult::NotSupported;
1241 [[maybe_unused]]
auto destinationIsEmpty = [dstfd]() {
1242 QT_STATBUF statBuffer;
1243 return QT_FSTAT(dstfd, &statBuffer) == 0 && statBuffer.st_size == 0;
1245 Q_ASSERT(destinationIsEmpty());
1247#if defined(Q_OS_LINUX)
1249 if (::ioctl(dstfd, FICLONE, srcfd) == 0)
1250 return TriStateResult::Success;
1251#elif defined(Q_OS_DARWIN)
1253 if (fcopyfile(srcfd, dstfd,
nullptr, COPYFILE_DATA | COPYFILE_STAT) == 0)
1254 return TriStateResult::Success;
1258 return TriStateResult::NotSupported;
1260 return TriStateResult::Failed;
1263#if QT_CONFIG(copy_file_range)
1267 QT_OFF_T srcoffset = 0;
1270 copied = ::copy_file_range(srcfd, &srcoffset, dstfd,
nullptr, SSIZE_MAX, 0);
1271 }
while (copied > 0 || (copied < 0 && errno == EINTR));
1273 return TriStateResult::Success;
1276 copied = ftruncate(dstfd, 0);
1277 return TriStateResult::Failed;
1290 return TriStateResult::Failed;
1294#if defined(Q_OS_LINUX)
1297 const size_t SendfileSize = 0x7ffff000;
1299 ssize_t n = ::sendfile(dstfd, srcfd,
nullptr, SendfileSize);
1304 return TriStateResult::Failed;
1308 return TriStateResult::NotSupported;
1312 n = ::sendfile(dstfd, srcfd,
nullptr, SendfileSize);
1315 n = ftruncate(dstfd, 0);
1316 n = lseek(srcfd, 0, SEEK_SET);
1317 n = lseek(dstfd, 0, SEEK_SET);
1318 return TriStateResult::Failed;
1322 return TriStateResult::Success;
1325 return TriStateResult::NotSupported;
1336 auto tryMkDir = [&path, mode]() -> QSystemError {
1337 if (QT_MKDIR(path, mode) == 0) {
1339 forceRequestedPermissionsOnVxWorks(path, mode);
1344 if (errno == EISDIR)
1346 if (errno == EEXIST || errno == EROFS) {
1350 if (QT_STAT(path.constData(), &st) != 0)
1351 return QSystemError::stdError(errno);
1352 const bool isDir = (st.st_mode & S_IFMT) == S_IFDIR;
1353 return isDir ? QSystemError{} : QSystemError::stdError(EEXIST);
1355 return QSystemError::stdError(errno);
1358 QSystemError result = tryMkDir();
1363 if (result.errorCode != ENOENT)
1366 qsizetype slash = path.lastIndexOf(
'/');
1367 while (slash > 0 && path[slash - 1] ==
'/')
1374 QByteArray parentPath = path.first(slash);
1375 if (result = createDirectoryWithParents(parentPath, mode); !result.ok())
1382bool QFileSystemEngine::mkpath(
const QFileSystemEntry &entry,
1383 std::optional<QFile::Permissions> permissions)
1385 QByteArray path = entry.nativeFilePath();
1388 mode_t mode = permissions ? QtPrivate::toMode_t(*permissions) : 0777;
1389 return createDirectoryWithParents(removeTrailingSlashes(path), mode).ok();
1392bool QFileSystemEngine::mkdir(
const QFileSystemEntry &entry,
1393 std::optional<QFile::Permissions> permissions)
1395 QByteArray path = entry.nativeFilePath();
1398 mode_t mode = permissions ? QtPrivate::toMode_t(*permissions) : 0777;
1399 auto result = QT_MKDIR(removeTrailingSlashes(path), mode) == 0;
1400#if defined(Q_OS_VXWORKS)
1402 forceRequestedPermissionsOnVxWorks(path, mode);
1407bool QFileSystemEngine::rmdir(
const QFileSystemEntry &entry)
1409 const QByteArray path = entry.nativeFilePath();
1411 return ::rmdir(path.constData()) == 0;
1414bool QFileSystemEngine::rmpath(
const QFileSystemEntry &entry)
1416 QByteArray path = QFile::encodeName(QDir::cleanPath(entry.filePath()));
1419 if (::rmdir(path.constData()) != 0)
1422 const char sep = QDir::separator().toLatin1();
1423 qsizetype slash = path.lastIndexOf(sep);
1425 for (; slash > 0; slash = path.lastIndexOf(sep)) {
1426 path.truncate(slash);
1427 if (::rmdir(path.constData()) != 0)
1435bool QFileSystemEngine::createLink(
const QFileSystemEntry &source,
const QFileSystemEntry &target, QSystemError &error)
1440 if (::symlink(source.nativeFilePath().constData(), target.nativeFilePath().constData()) == 0)
1442 error = QSystemError(errno, QSystemError::StandardLibraryError);
1446#if defined(QT_BOOTSTRAPPED) || !defined(AT_FDCWD) || defined(Q_OS_ANDROID) || !QT_CONFIG(datestring) || defined(Q_OS_VXWORKS)
1449bool QFileSystemEngine::supportsMoveFileToTrash()
1455bool QFileSystemEngine::moveFileToTrash(
const QFileSystemEntry &, QFileSystemEntry &,
1456 QSystemError &error)
1458 error = QSystemError(ENOSYS, QSystemError::StandardLibraryError);
1461#elif defined(Q_OS_DARWIN)
1463#elif defined(Q_OS_HARMONY)
1467
1468
1470bool QFileSystemEngine::supportsMoveFileToTrash()
1476struct FreeDesktopTrashOperation
1479
1480
1482 int filesDirFd = -1;
1486 QByteArray tempTrashFileName;
1487 QByteArray infoFilePath;
1488 QByteArray encodedRelativeFilePath;
1490 int infoFileFd = -1;
1491 ~FreeDesktopTrashOperation()
1496 constexpr bool isTrashDirOpen()
const {
return filesDirFd != -1 && infoDirFd != -1; }
1500 int savedErrno = errno;
1501 if (infoFileFd != -1) {
1502 Q_ASSERT(infoDirFd != -1);
1503 Q_ASSERT(!infoFilePath.isEmpty());
1504 Q_ASSERT(!trashPath.isEmpty());
1506 QT_CLOSE(infoFileFd);
1507 unlinkat(infoDirFd, infoFilePath, 0);
1510 if (!tempTrashFileName.isEmpty()) {
1511 Q_ASSERT(filesDirFd != -1);
1512 unlinkat(filesDirFd, tempTrashFileName, 0);
1514 if (filesDirFd >= 0)
1515 QT_CLOSE(filesDirFd);
1517 QT_CLOSE(infoDirFd);
1518 filesDirFd = infoDirFd = -1;
1522 bool tryCreateInfoFile(
const QString &filePath, QSystemError &error)
1524 QByteArray p = QFile::encodeName(filePath) +
".trashinfo";
1525 infoFileFd = qt_safe_openat(infoDirFd, p, QT_OPEN_RDWR | QT_OPEN_CREAT | QT_OPEN_EXCL, 0666);
1526 if (infoFileFd < 0) {
1527 error = QSystemError(errno, QSystemError::StandardLibraryError);
1530 infoFilePath = std::move(p);
1536 QT_CLOSE(infoFileFd);
1538 tempTrashFileName = {};
1542 static int openDirFd(
int dfd,
const char *path,
int mode)
1545 return qt_safe_openat(dfd, path, mode);
1549 static int openOrCreateDir(
int dfd,
const char *path,
int openmode = 0)
1552 int fd = openDirFd(dfd, path, openmode);
1553 if (fd >= 0 || errno != ENOENT)
1557 if (mkdirat(dfd, path, 0700) < 0)
1561 return openDirFd(dfd, path, openmode);
1565 getRelativeFilePathResolvingSymlinks(
const QFileSystemEntry &source,
const QString &volumeRoot)
1579 QByteArray nativeVolumeRoot = QFile::encodeName(volumeRoot);
1581 if (QT_STAT(nativeVolumeRoot.constData(), &st) != 0)
1583 const auto volumeDevice = st.st_dev;
1586 QByteArray dirPath = QFile::encodeName(source.path());
1587 char *nativeDir = dirPath.data();
1588 qsizetype crossingLength = dirPath.size();
1589 for (qsizetype pos = 1; pos <= dirPath.size(); ++pos) {
1590 if (pos < dirPath.size() && nativeDir[pos] !=
'/')
1594 QScopedValueRollback null(nativeDir[pos],
'\0');
1595 if (QT_STAT(nativeDir, &st) == 0 && st.st_dev == volumeDevice) {
1596 crossingLength = pos;
1604 QFileSystemEntry dir(dirPath.first(crossingLength), QFileSystemEntry::FromNativePath{});
1605 result = QFileSystemEngine::canonicalName(dir, md).nativeFilePath();
1606 if (Q_UNLIKELY(!result.startsWith(nativeVolumeRoot))) {
1610 dir = QFileSystemEntry(volumeRoot, QFileSystemEntry::FromInternalPath());
1611 nativeVolumeRoot = QFileSystemEngine::canonicalName(dir, md).nativeFilePath();
1613 result.remove(0, nativeVolumeRoot.size());
1616 result += dirPath.sliced(crossingLength);
1618 result += QFile::encodeName(source.fileName());
1619 Q_ASSERT(result.startsWith(
'/'));
1620 result.remove(0, 1);
1625 static QByteArray getRelativeFilePath(
const QFileSystemEntry &source,
1626 const QStorageInfo &storage)
1628 QString relativeFilePath = source.filePath();
1629 QString volumeRoot = storage.rootPath();
1630 qsizetype volumePrefixLength = volumeRoot.size();
1631 if (volumePrefixLength == 1) {
1633 return source.nativeFilePath();
1635 if (Q_LIKELY(relativeFilePath.startsWith(volumeRoot))) {
1637 relativeFilePath.remove(0, volumePrefixLength + 1);
1638 return QFile::encodeName(relativeFilePath);
1644 return getRelativeFilePathResolvingSymlinks(source, volumeRoot);
1649 QSystemError getTrashDir(
int parentfd, QString targetDir,
const QFileSystemEntry &source,
1652 if (parentfd == AT_FDCWD)
1653 trashPath = targetDir;
1654 QByteArray nativePath = QFile::encodeName(targetDir);
1657 int trashfd = openOrCreateDir(parentfd, nativePath, openmode);
1658 if (trashfd < 0 && errno != ENOENT)
1659 return QSystemError::stdError(errno);
1662 if (QT_STATBUF st; QT_FSTAT(trashfd, &st) < 0)
1663 return QSystemError::stdError(errno);
1664 else if (st.st_uid != getuid())
1665 return QSystemError::stdError(errno);
1667 filesDirFd = openOrCreateDir(trashfd,
"files");
1668 if (filesDirFd >= 0) {
1670 QTemporaryFileName tfn(
"XXXXXX"_L1);
1671 for (
int i = 0; i < 16; ++i) {
1672 QByteArray attempt = tfn.generateNext();
1673 if (linkat(AT_FDCWD, source.nativeFilePath(), filesDirFd, attempt, 0) == 0) {
1674 tempTrashFileName = std::move(attempt);
1677 if (errno != EEXIST)
1688 if (!tempTrashFileName.isEmpty() || errno == EPERM || errno == EMLINK)
1689 infoDirFd = openOrCreateDir(trashfd,
"info");
1692 if (infoDirFd < 0) {
1693 int saved_errno = errno;
1696 return QSystemError::stdError(saved_errno);
1703 QSystemError openMountPointTrashLocation(
const QFileSystemEntry &source,
1704 const QStorageInfo &sourceStorage)
1707
1708
1709
1710
1711
1712
1713
1714
1715
1718 const auto dotTrash =
"/.Trash"_L1;
1719 const QString userID = QString::number(::getuid());
1720 QFileSystemEntry dotTrashDir(sourceStorage.rootPath() + dotTrash);
1723 int genericTrashFd = openDirFd(AT_FDCWD, dotTrashDir.nativeFilePath(),
O_NOFOLLOW);
1725 if (genericTrashFd < 0 && errno != ENOENT && errno != EACCES) {
1727 if (QT_LSTAT(dotTrashDir.nativeFilePath(), &st) == 0 && S_ISLNK(st.st_mode)) {
1729 qCritical(
"Warning: '%s' is a symlink to '%s'",
1730 dotTrashDir.nativeFilePath().constData(),
1731 qt_readlink(dotTrashDir.nativeFilePath()).constData());
1732 error = QSystemError(ELOOP, QSystemError::StandardLibraryError);
1734 }
else if (genericTrashFd >= 0) {
1735 QT_FSTAT(genericTrashFd, &st);
1736 if ((st.st_mode & S_ISVTX) == 0) {
1738 qCritical(
"Warning: '%s' doesn't have sticky bit set!",
1739 dotTrashDir.nativeFilePath().constData());
1740 error = QSystemError(EPERM, QSystemError::StandardLibraryError);
1743
1744
1745
1746
1747
1748
1749
1750
1751 if (error = getTrashDir(genericTrashFd, userID, source,
O_NOFOLLOW); error.ok()) {
1753 trashPath = dotTrashDir.filePath() + u'/' + userID;
1756 QT_CLOSE(genericTrashFd);
1760
1761
1762
1763
1764
1765
1766 if (!isTrashDirOpen())
1767 error = getTrashDir(AT_FDCWD, sourceStorage.rootPath() + dotTrash + u'-' + userID, source,
1770 if (!isTrashDirOpen())
1773 QByteArray relativePath = getRelativeFilePath(source, sourceStorage);
1774 if (relativePath.isEmpty())
1775 return QSystemError::stdError(errno);
1776 encodedRelativeFilePath = relativePath.toPercentEncoding(
"/");
1777 return QSystemError();
1780 QSystemError openHomeTrashLocation(
const QFileSystemEntry &source)
1782 QString topDir = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation);
1784 QSystemError error = getTrashDir(AT_FDCWD, topDir +
"/Trash"_L1, source, openmode);
1789 encodedRelativeFilePath = QFile::encodeName(source.filePath()).toPercentEncoding(
"/");
1790 return QSystemError();
1793 QSystemError findTrashFor(
const QFileSystemEntry &source)
1796
1797
1798
1799
1800
1801 if (QSystemError error = openHomeTrashLocation(source); error.ok())
1802 return QSystemError();
1803 else if (error.errorCode != EXDEV)
1808 const QStorageInfo sourceStorage(source.path());
1809 if (!sourceStorage.isValid())
1810 return QSystemError::stdError(ENODEV);
1811 return openMountPointTrashLocation(source, sourceStorage);
1817bool QFileSystemEngine::moveFileToTrash(
const QFileSystemEntry &source,
1818 QFileSystemEntry &newLocation, QSystemError &error)
1820 const QFileSystemEntry sourcePath = [&] {
1821 if (QString path = source.filePath(); path.size() > 1 && path.endsWith(u'/')) {
1823 return absoluteName(QFileSystemEntry(path));
1825 return absoluteName(source);
1827 FreeDesktopTrashOperation op;
1828 if (error = op.findTrashFor(sourcePath); !error.ok())
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851 QString uniqueTrashedName = sourcePath.fileName();
1852 if (!op.tryCreateInfoFile(uniqueTrashedName, error) && error.errorCode == EEXIST) {
1856 if (QT_STATBUF st; Q_LIKELY(QT_STAT(source.nativeFilePath(), &st) == 0)) {
1857 counter = st.st_ino;
1859 error = QSystemError(errno, QSystemError::StandardLibraryError);
1863 QString uniqueTrashBase = std::move(uniqueTrashedName);
1865 uniqueTrashedName = QString::asprintf(
"%ls-%llu", qUtf16Printable(uniqueTrashBase),
1867 if (op.tryCreateInfoFile(uniqueTrashedName, error))
1869 if (error.errorCode != EEXIST)
1876 "Path=" + op.encodedRelativeFilePath +
"\n"
1877 "DeletionDate=" + QDateTime::currentDateTime().toString(Qt::ISODate).toUtf8()
1879 if (QT_WRITE(op.infoFileFd, info.data(), info.size()) < 0) {
1880 error = QSystemError(errno, QSystemError::StandardLibraryError);
1885
1886
1887
1888
1890 if (op.tempTrashFileName.isEmpty()) {
1892
1893
1894
1895
1896
1897 renamed = renameat(AT_FDCWD, source.nativeFilePath(), op.filesDirFd,
1898 QFile::encodeName(uniqueTrashedName)) == 0;
1900 renamed = renameat(op.filesDirFd, op.tempTrashFileName, op.filesDirFd,
1901 QFile::encodeName(uniqueTrashedName)) == 0;
1903 removeFile(sourcePath, error);
1906 error = QSystemError(errno, QSystemError::StandardLibraryError);
1911 newLocation = QFileSystemEntry(op.trashPath +
"/files/"_L1 + uniqueTrashedName);
1917#if !defined(AT_FDCWD) || defined(Q_OS_QNX) || defined(Q_OS_VXWORKS)
1925bool QFileSystemEngine::supportsRmdirRecursively()
noexcept
1931bool QFileSystemEngine::rmdirRecursively(
const QFileSystemEntry &, QSystemError &error)
1933 error = QSystemError(ENOSYS, QSystemError::StandardLibraryError);
1939bool QFileSystemEngine::supportsRmdirRecursively()
noexcept
1945bool QFileSystemEngine::rmdirRecursively(
const QFileSystemEntry &entry, QSystemError &error)
1947 Q_CHECK_FILE_NAME(entry,
false);
1949# if defined(Q_OS_ANDROID) || defined(Q_OS_BSD4) || defined(Q_OS_LINUX)
1956 static constexpr bool NeedsSecondFileDescriptor =
false;
1958 static constexpr bool NeedsSecondFileDescriptor =
true;
1966 auto openDirectory = [](
int parentFd, std::string name) {
1967 constexpr int OpenMode = QT_OPEN_RDONLY | O_DIRECTORY | O_NOFOLLOW;
1968 DirInfo di = {
nullptr, -1, parentFd, std::move(name) };
1969 di.dirfd = qt_safe_openat(parentFd, di.name.c_str(), OpenMode);
1974 di.dir = fdopendir(di.dirfd);
1980 if constexpr (NeedsSecondFileDescriptor) {
1981 di.dirfd = qt_safe_openat(parentFd, di.name.c_str(), OpenMode);
1982 if (Q_UNLIKELY(di.dirfd < 0))
1983 closedir(std::exchange(di.dir,
nullptr));
1989 QVarLengthArray<DirInfo, 16> stack;
1990 if (DirInfo di = openDirectory(AT_FDCWD, entry.nativeFilePath().toStdString()); di.dir) {
1991 stack.emplace_back(std::move(di));
1993 if (errno == ENOENT)
1995 error = QSystemError(errno, QSystemError::StandardLibraryError);
1999 while (!stack.isEmpty()) {
2000 DirInfo &top = stack.last();
2001 if (QT_DIRENT *de = QT_READDIR(top.dir)) {
2002 if (de->d_name[0] ==
'.') {
2003 if (de->d_name[1] ==
'\0' || (de->d_name[1] ==
'.' && de->d_name[2] ==
'\0'))
2007 QFileSystemMetaData meta;
2008 meta.fillFromDirEnt(*de);
2011 if (!meta.hasFlags(QFileSystemMetaData::DirectoryType) || !meta.isDirectory()) {
2012 if (unlinkat(top.dirfd, de->d_name, 0) == 0)
2018 if (DirInfo di = openDirectory(top.dirfd, de->d_name); di.dir) {
2020 stack.emplace_back(std::move(di));
2028 if constexpr (NeedsSecondFileDescriptor)
2029 QT_CLOSE(top.dirfd);
2032 int r = unlinkat(top.parentFd, top.name.c_str(), AT_REMOVEDIR);
2033 if (r < 0 && top.parentFd == AT_FDCWD && errno != ENOENT)
2034 error = QSystemError(errno, QSystemError::StandardLibraryError);
2046bool QFileSystemEngine::copyFile(
const QFileSystemEntry &source,
const QFileSystemEntry &target, QSystemError &error)
2050 error = QSystemError(ENOSYS, QSystemError::StandardLibraryError);
2055bool QFileSystemEngine::renameFile(
const QFileSystemEntry &source,
const QFileSystemEntry &target, QSystemError &error)
2057 QFileSystemEntry::NativePath srcPath = source.nativeFilePath();
2058 QFileSystemEntry::NativePath tgtPath = target.nativeFilePath();
2063#if defined(RENAME_NOREPLACE
) && QT_CONFIG(renameat2)
2064 if (renameat2(AT_FDCWD, srcPath, AT_FDCWD, tgtPath, RENAME_NOREPLACE) == 0)
2068 if (errno != EINVAL) {
2069 error = QSystemError(errno, QSystemError::StandardLibraryError);
2073#if defined(Q_OS_DARWIN) && defined(RENAME_EXCL)
2074 if (renameatx_np(AT_FDCWD, srcPath, AT_FDCWD, tgtPath, RENAME_EXCL) == 0)
2076 if (errno != ENOTSUP) {
2077 error = QSystemError(errno, QSystemError::StandardLibraryError);
2082 if (SupportsHardlinking && ::link(srcPath, tgtPath) == 0) {
2083 if (::unlink(srcPath) == 0)
2089 int savedErrno = errno;
2094 error = QSystemError(savedErrno, QSystemError::StandardLibraryError);
2096 }
else if (!SupportsHardlinking) {
2100 if (::access(tgtPath, F_OK) == 0) {
2125 if (::rename(srcPath, tgtPath) == 0)
2129 error = QSystemError(errno, QSystemError::StandardLibraryError);
2134bool QFileSystemEngine::renameOverwriteFile(
const QFileSystemEntry &source,
const QFileSystemEntry &target, QSystemError &error)
2139 if (::rename(source.nativeFilePath().constData(), target.nativeFilePath().constData()) == 0)
2141 error = QSystemError(errno, QSystemError::StandardLibraryError);
2146bool QFileSystemEngine::removeFile(
const QFileSystemEntry &entry, QSystemError &error)
2149 if (unlink(entry.nativeFilePath().constData()) == 0)
2151 error = QSystemError(errno, QSystemError::StandardLibraryError);
2157bool QFileSystemEngine::setPermissions(
const QFileSystemEntry &entry,
2158 QFile::Permissions permissions, QSystemError &error)
2162 mode_t mode = QtPrivate::toMode_t(permissions);
2163 bool success = ::chmod(entry.nativeFilePath().constData(), mode) == 0;
2165 error = QSystemError(errno, QSystemError::StandardLibraryError);
2170bool QFileSystemEngine::setPermissions(
int fd, QFile::Permissions permissions, QSystemError &error)
2172 mode_t mode = QtPrivate::toMode_t(permissions);
2173 bool success = ::fchmod(fd, mode) == 0;
2175 error = QSystemError(errno, QSystemError::StandardLibraryError);
2180bool QFileSystemEngine::setFileTime(
int fd,
const QDateTime &newDate, QFile::FileTime time, QSystemError &error)
2182 if (!newDate.isValid()
2183 || time == QFile::FileBirthTime || time == QFile::FileMetadataChangeTime) {
2184 error = QSystemError(EINVAL, QSystemError::StandardLibraryError);
2188#if QT_CONFIG(futimens)
2190 struct timespec ts[2] = {{0, UTIME_OMIT}, {0, UTIME_OMIT}};
2192 if (time == QFile::FileAccessTime || time == QFile::FileModificationTime) {
2193 const int idx = time == QFile::FileAccessTime ? 0 : 1;
2194 const std::chrono::milliseconds msecs{newDate.toMSecsSinceEpoch()};
2195 ts[idx] = durationToTimespec(msecs);
2198 if (futimens(fd, ts) == -1) {
2199 error = QSystemError(errno, QSystemError::StandardLibraryError);
2206 error = QSystemError(ENOSYS, QSystemError::StandardLibraryError);
2211QString QFileSystemEngine::homePath()
2213 QString home = qEnvironmentVariable(
"HOME");
2216 return QDir::cleanPath(home);
2219QString QFileSystemEngine::rootPath()
2227 QLatin1StringView temp =
_PATH_TMP ""_L1;
2228 static_assert(
_PATH_TMP[0] ==
'/',
"_PATH_TMP needs to be absolute");
2229 static_assert(
_PATH_TMP[1] !=
'\0',
"Are you really sure _PATH_TMP should be the root dir??");
2230 if (temp.endsWith(u'/'))
2235QString QFileSystemEngine::tempPath()
2237#ifdef QT_UNIX_TEMP_PATH_OVERRIDE
2238 return QT_UNIX_TEMP_PATH_OVERRIDE
""_L1;
2240 QString temp = qEnvironmentVariable(
"TMPDIR");
2241# if defined(Q_OS_DARWIN) && !defined(QT_BOOTSTRAPPED)
2242 if (NSString *nsPath; temp.isEmpty() && (nsPath = NSTemporaryDirectory()))
2243 temp = QString::fromCFString((CFStringRef)nsPath);
2246 return nativeTempPath();
2249 if (temp.size() > 1 && temp.endsWith(u'/'))
2252 QFileSystemEntry e(temp, QFileSystemEntry::FromInternalPath{});
2253 return QFileSystemEngine::absoluteName(e).filePath();
2257bool QFileSystemEngine::setCurrentPath(
const QFileSystemEntry &path)
2260 r = QT_CHDIR(path.nativeFilePath().constData());
2264QFileSystemEntry QFileSystemEngine::currentPath()
2266 QFileSystemEntry result;
2267#if defined(__GLIBC__
) && !defined(PATH_MAX)
2268 char *currentName = ::get_current_dir_name();
2270 result = QFileSystemEntry(QByteArray(currentName), QFileSystemEntry::FromNativePath());
2271 ::free(currentName);
2274 char currentName[PATH_MAX+1];
2275 if (::getcwd(currentName, PATH_MAX)) {
2276#if defined(Q_OS_VXWORKS) && defined(VXWORKS_VXSIM)
2277 QByteArray dir(currentName);
2278 if (dir.indexOf(
':') < dir.indexOf(
'/'))
2279 dir.remove(0, dir.indexOf(
':')+1);
2281 qstrncpy(currentName, dir.constData(), PATH_MAX);
2283 result = QFileSystemEntry(QByteArray(currentName), QFileSystemEntry::FromNativePath());
2285# if defined(QT_DEBUG)
2286 if (result.isEmpty())
2287 qWarning(
"QFileSystemEngine::currentPath: getcwd() failed");
2293bool QFileSystemEngine::isCaseSensitive(
const QFileSystemEntry &entry, QFileSystemMetaData &metaData)
2295#if defined(Q_OS_DARWIN)
2296 if (!metaData.hasFlags(QFileSystemMetaData::CaseSensitive))
2297 fillMetaData(entry, metaData, QFileSystemMetaData::CaseSensitive);
2298 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)
#define REALPATH_FALLBACK_INLINE
static constexpr QLatin1StringView nativeTempPath() noexcept
static QFileSystemNativeId nativeIdFromStatBuf(const QT_STATBUF &statResult)
static int qt_statx(const char *, struct statx *)
bool isValid() const noexcept