changeset 989:8ff8b1befcdf

I have implemented FDISK command. This supports '-l' option for listing and other options [-bCHS] for giving values like block size, Cylinders, Heads,...
author Ashwini Sharma <ak.ashwini@gmail.com>
date Mon, 05 Aug 2013 13:16:03 -0500
parents 77cba4e36d48
children 8caeba551a28
files toys/pending/fdisk.c
diffstat 1 files changed, 1554 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/toys/pending/fdisk.c	Mon Aug 05 13:16:03 2013 -0500
@@ -0,0 +1,1554 @@
+/* fdisk.c -  fdisk program to modify partitions on disk.
+ *
+ * Copyright 2012 Ashwini Kumar <ak.ashwini@gmail.com>
+ *
+ * No Standard.
+
+USE_FDISK(NEWTOY(fdisk, "C#<0H#<0S#<0b#<512ul", TOYFLAG_SBIN))
+
+config FDISK
+  bool "fdisk"
+  default n
+  help
+    usage: fdisk [-lu] [-C CYLINDERS] [-H HEADS] [-S SECTORS] [-b SECTSZ] DISK
+
+    Change partition table
+
+    -u            Start and End are in sectors (instead of cylinders)
+    -l            Show partition table for each DISK, then exit
+    -b size       sector size (512, 1024, 2048 or 4096)
+    -C CYLINDERS  Set number of cylinders/heads/sectors
+    -H HEADS
+    -S SECTORS
+*/
+
+#define FOR_fdisk
+#include "toys.h"
+#include <linux/hdreg.h>
+
+GLOBALS(
+  long sect_sz;
+  long sectors;
+  long heads;
+  long cylinders;
+)
+
+#define EXTENDED        0x05                                                                      
+#define WIN98_EXTENDED  0x0f
+#define LINUX_NATIVE    0x83
+#define LINUX_EXTENDED  0x85
+
+#define SECTOR_SIZE 512
+#define ONE_K       1024
+#define PARTITION_MAX  60  //partition max is modifiable
+#define IS_EXTENDED(i) ((i) == EXTENDED || (i) == WIN98_EXTENDED || (i) == LINUX_EXTENDED)
+#define sector(s) ((s) & 0x3f)
+#define cylinder(s, c) ((c) | (((s) & 0xc0) << 2))
+
+typedef off_t sector_t;
+
+struct partition {
+  unsigned char boot_ind, head, sector, cyl, sys_ind, end_head,
+                end_sector, end_cyl, start4[4], size4[4];
+};
+
+struct part_entry {
+  struct partition *part;
+  char *sec_buffer;
+  sector_t  start_offset;
+  int modified;
+};
+
+struct part_types {
+  int id;
+  char type[PATH_MAX];
+} sys_types[] = {
+  {0x00, "Empty"}, {0x01, "FAT12"}, {0x04, "FAT16 <32M"}, {0x05, "Extended"},
+  {0x06, "FAT16"}, {0x07, "HPFS/NTFS"}, {0x0a, "OS/2 Boot Manager"},
+  {0x0b, "Win95 FAT32"}, {0x0c, "Win95 FAT32 (LBA)"}, {0x0e, "Win95 FAT16 (LBA)"},
+  {0x0f, "Win95 Ext'd (LBA)"}, {0x11, "Hidden FAT12"}, {0x12, "Compaq diagnostics"},
+  {0x14, "Hidden FAT16 <32M"}, {0x16, "Hidden FAT16"}, {0x17, "Hidden HPFS/NTFS"},
+  {0x1b, "Hidden Win95 FAT32"}, {0x1c, "Hidden W95 FAT32 (LBA)"}, {0x1e, "Hidden W95 FAT16 (LBA)"},
+  {0x3c, "Part.Magic recovery"}, {0x41, "PPC PReP Boot"}, {0x42, "SFS"},
+  {0x63, "GNU HURD or SysV"}, {0x80, "Old Minix"}, {0x81, "Minix / old Linux"},
+  {0x82, "Linux swap"}, {0x83, "Linux"}, {0x84, "OS/2 hidden C: drive"},
+  {0x85, "Linux extended"}, {0x86, "NTFS volume set"}, {0x87, "NTFS volume set"},
+  {0x8e, "Linux LVM"}, {0x9f, "BSD/OS"}, {0xa0, "Thinkpad hibernation"},
+  {0xa5, "FreeBSD"}, {0xa6, "OpenBSD"}, {0xa8, "Darwin UFS"}, {0xa9, "NetBSD"},
+  {0xab, "Darwin boot"}, {0xb7, "BSDI fs"}, {0xb8, "BSDI swap"},
+  {0xbe, "Solaris boot"}, {0xeb, "BeOS fs"}, {0xee, "EFI GPT"},
+  {0xef, "EFI (FAT-12/16/32)"}, {0xf0, "Linux/PA-RISC boot"},
+  {0xf2, "DOS secondary"}, {0xfd, "Linux raid autodetect"},
+};
+
+static int num_parts, disp_unit_cyl, dos_flag, dev_fd = 3;
+static long g_cylinders, g_heads, g_sectors, g_sect_size;
+static sector_t total_number_sectors, extended_offset;
+static char MBRbuf[2048], *disk_device;
+struct part_entry partitions[PARTITION_MAX];
+
+static struct partition* part_offset(char *secbuf, int i)
+{
+  return (struct partition*)(secbuf + 0x1be + i*(sizeof(struct partition)));
+}
+
+static void set_levalue(unsigned char *cp, sector_t value )
+{
+  uint32_t val = SWAP_LE32(value);
+  memcpy(cp, (void*)&val, 4);
+}
+
+static void set_hsc(struct partition *p, sector_t start, sector_t end)
+{
+  if (dos_flag && (start / (g_sectors * g_heads) > 1023))
+    start = g_heads * g_sectors * ONE_K - 1;
+  p->sector = (start % g_sectors) + 1;
+  start /= g_sectors;
+  p->head = start % g_heads;
+  start /= g_heads;
+  p->cyl = start & 0xFF;
+  p->sector |= (start >> 2) & 0xc0;
+
+  if (dos_flag && (end / (g_sectors * g_heads) > 1023))
+    end = g_heads * g_sectors * ONE_K - 1;
+  p->end_sector = (end % g_sectors) + 1;
+  end /= g_sectors;
+  p->end_head = end % g_heads;
+  end /= g_heads;
+  p->end_cyl = end & 0xFF;
+  p->end_sector |= (end >> 2) & 0xc0;
+}
+
+static int chs_warn(void)
+{
+  if (g_heads && g_sectors && g_cylinders)
+    return 0;
+
+  printf("Unknown value(s) for:");
+  if (!g_heads) printf(" heads");
+  if (!g_sectors) printf(" sectors");
+  if (!g_cylinders) printf(" cylinders");
+  printf(". can set in the expert menu.\n");
+  return 1;
+}
+
+static void list_types(void)
+{
+  int i, adjust = 0, size = ARRAY_LEN(sys_types);
+ 
+  if(size % 2) adjust = 1;
+  for (i = 0; i < (size - adjust); i+=2)
+    xprintf("%2x %-22s\t\t%2x %-22.22s\n", sys_types[i].id, sys_types[i].type,
+        sys_types[i+1].id, sys_types[i+1].type);
+  if (adjust) xprintf("%2x %-22s\n",sys_types[size-1].id, sys_types[size-1].type);
+  xputc('\n');
+}
+
+static int valid(long size)
+{
+  if (size == 512 || size == 1024 || size == 2048 || size == 4096) return 1;
+  else {
+      toys.exithelp = 1;
+      error_exit("");
+  }
+}
+
+static void read_sec_sz()
+{
+  int arg;       
+  if (ioctl(dev_fd, BLKSSZGET, &arg) == 0) g_sect_size = arg;
+  if ((toys.optflags & FLAG_b) && valid(TT.sect_sz)) g_sect_size = TT.sect_sz;
+}
+
+static sector_t read_size()
+{
+  uint64_t sec64 = 0;
+  unsigned long sectors = 0;
+  if (ioctl(dev_fd, BLKGETSIZE64, &sec64) == 0) {
+    sec64 = sec64 >> 9; //convert to 512 block size.
+    if (sec64 != (uint32_t) sec64) {
+      perror_msg("device has more than 2^32 sectors, can't use all of them");
+      sec64 = (uint32_t) - 1L;
+    }
+    return sec64;
+  }
+  if (ioctl(dev_fd, BLKGETSIZE, &sectors) == 0)
+    if (sizeof(long) > sizeof(sector_t) && sectors != (sector_t)sectors)
+      sectors = (uint32_t) - 1L;
+  return sectors;
+}
+
+static int validate_part_buff(char *buffer)
+{
+  if ((buffer[510] != 0x55) || (buffer[511] != 0xAA)) return 0;
+  return 1;
+}
+
+static int is_partition_clear(struct partition* p)
+{
+  int i = 0;
+  unsigned char res = 0;
+  const char *ptr = (const char*)p;
+
+  for (i = 0; i < sizeof(struct partition); i++) res |= (unsigned char)ptr[i];
+  return (res == 0x00);
+}
+
+static uint32_t swap_le32toh(unsigned char *cp)
+{
+  uint32_t val;
+  memcpy((void*)&val, cp, 4);
+  return le32toh(val);
+}
+
+static int check_order(void)    
+{                              
+  sector_t first[num_parts], last_seen_val = 0;
+  int i;
+  struct part_entry *pe;       
+  struct partition *px;
+
+  for (i = 0; i < num_parts; i++) {
+    if (i == 4) last_seen_val = 0;
+    pe = &partitions[i];       
+    px = pe->part;             
+    if (px->sys_ind) {
+      first[i] = swap_le32toh(px->start4) + pe->start_offset;
+      if (last_seen_val > first[i]) return 1;
+      last_seen_val = first[i];
+    }
+  }
+  return 0;
+}
+
+static void read_geometry(struct hd_geometry *disk)
+{
+  struct hd_geometry geometry;
+
+  if (ioctl(dev_fd, HDIO_GETGEO, &geometry)) return;
+  disk->heads = geometry.heads;
+  disk->sectors = geometry.sectors;
+}
+
+/* Read the extended boot record for the 
+ * logical partion details.
+ */
+static void read_ebr(int idx)
+{
+  char *sec_buf = NULL;
+  sector_t offset = 0, local_start_off = 0;
+  struct partition *p, *q;
+
+  q = p = partitions[idx].part;
+  local_start_off = swap_le32toh(p->start4);
+
+  if (!extended_offset) extended_offset = local_start_off;
+  do {
+    sec_buf = xzalloc(g_sect_size);
+    partitions[num_parts].part = part_offset(sec_buf, 0);
+    partitions[num_parts].sec_buffer = sec_buf;
+    offset = swap_le32toh(q->start4);
+
+    if (num_parts > 4) offset += local_start_off;
+    partitions[num_parts].start_offset = offset;
+    xlseek(dev_fd, (off_t)(offset * g_sect_size), SEEK_SET);
+
+    if (g_sect_size != readall(dev_fd, sec_buf, g_sect_size)) {
+      close(dev_fd);
+      error_exit("Couldn't read sector zero\n");
+    }
+    num_parts++; //extended partions present.
+    q = part_offset(sec_buf, 1);
+  } while (!is_partition_clear(q));
+}
+
+static void physical_HS(int* h, int *s)
+{  
+  struct partition *p;
+  int i, end_h, end_s, e_hh = 0, e_ss = 0, ini = 1, dirty = 0;
+  const unsigned char *bufp = (const unsigned char *)MBRbuf;
+
+  if (!(validate_part_buff((char*)bufp))) return;
+
+  for (i = 0; i < 4; i++) {
+    p = part_offset((char*)bufp, i);
+    if (p->sys_ind) {
+      end_h = p->end_head + 1;
+      end_s = (p->end_sector & 077);
+      if (ini) {
+        e_hh = end_h;
+        e_ss = end_s;
+        ini = 0;
+      } else if (e_hh !=end_h || e_ss != end_s)
+        dirty = 1;
+    }
+  }
+  if (!dirty && !ini) {
+    *h = e_hh;
+    *s = e_ss;
+  }
+}
+
+//Reset the primary partition table
+static void reset_boot(int change)
+{
+  int i;
+  for(i = 0; i < 4; i++) {
+    struct part_entry *pe = &partitions[i];
+    pe->part = part_offset(MBRbuf, i);
+    pe->start_offset = 0;
+    pe->sec_buffer = MBRbuf;
+    pe->modified = change;
+  }
+}
+
+static inline void write_table_flag(char *buf)
+{
+  buf[510] = 0x55;
+  buf[511] = 0xaa;
+}
+
+/* free the buffers used for holding details of
+ * extended logical partions
+*/
+static void free_bufs(void)
+{
+  int i = 4;
+  for (; i < num_parts; i++) free(partitions[i].sec_buffer);
+}
+
+static void create_empty_doslabel(void)
+{
+  xprintf("Building a new DOS Disklabel. The changes will\n"
+      "remain in memory only, until you write it.\n");
+
+  num_parts = 4;
+  extended_offset = 0;
+  memset(&MBRbuf[510 - 4*16], 0, 4*16);
+  write_table_flag(MBRbuf);
+  partitions[0].modified = 1;
+  reset_boot(1);
+}
+
+/* Read the Master Boot sector of the device for the 
+ * partition table entries/details.
+ * If any extended partition is found then read the EBR
+ * for logical partition details
+ */
+static int read_mbr(char *device, int validate)
+{
+  int fd, sector_fac, i, h = 0, s = 0;
+  struct hd_geometry disk;
+  fd = open(device, O_RDWR);
+  if(fd < 0) {
+    perror_msg("can't open '%s'",device);
+    return 1;
+  }
+
+  disk_device = strdup(device);
+  if(fd != dev_fd) {
+    if(dup2(fd, dev_fd) != dev_fd) perror_exit("Can't dup2");
+    close(fd);
+  }
+
+  //read partition table - MBR
+  if (SECTOR_SIZE != readall(dev_fd, MBRbuf, SECTOR_SIZE)) {
+    close(dev_fd);
+    perror_exit("Couldn't read sector zero\n");
+  }
+  if (validate && !validate_part_buff(MBRbuf)) {
+    xprintf("Device contains neither a valid DOS "
+        "partition table, nor Sun, SGI, OSF or GPT "
+        "disklabel\n");
+    create_empty_doslabel();
+  }
+
+  disk.heads = disk.sectors = 0;
+  read_geometry(&disk); //CHS values
+  total_number_sectors = read_size(); //Device size
+  read_sec_sz();
+  sector_fac = g_sect_size/SECTOR_SIZE; //512 is hardware sector size.
+  physical_HS(&h, &s); //physical dimensions may be diferent from HDIO_GETGEO
+  g_sectors = (toys.optflags & FLAG_S && TT.sectors)? TT.sectors :  s? s : disk.sectors?disk.sectors : 63;
+  g_heads = (toys.optflags & FLAG_H && TT.heads)? TT.heads : h? h : disk.heads? disk.heads : 255;
+  g_cylinders = total_number_sectors/(g_heads * g_sectors * sector_fac);
+
+  if (!g_cylinders) g_cylinders = toys.optflags & FLAG_C? TT.cylinders : 0;
+  if ((g_cylinders > ONE_K) && !(toys.optflags & (FLAG_l | FLAG_S)))
+    xprintf("\nThe number of cylinders for this disk is set to %lu.\n"
+        "There is nothing wrong with that, but this is larger than 1024,\n"
+        "and could in certain setups cause problems.\n", g_cylinders);
+  for (i = 0; i < num_parts; i++) {
+    if (IS_EXTENDED(partitions[i].part->sys_ind)) {
+      read_ebr(i);
+      break;
+    }
+  }
+  chs_warn();
+
+  return 0;
+}
+
+static char* get_type(int sys_ind)
+{
+  int i, size = ARRAY_LEN(sys_types);
+  for (i = 0; i < size; i++)
+    if (sys_ind == sys_types[i].id)
+      return sys_types[i].type;
+  return "Unknown";
+}
+
+static void consistency_check(const struct partition *p, int partition)
+{        
+  unsigned physbc, physbh, physbs, physec, physeh, physes;
+  unsigned lbc, lbh, lbs, lec, leh, les;
+  sector_t start, end;
+
+  if (!g_heads || !g_sectors || (partition >= 4)) return;
+  // physical beginning c, h, s 
+  physbc = cylinder(p->sector,p->cyl);
+  physbh = p->head;
+  physbs = sector(p->sector);
+  // physical ending c, h, s 
+  physec = cylinder(p->end_sector, p->end_cyl);
+  physeh = p->end_head;
+  physes = sector(p->end_sector);
+  // logical begin and end CHS values 
+  start = swap_le32toh((unsigned char*)(p->start4));
+  end = start + swap_le32toh((unsigned char*)(p->size4)) -1;
+
+  lbc = start/(g_sectors * g_heads);
+  lbh = (start/g_sectors) % g_heads;
+  lbs = (start % g_sectors) + 1;
+
+  lec = end/(g_sectors * g_heads);
+  leh = (end/g_sectors) % g_heads;
+  les = (end % g_sectors) + 1;
+
+  //Logical and Physical diff 
+  if (g_cylinders <= ONE_K && (physbc != lbc || physbh != lbh || physbs != lbs)) {
+    xprintf("Partition %u has different physical/logical beginings (Non-Linux?): \n", partition+1);
+    xprintf("phys = (%u %u %u) ",physbc, physbh, physbs);
+    xprintf("logical = (%u %u %u)\n", lbc, lbh, lbs);
+  }
+  if (g_cylinders <= ONE_K && (physec != lec || physeh != leh || physes != les)) {
+    xprintf("Partition %u has different physical/logical endings: \n", partition+1);
+    xprintf("phys = (%u %u %u) ",physec, physeh, physes);
+    xprintf("logical = (%u %u %u)\n", lec, leh, les);
+  }
+  // Ending on cylinder boundary? 
+  if (physeh != (g_heads - 1) || physes != g_sectors)
+    xprintf("Partition %u does not end on cylinder boundary\n", partition + 1);
+}
+
+// List the partition details
+static void list_partitions(int validate)
+{
+  struct partition *p;
+  uint32_t start_cyl, end_cyl, start_sec, end_sec, blocks, secs;
+  char boot, lastchar = '\0', *dev = disk_device;
+  int i = 0, len = strlen(disk_device), odds = 0;
+
+  if (validate && !validate_part_buff(MBRbuf)) {
+    close(dev_fd);
+    toys.exitval = 1;
+    xprintf("Device %s: doesn't contain a valid partition table\n", disk_device);
+    return;
+  }
+  if (isdigit(dev[len - 1])) lastchar = 'p';
+
+  xprintf("%*s Boot      Start         End      Blocks  Id System\n", len+1, "Device");
+  for (i = 0; i < num_parts; i++) {
+    p = partitions[i].part;
+    if (is_partition_clear(p)) continue;
+
+    boot = p->boot_ind == 0x80?'*':' ';
+    start_sec = swap_le32toh(p->start4) + partitions[i].start_offset;
+    secs = swap_le32toh(p->size4);
+
+    if ((start_sec + secs) == 0) end_sec = 0;
+    else end_sec = start_sec + secs -1;
+    start_cyl = start_sec/(g_heads * g_sectors) + 1;
+    end_cyl = end_sec/(g_heads * g_sectors) + 1;
+    blocks = secs;
+    if (g_sect_size < ONE_K) {
+      blocks /= (ONE_K/g_sect_size);
+      odds = secs %(ONE_K/g_sect_size);
+    } else if (g_sect_size > ONE_K) blocks *= (g_sect_size/ONE_K);
+
+    if (lastchar) xprintf("%s%c%d",dev, lastchar, i+1);
+    else xprintf("%s%d",dev, i+1);
+
+    xprintf("   %c %11u %11u %11u%c %2x %s\n",
+        boot,
+        disp_unit_cyl == 0? start_sec: start_cyl,
+        disp_unit_cyl == 0? end_sec: end_cyl,
+        blocks,odds?'+':' ', p->sys_ind, get_type(p->sys_ind));
+
+    consistency_check(p, i);
+  }
+  if (check_order()) xprintf("\nPartition table entries are not in disk order");
+}
+
+//Print device details
+static void print_mbr(int validate)
+{
+  unsigned long long bytes = ((unsigned long long)total_number_sectors << 9);
+  long mbytes = bytes/1000000;
+
+  if (mbytes < 10000) xprintf("Disk %s: %lu MB, %llu bytes\n", disk_device, mbytes, bytes);
+  else xprintf("Disk %s: %lu.%lu GB, %llu bytes\n", disk_device, mbytes/1000, (mbytes/100)%10, bytes);
+  xprintf("%ld heads, %ld sectors/track, %ld cylinders", g_heads, g_sectors, g_cylinders);
+  if (!disp_unit_cyl) {
+    xprintf(", total %lld sectors\n", total_number_sectors/(g_sect_size/SECTOR_SIZE));
+    xprintf("Units = sectors of 1 * %ld = %ld bytes\n",g_sect_size, g_sect_size);
+  } else xprintf("\nUnits = cylinders of %ld * %ld = %ld bytes\n\n",
+      g_heads * g_sectors, g_sect_size, g_heads * g_sectors * g_sect_size);
+  list_partitions(validate);
+  xputc('\n');
+}
+
+static void init_members(void)
+{
+  int i = 0;
+  num_parts = 4; //max of primaries in a part table
+  disp_unit_cyl = dos_flag = 1;
+  extended_offset = 0;
+  g_sect_size = SECTOR_SIZE;
+  for (i = 0; i < num_parts; i++) {
+    partitions[i].part = part_offset(MBRbuf, i);
+    partitions[i].sec_buffer = MBRbuf;
+    partitions[i].modified = 0;
+    partitions[i].start_offset = 0;
+  }
+}
+
+static int read_input(char *mesg, char *outp)
+{
+  char *p;
+  int size = 0;
+redo:
+  xprintf("%s", mesg);
+  p = fgets(toybuf, 80, stdin);
+  
+  if (!p || !(size = strlen(p))) exit(0);
+  if (p[size-1] == '\n') p[--size] = '\0';
+  if (!size) goto redo;
+
+  while (*p != '\0' && *p <= ' ') p++;
+  if (outp) memcpy(outp, p, strlen(p) + 1); //1 for nul
+  return *p;
+}
+
+static int read_hex(char *mesg)
+{
+  int val;
+  char input[80], *endp;
+  while (1) {
+    read_input(mesg, input);
+    if ((*input | 0x20) == 'l') {
+      list_types();
+      memset(input, 0, 80);
+      continue;
+    }
+    val = strtoul(input, &endp, 16);
+    if (endp && *endp) continue;
+    if (val <= 0xff) return val;
+  }
+}
+
+/* Delete an exiting partition,
+ * if its primary, then just clear the partition details
+ * if extended, then clear the partition details, also for logical
+ * if only logical, then move the later partitions backwards 1 step
+ */
+void delete_partition(int i)
+{
+  int sys_id, looper = 0;
+  struct partition *p, *q, *ext_p, *ext_q;
+  sector_t new_start;
+  struct part_entry *pe = &partitions[i];
+  
+  if (chs_warn()) return;
+  p = pe->part;
+  sys_id = p->sys_ind;
+  if (!sys_id) xprintf("Partition %u is empty\n", i+1);
+
+  if (i < 4 && !IS_EXTENDED(sys_id)) {
+    memset(p, 0, sizeof(struct partition)); //clear_partition
+    pe->modified = 1;
+  } else if (i < 4 && IS_EXTENDED(sys_id)) {
+    memset(p, 0, sizeof(struct partition)); //clear_partition
+    pe->modified = 1;
+    for (looper = 4; looper < num_parts; looper++) {
+      pe = &partitions[looper];
+      p = pe->part; 
+      if (is_partition_clear(p)) break;
+      else {
+        memset(p, 0, sizeof(struct partition)); //clear_partition
+        pe->modified = 1;
+        free(pe->sec_buffer);
+      }
+    }
+    extended_offset = 0;
+    num_parts = 4;
+  } else {
+    //only logical is delete, need to move the rest of them backwards
+    if (i == 4) { //move partiton# 6 to 5.
+      partitions[i].modified = 1;
+      if (num_parts > i+1) {
+        q = partitions[i + 1].part;
+        *p = *q; //copy the part table
+        ext_p = part_offset(partitions[i].sec_buffer, 1);
+        ext_q = part_offset(partitions[i + 1].sec_buffer, 1);
+        *ext_p = *ext_q; //copy the extended info pointer
+        // change the start of the 4th partiton. 
+        new_start = partitions[i + 1].start_offset + swap_le32toh(q->start4) - extended_offset;
+        new_start = SWAP_LE32(new_start);
+        memcpy(p->start4, (void *)&new_start, 4);
+      } else {
+        memset(partitions[i].part, 0, sizeof(struct partition));
+        return; //only logical
+      }
+    } else if (i > 4) {
+      ext_p = part_offset(partitions[i-1].sec_buffer, 1);
+      ext_q = part_offset(partitions[i].sec_buffer, 1);
+      memcpy((void*)ext_p, (void *)ext_q, sizeof(struct partition));
+      partitions[i-1].modified = 1;
+    }
+    if (i == 4) looper = i+2;
+    else if (i > 4) looper = i+1;
+    for (; looper < num_parts; looper++)
+      partitions[looper-1] = partitions[looper];
+    num_parts--;
+  }
+}
+
+static int ask_partition(int num_parts)
+{
+  int val;
+  while (1) {
+    do {
+      xprintf("Partition (%u - %u):", 1, num_parts);
+      fgets(toybuf, 80, stdin);
+    } while (!isdigit(*toybuf));
+    val = atoi(toybuf);
+    if (val > 0 && val <= num_parts) return val;
+    else xprintf("Invalid number entered\n");
+  }
+}
+
+static void toggle_active_flag(int i)
+{
+  struct partition *p = partitions[i].part;
+  if (is_partition_clear(p)) xprintf("Partition %u is empty\n", i+1);
+  
+  if (IS_EXTENDED(p->sys_ind) && !p->boot_ind)
+    xprintf("WARNING: Partition %u is an extended partition\n", i + 1);
+  p->boot_ind = p->boot_ind == 0x80?0 : 0x80;
+  partitions[i].modified = 1;
+}
+
+//Write the partition details from Buffer to Disk.
+void write_table(void)
+{
+  int i =0;
+  struct part_entry *pe;
+  sector_t offset;
+
+  for (i = 0; i < 4; i++)
+    if (partitions[i].modified) partitions[3].modified = 1;
+
+  for (i = 3; i < num_parts; i++) {
+    pe = &partitions[i];
+    write_table_flag(pe->sec_buffer);
+    offset = pe->start_offset;
+    if (pe->modified == 1) {
+      xlseek(dev_fd, offset * g_sect_size, SEEK_SET);
+      xwrite(dev_fd, pe->sec_buffer, g_sect_size);
+    }
+  }
+  xprintf("The partition table has been altered.\n");
+  xprintf("Calling ioctl() to re-read partition table\n");
+  sync();
+  for (i = 4; i < num_parts; i++) free(partitions[i].sec_buffer);
+  if(ioctl(dev_fd, BLKRRPART, NULL) < 0)
+    perror_exit("WARNING: rereading partition table failed, kernel still uses old table");
+
+}
+
+/* try to find a partition for deletion, if only
+ * one, then select the same, else ask from USER
+ */
+static int get_non_free_partition(int max)
+{       
+  int num = -1, i = 0;
+
+  for (i = 0; i < max; i++) {
+    if (!is_partition_clear(partitions[i].part)) {
+      if (num >= 0) goto get_from_user;
+      num = i;
+    }
+  }
+  (num >= 0) ? xprintf("Selected partition %d\n",num+1):
+    xprintf("No partition is defined yet!\n");
+  return num;
+get_from_user:
+  return ask_partition(num_parts)-1;
+}
+
+/* a try at autodetecting an empty partition table entry,
+ * if multiple options then get USER's choce.
+ */
+static int get_free_partition(int max)
+{
+  int num = -1, i = 0;
+
+  for (i = 0; i < max; i++) {
+    if (is_partition_clear(partitions[i].part)) {
+      if (num >= 0) goto get_from_user;
+      num = i;
+    }
+  }
+  (num >= 0) ? xprintf("Selected partition %d\n",num+1):
+    xprintf("All primary partitions have been defined already!\n");
+  return num;
+get_from_user:
+  return ask_partition(4)-1;
+}
+
+//taking user input for partition start/end sectors/cyinders
+static uint32_t ask_value(char *mesg, sector_t left, sector_t right, sector_t defalt)
+{ 
+  char *str = toybuf;
+  uint32_t val;
+  int use_default = 1;
+
+  while (1) {
+    use_default = 1;
+    do {
+      xprintf("%s",mesg);
+      fgets(str, 80, stdin);
+    } while (!isdigit(*str) && (*str != '\n')
+        && (*str != '-') && (*str != '+') && (!isblank(*str)));
+    while (isblank(*str)) str++; //remove leading white spaces
+    if (*str == '+' || *str == '-') {
+      int minus = (*str == '-');
+      int absolute = 0;
+
+      val = atoi(str + 1);
+      while (isdigit(*++str)) use_default = 0;
+
+      switch (*str) {
+        case 'c':
+        case 'C':
+          if (!disp_unit_cyl) val *= g_heads * g_sectors;
+          break;
+        case 'K':
+          absolute = ONE_K;
+          break;
+        case 'k':
+          absolute = 1000;
+          break;
+        case 'm':
+        case 'M':
+          absolute = 1000000;
+          break;
+        case 'g':
+        case 'G':
+          absolute = 1000000000;
+          break;
+        default:
+          break;
+      }
+      if (absolute) {
+        unsigned long long bytes = (unsigned long long) val * absolute;
+        unsigned long unit = (disp_unit_cyl && (g_heads * g_sectors))? g_heads * g_sectors : 1;
+
+        unit = unit * g_sect_size;
+        bytes += unit/2; // rounding
+        bytes /= unit;
+        val = bytes;
+      }
+      if (minus)
+        val = -val;
+      val += left;
+    } else {
+      val = atoi(str);
+      while (isdigit(*str)) {
+        str++;
+        use_default = 0;
+      }
+    }
+    if(use_default) {
+      val = defalt;
+      xprintf("Using default value %lld\n", defalt);
+    }
+    if (val >= left && val <= right) return val;
+    else xprintf("Value out of range\n");
+  }
+}
+
+//validating if the start given falls in a limit or not
+static int validate(int start_index, sector_t* begin,sector_t* end, sector_t start
+    , int asked)
+{
+  int i, valid = 0;
+  for (i = start_index; i < num_parts; i++) {
+    if (start >= begin[i] && start <= end[i]) {
+      if (asked) xprintf("Sector %lld is already allocated\n",start);
+      valid = 0;
+      break;
+    } else valid = 1;
+  }
+  return valid;
+}
+
+//get the start sector/cylinder of a new partition
+static sector_t ask_start_sector(int idx, sector_t* begin, sector_t* end, int ext_idx)
+{
+  sector_t start, limit, temp = 0, start_cyl, limit_cyl, offset = 1;
+  char mesg[256];
+  int i, asked = 0, valid = 0, start_index = 0;
+
+  if (dos_flag) offset = g_sectors;
+  start = offset;
+  if (disp_unit_cyl) limit = (sector_t)g_sectors * g_heads * g_cylinders - 1;
+  else limit = total_number_sectors - 1;
+
+  if (disp_unit_cyl) //make the begin of every partition to cylnder boundary 
+    for (i = 0; i < num_parts; i++)
+      begin[i] = (begin[i]/(g_heads* g_sectors)) * (g_heads* g_sectors);
+
+  if (idx >= 4) {
+    if (!begin[ext_idx] && extended_offset) begin[ext_idx] = extended_offset;
+    start = begin[ext_idx] + offset;
+    limit = end[ext_idx];
+    start_index = 4;
+  }
+  do {
+    if (asked) valid = validate(start_index, begin, end, start, asked);
+    if (valid) break;
+
+find_start_again:
+    for (i = start_index; i < num_parts; i++) 
+      if (start >= begin[i] && start <= end[i])
+        start = end[i] + 1 + ((idx >= 4)? offset : 0);
+
+    if (!validate(start_index, begin, end, start, 0)) goto find_start_again;
+    start_cyl = start/(g_sectors * g_heads) + 1;
+    limit_cyl = limit/(g_sectors * g_heads) + 1;
+
+    if (start > limit) break;
+    sprintf(mesg, "First %s (%lld - %lld, default %lld): ", disp_unit_cyl? "cylinder" : "sector",
+        (long long int)(disp_unit_cyl? start_cyl : start), 
+        (long long int)(disp_unit_cyl? limit_cyl : limit),
+        (long long int)(disp_unit_cyl? start_cyl : start));
+    temp = ask_value(mesg, disp_unit_cyl? start_cyl : start, 
+        disp_unit_cyl? limit_cyl : limit, disp_unit_cyl? start_cyl : start);
+    asked = 1;
+
+    if (disp_unit_cyl) {
+      // point to the cylinder start sector
+      temp = (temp-1) * g_heads * g_sectors;
+      if (temp < start) //the boundary is falling in the already used sectors.
+        temp = start;
+    }
+    start = temp;
+  } while (asked && !valid);
+  return start;
+}
+
+//get the end sector/cylinder of a new partition
+static sector_t ask_end_sector(int idx, sector_t* begin, sector_t* end, int ext_idx, sector_t start_sec)
+{
+  sector_t limit, temp = 0, start_cyl, limit_cyl, start = start_sec;
+  char mesg[256];
+  int i;
+
+  if (disp_unit_cyl) limit = (sector_t)g_sectors * g_heads * g_cylinders - 1;
+  else limit = total_number_sectors - 1;
+
+  if (disp_unit_cyl) //make the begin of every partition to cylnder boundary
+    for (i = 0; i < num_parts; i++)
+      begin[i] = (begin[i]/(g_heads* g_sectors)) * (g_heads* g_sectors);
+
+  if (idx >= 4) limit = end[ext_idx];
+
+  for (i = 0; i < num_parts; i++)
+    if (start < begin[i] && limit >= begin[i]) limit = begin[i] - 1;
+
+  start_cyl = start/(g_sectors * g_heads) + 1;
+  limit_cyl = limit/(g_sectors * g_heads) + 1;
+  if (limit < start) { //the boundary is falling in the already used sectors.
+    xprintf("No Free sectors available\n");
+    return 0;
+  }
+  sprintf(mesg, "Last %s or +size or +sizeM or +sizeK (%lld - %lld, default %lld): ",
+      disp_unit_cyl? "cylinder" : "sector",
+      (long long int)(disp_unit_cyl? start_cyl : start), 
+      (long long int)(disp_unit_cyl? limit_cyl : limit),
+      (long long int)(disp_unit_cyl? limit_cyl : limit));
+  temp = ask_value(mesg, disp_unit_cyl? start_cyl : start, 
+      disp_unit_cyl? limit_cyl : limit, disp_unit_cyl? limit_cyl : limit);
+
+  if (disp_unit_cyl) { // point to the cylinder start sector
+    temp = temp * g_heads * g_sectors - 1;
+    if (temp > limit) temp = limit;
+  }
+  if (temp < start) { //the boundary is falling in the already used sectors.
+    xprintf("No Free sectors available\n");
+    return 0;
+  }
+  return temp;
+}
+
+// add a new partition to the partition table
+static int add_partition(int idx, int sys_id)
+{
+  int i, ext_idx = -1;
+  sector_t start, end, begin_sec[num_parts], end_sec[num_parts];
+  struct part_entry *pe = &partitions[idx];
+  struct partition *p = pe->part;
+
+  if (p && !is_partition_clear(p)) {
+    xprintf("Partition %u is already defined, delete it to re-add\n", idx+1);
+    return 0;
+  }
+  for (i = 0; i < num_parts; i++) {
+    pe = &partitions[i];
+    p = pe->part;
+    if (is_partition_clear(p)) {
+      begin_sec[i] = 0xffffffff;
+      end_sec[i] = 0;
+    } else {
+      begin_sec[i] = swap_le32toh(p->start4) + pe->start_offset;
+      end_sec[i] = begin_sec[i] + swap_le32toh(p->size4) - 1;
+    }
+    if (IS_EXTENDED(p->sys_ind)) ext_idx = i;
+  }
+  start = ask_start_sector(idx, begin_sec, end_sec, ext_idx);
+  end = ask_end_sector(idx, begin_sec, end_sec, ext_idx, start);
+  if (!end) return 0;
+  //Populate partition table entry  - 16 bytes
+  pe = &partitions[idx];
+  p = pe->part;
+
+  if (idx > 4) {
+    if (dos_flag) pe->start_offset = start - (sector_t)g_sectors;
+    else pe->start_offset = start - 1;
+    if (pe->start_offset == extended_offset) pe->start_offset++;
+    if (!dos_flag) start++;
+  }
+ 
+  set_levalue(p->start4, start - pe->start_offset);
+  set_levalue(p->size4, end - start + 1);
+  set_hsc(p, start, end);
+  p->boot_ind = 0;
+  p->sys_ind = sys_id;
+  pe->modified = 1;
+
+  if (idx > 4) {
+    p = partitions[idx-1].part + 1; //extended pointer for logical partitions
+    set_levalue(p->start4, pe->start_offset - extended_offset);
+    set_levalue(p->size4, end - start + 1 + (dos_flag? g_sectors: 1));
+    set_hsc(p, start, end);
+    p->boot_ind = 0;  
+    p->sys_ind = EXTENDED;
+    partitions[idx-1].modified = 1;   
+  }
+  if (IS_EXTENDED(sys_id)) {
+    pe = &partitions[4];
+    pe->modified = 1; 
+    pe->sec_buffer = xzalloc(g_sect_size);
+    pe->part = part_offset(pe->sec_buffer, 0);
+    pe->start_offset = extended_offset = start;
+    num_parts = 5;
+  }
+  return 1;
+}
+
+static void add_logical_partition(void)
+{
+  struct part_entry *pe;
+  if (num_parts > 5 || !is_partition_clear(partitions[4].part)) {
+    pe = &partitions[num_parts];
+    pe->modified = 1;
+    pe->sec_buffer = xzalloc(g_sect_size);
+    pe->part = part_offset(pe->sec_buffer, 0);
+    pe->start_offset = 0;
+    num_parts++;
+    if (!add_partition(num_parts - 1, LINUX_NATIVE)) {
+      num_parts--;
+      free(pe->sec_buffer);
+    }
+  } 
+  else add_partition(num_parts -1, LINUX_NATIVE);
+}
+
+/* Add a new partiton to the partition table.
+ * MAX partitions limit is taken to be 60, can be changed
+ */
+static void add_new_partition(void)
+{
+  int choice, idx, i, free_part = 0;
+  char *msg = NULL;
+  
+  if (chs_warn()) return;
+  for (i = 0; i < 4; i++) if(is_partition_clear(partitions[i].part)) free_part++;
+
+  if (!free_part && num_parts >= 60) {
+    xprintf("The maximum number of partitions has been created\n");
+    return;       
+  }
+  if (!free_part) {
+    if (extended_offset) add_logical_partition();
+    else xprintf("You must delete some partition and add "
+          "an extended partition first\n");
+    return;
+  }
+
+  msg = xmsprintf("  %s\n  p  primary partition(1-4)\n",
+          extended_offset? "l  logical (5 or over)" : "e  extended");
+
+  choice = 0x20 | read_input(msg, NULL);
+  free(msg);
+  if (choice == 'p') {
+    idx = get_free_partition(4);
+    if (idx >= 0) add_partition(idx, LINUX_NATIVE);
+    return;
+  }
+  if (choice =='l' && extended_offset) {
+    add_logical_partition();
+    return;
+  }
+  if (choice == 'e' && !extended_offset) {
+    idx = get_free_partition(4);   
+    if (idx >= 0) add_partition(idx, EXTENDED);
+    return;
+  }
+}
+
+static void change_systype(void )
+{
+  int i, sys_id;
+  struct partition *p;
+  struct part_entry *pe;
+
+  i = ask_partition(num_parts);
+  pe = &partitions[i-1];
+  p = pe->part;
+  if (is_partition_clear(p)) {
+    xprintf("Partition %d doesn't exist yet!\n", i);
+    return;
+  }
+  sys_id = read_hex("Hex code (L to list codes): ");
+  if ((IS_EXTENDED(p->sys_ind) && !IS_EXTENDED(sys_id)) ||
+      (!IS_EXTENDED(p->sys_ind) && IS_EXTENDED(sys_id))) {
+    xprintf("you can't change a  partition to an extended or vice-versa\n");
+    return;
+  }
+
+  xprintf("Changed system type of partition %u to %0x (%s)\n",i, sys_id, get_type(sys_id));
+  p->sys_ind = sys_id;
+  pe->modified = 1;
+}
+
+static void check(int n, unsigned h, unsigned s, unsigned c, sector_t start)
+{   
+  sector_t total, real_s, real_c;
+
+  real_s = sector(s) - 1;
+  real_c = cylinder(s, c);
+  total = (real_c * g_sectors + real_s) * g_heads + h;
+  if (!total) xprintf("Partition %u contains sector 0\n", n);
+  if (h >= g_heads)
+    xprintf("Partition %u: head %u greater than maximum %lu\n", n, h + 1, g_heads);
+  if (real_s >= g_sectors)
+    xprintf("Partition %u: sector %u greater than maximum %lu\n", n, s, g_sectors);
+  if (real_c >= g_cylinders)
+    xprintf("Partition %u: cylinder %lld greater than maximum %lu\n", n, real_c + 1, g_cylinders);
+  if (g_cylinders <= ONE_K && start != total)
+    xprintf("Partition %u: previous sectors %lld disagrees with total %lld\n", n, start, total);
+}
+
+static void verify_table(void)
+{
+  int i, j, ext_idx = -1;
+  sector_t begin_sec[num_parts], end_sec[num_parts], total = 1;
+  struct part_entry *pe;
+  struct partition *p;
+
+  for (i = 0; i < num_parts; i++) {
+    pe = &partitions[i];
+    p = pe->part;
+    if (is_partition_clear(p) || IS_EXTENDED(p->sys_ind)) {
+      begin_sec[i] = 0xffffffff;
+      end_sec[i] = 0;
+    } else {
+      begin_sec[i] = swap_le32toh(p->start4) + pe->start_offset;
+      end_sec[i] = begin_sec[i] + swap_le32toh(p->size4) - 1;
+    }
+    if (IS_EXTENDED(p->sys_ind)) ext_idx = i;
+  }
+  for (i = 0; i < num_parts; i++) {
+    pe = &partitions[i];
+    p = pe->part;
+    if (p->sys_ind && !IS_EXTENDED(p->sys_ind)) {
+      consistency_check(p, i);
+      if ((swap_le32toh(p->start4) + pe->start_offset) < begin_sec[i])
+        xprintf("Warning: bad start-of-data in partition %u\n", i + 1);
+      check(i + 1, p->end_head, p->end_sector, p->end_cyl, end_sec[i]);
+      total += end_sec[i] + 1 - begin_sec[i];
+      for (j = 0; j < i; j++) {
+        if ((begin_sec[i] >= begin_sec[j] && begin_sec[i] <= end_sec[j])
+            || ((end_sec[i] <= end_sec[j] && end_sec[i] >= begin_sec[j]))) {
+          xprintf("Warning: partition %u overlaps partition %u\n", j + 1, i + 1);
+          total += begin_sec[i] >= begin_sec[j] ? begin_sec[i] : begin_sec[j];
+          total -= end_sec[i] <= end_sec[j] ? end_sec[i] : end_sec[j];
+        }
+      }
+    }
+  }  
+  if (extended_offset) {
+    struct part_entry *pex = &partitions[ext_idx];
+    sector_t e_last = swap_le32toh(pex->part->start4) +
+      swap_le32toh(pex->part->size4) - 1;
+
+    for (i = 4; i < num_parts; i++) {
+      total++;
+      p = partitions[i].part;
+      if (!p->sys_ind) {
+        if (i != 4 || i + 1 < num_parts)
+          xprintf("Warning: partition %u is empty\n", i + 1);
+      } else if (begin_sec[i] < extended_offset || end_sec[i] > e_last)
+        xprintf("Logical partition %u not entirely in partition %u\n", i + 1, ext_idx + 1);
+    }
+  }
+  if (total > g_heads * g_sectors * g_cylinders)
+    xprintf("Total allocated sectors %lld greater than the maximum "
+        "%lu\n", total, g_heads * g_sectors * g_cylinders);
+  else {
+    total = g_heads * g_sectors * g_cylinders - total;
+    if (total) xprintf("%lld unallocated sectors\n", total);
+  }
+}
+
+static void move_begning(int idx)
+{
+  sector_t start, num, new_start, end;
+  char mesg[256];
+  struct part_entry *pe = &partitions[idx];
+  struct partition *p = pe->part;
+
+  if (chs_warn()) return;
+  start = swap_le32toh(p->start4) + pe->start_offset;
+  num = swap_le32toh(p->size4);
+  end = start + num -1;
+
+  if (!num || IS_EXTENDED(p->sys_ind)) {
+    xprintf("Partition %u doesn't have data area\n", idx+1);
+    return;
+  }
+  sprintf(mesg, "New begining of data (0 - %lld, default %lld): ", 
+      (long long int)(end), (long long int)(start));
+  new_start = ask_value(mesg, 0, end, start);
+  if (new_start != start) {
+    set_levalue(p->start4, new_start - pe->start_offset);
+    set_levalue(p->size4, end - new_start +1);
+    if ((read_input("Recalculate C/H/S (Y/n): ", NULL) | 0x20) == 'y')
+      set_hsc(p, new_start, end);
+    pe->modified = 1;
+  }
+}
+
+static void print_raw_sectors()
+{
+  int i, j;
+  struct part_entry *pe;
+
+  xprintf("Device: %s\n", disk_device);
+  for (i = 3; i < num_parts; i++) {
+    pe = &partitions[i];
+    for (j = 0; j < g_sect_size; j++) {
+      if (!(j % 16)) xprintf("\n0x%03X: ",j);
+      xprintf("%02X ",pe->sec_buffer[j]);
+    }
+    xputc('\n');
+  }
+}
+
+static void print_partitions_list(int ext)
+{
+  int i;                                                                                    
+  struct part_entry *pe;
+  struct partition *p;
+
+  xprintf("Disk %s: %lu heads, %lu sectors, %lu cylinders\n\n", disk_device, g_heads, g_sectors, g_cylinders);
+  xprintf("Nr AF  Hd Sec  Cyl  Hd Sec  Cyl      Start       Size ID\n");
+
+  for (i = 0; i < num_parts; i++) {
+    pe = &partitions[i];
+    p = pe->part;
+    if (p) {
+      if (ext && (i >= 4)) p = pe->part + 1;
+      if(ext && i < 4 && !IS_EXTENDED(p->sys_ind)) continue;
+
+      xprintf("%2u %02x%4u%4u%5u%4u%4u%5u%11u%11u %02x\n",
+          i+1, p->boot_ind, p->head,
+          sector(p->sector), cylinder(p->sector, p->cyl),
+          p->end_head,           
+          sector(p->end_sector), cylinder(p->end_sector, p->end_cyl),
+          swap_le32toh(p->start4),
+          swap_le32toh(p->size4),
+          p->sys_ind);
+      if (p->sys_ind) consistency_check(p, i);
+    }
+  }
+}
+
+//fix the partition table order to ascending
+static void fix_order(void)
+{
+  sector_t first[num_parts], min;
+  int i, j, oj, ojj, sj, sjj;
+  struct part_entry *pe;
+  struct partition *px, *py, temp, *pj, *pjj, tmp;
+
+  for (i = 0; i < num_parts; i++) {
+    pe = &partitions[i];
+    px = pe->part;
+    if (is_partition_clear(px)) first[i] = 0xffffffff;
+    else first[i] = swap_le32toh(px->start4) + pe->start_offset;
+  }
+  
+  if (!check_order()) {
+    xprintf("Ordering is already correct\n\n");
+    return;
+  }
+  for (i = 0; i < 4; i++) {
+    for (j = 0; j < 3; j++) {
+      if (first[j] > first[j+1]) {
+        py = partitions[j+1].part;
+        px = partitions[j].part;
+        memcpy(&temp, py, sizeof(struct partition));
+        memcpy(py, px, sizeof(struct partition));
+        memcpy(px, &temp, sizeof(struct partition));
+        min = first[j+1];
+        first[j+1] = first[j];
+        first[j] = min;
+        partitions[j].modified = 1;
+      }
+    }
+  }
+  for (i = 5; i < num_parts; i++) {
+    for (j = 5; j < num_parts - 1; j++) {
+      oj = partitions[j].start_offset;
+      ojj = partitions[j+1].start_offset;
+      if (oj > ojj) {
+        partitions[j].start_offset = ojj;
+        partitions[j+1].start_offset = oj;
+        pj = partitions[j].part;
+        set_levalue(pj->start4, swap_le32toh(pj->start4)+oj-ojj);
+        pjj = partitions[j+1].part;
+        set_levalue(pjj->start4, swap_le32toh(pjj->start4)+ojj-oj);
+        set_levalue((partitions[j-1].part+1)->start4, ojj-extended_offset);
+        set_levalue((partitions[j].part+1)->start4, oj-extended_offset);
+      }
+    }
+  }
+  for (i = 4; i < num_parts; i++) {
+    for (j = 4; j < num_parts - 1; j++) {
+      pj = partitions[j].part;
+      pjj = partitions[j+1].part;
+      sj = swap_le32toh(pj->start4);
+      sjj = swap_le32toh(pjj->start4);
+      oj = partitions[j].start_offset;
+      ojj = partitions[j+1].start_offset;
+      if (oj+sj > ojj+sjj) {
+        tmp = *pj;
+        *pj = *pjj;
+        *pjj = tmp;
+        set_levalue(pj->start4, ojj+sjj-oj);
+        set_levalue(pjj->start4, oj+sj-ojj);
+      }  
+    }    
+  }
+  // If anything changed 
+  for (j = 4; j < num_parts; j++) partitions[j].modified = 1;
+  xprintf("Done!\n");
+}
+
+static void print_menu(void)
+{
+  xprintf("a\ttoggle a bootable flag\n");
+  xprintf("b\tedit bsd disklabel\n");
+  xprintf("c\ttoggle the dos compatibility flag\n");
+  xprintf("d\tdelete a partition\n");
+  xprintf("l\tlist known partition types\n");
+  xprintf("n\tadd a new partition\n");
+  xprintf("o\tcreate a new empty DOS partition table\n");
+  xprintf("p\tprint the partition table\n");
+  xprintf("q\tquit without saving changes\n");
+  xprintf("s\tcreate a new empty Sun disklabel\n");  /* sun */
+  xprintf("t\tchange a partition's system id\n");
+  xprintf("u\tchange display/entry units\n");
+  xprintf("v\tverify the partition table\n");
+  xprintf("w\twrite table to disk and exit\n");
+  xprintf("x\textra functionality (experts only)\n");
+}
+
+static void print_xmenu(void)
+{
+  xprintf("b\tmove beginning of data in a partition\n");
+  xprintf("c\tchange number of cylinders\n");
+  xprintf("d\tprint the raw data in the partition table\n");
+  xprintf("e\tlist extended partitions\n");
+  xprintf("f\tfix partition order\n");    
+  xprintf("h\tchange number of heads\n");
+  xprintf("p\tprint the partition table\n");
+  xprintf("q\tquit without saving changes\n");
+  xprintf("r\treturn to main menu\n");
+  xprintf("s\tchange number of sectors/track\n");
+  xprintf("v\tverify the partition table\n");
+  xprintf("w\twrite table to disk and exit\n");
+}
+
+static void expert_menu(void)
+{
+  int choice, idx;
+  sector_t value;
+  char mesg[256];
+
+  while (1) {
+    xputc('\n');
+    char *msg = "Expert Command ('m' for help): ";
+    choice = 0x20 | read_input(msg, NULL);
+    switch (choice) {
+      case 'b': //move data begining in partition
+        idx = ask_partition(num_parts);
+        move_begning(idx - 1);
+        break;
+      case 'c': //change cylinders
+          sprintf(mesg, "Number of cylinders (1 - 1048576, default %lu): ", g_cylinders);
+          value = ask_value(mesg, 1, 1048576, g_cylinders);
+          g_cylinders = TT.cylinders = value;
+          toys.optflags |= FLAG_C;
+          if(g_cylinders > ONE_K)
+            xprintf("\nThe number of cylinders for this disk is set to %lu.\n"
+                "There is nothing wrong with that, but this is larger than 1024,\n"
+                "and could in certain setups cause problems.\n", g_cylinders);
+        break;
+      case 'd': //print raw data in part tables
+        print_raw_sectors();
+        break;
+      case 'e': //list extended partitions
+        print_partitions_list(1);
+        break;
+      case 'f': //fix part order
+        fix_order();
+        break;
+      case 'h': //change number of heads
+          sprintf(mesg, "Number of heads (1 - 256, default %lu): ", g_heads);
+          value = ask_value(mesg, 1, 256, g_heads);
+          g_heads = TT.heads = value;
+          toys.optflags |= FLAG_H;
+        break;
+      case 'p': //print partition table
+        print_partitions_list(0);
+        break;
+      case 'q':
+        free_bufs();
+        close(dev_fd);
+        xputc('\n');
+        exit(0);
+        break;
+      case 'r':
+        return;
+        break;
+      case 's': //change sector/track
+          sprintf(mesg, "Number of sectors (1 - 63, default %lu): ", g_sectors);
+          value = ask_value(mesg, 1, 63, g_sectors);
+          g_sectors = TT.sectors = value;
+          toys.optflags |= FLAG_H;
+        break;
+      case 'v':
+        verify_table();
+        break;
+      case 'w':
+        write_table();
+        toys.exitval = 0;
+        exit(0);
+        break;
+      case 'm':
+        print_xmenu();
+        break;
+      default:
+        xprintf("Unknown command '%c'\n",choice);
+        print_xmenu();
+        break;
+    }
+  } //while(1)
+}
+
+static int disk_proper(const char *device)
+{
+  unsigned length;
+  int fd = open(device, O_RDONLY);
+
+  if (fd != -1) {
+    struct hd_geometry dev_geo;
+    dev_geo.heads = 0;
+    dev_geo.sectors = 0;
+    int err = ioctl(fd, HDIO_GETGEO, &dev_geo);
+    close(fd);
+    if (!err) return (dev_geo.start == 0);
+  }
+  length = strlen(device);
+  if (length != 0 && isdigit(device[length - 1])) return 0;
+  return 1;
+}
+
+static void reset_entries()
+{
+  int i;
+
+  memset(MBRbuf, 0, sizeof(MBRbuf));
+  for (i = 4; i < num_parts; i++)
+    memset(&partitions[i], 0, sizeof(struct part_entry));
+}
+
+//this will keep dev_fd = 3 always alive
+static void move_fd()
+{
+  int fd = xopen("/dev/null", O_RDONLY);
+  if(fd != dev_fd) {
+    if(dup2(fd, dev_fd) != dev_fd) perror_exit("Can't dup2");
+    close(fd);
+  }
+}
+
+/* Read proc/partitions and then print the details
+ * for partitions on each device
+ */
+static void read_and_print_parts()
+{
+  unsigned int ma, mi, sz;
+  char *name = toybuf, *buffer = toybuf + ONE_K, *device = toybuf + 2048;
+  FILE* fp = xfopen("/proc/partitions", "r");
+
+  while (fgets(buffer, ONE_K, fp)) {
+    reset_entries();
+    num_parts = 4;
+    memset(name, 0, sizeof(name));
+    if (sscanf(buffer, " %u %u %u %[^\n ]", &ma, &mi, &sz, name) != 4)
+      continue;
+      
+    sprintf(device,"/dev/%s",name);
+    if (disk_proper(device)) {
+      if (read_mbr(device, 0)) continue;
+      print_mbr(1);
+      move_fd();
+    }
+  }
+  fclose(fp);
+}
+
+void fdisk_main(void)
+{
+  int choice, p;
+
+  init_members();
+  move_fd();
+  if (TT.heads >= 256) TT.heads = 0;
+  if (TT.sectors >= 64) TT.sectors = 0;
+  if (toys.optflags & FLAG_u) disp_unit_cyl = 0;
+  if (toys.optflags & FLAG_l) {
+    if (!toys.optc) read_and_print_parts();
+    else {
+      while(*toys.optargs){
+        if (read_mbr(*toys.optargs, 0)) {
+          toys.optargs++;
+          continue;
+        }
+        print_mbr(1);
+        move_fd();
+        toys.optargs++;
+      }
+    }
+    toys.exitval = 0;
+    return;
+  } else {
+    if (!toys.optc || toys.optc > 1 ) {
+      toys.exitval = toys.exithelp = 1;
+      show_help();
+      return;
+    }
+    if (read_mbr(toys.optargs[0], 1)) return;
+    while (1) {
+      xputc('\n');
+      char *msg = "Command ('m' for help): ";
+      choice = 0x20 | read_input(msg, NULL);
+      switch (choice) {
+        case 'a':
+          p = ask_partition(num_parts);
+          toggle_active_flag(p - 1); //partition table index start from 0.
+          break;
+        case 'b':
+          break;
+        case 'c':
+          dos_flag = !dos_flag;
+          xprintf("Dos compatible flag is %s\n", dos_flag?"Set" : "Not set");
+          break;
+        case 'd':
+          p = get_non_free_partition(num_parts); //4 was here
+          if(p >= 0) delete_partition(p);
+          break;
+        case 'l':
+          list_types();
+          break;
+        case 'n': //add new partition
+          add_new_partition();
+          break;
+        case 'o':
+          create_empty_doslabel();
+          break;
+        case 'p':
+          print_mbr(0);
+          break;
+        case 'q':
+          free_bufs();
+          close(dev_fd);
+          xputc('\n');
+          exit(0);
+          break;
+        case 's':
+          break;
+        case 't':
+          change_systype();
+          break;
+        case 'u':
+          disp_unit_cyl = !disp_unit_cyl;
+          xprintf("Changing Display/Entry units to %s\n",disp_unit_cyl?"cylinders" : "sectors");
+          break;
+        case 'v':
+          verify_table();
+          break;
+        case 'w':
+          write_table();
+          toys.exitval = 0;
+          return;
+          break;
+        case 'x':
+          expert_menu();
+          break;
+        case 'm':
+          print_menu();
+          break;
+        default:
+          xprintf("%c: Unknown command\n",choice);
+          break;
+      }
+    } //while(1)
+  }
+}