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