1
/****************************************************************************
2
**
3
** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4
** All rights reserved.
5
** Contact: Nokia Corporation (qt-info@nokia.com)
6
**
7
** This file is part of the QtCore module of the Qt Toolkit.
8
**
9
** $QT_BEGIN_LICENSE:LGPL$
10
** No Commercial Usage
11
** This file contains pre-release code and may not be distributed.
12
** You may use this file in accordance with the terms and conditions
13
** contained in the Technology Preview License Agreement accompanying
14
** this package.
15
**
16
** GNU Lesser General Public License Usage
17
** Alternatively, this file may be used under the terms of the GNU Lesser
18
** General Public License version 2.1 as published by the Free Software
19
** Foundation and appearing in the file LICENSE.LGPL included in the
20
** packaging of this file.  Please review the following information to
21
** ensure the GNU Lesser General Public License version 2.1 requirements
22
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23
**
24
** In addition, as a special exception, Nokia gives you certain additional
25
** rights.  These rights are described in the Nokia Qt LGPL Exception
26
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27
**
28
** If you have questions regarding the use of this file, please contact
29
** Nokia at qt-info@nokia.com.
30
**
31
**
32
**
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
46
#ifndef QT_NO_FSFILEENGINE
47
48
#ifndef QT_NO_REGEXP
49
# include "qregexp.h"
50
#endif
51
#include "qfile.h"
52
#include "qdir.h"
53
#include "qdatetime.h"
54
#include "qdebug.h"
55
#include "qvarlengtharray.h"
56
57
#include <sys/mman.h>
58
#include <stdlib.h>
59
#include <limits.h>
60
#include <errno.h>
61
#if !defined(QWS) && defined(Q_OS_MAC)
62
# include <private/qcore_mac_p.h>
63
#endif
64
65
QT_BEGIN_NAMESPACE
66
67
/*!
68
    \internal
69
70
    Returns the stdlib open string corresponding to a QIODevice::OpenMode.
71
*/
72
static QByteArray openModeToFopenMode(QIODevice::OpenMode flags, const QString &fileName = QString())
73
{
74
    QByteArray mode;
75
    if ((flags & QIODevice::ReadOnly) && !(flags & QIODevice::Truncate)) {
76
        mode = "rb";
77
        if (flags & QIODevice::WriteOnly) {
78
            if (!fileName.isEmpty() &&QFile::exists(fileName))
79
                mode = "rb+";
80
            else
81
                mode = "wb+";
82
        }
83
    } else if (flags & QIODevice::WriteOnly) {
84
        mode = "wb";
85
        if (flags & QIODevice::ReadOnly)
86
            mode += "+";
87
    }
88
    if (flags & QIODevice::Append) {
89
        mode = "ab";
90
        if (flags & QIODevice::ReadOnly)
91
            mode += "+";
92
    }
93
    return mode;
94
}
95
96
/*!
97
    \internal
98
99
    Returns the stdio open flags corresponding to a QIODevice::OpenMode.
100
*/
101
static int openModeToOpenFlags(QIODevice::OpenMode mode)
102
{
103
    int oflags = QT_OPEN_RDONLY;
104
#ifdef QT_LARGEFILE_SUPPORT
105
    oflags |= QT_OPEN_LARGEFILE;
106
#endif
107
108
    if ((mode & QFile::ReadWrite) == QFile::ReadWrite) {
109
        oflags = QT_OPEN_RDWR | QT_OPEN_CREAT;
110
    } else if (mode & QFile::WriteOnly) {
111
        oflags = QT_OPEN_WRONLY | QT_OPEN_CREAT;
112
    }
113
114
    if (mode & QFile::Append) {
115
        oflags |= QT_OPEN_APPEND;
116
    } else if (mode & QFile::WriteOnly) {
117
        if ((mode & QFile::Truncate) || !(mode & QFile::ReadOnly))
118
            oflags |= QT_OPEN_TRUNC;
119
    }
120
121
#ifdef O_CLOEXEC
122
    // supported on Linux >= 2.6.23; avoids one extra system call
123
    // and avoids a race condition: if another thread forks, we could
124
    // end up leaking a file descriptor...
125
    oflags |= O_CLOEXEC;
126
#endif
127
    return oflags;
128
}
129
130
/*!
131
    \internal
132
133
    Sets the file descriptor to close on exec. That is, the file
134
    descriptor is not inherited by child processes.
135
*/
136
static bool setCloseOnExec(int fd)
137
{
138
    return fd != -1 && fcntl(fd, F_SETFD, FD_CLOEXEC) != -1;
139
}
140
141
/*!
142
    \internal
143
*/
144
void QFSFileEnginePrivate::nativeInitFileName()
145
{
146
    nativeFilePath = QFile::encodeName(filePath);
147
}
148
149
/*!
150
    \internal
151
*/
152
bool QFSFileEnginePrivate::nativeOpen(QIODevice::OpenMode openMode)
153
{
154
    Q_Q(QFSFileEngine);
155
156
    if (openMode & QIODevice::Unbuffered) {
157
        int flags = openModeToOpenFlags(openMode);
158
159
        // Try to open the file in unbuffered mode.
160
        do {
161
            fd = QT_OPEN(nativeFilePath.constData(), flags, 0666);
162
        } while (fd == -1 && errno == EINTR);
163
164
        // On failure, return and report the error.
165
        if (fd == -1) {
166
            q->setError(errno == EMFILE ? QFile::ResourceError : QFile::OpenError,
167
                        qt_error_string(errno));
168
            return false;
169
        }
170
171
        QT_STATBUF statBuf;
172
        if (QT_FSTAT(fd, &statBuf) != -1) {
173
            if ((statBuf.st_mode & S_IFMT) == S_IFDIR) {
174
                q->setError(QFile::OpenError, QLatin1String("file to open is a directory"));
175
                QT_CLOSE(fd);
176
                return false;
177
            }
178
        }
179
180
#ifndef O_CLOEXEC
181
        // not needed on Linux >= 2.6.23
182
        setCloseOnExec(fd);     // ignore failure
183
#endif
184
185
        // Seek to the end when in Append mode.
186
        if (flags & QFile::Append) {
187
            int ret;
188
            do {
189
                ret = QT_LSEEK(fd, 0, SEEK_END);
190
            } while (ret == -1 && errno == EINTR);
191
192
            if (ret == -1) {
193
                q->setError(errno == EMFILE ? QFile::ResourceError : QFile::OpenError,
194
                            qt_error_string(int(errno)));
195
                return false;
196
            }
197
        }
198
199
        fh = 0;
200
    } else {
201
        QByteArray fopenMode = openModeToFopenMode(openMode, filePath);
202
203
        // Try to open the file in buffered mode.
204
        do {
205
            fh = QT_FOPEN(nativeFilePath.constData(), fopenMode.constData());
206
        } while (!fh && errno == EINTR);
207
208
        // On failure, return and report the error.
209
        if (!fh) {
210
            q->setError(errno == EMFILE ? QFile::ResourceError : QFile::OpenError,
211
                        qt_error_string(int(errno)));
212
            return false;
213
        }
214
215
        QT_STATBUF statBuf;
216
        if (QT_FSTAT(fileno(fh), &statBuf) != -1) {
217
            if ((statBuf.st_mode & S_IFMT) == S_IFDIR) {
218
                q->setError(QFile::OpenError, QLatin1String("file to open is a directory"));
219
                fclose(fh);
220
                return false;
221
            }
222
        }
223
224
        setCloseOnExec(fileno(fh)); // ignore failure
225
226
        // Seek to the end when in Append mode.
227
        if (openMode & QIODevice::Append) {
228
            int ret;
229
            do {
230
                ret = QT_FSEEK(fh, 0, SEEK_END);
231
            } while (ret == -1 && errno == EINTR);
232
233
            if (ret == -1) {
234
                q->setError(errno == EMFILE ? QFile::ResourceError : QFile::OpenError,
235
                            qt_error_string(int(errno)));
236
                return false;
237
            }
238
        }
239
240
        fd = -1;
241
    }
242
243
    closeFileHandle = true;
244
    return true;
245
}
246
247
/*!
248
    \internal
249
*/
250
bool QFSFileEnginePrivate::nativeClose()
251
{
252
    return closeFdFh();
253
}
254
255
/*!
256
    \internal
257
258
*/
259
bool QFSFileEnginePrivate::nativeFlush()
260
{
261
    return fh ? flushFh() : fd != -1;
262
}
263
264
/*!
265
    \internal
266
*/
267
qint64 QFSFileEnginePrivate::nativeRead(char *data, qint64 len)
268
{
269
    Q_Q(QFSFileEngine);
270
271
    if (fh && nativeIsSequential()) {
272
        size_t readBytes = 0;
273
        int oldFlags = fcntl(QT_FILENO(fh), F_GETFL);
274
        for (int i = 0; i < 2; ++i) {
275
            // Unix: Make the underlying file descriptor non-blocking
276
            if ((oldFlags & O_NONBLOCK) == 0)
277
                fcntl(QT_FILENO(fh), F_SETFL, oldFlags | O_NONBLOCK);
278
279
            // Cross platform stdlib read
280
            size_t read = 0;
281
            do {
282
                read = fread(data + readBytes, 1, size_t(len - readBytes), fh);
283
            } while (read == 0 && !feof(fh) && errno == EINTR);
284
            if (read > 0) {
285
                readBytes += read;
286
                break;
287
            } else {
288
                if (readBytes)
289
                    break;
290
                readBytes = read;
291
            }
292
293
            // Unix: Restore the blocking state of the underlying socket
294
            if ((oldFlags & O_NONBLOCK) == 0) {
295
                fcntl(QT_FILENO(fh), F_SETFL, oldFlags);
296
                if (readBytes == 0) {
297
                    int readByte = 0;
298
                    do {
299
                        readByte = fgetc(fh);
300
                    } while (readByte == -1 && errno == EINTR);
301
                    if (readByte != -1) {
302
                        *data = uchar(readByte);
303
                        readBytes += 1;
304
                    } else {
305
                        break;
306
                    }
307
                }
308
            }
309
        }
310
        // Unix: Restore the blocking state of the underlying socket
311
        if ((oldFlags & O_NONBLOCK) == 0) {
312
            fcntl(QT_FILENO(fh), F_SETFL, oldFlags);
313
        }
314
        if (readBytes == 0 && !feof(fh)) {
315
            // if we didn't read anything and we're not at EOF, it must be an error
316
            q->setError(QFile::ReadError, qt_error_string(int(errno)));
317
            return -1;
318
        }
319
        return readBytes;
320
    }
321
322
    return readFdFh(data, len);
323
}
324
325
/*!
326
    \internal
327
*/
328
qint64 QFSFileEnginePrivate::nativeReadLine(char *data, qint64 maxlen)
329
{
330
    return readLineFdFh(data, maxlen);
331
}
332
333
/*!
334
    \internal
335
*/
336
qint64 QFSFileEnginePrivate::nativeWrite(const char *data, qint64 len)
337
{
338
    return writeFdFh(data, len);
339
}
340
341
/*!
342
    \internal
343
*/
344
qint64 QFSFileEnginePrivate::nativePos() const
345
{
346
    return posFdFh();
347
}
348
349
/*!
350
    \internal
351
*/
352
bool QFSFileEnginePrivate::nativeSeek(qint64 pos)
353
{
354
    return seekFdFh(pos);
355
}
356
357
/*!
358
    \internal
359
*/
360
int QFSFileEnginePrivate::nativeHandle() const
361
{
362
    return fh ? fileno(fh) : fd;
363
}
364
365
/*!
366
    \internal
367
*/
368
bool QFSFileEnginePrivate::nativeIsSequential() const
369
{
370
    return isSequentialFdFh();
371
}
372
373
bool QFSFileEngine::remove()
374
{
375
    Q_D(QFSFileEngine);
376
    return unlink(d->nativeFilePath.constData()) == 0;
377
}
378
379
bool QFSFileEngine::copy(const QString &)
380
{
381
    // ### Add copy code for Unix here
382
    return false;
383
}
384
385
bool QFSFileEngine::rename(const QString &newName)
386
{
387
    Q_D(QFSFileEngine);
388
    return ::rename(d->nativeFilePath.constData(), QFile::encodeName(newName).constData()) == 0;
389
}
390
391
bool QFSFileEngine::link(const QString &newName)
392
{
393
    Q_D(QFSFileEngine);
394
    return ::symlink(d->nativeFilePath.constData(), QFile::encodeName(newName).constData()) == 0;
395
}
396
397
qint64 QFSFileEnginePrivate::nativeSize() const
398
{
399
    return sizeFdFh();
400
}
401
402
bool QFSFileEngine::mkdir(const QString &name, bool createParentDirectories) const
403
{
404
    QString dirName = name;
405
    if (createParentDirectories) {
406
        dirName = QDir::cleanPath(dirName);
407
        for(int oldslash = -1, slash=0; slash != -1; oldslash = slash) {
408
            slash = dirName.indexOf(QDir::separator(), oldslash+1);
409
            if (slash == -1) {
410
                if (oldslash == dirName.length())
411
                    break;
412
                slash = dirName.length();
413
            }
414
            if (slash) {
415
                QByteArray chunk = QFile::encodeName(dirName.left(slash));
416
                QT_STATBUF st;
417
                if (QT_STAT(chunk, &st) != -1) {
418
                    if ((st.st_mode & S_IFMT) != S_IFDIR)
419
                        return false;
420
                } else if (::mkdir(chunk, 0777) != 0) {
421
                        return false;
422
                }
423
            }
424
        }
425
        return true;
426
    }
427
#if defined(Q_OS_DARWIN)  // Mac X doesn't support trailing /'s
428
    if (dirName[dirName.length() - 1] == QLatin1Char('/'))
429
        dirName = dirName.left(dirName.length() - 1);
430
#endif
431
    return (::mkdir(QFile::encodeName(dirName), 0777) == 0);
432
}
433
434
bool QFSFileEngine::rmdir(const QString &name, bool recurseParentDirectories) const
435
{
436
    QString dirName = name;
437
    if (recurseParentDirectories) {
438
        dirName = QDir::cleanPath(dirName);
439
        for(int oldslash = 0, slash=dirName.length(); slash > 0; oldslash = slash) {
440
            QByteArray chunk = QFile::encodeName(dirName.left(slash));
441
            QT_STATBUF st;
442
            if (QT_STAT(chunk, &st) != -1) {
443
                if ((st.st_mode & S_IFMT) != S_IFDIR)
444
                    return false;
445
                if (::rmdir(chunk) != 0)
446
                    return oldslash != 0;
447
            } else {
448
                return false;
449
            }
450
            slash = dirName.lastIndexOf(QDir::separator(), oldslash-1);
451
        }
452
        return true;
453
    }
454
    return ::rmdir(QFile::encodeName(dirName)) == 0;
455
}
456
457
bool QFSFileEngine::caseSensitive() const
458
{
459
    return true;
460
}
461
462
bool QFSFileEngine::setCurrentPath(const QString &path)
463
{
464
    int r;
465
    r = ::chdir(QFile::encodeName(path));
466
    return r >= 0;
467
}
468
469
QString QFSFileEngine::currentPath(const QString &)
470
{
471
    QString result;
472
    QT_STATBUF st;
473
    if (QT_STAT(".", &st) == 0) {
474
#if defined(__GLIBC__) && !defined(PATH_MAX)
475
        char *currentName = ::get_current_dir_name();
476
        if (currentName) {
477
            result = QFile::decodeName(QByteArray(currentName));
478
            ::free(currentName);
479
        }
480
#else
481
        char currentName[PATH_MAX+1];
482
        if (::getcwd(currentName, PATH_MAX))
483
            result = QFile::decodeName(QByteArray(currentName));
484
#endif
485
#if defined(QT_DEBUG)
486
        if (result.isNull())
487
            qWarning("QDir::currentPath: getcwd() failed");
488
#endif
489
    } else {
490
#if defined(QT_DEBUG)
491
        qWarning("QDir::currentPath: stat(\".\") failed");
492
#endif
493
    }
494
    return result;
495
}
496
497
QString QFSFileEngine::homePath()
498
{
499
    QString home = QFile::decodeName(qgetenv("HOME"));
500
    if (home.isNull())
501
        home = rootPath();
502
    return home;
503
}
504
505
QString QFSFileEngine::rootPath()
506
{
507
    return QString::fromLatin1("/");
508
}
509
510
QString QFSFileEngine::tempPath()
511
{
512
    QString temp = QFile::decodeName(qgetenv("TMPDIR"));
513
    if (temp.isEmpty())
514
        temp = QString::fromLatin1("/tmp/");
515
    return temp;
516
}
517
518
QFileInfoList QFSFileEngine::drives()
519
{
520
    QFileInfoList ret;
521
    ret.append(rootPath());
522
    return ret;
523
}
524
525
bool QFSFileEnginePrivate::doStat() const
526
{
527
    if (tried_stat == 0) {
528
        QFSFileEnginePrivate *that = const_cast<QFSFileEnginePrivate*>(this);
529
        if (fh && nativeFilePath.isEmpty()) {
530
            // ### actually covers two cases: d->fh and when the file is not open
531
            that->could_stat = (QT_FSTAT(fileno(fh), &st) == 0);
532
        } else if (fd == -1) {
533
            // ### actually covers two cases: d->fh and when the file is not open
534
            that->could_stat = (QT_STAT(nativeFilePath.constData(), &st) == 0);
535
        } else {
536
            that->could_stat = (QT_FSTAT(fd, &st) == 0);
537
        }
538
	that->tried_stat = 1;
539
    }
540
    return could_stat;
541
}
542
543
bool QFSFileEnginePrivate::isSymlink() const
544
{
545
    if (need_lstat) {
546
        QFSFileEnginePrivate *that = const_cast<QFSFileEnginePrivate *>(this);
547
        that->need_lstat = false;
548
        QT_STATBUF st;          // don't clobber our main one
549
        that->is_link = (QT_LSTAT(nativeFilePath.constData(), &st) == 0) ? S_ISLNK(st.st_mode) : false;
550
    }
551
    return is_link;
552
}
553
554
#if !defined(QWS) && defined(Q_OS_MAC)
555
static bool _q_isMacHidden(const QString &path)
556
{
557
    OSErr err = noErr;
558
559
    FSRef fsRef;
560
561
#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4)
562
    if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_4) {
563
        err = FSPathMakeRefWithOptions(reinterpret_cast<const UInt8 *>(QFile::encodeName(QDir::cleanPath(path)).constData()),
564
                                        kFSPathMakeRefDoNotFollowLeafSymlink, &fsRef, 0);
565
    } else
