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