QFileSystemEngine: add rmpath()

removeDirectory() acts in two mode, rmdir (one entry) and rmpath (the
entry and all empty parent directories). This is irregular behavior, and
API with a very specific use-case (in unittests, where you create a dir
tree and want to cleanup after the test finishes).

So, split the code into rmdir() and rmpath(), which matches
QDir::rmdir() and QDir::rmpath().

On Unix, a further optimization pointed out by Thiago in review, remove
the stat() call, ::rmdir() will fail with ENOTDIR if we try to remove
anything that isn't a dir.

Change-Id: I1bbb6e6c1ce49ba6d73d3c510b449223498612fb
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
Ahmad Samir 2024-03-11 20:42:51 +02:00
parent 46737c0bc0
commit ead72a1155
4 changed files with 52 additions and 41 deletions

View File

@ -1552,7 +1552,7 @@ bool QDir::rmdir(const QString &dirName) const
QString fn = filePath(dirName); QString fn = filePath(dirName);
if (!d->fileEngine) if (!d->fileEngine)
return QFileSystemEngine::removeDirectory(QFileSystemEntry(fn), false); return QFileSystemEngine::rmdir(QFileSystemEntry(fn));
return d->fileEngine->rmdir(fn, false); return d->fileEngine->rmdir(fn, false);
} }
@ -1606,7 +1606,7 @@ bool QDir::rmpath(const QString &dirPath) const
QString fn = filePath(dirPath); QString fn = filePath(dirPath);
if (!d->fileEngine) if (!d->fileEngine)
return QFileSystemEngine::removeDirectory(QFileSystemEntry(fn), true); return QFileSystemEngine::rmpath(QFileSystemEntry(fn));
return d->fileEngine->rmdir(fn, true); return d->fileEngine->rmdir(fn, true);
} }

View File

@ -117,7 +117,15 @@ public:
static bool createDirectory(const QFileSystemEntry &entry, bool createParents, static bool createDirectory(const QFileSystemEntry &entry, bool createParents,
std::optional<QFile::Permissions> permissions = std::nullopt); std::optional<QFile::Permissions> permissions = std::nullopt);
static bool removeDirectory(const QFileSystemEntry &entry, bool removeEmptyParents); static bool removeDirectory(const QFileSystemEntry &entry, bool removeEmptyParents)
{
if (removeEmptyParents)
return rmpath(entry);
return rmdir(entry);
}
static bool rmdir(const QFileSystemEntry &entry);
static bool rmpath(const QFileSystemEntry &entry);
static bool createLink(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error); static bool createLink(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error);

View File

@ -1189,30 +1189,28 @@ bool QFileSystemEngine::createDirectory(const QFileSystemEntry &entry, bool crea
return createDirectoryWithParents(dirName, mode, false); return createDirectoryWithParents(dirName, mode, false);
} }
//static bool QFileSystemEngine::rmdir(const QFileSystemEntry &entry)
bool QFileSystemEngine::removeDirectory(const QFileSystemEntry &entry, bool removeEmptyParents)
{ {
Q_CHECK_FILE_NAME(entry, false); const QByteArray path = entry.nativeFilePath();
Q_CHECK_FILE_NAME(path, false);
return ::rmdir(path.constData()) == 0;
}
if (removeEmptyParents) { bool QFileSystemEngine::rmpath(const QFileSystemEntry &entry)
QString dirName = QDir::cleanPath(entry.filePath()); {
for (qsizetype oldslash = 0, slash=dirName.size(); slash > 0; oldslash = slash) { const QString path = QDir::cleanPath(entry.filePath());
const QByteArray chunk = QFile::encodeName(dirName.left(slash)); Q_CHECK_FILE_NAME(path, false);
QT_STATBUF st;
if (QT_STAT(chunk.constData(), &st) != -1) { for (qsizetype oldslash = 0, slash = path.size(); slash > 0; oldslash = slash) {
if ((st.st_mode & S_IFMT) != S_IFDIR) const QByteArray chunk = QFile::encodeName(path.left(slash));
return false;
if (::rmdir(chunk.constData()) != 0) if (::rmdir(chunk.constData()) != 0)
return oldslash != 0; return oldslash != 0;
} else {
return false; slash = path.lastIndexOf(QDir::separator(), oldslash - 1);
}
slash = dirName.lastIndexOf(QDir::separator(), oldslash-1);
} }
return true; return true;
} }
return rmdir(QFile::encodeName(entry.filePath()).constData()) == 0;
}
//static //static
bool QFileSystemEngine::createLink(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error) bool QFileSystemEngine::createLink(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error)

View File

@ -1554,31 +1554,36 @@ bool QFileSystemEngine::createDirectory(const QFileSystemEntry &entry, bool crea
return createDirectoryWithParents(dirName, securityAttributes, false); return createDirectoryWithParents(dirName, securityAttributes, false);
} }
//static bool QFileSystemEngine::rmdir(const QFileSystemEntry &entry)
bool QFileSystemEngine::removeDirectory(const QFileSystemEntry &entry, bool removeEmptyParents)
{ {
QString dirName = entry.filePath(); QString dirName = entry.filePath();
Q_CHECK_FILE_NAME(dirName, false); Q_CHECK_FILE_NAME(dirName, false);
if (removeEmptyParents) { return rmDir(dirName);
dirName = QDir::toNativeSeparators(QDir::cleanPath(dirName)); }
for (int oldslash = 0, slash=dirName.length(); slash > 0; oldslash = slash) {
bool QFileSystemEngine::rmpath(const QFileSystemEntry &entry)
{
const QString dirName = QDir::toNativeSeparators(QDir::cleanPath(entry.filePath()));
Q_CHECK_FILE_NAME(dirName, false);
for (int oldslash = 0, slash = dirName.size(); slash > 0; oldslash = slash) {
const auto chunkRef = QStringView{dirName}.left(slash); const auto chunkRef = QStringView{dirName}.left(slash);
if (chunkRef.length() == 2 && chunkRef.at(0).isLetter() if (chunkRef.length() == 2 && chunkRef.at(0).isLetter()
&& chunkRef.at(1) == u':') { && chunkRef.at(1) == u':') {
break; break;
} }
const QString chunk = chunkRef.toString(); const QString chunk = chunkRef.toString();
// TODO: get isDirPath() and rmDir() to accept QStringView
if (!isDirPath(chunk, nullptr)) if (!isDirPath(chunk, nullptr))
return false; return false;
if (!rmDir(chunk)) if (!rmDir(chunk))
return oldslash != 0; return oldslash != 0;
slash = dirName.lastIndexOf(QDir::separator(), oldslash - 1); slash = dirName.lastIndexOf(QDir::separator(), oldslash - 1);
} }
return true; return true;
} }
return rmDir(entry.filePath());
}
//static //static
QString QFileSystemEngine::rootPath() QString QFileSystemEngine::rootPath()