diff --git a/src/corelib/CMakeLists.txt b/src/corelib/CMakeLists.txt index 5ce46ba086d..4fb3e0d3aaa 100644 --- a/src/corelib/CMakeLists.txt +++ b/src/corelib/CMakeLists.txt @@ -988,7 +988,7 @@ qt_internal_extend_target(Core CONDITION APPLE AND NOT MACOS qt_internal_extend_target(Core CONDITION ANDROID SOURCES io/qstandardpaths_android.cpp - io/qstorageinfo_unix.cpp + io/qstorageinfo_linux.cpp kernel/qjnitypes.h kernel/qjnienvironment.cpp kernel/qjnienvironment.h kernel/qjniobject.cpp kernel/qjniobject.h @@ -1018,12 +1018,19 @@ qt_internal_extend_target(Core CONDITION HAIKU AND NOT ANDROID be ) -qt_internal_extend_target(Core CONDITION UNIX AND NOT APPLE AND NOT HAIKU AND NOT ANDROID +qt_internal_extend_target(Core + CONDITION UNIX AND NOT LINUX AND NOT APPLE AND NOT HAIKU AND NOT ANDROID SOURCES io/qstandardpaths_unix.cpp io/qstorageinfo_unix.cpp ) +qt_internal_extend_target(Core CONDITION LINUX AND NOT ANDROID + SOURCES + io/qstandardpaths_unix.cpp + io/qstorageinfo_linux.cpp +) + qt_internal_extend_target(Core CONDITION QT_FEATURE_itemmodel SOURCES itemmodels/qabstractitemmodel.cpp itemmodels/qabstractitemmodel.h itemmodels/qabstractitemmodel_p.h diff --git a/src/corelib/io/qstorageinfo_linux.cpp b/src/corelib/io/qstorageinfo_linux.cpp new file mode 100644 index 00000000000..fbad091a241 --- /dev/null +++ b/src/corelib/io/qstorageinfo_linux.cpp @@ -0,0 +1,172 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2014 Ivan Komissarov +// Copyright (C) 2016 Intel Corporation. +// Copyright (C) 2023 Ahmad Samir +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "qstorageinfo_linux_p.h" + +#include "qdiriterator.h" +#include + +#if defined(Q_OS_ANDROID) +# include +# include +# define QT_STATFS ::statfs +# define QT_STATFSBUF struct statfs +# if !defined(ST_RDONLY) +# define ST_RDONLY 1 // hack for missing define on Android +# endif +#else +# include +# if defined(QT_LARGEFILE_SUPPORT) +# define QT_STATFSBUF struct statvfs64 +# define QT_STATFS ::statvfs64 +# else +# define QT_STATFSBUF struct statvfs +# define QT_STATFS ::statvfs +# endif // QT_LARGEFILE_SUPPORT +#endif + +QT_BEGIN_NAMESPACE + +using namespace Qt::StringLiterals; + +// udev encodes the labels with ID_LABEL_FS_ENC which is done with +// blkid_encode_string(). Within this function some 1-byte utf-8 +// characters not considered safe (e.g. '\' or ' ') are encoded as hex +static QString decodeFsEncString(const QString &str) +{ + QString decoded; + decoded.reserve(str.size()); + + int i = 0; + while (i < str.size()) { + if (i <= str.size() - 4) { // we need at least four characters \xAB + if (QStringView{str}.sliced(i).startsWith("\\x"_L1)) { + bool bOk; + const int code = QStringView{str}.mid(i+2, 2).toInt(&bOk, 16); + if (bOk && code >= 0x20 && code < 0x80) { + decoded += QChar(code); + i += 4; + continue; + } + } + } + decoded += str.at(i); + ++i; + } + return decoded; +} + +static inline QString retrieveLabel(const QByteArray &device) +{ + static const char pathDiskByLabel[] = "/dev/disk/by-label"; + + QFileInfo devinfo(QFile::decodeName(device)); + QString devicePath = devinfo.canonicalFilePath(); + + QDirIterator it(QLatin1StringView(pathDiskByLabel), QDir::NoDotAndDotDot); + while (it.hasNext()) { + QFileInfo fileInfo = it.nextFileInfo(); + if (fileInfo.isSymLink() && fileInfo.symLinkTarget() == devicePath) + return decodeFsEncString(fileInfo.fileName()); + } + return QString(); +} + +void QStorageInfoPrivate::doStat() +{ + initRootPath(); + if (rootPath.isEmpty()) + return; + + retrieveVolumeInfo(); + name = retrieveLabel(device); +} + +void QStorageInfoPrivate::retrieveVolumeInfo() +{ + QT_STATFSBUF statfs_buf; + int result; + EINTR_LOOP(result, QT_STATFS(QFile::encodeName(rootPath).constData(), &statfs_buf)); + if (result == 0) { + valid = true; + ready = true; + + bytesTotal = statfs_buf.f_blocks * statfs_buf.f_frsize; + bytesFree = statfs_buf.f_bfree * statfs_buf.f_frsize; + bytesAvailable = statfs_buf.f_bavail * statfs_buf.f_frsize; + blockSize = statfs_buf.f_bsize; + +#if defined(Q_OS_ANDROID) +#if defined(_STATFS_F_FLAGS) + readOnly = (statfs_buf.f_flags & ST_RDONLY) != 0; +#endif +#else + readOnly = (statfs_buf.f_flag & ST_RDONLY) != 0; +#endif + } +} + +static std::vector parseMountInfo(FilterMountInfo filter = FilterMountInfo::All) +{ + QFile file(u"/proc/self/mountinfo"_s); + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) + return {}; + + QByteArray mountinfo = file.readAll(); + file.close(); + + return doParseMountInfo(mountinfo, filter); +} + +void QStorageInfoPrivate::initRootPath() +{ + rootPath = QFileInfo(rootPath).canonicalFilePath(); + if (rootPath.isEmpty()) + return; + + std::vector infos = parseMountInfo(); + if (infos.empty()) { + rootPath = u'/'; + return; + } + + qsizetype maxLength = 0; + const QString oldRootPath = rootPath; + rootPath.clear(); + + for (auto &info : infos) { + // we try to find most suitable entry + qsizetype mpSize = info.mountPoint.size(); + if (isParentOf(info.mountPoint, oldRootPath) && maxLength < mpSize) { + maxLength = mpSize; + rootPath = info.mountPoint; + device = info.device; + fileSystemType = info.fsType; + subvolume = info.fsRoot; + } + } +} + +QList QStorageInfoPrivate::mountedVolumes() +{ + std::vector infos = parseMountInfo(FilterMountInfo::Filtered); + if (infos.empty()) + return QList{root()}; + + QList volumes; + for (MountInfo &info : infos) { + QStorageInfo storage(info.mountPoint); + storage.d->device = info.device; + storage.d->fileSystemType = info.fsType; + storage.d->subvolume = info.fsRoot; + if (storage.bytesTotal() == 0 && storage != root()) + continue; + volumes.push_back(storage); + } + return volumes; +} + +QT_END_NAMESPACE diff --git a/src/corelib/io/qstorageinfo_mac.cpp b/src/corelib/io/qstorageinfo_mac.cpp index 690e7212d12..9ec5ae3f2f2 100644 --- a/src/corelib/io/qstorageinfo_mac.cpp +++ b/src/corelib/io/qstorageinfo_mac.cpp @@ -160,9 +160,4 @@ QList QStorageInfoPrivate::mountedVolumes() return volumes; } -QStorageInfo QStorageInfoPrivate::root() -{ - return QStorageInfo(QStringLiteral("/")); -} - QT_END_NAMESPACE diff --git a/src/corelib/io/qstorageinfo_p.h b/src/corelib/io/qstorageinfo_p.h index 77a7aa6c7d8..ae4ff02f0bc 100644 --- a/src/corelib/io/qstorageinfo_p.h +++ b/src/corelib/io/qstorageinfo_p.h @@ -16,6 +16,8 @@ // #include +#include +#include #include #include "qstorageinfo.h" @@ -35,7 +37,15 @@ public: void doStat(); static QList mountedVolumes(); - static QStorageInfo root(); + + static QStorageInfo root() + { +#ifdef Q_OS_WIN + return QStorageInfo(QDir::fromNativeSeparators(QFile::decodeName(qgetenv("SystemDrive")))); +#else + return QStorageInfo(QStringLiteral("/")); +#endif + }; protected: #if defined(Q_OS_WIN) diff --git a/src/corelib/io/qstorageinfo_unix.cpp b/src/corelib/io/qstorageinfo_unix.cpp index 34ff8bff844..3c80c314530 100644 --- a/src/corelib/io/qstorageinfo_unix.cpp +++ b/src/corelib/io/qstorageinfo_unix.cpp @@ -15,18 +15,9 @@ #include #include -#if defined(Q_OS_LINUX) -# include "qstorageinfo_linux_p.h" -#endif - #if defined(Q_OS_BSD4) # include # include -#elif defined(Q_OS_ANDROID) -# include -# include -#elif defined(Q_OS_LINUX) -# include #elif defined(Q_OS_HURD) # include # include @@ -60,12 +51,6 @@ # if !defined(_STATFS_F_FLAGS) && !defined(Q_OS_NETBSD) # define _STATFS_F_FLAGS 1 # endif -#elif defined(Q_OS_ANDROID) -# define QT_STATFS ::statfs -# define QT_STATFSBUF struct statfs -# if !defined(ST_RDONLY) -# define ST_RDONLY 1 // hack for missing define on Android -# endif #elif defined(Q_OS_HAIKU) # define QT_STATFSBUF struct statvfs # define QT_STATFS ::statvfs @@ -382,50 +367,9 @@ inline QByteArray QStorageIterator::subvolume() const } #endif -#ifdef Q_OS_LINUX -// udev encodes the labels with ID_LABEL_FS_ENC which is done with -// blkid_encode_string(). Within this function some 1-byte utf-8 -// characters not considered safe (e.g. '\' or ' ') are encoded as hex -static QString decodeFsEncString(const QString &str) -{ - QString decoded; - decoded.reserve(str.size()); - - int i = 0; - while (i < str.size()) { - if (i <= str.size() - 4) { // we need at least four characters \xAB - if (QStringView{str}.sliced(i).startsWith("\\x"_L1)) { - bool bOk; - const int code = QStringView{str}.mid(i+2, 2).toInt(&bOk, 16); - if (bOk && code >= 0x20 && code < 0x80) { - decoded += QChar(code); - i += 4; - continue; - } - } - } - decoded += str.at(i); - ++i; - } - return decoded; -} -#endif - static inline QString retrieveLabel(const QByteArray &device) { -#ifdef Q_OS_LINUX - static const char pathDiskByLabel[] = "/dev/disk/by-label"; - - QFileInfo devinfo(QFile::decodeName(device)); - QString devicePath = devinfo.canonicalFilePath(); - - QDirIterator it(QLatin1StringView(pathDiskByLabel), QDir::NoDotAndDotDot); - while (it.hasNext()) { - QFileInfo fileInfo = it.nextFileInfo(); - if (fileInfo.isSymLink() && fileInfo.symLinkTarget() == devicePath) - return decodeFsEncString(fileInfo.fileName()); - } -#elif defined Q_OS_HAIKU +#if defined Q_OS_HAIKU fs_info fsInfo; memset(&fsInfo, 0, sizeof(fsInfo)); @@ -484,67 +428,6 @@ void QStorageInfoPrivate::retrieveVolumeInfo() } } -#if defined(Q_OS_LINUX) -static std::vector parseMountInfo(FilterMountInfo filter = FilterMountInfo::All) -{ - QFile file(u"/proc/self/mountinfo"_s); - if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) - return {}; - - QByteArray mountinfo = file.readAll(); - file.close(); - - return doParseMountInfo(mountinfo, filter); -} - -void QStorageInfoPrivate::initRootPath() -{ - rootPath = QFileInfo(rootPath).canonicalFilePath(); - if (rootPath.isEmpty()) - return; - - std::vector infos = parseMountInfo(); - if (infos.empty()) { - rootPath = u'/'; - return; - } - - qsizetype maxLength = 0; - const QString oldRootPath = rootPath; - rootPath.clear(); - - for (auto &info : infos) { - // we try to find most suitable entry - qsizetype mpSize = info.mountPoint.size(); - if (isParentOf(info.mountPoint, oldRootPath) && maxLength < mpSize) { - maxLength = mpSize; - rootPath = info.mountPoint; - device = info.device; - fileSystemType = info.fsType; - subvolume = info.fsRoot; - } - } -} - -QList QStorageInfoPrivate::mountedVolumes() -{ - std::vector infos = parseMountInfo(FilterMountInfo::Filtered); - if (infos.empty()) - return QList{root()}; - - QList volumes; - for (MountInfo &info : infos) { - QStorageInfo storage(info.mountPoint); - storage.d->device = info.device; - storage.d->fileSystemType = info.fsType; - storage.d->subvolume = info.fsRoot; - if (storage.bytesTotal() == 0 && storage != root()) - continue; - volumes.push_back(storage); - } - return volumes; -} -#else // defined(Q_OS_LINUX) void QStorageInfoPrivate::initRootPath() { rootPath = QFileInfo(rootPath).canonicalFilePath(); @@ -600,11 +483,5 @@ QList QStorageInfoPrivate::mountedVolumes() return volumes; } -#endif // defined(Q_OS_LINUX) - -QStorageInfo QStorageInfoPrivate::root() -{ - return QStorageInfo(QStringLiteral("/")); -} QT_END_NAMESPACE diff --git a/src/corelib/io/qstorageinfo_win.cpp b/src/corelib/io/qstorageinfo_win.cpp index 418a8b77f8b..0d2d34898cf 100644 --- a/src/corelib/io/qstorageinfo_win.cpp +++ b/src/corelib/io/qstorageinfo_win.cpp @@ -170,11 +170,6 @@ QList QStorageInfoPrivate::mountedVolumes() return volumes; } -QStorageInfo QStorageInfoPrivate::root() -{ - return QStorageInfo(QDir::fromNativeSeparators(QFile::decodeName(qgetenv("SystemDrive")))); -} - bool QStorageInfoPrivate::queryStorageProperty() { QString path = QDir::toNativeSeparators(uR"(\\.\)" + rootPath);