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