1/* fdisk.c -  fdisk program to modify partitions on disk.
2 *
3 * Copyright 2012 Ashwini Kumar <ak.ashwini@gmail.com>
4 * Copyright 2013 Kyungwan Han <asura321@gmail.com>
5 *
6 * No Standard.
7
8USE_FDISK(NEWTOY(fdisk, "C#<0H#<0S#<0b#<512ul", TOYFLAG_SBIN))
9
10config FDISK
11  bool "fdisk"
12  default n
13  help
14    usage: fdisk [-lu] [-C CYLINDERS] [-H HEADS] [-S SECTORS] [-b SECTSZ] DISK
15
16    Change partition table
17
18    -u            Start and End are in sectors (instead of cylinders)
19    -l            Show partition table for each DISK, then exit
20    -b size       sector size (512, 1024, 2048 or 4096)
21    -C CYLINDERS  Set number of cylinders/heads/sectors
22    -H HEADS
23    -S SECTORS
24*/
25
26#define FOR_fdisk
27#include "toys.h"
28#include <linux/hdreg.h>
29
30GLOBALS(
31  long sect_sz;
32  long sectors;
33  long heads;
34  long cylinders;
35)
36
37#define EXTENDED        0x05
38#define WIN98_EXTENDED  0x0f
39#define LINUX_NATIVE    0x83
40#define LINUX_EXTENDED  0x85
41
42#define SECTOR_SIZE 512
43#define ONE_K       1024
44#define PARTITION_MAX  60  //partition max is modifiable
45#define IS_EXTENDED(i) ((i) == EXTENDED || (i) == WIN98_EXTENDED || (i) == LINUX_EXTENDED)
46#define sector(s) ((s) & 0x3f)
47#define cylinder(s, c) ((c) | (((s) & 0xc0) << 2))
48
49typedef off_t sector_t;
50
51struct partition {
52  unsigned char boot_ind, head, sector, cyl, sys_ind, end_head,
53                end_sector, end_cyl, start4[4], size4[4];
54};
55
56struct part_entry {
57  struct partition *part;
58  char *sec_buffer;
59  sector_t  start_offset;
60  int modified;
61};
62
63struct part_types {
64  int id;
65  char type[24];
66} sys_types[] = {
67  {0x00, "Empty"}, {0x01, "FAT12"}, {0x04, "FAT16 <32M"}, {0x05, "Extended"},
68  {0x06, "FAT16"}, {0x07, "HPFS/NTFS"}, {0x0a, "OS/2 Boot Manager"},
69  {0x0b, "Win95 FAT32"}, {0x0c, "Win95 FAT32 (LBA)"}, {0x0e, "Win95 FAT16 (LBA)"},
70  {0x0f, "Win95 Ext'd (LBA)"}, {0x11, "Hidden FAT12"}, {0x12, "Compaq diagnostics"},
71  {0x14, "Hidden FAT16 <32M"}, {0x16, "Hidden FAT16"}, {0x17, "Hidden HPFS/NTFS"},
72  {0x1b, "Hidden Win95 FAT32"}, {0x1c, "Hidden W95 FAT32 (LBA)"}, {0x1e, "Hidden W95 FAT16 (LBA)"},
73  {0x3c, "Part.Magic recovery"}, {0x41, "PPC PReP Boot"}, {0x42, "SFS"},
74  {0x63, "GNU HURD or SysV"}, {0x80, "Old Minix"}, {0x81, "Minix / old Linux"},
75  {0x82, "Linux swap"}, {0x83, "Linux"}, {0x84, "OS/2 hidden C: drive"},
76  {0x85, "Linux extended"}, {0x86, "NTFS volume set"}, {0x87, "NTFS volume set"},
77  {0x8e, "Linux LVM"}, {0x9f, "BSD/OS"}, {0xa0, "Thinkpad hibernation"},
78  {0xa5, "FreeBSD"}, {0xa6, "OpenBSD"}, {0xa8, "Darwin UFS"}, {0xa9, "NetBSD"},
79  {0xab, "Darwin boot"}, {0xb7, "BSDI fs"}, {0xb8, "BSDI swap"},
80  {0xbe, "Solaris boot"}, {0xeb, "BeOS fs"}, {0xee, "EFI GPT"},
81  {0xef, "EFI (FAT-12/16/32)"}, {0xf0, "Linux/PA-RISC boot"},
82  {0xf2, "DOS secondary"}, {0xfd, "Linux raid autodetect"},
83};
84
85static int num_parts, disp_unit_cyl, dos_flag, dev_fd = 3;
86static long g_cylinders, g_heads, g_sectors, g_sect_size;
87static sector_t total_number_sectors, extended_offset;
88static char MBRbuf[2048], *disk_device;
89struct part_entry partitions[PARTITION_MAX];
90
91static struct partition* part_offset(char *secbuf, int i)
92{
93  return (struct partition*)(secbuf + 0x1be + i*(sizeof(struct partition)));
94}
95
96static void set_levalue(unsigned char *cp, sector_t value )
97{
98  uint32_t val = SWAP_LE32(value);
99  memcpy(cp, (void*)&val, 4);
100}
101
102static void set_hsc(struct partition *p, sector_t start, sector_t end)
103{
104  if (dos_flag && (start / (g_sectors * g_heads) > 1023))
105    start = g_heads * g_sectors * ONE_K - 1;
106  p->sector = (start % g_sectors) + 1;
107  start /= g_sectors;
108  p->head = start % g_heads;
109  start /= g_heads;
110  p->cyl = start & 0xFF;
111  p->sector |= (start >> 2) & 0xc0;
112
113  if (dos_flag && (end / (g_sectors * g_heads) > 1023))
114    end = g_heads * g_sectors * ONE_K - 1;
115  p->end_sector = (end % g_sectors) + 1;
116  end /= g_sectors;
117  p->end_head = end % g_heads;
118  end /= g_heads;
119  p->end_cyl = end & 0xFF;
120  p->end_sector |= (end >> 2) & 0xc0;
121}
122
123static int chs_warn(void)
124{
125  if (g_heads && g_sectors && g_cylinders)
126    return 0;
127
128  printf("Unknown value(s) for:");
129  if (!g_heads) printf(" heads");
130  if (!g_sectors) printf(" sectors");
131  if (!g_cylinders) printf(" cylinders");
132  printf(". can set in the expert menu.\n");
133  return 1;
134}
135
136static void list_types(void)
137{
138  int i, adjust = 0, size = ARRAY_LEN(sys_types);
139
140  if(size % 2) adjust = 1;
141  for (i = 0; i < (size - adjust); i+=2)
142    xprintf("%2x %-22s\t\t%2x %-22.22s\n", sys_types[i].id, sys_types[i].type,
143        sys_types[i+1].id, sys_types[i+1].type);
144  if (adjust) xprintf("%2x %-22s\n",sys_types[size-1].id, sys_types[size-1].type);
145  xputc('\n');
146}
147
148static void read_sec_sz()
149{
150  int arg;
151  if (ioctl(dev_fd, BLKSSZGET, &arg) == 0) g_sect_size = arg;
152  if (toys.optflags & FLAG_b) {
153    if (TT.sect_sz !=  512 && TT.sect_sz != 1024 && TT.sect_sz != 2048 &&
154        TT.sect_sz != 4096) {
155      toys.exithelp++;
156      error_exit("bad sector size");
157    }
158    g_sect_size = TT.sect_sz;
159  }
160}
161
162static sector_t read_size()
163{
164  uint64_t sec64 = 0;
165  unsigned long sectors = 0;
166  if (ioctl(dev_fd, BLKGETSIZE64, &sec64) == 0) {
167    sec64 = sec64 >> 9; //convert to 512 block size.
168    if (sec64 != (uint32_t) sec64) {
169      perror_msg("device has more than 2^32 sectors, can't use all of them");
170      sec64 = (uint32_t) - 1L;
171    }
172    return sec64;
173  }
174  if (ioctl(dev_fd, BLKGETSIZE, &sectors) == 0)
175    if (sizeof(long) > sizeof(sector_t) && sectors != (sector_t)sectors)
176      sectors = (uint32_t) - 1L;
177  return sectors;
178}
179
180static int validate_part_buff(char *buffer)
181{
182  if ((buffer[510] != 0x55) || (buffer[511] != 0xAA)) return 0;
183  return 1;
184}
185
186static int is_partition_clear(struct partition* p)
187{
188  int i = 0;
189  unsigned char res = 0;
190  const char *ptr = (const char*)p;
191
192  for (i = 0; i < sizeof(struct partition); i++) res |= (unsigned char)ptr[i];
193  return (res == 0x00);
194}
195
196static uint32_t swap_le32toh(unsigned char *cp)
197{
198  uint32_t val;
199  memcpy((void*)&val, cp, 4);
200  return le32toh(val);
201}
202
203static int check_order(void)
204{
205  sector_t first[num_parts], last_seen_val = 0;
206  int i;
207  struct part_entry *pe;
208  struct partition *px;
209
210  for (i = 0; i < num_parts; i++) {
211    if (i == 4) last_seen_val = 0;
212    pe = &partitions[i];
213    px = pe->part;
214    if (px->sys_ind) {
215      first[i] = swap_le32toh(px->start4) + pe->start_offset;
216      if (last_seen_val > first[i]) return 1;
217      last_seen_val = first[i];
218    }
219  }
220  return 0;
221}
222
223static void read_geometry(struct hd_geometry *disk)
224{
225  struct hd_geometry geometry;
226
227  if (ioctl(dev_fd, HDIO_GETGEO, &geometry)) return;
228  disk->heads = geometry.heads;
229  disk->sectors = geometry.sectors;
230}
231
232/* Read the extended boot record for the
233 * logical partion details.
234 */
235static void read_ebr(int idx)
236{
237  char *sec_buf = NULL;
238  sector_t offset = 0, local_start_off = 0;
239  struct partition *p, *q;
240
241  q = p = partitions[idx].part;
242  local_start_off = swap_le32toh(p->start4);
243
244  if (!extended_offset) extended_offset = local_start_off;
245  do {
246    if (num_parts >= 60) {
247      xprintf("Warning: deleting partitions after 60\n");
248      memset(q, 0, sizeof(struct partition)); //clear_partition
249      partitions[num_parts-1].modified = 1;
250      break;
251    }
252
253    sec_buf = xzalloc(g_sect_size);
254    partitions[num_parts].part = part_offset(sec_buf, 0);
255    partitions[num_parts].sec_buffer = sec_buf;
256    offset = swap_le32toh(q->start4);
257
258    if (num_parts > 4) offset += local_start_off;
259    partitions[num_parts].start_offset = offset;
260    xlseek(dev_fd, (off_t)(offset * g_sect_size), SEEK_SET);
261
262    if (g_sect_size != readall(dev_fd, sec_buf, g_sect_size)) {
263      close(dev_fd);
264      error_exit("Couldn't read sector zero\n");
265    }
266    num_parts++; //extended partions present.
267    q = part_offset(sec_buf, 1);
268  } while (!is_partition_clear(q) && IS_EXTENDED(q->sys_ind));
269}
270
271static void physical_HS(int* h, int *s)
272{
273  struct partition *p;
274  int i, end_h, end_s, e_hh = 0, e_ss = 0, ini = 1, dirty = 0;
275  const unsigned char *bufp = (const unsigned char *)MBRbuf;
276
277  if (!(validate_part_buff((char*)bufp))) return;
278
279  for (i = 0; i < 4; i++) {
280    p = part_offset((char*)bufp, i);
281    if (p->sys_ind) {
282      end_h = p->end_head + 1;
283      end_s = (p->end_sector & 077);
284      if (ini) {
285        e_hh = end_h;
286        e_ss = end_s;
287        ini = 0;
288      } else if (e_hh !=end_h || e_ss != end_s)
289        dirty = 1;
290    }
291  }
292  if (!dirty && !ini) {
293    *h = e_hh;
294    *s = e_ss;
295  }
296}
297
298//Reset the primary partition table
299static void reset_boot(int change)
300{
301  int i;
302  for(i = 0; i < 4; i++) {
303    struct part_entry *pe = &partitions[i];
304    pe->part = part_offset(MBRbuf, i);
305    pe->start_offset = 0;
306    pe->sec_buffer = MBRbuf;
307    pe->modified = change;
308  }
309}
310
311static inline void write_table_flag(char *buf)
312{
313  buf[510] = 0x55;
314  buf[511] = 0xaa;
315}
316
317/* free the buffers used for holding details of
318 * extended logical partions
319*/
320static void free_bufs(void)
321{
322  int i = 4;
323  for (; i < num_parts; i++) free(partitions[i].sec_buffer);
324}
325
326static void create_empty_doslabel(void)
327{
328  xprintf("Building a new DOS Disklabel. The changes will\n"
329      "remain in memory only, until you write it.\n");
330
331  num_parts = 4;
332  extended_offset = 0;
333  memset(&MBRbuf[510 - 4*16], 0, 4*16);
334  write_table_flag(MBRbuf);
335  partitions[0].modified = 1;
336  reset_boot(1);
337}
338
339/* Read the Master Boot sector of the device for the
340 * partition table entries/details.
341 * If any extended partition is found then read the EBR
342 * for logical partition details
343 */
344static int read_mbr(char *device, int validate)
345{
346  int fd, sector_fac, i, h = 0, s = 0;
347  struct hd_geometry disk;
348  fd = open(device, O_RDWR);
349  if(fd < 0) {
350    perror_msg("can't open '%s'",device);
351    return 1;
352  }
353
354  disk_device = strdup(device);
355  if(fd != dev_fd) {
356    if(dup2(fd, dev_fd) != dev_fd) perror_exit("Can't dup2");
357    close(fd);
358  }
359
360  //read partition table - MBR
361  if (SECTOR_SIZE != readall(dev_fd, MBRbuf, SECTOR_SIZE)) {
362    close(dev_fd);
363    perror_exit("Couldn't read sector zero\n");
364  }
365  if (validate && !validate_part_buff(MBRbuf)) {
366    xprintf("Device contains neither a valid DOS "
367        "partition table, nor Sun, SGI, OSF or GPT "
368        "disklabel\n");
369    create_empty_doslabel();
370  }
371
372  disk.heads = disk.sectors = 0;
373  read_geometry(&disk); //CHS values
374  total_number_sectors = read_size(); //Device size
375  read_sec_sz();
376  sector_fac = g_sect_size/SECTOR_SIZE; //512 is hardware sector size.
377  physical_HS(&h, &s); //physical dimensions may be diferent from HDIO_GETGEO
378  g_sectors = (toys.optflags & FLAG_S && TT.sectors)? TT.sectors :  s? s : disk.sectors?disk.sectors : 63;
379  g_heads = (toys.optflags & FLAG_H && TT.heads)? TT.heads : h? h : disk.heads? disk.heads : 255;
380  g_cylinders = total_number_sectors/(g_heads * g_sectors * sector_fac);
381
382  if (!g_cylinders) g_cylinders = toys.optflags & FLAG_C? TT.cylinders : 0;
383  if ((g_cylinders > ONE_K) && !(toys.optflags & (FLAG_l | FLAG_S)))
384    xprintf("\nThe number of cylinders for this disk is set to %lu.\n"
385        "There is nothing wrong with that, but this is larger than 1024,\n"
386        "and could in certain setups cause problems.\n", g_cylinders);
387  for (i = 0; i < num_parts; i++) {
388    if (IS_EXTENDED(partitions[i].part->sys_ind)) {
389      read_ebr(i);
390      break;
391    }
392  }
393  chs_warn();
394
395  return 0;
396}
397
398static char* get_type(int sys_ind)
399{
400  int i, size = ARRAY_LEN(sys_types);
401  for (i = 0; i < size; i++)
402    if (sys_ind == sys_types[i].id)
403      return sys_types[i].type;
404  return "Unknown";
405}
406
407static void consistency_check(const struct partition *p, int partition)
408{
409  unsigned physbc, physbh, physbs, physec, physeh, physes;
410  unsigned lbc, lbh, lbs, lec, leh, les;
411  sector_t start, end;
412
413  if (!g_heads || !g_sectors || (partition >= 4)) return;
414  // physical beginning c, h, s
415  physbc = cylinder(p->sector,p->cyl);
416  physbh = p->head;
417  physbs = sector(p->sector);
418  // physical ending c, h, s
419  physec = cylinder(p->end_sector, p->end_cyl);
420  physeh = p->end_head;
421  physes = sector(p->end_sector);
422  // logical begin and end CHS values
423  start = swap_le32toh((unsigned char*)(p->start4));
424  end = start + swap_le32toh((unsigned char*)(p->size4)) -1;
425
426  lbc = start/(g_sectors * g_heads);
427  lbh = (start/g_sectors) % g_heads;
428  lbs = (start % g_sectors) + 1;
429
430  lec = end/(g_sectors * g_heads);
431  leh = (end/g_sectors) % g_heads;
432  les = (end % g_sectors) + 1;
433
434  //Logical and Physical diff
435  if (g_cylinders <= ONE_K && (physbc != lbc || physbh != lbh || physbs != lbs)) {
436    xprintf("Partition %u has different physical/logical beginings (Non-Linux?): \n", partition+1);
437    xprintf("phys = (%u %u %u) ",physbc, physbh, physbs);
438    xprintf("logical = (%u %u %u)\n", lbc, lbh, lbs);
439  }
440  if (g_cylinders <= ONE_K && (physec != lec || physeh != leh || physes != les)) {
441    xprintf("Partition %u has different physical/logical endings: \n", partition+1);
442    xprintf("phys = (%u %u %u) ",physec, physeh, physes);
443    xprintf("logical = (%u %u %u)\n", lec, leh, les);
444  }
445  // Ending on cylinder boundary?
446  if (physeh != (g_heads - 1) || physes != g_sectors)
447    xprintf("Partition %u does not end on cylinder boundary\n", partition + 1);
448}
449
450// List the partition details
451static void list_partitions(int validate)
452{
453  struct partition *p;
454  uint32_t start_cyl, end_cyl, start_sec, end_sec, blocks, secs;
455  char boot, lastchar = '\0', *dev = disk_device;
456  int i = 0, len = strlen(disk_device), odds = 0;
457
458  if (validate && !validate_part_buff(MBRbuf)) {
459    close(dev_fd);
460    toys.exitval = 1;
461    xprintf("Device %s: doesn't contain a valid partition table\n", disk_device);
462    return;
463  }
464  if (isdigit(dev[len - 1])) lastchar = 'p';
465
466  xprintf("%*s Boot      Start         End      Blocks  Id System\n", len+1, "Device");
467  for (i = 0; i < num_parts; i++) {
468    p = partitions[i].part;
469    if (is_partition_clear(p)) continue;
470
471    boot = ((p->boot_ind == 0x80)?'*':(!p->boot_ind)?' ':'?');
472    start_sec = swap_le32toh(p->start4) + partitions[i].start_offset;
473    secs = swap_le32toh(p->size4);
474
475    if ((start_sec + secs) == 0) end_sec = 0;
476    else end_sec = start_sec + secs -1;
477    start_cyl = start_sec/(g_heads * g_sectors) + 1;
478    end_cyl = end_sec/(g_heads * g_sectors) + 1;
479    blocks = secs;
480    if (g_sect_size < ONE_K) {
481      blocks /= (ONE_K/g_sect_size);
482      odds = secs %(ONE_K/g_sect_size);
483    } else if (g_sect_size > ONE_K) blocks *= (g_sect_size/ONE_K);
484
485    if (lastchar) xprintf("%s%c%d",dev, lastchar, i+1);
486    else xprintf("%s%d",dev, i+1);
487
488    xprintf("   %c %11u %11u %11u%c %2x %s\n",
489        boot,
490        disp_unit_cyl == 0? start_sec: start_cyl,
491        disp_unit_cyl == 0? end_sec: end_cyl,
492        blocks,odds?'+':' ', p->sys_ind, get_type(p->sys_ind));
493
494    consistency_check(p, i);
495  }
496  if (check_order()) xprintf("\nPartition table entries are not in disk order");
497}
498
499//Print device details
500static void print_mbr(int validate)
501{
502  unsigned long long bytes = ((unsigned long long)total_number_sectors << 9);
503  long mbytes = bytes/1000000;
504
505  if (mbytes < 10000) xprintf("Disk %s: %lu MB, %llu bytes\n", disk_device, mbytes, bytes);
506  else xprintf("Disk %s: %lu.%lu GB, %llu bytes\n", disk_device, mbytes/1000, (mbytes/100)%10, bytes);
507  xprintf("%ld heads, %ld sectors/track, %ld cylinders", g_heads, g_sectors, g_cylinders);
508  if (!disp_unit_cyl) {
509    xprintf(", total %lld sectors\n", total_number_sectors/(g_sect_size/SECTOR_SIZE));
510    xprintf("Units = sectors of 1 * %ld = %ld bytes\n",g_sect_size, g_sect_size);
511  } else xprintf("\nUnits = cylinders of %ld * %ld = %ld bytes\n\n",
512      g_heads * g_sectors, g_sect_size, g_heads * g_sectors * g_sect_size);
513  list_partitions(validate);
514  xputc('\n');
515}
516
517static void init_members(void)
518{
519  int i = 0;
520  num_parts = 4; //max of primaries in a part table
521  disp_unit_cyl = dos_flag = 1;
522  extended_offset = 0;
523  g_sect_size = SECTOR_SIZE;
524  for (i = 0; i < num_parts; i++) {
525    partitions[i].part = part_offset(MBRbuf, i);
526    partitions[i].sec_buffer = MBRbuf;
527    partitions[i].modified = 0;
528    partitions[i].start_offset = 0;
529  }
530}
531
532static int read_input(char *mesg, char *outp)
533{
534  char *p;
535  int size = 0;
536  do {
537    xprintf("%s", mesg);
538    p = fgets(toybuf, 80, stdin);
539
540    if (!p || !(size = strlen(p))) exit(0);
541    if (p[size-1] == '\n') p[--size] = '\0';
542  } while (!size);
543
544  while (*p != '\0' && *p <= ' ') p++;
545  if (outp) memcpy(outp, p, strlen(p) + 1); //1 for nul
546  return *p;
547}
548
549static int read_hex(char *mesg)
550{
551  int val;
552  char input[80], *endp;
553  while (1) {
554    read_input(mesg, input);
555    if ((*input | 0x20) == 'l') {
556      list_types();
557      memset(input, 0, 80);
558      continue;
559    }
560    val = strtoul(input, &endp, 16);
561    if (endp && *endp) continue;
562    if (val <= 0xff) return val;
563  }
564}
565
566/* Delete an exiting partition,
567 * if its primary, then just clear the partition details
568 * if extended, then clear the partition details, also for logical
569 * if only logical, then move the later partitions backwards 1 step
570 */
571void delete_partition(int i)
572{
573  int sys_id, looper = 0;
574  struct partition *p, *q, *ext_p, *ext_q;
575  sector_t new_start;
576  struct part_entry *pe = &partitions[i];
577
578  if (chs_warn()) return;
579  p = pe->part;
580  sys_id = p->sys_ind;
581  if (!sys_id) xprintf("Partition %u is empty\n", i+1);
582
583  if (i < 4 && !IS_EXTENDED(sys_id)) {
584    memset(p, 0, sizeof(struct partition)); //clear_partition
585    pe->modified = 1;
586  } else if (i < 4 && IS_EXTENDED(sys_id)) {
587    memset(p, 0, sizeof(struct partition)); //clear_partition
588    pe->modified = 1;
589    for (looper = 4; looper < num_parts; looper++) {
590      pe = &partitions[looper];
591      p = pe->part;
592      if (is_partition_clear(p)) break;
593      else {
594        memset(p, 0, sizeof(struct partition)); //clear_partition
595        pe->modified = 1;
596        free(pe->sec_buffer);
597      }
598    }
599    extended_offset = 0;
600    num_parts = 4;
601  } else {
602    //only logical is delete, need to move the rest of them backwards
603    if (i == 4) { //move partiton# 6 to 5.
604      partitions[i].modified = 1;
605      if (num_parts > i+1) {
606        q = partitions[i + 1].part;
607        *p = *q; //copy the part table
608        ext_p = part_offset(partitions[i].sec_buffer, 1);
609        ext_q = part_offset(partitions[i + 1].sec_buffer, 1);
610        *ext_p = *ext_q; //copy the extended info pointer
611        // change the start of the 4th partiton.
612        new_start = partitions[i + 1].start_offset + swap_le32toh(q->start4) - extended_offset;
613        new_start = SWAP_LE32(new_start);
614        memcpy(p->start4, (void *)&new_start, 4);
615      } else {
616        memset(partitions[i].part, 0, sizeof(struct partition));
617        return; //only logical
618      }
619    } else if (i > 4) {
620      ext_p = part_offset(partitions[i-1].sec_buffer, 1);
621      ext_q = part_offset(partitions[i].sec_buffer, 1);
622      memcpy((void*)ext_p, (void *)ext_q, sizeof(struct partition));
623      partitions[i-1].modified = 1;
624    }
625    if (i == 4) looper = i+2;
626    else if (i > 4) looper = i+1;
627    for (; looper < num_parts; looper++)
628      partitions[looper-1] = partitions[looper];
629    num_parts--;
630  }
631}
632
633static int ask_partition(int num_parts)
634{
635  int val;
636  while (1) {
637    do {
638      xprintf("Partition (%u - %u):", 1, num_parts);
639      fgets(toybuf, 80, stdin);
640    } while (!isdigit(*toybuf));
641    val = atoi(toybuf);
642    if (val > 0 && val <= num_parts) return val;
643    else xprintf("Invalid number entered\n");
644  }
645}
646
647static void toggle_active_flag(int i)
648{
649  struct partition *p = partitions[i].part;
650  if (is_partition_clear(p)) xprintf("Partition %u is empty\n", i+1);
651
652  if (IS_EXTENDED(p->sys_ind) && !p->boot_ind)
653    xprintf("WARNING: Partition %u is an extended partition\n", i + 1);
654  p->boot_ind = p->boot_ind == 0x80?0 : 0x80;
655  partitions[i].modified = 1;
656}
657
658//Write the partition details from Buffer to Disk.
659void write_table(void)
660{
661  int i =0;
662  struct part_entry *pe;
663  sector_t offset;
664
665  for (i = 0; i < 4; i++)
666    if (partitions[i].modified) partitions[3].modified = 1;
667
668  for (i = 3; i < num_parts; i++) {
669    pe = &partitions[i];
670    write_table_flag(pe->sec_buffer);
671    offset = pe->start_offset;
672    if (pe->modified == 1) {
673      xlseek(dev_fd, offset * g_sect_size, SEEK_SET);
674      xwrite(dev_fd, pe->sec_buffer, g_sect_size);
675    }
676  }
677  xprintf("The partition table has been altered.\n");
678  xprintf("Calling ioctl() to re-read partition table\n");
679  sync();
680  for (i = 4; i < num_parts; i++) free(partitions[i].sec_buffer);
681  if(ioctl(dev_fd, BLKRRPART, NULL) < 0)
682    perror_exit("WARNING: rereading partition table failed, kernel still uses old table");
683
684}
685
686/* try to find a partition for deletion, if only
687 * one, then select the same, else ask from USER
688 */
689static int get_non_free_partition(int max)
690{
691  int num = -1, i = 0;
692
693  for (i = 0; i < max; i++) {
694    if (!is_partition_clear(partitions[i].part)) {
695      if (num >= 0)
696        return ask_partition(num_parts)-1;
697      num = i;
698    }
699  }
700  (num >= 0) ? xprintf("Selected partition %d\n",num+1):
701    xprintf("No partition is defined yet!\n");
702  return num;
703}
704
705/* a try at autodetecting an empty partition table entry,
706 * if multiple options then get USER's choce.
707 */
708static int get_free_partition(int max)
709{
710  int num = -1, i = 0;
711
712  for (i = 0; i < max; i++) {
713    if (is_partition_clear(partitions[i].part)) {
714      if (num >= 0)
715        return ask_partition(4)-1;
716      num = i;
717    }
718  }
719  (num >= 0) ? xprintf("Selected partition %d\n",num+1):
720    xprintf("All primary partitions have been defined already!\n");
721  return num;
722}
723
724//taking user input for partition start/end sectors/cyinders
725static uint32_t ask_value(char *mesg, sector_t left, sector_t right, sector_t defalt)
726{
727  char *str = toybuf;
728  uint32_t val;
729  int use_default = 1;
730
731  while (1) {
732    use_default = 1;
733    do {
734      xprintf("%s",mesg);
735      fgets(str, 80, stdin);
736    } while (!isdigit(*str) && (*str != '\n')
737        && (*str != '-') && (*str != '+') && (!isblank(*str)));
738    while (isblank(*str)) str++; //remove leading white spaces
739    if (*str == '+' || *str == '-') {
740      int minus = (*str == '-');
741      int absolute = 0;
742
743      val = atoi(str + 1);
744      while (isdigit(*++str)) use_default = 0;
745
746      switch (*str) {
747        case 'c':
748        case 'C':
749          if (!disp_unit_cyl) val *= g_heads * g_sectors;
750          break;
751        case 'K':
752          absolute = ONE_K;
753          break;
754        case 'k':
755          absolute = 1000;
756          break;
757        case 'm':
758        case 'M':
759          absolute = 1000000;
760          break;
761        case 'g':
762        case 'G':
763          absolute = 1000000000;
764          break;
765        default:
766          break;
767      }
768      if (absolute) {
769        unsigned long long bytes = (unsigned long long) val * absolute;
770        unsigned long unit = (disp_unit_cyl && (g_heads * g_sectors))? g_heads * g_sectors : 1;
771
772        unit = unit * g_sect_size;
773        bytes += unit/2; // rounding
774        bytes /= unit;
775        val = bytes;
776      }
777      if (minus)
778        val = -val;
779      val += left;
780    } else {
781      val = atoi(str);
782      while (isdigit(*str)) {
783        str++;
784        use_default = 0;
785      }
786    }
787    if(use_default) {
788      val = defalt;
789      xprintf("Using default value %lld\n", defalt);
790    }
791    if (val >= left && val <= right) return val;
792    else xprintf("Value out of range\n");
793  }
794}
795
796//validating if the start given falls in a limit or not
797static int validate(int start_index, sector_t* begin,sector_t* end, sector_t start
798    , int asked)
799{
800  int i, valid = 0;
801  for (i = start_index; i < num_parts; i++) {
802    if (start >= begin[i] && start <= end[i]) {
803      if (asked) xprintf("Sector %lld is already allocated\n",start);
804      valid = 0;
805      break;
806    } else valid = 1;
807  }
808  return valid;
809}
810
811//get the start sector/cylinder of a new partition
812static sector_t ask_start_sector(int idx, sector_t* begin, sector_t* end, int ext_idx)
813{
814  sector_t start, limit, temp = 0, start_cyl, limit_cyl, offset = 1;
815  char mesg[256];
816  int i, asked = 0, valid = 0, start_index = 0;
817
818  if (dos_flag) offset = g_sectors;
819  start = offset;
820  if (disp_unit_cyl) limit = (sector_t)g_sectors * g_heads * g_cylinders - 1;
821  else limit = total_number_sectors - 1;
822
823  if (disp_unit_cyl) //make the begin of every partition to cylnder boundary
824    for (i = 0; i < num_parts; i++)
825      begin[i] = (begin[i]/(g_heads* g_sectors)) * (g_heads* g_sectors);
826
827  if (idx >= 4) {
828    if (!begin[ext_idx] && extended_offset) begin[ext_idx] = extended_offset;
829    start = begin[ext_idx] + offset;
830    limit = end[ext_idx];
831    start_index = 4;
832  }
833  do {
834    if (asked) valid = validate(start_index, begin, end, start, asked);
835    if (valid) break;
836
837    do {
838      for (i = start_index; i < num_parts; i++)
839        if (start >= begin[i] && start <= end[i])
840          start = end[i] + 1 + ((idx >= 4)? offset : 0);
841    } while (!validate(start_index, begin, end, start, 0));
842
843    start_cyl = start/(g_sectors * g_heads) + 1;
844    limit_cyl = limit/(g_sectors * g_heads) + 1;
845
846    if (start > limit) break;
847    sprintf(mesg, "First %s (%lld - %lld, default %lld): ", disp_unit_cyl? "cylinder" : "sector",
848        (long long int)(disp_unit_cyl? start_cyl : start),
849        (long long int)(disp_unit_cyl? limit_cyl : limit),
850        (long long int)(disp_unit_cyl? start_cyl : start));
851    temp = ask_value(mesg, disp_unit_cyl? start_cyl : start,
852        disp_unit_cyl? limit_cyl : limit, disp_unit_cyl? start_cyl : start);
853    asked = 1;
854
855    if (disp_unit_cyl) {
856      // point to the cylinder start sector
857      temp = (temp-1) * g_heads * g_sectors;
858      if (temp < start) //the boundary is falling in the already used sectors.
859        temp = start;
860    }
861    start = temp;
862  } while (asked && !valid);
863  return start;
864}
865
866//get the end sector/cylinder of a new partition
867static sector_t ask_end_sector(int idx, sector_t* begin, sector_t* end, int ext_idx, sector_t start_sec)
868{
869  sector_t limit, temp = 0, start_cyl, limit_cyl, start = start_sec;
870  char mesg[256];
871  int i;
872
873  if (disp_unit_cyl) limit = (sector_t)g_sectors * g_heads * g_cylinders - 1;
874  else limit = total_number_sectors - 1;
875
876  if (disp_unit_cyl) //make the begin of every partition to cylnder boundary
877    for (i = 0; i < num_parts; i++)
878      begin[i] = (begin[i]/(g_heads* g_sectors)) * (g_heads* g_sectors);
879
880  if (idx >= 4) limit = end[ext_idx];
881
882  for (i = 0; i < num_parts; i++) {
883    struct part_entry *pe = &partitions[i];
884    if (start < pe->start_offset && limit >= pe->start_offset) limit = pe->start_offset - 1;
885    if (start < begin[i] && limit >= begin[i]) limit = begin[i] - 1;
886  }
887
888  start_cyl = start/(g_sectors * g_heads) + 1;
889  limit_cyl = limit/(g_sectors * g_heads) + 1;
890  if (limit < start) { //the boundary is falling in the already used sectors.
891    xprintf("No Free sectors available\n");
892    return 0;
893  }
894  sprintf(mesg, "Last %s or +size or +sizeM or +sizeK (%lld - %lld, default %lld): ",
895      disp_unit_cyl? "cylinder" : "sector",
896      (long long int)(disp_unit_cyl? start_cyl : start),
897      (long long int)(disp_unit_cyl? limit_cyl : limit),
898      (long long int)(disp_unit_cyl? limit_cyl : limit));
899  temp = ask_value(mesg, disp_unit_cyl? start_cyl : start,
900      disp_unit_cyl? limit_cyl : limit, disp_unit_cyl? limit_cyl : limit);
901
902  if (disp_unit_cyl) { // point to the cylinder start sector
903    temp = temp * g_heads * g_sectors - 1;
904    if (temp > limit) temp = limit;
905  }
906  if (temp < start) { //the boundary is falling in the already used sectors.
907    xprintf("No Free sectors available\n");
908    return 0;
909  }
910  return temp;
911}
912
913// add a new partition to the partition table
914static int add_partition(int idx, int sys_id)
915{
916  int i, ext_idx = -1;
917  sector_t start, end, begin_sec[num_parts], end_sec[num_parts];
918  struct part_entry *pe = &partitions[idx];
919  struct partition *p = pe->part;
920
921  if (p && !is_partition_clear(p)) {
922    xprintf("Partition %u is already defined, delete it to re-add\n", idx+1);
923    return 0;
924  }
925  for (i = 0; i < num_parts; i++) {
926    pe = &partitions[i];
927    p = pe->part;
928    if (is_partition_clear(p)) {
929      begin_sec[i] = 0xffffffff;
930      end_sec[i] = 0;
931    } else {
932      begin_sec[i] = swap_le32toh(p->start4) + pe->start_offset;
933      end_sec[i] = begin_sec[i] + swap_le32toh(p->size4) - 1;
934    }
935    if (IS_EXTENDED(p->sys_ind)) ext_idx = i;
936  }
937  start = ask_start_sector(idx, begin_sec, end_sec, ext_idx);
938  end = ask_end_sector(idx, begin_sec, end_sec, ext_idx, start);
939  if (!end) return 0;
940  //Populate partition table entry  - 16 bytes
941  pe = &partitions[idx];
942  p = pe->part;
943
944  if (idx > 4) {
945    if (dos_flag) pe->start_offset = start - (sector_t)g_sectors;
946    else pe->start_offset = start - 1;
947    if (pe->start_offset == extended_offset) pe->start_offset++;
948    if (!dos_flag) start++;
949  }
950
951  set_levalue(p->start4, start - pe->start_offset);
952  set_levalue(p->size4, end - start + 1);
953  set_hsc(p, start, end);
954  p->boot_ind = 0;
955  p->sys_ind = sys_id;
956  pe->modified = 1;
957
958  if (idx > 4) {
959    p = partitions[idx-1].part + 1; //extended pointer for logical partitions
960    set_levalue(p->start4, pe->start_offset - extended_offset);
961    set_levalue(p->size4, end - start + 1 + (dos_flag? g_sectors: 1));
962    set_hsc(p, pe->start_offset, end);
963    p->boot_ind = 0;
964    p->sys_ind = EXTENDED;
965    partitions[idx-1].modified = 1;
966  }
967  if (IS_EXTENDED(sys_id)) {
968    pe = &partitions[4];
969    pe->modified = 1;
970    pe->sec_buffer = xzalloc(g_sect_size);
971    pe->part = part_offset(pe->sec_buffer, 0);
972    pe->start_offset = extended_offset = start;
973    num_parts = 5;
974  }
975  return 1;
976}
977
978static void add_logical_partition(void)
979{
980  struct part_entry *pe;
981  if (num_parts > 5 || !is_partition_clear(partitions[4].part)) {
982    pe = &partitions[num_parts];
983    pe->modified = 1;
984    pe->sec_buffer = xzalloc(g_sect_size);
985    pe->part = part_offset(pe->sec_buffer, 0);
986    pe->start_offset = 0;
987    num_parts++;
988    if (!add_partition(num_parts - 1, LINUX_NATIVE)) {
989      num_parts--;
990      free(pe->sec_buffer);
991    }
992  }
993  else add_partition(num_parts -1, LINUX_NATIVE);
994}
995
996/* Add a new partiton to the partition table.
997 * MAX partitions limit is taken to be 60, can be changed
998 */
999static void add_new_partition(void)
1000{
1001  int choice, idx, i, free_part = 0;
1002  char *msg = NULL;
1003
1004  if (chs_warn()) return;
1005  for (i = 0; i < 4; i++) if(is_partition_clear(partitions[i].part)) free_part++;
1006
1007  if (!free_part && num_parts >= 60) {
1008    xprintf("The maximum number of partitions has been created\n");
1009    return;
1010  }
1011  if (!free_part) {
1012    if (extended_offset) add_logical_partition();
1013    else xprintf("You must delete some partition and add "
1014          "an extended partition first\n");
1015    return;
1016  }
1017
1018  msg = xmprintf("  %s\n  p  primary partition(1-4)\n",
1019          extended_offset? "l  logical (5 or over)" : "e  extended");
1020
1021  choice = 0x20 | read_input(msg, NULL);
1022  free(msg);
1023  if (choice == 'p') {
1024    idx = get_free_partition(4);
1025    if (idx >= 0) add_partition(idx, LINUX_NATIVE);
1026    return;
1027  }
1028  if (choice =='l' && extended_offset) {
1029    add_logical_partition();
1030    return;
1031  }
1032  if (choice == 'e' && !extended_offset) {
1033    idx = get_free_partition(4);
1034    if (idx >= 0) add_partition(idx, EXTENDED);
1035    return;
1036  }
1037}
1038
1039static void change_systype(void )
1040{
1041  int i, sys_id;
1042  struct partition *p;
1043  struct part_entry *pe;
1044
1045  i = ask_partition(num_parts);
1046  pe = &partitions[i-1];
1047  p = pe->part;
1048  if (is_partition_clear(p)) {
1049    xprintf("Partition %d doesn't exist yet!\n", i);
1050    return;
1051  }
1052  sys_id = read_hex("Hex code (L to list codes): ");
1053  if ((IS_EXTENDED(p->sys_ind) && !IS_EXTENDED(sys_id)) ||
1054      (!IS_EXTENDED(p->sys_ind) && IS_EXTENDED(sys_id))) {
1055    xprintf("you can't change a  partition to an extended or vice-versa\n");
1056    return;
1057  }
1058
1059  xprintf("Changed system type of partition %u to %0x (%s)\n",i, sys_id, get_type(sys_id));
1060  p->sys_ind = sys_id;
1061  pe->modified = 1;
1062}
1063
1064static void check(int n, unsigned h, unsigned s, unsigned c, sector_t start)
1065{
1066  sector_t total, real_s, real_c;
1067
1068  real_s = sector(s) - 1;
1069  real_c = cylinder(s, c);
1070  total = (real_c * g_sectors + real_s) * g_heads + h;
1071  if (!total) xprintf("Partition %u contains sector 0\n", n);
1072  if (h >= g_heads)
1073    xprintf("Partition %u: head %u greater than maximum %lu\n", n, h + 1, g_heads);
1074  if (real_s >= g_sectors)
1075    xprintf("Partition %u: sector %u greater than maximum %lu\n", n, s, g_sectors);
1076  if (real_c >= g_cylinders)
1077    xprintf("Partition %u: cylinder %lld greater than maximum %lu\n", n, real_c + 1, g_cylinders);
1078  if (g_cylinders <= ONE_K && start != total)
1079    xprintf("Partition %u: previous sectors %lld disagrees with total %lld\n", n, start, total);
1080}
1081
1082static void verify_table(void)
1083{
1084  int i, j, ext_idx = -1;
1085  sector_t begin_sec[num_parts], end_sec[num_parts], total = 1;
1086  struct part_entry *pe;
1087  struct partition *p;
1088
1089  for (i = 0; i < num_parts; i++) {
1090    pe = &partitions[i];
1091    p = pe->part;
1092    if (is_partition_clear(p) || IS_EXTENDED(p->sys_ind)) {
1093      begin_sec[i] = 0xffffffff;
1094      end_sec[i] = 0;
1095    } else {
1096      begin_sec[i] = swap_le32toh(p->start4) + pe->start_offset;
1097      end_sec[i] = begin_sec[i] + swap_le32toh(p->size4) - 1;
1098    }
1099    if (IS_EXTENDED(p->sys_ind)) ext_idx = i;
1100  }
1101  for (i = 0; i < num_parts; i++) {
1102    pe = &partitions[i];
1103    p = pe->part;
1104    if (p->sys_ind && !IS_EXTENDED(p->sys_ind)) {
1105      consistency_check(p, i);
1106      if ((swap_le32toh(p->start4) + pe->start_offset) < begin_sec[i])
1107        xprintf("Warning: bad start-of-data in partition %u\n", i + 1);
1108      check(i + 1, p->end_head, p->end_sector, p->end_cyl, end_sec[i]);
1109      total += end_sec[i] + 1 - begin_sec[i];
1110      for (j = 0; j < i; j++) {
1111        if ((begin_sec[i] >= begin_sec[j] && begin_sec[i] <= end_sec[j])
1112            || ((end_sec[i] <= end_sec[j] && end_sec[i] >= begin_sec[j]))) {
1113          xprintf("Warning: partition %u overlaps partition %u\n", j + 1, i + 1);
1114          total += begin_sec[i] >= begin_sec[j] ? begin_sec[i] : begin_sec[j];
1115          total -= end_sec[i] <= end_sec[j] ? end_sec[i] : end_sec[j];
1116        }
1117      }
1118    }
1119  }
1120  if (extended_offset) {
1121    struct part_entry *pex = &partitions[ext_idx];
1122    sector_t e_last = swap_le32toh(pex->part->start4) +
1123      swap_le32toh(pex->part->size4) - 1;
1124
1125    for (i = 4; i < num_parts; i++) {
1126      total++;
1127      p = partitions[i].part;
1128      if (!p->sys_ind) {
1129        if (i != 4 || i + 1 < num_parts)
1130          xprintf("Warning: partition %u is empty\n", i + 1);
1131      } else if (begin_sec[i] < extended_offset || end_sec[i] > e_last)
1132        xprintf("Logical partition %u not entirely in partition %u\n", i + 1, ext_idx + 1);
1133    }
1134  }
1135  if (total > g_heads * g_sectors * g_cylinders)
1136    xprintf("Total allocated sectors %lld greater than the maximum "
1137        "%lu\n", total, g_heads * g_sectors * g_cylinders);
1138  else {
1139    total = g_heads * g_sectors * g_cylinders - total;
1140    if (total) xprintf("%lld unallocated sectors\n", total);
1141  }
1142}
1143
1144static void move_begning(int idx)
1145{
1146  sector_t start, num, new_start, end;
1147  char mesg[256];
1148  struct part_entry *pe = &partitions[idx];
1149  struct partition *p = pe->part;
1150
1151  if (chs_warn()) return;
1152  start = swap_le32toh(p->start4) + pe->start_offset;
1153  num = swap_le32toh(p->size4);
1154  end = start + num -1;
1155
1156  if (!num || IS_EXTENDED(p->sys_ind)) {
1157    xprintf("Partition %u doesn't have data area\n", idx+1);
1158    return;
1159  }
1160  sprintf(mesg, "New begining of data (0 - %lld, default %lld): ",
1161      (long long int)(end), (long long int)(start));
1162  new_start = ask_value(mesg, 0, end, start);
1163  if (new_start != start) {
1164    set_levalue(p->start4, new_start - pe->start_offset);
1165    set_levalue(p->size4, end - new_start +1);
1166    if ((read_input("Recalculate C/H/S (Y/n): ", NULL) | 0x20) == 'y')
1167      set_hsc(p, new_start, end);
1168    pe->modified = 1;
1169  }
1170}
1171
1172static void print_raw_sectors()
1173{
1174  int i, j;
1175  struct part_entry *pe;
1176
1177  xprintf("Device: %s\n", disk_device);
1178  for (i = 3; i < num_parts; i++) {
1179    pe = &partitions[i];
1180    for (j = 0; j < g_sect_size; j++) {
1181      if (!(j % 16)) xprintf("\n0x%03X: ",j);
1182      xprintf("%02X ",pe->sec_buffer[j]);
1183    }
1184    xputc('\n');
1185  }
1186}
1187
1188static void print_partitions_list(int ext)
1189{
1190  int i;
1191  struct part_entry *pe;
1192  struct partition *p;
1193
1194  xprintf("Disk %s: %lu heads, %lu sectors, %lu cylinders\n\n", disk_device, g_heads, g_sectors, g_cylinders);
1195  xprintf("Nr AF  Hd Sec  Cyl  Hd Sec  Cyl      Start       Size ID\n");
1196
1197  for (i = 0; i < num_parts; i++) {
1198    pe = &partitions[i];
1199    p = pe->part;
1200    if (p) {
1201      if (ext && (i >= 4)) p = pe->part + 1;
1202      if(ext && i < 4 && !IS_EXTENDED(p->sys_ind)) continue;
1203
1204      xprintf("%2u %02x%4u%4u%5u%4u%4u%5u%11u%11u %02x\n",
1205          i+1, p->boot_ind, p->head,
1206          sector(p->sector), cylinder(p->sector, p->cyl),
1207          p->end_head,
1208          sector(p->end_sector), cylinder(p->end_sector, p->end_cyl),
1209          swap_le32toh(p->start4),
1210          swap_le32toh(p->size4),
1211          p->sys_ind);
1212      if (p->sys_ind) consistency_check(p, i);
1213    }
1214  }
1215}
1216
1217//fix the partition table order to ascending
1218static void fix_order(void)
1219{
1220  sector_t first[num_parts], min;
1221  int i, j, oj, ojj, sj, sjj;
1222  struct part_entry *pe;
1223  struct partition *px, *py, temp, *pj, *pjj, tmp;
1224
1225  for (i = 0; i < num_parts; i++) {
1226    pe = &partitions[i];
1227    px = pe->part;
1228    if (is_partition_clear(px)) first[i] = 0xffffffff;
1229    else first[i] = swap_le32toh(px->start4) + pe->start_offset;
1230  }
1231
1232  if (!check_order()) {
1233    xprintf("Ordering is already correct\n\n");
1234    return;
1235  }
1236  for (i = 0; i < 4; i++) {
1237    for (j = 0; j < 3; j++) {
1238      if (first[j] > first[j+1]) {
1239        py = partitions[j+1].part;
1240        px = partitions[j].part;
1241        memcpy(&temp, py, sizeof(struct partition));
1242        memcpy(py, px, sizeof(struct partition));
1243        memcpy(px, &temp, sizeof(struct partition));
1244        min = first[j+1];
1245        first[j+1] = first[j];
1246        first[j] = min;
1247        partitions[j].modified = 1;
1248      }
1249    }
1250  }
1251  for (i = 5; i < num_parts; i++) {
1252    for (j = 5; j < num_parts - 1; j++) {
1253      oj = partitions[j].start_offset;
1254      ojj = partitions[j+1].start_offset;
1255      if (oj > ojj) {
1256        partitions[j].start_offset = ojj;
1257        partitions[j+1].start_offset = oj;
1258        pj = partitions[j].part;
1259        set_levalue(pj->start4, swap_le32toh(pj->start4)+oj-ojj);
1260        pjj = partitions[j+1].part;
1261        set_levalue(pjj->start4, swap_le32toh(pjj->start4)+ojj-oj);
1262        set_levalue((partitions[j-1].part+1)->start4, ojj-extended_offset);
1263        set_levalue((partitions[j].part+1)->start4, oj-extended_offset);
1264      }
1265    }
1266  }
1267  for (i = 4; i < num_parts; i++) {
1268    for (j = 4; j < num_parts - 1; j++) {
1269      pj = partitions[j].part;
1270      pjj = partitions[j+1].part;
1271      sj = swap_le32toh(pj->start4);
1272      sjj = swap_le32toh(pjj->start4);
1273      oj = partitions[j].start_offset;
1274      ojj = partitions[j+1].start_offset;
1275      if (oj+sj > ojj+sjj) {
1276        tmp = *pj;
1277        *pj = *pjj;
1278        *pjj = tmp;
1279        set_levalue(pj->start4, ojj+sjj-oj);
1280        set_levalue(pjj->start4, oj+sj-ojj);
1281      }
1282    }
1283  }
1284  // If anything changed
1285  for (j = 4; j < num_parts; j++) partitions[j].modified = 1;
1286  xprintf("Done!\n");
1287}
1288
1289static void print_menu(void)
1290{
1291  xprintf("a\ttoggle a bootable flag\n"
1292  "b\tedit bsd disklabel\n"
1293  "c\ttoggle the dos compatibility flag\n"
1294  "d\tdelete a partition\n"
1295  "l\tlist known partition types\n"
1296  "n\tadd a new partition\n"
1297  "o\tcreate a new empty DOS partition table\n"
1298  "p\tprint the partition table\n"
1299  "q\tquit without saving changes\n"
1300  "s\tcreate a new empty Sun disklabel\n"
1301  "t\tchange a partition's system id\n"
1302  "u\tchange display/entry units\n"
1303  "v\tverify the partition table\n"
1304  "w\twrite table to disk and exit\n"
1305  "x\textra functionality (experts only)\n");
1306}
1307
1308static void print_xmenu(void)
1309{
1310  xprintf("b\tmove beginning of data in a partition\n"
1311  "c\tchange number of cylinders\n"
1312  "d\tprint the raw data in the partition table\n"
1313  "e\tlist extended partitions\n"
1314  "f\tfix partition order\n"
1315  "h\tchange number of heads\n"
1316  "p\tprint the partition table\n"
1317  "q\tquit without saving changes\n"
1318  "r\treturn to main menu\n"
1319  "s\tchange number of sectors/track\n"
1320  "v\tverify the partition table\n"
1321  "w\twrite table to disk and exit\n");
1322}
1323
1324static void expert_menu(void)
1325{
1326  int choice, idx;
1327  sector_t value;
1328  char mesg[256];
1329
1330  while (1) {
1331    xputc('\n');
1332    char *msg = "Expert Command ('m' for help): ";
1333    choice = 0x20 | read_input(msg, NULL);
1334    switch (choice) {
1335      case 'b': //move data begining in partition
1336        idx = ask_partition(num_parts);
1337        move_begning(idx - 1);
1338        break;
1339      case 'c': //change cylinders
1340          sprintf(mesg, "Number of cylinders (1 - 1048576, default %lu): ", g_cylinders);
1341          value = ask_value(mesg, 1, 1048576, g_cylinders);
1342          g_cylinders = TT.cylinders = value;
1343          toys.optflags |= FLAG_C;
1344          if(g_cylinders > ONE_K)
1345            xprintf("\nThe number of cylinders for this disk is set to %lu.\n"
1346                "There is nothing wrong with that, but this is larger than 1024,\n"
1347                "and could in certain setups cause problems.\n", g_cylinders);
1348        break;
1349      case 'd': //print raw data in part tables
1350        print_raw_sectors();
1351        break;
1352      case 'e': //list extended partitions
1353        print_partitions_list(1);
1354        break;
1355      case 'f': //fix part order
1356        fix_order();
1357        break;
1358      case 'h': //change number of heads
1359          sprintf(mesg, "Number of heads (1 - 256, default %lu): ", g_heads);
1360          value = ask_value(mesg, 1, 256, g_heads);
1361          g_heads = TT.heads = value;
1362          toys.optflags |= FLAG_H;
1363        break;
1364      case 'p': //print partition table
1365        print_partitions_list(0);
1366        break;
1367      case 'q':
1368        free_bufs();
1369        close(dev_fd);
1370        xputc('\n');
1371        exit(0);
1372        break;
1373      case 'r':
1374        return;
1375        break;
1376      case 's': //change sector/track
1377          sprintf(mesg, "Number of sectors (1 - 63, default %lu): ", g_sectors);
1378          value = ask_value(mesg, 1, 63, g_sectors);
1379          g_sectors = TT.sectors = value;
1380          toys.optflags |= FLAG_H;
1381        break;
1382      case 'v':
1383        verify_table();
1384        break;
1385      case 'w':
1386        write_table();
1387        toys.exitval = 0;
1388        exit(0);
1389        break;
1390      case 'm':
1391        print_xmenu();
1392        break;
1393      default:
1394        xprintf("Unknown command '%c'\n",choice);
1395        print_xmenu();
1396        break;
1397    }
1398  } //while(1)
1399}
1400
1401static int disk_proper(const char *device)
1402{
1403  unsigned length;
1404  int fd = open(device, O_RDONLY);
1405
1406  if (fd != -1) {
1407    struct hd_geometry dev_geo;
1408    dev_geo.heads = 0;
1409    dev_geo.sectors = 0;
1410    int err = ioctl(fd, HDIO_GETGEO, &dev_geo);
1411    close(fd);
1412    if (!err) return (dev_geo.start == 0);
1413  }
1414  length = strlen(device);
1415  if (length != 0 && isdigit(device[length - 1])) return 0;
1416  return 1;
1417}
1418
1419static void reset_entries()
1420{
1421  int i;
1422
1423  memset(MBRbuf, 0, sizeof(MBRbuf));
1424  for (i = 4; i < num_parts; i++)
1425    memset(&partitions[i], 0, sizeof(struct part_entry));
1426}
1427
1428//this will keep dev_fd = 3 always alive
1429static void move_fd()
1430{
1431  int fd = xopen("/dev/null", O_RDONLY);
1432  if(fd != dev_fd) {
1433    if(dup2(fd, dev_fd) != dev_fd) perror_exit("Can't dup2");
1434    close(fd);
1435  }
1436}
1437
1438/* Read proc/partitions and then print the details
1439 * for partitions on each device
1440 */
1441static void read_and_print_parts()
1442{
1443  unsigned int ma, mi, sz;
1444  char *name = toybuf, *buffer = toybuf + ONE_K, *device = toybuf + 2048;
1445  FILE* fp = xfopen("/proc/partitions", "r");
1446
1447  while (fgets(buffer, ONE_K, fp)) {
1448    reset_entries();
1449    num_parts = 4;
1450    memset(name, 0, sizeof(name));
1451    if (sscanf(buffer, " %u %u %u %[^\n ]", &ma, &mi, &sz, name) != 4)
1452      continue;
1453
1454    sprintf(device,"/dev/%s",name);
1455    if (disk_proper(device)) {
1456      if (read_mbr(device, 0)) continue;
1457      print_mbr(1);
1458      move_fd();
1459    }
1460  }
1461  fclose(fp);
1462}
1463
1464void fdisk_main(void)
1465{
1466  int choice, p;
1467
1468  init_members();
1469  move_fd();
1470  if (TT.heads >= 256) TT.heads = 0;
1471  if (TT.sectors >= 64) TT.sectors = 0;
1472  if (toys.optflags & FLAG_u) disp_unit_cyl = 0;
1473  if (toys.optflags & FLAG_l) {
1474    if (!toys.optc) read_and_print_parts();
1475    else {
1476      while(*toys.optargs){
1477        if (read_mbr(*toys.optargs, 0)) {
1478          toys.optargs++;
1479          continue;
1480        }
1481        print_mbr(1);
1482        move_fd();
1483        toys.optargs++;
1484      }
1485    }
1486    toys.exitval = 0;
1487    return;
1488  } else {
1489    if (!toys.optc || toys.optc > 1 ) {
1490      toys.exitval = toys.exithelp = 1;
1491      show_help();
1492      return;
1493    }
1494    if (read_mbr(toys.optargs[0], 1)) return;
1495    while (1) {
1496      xputc('\n');
1497      char *msg = "Command ('m' for help): ";
1498      choice = 0x20 | read_input(msg, NULL);
1499      switch (choice) {
1500        case 'a':
1501          p = ask_partition(num_parts);
1502          toggle_active_flag(p - 1); //partition table index start from 0.
1503          break;
1504        case 'b':
1505          break;
1506        case 'c':
1507          dos_flag = !dos_flag;
1508          xprintf("Dos compatible flag is %s\n", dos_flag?"Set" : "Not set");
1509          break;
1510        case 'd':
1511          p = get_non_free_partition(num_parts); //4 was here
1512          if(p >= 0) delete_partition(p);
1513          break;
1514        case 'l':
1515          list_types();
1516          break;
1517        case 'n': //add new partition
1518          add_new_partition();
1519          break;
1520        case 'o':
1521          create_empty_doslabel();
1522          break;
1523        case 'p':
1524          print_mbr(0);
1525          break;
1526        case 'q':
1527          free_bufs();
1528          close(dev_fd);
1529          xputc('\n');
1530          exit(0);
1531          break;
1532        case 's':
1533          break;
1534        case 't':
1535          change_systype();
1536          break;
1537        case 'u':
1538          disp_unit_cyl = !disp_unit_cyl;
1539          xprintf("Changing Display/Entry units to %s\n",disp_unit_cyl?"cylinders" : "sectors");
1540          break;
1541        case 'v':
1542          verify_table();
1543          break;
1544        case 'w':
1545          write_table();
1546          toys.exitval = 0;
1547          return;
1548          break;
1549        case 'x':
1550          expert_menu();
1551          break;
1552        case 'm':
1553          print_menu();
1554          break;
1555        default:
1556          xprintf("%c: Unknown command\n",choice);
1557          break;
1558      }
1559    } //while(1)
1560  }
1561}
1562