diff --git a/src/core/kfileitem.cpp b/src/core/kfileitem.cpp --- a/src/core/kfileitem.cpp +++ b/src/core/kfileitem.cpp @@ -98,6 +98,8 @@ bool cmp(const KFileItemPrivate &item) const; bool isSlow() const; + static KMountPoint::List getMountPoints(); + /** * Extracts the data from the UDSEntry member and updates the KFileItem * accordingly. @@ -754,13 +756,39 @@ return entry().stringValue(KIO::UDSEntry::UDS_GROUP); } +KMountPoint::List KFileItemPrivate::getMountPoints() +{ + /** + * A cache of currently mounted filesystems + */ + static KMountPoint::List mountPoints; + static QDateTime lastMountPointUpdate; + + const auto now = QDateTime::currentDateTime(); + if (!lastMountPointUpdate.isValid() || + // refresh data at most every 5 seconds + lastMountPointUpdate.secsTo(now) > 5) { + // refresh cached mountpoints data + mountPoints = KMountPoint::currentMountPoints(); + lastMountPointUpdate = now; + } + + return mountPoints; +} + bool KFileItemPrivate::isSlow() const { if (m_slow == SlowUnknown) { const QString path = localPath(); if (!path.isEmpty()) { - const KFileSystemType::Type fsType = KFileSystemType::fileSystemType(path); - m_slow = (fsType == KFileSystemType::Nfs || fsType == KFileSystemType::Smb) ? Slow : Fast; + const auto &mountPoints = getMountPoints(); + auto mp = mountPoints.findByPath(path, KMountPoint::FindByPathFlag::FirstSlowMountPoint); + if (mp) { + m_slow = mp->probablySlow() ? Slow : Fast; + } else { + // if no mount point was found, can only mean we are on android + m_slow = Fast; + } } else { m_slow = Slow; } @@ -1216,7 +1244,8 @@ return false; } - if (d->m_bSkipMimeTypeFromContent) { + // avoid potential blocking stat on network mount + if (d->m_bSkipMimeTypeFromContent && d->isSlow()) { return false; } diff --git a/src/core/kmountpoint.h b/src/core/kmountpoint.h --- a/src/core/kmountpoint.h +++ b/src/core/kmountpoint.h @@ -38,21 +38,34 @@ { public: typedef QExplicitlySharedDataPointer Ptr; + + /** + * Flags that specify if the returned value of findByPath should be the first slow mount point + * making this function non-blocking, or try to return the real final mount point potentially blocking + * if a network filesystem is unresponsive + */ + enum FindByPathFlag { RealMountPoint, FirstSlowMountPoint }; + /** * List of mount points. */ class KIOCORE_EXPORT List : public QList { public: List(); + /** * Find the mountpoint on which resides @p path * For instance if /home is a separate partition, findByPath("/home/user/blah") * will return /home - * @param path the path to check + * Resolves symlinks recursively + * Its behavior can be tweaked by @see KMountPoint::FindByPathFlag + * + * @param path the path to check, must be absolute + * @param flag @see KMountPoint::FindByPathFlag * @return the mount point of the given file */ - Ptr findByPath(const QString &path) const; + Ptr findByPath(const QString &path, FindByPathFlag flag = RealMountPoint) const; /** * Returns the mount point associated with @p device, diff --git a/src/core/kmountpoint.cpp b/src/core/kmountpoint.cpp --- a/src/core/kmountpoint.cpp +++ b/src/core/kmountpoint.cpp @@ -412,18 +412,13 @@ } } -KMountPoint::Ptr KMountPoint::List::findByPath(const QString &path) const +KMountPoint::Ptr KMountPoint::List::findByPath(const QString &path, FindByPathFlag flag) const { -#ifndef Q_OS_WIN - /* If the path contains symlinks, get the real name */ - QFileInfo fileinfo(path); - const QString realname = fileinfo.exists() - ? fileinfo.canonicalFilePath() - : fileinfo.absolutePath(); //canonicalFilePath won't work unless file exists -#else +#ifdef Q_OS_WIN const QString realname = QDir::fromNativeSeparators(QDir(path).absolutePath()); +#else + const QString realname = path; #endif - int max = 0; KMountPoint::Ptr result; for (const KMountPoint::Ptr &mp : *this) { @@ -435,6 +430,22 @@ // keep iterating to check for a better match (bigger max) } } + + // if the file is not on a slow mount, or flag ignores this + if (result && (flag == RealMountPoint || !result->probablySlow())) { + // check recursively its parent directories for eventual symlinks + // and then resolves them transitively as canonicalPath would do + int cursor = path.lastIndexOf(QLatin1Char('/')); + while (cursor > 0) { + const QFileInfo fileinfo(path.left(cursor)); + if (fileinfo.isSymLink()) { + // pass the parent path plus the remainder of the path: + // for /dir/link/dir/test will return result for /destLink/dir/test + return findByPath(fileinfo.symLinkTarget() + path.mid(cursor)); + } + cursor = path.lastIndexOf(QLatin1Char('/'), cursor -1); + } + } return result; }