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

icside.c

/*
 * lib/scheme/icside.c
 *
 * 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/types.h"
#include "part/part.h"
#include "part/utils.h"

#define ICSIDE_SECTOR_SIZE    512
#define ICSIDE_PART_SECTOR    0
#define ICSIDE_NR_PARTITIONS  16

#undef  ICSIDE_DUMP
#define ICSIDE_SAVE

#define NR(x) (sizeof(x) / sizeof(x[0]))

typedef union {
  struct part {
    u32 start;
    s32 size;
  } p[ICSIDE_SECTOR_SIZE / sizeof(struct part)];
  u8 sector[ICSIDE_SECTOR_SIZE];
} icside_pe_t;

static u_int
icside_checksum(u8 *sector)
{
  u_int sum = 0x50617274;
  int i;

  for (i = 0; i < 508; i++)
    sum += sector[i];

  return sum;
}

/* Prototype: u_int icside_detect(part_t *part)
 * Purpose  : detect a drive partitioned with an ICS IDE table
 * Params   : part - partitionable device
 * Returns  : FALSE if not detected
 */
static u_int
icside_detect(part_t *part)
{
  u_int ret = 0;

  dbg_printf("icside_detect()");
  dbg_level_up();

  assert(part != NULL);

  do {
    icside_pe_t sector;
    u32 calc_checksum, disc_checksum;

    if (blkio_setblocksize(part->blkio, ICSIDE_SECTOR_SIZE) != ICSIDE_SECTOR_SIZE)
      break;

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

    calc_checksum = icside_checksum(sector.sector);
    disc_checksum = *(u32 *)&sector.sector[508];
    dbg_printf("-icside disc checksum: %08X", disc_checksum);
    dbg_printf("-icside calc checksum: %08X", calc_checksum);

    if (calc_checksum != disc_checksum) {
      dbg_memdump(sector.sector, ICSIDE_SECTOR_SIZE);
      break;
    }

    ret = 1;
  } while (0);

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

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

  dbg_printf("icside_readinfo()");
  dbg_level_up();

  assert(part != NULL);

  do {
    icside_pe_t sector;
    partinfo_i_t pinfo;
    char temp_sector[512];
    u_int idx, part_no;

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

    ret = 1;

    pinfo.info.chs_valid = 0;

    /* Scan through all available partitions, detecting filecore,
     * Linux and non-Linux partitions
     */
    for (idx = 0, part_no = 1; idx < ICSIDE_NR_PARTITIONS; idx ++, part_no ++) {
      pinfo.info.chs_valid    = 0;
      pinfo.info.blk_start    = sector.p[idx].start;
      pinfo.data.icside.idx   = idx;

      if (sector.p[idx].size > 0) {
        /* If size > 0, then we have a filecore partition */
        pinfo.info.blk_start    = sector.p[idx].start;
        pinfo.info.blk_end      = sector.p[idx].start + sector.p[idx].size - 1;
        pinfo.info.type         = ptyp_filecore;
        pinfo.info.kern_part_no = part_no;
        pinfo.data.icside.idx   = idx;

        if (!part_add(part, part_no, &pinfo)) {
          ret = 0;
          break;
        }
        /* should we check the filecore boot sector? */
      } else if (sector.p[idx].size < 0) {
        /* Otherwise if size < 0, we have some other partition */
        if (blkio_read(part->blkio, temp_sector, pinfo.info.blk_start, 1) == 1 &&
            memcmp(temp_sector, "LinuxPart", 9) == 0) {

          pinfo.info.chs_valid    = 0;
          pinfo.info.blk_start    = sector.p[idx].start;
          pinfo.info.blk_end      = sector.p[idx].start - sector.p[idx].size - 1;
          pinfo.info.kern_part_no = part_no;
          pinfo.data.icside.idx   = idx;

          switch (temp_sector[9]) {
          case 'N':
            pinfo.info.type = ptyp_linux_native;
            break;
          case 'S':
            pinfo.info.type = ptyp_linux_swap;
            break;
          default:
            break;
          }
          if (!part_add(part, part_no, &pinfo)) {
            ret = 0;
            break;
          }
        } else {
          /* Mark other partitions as unknown */
          pinfo.info.chs_valid    = 0;
          pinfo.info.blk_start    = sector.p[idx].start;
          pinfo.info.blk_end      = (sector.p[idx].start - sector.p[idx].size) - 1;
          pinfo.info.type         = ptyp_unknown;
          pinfo.info.kern_part_no = part_no;
          pinfo.data.icside.idx   = idx;

          if (!part_add(part, part_no, &pinfo)) {
            ret = 0;
            break;
          }
        }
      }
    }
  } while (0);

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

  return ret;
}

/* Prototype: u_int icside_writeinfo(part_t *part)
 * Purpose  : write all partition information back to the drive
 * Params   : part - partitionable device
 * Returns  : FALSE on error
 */
