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