Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qstorageinfo_linux_p.h
Go to the documentation of this file.
1// Copyright (C) 2021 The Qt Company Ltd.
2// Copyright (C) 2014 Ivan Komissarov <ABBAPOH@gmail.com>
3// Copyright (C) 2016 Intel Corporation.
4// Copyright (C) 2023 Ahmad Samir <a.samirh78@gmail.com>
5// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
6
7#ifndef QSTORAGEINFO_LINUX_P_H
8#define QSTORAGEINFO_LINUX_P_H
9
10//
11// W A R N I N G
12// -------------
13//
14// This file is not part of the Qt API.
15// This header file may change from version to
16// version without notice, or even be removed.
17//
18// We mean it.
19//
20
21#include "qstorageinfo_p.h"
22
23#include <QtCore/qsystemdetection.h>
24#include <QtCore/private/qlocale_tools_p.h>
25
26#include <sys/sysmacros.h> // makedev()
27
29
30using namespace Qt::StringLiterals;
31using MountInfo = QStorageInfoPrivate::MountInfo;
32
33static const char MountInfoPath[] = "/proc/self/mountinfo";
34
35static std::optional<dev_t> deviceNumber(QByteArrayView devno)
36{
37 // major:minor
38 auto it = devno.cbegin();
39 auto r = qstrntoll(it, devno.size(), 10);
40 if (!r.ok())
41 return std::nullopt;
42 int rdevmajor = int(r.result);
43 it += r.used;
44
45 if (*it != ':')
46 return std::nullopt;
47
48 r = qstrntoll(++it, devno.size() - r.used + 1, 10);
49 if (!r.ok())
50 return std::nullopt;
51
52 return makedev(rdevmajor, r.result);
53}
54
55// Helper function to parse paths that the kernel inserts escape sequences
56// for.
58{
59 // The kernel escapes with octal the following characters:
60 // space ' ', tab '\t', backslash '\\', and newline '\n'
61 // See:
62 // https://codebrowser.dev/linux/linux/fs/proc_namespace.c.html#show_mountinfo
63 // https://codebrowser.dev/linux/linux/fs/seq_file.c.html#mangle_path
64
65 QByteArray ret(path.size(), '\0');
66 char *dst = ret.data();
67 const char *src = path.data();
68 const char *srcEnd = path.data() + path.size();
69 while (src != srcEnd) {
70 switch (*src) {
71 case ' ': // Shouldn't happen
72 return {};
73
74 case '\\': {
75 // It always uses exactly three octal characters.
76 ++src;
77 char c = (*src++ - '0') << 6;
78 c |= (*src++ - '0') << 3;
79 c |= (*src++ - '0');
80 *dst++ = c;
81 break;
82 }
83
84 default:
85 *dst++ = *src++;
86 break;
87 }
88 }
89 // If "path" contains any of the characters this method is demangling,
90 // "ret" would be oversized with extra '\0' characters at the end.
91 ret.resize(dst - ret.data());
92 return ret;
93}
94
95// Indexes into the "fields" std::array in parseMountInfo()
96// static constexpr short MountId = 0;
97// static constexpr short ParentId = 1;
98static constexpr short DevNo = 2;
99static constexpr short FsRoot = 3;
100static constexpr short MountPoint = 4;
101static constexpr short MountOptions = 5;
102// static constexpr short OptionalFields = 6;
103// static constexpr short Separator = 7;
104static constexpr short FsType = 8;
105static constexpr short MountSource = 9;
106static constexpr short SuperOptions = 10;
107static constexpr short FieldCount = 11;
108
109// Splits a line from /proc/self/mountinfo into fields; fields are separated
110// by a single space.
111static void tokenizeLine(std::array<QByteArrayView, FieldCount> &fields, QByteArrayView line)
112{
113 size_t fieldIndex = 0;
114 qsizetype from = 0;
115 const char *begin = line.data();
116 const qsizetype len = line.size();
117 qsizetype spaceIndex = -1;
118 while ((spaceIndex = line.indexOf(' ', from)) != -1 && fieldIndex < FieldCount) {
119 fields[fieldIndex] = QByteArrayView{begin + from, begin + spaceIndex};
120 from = spaceIndex;
121
122 // Skip "OptionalFields" and Separator fields
123 if (fieldIndex == MountOptions) {
124 static constexpr char separatorField[] = " - ";
125 const qsizetype sepIndex = line.indexOf(separatorField, from);
126 if (sepIndex == -1) {
127 qCWarning(lcStorageInfo,
128 "Malformed line (missing '-' separator field) while parsing '%s':\n%s",
130 fields.fill({});
131 return;
132 }
133
134 from = sepIndex + strlen(separatorField);
135 // Continue parsing at FsType field
136 fieldIndex = FsType;
137 continue;
138 }
139
140 if (from + 1 < len)
141 ++from; // Skip the space at spaceIndex
142
143 ++fieldIndex;
144 }
145
146 // Currently we don't use the last field, so just check the index
147 if (fieldIndex != SuperOptions) {
148 qCInfo(lcStorageInfo,
149 "Expected %d fields while parsing line from '%s', but found %zu instead:\n%.*s",
150 FieldCount, MountInfoPath, fieldIndex, int(line.size()), line.data());
151 fields.fill({});
152 }
153}
154
155// parseMountInfo() is called from:
156// - QStorageInfoPrivate::initRootPath(), where a list of all mounted volumes is needed
157// - QStorageInfoPrivate::mountedVolumes(), where some filesystem types are ignored
158// (see shouldIncludefs())
159enum class FilterMountInfo {
160 All,
161 Filtered,
162};
163
164[[maybe_unused]] static std::vector<MountInfo>
166{
167 // https://www.kernel.org/doc/Documentation/filesystems/proc.txt:
168 // 36 35 98:0 /mnt1 /mnt2 rw,noatime master:1 - ext3 /dev/root rw,errors=continue
169 // (1)(2)(3) (4) (5) (6) (7) (8) (9) (10) (11)
170
171 auto it = mountinfo.cbegin();
172 const auto end = mountinfo.cend();
173 auto nextLine = [&it, &end]() -> QByteArrayView {
174 auto nIt = std::find(it, end, '\n');
175 if (nIt != end) {
176 QByteArrayView ba(it, nIt);
177 it = ++nIt; // Advance
178 return ba;
179 }
180 return {};
181 };
182
183 std::vector<MountInfo> infos;
184 std::array<QByteArrayView, FieldCount> fields;
186
187 auto checkField = [&line](QByteArrayView field) {
188 if (field.isEmpty()) {
189 qDebug("Failed to parse line from %s:\n%.*s", MountInfoPath, int(line.size()),
190 line.data());
191 return false;
192 }
193 return true;
194 };
195
196 // mountinfo has a stable format, no empty lines
197 while (!(line = nextLine()).isEmpty()) {
198 fields.fill({});
199 tokenizeLine(fields, line);
200
202 QByteArray mountP = parseMangledPath(fields[MountPoint]);
203 if (!checkField(mountP))
204 continue;
205 info.mountPoint = QFile::decodeName(mountP);
206
207 if (!checkField(fields[FsType]))
208 continue;
209 info.fsType = fields[FsType].toByteArray();
210
211 if (filter == FilterMountInfo::Filtered && !shouldIncludeFs(info.mountPoint, info.fsType))
212 continue;
213
214 std::optional<dev_t> devno = deviceNumber(fields[DevNo]);
215 if (!devno) {
216 checkField({});
217 continue;
218 }
219 info.stDev = *devno;
220
221 QByteArrayView fsRootView = fields[FsRoot];
222 if (!checkField(fsRootView))
223 continue;
224
225 // If the filesystem root is "/" -- it's not a *sub*-volume/bind-mount,
226 // in that case we leave info.fsRoot empty
227 if (fsRootView != "/") {
228 info.fsRoot = parseMangledPath(fsRootView);
229 if (!checkField(info.fsRoot))
230 continue;
231 }
232
233 info.device = parseMangledPath(fields[MountSource]);
234 if (!checkField(info.device))
235 continue;
236
237 infos.push_back(std::move(info));
238 }
239 return infos;
240}
241
243
244#endif // QSTORAGEINFO_LINUX_P_H
\inmodule QtCore
Definition qbytearray.h:57
static QString decodeName(const QByteArray &localFileName)
This does the reverse of QFile::encodeName() using localFileName.
Definition qfile.h:162
qsizetype size() const
Definition qset.h:50
const_iterator cbegin() const noexcept
Definition qset.h:138
qsizetype indexOf(QLatin1StringView s, qsizetype from=0, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Definition qstring.cpp:4517
const QChar * constData() const
Returns a pointer to the data stored in the QString.
Definition qstring.h:1246
qsizetype size() const noexcept
Returns the number of characters in this string.
Definition qstring.h:186
QChar * data()
Returns a pointer to the data stored in the QString.
Definition qstring.h:1240
QSet< QString >::iterator it
Combined button and popup list for selecting options.
QSimpleParsedNumber< qlonglong > qstrntoll(const char *begin, qsizetype size, int base)
#define qDebug
[1]
Definition qlogging.h:164
#define qCInfo(category,...)
#define qCWarning(category,...)
return ret
GLboolean r
[2]
GLuint GLuint end
GLenum src
GLenum GLenum dst
GLint GLint GLint GLint GLint GLint GLint GLbitfield GLenum filter
const GLubyte * c
GLsizei const GLchar *const * path
GLenum GLsizei len
QtPrivate::QRegularExpressionMatchIteratorRangeBasedForIterator begin(const QRegularExpressionMatchIterator &iterator)
static constexpr short MountSource
static constexpr short FsType
static std::vector< MountInfo > doParseMountInfo(const QByteArray &mountinfo, FilterMountInfo filter=FilterMountInfo::All)
static std::optional< dev_t > deviceNumber(QByteArrayView devno)
static constexpr short FieldCount
static constexpr short FsRoot
static QByteArray parseMangledPath(QByteArrayView path)
static const char MountInfoPath[]
QStorageInfoPrivate::MountInfo MountInfo
static constexpr short MountOptions
static void tokenizeLine(std::array< QByteArrayView, FieldCount > &fields, QByteArrayView line)
static constexpr short DevNo
static constexpr short MountPoint
static constexpr short SuperOptions
static bool shouldIncludeFs(const QString &mountDir, const QByteArray &fsType)
ptrdiff_t qsizetype
Definition qtypes.h:165
QByteArray ba
[0]
QHostInfo info
[0]