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