File indexing completed on 2024-05-05 05:48:52
0001 /* 0002 SPDX-FileCopyrightText: 2008-2010 Volker Lanz <vl@fidra.de> 0003 SPDX-FileCopyrightText: 2012-2019 Andrius Štikonas <andrius@stikonas.eu> 0004 SPDX-FileCopyrightText: 2015 Teo Mrnjavac <teo@kde.org> 0005 SPDX-FileCopyrightText: 2016 Chantara Tith <tith.chantara@gmail.com> 0006 0007 SPDX-License-Identifier: GPL-3.0-or-later 0008 */ 0009 0010 #include "ops/restoreoperation.h" 0011 0012 #include "core/partition.h" 0013 #include "core/device.h" 0014 #include "core/partitiontable.h" 0015 #include "core/partitionnode.h" 0016 0017 #include "jobs/createpartitionjob.h" 0018 #include "jobs/deletepartitionjob.h" 0019 #include "jobs/checkfilesystemjob.h" 0020 #include "jobs/restorefilesystemjob.h" 0021 #include "jobs/resizefilesystemjob.h" 0022 0023 #include "fs/filesystem.h" 0024 #include "fs/filesystemfactory.h" 0025 #include "fs/luks.h" 0026 0027 #include "util/capacity.h" 0028 #include "util/report.h" 0029 0030 #include <QDebug> 0031 #include <QString> 0032 #include <QFileInfo> 0033 0034 #include <KLocalizedString> 0035 0036 /** Creates a new RestoreOperation. 0037 @param d the Device to restore the Partition to 0038 @param p pointer to the Partition that will be restored. May not be nullptr. 0039 @param filename name of the image file to restore from 0040 */ 0041 RestoreOperation::RestoreOperation(Device& d, Partition* p, const QString& filename) : 0042 Operation(), 0043 m_TargetDevice(d), 0044 m_RestorePartition(p), 0045 m_FileName(filename), 0046 m_OverwrittenPartition(nullptr), 0047 m_MustDeleteOverwritten(false), 0048 m_ImageLength(QFileInfo(filename).size() / 512), // 512 being the "sector size" of an image file. 0049 m_CreatePartitionJob(nullptr), 0050 m_RestoreJob(nullptr), 0051 m_CheckTargetJob(nullptr), 0052 m_MaximizeJob(nullptr) 0053 { 0054 restorePartition().setState(Partition::State::Restore); 0055 0056 Q_ASSERT(targetDevice().partitionTable()); 0057 0058 Partition* dest = targetDevice().partitionTable()->findPartitionBySector(restorePartition().firstSector(), PartitionRole(PartitionRole::Primary | PartitionRole::Logical | PartitionRole::Unallocated)); 0059 0060 if (dest == nullptr) 0061 qWarning() << "destination partition not found at sector " << restorePartition().firstSector(); 0062 0063 Q_ASSERT(dest); 0064 0065 if (dest && !dest->roles().has(PartitionRole::Unallocated)) { 0066 restorePartition().setLastSector(dest->lastSector()); 0067 setOverwrittenPartition(dest); 0068 removePreviewPartition(targetDevice(), *dest); 0069 } 0070 0071 if (!overwrittenPartition()) 0072 addJob(m_CreatePartitionJob = new CreatePartitionJob(targetDevice(), restorePartition())); 0073 0074 addJob(m_RestoreJob = new RestoreFileSystemJob(targetDevice(), restorePartition(), fileName())); 0075 addJob(m_CheckTargetJob = new CheckFileSystemJob(restorePartition())); 0076 addJob(m_MaximizeJob = new ResizeFileSystemJob(targetDevice(), restorePartition())); 0077 } 0078 0079 RestoreOperation::~RestoreOperation() 0080 { 0081 if (status() == StatusPending) 0082 delete m_RestorePartition; 0083 0084 if (status() == StatusFinishedSuccess || status() == StatusFinishedWarning || status() == StatusError) 0085 cleanupOverwrittenPartition(); 0086 } 0087 0088 bool RestoreOperation::targets(const Device& d) const 0089 { 0090 return d == targetDevice(); 0091 } 0092 0093 bool RestoreOperation::targets(const Partition& p) const 0094 { 0095 return p == restorePartition(); 0096 } 0097 0098 void RestoreOperation::preview() 0099 { 0100 insertPreviewPartition(targetDevice(), restorePartition()); 0101 } 0102 0103 void RestoreOperation::undo() 0104 { 0105 removePreviewPartition(targetDevice(), restorePartition()); 0106 0107 if (overwrittenPartition()) 0108 insertPreviewPartition(targetDevice(), *overwrittenPartition()); 0109 } 0110 0111 bool RestoreOperation::execute(Report& parent) 0112 { 0113 bool rval = false; 0114 bool warning = false; 0115 0116 Report* report = parent.newChild(description()); 0117 0118 if (overwrittenPartition()) 0119 restorePartition().setPartitionPath(overwrittenPartition()->partitionPath()); 0120 0121 if (overwrittenPartition() || (rval = createPartitionJob()->run(*report))) { 0122 restorePartition().setState(Partition::State::None); 0123 0124 if ((rval = restoreJob()->run(*report))) { 0125 if ((rval = checkTargetJob()->run(*report))) { 0126 // If the partition was written over an existing one, the partition itself may now 0127 // be larger than the filesystem, so maximize the filesystem to the partition's size 0128 // or the image length, whichever is larger. If this fails, don't return an error, just 0129 // warn the user. 0130 if ((warning = !maximizeJob()->run(*report))) 0131 report->line() << xi18nc("@info:status", "<warning>Maximizing file system on target partition <filename>%1</filename> to the size of the partition failed.</warning>", restorePartition().deviceNode()); 0132 } else 0133 report->line() << xi18nc("@info:status", "Checking target file system on partition <filename>%1</filename> after the restore failed.", restorePartition().deviceNode()); 0134 } else { 0135 if (!overwrittenPartition()) 0136 DeletePartitionJob(targetDevice(), restorePartition()).run(*report); 0137 0138 report->line() << xi18nc("@info:status", "Restoring file system failed."); 0139 } 0140 } else 0141 report->line() << xi18nc("@info:status", "Creating the destination partition to restore to failed."); 0142 0143 if (rval) 0144 setStatus(warning ? StatusFinishedWarning : StatusFinishedSuccess); 0145 else 0146 setStatus(StatusError); 0147 0148 report->setStatus(xi18nc("@info:status (success, error, warning...) of operation", "%1: %2", description(), statusText())); 0149 0150 return rval; 0151 } 0152 0153 QString RestoreOperation::description() const 0154 { 0155 if (overwrittenPartition()) 0156 return xi18nc("@info:status", "Restore partition from <filename>%1</filename> to <filename>%2</filename>", fileName(), overwrittenPartition()->deviceNode()); 0157 0158 return xi18nc("@info:status", "Restore partition on <filename>%1</filename> at %2 from <filename>%3</filename>", targetDevice().deviceNode(), Capacity::formatByteSize(restorePartition().firstSector() * targetDevice().logicalSize()), fileName()); 0159 } 0160 0161 void RestoreOperation::setOverwrittenPartition(Partition* p) 0162 { 0163 // This is copied from CopyOperation. One day we might create a common base class ;-) 0164 0165 cleanupOverwrittenPartition(); 0166 m_OverwrittenPartition = p; 0167 m_MustDeleteOverwritten = (p && p->state() == Partition::State::None); 0168 } 0169 0170 void RestoreOperation::cleanupOverwrittenPartition() 0171 { 0172 if (mustDeleteOverwritten()) { 0173 delete overwrittenPartition(); 0174 m_OverwrittenPartition = nullptr; 0175 } 0176 } 0177 0178 /** Can a Partition be restored to somewhere? 0179 @param p the Partition in question, may be nullptr. 0180 @return true if a Partition can be restored to @p p. 0181 */ 0182 bool RestoreOperation::canRestore(const Partition* p) 0183 { 0184 if (p == nullptr) 0185 return false; 0186 0187 if (p->isMounted()) 0188 return false; 0189 0190 if (p->roles().has(PartitionRole::Extended)) 0191 return false; 0192 0193 if (p->roles().has(PartitionRole::Luks)) { 0194 const FS::luks* luksFs = static_cast<const FS::luks*>(&p->fileSystem()); 0195 return luksFs->mapperName().isEmpty(); 0196 } 0197 0198 return true; 0199 } 0200 /** Creates a new Partition to restore to. 0201 @param device the Device to create the Partition on 0202 @param parent the parent PartitionNode 0203 @param start start sector of the Partition 0204 @param filename name of the image file to restore from 0205 */ 0206 Partition* RestoreOperation::createRestorePartition(const Device& device, PartitionNode& parent, qint64 start, const QString& filename) 0207 { 0208 PartitionRole::Roles r = PartitionRole::Primary; 0209 0210 if (!parent.isRoot()) 0211 r = PartitionRole::Logical; 0212 0213 QFileInfo fileInfo(filename); 0214 0215 if (!fileInfo.exists()) 0216 return nullptr; 0217 0218 const qint64 end = start + fileInfo.size() / device.logicalSize() - 1; 0219 Partition* p = new Partition(&parent, device, PartitionRole(r), FileSystemFactory::create(FileSystem::Type::Unknown, start, end, device.logicalSize()), start, end, QString()); 0220 0221 p->setState(Partition::State::Restore); 0222 return p; 0223 } 0224