Blob Blame History Raw
From 4bcc9fdca681b48c54954924608b8c337d61fbad Mon Sep 17 00:00:00 2001
From: Karel Zak <kzak@redhat.com>
Date: Mon, 13 Apr 2015 13:40:25 +0200
Subject: [PATCH 4/5] libfdisk: support bootbits protection from (p)MBR

Signed-off-by: Karel Zak <kzak@redhat.com>
---
 include/pt-mbr.h                    |  1 +
 libfdisk/docs/libfdisk-sections.txt |  6 ++-
 libfdisk/src/context.c              | 33 ++++++++++++++++-
 libfdisk/src/dos.c                  |  5 ++-
 libfdisk/src/fdiskP.h               |  4 +-
 libfdisk/src/gpt.c                  |  6 ++-
 libfdisk/src/libfdisk.h.in          |  2 +
 libfdisk/src/libfdisk.sym           |  5 +++
 libfdisk/src/sgi.c                  |  2 +-
 libfdisk/src/sun.c                  |  2 +-
 libfdisk/src/utils.c                | 73 ++++++++++++++++++++++++++-----------
 11 files changed, 109 insertions(+), 30 deletions(-)

diff --git a/include/pt-mbr.h b/include/pt-mbr.h
index 1279e3c..6957b95 100644
--- a/include/pt-mbr.h
+++ b/include/pt-mbr.h
@@ -11,6 +11,7 @@ struct dos_partition {
 } __attribute__((packed));
 
 #define MBR_PT_OFFSET		0x1be
