493c6740b2ddd129ac59d97ef33fe8984f5941a2
[oweals/busybox.git] / util-linux / fdisk.c
1 /* vi: set sw=4 ts=4: */
2 /* fdisk.c -- Partition table manipulator for Linux.
3  *
4  * Copyright (C) 1992  A. V. Le Blanc (LeBlanc@mcc.ac.uk)
5  * Copyright (C) 2001,2002 Vladimir Oleynik <dzo@simtreas.ru> (initial bb port)
6  *
7  * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
8  */
9
10 #include <assert.h>             /* assert */
11 #include "busybox.h"
12
13 /* Looks like someone forgot to add this to config system */
14 #ifndef ENABLE_FEATURE_FDISK_BLKSIZE
15 # define ENABLE_FEATURE_FDISK_BLKSIZE 0
16 # define USE_FEATURE_FDISK_BLKSIZE(a)
17 #endif
18
19 #define DEFAULT_SECTOR_SIZE     512
20 #define MAX_SECTOR_SIZE 2048
21 #define SECTOR_SIZE     512     /* still used in osf/sgi/sun code */
22 #define MAXIMUM_PARTS   60
23
24 #define ACTIVE_FLAG     0x80
25
26 #define EXTENDED        0x05
27 #define WIN98_EXTENDED  0x0f
28 #define LINUX_PARTITION 0x81
29 #define LINUX_SWAP      0x82
30 #define LINUX_NATIVE    0x83
31 #define LINUX_EXTENDED  0x85
32 #define LINUX_LVM       0x8e
33 #define LINUX_RAID      0xfd
34
35 #define IS_EXTENDED(i) \
36         ((i) == EXTENDED || (i) == WIN98_EXTENDED || (i) == LINUX_EXTENDED)
37
38 #define SIZE(a) (sizeof(a)/sizeof((a)[0]))
39
40 #define cround(n)       (display_in_cyl_units ? ((n)/units_per_sector)+1 : (n))
41 #define scround(x)      (((x)+units_per_sector-1)/units_per_sector)
42
43 struct hd_geometry {
44         unsigned char heads;
45         unsigned char sectors;
46         unsigned short cylinders;
47         unsigned long start;
48 };
49
50 #define HDIO_GETGEO     0x0301  /* get device geometry */
51
52 static const char msg_building_new_label[] =
53 "Building a new %s. Changes will remain in memory only,\n"
54 "until you decide to write them. After that the previous content\n"
55 "won't be recoverable.\n\n";
56
57 static const char msg_part_already_defined[] =
58 "Partition %d is already defined, delete it before re-adding\n";
59
60
61 static unsigned sector_size = DEFAULT_SECTOR_SIZE;
62 static unsigned user_set_sector_size;
63 static unsigned sector_offset = 1;
64
65 #if ENABLE_FEATURE_OSF_LABEL
66 static int possibly_osf_label;
67 #endif
68
69 static unsigned heads, sectors, cylinders;
70 static void update_units(void);
71
72
73 struct partition {
74         unsigned char boot_ind;         /* 0x80 - active */
75         unsigned char head;             /* starting head */
76         unsigned char sector;           /* starting sector */
77         unsigned char cyl;              /* starting cylinder */
78         unsigned char sys_ind;          /* What partition type */
79         unsigned char end_head;         /* end head */
80         unsigned char end_sector;       /* end sector */
81         unsigned char end_cyl;          /* end cylinder */
82         unsigned char start4[4];        /* starting sector counting from 0 */
83         unsigned char size4[4];         /* nr of sectors in partition */
84 } ATTRIBUTE_PACKED;
85
86 enum failure {
87         ioctl_error, unable_to_open, unable_to_read, unable_to_seek,
88         unable_to_write
89 };
90
91 enum label_type {
92         label_dos, label_sun, label_sgi, label_aix, label_osf
93 };
94 #define LABEL_IS_DOS    (label_dos == current_label_type)
95
96 #if ENABLE_FEATURE_SUN_LABEL
97 #define LABEL_IS_SUN    (label_sun == current_label_type)
98 #define STATIC_SUN static
99 #else
100 #define LABEL_IS_SUN    0
101 #define STATIC_SUN extern
102 #endif
103
104 #if ENABLE_FEATURE_SGI_LABEL
105 #define LABEL_IS_SGI    (label_sgi == current_label_type)
106 #define STATIC_SGI static
107 #else
108 #define LABEL_IS_SGI    0
109 #define STATIC_SGI extern
110 #endif
111
112 #if ENABLE_FEATURE_AIX_LABEL
113 #define LABEL_IS_AIX    (label_aix == current_label_type)
114 #define STATIC_AIX static
115 #else
116 #define LABEL_IS_AIX    0
117 #define STATIC_AIX extern
118 #endif
119
120 #if ENABLE_FEATURE_OSF_LABEL
121 #define LABEL_IS_OSF    (label_osf == current_label_type)
122 #define STATIC_OSF static
123 #else
124 #define LABEL_IS_OSF    0
125 #define STATIC_OSF extern
126 #endif
127
128 enum action { fdisk, require, try_only, create_empty_dos, create_empty_sun };
129
130 static enum label_type current_label_type;
131
132 static const char *disk_device;
133 static int fd;                  /* the disk */
134 static int partitions = 4;      /* maximum partition + 1 */
135 static int display_in_cyl_units = 1;
136 static unsigned units_per_sector = 1;
137 #if ENABLE_FEATURE_FDISK_WRITABLE
138 static void change_units(void);
139 static void reread_partition_table(int leave);
140 static void delete_partition(int i);
141 static int get_partition(int warn, int max);
142 static void list_types(const char *const *sys);
143 static unsigned read_int(unsigned low, unsigned dflt, unsigned high, unsigned base, const char *mesg);
144 #endif
145 static const char *partition_type(unsigned char type);
146 static void fdisk_fatal(enum failure why) ATTRIBUTE_NORETURN;
147 static void get_geometry(void);
148 static int get_boot(enum action what);
149
150 #define PLURAL   0
151 #define SINGULAR 1
152
153 #define hex_val(c)      ({ \
154                                 char _c = (c); \
155                                 isdigit(_c) ? _c - '0' : \
156                                 tolower(_c) + 10 - 'a'; \
157                         })
158
159 #define LINE_LENGTH     80
160 #define pt_offset(b, n) ((struct partition *)((b) + 0x1be + \
161                                 (n) * sizeof(struct partition)))
162 #define sector(s)       ((s) & 0x3f)
163 #define cylinder(s, c)  ((c) | (((s) & 0xc0) << 2))
164
165 #define hsc2sector(h,s,c) (sector(s) - 1 + sectors * \
166                                 ((h) + heads * cylinder(s,c)))
167 #define set_hsc(h,s,c,sector) \
168         do { \
169                 s = sector % sectors + 1;  \
170                 sector /= sectors;         \
171                 h = sector % heads;        \
172                 sector /= heads;           \
173                 c = sector & 0xff;         \
174                 s |= (sector >> 2) & 0xc0; \
175         } while (0)
176
177 static unsigned get_start_sect(const struct partition *p);
178 static unsigned get_nr_sects(const struct partition *p);
179
180 /*
181  * per partition table entry data
182  *
183  * The four primary partitions have the same sectorbuffer (MBRbuffer)
184  * and have NULL ext_pointer.
185  * Each logical partition table entry has two pointers, one for the
186  * partition and one link to the next one.
187  */
188 struct pte {
189         struct partition *part_table;   /* points into sectorbuffer */
190         struct partition *ext_pointer;  /* points into sectorbuffer */
191 #if ENABLE_FEATURE_FDISK_WRITABLE
192         char changed;           /* boolean */
193 #endif
194         off_t offset;           /* disk sector number */
195         char *sectorbuffer;     /* disk sector contents */
196 };
197
198 struct globals {
199         /* Raw disk label. For DOS-type partition tables the MBR,
200          * with descriptions of the primary partitions. */
201         char MBRbuffer[MAX_SECTOR_SIZE];
202         /* Partition tables */
203         struct pte ptes[MAXIMUM_PARTS];
204 };
205
206 #define G (*(struct globals*)bb_common_bufsiz1)
207 #define MBRbuffer (G.MBRbuffer)
208 #define ptes      (G.ptes)
209
210
211 /*
212  * return partition name - uses static storage
213  */
214 static const char *
215 partname(const char *dev, int pno, int lth)
216 {
217         static char buffer[80];
218         const char *p;
219         int w, wp;
220         int bufsiz;
221         char *bufp;
222
223         bufp = buffer;
224         bufsiz = sizeof(buffer);
225
226         w = strlen(dev);
227         p = "";
228
229         if (isdigit(dev[w-1]))
230                 p = "p";
231
232         /* devfs kludge - note: fdisk partition names are not supposed
233            to equal kernel names, so there is no reason to do this */
234         if (strcmp(dev + w - 4, "disc") == 0) {
235                 w -= 4;
236                 p = "part";
237         }
238
239         wp = strlen(p);
240
241         if (lth) {
242                 snprintf(bufp, bufsiz, "%*.*s%s%-2u",
243                          lth-wp-2, w, dev, p, pno);
244         } else {
245                 snprintf(bufp, bufsiz, "%.*s%s%-2u", w, dev, p, pno);
246         }
247         return bufp;
248 }
249
250 #if ENABLE_FEATURE_FDISK_WRITABLE
251 static void
252 set_all_unchanged(void)
253 {
254         int i;
255
256         for (i = 0; i < MAXIMUM_PARTS; i++)
257                 ptes[i].changed = 0;
258 }
259
260 static ATTRIBUTE_ALWAYS_INLINE void
261 set_changed(int i)
262 {
263         ptes[i].changed = 1;
264 }
265 #endif /* FEATURE_FDISK_WRITABLE */
266
267 static ATTRIBUTE_ALWAYS_INLINE struct partition *
268 get_part_table(int i)
269 {
270         return ptes[i].part_table;
271 }
272
273 static const char *
274 str_units(int n)
275 {      /* n==1: use singular */
276         if (n == 1)
277                 return display_in_cyl_units ? "cylinder" : "sector";
278         return display_in_cyl_units ? "cylinders" : "sectors";
279 }
280
281 static int
282 valid_part_table_flag(const char *mbuffer)
283 {
284         return (mbuffer[510] == 0x55 && (uint8_t)mbuffer[511] == 0xaa);
285 }
286
287 #if ENABLE_FEATURE_FDISK_WRITABLE
288 static ATTRIBUTE_ALWAYS_INLINE void
289 write_part_table_flag(char *b)
290 {
291         b[510] = 0x55;
292         b[511] = 0xaa;
293 }
294
295 static char line_buffer[LINE_LENGTH];
296 static char *line_ptr;
297
298 /* read line; return 0 or first printable char */
299 static int
300 read_line(const char *prompt)
301 {
302         int sz;
303
304         sz = read_line_input(prompt, line_buffer, sizeof(line_buffer), NULL);
305         if (sz <= 0)
306                 exit(0); /* Ctrl-D or Ctrl-C */
307
308         if (line_buffer[sz-1] == '\n')
309                 line_buffer[--sz] = '\0';
310
311         line_ptr = line_buffer;
312         while (*line_ptr && !isgraph(*line_ptr))
313                 line_ptr++;
314         return *line_ptr;
315 }
316
317 static char
318 read_nonempty(const char *mesg)
319 {
320         while (!read_line(mesg)) /* repeat */;
321         return *line_ptr;
322 }
323
324 static char
325 read_maybe_empty(const char *mesg)
326 {
327         if (!read_line(mesg)) {
328                 line_ptr = line_buffer;
329                 line_ptr[0] = '\n';
330                 line_ptr[1] = '\0';
331         }
332         return line_ptr[0];
333 }
334
335 static int
336 read_hex(const char *const *sys)
337 {
338         unsigned long v;
339         while (1) {
340                 read_nonempty("Hex code (type L to list codes): ");
341                 if (*line_ptr == 'l' || *line_ptr == 'L') {
342                         list_types(sys);
343                         continue;
344                 }
345                 v = bb_strtoul(line_ptr, NULL, 16);
346                 if (v > 0xff)
347                         /* Bad input also triggers this */
348                         continue;
349                 return v;
350         }
351 }
352 #endif /* FEATURE_FDISK_WRITABLE */
353
354 #include "fdisk_aix.c"
355
356 typedef struct {
357         unsigned char info[128];   /* Informative text string */
358         unsigned char spare0[14];
359         struct sun_info {
360                 unsigned char spare1;
361                 unsigned char id;
362                 unsigned char spare2;
363                 unsigned char flags;
364         } infos[8];
365         unsigned char spare1[246]; /* Boot information etc. */
366         unsigned short rspeed;     /* Disk rotational speed */
367         unsigned short pcylcount;  /* Physical cylinder count */
368         unsigned short sparecyl;   /* extra sects per cylinder */
369         unsigned char spare2[4];   /* More magic... */
370         unsigned short ilfact;     /* Interleave factor */
371         unsigned short ncyl;       /* Data cylinder count */
372         unsigned short nacyl;      /* Alt. cylinder count */
373         unsigned short ntrks;      /* Tracks per cylinder */
374         unsigned short nsect;      /* Sectors per track */
375         unsigned char spare3[4];   /* Even more magic... */
376         struct sun_partinfo {
377                 uint32_t start_cylinder;
378                 uint32_t num_sectors;
379         } partitions[8];
380         unsigned short magic;      /* Magic number */
381         unsigned short csum;       /* Label xor'd checksum */
382 } sun_partition;
383 #define sunlabel ((sun_partition *)MBRbuffer)
384 STATIC_OSF void bsd_select(void);
385 STATIC_OSF void xbsd_print_disklabel(int);
386 #include "fdisk_osf.c"
387
388 #if ENABLE_FEATURE_SGI_LABEL || ENABLE_FEATURE_SUN_LABEL
389 static uint16_t
390 fdisk_swap16(uint16_t x)
391 {
392         return (x << 8) | (x >> 8);
393 }
394
395 static uint32_t
396 fdisk_swap32(uint32_t x)
397 {
398         return (x << 24) |
399                ((x & 0xFF00) << 8) |
400                ((x & 0xFF0000) >> 8) |
401                (x >> 24);
402 }
403 #endif
404
405 STATIC_SGI const char *const sgi_sys_types[];
406 STATIC_SGI unsigned sgi_get_num_sectors(int i);
407 STATIC_SGI int sgi_get_sysid(int i);
408 STATIC_SGI void sgi_delete_partition(int i);
409 STATIC_SGI void sgi_change_sysid(int i, int sys);
410 STATIC_SGI void sgi_list_table(int xtra);
411 #if ENABLE_FEATURE_FDISK_ADVANCED
412 STATIC_SGI void sgi_set_xcyl(void);
413 #endif
414 STATIC_SGI int verify_sgi(int verbose);
415 STATIC_SGI void sgi_add_partition(int n, int sys);
416 STATIC_SGI void sgi_set_swappartition(int i);
417 STATIC_SGI const char *sgi_get_bootfile(void);
418 STATIC_SGI void sgi_set_bootfile(const char* aFile);
419 STATIC_SGI void create_sgiinfo(void);
420 STATIC_SGI void sgi_write_table(void);
421 STATIC_SGI void sgi_set_bootpartition(int i);
422 #include "fdisk_sgi.c"
423
424 STATIC_SUN const char *const sun_sys_types[];
425 STATIC_SUN void sun_delete_partition(int i);
426 STATIC_SUN void sun_change_sysid(int i, int sys);
427 STATIC_SUN void sun_list_table(int xtra);
428 STATIC_SUN void add_sun_partition(int n, int sys);
429 #if ENABLE_FEATURE_FDISK_ADVANCED
430 STATIC_SUN void sun_set_alt_cyl(void);
431 STATIC_SUN void sun_set_ncyl(int cyl);
432 STATIC_SUN void sun_set_xcyl(void);
433 STATIC_SUN void sun_set_ilfact(void);
434 STATIC_SUN void sun_set_rspeed(void);
435 STATIC_SUN void sun_set_pcylcount(void);
436 #endif
437 STATIC_SUN void toggle_sunflags(int i, unsigned char mask);
438 STATIC_SUN void verify_sun(void);
439 STATIC_SUN void sun_write_table(void);
440 #include "fdisk_sun.c"
441
442 /* DOS partition types */
443
444 static const char *const i386_sys_types[] = {
445         "\x00" "Empty",
446         "\x01" "FAT12",
447         "\x04" "FAT16 <32M",
448         "\x05" "Extended",         /* DOS 3.3+ extended partition */
449         "\x06" "FAT16",            /* DOS 16-bit >=32M */
450         "\x07" "HPFS/NTFS",        /* OS/2 IFS, eg, HPFS or NTFS or QNX */
451         "\x0a" "OS/2 Boot Manager",/* OS/2 Boot Manager */
452         "\x0b" "Win95 FAT32",
453         "\x0c" "Win95 FAT32 (LBA)",/* LBA really is 'Extended Int 13h' */
454         "\x0e" "Win95 FAT16 (LBA)",
455         "\x0f" "Win95 Ext'd (LBA)",
456         "\x11" "Hidden FAT12",
457         "\x12" "Compaq diagnostics",
458         "\x14" "Hidden FAT16 <32M",
459         "\x16" "Hidden FAT16",
460         "\x17" "Hidden HPFS/NTFS",
461         "\x1b" "Hidden Win95 FAT32",
462         "\x1c" "Hidden W95 FAT32 (LBA)",
463         "\x1e" "Hidden W95 FAT16 (LBA)",
464         "\x3c" "Part.Magic recovery",
465         "\x41" "PPC PReP Boot",
466         "\x42" "SFS",
467         "\x63" "GNU HURD or SysV", /* GNU HURD or Mach or Sys V/386 (such as ISC UNIX) */
468         "\x80" "Old Minix",        /* Minix 1.4a and earlier */
469         "\x81" "Minix / old Linux",/* Minix 1.4b and later */
470         "\x82" "Linux swap",       /* also Solaris */
471         "\x83" "Linux",
472         "\x84" "OS/2 hidden C: drive",
473         "\x85" "Linux extended",
474         "\x86" "NTFS volume set",
475         "\x87" "NTFS volume set",
476         "\x8e" "Linux LVM",
477         "\x9f" "BSD/OS",           /* BSDI */
478         "\xa0" "Thinkpad hibernation",
479         "\xa5" "FreeBSD",          /* various BSD flavours */
480         "\xa6" "OpenBSD",
481         "\xa8" "Darwin UFS",
482         "\xa9" "NetBSD",
483         "\xab" "Darwin boot",
484         "\xb7" "BSDI fs",
485         "\xb8" "BSDI swap",
486         "\xbe" "Solaris boot",
487         "\xeb" "BeOS fs",
488         "\xee" "EFI GPT",                    /* Intel EFI GUID Partition Table */
489         "\xef" "EFI (FAT-12/16/32)",         /* Intel EFI System Partition */
490         "\xf0" "Linux/PA-RISC boot",         /* Linux/PA-RISC boot loader */
491         "\xf2" "DOS secondary",              /* DOS 3.3+ secondary */
492         "\xfd" "Linux raid autodetect",      /* New (2.2.x) raid partition with
493                                                 autodetect using persistent
494                                                 superblock */
495 #if 0 /* ENABLE_WEIRD_PARTITION_TYPES */
496         "\x02" "XENIX root",
497         "\x03" "XENIX usr",
498         "\x08" "AIX",              /* AIX boot (AIX -- PS/2 port) or SplitDrive */
499         "\x09" "AIX bootable",     /* AIX data or Coherent */
500         "\x10" "OPUS",
501         "\x18" "AST SmartSleep",
502         "\x24" "NEC DOS",
503         "\x39" "Plan 9",
504         "\x40" "Venix 80286",
505         "\x4d" "QNX4.x",
506         "\x4e" "QNX4.x 2nd part",
507         "\x4f" "QNX4.x 3rd part",
508         "\x50" "OnTrack DM",
509         "\x51" "OnTrack DM6 Aux1", /* (or Novell) */
510         "\x52" "CP/M",             /* CP/M or Microport SysV/AT */
511         "\x53" "OnTrack DM6 Aux3",
512         "\x54" "OnTrackDM6",
513         "\x55" "EZ-Drive",
514         "\x56" "Golden Bow",
515         "\x5c" "Priam Edisk",
516         "\x61" "SpeedStor",
517         "\x64" "Novell Netware 286",
518         "\x65" "Novell Netware 386",
519         "\x70" "DiskSecure Multi-Boot",
520         "\x75" "PC/IX",
521         "\x93" "Amoeba",
522         "\x94" "Amoeba BBT",       /* (bad block table) */
523         "\xa7" "NeXTSTEP",
524         "\xbb" "Boot Wizard hidden",
525         "\xc1" "DRDOS/sec (FAT-12)",
526         "\xc4" "DRDOS/sec (FAT-16 < 32M)",
527         "\xc6" "DRDOS/sec (FAT-16)",
528         "\xc7" "Syrinx",
529         "\xda" "Non-FS data",
530         "\xdb" "CP/M / CTOS / ...",/* CP/M or Concurrent CP/M or
531                                       Concurrent DOS or CTOS */
532         "\xde" "Dell Utility",     /* Dell PowerEdge Server utilities */
533         "\xdf" "BootIt",           /* BootIt EMBRM */
534         "\xe1" "DOS access",       /* DOS access or SpeedStor 12-bit FAT
535                                       extended partition */
536         "\xe3" "DOS R/O",          /* DOS R/O or SpeedStor */
537         "\xe4" "SpeedStor",        /* SpeedStor 16-bit FAT extended
538                                       partition < 1024 cyl. */
539         "\xf1" "SpeedStor",
540         "\xf4" "SpeedStor",        /* SpeedStor large partition */
541         "\xfe" "LANstep",          /* SpeedStor >1024 cyl. or LANstep */
542         "\xff" "BBT",              /* Xenix Bad Block Table */
543 #endif
544         NULL
545 };
546
547
548 #if ENABLE_FEATURE_FDISK_WRITABLE
549 /* start_sect and nr_sects are stored little endian on all machines */
550 /* moreover, they are not aligned correctly */
551 static void
552 store4_little_endian(unsigned char *cp, unsigned val)
553 {
554         cp[0] = val;
555         cp[1] = val >> 8;
556         cp[2] = val >> 16;
557         cp[3] = val >> 24;
558 }
559 #endif /* FEATURE_FDISK_WRITABLE */
560
561 static unsigned
562 read4_little_endian(const unsigned char *cp)
563 {
564         return cp[0] + (cp[1] << 8) + (cp[2] << 16) + (cp[3] << 24);
565 }
566
567 #if ENABLE_FEATURE_FDISK_WRITABLE
568 static void
569 set_start_sect(struct partition *p, unsigned start_sect)
570 {
571         store4_little_endian(p->start4, start_sect);
572 }
573 #endif
574
575 static unsigned
576 get_start_sect(const struct partition *p)
577 {
578         return read4_little_endian(p->start4);
579 }
580
581 #if ENABLE_FEATURE_FDISK_WRITABLE
582 static void
583 set_nr_sects(struct partition *p, unsigned nr_sects)
584 {
585         store4_little_endian(p->size4, nr_sects);
586 }
587 #endif
588
589 static unsigned
590 get_nr_sects(const struct partition *p)
591 {
592         return read4_little_endian(p->size4);
593 }
594
595 /* normally O_RDWR, -l option gives O_RDONLY */
596 static int type_open = O_RDWR;
597
598
599 static int ext_index;               /* the prime extended partition */
600 static int listing;                    /* no aborts for fdisk -l */
601 static int dos_compatible_flag = ~0;
602 #if ENABLE_FEATURE_FDISK_WRITABLE
603 static int dos_changed;
604 static int nowarn;            /* no warnings for fdisk -l/-s */
605 #endif
606
607
608
609 static unsigned user_cylinders, user_heads, user_sectors;
610 static unsigned pt_heads, pt_sectors;
611 static unsigned kern_heads, kern_sectors;
612
613 static off_t extended_offset;            /* offset of link pointers */
614
615 static unsigned long long total_number_of_sectors;
616
617
618 static jmp_buf listingbuf;
619
620 static void fdisk_fatal(enum failure why)
621 {
622         const char *message;
623
624         if (listing) {
625                 close(fd);
626                 longjmp(listingbuf, 1);
627         }
628
629         switch (why) {
630         case unable_to_open:
631                 message = "cannot open %s";
632                 break;
633         case unable_to_read:
634                 message = "cannot read from %s";
635                 break;
636         case unable_to_seek:
637                 message = "cannot seek on %s";
638                 break;
639         case unable_to_write:
640                 message = "cannot write to %s";
641                 break;
642         case ioctl_error:
643                 message = "BLKGETSIZE ioctl failed on %s";
644                 break;
645         default:
646                 message = "fatal error";
647         }
648
649         bb_error_msg_and_die(message, disk_device);
650 }
651
652 static void
653 seek_sector(off_t secno)
654 {
655         off_t offset = secno * sector_size;
656         if (lseek(fd, offset, SEEK_SET) == (off_t) -1)
657                 fdisk_fatal(unable_to_seek);
658 }
659
660 #if ENABLE_FEATURE_FDISK_WRITABLE
661 static void
662 write_sector(off_t secno, char *buf)
663 {
664         seek_sector(secno);
665         if (write(fd, buf, sector_size) != sector_size)
666                 fdisk_fatal(unable_to_write);
667 }
668 #endif
669
670 /* Allocate a buffer and read a partition table sector */
671 static void
672 read_pte(struct pte *pe, off_t offset)
673 {
674         pe->offset = offset;
675         pe->sectorbuffer = xmalloc(sector_size);
676         seek_sector(offset);
677         if (read(fd, pe->sectorbuffer, sector_size) != sector_size)
678                 fdisk_fatal(unable_to_read);
679 #if ENABLE_FEATURE_FDISK_WRITABLE
680         pe->changed = 0;
681 #endif
682         pe->part_table = pe->ext_pointer = NULL;
683 }
684
685 static unsigned
686 get_partition_start(const struct pte *pe)
687 {
688         return pe->offset + get_start_sect(pe->part_table);
689 }
690
691 #if ENABLE_FEATURE_FDISK_WRITABLE
692 /*
693  * Avoid warning about DOS partitions when no DOS partition was changed.
694  * Here a heuristic "is probably dos partition".
695  * We might also do the opposite and warn in all cases except
696  * for "is probably nondos partition".
697  */
698 static int
699 is_dos_partition(int t)
700 {
701         return (t == 1 || t == 4 || t == 6 ||
702                 t == 0x0b || t == 0x0c || t == 0x0e ||
703                 t == 0x11 || t == 0x12 || t == 0x14 || t == 0x16 ||
704                 t == 0x1b || t == 0x1c || t == 0x1e || t == 0x24 ||
705                 t == 0xc1 || t == 0xc4 || t == 0xc6);
706 }
707
708 static void
709 menu(void)
710 {
711         puts("Command Action");
712         if (LABEL_IS_SUN) {
713                 puts("a\ttoggle a read only flag");           /* sun */
714                 puts("b\tedit bsd disklabel");
715                 puts("c\ttoggle the mountable flag");         /* sun */
716                 puts("d\tdelete a partition");
717                 puts("l\tlist known partition types");
718                 puts("n\tadd a new partition");
719                 puts("o\tcreate a new empty DOS partition table");
720                 puts("p\tprint the partition table");
721                 puts("q\tquit without saving changes");
722                 puts("s\tcreate a new empty Sun disklabel");  /* sun */
723                 puts("t\tchange a partition's system id");
724                 puts("u\tchange display/entry units");
725                 puts("v\tverify the partition table");
726                 puts("w\twrite table to disk and exit");
727 #if ENABLE_FEATURE_FDISK_ADVANCED
728                 puts("x\textra functionality (experts only)");
729 #endif
730         } else if (LABEL_IS_SGI) {
731                 puts("a\tselect bootable partition");    /* sgi flavour */
732                 puts("b\tedit bootfile entry");          /* sgi */
733                 puts("c\tselect sgi swap partition");    /* sgi flavour */
734                 puts("d\tdelete a partition");
735                 puts("l\tlist known partition types");
736                 puts("n\tadd a new partition");
737                 puts("o\tcreate a new empty DOS partition table");
738                 puts("p\tprint the partition table");
739                 puts("q\tquit without saving changes");
740                 puts("s\tcreate a new empty Sun disklabel");  /* sun */
741                 puts("t\tchange a partition's system id");
742                 puts("u\tchange display/entry units");
743                 puts("v\tverify the partition table");
744                 puts("w\twrite table to disk and exit");
745         } else if (LABEL_IS_AIX) {
746                 puts("o\tcreate a new empty DOS partition table");
747                 puts("q\tquit without saving changes");
748                 puts("s\tcreate a new empty Sun disklabel");  /* sun */
749         } else {
750                 puts("a\ttoggle a bootable flag");
751                 puts("b\tedit bsd disklabel");
752                 puts("c\ttoggle the dos compatibility flag");
753                 puts("d\tdelete a partition");
754                 puts("l\tlist known partition types");
755                 puts("n\tadd a new partition");
756                 puts("o\tcreate a new empty DOS partition table");
757                 puts("p\tprint the partition table");
758                 puts("q\tquit without saving changes");
759                 puts("s\tcreate a new empty Sun disklabel");  /* sun */
760                 puts("t\tchange a partition's system id");
761                 puts("u\tchange display/entry units");
762                 puts("v\tverify the partition table");
763                 puts("w\twrite table to disk and exit");
764 #if ENABLE_FEATURE_FDISK_ADVANCED
765                 puts("x\textra functionality (experts only)");
766 #endif
767         }
768 }
769 #endif /* FEATURE_FDISK_WRITABLE */
770
771
772 #if ENABLE_FEATURE_FDISK_ADVANCED
773 static void
774 xmenu(void)
775 {
776         puts("Command Action");
777         if (LABEL_IS_SUN) {
778                 puts("a\tchange number of alternate cylinders");      /*sun*/
779                 puts("c\tchange number of cylinders");
780                 puts("d\tprint the raw data in the partition table");
781                 puts("e\tchange number of extra sectors per cylinder");/*sun*/
782                 puts("h\tchange number of heads");
783                 puts("i\tchange interleave factor");                  /*sun*/
784                 puts("o\tchange rotation speed (rpm)");               /*sun*/
785                 puts("p\tprint the partition table");
786                 puts("q\tquit without saving changes");
787                 puts("r\treturn to main menu");
788                 puts("s\tchange number of sectors/track");
789                 puts("v\tverify the partition table");
790                 puts("w\twrite table to disk and exit");
791                 puts("y\tchange number of physical cylinders");       /*sun*/
792         } else if (LABEL_IS_SGI) {
793                 puts("b\tmove beginning of data in a partition"); /* !sun */
794                 puts("c\tchange number of cylinders");
795                 puts("d\tprint the raw data in the partition table");
796                 puts("e\tlist extended partitions");          /* !sun */
797                 puts("g\tcreate an IRIX (SGI) partition table");/* sgi */
798                 puts("h\tchange number of heads");
799                 puts("p\tprint the partition table");
800                 puts("q\tquit without saving changes");
801                 puts("r\treturn to main menu");
802                 puts("s\tchange number of sectors/track");
803                 puts("v\tverify the partition table");
804                 puts("w\twrite table to disk and exit");
805         } else if (LABEL_IS_AIX) {
806                 puts("b\tmove beginning of data in a partition"); /* !sun */
807                 puts("c\tchange number of cylinders");
808                 puts("d\tprint the raw data in the partition table");
809                 puts("e\tlist extended partitions");          /* !sun */
810                 puts("g\tcreate an IRIX (SGI) partition table");/* sgi */
811                 puts("h\tchange number of heads");
812                 puts("p\tprint the partition table");
813                 puts("q\tquit without saving changes");
814                 puts("r\treturn to main menu");
815                 puts("s\tchange number of sectors/track");
816                 puts("v\tverify the partition table");
817                 puts("w\twrite table to disk and exit");
818         } else {
819                 puts("b\tmove beginning of data in a partition"); /* !sun */
820                 puts("c\tchange number of cylinders");
821                 puts("d\tprint the raw data in the partition table");
822                 puts("e\tlist extended partitions");          /* !sun */
823                 puts("f\tfix partition order");               /* !sun, !aix, !sgi */
824 #if ENABLE_FEATURE_SGI_LABEL
825                 puts("g\tcreate an IRIX (SGI) partition table");/* sgi */
826 #endif
827                 puts("h\tchange number of heads");
828                 puts("p\tprint the partition table");
829                 puts("q\tquit without saving changes");
830                 puts("r\treturn to main menu");
831                 puts("s\tchange number of sectors/track");
832                 puts("v\tverify the partition table");
833                 puts("w\twrite table to disk and exit");
834         }
835 }
836 #endif /* ADVANCED mode */
837
838 #if ENABLE_FEATURE_FDISK_WRITABLE
839 static const char *const *
840 get_sys_types(void)
841 {
842         return (
843                 LABEL_IS_SUN ? sun_sys_types :
844                 LABEL_IS_SGI ? sgi_sys_types :
845                 i386_sys_types);
846 }
847 #else
848 #define get_sys_types() i386_sys_types
849 #endif /* FEATURE_FDISK_WRITABLE */
850
851 static const char *
852 partition_type(unsigned char type)
853 {
854         int i;
855         const char *const *types = get_sys_types();
856
857         for (i = 0; types[i]; i++)
858                 if ((unsigned char)types[i][0] == type)
859                         return types[i] + 1;
860
861         return "Unknown";
862 }
863
864
865 #if ENABLE_FEATURE_FDISK_WRITABLE
866 static int
867 get_sysid(int i)
868 {
869         return LABEL_IS_SUN ? sunlabel->infos[i].id :
870                         (LABEL_IS_SGI ? sgi_get_sysid(i) :
871                                 ptes[i].part_table->sys_ind);
872 }
873
874 static void
875 list_types(const char *const *sys)
876 {
877         enum { COLS = 3 };
878
879         unsigned last[COLS];
880         unsigned done, next, size;
881         int i;
882
883         for (size = 0; sys[size]; size++) /* */;
884
885         done = 0;
886         for (i = COLS-1; i >= 0; i--) {
887                 done += (size + i - done) / (i + 1);
888                 last[COLS-1 - i] = done;
889         }
890
891         i = done = next = 0;
892         do {
893                 printf("%c%2x %-22.22s", i ? ' ' : '\n',
894                         (unsigned char)sys[next][0],
895                         sys[next] + 1);
896                 next = last[i++] + done;
897                 if (i >= COLS || next >= last[i]) {
898                         i = 0;
899                         next = ++done;
900                 }
901         } while (done < last[0]);
902         putchar('\n');
903 }
904 #endif /* FEATURE_FDISK_WRITABLE */
905
906 static int
907 is_cleared_partition(const struct partition *p)
908 {
909         return !(!p || p->boot_ind || p->head || p->sector || p->cyl ||
910                  p->sys_ind || p->end_head || p->end_sector || p->end_cyl ||
911                  get_start_sect(p) || get_nr_sects(p));
912 }
913
914 static void
915 clear_partition(struct partition *p)
916 {
917         if (!p)
918                 return;
919         memset(p, 0, sizeof(struct partition));
920 }
921
922 #if ENABLE_FEATURE_FDISK_WRITABLE
923 static void
924 set_partition(int i, int doext, off_t start, off_t stop, int sysid)
925 {
926         struct partition *p;
927         off_t offset;
928
929         if (doext) {
930                 p = ptes[i].ext_pointer;
931                 offset = extended_offset;
932         } else {
933                 p = ptes[i].part_table;
934                 offset = ptes[i].offset;
935         }
936         p->boot_ind = 0;
937         p->sys_ind = sysid;
938         set_start_sect(p, start - offset);
939         set_nr_sects(p, stop - start + 1);
940         if (dos_compatible_flag && (start/(sectors*heads) > 1023))
941                 start = heads*sectors*1024 - 1;
942         set_hsc(p->head, p->sector, p->cyl, start);
943         if (dos_compatible_flag && (stop/(sectors*heads) > 1023))
944                 stop = heads*sectors*1024 - 1;
945         set_hsc(p->end_head, p->end_sector, p->end_cyl, stop);
946         ptes[i].changed = 1;
947 }
948 #endif
949
950 static int
951 warn_geometry(void)
952 {
953         if (heads && sectors && cylinders)
954                 return 0;
955
956         printf("Unknown value(s) for:");
957         if (!heads)
958                 printf(" heads");
959         if (!sectors)
960                 printf(" sectors");
961         if (!cylinders)
962                 printf(" cylinders");
963         printf(
964 #if ENABLE_FEATURE_FDISK_WRITABLE
965                 " (settable in the extra functions menu)"
966 #endif
967                 "\n");
968         return 1;
969 }
970
971 static void
972 update_units(void)
973 {
974         int cyl_units = heads * sectors;
975
976         if (display_in_cyl_units && cyl_units)
977                 units_per_sector = cyl_units;
978         else
979                 units_per_sector = 1;   /* in sectors */
980 }
981
982 #if ENABLE_FEATURE_FDISK_WRITABLE
983 static void
984 warn_cylinders(void)
985 {
986         if (LABEL_IS_DOS && cylinders > 1024 && !nowarn)
987                 printf("\n"
988 "The number of cylinders for this disk is set to %d.\n"
989 "There is nothing wrong with that, but this is larger than 1024,\n"
990 "and could in certain setups cause problems with:\n"
991 "1) software that runs at boot time (e.g., old versions of LILO)\n"
992 "2) booting and partitioning software from other OSs\n"
993 "   (e.g., DOS FDISK, OS/2 FDISK)\n",
994                         cylinders);
995 }
996 #endif
997
998 static void
999 read_extended(int ext)
1000 {
1001         int i;
1002         struct pte *pex;
1003         struct partition *p, *q;
1004
1005         ext_index = ext;
1006         pex = &ptes[ext];
1007         pex->ext_pointer = pex->part_table;
1008
1009         p = pex->part_table;
1010         if (!get_start_sect(p)) {
1011                 printf("Bad offset in primary extended partition\n");
1012                 return;
1013         }
1014
1015         while (IS_EXTENDED(p->sys_ind)) {
1016                 struct pte *pe = &ptes[partitions];
1017
1018                 if (partitions >= MAXIMUM_PARTS) {
1019                         /* This is not a Linux restriction, but
1020                            this program uses arrays of size MAXIMUM_PARTS.
1021                            Do not try to 'improve' this test. */
1022                         struct pte *pre = &ptes[partitions-1];
1023 #if ENABLE_FEATURE_FDISK_WRITABLE
1024                         printf("Warning: deleting partitions after %d\n",
1025                                 partitions);
1026                         pre->changed = 1;
1027 #endif
1028                         clear_partition(pre->ext_pointer);
1029                         return;
1030                 }
1031
1032                 read_pte(pe, extended_offset + get_start_sect(p));
1033
1034                 if (!extended_offset)
1035                         extended_offset = get_start_sect(p);
1036
1037                 q = p = pt_offset(pe->sectorbuffer, 0);
1038                 for (i = 0; i < 4; i++, p++) if (get_nr_sects(p)) {
1039                         if (IS_EXTENDED(p->sys_ind)) {
1040                                 if (pe->ext_pointer)
1041                                         printf("Warning: extra link "
1042                                                 "pointer in partition table"
1043                                                 " %d\n", partitions + 1);
1044                                 else
1045                                         pe->ext_pointer = p;
1046                         } else if (p->sys_ind) {
1047                                 if (pe->part_table)
1048                                         printf("Warning: ignoring extra "
1049                                                   "data in partition table"
1050                                                   " %d\n", partitions + 1);
1051                                 else
1052                                         pe->part_table = p;
1053                         }
1054                 }
1055
1056                 /* very strange code here... */
1057                 if (!pe->part_table) {
1058                         if (q != pe->ext_pointer)
1059                                 pe->part_table = q;
1060                         else
1061                                 pe->part_table = q + 1;
1062                 }
1063                 if (!pe->ext_pointer) {
1064                         if (q != pe->part_table)
1065                                 pe->ext_pointer = q;
1066                         else
1067                                 pe->ext_pointer = q + 1;
1068                 }
1069
1070                 p = pe->ext_pointer;
1071                 partitions++;
1072         }
1073
1074 #if ENABLE_FEATURE_FDISK_WRITABLE
1075         /* remove empty links */
1076  remove:
1077         for (i = 4; i < partitions; i++) {
1078                 struct pte *pe = &ptes[i];
1079
1080                 if (!get_nr_sects(pe->part_table)
1081                  && (partitions > 5 || ptes[4].part_table->sys_ind)
1082                 ) {
1083                         printf("Omitting empty partition (%d)\n", i+1);
1084                         delete_partition(i);
1085                         goto remove;    /* numbering changed */
1086                 }
1087         }
1088 #endif
1089 }
1090
1091 #if ENABLE_FEATURE_FDISK_WRITABLE
1092 static void
1093 create_doslabel(void)
1094 {
1095         int i;
1096
1097         printf(msg_building_new_label, "DOS disklabel");
1098
1099         current_label_type = label_dos;
1100
1101 #if ENABLE_FEATURE_OSF_LABEL
1102         possibly_osf_label = 0;
1103 #endif
1104         partitions = 4;
1105
1106         for (i = 510-64; i < 510; i++)
1107                 MBRbuffer[i] = 0;
1108         write_part_table_flag(MBRbuffer);
1109         extended_offset = 0;
1110         set_all_unchanged();
1111         set_changed(0);
1112         get_boot(create_empty_dos);
1113 }
1114 #endif /* FEATURE_FDISK_WRITABLE */
1115
1116 static void
1117 get_sectorsize(void)
1118 {
1119         if (!user_set_sector_size) {
1120                 int arg;
1121                 if (ioctl(fd, BLKSSZGET, &arg) == 0)
1122                         sector_size = arg;
1123                 if (sector_size != DEFAULT_SECTOR_SIZE)
1124                         printf("Note: sector size is %d (not %d)\n",
1125                                    sector_size, DEFAULT_SECTOR_SIZE);
1126         }
1127 }
1128
1129 static void
1130 get_kernel_geometry(void)
1131 {
1132         struct hd_geometry geometry;
1133
1134         if (!ioctl(fd, HDIO_GETGEO, &geometry)) {
1135                 kern_heads = geometry.heads;
1136                 kern_sectors = geometry.sectors;
1137                 /* never use geometry.cylinders - it is truncated */
1138         }
1139 }
1140
1141 static void
1142 get_partition_table_geometry(void)
1143 {
1144         const unsigned char *bufp = (const unsigned char *)MBRbuffer;
1145         struct partition *p;
1146         int i, h, s, hh, ss;
1147         int first = 1;
1148         int bad = 0;
1149
1150         if (!(valid_part_table_flag((char*)bufp)))
1151                 return;
1152
1153         hh = ss = 0;
1154         for (i = 0; i < 4; i++) {
1155                 p = pt_offset(bufp, i);
1156                 if (p->sys_ind != 0) {
1157                         h = p->end_head + 1;
1158                         s = (p->end_sector & 077);
1159                         if (first) {
1160                                 hh = h;
1161                                 ss = s;
1162                                 first = 0;
1163                         } else if (hh != h || ss != s)
1164                                 bad = 1;
1165                 }
1166         }
1167
1168         if (!first && !bad) {
1169                 pt_heads = hh;
1170                 pt_sectors = ss;
1171         }
1172 }
1173
1174 static void
1175 get_geometry(void)
1176 {
1177         int sec_fac;
1178         unsigned long long bytes;       /* really u64 */
1179
1180         get_sectorsize();
1181         sec_fac = sector_size / 512;
1182 #if ENABLE_FEATURE_SUN_LABEL
1183         guess_device_type();
1184 #endif
1185         heads = cylinders = sectors = 0;
1186         kern_heads = kern_sectors = 0;
1187         pt_heads = pt_sectors = 0;
1188
1189         get_kernel_geometry();
1190         get_partition_table_geometry();
1191
1192         heads = user_heads ? user_heads :
1193                 pt_heads ? pt_heads :
1194                 kern_heads ? kern_heads : 255;
1195         sectors = user_sectors ? user_sectors :
1196                 pt_sectors ? pt_sectors :
1197                 kern_sectors ? kern_sectors : 63;
1198         if (ioctl(fd, BLKGETSIZE64, &bytes) == 0) {
1199                 /* got bytes */
1200         } else {
1201                 unsigned long longsectors;
1202
1203         if (ioctl(fd, BLKGETSIZE, &longsectors))
1204                 longsectors = 0;
1205                 bytes = ((unsigned long long) longsectors) << 9;
1206         }
1207
1208         total_number_of_sectors = (bytes >> 9);
1209
1210         sector_offset = 1;
1211         if (dos_compatible_flag)
1212                 sector_offset = sectors;
1213
1214         cylinders = total_number_of_sectors / (heads * sectors * sec_fac);
1215         if (!cylinders)
1216                 cylinders = user_cylinders;
1217 }
1218
1219 /*
1220  * Read MBR.  Returns:
1221  *   -1: no 0xaa55 flag present (possibly entire disk BSD)
1222  *    0: found or created label
1223  *    1: I/O error
1224  */
1225 static int
1226 get_boot(enum action what)
1227 {
1228         int i;
1229
1230         partitions = 4;
1231
1232         for (i = 0; i < 4; i++) {
1233                 struct pte *pe = &ptes[i];
1234
1235                 pe->part_table = pt_offset(MBRbuffer, i);
1236                 pe->ext_pointer = NULL;
1237                 pe->offset = 0;
1238                 pe->sectorbuffer = MBRbuffer;
1239 #if ENABLE_FEATURE_FDISK_WRITABLE
1240                 pe->changed = (what == create_empty_dos);
1241 #endif
1242         }
1243
1244 #if ENABLE_FEATURE_SUN_LABEL
1245         if (what == create_empty_sun && check_sun_label())
1246                 return 0;
1247 #endif
1248
1249         memset(MBRbuffer, 0, 512);
1250
1251 #if ENABLE_FEATURE_FDISK_WRITABLE
1252         if (what == create_empty_dos)
1253                 goto got_dos_table;             /* skip reading disk */
1254
1255         fd = open(disk_device, type_open);
1256         if (fd < 0) {
1257                 fd = open(disk_device, O_RDONLY);
1258                 if (fd < 0) {
1259                         if (what == try_only)
1260                                 return 1;
1261                         fdisk_fatal(unable_to_open);
1262                 } else
1263                         printf("You will not be able to write "
1264                                 "the partition table\n");
1265         }
1266
1267         if (512 != read(fd, MBRbuffer, 512)) {
1268                 if (what == try_only)
1269                         return 1;
1270                 fdisk_fatal(unable_to_read);
1271         }
1272 #else
1273         fd = open(disk_device, O_RDONLY);
1274         if (fd < 0)
1275                 return 1;
1276         if (512 != read(fd, MBRbuffer, 512))
1277                 return 1;
1278 #endif
1279
1280         get_geometry();
1281
1282         update_units();
1283
1284 #if ENABLE_FEATURE_SUN_LABEL
1285         if (check_sun_label())
1286                 return 0;
1287 #endif
1288
1289 #if ENABLE_FEATURE_SGI_LABEL
1290         if (check_sgi_label())
1291                 return 0;
1292 #endif
1293
1294 #if ENABLE_FEATURE_AIX_LABEL
1295         if (check_aix_label())
1296                 return 0;
1297 #endif
1298
1299 #if ENABLE_FEATURE_OSF_LABEL
1300         if (check_osf_label()) {
1301                 possibly_osf_label = 1;
1302                 if (!valid_part_table_flag(MBRbuffer)) {
1303                         current_label_type = label_osf;
1304                         return 0;
1305                 }
1306                 printf("This disk has both DOS and BSD magic.\n"
1307                          "Give the 'b' command to go to BSD mode.\n");
1308         }
1309 #endif
1310
1311 #if ENABLE_FEATURE_FDISK_WRITABLE
1312  got_dos_table:
1313 #endif
1314
1315         if (!valid_part_table_flag(MBRbuffer)) {
1316 #if !ENABLE_FEATURE_FDISK_WRITABLE
1317                 return -1;
1318 #else
1319                 switch (what) {
1320                 case fdisk:
1321                         printf("Device contains neither a valid DOS "
1322                                   "partition table, nor Sun, SGI or OSF "
1323                                   "disklabel\n");
1324 #ifdef __sparc__
1325 #if ENABLE_FEATURE_SUN_LABEL
1326                         create_sunlabel();
1327 #endif
1328 #else
1329                         create_doslabel();
1330 #endif
1331                         return 0;
1332                 case try_only:
1333                         return -1;
1334                 case create_empty_dos:
1335 #if ENABLE_FEATURE_SUN_LABEL
1336                 case create_empty_sun:
1337 #endif
1338                         break;
1339                 default:
1340                         bb_error_msg_and_die("internal error");
1341                 }
1342 #endif /* FEATURE_FDISK_WRITABLE */
1343         }
1344
1345 #if ENABLE_FEATURE_FDISK_WRITABLE
1346         warn_cylinders();
1347 #endif
1348         warn_geometry();
1349
1350         for (i = 0; i < 4; i++) {
1351                 struct pte *pe = &ptes[i];
1352
1353                 if (IS_EXTENDED(pe->part_table->sys_ind)) {
1354                         if (partitions != 4)
1355                                 printf("Ignoring extra extended "
1356                                         "partition %d\n", i + 1);
1357                         else
1358                                 read_extended(i);
1359                 }
1360         }
1361
1362         for (i = 3; i < partitions; i++) {
1363                 struct pte *pe = &ptes[i];
1364
1365                 if (!valid_part_table_flag(pe->sectorbuffer)) {
1366                         printf("Warning: invalid flag 0x%02x,0x%02x of partition "
1367                                 "table %d will be corrected by w(rite)\n",
1368                                 pe->sectorbuffer[510],
1369                                 pe->sectorbuffer[511],
1370                                 i + 1);
1371 #if ENABLE_FEATURE_FDISK_WRITABLE
1372                         pe->changed = 1;
1373 #endif
1374                 }
1375         }
1376
1377         return 0;
1378 }
1379
1380 #if ENABLE_FEATURE_FDISK_WRITABLE
1381 /*
1382  * Print the message MESG, then read an integer between LOW and HIGH (inclusive).
1383  * If the user hits Enter, DFLT is returned.
1384  * Answers like +10 are interpreted as offsets from BASE.
1385  *
1386  * There is no default if DFLT is not between LOW and HIGH.
1387  */
1388 static unsigned
1389 read_int(unsigned low, unsigned dflt, unsigned high, unsigned base, const char *mesg)
1390 {
1391         unsigned i;
1392         int default_ok = 1;
1393         const char *fmt = "%s (%u-%u, default %u): ";
1394
1395         if (dflt < low || dflt > high) {
1396                 fmt = "%s (%u-%u): ";
1397                 default_ok = 0;
1398         }
1399
1400         while (1) {
1401                 int use_default = default_ok;
1402
1403                 /* ask question and read answer */
1404                 do {
1405                         printf(fmt, mesg, low, high, dflt);
1406                         read_maybe_empty("");
1407                 } while (*line_ptr != '\n' && !isdigit(*line_ptr)
1408                  && *line_ptr != '-' && *line_ptr != '+');
1409
1410                 if (*line_ptr == '+' || *line_ptr == '-') {
1411                         int minus = (*line_ptr == '-');
1412                         int absolute = 0;
1413
1414                         i = atoi(line_ptr + 1);
1415
1416                         while (isdigit(*++line_ptr))
1417                                 use_default = 0;
1418
1419                         switch (*line_ptr) {
1420                         case 'c':
1421                         case 'C':
1422                                 if (!display_in_cyl_units)
1423                                         i *= heads * sectors;
1424                                 break;
1425                         case 'K':
1426                                 absolute = 1024;
1427                                 break;
1428                         case 'k':
1429                                 absolute = 1000;
1430                                 break;
1431                         case 'm':
1432                         case 'M':
1433                                 absolute = 1000000;
1434                                 break;
1435                         case 'g':
1436                         case 'G':
1437                                 absolute = 1000000000;
1438                                 break;
1439                         default:
1440                                 break;
1441                         }
1442                         if (absolute) {
1443                                 unsigned long long bytes;
1444                                 unsigned long unit;
1445
1446                                 bytes = (unsigned long long) i * absolute;
1447                                 unit = sector_size * units_per_sector;
1448                                 bytes += unit/2; /* round */
1449                                 bytes /= unit;
1450                                 i = bytes;
1451                         }
1452                         if (minus)
1453                                 i = -i;
1454                         i += base;
1455                 } else {
1456                         i = atoi(line_ptr);
1457                         while (isdigit(*line_ptr)) {
1458                                 line_ptr++;
1459                                 use_default = 0;
1460                         }
1461                 }
1462                 if (use_default) {
1463                         i = dflt;
1464                         printf("Using default value %u\n", i);
1465                 }
1466                 if (i >= low && i <= high)
1467                         break;
1468                 printf("Value is out of range\n");
1469         }
1470         return i;
1471 }
1472
1473 static int
1474 get_partition(int warn, int max)
1475 {
1476         struct pte *pe;
1477         int i;
1478
1479         i = read_int(1, 0, max, 0, "Partition number") - 1;
1480         pe = &ptes[i];
1481
1482         if (warn) {
1483                 if ((!LABEL_IS_SUN && !LABEL_IS_SGI && !pe->part_table->sys_ind)
1484                  || (LABEL_IS_SUN && (!sunlabel->partitions[i].num_sectors || !sunlabel->infos[i].id))
1485                  || (LABEL_IS_SGI && !sgi_get_num_sectors(i))
1486                 ) {
1487                         printf("Warning: partition %d has empty type\n", i+1);
1488                 }
1489         }
1490         return i;
1491 }
1492
1493 static int
1494 get_existing_partition(int warn, int max)
1495 {
1496         int pno = -1;
1497         int i;
1498
1499         for (i = 0; i < max; i++) {
1500                 struct pte *pe = &ptes[i];
1501                 struct partition *p = pe->part_table;
1502
1503                 if (p && !is_cleared_partition(p)) {
1504                         if (pno >= 0)
1505                                 goto not_unique;
1506                         pno = i;
1507                 }
1508         }
1509         if (pno >= 0) {
1510                 printf("Selected partition %d\n", pno+1);
1511                 return pno;
1512         }
1513         printf("No partition is defined yet!\n");
1514         return -1;
1515
1516  not_unique:
1517         return get_partition(warn, max);
1518 }
1519
1520 static int
1521 get_nonexisting_partition(int warn, int max)
1522 {
1523         int pno = -1;
1524         int i;
1525
1526         for (i = 0; i < max; i++) {
1527                 struct pte *pe = &ptes[i];
1528                 struct partition *p = pe->part_table;
1529
1530                 if (p && is_cleared_partition(p)) {
1531                         if (pno >= 0)
1532                                 goto not_unique;
1533                         pno = i;
1534                 }
1535         }
1536         if (pno >= 0) {
1537                 printf("Selected partition %d\n", pno+1);
1538                 return pno;
1539         }
1540         printf("All primary partitions have been defined already!\n");
1541         return -1;
1542
1543  not_unique:
1544         return get_partition(warn, max);
1545 }
1546
1547
1548 static void
1549 change_units(void)
1550 {
1551         display_in_cyl_units = !display_in_cyl_units;
1552         update_units();
1553         printf("Changing display/entry units to %s\n",
1554                 str_units(PLURAL));
1555 }
1556
1557 static void
1558 toggle_active(int i)
1559 {
1560         struct pte *pe = &ptes[i];
1561         struct partition *p = pe->part_table;
1562
1563         if (IS_EXTENDED(p->sys_ind) && !p->boot_ind)
1564                 printf("WARNING: Partition %d is an extended partition\n", i + 1);
1565         p->boot_ind = (p->boot_ind ? 0 : ACTIVE_FLAG);
1566         pe->changed = 1;
1567 }
1568
1569 static void
1570 toggle_dos_compatibility_flag(void)
1571 {
1572         dos_compatible_flag = ~dos_compatible_flag;
1573         if (dos_compatible_flag) {
1574                 sector_offset = sectors;
1575                 printf("DOS Compatibility flag is set\n");
1576         } else {
1577                 sector_offset = 1;
1578                 printf("DOS Compatibility flag is not set\n");
1579         }
1580 }
1581
1582 static void
1583 delete_partition(int i)
1584 {
1585         struct pte *pe = &ptes[i];
1586         struct partition *p = pe->part_table;
1587         struct partition *q = pe->ext_pointer;
1588
1589 /* Note that for the fifth partition (i == 4) we don't actually
1590  * decrement partitions.
1591  */
1592
1593         if (warn_geometry())
1594                 return;         /* C/H/S not set */
1595         pe->changed = 1;
1596
1597         if (LABEL_IS_SUN) {
1598                 sun_delete_partition(i);
1599                 return;
1600         }
1601         if (LABEL_IS_SGI) {
1602                 sgi_delete_partition(i);
1603                 return;
1604         }
1605
1606         if (i < 4) {
1607                 if (IS_EXTENDED(p->sys_ind) && i == ext_index) {
1608                         partitions = 4;
1609                         ptes[ext_index].ext_pointer = NULL;
1610                         extended_offset = 0;
1611                 }
1612                 clear_partition(p);
1613                 return;
1614         }
1615
1616         if (!q->sys_ind && i > 4) {
1617                 /* the last one in the chain - just delete */
1618                 --partitions;
1619                 --i;
1620                 clear_partition(ptes[i].ext_pointer);
1621                 ptes[i].changed = 1;
1622         } else {
1623                 /* not the last one - further ones will be moved down */
1624                 if (i > 4) {
1625                         /* delete this link in the chain */
1626                         p = ptes[i-1].ext_pointer;
1627                         *p = *q;
1628                         set_start_sect(p, get_start_sect(q));
1629                         set_nr_sects(p, get_nr_sects(q));
1630                         ptes[i-1].changed = 1;
1631                 } else if (partitions > 5) {    /* 5 will be moved to 4 */
1632                         /* the first logical in a longer chain */
1633                         pe = &ptes[5];
1634
1635                         if (pe->part_table) /* prevent SEGFAULT */
1636                                 set_start_sect(pe->part_table,
1637                                                    get_partition_start(pe) -
1638                                                    extended_offset);
1639                         pe->offset = extended_offset;
1640                         pe->changed = 1;
1641                 }
1642
1643                 if (partitions > 5) {
1644                         partitions--;
1645                         while (i < partitions) {
1646                                 ptes[i] = ptes[i+1];
1647                                 i++;
1648                         }
1649                 } else
1650                         /* the only logical: clear only */
1651                         clear_partition(ptes[i].part_table);
1652         }
1653 }
1654
1655 static void
1656 change_sysid(void)
1657 {
1658         int i, sys, origsys;
1659         struct partition *p;
1660
1661         /* If sgi_label then don't use get_existing_partition,
1662            let the user select a partition, since get_existing_partition()
1663            only works for Linux like partition tables. */
1664         if (!LABEL_IS_SGI) {
1665                 i = get_existing_partition(0, partitions);
1666         } else {
1667                 i = get_partition(0, partitions);
1668         }
1669         if (i == -1)
1670                 return;
1671         p = ptes[i].part_table;
1672         origsys = sys = get_sysid(i);
1673
1674         /* if changing types T to 0 is allowed, then
1675            the reverse change must be allowed, too */
1676         if (!sys && !LABEL_IS_SGI && !LABEL_IS_SUN && !get_nr_sects(p)) {
1677                 printf("Partition %d does not exist yet!\n", i + 1);
1678                 return;
1679         }
1680         while (1) {
1681                 sys = read_hex(get_sys_types());
1682
1683                 if (!sys && !LABEL_IS_SGI && !LABEL_IS_SUN) {
1684                         printf("Type 0 means free space to many systems\n"
1685                                    "(but not to Linux). Having partitions of\n"
1686                                    "type 0 is probably unwise.\n");
1687                         /* break; */
1688                 }
1689
1690                 if (!LABEL_IS_SUN && !LABEL_IS_SGI) {
1691                         if (IS_EXTENDED(sys) != IS_EXTENDED(p->sys_ind)) {
1692                                 printf("You cannot change a partition into"
1693                                            " an extended one or vice versa\n");
1694                                 break;
1695                         }
1696                 }
1697
1698                 if (sys < 256) {
1699 #if ENABLE_FEATURE_SUN_LABEL
1700                         if (LABEL_IS_SUN && i == 2 && sys != SUN_WHOLE_DISK)
1701                                 printf("Consider leaving partition 3 "
1702                                            "as Whole disk (5),\n"
1703                                            "as SunOS/Solaris expects it and "
1704                                            "even Linux likes it\n\n");
1705 #endif
1706 #if ENABLE_FEATURE_SGI_LABEL
1707                         if (LABEL_IS_SGI &&
1708                                 (
1709                                         (i == 10 && sys != SGI_ENTIRE_DISK) ||
1710                                         (i == 8 && sys != 0)
1711                                 )
1712                         ) {
1713                                 printf("Consider leaving partition 9 "
1714                                            "as volume header (0),\nand "
1715                                            "partition 11 as entire volume (6)"
1716                                            "as IRIX expects it\n\n");
1717                         }
1718 #endif
1719                         if (sys == origsys)
1720                                 break;
1721                         if (LABEL_IS_SUN) {
1722                                 sun_change_sysid(i, sys);
1723                         } else if (LABEL_IS_SGI) {
1724                                 sgi_change_sysid(i, sys);
1725                         } else
1726                                 p->sys_ind = sys;
1727
1728                         printf("Changed system type of partition %d "
1729                                 "to %x (%s)\n", i + 1, sys,
1730                                 partition_type(sys));
1731                         ptes[i].changed = 1;
1732                         if (is_dos_partition(origsys) ||
1733                                 is_dos_partition(sys))
1734                                 dos_changed = 1;
1735                         break;
1736                 }
1737         }
1738 }
1739 #endif /* FEATURE_FDISK_WRITABLE */
1740
1741
1742 /* check_consistency() and linear2chs() added Sat Mar 6 12:28:16 1993,
1743  * faith@cs.unc.edu, based on code fragments from pfdisk by Gordon W. Ross,
1744  * Jan.  1990 (version 1.2.1 by Gordon W. Ross Aug. 1990; Modified by S.
1745  * Lubkin Oct.  1991). */
1746
1747 static void
1748 linear2chs(unsigned ls, unsigned *c, unsigned *h, unsigned *s)
1749 {
1750         int spc = heads * sectors;
1751
1752         *c = ls / spc;
1753         ls = ls % spc;
1754         *h = ls / sectors;
1755         *s = ls % sectors + 1;  /* sectors count from 1 */
1756 }
1757
1758 static void
1759 check_consistency(const struct partition *p, int partition)
1760 {
1761         unsigned pbc, pbh, pbs;          /* physical beginning c, h, s */
1762         unsigned pec, peh, pes;          /* physical ending c, h, s */
1763         unsigned lbc, lbh, lbs;          /* logical beginning c, h, s */
1764         unsigned lec, leh, les;          /* logical ending c, h, s */
1765
1766         if (!heads || !sectors || (partition >= 4))
1767                 return;         /* do not check extended partitions */
1768
1769 /* physical beginning c, h, s */
1770         pbc = (p->cyl & 0xff) | ((p->sector << 2) & 0x300);
1771         pbh = p->head;
1772         pbs = p->sector & 0x3f;
1773
1774 /* physical ending c, h, s */
1775         pec = (p->end_cyl & 0xff) | ((p->end_sector << 2) & 0x300);
1776         peh = p->end_head;
1777         pes = p->end_sector & 0x3f;
1778
1779 /* compute logical beginning (c, h, s) */
1780         linear2chs(get_start_sect(p), &lbc, &lbh, &lbs);
1781
1782 /* compute logical ending (c, h, s) */
1783         linear2chs(get_start_sect(p) + get_nr_sects(p) - 1, &lec, &leh, &les);
1784
1785 /* Same physical / logical beginning? */
1786         if (cylinders <= 1024 && (pbc != lbc || pbh != lbh || pbs != lbs)) {
1787                 printf("Partition %d has different physical/logical "
1788                         "beginnings (non-Linux?):\n", partition + 1);
1789                 printf("     phys=(%d, %d, %d) ", pbc, pbh, pbs);
1790                 printf("logical=(%d, %d, %d)\n",lbc, lbh, lbs);
1791         }
1792
1793 /* Same physical / logical ending? */
1794         if (cylinders <= 1024 && (pec != lec || peh != leh || pes != les)) {
1795                 printf("Partition %d has different physical/logical "
1796                         "endings:\n", partition + 1);
1797                 printf("     phys=(%d, %d, %d) ", pec, peh, pes);
1798                 printf("logical=(%d, %d, %d)\n", lec, leh, les);
1799         }
1800
1801 /* Ending on cylinder boundary? */
1802         if (peh != (heads - 1) || pes != sectors) {
1803                 printf("Partition %i does not end on cylinder boundary\n",
1804                         partition + 1);
1805         }
1806 }
1807
1808 static void
1809 list_disk_geometry(void)
1810 {
1811         long long bytes = (total_number_of_sectors << 9);
1812         long megabytes = bytes/1000000;
1813
1814         if (megabytes < 10000)
1815                 printf("\nDisk %s: %ld MB, %lld bytes\n",
1816                            disk_device, megabytes, bytes);
1817         else
1818                 printf("\nDisk %s: %ld.%ld GB, %lld bytes\n",
1819                            disk_device, megabytes/1000, (megabytes/100)%10, bytes);
1820         printf("%d heads, %d sectors/track, %d cylinders",
1821                    heads, sectors, cylinders);
1822         if (units_per_sector == 1)
1823                 printf(", total %llu sectors",
1824                            total_number_of_sectors / (sector_size/512));
1825         printf("\nUnits = %s of %d * %d = %d bytes\n\n",
1826                    str_units(PLURAL),
1827                    units_per_sector, sector_size, units_per_sector * sector_size);
1828 }
1829
1830 /*
1831  * Check whether partition entries are ordered by their starting positions.
1832  * Return 0 if OK. Return i if partition i should have been earlier.
1833  * Two separate checks: primary and logical partitions.
1834  */
1835 static int
1836 wrong_p_order(int *prev)
1837 {
1838         const struct pte *pe;
1839         const struct partition *p;
1840         off_t last_p_start_pos = 0, p_start_pos;
1841         int i, last_i = 0;
1842
1843         for (i = 0 ; i < partitions; i++) {
1844                 if (i == 4) {
1845                         last_i = 4;
1846                         last_p_start_pos = 0;
1847                 }
1848                 pe = &ptes[i];
1849                 if ((p = pe->part_table)->sys_ind) {
1850                         p_start_pos = get_partition_start(pe);
1851
1852                         if (last_p_start_pos > p_start_pos) {
1853                                 if (prev)
1854                                         *prev = last_i;
1855                                 return i;
1856                         }
1857
1858                         last_p_start_pos = p_start_pos;
1859                         last_i = i;
1860                 }
1861         }
1862         return 0;
1863 }
1864
1865 #if ENABLE_FEATURE_FDISK_ADVANCED
1866 /*
1867  * Fix the chain of logicals.
1868  * extended_offset is unchanged, the set of sectors used is unchanged
1869  * The chain is sorted so that sectors increase, and so that
1870  * starting sectors increase.
1871  *
1872  * After this it may still be that cfdisk doesnt like the table.
1873  * (This is because cfdisk considers expanded parts, from link to
1874  * end of partition, and these may still overlap.)
1875  * Now
1876  *   sfdisk /dev/hda > ohda; sfdisk /dev/hda < ohda
1877  * may help.
1878  */
1879 static void
1880 fix_chain_of_logicals(void)
1881 {
1882         int j, oj, ojj, sj, sjj;
1883         struct partition *pj,*pjj,tmp;
1884
1885         /* Stage 1: sort sectors but leave sector of part 4 */
1886         /* (Its sector is the global extended_offset.) */
1887  stage1:
1888         for (j = 5; j < partitions-1; j++) {
1889                 oj = ptes[j].offset;
1890                 ojj = ptes[j+1].offset;
1891                 if (oj > ojj) {
1892                         ptes[j].offset = ojj;
1893                         ptes[j+1].offset = oj;
1894                         pj = ptes[j].part_table;
1895                         set_start_sect(pj, get_start_sect(pj)+oj-ojj);
1896                         pjj = ptes[j+1].part_table;
1897                         set_start_sect(pjj, get_start_sect(pjj)+ojj-oj);
1898                         set_start_sect(ptes[j-1].ext_pointer,
1899                                            ojj-extended_offset);
1900                         set_start_sect(ptes[j].ext_pointer,
1901                                            oj-extended_offset);
1902                         goto stage1;
1903                 }
1904         }
1905
1906         /* Stage 2: sort starting sectors */
1907  stage2:
1908         for (j = 4; j < partitions-1; j++) {
1909                 pj = ptes[j].part_table;
1910                 pjj = ptes[j+1].part_table;
1911                 sj = get_start_sect(pj);
1912                 sjj = get_start_sect(pjj);
1913                 oj = ptes[j].offset;
1914                 ojj = ptes[j+1].offset;
1915                 if (oj+sj > ojj+sjj) {
1916                         tmp = *pj;
1917                         *pj = *pjj;
1918                         *pjj = tmp;
1919                         set_start_sect(pj, ojj+sjj-oj);
1920                         set_start_sect(pjj, oj+sj-ojj);
1921                         goto stage2;
1922                 }
1923         }
1924
1925         /* Probably something was changed */
1926         for (j = 4; j < partitions; j++)
1927                 ptes[j].changed = 1;
1928 }
1929
1930
1931 static void
1932 fix_partition_table_order(void)
1933 {
1934         struct pte *pei, *pek;
1935         int i,k;
1936
1937         if (!wrong_p_order(NULL)) {
1938                 printf("Ordering is already correct\n\n");
1939                 return;
1940         }
1941
1942         while ((i = wrong_p_order(&k)) != 0 && i < 4) {
1943                 /* partition i should have come earlier, move it */
1944                 /* We have to move data in the MBR */
1945                 struct partition *pi, *pk, *pe, pbuf;
1946                 pei = &ptes[i];
1947                 pek = &ptes[k];
1948
1949                 pe = pei->ext_pointer;
1950                 pei->ext_pointer = pek->ext_pointer;
1951                 pek->ext_pointer = pe;
1952
1953                 pi = pei->part_table;
1954                 pk = pek->part_table;
1955
1956                 memmove(&pbuf, pi, sizeof(struct partition));
1957                 memmove(pi, pk, sizeof(struct partition));
1958                 memmove(pk, &pbuf, sizeof(struct partition));
1959
1960                 pei->changed = pek->changed = 1;
1961         }
1962
1963         if (i)
1964                 fix_chain_of_logicals();
1965
1966         printf("Done.\n");
1967
1968 }
1969 #endif
1970
1971 static void
1972 list_table(int xtra)
1973 {
1974         const struct partition *p;
1975         int i, w;
1976
1977         if (LABEL_IS_SUN) {
1978                 sun_list_table(xtra);
1979                 return;
1980         }
1981         if (LABEL_IS_SUN) {
1982                 sgi_list_table(xtra);
1983                 return;
1984         }
1985
1986         list_disk_geometry();
1987
1988         if (LABEL_IS_OSF) {
1989                 xbsd_print_disklabel(xtra);
1990                 return;
1991         }
1992
1993         /* Heuristic: we list partition 3 of /dev/foo as /dev/foo3,
1994            but if the device name ends in a digit, say /dev/foo1,
1995            then the partition is called /dev/foo1p3. */
1996         w = strlen(disk_device);
1997         if (w && isdigit(disk_device[w-1]))
1998                 w++;
1999         if (w < 5)
2000                 w = 5;
2001
2002         //            1 12345678901 12345678901 12345678901  12
2003         printf("%*s Boot      Start         End      Blocks  Id System\n",
2004                    w+1, "Device");
2005
2006         for (i = 0; i < partitions; i++) {
2007                 const struct pte *pe = &ptes[i];
2008                 off_t psects;
2009                 off_t pblocks;
2010                 unsigned podd;
2011
2012                 p = pe->part_table;
2013                 if (!p || is_cleared_partition(p))
2014                         continue;
2015
2016                 psects = get_nr_sects(p);
2017                 pblocks = psects;
2018                 podd = 0;
2019
2020                 if (sector_size < 1024) {
2021                         pblocks /= (1024 / sector_size);
2022                         podd = psects % (1024 / sector_size);
2023                 }
2024                 if (sector_size > 1024)
2025                         pblocks *= (sector_size / 1024);
2026
2027                 printf("%s  %c %11llu %11llu %11llu%c %2x %s\n",
2028                         partname(disk_device, i+1, w+2),
2029                         !p->boot_ind ? ' ' : p->boot_ind == ACTIVE_FLAG /* boot flag */
2030                                 ? '*' : '?',
2031                         (unsigned long long) cround(get_partition_start(pe)),           /* start */
2032                         (unsigned long long) cround(get_partition_start(pe) + psects    /* end */
2033                                 - (psects ? 1 : 0)),
2034                         (unsigned long long) pblocks, podd ? '+' : ' ', /* odd flag on end */
2035                         p->sys_ind,                                     /* type id */
2036                         partition_type(p->sys_ind));                    /* type name */
2037
2038                 check_consistency(p, i);
2039         }
2040
2041         /* Is partition table in disk order? It need not be, but... */
2042         /* partition table entries are not checked for correct order if this
2043            is a sgi, sun or aix labeled disk... */
2044         if (LABEL_IS_DOS && wrong_p_order(NULL)) {
2045                 /* FIXME */
2046                 printf("\nPartition table entries are not in disk order\n");
2047         }
2048 }
2049
2050 #if ENABLE_FEATURE_FDISK_ADVANCED
2051 static void
2052 x_list_table(int extend)
2053 {
2054         const struct pte *pe;
2055         const struct partition *p;
2056         int i;
2057
2058         printf("\nDisk %s: %d heads, %d sectors, %d cylinders\n\n",
2059                 disk_device, heads, sectors, cylinders);
2060         printf("Nr AF  Hd Sec  Cyl  Hd Sec  Cyl      Start       Size ID\n");
2061         for (i = 0 ; i < partitions; i++) {
2062                 pe = &ptes[i];
2063                 p = (extend ? pe->ext_pointer : pe->part_table);
2064                 if (p != NULL) {
2065                         printf("%2d %02x%4d%4d%5d%4d%4d%5d%11u%11u %02x\n",
2066                                 i + 1, p->boot_ind, p->head,
2067                                 sector(p->sector),
2068                                 cylinder(p->sector, p->cyl), p->end_head,
2069                                 sector(p->end_sector),
2070                                 cylinder(p->end_sector, p->end_cyl),
2071                                 get_start_sect(p), get_nr_sects(p), p->sys_ind);
2072                         if (p->sys_ind)
2073                                 check_consistency(p, i);
2074                 }
2075         }
2076 }
2077 #endif
2078
2079 #if ENABLE_FEATURE_FDISK_WRITABLE
2080 static void
2081 fill_bounds(off_t *first, off_t *last)
2082 {
2083         int i;
2084         const struct pte *pe = &ptes[0];
2085         const struct partition *p;
2086
2087         for (i = 0; i < partitions; pe++,i++) {
2088                 p = pe->part_table;
2089                 if (!p->sys_ind || IS_EXTENDED(p->sys_ind)) {
2090                         first[i] = 0xffffffff;
2091                         last[i] = 0;
2092                 } else {
2093                         first[i] = get_partition_start(pe);
2094                         last[i] = first[i] + get_nr_sects(p) - 1;
2095                 }
2096         }
2097 }
2098
2099 static void
2100 check(int n, unsigned h, unsigned s, unsigned c, off_t start)
2101 {
2102         off_t total, real_s, real_c;
2103
2104         real_s = sector(s) - 1;
2105         real_c = cylinder(s, c);
2106         total = (real_c * sectors + real_s) * heads + h;
2107         if (!total)
2108                 printf("Partition %d contains sector 0\n", n);
2109         if (h >= heads)
2110                 printf("Partition %d: head %d greater than maximum %d\n",
2111                         n, h + 1, heads);
2112         if (real_s >= sectors)
2113                 printf("Partition %d: sector %d greater than "
2114                         "maximum %d\n", n, s, sectors);
2115         if (real_c >= cylinders)
2116                 printf("Partition %d: cylinder %"OFF_FMT"u greater than "
2117                         "maximum %d\n", n, real_c + 1, cylinders);
2118         if (cylinders <= 1024 && start != total)
2119                 printf("Partition %d: previous sectors %"OFF_FMT"u disagrees with "
2120                         "total %"OFF_FMT"u\n", n, start, total);
2121 }
2122
2123 static void
2124 verify(void)
2125 {
2126         int i, j;
2127         unsigned total = 1;
2128         off_t first[partitions], last[partitions];
2129         struct partition *p;
2130
2131         if (warn_geometry())
2132                 return;
2133
2134         if (LABEL_IS_SUN) {
2135                 verify_sun();
2136                 return;
2137         }
2138         if (LABEL_IS_SGI) {
2139                 verify_sgi(1);
2140                 return;
2141         }
2142
2143         fill_bounds(first, last);
2144         for (i = 0; i < partitions; i++) {
2145                 struct pte *pe = &ptes[i];
2146
2147                 p = pe->part_table;
2148                 if (p->sys_ind && !IS_EXTENDED(p->sys_ind)) {
2149                         check_consistency(p, i);
2150                         if (get_partition_start(pe) < first[i])
2151                                 printf("Warning: bad start-of-data in "
2152                                         "partition %d\n", i + 1);
2153                         check(i + 1, p->end_head, p->end_sector, p->end_cyl,
2154                                 last[i]);
2155                         total += last[i] + 1 - first[i];
2156                         for (j = 0; j < i; j++) {
2157                                 if ((first[i] >= first[j] && first[i] <= last[j])
2158                                  || ((last[i] <= last[j] && last[i] >= first[j]))) {
2159                                         printf("Warning: partition %d overlaps "
2160                                                 "partition %d\n", j + 1, i + 1);
2161                                         total += first[i] >= first[j] ?
2162                                                 first[i] : first[j];
2163                                         total -= last[i] <= last[j] ?
2164                                                 last[i] : last[j];
2165                                 }
2166                         }
2167                 }
2168         }
2169
2170         if (extended_offset) {
2171                 struct pte *pex = &ptes[ext_index];
2172                 off_t e_last = get_start_sect(pex->part_table) +
2173                         get_nr_sects(pex->part_table) - 1;
2174
2175                 for (i = 4; i < partitions; i++) {
2176                         total++;
2177                         p = ptes[i].part_table;
2178                         if (!p->sys_ind) {
2179                                 if (i != 4 || i + 1 < partitions)
2180                                         printf("Warning: partition %d "
2181                                                 "is empty\n", i + 1);
2182                         } else if (first[i] < extended_offset || last[i] > e_last) {
2183                                 printf("Logical partition %d not entirely in "
2184                                         "partition %d\n", i + 1, ext_index + 1);
2185                         }
2186                 }
2187         }
2188
2189         if (total > heads * sectors * cylinders)
2190                 printf("Total allocated sectors %d greater than the maximum "
2191                         "%d\n", total, heads * sectors * cylinders);
2192         else {
2193                 total = heads * sectors * cylinders - total;
2194                 if (total != 0)
2195                         printf("%d unallocated sectors\n", total);
2196         }
2197 }
2198
2199 static void
2200 add_partition(int n, int sys)
2201 {
2202         char mesg[256];         /* 48 does not suffice in Japanese */
2203         int i, num_read = 0;
2204         struct partition *p = ptes[n].part_table;
2205         struct partition *q = ptes[ext_index].part_table;
2206         long long llimit;
2207         off_t start, stop = 0, limit, temp,
2208                 first[partitions], last[partitions];
2209
2210         if (p && p->sys_ind) {
2211                 printf(msg_part_already_defined, n + 1);
2212                 return;
2213         }
2214         fill_bounds(first, last);
2215         if (n < 4) {
2216                 start = sector_offset;
2217                 if (display_in_cyl_units || !total_number_of_sectors)
2218                         llimit = heads * sectors * cylinders - 1;
2219                 else
2220                         llimit = total_number_of_sectors - 1;
2221                 limit = llimit;
2222                 if (limit != llimit)
2223                         limit = 0x7fffffff;
2224                 if (extended_offset) {
2225                         first[ext_index] = extended_offset;
2226                         last[ext_index] = get_start_sect(q) +
2227                                 get_nr_sects(q) - 1;
2228                 }
2229         } else {
2230                 start = extended_offset + sector_offset;
2231                 limit = get_start_sect(q) + get_nr_sects(q) - 1;
2232         }
2233         if (display_in_cyl_units)
2234                 for (i = 0; i < partitions; i++)
2235                         first[i] = (cround(first[i]) - 1) * units_per_sector;
2236
2237         snprintf(mesg, sizeof(mesg), "First %s", str_units(SINGULAR));
2238         do {
2239                 temp = start;
2240                 for (i = 0; i < partitions; i++) {
2241                         int lastplusoff;
2242
2243                         if (start == ptes[i].offset)
2244                                 start += sector_offset;
2245                         lastplusoff = last[i] + ((n < 4) ? 0 : sector_offset);
2246                         if (start >= first[i] && start <= lastplusoff)
2247                                 start = lastplusoff + 1;
2248                 }
2249                 if (start > limit)
2250                         break;
2251                 if (start >= temp+units_per_sector && num_read) {
2252                         printf("Sector %"OFF_FMT"d is already allocated\n", temp);
2253                         temp = start;
2254                         num_read = 0;
2255                 }
2256                 if (!num_read && start == temp) {
2257                         off_t saved_start;
2258
2259                         saved_start = start;
2260                         start = read_int(cround(saved_start), cround(saved_start), cround(limit),
2261                                          0, mesg);
2262                         if (display_in_cyl_units) {
2263                                 start = (start - 1) * units_per_sector;
2264                                 if (start < saved_start) start = saved_start;
2265                         }
2266                         num_read = 1;
2267                 }
2268         } while (start != temp || !num_read);
2269         if (n > 4) {                    /* NOT for fifth partition */
2270                 struct pte *pe = &ptes[n];
2271
2272                 pe->offset = start - sector_offset;
2273                 if (pe->offset == extended_offset) { /* must be corrected */
2274                         pe->offset++;
2275                         if (sector_offset == 1)
2276                                 start++;
2277                 }
2278         }
2279
2280         for (i = 0; i < partitions; i++) {
2281                 struct pte *pe = &ptes[i];
2282
2283                 if (start < pe->offset && limit >= pe->offset)
2284                         limit = pe->offset - 1;
2285                 if (start < first[i] && limit >= first[i])
2286                         limit = first[i] - 1;
2287         }
2288         if (start > limit) {
2289                 printf("No free sectors available\n");
2290                 if (n > 4)
2291                         partitions--;
2292                 return;
2293         }
2294         if (cround(start) == cround(limit)) {
2295                 stop = limit;
2296         } else {
2297                 snprintf(mesg, sizeof(mesg),
2298                          "Last %s or +size or +sizeM or +sizeK",
2299                          str_units(SINGULAR));
2300                 stop = read_int(cround(start), cround(limit), cround(limit),
2301                                 cround(start), mesg);
2302                 if (display_in_cyl_units) {
2303                         stop = stop * units_per_sector - 1;
2304                         if (stop >limit)
2305                                 stop = limit;
2306                 }
2307         }
2308
2309         set_partition(n, 0, start, stop, sys);
2310         if (n > 4)
2311                 set_partition(n - 1, 1, ptes[n].offset, stop, EXTENDED);
2312
2313         if (IS_EXTENDED(sys)) {
2314                 struct pte *pe4 = &ptes[4];
2315                 struct pte *pen = &ptes[n];
2316
2317                 ext_index = n;
2318                 pen->ext_pointer = p;
2319                 pe4->offset = extended_offset = start;
2320                 pe4->sectorbuffer = xzalloc(sector_size);
2321                 pe4->part_table = pt_offset(pe4->sectorbuffer, 0);
2322                 pe4->ext_pointer = pe4->part_table + 1;
2323                 pe4->changed = 1;
2324                 partitions = 5;
2325         }
2326 }
2327
2328 static void
2329 add_logical(void)
2330 {
2331         if (partitions > 5 || ptes[4].part_table->sys_ind) {
2332                 struct pte *pe = &ptes[partitions];
2333
2334                 pe->sectorbuffer = xzalloc(sector_size);
2335                 pe->part_table = pt_offset(pe->sectorbuffer, 0);
2336                 pe->ext_pointer = pe->part_table + 1;
2337                 pe->offset = 0;
2338                 pe->changed = 1;
2339                 partitions++;
2340         }
2341         add_partition(partitions - 1, LINUX_NATIVE);
2342 }
2343
2344 static void
2345 new_partition(void)
2346 {
2347         int i, free_primary = 0;
2348
2349         if (warn_geometry())
2350                 return;
2351
2352         if (LABEL_IS_SUN) {
2353                 add_sun_partition(get_partition(0, partitions), LINUX_NATIVE);
2354                 return;
2355         }
2356         if (LABEL_IS_SGI) {
2357                 sgi_add_partition(get_partition(0, partitions), LINUX_NATIVE);
2358                 return;
2359         }
2360         if (LABEL_IS_AIX) {
2361                 printf("Sorry - this fdisk cannot handle AIX disk labels.\n"
2362 "If you want to add DOS-type partitions, create a new empty DOS partition\n"
2363 "table first (use 'o'). This will destroy the present disk contents.\n");
2364                 return;
2365         }
2366
2367         for (i = 0; i < 4; i++)
2368                 free_primary += !ptes[i].part_table->sys_ind;
2369
2370         if (!free_primary && partitions >= MAXIMUM_PARTS) {
2371                 printf("The maximum number of partitions has been created\n");
2372                 return;
2373         }
2374
2375         if (!free_primary) {
2376                 if (extended_offset)
2377                         add_logical();
2378                 else
2379                         printf("You must delete some partition and add "
2380                                  "an extended partition first\n");
2381         } else {
2382                 char c, line[LINE_LENGTH];
2383                 snprintf(line, sizeof(line),
2384                         "Command action\n"
2385                         "   %s\n"
2386                         "   p   primary partition (1-4)\n",
2387                         (extended_offset ?
2388                         "l   logical (5 or over)" : "e   extended"));
2389                 while (1) {
2390                         c = read_nonempty(line);
2391                         if (c == 'p' || c == 'P') {
2392                                 i = get_nonexisting_partition(0, 4);
2393                                 if (i >= 0)
2394                                         add_partition(i, LINUX_NATIVE);
2395                                 return;
2396                         }
2397                         if (c == 'l' && extended_offset) {
2398                                 add_logical();
2399                                 return;
2400                         }
2401                         if (c == 'e' && !extended_offset) {
2402                                 i = get_nonexisting_partition(0, 4);
2403                                 if (i >= 0)
2404                                         add_partition(i, EXTENDED);
2405                                 return;
2406                         }
2407                         printf("Invalid partition number "
2408                                          "for type '%c'\n", c);
2409                 }
2410         }
2411 }
2412
2413 static void
2414 write_table(void)
2415 {
2416         int i;
2417
2418         if (LABEL_IS_DOS) {
2419                 for (i = 0; i < 3; i++)
2420                         if (ptes[i].changed)
2421                                 ptes[3].changed = 1;
2422                 for (i = 3; i < partitions; i++) {
2423                         struct pte *pe = &ptes[i];
2424
2425                         if (pe->changed) {
2426                                 write_part_table_flag(pe->sectorbuffer);
2427                                 write_sector(pe->offset, pe->sectorbuffer);
2428                         }
2429                 }
2430         }
2431         else if (LABEL_IS_SGI) {
2432                 /* no test on change? the printf below might be mistaken */
2433                 sgi_write_table();
2434         }
2435         else if (LABEL_IS_SUN) {
2436                 int needw = 0;
2437
2438                 for (i = 0; i < 8; i++)
2439                         if (ptes[i].changed)
2440                                 needw = 1;
2441                 if (needw)
2442                         sun_write_table();
2443         }
2444
2445         printf("The partition table has been altered!\n\n");
2446         reread_partition_table(1);
2447 }
2448
2449 static void
2450 reread_partition_table(int leave)
2451 {
2452         int i;
2453
2454         printf("Calling ioctl() to re-read partition table\n");
2455         sync();
2456         /* sleep(2); Huh? */
2457         i = ioctl(fd, BLKRRPART);
2458 #if 0
2459         else {
2460                 /* some kernel versions (1.2.x) seem to have trouble
2461                    rereading the partition table, but if asked to do it
2462                    twice, the second time works. - biro@yggdrasil.com */
2463                 sync();
2464                 sleep(2);
2465                 i = ioctl(fd, BLKRRPART);
2466         }
2467 #endif
2468
2469         if (i) {
2470                 bb_perror_msg("WARNING: rereading partition table "
2471                         "failed, kernel still uses old table");
2472         }
2473
2474 #if 0
2475         if (dos_changed)
2476                 printf(
2477                 "\nWARNING: If you have created or modified any DOS 6.x\n"
2478                 "partitions, please see the fdisk manual page for additional\n"
2479                 "information\n");
2480 #endif
2481
2482         if (leave) {
2483                 if (ENABLE_FEATURE_CLEAN_UP)
2484                         close(fd);
2485                 exit(i != 0);
2486         }
2487 }
2488 #endif /* FEATURE_FDISK_WRITABLE */
2489
2490 #if ENABLE_FEATURE_FDISK_ADVANCED
2491 #define MAX_PER_LINE    16
2492 static void
2493 print_buffer(char *pbuffer)
2494 {
2495         int i,l;
2496
2497         for (i = 0, l = 0; i < sector_size; i++, l++) {
2498                 if (l == 0)
2499                         printf("0x%03X:", i);
2500                 printf(" %02X", (unsigned char) pbuffer[i]);
2501                 if (l == MAX_PER_LINE - 1) {
2502                         puts("");
2503                         l = -1;
2504                 }
2505         }
2506         if (l > 0)
2507                 puts("");
2508         puts("");
2509 }
2510
2511 static void
2512 print_raw(void)
2513 {
2514         int i;
2515
2516         printf("Device: %s\n", disk_device);
2517         if (LABEL_IS_SGI || LABEL_IS_SUN)
2518                 print_buffer(MBRbuffer);
2519         else {
2520                 for (i = 3; i < partitions; i++)
2521                         print_buffer(ptes[i].sectorbuffer);
2522         }
2523 }
2524
2525 static void
2526 move_begin(int i)
2527 {
2528         struct pte *pe = &ptes[i];
2529         struct partition *p = pe->part_table;
2530         off_t new, first;
2531
2532         if (warn_geometry())
2533                 return;
2534         if (!p->sys_ind || !get_nr_sects(p) || IS_EXTENDED(p->sys_ind)) {
2535                 printf("Partition %d has no data area\n", i + 1);
2536                 return;
2537         }
2538         first = get_partition_start(pe);
2539         new = read_int(first, first, first + get_nr_sects(p) - 1, first,
2540                            "New beginning of data") - pe->offset;
2541
2542         if (new != get_nr_sects(p)) {
2543                 first = get_nr_sects(p) + get_start_sect(p) - new;
2544                 set_nr_sects(p, first);
2545                 set_start_sect(p, new);
2546                 pe->changed = 1;
2547         }
2548 }
2549
2550 static void
2551 xselect(void)
2552 {
2553         char c;
2554
2555         while (1) {
2556                 putchar('\n');
2557                 c = tolower(read_nonempty("Expert command (m for help): "));
2558                 switch (c) {
2559                 case 'a':
2560                         if (LABEL_IS_SUN)
2561                                 sun_set_alt_cyl();
2562                         break;
2563                 case 'b':
2564                         if (LABEL_IS_DOS)
2565                                 move_begin(get_partition(0, partitions));
2566                         break;
2567                 case 'c':
2568                         user_cylinders = cylinders =
2569                                 read_int(1, cylinders, 1048576, 0,
2570                                         "Number of cylinders");
2571                         if (LABEL_IS_SUN)
2572                                 sun_set_ncyl(cylinders);
2573                         if (LABEL_IS_DOS)
2574                                 warn_cylinders();
2575                         break;
2576                 case 'd':
2577                         print_raw();
2578                         break;
2579                 case 'e':
2580                         if (LABEL_IS_SGI)
2581                                 sgi_set_xcyl();
2582                         else if (LABEL_IS_SUN)
2583                                 sun_set_xcyl();
2584                         else if (LABEL_IS_DOS)
2585                                 x_list_table(1);
2586                         break;
2587                 case 'f':
2588                         if (LABEL_IS_DOS)
2589                                 fix_partition_table_order();
2590                         break;
2591                 case 'g':
2592 #if ENABLE_FEATURE_SGI_LABEL
2593                         create_sgilabel();
2594 #endif
2595                         break;
2596                 case 'h':
2597                         user_heads = heads = read_int(1, heads, 256, 0,
2598                                         "Number of heads");
2599                         update_units();
2600                         break;
2601                 case 'i':
2602                         if (LABEL_IS_SUN)
2603                                 sun_set_ilfact();
2604                         break;
2605                 case 'o':
2606                         if (LABEL_IS_SUN)
2607                                 sun_set_rspeed();
2608                         break;
2609                 case 'p':
2610                         if (LABEL_IS_SUN)
2611                                 list_table(1);
2612                         else
2613                                 x_list_table(0);
2614                         break;
2615                 case 'q':
2616                         close(fd);
2617                         puts("");
2618                         exit(0);
2619                 case 'r':
2620                         return;
2621                 case 's':
2622                         user_sectors = sectors = read_int(1, sectors, 63, 0,
2623                                            "Number of sectors");
2624                         if (dos_compatible_flag) {
2625                                 sector_offset = sectors;
2626                                 printf("Warning: setting sector offset for DOS "
2627                                         "compatiblity\n");
2628                         }
2629                         update_units();
2630                         break;
2631                 case 'v':
2632                         verify();
2633                         break;
2634                 case 'w':
2635                         write_table();  /* does not return */
2636                         break;
2637                 case 'y':
2638                         if (LABEL_IS_SUN)
2639                                 sun_set_pcylcount();
2640                         break;
2641                 default:
2642                         xmenu();
2643                 }
2644         }
2645 }
2646 #endif /* ADVANCED mode */
2647
2648 static int
2649 is_ide_cdrom_or_tape(const char *device)
2650 {
2651         FILE *procf;
2652         char buf[100];
2653         struct stat statbuf;
2654         int is_ide = 0;
2655
2656         /* No device was given explicitly, and we are trying some
2657            likely things.  But opening /dev/hdc may produce errors like
2658            "hdc: tray open or drive not ready"
2659            if it happens to be a CD-ROM drive. It even happens that
2660            the process hangs on the attempt to read a music CD.
2661            So try to be careful. This only works since 2.1.73. */
2662
2663         if (strncmp("/dev/hd", device, 7))
2664                 return 0;
2665
2666         snprintf(buf, sizeof(buf), "/proc/ide/%s/media", device+5);
2667         procf = fopen(buf, "r");
2668         if (procf != NULL && fgets(buf, sizeof(buf), procf))
2669                 is_ide = (!strncmp(buf, "cdrom", 5) ||
2670                           !strncmp(buf, "tape", 4));
2671         else
2672                 /* Now when this proc file does not exist, skip the
2673                    device when it is read-only. */
2674                 if (stat(device, &statbuf) == 0)
2675                         is_ide = ((statbuf.st_mode & 0222) == 0);
2676
2677         if (procf)
2678                 fclose(procf);
2679         return is_ide;
2680 }
2681
2682
2683 static void
2684 trydev(const char *device, int user_specified)
2685 {
2686         int gb;
2687
2688         disk_device = device;
2689         if (setjmp(listingbuf))
2690                 return;
2691         if (!user_specified)
2692                 if (is_ide_cdrom_or_tape(device))
2693                         return;
2694         fd = open(disk_device, type_open);
2695         if (fd >= 0) {
2696                 gb = get_boot(try_only);
2697                 if (gb > 0) {   /* I/O error */
2698                         close(fd);
2699                 } else if (gb < 0) { /* no DOS signature */
2700                         list_disk_geometry();
2701                         if (LABEL_IS_AIX) {
2702                                 return;
2703                         }
2704 #if ENABLE_FEATURE_OSF_LABEL
2705                         if (bsd_trydev(device) < 0)
2706 #endif
2707                                 printf("Disk %s doesn't contain a valid "
2708                                         "partition table\n", device);
2709                         close(fd);
2710                 } else {
2711                         close(fd);
2712                         list_table(0);
2713 #if ENABLE_FEATURE_FDISK_WRITABLE
2714                         if (!LABEL_IS_SUN && partitions > 4){
2715                                 delete_partition(ext_index);
2716                         }
2717 #endif
2718                 }
2719         } else {
2720                 /* Ignore other errors, since we try IDE
2721                    and SCSI hard disks which may not be
2722                    installed on the system. */
2723                 if (errno == EACCES) {
2724                         printf("Cannot open %s\n", device);
2725                         return;
2726                 }
2727         }
2728 }
2729
2730 /* for fdisk -l: try all things in /proc/partitions
2731    that look like a partition name (do not end in a digit) */
2732 static void
2733 tryprocpt(void)
2734 {
2735         FILE *procpt;
2736         char line[100], ptname[100], devname[120], *s;
2737         int ma, mi, sz;
2738
2739         procpt = fopen_or_warn("/proc/partitions", "r");
2740
2741         while (fgets(line, sizeof(line), procpt)) {
2742                 if (sscanf(line, " %d %d %d %[^\n ]",
2743                                 &ma, &mi, &sz, ptname) != 4)
2744                         continue;
2745                 for (s = ptname; *s; s++);
2746                 if (isdigit(s[-1]))
2747                         continue;
2748                 sprintf(devname, "/dev/%s", ptname);
2749                 trydev(devname, 0);
2750         }
2751 #if ENABLE_FEATURE_CLEAN_UP
2752         fclose(procpt);
2753 #endif
2754 }
2755
2756 #if ENABLE_FEATURE_FDISK_WRITABLE
2757 static void
2758 unknown_command(int c)
2759 {
2760         printf("%c: unknown command\n", c);
2761 }
2762 #endif
2763
2764 void BUG_fdisk_globals_overflow(void);
2765
2766 int fdisk_main(int argc, char **argv);
2767 int fdisk_main(int argc, char **argv)
2768 {
2769         char *str_b, *str_C, *str_H, *str_S;
2770         unsigned opt;
2771         /*
2772          *  fdisk -v
2773          *  fdisk -l [-b sectorsize] [-u] device ...
2774          *  fdisk -s [partition] ...
2775          *  fdisk [-b sectorsize] [-u] device
2776          *
2777          * Options -C, -H, -S set the geometry.
2778          */
2779         enum {
2780                 OPT_b = 1 << 0,
2781                 OPT_C = 1 << 1,
2782                 OPT_H = 1 << 2,
2783                 OPT_l = 1 << 3,
2784                 OPT_S = 1 << 4,
2785                 OPT_u = 1 << 5,
2786                 OPT_s = (1 << 6) * ENABLE_FEATURE_FDISK_BLKSIZE,
2787         };
2788
2789         if (sizeof(G) > sizeof(bb_common_bufsiz1))
2790                 BUG_fdisk_globals_overflow();
2791
2792         opt = getopt32(argc, argv, "b:C:H:lS:u" USE_FEATURE_FDISK_BLKSIZE("s"),
2793                                 &str_b, &str_C, &str_H, &str_S);
2794         argc -= optind;
2795         argv += optind;
2796         if (opt & OPT_b) { // -b
2797                 /* Ugly: this sector size is really per device,
2798                    so cannot be combined with multiple disks,
2799                    and the same goes for the C/H/S options.
2800                 */
2801                 sector_size = xatoi_u(str_b);
2802                 if (sector_size != 512 && sector_size != 1024 &&
2803                         sector_size != 2048)
2804                         bb_show_usage();
2805                 sector_offset = 2;
2806                 user_set_sector_size = 1;
2807         }
2808         if (opt & OPT_C) user_cylinders = xatoi_u(str_C); // -C
2809         if (opt & OPT_H) { // -H
2810                 user_heads = xatoi_u(str_H);
2811                 if (user_heads <= 0 || user_heads >= 256)
2812                         user_heads = 0;
2813         }
2814         //if (opt & OPT_l) // -l
2815         if (opt & OPT_S) { // -S
2816                 user_sectors = xatoi_u(str_S);
2817                 if (user_sectors <= 0 || user_sectors >= 64)
2818                         user_sectors = 0;
2819         }
2820         if (opt & OPT_u) display_in_cyl_units = 0; // -u
2821         //if (opt & OPT_s) // -s
2822
2823         if (user_set_sector_size && argc != 1)
2824                 printf("Warning: the -b (set sector size) option should"
2825                          " be used with one specified device\n");
2826
2827 #if ENABLE_FEATURE_FDISK_WRITABLE
2828         if (opt & OPT_l) {
2829                 nowarn = 1;
2830 #endif
2831                 type_open = O_RDONLY;
2832                 if (argc > 0) {
2833                         int k;
2834 #if defined(__GNUC__)
2835                         /* avoid gcc warning:
2836                            variable `k' might be clobbered by `longjmp' */
2837                         (void)&k;
2838 #endif
2839                         listing = 1;
2840                         for (k = 0; k < argc; k++)
2841                                 trydev(argv[k], 1);
2842                 } else {
2843                         /* we no longer have default device names */
2844                         /* but, we can use /proc/partitions instead */
2845                         tryprocpt();
2846                 }
2847                 return 0;
2848 #if ENABLE_FEATURE_FDISK_WRITABLE
2849         }
2850 #endif
2851
2852 #if ENABLE_FEATURE_FDISK_BLKSIZE
2853         if (opt & OPT_s) {
2854                 long size;
2855                 int j;
2856
2857                 nowarn = 1;
2858                 type_open = O_RDONLY;
2859
2860                 if (argc <= 0)
2861                         bb_show_usage();
2862
2863                 for (j = 0; j < argc; j++) {
2864                         disk_device = argv[j];
2865                         fd = open(disk_device, type_open);
2866                         if (fd < 0)
2867                                 fdisk_fatal(unable_to_open);
2868                         if (ioctl(fd, BLKGETSIZE, &size))
2869                                 fdisk_fatal(ioctl_error);
2870                         close(fd);
2871                         if (argc == 1)
2872                                 printf("%ld\n", size/2);
2873                         else
2874                                 printf("%s: %ld\n", argv[j], size/2);
2875                 }
2876                 return 0;
2877         }
2878 #endif
2879
2880 #if ENABLE_FEATURE_FDISK_WRITABLE
2881         if (argc != 1)
2882                 bb_show_usage();
2883
2884         disk_device = argv[0];
2885         get_boot(fdisk);
2886
2887         if (LABEL_IS_OSF) {
2888                 /* OSF label, and no DOS label */
2889                 printf("Detected an OSF/1 disklabel on %s, entering "
2890                         "disklabel mode\n", disk_device);
2891                 bsd_select();
2892                 /*Why do we do this?  It seems to be counter-intuitive*/
2893                 current_label_type = label_dos;
2894                 /* If we return we may want to make an empty DOS label? */
2895         }
2896
2897         while (1) {
2898                 int c;
2899                 putchar('\n');
2900                 c = tolower(read_nonempty("Command (m for help): "));
2901                 switch (c) {
2902                 case 'a':
2903                         if (LABEL_IS_DOS)
2904                                 toggle_active(get_partition(1, partitions));
2905                         else if (LABEL_IS_SUN)
2906                                 toggle_sunflags(get_partition(1, partitions),
2907                                                 0x01);
2908                         else if (LABEL_IS_SGI)
2909                                 sgi_set_bootpartition(
2910                                         get_partition(1, partitions));
2911                         else
2912                                 unknown_command(c);
2913                         break;
2914                 case 'b':
2915                         if (LABEL_IS_SGI) {
2916                                 printf("\nThe current boot file is: %s\n",
2917                                         sgi_get_bootfile());
2918                                 if (read_maybe_empty("Please enter the name of the "
2919                                                    "new boot file: ") == '\n')
2920                                         printf("Boot file unchanged\n");
2921                                 else
2922                                         sgi_set_bootfile(line_ptr);
2923                         }
2924 #if ENABLE_FEATURE_OSF_LABEL
2925                         else
2926                                 bsd_select();
2927 #endif
2928                         break;
2929                 case 'c':
2930                         if (LABEL_IS_DOS)
2931                                 toggle_dos_compatibility_flag();
2932                         else if (LABEL_IS_SUN)
2933                                 toggle_sunflags(get_partition(1, partitions),
2934                                                 0x10);
2935                         else if (LABEL_IS_SGI)
2936                                 sgi_set_swappartition(
2937                                                 get_partition(1, partitions));
2938                         else
2939                                 unknown_command(c);
2940                         break;
2941                 case 'd':
2942                         {
2943                                 int j;
2944                         /* If sgi_label then don't use get_existing_partition,
2945                            let the user select a partition, since
2946                            get_existing_partition() only works for Linux-like
2947                            partition tables */
2948                                 if (!LABEL_IS_SGI) {
2949                                         j = get_existing_partition(1, partitions);
2950                                 } else {
2951                                         j = get_partition(1, partitions);
2952                                 }
2953                                 if (j >= 0)
2954                                         delete_partition(j);
2955                         }
2956                         break;
2957                 case 'i':
2958                         if (LABEL_IS_SGI)
2959                                 create_sgiinfo();
2960                         else
2961                                 unknown_command(c);
2962                 case 'l':
2963                         list_types(get_sys_types());
2964                         break;
2965                 case 'm':
2966                         menu();
2967                         break;
2968                 case 'n':
2969                         new_partition();
2970                         break;
2971                 case 'o':
2972                         create_doslabel();
2973                         break;
2974                 case 'p':
2975                         list_table(0);
2976                         break;
2977                 case 'q':
2978                         close(fd);
2979                         puts("");
2980                         return 0;
2981                 case 's':
2982 #if ENABLE_FEATURE_SUN_LABEL
2983                         create_sunlabel();
2984 #endif
2985                         break;
2986                 case 't':
2987                         change_sysid();
2988                         break;
2989                 case 'u':
2990                         change_units();
2991                         break;
2992                 case 'v':
2993                         verify();
2994                         break;
2995                 case 'w':
2996                         write_table();          /* does not return */
2997                         break;
2998 #if ENABLE_FEATURE_FDISK_ADVANCED
2999                 case 'x':
3000                         if (LABEL_IS_SGI) {
3001                                 printf("\n\tSorry, no experts menu for SGI "
3002                                         "partition tables available\n\n");
3003                         } else
3004                                 xselect();
3005                         break;
3006 #endif
3007                 default:
3008                         unknown_command(c);
3009                         menu();
3010                 }
3011         }
3012         return 0;
3013 #endif /* FEATURE_FDISK_WRITABLE */
3014 }