fdisk: tweak some messages
[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 static ALWAYS_INLINE struct partition *
477 get_part_table(int i)
478 {
479         return ptes[i].part_table;
480 }
481
482 static const char *
483 str_units(int n)
484 {      /* n==1: use singular */
485         if (n == 1)
486                 return display_in_cyl_units ? "cylinder" : "sector";
487         return display_in_cyl_units ? "cylinders" : "sectors";
488 }
489
490 static int
491 valid_part_table_flag(const char *mbuffer)
492 {
493         return (mbuffer[510] == 0x55 && (uint8_t)mbuffer[511] == 0xaa);
494 }
495
496 static void fdisk_fatal(const char *why)
497 {
498         if (listing) {
499                 close_dev_fd();
500                 longjmp(listingbuf, 1);
501         }
502         bb_error_msg_and_die(why, disk_device);
503 }
504
505 static void
506 seek_sector(sector_t secno)
507 {
508 #if ENABLE_FDISK_SUPPORT_LARGE_DISKS
509         off64_t off = (off64_t)secno * sector_size;
510         if (lseek64(dev_fd, off, SEEK_SET) == (off64_t) -1)
511                 fdisk_fatal(unable_to_seek);
512 #else
513         uint64_t off = (uint64_t)secno * sector_size;
514         if (off > MAXINT(off_t)
515          || lseek(dev_fd, (off_t)off, SEEK_SET) == (off_t) -1
516         ) {
517                 fdisk_fatal(unable_to_seek);
518         }
519 #endif
520 }
521
522 #if ENABLE_FEATURE_FDISK_WRITABLE
523 /* Read line; return 0 or first printable char */
524 static int
525 read_line(const char *prompt)
526 {
527         int sz;
528
529         sz = read_line_input(NULL, prompt, line_buffer, sizeof(line_buffer), /*timeout*/ -1);
530         if (sz <= 0)
531                 exit(EXIT_SUCCESS); /* Ctrl-D or Ctrl-C */
532
533         if (line_buffer[sz-1] == '\n')
534                 line_buffer[--sz] = '\0';
535
536         line_ptr = line_buffer;
537         while (*line_ptr != '\0' && (unsigned char)*line_ptr <= ' ')
538                 line_ptr++;
539         return *line_ptr;
540 }
541
542 static void
543 set_all_unchanged(void)
544 {
545         int i;
546
547         for (i = 0; i < MAXIMUM_PARTS; i++)
548                 ptes[i].changed = 0;
549 }
550
551 static ALWAYS_INLINE void
552 set_changed(int i)
553 {
554         ptes[i].changed = 1;
555 }
556
557 static ALWAYS_INLINE void
558 write_part_table_flag(char *b)
559 {
560         b[510] = 0x55;
561         b[511] = 0xaa;
562 }
563
564 static char
565 read_nonempty(const char *mesg)
566 {
567         while (!read_line(mesg))
568                 continue;
569         return *line_ptr;
570 }
571
572 static char
573 read_maybe_empty(const char *mesg)
574 {
575         if (!read_line(mesg)) {
576                 line_ptr = line_buffer;
577                 line_ptr[0] = '\n';
578                 line_ptr[1] = '\0';
579         }
580         return line_ptr[0];
581 }
582
583 static int
584 read_hex(const char *const *sys)
585 {
586         unsigned long v;
587         while (1) {
588                 read_nonempty("Hex code (type L to list codes): ");
589                 if ((line_ptr[0] | 0x20) == 'l') {
590                         list_types(sys);
591                         continue;
592                 }
593                 v = bb_strtoul(line_ptr, NULL, 16);
594                 if (v <= 0xff)
595                         return v;
596         }
597 }
598
599 static void
600 write_sector(sector_t secno, const void *buf)
601 {
602         seek_sector(secno);
603         xwrite(dev_fd, buf, sector_size);
604 }
605 #endif /* FEATURE_FDISK_WRITABLE */
606
607
608 #include "fdisk_aix.c"
609
610 struct sun_partition {
611         unsigned char info[128];   /* Informative text string */
612         unsigned char spare0[14];
613         struct sun_info {
614                 unsigned char spare1;
615                 unsigned char id;
616                 unsigned char spare2;
617                 unsigned char flags;
618         } infos[8];
619         unsigned char spare1[246]; /* Boot information etc. */
620         unsigned short rspeed;     /* Disk rotational speed */
621         unsigned short pcylcount;  /* Physical cylinder count */
622         unsigned short sparecyl;   /* extra sects per cylinder */
623         unsigned char spare2[4];   /* More magic... */
624         unsigned short ilfact;     /* Interleave factor */
625         unsigned short ncyl;       /* Data cylinder count */
626         unsigned short nacyl;      /* Alt. cylinder count */
627         unsigned short ntrks;      /* Tracks per cylinder */
628         unsigned short nsect;      /* Sectors per track */
629         unsigned char spare3[4];   /* Even more magic... */
630         struct sun_partinfo {
631                 uint32_t start_cylinder;
632                 uint32_t num_sectors;
633         } partitions[8];
634         unsigned short magic;      /* Magic number */
635         unsigned short csum;       /* Label xor'd checksum */
636 } FIX_ALIASING;
637 typedef struct sun_partition sun_partition;
638 #define sunlabel ((sun_partition *)MBRbuffer)
639 STATIC_OSF void bsd_select(void);
640 STATIC_OSF void xbsd_print_disklabel(int);
641 #include "fdisk_osf.c"
642
643 STATIC_GPT void gpt_list_table(int xtra);
644 #include "fdisk_gpt.c"
645
646 #if ENABLE_FEATURE_SGI_LABEL || ENABLE_FEATURE_SUN_LABEL
647 static uint16_t
648 fdisk_swap16(uint16_t x)
649 {
650         return (x << 8) | (x >> 8);
651 }
652
653 static uint32_t
654 fdisk_swap32(uint32_t x)
655 {
656         return (x << 24) |
657                ((x & 0xFF00) << 8) |
658                ((x & 0xFF0000) >> 8) |
659                (x >> 24);
660 }
661 #endif
662
663 STATIC_SGI const char *const sgi_sys_types[];
664 STATIC_SGI unsigned sgi_get_num_sectors(int i);
665 STATIC_SGI int sgi_get_sysid(int i);
666 STATIC_SGI void sgi_delete_partition(int i);
667 STATIC_SGI void sgi_change_sysid(int i, int sys);
668 STATIC_SGI void sgi_list_table(int xtra);
669 #if ENABLE_FEATURE_FDISK_ADVANCED
670 STATIC_SGI void sgi_set_xcyl(void);
671 #endif
672 STATIC_SGI int verify_sgi(int verbose);
673 STATIC_SGI void sgi_add_partition(int n, int sys);
674 STATIC_SGI void sgi_set_swappartition(int i);
675 STATIC_SGI const char *sgi_get_bootfile(void);
676 STATIC_SGI void sgi_set_bootfile(const char* aFile);
677 STATIC_SGI void create_sgiinfo(void);
678 STATIC_SGI void sgi_write_table(void);
679 STATIC_SGI void sgi_set_bootpartition(int i);
680 #include "fdisk_sgi.c"
681
682 STATIC_SUN const char *const sun_sys_types[];
683 STATIC_SUN void sun_delete_partition(int i);
684 STATIC_SUN void sun_change_sysid(int i, int sys);
685 STATIC_SUN void sun_list_table(int xtra);
686 STATIC_SUN void add_sun_partition(int n, int sys);
687 #if ENABLE_FEATURE_FDISK_ADVANCED
688 STATIC_SUN void sun_set_alt_cyl(void);
689 STATIC_SUN void sun_set_ncyl(int cyl);
690 STATIC_SUN void sun_set_xcyl(void);
691 STATIC_SUN void sun_set_ilfact(void);
692 STATIC_SUN void sun_set_rspeed(void);
693 STATIC_SUN void sun_set_pcylcount(void);
694 #endif
695 STATIC_SUN void toggle_sunflags(int i, unsigned char mask);
696 STATIC_SUN void verify_sun(void);
697 STATIC_SUN void sun_write_table(void);
698 #include "fdisk_sun.c"
699
700
701 static inline_if_little_endian unsigned
702 read4_little_endian(const unsigned char *cp)
703 {
704         uint32_t v;
705         move_from_unaligned32(v, cp);
706         return SWAP_LE32(v);
707 }
708
709 static sector_t
710 get_start_sect(const struct partition *p)
711 {
712         return read4_little_endian(p->start4);
713 }
714
715 static sector_t
716 get_nr_sects(const struct partition *p)
717 {
718         return read4_little_endian(p->size4);
719 }
720
721 #if ENABLE_FEATURE_FDISK_WRITABLE
722 /* start_sect and nr_sects are stored little endian on all machines */
723 /* moreover, they are not aligned correctly */
724 static inline_if_little_endian void
725 store4_little_endian(unsigned char *cp, unsigned val)
726 {
727         uint32_t v = SWAP_LE32(val);
728         move_to_unaligned32(cp, v);
729 }
730
731 static void
732 set_start_sect(struct partition *p, unsigned start_sect)
733 {
734         store4_little_endian(p->start4, start_sect);
735 }
736
737 static void
738 set_nr_sects(struct partition *p, unsigned nr_sects)
739 {
740         store4_little_endian(p->size4, nr_sects);
741 }
742 #endif
743
744 /* Allocate a buffer and read a partition table sector */
745 static void
746 read_pte(struct pte *pe, sector_t offset)
747 {
748         pe->offset_from_dev_start = offset;
749         pe->sectorbuffer = xzalloc(sector_size);
750         seek_sector(offset);
751         /* xread would make us abort - bad for fdisk -l */
752         if (full_read(dev_fd, pe->sectorbuffer, sector_size) != sector_size)
753                 fdisk_fatal(unable_to_read);
754 #if ENABLE_FEATURE_FDISK_WRITABLE
755         pe->changed = 0;
756 #endif
757         pe->part_table = pe->ext_pointer = NULL;
758 }
759
760 static sector_t
761 get_partition_start_from_dev_start(const struct pte *pe)
762 {
763         return pe->offset_from_dev_start + get_start_sect(pe->part_table);
764 }
765
766 #if ENABLE_FEATURE_FDISK_WRITABLE
767 /*
768  * Avoid warning about DOS partitions when no DOS partition was changed.
769  * Here a heuristic "is probably dos partition".
770  * We might also do the opposite and warn in all cases except
771  * for "is probably nondos partition".
772  */
773 #ifdef UNUSED
774 static int
775 is_dos_partition(int t)
776 {
777         return (t == 1 || t == 4 || t == 6 ||
778                 t == 0x0b || t == 0x0c || t == 0x0e ||
779                 t == 0x11 || t == 0x12 || t == 0x14 || t == 0x16 ||
780                 t == 0x1b || t == 0x1c || t == 0x1e || t == 0x24 ||
781                 t == 0xc1 || t == 0xc4 || t == 0xc6);
782 }
783 #endif
784
785 static void
786 menu(void)
787 {
788         puts("Command Action");
789         if (LABEL_IS_SUN) {
790                 puts("a\ttoggle a read only flag");           /* sun */
791                 puts("b\tedit bsd disklabel");
792                 puts("c\ttoggle the mountable flag");         /* sun */
793                 puts("d\tdelete a partition");
794                 puts("l\tlist known partition types");
795                 puts("n\tadd a new partition");
796                 puts("o\tcreate a new empty DOS partition table");
797                 puts("p\tprint the partition table");
798                 puts("q\tquit without saving changes");
799                 puts("s\tcreate a new empty Sun disklabel");  /* sun */
800                 puts("t\tchange a partition's system id");
801                 puts("u\tchange display/entry units");
802                 puts("v\tverify the partition table");
803                 puts("w\twrite table to disk and exit");
804 #if ENABLE_FEATURE_FDISK_ADVANCED
805                 puts("x\textra functionality (experts only)");
806 #endif
807         } else if (LABEL_IS_SGI) {
808                 puts("a\tselect bootable partition");    /* sgi flavour */
809                 puts("b\tedit bootfile entry");          /* sgi */
810                 puts("c\tselect sgi swap partition");    /* sgi flavour */
811                 puts("d\tdelete a partition");
812                 puts("l\tlist known partition types");
813                 puts("n\tadd a new partition");
814                 puts("o\tcreate a new empty DOS partition table");
815                 puts("p\tprint the partition table");
816                 puts("q\tquit without saving changes");
817                 puts("s\tcreate a new empty Sun disklabel");  /* sun */
818                 puts("t\tchange a partition's system id");
819                 puts("u\tchange display/entry units");
820                 puts("v\tverify the partition table");
821                 puts("w\twrite table to disk and exit");
822         } else if (LABEL_IS_AIX) {
823                 puts("o\tcreate a new empty DOS partition table");
824                 puts("q\tquit without saving changes");
825                 puts("s\tcreate a new empty Sun disklabel");  /* sun */
826         } else if (LABEL_IS_GPT) {
827                 puts("o\tcreate a new empty DOS partition table");
828                 puts("p\tprint the partition table");
829                 puts("q\tquit without saving changes");
830                 puts("s\tcreate a new empty Sun disklabel");  /* sun */
831         } else {
832                 puts("a\ttoggle a bootable flag");
833                 puts("b\tedit bsd disklabel");
834                 puts("c\ttoggle the dos compatibility flag");
835                 puts("d\tdelete a partition");
836                 puts("l\tlist known partition types");
837                 puts("n\tadd a new partition");
838                 puts("o\tcreate a new empty DOS partition table");
839                 puts("p\tprint the partition table");
840                 puts("q\tquit without saving changes");
841                 puts("s\tcreate a new empty Sun disklabel");  /* sun */
842                 puts("t\tchange a partition's system id");
843                 puts("u\tchange display/entry units");
844                 puts("v\tverify the partition table");
845                 puts("w\twrite table to disk and exit");
846 #if ENABLE_FEATURE_FDISK_ADVANCED
847                 puts("x\textra functionality (experts only)");
848 #endif
849         }
850 }
851 #endif /* FEATURE_FDISK_WRITABLE */
852
853
854 #if ENABLE_FEATURE_FDISK_ADVANCED
855 static void
856 xmenu(void)
857 {
858         puts("Command Action");
859         if (LABEL_IS_SUN) {
860                 puts("a\tchange number of alternate cylinders");      /*sun*/
861                 puts("c\tchange number of cylinders");
862                 puts("d\tprint the raw data in the partition table");
863                 puts("e\tchange number of extra sectors per cylinder");/*sun*/
864                 puts("h\tchange number of heads");
865                 puts("i\tchange interleave factor");                  /*sun*/
866                 puts("o\tchange rotation speed (rpm)");               /*sun*/
867                 puts("p\tprint the partition table");
868                 puts("q\tquit without saving changes");
869                 puts("r\treturn to main menu");
870                 puts("s\tchange number of sectors/track");
871                 puts("v\tverify the partition table");
872                 puts("w\twrite table to disk and exit");
873                 puts("y\tchange number of physical cylinders");       /*sun*/
874         } else if (LABEL_IS_SGI) {
875                 puts("b\tmove beginning of data in a partition"); /* !sun */
876                 puts("c\tchange number of cylinders");
877                 puts("d\tprint the raw data in the partition table");
878                 puts("e\tlist extended partitions");          /* !sun */
879                 puts("g\tcreate an IRIX (SGI) partition table");/* sgi */
880                 puts("h\tchange number of heads");
881                 puts("p\tprint the partition table");
882                 puts("q\tquit without saving changes");
883                 puts("r\treturn to main menu");
884                 puts("s\tchange number of sectors/track");
885                 puts("v\tverify the partition table");
886                 puts("w\twrite table to disk and exit");
887         } else if (LABEL_IS_AIX) {
888                 puts("b\tmove beginning of data in a partition"); /* !sun */
889                 puts("c\tchange number of cylinders");
890                 puts("d\tprint the raw data in the partition table");
891                 puts("e\tlist extended partitions");          /* !sun */
892                 puts("g\tcreate an IRIX (SGI) partition table");/* sgi */
893                 puts("h\tchange number of heads");
894                 puts("p\tprint the partition table");
895                 puts("q\tquit without saving changes");
896                 puts("r\treturn to main menu");
897                 puts("s\tchange number of sectors/track");
898                 puts("v\tverify the partition table");
899                 puts("w\twrite table to disk and exit");
900         } else {
901                 puts("b\tmove beginning of data in a partition"); /* !sun */
902                 puts("c\tchange number of cylinders");
903                 puts("d\tprint the raw data in the partition table");
904                 puts("e\tlist extended partitions");          /* !sun */
905                 puts("f\tfix partition order");               /* !sun, !aix, !sgi */
906 #if ENABLE_FEATURE_SGI_LABEL
907                 puts("g\tcreate an IRIX (SGI) partition table");/* sgi */
908 #endif
909                 puts("h\tchange number of heads");
910                 puts("p\tprint the partition table");
911                 puts("q\tquit without saving changes");
912                 puts("r\treturn to main menu");
913                 puts("s\tchange number of sectors/track");
914                 puts("v\tverify the partition table");
915                 puts("w\twrite table to disk and exit");
916         }
917 }
918 #endif /* ADVANCED mode */
919
920 #if ENABLE_FEATURE_FDISK_WRITABLE
921 static const char *const *
922 get_sys_types(void)
923 {
924         return (
925                 LABEL_IS_SUN ? sun_sys_types :
926                 LABEL_IS_SGI ? sgi_sys_types :
927                 i386_sys_types);
928 }
929 #else
930 #define get_sys_types() i386_sys_types
931 #endif
932
933 static const char *
934 partition_type(unsigned char type)
935 {
936         int i;
937         const char *const *types = get_sys_types();
938
939         for (i = 0; types[i]; i++)
940                 if ((unsigned char)types[i][0] == type)
941                         return types[i] + 1;
942
943         return "Unknown";
944 }
945
946 static int
947 is_cleared_partition(const struct partition *p)
948 {
949         /* We consider partition "cleared" only if it has only zeros */
950         const char *cp = (const char *)p;
951         int cnt = sizeof(*p);
952         char bits = 0;
953         while (--cnt >= 0)
954                 bits |= *cp++;
955         return (bits == 0);
956 }
957
958 static void
959 clear_partition(struct partition *p)
960 {
961         if (p)
962                 memset(p, 0, sizeof(*p));
963 }
964
965 #if ENABLE_FEATURE_FDISK_WRITABLE
966 static int
967 get_sysid(int i)
968 {
969         return LABEL_IS_SUN ? sunlabel->infos[i].id :
970                         (LABEL_IS_SGI ? sgi_get_sysid(i) :
971                                 ptes[i].part_table->sys_ind);
972 }
973
974 static void
975 list_types(const char *const *sys)
976 {
977         enum { COLS = 3 };
978
979         unsigned last[COLS];
980         unsigned done, next, size;
981         int i;
982
983         for (size = 0; sys[size]; size++)
984                 continue;
985
986         done = 0;
987         for (i = COLS-1; i >= 0; i--) {
988                 done += (size + i - done) / (i + 1);
989                 last[COLS-1 - i] = done;
990         }
991
992         i = done = next = 0;
993         do {
994                 printf("%c%2x %-22.22s", i ? ' ' : '\n',
995                         (unsigned char)sys[next][0],
996                         sys[next] + 1);
997                 next = last[i++] + done;
998                 if (i >= COLS || next >= last[i]) {
999                         i = 0;
1000                         next = ++done;
1001                 }
1002         } while (done < last[0]);
1003         bb_putchar('\n');
1004 }
1005
1006 #define set_hsc(h, s, c, sector) do \
1007 { \
1008         s = sector % g_sectors + 1;  \
1009         sector /= g_sectors;         \
1010         h = sector % g_heads;        \
1011         sector /= g_heads;           \
1012         c = sector & 0xff;           \
1013         s |= (sector >> 2) & 0xc0;   \
1014 } while (0)
1015
1016 static void set_hsc_start_end(struct partition *p, sector_t start, sector_t stop)
1017 {
1018         if (dos_compatible_flag && (start / (g_sectors * g_heads) > 1023))
1019                 start = g_heads * g_sectors * 1024 - 1;
1020         set_hsc(p->head, p->sector, p->cyl, start);
1021
1022         if (dos_compatible_flag && (stop / (g_sectors * g_heads) > 1023))
1023                 stop = g_heads * g_sectors * 1024 - 1;
1024         set_hsc(p->end_head, p->end_sector, p->end_cyl, stop);
1025 }
1026
1027 static void
1028 set_partition(int i, int doext, sector_t start, sector_t stop, int sysid)
1029 {
1030         struct partition *p;
1031         sector_t offset;
1032
1033         if (doext) {
1034                 p = ptes[i].ext_pointer;
1035                 offset = extended_offset;
1036         } else {
1037                 p = ptes[i].part_table;
1038                 offset = ptes[i].offset_from_dev_start;
1039         }
1040         p->boot_ind = 0;
1041         p->sys_ind = sysid;
1042         set_start_sect(p, start - offset);
1043         set_nr_sects(p, stop - start + 1);
1044         set_hsc_start_end(p, start, stop);
1045         ptes[i].changed = 1;
1046 }
1047 #endif
1048
1049 static int
1050 warn_geometry(void)
1051 {
1052         if (g_heads && g_sectors && g_cylinders)
1053                 return 0;
1054
1055         printf("Unknown value(s) for:");
1056         if (!g_heads)
1057                 printf(" heads");
1058         if (!g_sectors)
1059                 printf(" sectors");
1060         if (!g_cylinders)
1061                 printf(" cylinders");
1062 #if ENABLE_FEATURE_FDISK_WRITABLE
1063         puts(" (settable in the extra functions menu)");
1064 #else
1065         bb_putchar('\n');
1066 #endif
1067         return 1;
1068 }
1069
1070 static void
1071 update_units(void)
1072 {
1073         int cyl_units = g_heads * g_sectors;
1074
1075         if (display_in_cyl_units && cyl_units)
1076                 units_per_sector = cyl_units;
1077         else
1078                 units_per_sector = 1;   /* in sectors */
1079 }
1080
1081 #if ENABLE_FEATURE_FDISK_WRITABLE
1082 static void
1083 warn_cylinders(void)
1084 {
1085         if (LABEL_IS_DOS && g_cylinders > 1024 && !nowarn)
1086                 printf("\n"
1087 "The number of cylinders for this disk is set to %u.\n"
1088 "There is nothing wrong with that, but this is larger than 1024,\n"
1089 "and could in certain setups cause problems with:\n"
1090 "1) software that runs at boot time (e.g., old versions of LILO)\n"
1091 "2) booting and partitioning software from other OSs\n"
1092 "   (e.g., DOS FDISK, OS/2 FDISK)\n",
1093                         g_cylinders);
1094 }
1095 #endif
1096
1097 static void
1098 read_extended(int ext)
1099 {
1100         int i;
1101         struct pte *pex;
1102         struct partition *p, *q;
1103
1104         ext_index = ext;
1105         pex = &ptes[ext];
1106         pex->ext_pointer = pex->part_table;
1107
1108         p = pex->part_table;
1109         if (!get_start_sect(p)) {
1110                 puts("Bad offset in primary extended partition");
1111                 return;
1112         }
1113
1114         while (IS_EXTENDED(p->sys_ind)) {
1115                 struct pte *pe = &ptes[g_partitions];
1116
1117                 if (g_partitions >= MAXIMUM_PARTS) {
1118                         /* This is not a Linux restriction, but
1119                            this program uses arrays of size MAXIMUM_PARTS.
1120                            Do not try to 'improve' this test. */
1121                         struct pte *pre = &ptes[g_partitions - 1];
1122 #if ENABLE_FEATURE_FDISK_WRITABLE
1123                         printf("Warning: deleting partitions after %u\n",
1124                                 g_partitions);
1125                         pre->changed = 1;
1126 #endif
1127                         clear_partition(pre->ext_pointer);
1128                         return;
1129                 }
1130
1131                 read_pte(pe, extended_offset + get_start_sect(p));
1132
1133                 if (!extended_offset)
1134                         extended_offset = get_start_sect(p);
1135
1136                 q = p = pt_offset(pe->sectorbuffer, 0);
1137                 for (i = 0; i < 4; i++, p++) if (get_nr_sects(p)) {
1138                         if (IS_EXTENDED(p->sys_ind)) {
1139                                 if (pe->ext_pointer)
1140                                         printf("Warning: extra link "
1141                                                 "pointer in partition table"
1142                                                 " %u\n", g_partitions + 1);
1143                                 else
1144                                         pe->ext_pointer = p;
1145                         } else if (p->sys_ind) {
1146                                 if (pe->part_table)
1147                                         printf("Warning: ignoring extra "
1148                                                   "data in partition table"
1149                                                   " %u\n", g_partitions + 1);
1150                                 else
1151                                         pe->part_table = p;
1152                         }
1153                 }
1154
1155                 /* very strange code here... */
1156                 if (!pe->part_table) {
1157                         if (q != pe->ext_pointer)
1158                                 pe->part_table = q;
1159                         else
1160                                 pe->part_table = q + 1;
1161                 }
1162                 if (!pe->ext_pointer) {
1163                         if (q != pe->part_table)
1164                                 pe->ext_pointer = q;
1165                         else
1166                                 pe->ext_pointer = q + 1;
1167                 }
1168
1169                 p = pe->ext_pointer;
1170                 g_partitions++;
1171         }
1172
1173 #if ENABLE_FEATURE_FDISK_WRITABLE
1174         /* remove empty links */
1175  remove:
1176         for (i = 4; i < g_partitions; i++) {
1177                 struct pte *pe = &ptes[i];
1178
1179                 if (!get_nr_sects(pe->part_table)
1180                  && (g_partitions > 5 || ptes[4].part_table->sys_ind)
1181                 ) {
1182                         printf("Omitting empty partition (%u)\n", i+1);
1183                         delete_partition(i);
1184                         goto remove;    /* numbering changed */
1185                 }
1186         }
1187 #endif
1188 }
1189
1190 #if ENABLE_FEATURE_FDISK_WRITABLE
1191 static void
1192 create_doslabel(void)
1193 {
1194         printf(msg_building_new_label, "DOS disklabel");
1195
1196         current_label_type = LABEL_DOS;
1197 #if ENABLE_FEATURE_OSF_LABEL
1198         possibly_osf_label = 0;
1199 #endif
1200         g_partitions = 4;
1201
1202         memset(&MBRbuffer[510 - 4*16], 0, 4*16);
1203         write_part_table_flag(MBRbuffer);
1204         extended_offset = 0;
1205         set_all_unchanged();
1206         set_changed(0);
1207         get_boot(CREATE_EMPTY_DOS);
1208 }
1209 #endif
1210
1211 static void
1212 get_sectorsize(void)
1213 {
1214         if (!user_set_sector_size) {
1215                 int arg;
1216                 if (ioctl(dev_fd, BLKSSZGET, &arg) == 0)
1217                         sector_size = arg;
1218                 if (sector_size != DEFAULT_SECTOR_SIZE)
1219                         printf("Note: sector size is %u "
1220                                 "(not " DEFAULT_SECTOR_SIZE_STR ")\n",
1221                                 sector_size);
1222         }
1223 }
1224
1225 static void
1226 get_kernel_geometry(void)
1227 {
1228         struct hd_geometry geometry;
1229
1230         if (!ioctl(dev_fd, HDIO_GETGEO, &geometry)) {
1231                 kern_heads = geometry.heads;
1232                 kern_sectors = geometry.sectors;
1233                 /* never use geometry.cylinders - it is truncated */
1234         }
1235 }
1236
1237 static void
1238 get_partition_table_geometry(void)
1239 {
1240         const unsigned char *bufp = (const unsigned char *)MBRbuffer;
1241         struct partition *p;
1242         int i, h, s, hh, ss;
1243         int first = 1;
1244         int bad = 0;
1245
1246         if (!(valid_part_table_flag((char*)bufp)))
1247                 return;
1248
1249         hh = ss = 0;
1250         for (i = 0; i < 4; i++) {
1251                 p = pt_offset(bufp, i);
1252                 if (p->sys_ind != 0) {
1253                         h = p->end_head + 1;
1254                         s = (p->end_sector & 077);
1255                         if (first) {
1256                                 hh = h;
1257                                 ss = s;
1258                                 first = 0;
1259                         } else if (hh != h || ss != s)
1260                                 bad = 1;
1261                 }
1262         }
1263
1264         if (!first && !bad) {
1265                 pt_heads = hh;
1266                 pt_sectors = ss;
1267         }
1268 }
1269
1270 static void
1271 get_geometry(void)
1272 {
1273         int sec_fac;
1274
1275         get_sectorsize();
1276         sec_fac = sector_size / 512;
1277 #if ENABLE_FEATURE_SUN_LABEL
1278         guess_device_type();
1279 #endif
1280         g_heads = g_cylinders = g_sectors = 0;
1281         kern_heads = kern_sectors = 0;
1282         pt_heads = pt_sectors = 0;
1283
1284         get_kernel_geometry();
1285         get_partition_table_geometry();
1286
1287         g_heads = user_heads ? user_heads :
1288                 pt_heads ? pt_heads :
1289                 kern_heads ? kern_heads : 255;
1290         g_sectors = user_sectors ? user_sectors :
1291                 pt_sectors ? pt_sectors :
1292                 kern_sectors ? kern_sectors : 63;
1293         total_number_of_sectors = bb_BLKGETSIZE_sectors(dev_fd);
1294
1295         sector_offset = 1;
1296         if (dos_compatible_flag)
1297                 sector_offset = g_sectors;
1298
1299         g_cylinders = total_number_of_sectors / (g_heads * g_sectors * sec_fac);
1300         if (!g_cylinders)
1301                 g_cylinders = user_cylinders;
1302 }
1303
1304 /*
1305  * Opens disk_device and optionally reads MBR.
1306  *    If what == OPEN_MAIN:
1307  *      Open device, read MBR.  Abort program on short read.  Create empty
1308  *      disklabel if the on-disk structure is invalid (WRITABLE mode).
1309  *    If what == TRY_ONLY:
1310  *      Open device, read MBR.  Return an error if anything is out of place.
1311  *      Do not create an empty disklabel.  This is used for the "list"
1312  *      operations: "fdisk -l /dev/sda" and "fdisk -l" (all devices).
1313  *    If what == CREATE_EMPTY_*:
1314  *      This means that get_boot() was called recursively from create_*label().
1315  *      Do not re-open the device; just set up the ptes array and print
1316  *      geometry warnings.
1317  *
1318  * Returns:
1319  *   -1: no 0xaa55 flag present (possibly entire disk BSD)
1320  *    0: found or created label
1321  *    1: I/O error
1322  */
1323 #if ENABLE_FEATURE_SUN_LABEL || ENABLE_FEATURE_FDISK_WRITABLE
1324 static int get_boot(enum action what)
1325 #else
1326 static int get_boot(void)
1327 #define get_boot(what) get_boot()
1328 #endif
1329 {
1330         int i, fd;
1331
1332         g_partitions = 4;
1333         for (i = 0; i < 4; i++) {
1334                 struct pte *pe = &ptes[i];
1335                 pe->part_table = pt_offset(MBRbuffer, i);
1336                 pe->ext_pointer = NULL;
1337                 pe->offset_from_dev_start = 0;
1338                 pe->sectorbuffer = MBRbuffer;
1339 #if ENABLE_FEATURE_FDISK_WRITABLE
1340                 pe->changed = (what == CREATE_EMPTY_DOS);
1341 #endif
1342         }
1343
1344 #if ENABLE_FEATURE_FDISK_WRITABLE
1345 // ALERT! highly idiotic design!
1346 // We end up here when we call get_boot() recursively
1347 // via get_boot() [table is bad] -> create_doslabel() -> get_boot(CREATE_EMPTY_DOS).
1348 // or get_boot() [table is bad] -> create_sunlabel() -> get_boot(CREATE_EMPTY_SUN).
1349 // (just factor out re-init of ptes[0,1,2,3] in a separate fn instead?)
1350 // So skip opening device _again_...
1351         if (what == CREATE_EMPTY_DOS  IF_FEATURE_SUN_LABEL(|| what == CREATE_EMPTY_SUN))
1352                 goto created_table;
1353
1354         fd = open(disk_device, (option_mask32 & OPT_l) ? O_RDONLY : O_RDWR);
1355
1356         if (fd < 0) {
1357                 fd = open(disk_device, O_RDONLY);
1358                 if (fd < 0) {
1359                         if (what == TRY_ONLY)
1360                                 return 1;
1361                         fdisk_fatal(unable_to_open);
1362                 }
1363                 printf("'%s' is opened for read only\n", disk_device);
1364         }
1365         xmove_fd(fd, dev_fd);
1366         if (512 != full_read(dev_fd, MBRbuffer, 512)) {
1367                 if (what == TRY_ONLY) {
1368                         close_dev_fd();
1369                         return 1;
1370                 }
1371                 fdisk_fatal(unable_to_read);
1372         }
1373 #else
1374         fd = open(disk_device, O_RDONLY);
1375         if (fd < 0)
1376                 return 1;
1377         if (512 != full_read(fd, MBRbuffer, 512)) {
1378                 close(fd);
1379                 return 1;
1380         }
1381         xmove_fd(fd, dev_fd);
1382 #endif
1383
1384         get_geometry();
1385         update_units();
1386
1387 #if ENABLE_FEATURE_SUN_LABEL
1388         if (check_sun_label())
1389                 return 0;
1390 #endif
1391 #if ENABLE_FEATURE_SGI_LABEL
1392         if (check_sgi_label())
1393                 return 0;
1394 #endif
1395 #if ENABLE_FEATURE_AIX_LABEL
1396         if (check_aix_label())
1397                 return 0;
1398 #endif
1399 #if ENABLE_FEATURE_GPT_LABEL
1400         if (check_gpt_label())
1401                 return 0;
1402 #endif
1403 #if ENABLE_FEATURE_OSF_LABEL
1404         if (check_osf_label()) {
1405                 possibly_osf_label = 1;
1406                 if (!valid_part_table_flag(MBRbuffer)) {
1407                         current_label_type = LABEL_OSF;
1408                         return 0;
1409                 }
1410                 puts("This disk has both DOS and BSD magic.\n"
1411                      "Give the 'b' command to go to BSD mode.");
1412         }
1413 #endif
1414
1415 #if !ENABLE_FEATURE_FDISK_WRITABLE
1416         if (!valid_part_table_flag(MBRbuffer))
1417                 return -1;
1418 #else
1419         if (!valid_part_table_flag(MBRbuffer)) {
1420                 if (what == OPEN_MAIN) {
1421                         puts("Device contains neither a valid DOS "
1422                              "partition table, nor Sun, SGI, OSF or GPT "
1423                              "disklabel");
1424 #ifdef __sparc__
1425                         IF_FEATURE_SUN_LABEL(create_sunlabel();)
1426 #else
1427                         create_doslabel();
1428 #endif
1429                         return 0;
1430                 }
1431                 /* TRY_ONLY: */
1432                 return -1;
1433         }
1434  created_table:
1435 #endif /* FEATURE_FDISK_WRITABLE */
1436
1437
1438         IF_FEATURE_FDISK_WRITABLE(warn_cylinders();)
1439         warn_geometry();
1440
1441         for (i = 0; i < 4; i++) {
1442                 if (IS_EXTENDED(ptes[i].part_table->sys_ind)) {
1443                         if (g_partitions != 4)
1444                                 printf("Ignoring extra extended "
1445                                         "partition %u\n", i + 1);
1446                         else
1447                                 read_extended(i);
1448                 }
1449         }
1450
1451         for (i = 3; i < g_partitions; i++) {
1452                 struct pte *pe = &ptes[i];
1453                 if (!valid_part_table_flag(pe->sectorbuffer)) {
1454                         printf("Warning: invalid flag 0x%02x,0x%02x of partition "
1455                                 "table %u will be corrected by w(rite)\n",
1456                                 pe->sectorbuffer[510],
1457                                 pe->sectorbuffer[511],
1458                                 i + 1);
1459                         IF_FEATURE_FDISK_WRITABLE(pe->changed = 1;)
1460                 }
1461         }
1462
1463         return 0;
1464 }
1465
1466 #if ENABLE_FEATURE_FDISK_WRITABLE
1467 /*
1468  * Print the message MESG, then read an integer between LOW and HIGH (inclusive).
1469  * If the user hits Enter, DFLT is returned.
1470  * Answers like +10 are interpreted as offsets from BASE.
1471  *
1472  * There is no default if DFLT is not between LOW and HIGH.
1473  */
1474 static sector_t
1475 read_int(sector_t low, sector_t dflt, sector_t high, sector_t base, const char *mesg)
1476 {
1477         sector_t value;
1478         int default_ok = 1;
1479         const char *fmt = "%s (%u-%u, default %u): ";
1480
1481         if (dflt < low || dflt > high) {
1482                 fmt = "%s (%u-%u): ";
1483                 default_ok = 0;
1484         }
1485
1486         while (1) {
1487                 int use_default = default_ok;
1488
1489                 /* ask question and read answer */
1490                 do {
1491                         printf(fmt, mesg, low, high, dflt);
1492                         read_maybe_empty("");
1493                 } while (*line_ptr != '\n' && !isdigit(*line_ptr)
1494                  && *line_ptr != '-' && *line_ptr != '+');
1495
1496                 if (*line_ptr == '+' || *line_ptr == '-') {
1497                         int minus = (*line_ptr == '-');
1498                         int absolute = 0;
1499
1500                         value = atoi(line_ptr + 1);
1501
1502                         /* (1) if 2nd char is digit, use_default = 0.
1503                          * (2) move line_ptr to first non-digit. */
1504                         while (isdigit(*++line_ptr))
1505                                 use_default = 0;
1506
1507                         switch (*line_ptr) {
1508                         case 'c':
1509                         case 'C':
1510                                 if (!display_in_cyl_units)
1511                                         value *= g_heads * g_sectors;
1512                                 break;
1513                         case 'K':
1514                                 absolute = 1024;
1515                                 break;
1516                         case 'k':
1517                                 absolute = 1000;
1518                                 break;
1519                         case 'm':
1520                         case 'M':
1521                                 absolute = 1000000;
1522                                 break;
1523                         case 'g':
1524                         case 'G':
1525                                 absolute = 1000000000;
1526                                 break;
1527                         default:
1528                                 break;
1529                         }
1530                         if (absolute) {
1531                                 ullong bytes;
1532                                 unsigned long unit;
1533
1534                                 bytes = (ullong) value * absolute;
1535                                 unit = sector_size * units_per_sector;
1536                                 bytes += unit/2; /* round */
1537                                 bytes /= unit;
1538                                 value = bytes;
1539                         }
1540                         if (minus)
1541                                 value = -value;
1542                         value += base;
1543                 } else {
1544                         value = atoi(line_ptr);
1545                         while (isdigit(*line_ptr)) {
1546                                 line_ptr++;
1547                                 use_default = 0;
1548                         }
1549                 }
1550                 if (use_default) {
1551                         value = dflt;
1552                         printf("Using default value %u\n", value);
1553                 }
1554                 if (value >= low && value <= high)
1555                         break;
1556                 puts("Value is out of range");
1557         }
1558         return value;
1559 }
1560
1561 static unsigned
1562 get_partition(int warn, unsigned max)
1563 {
1564         struct pte *pe;
1565         unsigned i;
1566
1567         i = read_int(1, 0, max, 0, "Partition number") - 1;
1568         pe = &ptes[i];
1569
1570         if (warn) {
1571                 if ((!LABEL_IS_SUN && !LABEL_IS_SGI && !pe->part_table->sys_ind)
1572                  || (LABEL_IS_SUN && (!sunlabel->partitions[i].num_sectors || !sunlabel->infos[i].id))
1573                  || (LABEL_IS_SGI && !sgi_get_num_sectors(i))
1574                 ) {
1575                         printf("Warning: partition %u has empty type\n", i+1);
1576                 }
1577         }
1578         return i;
1579 }
1580
1581 static int
1582 get_existing_partition(int warn, unsigned max)
1583 {
1584         int pno = -1;
1585         unsigned i;
1586
1587         for (i = 0; i < max; i++) {
1588                 struct pte *pe = &ptes[i];
1589                 struct partition *p = pe->part_table;
1590
1591                 if (p && !is_cleared_partition(p)) {
1592                         if (pno >= 0)
1593                                 goto not_unique;
1594                         pno = i;
1595                 }
1596         }
1597         if (pno >= 0) {
1598                 printf("Selected partition %u\n", pno+1);
1599                 return pno;
1600         }
1601         puts("No partition is defined yet!");
1602         return -1;
1603
1604  not_unique:
1605         return get_partition(warn, max);
1606 }
1607
1608 static int
1609 get_nonexisting_partition(int warn, unsigned max)
1610 {
1611         int pno = -1;
1612         unsigned i;
1613
1614         for (i = 0; i < max; i++) {
1615                 struct pte *pe = &ptes[i];
1616                 struct partition *p = pe->part_table;
1617
1618                 if (p && is_cleared_partition(p)) {
1619                         if (pno >= 0)
1620                                 goto not_unique;
1621                         pno = i;
1622                 }
1623         }
1624         if (pno >= 0) {
1625                 printf("Selected partition %u\n", pno+1);
1626                 return pno;
1627         }
1628         puts("All primary partitions have been defined already!");
1629         return -1;
1630
1631  not_unique:
1632         return get_partition(warn, max);
1633 }
1634
1635
1636 static void
1637 change_units(void)
1638 {
1639         display_in_cyl_units = !display_in_cyl_units;
1640         update_units();
1641         printf("Changing display/entry units to %s\n",
1642                 str_units(PLURAL));
1643 }
1644
1645 static void
1646 toggle_active(int i)
1647 {
1648         struct pte *pe = &ptes[i];
1649         struct partition *p = pe->part_table;
1650
1651         if (IS_EXTENDED(p->sys_ind) && !p->boot_ind)
1652                 printf("WARNING: Partition %u is an extended partition\n", i + 1);
1653         p->boot_ind = (p->boot_ind ? 0 : ACTIVE_FLAG);
1654         pe->changed = 1;
1655 }
1656
1657 static void
1658 toggle_dos_compatibility_flag(void)
1659 {
1660         dos_compatible_flag = 1 - dos_compatible_flag;
1661         if (dos_compatible_flag) {
1662                 sector_offset = g_sectors;
1663                 printf("DOS Compatibility flag is %sset\n", "");
1664         } else {
1665                 sector_offset = 1;
1666                 printf("DOS Compatibility flag is %sset\n", "not ");
1667         }
1668 }
1669
1670 static void
1671 delete_partition(int i)
1672 {
1673         struct pte *pe = &ptes[i];
1674         struct partition *p = pe->part_table;
1675         struct partition *q = pe->ext_pointer;
1676
1677 /* Note that for the fifth partition (i == 4) we don't actually
1678  * decrement partitions.
1679  */
1680
1681         if (warn_geometry())
1682                 return;         /* C/H/S not set */
1683         pe->changed = 1;
1684
1685         if (LABEL_IS_SUN) {
1686                 sun_delete_partition(i);
1687                 return;
1688         }
1689         if (LABEL_IS_SGI) {
1690                 sgi_delete_partition(i);
1691                 return;
1692         }
1693
1694         if (i < 4) {
1695                 if (IS_EXTENDED(p->sys_ind) && i == ext_index) {
1696                         g_partitions = 4;
1697                         ptes[ext_index].ext_pointer = NULL;
1698                         extended_offset = 0;
1699                 }
1700                 clear_partition(p);
1701                 return;
1702         }
1703
1704         if (!q->sys_ind && i > 4) {
1705                 /* the last one in the chain - just delete */
1706                 --g_partitions;
1707                 --i;
1708                 clear_partition(ptes[i].ext_pointer);
1709                 ptes[i].changed = 1;
1710         } else {
1711                 /* not the last one - further ones will be moved down */
1712                 if (i > 4) {
1713                         /* delete this link in the chain */
1714                         p = ptes[i-1].ext_pointer;
1715                         *p = *q;
1716                         set_start_sect(p, get_start_sect(q));
1717                         set_nr_sects(p, get_nr_sects(q));
1718                         ptes[i-1].changed = 1;
1719                 } else if (g_partitions > 5) {    /* 5 will be moved to 4 */
1720                         /* the first logical in a longer chain */
1721                         pe = &ptes[5];
1722
1723                         if (pe->part_table) /* prevent SEGFAULT */
1724                                 set_start_sect(pe->part_table,
1725                                                 get_partition_start_from_dev_start(pe) -
1726                                                 extended_offset);
1727                         pe->offset_from_dev_start = extended_offset;
1728                         pe->changed = 1;
1729                 }
1730
1731                 if (g_partitions > 5) {
1732                         g_partitions--;
1733                         while (i < g_partitions) {
1734                                 ptes[i] = ptes[i+1];
1735                                 i++;
1736                         }
1737                 } else {
1738                         /* the only logical: clear only */
1739                         clear_partition(ptes[i].part_table);
1740                 }
1741         }
1742 }
1743
1744 static void
1745 change_sysid(void)
1746 {
1747         int i, sys, origsys;
1748         struct partition *p;
1749
1750         /* If sgi_label then don't use get_existing_partition,
1751            let the user select a partition, since get_existing_partition()
1752            only works for Linux like partition tables. */
1753         if (!LABEL_IS_SGI) {
1754                 i = get_existing_partition(0, g_partitions);
1755         } else {
1756                 i = get_partition(0, g_partitions);
1757         }
1758         if (i == -1)
1759                 return;
1760         p = ptes[i].part_table;
1761         origsys = sys = get_sysid(i);
1762
1763         /* if changing types T to 0 is allowed, then
1764            the reverse change must be allowed, too */
1765         if (!sys && !LABEL_IS_SGI && !LABEL_IS_SUN && !get_nr_sects(p)) {
1766                 printf("Partition %u does not exist yet!\n", i + 1);
1767                 return;
1768         }
1769         while (1) {
1770                 sys = read_hex(get_sys_types());
1771
1772                 if (!sys && !LABEL_IS_SGI && !LABEL_IS_SUN) {
1773                         puts("Type 0 means free space to many systems\n"
1774                                 "(but not to Linux). Having partitions of\n"
1775                                 "type 0 is probably unwise.");
1776                         /* break; */
1777                 }
1778
1779                 if (!LABEL_IS_SUN && !LABEL_IS_SGI) {
1780                         if (IS_EXTENDED(sys) != IS_EXTENDED(p->sys_ind)) {
1781                                 puts("You cannot change a partition into"
1782                                         " an extended one or vice versa");
1783                                 break;
1784                         }
1785                 }
1786
1787                 if (sys < 256) {
1788 #if ENABLE_FEATURE_SUN_LABEL
1789                         if (LABEL_IS_SUN && i == 2 && sys != SUN_WHOLE_DISK)
1790                                 puts("Consider leaving partition 3 "
1791                                         "as Whole disk (5),\n"
1792                                         "as SunOS/Solaris expects it and "
1793                                         "even Linux likes it\n");
1794 #endif
1795 #if ENABLE_FEATURE_SGI_LABEL
1796                         if (LABEL_IS_SGI &&
1797                                 (
1798                                         (i == 10 && sys != SGI_ENTIRE_DISK) ||
1799                                         (i == 8 && sys != 0)
1800                                 )
1801                         ) {
1802                                 puts("Consider leaving partition 9 "
1803                                         "as volume header (0),\nand "
1804                                         "partition 11 as entire volume (6)"
1805                                         "as IRIX expects it\n");
1806                         }
1807 #endif
1808                         if (sys == origsys)
1809                                 break;
1810                         if (LABEL_IS_SUN) {
1811                                 sun_change_sysid(i, sys);
1812                         } else if (LABEL_IS_SGI) {
1813                                 sgi_change_sysid(i, sys);
1814                         } else
1815                                 p->sys_ind = sys;
1816
1817                         printf("Changed system type of partition %u "
1818                                 "to %x (%s)\n", i + 1, sys,
1819                                 partition_type(sys));
1820                         ptes[i].changed = 1;
1821                         //if (is_dos_partition(origsys) || is_dos_partition(sys))
1822                         //      dos_changed = 1;
1823                         break;
1824                 }
1825         }
1826 }
1827 #endif /* FEATURE_FDISK_WRITABLE */
1828
1829
1830 /* check_consistency() and linear2chs() added Sat Mar 6 12:28:16 1993,
1831  * faith@cs.unc.edu, based on code fragments from pfdisk by Gordon W. Ross,
1832  * Jan.  1990 (version 1.2.1 by Gordon W. Ross Aug. 1990; Modified by S.
1833  * Lubkin Oct.  1991). */
1834
1835 static void
1836 linear2chs(unsigned ls, unsigned *c, unsigned *h, unsigned *s)
1837 {
1838         int spc = g_heads * g_sectors;
1839
1840         *c = ls / spc;
1841         ls = ls % spc;
1842         *h = ls / g_sectors;
1843         *s = ls % g_sectors + 1;  /* sectors count from 1 */
1844 }
1845
1846 static void
1847 check_consistency(const struct partition *p, int partition)
1848 {
1849         unsigned pbc, pbh, pbs;          /* physical beginning c, h, s */
1850         unsigned pec, peh, pes;          /* physical ending c, h, s */
1851         unsigned lbc, lbh, lbs;          /* logical beginning c, h, s */
1852         unsigned lec, leh, les;          /* logical ending c, h, s */
1853
1854         if (!g_heads || !g_sectors || (partition >= 4))
1855                 return;         /* do not check extended partitions */
1856
1857 /* physical beginning c, h, s */
1858         pbc = cylinder(p->sector, p->cyl);
1859         pbh = p->head;
1860         pbs = sector(p->sector);
1861
1862 /* physical ending c, h, s */
1863         pec = cylinder(p->end_sector, p->end_cyl);
1864         peh = p->end_head;
1865         pes = sector(p->end_sector);
1866
1867 /* compute logical beginning (c, h, s) */
1868         linear2chs(get_start_sect(p), &lbc, &lbh, &lbs);
1869
1870 /* compute logical ending (c, h, s) */
1871         linear2chs(get_start_sect(p) + get_nr_sects(p) - 1, &lec, &leh, &les);
1872
1873 /* Same physical / logical beginning? */
1874         if (g_cylinders <= 1024 && (pbc != lbc || pbh != lbh || pbs != lbs)) {
1875                 printf("Partition %u has different physical/logical "
1876                         "start (non-Linux?):\n", partition + 1);
1877                 printf("     phys=(%u,%u,%u) ", pbc, pbh, pbs);
1878                 printf("logical=(%u,%u,%u)\n", lbc, lbh, lbs);
1879         }
1880
1881 /* Same physical / logical ending? */
1882         if (g_cylinders <= 1024 && (pec != lec || peh != leh || pes != les)) {
1883                 printf("Partition %u has different physical/logical "
1884                         "end:\n", partition + 1);
1885                 printf("     phys=(%u,%u,%u) ", pec, peh, pes);
1886                 printf("logical=(%u,%u,%u)\n", lec, leh, les);
1887         }
1888
1889 /* Ending on cylinder boundary? */
1890         if (peh != (g_heads - 1) || pes != g_sectors) {
1891                 printf("Partition %u does not end on cylinder boundary\n",
1892                         partition + 1);
1893         }
1894 }
1895
1896 static void
1897 list_disk_geometry(void)
1898 {
1899         ullong bytes = ((ullong)total_number_of_sectors << 9);
1900         ullong xbytes = bytes / (1024*1024);
1901         char x = 'M';
1902
1903         if (xbytes >= 10000) {
1904                 xbytes += 512; /* fdisk util-linux 2.28 does this */
1905                 xbytes /= 1024;
1906                 x = 'G';
1907         }
1908         printf("Disk %s: %llu %cB, %llu bytes, %"SECT_FMT"u sectors\n"
1909                 "%u cylinders, %u heads, %u sectors/track\n"
1910                 "Units: %s of %u * %u = %u bytes\n\n",
1911                 disk_device, xbytes, x,
1912                 bytes, total_number_of_sectors,
1913                 g_cylinders, g_heads, g_sectors,
1914                 str_units(PLURAL),
1915                 units_per_sector, sector_size, units_per_sector * sector_size
1916         );
1917 }
1918
1919 /*
1920  * Check whether partition entries are ordered by their starting positions.
1921  * Return 0 if OK. Return i if partition i should have been earlier.
1922  * Two separate checks: primary and logical partitions.
1923  */
1924 static int
1925 wrong_p_order(int *prev)
1926 {
1927         const struct pte *pe;
1928         const struct partition *p;
1929         sector_t last_p_start_pos = 0, p_start_pos;
1930         unsigned i, last_i = 0;
1931
1932         for (i = 0; i < g_partitions; i++) {
1933                 if (i == 4) {
1934                         last_i = 4;
1935                         last_p_start_pos = 0;
1936                 }
1937                 pe = &ptes[i];
1938                 p = pe->part_table;
1939                 if (p->sys_ind) {
1940                         p_start_pos = get_partition_start_from_dev_start(pe);
1941
1942                         if (last_p_start_pos > p_start_pos) {
1943                                 if (prev)
1944                                         *prev = last_i;
1945                                 return i;
1946                         }
1947
1948                         last_p_start_pos = p_start_pos;
1949                         last_i = i;
1950                 }
1951         }
1952         return 0;
1953 }
1954
1955 #if ENABLE_FEATURE_FDISK_ADVANCED
1956 /*
1957  * Fix the chain of logicals.
1958  * extended_offset is unchanged, the set of sectors used is unchanged
1959  * The chain is sorted so that sectors increase, and so that
1960  * starting sectors increase.
1961  *
1962  * After this it may still be that cfdisk doesnt like the table.
1963  * (This is because cfdisk considers expanded parts, from link to
1964  * end of partition, and these may still overlap.)
1965  * Now
1966  *   sfdisk /dev/hda > ohda; sfdisk /dev/hda < ohda
1967  * may help.
1968  */
1969 static void
1970 fix_chain_of_logicals(void)
1971 {
1972         int j, oj, ojj, sj, sjj;
1973         struct partition *pj,*pjj,tmp;
1974
1975         /* Stage 1: sort sectors but leave sector of part 4 */
1976         /* (Its sector is the global extended_offset.) */
1977  stage1:
1978         for (j = 5; j < g_partitions - 1; j++) {
1979                 oj = ptes[j].offset_from_dev_start;
1980                 ojj = ptes[j+1].offset_from_dev_start;
1981                 if (oj > ojj) {
1982                         ptes[j].offset_from_dev_start = ojj;
1983                         ptes[j+1].offset_from_dev_start = oj;
1984                         pj = ptes[j].part_table;
1985                         set_start_sect(pj, get_start_sect(pj)+oj-ojj);
1986                         pjj = ptes[j+1].part_table;
1987                         set_start_sect(pjj, get_start_sect(pjj)+ojj-oj);
1988                         set_start_sect(ptes[j-1].ext_pointer,
1989                                            ojj-extended_offset);
1990                         set_start_sect(ptes[j].ext_pointer,
1991                                            oj-extended_offset);
1992                         goto stage1;
1993                 }
1994         }
1995
1996         /* Stage 2: sort starting sectors */
1997  stage2:
1998         for (j = 4; j < g_partitions - 1; j++) {
1999                 pj = ptes[j].part_table;
2000                 pjj = ptes[j+1].part_table;
2001                 sj = get_start_sect(pj);
2002                 sjj = get_start_sect(pjj);
2003                 oj = ptes[j].offset_from_dev_start;
2004                 ojj = ptes[j+1].offset_from_dev_start;
2005                 if (oj+sj > ojj+sjj) {
2006                         tmp = *pj;
2007                         *pj = *pjj;
2008                         *pjj = tmp;
2009                         set_start_sect(pj, ojj+sjj-oj);
2010                         set_start_sect(pjj, oj+sj-ojj);
2011                         goto stage2;
2012                 }
2013         }
2014
2015         /* Probably something was changed */
2016         for (j = 4; j < g_partitions; j++)
2017                 ptes[j].changed = 1;
2018 }
2019
2020
2021 static void
2022 fix_partition_table_order(void)
2023 {
2024         struct pte *pei, *pek;
2025         int i,k;
2026
2027         if (!wrong_p_order(NULL)) {
2028                 puts("Ordering is already correct\n");
2029                 return;
2030         }
2031
2032         while ((i = wrong_p_order(&k)) != 0 && i < 4) {
2033                 /* partition i should have come earlier, move it */
2034                 /* We have to move data in the MBR */
2035                 struct partition *pi, *pk, *pe, pbuf;
2036                 pei = &ptes[i];
2037                 pek = &ptes[k];
2038
2039                 pe = pei->ext_pointer;
2040                 pei->ext_pointer = pek->ext_pointer;
2041                 pek->ext_pointer = pe;
2042
2043                 pi = pei->part_table;
2044                 pk = pek->part_table;
2045
2046                 memmove(&pbuf, pi, sizeof(struct partition));
2047                 memmove(pi, pk, sizeof(struct partition));
2048                 memmove(pk, &pbuf, sizeof(struct partition));
2049
2050                 pei->changed = pek->changed = 1;
2051         }
2052
2053         if (i)
2054                 fix_chain_of_logicals();
2055
2056         puts("Done");
2057 }
2058 #endif
2059
2060 /* Return partition name */
2061 static const char *
2062 partname(const char *dev, int pno, int lth)
2063 {
2064         const char *p;
2065         int w, wp;
2066         int bufsiz;
2067         char *bufp;
2068
2069         bufp = auto_string(xzalloc(80));
2070         bufsiz = 80;
2071
2072         w = strlen(dev);
2073         p = "";
2074
2075         if (isdigit(dev[w-1]))
2076                 p = "p";
2077
2078         /* devfs kludge - note: fdisk partition names are not supposed
2079            to equal kernel names, so there is no reason to do this */
2080         if (strcmp(dev + w - 4, "disc") == 0) {
2081                 w -= 4;
2082                 p = "part";
2083         }
2084
2085         wp = strlen(p);
2086
2087         if (lth) {
2088                 snprintf(bufp, bufsiz, "%*.*s%s%-2u",
2089                         lth-wp-2, w, dev, p, pno);
2090         } else {
2091                 snprintf(bufp, bufsiz, "%.*s%s%-2u", w, dev, p, pno);
2092         }
2093         return bufp;
2094 }
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 }