Goto sanos source index

//
// fdisk.c
//
// Disk Partition Editor
//
// Copyright (C) 2002 Michael Ringgaard. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// 
// 1. Redistributions of source code must retain the above copyright 
//    notice, this list of conditions and the following disclaimer.  
// 2. Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in the
//    documentation and/or other materials provided with the distribution.  
// 3. Neither the name of the project nor the names of its contributors
//    may be used to endorse or promote products derived from this software
//    without specific prior written permission. 
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
// SUCH DAMAGE.
// 

#include <os.h>
#include <sys/types.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <inifile.h>

#include <os/mbr.h>
#include <os/dev.h>

#define ALIGN(n, size) ((((n) + (size) - 1) / (size)) * (size))

char *devname;
int hdev;
struct master_boot_record mbr;
struct geometry geom;

unsigned char bootrecord[512] = {
  0x33, 0xC0, 0x8E, 0xD0, 0xBC, 0x00, 0x7C, 0xFB, 0x50, 0x07, 0x50, 0x1F, 0xFC, 0xBE, 0x1B, 0x7C,
  0xBF, 0x1B, 0x06, 0x50, 0x57, 0xB9, 0xE5, 0x01, 0xF3, 0xA4, 0xCB, 0xBD, 0xBE, 0x07, 0xB1, 0x04,
  0x38, 0x6E, 0x00, 0x7C, 0x09, 0x75, 0x13, 0x83, 0xC5, 0x10, 0xE2, 0xF4, 0xCD, 0x18, 0x8B, 0xF5,
  0x83, 0xC6, 0x10, 0x49, 0x74, 0x19, 0x38, 0x2C, 0x74, 0xF6, 0xA0, 0xB5, 0x07, 0xB4, 0x07, 0x8B,
  0xF0, 0xAC, 0x3C, 0x00, 0x74, 0xFC, 0xBB, 0x07, 0x00, 0xB4, 0x0E, 0xCD, 0x10, 0xEB, 0xF2, 0x88,
  0x4E, 0x10, 0xE8, 0x46, 0x00, 0x73, 0x2A, 0xFE, 0x46, 0x10, 0x80, 0x7E, 0x04, 0x0B, 0x74, 0x0B,
  0x80, 0x7E, 0x04, 0x0C, 0x74, 0x05, 0xA0, 0xB6, 0x07, 0x75, 0xD2, 0x80, 0x46, 0x02, 0x06, 0x83,
  0x46, 0x08, 0x06, 0x83, 0x56, 0x0A, 0x00, 0xE8, 0x21, 0x00, 0x73, 0x05, 0xA0, 0xB6, 0x07, 0xEB,
  0xBC, 0x81, 0x3E, 0xFE, 0x7D, 0x55, 0xAA, 0x74, 0x0B, 0x80, 0x7E, 0x10, 0x00, 0x74, 0xC8, 0xA0,
  0xB7, 0x07, 0xEB, 0xA9, 0x8B, 0xFC, 0x1E, 0x57, 0x8B, 0xF5, 0xCB, 0xBF, 0x05, 0x00, 0x8A, 0x56,
  0x00, 0xB4, 0x08, 0xCD, 0x13, 0x72, 0x23, 0x8A, 0xC1, 0x24, 0x3F, 0x98, 0x8A, 0xDE, 0x8A, 0xFC,
  0x43, 0xF7, 0xE3, 0x8B, 0xD1, 0x86, 0xD6, 0xB1, 0x06, 0xD2, 0xEE, 0x42, 0xF7, 0xE2, 0x39, 0x56,
  0x0A, 0x77, 0x23, 0x72, 0x05, 0x39, 0x46, 0x08, 0x73, 0x1C, 0xB8, 0x01, 0x02, 0xBB, 0x00, 0x7C,
  0x8B, 0x4E, 0x02, 0x8B, 0x56, 0x00, 0xCD, 0x13, 0x73, 0x51, 0x4F, 0x74, 0x4E, 0x32, 0xE4, 0x8A,
  0x56, 0x00, 0xCD, 0x13, 0xEB, 0xE4, 0x8A, 0x56, 0x00, 0x60, 0xBB, 0xAA, 0x55, 0xB4, 0x41, 0xCD,
  0x13, 0x72, 0x36, 0x81, 0xFB, 0x55, 0xAA, 0x75, 0x30, 0xF6, 0xC1, 0x01, 0x74, 0x2B, 0x61, 0x60,
  0x6A, 0x00, 0x6A, 0x00, 0xFF, 0x76, 0x0A, 0xFF, 0x76, 0x08, 0x6A, 0x00, 0x68, 0x00, 0x7C, 0x6A,
  0x01, 0x6A, 0x10, 0xB4, 0x42, 0x8B, 0xF4, 0xCD, 0x13, 0x61, 0x61, 0x73, 0x0E, 0x4F, 0x74, 0x0B,
  0x32, 0xE4, 0x8A, 0x56, 0x00, 0xCD, 0x13, 0xEB, 0xD6, 0x61, 0xF9, 0xC3, 0x49, 0x6E, 0x76, 0x61,
  0x6C, 0x69, 0x64, 0x20, 0x70, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x74, 0x61,
  0x62, 0x6C, 0x65, 0x00, 0x45, 0x72, 0x72, 0x6F, 0x72, 0x20, 0x6C, 0x6F, 0x61, 0x64, 0x69, 0x6E,
  0x67, 0x20, 0x6F, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6E, 0x67, 0x20, 0x73, 0x79, 0x73, 0x74,
  0x65, 0x6D, 0x00, 0x4D, 0x69, 0x73, 0x73, 0x69, 0x6E, 0x67, 0x20, 0x6F, 0x70, 0x65, 0x72, 0x61,
  0x74, 0x69, 0x6E, 0x67, 0x20, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6D, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0xAA
};

