xref: /haiku/src/kits/storage/disk_device/DiskDeviceJobGenerator.cpp (revision 8a990d5228b2d1099e3062180532ba709dfeef6d)
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 	return _AddJob(new(nothrow) UninitializeJob(reference));
527 }
528 
529 
530 // _GenerateSetContentNameJob
531 status_t
532 DiskDeviceJobGenerator::_GenerateSetContentNameJob(BPartition* partition)
533 {
534 	PartitionReference* reference;
535 	status_t error = _GetPartitionReference(partition, reference);
536 	if (error != B_OK)
537 		return error;
538 
539 	SetStringJob* job = new(nothrow) SetStringJob(reference);
540 	if (!job)
541 		return B_NO_MEMORY;
542 
543 	error = job->Init(partition->ContentName(),
544 		B_DISK_DEVICE_JOB_SET_CONTENT_NAME);
545 	if (error != B_OK) {
546 		delete job;
547 		return error;
548 	}
549 
550 	return _AddJob(job);
551 }
552 
553 
554 // _GenerateSetContentParametersJob
555 status_t
556 DiskDeviceJobGenerator::_GenerateSetContentParametersJob(BPartition* partition)
557 {
558 	PartitionReference* reference;
559 	status_t error = _GetPartitionReference(partition, reference);
560 	if (error != B_OK)
561 		return error;
562 
563 	SetStringJob* job = new(nothrow) SetStringJob(reference);
564 	if (!job)
565 		return B_NO_MEMORY;
566 
567 	error = job->Init(partition->ContentParameters(),
568 		B_DISK_DEVICE_JOB_SET_CONTENT_PARAMETERS);
569 	if (error != B_OK) {
570 		delete job;
571 		return error;
572 	}
573 
574 	return _AddJob(job);
575 }
576 
577 
578 // _GenerateDefragmentJob
579 status_t
580 DiskDeviceJobGenerator::_GenerateDefragmentJob(BPartition* partition)
581 {
582 	PartitionReference* reference;
583 	status_t error = _GetPartitionReference(partition, reference);
584 	if (error != B_OK)
585 		return error;
586 
587 	return _AddJob(new(nothrow) DefragmentJob(reference));
588 }
589 
590 
591 // _GenerateRepairJob
592 status_t
593 DiskDeviceJobGenerator::_GenerateRepairJob(BPartition* partition, bool repair)
594 {
595 	PartitionReference* reference;
596 	status_t error = _GetPartitionReference(partition, reference);
597 	if (error != B_OK)
598 		return error;
599 
600 	return _AddJob(new(nothrow) RepairJob(reference, repair));
601 }
602 
603 
604 // _GenerateCreateChildJob
605 status_t
606 DiskDeviceJobGenerator::_GenerateCreateChildJob(BPartition* parent,
607 	BPartition* partition)
608 {
609 	PartitionReference* parentReference;
610 	status_t error = _GetPartitionReference(parent, parentReference);
611 	if (error != B_OK)
612 		return error;
613 
614 	PartitionReference* reference;
615 	error = _GetPartitionReference(partition, reference);
616 	if (error != B_OK)
617 		return error;
618 
619 	CreateChildJob* job = new(nothrow) CreateChildJob(parentReference,
620 		reference);
621 	if (!job)
622 		return B_NO_MEMORY;
623 
624 	error = job->Init(partition->Offset(), partition->Size(), partition->Type(),
625 		partition->Name(), partition->Parameters());
626 	if (error != B_OK) {
627 		delete job;
628 		return error;
629 	}
630 
631 	return _AddJob(job);
632 }
633 
634 
635 // _GenerateDeleteChildJob
636 status_t
637 DiskDeviceJobGenerator::_GenerateDeleteChildJob(BPartition* parent,
638 	BPartition* partition)
639 {
640 	PartitionReference* parentReference;
641 	status_t error = _GetPartitionReference(parent, parentReference);
642 	if (error != B_OK)
643 		return error;
644 
645 	PartitionReference* reference;
646 	error = _GetPartitionReference(partition, reference);
647 	if (error != B_OK)
648 		return error;
649 
650 	return _AddJob(new(nothrow) DeleteChildJob(parentReference, reference));
651 }
652 
653 
654 // _GenerateResizeJob
655 status_t
656 DiskDeviceJobGenerator::_GenerateResizeJob(BPartition* partition)
657 {
658 	BPartition* parent = partition->Parent();
659 	if (!parent)
660 		return B_BAD_VALUE;
661 
662 	PartitionReference* parentReference;
663 	status_t error = _GetPartitionReference(parent, parentReference);
664 	if (error != B_OK)
665 		return error;
666 
667 	PartitionReference* reference;
668 	error = _GetPartitionReference(partition, reference);
669 	if (error != B_OK)
670 		return error;
671 
672 	return _AddJob(new(nothrow) ResizeJob(parentReference, reference,
673 		partition->Size(), partition->ContentSize()));
674 }
675 
676 
677 // _GenerateMoveJob
678 status_t
679 DiskDeviceJobGenerator::_GenerateMoveJob(BPartition* partition)
680 {
681 	BPartition* parent = partition->Parent();
682 	if (!parent)
683 		return B_BAD_VALUE;
684 
685 	PartitionReference* parentReference;
686 	status_t error = _GetPartitionReference(parent, parentReference);
687 	if (error != B_OK)
688 		return error;
689 
690 	PartitionReference* reference;
691 	error = _GetPartitionReference(partition, reference);
692 	if (error != B_OK)
693 		return error;
694 
695 	// collect all descendants whose contents need to be moved
696 	fContentsToMoveCount = 0;
697 	error = _CollectContentsToMove(partition);
698 	if (error != B_OK)
699 		return B_OK;
700 
701 	// create and init the job
702 	MoveJob* job = new(nothrow) MoveJob(parentReference, reference);
703 	if (!job)
704 		return B_NO_MEMORY;
705 
706 	error = job->Init(partition->Offset(), fContentsToMove,
707 		fContentsToMoveCount);
708 	if (error != B_OK) {
709 		delete job;
710 		return error;
711 	}
712 
713 	return _AddJob(job);
714 }
715 
716 
717 // _GenerateSetNameJob
718 status_t
719 DiskDeviceJobGenerator::_GenerateSetNameJob(BPartition* parent,
720 	BPartition* partition)
721 {
722 	PartitionReference* parentReference;
723 	status_t error = _GetPartitionReference(parent, parentReference);
724 	if (error != B_OK)
725 		return error;
726 
727 	PartitionReference* reference;
728 	error = _GetPartitionReference(partition, reference);
729 	if (error != B_OK)
730 		return error;
731 
732 	SetStringJob* job = new(nothrow) SetStringJob(parentReference, reference);
733 	if (!job)
734 		return B_NO_MEMORY;
735 
736 	error = job->Init(partition->Name(), B_DISK_DEVICE_JOB_SET_NAME);
737 	if (error != B_OK) {
738 		delete job;
739 		return error;
740 	}
741 
742 	return _AddJob(job);
743 }
744 
745 
746 // _GenerateSetTypeJob
747 status_t
748 DiskDeviceJobGenerator::_GenerateSetTypeJob(BPartition* parent,
749 	BPartition* partition)
750 {
751 	PartitionReference* parentReference;
752 	status_t error = _GetPartitionReference(parent, parentReference);
753 	if (error != B_OK)
754 		return error;
755 
756 	PartitionReference* reference;
757 	error = _GetPartitionReference(partition, reference);
758 	if (error != B_OK)
759 		return error;
760 
761 	SetStringJob* job = new(nothrow) SetStringJob(parentReference, reference);
762 	if (!job)
763 		return B_NO_MEMORY;
764 
765 	error = job->Init(partition->Type(), B_DISK_DEVICE_JOB_SET_TYPE);
766 	if (error != B_OK) {
767 		delete job;
768 		return error;
769 	}
770 
771 	return _AddJob(job);
772 }
773 
774 
775 // _GenerateSetParametersJob
776 status_t
777 DiskDeviceJobGenerator::_GenerateSetParametersJob(BPartition* parent,
778 	BPartition* partition)
779 {
780 	PartitionReference* parentReference;
781 	status_t error = _GetPartitionReference(parent, parentReference);
782 	if (error != B_OK)
783 		return error;
784 
785 	PartitionReference* reference;
786 	error = _GetPartitionReference(partition, reference);
787 	if (error != B_OK)
788 		return error;
789 
790 	SetStringJob* job = new(nothrow) SetStringJob(parentReference, reference);
791 	if (!job)
792 		return B_NO_MEMORY;
793 
794 	error = job->Init(partition->Parameters(),
795 		B_DISK_DEVICE_JOB_SET_PARAMETERS);
796 	if (error != B_OK) {
797 		delete job;
798 		return error;
799 	}
800 
801 	return _AddJob(job);
802 }
803 
804 
805 // _CollectContentsToMove
806 status_t
807 DiskDeviceJobGenerator::_CollectContentsToMove(BPartition* partition)
808 {
809 	BMutablePartition* shadow = _GetMutablePartition(partition);
810 	if (shadow->Status() == B_PARTITION_UNRECOGNIZED)
811 		return B_ERROR;
812 
813 	// if the partition has contents, push its ID
814 	if (shadow->ContentType()
815 		&& !(shadow->ChangeFlags() & B_PARTITION_CHANGED_INITIALIZATION)) {
816 		status_t error = _PushContentsToMove(partition);
817 		if (error != B_OK)
818 			return error;
819 	}
820 
821 	// recurse
822 	for (int32 i = 0; BPartition* child = partition->ChildAt(i); i++) {
823 		status_t error = _CollectContentsToMove(child);
824 		if (error != B_OK)
825 			return error;
826 	}
827 	return B_OK;
828 }
829 
830 
831 // _PushContentsToMove
832 status_t
833 DiskDeviceJobGenerator::_PushContentsToMove(BPartition* partition)
834 {
835 	if (fContentsToMoveCount >= fPartitionCount)
836 		return B_ERROR;
837 
838 	PartitionReference* reference;
839 	status_t error = _GetPartitionReference(partition, reference);
840 	if (error != B_OK)
841 		return error;
842 
843 	fContentsToMove[fContentsToMoveCount++] = reference;
844 
845 	return B_OK;
846 }
847 
848 
849 // _GetPartitionReference
850 status_t
851 DiskDeviceJobGenerator::_GetPartitionReference(BPartition* partition,
852 	PartitionReference*& reference)
853 {
854 	if (!partition)
855 		return B_BAD_VALUE;
856 
857 	for (int32 i = 0; i < fPartitionCount; i++) {
858 		PartitionRefInfo& info = fPartitionRefs[i];
859 
860 		if (info.partition == partition) {
861 			reference = info.reference;
862 			return B_OK;
863 		}
864 
865 		if (info.partition == NULL) {
866 			// create partition reference
867 			info.reference = new(nothrow) PartitionReference();
868 			if (!info.reference)
869 				return B_NO_MEMORY;
870 
871 			// set partition ID and change counter
872 			user_partition_data* partitionData = partition->fPartitionData;
873 			if (partitionData) {
874 				info.reference->SetPartitionID(partitionData->id);
875 				info.reference->SetChangeCounter(partitionData->change_counter);
876 			}
877 
878 			info.partition = partition;
879 			reference = info.reference;
880 			return B_OK;
881 		}
882 	}
883 
884 	// Out of slots -- that can't happen.
885 	return B_ERROR;
886 }
887 
888 
889 // _CompareMoveInfoOffset
890 int
891 DiskDeviceJobGenerator::_CompareMoveInfoPosition(const void* _a, const void* _b)
892 {
893 	const MoveInfo* a = static_cast<const MoveInfo*>(_a);
894 	const MoveInfo* b = static_cast<const MoveInfo*>(_b);
895 	if (a->position < b->position)
896 		return -1;
897 	if (a->position > b->position)
898 		return 1;
899 	return 0;
900 }
901