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

linux.c

/*
 * lib/scheme/linux.c
 *
 * Partition backend for Linux on Filecore partitioning scheme.  This
 * uses the non-ADFS descriptor to point to the start of the disc to
 * be partitioned.  The first two sectors contain a partition table to
 * identify the Linux partitions contained within.
 *
 * Copyright (C) 1997,1998 Russell King
 */
#include <assert.h>
#include <stdlib.h>
#include <string.h>

#include "util/debug.h"
#include "util/error.h"
#include "part/part.h"
#include "part/utils.h"
#include "blkio/filecore.h"
#include "scheme/filecore.h"

#define LINUX_SAVE
#define LINUX_DEBUG

#define FILECORE_MAX_PARTITIONS     16
#define FILECORE_LINUX_NATIVE 0xdeafa1de
#define FILECORE_LINUX_SWAP   0xdeafab1e
#define FILECORE_DOSDISC      0x0d05d15c
#define FILECORE_FILECORE     0xf11ec05e

union partition_sector_u {
  struct {
    u32 magic;
    u32 offset_start;
    u32 size;
  } p[FILECORE_MAX_PARTITIONS];
  u8 sector[FILECORE_SECTOR_SIZE];
};

static void fcl_internalcheck(part_t *part)
{
  u_int i;

  assert(part != NULL);
  assert(part->nr_partitions <= FILECORE_MAX_PARTITIONS + 3);

  for (i = 0; i < part->nr_partitions; i++)
    if (part->partinfo[i])
      assert(part->partinfo[i]->info.kern_part_no == i);
}

/* Prototype: u_int fcl_detect(part_t *part)
 * Function : detect a drive partitioned with a filecore-linux partition table
 * Params   : part - partitionable device
 * Returns  : FALSE if not detected
 */
