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/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;
+}