1 /****************************************************************************
3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the QtCore module of the Qt Toolkit.
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
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.
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
51 #include "qdatetime.h"
52 #include "qvarlengtharray.h"
57 #if defined(Q_OS_SYMBIAN)
58 # include <syslimits.h>
60 # include <pathinfo.h>
61 # include "private/qcore_symbian_p.h"
64 #if !defined(QWS) && defined(Q_OS_MAC)
65 # include <private/qcore_mac_p.h>
71 #if defined(Q_OS_SYMBIAN)
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('/')))));
89 Returns the stdlib open string corresponding to a QIODevice::OpenMode.
91 static inline QByteArray
openModeToFopenMode(QIODevice::OpenMode flags
, const QString
&fileName
)
94 if ((flags
& QIODevice::ReadOnly
) && !(flags
& QIODevice::Truncate
)) {
96 if (flags
& QIODevice::WriteOnly
) {
98 if (!fileName
.isEmpty()
99 && QT_STAT(QFile::encodeName(fileName
), &statBuf
) == 0
100 && (statBuf
.st_mode
& S_IFMT
) == S_IFREG
) {
106 } else if (flags
& QIODevice::WriteOnly
) {
108 if (flags
& QIODevice::ReadOnly
)
111 if (flags
& QIODevice::Append
) {
113 if (flags
& QIODevice::ReadOnly
)
117 #if defined(__GLIBC__) && (__GLIBC__ * 0x100 + __GLIBC_MINOR__) >= 0x0207
118 // must be glibc >= 2.7
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
;
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
;
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;
167 void QFSFileEnginePrivate::nativeInitFileName()
169 nativeFilePath
= QFile::encodeName(filePath
);
175 bool QFSFileEnginePrivate::nativeOpen(QIODevice::OpenMode openMode
)
179 if (openMode
& QIODevice::Unbuffered
) {
180 int flags
= openModeToOpenFlags(openMode
);
182 // Try to open the file in unbuffered mode.
184 fd
= QT_OPEN(nativeFilePath
.constData(), flags
, 0666);
185 } while (fd
== -1 && errno
== EINTR
);
187 // On failure, return and report the error.
189 q
->setError(errno
== EMFILE
? QFile::ResourceError
: QFile::OpenError
,
190 qt_error_string(errno
));
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.
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"));
207 // Seek to the end when in Append mode.
208 if (flags
& QFile::Append
) {
211 ret
= QT_LSEEK(fd
, 0, SEEK_END
);
212 } while (ret
== -1 && errno
== EINTR
);
215 q
->setError(errno
== EMFILE
? QFile::ResourceError
: QFile::OpenError
,
216 qt_error_string(int(errno
)));
223 QByteArray fopenMode
= openModeToFopenMode(openMode
, filePath
);
225 // Try to open the file in buffered mode.
227 fh
= QT_FOPEN(nativeFilePath
.constData(), fopenMode
.constData());
228 } while (!fh
&& errno
== EINTR
);
230 // On failure, return and report the error.
232 q
->setError(errno
== EMFILE
? QFile::ResourceError
: QFile::OpenError
,
233 qt_error_string(int(errno
)));
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.
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"));
250 setCloseOnExec(fileno(fh
)); // ignore failure
252 // Seek to the end when in Append mode.
253 if (openMode
& QIODevice::Append
) {
256 ret
= QT_FSEEK(fh
, 0, SEEK_END
);
257 } while (ret
== -1 && errno
== EINTR
);
260 q
->setError(errno
== EMFILE
? QFile::ResourceError
: QFile::OpenError
,
261 qt_error_string(int(errno
)));
269 closeFileHandle
= true;
276 bool QFSFileEnginePrivate::nativeClose()
285 bool QFSFileEnginePrivate::nativeFlush()
287 return fh
? flushFh() : fd
!= -1;
293 qint64
QFSFileEnginePrivate::nativeRead(char *data
, qint64 len
)
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
308 read
= fread(data
+ readBytes
, 1, size_t(len
- readBytes
), fh
);
309 } while (read
== 0 && !feof(fh
) && errno
== EINTR
);
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) {
325 readByte
= fgetc(fh
);
326 } while (readByte
== -1 && errno
== EINTR
);
327 if (readByte
!= -1) {
328 *data
= uchar(readByte
);
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
)));
348 return readFdFh(data
, len
);
354 qint64
QFSFileEnginePrivate::nativeReadLine(char *data
, qint64 maxlen
)
356 return readLineFdFh(data
, maxlen
);
362 qint64
QFSFileEnginePrivate::nativeWrite(const char *data
, qint64 len
)
364 return writeFdFh(data
, len
);
370 qint64
QFSFileEnginePrivate::nativePos() const
378 bool QFSFileEnginePrivate::nativeSeek(qint64 pos
)
380 return seekFdFh(pos
);
386 int QFSFileEnginePrivate::nativeHandle() const
388 return fh
? fileno(fh
) : fd
;
394 bool QFSFileEnginePrivate::nativeIsSequential() const
396 return isSequentialFdFh();
399 bool QFSFileEngine::remove()
402 bool ret
= unlink(d
->nativeFilePath
.constData()) == 0;
404 setError(QFile::RemoveError
, qt_error_string(errno
));
408 bool QFSFileEngine::copy(const QString
&newName
)
410 #if defined(Q_OS_SYMBIAN)
412 RFs rfs
= qt_s60GetRFs();
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
));
421 fm
= CFileMan::NewL(rfs
);
423 err
= rfile
.Open(rfs
, oldPtr
, EFileShareReadersOrWriters
);
424 if (err
== KErrNone
) {
425 err
= fm
->Copy(rfile
, newPtr
);
430 // ### Add error reporting on failure
431 return (err
== KErrNone
);
434 // ### Add copy code for Unix here
435 setError(QFile::UnspecifiedError
, QLatin1String("Not implemented!"));
440 bool QFSFileEngine::rename(const QString
&newName
)
443 bool ret
= ::rename(d
->nativeFilePath
.constData(), QFile::encodeName(newName
).constData()) == 0;
445 setError(QFile::RenameError
, qt_error_string(errno
));
449 bool QFSFileEngine::link(const QString
&newName
)
452 bool ret
= ::symlink(d
->nativeFilePath
.constData(), QFile::encodeName(newName
).constData()) == 0;
454 setError(QFile::RenameError
, qt_error_string(errno
));
458 qint64
QFSFileEnginePrivate::nativeSize() const
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
);
471 for(int oldslash
= -1, slash
=0; slash
!= -1; oldslash
= slash
) {
472 slash
= dirName
.indexOf(QDir::separator(), oldslash
+1);
474 if (oldslash
== dirName
.length())
476 slash
= dirName
.length();
479 QByteArray chunk
= QFile::encodeName(dirName
.left(slash
));
481 if (QT_STAT(chunk
, &st
) != -1) {
482 if ((st
.st_mode
& S_IFMT
) != S_IFDIR
)
484 } else if (QT_MKDIR(chunk
, 0777) != 0) {
491 #if defined(Q_OS_DARWIN) // Mac X doesn't support trailing /'s
492 if (dirName
.endsWith(QLatin1Char('/')))
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
);
506 for(int oldslash
= 0, slash
=dirName
.length(); slash
> 0; oldslash
= slash
) {
507 QByteArray chunk
= QFile::encodeName(dirName
.left(slash
));
509 if (QT_STAT(chunk
, &st
) != -1) {
510 if ((st
.st_mode
& S_IFMT
) != S_IFDIR
)
512 if (::rmdir(chunk
) != 0)
513 return oldslash
!= 0;
517 slash
= dirName
.lastIndexOf(QDir::separator(), oldslash
-1);
521 return ::rmdir(QFile::encodeName(dirName
)) == 0;
524 bool QFSFileEngine::caseSensitive() const
526 #if defined(Q_OS_SYMBIAN)
533 bool QFSFileEngine::setCurrentPath(const QString
&path
)
536 r
= QT_CHDIR(QFile::encodeName(path
));
540 QString
QFSFileEngine::currentPath(const QString
&)
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");
554 if (QT_STAT(".", &st
) == 0) {
555 #if defined(__GLIBC__) && !defined(PATH_MAX)
556 char *currentName
= ::get_current_dir_name();
558 result
= QFile::decodeName(QByteArray(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)
567 qWarning("QFSFileEngine::currentPath: getcwd() failed");
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);
578 # if defined(QT_DEBUG)
579 qWarning("QFSFileEngine::currentPath: stat(\".\") failed");
586 QString
QFSFileEngine::homePath()
588 #if defined(Q_OS_SYMBIAN)
589 QString home
= rootPath();
591 QString home
= QFile::decodeName(qgetenv("HOME"));
598 QString
QFSFileEngine::rootPath()
600 #if defined(Q_OS_SYMBIAN)
602 TFileName symbianPath
= PathInfo::PhoneMemoryRootPath();
603 return QDir::cleanPath(QDir::fromNativeSeparators(qt_TDesC2QString(symbianPath
)));
605 # warning No fallback implementation of QFSFileEngine::rootPath()
609 return QLatin1String("/");
613 QString
QFSFileEngine::tempPath()
615 #if defined(Q_OS_SYMBIAN)
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);
624 # warning No fallback implementation of QFSFileEngine::tempPath()
628 QString temp
= QFile::decodeName(qgetenv("TMPDIR"));
630 temp
= QLatin1String("/tmp/");
635 QFileInfoList
QFSFileEngine::drives()
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
++) {
647 driveName
[0] = 'A' + i
;
648 ret
.append(QFileInfo(QLatin1String(driveName
)));
652 qWarning("QFSFileEngine::drives: Getting drives failed");
655 ret
.append(QFileInfo(rootPath()));
660 bool QFSFileEnginePrivate::doStat() const
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);
673 could_stat
= (QT_FSTAT(fd
, &st
) == 0);
679 bool QFSFileEnginePrivate::isSymlink() const
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;
690 #if defined(Q_OS_SYMBIAN)
691 static bool _q_isSymbianHidden(const QString
&path
, bool isDir
)
693 RFs rfs
= qt_s60GetRFs();
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
));
701 TInt err
= rfs
.Att(ptr
, attributes
);
702 return (err
== KErrNone
&& (attributes
& KEntryAttHidden
));
706 #if !defined(QWS) && defined(Q_OS_MAC)
707 static bool _q_isMacHidden(const QString
&path
)
713 err
= FSPathMakeRefWithOptions(reinterpret_cast<const UInt8
*>(QFile::encodeName(QDir::cleanPath(path
)).constData()),
714 kFSPathMakeRefDoNotFollowLeafSymlink
, &fsRef
, 0);
718 FSCatalogInfo catInfo
;
719 err
= FSGetCatalogInfo(&fsRef
, kFSCatInfoFinderInfo
, &catInfo
, NULL
, NULL
, NULL
);
723 FileInfo
* const fileInfo
= reinterpret_cast<FileInfo
*>(&catInfo
.finderInfo
);
724 bool result
= (fileInfo
->finderFlags
& kIsInvisible
);
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
) {
741 QAbstractFileEngine::FileFlags ret
= 0;
742 if (type
& FlagsMask
)
743 ret
|= LocalDiskFlag
;
744 bool exists
= d
->doStat();
745 if (!exists
&& !d
->isSymlink())
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
)
755 if (d
->st
.st_mode
& S_IRUSR
)
757 if (d
->st
.st_mode
& S_IWUSR
)
758 ret
|= WriteUserPerm
;
759 if (d
->st
.st_mode
& S_IXUSR
)
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
)
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
)
774 if (type
& TypesMask
) {
775 #if !defined(QWS) && defined(Q_OS_MAC)
776 bool foundAlias
= false;
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
) {
791 if ((type
& LinkType
) && d
->isSymlink())
793 if (exists
&& (d
->st
.st_mode
& S_IFMT
) == S_IFREG
)
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
))
808 if (type
& FlagsMask
) {
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('/'))) {
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
))
824 if (d
->filePath
== QLatin1String("/")) {
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
)
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
);
849 int colon
= d
->filePath
.lastIndexOf(QLatin1Char(':'));
851 return d
->filePath
.mid(colon
+ 1);
854 return d
->filePath
.mid(slash
+ 1);
855 } else if(file
== PathName
) {
856 if(!d
->filePath
.size())
859 int slash
= d
->filePath
.lastIndexOf(slashChar
);
861 if(d
->filePath
.length() >= 2 && d
->filePath
.at(1) == QLatin1Char(':'))
862 return d
->filePath
.left(2);
863 return QLatin1String(".");
866 return QLatin1String("/");
867 if(slash
== 2 && d
->filePath
.length() >= 2 && d
->filePath
.at(1) == QLatin1Char(':'))
869 return d
->filePath
.left(slash
);
871 } else if(file
== AbsoluteName
|| file
== AbsolutePathName
) {
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);
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
;
890 ret
= QDir::currentPath() + slashChar
+ d
->filePath
;
893 // The path should be absolute at this point.
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();
907 bool isDir
= ret
.endsWith(slashChar
);
908 ret
= QDir::cleanPath(ret
);
909 if (isDir
&& !ret
.endsWith(slashChar
))
912 if (file
== AbsolutePathName
) {
913 int slash
= ret
.lastIndexOf(slashChar
);
916 else if (ret
.at(0) != slashChar
&& slash
== 2)
917 return ret
.left(3); // include the slash
919 return ret
.left(slash
> 0 ? slash
: 1);
922 } else if(file
== CanonicalName
|| file
== CanonicalPathName
) {
923 if (!(fileFlags(ExistsFlag
) & ExistsFlag
))
926 QString ret
= QFSFileEnginePrivate::canonicalized(fileName(AbsoluteName
));
927 if (file
== CanonicalPathName
&& !ret
.isEmpty()) {
928 int slash
= ret
.lastIndexOf(slashChar
);
930 ret
= QDir::fromNativeSeparators(QDir::currentPath());
932 ret
= QLatin1String("/");
933 ret
= ret
.left(slash
);
936 } else if(file
== LinkName
) {
937 if (d
->isSymlink()) {
939 int len
= readlink(d
->nativeFilePath
.constData(), s
, PATH_MAX
);
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
))
949 ret
.prepend(QDir::currentPath() + slashChar
);
952 ret
= QDir::cleanPath(ret
);
953 if (ret
.size() > 1 && ret
.endsWith(slashChar
))
959 } else if(file
== BundleName
) {
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
);
982 } else if (file
== BaseName
) {
983 int slash
= d
->filePath
.lastIndexOf(QLatin1Char('/'));
985 return d
->filePath
.mid(slash
+ 1);
986 } else if (file
== PathName
) {
987 int slash
= d
->filePath
.lastIndexOf(QLatin1Char('/'));
989 return QLatin1String(".");
991 return QLatin1String("/");
992 return d
->filePath
.left(slash
);
993 } else if (file
== AbsoluteName
|| file
== AbsolutePathName
) {
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('/');
1002 if (ret
== QLatin1String("/"))
1004 bool isDir
= ret
.endsWith(QLatin1Char('/'));
1005 ret
= QDir::cleanPath(ret
);
1007 ret
+= QLatin1Char('/');
1008 if (file
== AbsolutePathName
) {
1009 int slash
= ret
.lastIndexOf(QLatin1Char('/'));
1011 return QDir::currentPath();
1013 return QLatin1String("/");
1014 return ret
.left(slash
);
1017 } else if (file
== CanonicalName
|| file
== CanonicalPathName
) {
1018 if (!(fileFlags(ExistsFlag
) & ExistsFlag
))
1021 QString ret
= QFSFileEnginePrivate::canonicalized(fileName(AbsoluteName
));
1022 if (file
== CanonicalPathName
&& !ret
.isEmpty()) {
1023 int slash
= ret
.lastIndexOf(QLatin1Char('/'));
1025 ret
= QDir::currentPath();
1026 else if (slash
== 0)
1027 ret
= QLatin1String("/");
1028 ret
= ret
.left(slash
);
1031 } else if (file
== LinkName
) {
1032 if (d
->isSymlink()) {
1033 #if defined(__GLIBC__) && !defined(PATH_MAX)
1034 #define PATH_CHUNK_SIZE 256
1037 int size
= PATH_CHUNK_SIZE
;
1040 s
= q_check_ptr((char *) ::realloc(s
, size
));
1041 len
= ::readlink(d
->nativeFilePath
.constData(), s
, size
);
1053 int len
= readlink(d
->nativeFilePath
.constData(), s
, PATH_MAX
);
1057 if (d
->doStat() && S_ISDIR(d
->st
.st_mode
) && s
[0] != '/') {
1058 QDir
parent(d
->filePath
);
1060 ret
= parent
.path();
1061 if (!ret
.isEmpty() && !ret
.endsWith(QLatin1Char('/')))
1062 ret
+= QLatin1Char('/');
1065 ret
+= QFile::decodeName(QByteArray(s
));
1066 #if defined(__GLIBC__) && !defined(PATH_MAX)
1070 if (!ret
.startsWith(QLatin1Char('/'))) {
1071 if (d
->filePath
.startsWith(QLatin1Char('/'))) {
1072 ret
.prepend(d
->filePath
.left(d
->filePath
.lastIndexOf(QLatin1Char('/')))
1073 + QLatin1Char('/'));
1075 ret
.prepend(QDir::currentPath() + QLatin1Char('/'));
1078 ret
= QDir::cleanPath(ret
);
1079 if (ret
.size() > 1 && ret
.endsWith(QLatin1Char('/')))
1084 #if !defined(QWS) && defined(Q_OS_MAC)
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
) {
1091 if (FSNewAlias(0, &fref
, &alias
) == noErr
&& alias
) {
1093 if (FSCopyAliasInfo(alias
, 0, 0, &cfstr
, 0, 0) == noErr
)
1094 return QCFString::toQString(cfstr
);
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
);
1112 return d
->filePath
.length() ? d
->filePath
[0] != QLatin1Char('/') : true;
1116 uint
QFSFileEngine::ownerId(FileOwner own
) const
1118 Q_D(const QFSFileEngine
);
1119 static const uint nobodyID
= (uint
) -2;
1121 if (own
== OwnerUser
)
1122 return d
->st
.st_uid
;
1124 return d
->st
.st_gid
;
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
);
1135 QVarLengthArray
<char, 1024> buf(size_max
);
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
);
1144 pw
= getpwuid(ownerId(own
));
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
);
1155 buf
.resize(size_max
);
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
)
1162 // ERANGE indicates that the buffer was too small
1163 if (!getgrgid_r(ownerId(own
), &entry
, buf
.data(), buf
.size(), &gr
)
1168 gr
= getgrgid(ownerId(own
));
1171 return QFile::decodeName(QByteArray(gr
->gr_name
));
1177 bool QFSFileEngine::setPermissions(uint perms
)
1182 if (perms
& ReadOwnerPerm
)
1184 if (perms
& WriteOwnerPerm
)
1186 if (perms
& ExeOwnerPerm
)
1188 if (perms
& ReadUserPerm
)
1190 if (perms
& WriteUserPerm
)
1192 if (perms
& ExeUserPerm
)
1194 if (perms
& ReadGroupPerm
)
1196 if (perms
& WriteGroupPerm
)
1198 if (perms
& ExeGroupPerm
)
1200 if (perms
& ReadOtherPerm
)
1202 if (perms
& WriteOtherPerm
)
1204 if (perms
& ExeOtherPerm
)
1207 ret
= fchmod(d
->fd
, mode
) == 0;
1209 ret
= ::chmod(d
->nativeFilePath
.constData(), mode
) == 0;
1211 setError(QFile::PermissionsError
, qt_error_string(errno
));
1215 bool QFSFileEngine::setSize(qint64 size
)
1220 ret
= QT_FTRUNCATE(d
->fd
, size
) == 0;
1222 ret
= QT_FTRUNCATE(QT_FILENO(d
->fh
), size
) == 0;
1224 ret
= QT_TRUNCATE(d
->nativeFilePath
.constData(), size
) == 0;
1226 setError(QFile::ResizeError
, qt_error_string(errno
));
1230 QDateTime
QFSFileEngine::fileTime(FileTime time
) const
1232 Q_D(const QFSFileEngine
);
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
);
1245 uchar
*QFSFileEnginePrivate::map(qint64 offset
, qint64 size
, QFile::MemoryMapFlags flags
)
1249 if (openMode
== QIODevice::NotOpen
) {
1250 q
->setError(QFile::PermissionsError
, qt_error_string(int(EACCES
)));
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
)));
1260 // If we know the mapping will extend beyond EOF, fail early to avoid
1261 // undefined behavior. Otherwise, let mmap have its say.
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");
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
)));
1278 size_t realSize
= (size_t)size
+ extra
;
1279 QT_OFF_T realOffset
= QT_OFF_T(offset
);
1280 realOffset
&= ~(QT_OFF_T(pageSize
- 1));
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
;
1292 void *mapAddress
= QT_MMAP((void*)0, realSize
,
1293 access
, MAP_SHARED
, nativeHandle(), realOffset
);
1295 if (MAP_FAILED
!= mapAddress
) {
1296 uchar
*address
= extra
+ static_cast<uchar
*>(mapAddress
);
1297 maps
[address
] = QPair
<int,size_t>(extra
, realSize
);
1303 q
->setError(QFile::PermissionsError
, qt_error_string(int(EACCES
)));
1307 q
->setError(QFile::ResourceError
, qt_error_string(int(errno
)));
1310 // size are out of bounds
1312 q
->setError(QFile::UnspecifiedError
, qt_error_string(int(errno
)));
1318 bool QFSFileEnginePrivate::unmap(uchar
*ptr
)
1321 if (!maps
.contains(ptr
)) {
1322 q
->setError(QFile::PermissionsError
, qt_error_string(EACCES
));
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
));
1338 #endif // QT_NO_FSFILEENGINE