Files
haiku-beta6/src/add-ons/kernel/partitioning_systems/intel/write_support.cpp
T
Ingo Weinhold db5c68ca46 * Added the partition size as parameter to the file and partitioning
system initialize() hooks. It's often the only info about the
  partition one needs and thus locking the partition just to get it is
  no longer necessary.
* intel partitioning system:
  - Removed passing around block sizes. We require 512 byte sectors
    anyway. In fact using the parent partition's block size was even
    wrong.
  - Simplified writing the partition map sector.
  - Simplified and corrected the partition map initialization.
  - We don't fail identifying a partition anymore, if the partition map
    contains no partitions. We would never identify a freshly
    initialized partition map before.
  - Made pm_identify() more intelligent: It determines the priority to
    return depending on whether the partition is the device itself and
    whether we have recognized child partitions.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@22447 a95241bf-73f2-0310-859d-f6bbb57e9c96
2007-10-04 23:48:18 +00:00

1984 lines
52 KiB
C++

/*
* Copyright 2003-2007, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Ingo Weinhold, [email protected]
* Tomas Kucera, [email protected]
*/
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <new>
#include <DiskDeviceTypes.h>
#include <KernelExport.h>
#include <AutoDeleter.h>
#include <ddm_modules.h>
#include "intel.h"
#include "PartitionLocker.h"
#include "PartitionMap.h"
#include "PartitionMapParser.h"
#include "PartitionMapWriter.h"
//#define TRACE(x) ;
#define TRACE(x) dprintf x
// Maximal size of move buffer (in sectors).
static const int32 MAX_MOVE_BUFFER = 2 * 1024 * 4;
// for logical partitions in Intel Extended Partition
// Count of free sectors after Partition Table Sector (at logical partition).
static const uint32 FREE_SECTORS_AFTER_PTS = 0;
// Count of free sectors after Master Boot Record.
static const uint32 FREE_SECTORS_AFTER_MBR = 0;
// size of logical partition header in blocks
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);
// #pragma mark - Intel Partition Map - support functions
// pm_get_supported_operations
uint32
pm_get_supported_operations(partition_data* partition, uint32 mask = ~0)
{
uint32 flags = B_DISK_SYSTEM_SUPPORTS_RESIZING
| B_DISK_SYSTEM_SUPPORTS_MOVING
| B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_PARAMETERS
| B_DISK_SYSTEM_SUPPORTS_INITIALIZING;
// creating child
int32 countSpaces = 0;
if (partition->child_count < 4
// free space check
&& pm_get_partitionable_spaces(partition, NULL, 0, &countSpaces) == B_OK
&& countSpaces > 0) {
flags |= B_DISK_SYSTEM_SUPPORTS_CREATING_CHILD;
}
return flags;
}
// pm_get_supported_child_operations
uint32
pm_get_supported_child_operations(partition_data* partition,
partition_data* child, uint32 mask = ~0)
{
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;
}
// pm_is_sub_system_for
bool
pm_is_sub_system_for(partition_data *partition)
{
// primary partition map doesn't naturally live in any other child partition
return false;
}
// #pragma mark - Intel Partition Map - validate functions
// sector_align (auxiliary function)
static inline
off_t
sector_align(off_t offset)
{
return offset / SECTOR_SIZE * SECTOR_SIZE;
}
// sector_align_up (auxiliary function)
static inline
off_t
sector_align_up(off_t offset)
{
return (offset + SECTOR_SIZE - 1) / SECTOR_SIZE * SECTOR_SIZE;
}
// validate_resize (auxiliary function)
static bool
validate_resize(partition_data *partition, off_t *size)
{
off_t new_size = *size;
// size remains the same?
if (new_size == partition->size)
return true;
if (new_size < 0)
new_size = 0;
else
new_size = sector_align(new_size);
// grow partition?
if (new_size > partition->size) {
*size = new_size;
return true;
}
// shrink partition
// no child has to be over the new size of the parent partition
// TODO: shouldn't be just: off_t current_end = new_size; ??? probably not
off_t current_end = partition->offset + new_size;
for (int32 i = 0; i < partition->child_count; i++) {
partition_data *child = get_child_partition(partition->id, i);
if (child && child->offset + child->size > current_end)
current_end = child->offset + child->size;
}
new_size = current_end - partition->offset;
// make the size a multiple of the block size (greater one)
new_size = sector_align_up(new_size);
*size = new_size;
return true;
}
// pm_validate_resize
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);
}
// get_offset_ep (auxiliary function)
static inline off_t
get_offset_ep(const partition_data *partition)
{
LogicalPartition *logical = (LogicalPartition *)partition->cookie;
off_t diff_offset = logical->Offset() - logical->PTSOffset();
return partition->offset - diff_offset;
}
// get_size_ep (auxiliary function)
static inline off_t
get_size_ep(const partition_data *partition)
{
LogicalPartition *logical = (LogicalPartition *)partition->cookie;
off_t diff_offset = logical->Offset() - logical->PTSOffset();
return partition->size + diff_offset;
}
// get_sibling_partitions_pm (auxiliary function)
/*!
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)
{
// finding out sibling partitions
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 {
// sibling->offset > childOffset
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;
}
}
// get_sibling_partitions_ep (auxiliary function)
/*!
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)
{
// finding out sibling partitions
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 (get_offset_ep(sibling) <= childOffset) {
if (!previousSibling || previousSibling->offset < sibling->offset)
previousSibling = sibling;
} else {
// get_offset_ep(sibling) > childOffset
if (!nextSibling || nextSibling->offset > sibling->offset)
nextSibling = sibling;
}
}
*previous = previousSibling;
*next = nextSibling;
if (previousSibling) {
*previousOffset = get_offset_ep(previousSibling);
*previousSize = get_size_ep(previousSibling);
}
if (nextSibling) {
*nextOffset = get_offset_ep(nextSibling);
*nextSize = get_size_ep(nextSibling);
}
}
// validate_resize_child (auxiliary function)
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)
{
// size remains the same?
if (*size == childSize)
return true;
// shrink partition?
if (*size < childSize) {
if (*size < 0)
*size = 0;
// make the size a multiple of the block size
*size = sector_align(*size);
return true;
}
// grow partition
// child must completely lie within the parent partition
if (childOffset + *size > partition->offset + partition->size)
*size = partition->offset + partition->size - childOffset;
// child must not intersect with sibling partitions
// finding out sibling partitions
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);
return true;
}
// pm_validate_resize_child
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);
}
// pm_validate_move
bool
pm_validate_move(partition_data *partition, off_t *start)
{
TRACE(("intel: pm_validate_move\n"));
if (!partition || !start)
return false;
// nothing to do here
return true;
}
// validate_move_child (auxiliary function)
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);
// finding out sibling partitions
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);
// we cannot move child over sibling partition
if (start < childOffset) {
// moving left
if (previousSibling && previousOffset + previousSize > start) {
start = previousOffset + previousSize;
start = sector_align_up(start);
}
} else {
// moving right
if (nextSibling && nextOffset < start + childSize) {
start = nextOffset - childSize;
start = sector_align(start);
}
}
*_start = start;
return true;
}
// pm_validate_move_child
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);
}
// is_type_valid_pm (auxiliary function)
/*!
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)
{
// validity check of the type
PartitionType ptype;
ptype.SetType(type);
if (!ptype.IsValid() || ptype.IsEmpty())
return false;
// only one extended partition is allowed
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;
}
// pm_validate_set_type
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 *father = get_parent_partition(partition->id);
if (!father)
return false;
PrimaryPartition *child = (PrimaryPartition*)partition->cookie;
if (!child)
return false;
// validity check of the type
return is_type_valid_pm(type, father, child);
}
// pm_validate_initialize
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;
}
// name is ignored
if (name)
name[0] = '\0';
// parameters are ignored, too
return true;
}
// validate_create_child_partition (auxiliary function)
static bool
validate_create_child_partition(partition_data *partition, off_t *start,
off_t *size, fc_get_sibling_partitions getSiblingPartitions)
{
// make the start and size a multiple of the block size
*start = sector_align(*start);
if (*size < 0)
*size = 0;
else
*size = sector_align(*size);
// child must completely lie within the parent partition
if (*start >= partition->offset + partition->size)
return false;
if (*start + *size > partition->offset + partition->size)
*size = partition->offset + partition->size - *start;
// new child must not intersect with sibling partitions
// finding out sibling partitions
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);
// position check of the new partition
if (previousSibling && (previousOffset + previousSize > *start)) {
*start = previousOffset + previousSize;
*start = sector_align_up(*start);
}
if (nextSibling && (nextOffset < *start + *size))
*size = nextOffset - *start;
*size = sector_align(*size);
if (*size == 0)
return false;
return true;
}
// pm_validate_create_child
/*!
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 *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;
}
// TODO: check parameters
// type check
if (!is_type_valid_pm(type, partition))
return false;
// finding out index of the new partition (first free record in MBR)
// at least one record has to be free
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;
}
}
// this cannot happen
if (newIndex < 0)
return false;
*index = newIndex;
if (*start < partition->offset + MBR_OFFSET * SECTOR_SIZE) {
*start = partition->offset + MBR_OFFSET * SECTOR_SIZE;
*start = sector_align_up(*start);
}
return validate_create_child_partition(partition, start, size,
get_sibling_partitions_pm);
}
// cmp_partition_position
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;
else if (offset1 > offset2)
return 1;
return 0;
}
// fill_partitionable_spaces_buffer_pm
/*!
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;
}
// fill_partitionable_spaces_buffer_ep
/*!
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 = get_offset_ep(child);
positions[partition_count].size = get_size_ep(child);
partition_count++;
}
}
return partition_count;
}
// get_partitionable_spaces (auxiliary function)
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;
// fill the array
int32 partition_count = fillBuffer(partition, positions);
// sort the array
qsort(positions, partition_count, sizeof(PartitionPosition),
cmp_partition_position);
// first sektor is MBR or EBR
off_t offset = startOffset + headerSize;
off_t size = 0;
int32 actualCount = 0;
// offset alignment (to upper bound)
offset = sector_align_up(offset);
// finding out all partitionable spaces
for (int32 i = 0; i < partition_count; i++) {
size = positions[i].offset - offset;
size = sector_align(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);
}
// space in the end of partition
size = partition->offset + partition->size - offset;
size = sector_align(size);
if (size > 0) {
if (actualCount < count) {
buffer[actualCount].offset = offset;
buffer[actualCount].size = size;
}
actualCount++;
}
// cleanup
if (positions)
delete[] positions;
*_actualCount = actualCount;
TRACE(("intel: get_partitionable_spaces - found: %ld\n", actualCount));
return B_OK;
}
// pm_get_partitionable_spaces
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 * SECTOR_SIZE,
0, 0);
}
// pm_get_next_supported_type
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;
// get type
PartitionType ptype;
ptype.SetType(type);
if (!ptype.IsValid())
return B_ENTRY_NOT_FOUND;
ptype.GetTypeString(_type);
// find next type
if (ptype.FindNext())
*cookie = ptype.Type();
else
*cookie = 256;
return B_OK;
}
// pm_shadow_changed
status_t
pm_shadow_changed(partition_data *partition, uint32 operation)
{
TRACE(("intel: pm_shadow_changed\n"));
if (!partition)
return B_BAD_VALUE;
// nothing to do here
return B_OK;
}
// #pragma mark - Intel Partition Map - writing functions
// pm_resize
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;
// get out partition
partition_data *partition = get_partition(partitionID);
if (!partition)
return B_BAD_VALUE;
// validate the new size
// TODO: The parameter has already been checked and must not be altered!
off_t validatedSize = size;
if (!pm_validate_resize(partition, &validatedSize))
return B_BAD_VALUE;
// update data stuctures
update_disk_device_job_progress(job, 0.0);
// TODO: partition->size is not supposed to be touched.
partition->size = validatedSize;
partition->content_size = validatedSize;
// all changes applied
update_disk_device_job_progress(job, 1.0);
partition_modified(partitionID);
return B_OK;
}
// pm_resize_child
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;
// get out partition, child and partition map structure
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;
// validate the new size
// TODO: The parameter has already been checked and must not be altered!
off_t validatedSize = size;
if (!pm_validate_resize_child(partition, child, &validatedSize))
return B_BAD_VALUE;
if (child->size == validatedSize)
return B_OK;
// update data stuctures and write changes
update_disk_device_job_progress(job, 0.0);
primary->SetSize(validatedSize);
// TODO: The partition is not supposed to be locked here!
PartitionMapWriter writer(fd, 0, partition->size);
status_t error = writer.WriteMBR(map, false);
if (error != B_OK) {
// putting into previous state
primary->SetSize(child->size);
return error;
}
child->size = validatedSize;
// all changes applied
update_disk_device_job_progress(job, 1.0);
partition_modified(partitionID);
return B_OK;
}
// pm_move
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;
// TODO: Should be a no-op!
PartitionWriteLocker locker(partitionID);
if (!locker.IsLocked())
return B_ERROR;
// get out partition
partition_data *partition = get_partition(partitionID);
if (!partition)
return B_BAD_VALUE;
// validate the new start
if (!pm_validate_move(partition, &offset))
return B_BAD_VALUE;
// nothing to do here
return B_OK;
}
// allocate_buffer (auxiliary function)
/*!
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;
}
// move_block (auxiliary function)
static status_t
move_block(int fd, off_t fromOffset, off_t toOffset, uint8 *buffer, int32 size)
{
status_t error = B_OK;
// read block to buffer
if (read_pos(fd, fromOffset, buffer, size) != size) {
error = errno;
if (error == B_OK)
error = B_IO_ERROR;
TRACE(("intel: move_block(): reading failed: %lx\n", error));
return error;
}
// write block from buffer
if (write_pos(fd, toOffset, buffer, size) != size) {
error = errno;
if (error == B_OK)
error = B_IO_ERROR;
TRACE(("intel: move_block(): writing failed: %lx\n", error));
}
return error;
}
// move_partition (auxiliary function)
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)
{
// TODO: This should be a service function of the DDM!
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;
}
// pm_move_child
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;
// get partition, child and partition map structure
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;
// TODO: The parameter has already been checked and must not be altered!
off_t validatedOffset = offset;
if (!pm_validate_move_child(partition, child, &validatedOffset))
return B_BAD_VALUE;
// if the old offset is the same, there is nothing to do
if (child->offset == validatedOffset)
return B_OK;
// buffer allocation
int32 allocated;
uint8 *buffer = allocate_buffer(SECTOR_SIZE, MAX_MOVE_BUFFER,
&allocated);
if (!buffer)
return B_NO_MEMORY;
// partition moving
// TODO: The partition is not supposed to be locked at this point!
update_disk_device_job_progress(job, 0.0);
status_t error = B_OK;
error = move_partition(fd, child->offset, validatedOffset, child->size,
buffer, allocated * SECTOR_SIZE, job);
delete[] buffer;
if (error != B_OK)
return error;
// partition moved
// updating data structure
child->offset = validatedOffset;
primary->SetOffset(validatedOffset);
PartitionMapWriter writer(fd, 0, partition->size);
error = writer.WriteMBR(map, false);
if (error != B_OK)
// something went wrong - this is fatal (partition has been moved)
// but MBR is not updated
return error;
// all changes applied
update_disk_device_job_progress(job, 1.0);
partition_modified(childID);
return B_OK;
}
// pm_set_type
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;
// get parent partition, child and partition map structure
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;
// TODO: The parameter has already been checked and must not be altered!
if (!pm_validate_set_type(child, type))
return B_BAD_VALUE;
// if the old type is the same, there is nothing to do
if (child->type && !strcmp(type, child->type))
return B_OK;
PartitionType ptype;
ptype.SetType(type);
// this is impossible
if (!ptype.IsValid() || ptype.IsEmpty())
return false;
// TODO: Incompatible return value!
// setting type to the partition
update_disk_device_job_progress(job, 0.0);
uint8 oldType = primary->Type();
primary->SetType(ptype.Type());
// TODO: The partition is not supposed to be locked at this point!
PartitionMapWriter writer(fd, 0, partition->size);
status_t error = writer.WriteMBR(map, false);
if (error != B_OK) {
// something went wrong - putting into previous state
primary->SetType(oldType);
return error;
}
free(child->type);
child->type = strdup(type);
if (!child->type)
return B_NO_MEMORY;
// all changes applied
update_disk_device_job_progress(job, 1.0);
partition_modified(partitionID);
return B_OK;
}
// pm_initialize
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;
update_disk_device_job_progress(job, 0.0);
// we will write an empty partition map
PartitionMap map;
// write the sector to disk
PartitionMapWriter writer(fd, 0, partitionSize);
status_t error = writer.WriteMBR(&map, true);
if (error != B_OK)
return error;
// rescan partition
error = scan_partition(partitionID);
if (error != B_OK)
return error;
// all changes applied
update_disk_device_job_progress(job, 1.0);
partition_modified(partitionID);
return B_OK;
}
// pm_create_child
status_t
pm_create_child(int fd, partition_id partitionID, off_t offset, off_t size,
const char *type, const char *parameters, disk_job_id job,
partition_id *childID)
// childID is used for the return value, but is also an optional input
// parameter -- -1 to be ignored
{
TRACE(("intel: pm_create_child\n"));
if (fd < 0 || !childID)
return B_BAD_VALUE;
PartitionWriteLocker locker(partitionID);
if (!locker.IsLocked())
return B_ERROR;
// get partition and partition map structure
partition_data *partition = get_partition(partitionID);
if (!partition)
return B_BAD_VALUE;
PartitionMap *map = (PartitionMap*)partition->content_cookie;
if (!map)
return B_BAD_VALUE;
// validate the offset, size and get index of the new partition
// TODO: The parameters have already been checked and must not be altered!
off_t validatedOffset = offset;
off_t validatedSize = size;
int32 index = 0;
if (!pm_validate_create_child(partition, &validatedOffset, &validatedSize,
type, parameters, &index)) {
return B_BAD_VALUE;
}
// finding out free primary partition in the map (index from
// pm_validate_create_child)
PrimaryPartition *primary = map->PrimaryPartitionAt(index);
if (!primary->IsEmpty())
return B_BAD_DATA;
// creating partition
update_disk_device_job_progress(job, 0.0);
partition_data *child = create_child_partition(partition->id, index,
*childID);
if (!child)
return B_ERROR;
PartitionType ptype;
ptype.SetType(type);
primary->SetPTSOffset(0);
primary->SetOffset(validatedOffset);
primary->SetSize(validatedSize);
primary->SetType(ptype.Type());
// TODO: correctly fill active parameter
primary->SetActive(false);
// write changes to disk
PartitionMapWriter writer(fd, 0, partition->size);
// TODO: The partition is not supposed to be locked at this point!
status_t error = writer.WriteMBR(map, false);
if (error != B_OK) {
// putting into previous state
primary->Unset();
delete_partition(child->id);
return error;
}
*childID = child->id;
child->offset = partition->offset + primary->Offset();
child->size = primary->Size();
child->block_size = SECTOR_SIZE;
// (no name)
child->type = strdup(type);
// parameters
child->parameters = strdup(parameters);
child->cookie = primary;
// check for allocation problems
if (!child->type || !child->parameters)
return B_NO_MEMORY;
// all changes applied
update_disk_device_job_progress(job, 1.0);
partition_modified(partitionID);
return B_OK;
}
// pm_delete_child
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;
// deleting child
update_disk_device_job_progress(job, 0.0);
if (!delete_partition(childID))
return B_ERROR;
primary->Unset();
// write changes to disk
PartitionMapWriter writer(fd, 0, partition->size);
// TODO: The partition is not supposed to be locked at this point!
status_t error = writer.WriteMBR(map, false);
if (error != B_OK)
return error;
// all changes applied
update_disk_device_job_progress(job, 1.0);
partition_modified(partitionID);
return B_OK;
}
// #pragma mark - Intel Extended Partition - support functions
// ep_get_supported_operations
uint32
ep_get_supported_operations(partition_data* partition, uint32 mask = ~0)
{
uint32 flags = B_DISK_SYSTEM_SUPPORTS_RESIZING
| B_DISK_SYSTEM_SUPPORTS_MOVING
| B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_PARAMETERS;
// initializing
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;
}
}
// creating child
int32 countSpaces = 0;
if (pm_get_partitionable_spaces(partition, NULL, 0, &countSpaces) == B_OK
&& countSpaces > 0) {
flags |= B_DISK_SYSTEM_SUPPORTS_CREATING_CHILD;
}
return flags;
}
// ep_get_supported_child_operations
uint32
ep_get_supported_child_operations(partition_data* partition,
partition_data* child, uint32 mask = ~0)
{
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;
}
// ep_is_sub_system_for
bool
ep_is_sub_system_for(partition_data *partition)
{
TRACE(("intel: ep_is_sub_system_for(%ld: %lld, %lld, %ld, %s)\n",
partition->id, partition->offset, partition->size,
partition->block_size, partition->content_type));
// Intel Extended Partition can live in child partition of Intel Partition
// Map
return (partition && partition->content_type
&& !strcmp(partition->content_type, kPartitionTypeIntel));
}
// #pragma mark - Intel Extended Partition - validate functions
// ep_validate_resize
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);
}
// ep_validate_resize_child
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;
// validate position
off_t diff_offset = child->offset - get_offset_ep(child);
off_t size = *_size + diff_offset;
if (!validate_resize_child(partition, child, get_offset_ep(child),
get_size_ep(child), &size, get_sibling_partitions_ep))
return false;
*_size = size - diff_offset;
return true;
}
// ep_validate_move
bool
ep_validate_move(partition_data *partition, off_t *start)
{
TRACE(("intel: ep_validate_move\n"));
if (!partition || !start)
return false;
// nothing to do here
return true;
}
// ep_validate_move_child
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;
// validate position
off_t diff_offset = child->offset - get_offset_ep(child);
off_t start = *_start - diff_offset;
if (!validate_move_child(partition, child, get_offset_ep(child),
get_size_ep(child), &start, get_sibling_partitions_ep))
return false;
*_start = start + diff_offset;
return true;
}
// is_type_valid_ep (auxiliary function)
static inline bool
is_type_valid_ep(const char *type)
{
// validity check of the type - it has to be known
PartitionType ptype;
ptype.SetType(type);
return (ptype.IsValid() && !ptype.IsEmpty() && !ptype.IsExtended());
}
// ep_validate_set_type
bool
ep_validate_set_type(partition_data *partition, const char *type)
{
TRACE(("intel: ep_validate_set_type\n"));
if (!partition || !type)
return false;
// validity check of the type
return is_type_valid_ep(type);
}
// ep_validate_initialize
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;
}
// name is ignored - we cannot set it to the Intel Extended Partition
// TODO: check parameters - don't know whether any parameters could be set
// to the Intel Extended Partition
return true;
}
// ep_validate_create_child
bool
ep_validate_create_child(partition_data *partition, off_t *_start, off_t *_size,
const char *type, const char *parameters, int32 *index)
// index - returns position of the new partition (the last one)
{
TRACE(("intel: ep_validate_create_child\n"));
if (!partition || !(ep_get_supported_operations(partition)
& B_DISK_SYSTEM_SUPPORTS_CREATING_CHILD)
|| !_start || !_size || !type || !index) {
return false;
}
// TODO: check parameters
// type check
if (!is_type_valid_ep(type))
return false;
// finding out index of the new partition (it will be the last child)
*index = partition->child_count;
// validate position
off_t diffOffset = PTS_OFFSET * SECTOR_SIZE;
off_t start = *_start - diffOffset;
off_t size = *_size + diffOffset;
if (start < partition->offset + PTS_OFFSET * SECTOR_SIZE) {
start = partition->offset + PTS_OFFSET * SECTOR_SIZE;
start = sector_align_up(start);
}
if (!validate_create_child_partition(partition, &start, &size,
get_sibling_partitions_ep)) {
return false;
}
*_start = start + diffOffset;
*_size = size - diffOffset;
if (*_size == 0)
return false;
return true;
}
// ep_get_partitionable_spaces
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 * SECTOR_SIZE,
PTS_OFFSET * SECTOR_SIZE,
PTS_OFFSET * SECTOR_SIZE);
}
// ep_get_next_supported_type
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;
// get type
PartitionType ptype;
ptype.SetType(type);
while (ptype.IsValid() && !ptype.IsExtended())
ptype.FindNext();
if (!ptype.IsValid())
return B_ENTRY_NOT_FOUND;
ptype.GetTypeString(_type);
// find next type
if (ptype.FindNext())
*cookie = ptype.Type();
else
*cookie = 256;
return B_OK;
}
// ep_shadow_changed
status_t
ep_shadow_changed(partition_data *partition, uint32 operation)
{
TRACE(("intel: ep_shadow_changed\n"));
if (!partition)
return B_BAD_VALUE;
// nothing to do here
return B_OK;
}
// #pragma mark - Intel Extended Partition - write functions
// ep_resize
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;
// get out partition
partition_data *partition = get_partition(partitionID);
if (!partition)
return B_BAD_VALUE;
// validate the new size
// TODO: The parameter has already been checked and must not be altered!
off_t validatedSize = size;
if (!ep_validate_resize(partition, &validatedSize))
return B_BAD_VALUE;
// update data stuctures
update_disk_device_job_progress(job, 0.0);
// TODO: partition->size is not supposed to be touched.
partition->size = validatedSize;
partition->content_size = validatedSize;
// all changes applied
update_disk_device_job_progress(job, 1.0);
partition_modified(partitionID);
return B_OK;
}
// ep_resize_child
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;
// get out partition, child and LogicalPartition structure
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;
if (!logical)
return B_BAD_VALUE;
// validate the new size
// TODO: The parameter has already been checked and must not be altered!
off_t validatedSize = size;
if (!ep_validate_resize_child(partition, child, &validatedSize))
return B_BAD_VALUE;
if (child->size == validatedSize)
return B_OK;
// update data stuctures and write changes
update_disk_device_job_progress(job, 0.0);
logical->SetSize(validatedSize);
PartitionMapWriter writer(fd, partition->offset, partition->size);
// TODO: The partition is not supposed to be locked here!
status_t error = writer.WriteLogical(NULL, logical);
if (error != B_OK) {
// putting into previous state
logical->SetSize(child->size);
return error;
}
LogicalPartition *prev = logical->Previous();
error = prev ? writer.WriteLogical(NULL, prev)
: writer.WriteExtendedHead(NULL, logical);
if (error != B_OK)
// this should be not so fatal
return error;
child->size = validatedSize;
// all changes applied
update_disk_device_job_progress(job, 1.0);
partition_modified(partitionID);
return B_OK;
}
// ep_move
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;
// get out partition
partition_data *partition = get_partition(partitionID);
if (!partition)
return B_BAD_VALUE;
// validate the new start
// TODO: The parameter has already been checked and must not be altered!
if (!ep_validate_move(partition, &offset))
return B_BAD_VALUE;
// nothing to do here
return B_OK;
}
// ep_move_child
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;
// get partition, child and LogicalPartition structure
partition_data *partition = get_partition(partitionID);
partition_data *child = get_partition(childID);
if (!partition || !child)
return B_BAD_VALUE;
LogicalPartition *logical = (LogicalPartition*)child->cookie;
if (!logical)
return B_BAD_VALUE;
// TODO: The parameter has already been checked and must not be altered!
off_t validatedOffset = offset;
if (!ep_validate_move_child(partition, child, &validatedOffset))
return B_BAD_VALUE;
// if the old offset is the same, there is nothing to do
if (child->offset == validatedOffset)
return B_OK;
off_t diffOffset = validatedOffset - child->offset;
// buffer allocation
int32 allocated;
uint8 *buffer = allocate_buffer(SECTOR_SIZE, MAX_MOVE_BUFFER, &allocated);
if (!buffer)
return B_NO_MEMORY;
// partition moving
update_disk_device_job_progress(job, 0.0);
status_t error = B_OK;
// move partition with its header (PTS table)
off_t pts_offset = logical->Offset() - logical->PTSOffset();
error = move_partition(fd, child->offset - pts_offset, validatedOffset - pts_offset,
child->size + pts_offset, buffer,
allocated * SECTOR_SIZE, job);
delete[] buffer;
if (error != B_OK)
return error;
// partition moved
// updating data structure
child->offset = validatedOffset;
logical->SetOffset(logical->Offset() + diffOffset);
logical->SetPTSOffset(logical->PTSOffset() + diffOffset);
PartitionMapWriter writer(fd, partition->offset, partition->size);
// TODO: The partition is not supposed to be locked here!
error = writer.WriteLogical(NULL, logical);
if (error != B_OK)
// something went wrong - this is fatal (partition has been moved)
// but EBR is not updated
return error;
LogicalPartition *prev = logical->Previous();
error = prev ? writer.WriteLogical(NULL, prev)
: writer.WriteExtendedHead(NULL, logical);
if (error != B_OK)
// this is fatal - linked list is not updated
return error;
// all changes applied
update_disk_device_job_progress(job, 1.0);
partition_modified(childID);
return B_OK;
}
// ep_set_type
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;
// get partition, child and LogicalPartition structure
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;
if (!logical)
return B_BAD_VALUE;
// TODO: The parameter has already been checked and must not be altered!
if (!ep_validate_set_type(child, type))
return B_BAD_VALUE;
// if the old type is the same, there is nothing to do
if (child->type && !strcmp(type, child->type))
return B_OK;
PartitionType ptype;
ptype.SetType(type);
// this is impossible
if (!ptype.IsValid() || ptype.IsEmpty() || ptype.IsExtended())
return false;
// setting type to the partition
update_disk_device_job_progress(job, 0.0);
uint8 oldType = logical->Type();
logical->SetType(ptype.Type());
PartitionMapWriter writer(fd, partition->offset, partition->size);
// TODO: The partition is not supposed to be locked here!
status_t error = writer.WriteLogical(NULL, logical);
if (error != B_OK) {
// something went wrong - putting into previous state
logical->SetType(oldType);
return error;
}
free(child->type);
child->type = strdup(type);
if (!child->type)
return B_NO_MEMORY;
// all changes applied
update_disk_device_job_progress(job, 1.0);
partition_modified(partitionID);
return B_OK;
}
// ep_initialize
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;
// get partition
partition_data *partition = get_partition(partitionID);
if (!partition)
return B_BAD_VALUE;
// name is ignored - we cannot set it to the Intel Extended Partition
// TODO: The parameter has already been checked and must not be altered!
if (!ep_validate_initialize(partition, NULL, parameters))
return B_BAD_VALUE;
// partition init (we have no child partition)
update_disk_device_job_progress(job, 0.0);
PrimaryPartition *primary = (PrimaryPartition*)partition->cookie;
// fill in the partition_data structure
partition->status = B_PARTITION_VALID;
partition->flags |= B_PARTITION_PARTITIONING_SYSTEM;
partition->content_size = partition->size;
// (no content_name and content_parameters)
// (content_type is set by the system)
partition->content_cookie = primary;
// we delete code area in EBR - nothing should be there
partition_table_sector pts;
pts.clear_code_area();
PartitionMapWriter writer(fd, partition->offset, partition->size);
// TODO: The partition is not supposed to be locked here!
status_t error = writer.WriteExtendedHead(&pts, NULL);
if (error != B_OK)
return error;
// all changes applied
update_disk_device_job_progress(job, 1.0);
partition_modified(partitionID);
return B_OK;
}
// ep_create_child
/*!
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 *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;
// get parent, partition and PrimaryPartition structure
partition_data *parent = get_parent_partition(partitionID);
partition_data *partition = get_partition(partitionID);
if (!parent || !partition)
return B_BAD_VALUE;
PrimaryPartition *primary = (PrimaryPartition*)partition->cookie;
if (!primary)
return B_BAD_VALUE;
// validate the offset, size and get index of the new partition
// TODO: The parameter has already been checked and must not be altered!
off_t validatedOffset = offset;
off_t validatedSize = size;
int32 index = 0;
if (!ep_validate_create_child(partition, &validatedOffset, &validatedSize,
type, parameters, &index)) {
return B_BAD_VALUE;
}
LogicalPartition *logical = new(nothrow) LogicalPartition;
if (!logical)
return B_NO_MEMORY;
// creating partition
update_disk_device_job_progress(job, 0.0);
partition_data *child = create_child_partition(partition->id, index,
*childID);
if (!child)
return B_ERROR;
PartitionType ptype;
ptype.SetType(type);
logical->SetPTSOffset(validatedOffset - PTS_OFFSET * SECTOR_SIZE
- partition->offset);
logical->SetOffset(validatedOffset - partition->offset);
logical->SetSize(validatedSize);
logical->SetType(ptype.Type());
// TODO: correctly fill active parameter
logical->SetActive(false);
// we delete code area in EBR - nothing should be there
partition_table_sector pts;
pts.clear_code_area();
// write changes to disk
PartitionMapWriter writer(fd, partition->offset, partition->size);
// TODO: The partition is not supposed to be locked here!
status_t error = writer.WriteLogical(&pts, logical);
if (error != B_OK) {
// putting into previous state
delete_partition(child->id);
delete logical;
return error;
}
// update linked list
primary->AddLogicalPartition(logical);
LogicalPartition *prev = logical->Previous();
error = prev ? writer.WriteLogical(NULL, prev)
: writer.WriteExtendedHead(NULL, logical);
if (error != B_OK) {
// putting into previous state
delete_partition(child->id);
primary->RemoveLogicalPartition(logical);
delete logical;
return error;
}
*childID = child->id;
child->offset = partition->offset + logical->Offset();
child->size = logical->Size();
child->block_size = SECTOR_SIZE;
// (no name)
child->type = strdup(type);
// parameters
child->parameters = strdup(parameters);
child->cookie = logical;
// check for allocation problems
if (!child->type || !child->parameters)
error = B_NO_MEMORY;
// all changes applied
update_disk_device_job_progress(job, 1.0);
partition_modified(partitionID);
return B_OK;
}
// ep_delete_child
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 *child = get_partition(childID);
if (!partition || !child)
return B_BAD_VALUE;
PrimaryPartition *primary = (PrimaryPartition*)partition->cookie;
LogicalPartition *logical = (LogicalPartition*)child->cookie;
if (!primary || !logical)
return B_BAD_VALUE;
// deleting child
update_disk_device_job_progress(job, 0.0);
if (!delete_partition(childID))
return B_ERROR;
LogicalPartition *next_logical = logical->Next();
LogicalPartition *prev_logical = logical->Previous();
primary->RemoveLogicalPartition(logical);
delete logical;
// write changes to disk
PartitionMapWriter writer(fd, partition->offset, partition->size);
// TODO: The partition is not supposed to be locked here!
status_t error = prev_logical ? writer.WriteLogical(NULL, prev_logical)
: writer.WriteExtendedHead(NULL, next_logical);
if (error != B_OK)
return error;
// all changes applied
update_disk_device_job_progress(job, 1.0);
partition_modified(partitionID);
return B_OK;
}