566
#endif
567
    {
568
        QFileInfo fi(path);
569
        FSRef parentRef;
570
        err = FSPathMakeRef(reinterpret_cast<const UInt8 *>(fi.absoluteDir().absolutePath().toUtf8().constData()),
571
                            &parentRef, 0);
572
        if (err == noErr) {
573
            QString fileName = fi.fileName();
574
            err = FSMakeFSRefUnicode(&parentRef, fileName.length(),
575
                                     reinterpret_cast<const UniChar *>(fileName.unicode()),
576
                                     kTextEncodingUnknown, &fsRef);
577
        }
578
    }
579
    if (err != noErr)
580
        return false;
581
582
    FSCatalogInfo catInfo;
583
    err = FSGetCatalogInfo(&fsRef, kFSCatInfoFinderInfo, &catInfo, NULL, NULL, NULL);
584
    if (err != noErr)
585
        return false;
586
587
    FileInfo * const fileInfo = reinterpret_cast<FileInfo*>(&catInfo.finderInfo);
588
    bool result = (fileInfo->finderFlags & kIsInvisible);
589
    return result;
590
}
591
#endif
592
593
/*!
594
    \reimp
595
*/
596
QAbstractFileEngine::FileFlags QFSFileEngine::fileFlags(FileFlags type) const
597
{
598
    Q_D(const QFSFileEngine);
599
    // Force a stat, so that we're guaranteed to get up-to-date results
600
    if (type & QAbstractFileEngine::FileFlag(QAbstractFileEngine::Refresh)) {
601
        d->tried_stat = 0;
602
        d->need_lstat = 1;
603
    }
604
605
    QAbstractFileEngine::FileFlags ret = 0;
606
    bool exists = d->doStat();
607
    if (!exists && !d->isSymlink())
608
        return ret;
609
610
    if (exists && (type & PermsMask)) {
611
        if (d->st.st_mode & S_IRUSR)
612
            ret |= ReadOwnerPerm;
613
        if (d->st.st_mode & S_IWUSR)
614
            ret |= WriteOwnerPerm;
615
        if (d->st.st_mode & S_IXUSR)
616
            ret |= ExeOwnerPerm;
617
        if (d->st.st_mode & S_IRUSR)
618
            ret |= ReadUserPerm;
619
        if (d->st.st_mode & S_IWUSR)
620
            ret |= WriteUserPerm;
621
        if (d->st.st_mode & S_IXUSR)
622
            ret |= ExeUserPerm;
623
        if (d->st.st_mode & S_IRGRP)
624
            ret |= ReadGroupPerm;
625
        if (d->st.st_mode & S_IWGRP)
626
            ret |= WriteGroupPerm;
627
        if (d->st.st_mode & S_IXGRP)
628
            ret |= ExeGroupPerm;
629
        if (d->st.st_mode & S_IROTH)
630
            ret |= ReadOtherPerm;
631
        if (d->st.st_mode & S_IWOTH)
632
            ret |= WriteOtherPerm;
633
        if (d->st.st_mode & S_IXOTH)
634
            ret |= ExeOtherPerm;
635
    }
636
    if (type & TypesMask) {
637
#if !defined(QWS) && defined(Q_OS_MAC)
638
        bool foundAlias = false;
639
        {
640
            FSRef fref;
641
            if (FSPathMakeRef((const UInt8 *)QFile::encodeName(QDir::cleanPath(d->filePath)).data(),
642
                             &fref, NULL) == noErr) {
643
                Boolean isAlias, isFolder;
644
                if (FSIsAliasFile(&fref, &isAlias, &isFolder) == noErr && isAlias) {
645
                    foundAlias = true;
646
                    ret |= LinkType;
647
                }
648
            }
649
        }
650
        if (!foundAlias)
651
#endif
652
        {
653
            if ((type & LinkType) && d->isSymlink())
654
                ret |= LinkType;
655
            if (exists && (d->st.st_mode & S_IFMT) == S_IFREG)
656
                ret |= FileType;
657
            else if (exists && (d->st.st_mode & S_IFMT) == S_IFDIR)
658
                ret |= DirectoryType;
659
#if !defined(QWS) && defined(Q_OS_MAC)
660
            if((ret & DirectoryType) && (type & BundleType)) {
661
                QCFType<CFURLRef> url = CFURLCreateWithFileSystemPath(0, QCFString(d->filePath),
662
                                                                      kCFURLPOSIXPathStyle, true);
663
                UInt32 type, creator;
664
                if(CFBundleGetPackageInfoInDirectory(url, &type, &creator))
665
                    ret |= BundleType;
666
            }
667
#endif
668
        }
669
    }
670
    if (type & FlagsMask) {
671
        ret |= LocalDiskFlag;
672
        if (exists)
673
            ret |= ExistsFlag;
674
        if (fileName(BaseName)[0] == QLatin1Char('.')
675
#if !defined(QWS) && defined(Q_OS_MAC)
676
            || _q_isMacHidden(d->filePath)
677
#endif
678
        )
679
            ret |= HiddenFlag;
680
        if (d->filePath == QLatin1String("/"))
681
            ret |= RootFlag;
682
    }
683
    return ret;
684
}
685
686
QString QFSFileEngine::fileName(FileName file) const
687
{
688
    Q_D(const QFSFileEngine);
689
    if (file == BundleName) {
690
#if !defined(QWS) && defined(Q_OS_MAC)
691
        QCFType<CFURLRef> url = CFURLCreateWithFileSystemPath(0, QCFString(d->filePath),
692
                                                              kCFURLPOSIXPathStyle, true);
693
        if(CFDictionaryRef dict = CFBundleCopyInfoDictionaryForURL(url)) {
694
            if(CFTypeRef name = (CFTypeRef)CFDictionaryGetValue(dict, kCFBundleNameKey)) {
695
                if(CFGetTypeID(name) == CFStringGetTypeID())
696
                    return QCFString::toQString((CFStringRef)name);
697
            }
698
        }
699
#endif
700
        return QString();
701
    } else if (file == BaseName) {
702
        int slash = d->filePath.lastIndexOf(QLatin1Char('/'));
703
        if (slash != -1)
704
            return d->filePath.mid(slash + 1);
705
    } else if (file == PathName) {
706
        int slash = d->filePath.lastIndexOf(QLatin1Char('/'));
707
        if (slash == -1)
708
            return QLatin1String(".");
709
        else if (!slash)
710
            return QLatin1String("/");
711
        return d->filePath.left(slash);
712
    } else if (file == AbsoluteName || file == AbsolutePathName) {
713
        QString ret;
714
        if (d->filePath.isEmpty() || !d->filePath.startsWith(QLatin1Char('/')))
715
            ret = QDir::currentPath();
716
        if (!d->filePath.isEmpty() && d->filePath != QLatin1String(".")) {
717
            if (!ret.isEmpty() && !ret.endsWith(QLatin1Char('/')))
718
                ret += QLatin1Char('/');
719
            ret += d->filePath;
720
        }
721
        if (ret == QLatin1String("/"))
722
            return ret;
723
        bool isDir = ret.endsWith(QLatin1Char('/'));
724
        ret = QDir::cleanPath(ret);
725
        if (isDir)
726
            ret += QLatin1String("/");
727
        if (file == AbsolutePathName) {
728
            int slash = ret.lastIndexOf(QLatin1Char('/'));
729
            if (slash == -1)
730
                return QDir::currentPath();
731
            else if (!slash)
732
                return QLatin1String("/");
733
            return ret.left(slash);
734
        }
735
        return ret;
736
    } else if (file == CanonicalName || file == CanonicalPathName) {
737
        if (!(fileFlags(ExistsFlag) & ExistsFlag))
738
            return QString();
739
740
        QString ret = QFSFileEnginePrivate::canonicalized(fileName(AbsoluteName));
741
        if (!ret.isEmpty() && file == CanonicalPathName) {
742
            int slash = ret.lastIndexOf(QLatin1Char('/'));
743
            if (slash == -1)
744
                ret = QDir::currentPath();
745
            else if (slash == 0)
746
                ret = QLatin1String("/");
747
            ret = ret.left(slash);
748
        }
749
        return ret;
750
    } else if (file == LinkName) {
751
        if (d->isSymlink()) {
752
#if defined(__GLIBC__) && !defined(PATH_MAX)
753
#define PATH_CHUNK_SIZE 256
754
            char *s = 0;
755
            int len = -1;
756
            int size = PATH_CHUNK_SIZE;
757
758
            while (1) {
759
                s = (char *) ::realloc(s, size);
760
                if (s == 0) {
761
                    len = -1;
762
                    break;
763
                }
764
                len = ::readlink(d->nativeFilePath.constData(), s, size);
765
                if (len < 0) {
766
                    ::free(s);
767
                    break;
768
                }
769
                if (len < size) {
770
                    break;
771
                }
772
                size *= 2;
773
            }
774
#else
775
            char s[PATH_MAX+1];
776
            int len = readlink(d->nativeFilePath.constData(), s, PATH_MAX);
777
#endif
778
            if (len > 0) {
779
                QString ret;
780
                if (d->doStat() && S_ISDIR(d->st.st_mode) && s[0] != '/') {
781
                    QDir parent(d->filePath);
782
                    parent.cdUp();
783
                    ret = parent.path();
784
                    if (!ret.isEmpty() && !ret.endsWith(QLatin1Char('/')))
785
                        ret += QLatin1Char('/');
786
                }
787
                s[len] = '\0';
788
                ret += QFile::decodeName(QByteArray(s));
789
#if defined(__GLIBC__) && !defined(PATH_MAX)
790
		::free(s);
791
#endif
792
793
                if (!ret.startsWith(QLatin1Char('/'))) {
794
                    if (d->filePath.startsWith(QLatin1Char('/'))) {
795
                        ret.prepend(d->filePath.left(d->filePath.lastIndexOf(QLatin1Char('/')))
796
                                    + QLatin1Char('/'));
797
                    } else {
798
                        ret.prepend(QDir::currentPath() + QLatin1Char('/'));
799
                    }
800
                }
801
                ret = QDir::cleanPath(ret);
802
                if (ret.size() > 1 && ret.endsWith(QLatin1Char('/')))
803
                    ret.chop(1);
804
                return ret;
805
            }
806
        }
807
#if !defined(QWS) && defined(Q_OS_MAC)
808
        {
809
            FSRef fref;
810
            if (FSPathMakeRef((const UInt8 *)QFile::encodeName(QDir::cleanPath(d->filePath)).data(), &fref, 0) == noErr) {
811
                Boolean isAlias, isFolder;
812
                if (FSResolveAliasFile(&fref, true, &isFolder, &isAlias) == noErr && isAlias) {
813
                    AliasHandle alias;
814
                    if (FSNewAlias(0, &fref, &alias) == noErr && alias) {
815
                        CFStringRef cfstr;
816
                        if (FSCopyAliasInfo(alias, 0, 0, &cfstr, 0, 0) == noErr)
817
                            return QCFString::toQString(cfstr);
818
                    }
819
                }
820
            }
821
        }
822
#endif
823
        return QString();
824
    }
825
    return d->filePath;
826
}
827
828
bool QFSFileEngine::isRelativePath() const
829
{
830
    Q_D(const QFSFileEngine);
831
    int len = d->filePath.length();
832
    if (len == 0)
833
        return true;
834
    return d->filePath[0] != QLatin1Char('/');
835
}
836
837
uint QFSFileEngine::ownerId(FileOwner own) const
838
{
839
    Q_D(const QFSFileEngine);
840
    static const uint nobodyID = (uint) -2;
841
    if (d->doStat()) {
842
        if (own == OwnerUser)
843
            return d->st.st_uid;
844
        else
845
            return d->st.st_gid;
846
    }
847
    return nobodyID;
848
}
849
850
QString QFSFileEngine::owner(FileOwner own) const
851
{
852
#if !defined(QT_NO_THREAD) && defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_OPENBSD)
853
    int size_max = sysconf(_SC_GETPW_R_SIZE_MAX);
854
    if (size_max == -1)
855
        size_max = 1024;
856
    QVarLengthArray<char, 1024> buf(size_max);
857
#endif
858
859
    if (own == OwnerUser) {
860
        struct passwd *pw = 0;
861
#if !defined(QT_NO_THREAD) && defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_OPENBSD)
862
        struct passwd entry;
863
        getpwuid_r(ownerId(own), &entry, buf.data(), buf.size(), &pw);
864
#else
865
        pw = getpwuid(ownerId(own));
866
#endif
867
        if (pw)
868
            return QFile::decodeName(QByteArray(pw->pw_name));
869
    } else if (own == OwnerGroup) {
870
        struct group *gr = 0;
871
#if !defined(QT_NO_THREAD) && defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_OPENBSD)
872
        size_max = sysconf(_SC_GETGR_R_SIZE_MAX);
873
        if (size_max == -1)
874
            size_max = 1024;
875
        buf.resize(size_max);
876
        struct group entry;
877
        // Some large systems have more members than the POSIX max size
878
        // Loop over by doubling the buffer size (upper limit 250k)
879
        for (unsigned size = size_max; size < 256000; size += size)
880
        {
881
            buf.resize(size);
882
            // ERANGE indicates that the buffer was too small
883
            if (!getgrgid_r(ownerId(own), &entry, buf.data(), buf.size(), &gr)
884
                || errno != ERANGE)
885
                break;
886
        }
887
888
#else
889
        gr = getgrgid(ownerId(own));
890
#endif
891
        if (gr)
892
            return QFile::decodeName(QByteArray(gr->gr_name));
893
    }
894
    return QString();
895
}
896
897
bool QFSFileEngine::setPermissions(uint perms)
898
{
899
    Q_D(QFSFileEngine);
900
    mode_t mode = 0;
901
    if (perms & ReadOwnerPerm)
902
        mode |= S_IRUSR;
903
    if (perms & WriteOwnerPerm)
904
        mode |= S_IWUSR;
905
    if (perms & ExeOwnerPerm)
906
        mode |= S_IXUSR;
907
    if (perms & ReadUserPerm)
908
        mode |= S_IRUSR;
909
    if (perms & WriteUserPerm)
910
        mode |= S_IWUSR;
911
    if (perms & ExeUserPerm)
912
        mode |= S_IXUSR;
913
    if (perms & ReadGroupPerm)
914
        mode |= S_IRGRP;
915
    if (perms & WriteGroupPerm)
916
        mode |= S_IWGRP;
917
    if (perms & ExeGroupPerm)
918
        mode |= S_IXGRP;
919
    if (perms & ReadOtherPerm)
920
        mode |= S_IROTH;
921
    if (perms & WriteOtherPerm)
922
        mode |= S_IWOTH;
923
    if (perms & ExeOtherPerm)
924
        mode |= S_IXOTH;
925
    if (d->fd != -1)
926
        return !fchmod(d->fd, mode);
927
    return !::chmod(d->nativeFilePath.constData(), mode);
928
}
929
930
bool QFSFileEngine::setSize(qint64 size)
931
{
932
    Q_D(QFSFileEngine);
933
    if (d->fd != -1)
934
        return !QT_FTRUNCATE(d->fd, size);
935
    return !QT_TRUNCATE(d->nativeFilePath.constData(), size);
936
}
937
938
QDateTime QFSFileEngine::fileTime(FileTime time) const
939
{
940
    Q_D(const QFSFileEngine);
941
    QDateTime ret;
942
    if (d->doStat()) {
943
        if (time == CreationTime)
944
            ret.setTime_t(d->st.st_ctime ? d->st.st_ctime : d->st.st_mtime);
945
        else if (time == ModificationTime)
946
            ret.setTime_t(d->st.st_mtime);
947
        else if (time == AccessTime)
948
            ret.setTime_t(d->st.st_atime);
949
    }
950
    return ret;
951
}
952
953
uchar *QFSFileEnginePrivate::map(qint64 offset, qint64 size, QFile::MemoryMapFlags flags)
954
{
955
    Q_Q(QFSFileEngine);
956
    Q_UNUSED(flags);
957
    if (offset < 0) {
958
        q->setError(QFile::UnspecifiedError, qt_error_string(int(EINVAL)));
959
        return 0;
960
    }
961
    if (openMode == QIODevice::NotOpen) {
962
        q->setError(QFile::PermissionsError, qt_error_string(int(EACCES)));
963
        return 0;
964
    }
965
    int access = 0;
966
    if (openMode & QIODevice::ReadOnly) access |= PROT_READ;
967
    if (openMode & QIODevice::WriteOnly) access |= PROT_WRITE;
968
969
    int pagesSize = getpagesize();
970
    int realOffset = offset / pagesSize;
971
    int extra = offset % pagesSize;
972
973
    void *mapAddress = mmap((void*)0, (size_t)size + extra,
974
                   access, MAP_SHARED, nativeHandle(), realOffset * pagesSize);
975
    if (MAP_FAILED != mapAddress) {
976
        uchar *address = extra + static_cast<uchar*>(mapAddress);
977
        maps[address] = QPair<int,int>(extra, size);
978
        return address;
979
    }
980
981
    switch(errno) {
982
    case EBADF:
983
        q->setError(QFile::PermissionsError, qt_error_string(int(EACCES)));
984
        break;
985
    case ENFILE:
986
    case ENOMEM:
987
        q->setError(QFile::ResourceError, qt_error_string(int(errno)));
988
        break;
989
    case EINVAL:
990
        // size are out of bounds
991
    default:
992
        q->setError(QFile::UnspecifiedError, qt_error_string(int(errno)));
993
        break;
994
    }
995
    return 0;
996
}
997
998
bool QFSFileEnginePrivate::unmap(uchar *ptr)
999
{
1000
    Q_Q(QFSFileEngine);
1001
    if (!maps.contains(ptr)) {
1002
        q->setError(QFile::PermissionsError, qt_error_string(EACCES));
1003
        return false;
1004
    }
1005
1006
    uchar *start = ptr - maps[ptr].first;
1007
    int len = maps[ptr].second;
1008
    if (-1 == munmap(start, len)) {
1009
        q->setError(QFile::UnspecifiedError, qt_error_string(errno));
1010
        return false;
1011
    }
1012
    maps.remove(ptr);
1013
    return true;
1014
}
1015
1016
QT_END_NAMESPACE
1017
1018
#endif // QT_NO_FSFILEENGINE