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