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