diff --git a/src/add-ons/kernel/partitioning_systems/intel/Jamfile b/src/add-ons/kernel/partitioning_systems/intel/Jamfile index 5b0499405c..aa2dceab73 100644 --- a/src/add-ons/kernel/partitioning_systems/intel/Jamfile +++ b/src/add-ons/kernel/partitioning_systems/intel/Jamfile @@ -11,7 +11,8 @@ KernelAddon intel : PartitionMap.cpp PartitionMapParser.cpp PartitionMapWriter.cpp - ; + write_support.cpp +; # Also build a userland version # ToDo: it's probably not a good idea to build them into the same directory diff --git a/src/add-ons/kernel/partitioning_systems/intel/intel.cpp b/src/add-ons/kernel/partitioning_systems/intel/intel.cpp index c2f4e344b0..893c302873 100644 --- a/src/add-ons/kernel/partitioning_systems/intel/intel.cpp +++ b/src/add-ons/kernel/partitioning_systems/intel/intel.cpp @@ -9,7 +9,7 @@ /*! \file intel.cpp - \brief disk_scanner partition module for "intel" style partitions. + \brief partitioning system module for "intel" style partitions. */ // TODO: The implementation is very strict right now. It rejects a partition @@ -22,29 +22,39 @@ #include #include -#include -#include -#include -#ifndef _BOOT_MODE -# include -#else -# include -//# include "DiskDeviceUtils.h" -#endif +#include + #include +#include +#include + +#include "intel.h" #include "PartitionLocker.h" #include "PartitionMap.h" #include "PartitionMapParser.h" -#include "PartitionMapWriter.h" + +#ifndef _BOOT_MODE +# include +# include "write_support.h" +#else +# include +# include +#endif + //#define TRACE(x) ; #define TRACE(x) dprintf x // module names #define INTEL_PARTITION_MODULE_NAME "partitioning_systems/intel/map/v1" -#define INTEL_EXTENDED_PARTITION_MODULE_NAME "partitioning_systems/intel/extended/v1" +#define INTEL_EXTENDED_PARTITION_MODULE_NAME \ + "partitioning_systems/intel/extended/v1" + +using std::nothrow; + +// TODO: This doesn't belong here! // no atomic_add() in the boot loader #ifdef _BOOT_MODE @@ -59,40 +69,28 @@ atomic_add(int32 *a, int32 num) #endif -// A PartitionMap with reference count. -struct PartitionMapCookie : PartitionMap { - int32 ref_count; -}; +#ifndef _BOOT_MODE +// get_type_for_content_type (for both pm_* and ep_*) +static status_t +get_type_for_content_type(const char *contentType, char *type) +{ + TRACE(("intel: get_type_for_content_type(%s)\n", + contentType)); + + if (!contentType || !type) + return B_BAD_VALUE; -// Maximal size of move buffer (in sectors). -static const int32 MAX_MOVE_BUFFER = 2 * 1024 * 4; + PartitionType ptype; + ptype.SetContentType(contentType); + if (!ptype.IsValid()) + return B_NAME_NOT_FOUND; -// 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; + ptype.GetTypeString(type); + return B_OK; +} - -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); - - -static status_t pm_get_partitionable_spaces(partition_data *partition, - partitionable_space_data *buffer, int32 count, int32 *actualCount); -static status_t ep_get_partitionable_spaces(partition_data *partition, - partitionable_space_data *buffer, int32 count, int32 *actualCount); +#endif // #pragma mark - Intel Partition Map Module @@ -258,1220 +256,6 @@ pm_free_partition_content_cookie(partition_data *partition) } } -#ifndef _BOOT_MODE - - -// #pragma mark - Intel Partition Map - support functions - - -// pm_get_supported_operations -static 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 -static 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 -static 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 - - -// block_align (auxiliary function) -static inline -off_t -block_align(off_t offset, uint32 block_size) -{ - return offset / block_size * block_size; -} - -// block_align_up (auxiliary function) -static inline -off_t -block_align_up(off_t offset, uint32 block_size) -{ - return (offset + block_size - 1) / block_size * block_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 = block_align(new_size, partition->block_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 = block_align_up(new_size, partition->block_size); - *size = new_size; - return true; -} - -// pm_validate_resize -static 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 = block_align(*size, partition->block_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 = block_align(*size, partition->block_size); - return true; -} - -// pm_validate_resize_child -static 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 -static 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 = block_align(start, partition->block_size); - - // 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 = block_align_up(start, partition->block_size); - } - } else { - // moving right - if (nextSibling && nextOffset < start + childSize) { - start = nextOffset - childSize; - start = block_align(start, partition->block_size); - } - } - *_start = start; - return true; -} - - -// pm_validate_move_child -static 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 -static 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 -static 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 - we cannot set it to the intel partitioning map - // TODO: check parameters - don't know whether any parameters could be set - // to the intel partition map - 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 = block_align(*start, partition->block_size); - if (*size < 0) - *size = 0; - else - *size = block_align(*size, partition->block_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 = block_align_up(*start, partition->block_size); - } - - if (nextSibling && (nextOffset < *start + *size)) - *size = nextOffset - *start; - *size = block_align(*size, partition->block_size); - if (*size == 0) - return false; - - return true; -} - -// pm_validate_create_child -/*! - index - returns position of the new partition (first free record in MBR) -*/ -static 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 * partition->block_size) { - *start = partition->offset + MBR_OFFSET * partition->block_size; - *start = block_align_up(*start, partition->block_size); - } - - 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 = block_align_up(offset, partition->block_size); - - // finding out all partitionable spaces - for (int32 i = 0; i < partition_count; i++) { - size = positions[i].offset - offset; - size = block_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 = block_align_up(offset, partition->block_size); - } - // space in the end of partition - size = partition->offset + partition->size - offset; - size = block_align(size, partition->block_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 -static 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); -} - -// pm_get_next_supported_type -static 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; -} - -// get_type_for_content_type (for both pm_* and ep_*) -static status_t -get_type_for_content_type(const char *contentType, char *type) -{ - TRACE(("intel: get_type_for_content_type(%s)\n", - contentType)); - - if (!contentType || !type) - return B_BAD_VALUE; - - PartitionType ptype; - ptype.SetContentType(contentType); - if (!ptype.IsValid()) - return B_NAME_NOT_FOUND; - - ptype.GetTypeString(type); - return B_OK; -} - -// pm_shadow_changed -static 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 -static 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 -static 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, partition->block_size); - status_t error = writer.WriteMBR(NULL, map); - 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 -static 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 -static 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(partition->block_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 * partition->block_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, partition->block_size); - error = writer.WriteMBR(NULL, map); - 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 -static 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, partition->block_size); - status_t error = writer.WriteMBR(NULL, map); - 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 -static status_t -pm_initialize(int fd, partition_id partitionID, const char *name, - const char *parameters, disk_job_id job) -{ - TRACE(("intel: pm_initialize\n")); - - if (fd < 0) - return B_ERROR; - - 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; - - // name is ignored - we cannot set it to the intel partitioning map -// TODO: The parameter has already been checked and must not be altered! - if (!pm_validate_initialize(partition, NULL, parameters)) - return B_BAD_VALUE; - - // partition init - update_disk_device_job_progress(job, 0.0); - // allocate a PartitionMap - PartitionMapCookie *map = new(nothrow) PartitionMapCookie; - if (!map) - return B_NO_MEMORY; - map->ref_count = 1; - // 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 = map; - - for (int32 i = 0; i < 4; i++) { - PrimaryPartition *primary = map->PrimaryPartitionAt(i); - primary->Unset(); - } - - // we delete code area in MBR, if there is any -// TODO: Huh?! - partition_table_sector pts; - pts.clear_code_area(); - - PartitionMapWriter writer(fd, 0, partition->size, partition->block_size); -// TODO: The partition is not supposed to be locked at this point! - status_t error = writer.WriteMBR((uint8*)&pts, map); - 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 -static 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, partition->block_size); -// TODO: The partition is not supposed to be locked at this point! - status_t error = writer.WriteMBR(NULL, map); - 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 = partition->block_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 -static 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, partition->block_size); -// TODO: The partition is not supposed to be locked at this point! - status_t error = writer.WriteMBR(NULL, map); - if (error != B_OK) - return error; - - // all changes applied - update_disk_device_job_progress(job, 1.0); - partition_modified(partitionID); - return B_OK; -} - -#endif // !_BOOT_MODE - - // #pragma mark - Intel Extended Partition Module @@ -1612,769 +396,6 @@ ep_free_partition_content_cookie(partition_data *partition) } -#ifndef _BOOT_MODE - - -// #pragma mark - Intel Extended Partition - support functions - - -// ep_get_supported_operations -static 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 -static 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 -static 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 -static 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 -static 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 -static 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 -static 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 -static 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 -static 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 -static 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 * partition->block_size; - off_t start = *_start - diffOffset; - off_t size = *_size + diffOffset; - if (start < partition->offset + PTS_OFFSET * partition->block_size) { - start = partition->offset + PTS_OFFSET * partition->block_size; - start = block_align_up(start, partition->block_size); - } - 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 -static 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); -} - -// ep_get_next_supported_type -static 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 -static 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 -static 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 -static 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, partition->block_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 -static 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 -static 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(partition->block_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 * partition->block_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, partition->block_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 -static 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, - partition->block_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 -static status_t -ep_initialize(int fd, partition_id partitionID, const char *name, - const char *parameters, 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, partition->block_size); -// TODO: The partition is not supposed to be locked here! - status_t error = writer.WriteExtendedHead((uint8*)&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 -*/ -static 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 * partition->block_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, - partition->block_size); -// TODO: The partition is not supposed to be locked here! - status_t error = writer.WriteLogical((uint8*)&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 = partition->block_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 -static 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, partition->block_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; -} - -#endif // !_BOOT_MODE - - // #pragma mark - modules diff --git a/src/add-ons/kernel/partitioning_systems/intel/intel.h b/src/add-ons/kernel/partitioning_systems/intel/intel.h new file mode 100644 index 0000000000..c5a33964d3 --- /dev/null +++ b/src/add-ons/kernel/partitioning_systems/intel/intel.h @@ -0,0 +1,20 @@ +/* + * Copyright 2007, Ingo Weinhold, bonefish@cs.tu-berlin.de. All rights reserved. + * + * Distributed under the terms of the MIT License. + */ +#ifndef INTEL_H +#define INTEL_H + +#include + +#include "PartitionMap.h" + + +// A PartitionMap with reference count. +struct PartitionMapCookie : PartitionMap { + int32 ref_count; +}; + + +#endif // INTEL_H diff --git a/src/add-ons/kernel/partitioning_systems/intel/write_support.cpp b/src/add-ons/kernel/partitioning_systems/intel/write_support.cpp new file mode 100644 index 0000000000..306e5501a7 --- /dev/null +++ b/src/add-ons/kernel/partitioning_systems/intel/write_support.cpp @@ -0,0 +1,2009 @@ +/* + * Copyright 2003-2007, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Ingo Weinhold, bonefish@cs.tu-berlin.de + * Tomas Kucera, kucerat@centrum.cz + */ + +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include + +#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 + + +// block_align (auxiliary function) +static inline +off_t +block_align(off_t offset, uint32 block_size) +{ + return offset / block_size * block_size; +} + +// block_align_up (auxiliary function) +static inline +off_t +block_align_up(off_t offset, uint32 block_size) +{ + return (offset + block_size - 1) / block_size * block_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 = block_align(new_size, partition->block_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 = block_align_up(new_size, partition->block_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 = block_align(*size, partition->block_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 = block_align(*size, partition->block_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 = block_align(start, partition->block_size); + + // 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 = block_align_up(start, partition->block_size); + } + } else { + // moving right + if (nextSibling && nextOffset < start + childSize) { + start = nextOffset - childSize; + start = block_align(start, partition->block_size); + } + } + *_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 - we cannot set it to the intel partitioning map + // TODO: check parameters - don't know whether any parameters could be set + // to the intel partition map + 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 = block_align(*start, partition->block_size); + if (*size < 0) + *size = 0; + else + *size = block_align(*size, partition->block_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 = block_align_up(*start, partition->block_size); + } + + if (nextSibling && (nextOffset < *start + *size)) + *size = nextOffset - *start; + *size = block_align(*size, partition->block_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 * partition->block_size) { + *start = partition->offset + MBR_OFFSET * partition->block_size; + *start = block_align_up(*start, partition->block_size); + } + + 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 = block_align_up(offset, partition->block_size); + + // finding out all partitionable spaces + for (int32 i = 0; i < partition_count; i++) { + size = positions[i].offset - offset; + size = block_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 = block_align_up(offset, partition->block_size); + } + // space in the end of partition + size = partition->offset + partition->size - offset; + size = block_align(size, partition->block_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 * partition->block_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, partition->block_size); + status_t error = writer.WriteMBR(NULL, map); + 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(partition->block_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 * partition->block_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, partition->block_size); + error = writer.WriteMBR(NULL, map); + 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, partition->block_size); + status_t error = writer.WriteMBR(NULL, map); + 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, disk_job_id job) +{ + TRACE(("intel: pm_initialize\n")); + + if (fd < 0) + return B_ERROR; + + 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; + + // name is ignored - we cannot set it to the intel partitioning map +// TODO: The parameter has already been checked and must not be altered! + if (!pm_validate_initialize(partition, NULL, parameters)) + return B_BAD_VALUE; + + // partition init + update_disk_device_job_progress(job, 0.0); + // allocate a PartitionMap + PartitionMapCookie *map = new(nothrow) PartitionMapCookie; + if (!map) + return B_NO_MEMORY; + map->ref_count = 1; + // 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 = map; + + for (int32 i = 0; i < 4; i++) { + PrimaryPartition *primary = map->PrimaryPartitionAt(i); + primary->Unset(); + } + + // we delete code area in MBR, if there is any +// TODO: Huh?! + partition_table_sector pts; + pts.clear_code_area(); + + PartitionMapWriter writer(fd, 0, partition->size, partition->block_size); +// TODO: The partition is not supposed to be locked at this point! + status_t error = writer.WriteMBR((uint8*)&pts, map); + 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, partition->block_size); +// TODO: The partition is not supposed to be locked at this point! + status_t error = writer.WriteMBR(NULL, map); + 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 = partition->block_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, partition->block_size); +// TODO: The partition is not supposed to be locked at this point! + status_t error = writer.WriteMBR(NULL, map); + 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 * partition->block_size; + off_t start = *_start - diffOffset; + off_t size = *_size + diffOffset; + if (start < partition->offset + PTS_OFFSET * partition->block_size) { + start = partition->offset + PTS_OFFSET * partition->block_size; + start = block_align_up(start, partition->block_size); + } + 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 * partition->block_size, + PTS_OFFSET * partition->block_size, + PTS_OFFSET * partition->block_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, partition->block_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(partition->block_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 * partition->block_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, partition->block_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, + partition->block_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, 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, partition->block_size); +// TODO: The partition is not supposed to be locked here! + status_t error = writer.WriteExtendedHead((uint8*)&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 * partition->block_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, + partition->block_size); +// TODO: The partition is not supposed to be locked here! + status_t error = writer.WriteLogical((uint8*)&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 = partition->block_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, partition->block_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; +} diff --git a/src/add-ons/kernel/partitioning_systems/intel/write_support.h b/src/add-ons/kernel/partitioning_systems/intel/write_support.h new file mode 100644 index 0000000000..5440f6ea5f --- /dev/null +++ b/src/add-ons/kernel/partitioning_systems/intel/write_support.h @@ -0,0 +1,101 @@ +/* + * Copyright 2007, Ingo Weinhold, bonefish@cs.tu-berlin.de. All rights reserved. + * + * Distributed under the terms of the MIT License. + */ +#ifndef INTEL_WRITE_SUPPORT_H +#define INTEL_WRITE_SUPPORT_H + +#include + + +uint32 pm_get_supported_operations(partition_data* partition, + uint32 mask = ~0); +uint32 pm_get_supported_child_operations(partition_data* partition, + partition_data* child, uint32 mask = ~0); +bool pm_is_sub_system_for(partition_data *partition); + +bool pm_validate_resize(partition_data *partition, off_t *size); +bool pm_validate_resize_child(partition_data *partition, + partition_data *child, off_t *size); +bool pm_validate_move(partition_data *partition, off_t *start); +bool pm_validate_move_child(partition_data *partition, + partition_data *child, off_t *start); +bool pm_validate_set_type(partition_data *partition, const char *type); +bool pm_validate_initialize(partition_data *partition, char *name, + const char *parameters); +bool pm_validate_create_child(partition_data *partition, off_t *start, + off_t *size, const char *type, const char *parameters, + int32 *index); + +status_t pm_get_partitionable_spaces(partition_data *partition, + partitionable_space_data *buffer, int32 count, + int32 *actualCount); +status_t pm_get_next_supported_type(partition_data *partition, int32 *cookie, + char *_type); +status_t pm_shadow_changed(partition_data *partition, uint32 operation); + +status_t pm_resize(int fd, partition_id partitionID, off_t size, + disk_job_id job); +status_t pm_resize_child(int fd, partition_id partitionID, off_t size, + disk_job_id job); +status_t pm_move(int fd, partition_id partitionID, off_t offset, + disk_job_id job); +status_t pm_move_child(int fd, partition_id partitionID, + partition_id childID, off_t offset, disk_job_id job); +status_t pm_set_type(int fd, partition_id partitionID, const char *type, + disk_job_id job); +status_t pm_initialize(int fd, partition_id partitionID, const char *name, + const char *parameters, disk_job_id job); +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); +status_t pm_delete_child(int fd, partition_id partitionID, + partition_id childID, disk_job_id job); + + +uint32 ep_get_supported_operations(partition_data* partition, + uint32 mask = ~0); +uint32 ep_get_supported_child_operations(partition_data* partition, + partition_data* child, uint32 mask = ~0); +bool ep_is_sub_system_for(partition_data *partition); + +bool ep_validate_resize(partition_data *partition, off_t *size); +bool ep_validate_resize_child(partition_data *partition, + partition_data *child, off_t *_size); +bool ep_validate_move(partition_data *partition, off_t *start); +bool ep_validate_move_child(partition_data *partition, + partition_data *child, off_t *_start); +bool ep_validate_set_type(partition_data *partition, const char *type); +bool ep_validate_initialize(partition_data *partition, char *name, + const char *parameters); +bool ep_validate_create_child(partition_data *partition, off_t *_start, + off_t *_size, const char *type, const char *parameters, + int32 *index); +status_t ep_get_partitionable_spaces(partition_data *partition, + partitionable_space_data *buffer, int32 count, + int32 *actualCount); +status_t ep_get_next_supported_type(partition_data *partition, int32 *cookie, + char *_type); +status_t ep_shadow_changed(partition_data *partition, uint32 operation); + +status_t ep_resize(int fd, partition_id partitionID, off_t size, + disk_job_id job); +status_t ep_resize_child(int fd, partition_id partitionID, off_t size, + disk_job_id job); +status_t ep_move(int fd, partition_id partitionID, off_t offset, + disk_job_id job); +status_t ep_move_child(int fd, partition_id partitionID, + partition_id childID, off_t offset, disk_job_id job); +status_t ep_set_type(int fd, partition_id partitionID, const char *type, + disk_job_id job); +status_t ep_initialize(int fd, partition_id partitionID, const char *name, + const char *parameters, disk_job_id job); +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); +status_t ep_delete_child(int fd, partition_id partitionID, + partition_id childID, disk_job_id job); + + +#endif // INTEL_WRITE_SUPPORT_H