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