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