2020-09-20 20:33:16 +01:00
|
|
|
/*
|
2020-10-01 00:33:19 +01:00
|
|
|
SPDX-FileCopyrightText: 2018 Caio Jordão Carvalho <caiojcarvalho@gmail.com>
|
2020-09-20 20:33:16 +01:00
|
|
|
|
|
|
|
SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
*/
|
2018-07-10 00:01:34 +01:00
|
|
|
|
|
|
|
#include "softwareraid.h"
|
|
|
|
|
|
|
|
#include "backend/corebackend.h"
|
|
|
|
#include "backend/corebackendmanager.h"
|
|
|
|
#include "core/partition.h"
|
|
|
|
#include "core/volumemanagerdevice_p.h"
|
|
|
|
#include "fs/filesystem.h"
|
|
|
|
#include "fs/filesystemfactory.h"
|
|
|
|
#include "util/externalcommand.h"
|
|
|
|
|
2020-10-24 22:01:45 +01:00
|
|
|
#include <utility>
|
|
|
|
|
2018-07-13 21:56:13 +01:00
|
|
|
#include <KLocalizedString>
|
2018-07-15 23:38:33 +01:00
|
|
|
#include <QFile>
|
2018-07-10 00:01:34 +01:00
|
|
|
#include <QRegularExpression>
|
|
|
|
|
|
|
|
#define d_ptr std::static_pointer_cast<SoftwareRAIDPrivate>(d)
|
|
|
|
|
|
|
|
class SoftwareRAIDPrivate : public VolumeManagerDevicePrivate
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
qint32 m_raidLevel;
|
|
|
|
qint64 m_chunkSize;
|
|
|
|
qint64 m_totalChunk;
|
|
|
|
qint64 m_arraySize;
|
|
|
|
QString m_UUID;
|
|
|
|
QStringList m_devicePathList;
|
2019-01-12 00:05:59 +00:00
|
|
|
QStringList m_partitionPathList;
|
2018-07-15 17:11:50 +01:00
|
|
|
SoftwareRAID::Status m_status;
|
2018-07-10 00:01:34 +01:00
|
|
|
};
|
|
|
|
|
2018-07-15 17:11:50 +01:00
|
|
|
SoftwareRAID::SoftwareRAID(const QString& name, SoftwareRAID::Status status, const QString& iconName)
|
2018-07-10 00:01:34 +01:00
|
|
|
: VolumeManagerDevice(std::make_shared<SoftwareRAIDPrivate>(),
|
|
|
|
name,
|
|
|
|
(QStringLiteral("/dev/") + name),
|
|
|
|
getChunkSize(QStringLiteral("/dev/") + name),
|
|
|
|
getTotalChunk(QStringLiteral("/dev/") + name),
|
|
|
|
iconName,
|
|
|
|
Device::Type::SoftwareRAID_Device)
|
|
|
|
{
|
|
|
|
d_ptr->m_raidLevel = getRaidLevel(deviceNode());
|
|
|
|
d_ptr->m_chunkSize = logicalSize();
|
|
|
|
d_ptr->m_totalChunk = totalLogical();
|
|
|
|
d_ptr->m_arraySize = getArraySize(deviceNode());
|
|
|
|
d_ptr->m_UUID = getUUID(deviceNode());
|
|
|
|
d_ptr->m_devicePathList = getDevicePathList(deviceNode());
|
2018-07-15 17:11:50 +01:00
|
|
|
d_ptr->m_status = status;
|
2018-07-10 00:01:34 +01:00
|
|
|
|
|
|
|
initPartitions();
|
|
|
|
}
|
|
|
|
|
|
|
|
const QStringList SoftwareRAID::deviceNodes() const
|
|
|
|
{
|
|
|
|
return d_ptr->m_devicePathList;
|
|
|
|
}
|
|
|
|
|
|
|
|
const QStringList& SoftwareRAID::partitionNodes() const
|
|
|
|
{
|
2019-01-12 00:05:59 +00:00
|
|
|
return d_ptr->m_partitionPathList;
|
2018-07-10 00:01:34 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
qint64 SoftwareRAID::partitionSize(QString &partitionPath) const
|
|
|
|
{
|
2018-07-12 00:12:43 +01:00
|
|
|
Q_UNUSED(partitionPath)
|
2018-07-10 00:01:34 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool SoftwareRAID::growArray(Report &report, const QStringList &devices)
|
|
|
|
{
|
2018-07-12 00:12:43 +01:00
|
|
|
Q_UNUSED(report)
|
|
|
|
Q_UNUSED(devices)
|
2018-07-10 00:01:34 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool SoftwareRAID::shrinkArray(Report &report, const QStringList &devices)
|
|
|
|
{
|
2018-07-12 00:12:43 +01:00
|
|
|
Q_UNUSED(report)
|
|
|
|
Q_UNUSED(devices)
|
2018-07-10 00:01:34 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-07-13 21:56:13 +01:00
|
|
|
QString SoftwareRAID::prettyName() const
|
|
|
|
{
|
2018-07-15 17:11:50 +01:00
|
|
|
QString raidInfo;
|
|
|
|
|
2018-07-15 17:21:12 +01:00
|
|
|
if (status() == SoftwareRAID::Status::Active)
|
2018-07-15 17:11:50 +01:00
|
|
|
raidInfo = xi18nc("@item:inlistbox [RAID level]", " [RAID %1]", raidLevel());
|
2018-07-15 17:21:12 +01:00
|
|
|
else if (status() == SoftwareRAID::Status::Recovery)
|
|
|
|
raidInfo = xi18nc("@item:inlistbox [RAID level - Recovering]", " [RAID %1 - Recovering]", raidLevel());
|
|
|
|
else if (status() == SoftwareRAID::Status::Resync)
|
|
|
|
raidInfo = xi18nc("@item:inlistbox [RAID level - Resyncing]", " [RAID %1 - Resyncing]", raidLevel());
|
2018-07-15 17:11:50 +01:00
|
|
|
else
|
|
|
|
raidInfo = QStringLiteral(" [RAID]");
|
|
|
|
|
|
|
|
return VolumeManagerDevice::prettyName() + raidInfo;
|
2018-07-15 14:31:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
bool SoftwareRAID::operator ==(const Device& other) const
|
|
|
|
{
|
|
|
|
bool equalDeviceNode = Device::operator ==(other);
|
|
|
|
|
|
|
|
if (other.type() == Device::Type::SoftwareRAID_Device) {
|
|
|
|
const SoftwareRAID& raid = static_cast<const SoftwareRAID&>(other);
|
|
|
|
|
|
|
|
if (!equalDeviceNode)
|
|
|
|
return raid.uuid() == uuid();
|
|
|
|
}
|
|
|
|
|
|
|
|
return equalDeviceNode;
|
2018-07-13 21:56:13 +01:00
|
|
|
}
|
|
|
|
|
2018-07-11 16:03:44 +01:00
|
|
|
qint32 SoftwareRAID::raidLevel() const
|
|
|
|
{
|
|
|
|
return d_ptr->m_raidLevel;
|
|
|
|
}
|
|
|
|
|
|
|
|
qint64 SoftwareRAID::chunkSize() const
|
|
|
|
{
|
|
|
|
return d_ptr->m_chunkSize;
|
|
|
|
}
|
|
|
|
|
|
|
|
qint64 SoftwareRAID::totalChunk() const
|
|
|
|
{
|
|
|
|
return d_ptr->m_totalChunk;
|
|
|
|
}
|
|
|
|
|
|
|
|
qint64 SoftwareRAID::arraySize() const
|
|
|
|
{
|
|
|
|
return d_ptr->m_arraySize;
|
|
|
|
}
|
|
|
|
|
|
|
|
QString SoftwareRAID::uuid() const
|
|
|
|
{
|
|
|
|
return d_ptr->m_UUID;
|
|
|
|
}
|
|
|
|
|
|
|
|
QStringList SoftwareRAID::devicePathList() const
|
|
|
|
{
|
|
|
|
return d_ptr->m_devicePathList;
|
|
|
|
}
|
|
|
|
|
2018-07-15 17:11:50 +01:00
|
|
|
SoftwareRAID::Status SoftwareRAID::status() const
|
2018-07-15 14:31:27 +01:00
|
|
|
{
|
2018-07-15 17:11:50 +01:00
|
|
|
return d_ptr->m_status;
|
2018-07-15 14:31:27 +01:00
|
|
|
}
|
|
|
|
|
2018-07-15 17:11:50 +01:00
|
|
|
void SoftwareRAID::setStatus(SoftwareRAID::Status status)
|
2018-07-15 14:31:27 +01:00
|
|
|
{
|
2018-07-15 17:11:50 +01:00
|
|
|
d_ptr->m_status = status;
|
2018-07-15 14:31:27 +01:00
|
|
|
}
|
|
|
|
|
2018-07-10 00:01:34 +01:00
|
|
|
void SoftwareRAID::scanSoftwareRAID(QList<Device*>& devices)
|
|
|
|
{
|
2018-07-15 19:51:23 +01:00
|
|
|
QStringList availableInConf;
|
2018-07-15 14:31:27 +01:00
|
|
|
|
|
|
|
// TODO: Support custom config files.
|
|
|
|
QString config = getRAIDConfiguration(QStringLiteral("/etc/mdadm.conf"));
|
|
|
|
|
|
|
|
if (!config.isEmpty()) {
|
2018-07-15 16:06:54 +01:00
|
|
|
QRegularExpression re(QStringLiteral("([\\t\\r\\n\\f\\s]|INACTIVE-)ARRAY \\/dev\\/([\\/\\w-]+)"));
|
2018-07-15 14:31:27 +01:00
|
|
|
QRegularExpressionMatchIterator i = re.globalMatch(config);
|
|
|
|
|
|
|
|
while (i.hasNext()) {
|
|
|
|
QRegularExpressionMatch reMatch = i.next();
|
2018-07-15 16:06:54 +01:00
|
|
|
QString deviceName = reMatch.captured(2).trimmed();
|
2018-07-15 14:31:27 +01:00
|
|
|
|
2018-07-15 19:51:23 +01:00
|
|
|
availableInConf << deviceName;
|
2018-07-15 14:31:27 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-15 23:38:33 +01:00
|
|
|
QFile mdstat(QStringLiteral("/proc/mdstat"));
|
|
|
|
|
|
|
|
if (mdstat.open(QIODevice::ReadOnly)) {
|
|
|
|
QTextStream stream(&mdstat);
|
|
|
|
|
|
|
|
QString content = stream.readAll();
|
|
|
|
|
|
|
|
mdstat.close();
|
2018-07-10 00:01:34 +01:00
|
|
|
|
2018-07-15 16:06:54 +01:00
|
|
|
QRegularExpression re(QStringLiteral("md([\\/\\w]+)\\s+:\\s+([\\w]+)"));
|
2018-07-15 23:38:33 +01:00
|
|
|
QRegularExpressionMatchIterator i = re.globalMatch(content);
|
2018-07-10 00:01:34 +01:00
|
|
|
while (i.hasNext()) {
|
|
|
|
QRegularExpressionMatch reMatch = i.next();
|
|
|
|
|
2018-07-15 14:31:27 +01:00
|
|
|
QString deviceNode = QStringLiteral("/dev/md") + reMatch.captured(1).trimmed();
|
2018-07-15 16:06:54 +01:00
|
|
|
QString status = reMatch.captured(2).trimmed();
|
2018-07-10 00:01:34 +01:00
|
|
|
|
2018-07-15 14:31:27 +01:00
|
|
|
SoftwareRAID* d = static_cast<SoftwareRAID *>(CoreBackendManager::self()->backend()->scanDevice(deviceNode));
|
2018-07-10 00:01:34 +01:00
|
|
|
|
2018-07-15 23:38:33 +01:00
|
|
|
// Just to prevent segfault in some case
|
|
|
|
if (d == nullptr)
|
|
|
|
continue;
|
|
|
|
|
2018-07-15 20:36:43 +01:00
|
|
|
const QStringList constAvailableInConf = availableInConf;
|
2018-07-15 19:51:23 +01:00
|
|
|
|
2018-07-15 20:36:43 +01:00
|
|
|
for (const QString& path : constAvailableInConf)
|
|
|
|
if (getUUID(QStringLiteral("/dev/") + path) == d->uuid())
|
2018-07-15 19:51:23 +01:00
|
|
|
availableInConf.removeAll(path);
|
|
|
|
|
2018-07-15 20:36:43 +01:00
|
|
|
devices << d;
|
2018-07-15 16:06:54 +01:00
|
|
|
|
2018-07-15 17:21:12 +01:00
|
|
|
if (status == QStringLiteral("inactive"))
|
|
|
|
d->setStatus(SoftwareRAID::Status::Inactive);
|
|
|
|
|
2018-07-15 17:11:50 +01:00
|
|
|
if (d->raidLevel() > 0) {
|
2018-07-15 23:38:33 +01:00
|
|
|
QRegularExpression reMirrorStatus(d->name() + QStringLiteral("\\s+:\\s+(.*\\n\\s+)+\\[[=>.]+\\]\\s+(resync|recovery)"));
|
2018-07-15 16:06:54 +01:00
|
|
|
|
2018-07-15 23:38:33 +01:00
|
|
|
QRegularExpressionMatch reMirrorStatusMatch = reMirrorStatus.match(content);
|
2018-07-15 16:06:54 +01:00
|
|
|
|
2018-07-15 17:11:50 +01:00
|
|
|
if (reMirrorStatusMatch.hasMatch()) {
|
2018-07-15 23:38:33 +01:00
|
|
|
if (reMirrorStatusMatch.captured(2) == QStringLiteral("resync"))
|
2018-07-15 17:11:50 +01:00
|
|
|
d->setStatus(SoftwareRAID::Status::Resync);
|
2018-07-15 23:38:33 +01:00
|
|
|
else if (reMirrorStatusMatch.captured(2) == QStringLiteral("recovery"))
|
2018-07-15 17:11:50 +01:00
|
|
|
d->setStatus(SoftwareRAID::Status::Recovery);
|
2018-07-15 16:10:19 +01:00
|
|
|
}
|
2018-07-15 16:06:54 +01:00
|
|
|
}
|
2018-07-10 00:01:34 +01:00
|
|
|
}
|
|
|
|
}
|
2018-07-15 14:31:27 +01:00
|
|
|
|
2020-10-24 22:01:45 +01:00
|
|
|
for (const QString& name : std::as_const(availableInConf)) {
|
2018-07-15 19:51:23 +01:00
|
|
|
SoftwareRAID *raidDevice = new SoftwareRAID(name, SoftwareRAID::Status::Inactive);
|
|
|
|
devices << raidDevice;
|
|
|
|
}
|
2018-07-10 00:01:34 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
qint32 SoftwareRAID::getRaidLevel(const QString &path)
|
|
|
|
{
|
|
|
|
QString output = getDetail(path);
|
|
|
|
|
|
|
|
if (!output.isEmpty()) {
|
|
|
|
QRegularExpression re(QStringLiteral("Raid Level :\\s+\\w+(\\d+)"));
|
|
|
|
QRegularExpressionMatch reMatch = re.match(output);
|
|
|
|
if (reMatch.hasMatch())
|
|
|
|
return reMatch.captured(1).toLongLong();
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
qint64 SoftwareRAID::getChunkSize(const QString &path)
|
|
|
|
{
|
2018-07-16 19:39:47 +01:00
|
|
|
if (getRaidLevel(path) == 1) {
|
|
|
|
QStringList devices = getDevicePathList(path);
|
|
|
|
|
|
|
|
if (!devices.isEmpty()) {
|
|
|
|
QString device = devices[0];
|
|
|
|
// Look sector size for the first device/partition on the list, as RAID 1 is composed by mirrored devices
|
|
|
|
ExternalCommand sectorSize(QStringLiteral("blockdev"), { QStringLiteral("--getss"), device });
|
|
|
|
|
|
|
|
if (sectorSize.run(-1) && sectorSize.exitCode() == 0) {
|
|
|
|
int sectors = sectorSize.output().trimmed().toLongLong();
|
|
|
|
return sectors;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
QString output = getDetail(path);
|
|
|
|
if (!output.isEmpty()) {
|
|
|
|
QRegularExpression re(QStringLiteral("Chunk Size :\\s+(\\d+)"));
|
|
|
|
QRegularExpressionMatch reMatch = re.match(output);
|
|
|
|
if (reMatch.hasMatch())
|
|
|
|
return reMatch.captured(1).toLongLong();
|
|
|
|
}
|
2018-07-10 00:01:34 +01:00
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
qint64 SoftwareRAID::getTotalChunk(const QString &path)
|
|
|
|
{
|
|
|
|
return getArraySize(path) / getChunkSize(path);
|
|
|
|
}
|
|
|
|
|
|
|
|
qint64 SoftwareRAID::getArraySize(const QString &path)
|
|
|
|
{
|
|
|
|
QString output = getDetail(path);
|
|
|
|
if (!output.isEmpty()) {
|
|
|
|
QRegularExpression re(QStringLiteral("Array Size :\\s+(\\d+)"));
|
|
|
|
QRegularExpressionMatch reMatch = re.match(output);
|
|
|
|
if (reMatch.hasMatch())
|
|
|
|
return reMatch.captured(1).toLongLong() * 1024;
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
QString SoftwareRAID::getUUID(const QString &path)
|
|
|
|
{
|
2018-07-15 14:31:27 +01:00
|
|
|
QString output = getDetail(path);
|
|
|
|
|
|
|
|
if (!output.isEmpty()) {
|
|
|
|
QRegularExpression re(QStringLiteral("UUID :\\s+([\\w:]+)"));
|
|
|
|
QRegularExpressionMatch reMatch = re.match(output);
|
|
|
|
|
|
|
|
if (reMatch.hasMatch())
|
|
|
|
return reMatch.captured(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
// If UUID was not found in detail output, it should be searched in config file
|
|
|
|
|
|
|
|
// TODO: Support custom config files.
|
|
|
|
QString config = getRAIDConfiguration(QStringLiteral("/etc/mdadm.conf"));
|
|
|
|
|
|
|
|
if (!config.isEmpty()) {
|
2018-07-15 16:06:54 +01:00
|
|
|
QRegularExpression re(QStringLiteral("([\\t\\r\\n\\f\\s]|INACTIVE-)ARRAY \\/dev\\/md([\\/\\w-]+)(.*)"));
|
2018-07-15 14:31:27 +01:00
|
|
|
QRegularExpressionMatchIterator i = re.globalMatch(config);
|
|
|
|
|
|
|
|
while (i.hasNext()) {
|
|
|
|
QRegularExpressionMatch reMatch = i.next();
|
2018-07-15 16:06:54 +01:00
|
|
|
QString deviceNode = QStringLiteral("/dev/md") + reMatch.captured(2).trimmed();
|
|
|
|
QString otherInfo = reMatch.captured(3).trimmed();
|
2018-07-15 14:31:27 +01:00
|
|
|
|
|
|
|
// Consider device node as name=host:deviceNode when the captured device node string has '-' character
|
|
|
|
// It happens when user have included the device to config file using 'mdadm --examine --scan'
|
|
|
|
if (deviceNode.contains(QLatin1Char('-'))) {
|
|
|
|
QRegularExpression reName(QStringLiteral("name=[\\w:]+\\/dev\\/md\\/([\\/\\w]+)"));
|
|
|
|
QRegularExpressionMatch nameMatch = reName.match(otherInfo);
|
|
|
|
|
|
|
|
if (nameMatch.hasMatch())
|
|
|
|
deviceNode = nameMatch.captured(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (deviceNode == path) {
|
|
|
|
QRegularExpression reUUID(QStringLiteral("(UUID=|uuid=)([\\w:]+)"));
|
|
|
|
QRegularExpressionMatch uuidMatch = reUUID.match(otherInfo);
|
|
|
|
|
|
|
|
if (uuidMatch.hasMatch())
|
|
|
|
return uuidMatch.captured(2);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return QString();
|
2018-07-10 00:01:34 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
QStringList SoftwareRAID::getDevicePathList(const QString &path)
|
|
|
|
{
|
2018-07-16 19:39:47 +01:00
|
|
|
QStringList result;
|
|
|
|
|
|
|
|
QString detail = getDetail(path);
|
|
|
|
|
|
|
|
if (!detail.isEmpty()) {
|
|
|
|
QRegularExpression re(QStringLiteral("\\s+\\/dev\\/(\\w+)"));
|
|
|
|
QRegularExpressionMatchIterator i = re.globalMatch(detail);
|
|
|
|
|
|
|
|
while (i.hasNext()) {
|
|
|
|
QRegularExpressionMatch match = i.next();
|
|
|
|
|
|
|
|
QString device = QStringLiteral("/dev/") + match.captured(1);
|
|
|
|
if (device != path)
|
|
|
|
result << device;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
2018-07-10 00:01:34 +01:00
|
|
|
}
|
|
|
|
|
2018-07-11 03:03:21 +01:00
|
|
|
bool SoftwareRAID::isRaidPath(const QString &path)
|
|
|
|
{
|
|
|
|
return !getDetail(path).isEmpty();
|
|
|
|
}
|
|
|
|
|
2018-07-10 00:01:34 +01:00
|
|
|
bool SoftwareRAID::createSoftwareRAID(Report &report,
|
|
|
|
const QString &name,
|
|
|
|
const QStringList devicePathList,
|
|
|
|
const qint32 raidLevel,
|
|
|
|
const qint32 chunkSize)
|
|
|
|
{
|
2019-01-12 00:05:59 +00:00
|
|
|
Q_UNUSED(report)
|
|
|
|
Q_UNUSED(name)
|
|
|
|
Q_UNUSED(devicePathList)
|
|
|
|
Q_UNUSED(raidLevel)
|
|
|
|
Q_UNUSED(chunkSize)
|
2018-07-10 00:01:34 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool SoftwareRAID::deleteSoftwareRAID(Report &report,
|
|
|
|
SoftwareRAID &raidDevice)
|
|
|
|
{
|
2018-07-12 00:12:43 +01:00
|
|
|
Q_UNUSED(report)
|
|
|
|
Q_UNUSED(raidDevice)
|
2018-07-10 00:01:34 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-07-12 18:38:52 +01:00
|
|
|
bool SoftwareRAID::assembleSoftwareRAID(const QString& deviceNode)
|
2018-07-10 00:01:34 +01:00
|
|
|
{
|
2018-07-12 18:38:52 +01:00
|
|
|
if (!isRaidPath(deviceNode))
|
|
|
|
return false;
|
|
|
|
|
2018-07-12 18:08:24 +01:00
|
|
|
ExternalCommand cmd(QStringLiteral("mdadm"),
|
2018-07-12 18:38:52 +01:00
|
|
|
{ QStringLiteral("--assemble"), QStringLiteral("--scan"), deviceNode });
|
2018-07-12 18:08:24 +01:00
|
|
|
|
|
|
|
return cmd.run(-1) && cmd.exitCode() == 0;
|
2018-07-10 00:01:34 +01:00
|
|
|
}
|
|
|
|
|
2018-07-12 18:38:52 +01:00
|
|
|
bool SoftwareRAID::stopSoftwareRAID(const QString& deviceNode)
|
2018-07-10 00:01:34 +01:00
|
|
|
{
|
2018-07-12 18:38:52 +01:00
|
|
|
if (!isRaidPath(deviceNode))
|
|
|
|
return false;
|
|
|
|
|
2018-07-12 18:08:24 +01:00
|
|
|
ExternalCommand cmd(QStringLiteral("mdadm"),
|
2018-07-13 20:20:38 +01:00
|
|
|
{ QStringLiteral("--manage"), QStringLiteral("--stop"), deviceNode });
|
2018-07-12 18:08:24 +01:00
|
|
|
|
|
|
|
return cmd.run(-1) && cmd.exitCode() == 0;
|
2018-07-10 00:01:34 +01:00
|
|
|
}
|
|
|
|
|
2018-07-12 18:38:52 +01:00
|
|
|
bool SoftwareRAID::reassembleSoftwareRAID(const QString &deviceNode)
|
|
|
|
{
|
|
|
|
return stopSoftwareRAID(deviceNode) && assembleSoftwareRAID(deviceNode);
|
|
|
|
}
|
|
|
|
|
2018-07-15 23:38:33 +01:00
|
|
|
bool SoftwareRAID::isRaidMember(const QString &path)
|
|
|
|
{
|
|
|
|
QFile mdstat(QStringLiteral("/proc/mdstat"));
|
|
|
|
|
|
|
|
if (!mdstat.open(QIODevice::ReadOnly))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
QTextStream stream(&mdstat);
|
|
|
|
|
|
|
|
QString content = stream.readAll();
|
|
|
|
|
|
|
|
mdstat.close();
|
|
|
|
|
|
|
|
QRegularExpression re(QStringLiteral("(\\w+)\\[\\d+\\]"));
|
|
|
|
QRegularExpressionMatchIterator i = re.globalMatch(content);
|
|
|
|
|
|
|
|
while (i.hasNext()) {
|
|
|
|
QRegularExpressionMatch reMatch = i.next();
|
|
|
|
|
|
|
|
QString match = QStringLiteral("/dev/") + reMatch.captured(1);
|
|
|
|
|
|
|
|
if (match == path)
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-07-10 00:01:34 +01:00
|
|
|
void SoftwareRAID::initPartitions()
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
qint64 SoftwareRAID::mappedSector(const QString &partitionPath, qint64 sector) const
|
|
|
|
{
|
2019-01-12 00:05:59 +00:00
|
|
|
Q_UNUSED(partitionPath)
|
|
|
|
Q_UNUSED(sector)
|
2018-07-10 00:01:34 +01:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
QString SoftwareRAID::getDetail(const QString &path)
|
|
|
|
{
|
|
|
|
ExternalCommand cmd(QStringLiteral("mdadm"),
|
2018-07-13 20:20:38 +01:00
|
|
|
{ QStringLiteral("--misc"), QStringLiteral("--detail"), path });
|
2018-07-15 14:31:27 +01:00
|
|
|
return (cmd.run(-1) && cmd.exitCode() == 0) ? cmd.output() : QString();
|
|
|
|
}
|
|
|
|
|
|
|
|
QString SoftwareRAID::getRAIDConfiguration(const QString &configurationPath)
|
|
|
|
{
|
2018-07-15 23:38:33 +01:00
|
|
|
QFile config(configurationPath);
|
2018-07-15 14:31:27 +01:00
|
|
|
|
2018-07-15 23:38:33 +01:00
|
|
|
if (!config.open(QIODevice::ReadOnly))
|
|
|
|
return QString();
|
|
|
|
|
|
|
|
QTextStream stream(&config);
|
|
|
|
|
|
|
|
QString result = stream.readAll();
|
|
|
|
|
|
|
|
config.close();
|
|
|
|
|
|
|
|
return result;
|
2018-07-10 00:01:34 +01:00
|
|
|
}
|