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