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