1// Copyright (C) 2021 The Qt Company Ltd.
2// Copyright (C) 2014 Ivan Komissarov <ABBAPOH@gmail.com>
3// Copyright (C) 2016 Intel Corporation.
4// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
5
6#include "qstorageinfo_p.h"
7
8#include <QtCore/qdiriterator.h>
9#include <QtCore/qfileinfo.h>
10#include <QtCore/qtextstream.h>
11
12#include <QtCore/private/qcore_unix_p.h>
13#include <QtCore/private/qlocale_tools_p.h>
14
15#include <errno.h>
16#include <sys/stat.h>
17
18#if defined(Q_OS_BSD4)
19# include <sys/mount.h>
20# include <sys/statvfs.h>
21#elif defined(Q_OS_ANDROID)
22# include <sys/mount.h>
23# include <sys/vfs.h>
24# include <mntent.h>
25#elif defined(Q_OS_LINUX) || defined(Q_OS_HURD)
26# include <mntent.h>
27# include <sys/statvfs.h>
28# include <sys/sysmacros.h>
29#elif defined(Q_OS_SOLARIS)
30# include <sys/mnttab.h>
31# include <sys/statvfs.h>
32#elif defined(Q_OS_HAIKU)
33# include <Directory.h>
34# include <Path.h>
35# include <Volume.h>
36# include <VolumeRoster.h>
37# include <fs_info.h>
38# include <sys/statvfs.h>
39#else
40# include <sys/statvfs.h>
41#endif
42
43#if defined(Q_OS_BSD4)
44# if defined(Q_OS_NETBSD)
45# define QT_STATFSBUF struct statvfs
46# define QT_STATFS ::statvfs
47# else
48# define QT_STATFSBUF struct statfs
49# define QT_STATFS ::statfs
50# endif
51
52# if !defined(ST_RDONLY)
53# define ST_RDONLY MNT_RDONLY
54# endif
55# if !defined(_STATFS_F_FLAGS) && !defined(Q_OS_NETBSD)
56# define _STATFS_F_FLAGS 1
57# endif
58#elif defined(Q_OS_ANDROID)
59# define QT_STATFS ::statfs
60# define QT_STATFSBUF struct statfs
61# if !defined(ST_RDONLY)
62# define ST_RDONLY 1 // hack for missing define on Android
63# endif
64#elif defined(Q_OS_HAIKU)
65# define QT_STATFSBUF struct statvfs
66# define QT_STATFS ::statvfs
67#else
68# if defined(QT_LARGEFILE_SUPPORT)
69# define QT_STATFSBUF struct statvfs64
70# define QT_STATFS ::statvfs64
71# else
72# define QT_STATFSBUF struct statvfs
73# define QT_STATFS ::statvfs
74# endif // QT_LARGEFILE_SUPPORT
75#endif // Q_OS_BSD4
76
77#if __has_include(<paths.h>)
78# include <paths.h>
79#endif
80#ifndef _PATH_MOUNTED
81# define _PATH_MOUNTED "/etc/mnttab"
82#endif
83
84QT_BEGIN_NAMESPACE
85
86using namespace Qt::StringLiterals;
87
88class QStorageIterator
89{
90public:
91 QStorageIterator();
92 ~QStorageIterator();
93
94 inline bool isValid() const;
95 inline bool next();
96 inline QString rootPath() const;
97 inline QByteArray fileSystemType() const;
98 inline QByteArray device() const;
99 inline QByteArray options() const;
100 inline QByteArray subvolume() const;
101private:
102#if defined(Q_OS_BSD4)
103 QT_STATFSBUF *stat_buf;
104 int entryCount;
105 int currentIndex;
106#elif defined(Q_OS_SOLARIS)
107 FILE *fp;
108 mnttab mnt;
109#elif defined(Q_OS_ANDROID)
110 QFile file;
111 QByteArray m_rootPath;
112 QByteArray m_fileSystemType;
113 QByteArray m_device;
114 QByteArray m_options;
115#elif defined(Q_OS_LINUX) || defined(Q_OS_HURD)
116 struct mountinfoent : public mntent {
117 // Details from proc(5) section from /proc/<pid>/mountinfo:
118 //(1) mount ID: a unique ID for the mount (may be reused after umount(2)).
119 int mount_id;
120 //(2) parent ID: the ID of the parent mount (or of self for the top of the mount tree).
121// int parent_id;
122 //(3) major:minor: the value of st_dev for files on this filesystem (see stat(2)).
123 dev_t rdev;
124 //(4) root: the pathname of the directory in the filesystem which forms the root of this mount.
125 char *subvolume;
126 //(5) mount point: the pathname of the mount point relative to the process's root directory.
127// char *mnt_dir; // in mntent
128 //(6) mount options: per-mount options.
129// char *mnt_opts; // in mntent
130 //(7) optional fields: zero or more fields of the form "tag[:value]"; see below.
131// int flags;
132 //(8) separator: the end of the optional fields is marked by a single hyphen.
133
134 //(9) filesystem type: the filesystem type in the form "type[.subtype]".
135// char *mnt_type; // in mntent
136 //(10) mount source: filesystem-specific information or "none".
137// char *mnt_fsname; // in mntent
138 //(11) super options: per-superblock options.
139 char *superopts;
140 };
141
142 FILE *fp;
143 QByteArray buffer;
144 mountinfoent mnt;
145 bool usingMountinfo;
146#elif defined(Q_OS_HAIKU)
147 BVolumeRoster m_volumeRoster;
148
149 QByteArray m_rootPath;
150 QByteArray m_fileSystemType;
151 QByteArray m_device;
152#endif
153};
154
155template <typename String>
156static bool isParentOf(const String &parent, const QString &dirName)
157{
158 return dirName.startsWith(parent) &&
159 (dirName.size() == parent.size() || dirName.at(i: parent.size()) == u'/' ||
160 parent.size() == 1);
161}
162
163static bool shouldIncludeFs(const QStorageIterator &it)
164{
165 /*
166 * This function implements a heuristic algorithm to determine whether a
167 * given mount should be reported to the user. Our objective is to list
168 * only entries that the end-user would find useful.
169 *
170 * We therefore ignore:
171 * - mounted in /dev, /proc, /sys: special mounts
172 * (this will catch /sys/fs/cgroup, /proc/sys/fs/binfmt_misc, /dev/pts,
173 * some of which are tmpfs on Linux)
174 * - mounted in /var/run or /var/lock: most likely pseudofs
175 * (on earlier systemd versions, /var/run was a bind-mount of /run, so
176 * everything would be unnecessarily duplicated)
177 * - filesystem type is "rootfs": artifact of the root-pivot on some Linux
178 * initrd
179 * - if the filesystem total size is zero, it's a pseudo-fs (not checked here).
180 */
181
182 QString mountDir = it.rootPath();
183 if (isParentOf(parent: "/dev"_L1, dirName: mountDir)
184 || isParentOf(parent: "/proc"_L1, dirName: mountDir)
185 || isParentOf(parent: "/sys"_L1, dirName: mountDir)
186 || isParentOf(parent: "/var/run"_L1, dirName: mountDir)
187 || isParentOf(parent: "/var/lock"_L1, dirName: mountDir)) {
188 return false;
189 }
190
191#if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)
192 if (it.fileSystemType() == "rootfs")
193 return false;
194#endif
195
196 // size checking in mountedVolumes()
197 return true;
198}
199
200#if defined(Q_OS_BSD4)
201
202#ifndef MNT_NOWAIT
203# define MNT_NOWAIT 0
204#endif
205
206inline QStorageIterator::QStorageIterator()
207 : entryCount(::getmntinfo(&stat_buf, MNT_NOWAIT)),
208 currentIndex(-1)
209{
210}
211
212inline QStorageIterator::~QStorageIterator()
213{
214}
215
216inline bool QStorageIterator::isValid() const
217{
218 return entryCount != -1;
219}
220
221inline bool QStorageIterator::next()
222{
223 return ++currentIndex < entryCount;
224}
225
226inline QString QStorageIterator::rootPath() const
227{
228 return QFile::decodeName(stat_buf[currentIndex].f_mntonname);
229}
230
231inline QByteArray QStorageIterator::fileSystemType() const
232{
233 return QByteArray(stat_buf[currentIndex].f_fstypename);
234}
235
236inline QByteArray QStorageIterator::device() const
237{
238 return QByteArray(stat_buf[currentIndex].f_mntfromname);
239}
240
241inline QByteArray QStorageIterator::options() const
242{
243 return QByteArray();
244}
245
246inline QByteArray QStorageIterator::subvolume() const
247{
248 return QByteArray();
249}
250#elif defined(Q_OS_SOLARIS)
251
252inline QStorageIterator::QStorageIterator()
253{
254 const int fd = qt_safe_open(_PATH_MOUNTED, O_RDONLY);
255 fp = ::fdopen(fd, "r");
256}
257
258inline QStorageIterator::~QStorageIterator()
259{
260 if (fp)
261 ::fclose(fp);
262}
263
264inline bool QStorageIterator::isValid() const
265{
266 return fp != nullptr;
267}
268
269inline bool QStorageIterator::next()
270{
271 return ::getmntent(fp, &mnt) == 0;
272}
273
274inline QString QStorageIterator::rootPath() const
275{
276 return QFile::decodeName(mnt.mnt_mountp);
277}
278
279inline QByteArray QStorageIterator::fileSystemType() const
280{
281 return QByteArray(mnt.mnt_fstype);
282}
283
284inline QByteArray QStorageIterator::device() const
285{
286 return QByteArray(mnt.mnt_mntopts);
287}
288
289inline QByteArray QStorageIterator::subvolume() const
290{
291 return QByteArray();
292}
293#elif defined(Q_OS_ANDROID)
294
295inline QStorageIterator::QStorageIterator()
296{
297 file.setFileName(QString::fromUtf8(_PATH_MOUNTED));
298 file.open(QIODevice::ReadOnly | QIODevice::Text);
299}
300
301inline QStorageIterator::~QStorageIterator()
302{
303}
304
305inline bool QStorageIterator::isValid() const
306{
307 return file.isOpen();
308}
309
310inline bool QStorageIterator::next()
311{
312 QList<QByteArray> data;
313 // If file is virtual, file.readLine() may succeed even when file.atEnd().
314 do {
315 const QByteArray line = file.readLine();
316 if (line.isEmpty() && file.atEnd())
317 return false;
318 data = line.split(' ');
319 } while (data.count() < 4);
320
321 m_device = data.at(0);
322 m_rootPath = data.at(1);
323 m_fileSystemType = data.at(2);
324 m_options = data.at(3);
325
326 return true;
327}
328
329inline QString QStorageIterator::rootPath() const
330{
331 return QFile::decodeName(m_rootPath);
332}
333
334inline QByteArray QStorageIterator::fileSystemType() const
335{
336 return m_fileSystemType;
337}
338
339inline QByteArray QStorageIterator::device() const
340{
341 return m_device;
342}
343
344inline QByteArray QStorageIterator::options() const
345{
346 return m_options;
347}
348
349inline QByteArray QStorageIterator::subvolume() const
350{
351 return QByteArray();
352}
353#elif defined(Q_OS_LINUX) || defined(Q_OS_HURD)
354
355static const int bufferSize = 1024; // 2 paths (mount point+device) and metainfo;
356 // should be enough
357
358inline QStorageIterator::QStorageIterator() :
359 buffer(QByteArray(bufferSize, 0))
360{
361 fp = nullptr;
362
363#ifdef Q_OS_LINUX
364 // first, try to open /proc/self/mountinfo, which has more details
365 fp = ::fopen(filename: "/proc/self/mountinfo", modes: "re");
366#endif
367 if (fp) {
368 usingMountinfo = true;
369 } else {
370 usingMountinfo = false;
371 fp = ::setmntent(_PATH_MOUNTED, mode: "r");
372 }
373}
374
375inline QStorageIterator::~QStorageIterator()
376{
377 if (fp) {
378 if (usingMountinfo)
379 ::fclose(stream: fp);
380 else
381 ::endmntent(stream: fp);
382 }
383}
384
385inline bool QStorageIterator::isValid() const
386{
387 return fp != nullptr;
388}
389
390inline bool QStorageIterator::next()
391{
392 mnt.subvolume = nullptr;
393 mnt.superopts = nullptr;
394 if (!usingMountinfo)
395 return ::getmntent_r(stream: fp, result: &mnt, buffer: buffer.data(), bufsize: buffer.size()) != nullptr;
396
397 // Helper function to parse paths that the kernel inserts escape sequences
398 // for. The unescaped string is left at \a src and is properly
399 // NUL-terminated. Returns a pointer to the delimiter that terminated the
400 // path, or nullptr if it failed.
401 auto parseMangledPath = [](char *src) {
402 // The kernel escapes with octal the following characters:
403 // space ' ', tab '\t', backslask '\\', and newline '\n'
404 char *dst = src;
405 while (*src) {
406 switch (*src) {
407 case ' ':
408 // Unescaped space: end of the field.
409 *dst = '\0';
410 return src;
411
412 default:
413 *dst++ = *src++;
414 break;
415
416 case '\\':
417 // It always uses exactly three octal characters.
418 ++src;
419 char c = (*src++ - '0') << 6;
420 c |= (*src++ - '0') << 3;
421 c |= (*src++ - '0');
422 *dst++ = c;
423 break;
424 }
425 }
426
427 // Found a NUL before the end of the field.
428 src = nullptr;
429 return src;
430 };
431
432 char *ptr = buffer.data();
433 if (fgets(s: ptr, n: buffer.size(), stream: fp) == nullptr)
434 return false;
435
436 size_t len = strlen(s: ptr);
437 if (len == 0)
438 return false;
439 while (Q_UNLIKELY(ptr[len - 1] != '\n' && !feof(fp))) {
440 // buffer wasn't large enough. Enlarge and try again.
441 // (we're readidng from the kernel, so OOM is unlikely)
442 buffer.resize(size: (buffer.size() + 4096) & ~4095);
443 ptr = buffer.data();
444 if (fgets(s: ptr + len, n: buffer.size() - len, stream: fp) == nullptr)
445 return false;
446
447 len += strlen(s: ptr + len);
448 Q_ASSERT(len < size_t(buffer.size()));
449 }
450 ptr[len - 1] = '\0';
451 const char *const stop = ptr + len - 1;
452
453 // parse the line
454 mnt.mnt_freq = 0;
455 mnt.mnt_passno = 0;
456
457 auto r = qstrntoll(nptr: ptr, size: stop - ptr, base: 10);
458 if (!r.ok())
459 return false;
460 mnt.mount_id = r.result;
461
462 ptr += r.used;
463 r = qstrntoll(nptr: ptr, size: stop - ptr, base: 10);
464 if (!r.ok())
465 return false;
466 // parent_id = r.result; // member currently not in use
467
468 ptr += r.used;
469 r = qstrntoll(nptr: ptr, size: stop - ptr, base: 10);
470 if (!r.ok())
471 return false;
472 ptr += r.used;
473 if (*ptr != ':')
474 return false;
475 int rdevmajor = r.result;
476 ++ptr; // Skip over the ':'
477 r = qstrntoll(nptr: ptr, size: stop - ptr, base: 10);
478 if (!r.ok())
479 return false;
480 mnt.rdev = makedev(rdevmajor, r.result);
481
482 ptr += r.used;
483 if (*ptr != ' ')
484 return false;
485
486 mnt.subvolume = ++ptr;
487 ptr = parseMangledPath(ptr);
488 if (!ptr)
489 return false;
490
491 // unset a subvolume of "/" -- it's not a *sub* volume
492 if (mnt.subvolume + 1 == ptr)
493 *mnt.subvolume = '\0';
494
495 mnt.mnt_dir = ++ptr;
496 ptr = parseMangledPath(ptr);
497 if (!ptr)
498 return false;
499
500 mnt.mnt_opts = ++ptr;
501 ptr = strchr(s: ptr, c: ' ');
502 if (!ptr)
503 return false;
504
505 // we don't parse the flags, so just find the separator
506 if (char *const dashed = strstr(haystack: ptr, needle: " - ")) {
507 *ptr = '\0';
508 ptr = dashed + strlen(s: " - ") - 1;
509 } else {
510 return false;
511 }
512
513 mnt.mnt_type = ++ptr;
514 ptr = strchr(s: ptr, c: ' ');
515 if (!ptr)
516 return false;
517 *ptr = '\0';
518
519 mnt.mnt_fsname = ++ptr;
520 ptr = parseMangledPath(ptr);
521 if (!ptr)
522 return false;
523
524 mnt.superopts = ++ptr;
525 ptr += strcspn(s: ptr, reject: " \n");
526 *ptr = '\0';
527
528 return true;
529}
530
531inline QString QStorageIterator::rootPath() const
532{
533 return QFile::decodeName(localFileName: mnt.mnt_dir);
534}
535
536inline QByteArray QStorageIterator::fileSystemType() const
537{
538 return QByteArray(mnt.mnt_type);
539}
540
541inline QByteArray QStorageIterator::device() const
542{
543#ifdef Q_OS_LINUX
544 // check that the device exists
545 if (mnt.mnt_fsname[0] == '/' && access(name: mnt.mnt_fsname, F_OK) != 0) {
546 // It doesn't, so let's try to resolve the dev_t from /dev/block.
547 // Note how strlen("4294967295") == digits10 + 1, so we need to add 1
548 // for each number, plus the ':'.
549 char buf[sizeof("/dev/block/") + 2 * std::numeric_limits<unsigned>::digits10 + 3];
550 QByteArray dev(PATH_MAX, Qt::Uninitialized);
551 char *devdata = dev.data();
552
553 snprintf(s: buf, maxlen: sizeof(buf), format: "/dev/block/%u:%u", major(mnt.rdev), minor(mnt.rdev));
554 if (realpath(name: buf, resolved: devdata)) {
555 dev.truncate(pos: strlen(s: devdata));
556 return dev;
557 }
558 }
559#endif
560 return QByteArray(mnt.mnt_fsname);
561}
562
563inline QByteArray QStorageIterator::options() const
564{
565 // Merge the two options, starting with the superblock options and letting
566 // the per-mount options override.
567 const char *superopts = mnt.superopts;
568
569 // Both mnt_opts and superopts start with "ro" or "rw", so we can skip the
570 // superblock's field (see show_mountinfo() in fs/proc_namespace.c).
571 if (superopts && superopts[0] == 'r') {
572 if (superopts[2] == '\0') // no other superopts besides "ro" / "rw"?
573 superopts = nullptr;
574 else if (superopts[2] == ',')
575 superopts += 3;
576 }
577
578 if (superopts)
579 return QByteArray(superopts) + ',' + mnt.mnt_opts;
580 return QByteArray(mnt.mnt_opts);
581}
582
583inline QByteArray QStorageIterator::subvolume() const
584{
585 return QByteArray(mnt.subvolume);
586}
587#elif defined(Q_OS_HAIKU)
588inline QStorageIterator::QStorageIterator()
589{
590}
591
592inline QStorageIterator::~QStorageIterator()
593{
594}
595
596inline bool QStorageIterator::isValid() const
597{
598 return true;
599}
600
601inline bool QStorageIterator::next()
602{
603 BVolume volume;
604
605 if (m_volumeRoster.GetNextVolume(&volume) != B_OK)
606 return false;
607
608 BDirectory directory;
609 if (volume.GetRootDirectory(&directory) != B_OK)
610 return false;
611
612 const BPath path(&directory);
613
614 fs_info fsInfo;
615 memset(&fsInfo, 0, sizeof(fsInfo));
616
617 if (fs_stat_dev(volume.Device(), &fsInfo) != 0)
618 return false;
619
620 m_rootPath = path.Path();
621 m_fileSystemType = QByteArray(fsInfo.fsh_name);
622
623 const QByteArray deviceName(fsInfo.device_name);
624 m_device = (deviceName.isEmpty() ? QByteArray::number(qint32(volume.Device())) : deviceName);
625
626 return true;
627}
628
629inline QString QStorageIterator::rootPath() const
630{
631 return QFile::decodeName(m_rootPath);
632}
633
634inline QByteArray QStorageIterator::fileSystemType() const
635{
636 return m_fileSystemType;
637}
638
639inline QByteArray QStorageIterator::device() const
640{
641 return m_device;
642}
643
644inline QByteArray QStorageIterator::options() const
645{
646 return QByteArray();
647}
648
649inline QByteArray QStorageIterator::subvolume() const
650{
651 return QByteArray();
652}
653#else
654
655inline QStorageIterator::QStorageIterator()
656{
657}
658
659inline QStorageIterator::~QStorageIterator()
660{
661}
662
663inline bool QStorageIterator::isValid() const
664{
665 return false;
666}
667
668inline bool QStorageIterator::next()
669{
670 return false;
671}
672
673inline QString QStorageIterator::rootPath() const
674{
675 return QString();
676}
677
678inline QByteArray QStorageIterator::fileSystemType() const
679{
680 return QByteArray();
681}
682
683inline QByteArray QStorageIterator::device() const
684{
685 return QByteArray();
686}
687
688inline QByteArray QStorageIterator::options() const
689{
690 return QByteArray();
691}
692
693inline QByteArray QStorageIterator::subvolume() const
694{
695 return QByteArray();
696}
697#endif
698
699void QStorageInfoPrivate::initRootPath()
700{
701 rootPath = QFileInfo(rootPath).canonicalFilePath();
702
703 if (rootPath.isEmpty())
704 return;
705
706 QStorageIterator it;
707 if (!it.isValid()) {
708 rootPath = QStringLiteral("/");
709 return;
710 }
711
712 int maxLength = 0;
713 const QString oldRootPath = rootPath;
714 rootPath.clear();
715
716 while (it.next()) {
717 const QString mountDir = it.rootPath();
718 const QByteArray fsName = it.fileSystemType();
719 // we try to find most suitable entry
720 if (isParentOf(parent: mountDir, dirName: oldRootPath) && maxLength < mountDir.size()) {
721 maxLength = mountDir.size();
722 rootPath = mountDir;
723 device = it.device();
724 fileSystemType = fsName;
725 subvolume = it.subvolume();
726 }
727 }
728}
729
730#ifdef Q_OS_LINUX
731// udev encodes the labels with ID_LABEL_FS_ENC which is done with
732// blkid_encode_string(). Within this function some 1-byte utf-8
733// characters not considered safe (e.g. '\' or ' ') are encoded as hex
734static QString decodeFsEncString(const QString &str)
735{
736 QString decoded;
737 decoded.reserve(asize: str.size());
738
739 int i = 0;
740 while (i < str.size()) {
741 if (i <= str.size() - 4) { // we need at least four characters \xAB
742 if (QStringView{str}.sliced(pos: i).startsWith(s: "\\x"_L1)) {
743 bool bOk;
744 const int code = QStringView{str}.mid(pos: i+2, n: 2).toInt(ok: &bOk, base: 16);
745 if (bOk && code >= 0x20 && code < 0x80) {
746 decoded += QChar(code);
747 i += 4;
748 continue;
749 }
750 }
751 }
752 decoded += str.at(i);
753 ++i;
754 }
755 return decoded;
756}
757#endif
758
759static inline QString retrieveLabel(const QByteArray &device)
760{
761#ifdef Q_OS_LINUX
762 static const char pathDiskByLabel[] = "/dev/disk/by-label";
763
764 QFileInfo devinfo(QFile::decodeName(localFileName: device));
765 QString devicePath = devinfo.canonicalFilePath();
766
767 QDirIterator it(QLatin1StringView(pathDiskByLabel), QDir::NoDotAndDotDot);
768 while (it.hasNext()) {
769 QFileInfo fileInfo = it.nextFileInfo();
770 if (fileInfo.isSymLink() && fileInfo.symLinkTarget() == devicePath)
771 return decodeFsEncString(str: fileInfo.fileName());
772 }
773#elif defined Q_OS_HAIKU
774 fs_info fsInfo;
775 memset(&fsInfo, 0, sizeof(fsInfo));
776
777 int32 pos = 0;
778 dev_t dev;
779 while ((dev = next_dev(&pos)) >= 0) {
780 if (fs_stat_dev(dev, &fsInfo) != 0)
781 continue;
782
783 if (qstrcmp(fsInfo.device_name, device.constData()) == 0)
784 return QString::fromLocal8Bit(fsInfo.volume_name);
785 }
786#else
787 Q_UNUSED(device);
788#endif
789
790 return QString();
791}
792
793void QStorageInfoPrivate::doStat()
794{
795 initRootPath();
796 if (rootPath.isEmpty())
797 return;
798
799 retrieveVolumeInfo();
800 name = retrieveLabel(device);
801}
802
803void QStorageInfoPrivate::retrieveVolumeInfo()
804{
805 QT_STATFSBUF statfs_buf;
806 int result;
807 EINTR_LOOP(result, QT_STATFS(QFile::encodeName(rootPath).constData(), &statfs_buf));
808 if (result == 0) {
809 valid = true;
810 ready = true;
811
812#if defined(Q_OS_INTEGRITY) || (defined(Q_OS_BSD4) && !defined(Q_OS_NETBSD)) || defined(Q_OS_RTEMS)
813 bytesTotal = statfs_buf.f_blocks * statfs_buf.f_bsize;
814 bytesFree = statfs_buf.f_bfree * statfs_buf.f_bsize;
815 bytesAvailable = statfs_buf.f_bavail * statfs_buf.f_bsize;
816#else
817 bytesTotal = statfs_buf.f_blocks * statfs_buf.f_frsize;
818 bytesFree = statfs_buf.f_bfree * statfs_buf.f_frsize;
819 bytesAvailable = statfs_buf.f_bavail * statfs_buf.f_frsize;
820#endif
821 blockSize = statfs_buf.f_bsize;
822#if defined(Q_OS_ANDROID) || defined(Q_OS_BSD4) || defined(Q_OS_INTEGRITY) || defined(Q_OS_RTEMS)
823#if defined(_STATFS_F_FLAGS)
824 readOnly = (statfs_buf.f_flags & ST_RDONLY) != 0;
825#endif
826#else
827 readOnly = (statfs_buf.f_flag & ST_RDONLY) != 0;
828#endif
829 }
830}
831
832QList<QStorageInfo> QStorageInfoPrivate::mountedVolumes()
833{
834 QStorageIterator it;
835 if (!it.isValid())
836 return QList<QStorageInfo>() << root();
837
838 QList<QStorageInfo> volumes;
839
840 while (it.next()) {
841 if (!shouldIncludeFs(it))
842 continue;
843
844 const QString mountDir = it.rootPath();
845 QStorageInfo info(mountDir);
846 info.d->device = it.device();
847 info.d->fileSystemType = it.fileSystemType();
848 info.d->subvolume = it.subvolume();
849 if (info.bytesTotal() == 0 && info != root())
850 continue;
851 volumes.append(t: info);
852 }
853
854 return volumes;
855}
856
857QStorageInfo QStorageInfoPrivate::root()
858{
859 return QStorageInfo(QStringLiteral("/"));
860}
861
862QT_END_NAMESPACE
863

source code of qtbase/src/corelib/io/qstorageinfo_unix.cpp