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