xref: /haiku/src/add-ons/disk_systems/intel/PartitionMapAddOn.cpp (revision ddac407426cd3b3d0b4589d7a161b300b3539a2a)
1 /*
2  * Copyright 2007, Ingo Weinhold, bonefish@users.sf.net.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 #include "PartitionMapAddOn.h"
7 
8 #include <new>
9 #include <stdio.h>
10 
11 #include <DiskDeviceTypes.h>
12 #include <MutablePartition.h>
13 #include <PartitioningInfo.h>
14 
15 #include <AutoDeleter.h>
16 
17 #include "IntelDiskSystem.h"
18 
19 
20 //#define TRACE_PARTITION_MAP_ADD_ON
21 #undef TRACE
22 #ifdef TRACE_PARTITION_MAP_ADD_ON
23 # define TRACE(x...) printf(x)
24 #else
25 # define TRACE(x...) do {} while (false)
26 #endif
27 
28 
29 using std::nothrow;
30 
31 
32 static const uint32 kDiskSystemFlags =
33 	0
34 //	| B_DISK_SYSTEM_SUPPORTS_CHECKING
35 //	| B_DISK_SYSTEM_SUPPORTS_REPAIRING
36 	| B_DISK_SYSTEM_SUPPORTS_RESIZING
37 	| B_DISK_SYSTEM_SUPPORTS_MOVING
38 //	| B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_NAME
39 	| B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_PARAMETERS
40 	| B_DISK_SYSTEM_SUPPORTS_INITIALIZING
41 //	| B_DISK_SYSTEM_SUPPORTS_CONTENT_NAME
42 
43 	| B_DISK_SYSTEM_SUPPORTS_RESIZING_CHILD
44 	| B_DISK_SYSTEM_SUPPORTS_MOVING_CHILD
45 //	| B_DISK_SYSTEM_SUPPORTS_SETTING_NAME
46 	| B_DISK_SYSTEM_SUPPORTS_SETTING_TYPE
47 //	| B_DISK_SYSTEM_SUPPORTS_SETTING_PARAMETERS
48 	| B_DISK_SYSTEM_SUPPORTS_CREATING_CHILD
49 	| B_DISK_SYSTEM_SUPPORTS_DELETING_CHILD
50 //	| B_DISK_SYSTEM_SUPPORTS_NAME
51 ;
52 
53 
54 // #pragma mark - PartitionMapAddOn
55 
56 
57 // constructor
58 PartitionMapAddOn::PartitionMapAddOn()
59 	: BDiskSystemAddOn(kPartitionTypeIntel, kDiskSystemFlags)
60 {
61 }
62 
63 
64 // destructor
65 PartitionMapAddOn::~PartitionMapAddOn()
66 {
67 }
68 
69 
70 // CreatePartitionHandle
71 status_t
72 PartitionMapAddOn::CreatePartitionHandle(BMutablePartition* partition,
73 	BPartitionHandle** _handle)
74 {
75 	PartitionMapHandle* handle = new(nothrow) PartitionMapHandle(partition);
76 	if (!handle)
77 		return B_NO_MEMORY;
78 
79 	status_t error = handle->Init();
80 	if (error != B_OK) {
81 		delete handle;
82 		return error;
83 	}
84 
85 	*_handle = handle;
86 	return B_OK;
87 }
88 
89 
90 // CanInitialize
91 bool
92 PartitionMapAddOn::CanInitialize(const BMutablePartition* partition)
93 {
94 	// If it's big enough, we can initialize it.
95 	return partition->Size() >= 2 * partition->BlockSize();
96 }
97 
98 
99 // GetInitializationParameterEditor
100 status_t
101 PartitionMapAddOn::GetInitializationParameterEditor(
102 	const BMutablePartition* partition, BDiskDeviceParameterEditor** editor)
103 {
104 	// Nothing to edit, really.
105 	*editor = NULL;
106 	return B_OK;
107 }
108 
109 
110 // ValidateInitialize
111 status_t
112 PartitionMapAddOn::ValidateInitialize(const BMutablePartition* partition,
113 	BString* name, const char* parameters)
114 {
115 	if (!CanInitialize(partition)
116 		|| parameters != NULL && strlen(parameters) > 0) {
117 		return B_BAD_VALUE;
118 	}
119 
120 	// we don't support a content name
121 	if (name != NULL)
122 		name->Truncate(0);
123 
124 	return B_OK;
125 }
126 
127 
128 // Initialize
129 status_t
130 PartitionMapAddOn::Initialize(BMutablePartition* partition, const char* name,
131 	const char* parameters, BPartitionHandle** _handle)
132 {
133 	if (!CanInitialize(partition)
134 		|| name != NULL && strlen(name) > 0
135 		|| parameters != NULL && strlen(parameters) > 0) {
136 		return B_BAD_VALUE;
137 	}
138 
139 	// create the handle
140 	PartitionMapHandle* handle = new(nothrow) PartitionMapHandle(partition);
141 	if (!handle)
142 		return B_NO_MEMORY;
143 	ObjectDeleter<PartitionMapHandle> handleDeleter(handle);
144 
145 	// init the partition
146 	status_t error = partition->SetContentType(Name());
147 	if (error != B_OK)
148 		return error;
149 	// TODO: The content type could as well be set by the caller.
150 
151 	partition->SetContentName(NULL);
152 	partition->SetContentParameters(NULL);
153 	partition->SetContentSize(
154 		sector_align(partition->Size(), partition->BlockSize()));
155 
156 	*_handle = handleDeleter.Detach();
157 
158 	return B_OK;
159 }
160 
161 
162 // #pragma mark - PartitionMapHandle
163 
164 
165 // constructor
166 PartitionMapHandle::PartitionMapHandle(BMutablePartition* partition)
167 	: BPartitionHandle(partition)
168 {
169 }
170 
171 
172 // destructor
173 PartitionMapHandle::~PartitionMapHandle()
174 {
175 }
176 
177 
178 // Init
179 status_t
180 PartitionMapHandle::Init()
181 {
182 	// initialize the partition map from the mutable partition
183 
184 	BMutablePartition* partition = Partition();
185 
186 	int32 count = partition->CountChildren();
187 	if (count > 4)
188 		return B_BAD_VALUE;
189 
190 	int32 extendedCount = 0;
191 
192 	for (int32 i = 0; i < count; i++) {
193 		BMutablePartition* child = partition->ChildAt(i);
194 		PartitionType type;
195 		if (!type.SetType(child->Type()))
196 			return B_BAD_VALUE;
197 
198 		// only one extended partition is allowed
199 		if (type.IsExtended()) {
200 			if (++extendedCount > 1)
201 				return B_BAD_VALUE;
202 		}
203 
204 		// TODO: Get these from the parameters.
205 		int32 index = i;
206 		bool active = false;
207 
208 		PrimaryPartition* primary = fPartitionMap.PrimaryPartitionAt(index);
209 		primary->SetTo(child->Offset(), child->Size(), type.Type(), active,
210 			partition->BlockSize());
211 
212 		child->SetChildCookie(primary);
213 	}
214 
215 	// The extended partition (if any) is initialized by
216 	// ExtendedPartitionHandle::Init().
217 
218 	return B_OK;
219 }
220 
221 
222 // SupportedOperations
223 uint32
224 PartitionMapHandle::SupportedOperations(uint32 mask)
225 {
226 	BMutablePartition* partition = Partition();
227 
228 	uint32 flags = B_DISK_SYSTEM_SUPPORTS_RESIZING
229 		| B_DISK_SYSTEM_SUPPORTS_MOVING
230 		| B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_PARAMETERS
231 		| B_DISK_SYSTEM_SUPPORTS_INITIALIZING;
232 
233 	// creating child
234 	if (mask & B_DISK_SYSTEM_SUPPORTS_CREATING_CHILD) {
235 		BPartitioningInfo info;
236 		if (partition->CountChildren() < 4
237 			&& GetPartitioningInfo(&info) == B_OK
238 			&& info.CountPartitionableSpaces() > 1) {
239 			flags |= B_DISK_SYSTEM_SUPPORTS_CREATING_CHILD;
240 		}
241 	}
242 
243 	return flags;
244 }
245 
246 
247 // SupportedChildOperations
248 uint32
249 PartitionMapHandle::SupportedChildOperations(const BMutablePartition* child,
250 	uint32 mask)
251 {
252 	return B_DISK_SYSTEM_SUPPORTS_RESIZING_CHILD
253 		| B_DISK_SYSTEM_SUPPORTS_MOVING_CHILD
254 		| B_DISK_SYSTEM_SUPPORTS_SETTING_TYPE
255 		| B_DISK_SYSTEM_SUPPORTS_DELETING_CHILD;
256 }
257 
258 
259 // GetNextSupportedType
260 status_t
261 PartitionMapHandle::GetNextSupportedType(const BMutablePartition* child,
262 	int32* cookie, BString* type)
263 {
264 	TRACE("%p->PartitionMapHandle::GetNextSupportedType(child: %p, "
265 		"cookie: %ld)\n", this, child, *cookie);
266 	// TODO: What are we supposed to do with the child?
267 
268 	// we support creating two types, primary and extended
269 	if (*cookie < 0 || *cookie > 1)
270 		return B_ENTRY_NOT_FOUND;
271 
272 	// check if there are any spaces at all
273 	// TODO: check if the spaces have enough size at all
274 	BPartitioningInfo info;
275 	status_t ret = GetPartitioningInfo(&info);
276 	if (ret < B_OK)
277 		return ret;
278 
279 	if (info.CountPartitionableSpaces() == 0)
280 		return B_ENTRY_NOT_FOUND;
281 
282 	// adjust the cookie here already so that we don't have
283 	// to worry about it when returning early below
284 	*cookie = *cookie + 1;
285 
286 	if (*cookie == 1) {
287 		// On first iteration, check if we can create more primary
288 		// partitions. If this is not possible, we cannot create
289 		// any extended partitions either.
290 		for (int32 i = 0; i < 4; i++) {
291 			PrimaryPartition* primary = fPartitionMap.PrimaryPartitionAt(i);
292 			if (primary->IsEmpty()) {
293 				*type = kPartitionTypeIntelPrimary;
294 				return B_OK;
295 			}
296 		}
297 	} else if (*cookie == 2) {
298 		// On second iteration, check if we can create more primary
299 		// partitions. Also check if there already is an extended
300 		// partition, only if there is at least one empty and no
301 		// extended partition, we can create an extended partition.
302 		bool foundExtended = false;
303 		bool foundEmpty = false;
304 		for (int32 i = 0; i < 4; i++) {
305 			PrimaryPartition* primary = fPartitionMap.PrimaryPartitionAt(i);
306 			if (primary->IsEmpty())
307 				foundEmpty = true;
308 			else if (primary->IsExtended())
309 				foundExtended = true;
310 		}
311 		if (foundEmpty && !foundExtended) {
312 			*type = kPartitionTypeIntelExtended;
313 			return B_OK;
314 		}
315 	}
316 
317 	return B_ENTRY_NOT_FOUND;
318 }
319 
320 
321 // GetPartitioningInfo
322 status_t
323 PartitionMapHandle::GetPartitioningInfo(BPartitioningInfo* info)
324 {
325 	// init to the full size (minus the first sector)
326 	off_t size = Partition()->ContentSize();
327 	status_t error = info->SetTo(Partition()->BlockSize(),
328 		size - Partition()->BlockSize());
329 	if (error != B_OK)
330 		return error;
331 
332 	// exclude the space of the existing partitions
333 	for (int32 i = 0; i < 4; i++) {
334 		PrimaryPartition* primary = fPartitionMap.PrimaryPartitionAt(i);
335 		if (!primary->IsEmpty()) {
336 			error = info->ExcludeOccupiedSpace(primary->Offset(),
337 				primary->Size());
338 			if (error != B_OK)
339 				return error;
340 		}
341 	}
342 
343 	return B_OK;
344 }
345 
346 
347 // GetChildCreationParameterEditor
348 status_t
349 PartitionMapHandle::GetChildCreationParameterEditor(const char* type,
350 	BDiskDeviceParameterEditor** editor)
351 {
352 	// TODO: We actually need an editor here.
353 	*editor = NULL;
354 	return B_OK;
355 }
356 
357 
358 // ValidateCreateChild
359 status_t
360 PartitionMapHandle::ValidateCreateChild(off_t* _offset, off_t* _size,
361 	const char* typeString, BString* name, const char* parameters)
362 {
363 	// check type
364 	PartitionType type;
365 	if (!type.SetType(typeString) || type.IsEmpty())
366 		return B_BAD_VALUE;
367 
368 	if (type.IsExtended() && fPartitionMap.ExtendedPartitionIndex() >= 0)
369 		return B_BAD_VALUE;
370 
371 	// check name
372 	if (name)
373 		name->Truncate(0);
374 
375 	// check parameters
376 	// TODO:...
377 
378 	// do we have a spare primary partition?
379 	if (fPartitionMap.CountNonEmptyPrimaryPartitions() == 4)
380 		return B_BAD_VALUE;
381 
382 	// check the free space situation
383 	BPartitioningInfo info;
384 	status_t error = GetPartitioningInfo(&info);
385 	if (error != B_OK)
386 		return error;
387 
388 	// any space in the partition at all?
389 	int32 spacesCount = info.CountPartitionableSpaces();
390 	if (spacesCount == 0)
391 		return B_BAD_VALUE;
392 
393 	// check offset and size
394 	off_t offset = sector_align(*_offset, Partition()->BlockSize());
395 	off_t size = sector_align(*_size, Partition()->BlockSize());
396 		// TODO: Rather round size up?
397 	off_t end = offset + size;
398 
399 	// get the first partitionable space the requested interval intersects with
400 	int32 spaceIndex = -1;
401 	int32 closestSpaceIndex = -1;
402 	off_t closestSpaceDistance = 0;
403 	for (int32 i = 0; i < spacesCount; i++) {
404 		off_t spaceOffset, spaceSize;
405 		info.GetPartitionableSpaceAt(i, &spaceOffset, &spaceSize);
406 		off_t spaceEnd = spaceOffset + spaceSize;
407 
408 		if (spaceOffset >= offset && spaceOffset < end
409 			|| offset >= spaceOffset && offset < spaceEnd) {
410 			spaceIndex = i;
411 			break;
412 		}
413 
414 		off_t distance;
415 		if (offset < spaceOffset)
416 			distance = spaceOffset - end;
417 		else
418 			distance = spaceEnd - offset;
419 
420 		if (closestSpaceIndex == -1 || distance < closestSpaceDistance) {
421 			closestSpaceIndex = i;
422 			closestSpaceDistance = distance;
423 		}
424 	}
425 
426 	// get the space we found
427 	off_t spaceOffset, spaceSize;
428 	info.GetPartitionableSpaceAt(
429 		spaceIndex >= 0 ? spaceIndex : closestSpaceIndex, &spaceOffset,
430 		&spaceSize);
431 	off_t spaceEnd = spaceOffset + spaceSize;
432 
433 	// If the requested intervald doesn't intersect with any space yet, move
434 	// it, so that it does.
435 	if (spaceIndex < 0) {
436 		spaceIndex = closestSpaceIndex;
437 		if (offset < spaceOffset) {
438 			offset = spaceOffset;
439 			end = offset + size;
440 		} else {
441 			end = spaceEnd;
442 			offset = end - size;
443 		}
444 	}
445 
446 	// move/shrink the interval, so that it fully lies within the space
447 	if (offset < spaceOffset) {
448 		offset = spaceOffset;
449 		end = offset + size;
450 		if (end > spaceEnd) {
451 			end = spaceEnd;
452 			size = end - offset;
453 		}
454 	} else if (end > spaceEnd) {
455 		end = spaceEnd;
456 		offset = end - size;
457 		if (offset < spaceOffset) {
458 			offset = spaceOffset;
459 			size = end - offset;
460 		}
461 	}
462 
463 	*_offset = offset;
464 	*_size = size;
465 
466 	return B_OK;
467 }
468 
469 
470 // CreateChild
471 status_t
472 PartitionMapHandle::CreateChild(off_t offset, off_t size,
473 	const char* typeString, const char* name, const char* parameters,
474 	BMutablePartition** _child)
475 {
476 	// check type
477 	PartitionType type;
478 	if (!type.SetType(typeString) || type.IsEmpty())
479 		return B_BAD_VALUE;
480 	if (type.IsExtended() && fPartitionMap.ExtendedPartitionIndex() >= 0)
481 		return B_BAD_VALUE;
482 
483 	// check name
484 	if (name && strlen(name) > 0)
485 		return B_BAD_VALUE;
486 
487 	// check parameters
488 	// TODO:...
489 
490 	// get a spare primary partition
491 	PrimaryPartition* primary = NULL;
492 	for (int32 i = 0; i < 4; i++) {
493 		if (fPartitionMap.PrimaryPartitionAt(i)->IsEmpty()) {
494 			primary = fPartitionMap.PrimaryPartitionAt(i);
495 			break;
496 		}
497 	}
498 	if (!primary)
499 		return B_BAD_VALUE;
500 
501 	// offset properly aligned?
502 	if (offset != sector_align(offset, Partition()->BlockSize())
503 		|| size != sector_align(size, Partition()->BlockSize()))
504 		return B_BAD_VALUE;
505 
506 	// check the free space situation
507 	BPartitioningInfo info;
508 	status_t error = GetPartitioningInfo(&info);
509 	if (error != B_OK)
510 		return error;
511 
512 	bool foundSpace = false;
513 	off_t end = offset + size;
514 	int32 spacesCount = info.CountPartitionableSpaces();
515 	for (int32 i = 0; i < spacesCount; i++) {
516 		off_t spaceOffset, spaceSize;
517 		info.GetPartitionableSpaceAt(i, &spaceOffset, &spaceSize);
518 		off_t spaceEnd = spaceOffset + spaceSize;
519 
520 		if (offset >= spaceOffset && end <= spaceEnd) {
521 			foundSpace = true;
522 			break;
523 		}
524 	}
525 
526 	if (!foundSpace)
527 		return B_BAD_VALUE;
528 
529 	// everything looks good, do it
530 
531 	// create the child
532 	// (Note: the primary partition index is indeed the child index, since
533 	// we picked the first empty primary partition.)
534 	BMutablePartition* partition = Partition();
535 	BMutablePartition* child;
536 	error = partition->CreateChild(primary->Index(), typeString, NULL,
537 		parameters, &child);
538 	if (error != B_OK)
539 		return error;
540 
541 	// init the child
542 	child->SetOffset(offset);
543 	child->SetSize(size);
544 	child->SetBlockSize(partition->BlockSize());
545 	//child->SetFlags(0);
546 	child->SetChildCookie(primary);
547 
548 	// init the primary partition
549 	bool active = false;
550 		// TODO: Get from parameters!
551 	primary->SetTo(offset, size, type.Type(), active, partition->BlockSize());
552 
553 	// TODO: If the child is an extended partition, we should trigger its
554 	// initialization.
555 
556 	*_child = child;
557 	return B_OK;
558 }
559 
560 
561 // DeleteChild
562 status_t
563 PartitionMapHandle::DeleteChild(BMutablePartition* child)
564 {
565 	BMutablePartition* parent = child->Parent();
566 	status_t error = parent->DeleteChild(child);
567 
568 	return error;
569 }
570