Qt 6.x
The Qt SDK
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;
31
32static const char MountInfoPath[] = "/proc/self/mountinfo";
33
34static std::optional<dev_t> deviceNumber(QByteArrayView devno)
35{
36 // major:minor
37 auto it = devno.cbegin();
38 auto r = qstrntoll(it, devno.size(), 10);
39 if (!r.ok())
40 return std::nullopt;
41 int rdevmajor = int(r.result);
42 it += r.used;
43
44 if (*it != ':')
45 return std::nullopt;
46
47 r = qstrntoll(++it, devno.size() - r.used + 1, 10);
48 if (!r.ok())
49 return std::nullopt;
50
51 return makedev(rdevmajor, r.result);
52}
53
54// Helper function to parse paths that the kernel inserts escape sequences
55// for.
57{
58 // The kernel escapes with octal the following characters:
59 // space ' ', tab '\t', backslash '\\', and newline '\n'
60 // See:
61 // https://codebrowser.dev/linux/linux/fs/proc_namespace.c.html#show_mountinfo
62 // https://codebrowser.dev/linux/linux/fs/seq_file.c.html#mangle_path
63
64 QByteArray ret(path.size(), '\0');
65 char *dst = ret.data();
66 const char *src = path.data();
67 const char *srcEnd = path.data() + path.size();
68 while (src != srcEnd) {
69 switch (*src) {
70 case ' ': // Shouldn't happen
71 return {};
72
73 case '\\': {
74 // It always uses exactly three octal characters.
75 ++src;
76 char c = (*src++ - '0') << 6;
77 c |= (*src++ - '0') << 3;
78 c |= (*src++ - '0');
79 *dst++ = c;
80 break;
81 }
82
83 default:
84 *dst++ = *src++;
85 break;
86 }
87 }
88 // If "path" contains any of the characters this method is demangling,
89 // "ret" would be oversized with extra '\0' characters at the end.
90 ret.resize(dst - ret.data());
91 return ret;
92}
93
94// Indexes into the "fields" std::array in parseMountInfo()
95// static constexpr short MountId = 0;
96// static constexpr short ParentId = 1;
97static constexpr short DevNo = 2;
98static constexpr short FsRoot = 3;
99static constexpr short MountPoint = 4;
100static constexpr short MountOptions = 5;
101// static constexpr short OptionalFields = 6;
102// static constexpr short Separator = 7;
103static constexpr short FsType = 8;
104static constexpr short MountSource = 9;
105static constexpr short SuperOptions = 10;
106static constexpr short FieldCount = 11;
107
108// Splits a line from /proc/self/mountinfo into fields; fields are separated
109// by a single space.
110static void tokenizeLine(std::array<QByteArrayView, FieldCount> &fields, QByteArrayView line)
111{
112 size_t fieldIndex = 0;
113 qsizetype from = 0;
114 const char *begin = line.data();
115 const qsizetype len = line.size();
116 qsizetype spaceIndex = -1;
117 while ((spaceIndex = line.indexOf(' ', from)) != -1 && fieldIndex < FieldCount) {
118 fields[fieldIndex] = QByteArrayView{begin + from, begin + spaceIndex};
119 from = spaceIndex;
120
121 // Skip "OptionalFields" and Separator fields
122 if (fieldIndex == MountOptions) {
123 static constexpr char separatorField[] = " - ";
124 const qsizetype sepIndex = line.indexOf(separatorField, from);
125 if (sepIndex == -1) {
126 qCWarning(lcStorageInfo,
127 "Malformed line (missing '-' separator field) while parsing '%s':\n%s",
129 fields.fill({});
130 return;
131 }
132
133 from = sepIndex + strlen(separatorField);
134 // Continue parsing at FsType field
135 fieldIndex = FsType;
136 continue;
137 }
138
139 if (from + 1 < len)
140 ++from; // Skip the space at spaceIndex
141
142 ++fieldIndex;
143 }
144
145 // Currently we don't use the last field, so just check the index
146 if (fieldIndex != SuperOptions) {
147 qCInfo(lcStorageInfo,
148 "Expected %d fields while parsing line from '%s', but found %zu instead:\n%.*s",
149 FieldCount, MountInfoPath, fieldIndex, int(line.size()), line.data());
150 fields.fill({});
151 }
152}
153
154namespace {
155struct MountInfo {
156 QString mountPoint;
157 QByteArray fsType;
159 QByteArray fsRoot;
160 dev_t stDev = 0;
161};
162}
163
164// parseMountInfo() is called from:
165// - QStorageInfoPrivate::initRootPath(), where a list of all mounted volumes is needed
166// - QStorageInfoPrivate::mountedVolumes(), where some filesystem types are ignored
167// (see shouldIncludefs())
168enum class FilterMountInfo {
169 All,
170 Filtered,
171};
172
173[[maybe_unused]] static std::vector<MountInfo>
175{
176 // https://www.kernel.org/doc/Documentation/filesystems/proc.txt:
177 // 36 35 98:0 /mnt1 /mnt2 rw,noatime master:1 - ext3 /dev/root rw,errors=continue
178 // (1)(2)(3) (4) (5) (6) (7) (8) (9) (10) (11)
179
180 auto it = mountinfo.cbegin();
181 const auto end = mountinfo.cend();
182 auto nextLine = [&it, &end]() -> QByteArrayView {
183 auto nIt = std::find(it, end, '\n');
184 if (nIt != end) {
185 QByteArrayView ba(it, nIt);
186 it = ++nIt; // Advance
187 return ba;
188 }
189 return {};
190 };
191
192 std::vector<MountInfo> infos;
193 std::array<QByteArrayView, FieldCount> fields;
195
196 auto checkField = [&line](QByteArrayView field) {
197 if (field.isEmpty()) {
198 qDebug("Failed to parse line from %s:\n%.*s", MountInfoPath, int(line.size()),
199 line.data());
200 return false;
201 }
202 return true;
203 };
204
205 // mountinfo has a stable format, no empty lines
206 while (!(line = nextLine()).isEmpty()) {
207 fields.fill({});
208 tokenizeLine(fields, line);
209
210 MountInfo info;
211 QByteArray mountP = parseMangledPath(fields[MountPoint]);
212 if (!checkField(mountP))
213 continue;
214 info.mountPoint = QFile::decodeName(mountP);
215
216 if (!checkField(fields[FsType]))
217 continue;
218 info.fsType = fields[FsType].toByteArray();
219
220 if (filter == FilterMountInfo::Filtered && !shouldIncludeFs(info.mountPoint, info.fsType))
221 continue;
222
223 std::optional<dev_t> devno = deviceNumber(fields[DevNo]);
224 if (!devno) {
225 checkField({});
226 continue;
227 }
228 info.stDev = *devno;
229
230 QByteArrayView fsRootView = fields[FsRoot];
231 if (!checkField(fsRootView))
232 continue;
233
234 // If the filesystem root is "/" -- it's not a *sub*-volume/bind-mount,
235 // in that case we leave info.fsRoot empty
236 if (fsRootView != "/") {
237 info.fsRoot = parseMangledPath(fsRootView);
238 if (!checkField(info.fsRoot))
239 continue;
240 }
241
242 info.device = parseMangledPath(fields[MountSource]);
243 if (!checkField(info.device))
244 continue;
245
246 infos.push_back(std::move(info));
247 }
248 return infos;
249}
250
252
253#endif // QSTORAGEINFO_LINUX_P_H
constexpr qsizetype size() const noexcept
constexpr const_iterator cbegin() const noexcept
\inmodule QtCore
Definition qbytearray.h:57
const_iterator cbegin() const noexcept
Definition qbytearray.h:430
const_iterator cend() const noexcept
Definition qbytearray.h:434
static QString decodeName(const QByteArray &localFileName)
This does the reverse of QFile::encodeName() using localFileName.
Definition qfile.h:162
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
const QChar * constData() const
Returns a pointer to the data stored in the QString.
Definition qstring.h:1101
qsizetype size() const
Returns the number of characters in this string.
Definition qstring.h:182
QChar * data()
Returns a pointer to the data stored in the QString.
Definition qstring.h:1095
static QString static QString qsizetype indexOf(QChar c, qsizetype from=0, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Definition qstring.cpp:4420
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:160
#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
GLenum GLsizei len
GLsizei const GLchar *const * path
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[]
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:70
QByteArray ba
[0]
QFileInfo info(fileName)
[8]