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