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