xref: /haiku/src/kits/storage/disk_device/DiskDeviceJobGenerator.cpp (revision aa3083e086e5a929c061c72983e09d916c548a38)
1 /*
2  * Copyright 2003-2007, Ingo Weinhold, ingo_weinhold@gmx.de.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 #include "DiskDeviceJobGenerator.h"
7 
8 #include <new>
9 
10 #include <stdlib.h>
11 #include <string.h>
12 
13 #include <DiskDevice.h>
14 #include <MutablePartition.h>
15 
16 #include <ddm_userland_interface_defs.h>
17 
18 #include "DiskDeviceJob.h"
19 #include "DiskDeviceJobQueue.h"
20 #include "PartitionDelegate.h"
21 #include "PartitionReference.h"
22 
23 #include "CreateChildJob.h"
24 #include "DeleteChildJob.h"
25 #include "DefragmentJob.h"
26 #include "InitializeJob.h"
27 #include "MoveJob.h"
28 #include "RepairJob.h"
29 #include "ResizeJob.h"
30 #include "SetStringJob.h"
31 #include "UninitializeJob.h"
32 
33 
34 #undef TRACE
35 #define TRACE(x...)
36 //#define TRACE(x...)	printf(x)
37 
38 
39 using std::nothrow;
40 
41 
42 // compare_string
43 /*!	\brief \c NULL aware strcmp().
44 
45 	\c NULL is considered the least of all strings. \c NULL equals \c NULL.
46 
47 	\param str1 First string.
48 	\param str2 Second string.
49 	\return A value less than 0, if \a str1 is less than \a str2,
50 			0, if they are equal, or a value greater than 0, if
51 			\a str1 is greater \a str2.
52 */
53 static inline int
54 compare_string(const char* str1, const char* str2)
55 {
56 	if (str1 == NULL) {
57 		if (str2 == NULL)
58 			return 0;
59 		return 1;
60 	} else if (str2 == NULL)
61 		return -1;
62 
63 	return strcmp(str1, str2);
64 }
65 
66 
67 // MoveInfo
68 struct DiskDeviceJobGenerator::MoveInfo {
69 	BPartition*	partition;
70 	off_t		position;
71 	off_t		target_position;
72 	off_t		size;
73 };
74 
75 
76 // PartitionRefInfo
77 struct DiskDeviceJobGenerator::PartitionRefInfo {
78 	PartitionRefInfo()
79 		: partition(NULL),
80 		  reference(NULL)
81 	{
82 	}
83 
84 	~PartitionRefInfo()
85 	{
86 		if (reference)
87 			reference->ReleaseReference();
88 	}
89 
90 	BPartition*			partition;
91 	PartitionReference*	reference;
92 };
93 
94 
95 // constructor
96 DiskDeviceJobGenerator::DiskDeviceJobGenerator(BDiskDevice* device,
97 		DiskDeviceJobQueue* jobQueue)
98 	: fDevice(device),
99 	  fJobQueue(jobQueue),
100 	  fMoveInfos(NULL),
101 	  fPartitionRefs(NULL),
102 	  fContentsToMove(NULL),
103 	  fContentsToMoveCount(0)
104 {
105 	// Make sure the arrays are big enough (worst case: all old partitions have
106 	// been deleted and new ones been created).
107 	fPartitionCount = fDevice->CountDescendants()
108 		+ fDevice->_CountDescendants();
109 
110 	fMoveInfos = new(nothrow) MoveInfo[fPartitionCount];
111 	fPartitionRefs = new(nothrow) PartitionRefInfo[fPartitionCount];
112 	fContentsToMove = new(nothrow) PartitionReference*[fPartitionCount];
113 }
114 
115 
116 // destructor
117 DiskDeviceJobGenerator::~DiskDeviceJobGenerator()
118 {
119 	delete[] fMoveInfos;
120 	delete[] fPartitionRefs;
121 	delete[] fContentsToMove;
122 }
123 
124 
125 // GenerateJobs
126 status_t
127 DiskDeviceJobGenerator::GenerateJobs()
128 {
129 	// check parameters
130 	if (!fDevice || !fJobQueue)
131 		return B_BAD_VALUE;
132 
133 	if (!fMoveInfos || !fPartitionRefs || !fContentsToMove)
134 		return B_NO_MEMORY;
135 
136 	// 1) Generate jobs for all physical partitions that don't have an
137 	// associated shadow partition, i.e. those that shall be deleted.
138 	// 2) Generate uninitialize jobs for all partition whose initialization
139 	// changes, also those that shall be initialized with a disk system.
140 	// This simplifies moving and resizing.
141 	status_t error = _GenerateCleanupJobs(fDevice);
142 	if (error != B_OK) {
143 		TRACE("DiskDeviceJobGenerator::GenerateJobs(): _GenerateCleanupJobs() "
144 			"failed\n");
145 		return error;
146 	}
147 
148 	// Generate jobs that move and resize the remaining physical partitions
149 	// to their final position/size.
150 	error = _GeneratePlacementJobs(fDevice);
151 	if (error != B_OK) {
152 		TRACE("DiskDeviceJobGenerator::GenerateJobs(): "
153 			"_GeneratePlacementJobs() failed\n");
154 		return error;
155 	}
156 
157 	// Generate the remaining jobs in one run: initialization, creation of
158 	// partitions, and changing of name, content name, type, parameters, and
159 	// content parameters.
160 	error = _GenerateRemainingJobs(NULL, fDevice);
161 	if (error != B_OK) {
162 		TRACE("DiskDeviceJobGenerator::GenerateJobs(): "
163 			"_GenerateRemainingJobs() failed\n");
164 		return error;
165 	}
166 
167 	TRACE("DiskDeviceJobGenerator::GenerateJobs(): succeeded\n");
168 
169 	return B_OK;
170 }
171 
172 
173 // _AddJob
174 status_t
175 DiskDeviceJobGenerator::_AddJob(DiskDeviceJob* job)
176 {
177 	if (!job)
178 		return B_NO_MEMORY;
179 
180 	status_t error = fJobQueue->AddJob(job);
181 	if (error != B_OK)
182 		delete job;
183 
184 	return error;
185 }
186 
187 
188 // _GenerateCleanupJobs
189 status_t
190 DiskDeviceJobGenerator::_GenerateCleanupJobs(BPartition* partition)
191 {
192 // TODO: Depending on how this shall be handled, we might want to unmount
193 // all descendants of a partition to be uninitialized or removed.
194 	if (BMutablePartition* shadow = _GetMutablePartition(partition)) {
195 		if ((shadow->ChangeFlags() & B_PARTITION_CHANGED_INITIALIZATION)
196 			&& partition->fPartitionData->content_type) {
197 			// partition changes initialization
198 			status_t error = _GenerateUninitializeJob(partition);
199 			if (error != B_OK)
200 				return error;
201 		} else {
202 			// recurse
203 			for (int32 i = 0; BPartition* child = partition->_ChildAt(i); i++) {
204 				status_t error = _GenerateCleanupJobs(child);
205 				if (error != B_OK)
206 					return error;
207 			}
208 		}
209 	} else if (BPartition* parent = partition->Parent()) {
210 		// create job and add it to the queue
211 		status_t error = _GenerateDeleteChildJob(parent, partition);
212 		if (error != B_OK)
213 			return error;
214 	}
215 	return B_OK;
216 }
217 
218 
219 // _GeneratePlacementJobs
220 status_t
221 DiskDeviceJobGenerator::_GeneratePlacementJobs(BPartition* partition)
222 {
223 	if (BMutablePartition* shadow = _GetMutablePartition(partition)) {
224 		// Don't resize/move partitions that have an unrecognized contents.
225 		// They must have been uninitialized before.
226 		if (shadow->Status() == B_PARTITION_UNRECOGNIZED
227 			&& (shadow->Size() != partition->Size()
228 				|| shadow->Offset() != partition->Offset())) {
229 			return B_ERROR;
230 		}
231 
232 		if (shadow->Size() > partition->Size()) {
233 			// size grows: resize first
234 			status_t error = _GenerateResizeJob(partition);
235 			if (error != B_OK)
236 				return error;
237 		}
238 
239 		// place the children
240 		status_t error = _GenerateChildPlacementJobs(partition);
241 		if (error != B_OK)
242 			return error;
243 
244 		if (shadow->Size() < partition->Size()) {
245 			// size shrinks: resize now
246 			status_t error = _GenerateResizeJob(partition);
247 			if (error != B_OK)
248 				return error;
249 		}
250 	}
251 
252 	return B_OK;
253 }
254 
255 
256 // _GenerateChildPlacementJobs
257 status_t
258 DiskDeviceJobGenerator::_GenerateChildPlacementJobs(BPartition* partition)
259 {
260 	BMutablePartition* shadow = _GetMutablePartition(partition);
261 
262 	// nothing to do, if the partition contains no partitioning system or
263 	// shall be re-initialized
264 	if (!shadow->ContentType()
265 		|| (shadow->ChangeFlags() & B_PARTITION_CHANGED_INITIALIZATION)) {
266 		return B_OK;
267 	}
268 
269 	// first resize all children that shall shrink and place their descendants
270 	int32 childCount = 0;
271 	int32 moveForth = 0;
272 	int32 moveBack = 0;
273 
274 	for (int32 i = 0; BPartition* child = partition->_ChildAt(i); i++) {
275 		if (BMutablePartition* childShadow = _GetMutablePartition(child)) {
276 			// add a MoveInfo for the child
277 			MoveInfo& info = fMoveInfos[childCount];
278 			childCount++;
279 			info.partition = child;
280 			info.position = child->Offset();
281 			info.target_position = childShadow->Offset();
282 			info.size = child->Size();
283 
284 			if (info.position < info.target_position)
285 				moveForth++;
286 			else if (info.position > info.target_position)
287 				moveBack++;
288 
289 			// resize the child, if it shall shrink
290 			if (childShadow->Size() < child->Size()) {
291 				status_t error = _GeneratePlacementJobs(child);
292 				if (error != B_OK)
293 					return error;
294 				info.size = childShadow->Size();
295 			}
296 		}
297 	}
298 
299 	// sort the move infos
300 	if (childCount > 0 && moveForth + moveBack > 0) {
301 		qsort(fMoveInfos, childCount, sizeof(MoveInfo),
302 			_CompareMoveInfoPosition);
303 	}
304 
305 	// move the children to their final positions
306 	while (moveForth + moveBack > 0) {
307 		int32 moved = 0;
308 		if (moveForth < moveBack) {
309 			// move children back
310 			for (int32 i = 0; i < childCount; i++) {
311 				MoveInfo& info = fMoveInfos[i];
312 				if (info.position > info.target_position) {
313 					if (i == 0
314 						|| info.target_position >= fMoveInfos[i - 1].position
315 							+ fMoveInfos[i - 1].size) {
316 						// check OK -- the partition wouldn't be moved before
317 						// the end of the preceding one
318 						status_t error = _GenerateMoveJob(info.partition);
319 						if (error != B_OK)
320 							return error;
321 						info.position = info.target_position;
322 						moved++;
323 						moveBack--;
324 					}
325 				}
326 			}
327 		} else {
328 			// move children forth
329 			for (int32 i = childCount - 1; i >= 0; i--) {
330 				MoveInfo &info = fMoveInfos[i];
331 				if (info.position > info.target_position) {
332 					if (i == childCount - 1
333 						|| info.target_position + info.size
334 							<= fMoveInfos[i - 1].position) {
335 						// check OK -- the partition wouldn't be moved before
336 						// the end of the preceding one
337 						status_t error = _GenerateMoveJob(info.partition);
338 						if (error != B_OK)
339 							return error;
340 						info.position = info.target_position;
341 						moved++;
342 						moveForth--;
343 					}
344 				}
345 			}
346 		}
347 
348 		// terminate, if no partition could be moved
349 		if (moved == 0)
350 			return B_ERROR;
351 	}
352 
353 	// now resize all children that shall grow/keep their size and place
354 	// their descendants
355 	for (int32 i = 0; BPartition* child = partition->_ChildAt(i); i++) {
356 		if (BMutablePartition* childShadow = _GetMutablePartition(child)) {
357 			if (childShadow->Size() >= child->Size()) {
358 				status_t error = _GeneratePlacementJobs(child);
359 				if (error != B_OK)
360 					return error;
361 			}
362 		}
363 	}
364 
365 	return B_OK;
366 }
367 
368 
369 // _GenerateRemainingJobs
370 status_t
371 DiskDeviceJobGenerator::_GenerateRemainingJobs(BPartition* parent,
372 	BPartition* partition)
373 {
374 	user_partition_data* partitionData = partition->fPartitionData;
375 
376 	uint32 changeFlags
377 		= partition->fDelegate->MutablePartition()->ChangeFlags();
378 
379 	// create the partition, if not existing yet
380 	if (!partitionData) {
381 		if (!parent)
382 			return B_BAD_VALUE;
383 
384 		status_t error = _GenerateCreateChildJob(parent, partition);
385 		if (error != B_OK)
386 			return error;
387 	} else {
388 		// partition already exists: set non-content properties
389 
390 		// name
391 		if ((changeFlags & B_PARTITION_CHANGED_NAME)
392 			|| compare_string(partition->Name(), partitionData->name)) {
393 			if (!parent)
394 				return B_BAD_VALUE;
395 
396 			status_t error = _GenerateSetNameJob(parent, partition);
397 			if (error != B_OK)
398 				return error;
399 		}
400 
401 		// type
402 		if ((changeFlags & B_PARTITION_CHANGED_TYPE)
403 			|| compare_string(partition->Type(), partitionData->type)) {
404 			if (!parent)
405 				return B_BAD_VALUE;
406 
407 			status_t error = _GenerateSetTypeJob(parent, partition);
408 			if (error != B_OK)
409 				return error;
410 		}
411 
412 		// parameters
413 		if ((changeFlags & B_PARTITION_CHANGED_PARAMETERS)
414 			|| compare_string(partition->Parameters(),
415 				partitionData->parameters)) {
416 			if (!parent)
417 				return B_BAD_VALUE;
418 
419 			status_t error = _GenerateSetParametersJob(parent, partition);
420 			if (error != B_OK)
421 				return error;
422 		}
423 	}
424 
425 	if (partition->ContentType()) {
426 		// initialize the partition, if required
427 		if (changeFlags & B_PARTITION_CHANGED_INITIALIZATION) {
428 			status_t error = _GenerateInitializeJob(partition);
429 			if (error != B_OK)
430 				return error;
431 		} else {
432 			// partition not (re-)initialized, set content properties
433 
434 			// content name
435 			if ((changeFlags & B_PARTITION_CHANGED_NAME)
436 				|| compare_string(partition->ContentName(),
437 					partitionData->content_name)) {
438 				status_t error = _GenerateSetContentNameJob(partition);
439 				if (error != B_OK)
440 					return error;
441 			}
442 
443 			// content parameters
444 			if ((changeFlags & B_PARTITION_CHANGED_PARAMETERS)
445 				|| compare_string(partition->ContentParameters(),
446 					partitionData->content_parameters)) {
447 				status_t error = _GenerateSetContentParametersJob(partition);
448 				if (error != B_OK)
449 					return error;
450 			}
451 
452 			// defragment
453 			if (changeFlags & B_PARTITION_CHANGED_DEFRAGMENTATION) {
454 				status_t error = _GenerateDefragmentJob(partition);
455 				if (error != B_OK)
456 					return error;
457 			}
458 
459 			// check / repair
460 			bool repair = (changeFlags & B_PARTITION_CHANGED_REPAIR);
461 			if ((changeFlags & B_PARTITION_CHANGED_CHECK)
462 				|| repair) {
463 				status_t error = _GenerateRepairJob(partition, repair);
464 				if (error != B_OK)
465 					return error;
466 			}
467 		}
468 	}
469 
470 	// recurse
471 	for (int32 i = 0; BPartition* child = partition->ChildAt(i); i++) {
472 		status_t error = _GenerateRemainingJobs(partition, child);
473 		if (error != B_OK)
474 			return error;
475 	}
476 
477 	return B_OK;
478 }
479 
480 
481 // _GetMutablePartition
482 BMutablePartition*
483 DiskDeviceJobGenerator::_GetMutablePartition(BPartition* partition)
484 {
485 	if (!partition)
486 		return NULL;
487 
488 	return partition->fDelegate
489 		? partition->fDelegate->MutablePartition() : NULL;
490 }
491 
492 
493 // _GenerateInitializeJob
494 status_t
495 DiskDeviceJobGenerator::_GenerateInitializeJob(BPartition* partition)
496 {
497 	PartitionReference* reference;
498 	status_t error = _GetPartitionReference(partition, reference);
499 	if (error != B_OK)
500 		return error;
501 
502 	InitializeJob* job = new(nothrow) InitializeJob(reference);
503 	if (!job)
504 		return B_NO_MEMORY;
505 
506 	error = job->Init(partition->ContentType(),
507 		partition->ContentName(), partition->ContentParameters());
508 	if (error != B_OK) {
509 		delete job;
510 		return error;
511 	}
512 
513 	return _AddJob(job);
514 }
515 
516 
517 // _GenerateUninitializeJob
518 status_t
519 DiskDeviceJobGenerator::_GenerateUninitializeJob(BPartition* partition)
520 {
521 	PartitionReference* reference;
522 	status_t error = _GetPartitionReference(partition, reference);
523 	if (error != B_OK)
524 		return error;
525 
526 	BPartition* parent = partition->Parent();
527 	PartitionReference* parentReference = NULL;
528 	if (parent != NULL) {
529 		error = _GetPartitionReference(parent, parentReference);
530 		if (error != B_OK)
531 			return error;
532 	}
533 
534 	return _AddJob(new(nothrow) UninitializeJob(reference, parentReference));
535 }
536 
537 
538 // _GenerateSetContentNameJob
539 status_t
540 DiskDeviceJobGenerator::_GenerateSetContentNameJob(BPartition* partition)
541 {
542 	PartitionReference* reference;
543 	status_t error = _GetPartitionReference(partition, reference);
544 	if (error != B_OK)
545 		return error;
546 
547 	SetStringJob* job = new(nothrow) SetStringJob(reference);
548 	if (!job)
549 		return B_NO_MEMORY;
550 
551 	error = job->Init(partition->ContentName(),
552 		B_DISK_DEVICE_JOB_SET_CONTENT_NAME);
553 	if (error != B_OK) {
554 		delete job;
555 		return error;
556 	}
557 
558 	return _AddJob(job);
559 }
560 
561 
562 // _GenerateSetContentParametersJob
563 status_t
564 DiskDeviceJobGenerator::_GenerateSetContentParametersJob(BPartition* partition)
565 {
566 	PartitionReference* reference;
567 	status_t error = _GetPartitionReference(partition, reference);
568 	if (error != B_OK)
569 		return error;
570 
571 	SetStringJob* job = new(nothrow) SetStringJob(reference);
572 	if (!job)
573 		return B_NO_MEMORY;
574 
575 	error = job->Init(partition->ContentParameters(),
576 		B_DISK_DEVICE_JOB_SET_CONTENT_PARAMETERS);
577 	if (error != B_OK) {
578 		delete job;
579 		return error;
580 	}
581 
582 	return _AddJob(job);
583 }
584 
585 
586 // _GenerateDefragmentJob
587 status_t
588 DiskDeviceJobGenerator::_GenerateDefragmentJob(BPartition* partition)
589 {
590 	PartitionReference* reference;
591 	status_t error = _GetPartitionReference(partition, reference);
592 	if (error != B_OK)
593 		return error;
594 
595 	return _AddJob(new(nothrow) DefragmentJob(reference));
596 }
597 
598 
599 // _GenerateRepairJob
600 status_t
601 DiskDeviceJobGenerator::_GenerateRepairJob(BPartition* partition, bool repair)
602 {
603 	PartitionReference* reference;
604 	status_t error = _GetPartitionReference(partition, reference);
605 	if (error != B_OK)
606 		return error;
607 
608 	return _AddJob(new(nothrow) RepairJob(reference, repair));
609 }
610 
611 
612 // _GenerateCreateChildJob
613 status_t
614 DiskDeviceJobGenerator::_GenerateCreateChildJob(BPartition* parent,
615 	BPartition* partition)
616 {
617 	PartitionReference* parentReference;
618 	status_t error = _GetPartitionReference(parent, parentReference);
619 	if (error != B_OK)
620 		return error;
621 
622 	PartitionReference* reference;
623 	error = _GetPartitionReference(partition, reference);
624 	if (error != B_OK)
625 		return error;
626 
627 	CreateChildJob* job = new(nothrow) CreateChildJob(parentReference,
628 		reference);
629 	if (!job)
630 		return B_NO_MEMORY;
631 
632 	error = job->Init(partition->Offset(), partition->Size(), partition->Type(),
633 		partition->Name(), partition->Parameters());
634 	if (error != B_OK) {
635 		delete job;
636 		return error;
637 	}
638 
639 	return _AddJob(job);
640 }
641 
642 
643 // _GenerateDeleteChildJob
644 status_t
645 DiskDeviceJobGenerator::_GenerateDeleteChildJob(BPartition* parent,
646 	BPartition* partition)
647 {
648 	PartitionReference* parentReference;
649 	status_t error = _GetPartitionReference(parent, parentReference);
650 	if (error != B_OK)
651 		return error;
652 
653 	PartitionReference* reference;
654 	error = _GetPartitionReference(partition, reference);
655 	if (error != B_OK)
656 		return error;
657 
658 	return _AddJob(new(nothrow) DeleteChildJob(parentReference, reference));
659 }
660 
661 
662 // _GenerateResizeJob
663 status_t
664 DiskDeviceJobGenerator::_GenerateResizeJob(BPartition* partition)
665 {
666 	BPartition* parent = partition->Parent();
667 	if (!parent)
668 		return B_BAD_VALUE;
669 
670 	PartitionReference* parentReference;
671 	status_t error = _GetPartitionReference(parent, parentReference);
672 	if (error != B_OK)
673 		return error;
674 
675 	PartitionReference* reference;
676 	error = _GetPartitionReference(partition, reference);
677 	if (error != B_OK)
678 		return error;
679 
680 	return _AddJob(new(nothrow) ResizeJob(parentReference, reference,
681 		partition->Size(), partition->ContentSize()));
682 }
683 
684 
685 // _GenerateMoveJob
686 status_t
687 DiskDeviceJobGenerator::_GenerateMoveJob(BPartition* partition)
688 {
689 	BPartition* parent = partition->Parent();
690 	if (!parent)
691 		return B_BAD_VALUE;
692 
693 	PartitionReference* parentReference;
694 	status_t error = _GetPartitionReference(parent, parentReference);
695 	if (error != B_OK)
696 		return error;
697 
698 	PartitionReference* reference;
699 	error = _GetPartitionReference(partition, reference);
700 	if (error != B_OK)
701 		return error;
702 
703 	// collect all descendants whose contents need to be moved
704 	fContentsToMoveCount = 0;
705 	error = _CollectContentsToMove(partition);
706 	if (error != B_OK)
707 		return B_OK;
708 
709 	// create and init the job
710 	MoveJob* job = new(nothrow) MoveJob(parentReference, reference);
711 	if (!job)
712 		return B_NO_MEMORY;
713 
714 	error = job->Init(partition->Offset(), fContentsToMove,
715 		fContentsToMoveCount);
716 	if (error != B_OK) {
717 		delete job;
718 		return error;
719 	}
720 
721 	return _AddJob(job);
722 }
723 
724 
725 // _GenerateSetNameJob
726 status_t
727 DiskDeviceJobGenerator::_GenerateSetNameJob(BPartition* parent,
728 	BPartition* partition)
729 {
730 	PartitionReference* parentReference;
731 	status_t error = _GetPartitionReference(parent, parentReference);
732 	if (error != B_OK)
733 		return error;
734 
735 	PartitionReference* reference;
736 	error = _GetPartitionReference(partition, reference);
737 	if (error != B_OK)
738 		return error;
739 
740 	SetStringJob* job = new(nothrow) SetStringJob(parentReference, reference);
741 	if (!job)
742 		return B_NO_MEMORY;
743 
744 	error = job->Init(partition->Name(), B_DISK_DEVICE_JOB_SET_NAME);
745 	if (error != B_OK) {
746 		delete job;
747 		return error;
748 	}
749 
750 	return _AddJob(job);
751 }
752 
753 
754 // _GenerateSetTypeJob
755 status_t
756 DiskDeviceJobGenerator::_GenerateSetTypeJob(BPartition* parent,
757 	BPartition* partition)
758 {
759 	PartitionReference* parentReference;
760 	status_t error = _GetPartitionReference(parent, parentReference);
761 	if (error != B_OK)
762 		return error;
763 
764 	PartitionReference* reference;
765 	error = _GetPartitionReference(partition, reference);
766 	if (error != B_OK)
767 		return error;
768 
769 	SetStringJob* job = new(nothrow) SetStringJob(parentReference, reference);
770 	if (!job)
771 		return B_NO_MEMORY;
772 
773 	error = job->Init(partition->Type(), B_DISK_DEVICE_JOB_SET_TYPE);
774 	if (error != B_OK) {
775 		delete job;
776 		return error;
777 	}
778 
779 	return _AddJob(job);
780 }
781 
782 
783 // _GenerateSetParametersJob
784 status_t
785 DiskDeviceJobGenerator::_GenerateSetParametersJob(BPartition* parent,
786 	BPartition* partition)
787 {
788 	PartitionReference* parentReference;
789 	status_t error = _GetPartitionReference(parent, parentReference);
790 	if (error != B_OK)
791 		return error;
792 
793 	PartitionReference* reference;
794 	error = _GetPartitionReference(partition, reference);
795 	if (error != B_OK)
796 		return error;
797 
798 	SetStringJob* job = new(nothrow) SetStringJob(parentReference, reference);
799 	if (!job)
800 		return B_NO_MEMORY;
801 
802 	error = job->Init(partition->Parameters(),
803 		B_DISK_DEVICE_JOB_SET_PARAMETERS);
804 	if (error != B_OK) {
805 		delete job;
806 		return error;
807 	}
808 
809 	return _AddJob(job);
810 }
811 
812 
813 // _CollectContentsToMove
814 status_t
815 DiskDeviceJobGenerator::_CollectContentsToMove(BPartition* partition)
816 {
817 	BMutablePartition* shadow = _GetMutablePartition(partition);
818 	if (shadow->Status() == B_PARTITION_UNRECOGNIZED)
819 		return B_ERROR;
820 
821 	// if the partition has contents, push its ID
822 	if (shadow->ContentType()
823 		&& !(shadow->ChangeFlags() & B_PARTITION_CHANGED_INITIALIZATION)) {
824 		status_t error = _PushContentsToMove(partition);
825 		if (error != B_OK)
826 			return error;
827 	}
828 
829 	// recurse
830 	for (int32 i = 0; BPartition* child = partition->ChildAt(i); i++) {
831 		status_t error = _CollectContentsToMove(child);
832 		if (error != B_OK)
833 			return error;
834 	}
835 	return B_OK;
836 }
837 
838 
839 // _PushContentsToMove
840 status_t
841 DiskDeviceJobGenerator::_PushContentsToMove(BPartition* partition)
842 {
843 	if (fContentsToMoveCount >= fPartitionCount)
844 		return B_ERROR;
845 
846 	PartitionReference* reference;
847 	status_t error = _GetPartitionReference(partition, reference);
848 	if (error != B_OK)
849 		return error;
850 
851 	fContentsToMove[fContentsToMoveCount++] = reference;
852 
853 	return B_OK;
854 }
855 
856 
857 // _GetPartitionReference
858 status_t
859 DiskDeviceJobGenerator::_GetPartitionReference(BPartition* partition,
860 	PartitionReference*& reference)
861 {
862 	if (!partition)
863 		return B_BAD_VALUE;
864 
865 	for (int32 i = 0; i < fPartitionCount; i++) {
866 		PartitionRefInfo& info = fPartitionRefs[i];
867 
868 		if (info.partition == partition) {
869 			reference = info.reference;
870 			return B_OK;
871 		}
872 
873 		if (info.partition == NULL) {
874 			// create partition reference
875 			info.reference = new(nothrow) PartitionReference();
876 			if (!info.reference)
877 				return B_NO_MEMORY;
878 
879 			// set partition ID and change counter
880 			user_partition_data* partitionData = partition->fPartitionData;
881 			if (partitionData) {
882 				info.reference->SetPartitionID(partitionData->id);
883 				info.reference->SetChangeCounter(partitionData->change_counter);
884 			}
885 
886 			info.partition = partition;
887 			reference = info.reference;
888 			return B_OK;
889 		}
890 	}
891 
892 	// Out of slots -- that can't happen.
893 	return B_ERROR;
894 }
895 
896 
897 // _CompareMoveInfoOffset
898 int
899 DiskDeviceJobGenerator::_CompareMoveInfoPosition(const void* _a, const void* _b)
900 {
901 	const MoveInfo* a = static_cast<const MoveInfo*>(_a);
902 	const MoveInfo* b = static_cast<const MoveInfo*>(_b);
903 	if (a->position < b->position)
904 		return -1;
905 	if (a->position > b->position)
906 		return 1;
907 	return 0;
908 }
909