Tomato 1.28
[tomato.git] / release / src / router / busybox / util-linux / fdisk.c
blobdc61e238ae8c6f617618b20cb52cdacd7d0ab27a
1 /* vi: set sw=4 ts=4: */
2 /* fdisk.c -- Partition table manipulator for Linux.
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)
7 * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
8 */
10 #ifndef _LARGEFILE64_SOURCE
11 /* For lseek64 */
12 #define _LARGEFILE64_SOURCE
13 #endif
14 #include <assert.h> /* assert */
15 #include "libbb.h"
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
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
29 #define ACTIVE_FLAG 0x80
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
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,
52 /* Used for sector numbers. Today's disk sizes make it necessary */
53 typedef unsigned long long ullong;
55 struct hd_geometry {
56 unsigned char heads;
57 unsigned char sectors;
58 unsigned short cylinders;
59 unsigned long start;
62 #define HDIO_GETGEO 0x0301 /* get device geometry */
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";
69 static const char msg_part_already_defined[] ALIGN1 =
70 "Partition %d is already defined, delete it before re-adding\n";
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 } PACKED;
86 static const char unable_to_open[] ALIGN1 = "can't open '%s'";
87 static const char unable_to_read[] ALIGN1 = "can't read from %s";
88 static const char unable_to_seek[] ALIGN1 = "can't seek on %s";
90 enum label_type {
91 LABEL_DOS, LABEL_SUN, LABEL_SGI, LABEL_AIX, LABEL_OSF
94 #define LABEL_IS_DOS (LABEL_DOS == current_label_type)
96 #if ENABLE_FEATURE_SUN_LABEL
97 #define LABEL_IS_SUN (LABEL_SUN == current_label_type)
98 #define STATIC_SUN static
99 #else
100 #define LABEL_IS_SUN 0
101 #define STATIC_SUN extern
102 #endif
104 #if ENABLE_FEATURE_SGI_LABEL
105 #define LABEL_IS_SGI (LABEL_SGI == current_label_type)
106 #define STATIC_SGI static
107 #else
108 #define LABEL_IS_SGI 0
109 #define STATIC_SGI extern
110 #endif
112 #if ENABLE_FEATURE_AIX_LABEL
113 #define LABEL_IS_AIX (LABEL_AIX == current_label_type)
114 #define STATIC_AIX static
115 #else
116 #define LABEL_IS_AIX 0
117 #define STATIC_AIX extern
118 #endif
120 #if ENABLE_FEATURE_OSF_LABEL
121 #define LABEL_IS_OSF (LABEL_OSF == current_label_type)
122 #define STATIC_OSF static
123 #else
124 #define LABEL_IS_OSF 0
125 #define STATIC_OSF extern
126 #endif
128 enum action { OPEN_MAIN, TRY_ONLY, CREATE_EMPTY_DOS, CREATE_EMPTY_SUN };
130 static void update_units(void);
131 #if ENABLE_FEATURE_FDISK_WRITABLE
132 static void change_units(void);
133 static void reread_partition_table(int leave);
134 static void delete_partition(int i);
135 static int get_partition(int warn, int max);
136 static void list_types(const char *const *sys);
137 static unsigned read_int(unsigned low, unsigned dflt, unsigned high, unsigned base, const char *mesg);
138 #endif
139 static const char *partition_type(unsigned char type);
140 static void get_geometry(void);
141 #if ENABLE_FEATURE_SUN_LABEL || ENABLE_FEATURE_FDISK_WRITABLE
142 static int get_boot(enum action what);
143 #else
144 static int get_boot(void);
145 #endif
147 #define PLURAL 0
148 #define SINGULAR 1
150 static unsigned get_start_sect(const struct partition *p);
151 static unsigned get_nr_sects(const struct partition *p);
154 * per partition table entry data
156 * The four primary partitions have the same sectorbuffer (MBRbuffer)
157 * and have NULL ext_pointer.
158 * Each logical partition table entry has two pointers, one for the
159 * partition and one link to the next one.
161 struct pte {
162 struct partition *part_table; /* points into sectorbuffer */
163 struct partition *ext_pointer; /* points into sectorbuffer */
164 ullong offset; /* disk sector number */
165 char *sectorbuffer; /* disk sector contents */
166 #if ENABLE_FEATURE_FDISK_WRITABLE
167 char changed; /* boolean */
168 #endif
171 /* DOS partition types */
173 static const char *const i386_sys_types[] = {
174 "\x00" "Empty",
175 "\x01" "FAT12",
176 "\x04" "FAT16 <32M",
177 "\x05" "Extended", /* DOS 3.3+ extended partition */
178 "\x06" "FAT16", /* DOS 16-bit >=32M */
179 "\x07" "HPFS/NTFS", /* OS/2 IFS, eg, HPFS or NTFS or QNX */
180 "\x0a" "OS/2 Boot Manager",/* OS/2 Boot Manager */
181 "\x0b" "Win95 FAT32",
182 "\x0c" "Win95 FAT32 (LBA)",/* LBA really is 'Extended Int 13h' */
183 "\x0e" "Win95 FAT16 (LBA)",
184 "\x0f" "Win95 Ext'd (LBA)",
185 "\x11" "Hidden FAT12",
186 "\x12" "Compaq diagnostics",
187 "\x14" "Hidden FAT16 <32M",
188 "\x16" "Hidden FAT16",
189 "\x17" "Hidden HPFS/NTFS",
190 "\x1b" "Hidden Win95 FAT32",
191 "\x1c" "Hidden W95 FAT32 (LBA)",
192 "\x1e" "Hidden W95 FAT16 (LBA)",
193 "\x3c" "Part.Magic recovery",
194 "\x41" "PPC PReP Boot",
195 "\x42" "SFS",
196 "\x63" "GNU HURD or SysV", /* GNU HURD or Mach or Sys V/386 (such as ISC UNIX) */
197 "\x80" "Old Minix", /* Minix 1.4a and earlier */
198 "\x81" "Minix / old Linux",/* Minix 1.4b and later */
199 "\x82" "Linux swap", /* also Solaris */
200 "\x83" "Linux",
201 "\x84" "OS/2 hidden C: drive",
202 "\x85" "Linux extended",
203 "\x86" "NTFS volume set",
204 "\x87" "NTFS volume set",
205 "\x8e" "Linux LVM",
206 "\x9f" "BSD/OS", /* BSDI */
207 "\xa0" "Thinkpad hibernation",
208 "\xa5" "FreeBSD", /* various BSD flavours */
209 "\xa6" "OpenBSD",
210 "\xa8" "Darwin UFS",
211 "\xa9" "NetBSD",
212 "\xab" "Darwin boot",
213 "\xb7" "BSDI fs",
214 "\xb8" "BSDI swap",
215 "\xbe" "Solaris boot",
216 "\xeb" "BeOS fs",
217 "\xee" "EFI GPT", /* Intel EFI GUID Partition Table */
218 "\xef" "EFI (FAT-12/16/32)", /* Intel EFI System Partition */
219 "\xf0" "Linux/PA-RISC boot", /* Linux/PA-RISC boot loader */
220 "\xf2" "DOS secondary", /* DOS 3.3+ secondary */
221 "\xfd" "Linux raid autodetect", /* New (2.2.x) raid partition with
222 autodetect using persistent
223 superblock */
224 #if 0 /* ENABLE_WEIRD_PARTITION_TYPES */
225 "\x02" "XENIX root",
226 "\x03" "XENIX usr",
227 "\x08" "AIX", /* AIX boot (AIX -- PS/2 port) or SplitDrive */
228 "\x09" "AIX bootable", /* AIX data or Coherent */
229 "\x10" "OPUS",
230 "\x18" "AST SmartSleep",
231 "\x24" "NEC DOS",
232 "\x39" "Plan 9",
233 "\x40" "Venix 80286",
234 "\x4d" "QNX4.x",
235 "\x4e" "QNX4.x 2nd part",
236 "\x4f" "QNX4.x 3rd part",
237 "\x50" "OnTrack DM",
238 "\x51" "OnTrack DM6 Aux1", /* (or Novell) */
239 "\x52" "CP/M", /* CP/M or Microport SysV/AT */
240 "\x53" "OnTrack DM6 Aux3",
241 "\x54" "OnTrackDM6",
242 "\x55" "EZ-Drive",
243 "\x56" "Golden Bow",
244 "\x5c" "Priam Edisk",
245 "\x61" "SpeedStor",
246 "\x64" "Novell Netware 286",
247 "\x65" "Novell Netware 386",
248 "\x70" "DiskSecure Multi-Boot",
249 "\x75" "PC/IX",
250 "\x93" "Amoeba",
251 "\x94" "Amoeba BBT", /* (bad block table) */
252 "\xa7" "NeXTSTEP",
253 "\xbb" "Boot Wizard hidden",
254 "\xc1" "DRDOS/sec (FAT-12)",
255 "\xc4" "DRDOS/sec (FAT-16 < 32M)",
256 "\xc6" "DRDOS/sec (FAT-16)",
257 "\xc7" "Syrinx",
258 "\xda" "Non-FS data",
259 "\xdb" "CP/M / CTOS / ...",/* CP/M or Concurrent CP/M or
260 Concurrent DOS or CTOS */
261 "\xde" "Dell Utility", /* Dell PowerEdge Server utilities */
262 "\xdf" "BootIt", /* BootIt EMBRM */
263 "\xe1" "DOS access", /* DOS access or SpeedStor 12-bit FAT
264 extended partition */
265 "\xe3" "DOS R/O", /* DOS R/O or SpeedStor */
266 "\xe4" "SpeedStor", /* SpeedStor 16-bit FAT extended
267 partition < 1024 cyl. */
268 "\xf1" "SpeedStor",
269 "\xf4" "SpeedStor", /* SpeedStor large partition */
270 "\xfe" "LANstep", /* SpeedStor >1024 cyl. or LANstep */
271 "\xff" "BBT", /* Xenix Bad Block Table */
272 #endif
273 NULL
276 enum {
277 dev_fd = 3 /* the disk */
280 /* Globals */
281 struct globals {
282 char *line_ptr;
284 const char *disk_device;
285 int g_partitions; // = 4; /* maximum partition + 1 */
286 unsigned units_per_sector; // = 1;
287 unsigned sector_size; // = DEFAULT_SECTOR_SIZE;
288 unsigned user_set_sector_size;
289 unsigned sector_offset; // = 1;
290 unsigned g_heads, g_sectors, g_cylinders;
291 smallint /* enum label_type */ current_label_type;
292 smallint display_in_cyl_units; // = 1;
293 #if ENABLE_FEATURE_OSF_LABEL
294 smallint possibly_osf_label;
295 #endif
297 smallint listing; /* no aborts for fdisk -l */
298 smallint dos_compatible_flag; // = 1;
299 #if ENABLE_FEATURE_FDISK_WRITABLE
300 //int dos_changed;
301 smallint nowarn; /* no warnings for fdisk -l/-s */
302 #endif
303 int ext_index; /* the prime extended partition */
304 unsigned user_cylinders, user_heads, user_sectors;
305 unsigned pt_heads, pt_sectors;
306 unsigned kern_heads, kern_sectors;
307 ullong extended_offset; /* offset of link pointers */
308 ullong total_number_of_sectors;
310 jmp_buf listingbuf;
311 char line_buffer[80];
312 char partname_buffer[80];
313 /* Raw disk label. For DOS-type partition tables the MBR,
314 * with descriptions of the primary partitions. */
315 char MBRbuffer[MAX_SECTOR_SIZE];
316 /* Partition tables */
317 struct pte ptes[MAXIMUM_PARTS];
319 #define G (*ptr_to_globals)
320 #define line_ptr (G.line_ptr )
321 #define disk_device (G.disk_device )
322 #define g_partitions (G.g_partitions )
323 #define units_per_sector (G.units_per_sector )
324 #define sector_size (G.sector_size )
325 #define user_set_sector_size (G.user_set_sector_size)
326 #define sector_offset (G.sector_offset )
327 #define g_heads (G.g_heads )
328 #define g_sectors (G.g_sectors )
329 #define g_cylinders (G.g_cylinders )
330 #define current_label_type (G.current_label_type )
331 #define display_in_cyl_units (G.display_in_cyl_units)
332 #define possibly_osf_label (G.possibly_osf_label )
333 #define listing (G.listing )
334 #define dos_compatible_flag (G.dos_compatible_flag )
335 #define nowarn (G.nowarn )
336 #define ext_index (G.ext_index )
337 #define user_cylinders (G.user_cylinders )
338 #define user_heads (G.user_heads )
339 #define user_sectors (G.user_sectors )
340 #define pt_heads (G.pt_heads )
341 #define pt_sectors (G.pt_sectors )
342 #define kern_heads (G.kern_heads )
343 #define kern_sectors (G.kern_sectors )
344 #define extended_offset (G.extended_offset )
345 #define total_number_of_sectors (G.total_number_of_sectors)
346 #define listingbuf (G.listingbuf )
347 #define line_buffer (G.line_buffer )
348 #define partname_buffer (G.partname_buffer)
349 #define MBRbuffer (G.MBRbuffer )
350 #define ptes (G.ptes )
351 #define INIT_G() do { \
352 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
353 sector_size = DEFAULT_SECTOR_SIZE; \
354 sector_offset = 1; \
355 g_partitions = 4; \
356 display_in_cyl_units = 1; \
357 units_per_sector = 1; \
358 dos_compatible_flag = 1; \
359 } while (0)
362 /* TODO: move to libbb? */
363 static ullong bb_BLKGETSIZE_sectors(int fd)
365 uint64_t v64;
366 unsigned long longsectors;
368 if (ioctl(fd, BLKGETSIZE64, &v64) == 0) {
369 /* Got bytes, convert to 512 byte sectors */
370 return (v64 >> 9);
372 /* Needs temp of type long */
373 if (ioctl(fd, BLKGETSIZE, &longsectors))
374 longsectors = 0;
375 return longsectors;
379 #define IS_EXTENDED(i) \
380 ((i) == EXTENDED || (i) == WIN98_EXTENDED || (i) == LINUX_EXTENDED)
382 #define cround(n) (display_in_cyl_units ? ((n)/units_per_sector)+1 : (n))
384 #define scround(x) (((x)+units_per_sector-1)/units_per_sector)
386 #define pt_offset(b, n) \
387 ((struct partition *)((b) + 0x1be + (n) * sizeof(struct partition)))
389 #define sector(s) ((s) & 0x3f)
391 #define cylinder(s, c) ((c) | (((s) & 0xc0) << 2))
393 #define hsc2sector(h,s,c) \
394 (sector(s) - 1 + sectors * ((h) + heads * cylinder(s,c)))
396 #define set_hsc(h,s,c,sector) \
397 do { \
398 s = sector % g_sectors + 1; \
399 sector /= g_sectors; \
400 h = sector % g_heads; \
401 sector /= g_heads; \
402 c = sector & 0xff; \
403 s |= (sector >> 2) & 0xc0; \
404 } while (0)
406 static void
407 close_dev_fd(void)
409 /* Not really closing, but making sure it is open, and to harmless place */
410 xmove_fd(xopen(bb_dev_null, O_RDONLY), dev_fd);
413 #if ENABLE_FEATURE_FDISK_WRITABLE
414 /* Read line; return 0 or first printable char */
415 static int
416 read_line(const char *prompt)
418 int sz;
420 sz = read_line_input(prompt, line_buffer, sizeof(line_buffer), NULL);
421 if (sz <= 0)
422 exit(EXIT_SUCCESS); /* Ctrl-D or Ctrl-C */
424 if (line_buffer[sz-1] == '\n')
425 line_buffer[--sz] = '\0';
427 line_ptr = line_buffer;
428 while (*line_ptr && !isgraph(*line_ptr))
429 line_ptr++;
430 return *line_ptr;
432 #endif
435 * Return partition name - uses static storage
437 static const char *
438 partname(const char *dev, int pno, int lth)
440 const char *p;
441 int w, wp;
442 int bufsiz;
443 char *bufp;
445 bufp = partname_buffer;
446 bufsiz = sizeof(partname_buffer);
448 w = strlen(dev);
449 p = "";
451 if (isdigit(dev[w-1]))
452 p = "p";
454 /* devfs kludge - note: fdisk partition names are not supposed
455 to equal kernel names, so there is no reason to do this */
456 if (strcmp(dev + w - 4, "disc") == 0) {
457 w -= 4;
458 p = "part";
461 wp = strlen(p);
463 if (lth) {
464 snprintf(bufp, bufsiz, "%*.*s%s%-2u",
465 lth-wp-2, w, dev, p, pno);
466 } else {
467 snprintf(bufp, bufsiz, "%.*s%s%-2u", w, dev, p, pno);
469 return bufp;
472 #if ENABLE_FEATURE_FDISK_WRITABLE
473 static void
474 set_all_unchanged(void)
476 int i;
478 for (i = 0; i < MAXIMUM_PARTS; i++)
479 ptes[i].changed = 0;
482 static ALWAYS_INLINE void
483 set_changed(int i)
485 ptes[i].changed = 1;
487 #endif /* FEATURE_FDISK_WRITABLE */
489 static ALWAYS_INLINE struct partition *
490 get_part_table(int i)
492 return ptes[i].part_table;
495 static const char *
496 str_units(int n)
497 { /* n==1: use singular */
498 if (n == 1)
499 return display_in_cyl_units ? "cylinder" : "sector";
500 return display_in_cyl_units ? "cylinders" : "sectors";
503 static int
504 valid_part_table_flag(const char *mbuffer)
506 return (mbuffer[510] == 0x55 && (uint8_t)mbuffer[511] == 0xaa);
509 #if ENABLE_FEATURE_FDISK_WRITABLE
510 static ALWAYS_INLINE void
511 write_part_table_flag(char *b)
513 b[510] = 0x55;
514 b[511] = 0xaa;
517 static char
518 read_nonempty(const char *mesg)
520 while (!read_line(mesg))
521 continue;
522 return *line_ptr;
525 static char
526 read_maybe_empty(const char *mesg)
528 if (!read_line(mesg)) {
529 line_ptr = line_buffer;
530 line_ptr[0] = '\n';
531 line_ptr[1] = '\0';
533 return line_ptr[0];
536 static int
537 read_hex(const char *const *sys)
539 unsigned long v;
540 while (1) {
541 read_nonempty("Hex code (type L to list codes): ");
542 if (*line_ptr == 'l' || *line_ptr == 'L') {
543 list_types(sys);
544 continue;
546 v = bb_strtoul(line_ptr, NULL, 16);
547 if (v > 0xff)
548 /* Bad input also triggers this */
549 continue;
550 return v;
553 #endif /* FEATURE_FDISK_WRITABLE */
555 static void fdisk_fatal(const char *why)
557 if (listing) {
558 close_dev_fd();
559 longjmp(listingbuf, 1);
561 bb_error_msg_and_die(why, disk_device);
564 static void
565 seek_sector(ullong secno)
567 secno *= sector_size;
568 #if ENABLE_FDISK_SUPPORT_LARGE_DISKS
569 if (lseek64(dev_fd, (off64_t)secno, SEEK_SET) == (off64_t) -1)
570 fdisk_fatal(unable_to_seek);
571 #else
572 if (secno > MAXINT(off_t)
573 || lseek(dev_fd, (off_t)secno, SEEK_SET) == (off_t) -1
575 fdisk_fatal(unable_to_seek);
577 #endif
580 #if ENABLE_FEATURE_FDISK_WRITABLE
581 static void
582 write_sector(ullong secno, const void *buf)
584 seek_sector(secno);
585 xwrite(dev_fd, buf, sector_size);
587 #endif
590 #include "fdisk_aix.c"
592 typedef struct {
593 unsigned char info[128]; /* Informative text string */
594 unsigned char spare0[14];
595 struct sun_info {
596 unsigned char spare1;
597 unsigned char id;
598 unsigned char spare2;
599 unsigned char flags;
600 } infos[8];
601 unsigned char spare1[246]; /* Boot information etc. */
602 unsigned short rspeed; /* Disk rotational speed */
603 unsigned short pcylcount; /* Physical cylinder count */
604 unsigned short sparecyl; /* extra sects per cylinder */
605 unsigned char spare2[4]; /* More magic... */
606 unsigned short ilfact; /* Interleave factor */
607 unsigned short ncyl; /* Data cylinder count */
608 unsigned short nacyl; /* Alt. cylinder count */
609 unsigned short ntrks; /* Tracks per cylinder */
610 unsigned short nsect; /* Sectors per track */
611 unsigned char spare3[4]; /* Even more magic... */
612 struct sun_partinfo {
613 uint32_t start_cylinder;
614 uint32_t num_sectors;
615 } partitions[8];
616 unsigned short magic; /* Magic number */
617 unsigned short csum; /* Label xor'd checksum */
618 } sun_partition;
619 #define sunlabel ((sun_partition *)MBRbuffer)
620 STATIC_OSF void bsd_select(void);
621 STATIC_OSF void xbsd_print_disklabel(int);
622 #include "fdisk_osf.c"
624 #if ENABLE_FEATURE_SGI_LABEL || ENABLE_FEATURE_SUN_LABEL
625 static uint16_t
626 fdisk_swap16(uint16_t x)
628 return (x << 8) | (x >> 8);
631 static uint32_t
632 fdisk_swap32(uint32_t x)
634 return (x << 24) |
635 ((x & 0xFF00) << 8) |
636 ((x & 0xFF0000) >> 8) |
637 (x >> 24);
639 #endif
641 STATIC_SGI const char *const sgi_sys_types[];
642 STATIC_SGI unsigned sgi_get_num_sectors(int i);
643 STATIC_SGI int sgi_get_sysid(int i);
644 STATIC_SGI void sgi_delete_partition(int i);
645 STATIC_SGI void sgi_change_sysid(int i, int sys);
646 STATIC_SGI void sgi_list_table(int xtra);
647 #if ENABLE_FEATURE_FDISK_ADVANCED
648 STATIC_SGI void sgi_set_xcyl(void);
649 #endif
650 STATIC_SGI int verify_sgi(int verbose);
651 STATIC_SGI void sgi_add_partition(int n, int sys);
652 STATIC_SGI void sgi_set_swappartition(int i);
653 STATIC_SGI const char *sgi_get_bootfile(void);
654 STATIC_SGI void sgi_set_bootfile(const char* aFile);
655 STATIC_SGI void create_sgiinfo(void);
656 STATIC_SGI void sgi_write_table(void);
657 STATIC_SGI void sgi_set_bootpartition(int i);
658 #include "fdisk_sgi.c"
660 STATIC_SUN const char *const sun_sys_types[];
661 STATIC_SUN void sun_delete_partition(int i);
662 STATIC_SUN void sun_change_sysid(int i, int sys);
663 STATIC_SUN void sun_list_table(int xtra);
664 STATIC_SUN void add_sun_partition(int n, int sys);
665 #if ENABLE_FEATURE_FDISK_ADVANCED
666 STATIC_SUN void sun_set_alt_cyl(void);
667 STATIC_SUN void sun_set_ncyl(int cyl);
668 STATIC_SUN void sun_set_xcyl(void);
669 STATIC_SUN void sun_set_ilfact(void);
670 STATIC_SUN void sun_set_rspeed(void);
671 STATIC_SUN void sun_set_pcylcount(void);
672 #endif
673 STATIC_SUN void toggle_sunflags(int i, unsigned char mask);
674 STATIC_SUN void verify_sun(void);
675 STATIC_SUN void sun_write_table(void);
676 #include "fdisk_sun.c"
679 #if ENABLE_FEATURE_FDISK_WRITABLE
680 /* start_sect and nr_sects are stored little endian on all machines */
681 /* moreover, they are not aligned correctly */
682 static void
683 store4_little_endian(unsigned char *cp, unsigned val)
685 cp[0] = val;
686 cp[1] = val >> 8;
687 cp[2] = val >> 16;
688 cp[3] = val >> 24;
690 #endif /* FEATURE_FDISK_WRITABLE */
692 static unsigned
693 read4_little_endian(const unsigned char *cp)
695 return cp[0] + (cp[1] << 8) + (cp[2] << 16) + (cp[3] << 24);
698 #if ENABLE_FEATURE_FDISK_WRITABLE
699 static void
700 set_start_sect(struct partition *p, unsigned start_sect)
702 store4_little_endian(p->start4, start_sect);
704 #endif
706 static unsigned
707 get_start_sect(const struct partition *p)
709 return read4_little_endian(p->start4);
712 #if ENABLE_FEATURE_FDISK_WRITABLE
713 static void
714 set_nr_sects(struct partition *p, unsigned nr_sects)
716 store4_little_endian(p->size4, nr_sects);
718 #endif
720 static unsigned
721 get_nr_sects(const struct partition *p)
723 return read4_little_endian(p->size4);
726 /* Allocate a buffer and read a partition table sector */
727 static void
728 read_pte(struct pte *pe, ullong offset)
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;
742 static unsigned
743 get_partition_start(const struct pte *pe)
745 return pe->offset + get_start_sect(pe->part_table);
748 #if ENABLE_FEATURE_FDISK_WRITABLE
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".
755 #ifdef UNUSED
756 static int
757 is_dos_partition(int t)
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);
765 #endif
767 static void
768 menu(void)
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
828 #endif /* FEATURE_FDISK_WRITABLE */
831 #if ENABLE_FEATURE_FDISK_ADVANCED
832 static void
833 xmenu(void)
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");
895 #endif /* ADVANCED mode */
897 #if ENABLE_FEATURE_FDISK_WRITABLE
898 static const char *const *
899 get_sys_types(void)
901 return (
902 LABEL_IS_SUN ? sun_sys_types :
903 LABEL_IS_SGI ? sgi_sys_types :
904 i386_sys_types);
906 #else
907 #define get_sys_types() i386_sys_types
908 #endif /* FEATURE_FDISK_WRITABLE */
910 static const char *
911 partition_type(unsigned char type)
913 int i;
914 const char *const *types = get_sys_types();
916 for (i = 0; types[i]; i++)
917 if ((unsigned char)types[i][0] == type)
918 return types[i] + 1;
920 return "Unknown";
924 #if ENABLE_FEATURE_FDISK_WRITABLE
925 static int
926 get_sysid(int i)
928 return LABEL_IS_SUN ? sunlabel->infos[i].id :
929 (LABEL_IS_SGI ? sgi_get_sysid(i) :
930 ptes[i].part_table->sys_ind);
933 static void
934 list_types(const char *const *sys)
936 enum { COLS = 3 };
938 unsigned last[COLS];
939 unsigned done, next, size;
940 int i;
942 for (size = 0; sys[size]; size++)
943 continue;
945 done = 0;
946 for (i = COLS-1; i >= 0; i--) {
947 done += (size + i - done) / (i + 1);
948 last[COLS-1 - i] = done;
951 i = done = next = 0;
952 do {
953 printf("%c%2x %-22.22s", i ? ' ' : '\n',
954 (unsigned char)sys[next][0],
955 sys[next] + 1);
956 next = last[i++] + done;
957 if (i >= COLS || next >= last[i]) {
958 i = 0;
959 next = ++done;
961 } while (done < last[0]);
962 bb_putchar('\n');
964 #endif /* FEATURE_FDISK_WRITABLE */
966 static int
967 is_cleared_partition(const struct partition *p)
969 return !(!p || p->boot_ind || p->head || p->sector || p->cyl ||
970 p->sys_ind || p->end_head || p->end_sector || p->end_cyl ||
971 get_start_sect(p) || get_nr_sects(p));
974 static void
975 clear_partition(struct partition *p)
977 if (!p)
978 return;
979 memset(p, 0, sizeof(struct partition));
982 #if ENABLE_FEATURE_FDISK_WRITABLE
983 static void
984 set_partition(int i, int doext, ullong start, ullong stop, int sysid)
986 struct partition *p;
987 ullong offset;
989 if (doext) {
990 p = ptes[i].ext_pointer;
991 offset = extended_offset;
992 } else {
993 p = ptes[i].part_table;
994 offset = ptes[i].offset;
996 p->boot_ind = 0;
997 p->sys_ind = sysid;
998 set_start_sect(p, start - offset);
999 set_nr_sects(p, stop - start + 1);
1000 if (dos_compatible_flag && (start / (g_sectors * g_heads) > 1023))
1001 start = g_heads * g_sectors * 1024 - 1;
1002 set_hsc(p->head, p->sector, p->cyl, start);
1003 if (dos_compatible_flag && (stop / (g_sectors * g_heads) > 1023))
1004 stop = g_heads * g_sectors * 1024 - 1;
1005 set_hsc(p->end_head, p->end_sector, p->end_cyl, stop);
1006 ptes[i].changed = 1;
1008 #endif
1010 static int
1011 warn_geometry(void)
1013 if (g_heads && g_sectors && g_cylinders)
1014 return 0;
1016 printf("Unknown value(s) for:");
1017 if (!g_heads)
1018 printf(" heads");
1019 if (!g_sectors)
1020 printf(" sectors");
1021 if (!g_cylinders)
1022 printf(" cylinders");
1023 printf(
1024 #if ENABLE_FEATURE_FDISK_WRITABLE
1025 " (settable in the extra functions menu)"
1026 #endif
1027 "\n");
1028 return 1;
1031 static void
1032 update_units(void)
1034 int cyl_units = g_heads * g_sectors;
1036 if (display_in_cyl_units && cyl_units)
1037 units_per_sector = cyl_units;
1038 else
1039 units_per_sector = 1; /* in sectors */
1042 #if ENABLE_FEATURE_FDISK_WRITABLE
1043 static void
1044 warn_cylinders(void)
1046 if (LABEL_IS_DOS && g_cylinders > 1024 && !nowarn)
1047 printf("\n"
1048 "The number of cylinders for this disk is set to %d.\n"
1049 "There is nothing wrong with that, but this is larger than 1024,\n"
1050 "and could in certain setups cause problems with:\n"
1051 "1) software that runs at boot time (e.g., old versions of LILO)\n"
1052 "2) booting and partitioning software from other OSs\n"
1053 " (e.g., DOS FDISK, OS/2 FDISK)\n",
1054 g_cylinders);
1056 #endif
1058 static void
1059 read_extended(int ext)
1061 int i;
1062 struct pte *pex;
1063 struct partition *p, *q;
1065 ext_index = ext;
1066 pex = &ptes[ext];
1067 pex->ext_pointer = pex->part_table;
1069 p = pex->part_table;
1070 if (!get_start_sect(p)) {
1071 printf("Bad offset in primary extended partition\n");
1072 return;
1075 while (IS_EXTENDED(p->sys_ind)) {
1076 struct pte *pe = &ptes[g_partitions];
1078 if (g_partitions >= MAXIMUM_PARTS) {
1079 /* This is not a Linux restriction, but
1080 this program uses arrays of size MAXIMUM_PARTS.
1081 Do not try to 'improve' this test. */
1082 struct pte *pre = &ptes[g_partitions - 1];
1083 #if ENABLE_FEATURE_FDISK_WRITABLE
1084 printf("Warning: deleting partitions after %d\n",
1085 g_partitions);
1086 pre->changed = 1;
1087 #endif
1088 clear_partition(pre->ext_pointer);
1089 return;
1092 read_pte(pe, extended_offset + get_start_sect(p));
1094 if (!extended_offset)
1095 extended_offset = get_start_sect(p);
1097 q = p = pt_offset(pe->sectorbuffer, 0);
1098 for (i = 0; i < 4; i++, p++) if (get_nr_sects(p)) {
1099 if (IS_EXTENDED(p->sys_ind)) {
1100 if (pe->ext_pointer)
1101 printf("Warning: extra link "
1102 "pointer in partition table"
1103 " %d\n", g_partitions + 1);
1104 else
1105 pe->ext_pointer = p;
1106 } else if (p->sys_ind) {
1107 if (pe->part_table)
1108 printf("Warning: ignoring extra "
1109 "data in partition table"
1110 " %d\n", g_partitions + 1);
1111 else
1112 pe->part_table = p;
1116 /* very strange code here... */
1117 if (!pe->part_table) {
1118 if (q != pe->ext_pointer)
1119 pe->part_table = q;
1120 else
1121 pe->part_table = q + 1;
1123 if (!pe->ext_pointer) {
1124 if (q != pe->part_table)
1125 pe->ext_pointer = q;
1126 else
1127 pe->ext_pointer = q + 1;
1130 p = pe->ext_pointer;
1131 g_partitions++;
1134 #if ENABLE_FEATURE_FDISK_WRITABLE
1135 /* remove empty links */
1136 remove:
1137 for (i = 4; i < g_partitions; i++) {
1138 struct pte *pe = &ptes[i];
1140 if (!get_nr_sects(pe->part_table)
1141 && (g_partitions > 5 || ptes[4].part_table->sys_ind)
1143 printf("Omitting empty partition (%d)\n", i+1);
1144 delete_partition(i);
1145 goto remove; /* numbering changed */
1148 #endif
1151 #if ENABLE_FEATURE_FDISK_WRITABLE
1152 static void
1153 create_doslabel(void)
1155 int i;
1157 printf(msg_building_new_label, "DOS disklabel");
1159 current_label_type = LABEL_DOS;
1161 #if ENABLE_FEATURE_OSF_LABEL
1162 possibly_osf_label = 0;
1163 #endif
1164 g_partitions = 4;
1166 for (i = 510-64; i < 510; i++)
1167 MBRbuffer[i] = 0;
1168 write_part_table_flag(MBRbuffer);
1169 extended_offset = 0;
1170 set_all_unchanged();
1171 set_changed(0);
1172 get_boot(CREATE_EMPTY_DOS);
1174 #endif /* FEATURE_FDISK_WRITABLE */
1176 static void
1177 get_sectorsize(void)
1179 if (!user_set_sector_size) {
1180 int arg;
1181 if (ioctl(dev_fd, BLKSSZGET, &arg) == 0)
1182 sector_size = arg;
1183 if (sector_size != DEFAULT_SECTOR_SIZE)
1184 printf("Note: sector size is %d "
1185 "(not " DEFAULT_SECTOR_SIZE_STR ")\n",
1186 sector_size);
1190 static void
1191 get_kernel_geometry(void)
1193 struct hd_geometry geometry;
1195 if (!ioctl(dev_fd, HDIO_GETGEO, &geometry)) {
1196 kern_heads = geometry.heads;
1197 kern_sectors = geometry.sectors;
1198 /* never use geometry.cylinders - it is truncated */
1202 static void
1203 get_partition_table_geometry(void)
1205 const unsigned char *bufp = (const unsigned char *)MBRbuffer;
1206 struct partition *p;
1207 int i, h, s, hh, ss;
1208 int first = 1;
1209 int bad = 0;
1211 if (!(valid_part_table_flag((char*)bufp)))
1212 return;
1214 hh = ss = 0;
1215 for (i = 0; i < 4; i++) {
1216 p = pt_offset(bufp, i);
1217 if (p->sys_ind != 0) {
1218 h = p->end_head + 1;
1219 s = (p->end_sector & 077);
1220 if (first) {
1221 hh = h;
1222 ss = s;
1223 first = 0;
1224 } else if (hh != h || ss != s)
1225 bad = 1;
1229 if (!first && !bad) {
1230 pt_heads = hh;
1231 pt_sectors = ss;
1235 static void
1236 get_geometry(void)
1238 int sec_fac;
1240 get_sectorsize();
1241 sec_fac = sector_size / 512;
1242 #if ENABLE_FEATURE_SUN_LABEL
1243 guess_device_type();
1244 #endif
1245 g_heads = g_cylinders = g_sectors = 0;
1246 kern_heads = kern_sectors = 0;
1247 pt_heads = pt_sectors = 0;
1249 get_kernel_geometry();
1250 get_partition_table_geometry();
1252 g_heads = user_heads ? user_heads :
1253 pt_heads ? pt_heads :
1254 kern_heads ? kern_heads : 255;
1255 g_sectors = user_sectors ? user_sectors :
1256 pt_sectors ? pt_sectors :
1257 kern_sectors ? kern_sectors : 63;
1258 total_number_of_sectors = bb_BLKGETSIZE_sectors(dev_fd);
1260 sector_offset = 1;
1261 if (dos_compatible_flag)
1262 sector_offset = g_sectors;
1264 g_cylinders = total_number_of_sectors / (g_heads * g_sectors * sec_fac);
1265 if (!g_cylinders)
1266 g_cylinders = user_cylinders;
1270 * Opens disk_device and optionally reads MBR.
1271 * FIXME: document what each 'what' value will do!
1272 * Returns:
1273 * -1: no 0xaa55 flag present (possibly entire disk BSD)
1274 * 0: found or created label
1275 * 1: I/O error
1277 #if ENABLE_FEATURE_SUN_LABEL || ENABLE_FEATURE_FDISK_WRITABLE
1278 static int get_boot(enum action what)
1279 #else
1280 static int get_boot(void)
1281 #define get_boot(what) get_boot()
1282 #endif
1284 int i, fd;
1286 g_partitions = 4;
1287 for (i = 0; i < 4; i++) {
1288 struct pte *pe = &ptes[i];
1289 pe->part_table = pt_offset(MBRbuffer, i);
1290 pe->ext_pointer = NULL;
1291 pe->offset = 0;
1292 pe->sectorbuffer = MBRbuffer;
1293 #if ENABLE_FEATURE_FDISK_WRITABLE
1294 pe->changed = (what == CREATE_EMPTY_DOS);
1295 #endif
1298 #if ENABLE_FEATURE_FDISK_WRITABLE
1299 // ALERT! highly idiotic design!
1300 // We end up here when we call get_boot() recursively
1301 // via get_boot() [table is bad] -> create_doslabel() -> get_boot(CREATE_EMPTY_DOS).
1302 // or get_boot() [table is bad] -> create_sunlabel() -> get_boot(CREATE_EMPTY_SUN).
1303 // (just factor out re-init of ptes[0,1,2,3] in a separate fn instead?)
1304 // So skip opening device _again_...
1305 if (what == CREATE_EMPTY_DOS USE_FEATURE_SUN_LABEL(|| what == CREATE_EMPTY_SUN))
1306 goto created_table;
1308 fd = open(disk_device, (option_mask32 & OPT_l) ? O_RDONLY : O_RDWR);
1310 if (fd < 0) {
1311 fd = open(disk_device, O_RDONLY);
1312 if (fd < 0) {
1313 if (what == TRY_ONLY)
1314 return 1;
1315 fdisk_fatal(unable_to_open);
1317 printf("'%s' is opened for read only\n", disk_device);
1319 xmove_fd(fd, dev_fd);
1320 if (512 != full_read(dev_fd, MBRbuffer, 512)) {
1321 if (what == TRY_ONLY) {
1322 close_dev_fd();
1323 return 1;
1325 fdisk_fatal(unable_to_read);
1327 #else
1328 fd = open(disk_device, O_RDONLY);
1329 if (fd < 0)
1330 return 1;
1331 if (512 != full_read(fd, MBRbuffer, 512)) {
1332 close(fd);
1333 return 1;
1335 xmove_fd(fd, dev_fd);
1336 #endif
1338 get_geometry();
1339 update_units();
1341 #if ENABLE_FEATURE_SUN_LABEL
1342 if (check_sun_label())
1343 return 0;
1344 #endif
1345 #if ENABLE_FEATURE_SGI_LABEL
1346 if (check_sgi_label())
1347 return 0;
1348 #endif
1349 #if ENABLE_FEATURE_AIX_LABEL
1350 if (check_aix_label())
1351 return 0;
1352 #endif
1353 #if ENABLE_FEATURE_OSF_LABEL
1354 if (check_osf_label()) {
1355 possibly_osf_label = 1;
1356 if (!valid_part_table_flag(MBRbuffer)) {
1357 current_label_type = LABEL_OSF;
1358 return 0;
1360 printf("This disk has both DOS and BSD magic.\n"
1361 "Give the 'b' command to go to BSD mode.\n");
1363 #endif
1365 #if !ENABLE_FEATURE_FDISK_WRITABLE
1366 if (!valid_part_table_flag(MBRbuffer))
1367 return -1;
1368 #else
1369 if (!valid_part_table_flag(MBRbuffer)) {
1370 if (what == OPEN_MAIN) {
1371 printf("Device contains neither a valid DOS "
1372 "partition table, nor Sun, SGI or OSF "
1373 "disklabel\n");
1374 #ifdef __sparc__
1375 USE_FEATURE_SUN_LABEL(create_sunlabel();)
1376 #else
1377 create_doslabel();
1378 #endif
1379 return 0;
1381 /* TRY_ONLY: */
1382 return -1;
1384 created_table:
1385 #endif /* FEATURE_FDISK_WRITABLE */
1388 USE_FEATURE_FDISK_WRITABLE(warn_cylinders();)
1389 warn_geometry();
1391 for (i = 0; i < 4; i++) {
1392 if (IS_EXTENDED(ptes[i].part_table->sys_ind)) {
1393 if (g_partitions != 4)
1394 printf("Ignoring extra extended "
1395 "partition %d\n", i + 1);
1396 else
1397 read_extended(i);
1401 for (i = 3; i < g_partitions; i++) {
1402 struct pte *pe = &ptes[i];
1403 if (!valid_part_table_flag(pe->sectorbuffer)) {
1404 printf("Warning: invalid flag 0x%02x,0x%02x of partition "
1405 "table %d will be corrected by w(rite)\n",
1406 pe->sectorbuffer[510],
1407 pe->sectorbuffer[511],
1408 i + 1);
1409 USE_FEATURE_FDISK_WRITABLE(pe->changed = 1;)
1413 return 0;
1416 #if ENABLE_FEATURE_FDISK_WRITABLE
1418 * Print the message MESG, then read an integer between LOW and HIGH (inclusive).
1419 * If the user hits Enter, DFLT is returned.
1420 * Answers like +10 are interpreted as offsets from BASE.
1422 * There is no default if DFLT is not between LOW and HIGH.
1424 static unsigned
1425 read_int(unsigned low, unsigned dflt, unsigned high, unsigned base, const char *mesg)
1427 unsigned i;
1428 int default_ok = 1;
1429 const char *fmt = "%s (%u-%u, default %u): ";
1431 if (dflt < low || dflt > high) {
1432 fmt = "%s (%u-%u): ";
1433 default_ok = 0;
1436 while (1) {
1437 int use_default = default_ok;
1439 /* ask question and read answer */
1440 do {
1441 printf(fmt, mesg, low, high, dflt);
1442 read_maybe_empty("");
1443 } while (*line_ptr != '\n' && !isdigit(*line_ptr)
1444 && *line_ptr != '-' && *line_ptr != '+');
1446 if (*line_ptr == '+' || *line_ptr == '-') {
1447 int minus = (*line_ptr == '-');
1448 int absolute = 0;
1450 i = atoi(line_ptr + 1);
1452 while (isdigit(*++line_ptr))
1453 use_default = 0;
1455 switch (*line_ptr) {
1456 case 'c':
1457 case 'C':
1458 if (!display_in_cyl_units)
1459 i *= g_heads * g_sectors;
1460 break;
1461 case 'K':
1462 absolute = 1024;
1463 break;
1464 case 'k':
1465 absolute = 1000;
1466 break;
1467 case 'm':
1468 case 'M':
1469 absolute = 1000000;
1470 break;
1471 case 'g':
1472 case 'G':
1473 absolute = 1000000000;
1474 break;
1475 default:
1476 break;
1478 if (absolute) {
1479 ullong bytes;
1480 unsigned long unit;
1482 bytes = (ullong) i * absolute;
1483 unit = sector_size * units_per_sector;
1484 bytes += unit/2; /* round */
1485 bytes /= unit;
1486 i = bytes;
1488 if (minus)
1489 i = -i;
1490 i += base;
1491 } else {
1492 i = atoi(line_ptr);
1493 while (isdigit(*line_ptr)) {
1494 line_ptr++;
1495 use_default = 0;
1498 if (use_default) {
1499 i = dflt;
1500 printf("Using default value %u\n", i);
1502 if (i >= low && i <= high)
1503 break;
1504 printf("Value is out of range\n");
1506 return i;
1509 static int
1510 get_partition(int warn, int max)
1512 struct pte *pe;
1513 int i;
1515 i = read_int(1, 0, max, 0, "Partition number") - 1;
1516 pe = &ptes[i];
1518 if (warn) {
1519 if ((!LABEL_IS_SUN && !LABEL_IS_SGI && !pe->part_table->sys_ind)
1520 || (LABEL_IS_SUN && (!sunlabel->partitions[i].num_sectors || !sunlabel->infos[i].id))
1521 || (LABEL_IS_SGI && !sgi_get_num_sectors(i))
1523 printf("Warning: partition %d has empty type\n", i+1);
1526 return i;
1529 static int
1530 get_existing_partition(int warn, int max)
1532 int pno = -1;
1533 int i;
1535 for (i = 0; i < max; i++) {
1536 struct pte *pe = &ptes[i];
1537 struct partition *p = pe->part_table;
1539 if (p && !is_cleared_partition(p)) {
1540 if (pno >= 0)
1541 goto not_unique;
1542 pno = i;
1545 if (pno >= 0) {
1546 printf("Selected partition %d\n", pno+1);
1547 return pno;
1549 printf("No partition is defined yet!\n");
1550 return -1;
1552 not_unique:
1553 return get_partition(warn, max);
1556 static int
1557 get_nonexisting_partition(int warn, int max)
1559 int pno = -1;
1560 int i;
1562 for (i = 0; i < max; i++) {
1563 struct pte *pe = &ptes[i];
1564 struct partition *p = pe->part_table;
1566 if (p && is_cleared_partition(p)) {
1567 if (pno >= 0)
1568 goto not_unique;
1569 pno = i;
1572 if (pno >= 0) {
1573 printf("Selected partition %d\n", pno+1);
1574 return pno;
1576 printf("All primary partitions have been defined already!\n");
1577 return -1;
1579 not_unique:
1580 return get_partition(warn, max);
1584 static void
1585 change_units(void)
1587 display_in_cyl_units = !display_in_cyl_units;
1588 update_units();
1589 printf("Changing display/entry units to %s\n",
1590 str_units(PLURAL));
1593 static void
1594 toggle_active(int i)
1596 struct pte *pe = &ptes[i];
1597 struct partition *p = pe->part_table;
1599 if (IS_EXTENDED(p->sys_ind) && !p->boot_ind)
1600 printf("WARNING: Partition %d is an extended partition\n", i + 1);
1601 p->boot_ind = (p->boot_ind ? 0 : ACTIVE_FLAG);
1602 pe->changed = 1;
1605 static void
1606 toggle_dos_compatibility_flag(void)
1608 dos_compatible_flag = 1 - dos_compatible_flag;
1609 if (dos_compatible_flag) {
1610 sector_offset = g_sectors;
1611 printf("DOS Compatibility flag is set\n");
1612 } else {
1613 sector_offset = 1;
1614 printf("DOS Compatibility flag is not set\n");
1618 static void
1619 delete_partition(int i)
1621 struct pte *pe = &ptes[i];
1622 struct partition *p = pe->part_table;
1623 struct partition *q = pe->ext_pointer;
1625 /* Note that for the fifth partition (i == 4) we don't actually
1626 * decrement partitions.
1629 if (warn_geometry())
1630 return; /* C/H/S not set */
1631 pe->changed = 1;
1633 if (LABEL_IS_SUN) {
1634 sun_delete_partition(i);
1635 return;
1637 if (LABEL_IS_SGI) {
1638 sgi_delete_partition(i);
1639 return;
1642 if (i < 4) {
1643 if (IS_EXTENDED(p->sys_ind) && i == ext_index) {
1644 g_partitions = 4;
1645 ptes[ext_index].ext_pointer = NULL;
1646 extended_offset = 0;
1648 clear_partition(p);
1649 return;
1652 if (!q->sys_ind && i > 4) {
1653 /* the last one in the chain - just delete */
1654 --g_partitions;
1655 --i;
1656 clear_partition(ptes[i].ext_pointer);
1657 ptes[i].changed = 1;
1658 } else {
1659 /* not the last one - further ones will be moved down */
1660 if (i > 4) {
1661 /* delete this link in the chain */
1662 p = ptes[i-1].ext_pointer;
1663 *p = *q;
1664 set_start_sect(p, get_start_sect(q));
1665 set_nr_sects(p, get_nr_sects(q));
1666 ptes[i-1].changed = 1;
1667 } else if (g_partitions > 5) { /* 5 will be moved to 4 */
1668 /* the first logical in a longer chain */
1669 pe = &ptes[5];
1671 if (pe->part_table) /* prevent SEGFAULT */
1672 set_start_sect(pe->part_table,
1673 get_partition_start(pe) -
1674 extended_offset);
1675 pe->offset = extended_offset;
1676 pe->changed = 1;
1679 if (g_partitions > 5) {
1680 g_partitions--;
1681 while (i < g_partitions) {
1682 ptes[i] = ptes[i+1];
1683 i++;
1685 } else
1686 /* the only logical: clear only */
1687 clear_partition(ptes[i].part_table);
1691 static void
1692 change_sysid(void)
1694 int i, sys, origsys;
1695 struct partition *p;
1697 /* If sgi_label then don't use get_existing_partition,
1698 let the user select a partition, since get_existing_partition()
1699 only works for Linux like partition tables. */
1700 if (!LABEL_IS_SGI) {
1701 i = get_existing_partition(0, g_partitions);
1702 } else {
1703 i = get_partition(0, g_partitions);
1705 if (i == -1)
1706 return;
1707 p = ptes[i].part_table;
1708 origsys = sys = get_sysid(i);
1710 /* if changing types T to 0 is allowed, then
1711 the reverse change must be allowed, too */
1712 if (!sys && !LABEL_IS_SGI && !LABEL_IS_SUN && !get_nr_sects(p)) {
1713 printf("Partition %d does not exist yet!\n", i + 1);
1714 return;
1716 while (1) {
1717 sys = read_hex(get_sys_types());
1719 if (!sys && !LABEL_IS_SGI && !LABEL_IS_SUN) {
1720 printf("Type 0 means free space to many systems\n"
1721 "(but not to Linux). Having partitions of\n"
1722 "type 0 is probably unwise.\n");
1723 /* break; */
1726 if (!LABEL_IS_SUN && !LABEL_IS_SGI) {
1727 if (IS_EXTENDED(sys) != IS_EXTENDED(p->sys_ind)) {
1728 printf("You cannot change a partition into"
1729 " an extended one or vice versa\n");
1730 break;
1734 if (sys < 256) {
1735 #if ENABLE_FEATURE_SUN_LABEL
1736 if (LABEL_IS_SUN && i == 2 && sys != SUN_WHOLE_DISK)
1737 printf("Consider leaving partition 3 "
1738 "as Whole disk (5),\n"
1739 "as SunOS/Solaris expects it and "
1740 "even Linux likes it\n\n");
1741 #endif
1742 #if ENABLE_FEATURE_SGI_LABEL
1743 if (LABEL_IS_SGI &&
1745 (i == 10 && sys != SGI_ENTIRE_DISK) ||
1746 (i == 8 && sys != 0)
1749 printf("Consider leaving partition 9 "
1750 "as volume header (0),\nand "
1751 "partition 11 as entire volume (6)"
1752 "as IRIX expects it\n\n");
1754 #endif
1755 if (sys == origsys)
1756 break;
1757 if (LABEL_IS_SUN) {
1758 sun_change_sysid(i, sys);
1759 } else if (LABEL_IS_SGI) {
1760 sgi_change_sysid(i, sys);
1761 } else
1762 p->sys_ind = sys;
1764 printf("Changed system type of partition %d "
1765 "to %x (%s)\n", i + 1, sys,
1766 partition_type(sys));
1767 ptes[i].changed = 1;
1768 //if (is_dos_partition(origsys) || is_dos_partition(sys))
1769 // dos_changed = 1;
1770 break;
1774 #endif /* FEATURE_FDISK_WRITABLE */
1777 /* check_consistency() and linear2chs() added Sat Mar 6 12:28:16 1993,
1778 * faith@cs.unc.edu, based on code fragments from pfdisk by Gordon W. Ross,
1779 * Jan. 1990 (version 1.2.1 by Gordon W. Ross Aug. 1990; Modified by S.
1780 * Lubkin Oct. 1991). */
1782 static void
1783 linear2chs(unsigned ls, unsigned *c, unsigned *h, unsigned *s)
1785 int spc = g_heads * g_sectors;
1787 *c = ls / spc;
1788 ls = ls % spc;
1789 *h = ls / g_sectors;
1790 *s = ls % g_sectors + 1; /* sectors count from 1 */
1793 static void
1794 check_consistency(const struct partition *p, int partition)
1796 unsigned pbc, pbh, pbs; /* physical beginning c, h, s */
1797 unsigned pec, peh, pes; /* physical ending c, h, s */
1798 unsigned lbc, lbh, lbs; /* logical beginning c, h, s */
1799 unsigned lec, leh, les; /* logical ending c, h, s */
1801 if (!g_heads || !g_sectors || (partition >= 4))
1802 return; /* do not check extended partitions */
1804 /* physical beginning c, h, s */
1805 pbc = (p->cyl & 0xff) | ((p->sector << 2) & 0x300);
1806 pbh = p->head;
1807 pbs = p->sector & 0x3f;
1809 /* physical ending c, h, s */
1810 pec = (p->end_cyl & 0xff) | ((p->end_sector << 2) & 0x300);
1811 peh = p->end_head;
1812 pes = p->end_sector & 0x3f;
1814 /* compute logical beginning (c, h, s) */
1815 linear2chs(get_start_sect(p), &lbc, &lbh, &lbs);
1817 /* compute logical ending (c, h, s) */
1818 linear2chs(get_start_sect(p) + get_nr_sects(p) - 1, &lec, &leh, &les);
1820 /* Same physical / logical beginning? */
1821 if (g_cylinders <= 1024 && (pbc != lbc || pbh != lbh || pbs != lbs)) {
1822 printf("Partition %d has different physical/logical "
1823 "beginnings (non-Linux?):\n", partition + 1);
1824 printf(" phys=(%d, %d, %d) ", pbc, pbh, pbs);
1825 printf("logical=(%d, %d, %d)\n", lbc, lbh, lbs);
1828 /* Same physical / logical ending? */
1829 if (g_cylinders <= 1024 && (pec != lec || peh != leh || pes != les)) {
1830 printf("Partition %d has different physical/logical "
1831 "endings:\n", partition + 1);
1832 printf(" phys=(%d, %d, %d) ", pec, peh, pes);
1833 printf("logical=(%d, %d, %d)\n", lec, leh, les);
1836 /* Ending on cylinder boundary? */
1837 if (peh != (g_heads - 1) || pes != g_sectors) {
1838 printf("Partition %i does not end on cylinder boundary\n",
1839 partition + 1);
1843 static void
1844 list_disk_geometry(void)
1846 long long bytes = (total_number_of_sectors << 9);
1847 long megabytes = bytes/1000000;
1849 if (megabytes < 10000)
1850 printf("\nDisk %s: %ld MB, %lld bytes\n",
1851 disk_device, megabytes, bytes);
1852 else
1853 printf("\nDisk %s: %ld.%ld GB, %lld bytes\n",
1854 disk_device, megabytes/1000, (megabytes/100)%10, bytes);
1855 printf("%d heads, %d sectors/track, %d cylinders",
1856 g_heads, g_sectors, g_cylinders);
1857 if (units_per_sector == 1)
1858 printf(", total %llu sectors",
1859 total_number_of_sectors / (sector_size/512));
1860 printf("\nUnits = %s of %d * %d = %d bytes\n\n",
1861 str_units(PLURAL),
1862 units_per_sector, sector_size, units_per_sector * sector_size);
1866 * Check whether partition entries are ordered by their starting positions.
1867 * Return 0 if OK. Return i if partition i should have been earlier.
1868 * Two separate checks: primary and logical partitions.
1870 static int
1871 wrong_p_order(int *prev)
1873 const struct pte *pe;
1874 const struct partition *p;
1875 ullong last_p_start_pos = 0, p_start_pos;
1876 int i, last_i = 0;
1878 for (i = 0; i < g_partitions; i++) {
1879 if (i == 4) {
1880 last_i = 4;
1881 last_p_start_pos = 0;
1883 pe = &ptes[i];
1884 p = pe->part_table;
1885 if (p->sys_ind) {
1886 p_start_pos = get_partition_start(pe);
1888 if (last_p_start_pos > p_start_pos) {
1889 if (prev)
1890 *prev = last_i;
1891 return i;
1894 last_p_start_pos = p_start_pos;
1895 last_i = i;
1898 return 0;
1901 #if ENABLE_FEATURE_FDISK_ADVANCED
1903 * Fix the chain of logicals.
1904 * extended_offset is unchanged, the set of sectors used is unchanged
1905 * The chain is sorted so that sectors increase, and so that
1906 * starting sectors increase.
1908 * After this it may still be that cfdisk doesnt like the table.
1909 * (This is because cfdisk considers expanded parts, from link to
1910 * end of partition, and these may still overlap.)
1911 * Now
1912 * sfdisk /dev/hda > ohda; sfdisk /dev/hda < ohda
1913 * may help.
1915 static void
1916 fix_chain_of_logicals(void)
1918 int j, oj, ojj, sj, sjj;
1919 struct partition *pj,*pjj,tmp;
1921 /* Stage 1: sort sectors but leave sector of part 4 */
1922 /* (Its sector is the global extended_offset.) */
1923 stage1:
1924 for (j = 5; j < g_partitions - 1; j++) {
1925 oj = ptes[j].offset;
1926 ojj = ptes[j+1].offset;
1927 if (oj > ojj) {
1928 ptes[j].offset = ojj;
1929 ptes[j+1].offset = oj;
1930 pj = ptes[j].part_table;
1931 set_start_sect(pj, get_start_sect(pj)+oj-ojj);
1932 pjj = ptes[j+1].part_table;
1933 set_start_sect(pjj, get_start_sect(pjj)+ojj-oj);
1934 set_start_sect(ptes[j-1].ext_pointer,
1935 ojj-extended_offset);
1936 set_start_sect(ptes[j].ext_pointer,
1937 oj-extended_offset);
1938 goto stage1;
1942 /* Stage 2: sort starting sectors */
1943 stage2:
1944 for (j = 4; j < g_partitions - 1; j++) {
1945 pj = ptes[j].part_table;
1946 pjj = ptes[j+1].part_table;
1947 sj = get_start_sect(pj);
1948 sjj = get_start_sect(pjj);
1949 oj = ptes[j].offset;
1950 ojj = ptes[j+1].offset;
1951 if (oj+sj > ojj+sjj) {
1952 tmp = *pj;
1953 *pj = *pjj;
1954 *pjj = tmp;
1955 set_start_sect(pj, ojj+sjj-oj);
1956 set_start_sect(pjj, oj+sj-ojj);
1957 goto stage2;
1961 /* Probably something was changed */
1962 for (j = 4; j < g_partitions; j++)
1963 ptes[j].changed = 1;
1967 static void
1968 fix_partition_table_order(void)
1970 struct pte *pei, *pek;
1971 int i,k;
1973 if (!wrong_p_order(NULL)) {
1974 printf("Ordering is already correct\n\n");
1975 return;
1978 while ((i = wrong_p_order(&k)) != 0 && i < 4) {
1979 /* partition i should have come earlier, move it */
1980 /* We have to move data in the MBR */
1981 struct partition *pi, *pk, *pe, pbuf;
1982 pei = &ptes[i];
1983 pek = &ptes[k];
1985 pe = pei->ext_pointer;
1986 pei->ext_pointer = pek->ext_pointer;
1987 pek->ext_pointer = pe;
1989 pi = pei->part_table;
1990 pk = pek->part_table;
1992 memmove(&pbuf, pi, sizeof(struct partition));
1993 memmove(pi, pk, sizeof(struct partition));
1994 memmove(pk, &pbuf, sizeof(struct partition));
1996 pei->changed = pek->changed = 1;
1999 if (i)
2000 fix_chain_of_logicals();
2002 printf("Done.\n");
2005 #endif
2007 static void
2008 list_table(int xtra)
2010 const struct partition *p;
2011 int i, w;
2013 if (LABEL_IS_SUN) {
2014 sun_list_table(xtra);
2015 return;
2017 if (LABEL_IS_SUN) {
2018 sgi_list_table(xtra);
2019 return;
2022 list_disk_geometry();
2024 if (LABEL_IS_OSF) {
2025 xbsd_print_disklabel(xtra);
2026 return;
2029 /* Heuristic: we list partition 3 of /dev/foo as /dev/foo3,
2030 but if the device name ends in a digit, say /dev/foo1,
2031 then the partition is called /dev/foo1p3. */
2032 w = strlen(disk_device);
2033 if (w && isdigit(disk_device[w-1]))
2034 w++;
2035 if (w < 5)
2036 w = 5;
2038 // 1 12345678901 12345678901 12345678901 12
2039 printf("%*s Boot Start End Blocks Id System\n",
2040 w+1, "Device");
2042 for (i = 0; i < g_partitions; i++) {
2043 const struct pte *pe = &ptes[i];
2044 ullong psects;
2045 ullong pblocks;
2046 unsigned podd;
2048 p = pe->part_table;
2049 if (!p || is_cleared_partition(p))
2050 continue;
2052 psects = get_nr_sects(p);
2053 pblocks = psects;
2054 podd = 0;
2056 if (sector_size < 1024) {
2057 pblocks /= (1024 / sector_size);
2058 podd = psects % (1024 / sector_size);
2060 if (sector_size > 1024)
2061 pblocks *= (sector_size / 1024);
2063 printf("%s %c %11llu %11llu %11llu%c %2x %s\n",
2064 partname(disk_device, i+1, w+2),
2065 !p->boot_ind ? ' ' : p->boot_ind == ACTIVE_FLAG /* boot flag */
2066 ? '*' : '?',
2067 (ullong) cround(get_partition_start(pe)), /* start */
2068 (ullong) cround(get_partition_start(pe) + psects /* end */
2069 - (psects ? 1 : 0)),
2070 (ullong) pblocks, podd ? '+' : ' ', /* odd flag on end */
2071 p->sys_ind, /* type id */
2072 partition_type(p->sys_ind)); /* type name */
2074 check_consistency(p, i);
2077 /* Is partition table in disk order? It need not be, but... */
2078 /* partition table entries are not checked for correct order if this
2079 is a sgi, sun or aix labeled disk... */
2080 if (LABEL_IS_DOS && wrong_p_order(NULL)) {
2081 /* FIXME */
2082 printf("\nPartition table entries are not in disk order\n");
2086 #if ENABLE_FEATURE_FDISK_ADVANCED
2087 static void
2088 x_list_table(int extend)
2090 const struct pte *pe;
2091 const struct partition *p;
2092 int i;
2094 printf("\nDisk %s: %d heads, %d sectors, %d cylinders\n\n",
2095 disk_device, g_heads, g_sectors, g_cylinders);
2096 printf("Nr AF Hd Sec Cyl Hd Sec Cyl Start Size ID\n");
2097 for (i = 0; i < g_partitions; i++) {
2098 pe = &ptes[i];
2099 p = (extend ? pe->ext_pointer : pe->part_table);
2100 if (p != NULL) {
2101 printf("%2d %02x%4d%4d%5d%4d%4d%5d%11u%11u %02x\n",
2102 i + 1, p->boot_ind, p->head,
2103 sector(p->sector),
2104 cylinder(p->sector, p->cyl), p->end_head,
2105 sector(p->end_sector),
2106 cylinder(p->end_sector, p->end_cyl),
2107 get_start_sect(p), get_nr_sects(p), p->sys_ind);
2108 if (p->sys_ind)
2109 check_consistency(p, i);
2113 #endif
2115 #if ENABLE_FEATURE_FDISK_WRITABLE
2116 static void
2117 fill_bounds(ullong *first, ullong *last)
2119 int i;
2120 const struct pte *pe = &ptes[0];
2121 const struct partition *p;
2123 for (i = 0; i < g_partitions; pe++,i++) {
2124 p = pe->part_table;
2125 if (!p->sys_ind || IS_EXTENDED(p->sys_ind)) {
2126 first[i] = 0xffffffff;
2127 last[i] = 0;
2128 } else {
2129 first[i] = get_partition_start(pe);
2130 last[i] = first[i] + get_nr_sects(p) - 1;
2135 static void
2136 check(int n, unsigned h, unsigned s, unsigned c, ullong start)
2138 ullong total, real_s, real_c;
2140 real_s = sector(s) - 1;
2141 real_c = cylinder(s, c);
2142 total = (real_c * g_sectors + real_s) * g_heads + h;
2143 if (!total)
2144 printf("Partition %d contains sector 0\n", n);
2145 if (h >= g_heads)
2146 printf("Partition %d: head %d greater than maximum %d\n",
2147 n, h + 1, g_heads);
2148 if (real_s >= g_sectors)
2149 printf("Partition %d: sector %d greater than "
2150 "maximum %d\n", n, s, g_sectors);
2151 if (real_c >= g_cylinders)
2152 printf("Partition %d: cylinder %llu greater than "
2153 "maximum %d\n", n, real_c + 1, g_cylinders);
2154 if (g_cylinders <= 1024 && start != total)
2155 printf("Partition %d: previous sectors %llu disagrees with "
2156 "total %llu\n", n, start, total);
2159 static void
2160 verify(void)
2162 int i, j;
2163 unsigned total = 1;
2164 ullong first[g_partitions], last[g_partitions];
2165 struct partition *p;
2167 if (warn_geometry())
2168 return;
2170 if (LABEL_IS_SUN) {
2171 verify_sun();
2172 return;
2174 if (LABEL_IS_SGI) {
2175 verify_sgi(1);
2176 return;
2179 fill_bounds(first, last);
2180 for (i = 0; i < g_partitions; i++) {
2181 struct pte *pe = &ptes[i];
2183 p = pe->part_table;
2184 if (p->sys_ind && !IS_EXTENDED(p->sys_ind)) {
2185 check_consistency(p, i);
2186 if (get_partition_start(pe) < first[i])
2187 printf("Warning: bad start-of-data in "
2188 "partition %d\n", i + 1);
2189 check(i + 1, p->end_head, p->end_sector, p->end_cyl,
2190 last[i]);
2191 total += last[i] + 1 - first[i];
2192 for (j = 0; j < i; j++) {
2193 if ((first[i] >= first[j] && first[i] <= last[j])
2194 || ((last[i] <= last[j] && last[i] >= first[j]))) {
2195 printf("Warning: partition %d overlaps "
2196 "partition %d\n", j + 1, i + 1);
2197 total += first[i] >= first[j] ?
2198 first[i] : first[j];
2199 total -= last[i] <= last[j] ?
2200 last[i] : last[j];
2206 if (extended_offset) {
2207 struct pte *pex = &ptes[ext_index];
2208 ullong e_last = get_start_sect(pex->part_table) +
2209 get_nr_sects(pex->part_table) - 1;
2211 for (i = 4; i < g_partitions; i++) {
2212 total++;
2213 p = ptes[i].part_table;
2214 if (!p->sys_ind) {
2215 if (i != 4 || i + 1 < g_partitions)
2216 printf("Warning: partition %d "
2217 "is empty\n", i + 1);
2218 } else if (first[i] < extended_offset || last[i] > e_last) {
2219 printf("Logical partition %d not entirely in "
2220 "partition %d\n", i + 1, ext_index + 1);
2225 if (total > g_heads * g_sectors * g_cylinders)
2226 printf("Total allocated sectors %d greater than the maximum "
2227 "%d\n", total, g_heads * g_sectors * g_cylinders);
2228 else {
2229 total = g_heads * g_sectors * g_cylinders - total;
2230 if (total != 0)
2231 printf("%d unallocated sectors\n", total);
2235 static void
2236 add_partition(int n, int sys)
2238 char mesg[256]; /* 48 does not suffice in Japanese */
2239 int i, num_read = 0;
2240 struct partition *p = ptes[n].part_table;
2241 struct partition *q = ptes[ext_index].part_table;
2242 ullong limit, temp;
2243 ullong start, stop = 0;
2244 ullong first[g_partitions], last[g_partitions];
2246 if (p && p->sys_ind) {
2247 printf(msg_part_already_defined, n + 1);
2248 return;
2250 fill_bounds(first, last);
2251 if (n < 4) {
2252 start = sector_offset;
2253 if (display_in_cyl_units || !total_number_of_sectors)
2254 limit = (ullong) g_heads * g_sectors * g_cylinders - 1;
2255 else
2256 limit = total_number_of_sectors - 1;
2257 if (extended_offset) {
2258 first[ext_index] = extended_offset;
2259 last[ext_index] = get_start_sect(q) +
2260 get_nr_sects(q) - 1;
2262 } else {
2263 start = extended_offset + sector_offset;
2264 limit = get_start_sect(q) + get_nr_sects(q) - 1;
2266 if (display_in_cyl_units)
2267 for (i = 0; i < g_partitions; i++)
2268 first[i] = (cround(first[i]) - 1) * units_per_sector;
2270 snprintf(mesg, sizeof(mesg), "First %s", str_units(SINGULAR));
2271 do {
2272 temp = start;
2273 for (i = 0; i < g_partitions; i++) {
2274 int lastplusoff;
2276 if (start == ptes[i].offset)
2277 start += sector_offset;
2278 lastplusoff = last[i] + ((n < 4) ? 0 : sector_offset);
2279 if (start >= first[i] && start <= lastplusoff)
2280 start = lastplusoff + 1;
2282 if (start > limit)
2283 break;
2284 if (start >= temp+units_per_sector && num_read) {
2285 printf("Sector %lld is already allocated\n", temp);
2286 temp = start;
2287 num_read = 0;
2289 if (!num_read && start == temp) {
2290 ullong saved_start;
2292 saved_start = start;
2293 start = read_int(cround(saved_start), cround(saved_start), cround(limit),
2294 0, mesg);
2295 if (display_in_cyl_units) {
2296 start = (start - 1) * units_per_sector;
2297 if (start < saved_start) start = saved_start;
2299 num_read = 1;
2301 } while (start != temp || !num_read);
2302 if (n > 4) { /* NOT for fifth partition */
2303 struct pte *pe = &ptes[n];
2305 pe->offset = start - sector_offset;
2306 if (pe->offset == extended_offset) { /* must be corrected */
2307 pe->offset++;
2308 if (sector_offset == 1)
2309 start++;
2313 for (i = 0; i < g_partitions; i++) {
2314 struct pte *pe = &ptes[i];
2316 if (start < pe->offset && limit >= pe->offset)
2317 limit = pe->offset - 1;
2318 if (start < first[i] && limit >= first[i])
2319 limit = first[i] - 1;
2321 if (start > limit) {
2322 printf("No free sectors available\n");
2323 if (n > 4)
2324 g_partitions--;
2325 return;
2327 if (cround(start) == cround(limit)) {
2328 stop = limit;
2329 } else {
2330 snprintf(mesg, sizeof(mesg),
2331 "Last %s or +size or +sizeM or +sizeK",
2332 str_units(SINGULAR));
2333 stop = read_int(cround(start), cround(limit), cround(limit),
2334 cround(start), mesg);
2335 if (display_in_cyl_units) {
2336 stop = stop * units_per_sector - 1;
2337 if (stop >limit)
2338 stop = limit;
2342 set_partition(n, 0, start, stop, sys);
2343 if (n > 4)
2344 set_partition(n - 1, 1, ptes[n].offset, stop, EXTENDED);
2346 if (IS_EXTENDED(sys)) {
2347 struct pte *pe4 = &ptes[4];
2348 struct pte *pen = &ptes[n];
2350 ext_index = n;
2351 pen->ext_pointer = p;
2352 pe4->offset = extended_offset = start;
2353 pe4->sectorbuffer = xzalloc(sector_size);
2354 pe4->part_table = pt_offset(pe4->sectorbuffer, 0);
2355 pe4->ext_pointer = pe4->part_table + 1;
2356 pe4->changed = 1;
2357 g_partitions = 5;
2361 static void
2362 add_logical(void)
2364 if (g_partitions > 5 || ptes[4].part_table->sys_ind) {
2365 struct pte *pe = &ptes[g_partitions];
2367 pe->sectorbuffer = xzalloc(sector_size);
2368 pe->part_table = pt_offset(pe->sectorbuffer, 0);
2369 pe->ext_pointer = pe->part_table + 1;
2370 pe->offset = 0;
2371 pe->changed = 1;
2372 g_partitions++;
2374 add_partition(g_partitions - 1, LINUX_NATIVE);
2377 static void
2378 new_partition(void)
2380 int i, free_primary = 0;
2382 if (warn_geometry())
2383 return;
2385 if (LABEL_IS_SUN) {
2386 add_sun_partition(get_partition(0, g_partitions), LINUX_NATIVE);
2387 return;
2389 if (LABEL_IS_SGI) {
2390 sgi_add_partition(get_partition(0, g_partitions), LINUX_NATIVE);
2391 return;
2393 if (LABEL_IS_AIX) {
2394 printf("Sorry - this fdisk cannot handle AIX disk labels.\n"
2395 "If you want to add DOS-type partitions, create a new empty DOS partition\n"
2396 "table first (use 'o'). This will destroy the present disk contents.\n");
2397 return;
2400 for (i = 0; i < 4; i++)
2401 free_primary += !ptes[i].part_table->sys_ind;
2403 if (!free_primary && g_partitions >= MAXIMUM_PARTS) {
2404 printf("The maximum number of partitions has been created\n");
2405 return;
2408 if (!free_primary) {
2409 if (extended_offset)
2410 add_logical();
2411 else
2412 printf("You must delete some partition and add "
2413 "an extended partition first\n");
2414 } else {
2415 char c, line[80];
2416 snprintf(line, sizeof(line),
2417 "Command action\n"
2418 " %s\n"
2419 " p primary partition (1-4)\n",
2420 (extended_offset ?
2421 "l logical (5 or over)" : "e extended"));
2422 while (1) {
2423 c = read_nonempty(line);
2424 if (c == 'p' || c == 'P') {
2425 i = get_nonexisting_partition(0, 4);
2426 if (i >= 0)
2427 add_partition(i, LINUX_NATIVE);
2428 return;
2430 if (c == 'l' && extended_offset) {
2431 add_logical();
2432 return;
2434 if (c == 'e' && !extended_offset) {
2435 i = get_nonexisting_partition(0, 4);
2436 if (i >= 0)
2437 add_partition(i, EXTENDED);
2438 return;
2440 printf("Invalid partition number "
2441 "for type '%c'\n", c);
2446 static void
2447 write_table(void)
2449 int i;
2451 if (LABEL_IS_DOS) {
2452 for (i = 0; i < 3; i++)
2453 if (ptes[i].changed)
2454 ptes[3].changed = 1;
2455 for (i = 3; i < g_partitions; i++) {
2456 struct pte *pe = &ptes[i];
2458 if (pe->changed) {
2459 write_part_table_flag(pe->sectorbuffer);
2460 write_sector(pe->offset, pe->sectorbuffer);
2464 else if (LABEL_IS_SGI) {
2465 /* no test on change? the printf below might be mistaken */
2466 sgi_write_table();
2468 else if (LABEL_IS_SUN) {
2469 int needw = 0;
2471 for (i = 0; i < 8; i++)
2472 if (ptes[i].changed)
2473 needw = 1;
2474 if (needw)
2475 sun_write_table();
2478 printf("The partition table has been altered!\n\n");
2479 reread_partition_table(1);
2482 static void
2483 reread_partition_table(int leave)
2485 int i;
2487 printf("Calling ioctl() to re-read partition table\n");
2488 sync();
2489 /* sleep(2); Huh? */
2490 i = ioctl_or_perror(dev_fd, BLKRRPART, NULL,
2491 "WARNING: rereading partition table "
2492 "failed, kernel still uses old table");
2493 #if 0
2494 if (dos_changed)
2495 printf(
2496 "\nWARNING: If you have created or modified any DOS 6.x\n"
2497 "partitions, please see the fdisk manual page for additional\n"
2498 "information\n");
2499 #endif
2501 if (leave) {
2502 if (ENABLE_FEATURE_CLEAN_UP)
2503 close_dev_fd();
2504 exit(i != 0);
2507 #endif /* FEATURE_FDISK_WRITABLE */
2509 #if ENABLE_FEATURE_FDISK_ADVANCED
2510 #define MAX_PER_LINE 16
2511 static void
2512 print_buffer(char *pbuffer)
2514 int i,l;
2516 for (i = 0, l = 0; i < sector_size; i++, l++) {
2517 if (l == 0)
2518 printf("0x%03X:", i);
2519 printf(" %02X", (unsigned char) pbuffer[i]);
2520 if (l == MAX_PER_LINE - 1) {
2521 bb_putchar('\n');
2522 l = -1;
2525 if (l > 0)
2526 bb_putchar('\n');
2527 bb_putchar('\n');
2530 static void
2531 print_raw(void)
2533 int i;
2535 printf("Device: %s\n", disk_device);
2536 if (LABEL_IS_SGI || LABEL_IS_SUN)
2537 print_buffer(MBRbuffer);
2538 else {
2539 for (i = 3; i < g_partitions; i++)
2540 print_buffer(ptes[i].sectorbuffer);
2544 static void
2545 move_begin(int i)
2547 struct pte *pe = &ptes[i];
2548 struct partition *p = pe->part_table;
2549 ullong new, first;
2551 if (warn_geometry())
2552 return;
2553 if (!p->sys_ind || !get_nr_sects(p) || IS_EXTENDED(p->sys_ind)) {
2554 printf("Partition %d has no data area\n", i + 1);
2555 return;
2557 first = get_partition_start(pe);
2558 new = read_int(first, first, first + get_nr_sects(p) - 1, first,
2559 "New beginning of data") - pe->offset;
2561 if (new != get_nr_sects(p)) {
2562 first = get_nr_sects(p) + get_start_sect(p) - new;
2563 set_nr_sects(p, first);
2564 set_start_sect(p, new);
2565 pe->changed = 1;
2569 static void
2570 xselect(void)
2572 char c;
2574 while (1) {
2575 bb_putchar('\n');
2576 c = tolower(read_nonempty("Expert command (m for help): "));
2577 switch (c) {
2578 case 'a':
2579 if (LABEL_IS_SUN)
2580 sun_set_alt_cyl();
2581 break;
2582 case 'b':
2583 if (LABEL_IS_DOS)
2584 move_begin(get_partition(0, g_partitions));
2585 break;
2586 case 'c':
2587 user_cylinders = g_cylinders =
2588 read_int(1, g_cylinders, 1048576, 0,
2589 "Number of cylinders");
2590 if (LABEL_IS_SUN)
2591 sun_set_ncyl(g_cylinders);
2592 if (LABEL_IS_DOS)
2593 warn_cylinders();
2594 break;
2595 case 'd':
2596 print_raw();
2597 break;
2598 case 'e':
2599 if (LABEL_IS_SGI)
2600 sgi_set_xcyl();
2601 else if (LABEL_IS_SUN)
2602 sun_set_xcyl();
2603 else if (LABEL_IS_DOS)
2604 x_list_table(1);
2605 break;
2606 case 'f':
2607 if (LABEL_IS_DOS)
2608 fix_partition_table_order();
2609 break;
2610 case 'g':
2611 #if ENABLE_FEATURE_SGI_LABEL
2612 create_sgilabel();
2613 #endif
2614 break;
2615 case 'h':
2616 user_heads = g_heads = read_int(1, g_heads, 256, 0,
2617 "Number of heads");
2618 update_units();
2619 break;
2620 case 'i':
2621 if (LABEL_IS_SUN)
2622 sun_set_ilfact();
2623 break;
2624 case 'o':
2625 if (LABEL_IS_SUN)
2626 sun_set_rspeed();
2627 break;
2628 case 'p':
2629 if (LABEL_IS_SUN)
2630 list_table(1);
2631 else
2632 x_list_table(0);
2633 break;
2634 case 'q':
2635 if (ENABLE_FEATURE_CLEAN_UP)
2636 close_dev_fd();
2637 bb_putchar('\n');
2638 exit(EXIT_SUCCESS);
2639 case 'r':
2640 return;
2641 case 's':
2642 user_sectors = g_sectors = read_int(1, g_sectors, 63, 0,
2643 "Number of sectors");
2644 if (dos_compatible_flag) {
2645 sector_offset = g_sectors;
2646 printf("Warning: setting sector offset for DOS "
2647 "compatiblity\n");
2649 update_units();
2650 break;
2651 case 'v':
2652 verify();
2653 break;
2654 case 'w':
2655 write_table(); /* does not return */
2656 break;
2657 case 'y':
2658 if (LABEL_IS_SUN)
2659 sun_set_pcylcount();
2660 break;
2661 default:
2662 xmenu();
2666 #endif /* ADVANCED mode */
2668 static int
2669 is_ide_cdrom_or_tape(const char *device)
2671 FILE *procf;
2672 char buf[100];
2673 struct stat statbuf;
2674 int is_ide = 0;
2676 /* No device was given explicitly, and we are trying some
2677 likely things. But opening /dev/hdc may produce errors like
2678 "hdc: tray open or drive not ready"
2679 if it happens to be a CD-ROM drive. It even happens that
2680 the process hangs on the attempt to read a music CD.
2681 So try to be careful. This only works since 2.1.73. */
2683 if (strncmp("/dev/hd", device, 7))
2684 return 0;
2686 snprintf(buf, sizeof(buf), "/proc/ide/%s/media", device+5);
2687 procf = fopen_for_read(buf);
2688 if (procf != NULL && fgets(buf, sizeof(buf), procf))
2689 is_ide = (!strncmp(buf, "cdrom", 5) ||
2690 !strncmp(buf, "tape", 4));
2691 else
2692 /* Now when this proc file does not exist, skip the
2693 device when it is read-only. */
2694 if (stat(device, &statbuf) == 0)
2695 is_ide = ((statbuf.st_mode & 0222) == 0);
2697 if (procf)
2698 fclose(procf);
2699 return is_ide;
2703 static void
2704 open_list_and_close(const char *device, int user_specified)
2706 int gb;
2708 disk_device = device;
2709 if (setjmp(listingbuf))
2710 return;
2711 if (!user_specified)
2712 if (is_ide_cdrom_or_tape(device))
2713 return;
2715 /* Open disk_device, save file descriptor to dev_fd */
2716 errno = 0;
2717 gb = get_boot(TRY_ONLY);
2718 if (gb > 0) { /* I/O error */
2719 /* Ignore other errors, since we try IDE
2720 and SCSI hard disks which may not be
2721 installed on the system. */
2722 if (user_specified || errno == EACCES)
2723 bb_perror_msg("can't open '%s'", device);
2724 return;
2727 if (gb < 0) { /* no DOS signature */
2728 list_disk_geometry();
2729 if (LABEL_IS_AIX)
2730 goto ret;
2731 #if ENABLE_FEATURE_OSF_LABEL
2732 if (bsd_trydev(device) < 0)
2733 #endif
2734 printf("Disk %s doesn't contain a valid "
2735 "partition table\n", device);
2736 } else {
2737 list_table(0);
2738 #if ENABLE_FEATURE_FDISK_WRITABLE
2739 if (!LABEL_IS_SUN && g_partitions > 4) {
2740 delete_partition(ext_index);
2742 #endif
2744 ret:
2745 close_dev_fd();
2748 /* for fdisk -l: try all things in /proc/partitions
2749 that look like a partition name (do not end in a digit) */
2750 static void
2751 list_devs_in_proc_partititons(void)
2753 FILE *procpt;
2754 char line[100], ptname[100], devname[120], *s;
2755 int ma, mi, sz;
2757 procpt = fopen_or_warn("/proc/partitions", "r");
2759 while (fgets(line, sizeof(line), procpt)) {
2760 if (sscanf(line, " %d %d %d %[^\n ]",
2761 &ma, &mi, &sz, ptname) != 4)
2762 continue;
2763 for (s = ptname; *s; s++)
2764 continue;
2765 if (isdigit(s[-1]))
2766 continue;
2767 sprintf(devname, "/dev/%s", ptname);
2768 open_list_and_close(devname, 0);
2770 #if ENABLE_FEATURE_CLEAN_UP
2771 fclose(procpt);
2772 #endif
2775 #if ENABLE_FEATURE_FDISK_WRITABLE
2776 static void
2777 unknown_command(int c)
2779 printf("%c: unknown command\n", c);
2781 #endif
2783 int fdisk_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
2784 int fdisk_main(int argc, char **argv)
2786 unsigned opt;
2788 * fdisk -v
2789 * fdisk -l [-b sectorsize] [-u] device ...
2790 * fdisk -s [partition] ...
2791 * fdisk [-b sectorsize] [-u] device
2793 * Options -C, -H, -S set the geometry.
2795 INIT_G();
2797 close_dev_fd(); /* needed: fd 3 must not stay closed */
2799 opt_complementary = "b+:C+:H+:S+"; /* numeric params */
2800 opt = getopt32(argv, "b:C:H:lS:u" USE_FEATURE_FDISK_BLKSIZE("s"),
2801 &sector_size, &user_cylinders, &user_heads, &user_sectors);
2802 argc -= optind;
2803 argv += optind;
2804 if (opt & OPT_b) { // -b
2805 /* Ugly: this sector size is really per device,
2806 so cannot be combined with multiple disks,
2807 and the same goes for the C/H/S options.
2809 if (sector_size != 512 && sector_size != 1024
2810 && sector_size != 2048)
2811 bb_show_usage();
2812 sector_offset = 2;
2813 user_set_sector_size = 1;
2815 if (user_heads <= 0 || user_heads >= 256)
2816 user_heads = 0;
2817 if (user_sectors <= 0 || user_sectors >= 64)
2818 user_sectors = 0;
2819 if (opt & OPT_u)
2820 display_in_cyl_units = 0; // -u
2822 #if ENABLE_FEATURE_FDISK_WRITABLE
2823 if (opt & OPT_l) {
2824 nowarn = 1;
2825 #endif
2826 if (*argv) {
2827 listing = 1;
2828 do {
2829 open_list_and_close(*argv, 1);
2830 } while (*++argv);
2831 } else {
2832 /* we don't have device names, */
2833 /* use /proc/partitions instead */
2834 list_devs_in_proc_partititons();
2836 return 0;
2837 #if ENABLE_FEATURE_FDISK_WRITABLE
2839 #endif
2841 #if ENABLE_FEATURE_FDISK_BLKSIZE
2842 if (opt & OPT_s) {
2843 int j;
2845 nowarn = 1;
2846 if (argc <= 0)
2847 bb_show_usage();
2848 for (j = 0; j < argc; j++) {
2849 unsigned long long size;
2850 fd = xopen(argv[j], O_RDONLY);
2851 size = bb_BLKGETSIZE_sectors(fd) / 2;
2852 close(fd);
2853 if (argc == 1)
2854 printf("%lld\n", size);
2855 else
2856 printf("%s: %lld\n", argv[j], size);
2858 return 0;
2860 #endif
2862 #if ENABLE_FEATURE_FDISK_WRITABLE
2863 if (argc != 1)
2864 bb_show_usage();
2866 disk_device = argv[0];
2867 get_boot(OPEN_MAIN);
2869 if (LABEL_IS_OSF) {
2870 /* OSF label, and no DOS label */
2871 printf("Detected an OSF/1 disklabel on %s, entering "
2872 "disklabel mode\n", disk_device);
2873 bsd_select();
2874 /*Why do we do this? It seems to be counter-intuitive*/
2875 current_label_type = LABEL_DOS;
2876 /* If we return we may want to make an empty DOS label? */
2879 while (1) {
2880 int c;
2881 bb_putchar('\n');
2882 c = tolower(read_nonempty("Command (m for help): "));
2883 switch (c) {
2884 case 'a':
2885 if (LABEL_IS_DOS)
2886 toggle_active(get_partition(1, g_partitions));
2887 else if (LABEL_IS_SUN)
2888 toggle_sunflags(get_partition(1, g_partitions),
2889 0x01);
2890 else if (LABEL_IS_SGI)
2891 sgi_set_bootpartition(
2892 get_partition(1, g_partitions));
2893 else
2894 unknown_command(c);
2895 break;
2896 case 'b':
2897 if (LABEL_IS_SGI) {
2898 printf("\nThe current boot file is: %s\n",
2899 sgi_get_bootfile());
2900 if (read_maybe_empty("Please enter the name of the "
2901 "new boot file: ") == '\n')
2902 printf("Boot file unchanged\n");
2903 else
2904 sgi_set_bootfile(line_ptr);
2906 #if ENABLE_FEATURE_OSF_LABEL
2907 else
2908 bsd_select();
2909 #endif
2910 break;
2911 case 'c':
2912 if (LABEL_IS_DOS)
2913 toggle_dos_compatibility_flag();
2914 else if (LABEL_IS_SUN)
2915 toggle_sunflags(get_partition(1, g_partitions),
2916 0x10);
2917 else if (LABEL_IS_SGI)
2918 sgi_set_swappartition(
2919 get_partition(1, g_partitions));
2920 else
2921 unknown_command(c);
2922 break;
2923 case 'd':
2925 int j;
2926 /* If sgi_label then don't use get_existing_partition,
2927 let the user select a partition, since
2928 get_existing_partition() only works for Linux-like
2929 partition tables */
2930 if (!LABEL_IS_SGI) {
2931 j = get_existing_partition(1, g_partitions);
2932 } else {
2933 j = get_partition(1, g_partitions);
2935 if (j >= 0)
2936 delete_partition(j);
2938 break;
2939 case 'i':
2940 if (LABEL_IS_SGI)
2941 create_sgiinfo();
2942 else
2943 unknown_command(c);
2944 case 'l':
2945 list_types(get_sys_types());
2946 break;
2947 case 'm':
2948 menu();
2949 break;
2950 case 'n':
2951 new_partition();
2952 break;
2953 case 'o':
2954 create_doslabel();
2955 break;
2956 case 'p':
2957 list_table(0);
2958 break;
2959 case 'q':
2960 if (ENABLE_FEATURE_CLEAN_UP)
2961 close_dev_fd();
2962 bb_putchar('\n');
2963 return 0;
2964 case 's':
2965 #if ENABLE_FEATURE_SUN_LABEL
2966 create_sunlabel();
2967 #endif
2968 break;
2969 case 't':
2970 change_sysid();
2971 break;
2972 case 'u':
2973 change_units();
2974 break;
2975 case 'v':
2976 verify();
2977 break;
2978 case 'w':
2979 write_table(); /* does not return */
2980 break;
2981 #if ENABLE_FEATURE_FDISK_ADVANCED
2982 case 'x':
2983 if (LABEL_IS_SGI) {
2984 printf("\n\tSorry, no experts menu for SGI "
2985 "partition tables available\n\n");
2986 } else
2987 xselect();
2988 break;
2989 #endif
2990 default:
2991 unknown_command(c);
2992 menu();
2995 return 0;
2996 #endif /* FEATURE_FDISK_WRITABLE */