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