xref: /haiku/src/add-ons/disk_systems/intel/PartitionMapAddOn.cpp (revision d9cebac2b77547b7064f22497514eecd2d047160)
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 // GetPartitioningInfo
249 status_t
250 PartitionMapHandle::GetPartitioningInfo(BPartitioningInfo* info)
251 {
252 	// init to the full size (minus the first sector)
253 	off_t size = Partition()->ContentSize();
254 	status_t error = info->SetTo(SECTOR_SIZE, size - SECTOR_SIZE);
255 	if (error != B_OK)
256 		return error;
257 
258 	// exclude the space of the existing partitions
259 	for (int32 i = 0; i < 4; i++) {
260 		PrimaryPartition* primary = fPartitionMap.PrimaryPartitionAt(i);
261 		if (!primary->IsEmpty()) {
262 			error = info->ExcludeOccupiedSpace(primary->Offset(),
263 				primary->Size());
264 			if (error != B_OK)
265 				return error;
266 		}
267 	}
268 
269 	return B_OK;
270 }
271 
272 
273 // GetChildCreationParameterEditor
274 status_t
275 PartitionMapHandle::GetChildCreationParameterEditor(const char* type,
276 	BDiskDeviceParameterEditor** editor)
277 {
278 	// TODO: We actually need an editor here.
279 	*editor = NULL;
280 	return B_OK;
281 }
282 
283 
284 // ValidateCreateChild
285 status_t
286 PartitionMapHandle::ValidateCreateChild(off_t* _offset, off_t* _size,
287 	const char* typeString, BString* name, const char* parameters)
288 {
289 	// check type
290 	PartitionType type;
291 	if (!type.SetType(typeString) || type.IsEmpty())
292 		return B_BAD_VALUE;
293 	if (type.IsExtended() && fPartitionMap.ExtendedPartitionIndex() >= 0)
294 		return B_BAD_VALUE;
295 
296 	// check name
297 	if (name)
298 		name->Truncate(0);
299 
300 	// check parameters
301 	// TODO:...
302 
303 	// do we have a spare primary partition?
304 	if (fPartitionMap.CountNonEmptyPrimaryPartitions() == 4)
305 		return B_BAD_VALUE;
306 
307 	// check the free space situation
308 	BPartitioningInfo info;
309 	status_t error = GetPartitioningInfo(&info);
310 	if (error != B_OK)
311 		return error;
312 
313 	// any space in the partition at all?
314 	int32 spacesCount = info.CountPartitionableSpaces();
315 	if (spacesCount == 0)
316 		return B_BAD_VALUE;
317 
318 	// check offset and size
319 	off_t offset = sector_align(*_offset);
320 	off_t size = sector_align(*_size);
321 		// TODO: Rather round size up?
322 	off_t end = offset + size;
323 
324 	// get the first partitionable space the requested interval intersects with
325 	int32 spaceIndex = -1;
326 	int32 closestSpaceIndex = -1;
327 	off_t closestSpaceDistance = 0;
328 	for (int32 i = 0; i < spacesCount; i++) {
329 		off_t spaceOffset, spaceSize;
330 		info.GetPartitionableSpaceAt(i, &spaceOffset, &spaceSize);
331 		off_t spaceEnd = spaceOffset + spaceSize;
332 
333 		if (spaceOffset >= offset && spaceOffset < end
334 			|| offset >= spaceOffset && offset < spaceEnd) {
335 			spaceIndex = i;
336 			break;
337 		}
338 
339 		off_t distance;
340 		if (offset < spaceOffset)
341 			distance = spaceOffset - end;
342 		else
343 			distance = spaceEnd - offset;
344 
345 		if (closestSpaceIndex == -1 || distance < closestSpaceDistance) {
346 			closestSpaceIndex = i;
347 			closestSpaceDistance = distance;
348 		}
349 	}
350 
351 	// get the space we found
352 	off_t spaceOffset, spaceSize;
353 	info.GetPartitionableSpaceAt(
354 		spaceIndex >= 0 ? spaceIndex : closestSpaceIndex, &spaceOffset,
355 		&spaceSize);
356 	off_t spaceEnd = spaceOffset + spaceSize;
357 
358 	// If the requested intervald doesn't intersect with any space yet, move
359 	// it, so that it does.
360 	if (spaceIndex < 0) {
361 		spaceIndex = closestSpaceIndex;
362 		if (offset < spaceOffset) {
363 			offset = spaceOffset;
364 			end = offset + size;
365 		} else {
366 			end = spaceEnd;
367 			offset = end - size;
368 		}
369 	}
370 
371 	// move/shrink the interval, so that it fully lies within the space
372 	if (offset < spaceOffset) {
373 		offset = spaceOffset;
374 		end = offset + size;
375 		if (end > spaceEnd) {
376 			end = spaceEnd;
377 			size = end - offset;
378 		}
379 	} else if (end > spaceEnd) {
380 		end = spaceEnd;
381 		offset = end - size;
382 		if (offset < spaceOffset) {
383 			offset = spaceOffset;
384 			size = end - offset;
385 		}
386 	}
387 
388 	*_offset = offset;
389 	*_size = size;
390 
391 	return B_OK;
392 }
393 
394 
395 // CreateChild
396 status_t
397 PartitionMapHandle::CreateChild(off_t offset, off_t size,
398 	const char* typeString, const char* name, const char* parameters,
399 	BMutablePartition** _child)
400 {
401 	// check type
402 	PartitionType type;
403 	if (!type.SetType(typeString) || type.IsEmpty())
404 		return B_BAD_VALUE;
405 	if (type.IsExtended() && fPartitionMap.ExtendedPartitionIndex() >= 0)
406 		return B_BAD_VALUE;
407 
408 	// check name
409 	if (name && strlen(name) > 0)
410 		return B_BAD_VALUE;
411 
412 	// check parameters
413 	// TODO:...
414 
415 	// get a spare primary partition
416 	PrimaryPartition* primary = NULL;
417 	for (int32 i = 0; i < 4; i++) {
418 		if (fPartitionMap.PrimaryPartitionAt(i)->IsEmpty()) {
419 			primary = fPartitionMap.PrimaryPartitionAt(i);
420 			break;
421 		}
422 	}
423 	if (!primary)
424 		return B_BAD_VALUE;
425 
426 	// offset properly aligned?
427 	if (offset != sector_align(offset) || size != sector_align(size))
428 		return B_BAD_VALUE;
429 
430 	// check the free space situation
431 	BPartitioningInfo info;
432 	status_t error = GetPartitioningInfo(&info);
433 	if (error != B_OK)
434 		return error;
435 
436 	bool foundSpace = false;
437 	off_t end = offset + size;
438 	int32 spacesCount = info.CountPartitionableSpaces();
439 	for (int32 i = 0; i < spacesCount; i++) {
440 		off_t spaceOffset, spaceSize;
441 		info.GetPartitionableSpaceAt(i, &spaceOffset, &spaceSize);
442 		off_t spaceEnd = spaceOffset + spaceSize;
443 
444 		if (offset >= spaceOffset && end <= spaceEnd) {
445 			foundSpace = true;
446 			break;
447 		}
448 	}
449 
450 	if (!foundSpace)
451 		return B_BAD_VALUE;
452 
453 	// everything looks good, do it
454 
455 	// create the child
456 	// (Note: the primary partition index is indeed the child index, since
457 	// we picked the first empty primary partition.)
458 	BMutablePartition* partition = Partition();
459 	BMutablePartition* child;
460 	error = partition->CreateChild(primary->Index(), typeString, NULL,
461 		parameters, &child);
462 	if (error != B_OK)
463 		return error;
464 
465 	// init the child
466 	child->SetOffset(offset);
467 	child->SetSize(size);
468 	child->SetBlockSize(SECTOR_SIZE);
469 	//child->SetFlags(0);
470 	child->SetChildCookie(primary);
471 
472 	// init the primary partition
473 	bool active = false;
474 		// TODO: Get from parameters!
475 	primary->SetTo(offset, size, type.Type(), active);
476 
477 // TODO: If the child is an extended partition, we should trigger its
478 // initialization.
479 
480 	*_child = child;
481 	return B_OK;
482 }
483 
484