xref: /haiku/src/kits/storage/disk_device/DiskDeviceJobGenerator.cpp (revision d9cebac2b77547b7064f22497514eecd2d047160)
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 <disk_device_manager/ddm_userland_interface.h>
16 
17 #include "DiskDeviceJob.h"
18 #include "DiskDeviceJobQueue.h"
19 #include "PartitionDelegate.h"
20 #include "PartitionReference.h"
21 
22 #include "InitializeJob.h"
23 
24 
25 using std::nothrow;
26 
27 
28 // compare_string
29 /*!	\brief \c NULL aware strcmp().
30 
31 	\c NULL is considered the least of all strings. \c NULL equals \c NULL.
32 
33 	\param str1 First string.
34 	\param str2 Second string.
35 	\return A value less than 0, if \a str1 is less than \a str2,
36 			0, if they are equal, or a value greater than 0, if
37 			\a str1 is greater \a str2.
38 */
39 static inline int
40 compare_string(const char* str1, const char* str2)
41 {
42 	if (str1 == NULL) {
43 		if (str2 == NULL)
44 			return 0;
45 		return 1;
46 	} else if (str2 == NULL)
47 		return -1;
48 
49 	return strcmp(str1, str2);
50 }
51 
52 
53 // MoveInfo
54 struct DiskDeviceJobGenerator::MoveInfo {
55 	BPartition*	partition;
56 	off_t		position;
57 	off_t		target_position;
58 	off_t		size;
59 };
60 
61 
62 // PartitionRefInfo
63 struct DiskDeviceJobGenerator::PartitionRefInfo {
64 	PartitionRefInfo()
65 		: partition(NULL),
66 		  reference(NULL)
67 	{
68 	}
69 
70 	~PartitionRefInfo()
71 	{
72 		if (reference)
73 			reference->RemoveReference();
74 	}
75 
76 	BPartition*			partition;
77 	PartitionReference*	reference;
78 };
79 
80 
81 // constructor
82 DiskDeviceJobGenerator::DiskDeviceJobGenerator(BDiskDevice* device,
83 		DiskDeviceJobQueue* jobQueue)
84 	: fDevice(device),
85 	  fJobQueue(jobQueue),
86 	  fMoveInfos(NULL),
87 	  fPartitionRefs(NULL)
88 {
89 	fPartitionCount = fDevice->CountDescendants();
90 
91 	fMoveInfos = new(nothrow) MoveInfo[fPartitionCount];
92 //	fPartitionIDs = new(nothrow) partition_id[fPartitionCount];
93 	fPartitionRefs = new(nothrow) PartitionRefInfo[fPartitionCount];
94 }
95 
96 
97 // destructor
98 DiskDeviceJobGenerator::~DiskDeviceJobGenerator()
99 {
100 	delete[] fMoveInfos;
101 //	delete[] fPartitionIDs;
102 	delete[] fPartitionRefs;
103 }
104 
105 
106 // GenerateJobs
107 status_t
108 DiskDeviceJobGenerator::GenerateJobs()
109 {
110 	// check parameters
111 	if (!fDevice || !fJobQueue)
112 		return B_BAD_VALUE;
113 
114 	if (!fMoveInfos /*|| !fPartitionIDs*/ || !fPartitionRefs)
115 		return B_NO_MEMORY;
116 
117 	// 1) Generate jobs for all physical partitions that don't have an
118 	// associated shadow partition, i.e. those that shall be deleted.
119 	// 2) Generate uninitialize jobs for all partition whose initialization
120 	// changes, also those that shall be initialized with a disk system.
121 	// This simplifies moving and resizing.
122 	status_t error = _GenerateCleanupJobs(fDevice);
123 	if (error != B_OK)
124 		return error;
125 
126 	// Generate jobs that move and resize the remaining physical partitions
127 	// to their final position/size.
128 	error = _GeneratePlacementJobs(fDevice);
129 	if (error != B_OK)
130 		return error;
131 
132 	// Generate the remaining jobs in one run: initialization, creation of
133 	// partitions, and changing of name, content name, type, parameters, and
134 	// content parameters.
135 	error = _GenerateRemainingJobs(NULL, fDevice);
136 	if (error != B_OK)
137 		return error;
138 
139 	return B_OK;
140 }
141 
142 
143 // _AddJob
144 status_t
145 DiskDeviceJobGenerator::_AddJob(DiskDeviceJob* job)
146 {
147 	if (!job)
148 		return B_NO_MEMORY;
149 
150 	status_t error = fJobQueue->AddJob(job);
151 	if (error != B_OK)
152 		delete job;
153 
154 	return error;
155 }
156 
157 
158 // _GenerateCleanupJobs
159 status_t
160 DiskDeviceJobGenerator::_GenerateCleanupJobs(BPartition* partition)
161 {
162 // TODO: Depending on how this shall be handled, we might want to unmount
163 // all descendants of a partition to be uninitialized or removed.
164 	if (BMutablePartition* shadow = _GetMutablePartition(partition)) {
165 		if (shadow->ChangeFlags() & B_PARTITION_CHANGED_INITIALIZATION) {
166 			// partition changes initialization
167 			status_t error = _GenerateUninitializeJob(partition);
168 			if (error != B_OK)
169 				return error;
170 		} else {
171 			// recurse
172 			for (int32 i = 0; BPartition* child = partition->_ChildAt(i); i++) {
173 				status_t error = _GenerateCleanupJobs(child);
174 				if (error != B_OK)
175 					return error;
176 			}
177 		}
178 	} else if (BPartition* parent = partition->Parent()) {
179 		// create job and add it to the queue
180 		status_t error = _GenerateDeleteChildJob(parent, partition);
181 		if (error != B_OK)
182 			return error;
183 	}
184 	return B_OK;
185 }
186 
187 
188 // _GeneratePlacementJobs
189 status_t
190 DiskDeviceJobGenerator::_GeneratePlacementJobs(BPartition* partition)
191 {
192 	if (BMutablePartition* shadow = _GetMutablePartition(partition)) {
193 		// Don't resize/move partitions that have an unrecognized contents.
194 		// They must have been uninitialized before.
195 		if (shadow->Status() == B_PARTITION_UNRECOGNIZED
196 			&& (shadow->Size() != partition->Size()
197 				|| shadow->Offset() != partition->Offset())) {
198 			return B_ERROR;
199 		}
200 
201 		if (shadow->Size() > partition->Size()) {
202 			// size grows: resize first
203 			status_t error = _GenerateResizeJob(partition);
204 			if (error != B_OK)
205 				return error;
206 		}
207 
208 		// place the children
209 		status_t error = _GenerateChildPlacementJobs(partition);
210 		if (error != B_OK)
211 			return error;
212 
213 		if (shadow->Size() < partition->Size()) {
214 			// size shrinks: resize now
215 			status_t error = _GenerateResizeJob(partition);
216 			if (error != B_OK)
217 				return error;
218 		}
219 	}
220 
221 	return B_OK;
222 }
223 
224 
225 // _GenerateChildPlacementJobs
226 status_t
227 DiskDeviceJobGenerator::_GenerateChildPlacementJobs(BPartition* partition)
228 {
229 	BMutablePartition* shadow = _GetMutablePartition(partition);
230 
231 	// nothing to do, if the partition contains no partitioning system or
232 	// shall be re-initialized
233 	if (!shadow->ContentType()
234 		|| (shadow->ChangeFlags() & B_PARTITION_CHANGED_INITIALIZATION)) {
235 		return B_OK;
236 	}
237 
238 	// first resize all children that shall shrink and place their descendants
239 	int32 childCount = 0;
240 	int32 moveForth = 0;
241 	int32 moveBack = 0;
242 
243 	for (int32 i = 0; BPartition* child = partition->_ChildAt(i); i++) {
244 		if (BMutablePartition* childShadow = _GetMutablePartition(child)) {
245 			// add a MoveInfo for the child
246 			MoveInfo& info = fMoveInfos[childCount];
247 			childCount++;
248 			info.partition = child;
249 			info.position = child->Offset();
250 			info.target_position = childShadow->Offset();
251 			info.size = child->Size();
252 
253 			if (info.position < info.target_position)
254 				moveForth++;
255 			else if (info.position > info.target_position)
256 				moveBack++;
257 
258 			// resize the child, if it shall shrink
259 			if (childShadow->Size() < child->Size()) {
260 				status_t error = _GeneratePlacementJobs(child);
261 				if (error != B_OK)
262 					return error;
263 				info.size = childShadow->Size();
264 			}
265 		}
266 	}
267 
268 	// sort the move infos
269 	if (childCount > 0 && moveForth + moveBack > 0) {
270 		qsort(fMoveInfos, childCount, sizeof(MoveInfo),
271 			  _CompareMoveInfoPosition);
272 	}
273 
274 	// move the children to their final positions
275 	while (moveForth + moveBack > 0) {
276 		int32 moved = 0;
277 		if (moveForth < moveBack) {
278 			// move children back
279 			for (int32 i = 0; i < childCount; i++) {
280 				MoveInfo &info = fMoveInfos[i];
281 				if (info.position > info.target_position) {
282 					if (i == 0
283 						|| info.target_position >= fMoveInfos[i - 1].position
284 							+ fMoveInfos[i - 1].size) {
285 						// check OK -- the partition wouldn't be moved before
286 						// the end of the preceding one
287 						status_t error = _GenerateMoveJob(info.partition);
288 						if (error != B_OK)
289 							return error;
290 						info.position = info.target_position;
291 						moved++;
292 						moveBack--;
293 					}
294 				}
295 			}
296 		} else {
297 			// move children forth
298 			for (int32 i = childCount - 1; i >= 0; i--) {
299 				MoveInfo &info = fMoveInfos[i];
300 				if (info.position > info.target_position) {
301 					if (i == childCount - 1
302 						|| info.target_position + info.size
303 							<= fMoveInfos[i - 1].position) {
304 						// check OK -- the partition wouldn't be moved before
305 						// the end of the preceding one
306 						status_t error = _GenerateMoveJob(info.partition);
307 						if (error != B_OK)
308 							return error;
309 						info.position = info.target_position;
310 						moved++;
311 						moveForth--;
312 					}
313 				}
314 			}
315 		}
316 
317 		// terminate, if no partition could be moved
318 		if (moved == 0)
319 			return B_ERROR;
320 	}
321 
322 	// now resize all children that shall grow/keep their size and place
323 	// their descendants
324 	for (int32 i = 0; BPartition* child = partition->_ChildAt(i); i++) {
325 		if (BMutablePartition* childShadow = _GetMutablePartition(child)) {
326 			if (childShadow->Size() >= child->Size()) {
327 				status_t error = _GeneratePlacementJobs(child);
328 				if (error != B_OK)
329 					return error;
330 			}
331 		}
332 	}
333 
334 	return B_OK;
335 }
336 
337 
338 // _GenerateRemainingJobs
339 status_t
340 DiskDeviceJobGenerator::_GenerateRemainingJobs(BPartition* parent,
341 	BPartition* partition)
342 {
343 	user_partition_data* partitionData = partition->fPartitionData;
344 
345 	uint32 changeFlags
346 		= partition->fDelegate->MutablePartition()->ChangeFlags();
347 
348 	// create the partition, if not existing yet
349 	if (!partitionData) {
350 		if (!parent)
351 			return B_BAD_VALUE;
352 
353 		status_t error = _GenerateCreateChildJob(parent, partition);
354 		if (error != B_OK)
355 			return error;
356 	} else {
357 		// partition already exists: set non-content properties
358 
359 
360 		// name
361 		if ((changeFlags & B_PARTITION_CHANGED_NAME)
362 			|| compare_string(partition->Name(), partitionData->name)) {
363 			if (!parent)
364 				return B_BAD_VALUE;
365 
366 			status_t error = _GenerateSetNameJob(parent, partition);
367 			if (error != B_OK)
368 				return error;
369 		}
370 
371 		// type
372 		if ((changeFlags & B_PARTITION_CHANGED_TYPE)
373 			|| compare_string(partition->Type(), partitionData->type)) {
374 			if (!parent)
375 				return B_BAD_VALUE;
376 
377 			status_t error = _GenerateSetTypeJob(parent, partition);
378 			if (error != B_OK)
379 				return error;
380 		}
381 
382 		// parameters
383 		if ((changeFlags & B_PARTITION_CHANGED_PARAMETERS)
384 			|| compare_string(partition->Parameters(),
385 				partitionData->parameters)) {
386 			if (!parent)
387 				return B_BAD_VALUE;
388 
389 			status_t error = _GenerateSetParametersJob(parent, partition);
390 			if (error != B_OK)
391 				return error;
392 		}
393 	}
394 
395 	if (partition->ContentType()) {
396 		// initialize the partition, if required
397 		if (changeFlags & B_PARTITION_CHANGED_INITIALIZATION) {
398 			status_t error = _GenerateInitializeJob(partition);
399 			if (error != B_OK)
400 				return error;
401 		} else {
402 			// partition not (re-)initialized, set content properties
403 
404 			// content name
405 			if ((changeFlags & B_PARTITION_CHANGED_NAME)
406 				|| compare_string(partition->ContentName(),
407 					partitionData->content_name)) {
408 				status_t error = _GenerateSetContentNameJob(partition);
409 				if (error != B_OK)
410 					return error;
411 			}
412 
413 			// content parameters
414 			if ((changeFlags & B_PARTITION_CHANGED_PARAMETERS)
415 				|| compare_string(partition->ContentParameters(),
416 					partitionData->content_parameters)) {
417 				status_t error = _GenerateSetContentParametersJob(partition);
418 				if (error != B_OK)
419 					return error;
420 			}
421 
422 			// defragment
423 			if (changeFlags & B_PARTITION_CHANGED_DEFRAGMENTATION) {
424 				status_t error = _GenerateDefragmentJob(partition);
425 				if (error != B_OK)
426 					return error;
427 			}
428 
429 			// check / repair
430 			bool repair = (changeFlags & B_PARTITION_CHANGED_REPAIR);
431 			if ((changeFlags & B_PARTITION_CHANGED_CHECK)
432 				|| repair) {
433 				status_t error = _GenerateRepairJob(partition, repair);
434 				if (error != B_OK)
435 					return error;
436 			}
437 		}
438 	}
439 
440 	// recurse
441 	for (int32 i = 0; BPartition* child = partition->ChildAt(i); i++) {
442 		status_t error = _GenerateRemainingJobs(partition, child);
443 		if (error != B_OK)
444 			return error;
445 	}
446 
447 	return B_OK;
448 }
449 
450 
451 // _GetMutablePartition
452 BMutablePartition*
453 DiskDeviceJobGenerator::_GetMutablePartition(BPartition* partition)
454 {
455 	if (!partition)
456 		return NULL;
457 
458 	return partition->fDelegate
459 		? partition->fDelegate->MutablePartition() : NULL;
460 }
461 
462 
463 // _GenerateInitializeJob
464 status_t
465 DiskDeviceJobGenerator::_GenerateInitializeJob(BPartition* partition)
466 {
467 	PartitionReference* reference;
468 	status_t error = _GetPartitionReference(partition, reference);
469 	if (error != B_OK)
470 		return error;
471 
472 	InitializeJob* job = new(nothrow) InitializeJob(reference);
473 	if (!job)
474 		return B_NO_MEMORY;
475 
476 	error = job->Init(partition->ContentType(),
477 		partition->ContentName(), partition->ContentParameters());
478 	if (error != B_OK) {
479 		delete job;
480 		return error;
481 	}
482 
483 	return _AddJob(job);
484 }
485 
486 
487 // _GenerateUninitializeJob
488 status_t
489 DiskDeviceJobGenerator::_GenerateUninitializeJob(BPartition* partition)
490 {
491 // TODO: Implement!
492 	return B_BAD_VALUE;
493 }
494 
495 
496 // _GenerateSetContentNameJob
497 status_t
498 DiskDeviceJobGenerator::_GenerateSetContentNameJob(BPartition* partition)
499 {
500 // TODO: Implement!
501 	return B_BAD_VALUE;
502 }
503 
504 
505 // _GenerateSetContentParametersJob
506 status_t
507 DiskDeviceJobGenerator::_GenerateSetContentParametersJob(BPartition* partition)
508 {
509 // TODO: Implement!
510 	return B_BAD_VALUE;
511 }
512 
513 
514 // _GenerateDefragmentJob
515 status_t
516 DiskDeviceJobGenerator::_GenerateDefragmentJob(BPartition* partition)
517 {
518 // TODO: Implement!
519 	return B_BAD_VALUE;
520 }
521 
522 
523 // _GenerateRepairJob
524 status_t
525 DiskDeviceJobGenerator::_GenerateRepairJob(BPartition* partition, bool repair)
526 {
527 // TODO: Implement!
528 	return B_BAD_VALUE;
529 }
530 
531 
532 // _GenerateCreateChildJob
533 status_t
534 DiskDeviceJobGenerator::_GenerateCreateChildJob(BPartition* parent,
535 	BPartition* partition)
536 {
537 // TODO: Implement!
538 	return B_BAD_VALUE;
539 }
540 
541 
542 // _GenerateDeleteChildJob
543 status_t
544 DiskDeviceJobGenerator::_GenerateDeleteChildJob(BPartition* parent,
545 	BPartition* child)
546 {
547 // TODO: Implement!
548 	return B_BAD_VALUE;
549 }
550 
551 
552 // _GenerateResizeJob
553 status_t
554 DiskDeviceJobGenerator::_GenerateResizeJob(BPartition* partition)
555 {
556 // TODO: Implement!
557 	return B_BAD_VALUE;
558 }
559 
560 
561 // _GenerateMoveJob
562 status_t
563 DiskDeviceJobGenerator::_GenerateMoveJob(BPartition* partition)
564 {
565 // TODO: Implement!
566 	return B_BAD_VALUE;
567 }
568 
569 
570 // _GenerateSetNameJob
571 status_t
572 DiskDeviceJobGenerator::_GenerateSetNameJob(BPartition* parent,
573 	BPartition* partition)
574 {
575 // TODO: Implement!
576 	return B_BAD_VALUE;
577 }
578 
579 
580 // _GenerateSetTypeJob
581 status_t
582 DiskDeviceJobGenerator::_GenerateSetTypeJob(BPartition* parent,
583 	BPartition* partition)
584 {
585 // TODO: Implement!
586 	return B_BAD_VALUE;
587 }
588 
589 
590 // _GenerateSetParametersJob
591 status_t
592 DiskDeviceJobGenerator::_GenerateSetParametersJob(BPartition* parent,
593 	BPartition* partition)
594 {
595 // TODO: Implement!
596 	return B_BAD_VALUE;
597 }
598 
599 
600 // _CollectContentsToMove
601 status_t
602 DiskDeviceJobGenerator::_CollectContentsToMove(BPartition* partition)
603 {
604 // TODO: Implement!
605 	return B_BAD_VALUE;
606 }
607 
608 
609 // _GetPartitionReference
610 status_t
611 DiskDeviceJobGenerator::_GetPartitionReference(BPartition* partition,
612 	PartitionReference*& reference)
613 {
614 	if (!partition)
615 		return B_BAD_VALUE;
616 
617 	for (int32 i = 0; i < fPartitionCount; i++) {
618 		PartitionRefInfo& info = fPartitionRefs[i];
619 
620 		if (info.partition == partition) {
621 			reference = info.reference;
622 			return B_OK;
623 		}
624 
625 		if (info.partition == NULL) {
626 			// create partition reference
627 			info.reference = new(nothrow) PartitionReference();
628 			if (!info.reference)
629 				return B_NO_MEMORY;
630 
631 			// set partition ID and change counter
632 			user_partition_data* partitionData = partition->fPartitionData;
633 			if (partitionData) {
634 				info.reference->SetPartitionID(partitionData->id);
635 				info.reference->SetChangeCounter(partitionData->change_counter);
636 			}
637 
638 			info.partition = partition;
639 			reference = info.reference;
640 			return B_OK;
641 		}
642 	}
643 
644 	// Out of slots -- that can't happen.
645 	return B_ERROR;
646 }
647 
648 
649 // _CompareMoveInfoOffset
650 int
651 DiskDeviceJobGenerator::_CompareMoveInfoPosition(const void* _a, const void* _b)
652 {
653 	const MoveInfo* a = static_cast<const MoveInfo*>(_a);
654 	const MoveInfo* b = static_cast<const MoveInfo*>(_b);
655 	if (a->position < b->position)
656 		return -1;
657 	if (a->position > b->position)
658 		return 1;
659 	return 0;
660 }
661