Logo Search packages:      
Sourcecode: dmraid version File versions  Download package

dos.c

/*
 * Copyright (C) 2004,2005  Heinz Mauelshagen, Red Hat GmbH.
 *                          All rights reserved.
 *
 * See file LICENSE at the top of this source tree for license information.
 */

/*
 * DOS partition format handler.
 *
 * dos_read() and dos_group influenced by libparted.
 */
#define     HANDLER     "dos"

#include "internal.h"
#define     FORMAT_HANDLER
#include "dos.h"
#include <datastruct/byteorder.h>

static const char *handler = HANDLER;

/* Make up RAID device name. */
static size_t _name(struct lib_context *lc, struct raid_dev *rd,
                unsigned short partition, char *str, size_t len,
                unsigned char type)
{
      return snprintf(str, len, type ? "%s%u" : "%s",
                  get_basename(lc, rd->di->path), partition);
}

static char *name(struct lib_context *lc, struct raid_dev *rd,
               unsigned int part, unsigned char type)
{
      size_t len;
      char *ret;

      if ((ret = dbg_malloc((len = _name(lc, rd, part, NULL, 0, type) + 1))))
            _name(lc, rd, part, ret, len, type);
      else
            log_alloc_err(lc, handler);

      return ret;
}

/*
 * Read a DOS partiton.
 */
/* Endianess conversion. */
#if   BYTE_ORDER == LITTLE_ENDIAN
#  define   to_cpu      NULL
#else
static void to_cpu(void *meta)
{
      struct dos *dos = meta;
      struct dos_partition *part = dos->partitions;

      for (part = dos->partitions; part < dos->partitions + 4; part++) {
            CVT32(part->start);
            CVT32(part->length);
      }

      CVT16(dos->magic);
}
#endif

static int is_dos(struct lib_context *lc, struct dev_info *di, void *meta)
{
      struct dos *dos = meta;
      struct dos_partition *part;

      if (dos->magic != DOS_MAGIC)
            return 0;

      for (part = dos->partitions; part < &dos->partitions[4]; part++) {
            if (part->type == PARTITION_GPT)
                  return 0;
      }

      return 1;
}

static void dos_file_metadata(struct lib_context *lc, struct dev_info *di,
                        void *meta)
{
      if (OPT_DUMP(lc))
            log_print(lc, "%s: filing metadata not supported (use fdisk "
                    "and friends)", handler);
}

/* Allocate a DOS partition sector struct and read the data. */
static int setup_rd(struct lib_context *lc, struct raid_dev *rd,
                struct dev_info *di, void *meta, union read_info *info);
static struct raid_dev *dos_read(struct lib_context *lc, struct dev_info *di)
{
      return read_raid_dev(lc, di, NULL,
                       sizeof(struct dos), DOS_CONFIGOFFSET,
                       to_cpu, is_dos, dos_file_metadata,
                       setup_rd, handler);
}

/* Support functions for dos_group to read the partition table(s). */
static int part_is_extended (struct dos_partition *part)
{
      return part->type == PARTITION_EXT ||
             part->type == PARTITION_EXT_LBA ||
             part->type == PARTITION_LINUX_EXT;
}

/* Get a partition start offset relative to a base location. */
static uint64_t get_part_start(const struct dos_partition *raw_part,
                       uint64_t offset)
{
      return (uint64_t) raw_part->start + offset;
}

/* RAID set allocation support function. */
static struct raid_set *_alloc_raid_set(struct lib_context *lc,
                              struct raid_dev *rd)
{
      struct raid_set *rs;

      if ((rs = find_set(lc, NULL, rd->name, FIND_TOP)))
            LOG_ERR(lc, NULL, "%s: RAID set %s already exists",
                  handler, rs->name);

      if (!(rs = alloc_raid_set(lc, handler)))
            return NULL;

      rs->status = rd->status;
      rs->type   = rd->type;

            if (!(rs->name = dbg_strdup(rd->name))) {
            dbg_free(rs);
            rs = NULL;
            log_alloc_err(lc, handler);
      }

