Revert check for file size to a warning in QFile::map
[qt-netbsd.git] / src / corelib / io / qfsfileengine_unix.cpp
blob35737b30fe2ee0e79798f578b09a78d18c8e1647
1 /****************************************************************************
2 **
3 ** Copyright (C) 2009 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 QT_STATBUF statBuf;
195 if (QT_FSTAT(fd, &statBuf) != -1) {
196 if ((statBuf.st_mode & S_IFMT) == S_IFDIR) {
197 q->setError(QFile::OpenError, QLatin1String("file to open is a directory"));
198 QT_CLOSE(fd);
199 return false;
203 // Seek to the end when in Append mode.
204 if (flags & QFile::Append) {
205 int ret;
206 do {
207 ret = QT_LSEEK(fd, 0, SEEK_END);
208 } while (ret == -1 && errno == EINTR);
210 if (ret == -1) {
211 q->setError(errno == EMFILE ? QFile::ResourceError : QFile::OpenError,
212 qt_error_string(int(errno)));
213 return false;
217 fh = 0;
218 } else {
219 QByteArray fopenMode = openModeToFopenMode(openMode, filePath);
221 // Try to open the file in buffered mode.
222 do {
223 fh = QT_FOPEN(nativeFilePath.constData(), fopenMode.constData());
224 } while (!fh && errno == EINTR);
226 // On failure, return and report the error.
227 if (!fh) {
228 q->setError(errno == EMFILE ? QFile::ResourceError : QFile::OpenError,
229 qt_error_string(int(errno)));
230 return false;
233 QT_STATBUF statBuf;
234 if (QT_FSTAT(fileno(fh), &statBuf) != -1) {
235 if ((statBuf.st_mode & S_IFMT) == S_IFDIR) {
236 q->setError(QFile::OpenError, QLatin1String("file to open is a directory"));
237 fclose(fh);
238 return false;
242 setCloseOnExec(fileno(fh)); // ignore failure
244 // Seek to the end when in Append mode.
245 if (openMode & QIODevice::Append) {
246 int ret;
247 do {
248 ret = QT_FSEEK(fh, 0, SEEK_END);
249 } while (ret == -1 && errno == EINTR);
251 if (ret == -1) {
252 q->setError(errno == EMFILE ? QFile::ResourceError : QFile::OpenError,
253 qt_error_string(int(errno)));
254 return false;
258 fd = -1;
261 closeFileHandle = true;
262 return true;
266 \internal
268 bool QFSFileEnginePrivate::nativeClose()
270 return closeFdFh();
274 \internal
277 bool QFSFileEnginePrivate::nativeFlush()
279 return fh ? flushFh() : fd != -1;
283 \internal
285 qint64 QFSFileEnginePrivate::nativeRead(char *data, qint64 len)
287 Q_Q(QFSFileEngine);
289 if (fh && nativeIsSequential()) {
290 size_t readBytes = 0;
291 int oldFlags = fcntl(QT_FILENO(fh), F_GETFL);
292 for (int i = 0; i < 2; ++i) {
293 // Unix: Make the underlying file descriptor non-blocking
294 if ((oldFlags & O_NONBLOCK) == 0)
295 fcntl(QT_FILENO(fh), F_SETFL, oldFlags | O_NONBLOCK);
297 // Cross platform stdlib read
298 size_t read = 0;
299 do {
300 read = fread(data + readBytes, 1, size_t(len - readBytes), fh);
301 } while (read == 0 && !feof(fh) && errno == EINTR);
302 if (read > 0) {
303 readBytes += read;
304 break;
305 } else {
306 if (readBytes)
307 break;
308 readBytes = read;
311 // Unix: Restore the blocking state of the underlying socket
312 if ((oldFlags & O_NONBLOCK) == 0) {
313 fcntl(QT_FILENO(fh), F_SETFL, oldFlags);
314 if (readBytes == 0) {
315 int readByte = 0;
316 do {
317 readByte = fgetc(fh);
318 } while (readByte == -1 && errno == EINTR);
319 if (readByte != -1) {
320 *data = uchar(readByte);
321 readBytes += 1;
322 } else {
323 break;
328 // Unix: Restore the blocking state of the underlying socket
329 if ((oldFlags & O_NONBLOCK) == 0) {
330 fcntl(QT_FILENO(fh), F_SETFL, oldFlags);
332 if (readBytes == 0 && !feof(fh)) {
333 // if we didn't read anything and we're not at EOF, it must be an error
334 q->setError(QFile::ReadError, qt_error_string(int(errno)));
335 return -1;
337 return readBytes;
340 return readFdFh(data, len);
344 \internal
346 qint64 QFSFileEnginePrivate::nativeReadLine(char *data, qint64 maxlen)
348 return readLineFdFh(data, maxlen);
352 \internal
354 qint64 QFSFileEnginePrivate::nativeWrite(const char *data, qint64 len)
356 return writeFdFh(data, len);
360 \internal
362 qint64 QFSFileEnginePrivate::nativePos() const
364 return posFdFh();
368 \internal
370 bool QFSFileEnginePrivate::nativeSeek(qint64 pos)
372 return seekFdFh(pos);
376 \internal
378 int QFSFileEnginePrivate::nativeHandle() const
380 return fh ? fileno(fh) : fd;
384 \internal
386 bool QFSFileEnginePrivate::nativeIsSequential() const
388 return isSequentialFdFh();
391 bool QFSFileEngine::remove()
393 Q_D(QFSFileEngine);
394 bool ret = unlink(d->nativeFilePath.constData()) == 0;
395 if (!ret)
396 setError(QFile::RemoveError, qt_error_string(errno));
397 return ret;
400 bool QFSFileEngine::copy(const QString &newName)
402 #if defined(Q_OS_SYMBIAN)
403 Q_D(QFSFileEngine);
404 RFs rfs = qt_s60GetRFs();
405 CFileMan* fm = NULL;
406 QString oldNative(QDir::toNativeSeparators(d->filePath));
407 TPtrC oldPtr(qt_QString2TPtrC(oldNative));
408 QFileInfo fi(newName);
409 QString absoluteNewName = fi.absoluteFilePath();
410 QString newNative(QDir::toNativeSeparators(absoluteNewName));
411 TPtrC newPtr(qt_QString2TPtrC(newNative));
412 TRAPD (err,
413 fm = CFileMan::NewL(rfs);
414 RFile rfile;
415 err = rfile.Open(rfs, oldPtr, EFileShareReadersOrWriters);
416 if (err == KErrNone) {
417 err = fm->Copy(rfile, newPtr);
418 rfile.Close();
420 ) // End TRAP
421 delete fm;
422 // ### Add error reporting on failure
423 return (err == KErrNone);
424 #else
425 Q_UNUSED(newName);
426 // ### Add copy code for Unix here
427 setError(QFile::UnspecifiedError, QLatin1String("Not implemented!"));
428 return false;
429 #endif
432 bool QFSFileEngine::rename(const QString &newName)
434 Q_D(QFSFileEngine);
435 bool ret = ::rename(d->nativeFilePath.constData(), QFile::encodeName(newName).constData()) == 0;
436 if (!ret)
437 setError(QFile::RenameError, qt_error_string(errno));
438 return ret;
441 bool QFSFileEngine::link(const QString &newName)
443 Q_D(QFSFileEngine);
444 bool ret = ::symlink(d->nativeFilePath.constData(), QFile::encodeName(newName).constData()) == 0;
445 if (!ret)
446 setError(QFile::RenameError, qt_error_string(errno));
447 return ret;
450 qint64 QFSFileEnginePrivate::nativeSize() const
452 return sizeFdFh();
455 bool QFSFileEngine::mkdir(const QString &name, bool createParentDirectories) const
457 QString dirName = name;
458 if (createParentDirectories) {
459 dirName = QDir::cleanPath(dirName);
460 #if defined(Q_OS_SYMBIAN)
461 dirName = QDir::toNativeSeparators(dirName);
462 #endif
463 for(int oldslash = -1, slash=0; slash != -1; oldslash = slash) {
464 slash = dirName.indexOf(QDir::separator(), oldslash+1);
465 if (slash == -1) {
466 if (oldslash == dirName.length())
467 break;
468 slash = dirName.length();
470 if (slash) {
471 QByteArray chunk = QFile::encodeName(dirName.left(slash));
472 QT_STATBUF st;
473 if (QT_STAT(chunk, &st) != -1) {
474 if ((st.st_mode & S_IFMT) != S_IFDIR)
475 return false;
476 } else if (QT_MKDIR(chunk, 0777) != 0) {
477 return false;
481 return true;
483 #if defined(Q_OS_DARWIN) // Mac X doesn't support trailing /'s
484 if (dirName.endsWith(QLatin1Char('/')))
485 dirName.chop(1);
486 #endif
487 return (QT_MKDIR(QFile::encodeName(dirName), 0777) == 0);
490 bool QFSFileEngine::rmdir(const QString &name, bool recurseParentDirectories) const
492 QString dirName = name;
493 if (recurseParentDirectories) {
494 dirName = QDir::cleanPath(dirName);
495 #if defined(Q_OS_SYMBIAN)
496 dirName = QDir::toNativeSeparators(dirName);
497 #endif
498 for(int oldslash = 0, slash=dirName.length(); slash > 0; oldslash = slash) {
499 QByteArray chunk = QFile::encodeName(dirName.left(slash));
500 QT_STATBUF st;
501 if (QT_STAT(chunk, &st) != -1) {
502 if ((st.st_mode & S_IFMT) != S_IFDIR)
503 return false;
504 if (::rmdir(chunk) != 0)
505 return oldslash != 0;
506 } else {
507 return false;
509 slash = dirName.lastIndexOf(QDir::separator(), oldslash-1);
511 return true;
513 return ::rmdir(QFile::encodeName(dirName)) == 0;
516 bool QFSFileEngine::caseSensitive() const
518 #if defined(Q_OS_SYMBIAN)
519 return false;
520 #else
521 return true;
522 #endif
525 bool QFSFileEngine::setCurrentPath(const QString &path)
527 int r;
528 r = QT_CHDIR(QFile::encodeName(path));
529 return r >= 0;
532 QString QFSFileEngine::currentPath(const QString &)
534 QString result;
535 QT_STATBUF st;
536 #if defined(Q_OS_SYMBIAN)
537 char nativeCurrentName[PATH_MAX+1];
538 if (::getcwd(nativeCurrentName, PATH_MAX))
539 result = QDir::fromNativeSeparators(QFile::decodeName(QByteArray(nativeCurrentName)));
540 if (result.isEmpty()) {
541 # if defined(QT_DEBUG)
542 qWarning("QFSFileEngine::currentPath: getcwd() failed");
543 # endif
544 } else
545 #endif
546 if (QT_STAT(".", &st) == 0) {
547 #if defined(__GLIBC__) && !defined(PATH_MAX)
548 char *currentName = ::get_current_dir_name();
549 if (currentName) {
550 result = QFile::decodeName(QByteArray(currentName));
551 ::free(currentName);
553 #elif !defined(Q_OS_SYMBIAN)
554 char currentName[PATH_MAX+1];
555 if (::getcwd(currentName, PATH_MAX))
556 result = QFile::decodeName(QByteArray(currentName));
557 # if defined(QT_DEBUG)
558 if (result.isNull())
559 qWarning("QFSFileEngine::currentPath: getcwd() failed");
560 # endif
561 #endif
562 } else {
563 #if defined(Q_OS_SYMBIAN)
564 // If current dir returned by Open C doesn't exist,
565 // try to create it (can happen with application private dirs)
566 // Ignore mkdir failures; we want to be consistent with Open C
567 // current path regardless.
568 QT_MKDIR(QFile::encodeName(QLatin1String(nativeCurrentName)), 0777);
569 #else
570 # if defined(QT_DEBUG)
571 qWarning("QFSFileEngine::currentPath: stat(\".\") failed");
572 # endif
573 #endif
575 return result;
578 QString QFSFileEngine::homePath()
580 #if defined(Q_OS_SYMBIAN)
581 QString home = rootPath();
582 #else
583 QString home = QFile::decodeName(qgetenv("HOME"));
584 if (home.isNull())
585 home = rootPath();
586 #endif
587 return home;
590 QString QFSFileEngine::rootPath()
592 #if defined(Q_OS_SYMBIAN)
593 # ifdef Q_WS_S60
594 TFileName symbianPath = PathInfo::PhoneMemoryRootPath();
595 return QDir::cleanPath(QDir::fromNativeSeparators(qt_TDesC2QString(symbianPath)));
596 # else
597 # warning No fallback implementation of QFSFileEngine::rootPath()
598 return QString();
599 # endif
600 #else
601 return QLatin1String("/");
602 #endif
605 QString QFSFileEngine::tempPath()
607 #if defined(Q_OS_SYMBIAN)
608 # ifdef Q_WS_S60
609 TFileName symbianPath = PathInfo::PhoneMemoryRootPath();
610 QString temp = QDir::fromNativeSeparators(qt_TDesC2QString(symbianPath));
611 temp += QLatin1String( "temp/");
613 // Just to verify that folder really exist on hardware
614 QT_MKDIR(QFile::encodeName(temp), 0777);
615 # else
616 # warning No fallback implementation of QFSFileEngine::tempPath()
617 QString temp;
618 # endif
619 #else
620 QString temp = QFile::decodeName(qgetenv("TMPDIR"));
621 if (temp.isEmpty())
622 temp = QLatin1String("/tmp/");
623 #endif
624 return temp;
627 QFileInfoList QFSFileEngine::drives()
629 QFileInfoList ret;
630 #if defined(Q_OS_SYMBIAN)
631 TDriveList driveList;
632 RFs rfs = qt_s60GetRFs();
633 TInt err = rfs.DriveList(driveList);
634 if (err == KErrNone) {
635 char driveName[] = "A:/";
637 for (char i = 0; i < KMaxDrives; i++) {
638 if (driveList[i]) {
639 driveName[0] = 'A' + i;
640 ret.append(QFileInfo(QLatin1String(driveName)));
643 } else {
644 qWarning("QFSFileEngine::drives: Getting drives failed");
646 #else
647 ret.append(QFileInfo(rootPath()));
648 #endif
649 return ret;
652 bool QFSFileEnginePrivate::doStat() const
654 if (!tried_stat) {
655 tried_stat = true;
656 could_stat = false;
658 if (fh && nativeFilePath.isEmpty()) {
659 // ### actually covers two cases: d->fh and when the file is not open
660 could_stat = (QT_FSTAT(QT_FILENO(fh), &st) == 0);
661 } else if (fd == -1) {
662 // ### actually covers two cases: d->fh and when the file is not open
663 could_stat = (QT_STAT(nativeFilePath.constData(), &st) == 0);
664 } else {
665 could_stat = (QT_FSTAT(fd, &st) == 0);
668 return could_stat;
671 bool QFSFileEnginePrivate::isSymlink() const
673 if (need_lstat) {
674 need_lstat = false;
676 QT_STATBUF st; // don't clobber our main one
677 is_link = (QT_LSTAT(nativeFilePath.constData(), &st) == 0) ? S_ISLNK(st.st_mode) : false;
679 return is_link;
682 #if defined(Q_OS_SYMBIAN)
683 static bool _q_isSymbianHidden(const QString &path, bool isDir)
685 RFs rfs = qt_s60GetRFs();
686 QFileInfo fi(path);
687 QString absPath = fi.absoluteFilePath();
688 if (isDir && !absPath.endsWith(QLatin1Char('/')))
689 absPath.append(QLatin1Char('/'));
690 QString native(QDir::toNativeSeparators(absPath));
691 TPtrC ptr(qt_QString2TPtrC(native));
692 TUint attributes;
693 TInt err = rfs.Att(ptr, attributes);
694 return (err == KErrNone && (attributes & KEntryAttHidden));
696 #endif
698 #if !defined(QWS) && defined(Q_OS_MAC)
699 static bool _q_isMacHidden(const QString &path)
701 OSErr err = noErr;
703 FSRef fsRef;
705 err = FSPathMakeRefWithOptions(reinterpret_cast<const UInt8 *>(QFile::encodeName(QDir::cleanPath(path)).constData()),
706 kFSPathMakeRefDoNotFollowLeafSymlink, &fsRef, 0);
707 if (err != noErr)
708 return false;
710 FSCatalogInfo catInfo;
711 err = FSGetCatalogInfo(&fsRef, kFSCatInfoFinderInfo, &catInfo, NULL, NULL, NULL);
712 if (err != noErr)
713 return false;
715 FileInfo * const fileInfo = reinterpret_cast<FileInfo*>(&catInfo.finderInfo);
716 bool result = (fileInfo->finderFlags & kIsInvisible);
717 return result;
719 #endif
722 \reimp
724 QAbstractFileEngine::FileFlags QFSFileEngine::fileFlags(FileFlags type) const
726 Q_D(const QFSFileEngine);
727 // Force a stat, so that we're guaranteed to get up-to-date results
728 if (type & Refresh) {
729 d->tried_stat = 0;
730 d->need_lstat = 1;
733 QAbstractFileEngine::FileFlags ret = 0;
734 if (type & FlagsMask)
735 ret |= LocalDiskFlag;
736 bool exists = d->doStat();
737 if (!exists && !d->isSymlink())
738 return ret;
740 if (exists && (type & PermsMask)) {
741 if (d->st.st_mode & S_IRUSR)
742 ret |= ReadOwnerPerm;
743 if (d->st.st_mode & S_IWUSR)
744 ret |= WriteOwnerPerm;
745 if (d->st.st_mode & S_IXUSR)
746 ret |= ExeOwnerPerm;
747 if (d->st.st_mode & S_IRUSR)
748 ret |= ReadUserPerm;
749 if (d->st.st_mode & S_IWUSR)
750 ret |= WriteUserPerm;
751 if (d->st.st_mode & S_IXUSR)
752 ret |= ExeUserPerm;
753 if (d->st.st_mode & S_IRGRP)
754 ret |= ReadGroupPerm;
755 if (d->st.st_mode & S_IWGRP)
756 ret |= WriteGroupPerm;
757 if (d->st.st_mode & S_IXGRP)
758 ret |= ExeGroupPerm;
759 if (d->st.st_mode & S_IROTH)
760 ret |= ReadOtherPerm;
761 if (d->st.st_mode & S_IWOTH)
762 ret |= WriteOtherPerm;
763 if (d->st.st_mode & S_IXOTH)
764 ret |= ExeOtherPerm;
766 if (type & TypesMask) {
767 #if !defined(QWS) && defined(Q_OS_MAC)
768 bool foundAlias = false;
770 FSRef fref;
771 if (FSPathMakeRef((const UInt8 *)QFile::encodeName(QDir::cleanPath(d->filePath)).data(),
772 &fref, NULL) == noErr) {
773 Boolean isAlias, isFolder;
774 if (FSIsAliasFile(&fref, &isAlias, &isFolder) == noErr && isAlias) {
775 foundAlias = true;
776 ret |= LinkType;
780 if (!foundAlias)
781 #endif
783 if ((type & LinkType) && d->isSymlink())
784 ret |= LinkType;
785 if (exists && (d->st.st_mode & S_IFMT) == S_IFREG)
786 ret |= FileType;
787 else if (exists && (d->st.st_mode & S_IFMT) == S_IFDIR)
788 ret |= DirectoryType;
789 #if !defined(QWS) && defined(Q_OS_MAC)
790 if ((ret & DirectoryType) && (type & BundleType)) {
791 QCFType<CFURLRef> url = CFURLCreateWithFileSystemPath(0, QCFString(d->filePath),
792 kCFURLPOSIXPathStyle, true);
793 UInt32 type, creator;
794 if (CFBundleGetPackageInfoInDirectory(url, &type, &creator))
795 ret |= BundleType;
797 #endif
800 if (type & FlagsMask) {
801 if (exists)
802 ret |= ExistsFlag;
803 #if defined(Q_OS_SYMBIAN)
804 if (d->filePath == QLatin1String("/")
805 || (d->filePath.length() == 3 && d->filePath.at(0).isLetter()
806 && d->filePath.at(1) == QLatin1Char(':') && d->filePath.at(2) == QLatin1Char('/'))) {
807 ret |= RootFlag;
808 } else {
809 // In Symbian, all symlinks have hidden attribute for some reason;
810 // lets make them visible for better compatibility with other platforms.
811 // If somebody actually wants a hidden link, then they are out of luck.
812 if (!d->isSymlink() && _q_isSymbianHidden(d->filePath, ret & DirectoryType))
813 ret |= HiddenFlag;
815 #else
816 if (d->filePath == QLatin1String("/")) {
817 ret |= RootFlag;
818 } else {
819 QString baseName = fileName(BaseName);
820 if ((baseName.size() > 1
821 && baseName.at(0) == QLatin1Char('.') && baseName.at(1) != QLatin1Char('.'))
822 # if !defined(QWS) && defined(Q_OS_MAC)
823 || _q_isMacHidden(d->filePath)
824 # endif
826 ret |= HiddenFlag;
829 #endif
831 return ret;
834 #if defined(Q_OS_SYMBIAN)
835 QString QFSFileEngine::fileName(FileName file) const
837 Q_D(const QFSFileEngine);
838 const QLatin1Char slashChar('/');
839 if(file == BaseName) {
840 int slash = d->filePath.lastIndexOf(slashChar);
841 if(slash == -1) {
842 int colon = d->filePath.lastIndexOf(QLatin1Char(':'));
843 if(colon != -1)
844 return d->filePath.mid(colon + 1);
845 return d->filePath;
847 return d->filePath.mid(slash + 1);
848 } else if(file == PathName) {
849 if(!d->filePath.size())
850 return d->filePath;
852 int slash = d->filePath.lastIndexOf(slashChar);
853 if(slash == -1) {
854 if(d->filePath.length() >= 2 && d->filePath.at(1) == QLatin1Char(':'))
855 return d->filePath.left(2);
856 return QLatin1String(".");
857 } else {
858 if(!slash)
859 return QLatin1String("/");
860 if(slash == 2 && d->filePath.length() >= 2 && d->filePath.at(1) == QLatin1Char(':'))
861 slash++;
862 return d->filePath.left(slash);
864 } else if(file == AbsoluteName || file == AbsolutePathName) {
865 QString ret;
866 if (!isRelativePathSymbian(d->filePath)) {
867 if (d->filePath.size() > 2 && d->filePath.at(1) == QLatin1Char(':')
868 && d->filePath.at(2) != slashChar){
869 // It's a drive-relative path, so C:a.txt -> C:/currentpath/a.txt,
870 // or if it's different drive than current, Z:a.txt -> Z:/a.txt
871 QString currentPath = QDir::currentPath();
872 if (0 == currentPath.left(1).compare(d->filePath.left(1), Qt::CaseInsensitive))
873 ret = currentPath + slashChar + d->filePath.mid(2);
874 else
875 ret = d->filePath.left(2) + slashChar + d->filePath.mid(2);
876 } else if (d->filePath.startsWith(slashChar)) {
877 // It's a absolute path to the current drive, so /a.txt -> C:/a.txt
878 ret = QDir::currentPath().left(2) + d->filePath;
879 } else {
880 ret = d->filePath;
882 } else {
883 ret = QDir::currentPath() + slashChar + d->filePath;
886 // The path should be absolute at this point.
887 // From the docs :
888 // Absolute paths begin with the directory separator "/"
889 // (optionally preceded by a drive specification under Windows).
890 if (ret.at(0) != slashChar) {
891 Q_ASSERT(ret.length() >= 2);
892 Q_ASSERT(ret.at(0).isLetter());
893 Q_ASSERT(ret.at(1) == QLatin1Char(':'));
895 // Force uppercase drive letters.
896 ret[0] = ret.at(0).toUpper();
899 // Clean up the path
900 bool isDir = ret.endsWith(slashChar);
901 ret = QDir::cleanPath(ret);
902 if (isDir && !ret.endsWith(slashChar))
903 ret += slashChar;
905 if (file == AbsolutePathName) {
906 int slash = ret.lastIndexOf(slashChar);
907 if (slash < 0)
908 return ret;
909 else if (ret.at(0) != slashChar && slash == 2)
910 return ret.left(3); // include the slash
911 else
912 return ret.left(slash > 0 ? slash : 1);
914 return ret;
915 } else if(file == CanonicalName || file == CanonicalPathName) {
916 if (!(fileFlags(ExistsFlag) & ExistsFlag))
917 return QString();
919 QString ret = QFSFileEnginePrivate::canonicalized(fileName(AbsoluteName));
920 if (file == CanonicalPathName && !ret.isEmpty()) {
921 int slash = ret.lastIndexOf(slashChar);
922 if (slash == -1)
923 ret = QDir::fromNativeSeparators(QDir::currentPath());
924 else if (slash == 0)
925 ret = QLatin1String("/");
926 ret = ret.left(slash);
928 return ret;
929 } else if(file == LinkName) {
930 if (d->isSymlink()) {
931 char s[PATH_MAX+1];
932 int len = readlink(d->nativeFilePath.constData(), s, PATH_MAX);
933 if (len > 0) {
934 s[len] = '\0';
935 QString ret = QFile::decodeName(QByteArray(s));
937 if (isRelativePathSymbian(ret)) {
938 if (!isRelativePathSymbian(d->filePath)) {
939 ret.prepend(d->filePath.left(d->filePath.lastIndexOf(slashChar))
940 + slashChar);
941 } else {
942 ret.prepend(QDir::currentPath() + slashChar);
945 ret = QDir::cleanPath(ret);
946 if (ret.size() > 1 && ret.endsWith(slashChar))
947 ret.chop(1);
948 return ret;
951 return QString();
952 } else if(file == BundleName) {
953 return QString();
955 return d->filePath;
958 #else
960 QString QFSFileEngine::fileName(FileName file) const
962 Q_D(const QFSFileEngine);
963 if (file == BundleName) {
964 #if !defined(QWS) && defined(Q_OS_MAC)
965 QCFType<CFURLRef> url = CFURLCreateWithFileSystemPath(0, QCFString(d->filePath),
966 kCFURLPOSIXPathStyle, true);
967 if (CFDictionaryRef dict = CFBundleCopyInfoDictionaryForURL(url)) {
968 if (CFTypeRef name = (CFTypeRef)CFDictionaryGetValue(dict, kCFBundleNameKey)) {
969 if (CFGetTypeID(name) == CFStringGetTypeID())
970 return QCFString::toQString((CFStringRef)name);
973 #endif
974 return QString();
975 } else if (file == BaseName) {
976 int slash = d->filePath.lastIndexOf(QLatin1Char('/'));
977 if (slash != -1)
978 return d->filePath.mid(slash + 1);
979 } else if (file == PathName) {
980 int slash = d->filePath.lastIndexOf(QLatin1Char('/'));
981 if (slash == -1)
982 return QLatin1String(".");
983 else if (!slash)
984 return QLatin1String("/");
985 return d->filePath.left(slash);
986 } else if (file == AbsoluteName || file == AbsolutePathName) {
987 QString ret;
988 if (d->filePath.isEmpty() || !d->filePath.startsWith(QLatin1Char('/')))
989 ret = QDir::currentPath();
990 if (!d->filePath.isEmpty() && d->filePath != QLatin1String(".")) {
991 if (!ret.isEmpty() && !ret.endsWith(QLatin1Char('/')))
992 ret += QLatin1Char('/');
993 ret += d->filePath;
995 if (ret == QLatin1String("/"))
996 return ret;
997 bool isDir = ret.endsWith(QLatin1Char('/'));
998 ret = QDir::cleanPath(ret);
999 if (isDir)
1000 ret += QLatin1Char('/');
1001 if (file == AbsolutePathName) {
1002 int slash = ret.lastIndexOf(QLatin1Char('/'));
1003 if (slash == -1)
1004 return QDir::currentPath();
1005 else if (!slash)
1006 return QLatin1String("/");
1007 return ret.left(slash);
1009 return ret;
1010 } else if (file == CanonicalName || file == CanonicalPathName) {
1011 if (!(fileFlags(ExistsFlag) & ExistsFlag))
1012 return QString();
1014 QString ret = QFSFileEnginePrivate::canonicalized(fileName(AbsoluteName));
1015 if (file == CanonicalPathName && !ret.isEmpty()) {
1016 int slash = ret.lastIndexOf(QLatin1Char('/'));
1017 if (slash == -1)
1018 ret = QDir::currentPath();
1019 else if (slash == 0)
1020 ret = QLatin1String("/");
1021 ret = ret.left(slash);
1023 return ret;
1024 } else if (file == LinkName) {
1025 if (d->isSymlink()) {
1026 #if defined(__GLIBC__) && !defined(PATH_MAX)
1027 #define PATH_CHUNK_SIZE 256
1028 char *s = 0;
1029 int len = -1;
1030 int size = PATH_CHUNK_SIZE;
1032 while (1) {
1033 s = q_check_ptr((char *) ::realloc(s, size));
1034 len = ::readlink(d->nativeFilePath.constData(), s, size);
1035 if (len < 0) {
1036 ::free(s);
1037 break;
1039 if (len < size) {
1040 break;
1042 size *= 2;
1044 #else
1045 char s[PATH_MAX+1];
1046 int len = readlink(d->nativeFilePath.constData(), s, PATH_MAX);
1047 #endif
1048 if (len > 0) {
1049 QString ret;
1050 if (d->doStat() && S_ISDIR(d->st.st_mode) && s[0] != '/') {
1051 QDir parent(d->filePath);
1052 parent.cdUp();
1053 ret = parent.path();
1054 if (!ret.isEmpty() && !ret.endsWith(QLatin1Char('/')))
1055 ret += QLatin1Char('/');
1057 s[len] = '\0';
1058 ret += QFile::decodeName(QByteArray(s));
1059 #if defined(__GLIBC__) && !defined(PATH_MAX)
1060 ::free(s);
1061 #endif
1063 if (!ret.startsWith(QLatin1Char('/'))) {
1064 if (d->filePath.startsWith(QLatin1Char('/'))) {
1065 ret.prepend(d->filePath.left(d->filePath.lastIndexOf(QLatin1Char('/')))
1066 + QLatin1Char('/'));
1067 } else {
1068 ret.prepend(QDir::currentPath() + QLatin1Char('/'));
1071 ret = QDir::cleanPath(ret);
1072 if (ret.size() > 1 && ret.endsWith(QLatin1Char('/')))
1073 ret.chop(1);
1074 return ret;
1077 #if !defined(QWS) && defined(Q_OS_MAC)
1079 FSRef fref;
1080 if (FSPathMakeRef((const UInt8 *)QFile::encodeName(QDir::cleanPath(d->filePath)).data(), &fref, 0) == noErr) {
1081 Boolean isAlias, isFolder;
1082 if (FSResolveAliasFile(&fref, true, &isFolder, &isAlias) == noErr && isAlias) {
1083 AliasHandle alias;
1084 if (FSNewAlias(0, &fref, &alias) == noErr && alias) {
1085 CFStringRef cfstr;
1086 if (FSCopyAliasInfo(alias, 0, 0, &cfstr, 0, 0) == noErr)
1087 return QCFString::toQString(cfstr);
1092 #endif
1093 return QString();
1095 return d->filePath;
1097 #endif // Q_OS_SYMBIAN
1099 bool QFSFileEngine::isRelativePath() const
1101 Q_D(const QFSFileEngine);
1102 #if defined(Q_OS_SYMBIAN)
1103 return isRelativePathSymbian(d->filePath);
1104 #else
1105 return d->filePath.length() ? d->filePath[0] != QLatin1Char('/') : true;
1106 #endif
1109 uint QFSFileEngine::ownerId(FileOwner own) const
1111 Q_D(const QFSFileEngine);
1112 static const uint nobodyID = (uint) -2;
1113 if (d->doStat()) {
1114 if (own == OwnerUser)
1115 return d->st.st_uid;
1116 else
1117 return d->st.st_gid;
1119 return nobodyID;
1122 QString QFSFileEngine::owner(FileOwner own) const
1124 #if !defined(QT_NO_THREAD) && defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_OPENBSD)
1125 int size_max = sysconf(_SC_GETPW_R_SIZE_MAX);
1126 if (size_max == -1)
1127 size_max = 1024;
1128 QVarLengthArray<char, 1024> buf(size_max);
1129 #endif
1131 if (own == OwnerUser) {
1132 struct passwd *pw = 0;
1133 #if !defined(QT_NO_THREAD) && defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_OPENBSD)
1134 struct passwd entry;
1135 getpwuid_r(ownerId(own), &entry, buf.data(), buf.size(), &pw);
1136 #else
1137 pw = getpwuid(ownerId(own));
1138 #endif
1139 if (pw)
1140 return QFile::decodeName(QByteArray(pw->pw_name));
1141 } else if (own == OwnerGroup) {
1142 #if !defined(Q_OS_SYMBIAN)
1143 struct group *gr = 0;
1144 #if !defined(QT_NO_THREAD) && defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_OPENBSD)
1145 size_max = sysconf(_SC_GETGR_R_SIZE_MAX);
1146 if (size_max == -1)
1147 size_max = 1024;
1148 buf.resize(size_max);
1149 struct group entry;
1150 // Some large systems have more members than the POSIX max size
1151 // Loop over by doubling the buffer size (upper limit 250k)
1152 for (unsigned size = size_max; size < 256000; size += size)
1154 buf.resize(size);
1155 // ERANGE indicates that the buffer was too small
1156 if (!getgrgid_r(ownerId(own), &entry, buf.data(), buf.size(), &gr)
1157 || errno != ERANGE)
1158 break;
1160 #else
1161 gr = getgrgid(ownerId(own));
1162 #endif
1163 if (gr)
1164 return QFile::decodeName(QByteArray(gr->gr_name));
1165 #endif
1167 return QString();
1170 bool QFSFileEngine::setPermissions(uint perms)
1172 Q_D(QFSFileEngine);
1173 bool ret = false;
1174 mode_t mode = 0;
1175 if (perms & ReadOwnerPerm)
1176 mode |= S_IRUSR;
1177 if (perms & WriteOwnerPerm)
1178 mode |= S_IWUSR;
1179 if (perms & ExeOwnerPerm)
1180 mode |= S_IXUSR;
1181 if (perms & ReadUserPerm)
1182 mode |= S_IRUSR;
1183 if (perms & WriteUserPerm)
1184 mode |= S_IWUSR;
1185 if (perms & ExeUserPerm)
1186 mode |= S_IXUSR;
1187 if (perms & ReadGroupPerm)
1188 mode |= S_IRGRP;
1189 if (perms & WriteGroupPerm)
1190 mode |= S_IWGRP;
1191 if (perms & ExeGroupPerm)
1192 mode |= S_IXGRP;
1193 if (perms & ReadOtherPerm)
1194 mode |= S_IROTH;
1195 if (perms & WriteOtherPerm)
1196 mode |= S_IWOTH;
1197 if (perms & ExeOtherPerm)
1198 mode |= S_IXOTH;
1199 if (d->fd != -1)
1200 ret = fchmod(d->fd, mode) == 0;
1201 else
1202 ret = ::chmod(d->nativeFilePath.constData(), mode) == 0;
1203 if (!ret)
1204 setError(QFile::PermissionsError, qt_error_string(errno));
1205 return ret;
1208 bool QFSFileEngine::setSize(qint64 size)
1210 Q_D(QFSFileEngine);
1211 bool ret = false;
1212 if (d->fd != -1)
1213 ret = QT_FTRUNCATE(d->fd, size) == 0;
1214 else if (d->fh)
1215 ret = QT_FTRUNCATE(QT_FILENO(d->fh), size) == 0;
1216 else
1217 ret = QT_TRUNCATE(d->nativeFilePath.constData(), size) == 0;
1218 if (!ret)
1219 setError(QFile::ResizeError, qt_error_string(errno));
1220 return ret;
1223 QDateTime QFSFileEngine::fileTime(FileTime time) const
1225 Q_D(const QFSFileEngine);
1226 QDateTime ret;
1227 if (d->doStat()) {
1228 if (time == CreationTime)
1229 ret.setTime_t(d->st.st_ctime ? d->st.st_ctime : d->st.st_mtime);
1230 else if (time == ModificationTime)
1231 ret.setTime_t(d->st.st_mtime);
1232 else if (time == AccessTime)
1233 ret.setTime_t(d->st.st_atime);
1235 return ret;
1238 uchar *QFSFileEnginePrivate::map(qint64 offset, qint64 size, QFile::MemoryMapFlags flags)
1240 Q_Q(QFSFileEngine);
1241 Q_UNUSED(flags);
1242 if (openMode == QIODevice::NotOpen) {
1243 q->setError(QFile::PermissionsError, qt_error_string(int(EACCES)));
1244 return 0;
1247 if (offset < 0 || offset != qint64(QT_OFF_T(offset))
1248 || size < 0 || size > qint64(size_t(-1))) {
1249 q->setError(QFile::UnspecifiedError, qt_error_string(int(EINVAL)));
1250 return 0;
1253 // If we know the mapping will extend beyond EOF, fail early to avoid
1254 // undefined behavior. Otherwise, let mmap have its say.
1255 if (doStat()
1256 && (QT_OFF_T(size) > st.st_size - QT_OFF_T(offset)))
1257 qWarning("QFSFileEngine::map: Mapping a file beyond its size is not portable");
1259 int access = 0;
1260 if (openMode & QIODevice::ReadOnly) access |= PROT_READ;
1261 if (openMode & QIODevice::WriteOnly) access |= PROT_WRITE;
1263 int pageSize = getpagesize();
1264 int extra = offset % pageSize;
1266 if (size + extra > (size_t)-1) {
1267 q->setError(QFile::UnspecifiedError, qt_error_string(int(EINVAL)));
1268 return 0;
1271 size_t realSize = (size_t)size + extra;
1272 QT_OFF_T realOffset = QT_OFF_T(offset);
1273 realOffset &= ~(QT_OFF_T(pageSize - 1));
1275 #ifdef Q_OS_SYMBIAN
1276 void *mapAddress;
1277 TRAPD(err, mapAddress = QT_MMAP((void*)0, realSize,
1278 access, MAP_SHARED, nativeHandle(), realOffset));
1279 if (err != KErrNone) {
1280 qWarning("OpenC bug: leave from mmap %d", err);
1281 mapAddress = MAP_FAILED;
1282 errno = EINVAL;
1284 #else
1285 void *mapAddress = QT_MMAP((void*)0, realSize,
1286 access, MAP_SHARED, nativeHandle(), realOffset);
1287 #endif
1288 if (MAP_FAILED != mapAddress) {
1289 uchar *address = extra + static_cast<uchar*>(mapAddress);
1290 maps[address] = QPair<int,size_t>(extra, realSize);
1291 return address;
1294 switch(errno) {
1295 case EBADF:
1296 q->setError(QFile::PermissionsError, qt_error_string(int(EACCES)));
1297 break;
1298 case ENFILE:
1299 case ENOMEM:
1300 q->setError(QFile::ResourceError, qt_error_string(int(errno)));
1301 break;
1302 case EINVAL:
1303 // size are out of bounds
1304 default:
1305 q->setError(QFile::UnspecifiedError, qt_error_string(int(errno)));
1306 break;
1308 return 0;
1311 bool QFSFileEnginePrivate::unmap(uchar *ptr)
1313 Q_Q(QFSFileEngine);
1314 if (!maps.contains(ptr)) {
1315 q->setError(QFile::PermissionsError, qt_error_string(EACCES));
1316 return false;
1319 uchar *start = ptr - maps[ptr].first;
1320 size_t len = maps[ptr].second;
1321 if (-1 == munmap(start, len)) {
1322 q->setError(QFile::UnspecifiedError, qt_error_string(errno));
1323 return false;
1325 maps.remove(ptr);
1326 return true;
1329 QT_END_NAMESPACE
1331 #endif // QT_NO_FSFILEENGINE