1
/***************************************************************************
2
* Copyright (C) 2008 by Volker Lanz <vl@fidra.de> *
4
* This program is free software; you can redistribute it and/or modify *
5
* it under the terms of the GNU General Public License as published by *
6
* the Free Software Foundation; either version 2 of the License, or *
7
* (at your option) any later version. *
9
* This program is distributed in the hope that it will be useful, *
10
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
11
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
12
* GNU General Public License for more details. *
14
* You should have received a copy of the GNU General Public License *
15
* along with this program; if not, write to the *
16
* Free Software Foundation, Inc., *
17
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
18
***************************************************************************/
20
#include "core/operationstack.h"
21
#include "core/device.h"
22
#include "core/partition.h"
23
#include "core/partitiontable.h"
25
#include "ops/operation.h"
26
#include "ops/deleteoperation.h"
27
#include "ops/newoperation.h"
28
#include "ops/resizeoperation.h"
29
#include "ops/copyoperation.h"
30
#include "ops/restoreoperation.h"
31
#include "ops/createfilesystemoperation.h"
32
#include "ops/setpartflagsoperation.h"
33
#include "ops/setfilesystemlabeloperation.h"
35
#include "jobs/setfilesystemlabeljob.h"
37
#include "fs/filesystemfactory.h"
39
#include "util/globallog.h"
45
/** Constructs a new OperationStack */
46
OperationStack::OperationStack() :
52
/** Destructs an OperationStack, cleaning up Operations and Devices */
53
OperationStack::~OperationStack()
59
/** Tries to merge an existing NewOperation with a new Operation pushed on the OperationStack
61
There are several cases what might need to be done:
65
<li>An existing operation created a Partition that is now being deleted: In this case, just remove
66
the corresponding NewOperation from the OperationStack.</li>
68
<li>An existing Operation created a Partition that is now being moved or resized. In this case,
69
remove the original NewOperation and create a new NewOperation with updated start and end
70
sectors. This new NewOperation is appended to the OperationStack.</li>
72
<li>An existing NewOperation created a Partition that is now being copied. We're not copying
73
but instead creating another new Partition in its place.</li>
75
<li>The label for a new Partition's FileSystem is modified: Modify in NewOperation and forget it.</li>
77
<li>File system is changed for a new Partition: Modify in NewOperation and forget it.</li>
80
@param currentOp the Operation already on the stack to try to merge with
81
@param pushedOp the newly pushed Operation
82
@return true if the OperationStack has been modified in a way that requires merging to stop
84
bool OperationStack::mergeNewOperation(Operation*& currentOp, Operation*& pushedOp)
86
NewOperation* newOp = dynamic_cast<NewOperation*>(currentOp);
91
DeleteOperation* pushedDeleteOp = dynamic_cast<DeleteOperation*>(pushedOp);
92
ResizeOperation* pushedResizeOp = dynamic_cast<ResizeOperation*>(pushedOp);
93
CopyOperation* pushedCopyOp = dynamic_cast<CopyOperation*>(pushedOp);
94
SetFileSystemLabelOperation* pushedLabelOp = dynamic_cast<SetFileSystemLabelOperation*>(pushedOp);
95
CreateFileSystemOperation* pushedCreateFileSystemOp = dynamic_cast<CreateFileSystemOperation*>(pushedOp);
98
if (pushedDeleteOp && &newOp->newPartition() == &pushedDeleteOp->deletedPartition())
100
log() << i18nc("@info/plain", "Deleting a partition just created: Undoing the operation to create the partition.");
106
delete operations().takeAt(operations().indexOf(newOp));
112
if (pushedResizeOp && &newOp->newPartition() == &pushedResizeOp->partition())
114
log() << i18nc("@info/plain", "Resizing a partition just created: Updating start and end in existing operation.");
116
Partition* newPartition = new Partition(newOp->newPartition());
117
newPartition->setFirstSector(pushedResizeOp->newFirstSector());
118
newPartition->fileSystem().setFirstSector(pushedResizeOp->newFirstSector());
119
newPartition->setLastSector(pushedResizeOp->newLastSector());
120
newPartition->fileSystem().setLastSector(pushedResizeOp->newLastSector());
122
NewOperation* revisedNewOp = new NewOperation(newOp->targetDevice(), newPartition);
124
pushedOp = revisedNewOp;
127
delete operations().takeAt(operations().indexOf(newOp));
133
if (pushedCopyOp && &newOp->newPartition() == &pushedCopyOp->sourcePartition())
135
log() << i18nc("@info/plain", "Copying a new partition: Creating a new partition instead.");
137
Partition* newPartition = new Partition(newOp->newPartition());
138
newPartition->setFirstSector(pushedCopyOp->copiedPartition().firstSector());
139
newPartition->fileSystem().setFirstSector(pushedCopyOp->copiedPartition().fileSystem().firstSector());
140
newPartition->setLastSector(pushedCopyOp->copiedPartition().lastSector());
141
newPartition->fileSystem().setLastSector(pushedCopyOp->copiedPartition().fileSystem().lastSector());
143
NewOperation* revisedNewOp = new NewOperation(pushedCopyOp->targetDevice(), newPartition);
145
pushedOp = revisedNewOp;
151
if (pushedLabelOp && &newOp->newPartition() == &pushedLabelOp->labeledPartition())
153
log() << i18nc("@info/plain", "Changing label for a new partition: No new operation required.");
155
newOp->setLabelJob()->setLabel(pushedLabelOp->newLabel());
156
newOp->newPartition().fileSystem().setLabel(pushedLabelOp->newLabel());
165
if (pushedCreateFileSystemOp && &newOp->newPartition() == &pushedCreateFileSystemOp->partition())
167
log() << i18nc("@info/plain", "Changing file system for a new partition: No new operation required.");
169
FileSystem* oldFs = &newOp->newPartition().fileSystem();
171
newOp->newPartition().setFileSystem(FileSystemFactory::cloneWithNewType(pushedCreateFileSystemOp->newFileSystem()->type(), *oldFs));
185
/** Tries to merge an existing CopyOperation with a new Operation pushed on the OperationStack.
187
These are the cases to consider:
191
<li>An existing CopyOperation created a Partition that is now being deleted. Remove the
192
CopyOperation, and, if the CopyOperation was an overwrite, carry on with the delete. Else
193
also remove the DeleteOperation.</li>
196
<li>An existing CopyOperation created a Partition that is now being copied. We're not copying
197
the target of this existing CopyOperation, but its source instead. If this merge is not done,
198
copied partitions will have misleading labels ("copy of sdXY" instead of "copy of copy of
199
sdXY" for a second-generation copy) but the Operation itself will still work.</li>
202
@param currentOp the Operation already on the stack to try to merge with
203
@param pushedOp the newly pushed Operation
204
@return true if the OperationStack has been modified in a way that requires merging to stop
206
bool OperationStack::mergeCopyOperation(Operation*& currentOp, Operation*& pushedOp)
208
CopyOperation* copyOp = dynamic_cast<CopyOperation*>(currentOp);
213
DeleteOperation* pushedDeleteOp = dynamic_cast<DeleteOperation*>(pushedOp);
214
CopyOperation* pushedCopyOp = dynamic_cast<CopyOperation*>(pushedOp);
217
if (pushedDeleteOp && ©Op->copiedPartition() == &pushedDeleteOp->deletedPartition())
219
// If the copy operation didn't overwrite, but create a new partition, just remove the
220
// copy operation, forget the delete and be done.
221
if (copyOp->overwrittenPartition() == NULL)
223
log() << i18nc("@info/plain", "Deleting a partition just copied: Removing the copy.");
230
log() << i18nc("@info/plain", "Deleting a partition just copied over an existing partition: Removing the copy and deleting the existing partition.");
232
pushedDeleteOp->setDeletedPartition(copyOp->overwrittenPartition());
236
delete operations().takeAt(operations().indexOf(copyOp));
242
if (pushedCopyOp && ©Op->copiedPartition() == &pushedCopyOp->sourcePartition())
244
log() << i18nc("@info/plain", "Copying a partition that is itself a copy: Copying the original source partition instead.");
246
pushedCopyOp->setSourcePartition(©Op->sourcePartition());
252
/** Tries to merge an existing RestoreOperation with a new Operation pushed on the OperationStack.
254
If an existing RestoreOperation created a Partition that is now being deleted, remove the
255
RestoreOperation, and, if the RestoreOperation was an overwrite, carry on with the delete. Else
256
also remove the DeleteOperation.
258
@param currentOp the Operation already on the stack to try to merge with
259
@param pushedOp the newly pushed Operation
260
@return true if the OperationStack has been modified in a way that requires merging to stop
262
bool OperationStack::mergeRestoreOperation(Operation*& currentOp, Operation*& pushedOp)
264
RestoreOperation* restoreOp = dynamic_cast<RestoreOperation*>(currentOp);
266
if (restoreOp == NULL)
269
DeleteOperation* pushedDeleteOp = dynamic_cast<DeleteOperation*>(pushedOp);
271
if (pushedDeleteOp && &restoreOp->restorePartition() == &pushedDeleteOp->deletedPartition())
273
if (restoreOp->overwrittenPartition() == NULL)
275
log() << i18nc("@info/plain", "Deleting a partition just restored: Removing the restore operation.");
282
log() << i18nc("@info/plain", "Deleting a partition just restored to an existing partition: Removing the restore operation and deleting the existing partition.");
284
pushedDeleteOp->setDeletedPartition(restoreOp->overwrittenPartition());
288
delete operations().takeAt(operations().indexOf(restoreOp));
296
/** Tries to merge an existing SetPartFlagsOperation with a new Operation pushed on the OperationStack.
298
If the Partition flags for an existing Partition are modified look if there is an existing
299
Operation for the same Partition and modify that one.
301
@param currentOp the Operation already on the stack to try to merge with
302
@param pushedOp the newly pushed Operation
303
@return true if the OperationStack has been modified in a way that requires merging to stop
305
bool OperationStack::mergePartFlagsOperation(Operation*& currentOp, Operation*& pushedOp)
307
SetPartFlagsOperation* partFlagsOp = dynamic_cast<SetPartFlagsOperation*>(currentOp);
309
if (partFlagsOp == NULL)
312
SetPartFlagsOperation* pushedFlagsOp = dynamic_cast<SetPartFlagsOperation*>(pushedOp);
314
if (pushedFlagsOp && &partFlagsOp->flagPartition() == &pushedFlagsOp->flagPartition())
316
log() << i18nc("@info/plain", "Changing flags again for the same partition: Removing old operation.");
318
pushedFlagsOp->setOldFlags(partFlagsOp->oldFlags());
320
delete operations().takeAt(operations().indexOf(partFlagsOp));
328
/** Tries to merge an existing SetFileSystemLabelOperation with a new Operation pushed on the OperationStack.
330
If a FileSystem label for an existing Partition is modified look if there is an existing
331
SetFileSystemLabelOperation for the same Partition.
333
@param currentOp the Operation already on the stack to try to merge with
334
@param pushedOp the newly pushed Operation
335
@return true if the OperationStack has been modified in a way that requires merging to stop
337
bool OperationStack::mergePartLabelOperation(Operation*& currentOp, Operation*& pushedOp)
339
SetFileSystemLabelOperation* partLabelOp = dynamic_cast<SetFileSystemLabelOperation*>(currentOp);
341
if (partLabelOp == NULL)
344
SetFileSystemLabelOperation* pushedLabelOp = dynamic_cast<SetFileSystemLabelOperation*>(pushedOp);
346
if (pushedLabelOp && &partLabelOp->labeledPartition() == &pushedLabelOp->labeledPartition())
348
log() << i18nc("@info/plain", "Changing label again for the same partition: Removing old operation.");
350
pushedLabelOp->setOldLabel(partLabelOp->oldLabel());
352
delete operations().takeAt(operations().indexOf(partLabelOp));
360
/** Pushes a new Operation on the OperationStack.
362
This method will call all methods that try to merge the new Operation with the
363
existing ones. It is not uncommon that any of these will delete the pushed
364
Operation. Callers <b>must not rely</b> on the pushed Operation to exist after
365
calling OperationStack::push().
367
@param o Pointer to the Operation. Must not be NULL.
369
void OperationStack::push(Operation* o)
373
foreach (Operation* currentOp, operations())
375
if (mergeNewOperation(currentOp, o))
378
if (mergeCopyOperation(currentOp, o))
381
if (mergeRestoreOperation(currentOp, o))
384
if (mergePartFlagsOperation(currentOp, o))
387
if (mergePartLabelOperation(currentOp, o))
393
log() << i18nc("@info/plain", "Add operation: %1", o->description());
394
operations().append(o);
396
o->setStatus(Operation::StatusPending);
400
/** Removes the topmost Operation from the OperationStack, calls Operation::undo() on it and deletes it. */
401
void OperationStack::pop()
403
Operation* o = operations().takeLast();
408
/** Removes all Operations from the OperationStack, calling Operation::undo() on them and deleting them. */
409
void OperationStack::clearOperations()
411
while (!operations().isEmpty())
413
Operation* o = operations().takeLast();
414
if (o->status() == Operation::StatusPending)
420
/** Clears the list of Devices. */
421
void OperationStack::clearDevices()
423
qDeleteAll(m_PreviewDevices);
424
m_PreviewDevices.clear();
427
/** Finds a Device a Partition is on.
428
@param p pointer to the Partition to find a Device for
429
@return the Device or NULL if none could be found
431
Device* OperationStack::findDeviceForPartition(const Partition* p)
433
foreach (Device* d, previewDevices())
435
if (d->partitionTable() == NULL)
438
foreach(const Partition* part, d->partitionTable()->children())
443
foreach (const Partition* child, part->children())
452
/** Adds a Device to the OperationStack
453
@param d pointer to the Device to add. Must not be NULL.
455
void OperationStack::addDevice(Device* d)
459
m_PreviewDevices.append(d);