+#define MBR_PT_BOOTBITS_SIZE	440
 
 static inline struct dos_partition *mbr_get_partition(unsigned char *mbr, int i)
 {
diff --git a/libfdisk/docs/libfdisk-sections.txt b/libfdisk/docs/libfdisk-sections.txt
index 676d500..2d5cd92 100644
--- a/libfdisk/docs/libfdisk-sections.txt
+++ b/libfdisk/docs/libfdisk-sections.txt
@@ -279,6 +279,7 @@ fdisk_unref_table
 fdisk_context
 fdisk_assign_device
 fdisk_deassign_device
+fdisk_enable_bootbits_protection
 fdisk_enable_details
 fdisk_enable_listonly
 fdisk_get_alignment_offset
@@ -297,22 +298,23 @@ fdisk_get_parent
 fdisk_get_physector_size
 fdisk_get_sector_size
 fdisk_get_size_unit
-FDISK_PLURAL
-FDISK_SINGULAR
 fdisk_get_unit
 fdisk_get_units_per_sector
 fdisk_has_label
+fdisk_has_protected_bootbits
 fdisk_is_details
 fdisk_is_labeltype
 fdisk_is_listonly
 fdisk_is_readonly
 fdisk_new_context
 fdisk_new_nested_context
+FDISK_PLURAL
 fdisk_ref_context
 fdisk_set_first_lba
 fdisk_set_last_lba
 fdisk_set_size_unit
 fdisk_set_unit
+FDISK_SINGULAR
 fdisk_unref_context
 fdisk_use_cylinders
 </SECTION>
diff --git a/libfdisk/src/context.c b/libfdisk/src/context.c
index 84867b0..efc961c 100644
--- a/libfdisk/src/context.c
+++ b/libfdisk/src/context.c
@@ -94,12 +94,13 @@ static int init_nested_from_parent(struct fdisk_context *cxt, int isnew)
 	cxt->user_log_sector =	parent->user_log_sector;
 	cxt->user_pyh_sector =  parent->user_pyh_sector;
 
-	/* parent <--> nested independent setting, initialize for new nested 
+	/* 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;
+		cxt->protect_bootbits = parent->protect_bootbits;
 	}
 
 	free(cxt->dev_path);
@@ -304,6 +305,36 @@ int fdisk_has_label(struct fdisk_context *cxt)
 }
 
 /**
+ * fdisk_has_protected_bootbits:
+ * @cxt: fdisk context
+ *
+ * Returns: return 1 if boot bits protection enabled.
+ */
+int fdisk_has_protected_bootbits(struct fdisk_context *cxt)
+{
+	return cxt && cxt->protect_bootbits;
+}
+
+/**
+ * fdisk_enable_bootbits_protection:
+ * @cxt: fdisk context
+ * @enable: 1 or 0
+ *
+ * The library zeroizes all the first sector when create a new disk label by
+ * default.  This function allows to control this behavior. For now it's
+ * supported for MBR and GPT.
+ *
+ * Returns: 0 on success, < 0 on error.
+ */
+int fdisk_enable_bootbits_protection(struct fdisk_context *cxt, int enable)
+{
+	if (!cxt)
+		return -EINVAL;
+	cxt->protect_bootbits = enable ? 1 : 0;
+	return 0;
+}
+
+/**
  * fdisk_get_npartitions:
  * @cxt: context
  *
diff --git a/libfdisk/src/dos.c b/libfdisk/src/dos.c
index 6468071..f2308e3 100644
--- a/libfdisk/src/dos.c
+++ b/libfdisk/src/dos.c
@@ -659,7 +659,10 @@ static int dos_create_disklabel(struct fdisk_context *cxt)
 	if (!has_id)
 		random_get_bytes(&id, sizeof(id));
 
-	rc = fdisk_init_firstsector_buffer(cxt);
+	if (fdisk_has_protected_bootbits(cxt))
+		rc = fdisk_init_firstsector_buffer(cxt, 0, MBR_PT_BOOTBITS_SIZE);
+	else
+		rc = fdisk_init_firstsector_buffer(cxt, 0, 0);
 	if (rc)
 		return rc;
 	dos_init(cxt);
diff --git a/libfdisk/src/fdiskP.h b/libfdisk/src/fdiskP.h
index dc561f3..0ca498d 100644
--- a/libfdisk/src/fdiskP.h
+++ b/libfdisk/src/fdiskP.h
@@ -350,6 +350,7 @@ struct fdisk_context {
 	unsigned int readonly : 1,		/* don't write to the device */
 		     display_in_cyl_units : 1,	/* for obscure labels */
 		     display_details : 1,	/* expert display mode */
+		     protect_bootbits : 1,	/* don't zeroize fll irst sector */
 		     listonly : 1;		/* list partition, nothing else */
 
 	int sizeunit;				/* SIZE fields, FDISK_SIZEUNIT_* */
@@ -402,7 +403,8 @@ 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_init_firstsector_buffer(struct fdisk_context *cxt,
+			unsigned int protect_off, unsigned int protect_size);
 extern int fdisk_read_firstsector(struct fdisk_context *cxt);
 extern char *fdisk_partname(const char *dev, size_t partno);
 
diff --git a/libfdisk/src/gpt.c b/libfdisk/src/gpt.c
index 61b0a32..22e2d16 100644
--- a/libfdisk/src/gpt.c
+++ b/libfdisk/src/gpt.c
@@ -27,6 +27,7 @@
 #include "bitops.h"
 #include "strutils.h"
 #include "all-io.h"
+#include "pt-mbr.h"
 
 /**
  * SECTION: gpt
@@ -406,7 +407,10 @@ static int gpt_mknew_pmbr(struct fdisk_context *cxt)
 	if (!cxt || !cxt->firstsector)
 		return -ENOSYS;
 
-	rc = fdisk_init_firstsector_buffer(cxt);
+	if (fdisk_has_protected_bootbits(cxt))
+		rc = fdisk_init_firstsector_buffer(cxt, 0, MBR_PT_BOOTBITS_SIZE);
+	else
+		rc = fdisk_init_firstsector_buffer(cxt, 0, 0);
 	if (rc)
 		return rc;
 
diff --git a/libfdisk/src/libfdisk.h.in b/libfdisk/src/libfdisk.h.in
index 3aeef1e..78c0728 100644
--- a/libfdisk/src/libfdisk.h.in
+++ b/libfdisk/src/libfdisk.h.in
@@ -211,6 +211,8 @@ enum {
 int fdisk_set_size_unit(struct fdisk_context *cxt, int unit);
 int fdisk_get_size_unit(struct fdisk_context *cxt);
 
+int fdisk_has_protected_bootbits(struct fdisk_context *cxt);
+int fdisk_enable_bootbits_protection(struct fdisk_context *cxt, int enable);
 
 /* parttype.c */
 struct fdisk_parttype *fdisk_new_parttype(void);
diff --git a/libfdisk/src/libfdisk.sym b/libfdisk/src/libfdisk.sym
index 541b247..9f720ff 100644
--- a/libfdisk/src/libfdisk.sym
+++ b/libfdisk/src/libfdisk.sym
@@ -239,3 +239,8 @@ global:
 local:
 	*;
 };
