QFile: Add open() overload that accepts permissions argument

The new overload allows creation of files with non-default permissions.
This is useful when files need to be created with more restrictive
permissions than the default ones, and removes the time window when
such files are available with less restrictive permissions.

[ChangeLog][QtCore][QFile] Added QDir::open() overload that
accepts permissions argument.

Fixes: QTBUG-79750
Change-Id: Iddfced3c324e03f2c53f421c9b31c76dee82df58
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
This commit is contained in:
Ievgenii Meshcheriakov 2021-10-22 13:52:27 +02:00
parent 56e13acf4e
commit 83f2f27bb6
3 changed files with 101 additions and 1 deletions

View File

@ -886,7 +886,9 @@ QFile::copy(const QString &fileName, const QString &newName)
\note In \l{QIODevice::}{WriteOnly} or \l{QIODevice::}{ReadWrite}
mode, if the relevant file does not already exist, this function
will try to create a new file before opening it.
will try to create a new file before opening it. The file will be
created with mode 0666 masked by the umask on POSIX systems, and
with permissions inherited from the parent directory on Windows.
\sa QIODevice::OpenMode, setFileName()
*/
@ -918,6 +920,51 @@ bool QFile::open(OpenMode mode)
return false;
}
/*!
\overload
If the file does not exist and \a mode implies creating it, it is created
with the specified \a permissions.
On POSIX systems the actual permissions are influenced by the
value of \c umask.
On Windows the permissions are emulated using ACLs. These ACLs may be in non-canonical
order when the group is granted less permissions than others. Files and directories with
such permissions will generate warnings when the Security tab of the Properties dialog
is opened. Granting the group all permissions granted to others avoids such warnings.
\sa QIODevice::OpenMode, setFileName()
\since 6.3
*/
bool QFile::open(OpenMode mode, QFile::Permissions permissions)
{
Q_D(QFile);
if (isOpen())
return file_already_open(*this);
// Either Append or NewOnly implies WriteOnly
if (mode & (Append | NewOnly))
mode |= WriteOnly;
unsetError();
if ((mode & (ReadOnly | WriteOnly)) == 0) {
qWarning("QIODevice::open: File access not specified");
return false;
}
// QIODevice provides the buffering, so there's no need to request it from the file engine.
if (d->engine()->open(mode | QIODevice::Unbuffered, permissions)) {
QIODevice::open(mode);
if (mode & Append)
seek(size());
return true;
}
QFile::FileError err = d->fileEngine->error();
if (err == QFile::UnspecifiedError)
err = QFile::OpenError;
d->setError(err, d->fileEngine->errorString());
return false;
}
/*!
\overload

View File

@ -284,6 +284,7 @@ public:
#endif // QT_CONFIG(cxx17_filesystem)
bool open(OpenMode flags) override;
bool open(OpenMode flags, Permissions permissions);
bool open(FILE *f, OpenMode ioFlags, FileHandleFlags handleFlags=DontCloseHandle);
bool open(int fd, OpenMode ioFlags, FileHandleFlags handleFlags=DontCloseHandle);

View File

@ -183,6 +183,8 @@ private slots:
void ungetChar();
void createFile();
void createFileNewOnly();
void createFilePermissions_data();
void createFilePermissions();
void openFileExistingOnly();
void append();
void permissions_data();
@ -1268,6 +1270,56 @@ void tst_QFile::createFileNewOnly()
QFile::remove("createme.txt");
}
void tst_QFile::createFilePermissions_data()
{
QTest::addColumn<QFile::Permissions>("permissions");
for (int u = 0; u < 8; ++u) {
for (int g = 0; g < 8; ++g) {
for (int o = 0; o < 8; ++o) {
auto permissions = QFileDevice::Permissions::fromInt((u << 12) | (g << 4) | o);
QTest::addRow("%04x", permissions.toInt()) << permissions;
}
}
}
}
void tst_QFile::createFilePermissions()
{
QFETCH(QFile::Permissions, permissions);
#ifdef Q_OS_WIN
QScopedValueRollback<int> ntfsMode(qt_ntfs_permission_lookup);
++qt_ntfs_permission_lookup;
#endif
#ifdef Q_OS_UNIX
auto restoreMask = qScopeGuard([oldMask = umask(0)] { umask(oldMask); });
#endif
const QFile::Permissions setPermissions = {
QFile::ReadOther, QFile::WriteOther, QFile::ExeOther,
QFile::ReadGroup, QFile::WriteGroup, QFile::ExeGroup,
QFile::ReadOwner, QFile::WriteOwner, QFile::ExeOwner
};
const QString fileName = u"createme.txt"_qs;
QFile::remove(fileName);
QVERIFY(!QFile::exists(fileName));
QFile f(fileName);
auto removeFile = qScopeGuard([&f] {
f.close();
f.remove();
});
QVERIFY2(f.open(QIODevice::WriteOnly, permissions), msgOpenFailed(f).constData());
QVERIFY(QFile::exists(fileName));
auto actualPermissions = QFileInfo(fileName).permissions();
QCOMPARE(actualPermissions & setPermissions, permissions);
}
void tst_QFile::openFileExistingOnly()
{
QFile::remove("dontcreateme.txt");