/* * Copyright (c) 2015, 2018 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. * * mbr.c * Master Boot Record. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "util.h" void mbr_partition_decode(struct mbr_partition* v) { // flags is endian agnostic. // start_head is endian agnostic. v->start_sector_cylinder = le16toh(v->start_sector_cylinder); v->end_sector_cylinder = le16toh(v->end_sector_cylinder); // system_id is endian agnostic. // end_head is endian agnostic. v->start_sector = le32toh(v->start_sector); v->total_sectors = le32toh(v->total_sectors); } void mbr_partition_encode(struct mbr_partition* v) { v->start_sector_cylinder = htole16(v->start_sector_cylinder); v->end_sector_cylinder = htole16(v->end_sector_cylinder); // flags is endian agnostic. // start_head is endian agnostic. v->start_sector = htole32(v->start_sector); v->total_sectors = htole32(v->total_sectors); // system_id is endian agnostic. // end_head is endian agnostic. } void mbr_partition_table_release(struct mbr_partition_table* pt) { if ( !pt ) return; free(pt->ebr_chain); free(pt); } static bool mbr_is_extended_partition(const struct mbr_partition* partition) { return partition->system_id == 0x05 || // CHS addressing. partition->system_id == 0x0F; // LBA addressing. } bool mbr_is_partition_used(const struct mbr_partition* partition) { if ( memiszero(partition, sizeof(*partition)) ) return false; if ( !partition->system_id || !partition->total_sectors ) return false; return true; } enum partition_error blockdevice_get_partition_table_mbr(struct partition_table** pt_ptr, struct blockdevice* bdev) { *pt_ptr = NULL; blksize_t logical_block_size = 512; if ( !blockdevice_check_reasonable_block_size(logical_block_size) ) return errno = EINVAL, PARTITION_ERROR_ERRNO; off_t device_size = blockdevice_size(bdev); const char* device_path = bdev->p ? bdev->p->path : bdev->hd->path; struct mbr mbr; if ( blockdevice_preadall(bdev, &mbr, sizeof(mbr), 0) != sizeof(mbr) ) return PARTITION_ERROR_ERRNO; if ( mbr.signature[0] != 0x55 && mbr.signature[1] != 0xAA ) return errno = EINVAL, PARTITION_ERROR_ERRNO; struct partition_table* pt = CALLOC_TYPE(struct partition_table); if ( !pt ) return PARTITION_ERROR_ERRNO; *pt_ptr = pt; pt->type = PARTITION_TABLE_TYPE_MBR; size_t pt__partitions_length = 0; pt->usable_start = logical_block_size; off_t last_sector = device_size / logical_block_size; if ( UINT32_MAX + 1LL < last_sector ) last_sector = UINT32_MAX + 1LL; pt->usable_end = last_sector * logical_block_size; struct mbr_partition_table* mbrpt = CALLOC_TYPE(struct mbr_partition_table); if ( !mbrpt ) return PARTITION_ERROR_ERRNO; pt->raw_partition_table = mbrpt; memcpy(&mbrpt->mbr, &mbr, sizeof(mbr)); unsigned int extended_partition_count = 0; for ( unsigned int i = 0; i < 4; i++ ) { struct mbr_partition pmbr; memcpy(&pmbr, mbr.partitions[i], sizeof(pmbr)); mbr_partition_decode(&pmbr); if ( mbr_is_partition_used(&pmbr) && mbr_is_extended_partition(&pmbr) ) extended_partition_count++; } if ( 2 <= extended_partition_count ) // Violates assumptions. return PARTITION_ERROR_TOO_EXTENDED; for ( unsigned int i = 0; i < 4; i++ ) { struct mbr_partition pmbr; memcpy(&pmbr, mbr.partitions[i], sizeof(pmbr)); mbr_partition_decode(&pmbr); if ( !mbr_is_partition_used(&pmbr) ) continue; // TODO: Potential overflow. if ( pmbr.start_sector == 0 ) return PARTITION_ERROR_BEFORE_USABLE; off_t start = (off_t) pmbr.start_sector * (off_t) logical_block_size; off_t length = (off_t) pmbr.total_sectors * (off_t) logical_block_size; if ( device_size < start || device_size - start < length ) return PARTITION_ERROR_BEYOND_DEVICE; for ( size_t j = 0; j < pt->partitions_count; j++ ) if ( partition_check_overlap(pt->partitions[j], start, length) ) return PARTITION_ERROR_OVERLAP; struct partition* p = CALLOC_TYPE(struct partition); if ( !p ) return PARTITION_ERROR_ERRNO; memset(&p->bdev, 0, sizeof(p->bdev)); p->bdev.p = p; p->parent_bdev = bdev; p->index = 1 + i; p->start = start; p->length = length; p->type = PARTITION_TYPE_PRIMARY; if ( mbr_is_extended_partition(&pmbr) ) { p->extended_start = start; p->extended_length = length; p->type = PARTITION_TYPE_EXTENDED; } p->table_type = PARTITION_TABLE_TYPE_MBR; p->mbr_system_id = pmbr.system_id; if ( !array_add((void***) &pt->partitions, &pt->partitions_count, &pt__partitions_length, p) ) return free(p), PARTITION_ERROR_ERRNO; if ( device_path && asprintf(&p->path, "%sp%u", device_path, p->index) < 0 ) return PARTITION_ERROR_ERRNO; } for ( unsigned int i = 0; i < 4; i++ ) { struct mbr_partition pextmbr; memcpy(&pextmbr, mbr.partitions[i], sizeof(pextmbr)); mbr_partition_decode(&pextmbr); if ( !mbr_is_partition_used(&pextmbr) ) continue; if ( !mbr_is_extended_partition(&pextmbr) ) continue; // TODO: Potential overflow. off_t pext_start = (off_t) pextmbr.start_sector * (off_t) logical_block_size; off_t pext_length = (off_t) pextmbr.total_sectors * (off_t) logical_block_size; off_t ebr_rel = 0; unsigned int j = 0; while ( true ) { struct mbr ebr; if ( pext_length <= ebr_rel ) return PARTITION_ERROR_BEYOND_EXTENDED; off_t ebr_off = pext_start + ebr_rel; if ( blockdevice_preadall(bdev, &ebr, sizeof(ebr), ebr_off) != sizeof(ebr) ) return PARTITION_ERROR_ERRNO; if ( ebr.signature[0] != 0x55 && ebr.signature[1] != 0xAA ) return PARTITION_ERROR_BAD_EXTENDED; size_t new_chain_count = mbrpt->ebr_chain_count + 1; size_t chain_size = sizeof(struct mbr_ebr_link) * new_chain_count; struct mbr_ebr_link* new_chain = (struct mbr_ebr_link*) realloc(mbrpt->ebr_chain, chain_size); if ( !new_chain ) return PARTITION_ERROR_ERRNO; mbrpt->ebr_chain = new_chain; mbrpt->ebr_chain_count = new_chain_count; memcpy(&mbrpt->ebr_chain[j].ebr, &ebr, sizeof(ebr)); mbrpt->ebr_chain[j].offset = ebr_off; struct mbr_partition pmbr; memcpy(&pmbr, ebr.partitions[0], sizeof(pmbr)); mbr_partition_decode(&pmbr); if ( mbr_is_partition_used(&pmbr) ) { // TODO: Potential overflow. off_t start = (off_t) pmbr.start_sector * (off_t) logical_block_size; off_t length = (off_t) pmbr.total_sectors * (off_t) logical_block_size; if ( pext_length - ebr_rel < start ) return PARTITION_ERROR_BEYOND_EXTENDED; off_t max_length = (pext_length - ebr_rel) - start; if ( max_length < length ) return PARTITION_ERROR_BEYOND_EXTENDED; struct partition* p = CALLOC_TYPE(struct partition); if ( !p ) return PARTITION_ERROR_ERRNO; memset(&p->bdev, 0, sizeof(p->bdev)); p->bdev.p = p; p->parent_bdev = bdev; p->index = 5 + j; p->start = ebr_off + start; p->length = length; p->type = PARTITION_TYPE_LOGICAL; p->table_type = PARTITION_TABLE_TYPE_MBR; p->mbr_system_id = pmbr.system_id; if ( !array_add((void***) &pt->partitions, &pt->partitions_count, &pt__partitions_length, p) ) return free(p), PARTITION_ERROR_ERRNO; if ( device_path && asprintf(&p->path, "%sp%u", device_path, p->index) < 0 ) return PARTITION_ERROR_ERRNO; } j++; struct mbr_partition next; memcpy(&next, ebr.partitions[1], sizeof(next)); mbr_partition_decode(&next); // TODO: Potential overflow. off_t next_rel = (off_t) next.start_sector * (off_t) logical_block_size; if ( !next_rel ) break; if ( next_rel <= ebr_rel ) // Violates assumptions. return PARTITION_ERROR_NONLINEAR_EXTENDED; if ( pext_length <= next_rel ) return PARTITION_ERROR_BEYOND_EXTENDED; ebr_rel = next_rel; } break; } return PARTITION_ERROR_NONE; }