~ubuntu-branches/ubuntu/oneiric/partitionmanager/oneiric

« back to all changes in this revision

Viewing changes to src/core/operationstack.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Anthony Mercatante
  • Date: 2009-01-23 17:57:36 UTC
  • Revision ID: james.westby@ubuntu.com-20090123175736-2ltrhgg3m55dokbm
Tags: upstream-1.0.0~beta1a
ImportĀ upstreamĀ versionĀ 1.0.0~beta1a

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/***************************************************************************
 
2
 *   Copyright (C) 2008 by Volker Lanz <vl@fidra.de>                       *
 
3
 *                                                                         *
 
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.                                   *
 
8
 *                                                                         *
 
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.                          *
 
13
 *                                                                         *
 
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
 ***************************************************************************/
 
19
 
 
20
#include "core/operationstack.h"
 
21
#include "core/device.h"
 
22
#include "core/partition.h"
 
23
#include "core/partitiontable.h"
 
24
 
 
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"
 
34
 
 
35
#include "jobs/setfilesystemlabeljob.h"
 
36
 
 
37
#include "fs/filesystemfactory.h"
 
38
 
 
39
#include "util/globallog.h"
 
40
 
 
41
 
 
42
#include <kdebug.h>
 
43
#include <klocale.h>
 
44
 
 
45
/** Constructs a new OperationStack */
 
46
OperationStack::OperationStack() :
 
47
        m_Operations(),
 
48
        m_PreviewDevices()
 
49
{
 
50
}
 
51
 
 
52
/** Destructs an OperationStack, cleaning up Operations and Devices */
 
53
OperationStack::~OperationStack()
 
54
{
 
55
        clearOperations();
 
56
        clearDevices();
 
57
}
 
58
 
 
59
/** Tries to merge an existing NewOperation with a new Operation pushed on the OperationStack
 
60
 
 
61
        There are several cases what might need to be done:
 
62
 
 
63
        <ol>
 
64
        <!-- 1 -->
 
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>
 
67
        <!-- 2 -->
 
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>
 
71
        <!-- 3 -->
 
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>
 
74
        <!-- 4 -->
 
75
        <li>The label for a new Partition's FileSystem is modified: Modify in NewOperation and forget it.</li>
 
76
        <!-- 5 -->
 
77
        <li>File system is changed for a new Partition: Modify in NewOperation and forget it.</li>
 
78
        </ol>
 
79
 
 
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
 
83
*/
 
84
bool OperationStack::mergeNewOperation(Operation*& currentOp, Operation*& pushedOp)
 
85
{
 
86
        NewOperation* newOp = dynamic_cast<NewOperation*>(currentOp);
 
87
 
 
88
        if (newOp == NULL)
 
89
                return false;
 
90
 
 
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);
 
96
 
 
97
        // -- 1 --
 
98
        if (pushedDeleteOp && &newOp->newPartition() == &pushedDeleteOp->deletedPartition())
 
99
        {
 
100
                log() << i18nc("@info/plain", "Deleting a partition just created: Undoing the operation to create the partition.");
 
101
 
 
102
                delete pushedOp;
 
103
                pushedOp = NULL;
 
104
 
 
105
                newOp->undo();
 
106
                delete operations().takeAt(operations().indexOf(newOp));
 
107
 
 
108
                return true;
 
109
        }
 
110
 
 
111
        // -- 2 --
 
112
        if (pushedResizeOp && &newOp->newPartition() == &pushedResizeOp->partition())
 
113
        {
 
114
                log() << i18nc("@info/plain", "Resizing a partition just created: Updating start and end in existing operation.");
 
115
 
 
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());
 
121
 
 
122
                NewOperation* revisedNewOp = new NewOperation(newOp->targetDevice(), newPartition);
 
123
                delete pushedOp;
 
124
                pushedOp = revisedNewOp;
 
125
 
 
126
                newOp->undo();
 
127
                delete operations().takeAt(operations().indexOf(newOp));
 
128
 
 
129
                return true;
 
130
        }
 
