whitespace fixes
[platform/upstream/busybox.git] / util-linux / fdisk.c
1 /* vi: set sw=4 ts=4: */
2 /* fdisk.c -- Partition table manipulator for Linux.
3  *
4  * Copyright (C) 1992  A. V. Le Blanc (LeBlanc@mcc.ac.uk)
5  * Copyright (C) 2001,2002 Vladimir Oleynik <dzo@simtreas.ru> (initial bb port)
6  *
7  * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
8  */
9
10 #ifndef _LARGEFILE64_SOURCE
11 /* For lseek64 */
12 # define _LARGEFILE64_SOURCE
13 #endif
14 #include <assert.h>             /* assert */
15 #include <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 }
2048 #endif
2049
2050 static void
2051 list_table(int xtra)
2052 {
2053         const struct partition *p;
2054         int i, w;
2055
2056         if (LABEL_IS_SUN) {
2057                 sun_list_table(xtra);
2058                 return;
2059         }
2060         if (LABEL_IS_SUN) {
2061                 sgi_list_table(xtra);
2062                 return;
2063         }
2064
2065         list_disk_geometry();
2066
2067         if (LABEL_IS_OSF) {
2068                 xbsd_print_disklabel(xtra);
2069                 return;
2070         }
2071
2072         /* Heuristic: we list partition 3 of /dev/foo as /dev/foo3,
2073            but if the device name ends in a digit, say /dev/foo1,
2074            then the partition is called /dev/foo1p3. */
2075         w = strlen(disk_device);
2076         if (w && isdigit(disk_device[w-1]))
2077                 w++;
2078         if (w < 5)
2079                 w = 5;
2080
2081         //            1 12345678901 12345678901 12345678901  12
2082         printf("%*s Boot      Start         End      Blocks  Id System\n",
2083                    w+1, "Device");
2084
2085         for (i = 0; i < g_partitions; i++) {
2086                 const struct pte *pe = &ptes[i];
2087                 sector_t psects;
2088                 sector_t pblocks;
2089                 unsigned podd;
2090
2091                 p = pe->part_table;
2092                 if (!p || is_cleared_partition(p))
2093                         continue;
2094
2095                 psects = get_nr_sects(p);
2096                 pblocks = psects;
2097                 podd = 0;
2098
2099                 if (sector_size < 1024) {
2100                         pblocks /= (1024 / sector_size);
2101                         podd = psects % (1024 / sector_size);
2102                 }
2103                 if (sector_size > 1024)
2104                         pblocks *= (sector_size / 1024);
2105
2106                 printf("%s  %c %11"SECT_FMT"u %11"SECT_FMT"u %11"SECT_FMT"u%c %2x %s\n",
2107                         partname(disk_device, i+1, w+2),
2108                         !p->boot_ind ? ' ' : p->boot_ind == ACTIVE_FLAG /* boot flag */
2109                                 ? '*' : '?',
2110                         cround(get_partition_start_from_dev_start(pe)),           /* start */
2111                         cround(get_partition_start_from_dev_start(pe) + psects    /* end */
2112                                 - (psects ? 1 : 0)),
2113                         pblocks, podd ? '+' : ' ', /* odd flag on end */
2114                         p->sys_ind,                                     /* type id */
2115                         partition_type(p->sys_ind));                    /* type name */
2116
2117                 check_consistency(p, i);
2118         }
2119
2120         /* Is partition table in disk order? It need not be, but... */
2121         /* partition table entries are not checked for correct order
2122          * if this is a sgi, sun or aix labeled disk... */
2123         if (LABEL_IS_DOS && wrong_p_order(NULL)) {
2124                 /* FIXME */
2125                 printf("\nPartition table entries are not in disk order\n");
2126         }
2127 }
2128
2129 #if ENABLE_FEATURE_FDISK_ADVANCED
2130 static void
2131 x_list_table(int extend)
2132 {
2133         const struct pte *pe;
2134         const struct partition *p;
2135         int i;
2136
2137         printf("\nDisk %s: %u heads, %u sectors, %u cylinders\n\n",
2138                 disk_device, g_heads, g_sectors, g_cylinders);
2139         printf("Nr AF  Hd Sec  Cyl  Hd Sec  Cyl      Start       Size ID\n");
2140         for (i = 0; i < g_partitions; i++) {
2141                 pe = &ptes[i];
2142                 p = (extend ? pe->ext_pointer : pe->part_table);
2143                 if (p != NULL) {
2144                         printf("%2u %02x%4u%4u%5u%4u%4u%5u%11"SECT_FMT"u%11"SECT_FMT"u %02x\n",
2145                                 i + 1, p->boot_ind, p->head,
2146                                 sector(p->sector),
2147                                 cylinder(p->sector, p->cyl), p->end_head,
2148                                 sector(p->end_sector),
2149                                 cylinder(p->end_sector, p->end_cyl),
2150                                 get_start_sect(p), get_nr_sects(p),
2151                                 p->sys_ind);
2152                         if (p->sys_ind)
2153                                 check_consistency(p, i);
2154                 }
2155         }
2156 }
2157 #endif
2158
2159 #if ENABLE_FEATURE_FDISK_WRITABLE
2160 static void
2161 fill_bounds(sector_t *first, sector_t *last)
2162 {
2163         unsigned i;
2164         const struct pte *pe = &ptes[0];
2165         const struct partition *p;
2166
2167         for (i = 0; i < g_partitions; pe++,i++) {
2168                 p = pe->part_table;
2169                 if (!p->sys_ind || IS_EXTENDED(p->sys_ind)) {
2170                         first[i] = 0xffffffff;
2171                         last[i] = 0;
2172                 } else {
2173                         first[i] = get_partition_start_from_dev_start(pe);
2174                         last[i] = first[i] + get_nr_sects(p) - 1;
2175                 }
2176         }
2177 }
2178
2179 static void
2180 check(int n, unsigned h, unsigned s, unsigned c, sector_t start)
2181 {
2182         sector_t total, real_s, real_c;
2183
2184         real_s = sector(s) - 1;
2185         real_c = cylinder(s, c);
2186         total = (real_c * g_sectors + real_s) * g_heads + h;
2187         if (!total)
2188                 printf("Partition %u contains sector 0\n", n);
2189         if (h >= g_heads)
2190                 printf("Partition %u: head %u greater than maximum %u\n",
2191                         n, h + 1, g_heads);
2192         if (real_s >= g_sectors)
2193                 printf("Partition %u: sector %u greater than "
2194                         "maximum %u\n", n, s, g_sectors);
2195         if (real_c >= g_cylinders)
2196                 printf("Partition %u: cylinder %"SECT_FMT"u greater than "
2197                         "maximum %u\n", n, real_c + 1, g_cylinders);
2198         if (g_cylinders <= 1024 && start != total)
2199                 printf("Partition %u: previous sectors %"SECT_FMT"u disagrees with "
2200                         "total %"SECT_FMT"u\n", n, start, total);
2201 }
2202
2203 static void
2204 verify(void)
2205 {
2206         int i, j;
2207         sector_t total = 1;
2208         sector_t first[g_partitions], last[g_partitions];
2209         struct partition *p;
2210
2211         if (warn_geometry())
2212                 return;
2213
2214         if (LABEL_IS_SUN) {
2215                 verify_sun();
2216                 return;
2217         }
2218         if (LABEL_IS_SGI) {
2219                 verify_sgi(1);
2220                 return;
2221         }
2222
2223         fill_bounds(first, last);
2224         for (i = 0; i < g_partitions; i++) {
2225                 struct pte *pe = &ptes[i];
2226
2227                 p = pe->part_table;
2228                 if (p->sys_ind && !IS_EXTENDED(p->sys_ind)) {
2229                         check_consistency(p, i);
2230                         if (get_partition_start_from_dev_start(pe) < first[i])
2231                                 printf("Warning: bad start-of-data in "
2232                                         "partition %u\n", i + 1);
2233                         check(i + 1, p->end_head, p->end_sector, p->end_cyl,
2234                                 last[i]);
2235                         total += last[i] + 1 - first[i];
2236                         for (j = 0; j < i; j++) {
2237                                 if ((first[i] >= first[j] && first[i] <= last[j])
2238                                  || ((last[i] <= last[j] && last[i] >= first[j]))) {
2239                                         printf("Warning: partition %u overlaps "
2240                                                 "partition %u\n", j + 1, i + 1);
2241                                         total += first[i] >= first[j] ?
2242                                                 first[i] : first[j];
2243                                         total -= last[i] <= last[j] ?
2244                                                 last[i] : last[j];
2245                                 }
2246                         }
2247                 }
2248         }
2249
2250         if (extended_offset) {
2251                 struct pte *pex = &ptes[ext_index];
2252                 sector_t e_last = get_start_sect(pex->part_table) +
2253                         get_nr_sects(pex->part_table) - 1;
2254
2255                 for (i = 4; i < g_partitions; i++) {
2256                         total++;
2257                         p = ptes[i].part_table;
2258                         if (!p->sys_ind) {
2259                                 if (i != 4 || i + 1 < g_partitions)
2260                                         printf("Warning: partition %u "
2261                                                 "is empty\n", i + 1);
2262                         } else if (first[i] < extended_offset || last[i] > e_last) {
2263                                 printf("Logical partition %u not entirely in "
2264                                         "partition %u\n", i + 1, ext_index + 1);
2265                         }
2266                 }
2267         }
2268
2269         if (total > g_heads * g_sectors * g_cylinders)
2270                 printf("Total allocated sectors %u greater than the maximum "
2271                         "%u\n", total, g_heads * g_sectors * g_cylinders);
2272         else {
2273                 total = g_heads * g_sectors * g_cylinders - total;
2274                 if (total != 0)
2275                         printf("%"SECT_FMT"u unallocated sectors\n", total);
2276         }
2277 }
2278
2279 static void
2280 add_partition(int n, int sys)
2281 {
2282         char mesg[256];         /* 48 does not suffice in Japanese */
2283         int i, num_read = 0;
2284         struct partition *p = ptes[n].part_table;
2285         struct partition *q = ptes[ext_index].part_table;
2286         sector_t limit, temp;
2287         sector_t start, stop = 0;
2288         sector_t first[g_partitions], last[g_partitions];
2289
2290         if (p && p->sys_ind) {
2291                 printf(msg_part_already_defined, n + 1);
2292                 return;
2293         }
2294         fill_bounds(first, last);
2295         if (n < 4) {
2296                 start = sector_offset;
2297                 if (display_in_cyl_units || !total_number_of_sectors)
2298                         limit = (sector_t) g_heads * g_sectors * g_cylinders - 1;
2299                 else
2300                         limit = total_number_of_sectors - 1;
2301                 if (extended_offset) {
2302                         first[ext_index] = extended_offset;
2303                         last[ext_index] = get_start_sect(q) +
2304                                 get_nr_sects(q) - 1;
2305                 }
2306         } else {
2307                 start = extended_offset + sector_offset;
2308                 limit = get_start_sect(q) + get_nr_sects(q) - 1;
2309         }
2310         if (display_in_cyl_units)
2311                 for (i = 0; i < g_partitions; i++)
2312                         first[i] = (cround(first[i]) - 1) * units_per_sector;
2313
2314         snprintf(mesg, sizeof(mesg), "First %s", str_units(SINGULAR));
2315         do {
2316                 temp = start;
2317                 for (i = 0; i < g_partitions; i++) {
2318                         int lastplusoff;
2319
2320                         if (start == ptes[i].offset_from_dev_start)
2321                                 start += sector_offset;
2322                         lastplusoff = last[i] + ((n < 4) ? 0 : sector_offset);
2323                         if (start >= first[i] && start <= lastplusoff)
2324                                 start = lastplusoff + 1;
2325                 }
2326                 if (start > limit)
2327                         break;
2328                 if (start >= temp+units_per_sector && num_read) {
2329                         printf("Sector %"SECT_FMT"u is already allocated\n", temp);
2330                         temp = start;
2331                         num_read = 0;
2332                 }
2333                 if (!num_read && start == temp) {
2334                         sector_t saved_start;
2335
2336                         saved_start = start;
2337                         start = read_int(cround(saved_start), cround(saved_start), cround(limit), 0, mesg);
2338                         if (display_in_cyl_units) {
2339                                 start = (start - 1) * units_per_sector;
2340                                 if (start < saved_start)
2341                                         start = saved_start;
2342                         }
2343                         num_read = 1;
2344                 }
2345         } while (start != temp || !num_read);
2346         if (n > 4) {                    /* NOT for fifth partition */
2347                 struct pte *pe = &ptes[n];
2348
2349                 pe->offset_from_dev_start = start - sector_offset;
2350                 if (pe->offset_from_dev_start == extended_offset) { /* must be corrected */
2351                         pe->offset_from_dev_start++;
2352                         if (sector_offset == 1)
2353                                 start++;
2354                 }
2355         }
2356
2357         for (i = 0; i < g_partitions; i++) {
2358                 struct pte *pe = &ptes[i];
2359
2360                 if (start < pe->offset_from_dev_start && limit >= pe->offset_from_dev_start)
2361                         limit = pe->offset_from_dev_start - 1;
2362                 if (start < first[i] && limit >= first[i])
2363                         limit = first[i] - 1;
2364         }
2365         if (start > limit) {
2366                 printf("No free sectors available\n");
2367                 if (n > 4)
2368                         g_partitions--;
2369                 return;
2370         }
2371         if (cround(start) == cround(limit)) {
2372                 stop = limit;
2373         } else {
2374                 snprintf(mesg, sizeof(mesg),
2375                          "Last %s or +size or +sizeM or +sizeK",
2376                          str_units(SINGULAR));
2377                 stop = read_int(cround(start), cround(limit), cround(limit), cround(start), mesg);
2378                 if (display_in_cyl_units) {
2379                         stop = stop * units_per_sector - 1;
2380                         if (stop >limit)
2381                                 stop = limit;
2382                 }
2383         }
2384
2385         set_partition(n, 0, start, stop, sys);
2386         if (n > 4)
2387                 set_partition(n - 1, 1, ptes[n].offset_from_dev_start, stop, EXTENDED);
2388
2389         if (IS_EXTENDED(sys)) {
2390                 struct pte *pe4 = &ptes[4];
2391                 struct pte *pen = &ptes[n];
2392
2393                 ext_index = n;
2394                 pen->ext_pointer = p;
2395                 pe4->offset_from_dev_start = extended_offset = start;
2396                 pe4->sectorbuffer = xzalloc(sector_size);
2397                 pe4->part_table = pt_offset(pe4->sectorbuffer, 0);
2398                 pe4->ext_pointer = pe4->part_table + 1;
2399                 pe4->changed = 1;
2400                 g_partitions = 5;
2401         }
2402 }
2403
2404 static void
2405 add_logical(void)
2406 {
2407         if (g_partitions > 5 || ptes[4].part_table->sys_ind) {
2408                 struct pte *pe = &ptes[g_partitions];
2409
2410                 pe->sectorbuffer = xzalloc(sector_size);
2411                 pe->part_table = pt_offset(pe->sectorbuffer, 0);
2412                 pe->ext_pointer = pe->part_table + 1;
2413                 pe->offset_from_dev_start = 0;
2414                 pe->changed = 1;
2415                 g_partitions++;
2416         }
2417         add_partition(g_partitions - 1, LINUX_NATIVE);
2418 }
2419
2420 static void
2421 new_partition(void)
2422 {
2423         int i, free_primary = 0;
2424
2425         if (warn_geometry())
2426                 return;
2427
2428         if (LABEL_IS_SUN) {
2429                 add_sun_partition(get_partition(0, g_partitions), LINUX_NATIVE);
2430                 return;
2431         }
2432         if (LABEL_IS_SGI) {
2433                 sgi_add_partition(get_partition(0, g_partitions), LINUX_NATIVE);
2434                 return;
2435         }
2436         if (LABEL_IS_AIX) {
2437                 printf("Sorry - this fdisk cannot handle AIX disk labels.\n"
2438 "If you want to add DOS-type partitions, create a new empty DOS partition\n"
2439 "table first (use 'o'). This will destroy the present disk contents.\n");
2440                 return;
2441         }
2442
2443         for (i = 0; i < 4; i++)
2444                 free_primary += !ptes[i].part_table->sys_ind;
2445
2446         if (!free_primary && g_partitions >= MAXIMUM_PARTS) {
2447                 printf("The maximum number of partitions has been created\n");
2448                 return;
2449         }
2450
2451         if (!free_primary) {
2452                 if (extended_offset)
2453                         add_logical();
2454                 else
2455                         printf("You must delete some partition and add "
2456                                  "an extended partition first\n");
2457         } else {
2458                 char c, line[80];
2459                 snprintf(line, sizeof(line),
2460                         "Command action\n"
2461                         "   %s\n"
2462                         "   p   primary partition (1-4)\n",
2463                         (extended_offset ?
2464                         "l   logical (5 or over)" : "e   extended"));
2465                 while (1) {
2466                         c = read_nonempty(line);
2467                         if ((c | 0x20) == 'p') {
2468                                 i = get_nonexisting_partition(0, 4);
2469                                 if (i >= 0)
2470                                         add_partition(i, LINUX_NATIVE);
2471                                 return;
2472                         }
2473                         if (c == 'l' && extended_offset) {
2474                                 add_logical();
2475                                 return;
2476                         }
2477                         if (c == 'e' && !extended_offset) {
2478                                 i = get_nonexisting_partition(0, 4);
2479                                 if (i >= 0)
2480                                         add_partition(i, EXTENDED);
2481                                 return;
2482                         }
2483                         printf("Invalid partition number "
2484                                          "for type '%c'\n", c);
2485                 }
2486         }
2487 }
2488
2489 static void
2490 write_table(void)
2491 {
2492         int i;
2493
2494         if (LABEL_IS_DOS) {
2495                 for (i = 0; i < 3; i++)
2496                         if (ptes[i].changed)
2497                                 ptes[3].changed = 1;
2498                 for (i = 3; i < g_partitions; i++) {
2499                         struct pte *pe = &ptes[i];
2500
2501                         if (pe->changed) {
2502                                 write_part_table_flag(pe->sectorbuffer);
2503                                 write_sector(pe->offset_from_dev_start, pe->sectorbuffer);
2504                         }
2505                 }
2506         }
2507         else if (LABEL_IS_SGI) {
2508                 /* no test on change? the printf below might be mistaken */
2509                 sgi_write_table();
2510         }
2511         else if (LABEL_IS_SUN) {
2512                 int needw = 0;
2513
2514                 for (i = 0; i < 8; i++)
2515                         if (ptes[i].changed)
2516                                 needw = 1;
2517                 if (needw)
2518                         sun_write_table();
2519         }
2520
2521         printf("The partition table has been altered!\n\n");
2522         reread_partition_table(1);
2523 }
2524
2525 static void
2526 reread_partition_table(int leave)
2527 {
2528         int i;
2529
2530         printf("Calling ioctl() to re-read partition table\n");
2531         sync();
2532         /* sleep(2); Huh? */
2533         i = ioctl_or_perror(dev_fd, BLKRRPART, NULL,
2534                         "WARNING: rereading partition table "
2535                         "failed, kernel still uses old table");
2536 #if 0
2537         if (dos_changed)
2538                 printf(
2539                 "\nWARNING: If you have created or modified any DOS 6.x\n"
2540                 "partitions, please see the fdisk manual page for additional\n"
2541                 "information\n");
2542 #endif
2543
2544         if (leave) {
2545                 if (ENABLE_FEATURE_CLEAN_UP)
2546                         close_dev_fd();
2547                 exit(i != 0);
2548         }
2549 }
2550 #endif /* FEATURE_FDISK_WRITABLE */
2551
2552 #if ENABLE_FEATURE_FDISK_ADVANCED
2553 #define MAX_PER_LINE    16
2554 static void
2555 print_buffer(char *pbuffer)
2556 {
2557         int i,l;
2558
2559         for (i = 0, l = 0; i < sector_size; i++, l++) {
2560                 if (l == 0)
2561                         printf("0x%03X:", i);
2562                 printf(" %02X", (unsigned char) pbuffer[i]);
2563                 if (l == MAX_PER_LINE - 1) {
2564                         bb_putchar('\n');
2565                         l = -1;
2566                 }
2567         }
2568         if (l > 0)
2569                 bb_putchar('\n');
2570         bb_putchar('\n');
2571 }
2572
2573 static void
2574 print_raw(void)
2575 {
2576         int i;
2577
2578         printf("Device: %s\n", disk_device);
2579         if (LABEL_IS_SGI || LABEL_IS_SUN)
2580                 print_buffer(MBRbuffer);
2581         else {
2582                 for (i = 3; i < g_partitions; i++)
2583                         print_buffer(ptes[i].sectorbuffer);
2584         }
2585 }
2586
2587 static void
2588 move_begin(unsigned i)
2589 {
2590         struct pte *pe = &ptes[i];
2591         struct partition *p = pe->part_table;
2592         sector_t new, first, nr_sects;
2593
2594         if (warn_geometry())
2595                 return;
2596         nr_sects = get_nr_sects(p);
2597         if (!p->sys_ind || !nr_sects || IS_EXTENDED(p->sys_ind)) {
2598                 printf("Partition %u has no data area\n", i + 1);
2599                 return;
2600         }
2601         first = get_partition_start_from_dev_start(pe); /* == pe->offset_from_dev_start + get_start_sect(p) */
2602         new = read_int(0 /*was:first*/, first, first + nr_sects - 1, first, "New beginning of data");
2603         if (new != first) {
2604                 sector_t new_relative = new - pe->offset_from_dev_start;
2605                 nr_sects += (get_start_sect(p) - new_relative);
2606                 set_start_sect(p, new_relative);
2607                 set_nr_sects(p, nr_sects);
2608                 read_nonempty("Recalculate C/H/S values? (Y/N): ");
2609                 if ((line_ptr[0] | 0x20) == 'y')
2610                         set_hsc_start_end(p, new, new + nr_sects - 1);
2611                 pe->changed = 1;
2612         }
2613 }
2614
2615 static void
2616 xselect(void)
2617 {
2618         char c;
2619
2620         while (1) {
2621                 bb_putchar('\n');
2622                 c = 0x20 | read_nonempty("Expert command (m for help): ");
2623                 switch (c) {
2624                 case 'a':
2625                         if (LABEL_IS_SUN)
2626                                 sun_set_alt_cyl();
2627                         break;
2628                 case 'b':
2629                         if (LABEL_IS_DOS)
2630                                 move_begin(get_partition(0, g_partitions));
2631                         break;
2632                 case 'c':
2633                         user_cylinders = g_cylinders =
2634                                 read_int(1, g_cylinders, 1048576, 0,
2635                                         "Number of cylinders");
2636                         if (LABEL_IS_SUN)
2637                                 sun_set_ncyl(g_cylinders);
2638                         if (LABEL_IS_DOS)
2639                                 warn_cylinders();
2640                         break;
2641                 case 'd':
2642                         print_raw();
2643                         break;
2644                 case 'e':
2645                         if (LABEL_IS_SGI)
2646                                 sgi_set_xcyl();
2647                         else if (LABEL_IS_SUN)
2648                                 sun_set_xcyl();
2649                         else if (LABEL_IS_DOS)
2650                                 x_list_table(1);
2651                         break;
2652                 case 'f':
2653                         if (LABEL_IS_DOS)
2654                                 fix_partition_table_order();
2655                         break;
2656                 case 'g':
2657 #if ENABLE_FEATURE_SGI_LABEL
2658                         create_sgilabel();
2659 #endif
2660                         break;
2661                 case 'h':
2662                         user_heads = g_heads = read_int(1, g_heads, 256, 0, "Number of heads");
2663                         update_units();
2664                         break;
2665                 case 'i':
2666                         if (LABEL_IS_SUN)
2667                                 sun_set_ilfact();
2668                         break;
2669                 case 'o':
2670                         if (LABEL_IS_SUN)
2671                                 sun_set_rspeed();
2672                         break;
2673                 case 'p':
2674                         if (LABEL_IS_SUN)
2675                                 list_table(1);
2676                         else
2677                                 x_list_table(0);
2678                         break;
2679                 case 'q':
2680                         if (ENABLE_FEATURE_CLEAN_UP)
2681                                 close_dev_fd();
2682                         bb_putchar('\n');
2683                         exit(EXIT_SUCCESS);
2684                 case 'r':
2685                         return;
2686                 case 's':
2687                         user_sectors = g_sectors = read_int(1, g_sectors, 63, 0, "Number of sectors");
2688                         if (dos_compatible_flag) {
2689                                 sector_offset = g_sectors;
2690                                 printf("Warning: setting sector offset for DOS "
2691                                         "compatiblity\n");
2692                         }
2693                         update_units();
2694                         break;
2695                 case 'v':
2696                         verify();
2697                         break;
2698                 case 'w':
2699                         write_table();  /* does not return */
2700                         break;
2701                 case 'y':
2702                         if (LABEL_IS_SUN)
2703                                 sun_set_pcylcount();
2704                         break;
2705                 default:
2706                         xmenu();
2707                 }
2708         }
2709 }
2710 #endif /* ADVANCED mode */
2711
2712 static int
2713 is_ide_cdrom_or_tape(const char *device)
2714 {
2715         FILE *procf;
2716         char buf[100];
2717         struct stat statbuf;
2718         int is_ide = 0;
2719
2720         /* No device was given explicitly, and we are trying some
2721            likely things.  But opening /dev/hdc may produce errors like
2722            "hdc: tray open or drive not ready"
2723            if it happens to be a CD-ROM drive. It even happens that
2724            the process hangs on the attempt to read a music CD.
2725            So try to be careful. This only works since 2.1.73. */
2726
2727         if (strncmp("/dev/hd", device, 7))
2728                 return 0;
2729
2730         snprintf(buf, sizeof(buf), "/proc/ide/%s/media", device+5);
2731         procf = fopen_for_read(buf);
2732         if (procf != NULL && fgets(buf, sizeof(buf), procf))
2733                 is_ide = (!strncmp(buf, "cdrom", 5) ||
2734                           !strncmp(buf, "tape", 4));
2735         else
2736                 /* Now when this proc file does not exist, skip the
2737                    device when it is read-only. */
2738                 if (stat(device, &statbuf) == 0)
2739                         is_ide = ((statbuf.st_mode & 0222) == 0);
2740
2741         if (procf)
2742                 fclose(procf);
2743         return is_ide;
2744 }
2745
2746
2747 static void
2748 open_list_and_close(const char *device, int user_specified)
2749 {
2750         int gb;
2751
2752         disk_device = device;
2753         if (setjmp(listingbuf))
2754                 return;
2755         if (!user_specified)
2756                 if (is_ide_cdrom_or_tape(device))
2757                         return;
2758
2759         /* Open disk_device, save file descriptor to dev_fd */
2760         errno = 0;
2761         gb = get_boot(TRY_ONLY);
2762         if (gb > 0) {   /* I/O error */
2763                 /* Ignore other errors, since we try IDE
2764                    and SCSI hard disks which may not be
2765                    installed on the system. */
2766                 if (user_specified || errno == EACCES)
2767                         bb_perror_msg("can't open '%s'", device);
2768                 return;
2769         }
2770
2771         if (gb < 0) { /* no DOS signature */
2772                 list_disk_geometry();
2773                 if (LABEL_IS_AIX)
2774                         goto ret;
2775 #if ENABLE_FEATURE_OSF_LABEL
2776                 if (bsd_trydev(device) < 0)
2777 #endif
2778                         printf("Disk %s doesn't contain a valid "
2779                                 "partition table\n", device);
2780         } else {
2781                 list_table(0);
2782 #if ENABLE_FEATURE_FDISK_WRITABLE
2783                 if (!LABEL_IS_SUN && g_partitions > 4) {
2784                         delete_partition(ext_index);
2785                 }
2786 #endif
2787         }
2788  ret:
2789         close_dev_fd();
2790 }
2791
2792 /* for fdisk -l: try all things in /proc/partitions
2793    that look like a partition name (do not end in a digit) */
2794 static void
2795 list_devs_in_proc_partititons(void)
2796 {
2797         FILE *procpt;
2798         char line[100], ptname[100], devname[120], *s;
2799         int ma, mi, sz;
2800
2801         procpt = fopen_or_warn("/proc/partitions", "r");
2802
2803         while (fgets(line, sizeof(line), procpt)) {
2804                 if (sscanf(line, " %u %u %u %[^\n ]",
2805                                 &ma, &mi, &sz, ptname) != 4)
2806                         continue;
2807                 for (s = ptname; *s; s++)
2808                         continue;
2809                 /* note: excluding '0': e.g. mmcblk0 is not a partition name! */
2810                 if (s[-1] >= '1' && s[-1] <= '9')
2811                         continue;
2812                 sprintf(devname, "/dev/%s", ptname);
2813                 open_list_and_close(devname, 0);
2814         }
2815 #if ENABLE_FEATURE_CLEAN_UP
2816         fclose(procpt);
2817 #endif
2818 }
2819
2820 #if ENABLE_FEATURE_FDISK_WRITABLE
2821 static void
2822 unknown_command(int c)
2823 {
2824         printf("%c: unknown command\n", c);
2825 }
2826 #endif
2827
2828 int fdisk_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
2829 int fdisk_main(int argc UNUSED_PARAM, char **argv)
2830 {
2831         unsigned opt;
2832         /*
2833          *  fdisk -v
2834          *  fdisk -l [-b sectorsize] [-u] device ...
2835          *  fdisk -s [partition] ...
2836          *  fdisk [-b sectorsize] [-u] device
2837          *
2838          * Options -C, -H, -S set the geometry.
2839          */
2840         INIT_G();
2841
2842         close_dev_fd(); /* needed: fd 3 must not stay closed */
2843
2844         opt_complementary = "b+:C+:H+:S+"; /* numeric params */
2845         opt = getopt32(argv, "b:C:H:lS:u" IF_FEATURE_FDISK_BLKSIZE("s"),
2846                                 &sector_size, &user_cylinders, &user_heads, &user_sectors);
2847         argv += optind;
2848         if (opt & OPT_b) {
2849                 /* Ugly: this sector size is really per device,
2850                  * so cannot be combined with multiple disks,
2851                  * and the same goes for the C/H/S options.
2852                  */
2853                 if (sector_size < 512
2854                  || sector_size > 0x10000
2855                  || (sector_size & (sector_size-1)) /* not power of 2 */
2856                 ) {
2857                         bb_show_usage();
2858                 }
2859                 sector_offset = 2;
2860                 user_set_sector_size = 1;
2861         }
2862         if (user_heads <= 0 || user_heads >= 256)
2863                 user_heads = 0;
2864         if (user_sectors <= 0 || user_sectors >= 64)
2865                 user_sectors = 0;
2866         if (opt & OPT_u)
2867                 display_in_cyl_units = 0; // -u
2868
2869 #if ENABLE_FEATURE_FDISK_WRITABLE
2870         if (opt & OPT_l) {
2871                 nowarn = 1;
2872 #endif
2873                 if (*argv) {
2874                         listing = 1;
2875                         do {
2876                                 open_list_and_close(*argv, 1);
2877                         } while (*++argv);
2878                 } else {
2879                         /* we don't have device names, */
2880                         /* use /proc/partitions instead */
2881                         list_devs_in_proc_partititons();
2882                 }
2883                 return 0;
2884 #if ENABLE_FEATURE_FDISK_WRITABLE
2885         }
2886 #endif
2887
2888 #if ENABLE_FEATURE_FDISK_BLKSIZE
2889         if (opt & OPT_s) {
2890                 int j;
2891
2892                 nowarn = 1;
2893                 if (!argv[0])
2894                         bb_show_usage();
2895                 for (j = 0; argv[j]; j++) {
2896                         unsigned long long size;
2897                         fd = xopen(argv[j], O_RDONLY);
2898                         size = bb_BLKGETSIZE_sectors(fd) / 2;
2899                         close(fd);
2900                         if (argv[1])
2901                                 printf("%llu\n", size);
2902                         else
2903                                 printf("%s: %llu\n", argv[j], size);
2904                 }
2905                 return 0;
2906         }
2907 #endif
2908
2909 #if ENABLE_FEATURE_FDISK_WRITABLE
2910         if (!argv[0] || argv[1])
2911                 bb_show_usage();
2912
2913         disk_device = argv[0];
2914         get_boot(OPEN_MAIN);
2915
2916         if (LABEL_IS_OSF) {
2917                 /* OSF label, and no DOS label */
2918                 printf("Detected an OSF/1 disklabel on %s, entering "
2919                         "disklabel mode\n", disk_device);
2920                 bsd_select();
2921                 /*Why do we do this?  It seems to be counter-intuitive*/
2922                 current_label_type = LABEL_DOS;
2923                 /* If we return we may want to make an empty DOS label? */
2924         }
2925
2926         while (1) {
2927                 int c;
2928                 bb_putchar('\n');
2929                 c = 0x20 | read_nonempty("Command (m for help): ");
2930                 switch (c) {
2931                 case 'a':
2932                         if (LABEL_IS_DOS)
2933                                 toggle_active(get_partition(1, g_partitions));
2934                         else if (LABEL_IS_SUN)
2935                                 toggle_sunflags(get_partition(1, g_partitions),
2936                                                 0x01);
2937                         else if (LABEL_IS_SGI)
2938                                 sgi_set_bootpartition(
2939                                         get_partition(1, g_partitions));
2940                         else
2941                                 unknown_command(c);
2942                         break;
2943                 case 'b':
2944                         if (LABEL_IS_SGI) {
2945                                 printf("\nThe current boot file is: %s\n",
2946                                         sgi_get_bootfile());
2947                                 if (read_maybe_empty("Please enter the name of the "
2948                                                    "new boot file: ") == '\n')
2949                                         printf("Boot file unchanged\n");
2950                                 else
2951                                         sgi_set_bootfile(line_ptr);
2952                         }
2953 #if ENABLE_FEATURE_OSF_LABEL
2954                         else
2955                                 bsd_select();
2956 #endif
2957                         break;
2958                 case 'c':
2959                         if (LABEL_IS_DOS)
2960                                 toggle_dos_compatibility_flag();
2961                         else if (LABEL_IS_SUN)
2962                                 toggle_sunflags(get_partition(1, g_partitions),
2963                                                 0x10);
2964                         else if (LABEL_IS_SGI)
2965                                 sgi_set_swappartition(
2966                                                 get_partition(1, g_partitions));
2967                         else
2968                                 unknown_command(c);
2969                         break;
2970                 case 'd':
2971                         {
2972                                 int j;
2973                         /* If sgi_label then don't use get_existing_partition,
2974                            let the user select a partition, since
2975                            get_existing_partition() only works for Linux-like
2976                            partition tables */
2977                                 if (!LABEL_IS_SGI) {
2978                                         j = get_existing_partition(1, g_partitions);
2979                                 } else {
2980                                         j = get_partition(1, g_partitions);
2981                                 }
2982                                 if (j >= 0)
2983                                         delete_partition(j);
2984                         }
2985                         break;
2986                 case 'i':
2987                         if (LABEL_IS_SGI)
2988                                 create_sgiinfo();
2989                         else
2990                                 unknown_command(c);
2991                 case 'l':
2992                         list_types(get_sys_types());
2993                         break;
2994                 case 'm':
2995                         menu();
2996                         break;
2997                 case 'n':
2998                         new_partition();
2999                         break;
3000                 case 'o':
3001                         create_doslabel();
3002                         break;
3003                 case 'p':
3004                         list_table(0);
3005                         break;
3006                 case 'q':
3007                         if (ENABLE_FEATURE_CLEAN_UP)
3008                                 close_dev_fd();
3009                         bb_putchar('\n');
3010                         return 0;
3011                 case 's':
3012 #if ENABLE_FEATURE_SUN_LABEL
3013                         create_sunlabel();
3014 #endif
3015                         break;
3016                 case 't':
3017                         change_sysid();
3018                         break;
3019                 case 'u':
3020                         change_units();
3021                         break;
3022                 case 'v':
3023                         verify();
3024                         break;
3025                 case 'w':
3026                         write_table();          /* does not return */
3027                         break;
3028 #if ENABLE_FEATURE_FDISK_ADVANCED
3029                 case 'x':
3030                         if (LABEL_IS_SGI) {
3031                                 printf("\n\tSorry, no experts menu for SGI "
3032                                         "partition tables available\n\n");
3033                         } else
3034                                 xselect();
3035                         break;
3036 #endif
3037                 default:
3038                         unknown_command(c);
3039                         menu();
3040                 }
3041         }
3042         return 0;
3043 #endif /* FEATURE_FDISK_WRITABLE */
3044 }