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