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