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
39using 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*/
53static inline int
54compare_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
68struct DiskDeviceJobGenerator::MoveInfo {
69	BPartition*	partition;
70	off_t		position;
71	off_t		target_position;
72	off_t		size;
73};
74
75
76// PartitionRefInfo
77struct 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
96DiskDeviceJobGenerator::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
117DiskDeviceJobGenerator::~DiskDeviceJobGenerator()
118{
119	delete[] fMoveInfos;
120	delete[] fPartitionRefs;
121	delete[] fContentsToMove;
122}
123
124
125// GenerateJobs
126status_t
127DiskDeviceJobGenerator::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
174status_t
175DiskDeviceJobGenerator::_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
189status_t
190DiskDeviceJobGenerator::_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
220status_t
221DiskDeviceJobGenerator::_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
257status_t
258DiskDeviceJobGenerator::_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
370status_t
371DiskDeviceJobGenerator::_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
482BMutablePartition*
483DiskDeviceJobGenerator::_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
494status_t
495DiskDeviceJobGenerator::_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
518status_t
519DiskDeviceJobGenerator::_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
531status_t
532DiskDeviceJobGenerator::_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
555status_t
556DiskDeviceJobGenerator::_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
579status_t
580DiskDeviceJobGenerator::_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
592status_t
593DiskDeviceJobGenerator::_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
605status_t
606DiskDeviceJobGenerator::_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
636status_t
637DiskDeviceJobGenerator::_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
655status_t
656DiskDeviceJobGenerator::_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
678status_t
679DiskDeviceJobGenerator::_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
718status_t
719DiskDeviceJobGenerator::_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
747status_t
748DiskDeviceJobGenerator::_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
776status_t
777DiskDeviceJobGenerator::_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
806status_t
807DiskDeviceJobGenerator::_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
832status_t
833DiskDeviceJobGenerator::_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
850status_t
851DiskDeviceJobGenerator::_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
890int
891DiskDeviceJobGenerator::_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