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