forked from Imagelibrary/rtems
The functions o rtems_bdbuf_get(), o rtems_bdbuf_read(), o rtems_bdbuf_syncdev(), and o rtems_bdbuf_purge_dev(), use now the disk device instead of the device identifier. This makes bdbuf independent of rtems_disk_obtain() and rtems_disk_release(). It is the responsiblity of the file system to obtain the disk device. This also reduces the overhead to get a buffer. The key for the AVL tree uses now the disk device instead of the device identifier. The pointer is interpreted as an unsigned integer. This reduces the memory overhead and makes the comparison operation a bit faster. Removed function rtems_bdbuf_purge_major(). This function was too destructive and could have unpredictable side effects.
368 lines
8.2 KiB
C
368 lines
8.2 KiB
C
/**
|
|
* @file
|
|
*
|
|
* @ingroup rtems_bdpart
|
|
*
|
|
* Block device partition management.
|
|
*/
|
|
|
|
/*
|
|
* Copyright (c) 2009, 2010
|
|
* embedded brains GmbH
|
|
* Obere Lagerstr. 30
|
|
* D-82178 Puchheim
|
|
* Germany
|
|
* <rtems@embedded-brains.de>
|
|
*
|
|
* The license and distribution terms for this file may be
|
|
* found in the file LICENSE in this distribution or at
|
|
* http://www.rtems.com/license/LICENSE.
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
|
|
#include <rtems.h>
|
|
#include <rtems/bdbuf.h>
|
|
#include <rtems/bdpart.h>
|
|
#include <rtems/endian.h>
|
|
|
|
#define RTEMS_BDPART_MBR_PARTITION_TYPE( type) \
|
|
{ \
|
|
(type), 0xa2U, 0x2eU, 0x38U, \
|
|
0x38U, 0xb5U, 0xdeU, 0x11U, \
|
|
0xbcU, 0x13U, 0x00U, 0x1dU, \
|
|
0x09U, 0xb0U, 0x5fU, 0xa4U \
|
|
}
|
|
|
|
static const uuid_t RTEMS_BDPART_MBR_MASTER_TYPE =
|
|
RTEMS_BDPART_MBR_PARTITION_TYPE( RTEMS_BDPART_MBR_EMPTY);
|
|
|
|
void rtems_bdpart_to_partition_type( uint8_t mbr_type, uuid_t type)
|
|
{
|
|
type [0] = mbr_type;
|
|
memcpy( type + 1, RTEMS_BDPART_MBR_MASTER_TYPE + 1, sizeof( uuid_t) - 1);
|
|
}
|
|
|
|
bool rtems_bdpart_to_mbr_partition_type(
|
|
const uuid_t type,
|
|
uint8_t *mbr_type
|
|
)
|
|
{
|
|
*mbr_type = rtems_bdpart_mbr_partition_type( type);
|
|
|
|
return memcmp(
|
|
type + 1,
|
|
RTEMS_BDPART_MBR_MASTER_TYPE + 1,
|
|
sizeof( uuid_t) - 1
|
|
) == 0;
|
|
}
|
|
|
|
/*
|
|
* FIXME: This code should the deviceio interface and not the bdbug interface.
|
|
*/
|
|
rtems_status_code rtems_bdpart_get_disk_data(
|
|
const char *disk_name,
|
|
int *fd_ptr,
|
|
const rtems_disk_device **dd_ptr,
|
|
rtems_blkdev_bnum *disk_end
|
|
)
|
|
{
|
|
rtems_status_code sc = RTEMS_SUCCESSFUL;
|
|
int rv = 0;
|
|
int fd = -1;
|
|
const rtems_disk_device *dd = NULL;
|
|
rtems_blkdev_bnum disk_begin = 0;
|
|
rtems_blkdev_bnum block_size = 0;
|
|
|
|
/* Open device file */
|
|
fd = open( disk_name, O_RDWR);
|
|
if (fd < 0) {
|
|
sc = RTEMS_INVALID_NAME;
|
|
goto error;
|
|
}
|
|
|
|
/* Get disk handle */
|
|
rv = rtems_disk_fd_get_disk_device( fd, &dd);
|
|
if (rv != 0) {
|
|
sc = RTEMS_INVALID_NAME;
|
|
goto error;
|
|
}
|
|
|
|
/* Get disk begin, end and block size */
|
|
disk_begin = dd->start;
|
|
*disk_end = dd->size;
|
|
block_size = dd->block_size;
|
|
|
|
/* Check block size */
|
|
if (block_size < RTEMS_BDPART_BLOCK_SIZE) {
|
|
sc = RTEMS_IO_ERROR;
|
|
goto error;
|
|
}
|
|
|
|
/* Check that we have do not have a logical disk */
|
|
if (disk_begin != 0) {
|
|
sc = RTEMS_IO_ERROR;
|
|
goto error;
|
|
}
|
|
|
|
error:
|
|
|
|
if (sc == RTEMS_SUCCESSFUL && fd_ptr != NULL && dd_ptr != NULL) {
|
|
*fd_ptr = fd;
|
|
*dd_ptr = dd;
|
|
} else {
|
|
close( fd);
|
|
}
|
|
|
|
return sc;
|
|
}
|
|
|
|
static bool rtems_bdpart_is_valid_record( const uint8_t *data)
|
|
{
|
|
return data [RTEMS_BDPART_MBR_OFFSET_SIGNATURE_0]
|
|
== RTEMS_BDPART_MBR_SIGNATURE_0
|
|
&& data [RTEMS_BDPART_MBR_OFFSET_SIGNATURE_1]
|
|
== RTEMS_BDPART_MBR_SIGNATURE_1;
|
|
}
|
|
|
|
static rtems_blkdev_bnum rtems_bdpart_next_ebr( const uint8_t *data)
|
|
{
|
|
rtems_blkdev_bnum begin =
|
|
rtems_uint32_from_little_endian( data + RTEMS_BDPART_MBR_OFFSET_BEGIN);
|
|
uint8_t type = data [RTEMS_BDPART_MBR_OFFSET_TYPE];
|
|
|
|
if (type == RTEMS_BDPART_MBR_EXTENDED) {
|
|
return begin;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static rtems_status_code rtems_bdpart_read_mbr_partition(
|
|
const uint8_t *data,
|
|
rtems_bdpart_partition **p,
|
|
const rtems_bdpart_partition *p_end,
|
|
rtems_blkdev_bnum *ep_begin
|
|
)
|
|
{
|
|
rtems_blkdev_bnum begin =
|
|
rtems_uint32_from_little_endian( data + RTEMS_BDPART_MBR_OFFSET_BEGIN);
|
|
rtems_blkdev_bnum size =
|
|
rtems_uint32_from_little_endian( data + RTEMS_BDPART_MBR_OFFSET_SIZE);
|
|
rtems_blkdev_bnum end = begin + size;
|
|
uint8_t type = data [RTEMS_BDPART_MBR_OFFSET_TYPE];
|
|
|
|
if (type == RTEMS_BDPART_MBR_EMPTY) {
|
|
return RTEMS_SUCCESSFUL;
|
|
} else if (*p == p_end) {
|
|
return RTEMS_TOO_MANY;
|
|
} else if (begin >= end) {
|
|
return RTEMS_IO_ERROR;
|
|
} else if (type == RTEMS_BDPART_MBR_EXTENDED) {
|
|
if (ep_begin != NULL) {
|
|
*ep_begin = begin;
|
|
}
|
|
} else {
|
|
/* Increment partition index */
|
|
++(*p);
|
|
|
|
/* Clear partition */
|
|
memset( *p, 0, sizeof( rtems_bdpart_partition));
|
|
|
|
/* Set values */
|
|
(*p)->begin = begin;
|
|
(*p)->end = end;
|
|
rtems_bdpart_to_partition_type( type, (*p)->type);
|
|
(*p)->flags = data [RTEMS_BDPART_MBR_OFFSET_FLAGS];
|
|
}
|
|
|
|
return RTEMS_SUCCESSFUL;
|
|
}
|
|
|
|
static rtems_status_code rtems_bdpart_read_record(
|
|
const rtems_disk_device *dd,
|
|
rtems_blkdev_bnum index,
|
|
rtems_bdbuf_buffer **block
|
|
)
|
|
{
|
|
rtems_status_code sc = RTEMS_SUCCESSFUL;
|
|
|
|
/* Release previous block if necessary */
|
|
if (*block != NULL) {
|
|
sc = rtems_bdbuf_release( *block);
|
|
if (sc != RTEMS_SUCCESSFUL) {
|
|
return sc;
|
|
}
|
|
}
|
|
|
|
/* Read the record block */
|
|
sc = rtems_bdbuf_read( dd, index, block);
|
|
if (sc != RTEMS_SUCCESSFUL) {
|
|
return sc;
|
|
}
|
|
|
|
/* just in case block did not get filled in */
|
|
if ( *block == NULL ) {
|
|
return RTEMS_INVALID_ADDRESS;
|
|
}
|
|
|
|
/* Check MBR signature */
|
|
if (!rtems_bdpart_is_valid_record( (*block)->buffer)) {
|
|
return RTEMS_IO_ERROR;
|
|
}
|
|
|
|
return RTEMS_SUCCESSFUL;
|
|
}
|
|
|
|
rtems_status_code rtems_bdpart_read(
|
|
const char *disk_name,
|
|
rtems_bdpart_format *format,
|
|
rtems_bdpart_partition *pt,
|
|
size_t *count
|
|
)
|
|
{
|
|
rtems_status_code sc = RTEMS_SUCCESSFUL;
|
|
rtems_status_code esc = RTEMS_SUCCESSFUL;
|
|
rtems_bdbuf_buffer *block = NULL;
|
|
rtems_bdpart_partition *p = pt - 1;
|
|
const rtems_bdpart_partition *p_end = pt + (count != NULL ? *count : 0);
|
|
rtems_blkdev_bnum ep_begin = 0; /* Extended partition begin */
|
|
rtems_blkdev_bnum ebr = 0; /* Extended boot record block index */
|
|
rtems_blkdev_bnum disk_end = 0;
|
|
size_t i = 0;
|
|
const uint8_t *data = NULL;
|
|
int fd = -1;
|
|
const rtems_disk_device *dd = NULL;
|
|
|
|
/* Check parameter */
|
|
if (format == NULL || pt == NULL || count == NULL) {
|
|
return RTEMS_INVALID_ADDRESS;
|
|
}
|
|
|
|
/* Set count to a save value */
|
|
*count = 0;
|
|
|
|
/* Get disk data */
|
|
sc = rtems_bdpart_get_disk_data( disk_name, &fd, &dd, &disk_end);
|
|
if (sc != RTEMS_SUCCESSFUL) {
|
|
return sc;
|
|
}
|
|
|
|
/* Read MBR */
|
|
sc = rtems_bdpart_read_record( dd, 0, &block);
|
|
if (sc != RTEMS_SUCCESSFUL) {
|
|
esc = sc;
|
|
goto cleanup;
|
|
}
|
|
|
|
/* Read the first partition entry */
|
|
data = block->buffer + RTEMS_BDPART_MBR_OFFSET_TABLE_0;
|
|
sc = rtems_bdpart_read_mbr_partition( data, &p, p_end, &ep_begin);
|
|
if (sc != RTEMS_SUCCESSFUL) {
|
|
esc = sc;
|
|
goto cleanup;
|
|
}
|
|
|
|
/* Determine if we have a MBR or GPT format */
|
|
if (rtems_bdpart_mbr_partition_type( p->type) == RTEMS_BDPART_MBR_GPT) {
|
|
esc = RTEMS_NOT_IMPLEMENTED;
|
|
goto cleanup;
|
|
}
|
|
|
|
/* Set format */
|
|
format->type = RTEMS_BDPART_FORMAT_MBR;
|
|
format->mbr.disk_id = rtems_uint32_from_little_endian(
|
|
block->buffer + RTEMS_BDPART_MBR_OFFSET_DISK_ID
|
|
);
|
|
format->mbr.dos_compatibility = true;
|
|
|
|
/* Iterate through the rest of the primary partition table */
|
|
for (i = 1; i < 4; ++i) {
|
|
data += RTEMS_BDPART_MBR_TABLE_ENTRY_SIZE;
|
|
|
|
sc = rtems_bdpart_read_mbr_partition( data, &p, p_end, &ep_begin);
|
|
if (sc != RTEMS_SUCCESSFUL) {
|
|
esc = sc;
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
/* Iterate through the logical partitions within the extended partition */
|
|
ebr = ep_begin;
|
|
while (ebr != 0) {
|
|
rtems_blkdev_bnum tmp = 0;
|
|
|
|
/* Read EBR */
|
|
sc = rtems_bdpart_read_record( dd, ebr, &block);
|
|
if (sc != RTEMS_SUCCESSFUL) {
|
|
esc = sc;
|
|
goto cleanup;
|
|
}
|
|
|
|
/* Read first partition entry */
|
|
sc = rtems_bdpart_read_mbr_partition(
|
|
block->buffer + RTEMS_BDPART_MBR_OFFSET_TABLE_0,
|
|
&p,
|
|
p_end,
|
|
NULL
|
|
);
|
|
if (sc != RTEMS_SUCCESSFUL) {
|
|
esc = sc;
|
|
goto cleanup;
|
|
}
|
|
|
|
/* Adjust partition begin */
|
|
tmp = p->begin + ebr;
|
|
if (tmp > p->begin) {
|
|
p->begin = tmp;
|
|
} else {
|
|
esc = RTEMS_IO_ERROR;
|
|
goto cleanup;
|
|
}
|
|
|
|
/* Adjust partition end */
|
|
tmp = p->end + ebr;
|
|
if (tmp > p->end) {
|
|
p->end = tmp;
|
|
} else {
|
|
esc = RTEMS_IO_ERROR;
|
|
goto cleanup;
|
|
}
|
|
|
|
/* Read second partition entry for next EBR block */
|
|
ebr = rtems_bdpart_next_ebr(
|
|
block->buffer + RTEMS_BDPART_MBR_OFFSET_TABLE_1
|
|
);
|
|
if (ebr != 0) {
|
|
/* Adjust partition EBR block index */
|
|
tmp = ebr + ep_begin;
|
|
if (tmp > ebr) {
|
|
ebr = tmp;
|
|
} else {
|
|
esc = RTEMS_IO_ERROR;
|
|
goto cleanup;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Return partition count */
|
|
*count = (size_t) (p - pt + 1);
|
|
|
|
cleanup:
|
|
|
|
if (fd >= 0) {
|
|
close( fd);
|
|
}
|
|
|
|
if (block != NULL) {
|
|
rtems_bdbuf_release( block);
|
|
}
|
|
|
|
return esc;
|
|
}
|