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