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