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