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