xref: /haiku/src/add-ons/disk_systems/intel/PartitionMapAddOn.cpp (revision 899e0ef82b5624ace2ccfa5f5a58c8ebee54aaef)
1 /*
2  * Copyright 2007, Ingo Weinhold, bonefish@users.sf.net.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 
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 #include "PrimaryParameterEditor.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, but not too big (ie. larger than 2^32 blocks) we can
94 	// initialize it.
95 	return partition->Size() >= 2 * partition->BlockSize()
96 		&& partition->Size() / partition->BlockSize() < UINT32_MAX;
97 }
98 
99 
100 status_t
101 PartitionMapAddOn::ValidateInitialize(const BMutablePartition* partition,
102 	BString* name, const char* parameters)
103 {
104 	if (!CanInitialize(partition)
105 		|| (parameters != NULL && parameters[0] != '\0')) {
106 		return B_BAD_VALUE;
107 	}
108 
109 	// we don't support a content name
110 	if (name != NULL)
111 		name->Truncate(0);
112 
113 	return B_OK;
114 }
115 
116 
117 status_t
118 PartitionMapAddOn::Initialize(BMutablePartition* partition, const char* name,
119 	const char* parameters, BPartitionHandle** _handle)
120 {
121 	if (!CanInitialize(partition)
122 		|| (name != NULL && name[0] != '\0')
123 		|| (parameters != NULL && parameters[0] != '\0')) {
124 		return B_BAD_VALUE;
125 	}
126 
127 	// create the handle
128 	PartitionMapHandle* handle = new(nothrow) PartitionMapHandle(partition);
129 	if (!handle)
130 		return B_NO_MEMORY;
131 	ObjectDeleter<PartitionMapHandle> handleDeleter(handle);
132 
133 	// init the partition
134 	status_t error = partition->SetContentType(Name());
135 	if (error != B_OK)
136 		return error;
137 	// TODO: The content type could as well be set by the caller.
138 
139 	partition->SetContentName(NULL);
140 	partition->SetContentParameters(NULL);
141 	partition->SetContentSize(
142 		sector_align(partition->Size(), partition->BlockSize()));
143 	partition->Changed(B_PARTITION_CHANGED_INITIALIZATION);
144 
145 	*_handle = handleDeleter.Detach();
146 
147 	return B_OK;
148 }
149 
150 
151 // #pragma mark - PartitionMapHandle
152 
153 
154 PartitionMapHandle::PartitionMapHandle(BMutablePartition* partition)
155 	:
156 	BPartitionHandle(partition)
157 {
158 }
159 
160 
161 PartitionMapHandle::~PartitionMapHandle()
162 {
163 }
164 
165 
166 status_t
167 PartitionMapHandle::Init()
168 {
169 	// initialize the partition map from the mutable partition
170 
171 	BMutablePartition* partition = Partition();
172 
173 	int32 count = partition->CountChildren();
174 	if (count > 4)
175 		return B_BAD_VALUE;
176 
177 	int32 extendedCount = 0;
178 
179 	for (int32 i = 0; i < count; i++) {
180 		BMutablePartition* child = partition->ChildAt(i);
181 		PartitionType type;
182 		if (!type.SetType(child->Type()))
183 			return B_BAD_VALUE;
184 
185 		// only one extended partition is allowed
186 		if (type.IsExtended()) {
187 			if (++extendedCount > 1)
188 				return B_BAD_VALUE;
189 		}
190 
191 		// TODO: Get these from the parameters.
192 		int32 index = i;
193 		bool active = false;
194 
195 		PrimaryPartition* primary = fPartitionMap.PrimaryPartitionAt(index);
196 		primary->SetTo(child->Offset(), child->Size(), type.Type(), active,
197 			partition->BlockSize());
198 
199 		child->SetChildCookie(primary);
200 	}
201 
202 	// The extended partition (if any) is initialized by
203 	// ExtendedPartitionHandle::Init().
204 
205 	return B_OK;
206 }
207 
208 
209 uint32
210 PartitionMapHandle::SupportedOperations(uint32 mask)
211 {
212 	BMutablePartition* partition = Partition();
213 
214 	uint32 flags = B_DISK_SYSTEM_SUPPORTS_RESIZING
215 		| B_DISK_SYSTEM_SUPPORTS_MOVING
216 		| B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_PARAMETERS
217 		| B_DISK_SYSTEM_SUPPORTS_INITIALIZING;
218 
219 	// creating child
220 	if ((mask & B_DISK_SYSTEM_SUPPORTS_CREATING_CHILD) != 0) {
221 		BPartitioningInfo info;
222 		if (partition->CountChildren() < 4
223 			&& GetPartitioningInfo(&info) == B_OK
224 			&& info.CountPartitionableSpaces() > 1) {
225 			flags |= B_DISK_SYSTEM_SUPPORTS_CREATING_CHILD;
226 		}
227 	}
228 
229 	return flags;
230 }
231 
232 
233 uint32
234 PartitionMapHandle::SupportedChildOperations(const BMutablePartition* child,
235 	uint32 mask)
236 {
237 	return B_DISK_SYSTEM_SUPPORTS_RESIZING_CHILD
238 		| B_DISK_SYSTEM_SUPPORTS_MOVING_CHILD
239 		| B_DISK_SYSTEM_SUPPORTS_SETTING_PARAMETERS
240 		| B_DISK_SYSTEM_SUPPORTS_DELETING_CHILD;
241 }
242 
243 
244 status_t
245 PartitionMapHandle::GetNextSupportedType(const BMutablePartition* child,
246 	int32* cookie, BString* type)
247 {
248 	TRACE("%p->PartitionMapHandle::GetNextSupportedType(child: %p, "
249 		"cookie: %ld)\n", this, child, *cookie);
250 
251 	int32 index = *cookie;
252 	const partition_type* nextType;
253 	while (true) {
254 		nextType = fPartitionMap.GetNextSupportedPartitionType(index);
255 		if (nextType == NULL)
256 			return B_ENTRY_NOT_FOUND;
257 		index++;
258 		if (nextType->used)
259 			break;
260 }
261 
262 	if (!nextType)
263 		return B_ENTRY_NOT_FOUND;
264 
265 	type->SetTo(nextType->name);
266 	*cookie = index;
267 
268 	return B_OK;
269 }
270 
271 
272 status_t
273 PartitionMapHandle::GetPartitioningInfo(BPartitioningInfo* info)
274 {
275 	// init to the full size (minus the first sector)
276 	off_t size = Partition()->ContentSize();
277 	status_t error = info->SetTo(Partition()->BlockSize(),
278 		size - Partition()->BlockSize());
279 	if (error != B_OK)
280 		return error;
281 
282 	// exclude the space of the existing partitions
283 	for (int32 i = 0; i < 4; i++) {
284 		PrimaryPartition* primary = fPartitionMap.PrimaryPartitionAt(i);
285 		if (!primary->IsEmpty()) {
286 			error = info->ExcludeOccupiedSpace(primary->Offset(),
287 				primary->Size());
288 			if (error != B_OK)
289 				return error;
290 		}
291 	}
292 
293 	return B_OK;
294 }
295 
296 
297 status_t
298 PartitionMapHandle::ValidateSetParameters(const BMutablePartition* child,
299 	const char* parameters)
300 {
301 	if (child == NULL || parameters == NULL)
302 		return B_NO_INIT;
303 
304 	void* handle = parse_driver_settings_string(parameters);
305 	if (handle == NULL)
306 		return B_BAD_DATA;
307 
308 	unload_driver_settings(handle);
309 
310 	return B_OK;
311 }
312 
313 
314 status_t
315 PartitionMapHandle::SetParameters(BMutablePartition* child,
316 	const char* parameters)
317 {
318 	void* handle = parse_driver_settings_string(parameters);
319 	if (handle == NULL)
320 		return B_BAD_DATA;
321 
322 	bool active = get_driver_boolean_parameter(handle, "active", false, true);
323 	unload_driver_settings(handle);
324 
325 	// Update our local state
326 	PrimaryPartition* partition = (PrimaryPartition*)child->ChildCookie();
327 	partition->SetActive(active);
328 
329 	// Forward the request to the BMutablePartition so it can be committed to
330 	// disk
331 	return child->SetParameters(parameters);
332 }
333 
334 
335 status_t
336 PartitionMapHandle::GetParameterEditor(B_PARAMETER_EDITOR_TYPE type,
337 	BPartitionParameterEditor** editor)
338 {
339 	*editor = NULL;
340 	if (type == B_CREATE_PARAMETER_EDITOR
341 		|| type == B_PROPERTIES_PARAMETER_EDITOR) {
342 		try {
343 			*editor = new PrimaryPartitionEditor();
344 		} catch (std::bad_alloc&) {
345 			return B_NO_MEMORY;
346 		}
347 		return B_OK;
348 	}
349 	return B_NOT_SUPPORTED;
350 }
351 
352 
353 status_t
354 PartitionMapHandle::ValidateCreateChild(off_t* _offset, off_t* _size,
355 	const char* typeString, BString* name, const char* parameters)
356 {
357 	// check type
358 	PartitionType type;
359 	if (!type.SetType(typeString) || type.IsEmpty())
360 		return B_BAD_TYPE;
361 
362 	if (type.IsExtended() && fPartitionMap.ExtendedPartitionIndex() >= 0) {
363 		// There can only be a single extended partition
364 		return B_NAME_IN_USE;
365 	}
366 
367 	// check name
368 	if (name)
369 		name->Truncate(0);
370 
371 	// check parameters
372 	void* handle = parse_driver_settings_string(parameters);
373 	if (handle == NULL)
374 		return B_ERROR;
375 	get_driver_boolean_parameter(handle, "active", false, true);
376 	unload_driver_settings(handle);
377 
378 	// do we have a spare primary partition?
379 	if (fPartitionMap.CountNonEmptyPrimaryPartitions() == 4)
380 		return B_BAD_INDEX;
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_DEVICE_FULL;
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 status_t
471 PartitionMapHandle::CreateChild(off_t offset, off_t size,
472 	const char* typeString, const char* name, const char* parameters,
473 	BMutablePartition** _child)
474 {
475 	// check type
476 	PartitionType type;
477 	if (!type.SetType(typeString) || type.IsEmpty())
478 		return B_BAD_VALUE;
479 	if (type.IsExtended() && fPartitionMap.ExtendedPartitionIndex() >= 0)
480 		return B_BAD_VALUE;
481 
482 	// check name
483 	if (name && *name != '\0')
484 		return B_BAD_VALUE;
485 
486 	// check parameters
487 	void* handle = parse_driver_settings_string(parameters);
488 	if (handle == NULL)
489 		return B_ERROR;
490 
491 	bool active = get_driver_boolean_parameter(handle, "active", false, true);
492 	unload_driver_settings(handle);
493 
494 	// get a spare primary partition
495 	PrimaryPartition* primary = NULL;
496 	for (int32 i = 0; i < 4; i++) {
497 		if (fPartitionMap.PrimaryPartitionAt(i)->IsEmpty()) {
498 			primary = fPartitionMap.PrimaryPartitionAt(i);
499 			break;
500 		}
501 	}
502 	if (!primary)
503 		return B_BAD_VALUE;
504 
505 	// offset properly aligned?
506 	if (offset != sector_align(offset, Partition()->BlockSize())
507 		|| size != sector_align(size, Partition()->BlockSize()))
508 		return B_BAD_VALUE;
509 
510 	// check the free space situation
511 	BPartitioningInfo info;
512 	status_t error = GetPartitioningInfo(&info);
513 	if (error != B_OK)
514 		return error;
515 
516 	bool foundSpace = false;
517 	off_t end = offset + size;
518 	int32 spacesCount = info.CountPartitionableSpaces();
519 	for (int32 i = 0; i < spacesCount; i++) {
520 		off_t spaceOffset, spaceSize;
521 		info.GetPartitionableSpaceAt(i, &spaceOffset, &spaceSize);
522 		off_t spaceEnd = spaceOffset + spaceSize;
523 
524 		if (offset >= spaceOffset && end <= spaceEnd) {
525 			foundSpace = true;
526 			break;
527 		}
528 	}
529 
530 	if (!foundSpace)
531 		return B_BAD_VALUE;
532 
533 	// create the child
534 	// (Note: the primary partition index is indeed the child index, since
535 	// we picked the first empty primary partition.)
536 	BMutablePartition* partition = Partition();
537 	BMutablePartition* child;
538 	error = partition->CreateChild(primary->Index(), typeString, name,
539 		parameters, &child);
540 	if (error != B_OK)
541 		return error;
542 
543 	// init the child
544 	child->SetOffset(offset);
545 	child->SetSize(size);
546 	child->SetBlockSize(partition->BlockSize());
547 	//child->SetFlags(0);
548 	child->SetChildCookie(primary);
549 
550 	// init the primary partition
551 	primary->SetTo(offset, size, type.Type(), active, partition->BlockSize());
552 
553 	*_child = child;
554 	return B_OK;
555 }
556 
557 
558 status_t
559 PartitionMapHandle::DeleteChild(BMutablePartition* child)
560 {
561 	BMutablePartition* parent = child->Parent();
562 	status_t error = parent->DeleteChild(child);
563 
564 	return error;
565 }
566