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