mirror of
https://gitlab.com/sortix/sortix.git
synced 2023-02-13 20:55:38 -05:00
238 lines
6.8 KiB
C
238 lines
6.8 KiB
C
/*******************************************************************************
|
|
|
|
Copyright(C) Jonas 'Sortie' Termansen 2015.
|
|
|
|
This file is part of Sortix libmount.
|
|
|
|
Sortix libmount is free software: you can redistribute it and/or modify it
|
|
under the terms of the GNU Lesser General Public License as published by the
|
|
Free Software Foundation, either version 3 of the License, or (at your
|
|
option) any later version.
|
|
|
|
Sortix libmount is distributed in the hope that it will be useful, but
|
|
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
|
License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public License
|
|
along with Sortix libmount. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
partition.c
|
|
Partition abstraction.
|
|
|
|
*******************************************************************************/
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <assert.h>
|
|
#include <endian.h>
|
|
#include <errno.h>
|
|
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include <mount/blockdevice.h>
|
|
#include <mount/gpt.h>
|
|
#include <mount/mbr.h>
|
|
#include <mount/partition.h>
|
|
|
|
#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);
|
|
}
|