Bump to version 1.22.1
[platform/upstream/busybox.git] / util-linux / fdisk.c
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
76 enum {
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
87 typedef unsigned long long ullong;
88 /* Used for sector numbers. Partition formats we know
89  * do not support more than 2^32 sectors
90  */
91 typedef 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
100 struct 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
109 static 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
114 static const char msg_part_already_defined[] ALIGN1 =
115 "Partition %u is already defined, delete it before re-adding\n";
116
117
118 struct 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  */
139 struct 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
153 enum 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
199 enum action { OPEN_MAIN, TRY_ONLY, CREATE_EMPTY_DOS, CREATE_EMPTY_SUN };
200
201 static void update_units(void);
202 #if ENABLE_FEATURE_FDISK_WRITABLE
203 static void change_units(void);
204 static void reread_partition_table(int leave);
205 static void delete_partition(int i);
206 static unsigned get_partition(int warn, unsigned max);
207 static void list_types(const char *const *sys);
208 static sector_t read_int(sector_t low, sector_t dflt, sector_t high, sector_t base, const char *mesg);
209 #endif
210 static const char *partition_type(unsigned char type);
211 static void get_geometry(void);
212 static void read_pte(struct pte *pe, sector_t offset);
213 #if ENABLE_FEATURE_SUN_LABEL || ENABLE_FEATURE_FDISK_WRITABLE
214 static int get_boot(enum action what);
215 #else
216 static int get_boot(void);
217 #endif
218
219 #define PLURAL   0
220 #define SINGULAR 1
221
222 static sector_t get_start_sect(const struct partition *p);
223 static sector_t get_nr_sects(const struct partition *p);
224
225 /* DOS partition types */
226
227 static 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
330 enum {
331         dev_fd = 3                  /* the disk */
332 };
333
334 /* Globals */
335 struct 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  */
420 static 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
474 static void
475 close_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  */
484 static const char *
485 partname(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
519 static ALWAYS_INLINE struct partition *
520 get_part_table(int i)
521 {
522         return ptes[i].part_table;
523 }
524
525 static const char *
526 str_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
533 static int
534 valid_part_table_flag(const char *mbuffer)
535 {
536         return (mbuffer[510] == 0x55 && (uint8_t)mbuffer[511] == 0xaa);
537 }
538
539 static 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
548 static void
549 seek_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 */
567 static int
568 read_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
585 static void
586 set_all_unchanged(void)
587 {
588         int i;
589
590         for (i = 0; i < MAXIMUM_PARTS; i++)
591                 ptes[i].changed = 0;
592 }
593
594 static ALWAYS_INLINE void
595 set_changed(int i)
596 {
597         ptes[i].changed = 1;
598 }
599
600 static ALWAYS_INLINE void
601 write_part_table_flag(char *b)
602 {
603         b[510] = 0x55;
604         b[511] = 0xaa;
605 }
606
607 static char
608 read_nonempty(const char *mesg)
609 {
610         while (!read_line(mesg))
611                 continue;
612         return *line_ptr;
613 }
614
615 static char
616 read_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
626 static int
627 read_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
642 static void
643 write_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
653 struct 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;
680 typedef struct sun_partition sun_partition;
681 #define sunlabel ((sun_partition *)MBRbuffer)
682 STATIC_OSF void bsd_select(void);
683 STATIC_OSF void xbsd_print_disklabel(int);
684 #include "fdisk_osf.c"
685
686 STATIC_GPT void gpt_list_table(int xtra);
687 #include "fdisk_gpt.c"
688
689 #if ENABLE_FEATURE_SGI_LABEL || ENABLE_FEATURE_SUN_LABEL
690 static uint16_t
691 fdisk_swap16(uint16_t x)
692 {
693         return (x << 8) | (x >> 8);
694 }
695
696 static uint32_t
697 fdisk_swap32(uint32_t x)
698 {
699         return (x << 24) |
700                ((x & 0xFF00) << 8) |
701                ((x & 0xFF0000) >> 8) |
702                (x >> 24);
703 }
704 #endif
705
706 STATIC_SGI const char *const sgi_sys_types[];
707 STATIC_SGI unsigned sgi_get_num_sectors(int i);
708 STATIC_SGI int sgi_get_sysid(int i);
709 STATIC_SGI void sgi_delete_partition(int i);
710 STATIC_SGI void sgi_change_sysid(int i, int sys);
711 STATIC_SGI void sgi_list_table(int xtra);
712 #if ENABLE_FEATURE_FDISK_ADVANCED
713 STATIC_SGI void sgi_set_xcyl(void);
714 #endif
715 STATIC_SGI int verify_sgi(int verbose);
716 STATIC_SGI void sgi_add_partition(int n, int sys);
717 STATIC_SGI void sgi_set_swappartition(int i);
718 STATIC_SGI const char *sgi_get_bootfile(void);
719 STATIC_SGI void sgi_set_bootfile(const char* aFile);
720 STATIC_SGI void create_sgiinfo(void);
721 STATIC_SGI void sgi_write_table(void);
722 STATIC_SGI void sgi_set_bootpartition(int i);
723 #include "fdisk_sgi.c"
724
725 STATIC_SUN const char *const sun_sys_types[];
726 STATIC_SUN void sun_delete_partition(int i);
727 STATIC_SUN void sun_change_sysid(int i, int sys);
728 STATIC_SUN void sun_list_table(int xtra);
729 STATIC_SUN void add_sun_partition(int n, int sys);
730 #if ENABLE_FEATURE_FDISK_ADVANCED
731 STATIC_SUN void sun_set_alt_cyl(void);
732 STATIC_SUN void sun_set_ncyl(int cyl);
733 STATIC_SUN void sun_set_xcyl(void);
734 STATIC_SUN void sun_set_ilfact(void);
735 STATIC_SUN void sun_set_rspeed(void);
736 STATIC_SUN void sun_set_pcylcount(void);
737 #endif
738 STATIC_SUN void toggle_sunflags(int i, unsigned char mask);
739 STATIC_SUN void verify_sun(void);
740 STATIC_SUN void sun_write_table(void);
741 #include "fdisk_sun.c"
742
743
744 static inline_if_little_endian unsigned
745 read4_little_endian(const unsigned char *cp)
746 {
747         uint32_t v;
748         move_from_unaligned32(v, cp);
749         return SWAP_LE32(v);
750 }
751
752 static sector_t
753 get_start_sect(const struct partition *p)
754 {
755         return read4_little_endian(p->start4);
756 }
757
758 static sector_t
759 get_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 */
767 static inline_if_little_endian void
768 store4_little_endian(unsigned char *cp, unsigned val)
769 {
770         uint32_t v = SWAP_LE32(val);
771         move_to_unaligned32(cp, v);
772 }
773
774 static void
775 set_start_sect(struct partition *p, unsigned start_sect)
776 {
777         store4_little_endian(p->start4, start_sect);
778 }
779
780 static void
781 set_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 */
788 static void
789 read_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
803 static sector_t
804 get_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
817 static int
818 is_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
828 static void
829 menu(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
898 static void
899 xmenu(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
964 static const char *const *
965 get_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
976 static const char *
977 partition_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
989 static int
990 is_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
1001 static void
1002 clear_partition(struct partition *p)
1003 {
1004         if (p)
1005                 memset(p, 0, sizeof(*p));
1006 }
1007
1008 #if ENABLE_FEATURE_FDISK_WRITABLE
1009 static int
1010 get_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
1017 static void
1018 list_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
1059 static 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
1070 static void
1071 set_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
1092 static int
1093 warn_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
1113 static void
1114 update_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
1125 static void
1126 warn_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
1140 static void
1141 read_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
1234 static void
1235 create_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
1254 static void
1255 get_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
1268 static void
1269 get_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
1280 static void
1281 get_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
1313 static void
1314 get_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
1367 static int get_boot(enum action what)
1368 #else
1369 static 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  */
1517 static sector_t
1518 read_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
1604 static unsigned
1605 get_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
1624 static int
1625 get_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
1651 static int
1652 get_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
1679 static void
1680 change_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
1688 static void
1689 toggle_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
1700 static void
1701 toggle_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
1713 static void
1714 delete_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
1787 static void
1788 change_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
1878 static void
1879 linear2chs(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
1889 static void
1890 check_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
1939 static void
1940 list_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  */
1966 static int
1967 wrong_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  */
2011 static void
2012 fix_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
2063 static void
2064 fix_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
2102 static void
2103 list_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
2186 static void
2187 x_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
2216 static void
2217 fill_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
2235 static void
2236 check(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
2259 static void
2260 verify(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
2335 static void
2336 add_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
2460 static void
2461 add_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
2476 static void
2477 new_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
2545 static void
2546 reread_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
2574 static void
2575 write_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
2611 static void
2612 print_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
2630 static void
2631 print_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
2644 static void
2645 move_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
2672 static void
2673 xselect(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
2769 static int
2770 is_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
2804 static void
2805 open_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. */
2851 static 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) */
2875 static void
2876 list_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
2899 static void
2900 unknown_command(int c)
2901 {
2902         printf("%c: unknown command\n", c);
2903 }
2904 #endif
2905
2906 int fdisk_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
2907 int 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 }