(root)/
util-linux-2.39/
libfdisk/
src/
label.c
       1  
       2  #include "fdiskP.h"
       3  
       4  
       5  /**
       6   * SECTION: label
       7   * @title: Label
       8   * @short_description: disk label (PT) specific data and functions
       9   *
      10   * The fdisk_new_context() initializes all label drivers, and allocate
      11   * per-label specific data struct. This concept can be used to store label specific
      12   * settings to the label driver independently on the currently active label
      13   * driver. Note that label struct cannot be deallocated, so there is no
      14   * reference counting for fdisk_label objects. All is destroyed by
      15   * fdisk_unref_context() only.
      16   *
      17   * Anyway, all label drives share in-memory first sector. The function
      18   * fdisk_create_disklabel() overwrites this in-memory sector. But it's possible that
      19   * label driver also uses another buffers, for example GPT reads more sectors
      20   * from the device.
      21   *
      22   * All label operations are in-memory only, except fdisk_write_disklabel().
      23   *
      24   * All functions that use "struct fdisk_context" rather than "struct
      25   * fdisk_label" use the currently active label driver.
      26   */
      27  
      28  
      29  int fdisk_probe_labels(struct fdisk_context *cxt)
      30  {
      31  	size_t i;
      32  
      33  	cxt->label = NULL;
      34  
      35  	for (i = 0; i < cxt->nlabels; i++) {
      36  		struct fdisk_label *lb = cxt->labels[i];
      37  		struct fdisk_label *org = fdisk_get_label(cxt, NULL);
      38  		int rc;
      39  
      40  		if (!lb->op->probe)
      41  			continue;
      42  		if (lb->disabled) {
      43  			DBG(CXT, ul_debugobj(cxt, "%s: disabled -- ignore", lb->name));
      44  			continue;
      45  		}
      46  		DBG(CXT, ul_debugobj(cxt, "probing for %s", lb->name));
      47  
      48  		cxt->label = lb;
      49  		rc = lb->op->probe(cxt);
      50  		cxt->label = org;
      51  
      52  		if (rc != 1) {
      53  			if (lb->op->deinit)
      54  				lb->op->deinit(lb);	/* for sure */
      55  			continue;
      56  		}
      57  
      58  		__fdisk_switch_label(cxt, lb);
      59  		return 0;
      60  	}
      61  
      62  	DBG(CXT, ul_debugobj(cxt, "no label found"));
      63  	return 1; /* not found */
      64  }
      65  
      66  /**
      67   * fdisk_label_get_name:
      68   * @lb: label
      69   *
      70   * Returns: label name
      71   */
      72  const char *fdisk_label_get_name(const struct fdisk_label *lb)
      73  {
      74  	return lb ? lb->name : NULL;
      75  }
      76  
      77  /**
      78   * fdisk_label_is_labeltype:
      79   * @lb: label
      80   *
      81   * Returns: FDISK_DISKLABEL_*.
      82   */
      83  int fdisk_label_get_type(const struct fdisk_label *lb)
      84  {
      85  	return lb->id;
      86  }
      87  
      88  /**
      89   * fdisk_label_require_geometry:
      90   * @lb: label
      91   *
      92   * Returns: 1 if label requires CHS geometry
      93   */
      94  int fdisk_label_require_geometry(const struct fdisk_label *lb)
      95  {
      96  	assert(lb);
      97  
      98  	return lb->flags & FDISK_LABEL_FL_REQUIRE_GEOMETRY ? 1 : 0;
      99  }
     100  
     101  /**
     102   * fdisk_label_get_fields_ids
     103   * @lb: label (or NULL for the current label)
     104   * @cxt: context
     105   * @ids: returns allocated array with FDISK_FIELD_* IDs
     106   * @nids: returns number of items in fields
     107   *
     108   * This function returns the default fields for the label.
     109   *
     110   * Note that the set of the default fields depends on fdisk_enable_details()
     111   * function. If the details are enabled then this function usually returns more
     112   * fields.
     113   *
     114   * Returns: 0 on success, otherwise, a corresponding error.
     115   */
     116  int fdisk_label_get_fields_ids(
     117  		const struct fdisk_label *lb,
     118  		struct fdisk_context *cxt,
     119  		int **ids, size_t *nids)
     120  {
     121  	size_t i, n;
     122  	int *c;
     123  
     124  	if (!cxt || (!lb && !cxt->label))
     125  		return -EINVAL;
     126  
     127  	lb = cxt->label;
     128  	if (!lb->fields || !lb->nfields)
     129  		return -ENOSYS;
     130  	c = calloc(lb->nfields, sizeof(int));
     131  	if (!c)
     132  		return -ENOMEM;
     133  	for (n = 0, i = 0; i < lb->nfields; i++) {
     134  		int id = lb->fields[i].id;
     135  
     136  		if ((fdisk_is_details(cxt) &&
     137  				(lb->fields[i].flags & FDISK_FIELDFL_EYECANDY))
     138  		     || (!fdisk_is_details(cxt) &&
     139  				(lb->fields[i].flags & FDISK_FIELDFL_DETAIL))
     140  		     || (id == FDISK_FIELD_SECTORS &&
     141  			         fdisk_use_cylinders(cxt))
     142  		     || (id == FDISK_FIELD_CYLINDERS &&
     143  			         !fdisk_use_cylinders(cxt)))
     144  			continue;
     145  
     146  		c[n++] = id;
     147  	}
     148  	if (ids)
     149  		*ids = c;
     150  	else
     151  		free(c);
     152  	if (nids)
     153  		*nids = n;
     154  	return 0;
     155  }
     156  
     157  /**
     158   * fdisk_label_get_fields_ids_all
     159   * @lb: label (or NULL for the current label)
     160   * @cxt: context
     161   * @ids: returns allocated array with FDISK_FIELD_* IDs
     162   * @nids: returns number of items in fields
     163   *
     164   * This function returns all fields for the label.
     165   *
     166   * Returns: 0 on success, otherwise, a corresponding error.
     167   */
     168  int fdisk_label_get_fields_ids_all(
     169  		const struct fdisk_label *lb,
     170  		struct fdisk_context *cxt,
     171  		int **ids, size_t *nids)
     172  {
     173  	size_t i, n;
     174  	int *c;
     175  
     176  	if (!cxt || (!lb && !cxt->label))
     177  		return -EINVAL;
     178  
     179  	lb = cxt->label;
     180  	if (!lb->fields || !lb->nfields)
     181  		return -ENOSYS;
     182  	c = calloc(lb->nfields, sizeof(int));
     183  	if (!c)
     184  		return -ENOMEM;
     185  	for (n = 0, i = 0; i < lb->nfields; i++)
     186  		c[n++] = lb->fields[i].id;
     187  	if (ids)
     188  		*ids = c;
     189  	else
     190  		free(c);
     191  	if (nids)
     192  		*nids = n;
     193  	return 0;
     194  }
     195  
     196  /**
     197   * fdisk_label_get_field:
     198   * @lb: label
     199   * @id: FDISK_FIELD_*
     200   *
     201   * The field struct describes data stored in struct fdisk_partition. The info
     202   * about data is usable for example to generate human readable output (e.g.
     203   * fdisk 'p'rint command). See fdisk_partition_to_string() and fdisk code.
     204   *
     205   * Returns: pointer to static instance of the field.
     206   */
     207  const struct fdisk_field *fdisk_label_get_field(const struct fdisk_label *lb, int id)
     208  {
     209  	size_t i;
     210  
     211  	assert(lb);
     212  	assert(id > 0);
     213  
     214  	for (i = 0; i < lb->nfields; i++) {
     215  		if (lb->fields[i].id == id)
     216  			return &lb->fields[i];
     217  	}
     218  
     219  	return NULL;
     220  }
     221  
     222  /**
     223   * fdisk_label_get_field_by_name
     224   * @lb: label
     225   * @name: field name
     226   *
     227   * Returns: pointer to static instance of the field.
     228   */
     229  const struct fdisk_field *fdisk_label_get_field_by_name(
     230  				const struct fdisk_label *lb,
     231  				const char *name)
     232  {
     233  	size_t i;
     234  
     235  	assert(lb);
     236  	assert(name);
     237  
     238  	for (i = 0; i < lb->nfields; i++) {
     239  		if (lb->fields[i].name && strcasecmp(lb->fields[i].name, name) == 0)
     240  			return &lb->fields[i];
     241  	}
     242  
     243  	return NULL;
     244  }
     245  
     246  /**
     247   * fdisk_write_disklabel:
     248   * @cxt: fdisk context
     249   *
     250   * This function wipes the device (if enabled by fdisk_enable_wipe()) and then
     251   * it writes in-memory changes to disk. Be careful!
     252   *
     253   * Returns: 0 on success, otherwise, a corresponding error.
     254   */
     255  int fdisk_write_disklabel(struct fdisk_context *cxt)
     256  {
     257  	if (!cxt || !cxt->label || cxt->readonly)
     258  		return -EINVAL;
     259  	if (!cxt->label->op->write)
     260  		return -ENOSYS;
     261  
     262  	fdisk_do_wipe(cxt);
     263  	return cxt->label->op->write(cxt);
     264  }
     265  
     266  /**
     267   * fdisk_verify_disklabel:
     268   * @cxt: fdisk context
     269   *
     270   * Verifies the partition table.
     271   *
     272   * Returns: 0 on success, <1 runtime or option errors, >0 number of detected issues
     273   */
     274  int fdisk_verify_disklabel(struct fdisk_context *cxt)
     275  {
     276  	if (!cxt || !cxt->label)
     277  		return -EINVAL;
     278  	if (!cxt->label->op->verify)
     279  		return -ENOSYS;
     280  	if (fdisk_missing_geometry(cxt))
     281  		return -EINVAL;
     282  
     283  	return cxt->label->op->verify(cxt);
     284  }
     285  
     286  /**
     287   * fdisk_list_disklabel:
     288   * @cxt: fdisk context
     289   *
     290   * Lists details about disklabel, but no partitions.
     291   *
     292   * This function is based on fdisk_get_disklabel_item() and prints all label
     293   * specific information by ASK interface (FDISK_ASKTYPE_INFO, aka fdisk_info()).
     294   * The function requires enabled "details" by fdisk_enable_details().
     295   *
     296   * It's recommended to use fdisk_get_disklabel_item() if you need better
     297   * control on output and formatting.
     298   *
     299   * Returns: 0 on success, otherwise, a corresponding error.
     300   */
     301  int fdisk_list_disklabel(struct fdisk_context *cxt)
     302  {
     303  	int id = 0, rc = 0;
     304  	struct fdisk_labelitem item = { .id = id };
     305  
     306  	if (!cxt || !cxt->label)
     307  		return -EINVAL;
     308  
     309  	if (!cxt->display_details)
     310  		return 0;
     311  
     312  	/* List all label items */
     313  	do {
     314  		/* rc: < 0 error, 0 success, 1 unknown item, 2 out of range */
     315  		rc = fdisk_get_disklabel_item(cxt, id++, &item);
     316  		if (rc != 0)
     317  			continue;
     318  		switch (item.type) {
     319  		case 'j':
     320  			fdisk_info(cxt, "%s: %ju", item.name, item.data.num64);
     321  			break;
     322  		case 's':
     323  			if (item.data.str && item.name)
     324  				fdisk_info(cxt, "%s: %s", item.name, item.data.str);
     325  			break;
     326  		}
     327  		fdisk_reset_labelitem(&item);
     328  	} while (rc == 0 || rc == 1);
     329  
     330  	return rc < 0 ? rc : 0;
     331  }
     332  
     333  /**
     334   * fdisk_create_disklabel:
     335   * @cxt: fdisk context
     336   * @name: label name
     337   *
     338   * Creates a new disk label of type @name. If @name is NULL, then it will
     339   * create a default system label type, either SUN or DOS. The function
     340   * automatically switches the current label driver to @name. The function
     341   * fdisk_get_label() returns the current label driver.
     342   *
     343   * The function modifies in-memory data only.
     344   *
     345   * Returns: 0 on success, otherwise, a corresponding error.
     346   */
     347  int fdisk_create_disklabel(struct fdisk_context *cxt, const char *name)
     348  {
     349  	int haslabel = 0;
     350  	struct fdisk_label *lb;
     351  
     352  	if (!cxt)
     353  		return -EINVAL;
     354  
     355  	if (!name) { /* use default label creation */
     356  #ifdef __sparc__
     357  		name = "sun";
     358  #else
     359  		name = "dos";
     360  #endif
     361  	}
     362  
     363  	if (cxt->label) {
     364  		fdisk_deinit_label(cxt->label);
     365  		haslabel = 1;
     366  	}
     367  
     368  	lb = fdisk_get_label(cxt, name);
     369  	if (!lb || lb->disabled)
     370  		return -EINVAL;
     371  
     372  	if (!haslabel || (lb && cxt->label != lb))
     373  		fdisk_check_collisions(cxt);
     374  
     375  	if (!lb->op->create)
     376  		return -ENOSYS;
     377  
     378  	__fdisk_switch_label(cxt, lb);
     379  	assert(cxt->label == lb);
     380  
     381  	if (haslabel && !cxt->parent)
     382  		fdisk_reset_device_properties(cxt);
     383  
     384  	DBG(CXT, ul_debugobj(cxt, "create a new %s label", lb->name));
     385  	return lb->op->create(cxt);
     386  }
     387  
     388  /**
     389   * fdisk_locate_disklabel:
     390   * @cxt: context
     391   * @n: N item
     392   * @name: return item name
     393   * @offset: return offset where is item
     394   * @size: of the item
     395   *
     396   * Locate disklabel and returns info about @n item of the label.
     397   *
     398   * For example GPT is composed from three items, PMBR and GPT, n=0 return
     399   * offset to PMBR and n=1 return offset to GPT Header and n=2 returns offset to
     400   * GPT array of partitions, n=3 and n=4 returns location of the backup GPT
     401   * label at the end of the disk.
     402   *
     403   * The function returns the current in-memory situation. It's possible that a
     404   * header location is modified by write operation, for example when enabled
     405   * minimization (see fdisk_gpt_enable_minimize()). In this case it's better to
     406   * call this function after fdisk_write_disklabel().
     407   *
     408   * For more details see 'D' expert fdisk command.
     409   *
     410   * Returns: 0 on success, <0 on error, 1 no more items.
     411   */
     412  int fdisk_locate_disklabel(struct fdisk_context *cxt, int n, const char **name,
     413  			   uint64_t *offset, size_t *size)
     414  {
     415  	if (!cxt || !cxt->label)
     416  		return -EINVAL;
     417  	if (!cxt->label->op->locate)
     418  		return -ENOSYS;
     419  
     420  	DBG(CXT, ul_debugobj(cxt, "locating %d chunk of %s.", n, cxt->label->name));
     421  	return cxt->label->op->locate(cxt, n, name, offset, size);
     422  }
     423  
     424  
     425  /**
     426   * fdisk_get_disklabel_id:
     427   * @cxt: fdisk context
     428   * @id: returns pointer to allocated string (MBR Id or GPT dirk UUID)
     429   *
     430   * Returns: 0 on success, otherwise, a corresponding error.
     431   */
     432  int fdisk_get_disklabel_id(struct fdisk_context *cxt, char **id)
     433  {
     434  	struct fdisk_labelitem item = FDISK_LABELITEM_INIT;
     435  	int rc;
     436  
     437  	if (!cxt || !cxt->label || !id)
     438  		return -EINVAL;
     439  
     440  	DBG(CXT, ul_debugobj(cxt, "asking for disk %s ID", cxt->label->name));
     441  
     442  	rc = fdisk_get_disklabel_item(cxt, FDISK_LABELITEM_ID, &item);
     443  	if (rc == 0) {
     444  		*id = item.data.str;
     445  		item.data.str = NULL;
     446  	}
     447  	fdisk_reset_labelitem(&item);
     448  	if (rc > 0)
     449  		rc = 0;
     450  	return rc;
     451  }
     452  
     453  /**
     454   * fdisk_get_disklabel_item:
     455   * @cxt: fdisk context
     456   * @id: item ID (FDISK_LABELITEM_* or *_LABELITEM_*)
     457   * @item: specifies and returns the item
     458   *
     459   * Note that @id is always in range 0..N. It's fine to use the function in loop
     460   * until it returns error or 2, the result in @item should be ignored when
     461   * function returns 1. Don't forget to use fdisk_reset_labelitem() or fdisk_unref_labelitem().
     462   *
     463   * Returns: 0 on success, < 0 on error, 1 on unsupported item, 2 id out of range
     464   */
     465  int fdisk_get_disklabel_item(struct fdisk_context *cxt, int id, struct fdisk_labelitem *item)
     466  {
     467  	if (!cxt || !cxt->label || !item)
     468  		return -EINVAL;
     469  
     470  	fdisk_reset_labelitem(item);
     471  	item->id = id;
     472  	DBG(CXT, ul_debugobj(cxt, "asking for disk %s item %d", cxt->label->name, item->id));
     473  
     474  	if (!cxt->label->op->get_item)
     475  		return -ENOSYS;
     476  
     477  	return cxt->label->op->get_item(cxt, item);
     478  }
     479  
     480  /**
     481   * fdisk_set_disklabel_id:
     482   * @cxt: fdisk context
     483   *
     484   * Returns: 0 on success, otherwise, a corresponding error.
     485   */
     486  int fdisk_set_disklabel_id(struct fdisk_context *cxt)
     487  {
     488  	if (!cxt || !cxt->label)
     489  		return -EINVAL;
     490  	if (!cxt->label->op->set_id)
     491  		return -ENOSYS;
     492  
     493  	DBG(CXT, ul_debugobj(cxt, "setting %s disk ID", cxt->label->name));
     494  	return cxt->label->op->set_id(cxt, NULL);
     495  }
     496  
     497  /**
     498   * fdisk_set_disklabel_id_from_string
     499   * @cxt: fdisk context
     500   * @str: new Id
     501   *
     502   * Returns: 0 on success, otherwise, a corresponding error.
     503   *
     504   * Since: 2.36
     505   */
     506  int fdisk_set_disklabel_id_from_string(struct fdisk_context *cxt, const char *str)
     507  {
     508  	if (!cxt || !cxt->label || !str)
     509  		return -EINVAL;
     510  	if (!cxt->label->op->set_id)
     511  		return -ENOSYS;
     512  
     513  	DBG(CXT, ul_debugobj(cxt, "setting %s disk ID from '%s'", cxt->label->name, str));
     514  	return cxt->label->op->set_id(cxt, str);
     515  }
     516  
     517  /**
     518   * fdisk_set_partition_type:
     519   * @cxt: fdisk context
     520   * @partnum: partition number
     521   * @t: new type
     522   *
     523   * Returns: 0 on success, < 0 on error.
     524   */
     525  int fdisk_set_partition_type(struct fdisk_context *cxt,
     526  			     size_t partnum,
     527  			     struct fdisk_parttype *t)
     528  {
     529  	if (!cxt || !cxt->label || !t)
     530  		return -EINVAL;
     531  
     532  
     533  	if (cxt->label->op->set_part) {
     534  		struct fdisk_partition *pa = fdisk_new_partition();
     535  		int rc;
     536  
     537  		if (!pa)
     538  			return -ENOMEM;
     539  		fdisk_partition_set_type(pa, t);
     540  
     541  		DBG(CXT, ul_debugobj(cxt, "partition: %zd: set type", partnum));
     542  		rc = cxt->label->op->set_part(cxt, partnum, pa);
     543  		fdisk_unref_partition(pa);
     544  		return rc;
     545  	}
     546  
     547  	return -ENOSYS;
     548  }
     549  
     550  
     551  /**
     552   * fdisk_toggle_partition_flag:
     553   * @cxt: fdisk context
     554   * @partnum: partition number
     555   * @flag: flag ID
     556   *
     557   * Returns: 0 on success, otherwise, a corresponding error.
     558   */
     559  int fdisk_toggle_partition_flag(struct fdisk_context *cxt,
     560  			       size_t partnum,
     561  			       unsigned long flag)
     562  {
     563  	int rc;
     564  
     565  	if (!cxt || !cxt->label)
     566  		return -EINVAL;
     567  	if (!cxt->label->op->part_toggle_flag)
     568  		return -ENOSYS;
     569  
     570  	rc = cxt->label->op->part_toggle_flag(cxt, partnum, flag);
     571  
     572  	DBG(CXT, ul_debugobj(cxt, "partition: %zd: toggle: 0x%04lx [rc=%d]", partnum, flag, rc));
     573  	return rc;
     574  }
     575  
     576  /**
     577   * fdisk_reorder_partitions
     578   * @cxt: fdisk context
     579   *
     580   * Sort partitions according to the partition start sector.
     581   *
     582   * Returns: 0 on success, 1 reorder unnecessary, otherwise a corresponding error.
     583   */
     584  int fdisk_reorder_partitions(struct fdisk_context *cxt)
     585  {
     586  	int rc;
     587  
     588  	if (!cxt || !cxt->label)
     589  		return -EINVAL;
     590  	if (!cxt->label->op->reorder)
     591  		return -ENOSYS;
     592  
     593  	rc = cxt->label->op->reorder(cxt);
     594  
     595  	switch (rc) {
     596  	case 0:
     597  		fdisk_info(cxt, _("Partitions order fixed."));
     598  		break;
     599  	case 1:
     600  		fdisk_info(cxt, _("Nothing to do. Ordering is correct already."));
     601  		break;
     602  	default:
     603  		fdisk_warnx(cxt, _("Failed to fix partitions order."));
     604  		break;
     605  	}
     606  
     607  	return rc;
     608  }
     609  
     610  /*
     611   * Resets the current used label driver to initial state
     612   */
     613  void fdisk_deinit_label(struct fdisk_label *lb)
     614  {
     615  	assert(lb);
     616  
     617  	/* private label information */
     618  	if (lb->op->deinit)
     619  		lb->op->deinit(lb);
     620  }
     621  
     622  /**
     623   * fdisk_label_set_changed:
     624   * @lb: label
     625   * @changed: 0/1
     626   *
     627   * Marks in-memory data as changed, to force fdisk_write_disklabel() to write
     628   * to device. This should be unnecessary by default, the library keeps track
     629   * about changes.
     630   */
     631  void fdisk_label_set_changed(struct fdisk_label *lb, int changed)
     632  {
     633  	assert(lb);
     634  	lb->changed = changed ? 1 : 0;
     635  }
     636  
     637  /**
     638   * fdisk_label_is_changed:
     639   * @lb: label
     640   *
     641   * Returns: 1 if in-memory data has been changed.
     642   */
     643  int fdisk_label_is_changed(const struct fdisk_label *lb)
     644  {
     645  	return lb ? lb->changed : 0;
     646  }
     647  
     648  /**
     649   * fdisk_label_set_disabled:
     650   * @lb: label
     651   * @disabled: 0 or 1
     652   *
     653   * Mark label as disabled, then libfdisk is going to ignore the label when
     654   * probe device for labels.
     655   */
     656  void fdisk_label_set_disabled(struct fdisk_label *lb, int disabled)
     657  {
     658  	assert(lb);
     659  
     660  	DBG(LABEL, ul_debug("%s label %s",
     661  				lb->name,
     662  				disabled ? "DISABLED" : "ENABLED"));
     663  	lb->disabled = disabled ? 1 : 0;
     664  }
     665  
     666  /**
     667   * fdisk_label_is_disabled:
     668   * @lb: label
     669   *
     670   * Returns: 1 if label driver disabled.
     671   */
     672  int fdisk_label_is_disabled(const struct fdisk_label *lb)
     673  {
     674  	assert(lb);
     675  	return lb ? lb->disabled : 0;
     676  }
     677  
     678  /**
     679   * fdisk_label_get_geomrange_sectors:
     680   * @lb: label
     681   * @mi: minimal number
     682   * @ma: maximal number
     683   *
     684   * The function provides minimal and maximal geometry supported for the label,
     685   * if no range defined by library then returns -ENOSYS.
     686   *
     687   * Since: 2.32
     688   *
     689   * Returns: 0 on success, otherwise, a corresponding error.
     690   */
     691  int fdisk_label_get_geomrange_sectors(const struct fdisk_label *lb,
     692  					fdisk_sector_t *mi, fdisk_sector_t *ma)
     693  {
     694  	if (!lb || lb->geom_min.sectors == 0)
     695  		return -ENOSYS;
     696  	if (mi)
     697  		*mi = lb->geom_min.sectors;
     698  	if (ma)
     699  		*ma = lb->geom_max.sectors;
     700  	return 0;
     701  }
     702  
     703  /**
     704   * fdisk_label_get_geomrange_heads:
     705   * @lb: label
     706   * @mi: minimal number
     707   * @ma: maximal number
     708   *
     709   * The function provides minimal and maximal geometry supported for the label,
     710   * if no range defined by library then returns -ENOSYS.
     711   *
     712   * Since: 2.32
     713   *
     714   * Returns: 0 on success, otherwise, a corresponding error.
     715   */
     716  int fdisk_label_get_geomrange_heads(const struct fdisk_label *lb,
     717  					unsigned int *mi, unsigned int *ma)
     718  {
     719  	if (!lb || lb->geom_min.heads == 0)
     720  		return -ENOSYS;
     721  	if (mi)
     722  		*mi = lb->geom_min.heads;
     723  	if (ma)
     724  		*ma = lb->geom_max.heads;
     725  	return 0;
     726  }
     727  
     728  /**
     729   * fdisk_label_get_geomrange_cylinders:
     730   * @lb: label
     731   * @mi: minimal number
     732   * @ma: maximal number
     733   *
     734   * The function provides minimal and maximal geometry supported for the label,
     735   * if no range defined by library then returns -ENOSYS.
     736   *
     737   * Since: 2.32
     738   *
     739   * Returns: 0 on success, otherwise, a corresponding error.
     740   */
     741  int fdisk_label_get_geomrange_cylinders(const struct fdisk_label *lb,
     742  					fdisk_sector_t *mi, fdisk_sector_t *ma)
     743  {
     744  	if (!lb || lb->geom_min.cylinders == 0)
     745  		return -ENOSYS;
     746  	if (mi)
     747  		*mi = lb->geom_min.cylinders;
     748  	if (ma)
     749  		*ma = lb->geom_max.cylinders;
     750  	return 0;
     751  }
     752