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