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