diff options
author | Thomas Doerfler <Thomas.Doerfler@embedded-brains.de> | 2009-05-05 12:53:41 +0000 |
---|---|---|
committer | Thomas Doerfler <Thomas.Doerfler@embedded-brains.de> | 2009-05-05 12:53:41 +0000 |
commit | 834df50c906689b7e6f2652b664a852bd53fe760 (patch) | |
tree | 21ccdeb62df806d2db07f56a3d09b9f5ea92ec7f /cpukit/libblock | |
parent | Update (diff) | |
download | rtems-834df50c906689b7e6f2652b664a852bd53fe760.tar.bz2 |
New files
Diffstat (limited to 'cpukit/libblock')
-rw-r--r-- | cpukit/libblock/include/rtems/bdpart.h | 360 | ||||
-rw-r--r-- | cpukit/libblock/src/bdpart.c | 1151 |
2 files changed, 1511 insertions, 0 deletions
diff --git a/cpukit/libblock/include/rtems/bdpart.h b/cpukit/libblock/include/rtems/bdpart.h new file mode 100644 index 0000000000..dc52b1c89c --- /dev/null +++ b/cpukit/libblock/include/rtems/bdpart.h @@ -0,0 +1,360 @@ +/** + * @file + * + * Block device partition management. + */ + +/* + * Copyright (c) 2009 + * 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. + */ + +#ifndef RTEMS_BDPART_H +#define RTEMS_BDPART_H + +#include <uuid/uuid.h> + +#include <rtems.h> +#include <rtems/blkdev.h> + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** + * @defgroup rtems_bdpart Block Device Partition Management + * + * @ingroup rtems_libblock + * + * This module provides functions to manage partitions of a disk device. + * + * A @ref rtems_disk "disk" is a set of blocks which are identified by a + * consecutive set of non-negative integers starting at zero. There are also + * logical disks which contain a subset of consecutive disk blocks. The + * logical disks are used to represent the partitions of a disk. The disk + * devices are accessed via the @ref rtems_disk "block device buffer module". + * + * The partition format on the physical disk will be converted to an internal + * representation. It is possible to convert the internal representation into + * a specific output format and write it to the physical disk. One of the + * constrains for the internal representation was to support the GPT format + * easily. + * + * Currently two physical partition formats are supported. These are the MBR + * and the GPT format. Please note that the GPT support is not implemented. + * With MBR format we mean the partition format of the wide spread IBM + * PC-compatible systems. The GPT format is defined in the Extensible Firmware + * Interface (EFI). + * + * The most common task will be to read the partition information of a disk and + * register logical disks for each partition. This can be done with the + * rtems_bdpart_register_from_disk() function. Afterwards you can + * @ref rtems_fsmount "mount" the file systems within the partitions. + * + * You can read the partition information from a disk with rtems_bdpart_read() + * and write it to the disk with rtems_bdpart_write(). + * + * To create a partition table from scratch for a disk use + * rtems_bdpart_create(). + * + * You can access some disk functions with the shell command @c fdisk. + * + * References used to create this module: + * - <a href="http://en.wikipedia.org/wiki/UUID">Universally Unique Identifier</a> + * - <a href="http://en.wikipedia.org/wiki/Globally_Unique_Identifier">Globally Unique Identifier</a> + * - <a href="http://en.wikipedia.org/wiki/Disk_partitioning">Disk Paritioning</a> + * - <a href="http://en.wikipedia.org/wiki/GUID_Partition_Table">GUID Partition Table</a> + * - <a href="http://en.wikipedia.org/wiki/Master_boot_record">Master Boot Record</a> + * - <a href="http://en.wikipedia.org/wiki/Extended_boot_record">Extended Boot Record</a> + * - <a href="http://en.wikipedia.org/wiki/Cylinder-head-sector">Cylinder Head Sector</a> + * - <a href="http://www.win.tue.nl/~aeb/partitions/partition_types-1.html">Partition Types</a> + * + * @{ + */ + +/** + * @name MBR Partition Types and Flags + * + * @{ + */ + +#define RTEMS_BDPART_MBR_EMPTY 0x0U + +#define RTEMS_BDPART_MBR_FAT_12 0x1U + +#define RTEMS_BDPART_MBR_FAT_16 0x4U + +#define RTEMS_BDPART_MBR_FAT_16_LBA 0xeU + +#define RTEMS_BDPART_MBR_FAT_32 0xbU + +#define RTEMS_BDPART_MBR_FAT_32_LBA 0xcU + +#define RTEMS_BDPART_MBR_EXTENDED 0x5U + +#define RTEMS_BDPART_MBR_DATA 0xdaU + +#define RTEMS_BDPART_MBR_GPT 0xeeU + +#define RTEMS_BDPART_MBR_FLAG_ACTIVE 0x80U + +/** @} */ + +/** + * Recommended maximum partition table size. + */ +#define RTEMS_BDPART_PARTITION_NUMBER_HINT 16 + +/** + * Partition description. + */ +typedef struct rtems_bdpart_partition { + /** + * Block index for partition begin. + */ + rtems_blkdev_bnum begin; + + /** + * Block index for partition end (this block is not a part of the partition). + */ + rtems_blkdev_bnum end; + + /** + * Partition type. + */ + uuid_t type; + + /** + * Partition ID. + */ + uuid_t id; + + /** + * Partition flags. + */ + uint64_t flags; +} rtems_bdpart_partition; + +/** + * Disk format for the partition tables. + */ +typedef enum { + /** + * Type value for MBR format. + */ + RTEMS_BDPART_FORMAT_MBR, + + /** + * Type value for GPT format. + */ + RTEMS_BDPART_FORMAT_GPT +} rtems_bdpart_format_type; + +/** + * Disk format description. + */ +typedef struct { + /** + * Format type. + */ + rtems_bdpart_format_type type; + union { + /** + * MBR format fields. + */ + struct { + /** + * Disk ID in MBR at offset 440. + */ + uint32_t disk_id; + + /** + * This option is used for partition table creation and validation checks + * before a write to the disk. It ensures that the first primary + * partition and the logical partitions start at head one and sector one + * under the virtual one head and 63 sectors geometry. Each begin and + * end of a partition will be aligned to the virtual cylinder boundary. + */ + bool dos_compatibility; + } mbr; + + /** + * GPT format fields. + */ + struct { + /** + * Disk ID in GPT header. + */ + uuid_t disk_id; + } gpt; + }; +} rtems_bdpart_format; + +/** + * Reads the partition information from the physical disk device with name + * @a disk_name. + * + * The partition information will be stored in the partition table + * @a partitions with a maximum of @a count partitions. The number of actual + * partitions will be stored in @a count. If there are more partitions than + * space for storage an error status will be returned. The partition table + * format recognized on the disk will be stored in @a format. + */ +rtems_status_code rtems_bdpart_read( + const char *disk_name, + rtems_bdpart_format *format, + rtems_bdpart_partition *partitions, + size_t *count +); + +/** + * Sorts the partition table @a partitions with @a count partitions to have + * ascending begin blocks + */ +void rtems_bdpart_sort( rtems_bdpart_partition *partitions, size_t count); + +/** + * Writes the partition table to the physical disk device with name + * @a disk_name. + * + * The partition table @a partitions with @a count partitions will be written + * to the disk. The output format for the partition table on the disk is + * specified by @a format. There are some consistency checks applied to the + * partition table. The partition table must be sorted such that the begin + * blocks are in ascending order. This can be done with the + * rtems_bdpart_sort() function. The partitions must not overlap. The + * partitions must have a positive size. The partitions must be within the + * disk. Depending on the output format there are additional constrains. + */ +rtems_status_code rtems_bdpart_write( + const char *disk_name, + const rtems_bdpart_format *format, + const rtems_bdpart_partition *partitions, + size_t count +); + +/** + * Creates a partition table in @a partitions with @a count partitions for the + * physical disk device with name @a disk_name. + * + * The array of positive integer weights in @a distribution must be of size @a + * size. The weights in the distribution array are summed up. Each weight is + * then divided by the sum to obtain the disk fraction which forms the + * corresponding partition. The partition boundaries are generated with + * respect to the output format in @a format. + */ +rtems_status_code rtems_bdpart_create( + const char *disk_name, + const rtems_bdpart_format *format, + rtems_bdpart_partition *partitions, + const unsigned *distribution, + size_t count +); + +/** + * Registers the partitions as logical disks for the physical disk device with + * name @a disk_name. + * + * For each partition of the partition table @a partitions with @a count + * partitions a logical disk is registered. The partition number equals the + * partition table index plus one. The name of the logical disk device is the + * concatenation of the physical disk device name and the partition number. + */ +rtems_status_code rtems_bdpart_register( + const char *disk_name, + const rtems_bdpart_partition *partitions, + size_t count +); + +/** + * Reads the partition table from the disk device with name @a disk_name and + * registers the partitions as logical disks. + * + * @see rtems_bdpart_register() and rtems_fsmount(). + */ +rtems_status_code rtems_bdpart_register_from_disk( const char *disk_name); + +/** + * Deletes the logical disks associated with the partitions of the disk device + * with name @a disk_name. + * + * The partition table @a partitions with @a count partitions will be used to + * determine which disks need to be deleted. It may be obtained from + * rtems_bdpart_read(). + */ +rtems_status_code rtems_bdpart_unregister( + const char *disk_name, + const rtems_bdpart_partition *partitions, + size_t count +); + +/** + * Mounts all supported file systems inside the logical disks derived from the + * partitions of the physical disk device with name @a disk_name. + * + * For each partition in the partition table @a partitions with @a count + * partitions it will be checked if it contains a supported file system. In + * this case a mount point derived from the disk name will be created in the + * mount base path @a mount_base. The file system will be mounted there. The + * partition number equals the partition table index plus one. The mount point + * name for each partition will be the concatenation of the mount base path, + * the disk device file name and the parition number. + * + * @see rtems_bdpart_read(). + */ +rtems_status_code rtems_bdpart_mount( + const char *disk_name, + const rtems_bdpart_partition *partitions, + size_t count, + const char *mount_base +); + +/** + * Unmounts all file systems mounted with rtems_bdpart_mount(). + */ +rtems_status_code rtems_bdpart_unmount( + const char *disk_name, + const rtems_bdpart_partition *partitions, + size_t count, + const char *mount_base +); + +/** + * Prints the partition table @a partitions with @a count partitions to + * standard output. + */ +void rtems_bdpart_dump( const rtems_bdpart_partition *partitions, size_t count); + +/** + * Returns the partition type for the MBR partition type value @a mbr_type in + * @a type. + */ +void rtems_bdpart_to_partition_type( uint8_t mbr_type, uuid_t type); + +/** + * Converts the partition type in @a type to the MBR partition type. + * + * The result will be stored in @a mbr_type. Returns @c true in case of a + * successful convertion and otherwise @c false. Both arguments must not be + * @c NULL. + */ +bool rtems_bdpart_to_mbr_partition_type( + const uuid_t type, + uint8_t *mbr_type +); + +/** @} */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* RTEMS_BDPART_H */ diff --git a/cpukit/libblock/src/bdpart.c b/cpukit/libblock/src/bdpart.c new file mode 100644 index 0000000000..29b770121e --- /dev/null +++ b/cpukit/libblock/src/bdpart.c @@ -0,0 +1,1151 @@ +/** + * @file + * + * Block device partition management. + */ + +/* + * Copyright (c) 2009 + * 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. + */ + +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/stat.h> +#include <sys/types.h> + +#include <rtems/bdbuf.h> +#include <rtems/bdpart.h> +#include <rtems/dosfs.h> +#include <rtems/endian.h> +#include <rtems/fsmount.h> + +#define RTEMS_BDPART_MBR_PARTITION_TYPE( type) \ + { \ + (type), 0xa2U, 0x2eU, 0x38U, \ + 0x38U, 0xb5U, 0xdeU, 0x11U, \ + 0xbcU, 0x13U, 0x00U, 0x1dU, \ + 0x09U, 0xb0U, 0x5fU, 0xa4U \ + } + +#define RTEMS_BDPART_BLOCK_SIZE 512 + +#define RTEMS_BDPART_MBR_CYLINDER_SIZE 63 + +#define RTEMS_BDPART_MBR_TABLE_ENTRY_SIZE 16 + +#define RTEMS_BDPART_MBR_OFFSET_TABLE_0 446 + +#define RTEMS_BDPART_MBR_OFFSET_TABLE_1 \ + (RTEMS_BDPART_MBR_OFFSET_TABLE_0 + RTEMS_BDPART_MBR_TABLE_ENTRY_SIZE) + +#define RTEMS_BDPART_MBR_OFFSET_DISK_ID 440 + +#define RTEMS_BDPART_MBR_OFFSET_SIGNATURE_0 510 + +#define RTEMS_BDPART_MBR_OFFSET_SIGNATURE_1 511 + +#define RTEMS_BDPART_MBR_SIGNATURE_0 0x55U + +#define RTEMS_BDPART_MBR_SIGNATURE_1 0xaaU + +#define RTEMS_BDPART_MBR_OFFSET_BEGIN 8 + +#define RTEMS_BDPART_MBR_OFFSET_SIZE 12 + +#define RTEMS_BDPART_MBR_OFFSET_TYPE 4 + +#define RTEMS_BDPART_MBR_OFFSET_FLAGS 0 + +#define RTEMS_BDPART_PARTITION_READ_MAX 32 + +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); +} + +static uint8_t rtems_bdpart_mbr_partition_type( + const uuid_t type +) +{ + return type [0]; +} + +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; +} + +static void rtems_bdpart_type_to_string( + const uuid_t type, + char str [37] +) +{ + uuid_unparse_lower( type, str); +} + +static rtems_status_code rtems_bdpart_get_disk_data( + const char *disk_name, + dev_t *disk, + rtems_blkdev_bnum *disk_end +) +{ + rtems_status_code sc = RTEMS_SUCCESSFUL; + int rv = 0; + rtems_blkdev_bnum disk_begin = 0; + rtems_blkdev_bnum block_size = 0; + rtems_disk_device *dd = NULL; + struct stat st; + + /* Get disk handle */ + rv = stat( disk_name, &st); + if (rv != 0) { + return RTEMS_INVALID_NAME; + } + *disk = st.st_dev; + + /* Get disk begin, end and block size */ + dd = rtems_disk_obtain( *disk); + if (dd == NULL) { + return RTEMS_INVALID_NAME; + } + disk_begin = dd->start; + *disk_end = dd->size; + block_size = dd->block_size; + sc = rtems_disk_release( dd); + if (sc != RTEMS_SUCCESSFUL) { + return sc; + } + + /* Check block size */ + if (block_size < RTEMS_BDPART_BLOCK_SIZE) { + return RTEMS_IO_ERROR; + } + + /* Check that we have do not have a logical disk */ + if (disk_begin != 0) { + return RTEMS_IO_ERROR; + } + + return RTEMS_SUCCESSFUL; +} + +static int rtems_bdpart_partition_compare( const void *aa, const void *bb) +{ + const rtems_bdpart_partition *a = aa; + const rtems_bdpart_partition *b = bb; + + if (a->begin < b->begin) { + return -1; + } else if (a->begin == b->begin) { + return 0; + } else { + return 1; + } +} + +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 void rtems_bdpart_write_mbr_partition( + uint8_t *data, + uint32_t begin, + uint32_t size, + uint8_t type, + uint8_t flags +) +{ + rtems_uint32_to_little_endian( begin, data + RTEMS_BDPART_MBR_OFFSET_BEGIN); + rtems_uint32_to_little_endian( size, data + RTEMS_BDPART_MBR_OFFSET_SIZE); + data [RTEMS_BDPART_MBR_OFFSET_TYPE] = type; + data [RTEMS_BDPART_MBR_OFFSET_FLAGS] = flags; +} + +static rtems_status_code rtems_bdpart_read_record( + dev_t disk, + 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( disk, index, block); + if (sc != RTEMS_SUCCESSFUL) { + return sc; + } + + /* Check MBR signature */ + if (!rtems_bdpart_is_valid_record( (*block)->buffer)) { + return RTEMS_IO_ERROR; + } + + return RTEMS_SUCCESSFUL; +} + +static rtems_status_code rtems_bdpart_new_record( + dev_t disk, + rtems_blkdev_bnum index, + rtems_bdbuf_buffer **block +) +{ + rtems_status_code sc = RTEMS_SUCCESSFUL; + + /* Synchronize previous block if necessary */ + if (*block != NULL) { + sc = rtems_bdbuf_sync( *block); + if (sc != RTEMS_SUCCESSFUL) { + return sc; + } + } + + /* Read the new record block (this accounts for disk block sizes > 512) */ + sc = rtems_bdbuf_read( disk, index, block); + if (sc != RTEMS_SUCCESSFUL) { + return sc; + } + + /* Clear record */ + memset( (*block)->buffer, 0, RTEMS_BDPART_BLOCK_SIZE); + + /* Write signature */ + (*block)->buffer [RTEMS_BDPART_MBR_OFFSET_SIGNATURE_0] = + RTEMS_BDPART_MBR_SIGNATURE_0; + (*block)->buffer [RTEMS_BDPART_MBR_OFFSET_SIGNATURE_1] = + RTEMS_BDPART_MBR_SIGNATURE_1; + + 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; + rtems_bdpart_partition *p_end = NULL; + 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; + dev_t disk = 0; + size_t i = 0; + const uint8_t *data = NULL; + + /* Check parameter */ + if (format == NULL || pt == NULL || count == NULL) { + return RTEMS_INVALID_ADDRESS; + } + + /* Set table end and the count to a save value */ + p_end = pt + *count; + *count = 0; + + /* Get disk data */ + sc = rtems_bdpart_get_disk_data( disk_name, &disk, &disk_end); + if (sc != RTEMS_SUCCESSFUL) { + return sc; + } + + /* Read MBR */ + sc = rtems_bdpart_read_record( disk, 0, &block); + if (sc != RTEMS_SUCCESSFUL) { + return sc; + } + + /* 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( disk, ebr, &block); + if (sc != RTEMS_SUCCESSFUL) { + return sc; + } + + /* 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 (block != NULL) { + rtems_bdbuf_release( block); + } + + return esc; +} + +void rtems_bdpart_sort( rtems_bdpart_partition *pt, size_t count) +{ + qsort( pt, count, sizeof( *pt), rtems_bdpart_partition_compare); +} + +rtems_status_code rtems_bdpart_write( + const char *disk_name, + const rtems_bdpart_format *format, + const rtems_bdpart_partition *pt, + size_t count +) +{ + rtems_status_code sc = RTEMS_SUCCESSFUL; + rtems_status_code esc = RTEMS_SUCCESSFUL; + bool dos_compatibility = format->type == RTEMS_BDPART_FORMAT_MBR + && format->mbr.dos_compatibility; + rtems_bdbuf_buffer *block = NULL; + rtems_blkdev_bnum disk_end = 0; + rtems_blkdev_bnum record_space = + dos_compatibility ? RTEMS_BDPART_MBR_CYLINDER_SIZE : 1; + dev_t disk = 0; + size_t ppc = 0; /* Primary partition count */ + size_t i = 0; + uint8_t *data = NULL; + + /* Check if we have something to do */ + if (count == 0) { + /* Nothing to do */ + return RTEMS_SUCCESSFUL; + } + + /* Check parameter */ + if (format == NULL || pt == NULL) { + return RTEMS_INVALID_ADDRESS; + } + + /* Get disk data */ + sc = rtems_bdpart_get_disk_data( disk_name, &disk, &disk_end); + if (sc != RTEMS_SUCCESSFUL) { + return sc; + } + + /* Align end of disk on cylinder boundary if necessary */ + if (dos_compatibility) { + disk_end -= (disk_end % record_space); + } + + /* Check that we have a consistent partition table */ + for (i = 0; i < count; ++i) { + const rtems_bdpart_partition *p = pt + i; + + /* Check that begin and end are proper within the disk */ + if (p->begin >= disk_end || p->end > disk_end) { + esc = RTEMS_INVALID_NUMBER; + goto cleanup; + } + + /* Check that begin and end are valid */ + if (p->begin >= p->end) { + esc = RTEMS_INVALID_NUMBER; + goto cleanup; + } + + /* Check that partitions do not overlap */ + if (i > 0 && pt [i - 1].end > p->begin) { + esc = RTEMS_INVALID_NUMBER; + goto cleanup; + } + } + + /* Check format */ + if (format->type != RTEMS_BDPART_FORMAT_MBR) { + esc = RTEMS_NOT_IMPLEMENTED; + goto cleanup; + } + + /* + * Set primary partition count. If we have more than four partitions we need + * an extended partition which will contain the partitions of number four and + * above as logical partitions. If we have four or less partitions we can + * use the primary partition table. + */ + ppc = count <= 4 ? count : 3; + + /* + * Check that the first primary partition starts at head one and sector one + * under the virtual one head and 63 sectors geometry if necessary. + */ + if (dos_compatibility && pt [0].begin != RTEMS_BDPART_MBR_CYLINDER_SIZE) { + esc = RTEMS_INVALID_NUMBER; + goto cleanup; + } + + /* + * Check that we have enough space for the EBRs. The partitions with number + * four and above are logical partitions if we have more than four partitions + * in total. The logical partitions are contained in the extended partition. + * Each logical partition is described via one EBR preceding the partition. + * The space for the EBR and maybe some space which is needed for DOS + * compatibility resides between the partitions. So there have to be gaps of + * the appropriate size between the partitions. + */ + for (i = ppc; i < count; ++i) { + if ((pt [i].begin - pt [i - 1].end) < record_space) { + esc = RTEMS_INVALID_NUMBER; + goto cleanup; + } + } + + /* Check that we can convert the parition descriptions to the MBR format */ + for (i = 0; i < count; ++i) { + uint8_t type = 0; + + const rtems_bdpart_partition *p = pt + i; + + /* Check type */ + if (!rtems_bdpart_to_mbr_partition_type( p->type, &type)) { + esc = RTEMS_INVALID_ID; + goto cleanup; + } + + /* Check flags */ + if (p->flags > 0xffU) { + esc = RTEMS_INVALID_ID; + goto cleanup; + } + + /* Check ID */ + /* TODO */ + } + + /* New MBR */ + sc = rtems_bdpart_new_record( disk, 0, &block); + if (sc != RTEMS_SUCCESSFUL) { + esc = sc; + goto cleanup; + } + + /* Write disk ID */ + rtems_uint32_to_little_endian( + format->mbr.disk_id, + block->buffer + RTEMS_BDPART_MBR_OFFSET_DISK_ID + ); + + /* Write primary partition table */ + data = block->buffer + RTEMS_BDPART_MBR_OFFSET_TABLE_0; + for (i = 0; i < ppc; ++i) { + const rtems_bdpart_partition *p = pt + i; + + /* Write partition entry */ + rtems_bdpart_write_mbr_partition( + data, + p->begin, + p->end - p->begin, + rtems_bdpart_mbr_partition_type( p->type), + (uint8_t) p->flags + ); + + data += RTEMS_BDPART_MBR_TABLE_ENTRY_SIZE; + } + + /* Write extended partition with logical partitions if necessary */ + if (ppc != count) { + rtems_blkdev_bnum ebr = 0; /* Extended boot record block index */ + + /* Begin of extended partition */ + rtems_blkdev_bnum ep_begin = pt [ppc].begin - record_space; + + /* Write extended partition */ + rtems_bdpart_write_mbr_partition( + data, + ep_begin, + disk_end - ep_begin, + RTEMS_BDPART_MBR_EXTENDED, + 0 + ); + + /* Write logical partitions */ + for (i = ppc; i < count; ++i) { + const rtems_bdpart_partition *p = pt + i; + + /* Write second partition entry */ + if (i > ppc) { + rtems_blkdev_bnum begin = p->begin - record_space; + + rtems_bdpart_write_mbr_partition( + block->buffer + RTEMS_BDPART_MBR_OFFSET_TABLE_1, + begin - ep_begin, + disk_end - begin, + RTEMS_BDPART_MBR_EXTENDED, + 0 + ); + } + + /* New EBR */ + ebr = p->begin - record_space; + sc = rtems_bdpart_new_record( disk, ebr, &block); + if (sc != RTEMS_SUCCESSFUL) { + esc = sc; + goto cleanup; + } + + /* Write first partition entry */ + rtems_bdpart_write_mbr_partition( + block->buffer + RTEMS_BDPART_MBR_OFFSET_TABLE_0, + record_space, + p->end - p->begin, + rtems_bdpart_mbr_partition_type( p->type), + (uint8_t) p->flags + ); + } + } + +cleanup: + + if (block != NULL) { + rtems_bdbuf_sync( block); + } + + return esc; +} + +rtems_status_code rtems_bdpart_create( + const char *disk_name, + const rtems_bdpart_format *format, + rtems_bdpart_partition *pt, + const unsigned *dist, + size_t count +) +{ + rtems_status_code sc = RTEMS_SUCCESSFUL; + bool dos_compatibility = format->type == RTEMS_BDPART_FORMAT_MBR + && format->mbr.dos_compatibility; + rtems_blkdev_bnum disk_end = 0; + rtems_blkdev_bnum pos = 0; + rtems_blkdev_bnum dist_sum = 0; + rtems_blkdev_bnum record_space = + dos_compatibility ? RTEMS_BDPART_MBR_CYLINDER_SIZE : 1; + rtems_blkdev_bnum overhead = 0; + rtems_blkdev_bnum free_space = 0; + dev_t disk = 0; + size_t i = 0; + + /* Check if we have something to do */ + if (count == 0) { + /* Nothing to do */ + return RTEMS_SUCCESSFUL; + } + + /* Check parameter */ + if (format == NULL || pt == NULL || dist == NULL) { + return RTEMS_INVALID_ADDRESS; + } + + /* Get disk data */ + sc = rtems_bdpart_get_disk_data( disk_name, &disk, &disk_end); + if (sc != RTEMS_SUCCESSFUL) { + return sc; + } + + /* Get distribution sum and check for overflow */ + for (i = 0; i < count; ++i) { + unsigned prev_sum = dist_sum; + + dist_sum += dist [i]; + + if (dist_sum < prev_sum) { + return RTEMS_INVALID_NUMBER; + } + + if (dist [i] == 0) { + return RTEMS_INVALID_NUMBER; + } + } + + /* Check format */ + if (format->type != RTEMS_BDPART_FORMAT_MBR) { + return RTEMS_NOT_IMPLEMENTED; + } + + /* Align end of disk on cylinder boundary if necessary */ + if (dos_compatibility) { + disk_end -= (disk_end % record_space); + } + + /* + * We need at least space for the MBR and the compatibility space for the + * first primary partition. + */ + overhead += record_space; + + /* + * In case we need an extended partition and logical partitions we have to + * account for the space of each EBR. + */ + if (count > 4) { + overhead += (count - 3) * record_space; + } + + /* + * Account space to align every partition on cylinder boundaries if + * necessary. + */ + if (dos_compatibility) { + overhead += (count - 1) * record_space; + } + + /* Check disk space */ + if ((overhead + count) > disk_end) { + return RTEMS_IO_ERROR; + } + + /* Begin of first primary partition */ + pos = record_space; + + /* Space for partitions */ + free_space = disk_end - overhead; + + for (i = 0; i < count; ++i) { + rtems_bdpart_partition *p = pt + i; + + /* Partition size */ + rtems_blkdev_bnum s = free_space * dist [i]; + if (s < free_space || s < dist [i]) { + /* TODO: Calculate without overflow */ + return RTEMS_INVALID_NUMBER; + } + s /= dist_sum; + + /* Ensure that the partition is not empty */ + if (s == 0) { + s = 1; + } + + /* Align partition upwards */ + s += record_space - (s % record_space); + + /* Partition begin and end */ + p->begin = pos; + pos += s; + p->end = pos; + + /* Reserve space for the EBR if necessary */ + if (count > 4 && i > 2) { + p->begin += record_space; + } + } + + /* Expand the last partition to the disk end */ + pt [count - 1].end = disk_end; + + return RTEMS_SUCCESSFUL; +} + +#define RTEMS_BDPART_NUMBER_SIZE 4 + +rtems_status_code rtems_bdpart_register( + const char *disk_name, + const rtems_bdpart_partition *pt, + size_t count +) +{ + rtems_status_code sc = RTEMS_SUCCESSFUL; + rtems_status_code esc = RTEMS_SUCCESSFUL; + rtems_device_major_number major = 0; + rtems_device_minor_number minor = 0; + rtems_blkdev_bnum disk_end = 0; + dev_t disk = 0; + dev_t logical_disk = 0; + char *logical_disk_name = NULL; + char *logical_disk_marker = NULL; + size_t disk_name_size = strlen( disk_name); + size_t i = 0; + + /* Get disk data */ + sc = rtems_bdpart_get_disk_data( disk_name, &disk, &disk_end); + if (sc != RTEMS_SUCCESSFUL) { + return sc; + } + + /* Get the disk device identifier */ + rtems_filesystem_split_dev_t( disk, major, minor); + + /* Create logical disk name */ + logical_disk_name = malloc( disk_name_size + RTEMS_BDPART_NUMBER_SIZE); + if (logical_disk_name == NULL) { + return RTEMS_NO_MEMORY; + } + strncpy( logical_disk_name, disk_name, disk_name_size); + logical_disk_marker = logical_disk_name + disk_name_size; + + /* Create a logical disk for each partition */ + for (i = 0; i < count; ++i) { + const rtems_bdpart_partition *p = pt + i; + int rv = 0; + + /* New minor number */ + ++minor; + + /* Create a new device identifier */ + logical_disk = rtems_filesystem_make_dev_t( major, minor); + + /* Set partition number for logical disk name */ + rv = snprintf( logical_disk_marker, RTEMS_BDPART_NUMBER_SIZE, "%u", i + 1); + if (rv >= RTEMS_BDPART_NUMBER_SIZE) { + esc = RTEMS_INVALID_NAME; + goto cleanup; + } + + /* Create logical disk */ + sc = rtems_disk_create_log( + logical_disk, + disk, + p->begin, + p->end - p->begin, + logical_disk_name + ); + if (sc != RTEMS_SUCCESSFUL) { + esc = sc; + goto cleanup; + } + } + +cleanup: + + free( logical_disk_name); + + return esc; +} + +rtems_status_code rtems_bdpart_register_from_disk( const char *disk_name) +{ + rtems_status_code sc = RTEMS_SUCCESSFUL; + rtems_bdpart_format format; + rtems_bdpart_partition pt [RTEMS_BDPART_PARTITION_NUMBER_HINT]; + size_t count = RTEMS_BDPART_PARTITION_NUMBER_HINT; + + /* Read partitions */ + sc = rtems_bdpart_read( disk_name, &format, pt, &count); + if (sc != RTEMS_SUCCESSFUL) { + return sc; + } + + /* Register partitions */ + return rtems_bdpart_register( disk_name, pt, count); +} + +rtems_status_code rtems_bdpart_unregister( + const char *disk_name, + const rtems_bdpart_partition *pt, + size_t count +) +{ + rtems_status_code sc = RTEMS_SUCCESSFUL; + rtems_device_major_number major = 0; + rtems_device_minor_number minor = 0; + rtems_blkdev_bnum disk_end = 0; + dev_t disk = 0; + dev_t logical_disk = 0; + size_t i = 0; + + /* Get disk data */ + sc = rtems_bdpart_get_disk_data( disk_name, &disk, &disk_end); + if (sc != RTEMS_SUCCESSFUL) { + return sc; + } + + /* Get the disk device identifier */ + rtems_filesystem_split_dev_t( disk, major, minor); + + /* Create a logical disk for each partition */ + for (i = 0; i < count; ++i) { + /* New minor number */ + ++minor; + + /* Get the device identifier */ + logical_disk = rtems_filesystem_make_dev_t( major, minor); + + /* Delete logical disk */ + sc = rtems_disk_delete( logical_disk); + if (sc != RTEMS_SUCCESSFUL) { + return sc; + } + } + + return RTEMS_SUCCESSFUL; +} + +rtems_status_code rtems_bdpart_mount( + const char *disk_name, + const rtems_bdpart_partition *pt, + size_t count, + const char *mount_base +) +{ + rtems_status_code sc = RTEMS_SUCCESSFUL; + rtems_status_code esc = RTEMS_SUCCESSFUL; + const char *disk_file_name = strrchr( disk_name, '/'); + char *logical_disk_name = NULL; + char *logical_disk_marker = NULL; + char *mount_point = NULL; + char *mount_marker = NULL; + size_t disk_file_name_size = 0; + size_t disk_name_size = strlen( disk_name); + size_t mount_base_size = strlen( mount_base); + size_t i = 0; + + /* Create logical disk name base */ + logical_disk_name = malloc( disk_name_size + RTEMS_BDPART_NUMBER_SIZE); + if (logical_disk_name == NULL) { + return RTEMS_NO_MEMORY; + } + strncpy( logical_disk_name, disk_name, disk_name_size); + + /* Get disk file name */ + if (disk_file_name != NULL) { + disk_file_name += 1; + disk_file_name_size = strlen( disk_file_name); + } else { + disk_file_name = disk_name; + disk_file_name_size = disk_name_size; + } + + /* Create mount point base */ + mount_point = malloc( mount_base_size + 1 + disk_file_name_size + RTEMS_BDPART_NUMBER_SIZE); + if (mount_point == NULL) { + esc = RTEMS_NO_MEMORY; + goto cleanup; + } + strncpy( mount_point, mount_base, mount_base_size); + mount_point [mount_base_size] = '/'; + strncpy( mount_point + mount_base_size + 1, disk_file_name, disk_file_name_size); + + /* Markers */ + logical_disk_marker = logical_disk_name + disk_name_size; + mount_marker = mount_point + mount_base_size + 1 + disk_file_name_size; + + /* Mount supported file systems for each partition */ + for (i = 0; i < count; ++i) { + /* Create logical disk name */ + int rv = snprintf( logical_disk_marker, RTEMS_BDPART_NUMBER_SIZE, "%u", i + 1); + if (rv >= RTEMS_BDPART_NUMBER_SIZE) { + esc = RTEMS_INVALID_NAME; + goto cleanup; + } + + /* Create mount point */ + strncpy( mount_marker, logical_disk_marker, RTEMS_BDPART_NUMBER_SIZE); + rv = rtems_fsmount_create_mount_point( mount_point); + if (rv != 0) { + esc = RTEMS_IO_ERROR; + goto cleanup; + } + + /* Mount */ + rv = mount( + NULL, + &msdos_ops, + 0, + logical_disk_name, + mount_point + ); + if (rv != 0) { + rmdir( mount_point); + } + } + +cleanup: + + free( logical_disk_name); + free( mount_point); + + return esc; +} + +rtems_status_code rtems_bdpart_unmount( + const char *disk_name, + const rtems_bdpart_partition *pt, + size_t count, + const char *mount_base +) +{ + rtems_status_code sc = RTEMS_SUCCESSFUL; + rtems_status_code esc = RTEMS_SUCCESSFUL; + const char *disk_file_name = strrchr( disk_name, '/'); + char *mount_point = NULL; + char *mount_marker = NULL; + size_t disk_file_name_size = 0; + size_t disk_name_size = strlen( disk_name); + size_t mount_base_size = strlen( mount_base); + size_t i = 0; + + /* Get disk file name */ + if (disk_file_name != NULL) { + disk_file_name += 1; + disk_file_name_size = strlen( disk_file_name); + } else { + disk_file_name = disk_name; + disk_file_name_size = disk_name_size; + } + + /* Create mount point base */ + mount_point = malloc( mount_base_size + 1 + disk_file_name_size + RTEMS_BDPART_NUMBER_SIZE); + if (mount_point == NULL) { + esc = RTEMS_NO_MEMORY; + goto cleanup; + } + strncpy( mount_point, mount_base, mount_base_size); + mount_point [mount_base_size] = '/'; + strncpy( mount_point + mount_base_size + 1, disk_file_name, disk_file_name_size); + + /* Marker */ + mount_marker = mount_point + mount_base_size + 1 + disk_file_name_size; + + /* Mount supported file systems for each partition */ + for (i = 0; i < count; ++i) { + /* Create mount point */ + int rv = snprintf( mount_marker, RTEMS_BDPART_NUMBER_SIZE, "%u", i + 1); + if (rv >= RTEMS_BDPART_NUMBER_SIZE) { + esc = RTEMS_INVALID_NAME; + goto cleanup; + } + + /* Unmount */ + rv = unmount( mount_point); + if (rv == 0) { + /* Remove mount point */ + rv = rmdir( mount_point); + if (rv != 0) { + esc = RTEMS_IO_ERROR; + goto cleanup; + } + } + } + +cleanup: + + free( mount_point); + + return esc; +} + +void rtems_bdpart_dump( const rtems_bdpart_partition *pt, size_t count) +{ + size_t i = 0; + + printf( + "-------------------------------------------------------------------------------\n" + " PARTITION TABLE\n" + "------------+------------+-----------------------------------------------------\n" + " BEGIN | END | TYPE\n" + "------------+------------+-----------------------------------------------------\n" + ); + + for (i = 0; i < count; ++i) { + const rtems_bdpart_partition *p = pt + i; + const char *type = NULL; + char type_buffer [52]; + uint8_t type_mbr = 0; + + if (rtems_bdpart_to_mbr_partition_type( p->type, &type_mbr)) { + switch (type_mbr) { + case RTEMS_BDPART_MBR_FAT_12: + type = "FAT 12"; + break; + case RTEMS_BDPART_MBR_FAT_16: + type = "FAT 16"; + break; + case RTEMS_BDPART_MBR_FAT_16_LBA: + type = "FAT 16 LBA"; + break; + case RTEMS_BDPART_MBR_FAT_32: + type = "FAT 32"; + break; + case RTEMS_BDPART_MBR_FAT_32_LBA: + type = "FAT 32 LBA"; + break; + case RTEMS_BDPART_MBR_DATA: + type = "DATA"; + break; + default: + snprintf( type_buffer, sizeof( type_buffer), "0x%02" PRIx8, type_mbr); + type = type_buffer; + break; + } + } else { + rtems_bdpart_type_to_string( p->type, type_buffer); + type = type_buffer; + } + + printf( + " %10lu | %10lu |%52s\n", + (unsigned long) p->begin, + (unsigned long) p->end, + type + ); + } + + puts( "------------+------------+-----------------------------------------------------"); +} |