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