diff options
| -rw-r--r-- | toys/pending/fdisk.c | 1554 | 
1 files changed, 1554 insertions, 0 deletions
| diff --git a/toys/pending/fdisk.c b/toys/pending/fdisk.c new file mode 100644 index 00000000..d34bbda9 --- /dev/null +++ b/toys/pending/fdisk.c @@ -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, §ors) == 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) +  } +} | 
