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