From 7a4e5e7433afe7150cdf40025d4525277809c412 Mon Sep 17 00:00:00 2001 From: Ryan Chu Date: Thu, 11 Jul 2019 13:23:59 +0200 Subject: Make Qt aware of symlinks and shortcuts on Windows Qt has traditionally considered Windows shortcut files equivalent to symlinks on Unix file systems. Because of NTFS symlinks, the interpretation of shotcut files as symlinks is confusing. In this change, QFileInfo treats shortcut (.lnk) files as regular files but can follow the pointed object. In addition, QFileInfo introduces a more comprehensive file type. So that applications can make well-informed decisions about how to treat a file system entry. Based on the implementation of QFileInfo::type(), two inline helper functions are introduced to QFileInfo. 1. isSymbolicLink, returns true if it points to a symbolic link. 2. isShortcut, returns true if it points to a shortcut. [ChangeLog][QtCore][QFileInfo] Introduce QFileInfo::type() to replace the isSymLink method. Task-number: QTBUG-75869 Change-Id: Icc0dd52f9ad0ea50b0265d77ee0d0a3d25054e39 Reviewed-by: Volker Hilsheimer --- src/corelib/io/qfileinfo.cpp | 123 +++++++++++++++++++++++++++++++++++++------ src/corelib/io/qfileinfo.h | 17 ++++++ 2 files changed, 125 insertions(+), 15 deletions(-) (limited to 'src') diff --git a/src/corelib/io/qfileinfo.cpp b/src/corelib/io/qfileinfo.cpp index a5a3bc8b3e..b720966d8f 100644 --- a/src/corelib/io/qfileinfo.cpp +++ b/src/corelib/io/qfileinfo.cpp @@ -256,10 +256,9 @@ QDateTime &QFileInfoPrivate::getFileTime(QAbstractFileEngine::FileTime request) \snippet code/src_corelib_io_qfileinfo.cpp 0 - On Windows, symlinks (shortcuts) are \c .lnk files. The reported - size() is that of the symlink (not the link's target), and - opening a symlink using QFile opens the \c .lnk file. For - example: + On Windows, shortcuts are \c .lnk files. The reported size() is that of + the shortcut (not the link's target), and opening a shortcut using QFile + opens the \c .lnk file. For example: \snippet code/src_corelib_io_qfileinfo.cpp 1 @@ -311,6 +310,19 @@ QDateTime &QFileInfoPrivate::getFileTime(QAbstractFileEngine::FileTime request) \sa QDir, QFile */ +/*! + \enum QFileInfo::FileType + + This enum is returned by type() to describe the type of the file system + entity described by the QFileInfo object. + + \value Unknown The object refers to an unknown item. + \value Regular The object refers to a regular file. + \value Directory The object refers to a directory. + \value SymbolicLink The object refers to a symbolic link. + \value Shortcut The object refers to a shortcut. +*/ + /*! \fn QFileInfo &QFileInfo::operator=(QFileInfo &&other) @@ -996,11 +1008,7 @@ bool QFileInfo::isNativePath() const */ bool QFileInfo::isFile() const { - Q_D(const QFileInfo); - return d->checkAttribute( - QFileSystemMetaData::FileType, - [d]() { return d->metaData.isFile(); }, - [d]() { return d->getFileFlags(QAbstractFileEngine::FileType); }); + return (type() & FileTypeMask) == Regular; } /*! @@ -1011,11 +1019,7 @@ bool QFileInfo::isFile() const */ bool QFileInfo::isDir() const { - Q_D(const QFileInfo); - return d->checkAttribute( - QFileSystemMetaData::DirectoryType, - [d]() { return d->metaData.isDirectory(); }, - [d]() { return d->getFileFlags(QAbstractFileEngine::DirectoryType); }); + return (type() & FileTypeMask) == Directory; } @@ -1036,7 +1040,7 @@ bool QFileInfo::isBundle() const } /*! - Returns \c true if this object points to a symbolic link; + Returns \c true if this object points to a symbolic link or shortcut; otherwise returns \c false. Symbolic links exist on Unix (including \macos and iOS) and Windows @@ -1065,6 +1069,48 @@ bool QFileInfo::isSymLink() const [d]() { return d->getFileFlags(QAbstractFileEngine::LinkType); }); } +/*! + \fn bool QFileInfo::isSymbolicLink() const + + Returns \c true if this object points to a symbolic link; + otherwise returns \c false. + + Symbolic links exist on Unix (including \macos and iOS) and Windows + (NTFS-symlink) and are typically created by the \c{ln -s} or \c{mklink} + commands, respectively. + + Unix handles symlinks transparently. Opening a symbolic link effectively + opens the \l{symLinkTarget()}{link's target}. + + In contrast to isSymLink(), false will be returned for shortcuts + (\c *.lnk files) on Windows. Use QFileInfo::isShortcut() instead. + + \note If the symlink points to a non existing file, exists() returns + false. + + \sa isFile(), isDir(), isShortcut(), symLinkTarget() +*/ + +/*! + \fn bool QFileInfo::isShortcut() const + + Returns \c true if this object points to a shortcut; + otherwise returns \c false. + + Shortcuts only exist on Windows and are typically \c .lnk files. + For instance, true will be returned for shortcuts (\c *.lnk files) on + Windows, but false will be returned on Unix (including \macos and iOS). + + The shortcut (.lnk) files are treated as regular files. Opening those will + open the \c .lnk file itself. In order to open the file a shortcut + references to, it must uses symLinkTarget() on a shortcut. + + \note Even if a shortcut (broken shortcut) points to a non existing file, + isShortcut() returns true. + + \sa isFile(), isDir(), isSymbolicLink(), symLinkTarget() +*/ + /*! 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 @@ -1268,6 +1314,53 @@ qint64 QFileInfo::size() const }); } +/*! + Returns the QFileInfo::FileTypes. + + QFileInfo::FileTypes combines with an indirection flag (link type) and a + base type it refers to. + + For example, \c SymbolicLink combines with \c Regular meaning a symlink to + a regular file. + + In addition, FileTypeMask and LinkTypeMask are used to extract the base + type and link type respectively. + + \sa isFile(), isDir(), isShortcut(), isSymbolicLink() +*/ +QFileInfo::FileTypes QFileInfo::type() const +{ + Q_D(const QFileInfo); + + QFileInfo::FileTypes type = QFileInfo::Unknown; + if (d->checkAttribute( + QFileSystemMetaData::LegacyLinkType, + [d]() { return d->metaData.isLnkFile(); }, + [d]() { return d->getFileFlags(QAbstractFileEngine::LinkType); })) { + type = QFileInfo::Shortcut; + } else if (d->checkAttribute( + QFileSystemMetaData::LegacyLinkType, + [d]() { return d->metaData.isLink(); }, + [d]() { return d->getFileFlags(QAbstractFileEngine::LinkType); })) { + type = QFileInfo::SymbolicLink; + } + + if (d->checkAttribute( + QFileSystemMetaData::DirectoryType, + [d]() { return d->metaData.isDirectory(); }, + [d]() { return d->getFileFlags(QAbstractFileEngine::DirectoryType); })) { + return type | QFileInfo::Directory; + } + + if (d->checkAttribute( + QFileSystemMetaData::FileType, + [d]() { return d->metaData.isFile(); }, + [d]() { return d->getFileFlags(QAbstractFileEngine::FileType); })) { + return type | QFileInfo::Regular; + } + return type; +} + #if QT_DEPRECATED_SINCE(5, 10) /*! \deprecated diff --git a/src/corelib/io/qfileinfo.h b/src/corelib/io/qfileinfo.h index 111517325d..1cbeafdd4a 100644 --- a/src/corelib/io/qfileinfo.h +++ b/src/corelib/io/qfileinfo.h @@ -66,6 +66,20 @@ public: QFileInfo(const QFileInfo &fileinfo); ~QFileInfo(); + enum FileType { + Unknown, + // base type + Regular, + Directory, + // indirection flag + SymbolicLink = 0x10, + Shortcut = 0x20, + // mask + FileTypeMask = 0x0f, + LinkTypeMask = 0xf0 + }; + Q_DECLARE_FLAGS(FileTypes, FileType) + QFileInfo &operator=(const QFileInfo &fileinfo); QFileInfo &operator=(QFileInfo &&other) noexcept { swap(other); return *this; } @@ -111,6 +125,8 @@ public: bool isFile() const; bool isDir() const; bool isSymLink() const; + inline bool isSymbolicLink() const { return type() & SymbolicLink; } + inline bool isShortcut() const { return type() & Shortcut; } bool isRoot() const; bool isBundle() const; @@ -129,6 +145,7 @@ public: QFile::Permissions permissions() const; qint64 size() const; + FileTypes type() const; // ### Qt6: inline these functions #if QT_DEPRECATED_SINCE(5, 10) -- cgit v1.2.3