xref: /haiku/src/add-ons/disk_systems/intel/PartitionMapAddOn.cpp (revision 89755088d790ff4fe36f8aa77dacb2bd15507108)
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 * SECTOR_SIZE;
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->SetBlockSize(SECTOR_SIZE);
144 	partition->SetContentSize(partition->Size() / SECTOR_SIZE * SECTOR_SIZE);
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 
201 		child->SetChildCookie(primary);
202 	}
203 
204 	// The extended partition (if any) is initialized by
205 	// ExtendedPartitionHandle::Init().
206 
207 	return B_OK;
208 }
209 
210 
211 // SupportedOperations
212 uint32
213 PartitionMapHandle::SupportedOperations(uint32 mask)
214 {
215 	BMutablePartition* partition = Partition();
216 
217 	uint32 flags = B_DISK_SYSTEM_SUPPORTS_RESIZING
218 		| B_DISK_SYSTEM_SUPPORTS_MOVING
219 		| B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_PARAMETERS
220 		| B_DISK_SYSTEM_SUPPORTS_INITIALIZING;
221 
222 	// creating child
223 	if (mask & B_DISK_SYSTEM_SUPPORTS_CREATING_CHILD) {
224 		BPartitioningInfo info;
225 		if (partition->CountChildren() < 4
226 			&& GetPartitioningInfo(&info) == B_OK
227 			&& info.CountPartitionableSpaces() > 1) {
228 			flags |= B_DISK_SYSTEM_SUPPORTS_CREATING_CHILD;
229 		}
230 	}
231 
232 	return flags;
233 }
234 
235 
236 // SupportedChildOperations
237 uint32
238 PartitionMapHandle::SupportedChildOperations(const BMutablePartition* child,
239 	uint32 mask)
240 {
241 	return B_DISK_SYSTEM_SUPPORTS_RESIZING_CHILD
242 		| B_DISK_SYSTEM_SUPPORTS_MOVING_CHILD
243 		| B_DISK_SYSTEM_SUPPORTS_SETTING_TYPE
244 		| B_DISK_SYSTEM_SUPPORTS_DELETING_CHILD;
245 }
246 
247 
248 // GetNextSupportedType
249 status_t
250 PartitionMapHandle::GetNextSupportedType(const BMutablePartition* child,
251 	int32* cookie, BString* type)
252 {
253 	// TODO: What are we supposed to do with the child?
254 
255 	// we support creating two types, primary and extended
256 	if (*cookie < 0 || *cookie > 1)
257 		return B_ENTRY_NOT_FOUND;
258 
259 	// check if there are any spaces at all
260 	// TODO: check if the spaces have enough size at all
261 	BPartitioningInfo info;
262 	status_t ret = GetPartitioningInfo(&info);
263 	if (ret < B_OK)
264 		return ret;
265 
266 	if (info.CountPartitionableSpaces() == 0)
267 		return B_ENTRY_NOT_FOUND;
268 
269 	// adjust the cookie here already so that we don't have
270 	// to worry about it when returning early below
271 	*cookie = *cookie + 1;
272 
273 	if (*cookie == 1) {
274 		// On first iteration, check if we can create more primary
275 		// partitions. If this is not possible, we cannot create
276 		// any extended partitions either.
277 		for (int32 i = 0; i < 4; i++) {
278 			PrimaryPartition* primary = fPartitionMap.PrimaryPartitionAt(i);
279 			if (primary->IsEmpty()) {
280 				*type = kPartitionTypeIntelPrimary;
281 				return B_OK;
282 			}
283 		}
284 	} else if (*cookie == 2) {
285 		// On second iteration, check if we can create more primary
286 		// partitions. Also check if there already is an extended
287 		// partition, only if there is at least one empty and no
288 		// extended partition, we can create an extended partition.
289 		bool foundExtended = false;
290 		bool foundEmpty = false;
291 		for (int32 i = 0; i < 4; i++) {
292 			PrimaryPartition* primary = fPartitionMap.PrimaryPartitionAt(i);
293 			if (primary->IsEmpty())
294 				foundEmpty = true;
295 			else if (primary->IsExtended())
296 				foundExtended = true;
297 		}
298 		if (foundEmpty && !foundExtended) {
299 			*type = kPartitionTypeIntelExtended;
300 			return B_OK;
301 		}
302 	}
303 
304 	return B_ENTRY_NOT_FOUND;
305 }
306 
307 
308 // GetPartitioningInfo
309 status_t
310 PartitionMapHandle::GetPartitioningInfo(BPartitioningInfo* info)
311 {
312 	// init to the full size (minus the first sector)
313 	off_t size = Partition()->ContentSize();
314 	status_t error = info->SetTo(SECTOR_SIZE, size - SECTOR_SIZE);
315 	if (error != B_OK)
316 		return error;
317 
318 	// exclude the space of the existing partitions
319 	for (int32 i = 0; i < 4; i++) {
320 		PrimaryPartition* primary = fPartitionMap.PrimaryPartitionAt(i);
321 		if (!primary->IsEmpty()) {
322 			error = info->ExcludeOccupiedSpace(primary->Offset(),
323 				primary->Size());
324 			if (error != B_OK)
325 				return error;
326 		}
327 	}
328 
329 	return B_OK;
330 }
331 
332 
333 // GetChildCreationParameterEditor
334 status_t
335 PartitionMapHandle::GetChildCreationParameterEditor(const char* type,
336 	BDiskDeviceParameterEditor** editor)
337 {
338 	// TODO: We actually need an editor here.
339 	*editor = NULL;
340 	return B_OK;
341 }
342 
343 
344 // ValidateCreateChild
345 status_t
346 PartitionMapHandle::ValidateCreateChild(off_t* _offset, off_t* _size,
347 	const char* typeString, BString* name, const char* parameters)
348 {
349 	// check type
350 	PartitionType type;
351 	if (!type.SetType(typeString) || type.IsEmpty())
352 		return B_BAD_VALUE;
353 	if (type.IsExtended() && fPartitionMap.ExtendedPartitionIndex() >= 0)
354 		return B_BAD_VALUE;
355 
356 	// check name
357 	if (name)
358 		name->Truncate(0);
359 
360 	// check parameters
361 	// TODO:...
362 
363 	// do we have a spare primary partition?
364 	if (fPartitionMap.CountNonEmptyPrimaryPartitions() == 4)
365 		return B_BAD_VALUE;
366 
367 	// check the free space situation
368 	BPartitioningInfo info;
369 	status_t error = GetPartitioningInfo(&info);
370 	if (error != B_OK)
371 		return error;
372 
373 	// any space in the partition at all?
374 	int32 spacesCount = info.CountPartitionableSpaces();
375 	if (spacesCount == 0)
376 		return B_BAD_VALUE;
377 
378 	// check offset and size
379 	off_t offset = sector_align(*_offset);
380 	off_t size = sector_align(*_size);
381 		// TODO: Rather round size up?
382 	off_t end = offset + size;
383 
384 	// get the first partitionable space the requested interval intersects with
385 	int32 spaceIndex = -1;
386 	int32 closestSpaceIndex = -1;
387 	off_t closestSpaceDistance = 0;
388 	for (int32 i = 0; i < spacesCount; i++) {
389 		off_t spaceOffset, spaceSize;
390 		info.GetPartitionableSpaceAt(i, &spaceOffset, &spaceSize);
391 		off_t spaceEnd = spaceOffset + spaceSize;
392 
393 		if (spaceOffset >= offset && spaceOffset < end
394 			|| offset >= spaceOffset && offset < spaceEnd) {
395 			spaceIndex = i;
396 			break;
397 		}
398 
399 		off_t distance;
400 		if (offset < spaceOffset)
401 			distance = spaceOffset - end;
402 		else
403 			distance = spaceEnd - offset;
404 
405 		if (closestSpaceIndex == -1 || distance < closestSpaceDistance) {
406 			closestSpaceIndex = i;
407 			closestSpaceDistance = distance;
408 		}
409 	}
410 
411 	// get the space we found
412 	off_t spaceOffset, spaceSize;
413 	info.GetPartitionableSpaceAt(
414 		spaceIndex >= 0 ? spaceIndex : closestSpaceIndex, &spaceOffset,
415 		&spaceSize);
416 	off_t spaceEnd = spaceOffset + spaceSize;
417 
418 	// If the requested intervald doesn't intersect with any space yet, move
419 	// it, so that it does.
420 	if (spaceIndex < 0) {
421 		spaceIndex = closestSpaceIndex;
422 		if (offset < spaceOffset) {
423 			offset = spaceOffset;
424 			end = offset + size;
425 		} else {
426 			end = spaceEnd;
427 			offset = end - size;
428 		}
429 	}
430 
431 	// move/shrink the interval, so that it fully lies within the space
432 	if (offset < spaceOffset) {
433 		offset = spaceOffset;
434 		end = offset + size;
435 		if (end > spaceEnd) {
436 			end = spaceEnd;
437 			size = end - offset;
438 		}
439 	} else if (end > spaceEnd) {
440 		end = spaceEnd;
441 		offset = end - size;
442 		if (offset < spaceOffset) {
443 			offset = spaceOffset;
444 			size = end - offset;
445 		}
446 	}
447 
448 	*_offset = offset;
449 	*_size = size;
450 
451 	return B_OK;
452 }
453 
454 
455 // CreateChild
456 status_t
457 PartitionMapHandle::CreateChild(off_t offset, off_t size,
458 	const char* typeString, const char* name, const char* parameters,
459 	BMutablePartition** _child)
460 {
461 	// check type
462 	PartitionType type;
463 	if (!type.SetType(typeString) || type.IsEmpty())
464 		return B_BAD_VALUE;
465 	if (type.IsExtended() && fPartitionMap.ExtendedPartitionIndex() >= 0)
466 		return B_BAD_VALUE;
467 
468 	// check name
469 	if (name && strlen(name) > 0)
470 		return B_BAD_VALUE;
471 
472 	// check parameters
473 	// TODO:...
474 
475 	// get a spare primary partition
476 	PrimaryPartition* primary = NULL;
477 	for (int32 i = 0; i < 4; i++) {
478 		if (fPartitionMap.PrimaryPartitionAt(i)->IsEmpty()) {
479 			primary = fPartitionMap.PrimaryPartitionAt(i);
480 			break;
481 		}
482 	}
483 	if (!primary)
484 		return B_BAD_VALUE;
485 
486 	// offset properly aligned?
487 	if (offset != sector_align(offset) || size != sector_align(size))
488 		return B_BAD_VALUE;
489 
490 	// check the free space situation
491 	BPartitioningInfo info;
492 	status_t error = GetPartitioningInfo(&info);
493 	if (error != B_OK)
494 		return error;
495 
496 	bool foundSpace = false;
497 	off_t end = offset + size;
498 	int32 spacesCount = info.CountPartitionableSpaces();
499 	for (int32 i = 0; i < spacesCount; i++) {
500 		off_t spaceOffset, spaceSize;
501 		info.GetPartitionableSpaceAt(i, &spaceOffset, &spaceSize);
502 		off_t spaceEnd = spaceOffset + spaceSize;
503 
504 		if (offset >= spaceOffset && end <= spaceEnd) {
505 			foundSpace = true;
506 			break;
507 		}
508 	}
509 
510 	if (!foundSpace)
511 		return B_BAD_VALUE;
512 
513 	// everything looks good, do it
514 
515 	// create the child
516 	// (Note: the primary partition index is indeed the child index, since
517 	// we picked the first empty primary partition.)
518 	BMutablePartition* partition = Partition();
519 	BMutablePartition* child;
520 	error = partition->CreateChild(primary->Index(), typeString, NULL,
521 		parameters, &child);
522 	if (error != B_OK)
523 		return error;
524 
525 	// init the child
526 	child->SetOffset(offset);
527 	child->SetSize(size);
528 	child->SetBlockSize(SECTOR_SIZE);
529 	//child->SetFlags(0);
530 	child->SetChildCookie(primary);
531 
532 	// init the primary partition
533 	bool active = false;
534 		// TODO: Get from parameters!
535 	primary->SetTo(offset, size, type.Type(), active);
536 
537 // TODO: If the child is an extended partition, we should trigger its
538 // initialization.
539 
540 	*_child = child;
541 	return B_OK;
542 }
543 
544