131
 
 
132
        // -- 3 --
 
133
        if (pushedCopyOp && &newOp->newPartition() == &pushedCopyOp->sourcePartition())
 
134
        {
 
135
                log() << i18nc("@info/plain", "Copying a new partition: Creating a new partition instead.");
 
136
 
 
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());
 
142
 
 
143
                NewOperation* revisedNewOp = new NewOperation(pushedCopyOp->targetDevice(), newPartition);
 
144
                delete pushedOp;
 
145
                pushedOp = revisedNewOp;
 
146
 
 
147
                return true;
 
148
        }
 
149
 
 
150
        // -- 4 --
 
151
        if (pushedLabelOp && &newOp->newPartition() == &pushedLabelOp->labeledPartition())
 
152
        {
 
153
                log() << i18nc("@info/plain", "Changing label for a new partition: No new operation required.");
 
154
 
 
155
                newOp->setLabelJob()->setLabel(pushedLabelOp->newLabel());
 
156
                newOp->newPartition().fileSystem().setLabel(pushedLabelOp->newLabel());
 
157
 
 
158
                delete pushedOp;
 
159
                pushedOp = NULL;
 
160
 
 
161
                return true;
 
162
        }
 
163
 
 
164
        // -- 5 --
 
165
        if (pushedCreateFileSystemOp && &newOp->newPartition() == &pushedCreateFileSystemOp->partition())
 
166
        {
 
167
                log() << i18nc("@info/plain", "Changing file system for a new partition: No new operation required.");
 
168
 
 
169
                FileSystem* oldFs = &newOp->newPartition().fileSystem();
 
170
 
 
171
                newOp->newPartition().setFileSystem(FileSystemFactory::cloneWithNewType(pushedCreateFileSystemOp->newFileSystem()->type(), *oldFs));
 
172
 
 
173
                delete oldFs;
 
174
                oldFs = NULL;
 
175
 
 
176
                delete pushedOp;
 
177
                pushedOp = NULL;
 
178
 
 
179
                return true;
 
180
        }
 
181
 
 
182
        return false;
 
183
}
 
184
 
 
185
/**     Tries to merge an existing CopyOperation with a new Operation pushed on the OperationStack.
 
186
 
 
187
        These are the cases to consider:
 
188
 
 
189
        <ol>
 
190
        <!-- 1 -->
 
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>
 
194
 
 
195
        <!-- 2 -->
 
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>
 
200
        </ol>
 
201
 
 
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
 
205
*/
 
206
bool OperationStack::mergeCopyOperation(Operation*& currentOp, Operation*& pushedOp)
 
207
{
 
208
        CopyOperation* copyOp = dynamic_cast<CopyOperation*>(currentOp);
 
209
 
 
210
        if (copyOp == NULL)
 
211
                return false;
 
212
 
 
213
        DeleteOperation* pushedDeleteOp = dynamic_cast<DeleteOperation*>(pushedOp);
 
214
        CopyOperation* pushedCopyOp = dynamic_cast<CopyOperation*>(pushedOp);
 
215
 
 
216
        // -- 1 --
 
217
        if (pushedDeleteOp && &copyOp->copiedPartition() == &pushedDeleteOp->deletedPartition())
 
218
        {
 
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)
 
222
                {
 
223
                        log() << i18nc("@info/plain", "Deleting a partition just copied: Removing the copy.");
 
224
 
 
225
                        delete pushedOp;
 
226
                        pushedOp = NULL;
 
227
                }
 
228
                else
 
229
                {
 
230
                        log() << i18nc("@info/plain", "Deleting a partition just copied over an existing partition: Removing the copy and deleting the existing partition.");
 
231
 
 
232
                        pushedDeleteOp->setDeletedPartition(copyOp->overwrittenPartition());
 
233
                }
 
234
 
 
235
                copyOp->undo();
 
236
                delete operations().takeAt(operations().indexOf(copyOp));
 
237
 
 
238
                return true;
 
239
        }
 
240
 
 
241
        // -- 2 --
 
