File indexing completed on 2024-05-05 05:48:46
0001 /* 0002 SPDX-FileCopyrightText: 2012-2018 Andrius Štikonas <andrius@stikonas.eu> 0003 SPDX-FileCopyrightText: 2016 Chantara Tith <tith.chantara@gmail.com> 0004 SPDX-FileCopyrightText: 2016 Teo Mrnjavac <teo@kde.org> 0005 SPDX-FileCopyrightText: 2018 Caio Jordão Carvalho <caiojcarvalho@gmail.com> 0006 SPDX-FileCopyrightText: 2019 Yuri Chornoivan <yurchor@ukr.net> 0007 SPDX-FileCopyrightText: 2020 Arnaud Ferraris <arnaud.ferraris@collabora.com> 0008 SPDX-FileCopyrightText: 2020 Gaël PORTAY <gael.portay@collabora.com> 0009 0010 SPDX-License-Identifier: GPL-3.0-or-later 0011 */ 0012 0013 #include "fs/lvm2_pv.h" 0014 #include "core/device.h" 0015 0016 #include "util/externalcommand.h" 0017 #include "util/capacity.h" 0018 0019 #include <QString> 0020 0021 #include <KLocalizedString> 0022 0023 namespace FS 0024 { 0025 FileSystem::CommandSupportType lvm2_pv::m_GetUsed = FileSystem::cmdSupportNone; 0026 FileSystem::CommandSupportType lvm2_pv::m_GetLabel = FileSystem::cmdSupportNone; 0027 FileSystem::CommandSupportType lvm2_pv::m_Create = FileSystem::cmdSupportNone; 0028 FileSystem::CommandSupportType lvm2_pv::m_Grow = FileSystem::cmdSupportNone; 0029 FileSystem::CommandSupportType lvm2_pv::m_Shrink = FileSystem::cmdSupportNone; 0030 FileSystem::CommandSupportType lvm2_pv::m_Move = FileSystem::cmdSupportNone; 0031 FileSystem::CommandSupportType lvm2_pv::m_Check = FileSystem::cmdSupportNone; 0032 FileSystem::CommandSupportType lvm2_pv::m_Copy = FileSystem::cmdSupportNone; 0033 FileSystem::CommandSupportType lvm2_pv::m_Backup = FileSystem::cmdSupportNone; 0034 FileSystem::CommandSupportType lvm2_pv::m_SetLabel = FileSystem::cmdSupportNone; 0035 FileSystem::CommandSupportType lvm2_pv::m_UpdateUUID = FileSystem::cmdSupportNone; 0036 FileSystem::CommandSupportType lvm2_pv::m_GetUUID = FileSystem::cmdSupportNone; 0037 0038 lvm2_pv::lvm2_pv(qint64 firstsector, qint64 lastsector, 0039 qint64 sectorsused, const QString& label, const QVariantMap& features) 0040 : FileSystem(firstsector, lastsector, sectorsused, label, features, FileSystem::Type::Lvm2_PV) 0041 , m_PESize(0) 0042 , m_TotalPE(0) 0043 , m_AllocatedPE(0) 0044 { 0045 } 0046 0047 void lvm2_pv::init() 0048 { 0049 CommandSupportType lvmFound = findExternal(QStringLiteral("lvm"), {}, 3) ? cmdSupportFileSystem : cmdSupportNone; 0050 0051 m_Create = lvmFound; 0052 m_Check = lvmFound; 0053 m_Grow = lvmFound; 0054 m_Shrink = lvmFound; 0055 m_UpdateUUID = lvmFound; 0056 m_GetUsed = lvmFound; 0057 0058 m_Move = (m_Check != cmdSupportNone) ? cmdSupportCore : cmdSupportNone; 0059 0060 m_GetLabel = cmdSupportCore; 0061 m_Backup = cmdSupportCore; 0062 m_GetUUID = cmdSupportCore; 0063 0064 m_GetLabel = cmdSupportNone; 0065 m_Copy = cmdSupportNone; // Copying PV can confuse LVM 0066 } 0067 0068 void lvm2_pv::scan(const QString& deviceNode) 0069 { 0070 getPESize(deviceNode); 0071 m_AllocatedPE = getAllocatedPE(deviceNode); 0072 m_TotalPE = getTotalPE(deviceNode); 0073 } 0074 0075 bool lvm2_pv::supportToolFound() const 0076 { 0077 return 0078 m_GetUsed != cmdSupportNone && 0079 m_Create != cmdSupportNone && 0080 m_Check != cmdSupportNone && 0081 m_UpdateUUID != cmdSupportNone && 0082 m_Grow != cmdSupportNone && 0083 m_Shrink != cmdSupportNone && 0084 m_Move != cmdSupportNone && 0085 m_Backup != cmdSupportNone && 0086 m_GetUUID != cmdSupportNone; 0087 } 0088 0089 FileSystem::SupportTool lvm2_pv::supportToolName() const 0090 { 0091 return SupportTool(QStringLiteral("lvm2"), QUrl(QStringLiteral("https://sourceware.org/lvm2/"))); 0092 } 0093 0094 qint64 lvm2_pv::maxCapacity() const 0095 { 0096 return Capacity::unitFactor(Capacity::Unit::Byte, Capacity::Unit::EiB); 0097 } 0098 0099 qint64 lvm2_pv::readUsedCapacity(const QString& deviceNode) const 0100 { 0101 QString pvUsed = getpvField(QStringLiteral("pv_used"), deviceNode); 0102 QString metadataOffset = getpvField(QStringLiteral("pe_start"), deviceNode); 0103 return pvUsed.isEmpty() ? -1 : pvUsed.toLongLong() + metadataOffset.toLongLong(); 0104 } 0105 0106 bool lvm2_pv::check(Report& report, const QString& deviceNode) const 0107 { 0108 ExternalCommand cmd(report, QStringLiteral("lvm"), { QStringLiteral("pvck"), QStringLiteral("--verbose"), deviceNode }); 0109 return cmd.run(-1) && cmd.exitCode() == 0; 0110 } 0111 0112 bool lvm2_pv::create(Report& report, const QString& deviceNode) 0113 { 0114 ExternalCommand cmd(report, QStringLiteral("lvm"), { QStringLiteral("pvcreate"), QStringLiteral("--force"), deviceNode }); 0115 return cmd.run(-1) && cmd.exitCode() == 0; 0116 } 0117 0118 bool lvm2_pv::remove(Report& report, const QString& deviceNode) const 0119 { 0120 ExternalCommand cmd(report, QStringLiteral("lvm"), { QStringLiteral("pvremove"), QStringLiteral("--force"), QStringLiteral("--force"), QStringLiteral("--yes"), deviceNode }); 0121 return cmd.run(-1) && cmd.exitCode() == 0; 0122 } 0123 0124 bool lvm2_pv::resize(Report& report, const QString& deviceNode, qint64 length) const 0125 { 0126 bool rval = true; 0127 0128 qint64 metadataOffset = getpvField(QStringLiteral("pe_start"), deviceNode).toLongLong(); 0129 0130 qint64 lastPE = getTotalPE(deviceNode) - 1; // starts from 0 0131 if (lastPE > 0) { // make sure that the PV is already in a VG 0132 qint64 targetPE = (length - metadataOffset) / peSize() - 1; // starts from 0 0133 if (targetPE < lastPE) { //shrinking FS 0134 qint64 firstMovedPE = qMax(targetPE + 1, getAllocatedPE(deviceNode)); // starts from 1 0135 ExternalCommand moveCmd(report, 0136 QStringLiteral("lvm"), { 0137 QStringLiteral("pvmove"), 0138 QStringLiteral("--alloc"), 0139 QStringLiteral("anywhere"), 0140 deviceNode + QStringLiteral(":") + QString::number(firstMovedPE) + QStringLiteral("-") + QString::number(lastPE), 0141 deviceNode + QStringLiteral(":") + QStringLiteral("0-") + QString::number(firstMovedPE - 1) 0142 }); 0143 rval = moveCmd.run(-1) && (moveCmd.exitCode() == 0 || moveCmd.exitCode() == 5); // FIXME: exit code 5: NO data to move 0144 } 0145 } 0146 0147 ExternalCommand cmd(report, QStringLiteral("lvm"), { 0148 QStringLiteral("pvresize"), 0149 QStringLiteral("--yes"), 0150 QStringLiteral("--setphysicalvolumesize"), 0151 QString::number(length) + QStringLiteral("B"), 0152 deviceNode }); 0153 return rval && cmd.run(-1) && cmd.exitCode() == 0; 0154 } 0155 0156 bool lvm2_pv::resizeOnline(Report& report, const QString& deviceNode, const QString& mountPoint, qint64 length) const 0157 { 0158 Q_UNUSED(mountPoint) 0159 return resize(report, deviceNode, length); 0160 } 0161 0162 bool lvm2_pv::updateUUID(Report& report, const QString& deviceNode) const 0163 { 0164 ExternalCommand cmd(report, QStringLiteral("lvm"), { QStringLiteral("pvchange"), QStringLiteral("--uuid"), deviceNode }); 0165 return cmd.run(-1) && cmd.exitCode() == 0; 0166 } 0167 0168 QString lvm2_pv::readUUID(const QString& deviceNode) const 0169 { 0170 return getpvField(QStringLiteral("pv_uuid"), deviceNode); 0171 } 0172 0173 bool lvm2_pv::mount(Report& report, const QString& deviceNode, const QString& mountPoint) 0174 { 0175 Q_UNUSED(report) 0176 Q_UNUSED(deviceNode) 0177 Q_UNUSED(mountPoint) 0178 return false; 0179 } 0180 0181 bool lvm2_pv::unmount(Report& report, const QString& deviceNode) 0182 { 0183 Q_UNUSED(deviceNode) 0184 Q_UNUSED(report) 0185 return false; 0186 } 0187 0188 bool lvm2_pv::canMount(const QString& deviceNode, const QString& mountPoint) const 0189 { 0190 Q_UNUSED(deviceNode) 0191 Q_UNUSED(mountPoint) 0192 return false; 0193 } 0194 0195 bool lvm2_pv::canUnmount(const QString& deviceNode) const 0196 { 0197 Q_UNUSED(deviceNode) 0198 return false; 0199 } 0200 0201 qint64 lvm2_pv::getTotalPE(const QString& deviceNode) 0202 { 0203 QString pvPeCount = getpvField(QStringLiteral("pv_pe_count"), deviceNode); 0204 return pvPeCount.isEmpty() ? -1 : pvPeCount.toLongLong(); 0205 } 0206 0207 qint64 lvm2_pv::getAllocatedPE(const QString& deviceNode) 0208 { 0209 QString pvPeAllocCount = getpvField(QStringLiteral("pv_pe_alloc_count"), deviceNode); 0210 return pvPeAllocCount.isEmpty() ? -1 : pvPeAllocCount.toLongLong(); 0211 } 0212 0213 void lvm2_pv::getPESize(const QString& deviceNode) 0214 { 0215 QString vgExtentSize = getpvField(QStringLiteral("vg_extent_size"), deviceNode); 0216 m_PESize = vgExtentSize.isEmpty() ? -1 : vgExtentSize.toLongLong(); 0217 } 0218 0219 /** Get pvs command output with field name 0220 * 0221 * @param fieldName LVM field name 0222 * @param deviceNode path to PV 0223 * @return raw output of pvs command, usually with many spaces 0224 */ 0225 QString lvm2_pv::getpvField(const QString& fieldName, const QString& deviceNode) 0226 { 0227 QStringList args = { QStringLiteral("pvs"), 0228 QStringLiteral("--foreign"), 0229 QStringLiteral("--readonly"), 0230 QStringLiteral("--noheadings"), 0231 QStringLiteral("--units"), 0232 QStringLiteral("B"), 0233 QStringLiteral("--nosuffix"), 0234 QStringLiteral("--options"), 0235 fieldName }; 0236 if (!deviceNode.isEmpty()) { 0237 args << deviceNode; 0238 } 0239 ExternalCommand cmd(QStringLiteral("lvm"), args, QProcess::ProcessChannelMode::SeparateChannels); 0240 if (cmd.run(-1) && cmd.exitCode() == 0) { 0241 return cmd.output().trimmed(); 0242 } 0243 return QString(); 0244 } 0245 0246 QString lvm2_pv::getVGName(const QString& deviceNode) 0247 { 0248 return getpvField(QStringLiteral("vg_name"), deviceNode); 0249 } 0250 0251 QList<LvmPV> lvm2_pv::getPVinNode(const PartitionNode* parent) 0252 { 0253 QList<LvmPV> partitions; 0254 if (parent == nullptr) 0255 return partitions; 0256 0257 for (const auto &node : parent->children()) { 0258 const Partition* p = dynamic_cast<const Partition*>(node); 0259 0260 if (p == nullptr) 0261 continue; 0262 0263 if (node->children().size() > 0) 0264 partitions.append(getPVinNode(node)); 0265 0266 // FIXME: reenable newly created PVs (before applying) once everything works 0267 if(p->fileSystem().type() == FileSystem::Type::Lvm2_PV && p->deviceNode() == p->partitionPath()) 0268 partitions.append(LvmPV(p->mountPoint(), p)); 0269 0270 if(p->fileSystem().type() == FileSystem::Type::Luks && p->deviceNode() == p->partitionPath()) 0271 partitions.append(LvmPV(p->mountPoint(), p, true)); 0272 } 0273 0274 return partitions; 0275 } 0276 0277 /** construct a list of Partition objects for LVM PVs that are either unused or belong to some VG. 0278 * 0279 * @param devices list of Devices which we scan for LVM PVs 0280 * @return list of LVM PVs 0281 */ 0282 QList<LvmPV> lvm2_pv::getPVs(const QList<Device*>& devices) 0283 { 0284 QList<LvmPV> partitions; 0285 for (auto const &d : devices) 0286 partitions.append(getPVinNode(d->partitionTable())); 0287 0288 return partitions; 0289 } 0290 0291 } 0292 0293 namespace LVM { 0294 0295 QList<LvmPV> pvList::m_list; 0296 0297 } 0298 0299 LvmPV::LvmPV(const QString vgName, const Partition* p, bool isLuks) 0300 : m_vgName(vgName) 0301 , m_p(p) 0302 , m_isLuks(isLuks) 0303 { 0304 }