xref: /haiku/src/add-ons/disk_systems/intel/PartitionMapAddOn.cpp (revision 90ae2e54f6ccaca73c011a2aa4cdd660417108ad)
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 		// There can only be a single extended partition
332 		return B_BAD_VALUE;
333 	}
334 
335 	// check name
336 	if (name)
337 		name->Truncate(0);
338 
339 	// check parameters
340 	void* handle = parse_driver_settings_string(parameters);
341 	if (handle == NULL)
342 		return B_ERROR;
343 	get_driver_boolean_parameter(handle, "active", false, true);
344 	delete_driver_settings(handle);
345 
346 	// do we have a spare primary partition?
347 	if (fPartitionMap.CountNonEmptyPrimaryPartitions() == 4)
348 		return B_BAD_VALUE;
349 
350 	// check the free space situation
351 	BPartitioningInfo info;
352 	status_t error = GetPartitioningInfo(&info);
353 	if (error != B_OK)
354 		return error;
355 
356 	// any space in the partition at all?
357 	int32 spacesCount = info.CountPartitionableSpaces();
358 	if (spacesCount == 0)
359 		return B_BAD_VALUE;
360 
361 	// check offset and size
362 	off_t offset = sector_align(*_offset, Partition()->BlockSize());
363 	off_t size = sector_align(*_size, Partition()->BlockSize());
364 		// TODO: Rather round size up?
365 	off_t end = offset + size;
366 
367 	// get the first partitionable space the requested interval intersects with
368 	int32 spaceIndex = -1;
369 	int32 closestSpaceIndex = -1;
370 	off_t closestSpaceDistance = 0;
371 	for (int32 i = 0; i < spacesCount; i++) {
372 		off_t spaceOffset, spaceSize;
373 		info.GetPartitionableSpaceAt(i, &spaceOffset, &spaceSize);
374 		off_t spaceEnd = spaceOffset + spaceSize;
375 
376 		if ((spaceOffset >= offset && spaceOffset < end)
377 			|| (offset >= spaceOffset && offset < spaceEnd)) {
378 			spaceIndex = i;
379 			break;
380 		}
381 
382 		off_t distance;
383 		if (offset < spaceOffset)
384 			distance = spaceOffset - end;
385 		else
386 			distance = spaceEnd - offset;
387 
388 		if (closestSpaceIndex == -1 || distance < closestSpaceDistance) {
389 			closestSpaceIndex = i;
390 			closestSpaceDistance = distance;
391 		}
392 	}
393 
394 	// get the space we found
395 	off_t spaceOffset, spaceSize;
396 	info.GetPartitionableSpaceAt(
397 		spaceIndex >= 0 ? spaceIndex : closestSpaceIndex, &spaceOffset,
398 		&spaceSize);
399 	off_t spaceEnd = spaceOffset + spaceSize;
400 
401 	// If the requested intervald doesn't intersect with any space yet, move
402 	// it, so that it does.
403 	if (spaceIndex < 0) {
404 		spaceIndex = closestSpaceIndex;
405 		if (offset < spaceOffset) {
406 			offset = spaceOffset;
407 			end = offset + size;
408 		} else {
409 			end = spaceEnd;
410 			offset = end - size;
411 		}
412 	}
413 
414 	// move/shrink the interval, so that it fully lies within the space
415 	if (offset < spaceOffset) {
416 		offset = spaceOffset;
417 		end = offset + size;
418 		if (end > spaceEnd) {
419 			end = spaceEnd;
420 			size = end - offset;
421 		}
422 	} else if (end > spaceEnd) {
423 		end = spaceEnd;
424 		offset = end - size;
425 		if (offset < spaceOffset) {
426 			offset = spaceOffset;
427 			size = end - offset;
428 		}
429 	}
430 
431 	*_offset = offset;
432 	*_size = size;
433 
434 	return B_OK;
435 }
436 
437 
438 status_t
439 PartitionMapHandle::CreateChild(off_t offset, off_t size,
440 	const char* typeString, const char* name, const char* parameters,
441 	BMutablePartition** _child)
442 {
443 	// check type
444 	PartitionType type;
445 	if (!type.SetType(typeString) || type.IsEmpty())
446 		return B_BAD_VALUE;
447 	if (type.IsExtended() && fPartitionMap.ExtendedPartitionIndex() >= 0)
448 		return B_BAD_VALUE;
449 
450 	// check name
451 	if (name && *name != '\0')
452 		return B_BAD_VALUE;
453 
454 	// check parameters
455 	void* handle = parse_driver_settings_string(parameters);
456 	if (handle == NULL)
457 		return B_ERROR;
458 
459 	bool active = get_driver_boolean_parameter(handle, "active", false, true);
460 	delete_driver_settings(handle);
461 
462 	// get a spare primary partition
463 	PrimaryPartition* primary = NULL;
464 	for (int32 i = 0; i < 4; i++) {
465 		if (fPartitionMap.PrimaryPartitionAt(i)->IsEmpty()) {
466 			primary = fPartitionMap.PrimaryPartitionAt(i);
467 			break;
468 		}
469 	}
470 	if (!primary)
471 		return B_BAD_VALUE;
472 
473 	// offset properly aligned?
474 	if (offset != sector_align(offset, Partition()->BlockSize())
475 		|| size != sector_align(size, Partition()->BlockSize()))
476 		return B_BAD_VALUE;
477 
478 	// check the free space situation
479 	BPartitioningInfo info;
480 	status_t error = GetPartitioningInfo(&info);
481 	if (error != B_OK)
482 		return error;
483 
484 	bool foundSpace = false;
485 	off_t end = offset + size;
486 	int32 spacesCount = info.CountPartitionableSpaces();
487 	for (int32 i = 0; i < spacesCount; i++) {
488 		off_t spaceOffset, spaceSize;
489 		info.GetPartitionableSpaceAt(i, &spaceOffset, &spaceSize);
490 		off_t spaceEnd = spaceOffset + spaceSize;
491 
492 		if (offset >= spaceOffset && end <= spaceEnd) {
493 			foundSpace = true;
494 			break;
495 		}
496 	}
497 
498 	if (!foundSpace)
499 		return B_BAD_VALUE;
500 
501 	// create the child
502 	// (Note: the primary partition index is indeed the child index, since
503 	// we picked the first empty primary partition.)
504 	BMutablePartition* partition = Partition();
505 	BMutablePartition* child;
506 	error = partition->CreateChild(primary->Index(), typeString, name,
507 		parameters, &child);
508 	if (error != B_OK)
509 		return error;
510 
511 	// init the child
512 	child->SetOffset(offset);
513 	child->SetSize(size);
514 	child->SetBlockSize(partition->BlockSize());
515 	//child->SetFlags(0);
516 	child->SetChildCookie(primary);
517 
518 	// init the primary partition
519 	primary->SetTo(offset, size, type.Type(), active, partition->BlockSize());
520 
521 	*_child = child;
522 	return B_OK;
523 }
524 
525 
526 status_t
527 PartitionMapHandle::DeleteChild(BMutablePartition* child)
528 {
529 	BMutablePartition* parent = child->Parent();
530 	status_t error = parent->DeleteChild(child);
531 
532 	return error;
533 }
534