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