Logo Search packages:      
Sourcecode: acorn-fdisk version File versions  Download package

pcbios.c

/*
 * lib/scheme/pcbios.c
 *
 * Partition backend for PC/BIOS partitioning scheme.  This consists
 * of a partition table in the first sector of the disk which contains
 * both CHS and LBA values for the partitions.  There can be up to four
 * entries in the primary partition table.  One of these can be an
 * extended partition containing up to four extra partitions.
 *
 * NOTE: We do not support extended partitions yet.
 *
 * Copyright (C) 1997,1998 Russell King
 */
#include <assert.h>
#include <stdlib.h>
#include <string.h>

#include "util/debug.h"
#include "util/error.h"
#include "util/warning.h"
#include "part/part.h"
#include "part/utils.h"
#include "scheme/pcbios.h"

#define PCBIOS_SAVE
#define PCBIOS_DEBUG

#define PCBIOS_SECTOR_SIZE    512
#define PCBIOS_BOOT_SECTOR    0
#define PCBIOS_NR_PRIMARY     4

#define is_extended(x)  ((x) == ptyp_dos_extended)

/* Prototype: void pcbios_copychsin(chs_t *chs, u8 *p)
 * Purpose  : Copy a CHS value from a PC partition table to a chs_t structure
 * Params   : chs - destination for CHS values
 *          : p   - pointer to start of packed CHS values
 */
static void pcbios_copychsin(chs_t *chs, u8 *p)
{
  chs->head     = p[0];
  chs->sector   = (p[1] & 63) - 1;
  chs->cylinder = p[2] | (p[1] & 0xc0) << 2;
}

/* Prototype: void pcbios_copychsout(u8 *p, chs_t *chs)
 * Purpose  : Copy a CHS value to a PC partition table from a chs_t structure
 * Params   : p   - pointer to start of packed CHS values
 *          : chs - values to pack for CHS values
 */
static void pcbios_copychsout(u8 *p, chs_t *chs)
{
  p[0] = chs->head;
  p[1] = ((chs->sector + 1) & 63) | ((chs->cylinder >> 2) & 0xc0);
  p[2] = chs->cylinder & 0xff;
}

/* Prototype: u32 pcbios_copylogin(u8 *p)
 * Purpose  : Copy a logical start/end 32-bit value from a PC partition table
 * Params   : p - pointer to start of 32-bit value
 * Returns  : 32-bit unsigned integer
 */
static u32 pcbios_copylogin(u8 *p)
{
  return p[0] | p[1] << 8 | p[2] << 16 | p[3] << 24;
}

/* Prototype: u_int pcbios_copylogout(u8 *p, u32 l)
 * Purpose  : Copy a logical start/end 32-bit value to a PC partition table
 * Params   : p - pointer to start of 32-bit value
 *          : l - value to place
 */
static void pcbios_copylogout(u8 *p, u32 l)
{
  p[0] = l & 255;
  p[1] = (l >> 8) & 255;
  p[2] = (l >> 16) & 255;
  p[3] = (l >> 24) & 255;
}

/* Prototype: u_int pcbios_detect(part_t *part)
 * Purpose  : detect a drive partitioned with a PC/BIOS partition table
 * Params   : part - partitionable device
 * Returns  : FALSE if not detected
 */
static u_int
pcbios_detect(part_t *part)
{
  u_char sector[PCBIOS_SECTOR_SIZE];
  u_int ret = 0;

  dbg_printf("pcbios_detect()");
  dbg_level_up();

  assert(part != NULL);

  do {
    if (blkio_setblocksize(part->blkio, PCBIOS_SECTOR_SIZE) != PCBIOS_SECTOR_SIZE)
      break;

    if (blkio_read(part->blkio, sector, PCBIOS_BOOT_SECTOR, 1) != 1)
      break;

    if (sector[510] != 0x55 || sector[511] != 0xaa)
      break;

    ret = 1;
  } while (0);

  dbg_level_down();
  dbg_printf("ret%s detected", ret ? "" : " not");
  return ret;
}

