source: branches/3.2/mindi-busybox/util-linux/fdisk.c @ 3232

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