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