static u_int
icside_writeinfo(part_t *part)
{
  icside_pe_t sector;
  u_int part_no, ret = 0;
  unsigned long csum;

  dbg_printf("icside_writeinfo()");
  dbg_level_up();

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

  memset(sector.sector, 0, sizeof(sector));

  for (part_no = 1; part_no < part->nr_partitions; part_no++)
    if (part->partinfo[part_no]) {
      partinfo_i_t *info = part->partinfo[part_no];
      u_int idx = part_no - 1;

      /*
       * Linux table partitions are just placeholders - ignore them
       */
      switch (info->info.type) {
      case ptyp_none:
        break;

      case ptyp_unknown:
        sector.p[idx].start = info->info.blk_start;
        sector.p[idx].size  = -((info->info.blk_end - info->info.blk_start) + 1);
        break;

      case ptyp_linux_native:
      case ptyp_linux_swap:
        sector.p[idx].start = info->info.blk_start;
        sector.p[idx].size  = -((info->info.blk_end - info->info.blk_start) + 1);
        break;

      case ptyp_filecore:
        sector.p[idx].start = info->info.blk_start;
        sector.p[idx].size  = (info->info.blk_end - info->info.blk_start) + 1;
        break;

      default:
        assert(0);
      }
    }
  /* XXX possible endianness issue */
  csum = icside_checksum(sector.sector);
  memcpy(&sector.sector[508], &csum, sizeof(csum));

#ifdef ICSIDE_DUMP
  dbg_memdump(sector.sector, ICSIDE_SECTOR_SIZE);
#endif
#ifdef ICSIDE_SAVE
  ret = 1;
  if (blkio_write(part->blkio, sector.sector, ICSIDE_PART_SECTOR, 1) != 1)
    ret = 0;
#endif

  for (part_no = 1; part_no < part->nr_partitions; part_no++)
    if (part->partinfo[part_no]) {
      partinfo_i_t *info = part->partinfo[part_no];
      char *id = NULL;

      switch (info->info.type) {
      case ptyp_linux_native:
        id = "LinuxPartN";
        break;

      case ptyp_linux_swap:
        id = "LinuxPartS";
        break;

      default:
        break;
      }

      if (id) {
        memset(sector.sector, 0, ICSIDE_SECTOR_SIZE);
        memcpy(sector.sector, id, strlen(id));

        dbg_printf("write to 0x%lX", info->info.blk_start);
#ifdef ICSIDE_DUMP
        dbg_memdump(sector.sector, ICSIDE_SECTOR_SIZE);
#endif
#ifdef ICSIDE_SAVE
        if (blkio_write(part->blkio, sector.sector, info->info.blk_start, 1) != 1)
          ret = 0;
#endif
      }
    }

#ifndef ICSIDE_SAVE
  set_error("ICS-style partition information cannot be saved with this build");
#endif

  dbg_level_down();
  dbg_printf("ret=%d", ret);

  return ret;
}

/* Prototype: u_int icside_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
icside_allocate(part_t *part, partinfo_t *pnew)
{
  u_int parn;

  dbg_printf("icside_allcate()");

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

  for (parn = 1; parn < part->nr_partitions; parn++)
    if (!part->partinfo[parn])
      break;

  if (parn >= ICSIDE_NR_PARTITIONS) {
    set_error("too many partitions already created");
    parn = PARN_ERROR;
  } else {
    pnew->kern_part_no = parn;
    pnew->type = ptyp_linux_native;
  }

  dbg_printf("ret %d", parn);
  return parn;
}

/* Prototype: u_int icside_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
icside_validate_change(part_t *part, u_int parn, const partinfo_t *pold, const partinfo_t *pnew)
{
  u_int ret = 0;

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

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

  do {
    if (pold->type == ptyp_filecore) {
      set_error("you cannot edit a filecore partition");
      break;
    }

    if (pnew) {
      if (pnew->type == ptyp_filecore) {
        set_error("you cannot create a filecore partition");
        break;
      }

      if (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 icside_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
icside_validate_creation(part_t *part, u_int parn, const partinfo_t *pold, const partinfo_t *pnew)
{
  u_int ret = 0;

  dbg_printf("icside_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) {
      if (pnew->type == ptyp_filecore) {
        set_error("you cannot create a filecore partition");
        break;
      }

      if (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 icside_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
icside_validate_deletion(part_t *part, u_int parn, const partinfo_t *pold)
{
  u_int ret = 0;

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

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

  ret = 1;

  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
icside_validate_partno(part_t *part, u_int parn)
{
  u_int ret = 0;

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

  assert(part != NULL);

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

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

  return ret;
}

/* Prototype: ptyp_t icside_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
icside_nexttype(part_t *part, u_int parn, ptyp_t current, int dir)
{
  ptyp_t types[] = { ptyp_filecore, ptyp_linux_native, ptyp_linux_swap, ptyp_unknown };
  ptyp_t ptype;

  dbg_printf("icside_nexttype(parn=%d, current=0x%X, %s)", parn, current,
           dir > 0 ? "next" : "previous");
  dbg_level_up();

  assert(parn < part->nr_partitions);

  if (parn == 1)
    ptype = ptyp_filecore;
  else {
    int i;

    for (i = 0; i < NR(types); i++)
      if (types[i] == current)
        break;
    i += dir;
    if (i < 0)
      i = 0;
    else if (i >= NR(types))
      i = NR(types) - 1;
    ptype = types[i];
  }

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

  return ptype;
}

scheme_t icside_scheme = {
  "ICSIDEFS",
  icside_detect,
  icside_readinfo,
  icside_writeinfo,
  icside_allocate,
  icside_validate_change,
  icside_validate_creation,
  icside_validate_deletion,
  icside_validate_partno,
  icside_nexttype
};

Generated by  Doxygen 1.6.0   Back to index