Make Qt aware of NTFS Junctions on Windows
On NTFS, a junction point can be created and deleted by the mklink and rmdir commands, respectively. If a directory is not identified correctly as a junction, then applications will likely try to remove it using recursive methods, leading to fatal data loss. With this change, Qt can identify file system entries as junctions, allowing applications to use the correct file system operation to remove it. The test needs to delay the cleaning up of junctions and files it creates until the checks are complete; since they might fail and make the test function return prematurely, use a scope guard. [ChangeLog][QtCore][QFileInfo] Add QFileInfo::isJunction so that applications can recognize NTFS file system entries as junctions Task-number: QTBUG-75869 Change-Id: I3c208245afbd9fb7555515fb776ff63b133ca858 Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>bb10
parent
b81863cfad
commit
004e3e0dc2
|
|
@ -1145,6 +1145,25 @@ bool QFileInfo::isShortcut() const
|
|||
[d]() { return d->getFileFlags(QAbstractFileEngine::LinkType); });
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
Returns \c true if the object points to a junction;
|
||||
otherwise returns \c false.
|
||||
|
||||
Junctions only exist on Windows' NTFS file system, and are typically
|
||||
created by the \c{mklink} command. They can be thought of as symlinks for
|
||||
directories, and can only be created for absolute paths on the local
|
||||
volume.
|
||||
*/
|
||||
bool QFileInfo::isJunction() const
|
||||
{
|
||||
Q_D(const QFileInfo);
|
||||
return d->checkAttribute<bool>(
|
||||
QFileSystemMetaData::LegacyLinkType,
|
||||
[d]() { return d->metaData.isJunction(); },
|
||||
[d]() { return d->getFileFlags(QAbstractFileEngine::LinkType); });
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns \c true if the object points to a directory or to a symbolic
|
||||
link to a directory, and that directory is the root directory; otherwise
|
||||
|
|
|
|||
|
|
@ -113,6 +113,7 @@ public:
|
|||
bool isSymLink() const;
|
||||
bool isSymbolicLink() const;
|
||||
bool isShortcut() const;
|
||||
bool isJunction() const;
|
||||
bool isRoot() const;
|
||||
bool isBundle() const;
|
||||
|
||||
|
|
|
|||
|
|
@ -111,8 +111,10 @@ public:
|
|||
AliasType = 0x0,
|
||||
#endif
|
||||
#if defined(Q_OS_WIN)
|
||||
JunctionType = 0x04000000,
|
||||
WinLnkType = 0x08000000, // Note: Uses the same position for AliasType on Mac
|
||||
#else
|
||||
JunctionType = 0x0,
|
||||
WinLnkType = 0x0,
|
||||
#endif
|
||||
SequentialType = 0x00800000, // Note: overlaps with QAbstractFileEngine::RootFlag
|
||||
|
|
@ -205,8 +207,10 @@ public:
|
|||
bool wasDeleted() const { return (entryFlags & WasDeletedAttribute); }
|
||||
#if defined(Q_OS_WIN)
|
||||
bool isLnkFile() const { return (entryFlags & WinLnkType); }
|
||||
bool isJunction() const { return (entryFlags & JunctionType); }
|
||||
#else
|
||||
bool isLnkFile() const { return false; }
|
||||
bool isJunction() const { return false; }
|
||||
#endif
|
||||
|
||||
qint64 size() const { return size_; }
|
||||
|
|
@ -356,9 +360,15 @@ inline void QFileSystemMetaData::fillFromFindData(WIN32_FIND_DATA &findData, boo
|
|||
if (setLinkType) {
|
||||
knownFlagsMask |= LinkType;
|
||||
entryFlags &= ~LinkType;
|
||||
if ((fileAttribute_ & FILE_ATTRIBUTE_REPARSE_POINT)
|
||||
&& (findData.dwReserved0 == IO_REPARSE_TAG_SYMLINK)) {
|
||||
entryFlags |= LinkType;
|
||||
if (fileAttribute_ & FILE_ATTRIBUTE_REPARSE_POINT) {
|
||||
if (findData.dwReserved0 == IO_REPARSE_TAG_SYMLINK) {
|
||||
entryFlags |= LinkType;
|
||||
#if defined(IO_REPARSE_TAG_MOUNT_POINT)
|
||||
} else if ((fileAttribute_ & FILE_ATTRIBUTE_DIRECTORY)
|
||||
&& (findData.dwReserved0 == IO_REPARSE_TAG_MOUNT_POINT)) {
|
||||
entryFlags |= JunctionType;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1813,19 +1813,22 @@ void tst_QFileInfo::ntfsJunctionPointsAndSymlinks()
|
|||
QVERIFY2(creationResult == ERROR_SUCCESS, qPrintable(errorMessage));
|
||||
|
||||
QFileInfo fi(path);
|
||||
const bool actualIsSymLink = fi.isSymbolicLink();
|
||||
auto guard = qScopeGuard([&fi, this]() {
|
||||
// Ensure that junctions, mountpoints are removed. If this fails, do not remove
|
||||
// temporary directory to prevent it from trashing the system.
|
||||
if (fi.isDir()) {
|
||||
if (!QDir().rmdir(fi.filePath())) {
|
||||
qWarning("Unable to remove NTFS junction '%ls', keeping '%ls'.",
|
||||
qUtf16Printable(fi.fileName()),
|
||||
qUtf16Printable(QDir::toNativeSeparators(m_dir.path())));
|
||||
m_dir.setAutoRemove(false);
|
||||
}
|
||||
}
|
||||
});
|
||||
const QString actualSymLinkTarget = isSymLink ? fi.symLinkTarget() : QString();
|
||||
const QString actualCanonicalFilePath = isSymLink ? fi.canonicalFilePath() : QString();
|
||||
// Ensure that junctions, mountpoints are removed. If this fails, do not remove
|
||||
// temporary directory to prevent it from trashing the system.
|
||||
if (fi.isDir()) {
|
||||
if (!QDir().rmdir(fi.filePath())) {
|
||||
qWarning("Unable to remove NTFS junction '%s'', keeping '%s'.",
|
||||
qPrintable(fi.fileName()), qPrintable(QDir::toNativeSeparators(m_dir.path())));
|
||||
m_dir.setAutoRemove(false);
|
||||
}
|
||||
}
|
||||
QCOMPARE(actualIsSymLink, isSymLink);
|
||||
QCOMPARE(fi.isJunction(), resource.type == NtfsTestResource::Junction);
|
||||
QCOMPARE(fi.isSymbolicLink(), isSymLink);
|
||||
if (isSymLink) {
|
||||
QCOMPARE(actualSymLinkTarget, linkTarget);
|
||||
QCOMPARE(actualCanonicalFilePath, canonicalFilePath);
|
||||
|
|
|
|||
Loading…
Reference in New Issue