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