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