QFileSystemEngine: verify that the file name isn't empty

Making system calls with empty file names is not a good idea. When you
run qmake $srcdir, you see this in strace:

 stat("", 0x7ffed229e250)                = -1 ENOENT (No such file or directory)
(twice)

I've also inlined the isEmpty() function for better code generation.

Some functions take QSystemError and some don't. That needs to be
corrected at some point, possibly with something like std::expected.

Change-Id: I1eba2b016de74620bfc8fffd14ccbfa162f93631
Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
This commit is contained in:
Thiago Macieira 2017-06-29 11:16:04 -07:00
parent 9a50333dd0
commit 0dee566e98
3 changed files with 41 additions and 8 deletions

View File

@ -95,6 +95,13 @@ static int statx(int dirfd, const char *pathname, int flag, unsigned mask, struc
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
#define emptyFileEntryWarning() emptyFileEntryWarning_(QT_MESSAGELOG_FILE, QT_MESSAGELOG_LINE, QT_MESSAGELOG_FUNC)
static void emptyFileEntryWarning_(const char *file, int line, const char *function)
{
QMessageLogger(file, line, function).warning("Empty filename passed to function");
errno = EINVAL;
}
#if defined(Q_OS_DARWIN) #if defined(Q_OS_DARWIN)
static inline bool hasResourcePropertyFlag(const QFileSystemMetaData &data, static inline bool hasResourcePropertyFlag(const QFileSystemMetaData &data,
const QFileSystemEntry &entry, const QFileSystemEntry &entry,
@ -602,6 +609,9 @@ void QFileSystemMetaData::fillFromDirEnt(const QT_DIRENT &entry)
//static //static
QFileSystemEntry QFileSystemEngine::getLinkTarget(const QFileSystemEntry &link, QFileSystemMetaData &data) QFileSystemEntry QFileSystemEngine::getLinkTarget(const QFileSystemEntry &link, QFileSystemMetaData &data)
{ {
if (Q_UNLIKELY(link.isEmpty()))
return emptyFileEntryWarning(), link;
QByteArray s = qt_readlink(link.nativeFilePath().constData()); QByteArray s = qt_readlink(link.nativeFilePath().constData());
if (s.length() > 0) { if (s.length() > 0) {
QString ret; QString ret;
@ -666,7 +676,9 @@ QFileSystemEntry QFileSystemEngine::getLinkTarget(const QFileSystemEntry &link,
//static //static
QFileSystemEntry QFileSystemEngine::canonicalName(const QFileSystemEntry &entry, QFileSystemMetaData &data) QFileSystemEntry QFileSystemEngine::canonicalName(const QFileSystemEntry &entry, QFileSystemMetaData &data)
{ {
if (entry.isEmpty() || entry.isRoot()) if (Q_UNLIKELY(entry.isEmpty()))
return emptyFileEntryWarning(), entry;
if (entry.isRoot())
return entry; return entry;
#if !defined(Q_OS_MAC) && !defined(Q_OS_QNX) && !defined(Q_OS_ANDROID) && !defined(Q_OS_HAIKU) && _POSIX_VERSION < 200809L #if !defined(Q_OS_MAC) && !defined(Q_OS_QNX) && !defined(Q_OS_ANDROID) && !defined(Q_OS_HAIKU) && _POSIX_VERSION < 200809L
@ -733,6 +745,8 @@ QFileSystemEntry QFileSystemEngine::canonicalName(const QFileSystemEntry &entry,
//static //static
QFileSystemEntry QFileSystemEngine::absoluteName(const QFileSystemEntry &entry) QFileSystemEntry QFileSystemEngine::absoluteName(const QFileSystemEntry &entry)
{ {
if (Q_UNLIKELY(entry.isEmpty()))
return emptyFileEntryWarning(), entry;
if (entry.isAbsolute() && entry.isClean()) if (entry.isAbsolute() && entry.isClean())
return entry; return entry;
@ -766,6 +780,9 @@ QFileSystemEntry QFileSystemEngine::absoluteName(const QFileSystemEntry &entry)
//static //static
QByteArray QFileSystemEngine::id(const QFileSystemEntry &entry) QByteArray QFileSystemEngine::id(const QFileSystemEntry &entry)
{ {
if (Q_UNLIKELY(entry.isEmpty()))
return emptyFileEntryWarning(), QByteArray();
QT_STATBUF statResult; QT_STATBUF statResult;
if (QT_STAT(entry.nativeFilePath().constData(), &statResult)) { if (QT_STAT(entry.nativeFilePath().constData(), &statResult)) {
if (errno != ENOENT) if (errno != ENOENT)
@ -873,6 +890,9 @@ QString QFileSystemEngine::bundleName(const QFileSystemEntry &entry)
bool QFileSystemEngine::fillMetaData(const QFileSystemEntry &entry, QFileSystemMetaData &data, bool QFileSystemEngine::fillMetaData(const QFileSystemEntry &entry, QFileSystemMetaData &data,
QFileSystemMetaData::MetaDataFlags what) QFileSystemMetaData::MetaDataFlags what)
{ {
if (Q_UNLIKELY(entry.isEmpty()))
return emptyFileEntryWarning(), false;
#if defined(Q_OS_DARWIN) #if defined(Q_OS_DARWIN)
if (what & QFileSystemMetaData::BundleType) { if (what & QFileSystemMetaData::BundleType) {
if (!data.hasFlags(QFileSystemMetaData::DirectoryType)) if (!data.hasFlags(QFileSystemMetaData::DirectoryType))
@ -1083,6 +1103,8 @@ static bool createDirectoryWithParents(const QByteArray &nativeName, bool should
bool QFileSystemEngine::createDirectory(const QFileSystemEntry &entry, bool createParents) bool QFileSystemEngine::createDirectory(const QFileSystemEntry &entry, bool createParents)
{ {
QString dirName = entry.filePath(); QString dirName = entry.filePath();
if (Q_UNLIKELY(dirName.isEmpty()))
return emptyFileEntryWarning(), false;
// Darwin doesn't support trailing /'s, so remove for everyone // Darwin doesn't support trailing /'s, so remove for everyone
while (dirName.size() > 1 && dirName.endsWith(QLatin1Char('/'))) while (dirName.size() > 1 && dirName.endsWith(QLatin1Char('/')))
@ -1101,6 +1123,9 @@ bool QFileSystemEngine::createDirectory(const QFileSystemEntry &entry, bool crea
//static //static
bool QFileSystemEngine::removeDirectory(const QFileSystemEntry &entry, bool removeEmptyParents) bool QFileSystemEngine::removeDirectory(const QFileSystemEntry &entry, bool removeEmptyParents)
{ {
if (Q_UNLIKELY(entry.isEmpty()))
return emptyFileEntryWarning(), false;
if (removeEmptyParents) { if (removeEmptyParents) {
QString dirName = QDir::cleanPath(entry.filePath()); QString dirName = QDir::cleanPath(entry.filePath());
for (int oldslash = 0, slash=dirName.length(); slash > 0; oldslash = slash) { for (int oldslash = 0, slash=dirName.length(); slash > 0; oldslash = slash) {
@ -1124,6 +1149,8 @@ bool QFileSystemEngine::removeDirectory(const QFileSystemEntry &entry, bool remo
//static //static
bool QFileSystemEngine::createLink(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error) bool QFileSystemEngine::createLink(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error)
{ {
if (Q_UNLIKELY(source.isEmpty() || target.isEmpty()))
return emptyFileEntryWarning(), false;
if (::symlink(source.nativeFilePath().constData(), target.nativeFilePath().constData()) == 0) if (::symlink(source.nativeFilePath().constData(), target.nativeFilePath().constData()) == 0)
return true; return true;
error = QSystemError(errno, QSystemError::StandardLibraryError); error = QSystemError(errno, QSystemError::StandardLibraryError);
@ -1158,6 +1185,8 @@ bool QFileSystemEngine::renameFile(const QFileSystemEntry &source, const QFileSy
{ {
QFileSystemEntry::NativePath srcPath = source.nativeFilePath(); QFileSystemEntry::NativePath srcPath = source.nativeFilePath();
QFileSystemEntry::NativePath tgtPath = target.nativeFilePath(); QFileSystemEntry::NativePath tgtPath = target.nativeFilePath();
if (Q_UNLIKELY(srcPath.isEmpty() || tgtPath.isEmpty()))
return emptyFileEntryWarning(), false;
#if defined(RENAME_NOREPLACE) && (QT_CONFIG(renameat2) || defined(SYS_renameat2)) #if defined(RENAME_NOREPLACE) && (QT_CONFIG(renameat2) || defined(SYS_renameat2))
if (renameat2(AT_FDCWD, srcPath, AT_FDCWD, tgtPath, RENAME_NOREPLACE) == 0) if (renameat2(AT_FDCWD, srcPath, AT_FDCWD, tgtPath, RENAME_NOREPLACE) == 0)
@ -1227,6 +1256,8 @@ bool QFileSystemEngine::renameFile(const QFileSystemEntry &source, const QFileSy
//static //static
bool QFileSystemEngine::renameOverwriteFile(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error) bool QFileSystemEngine::renameOverwriteFile(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error)
{ {
if (Q_UNLIKELY(source.isEmpty() || target.isEmpty()))
return emptyFileEntryWarning(), false;
if (::rename(source.nativeFilePath().constData(), target.nativeFilePath().constData()) == 0) if (::rename(source.nativeFilePath().constData(), target.nativeFilePath().constData()) == 0)
return true; return true;
error = QSystemError(errno, QSystemError::StandardLibraryError); error = QSystemError(errno, QSystemError::StandardLibraryError);
@ -1236,6 +1267,8 @@ bool QFileSystemEngine::renameOverwriteFile(const QFileSystemEntry &source, cons
//static //static
bool QFileSystemEngine::removeFile(const QFileSystemEntry &entry, QSystemError &error) bool QFileSystemEngine::removeFile(const QFileSystemEntry &entry, QSystemError &error)
{ {
if (Q_UNLIKELY(entry.isEmpty()))
return emptyFileEntryWarning(), false;
if (unlink(entry.nativeFilePath().constData()) == 0) if (unlink(entry.nativeFilePath().constData()) == 0)
return true; return true;
error = QSystemError(errno, QSystemError::StandardLibraryError); error = QSystemError(errno, QSystemError::StandardLibraryError);
@ -1270,8 +1303,10 @@ static mode_t toMode_t(QFile::Permissions permissions)
//static //static
bool QFileSystemEngine::setPermissions(const QFileSystemEntry &entry, QFile::Permissions permissions, QSystemError &error, QFileSystemMetaData *data) bool QFileSystemEngine::setPermissions(const QFileSystemEntry &entry, QFile::Permissions permissions, QSystemError &error, QFileSystemMetaData *data)
{ {
mode_t mode = toMode_t(permissions); if (Q_UNLIKELY(entry.isEmpty()))
return emptyFileEntryWarning(), false;
mode_t mode = toMode_t(permissions);
bool success = ::chmod(entry.nativeFilePath().constData(), mode) == 0; bool success = ::chmod(entry.nativeFilePath().constData(), mode) == 0;
if (success && data) { if (success && data) {
data->entryFlags &= ~QFileSystemMetaData::Permissions; data->entryFlags &= ~QFileSystemMetaData::Permissions;

View File

@ -331,11 +331,6 @@ bool QFileSystemEntry::isRoot() const
return isRootPath(m_filePath); return isRootPath(m_filePath);
} }
bool QFileSystemEntry::isEmpty() const
{
return m_filePath.isEmpty() && m_nativeFilePath.isEmpty();
}
// private methods // private methods
void QFileSystemEntry::findLastSeparator() const void QFileSystemEntry::findLastSeparator() const

View File

@ -98,7 +98,10 @@ public:
#endif #endif
bool isRoot() const; bool isRoot() const;
bool isEmpty() const; bool isEmpty() const
{
return m_filePath.isEmpty() && m_nativeFilePath.isEmpty();
}
void clear() void clear()
{ {
*this = QFileSystemEntry(); *this = QFileSystemEntry();