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