242
        if (pushedCopyOp && &copyOp->copiedPartition() == &pushedCopyOp->sourcePartition())
 
243
        {
 
244
                log() << i18nc("@info/plain", "Copying a partition that is itself a copy: Copying the original source partition instead.");
 
245
 
 
246
                pushedCopyOp->setSourcePartition(&copyOp->sourcePartition());
 
247
        }
 
248
 
 
249
        return false;
 
250
}
 
251
 
 
252
/** Tries to merge an existing RestoreOperation with a new Operation pushed on the OperationStack.
 
253
 
 
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.
 
257
 
 
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
 
261
*/
 
262
bool OperationStack::mergeRestoreOperation(Operation*& currentOp, Operation*& pushedOp)
 
263
{
 
264
        RestoreOperation* restoreOp = dynamic_cast<RestoreOperation*>(currentOp);
 
265
 
 
266
        if (restoreOp == NULL)
 
267
                return false;
 
268
 
 
269
        DeleteOperation* pushedDeleteOp = dynamic_cast<DeleteOperation*>(pushedOp);
 
270
 
 
271
        if (pushedDeleteOp && &restoreOp->restorePartition() == &pushedDeleteOp->deletedPartition())
 
272
        {
 
273
                if (restoreOp->overwrittenPartition() == NULL)
 
274
                {
 
275
                        log() << i18nc("@info/plain", "Deleting a partition just restored: Removing the restore operation.");
 
276
 
 
277
                        delete pushedOp;
 
278
                        pushedOp = NULL;
 
279
                }
 
280
                else
 
281
                {
 
282
                        log() << i18nc("@info/plain", "Deleting a partition just restored to an existing partition: Removing the restore operation and deleting the existing partition.");
 
283
 
 
284
                        pushedDeleteOp->setDeletedPartition(restoreOp->overwrittenPartition());
 
285
                }
 
286
 
 
287
                restoreOp->undo();
 
288
                delete operations().takeAt(operations().indexOf(restoreOp));
 
289
 
 
290
                return true;
 
291
        }
 
292
 
 
293
        return false;
 
294
}
 
295
 
 
296
/** Tries to merge an existing SetPartFlagsOperation with a new Operation pushed on the OperationStack.
 
297
 
 
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.
 
300
 
 
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
 
304
*/
 
305
bool OperationStack::mergePartFlagsOperation(Operation*& currentOp, Operation*& pushedOp)
 
306
{
 
307
        SetPartFlagsOperation* partFlagsOp = dynamic_cast<SetPartFlagsOperation*>(currentOp);
 
308
 
 
309
        if (partFlagsOp == NULL)
 
310
                return false;
 
311
 
 
312
        SetPartFlagsOperation* pushedFlagsOp = dynamic_cast<SetPartFlagsOperation*>(pushedOp);
 
313
 
 
314
        if (pushedFlagsOp && &partFlagsOp->flagPartition() == &pushedFlagsOp->flagPartition())
 
315
        {
 
316
                log() << i18nc("@info/plain", "Changing flags again for the same partition: Removing old operation.");
 
317
 
 
318
                pushedFlagsOp->setOldFlags(partFlagsOp->oldFlags());
 
319
                partFlagsOp->undo();
 
320
                delete operations().takeAt(operations().indexOf(partFlagsOp));
 
321
 
 
322
                return true;
 
323
        }
 
324
 
 
325
        return false;
 
326
}
 
327
 
 
328
/** Tries to merge an existing SetFileSystemLabelOperation with a new Operation pushed on the OperationStack.
 
329
 
 
330
        If a FileSystem label for an existing Partition is modified look if there is an existing
 
331
        SetFileSystemLabelOperation for the same Partition.
 
332
 
 
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
 
336
*/
 
337
bool OperationStack::mergePartLabelOperation(Operation*& currentOp, Operation*& pushedOp)
 
