Fix QDir::mkpath() when the path contains "symlink/../"

It is incorrect to collapse a "symlink/.." segment because the parent
directory of the symlink's target may not be the directory where the
symlink itself is located.

[ChangeLog][QtCore][QDir] Fixed a bug that caused QDir::mkpath() to
create the wrong directory if the requested path contained a symbolic
link and "../".

Change-Id: Iaddbecfbba5441c8b2e4fffd14a3e367730a1e24
Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@qt.io>
Reviewed-by: David Faure <david.faure@kdab.com>
This commit is contained in:
Thiago Macieira 2017-02-16 13:58:55 -08:00
parent 49163ca689
commit 23d08ce2ed
2 changed files with 54 additions and 19 deletions

View File

@ -1,5 +1,6 @@
/**************************************************************************** /****************************************************************************
** **
** Copyright (C) 2017 Intel Corporation.
** Copyright (C) 2016 The Qt Company Ltd. ** Copyright (C) 2016 The Qt Company Ltd.
** Copyright (C) 2013 Samuel Gaist <samuel.gaist@edeltech.ch> ** Copyright (C) 2013 Samuel Gaist <samuel.gaist@edeltech.ch>
** Contact: https://www.qt.io/licensing/ ** Contact: https://www.qt.io/licensing/
@ -603,25 +604,7 @@ bool QFileSystemEngine::createDirectory(const QFileSystemEntry &entry, bool crea
if (!createParents) if (!createParents)
return false; return false;
// we need the cleaned path in order to create the parents return createDirectoryWithParents(nativeName, false);
// and we save errno just in case encodeName needs to load codecs
int savedErrno = errno;
bool pathChanged;
{
QString cleanName = QDir::cleanPath(dirName);
// Check if the cleaned name is the same or not. If we were given a
// path with resolvable "../" sections, cleanPath will remove them, but
// this may change the target dir if one of those segments was a
// symlink. This operation depends on cleanPath's optimization of
// returning the original string if it didn't modify anything.
pathChanged = !dirName.isSharedWith(cleanName);
if (pathChanged)
nativeName = QFile::encodeName(cleanName);
}
errno = savedErrno;
return createDirectoryWithParents(nativeName, pathChanged);
} }
//static //static

View File

@ -1,5 +1,6 @@
/**************************************************************************** /****************************************************************************
** **
** Copyright (C) 2017 Intel Corporation.
** Copyright (C) 2016 The Qt Company Ltd. ** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/ ** Contact: https://www.qt.io/licensing/
** **
@ -101,6 +102,7 @@ private slots:
void mkdirRmdir_data(); void mkdirRmdir_data();
void mkdirRmdir(); void mkdirRmdir();
void mkdirOnSymlink();
void makedirReturnCode(); void makedirReturnCode();
@ -387,6 +389,56 @@ void tst_QDir::mkdirRmdir()
QVERIFY(!fi.exists()); QVERIFY(!fi.exists());
} }
void tst_QDir::mkdirOnSymlink()
{
#ifndef Q_OS_UNIX
QSKIP("Test only valid on an OS that supports symlinks");
#else
// Create the structure:
// .
// ├── symlink -> two/three
// └── two
// └── three
// so when we mkdir("symlink/../four/five"), we end up with:
// .
// ├── symlink -> two/three
// └── two
// ├── four
// │ └── five
// └── three
QDir dir;
struct Clean {
QDir &dir;
Clean(QDir &dir) : dir(dir) {}
~Clean() { doClean(); }
void doClean() {
dir.rmpath("two/three");
dir.rmpath("two/four/five");
// in case the test fails, don't leave junk behind
dir.rmpath("four/five");
QFile::remove("symlink");
}
};
Clean clean(dir);
clean.doClean();
// create our structure:
dir.mkpath("two/three");
::symlink("two/three", "symlink");
// try it:
QString path = "symlink/../four/five";
QVERIFY(dir.mkpath(path));
QFileInfo fi(path);
QVERIFY2(fi.exists() && fi.isDir(), msgDoesNotExist(path).constData());
path = "two/four/five";
fi.setFile(path);
QVERIFY2(fi.exists() && fi.isDir(), msgDoesNotExist(path).constData());
#endif
}
void tst_QDir::makedirReturnCode() void tst_QDir::makedirReturnCode()
{ {
QString dirName = QString::fromLatin1("makedirReturnCode"); QString dirName = QString::fromLatin1("makedirReturnCode");