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