Upload Tizen:Base source
[framework/base/util-linux-ng.git] / fdisk / fdisksunlabel.c
1 /*
2  * fdisksunlabel.c
3  *
4  * I think this is mostly, or entirely, due to
5  *      Jakub Jelinek (jj@sunsite.mff.cuni.cz), July 1996
6  *
7  * Merged with fdisk for other architectures, aeb, June 1998.
8  *
9  * Sat Mar 20 EST 1999 Arnaldo Carvalho de Melo <acme@conectiva.com.br>
10  *      Internationalization
11  */
12
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 */
20
21 #include "nls.h"
22 #include "blkdev.h"
23
24 #include <endian.h>
25 #ifdef HAVE_SCSI_SCSI_H
26 #define u_char  unsigned char
27 #include <scsi/scsi.h>          /* SCSI_IOCTL_GET_IDLUN */
28 #undef u_char
29 #endif
30 #ifdef HAVE_LINUX_MAJOR_H
31 #include <linux/major.h>        /* FLOPPY_MAJOR */
32 #endif
33
34 #include "common.h"
35 #include "fdisk.h"
36 #include "fdisksunlabel.h"
37
38 static int     other_endian = 0;
39 static int     scsi_disk = 0;
40 static int     floppy = 0;
41
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")},
59         { 0, NULL }
60 };
61
62 static inline unsigned short __swap16(unsigned short x) {
63         return (((uint16_t)(x) & 0xFF) << 8) | (((uint16_t)(x) & 0xFF00) >> 8);
64 }
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);
67 }
68
69 #define SSWAP16(x) (other_endian ? __swap16(x) \
70                                  : (uint16_t)(x))
71 #define SSWAP32(x) (other_endian ? __swap32(x) \
72                                  : (uint32_t)(x))
73
74 #ifndef FLOPPY_MAJOR
75 #define FLOPPY_MAJOR 2
76 #endif
77 #ifndef IDE0_MAJOR
78 #define IDE0_MAJOR 3
79 #endif
80 #ifndef IDE1_MAJOR
81 #define IDE1_MAJOR 22
82 #endif
83 void guess_device_type(int fd)
84 {
85         struct stat bootstat;
86
87         if (fstat (fd, &bootstat) < 0) {
88                 scsi_disk = 0;
89                 floppy = 0;
90         } else if (S_ISBLK(bootstat.st_mode)
91                    && (major(bootstat.st_rdev) == IDE0_MAJOR ||
92                        major(bootstat.st_rdev) == IDE1_MAJOR)) {
93                 scsi_disk = 0;
94                 floppy = 0;
95         } else if (S_ISBLK(bootstat.st_mode)
96                    && major(bootstat.st_rdev) == FLOPPY_MAJOR) {
97                 scsi_disk = 0;
98                 floppy = 1;
99         } else {
100                 scsi_disk = 1;
101                 floppy = 0;
102         }
103 }
104
105 static void set_sun_partition(int i, uint32_t start, uint32_t stop, uint16_t sysid)
106 {
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);
113         set_changed(i);
114 }
115
116 void sun_nolabel(void)
117 {
118         sun_label = 0;
119         sunlabel->magic = 0;
120         partitions = 4;
121 }
122
123 int check_sun_label(void)
124 {
125         unsigned short *ush;
126         int csum;
127
128         if (sunlabel->magic != SUN_LABEL_MAGIC &&
129             sunlabel->magic != SUN_LABEL_MAGIC_SWAPPED) {
130                 sun_label = 0;
131                 other_endian = 0;
132                 return 0;
133         }
134         other_endian = (sunlabel->magic == SUN_LABEL_MAGIC_SWAPPED);
135
136         ush = ((unsigned short *) (sunlabel + 1)) - 1;
137         for (csum = 0; ush >= (unsigned short *)sunlabel;)
138                 csum ^= *ush--;
139
140         if (csum) {
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"));
145         } else {
146                 int need_fixing = 0;
147
148                 heads = SSWAP16(sunlabel->nhead);
149                 cylinders = SSWAP16(sunlabel->ncyl);
150                 sectors = SSWAP16(sunlabel->nsect);
151
152                 if (sunlabel->version != SSWAP32(SUN_LABEL_VERSION)) {
153                         fprintf(stderr,_("Detected sun disklabel with wrong version [0x%08x].\n"),
154                                 sunlabel->version);
155                         need_fixing = 1;
156                 }
157                 if (sunlabel->sanity != SSWAP32(SUN_LABEL_SANE)) {
158                         fprintf(stderr,_("Detected sun disklabel with wrong sanity [0x%08x].\n"),
159                                 sunlabel->sanity);
160                         need_fixing = 1;
161                 }
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);
165                         need_fixing = 1;
166                 }
167                 if (need_fixing) {
168                         fprintf(stderr, _("Warning: Wrong values need to be "
169                                           "fixed up and will be corrected "
170                                           "by w(rite)\n"));
171                         sunlabel->version = SSWAP32(SUN_LABEL_VERSION);
172                         sunlabel->sanity = SSWAP32(SUN_LABEL_SANE);
173                         sunlabel->num_partitions = SSWAP16(SUN_NUM_PARTITIONS);
174
175                         ush = (unsigned short *)sunlabel;
176                         csum = 0;
177                         while(ush < (unsigned short *)(&sunlabel->cksum))
178                                 csum ^= *ush++;
179                         sunlabel->cksum = csum;
180
181                         set_changed(0);
182                 }
183         }
184         update_units();
185         sun_label = 1;
186         partitions = SUN_NUM_PARTITIONS;
187         return 1;
188 }
189
190 void create_sunlabel(void)
191 {
192         struct hd_geometry geometry;
193         unsigned long long llsectors, llcyls;
194         unsigned int ndiv;
195         int res, sec_fac;
196
197         fprintf(stderr,
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
202         other_endian = 1;
203 #else
204         other_endian = 0;
205 #endif
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);
211
212         res = blkdev_get_sectors(fd, &llsectors);
213         sec_fac = sector_size / 512;
214
215 #ifdef HDIO_GETGEO
216         if (!ioctl(fd, HDIO_GETGEO, &geometry)) {
217                 heads = geometry.heads;
218                 sectors = geometry.sectors;
219                 if (res == 0) {
220                         llcyls = llsectors / (heads * sectors * sec_fac);
221                         cylinders = llcyls;
222                         if (cylinders != llcyls)
223                                 cylinders = ~0;
224                 } else {
225                         cylinders = geometry.cylinders;
226                         fprintf(stderr,
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);
231                 }
232         } else
233 #endif
234         {
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"));
238         }
239
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);
246
247         sunlabel->nhead = SSWAP16(heads);
248         sunlabel->nsect = SSWAP16(sectors);
249         sunlabel->ncyl = SSWAP16(cylinders);
250
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);
254
255         if (cylinders * heads * sectors >= 150 * 2048) {
256                 ndiv = cylinders - (50 * 2048 / (heads * sectors)); /* 50M swap */
257         } else
258                 ndiv = cylinders * 2 / 3;
259
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,
264                           SUN_TAG_LINUX_SWAP);
265         sunlabel->part_tags[1].flag |= SSWAP16(SUN_FLAG_UNMNT);
266
267         set_sun_partition(2, 0, cylinders * heads * sectors, SUN_TAG_BACKUP);
268
269         {
270                 unsigned short *ush = (unsigned short *)sunlabel;
271                 unsigned short csum = 0;
272                 while(ush < (unsigned short *)(&sunlabel->cksum))
273                         csum ^= *ush++;
274                 sunlabel->cksum = csum;
275         }
276
277         set_all_unchanged();
278         get_boot(create_empty_sun);
279         set_changed(0);
280 }
281
282 void toggle_sunflags(int i, uint16_t mask)
283 {
284         struct sun_tag_flag *p = &sunlabel->part_tags[i];
285
286         p->flag ^= SSWAP16(mask);
287
288         set_changed(i);
289 }
290
291 static void fetch_sun(uint32_t *starts, uint32_t *lens, uint32_t *start, uint32_t *stop)
292 {
293         int i, continuous = 1;
294
295         *start = 0;
296         *stop = cylinders * heads * sectors;
297
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];
301
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) *
306                                      heads * sectors);
307                         lens[i] = SSWAP32(part->num_sectors);
308                         if (continuous) {
309                                 if (starts[i] == *start)
310                                         *start += lens[i];
311                                 else if (starts[i] + lens[i] >= *stop)
312                                         *stop = starts[i];
313                                 else
314                                         continuous = 0;
315                                         /* There will be probably more gaps
316                                           than one, so lets check afterwards */
317                         }
318                 } else {
319                         starts[i] = 0;
320                         lens[i] = 0;
321                 }
322         }
323 }
324
325 static unsigned int *verify_sun_starts;
326
327 static int verify_sun_cmp(int *a, int *b)
328 {
329     if (*a == -1)
330             return 1;
331     if (*b == -1)
332             return -1;
333     if (verify_sun_starts[*a] > verify_sun_starts[*b])
334             return 1;
335     return -1;
336 }
337
338 void verify_sun(void)
339 {
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];
343
344     verify_sun_starts = starts;
345
346     fetch_sun(starts, lens, &start, &stop);
347
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);
352             }
353             if (lens[i]) {
354                 for (j = 0; j < i; j++)
355                     if (lens[j]) {
356                         if (starts[j] == starts[i]+lens[i]) {
357                             starts[j] = starts[i]; lens[j] += lens[i];
358                             lens[i] = 0;
359                         } else if (starts[i] == starts[j]+lens[j]){
360                             lens[j] += lens[i];
361                             lens[i] = 0;
362                         } else if (!k) {
363                             if (starts[i] < starts[j]+lens[j] &&
364                                 starts[j] < starts[i]+lens[i]) {
365                                 starto = starts[i];
366                                 if (starts[j] > starto)
367                                         starto = starts[j];
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);
373                             }
374                         }
375                     }
376             }
377         }
378     }
379     for (i = 0; i < SUN_NUM_PARTITIONS; i++) {
380         if (lens[i])
381             array[i] = i;
382         else
383             array[i] = -1;
384     }
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"));
389         return;
390     }
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]]),
397                starts[array[i+1]]);
398     }
399     start = (starts[array[i]] + lens[array[i]]);
400     if (start < stop)
401         printf(_("Unused gap - sectors %d-%d\n"), start, stop);
402 }
403
404 void add_sun_partition(int n, int sys)
405 {
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;
410         int whole_disk = 0;
411                 
412         char mesg[256];
413         int i, first, last;
414
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);
418                 return;
419         }
420         
421         fetch_sun(starts, lens, &start, &stop);
422         if (stop <= start) {
423                 if (n == 2)
424                         whole_disk = 1;
425                 else {
426                         printf(_("Other partitions already cover the whole disk.\nDelete "
427                                "some/shrink them before retry.\n"));
428                         return;
429                 }
430         }
431         snprintf(mesg, sizeof(mesg), _("First %s"), str_units(SINGULAR));
432         for (;;) {
433                 if (whole_disk)
434                         first = read_int(0, 0, 0, 0, mesg);
435                 else
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;
440                 else {
441                         /* Starting sector has to be properly aligned */
442                         int cs = heads * sectors;
443                         int x = first % cs;
444
445                         if (x)
446                                 first += cs - x;
447                 }
448                 if (n == 2 && first != 0)
449                         printf (_("\
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
467                    be trashed. */
468                 for (i = 0; i < partitions; i++)
469                         if (lens[i] && starts[i] <= first
470                                     && starts[i] + lens[i] > first)
471                                 break;
472                 if (i < partitions && !whole_disk) {
473                         if (n == 2 && !first) {
474                             whole_disk = 1;
475                             break;
476                         }
477                         printf(_("Sector %d is already allocated\n"), first);
478                 } else
479                         break;
480         }
481         stop = cylinders * heads * sectors;     /* ancient */
482         stop2 = stop;
483         for (i = 0; i < partitions; i++) {
484                 if (starts[i] > first && starts[i] < stop)
485                         stop = starts[i];
486         }
487         snprintf(mesg, sizeof(mesg),
488                  _("Last %s or +size or +sizeM or +sizeK"),
489                  str_units(SINGULAR));
490         if (whole_disk)
491                 last = read_int(scround(stop2), scround(stop2), scround(stop2),
492                                 0, mesg);
493         else if (n == 2 && !first)
494                 last = read_int(scround(first), scround(stop2), scround(stop2),
495                                 scround(first), mesg);
496         else
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) {
502                 if (last >= stop2) {
503                     whole_disk = 1;
504                     last = stop2;
505                 } else if (last > stop) {
506                     printf (
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"
509      "to %d %s\n"),
510                         scround(last), str_units(SINGULAR),
511                         scround(stop), str_units(SINGULAR));
512                     last = stop;
513                 }
514         } else if (!whole_disk && last > stop)
515                 last = stop;
516
517         if (whole_disk)
518                 sys = SUN_TAG_BACKUP;
519
520         set_sun_partition(n, first, last, sys);
521 }
522
523 void sun_delete_partition(int i)
524 {
525         struct sun_partition *part = &sunlabel->partitions[i];
526         struct sun_tag_flag *tag = &sunlabel->part_tags[i];
527         unsigned int nsec;
528
529         if (i == 2 &&
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 "
537                        "sectors\n"), nsec);
538         tag->tag = SSWAP16(SUN_TAG_UNASSIGNED);
539         part->num_sectors = 0;
540 }
541
542 int sun_change_sysid(int i, uint16_t sys)
543 {
544         struct sun_partition *part = &sunlabel->partitions[i];
545         struct sun_tag_flag *tag = &sunlabel->part_tags[i];
546
547         if (sys == SUN_TAG_LINUX_SWAP && !part->start_cylinder) {
548             read_chars(
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")))
555                     return 0;
556         }
557         switch (sys) {
558         case SUN_TAG_SWAP:
559         case SUN_TAG_LINUX_SWAP:
560                 /* swaps are not mountable by default */
561                 tag->flag |= SSWAP16(SUN_FLAG_UNMNT);
562                 break;
563         default:
564                 /* assume other types are mountable;
565                    user can change it anyway */
566                 tag->flag &= ~SSWAP16(SUN_FLAG_UNMNT);
567                 break;
568         }
569         tag->tag = SSWAP16(sys);
570         return 1;
571 }
572
573 void sun_list_table(int xtra)
574 {
575         int i, w;
576         char *type;
577
578         w = strlen(disk_device);
579         if (xtra)
580                 printf(
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"
584                 "Label ID: %s\n"
585                 "Volume ID: %s\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),
592                        sunlabel->label_id,
593                        sunlabel->volume_id,
594                        str_units(PLURAL), units_per_sector);
595         else
596                 printf(
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);
601
602         printf(_("%*s Flag    Start       End    Blocks   Id  System\n"),
603                w + 1, _("Device"));
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];
607
608                 if (part->num_sectors) {
609                         uint32_t start = SSWAP32(part->start_cylinder) * heads * sectors;
610                         uint32_t len = SSWAP32(part->num_sectors);
611                         printf(
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"));
622                 }
623         }
624 }
625
626 void sun_set_alt_cyl(void)
627 {
628         sunlabel->acyl =
629                 SSWAP16(read_int(0,SSWAP16(sunlabel->acyl), 65535, 0,
630                                  _("Number of alternate cylinders")));
631 }
632
633 void sun_set_ncyl(int cyl)
634 {
635         sunlabel->ncyl = SSWAP16(cyl);
636 }
637
638 void sun_set_xcyl(void)
639 {
640         sunlabel->apc =
641                 SSWAP16(read_int(0, SSWAP16(sunlabel->apc), sectors, 0,
642                                  _("Extra sectors per cylinder")));
643 }
644
645 void sun_set_ilfact(void)
646 {
647         sunlabel->intrlv =
648                 SSWAP16(read_int(1, SSWAP16(sunlabel->intrlv), 32, 0,
649                                  _("Interleave factor")));
650 }
651
652 void sun_set_rspeed(void)
653 {
654         sunlabel->rpm =
655                 SSWAP16(read_int(1, SSWAP16(sunlabel->rpm), 100000, 0,
656                                  _("Rotation speed (rpm)")));
657 }
658
659 void sun_set_pcylcount(void)
660 {
661         sunlabel->pcyl =
662                 SSWAP16(read_int(0, SSWAP16(sunlabel->pcyl), 65535, 0,
663                                  _("Number of physical cylinders")));
664 }
665
666 void sun_write_table(void)
667 {
668         unsigned short *ush = (unsigned short *)sunlabel;
669         unsigned short csum = 0;
670
671         while(ush < (unsigned short *)(&sunlabel->cksum))
672                 csum ^= *ush++;
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);
678 }
679
680 int sun_get_sysid(int i)
681 {
682         return SSWAP16(sunlabel->part_tags[i].tag);
683 }