diff options
-rw-r--r-- | src/corelib/io/qsavefile.cpp | 37 | ||||
-rw-r--r-- | tests/auto/corelib/io/qsavefile/tst_qsavefile.cpp | 60 |
2 files changed, 92 insertions, 5 deletions
diff --git a/src/corelib/io/qsavefile.cpp b/src/corelib/io/qsavefile.cpp index 3f7ce8d9e9..63f2284ef5 100644 --- a/src/corelib/io/qsavefile.cpp +++ b/src/corelib/io/qsavefile.cpp @@ -231,6 +231,37 @@ bool QSaveFile::open(OpenMode mode) d->finalFileName = existingFile.filePath(); } + auto openDirectly = [&]() { + d->fileEngine = QAbstractFileEngine::create(d->finalFileName); + if (d->fileEngine->open(mode | QIODevice::Unbuffered)) { + d->useTemporaryFile = false; + QFileDevice::open(mode); + return true; + } + return false; + }; + +#ifdef Q_OS_WIN + // check if it is an Alternate Data Stream + if (d->finalFileName == d->fileName && d->fileName.indexOf(QLatin1Char(':'), 2) > 1) { + // yes, we can't rename onto it... + if (d->directWriteFallback) { + if (openDirectly()) + return true; + d->setError(d->fileEngine->error(), d->fileEngine->errorString()); + delete d->fileEngine; + d->fileEngine = 0; + } else { + QString msg = + QSaveFile::tr("QSaveFile cannot open '%1' without direct write fallback " + "enabled: path contains an Alternate Data Stream specifier") + .arg(QDir::toNativeSeparators(d->fileName)); + d->setError(QFileDevice::OpenError, msg); + } + return false; + } +#endif + d->fileEngine = new QTemporaryFileEngine(&d->finalFileName); // if the target file exists, we'll copy its permissions below, // but until then, let's ensure the temporary file is not accessible @@ -243,12 +274,8 @@ bool QSaveFile::open(OpenMode mode) #ifdef Q_OS_UNIX if (d->directWriteFallback && err == QFileDevice::OpenError && errno == EACCES) { delete d->fileEngine; - d->fileEngine = QAbstractFileEngine::create(d->finalFileName); - if (d->fileEngine->open(mode | QIODevice::Unbuffered)) { - d->useTemporaryFile = false; - QFileDevice::open(mode); + if (openDirectly()) return true; - } err = d->fileEngine->error(); } #endif diff --git a/tests/auto/corelib/io/qsavefile/tst_qsavefile.cpp b/tests/auto/corelib/io/qsavefile/tst_qsavefile.cpp index 9573f3078b..96970421d3 100644 --- a/tests/auto/corelib/io/qsavefile/tst_qsavefile.cpp +++ b/tests/auto/corelib/io/qsavefile/tst_qsavefile.cpp @@ -82,6 +82,11 @@ private slots: void transactionalWriteErrorRenaming(); void symlink(); void directory(); + +#ifdef Q_OS_WIN + void alternateDataStream_data(); + void alternateDataStream(); +#endif }; static inline QByteArray msgCannotOpen(const QFileDevice &f) @@ -474,5 +479,60 @@ void tst_QSaveFile::directory() #endif } +#ifdef Q_OS_WIN +void tst_QSaveFile::alternateDataStream_data() +{ + QTest::addColumn<bool>("directWriteFallback"); + QTest::addColumn<bool>("success"); + + QTest::newRow("default") << false << false; + QTest::newRow("directWriteFallback") << true << true; +} + +void tst_QSaveFile::alternateDataStream() +{ + QFETCH(bool, directWriteFallback); + QFETCH(bool, success); + static const char newContent[] = "New content\r\n"; + + QTemporaryDir dir; + QVERIFY2(dir.isValid(), qPrintable(dir.errorString())); + QString baseName = dir.path() + QLatin1String("/base"); + { + QFile baseFile(baseName); + QVERIFY2(baseFile.open(QIODevice::ReadWrite), qPrintable(baseFile.errorString())); + } + + // First, create a file with old content + QString adsName = baseName + QLatin1String(":outfile"); + { + QFile targetFile(adsName); + if (!targetFile.open(QIODevice::ReadWrite)) + QSKIP("Failed to ceate ADS file (" + targetFile.errorString().toUtf8() + + "). Temp dir is FAT?"); + targetFile.write("Old content\r\n"); + } + + // And write to it again using QSaveFile; only works if directWriteFallback is enabled + QSaveFile file(adsName); + file.setDirectWriteFallback(directWriteFallback); + QCOMPARE(file.directWriteFallback(), directWriteFallback); + + if (success) { + QVERIFY2(file.open(QIODevice::WriteOnly), qPrintable(file.errorString())); + file.write(newContent); + QVERIFY2(file.commit(), qPrintable(file.errorString())); + + // check the contents + QFile targetFile(adsName); + QVERIFY2(targetFile.open(QIODevice::ReadOnly), qPrintable(targetFile.errorString())); + QByteArray contents = targetFile.readAll(); + QCOMPARE(contents, QByteArray(newContent)); + } else { + QVERIFY(!file.open(QIODevice::WriteOnly)); + } +} +#endif + QTEST_MAIN(tst_QSaveFile) #include "tst_qsavefile.moc" |