4 * I think this is mostly, or entirely, due to
5 * Jakub Jelinek (jj@sunsite.mff.cuni.cz), July 1996
7 * Merged with fdisk for other architectures, aeb, June 1998.
9 * Sat Mar 20 EST 1999 Arnaldo Carvalho de Melo <acme@conectiva.com.br>
10 * Internationalization
13 #include <stdio.h> /* stderr */
14 #include <stdlib.h> /* qsort */
15 #include <string.h> /* strstr */
16 #include <unistd.h> /* write */
17 #include <sys/ioctl.h> /* ioctl */
18 #include <sys/stat.h> /* stat */
19 #include <sys/sysmacros.h> /* major */
25 #ifdef HAVE_SCSI_SCSI_H
26 #define u_char unsigned char
27 #include <scsi/scsi.h> /* SCSI_IOCTL_GET_IDLUN */
30 #ifdef HAVE_LINUX_MAJOR_H
31 #include <linux/major.h> /* FLOPPY_MAJOR */
36 #include "fdisksunlabel.h"
38 static int other_endian = 0;
39 static int scsi_disk = 0;
40 static int floppy = 0;
42 struct systypes sun_sys_types[] = {
43 {SUN_TAG_UNASSIGNED, N_("Unassigned")},
44 {SUN_TAG_BOOT, N_("Boot")},
45 {SUN_TAG_ROOT, N_("SunOS root")},
46 {SUN_TAG_SWAP, N_("SunOS swap")},
47 {SUN_TAG_USR, N_("SunOS usr")},
48 {SUN_TAG_BACKUP, N_("Whole disk")},
49 {SUN_TAG_STAND, N_("SunOS stand")},
50 {SUN_TAG_VAR, N_("SunOS var")},
51 {SUN_TAG_HOME, N_("SunOS home")},
52 {SUN_TAG_ALTSCTR, N_("SunOS alt sectors")},
53 {SUN_TAG_CACHE, N_("SunOS cachefs")},
54 {SUN_TAG_RESERVED, N_("SunOS reserved")},
55 {SUN_TAG_LINUX_SWAP, N_("Linux swap")},
56 {SUN_TAG_LINUX_NATIVE, N_("Linux native")},
57 {SUN_TAG_LINUX_LVM, N_("Linux LVM")},
58 {SUN_TAG_LINUX_RAID, N_("Linux raid autodetect")},
62 static inline unsigned short __swap16(unsigned short x) {
63 return (((uint16_t)(x) & 0xFF) << 8) | (((uint16_t)(x) & 0xFF00) >> 8);
65 static inline uint32_t __swap32(uint32_t x) {
66 return (((uint32_t)(x) & 0xFF) << 24) | (((uint32_t)(x) & 0xFF00) << 8) | (((uint32_t)(x) & 0xFF0000) >> 8) | (((uint32_t)(x) & 0xFF000000) >> 24);
69 #define SSWAP16(x) (other_endian ? __swap16(x) \
71 #define SSWAP32(x) (other_endian ? __swap32(x) \
75 #define FLOPPY_MAJOR 2
83 void guess_device_type(int fd)
87 if (fstat (fd, &bootstat) < 0) {
90 } else if (S_ISBLK(bootstat.st_mode)
91 && (major(bootstat.st_rdev) == IDE0_MAJOR ||
92 major(bootstat.st_rdev) == IDE1_MAJOR)) {
95 } else if (S_ISBLK(bootstat.st_mode)
96 && major(bootstat.st_rdev) == FLOPPY_MAJOR) {
105 static void set_sun_partition(int i, uint32_t start, uint32_t stop, uint16_t sysid)
107 sunlabel->part_tags[i].tag = SSWAP16(sysid);
108 sunlabel->part_tags[i].flag = SSWAP16(0);
109 sunlabel->partitions[i].start_cylinder =
110 SSWAP32(start / (heads * sectors));
111 sunlabel->partitions[i].num_sectors =
112 SSWAP32(stop - start);
116 void sun_nolabel(void)
123 int check_sun_label(void)
128 if (sunlabel->magic != SUN_LABEL_MAGIC &&
129 sunlabel->magic != SUN_LABEL_MAGIC_SWAPPED) {
134 other_endian = (sunlabel->magic == SUN_LABEL_MAGIC_SWAPPED);
136 ush = ((unsigned short *) (sunlabel + 1)) - 1;
137 for (csum = 0; ush >= (unsigned short *)sunlabel;)
141 fprintf(stderr,_("Detected sun disklabel with wrong checksum.\n"
142 "Probably you'll have to set all the values,\n"
143 "e.g. heads, sectors, cylinders and partitions\n"
144 "or force a fresh label (s command in main menu)\n"));
148 heads = SSWAP16(sunlabel->nhead);
149 cylinders = SSWAP16(sunlabel->ncyl);
150 sectors = SSWAP16(sunlabel->nsect);
152 if (sunlabel->version != SSWAP32(SUN_LABEL_VERSION)) {
153 fprintf(stderr,_("Detected sun disklabel with wrong version [0x%08x].\n"),
157 if (sunlabel->sanity != SSWAP32(SUN_LABEL_SANE)) {
158 fprintf(stderr,_("Detected sun disklabel with wrong sanity [0x%08x].\n"),
162 if (sunlabel->num_partitions != SSWAP16(SUN_NUM_PARTITIONS)) {
163 fprintf(stderr,_("Detected sun disklabel with wrong num_partitions [%u].\n"),
164 sunlabel->num_partitions);
168 fprintf(stderr, _("Warning: Wrong values need to be "
169 "fixed up and will be corrected "
171 sunlabel->version = SSWAP32(SUN_LABEL_VERSION);
172 sunlabel->sanity = SSWAP32(SUN_LABEL_SANE);
173 sunlabel->num_partitions = SSWAP16(SUN_NUM_PARTITIONS);
175 ush = (unsigned short *)sunlabel;
177 while(ush < (unsigned short *)(&sunlabel->cksum))
179 sunlabel->cksum = csum;
186 partitions = SUN_NUM_PARTITIONS;
190 void create_sunlabel(void)
192 struct hd_geometry geometry;
193 unsigned long long llsectors, llcyls;
198 _("Building a new sun disklabel. Changes will remain in memory only,\n"
199 "until you decide to write them. After that, of course, the previous\n"
200 "content won't be recoverable.\n\n"));
201 #if BYTE_ORDER == LITTLE_ENDIAN
206 memset(MBRbuffer, 0, sizeof(MBRbuffer));
207 sunlabel->magic = SSWAP16(SUN_LABEL_MAGIC);
208 sunlabel->sanity = SSWAP32(SUN_LABEL_SANE);
209 sunlabel->version = SSWAP32(SUN_LABEL_VERSION);
210 sunlabel->num_partitions = SSWAP16(SUN_NUM_PARTITIONS);
212 res = blkdev_get_sectors(fd, &llsectors);
213 sec_fac = sector_size / 512;
216 if (!ioctl(fd, HDIO_GETGEO, &geometry)) {
217 heads = geometry.heads;
218 sectors = geometry.sectors;
220 llcyls = llsectors / (heads * sectors * sec_fac);
222 if (cylinders != llcyls)
225 cylinders = geometry.cylinders;
227 _("Warning: BLKGETSIZE ioctl failed on %s. "
228 "Using geometry cylinder value of %d.\n"
229 "This value may be truncated for devices"
230 " > 33.8 GB.\n"), disk_device, cylinders);
235 heads = read_int(1,1,1024,0,_("Heads"));
236 sectors = read_int(1,1,1024,0,_("Sectors/track"));
237 cylinders = read_int(1,1,65535,0,_("Cylinders"));
240 sunlabel->acyl = SSWAP16(2);
241 sunlabel->pcyl = SSWAP16(cylinders);
242 sunlabel->ncyl = SSWAP16(cylinders - 2);
243 sunlabel->rpm = SSWAP16(5400);
244 sunlabel->intrlv = SSWAP16(1);
245 sunlabel->apc = SSWAP16(0);
247 sunlabel->nhead = SSWAP16(heads);
248 sunlabel->nsect = SSWAP16(sectors);
249 sunlabel->ncyl = SSWAP16(cylinders);
251 snprintf(sunlabel->label_id, sizeof(sunlabel->label_id),
252 "Linux cyl %d alt %d hd %d sec %llu",
253 cylinders, SSWAP16(sunlabel->acyl), heads, sectors);
255 if (cylinders * heads * sectors >= 150 * 2048) {
256 ndiv = cylinders - (50 * 2048 / (heads * sectors)); /* 50M swap */
258 ndiv = cylinders * 2 / 3;
260 set_sun_partition(0, 0, ndiv * heads * sectors,
261 SUN_TAG_LINUX_NATIVE);
262 set_sun_partition(1, ndiv * heads * sectors,
263 cylinders * heads * sectors,
265 sunlabel->part_tags[1].flag |= SSWAP16(SUN_FLAG_UNMNT);
267 set_sun_partition(2, 0, cylinders * heads * sectors, SUN_TAG_BACKUP);
270 unsigned short *ush = (unsigned short *)sunlabel;
271 unsigned short csum = 0;
272 while(ush < (unsigned short *)(&sunlabel->cksum))
274 sunlabel->cksum = csum;
278 get_boot(create_empty_sun);
282 void toggle_sunflags(int i, uint16_t mask)
284 struct sun_tag_flag *p = &sunlabel->part_tags[i];
286 p->flag ^= SSWAP16(mask);
291 static void fetch_sun(uint32_t *starts, uint32_t *lens, uint32_t *start, uint32_t *stop)
293 int i, continuous = 1;
296 *stop = cylinders * heads * sectors;
298 for (i = 0; i < partitions; i++) {
299 struct sun_partition *part = &sunlabel->partitions[i];
300 struct sun_tag_flag *tag = &sunlabel->part_tags[i];
302 if (part->num_sectors &&
303 tag->tag != SSWAP16(SUN_TAG_UNASSIGNED) &&
304 tag->tag != SSWAP16(SUN_TAG_BACKUP)) {
305 starts[i] = (SSWAP32(part->start_cylinder) *
307 lens[i] = SSWAP32(part->num_sectors);
309 if (starts[i] == *start)
311 else if (starts[i] + lens[i] >= *stop)
315 /* There will be probably more gaps
316 than one, so lets check afterwards */
325 static unsigned int *verify_sun_starts;
327 static int verify_sun_cmp(int *a, int *b)
333 if (verify_sun_starts[*a] > verify_sun_starts[*b])
338 void verify_sun(void)
340 uint32_t starts[SUN_NUM_PARTITIONS], lens[SUN_NUM_PARTITIONS], start, stop;
341 int i,j,k,starto,endo;
342 int array[SUN_NUM_PARTITIONS];
344 verify_sun_starts = starts;
346 fetch_sun(starts, lens, &start, &stop);
348 for (k = 0; k < 7; k++) {
349 for (i = 0; i < SUN_NUM_PARTITIONS; i++) {
350 if (k && (lens[i] % (heads * sectors))) {
351 printf(_("Partition %d doesn't end on cylinder boundary\n"), i+1);
354 for (j = 0; j < i; j++)
356 if (starts[j] == starts[i]+lens[i]) {
357 starts[j] = starts[i]; lens[j] += lens[i];
359 } else if (starts[i] == starts[j]+lens[j]){
363 if (starts[i] < starts[j]+lens[j] &&
364 starts[j] < starts[i]+lens[i]) {
366 if (starts[j] > starto)
368 endo = starts[i]+lens[i];
369 if (starts[j]+lens[j] < endo)
370 endo = starts[j]+lens[j];
371 printf(_("Partition %d overlaps with others in "
372 "sectors %d-%d\n"), i+1, starto, endo);
379 for (i = 0; i < SUN_NUM_PARTITIONS; i++) {
385 qsort(array,SIZE(array),sizeof(array[0]),
386 (int (*)(const void *,const void *)) verify_sun_cmp);
387 if (array[0] == -1) {
388 printf(_("No partitions defined\n"));
391 stop = cylinders * heads * sectors;
392 if (starts[array[0]])
393 printf(_("Unused gap - sectors 0-%d\n"), starts[array[0]]);
394 for (i = 0; i < 7 && array[i+1] != -1; i++) {
395 printf(_("Unused gap - sectors %d-%d\n"),
396 (starts[array[i]] + lens[array[i]]),
399 start = (starts[array[i]] + lens[array[i]]);
401 printf(_("Unused gap - sectors %d-%d\n"), start, stop);
404 void add_sun_partition(int n, int sys)
406 uint32_t starts[SUN_NUM_PARTITIONS], lens[SUN_NUM_PARTITIONS];
407 struct sun_partition *part = &sunlabel->partitions[n];
408 struct sun_tag_flag *tag = &sunlabel->part_tags[n];
409 uint32_t start, stop, stop2;
415 if (part->num_sectors && tag->tag != SSWAP16(SUN_TAG_UNASSIGNED)) {
416 printf(_("Partition %d is already defined. Delete "
417 "it before re-adding it.\n"), n + 1);
421 fetch_sun(starts, lens, &start, &stop);
426 printf(_("Other partitions already cover the whole disk.\nDelete "
427 "some/shrink them before retry.\n"));
431 snprintf(mesg, sizeof(mesg), _("First %s"), str_units(SINGULAR));
434 first = read_int(0, 0, 0, 0, mesg);
436 first = read_int(scround(start), scround(stop)+1,
437 scround(stop), 0, mesg);
438 if (display_in_cyl_units)
439 first *= units_per_sector;
441 /* Starting sector has to be properly aligned */
442 int cs = heads * sectors;
448 if (n == 2 && first != 0)
450 It is highly recommended that the third partition covers the whole disk\n\
451 and is of type `Whole disk'\n"));
452 /* ewt asks to add: "don't start a partition at cyl 0"
453 However, edmundo@rano.demon.co.uk writes:
454 "In addition to having a Sun partition table, to be able to
455 boot from the disc, the first partition, /dev/sdX1, must
456 start at cylinder 0. This means that /dev/sdX1 contains
457 the partition table and the boot block, as these are the
458 first two sectors of the disc. Therefore you must be
459 careful what you use /dev/sdX1 for. In particular, you must
460 not use a partition starting at cylinder 0 for Linux swap,
461 as that would overwrite the partition table and the boot
462 block. You may, however, use such a partition for a UFS
463 or EXT2 file system, as these file systems leave the first
464 1024 bytes undisturbed. */
465 /* On the other hand, one should not use partitions
466 starting at block 0 in an md, or the label will
468 for (i = 0; i < partitions; i++)
469 if (lens[i] && starts[i] <= first
470 && starts[i] + lens[i] > first)
472 if (i < partitions && !whole_disk) {
473 if (n == 2 && !first) {
477 printf(_("Sector %d is already allocated\n"), first);
481 stop = cylinders * heads * sectors; /* ancient */
483 for (i = 0; i < partitions; i++) {
484 if (starts[i] > first && starts[i] < stop)
487 snprintf(mesg, sizeof(mesg),
488 _("Last %s or +size or +sizeM or +sizeK"),
489 str_units(SINGULAR));
491 last = read_int(scround(stop2), scround(stop2), scround(stop2),
493 else if (n == 2 && !first)
494 last = read_int(scround(first), scround(stop2), scround(stop2),
495 scround(first), mesg);
497 last = read_int(scround(first), scround(stop), scround(stop),
498 scround(first), mesg);
499 if (display_in_cyl_units)
500 last *= units_per_sector;
501 if (n == 2 && !first) {
505 } else if (last > stop) {
507 _("You haven't covered the whole disk with the 3rd partition, but your value\n"
508 "%d %s covers some other partition. Your entry has been changed\n"
510 scround(last), str_units(SINGULAR),
511 scround(stop), str_units(SINGULAR));
514 } else if (!whole_disk && last > stop)
518 sys = SUN_TAG_BACKUP;
520 set_sun_partition(n, first, last, sys);
523 void sun_delete_partition(int i)
525 struct sun_partition *part = &sunlabel->partitions[i];
526 struct sun_tag_flag *tag = &sunlabel->part_tags[i];
530 tag->tag == SSWAP16(SUN_TAG_BACKUP) &&
531 !part->start_cylinder &&
532 (nsec = SSWAP32(part->num_sectors))
533 == heads * sectors * cylinders)
534 printf(_("If you want to maintain SunOS/Solaris compatibility, "
535 "consider leaving this\n"
536 "partition as Whole disk (5), starting at 0, with %u "
538 tag->tag = SSWAP16(SUN_TAG_UNASSIGNED);
539 part->num_sectors = 0;
542 int sun_change_sysid(int i, uint16_t sys)
544 struct sun_partition *part = &sunlabel->partitions[i];
545 struct sun_tag_flag *tag = &sunlabel->part_tags[i];
547 if (sys == SUN_TAG_LINUX_SWAP && !part->start_cylinder) {
549 _("It is highly recommended that the partition at offset 0\n"
550 "is UFS, EXT2FS filesystem or SunOS swap. Putting Linux swap\n"
551 "there may destroy your partition table and bootblock.\n"
552 "Type YES if you're very sure you would like that partition\n"
553 "tagged with 82 (Linux swap): "));
554 if (strcmp (line_ptr, _("YES\n")))
559 case SUN_TAG_LINUX_SWAP:
560 /* swaps are not mountable by default */
561 tag->flag |= SSWAP16(SUN_FLAG_UNMNT);
564 /* assume other types are mountable;
565 user can change it anyway */
566 tag->flag &= ~SSWAP16(SUN_FLAG_UNMNT);
569 tag->tag = SSWAP16(sys);
573 void sun_list_table(int xtra)
578 w = strlen(disk_device);
581 _("\nDisk %s (Sun disk label): %d heads, %llu sectors, %d rpm\n"
582 "%d cylinders, %d alternate cylinders, %d physical cylinders\n"
583 "%d extra sects/cyl, interleave %d:1\n"
586 "Units = %s of %d * 512 bytes\n\n"),
587 disk_device, heads, sectors, SSWAP16(sunlabel->rpm),
588 cylinders, SSWAP16(sunlabel->acyl),
589 SSWAP16(sunlabel->pcyl),
590 SSWAP16(sunlabel->apc),
591 SSWAP16(sunlabel->intrlv),
594 str_units(PLURAL), units_per_sector);
597 _("\nDisk %s (Sun disk label): %d heads, %llu sectors, %d cylinders\n"
598 "Units = %s of %d * 512 bytes\n\n"),
599 disk_device, heads, sectors, cylinders,
600 str_units(PLURAL), units_per_sector);
602 printf(_("%*s Flag Start End Blocks Id System\n"),
604 for (i = 0 ; i < partitions; i++) {
605 struct sun_partition *part = &sunlabel->partitions[i];
606 struct sun_tag_flag *tag = &sunlabel->part_tags[i];
608 if (part->num_sectors) {
609 uint32_t start = SSWAP32(part->start_cylinder) * heads * sectors;
610 uint32_t len = SSWAP32(part->num_sectors);
612 "%s %c%c %9ld %9ld %9ld%c %2x %s\n",
613 /* device */ partname(disk_device, i+1, w),
614 /* flags */ (tag->flag & SSWAP16(SUN_FLAG_UNMNT)) ? 'u' : ' ',
615 (tag->flag & SSWAP16(SUN_FLAG_RONLY)) ? 'r' : ' ',
616 /* start */ (long) scround(start),
617 /* end */ (long) scround(start+len),
618 /* odd flag on end */ (long) len / 2, len & 1 ? '+' : ' ',
619 /* type id */ SSWAP16(tag->tag),
620 /* type name */ (type = partition_type(SSWAP16(tag->tag)))
621 ? type : _("Unknown"));
626 void sun_set_alt_cyl(void)
629 SSWAP16(read_int(0,SSWAP16(sunlabel->acyl), 65535, 0,
630 _("Number of alternate cylinders")));
633 void sun_set_ncyl(int cyl)
635 sunlabel->ncyl = SSWAP16(cyl);
638 void sun_set_xcyl(void)
641 SSWAP16(read_int(0, SSWAP16(sunlabel->apc), sectors, 0,
642 _("Extra sectors per cylinder")));
645 void sun_set_ilfact(void)
648 SSWAP16(read_int(1, SSWAP16(sunlabel->intrlv), 32, 0,
649 _("Interleave factor")));
652 void sun_set_rspeed(void)
655 SSWAP16(read_int(1, SSWAP16(sunlabel->rpm), 100000, 0,
656 _("Rotation speed (rpm)")));
659 void sun_set_pcylcount(void)
662 SSWAP16(read_int(0, SSWAP16(sunlabel->pcyl), 65535, 0,
663 _("Number of physical cylinders")));
666 void sun_write_table(void)
668 unsigned short *ush = (unsigned short *)sunlabel;
669 unsigned short csum = 0;
671 while(ush < (unsigned short *)(&sunlabel->cksum))
673 sunlabel->cksum = csum;
674 if (lseek(fd, 0, SEEK_SET) < 0)
675 fatal(unable_to_seek);
676 if (write(fd, sunlabel, SECTOR_SIZE) != SECTOR_SIZE)
677 fatal(unable_to_write);
680 int sun_get_sysid(int i)
682 return SSWAP16(sunlabel->part_tags[i].tag);