      return rs;
}

/*
 * Allocate a DOS RAID device and a set.
 * Set the device up and add it to the set.
 */
static int _create_rs_and_rd(struct lib_context *lc, struct raid_dev *rd,
                       struct dos_partition *raw_part, uint64_t sector,
                       unsigned int part)
{
      struct raid_dev *r;
      struct raid_set *rs;

      if (!(r = alloc_raid_dev(lc, handler)))
            return 0;

      if (!(r->di = alloc_dev_info(lc, rd->di->path)))
            goto free_raid_dev;

        if (!(r->name = name(lc, rd, part, 1)))
            goto free_di;

      r->fmt = rd->fmt;
      r->status = rd->status;
      r->type = rd->type;

      if ((uint64_t) raw_part->start > sector)
            sector = 0;

      r->offset = get_part_start(raw_part, sector);
      r->sectors = (uint64_t) raw_part->length;

      if (!(rs = _alloc_raid_set(lc, r)))
            goto free_di;

      list_add_tail(&r->devs, &rs->devs);
      list_add_tail(&rs->list, LC_RS(lc));

      return 1;

  free_di:
      free_dev_info(lc, r->di);
  free_raid_dev:
      free_raid_dev(lc, &r);

      return 0;
}

/*
 * RAID set grouping.
 *
 * In this case it is not really about grouping, more about providing
 * the propper input to the activation layer by creating a RAID set per
 * partition and a RAID device hanging off it mapping the partition linearly.
 *
 * Partition code inspired by libparted and squeezed for this purpose (lemon).
 */
/* FIXME: Check for position of partition */
static int is_partition(struct dos_partition *p, uint64_t start_sector)
{
      return p->type != PARTITION_EMPTY && p->length && p->start;
}

static int group_rd_extended(struct lib_context *lc, struct raid_dev *rd,
                       uint64_t start_sector, uint64_t *extended_root,
                       unsigned int part)
{
      int ret = 0;
      uint64_t new_start_sector;
      struct dos *dos;
      struct dos_partition *p1, *p2;

      /* Allocate and read a logical partition table. */
      if (!(dos = alloc_private_and_read(lc, handler, sizeof(*dos),
                                 rd->di->path, start_sector << 9)))
            return 0;

      /* Weird: empty extended partitions are filled with 0xF6 by PM. */
#if   BYTE_ORDER != LITTLE_ENDIAN
      to_cpu(dos);
#endif
      if (dos->magic == PARTITION_MAGIC_MAGIC)
            goto out;
      
      /* Check magic to see if this is a real partition table. */
      if (dos->magic != DOS_MAGIC)
            goto out;

      /*
       * Logical partition tables only have two entries,
       * one for the partition and one for the next partition table.
       */
       
      /*
       * An entry pointing to the present logical partition.
       * It is an offset from the present partition table location.
       */
      p1 = &dos->partitions[0];
      
      /*
       * An entry pointing to the next logical partition table.
       * It is an offset from the main extended partition start.
       */
      p2 = &dos->partitions[1];

      /* If it is a partition, add it to the set */
      if (is_partition(p1, start_sector) &&
          !_create_rs_and_rd(lc, rd, p1, start_sector, part++))
            goto out;

      /*
       * Recurse into the logical partition chain.
       * Save start of main extended partition since
       * logical partition tables in the extended partition
       * are at offsets from this.
       */
      if (!*extended_root)
            *extended_root = start_sector;
      new_start_sector = get_part_start(p2, *extended_root);
      
      if (is_partition(p2, start_sector) &&
          !group_rd_extended(lc, rd, new_start_sector, extended_root, part))
            goto out;

      ret = 1;

  out:
      dbg_free(dos);
      return ret;
}

