Merge remote-tracking branch 'upstream/tags/v4.8.0-rc1' into experimental
[qt:android-lighthouse.git] / src / corelib / io / qfsfileengine_unix.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2011 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 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 **
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 **
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
29 **
30 ** Other Usage
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qplatformdefs.h"
43 #include "qabstractfileengine.h"
44 #include "private/qfsfileengine_p.h"
45 #include "private/qcore_unix_p.h"
46 #include "qfilesystementry_p.h"
47 #include "qfilesystemengine_p.h"
48
49 #ifndef QT_NO_FSFILEENGINE
50
51 #include "qfile.h"
52 #include "qdir.h"
53 #include "qdatetime.h"
54 #include "qvarlengtharray.h"
55
56 #include <sys/mman.h>
57 #include <stdlib.h>
58 #include <limits.h>
59 #if defined(Q_OS_SYMBIAN)
60 # include <sys/syslimits.h>
61 # include <f32file.h>
62 # include <pathinfo.h>
63 # include "private/qcore_symbian_p.h"
64 #endif
65 #include <errno.h>
66 #if !defined(QWS) && defined(Q_OS_MAC)
67 # include <private/qcore_mac_p.h>
68 #endif
69
70 QT_BEGIN_NAMESPACE
71
72 #if defined(Q_OS_SYMBIAN)
73 /*!
74     \internal
75
76     Returns true if supplied path is a relative path
77 */
78 static bool isRelativePathSymbian(const QString& fileName)
79 {
80     return !(fileName.startsWith(QLatin1Char('/'))
81              || (fileName.length() >= 2
82              && ((fileName.at(0).isLetter() && fileName.at(1) == QLatin1Char(':'))
83              || (fileName.at(0) == QLatin1Char('/') && fileName.at(1) == QLatin1Char('/')))));
84 }
85
86 #endif
87
88 #ifndef Q_OS_SYMBIAN
89 /*!
90     \internal
91
92     Returns the stdlib open string corresponding to a QIODevice::OpenMode.
93 */
94 static inline QByteArray openModeToFopenMode(QIODevice::OpenMode flags, const QFileSystemEntry &fileEntry,
95         QFileSystemMetaData &metaData)
96 {
97     QByteArray mode;
98     if ((flags & QIODevice::ReadOnly) && !(flags & QIODevice::Truncate)) {
99         mode = "rb";
100         if (flags & QIODevice::WriteOnly) {
101             metaData.clearFlags(QFileSystemMetaData::FileType);
102             if (!fileEntry.isEmpty()
103                     && QFileSystemEngine::fillMetaData(fileEntry, metaData, QFileSystemMetaData::FileType)
104                     && metaData.isFile()) {
105                 mode += '+';
106             } else {
107                 mode = "wb+";
108             }
109         }
110     } else if (flags & QIODevice::WriteOnly) {
111         mode = "wb";
112         if (flags & QIODevice::ReadOnly)
113             mode += '+';
114     }
115     if (flags & QIODevice::Append) {
116         mode = "ab";
117         if (flags & QIODevice::ReadOnly)
118             mode += '+';
119     }
120
121 #if defined(__GLIBC__) && (__GLIBC__ * 0x100 + __GLIBC_MINOR__) >= 0x0207
122     // must be glibc >= 2.7
123     mode += 'e';
124 #endif
125
126     return mode;
127 }
128 #endif
129
130 /*!
131     \internal
132
133     Returns the stdio open flags corresponding to a QIODevice::OpenMode.
134 */
135 static inline int openModeToOpenFlags(QIODevice::OpenMode mode)
136 {
137     int oflags = QT_OPEN_RDONLY;
138 #ifdef QT_LARGEFILE_SUPPORT
139     oflags |= QT_OPEN_LARGEFILE;
140 #endif
141
142     if ((mode & QFile::ReadWrite) == QFile::ReadWrite) {
143         oflags = QT_OPEN_RDWR | QT_OPEN_CREAT;
144     } else if (mode & QFile::WriteOnly) {
145         oflags = QT_OPEN_WRONLY | QT_OPEN_CREAT;
146     }
147
148     if (mode & QFile::Append) {
149         oflags |= QT_OPEN_APPEND;
150     } else if (mode & QFile::WriteOnly) {
151         if ((mode & QFile::Truncate) || !(mode & QFile::ReadOnly))
152             oflags |= QT_OPEN_TRUNC;
153     }
154
155     return oflags;
156 }
157
158 #ifndef Q_OS_SYMBIAN
159 /*!
160     \internal
161
162     Sets the file descriptor to close on exec. That is, the file
163     descriptor is not inherited by child processes.
164 */
165 static inline bool setCloseOnExec(int fd)
166 {
167     return fd != -1 && fcntl(fd, F_SETFD, FD_CLOEXEC) != -1;
168 }
169 #endif
170
171 #ifdef Q_OS_SYMBIAN
172 /*!
173     \internal
174 */
175 bool QFSFileEnginePrivate::nativeOpen(QIODevice::OpenMode openMode)
176 {
177     Q_Q(QFSFileEngine);
178         
179         fh = 0;
180         fd = -1;
181
182     QString fn(QFileSystemEngine::absoluteName(fileEntry).nativeFilePath());
183     RFs& fs = qt_s60GetRFs();
184
185     TUint symbianMode = 0;
186
187     if(openMode & QIODevice::ReadOnly)
188         symbianMode |= EFileRead;
189     if(openMode & QIODevice::WriteOnly)
190         symbianMode |= EFileWrite;
191     if(openMode & QIODevice::Text)
192         symbianMode |= EFileStreamText;
193
194     // pre Symbian 9.4, file I/O is always unbuffered, and the enum values don't exist
195     if(QSysInfo::symbianVersion() >= QSysInfo::SV_9_4) {
196         if (openMode & QFile::Unbuffered) {
197             if (openMode & QIODevice::WriteOnly)
198                 symbianMode |= 0x00001000; //EFileWriteDirectIO;
199             // ### Unbuffered read is not used, because it prevents file open in /resource
200             // ### and has no obvious benefits
201         } else {
202             if (openMode & QIODevice::WriteOnly)
203                 symbianMode |= 0x00000800; //EFileWriteBuffered;
204             // use implementation defaults for read buffering
205         }
206     }
207
208     // Until Qt supports file sharing, we can't support EFileShareReadersOrWriters safely,
209     // but Qt does this on other platforms and autotests rely on it.
210     // The reason is that Unix locks are only advisory - the application needs to test the
211     // lock after opening the file. Symbian and Windows locks are mandatory - opening a
212     // locked file will fail.
213     symbianMode |= EFileShareReadersOrWriters;
214
215     TInt r;
216     //note QIODevice::Truncate only has meaning for read/write access
217     //write-only files are always truncated unless append is specified
218     //reference openModeToOpenFlags in qfsfileengine_unix.cpp
219     if ((openMode & QIODevice::Truncate) || (!(openMode & QIODevice::ReadOnly) && !(openMode & QIODevice::Append))) {
220         r = symbianFile.Replace(fs, qt_QString2TPtrC(fn), symbianMode);
221     } else {
222         r = symbianFile.Open(fs, qt_QString2TPtrC(fn), symbianMode);
223         if (r == KErrNotFound && (openMode & QIODevice::WriteOnly)) {
224             r = symbianFile.Create(fs, qt_QString2TPtrC(fn), symbianMode);
225         }
226     }
227
228     if (r == KErrNone) {
229 #ifdef SYMBIAN_ENABLE_64_BIT_FILE_SERVER_API
230         TInt64 size;
231 #else
232         TInt size;
233 #endif
234         r = symbianFile.Size(size);
235         if (r==KErrNone) {
236             if (openMode & QIODevice::Append)
237                 symbianFilePos = size;
238             else
239                 symbianFilePos = 0;
240             //TODO: port this (QFileSystemMetaData in open?)
241             //cachedSize = size;
242         }
243     }
244
245     if (r != KErrNone) {
246         q->setError(QFile::OpenError, QSystemError(r, QSystemError::NativeError).toString());
247         symbianFile.Close();
248         return false;
249     }
250
251     closeFileHandle = true;
252     return true;
253 }
254
255 /*!
256     Opens the file descriptor specified by \a file in the mode given by
257     \a openMode. Returns true on success; otherwise returns false.
258
259     The \a handleFlags argument specifies whether the file handle will be
260     closed by Qt. See the QFile::FileHandleFlags documentation for more
261     information.
262 */
263 bool QFSFileEngine::open(QIODevice::OpenMode openMode, const RFile &file, QFile::FileHandleFlags handleFlags)
264 {
265     Q_D(QFSFileEngine);
266
267     // Append implies WriteOnly.
268     if (openMode & QFile::Append)
269         openMode |= QFile::WriteOnly;
270
271     // WriteOnly implies Truncate if neither ReadOnly nor Append are sent.
272     if ((openMode & QFile::WriteOnly) && !(openMode & (QFile::ReadOnly | QFile::Append)))
273         openMode |= QFile::Truncate;
274
275     d->openMode = openMode;
276     d->lastFlushFailed = false;
277     d->closeFileHandle = (handleFlags & QFile::AutoCloseHandle);
278     d->fileEntry.clear();
279     d->fh = 0;
280     d->fd = -1;
281     d->tried_stat = 0;
282
283 #ifdef SYMBIAN_ENABLE_64_BIT_FILE_SERVER_API
284     //RFile64 adds only functions to RFile, no data members
285     d->symbianFile = static_cast<const RFile64&>(file);
286 #else
287     d->symbianFile = file;
288 #endif
289     TInt ret;
290     d->symbianFilePos = 0;
291     if (openMode & QFile::Append) {
292         // Seek to the end when in Append mode.
293         ret = d->symbianFile.Size(d->symbianFilePos);
294     } else {
295         // Seek to current otherwise
296         ret = d->symbianFile.Seek(ESeekCurrent, d->symbianFilePos);
297     }
298
299     if (ret != KErrNone) {
300         setError(QFile::OpenError, QSystemError(ret, QSystemError::NativeError).toString());
301
302         d->openMode = QIODevice::NotOpen;
303 #ifdef SYMBIAN_ENABLE_64_BIT_FILE_SERVER_API
304         d->symbianFile = RFile64();
305 #else
306         d->symbianFile = RFile();
307 #endif
308         return false;
309     }
310
311     // Extract filename (best effort)
312     TFileName fn;
313     TInt err = d->symbianFile.FullName(fn);
314     if (err == KErrNone)
315         d->fileEntry = QFileSystemEntry(qt_TDesC2QString(fn), QFileSystemEntry::FromNativePath());
316     else
317         d->fileEntry.clear();
318
319     return true;
320 }
321 #else
322 /*!
323     \internal
324 */
325 bool QFSFileEnginePrivate::nativeOpen(QIODevice::OpenMode openMode)
326 {
327     Q_Q(QFSFileEngine);
328
329     if (openMode & QIODevice::Unbuffered) {
330         int flags = openModeToOpenFlags(openMode);
331
332         // Try to open the file in unbuffered mode.
333         do {
334             fd = QT_OPEN(fileEntry.nativeFilePath().constData(), flags, 0666);
335         } while (fd == -1 && errno == EINTR);
336
337         // On failure, return and report the error.
338         if (fd == -1) {
339             q->setError(errno == EMFILE ? QFile::ResourceError : QFile::OpenError,
340                         qt_error_string(errno));
341             return false;
342         }
343
344         if (!(openMode & QIODevice::WriteOnly)) {
345             // we don't need this check if we tried to open for writing because then
346             // we had received EISDIR anyway.
347             if (QFileSystemEngine::fillMetaData(fd, metaData)
348                     && metaData.isDirectory()) {
349                 q->setError(QFile::OpenError, QLatin1String("file to open is a directory"));
350                 QT_CLOSE(fd);
351                 return false;
352             }
353         }
354
355         // Seek to the end when in Append mode.
356         if (flags & QFile::Append) {
357             int ret;
358             do {
359                 ret = QT_LSEEK(fd, 0, SEEK_END);
360             } while (ret == -1 && errno == EINTR);
361
362             if (ret == -1) {
363                 q->setError(errno == EMFILE ? QFile::ResourceError : QFile::OpenError,
364                             qt_error_string(int(errno)));
365                 return false;
366             }
367         }
368
369         fh = 0;
370     } else {
371         QByteArray fopenMode = openModeToFopenMode(openMode, fileEntry, metaData);
372
373         // Try to open the file in buffered mode.
374         do {
375             fh = QT_FOPEN(fileEntry.nativeFilePath().constData(), fopenMode.constData());
376         } while (!fh && errno == EINTR);
377
378         // On failure, return and report the error.
379         if (!fh) {
380             q->setError(errno == EMFILE ? QFile::ResourceError : QFile::OpenError,
381                         qt_error_string(int(errno)));
382             return false;
383         }
384
385         if (!(openMode & QIODevice::WriteOnly)) {
386             // we don't need this check if we tried to open for writing because then
387             // we had received EISDIR anyway.
388             if (QFileSystemEngine::fillMetaData(QT_FILENO(fh), metaData)
389                     && metaData.isDirectory()) {
390                 q->setError(QFile::OpenError, QLatin1String("file to open is a directory"));
391                 fclose(fh);
392                 return false;
393             }
394         }
395
396         setCloseOnExec(fileno(fh)); // ignore failure
397
398         // Seek to the end when in Append mode.
399         if (openMode & QIODevice::Append) {
400             int ret;
401             do {
402                 ret = QT_FSEEK(fh, 0, SEEK_END);
403             } while (ret == -1 && errno == EINTR);
404
405             if (ret == -1) {
406                 q->setError(errno == EMFILE ? QFile::ResourceError : QFile::OpenError,
407                             qt_error_string(int(errno)));
408                 return false;
409             }
410         }
411
412         fd = -1;
413     }
414
415     closeFileHandle = true;
416     return true;
417 }
418 #endif
419
420 /*!
421     \internal
422 */
423 bool QFSFileEnginePrivate::nativeClose()
424 {
425     return closeFdFh();
426 }
427
428 /*!
429     \internal
430
431 */
432 bool QFSFileEnginePrivate::nativeFlush()
433 {
434 #ifdef Q_OS_SYMBIAN
435     if (symbianFile.SubSessionHandle())
436         return (KErrNone == symbianFile.Flush());
437 #endif
438     return fh ? flushFh() : fd != -1;
439 }
440
441 /*!
442     \internal
443 */
444 qint64 QFSFileEnginePrivate::nativeRead(char *data, qint64 len)
445 {
446     Q_Q(QFSFileEngine);
447
448 #ifdef Q_OS_SYMBIAN
449     if (symbianFile.SubSessionHandle()) {
450         if(len > KMaxTInt) {
451             //this check is more likely to catch a corrupt length, since it isn't possible to allocate 2GB buffers (yet..)
452             q->setError(QFile::ReadError, QLatin1String("Maximum 2GB in single read on this platform"));
453             return -1;
454         }
455         TPtr8 ptr(reinterpret_cast<TUint8*>(data), static_cast<TInt>(len));
456         TInt r = symbianFile.Read(symbianFilePos, ptr);
457         if (r != KErrNone)
458         {
459             q->setError(QFile::ReadError, QSystemError(r, QSystemError::NativeError).toString());
460             return -1;
461         }
462         symbianFilePos += ptr.Length();
463         return qint64(ptr.Length());
464     }
465 #endif
466     if (fh && nativeIsSequential()) {
467         size_t readBytes = 0;
468         int oldFlags = fcntl(QT_FILENO(fh), F_GETFL);
469         for (int i = 0; i < 2; ++i) {
470             // Unix: Make the underlying file descriptor non-blocking
471             if ((oldFlags & O_NONBLOCK) == 0)
472                 fcntl(QT_FILENO(fh), F_SETFL, oldFlags | O_NONBLOCK);
473
474             // Cross platform stdlib read
475             size_t read = 0;
476             do {
477                 read = fread(data + readBytes, 1, size_t(len - readBytes), fh);
478             } while (read == 0 && !feof(fh) && errno == EINTR);
479             if (read > 0) {
480                 readBytes += read;
481                 break;
482             } else {
483                 if (readBytes)
484                     break;
485                 readBytes = read;
486             }
487
488             // Unix: Restore the blocking state of the underlying socket
489             if ((oldFlags & O_NONBLOCK) == 0) {
490                 fcntl(QT_FILENO(fh), F_SETFL, oldFlags);
491                 if (readBytes == 0) {
492                     int readByte = 0;
493                     do {
494                         readByte = fgetc(fh);
495                     } while (readByte == -1 && errno == EINTR);
496                     if (readByte != -1) {
497                         *data = uchar(readByte);
498                         readBytes += 1;
499                     } else {
500                         break;
501                     }
502                 }
503             }
504         }
505         // Unix: Restore the blocking state of the underlying socket
506         if ((oldFlags & O_NONBLOCK) == 0) {
507             fcntl(QT_FILENO(fh), F_SETFL, oldFlags);
508         }
509         if (readBytes == 0 && !feof(fh)) {
510             // if we didn't read anything and we're not at EOF, it must be an error
511             q->setError(QFile::ReadError, qt_error_string(int(errno)));
512             return -1;
513         }
514         return readBytes;
515     }
516
517     return readFdFh(data, len);
518 }
519
520 /*!
521     \internal
522 */
523 qint64 QFSFileEnginePrivate::nativeReadLine(char *data, qint64 maxlen)
524 {
525     return readLineFdFh(data, maxlen);
526 }
527
528 /*!
529     \internal
530 */
531 qint64 QFSFileEnginePrivate::nativeWrite(const char *data, qint64 len)
532 {
533 #ifdef Q_OS_SYMBIAN
534     Q_Q(QFSFileEngine);
535     if (symbianFile.SubSessionHandle()) {
536         if(len > KMaxTInt) {
537             //this check is more likely to catch a corrupt length, since it isn't possible to allocate 2GB buffers (yet..)
538             q->setError(QFile::WriteError, QLatin1String("Maximum 2GB in single write on this platform"));
539             return -1;
540         }
541         const TPtrC8 ptr(reinterpret_cast<const TUint8*>(data), static_cast<TInt>(len));
542 #ifdef SYMBIAN_ENABLE_64_BIT_FILE_SERVER_API
543         TInt64 eofpos = 0;
544 #else
545         TInt eofpos = 0;
546 #endif
547         //The end of file position is not cached because QFile is read/write sharable, therefore another
548         //process may have altered the file size.
549         TInt r = symbianFile.Seek(ESeekEnd, eofpos);
550         if (r == KErrNone && symbianFilePos > eofpos) {
551             //seek position is beyond end of file so file needs to be extended before write.
552             //note that SetSize does not zero-initialise (c.f. posix lseek)
553             r = symbianFile.SetSize(symbianFilePos);
554         }
555         if (r == KErrNone) {
556             //write to specific position in the file (i.e. use our own cursor rather than calling seek)
557             r = symbianFile.Write(symbianFilePos, ptr);
558         }
559         if (r != KErrNone) {
560             q->setError(QFile::WriteError, QSystemError(r, QSystemError::NativeError).toString());
561             return -1;
562         }
563         symbianFilePos += len;
564         return len;
565     }
566 #endif
567     return writeFdFh(data, len);
568 }
569
570 /*!
571     \internal
572 */
573 qint64 QFSFileEnginePrivate::nativePos() const
574 {
575 #ifdef Q_OS_SYMBIAN
576     const Q_Q(QFSFileEngine);
577     if (symbianFile.SubSessionHandle()) {
578         return symbianFilePos;
579     }
580 #endif
581     return posFdFh();
582 }
583
584 /*!
585     \internal
586 */
587 bool QFSFileEnginePrivate::nativeSeek(qint64 pos)
588 {
589 #ifdef Q_OS_SYMBIAN
590     Q_Q(QFSFileEngine);
591     if (symbianFile.SubSessionHandle()) {
592 #ifndef SYMBIAN_ENABLE_64_BIT_FILE_SERVER_API
593         if(pos > KMaxTInt) {
594             q->setError(QFile::PositionError, QLatin1String("Maximum 2GB file position on this platform"));
595             return false;
596         }
597 #endif
598         symbianFilePos = pos;
599         return true;
600     }
601 #endif
602     return seekFdFh(pos);
603 }
604
605 /*!
606     \internal
607 */
608 int QFSFileEnginePrivate::nativeHandle() const
609 {
610     return fh ? fileno(fh) : fd;
611 }
612
613 #if defined(Q_OS_SYMBIAN) && !defined(QT_SYMBIAN_USE_NATIVE_FILEMAP)
614 int QFSFileEnginePrivate::getMapHandle()
615 {
616     if (symbianFile.SubSessionHandle()) {
617         // Symbian file handle can't be used for open C mmap() so open the file with open C as well.
618         if (fileHandleForMaps < 0) {
619             int flags = openModeToOpenFlags(openMode);
620             flags &= ~(O_CREAT | O_TRUNC);
621             fileHandleForMaps = ::wopen((wchar_t*)(fileEntry.nativeFilePath().utf16()), flags, 0666);
622         }
623         return fileHandleForMaps;
624     }
625     return nativeHandle();
626 }
627 #endif
628
629 /*!
630     \internal
631 */
632 bool QFSFileEnginePrivate::nativeIsSequential() const
633 {
634 #ifdef Q_OS_SYMBIAN
635     if (symbianFile.SubSessionHandle())
636         return false;
637 #endif
638     return isSequentialFdFh();
639 }
640
641 bool QFSFileEngine::remove()
642 {
643     Q_D(QFSFileEngine);
644     QSystemError error;
645     bool ret = QFileSystemEngine::removeFile(d->fileEntry, error);
646     d->metaData.clear();
647     if (!ret) {
648         setError(QFile::RemoveError, error.toString());
649     }
650     return ret;
651 }
652
653 bool QFSFileEngine::copy(const QString &newName)
654 {
655     Q_D(QFSFileEngine);
656     QSystemError error;
657     bool ret = QFileSystemEngine::copyFile(d->fileEntry, QFileSystemEntry(newName), error);
658     if (!ret) {
659         setError(QFile::CopyError, error.toString());
660     }
661     return ret;
662 }
663
664 bool QFSFileEngine::rename(const QString &newName)
665 {
666     Q_D(QFSFileEngine);
667     QSystemError error;
668     bool ret = QFileSystemEngine::renameFile(d->fileEntry, QFileSystemEntry(newName), error);
669
670     if (!ret) {
671         setError(QFile::RenameError, error.toString());
672     }
673
674     return ret;
675 }
676
677 bool QFSFileEngine::link(const QString &newName)
678 {
679     Q_D(QFSFileEngine);
680     QSystemError error;
681     bool ret = QFileSystemEngine::createLink(d->fileEntry, QFileSystemEntry(newName), error);
682     if (!ret) {
683         setError(QFile::RenameError, error.toString());
684     }
685     return ret;
686 }
687
688 qint64 QFSFileEnginePrivate::nativeSize() const
689 {
690 #ifdef Q_OS_SYMBIAN
691     const Q_Q(QFSFileEngine);
692     if (symbianFile.SubSessionHandle()) {
693 #ifdef SYMBIAN_ENABLE_64_BIT_FILE_SERVER_API
694         qint64 size;
695 #else
696         TInt size;
697 #endif
698         TInt err = symbianFile.Size(size);
699         if(err != KErrNone) {
700             const_cast<QFSFileEngine*>(q)->setError(QFile::PositionError, QSystemError(err, QSystemError::NativeError).toString());
701             return 0;
702         }
703         return size;
704     }
705 #endif
706     return sizeFdFh();
707 }
708
709 bool QFSFileEngine::mkdir(const QString &name, bool createParentDirectories) const
710 {
711     return QFileSystemEngine::createDirectory(QFileSystemEntry(name), createParentDirectories);
712 }
713
714 bool QFSFileEngine::rmdir(const QString &name, bool recurseParentDirectories) const
715 {
716     return QFileSystemEngine::removeDirectory(QFileSystemEntry(name), recurseParentDirectories);
717 }
718
719 bool QFSFileEngine::caseSensitive() const
720 {
721 #if defined(Q_OS_SYMBIAN)
722     return false;
723 #else
724     return true;
725 #endif
726 }
727
728 bool QFSFileEngine::setCurrentPath(const QString &path)
729 {
730     return QFileSystemEngine::setCurrentPath(QFileSystemEntry(path));
731 }
732
733 QString QFSFileEngine::currentPath(const QString &)
734 {
735     return QFileSystemEngine::currentPath().filePath();
736 }
737
738 QString QFSFileEngine::homePath()
739 {
740     return QFileSystemEngine::homePath();
741 }
742
743 QString QFSFileEngine::rootPath()
744 {
745     return QFileSystemEngine::rootPath();
746 }
747
748 QString QFSFileEngine::tempPath()
749 {
750     return QFileSystemEngine::tempPath();
751 }
752
753 QFileInfoList QFSFileEngine::drives()
754 {
755     QFileInfoList ret;
756 #if defined(Q_OS_SYMBIAN)
757     TDriveList driveList;
758     RFs rfs = qt_s60GetRFs();
759     TInt err = rfs.DriveList(driveList);
760     if (err == KErrNone) {
761         char driveName[] = "A:/";
762
763         for (char i = 0; i < KMaxDrives; i++) {
764             if (driveList[i]) {
765                 driveName[0] = 'A' + i;
766                 ret.append(QFileInfo(QLatin1String(driveName)));
767             }
768         }
769     } else {
770         qWarning("QFSFileEngine::drives: Getting drives failed");
771     }
772 #else
773     ret.append(QFileInfo(rootPath()));
774 #endif
775     return ret;
776 }
777
778 bool QFSFileEnginePrivate::doStat(QFileSystemMetaData::MetaDataFlags flags) const
779 {
780     if (!tried_stat || !metaData.hasFlags(flags)) {
781         tried_stat = 1;
782
783         int localFd = fd;
784         if (fh && fileEntry.isEmpty())
785             localFd = QT_FILENO(fh);
786         if (localFd != -1)
787             QFileSystemEngine::fillMetaData(localFd, metaData);
788
789         if (metaData.missingFlags(flags) && !fileEntry.isEmpty())
790             QFileSystemEngine::fillMetaData(fileEntry, metaData, metaData.missingFlags(flags));
791     }
792
793     return metaData.exists();
794 }
795
796 bool QFSFileEnginePrivate::isSymlink() const
797 {
798     if (!metaData.hasFlags(QFileSystemMetaData::LinkType))
799         QFileSystemEngine::fillMetaData(fileEntry, metaData, QFileSystemMetaData::LinkType);
800
801     return metaData.isLink();
802 }
803
804 /*!
805     \reimp
806 */
807 QAbstractFileEngine::FileFlags QFSFileEngine::fileFlags(FileFlags type) const
808 {
809     Q_D(const QFSFileEngine);
810
811     if (type & Refresh)
812         d->metaData.clear();
813
814     QAbstractFileEngine::FileFlags ret = 0;
815
816     if (type & FlagsMask)
817         ret |= LocalDiskFlag;
818
819     bool exists;
820     {
821         QFileSystemMetaData::MetaDataFlags queryFlags = 0;
822
823         queryFlags |= QFileSystemMetaData::MetaDataFlags(uint(type))
824                 & QFileSystemMetaData::Permissions;
825
826         if (type & TypesMask)
827             queryFlags |= QFileSystemMetaData::AliasType
828                     | QFileSystemMetaData::LinkType
829                     | QFileSystemMetaData::FileType
830                     | QFileSystemMetaData::DirectoryType
831                     | QFileSystemMetaData::BundleType;
832
833         if (type & FlagsMask)
834             queryFlags |= QFileSystemMetaData::HiddenAttribute
835                     | QFileSystemMetaData::ExistsAttribute;
836
837         queryFlags |= QFileSystemMetaData::LinkType;
838
839         exists = d->doStat(queryFlags);
840     }
841
842     if (!exists && !d->metaData.isLink())
843         return ret;
844
845     if (exists && (type & PermsMask))
846         ret |= FileFlags(uint(d->metaData.permissions()));
847
848     if (type & TypesMask) {
849         if (d->metaData.isAlias()) {
850             ret |= LinkType;
851         } else {
852             if ((type & LinkType) && d->metaData.isLink())
853                 ret |= LinkType;
854             if (exists) {
855                 if (d->metaData.isFile()) {
856                     ret |= FileType;
857                 } else if (d->metaData.isDirectory()) {
858                     ret |= DirectoryType;
859                     if ((type & BundleType) && d->metaData.isBundle())
860                         ret |= BundleType;
861                 }
862             }
863         }
864     }
865
866     if (type & FlagsMask) {
867         if (exists)
868             ret |= ExistsFlag;
869         if (d->fileEntry.isRoot())
870             ret |= RootFlag;
871         else if (d->metaData.isHidden())
872             ret |= HiddenFlag;
873     }
874
875     return ret;
876 }
877
878 QString QFSFileEngine::fileName(FileName file) const
879 {
880     Q_D(const QFSFileEngine);
881     if (file == BundleName) {
882         return QFileSystemEngine::bundleName(d->fileEntry);
883     } else if (file == BaseName) {
884         return d->fileEntry.fileName();
885     } else if (file == PathName) {
886         return d->fileEntry.path();
887     } else if (file == AbsoluteName || file == AbsolutePathName) {
888         QFileSystemEntry entry(QFileSystemEngine::absoluteName(d->fileEntry));
889         if (file == AbsolutePathName) {
890             return entry.path();
891         }
892         return entry.filePath();
893     } else if (file == CanonicalName || file == CanonicalPathName) {
894         QFileSystemEntry entry(QFileSystemEngine::canonicalName(d->fileEntry, d->metaData));
895         if (file == CanonicalPathName)
896             return entry.path();
897         return entry.filePath();
898     } else if (file == LinkName) {
899         if (d->isSymlink()) {
900             QFileSystemEntry entry = QFileSystemEngine::getLinkTarget(d->fileEntry, d->metaData);
901             return entry.filePath();
902         }
903         return QString();
904     }
905     return d->fileEntry.filePath();
906 }
907
908 bool QFSFileEngine::isRelativePath() const
909 {
910     Q_D(const QFSFileEngine);
911 #if defined(Q_OS_SYMBIAN)
912     return isRelativePathSymbian(d->fileEntry.filePath());
913 #else
914     return d->fileEntry.filePath().length() ? d->fileEntry.filePath()[0] != QLatin1Char('/') : true;
915 #endif
916 }
917
918 uint QFSFileEngine::ownerId(FileOwner own) const
919 {
920     Q_D(const QFSFileEngine);
921     static const uint nobodyID = (uint) -2;
922
923     if (d->doStat(QFileSystemMetaData::OwnerIds))
924         return d->metaData.ownerId(own);
925
926     return nobodyID;
927 }
928
929 QString QFSFileEngine::owner(FileOwner own) const
930 {
931 #ifndef Q_OS_SYMBIAN
932     if (own == OwnerUser)
933         return QFileSystemEngine::resolveUserName(ownerId(own));
934     return QFileSystemEngine::resolveGroupName(ownerId(own));
935 #else
936     Q_UNUSED(own)
937     return QString();
938 #endif
939 }
940
941 bool QFSFileEngine::setPermissions(uint perms)
942 {
943     Q_D(QFSFileEngine);
944     QSystemError error;
945     if (!QFileSystemEngine::setPermissions(d->fileEntry, QFile::Permissions(perms), error, 0)) {
946         setError(QFile::PermissionsError, error.toString());
947         return false;
948     }
949     return true;
950 }
951
952 #ifdef Q_OS_SYMBIAN
953 bool QFSFileEngine::setSize(qint64 size)
954 {
955     Q_D(QFSFileEngine);
956     bool ret = false;
957     TInt err = KErrNone;
958     if (d->symbianFile.SubSessionHandle()) {
959         TInt err = d->symbianFile.SetSize(size);
960         ret = (err == KErrNone);
961         if (ret && d->symbianFilePos > size)
962             d->symbianFilePos = size;
963     }
964     else if (d->fd != -1)
965         ret = QT_FTRUNCATE(d->fd, size) == 0;
966     else if (d->fh)
967         ret = QT_FTRUNCATE(QT_FILENO(d->fh), size) == 0;
968     else {
969         RFile tmp;
970         QString symbianFilename(d->fileEntry.nativeFilePath());
971         err = tmp.Open(qt_s60GetRFs(), qt_QString2TPtrC(symbianFilename), EFileWrite);
972         if (err == KErrNone)
973         {
974             err = tmp.SetSize(size);
975             tmp.Close();
976         }
977         ret = (err == KErrNone);
978     }
979     if (!ret) {
980         QSystemError error;
981         if (err)
982             error = QSystemError(err, QSystemError::NativeError);
983         else
984             error = QSystemError(errno, QSystemError::StandardLibraryError);
985         setError(QFile::ResizeError, error.toString());
986     }
987     return ret;
988 }
989 #else
990 bool QFSFileEngine::setSize(qint64 size)
991 {
992     Q_D(QFSFileEngine);
993     bool ret = false;
994     if (d->fd != -1)
995         ret = QT_FTRUNCATE(d->fd, size) == 0;
996     else if (d->fh)
997         ret = QT_FTRUNCATE(QT_FILENO(d->fh), size) == 0;
998 #ifndef Q_OS_ANDROID
999     else
1000         ret = QT_TRUNCATE(d->fileEntry.nativeFilePath().constData(), size) == 0;
1001 #endif
1002     if (!ret)
1003         setError(QFile::ResizeError, qt_error_string(errno));
1004     return ret;
1005 }
1006 #endif
1007
1008 QDateTime QFSFileEngine::fileTime(FileTime time) const
1009 {
1010     Q_D(const QFSFileEngine);
1011
1012     if (d->doStat(QFileSystemMetaData::Times))
1013         return d->metaData.fileTime(time);
1014
1015     return QDateTime();
1016 }
1017
1018 uchar *QFSFileEnginePrivate::map(qint64 offset, qint64 size, QFile::MemoryMapFlags flags)
1019 {
1020     Q_Q(QFSFileEngine);
1021     Q_UNUSED(flags);
1022     if (openMode == QIODevice::NotOpen) {
1023         q->setError(QFile::PermissionsError, qt_error_string(int(EACCES)));
1024         return 0;
1025     }
1026
1027     if (offset < 0 || offset != qint64(QT_OFF_T(offset))
1028             || size < 0 || quint64(size) > quint64(size_t(-1))) {
1029         q->setError(QFile::UnspecifiedError, qt_error_string(int(EINVAL)));
1030         return 0;
1031     }
1032
1033     // If we know the mapping will extend beyond EOF, fail early to avoid
1034     // undefined behavior. Otherwise, let mmap have its say.
1035     if (doStat(QFileSystemMetaData::SizeAttribute)
1036             && (QT_OFF_T(size) > metaData.size() - QT_OFF_T(offset)))
1037         qWarning("QFSFileEngine::map: Mapping a file beyond its size is not portable");
1038
1039     int access = 0;
1040     if (openMode & QIODevice::ReadOnly) access |= PROT_READ;
1041     if (openMode & QIODevice::WriteOnly) access |= PROT_WRITE;
1042
1043 #if defined(Q_OS_INTEGRITY)
1044     int pageSize = sysconf(_SC_PAGESIZE);
1045 #else
1046     int pageSize = getpagesize();
1047 #endif
1048     int extra = offset % pageSize;
1049
1050     if (quint64(size + extra) > quint64((size_t)-1)) {
1051         q->setError(QFile::UnspecifiedError, qt_error_string(int(EINVAL)));
1052         return 0;
1053     }
1054
1055     size_t realSize = (size_t)size + extra;
1056     QT_OFF_T realOffset = QT_OFF_T(offset);
1057     realOffset &= ~(QT_OFF_T(pageSize - 1));
1058
1059 #ifdef QT_SYMBIAN_USE_NATIVE_FILEMAP
1060     TInt nativeMapError = KErrNone;
1061     RFileMap mapping;
1062     TUint mode(EFileMapRemovableMedia);
1063     //If the file was opened for write or read/write, then open the map for read/write
1064     if (openMode & QIODevice::WriteOnly)
1065         mode |= EFileMapWrite;
1066     if (symbianFile.SubSessionHandle()) {
1067         nativeMapError = mapping.Open(symbianFile, offset, size, mode);
1068     } else {
1069         //map file by name if we don't have a native handle
1070         QString fn = QFileSystemEngine::absoluteName(fileEntry).nativeFilePath();
1071         TUint filemode = EFileShareReadersOrWriters | EFileRead;
1072         if (openMode & QIODevice::WriteOnly)
1073             filemode |= EFileWrite;
1074         nativeMapError = mapping.Open(qt_s60GetRFs(), qt_QString2TPtrC(fn), filemode, offset, size, mode);
1075     }
1076     if (nativeMapError == KErrNone) {
1077         QScopedResource<RFileMap> ptr(mapping); //will call Close if adding to mapping throws an exception
1078         uchar *address = mapping.Base();
1079         maps[address] = mapping;
1080         ptr.take();
1081         return address;
1082     }
1083     QFile::FileError reportedError = QFile::UnspecifiedError;
1084     switch (nativeMapError) {
1085     case KErrAccessDenied:
1086     case KErrPermissionDenied:
1087         reportedError = QFile::PermissionsError;
1088         break;
1089     case KErrNoMemory:
1090         reportedError = QFile::ResourceError;
1091         break;
1092     }
1093     q->setError(reportedError, QSystemError(nativeMapError, QSystemError::NativeError).toString());
1094     return 0;
1095 #else
1096 #ifdef Q_OS_SYMBIAN
1097     //older phones & emulator don't support native mapping, so need to keep the open C way around for those.
1098     void *mapAddress;
1099     TRAPD(err, mapAddress = QT_MMAP((void*)0, realSize,
1100                    access, MAP_SHARED, getMapHandle(), realOffset));
1101     if (err != KErrNone) {
1102         qWarning("OpenC bug: leave from mmap %d", err);
1103         mapAddress = MAP_FAILED;
1104         errno = EINVAL;
1105     }
1106 #else
1107     void *mapAddress = QT_MMAP((void*)0, realSize,
1108                    access, MAP_SHARED, nativeHandle(), realOffset);
1109 #endif
1110     if (MAP_FAILED != mapAddress) {
1111         uchar *address = extra + static_cast<uchar*>(mapAddress);
1112         maps[address] = QPair<int,size_t>(extra, realSize);
1113         return address;
1114     }
1115
1116     switch(errno) {
1117     case EBADF:
1118         q->setError(QFile::PermissionsError, qt_error_string(int(EACCES)));
1119         break;
1120     case ENFILE:
1121     case ENOMEM:
1122         q->setError(QFile::ResourceError, qt_error_string(int(errno)));
1123         break;
1124     case EINVAL:
1125         // size are out of bounds
1126     default:
1127         q->setError(QFile::UnspecifiedError, qt_error_string(int(errno)));
1128         break;
1129     }
1130     return 0;
1131 #endif
1132 }
1133
1134 bool QFSFileEnginePrivate::unmap(uchar *ptr)
1135 {
1136 #if !defined(Q_OS_INTEGRITY)
1137     Q_Q(QFSFileEngine);
1138     if (!maps.contains(ptr)) {
1139         q->setError(QFile::PermissionsError, qt_error_string(EACCES));
1140         return false;
1141     }
1142
1143 #ifdef QT_SYMBIAN_USE_NATIVE_FILEMAP
1144     RFileMap mapping = maps.value(ptr);
1145     TInt err = mapping.Flush();
1146     mapping.Close();
1147     maps.remove(ptr);
1148     if (err) {
1149         q->setError(QFile::WriteError, QSystemError(err, QSystemError::NativeError).toString());
1150         return false;
1151     }
1152     return true;
1153 #else
1154     uchar *start = ptr - maps[ptr].first;
1155     size_t len = maps[ptr].second;
1156     if (-1 == munmap(start, len)) {
1157         q->setError(QFile::UnspecifiedError, qt_error_string(errno));
1158         return false;
1159     }
1160     maps.remove(ptr);
1161     return true;
1162 #endif
1163 #else
1164     return false;
1165 #endif
1166 }
1167
1168 QT_END_NAMESPACE
1169
1170 #endif // QT_NO_FSFILEENGINE