QFileSystemEngine: Rework createDirectory on Windows

Try starting to create the directory at the end, not at the front. This
is the same way as the Unix implementation is doing. This avoids problems
like us trying to enter directories we are not allowed to read, which
might be due to access rights or due to sandboxing.

Change-Id: I67c1ed4bdc20a15b1af9b33aa48d59fea359da22
Reviewed-by: Christoph Schleifenbaum <christoph.schleifenbaum@kdab.com>
This commit is contained in:
Christoph Schleifenbaum 2019-09-25 12:52:07 +02:00
parent 9d504e1150
commit ed48391c59

View File

@ -1123,67 +1123,62 @@ static bool isDirPath(const QString &dirPath, bool *existed)
return fileAttrib & FILE_ATTRIBUTE_DIRECTORY;
}
// NOTE: if \a shouldMkdirFirst is false, we assume the caller did try to mkdir
// before calling this function.
static bool createDirectoryWithParents(const QString &nativeName, bool shouldMkdirFirst = true)
{
const auto isUNCRoot = [](const QString &nativeName) {
return nativeName.startsWith(QLatin1String("\\\\")) && nativeName.count(QDir::separator()) <= 3;
};
const auto isDriveName = [](const QString &nativeName) {
return nativeName.size() == 2 && nativeName.at(1) == QLatin1Char(':');
};
const auto isDir = [](const QString &nativeName) {
bool exists = false;
return isDirPath(nativeName, &exists) && exists;
};
// Do not try to mkdir a UNC root path or a drive letter.
if (isUNCRoot(nativeName) || isDriveName(nativeName))
return false;
if (shouldMkdirFirst) {
if (mkDir(nativeName))
return true;
}
const int backSlash = nativeName.lastIndexOf(QDir::separator());
if (backSlash < 1)
return false;
const QString parentNativeName = nativeName.left(backSlash);
if (!createDirectoryWithParents(parentNativeName))
return false;
// try again
if (mkDir(nativeName))
return true;
return isDir(nativeName);
}
//static
bool QFileSystemEngine::createDirectory(const QFileSystemEntry &entry, bool createParents)
{
QString dirName = entry.filePath();
Q_CHECK_FILE_NAME(dirName, false);
if (createParents) {
dirName = QDir::toNativeSeparators(QDir::cleanPath(dirName));
// We spefically search for / so \ would break it..
int oldslash = -1;
if (dirName.startsWith(QLatin1String("\\\\"))) {
// Don't try to create the root path of a UNC path;
// CreateDirectory() will just return ERROR_INVALID_NAME.
for (int i = 0; i < dirName.size(); ++i) {
if (dirName.at(i) != QDir::separator()) {
oldslash = i;
break;
}
}
if (oldslash != -1)
oldslash = dirName.indexOf(QDir::separator(), oldslash);
} else if (dirName.size() > 2
&& dirName.at(1) == QLatin1Char(':')) {
// Don't try to call mkdir with just a drive letter
oldslash = 2;
}
for (int slash=0; slash != -1; oldslash = slash) {
slash = dirName.indexOf(QDir::separator(), oldslash+1);
if (slash == -1) {
if (oldslash == dirName.length())
break;
slash = dirName.length();
}
if (slash) {
DWORD lastError;
QString chunk = dirName.left(slash);
if (!mkDir(chunk, &lastError)) {
if (lastError == ERROR_ALREADY_EXISTS || lastError == ERROR_ACCESS_DENIED) {
bool existed = false;
if (isDirPath(chunk, &existed) && existed)
continue;
#ifdef Q_OS_WINRT
static QThreadStorage<QString> dataLocation;
if (!dataLocation.hasLocalData())
dataLocation.setLocalData(QDir::toNativeSeparators(QStandardPaths::writableLocation(QStandardPaths::DataLocation)));
static QThreadStorage<QString> tempLocation;
if (!tempLocation.hasLocalData())
tempLocation.setLocalData(QDir::toNativeSeparators(QStandardPaths::writableLocation(QStandardPaths::TempLocation)));
// We try to create something outside the sandbox, which is forbidden
// However we could still try to pass into the sandbox
if (dataLocation.localData().startsWith(chunk) || tempLocation.localData().startsWith(chunk))
continue;
#endif
}
return false;
}
}
}
dirName = QDir::toNativeSeparators(QDir::cleanPath(dirName));
// try to mkdir this directory
DWORD lastError;
if (mkDir(dirName, &lastError))
return true;
}
return mkDir(entry.filePath());
// mkpath should return true, if the directory already exists, mkdir false.
if (!createParents)
return false;
if (lastError == ERROR_ALREADY_EXISTS)
return isDirPath(dirName, nullptr);
return createDirectoryWithParents(dirName, false);
}
//static