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