Update blkid to 2.25.0
Break libblkid into 4 libraries: libblkid, libuuid, libutil-linux and libfdisk.

This should help in later patch updates.

Change-Id: I680d9a7feb031e5c29a603e9c58aff4b65826262
diff --git a/libblkid/libfdisk/src/context.c b/libblkid/libfdisk/src/context.c
new file mode 100644
index 0000000..2a4d377
--- /dev/null
+++ b/libblkid/libfdisk/src/context.c
@@ -0,0 +1,1017 @@
+#ifdef HAVE_LIBBLKID
+# include <blkid.h>
+#endif
+
+#include "fdiskP.h"
+
+
+/**
+ * SECTION: context
+ * @title: Context
+ * @short_description: stores info about device, labels etc.
+ *
+ * The library distinguish between three types of partitioning objects.
+ *
+ * on-disk data
+ *    - disk label specific
+ *    - probed and read  by disklabel drivers when assign device to the context
+ *      or when switch to another disk label type
+ *    - only fdisk_write_disklabel() modify on-disk data
+ *
+ * in-memory data
+ *    - generic data and disklabel specific data stored in struct fdisk_label
+ *    - all partitioning operations are based on in-memory data only
+ *
+ * struct fdisk_partition
+ *    - provides abstraction to present partitions to users
+ *    - fdisk_partition is possible to gather to fdisk_table container
+ *    - used as unified template for new partitions
+ *    - the struct fdisk_partition is always completely independent object and
+ *      any change to the object has no effect to in-memory (or on-disk) label data
+ */
+
+/**
+ * fdisk_new_context:
+ *
+ * Returns: newly allocated libfdisk handler
+ */
+struct fdisk_context *fdisk_new_context(void)
+{
+	struct fdisk_context *cxt;
+
+	cxt = calloc(1, sizeof(*cxt));
+	if (!cxt)
+		return NULL;
+
+	DBG(CXT, ul_debugobj(cxt, "alloc"));
+	cxt->dev_fd = -1;
+	cxt->refcount = 1;
+
+	/*
+	 * Allocate label specific structs.
+	 *
+	 * This is necessary (for example) to store label specific
+	 * context setting.
+	 */
+	cxt->labels[ cxt->nlabels++ ] = fdisk_new_gpt_label(cxt);
+	cxt->labels[ cxt->nlabels++ ] = fdisk_new_dos_label(cxt);
+	cxt->labels[ cxt->nlabels++ ] = fdisk_new_bsd_label(cxt);
+	cxt->labels[ cxt->nlabels++ ] = fdisk_new_sgi_label(cxt);
+	cxt->labels[ cxt->nlabels++ ] = fdisk_new_sun_label(cxt);
+
+	return cxt;
+}
+
+static int init_nested_from_parent(struct fdisk_context *cxt, int isnew)
+{
+	struct fdisk_context *parent;
+
+	assert(cxt);
+	assert(cxt->parent);
+
+	parent = cxt->parent;
+
+	cxt->alignment_offset = parent->alignment_offset;
+	cxt->ask_cb =		parent->ask_cb;
+	cxt->ask_data =		parent->ask_data;
+	cxt->dev_fd =		parent->dev_fd;
+	cxt->first_lba =        parent->first_lba;
+	cxt->firstsector_bufsz = parent->firstsector_bufsz;
+	cxt->firstsector =	parent->firstsector;
+	cxt->geom =		parent->geom;
+	cxt->grain =            parent->grain;
+	cxt->io_size =          parent->io_size;
+	cxt->last_lba =		parent->last_lba;
+	cxt->min_io_size =      parent->min_io_size;
+	cxt->optimal_io_size =  parent->optimal_io_size;
+	cxt->phy_sector_size =  parent->phy_sector_size;
+	cxt->readonly =		parent->readonly;
+	cxt->script =		parent->script;
+	fdisk_ref_script(cxt->script);
+	cxt->sector_size =      parent->sector_size;
+	cxt->total_sectors =    parent->total_sectors;
+	cxt->user_geom =	parent->user_geom;
+	cxt->user_log_sector =	parent->user_log_sector;
+	cxt->user_pyh_sector =  parent->user_pyh_sector;
+
+	/* parent <--> nested independent setting, initialize for new nested 
+	 * contexts only */
+	if (isnew) {
+		cxt->listonly =	parent->listonly;
+		cxt->display_details =	parent->display_details;
+		cxt->display_in_cyl_units = parent->display_in_cyl_units;
+	}
+
+	free(cxt->dev_path);
+	cxt->dev_path = NULL;
+
+	if (parent->dev_path) {
+		cxt->dev_path =	strdup(parent->dev_path);
+		if (!cxt->dev_path)
+			return -ENOMEM;
+	}
+
+	return 0;
+}
+
+/**
+ * fdisk_new_nested_context:
+ * @parent: parental context
+ * @name: optional label name (e.g. "bsd")
+ *
+ * Create a new nested fdisk context for nested disk labels (e.g. BSD or PMBR).
+ * The function also probes for the nested label on the device if device is
+ * already assigned to parent.
+ *
+ * The new context is initialized according to @parent and both context shares
+ * some settings and file descriptor to the device. The child propagate some
+ * changes (like fdisk_assign_device()) to parent, but it does not work
+ * vice-versa. The behavior is undefined if you assign another device to
+ * parent.
+ *
+ * Returns: new context for nested partition table.
+ */
+struct fdisk_context *fdisk_new_nested_context(struct fdisk_context *parent,
+				const char *name)
+{
+	struct fdisk_context *cxt;
+	struct fdisk_label *lb = NULL;
+
+	assert(parent);
+
+	cxt = calloc(1, sizeof(*cxt));
+	if (!cxt)
+		return NULL;
+
+	DBG(CXT, ul_debugobj(parent, "alloc nested [%p]", cxt));
+	cxt->refcount = 1;
+
+	fdisk_ref_context(parent);
+	cxt->parent = parent;
+
+	if (init_nested_from_parent(cxt, 1) != 0)
+		return NULL;
+
+	if (name) {
+		if (strcmp(name, "bsd") == 0)
+			lb = cxt->labels[ cxt->nlabels++ ] = fdisk_new_bsd_label(cxt);
+		else if (strcmp(name, "dos") == 0)
+			lb = cxt->labels[ cxt->nlabels++ ] = fdisk_new_dos_label(cxt);
+	}
+
+	if (lb && parent->dev_fd >= 0) {
+		DBG(CXT, ul_debugobj(cxt, "probing for nested %s", lb->name));
+
+		cxt->label = lb;
+
+		if (lb->op->probe(cxt) == 1)
+			__fdisk_switch_label(cxt, lb);
+		else {
+			DBG(CXT, ul_debugobj(cxt, "not found %s label", lb->name));
+			if (lb->op->deinit)
+				lb->op->deinit(lb);
+			cxt->label = NULL;
+		}
+	}
+
+	return cxt;
+}
+
+
+/**
+ * fdisk_ref_context:
+ * @cxt: context pointer
+ *
+ * Increments reference counter.
+ */
+void fdisk_ref_context(struct fdisk_context *cxt)
+{
+	if (cxt)
+		cxt->refcount++;
+}
+
+/**
+ * fdisk_get_label:
+ * @cxt: context instance
+ * @name: label name (e.g. "gpt")
+ *
+ * If no @name specified then returns the current context label.
+ *
+ * The label is allocated and maintained within the context #cxt. There is
+ * nothing like reference counting for labels, you cannot delallocate the
+ * label.
+ *
+ * Returns: label struct or NULL in case of error.
+ */
+struct fdisk_label *fdisk_get_label(struct fdisk_context *cxt, const char *name)
+{
+	size_t i;
+
+	assert(cxt);
+
+	if (!name)
+		return cxt->label;
+
+	for (i = 0; i < cxt->nlabels; i++)
+		if (cxt->labels[i]
+		    && strcmp(cxt->labels[i]->name, name) == 0)
+			return cxt->labels[i];
+
+	DBG(CXT, ul_debugobj(cxt, "failed to found %s label driver", name));
+	return NULL;
+}
+
+/**
+ * fdisk_next_label:
+ * @cxt: context instance
+ * @lb: returns pointer to the next label
+ *
+ * <informalexample>
+ *   <programlisting>
+ *      // print all supported labels
+ *	struct fdisk_context *cxt = fdisk_new_context();
+ *	struct fdisk_label *lb = NULL;
+ *
+ *	while (fdisk_next_label(cxt, &lb) == 0)
+ *		print("label name: %s\n", fdisk_label_get_name(lb));
+ *	fdisk_unref_context(cxt);
+ *   </programlisting>
+ * </informalexample>
+ *
+ * Returns: <0 in case of error, 0 on success, 1 at the end.
+ */
+int fdisk_next_label(struct fdisk_context *cxt, struct fdisk_label **lb)
+{
+	size_t i;
+	struct fdisk_label *res = NULL;
+
+	if (!lb || !cxt)
+		return -EINVAL;
+
+	if (!*lb)
+		res = cxt->labels[0];
+	else {
+		for (i = 1; i < cxt->nlabels; i++) {
+			if (*lb == cxt->labels[i - 1]) {
+				res = cxt->labels[i];
+				break;
+			}
+		}
+	}
+
+	*lb = res;
+	return res ? 0 : 1;
+}
+
+/**
+ * fdisk_get_nlabels:
+ * @cxt: context
+ *
+ * Returns: number of supported label types
+ */
+size_t fdisk_get_nlabels(struct fdisk_context *cxt)
+{
+	return cxt ? cxt->nlabels : 0;
+}
+
+int __fdisk_switch_label(struct fdisk_context *cxt, struct fdisk_label *lb)
+{
+	if (!lb || !cxt)
+		return -EINVAL;
+	if (lb->disabled) {
+		DBG(CXT, ul_debugobj(cxt, "*** attempt to switch to disabled label %s -- ignore!", lb->name));
+		return -EINVAL;
+	}
+	cxt->label = lb;
+	DBG(CXT, ul_debugobj(cxt, "--> switching context to %s!", lb->name));
+	return 0;
+}
+
+/**
+ * fdisk_has_label:
+ * @cxt: fdisk context
+ *
+ * Returns: return 1 if there is label on the device.
+ */
+int fdisk_has_label(struct fdisk_context *cxt)
+{
+	return cxt && cxt->label;
+}
+
+/**
+ * fdisk_get_npartitions:
+ * @cxt: context
+ *
+ * The maximal number of the partitions depends on disklabel and does not
+ * have to describe the real limit of PT.
+ *
+ * For example the limit for MBR without extend partition is 4, with extended
+ * partition it's unlimited (so the function returns the current number of all
+ * partitions in this case).
+ *
+ * And for example for GPT it depends on space allocated on disk for array of
+ * entry records (usually 128).
+ *
+ * It's fine to use fdisk_get_npartitions() in loops, but don't forget that
+ * partition may be unused (see fdisk_is_partition_used()).
+ *
+ * <informalexample>
+ *   <programlisting>
+ *	struct fdisk_partition *pa = NULL;
+ *	size_t i, nmax = fdisk_get_npartitions(cxt);
+ *
+ *	for (i = 0; i < nmax; i++) {
+ *		if (!fdisk_is_partition_used(cxt, i))
+ *			continue;
+ *		... do something ...
+ *	}
+ *   </programlisting>
+ * </informalexample>
+ *
+ * Note that the recommended way to list partitions is to use
+ * fdisk_get_partitions() and struct fdisk_table than ask disk driver for each
+ * individual partitions.
+ *
+ * Returns: maximal number of partitions for the current label.
+ */
+size_t fdisk_get_npartitions(struct fdisk_context *cxt)
+{
+	return cxt && cxt->label ? cxt->label->nparts_max : 0;
+}
+
+/**
+ * fdisk_is_labeltype:
+ * @cxt: fdisk context
+ * @id: FDISK_DISKLABEL_*
+ *
+ * See also fdisk_is_label() macro in libfdisk.h.
+ *
+ * Returns: return 1 if the current label is @id
+ */
+int fdisk_is_labeltype(struct fdisk_context *cxt, enum fdisk_labeltype id)
+{
+	assert(cxt);
+
+	return cxt->label && fdisk_label_get_type(cxt->label) == id;
+}
+
+/**
+ * fdisk_get_parent:
+ * @cxt: nested fdisk context
+ *
+ * Returns: pointer to parental context, or NULL
+ */
+struct fdisk_context *fdisk_get_parent(struct fdisk_context *cxt)
+{
+	assert(cxt);
+	return cxt->parent;
+}
+
+static void reset_context(struct fdisk_context *cxt)
+{
+	size_t i;
+
+	DBG(CXT, ul_debugobj(cxt, "*** resetting context"));
+
+	/* reset drives' private data */
+	for (i = 0; i < cxt->nlabels; i++)
+		fdisk_deinit_label(cxt->labels[i]);
+
+	if (cxt->parent) {
+		/* the first sector may be independent on parent */
+		if (cxt->parent->firstsector != cxt->firstsector)
+			free(cxt->firstsector);
+	} else {
+		/* we close device only in primary context */
+		if (cxt->dev_fd > -1)
+			close(cxt->dev_fd);
+		free(cxt->firstsector);
+	}
+
+	free(cxt->dev_path);
+	cxt->dev_path = NULL;
+
+	cxt->dev_fd = -1;
+	cxt->firstsector = NULL;
+	cxt->firstsector_bufsz = 0;
+
+	fdisk_zeroize_device_properties(cxt);
+
+	fdisk_unref_script(cxt->script);
+	cxt->script = NULL;
+
+	cxt->label = NULL;
+}
+
+/*
+ * This function prints a warning if the device is not wiped (e.g. wipefs(8).
+ * Please don't call this function if there is already a PT.
+ *
+ * Returns: 0 if nothing found, < 0 on error, 1 if found a signature
+ */
+static int warn_wipe(struct fdisk_context *cxt)
+{
+#ifdef HAVE_LIBBLKID
+	blkid_probe pr;
+#endif
+	int rc = 0;
+
+	assert(cxt);
+
+	if (fdisk_has_label(cxt) || cxt->dev_fd < 0)
+		return -EINVAL;
+#ifdef HAVE_LIBBLKID
+	DBG(CXT, ul_debugobj(cxt, "wipe check: initialize libblkid prober"));
+
+	pr = blkid_new_probe();
+	if (!pr)
+		return -ENOMEM;
+	rc = blkid_probe_set_device(pr, cxt->dev_fd, 0, 0);
+	if (rc)
+		return rc;
+
+	blkid_probe_enable_superblocks(pr, 1);
+	blkid_probe_set_superblocks_flags(pr, BLKID_SUBLKS_TYPE);
+	blkid_probe_enable_partitions(pr, 1);
+
+	/* we care about the first found FS/raid, so don't call blkid_do_probe()
+	 * in loop or don't use blkid_do_fullprobe() ... */
+	rc = blkid_do_probe(pr);
+	if (rc == 0) {
+		const char *name = NULL;
+
+		if (blkid_probe_lookup_value(pr, "TYPE", &name, 0) == 0 ||
+		    blkid_probe_lookup_value(pr, "PTTYPE", &name, 0) == 0) {
+			fdisk_warnx(cxt, _(
+				"%s: device contains a valid '%s' signature; it is "
+				"strongly recommended to wipe the device with "
+				"wipefs(8) if this is unexpected, in order to "
+				"avoid possible collisions"), cxt->dev_path, name);
+			rc = 1;
+		}
+	}
+
+	blkid_free_probe(pr);
+#endif
+	return rc;
+}
+
+/**
+ * fdisk_assign_device:
+ * @cxt: context
+ * @fname: path to the device to be handled
+ * @readonly: how to open the device
+ *
+ * Open the device, discovery topology, geometry, detect disklabel and switch
+ * the current label driver to reflect the probing result.
+ *
+ * Note that this function resets all generic setting in context. If the @cxt
+ * is nested context then the device is assigned to the parental context and
+ * necessary properties are copied to the @cxt. The change is propagated in
+ * child->parent direction only. It's impossible to use a different device for
+ * primary and nested contexts.
+ *
+ * Returns: 0 on success, < 0 on error.
+ */
+int fdisk_assign_device(struct fdisk_context *cxt,
+			const char *fname, int readonly)
+{
+	int fd;
+
+	DBG(CXT, ul_debugobj(cxt, "assigning device %s", fname));
+	assert(cxt);
+
+	/* redirect request to parent */
+	if (cxt->parent) {
+		int rc, org = fdisk_is_listonly(cxt->parent);
+
+		/* assign_device() is sensitive to "listonly" mode, so let's
+		 * follow the current context setting for the parent to avoid 
+		 * unwanted extra warnings. */
+		fdisk_enable_listonly(cxt->parent, fdisk_is_listonly(cxt));
+
+		rc = fdisk_assign_device(cxt->parent, fname, readonly);
+		fdisk_enable_listonly(cxt->parent, org);
+
+		if (!rc)
+			rc = init_nested_from_parent(cxt, 0);
+		if (!rc)
+			fdisk_probe_labels(cxt);
+		return rc;
+	}
+
+	reset_context(cxt);
+
+	fd = open(fname, (readonly ? O_RDONLY : O_RDWR ) | O_CLOEXEC);
+	if (fd < 0)
+		return -errno;
+
+	cxt->readonly = readonly;
+	cxt->dev_fd = fd;
+	cxt->dev_path = strdup(fname);
+	if (!cxt->dev_path)
+		goto fail;
+
+	fdisk_discover_topology(cxt);
+	fdisk_discover_geometry(cxt);
+
+	if (fdisk_read_firstsector(cxt) < 0)
+		goto fail;
+
+	/* detect labels and apply labes specific stuff (e.g geomery)
+	 * to the context */
+	fdisk_probe_labels(cxt);
+
+	/* let's apply user geometry *after* label prober
+	 * to make it possible to override in-label setting */
+	fdisk_apply_user_device_properties(cxt);
+
+	/* warn about obsolete stuff on the device if we aren't in
+	 * list-only mode and there is not PT yet */
+	if (!fdisk_is_listonly(cxt) && !fdisk_has_label(cxt))
+		warn_wipe(cxt);
+
+	DBG(CXT, ul_debugobj(cxt, "initialized for %s [%s]",
+			      fname, readonly ? "READ-ONLY" : "READ-WRITE"));
+	return 0;
+fail:
+	DBG(CXT, ul_debugobj(cxt, "failed to assign device"));
+	return -errno;
+}
+
+/**
+ * fdisk_deassign_device:
+ * @cxt: context
+ * @nosync: disable fsync()
+ *
+ * Close device and call fsync(). If the @cxt is nested context than the
+ * request is redirected to the parent.
+ *
+ * Returns: 0 on success, < 0 on error.
+ */
+int fdisk_deassign_device(struct fdisk_context *cxt, int nosync)
+{
+	assert(cxt);
+	assert(cxt->dev_fd >= 0);
+
+	if (cxt->parent) {
+		int rc = fdisk_deassign_device(cxt->parent, nosync);
+
+		if (!rc)
+			rc = init_nested_from_parent(cxt, 0);
+		return rc;
+	}
+
+	if (cxt->readonly)
+		close(cxt->dev_fd);
+	else {
+		if (fsync(cxt->dev_fd) || close(cxt->dev_fd)) {
+			fdisk_warn(cxt, _("%s: close device failed"),
+					cxt->dev_path);
+			return -errno;
+		}
+
+		if (!nosync) {
+			fdisk_info(cxt, _("Syncing disks."));
+			sync();
+		}
+	}
+
+	free(cxt->dev_path);
+	cxt->dev_path = NULL;
+
+	cxt->dev_fd = -1;
+
+	return 0;
+}
+
+/**
+ * fdisk_is_readonly:
+ * @cxt: context
+ *
+ * Returns: 1 if device open readonly
+ */
+int fdisk_is_readonly(struct fdisk_context *cxt)
+{
+	assert(cxt);
+	return cxt->readonly;
+}
+
+/**
+ * fdisk_unref_context:
+ * @cxt: fdisk context
+ *
+ * Deallocates context struct.
+ */
+void fdisk_unref_context(struct fdisk_context *cxt)
+{
+	int i;
+
+	if (!cxt)
+		return;
+
+	cxt->refcount--;
+	if (cxt->refcount <= 0) {
+		DBG(CXT, ul_debugobj(cxt, "freeing context %p for %s", cxt, cxt->dev_path));
+
+		reset_context(cxt);	/* this is sensitive to parent<->child relationship! */
+
+		/* deallocate label's private stuff */
+		for (i = 0; i < cxt->nlabels; i++) {
+			if (!cxt->labels[i])
+				continue;
+			if (cxt->labels[i]->op->free)
+				cxt->labels[i]->op->free(cxt->labels[i]);
+			else
+				free(cxt->labels[i]);
+		}
+
+		fdisk_unref_context(cxt->parent);
+		cxt->parent = NULL;
+
+		free(cxt);
+	}
+}
+
+
+/**
+ * fdisk_enable_details:
+ * @cxt: context
+ * @enable: true/flase
+ *
+ * Enables or disables "details" display mode. This function has effect to
+ * fdisk_partition_to_string() function.
+ *
+ * Returns: 0 on success, < 0 on error.
+ */
+int fdisk_enable_details(struct fdisk_context *cxt, int enable)
+{
+	assert(cxt);
+	cxt->display_details = enable ? 1 : 0;
+	return 0;
+}
+
+/**
+ * fdisk_is_details:
+ * @cxt: context
+ *
+ * Returns: 1 if details are enabled
+ */
+int fdisk_is_details(struct fdisk_context *cxt)
+{
+	assert(cxt);
+	return cxt->display_details == 1;
+}
+
+/**
+ * fdisk_enable_listonly:
+ * @cxt: context
+ * @enable: true/flase
+ *
+ * Just list partition only, don't care about another details, mistakes, ...
+ *
+ * Returns: 0 on success, < 0 on error.
+ */
+int fdisk_enable_listonly(struct fdisk_context *cxt, int enable)
+{
+	assert(cxt);
+	cxt->listonly = enable ? 1 : 0;
+	return 0;
+}
+
+/**
+ * fdisk_is_listonly:
+ * @cxt: context
+ *
+ * Returns: 1 if list-only mode enabled
+ */
+int fdisk_is_listonly(struct fdisk_context *cxt)
+{
+	assert(cxt);
+	return cxt->listonly == 1;
+}
+
+
+/**
+ * fdisk_set_unit:
+ * @cxt: context
+ * @str: "cylinder" or "sector".
+ *
+ * This is pure shit, unfortunately for example Sun addresses begin of the
+ * partition by cylinders...
+ *
+ * Returns: 0 on succes, <0 on error.
+ */
+int fdisk_set_unit(struct fdisk_context *cxt, const char *str)
+{
+	assert(cxt);
+
+	cxt->display_in_cyl_units = 0;
+
+	if (!str)
+		return 0;
+
+	if (strcmp(str, "cylinder") == 0 || strcmp(str, "cylinders") == 0)
+		cxt->display_in_cyl_units = 1;
+
+	else if (strcmp(str, "sector") == 0 || strcmp(str, "sectors") == 0)
+		cxt->display_in_cyl_units = 0;
+
+	DBG(CXT, ul_debugobj(cxt, "display unit: %s", fdisk_get_unit(cxt, 0)));
+	return 0;
+}
+
+/**
+ * fdisk_get_unit:
+ * @cxt: context
+ * @n: FDISK_PLURAL or FDISK_SINGULAR
+ *
+ * Returns: unit name.
+ */
+const char *fdisk_get_unit(struct fdisk_context *cxt, int n)
+{
+	assert(cxt);
+
+	if (fdisk_use_cylinders(cxt))
+		return P_("cylinder", "cylinders", n);
+	return P_("sector", "sectors", n);
+}
+
+/**
+ * fdisk_use_cylinders:
+ * @cxt: context
+ *
+ * Returns: 1 if user wants to display in cylinders.
+ */
+int fdisk_use_cylinders(struct fdisk_context *cxt)
+{
+	assert(cxt);
+	return cxt->display_in_cyl_units == 1;
+}
+
+/**
+ * fdisk_get_units_per_sector:
+ * @cxt: context
+ *
+ * This is necessary only for brain dead situations when we use "cylinders";
+ *
+ * Returns: number of "units" per sector, default is 1 if display unit is sector.
+ */
+unsigned int fdisk_get_units_per_sector(struct fdisk_context *cxt)
+{
+	assert(cxt);
+
+	if (fdisk_use_cylinders(cxt)) {
+		assert(cxt->geom.heads);
+		return cxt->geom.heads * cxt->geom.sectors;
+	}
+	return 1;
+}
+
+/**
+ * fdisk_get_optimal_iosize:
+ * @cxt: context
+ *
+ * The optimal I/O is optional and does not have to be provided by device,
+ * anyway libfdisk never returns zero. If the optimal I/O size is not provided
+ * then libfdisk returns minimal I/O size or sector size.
+ *
+ * Returns: optimal I/O size in bytes.
+ */
+unsigned long fdisk_get_optimal_iosize(struct fdisk_context *cxt)
+{
+	assert(cxt);
+	return cxt->optimal_io_size ? cxt->optimal_io_size : cxt->io_size;
+}
+
+/**
+ * fdisk_get_minimal_iosize:
+ * @cxt: context
+ *
+ * Returns: minimal I/O size in bytes
+ */
+unsigned long fdisk_get_minimal_iosize(struct fdisk_context *cxt)
+{
+	assert(cxt);
+	return cxt->min_io_size;
+}
+
+/**
+ * fdisk_get_physector_size:
+ * @cxt: context
+ *
+ * Returns: physical sector size in bytes
+ */
+unsigned long fdisk_get_physector_size(struct fdisk_context *cxt)
+{
+	assert(cxt);
+	return cxt->phy_sector_size;
+}
+
+/**
+ * fdisk_get_sector_size:
+ * @cxt: context
+ *
+ * Returns: logical sector size in bytes
+ */
+unsigned long fdisk_get_sector_size(struct fdisk_context *cxt)
+{
+	assert(cxt);
+	return cxt->sector_size;
+}
+
+/**
+ * fdisk_get_alignment_offset
+ * @cxt: context
+ *
+ * The alignment offset is offset between logical and physical sectors. For
+ * backward compatibility the first logical sector on 4K disks does no have to
+ * start on the same place like physical sectors.
+ *
+ * Returns: alignment offset in bytes
+ */
+unsigned long fdisk_get_alignment_offset(struct fdisk_context *cxt)
+{
+	assert(cxt);
+	return cxt->alignment_offset;
+}
+
+/**
+ * fdisk_get_grain_size:
+ * @cxt: context
+ *
+ * Returns: grain in bytes used to align partitions (usually 1MiB)
+ */
+unsigned long fdisk_get_grain_size(struct fdisk_context *cxt)
+{
+	assert(cxt);
+	return cxt->grain;
+}
+
+/**
+ * fdisk_get_first_lba:
+ * @cxt: context
+ *
+ * Returns: first possible LBA on disk for data partitions.
+ */
+fdisk_sector_t fdisk_get_first_lba(struct fdisk_context *cxt)
+{
+	assert(cxt);
+	return cxt->first_lba;
+}
+
+/**
+ * fdisk_set_first_lba:
+ * @cxt: fdisk context
+ * @lba: first possible logical sector for data
+ *
+ * It's strongly recommended to use the default library setting. The first LBA
+ * is always reseted by fdisk_assign_device(), fdisk_override_geometry()
+ * and fdisk_reset_alignment(). This is very low level function and library
+ * does not check if your setting makes any sense.
+ *
+ * This function is necessary only when you want to work with very unusual
+ * partition tables like GPT protective MBR or hybrid partition tables on
+ * bootable media where the first partition may start on very crazy offsets.
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+fdisk_sector_t fdisk_set_first_lba(struct fdisk_context *cxt, fdisk_sector_t lba)
+{
+	assert(cxt);
+	DBG(CXT, ul_debugobj(cxt, "setting first LBA from %ju to %ju",
+			(uintmax_t) cxt->first_lba, (uintmax_t) lba));
+	cxt->first_lba = lba;
+	return 0;
+}
+
+/**
+ * fdisk_get_last_lba:
+ * @cxt: fdisk context
+ *
+ * Note that the device has to be already assigned.
+ *
+ * Returns: last possible LBA on device
+ */
+fdisk_sector_t fdisk_get_last_lba(struct fdisk_context *cxt)
+{
+	return cxt->last_lba;
+}
+
+/**
+ * fdisk_set_last_lba:
+ * @cxt: fdisk context
+ * @lba: last possible logical sector
+ *
+ * It's strongly recommended to use the default library setting. The last LBA
+ * is always reseted by fdisk_assign_device(), fdisk_override_geometry() and
+ * fdisk_reset_alignment().
+ *
+ * The default is number of sectors on the device, but maybe modified by the
+ * current disklabel driver (for example GPT uses and of disk for backup
+ * header, so last_lba is smaller than total number of sectors).
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+fdisk_sector_t fdisk_set_last_lba(struct fdisk_context *cxt, fdisk_sector_t lba)
+{
+	assert(cxt);
+
+	if (lba > cxt->total_sectors - 1 && lba < 1)
+		return -ERANGE;
+	cxt->last_lba = lba;
+	return 0;
+}
+
+
+/**
+ * fdisk_get_nsectors:
+ * @cxt: context
+ *
+ * Returns: size of the device in logical sectors.
+ */
+fdisk_sector_t fdisk_get_nsectors(struct fdisk_context *cxt)
+{
+	assert(cxt);
+	return cxt->total_sectors;
+}
+
+/**
+ * fdisk_get_devname:
+ * @cxt: context
+ *
+ * Returns: device name.
+ */
+const char *fdisk_get_devname(struct fdisk_context *cxt)
+{
+	assert(cxt);
+	return cxt->dev_path;
+}
+
+/**
+ * fdisk_get_devfd:
+ * @cxt: context
+ *
+ * Retruns: device file descriptor.
+ */
+int fdisk_get_devfd(struct fdisk_context *cxt)
+{
+	assert(cxt);
+	return cxt->dev_fd;
+}
+
+/**
+ * fdisk_get_geom_heads:
+ * @cxt: context
+ *
+ * Returns: number of geometry heads.
+ */
+unsigned int fdisk_get_geom_heads(struct fdisk_context *cxt)
+{
+	assert(cxt);
+	return cxt->geom.heads;
+}
+/**
+ * fdisk_get_geom_sectors:
+ * @cxt: context
+ *
+ * Returns: number of geometry sectors.
+ */
+fdisk_sector_t fdisk_get_geom_sectors(struct fdisk_context *cxt)
+{
+	assert(cxt);
+	return cxt->geom.sectors;
+
+}
+
+/**
+ * fdisk_get_geom_cylinders:
+ * @cxt: context
+ *
+ * Returns: number of geometry cylinders
+ */
+fdisk_sector_t fdisk_get_geom_cylinders(struct fdisk_context *cxt)
+{
+	assert(cxt);
+	return cxt->geom.cylinders;
+}
+
+int fdisk_missing_geometry(struct fdisk_context *cxt)
+{
+	int rc;
+
+	assert(cxt);
+
+	if (!cxt || !cxt->label)
+		return 0;
+
+	rc = (fdisk_label_require_geometry(cxt->label) &&
+		    (!cxt->geom.heads || !cxt->geom.sectors
+				      || !cxt->geom.cylinders));
+
+	if (rc && !fdisk_is_listonly(cxt))
+		fdisk_warnx(cxt, _("Incomplete geometry setting."));
+
+	return rc;
+}
+