static u_int
fcl_detect(part_t *part)
{
  u_char sector[FILECORE_SECTOR_SIZE];
  disc_record_t *disc_record;
  u_int ret = 0;

  dbg_printf("fcl_detect()");
  dbg_level_up();
  assert(part != NULL);

  do {
    u_int disc_csum, calc_csum;
    if (blkio_setblocksize(part->blkio, FILECORE_SECTOR_SIZE) != FILECORE_SECTOR_SIZE)
      break;

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

    disc_csum = sector[511];
    calc_csum = filecore_checksum(sector);
    dbg_printf("-fcl disc checksum: %02X", disc_csum);
    dbg_printf("-fcl calc checksum: %02X", calc_csum);
    if (calc_csum != disc_csum)
      break;

    /*
     * Check that we do not have a sector filled with zero
     */
    disc_record = (disc_record_t *)(sector + 0x1c0);
    if (disc_record->d.disc_size == 0 && disc_record->d.disc_size_high == 0)
      break;

    part->data.filecore_linux.disc_record = *disc_record;
    part->data.filecore_linux.boot_checksum = sector[511];

    /*
     * Check that a Linux partition table exists, or no non-ADFS partition
     */
    if ((sector[508] & 15) != 9 && sector[508] != 0)
      break;

    ret = 1;
  } while (0);

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

  return ret;
}

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

  dbg_printf("fcl_readinfo()");
  dbg_level_up();
  assert(part != NULL);

  do {
    disc_record_t *disc_record = &part->data.filecore_linux.disc_record;
    union partition_sector_u sector;
    partinfo_i_t pinfo;
    blk_t parttable_start;
    u_int i;

    /*
     * First partition is the filecore image
     */
    pinfo.info.chs_valid    = 0;
    pinfo.info.blk_start    = 0;
    pinfo.info.blk_end      = (disc_record->d.disc_size_high << (32 - 9) |
                         disc_record->d.disc_size >> 9) - 1;
    pinfo.info.type         = ptyp_filecore;
    pinfo.info.kern_part_no = 1;

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

    /*
     * If we don't have a non-filecore image, then we don't have a Linux
     * partition table, and therefore no Linux partitions.
     */
    if (disc_record->packing[508 - 0x1c0] == 0) {
      pinfo.info.chs_valid    = 0;
      pinfo.info.blk_start    = pinfo.info.blk_end + 1;
      pinfo.info.blk_end      = pinfo.info.blk_start + 1;
      pinfo.info.type         = ptyp_reserved;
      pinfo.info.kern_part_no = 2;
      if (!part_add(part, 2, &pinfo))
        break;
      ret = 1;
    } else {

      /*
       * Locate the start of the non-filecore image, and add that as
       * partition 2.  (It is always partition 2).
       */
      parttable_start = (disc_record->packing[509 - 0x1c0] |
                   disc_record->packing[510 - 0x1c0] << 8) *
                    disc_record->d.heads * disc_record->d.secspertrack;
      pinfo.info.chs_valid    = 0;
      pinfo.info.blk_start    = parttable_start;
      pinfo.info.blk_end      = pinfo.info.blk_start + 1;
      pinfo.info.type         = ptyp_linux_table;
      pinfo.info.kern_part_no = 2;

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

      /*
       * Now read the Linux partition table - one sector, but in fact
       * two sectors are set aside for it.
       */
      if (blkio_read(part->blkio, &sector, pinfo.info.blk_start, 1) != 1)
        break;

      ret = 1;

      /*
       * And add the partitions found within...  We only recognise
       * two different magic numbers representing Linux native and
       * Linux swap partition types.
       */
      for (i = 0; i < FILECORE_MAX_PARTITIONS; i++) {
        if (sector.p[i].magic == FILECORE_LINUX_NATIVE ||
            sector.p[i].magic == FILECORE_LINUX_SWAP) {
          pinfo.info.chs_valid    = 0;
          pinfo.info.blk_start    = parttable_start + sector.p[i].offset_start;
          pinfo.info.blk_end      = parttable_start + sector.p[i].offset_start
                            + sector.p[i].size - 1;
          pinfo.info.type         = sector.p[i].magic == FILECORE_LINUX_NATIVE ?
                            ptyp_linux_native : ptyp_linux_swap;
        pinfo.info.kern_part_no = 3 + i;
          if (!part_add(part, 3 + i, &pinfo)) {
            ret = 0;
            break;
          }
        }
      }
    }
  } while (0);

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

  return ret;
}

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

  dbg_printf("fcl_writeinfo()");
  dbg_level_up();
  fcl_internalcheck(part);

  do {
    disc_record_t *disc_record = &part->data.filecore_linux.disc_record;
    union partition_sector_u sector;

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

    /*
     * Ensure that the information we read was a valid boot sector.
     * Ok, I'm being paranoid... but then partitioning is dangerous.
     */
    if (filecore_checksum(sector.sector) != sector.sector[511]) {
      set_error("filecore boot sector is corrupt");
      break;
    }
    /*
     * We check to make sure that the checksum we have here is
     * identical to that when we read the sector in fcl_readinfo.
     * If it's different, we're in trouble!
     */
    if (sector.sector[511] != part->data.filecore_linux.boot_checksum) {
      set_error("filecore boot sector has been changed by another program");
      break;
    }
    /*
     * If we have partitions > 2, then we have a non-ADFS descriptor.
     * Otherwise, we remove the non-ADFS descriptor  (If it wasn't a
     * Linux descriptor, we shouldn't be here).
     */
    if (part->nr_partitions > 2 && part->partinfo[2]) {
      u_int start_cyl;

      start_cyl = part->partinfo[2]->info.blk_start /
                    (disc_record->d.heads * disc_record->d.secspertrack);

      /*
       * This should always be true.  If it's incorrect, then you've
       * been playing, and I'm confused.
       */
      assert(start_cyl == part->partinfo[2]->info.chs_start.cylinder);
      sector.sector[508] = 9;
      sector.sector[509] = start_cyl & 255;
      sector.sector[510] = start_cyl >> 8;
    } else {
      sector.sector[508] = 0;
      sector.sector[509] = 0;
      sector.sector[510] = 0;
    }

    /*
     * We have made our changes to the boot sector.  Recalculate
     * the checksum, and don't forget to update our own copy.
     */
    sector.sector[511] = filecore_checksum(sector.sector);
    part->data.filecore_linux.boot_checksum = sector.sector[511];

#ifdef LINUX_DEBUG
    dbg_printf("Filecore boot sector (0xc00):");
    dbg_memdump(sector.sector, FILECORE_SECTOR_SIZE);
#endif
#ifdef LINUX_SAVE
    if (blkio_write(part->blkio, sector.sector, FILECORE_BOOT_SECTOR, 1) != 1) {
      set_error("unable to write filecore boot sector");
      break;
    }
#endif

    if (part->nr_partitions > 2 && part->partinfo[2]) {
      blk_t parttable_start = part->partinfo[2]->info.blk_start;
      u_int i;
      /*
       * Now, write the partition information to the Linux
       * partition sectors.
       */
      memset(sector.sector, 0, sizeof(sector.sector));
      for (i = 0; i < part->nr_partitions - 3; i++) {
        partinfo_i_t *info = part->partinfo[i + 3];

        if (info && info->info.type != ptyp_none) {
          sector.p[i].offset_start = info->info.blk_start - parttable_start;
          sector.p[i].size         = info->info.blk_end -
                                   info->info.blk_start + 1;
        switch(info->info.type) {
        case ptyp_linux_native:
          sector.p[i].magic = FILECORE_LINUX_NATIVE;
          break;

        case ptyp_linux_swap:
          sector.p[i].magic = FILECORE_LINUX_SWAP;
          break;

        default:
          assert(0);
        }
        }
      }
#ifdef LINUX_DEBUG
      dbg_printf("Linux partition sector (0x%X):", parttable_start);
      dbg_memdump(sector.sector, FILECORE_SECTOR_SIZE);
#endif
#ifdef LINUX_SAVE
      if (blkio_write(part->blkio, sector.sector, parttable_start, 1) != 1) {
        set_error("unable to write linux partition sector");
        break;
      }
      ret = 1;
#endif
    }
  } while (0);

