File indexing completed on 2024-05-05 05:48:51
0001 /* 0002 SPDX-FileCopyrightText: 2008-2010 Volker Lanz <vl@fidra.de> 0003 SPDX-FileCopyrightText: 2012-2019 Andrius Štikonas <andrius@stikonas.eu> 0004 SPDX-FileCopyrightText: 2016 Chantara Tith <tith.chantara@gmail.com> 0005 0006 SPDX-License-Identifier: GPL-3.0-or-later 0007 */ 0008 0009 #include "ops/copyoperation.h" 0010 0011 #include "core/partition.h" 0012 #include "core/device.h" 0013 0014 #include "jobs/createpartitionjob.h" 0015 #include "jobs/deletepartitionjob.h" 0016 #include "jobs/checkfilesystemjob.h" 0017 #include "jobs/copyfilesystemjob.h" 0018 #include "jobs/resizefilesystemjob.h" 0019 0020 #include "fs/filesystemfactory.h" 0021 0022 #include "util/capacity.h" 0023 #include "util/report.h" 0024 0025 #include <QDebug> 0026 #include <QString> 0027 0028 #include <KLocalizedString> 0029 0030 /** Creates a new CopyOperation. 0031 @param targetdevice the Device to copy the Partition to 0032 @param copiedpartition pointer to the new Partition object on the target Device. May not be nullptr. 0033 @param sourcedevice the Device where to copy from 0034 @param sourcepartition pointer to the Partition to copy from. May not be nullptr. 0035 */ 0036 CopyOperation::CopyOperation(Device& targetdevice, Partition* copiedpartition, Device& sourcedevice, Partition* sourcepartition) : 0037 Operation(), 0038 m_TargetDevice(targetdevice), 0039 m_CopiedPartition(copiedpartition), 0040 m_SourceDevice(sourcedevice), 0041 m_SourcePartition(sourcepartition), 0042 m_OverwrittenPartition(nullptr), 0043 m_MustDeleteOverwritten(false), 0044 m_CheckSourceJob(nullptr), 0045 m_CreatePartitionJob(nullptr), 0046 m_CopyFSJob(nullptr), 0047 m_CheckTargetJob(nullptr), 0048 m_MaximizeJob(nullptr), 0049 m_Description(updateDescription()) 0050 { 0051 Q_ASSERT(targetDevice().partitionTable()); 0052 0053 Partition* dest = targetDevice().partitionTable()->findPartitionBySector(copiedPartition().firstSector(), PartitionRole(PartitionRole::Primary | PartitionRole::Logical | PartitionRole::Unallocated)); 0054 0055 if (dest == nullptr) 0056 qWarning() << "destination partition not found at sector " << copiedPartition().firstSector(); 0057 0058 Q_ASSERT(dest); 0059 0060 if (dest && !dest->roles().has(PartitionRole::Unallocated)) { 0061 copiedPartition().setLastSector(dest->lastSector()); 0062 setOverwrittenPartition(dest); 0063 } 0064 0065 addJob(m_CheckSourceJob = new CheckFileSystemJob(sourcePartition())); 0066 0067 if (overwrittenPartition() == nullptr) 0068 addJob(m_CreatePartitionJob = new CreatePartitionJob(targetDevice(), copiedPartition())); 0069 0070 addJob(m_CopyFSJob = new CopyFileSystemJob(targetDevice(), copiedPartition(), sourceDevice(), sourcePartition())); 0071 addJob(m_CheckTargetJob = new CheckFileSystemJob(copiedPartition())); 0072 addJob(m_MaximizeJob = new ResizeFileSystemJob(targetDevice(), copiedPartition())); 0073 } 0074 0075 CopyOperation::~CopyOperation() 0076 { 0077 if (status() == StatusPending) 0078 delete m_CopiedPartition; 0079 0080 if (status() == StatusFinishedSuccess || status() == StatusFinishedWarning || status() == StatusError) 0081 cleanupOverwrittenPartition(); 0082 } 0083 0084 bool CopyOperation::targets(const Device& d) const 0085 { 0086 return d == targetDevice(); 0087 } 0088 0089 bool CopyOperation::targets(const Partition& p) const 0090 { 0091 return p == copiedPartition(); 0092 } 0093 0094 void CopyOperation::preview() 0095 { 0096 if (overwrittenPartition()) 0097 removePreviewPartition(targetDevice(), *overwrittenPartition()); 0098 0099 insertPreviewPartition(targetDevice(), copiedPartition()); 0100 } 0101 0102 void CopyOperation::undo() 0103 { 0104 removePreviewPartition(targetDevice(), copiedPartition()); 0105 0106 if (overwrittenPartition()) 0107 insertPreviewPartition(targetDevice(), *overwrittenPartition()); 0108 } 0109 0110 bool CopyOperation::execute(Report& parent) 0111 { 0112 bool rval = false; 0113 bool warning = false; 0114 0115 Report* report = parent.newChild(description()); 0116 0117 // check the source first 0118 if ((rval = checkSourceJob()->run(*report))) { 0119 // At this point, if the target partition is to be created and not overwritten, it 0120 // will still have the wrong device path (the one of the source device). We need 0121 // to adjust that before we're creating it. 0122 copiedPartition().setDevicePath(targetDevice().deviceNode()); 0123 0124 // either we have no partition to create (because we're overwriting) or creating 0125 // must be successful 0126 if (!createPartitionJob() || (rval = createPartitionJob()->run(*report))) { 0127 // set the state of the target partition from StateCopy to StateNone or checking 0128 // it will fail (because its deviceNode() will still be "Copy of sdXn"). This is 0129 // only required for overwritten partitions, but doesn't hurt in any case. 0130 copiedPartition().setState(Partition::State::None); 0131 0132 // if we have overwritten a partition, reset device path and number 0133 if (overwrittenPartition()) { 0134 copiedPartition().setDevicePath(overwrittenPartition()->devicePath()); 0135 copiedPartition().setPartitionPath(overwrittenPartition()->partitionPath()); 0136 } 0137 0138 // now run the copy job itself 0139 if ((rval = copyFSJob()->run(*report))) { 0140 // and if the copy job succeeded, check the target 0141 if ((rval = checkTargetJob()->run(*report))) { 0142 // ok, everything went well 0143 rval = true; 0144 0145 // if maximizing doesn't work, just warn the user, don't fail 0146 if (!maximizeJob()->run(*report)) { 0147 report->line() << xi18nc("@info:status", "<warning>Maximizing file system on target partition <filename>%1</filename> to the size of the partition failed.</warning>", copiedPartition().deviceNode()); 0148 warning = true; 0149 } 0150 } else 0151 report->line() << xi18nc("@info:status", "Checking target partition <filename>%1</filename> after copy failed.", copiedPartition().deviceNode()); 0152 } else { 0153 if (createPartitionJob()) { 0154 DeletePartitionJob deleteJob(targetDevice(), copiedPartition()); 0155 deleteJob.run(*report); 0156 } 0157 0158 report->line() << xi18nc("@info:status", "Copying source to target partition failed."); 0159 } 0160 } else 0161 report->line() << xi18nc("@info:status", "Creating target partition for copying failed."); 0162 } else 0163 report->line() << xi18nc("@info:status", "Checking source partition <filename>%1</filename> failed.", sourcePartition().deviceNode()); 0164 0165 if (rval) 0166 setStatus(warning ? StatusFinishedWarning : StatusFinishedSuccess); 0167 else 0168 setStatus(StatusError); 0169 0170 report->setStatus(xi18nc("@info:status (success, error, warning...) of operation", "%1: %2", description(), statusText())); 0171 0172 return rval; 0173 } 0174 0175 QString CopyOperation::updateDescription() const 0176 { 0177 if (overwrittenPartition()) { 0178 if (copiedPartition().length() == overwrittenPartition()->length()) 0179 return xi18nc("@info:status", "Copy partition <filename>%1</filename> (%2, %3) to <filename>%4</filename> (%5, %6)", 0180 sourcePartition().deviceNode(), 0181 Capacity::formatByteSize(sourcePartition().capacity()), 0182 sourcePartition().fileSystem().name(), 0183 overwrittenPartition()->deviceNode(), 0184 Capacity::formatByteSize(overwrittenPartition()->capacity()), 0185 overwrittenPartition()->fileSystem().name() 0186 ); 0187 0188 return xi18nc("@info:status", "Copy partition <filename>%1</filename> (%2, %3) to <filename>%4</filename> (%5, %6) and grow it to %7", 0189 sourcePartition().deviceNode(), 0190 Capacity::formatByteSize(sourcePartition().capacity()), 0191 sourcePartition().fileSystem().name(), 0192 overwrittenPartition()->deviceNode(), 0193 Capacity::formatByteSize(overwrittenPartition()->capacity()), 0194 overwrittenPartition()->fileSystem().name(), 0195 Capacity::formatByteSize(copiedPartition().capacity()) 0196 ); 0197 } 0198 0199 if (copiedPartition().length() == sourcePartition().length()) 0200 return xi18nc("@info:status", "Copy partition <filename>%1</filename> (%2, %3) to unallocated space (starting at %4) on <filename>%5</filename>", 0201 sourcePartition().deviceNode(), 0202 Capacity::formatByteSize(sourcePartition().capacity()), 0203 sourcePartition().fileSystem().name(), 0204 Capacity::formatByteSize(copiedPartition().firstSector() * targetDevice().logicalSize()), 0205 targetDevice().deviceNode() 0206 ); 0207 0208 return xi18nc("@info:status", "Copy partition <filename>%1</filename> (%2, %3) to unallocated space (starting at %4) on <filename>%5</filename> and grow it to %6", 0209 sourcePartition().deviceNode(), 0210 Capacity::formatByteSize(sourcePartition().capacity()), 0211 sourcePartition().fileSystem().name(), 0212 Capacity::formatByteSize(copiedPartition().firstSector() * targetDevice().logicalSize()), 0213 targetDevice().deviceNode(), 0214 Capacity::formatByteSize(copiedPartition().capacity()) 0215 ); 0216 } 0217 0218 void CopyOperation::setOverwrittenPartition(Partition* p) 0219 { 0220 // this code is also in RestoreOperation. 0221 0222 cleanupOverwrittenPartition(); 0223 m_OverwrittenPartition = p; 0224 0225 // If the overwritten partition has no other operation that owns it (e.g., an OperationNew or 0226 // an OperationRestore), we're the new owner. So remember that, because after the operations all 0227 // have executed and we're asked to clean up after ourselves, the state of the overwritten partition 0228 // might have changed: If it was a new one and the NewOperation has successfully run, the state will 0229 // then be StateNone. 0230 m_MustDeleteOverwritten = (p && p->state() == Partition::State::None); 0231 } 0232 0233 void CopyOperation::cleanupOverwrittenPartition() 0234 { 0235 if (mustDeleteOverwritten()) { 0236 delete overwrittenPartition(); 0237 m_OverwrittenPartition = nullptr; 0238 } 0239 } 0240 0241 /** Creates a new copied Partition. 0242 @param target the target Partition to copy to (may be unallocated) 0243 @param source the source Partition to copy 0244 @return pointer to the newly created Partition object 0245 */ 0246 Partition* CopyOperation::createCopy(const Partition& target, const Partition& source) 0247 { 0248 Partition* p = target.roles().has(PartitionRole::Unallocated) ? new Partition(source) : new Partition(target); 0249 0250 p->setDevicePath(source.devicePath()); 0251 p->setPartitionPath(source.partitionPath()); 0252 p->setState(Partition::State::Copy); 0253 0254 p->deleteFileSystem(); 0255 p->setFileSystem(FileSystemFactory::create(source.fileSystem())); 0256 0257 p->fileSystem().setFirstSector(p->firstSector()); 0258 p->fileSystem().setLastSector(p->lastSector()); 0259 0260 p->setFlags(PartitionTable::Flag::None); 0261 0262 return p; 0263 } 0264 0265 /** Can a Partition be copied? 0266 @param p the Partition in question, may be nullptr. 0267 @return true if @p p can be copied. 0268 */ 0269 bool CopyOperation::canCopy(const Partition* p) 0270 { 0271 if (p == nullptr) 0272 return false; 0273 0274 if (p->state() == Partition::State::New && p->roles().has(PartitionRole::Luks)) 0275 return false; 0276 0277 if (p->isMounted()) 0278 return false; 0279 0280 // FIXME: Does not work well enough yet 0281 if (p->roles().has(PartitionRole::Lvm_Lv)) 0282 return false; 0283 0284 // Normally, copying partitions that have not been written to disk yet should 0285 // be forbidden here. The operation stack, however, will take care of these 0286 // problematic cases when pushing the CopyOperation onto the stack. 0287 0288 return p->fileSystem().supportCopy() != FileSystem::cmdSupportNone; 0289 } 0290 0291 /** Can a Partition be pasted on another one? 0292 @param p the Partition to be pasted to, may be nullptr 0293 @param source the Partition to be pasted, may be nullptr 0294 @return true if @p source can be pasted on @p p 0295 */ 0296 bool CopyOperation::canPaste(const Partition* p, const Partition* source) 0297 { 0298 if (p == nullptr || source == nullptr) 0299 return false; 0300 0301 if (p->isMounted()) 0302 return false; 0303 0304 if (p->roles().has(PartitionRole::Extended)) 0305 return false; 0306 0307 if (p->roles().has(PartitionRole::Lvm_Lv)) 0308 return false; 0309 0310 if (p == source) 0311 return false; 0312 0313 if (source->length() > p->length()) 0314 return false; 0315 0316 if (!p->roles().has(PartitionRole::Unallocated) && p->capacity() > source->fileSystem().maxCapacity()) 0317 return false; 0318 0319 return true; 0320 }