//
// ask
//

int ask(char *question, char *choices) {
  char ch;
  char *s;

  printf("%s", question);
  while (1) {
    if (read(fdin, &ch, 1) == 1) {
      s = choices;
      while (*s) {
        if (*s == ch)  {
          printf("%c\n", ch);
          return ch;
        }
        s++;
      }
    }
  }
}

//
// lba_to_chs
//

void lba_to_chs(int blkno, int *cyl, int *head, int *sect) {
  *cyl  = blkno / (geom.heads * geom.spt);
  *head = (blkno / geom.spt) % geom.heads;
  *sect = blkno % geom.spt + 1;
}

//
// read_mbr
//

int read_mbr() {
  int rc;

  lseek(hdev, 0, SEEK_SET);
  rc = read(hdev, &mbr, sizeof(mbr));
  if (rc < 0) return rc;

  if (mbr.signature != MBR_SIGNATURE) {
    errno = EINVAL;
    return -1;
  }

  return 0;
}

//
// add_partition
//

void add_partition() {
  int off, noff, poff;
  int size, psize;
  int i, partno, type;
  int cyl, head, sect;
  int ch;
  char str[128];

  // Get partition number
  ch = ask("partition number (0-3)? ", "0123\n");
  if (ch == '\n') return;
  partno = ch - '0';
  if (mbr.parttab[partno].systid != 0) {
    printf("error: partition already used\n");
    return;
  }

  // Get partition size
  printf("partition size (KB) (*=max size)? ");
  gets(str);
  size = *str == '*' ? -1 : atoi(str) * 1024 / geom.sectorsize;

  // Get partition type
  printf("partition type (default %x)? ", SANOS_BOOT_PARTITION_ID);
  gets(str);
  type = *str ? strtol(str, NULL, 16) : SANOS_BOOT_PARTITION_ID;

  // Adjust size to be a multiple of the sectors per track
  if (size > 0) {
    size = ALIGN(size, geom.spt);
    if (size < geom.spt) {
      printf("partition size must be at least %d sectors\n", geom.spt);
      return;
    }
  }

  // Search for the next partition in the table
  for (i = partno + 1; i < 4; i++) {
    if (mbr.parttab[i].systid != 0) break;
  }

  if (i >= 4) {
    noff = -1;
  } else {
    noff = mbr.parttab[i].relsect;
  }

  // Search for the previous partition in the table
  for (i = partno - 1; i >= 0; i--) {
    if (mbr.parttab[i].systid != 0) break;
  }

  if (i < 0) {
    poff = -1;
  } else {
    poff = mbr.parttab[i].relsect;
    psize = mbr.parttab[i].numsect;
  }

  // Compute offset of new partition
  if (poff < 0) {
    off = geom.spt;
  } else {
    off = ALIGN(poff + psize, geom.spt);
  }

  // Check whether specified partition will fit, or calculate maximum partition size
  if (size < 0) {
    size = (noff < 0 ? geom.sectors : noff) - off;
  } else {
    if (off + size > (noff < 0 ? geom.sectors : noff)) {
      printf("partition size too large\n");
      return;
    }
  }
  printf("add partition %d offset %d size %d KB\n", partno, off, size / (1024 / geom.sectorsize));

  mbr.parttab[partno].relsect = off;
  mbr.parttab[partno].numsect = size;
  mbr.parttab[partno].systid = type;

  lba_to_chs(off, &cyl, &head, &sect);
  mbr.parttab[partno].begcyl = cyl & 0xFF;
  mbr.parttab[partno].beghead = head;
  mbr.parttab[partno].begsect = sect | ((cyl >> 8) << 6);

  lba_to_chs(off + size - 1, &cyl, &head, &sect);
  mbr.parttab[partno].endcyl = cyl & 0xFF;
  mbr.parttab[partno].endhead = head;
  mbr.parttab[partno].endsect = sect | ((cyl >> 8) << 6);
}

//
// delete_partition
//

void delete_partition() {
  int ch;
  int partno;

  // Get partition number
  ch = ask("delete partition number (0-3)? ", "0123\n");
  if (ch == '\n') return;
  partno = ch - '0';
  if (mbr.parttab[partno].systid == 0) {
    printf("error: partition not used\n");
    return;
  }

  // Confirm
  ch = ask("\nARE YOU SURE (uppercase 'Y' to confirm)? ", "YNn\n");
  if (ch != 'Y') return;

  // Delete partition
  memset(mbr.parttab + partno, 0, sizeof(struct disk_partition));
  printf("partition %d deleted\n", partno);
}