#ifndef LINUX_SAVE
  set_error("filecore-linux partition information cannot be saved with this build");
#endif

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

  return ret;
}

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

  dbg_printf("fcl_allocate()");
  dbg_level_up();
  fcl_internalcheck(part);
  assert(pnew != NULL);

  if (part->nr_partitions < 2) {
    set_error("partition table missing");
    parn = PARN_ERROR;
  } else {
    for (parn = 3; parn < part->nr_partitions; parn++)
      if (part->partinfo[parn] == NULL)
        break;

    if (parn >= FILECORE_MAX_PARTITIONS) {
      set_error("partition table is full");
      parn = PARN_ERROR;
    } else {
      pnew->kern_part_no = parn;
      pnew->type = ptyp_linux_native;
    }
  }

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

  return parn;
}

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

  dbg_printf("fcl_validate_change(parn=%d)", parn);
  dbg_level_up();
  fcl_internalcheck(part);
  assert(parn < part->nr_partitions);

  do {
    if (pnew) {
      if (pnew->type != ptyp_linux_native &&
          pnew->type != ptyp_linux_swap) {
        set_error("invalid partition type");
        break;
      }

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

    if (parn >= 3 && parn < part->nr_partitions)
      ret = 1;
  } while (0);

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

  return ret;
}

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

  dbg_printf("fcl_validate_creation(parn=%d)", parn);
  dbg_level_up();
  fcl_internalcheck(part);
  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_linux_native &&
          pnew->type != ptyp_linux_swap) {
        set_error("invalid partition type");
        break;
      }

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

    if (part->partinfo[2] &&
        part->partinfo[2]->info.type == ptyp_reserved)
      part->partinfo[2]->info.type = ptyp_linux_table;

    ret = 1;
  } while (0);

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

  return ret;
}

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

  dbg_printf("fcl_validate_deletion(parn=%d)", parn);
  dbg_level_up();
  fcl_internalcheck(part);
  assert(pold != NULL);
  assert(parn < part->nr_partitions);

  switch (pold->kern_part_no) {
  case 0:
  case 1: /* filecore partition */
    set_error("unable to delete filecore partition");
    break;

  case 2: /* Linux table partition */
    {
      int i;

      for (i = 3; i < part->nr_partitions; i++)
        if (part->partinfo[i])
          break;
      if (i >= part->nr_partitions)
        ret = 1;
      else
        set_error("delete all partitions contained within first");
    }
    break;

  default:
    ret = 1;
    break;
  }

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

  return ret;
}

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

  dbg_printf("fcl_validate_partno(parn=%d)", parn);
  dbg_level_up();
  fcl_internalcheck(part);

  if (parn < FILECORE_MAX_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 fcl_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
fcl_nexttype(part_t *part, u_int parn, ptyp_t current, int dir)
{
  ptyp_t ptype = current;

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

  switch(parn) {
  case 0:
  case 1:
    ptype = ptyp_filecore;
    break;

  case 2:
    ptype = ptyp_linux_table;
    break;

  default:
    switch(current) {
    case ptyp_linux_native:
      if (dir > 0)
        ptype = ptyp_linux_swap;
      break;

    case ptyp_linux_swap:
      if (dir < 0)
        ptype = ptyp_linux_native;
      break;

    default:
      ptype = ptyp_linux_native;
      break;
    }
  }

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

  return ptype;
}

scheme_t filecore_linux_scheme = {
  "Filecore/Linux",
  fcl_detect,
  fcl_readinfo,
  fcl_writeinfo,
  fcl_allocate,
  fcl_validate_change,
  fcl_validate_creation,
  fcl_validate_deletion,
  fcl_validate_partno,
  fcl_nexttype
};

Generated by  Doxygen 1.6.0   Back to index