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

View File

@ -117,7 +117,15 @@ public:
static bool createDirectory(const QFileSystemEntry &entry, bool createParents,
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);

View File

@ -1189,30 +1189,28 @@ bool QFileSystemEngine::createDirectory(const QFileSystemEntry &entry, bool crea
return createDirectoryWithParents(dirName, mode, false);
}
//static
bool QFileSystemEngine::removeDirectory(const QFileSystemEntry &entry, bool removeEmptyParents)
bool QFileSystemEngine::rmdir(const QFileSystemEntry &entry)
{
Q_CHECK_FILE_NAME(entry, false);
const QByteArray path = entry.nativeFilePath();
Q_CHECK_FILE_NAME(path, false);
return ::rmdir(path.constData()) == 0;
}
if (removeEmptyParents) {
QString dirName = QDir::cleanPath(entry.filePath());
for (qsizetype oldslash = 0, slash=dirName.size(); slash > 0; oldslash = slash) {
const QByteArray chunk = QFile::encodeName(dirName.left(slash));
QT_STATBUF st;
if (QT_STAT(chunk.constData(), &st) != -1) {
if ((st.st_mode & S_IFMT) != S_IFDIR)
return false;
bool QFileSystemEngine::rmpath(const QFileSystemEntry &entry)
{
const QString path = QDir::cleanPath(entry.filePath());
Q_CHECK_FILE_NAME(path, false);
for (qsizetype oldslash = 0, slash = path.size(); slash > 0; oldslash = slash) {
const QByteArray chunk = QFile::encodeName(path.left(slash));
if (::rmdir(chunk.constData()) != 0)
return oldslash != 0;
} else {
return false;
}
slash = dirName.lastIndexOf(QDir::separator(), oldslash-1);
slash = path.lastIndexOf(QDir::separator(), oldslash - 1);
}
return true;
}
return rmdir(QFile::encodeName(entry.filePath()).constData()) == 0;
}
//static
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);
}
//static
bool QFileSystemEngine::removeDirectory(const QFileSystemEntry &entry, bool removeEmptyParents)
bool QFileSystemEngine::rmdir(const QFileSystemEntry &entry)
{
QString dirName = entry.filePath();
Q_CHECK_FILE_NAME(dirName, false);
if (removeEmptyParents) {
dirName = QDir::toNativeSeparators(QDir::cleanPath(dirName));
for (int oldslash = 0, slash=dirName.length(); slash > 0; oldslash = slash) {
return rmDir(dirName);
}
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);
if (chunkRef.length() == 2 && chunkRef.at(0).isLetter()
&& chunkRef.at(1) == u':') {
break;
}
const QString chunk = chunkRef.toString();
// TODO: get isDirPath() and rmDir() to accept QStringView
if (!isDirPath(chunk, nullptr))
return false;
if (!rmDir(chunk))
return oldslash != 0;
slash = dirName.lastIndexOf(QDir::separator(), oldslash - 1);
}
return true;
}
return rmDir(entry.filePath());
}
//static
QString QFileSystemEngine::rootPath()