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