(root)/
util-linux-2.39/
libfdisk/
src/
context.c
       1  #ifdef HAVE_LIBBLKID
       2  # include <blkid.h>
       3  #endif
       4  
       5  #include "blkdev.h"
       6  #ifdef __linux__
       7  # include "partx.h"
       8  #endif
       9  #include "loopdev.h"
      10  #include "fdiskP.h"
      11  
      12  #include "strutils.h"
      13  
      14  /**
      15   * SECTION: context
      16   * @title: Context
      17   * @short_description: stores info about device, labels etc.
      18   *
      19   * The library distinguish between three types of partitioning objects.
      20   *
      21   * on-disk label data
      22   *    - disk label specific
      23   *    - probed and read  by disklabel drivers when assign device to the context
      24   *      or when switch to another disk label type
      25   *    - only fdisk_write_disklabel() modify on-disk data
      26   *
      27   * in-memory label data
      28   *    - generic data and disklabel specific data stored in struct fdisk_label
      29   *    - all partitioning operations are based on in-memory data only
      30   *
      31   * struct fdisk_partition
      32   *    - provides abstraction to present partitions to users
      33   *    - fdisk_partition is possible to gather to fdisk_table container
      34   *    - used as unified template for new partitions
      35   *    - used (with fdisk_table) in fdisk scripts
      36   *    - the struct fdisk_partition is always completely independent object and
      37   *      any change to the object has no effect to in-memory (or on-disk) label data
      38   *
      39   * Don't forget to inform kernel about changes by fdisk_reread_partition_table()
      40   * or more smart fdisk_reread_changes().
      41   */
      42  
      43  /**
      44   * fdisk_new_context:
      45   *
      46   * Returns: newly allocated libfdisk handler
      47   */
      48  struct fdisk_context *fdisk_new_context(void)
      49  {
      50  	struct fdisk_context *cxt;
      51  
      52  	cxt = calloc(1, sizeof(*cxt));
      53  	if (!cxt)
      54  		return NULL;
      55  
      56  	DBG(CXT, ul_debugobj(cxt, "alloc"));
      57  	cxt->dev_fd = -1;
      58  	cxt->refcount = 1;
      59  
      60  	INIT_LIST_HEAD(&cxt->wipes);
      61  
      62  	/*
      63  	 * Allocate label specific structs.
      64  	 *
      65  	 * This is necessary (for example) to store label specific
      66  	 * context setting.
      67  	 */
      68  	cxt->labels[ cxt->nlabels++ ] = fdisk_new_gpt_label(cxt);
      69  	cxt->labels[ cxt->nlabels++ ] = fdisk_new_dos_label(cxt);
      70  	cxt->labels[ cxt->nlabels++ ] = fdisk_new_bsd_label(cxt);
      71  	cxt->labels[ cxt->nlabels++ ] = fdisk_new_sgi_label(cxt);
      72  	cxt->labels[ cxt->nlabels++ ] = fdisk_new_sun_label(cxt);
      73  
      74  	bindtextdomain(LIBFDISK_TEXTDOMAIN, LOCALEDIR);
      75  
      76  	return cxt;
      77  }
      78  
      79  static int init_nested_from_parent(struct fdisk_context *cxt, int isnew)
      80  {
      81  	struct fdisk_context *parent;
      82  
      83  	assert(cxt);
      84  	assert(cxt->parent);
      85  
      86  	parent = cxt->parent;
      87  
      88  	INIT_LIST_HEAD(&cxt->wipes);
      89  
      90  	cxt->alignment_offset = parent->alignment_offset;
      91  	cxt->ask_cb =		parent->ask_cb;
      92  	cxt->ask_data =		parent->ask_data;
      93  	cxt->dev_fd =		parent->dev_fd;
      94  	cxt->first_lba =        parent->first_lba;
      95  	cxt->firstsector_bufsz = parent->firstsector_bufsz;
      96  	cxt->firstsector =	parent->firstsector;
      97  	cxt->geom =		parent->geom;
      98  	cxt->grain =            parent->grain;
      99  	cxt->io_size =          parent->io_size;
     100  	cxt->last_lba =		parent->last_lba;
     101  	cxt->min_io_size =      parent->min_io_size;
     102  	cxt->optimal_io_size =  parent->optimal_io_size;
     103  	cxt->phy_sector_size =  parent->phy_sector_size;
     104  	cxt->readonly =		parent->readonly;
     105  	cxt->script =		parent->script;
     106  	fdisk_ref_script(cxt->script);
     107  	cxt->sector_size =      parent->sector_size;
     108  	cxt->total_sectors =    parent->total_sectors;
     109  	cxt->user_geom =	parent->user_geom;
     110  	cxt->user_log_sector =	parent->user_log_sector;
     111  	cxt->user_pyh_sector =  parent->user_pyh_sector;
     112  
     113  	/* parent <--> nested independent setting, initialize for new nested
     114  	 * contexts only */
     115  	if (isnew) {
     116  		cxt->listonly =	parent->listonly;
     117  		cxt->display_details =	parent->display_details;
     118  		cxt->display_in_cyl_units = parent->display_in_cyl_units;
     119  		cxt->protect_bootbits = parent->protect_bootbits;
     120  	}
     121  
     122  	free(cxt->dev_model);
     123  	cxt->dev_model = NULL;
     124  	cxt->dev_model_probed = 0;
     125  
     126  	return strdup_between_structs(cxt, parent, dev_path);
     127  }
     128  
     129  /**
     130   * fdisk_new_nested_context:
     131   * @parent: parental context
     132   * @name: optional label name (e.g. "bsd")
     133   *
     134   * Create a new nested fdisk context for nested disk labels (e.g. BSD or PMBR).
     135   * The function also probes for the nested label on the device if device is
     136   * already assigned to parent.
     137   *
     138   * The new context is initialized according to @parent and both context shares
     139   * some settings and file descriptor to the device. The child propagate some
     140   * changes (like fdisk_assign_device()) to parent, but it does not work
     141   * vice-versa. The behavior is undefined if you assign another device to
     142   * parent.
     143   *
     144   * Returns: new context for nested partition table.
     145   */
     146  struct fdisk_context *fdisk_new_nested_context(struct fdisk_context *parent,
     147  				const char *name)
     148  {
     149  	struct fdisk_context *cxt;
     150  	struct fdisk_label *lb = NULL;
     151  
     152  	assert(parent);
     153  
     154  	cxt = calloc(1, sizeof(*cxt));
     155  	if (!cxt)
     156  		return NULL;
     157  
     158  	DBG(CXT, ul_debugobj(parent, "alloc nested [%p] [name=%s]", cxt, name));
     159  	cxt->refcount = 1;
     160  
     161  	fdisk_ref_context(parent);
     162  	cxt->parent = parent;
     163  
     164  	if (init_nested_from_parent(cxt, 1) != 0) {
     165  		cxt->parent = NULL;
     166  		fdisk_unref_context(cxt);
     167  		return NULL;
     168  	}
     169  
     170  	if (name) {
     171  		if (strcasecmp(name, "bsd") == 0)
     172  			lb = cxt->labels[ cxt->nlabels++ ] = fdisk_new_bsd_label(cxt);
     173  		else if (strcasecmp(name, "dos") == 0 || strcasecmp(name, "mbr") == 0)
     174  			lb = cxt->labels[ cxt->nlabels++ ] = fdisk_new_dos_label(cxt);
     175  	}
     176  
     177  	if (lb && parent->dev_fd >= 0) {
     178  		DBG(CXT, ul_debugobj(cxt, "probing for nested %s", lb->name));
     179  
     180  		cxt->label = lb;
     181  
     182  		if (lb->op->probe(cxt) == 1)
     183  			__fdisk_switch_label(cxt, lb);
     184  		else {
     185  			DBG(CXT, ul_debugobj(cxt, "not found %s label", lb->name));
     186  			if (lb->op->deinit)
     187  				lb->op->deinit(lb);
     188  			cxt->label = NULL;
     189  		}
     190  	}
     191  
     192  	return cxt;
     193  }
     194  
     195  
     196  /**
     197   * fdisk_ref_context:
     198   * @cxt: context pointer
     199   *
     200   * Increments reference counter.
     201   */
     202  void fdisk_ref_context(struct fdisk_context *cxt)
     203  {
     204  	if (cxt)
     205  		cxt->refcount++;
     206  }
     207  
     208  /**
     209   * fdisk_get_label:
     210   * @cxt: context instance
     211   * @name: label name (e.g. "gpt")
     212   *
     213   * If no @name specified then returns the current context label.
     214   *
     215   * The label is allocated and maintained within the context #cxt. There is
     216   * nothing like reference counting for labels, you cannot deallocate the
     217   * label.
     218   *
     219   * Returns: label struct or NULL in case of error.
     220   */
     221  struct fdisk_label *fdisk_get_label(struct fdisk_context *cxt, const char *name)
     222  {
     223  	size_t i;
     224  
     225  	assert(cxt);
     226  
     227  	if (!name)
     228  		return cxt->label;
     229  
     230  	if (strcasecmp(name, "mbr") == 0)
     231  		name = "dos";
     232  
     233  	for (i = 0; i < cxt->nlabels; i++)
     234  		if (cxt->labels[i]
     235  		    && strcasecmp(cxt->labels[i]->name, name) == 0)
     236  			return cxt->labels[i];
     237  
     238  	DBG(CXT, ul_debugobj(cxt, "failed to found %s label driver", name));
     239  	return NULL;
     240  }
     241  
     242  /**
     243   * fdisk_next_label:
     244   * @cxt: context instance
     245   * @lb: returns pointer to the next label
     246   *
     247   * <informalexample>
     248   *   <programlisting>
     249   *      // print all supported labels
     250   *	struct fdisk_context *cxt = fdisk_new_context();
     251   *	struct fdisk_label *lb = NULL;
     252   *
     253   *	while (fdisk_next_label(cxt, &lb) == 0)
     254   *		print("label name: %s\n", fdisk_label_get_name(lb));
     255   *	fdisk_unref_context(cxt);
     256   *   </programlisting>
     257   * </informalexample>
     258   *
     259   * Returns: <0 in case of error, 0 on success, 1 at the end.
     260   */
     261  int fdisk_next_label(struct fdisk_context *cxt, struct fdisk_label **lb)
     262  {
     263  	size_t i;
     264  	struct fdisk_label *res = NULL;
     265  
     266  	if (!lb || !cxt)
     267  		return -EINVAL;
     268  
     269  	if (!*lb)
     270  		res = cxt->labels[0];
     271  	else {
     272  		for (i = 1; i < cxt->nlabels; i++) {
     273  			if (*lb == cxt->labels[i - 1]) {
     274  				res = cxt->labels[i];
     275  				break;
     276  			}
     277  		}
     278  	}
     279  
     280  	*lb = res;
     281  	return res ? 0 : 1;
     282  }
     283  
     284  /**
     285   * fdisk_get_nlabels:
     286   * @cxt: context
     287   *
     288   * Returns: number of supported label types
     289   */
     290  size_t fdisk_get_nlabels(struct fdisk_context *cxt)
     291  {
     292  	return cxt ? cxt->nlabels : 0;
     293  }
     294  
     295  int __fdisk_switch_label(struct fdisk_context *cxt, struct fdisk_label *lb)
     296  {
     297  	if (!lb || !cxt)
     298  		return -EINVAL;
     299  	if (lb->disabled) {
     300  		DBG(CXT, ul_debugobj(cxt, "*** attempt to switch to disabled label %s -- ignore!", lb->name));
     301  		return -EINVAL;
     302  	}
     303  	cxt->label = lb;
     304  	DBG(CXT, ul_debugobj(cxt, "--> switching context to %s!", lb->name));
     305  
     306  	fdisk_apply_label_device_properties(cxt);
     307  	return 0;
     308  }
     309  
     310  /**
     311   * fdisk_has_label:
     312   * @cxt: fdisk context
     313   *
     314   * Returns: return 1 if there is label on the device.
     315   */
     316  int fdisk_has_label(struct fdisk_context *cxt)
     317  {
     318  	return cxt && cxt->label;
     319  }
     320  
     321  /**
     322   * fdisk_has_protected_bootbits:
     323   * @cxt: fdisk context
     324   *
     325   * Returns: return 1 if boot bits protection enabled.
     326   */
     327  int fdisk_has_protected_bootbits(struct fdisk_context *cxt)
     328  {
     329  	return cxt && cxt->protect_bootbits;
     330  }
     331  
     332  /**
     333   * fdisk_enable_bootbits_protection:
     334   * @cxt: fdisk context
     335   * @enable: 1 or 0
     336   *
     337   * The library zeroizes all the first sector when create a new disk label by
     338   * default.  This function can be used to control this behavior. For now it's
     339   * supported for MBR and GPT.
     340   *
     341   * Returns: 0 on success, < 0 on error.
     342   */
     343  int fdisk_enable_bootbits_protection(struct fdisk_context *cxt, int enable)
     344  {
     345  	if (!cxt)
     346  		return -EINVAL;
     347  	cxt->protect_bootbits = enable ? 1 : 0;
     348  	return 0;
     349  }
     350  /**
     351   * fdisk_disable_dialogs
     352   * @cxt: fdisk context
     353   * @disable: 1 or 0
     354   *
     355   * The library uses dialog driven partitioning by default.
     356   *
     357   * Returns: 0 on success, < 0 on error.
     358   *
     359   * Since: 2.31
     360   */
     361  int fdisk_disable_dialogs(struct fdisk_context *cxt, int disable)
     362  {
     363  	if (!cxt)
     364  		return -EINVAL;
     365  
     366  	cxt->no_disalogs = disable;
     367  	return 0;
     368  }
     369  
     370  /**
     371   * fdisk_has_dialogs
     372   * @cxt: fdisk context
     373   *
     374   * See fdisk_disable_dialogs()
     375   *
     376   * Returns: 1 if dialog driven partitioning enabled (default), or 0.
     377   *
     378   * Since: 2.31
     379   */
     380  int fdisk_has_dialogs(struct fdisk_context *cxt)
     381  {
     382  	return cxt->no_disalogs == 0;
     383  }
     384  
     385  /**
     386   * fdisk_enable_wipe
     387   * @cxt: fdisk context
     388   * @enable: 1 or 0
     389   *
     390   * The library removes all PT/filesystem/RAID signatures before it writes
     391   * partition table. The probing area where it looks for signatures is from
     392   * the begin of the disk. The device is wiped by libblkid.
     393   *
     394   * See also fdisk_wipe_partition().
     395   *
     396   * Returns: 0 on success, < 0 on error.
     397   */
     398  int fdisk_enable_wipe(struct fdisk_context *cxt, int enable)
     399  {
     400  	if (!cxt)
     401  		return -EINVAL;
     402  
     403  	fdisk_set_wipe_area(cxt, 0, cxt->total_sectors, enable);
     404  	return 0;
     405  }
     406  
     407  /**
     408   * fdisk_has_wipe
     409   * @cxt: fdisk context
     410   *
     411   * Returns the current wipe setting. See fdisk_enable_wipe().
     412   *
     413   * Returns: 0 on success, < 0 on error.
     414   */
     415  int fdisk_has_wipe(struct fdisk_context *cxt)
     416  {
     417  	if (!cxt)
     418  		return 0;
     419  
     420  	return fdisk_has_wipe_area(cxt, 0, cxt->total_sectors);
     421  }
     422  
     423  
     424  /**
     425   * fdisk_get_collision
     426   * @cxt: fdisk context
     427   *
     428   * Returns: name of the filesystem or RAID detected on the device or NULL.
     429   */
     430  const char *fdisk_get_collision(struct fdisk_context *cxt)
     431  {
     432  	return cxt->collision;
     433  }
     434  
     435  /**
     436   * fdisk_is_ptcollision:
     437   * @cxt: fdisk context
     438   *
     439   * The collision detected by libblkid (usually another partition table). Note
     440   * that libfdisk does not support all partitions tables, so fdisk_has_label()
     441   * may return false, but fdisk_is_ptcollision() may return true.
     442   *
     443   * Since: 2.30
     444   *
     445   * Returns: 0 or 1
     446   */
     447  int fdisk_is_ptcollision(struct fdisk_context *cxt)
     448  {
     449  	return cxt->pt_collision;
     450  }
     451  
     452  /**
     453   * fdisk_get_npartitions:
     454   * @cxt: context
     455   *
     456   * The maximal number of the partitions depends on disklabel and does not
     457   * have to describe the real limit of PT.
     458   *
     459   * For example the limit for MBR without extend partition is 4, with extended
     460   * partition it's unlimited (so the function returns the current number of all
     461   * partitions in this case).
     462   *
     463   * And for example for GPT it depends on space allocated on disk for array of
     464   * entry records (usually 128).
     465   *
     466   * It's fine to use fdisk_get_npartitions() in loops, but don't forget that
     467   * partition may be unused (see fdisk_is_partition_used()).
     468   *
     469   * <informalexample>
     470   *   <programlisting>
     471   *	struct fdisk_partition *pa = NULL;
     472   *	size_t i, nmax = fdisk_get_npartitions(cxt);
     473   *
     474   *	for (i = 0; i < nmax; i++) {
     475   *		if (!fdisk_is_partition_used(cxt, i))
     476   *			continue;
     477   *		... do something ...
     478   *	}
     479   *   </programlisting>
     480   * </informalexample>
     481   *
     482   * Note that the recommended way to list partitions is to use
     483   * fdisk_get_partitions() and struct fdisk_table then ask disk driver for each
     484   * individual partitions.
     485   *
     486   * Returns: maximal number of partitions for the current label.
     487   */
     488  size_t fdisk_get_npartitions(struct fdisk_context *cxt)
     489  {
     490  	return cxt && cxt->label ? cxt->label->nparts_max : 0;
     491  }
     492  
     493  /**
     494   * fdisk_is_labeltype:
     495   * @cxt: fdisk context
     496   * @id: FDISK_DISKLABEL_*
     497   *
     498   * See also fdisk_is_label() macro in libfdisk.h.
     499   *
     500   * Returns: return 1 if the current label is @id
     501   */
     502  int fdisk_is_labeltype(struct fdisk_context *cxt, enum fdisk_labeltype id)
     503  {
     504  	assert(cxt);
     505  
     506  	return cxt->label && (unsigned)fdisk_label_get_type(cxt->label) == id;
     507  }
     508  
     509  /**
     510   * fdisk_get_parent:
     511   * @cxt: nested fdisk context
     512   *
     513   * Returns: pointer to parental context, or NULL
     514   */
     515  struct fdisk_context *fdisk_get_parent(struct fdisk_context *cxt)
     516  {
     517  	assert(cxt);
     518  	return cxt->parent;
     519  }
     520  
     521  static void reset_context(struct fdisk_context *cxt)
     522  {
     523  	size_t i;
     524  
     525  	DBG(CXT, ul_debugobj(cxt, "*** resetting context"));
     526  
     527  	/* reset drives' private data */
     528  	for (i = 0; i < cxt->nlabels; i++)
     529  		fdisk_deinit_label(cxt->labels[i]);
     530  
     531  	if (cxt->parent) {
     532  		/* the first sector may be independent on parent */
     533  		if (cxt->parent->firstsector != cxt->firstsector) {
     534  			DBG(CXT, ul_debugobj(cxt, "  firstsector independent on parent (freeing)"));
     535  			free(cxt->firstsector);
     536  		}
     537  	} else {
     538  		/* we close device only in primary context */
     539  		if (cxt->dev_fd > -1 && cxt->is_priv)
     540  			close(cxt->dev_fd);
     541  		DBG(CXT, ul_debugobj(cxt, "  freeing firstsector"));
     542  		free(cxt->firstsector);
     543  	}
     544  
     545  	free(cxt->dev_path);
     546  	cxt->dev_path = NULL;
     547  
     548  	free(cxt->dev_model);
     549  	cxt->dev_model = NULL;
     550  	cxt->dev_model_probed = 0;
     551  
     552  	free(cxt->collision);
     553  	cxt->collision = NULL;
     554  
     555  	memset(&cxt->dev_st, 0, sizeof(cxt->dev_st));
     556  
     557  	cxt->dev_fd = -1;
     558  	cxt->is_priv = 0;
     559  	cxt->is_excl = 0;
     560  	cxt->firstsector = NULL;
     561  	cxt->firstsector_bufsz = 0;
     562  
     563  	fdisk_zeroize_device_properties(cxt);
     564  
     565  	fdisk_unref_script(cxt->script);
     566  	cxt->script = NULL;
     567  
     568  	cxt->label = NULL;
     569  
     570  	fdisk_free_wipe_areas(cxt);
     571  }
     572  
     573  /* fdisk_assign_device() body */
     574  static int fdisk_assign_fd(struct fdisk_context *cxt, int fd,
     575  			const char *fname, int readonly,
     576  			int priv, int excl)
     577  {
     578  	assert(cxt);
     579  	assert(fd >= 0);
     580  
     581  	errno = 0;
     582  
     583  	/* redirect request to parent */
     584  	if (cxt->parent) {
     585  		int rc, org = fdisk_is_listonly(cxt->parent);
     586  
     587  		/* assign_device() is sensitive to "listonly" mode, so let's
     588  		 * follow the current context setting for the parent to avoid
     589  		 * unwanted extra warnings. */
     590  		fdisk_enable_listonly(cxt->parent, fdisk_is_listonly(cxt));
     591  
     592  		rc = fdisk_assign_fd(cxt->parent, fd, fname, readonly, priv, excl);
     593  		fdisk_enable_listonly(cxt->parent, org);
     594  
     595  		if (!rc)
     596  			rc = init_nested_from_parent(cxt, 0);
     597  		if (!rc)
     598  			fdisk_probe_labels(cxt);
     599  		return rc;
     600  	}
     601  
     602  	reset_context(cxt);
     603  
     604  	if (fstat(fd, &cxt->dev_st) != 0)
     605  		goto fail;
     606  
     607  	cxt->readonly = readonly ? 1 : 0;
     608  	cxt->dev_fd = fd;
     609  	cxt->is_priv = priv ? 1 : 0;
     610  	cxt->is_excl = excl ? 1 : 0;
     611  
     612  	cxt->dev_path = fname ? strdup(fname) : NULL;
     613  	if (!cxt->dev_path)
     614  		goto fail;
     615  
     616  	fdisk_discover_topology(cxt);
     617  	fdisk_discover_geometry(cxt);
     618  
     619  	fdisk_apply_user_device_properties(cxt);
     620  
     621  	if (fdisk_read_firstsector(cxt) < 0)
     622  		goto fail;
     623  
     624  	/* warn about obsolete stuff on the device if we aren't in list-only */
     625  	if (!fdisk_is_listonly(cxt) && fdisk_check_collisions(cxt) < 0)
     626  		goto fail;
     627  
     628  	fdisk_probe_labels(cxt);
     629  	fdisk_apply_label_device_properties(cxt);
     630  
     631  	/* Don't report collision if there is already a valid partition table.
     632  	 * The bootbits are wiped when we create a *new* partition table only. */
     633  	if (fdisk_is_ptcollision(cxt) && fdisk_has_label(cxt)) {
     634  		DBG(CXT, ul_debugobj(cxt, "ignore old %s", cxt->collision));
     635  		cxt->pt_collision = 0;
     636  		free(cxt->collision);
     637  		cxt->collision = NULL;
     638  	}
     639  
     640  	DBG(CXT, ul_debugobj(cxt, "initialized for %s [%s %s %s]",
     641  			      fname,
     642  			      cxt->readonly ? "READ-ONLY" : "READ-WRITE",
     643  			      cxt->is_excl ? "EXCL" : "",
     644  			      cxt->is_priv ? "PRIV" : ""));
     645  	return 0;
     646  fail:
     647  	{
     648  		int rc = errno ? -errno : -EINVAL;
     649  		cxt->dev_fd = -1;
     650  		DBG(CXT, ul_debugobj(cxt, "failed to assign device [rc=%d]", rc));
     651  		return rc;
     652  	}
     653  }
     654  
     655  /**
     656   * fdisk_assign_device:
     657   * @cxt: context
     658   * @fname: path to the device to be handled
     659   * @readonly: how to open the device
     660   *
     661   * Open the device, discovery topology, geometry, detect disklabel, check for
     662   * collisions and switch the current label driver to reflect the probing
     663   * result.
     664   *
     665   * If in standard mode (!= non-listonly mode) then also detects for collisions.
     666   * The result is accessible by fdisk_get_collision() and
     667   * fdisk_is_ptcollision().  The collision (e.g. old obsolete PT) may be removed
     668   * by fdisk_enable_wipe().  Note that new PT and old PT may be on different
     669   * locations.
     670   *
     671   * Note that this function resets all generic setting in context.
     672   *
     673   * If the @cxt is nested context (necessary for example to edit BSD or PMBR)
     674   * then the device is assigned to the parental context and necessary properties
     675   * are copied to the @cxt. The change is propagated in child->parent direction
     676   * only. It's impossible to use a different device for primary and nested
     677   * contexts.
     678   *
     679   * Returns: 0 on success, < 0 on error.
     680   */
     681  int fdisk_assign_device(struct fdisk_context *cxt,
     682  			const char *fname, int readonly)
     683  {
     684  	int fd, rc, flags = O_CLOEXEC;
     685  
     686  	DBG(CXT, ul_debugobj(cxt, "assigning device %s", fname));
     687  	assert(cxt);
     688  
     689  	if (readonly)
     690  		flags |= O_RDONLY;
     691  	else
     692  		flags |= (O_RDWR | O_EXCL);
     693  
     694  	errno = 0;
     695  	fd = open(fname,flags);
     696  	if (fd < 0 && errno == EBUSY && (flags & O_EXCL)) {
     697  		flags &= ~O_EXCL;
     698  		errno = 0;
     699  		fd = open(fname, flags);
     700  	}
     701  
     702  	if (fd < 0) {
     703  		rc = -errno;
     704  		DBG(CXT, ul_debugobj(cxt, "failed to assign device [rc=%d]", rc));
     705  		return rc;
     706  	}
     707  
     708  	rc = fdisk_assign_fd(cxt, fd, fname, readonly, 1, flags & O_EXCL);
     709  	if (rc)
     710  		close(fd);
     711  	return rc;
     712  }
     713  
     714  /**
     715   * fdisk_assign_device_by_fd:
     716   * @cxt: context
     717   * @fd: device file descriptor
     718   * @fname: path to the device (used for dialogs, debugging, partition names, ...)
     719   * @readonly: how to use the device
     720   *
     721   * Like fdisk_assign_device(), but caller is responsible to open and close the
     722   * device. The library only fsync() the device on fdisk_deassign_device().
     723   *
     724   * The device has to be open O_RDWR on @readonly=0.
     725   *
     726   * Returns: 0 on success, < 0 on error.
     727   *
     728   * Since: 2.35
     729   */
     730  int fdisk_assign_device_by_fd(struct fdisk_context *cxt, int fd,
     731  			const char *fname, int readonly)
     732  {
     733  	DBG(CXT, ul_debugobj(cxt, "assign by fd"));
     734  	return fdisk_assign_fd(cxt, fd, fname, readonly, 0, 0);
     735  }
     736  
     737  /**
     738   * fdisk_deassign_device:
     739   * @cxt: context
     740   * @nosync: disable sync() after close().
     741   *
     742   * Call fsync(), close() and than sync(), but for read-only handler close the
     743   * device only. If the @cxt is nested context then the request is redirected to
     744   * the parent.
     745   *
     746   * Returns: 0 on success, < 0 on error.
     747   */
     748  int fdisk_deassign_device(struct fdisk_context *cxt, int nosync)
     749  {
     750  	assert(cxt);
     751  	assert(cxt->dev_fd >= 0);
     752  
     753  	if (cxt->parent) {
     754  		int rc = fdisk_deassign_device(cxt->parent, nosync);
     755  
     756  		if (!rc)
     757  			rc = init_nested_from_parent(cxt, 0);
     758  		return rc;
     759  	}
     760  
     761  	DBG(CXT, ul_debugobj(cxt, "de-assigning device %s", cxt->dev_path));
     762  
     763  	if (cxt->readonly && cxt->is_priv)
     764  		close(cxt->dev_fd);
     765  	else {
     766  		if (fsync(cxt->dev_fd)) {
     767  			fdisk_warn(cxt, _("%s: fsync device failed"),
     768  					cxt->dev_path);
     769  			return -errno;
     770  		}
     771  		if (cxt->is_priv && close(cxt->dev_fd)) {
     772  			fdisk_warn(cxt, _("%s: close device failed"),
     773  					cxt->dev_path);
     774  			return -errno;
     775  		}
     776  		if (!nosync) {
     777  			fdisk_info(cxt, _("Syncing disks."));
     778  			sync();
     779  		}
     780  	}
     781  
     782  	free(cxt->dev_path);
     783  	cxt->dev_path = NULL;
     784  	cxt->dev_fd = -1;
     785  	cxt->is_priv = 0;
     786  	cxt->is_excl = 0;
     787  
     788  	return 0;
     789  }
     790  
     791  /**
     792   * fdisk_reassign_device:
     793   * @cxt: context
     794   *
     795   * This function is "hard reset" of the context and it does not write anything
     796   * to the device. All in-memory changes associated with the context will be
     797   * lost. It's recommended to use this function after some fatal problem when the
     798   * context (and label specific driver) is in an undefined state.
     799   *
     800   * Returns: 0 on success, < 0 on error.
     801   */
     802  int fdisk_reassign_device(struct fdisk_context *cxt)
     803  {
     804  	char *devname;
     805  	int rdonly, rc, fd, priv, excl;
     806  
     807  	assert(cxt);
     808  	assert(cxt->dev_fd >= 0);
     809  
     810  	DBG(CXT, ul_debugobj(cxt, "re-assigning device %s", cxt->dev_path));
     811  
     812  	devname = strdup(cxt->dev_path);
     813  	if (!devname)
     814  		return -ENOMEM;
     815  
     816  	rdonly = cxt->readonly;
     817  	fd = cxt->dev_fd;
     818  	priv = cxt->is_priv;
     819  	excl = cxt->is_excl;
     820  
     821  	fdisk_deassign_device(cxt, 1);
     822  
     823  	if (priv)
     824  		/* reopen and assign */
     825  		rc = fdisk_assign_device(cxt, devname, rdonly);
     826  	else
     827  		/* assign only */
     828  		rc = fdisk_assign_fd(cxt, fd, devname, rdonly, priv, excl);
     829  
     830  	free(devname);
     831  	return rc;
     832  }
     833  
     834  /**
     835   * fdisk_reread_partition_table:
     836   * @cxt: context
     837   *
     838   * Force *kernel* to re-read partition table on block devices.
     839   *
     840   * Returns: 0 on success, < 0 in case of error.
     841   */
     842  int fdisk_reread_partition_table(struct fdisk_context *cxt)
     843  {
     844  	int i = 0;
     845  
     846  	assert(cxt);
     847  	assert(cxt->dev_fd >= 0);
     848  
     849  	if (!S_ISBLK(cxt->dev_st.st_mode))
     850  		return 0;
     851  
     852  	DBG(CXT, ul_debugobj(cxt, "calling re-read ioctl"));
     853  	sync();
     854  #ifdef BLKRRPART
     855  	fdisk_info(cxt, _("Calling ioctl() to re-read partition table."));
     856  	i = ioctl(cxt->dev_fd, BLKRRPART);
     857  #else
     858  	errno = ENOSYS;
     859  	i = 1;
     860  #endif
     861  
     862  	if (i) {
     863  		fdisk_warn(cxt, _("Re-reading the partition table failed."));
     864  		fdisk_info(cxt,	_(
     865  			"The kernel still uses the old table. The "
     866  			"new table will be used at the next reboot "
     867  			"or after you run partprobe(8) or partx(8)."));
     868  		return -errno;
     869  	}
     870  
     871  	return 0;
     872  }
     873  
     874  #ifdef __linux__
     875  static inline int add_to_partitions_array(
     876  			struct fdisk_partition ***ary,
     877  			struct fdisk_partition *pa,
     878  			size_t *n, size_t nmax)
     879  {
     880  	if (!*ary) {
     881  		*ary = calloc(nmax, sizeof(struct fdisk_partition *));
     882  		if (!*ary)
     883  			return -ENOMEM;
     884  	}
     885  	(*ary)[*n] = pa;
     886  	(*n)++;
     887  	return 0;
     888  }
     889  #endif
     890  
     891  /**
     892   * fdisk_reread_changes:
     893   * @cxt: context
     894   * @org: original layout (on disk)
     895   *
     896   * Like fdisk_reread_partition_table() but don't forces kernel re-read all
     897   * partition table. The BLKPG_* ioctls are used for individual partitions. The
     898   * advantage is that unmodified partitions maybe mounted.
     899   *
     900   * The function behaves like fdisk_reread_partition_table() on systems where
     901   * are no available BLKPG_* ioctls.
     902   *
     903   * Returns: <0 on error, or 0.
     904   */
     905  #ifdef __linux__
     906  int fdisk_reread_changes(struct fdisk_context *cxt, struct fdisk_table *org)
     907  {
     908  	struct fdisk_table *tb = NULL;
     909  	struct fdisk_iter itr;
     910  	struct fdisk_partition *pa;
     911  	struct fdisk_partition **rem = NULL, **add = NULL, **upd = NULL;
     912  	int change, rc = 0, err = 0;
     913  	size_t nparts, i, nadds = 0, nupds = 0, nrems = 0;
     914  	unsigned int ssf;
     915  
     916  	DBG(CXT, ul_debugobj(cxt, "rereading changes"));
     917  
     918  	fdisk_reset_iter(&itr, FDISK_ITER_FORWARD);
     919  
     920  	/* the current layout */
     921  	fdisk_get_partitions(cxt, &tb);
     922  	/* maximal number of partitions */
     923  	nparts = max(fdisk_table_get_nents(tb), fdisk_table_get_nents(org));
     924  
     925  	while (fdisk_diff_tables(org, tb, &itr, &pa, &change) == 0) {
     926  		if (change == FDISK_DIFF_UNCHANGED)
     927  			continue;
     928  		switch (change) {
     929  		case FDISK_DIFF_REMOVED:
     930  			rc = add_to_partitions_array(&rem, pa, &nrems, nparts);
     931  			break;
     932  		case FDISK_DIFF_ADDED:
     933  			rc = add_to_partitions_array(&add, pa, &nadds, nparts);
     934  			break;
     935  		case FDISK_DIFF_RESIZED:
     936  			rc = add_to_partitions_array(&upd, pa, &nupds, nparts);
     937  			break;
     938  		case FDISK_DIFF_MOVED:
     939  			rc = add_to_partitions_array(&rem, pa, &nrems, nparts);
     940  			if (!rc)
     941  				rc = add_to_partitions_array(&add, pa, &nadds, nparts);
     942  			break;
     943  		}
     944  		if (rc != 0)
     945  			goto done;
     946  	}
     947  
     948  	/* sector size factor -- used to recount from real to 512-byte sectors */
     949  	ssf = cxt->sector_size / 512;
     950  
     951  	for (i = 0; i < nrems; i++) {
     952  		pa = rem[i];
     953  		DBG(PART, ul_debugobj(pa, "#%zu calling BLKPG_DEL_PARTITION", pa->partno));
     954  		if (partx_del_partition(cxt->dev_fd, pa->partno + 1) != 0) {
     955  			fdisk_warn(cxt, _("Failed to remove partition %zu from system"), pa->partno + 1);
     956  			err++;
     957  		}
     958  	}
     959  	for (i = 0; i < nupds; i++) {
     960  		pa = upd[i];
     961  		DBG(PART, ul_debugobj(pa, "#%zu calling BLKPG_RESIZE_PARTITION", pa->partno));
     962  		if (partx_resize_partition(cxt->dev_fd, pa->partno + 1,
     963  					   pa->start * ssf, pa->size * ssf) != 0) {
     964  			fdisk_warn(cxt, _("Failed to update system information about partition %zu"), pa->partno + 1);
     965  			err++;
     966  		}
     967  	}
     968  	for (i = 0; i < nadds; i++) {
     969  		uint64_t sz;
     970  
     971  		pa = add[i];
     972  		sz = pa->size * ssf;
     973  
     974  		DBG(PART, ul_debugobj(pa, "#%zu calling BLKPG_ADD_PARTITION", pa->partno));
     975  
     976  		if (fdisk_is_label(cxt, DOS) && fdisk_partition_is_container(pa))
     977  			/* Let's follow the Linux kernel and reduce
     978                           * DOS extended partition to 1 or 2 sectors.
     979  			 */
     980  			sz = min(sz, (uint64_t) 2);
     981  
     982  		if (partx_add_partition(cxt->dev_fd, pa->partno + 1,
     983  					pa->start * ssf, sz) != 0) {
     984  			fdisk_warn(cxt, _("Failed to add partition %zu to system"), pa->partno + 1);
     985  			err++;
     986  		}
     987  	}
     988  	if (err)
     989  		fdisk_info(cxt,	_(
     990  			"The kernel still uses the old partitions. The new "
     991  			"table will be used at the next reboot. "));
     992  done:
     993  	free(rem);
     994  	free(add);
     995  	free(upd);
     996  	fdisk_unref_table(tb);
     997  	return rc;
     998  }
     999  #else
    1000  int fdisk_reread_changes(struct fdisk_context *cxt,
    1001  			 struct fdisk_table *org __attribute__((__unused__))) {
    1002  	return fdisk_reread_partition_table(cxt);
    1003  }
    1004  #endif
    1005  
    1006  /**
    1007   * fdisk_device_is_used:
    1008   * @cxt: context
    1009   *
    1010   * The function returns always 0 if the device has not been opened by
    1011   * fdisk_assign_device() or if open read-only.
    1012   *
    1013   * Returns: 1 if the device assigned to the context is used by system, or 0.
    1014   */
    1015  int fdisk_device_is_used(struct fdisk_context *cxt)
    1016  {
    1017  	int rc;
    1018  	assert(cxt);
    1019  	assert(cxt->dev_fd >= 0);
    1020  
    1021  	rc = cxt->readonly ? 0 :
    1022  	     cxt->is_excl ? 0 :
    1023  	     cxt->is_priv ? 1 : 0;
    1024  
    1025  	DBG(CXT, ul_debugobj(cxt, "device used: %s [read-only=%d, excl=%d, priv:%d]",
    1026  				rc ? "TRUE" : "FALSE", cxt->readonly,
    1027  				cxt->is_excl, cxt->is_priv));
    1028  	return rc;
    1029  }
    1030  
    1031  /**
    1032   * fdisk_is_readonly:
    1033   * @cxt: context
    1034   *
    1035   * Returns: 1 if device open readonly
    1036   */
    1037  int fdisk_is_readonly(struct fdisk_context *cxt)
    1038  {
    1039  	assert(cxt);
    1040  	return cxt->readonly;
    1041  }
    1042  
    1043  /**
    1044   * fdisk_is_regfile:
    1045   * @cxt: context
    1046   *
    1047   * Since: 2.30
    1048   *
    1049   * Returns: 1 if open file descriptor is regular file rather than a block device.
    1050   */
    1051  int fdisk_is_regfile(struct fdisk_context *cxt)
    1052  {
    1053  	assert(cxt);
    1054  	return S_ISREG(cxt->dev_st.st_mode);
    1055  }
    1056  
    1057  /**
    1058   * fdisk_unref_context:
    1059   * @cxt: fdisk context
    1060   *
    1061   * Deallocates context struct.
    1062   */
    1063  void fdisk_unref_context(struct fdisk_context *cxt)
    1064  {
    1065  	unsigned i;
    1066  
    1067  	if (!cxt)
    1068  		return;
    1069  
    1070  	cxt->refcount--;
    1071  	if (cxt->refcount <= 0) {
    1072  		DBG(CXT, ul_debugobj(cxt, "freeing context %p for %s", cxt, cxt->dev_path));
    1073  
    1074  		reset_context(cxt);	/* this is sensitive to parent<->child relationship! */
    1075  
    1076  		/* deallocate label's private stuff */
    1077  		for (i = 0; i < cxt->nlabels; i++) {
    1078  			if (!cxt->labels[i])
    1079  				continue;
    1080  			if (cxt->labels[i]->op->free)
    1081  				cxt->labels[i]->op->free(cxt->labels[i]);
    1082  			else
    1083  				free(cxt->labels[i]);
    1084  			cxt->labels[i] = NULL;
    1085  		}
    1086  
    1087  		fdisk_unref_context(cxt->parent);
    1088  		cxt->parent = NULL;
    1089  
    1090  		free(cxt);
    1091  	}
    1092  }
    1093  
    1094  
    1095  /**
    1096   * fdisk_enable_details:
    1097   * @cxt: context
    1098   * @enable: true/false
    1099   *
    1100   * Enables or disables "details" display mode. This function has effect to
    1101   * fdisk_partition_to_string() function.
    1102   *
    1103   * Returns: 0 on success, < 0 on error.
    1104   */
    1105  int fdisk_enable_details(struct fdisk_context *cxt, int enable)
    1106  {
    1107  	assert(cxt);
    1108  	cxt->display_details = enable ? 1 : 0;
    1109  	return 0;
    1110  }
    1111  
    1112  /**
    1113   * fdisk_is_details:
    1114   * @cxt: context
    1115   *
    1116   * Returns: 1 if details are enabled
    1117   */
    1118  int fdisk_is_details(struct fdisk_context *cxt)
    1119  {
    1120  	assert(cxt);
    1121  	return cxt->display_details == 1;
    1122  }
    1123  
    1124  /**
    1125   * fdisk_enable_listonly:
    1126   * @cxt: context
    1127   * @enable: true/false
    1128   *
    1129   * Just list partition only, don't care about another details, mistakes, ...
    1130   *
    1131   * Returns: 0 on success, < 0 on error.
    1132   */
    1133  int fdisk_enable_listonly(struct fdisk_context *cxt, int enable)
    1134  {
    1135  	assert(cxt);
    1136  	cxt->listonly = enable ? 1 : 0;
    1137  	return 0;
    1138  }
    1139  
    1140  /**
    1141   * fdisk_is_listonly:
    1142   * @cxt: context
    1143   *
    1144   * Returns: 1 if list-only mode enabled
    1145   */
    1146  int fdisk_is_listonly(struct fdisk_context *cxt)
    1147  {
    1148  	assert(cxt);
    1149  	return cxt->listonly == 1;
    1150  }
    1151  
    1152  
    1153  /**
    1154   * fdisk_set_unit:
    1155   * @cxt: context
    1156   * @str: "cylinder" or "sector".
    1157   *
    1158   * This is pure shit, unfortunately for example Sun addresses begin of the
    1159   * partition by cylinders...
    1160   *
    1161   * Returns: 0 on success, <0 on error.
    1162   */
    1163  int fdisk_set_unit(struct fdisk_context *cxt, const char *str)
    1164  {
    1165  	assert(cxt);
    1166  
    1167  	cxt->display_in_cyl_units = 0;
    1168  
    1169  	if (!str)
    1170  		return 0;
    1171  
    1172  	if (strcmp(str, "cylinder") == 0 || strcmp(str, "cylinders") == 0)
    1173  		cxt->display_in_cyl_units = 1;
    1174  
    1175  	else if (strcmp(str, "sector") == 0 || strcmp(str, "sectors") == 0)
    1176  		cxt->display_in_cyl_units = 0;
    1177  
    1178  	DBG(CXT, ul_debugobj(cxt, "display unit: %s", fdisk_get_unit(cxt, 0)));
    1179  	return 0;
    1180  }
    1181  
    1182  /**
    1183   * fdisk_get_unit:
    1184   * @cxt: context
    1185   * @n: FDISK_PLURAL or FDISK_SINGULAR
    1186   *
    1187   * Returns: unit name.
    1188   */
    1189  const char *fdisk_get_unit(struct fdisk_context *cxt, int n)
    1190  {
    1191  	assert(cxt);
    1192  
    1193  	if (fdisk_use_cylinders(cxt))
    1194  		return P_("cylinder", "cylinders", n);
    1195  	return P_("sector", "sectors", n);
    1196  }
    1197  
    1198  /**
    1199   * fdisk_use_cylinders:
    1200   * @cxt: context
    1201   *
    1202   * Returns: 1 if user wants to display in cylinders.
    1203   */
    1204  int fdisk_use_cylinders(struct fdisk_context *cxt)
    1205  {
    1206  	assert(cxt);
    1207  	return cxt->display_in_cyl_units == 1;
    1208  }
    1209  
    1210  /**
    1211   * fdisk_get_units_per_sector:
    1212   * @cxt: context
    1213   *
    1214   * This is necessary only for brain dead situations when we use "cylinders";
    1215   *
    1216   * Returns: number of "units" per sector, default is 1 if display unit is sector.
    1217   */
    1218  unsigned int fdisk_get_units_per_sector(struct fdisk_context *cxt)
    1219  {
    1220  	assert(cxt);
    1221  
    1222  	if (fdisk_use_cylinders(cxt)) {
    1223  		assert(cxt->geom.heads);
    1224  		return cxt->geom.heads * cxt->geom.sectors;
    1225  	}
    1226  	return 1;
    1227  }
    1228  
    1229  /**
    1230   * fdisk_get_optimal_iosize:
    1231   * @cxt: context
    1232   *
    1233   * The optimal I/O is optional and does not have to be provided by device,
    1234   * anyway libfdisk never returns zero. If the optimal I/O size is not provided
    1235   * then libfdisk returns minimal I/O size or sector size.
    1236   *
    1237   * Returns: optimal I/O size in bytes.
    1238   */
    1239  unsigned long fdisk_get_optimal_iosize(struct fdisk_context *cxt)
    1240  {
    1241  	assert(cxt);
    1242  	return cxt->optimal_io_size ? cxt->optimal_io_size : cxt->io_size;
    1243  }
    1244  
    1245  /**
    1246   * fdisk_get_minimal_iosize:
    1247   * @cxt: context
    1248   *
    1249   * Returns: minimal I/O size in bytes
    1250   */
    1251  unsigned long fdisk_get_minimal_iosize(struct fdisk_context *cxt)
    1252  {
    1253  	assert(cxt);
    1254  	return cxt->min_io_size;
    1255  }
    1256  
    1257  /**
    1258   * fdisk_get_physector_size:
    1259   * @cxt: context
    1260   *
    1261   * Returns: physical sector size in bytes
    1262   */
    1263  unsigned long fdisk_get_physector_size(struct fdisk_context *cxt)
    1264  {
    1265  	assert(cxt);
    1266  	return cxt->phy_sector_size;
    1267  }
    1268  
    1269  /**
    1270   * fdisk_get_sector_size:
    1271   * @cxt: context
    1272   *
    1273   * Returns: logical sector size in bytes
    1274   */
    1275  unsigned long fdisk_get_sector_size(struct fdisk_context *cxt)
    1276  {
    1277  	assert(cxt);
    1278  	return cxt->sector_size;
    1279  }
    1280  
    1281  /**
    1282   * fdisk_get_alignment_offset
    1283   * @cxt: context
    1284   *
    1285   * The alignment offset is offset between logical and physical sectors. For
    1286   * backward compatibility the first logical sector on 4K disks does no have to
    1287   * start on the same place like physical sectors.
    1288   *
    1289   * Returns: alignment offset in bytes
    1290   */
    1291  unsigned long fdisk_get_alignment_offset(struct fdisk_context *cxt)
    1292  {
    1293  	assert(cxt);
    1294  	return cxt->alignment_offset;
    1295  }
    1296  
    1297  /**
    1298   * fdisk_get_grain_size:
    1299   * @cxt: context
    1300   *
    1301   * Returns: grain in bytes used to align partitions (usually 1MiB)
    1302   */
    1303  unsigned long fdisk_get_grain_size(struct fdisk_context *cxt)
    1304  {
    1305  	assert(cxt);
    1306  	return cxt->grain;
    1307  }
    1308  
    1309  /**
    1310   * fdisk_get_first_lba:
    1311   * @cxt: context
    1312   *
    1313   * Returns: first possible LBA on disk for data partitions.
    1314   */
    1315  fdisk_sector_t fdisk_get_first_lba(struct fdisk_context *cxt)
    1316  {
    1317  	assert(cxt);
    1318  	return cxt->first_lba;
    1319  }
    1320  
    1321  /**
    1322   * fdisk_set_first_lba:
    1323   * @cxt: fdisk context
    1324   * @lba: first possible logical sector for data
    1325   *
    1326   * It's strongly recommended to use the default library setting. The first LBA
    1327   * is always reset by fdisk_assign_device(), fdisk_override_geometry()
    1328   * and fdisk_reset_alignment(). This is very low level function and library
    1329   * does not check if your setting makes any sense.
    1330   *
    1331   * This function is necessary only when you want to work with very unusual
    1332   * partition tables like GPT protective MBR or hybrid partition tables on
    1333   * bootable media where the first partition may start on very crazy offsets.
    1334   *
    1335   * Note that this function changes only runtime information. It does not update
    1336   * any range in on-disk partition table. For example GPT Header contains First
    1337   * and Last usable LBA fields. These fields are not updated by this function.
    1338   * Be careful.
    1339   *
    1340   * Returns: 0 on success, <0 on error.
    1341   */
    1342  fdisk_sector_t fdisk_set_first_lba(struct fdisk_context *cxt, fdisk_sector_t lba)
    1343  {
    1344  	assert(cxt);
    1345  	DBG(CXT, ul_debugobj(cxt, "setting first LBA from %ju to %ju",
    1346  			(uintmax_t) cxt->first_lba, (uintmax_t) lba));
    1347  	cxt->first_lba = lba;
    1348  	return 0;
    1349  }
    1350  
    1351  /**
    1352   * fdisk_get_last_lba:
    1353   * @cxt: fdisk context
    1354   *
    1355   * Note that the device has to be already assigned.
    1356   *
    1357   * Returns: last possible LBA on device
    1358   */
    1359  fdisk_sector_t fdisk_get_last_lba(struct fdisk_context *cxt)
    1360  {
    1361  	return cxt->last_lba;
    1362  }
    1363  
    1364  /**
    1365   * fdisk_set_last_lba:
    1366   * @cxt: fdisk context
    1367   * @lba: last possible logical sector
    1368   *
    1369   * It's strongly recommended to use the default library setting. The last LBA
    1370   * is always reset by fdisk_assign_device(), fdisk_override_geometry() and
    1371   * fdisk_reset_alignment().
    1372   *
    1373   * The default is number of sectors on the device, but maybe modified by the
    1374   * current disklabel driver (for example GPT uses the end of disk for backup
    1375   * header, so last_lba is smaller than total number of sectors).
    1376   *
    1377   * Returns: 0 on success, <0 on error.
    1378   */
    1379  fdisk_sector_t fdisk_set_last_lba(struct fdisk_context *cxt, fdisk_sector_t lba)
    1380  {
    1381  	assert(cxt);
    1382  
    1383  	if (lba > cxt->total_sectors - 1 || lba < 1)
    1384  		return -ERANGE;
    1385  	cxt->last_lba = lba;
    1386  	return 0;
    1387  }
    1388  
    1389  /**
    1390   * fdisk_set_size_unit:
    1391   * @cxt: fdisk context
    1392   * @unit: FDISK_SIZEUNIT_*
    1393   *
    1394   * Sets unit for SIZE output field (see fdisk_partition_to_string()).
    1395   *
    1396   * Returns: 0 on success, <0 on error.
    1397   */
    1398  int fdisk_set_size_unit(struct fdisk_context *cxt, int unit)
    1399  {
    1400  	assert(cxt);
    1401  	cxt->sizeunit = unit;
    1402  	return 0;
    1403  }
    1404  
    1405  /**
    1406   * fdisk_get_size_unit:
    1407   * @cxt: fdisk context
    1408   *
    1409   * Gets unit for SIZE output field (see fdisk_partition_to_string()).
    1410   *
    1411   * Returns: unit
    1412   */
    1413  int fdisk_get_size_unit(struct fdisk_context *cxt)
    1414  {
    1415  	assert(cxt);
    1416  	return cxt->sizeunit;
    1417  }
    1418  
    1419  /**
    1420   * fdisk_get_nsectors:
    1421   * @cxt: context
    1422   *
    1423   * Returns: size of the device in logical sectors.
    1424   */
    1425  fdisk_sector_t fdisk_get_nsectors(struct fdisk_context *cxt)
    1426  {
    1427  	assert(cxt);
    1428  	return cxt->total_sectors;
    1429  }
    1430  
    1431  /**
    1432   * fdisk_get_devname:
    1433   * @cxt: context
    1434   *
    1435   * Returns: device name.
    1436   */
    1437  const char *fdisk_get_devname(struct fdisk_context *cxt)
    1438  {
    1439  	assert(cxt);
    1440  	return cxt->dev_path;
    1441  }
    1442  
    1443  /**
    1444   * fdisk_get_devno:
    1445   * @cxt: context
    1446   *
    1447   * Returns: device number or zero for non-block devices
    1448   *
    1449   * Since: 2.33
    1450   */
    1451  dev_t fdisk_get_devno(struct fdisk_context *cxt)
    1452  {
    1453  	assert(cxt);
    1454  	return S_ISBLK(cxt->dev_st.st_mode) ? cxt->dev_st.st_rdev : 0;
    1455  }
    1456  
    1457  /**
    1458   * fdisk_get_devmodel:
    1459   * @cxt: context
    1460   *
    1461   * Returns: device model string or NULL.
    1462   *
    1463   * Since: 2.33
    1464   */
    1465  #ifdef __linux__
    1466  const char *fdisk_get_devmodel(struct fdisk_context *cxt)
    1467  {
    1468  	assert(cxt);
    1469  
    1470  	if (cxt->dev_model_probed)
    1471  		return cxt->dev_model;
    1472  
    1473  	if (fdisk_get_devno(cxt)) {
    1474  		struct path_cxt *pc = ul_new_sysfs_path(fdisk_get_devno(cxt), NULL, NULL);
    1475  
    1476  		if (pc) {
    1477  			ul_path_read_string(pc, &cxt->dev_model, "device/model");
    1478  			ul_unref_path(pc);
    1479  		}
    1480  	}
    1481  	cxt->dev_model_probed = 1;
    1482  	return cxt->dev_model;
    1483  }
    1484  #else
    1485  const char *fdisk_get_devmodel(struct fdisk_context *cxt __attribute__((__unused__)))
    1486  {
    1487  	return NULL;
    1488  }
    1489  #endif
    1490  
    1491  /**
    1492   * fdisk_get_devfd:
    1493   * @cxt: context
    1494   *
    1495   * Returns: device file descriptor.
    1496   */
    1497  int fdisk_get_devfd(struct fdisk_context *cxt)
    1498  {
    1499  	assert(cxt);
    1500  	return cxt->dev_fd;
    1501  }
    1502  
    1503  /**
    1504   * fdisk_get_geom_heads:
    1505   * @cxt: context
    1506   *
    1507   * Returns: number of geometry heads.
    1508   */
    1509  unsigned int fdisk_get_geom_heads(struct fdisk_context *cxt)
    1510  {
    1511  	assert(cxt);
    1512  	return cxt->geom.heads;
    1513  }
    1514  /**
    1515   * fdisk_get_geom_sectors:
    1516   * @cxt: context
    1517   *
    1518   * Returns: number of geometry sectors.
    1519   */
    1520  fdisk_sector_t fdisk_get_geom_sectors(struct fdisk_context *cxt)
    1521  {
    1522  	assert(cxt);
    1523  	return cxt->geom.sectors;
    1524  
    1525  }
    1526  
    1527  /**
    1528   * fdisk_get_geom_cylinders:
    1529   * @cxt: context
    1530   *
    1531   * Returns: number of geometry cylinders
    1532   */
    1533  fdisk_sector_t fdisk_get_geom_cylinders(struct fdisk_context *cxt)
    1534  {
    1535  	assert(cxt);
    1536  	return cxt->geom.cylinders;
    1537  }
    1538  
    1539  int fdisk_missing_geometry(struct fdisk_context *cxt)
    1540  {
    1541  	int rc;
    1542  
    1543  	if (!cxt || !cxt->label)
    1544  		return 0;
    1545  
    1546  	rc = (fdisk_label_require_geometry(cxt->label) &&
    1547  		    (!cxt->geom.heads || !cxt->geom.sectors
    1548  				      || !cxt->geom.cylinders));
    1549  
    1550  	if (rc && !fdisk_is_listonly(cxt))
    1551  		fdisk_warnx(cxt, _("Incomplete geometry setting."));
    1552  
    1553  	return rc;
    1554  }
    1555