xref: /haiku/src/add-ons/disk_systems/intel/ExtendedPartitionAddOn.cpp (revision af435dd1c9460acc910170b4f82f44bfd3f557c9)
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 
ExtendedPartitionAddOn()60 ExtendedPartitionAddOn::ExtendedPartitionAddOn()
61 	:
62 	BDiskSystemAddOn(kPartitionTypeIntelExtended, kDiskSystemFlags)
63 {
64 }
65 
66 
~ExtendedPartitionAddOn()67 ExtendedPartitionAddOn::~ExtendedPartitionAddOn()
68 {
69 }
70 
71 
72 status_t
CreatePartitionHandle(BMutablePartition * partition,BPartitionHandle ** _handle)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
CanInitialize(const BMutablePartition * partition)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
ValidateInitialize(const BMutablePartition * partition,BString * name,const char * parameters)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
Initialize(BMutablePartition * partition,const char * name,const char * parameters,BPartitionHandle ** _handle)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 	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 
ExtendedPartitionHandle(BMutablePartition * partition)155 ExtendedPartitionHandle::ExtendedPartitionHandle(BMutablePartition* partition)
156 	:
157 	BPartitionHandle(partition)
158 {
159 }
160 
161 
~ExtendedPartitionHandle()162 ExtendedPartitionHandle::~ExtendedPartitionHandle()
163 {
164 }
165 
166 
167 status_t
Init()168 ExtendedPartitionHandle::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 
223 uint32
SupportedOperations(uint32 mask)224 ExtendedPartitionHandle::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 
241 uint32
SupportedChildOperations(const BMutablePartition * child,uint32 mask)242 ExtendedPartitionHandle::SupportedChildOperations(
243 	const BMutablePartition* child, uint32 mask)
244 {
245 	return B_DISK_SYSTEM_SUPPORTS_DELETING_CHILD;
246 }
247 
248 
249 status_t
GetNextSupportedType(const BMutablePartition * child,int32 * cookie,BString * type)250 ExtendedPartitionHandle::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 
276 status_t
GetPartitioningInfo(BPartitioningInfo * info)277 ExtendedPartitionHandle::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 
310 status_t
GetParameterEditor(B_PARAMETER_EDITOR_TYPE type,BPartitionParameterEditor ** editor)311 ExtendedPartitionHandle::GetParameterEditor(B_PARAMETER_EDITOR_TYPE type,
312 	BPartitionParameterEditor** editor)
313 {
314 	*editor = NULL;
315 	return B_NOT_SUPPORTED;
316 }
317 
318 
319 status_t
ValidateCreateChild(off_t * _offset,off_t * _size,const char * typeString,BString * name,const char * parameters)320 ExtendedPartitionHandle::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 
419 status_t
CreateChild(off_t offset,off_t size,const char * typeString,const char * name,const char * _parameters,BMutablePartition ** _child)420 ExtendedPartitionHandle::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 
482 status_t
DeleteChild(BMutablePartition * child)483 ExtendedPartitionHandle::DeleteChild(BMutablePartition* child)
484 {
485 	BMutablePartition* parent = child->Parent();
486 	status_t error = parent->DeleteChild(child);
487 
488 	return error;
489 }
490