xref: /haiku/src/add-ons/disk_systems/intel/PartitionMapAddOn.cpp (revision 2b76973fa2401f7a5edf68e6470f3d3210cbcff3)
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 
144 	*_handle = handleDeleter.Detach();
145 
146 	return B_OK;
147 }
148 
149 
150 // #pragma mark - PartitionMapHandle
151 
152 
153 PartitionMapHandle::PartitionMapHandle(BMutablePartition* partition)
154 	:
155 	BPartitionHandle(partition)
156 {
157 }
158 
159 
160 PartitionMapHandle::~PartitionMapHandle()
161 {
162 }
163 
164 
165 status_t
166 PartitionMapHandle::Init()
167 {
168 	// initialize the partition map from the mutable partition
169 
170 	BMutablePartition* partition = Partition();
171 
172 	int32 count = partition->CountChildren();
173 	if (count > 4)
174 		return B_BAD_VALUE;
175 
176 	int32 extendedCount = 0;
177 
178 	for (int32 i = 0; i < count; i++) {
179 		BMutablePartition* child = partition->ChildAt(i);
180 		PartitionType type;
181 		if (!type.SetType(child->Type()))
182 			return B_BAD_VALUE;
183 
184 		// only one extended partition is allowed
185 		if (type.IsExtended()) {
186 			if (++extendedCount > 1)
187 				return B_BAD_VALUE;
188 		}
189 
190 		// TODO: Get these from the parameters.
191 		int32 index = i;
192 		bool active = false;
193 
194 		PrimaryPartition* primary = fPartitionMap.PrimaryPartitionAt(index);
195 		primary->SetTo(child->Offset(), child->Size(), type.Type(), active,
196 			partition->BlockSize());
197 
198 		child->SetChildCookie(primary);
199 	}
200 
201 	// The extended partition (if any) is initialized by
202 	// ExtendedPartitionHandle::Init().
203 
204 	return B_OK;
205 }
206 
207 
208 uint32
209 PartitionMapHandle::SupportedOperations(uint32 mask)
210 {
211 	BMutablePartition* partition = Partition();
212 
213 	uint32 flags = B_DISK_SYSTEM_SUPPORTS_RESIZING
214 		| B_DISK_SYSTEM_SUPPORTS_MOVING
215 		| B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_PARAMETERS
216 		| B_DISK_SYSTEM_SUPPORTS_INITIALIZING;
217 
218 	// creating child
219 	if ((mask & B_DISK_SYSTEM_SUPPORTS_CREATING_CHILD) != 0) {
220 		BPartitioningInfo info;
221 		if (partition->CountChildren() < 4
222 			&& GetPartitioningInfo(&info) == B_OK
223 			&& info.CountPartitionableSpaces() > 1) {
224 			flags |= B_DISK_SYSTEM_SUPPORTS_CREATING_CHILD;
225 		}
226 	}
227 
228 	return flags;
229 }
230 
231 
232 uint32
233 PartitionMapHandle::SupportedChildOperations(const BMutablePartition* child,
234 	uint32 mask)
235 {
236 	return B_DISK_SYSTEM_SUPPORTS_RESIZING_CHILD
237 		| B_DISK_SYSTEM_SUPPORTS_MOVING_CHILD
238 		| B_DISK_SYSTEM_SUPPORTS_SETTING_TYPE
239 		| B_DISK_SYSTEM_SUPPORTS_DELETING_CHILD;
240 }
241 
242 
243 status_t
244 PartitionMapHandle::GetNextSupportedType(const BMutablePartition* child,
245 	int32* cookie, BString* type)
246 {
247 	TRACE("%p->PartitionMapHandle::GetNextSupportedType(child: %p, "
248 		"cookie: %ld)\n", this, child, *cookie);
249 
250 	int32 index = *cookie;
251 	const partition_type* nextType;
252 	while (true) {
253 		nextType = fPartitionMap.GetNextSupportedPartitionType(index);
254 		if (nextType == NULL)
255 			return B_ENTRY_NOT_FOUND;
256 		index++;
257 		if (nextType->used)
258 			break;
259 	}
260 
261 	if (!nextType)
262 		return B_ENTRY_NOT_FOUND;
263 
264 	type->SetTo(nextType->name);
265 	*cookie = index;
266 
267 	return B_OK;
268 }
269 
270 
271 status_t
272 PartitionMapHandle::GetPartitioningInfo(BPartitioningInfo* info)
273 {
274 	// init to the full size (minus the first sector)
275 	off_t size = Partition()->ContentSize();
276 	status_t error = info->SetTo(Partition()->BlockSize(),
277 		size - Partition()->BlockSize());
278 	if (error != B_OK)
279 		return error;
280 
281 	// exclude the space of the existing partitions
282 	for (int32 i = 0; i < 4; i++) {
283 		PrimaryPartition* primary = fPartitionMap.PrimaryPartitionAt(i);
284 		if (!primary->IsEmpty()) {
285 			error = info->ExcludeOccupiedSpace(primary->Offset(),
286 				primary->Size());
287 			if (error != B_OK)
288 				return error;
289 		}
290 	}
291 
292 	return B_OK;
293 }
294 
295 
296 status_t
297 PartitionMapHandle::GetParameterEditor(B_PARAMETER_EDITOR_TYPE type,
298 	BPartitionParameterEditor** editor)
299 {
300 	*editor = NULL;
301 	if (type == B_CREATE_PARAMETER_EDITOR
302 		|| type == B_PROPERTIES_PARAMETER_EDITOR) {
303 		try {
304 			*editor = new PrimaryPartitionEditor();
305 		} catch (std::bad_alloc) {
306 			return B_NO_MEMORY;
307 		}
308 		return B_OK;
309 	}
310 	return B_NOT_SUPPORTED;
311 }
312 
313 
314 status_t
315 PartitionMapHandle::ValidateCreateChild(off_t* _offset, off_t* _size,
316 	const char* typeString, BString* name, const char* parameters)
317 {
318 	// check type
319 	PartitionType type;
320 	if (!type.SetType(typeString) || type.IsEmpty())
321 		return B_BAD_VALUE;
322 
323 	if (type.IsExtended() && fPartitionMap.ExtendedPartitionIndex() >= 0) {
324 		// There can only be a single extended partition
325 		return B_BAD_VALUE;
326 	}
327 
328 	// check name
329 	if (name)
330 		name->Truncate(0);
331 
332 	// check parameters
333 	void* handle = parse_driver_settings_string(parameters);
334 	if (handle == NULL)
335 		return B_ERROR;
336 	get_driver_boolean_parameter(handle, "active", false, true);
337 	delete_driver_settings(handle);
338 
339 	// do we have a spare primary partition?
340 	if (fPartitionMap.CountNonEmptyPrimaryPartitions() == 4)
341 		return B_BAD_VALUE;
342 
343 	// check the free space situation
344 	BPartitioningInfo info;
345 	status_t error = GetPartitioningInfo(&info);
346 	if (error != B_OK)
347 		return error;
348 
349 	// any space in the partition at all?
350 	int32 spacesCount = info.CountPartitionableSpaces();
351 	if (spacesCount == 0)
352 		return B_BAD_VALUE;
353 
354 	// check offset and size
355 	off_t offset = sector_align(*_offset, Partition()->BlockSize());
356 	off_t size = sector_align(*_size, Partition()->BlockSize());
357 		// TODO: Rather round size up?
358 	off_t end = offset + size;
359 
360 	// get the first partitionable space the requested interval intersects with
361 	int32 spaceIndex = -1;
362 	int32 closestSpaceIndex = -1;
363 	off_t closestSpaceDistance = 0;
364 	for (int32 i = 0; i < spacesCount; i++) {
365 		off_t spaceOffset, spaceSize;
366 		info.GetPartitionableSpaceAt(i, &spaceOffset, &spaceSize);
367 		off_t spaceEnd = spaceOffset + spaceSize;
368 
369 		if ((spaceOffset >= offset && spaceOffset < end)
370 			|| (offset >= spaceOffset && offset < spaceEnd)) {
371 			spaceIndex = i;
372 			break;
373 		}
374 
375 		off_t distance;
376 		if (offset < spaceOffset)
377 			distance = spaceOffset - end;
378 		else
379 			distance = spaceEnd - offset;
380 
381 		if (closestSpaceIndex == -1 || distance < closestSpaceDistance) {
382 			closestSpaceIndex = i;
383 			closestSpaceDistance = distance;
384 		}
385 	}
386 
387 	// get the space we found
388 	off_t spaceOffset, spaceSize;
389 	info.GetPartitionableSpaceAt(
390 		spaceIndex >= 0 ? spaceIndex : closestSpaceIndex, &spaceOffset,
391 		&spaceSize);
392 	off_t spaceEnd = spaceOffset + spaceSize;
393 
394 	// If the requested intervald doesn't intersect with any space yet, move
395 	// it, so that it does.
396 	if (spaceIndex < 0) {
397 		spaceIndex = closestSpaceIndex;
398 		if (offset < spaceOffset) {
399 			offset = spaceOffset;
400 			end = offset + size;
401 		} else {
402 			end = spaceEnd;
403 			offset = end - size;
404 		}
405 	}
406 
407 	// move/shrink the interval, so that it fully lies within the space
408 	if (offset < spaceOffset) {
409 		offset = spaceOffset;
410 		end = offset + size;
411 		if (end > spaceEnd) {
412 			end = spaceEnd;
413 			size = end - offset;
414 		}
415 	} else if (end > spaceEnd) {
416 		end = spaceEnd;
417 		offset = end - size;
418 		if (offset < spaceOffset) {
419 			offset = spaceOffset;
420 			size = end - offset;
421 		}
422 	}
423 
424 	*_offset = offset;
425 	*_size = size;
426 
427 	return B_OK;
428 }
429 
430 
431 status_t
432 PartitionMapHandle::CreateChild(off_t offset, off_t size,
433 	const char* typeString, const char* name, const char* parameters,
434 	BMutablePartition** _child)
435 {
436 	// check type
437 	PartitionType type;
438 	if (!type.SetType(typeString) || type.IsEmpty())
439 		return B_BAD_VALUE;
440 	if (type.IsExtended() && fPartitionMap.ExtendedPartitionIndex() >= 0)
441 		return B_BAD_VALUE;
442 
443 	// check name
444 	if (name && *name != '\0')
445 		return B_BAD_VALUE;
446 
447 	// check parameters
448 	void* handle = parse_driver_settings_string(parameters);
449 	if (handle == NULL)
450 		return B_ERROR;
451 
452 	bool active = get_driver_boolean_parameter(handle, "active", false, true);
453 	delete_driver_settings(handle);
454 
455 	// get a spare primary partition
456 	PrimaryPartition* primary = NULL;
457 	for (int32 i = 0; i < 4; i++) {
458 		if (fPartitionMap.PrimaryPartitionAt(i)->IsEmpty()) {
459 			primary = fPartitionMap.PrimaryPartitionAt(i);
460 			break;
461 		}
462 	}
463 	if (!primary)
464 		return B_BAD_VALUE;
465 
466 	// offset properly aligned?
467 	if (offset != sector_align(offset, Partition()->BlockSize())
468 		|| size != sector_align(size, Partition()->BlockSize()))
469 		return B_BAD_VALUE;
470 
471 	// check the free space situation
472 	BPartitioningInfo info;
473 	status_t error = GetPartitioningInfo(&info);
474 	if (error != B_OK)
475 		return error;
476 
477 	bool foundSpace = false;
478 	off_t end = offset + size;
479 	int32 spacesCount = info.CountPartitionableSpaces();
480 	for (int32 i = 0; i < spacesCount; i++) {
481 		off_t spaceOffset, spaceSize;
482 		info.GetPartitionableSpaceAt(i, &spaceOffset, &spaceSize);
483 		off_t spaceEnd = spaceOffset + spaceSize;
484 
485 		if (offset >= spaceOffset && end <= spaceEnd) {
486 			foundSpace = true;
487 			break;
488 		}
489 	}
490 
491 	if (!foundSpace)
492 		return B_BAD_VALUE;
493 
494 	// create the child
495 	// (Note: the primary partition index is indeed the child index, since
496 	// we picked the first empty primary partition.)
497 	BMutablePartition* partition = Partition();
498 	BMutablePartition* child;
499 	error = partition->CreateChild(primary->Index(), typeString, name,
500 		parameters, &child);
501 	if (error != B_OK)
502 		return error;
503 
504 	// init the child
505 	child->SetOffset(offset);
506 	child->SetSize(size);
507 	child->SetBlockSize(partition->BlockSize());
508 	//child->SetFlags(0);
509 	child->SetChildCookie(primary);
510 
511 	// init the primary partition
512 	primary->SetTo(offset, size, type.Type(), active, partition->BlockSize());
513 
514 	*_child = child;
515 	return B_OK;
516 }
517 
518 
519 status_t
520 PartitionMapHandle::DeleteChild(BMutablePartition* child)
521 {
522 	BMutablePartition* parent = child->Parent();
523 	status_t error = parent->DeleteChild(child);
524 
525 	return error;
526 }
527