Merge branch 'master' of scm.dev.nokia.troll.no:qt/oslo-staging-1 into master-integration
[qt-netbsd.git] / src / corelib / io / qfsfileengine_unix.cpp
blob722d6d3a607233f6b8842ed24321e90df0abd6d6
1 /****************************************************************************
2 **
3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the QtCore module of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** No Commercial Usage
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
14 ** this package.
16 ** GNU Lesser General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU Lesser
18 ** General Public License version 2.1 as published by the Free Software
19 ** Foundation and appearing in the file LICENSE.LGPL included in the
20 ** packaging of this file. Please review the following information to
21 ** ensure the GNU Lesser General Public License version 2.1 requirements
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 ** In addition, as a special exception, Nokia gives you certain additional
25 ** rights. These rights are described in the Nokia Qt LGPL Exception
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
38 ** $QT_END_LICENSE$
40 ****************************************************************************/
42 #include "qplatformdefs.h"
43 #include "qabstractfileengine.h"
44 #include "private/qfsfileengine_p.h"
45 #include "private/qcore_unix_p.h"
47 #ifndef QT_NO_FSFILEENGINE
49 #include "qfile.h"
50 #include "qdir.h"
51 #include "qdatetime.h"
52 #include "qvarlengtharray.h"
54 #include <sys/mman.h>
55 #include <stdlib.h>
56 #include <limits.h>
57 #if defined(Q_OS_SYMBIAN)
58 # include <syslimits.h>
59 # include <f32file.h>
60 # include <pathinfo.h>
61 # include "private/qcore_symbian_p.h"
62 #endif
63 #include <errno.h>
64 #if !defined(QWS) && defined(Q_OS_MAC)
65 # include <private/qcore_mac_p.h>
66 #endif
68 QT_BEGIN_NAMESPACE
71 #if defined(Q_OS_SYMBIAN)
72 /*!
73 \internal
75 Returns true if supplied path is a relative path
77 static bool isRelativePathSymbian(const QString& fileName)
79 return !(fileName.startsWith(QLatin1Char('/'))
80 || (fileName.length() >= 2
81 && ((fileName.at(0).isLetter() && fileName.at(1) == QLatin1Char(':'))
82 || (fileName.at(0) == QLatin1Char('/') && fileName.at(1) == QLatin1Char('/')))));
84 #endif
86 /*!
87 \internal
89 Returns the stdlib open string corresponding to a QIODevice::OpenMode.
91 static inline QByteArray openModeToFopenMode(QIODevice::OpenMode flags, const QString &fileName)
93 QByteArray mode;
94 if ((flags & QIODevice::ReadOnly) && !(flags & QIODevice::Truncate)) {
95 mode = "rb";
96 if (flags & QIODevice::WriteOnly) {
97 QT_STATBUF statBuf;
98 if (!fileName.isEmpty()
99 && QT_STAT(QFile::encodeName(fileName), &statBuf) == 0
100 && (statBuf.st_mode & S_IFMT) == S_IFREG) {
101 mode += "+";
102 } else {
103 mode = "wb+";
106 } else if (flags & QIODevice::WriteOnly) {
107 mode = "wb";
108 if (flags & QIODevice::ReadOnly)
109 mode += '+';
111 if (flags & QIODevice::Append) {
112 mode = "ab";
113 if (flags & QIODevice::ReadOnly)
114 mode += '+';
117 #if defined(__GLIBC__) && (__GLIBC__ * 0x100 + __GLIBC_MINOR__) >= 0x0207
118 // must be glibc >= 2.7
119 mode += 'e';
120 #endif
122 return mode;
126 \internal
128 Returns the stdio open flags corresponding to a QIODevice::OpenMode.
130 static inline int openModeToOpenFlags(QIODevice::OpenMode mode)
132 int oflags = QT_OPEN_RDONLY;
133 #ifdef QT_LARGEFILE_SUPPORT
134 oflags |= QT_OPEN_LARGEFILE;
135 #endif
137 if ((mode & QFile::ReadWrite) == QFile::ReadWrite) {
138 oflags = QT_OPEN_RDWR | QT_OPEN_CREAT;
139 } else if (mode & QFile::WriteOnly) {
140 oflags = QT_OPEN_WRONLY | QT_OPEN_CREAT;
143 if (mode & QFile::Append) {
144 oflags |= QT_OPEN_APPEND;
145 } else if (mode & QFile::WriteOnly) {
146 if ((mode & QFile::Truncate) || !(mode & QFile::ReadOnly))
147 oflags |= QT_OPEN_TRUNC;
150 return oflags;
154 \internal
156 Sets the file descriptor to close on exec. That is, the file
157 descriptor is not inherited by child processes.
159 static inline bool setCloseOnExec(int fd)
161 return fd != -1 && fcntl(fd, F_SETFD, FD_CLOEXEC) != -1;
165 \internal
167 void QFSFileEnginePrivate::nativeInitFileName()
169 nativeFilePath = QFile::encodeName(filePath);
173 \internal
175 bool QFSFileEnginePrivate::nativeOpen(QIODevice::OpenMode openMode)
177 Q_Q(QFSFileEngine);
179 if (openMode & QIODevice::Unbuffered) {
180 int flags = openModeToOpenFlags(openMode);
182 // Try to open the file in unbuffered mode.
183 do {
184 fd = QT_OPEN(nativeFilePath.constData(), flags, 0666);
185 } while (fd == -1 && errno == EINTR);
187 // On failure, return and report the error.
188 if (fd == -1) {
189 q->setError(errno == EMFILE ? QFile::ResourceError : QFile::OpenError,
190 qt_error_string(errno));
191 return false;
194 if (!(openMode & QIODevice::WriteOnly)) {
195 // we don't need this check if we tried to open for writing because then
196 // we had received EISDIR anyway.
197 QT_STATBUF statBuf;
198 if (QT_FSTAT(fd, &statBuf) != -1) {
199 if ((statBuf.st_mode & S_IFMT) == S_IFDIR) {
200 q->setError(QFile::OpenError, QLatin1String("file to open is a directory"));
201 QT_CLOSE(fd);
202 return false;
207 // Seek to the end when in Append mode.
208 if (flags & QFile::Append) {
209 int ret;
210 do {
211 ret = QT_LSEEK(fd, 0, SEEK_END);
212 } while (ret == -1 && errno == EINTR);
214 if (ret == -1) {
215 q->setError(errno == EMFILE ? QFile::ResourceError : QFile::OpenError,
216 qt_error_string(int(errno)));
217 return false;
221 fh = 0;
222 } else {
223 QByteArray fopenMode = openModeToFopenMode(openMode, filePath);
225 // Try to open the file in buffered mode.
226 do {
227 fh = QT_FOPEN(nativeFilePath.constData(), fopenMode.constData());
228 } while (!fh && errno == EINTR);
230 // On failure, return and report the error.
231 if (!fh) {
232 q->setError(errno == EMFILE ? QFile::ResourceError : QFile::OpenError,
233 qt_error_string(int(errno)));
234 return false;
237 if (!(openMode & QIODevice::WriteOnly)) {
238 // we don't need this check if we tried to open for writing because then
239 // we had received EISDIR anyway.
240 QT_STATBUF statBuf;
241 if (QT_FSTAT(fileno(fh), &statBuf) != -1) {
242 if ((statBuf.st_mode & S_IFMT) == S_IFDIR) {
243 q->setError(QFile::OpenError, QLatin1String("file to open is a directory"));
244 fclose(fh);
245 return false;
250 setCloseOnExec(fileno(fh)); // ignore failure
252 // Seek to the end when in Append mode.
253 if (openMode & QIODevice::Append) {
254 int ret;
255 do {
256 ret = QT_FSEEK(fh, 0, SEEK_END);
257 } while (ret == -1 && errno == EINTR);
259 if (ret == -1) {
260 q->setError(errno == EMFILE ? QFile::ResourceError : QFile::OpenError,
261 qt_error_string(int(errno)));
262 return false;
266 fd = -1;
269 closeFileHandle = true;
270 return true;
274 \internal
276 bool QFSFileEnginePrivate::nativeClose()
278 return closeFdFh();
282 \internal
285 bool QFSFileEnginePrivate::nativeFlush()
287 return fh ? flushFh() : fd != -1;
291 \internal
293 qint64 QFSFileEnginePrivate::nativeRead(char *data, qint64 len)
295 Q_Q(QFSFileEngine);
297 if (fh && nativeIsSequential()) {
298 size_t readBytes = 0;
299 int oldFlags = fcntl(QT_FILENO(fh), F_GETFL);
300 for (int i = 0; i < 2; ++i) {
301 // Unix: Make the underlying file descriptor non-blocking
302 if ((oldFlags & O_NONBLOCK) == 0)
303 fcntl(QT_FILENO(fh), F_SETFL, oldFlags | O_NONBLOCK);
305 // Cross platform stdlib read
306 size_t read = 0;
307 do {
308 read = fread(data + readBytes, 1, size_t(len - readBytes), fh);
309 } while (read == 0 && !feof(fh) && errno == EINTR);
310 if (read > 0) {
311 readBytes += read;
312 break;
313 } else {
314 if (readBytes)
315 break;
316 readBytes = read;
319 // Unix: Restore the blocking state of the underlying socket
320 if ((oldFlags & O_NONBLOCK) == 0) {
321 fcntl(QT_FILENO(fh), F_SETFL, oldFlags);
322 if (readBytes == 0) {
323 int readByte = 0;
324 do {
325 readByte = fgetc(fh);
326 } while (readByte == -1 && errno == EINTR);
327 if (readByte != -1) {
328 *data = uchar(readByte);
329 readBytes += 1;
330 } else {
331 break;
336 // Unix: Restore the blocking state of the underlying socket
337 if ((oldFlags & O_NONBLOCK) == 0) {
338 fcntl(QT_FILENO(fh), F_SETFL, oldFlags);
340 if (readBytes == 0 && !feof(fh)) {
341 // if we didn't read anything and we're not at EOF, it must be an error
342 q->setError(QFile::ReadError, qt_error_string(int(errno)));
343 return -1;
345 return readBytes;
348 return readFdFh(data, len);
352 \internal
354 qint64 QFSFileEnginePrivate::nativeReadLine(char *data, qint64 maxlen)
356 return readLineFdFh(data, maxlen);
360 \internal
362 qint64 QFSFileEnginePrivate::nativeWrite(const char *data, qint64 len)
364 return writeFdFh(data, len);
368 \internal
370 qint64 QFSFileEnginePrivate::nativePos() const
372 return posFdFh();
376 \internal
378 bool QFSFileEnginePrivate::nativeSeek(qint64 pos)
380 return seekFdFh(pos);
384 \internal
386 int QFSFileEnginePrivate::nativeHandle() const
388 return fh ? fileno(fh) : fd;
392 \internal
394 bool QFSFileEnginePrivate::nativeIsSequential() const
396 return isSequentialFdFh();
399 bool QFSFileEngine::remove()
401 Q_D(QFSFileEngine);
402 bool ret = unlink(d->nativeFilePath.constData()) == 0;
403 if (!ret)
404 setError(QFile::RemoveError, qt_error_string(errno));
405 return ret;
408 bool QFSFileEngine::copy(const QString &newName)
410 #if defined(Q_OS_SYMBIAN)
411 Q_D(QFSFileEngine);
412 RFs rfs = qt_s60GetRFs();
413 CFileMan* fm = NULL;
414 QString oldNative(QDir::toNativeSeparators(d->filePath));
415 TPtrC oldPtr(qt_QString2TPtrC(oldNative));
416 QFileInfo fi(newName);
417 QString absoluteNewName = fi.absoluteFilePath();
418 QString newNative(QDir::toNativeSeparators(absoluteNewName));
419 TPtrC newPtr(qt_QString2TPtrC(newNative));
420 TRAPD (err,
421 fm = CFileMan::NewL(rfs);
422 RFile rfile;
423 err = rfile.Open(rfs, oldPtr, EFileShareReadersOrWriters);
424 if (err == KErrNone) {
425 err = fm->Copy(rfile, newPtr);
426 rfile.Close();
428 ) // End TRAP
429 delete fm;
430 // ### Add error reporting on failure
431 return (err == KErrNone);
432 #else
433 Q_UNUSED(newName);
434 // ### Add copy code for Unix here
435 setError(QFile::UnspecifiedError, QLatin1String("Not implemented!"));
436 return false;
437 #endif
440 bool QFSFileEngine::rename(const QString &newName)
442 Q_D(QFSFileEngine);
443 bool ret = ::rename(d->nativeFilePath.constData(), QFile::encodeName(newName).constData()) == 0;
444 if (!ret)
445 setError(QFile::RenameError, qt_error_string(errno));
446 return ret;
449 bool QFSFileEngine::link(const QString &newName)
451 Q_D(QFSFileEngine);
452 bool ret = ::symlink(d->nativeFilePath.constData(), QFile::encodeName(newName).constData()) == 0;
453 if (!ret)
454 setError(QFile::RenameError, qt_error_string(errno));
455 return ret;
458 qint64 QFSFileEnginePrivate::nativeSize() const
460 return sizeFdFh();
463 bool QFSFileEngine::mkdir(const QString &name, bool createParentDirectories) const
465 QString dirName = name;
466 if (createParentDirectories) {
467 dirName = QDir::cleanPath(dirName);
468 #if defined(Q_OS_SYMBIAN)
469 dirName = QDir::toNativeSeparators(dirName);
470 #endif
471 for(int oldslash = -1, slash=0; slash != -1; oldslash = slash) {
472 slash = dirName.indexOf(QDir::separator(), oldslash+1);
473 if (slash == -1) {
474 if (oldslash == dirName.length())
475 break;
476 slash = dirName.length();
478 if (slash) {
479 QByteArray chunk = QFile::encodeName(dirName.left(slash));
480 QT_STATBUF st;
481 if (QT_STAT(chunk, &st) != -1) {
482 if ((st.st_mode & S_IFMT) != S_IFDIR)
483 return false;
484 } else if (QT_MKDIR(chunk, 0777) != 0) {
485 return false;
489 return true;
491 #if defined(Q_OS_DARWIN) // Mac X doesn't support trailing /'s
492 if (dirName.endsWith(QLatin1Char('/')))
493 dirName.chop(1);
494 #endif
495 return (QT_MKDIR(QFile::encodeName(dirName), 0777) == 0);
498 bool QFSFileEngine::rmdir(const QString &name, bool recurseParentDirectories) const
500 QString dirName = name;
501 if (recurseParentDirectories) {
502 dirName = QDir::cleanPath(dirName);
503 #if defined(Q_OS_SYMBIAN)
504 dirName = QDir::toNativeSeparators(dirName);
505 #endif
506 for(int oldslash = 0, slash=dirName.length(); slash > 0; oldslash = slash) {
507 QByteArray chunk = QFile::encodeName(dirName.left(slash));
508 QT_STATBUF st;
509 if (QT_STAT(chunk, &st) != -1) {
510 if ((st.st_mode & S_IFMT) != S_IFDIR)
511 return false;
512 if (::rmdir(chunk) != 0)
513 return oldslash != 0;
514 } else {
515 return false;
517 slash = dirName.lastIndexOf(QDir::separator(), oldslash-1);
519 return true;
521 return ::rmdir(QFile::encodeName(dirName)) == 0;
524 bool QFSFileEngine::caseSensitive() const
526 #if defined(Q_OS_SYMBIAN)
527 return false;
528 #else
529 return true;
530 #endif
533 bool QFSFileEngine::setCurrentPath(const QString &path)
535 int r;
536 r = QT_CHDIR(QFile::encodeName(path));
537 return r >= 0;
540 QString QFSFileEngine::currentPath(const QString &)
542 QString result;
543 QT_STATBUF st;
544 #if defined(Q_OS_SYMBIAN)
545 char nativeCurrentName[PATH_MAX+1];
546 if (::getcwd(nativeCurrentName, PATH_MAX))
547 result = QDir::fromNativeSeparators(QFile::decodeName(QByteArray(nativeCurrentName)));
548 if (result.isEmpty()) {
549 # if defined(QT_DEBUG)
550 qWarning("QFSFileEngine::currentPath: getcwd() failed");
551 # endif
552 } else
553 #endif
554 if (QT_STAT(".", &st) == 0) {
555 #if defined(__GLIBC__) && !defined(PATH_MAX)
556 char *currentName = ::get_current_dir_name();
557 if (currentName) {
558 result = QFile::decodeName(QByteArray(currentName));
559 ::free(currentName);
561 #elif !defined(Q_OS_SYMBIAN)
562 char currentName[PATH_MAX+1];
563 if (::getcwd(currentName, PATH_MAX))
564 result = QFile::decodeName(QByteArray(currentName));
565 # if defined(QT_DEBUG)
566 if (result.isNull())
567 qWarning("QFSFileEngine::currentPath: getcwd() failed");
568 # endif
569 #endif
570 } else {
571 #if defined(Q_OS_SYMBIAN)
572 // If current dir returned by Open C doesn't exist,
573 // try to create it (can happen with application private dirs)
574 // Ignore mkdir failures; we want to be consistent with Open C
575 // current path regardless.
576 QT_MKDIR(QFile::encodeName(QLatin1String(nativeCurrentName)), 0777);
577 #else
578 # if defined(QT_DEBUG)
579 qWarning("QFSFileEngine::currentPath: stat(\".\") failed");
580 # endif
581 #endif
583 return result;
586 QString QFSFileEngine::homePath()
588 #if defined(Q_OS_SYMBIAN)
589 QString home = rootPath();
590 #else
591 QString home = QFile::decodeName(qgetenv("HOME"));
592 if (home.isNull())
593 home = rootPath();
594 #endif
595 return home;
598 QString QFSFileEngine::rootPath()
600 #if defined(Q_OS_SYMBIAN)
601 # ifdef Q_WS_S60
602 TFileName symbianPath = PathInfo::PhoneMemoryRootPath();
603 return QDir::cleanPath(QDir::fromNativeSeparators(qt_TDesC2QString(symbianPath)));
604 # else
605 # warning No fallback implementation of QFSFileEngine::rootPath()
606 return QString();
607 # endif
608 #else
609 return QLatin1String("/");
610 #endif
613 QString QFSFileEngine::tempPath()
615 #if defined(Q_OS_SYMBIAN)
616 # ifdef Q_WS_S60
617 TFileName symbianPath = PathInfo::PhoneMemoryRootPath();
618 QString temp = QDir::fromNativeSeparators(qt_TDesC2QString(symbianPath));
619 temp += QLatin1String( "temp/");
621 // Just to verify that folder really exist on hardware
622 QT_MKDIR(QFile::encodeName(temp), 0777);
623 # else
624 # warning No fallback implementation of QFSFileEngine::tempPath()
625 QString temp;
626 # endif
627 #else
628 QString temp = QFile::decodeName(qgetenv("TMPDIR"));
629 if (temp.isEmpty())
630 temp = QLatin1String("/tmp/");
631 #endif
632 return temp;
635 QFileInfoList QFSFileEngine::drives()
637 QFileInfoList ret;
638 #if defined(Q_OS_SYMBIAN)
639 TDriveList driveList;
640 RFs rfs = qt_s60GetRFs();
641 TInt err = rfs.DriveList(driveList);
642 if (err == KErrNone) {
643 char driveName[] = "A:/";
645 for (char i = 0; i < KMaxDrives; i++) {
646 if (driveList[i]) {
647 driveName[0] = 'A' + i;
648 ret.append(QFileInfo(QLatin1String(driveName)));
651 } else {
652 qWarning("QFSFileEngine::drives: Getting drives failed");
654 #else
655 ret.append(QFileInfo(rootPath()));
656 #endif
657 return ret;
660 bool QFSFileEnginePrivate::doStat() const
662 if (!tried_stat) {
663 tried_stat = true;
664 could_stat = false;
666 if (fh && nativeFilePath.isEmpty()) {
667 // ### actually covers two cases: d->fh and when the file is not open
668 could_stat = (QT_FSTAT(QT_FILENO(fh), &st) == 0);
669 } else if (fd == -1) {
670 // ### actually covers two cases: d->fh and when the file is not open
671 could_stat = (QT_STAT(nativeFilePath.constData(), &st) == 0);
672 } else {
673 could_stat = (QT_FSTAT(fd, &st) == 0);
676 return could_stat;
679 bool QFSFileEnginePrivate::isSymlink() const
681 if (need_lstat) {
682 need_lstat = false;
684 QT_STATBUF st; // don't clobber our main one
685 is_link = (QT_LSTAT(nativeFilePath.constData(), &st) == 0) ? S_ISLNK(st.st_mode) : false;
687 return is_link;
690 #if defined(Q_OS_SYMBIAN)
691 static bool _q_isSymbianHidden(const QString &path, bool isDir)
693 RFs rfs = qt_s60GetRFs();
694 QFileInfo fi(path);
695 QString absPath = fi.absoluteFilePath();
696 if (isDir && !absPath.endsWith(QLatin1Char('/')))
697 absPath.append(QLatin1Char('/'));
698 QString native(QDir::toNativeSeparators(absPath));
699 TPtrC ptr(qt_QString2TPtrC(native));
700 TUint attributes;
701 TInt err = rfs.Att(ptr, attributes);
702 return (err == KErrNone && (attributes & KEntryAttHidden));
704 #endif
706 #if !defined(QWS) && defined(Q_OS_MAC)
707 static bool _q_isMacHidden(const QString &path)
709 OSErr err = noErr;
711 FSRef fsRef;
713 err = FSPathMakeRefWithOptions(reinterpret_cast<const UInt8 *>(QFile::encodeName(QDir::cleanPath(path)).constData()),
714 kFSPathMakeRefDoNotFollowLeafSymlink, &fsRef, 0);
715 if (err != noErr)
716 return false;
718 FSCatalogInfo catInfo;
719 err = FSGetCatalogInfo(&fsRef, kFSCatInfoFinderInfo, &catInfo, NULL, NULL, NULL);
720 if (err != noErr)
721 return false;
723 FileInfo * const fileInfo = reinterpret_cast<FileInfo*>(&catInfo.finderInfo);
724 bool result = (fileInfo->finderFlags & kIsInvisible);
725 return result;
727 #endif
730 \reimp
732 QAbstractFileEngine::FileFlags QFSFileEngine::fileFlags(FileFlags type) const
734 Q_D(const QFSFileEngine);
735 // Force a stat, so that we're guaranteed to get up-to-date results
736 if (type & Refresh) {
737 d->tried_stat = 0;
738 d->need_lstat = 1;
741 QAbstractFileEngine::FileFlags ret = 0;
742 if (type & FlagsMask)
743 ret |= LocalDiskFlag;
744 bool exists = d->doStat();
745 if (!exists && !d->isSymlink())
746 return ret;
748 if (exists && (type & PermsMask)) {
749 if (d->st.st_mode & S_IRUSR)
750 ret |= ReadOwnerPerm;
751 if (d->st.st_mode & S_IWUSR)
752 ret |= WriteOwnerPerm;
753 if (d->st.st_mode & S_IXUSR)
754 ret |= ExeOwnerPerm;
755 if (d->st.st_mode & S_IRUSR)
756 ret |= ReadUserPerm;
757 if (d->st.st_mode & S_IWUSR)
758 ret |= WriteUserPerm;
759 if (d->st.st_mode & S_IXUSR)
760 ret |= ExeUserPerm;
761 if (d->st.st_mode & S_IRGRP)
762 ret |= ReadGroupPerm;
763 if (d->st.st_mode & S_IWGRP)
764 ret |= WriteGroupPerm;
765 if (d->st.st_mode & S_IXGRP)
766 ret |= ExeGroupPerm;
767 if (d->st.st_mode & S_IROTH)
768 ret |= ReadOtherPerm;
769 if (d->st.st_mode & S_IWOTH)
770 ret |= WriteOtherPerm;
771 if (d->st.st_mode & S_IXOTH)
772 ret |= ExeOtherPerm;
774 if (type & TypesMask) {
775 #if !defined(QWS) && defined(Q_OS_MAC)
776 bool foundAlias = false;
778 FSRef fref;
779 if (FSPathMakeRef((const UInt8 *)QFile::encodeName(QDir::cleanPath(d->filePath)).data(),
780 &fref, NULL) == noErr) {
781 Boolean isAlias, isFolder;
782 if (FSIsAliasFile(&fref, &isAlias, &isFolder) == noErr && isAlias) {
783 foundAlias = true;
784 ret |= LinkType;
788 if (!foundAlias)
789 #endif
791 if ((type & LinkType) && d->isSymlink())
792 ret |= LinkType;
793 if (exists && (d->st.st_mode & S_IFMT) == S_IFREG)
794 ret |= FileType;
795 else if (exists && (d->st.st_mode & S_IFMT) == S_IFDIR)
796 ret |= DirectoryType;
797 #if !defined(QWS) && defined(Q_OS_MAC)
798 if ((ret & DirectoryType) && (type & BundleType)) {
799 QCFType<CFURLRef> url = CFURLCreateWithFileSystemPath(0, QCFString(d->filePath),
800 kCFURLPOSIXPathStyle, true);
801 UInt32 type, creator;
802 if (CFBundleGetPackageInfoInDirectory(url, &type, &creator))
803 ret |= BundleType;
805 #endif
808 if (type & FlagsMask) {
809 if (exists)
810 ret |= ExistsFlag;
811 #if defined(Q_OS_SYMBIAN)
812 if (d->filePath == QLatin1String("/")
813 || (d->filePath.length() == 3 && d->filePath.at(0).isLetter()
814 && d->filePath.at(1) == QLatin1Char(':') && d->filePath.at(2) == QLatin1Char('/'))) {
815 ret |= RootFlag;
816 } else {
817 // In Symbian, all symlinks have hidden attribute for some reason;
818 // lets make them visible for better compatibility with other platforms.
819 // If somebody actually wants a hidden link, then they are out of luck.
820 if (!d->isSymlink() && _q_isSymbianHidden(d->filePath, ret & DirectoryType))
821 ret |= HiddenFlag;
823 #else
824 if (d->filePath == QLatin1String("/")) {
825 ret |= RootFlag;
826 } else {
827 QString baseName = fileName(BaseName);
828 if ((baseName.size() > 0 && baseName.at(0) == QLatin1Char('.'))
829 # if !defined(QWS) && defined(Q_OS_MAC)
830 || _q_isMacHidden(d->filePath)
831 # endif
833 ret |= HiddenFlag;
836 #endif
838 return ret;
841 #if defined(Q_OS_SYMBIAN)
842 QString QFSFileEngine::fileName(FileName file) const
844 Q_D(const QFSFileEngine);
845 const QLatin1Char slashChar('/');
846 if(file == BaseName) {
847 int slash = d->filePath.lastIndexOf(slashChar);
848 if(slash == -1) {
849 int colon = d->filePath.lastIndexOf(QLatin1Char(':'));
850 if(colon != -1)
851 return d->filePath.mid(colon + 1);
852 return d->filePath;
854 return d->filePath.mid(slash + 1);
855 } else if(file == PathName) {
856 if(!d->filePath.size())
857 return d->filePath;
859 int slash = d->filePath.lastIndexOf(slashChar);
860 if(slash == -1) {
861 if(d->filePath.length() >= 2 && d->filePath.at(1) == QLatin1Char(':'))
862 return d->filePath.left(2);
863 return QLatin1String(".");
864 } else {
865 if(!slash)
866 return QLatin1String("/");
867 if(slash == 2 && d->filePath.length() >= 2 && d->filePath.at(1) == QLatin1Char(':'))
868 slash++;
869 return d->filePath.left(slash);
871 } else if(file == AbsoluteName || file == AbsolutePathName) {
872 QString ret;
873 if (!isRelativePathSymbian(d->filePath)) {
874 if (d->filePath.size() > 2 && d->filePath.at(1) == QLatin1Char(':')
875 && d->filePath.at(2) != slashChar){
876 // It's a drive-relative path, so C:a.txt -> C:/currentpath/a.txt,
877 // or if it's different drive than current, Z:a.txt -> Z:/a.txt
878 QString currentPath = QDir::currentPath();
879 if (0 == currentPath.left(1).compare(d->filePath.left(1), Qt::CaseInsensitive))
880 ret = currentPath + slashChar + d->filePath.mid(2);
881 else
882 ret = d->filePath.left(2) + slashChar + d->filePath.mid(2);
883 } else if (d->filePath.startsWith(slashChar)) {
884 // It's a absolute path to the current drive, so /a.txt -> C:/a.txt
885 ret = QDir::currentPath().left(2) + d->filePath;
886 } else {
887 ret = d->filePath;
889 } else {
890 ret = QDir::currentPath() + slashChar + d->filePath;
893 // The path should be absolute at this point.
894 // From the docs :
895 // Absolute paths begin with the directory separator "/"
896 // (optionally preceded by a drive specification under Windows).
897 if (ret.at(0) != slashChar) {
898 Q_ASSERT(ret.length() >= 2);
899 Q_ASSERT(ret.at(0).isLetter());
900 Q_ASSERT(ret.at(1) == QLatin1Char(':'));
902 // Force uppercase drive letters.
903 ret[0] = ret.at(0).toUpper();
906 // Clean up the path
907 bool isDir = ret.endsWith(slashChar);
908 ret = QDir::cleanPath(ret);
909 if (isDir && !ret.endsWith(slashChar))
910 ret += slashChar;
912 if (file == AbsolutePathName) {
913 int slash = ret.lastIndexOf(slashChar);
914 if (slash < 0)
915 return ret;
916 else if (ret.at(0) != slashChar && slash == 2)
917 return ret.left(3); // include the slash
918 else
919 return ret.left(slash > 0 ? slash : 1);
921 return ret;
922 } else if(file == CanonicalName || file == CanonicalPathName) {
923 if (!(fileFlags(ExistsFlag) & ExistsFlag))
924 return QString();
926 QString ret = QFSFileEnginePrivate::canonicalized(fileName(AbsoluteName));
927 if (file == CanonicalPathName && !ret.isEmpty()) {
928 int slash = ret.lastIndexOf(slashChar);
929 if (slash == -1)
930 ret = QDir::fromNativeSeparators(QDir::currentPath());
931 else if (slash == 0)
932 ret = QLatin1String("/");
933 ret = ret.left(slash);
935 return ret;
936 } else if(file == LinkName) {
937 if (d->isSymlink()) {
938 char s[PATH_MAX+1];
939 int len = readlink(d->nativeFilePath.constData(), s, PATH_MAX);
940 if (len > 0) {
941 s[len] = '\0';
942 QString ret = QFile::decodeName(QByteArray(s));
944 if (isRelativePathSymbian(ret)) {
945 if (!isRelativePathSymbian(d->filePath)) {
946 ret.prepend(d->filePath.left(d->filePath.lastIndexOf(slashChar))
947 + slashChar);
948 } else {
949 ret.prepend(QDir::currentPath() + slashChar);
952 ret = QDir::cleanPath(ret);
953 if (ret.size() > 1 && ret.endsWith(slashChar))
954 ret.chop(1);
955 return ret;
958 return QString();
959 } else if(file == BundleName) {
960 return QString();
962 return d->filePath;
965 #else
967 QString QFSFileEngine::fileName(FileName file) const
969 Q_D(const QFSFileEngine);
970 if (file == BundleName) {
971 #if !defined(QWS) && defined(Q_OS_MAC)
972 QCFType<CFURLRef> url = CFURLCreateWithFileSystemPath(0, QCFString(d->filePath),
973 kCFURLPOSIXPathStyle, true);
974 if (CFDictionaryRef dict = CFBundleCopyInfoDictionaryForURL(url)) {
975 if (CFTypeRef name = (CFTypeRef)CFDictionaryGetValue(dict, kCFBundleNameKey)) {
976 if (CFGetTypeID(name) == CFStringGetTypeID())
977 return QCFString::toQString((CFStringRef)name);
980 #endif
981 return QString();
982 } else if (file == BaseName) {
983 int slash = d->filePath.lastIndexOf(QLatin1Char('/'));
984 if (slash != -1)
985 return d->filePath.mid(slash + 1);
986 } else if (file == PathName) {
987 int slash = d->filePath.lastIndexOf(QLatin1Char('/'));
988 if (slash == -1)
989 return QLatin1String(".");
990 else if (!slash)
991 return QLatin1String("/");
992 return d->filePath.left(slash);
993 } else if (file == AbsoluteName || file == AbsolutePathName) {
994 QString ret;
995 if (d->filePath.isEmpty() || !d->filePath.startsWith(QLatin1Char('/')))
996 ret = QDir::currentPath();
997 if (!d->filePath.isEmpty() && d->filePath != QLatin1String(".")) {
998 if (!ret.isEmpty() && !ret.endsWith(QLatin1Char('/')))
999 ret += QLatin1Char('/');
1000 ret += d->filePath;
1002 if (ret == QLatin1String("/"))
1003 return ret;
1004 bool isDir = ret.endsWith(QLatin1Char('/'));
1005 ret = QDir::cleanPath(ret);
1006 if (isDir)
1007 ret += QLatin1Char('/');
1008 if (file == AbsolutePathName) {
1009 int slash = ret.lastIndexOf(QLatin1Char('/'));
1010 if (slash == -1)
1011 return QDir::currentPath();
1012 else if (!slash)
1013 return QLatin1String("/");
1014 return ret.left(slash);
1016 return ret;
1017 } else if (file == CanonicalName || file == CanonicalPathName) {
1018 if (!(fileFlags(ExistsFlag) & ExistsFlag))
1019 return QString();
1021 QString ret = QFSFileEnginePrivate::canonicalized(fileName(AbsoluteName));
1022 if (file == CanonicalPathName && !ret.isEmpty()) {
1023 int slash = ret.lastIndexOf(QLatin1Char('/'));
1024 if (slash == -1)
1025 ret = QDir::currentPath();
1026 else if (slash == 0)
1027 ret = QLatin1String("/");
1028 ret = ret.left(slash);
1030 return ret;
1031 } else if (file == LinkName) {
1032 if (d->isSymlink()) {
1033 #if defined(__GLIBC__) && !defined(PATH_MAX)
1034 #define PATH_CHUNK_SIZE 256
1035 char *s = 0;
1036 int len = -1;
1037 int size = PATH_CHUNK_SIZE;
1039 while (1) {
1040 s = q_check_ptr((char *) ::realloc(s, size));
1041 len = ::readlink(d->nativeFilePath.constData(), s, size);
1042 if (len < 0) {
1043 ::free(s);
1044 break;
1046 if (len < size) {
1047 break;
1049 size *= 2;
1051 #else
1052 char s[PATH_MAX+1];
1053 int len = readlink(d->nativeFilePath.constData(), s, PATH_MAX);
1054 #endif
1055 if (len > 0) {
1056 QString ret;
1057 if (d->doStat() && S_ISDIR(d->st.st_mode) && s[0] != '/') {
1058 QDir parent(d->filePath);
1059 parent.cdUp();
1060 ret = parent.path();
1061 if (!ret.isEmpty() && !ret.endsWith(QLatin1Char('/')))
1062 ret += QLatin1Char('/');
1064 s[len] = '\0';
1065 ret += QFile::decodeName(QByteArray(s));
1066 #if defined(__GLIBC__) && !defined(PATH_MAX)
1067 ::free(s);
1068 #endif
1070 if (!ret.startsWith(QLatin1Char('/'))) {
1071 if (d->filePath.startsWith(QLatin1Char('/'))) {
1072 ret.prepend(d->filePath.left(d->filePath.lastIndexOf(QLatin1Char('/')))
1073 + QLatin1Char('/'));
1074 } else {
1075 ret.prepend(QDir::currentPath() + QLatin1Char('/'));
1078 ret = QDir::cleanPath(ret);
1079 if (ret.size() > 1 && ret.endsWith(QLatin1Char('/')))
1080 ret.chop(1);
1081 return ret;
1084 #if !defined(QWS) && defined(Q_OS_MAC)
1086 FSRef fref;
1087 if (FSPathMakeRef((const UInt8 *)QFile::encodeName(QDir::cleanPath(d->filePath)).data(), &fref, 0) == noErr) {
1088 Boolean isAlias, isFolder;
1089 if (FSResolveAliasFile(&fref, true, &isFolder, &isAlias) == noErr && isAlias) {
1090 AliasHandle alias;
1091 if (FSNewAlias(0, &fref, &alias) == noErr && alias) {
1092 CFStringRef cfstr;
1093 if (FSCopyAliasInfo(alias, 0, 0, &cfstr, 0, 0) == noErr)
1094 return QCFString::toQString(cfstr);
1099 #endif
1100 return QString();
1102 return d->filePath;
1104 #endif // Q_OS_SYMBIAN
1106 bool QFSFileEngine::isRelativePath() const
1108 Q_D(const QFSFileEngine);
1109 #if defined(Q_OS_SYMBIAN)
1110 return isRelativePathSymbian(d->filePath);
1111 #else
1112 return d->filePath.length() ? d->filePath[0] != QLatin1Char('/') : true;
1113 #endif
1116 uint QFSFileEngine::ownerId(FileOwner own) const
1118 Q_D(const QFSFileEngine);
1119 static const uint nobodyID = (uint) -2;
1120 if (d->doStat()) {
1121 if (own == OwnerUser)
1122 return d->st.st_uid;
1123 else
1124 return d->st.st_gid;
1126 return nobodyID;
1129 QString QFSFileEngine::owner(FileOwner own) const
1131 #if !defined(QT_NO_THREAD) && defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_OPENBSD)
1132 int size_max = sysconf(_SC_GETPW_R_SIZE_MAX);
1133 if (size_max == -1)
1134 size_max = 1024;
1135 QVarLengthArray<char, 1024> buf(size_max);
1136 #endif
1138 if (own == OwnerUser) {
1139 struct passwd *pw = 0;
1140 #if !defined(QT_NO_THREAD) && defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_OPENBSD)
1141 struct passwd entry;
1142 getpwuid_r(ownerId(own), &entry, buf.data(), buf.size(), &pw);
1143 #else
1144 pw = getpwuid(ownerId(own));
1145 #endif
1146 if (pw)
1147 return QFile::decodeName(QByteArray(pw->pw_name));
1148 } else if (own == OwnerGroup) {
1149 #if !defined(Q_OS_SYMBIAN)
1150 struct group *gr = 0;
1151 #if !defined(QT_NO_THREAD) && defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_OPENBSD)
1152 size_max = sysconf(_SC_GETGR_R_SIZE_MAX);
1153 if (size_max == -1)
1154 size_max = 1024;
1155 buf.resize(size_max);
1156 struct group entry;
1157 // Some large systems have more members than the POSIX max size
1158 // Loop over by doubling the buffer size (upper limit 250k)
1159 for (unsigned size = size_max; size < 256000; size += size)
1161 buf.resize(size);
1162 // ERANGE indicates that the buffer was too small
1163 if (!getgrgid_r(ownerId(own), &entry, buf.data(), buf.size(), &gr)
1164 || errno != ERANGE)
1165 break;
1167 #else
1168 gr = getgrgid(ownerId(own));
1169 #endif
1170 if (gr)
1171 return QFile::decodeName(QByteArray(gr->gr_name));
1172 #endif
1174 return QString();
1177 bool QFSFileEngine::setPermissions(uint perms)
1179 Q_D(QFSFileEngine);
1180 bool ret = false;
1181 mode_t mode = 0;
1182 if (perms & ReadOwnerPerm)
1183 mode |= S_IRUSR;
1184 if (perms & WriteOwnerPerm)
1185 mode |= S_IWUSR;
1186 if (perms & ExeOwnerPerm)
1187 mode |= S_IXUSR;
1188 if (perms & ReadUserPerm)
1189 mode |= S_IRUSR;
1190 if (perms & WriteUserPerm)
1191 mode |= S_IWUSR;
1192 if (perms & ExeUserPerm)
1193 mode |= S_IXUSR;
1194 if (perms & ReadGroupPerm)
1195 mode |= S_IRGRP;
1196 if (perms & WriteGroupPerm)
1197 mode |= S_IWGRP;
1198 if (perms & ExeGroupPerm)
1199 mode |= S_IXGRP;
1200 if (perms & ReadOtherPerm)
1201 mode |= S_IROTH;
1202 if (perms & WriteOtherPerm)
1203 mode |= S_IWOTH;
1204 if (perms & ExeOtherPerm)
1205 mode |= S_IXOTH;
1206 if (d->fd != -1)
1207 ret = fchmod(d->fd, mode) == 0;
1208 else
1209 ret = ::chmod(d->nativeFilePath.constData(), mode) == 0;
1210 if (!ret)
1211 setError(QFile::PermissionsError, qt_error_string(errno));
1212 return ret;
1215 bool QFSFileEngine::setSize(qint64 size)
1217 Q_D(QFSFileEngine);
1218 bool ret = false;
1219 if (d->fd != -1)
1220 ret = QT_FTRUNCATE(d->fd, size) == 0;
1221 else if (d->fh)
1222 ret = QT_FTRUNCATE(QT_FILENO(d->fh), size) == 0;
1223 else
1224 ret = QT_TRUNCATE(d->nativeFilePath.constData(), size) == 0;
1225 if (!ret)
1226 setError(QFile::ResizeError, qt_error_string(errno));
1227 return ret;
1230 QDateTime QFSFileEngine::fileTime(FileTime time) const
1232 Q_D(const QFSFileEngine);
1233 QDateTime ret;
1234 if (d->doStat()) {
1235 if (time == CreationTime)
1236 ret.setTime_t(d->st.st_ctime ? d->st.st_ctime : d->st.st_mtime);
1237 else if (time == ModificationTime)
1238 ret.setTime_t(d->st.st_mtime);
1239 else if (time == AccessTime)
1240 ret.setTime_t(d->st.st_atime);
1242 return ret;
1245 uchar *QFSFileEnginePrivate::map(qint64 offset, qint64 size, QFile::MemoryMapFlags flags)
1247 Q_Q(QFSFileEngine);
1248 Q_UNUSED(flags);
1249 if (openMode == QIODevice::NotOpen) {
1250 q->setError(QFile::PermissionsError, qt_error_string(int(EACCES)));
1251 return 0;
1254 if (offset < 0 || offset != qint64(QT_OFF_T(offset))
1255 || size < 0 || quint64(size) > quint64(size_t(-1))) {
1256 q->setError(QFile::UnspecifiedError, qt_error_string(int(EINVAL)));
1257 return 0;
1260 // If we know the mapping will extend beyond EOF, fail early to avoid
1261 // undefined behavior. Otherwise, let mmap have its say.
1262 if (doStat()
1263 && (QT_OFF_T(size) > st.st_size - QT_OFF_T(offset)))
1264 qWarning("QFSFileEngine::map: Mapping a file beyond its size is not portable");
1266 int access = 0;
1267 if (openMode & QIODevice::ReadOnly) access |= PROT_READ;
1268 if (openMode & QIODevice::WriteOnly) access |= PROT_WRITE;
1270 int pageSize = getpagesize();
1271 int extra = offset % pageSize;
1273 if (size + extra > (size_t)-1) {
1274 q->setError(QFile::UnspecifiedError, qt_error_string(int(EINVAL)));
1275 return 0;
1278 size_t realSize = (size_t)size + extra;
1279 QT_OFF_T realOffset = QT_OFF_T(offset);
1280 realOffset &= ~(QT_OFF_T(pageSize - 1));
1282 #ifdef Q_OS_SYMBIAN
1283 void *mapAddress;
1284 TRAPD(err, mapAddress = QT_MMAP((void*)0, realSize,
1285 access, MAP_SHARED, nativeHandle(), realOffset));
1286 if (err != KErrNone) {
1287 qWarning("OpenC bug: leave from mmap %d", err);
1288 mapAddress = MAP_FAILED;
1289 errno = EINVAL;
1291 #else
1292 void *mapAddress = QT_MMAP((void*)0, realSize,
1293 access, MAP_SHARED, nativeHandle(), realOffset);
1294 #endif
1295 if (MAP_FAILED != mapAddress) {
1296 uchar *address = extra + static_cast<uchar*>(mapAddress);
1297 maps[address] = QPair<int,size_t>(extra, realSize);
1298 return address;
1301 switch(errno) {
1302 case EBADF:
1303 q->setError(QFile::PermissionsError, qt_error_string(int(EACCES)));
1304 break;
1305 case ENFILE:
1306 case ENOMEM:
1307 q->setError(QFile::ResourceError, qt_error_string(int(errno)));
1308 break;
1309 case EINVAL:
1310 // size are out of bounds
1311 default:
1312 q->setError(QFile::UnspecifiedError, qt_error_string(int(errno)));
1313 break;
1315 return 0;
1318 bool QFSFileEnginePrivate::unmap(uchar *ptr)
1320 Q_Q(QFSFileEngine);
1321 if (!maps.contains(ptr)) {
1322 q->setError(QFile::PermissionsError, qt_error_string(EACCES));
1323 return false;
1326 uchar *start = ptr - maps[ptr].first;
1327 size_t len = maps[ptr].second;
1328 if (-1 == munmap(start, len)) {
1329 q->setError(QFile::UnspecifiedError, qt_error_string(errno));
1330 return false;
1332 maps.remove(ptr);
1333 return true;
1336 QT_END_NAMESPACE
1338 #endif // QT_NO_FSFILEENGINE