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