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