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