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