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