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