summaryrefslogblamecommitdiffstats
path: root/cpukit/libblock/src/bdpart-write.c
blob: 0418e82850b0bf8eb0171637fae4aab4b49b3946 (plain) (tree)
1
2
3
4
5
6



                        

                                           











                                                          
                                        



























                                                                              
                        














                                                                            
                                           































                                                          
                                     



                                                           


                                               
              
                               












                                        
                                                                   





























































































                                                                               
                                               






                                
                        






















































                                                                     
                                                     

















                                                        



                





                             
/**
 * @file
 *
 * @ingroup rtems_bdpart
 *
 * @brief 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.org/license/LICENSE.
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <string.h>

#include <rtems.h>
#include <rtems/bdbuf.h>
#include <rtems/bdpart.h>
#include <rtems/endian.h>

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_new_record(
  rtems_disk_device *dd,
  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( 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;
  }

  /* 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_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 != NULL
    && 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;
  size_t ppc = 0; /* Primary partition count */
  size_t i = 0;
  uint8_t *data = NULL;
  int fd = -1;
  rtems_disk_device *dd = 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, &fd, &dd, &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( dd, 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( dd, 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 (fd >= 0) {
    close( fd);
  }

  if (block != NULL) {
    rtems_bdbuf_sync( block);
  }

  return esc;
}