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