Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qfilesystemengine_unix.cpp
Go to the documentation of this file.
1// Copyright (C) 2018 Intel Corporation.
2// Copyright (C) 2016 The Qt Company Ltd.
3// Copyright (C) 2013 Samuel Gaist <samuel.gaist@edeltech.ch>
4// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
5
6#include "qplatformdefs.h"
8#include "qfile.h"
9#include "qstorageinfo.h"
10#include "qurl.h"
11
12#include <QtCore/qoperatingsystemversion.h>
13#include <QtCore/private/qcore_unix_p.h>
14#include <QtCore/private/qfiledevice_p.h>
15#include <QtCore/qvarlengtharray.h>
16#ifndef QT_BOOTSTRAPPED
17# include <QtCore/qstandardpaths.h>
18#endif // QT_BOOTSTRAPPED
19
20#include <pwd.h>
21#include <stdlib.h> // for realpath()
22#include <unistd.h>
23#include <stdio.h>
24#include <errno.h>
25
26#include <chrono>
27#include <memory> // for std::unique_ptr
28
29#if __has_include(<paths.h>)
30# include <paths.h>
31#endif
32#ifndef _PATH_TMP // from <paths.h>
33# define _PATH_TMP "/tmp"
34#endif
35
36#if defined(Q_OS_DARWIN)
37# include <QtCore/private/qcore_mac_p.h>
38# include <CoreFoundation/CFBundle.h>
39#endif
40
41#ifdef Q_OS_MACOS
42#include <CoreServices/CoreServices.h>
43#endif
44
45#if defined(QT_PLATFORM_UIKIT)
46#include <MobileCoreServices/MobileCoreServices.h>
47#endif
48
49#if defined(Q_OS_DARWIN)
50# include <sys/clonefile.h>
51# include <copyfile.h>
52// We cannot include <Foundation/Foundation.h> (it's an Objective-C header), but
53// we need these declarations:
55extern "C" NSString *NSTemporaryDirectory();
56#endif
57
58#if defined(Q_OS_LINUX)
59# include <sys/ioctl.h>
60# include <sys/sendfile.h>
61# include <linux/fs.h>
62
63// in case linux/fs.h is too old and doesn't define it:
64#ifndef FICLONE
65# define FICLONE _IOW(0x94, 9, int)
66#endif
67#endif
68
69#if defined(Q_OS_ANDROID)
70// statx() is disabled on Android because quite a few systems
71// come with sandboxes that kill applications that make system calls outside a
72// whitelist and several Android vendors can't be bothered to update the list.
73# undef STATX_BASIC_STATS
74#endif
75
76#ifndef STATX_ALL
77struct statx { mode_t stx_mode; }; // dummy
78#endif
79
81
82using namespace Qt::StringLiterals;
83
84enum {
85#ifdef Q_OS_ANDROID
86 // On Android, the link(2) system call has been observed to always fail
87 // with EACCES, regardless of whether there are permission problems or not.
89#else
91#endif
92};
93
94#if defined(Q_OS_DARWIN)
95static inline bool hasResourcePropertyFlag(const QFileSystemMetaData &data,
97 CFStringRef key)
98{
99 QCFString path = CFStringCreateWithFileSystemRepresentation(0,
100 entry.nativeFilePath().constData());
101 if (!path)
102 return false;
103
104 QCFType<CFURLRef> url = CFURLCreateWithFileSystemPath(0, path, kCFURLPOSIXPathStyle,
106 if (!url)
107 return false;
108
109 CFBooleanRef value;
110 if (CFURLCopyResourcePropertyForKey(url, key, &value, NULL)) {
111 if (value == kCFBooleanTrue)
112 return true;
113 }
114
115 return false;
116}
117
118static bool isPackage(const QFileSystemMetaData &data, const QFileSystemEntry &entry)
119{
120 if (!data.isDirectory())
121 return false;
122
123 QFileInfo info(entry.filePath());
124 QString suffix = info.suffix();
125
126 if (suffix.length() > 0) {
127 // First step: is the extension known ?
128 QCFType<CFStringRef> extensionRef = suffix.toCFString();
129 QCFType<CFStringRef> uniformTypeIdentifier = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, extensionRef, NULL);
130 if (UTTypeConformsTo(uniformTypeIdentifier, kUTTypeBundle))
131 return true;
132
133 // Second step: check if an application knows the package type
134 QCFType<CFStringRef> path = entry.filePath().toCFString();
135 QCFType<CFURLRef> url = CFURLCreateWithFileSystemPath(0, path, kCFURLPOSIXPathStyle, true);
136
137 UInt32 type, creator;
138 // Well created packages have the PkgInfo file
139 if (CFBundleGetPackageInfoInDirectory(url, &type, &creator))
140 return true;
141
142#ifdef Q_OS_MACOS
143 // Find if an application other than Finder claims to know how to handle the package
144 QCFType<CFURLRef> application = LSCopyDefaultApplicationURLForURL(url,
145 kLSRolesEditor | kLSRolesViewer, nullptr);
146
147 if (application) {
148 QCFType<CFBundleRef> bundle = CFBundleCreate(kCFAllocatorDefault, application);
149 CFStringRef identifier = CFBundleGetIdentifier(bundle);
150 QString applicationId = QString::fromCFString(identifier);
151 if (applicationId != "com.apple.finder"_L1)
152 return true;
153 }
154#endif
155 }
156
157 // Third step: check if the directory has the package bit set
158 return hasResourcePropertyFlag(data, entry, kCFURLIsPackageKey);
159}
160#endif
161
162namespace {
163namespace GetFileTimes {
164qint64 time_t_toMsecs(time_t t)
165{
166 using namespace std::chrono;
167 return milliseconds{seconds{t}}.count();
168}
169
170// fallback set
171[[maybe_unused]] qint64 atime(const QT_STATBUF &statBuffer, ulong)
172{
173 return time_t_toMsecs(statBuffer.st_atime);
174}
175[[maybe_unused]] qint64 birthtime(const QT_STATBUF &, ulong)
176{
177 return Q_INT64_C(0);
178}
179[[maybe_unused]] qint64 ctime(const QT_STATBUF &statBuffer, ulong)
180{
181 return time_t_toMsecs(statBuffer.st_ctime);
182}
183[[maybe_unused]] qint64 mtime(const QT_STATBUF &statBuffer, ulong)
184{
185 return time_t_toMsecs(statBuffer.st_mtime);
186}
187
188// T is either a stat.timespec or statx.statx_timestamp,
189// both have tv_sec and tv_nsec members
190template<typename T>
191qint64 timespecToMSecs(const T &spec)
192{
193 using namespace std::chrono;
194 const nanoseconds nsecs = seconds{spec.tv_sec} + nanoseconds{spec.tv_nsec};
195 return duration_cast<milliseconds>(nsecs).count();
196}
197
198// Xtim, POSIX.1-2008
199template <typename T>
200[[maybe_unused]] static typename std::enable_if<(&T::st_atim, true), qint64>::type
201atime(const T &statBuffer, int)
202{ return timespecToMSecs(statBuffer.st_atim); }
203
204template <typename T>
205[[maybe_unused]] static typename std::enable_if<(&T::st_birthtim, true), qint64>::type
206birthtime(const T &statBuffer, int)
207{ return timespecToMSecs(statBuffer.st_birthtim); }
208
209template <typename T>
210[[maybe_unused]] static typename std::enable_if<(&T::st_ctim, true), qint64>::type
211ctime(const T &statBuffer, int)
212{ return timespecToMSecs(statBuffer.st_ctim); }
213
214template <typename T>
215[[maybe_unused]] static typename std::enable_if<(&T::st_mtim, true), qint64>::type
216mtime(const T &statBuffer, int)
217{ return timespecToMSecs(statBuffer.st_mtim); }
218
219#ifndef st_mtimespec
220// Xtimespec
221template <typename T>
222[[maybe_unused]] static typename std::enable_if<(&T::st_atimespec, true), qint64>::type
223atime(const T &statBuffer, int)
224{ return timespecToMSecs(statBuffer.st_atimespec); }
225
226template <typename T>
227[[maybe_unused]] static typename std::enable_if<(&T::st_birthtimespec, true), qint64>::type
228birthtime(const T &statBuffer, int)
229{ return timespecToMSecs(statBuffer.st_birthtimespec); }
230
231template <typename T>
232[[maybe_unused]] static typename std::enable_if<(&T::st_ctimespec, true), qint64>::type
233ctime(const T &statBuffer, int)
234{ return timespecToMSecs(statBuffer.st_ctimespec); }
235
236template <typename T>
237[[maybe_unused]] static typename std::enable_if<(&T::st_mtimespec, true), qint64>::type
238mtime(const T &statBuffer, int)
239{ return timespecToMSecs(statBuffer.st_mtimespec); }
240#endif
241
242#if !defined(st_mtimensec) && !defined(__alpha__)
243// Xtimensec
244template <typename T>
245[[maybe_unused]] static typename std::enable_if<(&T::st_atimensec, true), qint64>::type
246atime(const T &statBuffer, int)
247{ return statBuffer.st_atime * Q_INT64_C(1000) + statBuffer.st_atimensec / 1000000; }
248
249template <typename T>
250[[maybe_unused]] static typename std::enable_if<(&T::st_birthtimensec, true), qint64>::type
251birthtime(const T &statBuffer, int)
252{ return statBuffer.st_birthtime * Q_INT64_C(1000) + statBuffer.st_birthtimensec / 1000000; }
253
254template <typename T>
255[[maybe_unused]] static typename std::enable_if<(&T::st_ctimensec, true), qint64>::type
256ctime(const T &statBuffer, int)
257{ return statBuffer.st_ctime * Q_INT64_C(1000) + statBuffer.st_ctimensec / 1000000; }
258
259template <typename T>
260[[maybe_unused]] static typename std::enable_if<(&T::st_mtimensec, true), qint64>::type
261mtime(const T &statBuffer, int)
262{ return statBuffer.st_mtime * Q_INT64_C(1000) + statBuffer.st_mtimensec / 1000000; }
263#endif
264} // namespace GetFileTimes
265} // unnamed namespace
266
267#ifdef STATX_BASIC_STATS
268static int qt_real_statx(int fd, const char *pathname, int flags, struct statx *statxBuffer)
269{
270 unsigned mask = STATX_BASIC_STATS | STATX_BTIME;
271 int ret = statx(fd, pathname, flags | AT_NO_AUTOMOUNT, mask, statxBuffer);
272 return ret == -1 ? -errno : 0;
273}
274
275static int qt_statx(const char *pathname, struct statx *statxBuffer)
276{
277 return qt_real_statx(AT_FDCWD, pathname, 0, statxBuffer);
278}
279
280static int qt_lstatx(const char *pathname, struct statx *statxBuffer)
281{
282 return qt_real_statx(AT_FDCWD, pathname, AT_SYMLINK_NOFOLLOW, statxBuffer);
283}
284
285static int qt_fstatx(int fd, struct statx *statxBuffer)
286{
287 return qt_real_statx(fd, "", AT_EMPTY_PATH, statxBuffer);
288}
289
290inline void QFileSystemMetaData::fillFromStatxBuf(const struct statx &statxBuffer)
291{
292 // Permissions
293 if (statxBuffer.stx_mode & S_IRUSR)
295 if (statxBuffer.stx_mode & S_IWUSR)
297 if (statxBuffer.stx_mode & S_IXUSR)
299
300 if (statxBuffer.stx_mode & S_IRGRP)
302 if (statxBuffer.stx_mode & S_IWGRP)
304 if (statxBuffer.stx_mode & S_IXGRP)
306
307 if (statxBuffer.stx_mode & S_IROTH)
309 if (statxBuffer.stx_mode & S_IWOTH)
311 if (statxBuffer.stx_mode & S_IXOTH)
313
314 // Type
315 if (S_ISLNK(statxBuffer.stx_mode))
316 entryFlags |= QFileSystemMetaData::LinkType;
317 if ((statxBuffer.stx_mode & S_IFMT) == S_IFREG)
318 entryFlags |= QFileSystemMetaData::FileType;
319 else if ((statxBuffer.stx_mode & S_IFMT) == S_IFDIR)
321 else if ((statxBuffer.stx_mode & S_IFMT) != S_IFBLK)
323
324 // Attributes
325 entryFlags |= QFileSystemMetaData::ExistsAttribute; // inode exists
326 if (statxBuffer.stx_nlink == 0)
328 size_ = qint64(statxBuffer.stx_size);
329
330 // Times
331 using namespace GetFileTimes;
332 accessTime_ = timespecToMSecs(statxBuffer.stx_atime);
333 metadataChangeTime_ = timespecToMSecs(statxBuffer.stx_ctime);
334 modificationTime_ = timespecToMSecs(statxBuffer.stx_mtime);
335 const bool birthMask = statxBuffer.stx_mask & STATX_BTIME;
336 birthTime_ = birthMask ? timespecToMSecs(statxBuffer.stx_btime) : 0;
337
338 userId_ = statxBuffer.stx_uid;
339 groupId_ = statxBuffer.stx_gid;
340}
341#else
342static int qt_statx(const char *, struct statx *)
343{ return -ENOSYS; }
344
345static int qt_lstatx(const char *, struct statx *)
346{ return -ENOSYS; }
347
348static int qt_fstatx(int, struct statx *)
349{ return -ENOSYS; }
350
351inline void QFileSystemMetaData::fillFromStatxBuf(const struct statx &)
352{ }
353#endif
354
355//static
357{
358 data.entryFlags &= ~QFileSystemMetaData::PosixStatFlags;
360
361 struct statx statxBuffer;
362
363 int ret = qt_fstatx(fd, &statxBuffer);
364 if (ret != -ENOSYS) {
365 if (ret == 0) {
366 data.fillFromStatxBuf(statxBuffer);
367 return true;
368 }
369 return false;
370 }
371
372 QT_STATBUF statBuffer;
373
374 if (QT_FSTAT(fd, &statBuffer) == 0) {
375 data.fillFromStatBuf(statBuffer);
376 return true;
377 }
378
379 return false;
380}
381
382#if defined(_DEXTRA_FIRST)
383static void fillStat64fromStat32(struct stat64 *statBuf64, const struct stat &statBuf32)
384{
385 statBuf64->st_mode = statBuf32.st_mode;
386 statBuf64->st_size = statBuf32.st_size;
387#if _POSIX_VERSION >= 200809L
388 statBuf64->st_ctim = statBuf32.st_ctim;
389 statBuf64->st_mtim = statBuf32.st_mtim;
390 statBuf64->st_atim = statBuf32.st_atim;
391#else
392 statBuf64->st_ctime = statBuf32.st_ctime;
393 statBuf64->st_mtime = statBuf32.st_mtime;
394 statBuf64->st_atime = statBuf32.st_atime;
395#endif
396 statBuf64->st_uid = statBuf32.st_uid;
397 statBuf64->st_gid = statBuf32.st_gid;
398}
399#endif
400
401void QFileSystemMetaData::fillFromStatBuf(const QT_STATBUF &statBuffer)
402{
403 // Permissions
404 if (statBuffer.st_mode & S_IRUSR)
406 if (statBuffer.st_mode & S_IWUSR)
408 if (statBuffer.st_mode & S_IXUSR)
410
411 if (statBuffer.st_mode & S_IRGRP)
413 if (statBuffer.st_mode & S_IWGRP)
415 if (statBuffer.st_mode & S_IXGRP)
417
418 if (statBuffer.st_mode & S_IROTH)
420 if (statBuffer.st_mode & S_IWOTH)
422 if (statBuffer.st_mode & S_IXOTH)
424
425 // Type
426 if ((statBuffer.st_mode & S_IFMT) == S_IFREG)
427 entryFlags |= QFileSystemMetaData::FileType;
428 else if ((statBuffer.st_mode & S_IFMT) == S_IFDIR)
430 else if ((statBuffer.st_mode & S_IFMT) != S_IFBLK)
432
433 // Attributes
434 entryFlags |= QFileSystemMetaData::ExistsAttribute; // inode exists
435 if (statBuffer.st_nlink == 0)
437 size_ = statBuffer.st_size;
438#ifdef UF_HIDDEN
439 if (statBuffer.st_flags & UF_HIDDEN) {
441 knownFlagsMask |= QFileSystemMetaData::HiddenAttribute;
442 }
443#endif
444
445 // Times
446 accessTime_ = GetFileTimes::atime(statBuffer, 0);
447 birthTime_ = GetFileTimes::birthtime(statBuffer, 0);
448 metadataChangeTime_ = GetFileTimes::ctime(statBuffer, 0);
449 modificationTime_ = GetFileTimes::mtime(statBuffer, 0);
450
451 userId_ = statBuffer.st_uid;
452 groupId_ = statBuffer.st_gid;
453}
454
455void QFileSystemMetaData::fillFromDirEnt(const QT_DIRENT &entry)
456{
457#if defined(_DEXTRA_FIRST)
458 knownFlagsMask = {};
459 entryFlags = {};
460 for (dirent_extra *extra = _DEXTRA_FIRST(&entry); _DEXTRA_VALID(extra, &entry);
461 extra = _DEXTRA_NEXT(extra)) {
462 if (extra->d_type == _DTYPE_STAT || extra->d_type == _DTYPE_LSTAT) {
463
464 const struct dirent_extra_stat * const extra_stat =
465 reinterpret_cast<struct dirent_extra_stat *>(extra);
466
467 // Remember whether this was a link or not, this saves an lstat() call later.
468 if (extra->d_type == _DTYPE_LSTAT) {
469 knownFlagsMask |= QFileSystemMetaData::LinkType;
470 if (S_ISLNK(extra_stat->d_stat.st_mode))
471 entryFlags |= QFileSystemMetaData::LinkType;
472 }
473
474 // For symlinks, the extra type _DTYPE_LSTAT doesn't work for filling out the meta data,
475 // as we need the stat() information there, not the lstat() information.
476 // In this case, don't use the extra information.
477 // Unfortunately, readdir() never seems to return extra info of type _DTYPE_STAT, so for
478 // symlinks, we always incur the cost of an extra stat() call later.
479 if (S_ISLNK(extra_stat->d_stat.st_mode) && extra->d_type == _DTYPE_LSTAT)
480 continue;
481
482#if defined(QT_USE_XOPEN_LFS_EXTENSIONS) && defined(QT_LARGEFILE_SUPPORT)
483 // Even with large file support, d_stat is always of type struct stat, not struct stat64,
484 // so it needs to be converted
485 struct stat64 statBuf;
486 fillStat64fromStat32(&statBuf, extra_stat->d_stat);
487 fillFromStatBuf(statBuf);
488#else
489 fillFromStatBuf(extra_stat->d_stat);
490#endif
491 knownFlagsMask |= QFileSystemMetaData::PosixStatFlags;
492 if (!S_ISLNK(extra_stat->d_stat.st_mode)) {
493 knownFlagsMask |= QFileSystemMetaData::ExistsAttribute;
495 }
496 }
497 }
498#elif defined(_DIRENT_HAVE_D_TYPE) || defined(Q_OS_BSD4)
499 // BSD4 includes OS X and iOS
500
501 // ### This will clear all entry flags and knownFlagsMask
502 switch (entry.d_type)
503 {
504 case DT_DIR:
505 knownFlagsMask = QFileSystemMetaData::LinkType
510
513
514 break;
515
516 case DT_BLK:
517 knownFlagsMask = QFileSystemMetaData::LinkType
524
526
527 break;
528
529 case DT_CHR:
530 case DT_FIFO:
531 case DT_SOCK:
532 // ### System attribute
533 knownFlagsMask = QFileSystemMetaData::LinkType
540
543
544 break;
545
546 case DT_LNK:
547 knownFlagsMask = QFileSystemMetaData::LinkType;
549 break;
550
551 case DT_REG:
552 knownFlagsMask = QFileSystemMetaData::LinkType
558
561
562 break;
563
564 case DT_UNKNOWN:
565 default:
566 clear();
567 }
568#else
570#endif
571}
572
573//static
575{
576 Q_CHECK_FILE_NAME(link, link);
577
579 if (s.size() > 0) {
580 QString ret;
583 if (data.isDirectory() && s[0] != '/') {
584 QDir parent(link.filePath());
585 parent.cdUp();
586 ret = parent.path();
587 if (!ret.isEmpty() && !ret.endsWith(u'/'))
588 ret += u'/';
589 }
591
592 if (!ret.startsWith(u'/'))
593 ret.prepend(absoluteName(link).path() + u'/');
595 if (ret.size() > 1 && ret.endsWith(u'/'))
596 ret.chop(1);
597 return QFileSystemEntry(ret);
598 }
599#if defined(Q_OS_DARWIN)
600 {
601 QCFString path = CFStringCreateWithFileSystemRepresentation(0,
603 if (!path)
604 return QFileSystemEntry();
605
606 QCFType<CFURLRef> url = CFURLCreateWithFileSystemPath(0, path, kCFURLPOSIXPathStyle,
608 if (!url)
609 return QFileSystemEntry();
610
611 QCFType<CFDataRef> bookmarkData = CFURLCreateBookmarkDataFromFile(0, url, NULL);
612 if (!bookmarkData)
613 return QFileSystemEntry();
614
615 QCFType<CFURLRef> resolvedUrl = CFURLCreateByResolvingBookmarkData(0,
616 bookmarkData,
617 (CFURLBookmarkResolutionOptions)(kCFBookmarkResolutionWithoutUIMask
618 | kCFBookmarkResolutionWithoutMountingMask), NULL, NULL, NULL, NULL);
619 if (!resolvedUrl)
620 return QFileSystemEntry();
621
622 QCFString cfstr(CFURLCopyFileSystemPath(resolvedUrl, kCFURLPOSIXPathStyle));
623 if (!cfstr)
624 return QFileSystemEntry();
625
626 return QFileSystemEntry(QString::fromCFString(cfstr));
627 }
628#endif
629 return QFileSystemEntry();
630}
631
632//static
635{
639 return QFileSystemEntry(ret);
640}
641
642//static
644{
646
647#if !defined(Q_OS_DARWIN) && !defined(Q_OS_QNX) && !defined(Q_OS_ANDROID) && !defined(Q_OS_HAIKU) && _POSIX_VERSION < 200809L
648 // realpath(X,0) is not supported
649 Q_UNUSED(data);
650 return QFileSystemEntry(slowCanonicalized(absoluteName(entry).filePath()));
651#else
652# if defined(Q_OS_DARWIN) || defined(Q_OS_ANDROID) || _POSIX_VERSION < 200801L
653 // used to store the result of realpath in case where realpath cannot allocate itself
654 char stack_result[PATH_MAX + 1];
655#else
656 // enables unconditionally passing stack_result below
657 std::nullptr_t stack_result = nullptr;
658# endif
659 auto resolved_path_deleter = [&](char *ptr) {
660 // frees resolved_name if it was allocated by realpath
661# if defined(Q_OS_DARWIN) || defined(Q_OS_ANDROID) || _POSIX_VERSION < 200801L
662 // ptr is either null, or points to stack_result
663 Q_ASSERT(!ptr || ptr == stack_result);
664 return;
665#else
666 free(ptr);
667# endif
668 };
669 std::unique_ptr<char, decltype (resolved_path_deleter)> resolved_name {nullptr, resolved_path_deleter};
670# if defined(Q_OS_DARWIN) || defined(Q_OS_ANDROID)
671 // On some Android and macOS versions, realpath() will return a path even if
672 // it does not exist. To work around this, we check existence in advance.
675
676 if (!data.exists())
677 errno = ENOENT;
678 else
679 resolved_name.reset(realpath(entry.nativeFilePath().constData(), stack_result));
680# else
681 resolved_name.reset(realpath(entry.nativeFilePath().constData(), stack_result));
682# endif
683 if (resolved_name) {
688 } else if (errno == ENOENT || errno == ENOTDIR) { // file doesn't exist
691 return QFileSystemEntry();
692 }
693 return entry;
694#endif
695}
696
697//static
699{
701
702 if (entry.isAbsolute() && entry.isClean())
703 return entry;
704
705 QByteArray orig = entry.nativeFilePath();
707 if (orig.isEmpty() || !orig.startsWith('/')) {
709 result = cur.nativeFilePath();
710 }
711 if (!orig.isEmpty() && !(orig.size() == 1 && orig[0] == '.')) {
712 if (!result.isEmpty() && !result.endsWith('/'))
713 result.append('/');
714 result.append(orig);
715 }
716
717 if (result.size() == 1 && result[0] == '/')
719 const bool isDir = result.endsWith('/');
720
721 /* as long as QDir::cleanPath() operates on a QString we have to convert to a string here.
722 * ideally we never convert to a string since that loses information. Please fix after
723 * we get a QByteArray version of QDir::cleanPath()
724 */
726 QString stringVersion = QDir::cleanPath(resultingEntry.filePath());
727 if (isDir)
728 stringVersion.append(u'/');
729 return QFileSystemEntry(stringVersion);
730}
731
732//static
734{
736
737 QT_STATBUF statResult;
738 if (QT_STAT(entry.nativeFilePath().constData(), &statResult)) {
739 if (errno != ENOENT)
740 qErrnoWarning("stat() failed for '%s'", entry.nativeFilePath().constData());
741 return QByteArray();
742 }
743 QByteArray result = QByteArray::number(quint64(statResult.st_dev), 16);
744 result += ':';
745 result += QByteArray::number(quint64(statResult.st_ino));
746 return result;
747}
748
749//static
751{
752 QT_STATBUF statResult;
753 if (QT_FSTAT(fd, &statResult)) {
754 qErrnoWarning("fstat() failed for fd %d", fd);
755 return QByteArray();
756 }
757 QByteArray result = QByteArray::number(quint64(statResult.st_dev), 16);
758 result += ':';
759 result += QByteArray::number(quint64(statResult.st_ino));
760 return result;
761}
762
763//static
765{
766#if QT_CONFIG(thread) && defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_OPENBSD)
767 long size_max = sysconf(_SC_GETPW_R_SIZE_MAX);
768 if (size_max == -1)
769 size_max = 1024;
771#endif
772
773#if !defined(Q_OS_INTEGRITY) && !defined(Q_OS_WASM)
774 struct passwd *pw = nullptr;
775#if QT_CONFIG(thread) && defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_OPENBSD) && !defined(Q_OS_VXWORKS)
776 struct passwd entry;
777 getpwuid_r(userId, &entry, buf.data(), buf.size(), &pw);
778#else
779 pw = getpwuid(userId);
780#endif
781 if (pw)
782 return QFile::decodeName(QByteArray(pw->pw_name));
783#else // Integrity || WASM
784 Q_UNUSED(userId);
785#endif
786 return QString();
787}
788
789//static
791{
792#if QT_CONFIG(thread) && defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_OPENBSD)
793 long size_max = sysconf(_SC_GETPW_R_SIZE_MAX);
794 if (size_max == -1)
795 size_max = 1024;
797#endif
798
799#if !defined(Q_OS_INTEGRITY) && !defined(Q_OS_WASM)
800 struct group *gr = nullptr;
801#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))
802 size_max = sysconf(_SC_GETGR_R_SIZE_MAX);
803 if (size_max == -1)
804 size_max = 1024;
805 buf.resize(size_max);
806 struct group entry;
807 // Some large systems have more members than the POSIX max size
808 // Loop over by doubling the buffer size (upper limit 250k)
809 for (long size = size_max; size < 256000; size += size)
810 {
811 buf.resize(size);
812 // ERANGE indicates that the buffer was too small
813 if (!getgrgid_r(groupId, &entry, buf.data(), buf.size(), &gr)
814 || errno != ERANGE)
815 break;
816 }
817#else
818 gr = getgrgid(groupId);
819#endif
820 if (gr)
821 return QFile::decodeName(QByteArray(gr->gr_name));
822#else // Integrity || WASM
823 Q_UNUSED(groupId);
824#endif
825 return QString();
826}
827
828#if defined(Q_OS_DARWIN)
829//static
831{
832 QCFType<CFURLRef> url = CFURLCreateWithFileSystemPath(0, QCFString(entry.filePath()),
833 kCFURLPOSIXPathStyle, true);
834 if (QCFType<CFDictionaryRef> dict = CFBundleCopyInfoDictionaryForURL(url)) {
835 if (CFTypeRef name = (CFTypeRef)CFDictionaryGetValue(dict, kCFBundleNameKey)) {
836 if (CFGetTypeID(name) == CFStringGetTypeID())
837 return QString::fromCFString((CFStringRef)name);
838 }
839 }
840 return QString();
841}
842#endif
843
844//static
846 QFileSystemMetaData::MetaDataFlags what)
847{
848 Q_CHECK_FILE_NAME(entry, false);
849
850#if defined(Q_OS_DARWIN)
854 }
857#endif
858#ifdef UF_HIDDEN
860 // OS X >= 10.5: st_flags & UF_HIDDEN
862 }
863#endif // defined(Q_OS_DARWIN)
864
865 // if we're asking for any of the stat(2) flags, then we're getting them all
868
869 data.entryFlags &= ~what;
870
871 const QByteArray nativeFilePath = entry.nativeFilePath();
872 int entryErrno = 0; // innocent until proven otherwise
873
874 // first, we may try lstat(2). Possible outcomes:
875 // - success and is a symlink: filesystem entry exists, but we need stat(2)
876 // -> statResult = -1;
877 // - success and is not a symlink: filesystem entry exists and we're done
878 // -> statResult = 0
879 // - failure: really non-existent filesystem entry
880 // -> entryExists = false; statResult = 0;
881 // both stat(2) and lstat(2) may generate a number of different errno
882 // conditions, but of those, the only ones that could happen and the
883 // entry still exist are EACCES, EFAULT, ENOMEM and EOVERFLOW. If we get
884 // EACCES or ENOMEM, then we have no choice on how to proceed, so we may
885 // as well conclude it doesn't exist; EFAULT can't happen and EOVERFLOW
886 // shouldn't happen because we build in _LARGEFIE64.
887 union {
888 QT_STATBUF statBuffer;
889 struct statx statxBuffer;
890 };
891 int statResult = -1;
893 mode_t mode = 0;
894 statResult = qt_lstatx(nativeFilePath, &statxBuffer);
895 if (statResult == -ENOSYS) {
896 // use lstst(2)
897 statResult = QT_LSTAT(nativeFilePath, &statBuffer);
898 if (statResult == 0)
899 mode = statBuffer.st_mode;
900 } else if (statResult == 0) {
901 statResult = 1; // record it was statx(2) that succeeded
902 mode = statxBuffer.stx_mode;
903 }
904
905 if (statResult >= 0) {
906 if (S_ISLNK(mode)) {
907 // it's a symlink, we don't know if the file "exists"
909 statResult = -1; // force stat(2) below
910 } else {
911 // it's a reagular file and it exists
912 if (statResult)
913 data.fillFromStatxBuf(statxBuffer);
914 else
915 data.fillFromStatBuf(statBuffer);
919 }
920 } else {
921 // it doesn't exist
922 entryErrno = errno;
924 }
925
926 data.knownFlagsMask |= QFileSystemMetaData::LinkType;
927 }
928
929 // second, we try a regular stat(2)
930 if (statResult == -1 && (what & QFileSystemMetaData::PosixStatFlags)) {
931 if (entryErrno == 0 && statResult == -1) {
932 data.entryFlags &= ~QFileSystemMetaData::PosixStatFlags;
933 statResult = qt_statx(nativeFilePath, &statxBuffer);
934 if (statResult == -ENOSYS) {
935 // use stat(2)
936 statResult = QT_STAT(nativeFilePath, &statBuffer);
937 if (statResult == 0)
938 data.fillFromStatBuf(statBuffer);
939 } else if (statResult == 0) {
940 data.fillFromStatxBuf(statxBuffer);
941 }
942 }
943
944 if (statResult != 0) {
945 entryErrno = errno;
946 data.birthTime_ = 0;
947 data.metadataChangeTime_ = 0;
948 data.modificationTime_ = 0;
949 data.accessTime_ = 0;
950 data.size_ = 0;
951 data.userId_ = (uint) -2;
952 data.groupId_ = (uint) -2;
953 }
954
955 // reset the mask
958 }
959
960 // third, we try access(2)
962 // calculate user permissions
963 auto checkAccess = [&](QFileSystemMetaData::MetaDataFlag flag, int mode) {
964 if (entryErrno != 0 || (what & flag) == 0)
965 return;
966 if (QT_ACCESS(nativeFilePath, mode) == 0) {
967 // access ok (and file exists)
968 data.entryFlags |= flag | QFileSystemMetaData::ExistsAttribute;
969 } else if (errno != EACCES && errno != EROFS) {
970 entryErrno = errno;
971 }
972 };
973
977
978 // if we still haven't found out if the file exists, try F_OK
979 if (entryErrno == 0 && (data.entryFlags & QFileSystemMetaData::ExistsAttribute) == 0) {
980 if (QT_ACCESS(nativeFilePath, F_OK) == -1)
981 entryErrno = errno;
982 else
984 }
985
986 data.knownFlagsMask |= (what & QFileSystemMetaData::UserPermissions) |
988 }
989
990#if defined(Q_OS_DARWIN)
992 if (entryErrno == 0 && hasResourcePropertyFlag(data, entry, kCFURLIsAliasFileKey)) {
993 // kCFURLIsAliasFileKey includes symbolic links, so filter those out
994 if (!(data.entryFlags & QFileSystemMetaData::LinkType))
996 }
997 data.knownFlagsMask |= QFileSystemMetaData::AliasType;
998 }
999
1001 if (entryErrno == 0 && isPackage(data, entry))
1003
1004 data.knownFlagsMask |= QFileSystemMetaData::BundleType;
1005 }
1006#endif
1007
1009 && !data.isHidden()) {
1010 QString fileName = entry.fileName();
1011 if (fileName.startsWith(u'.')
1012#if defined(Q_OS_DARWIN)
1013 || (entryErrno == 0 && hasResourcePropertyFlag(data, entry, kCFURLIsHiddenKey))
1014#endif
1015 )
1018 }
1019
1020 if (entryErrno != 0) {
1021 what &= ~QFileSystemMetaData::LinkType; // don't clear link: could be broken symlink
1022 data.clearFlags(what);
1023 return false;
1024 }
1025 return true;
1026}
1027
1028// static
1029bool QFileSystemEngine::cloneFile(int srcfd, int dstfd, const QFileSystemMetaData &knownData)
1030{
1031 QT_STATBUF statBuffer;
1033 knownData.isFile()) {
1034 statBuffer.st_mode = S_IFREG;
1035 } else if (knownData.hasFlags(QFileSystemMetaData::PosixStatFlags) &&
1036 knownData.isDirectory()) {
1037 return false; // fcopyfile(3) returns success on directories
1038 } else if (QT_FSTAT(srcfd, &statBuffer) == -1) {
1039 return false;
1040 } else if (!S_ISREG((statBuffer.st_mode))) {
1041 // not a regular file, let QFile do the copy
1042 return false;
1043 }
1044
1045#if defined(Q_OS_LINUX)
1046 // first, try FICLONE (only works on regular files and only on certain fs)
1047 if (::ioctl(dstfd, FICLONE, srcfd) == 0)
1048 return true;
1049
1050 // Second, try sendfile (it can send to some special types too).
1051 // sendfile(2) is limited in the kernel to 2G - 4k
1052 const size_t SendfileSize = 0x7ffff000;
1053
1054 ssize_t n = ::sendfile(dstfd, srcfd, nullptr, SendfileSize);
1055 if (n == -1) {
1056 // if we got an error here, give up and try at an upper layer
1057 return false;
1058 }
1059
1060 while (n) {
1061 n = ::sendfile(dstfd, srcfd, nullptr, SendfileSize);
1062 if (n == -1) {
1063 // uh oh, this is probably a real error (like ENOSPC), but we have
1064 // no way to notify QFile of partial success, so just erase any work
1065 // done (hopefully we won't get any errors, because there's nothing
1066 // we can do about them)
1067 n = ftruncate(dstfd, 0);
1068 n = lseek(srcfd, 0, SEEK_SET);
1069 n = lseek(dstfd, 0, SEEK_SET);
1070 return false;
1071 }
1072 }
1073
1074 return true;
1075#elif defined(Q_OS_DARWIN)
1076 // try fcopyfile
1077 return fcopyfile(srcfd, dstfd, nullptr, COPYFILE_DATA | COPYFILE_STAT) == 0;
1078#else
1079 Q_UNUSED(dstfd);
1080 return false;
1081#endif
1082}
1083
1084// Note: if \a shouldMkdirFirst is false, we assume the caller did try to mkdir
1085// before calling this function.
1086static bool createDirectoryWithParents(const QByteArray &nativeName, mode_t mode,
1087 bool shouldMkdirFirst = true)
1088{
1089 // helper function to check if a given path is a directory, since mkdir can
1090 // fail if the dir already exists (it may have been created by another
1091 // thread or another process)
1092 const auto isDir = [](const QByteArray &nativeName) {
1093 QT_STATBUF st;
1094 return QT_STAT(nativeName.constData(), &st) == 0 && (st.st_mode & S_IFMT) == S_IFDIR;
1095 };
1096
1097 if (shouldMkdirFirst && QT_MKDIR(nativeName, mode) == 0)
1098 return true;
1099 if (errno == EISDIR)
1100 return true;
1101 if (errno == EEXIST)
1102 return isDir(nativeName);
1103 if (errno != ENOENT)
1104 return false;
1105
1106 // mkdir failed because the parent dir doesn't exist, so try to create it
1107 qsizetype slash = nativeName.lastIndexOf('/');
1108 if (slash < 1)
1109 return false;
1110
1111 QByteArray parentNativeName = nativeName.left(slash);
1112 if (!createDirectoryWithParents(parentNativeName, mode))
1113 return false;
1114
1115 // try again
1116 if (QT_MKDIR(nativeName, mode) == 0)
1117 return true;
1118 return errno == EEXIST && isDir(nativeName);
1119}
1120
1121//static
1123 std::optional<QFile::Permissions> permissions)
1124{
1125 QString dirName = entry.filePath();
1126 Q_CHECK_FILE_NAME(dirName, false);
1127
1128 // Darwin doesn't support trailing /'s, so remove for everyone
1129 while (dirName.size() > 1 && dirName.endsWith(u'/'))
1130 dirName.chop(1);
1131
1132 // try to mkdir this directory
1133 QByteArray nativeName = QFile::encodeName(dirName);
1134 mode_t mode = permissions ? QtPrivate::toMode_t(*permissions) : 0777;
1135 if (QT_MKDIR(nativeName, mode) == 0)
1136 return true;
1137 if (!createParents)
1138 return false;
1139
1140 return createDirectoryWithParents(nativeName, mode, false);
1141}
1142
1143//static
1144bool QFileSystemEngine::removeDirectory(const QFileSystemEntry &entry, bool removeEmptyParents)
1145{
1146 Q_CHECK_FILE_NAME(entry, false);
1147
1148 if (removeEmptyParents) {
1149 QString dirName = QDir::cleanPath(entry.filePath());
1150 for (qsizetype oldslash = 0, slash=dirName.size(); slash > 0; oldslash = slash) {
1151 const QByteArray chunk = QFile::encodeName(dirName.left(slash));
1152 QT_STATBUF st;
1153 if (QT_STAT(chunk.constData(), &st) != -1) {
1154 if ((st.st_mode & S_IFMT) != S_IFDIR)
1155 return false;
1156 if (::rmdir(chunk.constData()) != 0)
1157 return oldslash != 0;
1158 } else {
1159 return false;
1160 }
1161 slash = dirName.lastIndexOf(QDir::separator(), oldslash-1);
1162 }
1163 return true;
1164 }
1165 return rmdir(QFile::encodeName(entry.filePath()).constData()) == 0;
1166}
1167
1168//static
1170{
1171 Q_CHECK_FILE_NAME(source, false);
1172 Q_CHECK_FILE_NAME(target, false);
1173
1174 if (::symlink(source.nativeFilePath().constData(), target.nativeFilePath().constData()) == 0)
1175 return true;
1177 return false;
1178}
1179
1180#ifndef Q_OS_DARWIN
1181/*
1182 Implementing as per https://specifications.freedesktop.org/trash-spec/trashspec-1.0.html
1183*/
1184
1185// bootstrapped tools don't need this, and we don't want QStorageInfo
1186#ifndef QT_BOOTSTRAPPED
1188{
1189 auto makeTrashDir = [](const QDir &topDir, const QString &trashDir) -> QString {
1190 auto ownerPerms = QFileDevice::ReadOwner
1193 QString targetDir = topDir.filePath(trashDir);
1194 // deliberately not using mkpath, since we want to fail if topDir doesn't exist
1195 if (topDir.mkdir(trashDir))
1196 QFile::setPermissions(targetDir, ownerPerms);
1197 if (QFileInfo(targetDir).isDir())
1198 return targetDir;
1199 return QString();
1200 };
1201 auto isSticky = [](const QFileInfo &fileInfo) -> bool {
1202 struct stat st;
1203 if (stat(QFile::encodeName(fileInfo.absoluteFilePath()).constData(), &st) == 0)
1204 return st.st_mode & S_ISVTX;
1205
1206 return false;
1207 };
1208
1209 QString trash;
1210 const QStorageInfo sourceStorage(sourcePath);
1211 const QStorageInfo homeStorage(QDir::home());
1212 // We support trashing of files outside the users home partition
1213 if (sourceStorage != homeStorage) {
1214 const auto dotTrash = ".Trash"_L1;
1215 QDir topDir(sourceStorage.rootPath());
1216 /*
1217 Method 1:
1218 "An administrator can create an $topdir/.Trash directory. The permissions on this
1219 directories should permit all users who can trash files at all to write in it;
1220 and the “sticky bit” in the permissions must be set, if the file system supports
1221 it.
1222 When trashing a file from a non-home partition/device, an implementation
1223 (if it supports trashing in top directories) MUST check for the presence
1224 of $topdir/.Trash."
1225 */
1226 const QString userID = QString::number(::getuid());
1227 if (topDir.cd(dotTrash)) {
1228 const QFileInfo trashInfo(topDir.path());
1229
1230 // we MUST check that the sticky bit is set, and that it is not a symlink
1231 if (trashInfo.isSymLink()) {
1232 // we SHOULD report the failed check to the administrator
1233 qCritical("Warning: '%s' is a symlink to '%s'",
1234 trashInfo.absoluteFilePath().toLocal8Bit().constData(),
1235 trashInfo.symLinkTarget().toLatin1().constData());
1236 } else if (!isSticky(trashInfo)) {
1237 // we SHOULD report the failed check to the administrator
1238 qCritical("Warning: '%s' doesn't have sticky bit set!",
1239 trashInfo.absoluteFilePath().toLocal8Bit().constData());
1240 } else if (trashInfo.isDir()) {
1241 /*
1242 "If the directory exists and passes the checks, a subdirectory of the
1243 $topdir/.Trash directory is to be used as the user's trash directory
1244 for this partition/device. The name of this subdirectory is the numeric
1245 identifier of the current user ($topdir/.Trash/$uid).
1246 When trashing a file, if this directory does not exist for the current user,
1247 the implementation MUST immediately create it, without any warnings or
1248 delays for the user."
1249 */
1250 trash = makeTrashDir(topDir, userID);
1251 }
1252 }
1253 /*
1254 Method 2:
1255 "If an $topdir/.Trash directory is absent, an $topdir/.Trash-$uid directory is to be
1256 used as the user's trash directory for this device/partition. [...] When trashing a
1257 file, if an $topdir/.Trash-$uid directory does not exist, the implementation MUST
1258 immediately create it, without any warnings or delays for the user."
1259 */
1260 if (trash.isEmpty()) {
1261 topDir = QDir(sourceStorage.rootPath());
1262 const QString userTrashDir = dotTrash + u'-' + userID;
1263 trash = makeTrashDir(topDir, userTrashDir);
1264 }
1265 }
1266 /*
1267 "If both (1) and (2) fail [...], the implementation MUST either trash the
1268 file into the user's “home trash” or refuse to trash it."
1269
1270 We trash the file into the user's home trash.
1271
1272 "Its name and location are $XDG_DATA_HOME/Trash"; $XDG_DATA_HOME is what
1273 QStandardPaths returns for GenericDataLocation. If that doesn't exist, then
1274 we are not running on a freedesktop.org-compliant environment, and give up.
1275 */
1276 if (trash.isEmpty()) {
1278 trash = makeTrashDir(topDir, "Trash"_L1);
1279 if (!QFileInfo(trash).isDir()) {
1280 qWarning("Unable to establish trash directory in %s",
1281 topDir.path().toLocal8Bit().constData());
1282 }
1283 }
1284
1285 return trash;
1286}
1287#endif // QT_BOOTSTRAPPED
1288
1289//static
1291 QFileSystemEntry &newLocation, QSystemError &error)
1292{
1293#ifdef QT_BOOTSTRAPPED
1295 Q_UNUSED(newLocation);
1297 return false;
1298#else
1299 const QFileInfo sourceInfo(source.filePath());
1300 if (!sourceInfo.exists()) {
1302 return false;
1303 }
1304 const QString sourcePath = sourceInfo.absoluteFilePath();
1305
1306 QDir trashDir(freeDesktopTrashLocation(sourcePath));
1307 if (!trashDir.exists())
1308 return false;
1309 /*
1310 "A trash directory contains two subdirectories, named info and files."
1311 */
1312 const auto filesDir = "files"_L1;
1313 const auto infoDir = "info"_L1;
1314 trashDir.mkdir(filesDir);
1315 int savedErrno = errno;
1316 trashDir.mkdir(infoDir);
1317 if (!savedErrno)
1318 savedErrno = errno;
1319 if (!trashDir.exists(filesDir) || !trashDir.exists(infoDir)) {
1321 return false;
1322 }
1323 /*
1324 "The $trash/files directory contains the files and directories that were trashed.
1325 The names of files in this directory are to be determined by the implementation;
1326 the only limitation is that they must be unique within the directory. Even if a
1327 file with the same name and location gets trashed many times, each subsequent
1328 trashing must not overwrite a previous copy."
1329 */
1330 const QString trashedName = sourceInfo.isDir()
1331 ? QDir(sourcePath).dirName()
1332 : sourceInfo.fileName();
1333 QString uniqueTrashedName = u'/' + trashedName;
1334 QString infoFileName;
1335 int counter = 0;
1336 QFile infoFile;
1337 auto makeUniqueTrashedName = [trashedName, &counter]() -> QString {
1338 return QString::asprintf("/%ls-%04d", qUtf16Printable(trashedName), ++counter);
1339 };
1340 do {
1341 while (QFile::exists(trashDir.filePath(filesDir) + uniqueTrashedName))
1342 uniqueTrashedName = makeUniqueTrashedName();
1343 /*
1344 "The $trash/info directory contains an "information file" for every file and directory
1345 in $trash/files. This file MUST have exactly the same name as the file or directory in
1346 $trash/files, plus the extension ".trashinfo"
1347 [...]
1348 When trashing a file or directory, the implementation MUST create the corresponding
1349 file in $trash/info first. Moreover, it MUST try to do this in an atomic fashion,
1350 so that if two processes try to trash files with the same filename this will result
1351 in two different trash files. On Unix-like systems this is done by generating a
1352 filename, and then opening with O_EXCL. If that succeeds the creation was atomic
1353 (at least on the same machine), if it fails you need to pick another filename."
1354 */
1355 infoFileName = trashDir.filePath(infoDir)
1356 + uniqueTrashedName + ".trashinfo"_L1;
1357 infoFile.setFileName(infoFileName);
1359 uniqueTrashedName = makeUniqueTrashedName();
1360 } while (!infoFile.isOpen());
1361
1362 const QString targetPath = trashDir.filePath(filesDir) + uniqueTrashedName;
1363 const QFileSystemEntry target(targetPath);
1364
1365 QString infoPath;
1366 const QStorageInfo storageInfo(sourcePath);
1367 if (storageInfo.isValid() && storageInfo.rootPath() != rootPath() && storageInfo != QStorageInfo(QDir::home())) {
1368 infoPath = sourcePath.mid(storageInfo.rootPath().length());
1369 if (infoPath.front() == u'/')
1370 infoPath = infoPath.mid(1);
1371 } else {
1372 infoPath = sourcePath;
1373 }
1374
1375 /*
1376 We might fail to rename if source and target are on different file systems.
1377 In that case, we don't try further, i.e. copying and removing the original
1378 is usually not what the user would expect to happen.
1379 */
1380 if (!renameFile(source, target, error)) {
1381 infoFile.close();
1382 infoFile.remove();
1383 return false;
1384 }
1385
1387 "[Trash Info]\n"
1388 "Path=" + QUrl::toPercentEncoding(infoPath, "/") + "\n"
1389 "DeletionDate=" + QDateTime::currentDateTime().toString("yyyy-MM-ddThh:mm:ss"_L1).toUtf8()
1390 + "\n";
1391 infoFile.write(info);
1392 infoFile.close();
1393
1394 newLocation = QFileSystemEntry(targetPath);
1395 return true;
1396#endif // QT_BOOTSTRAPPED
1397}
1398#endif // Q_OS_DARWIN
1399
1400//static
1402{
1403#if defined(Q_OS_DARWIN)
1404 if (::clonefile(source.nativeFilePath().constData(),
1405 target.nativeFilePath().constData(), 0) == 0)
1406 return true;
1408 return false;
1409#else
1412 error = QSystemError(ENOSYS, QSystemError::StandardLibraryError); //Function not implemented
1413 return false;
1414#endif
1415}
1416
1417//static
1419{
1420 QFileSystemEntry::NativePath srcPath = source.nativeFilePath();
1421 QFileSystemEntry::NativePath tgtPath = target.nativeFilePath();
1422
1423 Q_CHECK_FILE_NAME(srcPath, false);
1424 Q_CHECK_FILE_NAME(tgtPath, false);
1425
1426#if defined(RENAME_NOREPLACE) && QT_CONFIG(renameat2)
1427 if (renameat2(AT_FDCWD, srcPath, AT_FDCWD, tgtPath, RENAME_NOREPLACE) == 0)
1428 return true;
1429
1430 // We can also get EINVAL for some non-local filesystems.
1431 if (errno != EINVAL) {
1433 return false;
1434 }
1435#endif
1436#if defined(Q_OS_DARWIN) && defined(RENAME_EXCL)
1437 if (renameatx_np(AT_FDCWD, srcPath, AT_FDCWD, tgtPath, RENAME_EXCL) == 0)
1438 return true;
1439 if (errno != ENOTSUP) {
1441 return false;
1442 }
1443#endif
1444
1445 if (SupportsHardlinking && ::link(srcPath, tgtPath) == 0) {
1446 if (::unlink(srcPath) == 0)
1447 return true;
1448
1449 // if we managed to link but can't unlink the source, it's likely
1450 // it's in a directory we don't have write access to; fail the
1451 // renaming instead
1452 int savedErrno = errno;
1453
1454 // this could fail too, but there's nothing we can do about it now
1455 ::unlink(tgtPath);
1456
1458 return false;
1459 } else if (!SupportsHardlinking) {
1460 // man 2 link on Linux has:
1461 // EPERM The filesystem containing oldpath and newpath does not
1462 // support the creation of hard links.
1463 errno = EPERM;
1464 }
1465
1466 switch (errno) {
1467 case EACCES:
1468 case EEXIST:
1469 case ENAMETOOLONG:
1470 case ENOENT:
1471 case ENOTDIR:
1472 case EROFS:
1473 case EXDEV:
1474 // accept the error from link(2) (especially EEXIST) and don't retry
1475 break;
1476
1477 default:
1478 // fall back to rename()
1479 // ### Race condition. If a file is moved in after this, it /will/ be
1480 // overwritten.
1481 if (::rename(srcPath, tgtPath) == 0)
1482 return true;
1483 }
1484
1486 return false;
1487}
1488
1489//static
1491{
1492 Q_CHECK_FILE_NAME(source, false);
1493 Q_CHECK_FILE_NAME(target, false);
1494
1495 if (::rename(source.nativeFilePath().constData(), target.nativeFilePath().constData()) == 0)
1496 return true;
1498 return false;
1499}
1500
1501//static
1503{
1504 Q_CHECK_FILE_NAME(entry, false);
1505 if (unlink(entry.nativeFilePath().constData()) == 0)
1506 return true;
1508 return false;
1509
1510}
1511
1512//static
1514{
1515 Q_CHECK_FILE_NAME(entry, false);
1516
1517 mode_t mode = QtPrivate::toMode_t(permissions);
1518 bool success = ::chmod(entry.nativeFilePath().constData(), mode) == 0;
1519 if (success && data) {
1520 data->entryFlags &= ~QFileSystemMetaData::Permissions;
1521 data->entryFlags |= QFileSystemMetaData::MetaDataFlag(uint(permissions.toInt()));
1522 data->knownFlagsMask |= QFileSystemMetaData::Permissions;
1523 }
1524 if (!success)
1526 return success;
1527}
1528
1529//static
1530bool QFileSystemEngine::setPermissions(int fd, QFile::Permissions permissions, QSystemError &error, QFileSystemMetaData *data)
1531{
1532 mode_t mode = QtPrivate::toMode_t(permissions);
1533
1534 bool success = ::fchmod(fd, mode) == 0;
1535 if (success && data) {
1536 data->entryFlags &= ~QFileSystemMetaData::Permissions;
1537 data->entryFlags |= QFileSystemMetaData::MetaDataFlag(uint(permissions.toInt()));
1538 data->knownFlagsMask |= QFileSystemMetaData::Permissions;
1539 }
1540 if (!success)
1542 return success;
1543}
1544
1545//static
1547{
1548 if (!newDate.isValid() || time == QAbstractFileEngine::BirthTime ||
1551 return false;
1552 }
1553
1554#if QT_CONFIG(futimens)
1555 // UTIME_OMIT: leave file timestamp unchanged
1556 struct timespec ts[2] = {{0, UTIME_OMIT}, {0, UTIME_OMIT}};
1557
1559 const int idx = time == QAbstractFileEngine::AccessTime ? 0 : 1;
1560 const std::chrono::milliseconds msecs{newDate.toMSecsSinceEpoch()};
1561 ts[idx] = durationToTimespec(msecs);
1562 }
1563
1564 if (futimens(fd, ts) == -1) {
1566 return false;
1567 }
1568
1569 return true;
1570#else
1571 Q_UNUSED(fd);
1573 return false;
1574#endif
1575}
1576
1578{
1579 QString home = QFile::decodeName(qgetenv("HOME"));
1580 if (home.isEmpty())
1581 home = rootPath();
1582 return QDir::cleanPath(home);
1583}
1584
1586{
1587 return u"/"_s;
1588}
1589
1591{
1592#ifdef QT_UNIX_TEMP_PATH_OVERRIDE
1593 return QT_UNIX_TEMP_PATH_OVERRIDE ""_L1;
1594#else
1595 QString temp = QFile::decodeName(qgetenv("TMPDIR"));
1596 if (temp.isEmpty()) {
1597 if (false) {
1598#if defined(Q_OS_DARWIN) && !defined(QT_BOOTSTRAPPED)
1599 } else if (NSString *nsPath = NSTemporaryDirectory()) {
1600 temp = QString::fromCFString((CFStringRef)nsPath);
1601#endif
1602 } else {
1603 temp = _PATH_TMP ""_L1;
1604 }
1605 }
1606 return QDir(QDir::cleanPath(temp)).canonicalPath();
1607#endif
1608}
1609
1611{
1612 int r;
1613 r = QT_CHDIR(path.nativeFilePath().constData());
1614 return r >= 0;
1615}
1616
1618{
1620#if defined(__GLIBC__) && !defined(PATH_MAX)
1621 char *currentName = ::get_current_dir_name();
1622 if (currentName) {
1624 ::free(currentName);
1625 }
1626#else
1627 char currentName[PATH_MAX+1];
1628 if (::getcwd(currentName, PATH_MAX)) {
1629#if defined(Q_OS_VXWORKS) && defined(VXWORKS_VXSIM)
1630 QByteArray dir(currentName);
1631 if (dir.indexOf(':') < dir.indexOf('/'))
1632 dir.remove(0, dir.indexOf(':')+1);
1633
1634 qstrncpy(currentName, dir.constData(), PATH_MAX);
1635#endif
1637 }
1638# if defined(QT_DEBUG)
1639 if (result.isEmpty())
1640 qWarning("QFileSystemEngine::currentPath: getcwd() failed");
1641# endif
1642#endif
1643 return result;
1644}
FileTime
These are used by the fileTime() function.
\inmodule QtCore
Definition qbytearray.h:57
char * data()
\macro QT_NO_CAST_FROM_BYTEARRAY
Definition qbytearray.h:534
qsizetype size() const noexcept
Returns the number of bytes in this byte array.
Definition qbytearray.h:474
const char * constData() const noexcept
Returns a pointer to the const data stored in the byte array.
Definition qbytearray.h:122
bool startsWith(QByteArrayView bv) const
Definition qbytearray.h:170
bool isEmpty() const noexcept
Returns true if the byte array has size 0; otherwise returns false.
Definition qbytearray.h:106
static QByteArray number(int, int base=10)
Returns a byte-array representing the whole number n as text.
QByteArray left(qsizetype len) const
Returns a byte array that contains the first len bytes of this byte array.
qsizetype lastIndexOf(char c, qsizetype from=-1) const
This is an overloaded member function, provided for convenience. It differs from the above function o...
QByteArray & append(char c)
This is an overloaded member function, provided for convenience. It differs from the above function o...
\inmodule QtCore\reentrant
Definition qdatetime.h:257
static QDateTime currentDateTime()
This is an overloaded member function, provided for convenience. It differs from the above function o...
qint64 toMSecsSinceEpoch() const
bool isValid() const
Returns true if this datetime represents a definite moment, otherwise false.
\inmodule QtCore
Definition qdir.h:19
bool mkdir(const QString &dirName) const
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qdir.cpp:1527
QString dirName() const
Returns the name of the directory; this is not the same as the path, e.g.
Definition qdir.cpp:715
QString path() const
Returns the path.
Definition qdir.cpp:653
bool cd(const QString &dirName)
Changes the QDir's directory to dirName.
Definition qdir.cpp:984
QString canonicalPath() const
Returns the canonical path, i.e.
Definition qdir.cpp:692
static QChar separator()
Returns the native directory separator: "/" under Unix and "\\" under Windows.
Definition qdir.h:206
QString filePath(const QString &fileName) const
Returns the path name of a file in the directory.
Definition qdir.cpp:778
static QDir home()
Returns the user's home directory.
Definition qdir.h:219
static QString cleanPath(const QString &path)
Returns path with directory separators normalized (that is, platform-native separators converted to "...
Definition qdir.cpp:2395
void close() override
Calls QFileDevice::flush() and closes the file.
\inmodule QtCore \reentrant
Definition qfileinfo.h:22
bool isSymLink() const
Returns true if this object points to a symbolic link, shortcut, or alias; otherwise returns false.
QString symLinkTarget() const
QString suffix() const
Returns the suffix (extension) of the file.
QString absoluteFilePath() const
Returns an absolute path including the file name.
bool isDir() const
Returns true if this object points to a directory or to a symbolic link to a directory.
static bool setFileTime(const QFileSystemEntry &entry, const QDateTime &newDate, QAbstractFileEngine::FileTime whatTime, QSystemError &error)
static QFileSystemEntry getLinkTarget(const QFileSystemEntry &link, QFileSystemMetaData &data)
static QFileSystemEntry canonicalName(const QFileSystemEntry &entry, QFileSystemMetaData &data)
static QByteArray id(const QFileSystemEntry &entry)
static bool setCurrentPath(const QFileSystemEntry &entry)
static bool moveFileToTrash(const QFileSystemEntry &source, QFileSystemEntry &newLocation, QSystemError &error)
static QFileSystemEntry getRawLinkPath(const QFileSystemEntry &link, QFileSystemMetaData &data)
static bool copyFile(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error)
static bool renameOverwriteFile(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error)
static bool fillMetaData(const QFileSystemEntry &entry, QFileSystemMetaData &data, QFileSystemMetaData::MetaDataFlags what)
static bool createDirectory(const QFileSystemEntry &entry, bool createParents, std::optional< QFile::Permissions > permissions=std::nullopt)
static QString bundleName(const QFileSystemEntry &)
static bool createLink(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error)
static bool setPermissions(const QFileSystemEntry &entry, QFile::Permissions permissions, QSystemError &error, QFileSystemMetaData *data=nullptr)
static bool renameFile(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error)
static QFileSystemEntry absoluteName(const QFileSystemEntry &entry)
static QString resolveUserName(const QFileSystemEntry &entry, QFileSystemMetaData &data)
static QString resolveGroupName(const QFileSystemEntry &entry, QFileSystemMetaData &data)
static bool removeFile(const QFileSystemEntry &entry, QSystemError &error)
static bool removeDirectory(const QFileSystemEntry &entry, bool removeEmptyParents)
static QFileSystemEntry currentPath()
Q_AUTOTEST_EXPORT NativePath nativeFilePath() const
Q_AUTOTEST_EXPORT QString filePath() const
bool hasFlags(MetaDataFlags flags) const
void clearFlags(MetaDataFlags flags=AllMetaDataFlags)
\inmodule QtCore
Definition qfile.h:93
bool open(OpenMode flags) override
Opens the file using OpenMode mode, returning true if successful; otherwise false.
Definition qfile.cpp:881
bool setPermissions(Permissions permissionSpec) override
Sets the permissions for the file to the permissions specified.
Definition qfile.cpp:1136
bool remove()
Removes the file specified by fileName().
Definition qfile.cpp:419
static QByteArray encodeName(const QString &fileName)
Converts fileName to an 8-bit encoding that you can use in native APIs.
Definition qfile.h:158
void setFileName(const QString &name)
Sets the name of the file.
Definition qfile.cpp:302
static QString decodeName(const QByteArray &localFileName)
This does the reverse of QFile::encodeName() using localFileName.
Definition qfile.h:162
bool exists() const
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qfile.cpp:351
bool isOpen() const
Returns true if the device is open; otherwise returns false.
qint64 write(const char *data, qint64 len)
Writes at most maxSize bytes of data from data to the device.
static QString writableLocation(StandardLocation type)
\inmodule QtCore
QString rootPath() const
Returns the mount point of the filesystem this QStorageInfo object represents.
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
QByteArray toLatin1() const &
Definition qstring.h:559
qsizetype lastIndexOf(QChar c, Qt::CaseSensitivity cs=Qt::CaseSensitive) const noexcept
Definition qstring.h:279
void chop(qsizetype n)
Removes n characters from the end of the string.
Definition qstring.cpp:6180
const QChar * constData() const
Returns a pointer to the data stored in the QString.
Definition qstring.h:1101
qsizetype size() const
Returns the number of characters in this string.
Definition qstring.h:182
QString mid(qsizetype position, qsizetype n=-1) const
Returns a string that contains n characters of this string, starting at the specified position index.
Definition qstring.cpp:5204
QChar front() const
Definition qstring.h:214
bool endsWith(const QString &s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Returns true if the string ends with s; otherwise returns false.
Definition qstring.cpp:5350
bool isEmpty() const
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:1083
QByteArray toLocal8Bit() const &
Definition qstring.h:567
static QString number(int, int base=10)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:7822
QString & append(QChar c)
Definition qstring.cpp:3227
QString left(qsizetype n) const
Returns a substring that contains the n leftmost characters of the string.
Definition qstring.cpp:5161
static QString static QString qsizetype indexOf(QChar c, qsizetype from=0, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Definition qstring.cpp:4420
QString & remove(qsizetype i, qsizetype len)
Removes n characters from the string, starting at the given position index, and returns a reference t...
Definition qstring.cpp:3435
static QString static QString asprintf(const char *format,...) Q_ATTRIBUTE_FORMAT_PRINTF(1
Definition qstring.cpp:7005
qsizetype length() const
Returns the number of characters in this string.
Definition qstring.h:187
static QByteArray toPercentEncoding(const QString &, const QByteArray &exclude=QByteArray(), const QByteArray &include=QByteArray())
Returns an encoded copy of input.
Definition qurl.cpp:3016
void qErrnoWarning(const char *msg,...)
Combined button and popup list for selecting options.
Q_CORE_EXPORT char * qstrncpy(char *dst, const char *src, size_t len)
QT_BEGIN_NAMESPACE QByteArray qt_readlink(const char *path)
timespec durationToTimespec(std::chrono::nanoseconds timeout) noexcept
#define Q_FORWARD_DECLARE_OBJC_CLASS(classname)
DBusConnection const char DBusError * error
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
#define Q_CHECK_FILE_NAME(name, result)
static int qt_lstatx(const char *, struct statx *)
#define _PATH_TMP
static int qt_fstatx(int, struct statx *)
@ SupportsHardlinking
static bool createDirectoryWithParents(const QByteArray &nativeName, mode_t mode, bool shouldMkdirFirst=true)
static QString freeDesktopTrashLocation(const QString &sourcePath)
static int qt_statx(const char *, struct statx *)
#define PATH_MAX
#define qCritical
Definition qlogging.h:163
#define qWarning
Definition qlogging.h:162
return ret
static ControlElement< T > * ptr(QWidget *widget)
GLenum mode
GLuint64 key
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLboolean r
[2]
GLuint srcPath
GLenum type
GLboolean GLuint group
GLenum GLuint GLenum GLsizei const GLchar * buf
GLenum target
GLbitfield flags
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLuint64 GLenum GLint fd
GLuint name
GLint GLint GLint GLint GLint GLint GLint GLbitfield mask
GLfloat n
GLuint counter
GLsizei GLsizei GLchar * source
GLuint entry
GLdouble GLdouble t
Definition qopenglext.h:243
GLsizei const GLchar *const * path
GLuint64EXT * result
[6]
GLdouble s
[6]
Definition qopenglext.h:235
static QUrl resolvedUrl(const QUrl &url, const QQmlRefPointer< QQmlContextData > &context)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
static QString canonicalPath(const QString &rootPath)
#define qUtf16Printable(string)
Definition qstring.h:1403
Q_CORE_EXPORT QByteArray qgetenv(const char *varName)
#define Q_UNUSED(x)
unsigned long ulong
Definition qtypes.h:30
unsigned long long quint64
Definition qtypes.h:56
ptrdiff_t qsizetype
Definition qtypes.h:70
unsigned int uint
Definition qtypes.h:29
long long qint64
Definition qtypes.h:55
#define Q_INT64_C(c)
Definition qtypes.h:52
QString bundle
QFileInfo info(fileName)
[8]
QUrl url("example.com")
[constructor-url-reference]
QString dir
[11]
QItemEditorCreatorBase * creator
IUIAutomationTreeWalker __RPC__deref_out_opt IUIAutomationElement ** parent