/* * Copyright (c) 2015 Jonas 'Sortie' Termansen. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * partition.c * Partition abstraction. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "util.h" int partition_compare_index(const struct partition* a, const struct partition* b) { if ( a->index < b->index ) return -1; if ( b->index < a->index ) return 1; return 0; } int partition_compare_index_indirect(const void* a_ptr, const void* b_ptr) { const struct partition* a = *(const struct partition* const*) a_ptr; const struct partition* b = *(const struct partition* const*) b_ptr; return partition_compare_index(a, b); } int partition_compare_start(const struct partition* a, const struct partition* b) { if ( a->start < b->start ) return -1; if ( b->start < a->start ) return 1; return 0; } int partition_compare_start_indirect(const void* a_ptr, const void* b_ptr) { const struct partition* a = *(const struct partition* const*) a_ptr; const struct partition* b = *(const struct partition* const*) b_ptr; return partition_compare_index(a, b); } const char* partition_error_string(enum partition_error error) { switch ( error ) { case PARTITION_ERROR_NONE: break; case PARTITION_ERROR_ABSENT: return "No partition table found"; case PARTITION_ERROR_UNRECOGNIZED: return "Unrecognized partitioning scheme"; case PARTITION_ERROR_ERRNO: return (const char*) strerror(errno); case PARTITION_ERROR_INVALID: return "Invalid partition table"; case PARTITION_ERROR_HEADER_TOO_LARGE: return "Partition table header larger than logical sector size"; case PARTITION_ERROR_CHECKSUM: return "Partition table does not match its checksum"; case PARTITION_ERROR_OVERLAP: return "Partition table contains overlapping partitions"; case PARTITION_ERROR_END_BEFORE_START: return "Invalid partition ends before its start"; case PARTITION_ERROR_BEFORE_USABLE: return "Invalid partition begins before the usable region"; case PARTITION_ERROR_BEYOND_DEVICE: return "Invalid partition exceeds device"; case PARTITION_ERROR_BEYOND_EXTENDED: return "Invalid logical partition exceeds its extended partition"; case PARTITION_ERROR_BEYOND_USABLE: return "Invalid partition ends after the usable region"; case PARTITION_ERROR_TOO_EXTENDED: return "Bad partition table (more than one extended partition)"; case PARTITION_ERROR_BAD_EXTENDED: return "Bad extended partition"; case PARTITION_ERROR_NONLINEAR_EXTENDED: return "Extended partition is not linearly linked together"; } return "Unknown error condition"; } bool partition_check_overlap(const struct partition* partition, off_t start, off_t length) { assert(0 <= start); assert(0 <= length); assert(0 <= partition->start); assert(0 <= partition->length); if ( start <= partition->start ) { off_t max_length = partition->start - start; return max_length < length; } else { off_t max_length = start - partition->start; return max_length < partition->length; } } bool blockdevice_probe_partition_table_type(enum partition_table_type* result_out, struct blockdevice* bdev) { blksize_t logical_block_size = blockdevice_logical_block_size(bdev); if ( !blockdevice_check_reasonable_block_size(logical_block_size) ) return errno = EINVAL, false; // TODO: Overflow checks for truncation, and the multiplication. size_t block_size = logical_block_size; size_t leading_size = 2 * block_size; unsigned char* leading = (unsigned char*) malloc(leading_size); if ( !leading ) return false; size_t amount = blockdevice_preadall(bdev, leading, leading_size, 0); if ( amount < leading_size && errno != EEOF ) return free(leading), false; enum partition_table_type result = PARTITION_TABLE_TYPE_NONE; do if ( 2*block_size <= amount ) { struct gpt gpt; memcpy(&gpt, leading + 1 * block_size, sizeof(gpt)); gpt_decode(&gpt); if ( memcmp(gpt.signature, "EFI PART", 8) != 0 ) break; result = PARTITION_TABLE_TYPE_GPT; goto out; } while ( 0 ); do if ( sizeof(struct mbr) <= amount ) { struct mbr mbr; memcpy(&mbr, leading, sizeof(mbr)); if ( mbr.signature[0] != 0x55 && mbr.signature[1] != 0xAA ) break; result = PARTITION_TABLE_TYPE_MBR; goto out; } while ( 0 ); for ( size_t i = 0; i < leading_size; i++ ) { if ( leading[i] == 0x00 ) continue; result = PARTITION_TABLE_TYPE_UNKNOWN; goto out; } out: free(leading); return *result_out = result, true; } enum partition_error blockdevice_get_partition_table(struct partition_table** pt_ptr, struct blockdevice* bdev) { enum partition_table_type pt_type; if ( !blockdevice_probe_partition_table_type(&pt_type, bdev) ) return *pt_ptr = NULL, PARTITION_ERROR_ERRNO; switch ( pt_type ) { case PARTITION_TABLE_TYPE_NONE: return *pt_ptr = NULL, PARTITION_ERROR_ABSENT; case PARTITION_TABLE_TYPE_UNKNOWN: break; case PARTITION_TABLE_TYPE_MBR: return blockdevice_get_partition_table_mbr(pt_ptr, bdev); case PARTITION_TABLE_TYPE_GPT: return blockdevice_get_partition_table_gpt(pt_ptr, bdev); } return *pt_ptr = NULL, PARTITION_ERROR_UNRECOGNIZED; } void partition_release(struct partition* p) { if ( !p ) return; free(p->gpt_name); free(p); } void partition_table_release(struct partition_table* pt) { if ( !pt ) return; switch ( pt->type ) { case PARTITION_TABLE_TYPE_NONE: break; case PARTITION_TABLE_TYPE_UNKNOWN: break; case PARTITION_TABLE_TYPE_MBR: mbr_partition_table_release( (struct mbr_partition_table*) pt->raw_partition_table); break; case PARTITION_TABLE_TYPE_GPT: gpt_partition_table_release( (struct gpt_partition_table*) pt->raw_partition_table); break; } for ( size_t i = 0; i < pt->partitions_count; i++ ) partition_release(pt->partitions[i]); free(pt->partitions); free(pt); }