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