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