+
+FDISK_2.27 {
+	fdisk_enable_bootbits_protection;
+	fdisk_has_protected_bootbits;
+} FDISK_2.26;
diff --git a/libfdisk/src/sgi.c b/libfdisk/src/sgi.c
index cd4cedf..7cc68b5 100644
--- a/libfdisk/src/sgi.c
+++ b/libfdisk/src/sgi.c
@@ -965,7 +965,7 @@ static int sgi_create_disklabel(struct fdisk_context *cxt)
 		}
 	}
 #endif
-	rc = fdisk_init_firstsector_buffer(cxt);
+	rc = fdisk_init_firstsector_buffer(cxt, 0, 0);
 	if (rc)
 		return rc;
 
diff --git a/libfdisk/src/sun.c b/libfdisk/src/sun.c
index d99c39f..27e3bdd 100644
--- a/libfdisk/src/sun.c
+++ b/libfdisk/src/sun.c
@@ -209,7 +209,7 @@ static int sun_create_disklabel(struct fdisk_context *cxt)
 	assert(fdisk_is_label(cxt, SUN));
 
 	/* map first sector to header */
-	rc = fdisk_init_firstsector_buffer(cxt);
+	rc = fdisk_init_firstsector_buffer(cxt, 0, 0);
 	if (rc)
 		return rc;
 
diff --git a/libfdisk/src/utils.c b/libfdisk/src/utils.c
index 482a306..cc470d9 100644
--- a/libfdisk/src/utils.c
+++ b/libfdisk/src/utils.c
@@ -10,14 +10,49 @@
  * @short_description: misc fdisk functions
  */
 
+static int read_from_device(struct fdisk_context *cxt,
+		unsigned char *buf,
+		uintmax_t start, size_t size)
+{
+	ssize_t r;
+
+	assert(cxt);
+
+	DBG(CXT, ul_debugobj(cxt, "reading: offset=%ju, size=%zu",
+				start, size));
+
+	r = lseek(cxt->dev_fd, start, SEEK_SET);
+	if (r == -1)
+	{
+		DBG(CXT, ul_debugobj(cxt, "failed to seek to offset %ju: %m", start));
+		return -errno;
+	}
+
+	r = read(cxt->dev_fd, buf, size);
+	if (r < 0 || r != size) {
+		if (!errno)
+			errno = EINVAL;	/* probably too small file/device */
+		DBG(CXT, ul_debugobj(cxt, "failed to read %zu from offset %ju: %m",
+				size, start));
+		return -errno;
+	}
+
+	return 0;
+}
+
+
 /*
  * Zeros in-memory first sector buffer
  */
-int fdisk_init_firstsector_buffer(struct fdisk_context *cxt)
+int fdisk_init_firstsector_buffer(struct fdisk_context *cxt,
+				  unsigned int protect_off,
+				  unsigned int protect_size)
 {
 	if (!cxt)
 		return -EINVAL;
 
+	assert(protect_off + protect_size <= cxt->sector_size);
+
 	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 */
@@ -36,43 +71,37 @@ int fdisk_init_firstsector_buffer(struct fdisk_context *cxt)
 
 	DBG(CXT, ul_debugobj(cxt, "zeroize in-memory first sector buffer"));
 	memset(cxt->firstsector, 0, cxt->firstsector_bufsz);
+
+	if (protect_size) {
+		/*
+		 * It would be possible to reuse data from cxt->firstsector
+		 * (call memset() for non-protected area only) and avoid one
+		 * read() from the device, but it seems like a too fragile
+		 * solution as we have no clue about stuff in the buffer --
+		 * maybe it was already modified. Let's re-read from the device
+		 * to be sure.			-- kzak 13-Apr-2015
+		 */
+		DBG(CXT, ul_debugobj(cxt, "first sector protection enabled -- re-reading"));
+		read_from_device(cxt, cxt->firstsector, protect_off, protect_size);
+	}
 	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);
+	rc = fdisk_init_firstsector_buffer(cxt, 0, 0);
 	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;
+	return  read_from_device(cxt, cxt->firstsector, 0, cxt->sector_size);
 }
 
 /**
-- 
2.1.0