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