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