xref: /haiku/src/kits/storage/disk_device/DiskDeviceJobGenerator.cpp (revision 4a55cc230cf7566cadcbb23b1928eefff8aea9a2)
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 ((partition->Parameters() != NULL)
414 			&& ((changeFlags & B_PARTITION_CHANGED_PARAMETERS) != 0
415 				|| compare_string(partition->Parameters(),
416 					partitionData->parameters))) {
417 			if (!parent)
418 				return B_BAD_VALUE;
419 
420 			status_t error = _GenerateSetParametersJob(parent, partition);
421 			if (error != B_OK)
422 				return error;
423 		}
424 	}
425 
426 	if (partition->ContentType()) {
427 		// initialize the partition, if required
428 		if (changeFlags & B_PARTITION_CHANGED_INITIALIZATION) {
429 			status_t error = _GenerateInitializeJob(partition);
430 			if (error != B_OK)
431 				return error;
432 		} else {
433 			// partition not (re-)initialized, set content properties
434 
435 			// content name
436 			if ((changeFlags & B_PARTITION_CHANGED_NAME)
437 				|| compare_string(partition->RawContentName(),
438 					partitionData->content_name)) {
439 				status_t error = _GenerateSetContentNameJob(partition);
440 				if (error != B_OK)
441 					return error;
442 			}
443 
444 			// content parameters
445 			if ((partition->ContentParameters() != NULL)
446 				&& ((changeFlags & B_PARTITION_CHANGED_PARAMETERS) != 0
447 					|| compare_string(partition->ContentParameters(),
448 						partitionData->content_parameters))) {
449 				status_t error = _GenerateSetContentParametersJob(partition);
450 				if (error != B_OK)
451 					return error;
452 			}
453 
454 			// defragment
455 			if (changeFlags & B_PARTITION_CHANGED_DEFRAGMENTATION) {
456 				status_t error = _GenerateDefragmentJob(partition);
457 				if (error != B_OK)
458 					return error;
459 			}
460 
461 			// check / repair
462 			bool repair = (changeFlags & B_PARTITION_CHANGED_REPAIR);
463 			if ((changeFlags & B_PARTITION_CHANGED_CHECK)
464 				|| repair) {
465 				status_t error = _GenerateRepairJob(partition, repair);
466 				if (error != B_OK)
467 					return error;
468 			}
469 		}
470 	}
471 
472 	// recurse
473 	for (int32 i = 0; BPartition* child = partition->ChildAt(i); i++) {
474 		status_t error = _GenerateRemainingJobs(partition, child);
475 		if (error != B_OK)
476 			return error;
477 	}
478 
479 	return B_OK;
480 }
481 
482 
483 // _GetMutablePartition
484 BMutablePartition*
485 DiskDeviceJobGenerator::_GetMutablePartition(BPartition* partition)
486 {
487 	if (!partition)
488 		return NULL;
489 
490 	return partition->fDelegate
491 		? partition->fDelegate->MutablePartition() : NULL;
492 }
493 
494 
495 // _GenerateInitializeJob
496 status_t
497 DiskDeviceJobGenerator::_GenerateInitializeJob(BPartition* partition)
498 {
499 	PartitionReference* reference;
500 	status_t error = _GetPartitionReference(partition, reference);
501 	if (error != B_OK)
502 		return error;
503 
504 	InitializeJob* job = new(nothrow) InitializeJob(reference);
505 	if (!job)
506 		return B_NO_MEMORY;
507 
508 	error = job->Init(partition->ContentType(),
509 		partition->RawContentName(), partition->ContentParameters());
510 	if (error != B_OK) {
511 		delete job;
512 		return error;
513 	}
514 
515 	return _AddJob(job);
516 }
517 
518 
519 // _GenerateUninitializeJob
520 status_t
521 DiskDeviceJobGenerator::_GenerateUninitializeJob(BPartition* partition)
522 {
523 	PartitionReference* reference;
524 	status_t error = _GetPartitionReference(partition, reference);
525 	if (error != B_OK)
526 		return error;
527 
528 	BPartition* parent = partition->Parent();
529 	PartitionReference* parentReference = NULL;
530 	if (parent != NULL) {
531 		error = _GetPartitionReference(parent, parentReference);
532 		if (error != B_OK)
533 			return error;
534 	}
535 
536 	return _AddJob(new(nothrow) UninitializeJob(reference, parentReference));
537 }
538 
539 
540 // _GenerateSetContentNameJob
541 status_t
542 DiskDeviceJobGenerator::_GenerateSetContentNameJob(BPartition* partition)
543 {
544 	PartitionReference* reference;
545 	status_t error = _GetPartitionReference(partition, reference);
546 	if (error != B_OK)
547 		return error;
548 
549 	SetStringJob* job = new(nothrow) SetStringJob(reference);
550 	if (!job)
551 		return B_NO_MEMORY;
552 
553 	error = job->Init(partition->RawContentName(),
554 		B_DISK_DEVICE_JOB_SET_CONTENT_NAME);
555 	if (error != B_OK) {
556 		delete job;
557 		return error;
558 	}
559 
560 	return _AddJob(job);
561 }
562 
563 
564 // _GenerateSetContentParametersJob
565 status_t
566 DiskDeviceJobGenerator::_GenerateSetContentParametersJob(BPartition* partition)
567 {
568 	PartitionReference* reference;
569 	status_t error = _GetPartitionReference(partition, reference);
570 	if (error != B_OK)
571 		return error;
572 
573 	SetStringJob* job = new(nothrow) SetStringJob(reference);
574 	if (!job)
575 		return B_NO_MEMORY;
576 
577 	error = job->Init(partition->ContentParameters(),
578 		B_DISK_DEVICE_JOB_SET_CONTENT_PARAMETERS);
579 	if (error != B_OK) {
580 		delete job;
581 		return error;
582 	}
583 
584 	return _AddJob(job);
585 }
586 
587 
588 // _GenerateDefragmentJob
589 status_t
590 DiskDeviceJobGenerator::_GenerateDefragmentJob(BPartition* partition)
591 {
592 	PartitionReference* reference;
593 	status_t error = _GetPartitionReference(partition, reference);
594 	if (error != B_OK)
595 		return error;
596 
597 	return _AddJob(new(nothrow) DefragmentJob(reference));
598 }
599 
600 
601 // _GenerateRepairJob
602 status_t
603 DiskDeviceJobGenerator::_GenerateRepairJob(BPartition* partition, bool repair)
604 {
605 	PartitionReference* reference;
606 	status_t error = _GetPartitionReference(partition, reference);
607 	if (error != B_OK)
608 		return error;
609 
610 	return _AddJob(new(nothrow) RepairJob(reference, repair));
611 }
612 
613 
614 // _GenerateCreateChildJob
615 status_t
616 DiskDeviceJobGenerator::_GenerateCreateChildJob(BPartition* parent,
617 	BPartition* partition)
618 {
619 	PartitionReference* parentReference;
620 	status_t error = _GetPartitionReference(parent, parentReference);
621 	if (error != B_OK)
622 		return error;
623 
624 	PartitionReference* reference;
625 	error = _GetPartitionReference(partition, reference);
626 	if (error != B_OK)
627 		return error;
628 
629 	CreateChildJob* job = new(nothrow) CreateChildJob(parentReference,
630 		reference);
631 	if (!job)
632 		return B_NO_MEMORY;
633 
634 	error = job->Init(partition->Offset(), partition->Size(), partition->Type(),
635 		partition->Name(), partition->Parameters());
636 	if (error != B_OK) {
637 		delete job;
638 		return error;
639 	}
640 
641 	return _AddJob(job);
642 }
643 
644 
645 // _GenerateDeleteChildJob
646 status_t
647 DiskDeviceJobGenerator::_GenerateDeleteChildJob(BPartition* parent,
648 	BPartition* partition)
649 {
650 	PartitionReference* parentReference;
651 	status_t error = _GetPartitionReference(parent, parentReference);
652 	if (error != B_OK)
653 		return error;
654 
655 	PartitionReference* reference;
656 	error = _GetPartitionReference(partition, reference);
657 	if (error != B_OK)
658 		return error;
659 
660 	return _AddJob(new(nothrow) DeleteChildJob(parentReference, reference));
661 }
662 
663 
664 // _GenerateResizeJob
665 status_t
666 DiskDeviceJobGenerator::_GenerateResizeJob(BPartition* partition)
667 {
668 	BPartition* parent = partition->Parent();
669 	if (!parent)
670 		return B_BAD_VALUE;
671 
672 	PartitionReference* parentReference;
673 	status_t error = _GetPartitionReference(parent, parentReference);
674 	if (error != B_OK)
675 		return error;
676 
677 	PartitionReference* reference;
678 	error = _GetPartitionReference(partition, reference);
679 	if (error != B_OK)
680 		return error;
681 
682 	return _AddJob(new(nothrow) ResizeJob(parentReference, reference,
683 		partition->Size(), partition->ContentSize()));
684 }
685 
686 
687 // _GenerateMoveJob
688 status_t
689 DiskDeviceJobGenerator::_GenerateMoveJob(BPartition* partition)
690 {
691 	BPartition* parent = partition->Parent();
692 	if (!parent)
693 		return B_BAD_VALUE;
694 
695 	PartitionReference* parentReference;
696 	status_t error = _GetPartitionReference(parent, parentReference);
697 	if (error != B_OK)
698 		return error;
699 
700 	PartitionReference* reference;
701 	error = _GetPartitionReference(partition, reference);
702 	if (error != B_OK)
703 		return error;
704 
705 	// collect all descendants whose contents need to be moved
706 	fContentsToMoveCount = 0;
707 	error = _CollectContentsToMove(partition);
708 	if (error != B_OK)
709 		return B_OK;
710 
711 	// create and init the job
712 	MoveJob* job = new(nothrow) MoveJob(parentReference, reference);
713 	if (!job)
714 		return B_NO_MEMORY;
715 
716 	error = job->Init(partition->Offset(), fContentsToMove,
717 		fContentsToMoveCount);
718 	if (error != B_OK) {
719 		delete job;
720 		return error;
721 	}
722 
723 	return _AddJob(job);
724 }
725 
726 
727 // _GenerateSetNameJob
728 status_t
729 DiskDeviceJobGenerator::_GenerateSetNameJob(BPartition* parent,
730 	BPartition* partition)
731 {
732 	PartitionReference* parentReference;
733 	status_t error = _GetPartitionReference(parent, parentReference);
734 	if (error != B_OK)
735 		return error;
736 
737 	PartitionReference* reference;
738 	error = _GetPartitionReference(partition, reference);
739 	if (error != B_OK)
740 		return error;
741 
742 	SetStringJob* job = new(nothrow) SetStringJob(parentReference, reference);
743 	if (!job)
744 		return B_NO_MEMORY;
745 
746 	error = job->Init(partition->Name(), B_DISK_DEVICE_JOB_SET_NAME);
747 	if (error != B_OK) {
748 		delete job;
749 		return error;
750 	}
751 
752 	return _AddJob(job);
753 }
754 
755 
756 // _GenerateSetTypeJob
757 status_t
758 DiskDeviceJobGenerator::_GenerateSetTypeJob(BPartition* parent,
759 	BPartition* partition)
760 {
761 	PartitionReference* parentReference;
762 	status_t error = _GetPartitionReference(parent, parentReference);
763 	if (error != B_OK)
764 		return error;
765 
766 	PartitionReference* reference;
767 	error = _GetPartitionReference(partition, reference);
768 	if (error != B_OK)
769 		return error;
770 
771 	SetStringJob* job = new(nothrow) SetStringJob(parentReference, reference);
772 	if (!job)
773 		return B_NO_MEMORY;
774 
775 	error = job->Init(partition->Type(), B_DISK_DEVICE_JOB_SET_TYPE);
776 	if (error != B_OK) {
777 		delete job;
778 		return error;
779 	}
780 
781 	return _AddJob(job);
782 }
783 
784 
785 // _GenerateSetParametersJob
786 status_t
787 DiskDeviceJobGenerator::_GenerateSetParametersJob(BPartition* parent,
788 	BPartition* partition)
789 {
790 	PartitionReference* parentReference;
791 	status_t error = _GetPartitionReference(parent, parentReference);
792 	if (error != B_OK)
793 		return error;
794 
795 	PartitionReference* reference;
796 	error = _GetPartitionReference(partition, reference);
797 	if (error != B_OK)
798 		return error;
799 
800 	SetStringJob* job = new(nothrow) SetStringJob(parentReference, reference);
801 	if (!job)
802 		return B_NO_MEMORY;
803 
804 	error = job->Init(partition->Parameters(),
805 		B_DISK_DEVICE_JOB_SET_PARAMETERS);
806 	if (error != B_OK) {
807 		delete job;
808 		return error;
809 	}
810 
811 	return _AddJob(job);
812 }
813 
814 
815 // _CollectContentsToMove
816 status_t
817 DiskDeviceJobGenerator::_CollectContentsToMove(BPartition* partition)
818 {
819 	BMutablePartition* shadow = _GetMutablePartition(partition);
820 	if (shadow->Status() == B_PARTITION_UNRECOGNIZED)
821 		return B_ERROR;
822 
823 	// if the partition has contents, push its ID
824 	if (shadow->ContentType()
825 		&& !(shadow->ChangeFlags() & B_PARTITION_CHANGED_INITIALIZATION)) {
826 		status_t error = _PushContentsToMove(partition);
827 		if (error != B_OK)
828 			return error;
829 	}
830 
831 	// recurse
832 	for (int32 i = 0; BPartition* child = partition->ChildAt(i); i++) {
833 		status_t error = _CollectContentsToMove(child);
834 		if (error != B_OK)
835 			return error;
836 	}
837 	return B_OK;
838 }
839 
840 
841 // _PushContentsToMove
842 status_t
843 DiskDeviceJobGenerator::_PushContentsToMove(BPartition* partition)
844 {
845 	if (fContentsToMoveCount >= fPartitionCount)
846 		return B_ERROR;
847 
848 	PartitionReference* reference;
849 	status_t error = _GetPartitionReference(partition, reference);
850 	if (error != B_OK)
851 		return error;
852 
853 	fContentsToMove[fContentsToMoveCount++] = reference;
854 
855 	return B_OK;
856 }
857 
858 
859 // _GetPartitionReference
860 status_t
861 DiskDeviceJobGenerator::_GetPartitionReference(BPartition* partition,
862 	PartitionReference*& reference)
863 {
864 	if (!partition)
865 		return B_BAD_VALUE;
866 
867 	for (int32 i = 0; i < fPartitionCount; i++) {
868 		PartitionRefInfo& info = fPartitionRefs[i];
869 
870 		if (info.partition == partition) {
871 			reference = info.reference;
872 			return B_OK;
873 		}
874 
875 		if (info.partition == NULL) {
876 			// create partition reference
877 			info.reference = new(nothrow) PartitionReference();
878 			if (!info.reference)
879 				return B_NO_MEMORY;
880 
881 			// set partition ID and change counter
882 			user_partition_data* partitionData = partition->fPartitionData;
883 			if (partitionData) {
884 				info.reference->SetPartitionID(partitionData->id);
885 				info.reference->SetChangeCounter(partitionData->change_counter);
886 			}
887 
888 			info.partition = partition;
889 			reference = info.reference;
890 			return B_OK;
891 		}
892 	}
893 
894 	// Out of slots -- that can't happen.
895 	return B_ERROR;
896 }
897 
898 
899 // _CompareMoveInfoOffset
900 int
901 DiskDeviceJobGenerator::_CompareMoveInfoPosition(const void* _a, const void* _b)
902 {
903 	const MoveInfo* a = static_cast<const MoveInfo*>(_a);
904 	const MoveInfo* b = static_cast<const MoveInfo*>(_b);
905 	if (a->position < b->position)
906 		return -1;
907 	if (a->position > b->position)
908 		return 1;
909 	return 0;
910 }
911