static u_int
pcbios_readextended(part_t *part, blk_t blkstart, u_int size)
{
  static u_char sector[PCBIOS_SECTOR_SIZE];
  u_int ret = 0;
  u_int extended_nr = 5;

  dbg_printf("pcbios_readextended(%d, %d)", blkstart, size);
  dbg_level_up();

  assert(part != NULL);

  do {
    u_char *p = sector + 0x1be;
    partinfo_i_t pinfo;
    u_int parn;

    if (blkio_read(part->blkio, sector, blkstart, 1) != 1)
      break;

    if (sector[510] != 0x55 || sector[511] != 0xaa) {
      issue_warning("The extended partition table does not contain a valid signature."
                      "  This will be corrected when writing partition information "
                      "to disk.");
      sector[510] = 0x55;
      sector[511] = 0xaa;
    }

    part->data.pcbios.extended_sector = sector;

    ret = 1;
    for (parn = 0; parn < PCBIOS_NR_PRIMARY; parn++, p += 16)
      if (p[4] && !is_extended(p[4])) {
        blk_t start;
        u32 size;

        pinfo.info.chs_valid = 1;
        pinfo.info.kern_part_no = extended_nr;
        pcbios_copychsin(&pinfo.info.chs_start, p + 1);
        pcbios_copychsin(&pinfo.info.chs_end, p + 5);
        pinfo.info.type         = p[4];
        start = pcbios_copylogin(p + 8) + blkstart;
        size  = pcbios_copylogin(p + 12);
        pinfo.info.blk_start    = start;
        pinfo.info.blk_end      = start + size - 1;
        if (!part_add (part, extended_nr, &pinfo)) {
          ret = 0;
          break;
        }
        extended_nr += 1;
      }
    p = sector + 0x1be;
    for (parn = 0; parn < PCBIOS_NR_PRIMARY; parn++, p += 16)
      if (is_extended(p[4])) {
            blkstart += pcbios_copylogin(p + 8);
            size = pcbios_copylogin(p + 12);
      }
  } while (0);

  dbg_level_down();
  dbg_printf("ret %s", ret ? "ok" : "error");

  return ret;
}

/* Prototype: u_int pcbios_readinfo(part_t *part)
 * Purpose  : read all partition information from the drive
 * Params   : part - partitionable device
 * Returns  : FALSE on error
 */