/* Handle primary partitions. */
static int group_rd(struct lib_context *lc, struct raid_dev *rd,
                uint64_t start_sector)
{
      unsigned int i;
      uint64_t part_start, part_end,
             extended_part_start = 0, extended_root = 0;
      struct dos *dos = META(rd, dos);
      struct dos_partition *raw_table_entry;

      /*
       * Walk the 4 array entries in the primary partition table.
       *
       * Finish all primary partitions before going on to
       * the extended partition. Hopefully, for now you only
       * have one extended partition!
       */
      for (i = 0; i < 4; i++) {
            raw_table_entry = &dos->partitions[i];
            if (!is_partition(raw_table_entry, start_sector))
                  continue;

            /* Add partition start from partition table to
             * start of drive.
             */
            part_start = get_part_start(raw_table_entry, start_sector);
            part_end   = part_start + raw_table_entry->length;
            
            /* Avoid infinite recursion (mostly). */
            if (part_start == start_sector ||
                part_end > rd->sectors)
                  continue;

            /*
             * If we have an extended partition, save
             * partition start as a flag for later use.
             * Else go put partition in set.
             */
            if (part_is_extended(raw_table_entry))
                  extended_part_start = part_start;
            else if (!_create_rs_and_rd(lc, rd, raw_table_entry,
                                  start_sector, i + 1))
                  return 0;
      }
      
      /* When we are finished with all the primary partitions,
       * go do the extended partition if we have one.
       * It always starts with partition 5.
       */
      return extended_part_start ?
             group_rd_extended(lc, rd, extended_part_start,
                         &extended_root, 5) : 1;
}

/* Add a DOS RAID device to a set */
static struct raid_set *dos_group(struct lib_context *lc,
                            struct raid_dev *rd)
{
      /*
       * Once we get here, a DOS partition table
       * has been discovered by dos_read.
       *
       * We need to run through the (nested) partition tables and create
       * a RAID set and a linear RAID device hanging off it for every primary
       * and logical partition, so that the activate code is happy.
       *
       * We start with start_sector = 0 because all primary partitions are
       * located at offsets from the start of the drive. This COULD be changed
       * to something else for some strange partitioning scheme because the
       * code will handle it.
       */
      return group_rd(lc, rd, 0) ? (struct raid_set*) 1 : NULL;
}

/*
 * Check integrity of a DOS RAID set.
 */
static int dos_check(struct lib_context *lc, struct raid_set *rs)
{
      return 1; /* Nice, eh ? */
}

static struct dmraid_format dos_format = {
      .name = HANDLER,
      .descr      = "DOS partitions on SW RAIDs",
      .caps = NULL, /* Not supported */
      .format = FMT_PARTITION,
      .read = dos_read,
      .write      = NULL, /* Not supported */
      .group      = dos_group,
      .check      = dos_check,
      .events     = NULL, /* Not supported */
#ifdef DMRAID_NATIVE_LOG
      .log  = NULL, /* Not supported */
#endif
};

/* Register this format handler with the format core. */
int register_dos(struct lib_context *lc)
{
      return register_format_handler(lc, &dos_format);
}

/*
 * Set the RAID device contents up derived from the DOS ones
 *
 * For a DOS partition we essentially just save the
 * partition table sector and let dos_group do the rest...
 */
static int setup_rd(struct lib_context *lc, struct raid_dev *rd,
                struct dev_info *di, void *meta, union read_info *info)
{
      struct dos *dos = meta;

      if (!(rd->meta_areas = alloc_meta_areas(lc, rd, handler, 1)))
            return 0;

      rd->meta_areas->offset = DOS_CONFIGOFFSET >> 9;
      rd->meta_areas->size = sizeof(*dos);
      rd->meta_areas->area = (void*) dos;

        rd->di  = di;
      rd->fmt = &dos_format;

        rd->status = s_ok; /* Always :-) */
      rd->type   = t_partition;

      rd->offset   = DOS_DATAOFFSET;
      rd->sectors  = di->sectors;

        return (rd->name = name(lc, rd, 0, 0)) ? 1 : 0;
}

Generated by  Doxygen 1.6.0   Back to index