1#if ENABLE_FEATURE_SUN_LABEL
2
3#define SUNOS_SWAP 3
4#define SUN_WHOLE_DISK 5
5
6#define SUN_LABEL_MAGIC          0xDABE
7#define SUN_LABEL_MAGIC_SWAPPED  0xBEDA
8#define SUN_SSWAP16(x) (sun_other_endian ? fdisk_swap16(x) : (uint16_t)(x))
9#define SUN_SSWAP32(x) (sun_other_endian ? fdisk_swap32(x) : (uint32_t)(x))
10
11/* Copied from linux/major.h */
12#define FLOPPY_MAJOR    2
13
14#define SCSI_IOCTL_GET_IDLUN 0x5382
15
16/*
17 * fdisksunlabel.c
18 *
19 * I think this is mostly, or entirely, due to
20 *      Jakub Jelinek (jj@sunsite.mff.cuni.cz), July 1996
21 *
22 * Merged with fdisk for other architectures, aeb, June 1998.
23 *
24 * Sat Mar 20 EST 1999 Arnaldo Carvalho de Melo <acme@conectiva.com.br>
25 *      Internationalization
26 */
27
28
29static int sun_other_endian;
30static int scsi_disk;
31static int floppy;
32
33#ifndef IDE0_MAJOR
34#define IDE0_MAJOR 3
35#endif
36#ifndef IDE1_MAJOR
37#define IDE1_MAJOR 22
38#endif
39
40static void
41guess_device_type(void)
42{
43	struct stat bootstat;
44
45	if (fstat(fd, &bootstat) < 0) {
46		scsi_disk = 0;
47		floppy = 0;
48	} else if (S_ISBLK(bootstat.st_mode)
49		&& (major(bootstat.st_rdev) == IDE0_MAJOR ||
50		    major(bootstat.st_rdev) == IDE1_MAJOR)) {
51		scsi_disk = 0;
52		floppy = 0;
53	} else if (S_ISBLK(bootstat.st_mode)
54		&& major(bootstat.st_rdev) == FLOPPY_MAJOR) {
55		scsi_disk = 0;
56		floppy = 1;
57	} else {
58		scsi_disk = 1;
59		floppy = 0;
60	}
61}
62
63static const char *const sun_sys_types[] = {
64	"\x00" "Empty"       , /* 0            */
65	"\x01" "Boot"        , /* 1            */
66	"\x02" "SunOS root"  , /* 2            */
67	"\x03" "SunOS swap"  , /* SUNOS_SWAP   */
68	"\x04" "SunOS usr"   , /* 4            */
69	"\x05" "Whole disk"  , /* SUN_WHOLE_DISK   */
70	"\x06" "SunOS stand" , /* 6            */
71	"\x07" "SunOS var"   , /* 7            */
72	"\x08" "SunOS home"  , /* 8            */
73	"\x82" "Linux swap"  , /* LINUX_SWAP   */
74	"\x83" "Linux native", /* LINUX_NATIVE */
75	"\x8e" "Linux LVM"   , /* 0x8e         */
76/* New (2.2.x) raid partition with autodetect using persistent superblock */
77	"\xfd" "Linux raid autodetect", /* 0xfd         */
78	NULL
79};
80
81
82static void
83set_sun_partition(int i, uint start, uint stop, int sysid)
84{
85	sunlabel->infos[i].id = sysid;
86	sunlabel->partitions[i].start_cylinder =
87		SUN_SSWAP32(start / (heads * sectors));
88	sunlabel->partitions[i].num_sectors =
89		SUN_SSWAP32(stop - start);
90	set_changed(i);
91}
92
93static int
94check_sun_label(void)
95{
96	unsigned short *ush;
97	int csum;
98
99	if (sunlabel->magic != SUN_LABEL_MAGIC
100	 && sunlabel->magic != SUN_LABEL_MAGIC_SWAPPED) {
101		current_label_type = label_dos;
102		sun_other_endian = 0;
103		return 0;
104	}
105	sun_other_endian = (sunlabel->magic == SUN_LABEL_MAGIC_SWAPPED);
106	ush = ((unsigned short *) (sunlabel + 1)) - 1;
107	for (csum = 0; ush >= (unsigned short *)sunlabel;) csum ^= *ush--;
108	if (csum) {
109		printf("Detected sun disklabel with wrong checksum.\n"
110"Probably you'll have to set all the values,\n"
111"e.g. heads, sectors, cylinders and partitions\n"
112"or force a fresh label (s command in main menu)\n");
113	} else {
114		heads = SUN_SSWAP16(sunlabel->ntrks);
115		cylinders = SUN_SSWAP16(sunlabel->ncyl);
116		sectors = SUN_SSWAP16(sunlabel->nsect);
117	}
118	update_units();
119	current_label_type = label_sun;
120	partitions = 8;
121	return 1;
122}
123
124static const struct sun_predefined_drives {
125	const char *vendor;
126	const char *model;
127	unsigned short sparecyl;
128	unsigned short ncyl;
129	unsigned short nacyl;
130	unsigned short pcylcount;
131	unsigned short ntrks;
132	unsigned short nsect;
133	unsigned short rspeed;
134} sun_drives[] = {
135	{ "Quantum","ProDrive 80S",1,832,2,834,6,34,3662},
136	{ "Quantum","ProDrive 105S",1,974,2,1019,6,35,3662},
137	{ "CDC","Wren IV 94171-344",3,1545,2,1549,9,46,3600},
138	{ "IBM","DPES-31080",0,4901,2,4903,4,108,5400},
139	{ "IBM","DORS-32160",0,1015,2,1017,67,62,5400},
140	{ "IBM","DNES-318350",0,11199,2,11474,10,320,7200},
141	{ "SEAGATE","ST34371",0,3880,2,3882,16,135,7228},
142	{ "","SUN0104",1,974,2,1019,6,35,3662},
143	{ "","SUN0207",4,1254,2,1272,9,36,3600},
144	{ "","SUN0327",3,1545,2,1549,9,46,3600},
145	{ "","SUN0340",0,1538,2,1544,6,72,4200},
146	{ "","SUN0424",2,1151,2,2500,9,80,4400},
147	{ "","SUN0535",0,1866,2,2500,7,80,5400},
148	{ "","SUN0669",5,1614,2,1632,15,54,3600},
149	{ "","SUN1.0G",5,1703,2,1931,15,80,3597},
150	{ "","SUN1.05",0,2036,2,2038,14,72,5400},
151	{ "","SUN1.3G",6,1965,2,3500,17,80,5400},
152	{ "","SUN2.1G",0,2733,2,3500,19,80,5400},
153	{ "IOMEGA","Jaz",0,1019,2,1021,64,32,5394},
154};
155
156static const struct sun_predefined_drives *
157sun_autoconfigure_scsi(void)
158{
159	const struct sun_predefined_drives *p = NULL;
160
161#ifdef SCSI_IOCTL_GET_IDLUN
162	unsigned int id[2];
163	char buffer[2048];
164	char buffer2[2048];
165	FILE *pfd;
166	char *vendor;
167	char *model;
168	char *q;
169	int i;
170
171	if (ioctl(fd, SCSI_IOCTL_GET_IDLUN, &id))
172		return NULL;
173
174	sprintf(buffer,
175		"Host: scsi%d Channel: %02d Id: %02d Lun: %02d\n",
176		/* This is very wrong (works only if you have one HBA),
177		   but I haven't found a way how to get hostno
178		   from the current kernel */
179		0,
180		(id[0]>>16) & 0xff,
181		id[0] & 0xff,
182		(id[0]>>8) & 0xff
183	);
184	pfd = fopen("/proc/scsi/scsi", "r");
185	if (!pfd) {
186		return NULL;
187	}
188	while (fgets(buffer2, 2048, pfd)) {
189		if (strcmp(buffer, buffer2))
190			continue;
191		if (!fgets(buffer2, 2048, pfd))
192			break;
193		q = strstr(buffer2, "Vendor: ");
194		if (!q)
195			break;
196		q += 8;
197		vendor = q;
198		q = strstr(q, " ");
199		*q++ = '\0';   /* truncate vendor name */
200		q = strstr(q, "Model: ");
201		if (!q)
202			break;
203		*q = '\0';
204		q += 7;
205		model = q;
206		q = strstr(q, " Rev: ");
207		if (!q)
208			break;
209		*q = '\0';
210		for (i = 0; i < ARRAY_SIZE(sun_drives); i++) {
211			if (*sun_drives[i].vendor && strcasecmp(sun_drives[i].vendor, vendor))
212				continue;
213			if (!strstr(model, sun_drives[i].model))
214				continue;
215			printf("Autoconfigure found a %s%s%s\n",
216					sun_drives[i].vendor,
217					(*sun_drives[i].vendor) ? " " : "",
218					sun_drives[i].model);
219			p = sun_drives + i;
220			break;
221		}
222		break;
223	}
224	fclose(pfd);
225#endif
226	return p;
227}
228
229static void
230create_sunlabel(void)
231{
232	struct hd_geometry geometry;
233	unsigned int ndiv;
234	int i;
235	unsigned char c;
236	const struct sun_predefined_drives *p = NULL;
237
238	printf(msg_building_new_label, "sun disklabel");
239
240	sun_other_endian = BB_LITTLE_ENDIAN;
241	memset(MBRbuffer, 0, sizeof(MBRbuffer));
242	sunlabel->magic = SUN_SSWAP16(SUN_LABEL_MAGIC);
243	if (!floppy) {
244		puts("Drive type\n"
245		 "   ?   auto configure\n"
246		 "   0   custom (with hardware detected defaults)");
247		for (i = 0; i < ARRAY_SIZE(sun_drives); i++) {
248			printf("   %c   %s%s%s\n",
249				i + 'a', sun_drives[i].vendor,
250				(*sun_drives[i].vendor) ? " " : "",
251				sun_drives[i].model);
252		}
253		while (1) {
254			c = read_nonempty("Select type (? for auto, 0 for custom): ");
255			if (c == '0') {
256				break;
257			}
258			if (c >= 'a' && c < 'a' + ARRAY_SIZE(sun_drives)) {
259				p = sun_drives + c - 'a';
260				break;
261			}
262			if (c >= 'A' && c < 'A' + ARRAY_SIZE(sun_drives)) {
263				p = sun_drives + c - 'A';
264				break;
265			}
266			if (c == '?' && scsi_disk) {
267				p = sun_autoconfigure_scsi();
268				if (p)
269					break;
270				printf("Autoconfigure failed\n");
271			}
272		}
273	}
274	if (!p || floppy) {
275		if (!ioctl(fd, HDIO_GETGEO, &geometry)) {
276			heads = geometry.heads;
277			sectors = geometry.sectors;
278			cylinders = geometry.cylinders;
279		} else {
280			heads = 0;
281			sectors = 0;
282			cylinders = 0;
283		}
284		if (floppy) {
285			sunlabel->nacyl = 0;
286			sunlabel->pcylcount = SUN_SSWAP16(cylinders);
287			sunlabel->rspeed = SUN_SSWAP16(300);
288			sunlabel->ilfact = SUN_SSWAP16(1);
289			sunlabel->sparecyl = 0;
290		} else {
291			heads = read_int(1, heads, 1024, 0, "Heads");
292			sectors = read_int(1, sectors, 1024, 0, "Sectors/track");
293		if (cylinders)
294			cylinders = read_int(1, cylinders-2, 65535, 0, "Cylinders");
295		else
296			cylinders = read_int(1, 0, 65535, 0, "Cylinders");
297			sunlabel->nacyl = SUN_SSWAP16(read_int(0, 2, 65535, 0, "Alternate cylinders"));
298			sunlabel->pcylcount = SUN_SSWAP16(read_int(0, cylinders+SUN_SSWAP16(sunlabel->nacyl), 65535, 0, "Physical cylinders"));
299			sunlabel->rspeed = SUN_SSWAP16(read_int(1, 5400, 100000, 0, "Rotation speed (rpm)"));
300			sunlabel->ilfact = SUN_SSWAP16(read_int(1, 1, 32, 0, "Interleave factor"));
301			sunlabel->sparecyl = SUN_SSWAP16(read_int(0, 0, sectors, 0, "Extra sectors per cylinder"));
302		}
303	} else {
304		sunlabel->sparecyl = SUN_SSWAP16(p->sparecyl);
305		sunlabel->ncyl = SUN_SSWAP16(p->ncyl);
306		sunlabel->nacyl = SUN_SSWAP16(p->nacyl);
307		sunlabel->pcylcount = SUN_SSWAP16(p->pcylcount);
308		sunlabel->ntrks = SUN_SSWAP16(p->ntrks);
309		sunlabel->nsect = SUN_SSWAP16(p->nsect);
310		sunlabel->rspeed = SUN_SSWAP16(p->rspeed);
311		sunlabel->ilfact = SUN_SSWAP16(1);
312		cylinders = p->ncyl;
313		heads = p->ntrks;
314		sectors = p->nsect;
315		puts("You may change all the disk params from the x menu");
316	}
317
318	snprintf((char *)(sunlabel->info), sizeof(sunlabel->info),
319		"%s%s%s cyl %d alt %d hd %d sec %d",
320		p ? p->vendor : "", (p && *p->vendor) ? " " : "",
321		p ? p->model : (floppy ? "3,5\" floppy" : "Linux custom"),
322		cylinders, SUN_SSWAP16(sunlabel->nacyl), heads, sectors);
323
324	sunlabel->ntrks = SUN_SSWAP16(heads);
325	sunlabel->nsect = SUN_SSWAP16(sectors);
326	sunlabel->ncyl = SUN_SSWAP16(cylinders);
327	if (floppy)
328		set_sun_partition(0, 0, cylinders * heads * sectors, LINUX_NATIVE);
329	else {
330		if (cylinders * heads * sectors >= 150 * 2048) {
331			ndiv = cylinders - (50 * 2048 / (heads * sectors)); /* 50M swap */
332		} else
333			ndiv = cylinders * 2 / 3;
334		set_sun_partition(0, 0, ndiv * heads * sectors, LINUX_NATIVE);
335		set_sun_partition(1, ndiv * heads * sectors, cylinders * heads * sectors, LINUX_SWAP);
336		sunlabel->infos[1].flags |= 0x01; /* Not mountable */
337	}
338	set_sun_partition(2, 0, cylinders * heads * sectors, SUN_WHOLE_DISK);
339	{
340		unsigned short *ush = (unsigned short *)sunlabel;
341		unsigned short csum = 0;
342		while (ush < (unsigned short *)(&sunlabel->csum))
343			csum ^= *ush++;
344		sunlabel->csum = csum;
345	}
346
347	set_all_unchanged();
348	set_changed(0);
349	get_boot(create_empty_sun);
350}
351
352static void
353toggle_sunflags(int i, unsigned char mask)
354{
355	if (sunlabel->infos[i].flags & mask)
356		sunlabel->infos[i].flags &= ~mask;
357	else
358		sunlabel->infos[i].flags |= mask;
359	set_changed(i);
360}
361
362static void
363fetch_sun(uint *starts, uint *lens, uint *start, uint *stop)
364{
365	int i, continuous = 1;
366
367	*start = 0;
368	*stop = cylinders * heads * sectors;
369	for (i = 0; i < partitions; i++) {
370		if (sunlabel->partitions[i].num_sectors
371		 && sunlabel->infos[i].id
372		 && sunlabel->infos[i].id != SUN_WHOLE_DISK) {
373			starts[i] = SUN_SSWAP32(sunlabel->partitions[i].start_cylinder) * heads * sectors;
374			lens[i] = SUN_SSWAP32(sunlabel->partitions[i].num_sectors);
375			if (continuous) {
376				if (starts[i] == *start)
377					*start += lens[i];
378				else if (starts[i] + lens[i] >= *stop)
379					*stop = starts[i];
380				else
381					continuous = 0;
382					/* There will be probably more gaps
383					  than one, so lets check afterwards */
384			}
385		} else {
386			starts[i] = 0;
387			lens[i] = 0;
388		}
389	}
390}
391
392static uint *verify_sun_starts;
393
394static int
395verify_sun_cmp(int *a, int *b)
396{
397	if (*a == -1) return 1;
398	if (*b == -1) return -1;
399	if (verify_sun_starts[*a] > verify_sun_starts[*b]) return 1;
400	return -1;
401}
402
403static void
404verify_sun(void)
405{
406	uint starts[8], lens[8], start, stop;
407	int i,j,k,starto,endo;
408	int array[8];
409
410	verify_sun_starts = starts;
411	fetch_sun(starts,lens,&start,&stop);
412	for (k = 0; k < 7; k++) {
413		for (i = 0; i < 8; i++) {
414			if (k && (lens[i] % (heads * sectors))) {
415				printf("Partition %d doesn't end on cylinder boundary\n", i+1);
416			}
417			if (lens[i]) {
418				for (j = 0; j < i; j++)
419					if (lens[j]) {
420						if (starts[j] == starts[i]+lens[i]) {
421							starts[j] = starts[i]; lens[j] += lens[i];
422							lens[i] = 0;
423						} else if (starts[i] == starts[j]+lens[j]){
424							lens[j] += lens[i];
425							lens[i] = 0;
426						} else if (!k) {
427							if (starts[i] < starts[j]+lens[j]
428							 && starts[j] < starts[i]+lens[i]) {
429								starto = starts[i];
430								if (starts[j] > starto)
431									starto = starts[j];
432								endo = starts[i]+lens[i];
433								if (starts[j]+lens[j] < endo)
434									endo = starts[j]+lens[j];
435								printf("Partition %d overlaps with others in "
436									"sectors %d-%d\n", i+1, starto, endo);
437							}
438						}
439					}
440			}
441		}
442	}
443	for (i = 0; i < 8; i++) {
444		if (lens[i])
445			array[i] = i;
446		else
447			array[i] = -1;
448	}
449	qsort(array, ARRAY_SIZE(array), sizeof(array[0]),
450		(int (*)(const void *,const void *)) verify_sun_cmp);
451	if (array[0] == -1) {
452		printf("No partitions defined\n");
453		return;
454	}
455	stop = cylinders * heads * sectors;
456	if (starts[array[0]])
457		printf("Unused gap - sectors 0-%d\n", starts[array[0]]);
458	for (i = 0; i < 7 && array[i+1] != -1; i++) {
459		printf("Unused gap - sectors %d-%d\n", starts[array[i]]+lens[array[i]], starts[array[i+1]]);
460	}
461	start = starts[array[i]] + lens[array[i]];
462	if (start < stop)
463		printf("Unused gap - sectors %d-%d\n", start, stop);
464}
465
466static void
467add_sun_partition(int n, int sys)
468{
469	uint start, stop, stop2;
470	uint starts[8], lens[8];
471	int whole_disk = 0;
472
473	char mesg[256];
474	int i, first, last;
475
476	if (sunlabel->partitions[n].num_sectors && sunlabel->infos[n].id) {
477		printf(msg_part_already_defined, n + 1);
478		return;
479	}
480
481	fetch_sun(starts,lens,&start,&stop);
482	if (stop <= start) {
483		if (n == 2)
484			whole_disk = 1;
485		else {
486			printf("Other partitions already cover the whole disk.\n"
487				"Delete/shrink them before retry.\n");
488			return;
489		}
490	}
491	snprintf(mesg, sizeof(mesg), "First %s", str_units(SINGULAR));
492	while (1) {
493		if (whole_disk)
494			first = read_int(0, 0, 0, 0, mesg);
495		else
496			first = read_int(scround(start), scround(stop)+1,
497					 scround(stop), 0, mesg);
498		if (display_in_cyl_units)
499			first *= units_per_sector;
500		else
501			/* Starting sector has to be properly aligned */
502			first = (first + heads * sectors - 1) / (heads * sectors);
503		if (n == 2 && first != 0)
504			printf("\
505It is highly recommended that the third partition covers the whole disk\n\
506and is of type 'Whole disk'\n");
507		/* ewt asks to add: "don't start a partition at cyl 0"
508		   However, edmundo@rano.demon.co.uk writes:
509		   "In addition to having a Sun partition table, to be able to
510		   boot from the disc, the first partition, /dev/sdX1, must
511		   start at cylinder 0. This means that /dev/sdX1 contains
512		   the partition table and the boot block, as these are the
513		   first two sectors of the disc. Therefore you must be
514		   careful what you use /dev/sdX1 for. In particular, you must
515		   not use a partition starting at cylinder 0 for Linux swap,
516		   as that would overwrite the partition table and the boot
517		   block. You may, however, use such a partition for a UFS
518		   or EXT2 file system, as these file systems leave the first
519		   1024 bytes undisturbed. */
520		/* On the other hand, one should not use partitions
521		   starting at block 0 in an md, or the label will
522		   be trashed. */
523		for (i = 0; i < partitions; i++)
524			if (lens[i] && starts[i] <= first && starts[i] + lens[i] > first)
525				break;
526		if (i < partitions && !whole_disk) {
527			if (n == 2 && !first) {
528				whole_disk = 1;
529				break;
530			}
531			printf("Sector %d is already allocated\n", first);
532		} else
533			break;
534	}
535	stop = cylinders * heads * sectors;
536	stop2 = stop;
537	for (i = 0; i < partitions; i++) {
538		if (starts[i] > first && starts[i] < stop)
539			stop = starts[i];
540	}
541	snprintf(mesg, sizeof(mesg),
542		"Last %s or +size or +sizeM or +sizeK",
543		str_units(SINGULAR));
544	if (whole_disk)
545		last = read_int(scround(stop2), scround(stop2), scround(stop2),
546				0, mesg);
547	else if (n == 2 && !first)
548		last = read_int(scround(first), scround(stop2), scround(stop2),
549				scround(first), mesg);
550	else
551		last = read_int(scround(first), scround(stop), scround(stop),
552				scround(first), mesg);
553	if (display_in_cyl_units)
554		last *= units_per_sector;
555	if (n == 2 && !first) {
556		if (last >= stop2) {
557			whole_disk = 1;
558			last = stop2;
559		} else if (last > stop) {
560			printf(
561"You haven't covered the whole disk with the 3rd partition,\n"
562"but your value %d %s covers some other partition.\n"
563"Your entry has been changed to %d %s\n",
564				scround(last), str_units(SINGULAR),
565				scround(stop), str_units(SINGULAR));
566			last = stop;
567		}
568	} else if (!whole_disk && last > stop)
569		last = stop;
570
571	if (whole_disk)
572		sys = SUN_WHOLE_DISK;
573	set_sun_partition(n, first, last, sys);
574}
575
576static void
577sun_delete_partition(int i)
578{
579	unsigned int nsec;
580
581	if (i == 2
582	 && sunlabel->infos[i].id == SUN_WHOLE_DISK
583	 && !sunlabel->partitions[i].start_cylinder
584	 && (nsec = SUN_SSWAP32(sunlabel->partitions[i].num_sectors)) == heads * sectors * cylinders)
585		printf("If you want to maintain SunOS/Solaris compatibility, "
586			"consider leaving this\n"
587			"partition as Whole disk (5), starting at 0, with %u "
588			"sectors\n", nsec);
589	sunlabel->infos[i].id = 0;
590	sunlabel->partitions[i].num_sectors = 0;
591}
592
593static void
594sun_change_sysid(int i, int sys)
595{
596	if (sys == LINUX_SWAP && !sunlabel->partitions[i].start_cylinder) {
597		read_maybe_empty(
598			"It is highly recommended that the partition at offset 0\n"
599			"is UFS, EXT2FS filesystem or SunOS swap. Putting Linux swap\n"
600			"there may destroy your partition table and bootblock.\n"
601			"Type YES if you're very sure you would like that partition\n"
602			"tagged with 82 (Linux swap): ");
603		if (strcmp (line_ptr, "YES\n"))
604			return;
605	}
606	switch (sys) {
607	case SUNOS_SWAP:
608	case LINUX_SWAP:
609		/* swaps are not mountable by default */
610		sunlabel->infos[i].flags |= 0x01;
611		break;
612	default:
613		/* assume other types are mountable;
614		   user can change it anyway */
615		sunlabel->infos[i].flags &= ~0x01;
616		break;
617	}
618	sunlabel->infos[i].id = sys;
619}
620
621static void
622sun_list_table(int xtra)
623{
624	int i, w;
625
626	w = strlen(disk_device);
627	if (xtra)
628		printf(
629		"\nDisk %s (Sun disk label): %d heads, %d sectors, %d rpm\n"
630		"%d cylinders, %d alternate cylinders, %d physical cylinders\n"
631		"%d extra sects/cyl, interleave %d:1\n"
632		"%s\n"
633		"Units = %s of %d * 512 bytes\n\n",
634			disk_device, heads, sectors, SUN_SSWAP16(sunlabel->rspeed),
635			cylinders, SUN_SSWAP16(sunlabel->nacyl),
636			SUN_SSWAP16(sunlabel->pcylcount),
637			SUN_SSWAP16(sunlabel->sparecyl),
638			SUN_SSWAP16(sunlabel->ilfact),
639			(char *)sunlabel,
640			str_units(PLURAL), units_per_sector);
641	else
642		printf(
643	"\nDisk %s (Sun disk label): %d heads, %d sectors, %d cylinders\n"
644	"Units = %s of %d * 512 bytes\n\n",
645			disk_device, heads, sectors, cylinders,
646			str_units(PLURAL), units_per_sector);
647
648	printf("%*s Flag    Start       End    Blocks   Id  System\n",
649		w + 1, "Device");
650	for (i = 0; i < partitions; i++) {
651		if (sunlabel->partitions[i].num_sectors) {
652			uint32_t start = SUN_SSWAP32(sunlabel->partitions[i].start_cylinder) * heads * sectors;
653			uint32_t len = SUN_SSWAP32(sunlabel->partitions[i].num_sectors);
654			printf("%s %c%c %9ld %9ld %9ld%c  %2x  %s\n",
655				partname(disk_device, i+1, w),			/* device */
656				(sunlabel->infos[i].flags & 0x01) ? 'u' : ' ',  /* flags */
657				(sunlabel->infos[i].flags & 0x10) ? 'r' : ' ',
658				(long) scround(start),                          /* start */
659				(long) scround(start+len),                      /* end */
660				(long) len / 2, len & 1 ? '+' : ' ',            /* odd flag on end */
661				sunlabel->infos[i].id,                          /* type id */
662				partition_type(sunlabel->infos[i].id));         /* type name */
663		}
664	}
665}
666
667#if ENABLE_FEATURE_FDISK_ADVANCED
668
669static void
670sun_set_alt_cyl(void)
671{
672	sunlabel->nacyl =
673		SUN_SSWAP16(read_int(0, SUN_SSWAP16(sunlabel->nacyl), 65535, 0,
674				"Number of alternate cylinders"));
675}
676
677static void
678sun_set_ncyl(int cyl)
679{
680	sunlabel->ncyl = SUN_SSWAP16(cyl);
681}
682
683static void
684sun_set_xcyl(void)
685{
686	sunlabel->sparecyl =
687		SUN_SSWAP16(read_int(0, SUN_SSWAP16(sunlabel->sparecyl), sectors, 0,
688				"Extra sectors per cylinder"));
689}
690
691static void
692sun_set_ilfact(void)
693{
694	sunlabel->ilfact =
695		SUN_SSWAP16(read_int(1, SUN_SSWAP16(sunlabel->ilfact), 32, 0,
696				"Interleave factor"));
697}
698
699static void
700sun_set_rspeed(void)
701{
702	sunlabel->rspeed =
703		SUN_SSWAP16(read_int(1, SUN_SSWAP16(sunlabel->rspeed), 100000, 0,
704				"Rotation speed (rpm)"));
705}
706
707static void
708sun_set_pcylcount(void)
709{
710	sunlabel->pcylcount =
711		SUN_SSWAP16(read_int(0, SUN_SSWAP16(sunlabel->pcylcount), 65535, 0,
712				"Number of physical cylinders"));
713}
714#endif /* FEATURE_FDISK_ADVANCED */
715
716static void
717sun_write_table(void)
718{
719	unsigned short *ush = (unsigned short *)sunlabel;
720	unsigned short csum = 0;
721
722	while (ush < (unsigned short *)(&sunlabel->csum))
723		csum ^= *ush++;
724	sunlabel->csum = csum;
725	if (lseek(fd, 0, SEEK_SET) < 0)
726		fdisk_fatal(unable_to_seek);
727	if (write(fd, sunlabel, SECTOR_SIZE) != SECTOR_SIZE)
728		fdisk_fatal(unable_to_write);
729}
730#endif /* SUN_LABEL */
731