QFileInfo: add isOther()
"Special" is interpreted similar to the logic used in QDirListing::IteratorFlag::ExcludeOther. This also matches how std::filesystem::is_other() is supposed to work except that in Qt .lnk files on Windows are treated as "other". [ChangeLog][QtCore][QFileInfo] Added isOther() method. Fixes: QTBUG-133331 Change-Id: I2550fd37cd495b4593cacf704ce63da7d1c2addd Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
parent
c553f39e3d
commit
9a83c2b88c
@ -1178,6 +1178,57 @@ bool QFileInfo::isSymbolicLink() const
|
|||||||
[d]() { return d->getFileFlags(QAbstractFileEngine::LinkType); });
|
[d]() { return d->getFileFlags(QAbstractFileEngine::LinkType); });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\since 6.10
|
||||||
|
|
||||||
|
Returns \c true if this QFileInfo refers to a file system entry that is
|
||||||
|
\e not a directory, regular file or symbolic link. Otherwise returns
|
||||||
|
\c false.
|
||||||
|
|
||||||
|
If this QFileInfo refers to a nonexistent entry, this method returns
|
||||||
|
\c false.
|
||||||
|
|
||||||
|
If the entry is a dangling symbolic link (the target doesn't exist), this
|
||||||
|
method returns \c false. For a non-dangling symbolic link, this function
|
||||||
|
returns information about the target, not the symbolic link.
|
||||||
|
|
||||||
|
On Unix a special (other) file system entry is a FIFO, socket, character
|
||||||
|
device, or block device. For more details, see the
|
||||||
|
\l{https://pubs.opengroup.org/onlinepubs/9699919799/functions/mknod.html}{\c mknod}
|
||||||
|
manual page.
|
||||||
|
|
||||||
|
On Windows (for historical reasons, see \l{Symbolic Links and Shortcuts})
|
||||||
|
this method returns \c true for \c .lnk files.
|
||||||
|
|
||||||
|
\sa isDir(), isFile(), isSymLink(), QDirListing::IteratorFlag::ExcludeOther
|
||||||
|
*/
|
||||||
|
bool QFileInfo::isOther() const
|
||||||
|
{
|
||||||
|
Q_D(const QFileInfo);
|
||||||
|
using M = QFileSystemMetaData::MetaDataFlag;
|
||||||
|
// No M::LinkType to make QFileSystemEngine always call stat().
|
||||||
|
// M::WinLnkType is only relevant on Windows for '.lnk' files
|
||||||
|
constexpr auto mdFlags = M::ExistsAttribute | M::DirectoryType | M::FileType | M::WinLnkType;
|
||||||
|
|
||||||
|
auto fsLambda = [d]() {
|
||||||
|
// Check isLnkFile() first because currently exists() returns false for
|
||||||
|
// a broken '.lnk' where the target doesn't exist.
|
||||||
|
if (d->metaData.isLnkFile()) // Always false on non-Windows OSes
|
||||||
|
return true;
|
||||||
|
return d->metaData.exists() && !d->metaData.isDirectory() && !d->metaData.isFile();
|
||||||
|
};
|
||||||
|
|
||||||
|
auto engineLambda = [d]() {
|
||||||
|
using F = QAbstractFileEngine::FileFlag;
|
||||||
|
return d->getFileFlags(F::ExistsFlag)
|
||||||
|
&& !d->getFileFlags(F::LinkType) // QAFE doesn't have a separate type for ".lnk" file
|
||||||
|
&& !d->getFileFlags(F::DirectoryType)
|
||||||
|
&& !d->getFileFlags(F::FileType);
|
||||||
|
};
|
||||||
|
|
||||||
|
return d->checkAttribute<bool>(mdFlags, std::move(fsLambda), std::move(engineLambda));
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns \c true if this object points to a shortcut;
|
Returns \c true if this object points to a shortcut;
|
||||||
otherwise returns \c false.
|
otherwise returns \c false.
|
||||||
|
@ -125,6 +125,7 @@ public:
|
|||||||
bool isDir() const;
|
bool isDir() const;
|
||||||
bool isSymLink() const;
|
bool isSymLink() const;
|
||||||
bool isSymbolicLink() const;
|
bool isSymbolicLink() const;
|
||||||
|
bool isOther() const;
|
||||||
bool isShortcut() const;
|
bool isShortcut() const;
|
||||||
bool isAlias() const;
|
bool isAlias() const;
|
||||||
bool isJunction() const;
|
bool isJunction() const;
|
||||||
|
@ -133,6 +133,9 @@ private slots:
|
|||||||
void isDir_data();
|
void isDir_data();
|
||||||
void isDir();
|
void isDir();
|
||||||
|
|
||||||
|
void isOther_data();
|
||||||
|
void isOther();
|
||||||
|
|
||||||
void isRoot_data();
|
void isRoot_data();
|
||||||
void isRoot();
|
void isRoot();
|
||||||
|
|
||||||
@ -258,6 +261,7 @@ private:
|
|||||||
QString m_resourcesDir;
|
QString m_resourcesDir;
|
||||||
QTemporaryDir m_dir;
|
QTemporaryDir m_dir;
|
||||||
QSharedPointer<QTemporaryDir> m_dataDir;
|
QSharedPointer<QTemporaryDir> m_dataDir;
|
||||||
|
QTemporaryDir m_tempSubDir;
|
||||||
};
|
};
|
||||||
|
|
||||||
void tst_QFileInfo::initTestCase()
|
void tst_QFileInfo::initTestCase()
|
||||||
@ -412,6 +416,107 @@ void tst_QFileInfo::isDir()
|
|||||||
QVERIFY(!isDir);
|
QVERIFY(!isDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void tst_QFileInfo::isOther_data()
|
||||||
|
{
|
||||||
|
QTest::addColumn<QString>("path");
|
||||||
|
QTest::addColumn<bool>("expected");
|
||||||
|
|
||||||
|
m_tempSubDir.remove();
|
||||||
|
m_tempSubDir = QTemporaryDir(m_dir.path() + "/isother_test_dir.XXXXXX"_L1);
|
||||||
|
|
||||||
|
const QString prefix = m_tempSubDir.path() + u'/';
|
||||||
|
|
||||||
|
const QString filePath = prefix + "file"_L1;
|
||||||
|
QFile file(filePath);
|
||||||
|
if (file.open(QFile::WriteOnly)) {
|
||||||
|
file.write("JAJAJAA");
|
||||||
|
file.close();
|
||||||
|
}
|
||||||
|
const QString linkToFile = prefix + "link-to-file"_L1;
|
||||||
|
file.link(linkToFile);
|
||||||
|
|
||||||
|
const QString dirPath = m_dir.path();
|
||||||
|
const QString linkToDir = prefix + "link-to-dir"_L1;
|
||||||
|
QFile::link(dirPath, linkToDir);
|
||||||
|
|
||||||
|
const QString brokenLink = prefix + "broken-symlink"_L1;
|
||||||
|
QFile dummy(prefix + "dummyfile"_L1);
|
||||||
|
(void)dummy.open(QIODevice::WriteOnly);
|
||||||
|
dummy.link(brokenLink);
|
||||||
|
dummy.remove();
|
||||||
|
|
||||||
|
QTest::newRow("regular-file") << filePath << false;
|
||||||
|
QTest::newRow("symlink-to-regular-file") << linkToFile << false;
|
||||||
|
QTest::newRow("dir") << dirPath << false;
|
||||||
|
QTest::newRow("symlink-to-dir") << linkToDir << false;
|
||||||
|
QTest::newRow("broken-symlink") << brokenLink << false;
|
||||||
|
|
||||||
|
QTest::newRow("qresources-file") << ":/tst_qfileinfo/resources/file1" << false;
|
||||||
|
QTest::newRow("qresources-broken-dir") << ":/I/do_not_expect_this_path_to_exist/" << false;
|
||||||
|
QTest::newRow("qresources-broken-file") << ":/tst_qfileinfo/resources/ghost-file" << false;
|
||||||
|
|
||||||
|
#ifdef Q_OS_UNIX
|
||||||
|
auto addSpecialRow = [&prefix](const QString &s, uint type) {
|
||||||
|
const QString name = prefix + s;
|
||||||
|
if (::mknod(name.toLatin1().constData(), 0777 | type, dev_t{}) == 0)
|
||||||
|
QTest::addRow("%s", qPrintable(name)) << name << true;
|
||||||
|
else
|
||||||
|
qDebug("mknod call failed: %s", strerror(errno));
|
||||||
|
};
|
||||||
|
addSpecialRow("fifo-test", S_IFIFO);
|
||||||
|
addSpecialRow("socket-test", S_IFSOCK);
|
||||||
|
|
||||||
|
const QString linkToSpecial = prefix + "link-to-dev-null"_L1;
|
||||||
|
QFile::link(u"/dev/null"_s, linkToSpecial);
|
||||||
|
|
||||||
|
QTest::newRow("character-device") << u"/dev/null"_s << true;
|
||||||
|
QTest::newRow("symlink-to-special") << linkToSpecial << true;
|
||||||
|
#elif defined(Q_OS_WIN)
|
||||||
|
const QString name = prefix + "win-CreateSymbolicLink-file"_L1;
|
||||||
|
const auto res = FileSystem::createSymbolicLink(name.toLatin1().constData(),
|
||||||
|
file.fileName().toLatin1().constData());
|
||||||
|
if (res.dwErr == ERROR_SUCCESS)
|
||||||
|
QTest::newRow("win-CreateSymbolicLink-file") << name << false;
|
||||||
|
else if (res.dwErr == ERROR_PRIVILEGE_NOT_HELD)
|
||||||
|
qDebug() << msgInsufficientPrivileges(res.errorMessage);
|
||||||
|
else
|
||||||
|
qDebug() << res.errorMessage;
|
||||||
|
|
||||||
|
const QString winLnk = prefix + "winLnk.lnk"_L1;
|
||||||
|
QFile lnkTarget(prefix + "lnkTarget"_L1);
|
||||||
|
(void)lnkTarget.open(QIODevice::WriteOnly);
|
||||||
|
lnkTarget.link(winLnk);
|
||||||
|
QTest::newRow("winLnk") << winLnk << true;
|
||||||
|
|
||||||
|
const QString brokenWinLnk = prefix + "broken_winLnk.lnk"_L1;
|
||||||
|
const QString dummyPath = prefix + "lnkDummy"_L1;
|
||||||
|
lnkTarget.copy(dummyPath);
|
||||||
|
QFile lnkDummy(dummyPath);
|
||||||
|
lnkDummy.link(brokenWinLnk);
|
||||||
|
lnkDummy.remove();
|
||||||
|
QTest::newRow("broken-winLnk") << brokenWinLnk << true;
|
||||||
|
#endif // Q_OS_UNIX
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QFileInfo::isOther()
|
||||||
|
{
|
||||||
|
QFETCH(QString, path);
|
||||||
|
QFETCH(bool, expected);
|
||||||
|
|
||||||
|
QFileInfo info(path);
|
||||||
|
QCOMPARE(info.isOther(), expected);
|
||||||
|
if (!QByteArrayView{QTest::currentDataTag()}.contains("broken"))
|
||||||
|
QVERIFY(info.exists());
|
||||||
|
|
||||||
|
#if QT_CONFIG(cxx17_filesystem) // This code doesn't work in QNX on the CI
|
||||||
|
if (QByteArrayView{QTest::currentDataTag()}.contains("winLnk"))
|
||||||
|
QEXPECT_FAIL("", "QFileInfo::isOther() returns true for '.lnk' files on Windows, "
|
||||||
|
"std::filesystem::is_other() returns false", Continue);
|
||||||
|
|
||||||
|
QCOMPARE_EQ(info.isOther(), std::filesystem::is_other(path.toStdString()));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
void tst_QFileInfo::isRoot_data()
|
void tst_QFileInfo::isRoot_data()
|
||||||
{
|
{
|
||||||
QTest::addColumn<QString>("path");
|
QTest::addColumn<QString>("path");
|
||||||
|
Loading…
x
Reference in New Issue
Block a user