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