1/*
2 * Copyright 2007, Ingo Weinhold, bonefish@users.sf.net.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7#include "ExtendedPartitionAddOn.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
20
21//#define TRACE_EXTENDED_PARTITION_ADD_ON
22#undef TRACE
23#ifdef TRACE_EXTENDED_PARTITION_ADD_ON
24#	define TRACE(x...) printf(x)
25#else
26#	define TRACE(x...) do {} while (false)
27#endif
28
29#define PTS_OFFSET (63 * Partition()->BlockSize())
30
31
32using std::nothrow;
33
34
35static const uint32 kDiskSystemFlags =
36	0
37//	| B_DISK_SYSTEM_SUPPORTS_CHECKING
38//	| B_DISK_SYSTEM_SUPPORTS_REPAIRING
39//	| B_DISK_SYSTEM_SUPPORTS_RESIZING
40//	| B_DISK_SYSTEM_SUPPORTS_MOVING
41//	| B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_NAME
42//	| B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_PARAMETERS
43//	| B_DISK_SYSTEM_SUPPORTS_INITIALIZING
44//	| B_DISK_SYSTEM_SUPPORTS_CONTENT_NAME
45
46//	| B_DISK_SYSTEM_SUPPORTS_RESIZING_CHILD
47//	| B_DISK_SYSTEM_SUPPORTS_MOVING_CHILD
48//	| B_DISK_SYSTEM_SUPPORTS_SETTING_NAME
49	| B_DISK_SYSTEM_SUPPORTS_SETTING_TYPE
50//	| B_DISK_SYSTEM_SUPPORTS_SETTING_PARAMETERS
51	| B_DISK_SYSTEM_SUPPORTS_CREATING_CHILD
52	| B_DISK_SYSTEM_SUPPORTS_DELETING_CHILD
53//	| B_DISK_SYSTEM_SUPPORTS_NAME
54;
55
56
57// #pragma mark - ExtendedPartitionAddOn
58
59
60ExtendedPartitionAddOn::ExtendedPartitionAddOn()
61	:
62	BDiskSystemAddOn(kPartitionTypeIntelExtended, kDiskSystemFlags)
63{
64}
65
66
67ExtendedPartitionAddOn::~ExtendedPartitionAddOn()
68{
69}
70
71
72status_t
73ExtendedPartitionAddOn::CreatePartitionHandle(BMutablePartition* partition,
74	BPartitionHandle** _handle)
75{
76	ExtendedPartitionHandle* handle
77		= new(nothrow) ExtendedPartitionHandle(partition);
78	if (!handle)
79		return B_NO_MEMORY;
80
81	status_t error = handle->Init();
82	if (error != B_OK) {
83		delete handle;
84		return error;
85	}
86
87	*_handle = handle;
88	return B_OK;
89}
90
91
92bool
93ExtendedPartitionAddOn::CanInitialize(const BMutablePartition* partition)
94{
95	// If it's big enough, we can initialize it.
96	return false;
97}
98
99
100status_t
101ExtendedPartitionAddOn::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
118ExtendedPartitionAddOn::Initialize(BMutablePartition* partition,
119	const char* name, 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	ExtendedPartitionHandle* handle
129		= new(nothrow) ExtendedPartitionHandle(partition);
130	if (!handle)
131		return B_NO_MEMORY;
132	ObjectDeleter<ExtendedPartitionHandle> handleDeleter(handle);
133
134	// init the partition
135	status_t error = partition->SetContentType(Name());
136	if (error != B_OK)
137		return error;
138	// TODO: The content type could as well be set by the caller.
139
140	partition->SetContentName(NULL);
141	partition->SetContentParameters(NULL);
142	partition->SetContentSize(
143		sector_align(partition->Size(), partition->BlockSize()));
144	partition->Changed(B_PARTITION_CHANGED_INITIALIZATION);
145
146	*_handle = handleDeleter.Detach();
147
148	return B_OK;
149}
150
151
152// #pragma mark - ExtendedPartitionHandle
153
154
155ExtendedPartitionHandle::ExtendedPartitionHandle(BMutablePartition* partition)
156	:
157	BPartitionHandle(partition)
158{
159}
160
161
162ExtendedPartitionHandle::~ExtendedPartitionHandle()
163{
164}
165
166
167status_t
168ExtendedPartitionHandle::Init()
169{
170	// initialize the extended partition from the mutable partition
171
172	BMutablePartition* partition = Partition();
173
174	// our parent has already set the child cookie to the primary partition.
175	fPrimaryPartition = (PrimaryPartition*)partition->ChildCookie();
176	if (!fPrimaryPartition)
177		return B_BAD_VALUE;
178
179	if (!fPrimaryPartition->IsExtended())
180		return B_BAD_VALUE;
181
182	// init the child partitions
183	int32 count = partition->CountChildren();
184	for (int32 i = 0; i < count; i++) {
185		BMutablePartition* child = partition->ChildAt(i);
186
187		PartitionType type;
188		if (!type.SetType(child->Type()))
189			return B_BAD_VALUE;
190
191		void* handle = parse_driver_settings_string(child->Parameters());
192		if (handle == NULL)
193			return B_ERROR;
194
195		bool active = get_driver_boolean_parameter(
196			handle, "active", false, true);
197
198		off_t ptsOffset = 0;
199		const char* buffer = get_driver_parameter(handle,
200			"partition_table_offset", NULL, NULL);
201		if (buffer != NULL)
202			ptsOffset = strtoull(buffer, NULL, 10);
203		else {
204			unload_driver_settings(handle);
205			return B_BAD_VALUE;
206		}
207		unload_driver_settings(handle);
208
209		LogicalPartition* logical = new(nothrow) LogicalPartition;
210		if (!logical)
211			return B_NO_MEMORY;
212
213		logical->SetTo(child->Offset(), child->Size(), type.Type(), active,
214			ptsOffset, fPrimaryPartition);
215
216		child->SetChildCookie(logical);
217	}
218
219	return B_OK;
220}
221
222
223uint32
224ExtendedPartitionHandle::SupportedOperations(uint32 mask)
225{
226	uint32 flags = 0;
227
228	// creating child
229	if ((mask & B_DISK_SYSTEM_SUPPORTS_CREATING_CHILD) != 0) {
230		BPartitioningInfo info;
231		if (GetPartitioningInfo(&info) == B_OK
232			&& info.CountPartitionableSpaces() > 1) {
233			flags |= B_DISK_SYSTEM_SUPPORTS_CREATING_CHILD;
234		}
235	}
236
237	return flags;
238}
239
240
241uint32
242ExtendedPartitionHandle::SupportedChildOperations(
243	const BMutablePartition* child, uint32 mask)
244{
245	return B_DISK_SYSTEM_SUPPORTS_DELETING_CHILD;
246}
247
248
249status_t
250ExtendedPartitionHandle::GetNextSupportedType(const BMutablePartition* child,
251	int32* cookie, BString* type)
252{
253	int32 index = *cookie;
254	const partition_type* nextType;
255	PartitionMap partitionMap;
256	while (true) {
257		nextType = partitionMap.GetNextSupportedPartitionType(index);
258		if (nextType == NULL)
259			return B_ENTRY_NOT_FOUND;
260		index++;
261		if (nextType->used
262			&& strcmp(nextType->name, kPartitionTypeIntelExtended) != 0)
263			break;
264	}
265
266	if (!nextType)
267		return B_ENTRY_NOT_FOUND;
268
269	type->SetTo(nextType->name);
270	*cookie = index;
271
272	return B_OK;
273}
274
275
276status_t
277ExtendedPartitionHandle::GetPartitioningInfo(BPartitioningInfo* info)
278{
279	// init to the full size (minus the first PTS_OFFSET)
280	BMutablePartition* partition = Partition();
281	off_t offset = partition->Offset() + PTS_OFFSET;
282	off_t size = partition->Size() - PTS_OFFSET;
283	status_t error = info->SetTo(offset, size);
284	if (error != B_OK)
285		return error;
286
287	// exclude the space of the existing logical partitions
288	int32 count = partition->CountChildren();
289	for (int32 i = 0; i < count; i++) {
290		BMutablePartition* child = partition->ChildAt(i);
291		error = info->ExcludeOccupiedSpace(child->Offset(),
292			child->Size() + PTS_OFFSET + Partition()->BlockSize());
293		if (error != B_OK)
294			return error;
295
296		LogicalPartition* logical = (LogicalPartition*)child->ChildCookie();
297		if (logical == NULL)
298			return B_BAD_VALUE;
299		error = info->ExcludeOccupiedSpace(
300			logical->PartitionTableOffset(),
301				PTS_OFFSET + Partition()->BlockSize());
302		if (error != B_OK)
303			return error;
304	}
305
306	return B_OK;
307}
308
309
310status_t
311ExtendedPartitionHandle::GetParameterEditor(B_PARAMETER_EDITOR_TYPE type,
312	BPartitionParameterEditor** editor)
313{
314	*editor = NULL;
315	return B_NOT_SUPPORTED;
316}
317
318
319status_t
320ExtendedPartitionHandle::ValidateCreateChild(off_t* _offset, off_t* _size,
321	const char* typeString, BString* name, const char* parameters)
322{
323	// check type
324	if (!typeString)
325		return B_BAD_VALUE;
326
327	// check name
328	if (name)
329		name->Truncate(0);
330
331	// check the free space situation
332	BPartitioningInfo info;
333	status_t error = GetPartitioningInfo(&info);
334	if (error != B_OK)
335		return error;
336
337	// any space in the partition at all?
338	int32 spacesCount = info.CountPartitionableSpaces();
339	if (spacesCount == 0)
340		return B_BAD_VALUE;
341
342	// check offset and size
343	off_t offset = sector_align(*_offset, Partition()->BlockSize());
344	off_t size = sector_align(*_size, Partition()->BlockSize());
345		// TODO: Rather round size up?
346	off_t end = offset + size;
347
348	// get the first partitionable space the requested interval intersects with
349	int32 spaceIndex = -1;
350	int32 closestSpaceIndex = -1;
351	off_t closestSpaceDistance = 0;
352	for (int32 i = 0; i < spacesCount; i++) {
353		off_t spaceOffset, spaceSize;
354		info.GetPartitionableSpaceAt(i, &spaceOffset, &spaceSize);
355		off_t spaceEnd = spaceOffset + spaceSize;
356
357		if ((spaceOffset >= offset && spaceOffset < end)
358			|| (offset >= spaceOffset && offset < spaceEnd)) {
359			spaceIndex = i;
360			break;
361		}
362
363		off_t distance;
364		if (offset < spaceOffset)
365			distance = spaceOffset - end;
366		else
367			distance = spaceEnd - offset;
368
369		if (closestSpaceIndex == -1 || distance < closestSpaceDistance) {
370			closestSpaceIndex = i;
371			closestSpaceDistance = distance;
372		}
373	}
374
375	// get the space we found
376	off_t spaceOffset, spaceSize;
377	info.GetPartitionableSpaceAt(
378		spaceIndex >= 0 ? spaceIndex : closestSpaceIndex, &spaceOffset,
379		&spaceSize);
380	off_t spaceEnd = spaceOffset + spaceSize;
381
382	// If the requested intervald doesn't intersect with any space yet, move
383	// it, so that it does.
384	if (spaceIndex < 0) {
385		spaceIndex = closestSpaceIndex;
386		if (offset < spaceOffset) {
387			offset = spaceOffset;
388			end = offset + size;
389		} else {
390			end = spaceEnd;
391			offset = end - size;
392		}
393	}
394
395	// move/shrink the interval, so that it fully lies within the space
396	if (offset < spaceOffset) {
397		offset = spaceOffset;
398		end = offset + size;
399		if (end > spaceEnd) {
400			end = spaceEnd;
401			size = end - offset;
402		}
403	} else if (end > spaceEnd) {
404		end = spaceEnd;
405		offset = end - size;
406		if (offset < spaceOffset) {
407			offset = spaceOffset;
408			size = end - offset;
409		}
410	}
411
412	*_offset = offset;
413	*_size = size;
414
415	return B_OK;
416}
417
418
419status_t
420ExtendedPartitionHandle::CreateChild(off_t offset, off_t size,
421	const char* typeString, const char* name, const char* _parameters,
422	BMutablePartition** _child)
423{
424	// check type
425	PartitionType type;
426	if (!type.SetType(typeString) || type.IsEmpty())
427		return B_BAD_VALUE;
428
429	// check name
430	if (name != NULL && name[0] != '\0')
431		return B_BAD_VALUE;
432
433	// offset properly aligned?
434	if (offset != sector_align(offset, Partition()->BlockSize())
435		|| size != sector_align(size, Partition()->BlockSize()))
436		return B_BAD_VALUE;
437
438	// check the free space situation
439	BPartitioningInfo info;
440	status_t error = GetPartitioningInfo(&info);
441	if (error != B_OK)
442		return error;
443
444	bool foundSpace = false;
445	off_t end = offset + size;
446	int32 spacesCount = info.CountPartitionableSpaces();
447	for (int32 i = 0; i < spacesCount; i++) {
448		off_t spaceOffset, spaceSize;
449		info.GetPartitionableSpaceAt(i, &spaceOffset, &spaceSize);
450		off_t spaceEnd = spaceOffset + spaceSize;
451
452		if (offset >= spaceOffset && end <= spaceEnd) {
453			foundSpace = true;
454			break;
455		}
456	}
457
458	if (!foundSpace)
459		return B_BAD_VALUE;
460
461	BString parameters(_parameters);
462	parameters << "partition_table_offset " << offset - PTS_OFFSET << " ;\n";
463	// everything looks good, create the child
464	BMutablePartition* child;
465	error = Partition()->CreateChild(-1, typeString,
466		NULL, parameters.String(), &child);
467	if (error != B_OK)
468		return error;
469
470	// init the child
471	child->SetOffset(offset);
472	child->SetSize(size);
473	child->SetBlockSize(Partition()->BlockSize());
474	//child->SetFlags(0);
475	child->SetChildCookie(Partition());
476
477	*_child = child;
478	return B_OK;
479}
480
481
482status_t
483ExtendedPartitionHandle::DeleteChild(BMutablePartition* child)
484{
485	BMutablePartition* parent = child->Parent();
486	status_t error = parent->DeleteChild(child);
487
488	return error;
489}
490