static u_int
pcbios_readinfo(part_t *part)
{
  static u_char sector[PCBIOS_SECTOR_SIZE];
  u_int ret = 0;

  dbg_printf("pcbios_readinfo()");
  dbg_level_up();

  assert(part != NULL);

  do {
    partinfo_i_t pinfo;
    chs_t chs;
    blk_t start;
    u_int parn, valid;
    u_char *p;

    part->data.pcbios.pcboot_sector = sector;

    if (blkio_read(part->blkio, sector, PCBIOS_BOOT_SECTOR, 1) != 1)
      break;

    valid = sector[510] == 0x55 && sector[511] == 0xaa;

    if (!valid) {
      issue_warning("The master boot sector does not contain a valid signature."
                      "  This will be corrected when writing partition information "
                      " to disk.");
      sector[510] = 0x55;
      sector[511] = 0xaa;
    }

    p = part->data.pcbios.pcboot_sector + 0x1be;

#if 0
    if (valid && chs.cylinder == 0) {
    /* } chs is uninitialised here */
#else
    if (valid) {
#endif
      /*
       * Attempt to calculate the size of one track
       *  track_size = (start_log / (cyl * head) - sector
       */
      start = pcbios_copylogin(p + 8);
      pcbios_copychsin(&chs, p + 1);
      start /= chs.head ? chs.head : 1;
      start -= chs.sector;
    } else {
      geometry_t geo;

      blkio_getgeometry(part->blkio, &geo);
      
      start = geo.sectors * geo.heads;
    }

    memset (&pinfo, 0, sizeof(pinfo));
    pinfo.info.chs_valid        = 1;
    pinfo.info.chs_start.sector       = 0;
    pinfo.info.chs_end.sector   = start - 1;
    pinfo.info.blk_end          = start - 1;
    pinfo.info.type             = ptyp_pc_table;

    if (!part_add(part, 0, &pinfo))
      break;

    /*
     * Look for all possible primary partitions
     *  - feature - if the drive is already partitioned,
     *     with non-Linux partitions, we will assert!
     */
    ret = 1;
    for (parn = 0; parn < PCBIOS_NR_PRIMARY; parn++, p += 16)
      if (p[4]) {
        blk_t start;
        u32 size;

        pinfo.info.chs_valid = 1;
        pinfo.info.kern_part_no = parn + 1;
        pcbios_copychsin(&pinfo.info.chs_start, p + 1);
        pcbios_copychsin(&pinfo.info.chs_end, p + 5);
        pinfo.info.type         = p[4];
        start = pcbios_copylogin(p + 8);
        size  = pcbios_copylogin(p + 12);
        pinfo.info.blk_start    = start;
        pinfo.info.blk_end      = start + size - 1;
        if (!part_add (part, parn + 1, &pinfo)) {
          ret = 0;
          break;
        }
        if (is_extended(p[4]) && !pcbios_readextended(part, start, size)) {
          ret = 0;
          break;
        }
      }
  } while (0);

  dbg_level_down();
  dbg_printf("ret %s", ret ? "ok" : "error");

  return ret;
}

/* Prototype: u_int pcbios_writeinfo(part_t *part)
 * Purpose  : write all partition information back to the drive
 * Params   : part - partitionable device
 * Returns  : FALSE on error
 */
static u_int
pcbios_writeinfo(part_t *part)
{
  u_int parn;
  u_int ret = 0;
  u_char *p;

  dbg_printf("pcbios_writeinfo()");
  dbg_level_up();

  assert(part != NULL);
  assert(part->nr_partitions <= PCBIOS_NR_PRIMARY + 1);
  assert(part->data.pcbios.pcboot_sector != NULL);

  /*
   * We use the mbr sector that we loaded when we started
   * the partition process, so keeping any PC code
   */
  p = part->data.pcbios.pcboot_sector + 0x1be;
  /*
   * Write the magic number
   */
  part->data.pcbios.pcboot_sector[510] = 0x55;
  part->data.pcbios.pcboot_sector[511] = 0xaa;
  /*
   * Loop through *all* possible partitions - we zero the
   * undefined partition entries.
   */
  for (parn = 1; parn < PCBIOS_NR_PRIMARY + 1; parn++, p += 16) {
    partinfo_i_t *info;

    memset(p, 0, 16);

    if (parn < part->nr_partitions) {
      info = part->partinfo[parn];

      if (info && info->info.type != ptyp_none) {
        /*
         * We should always have 'type' as an 8-bit value
         */
        assert(info->info.type <= 0xff);
        /*
         * p[0] = boot flag... (we don't set yet)
         */
        p[4] = info->info.type;
        pcbios_copychsout(p + 1, &info->info.chs_start);
        pcbios_copychsout(p + 5, &info->info.chs_end);
        pcbios_copylogout(p + 8, info->info.blk_start);
        pcbios_copylogout(p + 12, info->info.blk_end - info->info.blk_start + 1);
      }
    }
  }

#ifdef PCBIOS_DEBUG
  dbg_printf("Boot sector:");
  dbg_memdump(part->data.pcbios.pcboot_sector, 512);
#endif
#ifdef PCBIOS_SAVE
  if (blkio_write(part->blkio, part->data.pcbios.pcboot_sector, PCBIOS_BOOT_SECTOR, 1) != 1)
    set_error("unable to save partition information");
  else
    ret = 1;
#else
  set_error("PC/BIOS partition information cannot be saved with this build");
#endif

  dbg_level_down();
  dbg_printf("ret %s", ret ? "ok" : "error");

  return ret;
}

/* Prototype: u_int pcbios_allocate(part_t *part, partinfo_t *pnew)
 * Purpose  : allocate a partition entry number and a kernel partition
 *            number for a new partition
 * Params   : part - partitionable device
 * Returns  : partition number, or PARN_ERROR on error
 */
static u_int
pcbios_allocate(part_t *part, partinfo_t *pnew)
{
  u_int parn;

  dbg_printf("pcbios_allocate()");

  assert(part != NULL);
  assert(pnew != NULL);

  /*
   * Look for a free primary partition
   */
  for (parn = 1; parn < 1 + PCBIOS_NR_PRIMARY && parn < part->nr_partitions; parn++)
    if (!part->partinfo[parn] || part->partinfo[parn]->info.type == ptyp_none)
      break;

  if (parn >= 1 + PCBIOS_NR_PRIMARY) {
    set_error("primary bios partition table is full");
    parn = PARN_ERROR;
  } else {
    pnew->kern_part_no = parn;
    pnew->type = ptyp_linux_native;
  }

  dbg_printf("ret %d", parn);

  assert(parn < 1 + PCBIOS_NR_PRIMARY || parn == PARN_ERROR);

  return parn;
}

/* Prototype: u_int pcbios_validate_change(part_t *part, u_int parn,
 *                                         const partinfo_t *pold, const partinfo_t *pnew)
 * Purpose  : validate changes made to a partition
 * Params   : part - partitionable device
 *          : parn - partition number
 *          : pold - old partition information
 *          : pnew - new partition information
 * Returns  : FALSE if we reject the change
 */
static u_int
pcbios_validate_change(part_t *part, u_int parn, const partinfo_t *pold, const partinfo_t *pnew)
{
  u_int ret = 0;

  dbg_printf("pcbios_validate_change(parn=%d)", parn);
  dbg_level_up();

  assert(part != NULL);
  assert(pold != NULL);
  assert(parn < part->nr_partitions);

  do {
    if (parn == 0) {
      set_error("unable to modify the BIOS partition table partition");
      break;
    }

    if (pnew && check_overlap(part, parn, pnew)) {
      set_error("the modified partition overlaps an existing partition");
      break;
    }

    ret = 1;
  } while (0);

  dbg_level_down();
  dbg_printf("ret %svalid", ret ? "" : "in");

  return ret;
}

/* Prototype: u_int pcbios_validate_creation(part_t *part, u_int parn,
 *                                           const partinfo_t *pold, const partinfo_t *pnew)
 * Purpose  : validate changes made to a partition
 * Params   : part - partitionable device
 *          : parn - partition number
 *          : pold - old partition information (NULL if none)
 *          : pnew - new partition information
 * Returns  : FALSE if we reject the creation
 */
static u_int
pcbios_validate_creation(part_t *part, u_int parn, const partinfo_t *pold, const partinfo_t *pnew)
{
  u_int ret = 0;

  dbg_printf("pcbios_validate_creation(parn=%d)", parn);
  dbg_level_up();

  assert(part != NULL);
  assert(parn < part->nr_partitions);

  do {
    if (pold != NULL) {
      set_error("unable to create partition in this slot");
      break;
    }

    if (pnew && check_overlap(part, parn, pnew)) {
      set_error("the new partition overlaps an existing partition");
      break;
    }

    ret = 1;
  } while (0);

  dbg_level_down();
  dbg_printf("ret %svalid", ret ? "" : "in");

  return ret;
}

/* Prototype: u_int pcbios_validate_deletion(part_t *part, u_int parn, const partinfo_t *pold)
 * Purpose  : validate a deletion of a partition
 * Params   : part - partitionable device
 *          : parn - partition number
 *          : pold - old partition information
 * Returns  : FALSE if we reject the deletion
 */
static u_int
pcbios_validate_deletion(part_t *part, u_int parn, const partinfo_t *pold)
{
  u_int ret = 0;

  dbg_printf("pcbios_validate_deletion(parn=%d)", parn);
  dbg_level_up();

  assert(part != NULL);
  assert(pold != NULL);
  assert(parn < part->nr_partitions);

  if (parn > 0)
    ret = 1;
  else
    set_error("unable to delete partition");

  dbg_level_down();
  dbg_printf("ret %svalid", ret ? "" : "in");

  return ret;
}

/* Prototype: u_int icside_validate_partno(part_t *part, u_int parn)
 * Purpose  : validate a partition number
 * Params   : part - partitionable device
 *          : parn - partition number
 * Returns  : FALSE if we reject the partition number
 */
static u_int
pcbios_validate_partno(part_t *part, u_int parn)
{
  u_int ret = 0;

  dbg_printf("pcbios_validate_partno(parn=%d)", parn);
  dbg_level_up();

  assert(part != NULL);

  if (parn > 0 && parn < 1 + PCBIOS_NR_PRIMARY)
    ret = 1;
  else
    set_error("the requested partition is invalid");

  dbg_level_down();
  dbg_printf("ret %svalid", ret ? "" : "in");

  return ret;
}

/* Prototype: ptyp_t pcbios_nexttype(part_t *part, u_int parn, ptyp_t current, int dir)
 * Purpose  : return next valid partition type for an entry
 * Params   : part    - partitionable device
 *          : parn    - partition number
 *          : current - currently selected partition type
 *          : dir     - next/previous flag
 * Returns  : next valid partition type
 */
static ptyp_t
pcbios_nexttype(part_t *part, u_int parn, ptyp_t current, int dir)
{
  static ptyp_t types[] = {
    ptyp_dos12, ptyp_xenixroot, ptyp_xenixusr, ptyp_dos16l32, ptyp_dos_extended,
    ptyp_dos16g32, ptyp_hpfs, ptyp_aix, ptyp_aixboot, ptyp_os2, ptyp_win98_extended,
    ptyp_venix, ptyp_novell, ptyp_microport, ptyp_dm6_aux3, ptyp_dm6,
    ptyp_ezdrive, ptyp_gnuhurd, ptyp_novelnet286, ptyp_novelnet386, ptyp_pcix, ptyp_old_minix,
    ptyp_minix, ptyp_linux_swap, ptyp_linux_native, ptyp_linux_extended, ptyp_amoeba,
    ptyp_amoebabbt, ptyp_bsd386, ptyp_bsdifs, ptyp_bsdiswap, ptyp_syrinx, ptyp_cpm,
    ptyp_dosaccess, ptyp_dosro, ptyp_dosscc, ptyp_bbt
  };
#define NR_TYPES (sizeof(types) / sizeof(ptyp_t))
  ptyp_t ptype = current;

  dbg_printf("pcbios_nexttype(parn=%d, current=0x%X, %s)", parn, current,
           dir > 0 ? "next" : "previous");
  dbg_level_up();
  assert(part != NULL);
  assert(parn < part->nr_partitions);

  if (parn == 0)
    ptype = ptyp_pc_table;
  else {
    s_int i;

    for (i = 0; i < NR_TYPES; i++)
      if (types[i] == current)
        break;

    if (i < NR_TYPES && types[i] == current) {
      i += dir;

      if (i < 0)
        i = 0;

      if (i >= NR_TYPES)
        i = NR_TYPES - 1;

      ptype = types[i];
    } else
      ptype = ptyp_linux_native;
  }

  dbg_level_down();
  dbg_printf("ret 0x%X", ptype);

  return ptype;
}
scheme_t pcbios_scheme = {
  "PC/BIOS",
  pcbios_detect,
  pcbios_readinfo,
  pcbios_writeinfo,
  pcbios_allocate,
  pcbios_validate_change,
  pcbios_validate_creation,
  pcbios_validate_deletion,
  pcbios_validate_partno,
  pcbios_nexttype
};

Generated by  Doxygen 1.6.0   Back to index