1 /*
2 * Copyright 2007, Ingo Weinhold, bonefish@users.sf.net.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7 #include "PartitionMapAddOn.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 #include "PrimaryParameterEditor.h"
20
21
22 //#define TRACE_PARTITION_MAP_ADD_ON
23 #undef TRACE
24 #ifdef TRACE_PARTITION_MAP_ADD_ON
25 # define TRACE(x...) printf(x)
26 #else
27 # define TRACE(x...) do {} while (false)
28 #endif
29
30
31 using std::nothrow;
32
33
34 static const uint32 kDiskSystemFlags =
35 0
36 // | B_DISK_SYSTEM_SUPPORTS_CHECKING
37 // | B_DISK_SYSTEM_SUPPORTS_REPAIRING
38 | B_DISK_SYSTEM_SUPPORTS_RESIZING
39 | B_DISK_SYSTEM_SUPPORTS_MOVING
40 // | B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_NAME
41 | B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_PARAMETERS
42 | B_DISK_SYSTEM_SUPPORTS_INITIALIZING
43 // | B_DISK_SYSTEM_SUPPORTS_CONTENT_NAME
44
45 | B_DISK_SYSTEM_SUPPORTS_RESIZING_CHILD
46 | B_DISK_SYSTEM_SUPPORTS_MOVING_CHILD
47 // | B_DISK_SYSTEM_SUPPORTS_SETTING_NAME
48 | B_DISK_SYSTEM_SUPPORTS_SETTING_TYPE
49 // | B_DISK_SYSTEM_SUPPORTS_SETTING_PARAMETERS
50 | B_DISK_SYSTEM_SUPPORTS_CREATING_CHILD
51 | B_DISK_SYSTEM_SUPPORTS_DELETING_CHILD
52 // | B_DISK_SYSTEM_SUPPORTS_NAME
53 ;
54
55
56 // #pragma mark - PartitionMapAddOn
57
58
PartitionMapAddOn()59 PartitionMapAddOn::PartitionMapAddOn()
60 :
61 BDiskSystemAddOn(kPartitionTypeIntel, kDiskSystemFlags)
62 {
63 }
64
65
~PartitionMapAddOn()66 PartitionMapAddOn::~PartitionMapAddOn()
67 {
68 }
69
70
71 status_t
CreatePartitionHandle(BMutablePartition * partition,BPartitionHandle ** _handle)72 PartitionMapAddOn::CreatePartitionHandle(BMutablePartition* partition,
73 BPartitionHandle** _handle)
74 {
75 PartitionMapHandle* handle = new(nothrow) PartitionMapHandle(partition);
76 if (!handle)
77 return B_NO_MEMORY;
78
79 status_t error = handle->Init();
80 if (error != B_OK) {
81 delete handle;
82 return error;
83 }
84
85 *_handle = handle;
86 return B_OK;
87 }
88
89
90 bool
CanInitialize(const BMutablePartition * partition)91 PartitionMapAddOn::CanInitialize(const BMutablePartition* partition)
92 {
93 // If it's big enough, but not too big (ie. larger than 2^32 blocks) we can
94 // initialize it.
95 return partition->Size() >= 2 * partition->BlockSize()
96 && partition->Size() / partition->BlockSize() < UINT32_MAX;
97 }
98
99
100 status_t
ValidateInitialize(const BMutablePartition * partition,BString * name,const char * parameters)101 PartitionMapAddOn::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 PartitionMapAddOn::Initialize(BMutablePartition* partition, const char* name,
119 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 PartitionMapHandle* handle = new(nothrow) PartitionMapHandle(partition);
129 if (!handle)
130 return B_NO_MEMORY;
131 ObjectDeleter<PartitionMapHandle> handleDeleter(handle);
132
133 // init the partition
134 status_t error = partition->SetContentType(Name());
135 if (error != B_OK)
136 return error;
137 // TODO: The content type could as well be set by the caller.
138
139 partition->SetContentName(NULL);
140 partition->SetContentParameters(NULL);
141 partition->SetContentSize(
142 sector_align(partition->Size(), partition->BlockSize()));
143 partition->Changed(B_PARTITION_CHANGED_INITIALIZATION);
144
145 *_handle = handleDeleter.Detach();
146
147 return B_OK;
148 }
149
150
151 // #pragma mark - PartitionMapHandle
152
153
PartitionMapHandle(BMutablePartition * partition)154 PartitionMapHandle::PartitionMapHandle(BMutablePartition* partition)
155 :
156 BPartitionHandle(partition)
157 {
158 }
159
160
~PartitionMapHandle()161 PartitionMapHandle::~PartitionMapHandle()
162 {
163 }
164
165
166 status_t
Init()167 PartitionMapHandle::Init()
168 {
169 // initialize the partition map from the mutable partition
170
171 BMutablePartition* partition = Partition();
172
173 int32 count = partition->CountChildren();
174 if (count > 4)
175 return B_BAD_VALUE;
176
177 int32 extendedCount = 0;
178
179 for (int32 i = 0; i < count; i++) {
180 BMutablePartition* child = partition->ChildAt(i);
181 PartitionType type;
182 if (!type.SetType(child->Type()))
183 return B_BAD_VALUE;
184
185 // only one extended partition is allowed
186 if (type.IsExtended()) {
187 if (++extendedCount > 1)
188 return B_BAD_VALUE;
189 }
190
191 // TODO: Get these from the parameters.
192 int32 index = i;
193 bool active = false;
194
195 PrimaryPartition* primary = fPartitionMap.PrimaryPartitionAt(index);
196 primary->SetTo(child->Offset(), child->Size(), type.Type(), active,
197 partition->BlockSize());
198
199 child->SetChildCookie(primary);
200 }
201
202 // The extended partition (if any) is initialized by
203 // ExtendedPartitionHandle::Init().
204
205 return B_OK;
206 }
207
208
209 uint32
SupportedOperations(uint32 mask)210 PartitionMapHandle::SupportedOperations(uint32 mask)
211 {
212 BMutablePartition* partition = Partition();
213
214 uint32 flags = B_DISK_SYSTEM_SUPPORTS_RESIZING
215 | B_DISK_SYSTEM_SUPPORTS_MOVING
216 | B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_PARAMETERS
217 | B_DISK_SYSTEM_SUPPORTS_INITIALIZING;
218
219 // creating child
220 if ((mask & B_DISK_SYSTEM_SUPPORTS_CREATING_CHILD) != 0) {
221 BPartitioningInfo info;
222 if (partition->CountChildren() < 4
223 && GetPartitioningInfo(&info) == B_OK
224 && info.CountPartitionableSpaces() > 1) {
225 flags |= B_DISK_SYSTEM_SUPPORTS_CREATING_CHILD;
226 }
227 }
228
229 return flags;
230 }
231
232
233 uint32
SupportedChildOperations(const BMutablePartition * child,uint32 mask)234 PartitionMapHandle::SupportedChildOperations(const BMutablePartition* child,
235 uint32 mask)
236 {
237 return B_DISK_SYSTEM_SUPPORTS_RESIZING_CHILD
238 | B_DISK_SYSTEM_SUPPORTS_MOVING_CHILD
239 | B_DISK_SYSTEM_SUPPORTS_SETTING_PARAMETERS
240 | B_DISK_SYSTEM_SUPPORTS_DELETING_CHILD;
241 }
242
243
244 status_t
GetNextSupportedType(const BMutablePartition * child,int32 * cookie,BString * type)245 PartitionMapHandle::GetNextSupportedType(const BMutablePartition* child,
246 int32* cookie, BString* type)
247 {
248 TRACE("%p->PartitionMapHandle::GetNextSupportedType(child: %p, "
249 "cookie: %ld)\n", this, child, *cookie);
250
251 int32 index = *cookie;
252 const partition_type* nextType;
253 while (true) {
254 nextType = fPartitionMap.GetNextSupportedPartitionType(index);
255 if (nextType == NULL)
256 return B_ENTRY_NOT_FOUND;
257 index++;
258 if (nextType->used)
259 break;
260 }
261
262 if (!nextType)
263 return B_ENTRY_NOT_FOUND;
264
265 type->SetTo(nextType->name);
266 *cookie = index;
267
268 return B_OK;
269 }
270
271
272 status_t
GetPartitioningInfo(BPartitioningInfo * info)273 PartitionMapHandle::GetPartitioningInfo(BPartitioningInfo* info)
274 {
275 // init to the full size (minus the first sector)
276 off_t size = Partition()->ContentSize();
277 status_t error = info->SetTo(Partition()->BlockSize(),
278 size - Partition()->BlockSize());
279 if (error != B_OK)
280 return error;
281
282 // exclude the space of the existing partitions
283 for (int32 i = 0; i < 4; i++) {
284 PrimaryPartition* primary = fPartitionMap.PrimaryPartitionAt(i);
285 if (!primary->IsEmpty()) {
286 error = info->ExcludeOccupiedSpace(primary->Offset(),
287 primary->Size());
288 if (error != B_OK)
289 return error;
290 }
291 }
292
293 return B_OK;
294 }
295
296
297 status_t
ValidateSetParameters(const BMutablePartition * child,const char * parameters)298 PartitionMapHandle::ValidateSetParameters(const BMutablePartition* child,
299 const char* parameters)
300 {
301 if (child == NULL || parameters == NULL)
302 return B_NO_INIT;
303
304 void* handle = parse_driver_settings_string(parameters);
305 if (handle == NULL)
306 return B_BAD_DATA;
307
308 unload_driver_settings(handle);
309
310 return B_OK;
311 }
312
313
314 status_t
SetParameters(BMutablePartition * child,const char * parameters)315 PartitionMapHandle::SetParameters(BMutablePartition* child,
316 const char* parameters)
317 {
318 void* handle = parse_driver_settings_string(parameters);
319 if (handle == NULL)
320 return B_BAD_DATA;
321
322 bool active = get_driver_boolean_parameter(handle, "active", false, true);
323 unload_driver_settings(handle);
324
325 // Update our local state
326 PrimaryPartition* partition = (PrimaryPartition*)child->ChildCookie();
327 partition->SetActive(active);
328
329 // Forward the request to the BMutablePartition so it can be committed to
330 // disk
331 return child->SetParameters(parameters);
332 }
333
334
335 status_t
GetParameterEditor(B_PARAMETER_EDITOR_TYPE type,BPartitionParameterEditor ** editor)336 PartitionMapHandle::GetParameterEditor(B_PARAMETER_EDITOR_TYPE type,
337 BPartitionParameterEditor** editor)
338 {
339 *editor = NULL;
340 if (type == B_CREATE_PARAMETER_EDITOR
341 || type == B_PROPERTIES_PARAMETER_EDITOR) {
342 try {
343 *editor = new PrimaryPartitionEditor(type == B_CREATE_PARAMETER_EDITOR);
344 } catch (std::bad_alloc&) {
345 return B_NO_MEMORY;
346 }
347 return B_OK;
348 }
349 return B_NOT_SUPPORTED;
350 }
351
352
353 status_t
ValidateCreateChild(off_t * _offset,off_t * _size,const char * typeString,BString * name,const char * parameters)354 PartitionMapHandle::ValidateCreateChild(off_t* _offset, off_t* _size,
355 const char* typeString, BString* name, const char* parameters)
356 {
357 // check type
358 PartitionType type;
359 if (!type.SetType(typeString) || type.IsEmpty())
360 return B_BAD_TYPE;
361
362 if (type.IsExtended() && fPartitionMap.ExtendedPartitionIndex() >= 0) {
363 // There can only be a single extended partition
364 return B_NAME_IN_USE;
365 }
366
367 // check name
368 if (name)
369 name->Truncate(0);
370
371 // check parameters
372 void* handle = parse_driver_settings_string(parameters);
373 if (handle == NULL)
374 return B_ERROR;
375 get_driver_boolean_parameter(handle, "active", false, true);
376 unload_driver_settings(handle);
377
378 // do we have a spare primary partition?
379 if (fPartitionMap.CountNonEmptyPrimaryPartitions() == 4)
380 return B_BAD_INDEX;
381
382 // check the free space situation
383 BPartitioningInfo info;
384 status_t error = GetPartitioningInfo(&info);
385 if (error != B_OK)
386 return error;
387
388 // any space in the partition at all?
389 int32 spacesCount = info.CountPartitionableSpaces();
390 if (spacesCount == 0)
391 return B_DEVICE_FULL;
392
393 // check offset and size
394 off_t offset = sector_align(*_offset, Partition()->BlockSize());
395 off_t size = sector_align(*_size, Partition()->BlockSize());
396 // TODO: Rather round size up?
397 off_t end = offset + size;
398
399 // get the first partitionable space the requested interval intersects with
400 int32 spaceIndex = -1;
401 int32 closestSpaceIndex = -1;
402 off_t closestSpaceDistance = 0;
403 for (int32 i = 0; i < spacesCount; i++) {
404 off_t spaceOffset, spaceSize;
405 info.GetPartitionableSpaceAt(i, &spaceOffset, &spaceSize);
406 off_t spaceEnd = spaceOffset + spaceSize;
407
408 if ((spaceOffset >= offset && spaceOffset < end)
409 || (offset >= spaceOffset && offset < spaceEnd)) {
410 spaceIndex = i;
411 break;
412 }
413
414 off_t distance;
415 if (offset < spaceOffset)
416 distance = spaceOffset - end;
417 else
418 distance = spaceEnd - offset;
419
420 if (closestSpaceIndex == -1 || distance < closestSpaceDistance) {
421 closestSpaceIndex = i;
422 closestSpaceDistance = distance;
423 }
424 }
425
426 // get the space we found
427 off_t spaceOffset, spaceSize;
428 info.GetPartitionableSpaceAt(
429 spaceIndex >= 0 ? spaceIndex : closestSpaceIndex, &spaceOffset,
430 &spaceSize);
431 off_t spaceEnd = spaceOffset + spaceSize;
432
433 // If the requested intervald doesn't intersect with any space yet, move
434 // it, so that it does.
435 if (spaceIndex < 0) {
436 spaceIndex = closestSpaceIndex;
437 if (offset < spaceOffset) {
438 offset = spaceOffset;
439 end = offset + size;
440 } else {
441 end = spaceEnd;
442 offset = end - size;
443 }
444 }
445
446 // move/shrink the interval, so that it fully lies within the space
447 if (offset < spaceOffset) {
448 offset = spaceOffset;
449 end = offset + size;
450 if (end > spaceEnd) {
451 end = spaceEnd;
452 size = end - offset;
453 }
454 } else if (end > spaceEnd) {
455 end = spaceEnd;
456 offset = end - size;
457 if (offset < spaceOffset) {
458 offset = spaceOffset;
459 size = end - offset;
460 }
461 }
462
463 *_offset = offset;
464 *_size = size;
465
466 return B_OK;
467 }
468
469
470 status_t
CreateChild(off_t offset,off_t size,const char * typeString,const char * name,const char * parameters,BMutablePartition ** _child)471 PartitionMapHandle::CreateChild(off_t offset, off_t size,
472 const char* typeString, const char* name, const char* parameters,
473 BMutablePartition** _child)
474 {
475 // check type
476 PartitionType type;
477 if (!type.SetType(typeString) || type.IsEmpty())
478 return B_BAD_VALUE;
479 if (type.IsExtended() && fPartitionMap.ExtendedPartitionIndex() >= 0)
480 return B_BAD_VALUE;
481
482 // check name
483 if (name && *name != '\0')
484 return B_BAD_VALUE;
485
486 // check parameters
487 void* handle = parse_driver_settings_string(parameters);
488 if (handle == NULL)
489 return B_ERROR;
490
491 bool active = get_driver_boolean_parameter(handle, "active", false, true);
492 unload_driver_settings(handle);
493
494 // get a spare primary partition
495 PrimaryPartition* primary = NULL;
496 for (int32 i = 0; i < 4; i++) {
497 if (fPartitionMap.PrimaryPartitionAt(i)->IsEmpty()) {
498 primary = fPartitionMap.PrimaryPartitionAt(i);
499 break;
500 }
501 }
502 if (!primary)
503 return B_BAD_VALUE;
504
505 // offset properly aligned?
506 if (offset != sector_align(offset, Partition()->BlockSize())
507 || size != sector_align(size, Partition()->BlockSize()))
508 return B_BAD_VALUE;
509
510 // check the free space situation
511 BPartitioningInfo info;
512 status_t error = GetPartitioningInfo(&info);
513 if (error != B_OK)
514 return error;
515
516 bool foundSpace = false;
517 off_t end = offset + size;
518 int32 spacesCount = info.CountPartitionableSpaces();
519 for (int32 i = 0; i < spacesCount; i++) {
520 off_t spaceOffset, spaceSize;
521 info.GetPartitionableSpaceAt(i, &spaceOffset, &spaceSize);
522 off_t spaceEnd = spaceOffset + spaceSize;
523
524 if (offset >= spaceOffset && end <= spaceEnd) {
525 foundSpace = true;
526 break;
527 }
528 }
529
530 if (!foundSpace)
531 return B_BAD_VALUE;
532
533 // create the child
534 // (Note: the primary partition index is indeed the child index, since
535 // we picked the first empty primary partition.)
536 BMutablePartition* partition = Partition();
537 BMutablePartition* child;
538 error = partition->CreateChild(primary->Index(), typeString, name,
539 parameters, &child);
540 if (error != B_OK)
541 return error;
542
543 // init the child
544 child->SetOffset(offset);
545 child->SetSize(size);
546 child->SetBlockSize(partition->BlockSize());
547 //child->SetFlags(0);
548 child->SetChildCookie(primary);
549
550 // init the primary partition
551 primary->SetTo(offset, size, type.Type(), active, partition->BlockSize());
552
553 *_child = child;
554 return B_OK;
555 }
556
557
558 status_t
DeleteChild(BMutablePartition * child)559 PartitionMapHandle::DeleteChild(BMutablePartition* child)
560 {
561 BMutablePartition* parent = child->Parent();
562 status_t error = parent->DeleteChild(child);
563
564 return error;
565 }
566