338
{
 
339
        SetFileSystemLabelOperation* partLabelOp = dynamic_cast<SetFileSystemLabelOperation*>(currentOp);
 
340
 
 
341
        if (partLabelOp == NULL)
 
342
                return false;
 
343
 
 
344
        SetFileSystemLabelOperation* pushedLabelOp = dynamic_cast<SetFileSystemLabelOperation*>(pushedOp);
 
345
 
 
346
        if (pushedLabelOp && &partLabelOp->labeledPartition() == &pushedLabelOp->labeledPartition())
 
347
        {
 
348
                log() << i18nc("@info/plain", "Changing label again for the same partition: Removing old operation.");
 
349
 
 
350
                pushedLabelOp->setOldLabel(partLabelOp->oldLabel());
 
351
                partLabelOp->undo();
 
352
                delete operations().takeAt(operations().indexOf(partLabelOp));
 
353
 
 
354
                return true;
 
355
        }
 
356
 
 
357
        return false;
 
358
}
 
359
 
 
360
/** Pushes a new Operation on the OperationStack.
 
361
 
 
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().
 
366
 
 
367
        @param o Pointer to the Operation. Must not be NULL.
 
368
*/
 
369
void OperationStack::push(Operation* o)
 
370
{
 
371
        Q_ASSERT(o);
 
372
 
 
373
        foreach (Operation* currentOp, operations())
 
374
        {
 
375
                if (mergeNewOperation(currentOp, o))
 
376
                        break;
 
377
 
 
378
                if (mergeCopyOperation(currentOp, o))
 
379
                        break;
 
380
 
 
381
                if (mergeRestoreOperation(currentOp, o))
 
382
                        break;
 
383
 
 
384
                if (mergePartFlagsOperation(currentOp, o))
 
385
                        break;
 
386
 
 
387
                if (mergePartLabelOperation(currentOp, o))
 
388
                        break;
 
389
        }
 
390
 
 
391
        if (o != NULL)
 
392
        {
 
393
                log() << i18nc("@info/plain", "Add operation: %1", o->description());
 
394
                operations().append(o);
 
395
                o->preview();
 
396
                o->setStatus(Operation::StatusPending);
 
397
        }
 
398
}
 
399
 
 
400
/** Removes the topmost Operation from the OperationStack, calls Operation::undo() on it and deletes it. */
 
401
void OperationStack::pop()
 
402
{
 
403
        Operation* o = operations().takeLast();
 
404
        o->undo();
 
405
        delete o;
 
406
}
 
407
 
 
408
/** Removes all Operations from the OperationStack, calling Operation::undo() on them and deleting them. */
 
409
void OperationStack::clearOperations()
 
410
{
 
411
        while (!operations().isEmpty())
 
412
        {
 
413
                Operation* o = operations().takeLast();
 
414
                if (o->status() == Operation::StatusPending)
 
415
                        o->undo();
 
416
                delete o;
 
417
        }
 
418
}
 
419
 
 
420
/** Clears the list of Devices. */
 
421
void OperationStack::clearDevices()
 
422
{
 
423
        qDeleteAll(m_PreviewDevices);
 
424
        m_PreviewDevices.clear();
 
425
}
 
426
 
 
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
 
430
*/
 
431
Device* OperationStack::findDeviceForPartition(const Partition* p)
 
432
{
 
433
        foreach (Device* d, previewDevices())
 
434
        {
 
435
                if (d->partitionTable() == NULL)
 
436
                        continue;
 
437
 
 
438
                foreach(const Partition* part, d->partitionTable()->children())
 
439
                {
 
440
                        if (part == p)
 
441
                                return d;
 
442
 
 
443
                        foreach (const Partition* child, part->children())
 
444
                                if (child == p)
 
445
                                        return d;
 
446
                }
 
447
        }
 
448
 
 
449
        return NULL;
 
450
}
 
451
 
 
452
/** Adds a Device to the OperationStack
 
453
        @param d pointer to the Device to add. Must not be NULL.
 
454
*/
 
455
void OperationStack::addDevice(Device* d)
 
456
{
 
457
        Q_ASSERT(d);
 
458
 
 
459
        m_PreviewDevices.append(d);
 
460
}