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/.gitignore b/libblkid/libfdisk/src/.gitignore
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/libblkid/libfdisk/src/.gitignore
diff --git a/libblkid/libfdisk/src/Makemodule.am b/libblkid/libfdisk/src/Makemodule.am
new file mode 100644
index 0000000..18ddec7
--- /dev/null
+++ b/libblkid/libfdisk/src/Makemodule.am
@@ -0,0 +1,112 @@
+
+# libfdisk.h is generated, so it's stored in builddir!
+fdiskincdir = $(includedir)/libfdisk
+nodist_fdiskinc_HEADERS = $(top_builddir)/libfdisk/src/libfdisk.h
+
+usrlib_exec_LTLIBRARIES += libfdisk.la
+libfdisk_la_SOURCES = \
+	include/list.h \
+	\
+	libfdisk/src/fdiskP.h \
+	libfdisk/src/init.c \
+	libfdisk/src/test.c \
+	libfdisk/src/ask.c \
+	libfdisk/src/alignment.c \
+	libfdisk/src/label.c \
+	libfdisk/src/utils.c \
+	libfdisk/src/context.c \
+	libfdisk/src/parttype.c \
+	libfdisk/src/partition.c \
+	libfdisk/src/table.c \
+	libfdisk/src/iter.c \
+	libfdisk/src/script.c \
+	\
+	libfdisk/src/sun.c \
+	libfdisk/src/sgi.c \
+	libfdisk/src/dos.c \
+	libfdisk/src/bsd.c \
+	libfdisk/src/gpt.c \
+	$(nodist_fdiskinc_HEADERS)
+
+
+nodist_libfdisk_la_SOURCES = libfdisk/src/fdiskP.h
+
+libfdisk_la_LIBADD = libcommon.la libuuid.la
+
+libfdisk_la_CFLAGS = \
+	$(SOLIB_CFLAGS) \
+	-I$(ul_libuuid_incdir) \
+	-I$(ul_libfdisk_incdir) \
+	-I$(top_srcdir)/libfdisk/src
+
+libfdisk_la_DEPENDENCIES = \
+	libcommon.la \
+	libuuid.la \
+	libfdisk/src/libfdisk.sym \
+	libfdisk/src/libfdisk.h.in
+
+libfdisk_la_LDFLAGS = \
+	$(SOLIB_LDFLAGS) \
+	-Wl,--version-script=$(top_srcdir)/libfdisk/src/libfdisk.sym \
+	-version-info $(LIBFDISK_VERSION_INFO)
+
+if BUILD_LIBBLKID
+libfdisk_la_LIBADD += libblkid.la
+libfdisk_la_DEPENDENCIES += libblkid.la
+libfdisk_la_CFLAGS += -I$(ul_libblkid_incdir)
+endif
+
+EXTRA_DIST += \
+	libfdisk/src/libfdisk.sym \
+	libfdisk/src/libfdisk.h.in
+
+if BUILD_LIBFDISK_TESTS
+check_PROGRAMS += \
+	test_fdisk_ask \
+	test_fdisk_script \
+	test_fdisk_utils
+
+libfdisk_tests_cflags  = -DTEST_PROGRAM $(libfdisk_la_CFLAGS)
+libfdisk_tests_ldflags = libuuid.la -static
+libfdisk_tests_ldadd   = libfdisk.la $(UUID_LIBS)
+
+if BUILD_LIBBLKID
+libfdisk_tests_ldflags += libblkid.la
+endif
+
+test_fdisk_ask_SOURCES = libfdisk/src/ask.c
+test_fdisk_ask_CFLAGS = $(libfdisk_tests_cflags)
+test_fdisk_ask_LDFLAGS = $(libfdisk_tests_ldflags)
+test_fdisk_ask_LDADD = $(libfdisk_tests_ldadd)
+
+test_fdisk_utils_SOURCES = libfdisk/src/utils.c
+test_fdisk_utils_CFLAGS = $(libfdisk_tests_cflags)
+test_fdisk_utils_LDFLAGS = $(libfdisk_tests_ldflags)
+test_fdisk_utils_LDADD = $(libfdisk_tests_ldadd)
+
+test_fdisk_script_SOURCES = libfdisk/src/script.c
+test_fdisk_script_CFLAGS = $(libfdisk_tests_cflags)
+test_fdisk_script_LDFLAGS = $(libfdisk_tests_ldflags)
+test_fdisk_script_LDADD = $(libfdisk_tests_ldadd)
+
+endif # BUILD_LIBFDISK_TESTS
+
+
+# move lib from $(usrlib_execdir) to $(libdir) if needed
+install-exec-hook-libfdisk:
+	if test "$(usrlib_execdir)" != "$(libdir)" -a -f "$(DESTDIR)$(usrlib_execdir)/libfdisk.so"; then \
+		mkdir -p $(DESTDIR)$(libdir); \
+		mv $(DESTDIR)$(usrlib_execdir)/libfdisk.so.* $(DESTDIR)$(libdir); \
+		so_img_name=$$(readlink $(DESTDIR)$(usrlib_execdir)/libfdisk.so); \
+		so_img_rel_target=$$(echo $(usrlib_execdir) | sed 's,\(^/\|\)[^/][^/]*,..,g'); \
+		(cd $(DESTDIR)$(usrlib_execdir) && \
+			rm -f libfdisk.so && \
+			$(LN_S) $$so_img_rel_target$(libdir)/$$so_img_name libfdisk.so); \
+	fi
+
+uninstall-hook-libfdisk:
+	rm -f $(DESTDIR)$(libdir)/libfdisk.so*
+
+INSTALL_EXEC_HOOKS += install-exec-hook-libfdisk
+UNINSTALL_HOOKS += uninstall-hook-libfdisk
+
diff --git a/libblkid/libfdisk/src/alignment.c b/libblkid/libfdisk/src/alignment.c
new file mode 100644
index 0000000..67f1ddd
--- /dev/null
+++ b/libblkid/libfdisk/src/alignment.c
@@ -0,0 +1,654 @@
+
+#ifdef HAVE_LIBBLKID
+#include <blkid.h>
+#endif
+#include "blkdev.h"
+
+#include "fdiskP.h"
+
+/**
+ * SECTION: alignment
+ * @title: Alignment
+ * @short_description: functions to align partitions and work with disk topology and geometry
+ *
+ * The libfdisk aligns the end of the partitions to make it possible to align
+ * the next partition to the "grain" (see fdisk_get_grain()). The grain is
+ * usually 1MiB (or more for devices where optimal I/O is greater than 1MiB).
+ *
+ * It means that the library does not align strictly to physical sector size
+ * (or minimal or optimal I/O), but it uses greater granularity. It makes
+ * partition tables more portable. If you copy disk layout from 512-sector to
+ * 4K-sector device, all partitions are still aligned to physical sectors.
+ *
+ * This unified concept also makes partition tables more user friendly, all
+ * tables look same, LBA of the first partition is 2048 sectors everywhere, etc.
+ *
+ * It's recommended to not change any alignment or device properties. All is
+ * initialized by default by fdisk_assign_device().
+ *
+ * Note that terminology used by libfdisk is: 
+ *   - device properties: I/O limits (topology), geometry, sector size, ...
+ *   - alignment: first, last LBA, grain, ...
+ *
+ * The alignment setting may be modified by disk label driver.
+ */
+
+/*
+ * Alignment according to logical granularity (usually 1MiB)
+ */
+static int lba_is_aligned(struct fdisk_context *cxt, fdisk_sector_t lba)
+{
+	unsigned long granularity = max(cxt->phy_sector_size, cxt->min_io_size);
+	uintmax_t offset;
+
+	if (cxt->grain > granularity)
+		granularity = cxt->grain;
+	offset = (lba * cxt->sector_size) & (granularity - 1);
+
+	return !((granularity + cxt->alignment_offset - offset) & (granularity - 1));
+}
+
+/*
+ * Alignment according to physical device topology (usually minimal i/o size)
+ */
+static int lba_is_phy_aligned(struct fdisk_context *cxt, fdisk_sector_t lba)
+{
+	unsigned long granularity = max(cxt->phy_sector_size, cxt->min_io_size);
+	uintmax_t offset = (lba * cxt->sector_size) & (granularity - 1);
+
+	return !((granularity + cxt->alignment_offset - offset) & (granularity - 1));
+}
+
+/**
+ * fdisk_align_lba:
+ * @cxt: context
+ * @lba: address to align
+ * @direction: FDISK_ALIGN_{UP,DOWN,NEAREST}
+ *
+ * This function aligns @lba to the "grain" (see fdisk_get_grain()). If the
+ * device uses alignment offset then the result is moved according the offset
+ * to be on the physical boundary.
+ *
+ * Returns: alignment LBA.
+ */
+fdisk_sector_t fdisk_align_lba(struct fdisk_context *cxt, fdisk_sector_t lba, int direction)
+{
+	fdisk_sector_t res;
+
+	if (lba_is_aligned(cxt, lba))
+		res = lba;
+	else {
+		fdisk_sector_t sects_in_phy = cxt->grain / cxt->sector_size;
+
+		if (lba < cxt->first_lba)
+			res = cxt->first_lba;
+
+		else if (direction == FDISK_ALIGN_UP)
+			res = ((lba + sects_in_phy) / sects_in_phy) * sects_in_phy;
+
+		else if (direction == FDISK_ALIGN_DOWN)
+			res = (lba / sects_in_phy) * sects_in_phy;
+
+		else /* FDISK_ALIGN_NEAREST */
+			res = ((lba + sects_in_phy / 2) / sects_in_phy) * sects_in_phy;
+
+		if (cxt->alignment_offset && !lba_is_aligned(cxt, res) &&
+		    res > cxt->alignment_offset / cxt->sector_size) {
+			/*
+			 * apply alignment_offset
+			 *
+			 * On disk with alignment compensation physical blocks starts
+			 * at LBA < 0 (usually LBA -1). It means we have to move LBA
+			 * according the offset to be on the physical boundary.
+			 */
+			/* fprintf(stderr, "LBA: %llu apply alignment_offset\n", res); */
+			res -= (max(cxt->phy_sector_size, cxt->min_io_size) -
+					cxt->alignment_offset) / cxt->sector_size;
+
+			if (direction == FDISK_ALIGN_UP && res < lba)
+				res += sects_in_phy;
+		}
+	}
+
+	if (lba != res)
+		DBG(CXT, ul_debugobj(cxt, "LBA %ju -aligned-to-> %ju",
+				(uintmax_t) lba,
+				(uintmax_t) res));
+	return res;
+}
+
+/**
+ * fdisk_align_lba_in_range:
+ * @cxt: context
+ * @lba: LBA
+ * @start: range start
+ * @stop: range stop
+ *
+ * Align @lba, the result has to be between @start and @stop
+ *
+ * Returns: aligned LBA
+ */
+fdisk_sector_t fdisk_align_lba_in_range(struct fdisk_context *cxt,
+				  fdisk_sector_t lba, fdisk_sector_t start, fdisk_sector_t stop)
+{
+	fdisk_sector_t res;
+
+	start = fdisk_align_lba(cxt, start, FDISK_ALIGN_UP);
+	stop = fdisk_align_lba(cxt, stop, FDISK_ALIGN_DOWN);
+	lba = fdisk_align_lba(cxt, lba, FDISK_ALIGN_NEAREST);
+
+	if (lba < start)
+		res = start;
+	else if (lba > stop)
+		res = stop;
+	else
+		res = lba;
+
+	DBG(CXT, ul_debugobj(cxt, "LBA %ju range:<%ju..%ju>, result: %ju",
+				(uintmax_t) lba,
+				(uintmax_t) start,
+				(uintmax_t) stop,
+				(uintmax_t) res));
+	return res;
+}
+
+/**
+ * fdisk_lba_is_phy_aligned:
+ * @cxt: context
+ * @lba: LBA to check
+ *
+ * Check if the @lba is aligned to physical sector boundary.
+ *
+ * Returns: 1 if aligned.
+ */
+int fdisk_lba_is_phy_aligned(struct fdisk_context *cxt, fdisk_sector_t lba)
+{
+	return lba_is_phy_aligned(cxt, lba);
+}
+
+static unsigned long get_sector_size(int fd)
+{
+	int sect_sz;
+
+	if (!blkdev_get_sector_size(fd, &sect_sz))
+		return (unsigned long) sect_sz;
+	return DEFAULT_SECTOR_SIZE;
+}
+
+static void recount_geometry(struct fdisk_context *cxt)
+{
+	if (!cxt->geom.heads)
+		cxt->geom.heads = 255;
+	if (!cxt->geom.sectors)
+		cxt->geom.sectors = 63;
+
+	cxt->geom.cylinders = cxt->total_sectors /
+		(cxt->geom.heads * cxt->geom.sectors);
+}
+
+/**
+ * fdisk_override_geometry:
+ * @cxt: fdisk context
+ * @cylinders: user specified cylinders
+ * @heads: user specified heads
+ * @sectors: user specified sectors
+ *
+ * Overrides auto-discovery. The function fdisk_reset_device_properties()
+ * restores the original setting.
+ *
+ * The difference between fdisk_override_geometry() and fdisk_save_user_geometry()
+ * is that saved user geometry is persistent setting and it's applied always
+ * when device is assigned to the context or device properties are reseted.
+ *
+ * Returns: 0 on success, < 0 on error.
+ */
+int fdisk_override_geometry(struct fdisk_context *cxt,
+			    unsigned int cylinders,
+			    unsigned int heads,
+			    unsigned int sectors)
+{
+	if (!cxt)
+		return -EINVAL;
+	if (heads)
+		cxt->geom.heads = heads;
+	if (sectors)
+		cxt->geom.sectors = sectors;
+
+	if (cylinders)
+		cxt->geom.cylinders = cylinders;
+	else
+		recount_geometry(cxt);
+
+	fdisk_reset_alignment(cxt);
+
+	DBG(CXT, ul_debugobj(cxt, "override C/H/S: %u/%u/%u",
+		(unsigned) cxt->geom.cylinders,
+		(unsigned) cxt->geom.heads,
+		(unsigned) cxt->geom.sectors));
+
+	return 0;
+}
+
+/**
+ * fdisk_save_user_geometry:
+ * @cxt: context
+ * @cylinders: C
+ * @heads: H
+ * @sectors: S
+ *
+ * Save user defined geometry to use it for partitioning.
+ *
+ * The user properties are applied by fdisk_assign_device() or
+ * fdisk_reset_device_properties().
+
+ * Returns: <0 on error, 0 on success.
+ */
+int fdisk_save_user_geometry(struct fdisk_context *cxt,
+			    unsigned int cylinders,
+			    unsigned int heads,
+			    unsigned int sectors)
+{
+	if (!cxt)
+		return -EINVAL;
+
+	if (heads)
+		cxt->user_geom.heads = heads > 256 ? 0 : heads;
+	if (sectors)
+		cxt->user_geom.sectors = sectors >= 64 ? 0 : sectors;
+	if (cylinders)
+		cxt->user_geom.cylinders = cylinders;
+
+	DBG(CXT, ul_debugobj(cxt, "user C/H/S: %u/%u/%u",
+				(unsigned) cxt->user_geom.cylinders,
+				(unsigned) cxt->user_geom.heads,
+				(unsigned) cxt->user_geom.sectors));
+
+	return 0;
+}
+
+/**
+ * fdisk_save_user_sector_size:
+ * @cxt: context
+ * @phy: physical sector size
+ * @log: logicla sector size
+ *
+ * Save user defined sector sizes to use it for partitioning.
+ *
+ * The user properties are applied by fdisk_assign_device() or 
+ * fdisk_reset_device_properties().
+ *
+ * Returns: <0 on error, 0 on success.
+ */
+int fdisk_save_user_sector_size(struct fdisk_context *cxt,
+				unsigned int phy,
+				unsigned int log)
+{
+	if (!cxt)
+		return -EINVAL;
+
+	DBG(CXT, ul_debugobj(cxt, "user phy/log sector size: %u/%u", phy, log));
+
+	cxt->user_pyh_sector = phy;
+	cxt->user_log_sector = log;
+
+	return 0;
+}
+
+/**
+ * fdisk_has_user_device_properties:
+ * @cxt: context
+ *
+ * Returns: 1 if user specified any properties
+ */
+int fdisk_has_user_device_properties(struct fdisk_context *cxt)
+{
+	return (cxt->user_pyh_sector
+		    || cxt->user_log_sector
+		    || cxt->user_geom.heads
+		    || cxt->user_geom.sectors
+		    || cxt->user_geom.cylinders);
+}
+
+int fdisk_apply_user_device_properties(struct fdisk_context *cxt)
+{
+	if (!cxt)
+		return -EINVAL;
+
+	DBG(CXT, ul_debugobj(cxt, "appling user device properties"));
+
+	if (cxt->user_pyh_sector)
+		cxt->phy_sector_size = cxt->user_pyh_sector;
+	if (cxt->user_log_sector)
+		cxt->sector_size = cxt->min_io_size =
+			cxt->io_size = cxt->user_log_sector;
+
+	if (cxt->user_geom.heads)
+		cxt->geom.heads = cxt->user_geom.heads;
+	if (cxt->user_geom.sectors)
+		cxt->geom.sectors = cxt->user_geom.sectors;
+
+	if (cxt->user_geom.cylinders)
+		cxt->geom.cylinders = cxt->user_geom.cylinders;
+	else if (cxt->user_geom.heads || cxt->user_geom.sectors)
+		recount_geometry(cxt);
+
+	fdisk_reset_alignment(cxt);
+	if (cxt->firstsector_bufsz != cxt->sector_size)
+		fdisk_read_firstsector(cxt);
+
+	DBG(CXT, ul_debugobj(cxt, "new C/H/S: %u/%u/%u",
+		(unsigned) cxt->geom.cylinders,
+		(unsigned) cxt->geom.heads,
+		(unsigned) cxt->geom.sectors));
+	DBG(CXT, ul_debugobj(cxt, "new log/phy sector size: %u/%u",
+		(unsigned) cxt->sector_size,
+		(unsigned) cxt->phy_sector_size));
+
+	return 0;
+}
+
+void fdisk_zeroize_device_properties(struct fdisk_context *cxt)
+{
+	assert(cxt);
+
+	cxt->io_size = 0;
+	cxt->optimal_io_size = 0;
+	cxt->min_io_size = 0;
+	cxt->phy_sector_size = 0;
+	cxt->sector_size = 0;
+	cxt->alignment_offset = 0;
+	cxt->grain = 0;
+	cxt->first_lba = 0;
+	cxt->last_lba = 0;
+	cxt->total_sectors = 0;
+
+	memset(&cxt->geom, 0, sizeof(struct fdisk_geometry));
+}
+
+/**
+ * fdisk_reset_device_properties:
+ * @cxt: context
+ *
+ * Resets and discovery topology (I/O limits), geometry, re-read the first
+ * rector on the device if necessary and apply user device setting (geometry
+ * and sector size), then initialize alignment according to label driver (see
+ * fdisk_reset_alignment()).
+ *
+ * You don't have to use this function by default, fdisk_assign_device() is
+ * smart enough to initialize all necessary setting.
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int fdisk_reset_device_properties(struct fdisk_context *cxt)
+{
+	int rc;
+
+	if (!cxt)
+		return -EINVAL;
+
+	DBG(CXT, ul_debugobj(cxt, "*** reseting device properties"));
+
+	fdisk_zeroize_device_properties(cxt);
+	fdisk_discover_topology(cxt);
+	fdisk_discover_geometry(cxt);
+
+	rc = fdisk_read_firstsector(cxt);
+	if (rc)
+		return rc;
+
+	fdisk_apply_user_device_properties(cxt);
+	return 0;
+}
+
+/*
+ * Generic (label independent) geometry
+ */
+int fdisk_discover_geometry(struct fdisk_context *cxt)
+{
+	fdisk_sector_t nsects;
+
+	assert(cxt);
+	assert(cxt->geom.heads == 0);
+
+	DBG(CXT, ul_debugobj(cxt, "%s: discovering geometry...", cxt->dev_path));
+
+	/* get number of 512-byte sectors, and convert it the real sectors */
+	if (!blkdev_get_sectors(cxt->dev_fd, (unsigned long long *) &nsects))
+		cxt->total_sectors = (nsects / (cxt->sector_size >> 9));
+
+	DBG(CXT, ul_debugobj(cxt, "total sectors: %ju (ioctl=%ju)",
+				(uintmax_t) cxt->total_sectors,
+				(uintmax_t) nsects));
+
+	/* what the kernel/bios thinks the geometry is */
+	blkdev_get_geometry(cxt->dev_fd, &cxt->geom.heads, (unsigned int *) &cxt->geom.sectors);
+
+	/* obtained heads and sectors */
+	recount_geometry(cxt);
+
+	DBG(CXT, ul_debugobj(cxt, "result: C/H/S: %u/%u/%u",
+			       (unsigned) cxt->geom.cylinders,
+			       (unsigned) cxt->geom.heads,
+			       (unsigned) cxt->geom.sectors));
+	return 0;
+}
+
+int fdisk_discover_topology(struct fdisk_context *cxt)
+{
+#ifdef HAVE_LIBBLKID
+	blkid_probe pr;
+#endif
+	assert(cxt);
+	assert(cxt->sector_size == 0);
+
+	DBG(CXT, ul_debugobj(cxt, "%s: discovering topology...", cxt->dev_path));
+#ifdef HAVE_LIBBLKID
+	DBG(CXT, ul_debugobj(cxt, "initialize libblkid prober"));
+
+	pr = blkid_new_probe();
+	if (pr && blkid_probe_set_device(pr, cxt->dev_fd, 0, 0) == 0) {
+		blkid_topology tp = blkid_probe_get_topology(pr);
+
+		if (tp) {
+			cxt->min_io_size = blkid_topology_get_minimum_io_size(tp);
+			cxt->optimal_io_size = blkid_topology_get_optimal_io_size(tp);
+			cxt->phy_sector_size = blkid_topology_get_physical_sector_size(tp);
+			cxt->alignment_offset = blkid_topology_get_alignment_offset(tp);
+
+			/* I/O size used by fdisk */
+			cxt->io_size = cxt->optimal_io_size;
+			if (!cxt->io_size)
+				/* optimal IO is optional, default to minimum IO */
+				cxt->io_size = cxt->min_io_size;
+		}
+	}
+	blkid_free_probe(pr);
+#endif
+
+	cxt->sector_size = get_sector_size(cxt->dev_fd);
+	if (!cxt->phy_sector_size) /* could not discover physical size */
+		cxt->phy_sector_size = cxt->sector_size;
+
+	/* no blkid or error, use default values */
+	if (!cxt->min_io_size)
+		cxt->min_io_size = cxt->sector_size;
+	if (!cxt->io_size)
+		cxt->io_size = cxt->sector_size;
+
+	DBG(CXT, ul_debugobj(cxt, "result: log/phy sector size: %ld/%ld",
+			cxt->sector_size, cxt->phy_sector_size));
+	DBG(CXT, ul_debugobj(cxt, "result: fdisk/min/optimal io: %ld/%ld/%ld",
+		       cxt->io_size, cxt->optimal_io_size, cxt->min_io_size));
+	return 0;
+}
+
+static int has_topology(struct fdisk_context *cxt)
+{
+	/*
+	 * Assume that the device provides topology info if
+	 * optimal_io_size is set or alignment_offset is set or
+	 * minimum_io_size is not power of 2.
+	 */
+	if (cxt &&
+	    (cxt->optimal_io_size ||
+	     cxt->alignment_offset ||
+	     !is_power_of_2(cxt->min_io_size)))
+		return 1;
+	return 0;
+}
+
+/*
+ * The LBA of the first partition is based on the device geometry and topology.
+ * This offset is generic (and recommended) for all labels.
+ *
+ * Returns: 0 on error or number of logical sectors.
+ */
+static fdisk_sector_t topology_get_first_lba(struct fdisk_context *cxt)
+{
+	fdisk_sector_t x = 0, res;
+
+	if (!cxt)
+		return 0;
+
+	if (!cxt->io_size)
+		fdisk_discover_topology(cxt);
+
+	/*
+	 * Align the begin of partitions to:
+	 *
+	 * a) topology
+	 *  a2) alignment offset
+	 *  a1) or physical sector (minimal_io_size, aka "grain")
+	 *
+	 * b) or default to 1MiB (2048 sectrors, Windows Vista default)
+	 *
+	 * c) or for very small devices use 1 phy.sector
+	 */
+	if (has_topology(cxt)) {
+		if (cxt->alignment_offset)
+			x = cxt->alignment_offset;
+		else if (cxt->io_size > 2048 * 512)
+			x = cxt->io_size;
+	}
+	/* default to 1MiB */
+	if (!x)
+		x = 2048 * 512;
+
+	res = x / cxt->sector_size;
+
+	/* don't use huge offset on small devices */
+	if (cxt->total_sectors <= res * 4)
+		res = cxt->phy_sector_size / cxt->sector_size;
+
+	return res;
+}
+
+static unsigned long topology_get_grain(struct fdisk_context *cxt)
+{
+	unsigned long res;
+
+	if (!cxt)
+		return 0;
+
+	if (!cxt->io_size)
+		fdisk_discover_topology(cxt);
+
+	res = cxt->io_size;
+
+	/* use 1MiB grain always when possible */
+	if (res < 2048 * 512)
+		res = 2048 * 512;
+
+	/* don't use huge grain on small devices */
+	if (cxt->total_sectors <= (res * 4 / cxt->sector_size))
+		res = cxt->phy_sector_size;
+
+	return res;
+}
+
+/**
+ * fdisk_reset_alignment:
+ * @cxt: fdisk context
+ *
+ * Resets alignment setting to the default and label specific values. This
+ * function does not change device properties (I/O limits, geometry etc.).
+ *
+ * Returns: 0 on success, < 0 in case of error.
+ */
+int fdisk_reset_alignment(struct fdisk_context *cxt)
+{
+	int rc = 0;
+
+	if (!cxt)
+		return -EINVAL;
+
+	DBG(CXT, ul_debugobj(cxt, "reseting alignment..."));
+
+	/* default */
+	cxt->grain = topology_get_grain(cxt);
+	cxt->first_lba = topology_get_first_lba(cxt);
+	cxt->last_lba = cxt->total_sectors - 1;
+
+	/* overwrite default by label stuff */
+	if (cxt->label && cxt->label->op->reset_alignment)
+		rc = cxt->label->op->reset_alignment(cxt);
+
+	DBG(CXT, ul_debugobj(cxt, "alignment reseted to: "
+			    "first LBA=%ju, last LBA=%ju, grain=%lu [rc=%d]",
+			    (uintmax_t) cxt->first_lba, (uintmax_t) cxt->last_lba,
+			    cxt->grain,	rc));
+	return rc;
+}
+
+
+fdisk_sector_t fdisk_scround(struct fdisk_context *cxt, fdisk_sector_t num)
+{
+	fdisk_sector_t un = fdisk_get_units_per_sector(cxt);
+	return (num + un - 1) / un;
+}
+
+fdisk_sector_t fdisk_cround(struct fdisk_context *cxt, fdisk_sector_t num)
+{
+	return fdisk_use_cylinders(cxt) ?
+			(num / fdisk_get_units_per_sector(cxt)) + 1 : num;
+}
+
+/**
+ * fdisk_reread_partition_table:
+ * @cxt: context
+ *
+ * Force *kernel* to re-read partition table on block devices.
+ *
+ * Returns: 0 on success, < 0 in case of error.
+ */
+int fdisk_reread_partition_table(struct fdisk_context *cxt)
+{
+	int i;
+	struct stat statbuf;
+
+	assert(cxt);
+	assert(cxt->dev_fd >= 0);
+
+	i = fstat(cxt->dev_fd, &statbuf);
+	if (i == 0 && S_ISBLK(statbuf.st_mode)) {
+		sync();
+#ifdef BLKRRPART
+		fdisk_info(cxt, _("Calling ioctl() to re-read partition table."));
+		i = ioctl(cxt->dev_fd, BLKRRPART);
+#else
+		errno = ENOSYS;
+		i = 1;
+#endif
+	}
+
+	if (i) {
+		fdisk_warn(cxt, _("Re-reading the partition table failed."));
+		fdisk_info(cxt,	_(
+			"The kernel still uses the old table. The "
+			"new table will be used at the next reboot "
+			"or after you run partprobe(8) or kpartx(8)."));
+		return -errno;
+	}
+
+	return 0;
+}
diff --git a/libblkid/libfdisk/src/ask.c b/libblkid/libfdisk/src/ask.c
new file mode 100644
index 0000000..7e0c3c2
--- /dev/null
+++ b/libblkid/libfdisk/src/ask.c
@@ -0,0 +1,1044 @@
+
+#include "strutils.h"
+#include "fdiskP.h"
+
+/**
+ * SECTION: ask
+ * @title: Ask
+ * @short_description: interface for dialog driven partitioning, warning and info messages
+ *
+ */
+
+static void fdisk_ask_menu_reset_items(struct fdisk_ask *ask);
+
+
+/**
+ * fdisk_set_ask:
+ * @cxt: context
+ * @ask_cb: callback
+ * @data: callback data
+ *
+ * Set callback for dialog driven partitioning and library warnings/errors.
+ *
+ * Returns: 0 on success, < 0 on error.
+ */
+int fdisk_set_ask(struct fdisk_context *cxt,
+		int (*ask_cb)(struct fdisk_context *, struct fdisk_ask *, void *),
+		void *data)
+{
+	assert(cxt);
+
+	cxt->ask_cb = ask_cb;
+	cxt->ask_data = data;
+	return 0;
+}
+
+struct fdisk_ask *fdisk_new_ask(void)
+{
+	struct fdisk_ask *ask = calloc(1, sizeof(struct fdisk_ask));
+	DBG(ASK, ul_debugobj(ask, "alloc"));
+	ask->refcount = 1;
+	return ask;
+}
+
+void fdisk_reset_ask(struct fdisk_ask *ask)
+{
+	int refcount;
+
+	assert(ask);
+	free(ask->query);
+
+	DBG(ASK, ul_debugobj(ask, "reset"));
+	refcount = ask->refcount;
+
+	if (fdisk_is_ask(ask, MENU))
+		fdisk_ask_menu_reset_items(ask);
+
+	memset(ask, 0, sizeof(*ask));
+	ask->refcount = refcount;
+}
+
+/**
+ * fdisk_ref_ask:
+ * @ask: ask instance
+ *
+ * Incremparts reference counter.
+ */
+void fdisk_ref_ask(struct fdisk_ask *ask)
+{
+	if (ask)
+		ask->refcount++;
+}
+
+
+/**
+ * fdisk_unref_ask:
+ * @ask: ask instance
+ *
+ * De-incremparts reference counter, on zero the @ask is automatically
+ * deallocated.
+ */
+void fdisk_unref_ask(struct fdisk_ask *ask)
+{
+	if (!ask)
+		return;
+	ask->refcount--;
+
+	if (ask->refcount <= 0) {
+		fdisk_reset_ask(ask);
+		DBG(ASK, ul_debugobj(ask, "free"));
+		free(ask);
+	}
+}
+
+/**
+ * fdisk_ask_get_query:
+ * @ask: ask instance
+ *
+ * Returns: pointer to dialog string.
+ */
+const char *fdisk_ask_get_query(struct fdisk_ask *ask)
+{
+	assert(ask);
+	return ask->query;
+}
+
+int fdisk_ask_set_query(struct fdisk_ask *ask, const char *str)
+{
+	assert(ask);
+	return !strdup_to_struct_member(ask, query, str) ? -ENOMEM : 0;
+}
+
+/**
+ * fdisk_ask_get_type:
+ * @ask: ask instance
+ *
+ * Returns: FDISK_ASKTYPE_*
+ */
+int fdisk_ask_get_type(struct fdisk_ask *ask)
+{
+	assert(ask);
+	return ask->type;
+}
+
+int fdisk_ask_set_type(struct fdisk_ask *ask, int type)
+{
+	assert(ask);
+	ask->type = type;
+	return 0;
+}
+
+int fdisk_do_ask(struct fdisk_context *cxt, struct fdisk_ask *ask)
+{
+	int rc;
+
+	assert(ask);
+	assert(cxt);
+
+	DBG(ASK, ul_debugobj(ask, "do_ask for '%s'",
+				ask->query ? ask->query :
+				ask->type == FDISK_ASKTYPE_INFO  ? "info" :
+				ask->type == FDISK_ASKTYPE_WARNX ? "warnx" :
+				ask->type == FDISK_ASKTYPE_WARN  ? "warn" :
+				"?nothing?"));
+
+	if (!cxt->ask_cb) {
+		DBG(ASK, ul_debugobj(ask, "no ask callback specified!"));
+		return -EINVAL;
+	}
+
+	rc = cxt->ask_cb(cxt, ask, cxt->ask_data);
+
+	DBG(ASK, ul_debugobj(ask, "do_ask done [rc=%d]", rc));
+	return rc;
+}
+
+#define is_number_ask(a)  (fdisk_is_ask(a, NUMBER) || fdisk_is_ask(a, OFFSET))
+
+/**
+ * fdisk_ask_number_get_range:
+ * @ask: ask instance
+ *
+ * Returns: string with range (e.g. "1,3,5-10")
+ */
+const char *fdisk_ask_number_get_range(struct fdisk_ask *ask)
+{
+	assert(ask);
+	assert(is_number_ask(ask));
+	return ask->data.num.range;
+}
+
+int fdisk_ask_number_set_range(struct fdisk_ask *ask, const char *range)
+{
+	assert(ask);
+	assert(is_number_ask(ask));
+	ask->data.num.range = range;
+	return 0;
+}
+
+/**
+ * fdisk_ask_number_get_default:
+ * @ask: ask instance
+ *
+ * Returns: default number
+ *
+ */
+uint64_t fdisk_ask_number_get_default(struct fdisk_ask *ask)
+{
+	assert(ask);
+	assert(is_number_ask(ask));
+	return ask->data.num.dfl;
+}
+
+int fdisk_ask_number_set_default(struct fdisk_ask *ask, uint64_t dflt)
+{
+	assert(ask);
+	ask->data.num.dfl = dflt;
+	return 0;
+}
+
+/**
+ * fdisk_ask_number_get_low:
+ * @ask: ask instance
+ *
+ * Returns: minimal possible number when ask for numbers in range
+ */
+uint64_t fdisk_ask_number_get_low(struct fdisk_ask *ask)
+{
+	assert(ask);
+	assert(is_number_ask(ask));
+	return ask->data.num.low;
+}
+
+int fdisk_ask_number_set_low(struct fdisk_ask *ask, uint64_t low)
+{
+	assert(ask);
+	ask->data.num.low = low;
+	return 0;
+}
+
+/**
+ * fdisk_ask_number_get_high:
+ * @ask: ask instance
+ *
+ * Returns: maximal possible number when ask for numbers in range
+ */
+uint64_t fdisk_ask_number_get_high(struct fdisk_ask *ask)
+{
+	assert(ask);
+	assert(is_number_ask(ask));
+	return ask->data.num.hig;
+}
+
+int fdisk_ask_number_set_high(struct fdisk_ask *ask, uint64_t high)
+{
+	assert(ask);
+	ask->data.num.hig = high;
+	return 0;
+}
+
+/**
+ * fdisk_ask_number_get_result:
+ * @ask: ask instance
+ *
+ * Returns: result
+ */
+uint64_t fdisk_ask_number_get_result(struct fdisk_ask *ask)
+{
+	assert(ask);
+	assert(is_number_ask(ask));
+	return ask->data.num.result;
+}
+
+/**
+ * fdisk_ask_number_set_result:
+ * @ask: ask instance
+ * @result: dialog result
+ *
+ * Returns: 0 on success, <0 on error
+ */
+int fdisk_ask_number_set_result(struct fdisk_ask *ask, uint64_t result)
+{
+	assert(ask);
+	ask->data.num.result = result;
+	return 0;
+}
+
+/**
+ * fdisk_ask_number_get_base:
+ * @ask: ask instance
+ *
+ * Returns: base when user specify number in relative notation (+size)
+ */
+uint64_t fdisk_ask_number_get_base(struct fdisk_ask *ask)
+{
+	assert(ask);
+	assert(is_number_ask(ask));
+	return ask->data.num.base;
+}
+
+int fdisk_ask_number_set_base(struct fdisk_ask *ask, uint64_t base)
+{
+	assert(ask);
+	ask->data.num.base = base;
+	return 0;
+}
+
+/**
+ * fdisk_ask_number_get_unit:
+ * @ask: ask instance
+ *
+ * Returns: number of bytes per the unit
+ */
+uint64_t fdisk_ask_number_get_unit(struct fdisk_ask *ask)
+{
+	assert(ask);
+	assert(is_number_ask(ask));
+	return ask->data.num.unit;
+}
+
+int fdisk_ask_number_set_unit(struct fdisk_ask *ask, uint64_t unit)
+{
+	assert(ask);
+	ask->data.num.unit = unit;
+	return 0;
+}
+
+int fdisk_ask_number_is_relative(struct fdisk_ask *ask)
+{
+	assert(ask);
+	assert(is_number_ask(ask));
+	return ask->data.num.relative;
+}
+
+/**
+ * fdisk_ask_number_set_relative
+ * @ask: ask instance
+ * @relative: 0 or 1
+ *
+ * Inform libfdisk that user specified number in relative notation rather than
+ * by explicit number. This info allows to fdisk do some optimization (e.g.
+ * align end of partiton, etc.)
+ *
+ * Returns: 0 on success, <0 on error
+ */
+int fdisk_ask_number_set_relative(struct fdisk_ask *ask, int relative)
+{
+	assert(ask);
+	ask->data.num.relative = relative ? 1 : 0;
+	return 0;
+}
+
+/**
+ * fdisk_ask_number_inchars:
+ * @ask: ask instance
+ *
+ * For example for BSD is normal to address partition by chars rather than by
+ * number (first partition is 'a').
+ *
+ * Returns: 1 if number should be presented as chars
+ *
+ */
+int fdisk_ask_number_inchars(struct fdisk_ask *ask)
+{
+	assert(ask);
+	assert(is_number_ask(ask));
+	return ask->data.num.inchars;
+}
+
+/*
+ * Generates string with list ranges (e.g. 1,2,5-8) for the 'cur'
+ */
+#define tochar(num)	((int) ('a' + num - 1))
+static char *mk_string_list(char *ptr, size_t *len, size_t *begin,
+			    size_t *run, ssize_t cur, int inchar)
+{
+	int rlen;
+
+	if (cur != -1) {
+		if (!*begin) {			/* begin of the list */
+			*begin = cur + 1;
+			return ptr;
+		}
+
+		if (*begin + *run == cur) {	/* no gap, continue */
+			(*run)++;
+			return ptr;
+		}
+	} else if (!*begin) {
+		*ptr = '\0';
+		return ptr;		/* end of empty list */
+	}
+
+					/* add to the list */
+	if (!*run)
+		rlen = inchar ? snprintf(ptr, *len, "%c,", tochar(*begin)) :
+				snprintf(ptr, *len, "%zu,", *begin);
+	else if (*run == 1)
+		rlen = inchar ?
+			snprintf(ptr, *len, "%c,%c,", tochar(*begin), tochar(*begin + 1)) :
+			snprintf(ptr, *len, "%zu,%zu,", *begin, *begin + 1);
+	else
+		rlen = inchar ?
+			snprintf(ptr, *len, "%c-%c,", tochar(*begin), tochar(*begin + *run)) :
+			snprintf(ptr, *len, "%zu-%zu,", *begin, *begin + *run);
+
+	if (rlen < 0 || (size_t) rlen + 1 > *len)
+		return NULL;
+
+	ptr += rlen;
+
+	if (rlen > 0 && *len > (size_t) rlen)
+		*len -= rlen;
+	else
+		*len = 0;
+
+	if (cur == -1 && *begin) {
+		/* end of the list */
+		*(ptr - 1) = '\0';	/* remove tailing ',' from the list */
+		return ptr;
+	}
+
+	*begin = cur + 1;
+	*run = 0;
+
+	return ptr;
+}
+
+/**
+ * fdisk_ask_partnum:
+ * @cxt: context
+ * @partnum: returns partition number
+ * @wantnew: 0|1
+ *
+ * High-level API to ask for used or unused partition number.
+ *
+ * Returns: 0 on success, < 0 on error, 1 if no free/used partition
+ */
+int fdisk_ask_partnum(struct fdisk_context *cxt, size_t *partnum, int wantnew)
+{
+	int rc = 0, inchar = 0;
+	char range[BUFSIZ], *ptr = range;
+	size_t i, len = sizeof(range), begin = 0, run = 0;
+	struct fdisk_ask *ask = NULL;
+	__typeof__(ask->data.num) *num;
+
+	assert(cxt);
+	assert(cxt->label);
+	assert(partnum);
+
+	if (cxt->label && cxt->label->flags & FDISK_LABEL_FL_INCHARS_PARTNO)
+		inchar = 1;
+
+	DBG(ASK, ul_debug("%s: asking for %s partition number "
+			  "(max: %zu, inchar: %s)",
+			cxt->label->name,
+			wantnew ? "new" : "used",
+			cxt->label->nparts_max,
+			inchar ? "yes" : "not"));
+
+	ask = fdisk_new_ask();
+	if (!ask)
+		return -ENOMEM;
+
+	fdisk_ask_set_type(ask, FDISK_ASKTYPE_NUMBER);
+	num = &ask->data.num;
+
+	ask->data.num.inchars = inchar ? 1 : 0;
+
+	for (i = 0; i < cxt->label->nparts_max; i++) {
+		int used = fdisk_is_partition_used(cxt, i);
+
+		if (wantnew && !used) {
+			ptr = mk_string_list(ptr, &len, &begin, &run, i, inchar);
+			if (!ptr) {
+				rc = -EINVAL;
+				break;
+			}
+			if (!num->low)
+				num->dfl = num->low = i + 1;
+			num->hig = i + 1;
+		} else if (!wantnew && used) {
+			ptr = mk_string_list(ptr, &len, &begin, &run, i, inchar);
+			if (!num->low)
+				num->low = i + 1;
+			num->dfl = num->hig = i + 1;
+		}
+	}
+
+	DBG(ASK, ul_debugobj(ask, "ask limits: low: %ju, high: %ju, default: %ju",
+				num->low, num->hig, num->dfl));
+
+	if (!rc && !wantnew && num->low == num->hig) {
+		if (num->low > 0) {
+			/* only one existing partiton, don't ask, return the number */
+			fdisk_ask_number_set_result(ask, num->low);
+			fdisk_info(cxt, _("Selected partition %ju"), num->low);
+
+		} else if (num->low == 0) {
+			fdisk_warnx(cxt, _("No partition is defined yet!"));
+			rc = 1;
+		}
+		goto dont_ask;
+	}
+	if (!rc && wantnew && num->low == num->hig) {
+		if (num->low > 0) {
+			/* only one free partition, don't ask, return the number */
+			fdisk_ask_number_set_result(ask, num->low);
+			fdisk_info(cxt, _("Selected partition %ju"), num->low);
+		}
+		if (num->low == 0) {
+			fdisk_warnx(cxt, _("No free partition available!"));
+			rc = 1;
+		}
+		goto dont_ask;
+	}
+	if (!rc) {
+		mk_string_list(ptr, &len, &begin, &run, -1, inchar);	/* terminate the list */
+		rc = fdisk_ask_number_set_range(ask, range);
+	}
+	if (!rc)
+		rc = fdisk_ask_set_query(ask, _("Partition number"));
+	if (!rc)
+		rc = fdisk_do_ask(cxt, ask);
+
+dont_ask:
+	if (!rc) {
+		*partnum = fdisk_ask_number_get_result(ask);
+		if (*partnum)
+			*partnum -= 1;
+	}
+	DBG(ASK, ul_debugobj(ask, "result: %ju [rc=%d]\n", fdisk_ask_number_get_result(ask), rc));
+	fdisk_unref_ask(ask);
+	return rc;
+}
+
+/**
+ * fdisk_ask_number:
+ * @cxt: context
+ * @low: minimal possible number
+ * @dflt: default suggestion
+ * @high: maximal possible number
+ * @query: question string
+ * @result: returns result
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int fdisk_ask_number(struct fdisk_context *cxt,
+		     uintmax_t low,
+		     uintmax_t dflt,
+		     uintmax_t high,
+		     const char *query,
+		     uintmax_t *result)
+{
+	struct fdisk_ask *ask;
+	int rc;
+
+	assert(cxt);
+
+	ask = fdisk_new_ask();
+	if (!ask)
+		return -ENOMEM;
+
+	rc = fdisk_ask_set_type(ask, FDISK_ASKTYPE_NUMBER);
+	if (!rc)
+		fdisk_ask_number_set_low(ask, low);
+	if (!rc)
+		fdisk_ask_number_set_default(ask, dflt);
+	if (!rc)
+		fdisk_ask_number_set_high(ask, high);
+	if (!rc)
+		fdisk_ask_set_query(ask, query);
+	if (!rc)
+		rc = fdisk_do_ask(cxt, ask);
+	if (!rc)
+		*result = fdisk_ask_number_get_result(ask);
+
+	DBG(ASK, ul_debugobj(ask, "result: %ju [rc=%d]\n", *result, rc));
+	fdisk_unref_ask(ask);
+	return rc;
+}
+
+/**
+ * fdisk_ask_string_get_result:
+ * @ask: ask instance
+ *
+ * Returns: pointer to dialog result
+ */
+char *fdisk_ask_string_get_result(struct fdisk_ask *ask)
+{
+	assert(ask);
+	assert(fdisk_is_ask(ask, STRING));
+	return ask->data.str.result;
+}
+
+/**
+ * fdisk_ask_string_set_result:
+ * @ask: ask instance
+ * @result: pointer to allocated buffer with string
+ *
+ * You don't have to care about the @result deallocation, libfdisk is going to
+ * deallocate the result when destroy @ask instance.
+ *
+ * Returns: 0 on success, <0 on error
+ */
+int fdisk_ask_string_set_result(struct fdisk_ask *ask, char *result)
+{
+	assert(ask);
+	ask->data.str.result = result;
+	return 0;
+}
+
+/**
+ * fdisk_ask_string:
+ * @cxt: context:
+ * @query: question string
+ * @result: returns allocated buffer
+ *
+ * High-level API to ask for strings. Don't forget to deallocate the @result.
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int fdisk_ask_string(struct fdisk_context *cxt,
+		     const char *query,
+		     char **result)
+{
+	struct fdisk_ask *ask;
+	int rc;
+
+	assert(cxt);
+
+	ask = fdisk_new_ask();
+	if (!ask)
+		return -ENOMEM;
+
+	rc = fdisk_ask_set_type(ask, FDISK_ASKTYPE_STRING);
+	if (!rc)
+		fdisk_ask_set_query(ask, query);
+	if (!rc)
+		rc = fdisk_do_ask(cxt, ask);
+	if (!rc)
+		*result = fdisk_ask_string_get_result(ask);
+
+	DBG(ASK, ul_debugobj(ask, "result: %s [rc=%d]\n", *result, rc));
+	fdisk_unref_ask(ask);
+	return rc;
+}
+
+/**
+ * fdisk_ask_yesno:
+ * @cxt: context
+ * @query: question string
+ * @result: returns 0 (no) or 1 (yes)
+ *
+ * Hight-level API to ask Yes/No questions
+ *
+ * Returns: 0 on success, <0 on error
+ */
+int fdisk_ask_yesno(struct fdisk_context *cxt,
+		     const char *query,
+		     int *result)
+{
+	struct fdisk_ask *ask;
+	int rc;
+
+	assert(cxt);
+
+	ask = fdisk_new_ask();
+	if (!ask)
+		return -ENOMEM;
+
+	rc = fdisk_ask_set_type(ask, FDISK_ASKTYPE_YESNO);
+	if (!rc)
+		fdisk_ask_set_query(ask, query);
+	if (!rc)
+		rc = fdisk_do_ask(cxt, ask);
+	if (!rc)
+		*result = fdisk_ask_yesno_get_result(ask) == 1 ? 1 : 0;
+
+	DBG(ASK, ul_debugobj(ask, "result: %d [rc=%d]\n", *result, rc));
+	fdisk_unref_ask(ask);
+	return rc;
+}
+
+/**
+ * fdisk_ask_yesno_get_result:
+ * @ask: ask instance
+ *
+ * Returns: 0 or 1
+ */
+int fdisk_ask_yesno_get_result(struct fdisk_ask *ask)
+{
+	assert(ask);
+	assert(fdisk_is_ask(ask, YESNO));
+	return ask->data.yesno.result;
+}
+
+/**
+ * fdisk_ask_yesno_set_result:
+ * @ask: ask instance
+ * @result: 1 or 0
+ *
+ * Returns: 0 on success, <0 on error
+ */
+int fdisk_ask_yesno_set_result(struct fdisk_ask *ask, int result)
+{
+	assert(ask);
+	ask->data.yesno.result = result;
+	return 0;
+}
+
+/*
+ * menu
+ */
+int fdisk_ask_menu_set_default(struct fdisk_ask *ask, int dfl)
+{
+	assert(ask);
+	assert(fdisk_is_ask(ask, MENU));
+	ask->data.menu.dfl = dfl;
+	return 0;
+}
+
+/**
+ * fdisk_ask_menu_get_default:
+ * @ask: ask instance
+ *
+ * Returns: default menu item key
+ */
+int fdisk_ask_menu_get_default(struct fdisk_ask *ask)
+{
+	assert(ask);
+	assert(fdisk_is_ask(ask, MENU));
+	return ask->data.menu.dfl;
+}
+
+/**
+ * fdisk_ask_menu_set_result:
+ * @ask: ask instance
+ * @key: result
+ *
+ * Returns: 0 on success, <0 on error
+ */
+int fdisk_ask_menu_set_result(struct fdisk_ask *ask, int key)
+{
+	assert(ask);
+	assert(fdisk_is_ask(ask, MENU));
+	ask->data.menu.result = key;
+	DBG(ASK, ul_debugobj(ask, "menu result: %c\n", key));
+	return 0;
+
+}
+
+/**
+ * fdisk_ask_menu_get_result:
+ * @ask: ask instance
+ * @key: returns selected menu item key
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int fdisk_ask_menu_get_result(struct fdisk_ask *ask, int *key)
+{
+	assert(ask);
+	assert(fdisk_is_ask(ask, MENU));
+	if (key)
+		*key =  ask->data.menu.result;
+	return 0;
+}
+
+/**
+ * fdisk_ask_menu_get_item:
+ * @ask: ask menu instance
+ * @idx: wanted menu item index
+ * @key: returns key of the menu item
+ * @name: returns name of the menu item
+ * @desc: returns description of the menu item
+ *
+ * Returns: 0 on success, <0 on error, >0 if idx out-of-range
+ */
+int fdisk_ask_menu_get_item(struct fdisk_ask *ask, size_t idx, int *key,
+			    const char **name, const char **desc)
+{
+	size_t i;
+	struct ask_menuitem *mi;
+
+	assert(ask);
+	assert(fdisk_is_ask(ask, MENU));
+
+	for (i = 0, mi = ask->data.menu.first; mi; mi = mi->next, i++) {
+		if (i == idx)
+			break;
+	}
+
+	if (!mi)
+		return 1;	/* no more items */
+	if (key)
+		*key = mi->key;
+	if (name)
+		*name = mi->name;
+	if (desc)
+		*desc = mi->desc;
+	return 0;
+}
+
+static void fdisk_ask_menu_reset_items(struct fdisk_ask *ask)
+{
+	struct ask_menuitem *mi;
+
+	assert(ask);
+	assert(fdisk_is_ask(ask, MENU));
+
+	for (mi = ask->data.menu.first; mi; ) {
+		struct ask_menuitem *next = mi->next;
+		free(mi);
+		mi = next;
+	}
+}
+
+/**
+ * fdisk_ask_menu_get_nitems:
+ * @ask: ask instance
+ *
+ * Returns: number of menu items
+ */
+size_t fdisk_ask_menu_get_nitems(struct fdisk_ask *ask)
+{
+	struct ask_menuitem *mi;
+	size_t n;
+
+	assert(ask);
+	assert(fdisk_is_ask(ask, MENU));
+
+	for (n = 0, mi = ask->data.menu.first; mi; mi = mi->next, n++);
+
+	return n;
+}
+
+int fdisk_ask_menu_add_item(struct fdisk_ask *ask, int key,
+			const char *name, const char *desc)
+{
+	struct ask_menuitem *mi;
+
+	assert(ask);
+	assert(fdisk_is_ask(ask, MENU));
+
+	mi = calloc(1, sizeof(*mi));
+	if (!mi)
+		return -ENOMEM;
+	mi->key = key;
+	mi->name = name;
+	mi->desc = desc;
+
+	if (!ask->data.menu.first)
+		ask->data.menu.first = mi;
+	else {
+	        struct ask_menuitem *last = ask->data.menu.first;
+
+		while (last->next)
+			last = last->next;
+		last->next = mi;
+	}
+
+	DBG(ASK, ul_debugobj(ask, "new menu item: %c, \"%s\" (%s)\n", mi->key, mi->name, mi->desc));
+	return 0;
+}
+
+
+/*
+ * print-like
+ */
+
+#define is_print_ask(a) (fdisk_is_ask(a, WARN) || fdisk_is_ask(a, WARNX) || fdisk_is_ask(a, INFO))
+
+/**
+ * fdisk_ask_print_get_errno:
+ * @ask: ask instance
+ *
+ * Returns: error number for warning/error messages
+ */
+int fdisk_ask_print_get_errno(struct fdisk_ask *ask)
+{
+	assert(ask);
+	assert(is_print_ask(ask));
+	return ask->data.print.errnum;
+}
+
+int fdisk_ask_print_set_errno(struct fdisk_ask *ask, int errnum)
+{
+	assert(ask);
+	ask->data.print.errnum = errnum;
+	return 0;
+}
+
+/**
+ * fdisk_ask_print_get_mesg:
+ * @ask: ask instance
+ *
+ * Returns: pointer to message
+ */
+const char *fdisk_ask_print_get_mesg(struct fdisk_ask *ask)
+{
+	assert(ask);
+	assert(is_print_ask(ask));
+	return ask->data.print.mesg;
+}
+
+/* does not reallocate the message! */
+int fdisk_ask_print_set_mesg(struct fdisk_ask *ask, const char *mesg)
+{
+	assert(ask);
+	ask->data.print.mesg = mesg;
+	return 0;
+}
+
+static int do_vprint(struct fdisk_context *cxt, int errnum, int type,
+		     const char *fmt, va_list va)
+{
+	struct fdisk_ask *ask;
+	int rc;
+	char *mesg;
+
+	assert(cxt);
+
+	if (vasprintf(&mesg, fmt, va) < 0)
+		return -ENOMEM;
+
+	ask = fdisk_new_ask();
+	if (!ask) {
+		free(mesg);
+		return -ENOMEM;
+	}
+
+	fdisk_ask_set_type(ask, type);
+	fdisk_ask_print_set_mesg(ask, mesg);
+	if (errnum >= 0)
+		fdisk_ask_print_set_errno(ask, errnum);
+	rc = fdisk_do_ask(cxt, ask);
+
+	fdisk_unref_ask(ask);
+	free(mesg);
+	return rc;
+}
+
+/**
+ * fdisk_info:
+ * @cxt: context
+ * @fmt: printf-like formatted string
+ * @...: variable parametrs
+ *
+ * High-level API to print info messages,
+ *
+ * Returns: 0 on success, <0 on error
+ */
+int fdisk_info(struct fdisk_context *cxt, const char *fmt, ...)
+{
+	int rc;
+	va_list ap;
+
+	assert(cxt);
+	va_start(ap, fmt);
+	rc = do_vprint(cxt, -1, FDISK_ASKTYPE_INFO, fmt, ap);
+	va_end(ap);
+	return rc;
+}
+
+/**
+ * fdisk_info:
+ * @cxt: context
+ * @fmt: printf-like formatted string
+ * @...: variable parametrs
+ *
+ * High-level API to print warning message (errno expected)
+ *
+ * Returns: 0 on success, <0 on error
+ */
+int fdisk_warn(struct fdisk_context *cxt, const char *fmt, ...)
+{
+	int rc;
+	va_list ap;
+
+	assert(cxt);
+	va_start(ap, fmt);
+	rc = do_vprint(cxt, errno, FDISK_ASKTYPE_WARN, fmt, ap);
+	va_end(ap);
+	return rc;
+}
+
+/**
+ * fdisk_warnx:
+ * @cxt: context
+ * @fmt: printf-like formatted string
+ * @...: variable options
+ *
+ * High-level API to print warning message
+ *
+ * Returns: 0 on success, <0 on error
+ */
+int fdisk_warnx(struct fdisk_context *cxt, const char *fmt, ...)
+{
+	int rc;
+	va_list ap;
+
+	assert(cxt);
+	va_start(ap, fmt);
+	rc = do_vprint(cxt, -1, FDISK_ASKTYPE_WARNX, fmt, ap);
+	va_end(ap);
+	return rc;
+}
+
+int fdisk_info_new_partition(
+			struct fdisk_context *cxt,
+			int num, fdisk_sector_t start, fdisk_sector_t stop,
+			struct fdisk_parttype *t)
+{
+	int rc;
+	char *str = size_to_human_string(SIZE_SUFFIX_3LETTER | SIZE_SUFFIX_SPACE,
+				     (uint64_t)(stop - start + 1) * cxt->sector_size);
+
+	rc = fdisk_info(cxt,
+			_("Created a new partition %d of type '%s' and of size %s."),
+			num, t ? t->name : _("Unknown"), str);
+	free(str);
+	return rc;
+}
+
+#ifdef TEST_PROGRAM
+int test_ranges(struct fdisk_test *ts, int argc, char *argv[])
+{
+	/*                1  -  3,       6,    8, 9,   11    13 */
+	size_t nums[] = { 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1 };
+	size_t numx[] = { 0, 0, 0 };
+	char range[BUFSIZ], *ptr = range;
+	size_t i, len = sizeof(range), begin = 0, run = 0;
+
+	for (i = 0; i < ARRAY_SIZE(nums); i++) {
+		if (!nums[i])
+			continue;
+		ptr = mk_string_list(ptr, &len, &begin, &run, i, 0);
+	}
+	mk_string_list(ptr, &len, &begin, &run, -1, 0);
+	printf("list: '%s'\n", range);
+
+	ptr = range;
+	len = sizeof(range), begin = 0, run = 0;
+	for (i = 0; i < ARRAY_SIZE(numx); i++) {
+		if (!numx[i])
+			continue;
+		ptr = mk_string_list(ptr, &len, &begin, &run, i, 0);
+	}
+	mk_string_list(ptr, &len, &begin, &run, -1, 0);
+	printf("empty list: '%s'\n", range);
+
+	return 0;
+}
+
+int main(int argc, char *argv[])
+{
+	struct fdisk_test tss[] = {
+	{ "--ranges",  test_ranges,    "generates ranges" },
+	{ NULL }
+	};
+
+	return fdisk_run_test(tss, argc, argv);
+}
+
+#endif
diff --git a/libblkid/libfdisk/src/bsd.c b/libblkid/libfdisk/src/bsd.c
new file mode 100644
index 0000000..618a3ee
--- /dev/null
+++ b/libblkid/libfdisk/src/bsd.c
@@ -0,0 +1,992 @@
+/*
+ * Copyright (C) 2007-2013 Karel Zak <kzak@redhat.com>
+ *
+ * Based on the original code from fdisk
+ *    written by Bernhard Fastenrath (fasten@informatik.uni-bonn.de)
+ *    with code from the NetBSD disklabel command.
+ *
+ *    Arnaldo Carvalho de Melo <acme@conectiva.com.br>, March 1999
+ *    David Huggins-Daines <dhuggins@linuxcare.com>, January 2000
+ */
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/param.h>
+
+#include "nls.h"
+#include "blkdev.h"
+#include "fdiskP.h"
+#include "pt-mbr.h"
+#include "pt-bsd.h"
+#include "all-io.h"
+
+
+/**
+ * SECTION: bsd
+ * @title: BSD
+ * @short_description: disk label specific functions
+ *
+ */
+
+static const char *bsd_dktypenames[] = {
+	"unknown",
+	"SMD",
+	"MSCP",
+	"old DEC",
+	"SCSI",
+	"ESDI",
+	"ST506",
+	"HP-IB",
+	"HP-FL",
+	"type 9",
+	"floppy",
+	0
+};
+#define BSD_DKMAXTYPES	(ARRAY_SIZE(bsd_dktypenames) - 1)
+
+static struct fdisk_parttype bsd_fstypes[] = {
+        {BSD_FS_UNUSED, "unused"},
+	{BSD_FS_SWAP,   "swap"},
+	{BSD_FS_V6,     "Version 6"},
+	{BSD_FS_V7,     "Version 7"},
+	{BSD_FS_SYSV,   "System V"},
+	{BSD_FS_V71K,   "4.1BSD"},
+	{BSD_FS_V8,     "Eighth Edition"},
+	{BSD_FS_BSDFFS, "4.2BSD"},
+#ifdef __alpha__
+	{BSD_FS_EXT2,   "ext2"},
+#else
+	{BSD_FS_MSDOS,  "MS-DOS"},
+#endif
+	{BSD_FS_BSDLFS, "4.4LFS"},
+	{BSD_FS_OTHER,  "unknown"},
+	{BSD_FS_HPFS,   "HPFS"},
+	{BSD_FS_ISO9660,"ISO-9660"},
+	{BSD_FS_BOOT,   "boot"},
+	{BSD_FS_ADOS,   "ADOS"},
+	{BSD_FS_HFS,    "HFS"},
+	{BSD_FS_ADVFS,	"AdvFS"},
+	{ 0, NULL }
+};
+#define BSD_FSMAXTYPES (ARRAY_SIZE(bsd_fstypes)-1)
+
+/*
+ * in-memory fdisk BSD stuff
+ */
+struct fdisk_bsd_label {
+	struct fdisk_label	head;		/* generic part */
+
+	struct dos_partition *dos_part;		/* parent */
+	struct bsd_disklabel bsd;		/* on disk label */
+#if defined (__alpha__)
+	/* We access this through a u_int64_t * when checksumming */
+	char bsdbuffer[BSD_BBSIZE] __attribute__((aligned(8)));
+#else
+	char bsdbuffer[BSD_BBSIZE];
+#endif
+};
+
+static int bsd_list_disklabel(struct fdisk_context *cxt);
+static int bsd_initlabel(struct fdisk_context *cxt);
+static int bsd_readlabel(struct fdisk_context *cxt);
+static void sync_disks(struct fdisk_context *cxt);
+
+static inline struct fdisk_bsd_label *self_label(struct fdisk_context *cxt)
+{
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, BSD));
+
+	return (struct fdisk_bsd_label *) cxt->label;
+}
+
+static inline struct bsd_disklabel *self_disklabel(struct fdisk_context *cxt)
+{
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, BSD));
+
+	return &((struct fdisk_bsd_label *) cxt->label)->bsd;
+}
+
+static struct fdisk_parttype *bsd_partition_parttype(
+		struct fdisk_context *cxt,
+		struct bsd_partition *p)
+{
+	struct fdisk_parttype *t
+		= fdisk_label_get_parttype_from_code(cxt->label, p->p_fstype);
+	return t ? : fdisk_new_unknown_parttype(p->p_fstype, NULL);
+}
+
+
+#if defined (__alpha__)
+static void alpha_bootblock_checksum (char *boot)
+{
+	uint64_t *dp = (uint64_t *) boot, sum = 0;
+	int i;
+
+	for (i = 0; i < 63; i++)
+		sum += dp[i];
+	dp[63] = sum;
+}
+#endif /* __alpha__ */
+
+#define HIDDEN_MASK	0x10
+
+static int is_bsd_partition_type(int type)
+{
+	return (type == MBR_FREEBSD_PARTITION ||
+		type == (MBR_FREEBSD_PARTITION ^ HIDDEN_MASK) ||
+		type == MBR_NETBSD_PARTITION ||
+		type == (MBR_NETBSD_PARTITION ^ HIDDEN_MASK) ||
+		type == MBR_OPENBSD_PARTITION ||
+		type == (MBR_OPENBSD_PARTITION ^ HIDDEN_MASK));
+}
+
+/*
+ * look for DOS partition usable for nested BSD partition table
+ */
+static int bsd_assign_dos_partition(struct fdisk_context *cxt)
+{
+	struct fdisk_bsd_label *l = self_label(cxt);
+	size_t i;
+
+	for (i = 0; i < 4; i++) {
+		fdisk_sector_t ss;
+
+		l->dos_part = fdisk_dos_get_partition(cxt->parent, i);
+
+		if (!l->dos_part || !is_bsd_partition_type(l->dos_part->sys_ind))
+			continue;
+
+		ss = dos_partition_get_start(l->dos_part);
+		if (!ss) {
+			fdisk_warnx(cxt, _("Partition %zd: has invalid starting "
+					   "sector 0."), i + 1);
+			return -1;
+		}
+
+		if (cxt->parent->dev_path) {
+			free(cxt->dev_path);
+			cxt->dev_path = fdisk_partname(
+						cxt->parent->dev_path, i + 1);
+		}
+
+		DBG(LABEL, ul_debug("partition %zu assigned to BSD", i + 1));
+		return 0;
+	}
+
+	fdisk_warnx(cxt, _("There is no *BSD partition on %s."),
+				cxt->parent->dev_path);
+	free(cxt->dev_path);
+	cxt->dev_path = NULL;
+	l->dos_part = NULL;
+	return 1;
+}
+
+static int bsd_probe_label(struct fdisk_context *cxt)
+{
+	int rc = 0;
+
+	if (cxt->parent)
+		rc = bsd_assign_dos_partition(cxt);	/* nested BSD partiotn table */
+	if (!rc)
+		rc = bsd_readlabel(cxt);
+	if (!rc)
+		return 1;	/* found BSD */
+	return 0;		/* not found */
+}
+
+static int set_parttype(
+		struct fdisk_context *cxt,
+		size_t partnum,
+		struct fdisk_parttype *t)
+{
+	struct bsd_partition *p;
+	struct bsd_disklabel *d = self_disklabel(cxt);
+
+	if (partnum >= d->d_npartitions || !t || t->code > UINT8_MAX)
+		return -EINVAL;
+
+	p = &d->d_partitions[partnum];
+	if (t->code == p->p_fstype)
+		return 0;
+
+	p->p_fstype = t->code;
+	fdisk_label_set_changed(cxt->label, 1);
+	return 0;
+}
+
+static int bsd_add_partition(struct fdisk_context *cxt,
+			     struct fdisk_partition *pa,
+			     size_t *partno)
+{
+	struct fdisk_bsd_label *l = self_label(cxt);
+	struct bsd_disklabel *d = self_disklabel(cxt);
+	size_t i;
+	unsigned int begin = 0, end;
+	int rc = 0;
+
+	rc = fdisk_partition_next_partno(pa, cxt, &i);
+	if (rc)
+		return rc;
+	if (i >= BSD_MAXPARTITIONS)
+		return -ERANGE;
+	if (l->dos_part) {
+		begin = dos_partition_get_start(l->dos_part);
+		end = begin + dos_partition_get_size(l->dos_part) - 1;
+	} else
+		end = d->d_secperunit - 1;
+
+	/*
+	 * First sector
+	 */
+	if (pa && pa->start_follow_default)
+		;
+	else if (pa && fdisk_partition_has_start(pa)) {
+		if (pa->start < begin || pa->start > end)
+			return -ERANGE;
+		begin = pa->start;
+	} else {
+		struct fdisk_ask *ask = fdisk_new_ask();
+
+		if (!ask)
+			return -ENOMEM;
+		fdisk_ask_set_query(ask,
+			fdisk_use_cylinders(cxt) ?
+			_("First cylinder") : _("First sector"));
+		fdisk_ask_set_type(ask, FDISK_ASKTYPE_NUMBER);
+		fdisk_ask_number_set_low(ask, fdisk_cround(cxt, begin));
+		fdisk_ask_number_set_default(ask, fdisk_cround(cxt, begin));
+		fdisk_ask_number_set_high(ask, fdisk_cround(cxt, end));
+
+		rc = fdisk_do_ask(cxt, ask);
+		begin = fdisk_ask_number_get_result(ask);
+		fdisk_unref_ask(ask);
+		if (rc)
+			return rc;
+		if (fdisk_use_cylinders(cxt))
+			begin = (begin - 1) * d->d_secpercyl;
+	}
+
+	/*
+	 * Last sector
+	 */
+	if (pa && pa->end_follow_default)
+		;
+	else if (pa && fdisk_partition_has_size(pa)) {
+		if (begin + pa->size > end)
+			return -ERANGE;
+		end = begin + pa->size - 1ULL;
+	} else {
+		/* ask user by dialog */
+		struct fdisk_ask *ask = fdisk_new_ask();
+
+		if (!ask)
+			return -ENOMEM;
+		fdisk_ask_set_type(ask, FDISK_ASKTYPE_OFFSET);
+
+		if (fdisk_use_cylinders(cxt)) {
+			fdisk_ask_set_query(ask, _("Last cylinder, +cylinders or +size{K,M,G,T,P}"));
+			fdisk_ask_number_set_unit(ask,
+				     cxt->sector_size *
+				     fdisk_get_units_per_sector(cxt));
+		} else {
+			fdisk_ask_set_query(ask, _("Last sector, +sectors or +size{K,M,G,T,P}"));
+			fdisk_ask_number_set_unit(ask,cxt->sector_size);
+		}
+
+		fdisk_ask_number_set_low(ask, fdisk_cround(cxt, begin));
+		fdisk_ask_number_set_default(ask, fdisk_cround(cxt, end));
+		fdisk_ask_number_set_high(ask, fdisk_cround(cxt, end));
+		fdisk_ask_number_set_base(ask, fdisk_cround(cxt, begin));
+
+		rc = fdisk_do_ask(cxt, ask);
+		end = fdisk_ask_number_get_result(ask);
+		fdisk_unref_ask(ask);
+		if (rc)
+			return rc;
+		if (fdisk_use_cylinders(cxt))
+			end = end * d->d_secpercyl - 1;
+	}
+
+	d->d_partitions[i].p_size   = end - begin + 1;
+	d->d_partitions[i].p_offset = begin;
+	d->d_partitions[i].p_fstype = BSD_FS_UNUSED;
+
+	if (i >= d->d_npartitions)
+		d->d_npartitions = i + 1;
+	cxt->label->nparts_cur = d->d_npartitions;
+
+	if (pa && pa->type)
+		set_parttype(cxt, i, pa->type);
+
+	fdisk_label_set_changed(cxt->label, 1);
+	if (partno)
+		*partno = i;
+	return 0;
+}
+
+static int bsd_set_partition(struct fdisk_context *cxt, size_t n,
+			     struct fdisk_partition *pa)
+{
+	struct bsd_partition *p;
+	struct fdisk_bsd_label *l = self_label(cxt);
+	struct bsd_disklabel *d = self_disklabel(cxt);
+
+	if (n >= d->d_npartitions)
+		return -EINVAL;
+
+	p = &d->d_partitions[n];
+
+	/* we have to stay within parental DOS partition */
+	if (l->dos_part && (fdisk_partition_has_start(pa) ||
+			    fdisk_partition_has_size(pa))) {
+
+		fdisk_sector_t dosbegin = dos_partition_get_start(l->dos_part);
+		fdisk_sector_t dosend = dosbegin + dos_partition_get_size(l->dos_part) - 1;
+		fdisk_sector_t begin = fdisk_partition_has_start(pa) ? pa->start : p->p_offset;
+		fdisk_sector_t end = begin + (fdisk_partition_has_size(pa) ? pa->size : p->p_size) - 1;
+
+		if (begin < dosbegin || begin > dosend)
+			return -ERANGE;
+		if (end < dosbegin || end > dosend)
+			return -ERANGE;
+	}
+
+	if (pa->type) {
+		int rc = set_parttype(cxt, n, pa->type);
+		if (rc)
+			return rc;
+	}
+
+	if (fdisk_partition_has_start(pa))
+		d->d_partitions[n].p_offset = pa->start;
+	if (fdisk_partition_has_size(pa))
+		d->d_partitions[n].p_size = pa->size;
+
+	fdisk_label_set_changed(cxt->label, 1);
+	return 0;
+}
+
+
+/* Returns 0 on success, < 0 on error. */
+static int bsd_create_disklabel(struct fdisk_context *cxt)
+{
+	int rc, yes = 0;
+	struct bsd_disklabel *d = self_disklabel(cxt);
+
+	fdisk_info(cxt, _("The device %s does not contain BSD disklabel."), cxt->dev_path);
+	rc = fdisk_ask_yesno(cxt,
+			_("Do you want to create a BSD disklabel?"),
+			&yes);
+	if (rc)
+		return rc;
+	if (!yes)
+		return 1;
+	if (cxt->parent) {
+		rc = bsd_assign_dos_partition(cxt);
+		if (rc == 1)
+			/* not found DOS partition usable for BSD label */
+			rc = -EINVAL;
+	}
+	if (rc)
+		return rc;
+
+	rc = bsd_initlabel(cxt);
+	if (!rc) {
+		int org = fdisk_is_details(cxt);
+
+		cxt->label->nparts_cur = d->d_npartitions;
+		cxt->label->nparts_max = BSD_MAXPARTITIONS;
+
+		fdisk_enable_details(cxt, 1);
+		bsd_list_disklabel(cxt);
+		fdisk_enable_details(cxt, org);
+	}
+
+	return rc;
+}
+
+static int bsd_delete_part(
+		struct fdisk_context *cxt,
+		size_t partnum)
+{
+	struct bsd_disklabel *d = self_disklabel(cxt);
+
+	d->d_partitions[partnum].p_size   = 0;
+	d->d_partitions[partnum].p_offset = 0;
+	d->d_partitions[partnum].p_fstype = BSD_FS_UNUSED;
+
+	if (d->d_npartitions == partnum + 1)
+		while (!d->d_partitions[d->d_npartitions - 1].p_size)
+			d->d_npartitions--;
+
+	cxt->label->nparts_cur = d->d_npartitions;
+	fdisk_label_set_changed(cxt->label, 1);
+	return 0;
+}
+
+static int bsd_list_disklabel(struct fdisk_context *cxt)
+{
+	struct bsd_disklabel *d = self_disklabel(cxt);
+
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, BSD));
+
+	if (fdisk_is_details(cxt)) {
+		fdisk_info(cxt, "# %s:", cxt->dev_path);
+
+		if ((unsigned) d->d_type < BSD_DKMAXTYPES)
+			fdisk_info(cxt, _("type: %s"), bsd_dktypenames[d->d_type]);
+		else
+			fdisk_info(cxt, _("type: %d"), d->d_type);
+
+		fdisk_info(cxt, _("disk: %.*s"), (int) sizeof(d->d_typename), d->d_typename);
+		fdisk_info(cxt, _("label: %.*s"), (int) sizeof(d->d_packname), d->d_packname);
+
+		fdisk_info(cxt, _("flags: %s"),
+			d->d_flags & BSD_D_REMOVABLE ? _(" removable") :
+			d->d_flags & BSD_D_ECC ? _(" ecc") :
+			d->d_flags & BSD_D_BADSECT ? _(" badsect") : "");
+
+		/* On various machines the fields of *lp are short/int/long */
+		/* In order to avoid problems, we cast them all to long. */
+		fdisk_info(cxt, _("bytes/sector: %ld"), (long) d->d_secsize);
+		fdisk_info(cxt, _("sectors/track: %ld"), (long) d->d_nsectors);
+		fdisk_info(cxt, _("tracks/cylinder: %ld"), (long) d->d_ntracks);
+		fdisk_info(cxt, _("sectors/cylinder: %ld"), (long) d->d_secpercyl);
+		fdisk_info(cxt, _("cylinders: %ld"), (long) d->d_ncylinders);
+		fdisk_info(cxt, _("rpm: %d"), d->d_rpm);
+		fdisk_info(cxt, _("interleave: %d"), d->d_interleave);
+		fdisk_info(cxt, _("trackskew: %d"), d->d_trackskew);
+		fdisk_info(cxt, _("cylinderskew: %d"), d->d_cylskew);
+		fdisk_info(cxt, _("headswitch: %ld (milliseconds)"), (long) d->d_headswitch);
+		fdisk_info(cxt, _("track-to-track seek: %ld (milliseconds)"), (long) d->d_trkseek);
+	}
+
+	fdisk_info(cxt, _("partitions: %d"), d->d_npartitions);
+
+	return 0;
+}
+
+static int bsd_get_partition(struct fdisk_context *cxt, size_t n,
+			     struct fdisk_partition *pa)
+{
+	struct bsd_partition *p;
+	struct bsd_disklabel *d = self_disklabel(cxt);
+
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, BSD));
+
+	if (n >= d->d_npartitions)
+		return -EINVAL;
+
+	p = &d->d_partitions[n];
+
+	pa->used = p->p_size ? 1 : 0;
+	if (!pa->used)
+		return 0;
+
+	if (fdisk_use_cylinders(cxt) && d->d_secpercyl) {
+		pa->start_post = p->p_offset % d->d_secpercyl ? '*' : ' ';
+		pa->end_post = (p->p_offset + p->p_size) % d->d_secpercyl ? '*' : ' ';
+	}
+
+	pa->start = p->p_offset;
+	pa->size = p->p_size;
+	pa->type = bsd_partition_parttype(cxt, p);
+
+	if (p->p_fstype == BSD_FS_UNUSED || p->p_fstype == BSD_FS_BSDFFS) {
+		pa->fsize = p->p_fsize;
+		pa->bsize = p->p_fsize * p->p_frag;
+	}
+	if (p->p_fstype == BSD_FS_BSDFFS)
+		pa->cpg = p->p_cpg;
+
+	return 0;
+}
+
+static uint32_t ask_uint32(struct fdisk_context *cxt,
+		uint32_t dflt, char *mesg)
+{
+	uintmax_t res;
+
+	if (fdisk_ask_number(cxt, min(dflt, (uint32_t) 1), dflt,
+				UINT32_MAX, mesg, &res) == 0)
+		return res;
+	return dflt;
+}
+
+static uint16_t ask_uint16(struct fdisk_context *cxt,
+		uint16_t dflt, char *mesg)
+{
+	uintmax_t res;
+
+	if (fdisk_ask_number(cxt, min(dflt, (uint16_t) 1),
+				dflt, UINT16_MAX, mesg, &res) == 0)
+		return res;
+	return dflt;
+}
+
+/**
+ * fdisk_bsd_edit_disklabel:
+ * @cxt: context
+ *
+ * Edits fields in BSD disk label.
+ *
+ * Returns: 0 on success, <0 on error
+ */
+int fdisk_bsd_edit_disklabel(struct fdisk_context *cxt)
+{
+	struct bsd_disklabel *d = self_disklabel(cxt);
+	uintmax_t res;
+
+#if defined (__alpha__) || defined (__ia64__)
+	if (fdisk_ask_number(cxt, DEFAULT_SECTOR_SIZE, d->d_secsize,
+			     UINT32_MAX, _("bytes/sector"), &res) == 0)
+		d->d_secsize = res;
+
+	d->d_nsectors = ask_uint32(cxt, d->d_nsectors, _("sectors/track"));
+	d->d_ntracks = ask_uint32(cxt, d->d_ntracks, _("tracks/cylinder"));
+	d->d_ncylinders = ask_uint32(cxt, d->d_ncylinders  ,_("cylinders"));
+#endif
+	if (fdisk_ask_number(cxt, 1, d->d_nsectors * d->d_ntracks,
+			     d->d_nsectors * d->d_ntracks,
+			     _("sectors/cylinder"), &res) == 0)
+		d->d_secpercyl = res;
+
+	d->d_rpm = ask_uint16(cxt, d->d_rpm, _("rpm"));
+	d->d_interleave = ask_uint16(cxt, d->d_interleave, _("interleave"));
+	d->d_trackskew = ask_uint16(cxt, d->d_trackskew, _("trackskew"));
+	d->d_cylskew = ask_uint16(cxt, d->d_cylskew, _("cylinderskew"));
+
+	d->d_headswitch = ask_uint32(cxt, d->d_headswitch, _("headswitch"));
+	d->d_trkseek = ask_uint32(cxt, d->d_trkseek, _("track-to-track seek"));
+
+	d->d_secperunit = d->d_secpercyl * d->d_ncylinders;
+	return 0;
+}
+
+static int bsd_get_bootstrap(struct fdisk_context *cxt,
+			char *path, void *ptr, int size)
+{
+	int fd;
+
+	if ((fd = open(path, O_RDONLY)) < 0) {
+		fdisk_warn(cxt, _("cannot open %s"), path);
+		return -errno;
+	}
+
+	if (read_all(fd, ptr, size) != size) {
+		fdisk_warn(cxt, _("cannot read %s"), path);
+		close(fd);
+		return -errno;
+	}
+
+	fdisk_info(cxt, _("The bootstrap file %s successfully loaded."), path);
+	close (fd);
+	return 0;
+}
+
+/**
+ * fdisk_bsd_write_bootstrap:
+ * @cxt: context
+ *
+ * Install bootstrap file to the BSD device
+ */
+int fdisk_bsd_write_bootstrap(struct fdisk_context *cxt)
+{
+	struct bsd_disklabel dl, *d = self_disklabel(cxt);
+	struct fdisk_bsd_label *l = self_label(cxt);
+	char *name = d->d_type == BSD_DTYPE_SCSI ? "sd" : "wd";
+	char buf[BUFSIZ];
+	char *res, *dp, *p;
+	int rc;
+	fdisk_sector_t sector;
+
+	snprintf(buf, sizeof(buf),
+		_("Bootstrap: %1$sboot -> boot%1$s (default %1$s)"),
+		name);
+	rc = fdisk_ask_string(cxt, buf, &res);
+	if (rc)
+		goto done;
+	if (res && *res)
+		name = res;
+
+	snprintf(buf, sizeof(buf), "%s/%sboot", BSD_LINUX_BOOTDIR, name);
+	rc = bsd_get_bootstrap(cxt, buf, l->bsdbuffer,	(int) d->d_secsize);
+	if (rc)
+		goto done;
+
+	/* We need a backup of the disklabel (might have changed). */
+	dp = &l->bsdbuffer[BSD_LABELSECTOR * DEFAULT_SECTOR_SIZE];
+	memmove(&dl, dp, sizeof(struct bsd_disklabel));
+
+	/* The disklabel will be overwritten by 0's from bootxx anyway */
+	memset(dp, 0, sizeof(struct bsd_disklabel));
+
+	snprintf(buf, sizeof(buf), "%s/boot%s", BSD_LINUX_BOOTDIR, name);
+	rc = bsd_get_bootstrap(cxt, buf,
+			&l->bsdbuffer[d->d_secsize],
+			(int) d->d_bbsize - d->d_secsize);
+	if (rc)
+		goto done;
+
+	/* check end of the bootstrap */
+	for (p = dp; p < dp + sizeof(struct bsd_disklabel); p++) {
+		if (!*p)
+			continue;
+		fdisk_warnx(cxt, _("Bootstrap overlaps with disklabel!"));
+		return -EINVAL;
+	}
+
+	/* move disklabel back */
+	memmove(dp, &dl, sizeof(struct bsd_disklabel));
+
+	sector = 0;
+	if (l->dos_part)
+		sector = dos_partition_get_start(l->dos_part);
+#if defined (__alpha__)
+	alpha_bootblock_checksum(l->bsdbuffer);
+#endif
+	if (lseek(cxt->dev_fd, (off_t) sector * DEFAULT_SECTOR_SIZE, SEEK_SET) == -1) {
+		fdisk_warn(cxt, _("seek on %s failed"), cxt->dev_path);
+		rc = -errno;
+		goto done;
+	}
+	if (write_all(cxt->dev_fd, l->bsdbuffer, BSD_BBSIZE)) {
+		fdisk_warn(cxt, _("cannot write %s"), cxt->dev_path);
+		rc = -errno;
+		goto done;
+	}
+
+	fdisk_info(cxt, _("Bootstrap installed on %s."), cxt->dev_path);
+	sync_disks(cxt);
+
+	rc = 0;
+done:
+	free(res);
+	return rc;
+}
+
+static unsigned short bsd_dkcksum (struct bsd_disklabel *lp)
+{
+	unsigned short *start, *end;
+	unsigned short sum = 0;
+
+	start = (unsigned short *) lp;
+	end = (unsigned short *) &lp->d_partitions[lp->d_npartitions];
+	while (start < end)
+		sum ^= *start++;
+	return sum;
+}
+
+static int bsd_initlabel (struct fdisk_context *cxt)
+{
+	struct fdisk_bsd_label *l = self_label(cxt);
+	struct bsd_disklabel *d = self_disklabel(cxt);
+	struct bsd_partition *pp;
+
+	memset (d, 0, sizeof (struct bsd_disklabel));
+
+	d -> d_magic = BSD_DISKMAGIC;
+
+	if (strncmp (cxt->dev_path, "/dev/sd", 7) == 0)
+		d -> d_type = BSD_DTYPE_SCSI;
+	else
+		d -> d_type = BSD_DTYPE_ST506;
+
+#if !defined (__alpha__)
+	d -> d_flags = BSD_D_DOSPART;
+#else
+	d -> d_flags = 0;
+#endif
+	d -> d_secsize = DEFAULT_SECTOR_SIZE;		/* bytes/sector  */
+	d -> d_nsectors = cxt->geom.sectors;		/* sectors/track */
+	d -> d_ntracks = cxt->geom.heads;		/* tracks/cylinder (heads) */
+	d -> d_ncylinders = cxt->geom.cylinders;
+	d -> d_secpercyl  = cxt->geom.sectors * cxt->geom.heads;/* sectors/cylinder */
+	if (d -> d_secpercyl == 0)
+		d -> d_secpercyl = 1;		/* avoid segfaults */
+	d -> d_secperunit = d -> d_secpercyl * d -> d_ncylinders;
+
+	d -> d_rpm = 3600;
+	d -> d_interleave = 1;
+	d -> d_trackskew = 0;
+	d -> d_cylskew = 0;
+	d -> d_headswitch = 0;
+	d -> d_trkseek = 0;
+
+	d -> d_magic2 = BSD_DISKMAGIC;
+	d -> d_bbsize = BSD_BBSIZE;
+	d -> d_sbsize = BSD_SBSIZE;
+
+	if (l->dos_part) {
+		d->d_npartitions = 4;
+
+		pp = &d->d_partitions[2];	/* Partition C should be the NetBSD partition */
+		pp->p_offset = dos_partition_get_start(l->dos_part);
+		pp->p_size   = dos_partition_get_size(l->dos_part);
+		pp->p_fstype = BSD_FS_UNUSED;
+
+		pp = &d -> d_partitions[3];	/* Partition D should be the whole disk */
+		pp->p_offset = 0;
+		pp->p_size   = d->d_secperunit;
+		pp->p_fstype = BSD_FS_UNUSED;
+	} else {
+		d->d_npartitions = 3;
+
+		pp = &d->d_partitions[2];	/* Partition C should be the whole disk */
+		pp->p_offset = 0;
+		pp->p_size   = d->d_secperunit;
+		pp->p_fstype = BSD_FS_UNUSED;
+	}
+
+	return 0;
+}
+
+/*
+ * Read a bsd_disklabel from sector 0 or from the starting sector of p.
+ * If it has the right magic, return 0.
+ */
+static int bsd_readlabel(struct fdisk_context *cxt)
+{
+	struct fdisk_bsd_label *l;
+	struct bsd_disklabel *d;
+	int t;
+	off_t offset = 0;
+
+	l = self_label(cxt);
+	d = self_disklabel(cxt);
+
+	if (l->dos_part)
+		/* BSD is nested within DOS partition, get the begin of the
+		 * partition. Note that DOS uses native sector size. */
+		offset = dos_partition_get_start(l->dos_part) * cxt->sector_size;
+
+	if (lseek(cxt->dev_fd, offset, SEEK_SET) == -1)
+		return -1;
+	if (read_all(cxt->dev_fd, l->bsdbuffer, sizeof(l->bsdbuffer)) < 0)
+		return errno ? -errno : -1;
+
+	/* The offset to begin of the disk label. Note that BSD uses
+	 * 512-byte (default) sectors. */
+	memmove(d, &l->bsdbuffer[BSD_LABELSECTOR * DEFAULT_SECTOR_SIZE
+			      + BSD_LABELOFFSET], sizeof(*d));
+
+	if (d->d_magic != BSD_DISKMAGIC || d->d_magic2 != BSD_DISKMAGIC) {
+		DBG(LABEL, ul_debug("not found magic"));
+		return -1;
+	}
+
+	for (t = d->d_npartitions; t < BSD_MAXPARTITIONS; t++) {
+		d->d_partitions[t].p_size   = 0;
+		d->d_partitions[t].p_offset = 0;
+		d->d_partitions[t].p_fstype = BSD_FS_UNUSED;
+	}
+
+	if (d->d_npartitions > BSD_MAXPARTITIONS)
+		fdisk_warnx(cxt, ("Too many partitions (%d, maximum is %d)."),
+				d->d_npartitions, BSD_MAXPARTITIONS);
+
+	/* let's follow in-PT geometry */
+	cxt->geom.sectors = d->d_nsectors;
+	cxt->geom.heads = d->d_ntracks;
+	cxt->geom.cylinders = d->d_ncylinders;
+
+	cxt->label->nparts_cur = d->d_npartitions;
+	cxt->label->nparts_max = BSD_MAXPARTITIONS;
+	DBG(LABEL, ul_debug("read BSD label"));
+	return 0;
+}
+
+static int bsd_write_disklabel(struct fdisk_context *cxt)
+{
+	off_t offset = 0;
+	struct fdisk_bsd_label *l = self_label(cxt);
+	struct bsd_disklabel *d = self_disklabel(cxt);
+
+
+	if (l->dos_part)
+		offset = dos_partition_get_start(l->dos_part) * cxt->sector_size;
+
+	d->d_checksum = 0;
+	d->d_checksum = bsd_dkcksum(d);
+
+	/* Update label within boot block. */
+	memmove(&l->bsdbuffer[BSD_LABELSECTOR * DEFAULT_SECTOR_SIZE
+			   + BSD_LABELOFFSET], d, sizeof(*d));
+
+#if defined (__alpha__) && BSD_LABELSECTOR == 0
+	/* Write the checksum to the end of the first sector. */
+	alpha_bootblock_checksum(l->bsdbuffer);
+#endif
+	if (lseek(cxt->dev_fd, offset, SEEK_SET) == -1) {
+		fdisk_warn(cxt, _("seek on %s failed"), cxt->dev_path);
+		return -errno;
+	}
+	if (write_all(cxt->dev_fd, l->bsdbuffer, sizeof(l->bsdbuffer))) {
+		fdisk_warn(cxt, _("cannot write %s"), cxt->dev_path);
+		return -errno;
+	}
+	sync_disks(cxt);
+
+	fdisk_info(cxt, _("Disklabel written to %s."), cxt->dev_path);
+	return 0;
+}
+
+static void sync_disks(struct fdisk_context *cxt)
+{
+	fdisk_info(cxt, _("Syncing disks."));
+	sync();
+}
+
+static int bsd_translate_fstype (int linux_type)
+{
+	switch (linux_type) {
+	case 0x01: /* DOS 12-bit FAT   */
+	case 0x04: /* DOS 16-bit <32M  */
+	case 0x06: /* DOS 16-bit >=32M */
+	case 0xe1: /* DOS access       */
+	case 0xe3: /* DOS R/O          */
+#if !defined (__alpha__)
+	case 0xf2: /* DOS secondary    */
+		return BSD_FS_MSDOS;
+#endif
+	case 0x07: /* OS/2 HPFS        */
+		return BSD_FS_HPFS;
+	default:
+		break;
+	}
+
+	return BSD_FS_OTHER;
+}
+
+/**
+ * fdisk_bsd_link_partition:
+ * @cxt: context
+ *
+ * Links partition from parent (DOS) to nested BSD partition table.
+ *
+ * Returns: 0 on success, <0 on error
+ */
+int fdisk_bsd_link_partition(struct fdisk_context *cxt)
+{
+	size_t k, i;
+	int rc;
+	struct dos_partition *p;
+	struct bsd_disklabel *d = self_disklabel(cxt);
+
+	if (!cxt->parent || !fdisk_is_label(cxt->parent, DOS)) {
+		fdisk_warnx(cxt, _("BSD label is not nested within a DOS partition."));
+		return -EINVAL;
+	}
+
+	/* ask for DOS partition */
+	rc = fdisk_ask_partnum(cxt->parent, &k, FALSE);
+	if (rc)
+		return rc;
+	/* ask for BSD partition */
+	rc = fdisk_ask_partnum(cxt, &i, TRUE);
+	if (rc)
+		return rc;
+
+	if (i >= BSD_MAXPARTITIONS)
+		return -EINVAL;
+
+	p = fdisk_dos_get_partition(cxt->parent, k);
+
+	d->d_partitions[i].p_size   = dos_partition_get_size(p);
+	d->d_partitions[i].p_offset = dos_partition_get_start(p);
+	d->d_partitions[i].p_fstype = bsd_translate_fstype(p->sys_ind);
+
+	if (i >= d->d_npartitions)
+		d->d_npartitions = i + 1;
+
+	cxt->label->nparts_cur = d->d_npartitions;
+	fdisk_label_set_changed(cxt->label, 1);
+
+	fdisk_info(cxt, _("BSD partition '%c' linked to DOS partition %zu."),
+			'a' + (int) i, k + 1);
+	return 0;
+}
+
+
+static int bsd_partition_is_used(
+		struct fdisk_context *cxt,
+		size_t partnum)
+{
+	struct bsd_disklabel *d = self_disklabel(cxt);
+
+	if (partnum >= BSD_MAXPARTITIONS)
+		return 0;
+
+	return d->d_partitions[partnum].p_size ? 1 : 0;
+}
+
+
+static const struct fdisk_label_operations bsd_operations =
+{
+	.probe		= bsd_probe_label,
+	.list		= bsd_list_disklabel,
+	.write		= bsd_write_disklabel,
+	.create		= bsd_create_disklabel,
+
+	.del_part	= bsd_delete_part,
+	.get_part	= bsd_get_partition,
+	.set_part	= bsd_set_partition,
+	.add_part	= bsd_add_partition,
+
+	.part_is_used   = bsd_partition_is_used,
+};
+
+static const struct fdisk_field bsd_fields[] =
+{
+	{ FDISK_FIELD_DEVICE,	N_("Slice"),	  1,	0 },
+	{ FDISK_FIELD_START,	N_("Start"),	  5,	FDISK_FIELDFL_NUMBER },
+	{ FDISK_FIELD_END,	N_("End"),	  5,	FDISK_FIELDFL_NUMBER },
+	{ FDISK_FIELD_SECTORS,	N_("Sectors"),    5,	FDISK_FIELDFL_NUMBER },
+	{ FDISK_FIELD_CYLINDERS,N_("Cylinders"),  5,	FDISK_FIELDFL_NUMBER },
+	{ FDISK_FIELD_SIZE,	N_("Size"),	  5,	FDISK_FIELDFL_NUMBER },
+	{ FDISK_FIELD_TYPE,	N_("Type"),	  8,	0 },
+	{ FDISK_FIELD_FSIZE,	N_("Fsize"),	  5,	FDISK_FIELDFL_NUMBER },
+	{ FDISK_FIELD_BSIZE,	N_("Bsize"),	  5,	FDISK_FIELDFL_NUMBER },
+	{ FDISK_FIELD_CPG,	N_("Cpg"),	  5,	FDISK_FIELDFL_NUMBER }
+};
+
+/*
+ * allocates BSD label driver
+ */
+struct fdisk_label *fdisk_new_bsd_label(struct fdisk_context *cxt)
+{
+	struct fdisk_label *lb;
+	struct fdisk_bsd_label *bsd;
+
+	assert(cxt);
+
+	bsd = calloc(1, sizeof(*bsd));
+	if (!bsd)
+		return NULL;
+
+	/* initialize generic part of the driver */
+	lb = (struct fdisk_label *) bsd;
+	lb->name = "bsd";
+	lb->id = FDISK_DISKLABEL_BSD;
+	lb->op = &bsd_operations;
+	lb->parttypes = bsd_fstypes;
+	lb->nparttypes = ARRAY_SIZE(bsd_fstypes) - 1;
+
+	lb->fields = bsd_fields;
+	lb->nfields = ARRAY_SIZE(bsd_fields);
+
+	lb->flags |= FDISK_LABEL_FL_INCHARS_PARTNO;
+	lb->flags |= FDISK_LABEL_FL_REQUIRE_GEOMETRY;
+
+	return lb;
+}
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;
+}
+
diff --git a/libblkid/libfdisk/src/dos.c b/libblkid/libfdisk/src/dos.c
new file mode 100644
index 0000000..2a06707
--- /dev/null
+++ b/libblkid/libfdisk/src/dos.c
@@ -0,0 +1,2331 @@
+/*
+ *
+ * Copyright (C) 2007-2013 Karel Zak <kzak@redhat.com>
+ *                    2012 Davidlohr Bueso <dave@gnu.org>
+ *
+ * This is re-written version for libfdisk, the original was fdiskdoslabel.c
+ * from util-linux fdisk.
+ */
+#include "c.h"
+#include "nls.h"
+#include "randutils.h"
+#include "pt-mbr.h"
+#include "strutils.h"
+
+#include "fdiskP.h"
+
+#include <ctype.h>
+
+#define MAXIMUM_PARTS	60
+#define ACTIVE_FLAG     0x80
+
+/**
+ * SECTION: dos
+ * @title: DOS (MBR)
+ * @short_description: disk label specific functions
+ *
+ */
+
+
+#define IS_EXTENDED(i) \
+	((i) == MBR_DOS_EXTENDED_PARTITION \
+	 || (i) == MBR_W95_EXTENDED_PARTITION \
+	 || (i) == MBR_LINUX_EXTENDED_PARTITION)
+
+/*
+ * per partition table entry data
+ *
+ * The four primary partitions have the same sectorbuffer
+ * and have NULL ex_entry.
+ *
+ * Each logical partition table entry has two pointers, one for the
+ * partition and one link to the next one.
+ */
+struct pte {
+	struct dos_partition *pt_entry;	/* on-disk MBR entry */
+	struct dos_partition *ex_entry;	/* on-disk EBR entry */
+	fdisk_sector_t offset;	        /* disk sector number */
+	unsigned char *sectorbuffer;	/* disk sector contents */
+
+	unsigned int changed : 1,
+		     private_sectorbuffer : 1;
+};
+
+/*
+ * in-memory fdisk GPT stuff
+ */
+struct fdisk_dos_label {
+	struct fdisk_label	head;		/* generic part */
+
+	struct pte	ptes[MAXIMUM_PARTS];	/* partition */
+	fdisk_sector_t	ext_offset;		/* start of the ext.partition */
+	size_t		ext_index;		/* ext.partition index (if ext_offset is set) */
+	unsigned int	compatible : 1,		/* is DOS compatible? */
+			non_pt_changed : 1;	/* MBR, but no PT changed */
+};
+
+/*
+ * Partition types
+ */
+static struct fdisk_parttype dos_parttypes[] = {
+	#include "pt-mbr-partnames.h"
+};
+
+#define set_hsc(h,s,c,sector) { \
+		s = sector % cxt->geom.sectors + 1;			\
+		sector /= cxt->geom.sectors;				\
+		h = sector % cxt->geom.heads;				\
+		sector /= cxt->geom.heads;				\
+		c = sector & 0xff;					\
+		s |= (sector >> 2) & 0xc0;				\
+	}
+
+
+#define sector(s)	((s) & 0x3f)
+#define cylinder(s, c)	((c) | (((s) & 0xc0) << 2))
+
+#define alignment_required(_x)	((_x)->grain != (_x)->sector_size)
+
+#define is_dos_compatible(_x) \
+		   (fdisk_is_label(_x, DOS) && \
+                    fdisk_dos_is_compatible(fdisk_get_label(_x, NULL)))
+
+#define cround(c, n)	fdisk_cround(c, n)
+
+
+static inline struct fdisk_dos_label *self_label(struct fdisk_context *cxt)
+{
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, DOS));
+
+	return (struct fdisk_dos_label *) cxt->label;
+}
+
+static inline struct pte *self_pte(struct fdisk_context *cxt, size_t i)
+{
+	struct fdisk_dos_label *l = self_label(cxt);
+
+	if (i >= ARRAY_SIZE(l->ptes))
+		return NULL;
+
+	return &l->ptes[i];
+}
+
+static inline struct dos_partition *self_partition(
+				struct fdisk_context *cxt,
+				size_t i)
+{
+	struct pte *pe = self_pte(cxt, i);
+	return pe ? pe->pt_entry : NULL;
+}
+
+struct dos_partition *fdisk_dos_get_partition(
+				struct fdisk_context *cxt,
+				size_t i)
+{
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, DOS));
+
+	return self_partition(cxt, i);
+}
+
+static struct fdisk_parttype *dos_partition_parttype(
+		struct fdisk_context *cxt,
+		struct dos_partition *p)
+{
+	struct fdisk_parttype *t
+		= fdisk_label_get_parttype_from_code(cxt->label, p->sys_ind);
+	return t ? : fdisk_new_unknown_parttype(p->sys_ind, NULL);
+}
+
+/*
+ * Linux kernel cares about partition size only. Things like
+ * partition type or so are completely irrelevant -- kzak Nov-2013
+ */
+static int is_used_partition(struct dos_partition *p)
+{
+	return p && dos_partition_get_size(p) != 0;
+}
+
+static void partition_set_changed(
+				struct fdisk_context *cxt,
+				size_t i,
+				int changed)
+{
+	struct pte *pe = self_pte(cxt, i);
+
+	if (!pe)
+		return;
+
+	DBG(LABEL, ul_debug("DOS: setting %zu partition to %s", i,
+				changed ? "changed" : "unchanged"));
+
+	pe->changed = changed ? 1 : 0;
+	if (changed)
+		fdisk_label_set_changed(cxt->label, 1);
+}
+
+static fdisk_sector_t get_abs_partition_start(struct pte *pe)
+{
+	assert(pe);
+	assert(pe->pt_entry);
+
+	return pe->offset + dos_partition_get_start(pe->pt_entry);
+}
+
+static fdisk_sector_t get_abs_partition_end(struct pte *pe)
+{
+	fdisk_sector_t size;
+
+	assert(pe);
+	assert(pe->pt_entry);
+
+	size = dos_partition_get_size(pe->pt_entry);
+	return get_abs_partition_start(pe) + size - (size ? 1 : 0);
+}
+
+static int is_cleared_partition(struct dos_partition *p)
+{
+	return !(!p || p->boot_ind || p->bh || p->bs || p->bc ||
+		 p->sys_ind || p->eh || p->es || p->ec ||
+		 dos_partition_get_start(p) || dos_partition_get_size(p));
+}
+
+static int get_partition_unused_primary(struct fdisk_context *cxt,
+					struct fdisk_partition *pa,
+					size_t *partno)
+{
+	size_t org, n;
+	int rc;
+
+	assert(cxt);
+	assert(cxt->label);
+	assert(partno);
+
+	org = cxt->label->nparts_max;
+
+	cxt->label->nparts_max = 4;
+	rc = fdisk_partition_next_partno(pa, cxt, &n);
+	cxt->label->nparts_max = org;
+
+	if (rc == 1) {
+		fdisk_info(cxt, _("All primary partitions have been defined already."));
+		rc = -1;
+	} else if (rc == 0)
+		*partno = n;
+	return rc;
+}
+
+static int seek_sector(struct fdisk_context *cxt, fdisk_sector_t secno)
+{
+	off_t offset = (off_t) secno * cxt->sector_size;
+
+	return lseek(cxt->dev_fd, offset, SEEK_SET) == (off_t) -1 ? -errno : 0;
+}
+
+static int read_sector(struct fdisk_context *cxt, fdisk_sector_t secno,
+			unsigned char *buf)
+{
+	int rc = seek_sector(cxt, secno);
+	ssize_t r;
+
+	if (rc < 0)
+		return rc;
+
+	r = read(cxt->dev_fd, buf, cxt->sector_size);
+	if (r == (ssize_t) cxt->sector_size)
+		return 0;
+	if (r < 0)
+		return -errno;
+	return -1;
+}
+
+/* Allocate a buffer and read a partition table sector */
+static int read_pte(struct fdisk_context *cxt, size_t pno, fdisk_sector_t offset)
+{
+	int rc;
+	unsigned char *buf;
+	struct pte *pe = self_pte(cxt, pno);
+
+	buf = calloc(1, cxt->sector_size);
+	if (!buf)
+		return -ENOMEM;
+
+	DBG(LABEL, ul_debug("DOS: reading EBR %zu: offset=%ju, buffer=%p",
+				pno, (uintmax_t) offset, buf));
+
+	pe->offset = offset;
+	pe->sectorbuffer = buf;
+	pe->private_sectorbuffer = 1;
+
+	rc = read_sector(cxt, offset, pe->sectorbuffer);
+	if (rc) {
+		fdisk_warn(cxt, _("Failed to read extended partition table "
+				"(offset=%ju)"), (uintmax_t) offset);
+		return rc;
+	}
+
+	pe->changed = 0;
+	pe->pt_entry = pe->ex_entry = NULL;
+	return 0;
+}
+
+
+static void clear_partition(struct dos_partition *p)
+{
+	if (!p)
+		return;
+	p->boot_ind = 0;
+	p->bh = 0;
+	p->bs = 0;
+	p->bc = 0;
+	p->sys_ind = 0;
+	p->eh = 0;
+	p->es = 0;
+	p->ec = 0;
+	dos_partition_set_start(p,0);
+	dos_partition_set_size(p,0);
+}
+
+static void dos_init(struct fdisk_context *cxt)
+{
+	struct fdisk_dos_label *l = self_label(cxt);
+	size_t i;
+
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, DOS));
+
+	DBG(LABEL, ul_debug("DOS: initialize, first sector buffer %p", cxt->firstsector));
+
+	cxt->label->nparts_max = 4;	/* default, unlimited number of logical */
+
+	l->ext_index = 0;
+	l->ext_offset = 0;
+	l->non_pt_changed = 0;
+
+	memset(l->ptes, 0, sizeof(l->ptes));
+
+	for (i = 0; i < 4; i++) {
+		struct pte *pe = self_pte(cxt, i);
+
+		pe->pt_entry = mbr_get_partition(cxt->firstsector, i);
+		pe->ex_entry = NULL;
+		pe->offset = 0;
+		pe->sectorbuffer = cxt->firstsector;
+		pe->private_sectorbuffer = 0;
+		pe->changed = 0;
+	}
+
+	if (fdisk_is_listonly(cxt))
+		return;
+	/*
+	 * Various warnings...
+	 */
+	if (fdisk_missing_geometry(cxt))
+		fdisk_warnx(cxt, _("You can set geometry from the extra functions menu."));
+
+	if (is_dos_compatible(cxt)) {
+		fdisk_warnx(cxt, _("DOS-compatible mode is deprecated."));
+
+		if (cxt->sector_size != cxt->phy_sector_size)
+			fdisk_info(cxt, _(
+		"The device presents a logical sector size that is smaller than "
+		"the physical sector size. Aligning to a physical sector (or optimal "
+		"I/O) size boundary is recommended, or performance may be impacted."));
+	}
+
+	if (fdisk_use_cylinders(cxt))
+		fdisk_warnx(cxt, _("Cylinders as display units are deprecated."));
+
+	if (cxt->total_sectors > UINT_MAX) {
+		uint64_t bytes = cxt->total_sectors * cxt->sector_size;
+		char *szstr = size_to_human_string(SIZE_SUFFIX_SPACE
+					   | SIZE_SUFFIX_3LETTER, bytes);
+		fdisk_warnx(cxt,
+		_("The size of this disk is %s (%ju bytes). DOS "
+		  "partition table format can not be used on drives for "
+		  "volumes larger than %lu bytes for %lu-byte "
+		  "sectors. Use GUID partition table format (GPT)."),
+			szstr, bytes,
+			UINT_MAX * cxt->sector_size,
+			cxt->sector_size);
+		free(szstr);
+	}
+}
+
+/* callback called by libfdisk */
+static void dos_deinit(struct fdisk_label *lb)
+{
+	size_t i;
+	struct fdisk_dos_label *l = (struct fdisk_dos_label *) lb;
+
+	for (i = 0; i < ARRAY_SIZE(l->ptes); i++) {
+		struct pte *pe = &l->ptes[i];
+
+		if (pe->private_sectorbuffer && pe->sectorbuffer) {
+			DBG(LABEL, ul_debug("DOS: freeing pte %zu sector buffer %p",
+						i, pe->sectorbuffer));
+			free(pe->sectorbuffer);
+		}
+		pe->sectorbuffer = NULL;
+		pe->private_sectorbuffer = 0;
+	}
+
+	memset(l->ptes, 0, sizeof(l->ptes));
+}
+
+static void reset_pte(struct pte *pe)
+{
+	assert(pe);
+
+	if (pe->private_sectorbuffer) {
+		DBG(LABEL, ul_debug("  --> freeing pte sector buffer %p",
+					pe->sectorbuffer));
+		free(pe->sectorbuffer);
+	}
+	memset(pe, 0, sizeof(struct pte));
+}
+
+static int dos_delete_partition(struct fdisk_context *cxt, size_t partnum)
+{
+	struct fdisk_dos_label *l;
+	struct pte *pe;
+	struct dos_partition *p;
+	struct dos_partition *q;
+
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, DOS));
+
+	pe = self_pte(cxt, partnum);
+	if (!pe)
+		return -EINVAL;
+
+	DBG(LABEL, ul_debug("DOS: delete partiton %zu (max=%zu)", partnum,
+				cxt->label->nparts_max));
+
+	l = self_label(cxt);
+	p = pe->pt_entry;
+	q = pe->ex_entry;
+
+	/* Note that for the fifth partition (partnum == 4) we don't actually
+	   decrement partitions. */
+	if (partnum < 4) {
+		DBG(LABEL, ul_debug("--> delete primary"));
+		if (IS_EXTENDED(p->sys_ind) && partnum == l->ext_index) {
+			cxt->label->nparts_max = 4;
+			l->ptes[l->ext_index].ex_entry = NULL;
+			l->ext_offset = 0;
+			l->ext_index = 0;
+		}
+		partition_set_changed(cxt, partnum, 1);
+		clear_partition(p);
+	} else if (!q->sys_ind && partnum > 4) {
+		DBG(LABEL, ul_debug("--> delete logical [last in the chain]"));
+		reset_pte(&l->ptes[partnum]);
+		--cxt->label->nparts_max;
+		--partnum;
+		/* clear link to deleted partition */
+		clear_partition(l->ptes[partnum].ex_entry);
+		partition_set_changed(cxt, partnum, 1);
+	} else {
+		DBG(LABEL, ul_debug("--> delete logical [move down]"));
+		if (partnum > 4) {
+			DBG(LABEL, ul_debug(" --> delete %zu logical link", partnum));
+			p = l->ptes[partnum - 1].ex_entry;
+			*p = *q;
+			dos_partition_set_start(p, dos_partition_get_start(q));
+			dos_partition_set_size(p, dos_partition_get_size(q));
+			partition_set_changed(cxt, partnum - 1, 1);
+
+		} else if (cxt->label->nparts_max > 5) {
+			DBG(LABEL, ul_debug(" --> delete first logical link"));
+			pe = &l->ptes[5];	/* second logical */
+
+			if (pe->pt_entry)	/* prevent SEGFAULT */
+				dos_partition_set_start(pe->pt_entry,
+					       get_abs_partition_start(pe) -
+					       l->ext_offset);
+			pe->offset = l->ext_offset;
+			partition_set_changed(cxt, 5, 1);
+		}
+
+		if (cxt->label->nparts_max > 5) {
+			DBG(LABEL, ul_debug(" --> move ptes"));
+			cxt->label->nparts_max--;
+			reset_pte(&l->ptes[partnum]);
+			while (partnum < cxt->label->nparts_max) {
+				DBG(LABEL, ul_debug("  --> moving pte %zu <-- %zu", partnum, partnum + 1));
+				l->ptes[partnum] = l->ptes[partnum + 1];
+				partnum++;
+			}
+			memset(&l->ptes[partnum], 0, sizeof(struct pte));
+		} else {
+			DBG(LABEL, ul_debug(" --> the only logical: clear only"));
+			clear_partition(l->ptes[partnum].pt_entry);
+			cxt->label->nparts_max--;
+
+			if (partnum == 4) {
+				DBG(LABEL, ul_debug("  --> clear last logical"));
+				reset_pte(&l->ptes[partnum]);
+				partition_set_changed(cxt, l->ext_index, 1);
+			}
+		}
+	}
+
+	fdisk_label_set_changed(cxt->label, 1);
+	return 0;
+}
+
+static void read_extended(struct fdisk_context *cxt, size_t ext)
+{
+	size_t i;
+	struct pte *pex, *pe;
+	struct dos_partition *p, *q;
+	struct fdisk_dos_label *l = self_label(cxt);
+
+	l->ext_index = ext;
+	pex = self_pte(cxt, ext);
+	pex->ex_entry = pex->pt_entry;
+
+	p = pex->pt_entry;
+	if (!dos_partition_get_start(p)) {
+		fdisk_warnx(cxt, _("Bad offset in primary extended partition."));
+		return;
+	}
+
+	DBG(LABEL, ul_debug("DOS: Reading extended %zu", ext));
+
+	while (IS_EXTENDED (p->sys_ind)) {
+		pe = self_pte(cxt, cxt->label->nparts_max);
+
+		if (cxt->label->nparts_max >= MAXIMUM_PARTS) {
+			/* This is not a Linux restriction, but
+			   this program uses arrays of size MAXIMUM_PARTS.
+			   Do not try to `improve' this test. */
+			struct pte *pre = self_pte(cxt,
+						cxt->label->nparts_max - 1);
+			fdisk_warnx(cxt,
+			_("Omitting partitions after #%zu. They will be deleted "
+			  "if you save this partition table."),
+				cxt->label->nparts_max);
+
+			clear_partition(pre->ex_entry);
+			partition_set_changed(cxt,
+					cxt->label->nparts_max - 1, 1);
+			return;
+		}
+
+		if (read_pte(cxt, cxt->label->nparts_max, l->ext_offset +
+						dos_partition_get_start(p)))
+			return;
+
+		if (!l->ext_offset)
+			l->ext_offset = dos_partition_get_start(p);
+
+		assert(pe->sectorbuffer);
+		q = p = mbr_get_partition(pe->sectorbuffer, 0);
+
+		for (i = 0; i < 4; i++, p++) {
+			if (!dos_partition_get_size(p))
+				continue;
+
+			if (IS_EXTENDED (p->sys_ind)) {
+				if (pe->ex_entry)
+					fdisk_warnx(cxt, _(
+					"Extra link pointer in partition "
+					"table %zu."),
+						cxt->label->nparts_max + 1);
+				else
+					pe->ex_entry = p;
+			} else if (p->sys_ind) {
+				if (pe->pt_entry)
+					fdisk_warnx(cxt, _(
+					"Ignoring extra data in partition "
+					"table %zu."),
+						cxt->label->nparts_max + 1);
+				else
+					pe->pt_entry = p;
+			}
+		}
+
+		/* very strange code here... */
+		if (!pe->pt_entry) {
+			if (q != pe->ex_entry)
+				pe->pt_entry = q;
+			else
+				pe->pt_entry = q + 1;
+		}
+		if (!pe->ex_entry) {
+			if (q != pe->pt_entry)
+				pe->ex_entry = q;
+			else
+				pe->ex_entry = q + 1;
+		}
+
+		p = pe->ex_entry;
+		cxt->label->nparts_cur = ++cxt->label->nparts_max;
+
+		DBG(LABEL, ul_debug("DOS: EBR[offset=%ju]: link: type=%x,  start=%u, size=%u; "
+				                         " data: type=%x, start=%u, size=%u",
+				    (uintmax_t) pe->offset,
+				    pe->ex_entry->sys_ind,
+				    dos_partition_get_start(pe->ex_entry),
+				    dos_partition_get_size(pe->ex_entry),
+				    pe->pt_entry->sys_ind,
+				    dos_partition_get_start(pe->pt_entry),
+				    dos_partition_get_size(pe->pt_entry)));
+
+	}
+
+	/* remove last empty EBR */
+	pe = self_pte(cxt, cxt->label->nparts_max - 1);
+	if (is_cleared_partition(pe->ex_entry) &&
+	    is_cleared_partition(pe->pt_entry)) {
+		DBG(LABEL, ul_debug("DOS: EBR[offset=%ju]: empty, remove", (uintmax_t) pe->offset));
+		reset_pte(pe);
+		cxt->label->nparts_max--;
+		cxt->label->nparts_cur--;
+	}
+
+	/* remove empty links */
+ remove:
+	q = self_partition(cxt, 4);
+	for (i = 4; i < cxt->label->nparts_max; i++) {
+		p = self_partition(cxt, i);
+
+		if (!dos_partition_get_size(p) &&
+		    (cxt->label->nparts_max > 5 || q->sys_ind)) {
+			fdisk_info(cxt, _("omitting empty partition (%zu)"), i+1);
+			dos_delete_partition(cxt, i);
+			goto remove; 	/* numbering changed */
+		}
+	}
+
+	DBG(LABEL, ul_debug("DOS: nparts_max: %zu", cxt->label->nparts_max));
+}
+
+static int dos_get_disklabel_id(struct fdisk_context *cxt, char **id)
+{
+	unsigned int num;
+
+	assert(cxt);
+	assert(id);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, DOS));
+
+	num = mbr_get_id(cxt->firstsector);
+	if (asprintf(id, "0x%08x", num) > 0)
+		return 0;
+
+	return -ENOMEM;
+}
+
+static int dos_create_disklabel(struct fdisk_context *cxt)
+{
+	unsigned int id = 0;
+	int rc, has_id = 0;
+
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, DOS));
+
+	DBG(LABEL, ul_debug("DOS: creating new disklabel"));
+
+	if (cxt->script) {
+		char *end = NULL;
+		const char *s = fdisk_script_get_header(cxt->script, "label-id");
+
+		if (s) {
+			errno = 0;
+			id = strtol(s, &end, 16);
+			if (!errno && end && s < end)
+				has_id = 1;
+		}
+	}
+
+	/* random disk signature */
+	if (!has_id)
+		random_get_bytes(&id, sizeof(id));
+
+	dos_init(cxt);
+	rc = fdisk_init_firstsector_buffer(cxt);
+	if (rc)
+		return rc;
+	fdisk_label_set_changed(cxt->label, 1);
+
+	/* Generate an MBR ID for this disk */
+	mbr_set_id(cxt->firstsector, id);
+
+	/* Put MBR signature */
+	mbr_set_magic(cxt->firstsector);
+
+	fdisk_info(cxt, _("Created a new DOS disklabel with disk "
+			 "identifier 0x%08x."), id);
+	return 0;
+}
+
+static int dos_set_disklabel_id(struct fdisk_context *cxt)
+{
+	char *end = NULL, *str = NULL;
+	unsigned int id, old;
+	struct fdisk_dos_label *l;
+	int rc;
+
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, DOS));
+
+	DBG(LABEL, ul_debug("DOS: setting Id"));
+
+	l = self_label(cxt);
+	old = mbr_get_id(cxt->firstsector);
+	rc = fdisk_ask_string(cxt,
+			_("Enter the new disk identifier"), &str);
+	if (rc)
+		return rc;
+
+	errno = 0;
+	id = strtoul(str, &end, 0);
+	if (errno || str == end || (end && *end)) {
+		fdisk_warnx(cxt, _("Incorrect value."));
+		return -EINVAL;
+	}
+
+
+	mbr_set_id(cxt->firstsector, id);
+	l->non_pt_changed = 1;
+	fdisk_label_set_changed(cxt->label, 1);
+
+	fdisk_info(cxt, _("Disk identifier changed from 0x%08x to 0x%08x."),
+			old, id);
+	return 0;
+}
+
+static void get_partition_table_geometry(struct fdisk_context *cxt,
+			unsigned int *ph, unsigned int *ps)
+{
+	unsigned char *bufp = cxt->firstsector;
+	struct dos_partition *p;
+	int i, h, s, hh, ss;
+	int first = 1;
+	int bad = 0;
+
+	hh = ss = 0;
+	for (i = 0; i < 4; i++) {
+		p = mbr_get_partition(bufp, i);
+		if (p->sys_ind != 0) {
+			h = p->eh + 1;
+			s = (p->es & 077);
+			if (first) {
+				hh = h;
+				ss = s;
+				first = 0;
+			} else if (hh != h || ss != s)
+				bad = 1;
+		}
+	}
+
+	if (!first && !bad) {
+		*ph = hh;
+		*ps = ss;
+	}
+
+	DBG(LABEL, ul_debug("DOS PT geometry: heads=%u, sectors=%u", *ph, *ps));
+}
+
+static int dos_reset_alignment(struct fdisk_context *cxt)
+{
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, DOS));
+
+	/* overwrite necessary stuff by DOS deprecated stuff */
+	if (is_dos_compatible(cxt)) {
+		DBG(LABEL, ul_debug("DOS: reseting alignemnt for DOS-comaptiblem PT"));
+		if (cxt->geom.sectors)
+			cxt->first_lba = cxt->geom.sectors;	/* usually 63 */
+
+		cxt->grain = cxt->sector_size;			/* usually 512 */
+	}
+
+	return 0;
+}
+
+/* TODO: move to include/pt-dos.h and share with libblkid */
+#define AIX_MAGIC_STRING	"\xC9\xC2\xD4\xC1"
+#define AIX_MAGIC_STRLEN	(sizeof(AIX_MAGIC_STRING) - 1)
+
+static int dos_probe_label(struct fdisk_context *cxt)
+{
+	size_t i;
+	unsigned int h = 0, s = 0;
+
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, DOS));
+
+	/* ignore disks with AIX magic number */
+	if (memcmp(cxt->firstsector, AIX_MAGIC_STRING, AIX_MAGIC_STRLEN) == 0)
+		return 0;
+
+	if (!mbr_is_valid_magic(cxt->firstsector))
+		return 0;
+
+	dos_init(cxt);
+
+	get_partition_table_geometry(cxt, &h, &s);
+	if (h && s) {
+		cxt->geom.heads = h;
+	        cxt->geom.sectors = s;
+	}
+
+	for (i = 0; i < 4; i++) {
+		struct pte *pe = self_pte(cxt, i);
+
+		if (is_used_partition(pe->pt_entry))
+			cxt->label->nparts_cur++;
+
+		if (IS_EXTENDED (pe->pt_entry->sys_ind)) {
+			if (cxt->label->nparts_max != 4)
+				fdisk_warnx(cxt, _(
+				"Ignoring extra extended partition %zu"),
+					i + 1);
+			else
+				read_extended(cxt, i);
+		}
+	}
+
+	for (i = 3; i < cxt->label->nparts_max; i++) {
+		struct pte *pe = self_pte(cxt, i);
+		struct fdisk_dos_label *l = self_label(cxt);
+
+		if (!mbr_is_valid_magic(pe->sectorbuffer)) {
+			fdisk_info(cxt, _(
+			"Invalid flag 0x%02x%02x of EBR (for partition %zu) will "
+			"be corrected by w(rite)."),
+				pe->sectorbuffer[510],
+				pe->sectorbuffer[511],
+				i + 1);
+			partition_set_changed(cxt, i, 1);
+
+			/* mark also extended as changed to update the first EBR
+			 * in situation that there is no logical partitions at all */
+			partition_set_changed(cxt, l->ext_index, 1);
+		}
+	}
+
+	return 1;
+}
+
+static void set_partition(struct fdisk_context *cxt,
+			  int i, int doext, fdisk_sector_t start,
+			  fdisk_sector_t stop, int sysid, int boot)
+{
+	struct pte *pe = self_pte(cxt, i);
+	struct dos_partition *p;
+	fdisk_sector_t offset;
+
+	assert(!FDISK_IS_UNDEF(start));
+	assert(!FDISK_IS_UNDEF(stop));
+
+	if (doext) {
+		struct fdisk_dos_label *l = self_label(cxt);
+		p = pe->ex_entry;
+		offset = l->ext_offset;
+	} else {
+		p = pe->pt_entry;
+		offset = pe->offset;
+	}
+
+	DBG(LABEL, ul_debug("DOS: setting partition %d%s, offset=%zu, start=%zu, stop=%zu, sysid=%02x",
+				i, doext ? " [extended]" : "",
+				(size_t) offset,
+				(size_t) (start -  offset),
+				(size_t) (stop - start + 1),
+				sysid));
+
+	p->boot_ind = boot ? ACTIVE_FLAG : 0;
+	p->sys_ind = sysid;
+	dos_partition_set_start(p, start - offset);
+	dos_partition_set_size(p, stop - start + 1);
+
+	if (is_dos_compatible(cxt) && (start/(cxt->geom.sectors*cxt->geom.heads) > 1023))
+		start = cxt->geom.heads*cxt->geom.sectors*1024 - 1;
+	set_hsc(p->bh, p->bs, p->bc, start);
+	if (is_dos_compatible(cxt) && (stop/(cxt->geom.sectors*cxt->geom.heads) > 1023))
+		stop = cxt->geom.heads*cxt->geom.sectors*1024 - 1;
+	set_hsc(p->eh, p->es, p->ec, stop);
+	partition_set_changed(cxt, i, 1);
+}
+
+static fdisk_sector_t get_unused_start(struct fdisk_context *cxt,
+				 int part_n, fdisk_sector_t start,
+				 fdisk_sector_t first[], fdisk_sector_t last[])
+{
+	size_t i;
+
+	for (i = 0; i < cxt->label->nparts_max; i++) {
+		fdisk_sector_t lastplusoff;
+		struct pte *pe = self_pte(cxt, i);
+
+		if (start == pe->offset)
+			start += cxt->first_lba;
+		lastplusoff = last[i] + ((part_n < 4) ? 0 : cxt->first_lba);
+		if (start >= first[i] && start <= lastplusoff)
+			start = lastplusoff + 1;
+	}
+
+	return start;
+}
+
+static void fill_bounds(struct fdisk_context *cxt,
+			fdisk_sector_t *first, fdisk_sector_t *last)
+{
+	size_t i;
+	struct pte *pe = self_pte(cxt, 0);
+	struct dos_partition *p;
+
+	for (i = 0; i < cxt->label->nparts_max; pe++,i++) {
+		p = pe->pt_entry;
+		if (is_cleared_partition(p) || IS_EXTENDED (p->sys_ind)) {
+			first[i] = 0xffffffff;
+			last[i] = 0;
+		} else {
+			first[i] = get_abs_partition_start(pe);
+			last[i]  = get_abs_partition_end(pe);
+		}
+	}
+}
+
+static int get_start_from_user(	struct fdisk_context *cxt,
+				fdisk_sector_t *start,
+				fdisk_sector_t low,
+				fdisk_sector_t dflt,
+				fdisk_sector_t limit,
+				struct fdisk_partition *pa)
+{
+	assert(start);
+
+	/* try to use tepmlate from 'pa' */
+	if (pa && pa->start_follow_default)
+		*start = dflt;
+
+	else if (pa && fdisk_partition_has_start(pa)) {
+		DBG(LABEL, ul_debug("DOS: start: wanted=%ju, low=%ju, limit=%ju",
+				(uintmax_t) pa->start, (uintmax_t) low, (uintmax_t) limit));
+		*start = pa->start;
+		if (*start < low || *start > limit) {
+			fdisk_warnx(cxt, _("Start sector %ju out of range."),
+					(uintmax_t) *start);
+			return -ERANGE;
+		}
+	} else {
+		/* ask user by dialog */
+		struct fdisk_ask *ask = fdisk_new_ask();
+		int rc;
+
+		if (!ask)
+			return -ENOMEM;
+		fdisk_ask_set_query(ask,
+			fdisk_use_cylinders(cxt) ?
+				_("First cylinder") : _("First sector"));
+		fdisk_ask_set_type(ask, FDISK_ASKTYPE_NUMBER);
+		fdisk_ask_number_set_low(ask, fdisk_cround(cxt, low));
+		fdisk_ask_number_set_default(ask, fdisk_cround(cxt, dflt));
+		fdisk_ask_number_set_high(ask, fdisk_cround(cxt, limit));
+
+		rc = fdisk_do_ask(cxt, ask);
+		*start = fdisk_ask_number_get_result(ask);
+		fdisk_unref_ask(ask);
+		if (rc)
+			return rc;
+		if (fdisk_use_cylinders(cxt)) {
+		        *start = (*start - 1)
+				* fdisk_get_units_per_sector(cxt);
+			if (*start < low)
+				*start = low;
+		}
+	}
+
+	DBG(LABEL, ul_debug("DOS: start is %ju", (uintmax_t) *start));
+	return 0;
+}
+
+static fdisk_sector_t get_possible_last(struct fdisk_context *cxt, size_t n)
+{
+	fdisk_sector_t limit;
+
+	if (n >= 4) {
+		/* logical partitions */
+		struct fdisk_dos_label *l = self_label(cxt);
+		struct pte *ext_pe = l->ext_offset ? self_pte(cxt, l->ext_index) : NULL;
+
+		if (!ext_pe)
+			return 0;
+		limit = get_abs_partition_end(ext_pe);
+	} else {
+		/* primary partitions */
+		if (fdisk_use_cylinders(cxt) || !cxt->total_sectors)
+			limit = cxt->geom.heads * cxt->geom.sectors * cxt->geom.cylinders - 1;
+		else
+			limit = cxt->total_sectors - 1;
+
+		if (limit > UINT_MAX)
+			limit = UINT_MAX;
+	}
+
+	DBG(LABEL, ul_debug("DOS: last possible sector for #%zu is %ju",
+				n, (uintmax_t) limit));
+	return limit;
+}
+
+/* returns last free sector for area addressed by @start, the first[] and
+ * last[] are fill_bounds() results */
+static fdisk_sector_t get_unused_last(struct fdisk_context *cxt, size_t n,
+				fdisk_sector_t start,
+				fdisk_sector_t first[], fdisk_sector_t last[])
+{
+	size_t i;
+	fdisk_sector_t limit = get_possible_last(cxt, n);
+
+	for (i = 0; i < cxt->label->nparts_max; i++) {
+		struct pte *pe = self_pte(cxt, i);
+
+		if (start < pe->offset && limit >= pe->offset)
+			limit = pe->offset - 1;
+		if (start < first[i] && limit >= first[i])
+			limit = first[i] - 1;
+	}
+
+	DBG(LABEL, ul_debug("DOS: unused sector for #%zu is %ju",
+				n, (uintmax_t) limit));
+	return limit;
+}
+
+static int add_partition(struct fdisk_context *cxt, size_t n,
+			 struct fdisk_partition *pa)
+{
+	int sys, read = 0, rc, isrel = 0;
+	size_t i;
+	struct fdisk_dos_label *l = self_label(cxt);
+	struct dos_partition *p = self_partition(cxt, n);
+	struct pte *ext_pe = l->ext_offset ? self_pte(cxt, l->ext_index) : NULL;
+
+	fdisk_sector_t start, stop = 0, limit, temp,
+		first[cxt->label->nparts_max],
+		last[cxt->label->nparts_max];
+
+	DBG(LABEL, ul_debug("DOS: adding partition %zu", n));
+
+	sys = pa && pa->type ? pa->type->code : MBR_LINUX_DATA_PARTITION;
+
+	if (is_used_partition(p)) {
+		fdisk_warnx(cxt, _("Partition %zu is already defined.  "
+			           "Delete it before re-adding it."),
+				n + 1);
+		return -EINVAL;
+	}
+	fill_bounds(cxt, first, last);
+	limit = get_possible_last(cxt, n);
+
+	if (n < 4) {
+		if (cxt->parent && fdisk_is_label(cxt->parent, GPT))
+			start = 1;		/* Bad boy modifies hybrid MBR */
+		else {
+			if (cxt->script && pa && fdisk_partition_has_start(pa)
+			    && pa->start < cxt->first_lba
+			    && pa->start >= 1)
+				fdisk_set_first_lba(cxt, 1);
+
+			start = cxt->first_lba;
+		}
+
+		if (l->ext_offset) {
+			assert(ext_pe);
+			first[l->ext_index] = l->ext_offset;
+			last[l->ext_index] = get_abs_partition_end(ext_pe);
+		}
+	} else {
+		assert(ext_pe);
+
+		if (cxt->script && pa && fdisk_partition_has_start(pa)
+		    && pa->start >= l->ext_offset
+		    && pa->start < l->ext_offset + cxt->first_lba)
+			fdisk_set_first_lba(cxt, 1);
+
+		start = l->ext_offset + cxt->first_lba;
+	}
+
+	if (fdisk_use_cylinders(cxt))
+		for (i = 0; i < cxt->label->nparts_max; i++) {
+			first[i] = (fdisk_cround(cxt, first[i]) - 1)
+				* fdisk_get_units_per_sector(cxt);
+		}
+
+	/*
+	 * Ask for first sector
+	 */
+	do {
+		fdisk_sector_t dflt, aligned;
+
+		temp = start;
+		dflt = start = get_unused_start(cxt, n, start, first, last);
+
+		if (n >= 4 && pa && fdisk_partition_has_start(pa) && cxt->script
+		    && cxt->first_lba > 1
+		    && temp == start - cxt->first_lba) {
+			fdisk_set_first_lba(cxt, 1);
+			start = pa->start;
+		}
+
+		/* the default sector should be aligned and unused */
+		do {
+			aligned = fdisk_align_lba_in_range(cxt, dflt, dflt, limit);
+			dflt = get_unused_start(cxt, n, aligned, first, last);
+		} while (dflt != aligned && dflt > aligned && dflt < limit);
+
+		if (dflt >= limit)
+			dflt = start;
+		if (start > limit)
+			break;
+		if (start >= temp + fdisk_get_units_per_sector(cxt)
+		    && read) {
+			fdisk_info(cxt, _("Sector %llu is already allocated."),
+					temp);
+			temp = start;
+			read = 0;
+			if (pa && (fdisk_partition_has_start(pa) ||
+				   pa->start_follow_default))
+				break;
+		}
+
+		if (!read && start == temp) {
+			rc = get_start_from_user(cxt, &start, temp, dflt, limit, pa);
+			if (rc)
+				return rc;
+			read = 1;
+		}
+	} while (start != temp || !read);
+
+	if (n == 4) {
+		/* The first EBR is stored at begin of the extended partition */
+		struct pte *pe = self_pte(cxt, n);
+		pe->offset = l->ext_offset;
+
+	} else if (n > 4) {
+		/* The second (and another) EBR */
+		struct pte *pe = self_pte(cxt, n);
+
+		pe->offset = start - cxt->first_lba;
+		if (pe->offset == l->ext_offset) { /* must be corrected */
+			pe->offset++;
+			if (cxt->first_lba == 1)
+				start++;
+		}
+	}
+
+	limit = get_unused_last(cxt, n, start, first, last);
+
+	if (start > limit) {
+		fdisk_info(cxt, _("No free sectors available."));
+		if (n > 4)
+			cxt->label->nparts_max--;
+		return -ENOSPC;
+	}
+
+	/*
+	 * Ask for last sector
+	 */
+	if (fdisk_cround(cxt, start) == fdisk_cround(cxt, limit))
+		stop = limit;
+	else if (pa && pa->end_follow_default)
+		stop = limit;
+	else if (pa && fdisk_partition_has_size(pa)) {
+		stop = start + pa->size - 1;
+		isrel = pa->size_explicit ? 0 : 1;
+	} else {
+		/* ask user by dialog */
+		struct fdisk_ask *ask = fdisk_new_ask();
+
+		if (!ask)
+			return -ENOMEM;
+		fdisk_ask_set_type(ask, FDISK_ASKTYPE_OFFSET);
+
+		if (fdisk_use_cylinders(cxt)) {
+			fdisk_ask_set_query(ask, _("Last cylinder, +cylinders or +size{K,M,G,T,P}"));
+			fdisk_ask_number_set_unit(ask,
+				     cxt->sector_size *
+				     fdisk_get_units_per_sector(cxt));
+		} else {
+			fdisk_ask_set_query(ask, _("Last sector, +sectors or +size{K,M,G,T,P}"));
+			fdisk_ask_number_set_unit(ask,cxt->sector_size);
+		}
+
+		fdisk_ask_number_set_low(ask, fdisk_cround(cxt, start));
+		fdisk_ask_number_set_default(ask, fdisk_cround(cxt, limit));
+		fdisk_ask_number_set_high(ask, fdisk_cround(cxt, limit));
+		fdisk_ask_number_set_base(ask, fdisk_cround(cxt, start));	/* base for relative input */
+
+		rc = fdisk_do_ask(cxt, ask);
+		stop = fdisk_ask_number_get_result(ask);
+		isrel = fdisk_ask_number_is_relative(ask);
+		fdisk_unref_ask(ask);
+		if (rc)
+			return rc;
+		if (fdisk_use_cylinders(cxt)) {
+			stop = stop * fdisk_get_units_per_sector(cxt) - 1;
+			if (stop >limit)
+				stop = limit;
+		}
+	}
+
+	DBG(LABEL, ul_debug("DOS: raw stop: %ju", (uintmax_t) stop));
+
+	if (stop > limit)
+		stop = limit;
+
+	if (stop < limit) {
+		if (isrel && alignment_required(cxt)) {
+			/* the last sector has not been exactly requested (but
+			 * defined by +size{K,M,G} convention), so be smart and
+			 * align the end of the partition. The next partition
+			 * will start at phy.block boundary.
+			 */
+			stop = fdisk_align_lba_in_range(cxt, stop, start, limit) - 1;
+			if (stop > limit)
+				stop = limit;
+		}
+	}
+
+	set_partition(cxt, n, 0, start, stop, sys, pa && pa->boot == 1 ? 1 : 0);
+	if (n > 4) {
+		struct pte *pe = self_pte(cxt, n);
+		set_partition(cxt, n - 1, 1, pe->offset, stop,
+					MBR_DOS_EXTENDED_PARTITION, 0);
+	}
+
+	/* report */
+	{
+		struct fdisk_parttype *t =
+			fdisk_label_get_parttype_from_code(cxt->label, sys);
+		fdisk_info_new_partition(cxt, n + 1, start, stop, t);
+		fdisk_unref_parttype(t);
+	}
+
+
+	if (IS_EXTENDED(sys)) {
+		struct pte *pen = self_pte(cxt, n);
+
+		l->ext_index = n;
+		l->ext_offset = start;
+		pen->ex_entry = p;
+	}
+
+	fdisk_label_set_changed(cxt->label, 1);
+	return 0;
+}
+
+static int add_logical(struct fdisk_context *cxt,
+		       struct fdisk_partition *pa,
+		       size_t *partno)
+{
+	struct pte *pe;
+	int rc;
+
+	assert(cxt);
+	assert(partno);
+	assert(cxt->label);
+	assert(self_label(cxt)->ext_offset);
+
+	DBG(LABEL, ul_debug("DOS: nparts max: %zu", cxt->label->nparts_max));
+	pe = self_pte(cxt, cxt->label->nparts_max);
+
+	if (!pe->sectorbuffer) {
+		pe->sectorbuffer = calloc(1, cxt->sector_size);
+		if (!pe->sectorbuffer)
+			return -ENOMEM;
+		DBG(LABEL, ul_debug("DOS: logical: %zu: new EBR sector buffer %p",
+					cxt->label->nparts_max, pe->sectorbuffer));
+		pe->private_sectorbuffer = 1;
+	}
+	pe->pt_entry = mbr_get_partition(pe->sectorbuffer, 0);
+	pe->ex_entry = pe->pt_entry + 1;
+	pe->offset = 0;
+	partition_set_changed(cxt, cxt->label->nparts_max, 1);
+
+	cxt->label->nparts_max++;
+
+	/* this message makes sense only when we use extended/primary/logical
+	 * dialog. The dialog is disable for scripts, see dos_add_partition() */
+	if (!cxt->script)
+		fdisk_info(cxt, _("Adding logical partition %zu"),
+				cxt->label->nparts_max);
+	*partno = cxt->label->nparts_max - 1;
+	rc = add_partition(cxt, *partno, pa);
+
+	if (rc) {
+		/* reset on error */
+		cxt->label->nparts_max--;
+		pe->pt_entry = NULL;
+		pe->ex_entry = NULL;
+		pe->offset = 0;
+		pe->changed = 0;
+	}
+
+	return rc;
+}
+
+static void check(struct fdisk_context *cxt, size_t n,
+	   unsigned int h, unsigned int s, unsigned int c,
+	   unsigned int start)
+{
+	unsigned int total, real_s, real_c;
+
+	if (!is_dos_compatible(cxt))
+		return;
+
+	real_s = sector(s) - 1;
+	real_c = cylinder(s, c);
+	total = (real_c * cxt->geom.heads + h) * cxt->geom.sectors + real_s;
+
+	if (!total)
+		fdisk_warnx(cxt, _("Partition %zu: contains sector 0"), n);
+	if (h >= cxt->geom.heads)
+		fdisk_warnx(cxt, _("Partition %zu: head %d greater than "
+				   "maximum %d"), n, h + 1, cxt->geom.heads);
+	if (real_s >= cxt->geom.sectors)
+		fdisk_warnx(cxt, _("Partition %zu: sector %d greater than "
+				   "maximum %llu"), n, s, cxt->geom.sectors);
+	if (real_c >= cxt->geom.cylinders)
+		fdisk_warnx(cxt, _("Partition %zu: cylinder %d greater than "
+				   "maximum %llu"),
+				n, real_c + 1,
+				cxt->geom.cylinders);
+
+	if (cxt->geom.cylinders <= 1024 && start != total)
+		fdisk_warnx(cxt, _("Partition %zu: previous sectors %u "
+				   "disagrees with total %u"), n, start, total);
+}
+
+/* check_consistency() and long2chs() added Sat Mar 6 12:28:16 1993,
+ * faith@cs.unc.edu, based on code fragments from pfdisk by Gordon W. Ross,
+ * Jan.  1990 (version 1.2.1 by Gordon W. Ross Aug. 1990; Modified by S.
+ * Lubkin Oct.  1991). */
+
+static void
+long2chs(struct fdisk_context *cxt, unsigned long ls,
+	 unsigned int *c, unsigned int *h, unsigned int *s) {
+	int spc = cxt->geom.heads * cxt->geom.sectors;
+
+	*c = ls / spc;
+	ls = ls % spc;
+	*h = ls / cxt->geom.sectors;
+	*s = ls % cxt->geom.sectors + 1;	/* sectors count from 1 */
+}
+
+static void check_consistency(struct fdisk_context *cxt, struct dos_partition *p,
+			      size_t partition)
+{
+	unsigned int pbc, pbh, pbs;	/* physical beginning c, h, s */
+	unsigned int pec, peh, pes;	/* physical ending c, h, s */
+	unsigned int lbc, lbh, lbs;	/* logical beginning c, h, s */
+	unsigned int lec, leh, les;	/* logical ending c, h, s */
+
+	if (!is_dos_compatible(cxt))
+		return;
+
+	if (!cxt->geom.heads || !cxt->geom.sectors || (partition >= 4))
+		return;		/* do not check extended partitions */
+
+	/* physical beginning c, h, s */
+	pbc = (p->bc & 0xff) | ((p->bs << 2) & 0x300);
+	pbh = p->bh;
+	pbs = p->bs & 0x3f;
+
+	/* physical ending c, h, s */
+	pec = (p->ec & 0xff) | ((p->es << 2) & 0x300);
+	peh = p->eh;
+	pes = p->es & 0x3f;
+
+	/* compute logical beginning (c, h, s) */
+	long2chs(cxt, dos_partition_get_start(p), &lbc, &lbh, &lbs);
+
+	/* compute logical ending (c, h, s) */
+	long2chs(cxt, dos_partition_get_start(p) + dos_partition_get_size(p) - 1, &lec, &leh, &les);
+
+	/* Same physical / logical beginning? */
+	if (cxt->geom.cylinders <= 1024
+	    && (pbc != lbc || pbh != lbh || pbs != lbs)) {
+		fdisk_warnx(cxt, _("Partition %zu: different physical/logical "
+			"beginnings (non-Linux?): "
+			"phys=(%d, %d, %d), logical=(%d, %d, %d)"),
+			partition + 1,
+			pbc, pbh, pbs,
+			lbc, lbh, lbs);
+	}
+
+	/* Same physical / logical ending? */
+	if (cxt->geom.cylinders <= 1024
+	    && (pec != lec || peh != leh || pes != les)) {
+		fdisk_warnx(cxt, _("Partition %zu: different physical/logical "
+			"endings: phys=(%d, %d, %d), logical=(%d, %d, %d)"),
+			partition + 1,
+			pec, peh, pes,
+			lec, leh, les);
+	}
+
+	/* Ending on cylinder boundary? */
+	if (peh != (cxt->geom.heads - 1) || pes != cxt->geom.sectors) {
+		fdisk_warnx(cxt, _("Partition %zu: does not end on "
+				   "cylinder boundary."),
+			partition + 1);
+	}
+}
+
+static int dos_verify_disklabel(struct fdisk_context *cxt)
+{
+	size_t i, j;
+	fdisk_sector_t total = 1, n_sectors = cxt->total_sectors;
+	fdisk_sector_t first[cxt->label->nparts_max],
+		       last[cxt->label->nparts_max];
+	struct dos_partition *p;
+	struct fdisk_dos_label *l = self_label(cxt);
+
+	assert(fdisk_is_label(cxt, DOS));
+
+	fill_bounds(cxt, first, last);
+	for (i = 0; i < cxt->label->nparts_max; i++) {
+		struct pte *pe = self_pte(cxt, i);
+
+		p = self_partition(cxt, i);
+		if (is_used_partition(p) && !IS_EXTENDED(p->sys_ind)) {
+			check_consistency(cxt, p, i);
+			if (get_abs_partition_start(pe) < first[i])
+				fdisk_warnx(cxt, _(
+					"Partition %zu: bad start-of-data."),
+					 i + 1);
+
+			check(cxt, i + 1, p->eh, p->es, p->ec, last[i]);
+			total += last[i] + 1 - first[i];
+
+			if (i == 0)
+				total += get_abs_partition_start(pe) - 1;
+
+			for (j = 0; j < i; j++) {
+				if ((first[i] >= first[j] && first[i] <= last[j])
+				    || ((last[i] <= last[j] && last[i] >= first[j]))) {
+
+					fdisk_warnx(cxt, _("Partition %zu: "
+						"overlaps partition %zu."),
+						j + 1, i + 1);
+
+					total += first[i] >= first[j] ?
+						first[i] : first[j];
+					total -= last[i] <= last[j] ?
+						last[i] : last[j];
+				}
+			}
+		}
+	}
+
+	if (l->ext_offset) {
+		fdisk_sector_t e_last;
+		struct pte *ext_pe = self_pte(cxt, l->ext_index);
+
+		e_last = get_abs_partition_end(ext_pe);
+
+		for (i = 4; i < cxt->label->nparts_max; i++) {
+			total++;
+			p = self_partition(cxt, i);
+
+			if (!p->sys_ind) {
+				if (i != 4 || i + 1 < cxt->label->nparts_max)
+					fdisk_warnx(cxt,
+						_("Partition %zu: empty."),
+						i + 1);
+			} else if (first[i] < l->ext_offset
+				   || last[i] > e_last) {
+
+				fdisk_warnx(cxt, _("Logical partition %zu: "
+					"not entirely in partition %zu."),
+					i + 1, l->ext_index + 1);
+			}
+		}
+	}
+
+	if (total > n_sectors)
+		fdisk_warnx(cxt, _("Total allocated sectors %llu greater "
+			"than the maximum %llu."), total, n_sectors);
+	else if (total < n_sectors)
+		fdisk_warnx(cxt, _("Remaining %lld unallocated %ld-byte "
+			"sectors."), n_sectors - total, cxt->sector_size);
+
+	return 0;
+}
+
+/*
+ * Ask the user for new partition type information (logical, extended).
+ * This function calls the actual partition adding logic - add_partition.
+ *
+ * API callback.
+ */
+static int dos_add_partition(struct fdisk_context *cxt,
+			     struct fdisk_partition *pa,
+			     size_t *partno)
+{
+	size_t i, free_primary = 0, free_sectors = 0;
+	fdisk_sector_t last = 0, grain;
+	int rc = 0;
+	struct fdisk_dos_label *l;
+	struct pte *ext_pe;
+	size_t res;		/* partno */
+
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, DOS));
+
+	DBG(LABEL, ul_debug("DOS: new partition wanted"));
+
+	l = self_label(cxt);
+	ext_pe = l->ext_offset ? self_pte(cxt, l->ext_index) : NULL;
+
+	/*
+	 * partition template (@pa) based partitioning
+	 */
+
+	/* pa specifies start within extended partition, add logical */
+	if (pa && fdisk_partition_has_start(pa) && ext_pe
+	    && pa->start >= l->ext_offset
+	    && pa->start <= get_abs_partition_end(ext_pe)) {
+		DBG(LABEL, ul_debug("DOS: pa template %p: add logical", pa));
+		rc = add_logical(cxt, pa, &res);
+		goto done;
+
+	/* pa specifies that extended partition is wanted */
+	} else if (pa && pa->type && pa->type->code == MBR_DOS_EXTENDED_PARTITION) {
+		DBG(LABEL, ul_debug("DOS: pa template %p: add extened", pa));
+		if (l->ext_offset) {
+			fdisk_warnx(cxt, _("Extended partition already exists."));
+			return -EINVAL;
+		}
+		rc = get_partition_unused_primary(cxt, pa, &res);
+		if (rc == 0) {
+			rc = add_partition(cxt, res, pa);
+			goto done;
+		}
+
+	/* pa specifies start, but outside extended partition */
+	} else if (pa && fdisk_partition_has_start(pa) && l->ext_offset) {
+		DBG(LABEL, ul_debug("DOS: pa template %p: add primary", pa));
+		rc = get_partition_unused_primary(cxt, pa, &res);
+		if (rc == 0) {
+			rc = add_partition(cxt, res, pa);
+			goto done;
+		}
+	}
+
+	/*
+	 * dialog driven partitioning (it does not mean that @pa template is
+	 * completely ignored!)
+	 */
+
+	/* check if there is space for primary partition */
+	grain = cxt->grain > cxt->sector_size ? cxt->grain / cxt->sector_size : 1;
+	last = cxt->first_lba;
+
+	for (i = 0; i < 4; i++) {
+		struct dos_partition *p = self_partition(cxt, i);
+
+		if (is_used_partition(p)) {
+			fdisk_sector_t start = dos_partition_get_start(p);
+			if (last + grain <= start)
+				free_sectors = 1;
+			last = start + dos_partition_get_size(p);
+		} else
+			free_primary++;
+	}
+	if (last + grain < cxt->total_sectors - 1)
+		free_sectors = 1;
+
+	if (!free_primary && cxt->label->nparts_max >= MAXIMUM_PARTS) {
+		fdisk_info(cxt, _("The maximum number of partitions has "
+				  "been created."));
+		return -EINVAL;
+	}
+	rc = 1;
+
+	if (!free_primary || !free_sectors) {
+		DBG(LABEL, ul_debug("DOS: primary impossible, add logical"));
+		if (l->ext_offset) {
+			if (!pa || fdisk_partition_has_start(pa)) {
+				if (!free_primary)
+					fdisk_info(cxt, _("All primary partitions are in use."));
+				else if (!free_sectors)
+					fdisk_info(cxt, _("All space for primary partitions is in use."));
+			}
+			rc = add_logical(cxt, pa, &res);
+		} else {
+			fdisk_info(cxt,
+			     _(	"Impossible to create another primary partition. "
+				"If you want to create more partitions, you must "
+				"replace a primary partition with an extended "
+				"partition first."));
+			return -EINVAL;
+		}
+	} else if (cxt->label->nparts_max >= MAXIMUM_PARTS) {
+		fdisk_info(cxt, _("All logical partitions are in use. "
+				  "Adding a primary partition."));
+		rc = get_partition_unused_primary(cxt, pa, &res);
+		if (rc == 0)
+			rc = add_partition(cxt, res, pa);
+	} else {
+		char hint[BUFSIZ];
+		struct fdisk_ask *ask;
+		int c;
+
+		/* the default layout for scripts is to create primary partitions */
+		if (cxt->script) {
+			rc = get_partition_unused_primary(cxt, pa, &res);
+			if (rc == 0)
+				rc = add_partition(cxt, res, pa);
+			goto done;
+		}
+
+		ask = fdisk_new_ask();
+		if (!ask)
+			return -ENOMEM;
+		fdisk_ask_set_type(ask, FDISK_ASKTYPE_MENU);
+		fdisk_ask_set_query(ask, _("Partition type"));
+		fdisk_ask_menu_set_default(ask, free_primary == 1
+						&& !l->ext_offset ? 'e' : 'p');
+		snprintf(hint, sizeof(hint),
+				_("%zu primary, %d extended, %zu free"),
+				4 - (l->ext_offset ? 1 : 0) - free_primary,
+				l->ext_offset ? 1 : 0,
+				free_primary);
+
+		fdisk_ask_menu_add_item(ask, 'p', _("primary"), hint);
+		if (!l->ext_offset)
+			fdisk_ask_menu_add_item(ask, 'e', _("extended"), _("container for logical partitions"));
+		else
+			fdisk_ask_menu_add_item(ask, 'l', _("logical"), _("numbered from 5"));
+
+		rc = fdisk_do_ask(cxt, ask);
+		if (rc)
+			return rc;
+		fdisk_ask_menu_get_result(ask, &c);
+		fdisk_unref_ask(ask);
+
+		if (c == 'p') {
+			rc = get_partition_unused_primary(cxt, pa, &res);
+			if (rc == 0)
+				rc = add_partition(cxt, res, pa);
+			goto done;
+		} else if (c == 'l' && l->ext_offset) {
+			rc = add_logical(cxt, pa, &res);
+			goto done;
+		} else if (c == 'e' && !l->ext_offset) {
+			rc = get_partition_unused_primary(cxt, pa, &res);
+			if (rc == 0) {
+				struct fdisk_partition *xpa = NULL;
+				struct fdisk_parttype *t;
+
+				t = fdisk_label_get_parttype_from_code(cxt->label,
+						MBR_DOS_EXTENDED_PARTITION);
+				if (!pa) {
+					pa = xpa = fdisk_new_partition();
+					if (!xpa)
+						return -ENOMEM;
+				}
+				fdisk_partition_set_type(pa, t);
+				rc = add_partition(cxt, res, pa);
+				if (xpa) {
+					fdisk_unref_partition(xpa);
+					pa = NULL;
+				}
+			}
+			goto done;
+		} else
+			fdisk_warnx(cxt, _("Invalid partition type `%c'."), c);
+	}
+done:
+	if (rc == 0) {
+		cxt->label->nparts_cur++;
+		if (partno)
+			*partno = res;
+	}
+	return rc;
+}
+
+static int write_sector(struct fdisk_context *cxt, fdisk_sector_t secno,
+			       unsigned char *buf)
+{
+	int rc;
+
+	rc = seek_sector(cxt, secno);
+	if (rc != 0) {
+		fdisk_warn(cxt, _("Cannot write sector %jd: seek failed"),
+				(uintmax_t) secno);
+		return rc;
+	}
+
+	DBG(LABEL, ul_debug("DOS: writting to sector %ju", (uintmax_t) secno));
+
+	if (write(cxt->dev_fd, buf, cxt->sector_size) != (ssize_t) cxt->sector_size)
+		return -errno;
+	return 0;
+}
+
+static int dos_write_disklabel(struct fdisk_context *cxt)
+{
+	struct fdisk_dos_label *l = self_label(cxt);
+	size_t i;
+	int rc = 0, mbr_changed = 0;
+
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, DOS));
+
+	mbr_changed = l->non_pt_changed;
+
+	/* MBR (primary partitions) */
+	if (!mbr_changed) {
+		for (i = 0; i < 4; i++) {
+			struct pte *pe = self_pte(cxt, i);
+			if (pe->changed)
+				mbr_changed = 1;
+		}
+	}
+	if (mbr_changed) {
+		mbr_set_magic(cxt->firstsector);
+		rc = write_sector(cxt, 0, cxt->firstsector);
+		if (rc)
+			goto done;
+	}
+
+	if (cxt->label->nparts_max <= 4 && l->ext_offset) {
+		/* we have empty extended partition, check if the partition has
+		 * been modified and then cleanup possible remaining EBR  */
+		struct pte *pe = self_pte(cxt, l->ext_index);
+		unsigned char empty[512] = { 0 };
+		fdisk_sector_t off = pe ? get_abs_partition_start(pe) : 0;
+
+		if (off && pe->changed) {
+			mbr_set_magic(empty);
+			write_sector(cxt, off, empty);
+		}
+	}
+
+	/* EBR (logical partitions) */
+	for (i = 4; i < cxt->label->nparts_max; i++) {
+		struct pte *pe = self_pte(cxt, i);
+
+		if (!pe->changed || !pe->offset || !pe->sectorbuffer)
+			continue;
+
+		mbr_set_magic(pe->sectorbuffer);
+		rc = write_sector(cxt, pe->offset, pe->sectorbuffer);
+		if (rc)
+			goto done;
+	}
+
+done:
+	return rc;
+}
+
+static int dos_locate_disklabel(struct fdisk_context *cxt, int n,
+		const char **name, off_t *offset, size_t *size)
+{
+	assert(cxt);
+
+	*name = NULL;
+	*offset = 0;
+	*size = 0;
+
+	switch (n) {
+	case 0:
+		*name = "MBR";
+		*offset = 0;
+		*size = 512;
+		break;
+	default:
+		/* extended partitions */
+		if (n - 1 + 4 < cxt->label->nparts_max) {
+			struct pte *pe = self_pte(cxt, n - 1 + 4);
+
+			assert(pe->private_sectorbuffer);
+
+			*name = "EBR";
+			*offset = pe->offset * cxt->sector_size;
+			*size = 512;
+		} else
+			return 1;
+		break;
+	}
+
+	return 0;
+}
+
+/*
+ * Check whether partition entries are ordered by their starting positions.
+ * Return 0 if OK. Return i if partition i should have been earlier.
+ * Two separate checks: primary and logical partitions.
+ */
+static int wrong_p_order(struct fdisk_context *cxt, size_t *prev)
+{
+	size_t last_p_start_pos = 0, p_start_pos;
+	size_t i, last_i = 0;
+
+	for (i = 0 ; i < cxt->label->nparts_max; i++) {
+
+		struct pte *pe = self_pte(cxt, i);
+		struct dos_partition *p = pe->pt_entry;
+
+		if (i == 4) {
+			last_i = 4;
+			last_p_start_pos = 0;
+		}
+		if (is_used_partition(p)) {
+			p_start_pos = get_abs_partition_start(pe);
+
+			if (last_p_start_pos > p_start_pos) {
+				if (prev)
+					*prev = last_i;
+				return i;
+			}
+
+			last_p_start_pos = p_start_pos;
+			last_i = i;
+		}
+	}
+	return 0;
+}
+
+static int dos_list_disklabel(struct fdisk_context *cxt)
+{
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, DOS));
+
+	return 0;
+}
+
+static int dos_get_partition(struct fdisk_context *cxt, size_t n,
+			     struct fdisk_partition *pa)
+{
+	struct dos_partition *p;
+	struct pte *pe;
+	struct fdisk_dos_label *lb;
+
+	assert(cxt);
+	assert(pa);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, DOS));
+
+	lb = self_label(cxt);
+	pe = self_pte(cxt, n);
+	p = pe->pt_entry;
+	pa->used = !is_cleared_partition(p);
+	if (!pa->used)
+		return 0;
+
+	pa->type = dos_partition_parttype(cxt, p);
+	pa->boot = p->boot_ind == ACTIVE_FLAG ? 1 : 0;
+	pa->start = get_abs_partition_start(pe);
+	pa->size = dos_partition_get_size(p);
+	pa->container = lb->ext_offset && n == lb->ext_index;
+
+	if (n >= 4)
+		pa->parent_partno = lb->ext_index;
+
+	if (p->boot_ind && asprintf(&pa->attrs, "%02x", p->boot_ind) < 0)
+		return -ENOMEM;
+
+	/* start C/H/S */
+	if (asprintf(&pa->start_chs, "%d/%d/%d",
+				cylinder(p->bs, p->bc),
+				sector(p->bs),
+				p->bh) < 0)
+		return -ENOMEM;
+
+	/* end C/H/S */
+	if (asprintf(&pa->end_chs, "%d/%d/%d",
+				cylinder(p->es, p->ec),
+				sector(p->es),
+				p->eh) < 0)
+		return -ENOMEM;
+
+	return 0;
+}
+
+static int dos_set_partition(struct fdisk_context *cxt, size_t n,
+			     struct fdisk_partition *pa)
+{
+	struct fdisk_dos_label *l;
+	struct dos_partition *p;
+	struct pte *pe;
+	fdisk_sector_t start, size;
+
+	assert(cxt);
+	assert(pa);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, DOS));
+
+	if (n >= cxt->label->nparts_max)
+		return -EINVAL;
+
+	if (pa->type && IS_EXTENDED(pa->type->code)) {
+		fdisk_warnx(cxt, _("You cannot change a partition into an "
+			"extended one or vice versa. Delete it first."));
+		return -EINVAL;
+	}
+
+	if (pa->type && !pa->type->code)
+		fdisk_warnx(cxt, _("Type 0 means free space to many systems. "
+				   "Having partitions of type 0 is probably unwise."));
+	l = self_label(cxt);
+	p = self_partition(cxt, n);
+	pe = self_pte(cxt, n);
+
+	FDISK_INIT_UNDEF(start);
+	FDISK_INIT_UNDEF(size);
+
+	if (fdisk_partition_has_start(pa))
+		start = pa->start;
+	if (fdisk_partition_has_size(pa))
+		size = pa->size;
+
+	if (pa->end_follow_default) {
+		fdisk_sector_t first[cxt->label->nparts_max],
+			 last[cxt->label->nparts_max],
+			 xlast;
+		struct pte *ext = l->ext_offset ? self_pte(cxt, l->ext_index) : NULL; 
+
+		fill_bounds(cxt, first, last);
+
+		if (ext && l->ext_offset) {
+			first[l->ext_index] = l->ext_offset;
+			last[l->ext_index] = get_abs_partition_end(ext);
+		}
+		if (FDISK_IS_UNDEF(start))
+			start = get_abs_partition_start(pe);
+
+		DBG(LABEL, ul_debug("DOS: #%zu    now %ju +%ju sectors",
+			n, (uintmax_t) start, (uintmax_t) dos_partition_get_size(p)));
+
+		xlast = get_unused_last(cxt, n, start, first, last);
+		size = xlast ? xlast - start + 1: dos_partition_get_size(p);
+
+		DBG(LABEL, ul_debug("DOS: #%zu wanted %ju +%ju sectors",
+			n, (uintmax_t) start, (uintmax_t) size));
+	}
+
+	if (!FDISK_IS_UNDEF(start) || !FDISK_IS_UNDEF(size)) {
+		DBG(LABEL, ul_debug("DOS: resize partition"));
+
+		if (FDISK_IS_UNDEF(start))
+			start = get_abs_partition_start(pe);
+		if (FDISK_IS_UNDEF(size))
+			size = dos_partition_get_size(p);
+
+		set_partition(cxt, n, 0, start, start + size - 1,
+				pa->type  ? pa->type->code : p->sys_ind,
+				pa->boot == 1);
+	} else {
+		DBG(LABEL, ul_debug("DOS: keep size, modify properties"));
+		if (pa->type)
+			p->sys_ind = pa->type->code;
+		if (!FDISK_IS_UNDEF(pa->boot))
+			p->boot_ind = pa->boot == 1 ? ACTIVE_FLAG : 0;
+	}
+
+	partition_set_changed(cxt, n, 1);
+	return 0;
+}
+
+static void print_chain_of_logicals(struct fdisk_context *cxt)
+{
+	size_t i;
+	struct fdisk_dos_label *l = self_label(cxt);
+
+	fputc('\n', stdout);
+
+	for (i = 4; i < cxt->label->nparts_max; i++) {
+		struct pte *pe = self_pte(cxt, i);
+
+		fprintf(stderr, "#%02zu EBR [%10ju], "
+			"data[start=%10ju (%10ju), size=%10ju], "
+			"link[start=%10ju (%10ju), size=%10ju]\n",
+			i, (uintmax_t) pe->offset,
+			/* data */
+			(uintmax_t) dos_partition_get_start(pe->pt_entry),
+			(uintmax_t) get_abs_partition_start(pe),
+			(uintmax_t) dos_partition_get_size(pe->pt_entry),
+			/* link */
+			(uintmax_t) dos_partition_get_start(pe->ex_entry),
+			(uintmax_t) l->ext_offset + dos_partition_get_start(pe->ex_entry),
+			(uintmax_t) dos_partition_get_size(pe->ex_entry));
+	}
+}
+
+static int cmp_ebr_offsets(const void *a, const void *b)
+{
+	struct pte *ae = (struct pte *) a,
+		   *be = (struct pte *) b;
+
+	if (ae->offset == 0 && be->offset == 0)
+		return 0;
+	if (ae->offset == 0)
+		return 1;
+	if (be->offset == 0)
+		return -1;
+
+	return cmp_numbers(ae->offset, be->offset);
+}
+
+/*
+ * Fix the chain of logicals.
+ *
+ * The function does not modify data partitions within EBR tables
+ * (pte->pt_entry). It sorts the chain by EBR offsets and then update links
+ * (pte->ex_entry) between EBR tables.
+ *
+ */
+static void fix_chain_of_logicals(struct fdisk_context *cxt)
+{
+	struct fdisk_dos_label *l = self_label(cxt);
+	struct pte *last;
+	size_t i;
+
+	DBG(LABEL, print_chain_of_logicals(cxt));
+
+	/* Sort chain by EBR offsets */
+	qsort(&l->ptes[4], cxt->label->nparts_max - 4, sizeof(struct pte),
+			cmp_ebr_offsets);
+
+again:
+	/* Sort data partitions by start */
+	for (i = 4; i < cxt->label->nparts_max - 1; i++) {
+		struct pte *cur = self_pte(cxt, i),
+			   *nxt = self_pte(cxt, i + 1);
+
+		if (get_abs_partition_start(cur) >
+		    get_abs_partition_start(nxt)) {
+
+			struct dos_partition tmp = *cur->pt_entry;
+			fdisk_sector_t cur_start = get_abs_partition_start(cur),
+				 nxt_start = get_abs_partition_start(nxt);
+
+			/* swap data partitions */
+			*cur->pt_entry = *nxt->pt_entry;
+			*nxt->pt_entry = tmp;
+
+			/* Recount starts according to EBR offsets, the absolute
+			 * address tas to be still the same! */
+			dos_partition_set_start(cur->pt_entry, nxt_start - cur->offset);
+			dos_partition_set_start(nxt->pt_entry, cur_start - nxt->offset);
+
+			partition_set_changed(cxt, i, 1);
+			partition_set_changed(cxt, i + 1, 1);
+			goto again;
+		}
+	}
+
+	/* Update EBR links */
+	for (i = 4; i < cxt->label->nparts_max - 1; i++) {
+		struct pte *cur = self_pte(cxt, i),
+			   *nxt = self_pte(cxt, i + 1);
+
+		fdisk_sector_t noff = nxt->offset - l->ext_offset,
+			 ooff = dos_partition_get_start(cur->ex_entry);
+
+		if (noff == ooff)
+			continue;
+
+		DBG(LABEL, ul_debug("DOS: fix EBR [%10ju] link %ju -> %ju",
+			(uintmax_t) cur->offset,
+			(uintmax_t) ooff, (uintmax_t) noff));
+
+		set_partition(cxt, i, 1, nxt->offset,
+				get_abs_partition_end(nxt),
+				MBR_DOS_EXTENDED_PARTITION, 0);
+	}
+
+	/* always terminate the chain ! */
+	last = self_pte(cxt, cxt->label->nparts_max - 1);
+	if (last) {
+		clear_partition(last->ex_entry);
+		partition_set_changed(cxt, cxt->label->nparts_max - 1, 1);
+	}
+
+	DBG(LABEL, print_chain_of_logicals(cxt));
+}
+
+static int dos_reorder(struct fdisk_context *cxt)
+{
+	struct pte *pei, *pek;
+	size_t i,k;
+
+	if (!wrong_p_order(cxt, NULL)) {
+		fdisk_info(cxt, _("Nothing to do. Ordering is correct already."));
+		return 0;
+	}
+
+	while ((i = wrong_p_order(cxt, &k)) != 0 && i < 4) {
+		/* partition i should have come earlier, move it */
+		/* We have to move data in the MBR */
+		struct dos_partition *pi, *pk, *pe, pbuf;
+		pei = self_pte(cxt, i);
+		pek = self_pte(cxt, k);
+
+		pe = pei->ex_entry;
+		pei->ex_entry = pek->ex_entry;
+		pek->ex_entry = pe;
+
+		pi = pei->pt_entry;
+		pk = pek->pt_entry;
+
+		memmove(&pbuf, pi, sizeof(struct dos_partition));
+		memmove(pi, pk, sizeof(struct dos_partition));
+		memmove(pk, &pbuf, sizeof(struct dos_partition));
+
+		partition_set_changed(cxt, i, 1);
+		partition_set_changed(cxt, k, 1);
+	}
+
+	if (i)
+		fix_chain_of_logicals(cxt);
+
+	fdisk_info(cxt, _("Done."));
+	return 0;
+}
+
+/* TODO: use fdisk_set_partition() API */
+int fdisk_dos_move_begin(struct fdisk_context *cxt, size_t i)
+{
+	struct pte *pe;
+	struct dos_partition *p;
+	unsigned int new, free_start, curr_start, last;
+	uintmax_t res = 0;
+	size_t x;
+	int rc;
+
+	assert(cxt);
+	assert(fdisk_is_label(cxt, DOS));
+
+	pe = self_pte(cxt, i);
+	p = pe->pt_entry;
+
+	if (!is_used_partition(p) || IS_EXTENDED (p->sys_ind)) {
+		fdisk_warnx(cxt, _("Partition %zu: no data area."), i + 1);
+		return 0;
+	}
+
+	/* the default start is at the second sector of the disk or at the
+	 * second sector of the extended partition
+	 */
+	free_start = pe->offset ? pe->offset + 1 : 1;
+
+	curr_start = get_abs_partition_start(pe);
+
+	/* look for a free space before the current start of the partition */
+	for (x = 0; x < cxt->label->nparts_max; x++) {
+		unsigned int end;
+		struct pte *prev_pe = self_pte(cxt, x);
+		struct dos_partition *prev_p = prev_pe->pt_entry;
+
+		if (!prev_p)
+			continue;
+		end = get_abs_partition_start(prev_pe)
+		      + dos_partition_get_size(prev_p);
+
+		if (is_used_partition(prev_p) &&
+		    end > free_start && end <= curr_start)
+			free_start = end;
+	}
+
+	last = get_abs_partition_end(pe);
+
+	rc = fdisk_ask_number(cxt, free_start, curr_start, last,
+			_("New beginning of data"), &res);
+	if (rc)
+		return rc;
+
+	new = res - pe->offset;
+
+	if (new != dos_partition_get_size(p)) {
+		unsigned int sects = dos_partition_get_size(p)
+				+ dos_partition_get_start(p) - new;
+
+		dos_partition_set_size(p, sects);
+		dos_partition_set_start(p, new);
+
+		partition_set_changed(cxt, i, 1);
+	}
+
+	return rc;
+}
+
+static int dos_partition_is_used(
+		struct fdisk_context *cxt,
+		size_t i)
+{
+	struct dos_partition *p;
+
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, DOS));
+
+	if (i >= cxt->label->nparts_max)
+		return 0;
+
+	p = self_partition(cxt, i);
+
+	return p && !is_cleared_partition(p);
+}
+
+static int dos_toggle_partition_flag(
+		struct fdisk_context *cxt,
+		size_t i,
+		unsigned long flag)
+{
+	struct dos_partition *p;
+
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, DOS));
+
+	if (i >= cxt->label->nparts_max)
+		return -EINVAL;
+
+	p = self_partition(cxt, i);
+
+	switch (flag) {
+	case DOS_FLAG_ACTIVE:
+		if (IS_EXTENDED(p->sys_ind) && !p->boot_ind)
+			fdisk_warnx(cxt, _("Partition %zu: is an extended "
+					"partition."), i + 1);
+
+		p->boot_ind = (p->boot_ind ? 0 : ACTIVE_FLAG);
+		partition_set_changed(cxt, i, 1);
+		fdisk_info(cxt,	p->boot_ind ?
+			_("The bootable flag on partition %zu is enabled now.") :
+			_("The bootable flag on partition %zu is disabled now."),
+			i + 1);
+		break;
+	default:
+		return 1;
+	}
+
+	return 0;
+}
+
+static const struct fdisk_field dos_fields[] =
+{
+	/* basic */
+	{ FDISK_FIELD_DEVICE,	N_("Device"),	 10,	0 },
+	{ FDISK_FIELD_BOOT,	N_("Boot"),	  1,	0 },
+	{ FDISK_FIELD_START,	N_("Start"),	  5,	FDISK_FIELDFL_NUMBER },
+	{ FDISK_FIELD_END,	N_("End"),	  5,	FDISK_FIELDFL_NUMBER },
+	{ FDISK_FIELD_SECTORS,	N_("Sectors"),	  5,	FDISK_FIELDFL_NUMBER },
+	{ FDISK_FIELD_CYLINDERS,N_("Cylinders"),  5,	FDISK_FIELDFL_NUMBER },
+	{ FDISK_FIELD_SIZE,	N_("Size"),	  5,	FDISK_FIELDFL_NUMBER | FDISK_FIELDFL_EYECANDY },
+	{ FDISK_FIELD_TYPEID,	N_("Id"),	  2,	FDISK_FIELDFL_NUMBER },
+	{ FDISK_FIELD_TYPE,	N_("Type"),	0.1,	0 },
+
+	/* expert mode */
+	{ FDISK_FIELD_SADDR,	N_("Start-C/H/S"), 1,   FDISK_FIELDFL_NUMBER | FDISK_FIELDFL_DETAIL },
+	{ FDISK_FIELD_EADDR,	N_("End-C/H/S"),   1,   FDISK_FIELDFL_NUMBER | FDISK_FIELDFL_DETAIL },
+	{ FDISK_FIELD_ATTR,	N_("Attrs"),	   2,   FDISK_FIELDFL_NUMBER | FDISK_FIELDFL_DETAIL }
+
+};
+
+static const struct fdisk_label_operations dos_operations =
+{
+	.probe		= dos_probe_label,
+	.write		= dos_write_disklabel,
+	.verify		= dos_verify_disklabel,
+	.create		= dos_create_disklabel,
+	.locate		= dos_locate_disklabel,
+	.list		= dos_list_disklabel,
+	.reorder	= dos_reorder,
+	.get_id		= dos_get_disklabel_id,
+	.set_id		= dos_set_disklabel_id,
+
+	.get_part	= dos_get_partition,
+	.set_part	= dos_set_partition,
+	.add_part	= dos_add_partition,
+	.del_part	= dos_delete_partition,
+
+	.part_toggle_flag = dos_toggle_partition_flag,
+	.part_is_used	= dos_partition_is_used,
+
+	.reset_alignment = dos_reset_alignment,
+
+	.deinit		= dos_deinit,
+};
+
+/*
+ * allocates DOS in-memory stuff
+ */
+struct fdisk_label *fdisk_new_dos_label(struct fdisk_context *cxt)
+{
+	struct fdisk_label *lb;
+	struct fdisk_dos_label *dos;
+
+	assert(cxt);
+
+	dos = calloc(1, sizeof(*dos));
+	if (!dos)
+		return NULL;
+
+	/* initialize generic part of the driver */
+	lb = (struct fdisk_label *) dos;
+	lb->name = "dos";
+	lb->id = FDISK_DISKLABEL_DOS;
+	lb->op = &dos_operations;
+	lb->parttypes = dos_parttypes;
+	lb->nparttypes = ARRAY_SIZE(dos_parttypes) - 1;
+	lb->fields = dos_fields;
+	lb->nfields = ARRAY_SIZE(dos_fields);
+
+	return lb;
+}
+
+/**
+ * fdisk_dos_enable_compatible:
+ * @lb: DOS label (see fdisk_get_label())
+ * @enable: 0 or 1
+ *
+ * Enables deprecated DOS compatible mode, in this mode library checks for
+ * cylinders boundary, cases about CHS addressing and another obscure things.
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int fdisk_dos_enable_compatible(struct fdisk_label *lb, int enable)
+{
+	struct fdisk_dos_label *dos = (struct fdisk_dos_label *) lb;
+
+	if (!lb)
+		return -EINVAL;
+
+	dos->compatible = enable;
+	if (enable)
+		lb->flags |= FDISK_LABEL_FL_REQUIRE_GEOMETRY;
+	return 0;
+}
+
+/**
+ * fdisk_dos_is_compatible:
+ * @lb: DOS label
+ *
+ * Returns: 0 if DOS compatibility disabled, 1 if enabled
+ */
+int fdisk_dos_is_compatible(struct fdisk_label *lb)
+{
+	return ((struct fdisk_dos_label *) lb)->compatible;
+}
diff --git a/libblkid/libfdisk/src/fdiskP.h b/libblkid/libfdisk/src/fdiskP.h
new file mode 100644
index 0000000..b169a9f
--- /dev/null
+++ b/libblkid/libfdisk/src/fdiskP.h
@@ -0,0 +1,438 @@
+/*
+ * fdiskP.h - private library header file
+ *
+ * Copyright (C) 2012 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+
+#ifndef _LIBFDISK_PRIVATE_H
+#define _LIBFDISK_PRIVATE_H
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+
+#include "c.h"
+#include "libfdisk.h"
+
+#include "nls.h"		/* temporary before dialog API will be implamented */
+#include "list.h"
+#include "debug.h"
+#include <stdio.h>
+#include <stdarg.h>
+
+/* features */
+#define CONFIG_LIBFDISK_ASSERT
+
+#ifdef CONFIG_LIBFDISK_ASSERT
+#include <assert.h>
+#endif
+
+/*
+ * Debug
+ */
+#define LIBFDISK_DEBUG_HELP	(1 << 0)
+#define LIBFDISK_DEBUG_INIT	(1 << 1)
+#define LIBFDISK_DEBUG_CXT	(1 << 2)
+#define LIBFDISK_DEBUG_LABEL    (1 << 3)
+#define LIBFDISK_DEBUG_ASK      (1 << 4)
+#define LIBFDISK_DEBUG_PART	(1 << 6)
+#define LIBFDISK_DEBUG_PARTTYPE	(1 << 7)
+#define LIBFDISK_DEBUG_TAB	(1 << 8)
+#define LIBFDISK_DEBUG_SCRIPT	(1 << 9)
+#define LIBFDISK_DEBUG_ALL	0xFFFF
+
+UL_DEBUG_DECLARE_MASK(libfdisk);
+#define DBG(m, x)	__UL_DBG(libfdisk, LIBFDISK_DEBUG_, m, x)
+#define ON_DBG(m, x)	__UL_DBG_CALL(libfdisk, LIBFDISK_DEBUG_, m, x)
+#define DBG_FLUSH	__UL_DBG_FLUSH(libfdisk, LIBFDISK_DEBUG_)
+
+#ifdef TEST_PROGRAM
+struct fdisk_test {
+	const char	*name;
+	int		(*body)(struct fdisk_test *ts, int argc, char *argv[]);
+	const char	*usage;
+};
+
+/* test.c */
+extern int fdisk_run_test(struct fdisk_test *tests, int argc, char *argv[]);
+#endif
+
+
+/*
+ * Generic iterator
+ */
+struct fdisk_iter {
+        struct list_head        *p;		/* current position */
+        struct list_head        *head;		/* start position */
+	int			direction;	/* FDISK_ITER_{FOR,BACK}WARD */
+};
+
+#define IS_ITER_FORWARD(_i)	((_i)->direction == FDISK_ITER_FORWARD)
+#define IS_ITER_BACKWARD(_i)	((_i)->direction == FDISK_ITER_BACKWARD)
+
+#define FDISK_ITER_INIT(itr, list) \
+	do { \
+		(itr)->p = IS_ITER_FORWARD(itr) ? \
+				(list)->next : (list)->prev; \
+		(itr)->head = (list); \
+	} while(0)
+
+#define FDISK_ITER_ITERATE(itr, res, restype, member) \
+	do { \
+		res = list_entry((itr)->p, restype, member); \
+		(itr)->p = IS_ITER_FORWARD(itr) ? \
+				(itr)->p->next : (itr)->p->prev; \
+	} while(0)
+
+/*
+ * Partition types
+ */
+struct fdisk_parttype {
+	unsigned int	code;		/* type as number or zero */
+	char		*name;		/* description */
+	char		*typestr;	/* type as string or NULL */
+
+	unsigned int	flags;		/* FDISK_PARTTYPE_* flags */
+	int		refcount;	/* reference counter for allocated types */
+};
+
+enum {
+	FDISK_PARTTYPE_UNKNOWN		= (1 << 1),
+	FDISK_PARTTYPE_INVISIBLE	= (1 << 2),
+	FDISK_PARTTYPE_ALLOCATED	= (1 << 3)
+};
+
+#define fdisk_parttype_is_invisible(_x)	((_x) && ((_x)->flags & FDISK_PARTTYPE_INVISIBLE))
+#define fdisk_parttype_is_allocated(_x)	((_x) && ((_x)->flags & FDISK_PARTTYPE_ALLOCATED))
+
+struct fdisk_partition {
+	int		refcount;		/* reference counter */
+
+	size_t		partno;			/* partition number */
+	size_t		parent_partno;		/* for logical partitions */
+
+	fdisk_sector_t	start;			/* first sectors */
+	fdisk_sector_t	size;			/* size in sectors */
+
+	char		*name;			/* partition name */
+	char		*uuid;			/* partition UUID */
+	char		*attrs;			/* partition flags/attributes converted to string */
+	struct fdisk_parttype	*type;		/* partition type */
+
+	struct list_head	parts;		/* list of partitions */
+
+	/* extra fields for partition_to_string() */
+	char		start_post;		/* start postfix  (e.g. '+') */
+	char		end_post;		/* end postfix */
+	char		size_post;		/* size postfix */
+
+	uint64_t	fsize;			/* bsd junk */
+	uint64_t	bsize;
+	uint64_t	cpg;
+
+	char		*start_chs;		/* start C/H/S in string */
+	char		*end_chs;		/* end C/H/S in string */
+
+	unsigned int	boot;			/* MBR: bootable */
+
+	unsigned int	container : 1,			/* container partition (e.g. extended partition) */
+			end_follow_default : 1,		/* use default end */
+			freespace : 1,			/* this is free space */
+			partno_follow_default : 1,	/* use default partno */
+			size_explicit : 1,		/* don't align the size */
+			start_follow_default : 1,	/* use default start */
+			used : 1,			/* partition already used */
+			wholedisk : 1;			/* special system partition */
+};
+
+#define FDISK_INIT_UNDEF(_x)	((_x) = (__typeof__(_x)) -1)
+#define FDISK_IS_UNDEF(_x)	((_x) == (__typeof__(_x)) -1)
+
+struct fdisk_table {
+	struct list_head	parts;		/* partitions */
+	int			refcount;
+	size_t			nents;		/* number of partitions */
+};
+
+/*
+ * Legacy CHS based geometry
+ */
+struct fdisk_geometry {
+	unsigned int heads;
+	fdisk_sector_t sectors;
+	fdisk_sector_t cylinders;
+};
+
+/*
+ * Label specific operations
+ */
+struct fdisk_label_operations {
+	/* probe disk label */
+	int (*probe)(struct fdisk_context *cxt);
+	/* write in-memory changes to disk */
+	int (*write)(struct fdisk_context *cxt);
+	/* verify the partition table */
+	int (*verify)(struct fdisk_context *cxt);
+	/* create new disk label */
+	int (*create)(struct fdisk_context *cxt);
+	/* list disklabel details */
+	int (*list)(struct fdisk_context *cxt);
+	/* returns offset and size of the 'n' part of the PT */
+	int (*locate)(struct fdisk_context *cxt, int n, const char **name, off_t *offset, size_t *size);
+	/* reorder partitions */
+	int (*reorder)(struct fdisk_context *cxt);
+
+	/* get disk label ID */
+	int (*get_id)(struct fdisk_context *cxt, char **id);
+	/* set disk label ID */
+	int (*set_id)(struct fdisk_context *cxt);
+
+
+	/* new partition */
+	int (*add_part)(struct fdisk_context *cxt, struct fdisk_partition *pa,
+						size_t *partno);
+	/* delete partition */
+	int (*del_part)(struct fdisk_context *cxt, size_t partnum);
+
+	/* fill in partition struct */
+	int (*get_part)(struct fdisk_context *cxt, size_t n,
+						struct fdisk_partition *pa);
+	/* modify partition */
+	int (*set_part)(struct fdisk_context *cxt, size_t n,
+						struct fdisk_partition *pa);
+
+	/* return state of the partition */
+	int (*part_is_used)(struct fdisk_context *cxt, size_t partnum);
+
+	int (*part_toggle_flag)(struct fdisk_context *cxt, size_t i, unsigned long flag);
+
+	/* refresh alignment setting */
+	int (*reset_alignment)(struct fdisk_context *cxt);
+
+	/* free in-memory label stuff */
+	void (*free)(struct fdisk_label *lb);
+
+	/* deinit in-memory label stuff */
+	void (*deinit)(struct fdisk_label *lb);
+};
+
+/*
+ * The fields describes how to display libfdisk_partition
+ */
+struct fdisk_field {
+	int		id;		/* FDISK_FIELD_* */
+	const char	*name;		/* field name */
+	double		width;		/* field width (compatible with libsmartcols whint) */
+	int		flags;		/* FDISK_FIELDFL_* */
+};
+
+/* note that the defauls is to display a column always */
+enum {
+	FDISK_FIELDFL_DETAIL	= (1 << 1),	/* only display if fdisk_is_details() */
+	FDISK_FIELDFL_EYECANDY	= (1 << 2),	/* don't display if fdisk_is_details() */
+	FDISK_FIELDFL_NUMBER	= (1 << 3),	/* column display numbers */
+};
+
+/*
+ * Generic label
+ */
+struct fdisk_label {
+	const char		*name;		/* label name */
+	enum fdisk_labeltype	id;		/* FDISK_DISKLABEL_* */
+	struct fdisk_parttype	*parttypes;	/* supported partitions types */
+	size_t			nparttypes;	/* number of items in parttypes[] */
+
+	size_t			nparts_max;	/* maximal number of partitions */
+	size_t			nparts_cur;	/* number of currently used partitions */
+
+	int			flags;		/* FDISK_LABEL_FL_* flags */
+
+	unsigned int		changed:1,	/* label has been modified */
+				disabled:1;	/* this driver is disabled at all */
+
+	const struct fdisk_field *fields;	/* all possible fields */
+	size_t			nfields;
+
+	const struct fdisk_label_operations *op;
+};
+
+
+/* label driver flags */
+enum {
+	FDISK_LABEL_FL_REQUIRE_GEOMETRY = (1 << 2),
+	FDISK_LABEL_FL_INCHARS_PARTNO   = (1 << 3)
+};
+
+/* label allocators */
+extern struct fdisk_label *fdisk_new_gpt_label(struct fdisk_context *cxt);
+extern struct fdisk_label *fdisk_new_dos_label(struct fdisk_context *cxt);
+extern struct fdisk_label *fdisk_new_bsd_label(struct fdisk_context *cxt);
+extern struct fdisk_label *fdisk_new_sgi_label(struct fdisk_context *cxt);
+extern struct fdisk_label *fdisk_new_sun_label(struct fdisk_context *cxt);
+
+
+struct ask_menuitem {
+	char	key;
+	const char	*name;
+	const char	*desc;
+
+	struct ask_menuitem *next;
+};
+
+/* fdisk dialog -- note that nothing from this stuff will be directly exported,
+ * we will have get/set() function for everything.
+ */
+struct fdisk_ask {
+	int		type;		/* FDISK_ASKTYPE_* */
+	char		*query;
+
+	int		refcount;
+
+	union {
+		/* FDISK_ASKTYPE_{NUMBER,OFFSET} */
+		struct ask_number {
+			uint64_t	hig;		/* high limit */
+			uint64_t	low;		/* low limit */
+			uint64_t	dfl;		/* default */
+			uint64_t	result;
+			uint64_t	base;		/* for relative results */
+			uint64_t	unit;		/* unit for offsets */
+			const char	*range;		/* by library generated list */
+			unsigned int	relative :1,
+					inchars  :1;
+		} num;
+		/* FDISK_ASKTYPE_{WARN,WARNX,..} */
+		struct ask_print {
+			const char	*mesg;
+			int		errnum;		/* errno */
+		} print;
+		/* FDISK_ASKTYPE_YESNO */
+		struct ask_yesno {
+			int		result;		/* TRUE or FALSE */
+		} yesno;
+		/* FDISK_ASKTYPE_STRING */
+		struct ask_string {
+			char		*result;	/* allocated */
+		} str;
+		/* FDISK_ASKTYPE_MENU */
+		struct ask_menu {
+			int		dfl;		/* default meni item */
+			int		result;
+			struct ask_menuitem *first;
+		} menu;
+	} data;
+};
+
+struct fdisk_context {
+	int dev_fd;         /* device descriptor */
+	char *dev_path;     /* device path */
+	int refcount;
+
+	unsigned char *firstsector; /* buffer with master boot record */
+	unsigned long firstsector_bufsz;
+
+	/* topology */
+	unsigned long io_size;		/* I/O size used by fdisk */
+	unsigned long optimal_io_size;	/* optional I/O returned by device */
+	unsigned long min_io_size;	/* minimal I/O size */
+	unsigned long phy_sector_size;	/* physical size */
+	unsigned long sector_size;	/* logical size */
+	unsigned long alignment_offset;
+
+	unsigned int readonly : 1,		/* don't write to the device */
+		     display_in_cyl_units : 1,	/* for obscure labels */
+		     display_details : 1,	/* expert display mode */
+		     listonly : 1;		/* list partition, nothing else */
+
+	/* alignment */
+	unsigned long grain;		/* alignment unit */
+	fdisk_sector_t first_lba;		/* recommended begin of the first partition */
+	fdisk_sector_t last_lba;		/* recomennded end of last partition */
+
+	/* geometry */
+	fdisk_sector_t total_sectors;	/* in logical sectors */
+	struct fdisk_geometry geom;
+
+	/* user setting to overwrite device default */
+	struct fdisk_geometry user_geom;
+	unsigned long user_pyh_sector;
+	unsigned long user_log_sector;
+
+	struct fdisk_label *label;	/* current label, pointer to labels[] */
+
+	size_t nlabels;			/* number of initialized label drivers */
+	struct fdisk_label *labels[8];	/* all supported labels,
+					 * FIXME: use any enum rather than hardcoded number */
+
+	int	(*ask_cb)(struct fdisk_context *, struct fdisk_ask *, void *);	/* fdisk dialogs callback */
+	void	*ask_data;		/* ask_cb() data */
+
+	struct fdisk_context	*parent;	/* for nested PT */
+	struct fdisk_script	*script;	/* what we want to follow */
+};
+
+/* partition.c */
+int fdisk_partition_next_partno(struct fdisk_partition *pa,
+				       struct fdisk_context *cxt,
+				       size_t *n);
+
+/* context.c */
+extern int __fdisk_switch_label(struct fdisk_context *cxt,
+				    struct fdisk_label *lb);
+extern int fdisk_missing_geometry(struct fdisk_context *cxt);
+
+/* alignment.c */
+fdisk_sector_t fdisk_scround(struct fdisk_context *cxt, fdisk_sector_t num);
+fdisk_sector_t fdisk_cround(struct fdisk_context *cxt, fdisk_sector_t num);
+
+extern int fdisk_discover_geometry(struct fdisk_context *cxt);
+extern int fdisk_discover_topology(struct fdisk_context *cxt);
+
+extern int fdisk_apply_user_device_properties(struct fdisk_context *cxt);
+extern void fdisk_zeroize_device_properties(struct fdisk_context *cxt);
+
+/* utils.c */
+extern int fdisk_init_firstsector_buffer(struct fdisk_context *cxt);
+extern int fdisk_read_firstsector(struct fdisk_context *cxt);
+extern char *fdisk_partname(const char *dev, size_t partno);
+
+/* label.c */
+extern int fdisk_probe_labels(struct fdisk_context *cxt);
+extern void fdisk_deinit_label(struct fdisk_label *lb);
+
+/* ask.c */
+struct fdisk_ask *fdisk_new_ask(void);
+void fdisk_reset_ask(struct fdisk_ask *ask);
+int fdisk_ask_set_query(struct fdisk_ask *ask, const char *str);
+int fdisk_ask_set_type(struct fdisk_ask *ask, int type);
+int fdisk_do_ask(struct fdisk_context *cxt, struct fdisk_ask *ask);
+int fdisk_ask_number_set_range(struct fdisk_ask *ask, const char *range);
+int fdisk_ask_number_set_default(struct fdisk_ask *ask, uint64_t dflt);
+int fdisk_ask_number_set_low(struct fdisk_ask *ask, uint64_t low);
+int fdisk_ask_number_set_high(struct fdisk_ask *ask, uint64_t high);
+int fdisk_ask_number_set_base(struct fdisk_ask *ask, uint64_t base);
+int fdisk_ask_number_set_unit(struct fdisk_ask *ask, uint64_t unit);
+int fdisk_ask_number_is_relative(struct fdisk_ask *ask);
+int fdisk_ask_menu_set_default(struct fdisk_ask *ask, int dfl);
+int fdisk_ask_menu_add_item(struct fdisk_ask *ask, int key,
+			const char *name, const char *desc);
+int fdisk_ask_print_set_errno(struct fdisk_ask *ask, int errnum);
+int fdisk_ask_print_set_mesg(struct fdisk_ask *ask, const char *mesg);
+int fdisk_info_new_partition(
+			struct fdisk_context *cxt,
+			int num, fdisk_sector_t start, fdisk_sector_t stop,
+			struct fdisk_parttype *t);
+
+/* dos.c */
+extern struct dos_partition *fdisk_dos_get_partition(
+				struct fdisk_context *cxt,
+				size_t i);
+
+#endif /* _LIBFDISK_PRIVATE_H */
diff --git a/libblkid/libfdisk/src/gpt.c b/libblkid/libfdisk/src/gpt.c
new file mode 100644
index 0000000..8c1c96c
--- /dev/null
+++ b/libblkid/libfdisk/src/gpt.c
@@ -0,0 +1,2565 @@
+/*
+ * Copyright (C) 2007 Karel Zak <kzak@redhat.com>
+ * Copyright (C) 2012 Davidlohr Bueso <dave@gnu.org>
+ *
+ * GUID Partition Table (GPT) support. Based on UEFI Specs 2.3.1
+ * Chapter 5: GUID Partition Table (GPT) Disk Layout (Jun 27th, 2012).
+ * Some ideas and inspiration from GNU parted and gptfdisk.
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <sys/stat.h>
+#include <sys/utsname.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <ctype.h>
+#include <uuid.h>
+
+#include "fdiskP.h"
+
+#include "nls.h"
+#include "crc32.h"
+#include "blkdev.h"
+#include "bitops.h"
+#include "strutils.h"
+#include "all-io.h"
+
+/**
+ * SECTION: gpt
+ * @title: UEFI GPT
+ * @short_description: specific functionality
+ */
+
+#define GPT_HEADER_SIGNATURE 0x5452415020494645LL /* EFI PART */
+#define GPT_HEADER_REVISION_V1_02 0x00010200
+#define GPT_HEADER_REVISION_V1_00 0x00010000
+#define GPT_HEADER_REVISION_V0_99 0x00009900
+#define GPT_HEADER_MINSZ          92 /* bytes */
+
+#define GPT_PMBR_LBA        0
+#define GPT_MBR_PROTECTIVE  1
+#define GPT_MBR_HYBRID      2
+
+#define GPT_PRIMARY_PARTITION_TABLE_LBA 0x00000001
+
+#define EFI_PMBR_OSTYPE     0xEE
+#define MSDOS_MBR_SIGNATURE 0xAA55
+#define GPT_PART_NAME_LEN   (72 / sizeof(uint16_t))
+#define GPT_NPARTITIONS     128
+
+/* Globally unique identifier */
+struct gpt_guid {
+	uint32_t   time_low;
+	uint16_t   time_mid;
+	uint16_t   time_hi_and_version;
+	uint8_t    clock_seq_hi;
+	uint8_t    clock_seq_low;
+	uint8_t    node[6];
+};
+
+
+/* only checking that the GUID is 0 is enough to verify an empty partition. */
+#define GPT_UNUSED_ENTRY_GUID						\
+	((struct gpt_guid) { 0x00000000, 0x0000, 0x0000, 0x00, 0x00,	\
+			     { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }})
+
+/* Linux native partition type */
+#define GPT_DEFAULT_ENTRY_TYPE "0FC63DAF-8483-4772-8E79-3D69D8477DE4"
+
+/*
+ * Attribute bits
+ */
+enum {
+	/* UEFI specific */
+	GPT_ATTRBIT_REQ      = 0,
+	GPT_ATTRBIT_NOBLOCK  = 1,
+	GPT_ATTRBIT_LEGACY   = 2,
+
+	/* GUID specific (range 48..64)*/
+	GPT_ATTRBIT_GUID_FIRST	= 48,
+	GPT_ATTRBIT_GUID_COUNT	= 16
+};
+
+#define GPT_ATTRSTR_REQ		"RequiredPartiton"
+#define GPT_ATTRSTR_NOBLOCK	"NoBlockIOProtocol"
+#define GPT_ATTRSTR_LEGACY	"LegacyBIOSBootable"
+
+/* The GPT Partition entry array contains an array of GPT entries. */
+struct gpt_entry {
+	struct gpt_guid     type; /* purpose and type of the partition */
+	struct gpt_guid     partition_guid;
+	uint64_t            lba_start;
+	uint64_t            lba_end;
+	uint64_t            attrs;
+	uint16_t            name[GPT_PART_NAME_LEN];
+}  __attribute__ ((packed));
+
+/* GPT header */
+struct gpt_header {
+	uint64_t            signature; /* header identification */
+	uint32_t            revision; /* header version */
+	uint32_t            size; /* in bytes */
+	uint32_t            crc32; /* header CRC checksum */
+	uint32_t            reserved1; /* must be 0 */
+	uint64_t            my_lba; /* LBA of block that contains this struct (LBA 1) */
+	uint64_t            alternative_lba; /* backup GPT header */
+	uint64_t            first_usable_lba; /* first usable logical block for partitions */
+	uint64_t            last_usable_lba; /* last usable logical block for partitions */
+	struct gpt_guid     disk_guid; /* unique disk identifier */
+	uint64_t            partition_entry_lba; /* LBA of start of partition entries array */
+	uint32_t            npartition_entries; /* total partition entries - normally 128 */
+	uint32_t            sizeof_partition_entry; /* bytes for each GUID pt */
+	uint32_t            partition_entry_array_crc32; /* partition CRC checksum */
+	uint8_t             reserved2[512 - 92]; /* must all be 0 */
+} __attribute__ ((packed));
+
+struct gpt_record {
+	uint8_t             boot_indicator; /* unused by EFI, set to 0x80 for bootable */
+	uint8_t             start_head; /* unused by EFI, pt start in CHS */
+	uint8_t             start_sector; /* unused by EFI, pt start in CHS */
+	uint8_t             start_track;
+	uint8_t             os_type; /* EFI and legacy non-EFI OS types */
+	uint8_t             end_head; /* unused by EFI, pt end in CHS */
+	uint8_t             end_sector; /* unused by EFI, pt end in CHS */
+	uint8_t             end_track; /* unused by EFI, pt end in CHS */
+	uint32_t            starting_lba; /* used by EFI - start addr of the on disk pt */
+	uint32_t            size_in_lba; /* used by EFI - size of pt in LBA */
+} __attribute__ ((packed));
+
+/* Protected MBR and legacy MBR share same structure */
+struct gpt_legacy_mbr {
+	uint8_t             boot_code[440];
+	uint32_t            unique_mbr_signature;
+	uint16_t            unknown;
+	struct gpt_record   partition_record[4];
+	uint16_t            signature;
+} __attribute__ ((packed));
+
+/*
+ * Here be dragons!
+ * See: http://en.wikipedia.org/wiki/GUID_Partition_Table#Partition_type_GUIDs
+ */
+#define DEF_GUID(_u, _n) \
+	{ \
+		.typestr = (_u), \
+		.name = (_n),    \
+	}
+
+static struct fdisk_parttype gpt_parttypes[] =
+{
+	/* Generic OS */
+	DEF_GUID("C12A7328-F81F-11D2-BA4B-00A0C93EC93B", N_("EFI System")),
+
+	DEF_GUID("024DEE41-33E7-11D3-9D69-0008C781F39F", N_("MBR partition scheme")),
+	DEF_GUID("D3BFE2DE-3DAF-11DF-BA40-E3A556D89593", N_("Intel Fast Flash")),
+
+	/* Hah!IdontneedEFI */
+	DEF_GUID("21686148-6449-6E6F-744E-656564454649", N_("BIOS boot")),
+
+	/* Windows */
+	DEF_GUID("E3C9E316-0B5C-4DB8-817D-F92DF00215AE", N_("Microsoft reserved")),
+	DEF_GUID("EBD0A0A2-B9E5-4433-87C0-68B6B72699C7", N_("Microsoft basic data")),
+	DEF_GUID("5808C8AA-7E8F-42E0-85D2-E1E90434CFB3", N_("Microsoft LDM metadata")),
+	DEF_GUID("AF9B60A0-1431-4F62-BC68-3311714A69AD", N_("Microsoft LDM data")),
+	DEF_GUID("DE94BBA4-06D1-4D40-A16A-BFD50179D6AC", N_("Windows recovery environment")),
+	DEF_GUID("37AFFC90-EF7D-4E96-91C3-2D7AE055B174", N_("IBM General Parallel Fs")),
+	DEF_GUID("E75CAF8F-F680-4CEE-AFA3-B001E56EFC2D", N_("Microsoft Storage Spaces")),
+
+	/* HP-UX */
+	DEF_GUID("75894C1E-3AEB-11D3-B7C1-7B03A0000000", N_("HP-UX data")),
+	DEF_GUID("E2A1E728-32E3-11D6-A682-7B03A0000000", N_("HP-UX service")),
+
+	/* Linux (http://www.freedesktop.org/wiki/Specifications/DiscoverablePartitionsSpec) */
+	DEF_GUID("0657FD6D-A4AB-43C4-84E5-0933C84B4F4F", N_("Linux swap")),
+	DEF_GUID("0FC63DAF-8483-4772-8E79-3D69D8477DE4", N_("Linux filesystem")),
+	DEF_GUID("3B8F8425-20E0-4F3B-907F-1A25A76F98E8", N_("Linux server data")),
+	DEF_GUID("44479540-F297-41B2-9AF7-D131D5F0458A", N_("Linux root (x86)")),
+	DEF_GUID("4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709", N_("Linux root (x86-64)")),
+	DEF_GUID("8DA63339-0007-60C0-C436-083AC8230908", N_("Linux reserved")),
+	DEF_GUID("933AC7E1-2EB4-4F13-B844-0E14E2AEF915", N_("Linux home")),
+	DEF_GUID("A19D880F-05FC-4D3B-A006-743F0F84911E", N_("Linux RAID")),
+	DEF_GUID("BC13C2FF-59E6-4262-A352-B275FD6F7172", N_("Linux extended boot")),
+	DEF_GUID("E6D6D379-F507-44C2-A23C-238F2A3DF928", N_("Linux LVM")),
+
+	/* FreeBSD */
+	DEF_GUID("516E7CB4-6ECF-11D6-8FF8-00022D09712B", N_("FreeBSD data")),
+	DEF_GUID("83BD6B9D-7F41-11DC-BE0B-001560B84F0F", N_("FreeBSD boot")),
+	DEF_GUID("516E7CB5-6ECF-11D6-8FF8-00022D09712B", N_("FreeBSD swap")),
+	DEF_GUID("516E7CB6-6ECF-11D6-8FF8-00022D09712B", N_("FreeBSD UFS")),
+	DEF_GUID("516E7CBA-6ECF-11D6-8FF8-00022D09712B", N_("FreeBSD ZFS")),
+	DEF_GUID("516E7CB8-6ECF-11D6-8FF8-00022D09712B", N_("FreeBSD Vinum")),
+
+	/* Apple OSX */
+	DEF_GUID("48465300-0000-11AA-AA11-00306543ECAC", N_("Apple HFS/HFS+")),
+	DEF_GUID("55465300-0000-11AA-AA11-00306543ECAC", N_("Apple UFS")),
+	DEF_GUID("52414944-0000-11AA-AA11-00306543ECAC", N_("Apple RAID")),
+	DEF_GUID("52414944-5F4F-11AA-AA11-00306543ECAC", N_("Apple RAID offline")),
+	DEF_GUID("426F6F74-0000-11AA-AA11-00306543ECAC", N_("Apple boot")),
+	DEF_GUID("4C616265-6C00-11AA-AA11-00306543ECAC", N_("Apple label")),
+	DEF_GUID("5265636F-7665-11AA-AA11-00306543ECAC", N_("Apple TV recovery")),
+	DEF_GUID("53746F72-6167-11AA-AA11-00306543ECAC", N_("Apple Core storage")),
+
+	/* Solaris */
+	DEF_GUID("6A82CB45-1DD2-11B2-99A6-080020736631", N_("Solaris boot")),
+	DEF_GUID("6A85CF4D-1DD2-11B2-99A6-080020736631", N_("Solaris root")),
+	/* same as Apple ZFS */
+	DEF_GUID("6A898CC3-1DD2-11B2-99A6-080020736631", N_("Solaris /usr & Apple ZFS")),
+	DEF_GUID("6A87C46F-1DD2-11B2-99A6-080020736631", N_("Solaris swap")),
+	DEF_GUID("6A8B642B-1DD2-11B2-99A6-080020736631", N_("Solaris backup")),
+	DEF_GUID("6A8EF2E9-1DD2-11B2-99A6-080020736631", N_("Solaris /var")),
+	DEF_GUID("6A90BA39-1DD2-11B2-99A6-080020736631", N_("Solaris /home")),
+	DEF_GUID("6A9283A5-1DD2-11B2-99A6-080020736631", N_("Solaris alternate sector")),
+	DEF_GUID("6A945A3B-1DD2-11B2-99A6-080020736631", N_("Solaris reserved 1")),
+	DEF_GUID("6A9630D1-1DD2-11B2-99A6-080020736631", N_("Solaris reserved 2")),
+	DEF_GUID("6A980767-1DD2-11B2-99A6-080020736631", N_("Solaris reserved 3")),
+	DEF_GUID("6A96237F-1DD2-11B2-99A6-080020736631", N_("Solaris reserved 4")),
+	DEF_GUID("6A8D2AC7-1DD2-11B2-99A6-080020736631", N_("Solaris reserved 5")),
+
+	/* NetBSD */
+	DEF_GUID("49F48D32-B10E-11DC-B99B-0019D1879648", N_("NetBSD swap")),
+	DEF_GUID("49F48D5A-B10E-11DC-B99B-0019D1879648", N_("NetBSD FFS")),
+	DEF_GUID("49F48D82-B10E-11DC-B99B-0019D1879648", N_("NetBSD LFS")),
+	DEF_GUID("2DB519C4-B10E-11DC-B99B-0019D1879648", N_("NetBSD concatenated")),
+	DEF_GUID("2DB519EC-B10E-11DC-B99B-0019D1879648", N_("NetBSD encrypted")),
+	DEF_GUID("49F48DAA-B10E-11DC-B99B-0019D1879648", N_("NetBSD RAID")),
+
+	/* ChromeOS */
+	DEF_GUID("FE3A2A5D-4F32-41A7-B725-ACCC3285A309", N_("ChromeOS kernel")),
+	DEF_GUID("3CB8E202-3B7E-47DD-8A3C-7FF2A13CFCEC", N_("ChromeOS root fs")),
+	DEF_GUID("2E0A753D-9E48-43B0-8337-B15192CB1B5E", N_("ChromeOS reserved")),
+
+	/* MidnightBSD */
+	DEF_GUID("85D5E45A-237C-11E1-B4B3-E89A8F7FC3A7", N_("MidnightBSD data")),
+	DEF_GUID("85D5E45E-237C-11E1-B4B3-E89A8F7FC3A7", N_("MidnightBSD boot")),
+	DEF_GUID("85D5E45B-237C-11E1-B4B3-E89A8F7FC3A7", N_("MidnightBSD swap")),
+	DEF_GUID("0394Ef8B-237C-11E1-B4B3-E89A8F7FC3A7", N_("MidnightBSD UFS")),
+	DEF_GUID("85D5E45D-237C-11E1-B4B3-E89A8F7FC3A7", N_("MidnightBSD ZFS")),
+	DEF_GUID("85D5E45C-237C-11E1-B4B3-E89A8F7FC3A7", N_("MidnightBSD Vinum")),
+};
+
+/* gpt_entry macros */
+#define gpt_partition_start(_e)		le64_to_cpu((_e)->lba_start)
+#define gpt_partition_end(_e)		le64_to_cpu((_e)->lba_end)
+
+/*
+ * in-memory fdisk GPT stuff
+ */
+struct fdisk_gpt_label {
+	struct fdisk_label	head;		/* generic part */
+
+	/* gpt specific part */
+	struct gpt_header	*pheader;	/* primary header */
+	struct gpt_header	*bheader;	/* backup header */
+	struct gpt_entry	*ents;		/* entries (partitions) */
+};
+
+static void gpt_deinit(struct fdisk_label *lb);
+
+static inline struct fdisk_gpt_label *self_label(struct fdisk_context *cxt)
+{
+	return (struct fdisk_gpt_label *) cxt->label;
+}
+
+/*
+ * Returns the partition length, or 0 if end is before beginning.
+ */
+static uint64_t gpt_partition_size(const struct gpt_entry *e)
+{
+	uint64_t start = gpt_partition_start(e);
+	uint64_t end = gpt_partition_end(e);
+
+	return start > end ? 0 : end - start + 1ULL;
+}
+
+/* prints UUID in the real byte order! */
+static void gpt_debug_uuid(const char *mesg, struct gpt_guid *guid)
+{
+	const unsigned char *uuid = (unsigned char *) guid;
+
+	fprintf(stderr, "%s: "
+		"%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n",
+		mesg,
+		uuid[0], uuid[1], uuid[2], uuid[3],
+		uuid[4], uuid[5],
+		uuid[6], uuid[7],
+		uuid[8], uuid[9],
+		uuid[10], uuid[11], uuid[12], uuid[13], uuid[14],uuid[15]);
+}
+
+/*
+ * UUID is traditionally 16 byte big-endian array, except Intel EFI
+ * specification where the UUID is a structure of little-endian fields.
+ */
+static void swap_efi_guid(struct gpt_guid *uid)
+{
+	uid->time_low = swab32(uid->time_low);
+	uid->time_mid = swab16(uid->time_mid);
+	uid->time_hi_and_version = swab16(uid->time_hi_and_version);
+}
+
+static int string_to_guid(const char *in, struct gpt_guid *guid)
+{
+	if (uuid_parse(in, (unsigned char *) guid))	/* BE */
+		return -1;
+	swap_efi_guid(guid);				/* LE */
+	return 0;
+}
+
+static char *guid_to_string(const struct gpt_guid *guid, char *out)
+{
+	struct gpt_guid u = *guid;	/* LE */
+
+	swap_efi_guid(&u);		/* BE */
+	uuid_unparse_upper((unsigned char *) &u, out);
+
+	return out;
+}
+
+static struct fdisk_parttype *gpt_partition_parttype(
+		struct fdisk_context *cxt,
+		const struct gpt_entry *e)
+{
+	struct fdisk_parttype *t;
+	char str[37];
+
+	guid_to_string(&e->type, str);
+	t = fdisk_label_get_parttype_from_string(cxt->label, str);
+	return t ? : fdisk_new_unknown_parttype(0, str);
+}
+
+static void gpt_entry_set_type(struct gpt_entry *e, struct gpt_guid *uuid)
+{
+	e->type = *uuid;
+	DBG(LABEL, gpt_debug_uuid("new type", &(e->type)));
+}
+
+static void gpt_entry_set_name(struct gpt_entry *e, char *str)
+{
+	char name[GPT_PART_NAME_LEN] = { 0 };
+	size_t i, sz = strlen(str);
+
+	if (sz) {
+		if (sz > GPT_PART_NAME_LEN)
+			sz = GPT_PART_NAME_LEN;
+		memcpy(name, str, sz);
+	}
+
+	for (i = 0; i < GPT_PART_NAME_LEN; i++)
+		e->name[i] = cpu_to_le16((uint16_t) name[i]);
+}
+
+static int gpt_entry_set_uuid(struct gpt_entry *e, char *str)
+{
+	struct gpt_guid uuid;
+	int rc;
+
+	rc = string_to_guid(str, &uuid);
+	if (rc)
+		return rc;
+
+	e->partition_guid = uuid;
+	return 0;
+}
+
+
+static const char *gpt_get_header_revstr(struct gpt_header *header)
+{
+	if (!header)
+		goto unknown;
+
+	switch (header->revision) {
+	case GPT_HEADER_REVISION_V1_02:
+		return "1.2";
+	case GPT_HEADER_REVISION_V1_00:
+		return "1.0";
+	case GPT_HEADER_REVISION_V0_99:
+		return "0.99";
+	default:
+		goto unknown;
+	}
+
+unknown:
+	return "unknown";
+}
+
+static inline int partition_unused(const struct gpt_entry *e)
+{
+	return !memcmp(&e->type, &GPT_UNUSED_ENTRY_GUID,
+			sizeof(struct gpt_guid));
+}
+
+/*
+ * Builds a clean new valid protective MBR - will wipe out any existing data.
+ * Returns 0 on success, otherwise < 0 on error.
+ */
+static int gpt_mknew_pmbr(struct fdisk_context *cxt)
+{
+	struct gpt_legacy_mbr *pmbr = NULL;
+	int rc;
+
+	if (!cxt || !cxt->firstsector)
+		return -ENOSYS;
+
+	rc = fdisk_init_firstsector_buffer(cxt);
+	if (rc)
+		return rc;
+
+	pmbr = (struct gpt_legacy_mbr *) cxt->firstsector;
+
+	pmbr->signature = cpu_to_le16(MSDOS_MBR_SIGNATURE);
+	pmbr->partition_record[0].os_type      = EFI_PMBR_OSTYPE;
+	pmbr->partition_record[0].start_sector = 1;
+	pmbr->partition_record[0].end_head     = 0xFE;
+	pmbr->partition_record[0].end_sector   = 0xFF;
+	pmbr->partition_record[0].end_track    = 0xFF;
+	pmbr->partition_record[0].starting_lba = cpu_to_le32(1);
+	pmbr->partition_record[0].size_in_lba  =
+		cpu_to_le32(min((uint32_t) cxt->total_sectors - 1, 0xFFFFFFFF));
+
+	return 0;
+}
+
+/* some universal differences between the headers */
+static void gpt_mknew_header_common(struct fdisk_context *cxt,
+				    struct gpt_header *header, uint64_t lba)
+{
+	if (!cxt || !header)
+		return;
+
+	header->my_lba = cpu_to_le64(lba);
+
+	if (lba == GPT_PRIMARY_PARTITION_TABLE_LBA) { /* primary */
+		header->alternative_lba = cpu_to_le64(cxt->total_sectors - 1);
+		header->partition_entry_lba = cpu_to_le64(2);
+	} else { /* backup */
+		uint64_t esz = le32_to_cpu(header->npartition_entries) * sizeof(struct gpt_entry);
+		uint64_t esects = (esz + cxt->sector_size - 1) / cxt->sector_size;
+
+		header->alternative_lba = cpu_to_le64(GPT_PRIMARY_PARTITION_TABLE_LBA);
+		header->partition_entry_lba = cpu_to_le64(cxt->total_sectors - 1 - esects);
+	}
+}
+
+/*
+ * Builds a new GPT header (at sector lba) from a backup header2.
+ * If building a primary header, then backup is the secondary, and vice versa.
+ *
+ * Always pass a new (zeroized) header to build upon as we don't
+ * explicitly zero-set some values such as CRCs and reserved.
+ *
+ * Returns 0 on success, otherwise < 0 on error.
+ */
+static int gpt_mknew_header_from_bkp(struct fdisk_context *cxt,
+				     struct gpt_header *header,
+				     uint64_t lba,
+				     struct gpt_header *header2)
+{
+	if (!cxt || !header || !header2)
+		return -ENOSYS;
+
+	header->signature              = header2->signature;
+	header->revision               = header2->revision;
+	header->size                   = header2->size;
+	header->npartition_entries     = header2->npartition_entries;
+	header->sizeof_partition_entry = header2->sizeof_partition_entry;
+	header->first_usable_lba       = header2->first_usable_lba;
+	header->last_usable_lba        = header2->last_usable_lba;
+
+	memcpy(&header->disk_guid,
+	       &header2->disk_guid, sizeof(header2->disk_guid));
+	gpt_mknew_header_common(cxt, header, lba);
+
+	return 0;
+}
+
+static struct gpt_header *gpt_copy_header(struct fdisk_context *cxt,
+			   struct gpt_header *src)
+{
+	struct gpt_header *res;
+
+	if (!cxt || !src)
+		return NULL;
+
+	res = calloc(1, sizeof(*res));
+	if (!res) {
+		fdisk_warn(cxt, _("failed to allocate GPT header"));
+		return NULL;
+	}
+
+	res->my_lba                 = src->alternative_lba;
+	res->alternative_lba        = src->my_lba;
+
+	res->signature              = src->signature;
+	res->revision               = src->revision;
+	res->size                   = src->size;
+	res->npartition_entries     = src->npartition_entries;
+	res->sizeof_partition_entry = src->sizeof_partition_entry;
+	res->first_usable_lba       = src->first_usable_lba;
+	res->last_usable_lba        = src->last_usable_lba;
+
+	memcpy(&res->disk_guid, &src->disk_guid, sizeof(src->disk_guid));
+
+
+	if (res->my_lba == GPT_PRIMARY_PARTITION_TABLE_LBA)
+		res->partition_entry_lba = cpu_to_le64(2);
+	else {
+		uint64_t esz = le32_to_cpu(src->npartition_entries) * sizeof(struct gpt_entry);
+		uint64_t esects = (esz + cxt->sector_size - 1) / cxt->sector_size;
+
+		res->partition_entry_lba = cpu_to_le64(cxt->total_sectors - 1 - esects);
+	}
+
+	return res;
+}
+
+static void count_first_last_lba(struct fdisk_context *cxt,
+				 uint64_t *first, uint64_t *last)
+{
+	uint64_t esz = 0;
+
+	assert(cxt);
+
+	esz = sizeof(struct gpt_entry) * GPT_NPARTITIONS / cxt->sector_size;
+	*last = cxt->total_sectors - 2 - esz;
+	*first = esz + 2;
+
+	if (*first < cxt->first_lba && cxt->first_lba < *last)
+		/* Align according to topology */
+		*first = cxt->first_lba;
+}
+
+/*
+ * Builds a clean new GPT header (currently under revision 1.0).
+ *
+ * Always pass a new (zeroized) header to build upon as we don't
+ * explicitly zero-set some values such as CRCs and reserved.
+ *
+ * Returns 0 on success, otherwise < 0 on error.
+ */
+static int gpt_mknew_header(struct fdisk_context *cxt,
+			    struct gpt_header *header, uint64_t lba)
+{
+	uint64_t first, last;
+	int has_id = 0;
+
+	if (!cxt || !header)
+		return -ENOSYS;
+
+	header->signature = cpu_to_le64(GPT_HEADER_SIGNATURE);
+	header->revision  = cpu_to_le32(GPT_HEADER_REVISION_V1_00);
+	header->size      = cpu_to_le32(sizeof(struct gpt_header));
+
+	/*
+	 * 128 partitions are the default. It can go beyond that, but
+	 * we're creating a de facto header here, so no funny business.
+	 */
+	header->npartition_entries     = cpu_to_le32(GPT_NPARTITIONS);
+	header->sizeof_partition_entry = cpu_to_le32(sizeof(struct gpt_entry));
+
+	count_first_last_lba(cxt, &first, &last);
+	header->first_usable_lba = cpu_to_le64(first);
+	header->last_usable_lba  = cpu_to_le64(last);
+
+	gpt_mknew_header_common(cxt, header, lba);
+
+	if (cxt->script) {
+		const char *id = fdisk_script_get_header(cxt->script, "label-id");
+		if (id && string_to_guid(id, &header->disk_guid) == 0)
+			has_id = 1;
+	}
+
+	if (!has_id) {
+		uuid_generate_random((unsigned char *) &header->disk_guid);
+		swap_efi_guid(&header->disk_guid);
+	}
+	return 0;
+}
+
+/*
+ * Checks if there is a valid protective MBR partition table.
+ * Returns 0 if it is invalid or failure. Otherwise, return
+ * GPT_MBR_PROTECTIVE or GPT_MBR_HYBRID, depeding on the detection.
+ */
+static int valid_pmbr(struct fdisk_context *cxt)
+{
+	int i, part = 0, ret = 0; /* invalid by default */
+	struct gpt_legacy_mbr *pmbr = NULL;
+	uint32_t sz_lba = 0;
+
+	if (!cxt->firstsector)
+		goto done;
+
+	pmbr = (struct gpt_legacy_mbr *) cxt->firstsector;
+
+	if (le16_to_cpu(pmbr->signature) != MSDOS_MBR_SIGNATURE)
+		goto done;
+
+	/* LBA of the GPT partition header */
+	if (pmbr->partition_record[0].starting_lba !=
+	    cpu_to_le32(GPT_PRIMARY_PARTITION_TABLE_LBA))
+		goto done;
+
+	/* seems like a valid MBR was found, check DOS primary partitions */
+	for (i = 0; i < 4; i++) {
+		if (pmbr->partition_record[i].os_type == EFI_PMBR_OSTYPE) {
+			/*
+			 * Ok, we at least know that there's a protective MBR,
+			 * now check if there are other partition types for
+			 * hybrid MBR.
+			 */
+			part = i;
+			ret = GPT_MBR_PROTECTIVE;
+			goto check_hybrid;
+		}
+	}
+
+	if (ret != GPT_MBR_PROTECTIVE)
+		goto done;
+check_hybrid:
+	for (i = 0 ; i < 4; i++) {
+		if ((pmbr->partition_record[i].os_type != EFI_PMBR_OSTYPE) &&
+		    (pmbr->partition_record[i].os_type != 0x00))
+			ret = GPT_MBR_HYBRID;
+	}
+
+	/*
+	 * Protective MBRs take up the lesser of the whole disk
+	 * or 2 TiB (32bit LBA), ignoring the rest of the disk.
+	 * Some partitioning programs, nonetheless, choose to set
+	 * the size to the maximum 32-bit limitation, disregarding
+	 * the disk size.
+	 *
+	 * Hybrid MBRs do not necessarily comply with this.
+	 *
+	 * Consider a bad value here to be a warning to support dd-ing
+	 * an image from a smaller disk to a bigger disk.
+	 */
+	if (ret == GPT_MBR_PROTECTIVE) {
+		sz_lba = le32_to_cpu(pmbr->partition_record[part].size_in_lba);
+		if (sz_lba != (uint32_t) cxt->total_sectors - 1 && sz_lba != 0xFFFFFFFF) {
+			fdisk_warnx(cxt, _("GPT PMBR size mismatch (%u != %u) "
+					   "will be corrected by w(rite)."),
+					sz_lba,
+					(uint32_t) cxt->total_sectors - 1);
+			fdisk_label_set_changed(cxt->label, 1);
+		}
+	}
+done:
+	return ret;
+}
+
+static uint64_t last_lba(struct fdisk_context *cxt)
+{
+	struct stat s;
+	uint64_t sectors = 0;
+
+	memset(&s, 0, sizeof(s));
+	if (fstat(cxt->dev_fd, &s) == -1) {
+		fdisk_warn(cxt, _("gpt: stat() failed"));
+		return 0;
+	}
+
+	if (S_ISBLK(s.st_mode))
+		sectors = cxt->total_sectors - 1;
+	else if (S_ISREG(s.st_mode))
+		sectors = ((uint64_t) s.st_size /
+			   (uint64_t) cxt->sector_size) - 1ULL;
+	else
+		fdisk_warnx(cxt, _("gpt: cannot handle files with mode %o"), s.st_mode);
+
+	DBG(LABEL, ul_debug("GPT last LBA: %ju", sectors));
+	return sectors;
+}
+
+static ssize_t read_lba(struct fdisk_context *cxt, uint64_t lba,
+			void *buffer, const size_t bytes)
+{
+	off_t offset = lba * cxt->sector_size;
+
+	if (lseek(cxt->dev_fd, offset, SEEK_SET) == (off_t) -1)
+		return -1;
+	return read(cxt->dev_fd, buffer, bytes) != bytes;
+}
+
+
+/* Returns the GPT entry array */
+static struct gpt_entry *gpt_read_entries(struct fdisk_context *cxt,
+					 struct gpt_header *header)
+{
+	ssize_t sz;
+	struct gpt_entry *ret = NULL;
+	off_t offset;
+
+	assert(cxt);
+	assert(header);
+
+	sz = le32_to_cpu(header->npartition_entries) *
+	     le32_to_cpu(header->sizeof_partition_entry);
+
+	ret = calloc(1, sz);
+	if (!ret)
+		return NULL;
+	offset = le64_to_cpu(header->partition_entry_lba) *
+		       cxt->sector_size;
+
+	if (offset != lseek(cxt->dev_fd, offset, SEEK_SET))
+		goto fail;
+	if (sz != read(cxt->dev_fd, ret, sz))
+		goto fail;
+
+	return ret;
+
+fail:
+	free(ret);
+	return NULL;
+}
+
+static inline uint32_t count_crc32(const unsigned char *buf, size_t len)
+{
+	return (crc32(~0L, buf, len) ^ ~0L);
+}
+
+/*
+ * Recompute header and partition array 32bit CRC checksums.
+ * This function does not fail - if there's corruption, then it
+ * will be reported when checksuming it again (ie: probing or verify).
+ */
+static void gpt_recompute_crc(struct gpt_header *header, struct gpt_entry *ents)
+{
+	uint32_t crc = 0;
+	size_t entry_sz = 0;
+
+	if (!header)
+		return;
+
+	/* header CRC */
+	header->crc32 = 0;
+	crc = count_crc32((unsigned char *) header, le32_to_cpu(header->size));
+	header->crc32 = cpu_to_le32(crc);
+
+	/* partition entry array CRC */
+	header->partition_entry_array_crc32 = 0;
+	entry_sz = le32_to_cpu(header->npartition_entries) *
+		le32_to_cpu(header->sizeof_partition_entry);
+
+	crc = count_crc32((unsigned char *) ents, entry_sz);
+	header->partition_entry_array_crc32 = cpu_to_le32(crc);
+}
+
+/*
+ * Compute the 32bit CRC checksum of the partition table header.
+ * Returns 1 if it is valid, otherwise 0.
+ */
+static int gpt_check_header_crc(struct gpt_header *header, struct gpt_entry *ents)
+{
+	uint32_t crc, orgcrc = le32_to_cpu(header->crc32);
+
+	header->crc32 = 0;
+	crc = count_crc32((unsigned char *) header, le32_to_cpu(header->size));
+	header->crc32 = cpu_to_le32(orgcrc);
+
+	if (crc == le32_to_cpu(header->crc32))
+		return 1;
+
+	/*
+	 * If we have checksum mismatch it may be due to stale data,
+	 * like a partition being added or deleted. Recompute the CRC again
+	 * and make sure this is not the case.
+	 */
+	if (ents) {
+		gpt_recompute_crc(header, ents);
+		orgcrc = le32_to_cpu(header->crc32);
+		header->crc32 = 0;
+		crc = count_crc32((unsigned char *) header, le32_to_cpu(header->size));
+		header->crc32 = cpu_to_le32(orgcrc);
+
+		return crc == le32_to_cpu(header->crc32);
+	}
+
+	return 0;
+}
+
+/*
+ * It initializes the partition entry array.
+ * Returns 1 if the checksum is valid, otherwise 0.
+ */
+static int gpt_check_entryarr_crc(struct gpt_header *header,
+				  struct gpt_entry *ents)
+{
+	int ret = 0;
+	ssize_t entry_sz;
+	uint32_t crc;
+
+	if (!header || !ents)
+		goto done;
+
+	entry_sz = le32_to_cpu(header->npartition_entries) *
+		   le32_to_cpu(header->sizeof_partition_entry);
+
+	if (!entry_sz)
+		goto done;
+
+	crc = count_crc32((unsigned char *) ents, entry_sz);
+	ret = (crc == le32_to_cpu(header->partition_entry_array_crc32));
+done:
+	return ret;
+}
+
+static int gpt_check_lba_sanity(struct fdisk_context *cxt, struct gpt_header *header)
+{
+	int ret = 0;
+	uint64_t lu, fu, lastlba = last_lba(cxt);
+
+	fu = le64_to_cpu(header->first_usable_lba);
+	lu = le64_to_cpu(header->last_usable_lba);
+
+	/* check if first and last usable LBA make sense */
+	if (lu < fu) {
+		DBG(LABEL, ul_debug("error: header last LBA is before first LBA"));
+		goto done;
+	}
+
+	/* check if first and last usable LBAs with the disk's last LBA */
+	if (fu > lastlba || lu > lastlba) {
+		DBG(LABEL, ul_debug("error: header LBAs are after the disk's last LBA"));
+		goto done;
+	}
+
+	/* the header has to be outside usable range */
+	if (fu < GPT_PRIMARY_PARTITION_TABLE_LBA &&
+	    GPT_PRIMARY_PARTITION_TABLE_LBA < lu) {
+		DBG(LABEL, ul_debug("error: header outside of usable range"));
+		goto done;
+	}
+
+	ret = 1; /* sane */
+done:
+	return ret;
+}
+
+/* Check if there is a valid header signature */
+static int gpt_check_signature(struct gpt_header *header)
+{
+	return header->signature == cpu_to_le64(GPT_HEADER_SIGNATURE);
+}
+
+/*
+ * Return the specified GPT Header, or NULL upon failure/invalid.
+ * Note that all tests must pass to ensure a valid header,
+ * we do not rely on only testing the signature for a valid probe.
+ */
+static struct gpt_header *gpt_read_header(struct fdisk_context *cxt,
+					  uint64_t lba,
+					  struct gpt_entry **_ents)
+{
+	struct gpt_header *header = NULL;
+	struct gpt_entry *ents = NULL;
+	uint32_t hsz;
+
+	if (!cxt)
+		return NULL;
+
+	header = calloc(1, sizeof(*header));
+	if (!header)
+		return NULL;
+
+	/* read and verify header */
+	if (read_lba(cxt, lba, header, sizeof(struct gpt_header)) != 0)
+		goto invalid;
+
+	if (!gpt_check_signature(header))
+		goto invalid;
+
+	if (!gpt_check_header_crc(header, NULL))
+		goto invalid;
+
+	/* read and verify entries */
+	ents = gpt_read_entries(cxt, header);
+	if (!ents)
+		goto invalid;
+
+	if (!gpt_check_entryarr_crc(header, ents))
+		goto invalid;
+
+	if (!gpt_check_lba_sanity(cxt, header))
+		goto invalid;
+
+	/* valid header must be at MyLBA */
+	if (le64_to_cpu(header->my_lba) != lba)
+		goto invalid;
+
+	/* make sure header size is between 92 and sector size bytes */
+	hsz = le32_to_cpu(header->size);
+	if (hsz < GPT_HEADER_MINSZ || hsz > cxt->sector_size)
+		goto invalid;
+
+	if (_ents)
+		*_ents = ents;
+	else
+		free(ents);
+
+	DBG(LABEL, ul_debug("found valid GPT Header on LBA %ju", lba));
+	return header;
+invalid:
+	free(header);
+	free(ents);
+
+	DBG(LABEL, ul_debug("read GPT Header on LBA %ju failed", lba));
+	return NULL;
+}
+
+
+static int gpt_locate_disklabel(struct fdisk_context *cxt, int n,
+		const char **name, off_t *offset, size_t *size)
+{
+	struct fdisk_gpt_label *gpt;
+
+	assert(cxt);
+
+	*name = NULL;
+	*offset = 0;
+	*size = 0;
+
+	switch (n) {
+	case 0:
+		*name = "PMBR";
+		*offset = 0;
+		*size = 512;
+		break;
+	case 1:
+		*name = _("GPT Header");
+		*offset = GPT_PRIMARY_PARTITION_TABLE_LBA * cxt->sector_size;
+		*size = sizeof(struct gpt_header);
+		break;
+	case 2:
+		*name = _("GPT Entries");
+		gpt = self_label(cxt);
+		*offset = le64_to_cpu(gpt->pheader->partition_entry_lba) * cxt->sector_size;
+		*size = le32_to_cpu(gpt->pheader->npartition_entries) *
+			 le32_to_cpu(gpt->pheader->sizeof_partition_entry);
+		break;
+	default:
+		return 1;			/* no more chunks */
+	}
+
+	return 0;
+}
+
+
+
+/*
+ * Returns the number of partitions that are in use.
+ */
+static unsigned partitions_in_use(struct gpt_header *header,
+				  struct gpt_entry *ents)
+{
+	uint32_t i, used = 0;
+
+	if (!header || ! ents)
+		return 0;
+
+	for (i = 0; i < le32_to_cpu(header->npartition_entries); i++)
+		if (!partition_unused(&ents[i]))
+			used++;
+	return used;
+}
+
+
+/*
+ * Check if a partition is too big for the disk (sectors).
+ * Returns the faulting partition number, otherwise 0.
+ */
+static uint32_t check_too_big_partitions(struct gpt_header *header,
+				   struct gpt_entry *ents, uint64_t sectors)
+{
+	uint32_t i;
+
+	for (i = 0; i < le32_to_cpu(header->npartition_entries); i++) {
+		if (partition_unused(&ents[i]))
+			continue;
+		if (gpt_partition_end(&ents[i]) >= sectors)
+			return i + 1;
+	}
+
+	return 0;
+}
+
+/*
+ * Check if a partition ends before it begins
+ * Returns the faulting partition number, otherwise 0.
+ */
+static uint32_t check_start_after_end_paritions(struct gpt_header *header,
+						struct gpt_entry *ents)
+{
+	uint32_t i;
+
+	for (i = 0; i < le32_to_cpu(header->npartition_entries); i++) {
+		if (partition_unused(&ents[i]))
+			continue;
+		if (gpt_partition_start(&ents[i]) > gpt_partition_end(&ents[i]))
+			return i + 1;
+	}
+
+	return 0;
+}
+
+/*
+ * Check if partition e1 overlaps with partition e2.
+ */
+static inline int partition_overlap(struct gpt_entry *e1, struct gpt_entry *e2)
+{
+	uint64_t start1 = gpt_partition_start(e1);
+	uint64_t end1   = gpt_partition_end(e1);
+	uint64_t start2 = gpt_partition_start(e2);
+	uint64_t end2   = gpt_partition_end(e2);
+
+	return (start1 && start2 && (start1 <= end2) != (end1 < start2));
+}
+
+/*
+ * Find any partitions that overlap.
+ */
+static uint32_t check_overlap_partitions(struct gpt_header *header,
+					 struct gpt_entry *ents)
+{
+	uint32_t i, j;
+
+	for (i = 0; i < le32_to_cpu(header->npartition_entries); i++)
+		for (j = 0; j < i; j++) {
+			if (partition_unused(&ents[i]) ||
+			    partition_unused(&ents[j]))
+				continue;
+			if (partition_overlap(&ents[i], &ents[j])) {
+				DBG(LABEL, ul_debug("GPT partitions overlap detected [%u vs. %u]", i, j));
+				return i + 1;
+			}
+		}
+
+	return 0;
+}
+
+/*
+ * Find the first available block after the starting point; returns 0 if
+ * there are no available blocks left, or error. From gdisk.
+ */
+static uint64_t find_first_available(struct gpt_header *header,
+				     struct gpt_entry *ents, uint64_t start)
+{
+	uint64_t first;
+	uint32_t i, first_moved = 0;
+
+	uint64_t fu, lu;
+
+	if (!header || !ents)
+		return 0;
+
+	fu = le64_to_cpu(header->first_usable_lba);
+	lu = le64_to_cpu(header->last_usable_lba);
+
+	/*
+	 * Begin from the specified starting point or from the first usable
+	 * LBA, whichever is greater...
+	 */
+	first = start < fu ? fu : start;
+
+	/*
+	 * Now search through all partitions; if first is within an
+	 * existing partition, move it to the next sector after that
+	 * partition and repeat. If first was moved, set firstMoved
+	 * flag; repeat until firstMoved is not set, so as to catch
+	 * cases where partitions are out of sequential order....
+	 */
+	do {
+		first_moved = 0;
+		for (i = 0; i < le32_to_cpu(header->npartition_entries); i++) {
+			if (partition_unused(&ents[i]))
+				continue;
+			if (first < gpt_partition_start(&ents[i]))
+				continue;
+			if (first <= gpt_partition_end(&ents[i])) {
+				first = gpt_partition_end(&ents[i]) + 1;
+				first_moved = 1;
+			}
+		}
+	} while (first_moved == 1);
+
+	if (first > lu)
+		first = 0;
+
+	return first;
+}
+
+
+/* Returns last available sector in the free space pointed to by start. From gdisk. */
+static uint64_t find_last_free(struct gpt_header *header,
+			       struct gpt_entry *ents, uint64_t start)
+{
+	uint32_t i;
+	uint64_t nearest_start;
+
+	if (!header || !ents)
+		return 0;
+
+	nearest_start = le64_to_cpu(header->last_usable_lba);
+
+	for (i = 0; i < le32_to_cpu(header->npartition_entries); i++) {
+		uint64_t ps = gpt_partition_start(&ents[i]);
+
+		if (nearest_start > ps && ps > start)
+			nearest_start = ps - 1;
+	}
+
+	return nearest_start;
+}
+
+/* Returns the last free sector on the disk. From gdisk. */
+static uint64_t find_last_free_sector(struct gpt_header *header,
+				      struct gpt_entry *ents)
+{
+	uint32_t i, last_moved;
+	uint64_t last = 0;
+
+	if (!header || !ents)
+		goto done;
+
+	/* start by assuming the last usable LBA is available */
+	last = le64_to_cpu(header->last_usable_lba);
+	do {
+		last_moved = 0;
+		for (i = 0; i < le32_to_cpu(header->npartition_entries); i++) {
+			if ((last >= gpt_partition_start(&ents[i])) &&
+			    (last <= gpt_partition_end(&ents[i]))) {
+				last = gpt_partition_start(&ents[i]) - 1;
+				last_moved = 1;
+			}
+		}
+	} while (last_moved == 1);
+done:
+	return last;
+}
+
+/*
+ * Finds the first available sector in the largest block of unallocated
+ * space on the disk. Returns 0 if there are no available blocks left.
+ * From gdisk.
+ */
+static uint64_t find_first_in_largest(struct gpt_header *header,
+				      struct gpt_entry *ents)
+{
+	uint64_t start = 0, first_sect, last_sect;
+	uint64_t segment_size, selected_size = 0, selected_segment = 0;
+
+	if (!header || !ents)
+		goto done;
+
+	do {
+		first_sect =  find_first_available(header, ents, start);
+		if (first_sect != 0) {
+			last_sect = find_last_free(header, ents, first_sect);
+			segment_size = last_sect - first_sect + 1;
+
+			if (segment_size > selected_size) {
+				selected_size = segment_size;
+				selected_segment = first_sect;
+			}
+			start = last_sect + 1;
+		}
+	} while (first_sect != 0);
+
+done:
+	return selected_segment;
+}
+
+/*
+ * Find the total number of free sectors, the number of segments in which
+ * they reside, and the size of the largest of those segments. From gdisk.
+ */
+static uint64_t get_free_sectors(struct fdisk_context *cxt, struct gpt_header *header,
+				 struct gpt_entry *ents, uint32_t *nsegments,
+				 uint64_t *largest_segment)
+{
+	uint32_t num = 0;
+	uint64_t first_sect, last_sect;
+	uint64_t largest_seg = 0, segment_sz;
+	uint64_t totfound = 0, start = 0; /* starting point for each search */
+
+	if (!cxt->total_sectors)
+		goto done;
+
+	do {
+		first_sect = find_first_available(header, ents, start);
+		if (first_sect) {
+			last_sect = find_last_free(header, ents, first_sect);
+			segment_sz = last_sect - first_sect + 1;
+
+			if (segment_sz > largest_seg)
+				largest_seg = segment_sz;
+			totfound += segment_sz;
+			num++;
+			start = last_sect + 1;
+		}
+	} while (first_sect);
+
+done:
+	if (nsegments)
+		*nsegments = num;
+	if (largest_segment)
+		*largest_segment = largest_seg;
+
+	return totfound;
+}
+
+static int gpt_probe_label(struct fdisk_context *cxt)
+{
+	int mbr_type;
+	struct fdisk_gpt_label *gpt;
+
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, GPT));
+
+	gpt = self_label(cxt);
+
+	/* TODO: it would be nice to support scenario when GPT headers are OK,
+	 *       but PMBR is corrupt */
+	mbr_type = valid_pmbr(cxt);
+	if (!mbr_type)
+		goto failed;
+
+	DBG(LABEL, ul_debug("found a %s MBR", mbr_type == GPT_MBR_PROTECTIVE ?
+			    "protective" : "hybrid"));
+
+	/* primary header */
+	gpt->pheader = gpt_read_header(cxt, GPT_PRIMARY_PARTITION_TABLE_LBA,
+				       &gpt->ents);
+
+	if (gpt->pheader)
+		/* primary OK, try backup from alternative LBA */
+		gpt->bheader = gpt_read_header(cxt,
+					le64_to_cpu(gpt->pheader->alternative_lba),
+					NULL);
+	else
+		/* primary corrupted -- try last LBA */
+		gpt->bheader = gpt_read_header(cxt, last_lba(cxt), &gpt->ents);
+
+	if (!gpt->pheader && !gpt->bheader)
+		goto failed;
+
+	/* primary OK, backup corrupted -- recovery */
+	if (gpt->pheader && !gpt->bheader) {
+		fdisk_warnx(cxt, _("The backup GPT table is corrupt, but the "
+				  "primary appears OK, so that will be used."));
+		gpt->bheader = gpt_copy_header(cxt, gpt->pheader);
+		if (!gpt->bheader)
+			goto failed;
+		gpt_recompute_crc(gpt->bheader, gpt->ents);
+
+	/* primary corrupted, backup OK -- recovery */
+	} else if (!gpt->pheader && gpt->bheader) {
+		fdisk_warnx(cxt, _("The primary GPT table is corrupt, but the "
+				  "backup appears OK, so that will be used."));
+		gpt->pheader = gpt_copy_header(cxt, gpt->bheader);
+		if (!gpt->pheader)
+			goto failed;
+		gpt_recompute_crc(gpt->pheader, gpt->ents);
+	}
+
+	cxt->label->nparts_max = le32_to_cpu(gpt->pheader->npartition_entries);
+	cxt->label->nparts_cur = partitions_in_use(gpt->pheader, gpt->ents);
+	return 1;
+failed:
+	DBG(LABEL, ul_debug("GPT probe failed"));
+	gpt_deinit(cxt->label);
+	return 0;
+}
+
+/*
+ * Stolen from libblkid - can be removed once partition semantics
+ * are added to the fdisk API.
+ */
+static char *encode_to_utf8(unsigned char *src, size_t count)
+{
+	uint16_t c;
+	char *dest;
+	size_t i, j, len = count;
+
+	dest = calloc(1, count);
+	if (!dest)
+		return NULL;
+
+	for (j = i = 0; i + 2 <= count; i += 2) {
+		/* always little endian */
+		c = (src[i+1] << 8) | src[i];
+		if (c == 0) {
+			dest[j] = '\0';
+			break;
+		} else if (c < 0x80) {
+			if (j+1 >= len)
+				break;
+			dest[j++] = (uint8_t) c;
+		} else if (c < 0x800) {
+			if (j+2 >= len)
+				break;
+			dest[j++] = (uint8_t) (0xc0 | (c >> 6));
+			dest[j++] = (uint8_t) (0x80 | (c & 0x3f));
+		} else {
+			if (j+3 >= len)
+				break;
+			dest[j++] = (uint8_t) (0xe0 | (c >> 12));
+			dest[j++] = (uint8_t) (0x80 | ((c >> 6) & 0x3f));
+			dest[j++] = (uint8_t) (0x80 | (c & 0x3f));
+		}
+	}
+	dest[j] = '\0';
+
+	return dest;
+}
+
+static int gpt_entry_attrs_to_string(struct gpt_entry *e, char **res)
+{
+	unsigned int n, count = 0;
+	size_t l;
+	char *bits, *p;
+	uint64_t attrs;
+
+	assert(e);
+	assert(res);
+
+	*res = NULL;
+	attrs = le64_to_cpu(e->attrs);
+	if (!attrs)
+		return 0;	/* no attributes at all */
+
+	bits = (char *) &attrs;
+
+	/* Note that sizeof() is correct here, we need separators between
+	 * the strings so also count \0 is correct */
+	*res = calloc(1, sizeof(GPT_ATTRSTR_NOBLOCK) +
+			 sizeof(GPT_ATTRSTR_REQ) +
+			 sizeof(GPT_ATTRSTR_LEGACY) +
+			 sizeof("GUID:") + (GPT_ATTRBIT_GUID_COUNT * 3));
+	if (!*res)
+		return -errno;
+
+	p = *res;
+	if (isset(bits, GPT_ATTRBIT_REQ)) {
+		memcpy(p, GPT_ATTRSTR_REQ, (l = sizeof(GPT_ATTRSTR_REQ)));
+		p += l - 1;
+	}
+	if (isset(bits, GPT_ATTRBIT_NOBLOCK)) {
+		if (p > *res)
+			*p++ = ' ';
+		memcpy(p, GPT_ATTRSTR_NOBLOCK, (l = sizeof(GPT_ATTRSTR_NOBLOCK)));
+		p += l - 1;
+	}
+	if (isset(bits, GPT_ATTRBIT_LEGACY)) {
+		if (p > *res)
+			*p++ = ' ';
+		memcpy(p, GPT_ATTRSTR_LEGACY, (l = sizeof(GPT_ATTRSTR_LEGACY)));
+		p += l - 1;
+	}
+
+	for (n = GPT_ATTRBIT_GUID_FIRST;
+	     n < GPT_ATTRBIT_GUID_FIRST + GPT_ATTRBIT_GUID_COUNT; n++) {
+
+		if (!isset(bits, n))
+			continue;
+		if (!count) {
+			if (p > *res)
+				*p++ = ' ';
+			p += sprintf(p, "GUID:%u", n);
+		} else
+			p += sprintf(p, ",%u", n);
+		count++;
+	}
+
+	return 0;
+}
+
+static int gpt_entry_attrs_from_string(
+			struct fdisk_context *cxt,
+			struct gpt_entry *e,
+			const char *str)
+{
+	const char *p = str;
+	uint64_t attrs = 0;
+	char *bits;
+
+	assert(e);
+	assert(p);
+
+	DBG(LABEL, ul_debug("GPT: parsing string attributes '%s'", p));
+
+	bits = (char *) &attrs;
+
+	while (p && *p) {
+		int bit = -1;
+
+		while (isblank(*p)) p++;
+		if (!*p)
+			break;
+
+		DBG(LABEL, ul_debug(" parsing item '%s'", p));
+
+		if (strncmp(p, "GUID:", 5) == 0) {
+			p += 5;
+			continue;
+		} else if (strncmp(p, GPT_ATTRSTR_REQ,
+					sizeof(GPT_ATTRSTR_REQ) - 1) == 0) {
+			bit = GPT_ATTRBIT_REQ;
+			p += sizeof(GPT_ATTRSTR_REQ) - 1;
+		} else if (strncmp(p, GPT_ATTRSTR_LEGACY,
+					sizeof(GPT_ATTRSTR_LEGACY) - 1) == 0) {
+			bit = GPT_ATTRBIT_LEGACY;
+			p += sizeof(GPT_ATTRSTR_LEGACY) - 1;
+		} else if (strncmp(p, GPT_ATTRSTR_NOBLOCK,
+					sizeof(GPT_ATTRSTR_NOBLOCK) - 1) == 0) {
+			bit = GPT_ATTRBIT_NOBLOCK;
+			p += sizeof(GPT_ATTRSTR_NOBLOCK) - 1;
+		} else if (isdigit((unsigned int) *p)) {
+			char *end = NULL;
+
+			errno = 0;
+			bit = strtol(p, &end, 0);
+			if (errno || !end || end == str
+			    || bit < GPT_ATTRBIT_GUID_FIRST
+			    || bit >= GPT_ATTRBIT_GUID_FIRST + GPT_ATTRBIT_GUID_COUNT)
+				bit = -1;
+			else
+				p = end;
+		}
+
+		if (bit < 0) {
+			fdisk_warnx(cxt, _("unssuported GPT attribute bit '%s'"), p);
+			return -EINVAL;
+		}
+
+		setbit(bits, bit);
+
+		while (isblank(*p)) p++;
+		if (*p == ',')
+			p++;
+	}
+
+	e->attrs = cpu_to_le64(attrs);
+	return 0;
+}
+
+static int gpt_get_partition(struct fdisk_context *cxt, size_t n,
+			     struct fdisk_partition *pa)
+{
+	struct fdisk_gpt_label *gpt;
+	struct gpt_entry *e;
+	char u_str[37];
+	int rc = 0;
+
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, GPT));
+
+	gpt = self_label(cxt);
+
+	if ((uint32_t) n >= le32_to_cpu(gpt->pheader->npartition_entries))
+		return -EINVAL;
+
+	gpt = self_label(cxt);
+	e = &gpt->ents[n];
+
+	pa->used = !partition_unused(e) || gpt_partition_start(e);
+	if (!pa->used)
+		return 0;
+
+	pa->start = gpt_partition_start(e);
+	pa->size = gpt_partition_size(e);
+	pa->type = gpt_partition_parttype(cxt, e);
+
+	if (guid_to_string(&e->partition_guid, u_str)) {
+		pa->uuid = strdup(u_str);
+		if (!pa->uuid) {
+			rc = -errno;
+			goto done;
+		}
+	} else
+		pa->uuid = NULL;
+
+	rc = gpt_entry_attrs_to_string(e, &pa->attrs);
+	if (rc)
+		goto done;
+
+	pa->name = encode_to_utf8((unsigned char *)e->name, sizeof(e->name));
+	return 0;
+done:
+	fdisk_reset_partition(pa);
+	return rc;
+}
+
+
+static int gpt_set_partition(struct fdisk_context *cxt, size_t n,
+			     struct fdisk_partition *pa)
+{
+	struct fdisk_gpt_label *gpt;
+	struct gpt_entry *e;
+	int rc = 0;
+	uint64_t start, end;
+
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, GPT));
+
+	gpt = self_label(cxt);
+
+	if ((uint32_t) n >= le32_to_cpu(gpt->pheader->npartition_entries))
+		return -EINVAL;
+
+	FDISK_INIT_UNDEF(start);
+	FDISK_INIT_UNDEF(end);
+
+	gpt = self_label(cxt);
+	e = &gpt->ents[n];
+
+	if (pa->uuid) {
+		char new_u[37], old_u[37];
+
+		guid_to_string(&e->partition_guid, old_u);
+		rc = gpt_entry_set_uuid(e, pa->uuid);
+		if (rc)
+			return rc;
+		guid_to_string(&e->partition_guid, new_u);
+		fdisk_info(cxt, _("Partition UUID changed from %s to %s."),
+			old_u, new_u);
+	}
+
+	if (pa->name) {
+		char *old = encode_to_utf8((unsigned char *)e->name, sizeof(e->name));
+		gpt_entry_set_name(e, pa->name);
+
+		fdisk_info(cxt, _("Partition name changed from '%s' to '%.*s'."),
+			old, (int) GPT_PART_NAME_LEN, pa->name);
+		free(old);
+	}
+
+	if (pa->type && pa->type->typestr) {
+		struct gpt_guid typeid;
+
+		rc = string_to_guid(pa->type->typestr, &typeid);
+		if (rc)
+			return rc;
+		gpt_entry_set_type(e, &typeid);
+	}
+	if (pa->attrs) {
+		rc = gpt_entry_attrs_from_string(cxt, e, pa->attrs);
+		if (rc)
+			return rc;
+	}
+
+	if (fdisk_partition_has_start(pa))
+		start = pa->start;
+	if (fdisk_partition_has_size(pa))
+		end = gpt_partition_start(e) + pa->size - 1ULL;
+
+	if (pa->end_follow_default) {
+		/* enlarge */
+		if (!FDISK_IS_UNDEF(start))
+			start = gpt_partition_start(e);
+		end = find_last_free(gpt->bheader, gpt->ents, start);
+		if (!end)
+			FDISK_INIT_UNDEF(end);
+	}
+
+	if (!FDISK_IS_UNDEF(start))
+		e->lba_start = cpu_to_le64(start);
+	if (!FDISK_IS_UNDEF(end))
+		e->lba_end = cpu_to_le64(end);
+
+	gpt_recompute_crc(gpt->pheader, gpt->ents);
+	gpt_recompute_crc(gpt->bheader, gpt->ents);
+
+	fdisk_label_set_changed(cxt->label, 1);
+	return rc;
+}
+
+
+/*
+ * List label partitions.
+ */
+static int gpt_list_disklabel(struct fdisk_context *cxt)
+{
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, GPT));
+
+	if (fdisk_is_details(cxt)) {
+		struct gpt_header *h = self_label(cxt)->pheader;
+
+		fdisk_info(cxt, _("First LBA: %ju"), h->first_usable_lba);
+		fdisk_info(cxt, _("Last LBA: %ju"), h->last_usable_lba);
+		/* TRANSLATORS: The LBA (Logical Block Address) of the backup GPT header. */
+		fdisk_info(cxt, _("Alternative LBA: %ju"), h->alternative_lba);
+		/* TRANSLATORS: The start of the array of partition entries. */
+		fdisk_info(cxt, _("Partition entries LBA: %ju"), h->partition_entry_lba);
+		fdisk_info(cxt, _("Allocated partition entries: %u"), h->npartition_entries);
+	}
+
+	return 0;
+}
+
+/*
+ * Write partitions.
+ * Returns 0 on success, or corresponding error otherwise.
+ */
+static int gpt_write_partitions(struct fdisk_context *cxt,
+				struct gpt_header *header, struct gpt_entry *ents)
+{
+	off_t offset = le64_to_cpu(header->partition_entry_lba) * cxt->sector_size;
+	uint32_t nparts = le32_to_cpu(header->npartition_entries);
+	uint32_t totwrite = nparts * le32_to_cpu(header->sizeof_partition_entry);
+	ssize_t rc;
+
+	if (offset != lseek(cxt->dev_fd, offset, SEEK_SET))
+		goto fail;
+
+	rc = write(cxt->dev_fd, ents, totwrite);
+	if (rc > 0 && totwrite == (uint32_t) rc)
+		return 0;
+fail:
+	return -errno;
+}
+
+/*
+ * Write a GPT header to a specified LBA
+ * Returns 0 on success, or corresponding error otherwise.
+ */
+static int gpt_write_header(struct fdisk_context *cxt,
+			    struct gpt_header *header, uint64_t lba)
+{
+	off_t offset = lba * cxt->sector_size;
+
+	if (offset != lseek(cxt->dev_fd, offset, SEEK_SET))
+		goto fail;
+	if (cxt->sector_size ==
+	    (size_t) write(cxt->dev_fd, header, cxt->sector_size))
+		return 0;
+fail:
+	return -errno;
+}
+
+/*
+ * Write the protective MBR.
+ * Returns 0 on success, or corresponding error otherwise.
+ */
+static int gpt_write_pmbr(struct fdisk_context *cxt)
+{
+	off_t offset;
+	struct gpt_legacy_mbr *pmbr = NULL;
+
+	assert(cxt);
+	assert(cxt->firstsector);
+
+	pmbr = (struct gpt_legacy_mbr *) cxt->firstsector;
+
+	/* zero out the legacy partitions */
+	memset(pmbr->partition_record, 0, sizeof(pmbr->partition_record));
+
+	pmbr->signature = cpu_to_le16(MSDOS_MBR_SIGNATURE);
+	pmbr->partition_record[0].os_type      = EFI_PMBR_OSTYPE;
+	pmbr->partition_record[0].start_sector = 1;
+	pmbr->partition_record[0].end_head     = 0xFE;
+	pmbr->partition_record[0].end_sector   = 0xFF;
+	pmbr->partition_record[0].end_track    = 0xFF;
+	pmbr->partition_record[0].starting_lba = cpu_to_le32(1);
+
+	/*
+	 * Set size_in_lba to the size of the disk minus one. If the size of the disk
+	 * is too large to be represented by a 32bit LBA (2Tb), set it to 0xFFFFFFFF.
+	 */
+	if (cxt->total_sectors - 1 > 0xFFFFFFFFULL)
+		pmbr->partition_record[0].size_in_lba = cpu_to_le32(0xFFFFFFFF);
+	else
+		pmbr->partition_record[0].size_in_lba =
+			cpu_to_le32(cxt->total_sectors - 1UL);
+
+	offset = GPT_PMBR_LBA * cxt->sector_size;
+	if (offset != lseek(cxt->dev_fd, offset, SEEK_SET))
+		goto fail;
+
+	/* pMBR covers the first sector (LBA) of the disk */
+	if (write_all(cxt->dev_fd, pmbr, cxt->sector_size))
+		goto fail;
+	return 0;
+fail:
+	return -errno;
+}
+
+/*
+ * Writes in-memory GPT and pMBR data to disk.
+ * Returns 0 if successful write, otherwise, a corresponding error.
+ * Any indication of error will abort the operation.
+ */
+static int gpt_write_disklabel(struct fdisk_context *cxt)
+{
+	struct fdisk_gpt_label *gpt;
+	int mbr_type;
+
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, GPT));
+
+	gpt = self_label(cxt);
+	mbr_type = valid_pmbr(cxt);
+
+	/* check that disk is big enough to handle the backup header */
+	if (le64_to_cpu(gpt->pheader->alternative_lba) > cxt->total_sectors)
+		goto err0;
+
+	/* check that the backup header is properly placed */
+	if (le64_to_cpu(gpt->pheader->alternative_lba) < cxt->total_sectors - 1)
+		/* TODO: correct this (with user authorization) and write */
+		goto err0;
+
+	if (check_overlap_partitions(gpt->pheader, gpt->ents))
+		goto err0;
+
+	/* recompute CRCs for both headers */
+	gpt_recompute_crc(gpt->pheader, gpt->ents);
+	gpt_recompute_crc(gpt->bheader, gpt->ents);
+
+	/*
+	 * UEFI requires writing in this specific order:
+	 *   1) backup partition tables
+	 *   2) backup GPT header
+	 *   3) primary partition tables
+	 *   4) primary GPT header
+	 *   5) protective MBR
+	 *
+	 * If any write fails, we abort the rest.
+	 */
+	if (gpt_write_partitions(cxt, gpt->bheader, gpt->ents) != 0)
+		goto err1;
+	if (gpt_write_header(cxt, gpt->bheader,
+			     le64_to_cpu(gpt->pheader->alternative_lba)) != 0)
+		goto err1;
+	if (gpt_write_partitions(cxt, gpt->pheader, gpt->ents) != 0)
+		goto err1;
+	if (gpt_write_header(cxt, gpt->pheader, GPT_PRIMARY_PARTITION_TABLE_LBA) != 0)
+		goto err1;
+
+	if (mbr_type == GPT_MBR_HYBRID)
+		fdisk_warnx(cxt, _("The device contains hybrid MBR -- writing GPT only. "
+				   "You have to sync the MBR manually."));
+	else if (gpt_write_pmbr(cxt) != 0)
+		goto err1;
+
+	DBG(LABEL, ul_debug("GPT write success"));
+	return 0;
+err0:
+	DBG(LABEL, ul_debug("GPT write failed: incorrect input"));
+	errno = EINVAL;
+	return -EINVAL;
+err1:
+	DBG(LABEL, ul_debug("GPT write failed: %m"));
+	return -errno;
+}
+
+/*
+ * Verify data integrity and report any found problems for:
+ *   - primary and backup header validations
+ *   - paritition validations
+ */
+static int gpt_verify_disklabel(struct fdisk_context *cxt)
+{
+	int nerror = 0;
+	unsigned int ptnum;
+	struct fdisk_gpt_label *gpt;
+
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, GPT));
+
+	gpt = self_label(cxt);
+
+	if (!gpt || !gpt->bheader) {
+		nerror++;
+		fdisk_warnx(cxt, _("Disk does not contain a valid backup header."));
+	}
+
+	if (!gpt_check_header_crc(gpt->pheader, gpt->ents)) {
+		nerror++;
+		fdisk_warnx(cxt, _("Invalid primary header CRC checksum."));
+	}
+	if (gpt->bheader && !gpt_check_header_crc(gpt->bheader, gpt->ents)) {
+		nerror++;
+		fdisk_warnx(cxt, _("Invalid backup header CRC checksum."));
+	}
+
+	if (!gpt_check_entryarr_crc(gpt->pheader, gpt->ents)) {
+		nerror++;
+		fdisk_warnx(cxt, _("Invalid partition entry checksum."));
+	}
+
+	if (!gpt_check_lba_sanity(cxt, gpt->pheader)) {
+		nerror++;
+		fdisk_warnx(cxt, _("Invalid primary header LBA sanity checks."));
+	}
+	if (gpt->bheader && !gpt_check_lba_sanity(cxt, gpt->bheader)) {
+		nerror++;
+		fdisk_warnx(cxt, _("Invalid backup header LBA sanity checks."));
+	}
+
+	if (le64_to_cpu(gpt->pheader->my_lba) != GPT_PRIMARY_PARTITION_TABLE_LBA) {
+		nerror++;
+		fdisk_warnx(cxt, _("MyLBA mismatch with real position at primary header."));
+	}
+	if (gpt->bheader && le64_to_cpu(gpt->bheader->my_lba) != last_lba(cxt)) {
+		nerror++;
+		fdisk_warnx(cxt, _("MyLBA mismatch with real position at backup header."));
+
+	}
+	if (le64_to_cpu(gpt->pheader->alternative_lba) >= cxt->total_sectors) {
+		nerror++;
+		fdisk_warnx(cxt, _("Disk is too small to hold all data."));
+	}
+
+	/*
+	 * if the GPT is the primary table, check the alternateLBA
+	 * to see if it is a valid GPT
+	 */
+	if (gpt->bheader && (le64_to_cpu(gpt->pheader->my_lba) !=
+			     le64_to_cpu(gpt->bheader->alternative_lba))) {
+		nerror++;
+		fdisk_warnx(cxt, _("Primary and backup header mismatch."));
+	}
+
+	ptnum = check_overlap_partitions(gpt->pheader, gpt->ents);
+	if (ptnum) {
+		nerror++;
+		fdisk_warnx(cxt, _("Partition %u overlaps with partition %u."),
+				ptnum, ptnum+1);
+	}
+
+	ptnum = check_too_big_partitions(gpt->pheader, gpt->ents, cxt->total_sectors);
+	if (ptnum) {
+		nerror++;
+		fdisk_warnx(cxt, _("Partition %u is too big for the disk."),
+				ptnum);
+	}
+
+	ptnum = check_start_after_end_paritions(gpt->pheader, gpt->ents);
+	if (ptnum) {
+		nerror++;
+		fdisk_warnx(cxt, _("Partition %u ends before it starts."),
+				ptnum);
+	}
+
+	if (!nerror) { /* yay :-) */
+		uint32_t nsegments = 0;
+		uint64_t free_sectors = 0, largest_segment = 0;
+		char *strsz = NULL;
+
+		fdisk_info(cxt, _("No errors detected."));
+		fdisk_info(cxt, _("Header version: %s"), gpt_get_header_revstr(gpt->pheader));
+		fdisk_info(cxt, _("Using %u out of %d partitions."),
+		       partitions_in_use(gpt->pheader, gpt->ents),
+		       le32_to_cpu(gpt->pheader->npartition_entries));
+
+		free_sectors = get_free_sectors(cxt, gpt->pheader, gpt->ents,
+						&nsegments, &largest_segment);
+		if (largest_segment)
+			strsz = size_to_human_string(SIZE_SUFFIX_SPACE | SIZE_SUFFIX_3LETTER,
+					largest_segment * cxt->sector_size);
+
+		fdisk_info(cxt,
+			   P_("A total of %ju free sectors is available in %u segment.",
+			      "A total of %ju free sectors is available in %u segments "
+			      "(the largest is %s).", nsegments),
+			   free_sectors, nsegments, strsz);
+		free(strsz);
+
+	} else
+		fdisk_warnx(cxt,
+			P_("%d error detected.", "%d errors detected.", nerror),
+			nerror);
+
+	return 0;
+}
+
+/* Delete a single GPT partition, specified by partnum. */
+static int gpt_delete_partition(struct fdisk_context *cxt,
+				size_t partnum)
+{
+	struct fdisk_gpt_label *gpt;
+
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, GPT));
+
+	gpt = self_label(cxt);
+
+	if (partnum >= cxt->label->nparts_max
+	    ||  partition_unused(&gpt->ents[partnum]))
+		return -EINVAL;
+
+	/* hasta la vista, baby! */
+	memset(&gpt->ents[partnum], 0, sizeof(struct gpt_entry));
+	if (!partition_unused(&gpt->ents[partnum]))
+		return -EINVAL;
+	else {
+		gpt_recompute_crc(gpt->pheader, gpt->ents);
+		gpt_recompute_crc(gpt->bheader, gpt->ents);
+		cxt->label->nparts_cur--;
+		fdisk_label_set_changed(cxt->label, 1);
+	}
+
+	return 0;
+}
+
+
+/* Performs logical checks to add a new partition entry */
+static int gpt_add_partition(
+		struct fdisk_context *cxt,
+		struct fdisk_partition *pa,
+		size_t *partno)
+{
+	uint64_t user_f, user_l;	/* user input ranges for first and last sectors */
+	uint64_t disk_f, disk_l;	/* first and last available sector ranges on device*/
+	uint64_t dflt_f, dflt_l;	/* largest segment (default) */
+	struct gpt_guid typeid;
+	struct fdisk_gpt_label *gpt;
+	struct gpt_header *pheader;
+	struct gpt_entry *e, *ents;
+	struct fdisk_ask *ask = NULL;
+	size_t partnum;
+	int rc;
+
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, GPT));
+
+	gpt = self_label(cxt);
+	pheader = gpt->pheader;
+	ents = gpt->ents;
+
+	rc = fdisk_partition_next_partno(pa, cxt, &partnum);
+	if (rc) {
+		DBG(LABEL, ul_debug("GPT failed to get next partno"));
+		return rc;
+	}
+	if (!partition_unused(&ents[partnum])) {
+		fdisk_warnx(cxt, _("Partition %zu is already defined.  "
+			           "Delete it before re-adding it."), partnum +1);
+		return -ERANGE;
+	}
+	if (le32_to_cpu(pheader->npartition_entries) ==
+			partitions_in_use(pheader, ents)) {
+		fdisk_warnx(cxt, _("All partitions are already in use."));
+		return -ENOSPC;
+	}
+	if (!get_free_sectors(cxt, pheader, ents, NULL, NULL)) {
+		fdisk_warnx(cxt, _("No free sectors available."));
+		return -ENOSPC;
+	}
+
+	string_to_guid(pa && pa->type && pa->type->typestr ?
+				pa->type->typestr:
+				GPT_DEFAULT_ENTRY_TYPE, &typeid);
+
+	disk_f = find_first_available(pheader, ents, pheader->first_usable_lba);
+
+	/* if first sector no explicitly defined then ignore small gaps before
+	 * the first partition */
+	if ((!pa || !fdisk_partition_has_start(pa))
+	    && !partition_unused(&ents[0])
+	    && disk_f < gpt_partition_start(&ents[0])) {
+
+		do {
+			uint64_t x;
+			DBG(LABEL, ul_debug("testing first sector %ju", disk_f));
+			disk_f = find_first_available(pheader, ents, disk_f);
+			if (!disk_f)
+				break;
+			x = find_last_free(pheader, ents, disk_f);
+			if (x - disk_f >= cxt->grain / cxt->sector_size)
+				break;
+			DBG(LABEL, ul_debug("first sector %ju addresses to small space, continue...", disk_f));
+			disk_f = x + 1;
+		} while(1);
+
+		if (disk_f == 0)
+			disk_f = find_first_available(pheader, ents, pheader->first_usable_lba);
+	}
+
+	disk_l = find_last_free_sector(pheader, ents);
+
+	/* the default is the largest free space */
+	dflt_f = find_first_in_largest(pheader, ents);
+	dflt_l = find_last_free(pheader, ents, dflt_f);
+
+	/* align the default in range <dflt_f,dflt_l>*/
+	dflt_f = fdisk_align_lba_in_range(cxt, dflt_f, dflt_f, dflt_l);
+
+	/* first sector */
+	if (pa && pa->start_follow_default) {
+		user_f = dflt_f;
+
+	} else if (pa && fdisk_partition_has_start(pa)) {
+		DBG(LABEL, ul_debug("first sector defined: %ju", pa->start));
+		if (pa->start != find_first_available(pheader, ents, pa->start)) {
+			fdisk_warnx(cxt, _("Sector %ju already used."), pa->start);
+			return -ERANGE;
+		}
+		user_f = pa->start;
+	} else {
+		/*  ask by dialog */
+		for (;;) {
+			if (!ask)
+				ask = fdisk_new_ask();
+			else
+				fdisk_reset_ask(ask);
+
+			/* First sector */
+			fdisk_ask_set_query(ask, _("First sector"));
+			fdisk_ask_set_type(ask, FDISK_ASKTYPE_NUMBER);
+			fdisk_ask_number_set_low(ask,     disk_f);	/* minimal */
+			fdisk_ask_number_set_default(ask, dflt_f);	/* default */
+			fdisk_ask_number_set_high(ask,    disk_l);	/* maximal */
+
+			rc = fdisk_do_ask(cxt, ask);
+			if (rc)
+				goto done;
+
+			user_f = fdisk_ask_number_get_result(ask);
+			if (user_f != find_first_available(pheader, ents, user_f)) {
+				fdisk_warnx(cxt, _("Sector %ju already used."), user_f);
+				continue;
+			}
+			break;
+		}
+	}
+
+
+	/* Last sector */
+	dflt_l = find_last_free(pheader, ents, user_f);
+
+	if (pa && pa->end_follow_default) {
+		user_l = dflt_l;
+
+	} else if (pa && fdisk_partition_has_size(pa)) {
+		user_l = user_f + pa->size - 1;
+		DBG(LABEL, ul_debug("size defined: %ju, end: %ju (last possible: %ju)",
+					pa->size, user_l, dflt_l));
+		if (user_l != dflt_l && !pa->size_explicit)
+			user_l = fdisk_align_lba_in_range(cxt, user_l, user_f, dflt_l) - 1;
+
+	} else {
+		for (;;) {
+			if (!ask)
+				ask = fdisk_new_ask();
+			else
+				fdisk_reset_ask(ask);
+
+			fdisk_ask_set_query(ask, _("Last sector, +sectors or +size{K,M,G,T,P}"));
+			fdisk_ask_set_type(ask, FDISK_ASKTYPE_OFFSET);
+			fdisk_ask_number_set_low(ask,     user_f);	/* minimal */
+			fdisk_ask_number_set_default(ask, dflt_l);	/* default */
+			fdisk_ask_number_set_high(ask,    dflt_l);	/* maximal */
+			fdisk_ask_number_set_base(ask,    user_f);	/* base for relative input */
+			fdisk_ask_number_set_unit(ask,    cxt->sector_size);
+
+			rc = fdisk_do_ask(cxt, ask);
+			if (rc)
+				goto done;
+
+			user_l = fdisk_ask_number_get_result(ask);
+			if (fdisk_ask_number_is_relative(ask)) {
+				user_l = fdisk_align_lba_in_range(cxt, user_l, user_f, dflt_l) - 1;
+
+				/* no space for anything useful, use all space
+				if (user_l + (cxt->grain / cxt->sector_size) > dflt_l)
+					user_l = dflt_l;
+				*/
+			}
+
+			if (user_l > user_f && user_l <= disk_l)
+				break;
+		}
+	}
+
+
+	if (user_f > user_l || partnum >= cxt->label->nparts_max) {
+		fdisk_warnx(cxt, _("Could not create partition %zu"), partnum + 1);
+		rc = -EINVAL;
+		goto done;
+	}
+
+	assert(!FDISK_IS_UNDEF(user_l));
+	assert(!FDISK_IS_UNDEF(user_f));
+
+	e = &ents[partnum];
+	e->lba_end = cpu_to_le64(user_l);
+	e->lba_start = cpu_to_le64(user_f);
+
+	gpt_entry_set_type(e, &typeid);
+
+	if (pa && pa->uuid) {
+		/* Sometimes it's necessary to create a copy of the PT and
+		 * reuse already defined UUID
+		 */
+		rc = gpt_entry_set_uuid(e, pa->uuid);
+		if (rc)
+			goto done;
+	} else {
+		/* Any time a new partition entry is created a new GUID must be
+		 * generated for that partition, and every partition is guaranteed
+		 * to have a unique GUID.
+		 */
+		uuid_generate_random((unsigned char *) &e->partition_guid);
+		swap_efi_guid(&e->partition_guid);
+	}
+
+	if (pa && pa->name && *pa->name)
+		gpt_entry_set_name(e, pa->name);
+	if (pa && pa->attrs)
+		gpt_entry_attrs_from_string(cxt, e, pa->attrs);
+
+	DBG(LABEL, ul_debug("GPT new partition: partno=%zu, start=%ju, end=%ju, size=%ju",
+				partnum,
+				gpt_partition_start(e),
+				gpt_partition_end(e),
+				gpt_partition_size(e)));
+
+	gpt_recompute_crc(gpt->pheader, ents);
+	gpt_recompute_crc(gpt->bheader, ents);
+
+	/* report result */
+	{
+		struct fdisk_parttype *t;
+
+		cxt->label->nparts_cur++;
+		fdisk_label_set_changed(cxt->label, 1);
+
+		t = gpt_partition_parttype(cxt, &ents[partnum]);
+		fdisk_info_new_partition(cxt, partnum + 1, user_f, user_l, t);
+		fdisk_unref_parttype(t);
+	}
+
+	rc = 0;
+	if (partno)
+		*partno = partnum;
+done:
+	fdisk_unref_ask(ask);
+	return rc;
+}
+
+/*
+ * Create a new GPT disklabel - destroys any previous data.
+ */
+static int gpt_create_disklabel(struct fdisk_context *cxt)
+{
+	int rc = 0;
+	ssize_t esz = 0;
+	char str[37];
+	struct fdisk_gpt_label *gpt;
+
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, GPT));
+
+	gpt = self_label(cxt);
+
+	/* label private stuff has to be empty, see gpt_deinit() */
+	assert(gpt->pheader == NULL);
+	assert(gpt->bheader == NULL);
+
+	/*
+	 * When no header, entries or pmbr is set, we're probably
+	 * dealing with a new, empty disk - so always allocate memory
+	 * to deal with the data structures whatever the case is.
+	 */
+	rc = gpt_mknew_pmbr(cxt);
+	if (rc < 0)
+		goto done;
+
+	/* primary */
+	gpt->pheader = calloc(1, sizeof(*gpt->pheader));
+	if (!gpt->pheader) {
+		rc = -ENOMEM;
+		goto done;
+	}
+	rc = gpt_mknew_header(cxt, gpt->pheader, GPT_PRIMARY_PARTITION_TABLE_LBA);
+	if (rc < 0)
+		goto done;
+
+	/* backup ("copy" primary) */
+	gpt->bheader = calloc(1, sizeof(*gpt->bheader));
+	if (!gpt->bheader) {
+		rc = -ENOMEM;
+		goto done;
+	}
+	rc = gpt_mknew_header_from_bkp(cxt, gpt->bheader,
+			last_lba(cxt), gpt->pheader);
+	if (rc < 0)
+		goto done;
+
+	esz = le32_to_cpu(gpt->pheader->npartition_entries) *
+	      le32_to_cpu(gpt->pheader->sizeof_partition_entry);
+	gpt->ents = calloc(1, esz);
+	if (!gpt->ents) {
+		rc = -ENOMEM;
+		goto done;
+	}
+	gpt_recompute_crc(gpt->pheader, gpt->ents);
+	gpt_recompute_crc(gpt->bheader, gpt->ents);
+
+	cxt->label->nparts_max = le32_to_cpu(gpt->pheader->npartition_entries);
+	cxt->label->nparts_cur = 0;
+
+	guid_to_string(&gpt->pheader->disk_guid, str);
+	fdisk_label_set_changed(cxt->label, 1);
+	fdisk_info(cxt, _("Created a new GPT disklabel (GUID: %s)."), str);
+done:
+	return rc;
+}
+
+static int gpt_get_disklabel_id(struct fdisk_context *cxt, char **id)
+{
+	struct fdisk_gpt_label *gpt;
+	char str[37];
+
+	assert(cxt);
+	assert(id);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, GPT));
+
+	gpt = self_label(cxt);
+	guid_to_string(&gpt->pheader->disk_guid, str);
+
+	*id = strdup(str);
+	if (!*id)
+		return -ENOMEM;
+	return 0;
+}
+
+static int gpt_set_disklabel_id(struct fdisk_context *cxt)
+{
+	struct fdisk_gpt_label *gpt;
+	struct gpt_guid uuid;
+	char *str, *old, *new;
+	int rc;
+
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, GPT));
+
+	gpt = self_label(cxt);
+	if (fdisk_ask_string(cxt,
+			_("Enter new disk UUID (in 8-4-4-4-12 format)"), &str))
+		return -EINVAL;
+
+	rc = string_to_guid(str, &uuid);
+	free(str);
+
+	if (rc) {
+		fdisk_warnx(cxt, _("Failed to parse your UUID."));
+		return rc;
+	}
+
+	gpt_get_disklabel_id(cxt, &old);
+
+	gpt->pheader->disk_guid = uuid;
+	gpt->bheader->disk_guid = uuid;
+
+	gpt_recompute_crc(gpt->pheader, gpt->ents);
+	gpt_recompute_crc(gpt->bheader, gpt->ents);
+
+	gpt_get_disklabel_id(cxt, &new);
+
+	fdisk_info(cxt, _("Disk identifier changed from %s to %s."), old, new);
+
+	free(old);
+	free(new);
+	fdisk_label_set_changed(cxt->label, 1);
+	return 0;
+}
+
+static int gpt_part_is_used(struct fdisk_context *cxt, size_t i)
+{
+	struct fdisk_gpt_label *gpt;
+	struct gpt_entry *e;
+
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, GPT));
+
+	gpt = self_label(cxt);
+
+	if ((uint32_t) i >= le32_to_cpu(gpt->pheader->npartition_entries))
+		return 0;
+	e = &gpt->ents[i];
+
+	return !partition_unused(e) || gpt_partition_start(e);
+}
+
+/**
+ * fdisk_gpt_is_hybrid:
+ * @cxt: context
+ *
+ * The regular GPT contains PMBR (dummy protective MBR) where the protective
+ * MBR does not address any partitions.
+ *
+ * Hybrid GPT contains regular MBR where this partition table addresses the
+ * same partitions as GPT. It's recommended to not use hybrid GPT due to MBR
+ * limits.
+ *
+ * The libfdisk does not provide functionality to sync GPT and MBR, you have to
+ * directly access and modify (P)MBR (see fdisk_new_nested_context()).
+ *
+ * Returns: 1 if partition table detected as hybrid otherwise return 0
+ */
+int fdisk_gpt_is_hybrid(struct fdisk_context *cxt)
+{
+	assert(cxt);
+	return valid_pmbr(cxt) == GPT_MBR_HYBRID;
+}
+
+static int gpt_toggle_partition_flag(
+		struct fdisk_context *cxt,
+		size_t i,
+		unsigned long flag)
+{
+	struct fdisk_gpt_label *gpt;
+	uint64_t attrs, tmp;
+	char *bits;
+	const char *name = NULL;
+	int bit = -1, rc;
+
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, GPT));
+
+	DBG(LABEL, ul_debug("GPT entry attribute change requested partno=%zu", i));
+	gpt = self_label(cxt);
+
+	if ((uint32_t) i >= le32_to_cpu(gpt->pheader->npartition_entries))
+		return -EINVAL;
+
+	attrs = le64_to_cpu(gpt->ents[i].attrs);
+	bits = (char *) &attrs;
+
+	switch (flag) {
+	case GPT_FLAG_REQUIRED:
+		bit = GPT_ATTRBIT_REQ;
+		name = GPT_ATTRSTR_REQ;
+		break;
+	case GPT_FLAG_NOBLOCK:
+		bit = GPT_ATTRBIT_NOBLOCK;
+		name = GPT_ATTRSTR_NOBLOCK;
+		break;
+	case GPT_FLAG_LEGACYBOOT:
+		bit = GPT_ATTRBIT_LEGACY;
+		name = GPT_ATTRSTR_LEGACY;
+		break;
+	case GPT_FLAG_GUIDSPECIFIC:
+		rc = fdisk_ask_number(cxt, 48, 48, 63, _("Enter GUID specific bit"), &tmp);
+		if (rc)
+			return rc;
+		bit = tmp;
+		break;
+	default:
+		/* already specified PT_FLAG_GUIDSPECIFIC bit */
+		if (flag >= 48 && flag <= 63) {
+			bit = flag;
+			flag = GPT_FLAG_GUIDSPECIFIC;
+		}
+		break;
+	}
+
+	if (bit < 0) {
+		fdisk_warnx(cxt, _("failed to toggle unsupported bit %lu"), flag);
+		return -EINVAL;
+	}
+
+	if (!isset(bits, bit))
+		setbit(bits, bit);
+	else
+		clrbit(bits, bit);
+
+	gpt->ents[i].attrs = cpu_to_le64(attrs);
+
+	if (flag == GPT_FLAG_GUIDSPECIFIC)
+		fdisk_info(cxt, isset(bits, bit) ?
+			_("The GUID specific bit %d on partition %zu is enabled now.") :
+			_("The GUID specific bit %d on partition %zu is disabled now."),
+			bit, i + 1);
+	else
+		fdisk_info(cxt, isset(bits, bit) ?
+			_("The %s flag on partition %zu is enabled now.") :
+			_("The %s flag on partition %zu is disabled now."),
+			name, i + 1);
+
+	gpt_recompute_crc(gpt->pheader, gpt->ents);
+	gpt_recompute_crc(gpt->bheader, gpt->ents);
+	fdisk_label_set_changed(cxt->label, 1);
+	return 0;
+}
+
+static int gpt_entry_cmp_start(const void *a, const void *b)
+{
+	struct gpt_entry *ae = (struct gpt_entry *) a,
+			 *be = (struct gpt_entry *) b;
+	int au = partition_unused(ae),
+	    bu = partition_unused(be);
+
+	if (au && bu)
+		return 0;
+	if (au)
+		return 1;
+	if (bu)
+		return -1;
+
+	return cmp_numbers(gpt_partition_start(ae), gpt_partition_start(be));
+}
+
+/* sort partition by start sector */
+static int gpt_reorder(struct fdisk_context *cxt)
+{
+	struct fdisk_gpt_label *gpt;
+	size_t nparts;
+
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, GPT));
+
+	gpt = self_label(cxt);
+	nparts = le32_to_cpu(gpt->pheader->npartition_entries);
+
+	qsort(gpt->ents, nparts, sizeof(struct gpt_entry),
+			gpt_entry_cmp_start);
+
+	gpt_recompute_crc(gpt->pheader, gpt->ents);
+	gpt_recompute_crc(gpt->bheader, gpt->ents);
+	fdisk_label_set_changed(cxt->label, 1);
+
+	fdisk_info(cxt, _("Done."));
+	return 0;
+}
+
+static int gpt_reset_alignment(struct fdisk_context *cxt)
+{
+	struct fdisk_gpt_label *gpt;
+	struct gpt_header *h;
+
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, GPT));
+
+	gpt = self_label(cxt);
+	h = gpt ? gpt->pheader : NULL;
+
+	if (h) {
+		/* always follow existing table */
+		cxt->first_lba = h->first_usable_lba;
+		cxt->last_lba  = h->last_usable_lba;
+	} else {
+		/* estimate ranges for GPT */
+		uint64_t first, last;
+
+		count_first_last_lba(cxt, &first, &last);
+
+		if (cxt->first_lba < first)
+			cxt->first_lba = first;
+		if (cxt->last_lba > last)
+			cxt->last_lba = last;
+	}
+
+	return 0;
+}
+/*
+ * Deinitialize fdisk-specific variables
+ */
+static void gpt_deinit(struct fdisk_label *lb)
+{
+	struct fdisk_gpt_label *gpt = (struct fdisk_gpt_label *) lb;
+
+	if (!gpt)
+		return;
+
+	free(gpt->ents);
+	free(gpt->pheader);
+	free(gpt->bheader);
+
+	gpt->ents = NULL;
+	gpt->pheader = NULL;
+	gpt->bheader = NULL;
+}
+
+static const struct fdisk_label_operations gpt_operations =
+{
+	.probe		= gpt_probe_label,
+	.write		= gpt_write_disklabel,
+	.verify		= gpt_verify_disklabel,
+	.create		= gpt_create_disklabel,
+	.list		= gpt_list_disklabel,
+	.locate		= gpt_locate_disklabel,
+	.reorder	= gpt_reorder,
+	.get_id		= gpt_get_disklabel_id,
+	.set_id		= gpt_set_disklabel_id,
+
+	.get_part	= gpt_get_partition,
+	.set_part	= gpt_set_partition,
+	.add_part	= gpt_add_partition,
+	.del_part	= gpt_delete_partition,
+
+	.part_is_used	= gpt_part_is_used,
+	.part_toggle_flag = gpt_toggle_partition_flag,
+
+	.deinit		= gpt_deinit,
+
+	.reset_alignment = gpt_reset_alignment
+};
+
+static const struct fdisk_field gpt_fields[] =
+{
+	/* basic */
+	{ FDISK_FIELD_DEVICE,	N_("Device"),	 10,	0 },
+	{ FDISK_FIELD_START,	N_("Start"),	  5,	FDISK_FIELDFL_NUMBER },
+	{ FDISK_FIELD_END,	N_("End"),	  5,	FDISK_FIELDFL_NUMBER },
+	{ FDISK_FIELD_SECTORS,	N_("Sectors"),	  5,	FDISK_FIELDFL_NUMBER },
+	{ FDISK_FIELD_SIZE,	N_("Size"),	  5,	FDISK_FIELDFL_NUMBER | FDISK_FIELDFL_EYECANDY },
+	{ FDISK_FIELD_TYPE,	N_("Type"),	0.1,	FDISK_FIELDFL_EYECANDY },
+	/* expert */
+	{ FDISK_FIELD_TYPEID,	N_("Type-UUID"), 36,	FDISK_FIELDFL_DETAIL },
+	{ FDISK_FIELD_UUID,	N_("UUID"),	 36,	FDISK_FIELDFL_DETAIL },
+	{ FDISK_FIELD_NAME,	N_("Name"),	0.2,	FDISK_FIELDFL_DETAIL },
+	{ FDISK_FIELD_ATTR,	N_("Attrs"),	  0,	FDISK_FIELDFL_DETAIL }
+};
+
+/*
+ * allocates GPT in-memory stuff
+ */
+struct fdisk_label *fdisk_new_gpt_label(struct fdisk_context *cxt)
+{
+	struct fdisk_label *lb;
+	struct fdisk_gpt_label *gpt;
+
+	assert(cxt);
+
+	gpt = calloc(1, sizeof(*gpt));
+	if (!gpt)
+		return NULL;
+
+	/* initialize generic part of the driver */
+	lb = (struct fdisk_label *) gpt;
+	lb->name = "gpt";
+	lb->id = FDISK_DISKLABEL_GPT;
+	lb->op = &gpt_operations;
+	lb->parttypes = gpt_parttypes;
+	lb->nparttypes = ARRAY_SIZE(gpt_parttypes);
+
+	lb->fields = gpt_fields;
+	lb->nfields = ARRAY_SIZE(gpt_fields);
+
+	return lb;
+}
diff --git a/libblkid/libfdisk/src/init.c b/libblkid/libfdisk/src/init.c
new file mode 100644
index 0000000..61acb0a
--- /dev/null
+++ b/libblkid/libfdisk/src/init.c
@@ -0,0 +1,55 @@
+
+#include "fdiskP.h"
+
+
+/**
+ * SECTION: init
+ * @title: Library initialization
+ * @short_description: initialize debug stuff
+ *
+ */
+
+UL_DEBUG_DEFINE_MASK(libfdisk);
+UL_DEBUG_DEFINE_MASKNAMES(libfdisk) =
+{
+	{ "all",	LIBFDISK_DEBUG_ALL,	"info about all subsystems" },
+	{ "ask",	LIBFDISK_DEBUG_ASK,	"fdisk dialogs" },
+	{ "help",	LIBFDISK_DEBUG_HELP,	"this help" },
+	{ "cxt",	LIBFDISK_DEBUG_CXT,	"library context (handler)" },
+	{ "label",	LIBFDISK_DEBUG_LABEL,	"disk label utils" },
+	{ "part",	LIBFDISK_DEBUG_PART,	"partition utils" },
+	{ "parttype",	LIBFDISK_DEBUG_PARTTYPE,"partition type utils" },
+	{ "script",	LIBFDISK_DEBUG_SCRIPT,	"sfdisk-like scripts" },
+	{ "tab",	LIBFDISK_DEBUG_TAB,	"table utils"},
+	{ NULL, 0 }
+};
+
+/**
+ * fdisk_init_debug:
+ * @mask: debug mask (0xffff to enable full debuging)
+ *
+ * If the @mask is not specified then this function reads
+ * LIBFDISK_DEBUG environment variable to get the mask.
+ *
+ * Already initialized debugging stuff cannot be changed. It does not
+ * have effect to call this function twice.
+ *
+ * It's strongly recommended to use fdisk_init_debug(0) in your code.
+ */
+void fdisk_init_debug(int mask)
+{
+	if (libfdisk_debug_mask)
+		return;
+
+	__UL_INIT_DEBUG(libfdisk, LIBFDISK_DEBUG_, mask, LIBFDISK_DEBUG);
+
+
+	if (libfdisk_debug_mask != LIBFDISK_DEBUG_INIT
+	    && libfdisk_debug_mask != (LIBFDISK_DEBUG_HELP|LIBFDISK_DEBUG_INIT)) {
+
+		DBG(INIT, ul_debug("library debug mask: 0x%04x", libfdisk_debug_mask));
+	}
+
+	ON_DBG(HELP, ul_debug_print_masks("LIBFDISK_DEBUG",
+				UL_DEBUG_MASKNAMES(libfdisk)));
+}
diff --git a/libblkid/libfdisk/src/iter.c b/libblkid/libfdisk/src/iter.c
new file mode 100644
index 0000000..9a0b080
--- /dev/null
+++ b/libblkid/libfdisk/src/iter.c
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2009 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+
+/**
+ * SECTION: iter
+ * @title: Iterator
+ * @short_description: unified iterator
+ *
+ * The iterator keeps the direction and the last position for access to the
+ * internal library tables/lists.
+ *
+ * It's very unusual to use the same iterator on multiple places in your
+ * application or share the same iterator, for this purpose libfdisk does not
+ * provide reference counting for this object. It's recommended to initialize
+ * the iterator by fdisk_new_iter() at begin of your function and then
+ * fdisk_free_iter() before you return from the function. 
+ *
+ * Don't forget to call fdisk_reset_iter() if you want to use the iterator more
+ * than once.
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "fdiskP.h"
+
+/**
+ * fdisk_new_iter:
+ * @direction: FDISK_INTER_{FOR,BACK}WARD direction
+ *
+ * Returns: newly allocated generic libmount iterator.
+ */
+struct fdisk_iter *fdisk_new_iter(int direction)
+{
+	struct fdisk_iter *itr = calloc(1, sizeof(*itr));
+	if (!itr)
+		return NULL;
+	itr->direction = direction;
+	return itr;
+}
+
+/**
+ * fdisk_free_iter:
+ * @itr: iterator pointer
+ *
+ * Deallocates the iterator.
+ */
+void fdisk_free_iter(struct fdisk_iter *itr)
+{
+	free(itr);
+}
+
+/**
+ * fdisk_reset_iter:
+ * @itr: iterator pointer
+ * @direction: FDISK_INTER_{FOR,BACK}WARD or -1 to keep the direction unchanged
+ *
+ * Resets the iterator.
+ */
+void fdisk_reset_iter(struct fdisk_iter *itr, int direction)
+{
+	if (direction == -1)
+		direction = itr->direction;
+
+	memset(itr, 0, sizeof(*itr));
+	itr->direction = direction;
+}
+
+/**
+ * fdisk_iter_get_direction:
+ * @itr: iterator pointer
+ *
+ * Returns: FDISK_INTER_{FOR,BACK}WARD
+ */
+int fdisk_iter_get_direction(struct fdisk_iter *itr)
+{
+	return itr->direction;
+}
diff --git a/libblkid/libfdisk/src/label.c b/libblkid/libfdisk/src/label.c
new file mode 100644
index 0000000..750cfca
--- /dev/null
+++ b/libblkid/libfdisk/src/label.c
@@ -0,0 +1,569 @@
+
+#include "fdiskP.h"
+
+
+/**
+ * SECTION: label
+ * @title: Label
+ * @short_description: disk label (PT) specific data and functions
+ *
+ * The fdisk_new_context() initializes all label drivers, and allocate
+ * per-label specific data struct. This concept allows to store label specific
+ * settings to the label driver independently on the currently active label
+ * driver. Note that label struct cannot be deallocated, so there is no
+ * reference counting for fdisk_label objects. All is destroyed by
+ * fdisk_unref_context() only.
+ *
+ * Anyway, all label drives share in-memory first sector. The function
+ * fdisk_create_disklabel() overwrites the sector. But it's possible that
+ * label driver also uses another buffers, for example GPT uses more than only
+ * the first sector.
+ *
+ * All label operations are in-memory only, except fdisk_write_disklabel().
+ *
+ * All functions that use "struct fdisk_context" rather than "struct
+ * fdisk_label" use the currently active label driver.
+ */
+
+
+int fdisk_probe_labels(struct fdisk_context *cxt)
+{
+	size_t i;
+
+	cxt->label = NULL;
+
+	for (i = 0; i < cxt->nlabels; i++) {
+		struct fdisk_label *lb = cxt->labels[i];
+		struct fdisk_label *org = fdisk_get_label(cxt, NULL);
+		int rc;
+
+		if (!lb->op->probe)
+			continue;
+		if (lb->disabled) {
+			DBG(CXT, ul_debugobj(cxt, "%s: disabled -- ignore", lb->name));
+			continue;
+		}
+		DBG(CXT, ul_debugobj(cxt, "probing for %s", lb->name));
+
+		cxt->label = lb;
+		rc = lb->op->probe(cxt);
+		cxt->label = org;
+
+		if (rc != 1) {
+			if (lb->op->deinit)
+				lb->op->deinit(lb);	/* for sure */
+			continue;
+		}
+
+		__fdisk_switch_label(cxt, lb);
+		return 0;
+	}
+
+	DBG(CXT, ul_debugobj(cxt, "no label found"));
+	return 1; /* not found */
+}
+
+/**
+ * fdisk_label_get_name:
+ * @lb: label
+ *
+ * Returns: label name
+ */
+const char *fdisk_label_get_name(const struct fdisk_label *lb)
+{
+	return lb ? lb->name : NULL;
+}
+
+/**
+ * fdisk_label_is_labeltype:
+ * @lb: label
+ *
+ * Returns: FDISK_DISKLABEL_*.
+ */
+int fdisk_label_get_type(const struct fdisk_label *lb)
+{
+	return lb->id;
+}
+
+/**
+ * fdisk_label_require_geometry:
+ * @lb: label
+ *
+ * Returns: 1 if label requires CHS geometry
+ */
+int fdisk_label_require_geometry(const struct fdisk_label *lb)
+{
+	assert(lb);
+
+	return lb->flags & FDISK_LABEL_FL_REQUIRE_GEOMETRY ? 1 : 0;
+}
+
+/**
+ * fdisk_label_get_fields_ids
+ * @lb: label (or NULL for the current label)
+ * @cxt: context
+ * @ids: returns allocated array with FDISK_FIELD_* IDs
+ * @nids: returns number of items in fields
+ *
+ * This function returns the default fields for the label.
+ *
+ * Note that the set of the default fields depends on fdisk_enable_details()
+ * function. If the details are enabled then this function usually returns more
+ * fields.
+ *
+ * Returns: 0 on success, otherwise, a corresponding error.
+ */
+int fdisk_label_get_fields_ids(
+		const struct fdisk_label *lb,
+		struct fdisk_context *cxt,
+		int **ids, size_t *nids)
+{
+	size_t i, n;
+	int *c;
+
+	assert(cxt);
+
+	if (!lb)
+		lb = cxt->label;
+	if (!lb)
+		return -EINVAL;
+	if (!lb->fields || !lb->nfields)
+		return -ENOSYS;
+	c = calloc(lb->nfields, sizeof(int));
+	if (!c)
+		return -ENOMEM;
+	for (n = 0, i = 0; i < lb->nfields; i++) {
+		int id = lb->fields[i].id;
+
+		if ((fdisk_is_details(cxt) &&
+				(lb->fields[i].flags & FDISK_FIELDFL_EYECANDY))
+		     || (!fdisk_is_details(cxt) &&
+				(lb->fields[i].flags & FDISK_FIELDFL_DETAIL))
+		     || (id == FDISK_FIELD_SECTORS &&
+			         fdisk_use_cylinders(cxt))
+		     || (id == FDISK_FIELD_CYLINDERS &&
+			         !fdisk_use_cylinders(cxt)))
+			continue;
+
+		c[n++] = id;
+	}
+	if (ids)
+		*ids = c;
+	else
+		free(c);
+	if (nids)
+		*nids = n;
+	return 0;
+}
+
+/**
+ * fdisk_label_get_field:
+ * @lb: label
+ * @id: FDISK_FIELD_*
+ *
+ * The field struct describes data stored in struct fdisk_partition. The info
+ * about data is usable for example to generate human readable output (e.g.
+ * fdisk 'p'rint command). See fdisk_partition_to_stirng() and fdisk code.
+ *
+ * Returns: pointer to static instance of the field.
+ */
+const struct fdisk_field *fdisk_label_get_field(const struct fdisk_label *lb, int id)
+{
+	size_t i;
+
+	assert(lb);
+	assert(id > 0);
+
+	for (i = 0; i < lb->nfields; i++) {
+		if (lb->fields[i].id == id)
+			return &lb->fields[i];
+	}
+
+	return NULL;
+}
+
+/**
+ * fdisk_label_get_field_by_name
+ * @lb: label
+ * @name: field name
+ *
+ * Returns: pointer to static instance of the field.
+ */
+const struct fdisk_field *fdisk_label_get_field_by_name(
+				const struct fdisk_label *lb,
+				const char *name)
+{
+	size_t i;
+
+	assert(lb);
+	assert(name);
+
+	for (i = 0; i < lb->nfields; i++) {
+		if (lb->fields[i].name && strcasecmp(lb->fields[i].name, name) == 0)
+			return &lb->fields[i];
+	}
+
+	return NULL;
+}
+
+
+/**
+ * fdisk_field_get_id:
+ * @field: field instance
+ *
+ * Returns: field Id (FDISK_FIELD_*)
+ */
+int fdisk_field_get_id(const struct fdisk_field *field)
+{
+	return field ? field->id : -EINVAL;
+}
+
+/**
+ * fdisk_field_get_name:
+ * @field: field instance
+ *
+ * Returns: field name
+ */
+const char *fdisk_field_get_name(const struct fdisk_field *field)
+{
+	return field ? field->name : NULL;
+}
+
+/**
+ * fdisk_field_get_width:
+ * @field: field instance
+ *
+ * Returns: libsmartcols compatible width.
+ */
+double fdisk_field_get_width(const struct fdisk_field *field)
+{
+	return field ? field->width : -EINVAL;
+}
+
+/**
+ * fdisk_field_is_number:
+ * @field: field instance
+ *
+ * Returns: 1 if field represent number
+ */
+int fdisk_field_is_number(const struct fdisk_field *field)
+{
+	return field->flags ? field->flags & FDISK_FIELDFL_NUMBER : 0;
+}
+
+
+/**
+ * fdisk_write_disklabel:
+ * @cxt: fdisk context
+ *
+ * Write in-memory changes to disk. Be careful!
+ *
+ * Returns: 0 on success, otherwise, a corresponding error.
+ */
+int fdisk_write_disklabel(struct fdisk_context *cxt)
+{
+	if (!cxt || !cxt->label || cxt->readonly)
+		return -EINVAL;
+	if (!cxt->label->op->write)
+		return -ENOSYS;
+	return cxt->label->op->write(cxt);
+}
+
+/**
+ * fdisk_verify_disklabel:
+ * @cxt: fdisk context
+ *
+ * Verifies the partition table.
+ *
+ * Returns: 0 on success, otherwise, a corresponding error.
+ */
+int fdisk_verify_disklabel(struct fdisk_context *cxt)
+{
+	if (!cxt || !cxt->label)
+		return -EINVAL;
+	if (!cxt->label->op->verify)
+		return -ENOSYS;
+	if (fdisk_missing_geometry(cxt))
+		return -EINVAL;
+
+	return cxt->label->op->verify(cxt);
+}
+
+/**
+ * fdisk_list_disklabel:
+ * @cxt: fdisk context
+ *
+ * Lists details about disklabel, but no partitions.
+ *
+ * This function uses libfdisk ASK interface to print data. The details about
+ * partitions table are printed by FDISK_ASKTYPE_INFO.
+ *
+ * Returns: 0 on success, otherwise, a corresponding error.
+ */
+int fdisk_list_disklabel(struct fdisk_context *cxt)
+{
+	if (!cxt || !cxt->label)
+		return -EINVAL;
+	if (!cxt->label->op->list)
+		return -ENOSYS;
+
+	return cxt->label->op->list(cxt);
+}
+
+/**
+ * fdisk_create_disklabel:
+ * @cxt: fdisk context
+ * @name: label name
+ *
+ * Creates a new disk label of type @name. If @name is NULL, then it will
+ * create a default system label type, either SUN or DOS. The function
+ * automaticaly switches the current label driver to @name. The function
+ * fdisk_get_label() returns the current label driver.
+ *
+ * The function modifies in-memory data only.
+ *
+ * Returns: 0 on success, otherwise, a corresponding error.
+ */
+int fdisk_create_disklabel(struct fdisk_context *cxt, const char *name)
+{
+	int haslabel = 0;
+	struct fdisk_label *lb;
+
+	if (!cxt)
+		return -EINVAL;
+
+	if (!name) { /* use default label creation */
+#ifdef __sparc__
+		name = "sun";
+#else
+		name = "dos";
+#endif
+	}
+
+	if (cxt->label) {
+		fdisk_deinit_label(cxt->label);
+		haslabel = 1;
+	}
+
+	lb = fdisk_get_label(cxt, name);
+	if (!lb || lb->disabled)
+		return -EINVAL;
+	if (!lb->op->create)
+		return -ENOSYS;
+
+	__fdisk_switch_label(cxt, lb);
+
+	if (haslabel && !cxt->parent)
+		fdisk_reset_device_properties(cxt);
+
+	DBG(CXT, ul_debugobj(cxt, "create a new %s label", lb->name));
+	return cxt->label->op->create(cxt);
+}
+
+/**
+ * fdisk_locate_disklabel:
+ * @cxt: context
+ * @n: N item
+ * @name: return item name
+ * @offset: return offset where is item
+ * @size: of the item
+ *
+ * Locate disklabel and returns info about @n item of the label. For example
+ * GPT is composed from two items, PMBR and GPT, n=0 return offset to PMBR and n=1
+ * return offset to GPT. For more details see 'D' expect fdisk command.
+ *
+ * Returns: 0 on succes, <0 on error, 1 no more items.
+ */
+int fdisk_locate_disklabel(struct fdisk_context *cxt, int n, const char **name,
+			   off_t *offset, size_t *size)
+{
+	if (!cxt || !cxt->label)
+		return -EINVAL;
+	if (!cxt->label->op->locate)
+		return -ENOSYS;
+
+	DBG(CXT, ul_debugobj(cxt, "locating %d chunk of %s.", n, cxt->label->name));
+	return cxt->label->op->locate(cxt, n, name, offset, size);
+}
+
+
+/**
+ * fdisk_get_disklabel_id:
+ * @cxt: fdisk context
+ * @id: returns pointer to allocated string (MBR Id or GPT dirk UUID)
+ *
+ * Returns: 0 on success, otherwise, a corresponding error.
+ */
+int fdisk_get_disklabel_id(struct fdisk_context *cxt, char **id)
+{
+	if (!cxt || !cxt->label)
+		return -EINVAL;
+	if (!cxt->label->op->get_id)
+		return -ENOSYS;
+
+	DBG(CXT, ul_debugobj(cxt, "asking for disk %s ID", cxt->label->name));
+	return cxt->label->op->get_id(cxt, id);
+}
+
+/**
+ * fdisk_set_disklabel_id:
+ * @cxt: fdisk context
+ *
+ * Returns: 0 on success, otherwise, a corresponding error.
+ */
+int fdisk_set_disklabel_id(struct fdisk_context *cxt)
+{
+	if (!cxt || !cxt->label)
+		return -EINVAL;
+	if (!cxt->label->op->set_id)
+		return -ENOSYS;
+
+	DBG(CXT, ul_debugobj(cxt, "setting %s disk ID", cxt->label->name));
+	return cxt->label->op->set_id(cxt);
+}
+
+/**
+ * fdisk_set_partition_type:
+ * @cxt: fdisk context
+ * @partnum: partition number
+ * @t: new type
+ *
+ * Returns: 0 on success, < 0 on error.
+ */
+int fdisk_set_partition_type(struct fdisk_context *cxt,
+			     size_t partnum,
+			     struct fdisk_parttype *t)
+{
+	if (!cxt || !cxt->label || !t)
+		return -EINVAL;
+
+
+	if (cxt->label->op->set_part) {
+		struct fdisk_partition *pa = fdisk_new_partition();
+		int rc;
+
+		if (!pa)
+			return -ENOMEM;
+		fdisk_partition_set_type(pa, t);
+
+		DBG(CXT, ul_debugobj(cxt, "partition: %zd: set type", partnum));
+		rc = cxt->label->op->set_part(cxt, partnum, pa);
+		fdisk_unref_partition(pa);
+		return rc;
+	}
+
+	return -ENOSYS;
+}
+
+
+/**
+ * fdisk_toggle_partition_flag:
+ * @cxt: fdisk context
+ * @partnum: partition number
+ * @flag: flag ID
+ *
+ * Returns: 0 on success, otherwise, a corresponding error.
+ */
+int fdisk_toggle_partition_flag(struct fdisk_context *cxt,
+			       size_t partnum,
+			       unsigned long flag)
+{
+	int rc;
+
+	if (!cxt || !cxt->label)
+		return -EINVAL;
+	if (!cxt->label->op->part_toggle_flag)
+		return -ENOSYS;
+
+	rc = cxt->label->op->part_toggle_flag(cxt, partnum, flag);
+
+	DBG(CXT, ul_debugobj(cxt, "partition: %zd: toggle: 0x%04lx [rc=%d]", partnum, flag, rc));
+	return rc;
+}
+
+/**
+ * fdisk_reorder_partitions
+ * @cxt: fdisk context
+ *
+ * Sort partitions according to the partition start sector.
+ *
+ * Returns: 0 on success, otherwise, a corresponding error.
+ */
+int fdisk_reorder_partitions(struct fdisk_context *cxt)
+{
+	if (!cxt || !cxt->label)
+		return -EINVAL;
+	if (!cxt->label->op->reorder)
+		return -ENOSYS;
+
+	return cxt->label->op->reorder(cxt);
+}
+
+/*
+ * Resets the current used label driver to initial state
+ */
+void fdisk_deinit_label(struct fdisk_label *lb)
+{
+	assert(lb);
+
+	/* private label information */
+	if (lb->op->deinit)
+		lb->op->deinit(lb);
+}
+
+/**
+ * fdisk_label_set_changed:
+ * @lb: label
+ * @changed: 0/1
+ *
+ * Marks in-memory data as changed, to force fdisk_write_disklabel() to write
+ * to device. This should be unnecessar by default, the library keeps track
+ * about changes.
+ */
+void fdisk_label_set_changed(struct fdisk_label *lb, int changed)
+{
+	assert(lb);
+	lb->changed = changed ? 1 : 0;
+}
+
+/**
+ * fdisk_label_is_changed:
+ * @lb: label
+ *
+ * Returns: 1 if in-memory data has been changed.
+ */
+int fdisk_label_is_changed(const struct fdisk_label *lb)
+{
+	assert(lb);
+	return lb ? lb->changed : 0;
+}
+
+/**
+ * fdisk_label_set_disabled:
+ * @lb: label
+ * @disabled: 0 or 1
+ *
+ * Mark label as disabled, then libfdisk is going to ignore the label when
+ * probe device for labels.
+ */
+void fdisk_label_set_disabled(struct fdisk_label *lb, int disabled)
+{
+	assert(lb);
+
+	DBG(LABEL, ul_debug("%s label %s",
+				lb->name,
+				disabled ? "DISABLED" : "ENABLED"));
+	lb->disabled = disabled ? 1 : 0;
+}
+
+/**
+ * fdisk_label_is_disabled:
+ * @lb: label
+ *
+ * Returns: 1 if label driver disabled.
+ */
+int fdisk_label_is_disabled(const struct fdisk_label *lb)
+{
+	assert(lb);
+	return lb ? lb->disabled : 0;
+}
diff --git a/libblkid/libfdisk/src/libfdisk.h b/libblkid/libfdisk/src/libfdisk.h
new file mode 100644
index 0000000..844e17e
--- /dev/null
+++ b/libblkid/libfdisk/src/libfdisk.h
@@ -0,0 +1,579 @@
+/*
+ * libfdisk.h - libfdisk API
+ *
+ * Copyright (C) 2012-2014 Karel Zak <kzak@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef _LIBFDISK_H
+#define _LIBFDISK_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdint.h>
+
+/**
+ * LIBFDISK_VERSION:
+ *
+ * Library version string
+ */
+#define LIBFDISK_VERSION   "2.25.0"
+
+/**
+ * fdisk_context:
+ *
+ * Basic library handler.
+ */
+struct fdisk_context;
+
+/**
+ * fdisk_label:
+ *
+ * Disk label specific driver and setting.
+ */
+struct fdisk_label;
+
+/**
+ * fdisk_parttype:
+ *
+ * Partition type.
+ */
+struct fdisk_parttype;
+
+/**
+ * fdisk_partition:
+ *
+ * Partition abstraction (and template).
+ */
+struct fdisk_partition;
+
+/**
+ * fdisk_ask:
+ *
+ * Ask API handler for dialogs with users.
+ */
+struct fdisk_ask;
+
+/**
+ * fdisk_iter:
+ *
+ * Unified iterator.
+ */
+struct fdisk_iter;
+
+/**
+ * fdisk_table:
+ *
+ * Container for fdisk_partition objects
+ */
+struct fdisk_table;
+
+/**
+ * fdisk_field
+ *
+ * Output field description.
+ */
+struct fdisk_field;
+
+/**
+ * fdisk_script
+ *
+ * library handler for sfdisk compatible scripts
+ */
+struct fdisk_script;
+
+/**
+ * fdisk_sector_t
+ *
+ * LBA adresses type
+ */
+typedef uint64_t fdisk_sector_t;
+
+/**
+ * fdisk_labeltype:
+ *
+ * Supported partition table types (labels)
+ */
+enum fdisk_labeltype {
+	FDISK_DISKLABEL_DOS = (1 << 1),
+	FDISK_DISKLABEL_SUN = (1 << 2),
+	FDISK_DISKLABEL_SGI = (1 << 3),
+	FDISK_DISKLABEL_BSD = (1 << 4),
+	FDISK_DISKLABEL_GPT = (1 << 5)
+};
+
+/**
+ * fdisk_asktype:
+ *
+ * Ask API dialog types
+ */
+enum fdisk_asktype {
+	FDISK_ASKTYPE_NONE = 0,
+	FDISK_ASKTYPE_NUMBER,
+	FDISK_ASKTYPE_OFFSET,
+	FDISK_ASKTYPE_WARN,
+	FDISK_ASKTYPE_WARNX,
+	FDISK_ASKTYPE_INFO,
+	FDISK_ASKTYPE_YESNO,
+	FDISK_ASKTYPE_STRING,
+	FDISK_ASKTYPE_MENU
+};
+
+/* init.c */
+extern void fdisk_init_debug(int mask);
+
+/* context.h */
+
+#define FDISK_PLURAL	0
+#define FDISK_SINGULAR	1
+
+struct fdisk_context *fdisk_new_context(void);
+struct fdisk_context *fdisk_new_nested_context(struct fdisk_context *parent, const char *name);
+void fdisk_unref_context(struct fdisk_context *cxt);
+void fdisk_ref_context(struct fdisk_context *cxt);
+
+struct fdisk_context *fdisk_get_parent(struct fdisk_context *cxt);
+size_t fdisk_get_npartitions(struct fdisk_context *cxt);
+
+struct fdisk_label *fdisk_get_label(struct fdisk_context *cxt, const char *name);
+int fdisk_next_label(struct fdisk_context *cxt, struct fdisk_label **lb);
+size_t fdisk_get_nlabels(struct fdisk_context *cxt);
+
+int fdisk_has_label(struct fdisk_context *cxt);
+int fdisk_is_labeltype(struct fdisk_context *cxt, enum fdisk_labeltype id);
+#define fdisk_is_label(c, x) fdisk_is_labeltype(c, FDISK_DISKLABEL_ ## x)
+
+
+int fdisk_assign_device(struct fdisk_context *cxt,
+			const char *fname, int readonly);
+int fdisk_deassign_device(struct fdisk_context *cxt, int nosync);
+int fdisk_is_readonly(struct fdisk_context *cxt);
+
+int fdisk_enable_details(struct fdisk_context *cxt, int enable);
+int fdisk_is_details(struct fdisk_context *cxt);
+
+int fdisk_enable_listonly(struct fdisk_context *cxt, int enable);
+int fdisk_is_listonly(struct fdisk_context *cxt);
+
+int fdisk_set_unit(struct fdisk_context *cxt, const char *str);
+const char *fdisk_get_unit(struct fdisk_context *cxt, int n);
+int fdisk_use_cylinders(struct fdisk_context *cxt);
+unsigned int fdisk_get_units_per_sector(struct fdisk_context *cxt);
+
+unsigned long fdisk_get_optimal_iosize(struct fdisk_context *cxt);
+unsigned long fdisk_get_minimal_iosize(struct fdisk_context *cxt);
+unsigned long fdisk_get_physector_size(struct fdisk_context *cxt);
+unsigned long fdisk_get_sector_size(struct fdisk_context *cxt);
+unsigned long fdisk_get_alignment_offset(struct fdisk_context *cxt);
+unsigned long fdisk_get_grain_size(struct fdisk_context *cxt);
+fdisk_sector_t fdisk_get_first_lba(struct fdisk_context *cxt);
+fdisk_sector_t fdisk_set_first_lba(struct fdisk_context *cxt, fdisk_sector_t lba);
+fdisk_sector_t fdisk_get_last_lba(struct fdisk_context *cxt);
+fdisk_sector_t fdisk_set_last_lba(struct fdisk_context *cxt, fdisk_sector_t lba);
+fdisk_sector_t fdisk_get_nsectors(struct fdisk_context *cxt);
+const char *fdisk_get_devname(struct fdisk_context *cxt);
+int fdisk_get_devfd(struct fdisk_context *cxt);
+
+unsigned int fdisk_get_geom_heads(struct fdisk_context *cxt);
+fdisk_sector_t fdisk_get_geom_sectors(struct fdisk_context *cxt);
+fdisk_sector_t fdisk_get_geom_cylinders(struct fdisk_context *cxt);
+
+
+
+/* parttype.c */
+struct fdisk_parttype *fdisk_new_parttype(void);
+void fdisk_ref_parttype(struct fdisk_parttype *t);
+void fdisk_unref_parttype(struct fdisk_parttype *t);
+int fdisk_parttype_set_name(struct fdisk_parttype *t, const char *str);
+int fdisk_parttype_set_typestr(struct fdisk_parttype *t, const char *str);
+int fdisk_parttype_set_code(struct fdisk_parttype *t, int code);
+size_t fdisk_label_get_nparttypes(const struct fdisk_label *lb);
+struct fdisk_parttype *fdisk_label_get_parttype(const struct fdisk_label *lb, size_t n);
+int fdisk_label_has_code_parttypes(const struct fdisk_label *lb);
+struct fdisk_parttype *fdisk_label_get_parttype_from_code(
+				const struct fdisk_label *lb,
+				unsigned int code);
+struct fdisk_parttype *fdisk_label_get_parttype_from_string(
+				const struct fdisk_label *lb,
+				const char *str);
+struct fdisk_parttype *fdisk_new_unknown_parttype(unsigned int code,
+						  const char *typestr);
+struct fdisk_parttype *fdisk_copy_parttype(const struct fdisk_parttype *type);
+struct fdisk_parttype *fdisk_label_parse_parttype(
+				const struct fdisk_label *lb,
+				const char *str);
+const char *fdisk_parttype_get_string(const struct fdisk_parttype *t);
+unsigned int fdisk_parttype_get_code(const struct fdisk_parttype *t);
+const char *fdisk_parttype_get_name(const struct fdisk_parttype *t);
+int fdisk_parttype_is_unknown(const struct fdisk_parttype *t);
+
+/* label.c */
+
+/**
+ * fdisk_fieldtype
+ *
+ * Types of fdisk_field
+ */
+enum fdisk_fieldtype {
+	FDISK_FIELD_NONE = 0,
+
+	/* generic */
+	FDISK_FIELD_DEVICE,
+	FDISK_FIELD_START,
+	FDISK_FIELD_END,
+	FDISK_FIELD_SECTORS,
+	FDISK_FIELD_CYLINDERS,
+	FDISK_FIELD_SIZE,
+	FDISK_FIELD_TYPE,
+	FDISK_FIELD_TYPEID,
+
+	/* label specific */
+	FDISK_FIELD_ATTR,
+	FDISK_FIELD_BOOT,
+	FDISK_FIELD_BSIZE,
+	FDISK_FIELD_CPG,
+	FDISK_FIELD_EADDR,
+	FDISK_FIELD_FSIZE,
+	FDISK_FIELD_NAME,
+	FDISK_FIELD_SADDR,
+	FDISK_FIELD_UUID,
+
+	FDISK_NFIELDS		/* must be last */
+};
+
+int fdisk_label_get_type(const struct fdisk_label *lb);
+const char *fdisk_label_get_name(const struct fdisk_label *lb);
+int fdisk_label_require_geometry(const struct fdisk_label *lb);
+
+
+extern int fdisk_write_disklabel(struct fdisk_context *cxt);
+extern int fdisk_verify_disklabel(struct fdisk_context *cxt);
+extern int fdisk_create_disklabel(struct fdisk_context *cxt, const char *name);
+extern int fdisk_list_disklabel(struct fdisk_context *cxt);
+extern int fdisk_locate_disklabel(struct fdisk_context *cxt, int n, const char **name, off_t *offset, size_t *size);
+
+extern int fdisk_get_disklabel_id(struct fdisk_context *cxt, char **id);
+extern int fdisk_set_disklabel_id(struct fdisk_context *cxt);
+
+extern int fdisk_get_partition(struct fdisk_context *cxt, size_t partno, struct fdisk_partition **pa);
+extern int fdisk_set_partition(struct fdisk_context *cxt, size_t partno, struct fdisk_partition *pa);
+extern int fdisk_add_partition(struct fdisk_context *cxt, struct fdisk_partition *pa, size_t *partno);
+extern int fdisk_delete_partition(struct fdisk_context *cxt, size_t partno);
+
+extern int fdisk_delete_all_partitions(struct fdisk_context *cxt);
+
+extern int fdisk_set_partition_type(struct fdisk_context *cxt, size_t partnum,
+			     struct fdisk_parttype *t);
+
+
+extern int fdisk_label_get_fields_ids(
+			const struct fdisk_label *lb,
+			struct fdisk_context *cxt,
+			int **ids, size_t *nids);
+
+extern const struct fdisk_field *fdisk_label_get_field(const struct fdisk_label *lb, int id);
+extern const struct fdisk_field *fdisk_label_get_field_by_name(
+			const struct fdisk_label *lb,
+			const char *name);
+
+extern int fdisk_field_get_id(const struct fdisk_field *field);
+extern const char *fdisk_field_get_name(const struct fdisk_field *field);
+extern double fdisk_field_get_width(const struct fdisk_field *field);
+extern int fdisk_field_is_number(const struct fdisk_field *field);
+
+
+extern void fdisk_label_set_changed(struct fdisk_label *lb, int changed);
+extern int fdisk_label_is_changed(const struct fdisk_label *lb);
+
+extern void fdisk_label_set_disabled(struct fdisk_label *lb, int disabled);
+extern int fdisk_label_is_disabled(const struct fdisk_label *lb);
+
+extern int fdisk_is_partition_used(struct fdisk_context *cxt, size_t n);
+
+extern int fdisk_toggle_partition_flag(struct fdisk_context *cxt, size_t partnum, unsigned long flag);
+
+extern struct fdisk_partition *fdisk_new_partition(void);
+extern void fdisk_reset_partition(struct fdisk_partition *pa);
+extern void fdisk_ref_partition(struct fdisk_partition *pa);
+extern void fdisk_unref_partition(struct fdisk_partition *pa);
+extern int fdisk_partition_is_freespace(struct fdisk_partition *pa);
+
+int fdisk_partition_set_start(struct fdisk_partition *pa, uint64_t off);
+int fdisk_partition_unset_start(struct fdisk_partition *pa);
+uint64_t fdisk_partition_get_start(struct fdisk_partition *pa);
+int fdisk_partition_has_start(struct fdisk_partition *pa);
+int fdisk_partition_cmp_start(struct fdisk_partition *a,
+			      struct fdisk_partition *b);
+int fdisk_partition_start_follow_default(struct fdisk_partition *pa, int enable);
+int fdisk_partition_start_is_default(struct fdisk_partition *pa);
+
+int fdisk_partition_set_size(struct fdisk_partition *pa, uint64_t sz);
+int fdisk_partition_unset_size(struct fdisk_partition *pa);
+uint64_t fdisk_partition_get_size(struct fdisk_partition *pa);
+int fdisk_partition_has_size(struct fdisk_partition *pa);
+int fdisk_partition_size_explicit(struct fdisk_partition *pa, int enable);
+
+int fdisk_partition_has_end(struct fdisk_partition *pa);
+fdisk_sector_t fdisk_partition_get_end(struct fdisk_partition *pa);
+
+int fdisk_partition_set_partno(struct fdisk_partition *pa, size_t num);
+int fdisk_partition_unset_partno(struct fdisk_partition *pa);
+size_t fdisk_partition_get_partno(struct fdisk_partition *pa);
+int fdisk_partition_has_partno(struct fdisk_partition *pa);
+int fdisk_partition_cmp_partno(struct fdisk_partition *a,
+	                               struct fdisk_partition *b);
+int fdisk_partition_partno_follow_default(struct fdisk_partition *pa, int enable);
+
+
+extern int fdisk_partition_set_type(struct fdisk_partition *pa, struct fdisk_parttype *type);
+extern struct fdisk_parttype *fdisk_partition_get_type(struct fdisk_partition *pa);
+extern int fdisk_partition_set_name(struct fdisk_partition *pa, const char *name);
+extern const char *fdisk_partition_get_name(struct fdisk_partition *pa);
+extern int fdisk_partition_set_uuid(struct fdisk_partition *pa, const char *uuid);
+extern int fdisk_partition_set_attrs(struct fdisk_partition *pa, const char *attrs);
+extern const char *fdisk_partition_get_uuid(struct fdisk_partition *pa);
+extern const char *fdisk_partition_get_attrs(struct fdisk_partition *pa);
+extern int fdisk_partition_is_nested(struct fdisk_partition *pa);
+extern int fdisk_partition_is_container(struct fdisk_partition *pa);
+extern int fdisk_partition_get_parent(struct fdisk_partition *pa, size_t *parent);
+extern int fdisk_partition_is_used(struct fdisk_partition *pa);
+extern int fdisk_partition_is_bootable(struct fdisk_partition *pa);
+extern int fdisk_partition_to_string(struct fdisk_partition *pa,
+				     struct fdisk_context *cxt,
+				     int id, char **data);
+
+int fdisk_partition_next_partno(struct fdisk_partition *pa,
+				       struct fdisk_context *cxt,
+				       size_t *n);
+
+extern int fdisk_partition_end_follow_default(struct fdisk_partition *pa, int enable);
+extern int fdisk_partition_end_is_default(struct fdisk_partition *pa);
+
+extern int fdisk_reorder_partitions(struct fdisk_context *cxt);
+
+/* table.c */
+extern struct fdisk_table *fdisk_new_table(void);
+extern int fdisk_reset_table(struct fdisk_table *tb);
+extern void fdisk_ref_table(struct fdisk_table *tb);
+extern void fdisk_unref_table(struct fdisk_table *tb);
+extern size_t fdisk_table_get_nents(struct fdisk_table *tb);
+extern int fdisk_table_is_empty(struct fdisk_table *tb);
+extern int fdisk_table_add_partition(struct fdisk_table *tb, struct fdisk_partition *pa);
+extern int fdisk_table_remove_partition(struct fdisk_table *tb, struct fdisk_partition *pa);
+
+extern int fdisk_get_partitions(struct fdisk_context *cxt, struct fdisk_table **tb);
+extern int fdisk_get_freespaces(struct fdisk_context *cxt, struct fdisk_table **tb);
+
+extern int fdisk_table_wrong_order(struct fdisk_table *tb);
+extern int fdisk_table_sort_partitions(struct fdisk_table *tb,
+			int (*cmp)(struct fdisk_partition *,
+				   struct fdisk_partition *));
+
+extern int fdisk_table_next_partition(
+			struct fdisk_table *tb,
+			struct fdisk_iter *itr,
+			struct fdisk_partition **pa);
+
+extern struct fdisk_partition *fdisk_table_get_partition(
+			struct fdisk_table *tb,
+			size_t n);
+extern int fdisk_apply_table(struct fdisk_context *cxt, struct fdisk_table *tb);
+
+/* alignment.c */
+#define FDISK_ALIGN_UP		1
+#define FDISK_ALIGN_DOWN	2
+#define FDISK_ALIGN_NEAREST	3
+
+fdisk_sector_t fdisk_align_lba(struct fdisk_context *cxt, fdisk_sector_t lba, int direction);
+fdisk_sector_t fdisk_align_lba_in_range(struct fdisk_context *cxt,
+				  fdisk_sector_t lba, fdisk_sector_t start, fdisk_sector_t stop);
+int fdisk_lba_is_phy_aligned(struct fdisk_context *cxt, fdisk_sector_t lba);
+
+int fdisk_override_geometry(struct fdisk_context *cxt,
+			    unsigned int cylinders,
+			    unsigned int heads,
+			    unsigned int sectors);
+int fdisk_save_user_geometry(struct fdisk_context *cxt,
+			    unsigned int cylinders,
+			    unsigned int heads,
+			    unsigned int sectors);
+int fdisk_save_user_sector_size(struct fdisk_context *cxt,
+				unsigned int phy,
+				unsigned int log);
+int fdisk_has_user_device_properties(struct fdisk_context *cxt);
+int fdisk_reset_alignment(struct fdisk_context *cxt);
+int fdisk_reset_device_properties(struct fdisk_context *cxt);
+int fdisk_reread_partition_table(struct fdisk_context *cxt);
+
+/* iter.c */
+enum {
+
+	FDISK_ITER_FORWARD = 0,
+	FDISK_ITER_BACKWARD
+};
+extern struct fdisk_iter *fdisk_new_iter(int direction);
+extern void fdisk_free_iter(struct fdisk_iter *itr);
+extern void fdisk_reset_iter(struct fdisk_iter *itr, int direction);
+extern int fdisk_iter_get_direction(struct fdisk_iter *itr);
+
+
+/* dos.c */
+#define DOS_FLAG_ACTIVE	1
+
+extern int fdisk_dos_move_begin(struct fdisk_context *cxt, size_t i);
+extern int fdisk_dos_enable_compatible(struct fdisk_label *lb, int enable);
+extern int fdisk_dos_is_compatible(struct fdisk_label *lb);
+
+/* sun.h */
+extern int fdisk_sun_set_alt_cyl(struct fdisk_context *cxt);
+extern int fdisk_sun_set_xcyl(struct fdisk_context *cxt);
+extern int fdisk_sun_set_ilfact(struct fdisk_context *cxt);
+extern int fdisk_sun_set_rspeed(struct fdisk_context *cxt);
+extern int fdisk_sun_set_pcylcount(struct fdisk_context *cxt);
+
+/* bsd.c */
+extern int fdisk_bsd_edit_disklabel(struct fdisk_context *cxt);
+extern int fdisk_bsd_write_bootstrap(struct fdisk_context *cxt);
+extern int fdisk_bsd_link_partition(struct fdisk_context *cxt);
+
+/* sgi.h */
+#define SGI_FLAG_BOOT	1
+#define SGI_FLAG_SWAP	2
+extern int fdisk_sgi_set_bootfile(struct fdisk_context *cxt);
+extern int fdisk_sgi_create_info(struct fdisk_context *cxt);
+
+/* gpt */
+
+/* GPT partition attributes */
+enum {
+	/* System partition (disk partitioning utilities must preserve the
+	 * partition as is) */
+	GPT_FLAG_REQUIRED = 1,
+
+	/* EFI firmware should ignore the content of the partition and not try
+	 * to read from it */
+	GPT_FLAG_NOBLOCK,
+
+	/* Legacy BIOS bootable  */
+	GPT_FLAG_LEGACYBOOT,
+
+	/* bites 48-63, Defined and used by the individual partition type.
+	 *
+	 * The flag GPT_FLAG_GUIDSPECIFIC forces libfdisk to ask (by ask API)
+	 * for a bit number. If you want to toggle specific bit and avoid any
+	 * dialog, then use the bit number (in range 48..63). For example:
+	 *
+	 * // start dialog to ask for bit number
+	 * fdisk_toggle_partition_flag(cxt, n, GPT_FLAG_GUIDSPECIFIC);
+	 *
+	 * // toggle bit 60
+	 * fdisk_toggle_partition_flag(cxt, n, 60);
+	 */
+	GPT_FLAG_GUIDSPECIFIC
+};
+
+extern int fdisk_gpt_is_hybrid(struct fdisk_context *cxt);
+
+
+/* script.c */
+struct fdisk_script *fdisk_new_script(struct fdisk_context *cxt);
+struct fdisk_script *fdisk_new_script_from_file(struct fdisk_context *cxt,
+						 const char *filename);
+void fdisk_ref_script(struct fdisk_script *dp);
+void fdisk_unref_script(struct fdisk_script *dp);
+
+const char *fdisk_script_get_header(struct fdisk_script *dp, const char *name);
+int fdisk_script_set_header(struct fdisk_script *dp, const char *name, const char *data);
+struct fdisk_table *fdisk_script_get_table(struct fdisk_script *dp);
+int fdisk_script_get_nlines(struct fdisk_script *dp);
+
+int fdisk_script_read_context(struct fdisk_script *dp, struct fdisk_context *cxt);
+int fdisk_script_write_file(struct fdisk_script *dp, FILE *f);
+int fdisk_script_read_file(struct fdisk_script *dp, FILE *f);
+int fdisk_script_read_line(struct fdisk_script *dp, FILE *f, char *buf, size_t bufsz);
+
+int fdisk_set_script(struct fdisk_context *cxt, struct fdisk_script *dp);
+struct fdisk_script *fdisk_get_script(struct fdisk_context *cxt);
+
+int fdisk_apply_script_headers(struct fdisk_context *cxt, struct fdisk_script *dp);
+int fdisk_apply_script(struct fdisk_context *cxt, struct fdisk_script *dp);
+
+
+/* ask.c */
+#define fdisk_is_ask(a, x) (fdisk_ask_get_type(a) == FDISK_ASKTYPE_ ## x)
+
+int fdisk_set_ask(struct fdisk_context *cxt,
+		int (*ask_cb)(struct fdisk_context *, struct fdisk_ask *, void *),
+		void *data);
+
+
+void fdisk_ref_ask(struct fdisk_ask *ask);
+void fdisk_unref_ask(struct fdisk_ask *ask);
+const char *fdisk_ask_get_query(struct fdisk_ask *ask);
+int fdisk_ask_get_type(struct fdisk_ask *ask);
+const char *fdisk_ask_number_get_range(struct fdisk_ask *ask);
+uint64_t fdisk_ask_number_get_default(struct fdisk_ask *ask);
+uint64_t fdisk_ask_number_get_low(struct fdisk_ask *ask);
+uint64_t fdisk_ask_number_get_high(struct fdisk_ask *ask);
+uint64_t fdisk_ask_number_get_result(struct fdisk_ask *ask);
+int fdisk_ask_number_set_result(struct fdisk_ask *ask, uint64_t result);
+uint64_t fdisk_ask_number_get_base(struct fdisk_ask *ask);
+uint64_t fdisk_ask_number_get_unit(struct fdisk_ask *ask);
+int fdisk_ask_number_set_relative(struct fdisk_ask *ask, int relative);
+int fdisk_ask_number_inchars(struct fdisk_ask *ask);
+int fdisk_ask_partnum(struct fdisk_context *cxt, size_t *partnum, int wantnew);
+
+int fdisk_ask_number(struct fdisk_context *cxt,
+		     uintmax_t low,
+		     uintmax_t dflt,
+		     uintmax_t high,
+		     const char *query,
+		     uintmax_t *result);
+char *fdisk_ask_string_get_result(struct fdisk_ask *ask);
+int fdisk_ask_string_set_result(struct fdisk_ask *ask, char *result);
+int fdisk_ask_string(struct fdisk_context *cxt,
+		     const char *query,
+		     char **result);
+int fdisk_ask_yesno(struct fdisk_context *cxt,
+		     const char *query,
+		     int *result);
+int fdisk_ask_yesno_get_result(struct fdisk_ask *ask);
+int fdisk_ask_yesno_set_result(struct fdisk_ask *ask, int result);
+int fdisk_ask_menu_get_default(struct fdisk_ask *ask);
+int fdisk_ask_menu_set_result(struct fdisk_ask *ask, int key);
+int fdisk_ask_menu_get_result(struct fdisk_ask *ask, int *key);
+int fdisk_ask_menu_get_item(struct fdisk_ask *ask, size_t idx, int *key,
+			    const char **name, const char **desc);
+size_t fdisk_ask_menu_get_nitems(struct fdisk_ask *ask);
+int fdisk_ask_print_get_errno(struct fdisk_ask *ask);
+const char *fdisk_ask_print_get_mesg(struct fdisk_ask *ask);
+
+int fdisk_info(struct fdisk_context *cxt, const char *fmt, ...);
+int fdisk_warn(struct fdisk_context *cxt, const char *fmt, ...);
+int fdisk_warnx(struct fdisk_context *cxt, const char *fmt, ...);
+
+/* utils.h */
+extern char *fdisk_partname(const char *dev, size_t partno);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBFDISK_H */
diff --git a/libblkid/libfdisk/src/libfdisk.h.in b/libblkid/libfdisk/src/libfdisk.h.in
new file mode 100644
index 0000000..f82d5bd
--- /dev/null
+++ b/libblkid/libfdisk/src/libfdisk.h.in
@@ -0,0 +1,579 @@
+/*
+ * libfdisk.h - libfdisk API
+ *
+ * Copyright (C) 2012-2014 Karel Zak <kzak@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef _LIBFDISK_H
+#define _LIBFDISK_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdint.h>
+
+/**
+ * LIBFDISK_VERSION:
+ *
+ * Library version string
+ */
+#define LIBFDISK_VERSION   "@LIBFDISK_VERSION@"
+
+/**
+ * fdisk_context:
+ *
+ * Basic library handler.
+ */
+struct fdisk_context;
+
+/**
+ * fdisk_label:
+ *
+ * Disk label specific driver and setting.
+ */
+struct fdisk_label;
+
+/**
+ * fdisk_parttype:
+ *
+ * Partition type.
+ */
+struct fdisk_parttype;
+
+/**
+ * fdisk_partition:
+ *
+ * Partition abstraction (and template).
+ */
+struct fdisk_partition;
+
+/**
+ * fdisk_ask:
+ *
+ * Ask API handler for dialogs with users.
+ */
+struct fdisk_ask;
+
+/**
+ * fdisk_iter:
+ *
+ * Unified iterator.
+ */
+struct fdisk_iter;
+
+/**
+ * fdisk_table:
+ *
+ * Container for fdisk_partition objects
+ */
+struct fdisk_table;
+
+/**
+ * fdisk_field
+ *
+ * Output field description.
+ */
+struct fdisk_field;
+
+/**
+ * fdisk_script
+ *
+ * library handler for sfdisk compatible scripts
+ */
+struct fdisk_script;
+
+/**
+ * fdisk_sector_t
+ *
+ * LBA adresses type
+ */
+typedef uint64_t fdisk_sector_t;
+
+/**
+ * fdisk_labeltype:
+ *
+ * Supported partition table types (labels)
+ */
+enum fdisk_labeltype {
+	FDISK_DISKLABEL_DOS = (1 << 1),
+	FDISK_DISKLABEL_SUN = (1 << 2),
+	FDISK_DISKLABEL_SGI = (1 << 3),
+	FDISK_DISKLABEL_BSD = (1 << 4),
+	FDISK_DISKLABEL_GPT = (1 << 5)
+};
+
+/**
+ * fdisk_asktype:
+ *
+ * Ask API dialog types
+ */
+enum fdisk_asktype {
+	FDISK_ASKTYPE_NONE = 0,
+	FDISK_ASKTYPE_NUMBER,
+	FDISK_ASKTYPE_OFFSET,
+	FDISK_ASKTYPE_WARN,
+	FDISK_ASKTYPE_WARNX,
+	FDISK_ASKTYPE_INFO,
+	FDISK_ASKTYPE_YESNO,
+	FDISK_ASKTYPE_STRING,
+	FDISK_ASKTYPE_MENU
+};
+
+/* init.c */
+extern void fdisk_init_debug(int mask);
+
+/* context.h */
+
+#define FDISK_PLURAL	0
+#define FDISK_SINGULAR	1
+
+struct fdisk_context *fdisk_new_context(void);
+struct fdisk_context *fdisk_new_nested_context(struct fdisk_context *parent, const char *name);
+void fdisk_unref_context(struct fdisk_context *cxt);
+void fdisk_ref_context(struct fdisk_context *cxt);
+
+struct fdisk_context *fdisk_get_parent(struct fdisk_context *cxt);
+size_t fdisk_get_npartitions(struct fdisk_context *cxt);
+
+struct fdisk_label *fdisk_get_label(struct fdisk_context *cxt, const char *name);
+int fdisk_next_label(struct fdisk_context *cxt, struct fdisk_label **lb);
+size_t fdisk_get_nlabels(struct fdisk_context *cxt);
+
+int fdisk_has_label(struct fdisk_context *cxt);
+int fdisk_is_labeltype(struct fdisk_context *cxt, enum fdisk_labeltype id);
+#define fdisk_is_label(c, x) fdisk_is_labeltype(c, FDISK_DISKLABEL_ ## x)
+
+
+int fdisk_assign_device(struct fdisk_context *cxt,
+			const char *fname, int readonly);
+int fdisk_deassign_device(struct fdisk_context *cxt, int nosync);
+int fdisk_is_readonly(struct fdisk_context *cxt);
+
+int fdisk_enable_details(struct fdisk_context *cxt, int enable);
+int fdisk_is_details(struct fdisk_context *cxt);
+
+int fdisk_enable_listonly(struct fdisk_context *cxt, int enable);
+int fdisk_is_listonly(struct fdisk_context *cxt);
+
+int fdisk_set_unit(struct fdisk_context *cxt, const char *str);
+const char *fdisk_get_unit(struct fdisk_context *cxt, int n);
+int fdisk_use_cylinders(struct fdisk_context *cxt);
+unsigned int fdisk_get_units_per_sector(struct fdisk_context *cxt);
+
+unsigned long fdisk_get_optimal_iosize(struct fdisk_context *cxt);
+unsigned long fdisk_get_minimal_iosize(struct fdisk_context *cxt);
+unsigned long fdisk_get_physector_size(struct fdisk_context *cxt);
+unsigned long fdisk_get_sector_size(struct fdisk_context *cxt);
+unsigned long fdisk_get_alignment_offset(struct fdisk_context *cxt);
+unsigned long fdisk_get_grain_size(struct fdisk_context *cxt);
+fdisk_sector_t fdisk_get_first_lba(struct fdisk_context *cxt);
+fdisk_sector_t fdisk_set_first_lba(struct fdisk_context *cxt, fdisk_sector_t lba);
+fdisk_sector_t fdisk_get_last_lba(struct fdisk_context *cxt);
+fdisk_sector_t fdisk_set_last_lba(struct fdisk_context *cxt, fdisk_sector_t lba);
+fdisk_sector_t fdisk_get_nsectors(struct fdisk_context *cxt);
+const char *fdisk_get_devname(struct fdisk_context *cxt);
+int fdisk_get_devfd(struct fdisk_context *cxt);
+
+unsigned int fdisk_get_geom_heads(struct fdisk_context *cxt);
+fdisk_sector_t fdisk_get_geom_sectors(struct fdisk_context *cxt);
+fdisk_sector_t fdisk_get_geom_cylinders(struct fdisk_context *cxt);
+
+
+
+/* parttype.c */
+struct fdisk_parttype *fdisk_new_parttype(void);
+void fdisk_ref_parttype(struct fdisk_parttype *t);
+void fdisk_unref_parttype(struct fdisk_parttype *t);
+int fdisk_parttype_set_name(struct fdisk_parttype *t, const char *str);
+int fdisk_parttype_set_typestr(struct fdisk_parttype *t, const char *str);
+int fdisk_parttype_set_code(struct fdisk_parttype *t, int code);
+size_t fdisk_label_get_nparttypes(const struct fdisk_label *lb);
+struct fdisk_parttype *fdisk_label_get_parttype(const struct fdisk_label *lb, size_t n);
+int fdisk_label_has_code_parttypes(const struct fdisk_label *lb);
+struct fdisk_parttype *fdisk_label_get_parttype_from_code(
+				const struct fdisk_label *lb,
+				unsigned int code);
+struct fdisk_parttype *fdisk_label_get_parttype_from_string(
+				const struct fdisk_label *lb,
+				const char *str);
+struct fdisk_parttype *fdisk_new_unknown_parttype(unsigned int code,
+						  const char *typestr);
+struct fdisk_parttype *fdisk_copy_parttype(const struct fdisk_parttype *type);
+struct fdisk_parttype *fdisk_label_parse_parttype(
+				const struct fdisk_label *lb,
+				const char *str);
+const char *fdisk_parttype_get_string(const struct fdisk_parttype *t);
+unsigned int fdisk_parttype_get_code(const struct fdisk_parttype *t);
+const char *fdisk_parttype_get_name(const struct fdisk_parttype *t);
+int fdisk_parttype_is_unknown(const struct fdisk_parttype *t);
+
+/* label.c */
+
+/**
+ * fdisk_fieldtype
+ *
+ * Types of fdisk_field
+ */
+enum fdisk_fieldtype {
+	FDISK_FIELD_NONE = 0,
+
+	/* generic */
+	FDISK_FIELD_DEVICE,
+	FDISK_FIELD_START,
+	FDISK_FIELD_END,
+	FDISK_FIELD_SECTORS,
+	FDISK_FIELD_CYLINDERS,
+	FDISK_FIELD_SIZE,
+	FDISK_FIELD_TYPE,
+	FDISK_FIELD_TYPEID,
+
+	/* label specific */
+	FDISK_FIELD_ATTR,
+	FDISK_FIELD_BOOT,
+	FDISK_FIELD_BSIZE,
+	FDISK_FIELD_CPG,
+	FDISK_FIELD_EADDR,
+	FDISK_FIELD_FSIZE,
+	FDISK_FIELD_NAME,
+	FDISK_FIELD_SADDR,
+	FDISK_FIELD_UUID,
+
+	FDISK_NFIELDS		/* must be last */
+};
+
+int fdisk_label_get_type(const struct fdisk_label *lb);
+const char *fdisk_label_get_name(const struct fdisk_label *lb);
+int fdisk_label_require_geometry(const struct fdisk_label *lb);
+
+
+extern int fdisk_write_disklabel(struct fdisk_context *cxt);
+extern int fdisk_verify_disklabel(struct fdisk_context *cxt);
+extern int fdisk_create_disklabel(struct fdisk_context *cxt, const char *name);
+extern int fdisk_list_disklabel(struct fdisk_context *cxt);
+extern int fdisk_locate_disklabel(struct fdisk_context *cxt, int n, const char **name, off_t *offset, size_t *size);
+
+extern int fdisk_get_disklabel_id(struct fdisk_context *cxt, char **id);
+extern int fdisk_set_disklabel_id(struct fdisk_context *cxt);
+
+extern int fdisk_get_partition(struct fdisk_context *cxt, size_t partno, struct fdisk_partition **pa);
+extern int fdisk_set_partition(struct fdisk_context *cxt, size_t partno, struct fdisk_partition *pa);
+extern int fdisk_add_partition(struct fdisk_context *cxt, struct fdisk_partition *pa, size_t *partno);
+extern int fdisk_delete_partition(struct fdisk_context *cxt, size_t partno);
+
+extern int fdisk_delete_all_partitions(struct fdisk_context *cxt);
+
+extern int fdisk_set_partition_type(struct fdisk_context *cxt, size_t partnum,
+			     struct fdisk_parttype *t);
+
+
+extern int fdisk_label_get_fields_ids(
+			const struct fdisk_label *lb,
+			struct fdisk_context *cxt,
+			int **ids, size_t *nids);
+
+extern const struct fdisk_field *fdisk_label_get_field(const struct fdisk_label *lb, int id);
+extern const struct fdisk_field *fdisk_label_get_field_by_name(
+			const struct fdisk_label *lb,
+			const char *name);
+
+extern int fdisk_field_get_id(const struct fdisk_field *field);
+extern const char *fdisk_field_get_name(const struct fdisk_field *field);
+extern double fdisk_field_get_width(const struct fdisk_field *field);
+extern int fdisk_field_is_number(const struct fdisk_field *field);
+
+
+extern void fdisk_label_set_changed(struct fdisk_label *lb, int changed);
+extern int fdisk_label_is_changed(const struct fdisk_label *lb);
+
+extern void fdisk_label_set_disabled(struct fdisk_label *lb, int disabled);
+extern int fdisk_label_is_disabled(const struct fdisk_label *lb);
+
+extern int fdisk_is_partition_used(struct fdisk_context *cxt, size_t n);
+
+extern int fdisk_toggle_partition_flag(struct fdisk_context *cxt, size_t partnum, unsigned long flag);
+
+extern struct fdisk_partition *fdisk_new_partition(void);
+extern void fdisk_reset_partition(struct fdisk_partition *pa);
+extern void fdisk_ref_partition(struct fdisk_partition *pa);
+extern void fdisk_unref_partition(struct fdisk_partition *pa);
+extern int fdisk_partition_is_freespace(struct fdisk_partition *pa);
+
+int fdisk_partition_set_start(struct fdisk_partition *pa, uint64_t off);
+int fdisk_partition_unset_start(struct fdisk_partition *pa);
+uint64_t fdisk_partition_get_start(struct fdisk_partition *pa);
+int fdisk_partition_has_start(struct fdisk_partition *pa);
+int fdisk_partition_cmp_start(struct fdisk_partition *a,
+			      struct fdisk_partition *b);
+int fdisk_partition_start_follow_default(struct fdisk_partition *pa, int enable);
+int fdisk_partition_start_is_default(struct fdisk_partition *pa);
+
+int fdisk_partition_set_size(struct fdisk_partition *pa, uint64_t sz);
+int fdisk_partition_unset_size(struct fdisk_partition *pa);
+uint64_t fdisk_partition_get_size(struct fdisk_partition *pa);
+int fdisk_partition_has_size(struct fdisk_partition *pa);
+int fdisk_partition_size_explicit(struct fdisk_partition *pa, int enable);
+
+int fdisk_partition_has_end(struct fdisk_partition *pa);
+fdisk_sector_t fdisk_partition_get_end(struct fdisk_partition *pa);
+
+int fdisk_partition_set_partno(struct fdisk_partition *pa, size_t num);
+int fdisk_partition_unset_partno(struct fdisk_partition *pa);
+size_t fdisk_partition_get_partno(struct fdisk_partition *pa);
+int fdisk_partition_has_partno(struct fdisk_partition *pa);
+int fdisk_partition_cmp_partno(struct fdisk_partition *a,
+	                               struct fdisk_partition *b);
+int fdisk_partition_partno_follow_default(struct fdisk_partition *pa, int enable);
+
+
+extern int fdisk_partition_set_type(struct fdisk_partition *pa, struct fdisk_parttype *type);
+extern struct fdisk_parttype *fdisk_partition_get_type(struct fdisk_partition *pa);
+extern int fdisk_partition_set_name(struct fdisk_partition *pa, const char *name);
+extern const char *fdisk_partition_get_name(struct fdisk_partition *pa);
+extern int fdisk_partition_set_uuid(struct fdisk_partition *pa, const char *uuid);
+extern int fdisk_partition_set_attrs(struct fdisk_partition *pa, const char *attrs);
+extern const char *fdisk_partition_get_uuid(struct fdisk_partition *pa);
+extern const char *fdisk_partition_get_attrs(struct fdisk_partition *pa);
+extern int fdisk_partition_is_nested(struct fdisk_partition *pa);
+extern int fdisk_partition_is_container(struct fdisk_partition *pa);
+extern int fdisk_partition_get_parent(struct fdisk_partition *pa, size_t *parent);
+extern int fdisk_partition_is_used(struct fdisk_partition *pa);
+extern int fdisk_partition_is_bootable(struct fdisk_partition *pa);
+extern int fdisk_partition_to_string(struct fdisk_partition *pa,
+				     struct fdisk_context *cxt,
+				     int id, char **data);
+
+int fdisk_partition_next_partno(struct fdisk_partition *pa,
+				       struct fdisk_context *cxt,
+				       size_t *n);
+
+extern int fdisk_partition_end_follow_default(struct fdisk_partition *pa, int enable);
+extern int fdisk_partition_end_is_default(struct fdisk_partition *pa);
+
+extern int fdisk_reorder_partitions(struct fdisk_context *cxt);
+
+/* table.c */
+extern struct fdisk_table *fdisk_new_table(void);
+extern int fdisk_reset_table(struct fdisk_table *tb);
+extern void fdisk_ref_table(struct fdisk_table *tb);
+extern void fdisk_unref_table(struct fdisk_table *tb);
+extern size_t fdisk_table_get_nents(struct fdisk_table *tb);
+extern int fdisk_table_is_empty(struct fdisk_table *tb);
+extern int fdisk_table_add_partition(struct fdisk_table *tb, struct fdisk_partition *pa);
+extern int fdisk_table_remove_partition(struct fdisk_table *tb, struct fdisk_partition *pa);
+
+extern int fdisk_get_partitions(struct fdisk_context *cxt, struct fdisk_table **tb);
+extern int fdisk_get_freespaces(struct fdisk_context *cxt, struct fdisk_table **tb);
+
+extern int fdisk_table_wrong_order(struct fdisk_table *tb);
+extern int fdisk_table_sort_partitions(struct fdisk_table *tb,
+			int (*cmp)(struct fdisk_partition *,
+				   struct fdisk_partition *));
+
+extern int fdisk_table_next_partition(
+			struct fdisk_table *tb,
+			struct fdisk_iter *itr,
+			struct fdisk_partition **pa);
+
+extern struct fdisk_partition *fdisk_table_get_partition(
+			struct fdisk_table *tb,
+			size_t n);
+extern int fdisk_apply_table(struct fdisk_context *cxt, struct fdisk_table *tb);
+
+/* alignment.c */
+#define FDISK_ALIGN_UP		1
+#define FDISK_ALIGN_DOWN	2
+#define FDISK_ALIGN_NEAREST	3
+
+fdisk_sector_t fdisk_align_lba(struct fdisk_context *cxt, fdisk_sector_t lba, int direction);
+fdisk_sector_t fdisk_align_lba_in_range(struct fdisk_context *cxt,
+				  fdisk_sector_t lba, fdisk_sector_t start, fdisk_sector_t stop);
+int fdisk_lba_is_phy_aligned(struct fdisk_context *cxt, fdisk_sector_t lba);
+
+int fdisk_override_geometry(struct fdisk_context *cxt,
+			    unsigned int cylinders,
+			    unsigned int heads,
+			    unsigned int sectors);
+int fdisk_save_user_geometry(struct fdisk_context *cxt,
+			    unsigned int cylinders,
+			    unsigned int heads,
+			    unsigned int sectors);
+int fdisk_save_user_sector_size(struct fdisk_context *cxt,
+				unsigned int phy,
+				unsigned int log);
+int fdisk_has_user_device_properties(struct fdisk_context *cxt);
+int fdisk_reset_alignment(struct fdisk_context *cxt);
+int fdisk_reset_device_properties(struct fdisk_context *cxt);
+int fdisk_reread_partition_table(struct fdisk_context *cxt);
+
+/* iter.c */
+enum {
+
+	FDISK_ITER_FORWARD = 0,
+	FDISK_ITER_BACKWARD
+};
+extern struct fdisk_iter *fdisk_new_iter(int direction);
+extern void fdisk_free_iter(struct fdisk_iter *itr);
+extern void fdisk_reset_iter(struct fdisk_iter *itr, int direction);
+extern int fdisk_iter_get_direction(struct fdisk_iter *itr);
+
+
+/* dos.c */
+#define DOS_FLAG_ACTIVE	1
+
+extern int fdisk_dos_move_begin(struct fdisk_context *cxt, size_t i);
+extern int fdisk_dos_enable_compatible(struct fdisk_label *lb, int enable);
+extern int fdisk_dos_is_compatible(struct fdisk_label *lb);
+
+/* sun.h */
+extern int fdisk_sun_set_alt_cyl(struct fdisk_context *cxt);
+extern int fdisk_sun_set_xcyl(struct fdisk_context *cxt);
+extern int fdisk_sun_set_ilfact(struct fdisk_context *cxt);
+extern int fdisk_sun_set_rspeed(struct fdisk_context *cxt);
+extern int fdisk_sun_set_pcylcount(struct fdisk_context *cxt);
+
+/* bsd.c */
+extern int fdisk_bsd_edit_disklabel(struct fdisk_context *cxt);
+extern int fdisk_bsd_write_bootstrap(struct fdisk_context *cxt);
+extern int fdisk_bsd_link_partition(struct fdisk_context *cxt);
+
+/* sgi.h */
+#define SGI_FLAG_BOOT	1
+#define SGI_FLAG_SWAP	2
+extern int fdisk_sgi_set_bootfile(struct fdisk_context *cxt);
+extern int fdisk_sgi_create_info(struct fdisk_context *cxt);
+
+/* gpt */
+
+/* GPT partition attributes */
+enum {
+	/* System partition (disk partitioning utilities must preserve the
+	 * partition as is) */
+	GPT_FLAG_REQUIRED = 1,
+
+	/* EFI firmware should ignore the content of the partition and not try
+	 * to read from it */
+	GPT_FLAG_NOBLOCK,
+
+	/* Legacy BIOS bootable  */
+	GPT_FLAG_LEGACYBOOT,
+
+	/* bites 48-63, Defined and used by the individual partition type.
+	 *
+	 * The flag GPT_FLAG_GUIDSPECIFIC forces libfdisk to ask (by ask API)
+	 * for a bit number. If you want to toggle specific bit and avoid any
+	 * dialog, then use the bit number (in range 48..63). For example:
+	 *
+	 * // start dialog to ask for bit number
+	 * fdisk_toggle_partition_flag(cxt, n, GPT_FLAG_GUIDSPECIFIC);
+	 *
+	 * // toggle bit 60
+	 * fdisk_toggle_partition_flag(cxt, n, 60);
+	 */
+	GPT_FLAG_GUIDSPECIFIC
+};
+
+extern int fdisk_gpt_is_hybrid(struct fdisk_context *cxt);
+
+
+/* script.c */
+struct fdisk_script *fdisk_new_script(struct fdisk_context *cxt);
+struct fdisk_script *fdisk_new_script_from_file(struct fdisk_context *cxt,
+						 const char *filename);
+void fdisk_ref_script(struct fdisk_script *dp);
+void fdisk_unref_script(struct fdisk_script *dp);
+
+const char *fdisk_script_get_header(struct fdisk_script *dp, const char *name);
+int fdisk_script_set_header(struct fdisk_script *dp, const char *name, const char *data);
+struct fdisk_table *fdisk_script_get_table(struct fdisk_script *dp);
+int fdisk_script_get_nlines(struct fdisk_script *dp);
+
+int fdisk_script_read_context(struct fdisk_script *dp, struct fdisk_context *cxt);
+int fdisk_script_write_file(struct fdisk_script *dp, FILE *f);
+int fdisk_script_read_file(struct fdisk_script *dp, FILE *f);
+int fdisk_script_read_line(struct fdisk_script *dp, FILE *f, char *buf, size_t bufsz);
+
+int fdisk_set_script(struct fdisk_context *cxt, struct fdisk_script *dp);
+struct fdisk_script *fdisk_get_script(struct fdisk_context *cxt);
+
+int fdisk_apply_script_headers(struct fdisk_context *cxt, struct fdisk_script *dp);
+int fdisk_apply_script(struct fdisk_context *cxt, struct fdisk_script *dp);
+
+
+/* ask.c */
+#define fdisk_is_ask(a, x) (fdisk_ask_get_type(a) == FDISK_ASKTYPE_ ## x)
+
+int fdisk_set_ask(struct fdisk_context *cxt,
+		int (*ask_cb)(struct fdisk_context *, struct fdisk_ask *, void *),
+		void *data);
+
+
+void fdisk_ref_ask(struct fdisk_ask *ask);
+void fdisk_unref_ask(struct fdisk_ask *ask);
+const char *fdisk_ask_get_query(struct fdisk_ask *ask);
+int fdisk_ask_get_type(struct fdisk_ask *ask);
+const char *fdisk_ask_number_get_range(struct fdisk_ask *ask);
+uint64_t fdisk_ask_number_get_default(struct fdisk_ask *ask);
+uint64_t fdisk_ask_number_get_low(struct fdisk_ask *ask);
+uint64_t fdisk_ask_number_get_high(struct fdisk_ask *ask);
+uint64_t fdisk_ask_number_get_result(struct fdisk_ask *ask);
+int fdisk_ask_number_set_result(struct fdisk_ask *ask, uint64_t result);
+uint64_t fdisk_ask_number_get_base(struct fdisk_ask *ask);
+uint64_t fdisk_ask_number_get_unit(struct fdisk_ask *ask);
+int fdisk_ask_number_set_relative(struct fdisk_ask *ask, int relative);
+int fdisk_ask_number_inchars(struct fdisk_ask *ask);
+int fdisk_ask_partnum(struct fdisk_context *cxt, size_t *partnum, int wantnew);
+
+int fdisk_ask_number(struct fdisk_context *cxt,
+		     uintmax_t low,
+		     uintmax_t dflt,
+		     uintmax_t high,
+		     const char *query,
+		     uintmax_t *result);
+char *fdisk_ask_string_get_result(struct fdisk_ask *ask);
+int fdisk_ask_string_set_result(struct fdisk_ask *ask, char *result);
+int fdisk_ask_string(struct fdisk_context *cxt,
+		     const char *query,
+		     char **result);
+int fdisk_ask_yesno(struct fdisk_context *cxt,
+		     const char *query,
+		     int *result);
+int fdisk_ask_yesno_get_result(struct fdisk_ask *ask);
+int fdisk_ask_yesno_set_result(struct fdisk_ask *ask, int result);
+int fdisk_ask_menu_get_default(struct fdisk_ask *ask);
+int fdisk_ask_menu_set_result(struct fdisk_ask *ask, int key);
+int fdisk_ask_menu_get_result(struct fdisk_ask *ask, int *key);
+int fdisk_ask_menu_get_item(struct fdisk_ask *ask, size_t idx, int *key,
+			    const char **name, const char **desc);
+size_t fdisk_ask_menu_get_nitems(struct fdisk_ask *ask);
+int fdisk_ask_print_get_errno(struct fdisk_ask *ask);
+const char *fdisk_ask_print_get_mesg(struct fdisk_ask *ask);
+
+int fdisk_info(struct fdisk_context *cxt, const char *fmt, ...);
+int fdisk_warn(struct fdisk_context *cxt, const char *fmt, ...);
+int fdisk_warnx(struct fdisk_context *cxt, const char *fmt, ...);
+
+/* utils.h */
+extern char *fdisk_partname(const char *dev, size_t partno);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBFDISK_H */
diff --git a/libblkid/libfdisk/src/libfdisk.sym b/libblkid/libfdisk/src/libfdisk.sym
new file mode 100644
index 0000000..bf85d4e
--- /dev/null
+++ b/libblkid/libfdisk/src/libfdisk.sym
@@ -0,0 +1,234 @@
+/*
+ * The symbol versioning ensures that a new application requiring symbol foo;
+ * can't run with old libblkid.so not providing foo;
+ * version info can't enforce this since we never change the SONAME.
+ *
+ * Copyright (C) 2014 Karel Zak <kzak@redhat.com>
+ */
+MOUNT_2.26 {
+global:
+	fdisk_add_partition;
+	fdisk_align_lba;
+	fdisk_align_lba_in_range;
+	fdisk_apply_script;
+	fdisk_apply_script_headers;
+	fdisk_apply_table;
+	fdisk_ask_get_query;
+	fdisk_ask_get_type;
+	fdisk_ask_menu_get_default;
+	fdisk_ask_menu_get_item;
+	fdisk_ask_menu_get_nitems;
+	fdisk_ask_menu_get_result;
+	fdisk_ask_menu_set_result;
+	fdisk_ask_number;
+	fdisk_ask_number_get_base;
+	fdisk_ask_number_get_default;
+	fdisk_ask_number_get_high;
+	fdisk_ask_number_get_low;
+	fdisk_ask_number_get_range;
+	fdisk_ask_number_get_result;
+	fdisk_ask_number_get_unit;
+	fdisk_ask_number_inchars;
+	fdisk_ask_number_set_relative;
+	fdisk_ask_number_set_result;
+	fdisk_ask_partnum;
+	fdisk_ask_print_get_errno;
+	fdisk_ask_print_get_mesg;
+	fdisk_ask_string;
+	fdisk_ask_string_get_result;
+	fdisk_ask_string_set_result;
+	fdisk_ask_yesno;
+	fdisk_ask_yesno_get_result;
+	fdisk_ask_yesno_set_result;
+	fdisk_assign_device;
+	fdisk_bsd_edit_disklabel;
+	fdisk_bsd_link_partition;
+	fdisk_bsd_write_bootstrap;
+	fdisk_copy_parttype;
+	fdisk_create_disklabel;
+	fdisk_deassign_device;
+	fdisk_delete_all_partitions;
+	fdisk_delete_partition;
+	fdisk_dos_enable_compatible;
+	fdisk_dos_is_compatible;
+	fdisk_dos_move_begin;
+	fdisk_enable_details;
+	fdisk_enable_listonly;
+	fdisk_field_get_id;
+	fdisk_field_get_name;
+	fdisk_field_get_width;
+	fdisk_field_is_number;
+	fdisk_free_iter;
+	fdisk_get_alignment_offset;
+	fdisk_get_devfd;
+	fdisk_get_devname;
+	fdisk_get_disklabel_id;
+	fdisk_get_first_lba;
+	fdisk_get_freespaces;
+	fdisk_get_geom_cylinders;
+	fdisk_get_geom_heads;
+	fdisk_get_geom_sectors;
+	fdisk_get_grain_size;
+	fdisk_get_label;
+	fdisk_get_last_lba;
+	fdisk_get_minimal_iosize;
+	fdisk_get_nlabels;
+	fdisk_get_npartitions;
+	fdisk_get_nsectors;
+	fdisk_get_optimal_iosize;
+	fdisk_get_parent;
+	fdisk_get_partition;
+	fdisk_get_partitions;
+	fdisk_get_physector_size;
+	fdisk_get_script;
+	fdisk_get_sector_size;
+	fdisk_get_unit;
+	fdisk_get_units_per_sector;
+	fdisk_gpt_is_hybrid;
+	fdisk_has_label;
+	fdisk_has_user_device_properties;
+	fdisk_info;
+	fdisk_init_debug;
+	fdisk_is_details;
+	fdisk_is_labeltype;
+	fdisk_is_listonly;
+	fdisk_is_partition_used;
+	fdisk_is_readonly;
+	fdisk_iter_get_direction;
+	fdisk_label_get_field;
+	fdisk_label_get_field_by_name;
+	fdisk_label_get_fields_ids;
+	fdisk_label_get_name;
+	fdisk_label_get_nparttypes;
+	fdisk_label_get_parttype;
+	fdisk_label_get_parttype_from_code;
+	fdisk_label_get_parttype_from_string;
+	fdisk_label_get_type;
+	fdisk_label_has_code_parttypes;
+	fdisk_label_is_changed;
+	fdisk_label_is_disabled;
+	fdisk_label_parse_parttype;
+	fdisk_label_require_geometry;
+	fdisk_label_set_changed;
+	fdisk_label_set_disabled;
+	fdisk_lba_is_phy_aligned;
+	fdisk_list_disklabel;
+	fdisk_locate_disklabel;
+	fdisk_new_context;
+	fdisk_new_iter;
+	fdisk_new_nested_context;
+	fdisk_new_partition;
+	fdisk_new_parttype;
+	fdisk_new_script;
+	fdisk_new_script_from_file;
+	fdisk_new_table;
+	fdisk_new_unknown_parttype;
+	fdisk_next_label;
+	fdisk_override_geometry;
+	fdisk_partition_cmp_partno;
+	fdisk_partition_cmp_start;
+	fdisk_partition_end_follow_default;
+	fdisk_partition_end_is_default;
+	fdisk_partition_get_attrs;
+	fdisk_partition_get_end;
+	fdisk_partition_get_name;
+	fdisk_partition_get_parent;
+	fdisk_partition_get_partno;
+	fdisk_partition_get_size;
+	fdisk_partition_get_start;
+	fdisk_partition_get_type;
+	fdisk_partition_get_uuid;
+	fdisk_partition_has_end;
+	fdisk_partition_has_partno;
+	fdisk_partition_has_size;
+	fdisk_partition_has_start;
+	fdisk_partition_is_bootable;
+	fdisk_partition_is_container;
+	fdisk_partition_is_freespace;
+	fdisk_partition_is_nested;
+	fdisk_partition_is_used;
+	fdisk_partition_next_partno;
+	fdisk_partition_partno_follow_default;
+	fdisk_partition_set_attrs;
+	fdisk_partition_set_name;
+	fdisk_partition_set_partno;
+	fdisk_partition_set_size;
+	fdisk_partition_set_start;
+	fdisk_partition_set_type;
+	fdisk_partition_set_uuid;
+	fdisk_partition_size_explicit;
+	fdisk_partition_start_follow_default;
+	fdisk_partition_start_is_default;
+	fdisk_toggle_partition_flag;
+	fdisk_partition_to_string;
+	fdisk_partition_unset_partno;
+	fdisk_partition_unset_size;
+	fdisk_partition_unset_start;
+	fdisk_partname;
+	fdisk_parttype_get_code;
+	fdisk_parttype_get_name;
+	fdisk_parttype_get_string;
+	fdisk_parttype_is_unknown;
+	fdisk_parttype_set_code;
+	fdisk_parttype_set_name;
+	fdisk_parttype_set_typestr;
+	fdisk_ref_ask;
+	fdisk_ref_context;
+	fdisk_ref_partition;
+	fdisk_ref_parttype;
+	fdisk_ref_script;
+	fdisk_ref_table;
+	fdisk_reorder_partitions;
+	fdisk_reread_partition_table;
+	fdisk_reset_alignment;
+	fdisk_reset_device_properties;
+	fdisk_reset_iter;
+	fdisk_reset_partition;
+	fdisk_reset_table;
+	fdisk_save_user_geometry;
+	fdisk_save_user_sector_size;
+	fdisk_script_get_header;
+	fdisk_script_get_nlines;
+	fdisk_script_get_table;
+	fdisk_script_read_context;
+	fdisk_script_read_file;
+	fdisk_script_read_line;
+	fdisk_script_set_header;
+	fdisk_script_write_file;
+	fdisk_set_ask;
+	fdisk_set_disklabel_id;
+	fdisk_set_first_lba;
+	fdisk_set_last_lba;
+	fdisk_set_partition;
+	fdisk_set_partition_type;
+	fdisk_set_script;
+	fdisk_set_unit;
+	fdisk_sgi_create_info;
+	fdisk_sgi_set_bootfile;
+	fdisk_sun_set_alt_cyl;
+	fdisk_sun_set_ilfact;
+	fdisk_sun_set_pcylcount;
+	fdisk_sun_set_rspeed;
+	fdisk_sun_set_xcyl;
+	fdisk_table_add_partition;
+	fdisk_table_get_nents;
+	fdisk_table_get_partition;
+	fdisk_table_is_empty;
+	fdisk_table_next_partition;
+	fdisk_table_remove_partition;
+	fdisk_table_sort_partitions;
+	fdisk_table_wrong_order;
+	fdisk_unref_ask;
+	fdisk_unref_context;
+	fdisk_unref_partition;
+	fdisk_unref_parttype;
+	fdisk_unref_script;
+	fdisk_unref_table;
+	fdisk_use_cylinders;
+	fdisk_verify_disklabel;
+	fdisk_warn;
+	fdisk_warnx;
+	fdisk_write_disklabel;
+local:
+	*;
+};
diff --git a/libblkid/libfdisk/src/partition.c b/libblkid/libfdisk/src/partition.c
new file mode 100644
index 0000000..8f84027
--- /dev/null
+++ b/libblkid/libfdisk/src/partition.c
@@ -0,0 +1,963 @@
+
+#include "c.h"
+#include "strutils.h"
+
+#include "fdiskP.h"
+
+/**
+ * SECTION: partition
+ * @title: Partition
+ * @short_description: generic label independent partition abstraction
+ *
+ * The fdisk_partition provides label independent abstraction. The partitions
+ * are not directly connected with partition table (label) data. Any change to
+ * fdisk_partition does not affects in-memory or on-disk label data.
+ *
+ * The fdisk_partition is possible to use as a template for
+ * fdisk_add_partition() or fdisk_set_partition() operations.
+ */
+
+static void init_partition(struct fdisk_partition *pa)
+{
+	FDISK_INIT_UNDEF(pa->size);
+	FDISK_INIT_UNDEF(pa->start);
+	FDISK_INIT_UNDEF(pa->partno);
+	FDISK_INIT_UNDEF(pa->parent_partno);
+	FDISK_INIT_UNDEF(pa->boot);
+
+	INIT_LIST_HEAD(&pa->parts);
+}
+
+/**
+ * fdisk_new_partition:
+ *
+ * Returns: new instance.
+ */
+struct fdisk_partition *fdisk_new_partition(void)
+{
+	struct fdisk_partition *pa = calloc(1, sizeof(*pa));
+
+	pa->refcount = 1;
+	init_partition(pa);
+	DBG(PART, ul_debugobj(pa, "alloc"));
+	return pa;
+}
+
+/**
+ * fdisk_reset_partition:
+ * @pa: partition
+ *
+ * Resets partition content.
+ */
+void fdisk_reset_partition(struct fdisk_partition *pa)
+{
+	int ref;
+
+	if (!pa)
+		return;
+
+	DBG(PART, ul_debugobj(pa, "reset"));
+	ref = pa->refcount;
+
+	fdisk_unref_parttype(pa->type);
+	free(pa->name);
+	free(pa->uuid);
+	free(pa->attrs);
+
+	memset(pa, 0, sizeof(*pa));
+	pa->refcount = ref;
+
+	init_partition(pa);
+}
+
+/**
+ * fdisk_ref_partition:
+ * @pa: partition pointer
+ *
+ * Incremparts reference counter.
+ */
+void fdisk_ref_partition(struct fdisk_partition *pa)
+{
+	if (pa)
+		pa->refcount++;
+}
+
+/**
+ * fdisk_unref_partition:
+ * @pa: partition pointer
+ *
+ * De-incremparts reference counter, on zero the @pa is automatically
+ * deallocated.
+ */
+void fdisk_unref_partition(struct fdisk_partition *pa)
+{
+	if (!pa)
+		return;
+
+	pa->refcount--;
+	if (pa->refcount <= 0) {
+		fdisk_reset_partition(pa);
+		list_del(&pa->parts);
+		DBG(PART, ul_debugobj(pa, "free"));
+		free(pa);
+	}
+}
+
+/**
+ * fdisk_partition_set_start:
+ * @pa: partition
+ * @off: offset in sectors, maximal is UINT64_MAX-1
+ *
+ * Note that zero is valid offset too. Use fdisk_partition_unset_start() to
+ * undefine the offset. 
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int fdisk_partition_set_start(struct fdisk_partition *pa, fdisk_sector_t off)
+{
+	if (!pa)
+		return -EINVAL;
+	if (FDISK_IS_UNDEF(off))
+		return -ERANGE;
+	pa->start = off;
+	return 0;
+}
+
+/**
+ * fdisk_partition_unset_start:
+ * @pa: partition
+ *
+ * Sets the size as undefined. See fdisk_partition_has_start().
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int fdisk_partition_unset_start(struct fdisk_partition *pa)
+{
+	if (!pa)
+		return -EINVAL;
+	FDISK_INIT_UNDEF(pa->start);
+	return 0;
+}
+
+/**
+ * fdisk_partition_get_start:
+ * @pa: partition
+ *
+ * The zero is also valid offset. The function may return random undefined
+ * value when start offset is undefined (for example after
+ * fdisk_partition_unset_start()). Always use fdisk_partition_has_start() to be
+ * sure that you work with valid numbers.
+ *
+ * Returns: start offset in sectors
+ */
+fdisk_sector_t fdisk_partition_get_start(struct fdisk_partition *pa)
+{
+	return pa->start;
+}
+
+/**
+ * fdisk_partition_has_start:
+ * @pa: partition
+ *
+ * Returns: 1 or 0
+ */
+int fdisk_partition_has_start(struct fdisk_partition *pa)
+{
+	return pa && !FDISK_IS_UNDEF(pa->start);
+}
+
+
+/**
+ * fdisk_partition_cmp_start:
+ * @a: partition
+ * @b: partition
+ *
+ * Compares partitons according to start offset, See fdisk_sort_table().
+ *
+ * Return: 0 if the same, <0 if @b greater, >0 if @a greater.
+ */
+int fdisk_partition_cmp_start(struct fdisk_partition *a,
+			      struct fdisk_partition *b)
+{
+	int no_a = FDISK_IS_UNDEF(a->start),
+	    no_b = FDISK_IS_UNDEF(b->start);
+
+	if (no_a && no_b)
+		return 0;
+	if (no_a)
+		return -1;
+	if (no_b)
+		return 1;
+
+	return cmp_numbers(a->start, b->start);
+}
+
+/**
+ * fdisk_partition_start_follow_default
+ * @pa: partition
+ * @enable: 0|1
+ *
+ * When @pa used as a tempalate for fdisk_add_partition() when force label driver
+ * to use the first possible space for the new partition.
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int fdisk_partition_start_follow_default(struct fdisk_partition *pa, int enable)
+{
+	if (!pa)
+		return -EINVAL;
+	pa->start_follow_default = enable ? 1 : 0;
+	return 0;
+}
+
+/**
+ * fdisk_partition_start_is_default:
+ * @pa: partition
+ *
+ * See fdisk_partition_start_follow_default().
+ *
+ * Returns: 1 if the partition follows default
+ */
+int fdisk_partition_start_is_default(struct fdisk_partition *pa)
+{
+	assert(pa);
+	return pa->start_follow_default;
+}
+
+
+/**
+ * fdisk_partition_set_size:
+ * @pa: partition
+ * @sz: size in sectors, maximal is UIN64_MAX-1
+ *
+ * Note that zero is valid size too. Use fdisk_partition_unset_size() to
+ * undefine the size.
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int fdisk_partition_set_size(struct fdisk_partition *pa, fdisk_sector_t sz)
+{
+	if (!pa)
+		return -EINVAL;
+	if (FDISK_IS_UNDEF(sz))
+		return -ERANGE;
+	pa->size = sz;
+	return 0;
+}
+
+/**
+ * fdisk_partition_unset_size:
+ * @pa: partition
+ *
+ * Sets the size as undefined. See fdisk_partition_has_size().
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int fdisk_partition_unset_size(struct fdisk_partition *pa)
+{
+	if (!pa)
+		return -EINVAL;
+	FDISK_INIT_UNDEF(pa->size);
+	return 0;
+}
+
+/**
+ * fdisk_partition_get_size:
+ * @pa: partition
+ *
+ * The zero is also valid size. The function may return random undefined
+ * value when size is undefined (for example after fdisk_partition_unset_size()).
+ * Always use fdisk_partition_has_size() to be sure that you work with valid 
+ * numbers.
+ *
+ * Returns: size offset in sectors
+ */
+fdisk_sector_t fdisk_partition_get_size(struct fdisk_partition *pa)
+{
+	return pa->size;
+}
+
+/**
+ * fdisk_partition_has_size:
+ * @pa: partition
+ *
+ * Returns: 1 or 0
+ */
+int fdisk_partition_has_size(struct fdisk_partition *pa)
+{
+	return pa && !FDISK_IS_UNDEF(pa->size);
+}
+
+/**
+ * fdisk_partition_size_explicit:
+ * @pa: partition
+ * @enable: 0|1
+ *
+ * By default libfdisk aligns the size when add the new partition (by
+ * fdisk_add_partrition()). If you want to disable this functionality use
+ * @enable = 1.
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int fdisk_partition_size_explicit(struct fdisk_partition *pa, int enable)
+{
+	if (!pa)
+		return -EINVAL;
+	pa->size_explicit = enable ? 1 : 0;
+	return 0;
+}
+
+/**
+ * fdisk_partition_set_partno:
+ * @pa: partition
+ * @num: partitin number (0 is the first partition, maximal is SIZE_MAX-1)
+ *
+ * Note that zero is valid partno too. Use fdisk_partition_unset_partno() to
+ * undefine the partno.
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int fdisk_partition_set_partno(struct fdisk_partition *pa, size_t num)
+{
+	if (!pa)
+		return -EINVAL;
+	if (FDISK_IS_UNDEF(num))
+		return -ERANGE;
+	pa->partno = num;
+	return 0;
+}
+
+/**
+ * fdisk_partition_unset_partno:
+ * @pa: partition
+ *
+ * Sets the partno as undefined. See fdisk_partition_has_partno().
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int fdisk_partition_unset_partno(struct fdisk_partition *pa)
+{
+	if (!pa)
+		return -EINVAL;
+	FDISK_INIT_UNDEF(pa->partno);
+	return 0;
+}
+
+/**
+ * fdisk_partition_get_partno:
+ * @pa: partition
+ *
+ * The zero is also valid parition number. The function may return random
+ * value when partno is undefined (for example after fdisk_partition_unset_partno()).
+ * Always use fdisk_partition_has_partno() to be sure that you work with valid
+ * numbers.
+ *
+ * Returns: partition number (0 is the first partition)
+ */
+size_t fdisk_partition_get_partno(struct fdisk_partition *pa)
+{
+	return pa->partno;
+}
+
+/**
+ * fdisk_partition_has_partno:
+ * @pa: partition
+ *
+ * Returns: 1 or 0
+ */
+int fdisk_partition_has_partno(struct fdisk_partition *pa)
+{
+	return pa && !FDISK_IS_UNDEF(pa->partno);
+}
+
+
+/**
+ * fdisk_partition_cmp_partno:
+ * @a: partition
+ * @b: partition
+ *
+ * Compares partitons according to partition number See fdisk_sort_table().
+ *
+ * Return: 0 if the same, <0 if @b greater, >0 if @a greater.
+ */
+int fdisk_partition_cmp_partno(struct fdisk_partition *a,
+			       struct fdisk_partition *b)
+{
+	return a->partno - b->partno;
+}
+
+/**
+ * fdisk_partition_partno_follow_default
+ * @pa: partition
+ * @enable: 0|1
+ *
+ * When @pa used as a tempalate for fdisk_add_partition() when force label driver
+ * to add a new partition to the default (next) position.
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int fdisk_partition_partno_follow_default(struct fdisk_partition *pa, int enable)
+{
+	if (!pa)
+		return -EINVAL;
+	pa->partno_follow_default = enable ? 1 : 0;
+	return 0;
+}
+
+/**
+ * fdisk_partition_set_type:
+ * @pa: partition
+ * @type: partition type
+ *
+ * Sets parition type.
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int fdisk_partition_set_type(struct fdisk_partition *pa,
+			     struct fdisk_parttype *type)
+{
+	if (!pa)
+		return -EINVAL;
+
+	fdisk_ref_parttype(type);
+	fdisk_unref_parttype(pa->type);
+	pa->type = type;
+
+	return 0;
+}
+
+/**
+ * fdisk_partition_get_type:
+ * @pa: partition
+ *
+ * Returns: pointer to partition type.
+ */
+struct fdisk_parttype *fdisk_partition_get_type(struct fdisk_partition *pa)
+{
+	return pa ? pa->type : NULL;
+}
+
+int fdisk_partition_set_name(struct fdisk_partition *pa, const char *name)
+{
+	char *p = NULL;
+
+	if (!pa)
+		return -EINVAL;
+	if (name) {
+	       p = strdup(name);
+	       if (!p)
+		       return -ENOMEM;
+	}
+	free(pa->name);
+	pa->name = p;
+	return 0;
+}
+
+const char *fdisk_partition_get_name(struct fdisk_partition *pa)
+{
+	return pa ? pa->name : NULL;
+}
+
+int fdisk_partition_set_uuid(struct fdisk_partition *pa, const char *uuid)
+{
+	char *p = NULL;
+
+	if (!pa)
+		return -EINVAL;
+	if (uuid) {
+	       p = strdup(uuid);
+	       if (!p)
+		       return -ENOMEM;
+	}
+	free(pa->uuid);
+	pa->uuid = p;
+	return 0;
+}
+
+/**
+ * fdisk_partition_has_end:
+ * @pa: partition
+ *
+ * Returns: 1 if the partition has defined last sector
+ */
+int fdisk_partition_has_end(struct fdisk_partition *pa)
+{
+	return pa && !FDISK_IS_UNDEF(pa->start) && !FDISK_IS_UNDEF(pa->size);
+}
+
+/**
+ * fdisk_partition_get_end:
+ * @pa: partition
+ *
+ * This function may returns absolute non-sense, always check
+ * fdisk_partition_has_end().
+ *
+ * Note that partition end is defined by fdisk_partition_set_start() and
+ * fdisk_partition_set_size().
+ *
+ * Returns: last partition sector LBA.
+ */
+fdisk_sector_t fdisk_partition_get_end(struct fdisk_partition *pa)
+{
+	return pa->start + pa->size - (pa->size == 0 ? 0 : 1);
+}
+
+/**
+ * fdisk_partition_end_follow_default
+ * @pa: partition
+ * @enable: 0|1
+ *
+ * When @pa used as a tempalate for fdisk_add_partition() when force label driver
+ * to use all the possible space for the new partition.
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int fdisk_partition_end_follow_default(struct fdisk_partition *pa, int enable)
+{
+	if (!pa)
+		return -EINVAL;
+	pa->end_follow_default = enable ? 1 : 0;
+	return 0;
+}
+
+/**
+ * fdisk_partition_end_is_default:
+ * @pa: partition
+ *
+ * Returns: 1 if the partition follows default
+ */
+int fdisk_partition_end_is_default(struct fdisk_partition *pa)
+{
+	assert(pa);
+	return pa->end_follow_default;
+}
+
+const char *fdisk_partition_get_uuid(struct fdisk_partition *pa)
+{
+	return pa ? pa->uuid : NULL;
+}
+
+const char *fdisk_partition_get_attrs(struct fdisk_partition *pa)
+{
+	return pa ? pa->attrs : NULL;
+}
+
+int fdisk_partition_set_attrs(struct fdisk_partition *pa, const char *attrs)
+{
+	char *p = NULL;
+
+	if (!pa)
+		return -EINVAL;
+	if (attrs) {
+	       p = strdup(attrs);
+	       if (!p)
+		       return -ENOMEM;
+	}
+	free(pa->attrs);
+	pa->attrs = p;
+	return 0;
+}
+
+int fdisk_partition_is_nested(struct fdisk_partition *pa)
+{
+	return pa && !FDISK_IS_UNDEF(pa->parent_partno);
+}
+
+int fdisk_partition_is_container(struct fdisk_partition *pa)
+{
+	return pa && pa->container;
+}
+
+int fdisk_partition_get_parent(struct fdisk_partition *pa, size_t *parent)
+{
+	if (pa && parent)
+		*parent = pa->parent_partno;
+	else
+		return -EINVAL;
+	return 0;
+}
+
+int fdisk_partition_is_used(struct fdisk_partition *pa)
+{
+	return pa && pa->used;
+}
+
+int fdisk_partition_is_bootable(struct fdisk_partition *pa)
+{
+	return pa && pa->boot == 1;
+}
+
+int fdisk_partition_is_freespace(struct fdisk_partition *pa)
+{
+	return pa && pa->freespace;
+}
+
+int fdisk_partition_next_partno(
+		struct fdisk_partition *pa,
+		struct fdisk_context *cxt,
+		size_t *n)
+{
+	assert(cxt);
+	assert(n);
+
+	if (pa && pa->partno_follow_default) {
+		size_t i;
+
+		DBG(PART, ul_debugobj(pa, "next partno (follow default)"));
+
+		for (i = 0; i < cxt->label->nparts_max; i++) {
+			if (!fdisk_is_partition_used(cxt, i)) {
+				*n = i;
+				return 0;
+			}
+		}
+		return -ERANGE;
+
+	} else if (pa && fdisk_partition_has_partno(pa)) {
+
+		DBG(PART, ul_debugobj(pa, "next partno (specified=%zu)", pa->partno));
+
+		if (pa->partno >= cxt->label->nparts_max)
+			return -ERANGE;
+		*n = pa->partno;
+	} else
+		return fdisk_ask_partnum(cxt, n, 1);
+
+	return 0;
+}
+
+/**
+ * fdisk_partition_to_string:
+ * @pa: partition
+ * @cxt: context
+ * @id: field (FDISK_FIELD_*)
+ * @data: returns string with allocated data
+ *
+ * Returns info about partition converted to printable string.
+ *
+ * For example
+ * <informalexample>
+ *   <programlisting>
+ *      struct fdisk_parition *pa;
+ *
+ *      fdisk_get_partition(cxt, 0, &pa);
+ *	fdisk_partition_to_string(pa, FDISK_FIELD_UUID, &data);
+ *	printf("first partition uuid: %s\n", data);
+ *	free(data);
+ *	fdisk_unref_partition(pa);
+ *   </programlisting>
+ * </informalexample>
+ *
+ * returns UUID for the first partition.
+ *
+ * Returns: 0 on success, otherwise, a corresponding error.
+ */
+int fdisk_partition_to_string(struct fdisk_partition *pa,
+			      struct fdisk_context *cxt,
+			      int id,
+			      char **data)
+{
+	char *p = NULL;
+	int rc = 0;
+	uint64_t x;
+
+	if (!pa || !cxt)
+		return -EINVAL;
+
+	switch (id) {
+	case FDISK_FIELD_DEVICE:
+		if (pa->freespace)
+			p = strdup(_("Free space"));
+		else if (fdisk_partition_has_partno(pa) && cxt->dev_path) {
+			if (cxt->label->flags & FDISK_LABEL_FL_INCHARS_PARTNO)
+				rc = asprintf(&p, "%c", (int) pa->partno + 'a');
+			else
+				p = fdisk_partname(cxt->dev_path, pa->partno + 1);
+		}
+		break;
+	case FDISK_FIELD_BOOT:
+		if (fdisk_partition_is_bootable(pa))
+			rc = asprintf(&p, "%c", pa->boot ? '*' : ' ');
+		break;
+	case FDISK_FIELD_START:
+		if (fdisk_partition_has_start(pa)) {
+			x = fdisk_cround(cxt, pa->start);
+			rc = pa->start_post ?
+				asprintf(&p, "%ju%c", x, pa->start_post) :
+				asprintf(&p, "%ju", x);
+		}
+		break;
+	case FDISK_FIELD_END:
+		if (fdisk_partition_has_end(pa)) {
+			x = fdisk_cround(cxt, fdisk_partition_get_end(pa));
+			rc = pa->end_post ?
+					asprintf(&p, "%ju%c", x, pa->end_post) :
+					asprintf(&p, "%ju", x);
+		}
+		break;
+	case FDISK_FIELD_SIZE:
+		if (fdisk_partition_has_size(pa)) {
+			uint64_t sz = pa->size * cxt->sector_size;
+
+			if (fdisk_is_details(cxt)) {
+				rc = pa->size_post ?
+						asprintf(&p, "%ju%c", sz, pa->size_post) :
+						asprintf(&p, "%ju", sz);
+			} else {
+				p = size_to_human_string(SIZE_SUFFIX_1LETTER, sz);
+				if (!p)
+					rc = -ENOMEM;
+			}
+		}
+		break;
+	case FDISK_FIELD_CYLINDERS:
+		rc = asprintf(&p, "%ju", (uintmax_t)
+			fdisk_cround(cxt, fdisk_partition_has_size(pa) ? pa->size : 0));
+		break;
+	case FDISK_FIELD_SECTORS:
+		rc = asprintf(&p, "%ju",
+			fdisk_partition_has_size(pa) ? (uintmax_t) pa->size : 0);
+		break;
+	case FDISK_FIELD_BSIZE:
+		rc = asprintf(&p, "%ju", pa->bsize);
+		break;
+	case FDISK_FIELD_FSIZE:
+		rc = asprintf(&p, "%ju", pa->fsize);
+		break;
+	case FDISK_FIELD_CPG:
+		rc = asprintf(&p, "%ju", pa->cpg);
+		break;
+	case FDISK_FIELD_TYPE:
+		p = pa->type && pa->type->name ? strdup(pa->type->name) : NULL;
+		break;
+	case FDISK_FIELD_TYPEID:
+		if (pa->type && fdisk_parttype_get_string(pa->type))
+			rc = asprintf(&p, "%s", fdisk_parttype_get_string(pa->type));
+		else if (pa->type)
+			rc = asprintf(&p, "%x", fdisk_parttype_get_code(pa->type));
+		break;
+	case FDISK_FIELD_UUID:
+		p = pa->uuid ? strdup(pa->uuid) : NULL;
+		break;
+	case FDISK_FIELD_NAME:
+		p = pa->name ? strdup(pa->name) : NULL;
+		break;
+	case FDISK_FIELD_ATTR:
+		p = pa->attrs ? strdup(pa->attrs) : NULL;
+		break;
+	case FDISK_FIELD_SADDR:
+		p = pa->start_chs ? strdup(pa->start_chs) : NULL;
+		break;
+	case FDISK_FIELD_EADDR:
+		p = pa->end_chs ? strdup(pa->end_chs) : NULL;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (rc < 0)
+		rc = -ENOMEM;
+	else if (rc > 0)
+		rc = 0;
+
+	if (data)
+		*data = p;
+	return rc;
+}
+
+/**
+ * fdisk_get_partition:
+ * @cxt: context
+ * @partno: partition number (0 is the first partition)
+ * @pa: returns data about partition
+ *
+ * Reads disklabel and fills in @pa with data about partition @n.
+ *
+ * Note that partno may address unused partition and then this function does
+ * not fill anything to @pa.  See fdisk_is_partition_used(). If @pa points to
+ * NULL then the function allocates a newly allocated fdisk_partition struct,
+ * use fdisk_unref_partition() to deallocate.
+ *
+ * Returns: 0 on success, otherwise, a corresponding error.
+ */
+int fdisk_get_partition(struct fdisk_context *cxt, size_t partno,
+			struct fdisk_partition **pa)
+{
+	int rc;
+	struct fdisk_partition *np = NULL;
+
+	if (!cxt || !cxt->label || !pa)
+		return -EINVAL;
+	if (!cxt->label->op->get_part)
+		return -ENOSYS;
+	if (!fdisk_is_partition_used(cxt, partno))
+		return -EINVAL;
+
+	if (!*pa) {
+		np = *pa = fdisk_new_partition();
+		if (!*pa)
+			return -ENOMEM;
+	} else
+		fdisk_reset_partition(*pa);
+
+	(*pa)->partno = partno;
+	rc = cxt->label->op->get_part(cxt, partno, *pa);
+
+	if (rc) {
+		if (np) {
+			fdisk_unref_partition(np);
+			*pa = NULL;
+		} else
+			fdisk_reset_partition(*pa);
+	} else
+		(*pa)->size_explicit = 1;
+	return rc;
+}
+
+/**
+ * fdisk_set_partition:
+ * @cxt: context
+ * @partno: partition number (0 is the first partition)
+ * @pa: new partition setting
+ *
+ * Modifies disklabel according to setting with in @pa.
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int fdisk_set_partition(struct fdisk_context *cxt, size_t partno,
+			struct fdisk_partition *pa)
+{
+	if (!cxt || !cxt->label || !pa)
+		return -EINVAL;
+	if (!cxt->label->op->set_part)
+		return -ENOSYS;
+
+	DBG(CXT, ul_debugobj(cxt, "setting partition %zu %p (start=%ju, end=%ju, size=%ju, "
+		    "defaults(start=%s, end=%s, partno=%s)",
+		    partno, pa,
+		    (uintmax_t) fdisk_partition_get_start(pa),
+		    (uintmax_t) fdisk_partition_get_end(pa),
+		    (uintmax_t) fdisk_partition_get_size(pa),
+		    pa->start_follow_default ? "yes" : "no",
+		    pa->end_follow_default ? "yes" : "no",
+		    pa->partno_follow_default ? "yes" : "no"));
+
+	return cxt->label->op->set_part(cxt, partno, pa);
+}
+
+/**
+ * fdisk_add_partition:
+ * @cxt: fdisk context
+ * @pa: template for the partition (or NULL)
+ * @partno: NULL or returns new partition number
+ *
+ * If @pa is not specified or any @pa item is missiong the libfdisk will ask by
+ * fdisk_ask_ API.
+ *
+ * Adds a new partition to disklabel.
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int fdisk_add_partition(struct fdisk_context *cxt,
+			struct fdisk_partition *pa,
+			size_t *partno)
+{
+	int rc;
+
+	assert(cxt);
+	assert(cxt->label);
+
+	if (!cxt || !cxt->label)
+		return -EINVAL;
+	if (!cxt->label->op->add_part)
+		return -ENOSYS;
+	if (fdisk_missing_geometry(cxt))
+		return -EINVAL;
+
+	if (pa)
+		DBG(CXT, ul_debugobj(cxt, "adding new partition %p (start=%ju, end=%ju, size=%ju, "
+			    "defaults(start=%s, end=%s, partno=%s)",
+			    pa,
+			    (uintmax_t) fdisk_partition_get_start(pa),
+			    (uintmax_t) fdisk_partition_get_end(pa),
+			    (uintmax_t) fdisk_partition_get_size(pa),
+			    pa->start_follow_default ? "yes" : "no",
+			    pa->end_follow_default ? "yes" : "no",
+			    pa->partno_follow_default ? "yes" : "no"));
+	else
+		DBG(CXT, ul_debugobj(cxt, "adding partition"));
+
+	rc = cxt->label->op->add_part(cxt, pa, partno);
+
+	DBG(CXT, ul_debugobj(cxt, "add partition done (rc=%d)", rc));
+	return rc;
+}
+
+/**
+ * fdisk_delete_partition:
+ * @cxt: fdisk context
+ * @partno: partition number to delete (0 is the first partition)
+ *
+ * Deletes a @partno partition from disklabel.
+ *
+ * Returns: 0 on success, <0 on error
+ */
+int fdisk_delete_partition(struct fdisk_context *cxt, size_t partno)
+{
+	if (!cxt || !cxt->label)
+		return -EINVAL;
+	if (!cxt->label->op->del_part)
+		return -ENOSYS;
+
+	DBG(CXT, ul_debugobj(cxt, "deleting %s partition number %zd",
+				cxt->label->name, partno));
+	return cxt->label->op->del_part(cxt, partno);
+}
+
+/**
+ * fdisk_delete_all_partitions:
+ * @cxt: fdisk context
+ *
+ * Delete all used partitions from disklabel.
+ *
+ * Returns: 0 on success, otherwise, a corresponding error.
+ */
+int fdisk_delete_all_partitions(struct fdisk_context *cxt)
+{
+	size_t i;
+	int rc;
+
+	if (!cxt || !cxt->label)
+		return -EINVAL;
+
+	for (i = 0; i < cxt->label->nparts_max; i++) {
+
+		if (!fdisk_is_partition_used(cxt, i))
+			continue;
+		rc = fdisk_delete_partition(cxt, i);
+		if (rc)
+			break;
+	}
+
+	return rc;
+}
+
+/**
+ * fdisk_is_partition_used:
+ * @cxt: context
+ * @n: partition number (0 is the first partition)
+ *
+ * This is faster than fdisk_get_partition() + fdisk_partition_is_used().
+ *
+ * Returns: 0 or 1
+ */
+int fdisk_is_partition_used(struct fdisk_context *cxt, size_t n)
+{
+	if (!cxt || !cxt->label)
+		return -EINVAL;
+	if (!cxt->label->op->part_is_used)
+		return -ENOSYS;
+
+	return cxt->label->op->part_is_used(cxt, n);
+}
+
diff --git a/libblkid/libfdisk/src/parttype.c b/libblkid/libfdisk/src/parttype.c
new file mode 100644
index 0000000..aedf4e8
--- /dev/null
+++ b/libblkid/libfdisk/src/parttype.c
@@ -0,0 +1,401 @@
+
+#include <ctype.h>
+
+#include "nls.h"
+#include "fdiskP.h"
+
+/**
+ * SECTION: parttype
+ * @title: Partition types
+ * @short_description: abstraction to partition types
+ *
+ * There are two basic types of parttypes, string based (e.g. GPT)
+ * and code/hex based (e.g. MBR).
+ */
+
+/**
+ * fdisk_new_parttype:
+ *
+ * It's recommended to use fdisk_label_get_parttype_from_code() or
+ * fdisk_label_get_parttype_from_string() for well known types rather
+ * than allocate a new instance.
+ *
+ * Returns: new instance.
+ */
+struct fdisk_parttype *fdisk_new_parttype(void)
+{
+	struct fdisk_parttype *t = calloc(1, sizeof(*t));
+
+	t->refcount = 1;
+	t->flags = FDISK_PARTTYPE_ALLOCATED;
+	DBG(PARTTYPE, ul_debugobj(t, "alloc"));
+	return t;
+}
+
+/**
+ * fdisk_ref_parttype:
+ * @t: partition type
+ *
+ * Incremparts reference counter for allocated types
+ */
+void fdisk_ref_parttype(struct fdisk_parttype *t)
+{
+	if (fdisk_parttype_is_allocated(t))
+		t->refcount++;
+}
+
+/**
+ * fdisk_unref_parttype
+ * @t: partition pointer
+ *
+ * De-incremparts reference counter, on zero the @t is automatically
+ * deallocated.
+ */
+void fdisk_unref_parttype(struct fdisk_parttype *t)
+{
+	if (!fdisk_parttype_is_allocated(t))
+		return;
+
+	t->refcount--;
+	if (t->refcount <= 0) {
+		DBG(PARTTYPE, ul_debugobj(t, "free"));
+		free(t->typestr);
+		free(t->name);
+		free(t);
+	}
+}
+
+/**
+ * fdisk_parttype_set_name:
+ * @t: partition type
+ * @str: type name
+ *
+ * Sets type name to allocated partition type, for static types
+ * it returns -EINVAL.
+ *
+ * Return: 0 on success, <0 on error
+ */
+int fdisk_parttype_set_name(struct fdisk_parttype *t, const char *str)
+{
+	char *p = NULL;
+
+	if (!t || !fdisk_parttype_is_allocated(t))
+		return -EINVAL;
+	if (str) {
+		p = strdup(str);
+		if (!p)
+			return -ENOMEM;
+	}
+
+	free(t->name);
+	t->name = p;
+	return 0;
+}
+
+/**
+ * fdisk_parttype_set_typestr:
+ * @t: partition type
+ * @str: type identificator (e.g. GUID for GPT)
+ *
+ * Sets type string to allocated partition type, for static types
+ * it returns -EINVAL. Don't use this function for MBR, see
+ * fdisk_parttype_set_code().
+ *
+ * Return: 0 on success, <0 on error
+ */
+int fdisk_parttype_set_typestr(struct fdisk_parttype *t, const char *str)
+{
+	char *p = NULL;
+
+	if (!t || !fdisk_parttype_is_allocated(t))
+		return -EINVAL;
+	if (str) {
+		p = strdup(str);
+		if (!p)
+			return -ENOMEM;
+	}
+
+	free(t->typestr);
+	t->typestr = p;
+	return 0;
+}
+
+/**
+ * fdisk_parttype_set_code:
+ * @t: partition type
+ * @code: type identificator (e.g. MBR type codes)
+ *
+ * Sets type code to allocated partition type, for static types it returns
+ * -EINVAL. Don't use this function for GPT, see fdisk_parttype_set_typestr().
+ *
+ * Return: 0 on success, <0 on error
+ */
+int fdisk_parttype_set_code(struct fdisk_parttype *t, int code)
+{
+	if (!t || !fdisk_parttype_is_allocated(t))
+		return -EINVAL;
+	t->code = code;
+	return 0;
+}
+
+/**
+ * fdisk_label_get_nparttypes:
+ * @lb: label
+ *
+ * Returns: number of types supported by label.
+ */
+size_t fdisk_label_get_nparttypes(const struct fdisk_label *lb)
+{
+	if (!lb)
+		return 0;
+	return lb->nparttypes;
+}
+
+/**
+ * fdisk_label_get_parttype:
+ * @lb: label
+ * @n: number
+ *
+ * Returns: return parttype
+ */
+struct fdisk_parttype *fdisk_label_get_parttype(const struct fdisk_label *lb, size_t n)
+{
+	if (!lb || n >= lb->nparttypes)
+		return NULL;
+	return &lb->parttypes[n];
+}
+
+/**
+ * fdisk_label_has_code_parttypes:
+ * @lb: label
+ *
+ * Returns: 1 if the label uses code as partition type
+ *          identifiers (e.g. MBR) or 0.
+ */
+int fdisk_label_has_code_parttypes(const struct fdisk_label *lb)
+{
+	assert(lb);
+
+	if (lb->parttypes && lb->parttypes[0].typestr)
+		return 0;
+	return 1;
+}
+
+
+/**
+ * fdisk_label_get_parttype_from_code:
+ * @lb: label
+ * @code: code to search for
+ *
+ * Search for partition type in label-specific table. The result
+ * is pointer to static array of label types.
+ *
+ * Returns: partition type or NULL upon failure or invalid @code.
+ */
+struct fdisk_parttype *fdisk_label_get_parttype_from_code(
+				const struct fdisk_label *lb,
+				unsigned int code)
+{
+	size_t i;
+
+	assert(lb);
+
+	if (!lb->nparttypes)
+		return NULL;
+
+	for (i = 0; i < lb->nparttypes; i++)
+		if (lb->parttypes[i].code == code)
+			return &lb->parttypes[i];
+	return NULL;
+}
+
+/**
+ * fdisk_label_get_parttype_from_string:
+ * @lb: label
+ * @str: string to search for
+ *
+ * Search for partition type in label-specific table. The result
+ * is pointer to static array of label types.
+ *
+ * Returns: partition type or NULL upon failure or invalid @str.
+ */
+struct fdisk_parttype *fdisk_label_get_parttype_from_string(
+				const struct fdisk_label *lb,
+				const char *str)
+{
+	size_t i;
+
+	assert(lb);
+
+	if (!lb->nparttypes)
+		return NULL;
+
+	for (i = 0; i < lb->nparttypes; i++)
+		if (lb->parttypes[i].typestr
+		    && strcasecmp(lb->parttypes[i].typestr, str) == 0)
+			return &lb->parttypes[i];
+
+	return NULL;
+}
+
+/**
+ * fdisk_new_unknown_parttype:
+ * @code: type as number
+ * @typestr: type as string
+
+ * Allocates new 'unknown' partition type. Use fdisk_unref_parttype() to
+ * deallocate.
+ *
+ * Returns: newly allocated partition type, or NULL upon failure.
+ */
+struct fdisk_parttype *fdisk_new_unknown_parttype(unsigned int code,
+						  const char *typestr)
+{
+	struct fdisk_parttype *t = fdisk_new_parttype();
+
+	if (!t)
+		return NULL;
+
+	fdisk_parttype_set_name(t, _("unknown"));
+	fdisk_parttype_set_code(t, code);
+	fdisk_parttype_set_typestr(t, typestr);
+	t->flags |= FDISK_PARTTYPE_UNKNOWN;
+
+	return t;
+}
+
+/**
+ * fdisk_copy_parttype:
+ * @type: type to copy
+ *
+ * Use fdisk_unref_parttype() to deallocate.
+ *
+ * Returns: newly allocated partition type, or NULL upon failure.
+ */
+struct fdisk_parttype *fdisk_copy_parttype(const struct fdisk_parttype *type)
+{
+	struct fdisk_parttype *t = fdisk_new_parttype();
+
+	if (!t)
+		return NULL;
+
+	fdisk_parttype_set_name(t, type->name);
+	fdisk_parttype_set_code(t, type->code);
+	fdisk_parttype_set_typestr(t, type->typestr);
+
+	return t;
+}
+
+/**
+ * fdisk_label_parse_parttype:
+ * @lb: label
+ * @str: string to parse from
+ *
+ * Parses partition type from @str according to the label. Thefunction returns
+ * a pointer to static table of the partition types, or newly allocated
+ * partition type for unknown types (see fdisk_parttype_is_unknown(). It's
+ * safe to call fdisk_unref_parttype() for all results.
+ *
+ * Returns: pointer to type or NULL on error.
+ */
+struct fdisk_parttype *fdisk_label_parse_parttype(
+				const struct fdisk_label *lb,
+				const char *str)
+{
+	struct fdisk_parttype *types, *ret;
+	unsigned int code = 0;
+	char *typestr = NULL, *end = NULL;
+
+	assert(lb);
+
+	if (!lb->nparttypes)
+		return NULL;
+
+	DBG(LABEL, ul_debugobj((void *) lb, "parsing '%s' (%s) partition type",
+				str, lb->name));
+	types = lb->parttypes;
+
+	if (types[0].typestr == NULL && isxdigit(*str)) {
+
+		errno = 0;
+		code = strtol(str, &end, 16);
+
+		if (errno || *end != '\0') {
+			DBG(LABEL, ul_debugobj((void *) lb, "parsing failed: %m"));
+			return NULL;
+		}
+		ret = fdisk_label_get_parttype_from_code(lb, code);
+		if (ret)
+			goto done;
+	} else {
+		int i;
+
+		/* maybe specified by type string (e.g. UUID) */
+		ret = fdisk_label_get_parttype_from_string(lb, str);
+		if (ret)
+			goto done;
+
+		/* maybe specified by order number */
+		errno = 0;
+		i = strtol(str, &end, 0);
+		if (errno == 0 && *end == '\0' && i > 0
+		    && i - 1 < (int) lb->nparttypes) {
+			ret = &types[i - 1];
+			goto done;
+		}
+	}
+
+	ret = fdisk_new_unknown_parttype(code, typestr);
+done:
+	DBG(PARTTYPE, ul_debugobj(ret, "returns parsed '%s' partition type", ret->name));
+	return ret;
+}
+
+/**
+ * fdisk_parttype_get_string:
+ * @t: type
+ *
+ * Returns: partition type string (e.g. GUID for GPT)
+ */
+const char *fdisk_parttype_get_string(const struct fdisk_parttype *t)
+{
+	assert(t);
+	return t->typestr && *t->typestr ? t->typestr : NULL;
+}
+
+/**
+ * fdisk_parttype_get_code:
+ * @t: type
+ *
+ * Returns: partition type code (e.g. for MBR)
+ */
+unsigned int fdisk_parttype_get_code(const struct fdisk_parttype *t)
+{
+	assert(t);
+	return t->code;
+}
+
+/**
+ * fdisk_parttype_get_name:
+ * @t: type
+ *
+ * Returns: partition type human readable name
+ */
+const char *fdisk_parttype_get_name(const struct fdisk_parttype *t)
+{
+	assert(t);
+	return t->name;
+}
+
+/**
+ * fdisk_parttype_is_unknown:
+ * @t: type
+ *
+ * Checks for example result from fdisk_label_parse_parttype().
+ *
+ * Returns: 1 is type is "unknonw" or 0.
+ */
+int fdisk_parttype_is_unknown(const struct fdisk_parttype *t)
+{
+	return t && (t->flags & FDISK_PARTTYPE_UNKNOWN) ? 1 : 0;
+}
diff --git a/libblkid/libfdisk/src/script.c b/libblkid/libfdisk/src/script.c
new file mode 100644
index 0000000..83bda99
--- /dev/null
+++ b/libblkid/libfdisk/src/script.c
@@ -0,0 +1,1235 @@
+
+#include "fdiskP.h"
+#include "strutils.h"
+
+/**
+ * SECTION: script
+ * @title: Script
+ * @short_description: text based sfdisk compatible description of partition table
+ *
+ * The libfdisk scripts are based on original sfdisk script (dumps).  Each
+ * script has two parts: script headers and partition table entries
+ * (partitions).
+ *
+ * For more details about script format see sfdisk man page.
+ */
+
+/* script header (e.g. unit: sectors) */
+struct fdisk_scriptheader {
+	struct list_head	headers;
+	char			*name;
+	char			*data;
+};
+
+/* script control struct */
+struct fdisk_script {
+	struct fdisk_table	*table;
+	struct list_head	headers;
+	struct fdisk_context	*cxt;
+
+	int			refcount;
+
+	/* parser's state */
+	size_t			nlines;
+	int			fmt;		/* input format */
+	struct fdisk_label	*label;
+};
+
+
+static void fdisk_script_free_header(struct fdisk_script *dp, struct fdisk_scriptheader *fi)
+{
+	if (!fi)
+		return;
+
+	DBG(SCRIPT, ul_debugobj(fi, "free header %s", fi->name));
+	free(fi->name);
+	free(fi->data);
+	list_del(&fi->headers);
+	free(fi);
+}
+
+/**
+ * fdisk_new_script:
+ * @cxt: context
+ *
+ * The script hold fdisk_table and additional information to read/write
+ * script to the file.
+ *
+ * Returns: newly allocated script struct.
+ */
+struct fdisk_script *fdisk_new_script(struct fdisk_context *cxt)
+{
+	struct fdisk_script *dp = NULL;
+
+	dp = calloc(1, sizeof(*dp));
+	if (!dp)
+		return NULL;
+
+	DBG(SCRIPT, ul_debugobj(dp, "alloc"));
+	dp->refcount = 1;
+	dp->cxt = cxt;
+	fdisk_ref_context(cxt);
+
+	dp->table = fdisk_new_table();
+	if (!dp->table) {
+		fdisk_unref_script(dp);
+		return NULL;
+	}
+
+	INIT_LIST_HEAD(&dp->headers);
+	return dp;
+}
+
+/**
+ * fdisk_new_script_from_file:
+ * @cxt: context
+ * @filename: path to the script file
+ *
+ * Allocates a new script and reads script from @filename.
+ *
+ * Returns: new script instance or NULL in case of error (check errno for more details).
+ */
+struct fdisk_script *fdisk_new_script_from_file(struct fdisk_context *cxt,
+						 const char *filename)
+{
+	int rc;
+	FILE *f;
+	struct fdisk_script *dp, *res = NULL;
+
+	assert(cxt);
+	assert(filename);
+
+	DBG(SCRIPT, ul_debug("opening %s", filename));
+	f = fopen(filename, "r");
+	if (!f)
+		return NULL;
+
+	dp = fdisk_new_script(cxt);
+	if (!dp)
+		goto done;
+
+	rc = fdisk_script_read_file(dp, f);
+	if (rc) {
+		errno = -rc;
+		goto done;
+	}
+
+	res = dp;
+done:
+	fclose(f);
+	if (!res)
+		fdisk_unref_script(dp);
+	else
+		errno = 0;
+
+	return res;
+}
+
+/**
+ * fdisk_ref_script:
+ * @dp: script pointer
+ *
+ * Incremparts reference counter.
+ */
+void fdisk_ref_script(struct fdisk_script *dp)
+{
+	if (dp)
+		dp->refcount++;
+}
+
+static void fdisk_reset_script(struct fdisk_script *dp)
+{
+	assert(dp);
+
+	DBG(SCRIPT, ul_debugobj(dp, "reset"));
+	fdisk_unref_table(dp->table);
+	dp->table = NULL;
+
+	while (!list_empty(&dp->headers)) {
+		struct fdisk_scriptheader *fi = list_entry(dp->headers.next,
+						  struct fdisk_scriptheader, headers);
+		fdisk_script_free_header(dp, fi);
+	}
+	INIT_LIST_HEAD(&dp->headers);
+}
+
+/**
+ * fdisk_unref_script:
+ * @dp: script pointer
+ *
+ * De-incremparts reference counter, on zero the @dp is automatically
+ * deallocated.
+ */
+void fdisk_unref_script(struct fdisk_script *dp)
+{
+	if (!dp)
+		return;
+
+	dp->refcount--;
+	if (dp->refcount <= 0) {
+		fdisk_reset_script(dp);
+		fdisk_unref_context(dp->cxt);
+		DBG(SCRIPT, ul_debugobj(dp, "free script"));
+		free(dp);
+	}
+}
+
+static struct fdisk_scriptheader *script_get_header(struct fdisk_script *dp,
+						     const char *name)
+{
+	struct list_head *p;
+
+	list_for_each(p, &dp->headers) {
+		struct fdisk_scriptheader *fi = list_entry(p, struct fdisk_scriptheader, headers);
+
+		if (strcasecmp(fi->name, name) == 0)
+			return fi;
+	}
+
+	return NULL;
+}
+
+/**
+ * fdisk_script_get_header:
+ * @dp: script instance
+ * @name: header name
+ *
+ * Returns: pointer to header data or NULL.
+ */
+const char *fdisk_script_get_header(struct fdisk_script *dp, const char *name)
+{
+	struct fdisk_scriptheader *fi;
+
+	assert(dp);
+	assert(name);
+
+	fi = script_get_header(dp, name);
+	return fi ? fi->data : NULL;
+}
+
+
+/**
+ * fdisk_script_set_header:
+ * @dp: script instance
+ * @name: header name
+ * @data: header data (or NULL)
+ *
+ * The headers are used as global options (in script) for whole partition
+ * table, always one header per line.
+ *
+ * If no @data specified then the header is removed. If header does not exist
+ * and @data specified then a new header added.
+ *
+ * Note that libfdisk allows to specify arbitrary custom header, the default
+ * build-in headers are "unit" and "label", and some label specific headers
+ * (for example "uuid" and "name" for GPT).
+ *
+ * Returns: 0 on success, <0 on error
+ */
+int fdisk_script_set_header(struct fdisk_script *dp,
+			    const char *name,
+			    const char *data)
+{
+	struct fdisk_scriptheader *fi;
+
+	assert(dp);
+	assert(name);
+
+	if (!dp || !name)
+		return -EINVAL;
+
+	fi = script_get_header(dp, name);
+	if (!fi && !data)
+		return 0;	/* want to remove header that does not exist, success */
+
+	if (!data) {
+		/* no data, remove the header */
+		fdisk_script_free_header(dp, fi);
+		return 0;
+	}
+
+	if (!fi) {
+		/* new header */
+		fi = calloc(1, sizeof(*fi));
+		if (!fi)
+			return -ENOMEM;
+		INIT_LIST_HEAD(&fi->headers);
+		fi->name = strdup(name);
+		fi->data = strdup(data);
+		if (!fi->data || !fi->name) {
+			fdisk_script_free_header(dp, fi);
+			return -ENOMEM;
+		}
+		list_add_tail(&fi->headers, &dp->headers);
+	} else {
+		/* update existing */
+		char *x = strdup(data);
+
+		if (!x)
+			return -ENOMEM;
+		free(fi->data);
+		fi->data = x;
+	}
+
+	if (strcmp(name, "label") == 0)
+		dp->label = NULL;
+
+	return 0;
+}
+
+/**
+ * fdisk_script_get_table:
+ * @dp: script
+ *
+ * The table (container with partitions) is possible to create by
+ * fdisk_script_read_context() or fdisk_script_read_file(), otherwise
+ * this function returns NULL.
+ *
+ * Returns: NULL or script.
+ */
+struct fdisk_table *fdisk_script_get_table(struct fdisk_script *dp)
+{
+	assert(dp);
+	return dp ? dp->table : NULL;
+}
+
+static struct fdisk_label *script_get_label(struct fdisk_script *dp)
+{
+	assert(dp);
+	assert(dp->cxt);
+
+	if (!dp->label) {
+		dp->label = fdisk_get_label(dp->cxt,
+					fdisk_script_get_header(dp, "label"));
+		DBG(SCRIPT, ul_debugobj(dp, "label '%s'", dp->label ? dp->label->name : ""));
+	}
+	return dp->label;
+}
+
+/**
+ * fdisk_script_get_nlines:
+ * @dp: script
+ *
+ * Returns: number of parsed lines or <0 on error.
+ */
+int fdisk_script_get_nlines(struct fdisk_script *dp)
+{
+	assert(dp);
+	return dp->nlines;
+}
+
+/**
+ * fdisk_script_read_context:
+ * @dp: script
+ * @cxt: context
+ *
+ * Reads data from the @cxt context (on disk partition table) into the script.
+ * If the context is no specified than defaults to context used for fdisk_new_script().
+ *
+ * Return: 0 on success, <0 on error.
+ */
+int fdisk_script_read_context(struct fdisk_script *dp, struct fdisk_context *cxt)
+{
+	struct fdisk_label *lb;
+	int rc;
+	char *p = NULL;
+
+	assert(dp);
+
+	if (!cxt)
+		cxt = dp->cxt;
+
+	if (!dp || !cxt)
+		return -EINVAL;
+
+	DBG(SCRIPT, ul_debugobj(dp, "reading context into script"));
+	fdisk_reset_script(dp);
+
+	lb = fdisk_get_label(cxt, NULL);
+	if (!lb)
+		return -EINVAL;
+
+	/* allocate and fill new table */
+	rc = fdisk_get_partitions(cxt, &dp->table);
+	if (rc)
+		return rc;
+
+	/* generate headers */
+	rc = fdisk_script_set_header(dp, "label", fdisk_label_get_name(lb));
+
+	if (!rc && fdisk_get_disklabel_id(cxt, &p) == 0 && p) {
+		rc = fdisk_script_set_header(dp, "label-id", p);
+		free(p);
+	}
+	if (!rc && cxt->dev_path)
+		rc = fdisk_script_set_header(dp, "device", cxt->dev_path);
+	if (!rc)
+		rc = fdisk_script_set_header(dp, "unit", "sectors");
+
+	/* TODO: label specific headers (e.g. uuid for GPT) */
+
+	DBG(SCRIPT, ul_debugobj(dp, "read context done [rc=%d]", rc));
+	return rc;
+}
+
+/**
+ * fdisk_script_write_file:
+ * @dp: script
+ * @f: output file
+ *
+ * Writes script @dp to the ile @f.
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int fdisk_script_write_file(struct fdisk_script *dp, FILE *f)
+{
+	struct list_head *h;
+	struct fdisk_partition *pa;
+	struct fdisk_iter itr;
+	const char *devname = NULL;
+
+	assert(dp);
+	assert(f);
+
+	DBG(SCRIPT, ul_debugobj(dp, "writing script to file"));
+
+	/* script headers */
+	list_for_each(h, &dp->headers) {
+		struct fdisk_scriptheader *fi = list_entry(h, struct fdisk_scriptheader, headers);
+		fprintf(f, "%s: %s\n", fi->name, fi->data);
+		if (strcmp(fi->name, "device") == 0)
+			devname = fi->data;
+	}
+
+	if (!dp->table) {
+		DBG(SCRIPT, ul_debugobj(dp, "script table empty"));
+		return 0;
+	}
+
+	DBG(SCRIPT, ul_debugobj(dp, "%zu entries", fdisk_table_get_nents(dp->table)));
+
+	fputc('\n', f);
+
+	fdisk_reset_iter(&itr, FDISK_ITER_FORWARD);
+	while (fdisk_table_next_partition(dp->table, &itr, &pa) == 0) {
+		char *p = NULL;
+
+		if (devname)
+			p = fdisk_partname(devname, pa->partno + 1);
+		if (p) {
+			DBG(SCRIPT, ul_debugobj(dp, "write %s entry", p));
+			fprintf(f, "%s :", p);
+		} else
+			fprintf(f, "%zu :", pa->partno + 1);
+
+		if (fdisk_partition_has_start(pa))
+			fprintf(f, " start=%12ju", pa->start);
+		if (fdisk_partition_has_size(pa))
+			fprintf(f, ", size=%12ju", pa->size);
+
+		if (pa->type && fdisk_parttype_get_string(pa->type))
+			fprintf(f, ", type=%s", fdisk_parttype_get_string(pa->type));
+		else if (pa->type)
+			fprintf(f, ", type=%x", fdisk_parttype_get_code(pa->type));
+
+		if (pa->uuid)
+			fprintf(f, ", uuid=%s", pa->uuid);
+		if (pa->name && *pa->name)
+			fprintf(f, ", name=\"%s\"", pa->name);
+
+		/* for MBR attr=80 means bootable */
+		if (pa->attrs) {
+			struct fdisk_label *lb = script_get_label(dp);
+
+			if (!lb || fdisk_label_get_type(lb) != FDISK_DISKLABEL_DOS)
+				fprintf(f, ", attrs=\"%s\"", pa->attrs);
+		}
+		if (fdisk_partition_is_bootable(pa))
+			fprintf(f, ", bootable");
+		fputc('\n', f);
+	}
+
+	DBG(SCRIPT, ul_debugobj(dp, "write script done"));
+	return 0;
+}
+
+static inline int is_header_line(const char *s)
+{
+	const char *p = strchr(s, ':');
+
+	if (!p || p == s || !*(p + 1) || strchr(s, '='))
+		return 0;
+
+	return 1;
+}
+
+/* parses "<name>: value", note modifies @s*/
+static int parse_header_line(struct fdisk_script *dp, char *s)
+{
+	int rc = -EINVAL;
+	char *name, *value;
+
+	DBG(SCRIPT, ul_debugobj(dp, "   parse header '%s'", s));
+
+	if (!s || !*s)
+		return -EINVAL;
+
+	name = s;
+	value = strchr(s, ':');
+	if (!value)
+		goto done;
+	*value = '\0';
+	value++;
+
+	ltrim_whitespace((unsigned char *) name);
+	rtrim_whitespace((unsigned char *) name);
+	ltrim_whitespace((unsigned char *) value);
+	rtrim_whitespace((unsigned char *) value);
+
+	if (strcmp(name, "label") == 0) {
+		if (dp->cxt && !fdisk_get_label(dp->cxt, value))
+			goto done;			/* unknown label name */
+	} else if (strcmp(name, "unit") == 0) {
+		if (strcmp(value, "sectors") != 0)
+			goto done;			/* only "sectors" supported */
+	} else if (strcmp(name, "label-id") == 0
+		   || strcmp(name, "device") == 0) {
+		;					/* whatever is posssible */
+	} else
+		goto done;				/* unknown header */
+
+	if (*name && *value)
+		rc = fdisk_script_set_header(dp, name, value);
+done:
+	if (rc)
+		DBG(SCRIPT, ul_debugobj(dp, "header parse error: "
+				"[rc=%d, name='%s', value='%s']",
+				rc, name, value));
+	return rc;
+
+}
+
+/* returns zero terminated string with next token and @str is updated */
+static char *next_token(char **str)
+{
+	char *tk_begin = NULL,
+	     *tk_end = NULL,
+	     *end = NULL,
+	     *p;
+	int open_quote = 0;
+
+	for (p = *str; p && *p; p++) {
+		if (!tk_begin) {
+			if (isblank(*p))
+				continue;
+			tk_begin = *p == '"' ? p + 1 : p;
+		}
+		if (*p == '"')
+			open_quote ^= 1;
+		if (open_quote)
+			continue;
+		if (isblank(*p) || *p == ',' || *p == ';' || *p == '"' )
+			tk_end = p;
+		else if (*(p + 1) == '\0')
+			tk_end = p + 1;
+		if (tk_begin && tk_end)
+			break;
+	}
+
+	if (!tk_end)
+		return NULL;
+	end = isblank(*tk_end) ? (char *) skip_blank(tk_end) : tk_end;
+	if (*end == ',' || *end == ';')
+		end++;
+
+	*tk_end = '\0';
+	*str = end;
+	return tk_begin;
+}
+
+static int next_number(char **s, uint64_t *num, int *power)
+{
+	char *tk;
+	int rc = -EINVAL;
+
+	assert(num);
+	assert(s);
+
+	tk = next_token(s);
+	if (tk)
+		rc = parse_size(tk, (uintmax_t *) num, power);
+	return rc;
+}
+
+static int next_string(char **s, char **str)
+{
+	char *tk;
+	int rc = -EINVAL;
+
+	assert(s);
+	assert(str);
+
+	tk = next_token(s);
+	if (tk) {
+		*str = strdup(tk);
+		rc = !*str ? -ENOMEM : 0;
+	}
+	return rc;
+}
+
+static int partno_from_devname(char *s)
+{
+	int pno;
+	size_t sz;
+	char *end, *p;
+
+	sz = rtrim_whitespace((unsigned char *)s);
+	p = s + sz - 1;
+
+	while (p > s && isdigit(*(p - 1)))
+		p--;
+
+	errno = 0;
+	pno = strtol(p, &end, 10);
+	if (errno || !end || p == end)
+		return -1;
+	return pno - 1;
+}
+
+/* dump format
+ * <device>: start=<num>, size=<num>, type=<string>, ...
+ */
+static int parse_script_line(struct fdisk_script *dp, char *s)
+{
+	char *p, *x;
+	struct fdisk_partition *pa;
+	int rc = 0;
+	uint64_t num;
+	int pno;
+
+	assert(dp);
+	assert(s);
+
+	DBG(SCRIPT, ul_debugobj(dp, "   parse script line: '%s'", s));
+
+	pa = fdisk_new_partition();
+	if (!pa)
+		return -ENOMEM;
+
+	fdisk_partition_start_follow_default(pa, 1);
+	fdisk_partition_end_follow_default(pa, 1);
+	fdisk_partition_partno_follow_default(pa, 1);
+
+	/* set partno */
+	p = strchr(s, ':');
+	x = strchr(s, '=');
+	if (p && (!x || p < x)) {
+		*p = '\0';
+		p++;
+
+		pno = partno_from_devname(s);
+		if (pno >= 0) {
+			fdisk_partition_partno_follow_default(pa, 0);
+			fdisk_partition_set_partno(pa, pno);
+		}
+	} else
+		p = s;
+
+	while (rc == 0 && p && *p) {
+
+		DBG(SCRIPT, ul_debugobj(dp, " parsing '%s'", p));
+		p = (char *) skip_blank(p);
+
+		if (!strncasecmp(p, "start=", 6)) {
+			p += 6;
+			rc = next_number(&p, &num, NULL);
+			if (!rc) {
+				fdisk_partition_set_start(pa, num);
+				fdisk_partition_start_follow_default(pa, 0);
+			}
+		} else if (!strncasecmp(p, "size=", 5)) {
+			int pow = 0;
+
+			p += 5;
+			rc = next_number(&p, &num, &pow);
+			if (!rc) {
+				if (pow)	/* specified as <num><suffix> */
+					num /= dp->cxt->sector_size;
+				else		/* specified as number of sectors */
+					fdisk_partition_size_explicit(pa, 1);
+				fdisk_partition_set_size(pa, num);
+				fdisk_partition_end_follow_default(pa, 0);
+			}
+
+		} else if (!strncasecmp(p, "bootable", 8)) {
+			char *tk = next_token(&p);
+			if (strcmp(tk, "bootable") == 0)
+				pa->boot = 1;
+			else
+				rc = -EINVAL;
+
+		} else if (!strncasecmp(p, "attrs=", 6)) {
+			p += 6;
+			rc = next_string(&p, &pa->attrs);
+
+		} else if (!strncasecmp(p, "uuid=", 5)) {
+			p += 5;
+			rc = next_string(&p, &pa->uuid);
+
+		} else if (!strncasecmp(p, "name=", 5)) {
+			p += 5;
+			rc = next_string(&p, &pa->name);
+
+		} else if (!strncasecmp(p, "type=", 5) ||
+
+			   !strncasecmp(p, "Id=", 3)) {		/* backward compatiility */
+			char *type;
+
+			p += (*p == 'I' ? 3 : 5);		/* "Id=" or "type=" */
+
+			rc = next_string(&p, &type);
+			if (rc)
+				break;
+			pa->type = fdisk_label_parse_parttype(
+					script_get_label(dp), type);
+			free(type);
+
+			if (!pa->type || fdisk_parttype_is_unknown(pa->type)) {
+				rc = -EINVAL;
+				fdisk_unref_parttype(pa->type);
+				pa->type = NULL;
+				break;
+			}
+
+		} else {
+			DBG(SCRIPT, ul_debugobj(dp, "script parse error: unknown field '%s'", p));
+			rc = -EINVAL;
+			break;
+		}
+	}
+
+	if (!rc)
+		rc = fdisk_table_add_partition(dp->table, pa);
+	if (rc)
+		DBG(SCRIPT, ul_debugobj(dp, "script parse error: [rc=%d]", rc));
+
+	fdisk_unref_partition(pa);
+	return rc;
+}
+
+/* original sfdisk supports partition types shortcuts like 'L' = Linux native
+ */
+static struct fdisk_parttype *translate_type_shortcuts(struct fdisk_script *dp, char *str)
+{
+	struct fdisk_label *lb;
+	const char *type = NULL;
+
+	if (strlen(str) != 1)
+		return NULL;
+
+	lb = script_get_label(dp);
+	if (!lb)
+		return NULL;
+
+	if (lb->id == FDISK_DISKLABEL_DOS) {
+		switch (*str) {
+		case 'L':	/* Linux */
+			type = "83";
+			break;
+		case 'S':	/* Swap */
+			type = "82";
+			break;
+		case 'E':	/* Dos extended */
+			type = "05";
+			break;
+		case 'X':	/* Linux extended */
+			type = "85";
+			break;
+		}
+	} else if (lb->id == FDISK_DISKLABEL_GPT) {
+		switch (*str) {
+		case 'L':	/* Linux */
+			type = "0FC63DAF-8483-4772-8E79-3D69D8477DE4";
+			break;
+		case 'S':	/* Swap */
+			type = "0657FD6D-A4AB-43C4-84E5-0933C84B4F4F";
+			break;
+		case 'H':	/* Home */
+			type = "933AC7E1-2EB4-4F13-B844-0E14E2AEF915";
+			break;
+		}
+	}
+
+	return type ? fdisk_label_parse_parttype(lb, type) : NULL;
+}
+
+/* simple format:
+ * <start>, <size>, <type>, <bootable>, ...
+ */
+static int parse_commas_line(struct fdisk_script *dp, char *s)
+{
+	int rc = 0;
+	char *p = s, *str;
+	struct fdisk_partition *pa;
+	enum { ITEM_START, ITEM_SIZE, ITEM_TYPE, ITEM_BOOTABLE };
+	int item = -1;
+
+	assert(dp);
+	assert(s);
+
+	pa = fdisk_new_partition();
+	if (!pa)
+		return -ENOMEM;
+
+	fdisk_partition_start_follow_default(pa, 1);
+	fdisk_partition_end_follow_default(pa, 1);
+	fdisk_partition_partno_follow_default(pa, 1);
+
+	while (rc == 0 && p && *p) {
+		uint64_t num;
+		char *begin;
+
+		p = (char *) skip_blank(p);
+		item++;
+
+		DBG(SCRIPT, ul_debugobj(dp, " parsing item %d ('%s')", item, p));
+		begin = p;
+
+		switch (item) {
+		case ITEM_START:
+			if (*p == ',' || *p == ';')
+				fdisk_partition_start_follow_default(pa, 1);
+			else {
+				rc = next_number(&p, &num, NULL);
+				if (!rc)
+					fdisk_partition_set_start(pa, num);
+				fdisk_partition_start_follow_default(pa, 0);
+			}
+			break;
+		case ITEM_SIZE:
+			if (*p == ',' || *p == ';' || *p == '+')
+				fdisk_partition_end_follow_default(pa, 1);
+			else {
+				int pow = 0;
+				rc = next_number(&p, &num, &pow);
+				if (!rc) {
+					if (pow) /* specified as <size><suffix> */
+						num /= dp->cxt->sector_size;
+					else	 /* specified as number of sectors */
+						fdisk_partition_size_explicit(pa, 1);
+					fdisk_partition_set_size(pa, num);
+				}
+				fdisk_partition_end_follow_default(pa, 0);
+			}
+			break;
+		case ITEM_TYPE:
+			if (*p == ',' || *p == ';')
+				break;	/* use default type */
+
+			rc = next_string(&p, &str);
+			if (rc)
+				break;
+
+			pa->type = translate_type_shortcuts(dp, str);
+			if (!pa->type)
+				pa->type = fdisk_label_parse_parttype(
+						script_get_label(dp), str);
+			free(str);
+
+			if (!pa->type || fdisk_parttype_is_unknown(pa->type)) {
+				rc = -EINVAL;
+				fdisk_unref_parttype(pa->type);
+				pa->type = NULL;
+				break;
+			}
+			break;
+		case ITEM_BOOTABLE:
+			if (*p == ',' || *p == ';')
+				break;
+			else {
+				char *tk = next_token(&p);
+				if (tk && *tk == '*' && *(tk + 1) == '\0')
+					pa->boot = 1;
+				else if (tk && *tk == '-' && *(tk + 1) == '\0')
+					pa->boot = 0;
+				else
+					rc = -EINVAL;
+			}
+			break;
+		default:
+			break;
+		}
+
+		if (begin == p)
+			p++;
+	}
+
+	if (!rc)
+		rc = fdisk_table_add_partition(dp->table, pa);
+	if (rc)
+		DBG(SCRIPT, ul_debugobj(dp, "script parse error: [rc=%d]", rc));
+
+	fdisk_unref_partition(pa);
+	return rc;
+}
+
+/* modifies @s ! */
+int fdisk_script_read_buffer(struct fdisk_script *dp, char *s)
+{
+	int rc = 0;
+
+	assert(dp);
+	assert(s);
+
+	DBG(SCRIPT, ul_debugobj(dp, "  parsing buffer"));
+
+	s = (char *) skip_blank(s);
+	if (!s || !*s)
+		return 0;	/* nothing baby, ignore */
+
+	if (!dp->table) {
+		dp->table = fdisk_new_table();
+		if (!dp->table)
+			return -ENOMEM;
+	}
+
+	/* parse header lines only if no partition specified yet */
+	if (fdisk_table_is_empty(dp->table) && is_header_line(s))
+		rc = parse_header_line(dp, s);
+
+	/* parse script format */
+	else if (strchr(s, '='))
+		rc = parse_script_line(dp, s);
+
+	/* parse simple <value>, ... format */
+	else
+		rc = parse_commas_line(dp, s);
+
+	if (rc)
+		DBG(SCRIPT, ul_debugobj(dp, "%zu: parse error [rc=%d]",
+				dp->nlines, rc));
+	return rc;
+}
+
+/**
+ * fdisk_script_read_line:
+ * @dp: script
+ * @f: file
+ * @buf: buffer to store one line of the file
+ * @bufsz: buffer size
+ *
+ * Reads next line into dump.
+ *
+ * Returns: 0 on success, <0 on error, 1 when nothing to read.
+ */
+int fdisk_script_read_line(struct fdisk_script *dp, FILE *f, char *buf, size_t bufsz)
+{
+	char *s;
+
+	assert(dp);
+	assert(f);
+
+	DBG(SCRIPT, ul_debugobj(dp, " parsing line %zu", dp->nlines));
+
+	/* read the next non-blank non-comment line */
+	do {
+		if (fgets(buf, bufsz, f) == NULL)
+			return 1;
+		dp->nlines++;
+		s = strchr(buf, '\n');
+		if (!s) {
+			/* Missing final newline?  Otherwise an extremely */
+			/* long line - assume file was corrupted */
+			if (feof(f)) {
+				DBG(SCRIPT, ul_debugobj(dp, "no final newline"));
+				s = strchr(buf, '\0');
+			} else {
+				DBG(SCRIPT, ul_debugobj(dp,
+					"%zu: missing newline at line", dp->nlines));
+				return -EINVAL;
+			}
+		}
+
+		*s = '\0';
+		if (--s >= buf && *s == '\r')
+			*s = '\0';
+		s = (char *) skip_blank(buf);
+	} while (*s == '\0' || *s == '#');
+
+	return fdisk_script_read_buffer(dp, s);
+}
+
+
+/**
+ * fdisk_script_read_file:
+ * @dp: script
+ * @f: input file
+ *
+ * Reads file @f into script @dp.
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int fdisk_script_read_file(struct fdisk_script *dp, FILE *f)
+{
+	char buf[BUFSIZ];
+	int rc;
+
+	assert(dp);
+	assert(f);
+
+	DBG(SCRIPT, ul_debugobj(dp, "parsing file"));
+
+	while (!feof(f)) {
+		rc = fdisk_script_read_line(dp, f, buf, sizeof(buf));
+		if (rc)
+			break;
+	}
+
+	if (rc == 1)
+		rc = 0;		/* end of file */
+
+	DBG(SCRIPT, ul_debugobj(dp, "parsing file done [rc=%d]", rc));
+	return rc;
+}
+
+/**
+ * fdisk_set_script:
+ * @cxt: context
+ * @dp: script (or NULL to remove previous reference)
+ *
+ * Sets reference to the @dp script. The script headers might be used by label
+ * drivers to overwrite built-in defaults (for example disk label Id) and label
+ * driver might optimize the default semantic to be more usable for scripts
+ * (for example to not ask for primary/logical/extended partition type).
+ *
+ * Note that script also contains reference to the fdisk context (see
+ * fdisk_new_script()). This context may be completely independent on
+ * context used for fdisk_set_script().
+ *
+ * Returns: <0 on error, 0 on success.
+ */
+int fdisk_set_script(struct fdisk_context *cxt, struct fdisk_script *dp)
+{
+	assert(cxt);
+
+	/* unref old */
+	if (cxt->script)
+		fdisk_unref_script(cxt->script);
+
+	/* ref new */
+	cxt->script = dp;
+	if (cxt->script) {
+		DBG(CXT, ul_debugobj(cxt, "setting reference to script %p", cxt->script));
+		fdisk_ref_script(cxt->script);
+	}
+
+	return 0;
+}
+
+/**
+ * fdisk_get_script:
+ * @cxt: context
+ *
+ * Returns: the current script or NULL.
+ */
+struct fdisk_script *fdisk_get_script(struct fdisk_context *cxt)
+{
+	assert(cxt);
+	return cxt->script;
+}
+
+/**
+ * fdisk_apply_script_headers:
+ * @cxt: context
+ * @dp: script
+ *
+ * Associte context @cxt with script @dp and creates a new empty disklabel.
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int fdisk_apply_script_headers(struct fdisk_context *cxt, struct fdisk_script *dp)
+{
+	const char *name;
+
+	assert(cxt);
+	assert(dp);
+
+	DBG(SCRIPT, ul_debugobj(dp, "applying script headers"));
+	fdisk_set_script(cxt, dp);
+
+	/* create empty label */
+	name = fdisk_script_get_header(dp, "label");
+	if (!name)
+		return -EINVAL;
+
+	return fdisk_create_disklabel(cxt, name);
+}
+
+/**
+ * fdisk_apply_script:
+ * @cxt: context
+ * @dp: script
+ *
+ * This function creates a new disklabel and partition within context @cxt. You
+ * have to call fdisk_write_disklabel() to apply changes to the device.
+ *
+ * Returns: 0 on error, <0 on error.
+ */
+int fdisk_apply_script(struct fdisk_context *cxt, struct fdisk_script *dp)
+{
+	int rc;
+	struct fdisk_script *old;
+
+	assert(dp);
+	assert(cxt);
+
+	DBG(CXT, ul_debugobj(cxt, "applying script %p", dp));
+
+	old = fdisk_get_script(cxt);
+
+	/* create empty disk label */
+	rc = fdisk_apply_script_headers(cxt, dp);
+
+	/* create partitions */
+	if (!rc && dp->table)
+		rc = fdisk_apply_table(cxt, dp->table);
+
+	fdisk_set_script(cxt, old);
+	DBG(CXT, ul_debugobj(cxt, "script done [rc=%d]", rc));
+	return rc;
+}
+
+#ifdef TEST_PROGRAM
+int test_dump(struct fdisk_test *ts, int argc, char *argv[])
+{
+	char *devname = argv[1];
+	struct fdisk_context *cxt;
+	struct fdisk_script *dp;
+
+	cxt = fdisk_new_context();
+	fdisk_assign_device(cxt, devname, 1);
+
+	dp = fdisk_new_script(cxt);
+	fdisk_script_read_context(dp, NULL);
+
+	fdisk_script_write_file(dp, stdout);
+	fdisk_unref_script(dp);
+	fdisk_unref_context(cxt);
+
+	return 0;
+}
+
+int test_read(struct fdisk_test *ts, int argc, char *argv[])
+{
+	char *filename = argv[1];
+	struct fdisk_script *dp;
+	struct fdisk_context *cxt;
+	FILE *f;
+
+	if (!(f = fopen(filename, "r")))
+		err(EXIT_FAILURE, "%s: cannot open", filename);
+
+	cxt = fdisk_new_context();
+	dp = fdisk_new_script(cxt);
+
+	fdisk_script_read_file(dp, f);
+	fclose(f);
+
+	fdisk_script_write_file(dp, stdout);
+	fdisk_unref_script(dp);
+	fdisk_unref_context(cxt);
+
+	return 0;
+}
+
+int test_stdin(struct fdisk_test *ts, int argc, char *argv[])
+{
+	char buf[BUFSIZ];
+	struct fdisk_script *dp;
+	struct fdisk_context *cxt;
+	int rc = 0;
+
+	cxt = fdisk_new_context();
+	dp = fdisk_new_script(cxt);
+	fdisk_script_set_header(dp, "label", "dos");
+
+	printf("<start>, <size>, <type>, <bootable: *|->\n");
+	do {
+		struct fdisk_partition *pa;
+		size_t n = fdisk_table_get_nents(dp->table);
+
+		printf(" #%zu :\n", n + 1);
+		rc = fdisk_script_read_line(dp, stdin, buf, sizeof(buf));
+
+		if (rc == 0) {
+			pa = fdisk_table_get_partition(dp->table, n);
+			printf(" #%zu  %12ju %12ju\n",	n + 1,
+						fdisk_partition_get_start(pa),
+						fdisk_partition_get_size(pa));
+		}
+	} while (rc == 0);
+
+	if (!rc)
+		fdisk_script_write_file(dp, stdout);
+	fdisk_unref_script(dp);
+	fdisk_unref_context(cxt);
+
+	return rc;
+}
+
+int test_apply(struct fdisk_test *ts, int argc, char *argv[])
+{
+	char *devname = argv[1], *scriptname = argv[2];
+	struct fdisk_context *cxt;
+	struct fdisk_script *dp = NULL;
+	struct fdisk_table *tb = NULL;
+	struct fdisk_iter *itr = NULL;
+	struct fdisk_partition *pa = NULL;
+	int rc;
+
+	cxt = fdisk_new_context();
+	fdisk_assign_device(cxt, devname, 0);
+
+	dp = fdisk_new_script_from_file(cxt, scriptname);
+	if (!dp)
+		return -errno;
+
+	rc = fdisk_apply_script(cxt, dp);
+	if (rc)
+		goto done;
+	fdisk_unref_script(dp);
+
+	/* list result */
+	fdisk_list_disklabel(cxt);
+	fdisk_get_partitions(cxt, &tb);
+
+	itr = fdisk_new_iter(FDISK_ITER_FORWARD);
+	while (fdisk_table_next_partition(tb, itr, &pa) == 0) {
+		printf(" #%zu  %12ju %12ju\n",	fdisk_partition_get_partno(pa),
+						fdisk_partition_get_start(pa),
+						fdisk_partition_get_size(pa));
+	}
+
+done:
+	fdisk_free_iter(itr);
+	fdisk_unref_table(tb);
+
+	/*fdisk_write_disklabel(cxt);*/
+	fdisk_unref_context(cxt);
+	return 0;
+}
+
+int main(int argc, char *argv[])
+{
+	struct fdisk_test tss[] = {
+	{ "--dump",    test_dump,    "<device>            dump PT as script" },
+	{ "--read",    test_read,    "<file>              read PT script from file" },
+	{ "--apply",   test_apply,   "<device> <file>     try apply script from file to device" },
+	{ "--stdin",   test_stdin,   "                    read input like sfdisk" },
+	{ NULL }
+	};
+
+	return fdisk_run_test(tss, argc, argv);
+}
+
+#endif
diff --git a/libblkid/libfdisk/src/sgi.c b/libblkid/libfdisk/src/sgi.c
new file mode 100644
index 0000000..cd4cedf
--- /dev/null
+++ b/libblkid/libfdisk/src/sgi.c
@@ -0,0 +1,1185 @@
+/*
+ *
+ * Copyright (C) 2012 Davidlohr Bueso <dave@gnu.org>
+ *               2013 Karel Zak <kzak@redhat.com>
+ *
+ * This is a re-written version for libfdisk, the original was fdisksgilabel.c
+ * from util-linux fdisk, by:
+ *
+ *               Andreas Neuper, Sep 1998,
+ *               Arnaldo Carvalho de Melo <acme@conectiva.com.br>, Mar 1999,
+ *               Phillip Kesling <pkesling@sgi.com>, Mar 2003.
+ */
+
+#include "c.h"
+#include "nls.h"
+#include "all-io.h"
+
+#include "blkdev.h"
+
+#include "bitops.h"
+#include "pt-sgi.h"
+#include "pt-mbr.h"
+#include "fdiskP.h"
+
+/**
+ * SECTION: sgi
+ * @title: SGI
+ * @short_description: disk label specific functions
+ *
+ */
+
+/*
+ * in-memory fdisk SGI stuff
+ */
+struct fdisk_sgi_label {
+	struct fdisk_label	head;		/* generic fdisk part */
+	struct sgi_disklabel	*header;	/* on-disk data (pointer to cxt->firstsector) */
+
+	struct sgi_freeblocks {
+		unsigned int first;
+		unsigned int last;
+	} freelist[SGI_MAXPARTITIONS + 1];
+};
+
+static struct fdisk_parttype sgi_parttypes[] =
+{
+	{SGI_TYPE_VOLHDR,	N_("SGI volhdr")},
+	{SGI_TYPE_TRKREPL,	N_("SGI trkrepl")},
+	{SGI_TYPE_SECREPL,	N_("SGI secrepl")},
+	{SGI_TYPE_SWAP,		N_("SGI raw")},
+	{SGI_TYPE_BSD,		N_("SGI bsd")},
+	{SGI_TYPE_SYSV,		N_("SGI sysv")},
+	{SGI_TYPE_ENTIRE_DISK,	N_("SGI volume")},
+	{SGI_TYPE_EFS,		N_("SGI efs")},
+	{SGI_TYPE_LVOL,		N_("SGI lvol")},
+	{SGI_TYPE_RLVOL,	N_("SGI rlvol")},
+	{SGI_TYPE_XFS,		N_("SGI xfs")},
+	{SGI_TYPE_XFSLOG,	N_("SGI xfslog")},
+	{SGI_TYPE_XLV,		N_("SGI xlv")},
+	{SGI_TYPE_XVM,		N_("SGI xvm")},
+	{MBR_LINUX_SWAP_PARTITION, N_("Linux swap")},
+	{MBR_LINUX_DATA_PARTITION, N_("Linux native")},
+	{MBR_LINUX_LVM_PARTITION, N_("Linux LVM")},
+	{MBR_LINUX_RAID_PARTITION, N_("Linux RAID")},
+	{0, NULL }
+};
+
+static unsigned int sgi_get_start_sector(struct fdisk_context *cxt, int i );
+static unsigned int sgi_get_num_sectors(struct fdisk_context *cxt, int i );
+static int sgi_get_bootpartition(struct fdisk_context *cxt);
+static int sgi_get_swappartition(struct fdisk_context *cxt);
+
+/* Returns a pointer buffer with on-disk data. */
+static inline struct sgi_disklabel *self_disklabel(struct fdisk_context *cxt)
+{
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, SGI));
+
+	return ((struct fdisk_sgi_label *) cxt->label)->header;
+}
+
+/* Returns in-memory fdisk data. */
+static inline struct fdisk_sgi_label *self_label(struct fdisk_context *cxt)
+{
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, SGI));
+
+	return (struct fdisk_sgi_label *) cxt->label;
+}
+
+/*
+ * Information within second on-disk block
+ */
+#define	SGI_INFO_MAGIC		0x00072959
+
+struct sgi_info {
+	unsigned int   magic;		/* looks like a magic number */
+	unsigned int   a2;
+	unsigned int   a3;
+	unsigned int   a4;
+	unsigned int   b1;
+	unsigned short b2;
+	unsigned short b3;
+	unsigned int   c[16];
+	unsigned short d[3];
+	unsigned char  scsi_string[50];
+	unsigned char  serial[137];
+	unsigned short check1816;
+	unsigned char  installer[225];
+};
+
+static struct sgi_info *sgi_new_info(void)
+{
+	struct sgi_info *info = calloc(1, sizeof(struct sgi_info));
+
+	if (!info)
+		return NULL;
+
+	info->magic = cpu_to_be32(SGI_INFO_MAGIC);
+	info->b1 = cpu_to_be32(-1);
+	info->b2 = cpu_to_be16(-1);
+	info->b3 = cpu_to_be16(1);
+
+	/* You may want to replace this string !!!!!!! */
+	strcpy((char *) info->scsi_string, "IBM OEM 0662S12         3 30");
+	strcpy((char *) info->serial, "0000");
+	info->check1816 = cpu_to_be16(18 * 256 + 16);
+	strcpy((char *) info->installer, "Sfx version 5.3, Oct 18, 1994");
+
+	return info;
+}
+
+static void sgi_free_info(struct sgi_info *info)
+{
+	free(info);
+}
+
+/**
+ * fdisk_sgi_create_info:
+ * @cxt: context
+ *
+ * This function add hint about SGI label (e.g. set "sgilabel" as volume name)
+ * to the first SGI volume. This is probably old SGI convention without any
+ * effect to the device partitioning.
+ *
+ * Returns: 0 on success, <0 on error
+ */
+int fdisk_sgi_create_info(struct fdisk_context *cxt)
+{
+	struct sgi_disklabel *sgilabel = self_disklabel(cxt);
+
+	/* I keep SGI's habit to write the sgilabel to the second block */
+	sgilabel->volume[0].block_num = cpu_to_be32(2);
+	sgilabel->volume[0].num_bytes = cpu_to_be32(sizeof(struct sgi_info));
+	strncpy((char *) sgilabel->volume[0].name, "sgilabel", 8);
+
+	fdisk_info(cxt, _("SGI info created on second sector."));
+	return 0;
+}
+
+
+/*
+ * only dealing with free blocks here
+ */
+static void set_freelist(struct fdisk_context *cxt,
+		size_t i, unsigned int f, unsigned int l)
+{
+	struct fdisk_sgi_label *sgi = self_label(cxt);
+
+	if (i < ARRAY_SIZE(sgi->freelist)) {
+		sgi->freelist[i].first = f;
+		sgi->freelist[i].last = l;
+	}
+}
+
+static void add_to_freelist(struct fdisk_context *cxt,
+		unsigned int f, unsigned int l)
+{
+	struct fdisk_sgi_label *sgi = self_label(cxt);
+	size_t i;
+
+	for (i = 0; i < ARRAY_SIZE(sgi->freelist); i++) {
+		if (sgi->freelist[i].last == 0)
+			break;
+	}
+	set_freelist(cxt, i, f, l);
+}
+
+static void clear_freelist(struct fdisk_context *cxt)
+{
+	struct fdisk_sgi_label *sgi = self_label(cxt);
+
+	memset(sgi->freelist, 0, sizeof(sgi->freelist));
+}
+
+static unsigned int is_in_freelist(struct fdisk_context *cxt, unsigned int b)
+{
+	struct fdisk_sgi_label *sgi = self_label(cxt);
+	size_t i;
+
+	for (i = 0; i < ARRAY_SIZE(sgi->freelist); i++) {
+		if (sgi->freelist[i].first <= b
+		    && sgi->freelist[i].last >= b)
+			return sgi->freelist[i].last;
+	}
+
+	return 0;
+}
+
+
+static int sgi_get_nsect(struct fdisk_context *cxt)
+{
+	struct sgi_disklabel *sgilabel = self_disklabel(cxt);
+	return be16_to_cpu(sgilabel->devparam.nsect);
+}
+
+static int sgi_get_ntrks(struct fdisk_context *cxt)
+{
+	struct sgi_disklabel *sgilabel = self_disklabel(cxt);
+	return be16_to_cpu(sgilabel->devparam.ntrks);
+}
+
+static size_t count_used_partitions(struct fdisk_context *cxt)
+{
+	size_t i, ct = 0;
+
+	for (i = 0; i < cxt->label->nparts_max; i++)
+		ct += sgi_get_num_sectors(cxt, i) > 0;
+
+	return ct;
+}
+
+static int sgi_probe_label(struct fdisk_context *cxt)
+{
+	struct fdisk_sgi_label *sgi;
+	struct sgi_disklabel *sgilabel;
+
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, SGI));
+	assert(sizeof(struct sgi_disklabel) <= 512);
+
+	/* map first sector to header */
+	sgi = (struct fdisk_sgi_label *) cxt->label;
+	sgi->header = (struct sgi_disklabel *) cxt->firstsector;
+	sgilabel = sgi->header;
+
+	if (be32_to_cpu(sgilabel->magic) != SGI_LABEL_MAGIC) {
+		sgi->header = NULL;
+		return 0;
+	}
+
+	/*
+	 * test for correct checksum
+	 */
+	if (sgi_pt_checksum(sgilabel) != 0)
+		fdisk_warnx(cxt, _("Detected an SGI disklabel with wrong checksum."));
+
+	clear_freelist(cxt);
+	cxt->label->nparts_max = SGI_MAXPARTITIONS;
+	cxt->label->nparts_cur = count_used_partitions(cxt);
+	return 1;
+}
+
+static int sgi_list_table(struct fdisk_context *cxt)
+{
+	struct sgi_disklabel *sgilabel = self_disklabel(cxt);
+	struct sgi_device_parameter *sgiparam = &sgilabel->devparam;
+	int rc = 0;
+
+	if (fdisk_is_details(cxt))
+		fdisk_info(cxt, _(
+			"Label geometry: %d heads, %llu sectors\n"
+			"                %llu cylinders, %d physical cylinders\n"
+			"                %d extra sects/cyl, interleave %d:1\n"),
+			cxt->geom.heads, cxt->geom.sectors,
+			cxt->geom.cylinders, be16_to_cpu(sgiparam->pcylcount),
+			(int) sgiparam->sparecyl, be16_to_cpu(sgiparam->ilfact));
+
+	fdisk_info(cxt, _("Bootfile: %s"), sgilabel->boot_file);
+	return rc;
+}
+
+static unsigned int sgi_get_start_sector(struct fdisk_context *cxt, int i)
+{
+	struct sgi_disklabel *sgilabel = self_disklabel(cxt);
+	return be32_to_cpu(sgilabel->partitions[i].first_block);
+}
+
+static unsigned int sgi_get_num_sectors(struct fdisk_context *cxt, int i)
+{
+	struct sgi_disklabel *sgilabel = self_disklabel(cxt);
+	return be32_to_cpu(sgilabel->partitions[i].num_blocks);
+}
+
+static int sgi_get_sysid(struct fdisk_context *cxt, int i)
+{
+	struct sgi_disklabel *sgilabel = self_disklabel(cxt);
+	return be32_to_cpu(sgilabel->partitions[i].type);
+}
+
+static int sgi_get_bootpartition(struct fdisk_context *cxt)
+{
+	struct sgi_disklabel *sgilabel = self_disklabel(cxt);
+	return be16_to_cpu(sgilabel->root_part_num);
+}
+
+static int sgi_get_swappartition(struct fdisk_context *cxt)
+{
+	struct sgi_disklabel *sgilabel = self_disklabel(cxt);
+	return be16_to_cpu(sgilabel->swap_part_num);
+}
+
+static unsigned int sgi_get_lastblock(struct fdisk_context *cxt)
+{
+	return cxt->geom.heads * cxt->geom.sectors * cxt->geom.cylinders;
+}
+
+static struct fdisk_parttype *sgi_get_parttype(struct fdisk_context *cxt, size_t n)
+{
+	struct fdisk_parttype *t;
+
+	if (n >= cxt->label->nparts_max)
+		return NULL;
+
+	t = fdisk_label_get_parttype_from_code(cxt->label, sgi_get_sysid(cxt, n));
+	return t ? : fdisk_new_unknown_parttype(sgi_get_sysid(cxt, n), NULL);
+}
+
+/* fdisk_get_partition() backend */
+static int sgi_get_partition(struct fdisk_context *cxt, size_t n, struct fdisk_partition *pa)
+{
+	fdisk_sector_t start, len;
+
+	pa->used = sgi_get_num_sectors(cxt, n) > 0;
+	if (!pa->used)
+		return 0;
+
+	start = sgi_get_start_sector(cxt, n);
+	len = sgi_get_num_sectors(cxt, n);
+
+	pa->type = sgi_get_parttype(cxt, n);
+	pa->size = len;
+	pa->start = start;
+
+	if (pa->type && pa->type->code == SGI_TYPE_ENTIRE_DISK)
+		pa->wholedisk = 1;
+
+	pa->attrs = sgi_get_swappartition(cxt) == (int) n ? "swap" :
+		    sgi_get_bootpartition(cxt) == (int) n ? "boot" : NULL;
+	if (pa->attrs)
+		pa->attrs = strdup(pa->attrs);
+
+	return 0;
+}
+
+
+static int sgi_check_bootfile(struct fdisk_context *cxt, const char *name)
+{
+	size_t sz;
+	struct sgi_disklabel *sgilabel = self_disklabel(cxt);
+
+	sz = strlen(name);
+
+	if (sz < 3) {
+		/* "/a\n" is minimum */
+		fdisk_warnx(cxt, _("Invalid bootfile!  The bootfile must "
+				   "be an absolute non-zero pathname, "
+				   "e.g. \"/unix\" or \"/unix.save\"."));
+		return -EINVAL;
+
+	} else if (sz > sizeof(sgilabel->boot_file)) {
+		fdisk_warnx(cxt, P_("Name of bootfile is too long: %zu byte maximum.",
+				    "Name of bootfile is too long: %zu bytes maximum.",
+				    sizeof(sgilabel->boot_file)),
+			    sizeof(sgilabel->boot_file));
+		return -EINVAL;
+
+	} else if (*name != '/') {
+		fdisk_warnx(cxt, _("Bootfile must have a fully qualified pathname."));
+		return -EINVAL;
+	}
+
+	if (strncmp(name, (char *) sgilabel->boot_file,
+				sizeof(sgilabel->boot_file))) {
+		fdisk_warnx(cxt, _("Be aware that the bootfile is not checked "
+				   "for existence.  SGI's default is \"/unix\", "
+				   "and for backup \"/unix.save\"."));
+		return 0;	/* filename is correct and did change */
+	}
+
+	return 1;	/* filename did not change */
+}
+
+/**
+ * fdisk_sgi_set_bootfile:
+ * @cxt: context
+ *
+ * Allows to set SGI boot file. The function uses Ask API for dialog with
+ * user.
+ *
+ * Returns: 0 on success, <0 on error
+ */
+int fdisk_sgi_set_bootfile(struct fdisk_context *cxt)
+{
+	int rc = 0;
+	size_t sz;
+	char *name = NULL;
+	struct sgi_disklabel *sgilabel = self_disklabel(cxt);
+
+	fdisk_info(cxt, _("The current boot file is: %s"), sgilabel->boot_file);
+
+	rc = fdisk_ask_string(cxt, _("Enter of the new boot file"), &name);
+	if (rc == 0)
+		rc = sgi_check_bootfile(cxt, name);
+	if (rc) {
+		if (rc == 1)
+			fdisk_info(cxt, _("Boot file is unchanged."));
+		goto done;
+	}
+
+	memset(sgilabel->boot_file, 0, sizeof(sgilabel->boot_file));
+	sz = strlen(name);
+
+	assert(sz <= sizeof(sgilabel->boot_file));	/* see sgi_check_bootfile() */
+
+	memcpy(sgilabel->boot_file, name, sz);
+
+	fdisk_info(cxt, _("Bootfile has been changed to \"%s\"."), name);
+done:
+	free(name);
+	return rc;
+}
+
+static int sgi_write_disklabel(struct fdisk_context *cxt)
+{
+	struct sgi_disklabel *sgilabel;
+	struct sgi_info *info = NULL;
+
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, SGI));
+
+	sgilabel = self_disklabel(cxt);
+	sgilabel->csum = 0;
+	sgilabel->csum = cpu_to_be32(sgi_pt_checksum(sgilabel));
+
+	assert(sgi_pt_checksum(sgilabel) == 0);
+
+	if (lseek(cxt->dev_fd, 0, SEEK_SET) < 0)
+		goto err;
+	if (write_all(cxt->dev_fd, sgilabel, DEFAULT_SECTOR_SIZE))
+		goto err;
+	if (!strncmp((char *) sgilabel->volume[0].name, "sgilabel", 8)) {
+		/*
+		 * Keep this habit of first writing the "sgilabel".
+		 * I never tested whether it works without. (AN 1998-10-02)
+		 */
+		int infostartblock
+			= be32_to_cpu(sgilabel->volume[0].block_num);
+
+		if (lseek(cxt->dev_fd, (off_t) infostartblock *
+					DEFAULT_SECTOR_SIZE, SEEK_SET) < 0)
+			goto err;
+		info = sgi_new_info();
+		if (!info)
+			goto err;
+		if (write_all(cxt->dev_fd, info, sizeof(*info)))
+			goto err;
+	}
+
+	sgi_free_info(info);
+	return 0;
+err:
+	sgi_free_info(info);
+	return -errno;
+}
+
+static int compare_start(struct fdisk_context *cxt,
+			 const void *x, const void *y)
+{
+	/*
+	 * Sort according to start sectors and prefer the largest partition:
+	 * entry zero is the entire-disk entry.
+	 */
+	unsigned int i = *(int *) x;
+	unsigned int j = *(int *) y;
+	unsigned int a = sgi_get_start_sector(cxt, i);
+	unsigned int b = sgi_get_start_sector(cxt, j);
+	unsigned int c = sgi_get_num_sectors(cxt, i);
+	unsigned int d = sgi_get_num_sectors(cxt, j);
+
+	if (a == b)
+		return (d > c) ? 1 : (d == c) ? 0 : -1;
+	return (a > b) ? 1 : -1;
+}
+
+static void generic_swap(void *a0, void *b0, int size)
+{
+	char *a = a0, *b = b0;
+
+	for (; size > 0; --size, a++, b++) {
+		char t = *a;
+		*a = *b;
+		*b = t;
+	}
+}
+
+
+/* heap sort, based on Matt Mackall's linux kernel version */
+static void sort(void *base0, size_t num, size_t size, struct fdisk_context *cxt,
+		 int (*cmp_func)(struct fdisk_context *, const void *, const void *))
+{
+	/* pre-scale counters for performance */
+	int i = (num/2 - 1) * size;
+	size_t n = num * size, c, r;
+	char *base = base0;
+
+	/* heapify */
+	for ( ; i >= 0; i -= size) {
+		for (r = i; r * 2 + size < n; r  = c) {
+			c = r * 2 + size;
+			if (c < n - size &&
+			    cmp_func(cxt, base + c, base + c + size) < 0)
+				c += size;
+			if (cmp_func(cxt, base + r, base + c) >= 0)
+				break;
+			generic_swap(base + r, base + c, size);
+		}
+	}
+
+	/* sort */
+	for (i = n - size; i > 0; i -= size) {
+		generic_swap(base, base + i, size);
+		for (r = 0; r * 2 + size < (size_t) i; r = c) {
+			c = r * 2 + size;
+			if (c < i - size &&
+			    cmp_func(cxt, base + c, base + c + size) < 0)
+				c += size;
+			if (cmp_func(cxt, base + r, base + c) >= 0)
+				break;
+			generic_swap(base + r, base + c, size);
+		}
+	}
+}
+
+static int verify_disklabel(struct fdisk_context *cxt, int verbose)
+{
+	int Index[SGI_MAXPARTITIONS];	/* list of valid partitions */
+	int sortcount = 0;		/* number of used partitions, i.e. non-zero lengths */
+	int entire = 0, i = 0;
+	unsigned int start = 0;
+	long long gap = 0;	/* count unused blocks */
+	unsigned int lastblock = sgi_get_lastblock(cxt);
+
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, SGI));
+
+	clear_freelist(cxt);
+	memset(Index, 0, sizeof(Index));
+
+	for (i=0; i < SGI_MAXPARTITIONS; i++) {
+		if (sgi_get_num_sectors(cxt, i) != 0) {
+			Index[sortcount++] = i;
+			if (sgi_get_sysid(cxt, i) == SGI_TYPE_ENTIRE_DISK
+			    && entire++ == 1) {
+				if (verbose)
+					fdisk_info(cxt, _("More than one entire "
+						"disk entry present."));
+			}
+		}
+	}
+	if (sortcount == 0) {
+		if (verbose)
+			fdisk_info(cxt, _("No partitions defined."));
+		if (lastblock)
+			add_to_freelist(cxt, 0, lastblock);
+		return (lastblock > 0) ? 1 : (lastblock == 0) ? 0 : -1;
+	}
+
+	sort(Index, sortcount, sizeof(Index[0]), cxt, compare_start);
+
+	if (sgi_get_sysid(cxt, Index[0]) == SGI_TYPE_ENTIRE_DISK) {
+		if (verbose && Index[0] != 10)
+			fdisk_info(cxt, _("IRIX likes it when partition 11 "
+					  "covers the entire disk."));
+
+		if (verbose && sgi_get_start_sector(cxt, Index[0]) != 0)
+			fdisk_info(cxt, _("The entire disk partition should "
+					  "start at block 0, not at block %d."),
+				   sgi_get_start_sector(cxt, Index[0]));
+
+		if (verbose && sgi_get_num_sectors(cxt, Index[0]) != lastblock)
+			DBG(LABEL, ul_debug(
+				"entire disk partition=%ds, but disk=%ds",
+				sgi_get_num_sectors(cxt, Index[0]),
+				lastblock));
+		lastblock = sgi_get_num_sectors(cxt, Index[0]);
+	} else if (verbose) {
+		fdisk_info(cxt, _("Partition 11 should cover the entire disk."));
+		DBG(LABEL, ul_debug("sysid=%d\tpartition=%d",
+			       sgi_get_sysid(cxt, Index[0]), Index[0]+1));
+	}
+	for (i=1, start=0; i<sortcount; i++) {
+		int cylsize = sgi_get_nsect(cxt) * sgi_get_ntrks(cxt);
+
+		if (verbose && cylsize
+		    && (sgi_get_start_sector(cxt, Index[i]) % cylsize) != 0)
+			DBG(LABEL, ul_debug("partition %d does not start on "
+					"cylinder boundary.", Index[i]+1));
+
+		if (verbose && cylsize
+		    && sgi_get_num_sectors(cxt, Index[i]) % cylsize != 0)
+			DBG(LABEL, ul_debug("partition %d does not end on "
+					"cylinder boundary.", Index[i]+1));
+
+		/* We cannot handle several "entire disk" entries. */
+		if (sgi_get_sysid(cxt, Index[i]) == SGI_TYPE_ENTIRE_DISK)
+			continue;
+
+		if (start > sgi_get_start_sector(cxt, Index[i])) {
+			if (verbose)
+				fdisk_info(cxt,
+					   P_("Partitions %d and %d overlap by %d sector.",
+					      "Partitions %d and %d overlap by %d sectors.",
+					      start - sgi_get_start_sector(cxt, Index[i])),
+					   Index[i-1]+1, Index[i]+1,
+					   start - sgi_get_start_sector(cxt, Index[i]));
+			if (gap >  0) gap = -gap;
+			if (gap == 0) gap = -1;
+		}
+		if (start < sgi_get_start_sector(cxt, Index[i])) {
+			if (verbose)
+				fdisk_info(cxt,
+					   P_("Unused gap of %8u sector: sector %8u",
+					      "Unused gap of %8u sectors: sectors %8u-%u",
+					      sgi_get_start_sector(cxt, Index[i]) - start),
+					   sgi_get_start_sector(cxt, Index[i]) - start,
+					   start, sgi_get_start_sector(cxt, Index[i])-1);
+			gap += sgi_get_start_sector(cxt, Index[i]) - start;
+			add_to_freelist(cxt, start,
+					sgi_get_start_sector(cxt, Index[i]));
+		}
+		start = sgi_get_start_sector(cxt, Index[i])
+			+ sgi_get_num_sectors(cxt, Index[i]);
+		/* Align free space on cylinder boundary. */
+		if (cylsize && start % cylsize)
+			start += cylsize - (start % cylsize);
+
+		DBG(LABEL, ul_debug("%2d:%12d\t%12d\t%12d", Index[i],
+				       sgi_get_start_sector(cxt, Index[i]),
+				       sgi_get_num_sectors(cxt, Index[i]),
+				       sgi_get_sysid(cxt, Index[i])));
+	}
+	if (start < lastblock) {
+		if (verbose)
+			fdisk_info(cxt, P_("Unused gap of %8u sector: sector %8u",
+					   "Unused gap of %8u sectors: sectors %8u-%u",
+					   lastblock - start),
+				   lastblock - start, start, lastblock-1);
+		gap += lastblock - start;
+		add_to_freelist(cxt, start, lastblock);
+	}
+	/*
+	 * Done with arithmetics. Go for details now.
+	 */
+	if (verbose) {
+		if (sgi_get_bootpartition(cxt) < 0
+		    || !sgi_get_num_sectors(cxt, sgi_get_bootpartition(cxt)))
+			fdisk_info(cxt, _("The boot partition does not exist."));
+
+		if (sgi_get_swappartition(cxt) < 0
+		    || !sgi_get_num_sectors(cxt, sgi_get_swappartition(cxt)))
+			fdisk_info(cxt, _("The swap partition does not exist."));
+
+		else if (sgi_get_sysid(cxt, sgi_get_swappartition(cxt)) != SGI_TYPE_SWAP
+		    && sgi_get_sysid(cxt, sgi_get_swappartition(cxt)) != MBR_LINUX_SWAP_PARTITION)
+			fdisk_info(cxt, _("The swap partition has no swap type."));
+
+		if (sgi_check_bootfile(cxt, "/unix"))
+			fdisk_info(cxt, _("You have chosen an unusual bootfile name."));
+	}
+
+	return (gap > 0) ? 1 : (gap == 0) ? 0 : -1;
+}
+
+static int sgi_verify_disklabel(struct fdisk_context *cxt)
+{
+	return verify_disklabel(cxt, 1);
+}
+
+static int sgi_gaps(struct fdisk_context *cxt)
+{
+	/*
+	 * returned value is:
+	 *  = 0 : disk is properly filled to the rim
+	 *  < 0 : there is an overlap
+	 *  > 0 : there is still some vacant space
+	 */
+	return verify_disklabel(cxt, 0);
+}
+
+/* Returns partition index of first entry marked as entire disk. */
+static int sgi_entire(struct fdisk_context *cxt)
+{
+	size_t i;
+
+	for (i = 0; i < SGI_MAXPARTITIONS; i++)
+		if (sgi_get_sysid(cxt, i) == SGI_TYPE_ENTIRE_DISK)
+			return i;
+	return -1;
+}
+
+static int set_partition(struct fdisk_context *cxt, size_t i,
+			     unsigned int start, unsigned int length, int sys)
+{
+	struct sgi_disklabel *sgilabel;
+
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, SGI));
+
+	sgilabel = self_disklabel(cxt);
+	sgilabel->partitions[i].type = cpu_to_be32(sys);
+	sgilabel->partitions[i].num_blocks = cpu_to_be32(length);
+	sgilabel->partitions[i].first_block = cpu_to_be32(start);
+
+	fdisk_label_set_changed(cxt->label, 1);
+
+	if (sgi_gaps(cxt) < 0)	/* rebuild freelist */
+		fdisk_warnx(cxt, _("Partition overlap on the disk."));
+	if (length) {
+		struct fdisk_parttype *t =
+				fdisk_label_get_parttype_from_code(cxt->label, sys);
+		fdisk_info_new_partition(cxt, i + 1, start, start + length, t);
+	}
+
+	return 0;
+}
+
+static void sgi_set_entire(struct fdisk_context *cxt)
+{
+	size_t n;
+
+	for (n = 10; n < cxt->label->nparts_max; n++) {
+		if (!sgi_get_num_sectors(cxt, n)) {
+			set_partition(cxt, n, 0, sgi_get_lastblock(cxt), SGI_TYPE_ENTIRE_DISK);
+			break;
+		}
+	}
+}
+
+static void sgi_set_volhdr(struct fdisk_context *cxt)
+{
+	size_t n;
+
+	for (n = 8; n < cxt->label->nparts_max; n++) {
+		if (!sgi_get_num_sectors(cxt, n)) {
+			/* Choose same default volume header size as IRIX fx uses. */
+			if (4096 < sgi_get_lastblock(cxt))
+				set_partition(cxt, n, 0, 4096, SGI_TYPE_VOLHDR);
+			break;
+		}
+	}
+}
+
+static int sgi_delete_partition(struct fdisk_context *cxt, size_t partnum)
+{
+	int rc;
+
+	assert(cxt);
+	assert(cxt->label);
+
+	if (partnum > cxt->label->nparts_max)
+		return -EINVAL;
+
+	rc = set_partition(cxt, partnum, 0, 0, 0);
+
+	cxt->label->nparts_cur = count_used_partitions(cxt);
+
+	return rc;
+}
+
+static int sgi_add_partition(struct fdisk_context *cxt,
+			     struct fdisk_partition *pa,
+			     size_t *partno)
+{
+	struct fdisk_sgi_label *sgi;
+	char mesg[256];
+	unsigned int first = 0, last = 0;
+	struct fdisk_ask *ask;
+	int sys = pa && pa->type ? pa->type->code : SGI_TYPE_XFS;
+	int rc;
+	size_t n;
+
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, SGI));
+
+	rc = fdisk_partition_next_partno(pa, cxt, &n);
+	if (rc)
+		return rc;
+	if (n == 10)
+		sys = SGI_TYPE_ENTIRE_DISK;
+	else if (n == 8)
+		sys = 0;
+
+	sgi = self_label(cxt);
+
+	if (sgi_get_num_sectors(cxt, n)) {
+		fdisk_warnx(cxt, _("Partition %zu is already defined.  "
+				   "Delete it before re-adding it."), n + 1);
+		return -EINVAL;
+	}
+	if (!cxt->script && sgi_entire(cxt) == -1 &&  sys != SGI_TYPE_ENTIRE_DISK) {
+		fdisk_info(cxt, _("Attempting to generate entire disk entry automatically."));
+		sgi_set_entire(cxt);
+		sgi_set_volhdr(cxt);
+	}
+	if (sgi_gaps(cxt) == 0 && sys != SGI_TYPE_ENTIRE_DISK) {
+		fdisk_warnx(cxt, _("The entire disk is already covered with partitions."));
+		return -EINVAL;
+	}
+	if (sgi_gaps(cxt) < 0) {
+		fdisk_warnx(cxt, _("You got a partition overlap on the disk. Fix it first!"));
+		return -EINVAL;
+	}
+
+	if (sys == SGI_TYPE_ENTIRE_DISK) {
+		first = 0;
+		last = sgi_get_lastblock(cxt);
+	} else {
+		first = sgi->freelist[0].first;
+		last  = sgi->freelist[0].last;
+	}
+
+	/* first sector */
+	if (pa && pa->start_follow_default)
+		;
+	else if (pa && fdisk_partition_has_start(pa)) {
+		first = pa->start;
+		last = is_in_freelist(cxt, first);
+
+		if (sys != SGI_TYPE_ENTIRE_DISK && !last)
+			return -ERANGE;
+	} else {
+		snprintf(mesg, sizeof(mesg), _("First %s"),
+				fdisk_get_unit(cxt, FDISK_SINGULAR));
+		ask = fdisk_new_ask();
+		if (!ask)
+			return -ENOMEM;
+
+		fdisk_ask_set_query(ask, mesg);
+		fdisk_ask_set_type(ask, FDISK_ASKTYPE_NUMBER);
+
+		fdisk_ask_number_set_low(ask,     fdisk_scround(cxt, first));	/* minimal */
+		fdisk_ask_number_set_default(ask, fdisk_scround(cxt, first));	/* default */
+		fdisk_ask_number_set_high(ask,    fdisk_scround(cxt, last) - 1); /* maximal */
+
+		rc = fdisk_do_ask(cxt, ask);
+		first = fdisk_ask_number_get_result(ask);
+		fdisk_unref_ask(ask);
+
+		if (rc)
+			return rc;
+		if (fdisk_use_cylinders(cxt))
+			first *= fdisk_get_units_per_sector(cxt);
+	}
+
+	if (first && sys == SGI_TYPE_ENTIRE_DISK)
+		fdisk_info(cxt, _("It is highly recommended that the "
+				  "eleventh partition covers the entire "
+				  "disk and is of type 'SGI volume'."));
+	if (!last)
+		last = is_in_freelist(cxt, first);
+
+	/* last sector */
+	if (pa && pa->end_follow_default)
+		last -= 1ULL;
+	else if (pa && fdisk_partition_has_size(pa)) {
+		if (first + pa->size - 1ULL > last)
+			return -ERANGE;
+		last = first + pa->size - 1ULL;
+	} else {
+		snprintf(mesg, sizeof(mesg),
+			 _("Last %s or +%s or +size{K,M,G,T,P}"),
+			 fdisk_get_unit(cxt, FDISK_SINGULAR),
+			 fdisk_get_unit(cxt, FDISK_PLURAL));
+
+		ask = fdisk_new_ask();
+		if (!ask)
+			return -ENOMEM;
+
+		fdisk_ask_set_query(ask, mesg);
+		fdisk_ask_set_type(ask, FDISK_ASKTYPE_OFFSET);
+
+		fdisk_ask_number_set_low(ask,     fdisk_scround(cxt, first));	/* minimal */
+		fdisk_ask_number_set_default(ask, fdisk_scround(cxt, last) - 1);/* default */
+		fdisk_ask_number_set_high(ask,    fdisk_scround(cxt, last) - 1);/* maximal */
+		fdisk_ask_number_set_base(ask,    fdisk_scround(cxt, first));
+
+		if (fdisk_use_cylinders(cxt))
+			fdisk_ask_number_set_unit(ask,
+				     cxt->sector_size *
+				     fdisk_get_units_per_sector(cxt));
+		else
+			fdisk_ask_number_set_unit(ask,cxt->sector_size);
+
+		rc = fdisk_do_ask(cxt, ask);
+		last = fdisk_ask_number_get_result(ask) + 1;
+
+		fdisk_unref_ask(ask);
+		if (rc)
+			return rc;
+		if (fdisk_use_cylinders(cxt))
+			last *= fdisk_get_units_per_sector(cxt);
+	}
+
+	if (sys == SGI_TYPE_ENTIRE_DISK
+	    && (first != 0 || last != sgi_get_lastblock(cxt)))
+		fdisk_info(cxt, _("It is highly recommended that the "
+				  "eleventh partition covers the entire "
+				  "disk and is of type 'SGI volume'."));
+
+	set_partition(cxt, n, first, last - first, sys);
+	cxt->label->nparts_cur = count_used_partitions(cxt);
+	if (partno)
+		*partno = n;
+	return 0;
+}
+
+static int sgi_create_disklabel(struct fdisk_context *cxt)
+{
+	struct fdisk_sgi_label *sgi;
+	struct sgi_disklabel *sgilabel;
+	int rc;
+
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, SGI));
+
+#ifdef HDIO_GETGEO
+	if (cxt->geom.heads && cxt->geom.sectors) {
+		fdisk_sector_t llsectors;
+
+		if (blkdev_get_sectors(cxt->dev_fd, (unsigned long long *) &llsectors) == 0) {
+			/* the get device size ioctl was successful */
+			fdisk_sector_t llcyls;
+			int sec_fac = cxt->sector_size / 512;
+
+			llcyls = llsectors / (cxt->geom.heads * cxt->geom.sectors * sec_fac);
+			cxt->geom.cylinders = llcyls;
+			if (cxt->geom.cylinders != llcyls)	/* truncated? */
+				cxt->geom.cylinders = ~0;
+		} else {
+			/* otherwise print error and use truncated version */
+			fdisk_warnx(cxt,
+				_("BLKGETSIZE ioctl failed on %s. "
+				  "Using geometry cylinder value of %llu. "
+				  "This value may be truncated for devices "
+				  "> 33.8 GB."), cxt->dev_path, cxt->geom.cylinders);
+		}
+	}
+#endif
+	rc = fdisk_init_firstsector_buffer(cxt);
+	if (rc)
+		return rc;
+
+	sgi = (struct fdisk_sgi_label *) cxt->label;
+	sgi->header = (struct sgi_disklabel *) cxt->firstsector;
+
+	sgilabel = sgi->header;
+
+	sgilabel->magic = cpu_to_be32(SGI_LABEL_MAGIC);
+	sgilabel->root_part_num = cpu_to_be16(0);
+	sgilabel->swap_part_num = cpu_to_be16(1);
+
+	/* sizeof(sgilabel->boot_file) = 16 > 6 */
+	memset(sgilabel->boot_file, 0, 16);
+	strcpy((char *) sgilabel->boot_file, "/unix");
+
+	sgilabel->devparam.skew			= (0);
+	sgilabel->devparam.gap1			= (0);
+	sgilabel->devparam.gap2			= (0);
+	sgilabel->devparam.sparecyl			= (0);
+	sgilabel->devparam.pcylcount	= cpu_to_be16(cxt->geom.cylinders);
+	sgilabel->devparam.head_vol0	= cpu_to_be16(0);
+	sgilabel->devparam.ntrks	= cpu_to_be16(cxt->geom.heads);
+	/* tracks/cylinder (heads) */
+	sgilabel->devparam.cmd_tag_queue_depth	= (0);
+	sgilabel->devparam.unused0			= (0);
+	sgilabel->devparam.unused1	= cpu_to_be16(0);
+	sgilabel->devparam.nsect	= cpu_to_be16(cxt->geom.sectors);
+	/* sectors/track */
+	sgilabel->devparam.bytes	= cpu_to_be16(cxt->sector_size);
+	sgilabel->devparam.ilfact	= cpu_to_be16(1);
+	sgilabel->devparam.flags	= cpu_to_be32(
+			SGI_DEVPARAM_TRACK_FWD
+			| SGI_DEVPARAM_IGNORE_ERRORS
+			| SGI_DEVPARAM_RESEEK);
+	sgilabel->devparam.datarate	= cpu_to_be32(0);
+	sgilabel->devparam.retries_on_error	= cpu_to_be32(1);
+	sgilabel->devparam.ms_per_word		= cpu_to_be32(0);
+	sgilabel->devparam.xylogics_gap1	= cpu_to_be16(0);
+	sgilabel->devparam.xylogics_syncdelay	= cpu_to_be16(0);
+	sgilabel->devparam.xylogics_readdelay	= cpu_to_be16(0);
+	sgilabel->devparam.xylogics_gap2	= cpu_to_be16(0);
+	sgilabel->devparam.xylogics_readgate	= cpu_to_be16(0);
+	sgilabel->devparam.xylogics_writecont	= cpu_to_be16(0);
+
+	memset(&(sgilabel->volume), 0,
+			sizeof(struct sgi_volume) * SGI_MAXVOLUMES);
+	memset(&(sgilabel->partitions), 0,
+			sizeof(struct sgi_partition) * SGI_MAXPARTITIONS);
+	cxt->label->nparts_max = SGI_MAXPARTITIONS;
+
+	/* don't create default layout when a script defined */
+	if (!cxt->script) {
+		sgi_set_entire(cxt);
+		sgi_set_volhdr(cxt);
+	}
+	cxt->label->nparts_cur = count_used_partitions(cxt);
+
+	fdisk_info(cxt, _("Created a new SGI disklabel."));
+	return 0;
+}
+
+static int sgi_set_partition(struct fdisk_context *cxt,
+		size_t i,
+		struct fdisk_partition *pa)
+{
+	struct sgi_disklabel *sgilabel;
+
+	if (i >= cxt->label->nparts_max)
+		return -EINVAL;
+
+	sgilabel = self_disklabel(cxt);
+
+	if (pa->type) {
+		struct fdisk_parttype *t = pa->type;
+
+		if (t->code > UINT32_MAX)
+			return -EINVAL;
+
+		if (sgi_get_num_sectors(cxt, i) == 0)	/* caught already before, ... */ {
+			fdisk_warnx(cxt, _("Sorry, only for non-empty partitions you can change the tag."));
+			return -EINVAL;
+		}
+
+		if ((i == 10 && t->code != SGI_TYPE_ENTIRE_DISK)
+		    || (i == 8 && t->code != 0))
+			fdisk_info(cxt, _("Consider leaving partition 9 as volume header (0), "
+					  "and partition 11 as entire volume (6), "
+					  "as IRIX expects it."));
+
+		if (cxt->script == NULL
+		    && ((t->code != SGI_TYPE_ENTIRE_DISK) && (t->code != SGI_TYPE_VOLHDR))
+		    && (sgi_get_start_sector(cxt, i) < 1)) {
+			int yes = 0;
+			fdisk_ask_yesno(cxt,
+				_("It is highly recommended that the partition at offset 0 "
+				  "is of type \"SGI volhdr\", the IRIX system will rely on it to "
+				  "retrieve from its directory standalone tools like sash and fx. "
+				  "Only the \"SGI volume\" entire disk section may violate this. "
+				  "Are you sure about tagging this partition differently?"), &yes);
+			if (!yes)
+				return 1;
+		}
+
+		sgilabel->partitions[i].type = cpu_to_be32(t->code);
+	}
+
+	if (fdisk_partition_has_start(pa))
+		sgilabel->partitions[i].first_block = cpu_to_be32(pa->start);
+	if (fdisk_partition_has_size(pa))
+		sgilabel->partitions[i].num_blocks = cpu_to_be32(pa->size);
+
+	fdisk_label_set_changed(cxt->label, 1);
+	return 0;
+}
+
+
+static int sgi_partition_is_used(
+		struct fdisk_context *cxt,
+		size_t i)
+{
+	assert(cxt);
+	assert(fdisk_is_label(cxt, SGI));
+
+	if (i >= cxt->label->nparts_max)
+		return 0;
+	return sgi_get_num_sectors(cxt, i) ? 1 : 0;
+}
+
+static int sgi_toggle_partition_flag(struct fdisk_context *cxt, size_t i, unsigned long flag)
+{
+	struct sgi_disklabel *sgilabel;
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, SGI));
+
+	if (i >= cxt->label->nparts_max)
+		return -EINVAL;
+
+	sgilabel = self_disklabel(cxt);
+
+	switch (flag) {
+	case SGI_FLAG_BOOT:
+		sgilabel->root_part_num =
+			be16_to_cpu(sgilabel->root_part_num) == i ?
+			0 : cpu_to_be16(i);
+		fdisk_label_set_changed(cxt->label, 1);
+		break;
+	case SGI_FLAG_SWAP:
+		sgilabel->swap_part_num =
+			be16_to_cpu(sgilabel->swap_part_num) == i ?
+			0 : cpu_to_be16(i);
+		fdisk_label_set_changed(cxt->label, 1);
+		break;
+	default:
+		return 1;
+	}
+
+	return 0;
+}
+
+static const struct fdisk_field sgi_fields[] =
+{
+	{ FDISK_FIELD_DEVICE,	N_("Device"),	 10,	0 },
+	{ FDISK_FIELD_START,	N_("Start"),	  5,	FDISK_FIELDFL_NUMBER },
+	{ FDISK_FIELD_END,	N_("End"),	  5,	FDISK_FIELDFL_NUMBER },
+	{ FDISK_FIELD_SECTORS,	N_("Sectors"),	  5,	FDISK_FIELDFL_NUMBER },
+	{ FDISK_FIELD_CYLINDERS,N_("Cylinders"),  5,	FDISK_FIELDFL_NUMBER },
+	{ FDISK_FIELD_SIZE,	N_("Size"),	  5,	FDISK_FIELDFL_NUMBER | FDISK_FIELDFL_EYECANDY },
+	{ FDISK_FIELD_TYPEID,	N_("Id"),	  2,	FDISK_FIELDFL_NUMBER },
+	{ FDISK_FIELD_TYPE,	N_("Type"),	0.1,	FDISK_FIELDFL_EYECANDY },
+	{ FDISK_FIELD_ATTR,	N_("Attrs"),	  0,	FDISK_FIELDFL_NUMBER }
+};
+
+static const struct fdisk_label_operations sgi_operations =
+{
+	.probe		= sgi_probe_label,
+	.write		= sgi_write_disklabel,
+	.verify		= sgi_verify_disklabel,
+	.create		= sgi_create_disklabel,
+	.list		= sgi_list_table,
+
+	.get_part	= sgi_get_partition,
+	.set_part	= sgi_set_partition,
+	.add_part	= sgi_add_partition,
+	.del_part	= sgi_delete_partition,
+
+	.part_is_used	= sgi_partition_is_used,
+	.part_toggle_flag = sgi_toggle_partition_flag
+};
+
+/* Allocates an SGI label driver. */
+struct fdisk_label *fdisk_new_sgi_label(struct fdisk_context *cxt)
+{
+	struct fdisk_label *lb;
+	struct fdisk_sgi_label *sgi;
+
+	assert(cxt);
+
+	sgi = calloc(1, sizeof(*sgi));
+	if (!sgi)
+		return NULL;
+
+	/* initialize generic part of the driver */
+	lb = (struct fdisk_label *) sgi;
+	lb->name = "sgi";
+	lb->id = FDISK_DISKLABEL_SGI;
+	lb->op = &sgi_operations;
+	lb->parttypes = sgi_parttypes;
+	lb->nparttypes = ARRAY_SIZE(sgi_parttypes) - 1;
+	lb->fields = sgi_fields;
+	lb->nfields = ARRAY_SIZE(sgi_fields);
+
+	lb->flags |= FDISK_LABEL_FL_REQUIRE_GEOMETRY;
+
+	return lb;
+}
diff --git a/libblkid/libfdisk/src/sun.c b/libblkid/libfdisk/src/sun.c
new file mode 100644
index 0000000..babff62
--- /dev/null
+++ b/libblkid/libfdisk/src/sun.c
@@ -0,0 +1,1130 @@
+/*
+ * Copyright (C) 2013 Karel Zak <kzak@redhat.com>
+ *
+ * Based on original code from fdisk:
+ *   Jakub Jelinek (jj@sunsite.mff.cuni.cz), July 1996
+ *   Merged with fdisk for other architectures, aeb, June 1998.
+ *   Arnaldo Carvalho de Melo <acme@conectiva.com.br> Mar 1999, Internationalization
+ */
+#include <stdio.h>		/* stderr */
+#include <stdlib.h>		/* qsort */
+#include <string.h>		/* strstr */
+#include <unistd.h>		/* write */
+#include <sys/ioctl.h>		/* ioctl */
+
+#include "nls.h"
+#include "blkdev.h"
+#include "bitops.h"
+
+#include "fdiskP.h"
+#include "pt-sun.h"
+#include "all-io.h"
+
+
+/**
+ * SECTION: sun
+ * @title: SUN
+ * @short_description: disk label specific functions
+ *
+ */
+
+/*
+ * in-memory fdisk SUN stuff
+ */
+struct fdisk_sun_label {
+	struct fdisk_label	head;		/* generic part */
+	struct sun_disklabel   *header;		/* on-disk data (pointer to cxt->firstsector) */
+};
+
+static struct fdisk_parttype sun_parttypes[] = {
+	{SUN_TAG_UNASSIGNED, N_("Unassigned")},
+	{SUN_TAG_BOOT, N_("Boot")},
+	{SUN_TAG_ROOT, N_("SunOS root")},
+	{SUN_TAG_SWAP, N_("SunOS swap")},
+	{SUN_TAG_USR, N_("SunOS usr")},
+	{SUN_TAG_WHOLEDISK, N_("Whole disk")},
+	{SUN_TAG_STAND, N_("SunOS stand")},
+	{SUN_TAG_VAR, N_("SunOS var")},
+	{SUN_TAG_HOME, N_("SunOS home")},
+	{SUN_TAG_ALTSCTR, N_("SunOS alt sectors")},
+	{SUN_TAG_CACHE, N_("SunOS cachefs")},
+	{SUN_TAG_RESERVED, N_("SunOS reserved")},
+	{SUN_TAG_LINUX_SWAP, N_("Linux swap")},
+	{SUN_TAG_LINUX_NATIVE, N_("Linux native")},
+	{SUN_TAG_LINUX_LVM, N_("Linux LVM")},
+	{SUN_TAG_LINUX_RAID, N_("Linux raid autodetect")},
+	{ 0, NULL }
+};
+
+/* return poiter buffer with on-disk data */
+static inline struct sun_disklabel *self_disklabel(struct fdisk_context *cxt)
+{
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, SUN));
+
+	return ((struct fdisk_sun_label *) cxt->label)->header;
+}
+
+/* return in-memory sun fdisk data */
+static inline struct fdisk_sun_label *self_label(struct fdisk_context *cxt)
+{
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, SUN));
+
+	return (struct fdisk_sun_label *) cxt->label;
+}
+
+static void set_partition(struct fdisk_context *cxt, size_t i,
+		uint32_t start,uint32_t stop, uint16_t sysid)
+{
+	struct sun_disklabel *sunlabel = self_disklabel(cxt);
+	struct fdisk_parttype *t =
+			fdisk_label_get_parttype_from_code(cxt->label, sysid);
+
+	sunlabel->vtoc.infos[i].id = cpu_to_be16(sysid);
+	sunlabel->vtoc.infos[i].flags = cpu_to_be16(0);
+	sunlabel->partitions[i].start_cylinder =
+		cpu_to_be32(start / (cxt->geom.heads * cxt->geom.sectors));
+	sunlabel->partitions[i].num_sectors = cpu_to_be32(stop - start);
+	fdisk_label_set_changed(cxt->label, 1);
+
+	fdisk_info_new_partition(cxt, i + 1, start, stop, t);
+}
+
+static size_t count_used_partitions(struct fdisk_context *cxt)
+{
+	struct sun_disklabel *sunlabel = self_disklabel(cxt);
+	size_t ct = 0, i;
+
+	assert(sunlabel);
+
+	for (i = 0; i < cxt->label->nparts_max; i++) {
+		if (sunlabel->partitions[i].num_sectors)
+			ct++;
+	}
+	return ct;
+}
+
+static int sun_probe_label(struct fdisk_context *cxt)
+{
+	struct fdisk_sun_label *sun;
+	struct sun_disklabel *sunlabel;
+	unsigned short *ush;
+	int csum;
+	int need_fixing = 0;
+
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, SUN));
+
+	/* map first sector to header */
+	sun = (struct fdisk_sun_label *) cxt->label;
+	sun->header = (struct sun_disklabel *) cxt->firstsector;
+	sunlabel = sun->header;
+
+	if (be16_to_cpu(sunlabel->magic) != SUN_LABEL_MAGIC) {
+		sun->header = NULL;
+		return 0;		/* failed */
+	}
+
+	ush = ((unsigned short *) (sunlabel + 1)) - 1;
+	for (csum = 0; ush >= (unsigned short *)sunlabel;)
+		csum ^= *ush--;
+
+	if (csum) {
+		fdisk_warnx(cxt, _("Detected sun disklabel with wrong checksum. "
+			      "Probably you'll have to set all the values, "
+			      "e.g. heads, sectors, cylinders and partitions "
+			      "or force a fresh label (s command in main menu)"));
+		return 1;
+	}
+
+	cxt->label->nparts_max = SUN_MAXPARTITIONS;
+	cxt->geom.heads = be16_to_cpu(sunlabel->nhead);
+	cxt->geom.cylinders = be16_to_cpu(sunlabel->ncyl);
+	cxt->geom.sectors = be16_to_cpu(sunlabel->nsect);
+
+	if (be32_to_cpu(sunlabel->vtoc.version) != SUN_VTOC_VERSION) {
+		fdisk_warnx(cxt, _("Detected sun disklabel with wrong version [%d]."),
+			be32_to_cpu(sunlabel->vtoc.version));
+		need_fixing = 1;
+	}
+	if (be32_to_cpu(sunlabel->vtoc.sanity) != SUN_VTOC_SANITY) {
+		fdisk_warnx(cxt, _("Detected sun disklabel with wrong vtoc.sanity [0x%08x]."),
+			be32_to_cpu(sunlabel->vtoc.sanity));
+		need_fixing = 1;
+	}
+	if (be16_to_cpu(sunlabel->vtoc.nparts) != SUN_MAXPARTITIONS) {
+		fdisk_warnx(cxt, _("Detected sun disklabel with wrong vtoc.nparts [%u]."),
+			be16_to_cpu(sunlabel->vtoc.nparts));
+		need_fixing = 1;
+	}
+	if (need_fixing) {
+		fdisk_warnx(cxt, _("Warning: Wrong values need to be fixed up and "
+			           "will be corrected by w(rite)"));
+
+		sunlabel->vtoc.version = cpu_to_be32(SUN_VTOC_VERSION);
+		sunlabel->vtoc.sanity = cpu_to_be32(SUN_VTOC_SANITY);
+		sunlabel->vtoc.nparts = cpu_to_be16(SUN_MAXPARTITIONS);
+
+		ush = (unsigned short *)sunlabel;
+		csum = 0;
+		while(ush < (unsigned short *)(&sunlabel->csum))
+			csum ^= *ush++;
+		sunlabel->csum = csum;
+
+		fdisk_label_set_changed(cxt->label, 1);
+	}
+
+	cxt->label->nparts_cur = count_used_partitions(cxt);
+
+	return 1;
+}
+
+static void ask_geom(struct fdisk_context *cxt)
+{
+	uintmax_t res;
+
+	assert(cxt);
+
+	if (fdisk_ask_number(cxt, 1, 1, 1024, _("Heads"), &res) == 0)
+		cxt->geom.heads = res;
+	if (fdisk_ask_number(cxt, 1, 1, 1024, _("Sectors/track"), &res) == 0)
+		cxt->geom.sectors = res;
+	if (fdisk_ask_number(cxt, 1, 1, USHRT_MAX, _("Cylinders"), &res) == 0)
+		cxt->geom.cylinders = res;
+}
+
+static int sun_create_disklabel(struct fdisk_context *cxt)
+{
+	unsigned int ndiv;
+	struct fdisk_sun_label *sun;		/* libfdisk sun handler */
+	struct sun_disklabel *sunlabel;	/* on disk data */
+	int rc = 0;
+
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, SUN));
+
+	/* map first sector to header */
+	rc = fdisk_init_firstsector_buffer(cxt);
+	if (rc)
+		return rc;
+
+	sun = (struct fdisk_sun_label *) cxt->label;
+	sun->header = (struct sun_disklabel *) cxt->firstsector;
+
+	sunlabel = sun->header;
+
+	cxt->label->nparts_max = SUN_MAXPARTITIONS;
+
+	sunlabel->magic = cpu_to_be16(SUN_LABEL_MAGIC);
+	sunlabel->vtoc.version = cpu_to_be32(SUN_VTOC_VERSION);
+	sunlabel->vtoc.sanity = cpu_to_be32(SUN_VTOC_SANITY);
+	sunlabel->vtoc.nparts = cpu_to_be16(SUN_MAXPARTITIONS);
+
+#ifdef HDIO_GETGEO
+	if (cxt->geom.heads && cxt->geom.sectors) {
+		fdisk_sector_t llsectors;
+
+		if (blkdev_get_sectors(cxt->dev_fd, (unsigned long long *) &llsectors) == 0) {
+			int sec_fac = cxt->sector_size / 512;
+			fdisk_sector_t llcyls;
+
+			llcyls = llsectors / (cxt->geom.heads * cxt->geom.sectors * sec_fac);
+			cxt->geom.cylinders = llcyls;
+			if (cxt->geom.cylinders != llcyls)
+				cxt->geom.cylinders = ~0;
+		} else {
+			fdisk_warnx(cxt,
+				_("BLKGETSIZE ioctl failed on %s. "
+				  "Using geometry cylinder value of %llu. "
+				  "This value may be truncated for devices "
+				  "> 33.8 GB."),
+				cxt->dev_path, cxt->geom.cylinders);
+		}
+	} else
+#endif
+		ask_geom(cxt);
+
+	sunlabel->acyl   = cpu_to_be16(0);
+	sunlabel->pcyl   = cpu_to_be16(cxt->geom.cylinders);
+	sunlabel->rpm    = cpu_to_be16(5400);
+	sunlabel->intrlv = cpu_to_be16(1);
+	sunlabel->apc    = cpu_to_be16(0);
+
+	sunlabel->nhead  = cpu_to_be16(cxt->geom.heads);
+	sunlabel->nsect  = cpu_to_be16(cxt->geom.sectors);
+	sunlabel->ncyl   = cpu_to_be16(cxt->geom.cylinders);
+
+	snprintf((char *) sunlabel->label_id, sizeof(sunlabel->label_id),
+		 "Linux cyl %ju alt %u hd %u sec %ju",
+		 (uintmax_t) cxt->geom.cylinders,
+		 be16_to_cpu(sunlabel->acyl),
+		 cxt->geom.heads,
+		 (uintmax_t) cxt->geom.sectors);
+
+	if (cxt->geom.cylinders * cxt->geom.heads * cxt->geom.sectors >= 150 * 2048) {
+	        ndiv = cxt->geom.cylinders - (50 * 2048 / (cxt->geom.heads * cxt->geom.sectors)); /* 50M swap */
+	} else
+	        ndiv = cxt->geom.cylinders * 2 / 3;
+
+	/* create the default layout only if no-script defined */
+	if (!cxt->script) {
+		set_partition(cxt, 0, 0, ndiv * cxt->geom.heads * cxt->geom.sectors,
+			  SUN_TAG_LINUX_NATIVE);
+		set_partition(cxt, 1, ndiv * cxt->geom.heads * cxt->geom.sectors,
+			  cxt->geom.cylinders * cxt->geom.heads * cxt->geom.sectors,
+			  SUN_TAG_LINUX_SWAP);
+		sunlabel->vtoc.infos[1].flags |= cpu_to_be16(SUN_FLAG_UNMNT);
+
+		set_partition(cxt, 2, 0,
+			  cxt->geom.cylinders * cxt->geom.heads * cxt->geom.sectors,
+			  SUN_TAG_WHOLEDISK);
+	}
+
+	{
+		unsigned short *ush = (unsigned short *)sunlabel;
+		unsigned short csum = 0;
+		while(ush < (unsigned short *)(&sunlabel->csum))
+			csum ^= *ush++;
+		sunlabel->csum = csum;
+	}
+
+	fdisk_label_set_changed(cxt->label, 1);
+	cxt->label->nparts_cur = count_used_partitions(cxt);
+
+	fdisk_info(cxt, _("Created a new Sun disklabel."));
+	return 0;
+}
+
+static int sun_toggle_partition_flag(struct fdisk_context *cxt, size_t i, unsigned long flag)
+{
+	struct sun_disklabel *sunlabel;
+	struct sun_info *p;
+
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, SUN));
+
+	if (i >= cxt->label->nparts_max)
+		return -EINVAL;
+
+	sunlabel = self_disklabel(cxt);
+	p = &sunlabel->vtoc.infos[i];
+
+	switch (flag) {
+	case SUN_FLAG_UNMNT:
+		p->flags ^= cpu_to_be16(SUN_FLAG_UNMNT);
+		fdisk_label_set_changed(cxt->label, 1);
+		break;
+	case SUN_FLAG_RONLY:
+		p->flags ^= cpu_to_be16(SUN_FLAG_RONLY);
+		fdisk_label_set_changed(cxt->label, 1);
+		break;
+	default:
+		return 1;
+	}
+
+	return 0;
+}
+
+static void fetch_sun(struct fdisk_context *cxt,
+		      uint32_t *starts,
+		      uint32_t *lens,
+		      uint32_t *start,
+		      uint32_t *stop)
+{
+	struct sun_disklabel *sunlabel;
+	int continuous = 1;
+	size_t i;
+
+	assert(cxt);
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, SUN));
+
+	sunlabel = self_disklabel(cxt);
+
+	*start = 0;
+	*stop = cxt->geom.cylinders * cxt->geom.heads * cxt->geom.sectors;
+
+	for (i = 0; i < cxt->label->nparts_max; i++) {
+		struct sun_partition *part = &sunlabel->partitions[i];
+		struct sun_info *info = &sunlabel->vtoc.infos[i];
+
+		if (part->num_sectors &&
+		    be16_to_cpu(info->id) != SUN_TAG_UNASSIGNED &&
+		    be16_to_cpu(info->id) != SUN_TAG_WHOLEDISK) {
+			starts[i] = be32_to_cpu(part->start_cylinder) *
+				     cxt->geom.heads * cxt->geom.sectors;
+			lens[i] = be32_to_cpu(part->num_sectors);
+			if (continuous) {
+				if (starts[i] == *start)
+					*start += lens[i];
+				else if (starts[i] + lens[i] >= *stop)
+					*stop = starts[i];
+				else
+					continuous = 0;
+				        /* There will be probably more gaps
+					  than one, so lets check afterwards */
+			}
+		} else {
+			starts[i] = 0;
+			lens[i] = 0;
+		}
+	}
+}
+
+#ifdef HAVE_QSORT_R
+static int verify_sun_cmp(int *a, int *b, void *data)
+{
+    unsigned int *verify_sun_starts = (unsigned int *) data;
+
+    if (*a == -1)
+	    return 1;
+    if (*b == -1)
+	    return -1;
+    if (verify_sun_starts[*a] > verify_sun_starts[*b])
+	    return 1;
+    return -1;
+}
+#endif
+
+static int sun_verify_disklabel(struct fdisk_context *cxt)
+{
+    uint32_t starts[SUN_MAXPARTITIONS], lens[SUN_MAXPARTITIONS], start, stop;
+    uint32_t i,j,k,starto,endo;
+#ifdef HAVE_QSORT_R
+    int array[SUN_MAXPARTITIONS];
+    unsigned int *verify_sun_starts;
+#endif
+    assert(cxt);
+    assert(cxt->label);
+    assert(fdisk_is_label(cxt, SUN));
+
+    fetch_sun(cxt, starts, lens, &start, &stop);
+
+    for (k = 0; k < 7; k++) {
+	for (i = 0; i < SUN_MAXPARTITIONS; i++) {
+	    if (k && (lens[i] % (cxt->geom.heads * cxt->geom.sectors)))
+	        fdisk_warnx(cxt, _("Partition %u doesn't end on cylinder boundary."), i+1);
+	    if (lens[i]) {
+	        for (j = 0; j < i; j++)
+	            if (lens[j]) {
+	                if (starts[j] == starts[i]+lens[i]) {
+	                    starts[j] = starts[i]; lens[j] += lens[i];
+	                    lens[i] = 0;
+	                } else if (starts[i] == starts[j]+lens[j]){
+	                    lens[j] += lens[i];
+	                    lens[i] = 0;
+	                } else if (!k) {
+	                    if (starts[i] < starts[j]+lens[j] &&
+				starts[j] < starts[i]+lens[i]) {
+	                        starto = starts[i];
+	                        if (starts[j] > starto)
+					starto = starts[j];
+	                        endo = starts[i]+lens[i];
+	                        if (starts[j]+lens[j] < endo)
+					endo = starts[j]+lens[j];
+	                        fdisk_warnx(cxt, _("Partition %u overlaps with others in "
+				       "sectors %u-%u."), i+1, starto, endo);
+	                    }
+	                }
+	            }
+	    }
+	}
+    }
+
+#ifdef HAVE_QSORT_R
+    for (i = 0; i < SUN_MAXPARTITIONS; i++) {
+        if (lens[i])
+            array[i] = i;
+        else
+            array[i] = -1;
+    }
+    verify_sun_starts = starts;
+
+    qsort_r(array,ARRAY_SIZE(array),sizeof(array[0]),
+	  (int (*)(const void *,const void *,void *)) verify_sun_cmp,
+	  verify_sun_starts);
+
+    if (array[0] == -1) {
+	fdisk_info(cxt, _("No partitions defined."));
+	return 0;
+    }
+    stop = cxt->geom.cylinders * cxt->geom.heads * cxt->geom.sectors;
+    if (starts[array[0]])
+        fdisk_warnx(cxt, _("Unused gap - sectors 0-%u."), starts[array[0]]);
+    for (i = 0; i < 7 && array[i+1] != -1; i++) {
+        fdisk_warnx(cxt, _("Unused gap - sectors %u-%u."),
+	       (starts[array[i]] + lens[array[i]]),
+	       starts[array[i+1]]);
+    }
+    start = (starts[array[i]] + lens[array[i]]);
+    if (start < stop)
+        fdisk_warnx(cxt, _("Unused gap - sectors %u-%u."), start, stop);
+#endif
+    return 0;
+}
+
+
+static int is_free_sector(struct fdisk_context *cxt,
+		fdisk_sector_t s, uint32_t starts[], uint32_t lens[])
+{
+	size_t i;
+
+	for (i = 0; i < cxt->label->nparts_max; i++) {
+		if (lens[i] && starts[i] <= s
+		    && starts[i] + lens[i] > s)
+			return 0;
+	}
+	return 1;
+}
+
+static int sun_add_partition(
+		struct fdisk_context *cxt,
+		struct fdisk_partition *pa,
+		size_t *partno)
+{
+	struct sun_disklabel *sunlabel = self_disklabel(cxt);
+	uint32_t starts[SUN_MAXPARTITIONS], lens[SUN_MAXPARTITIONS];
+	struct sun_partition *part;
+	struct sun_info *info;
+	uint32_t start, stop, stop2;
+	int whole_disk = 0;
+	int sys = pa && pa->type ? pa->type->code : SUN_TAG_LINUX_NATIVE;
+	int rc;
+	size_t n;
+
+	char mesg[256];
+	size_t i;
+	unsigned int first, last;
+
+	rc = fdisk_partition_next_partno(pa, cxt, &n);
+	if (rc)
+		return rc;
+
+	part = &sunlabel->partitions[n];
+	info = &sunlabel->vtoc.infos[n];
+
+	if (part->num_sectors && be16_to_cpu(info->id) != SUN_TAG_UNASSIGNED) {
+		fdisk_info(cxt, _("Partition %zu is already defined.  Delete "
+			"it before re-adding it."), n + 1);
+		return -EINVAL;
+	}
+
+	fetch_sun(cxt, starts, lens, &start, &stop);
+
+	if (stop <= start) {
+		if (n == 2)
+			whole_disk = 1;
+		else {
+			fdisk_info(cxt, _("Other partitions already cover the "
+				"whole disk. Delete some/shrink them before retry."));
+			return -EINVAL;
+		}
+	}
+
+	if (pa && pa->start_follow_default)
+		first = start;
+	else if (pa && fdisk_partition_has_start(pa)) {
+		first = pa->start;
+
+		if (!whole_disk && !is_free_sector(cxt, first, starts, lens))
+			return -ERANGE;
+	} else {
+		struct fdisk_ask *ask;
+
+		snprintf(mesg, sizeof(mesg), _("First %s"),
+				fdisk_get_unit(cxt, FDISK_SINGULAR));
+		for (;;) {
+			ask = fdisk_new_ask();
+			if (!ask)
+				return -ENOMEM;
+
+			fdisk_ask_set_query(ask, mesg);
+			fdisk_ask_set_type(ask, FDISK_ASKTYPE_NUMBER);
+
+			if (whole_disk) {
+				fdisk_ask_number_set_low(ask,     0);	/* minimal */
+				fdisk_ask_number_set_default(ask, 0);	/* default */
+				fdisk_ask_number_set_high(ask,    0);	/* maximal */
+			} else {
+				fdisk_ask_number_set_low(ask,     fdisk_scround(cxt, start));	/* minimal */
+				fdisk_ask_number_set_default(ask, fdisk_scround(cxt, start));	/* default */
+				fdisk_ask_number_set_high(ask,    fdisk_scround(cxt, stop));	/* maximal */
+			}
+			rc = fdisk_do_ask(cxt, ask);
+			first = fdisk_ask_number_get_result(ask);
+			fdisk_unref_ask(ask);
+			if (rc)
+				return rc;
+
+			if (fdisk_use_cylinders(cxt))
+				first *= fdisk_get_units_per_sector(cxt);
+
+			/* ewt asks to add: "don't start a partition at cyl 0"
+			   However, edmundo@rano.demon.co.uk writes:
+			   "In addition to having a Sun partition table, to be able to
+			   boot from the disc, the first partition, /dev/sdX1, must
+			   start at cylinder 0. This means that /dev/sdX1 contains
+			   the partition table and the boot block, as these are the
+			   first two sectors of the disc. Therefore you must be
+			   careful what you use /dev/sdX1 for. In particular, you must
+			   not use a partition starting at cylinder 0 for Linux swap,
+			   as that would overwrite the partition table and the boot
+			   block. You may, however, use such a partition for a UFS
+			   or EXT2 file system, as these file systems leave the first
+			   1024 bytes undisturbed. */
+			/* On the other hand, one should not use partitions
+			   starting at block 0 in an md, or the label will
+			   be trashed. */
+			if (!is_free_sector(cxt, first, starts,  lens) && !whole_disk) {
+				if (n == 2 && !first) {
+				    whole_disk = 1;
+				    break;
+				}
+				fdisk_warnx(cxt, _("Sector %d is already allocated"), first);
+			} else
+				break;
+		}
+	}
+
+	if (n == 2 && first != 0)
+		fdisk_warnx(cxt, _("It is highly recommended that the "
+				   "third partition covers the whole disk "
+				   "and is of type `Whole disk'"));
+
+	if (!fdisk_use_cylinders(cxt)) {
+		/* Starting sector has to be properly aligned */
+		int cs = cxt->geom.heads * cxt->geom.sectors;
+		int x = first % cs;
+
+		if (x) {
+			fdisk_info(cxt, _("Aligning the first sector from %u to %u "
+					  "to be on cylinder boundary."),
+					first, first + cs - x);
+			first += cs - x;
+		}
+	}
+
+	stop = cxt->geom.cylinders * cxt->geom.heads * cxt->geom.sectors;	/* ancient */
+	stop2 = stop;
+	for (i = 0; i < cxt->label->nparts_max; i++) {
+		if (starts[i] > first && starts[i] < stop)
+			stop = starts[i];
+	}
+
+	/* last */
+	if (pa && pa->end_follow_default)
+		last = whole_disk || (n == 2 && !first) ? stop2 : stop;
+	else if (pa && fdisk_partition_has_size(pa)) {
+		last = first + pa->size - 1ULL;
+
+		if (!whole_disk && last > stop)
+			return -ERANGE;
+	} else {
+		struct fdisk_ask *ask = fdisk_new_ask();
+
+		if (!ask)
+			return -ENOMEM;
+
+		snprintf(mesg, sizeof(mesg),
+			 _("Last %s or +%s or +size{K,M,G,T,P}"),
+			 fdisk_get_unit(cxt, FDISK_SINGULAR),
+			 fdisk_get_unit(cxt, FDISK_PLURAL));
+		fdisk_ask_set_query(ask, mesg);
+		fdisk_ask_set_type(ask, FDISK_ASKTYPE_OFFSET);
+
+		if (whole_disk) {
+			fdisk_ask_number_set_low(ask,     fdisk_scround(cxt, stop2));	/* minimal */
+			fdisk_ask_number_set_default(ask, fdisk_scround(cxt, stop2));	/* default */
+			fdisk_ask_number_set_high(ask,    fdisk_scround(cxt, stop2));	/* maximal */
+			fdisk_ask_number_set_base(ask,    0);
+		} else if (n == 2 && !first) {
+			fdisk_ask_number_set_low(ask,     fdisk_scround(cxt, first));	/* minimal */
+			fdisk_ask_number_set_default(ask, fdisk_scround(cxt, stop2));	/* default */
+			fdisk_ask_number_set_high(ask,    fdisk_scround(cxt, stop2));	/* maximal */
+			fdisk_ask_number_set_base(ask,	  fdisk_scround(cxt, first));
+		} else {
+			fdisk_ask_number_set_low(ask,     fdisk_scround(cxt, first));	/* minimal */
+			fdisk_ask_number_set_default(ask, fdisk_scround(cxt, stop));	/* default */
+			fdisk_ask_number_set_high(ask,    fdisk_scround(cxt, stop));	/* maximal */
+			fdisk_ask_number_set_base(ask,    fdisk_scround(cxt, first));
+		}
+
+		if (fdisk_use_cylinders(cxt))
+			fdisk_ask_number_set_unit(ask,
+				     cxt->sector_size *
+				     fdisk_get_units_per_sector(cxt));
+		else
+			fdisk_ask_number_set_unit(ask,	cxt->sector_size);
+
+		rc = fdisk_do_ask(cxt, ask);
+		last = fdisk_ask_number_get_result(ask);
+
+		fdisk_unref_ask(ask);
+		if (rc)
+			return rc;
+		if (fdisk_use_cylinders(cxt))
+			last *= fdisk_get_units_per_sector(cxt);
+	}
+
+	if (n == 2 && !first) {
+		if (last >= stop2) {
+		    whole_disk = 1;
+		    last = stop2;
+		} else if (last > stop) {
+		    fdisk_warnx(cxt,
+   _("You haven't covered the whole disk with the 3rd partition, but your value\n"
+     "%lu %s covers some other partition. Your entry has been changed\n"
+     "to %lu %s"),
+			(unsigned long) fdisk_scround(cxt, last), fdisk_get_unit(cxt, FDISK_SINGULAR),
+			(unsigned long) fdisk_scround(cxt, stop), fdisk_get_unit(cxt, FDISK_SINGULAR));
+		    last = stop;
+		}
+	} else if (!whole_disk && last > stop)
+		last = stop;
+
+	if (whole_disk)
+		sys = SUN_TAG_WHOLEDISK;
+
+	set_partition(cxt, n, first, last, sys);
+	cxt->label->nparts_cur = count_used_partitions(cxt);
+	if (partno)
+		*partno = n;
+	return 0;
+}
+
+static int sun_delete_partition(struct fdisk_context *cxt,
+		size_t partnum)
+{
+	struct sun_disklabel *sunlabel;
+	struct sun_partition *part;
+	struct sun_info *info;
+	unsigned int nsec;
+
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, SUN));
+
+	sunlabel = self_disklabel(cxt);
+	part = &sunlabel->partitions[partnum];
+	info = &sunlabel->vtoc.infos[partnum];
+
+	if (partnum == 2 &&
+	    be16_to_cpu(info->id) == SUN_TAG_WHOLEDISK &&
+	    !part->start_cylinder &&
+	    (nsec = be32_to_cpu(part->num_sectors))
+	    == cxt->geom.heads * cxt->geom.sectors * cxt->geom.cylinders)
+		fdisk_info(cxt, _("If you want to maintain SunOS/Solaris compatibility, "
+			 "consider leaving this "
+			 "partition as Whole disk (5), starting at 0, with %u "
+			 "sectors"), nsec);
+	info->id = cpu_to_be16(SUN_TAG_UNASSIGNED);
+	part->num_sectors = 0;
+	cxt->label->nparts_cur = count_used_partitions(cxt);
+	fdisk_label_set_changed(cxt->label, 1);
+	return 0;
+}
+
+
+static int sun_list_disklabel(struct fdisk_context *cxt)
+{
+	struct sun_disklabel *sunlabel;
+
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, SUN));
+
+	sunlabel = self_disklabel(cxt);
+
+	if (fdisk_is_details(cxt)) {
+		fdisk_info(cxt,
+		_("Label geometry: %d rpm, %d alternate and %d physical cylinders,\n"
+		  "                %d extra sects/cyl, interleave %d:1"),
+		       be16_to_cpu(sunlabel->rpm),
+		       be16_to_cpu(sunlabel->acyl),
+		       be16_to_cpu(sunlabel->pcyl),
+		       be16_to_cpu(sunlabel->apc),
+		       be16_to_cpu(sunlabel->intrlv));
+		fdisk_info(cxt, _("Label ID: %s"), sunlabel->label_id);
+		fdisk_info(cxt, _("Volume ID: %s"),
+		       *sunlabel->vtoc.volume_id ? sunlabel->vtoc.volume_id : _("<none>"));
+	}
+
+	return 0;
+}
+
+static struct fdisk_parttype *sun_get_parttype(
+		struct fdisk_context *cxt,
+		size_t n)
+{
+	struct sun_disklabel *sunlabel = self_disklabel(cxt);
+	struct fdisk_parttype *t;
+
+	if (n >= cxt->label->nparts_max)
+		return NULL;
+
+	t = fdisk_label_get_parttype_from_code(cxt->label,
+			be16_to_cpu(sunlabel->vtoc.infos[n].id));
+	return t ? : fdisk_new_unknown_parttype(be16_to_cpu(sunlabel->vtoc.infos[n].id), NULL);
+}
+
+
+static int sun_get_partition(struct fdisk_context *cxt, size_t n,
+			      struct fdisk_partition *pa)
+{
+	struct sun_disklabel *sunlabel;
+	struct sun_partition *part;
+	uint16_t flags;
+	uint32_t start, len;
+
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, SUN));
+
+	if (n >= cxt->label->nparts_max)
+		return -EINVAL;
+
+	sunlabel = self_disklabel(cxt);
+	part = &sunlabel->partitions[n];
+
+	pa->used = part->num_sectors ? 1 : 0;
+	if (!pa->used)
+		return 0;
+
+	flags = be16_to_cpu(sunlabel->vtoc.infos[n].flags);
+	start = be32_to_cpu(part->start_cylinder)
+			* cxt->geom.heads * cxt->geom.sectors;
+	len = be32_to_cpu(part->num_sectors);
+
+	pa->type = sun_get_parttype(cxt, n);
+	if (pa->type && pa->type->code == SUN_TAG_WHOLEDISK)
+		pa->wholedisk = 1;
+
+	if (flags & SUN_FLAG_UNMNT || flags & SUN_FLAG_RONLY) {
+		if (asprintf(&pa->attrs, "%c%c",
+				flags & SUN_FLAG_UNMNT ? 'u' : ' ',
+				flags & SUN_FLAG_RONLY ? 'r' : ' ') < 0)
+			return -ENOMEM;
+	}
+
+	pa->start = start;
+	pa->size = len;
+
+	return 0;
+}
+
+/**
+ * fdisk_sun_set_alt_cyl:
+ * @cxt: context
+ *
+ * Sets number of alternative cylinders. This function uses libfdisk Ask API
+ * for dialog with user.
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int fdisk_sun_set_alt_cyl(struct fdisk_context *cxt)
+{
+	struct sun_disklabel *sunlabel = self_disklabel(cxt);
+	uintmax_t res;
+	int rc = fdisk_ask_number(cxt, 0,			/* low */
+			be16_to_cpu(sunlabel->acyl),		/* default */
+			65535,					/* high */
+			_("Number of alternate cylinders"),	/* query */
+			&res);					/* result */
+	if (rc)
+		return rc;
+
+	sunlabel->acyl = cpu_to_be16(res);
+	return 0;
+}
+
+/**
+ * fdisk_sun_set_xcyl:
+ * @cxt: context
+ *
+ * Sets number of extra sectors per cylinder. This function uses libfdisk Ask API
+ * for dialog with user.
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int fdisk_sun_set_xcyl(struct fdisk_context *cxt)
+{
+	struct sun_disklabel *sunlabel = self_disklabel(cxt);
+	uintmax_t res;
+	int rc = fdisk_ask_number(cxt, 0,			/* low */
+			be16_to_cpu(sunlabel->apc),		/* default */
+			cxt->geom.sectors,			/* high */
+			_("Extra sectors per cylinder"),	/* query */
+			&res);					/* result */
+	if (rc)
+		return rc;
+	sunlabel->apc = cpu_to_be16(res);
+	return 0;
+}
+
+/**
+ * fdisk_sun_set_ilfact:
+ * @cxt: context
+ *
+ * Sets interleave factor. This function uses libfdisk Ask API for dialog with
+ * user.
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int fdisk_sun_set_ilfact(struct fdisk_context *cxt)
+{
+	struct sun_disklabel *sunlabel = self_disklabel(cxt);
+	uintmax_t res;
+	int rc = fdisk_ask_number(cxt, 1,			/* low */
+			be16_to_cpu(sunlabel->intrlv),		/* default */
+			32,					/* high */
+			_("Interleave factor"),	/* query */
+			&res);					/* result */
+	if (rc)
+		return rc;
+	sunlabel->intrlv = cpu_to_be16(res);
+	return 0;
+}
+
+/**
+ * fdisk_sun_set_rspeed
+ * @cxt: context
+ *
+ * Sets rotation speed. This function uses libfdisk Ask API for dialog with
+ * user.
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int fdisk_sun_set_rspeed(struct fdisk_context *cxt)
+{
+	struct sun_disklabel *sunlabel = self_disklabel(cxt);
+	uintmax_t res;
+	int rc = fdisk_ask_number(cxt, 1,			/* low */
+			be16_to_cpu(sunlabel->rpm),		/* default */
+			USHRT_MAX,				/* high */
+			_("Rotation speed (rpm)"),		/* query */
+			&res);					/* result */
+	if (rc)
+		return rc;
+	sunlabel->rpm = cpu_to_be16(res);
+	return 0;
+}
+
+/**
+ * fdisk_sun_set_pcylcount
+ * @cxt: context
+ *
+ * Sets number of physical cylinders. This function uses libfdisk Ask API for
+ * dialog with user.
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int fdisk_sun_set_pcylcount(struct fdisk_context *cxt)
+{
+	struct sun_disklabel *sunlabel = self_disklabel(cxt);
+	uintmax_t res;
+	int rc = fdisk_ask_number(cxt, 0,			/* low */
+			be16_to_cpu(sunlabel->pcyl),		/* default */
+			USHRT_MAX,				/* high */
+			_("Number of physical cylinders"),	/* query */
+			&res);					/* result */
+	if (!rc)
+		return rc;
+	sunlabel->pcyl = cpu_to_be16(res);
+	return 0;
+}
+
+static int sun_write_disklabel(struct fdisk_context *cxt)
+{
+	struct sun_disklabel *sunlabel;
+	unsigned short *ush;
+	unsigned short csum = 0;
+	const size_t sz = sizeof(struct sun_disklabel);
+
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, SUN));
+
+	sunlabel = self_disklabel(cxt);
+
+	/* Maybe geometry has been modified */
+	sunlabel->nhead = cpu_to_be16(cxt->geom.heads);
+	sunlabel->nsect = cpu_to_be16(cxt->geom.sectors);
+
+	if (cxt->geom.cylinders != be16_to_cpu(sunlabel->ncyl))
+		sunlabel->ncyl = cpu_to_be16( cxt->geom.cylinders
+				      - be16_to_cpu(sunlabel->acyl) );
+
+	ush = (unsigned short *) sunlabel;
+
+	while(ush < (unsigned short *)(&sunlabel->csum))
+		csum ^= *ush++;
+	sunlabel->csum = csum;
+	if (lseek(cxt->dev_fd, 0, SEEK_SET) < 0)
+		return -errno;
+	if (write_all(cxt->dev_fd, sunlabel, sz) != 0)
+		return -errno;
+
+	return 0;
+}
+
+static int sun_set_partition(
+		struct fdisk_context *cxt,
+		size_t i,
+		struct fdisk_partition *pa)
+{
+	struct sun_disklabel *sunlabel;
+	struct sun_partition *part;
+	struct sun_info *info;
+
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, SUN));
+
+	sunlabel = self_disklabel(cxt);
+
+	if (i >= cxt->label->nparts_max)
+		return -EINVAL;
+
+	if (pa->type) {
+		struct fdisk_parttype *t = pa->type;
+
+		if (t->code > UINT16_MAX)
+			return -EINVAL;
+
+		if (i == 2 && t->code != SUN_TAG_WHOLEDISK)
+			fdisk_info(cxt, _("Consider leaving partition 3 as Whole disk (5),\n"
+			         "as SunOS/Solaris expects it and even Linux likes it.\n"));
+
+		part = &sunlabel->partitions[i];
+		info = &sunlabel->vtoc.infos[i];
+
+		if (cxt->script == NULL &&
+		    t->code == SUN_TAG_LINUX_SWAP && !part->start_cylinder) {
+			int yes, rc;
+
+			rc = fdisk_ask_yesno(cxt,
+			      _("It is highly recommended that the partition at offset 0\n"
+			      "is UFS, EXT2FS filesystem or SunOS swap. Putting Linux swap\n"
+			      "there may destroy your partition table and bootblock.\n"
+			      "Are you sure you want to tag the partition as Linux swap?"), &yes);
+			if (rc)
+				return rc;
+			if (!yes)
+				return 1;
+		}
+
+		switch (t->code) {
+		case SUN_TAG_SWAP:
+		case SUN_TAG_LINUX_SWAP:
+			/* swaps are not mountable by default */
+			info->flags |= cpu_to_be16(SUN_FLAG_UNMNT);
+			break;
+		default:
+			/* assume other types are mountable;
+			   user can change it anyway */
+			info->flags &= ~cpu_to_be16(SUN_FLAG_UNMNT);
+			break;
+		}
+		info->id = cpu_to_be16(t->code);
+	}
+
+	if (fdisk_partition_has_start(pa))
+		sunlabel->partitions[i].start_cylinder =
+			cpu_to_be32(pa->start / (cxt->geom.heads * cxt->geom.sectors));
+	if (fdisk_partition_has_size(pa))
+		sunlabel->partitions[i].num_sectors = cpu_to_be32(pa->size);
+
+	fdisk_label_set_changed(cxt->label, 1);
+	return 0;
+}
+
+
+static int sun_reset_alignment(struct fdisk_context *cxt __attribute__((__unused__)))
+{
+	return 0;
+}
+
+
+static int sun_partition_is_used(
+		struct fdisk_context *cxt,
+		size_t i)
+{
+	struct sun_disklabel *sunlabel;
+
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, SUN));
+
+	if (i >= cxt->label->nparts_max)
+		return 0;
+
+	sunlabel = self_disklabel(cxt);
+	return sunlabel->partitions[i].num_sectors ? 1 : 0;
+}
+
+static const struct fdisk_field sun_fields[] =
+{
+	{ FDISK_FIELD_DEVICE,	N_("Device"),	 10,	0 },
+	{ FDISK_FIELD_START,	N_("Start"),	  5,	FDISK_FIELDFL_NUMBER },
+	{ FDISK_FIELD_END,	N_("End"),	  5,	FDISK_FIELDFL_NUMBER },
+	{ FDISK_FIELD_SECTORS,	N_("Sectors"),	  5,	FDISK_FIELDFL_NUMBER },
+	{ FDISK_FIELD_CYLINDERS,N_("Cylinders"),  5,	FDISK_FIELDFL_NUMBER },
+	{ FDISK_FIELD_SIZE,	N_("Size"),	  5,	FDISK_FIELDFL_NUMBER },
+	{ FDISK_FIELD_TYPEID,	N_("Id"),	  2,	FDISK_FIELDFL_NUMBER },
+	{ FDISK_FIELD_TYPE,	N_("Type"),	0.1,	0 },
+	{ FDISK_FIELD_ATTR,	N_("Flags"),	  0,	FDISK_FIELDFL_NUMBER }
+};
+
+const struct fdisk_label_operations sun_operations =
+{
+	.probe		= sun_probe_label,
+	.write		= sun_write_disklabel,
+	.verify		= sun_verify_disklabel,
+	.create		= sun_create_disklabel,
+	.list		= sun_list_disklabel,
+
+	.get_part	= sun_get_partition,
+	.set_part	= sun_set_partition,
+	.add_part	= sun_add_partition,
+	.del_part	= sun_delete_partition,
+
+	.part_is_used	= sun_partition_is_used,
+	.part_toggle_flag = sun_toggle_partition_flag,
+
+	.reset_alignment = sun_reset_alignment,
+};
+
+/*
+ * allocates SUN label driver
+ */
+struct fdisk_label *fdisk_new_sun_label(struct fdisk_context *cxt)
+{
+	struct fdisk_label *lb;
+	struct fdisk_sun_label *sun;
+
+	assert(cxt);
+
+	sun = calloc(1, sizeof(*sun));
+	if (!sun)
+		return NULL;
+
+	/* initialize generic part of the driver */
+	lb = (struct fdisk_label *) sun;
+	lb->name = "sun";
+	lb->id = FDISK_DISKLABEL_SUN;
+	lb->op = &sun_operations;
+	lb->parttypes = sun_parttypes;
+	lb->nparttypes = ARRAY_SIZE(sun_parttypes) - 1;
+	lb->fields = sun_fields;
+	lb->nfields = ARRAY_SIZE(sun_fields);
+	lb->flags |= FDISK_LABEL_FL_REQUIRE_GEOMETRY;
+
+	return lb;
+}
diff --git a/libblkid/libfdisk/src/table.c b/libblkid/libfdisk/src/table.c
new file mode 100644
index 0000000..1add09f
--- /dev/null
+++ b/libblkid/libfdisk/src/table.c
@@ -0,0 +1,664 @@
+
+#include "fdiskP.h"
+
+/**
+ * SECTION: table
+ * @title: Table
+ * @short_description: container for fdisk partitions
+ *
+ * The fdisk_table is simple container for fdisk_partitions. The table is no
+ * directly connected to label data (partition table), and table changes don't
+ * affect in-memory or on-disk data.
+ */
+
+/**
+ * fdisk_new_table:
+ *
+ * The table is a container for struct fdisk_partition entries. The container
+ * does not have any real connection with label (partition table) and with
+ * real on-disk data.
+ *
+ * Returns: newly allocated table struct.
+ */
+struct fdisk_table *fdisk_new_table(void)
+{
+	struct fdisk_table *tb = NULL;
+
+	tb = calloc(1, sizeof(*tb));
+	if (!tb)
+		return NULL;
+
+	DBG(TAB, ul_debugobj(tb, "alloc"));
+	tb->refcount = 1;
+	INIT_LIST_HEAD(&tb->parts);
+	return tb;
+}
+
+/**
+ * fdisk_reset_table:
+ * @tb: tab pointer
+ *
+ * Removes all entries (partitions) from the table. The parititons with zero
+ * reference count will be deallocated. This function does not modify partition
+ * table.
+ *
+ * Returns: 0 on success or negative number in case of error.
+ */
+int fdisk_reset_table(struct fdisk_table *tb)
+{
+	if (!tb)
+		return -EINVAL;
+
+	DBG(TAB, ul_debugobj(tb, "reset"));
+
+	while (!list_empty(&tb->parts)) {
+		struct fdisk_partition *pa = list_entry(tb->parts.next,
+				                  struct fdisk_partition, parts);
+		fdisk_table_remove_partition(tb, pa);
+	}
+
+	tb->nents = 0;
+	return 0;
+}
+
+/**
+ * fdisk_ref_table:
+ * @tb: table pointer
+ *
+ * Incremparts reference counter.
+ */
+void fdisk_ref_table(struct fdisk_table *tb)
+{
+	if (tb)
+		tb->refcount++;
+}
+
+/**
+ * fdisk_unref_table:
+ * @tb: table pointer
+ *
+ * De-incremparts reference counter, on zero the @tb is automatically
+ * deallocated.
+ */
+void fdisk_unref_table(struct fdisk_table *tb)
+{
+	if (!tb)
+		return;
+
+	tb->refcount--;
+	if (tb->refcount <= 0) {
+		fdisk_reset_table(tb);
+
+		DBG(TAB, ul_debugobj(tb, "free"));
+		free(tb);
+	}
+}
+
+/**
+ * fdisk_table_is_empty:
+ * @tb: pointer to tab
+ *
+ * Returns: 1 if the table is without filesystems, or 0.
+ */
+int fdisk_table_is_empty(struct fdisk_table *tb)
+{
+	return tb == NULL || list_empty(&tb->parts) ? 1 : 0;
+}
+
+/**
+ * fdisk_table_get_nents:
+ * @tb: pointer to tab
+ *
+ * Returns: number of entries in table.
+ */
+size_t fdisk_table_get_nents(struct fdisk_table *tb)
+{
+	return tb ? tb->nents : 0;
+}
+
+/**
+ * fdisk_table_next_partition:
+ * @tb: tab pointer
+ * @itr: iterator
+ * @pa: returns the next tab entry
+ *
+ * Returns: 0 on success, negative number in case of error or 1 at the end of list.
+ *
+ * Example:
+ * <informalexample>
+ *   <programlisting>
+ *	while(fdisk_table_next_partition(tb, itr, &pa) == 0) {
+ *		...
+ *	}
+ *   </programlisting>
+ * </informalexample>
+ */
+int fdisk_table_next_partition(
+			struct fdisk_table *tb,
+			struct fdisk_iter *itr,
+			struct fdisk_partition **pa)
+{
+	int rc = 1;
+
+	assert(tb);
+	assert(itr);
+	assert(pa);
+
+	if (!tb || !itr || !pa)
+		return -EINVAL;
+	*pa = NULL;
+
+	if (!itr->head)
+		FDISK_ITER_INIT(itr, &tb->parts);
+	if (itr->p != itr->head) {
+		FDISK_ITER_ITERATE(itr, *pa, struct fdisk_partition, parts);
+		rc = 0;
+	}
+
+	return rc;
+}
+
+struct fdisk_partition *fdisk_table_get_partition(
+			struct fdisk_table *tb,
+			size_t n)
+{
+	struct fdisk_partition *pa = NULL;
+	struct fdisk_iter itr;
+
+	if (!tb)
+		return NULL;
+
+	fdisk_reset_iter(&itr, FDISK_ITER_FORWARD);
+
+	while (fdisk_table_next_partition(tb, &itr, &pa) == 0) {
+		if (n == 0)
+			return pa;
+		n--;
+	}
+
+	return NULL;
+}
+
+/**
+ * fdisk_table_add_partition
+ * @tb: tab pointer
+ * @pa: new entry
+ *
+ * Adds a new entry to table and increment @pa reference counter. Don't forget to
+ * use fdisk_unref_pa() after fdisk_table_add_partition() if you want to keep
+ * the @pa referenced by the table only.
+ *
+ * Returns: 0 on success or negative number in case of error.
+ */
+int fdisk_table_add_partition(struct fdisk_table *tb, struct fdisk_partition *pa)
+{
+	assert(tb);
+	assert(pa);
+
+	if (!tb || !pa)
+		return -EINVAL;
+
+	fdisk_ref_partition(pa);
+	list_add_tail(&pa->parts, &tb->parts);
+	tb->nents++;
+
+	DBG(TAB, ul_debugobj(tb, "add entry %p [start=%ju, end=%ju, size=%ju, %s %s %s]",
+			pa,
+			(uintmax_t) fdisk_partition_get_start(pa),
+			(uintmax_t) fdisk_partition_get_end(pa),
+			(uintmax_t) fdisk_partition_get_size(pa),
+			fdisk_partition_is_freespace(pa) ? "freespace" : "",
+			fdisk_partition_is_nested(pa)    ? "nested"    : "",
+			fdisk_partition_is_container(pa) ? "container" : "primary"));
+	return 0;
+}
+
+/* inserts @pa after @poz */
+static int table_insert_partition(
+			struct fdisk_table *tb,
+			struct fdisk_partition *poz,
+			struct fdisk_partition *pa)
+{
+	assert(tb);
+	assert(pa);
+
+	fdisk_ref_partition(pa);
+	if (poz)
+		list_add(&pa->parts, &poz->parts);
+	else
+		list_add(&pa->parts, &tb->parts);
+	tb->nents++;
+
+	DBG(TAB, ul_debugobj(tb, "insert entry %p pre=%p [start=%ju, end=%ju, size=%ju, %s %s %s]",
+			pa, poz ? poz : NULL,
+			(uintmax_t) fdisk_partition_get_start(pa),
+			(uintmax_t) fdisk_partition_get_end(pa),
+			(uintmax_t) fdisk_partition_get_size(pa),
+			fdisk_partition_is_freespace(pa) ? "freespace" : "",
+			fdisk_partition_is_nested(pa)    ? "nested"    : "",
+			fdisk_partition_is_container(pa) ? "container" : ""));
+	return 0;
+}
+
+/**
+ * fdisk_table_remove_partition
+ * @tb: tab pointer
+ * @pa: new entry
+ *
+ * Removes the @pa from the table and de-increment reference counter of the @pa. The
+ * partition with zero reference counter will be deallocated. Don't forget to use
+ * fdisk_ref_partition() before call fdisk_table_remove_partition() if you want
+ * to use @pa later.
+ *
+ * Returns: 0 on success or negative number in case of error.
+ */
+int fdisk_table_remove_partition(struct fdisk_table *tb, struct fdisk_partition *pa)
+{
+	assert(tb);
+	assert(pa);
+
+	if (!tb || !pa)
+		return -EINVAL;
+
+	DBG(TAB, ul_debugobj(tb, "remove entry %p", pa));
+	list_del(&pa->parts);
+	INIT_LIST_HEAD(&pa->parts);
+
+	fdisk_unref_partition(pa);
+	tb->nents--;
+
+	return 0;
+}
+
+/**
+ * fdisk_get_partitions
+ * @cxt: fdisk context
+ * @tb: returns table
+ *
+ * This function adds partitions from disklabel to @table, it allocates a new
+ * table if if @table points to NULL.
+ *
+ * Returns: 0 on success, otherwise, a corresponding error.
+ */
+int fdisk_get_partitions(struct fdisk_context *cxt, struct fdisk_table **tb)
+{
+	size_t i;
+
+	if (!cxt || !cxt->label || !tb)
+		return -EINVAL;
+	if (!cxt->label->op->get_part)
+		return -ENOSYS;
+
+	DBG(CXT, ul_debugobj(cxt, "get table"));
+
+	if (!*tb && !(*tb = fdisk_new_table()))
+		return -ENOMEM;
+
+	for (i = 0; i < cxt->label->nparts_max; i++) {
+		struct fdisk_partition *pa = NULL;
+
+		if (fdisk_get_partition(cxt, i, &pa) != 0)
+			continue;
+		if (fdisk_partition_is_used(pa))
+			fdisk_table_add_partition(*tb, pa);
+		fdisk_unref_partition(pa);
+	}
+
+	return 0;
+}
+
+static void debug_print_table(struct fdisk_table *tb)
+{
+	struct fdisk_iter itr;
+	struct fdisk_partition *pa;
+
+	fdisk_reset_iter(&itr, FDISK_ITER_FORWARD);
+	while (fdisk_table_next_partition(tb, &itr, &pa) == 0)
+		ul_debugobj(tb, "partition %p [partno=%zu, start=%ju, end=%ju, size=%ju] ",
+			    pa, pa->partno,
+			    (uintmax_t) fdisk_partition_get_start(pa),
+			    (uintmax_t) fdisk_partition_get_end(pa),
+			    (uintmax_t) fdisk_partition_get_size(pa));
+
+}
+
+
+typedef	int (*fdisk_partcmp_t)(struct fdisk_partition *, struct fdisk_partition *);
+
+static int cmp_parts_wrapper(struct list_head *a, struct list_head *b, void *data)
+{
+	struct fdisk_partition *pa = list_entry(a, struct fdisk_partition, parts),
+			       *pb = list_entry(b, struct fdisk_partition, parts);
+
+	fdisk_partcmp_t cmp = (fdisk_partcmp_t) data;
+
+	return cmp(pa, pb);
+}
+
+
+/**
+ * fdisk_table_sort_partitions:
+ * @tb: table
+ * @cmp: compare function
+ *
+ * Sort partition in the table.
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int fdisk_table_sort_partitions(struct fdisk_table *tb,
+			int (*cmp)(struct fdisk_partition *,
+				   struct fdisk_partition *))
+{
+	if (!tb)
+		return -EINVAL;
+
+	DBG(TAB, ul_debugobj(tb, "Before sort:"));
+	ON_DBG(TAB, debug_print_table(tb));
+
+	list_sort(&tb->parts, cmp_parts_wrapper, (void *) cmp);
+
+	DBG(TAB, ul_debugobj(tb, "After sort:"));
+	ON_DBG(TAB, debug_print_table(tb));
+
+	return 0;
+}
+
+/* allocates a new freespace description */
+static int new_freespace(struct fdisk_context *cxt,
+			 fdisk_sector_t start,
+			 fdisk_sector_t end,
+			 struct fdisk_partition *parent,
+			 struct fdisk_partition **pa)
+{
+	assert(cxt);
+	assert(pa);
+
+	*pa = NULL;
+
+	if (start == end)
+		return 0;
+	*pa = fdisk_new_partition();
+	if (!*pa)
+		return -ENOMEM;
+
+	assert(start);
+	assert(end);
+	assert(end > start);
+
+	(*pa)->freespace = 1;
+	(*pa)->start = fdisk_align_lba_in_range(cxt, start, start, end);
+	(*pa)->size = end - (*pa)->start + 1ULL;
+
+	if (parent)
+		(*pa)->parent_partno = parent->partno;
+	return 0;
+}
+
+/* add freespace description to the right place within @tb */
+static int table_add_freespace(
+			struct fdisk_context *cxt,
+			struct fdisk_table *tb,
+			fdisk_sector_t start,
+			fdisk_sector_t end,
+			struct fdisk_partition *parent)
+{
+	struct fdisk_partition *pa, *x, *real_parent = NULL, *best = NULL;
+	struct fdisk_iter itr;
+	int rc = 0;
+
+	assert(tb);
+
+	rc = new_freespace(cxt, start, end, parent, &pa);
+	if (rc)
+		return -ENOMEM;
+	if (!pa)
+		return 0;
+
+	assert(fdisk_partition_has_start(pa));
+	assert(fdisk_partition_has_end(pa));
+
+	DBG(TAB, ul_debugobj(tb, "adding freespace"));
+
+	fdisk_reset_iter(&itr, FDISK_ITER_FORWARD);
+	if (parent && fdisk_partition_has_partno(parent)) {
+		while (fdisk_table_next_partition(tb, &itr, &x) == 0) {
+			if (!fdisk_partition_has_partno(x))
+				continue;
+			if (x->partno == parent->partno) {
+				real_parent = x;
+				break;
+			}
+		}
+		if (!real_parent) {
+			DBG(TAB, ul_debugobj(tb, "not found freespace parent (partno=%zu)",
+					parent->partno));
+			fdisk_reset_iter(&itr, FDISK_ITER_FORWARD);
+		}
+	}
+
+	while (fdisk_table_next_partition(tb, &itr, &x) == 0) {
+		fdisk_sector_t end, best_end = 0;
+
+		if (!fdisk_partition_has_end(x))
+			continue;
+
+		end = fdisk_partition_get_end(x);
+		if (best)
+			best_end = fdisk_partition_get_end(best);
+
+		if (end < pa->start && (!best || best_end < end))
+			best = x;
+	}
+
+	if (!best && real_parent)
+		best = real_parent;
+	rc = table_insert_partition(tb, best, pa);
+
+	fdisk_unref_partition(pa);
+
+	DBG(TAB, ul_debugobj(tb, "adding freespace DONE [rc=%d]", rc));
+	return rc;
+}
+
+/* analyze @cont(ainer) in @parts and add all detected freespace into @tb, note
+ * that @parts has to be sorted by partition starts */
+static int check_container_freespace(struct fdisk_context *cxt,
+				     struct fdisk_table *parts,
+				     struct fdisk_table *tb,
+				     struct fdisk_partition *cont)
+{
+	struct fdisk_iter itr;
+	struct fdisk_partition *pa;
+	fdisk_sector_t x, last, grain, lastplusoff;
+	int rc = 0;
+
+	assert(cxt);
+	assert(parts);
+	assert(tb);
+	assert(cont);
+	assert(fdisk_partition_has_start(cont));
+
+	DBG(TAB, ul_debugobj(tb, "analyze container 0x%p", cont));
+
+	last = fdisk_partition_get_start(cont);
+	grain = cxt->grain > cxt->sector_size ?	cxt->grain / cxt->sector_size : 1;
+	fdisk_reset_iter(&itr, FDISK_ITER_FORWARD);
+
+	DBG(CXT, ul_debugobj(cxt, "initialized:  last=%ju, grain=%ju", last, grain));
+
+	while (fdisk_table_next_partition(parts, &itr, &pa) == 0) {
+
+		DBG(CXT, ul_debugobj(cxt, "partno=%zu, start=%ju", pa->partno, pa->start));
+
+		if (!pa->used || !fdisk_partition_is_nested(pa)
+			      || !fdisk_partition_has_start(pa))
+			continue;
+
+		DBG(CXT, ul_debugobj(cxt, "freespace container analyze: partno=%zu, start=%ju, end=%ju",
+					pa->partno,
+					(uintmax_t) fdisk_partition_get_start(pa),
+					(uintmax_t) fdisk_partition_get_end(pa)));
+
+		lastplusoff = last + cxt->first_lba;
+		if (pa->start > lastplusoff && pa->start - lastplusoff > grain)
+			rc = table_add_freespace(cxt, tb, lastplusoff, pa->start, cont);
+		if (rc)
+			goto done;
+		last = fdisk_partition_get_end(pa);
+	}
+
+	/* free-space remaining in extended partition */
+	x = fdisk_partition_get_start(cont) + fdisk_partition_get_size(cont) - 1;
+	lastplusoff = last + cxt->first_lba;
+	if (lastplusoff < x && x - lastplusoff > grain) {
+		DBG(TAB, ul_debugobj(tb, "add remaining space in container 0x%p", cont));
+		rc = table_add_freespace(cxt, tb, lastplusoff, x, cont);
+	}
+
+done:
+	DBG(TAB, ul_debugobj(tb, "analyze container 0x%p DONE [rc=%d]", cont, rc));
+	return rc;
+}
+
+
+/**
+ * fdisk_get_freespaces
+ * @cxt: fdisk context
+ * @tb: returns table
+ *
+ * This function adds freespace (described by fdisk_partition) to @table, it
+ * allocates a new table if the @table points to NULL.
+ *
+ * Note that free space smaller than grain (see fdisk_get_grain()) is ignored.
+
+ * Returns: 0 on success, otherwise, a corresponding error.
+ */
+int fdisk_get_freespaces(struct fdisk_context *cxt, struct fdisk_table **tb)
+{
+	int rc = 0;
+	fdisk_sector_t last, grain;
+	struct fdisk_table *parts = NULL;
+	struct fdisk_partition *pa;
+	struct fdisk_iter itr;
+
+	DBG(CXT, ul_debugobj(cxt, "get freespace"));
+
+	if (!cxt || !cxt->label || !tb)
+		return -EINVAL;
+	if (!*tb && !(*tb = fdisk_new_table()))
+		return -ENOMEM;
+
+	rc = fdisk_get_partitions(cxt, &parts);
+	if (rc)
+		goto done;
+
+	fdisk_table_sort_partitions(parts, fdisk_partition_cmp_start);
+	fdisk_reset_iter(&itr, FDISK_ITER_FORWARD);
+	last = cxt->first_lba;
+	grain = cxt->grain > cxt->sector_size ?	cxt->grain / cxt->sector_size : 1;
+
+	DBG(CXT, ul_debugobj(cxt, "initialized:  last=%ju, grain=%ju", last, grain));
+
+	/* analyze gaps between partitions */
+	while (rc == 0 && fdisk_table_next_partition(parts, &itr, &pa) == 0) {
+
+		DBG(CXT, ul_debugobj(cxt, "partno=%zu, start=%ju", pa->partno, pa->start));
+
+		if (!pa->used || pa->wholedisk || fdisk_partition_is_nested(pa)
+			      || !fdisk_partition_has_start(pa))
+			continue;
+		DBG(CXT, ul_debugobj(cxt, "freespace analyze: partno=%zu, start=%ju, end=%ju",
+					pa->partno,
+					(uintmax_t) fdisk_partition_get_start(pa),
+					(uintmax_t) fdisk_partition_get_end(pa)));
+		if (last + grain <= pa->start) {
+			rc = table_add_freespace(cxt, *tb,
+				last + (last > cxt->first_lba ? 1 : 0),
+				pa->start - 1, NULL);
+		}
+		/* add gaps between logical partitions */
+		if (fdisk_partition_is_container(pa))
+			rc = check_container_freespace(cxt, parts, *tb, pa);
+		last = fdisk_partition_get_end(pa);
+	}
+
+	/* add free-space behind last partition to the end of the table (so
+	 * don't use table_add_freespace()) */
+	if (rc == 0 && last + grain < cxt->total_sectors - 1) {
+		DBG(CXT, ul_debugobj(cxt, "freespace behind last partition detected"));
+		rc = new_freespace(cxt,
+			last + (last > cxt->first_lba ? 1 : 0),
+			cxt->last_lba, NULL, &pa);
+		if (pa) {
+			fdisk_table_add_partition(*tb, pa);
+			fdisk_unref_partition(pa);
+		}
+	}
+
+done:
+	fdisk_unref_table(parts);
+
+	DBG(CXT, ul_debugobj(cxt, "get freespace DONE [rc=%d]", rc));
+	return rc;
+}
+
+/**
+ * fdisk_table_wrong_order:
+ * @tb: table
+ *
+ * Returns: 1 of the table is not in disk order
+ */
+int fdisk_table_wrong_order(struct fdisk_table *tb)
+{
+	struct fdisk_partition *pa;
+	struct fdisk_iter itr;
+	fdisk_sector_t last = 0;
+
+	DBG(TAB, ul_debugobj(tb, "wrong older check"));
+
+	fdisk_reset_iter(&itr, FDISK_ITER_FORWARD);
+	while (tb && fdisk_table_next_partition(tb, &itr, &pa) == 0) {
+		if (!fdisk_partition_has_start(pa))
+			continue;
+		if (pa->start < last)
+			return 1;
+		last = pa->start;
+	}
+	return 0;
+}
+
+/**
+ * fdisk_apply_table:
+ * @cxt: context
+ * @tb: table
+ *
+ * Add partitions from table @tb to the in-memory disk label. See
+ * fdisk_add_partition(), fdisk_delete_all_partitions(). The partitons
+ * that does not define start (or does not follow the default start)
+ * are ingored.
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int fdisk_apply_table(struct fdisk_context *cxt, struct fdisk_table *tb)
+{
+	struct fdisk_partition *pa;
+	struct fdisk_iter itr;
+	int rc = 0;
+
+	assert(cxt);
+	assert(tb);
+
+	DBG(TAB, ul_debugobj(tb, "applying to context %p", cxt));
+
+	fdisk_reset_iter(&itr, FDISK_ITER_FORWARD);
+	while (tb && fdisk_table_next_partition(tb, &itr, &pa) == 0) {
+		if (!fdisk_partition_has_start(pa) && !pa->start_follow_default)
+			continue;
+		rc = fdisk_add_partition(cxt, pa, NULL);
+		if (rc)
+			break;
+	}
+
+	return rc;
+}
+
diff --git a/libblkid/libfdisk/src/test.c b/libblkid/libfdisk/src/test.c
new file mode 100644
index 0000000..31ed7e0
--- /dev/null
+++ b/libblkid/libfdisk/src/test.c
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2008-2009 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ *
+ * Routines for TEST_PROGRAMs
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#ifndef TEST_PROGRAM
+#define TEST_PROGRAM
+#endif
+
+#include "fdiskP.h"
+
+int fdisk_run_test(struct fdisk_test *tests, int argc, char *argv[])
+{
+	int rc = -1;
+	struct fdisk_test *ts;
+
+	assert(tests);
+	assert(argc);
+	assert(argv);
+
+	if (argc < 2 ||
+	    strcmp(argv[1], "--help") == 0 ||
+	    strcmp(argv[1], "-h") == 0)
+		goto usage;
+
+	fdisk_init_debug(0);
+
+	for (ts = tests; ts->name; ts++) {
+		if (strcmp(ts->name, argv[1]) == 0) {
+			rc = ts->body(ts, argc - 1, argv + 1);
+			if (rc)
+				printf("FAILED [rc=%d]", rc);
+			break;
+		}
+	}
+
+	if (rc < 0 && ts->name == NULL)
+		goto usage;
+
+	return rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
+usage:
+	printf("\nUsage:\n\t%s <test> [testoptions]\nTests:\n",
+			program_invocation_short_name);
+	for (ts = tests; ts->name; ts++) {
+		printf("\t%-15s", ts->name);
+		if (ts->usage)
+			printf(" %s\n", ts->usage);
+	}
+	printf("\n");
+	return EXIT_FAILURE;
+}
diff --git a/libblkid/libfdisk/src/utils.c b/libblkid/libfdisk/src/utils.c
new file mode 100644
index 0000000..482a306
--- /dev/null
+++ b/libblkid/libfdisk/src/utils.c
@@ -0,0 +1,154 @@
+
+#include "fdiskP.h"
+#include "pathnames.h"
+
+#include <ctype.h>
+
+/**
+ * SECTION: utils
+ * @title: Utils
+ * @short_description: misc fdisk functions
+ */
+
+/*
+ * Zeros in-memory first sector buffer
+ */
+int fdisk_init_firstsector_buffer(struct fdisk_context *cxt)
+{
+	if (!cxt)
+		return -EINVAL;
+
+	if (!cxt->firstsector || cxt->firstsector_bufsz != cxt->sector_size) {
+		/* Let's allocate a new buffer if no allocated yet, or the
+		 * current buffer has incorrect size */
+		if (!cxt->parent || cxt->parent->firstsector != cxt->firstsector)
+			free(cxt->firstsector);
+
+		DBG(CXT, ul_debugobj(cxt, "initialize in-memory first sector "
+				"buffer [sector_size=%lu]", cxt->sector_size));
+		cxt->firstsector = calloc(1, cxt->sector_size);
+		if (!cxt->firstsector)
+			return -ENOMEM;
+
+		cxt->firstsector_bufsz = cxt->sector_size;
+		return 0;
+	}
+
+	DBG(CXT, ul_debugobj(cxt, "zeroize in-memory first sector buffer"));
+	memset(cxt->firstsector, 0, cxt->firstsector_bufsz);
+	return 0;
+}
+
+int fdisk_read_firstsector(struct fdisk_context *cxt)
+{
+	ssize_t r;
+	int rc;
+
+	assert(cxt);
+	assert(cxt->sector_size);
+
+	rc = fdisk_init_firstsector_buffer(cxt);
+	if (rc)
+		return rc;
+
+	assert(cxt->sector_size == cxt->firstsector_bufsz);
+
+	DBG(CXT, ul_debugobj(cxt, "reading first sector "
+				"buffer [sector_size=%lu]", cxt->sector_size));
+
+	r = lseek(cxt->dev_fd, 0, SEEK_SET);
+	if (r == -1)
+	{
+		DBG(CXT, ul_debugobj(cxt, "failed to seek to first sector %m"));
+		return -errno;
+	}
+
+	r = read(cxt->dev_fd, cxt->firstsector, cxt->sector_size);
+
+	if (r != cxt->sector_size) {
+		if (!errno)
+			errno = EINVAL;	/* probably too small file/device */
+		DBG(CXT, ul_debugobj(cxt, "failed to read first sector %m"));
+		return -errno;
+	}
+
+	return 0;
+}
+
+/**
+ * fdisk_partname:
+ * @dev: device name
+ * @partno: partition name
+ *
+ * Return: allocated buffer with partition name, use free() to deallocate.
+ */
+char *fdisk_partname(const char *dev, size_t partno)
+{
+	char *res = NULL;
+	const char *p = "";
+	int w = 0;
+
+	if (!dev || !*dev) {
+		if (asprintf(&res, "%zd", partno) > 0)
+			return res;
+		return NULL;
+	}
+
+	w = strlen(dev);
+	if (isdigit(dev[w - 1]))
+#ifdef __GNU__
+		p = "s";
+#else
+		p = "p";
+#endif
+
+	/* devfs kludge - note: fdisk partition names are not supposed
+	   to equal kernel names, so there is no reason to do this */
+	if (strcmp(dev + w - 4, "disc") == 0) {
+		w -= 4;
+		p = "part";
+	}
+
+	/* udev names partitions by appending -partN
+	   e.g. ata-SAMSUNG_SV8004H_0357J1FT712448-part1 */
+	if ((strncmp(dev, _PATH_DEV_BYID, sizeof(_PATH_DEV_BYID) - 1) == 0) ||
+	     strncmp(dev, _PATH_DEV_BYPATH, sizeof(_PATH_DEV_BYPATH) - 1) == 0) {
+	       p = "-part";
+	}
+
+	if (asprintf(&res, "%.*s%s%zu", w, dev, p, partno) > 0)
+		return res;
+
+	return NULL;
+}
+
+#ifdef TEST_PROGRAM
+struct fdisk_label *fdisk_new_dos_label(struct fdisk_context *cxt) { return NULL; }
+struct fdisk_label *fdisk_new_bsd_label(struct fdisk_context *cxt) { return NULL; }
+
+int test_partnames(struct fdisk_test *ts, int argc, char *argv[])
+{
+	size_t i;
+	const char *disk = argv[1];
+
+	for (i = 0; i < 5; i++) {
+		char *p = fdisk_partname(disk, i + 1);
+		if (p)
+			printf("%zu: '%s'\n", i + 1, p);
+		free(p);
+	}
+
+	return 0;
+}
+
+int main(int argc, char *argv[])
+{
+	struct fdisk_test tss[] = {
+		{ "--partnames",  test_partnames,  "<diskname>" },
+		{ NULL }
+	};
+
+	return fdisk_run_test(tss, argc, argv);
+}
+
+#endif