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