1 /*
2  * Copyright (C) 2013 Karel Zak <kzak@redhat.com>
3  *
4  * Based on original code from fdisk:
5  *   Jakub Jelinek (jj@sunsite.mff.cuni.cz), July 1996
6  *   Merged with fdisk for other architectures, aeb, June 1998.
7  *   Arnaldo Carvalho de Melo <acme@conectiva.com.br> Mar 1999, Internationalization
8  */
9 #include <stdio.h>		/* stderr */
10 #include <stdlib.h>		/* qsort */
11 #include <string.h>		/* strstr */
12 #include <unistd.h>		/* write */
13 #include <sys/ioctl.h>		/* ioctl */
14 
15 #include <libsmartcols.h>
16 
17 #include "nls.h"
18 #include "blkdev.h"
19 #include "bitops.h"
20 
21 #include "fdiskP.h"
22 #include "pt-sun.h"
23 #include "all-io.h"
24 
25 /*
26  * in-memory fdisk SUN stuff
27  */
28 struct fdisk_sun_label {
29 	struct fdisk_label	head;		/* generic part */
30 	struct sun_disklabel   *header;		/* on-disk data (pointer to cxt->firstsector) */
31 };
32 
33 static struct fdisk_parttype sun_parttypes[] = {
34 	{SUN_TAG_UNASSIGNED, N_("Unassigned")},
35 	{SUN_TAG_BOOT, N_("Boot")},
36 	{SUN_TAG_ROOT, N_("SunOS root")},
37 	{SUN_TAG_SWAP, N_("SunOS swap")},
38 	{SUN_TAG_USR, N_("SunOS usr")},
39 	{SUN_TAG_WHOLEDISK, N_("Whole disk")},
40 	{SUN_TAG_STAND, N_("SunOS stand")},
41 	{SUN_TAG_VAR, N_("SunOS var")},
42 	{SUN_TAG_HOME, N_("SunOS home")},
43 	{SUN_TAG_ALTSCTR, N_("SunOS alt sectors")},
44 	{SUN_TAG_CACHE, N_("SunOS cachefs")},
45 	{SUN_TAG_RESERVED, N_("SunOS reserved")},
46 	{SUN_TAG_LINUX_SWAP, N_("Linux swap")},
47 	{SUN_TAG_LINUX_NATIVE, N_("Linux native")},
48 	{SUN_TAG_LINUX_LVM, N_("Linux LVM")},
49 	{SUN_TAG_LINUX_RAID, N_("Linux raid autodetect")},
50 	{ 0, NULL }
51 };
52 
53 /* return poiter buffer with on-disk data */
self_disklabel(struct fdisk_context * cxt)54 static inline struct sun_disklabel *self_disklabel(struct fdisk_context *cxt)
55 {
56 	assert(cxt);
57 	assert(cxt->label);
58 	assert(fdisk_is_disklabel(cxt, SUN));
59 
60 	return ((struct fdisk_sun_label *) cxt->label)->header;
61 }
62 
63 /* return in-memory sun fdisk data */
self_label(struct fdisk_context * cxt)64 static inline struct fdisk_sun_label *self_label(struct fdisk_context *cxt)
65 {
66 	assert(cxt);
67 	assert(cxt->label);
68 	assert(fdisk_is_disklabel(cxt, SUN));
69 
70 	return (struct fdisk_sun_label *) cxt->label;
71 }
72 
set_sun_partition(struct fdisk_context * cxt,size_t i,uint32_t start,uint32_t stop,uint16_t sysid)73 static void set_sun_partition(struct fdisk_context *cxt, size_t i,
74 		uint32_t start,uint32_t stop, uint16_t sysid)
75 {
76 	struct sun_disklabel *sunlabel = self_disklabel(cxt);
77 	struct fdisk_parttype *t = fdisk_get_parttype_from_code(cxt, sysid);
78 
79 	sunlabel->vtoc.infos[i].id = cpu_to_be16(sysid);
80 	sunlabel->vtoc.infos[i].flags = cpu_to_be16(0);
81 	sunlabel->partitions[i].start_cylinder =
82 		cpu_to_be32(start / (cxt->geom.heads * cxt->geom.sectors));
83 	sunlabel->partitions[i].num_sectors = cpu_to_be32(stop - start);
84 	fdisk_label_set_changed(cxt->label, 1);
85 
86 	fdisk_info_new_partition(cxt, i + 1, start, stop, t);
87 }
88 
count_used_partitions(struct fdisk_context * cxt)89 static size_t count_used_partitions(struct fdisk_context *cxt)
90 {
91 	struct sun_disklabel *sunlabel = self_disklabel(cxt);
92 	size_t ct = 0, i;
93 
94 	assert(sunlabel);
95 
96 	for (i = 0; i < cxt->label->nparts_max; i++) {
97 		if (sunlabel->partitions[i].num_sectors)
98 			ct++;
99 	}
100 	return ct;
101 }
102 
sun_probe_label(struct fdisk_context * cxt)103 static int sun_probe_label(struct fdisk_context *cxt)
104 {
105 	struct fdisk_sun_label *sun;
106 	struct sun_disklabel *sunlabel;
107 	unsigned short *ush;
108 	int csum;
109 	int need_fixing = 0;
110 
111 	assert(cxt);
112 	assert(cxt->label);
113 	assert(fdisk_is_disklabel(cxt, SUN));
114 
115 	/* map first sector to header */
116 	sun = (struct fdisk_sun_label *) cxt->label;
117 	sun->header = (struct sun_disklabel *) cxt->firstsector;
118 	sunlabel = sun->header;
119 
120 	if (be16_to_cpu(sunlabel->magic) != SUN_LABEL_MAGIC) {
121 		sun->header = NULL;
122 		return 0;		/* failed */
123 	}
124 
125 	ush = ((unsigned short *) (sunlabel + 1)) - 1;
126 	for (csum = 0; ush >= (unsigned short *)sunlabel;)
127 		csum ^= *ush--;
128 
129 	if (csum) {
130 		fdisk_warnx(cxt, _("Detected sun disklabel with wrong checksum. "
131 			      "Probably you'll have to set all the values, "
132 			      "e.g. heads, sectors, cylinders and partitions "
133 			      "or force a fresh label (s command in main menu)"));
134 		return 1;
135 	}
136 
137 	cxt->label->nparts_max = SUN_MAXPARTITIONS;
138 	cxt->geom.heads = be16_to_cpu(sunlabel->nhead);
139 	cxt->geom.cylinders = be16_to_cpu(sunlabel->ncyl);
140 	cxt->geom.sectors = be16_to_cpu(sunlabel->nsect);
141 
142 	if (be32_to_cpu(sunlabel->vtoc.version) != SUN_VTOC_VERSION) {
143 		fdisk_warnx(cxt, _("Detected sun disklabel with wrong version [%d]."),
144 			be32_to_cpu(sunlabel->vtoc.version));
145 		need_fixing = 1;
146 	}
147 	if (be32_to_cpu(sunlabel->vtoc.sanity) != SUN_VTOC_SANITY) {
148 		fdisk_warnx(cxt, _("Detected sun disklabel with wrong vtoc.sanity [0x%08x]."),
149 			be32_to_cpu(sunlabel->vtoc.sanity));
150 		need_fixing = 1;
151 	}
152 	if (be16_to_cpu(sunlabel->vtoc.nparts) != SUN_MAXPARTITIONS) {
153 		fdisk_warnx(cxt, _("Detected sun disklabel with wrong vtoc.nparts [%u]."),
154 			be16_to_cpu(sunlabel->vtoc.nparts));
155 		need_fixing = 1;
156 	}
157 	if (need_fixing) {
158 		fdisk_warnx(cxt, _("Warning: Wrong values need to be fixed up and "
159 			           "will be corrected by w(rite)"));
160 
161 		sunlabel->vtoc.version = cpu_to_be32(SUN_VTOC_VERSION);
162 		sunlabel->vtoc.sanity = cpu_to_be32(SUN_VTOC_SANITY);
163 		sunlabel->vtoc.nparts = cpu_to_be16(SUN_MAXPARTITIONS);
164 
165 		ush = (unsigned short *)sunlabel;
166 		csum = 0;
167 		while(ush < (unsigned short *)(&sunlabel->csum))
168 			csum ^= *ush++;
169 		sunlabel->csum = csum;
170 
171 		fdisk_label_set_changed(cxt->label, 1);
172 	}
173 
174 	cxt->label->nparts_cur = count_used_partitions(cxt);
175 
176 	return 1;
177 }
178 
ask_geom(struct fdisk_context * cxt)179 static void ask_geom(struct fdisk_context *cxt)
180 {
181 	uintmax_t res;
182 
183 	assert(cxt);
184 
185 	if (fdisk_ask_number(cxt, 1, 1, 1024, _("Heads"), &res) == 0)
186 		cxt->geom.heads = res;
187 	if (fdisk_ask_number(cxt, 1, 1, 1024, _("Sectors/track"), &res) == 0)
188 		cxt->geom.sectors = res;
189 	if (fdisk_ask_number(cxt, 1, 1, USHRT_MAX, _("Cylinders"), &res) == 0)
190 		cxt->geom.cylinders = res;
191 }
192 
sun_create_disklabel(struct fdisk_context * cxt)193 static int sun_create_disklabel(struct fdisk_context *cxt)
194 {
195 	unsigned int ndiv;
196 	struct fdisk_sun_label *sun;		/* libfdisk sun handler */
197 	struct sun_disklabel *sunlabel;	/* on disk data */
198 	int rc = 0;
199 
200 	assert(cxt);
201 	assert(cxt->label);
202 	assert(fdisk_is_disklabel(cxt, SUN));
203 
204 	/* map first sector to header */
205 	rc = fdisk_init_firstsector_buffer(cxt);
206 	if (rc)
207 		return rc;
208 
209 	sun = (struct fdisk_sun_label *) cxt->label;
210 	sun->header = (struct sun_disklabel *) cxt->firstsector;
211 
212 	sunlabel = sun->header;
213 
214 	cxt->label->nparts_max = SUN_MAXPARTITIONS;
215 
216 	sunlabel->magic = cpu_to_be16(SUN_LABEL_MAGIC);
217 	sunlabel->vtoc.version = cpu_to_be32(SUN_VTOC_VERSION);
218 	sunlabel->vtoc.sanity = cpu_to_be32(SUN_VTOC_SANITY);
219 	sunlabel->vtoc.nparts = cpu_to_be16(SUN_MAXPARTITIONS);
220 
221 #ifdef HDIO_GETGEO
222 	if (cxt->geom.heads && cxt->geom.sectors) {
223 		sector_t llsectors;
224 
225 		if (blkdev_get_sectors(cxt->dev_fd, &llsectors) == 0) {
226 			int sec_fac = cxt->sector_size / 512;
227 			sector_t llcyls;
228 
229 			llcyls = llsectors / (cxt->geom.heads * cxt->geom.sectors * sec_fac);
230 			cxt->geom.cylinders = llcyls;
231 			if (cxt->geom.cylinders != llcyls)
232 				cxt->geom.cylinders = ~0;
233 		} else {
234 			fdisk_warnx(cxt,
235 				_("BLKGETSIZE ioctl failed on %s. "
236 				  "Using geometry cylinder value of %llu. "
237 				  "This value may be truncated for devices "
238 				  "> 33.8 GB."),
239 				cxt->dev_path, cxt->geom.cylinders);
240 		}
241 	} else
242 #endif
243 		ask_geom(cxt);
244 
245 	sunlabel->acyl   = cpu_to_be16(0);
246 	sunlabel->pcyl   = cpu_to_be16(cxt->geom.cylinders);
247 	sunlabel->rpm    = cpu_to_be16(5400);
248 	sunlabel->intrlv = cpu_to_be16(1);
249 	sunlabel->apc    = cpu_to_be16(0);
250 
251 	sunlabel->nhead  = cpu_to_be16(cxt->geom.heads);
252 	sunlabel->nsect  = cpu_to_be16(cxt->geom.sectors);
253 	sunlabel->ncyl   = cpu_to_be16(cxt->geom.cylinders);
254 
255 	snprintf((char *) sunlabel->label_id, sizeof(sunlabel->label_id),
256 		 "Linux cyl %llu alt %u hd %u sec %llu",
257 		 cxt->geom.cylinders, be16_to_cpu(sunlabel->acyl),
258 		 cxt->geom.heads, cxt->geom.sectors);
259 
260 	if (cxt->geom.cylinders * cxt->geom.heads * cxt->geom.sectors >= 150 * 2048) {
261 	        ndiv = cxt->geom.cylinders - (50 * 2048 / (cxt->geom.heads * cxt->geom.sectors)); /* 50M swap */
262 	} else
263 	        ndiv = cxt->geom.cylinders * 2 / 3;
264 
265 	set_sun_partition(cxt, 0, 0, ndiv * cxt->geom.heads * cxt->geom.sectors,
266 			  SUN_TAG_LINUX_NATIVE);
267 	set_sun_partition(cxt, 1, ndiv * cxt->geom.heads * cxt->geom.sectors,
268 			  cxt->geom.cylinders * cxt->geom.heads * cxt->geom.sectors,
269 			  SUN_TAG_LINUX_SWAP);
270 	sunlabel->vtoc.infos[1].flags |= cpu_to_be16(SUN_FLAG_UNMNT);
271 
272 	set_sun_partition(cxt, 2, 0,
273 			  cxt->geom.cylinders * cxt->geom.heads * cxt->geom.sectors,
274 			  SUN_TAG_WHOLEDISK);
275 
276 	{
277 		unsigned short *ush = (unsigned short *)sunlabel;
278 		unsigned short csum = 0;
279 		while(ush < (unsigned short *)(&sunlabel->csum))
280 			csum ^= *ush++;
281 		sunlabel->csum = csum;
282 	}
283 
284 	fdisk_label_set_changed(cxt->label, 1);
285 	cxt->label->nparts_cur = count_used_partitions(cxt);
286 
287 	fdisk_sinfo(cxt, FDISK_INFO_SUCCESS,
288 			_("Created a new Sun disklabel."));
289 	return 0;
290 }
291 
sun_toggle_partition_flag(struct fdisk_context * cxt,size_t i,unsigned long flag)292 static int sun_toggle_partition_flag(struct fdisk_context *cxt, size_t i, unsigned long flag)
293 {
294 	struct sun_disklabel *sunlabel;
295 	struct sun_info *p;
296 
297 	assert(cxt);
298 	assert(cxt->label);
299 	assert(fdisk_is_disklabel(cxt, SUN));
300 
301 	if (i >= cxt->label->nparts_max)
302 		return -EINVAL;
303 
304 	sunlabel = self_disklabel(cxt);
305 	p = &sunlabel->vtoc.infos[i];
306 
307 	switch (flag) {
308 	case SUN_FLAG_UNMNT:
309 		p->flags ^= cpu_to_be16(SUN_FLAG_UNMNT);
310 		fdisk_label_set_changed(cxt->label, 1);
311 		break;
312 	case SUN_FLAG_RONLY:
313 		p->flags ^= cpu_to_be16(SUN_FLAG_RONLY);
314 		fdisk_label_set_changed(cxt->label, 1);
315 		break;
316 	default:
317 		return 1;
318 	}
319 
320 	return 0;
321 }
322 
fetch_sun(struct fdisk_context * cxt,uint32_t * starts,uint32_t * lens,uint32_t * start,uint32_t * stop)323 static void fetch_sun(struct fdisk_context *cxt,
324 		      uint32_t *starts,
325 		      uint32_t *lens,
326 		      uint32_t *start,
327 		      uint32_t *stop)
328 {
329 	struct sun_disklabel *sunlabel;
330 	int continuous = 1;
331 	size_t i;
332 
333 	assert(cxt);
334 	assert(cxt);
335 	assert(cxt->label);
336 	assert(fdisk_is_disklabel(cxt, SUN));
337 
338 	sunlabel = self_disklabel(cxt);
339 
340 	*start = 0;
341 	*stop = cxt->geom.cylinders * cxt->geom.heads * cxt->geom.sectors;
342 
343 	for (i = 0; i < cxt->label->nparts_max; i++) {
344 		struct sun_partition *part = &sunlabel->partitions[i];
345 		struct sun_info *info = &sunlabel->vtoc.infos[i];
346 
347 		if (part->num_sectors &&
348 		    be16_to_cpu(info->id) != SUN_TAG_UNASSIGNED &&
349 		    be16_to_cpu(info->id) != SUN_TAG_WHOLEDISK) {
350 			starts[i] = be32_to_cpu(part->start_cylinder) *
351 				     cxt->geom.heads * cxt->geom.sectors;
352 			lens[i] = be32_to_cpu(part->num_sectors);
353 			if (continuous) {
354 				if (starts[i] == *start)
355 					*start += lens[i];
356 				else if (starts[i] + lens[i] >= *stop)
357 					*stop = starts[i];
358 				else
359 					continuous = 0;
360 				        /* There will be probably more gaps
361 					  than one, so lets check afterwards */
362 			}
363 		} else {
364 			starts[i] = 0;
365 			lens[i] = 0;
366 		}
367 	}
368 }
369 
370 #ifdef HAVE_QSORT_R
verify_sun_cmp(int * a,int * b,void * data)371 static int verify_sun_cmp(int *a, int *b, void *data)
372 {
373     unsigned int *verify_sun_starts = (unsigned int *) data;
374 
375     if (*a == -1)
376 	    return 1;
377     if (*b == -1)
378 	    return -1;
379     if (verify_sun_starts[*a] > verify_sun_starts[*b])
380 	    return 1;
381     return -1;
382 }
383 #endif
384 
sun_verify_disklabel(struct fdisk_context * cxt)385 static int sun_verify_disklabel(struct fdisk_context *cxt)
386 {
387     uint32_t starts[SUN_MAXPARTITIONS], lens[SUN_MAXPARTITIONS], start, stop;
388     uint32_t i,j,k,starto,endo;
389 #ifdef HAVE_QSORT_R
390     int array[SUN_MAXPARTITIONS];
391     unsigned int *verify_sun_starts;
392 #endif
393     assert(cxt);
394     assert(cxt->label);
395     assert(fdisk_is_disklabel(cxt, SUN));
396 
397     fetch_sun(cxt, starts, lens, &start, &stop);
398 
399     for (k = 0; k < 7; k++) {
400 	for (i = 0; i < SUN_MAXPARTITIONS; i++) {
401 	    if (k && (lens[i] % (cxt->geom.heads * cxt->geom.sectors)))
402 	        fdisk_warnx(cxt, _("Partition %u doesn't end on cylinder boundary."), i+1);
403 	    if (lens[i]) {
404 	        for (j = 0; j < i; j++)
405 	            if (lens[j]) {
406 	                if (starts[j] == starts[i]+lens[i]) {
407 	                    starts[j] = starts[i]; lens[j] += lens[i];
408 	                    lens[i] = 0;
409 	                } else if (starts[i] == starts[j]+lens[j]){
410 	                    lens[j] += lens[i];
411 	                    lens[i] = 0;
412 	                } else if (!k) {
413 	                    if (starts[i] < starts[j]+lens[j] &&
414 				starts[j] < starts[i]+lens[i]) {
415 	                        starto = starts[i];
416 	                        if (starts[j] > starto)
417 					starto = starts[j];
418 	                        endo = starts[i]+lens[i];
419 	                        if (starts[j]+lens[j] < endo)
420 					endo = starts[j]+lens[j];
421 	                        fdisk_warnx(cxt, _("Partition %u overlaps with others in "
422 				       "sectors %u-%u."), i+1, starto, endo);
423 	                    }
424 	                }
425 	            }
426 	    }
427 	}
428     }
429 
430 #ifdef HAVE_QSORT_R
431     for (i = 0; i < SUN_MAXPARTITIONS; i++) {
432         if (lens[i])
433             array[i] = i;
434         else
435             array[i] = -1;
436     }
437     verify_sun_starts = starts;
438 
439     qsort_r(array,ARRAY_SIZE(array),sizeof(array[0]),
440 	  (int (*)(const void *,const void *,void *)) verify_sun_cmp,
441 	  verify_sun_starts);
442 
443     if (array[0] == -1) {
444 	fdisk_info(cxt, _("No partitions defined."));
445 	return 0;
446     }
447     stop = cxt->geom.cylinders * cxt->geom.heads * cxt->geom.sectors;
448     if (starts[array[0]])
449         fdisk_warnx(cxt, _("Unused gap - sectors 0-%u."), starts[array[0]]);
450     for (i = 0; i < 7 && array[i+1] != -1; i++) {
451         fdisk_warnx(cxt, _("Unused gap - sectors %u-%u."),
452 	       (starts[array[i]] + lens[array[i]]),
453 	       starts[array[i+1]]);
454     }
455     start = (starts[array[i]] + lens[array[i]]);
456     if (start < stop)
457         fdisk_warnx(cxt, _("Unused gap - sectors %u-%u."), start, stop);
458 #endif
459     return 0;
460 }
461 
462 
is_free_sector(struct fdisk_context * cxt,sector_t s,uint32_t starts[],uint32_t lens[])463 static int is_free_sector(struct fdisk_context *cxt,
464 		sector_t s, uint32_t starts[], uint32_t lens[])
465 {
466 	size_t i;
467 
468 	for (i = 0; i < cxt->label->nparts_max; i++) {
469 		if (lens[i] && starts[i] <= s
470 		    && starts[i] + lens[i] > s)
471 			return 0;
472 	}
473 	return 1;
474 }
475 
sun_add_partition(struct fdisk_context * cxt,struct fdisk_partition * pa)476 static int sun_add_partition(
477 		struct fdisk_context *cxt,
478 		struct fdisk_partition *pa)
479 {
480 	struct sun_disklabel *sunlabel = self_disklabel(cxt);
481 	uint32_t starts[SUN_MAXPARTITIONS], lens[SUN_MAXPARTITIONS];
482 	struct sun_partition *part;
483 	struct sun_info *info;
484 	uint32_t start, stop, stop2;
485 	int whole_disk = 0;
486 	int sys = pa && pa->type ? pa->type->type : SUN_TAG_LINUX_NATIVE;
487 	int rc;
488 	size_t n;
489 
490 	char mesg[256];
491 	size_t i;
492 	unsigned int first, last;
493 
494 	rc = fdisk_partition_next_partno(pa, cxt, &n);
495 	if (rc)
496 		return rc;
497 
498 	part = &sunlabel->partitions[n];
499 	info = &sunlabel->vtoc.infos[n];
500 
501 	if (part->num_sectors && be16_to_cpu(info->id) != SUN_TAG_UNASSIGNED) {
502 		fdisk_info(cxt, _("Partition %zu is already defined.  Delete "
503 			"it before re-adding it."), n + 1);
504 		return -EINVAL;
505 	}
506 
507 	fetch_sun(cxt, starts, lens, &start, &stop);
508 
509 	if (stop <= start) {
510 		if (n == 2)
511 			whole_disk = 1;
512 		else {
513 			fdisk_info(cxt, _("Other partitions already cover the "
514 				"whole disk. Delete some/shrink them before retry."));
515 			return -EINVAL;
516 		}
517 	}
518 
519 	if (pa && pa->start_follow_default)
520 		first = start;
521 	else if (pa && pa->start) {
522 		first = pa->start;
523 
524 		if (!whole_disk && !is_free_sector(cxt, first, starts, lens))
525 			return -ERANGE;
526 	} else {
527 		struct fdisk_ask *ask;
528 
529 		snprintf(mesg, sizeof(mesg), _("First %s"),
530 				fdisk_context_get_unit(cxt, SINGULAR));
531 		for (;;) {
532 			ask = fdisk_new_ask();
533 			if (!ask)
534 				return -ENOMEM;
535 
536 			fdisk_ask_set_query(ask, mesg);
537 			fdisk_ask_set_type(ask, FDISK_ASKTYPE_NUMBER);
538 
539 			if (whole_disk) {
540 				fdisk_ask_number_set_low(ask,     0);	/* minimal */
541 				fdisk_ask_number_set_default(ask, 0);	/* default */
542 				fdisk_ask_number_set_high(ask,    0);	/* maximal */
543 			} else {
544 				fdisk_ask_number_set_low(ask,     fdisk_scround(cxt, start));	/* minimal */
545 				fdisk_ask_number_set_default(ask, fdisk_scround(cxt, start));	/* default */
546 				fdisk_ask_number_set_high(ask,    fdisk_scround(cxt, stop));	/* maximal */
547 			}
548 			rc = fdisk_do_ask(cxt, ask);
549 			first = fdisk_ask_number_get_result(ask);
550 			fdisk_free_ask(ask);
551 			if (rc)
552 				return rc;
553 
554 			if (fdisk_context_use_cylinders(cxt))
555 				first *= fdisk_context_get_units_per_sector(cxt);
556 
557 			/* ewt asks to add: "don't start a partition at cyl 0"
558 			   However, edmundo@rano.demon.co.uk writes:
559 			   "In addition to having a Sun partition table, to be able to
560 			   boot from the disc, the first partition, /dev/sdX1, must
561 			   start at cylinder 0. This means that /dev/sdX1 contains
562 			   the partition table and the boot block, as these are the
563 			   first two sectors of the disc. Therefore you must be
564 			   careful what you use /dev/sdX1 for. In particular, you must
565 			   not use a partition starting at cylinder 0 for Linux swap,
566 			   as that would overwrite the partition table and the boot
567 			   block. You may, however, use such a partition for a UFS
568 			   or EXT2 file system, as these file systems leave the first
569 			   1024 bytes undisturbed. */
570 			/* On the other hand, one should not use partitions
571 			   starting at block 0 in an md, or the label will
572 			   be trashed. */
573 			if (!is_free_sector(cxt, first, starts,  lens) && !whole_disk) {
574 				if (n == 2 && !first) {
575 				    whole_disk = 1;
576 				    break;
577 				}
578 				fdisk_warnx(cxt, _("Sector %d is already allocated"), first);
579 			} else
580 				break;
581 		}
582 	}
583 
584 	if (n == 2 && first != 0)
585 		fdisk_warnx(cxt, _("It is highly recommended that the "
586 				   "third partition covers the whole disk "
587 				   "and is of type `Whole disk'"));
588 
589 	if (!fdisk_context_use_cylinders(cxt)) {
590 		/* Starting sector has to be properly aligned */
591 		int cs = cxt->geom.heads * cxt->geom.sectors;
592 		int x = first % cs;
593 
594 		if (x) {
595 			fdisk_info(cxt, _("Aligning the first sector from %u to %u "
596 					  "to be on cylinder boundary."),
597 					first, first + cs - x);
598 			first += cs - x;
599 		}
600 	}
601 
602 	stop = cxt->geom.cylinders * cxt->geom.heads * cxt->geom.sectors;	/* ancient */
603 	stop2 = stop;
604 	for (i = 0; i < cxt->label->nparts_max; i++) {
605 		if (starts[i] > first && starts[i] < stop)
606 			stop = starts[i];
607 	}
608 
609 	/* last */
610 	if (pa && pa->end_follow_default)
611 		last = whole_disk || (n == 2 && !first) ? stop2 : stop;
612 	else if (pa && pa->size) {
613 		last = pa->size;
614 
615 		if (!whole_disk && last > stop)
616 			return -ERANGE;
617 	} else {
618 		struct fdisk_ask *ask = fdisk_new_ask();
619 
620 		if (!ask)
621 			return -ENOMEM;
622 
623 		snprintf(mesg, sizeof(mesg),
624 			 _("Last %s or +%s or +size{K,M,G,T,P}"),
625 			 fdisk_context_get_unit(cxt, SINGULAR),
626 			 fdisk_context_get_unit(cxt, PLURAL));
627 		fdisk_ask_set_query(ask, mesg);
628 		fdisk_ask_set_type(ask, FDISK_ASKTYPE_OFFSET);
629 
630 		if (whole_disk) {
631 			fdisk_ask_number_set_low(ask,     fdisk_scround(cxt, stop2));	/* minimal */
632 			fdisk_ask_number_set_default(ask, fdisk_scround(cxt, stop2));	/* default */
633 			fdisk_ask_number_set_high(ask,    fdisk_scround(cxt, stop2));	/* maximal */
634 			fdisk_ask_number_set_base(ask,    0);
635 		} else if (n == 2 && !first) {
636 			fdisk_ask_number_set_low(ask,     fdisk_scround(cxt, first));	/* minimal */
637 			fdisk_ask_number_set_default(ask, fdisk_scround(cxt, stop2));	/* default */
638 			fdisk_ask_number_set_high(ask,    fdisk_scround(cxt, stop2));	/* maximal */
639 			fdisk_ask_number_set_base(ask,	  fdisk_scround(cxt, first));
640 		} else {
641 			fdisk_ask_number_set_low(ask,     fdisk_scround(cxt, first));	/* minimal */
642 			fdisk_ask_number_set_default(ask, fdisk_scround(cxt, stop));	/* default */
643 			fdisk_ask_number_set_high(ask,    fdisk_scround(cxt, stop));	/* maximal */
644 			fdisk_ask_number_set_base(ask,    fdisk_scround(cxt, first));
645 		}
646 
647 		if (fdisk_context_use_cylinders(cxt))
648 			fdisk_ask_number_set_unit(ask,
649 				     cxt->sector_size *
650 				     fdisk_context_get_units_per_sector(cxt));
651 		else
652 			fdisk_ask_number_set_unit(ask,	cxt->sector_size);
653 
654 		rc = fdisk_do_ask(cxt, ask);
655 		last = fdisk_ask_number_get_result(ask);
656 
657 		fdisk_free_ask(ask);
658 		if (rc)
659 			return rc;
660 		if (fdisk_context_use_cylinders(cxt))
661 			last *= fdisk_context_get_units_per_sector(cxt);
662 	}
663 
664 	if (n == 2 && !first) {
665 		if (last >= stop2) {
666 		    whole_disk = 1;
667 		    last = stop2;
668 		} else if (last > stop) {
669 		    fdisk_warnx(cxt,
670    _("You haven't covered the whole disk with the 3rd partition, but your value\n"
671      "%lu %s covers some other partition. Your entry has been changed\n"
672      "to %lu %s"),
673 			(unsigned long) fdisk_scround(cxt, last), fdisk_context_get_unit(cxt, SINGULAR),
674 			(unsigned long) fdisk_scround(cxt, stop), fdisk_context_get_unit(cxt, SINGULAR));
675 		    last = stop;
676 		}
677 	} else if (!whole_disk && last > stop)
678 		last = stop;
679 
680 	if (whole_disk)
681 		sys = SUN_TAG_WHOLEDISK;
682 
683 	set_sun_partition(cxt, n, first, last, sys);
684 	cxt->label->nparts_cur = count_used_partitions(cxt);
685 	return 0;
686 }
687 
sun_delete_partition(struct fdisk_context * cxt,size_t partnum)688 static int sun_delete_partition(struct fdisk_context *cxt,
689 		size_t partnum)
690 {
691 	struct sun_disklabel *sunlabel;
692 	struct sun_partition *part;
693 	struct sun_info *info;
694 	unsigned int nsec;
695 
696 	assert(cxt);
697 	assert(cxt->label);
698 	assert(fdisk_is_disklabel(cxt, SUN));
699 
700 	sunlabel = self_disklabel(cxt);
701 	part = &sunlabel->partitions[partnum];
702 	info = &sunlabel->vtoc.infos[partnum];
703 
704 	if (partnum == 2 &&
705 	    be16_to_cpu(info->id) == SUN_TAG_WHOLEDISK &&
706 	    !part->start_cylinder &&
707 	    (nsec = be32_to_cpu(part->num_sectors))
708 	    == cxt->geom.heads * cxt->geom.sectors * cxt->geom.cylinders)
709 		fdisk_info(cxt, _("If you want to maintain SunOS/Solaris compatibility, "
710 			 "consider leaving this "
711 			 "partition as Whole disk (5), starting at 0, with %u "
712 			 "sectors"), nsec);
713 	info->id = cpu_to_be16(SUN_TAG_UNASSIGNED);
714 	part->num_sectors = 0;
715 	cxt->label->nparts_cur = count_used_partitions(cxt);
716 	fdisk_label_set_changed(cxt->label, 1);
717 	return 0;
718 }
719 
720 
sun_list_disklabel(struct fdisk_context * cxt)721 static int sun_list_disklabel(struct fdisk_context *cxt)
722 {
723 	struct sun_disklabel *sunlabel;
724 
725 	assert(cxt);
726 	assert(cxt->label);
727 	assert(fdisk_is_disklabel(cxt, SUN));
728 
729 	sunlabel = self_disklabel(cxt);
730 
731 	if (fdisk_context_display_details(cxt)) {
732 		fdisk_info(cxt,
733 		_("Label geometry: %d rpm, %d alternate and %d physical cylinders,\n"
734 		  "                %d extra sects/cyl, interleave %d:1"),
735 		       be16_to_cpu(sunlabel->rpm),
736 		       be16_to_cpu(sunlabel->acyl),
737 		       be16_to_cpu(sunlabel->pcyl),
738 		       be16_to_cpu(sunlabel->apc),
739 		       be16_to_cpu(sunlabel->intrlv));
740 		fdisk_info(cxt, _("Label ID: %s"), sunlabel->label_id);
741 		fdisk_info(cxt, _("Volume ID: %s"),
742 		       *sunlabel->vtoc.volume_id ? sunlabel->vtoc.volume_id : _("<none>"));
743 	}
744 
745 	return 0;
746 }
747 
sun_get_parttype(struct fdisk_context * cxt,size_t n)748 static struct fdisk_parttype *sun_get_parttype(
749 		struct fdisk_context *cxt,
750 		size_t n)
751 {
752 	struct sun_disklabel *sunlabel = self_disklabel(cxt);
753 	struct fdisk_parttype *t;
754 
755 	if (n >= cxt->label->nparts_max)
756 		return NULL;
757 
758 	t = fdisk_get_parttype_from_code(cxt, be16_to_cpu(sunlabel->vtoc.infos[n].id));
759 	return t ? : fdisk_new_unknown_parttype(be16_to_cpu(sunlabel->vtoc.infos[n].id), NULL);
760 }
761 
762 
sun_get_partition(struct fdisk_context * cxt,size_t n,struct fdisk_partition * pa)763 static int sun_get_partition(struct fdisk_context *cxt, size_t n,
764 			      struct fdisk_partition *pa)
765 {
766 	struct sun_disklabel *sunlabel;
767 	struct sun_partition *part;
768 	uint16_t flags;
769 	uint32_t start, len;
770 
771 	assert(cxt);
772 	assert(cxt->label);
773 	assert(fdisk_is_disklabel(cxt, SUN));
774 
775 	if (n >= cxt->label->nparts_max)
776 		return -EINVAL;
777 
778 	sunlabel = self_disklabel(cxt);
779 	part = &sunlabel->partitions[n];
780 
781 	pa->used = part->num_sectors ? 1 : 0;
782 	if (!pa->used)
783 		return 0;
784 
785 	flags = be16_to_cpu(sunlabel->vtoc.infos[n].flags);
786 	start = be32_to_cpu(part->start_cylinder)
787 			* cxt->geom.heads * cxt->geom.sectors;
788 	len = be32_to_cpu(part->num_sectors);
789 
790 	pa->type = sun_get_parttype(cxt, n);
791 	if (pa->type && pa->type->type == SUN_TAG_WHOLEDISK)
792 		pa->wholedisk = 1;
793 
794 	if (flags & SUN_FLAG_UNMNT || flags & SUN_FLAG_RONLY) {
795 		if (asprintf(&pa->attrs, "%c%c",
796 				flags & SUN_FLAG_UNMNT ? 'u' : ' ',
797 				flags & SUN_FLAG_RONLY ? 'r' : ' ') < 0)
798 			return -ENOMEM;
799 	}
800 
801 	pa->start = start;
802 	pa->end = start + len - (len ? 1 : 0);
803 	pa->size = len;
804 
805 	return 0;
806 }
807 
808 
fdisk_sun_set_alt_cyl(struct fdisk_context * cxt)809 int fdisk_sun_set_alt_cyl(struct fdisk_context *cxt)
810 {
811 	struct sun_disklabel *sunlabel = self_disklabel(cxt);
812 	uintmax_t res;
813 	int rc = fdisk_ask_number(cxt, 0,			/* low */
814 			be16_to_cpu(sunlabel->acyl),		/* default */
815 			65535,					/* high */
816 			_("Number of alternate cylinders"),	/* query */
817 			&res);					/* result */
818 	if (rc)
819 		return rc;
820 
821 	sunlabel->acyl = cpu_to_be16(res);
822 	return 0;
823 }
824 
fdisk_sun_set_xcyl(struct fdisk_context * cxt)825 int fdisk_sun_set_xcyl(struct fdisk_context *cxt)
826 {
827 	struct sun_disklabel *sunlabel = self_disklabel(cxt);
828 	uintmax_t res;
829 	int rc = fdisk_ask_number(cxt, 0,			/* low */
830 			be16_to_cpu(sunlabel->apc),		/* default */
831 			cxt->geom.sectors,			/* high */
832 			_("Extra sectors per cylinder"),	/* query */
833 			&res);					/* result */
834 	if (rc)
835 		return rc;
836 	sunlabel->apc = cpu_to_be16(res);
837 	return 0;
838 }
839 
fdisk_sun_set_ilfact(struct fdisk_context * cxt)840 int fdisk_sun_set_ilfact(struct fdisk_context *cxt)
841 {
842 	struct sun_disklabel *sunlabel = self_disklabel(cxt);
843 	uintmax_t res;
844 	int rc = fdisk_ask_number(cxt, 1,			/* low */
845 			be16_to_cpu(sunlabel->intrlv),		/* default */
846 			32,					/* high */
847 			_("Interleave factor"),	/* query */
848 			&res);					/* result */
849 	if (rc)
850 		return rc;
851 	sunlabel->intrlv = cpu_to_be16(res);
852 	return 0;
853 }
854 
fdisk_sun_set_rspeed(struct fdisk_context * cxt)855 int fdisk_sun_set_rspeed(struct fdisk_context *cxt)
856 {
857 	struct sun_disklabel *sunlabel = self_disklabel(cxt);
858 	uintmax_t res;
859 	int rc = fdisk_ask_number(cxt, 1,			/* low */
860 			be16_to_cpu(sunlabel->rpm),		/* default */
861 			USHRT_MAX,				/* high */
862 			_("Rotation speed (rpm)"),		/* query */
863 			&res);					/* result */
864 	if (rc)
865 		return rc;
866 	sunlabel->rpm = cpu_to_be16(res);
867 	return 0;
868 }
869 
fdisk_sun_set_pcylcount(struct fdisk_context * cxt)870 int fdisk_sun_set_pcylcount(struct fdisk_context *cxt)
871 {
872 	struct sun_disklabel *sunlabel = self_disklabel(cxt);
873 	uintmax_t res;
874 	int rc = fdisk_ask_number(cxt, 0,			/* low */
875 			be16_to_cpu(sunlabel->pcyl),		/* default */
876 			USHRT_MAX,				/* high */
877 			_("Number of physical cylinders"),	/* query */
878 			&res);					/* result */
879 	if (!rc)
880 		return rc;
881 	sunlabel->pcyl = cpu_to_be16(res);
882 	return 0;
883 }
884 
sun_write_disklabel(struct fdisk_context * cxt)885 static int sun_write_disklabel(struct fdisk_context *cxt)
886 {
887 	struct sun_disklabel *sunlabel;
888 	unsigned short *ush;
889 	unsigned short csum = 0;
890 	const size_t sz = sizeof(struct sun_disklabel);
891 
892 	assert(cxt);
893 	assert(cxt->label);
894 	assert(fdisk_is_disklabel(cxt, SUN));
895 
896 	sunlabel = self_disklabel(cxt);
897 
898 	/* Maybe geometry has been modified */
899 	sunlabel->nhead = cpu_to_be16(cxt->geom.heads);
900 	sunlabel->nsect = cpu_to_be16(cxt->geom.sectors);
901 
902 	if (cxt->geom.cylinders != be16_to_cpu(sunlabel->ncyl))
903 		sunlabel->ncyl = cpu_to_be16( cxt->geom.cylinders
904 				      - be16_to_cpu(sunlabel->acyl) );
905 
906 	ush = (unsigned short *) sunlabel;
907 
908 	while(ush < (unsigned short *)(&sunlabel->csum))
909 		csum ^= *ush++;
910 	sunlabel->csum = csum;
911 	if (lseek(cxt->dev_fd, 0, SEEK_SET) < 0)
912 		return -errno;
913 	if (write_all(cxt->dev_fd, sunlabel, sz) != 0)
914 		return -errno;
915 
916 	return 0;
917 }
918 
sun_set_parttype(struct fdisk_context * cxt,size_t i,struct fdisk_parttype * t)919 static int sun_set_parttype(
920 		struct fdisk_context *cxt,
921 		size_t i,
922 		struct fdisk_parttype *t)
923 {
924 	struct sun_disklabel *sunlabel;
925 	struct sun_partition *part;
926 	struct sun_info *info;
927 
928 	assert(cxt);
929 	assert(cxt->label);
930 	assert(fdisk_is_disklabel(cxt, SUN));
931 
932 	sunlabel = self_disklabel(cxt);
933 
934 	if (i >= cxt->label->nparts_max || !t || t->type > UINT16_MAX)
935 		return -EINVAL;
936 
937 	if (i == 2 && t->type != SUN_TAG_WHOLEDISK)
938 		fdisk_info(cxt, _("Consider leaving partition 3 as Whole disk (5),\n"
939 		         "as SunOS/Solaris expects it and even Linux likes it.\n"));
940 
941 	part = &sunlabel->partitions[i];
942 	info = &sunlabel->vtoc.infos[i];
943 
944 	if (t->type == SUN_TAG_LINUX_SWAP && !part->start_cylinder) {
945 	    int yes, rc;
946 	    rc = fdisk_ask_yesno(cxt,
947 	      _("It is highly recommended that the partition at offset 0\n"
948 	      "is UFS, EXT2FS filesystem or SunOS swap. Putting Linux swap\n"
949 	      "there may destroy your partition table and bootblock.\n"
950 	      "Are you sure you want to tag the partition as Linux swap?"), &yes);
951 	    if (rc)
952 		    return rc;
953 	    if (!yes)
954 		    return 1;
955 	}
956 
957 	switch (t->type) {
958 	case SUN_TAG_SWAP:
959 	case SUN_TAG_LINUX_SWAP:
960 		/* swaps are not mountable by default */
961 		info->flags |= cpu_to_be16(SUN_FLAG_UNMNT);
962 		break;
963 	default:
964 		/* assume other types are mountable;
965 		   user can change it anyway */
966 		info->flags &= ~cpu_to_be16(SUN_FLAG_UNMNT);
967 		break;
968 	}
969 	info->id = cpu_to_be16(t->type);
970 	return 0;
971 }
972 
973 
sun_reset_alignment(struct fdisk_context * cxt)974 static int sun_reset_alignment(struct fdisk_context *cxt __attribute__((__unused__)))
975 {
976 	return 0;
977 }
978 
979 
sun_partition_is_used(struct fdisk_context * cxt,size_t i)980 static int sun_partition_is_used(
981 		struct fdisk_context *cxt,
982 		size_t i)
983 {
984 	struct sun_disklabel *sunlabel;
985 
986 	assert(cxt);
987 	assert(cxt->label);
988 	assert(fdisk_is_disklabel(cxt, SUN));
989 
990 	if (i >= cxt->label->nparts_max)
991 		return 0;
992 
993 	sunlabel = self_disklabel(cxt);
994 	return sunlabel->partitions[i].num_sectors ? 1 : 0;
995 }
996 
997 
998 static const struct fdisk_column sun_columns[] =
999 {
1000 	{ FDISK_COL_DEVICE,	N_("Device"),	 10,	0 },
1001 	{ FDISK_COL_START,	N_("Start"),	  5,	SCOLS_FL_RIGHT },
1002 	{ FDISK_COL_END,	N_("End"),	  5,	SCOLS_FL_RIGHT },
1003 	{ FDISK_COL_SECTORS,	N_("Sectors"),	  5,	SCOLS_FL_RIGHT },
1004 	{ FDISK_COL_CYLINDERS,	N_("Cylinders"),  5,	SCOLS_FL_RIGHT },
1005 	{ FDISK_COL_SIZE,	N_("Size"),	  5,	SCOLS_FL_RIGHT },
1006 	{ FDISK_COL_TYPEID,	N_("Id"),	  2,	SCOLS_FL_RIGHT },
1007 	{ FDISK_COL_TYPE,	N_("Type"),	0.1,	SCOLS_FL_TRUNC },
1008 	{ FDISK_COL_ATTR,	N_("Flags"),	  0,	SCOLS_FL_RIGHT }
1009 };
1010 
1011 const struct fdisk_label_operations sun_operations =
1012 {
1013 	.probe		= sun_probe_label,
1014 	.write		= sun_write_disklabel,
1015 	.verify		= sun_verify_disklabel,
1016 	.create		= sun_create_disklabel,
1017 	.list		= sun_list_disklabel,
1018 
1019 	.get_part	= sun_get_partition,
1020 	.add_part	= sun_add_partition,
1021 
1022 	.part_delete	= sun_delete_partition,
1023 	.part_set_type	= sun_set_parttype,
1024 
1025 	.part_is_used	= sun_partition_is_used,
1026 	.part_toggle_flag = sun_toggle_partition_flag,
1027 
1028 	.reset_alignment = sun_reset_alignment,
1029 };
1030 
1031 /*
1032  * allocates SUN label driver
1033  */
fdisk_new_sun_label(struct fdisk_context * cxt)1034 struct fdisk_label *fdisk_new_sun_label(struct fdisk_context *cxt)
1035 {
1036 	struct fdisk_label *lb;
1037 	struct fdisk_sun_label *sun;
1038 
1039 	assert(cxt);
1040 
1041 	sun = calloc(1, sizeof(*sun));
1042 	if (!sun)
1043 		return NULL;
1044 
1045 	/* initialize generic part of the driver */
1046 	lb = (struct fdisk_label *) sun;
1047 	lb->name = "sun";
1048 	lb->id = FDISK_DISKLABEL_SUN;
1049 	lb->op = &sun_operations;
1050 	lb->parttypes = sun_parttypes;
1051 	lb->nparttypes = ARRAY_SIZE(sun_parttypes);
1052 	lb->columns = sun_columns;
1053 	lb->ncolumns = ARRAY_SIZE(sun_columns);
1054 	lb->flags |= FDISK_LABEL_FL_REQUIRE_GEOMETRY;
1055 
1056 	return lb;
1057 }
1058