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