/** * @file * * @ingroup rtems_bdpart * * @brief Block Device Partition Management */ /* * Copyright (c) 2009, 2010 * embedded brains GmbH * Obere Lagerstr. 30 * D-82178 Puchheim * Germany * * * The license and distribution terms for this file may be * found in the file LICENSE in this distribution or at * http://www.rtems.org/license/LICENSE. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #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, rtems_disk_device **dd_ptr, rtems_blkdev_bnum *disk_end ) { rtems_status_code sc = RTEMS_SUCCESSFUL; int rv = 0; int fd; 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 out; } /* 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); } out: 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( 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; 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; }