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