diff options
Diffstat (limited to 'src/corelib/io/qstorageinfo_unix.cpp')
-rw-r--r-- | src/corelib/io/qstorageinfo_unix.cpp | 468 |
1 files changed, 45 insertions, 423 deletions
diff --git a/src/corelib/io/qstorageinfo_unix.cpp b/src/corelib/io/qstorageinfo_unix.cpp index c58b7ce87d..9df098a389 100644 --- a/src/corelib/io/qstorageinfo_unix.cpp +++ b/src/corelib/io/qstorageinfo_unix.cpp @@ -5,7 +5,6 @@ #include "qstorageinfo_p.h" -#include <QtCore/qdiriterator.h> #include <QtCore/qfileinfo.h> #include <QtCore/qtextstream.h> @@ -18,11 +17,7 @@ #if defined(Q_OS_BSD4) # include <sys/mount.h> # include <sys/statvfs.h> -#elif defined(Q_OS_ANDROID) -# include <sys/mount.h> -# include <sys/vfs.h> -# include <mntent.h> -#elif defined(Q_OS_LINUX) || defined(Q_OS_HURD) +#elif defined(Q_OS_HURD) # include <mntent.h> # include <sys/statvfs.h> # include <sys/sysmacros.h> @@ -55,12 +50,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 @@ -106,43 +95,10 @@ private: #elif defined(Q_OS_SOLARIS) FILE *fp; mnttab mnt; -#elif defined(Q_OS_ANDROID) - QFile file; - QByteArray m_rootPath; - QByteArray m_fileSystemType; - QByteArray m_device; - QByteArray m_options; -#elif defined(Q_OS_LINUX) || defined(Q_OS_HURD) - struct mountinfoent : public mntent { - // Details from proc(5) section from /proc/<pid>/mountinfo: - //(1) mount ID: a unique ID for the mount (may be reused after umount(2)). - int mount_id; - //(2) parent ID: the ID of the parent mount (or of self for the top of the mount tree). -// int parent_id; - //(3) major:minor: the value of st_dev for files on this filesystem (see stat(2)). - dev_t rdev; - //(4) root: the pathname of the directory in the filesystem which forms the root of this mount. - char *subvolume; - //(5) mount point: the pathname of the mount point relative to the process's root directory. -// char *mnt_dir; // in mntent - //(6) mount options: per-mount options. -// char *mnt_opts; // in mntent - //(7) optional fields: zero or more fields of the form "tag[:value]"; see below. -// int flags; - //(8) separator: the end of the optional fields is marked by a single hyphen. - - //(9) filesystem type: the filesystem type in the form "type[.subtype]". -// char *mnt_type; // in mntent - //(10) mount source: filesystem-specific information or "none". -// char *mnt_fsname; // in mntent - //(11) super options: per-superblock options. - char *superopts; - }; - +#elif defined(Q_OS_HURD) FILE *fp; QByteArray buffer; mountinfoent mnt; - bool usingMountinfo; #elif defined(Q_OS_HAIKU) BVolumeRoster m_volumeRoster; @@ -152,51 +108,6 @@ private: #endif }; -template <typename String> -static bool isParentOf(const String &parent, const QString &dirName) -{ - return dirName.startsWith(parent) && - (dirName.size() == parent.size() || dirName.at(parent.size()) == u'/' || - parent.size() == 1); -} - -static bool shouldIncludeFs(const QStorageIterator &it) -{ - /* - * This function implements a heuristic algorithm to determine whether a - * given mount should be reported to the user. Our objective is to list - * only entries that the end-user would find useful. - * - * We therefore ignore: - * - mounted in /dev, /proc, /sys: special mounts - * (this will catch /sys/fs/cgroup, /proc/sys/fs/binfmt_misc, /dev/pts, - * some of which are tmpfs on Linux) - * - mounted in /var/run or /var/lock: most likely pseudofs - * (on earlier systemd versions, /var/run was a bind-mount of /run, so - * everything would be unnecessarily duplicated) - * - filesystem type is "rootfs": artifact of the root-pivot on some Linux - * initrd - * - if the filesystem total size is zero, it's a pseudo-fs (not checked here). - */ - - QString mountDir = it.rootPath(); - if (isParentOf("/dev"_L1, mountDir) - || isParentOf("/proc"_L1, mountDir) - || isParentOf("/sys"_L1, mountDir) - || isParentOf("/var/run"_L1, mountDir) - || isParentOf("/var/lock"_L1, mountDir)) { - return false; - } - -#if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID) - if (it.fileSystemType() == "rootfs") - return false; -#endif - - // size checking in mountedVolumes() - return true; -} - #if defined(Q_OS_BSD4) #ifndef MNT_NOWAIT @@ -290,67 +201,8 @@ inline QByteArray QStorageIterator::subvolume() const { return QByteArray(); } -#elif defined(Q_OS_ANDROID) -inline QStorageIterator::QStorageIterator() -{ - file.setFileName(QString::fromUtf8(_PATH_MOUNTED)); - file.open(QIODevice::ReadOnly | QIODevice::Text); -} - -inline QStorageIterator::~QStorageIterator() -{ -} - -inline bool QStorageIterator::isValid() const -{ - return file.isOpen(); -} - -inline bool QStorageIterator::next() -{ - QList<QByteArray> data; - // If file is virtual, file.readLine() may succeed even when file.atEnd(). - do { - const QByteArray line = file.readLine(); - if (line.isEmpty() && file.atEnd()) - return false; - data = line.split(' '); - } while (data.count() < 4); - - m_device = data.at(0); - m_rootPath = data.at(1); - m_fileSystemType = data.at(2); - m_options = data.at(3); - - return true; -} - -inline QString QStorageIterator::rootPath() const -{ - return QFile::decodeName(m_rootPath); -} - -inline QByteArray QStorageIterator::fileSystemType() const -{ - return m_fileSystemType; -} - -inline QByteArray QStorageIterator::device() const -{ - return m_device; -} - -inline QByteArray QStorageIterator::options() const -{ - return m_options; -} - -inline QByteArray QStorageIterator::subvolume() const -{ - return QByteArray(); -} -#elif defined(Q_OS_LINUX) || defined(Q_OS_HURD) +#elif defined(Q_OS_HURD) static const int bufferSize = 1024; // 2 paths (mount point+device) and metainfo; // should be enough @@ -358,28 +210,13 @@ static const int bufferSize = 1024; // 2 paths (mount point+device) and metainfo inline QStorageIterator::QStorageIterator() : buffer(QByteArray(bufferSize, 0)) { - fp = nullptr; - -#ifdef Q_OS_LINUX - // first, try to open /proc/self/mountinfo, which has more details - fp = ::fopen("/proc/self/mountinfo", "re"); -#endif - if (fp) { - usingMountinfo = true; - } else { - usingMountinfo = false; - fp = ::setmntent(_PATH_MOUNTED, "r"); - } + fp = ::setmntent(_PATH_MOUNTED, "r"); } inline QStorageIterator::~QStorageIterator() { - if (fp) { - if (usingMountinfo) - ::fclose(fp); - else - ::endmntent(fp); - } + if (fp) + ::endmntent(fp); } inline bool QStorageIterator::isValid() const @@ -389,143 +226,7 @@ inline bool QStorageIterator::isValid() const inline bool QStorageIterator::next() { - mnt.subvolume = nullptr; - mnt.superopts = nullptr; - if (!usingMountinfo) - return ::getmntent_r(fp, &mnt, buffer.data(), buffer.size()) != nullptr; - - // Helper function to parse paths that the kernel inserts escape sequences - // for. The unescaped string is left at \a src and is properly - // NUL-terminated. Returns a pointer to the delimiter that terminated the - // path, or nullptr if it failed. - auto parseMangledPath = [](char *src) { - // The kernel escapes with octal the following characters: - // space ' ', tab '\t', backslask '\\', and newline '\n' - char *dst = src; - while (*src) { - switch (*src) { - case ' ': - // Unescaped space: end of the field. - *dst = '\0'; - return src; - - default: - *dst++ = *src++; - break; - - case '\\': - // It always uses exactly three octal characters. - ++src; - char c = (*src++ - '0') << 6; - c |= (*src++ - '0') << 3; - c |= (*src++ - '0'); - *dst++ = c; - break; - } - } - - // Found a NUL before the end of the field. - src = nullptr; - return src; - }; - - char *ptr = buffer.data(); - if (fgets(ptr, buffer.size(), fp) == nullptr) - return false; - - size_t len = strlen(ptr); - if (len == 0) - return false; - while (Q_UNLIKELY(ptr[len - 1] != '\n' && !feof(fp))) { - // buffer wasn't large enough. Enlarge and try again. - // (we're readidng from the kernel, so OOM is unlikely) - buffer.resize((buffer.size() + 4096) & ~4095); - ptr = buffer.data(); - if (fgets(ptr + len, buffer.size() - len, fp) == nullptr) - return false; - - len += strlen(ptr + len); - Q_ASSERT(len < size_t(buffer.size())); - } - ptr[len - 1] = '\0'; - const char *const stop = ptr + len - 1; - - // parse the line - mnt.mnt_freq = 0; - mnt.mnt_passno = 0; - - auto r = qstrntoll(ptr, stop - ptr, 10); - if (!r.ok()) - return false; - mnt.mount_id = r.result; - - ptr += r.used; - r = qstrntoll(ptr, stop - ptr, 10); - if (!r.ok()) - return false; - // parent_id = r.result; // member currently not in use - - ptr += r.used; - r = qstrntoll(ptr, stop - ptr, 10); - if (!r.ok()) - return false; - ptr += r.used; - if (*ptr != ':') - return false; - int rdevmajor = r.result; - ++ptr; // Skip over the ':' - r = qstrntoll(ptr, stop - ptr, 10); - if (!r.ok()) - return false; - mnt.rdev = makedev(rdevmajor, r.result); - - ptr += r.used; - if (*ptr != ' ') - return false; - - mnt.subvolume = ++ptr; - ptr = parseMangledPath(ptr); - if (!ptr) - return false; - - // unset a subvolume of "/" -- it's not a *sub* volume - if (mnt.subvolume + 1 == ptr) - *mnt.subvolume = '\0'; - - mnt.mnt_dir = ++ptr; - ptr = parseMangledPath(ptr); - if (!ptr) - return false; - - mnt.mnt_opts = ++ptr; - ptr = strchr(ptr, ' '); - if (!ptr) - return false; - - // we don't parse the flags, so just find the separator - if (char *const dashed = strstr(ptr, " - ")) { - *ptr = '\0'; - ptr = dashed + strlen(" - ") - 1; - } else { - return false; - } - - mnt.mnt_type = ++ptr; - ptr = strchr(ptr, ' '); - if (!ptr) - return false; - *ptr = '\0'; - - mnt.mnt_fsname = ++ptr; - ptr = parseMangledPath(ptr); - if (!ptr) - return false; - - mnt.superopts = ++ptr; - ptr += strcspn(ptr, " \n"); - *ptr = '\0'; - - return true; + return ::getmntent_r(fp, &mnt, buffer.data(), buffer.size()) != nullptr; } inline QString QStorageIterator::rootPath() const @@ -540,49 +241,17 @@ inline QByteArray QStorageIterator::fileSystemType() const inline QByteArray QStorageIterator::device() const { -#ifdef Q_OS_LINUX - // check that the device exists - if (mnt.mnt_fsname[0] == '/' && access(mnt.mnt_fsname, F_OK) != 0) { - // It doesn't, so let's try to resolve the dev_t from /dev/block. - // Note how strlen("4294967295") == digits10 + 1, so we need to add 1 - // for each number, plus the ':'. - char buf[sizeof("/dev/block/") + 2 * std::numeric_limits<unsigned>::digits10 + 3]; - QByteArray dev(PATH_MAX, Qt::Uninitialized); - char *devdata = dev.data(); - - snprintf(buf, sizeof(buf), "/dev/block/%u:%u", major(mnt.rdev), minor(mnt.rdev)); - if (realpath(buf, devdata)) { - dev.truncate(strlen(devdata)); - return dev; - } - } -#endif return QByteArray(mnt.mnt_fsname); } inline QByteArray QStorageIterator::options() const { - // Merge the two options, starting with the superblock options and letting - // the per-mount options override. - const char *superopts = mnt.superopts; - - // Both mnt_opts and superopts start with "ro" or "rw", so we can skip the - // superblock's field (see show_mountinfo() in fs/proc_namespace.c). - if (superopts && superopts[0] == 'r') { - if (superopts[2] == '\0') // no other superopts besides "ro" / "rw"? - superopts = nullptr; - else if (superopts[2] == ',') - superopts += 3; - } - - if (superopts) - return QByteArray(superopts) + ',' + mnt.mnt_opts; return QByteArray(mnt.mnt_opts); } inline QByteArray QStorageIterator::subvolume() const { - return QByteArray(mnt.subvolume); + return QByteArray(); } #elif defined(Q_OS_HAIKU) inline QStorageIterator::QStorageIterator() @@ -650,6 +319,7 @@ inline QByteArray QStorageIterator::subvolume() const { return QByteArray(); } + #else inline QStorageIterator::QStorageIterator() @@ -696,83 +366,9 @@ inline QByteArray QStorageIterator::subvolume() const } #endif -void QStorageInfoPrivate::initRootPath() -{ - rootPath = QFileInfo(rootPath).canonicalFilePath(); - - if (rootPath.isEmpty()) - return; - - QStorageIterator it; - if (!it.isValid()) { - rootPath = QStringLiteral("/"); - return; - } - - int maxLength = 0; - const QString oldRootPath = rootPath; - rootPath.clear(); - - while (it.next()) { - const QString mountDir = it.rootPath(); - const QByteArray fsName = it.fileSystemType(); - // we try to find most suitable entry - if (isParentOf(mountDir, oldRootPath) && maxLength < mountDir.size()) { - maxLength = mountDir.size(); - rootPath = mountDir; - device = it.device(); - fileSystemType = fsName; - subvolume = it.subvolume(); - } - } -} - -#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); - // only decode characters between 0x20 and 0x7f but not - // the backslash to prevent collisions - if (bOk && code >= 0x20 && code < 0x80 && code != '\\') { - 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)); @@ -806,7 +402,7 @@ void QStorageInfoPrivate::retrieveVolumeInfo() { QT_STATFSBUF statfs_buf; int result; - EINTR_LOOP(result, QT_STATFS(QFile::encodeName(rootPath).constData(), &statfs_buf)); + QT_EINTR_LOOP(result, QT_STATFS(QFile::encodeName(rootPath).constData(), &statfs_buf)); if (result == 0) { valid = true; ready = true; @@ -820,7 +416,7 @@ void QStorageInfoPrivate::retrieveVolumeInfo() bytesFree = statfs_buf.f_bfree * statfs_buf.f_frsize; bytesAvailable = statfs_buf.f_bavail * statfs_buf.f_frsize; #endif - blockSize = statfs_buf.f_bsize; + blockSize = int(statfs_buf.f_bsize); #if defined(Q_OS_ANDROID) || defined(Q_OS_BSD4) || defined(Q_OS_INTEGRITY) || defined(Q_OS_RTEMS) #if defined(_STATFS_F_FLAGS) readOnly = (statfs_buf.f_flags & ST_RDONLY) != 0; @@ -831,6 +427,37 @@ void QStorageInfoPrivate::retrieveVolumeInfo() } } +void QStorageInfoPrivate::initRootPath() +{ + rootPath = QFileInfo(rootPath).canonicalFilePath(); + + if (rootPath.isEmpty()) + return; + + QStorageIterator it; + if (!it.isValid()) { + rootPath = QStringLiteral("/"); + return; + } + + int maxLength = 0; + const QString oldRootPath = rootPath; + rootPath.clear(); + + while (it.next()) { + const QString mountDir = it.rootPath(); + const QByteArray fsName = it.fileSystemType(); + // we try to find most suitable entry + if (maxLength < mountDir.size() && isParentOf(mountDir, oldRootPath)) { + maxLength = mountDir.size(); + rootPath = mountDir; + device = it.device(); + fileSystemType = fsName; + subvolume = it.subvolume(); + } + } +} + QList<QStorageInfo> QStorageInfoPrivate::mountedVolumes() { QStorageIterator it; @@ -840,7 +467,7 @@ QList<QStorageInfo> QStorageInfoPrivate::mountedVolumes() QList<QStorageInfo> volumes; while (it.next()) { - if (!shouldIncludeFs(it)) + if (!shouldIncludeFs(it.rootPath(), it.fileSystemType())) continue; const QString mountDir = it.rootPath(); @@ -848,7 +475,7 @@ QList<QStorageInfo> QStorageInfoPrivate::mountedVolumes() info.d->device = it.device(); info.d->fileSystemType = it.fileSystemType(); info.d->subvolume = it.subvolume(); - if (info.bytesTotal() == 0 && info != root()) + if (info.bytesTotal() <= 0 && info != root()) continue; volumes.append(info); } @@ -856,9 +483,4 @@ QList<QStorageInfo> QStorageInfoPrivate::mountedVolumes() return volumes; } -QStorageInfo QStorageInfoPrivate::root() -{ - return QStorageInfo(QStringLiteral("/")); -} - QT_END_NAMESPACE |