* Copyright 2003-2011, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Ingo Weinhold, ingo_weinhold@gmx.de
* Tomas Kucera, kucerat@centrum.cz
*/
#include "write_support.h"
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <new>
#include <DiskDeviceTypes.h>
#include <KernelExport.h>
#include <AutoDeleter.h>
#include "intel.h"
#include "PartitionLocker.h"
#include "PartitionMap.h"
#include "PartitionMapParser.h"
#include "PartitionMapWriter.h"
#define TRACE(x) dprintf x
static const int32 MAX_MOVE_BUFFER = 2 * 1024 * 4;
static const uint32 FREE_SECTORS_AFTER_PTS = 63;
static const uint32 FREE_SECTORS_AFTER_MBR = 63;
static const uint32 PTS_OFFSET = FREE_SECTORS_AFTER_PTS + 1;
static const uint32 MBR_OFFSET = FREE_SECTORS_AFTER_MBR + 1;
typedef partitionable_space_data PartitionPosition;
typedef void (*fc_get_sibling_partitions)(partition_data* partition,
partition_data* child, off_t childOffset, partition_data** prec,
partition_data** follow, off_t* prec_offset, off_t* prec_size,
off_t* follow_offset, off_t* follow_size);
typedef int32 (*fc_fill_partitionable_spaces_buffer)(partition_data* partition,
PartitionPosition* positions);
status_t pm_get_partitionable_spaces(partition_data* partition,
partitionable_space_data* buffer, int32 count, int32* actualCount);
status_t ep_get_partitionable_spaces(partition_data* partition,
partitionable_space_data* buffer, int32 count, int32* actualCount);
uint32
pm_get_supported_operations(partition_data* partition, uint32 mask)
{
uint32 flags = B_DISK_SYSTEM_SUPPORTS_RESIZING
| B_DISK_SYSTEM_SUPPORTS_MOVING
| B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_PARAMETERS
| B_DISK_SYSTEM_SUPPORTS_INITIALIZING;
int32 countSpaces = 0;
if (partition->child_count < 4
&& pm_get_partitionable_spaces(partition, NULL, 0, &countSpaces)
== B_BUFFER_OVERFLOW
&& countSpaces > 0) {
flags |= B_DISK_SYSTEM_SUPPORTS_CREATING_CHILD;
}
return flags;
}
uint32
pm_get_supported_child_operations(partition_data* partition,
partition_data* child, uint32 mask)
{
return B_DISK_SYSTEM_SUPPORTS_RESIZING_CHILD
| B_DISK_SYSTEM_SUPPORTS_MOVING_CHILD
| B_DISK_SYSTEM_SUPPORTS_SETTING_TYPE
| B_DISK_SYSTEM_SUPPORTS_DELETING_CHILD;
}
bool
pm_is_sub_system_for(partition_data* partition)
{
return false;
}
bool
get_partition_from_offset_ep(partition_data* partition, off_t offset,
partition_data** nextPartition)
{
for (int32 i = 0; i < partition->child_count; i++) {
partition_data* sibling = get_child_partition(partition->id, i);
if (sibling != NULL && sibling->offset == offset) {
*nextPartition = sibling;
return true;
}
}
return false;
}
static inline off_t
sector_align(off_t offset, int32 blockSize)
{
return offset / blockSize * blockSize;
}
static inline off_t
sector_align_up(off_t offset, int32 blockSize)
{
return (offset + blockSize - 1) / blockSize * blockSize;
}
static bool
validate_resize(partition_data* partition, off_t* size)
{
off_t newSize = *size;
if (newSize == partition->size)
return true;
if (newSize < 0)
newSize = 0;
else
newSize = sector_align(newSize, partition->block_size);
if (newSize > partition->size) {
*size = newSize;
return true;
}
off_t currentEnd = partition->offset + newSize;
for (int32 i = 0; i < partition->child_count; i++) {
partition_data* child = get_child_partition(partition->id, i);
if (child && child->offset + child->size > currentEnd)
currentEnd = child->offset + child->size;
}
newSize = currentEnd - partition->offset;
newSize = sector_align_up(newSize, partition->block_size);
*size = newSize;
return true;
}
bool
pm_validate_resize(partition_data* partition, off_t* size)
{
TRACE(("intel: pm_validate_resize\n"));
if (!partition || !size)
return false;
return validate_resize(partition, size);
}
according to childOffset returns previous and next sibling or NULL
precious, next output parameters
partition - Intel Partition Map
*/
static void
get_sibling_partitions_pm(partition_data* partition,
partition_data* child, off_t childOffset, partition_data** previous,
partition_data** next, off_t* previousOffset, off_t* previousSize,
off_t* nextOffset, off_t* nextSize)
{
partition_data* previousSibling = NULL;
partition_data* nextSibling = NULL;
for (int32 i = 0; i < partition->child_count; i++) {
partition_data* sibling = get_child_partition(partition->id, i);
if (sibling && sibling != child) {
if (sibling->offset <= childOffset) {
if (!previousSibling || previousSibling->offset < sibling->offset)
previousSibling = sibling;
} else {
if (!nextSibling || nextSibling->offset > sibling->offset)
nextSibling = sibling;
}
}
}
*previous = previousSibling;
*next = nextSibling;
if (previousSibling) {
*previousOffset = previousSibling->offset;
*previousSize = previousSibling->size;
}
if (nextSibling) {
*nextOffset = nextSibling->offset;
*nextSize = nextSibling->size;
}
}
according to childOffset returns previous and next sibling or NULL
previous, next output parameters
partition - Intel Extended Partition
*/
static void
get_sibling_partitions_ep(partition_data* partition,
partition_data* child, off_t childOffset, partition_data** previous,
partition_data** next, off_t* previousOffset, off_t* previousSize,
off_t* nextOffset, off_t* nextSize)
{
partition_data* previousSibling = NULL;
partition_data* nextSibling = NULL;
for (int32 i = 0; i < partition->child_count; i++) {
partition_data* sibling = get_child_partition(partition->id, i);
if (sibling && sibling != child) {
if (sibling->offset <= childOffset) {
if (!previousSibling || previousSibling->offset < sibling->offset)
previousSibling = sibling;
} else {
if (!nextSibling || nextSibling->offset > sibling->offset)
nextSibling = sibling;
}
}
}
*previous = previousSibling;
*next = nextSibling;
if (previousSibling) {
*previousOffset = previousSibling->offset;
*previousSize = previousSibling->size;
}
if (nextSibling) {
*nextOffset = nextSibling->offset;
*nextSize = nextSibling->size;
}
}
static bool
validate_resize_child(partition_data* partition, partition_data* child,
off_t childOffset, off_t childSize, off_t* size,
fc_get_sibling_partitions getSiblingPartitions)
{
if (*size == childSize)
return true;
if (*size < childSize) {
if (*size < 0)
*size = 0;
*size = sector_align(*size, partition->block_size);
return true;
}
if (childOffset + *size > partition->offset + partition->size)
*size = partition->offset + partition->size - childOffset;
partition_data* previousSibling = NULL;
partition_data* nextSibling = NULL;
off_t previousOffset = 0, previousSize = 0, nextOffset = 0, nextSize = 0;
getSiblingPartitions(partition, child, childOffset, &previousSibling,
&nextSibling, &previousOffset, &previousSize, &nextOffset, &nextSize);
if (nextSibling && (nextOffset < childOffset + *size))
*size = nextOffset - childOffset;
*size = sector_align(*size, partition->block_size);
return true;
}
bool
pm_validate_resize_child(partition_data* partition, partition_data* child,
off_t* size)
{
TRACE(("intel: pm_validate_resize_child\n"));
if (!partition || !child || !size)
return false;
return validate_resize_child(partition, child, child->offset,
child->size, size, get_sibling_partitions_pm);
}
bool
pm_validate_move(partition_data* partition, off_t* start)
{
TRACE(("intel: pm_validate_move\n"));
if (!partition || !start)
return false;
return true;
}
static bool
validate_move_child(partition_data* partition, partition_data* child,
off_t childOffset, off_t childSize, off_t* _start,
fc_get_sibling_partitions getSiblingPartitions)
{
off_t start = *_start;
if (start < 0)
start = 0;
else if (start + childSize > partition->size)
start = partition->size - childSize;
start = sector_align(start, partition->block_size);
partition_data* previousSibling = NULL;
partition_data* nextSibling = NULL;
off_t previousOffset = 0, previousSize = 0, nextOffset = 0, nextSize = 0;
getSiblingPartitions(partition, child, childOffset, &previousSibling,
&nextSibling, &previousOffset, &previousSize, &nextOffset, &nextSize);
if (start < childOffset) {
if (previousSibling && previousOffset + previousSize > start) {
start = previousOffset + previousSize;
start = sector_align_up(start, partition->block_size);
}
} else {
if (nextSibling && nextOffset < start + childSize) {
start = nextOffset - childSize;
start = sector_align(start, partition->block_size);
}
}
*_start = start;
return true;
}
bool
pm_validate_move_child(partition_data* partition, partition_data* child,
off_t* start)
{
TRACE(("intel: pm_validate_move_child\n"));
if (!partition || !child || !start)
return false;
if (*start == child->offset)
return true;
return validate_move_child(partition, child, child->offset,
child->size, start, get_sibling_partitions_pm);
}
type has to be known, only one extended partition is allowed
partition - intel partition map
child can be NULL
*/
static bool
is_type_valid_pm(const char* type, partition_data* partition,
PrimaryPartition* child = NULL)
{
PartitionType ptype;
ptype.SetType(type);
if (!ptype.IsValid() || ptype.IsEmpty())
return false;
if (ptype.IsExtended()) {
PartitionMap* map = (PartitionMap*)partition->content_cookie;
if (!map)
return false;
for (int32 i = 0; i < partition->child_count; i++) {
PrimaryPartition* primary = map->PrimaryPartitionAt(i);
if (primary && primary->IsExtended() && primary != child)
return false;
}
}
return true;
}
bool
pm_validate_set_type(partition_data* partition, const char* type)
{
TRACE(("intel: pm_validate_set_type\n"));
if (!partition || !type)
return false;
partition_data* parent = get_parent_partition(partition->id);
if (!parent)
return false;
PrimaryPartition* child = (PrimaryPartition*)partition->cookie;
if (!child)
return false;
return is_type_valid_pm(type, parent, child);
}
bool
pm_validate_initialize(partition_data* partition, char* name,
const char* parameters)
{
TRACE(("intel: pm_validate_initialize\n"));
if (!partition || !(pm_get_supported_operations(partition)
& B_DISK_SYSTEM_SUPPORTS_INITIALIZING)) {
return false;
}
if (name)
name[0] = '\0';
return true;
}
static bool
validate_create_child_partition(partition_data* partition, off_t* start,
off_t* size, fc_get_sibling_partitions getSiblingPartitions)
{
*start = sector_align(*start, partition->block_size);
if (*size < 0)
*size = 0;
else
*size = sector_align(*size, partition->block_size);
if (*start >= partition->offset + partition->size)
return false;
if (*start + *size > partition->offset + partition->size)
*size = partition->offset + partition->size - *start;
partition_data* previousSibling = NULL;
partition_data* nextSibling = NULL;
off_t previousOffset = 0, previousSize = 0, nextOffset = 0, nextSize = 0;
getSiblingPartitions(partition, NULL, *start, &previousSibling,
&nextSibling, &previousOffset, &previousSize, &nextOffset, &nextSize);
if (previousSibling && (previousOffset + previousSize > *start)) {
*start = previousOffset + previousSize;
*start = sector_align_up(*start, partition->block_size);
}
if (nextSibling && (nextOffset < *start + *size))
*size = nextOffset - *start;
*size = sector_align(*size, partition->block_size);
if (*size == 0)
return false;
return true;
}
index - returns position of the new partition (first free record in MBR)
*/
bool
pm_validate_create_child(partition_data* partition, off_t* start, off_t* size,
const char* type, const char* name, const char* parameters, int32* index)
{
TRACE(("intel: pm_validate_create_child\n"));
if (!partition || !(pm_get_supported_operations(partition)
& B_DISK_SYSTEM_SUPPORTS_CREATING_CHILD)
|| !start || !size || !type || !index) {
return false;
}
if (!is_type_valid_pm(type, partition))
return false;
PartitionMap* map = (PartitionMap*)partition->content_cookie;
if (!map)
return false;
int32 newIndex = -1;
for (int32 i = 0; i < 4; i++) {
PrimaryPartition* primary = map->PrimaryPartitionAt(i);
if (primary->IsEmpty()) {
newIndex = i;
break;
}
}
if (newIndex < 0)
return false;
*index = newIndex;
if (*start < partition->offset + MBR_OFFSET * partition->block_size) {
*start = partition->offset + MBR_OFFSET * partition->block_size;
*start = sector_align_up(*start, partition->block_size);
}
return validate_create_child_partition(partition, start, size,
get_sibling_partitions_pm);
}
static int
cmp_partition_position(const void* o1, const void* o2)
{
off_t offset1 = ((PartitionPosition*)o1)->offset;
off_t offset2 = ((PartitionPosition*)o2)->offset;
if (offset1 < offset2)
return -1;
if (offset1 > offset2)
return 1;
return 0;
}
positions - output buffer with sufficient size
returns partition count
*/
static int32
fill_partitionable_spaces_buffer_pm(partition_data* partition,
PartitionPosition* positions)
{
int32 partition_count = 0;
for (int32 i = 0; i < partition->child_count; i++) {
const partition_data* child = get_child_partition(partition->id, i);
if (child) {
positions[partition_count].offset = child->offset;
positions[partition_count].size = child->size;
partition_count++;
}
}
return partition_count;
}
positions - output buffer with sufficient size
returns partition count
*/
static int32
fill_partitionable_spaces_buffer_ep(partition_data* partition,
PartitionPosition* positions)
{
int32 partition_count = 0;
for (int32 i = 0; i < partition->child_count; i++) {
const partition_data* child = get_child_partition(partition->id, i);
if (child) {
positions[partition_count].offset = child->offset;
positions[partition_count].size = child->size;
partition_count++;
}
}
return partition_count;
}
static status_t
get_partitionable_spaces(partition_data* partition,
partitionable_space_data* buffer, int32 count, int32* _actualCount,
fc_fill_partitionable_spaces_buffer fillBuffer, off_t startOffset,
off_t limitSize = 0, off_t headerSize = 0)
{
PartitionPosition* positions
= new(nothrow) PartitionPosition[partition->child_count];
if (!positions)
return B_NO_MEMORY;
int32 partition_count = fillBuffer(partition, positions);
qsort(positions, partition_count, sizeof(PartitionPosition),
cmp_partition_position);
off_t offset = startOffset + headerSize;
off_t size = 0;
int32 actualCount = 0;
offset = sector_align_up(offset, partition->block_size);
for (int32 i = 0; i < partition_count; i++) {
size = positions[i].offset - offset;
size = sector_align(size, partition->block_size);
if (size >= limitSize) {
if (actualCount < count) {
buffer[actualCount].offset = offset;
buffer[actualCount].size = size;
}
actualCount++;
}
offset = positions[i].offset + positions[i].size + headerSize;
offset = sector_align_up(offset, partition->block_size);
}
size = partition->offset + partition->size - offset;
size = sector_align(size, partition->block_size);
if (size > 0) {
if (actualCount < count) {
buffer[actualCount].offset = offset;
buffer[actualCount].size = size;
}
actualCount++;
}
if (positions)
delete[] positions;
TRACE(("intel: get_partitionable_spaces - found: %" B_PRId32 "\n",
actualCount));
*_actualCount = actualCount;
if (count < actualCount)
return B_BUFFER_OVERFLOW;
return B_OK;
}
status_t
pm_get_partitionable_spaces(partition_data* partition,
partitionable_space_data* buffer, int32 count, int32* actualCount)
{
TRACE(("intel: pm_get_partitionable_spaces\n"));
if (!partition || !partition->content_type
|| strcmp(partition->content_type, kPartitionTypeIntel)
|| !actualCount) {
return B_BAD_VALUE;
}
if (count > 0 && !buffer)
return B_BAD_VALUE;
return get_partitionable_spaces(partition, buffer, count, actualCount,
fill_partitionable_spaces_buffer_pm, MBR_OFFSET * partition->block_size,
0, 0);
}
status_t
pm_get_next_supported_type(partition_data* partition, int32* cookie,
char* _type)
{
TRACE(("intel: pm_get_next_supported_type\n"));
if (!partition || !partition->content_type
|| strcmp(partition->content_type, kPartitionTypeIntel)
|| !cookie || !_type) {
return B_BAD_VALUE;
}
if (*cookie > 255)
return B_ENTRY_NOT_FOUND;
if (*cookie < 1)
*cookie = 1;
uint8 type = *cookie;
PartitionType ptype;
ptype.SetType(type);
if (!ptype.IsValid())
return B_ENTRY_NOT_FOUND;
ptype.GetTypeString(_type);
if (ptype.FindNext())
*cookie = ptype.Type();
else
*cookie = 256;
return B_OK;
}
status_t
pm_shadow_changed(partition_data* partition, partition_data* child,
uint32 operation)
{
TRACE(("intel: pm_shadow_changed(%p, %p, %" B_PRIu32 ")\n", partition,
child, operation));
switch (operation) {
case B_PARTITION_SHADOW:
{
partition_data* physicalPartition = get_partition(
partition->id);
if (!physicalPartition) {
dprintf("intel: pm_shadow_changed(B_PARTITION_SHADOW): no "
"physical partition with ID %" B_PRId32 "\n",
partition->id);
return B_ERROR;
}
if (!physicalPartition->content_cookie) {
dprintf("intel: pm_shadow_changed(B_PARTITION_SHADOW): no "
"content cookie, physical partition: %" B_PRId32 "\n",
partition->id);
return B_ERROR;
}
PartitionMapCookie* map = new(nothrow) PartitionMapCookie;
if (!map)
return B_NO_MEMORY;
status_t error = map->Assign(
*(PartitionMapCookie*)physicalPartition->content_cookie);
if (error != B_OK) {
delete map;
return error;
}
partition->content_cookie = map;
return B_OK;
}
case B_PARTITION_SHADOW_CHILD:
{
partition_data* physical = get_partition(child->id);
if (!physical) {
dprintf("intel: pm_shadow_changed(B_PARTITION_SHADOW_CHILD): "
"no physical partition with ID %" B_PRId32 "\n", child->id);
return B_ERROR;
}
if (!physical->cookie) {
dprintf("intel: pm_shadow_changed(B_PARTITION_SHADOW_CHILD): "
"no cookie, physical partition: %" B_PRId32 "\n",
child->id);
return B_ERROR;
}
int32 index = ((PrimaryPartition*)physical->cookie)->Index();
if (!partition->content_cookie) {
dprintf("intel: pm_shadow_changed(B_PARTITION_SHADOW_CHILD): "
"no content cookie, physical partition: %" B_PRId32 "\n",
partition->id);
return B_ERROR;
}
PartitionMapCookie* map
= ((PartitionMapCookie*)partition->content_cookie);
PrimaryPartition* primary = map->PrimaryPartitionAt(index);
if (!primary || primary->IsEmpty()) {
dprintf("intel: pm_shadow_changed(B_PARTITION_SHADOW_CHILD): "
"partition %" B_PRId32 " is empty, primary index: "
"%" B_PRId32 "\n", child->id, index);
return B_BAD_VALUE;
}
child->cookie = primary;
return B_OK;
}
case B_PARTITION_INITIALIZE:
{
PartitionMapCookie* map = new(nothrow) PartitionMapCookie;
if (!map)
return B_NO_MEMORY;
partition->content_cookie = map;
return B_OK;
}
case B_PARTITION_CREATE_CHILD:
{
if (!partition->content_cookie) {
dprintf("intel: pm_shadow_changed(B_PARTITION_CREATE_CHILD): "
"no content cookie, partition: %" B_PRId32 "\n",
partition->id);
return B_ERROR;
}
PartitionMapCookie* map
= ((PartitionMapCookie*)partition->content_cookie);
PrimaryPartition* primary = NULL;
for (int32 i = 0; i < 4; i++) {
if (map->PrimaryPartitionAt(i)->IsEmpty()) {
primary = map->PrimaryPartitionAt(i);
break;
}
}
if (!primary) {
dprintf("intel: pm_shadow_changed(B_PARTITION_CREATE_CHILD): "
"no empty primary slot, partition: %" B_PRId32 "\n",
partition->id);
return B_ERROR;
}
PartitionType type;
type.SetType(child->type);
if (!type.IsValid()) {
dprintf("intel: pm_shadow_changed(B_PARTITION_CREATE_CHILD): "
"invalid partition type, partition: %" B_PRId32 "\n",
partition->id);
return B_ERROR;
}
primary->SetType(type.Type());
child->cookie = primary;
return B_OK;
}
case B_PARTITION_DEFRAGMENT:
case B_PARTITION_REPAIR:
case B_PARTITION_RESIZE:
case B_PARTITION_RESIZE_CHILD:
case B_PARTITION_MOVE:
case B_PARTITION_MOVE_CHILD:
case B_PARTITION_SET_NAME:
case B_PARTITION_SET_CONTENT_NAME:
case B_PARTITION_SET_TYPE:
case B_PARTITION_SET_PARAMETERS:
case B_PARTITION_SET_CONTENT_PARAMETERS:
case B_PARTITION_DELETE_CHILD:
break;
}
return B_ERROR;
}
status_t
pm_resize(int fd, partition_id partitionID, off_t size, disk_job_id job)
{
TRACE(("intel: pm_resize\n"));
if (fd < 0)
return B_ERROR;
PartitionWriteLocker locker(partitionID);
if (!locker.IsLocked())
return B_ERROR;
partition_data* partition = get_partition(partitionID);
if (!partition)
return B_BAD_VALUE;
off_t validatedSize = size;
if (!pm_validate_resize(partition, &validatedSize))
return B_BAD_VALUE;
update_disk_device_job_progress(job, 0.0);
partition->size = validatedSize;
partition->content_size = validatedSize;
update_disk_device_job_progress(job, 1.0);
partition_modified(partitionID);
return B_OK;
}
status_t
pm_resize_child(int fd, partition_id partitionID, off_t size, disk_job_id job)
{
TRACE(("intel: pm_resize_child\n"));
if (fd < 0)
return B_ERROR;
PartitionWriteLocker locker(partitionID);
if (!locker.IsLocked())
return B_ERROR;
partition_data* partition = get_parent_partition(partitionID);
partition_data* child = get_partition(partitionID);
if (!partition || !child)
return B_BAD_VALUE;
PartitionMap* map = (PartitionMap*)partition->content_cookie;
PrimaryPartition* primary = (PrimaryPartition*)child->cookie;
if (!map || !primary)
return B_BAD_VALUE;
off_t validatedSize = size;
if (!pm_validate_resize_child(partition, child, &validatedSize))
return B_BAD_VALUE;
if (child->size == validatedSize)
return B_OK;
update_disk_device_job_progress(job, 0.0);
primary->SetSize(validatedSize);
PartitionMapWriter writer(fd, primary->BlockSize());
status_t error = writer.WriteMBR(map, false);
if (error != B_OK) {
primary->SetSize(child->size);
return error;
}
child->size = validatedSize;
update_disk_device_job_progress(job, 1.0);
partition_modified(partitionID);
return B_OK;
}
status_t
pm_move(int fd, partition_id partitionID, off_t offset, disk_job_id job)
{
TRACE(("intel: pm_move\n"));
if (fd < 0)
return B_ERROR;
PartitionWriteLocker locker(partitionID);
if (!locker.IsLocked())
return B_ERROR;
partition_data* partition = get_partition(partitionID);
if (!partition)
return B_BAD_VALUE;
if (!pm_validate_move(partition, &offset))
return B_BAD_VALUE;
return B_OK;
}
tries to allocate buffer with the size: blockSize * tryAlloc
if it's not possible, tries smaller buffer (until the size: blockSize * 1)
returns pointer to the buffer (it's size is: blockSize * allocated)
or returns NULL - B_NO_MEMORY
*/
static uint8*
allocate_buffer(uint32 blockSize, int32 tryAlloc, int32* allocated)
{
uint8* buffer = NULL;
for (int32 i = tryAlloc; i > 1; i /= 2) {
buffer = new(nothrow) uint8[i * blockSize];
if (buffer) {
*allocated = i;
return buffer;
}
}
*allocated = 0;
return NULL;
}
static status_t
move_block(int fd, off_t fromOffset, off_t toOffset, uint8* buffer, int32 size)
{
status_t error = B_OK;
if (read_pos(fd, fromOffset, buffer, size) != size) {
error = errno;
if (error == B_OK)
error = B_IO_ERROR;
TRACE(("intel: move_block(): reading failed: %" B_PRIx32 "\n", error));
return error;
}
if (write_pos(fd, toOffset, buffer, size) != size) {
error = errno;
if (error == B_OK)
error = B_IO_ERROR;
TRACE(("intel: move_block(): writing failed: %" B_PRIx32 "\n", error));
}
return error;
}
static status_t
move_partition(int fd, off_t fromOffset, off_t toOffset, off_t size,
uint8* buffer, int32 buffer_size, disk_job_id job)
{
status_t error = B_OK;
off_t cycleCount = size / buffer_size;
int32 remainingSize = size - cycleCount * buffer_size;
update_disk_device_job_progress(job, 0.0);
for (off_t i = 0; i < cycleCount; i++) {
error = move_block(fd, fromOffset, toOffset, buffer, buffer_size);
if (error != B_OK)
return error;
fromOffset += buffer_size;
toOffset += buffer_size;
update_disk_device_job_progress(job, (float)i / cycleCount);
}
if (remainingSize)
error = move_block(fd, fromOffset, toOffset, buffer, remainingSize);
update_disk_device_job_progress(job, 1.0);
return error;
}
status_t
pm_move_child(int fd, partition_id partitionID, partition_id childID,
off_t offset, disk_job_id job)
{
TRACE(("intel: pm_move_child\n"));
if (fd < 0)
return B_ERROR;
PartitionWriteLocker locker(partitionID);
if (!locker.IsLocked())
return B_ERROR;
partition_data* partition = get_partition(partitionID);
partition_data* child = get_partition(childID);
if (!partition || !child)
return B_BAD_VALUE;
PartitionMap* map = (PartitionMap*)partition->content_cookie;
PrimaryPartition* primary = (PrimaryPartition*)child->cookie;
if (!map || !primary)
return B_BAD_VALUE;
off_t validatedOffset = offset;
if (!pm_validate_move_child(partition, child, &validatedOffset))
return B_BAD_VALUE;
if (child->offset == validatedOffset)
return B_OK;
int32 allocated;
uint8* buffer = allocate_buffer(partition->block_size, MAX_MOVE_BUFFER,
&allocated);
if (!buffer)
return B_NO_MEMORY;
update_disk_device_job_progress(job, 0.0);
status_t error = B_OK;
error = move_partition(fd, child->offset, validatedOffset, child->size,
buffer, allocated * partition->block_size, job);
delete[] buffer;
if (error != B_OK)
return error;
child->offset = validatedOffset;
primary->SetOffset(validatedOffset);
PartitionMapWriter writer(fd, partition->block_size);
error = writer.WriteMBR(map, false);
if (error != B_OK)
return error;
update_disk_device_job_progress(job, 1.0);
partition_modified(childID);
return B_OK;
}
status_t
pm_set_type(int fd, partition_id partitionID, const char* type, disk_job_id job)
{
TRACE(("intel: pm_set_type\n"));
if (fd < 0 || !type)
return B_BAD_VALUE;
PartitionWriteLocker locker(partitionID);
if (!locker.IsLocked())
return B_ERROR;
partition_data* partition = get_parent_partition(partitionID);
partition_data* child = get_partition(partitionID);
if (!partition || !child)
return B_BAD_VALUE;
PartitionMap* map = (PartitionMap*)partition->content_cookie;
PrimaryPartition* primary = (PrimaryPartition*)child->cookie;
if (!map || !primary)
return B_BAD_VALUE;
if (!pm_validate_set_type(child, type))
return B_BAD_VALUE;
if (child->type && !strcmp(type, child->type))
return B_OK;
PartitionType ptype;
ptype.SetType(type);
if (!ptype.IsValid() || ptype.IsEmpty())
return false;
update_disk_device_job_progress(job, 0.0);
uint8 oldType = primary->Type();
primary->SetType(ptype.Type());
PartitionMapWriter writer(fd, primary->BlockSize());
status_t error = writer.WriteMBR(map, false);
if (error != B_OK) {
primary->SetType(oldType);
return error;
}
free(child->type);
child->type = strdup(type);
if (!child->type)
return B_NO_MEMORY;
update_disk_device_job_progress(job, 1.0);
partition_modified(partitionID);
return B_OK;
}
status_t
pm_set_parameters(int fd, partition_id partitionID, const char* parameters,
disk_job_id job)
{
TRACE(("intel: pm_set_parameters\n"));
if (fd < 0)
return B_BAD_VALUE;
if (parameters == NULL)
return B_OK;
PartitionWriteLocker locker(partitionID);
if (!locker.IsLocked())
return B_ERROR;
partition_data* partition = get_parent_partition(partitionID);
partition_data* child = get_partition(partitionID);
if (partition == NULL || child == NULL)
return B_BAD_VALUE;
PartitionMap* map = (PartitionMap*)partition->content_cookie;
PrimaryPartition* primary = (PrimaryPartition*)child->cookie;
if (map ==NULL || primary == NULL)
return B_BAD_VALUE;
void* handle = parse_driver_settings_string(parameters);
if (handle == NULL)
return B_ERROR;
bool active = get_driver_boolean_parameter(handle, "active", false, true);
unload_driver_settings(handle);
if (primary->Active() == active) {
TRACE(("intel: pm_set_parameters: no changes required.\n"));
return B_OK;
}
update_disk_device_job_progress(job, 0.0);
if (active) {
for (int i = 0; i < 4; i++) {
PrimaryPartition* partition = map->PrimaryPartitionAt(i);
partition->SetActive(false);
}
}
bool oldActive = primary->Active();
primary->SetActive(active);
PartitionMapWriter writer(fd, primary->BlockSize());
status_t error = writer.WriteMBR(map, false);
if (error != B_OK) {
TRACE(("intel: pm_set_parameters: Failed to rewrite MBR: %s\n",
strerror(error)));
primary->SetType(oldActive);
return error;
}
update_disk_device_job_progress(job, 1.0);
partition_modified(partitionID);
return B_OK;
}
status_t
pm_initialize(int fd, partition_id partitionID, const char* name,
const char* parameters, off_t partitionSize, disk_job_id job)
{
TRACE(("intel: pm_initialize\n"));
if (fd < 0)
return B_ERROR;
PartitionWriteLocker locker(partitionID);
if (!locker.IsLocked())
return B_ERROR;
partition_data* partition = get_partition(partitionID);
if (!partition)
return B_BAD_VALUE;
update_disk_device_job_progress(job, 0.0);
PartitionMap map;
PartitionMapWriter writer(fd, partition->block_size);
status_t error = writer.WriteMBR(&map, true);
if (error != B_OK)
return error;
error = scan_partition(partitionID);
if (error != B_OK)
return error;
update_disk_device_job_progress(job, 1.0);
partition_modified(partitionID);
return B_OK;
}
status_t
pm_uninitialize(int fd, partition_id partitionID, off_t partitionSize,
uint32 blockSize, disk_job_id job)
{
if (blockSize == 0)
return B_BAD_VALUE;
void* block = malloc(blockSize);
if (block == NULL)
return B_NO_MEMORY;
MemoryDeleter blockDeleter(block);
memset(block, 0, blockSize);
if (write_pos(fd, 0, block, blockSize) < 0)
return errno;
update_disk_device_job_progress(job, 1.0);
return B_OK;
}
parameter -- -1 to be ignored
*/
status_t
pm_create_child(int fd, partition_id partitionID, off_t offset, off_t size,
const char* type, const char* name, const char* parameters,
disk_job_id job, partition_id* childID)
{
TRACE(("intel: pm_create_child\n"));
if (fd < 0 || !childID)
return B_BAD_VALUE;
PartitionWriteLocker locker(partitionID);
if (!locker.IsLocked())
return B_ERROR;
partition_data* partition = get_partition(partitionID);
if (!partition)
return B_BAD_VALUE;
PartitionMap* map = (PartitionMap*)partition->content_cookie;
if (!map)
return B_BAD_VALUE;
off_t validatedOffset = offset;
off_t validatedSize = size;
int32 index = 0;
if (!pm_validate_create_child(partition, &validatedOffset, &validatedSize,
type, name, parameters, &index)) {
return B_BAD_VALUE;
}
PrimaryPartition* primary = map->PrimaryPartitionAt(index);
if (!primary->IsEmpty())
return B_BAD_DATA;
update_disk_device_job_progress(job, 0.0);
partition_data* child = create_child_partition(partition->id, index,
validatedOffset, validatedSize, *childID);
if (!child)
return B_ERROR;
PartitionType ptype;
ptype.SetType(type);
void* handle = parse_driver_settings_string(parameters);
if (handle == NULL)
return B_ERROR;
bool active = get_driver_boolean_parameter(handle, "active", false, true);
unload_driver_settings(handle);
if (active) {
for (int i = 0; i < 4; i++) {
PrimaryPartition* partition = map->PrimaryPartitionAt(i);
partition->SetActive(false);
}
}
primary->SetPartitionTableOffset(0);
primary->SetOffset(validatedOffset);
primary->SetSize(validatedSize);
primary->SetType(ptype.Type());
primary->SetActive(active);
PartitionMapWriter writer(fd, primary->BlockSize());
status_t error = writer.WriteMBR(map, false);
if (error != B_OK) {
primary->Unset();
delete_partition(child->id);
return error;
}
*childID = child->id;
child->block_size = primary->BlockSize();
child->type = strdup(type);
child->parameters = strdup(parameters);
child->cookie = primary;
if (!child->type || !child->parameters)
return B_NO_MEMORY;
if (strcmp(type, INTEL_EXTENDED_PARTITION_NAME) == 0) {
writer.ClearExtendedHead(primary);
error = scan_partition(partitionID);
if (error != B_OK)
return error;
}
update_disk_device_job_progress(job, 1.0);
partition_modified(partitionID);
return B_OK;
}
status_t
pm_delete_child(int fd, partition_id partitionID, partition_id childID,
disk_job_id job)
{
TRACE(("intel: pm_delete_child\n"));
if (fd < 0)
return B_ERROR;
PartitionWriteLocker locker(partitionID);
if (!locker.IsLocked())
return B_ERROR;
partition_data* partition = get_partition(partitionID);
partition_data* child = get_partition(childID);
if (!partition || !child)
return B_BAD_VALUE;
PartitionMap* map = (PartitionMap*)partition->content_cookie;
PrimaryPartition* primary = (PrimaryPartition*)child->cookie;
if (!map || !primary)
return B_BAD_VALUE;
update_disk_device_job_progress(job, 0.0);
if (!delete_partition(childID))
return B_ERROR;
primary->Unset();
PartitionMapWriter writer(fd, primary->BlockSize());
status_t error = writer.WriteMBR(map, false);
if (error != B_OK)
return error;
update_disk_device_job_progress(job, 1.0);
partition_modified(partitionID);
return B_OK;
}
uint32
ep_get_supported_operations(partition_data* partition, uint32 mask)
{
uint32 flags = B_DISK_SYSTEM_SUPPORTS_RESIZING
| B_DISK_SYSTEM_SUPPORTS_MOVING
| B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_PARAMETERS;
if (partition_data* parent = get_parent_partition(partition->id)) {
if (partition->type
&& strcmp(partition->type, kPartitionTypeIntelExtended) == 0
&& strcmp(parent->content_type, kPartitionTypeIntel) == 0) {
flags |= B_DISK_SYSTEM_SUPPORTS_INITIALIZING;
}
}
int32 countSpaces = 0;
if (ep_get_partitionable_spaces(partition, NULL, 0, &countSpaces)
== B_BUFFER_OVERFLOW
&& countSpaces > 0) {
flags |= B_DISK_SYSTEM_SUPPORTS_CREATING_CHILD;
}
return flags;
}
uint32
ep_get_supported_child_operations(partition_data* partition,
partition_data* child, uint32 mask)
{
return B_DISK_SYSTEM_SUPPORTS_RESIZING_CHILD
| B_DISK_SYSTEM_SUPPORTS_MOVING_CHILD
| B_DISK_SYSTEM_SUPPORTS_SETTING_TYPE
| B_DISK_SYSTEM_SUPPORTS_DELETING_CHILD;
}
bool
ep_is_sub_system_for(partition_data* partition)
{
if (partition == NULL)
return false;
TRACE(("intel: ep_is_sub_system_for(%" B_PRId32 ": %" B_PRId64 ", "
"%" B_PRId64 ", %" B_PRId32 ", %s)\n", partition->id, partition->offset,
partition->size, partition->block_size, partition->content_type));
return partition->content_type
&& !strcmp(partition->content_type, kPartitionTypeIntel);
}
bool
ep_validate_resize(partition_data* partition, off_t* size)
{
TRACE(("intel: ep_validate_resize\n"));
if (!partition || !size)
return false;
return validate_resize(partition, size);
}
bool
ep_validate_resize_child(partition_data* partition, partition_data* child,
off_t* _size)
{
TRACE(("intel: ep_validate_resize_child\n"));
if (!partition || !child || !_size)
return false;
off_t size = *_size;
if (!validate_resize_child(partition, child, child->offset,
child->size, &size, get_sibling_partitions_ep))
return false;
*_size = size;
return true;
}
bool
ep_validate_move(partition_data* partition, off_t* start)
{
TRACE(("intel: ep_validate_move\n"));
if (!partition || !start)
return false;
return true;
}
bool
ep_validate_move_child(partition_data* partition, partition_data* child,
off_t* _start)
{
TRACE(("intel: ep_validate_move_child\n"));
if (!partition || !child || !_start)
return false;
if (*_start == child->offset)
return true;
off_t start = *_start;
if (!validate_move_child(partition, child, child->offset,
child->size, &start, get_sibling_partitions_ep))
return false;
*_start = start;
return true;
}
static inline bool
is_type_valid_ep(const char* type)
{
PartitionType ptype;
ptype.SetType(type);
return (ptype.IsValid() && !ptype.IsEmpty() && !ptype.IsExtended());
}
bool
ep_validate_set_type(partition_data* partition, const char* type)
{
TRACE(("intel: ep_validate_set_type\n"));
if (!partition || !type)
return false;
return is_type_valid_ep(type);
}
bool
ep_validate_initialize(partition_data* partition, char* name,
const char* parameters)
{
TRACE(("intel: ep_validate_initialize\n"));
if (!partition || !(ep_get_supported_operations(partition)
& B_DISK_SYSTEM_SUPPORTS_INITIALIZING)) {
return false;
}
return true;
}
bool
ep_validate_create_child(partition_data* partition, off_t* offset, off_t* size,
const char* type, const char* name, const char* parameters, int32* index)
{
return false;
}
status_t
ep_get_partitionable_spaces(partition_data* partition,
partitionable_space_data* buffer, int32 count, int32* actualCount)
{
TRACE(("intel: ep_get_partitionable_spaces\n"));
if (!partition || !partition->content_type
|| strcmp(partition->content_type, kPartitionTypeIntelExtended)
|| !actualCount) {
return B_BAD_VALUE;
}
if (count > 0 && !buffer)
return B_BAD_VALUE;
return get_partitionable_spaces(partition, buffer, count, actualCount,
fill_partitionable_spaces_buffer_ep,
partition->offset + PTS_OFFSET * partition->block_size,
PTS_OFFSET * partition->block_size,
PTS_OFFSET * partition->block_size);
}
status_t
ep_get_next_supported_type(partition_data* partition, int32* cookie,
char* _type)
{
TRACE(("intel: ep_get_next_supported_type\n"));
if (!partition || !partition->content_type
|| strcmp(partition->content_type, kPartitionTypeIntelExtended)
|| !cookie || !_type) {
return B_BAD_VALUE;
}
if (*cookie > 255)
return B_ENTRY_NOT_FOUND;
if (*cookie < 1)
*cookie = 1;
uint8 type = *cookie;
PartitionType ptype;
ptype.SetType(type);
while (ptype.IsValid() && !ptype.IsExtended())
ptype.FindNext();
if (!ptype.IsValid())
return B_ENTRY_NOT_FOUND;
ptype.GetTypeString(_type);
if (ptype.FindNext())
*cookie = ptype.Type();
else
*cookie = 256;
return B_OK;
}
status_t
ep_shadow_changed(partition_data* partition, partition_data* child,
uint32 operation)
{
TRACE(("intel: ep_shadow_changed\n"));
if (!partition)
return B_BAD_VALUE;
return B_OK;
}
bool
check_partition_location_ep(partition_data* partition, off_t offset,
off_t size, off_t ptsOffset)
{
if (!partition)
return false;
off_t alignedOffset = sector_align(offset, partition->block_size);
if (alignedOffset != offset)
return false;
if (offset < partition->offset
|| (offset > partition->offset + partition->size
&& offset + size <= partition->offset + partition->size))
return false;
for (int32 i = 0; i < partition->child_count; i++) {
partition_data* sibling = get_child_partition(partition->id, i);
LogicalPartition* logical = (LogicalPartition*)sibling->cookie;
if (logical == NULL)
return false;
if (ptsOffset > logical->Offset()
&& ptsOffset < logical->Offset() + logical->Size())
return false;
if ((logical->PartitionTableOffset() >= offset
&& logical->PartitionTableOffset() < offset + size)
|| logical->PartitionTableOffset() == ptsOffset)
return false;
}
return true;
}
status_t
ep_resize(int fd, partition_id partitionID, off_t size, disk_job_id job)
{
TRACE(("intel: ep_resize\n"));
if (fd < 0)
return B_ERROR;
PartitionWriteLocker locker(partitionID);
if (!locker.IsLocked())
return B_ERROR;
partition_data* partition = get_partition(partitionID);
if (!partition)
return B_BAD_VALUE;
off_t validatedSize = size;
if (!ep_validate_resize(partition, &validatedSize))
return B_BAD_VALUE;
update_disk_device_job_progress(job, 0.0);
partition->size = validatedSize;
partition->content_size = validatedSize;
update_disk_device_job_progress(job, 1.0);
partition_modified(partitionID);
return B_OK;
}
status_t
ep_resize_child(int fd, partition_id partitionID, off_t size, disk_job_id job)
{
TRACE(("intel: ep_resize_child\n"));
if (fd < 0)
return B_ERROR;
PartitionWriteLocker locker(partitionID);
if (!locker.IsLocked())
return B_ERROR;
partition_data* partition = get_parent_partition(partitionID);
partition_data* child = get_partition(partitionID);
if (!partition || !child)
return B_BAD_VALUE;
LogicalPartition* logical = (LogicalPartition*)child->cookie;
PrimaryPartition* primary = (PrimaryPartition*)partition->cookie;
if (!logical || !primary)
return B_BAD_VALUE;
off_t validatedSize = size;
if (!ep_validate_resize_child(partition, child, &validatedSize))
return B_BAD_VALUE;
if (child->size == validatedSize)
return B_OK;
update_disk_device_job_progress(job, 0.0);
logical->SetSize(validatedSize);
PartitionMapWriter writer(fd, partition->block_size);
status_t error = writer.WriteLogical(logical, primary, false);
if (error != B_OK) {
logical->SetSize(child->size);
return error;
}
LogicalPartition* prev = logical->Previous();
error = prev ? writer.WriteLogical(prev, primary, false)
: writer.WriteLogical(logical, primary, false);
if (error != B_OK)
return error;
child->size = validatedSize;
update_disk_device_job_progress(job, 1.0);
partition_modified(partitionID);
return B_OK;
}
status_t
ep_move(int fd, partition_id partitionID, off_t offset, disk_job_id job)
{
TRACE(("intel: ep_move\n"));
if (fd < 0)
return B_ERROR;
PartitionWriteLocker locker(partitionID);
if (!locker.IsLocked())
return B_ERROR;
partition_data* partition = get_partition(partitionID);
if (!partition)
return B_BAD_VALUE;
if (!ep_validate_move(partition, &offset))
return B_BAD_VALUE;
return B_OK;
}
status_t
ep_move_child(int fd, partition_id partitionID, partition_id childID,
off_t offset, disk_job_id job)
{
TRACE(("intel: ep_move_child\n"));
if (fd < 0)
return B_ERROR;
PartitionWriteLocker locker(partitionID);
if (!locker.IsLocked())
return B_ERROR;
partition_data* partition = get_partition(partitionID);
partition_data* child = get_partition(childID);
if (!partition || !child)
return B_BAD_VALUE;
LogicalPartition* logical = (LogicalPartition*)child->cookie;
PrimaryPartition* primary = (PrimaryPartition*)partition->cookie;
if (!logical || !primary)
return B_BAD_VALUE;
off_t validatedOffset = offset;
if (!ep_validate_move_child(partition, child, &validatedOffset))
return B_BAD_VALUE;
if (child->offset == validatedOffset)
return B_OK;
off_t diffOffset = validatedOffset - child->offset;
int32 allocated;
uint8* buffer = allocate_buffer(partition->block_size, MAX_MOVE_BUFFER,
&allocated);
if (!buffer)
return B_NO_MEMORY;
update_disk_device_job_progress(job, 0.0);
status_t error = B_OK;
off_t pts_offset = logical->Offset() - logical->PartitionTableOffset();
error = move_partition(fd, child->offset - pts_offset,
validatedOffset - pts_offset, child->size + pts_offset, buffer,
allocated * partition->block_size, job);
delete[] buffer;
if (error != B_OK)
return error;
child->offset = validatedOffset;
logical->SetOffset(logical->Offset() + diffOffset);
logical->SetPartitionTableOffset(logical->PartitionTableOffset() + diffOffset);
PartitionMapWriter writer(fd, partition->block_size);
error = writer.WriteLogical(logical, primary, false);
if (error != B_OK)
return error;
LogicalPartition* prev = logical->Previous();
error = prev ? writer.WriteLogical(prev, primary, false)
: writer.WriteLogical(logical, primary, false);
if (error != B_OK)
return error;
update_disk_device_job_progress(job, 1.0);
partition_modified(childID);
return B_OK;
}
status_t
ep_set_type(int fd, partition_id partitionID, const char* type, disk_job_id job)
{
TRACE(("intel: ep_set_type\n"));
if (fd < 0 || !type)
return B_BAD_VALUE;
PartitionWriteLocker locker(partitionID);
if (!locker.IsLocked())
return B_ERROR;
partition_data* partition = get_parent_partition(partitionID);
partition_data* child = get_partition(partitionID);
if (!partition || !child)
return B_BAD_VALUE;
LogicalPartition* logical = (LogicalPartition*)child->cookie;
PrimaryPartition* primary = (PrimaryPartition*)partition->cookie;
if (!logical || !primary)
return B_BAD_VALUE;
if (!ep_validate_set_type(child, type))
return B_BAD_VALUE;
if (child->type && !strcmp(type, child->type))
return B_OK;
PartitionType ptype;
ptype.SetType(type);
if (!ptype.IsValid() || ptype.IsEmpty() || ptype.IsExtended())
return false;
update_disk_device_job_progress(job, 0.0);
uint8 oldType = logical->Type();
logical->SetType(ptype.Type());
PartitionMapWriter writer(fd, partition->block_size);
status_t error = writer.WriteLogical(logical, primary, false);
if (error != B_OK) {
logical->SetType(oldType);
return error;
}
free(child->type);
child->type = strdup(type);
if (!child->type)
return B_NO_MEMORY;
update_disk_device_job_progress(job, 1.0);
partition_modified(partitionID);
return B_OK;
}
status_t
ep_initialize(int fd, partition_id partitionID, const char* name,
const char* parameters, off_t partitionSize, disk_job_id job)
{
TRACE(("intel: ep_initialize\n"));
if (fd < 0)
return B_ERROR;
PartitionWriteLocker locker(partitionID);
if (!locker.IsLocked())
return B_ERROR;
partition_data* partition = get_partition(partitionID);
if (!partition)
return B_BAD_VALUE;
PrimaryPartition* primary = (PrimaryPartition*)partition->cookie;
if (!primary)
return B_BAD_VALUE;
if (!ep_validate_initialize(partition, NULL, parameters))
return B_BAD_VALUE;
update_disk_device_job_progress(job, 0.0);
partition->status = B_PARTITION_VALID;
partition->flags |= B_PARTITION_PARTITIONING_SYSTEM;
partition->content_size = partition->size;
partition->content_cookie = primary;
partition_table table;
table.clear_code_area();
PartitionMapWriter writer(fd, partition->block_size);
status_t error = writer.ClearExtendedHead(primary);
if (error != B_OK)
return error;
update_disk_device_job_progress(job, 1.0);
partition_modified(partitionID);
return B_OK;
}
childID is used for the return value, but is also an optional input
parameter -- -1 to be ignored
*/
status_t
ep_create_child(int fd, partition_id partitionID, off_t offset, off_t size,
const char* type, const char* name, const char* parameters, disk_job_id job,
partition_id* childID)
{
TRACE(("intel: ep_create_child\n"));
if (fd < 0 || !childID)
return B_BAD_VALUE;
PartitionWriteLocker locker(partitionID);
if (!locker.IsLocked())
return B_ERROR;
partition_data* partition = get_partition(partitionID);
partition_data* parent = get_parent_partition(partitionID);
if (partition == NULL || parent == NULL)
return B_BAD_VALUE;
PrimaryPartition* primary = (PrimaryPartition*)partition->cookie;
if (!primary)
return B_BAD_VALUE;
void* handle = parse_driver_settings_string(parameters);
if (handle == NULL)
return B_ERROR;
bool active = get_driver_boolean_parameter(handle, "active", false, true);
off_t ptsOffset = 0;
const char* buffer = get_driver_parameter(
handle, "partition_table_offset", NULL, NULL);
if (buffer != NULL)
ptsOffset = strtoull(buffer, NULL, 10);
else {
unload_driver_settings(handle);
return B_BAD_VALUE;
}
unload_driver_settings(handle);
if (!check_partition_location_ep(partition, offset, size, ptsOffset))
return B_BAD_VALUE;
update_disk_device_job_progress(job, 0.0);
partition_data* child = create_child_partition(partition->id,
partition->child_count, offset, size, *childID);
if (!child)
return B_ERROR;
LogicalPartition* logical = new(nothrow) LogicalPartition;
if (!logical)
return B_NO_MEMORY;
PartitionType ptype;
ptype.SetType(type);
logical->SetPartitionTableOffset(ptsOffset - parent->offset);
logical->SetOffset(offset);
logical->SetSize(size);
logical->SetType(ptype.Type());
logical->SetActive(active);
logical->SetPrimaryPartition(primary);
logical->SetBlockSize(partition->block_size);
primary->AddLogicalPartition(logical);
int parentFD = open_partition(parent->id, O_RDWR);
if (parentFD < 0) {
primary->RemoveLogicalPartition(logical);
delete logical;
return B_IO_ERROR;
}
PartitionMapWriter writer(parentFD, primary->BlockSize());
status_t error = writer.WriteLogical(logical, primary, true);
if (error != B_OK) {
primary->RemoveLogicalPartition(logical);
delete logical;
return error;
}
LogicalPartition* previous = logical->Previous();
if (previous != NULL) {
error = writer.WriteLogical(previous, primary, true);
if (error != B_OK) {
primary->RemoveLogicalPartition(logical);
delete logical;
return error;
}
}
*childID = child->id;
child->block_size = logical->BlockSize();
child->type = strdup(type);
child->parameters = strdup(parameters);
child->cookie = logical;
if (!child->type || !child->parameters)
error = B_NO_MEMORY;
update_disk_device_job_progress(job, 1.0);
partition_modified(partitionID);
return B_OK;
}
status_t
ep_delete_child(int fd, partition_id partitionID, partition_id childID,
disk_job_id job)
{
TRACE(("intel: ep_delete_child\n"));
if (fd < 0)
return B_ERROR;
PartitionWriteLocker locker(partitionID);
if (!locker.IsLocked())
return B_ERROR;
partition_data* partition = get_partition(partitionID);
partition_data* parent = get_parent_partition(partitionID);
partition_data* child = get_partition(childID);
if (partition == NULL || parent == NULL || child == NULL)
return B_BAD_VALUE;
PrimaryPartition* primary = (PrimaryPartition*)partition->cookie;
LogicalPartition* logical = (LogicalPartition*)child->cookie;
if (primary == NULL || logical == NULL)
return B_BAD_VALUE;
update_disk_device_job_progress(job, 0.0);
if (!delete_partition(childID))
return B_ERROR;
LogicalPartition* previous = logical->Previous();
LogicalPartition* next = logical->Next();
primary->RemoveLogicalPartition(logical);
delete logical;
int parentFD = open_partition(parent->id, O_RDWR);
if (parentFD < 0)
return B_IO_ERROR;
PartitionMapWriter writer(parentFD, primary->BlockSize());
status_t error;
if (previous != NULL) {
error = writer.WriteLogical(previous, primary, true);
} else {
error = writer.WriteExtendedHead(next, primary, true);
if (next != NULL) {
next->SetPartitionTableOffset(primary->Offset());
partition_data* nextSibling = NULL;
if (get_partition_from_offset_ep(partition, next->Offset(),
&nextSibling)) {
char buffer[128];
sprintf(buffer, "active %s ;\npartition_table_offset %" B_PRId64
" ;\n", next->Active() ? "true" : "false",
next->PartitionTableOffset());
nextSibling->parameters = strdup(buffer);
}
}
}
close(parentFD);
if (error != B_OK)
return error;
update_disk_device_job_progress(job, 1.0);
partition_modified(partitionID);
return B_OK;
}