/* * Copyright 2003-2011, Haiku, Inc. All Rights Reserved. * Distributed under the terms of the MIT License. * * Authors: * Ingo Weinhold, ingo_weinhold@gmx.de * Tomas Kucera, kucerat@centrum.cz */ #include "write_support.h" #include #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 = 63; // Count of free sectors after Master Boot Record. static const uint32 FREE_SECTORS_AFTER_MBR = 63; // 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) { 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_BUFFER_OVERFLOW && 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) { 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; } bool get_partition_from_offset_ep(partition_data* partition, off_t offset, partition_data** nextPartition) { for (int32 i = 0; i < partition->child_count; i++) { partition_data* sibling = get_child_partition(partition->id, i); if (sibling != NULL && sibling->offset == offset) { *nextPartition = sibling; return true; } } return false; } // #pragma mark - Intel Partition Map - validate functions // sector_align (auxiliary function) static inline off_t sector_align(off_t offset, int32 blockSize) { return offset / blockSize * blockSize; } // sector_align_up (auxiliary function) static inline off_t sector_align_up(off_t offset, int32 blockSize) { return (offset + blockSize - 1) / blockSize * blockSize; } // validate_resize (auxiliary function) static bool validate_resize(partition_data* partition, off_t* size) { off_t newSize = *size; // size remains the same? if (newSize == partition->size) return true; if (newSize < 0) newSize = 0; else newSize = sector_align(newSize, partition->block_size); // grow partition? if (newSize > partition->size) { *size = newSize; return true; } // shrink partition // no child has to be over the new size of the parent partition // TODO: shouldn't be just: off_t currentEnd = newSize; ??? probably not // If child->offset is relative to parent, then yes! off_t currentEnd = partition->offset + newSize; for (int32 i = 0; i < partition->child_count; i++) { partition_data* child = get_child_partition(partition->id, i); if (child && child->offset + child->size > currentEnd) currentEnd = child->offset + child->size; } newSize = currentEnd - partition->offset; // make the size a multiple of the block size (greater one) newSize = sector_align_up(newSize, partition->block_size); *size = newSize; 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_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 (sibling->offset <= 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 = previousSibling->offset; *previousSize = previousSibling->size; } if (nextSibling) { *nextOffset = nextSibling->offset; *nextSize = nextSibling->size; } } // validate_resize_child (auxiliary function) static bool validate_resize_child(partition_data* partition, partition_data* child, off_t childOffset, off_t childSize, off_t* size, fc_get_sibling_partitions getSiblingPartitions) { // size remains the same? if (*size == childSize) return true; // shrink partition? if (*size < childSize) { if (*size < 0) *size = 0; // make the size a multiple of the block size *size = sector_align(*size, 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 = sector_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 = sector_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 = sector_align_up(start, partition->block_size); } } else { // moving right if (nextSibling && nextOffset < start + childSize) { start = nextOffset - childSize; start = sector_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* parent = get_parent_partition(partition->id); if (!parent) return false; PrimaryPartition* child = (PrimaryPartition*)partition->cookie; if (!child) return false; // validity check of the type return is_type_valid_pm(type, parent, child); } // pm_validate_initialize bool pm_validate_initialize(partition_data* partition, char* name, const char* parameters) { TRACE(("intel: pm_validate_initialize\n")); if (!partition || !(pm_get_supported_operations(partition) & B_DISK_SYSTEM_SUPPORTS_INITIALIZING)) { return false; } // name is ignored if (name) name[0] = '\0'; // parameters are ignored, too return true; } // validate_create_child_partition (auxiliary function) static bool validate_create_child_partition(partition_data* partition, off_t* start, off_t* size, fc_get_sibling_partitions getSiblingPartitions) { // make the start and size a multiple of the block size *start = sector_align(*start, partition->block_size); if (*size < 0) *size = 0; else *size = sector_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 = sector_align_up(*start, partition->block_size); } if (nextSibling && (nextOffset < *start + *size)) *size = nextOffset - *start; *size = sector_align(*size, partition->block_size); if (*size == 0) return false; return true; } // 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* name, const char* parameters, int32* index) { TRACE(("intel: pm_validate_create_child\n")); if (!partition || !(pm_get_supported_operations(partition) & B_DISK_SYSTEM_SUPPORTS_CREATING_CHILD) || !start || !size || !type || !index) { return false; } // TODO: check name // 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 = sector_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; 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 = child->offset; positions[partition_count].size = child->size; partition_count++; } } return partition_count; } // get_partitionable_spaces (auxiliary function) static status_t get_partitionable_spaces(partition_data* partition, partitionable_space_data* buffer, int32 count, int32* _actualCount, fc_fill_partitionable_spaces_buffer fillBuffer, off_t startOffset, off_t limitSize = 0, off_t headerSize = 0) { PartitionPosition* positions = new(nothrow) PartitionPosition[partition->child_count]; if (!positions) return B_NO_MEMORY; // fill the array int32 partition_count = fillBuffer(partition, positions); // sort the array qsort(positions, partition_count, sizeof(PartitionPosition), cmp_partition_position); // first sektor is MBR or EBR off_t offset = startOffset + headerSize; off_t size = 0; int32 actualCount = 0; // offset alignment (to upper bound) offset = sector_align_up(offset, partition->block_size); // finding out all partitionable spaces for (int32 i = 0; i < partition_count; i++) { size = positions[i].offset - offset; size = sector_align(size, partition->block_size); if (size >= limitSize) { if (actualCount < count) { buffer[actualCount].offset = offset; buffer[actualCount].size = size; } actualCount++; } offset = positions[i].offset + positions[i].size + headerSize; offset = sector_align_up(offset, partition->block_size); } // space in the end of partition size = partition->offset + partition->size - offset; size = sector_align(size, partition->block_size); if (size > 0) { if (actualCount < count) { buffer[actualCount].offset = offset; buffer[actualCount].size = size; } actualCount++; } // cleanup if (positions) delete[] positions; TRACE(("intel: get_partitionable_spaces - found: %" B_PRId32 "\n", actualCount)); *_actualCount = actualCount; if (count < actualCount) return B_BUFFER_OVERFLOW; return B_OK; } // 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, partition_data* child, uint32 operation) { TRACE(("intel: pm_shadow_changed(%p, %p, %" B_PRIu32 ")\n", partition, child, operation)); switch (operation) { case B_PARTITION_SHADOW: { // get the physical partition partition_data* physicalPartition = get_partition( partition->id); if (!physicalPartition) { dprintf("intel: pm_shadow_changed(B_PARTITION_SHADOW): no " "physical partition with ID %" B_PRId32 "\n", partition->id); return B_ERROR; } // clone the map if (!physicalPartition->content_cookie) { dprintf("intel: pm_shadow_changed(B_PARTITION_SHADOW): no " "content cookie, physical partition: %" B_PRId32 "\n", partition->id); return B_ERROR; } PartitionMapCookie* map = new(nothrow) PartitionMapCookie; if (!map) return B_NO_MEMORY; status_t error = map->Assign( *(PartitionMapCookie*)physicalPartition->content_cookie); if (error != B_OK) { delete map; return error; } partition->content_cookie = map; return B_OK; } case B_PARTITION_SHADOW_CHILD: { // get the physical child partition partition_data* physical = get_partition(child->id); if (!physical) { dprintf("intel: pm_shadow_changed(B_PARTITION_SHADOW_CHILD): " "no physical partition with ID %" B_PRId32 "\n", child->id); return B_ERROR; } if (!physical->cookie) { dprintf("intel: pm_shadow_changed(B_PARTITION_SHADOW_CHILD): " "no cookie, physical partition: %" B_PRId32 "\n", child->id); return B_ERROR; } // primary partition index int32 index = ((PrimaryPartition*)physical->cookie)->Index(); if (!partition->content_cookie) { dprintf("intel: pm_shadow_changed(B_PARTITION_SHADOW_CHILD): " "no content cookie, physical partition: %" B_PRId32 "\n", partition->id); return B_ERROR; } // get the primary partition PartitionMapCookie* map = ((PartitionMapCookie*)partition->content_cookie); PrimaryPartition* primary = map->PrimaryPartitionAt(index); if (!primary || primary->IsEmpty()) { dprintf("intel: pm_shadow_changed(B_PARTITION_SHADOW_CHILD): " "partition %" B_PRId32 " is empty, primary index: " "%" B_PRId32 "\n", child->id, index); return B_BAD_VALUE; } child->cookie = primary; return B_OK; } case B_PARTITION_INITIALIZE: { // create an empty partition map PartitionMapCookie* map = new(nothrow) PartitionMapCookie; if (!map) return B_NO_MEMORY; partition->content_cookie = map; return B_OK; } case B_PARTITION_CREATE_CHILD: { if (!partition->content_cookie) { dprintf("intel: pm_shadow_changed(B_PARTITION_CREATE_CHILD): " "no content cookie, partition: %" B_PRId32 "\n", partition->id); return B_ERROR; } PartitionMapCookie* map = ((PartitionMapCookie*)partition->content_cookie); // find an empty primary partition slot PrimaryPartition* primary = NULL; for (int32 i = 0; i < 4; i++) { if (map->PrimaryPartitionAt(i)->IsEmpty()) { primary = map->PrimaryPartitionAt(i); break; } } if (!primary) { dprintf("intel: pm_shadow_changed(B_PARTITION_CREATE_CHILD): " "no empty primary slot, partition: %" B_PRId32 "\n", partition->id); return B_ERROR; } // apply type PartitionType type; type.SetType(child->type); if (!type.IsValid()) { dprintf("intel: pm_shadow_changed(B_PARTITION_CREATE_CHILD): " "invalid partition type, partition: %" B_PRId32 "\n", partition->id); return B_ERROR; } primary->SetType(type.Type()); // TODO: Apply parameters! child->cookie = primary; return B_OK; } case B_PARTITION_DEFRAGMENT: case B_PARTITION_REPAIR: case B_PARTITION_RESIZE: case B_PARTITION_RESIZE_CHILD: case B_PARTITION_MOVE: case B_PARTITION_MOVE_CHILD: case B_PARTITION_SET_NAME: case B_PARTITION_SET_CONTENT_NAME: case B_PARTITION_SET_TYPE: case B_PARTITION_SET_PARAMETERS: case B_PARTITION_SET_CONTENT_PARAMETERS: case B_PARTITION_DELETE_CHILD: break; } return B_ERROR; } // #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, primary->BlockSize()); // TODO: disk size? status_t error = writer.WriteMBR(map, false); if (error != B_OK) { // putting into previous state primary->SetSize(child->size); return error; } child->size = validatedSize; // all changes applied update_disk_device_job_progress(job, 1.0); partition_modified(partitionID); return B_OK; } // pm_move status_t pm_move(int fd, partition_id partitionID, off_t offset, disk_job_id job) { TRACE(("intel: pm_move\n")); if (fd < 0) return B_ERROR; // TODO: Should be a no-op! PartitionWriteLocker locker(partitionID); if (!locker.IsLocked()) return B_ERROR; // get out partition partition_data* partition = get_partition(partitionID); if (!partition) return B_BAD_VALUE; // validate the new start if (!pm_validate_move(partition, &offset)) return B_BAD_VALUE; // nothing to do here return B_OK; } // allocate_buffer (auxiliary function) /*! tries to allocate buffer with the size: blockSize * tryAlloc if it's not possible, tries smaller buffer (until the size: blockSize * 1) returns pointer to the buffer (it's size is: blockSize * allocated) or returns NULL - B_NO_MEMORY */ static uint8* allocate_buffer(uint32 blockSize, int32 tryAlloc, int32* allocated) { uint8* buffer = NULL; for (int32 i = tryAlloc; i > 1; i /= 2) { buffer = new(nothrow) uint8[i * blockSize]; if (buffer) { *allocated = i; return buffer; } } *allocated = 0; return NULL; } // move_block (auxiliary function) static status_t move_block(int fd, off_t fromOffset, off_t toOffset, uint8* buffer, int32 size) { status_t error = B_OK; // read block to buffer if (read_pos(fd, fromOffset, buffer, size) != size) { error = errno; if (error == B_OK) error = B_IO_ERROR; TRACE(("intel: move_block(): reading failed: %" B_PRIx32 "\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: %" B_PRIx32 "\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! // TODO: This seems to be broken if source and destination overlap. 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, partition->block_size); // TODO: disk size? error = writer.WriteMBR(map, false); if (error != B_OK) // something went wrong - this is fatal (partition has been moved) // but MBR is not updated return error; // all changes applied update_disk_device_job_progress(job, 1.0); partition_modified(childID); return B_OK; } // pm_set_type status_t pm_set_type(int fd, partition_id partitionID, const char* type, disk_job_id job) { TRACE(("intel: pm_set_type\n")); if (fd < 0 || !type) return B_BAD_VALUE; PartitionWriteLocker locker(partitionID); if (!locker.IsLocked()) return B_ERROR; // get parent partition, child and partition map structure partition_data* partition = get_parent_partition(partitionID); partition_data* child = get_partition(partitionID); if (!partition || !child) return B_BAD_VALUE; PartitionMap* map = (PartitionMap*)partition->content_cookie; PrimaryPartition* primary = (PrimaryPartition*)child->cookie; if (!map || !primary) return B_BAD_VALUE; // TODO: The parameter has already been checked and must not be altered! if (!pm_validate_set_type(child, type)) return B_BAD_VALUE; // if the old type is the same, there is nothing to do if (child->type && !strcmp(type, child->type)) return B_OK; PartitionType ptype; ptype.SetType(type); // this is impossible if (!ptype.IsValid() || ptype.IsEmpty()) return false; // TODO: Incompatible return value! // setting type to the partition update_disk_device_job_progress(job, 0.0); uint8 oldType = primary->Type(); primary->SetType(ptype.Type()); // TODO: The partition is not supposed to be locked at this point! PartitionMapWriter writer(fd, primary->BlockSize()); // TODO: disk size? status_t error = writer.WriteMBR(map, false); if (error != B_OK) { // something went wrong - putting into previous state primary->SetType(oldType); return error; } free(child->type); child->type = strdup(type); if (!child->type) return B_NO_MEMORY; // all changes applied update_disk_device_job_progress(job, 1.0); partition_modified(partitionID); return B_OK; } // pm_initialize status_t pm_initialize(int fd, partition_id partitionID, const char* name, const char* parameters, off_t partitionSize, disk_job_id job) { TRACE(("intel: pm_initialize\n")); if (fd < 0) return B_ERROR; 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; update_disk_device_job_progress(job, 0.0); // we will write an empty partition map PartitionMap map; // write the sector to disk PartitionMapWriter writer(fd, partition->block_size); // TODO: disk size or 2 * SECTOR_SIZE? status_t error = writer.WriteMBR(&map, true); if (error != B_OK) return error; // rescan partition error = scan_partition(partitionID); if (error != B_OK) return error; // all changes applied update_disk_device_job_progress(job, 1.0); partition_modified(partitionID); return B_OK; } status_t pm_uninitialize(int fd, partition_id partitionID, off_t partitionSize, uint32 blockSize, disk_job_id job) { if (blockSize == 0) return B_BAD_VALUE; // We overwrite the first block, which contains the partition table. // Allocate a buffer, we can clear and write. void* block = malloc(blockSize); if (block == NULL) return B_NO_MEMORY; MemoryDeleter blockDeleter(block); memset(block, 0, blockSize); if (write_pos(fd, 0, block, blockSize) < 0) return errno; update_disk_device_job_progress(job, 1.0); return B_OK; } // pm_create_child /*! childID is used for the return value, but is also an optional input parameter -- -1 to be ignored */ status_t pm_create_child(int fd, partition_id partitionID, off_t offset, off_t size, const char* type, const char* name, const char* parameters, disk_job_id job, partition_id* childID) { TRACE(("intel: pm_create_child\n")); if (fd < 0 || !childID) return B_BAD_VALUE; PartitionWriteLocker locker(partitionID); if (!locker.IsLocked()) return B_ERROR; // 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, name, 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, validatedOffset, validatedSize, *childID); if (!child) return B_ERROR; PartitionType ptype; ptype.SetType(type); // check parameters void* handle = parse_driver_settings_string(parameters); if (handle == NULL) return B_ERROR; bool active = get_driver_boolean_parameter(handle, "active", false, true); delete_driver_settings(handle); // set the active flags to false if (active) { for (int i = 0; i < 4; i++) { PrimaryPartition* partition = map->PrimaryPartitionAt(i); partition->SetActive(false); } } primary->SetPartitionTableOffset(0); primary->SetOffset(validatedOffset); primary->SetSize(validatedSize); primary->SetType(ptype.Type()); primary->SetActive(active); // write changes to disk PartitionMapWriter writer(fd, primary->BlockSize()); // TODO: The partition is not supposed to be locked at this point! status_t error = writer.WriteMBR(map, false); if (error != B_OK) { // putting into previous state primary->Unset(); delete_partition(child->id); return error; } *childID = child->id; child->block_size = primary->BlockSize(); // (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; // rescan partition if needed if (strcmp(type, INTEL_EXTENDED_PARTITION_NAME) == 0) { writer.ClearExtendedHead(primary); error = scan_partition(partitionID); if (error != B_OK) return error; } // all changes applied update_disk_device_job_progress(job, 1.0); partition_modified(partitionID); return B_OK; } // pm_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, primary->BlockSize()); // TODO: disk size or 2 * SECTOR_SIZE? // TODO: The partition is not supposed to be locked at this point! status_t error = writer.WriteMBR(map, false); if (error != B_OK) return error; // all changes applied update_disk_device_job_progress(job, 1.0); partition_modified(partitionID); return B_OK; } // #pragma mark - Intel Extended Partition - support functions // ep_get_supported_operations uint32 ep_get_supported_operations(partition_data* partition, uint32 mask) { 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 (ep_get_partitionable_spaces(partition, NULL, 0, &countSpaces) == B_BUFFER_OVERFLOW && 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) { 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) { if (partition == NULL) return false; TRACE(("intel: ep_is_sub_system_for(%" B_PRId32 ": %" B_PRId64 ", " "%" B_PRId64 ", %" B_PRId32 ", %s)\n", partition->id, partition->offset, partition->size, partition->block_size, partition->content_type)); // Intel Extended Partition can live in child partition of Intel Partition // Map return 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 size = *_size; if (!validate_resize_child(partition, child, child->offset, child->size, &size, get_sibling_partitions_ep)) return false; *_size = size; 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 start = *_start; if (!validate_move_child(partition, child, child->offset, child->size, &start, get_sibling_partitions_ep)) return false; *_start = start; 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* offset, off_t* size, const char* type, const char* name, const char* parameters, int32* index) // index - returns position of the new partition (the last one) { return false; } // 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, partition_data* child, uint32 operation) { TRACE(("intel: ep_shadow_changed\n")); if (!partition) return B_BAD_VALUE; // nothing to do here return B_OK; } bool check_partition_location_ep(partition_data* partition, off_t offset, off_t size, off_t ptsOffset) { if (!partition) return false; // make sure we are sector aligned off_t alignedOffset = sector_align(offset, partition->block_size); if (alignedOffset != offset) return false; // partition does not lie within extended partition if (offset < partition->offset || (offset > partition->offset + partition->size && offset + size <= partition->offset + partition->size)) return false; // check if the new partition table is within an existing partition // or that the new partition does not overwrite an existing partition // table. for (int32 i = 0; i < partition->child_count; i++) { partition_data* sibling = get_child_partition(partition->id, i); LogicalPartition* logical = (LogicalPartition*)sibling->cookie; if (logical == NULL) return false; if (ptsOffset > logical->Offset() && ptsOffset < logical->Offset() + logical->Size()) return false; if ((logical->PartitionTableOffset() >= offset && logical->PartitionTableOffset() < offset + size) || logical->PartitionTableOffset() == ptsOffset) return false; } return true; } // #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; PrimaryPartition* primary = (PrimaryPartition*)partition->cookie; if (!logical || !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 (!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->block_size); // TODO: The partition is not supposed to be locked here! status_t error = writer.WriteLogical(logical, primary, false); if (error != B_OK) { // putting into previous state logical->SetSize(child->size); return error; } LogicalPartition* prev = logical->Previous(); error = prev ? writer.WriteLogical(prev, primary, false) : writer.WriteLogical(logical, primary, false); if (error != B_OK) // 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; PrimaryPartition* primary = (PrimaryPartition*)partition->cookie; if (!logical || !primary) 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 (partition table) off_t pts_offset = logical->Offset() - logical->PartitionTableOffset(); error = move_partition(fd, child->offset - pts_offset, validatedOffset - pts_offset, child->size + pts_offset, buffer, allocated * partition->block_size, job); delete[] buffer; if (error != B_OK) return error; // partition moved // updating data structure child->offset = validatedOffset; logical->SetOffset(logical->Offset() + diffOffset); logical->SetPartitionTableOffset(logical->PartitionTableOffset() + diffOffset); PartitionMapWriter writer(fd, partition->block_size); // TODO: If partition->offset is > prev->offset, then writing // the previous logical partition table will fail! // TODO: The partition is not supposed to be locked here! error = writer.WriteLogical(logical, primary, false); 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(prev, primary, false) : writer.WriteLogical(logical, primary, false); 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; PrimaryPartition* primary = (PrimaryPartition*)partition->cookie; if (!logical || !primary) 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->block_size); // TODO: The partition is not supposed to be locked here! status_t error = writer.WriteLogical(logical, primary, false); if (error != B_OK) { // something went wrong - putting into previous state logical->SetType(oldType); return error; } free(child->type); child->type = strdup(type); if (!child->type) return B_NO_MEMORY; // all changes applied update_disk_device_job_progress(job, 1.0); partition_modified(partitionID); return B_OK; } // ep_initialize status_t ep_initialize(int fd, partition_id partitionID, const char* name, const char* parameters, off_t partitionSize, disk_job_id job) { TRACE(("intel: ep_initialize\n")); if (fd < 0) return B_ERROR; PartitionWriteLocker locker(partitionID); if (!locker.IsLocked()) return B_ERROR; // get partition partition_data* partition = get_partition(partitionID); if (!partition) return B_BAD_VALUE; PrimaryPartition* primary = (PrimaryPartition*)partition->cookie; if (!primary) 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); // 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 table; table.clear_code_area(); PartitionMapWriter writer(fd, partition->block_size); // TODO: The partition is not supposed to be locked here! status_t error = writer.ClearExtendedHead(primary); 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* name, const char* parameters, disk_job_id job, partition_id* childID) { TRACE(("intel: ep_create_child\n")); if (fd < 0 || !childID) return B_BAD_VALUE; // aquire lock PartitionWriteLocker locker(partitionID); if (!locker.IsLocked()) return B_ERROR; // get partition data partition_data* partition = get_partition(partitionID); partition_data* parent = get_parent_partition(partitionID); if (partition == NULL || parent == NULL) return B_BAD_VALUE; PrimaryPartition* primary = (PrimaryPartition*)partition->cookie; if (!primary) return B_BAD_VALUE; // parse parameters void* handle = parse_driver_settings_string(parameters); if (handle == NULL) return B_ERROR; bool active = get_driver_boolean_parameter(handle, "active", false, true); off_t ptsOffset = 0; const char* buffer = get_driver_parameter( handle, "partition_table_offset", NULL, NULL); if (buffer != NULL) ptsOffset = strtoull(buffer, NULL, 10); else { delete_driver_settings(handle); return B_BAD_VALUE; } delete_driver_settings(handle); // check the partition location if (!check_partition_location_ep(partition, offset, size, ptsOffset)) return B_BAD_VALUE; // creating partition update_disk_device_job_progress(job, 0.0); partition_data* child = create_child_partition(partition->id, partition->child_count, offset, size, *childID); if (!child) return B_ERROR; // setup logical partition LogicalPartition* logical = new(nothrow) LogicalPartition; if (!logical) return B_NO_MEMORY; PartitionType ptype; ptype.SetType(type); logical->SetPartitionTableOffset(ptsOffset - parent->offset); logical->SetOffset(offset); logical->SetSize(size); logical->SetType(ptype.Type()); logical->SetActive(active); logical->SetPrimaryPartition(primary); logical->SetBlockSize(partition->block_size); primary->AddLogicalPartition(logical); int parentFD = open_partition(parent->id, O_RDWR); if (parentFD < 0) { primary->RemoveLogicalPartition(logical); delete logical; return B_IO_ERROR; } // write changes to disk PartitionMapWriter writer(parentFD, primary->BlockSize()); // Write the logical partition's EBR first in case of failure. // This way we will not add a partition to the previous logical // partition. If there is no previous logical partition then write // the current partition's EBR to the first sector of the primary partition status_t error = writer.WriteLogical(logical, primary, true); if (error != B_OK) { primary->RemoveLogicalPartition(logical); delete logical; return error; } LogicalPartition* previous = logical->Previous(); if (previous != NULL) { error = writer.WriteLogical(previous, primary, true); if (error != B_OK) { primary->RemoveLogicalPartition(logical); delete logical; return error; } } *childID = child->id; child->block_size = logical->BlockSize(); child->type = strdup(type); child->parameters = strdup(parameters); child->cookie = logical; // 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* parent = get_parent_partition(partitionID); partition_data* child = get_partition(childID); if (partition == NULL || parent == NULL || child == NULL) return B_BAD_VALUE; PrimaryPartition* primary = (PrimaryPartition*)partition->cookie; LogicalPartition* logical = (LogicalPartition*)child->cookie; if (primary == NULL || logical == NULL) return B_BAD_VALUE; // deleting child update_disk_device_job_progress(job, 0.0); if (!delete_partition(childID)) return B_ERROR; LogicalPartition* previous = logical->Previous(); LogicalPartition* next = logical->Next(); primary->RemoveLogicalPartition(logical); delete logical; int parentFD = open_partition(parent->id, O_RDWR); if (parentFD < 0) return B_IO_ERROR; // write changes to disk PartitionMapWriter writer(parentFD, primary->BlockSize()); status_t error; if (previous != NULL) { error = writer.WriteLogical(previous, primary, true); } else { error = writer.WriteExtendedHead(next, primary, true); if (next != NULL) { next->SetPartitionTableOffset(primary->Offset()); partition_data* nextSibling = NULL; if (get_partition_from_offset_ep(partition, next->Offset(), &nextSibling)) { char buffer[128]; sprintf(buffer, "active %s ;\npartition_table_offset %" B_PRId64 " ;\n", next->Active() ? "true" : "false", next->PartitionTableOffset()); nextSibling->parameters = strdup(buffer); } } } close(parentFD); if (error != B_OK) return error; // all changes applied update_disk_device_job_progress(job, 1.0); partition_modified(partitionID); return B_OK; }