//
// set_boot_part
//

void set_boot_part() {
  int ch;
  int partno;
  int i;

  // Get partition number
  ch = ask("boot partition number (0-3)? ", "0123\n");
  if (ch == '\n') return;
  partno = ch - '0';

  // Mark partition as active boot partition
  for (i = 0; i < 4; i++) mbr.parttab[i].bootid = 0;
  mbr.parttab[partno].bootid = 0x80;
  printf("partition %d is now boot partition\n", partno);
}

//
// list_partitions
//

void list_partitions() {
  int i;

  printf("device %s: %d KB CHS=%d/%d/%d\n\n", devname, geom.sectors / (1024 / geom.sectorsize), geom.cyls, geom.heads, geom.spt);

  printf("     ------start------ -------end-------\n");
  printf("part   cyl head sector   cyl head sector   offset         size type\n");

  for (i = 0; i < 4; i++) {
    struct disk_partition *p = &mbr.parttab[i];
    
    printf("%c", p->bootid == 0x80 ? '*' : ' ');
    printf("%3d  %4u  %3u     %2u  %4u  %3u     %2u %8u %12u  %02x\n",
           i, 
           p->begcyl + ((p->begsect >> 6) << 8), p->beghead, p->begsect & 0x3F, 
           p->endcyl + ((p->endsect >> 6) << 8), p->endhead, p->endsect & 0x3F, 
           p->relsect, p->numsect / (1024 / geom.sectorsize), p->systid);
  }
}

//
// commit_mbr
//

void commit_mbr() {
  int rc;

  if (ask("save partition table (y/n)? ", "yn") == 'y') {
    lseek(hdev, 0, SEEK_SET);
    rc = write(hdev, &mbr, sizeof(mbr));
    if (rc < 0) {
      printf("%s: error %d writing master boot record\n", devname, errno);
      return;
    }

    rc = ioctl(hdev, IOCTL_REVALIDATE, NULL, 0);
    if (rc < 0) {
      printf("%s: error %d revalidating partitions\n", devname, errno);
      return;
    }

    printf("partition tables saved\n");
  }
}

//
// clear_mbr
//

void clear_mbr() {
  if (ask("create new master boot record (y/n)? ", "yn") == 'y') {
    memcpy(&mbr, bootrecord, sizeof(mbr));
    printf("new master boot record created\n");
  }
}

//
// help
//

void help() {
  printf("  (a)dd     add new partition\n");
  printf("  (b)oot    set boot partition\n");
  printf("  (c)ommit  save partition table in master boot record\n");
  printf("  (d)elete  delete partition\n");
  printf("  (l)ist    list partitions\n");
  printf("  (m)br     reinitialize master boot record and clear partition table\n");
  printf("  (h)elp    this help\n");
  printf("  e(x)it    exit fdisk (discarding uncommitted changes)\n");
}

//
// main
//

int main(int argc, char *argv[]) {
  int rc;
  int cmd;
  int done = 0;

  // Check arguments
  if (argc == 1) {
    devname = "/dev/hd0";
  } else if (argc == 2) {
    devname = argv[1];
  } else {
    printf("usage: fdisk <device>\n");
    return 1;
  }

  // Open device
  hdev = open(devname, O_RDWR | O_BINARY);
  if (hdev < 0) {
    printf("%s: error %d opening device\n", devname, errno);
    return 1;
  }

  // Get disk geometry
  rc = ioctl(hdev, IOCTL_GETGEOMETRY, &geom , sizeof(struct geometry));
  if (rc < 0) {
    printf("%s: error %d determining disk geometry\n", devname, errno);
    close(hdev);
    return 1;
  }

  // Read master boot record
  rc = read_mbr();
  if (rc < 0 && errno != EINVAL) {
    printf("%s: error %d reading master boot record\n", devname, errno);
    close(hdev);
    return 1;
  }

  // Ask to create new master boot record if the existing is invalid
  if (rc < 0 && errno == EINVAL) {
    printf("%s: invalid master boot record\n", devname);
    if (ask("create new master boot record (y/n)? ", "yn") == 'y') {
      memcpy(&mbr, bootrecord, sizeof(mbr));
    }
  }

  // Read commands
  printf("(a)dd (b)oot (c)ommit (d)elete (l)ist (m)br (h)elp e(x)it\n");
  while (!done) {
    cmd = ask("fdisk> ", "abcdlmhx?");

    switch (cmd) {
      case 'a':
        add_partition();
        break;

      case 'b':
        set_boot_part();
        break;

      case 'c':
        commit_mbr();
        break;

      case 'd':
        delete_partition();
        break;

      case 'l':
        list_partitions();
        break;

      case 'm':
        clear_mbr();
        break;

      case 'h':
      case '?':
        help();
        break;

      case 'x':
        done = 1;
        break;
    }
  }

  // Close device
  close(hdev);
  return 0;
}