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
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::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
117status_t
118PartitionMapAddOn::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
154PartitionMapHandle::PartitionMapHandle(BMutablePartition* partition)
155	:
156	BPartitionHandle(partition)
157{
158}
159
160
161PartitionMapHandle::~PartitionMapHandle()
162{
163}
164
165
166status_t
167PartitionMapHandle::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
209uint32
210PartitionMapHandle::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
233uint32
234PartitionMapHandle::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
244status_t
245PartitionMapHandle::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
272status_t
273PartitionMapHandle::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
297status_t
298PartitionMapHandle::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
314status_t
315PartitionMapHandle::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
335status_t
336PartitionMapHandle::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
353status_t
354PartitionMapHandle::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
470status_t
471PartitionMapHandle::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
558status_t
559PartitionMapHandle::DeleteChild(BMutablePartition* child)
560{
561	BMutablePartition* parent = child->Parent();
562	status_t error = parent->DeleteChild(child);
563
564	return error;
565}
566