From melanhit@gmail.com  Sun May  5 23:08:59 2013
Return-Path: <melanhit@gmail.com>
Received: from mx1.freebsd.org (mx1.FreeBSD.org [8.8.178.115])
	by hub.freebsd.org (Postfix) with ESMTP id CDF944D7
	for <FreeBSD-gnats-submit@freebsd.org>; Sun,  5 May 2013 23:08:59 +0000 (UTC)
	(envelope-from melanhit@gmail.com)
Received: from mail-qc0-x234.google.com (mail-qc0-x234.google.com [IPv6:2607:f8b0:400d:c01::234])
	by mx1.freebsd.org (Postfix) with ESMTP id 8E916162
	for <FreeBSD-gnats-submit@freebsd.org>; Sun,  5 May 2013 23:08:59 +0000 (UTC)
Received: by mail-qc0-f180.google.com with SMTP id b13so1472212qca.25
        for <FreeBSD-gnats-submit@freebsd.org>; Sun, 05 May 2013 16:08:59 -0700 (PDT)
Received: from localhost.localdomain (128-124-28-114.dialup.umc.net.ua. [128.124.28.114])
        by mx.google.com with ESMTPSA id bc5sm34286984qeb.3.2013.05.05.16.08.48
        for <FreeBSD-gnats-submit@freebsd.org>
        (version=TLSv1.2 cipher=ECDHE-RSA-RC4-SHA bits=128/128);
        Sun, 05 May 2013 16:08:58 -0700 (PDT)
Received: from ion.uabsd.org (ion.uabsd.org [192.168.56.100])
	by localhost.localdomain (Postfix) with ESMTP id EBF68160411
	for <FreeBSD-gnats-submit@freebsd.org>; Mon,  6 May 2013 02:08:35 +0300 (EEST)
Received: from ion.uabsd.org (localhost [127.0.0.1])
	by ion.uabsd.org (8.14.7/8.14.7) with ESMTP id r45K8NwS028642
	for <FreeBSD-gnats-submit@freebsd.org>; Sun, 5 May 2013 23:08:26 +0300 (EEST)
	(envelope-from melanhit@ion.uabsd.org)
Received: (from melanhit@localhost)
	by ion.uabsd.org (8.14.7/8.14.7/Submit) id r45K8MfD028641;
	Sun, 5 May 2013 23:08:22 +0300 (EEST)
	(envelope-from melanhit)
Message-Id: <201305052008.r45K8MfD028641@ion.uabsd.org>
Date: Sun, 5 May 2013 23:08:22 +0300 (EEST)
From: Andrew Romanenko <melanhit@gmail.com>
Reply-To: Andrew Romanenko <melanhit@gmail.com>
To: FreeBSD-gnats-submit@freebsd.org
Cc:
Subject: geom_eli: support external metadata
X-Send-Pr-Version: 3.114
X-GNATS-Notify:

>Number:         178359
>Category:       kern
>Synopsis:       [geom] [patch] geom_eli: support external metadata
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    freebsd-geom
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:  
>Class:          change-request
>Submitter-Id:   current-users
>Arrival-Date:   Sun May 05 23:10:00 UTC 2013
>Closed-Date:    
>Last-Modified:  Wed May  8 21:40:00 UTC 2013
>Originator:     Andrew Romanenko
>Release:        FreeBSD 9.1-STABLE i386
>Organization:
UA BSD
>Environment:
System: FreeBSD ion.uabsd.org 9.1-STABLE FreeBSD 9.1-STABLE #0 r250121: Wed May 1 23:38:36 EEST 2013 root@ion.uabsd.org:/usr/obj/usr/src/sys/GENERIC i386


	
>Description:
	Add support external metadata for geom eli.
	For i386 all works fine.
	Need check manpage for grammatical errors (i don't spell english very well)
>How-To-Repeat:
	
>Fix:

	

--- geli.patch begins here ---
--- sbin/geom/class/eli/geom_eli.c.orig	2013-05-03 00:00:34.551720905 +0300
+++ sbin/geom/class/eli/geom_eli.c	2013-05-05 23:57:52.631347936 +0300
@@ -60,7 +60,6 @@
 
 #define	GELI_BACKUP_DIR	"/var/backups/"
 #define	GELI_ENC_ALGO	"aes"
-
 static void eli_main(struct gctl_req *req, unsigned flags);
 static void eli_init(struct gctl_req *req);
 static void eli_attach(struct gctl_req *req);
@@ -81,23 +80,23 @@
 /*
  * Available commands:
  *
- * init [-bhPv] [-a aalgo] [-B backupfile] [-e ealgo] [-i iterations] [-l keylen] [-J newpassfile] [-K newkeyfile] prov
+ * init [-bPv] [-a aalgo] [-B backupfile] [-H headerfile] [-e ealgo] [-i iterations] [-l keylen] [-J newpassfile] [-K newkeyfile] prov
  * label - alias for 'init'
- * attach [-dprv] [-j passfile] [-k keyfile] prov
+ * attach [-dprv] [-h headerfile] [-j passfile] [-k keyfile] prov
  * detach [-fl] prov ...
  * stop - alias for 'detach'
  * onetime [-d] [-a aalgo] [-e ealgo] [-l keylen] prov
- * configure [-bB] prov ...
- * setkey [-pPv] [-n keyno] [-j passfile] [-J newpassfile] [-k keyfile] [-K newkeyfile] prov
- * delkey [-afv] [-n keyno] prov
+ * configure [-bB] [-h headerfile] prov ...
+ * setkey [-pPv] [-h headerfile] [-n keyno] [-j passfile] [-J newpassfile] [-k keyfile] [-K newkeyfile] prov
+ * delkey [-afv] [-h headerfile] [-n keyno] prov
  * suspend [-v] -a | prov ...
- * resume [-pv] [-j passfile] [-k keyfile] prov
+ * resume [-pv] [-h headerfile] [-j passfile] [-k keyfile] prov
  * kill [-av] [prov ...]
  * backup [-v] prov file
  * restore [-fv] file prov
- * resize [-v] -s oldsize prov
+ * resize [-v] [-h headerfile] -s oldsize prov
  * clear [-v] prov ...
- * dump [-v] prov ...
+ * dump [-v] [-h headerfile] prov ...
  */
 struct g_command class_commands[] = {
 	{ "init", G_FLAG_VERBOSE, eli_main,
@@ -112,9 +111,10 @@
 		{ 'l', "keylen", "0", G_TYPE_NUMBER },
 		{ 'P', "nonewpassphrase", NULL, G_TYPE_BOOL },
 		{ 's', "sectorsize", "0", G_TYPE_NUMBER },
+		{ 'H', "header", "", G_TYPE_STRING },
 		G_OPT_SENTINEL
 	    },
-	    "[-bPv] [-a aalgo] [-B backupfile] [-e ealgo] [-i iterations] [-l keylen] [-J newpassfile] [-K newkeyfile] [-s sectorsize] prov"
+	    "[-bPv] [-H headerfile] [-a aalgo] [-B backupfile] [-e ealgo] [-i iterations] [-l keylen] [-J newpassfile] [-K newkeyfile] [-s sectorsize] prov"
 	},
 	{ "label", G_FLAG_VERBOSE, eli_main,
 	    {
@@ -128,6 +128,7 @@
 		{ 'l', "keylen", "0", G_TYPE_NUMBER },
 		{ 'P', "nonewpassphrase", NULL, G_TYPE_BOOL },
 		{ 's', "sectorsize", "0", G_TYPE_NUMBER },
+		{ 'h', "header", "", G_TYPE_STRING },
 		G_OPT_SENTINEL
 	    },
 	    "- an alias for 'init'"
@@ -139,9 +140,10 @@
 		{ 'k', "keyfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI },
 		{ 'p', "nopassphrase", NULL, G_TYPE_BOOL },
 		{ 'r', "readonly", NULL, G_TYPE_BOOL },
+		{ 'h', "header", "", G_TYPE_STRING },
 		G_OPT_SENTINEL
 	    },
-	    "[-dprv] [-j passfile] [-k keyfile] prov"
+	    "[-dprv] [-h headerfile] [-j passfile] [-k keyfile] prov"
 	},
 	{ "detach", 0, NULL,
 	    {
@@ -174,9 +176,10 @@
 	    {
 		{ 'b', "boot", NULL, G_TYPE_BOOL },
 		{ 'B', "noboot", NULL, G_TYPE_BOOL },
+		{ 'h', "header", "", G_TYPE_STRING },
 		G_OPT_SENTINEL
 	    },
-	    "[-bB] prov ..."
+	    "[-bB] [-h headerfile] prov ..."
 	},
 	{ "setkey", G_FLAG_VERBOSE, eli_main,
 	    {
@@ -188,18 +191,20 @@
 		{ 'n', "keyno", "-1", G_TYPE_NUMBER },
 		{ 'p', "nopassphrase", NULL, G_TYPE_BOOL },
 		{ 'P', "nonewpassphrase", NULL, G_TYPE_BOOL },
+		{ 'h', "header", "", G_TYPE_STRING },
 		G_OPT_SENTINEL
 	    },
-	    "[-pPv] [-n keyno] [-i iterations] [-j passfile] [-J newpassfile] [-k keyfile] [-K newkeyfile] prov"
+	    "[-pPv] [-h headerfile] [-n keyno] [-i iterations] [-j passfile] [-J newpassfile] [-k keyfile] [-K newkeyfile] prov"
 	},
 	{ "delkey", G_FLAG_VERBOSE, eli_main,
 	    {
 		{ 'a', "all", NULL, G_TYPE_BOOL },
 		{ 'f', "force", NULL, G_TYPE_BOOL },
 		{ 'n', "keyno", "-1", G_TYPE_NUMBER },
+		{ 'h', "header", "", G_TYPE_STRING },
 		G_OPT_SENTINEL
 	    },
-	    "[-afv] [-n keyno] prov"
+	    "[-afv] [-h headerfile] [-n keyno] prov"
 	},
 	{ "suspend", G_FLAG_VERBOSE, NULL,
 	    {
@@ -213,9 +218,10 @@
 		{ 'j', "passfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI },
 		{ 'k', "keyfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI },
 		{ 'p', "nopassphrase", NULL, G_TYPE_BOOL },
+		{ 'h', "header", "", G_TYPE_STRING },
 		G_OPT_SENTINEL
 	    },
-	    "[-pv] [-j passfile] [-k keyfile] prov"
+	    "[-pv] [-h headerfile] [-j passfile] [-k keyfile] prov"
 	},
 	{ "kill", G_FLAG_VERBOSE, eli_main,
 	    {
@@ -237,15 +243,20 @@
 	{ "resize", G_FLAG_VERBOSE, eli_main,
 	    {
 		{ 's', "oldsize", NULL, G_TYPE_NUMBER },
+		{ 'h', "header", "", G_TYPE_STRING },
 		G_OPT_SENTINEL
 	    },
-	    "[-v] -s oldsize prov"
+	    "[-v] [-h headerfile] -s oldsize prov"
 	},
 	{ "clear", G_FLAG_VERBOSE, eli_main, G_NULL_OPTS,
 	    "[-v] prov ..."
 	},
-	{ "dump", G_FLAG_VERBOSE, eli_main, G_NULL_OPTS,
-	    "[-v] prov ..."
+	{ "dump", G_FLAG_VERBOSE, eli_main,
+		{
+		{ 'h', "header", "", G_TYPE_STRING },
+		G_OPT_SENTINEL
+		},
+	    "[-v] [-h headerfile]  prov ..."
 	},
 	G_CMD_SENTINEL
 };
@@ -653,7 +664,7 @@
 	unsigned char sector[sizeof(struct g_eli_metadata)];
 	unsigned char key[G_ELI_USERKEYLEN];
 	char backfile[MAXPATHLEN];
-	const char *str, *prov;
+	const char *str, *prov, *header;
 	unsigned secsize;
 	off_t mediasize;
 	intmax_t val;
@@ -776,17 +787,39 @@
 		return;
 	}
 
+	header = gctl_get_ascii(req, "header");
+
+	/* Store header if it present */
+	if(header[0] != '\0') {
+		error = eli_metadata_store(req, header, &md);
+
+		if(error != 0) {
+			gctl_error(req, "Cannot store header %s: %s.", header,
+					strerror(error));
+			return;
+		}
+
+		str = gctl_get_ascii(req, "backupfile");
+		if(str[0] != '\0')
+			if(strcmp(str, "none") != 0)
+				printf("Warning: options -B and -h are mutualy exlusive\n");
+
+		return;
+	}
+
 	eli_metadata_encode(&md, sector);
 	bzero(&md, sizeof(md));
+
 	error = g_metadata_store(prov, sector, sizeof(sector));
 	bzero(sector, sizeof(sector));
 	if (error != 0) {
 		gctl_error(req, "Cannot store metadata on %s: %s.", prov,
-		    strerror(error));
+			strerror(error));
 		return;
 	}
 	if (verbose)
 		printf("Metadata value stored on %s.\n", prov);
+
 	/* Backup metadata to a file. */
 	str = gctl_get_ascii(req, "backupfile");
 	if (str[0] != '\0') {
@@ -820,10 +853,14 @@
 {
 	struct g_eli_metadata md;
 	unsigned char key[G_ELI_USERKEYLEN];
-	const char *prov;
+	unsigned char *hd;
+	const char *prov, *str, *header;
 	off_t mediasize;
+	ssize_t hdsize;
 	int nargs;
 
+	hd = NULL;
+
 	nargs = gctl_get_int(req, "nargs");
 	if (nargs != 1) {
 		gctl_error(req, "Invalid number of arguments.");
@@ -831,9 +868,16 @@
 	}
 	prov = gctl_get_ascii(req, "arg0");
 
-	if (eli_metadata_read(req, prov, &md) == -1)
+	header = gctl_get_ascii(req, "header");
+	if (header[0] != '\0')
+		str = header;
+	else
+		str = prov;
+
+	if (eli_metadata_read(req, str, &md) == -1)
 		return;
 
+	hdsize = g_get_sectorsize(prov);
 	mediasize = g_get_mediasize(prov);
 	if (md.md_provsize != (uint64_t)mediasize) {
 		gctl_error(req, "Provider size mismatch.");
@@ -845,20 +889,43 @@
 		return;
 	}
 
+	if(header[0] != '\0') {
+		hd = malloc(hdsize);
+		if(hd == NULL) {
+			gctl_error(req, "Cannot allocate %zd bytes of memory.", hdsize);
+			return;
+		}
+
+		bzero(hd, hdsize);
+		eli_metadata_encode(&md, hd);
+	} else {
+		hdsize = sizeof(hd);
+	}
+
+	gctl_ro_param(req, "hd", hdsize, hd);
 	gctl_ro_param(req, "key", sizeof(key), key);
 	if (gctl_issue(req) == NULL) {
 		if (verbose)
 			printf("Attached to %s.\n", prov);
 	}
 	bzero(key, sizeof(key));
+	if(hd != NULL)
+		free(hd);
 }
 
 static void
 eli_configure_detached(struct gctl_req *req, const char *prov, bool boot)
 {
 	struct g_eli_metadata md;
+	const char *str, *header;
 
-	if (eli_metadata_read(req, prov, &md) == -1)
+	header = gctl_get_ascii(req, "header");
+	if (header[0] != '\0')
+		str = header;
+	else
+		str = prov;
+
+	if (eli_metadata_read(req, str, &md) == -1)
 		return;
 
 	if (boot && (md.md_flags & G_ELI_FLAG_BOOT)) {
@@ -872,7 +939,13 @@
 			md.md_flags |= G_ELI_FLAG_BOOT;
 		else
 			md.md_flags &= ~G_ELI_FLAG_BOOT;
-		eli_metadata_store(req, prov, &md);
+
+		if(header[0] != '\0')
+			str = header;
+		else
+			str = prov;
+
+		eli_metadata_store(req, str, &md);
 	}
 	bzero(&md, sizeof(md));
 }
@@ -880,7 +953,7 @@
 static void
 eli_configure(struct gctl_req *req)
 {
-	const char *prov;
+	const char *prov, *header;
 	bool boot, noboot;
 	int i, nargs;
 
@@ -902,14 +975,29 @@
 		return;
 	}
 
+	header = gctl_get_ascii(req, "header");
+
 	/* First attached providers. */
-	gctl_issue(req);
+	if(header[0] != '\0') {
+		if(nargs != 1) {
+			gctl_error(req, "Too many arguments.");
+			return;
+		}
+
+		prov = gctl_get_ascii(req, "arg0");
+		eli_configure_detached(req, prov, boot);
+		return;
+	} else {
+		gctl_issue(req);
+	}
+
 	/* Now the rest. */
 	for (i = 0; i < nargs; i++) {
 		prov = gctl_get_ascii(req, "arg%d", i);
 		if (!eli_is_attached(prov))
 			eli_configure_detached(req, prov, boot);
 	}
+
 }
 
 static void
@@ -950,6 +1038,7 @@
 eli_setkey_detached(struct gctl_req *req, const char *prov,
  struct g_eli_metadata *md)
 {
+	const char *header, *str;
 	unsigned char key[G_ELI_USERKEYLEN], mkey[G_ELI_DATAIVKEYLEN];
 	unsigned char *mkeydst;
 	unsigned int nkey;
@@ -1035,7 +1124,14 @@
 	}
 
 	/* Store metadata with fresh key. */
-	eli_metadata_store(req, prov, md);
+	header = gctl_get_ascii(req, "header");
+	if(header[0] != '\0')
+		str = header;
+	else
+		str = prov;
+
+	eli_metadata_store(req, str, md);
+
 	bzero(md, sizeof(*md));
 }
 
@@ -1043,7 +1139,7 @@
 eli_setkey(struct gctl_req *req)
 {
 	struct g_eli_metadata md;
-	const char *prov;
+	const char *prov, *header, *str;
 	int nargs;
 
 	nargs = gctl_get_int(req, "nargs");
@@ -1053,10 +1149,16 @@
 	}
 	prov = gctl_get_ascii(req, "arg0");
 
-	if (eli_metadata_read(req, prov, &md) == -1)
+	header = gctl_get_ascii(req, "header");
+	if(header[0] != '\0')
+		str = header;
+	else
+		str = prov;
+
+	if (eli_metadata_read(req, str, &md) == -1)
 		return;
 
-	if (eli_is_attached(prov))
+	if (eli_is_attached(prov) && header[0] == '\0')
 		eli_setkey_attached(req, &md);
 	else
 		eli_setkey_detached(req, prov, &md);
@@ -1079,12 +1181,19 @@
 eli_delkey_detached(struct gctl_req *req, const char *prov)
 {
 	struct g_eli_metadata md;
+	const char *header, *str;
 	unsigned char *mkeydst;
 	unsigned int nkey;
 	intmax_t val;
 	bool all, force;
 
-	if (eli_metadata_read(req, prov, &md) == -1)
+	header = gctl_get_ascii(req, "header");
+	if(header[0] != '\0')
+		str = header;
+	else
+		str = prov;
+
+	if (eli_metadata_read(req, str, &md) == -1)
 		return;
 
 	all = gctl_get_int(req, "all");
@@ -1116,6 +1225,11 @@
 		arc4rand(mkeydst, G_ELI_MKEYLEN);
 	}
 
+	if(header[0] != '\0')
+		str = header;
+	else
+		str = prov;
+
 	eli_metadata_store(req, prov, &md);
 	bzero(&md, sizeof(md));
 }
@@ -1123,7 +1237,7 @@
 static void
 eli_delkey(struct gctl_req *req)
 {
-	const char *prov;
+	const char *prov, *header;
 	int nargs;
 
 	nargs = gctl_get_int(req, "nargs");
@@ -1133,7 +1247,9 @@
 	}
 	prov = gctl_get_ascii(req, "arg0");
 
-	if (eli_is_attached(prov))
+	header = gctl_get_ascii(req, "header");
+
+	if (eli_is_attached(prov) && header[0] == '\0')
 		eli_delkey_attached(req, prov);
 	else
 		eli_delkey_detached(req, prov);
@@ -1144,10 +1260,14 @@
 {
 	struct g_eli_metadata md;
 	unsigned char key[G_ELI_USERKEYLEN];
-	const char *prov;
+	unsigned char *hd;
+	const char *prov, *str, *header;
 	off_t mediasize;
+	ssize_t hdsize;
 	int nargs;
 
+	hd = NULL;
+
 	nargs = gctl_get_int(req, "nargs");
 	if (nargs != 1) {
 		gctl_error(req, "Invalid number of arguments.");
@@ -1155,10 +1275,18 @@
 	}
 	prov = gctl_get_ascii(req, "arg0");
 
-	if (eli_metadata_read(req, prov, &md) == -1)
+	header = gctl_get_ascii(req, "header");
+	if (header[0] != '\0')
+		str = header;
+	else
+		str = prov;
+
+	if (eli_metadata_read(req, str, &md) == -1)
 		return;
 
 	mediasize = g_get_mediasize(prov);
+	hdsize = g_get_sectorsize(prov);
+
 	if (md.md_provsize != (uint64_t)mediasize) {
 		gctl_error(req, "Provider size mismatch.");
 		return;
@@ -1169,12 +1297,30 @@
 		return;
 	}
 
+	if(header[0] != '\0') {
+		hd = malloc(hdsize);
+		if(hd == NULL) {
+			gctl_error(req, "Cannot allocate %zd bytes of memory.", hdsize);
+			return;
+		}
+
+		bzero(hd, hdsize);
+		eli_metadata_encode(&md, hd);
+	} else {
+		hdsize = sizeof(hd);
+	}
+
+	gctl_ro_param(req, "hd", hdsize, hd);
 	gctl_ro_param(req, "key", sizeof(key), key);
+
 	if (gctl_issue(req) == NULL) {
 		if (verbose)
 			printf("Resumed %s.\n", prov);
 	}
 	bzero(key, sizeof(key));
+
+	if(hd != NULL)
+		free(hd);
 }
 
 static int
@@ -1469,11 +1615,11 @@
 eli_resize(struct gctl_req *req)
 {
 	struct g_eli_metadata md;
-	const char *prov;
+	const char *prov, *header;
 	unsigned char *sector;
 	ssize_t secsize;
 	off_t mediasize, oldsize;
-	int nargs, provfd;
+	int nargs, provfd, error;
 
 	nargs = gctl_get_int(req, "nargs");
 	if (nargs != 1) {
@@ -1486,14 +1632,18 @@
 	sector = NULL;
 	secsize = 0;
 
-	provfd = g_open(prov, 1);
-	if (provfd == -1) {
-		gctl_error(req, "Cannot open %s: %s.", prov, strerror(errno));
-		goto out;
+	header = gctl_get_ascii(req, "header");
+
+	if(header[0] == '\0') {
+		provfd = g_open(prov, 1);
+		if (provfd == -1) {
+			gctl_error(req, "Cannot open %s: %s.", prov, strerror(errno));
+			goto out;
+		}
 	}
 
-	mediasize = g_mediasize(provfd);
-	secsize = g_sectorsize(provfd);
+	mediasize = g_get_mediasize(prov);
+	secsize = g_get_sectorsize(prov);
 	if (mediasize == -1 || secsize == -1) {
 		gctl_error(req, "Cannot get information about %s: %s.", prov,
 		    strerror(errno));
@@ -1517,16 +1667,24 @@
 	}
 
 	/* Read metadata from the 'oldsize' offset. */
-	if (pread(provfd, sector, secsize, oldsize - secsize) != secsize) {
-		gctl_error(req, "Cannot read old metadata: %s.",
-		    strerror(errno));
-		goto out;
-	}
+	if(header[0] != '\0') {
+		if (eli_metadata_read(req, header, &md) == -1) {
+			gctl_error(req, "Cannot read old metadata: %s.",
+					header);
+			goto out;
+		}
+	} else {
+		if (pread(provfd, sector, secsize, oldsize - secsize) != secsize) {
+			gctl_error(req, "Cannot read old metadata: %s.",
+				strerror(errno));
+			goto out;
+		}
 
-	/* Check if this sector contains geli metadata. */
-	if (eli_metadata_decode(sector, &md) != 0) {
-		gctl_error(req, "MD5 hash mismatch: no metadata for oldsize.");
-		goto out;
+		/* Check if this sector contains geli metadata. */
+		if (eli_metadata_decode(sector, &md) != 0) {
+			gctl_error(req, "MD5 hash mismatch: no metadata for oldsize.");
+			goto out;
+		}
 	}
 
 	/*
@@ -1544,15 +1702,25 @@
 	 */
 	md.md_provsize = mediasize;
 	eli_metadata_encode(&md, sector);
-	if (pwrite(provfd, sector, secsize, mediasize - secsize) != secsize) {
-		gctl_error(req, "Cannot write metadata: %s.", strerror(errno));
-		goto out;
-	}
-	(void)g_flush(provfd);
 
-	/* Now trash the old metadata. */
-	if (eli_trash_metadata(req, prov, provfd, oldsize - secsize) == -1)
-		goto out;
+	if(header[0] != '\0') {
+		error = eli_metadata_store(req, header, &md);
+		if(error != 0) {
+			gctl_error(req, "Cannot store header %s: %s.", header,
+					strerror(error));
+			goto out;
+		}
+	} else {
+		if (pwrite(provfd, sector, secsize, mediasize - secsize) != secsize) {
+			gctl_error(req, "Cannot write metadata: %s.", strerror(errno));
+			goto out;
+		}
+		(void)g_flush(provfd);
+
+		/* Now trash the old metadata. */
+		if (eli_trash_metadata(req, prov, provfd, oldsize - secsize) == -1)
+			goto out;
+	}
 out:
 	if (provfd >= 0)
 		(void)g_close(provfd);
@@ -1592,7 +1760,7 @@
 eli_dump(struct gctl_req *req)
 {
 	struct g_eli_metadata md, tmpmd;
-	const char *name;
+	const char *name, *header;
 	int error, i, nargs;
 
 	nargs = gctl_get_int(req, "nargs");
@@ -1601,15 +1769,40 @@
 		return;
 	}
 
-	for (i = 0; i < nargs; i++) {
-		name = gctl_get_ascii(req, "arg%d", i);
-		error = g_metadata_read(name, (unsigned char *)&tmpmd,
-		    sizeof(tmpmd), G_ELI_MAGIC);
-		if (error != 0) {
+	header = gctl_get_ascii(req, "header");
+	if(header[0] != '\0') {
+		if(nargs != 1) {
+			gctl_error(req, "Too many arguments.");
+			return;
+		}
+
+		if (eli_metadata_read(req, header, &tmpmd) == -1)
+			return;
+
+		name = gctl_get_ascii(req, "arg0");
+
+		if (strcmp(tmpmd.md_magic, G_ELI_MAGIC) != 0) {
+			error = EINVAL;
 			fprintf(stderr, "Cannot read metadata from %s: %s.\n",
-			    name, strerror(error));
+					name, strerror(error));
 			gctl_error(req, "Not fully done.");
-			continue;
+			return;
+		}
+
+		name = header;
+	}
+
+	for (i = 0; i < nargs; i++) {
+		if(header[0] == '\0') {
+			name = gctl_get_ascii(req, "arg%d", i);
+			error = g_metadata_read(name, (unsigned char *)&tmpmd,
+				sizeof(tmpmd), G_ELI_MAGIC);
+			if (error != 0) {
+				fprintf(stderr, "Cannot read metadata from %s: %s.\n",
+					name, strerror(error));
+				gctl_error(req, "Not fully done.");
+				continue;
+			}
 		}
 		if (eli_metadata_decode((unsigned char *)&tmpmd, &md) != 0) {
 			fprintf(stderr, "MD5 hash mismatch for %s, skipping.\n",
--- sbin/geom/class/eli/geli.8.orig	2013-04-29 01:45:56.000000000 +0300
+++ sbin/geom/class/eli/geli.8	2013-05-05 16:49:57.642188841 +0300
@@ -52,6 +52,7 @@
 .Nm
 .Cm init
 .Op Fl bPv
+.Op Fl H Ar headerfile
 .Op Fl a Ar aalgo
 .Op Fl B Ar backupfile
 .Op Fl e Ar ealgo
@@ -67,6 +68,7 @@
 .Nm
 .Cm attach
 .Op Fl dprv
+.Op Fl h Ar headerfile
 .Op Fl j Ar passfile
 .Op Fl k Ar keyfile
 .Ar prov
@@ -88,10 +90,12 @@
 .Nm
 .Cm configure
 .Op Fl bB
+.Op Fl h Ar headerfile
 .Ar prov ...
 .Nm
 .Cm setkey
 .Op Fl pPv
+.Op Fl h Ar headerfile
 .Op Fl i Ar iterations
 .Op Fl j Ar passfile
 .Op Fl J Ar newpassfile
@@ -102,6 +106,7 @@
 .Nm
 .Cm delkey
 .Op Fl afv
+.Op Fl h Ar headerfile
 .Op Fl n Ar keyno
 .Ar prov
 .Nm
@@ -125,12 +130,14 @@
 .Nm
 .Cm resume
 .Op Fl pv
+.Op Fl h Ar headerfile
 .Op Fl j Ar passfile
 .Op Fl k Ar keyfile
 .Ar prov
 .Nm
 .Cm resize
 .Op Fl v
+.Op Fl h Ar headerfile
 .Fl s Ar oldsize
 .Ar prov
 .Nm
@@ -140,6 +147,7 @@
 .Nm
 .Cm dump
 .Op Fl v
+.Op Fl h Ar headerfile
 .Ar prov ...
 .Nm
 .Cm list
@@ -240,6 +248,8 @@
 .Pp
 Additional options include:
 .Bl -tag -width ".Fl J Ar newpassfile"
+.It Fl h Ar headerfile
+Store GELI metadata (header) in the external file
 .It Fl a Ar aalgo
 Enable data integrity verification (authentication) using the given algorithm.
 This will reduce size of available storage and also reduce speed.
@@ -341,6 +351,8 @@
 option for the
 .Cm detach
 subcommand.
+.It Fl h Ar headerfile
+Read metadata from a file instead from a provider
 .It Fl j Ar passfile
 Specifies a file which contains the passphrase or its part.
 For more information see the description of the
@@ -415,7 +427,9 @@
 Change configuration of the given providers.
 .Pp
 Additional options include:
-.Bl -tag -width ".Fl b"
+.Bl -tag -width ".Fl h Ar headerfile"
+.It Fl h Ar headerfile
+Handle external metadata
 .It Fl b
 Set the BOOT flag on the given providers.
 For more information, see the description of the
@@ -437,6 +451,8 @@
 .Pp
 Additional options include:
 .Bl -tag -width ".Fl J Ar newpassfile"
+.It Fl h Ar headerfile
+Handle external metadata
 .It Fl i Ar iterations
 Number of iterations to use with PKCS#5v2.
 If 0 is given, PKCS#5v2 will not be used.
@@ -472,7 +488,9 @@
 subcommand.
 .Pp
 Additional options include:
-.Bl -tag -width ".Fl a Ar keyno"
+.Bl -tag -width ".Fl h Ar headerfile"
+.It Fl h Ar headerfile
+Handle external metadata
 .It Fl a
 Destroy all keys (does not need
 .Fl f
@@ -567,7 +585,9 @@
 utility is stored is bad idea.
 .Pp
 Additional options include:
-.Bl -tag -width ".Fl j Ar passfile"
+.Bl -tag -width ".Fl h Ar headerfile"
+.It Fl h Ar headerfile
+Handle external metadata
 .It Fl j Ar passfile
 Specifies a file which contains the passphrase or its part.
 For more information see the description of the
@@ -593,7 +613,9 @@
 provider and the provider size is updated.
 .Pp
 Additional options include:
-.Bl -tag -width ".Fl s Ar oldsize"
+.Bl -tag -width ".Fl h Ar headerfile"
+.It Fl h Ar headerfile
+Handle external metadata
 .It Fl s Ar oldsize
 The size of the provider before it was resized.
 .El
@@ -764,6 +786,9 @@
 # dd if=/dev/random of=/dev/da1s3a bs=1m
 # dd if=/dev/random of=/boot/keys/da1s3a.key bs=128k count=1
 # geli init -b -P -K /boot/keys/da1s3a.key da1s3a
+# dd if=/dev/random of=/dev/ada1 bs=1m
+# dd if=/dev/random of=/boot/keys/ada1.key bs=8 count=8
+# geli init -b -H /boot/hd/ada1.hd -P -K /boot/keys/ada1.key ada1
 .Ed
 .Pp
 The providers are initialized, now we have to add those lines to
@@ -782,6 +807,13 @@
 geli_da1s3a_keyfile0_load="YES"
 geli_da1s3a_keyfile0_type="da1s3a:geli_keyfile0"
 geli_da1s3a_keyfile0_name="/boot/keys/da1s3a.key"
+
+geli_ada1_header_load="YES"
+geli_ada1_header_type="ada1:geli_header"
+geli_ada1_header_name="/boot/hd/ada1.hd"
+geli_ada1_keyfile0_load="YES"
+geli_ada1_keyfile0_type="ada1:geli_keyfile0"
+geli_ada1_keyfile0_name="/boot/keys/ada1.key"
 .Ed
 .Pp
 Not only configure encryption, but also data integrity verification using
--- sys/geom/eli/g_eli_ctl.c.orig	2013-05-04 01:21:45.381136674 +0300
+++ sys/geom/eli/g_eli_ctl.c	2013-05-05 03:20:20.180243224 +0300
@@ -56,10 +56,11 @@
 	struct g_eli_metadata md;
 	struct g_provider *pp;
 	const char *name;
-	u_char *key, mkey[G_ELI_DATAIVKEYLEN];
+	u_char *key, *hd, mkey[G_ELI_DATAIVKEYLEN];
 	int *nargs, *detach, *readonly;
 	int keysize, error;
 	u_int nkey;
+	ssize_t hdsize;
 
 	g_topology_assert();
 
@@ -97,11 +98,18 @@
 		gctl_error(req, "Provider %s is invalid.", name);
 		return;
 	}
-	error = g_eli_read_metadata(mp, pp, &md);
-	if (error != 0) {
-		gctl_error(req, "Cannot read metadata from %s (error=%d).",
-		    name, error);
-		return;
+
+	hd = gctl_get_param(req, "hd", &hdsize);
+
+	if(hdsize == pp->sectorsize) {
+		eli_metadata_decode(hd, &md);
+	} else {
+		error = g_eli_read_metadata(mp, pp, &md);
+		if (error != 0) {
+			gctl_error(req, "Cannot read metadata from %s (error=%d).",
+				name, error);
+			return;
+		}
 	}
 	if (md.md_keys == 0x00) {
 		bzero(&md, sizeof(md));
@@ -448,8 +456,8 @@
 		error = g_eli_read_metadata(mp, pp, &md);
 		if (error != 0) {
 			gctl_error(req,
-			    "Cannot read metadata from %s (error=%d).",
-			    prov, error);
+				"Cannot read metadata from %s (error=%d).",
+				prov, error);
 			continue;
 		}
 
@@ -464,12 +472,13 @@
 		sector = malloc(pp->sectorsize, M_ELI, M_WAITOK | M_ZERO);
 		eli_metadata_encode(&md, sector);
 		error = g_write_data(cp, pp->mediasize - pp->sectorsize, sector,
-		    pp->sectorsize);
+			pp->sectorsize);
 		if (error != 0) {
 			gctl_error(req,
-			    "Cannot store metadata on %s (error=%d).",
-			    prov, error);
+				"Cannot store metadata on %s (error=%d).",
+				prov, error);
 		}
+		
 		bzero(&md, sizeof(md));
 		bzero(sector, sizeof(sector));
 		free(sector, M_ELI);
@@ -815,9 +824,10 @@
 	struct g_provider *pp;
 	struct g_consumer *cp;
 	const char *name;
-	u_char *key, mkey[G_ELI_DATAIVKEYLEN];
+	u_char *key, *hd, mkey[G_ELI_DATAIVKEYLEN];
 	int *nargs, keysize, error;
 	u_int nkey;
+	ssize_t hdsize;
 
 	g_topology_assert();
 
@@ -843,12 +853,20 @@
 	}
 	cp = LIST_FIRST(&sc->sc_geom->consumer);
 	pp = cp->provider;
-	error = g_eli_read_metadata(mp, pp, &md);
-	if (error != 0) {
-		gctl_error(req, "Cannot read metadata from %s (error=%d).",
-		    name, error);
-		return;
+
+	hd = gctl_get_param(req, "hd", &hdsize);
+
+	if(hdsize == pp->sectorsize) {
+		eli_metadata_decode(hd, &md);
+	} else {
+		error = g_eli_read_metadata(mp, pp, &md);
+		if (error != 0) {
+			gctl_error(req, "Cannot read metadata from %s (error=%d).",
+				name, error);
+			return;
+		}
 	}
+
 	if (md.md_keys == 0x00) {
 		bzero(&md, sizeof(md));
 		gctl_error(req, "No valid keys on %s.", pp->name);
--- geli.patch ends here ---


>Release-Note:
>Audit-Trail:
Responsible-Changed-From-To: freebsd-bugs->freebsd-geom 
Responsible-Changed-By: linimon 
Responsible-Changed-When: Mon May 6 00:54:16 UTC 2013 
Responsible-Changed-Why:  
Over to maintainer(s). 

http://www.freebsd.org/cgi/query-pr.cgi?pr=178359 

From: Mark Linimon <linimon@lonesome.com>
To: bug-followup@FreeBSD.org
Cc:  
Subject: Re: kern/178359: [geom] [patch] geom_eli: support external metadata
Date: Mon, 6 May 2013 20:40:23 -0500

 ----- Forwarded message from Scot Hetzel <swhetzel@gmail.com> -----
 
 Date: Sun, 5 May 2013 23:32:13 -0500
 From: Scot Hetzel <swhetzel@gmail.com>
 To: freebsd-bugs@freebsd.org
 Cc: Andrew Romanenko <melanhit@gmail.com>, freebsd-geom@freebsd.org
 Subject: Re: kern/178359: [geom] [patch] geom_eli: support external metadata
 
 My mistake, the -h option was in the geom_eli.c file, but only in the
 comment for the commands.  According to the original commit:
 
 https://svnweb.freebsd.org/base?view=revision&revision=148456
 
 The -h was never in the class_commands for the init method.  Not sure
 what it was there for (maybe to display the usuage?).
 
 > It would be best that init, attach, configure, setkey, delkey, resume,
 > resize, and dump methods use the same option (-h or -H) to specify the
 > headerfile.
 >
 So for now it looks like using -H would be the better option for all
 these methods.
 
 -- 
 DISCLAIMER:
 
 No electrons were maimed while sending this message. Only slightly bruised.
 
 ----- End forwarded message -----

From: Mark Linimon <linimon@lonesome.com>
To: bug-followup@FreeBSD.org
Cc:  
Subject: Re: kern/178359: [geom] [patch] geom_eli: support external metadata
Date: Mon, 6 May 2013 20:39:59 -0500

 ----- Forwarded message from Scot Hetzel <swhetzel@gmail.com> -----
 
 Date: Sun, 5 May 2013 22:51:10 -0500
 From: Scot Hetzel <swhetzel@gmail.com>
 To: freebsd-bugs@freebsd.org
 Cc: Andrew Romanenko <melanhit@gmail.com>, freebsd-geom@freebsd.org
 Subject: Re: kern/178359: [geom] [patch] geom_eli: support external metadata
 
 I had a quick look at the patch and noticed that it is using -H for
 init method, but -h for the attach, configure, setkey, delkey, resume,
 resize, and dump methods.
 
 The patch also shows that it removes the -h option from the init
 method.  I looked at the -Current and release/9.1.0 sources for
 geom_eli.8 and they don't have the -h option.  I suspect it was a
 local modification that was made before this patch was created.
 
 It would be best that init, attach, configure, setkey, delkey, resume,
 resize, and dump methods use the same option (-h or -H) to specify the
 headerfile.
 
 -- 
 DISCLAIMER:
 
 No electrons were maimed while sending this message. Only slightly bruised.
 
 ----- End forwarded message -----

From: Mark Linimon <linimon@lonesome.com>
To: bug-followup@FreeBSD.org
Cc:  
Subject: Re: kern/178359: [geom] [patch] geom_eli: support external metadata
Date: Mon, 6 May 2013 20:40:48 -0500

 ----- Forwarded message from Andrew Romanenko <melanhit@gmail.com> -----
 
 Date: Mon, 06 May 2013 14:29:11 +0300
 From: Andrew Romanenko <melanhit@gmail.com>
 To: Scot Hetzel <swhetzel@gmail.com>
 Cc: freebsd-bugs@freebsd.org, freebsd-geom@freebsd.org
 Subject: Re: kern/178359: [geom] [patch] geom_eli: support external metadata
 User-Agent: Mozilla/5.0 (X11; Linux i686;
 	rv:17.0) Gecko/20130407 Thunderbird/17.0.5
 
 Fixed, using '-H' for all methods
 (updated patch in the attachment)
 
 
 --- sbin/geom/class/eli/geom_eli.c.orig	2013-05-03 00:00:34.551720905 +0300
 +++ sbin/geom/class/eli/geom_eli.c	2013-05-05 23:57:52.631347936 +0300
 @@ -60,7 +60,6 @@
  
  #define	GELI_BACKUP_DIR	"/var/backups/"
  #define	GELI_ENC_ALGO	"aes"
 -
  static void eli_main(struct gctl_req *req, unsigned flags);
  static void eli_init(struct gctl_req *req);
  static void eli_attach(struct gctl_req *req);
 @@ -81,23 +80,23 @@
  /*
   * Available commands:
   *
 - * init [-bhPv] [-a aalgo] [-B backupfile] [-e ealgo] [-i iterations] [-l keylen] [-J newpassfile] [-K newkeyfile] prov
 + * init [-bPv] [-a aalgo] [-B backupfile] [-H headerfile] [-e ealgo] [-i iterations] [-l keylen] [-J newpassfile] [-K newkeyfile] prov
   * label - alias for 'init'
 - * attach [-dprv] [-j passfile] [-k keyfile] prov
 + * attach [-dprv] [-H headerfile] [-j passfile] [-k keyfile] prov
   * detach [-fl] prov ...
   * stop - alias for 'detach'
   * onetime [-d] [-a aalgo] [-e ealgo] [-l keylen] prov
 - * configure [-bB] prov ...
 - * setkey [-pPv] [-n keyno] [-j passfile] [-J newpassfile] [-k keyfile] [-K newkeyfile] prov
 - * delkey [-afv] [-n keyno] prov
 + * configure [-bB] [-H headerfile] prov ...
 + * setkey [-pPv] [-H headerfile] [-n keyno] [-j passfile] [-J newpassfile] [-k keyfile] [-K newkeyfile] prov
 + * delkey [-afv] [-H headerfile] [-n keyno] prov
   * suspend [-v] -a | prov ...
 - * resume [-pv] [-j passfile] [-k keyfile] prov
 + * resume [-pv] [-H headerfile] [-j passfile] [-k keyfile] prov
   * kill [-av] [prov ...]
   * backup [-v] prov file
   * restore [-fv] file prov
 - * resize [-v] -s oldsize prov
 + * resize [-v] [-H headerfile] -s oldsize prov
   * clear [-v] prov ...
 - * dump [-v] prov ...
 + * dump [-v] [-H headerfile] prov ...
   */
  struct g_command class_commands[] = {
  	{ "init", G_FLAG_VERBOSE, eli_main,
 @@ -112,9 +111,10 @@
  		{ 'l', "keylen", "0", G_TYPE_NUMBER },
  		{ 'P', "nonewpassphrase", NULL, G_TYPE_BOOL },
  		{ 's', "sectorsize", "0", G_TYPE_NUMBER },
 +		{ 'H', "header", "", G_TYPE_STRING },
  		G_OPT_SENTINEL
  	    },
 -	    "[-bPv] [-a aalgo] [-B backupfile] [-e ealgo] [-i iterations] [-l keylen] [-J newpassfile] [-K newkeyfile] [-s sectorsize] prov"
 +	    "[-bPv] [-H headerfile] [-a aalgo] [-B backupfile] [-e ealgo] [-i iterations] [-l keylen] [-J newpassfile] [-K newkeyfile] [-s sectorsize] prov"
  	},
  	{ "label", G_FLAG_VERBOSE, eli_main,
  	    {
 @@ -128,6 +128,7 @@
  		{ 'l', "keylen", "0", G_TYPE_NUMBER },
  		{ 'P', "nonewpassphrase", NULL, G_TYPE_BOOL },
  		{ 's', "sectorsize", "0", G_TYPE_NUMBER },
 +		{ 'H', "header", "", G_TYPE_STRING },
  		G_OPT_SENTINEL
  	    },
  	    "- an alias for 'init'"
 @@ -139,9 +140,10 @@
  		{ 'k', "keyfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI },
  		{ 'p', "nopassphrase", NULL, G_TYPE_BOOL },
  		{ 'r', "readonly", NULL, G_TYPE_BOOL },
 +		{ 'H', "header", "", G_TYPE_STRING },
  		G_OPT_SENTINEL
  	    },
 -	    "[-dprv] [-j passfile] [-k keyfile] prov"
 +	    "[-dprv] [-H headerfile] [-j passfile] [-k keyfile] prov"
  	},
  	{ "detach", 0, NULL,
  	    {
 @@ -174,9 +176,10 @@
  	    {
  		{ 'b', "boot", NULL, G_TYPE_BOOL },
  		{ 'B', "noboot", NULL, G_TYPE_BOOL },
 +		{ 'H', "header", "", G_TYPE_STRING },
  		G_OPT_SENTINEL
  	    },
 -	    "[-bB] prov ..."
 +	    "[-bB] [-H headerfile] prov ..."
  	},
  	{ "setkey", G_FLAG_VERBOSE, eli_main,
  	    {
 @@ -188,18 +191,20 @@
  		{ 'n', "keyno", "-1", G_TYPE_NUMBER },
  		{ 'p', "nopassphrase", NULL, G_TYPE_BOOL },
  		{ 'P', "nonewpassphrase", NULL, G_TYPE_BOOL },
 +		{ 'H', "header", "", G_TYPE_STRING },
  		G_OPT_SENTINEL
  	    },
 -	    "[-pPv] [-n keyno] [-i iterations] [-j passfile] [-J newpassfile] [-k keyfile] [-K newkeyfile] prov"
 +	    "[-pPv] [-H headerfile] [-n keyno] [-i iterations] [-j passfile] [-J newpassfile] [-k keyfile] [-K newkeyfile] prov"
  	},
  	{ "delkey", G_FLAG_VERBOSE, eli_main,
  	    {
  		{ 'a', "all", NULL, G_TYPE_BOOL },
  		{ 'f', "force", NULL, G_TYPE_BOOL },
  		{ 'n', "keyno", "-1", G_TYPE_NUMBER },
 +		{ 'H', "header", "", G_TYPE_STRING },
  		G_OPT_SENTINEL
  	    },
 -	    "[-afv] [-n keyno] prov"
 +	    "[-afv] [-H headerfile] [-n keyno] prov"
  	},
  	{ "suspend", G_FLAG_VERBOSE, NULL,
  	    {
 @@ -213,9 +218,10 @@
  		{ 'j', "passfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI },
  		{ 'k', "keyfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI },
  		{ 'p', "nopassphrase", NULL, G_TYPE_BOOL },
 +		{ 'H', "header", "", G_TYPE_STRING },
  		G_OPT_SENTINEL
  	    },
 -	    "[-pv] [-j passfile] [-k keyfile] prov"
 +	    "[-pv] [-H headerfile] [-j passfile] [-k keyfile] prov"
  	},
  	{ "kill", G_FLAG_VERBOSE, eli_main,
  	    {
 @@ -237,15 +243,20 @@
  	{ "resize", G_FLAG_VERBOSE, eli_main,
  	    {
  		{ 's', "oldsize", NULL, G_TYPE_NUMBER },
 +		{ 'H', "header", "", G_TYPE_STRING },
  		G_OPT_SENTINEL
  	    },
 -	    "[-v] -s oldsize prov"
 +	    "[-v] [-H headerfile] -s oldsize prov"
  	},
  	{ "clear", G_FLAG_VERBOSE, eli_main, G_NULL_OPTS,
  	    "[-v] prov ..."
  	},
 -	{ "dump", G_FLAG_VERBOSE, eli_main, G_NULL_OPTS,
 -	    "[-v] prov ..."
 +	{ "dump", G_FLAG_VERBOSE, eli_main,
 +		{
 +		{ 'H', "header", "", G_TYPE_STRING },
 +		G_OPT_SENTINEL
 +		},
 +	    "[-v] [-H headerfile]  prov ..."
  	},
  	G_CMD_SENTINEL
  };
 @@ -653,7 +664,7 @@
  	unsigned char sector[sizeof(struct g_eli_metadata)];
  	unsigned char key[G_ELI_USERKEYLEN];
  	char backfile[MAXPATHLEN];
 -	const char *str, *prov;
 +	const char *str, *prov, *header;
  	unsigned secsize;
  	off_t mediasize;
  	intmax_t val;
 @@ -776,17 +787,39 @@
  		return;
  	}
  
 +	header = gctl_get_ascii(req, "header");
 +
 +	/* Store header if it present */
 +	if(header[0] != '\0') {
 +		error = eli_metadata_store(req, header, &md);
 +
 +		if(error != 0) {
 +			gctl_error(req, "Cannot store header %s: %s.", header,
 +					strerror(error));
 +			return;
 +		}
 +
 +		str = gctl_get_ascii(req, "backupfile");
 +		if(str[0] != '\0')
 +			if(strcmp(str, "none") != 0)
 +				printf("Warning: options -B and -h are mutualy exlusive\n");
 +
 +		return;
 +	}
 +
  	eli_metadata_encode(&md, sector);
  	bzero(&md, sizeof(md));
 +
  	error = g_metadata_store(prov, sector, sizeof(sector));
  	bzero(sector, sizeof(sector));
  	if (error != 0) {
  		gctl_error(req, "Cannot store metadata on %s: %s.", prov,
 -		    strerror(error));
 +			strerror(error));
  		return;
  	}
  	if (verbose)
  		printf("Metadata value stored on %s.\n", prov);
 +
  	/* Backup metadata to a file. */
  	str = gctl_get_ascii(req, "backupfile");
  	if (str[0] != '\0') {
 @@ -820,10 +853,14 @@
  {
  	struct g_eli_metadata md;
  	unsigned char key[G_ELI_USERKEYLEN];
 -	const char *prov;
 +	unsigned char *hd;
 +	const char *prov, *str, *header;
  	off_t mediasize;
 +	ssize_t hdsize;
  	int nargs;
  
 +	hd = NULL;
 +
  	nargs = gctl_get_int(req, "nargs");
  	if (nargs != 1) {
  		gctl_error(req, "Invalid number of arguments.");
 @@ -831,9 +868,16 @@
  	}
  	prov = gctl_get_ascii(req, "arg0");
  
 -	if (eli_metadata_read(req, prov, &md) == -1)
 +	header = gctl_get_ascii(req, "header");
 +	if (header[0] != '\0')
 +		str = header;
 +	else
 +		str = prov;
 +
 +	if (eli_metadata_read(req, str, &md) == -1)
  		return;
  
 +	hdsize = g_get_sectorsize(prov);
  	mediasize = g_get_mediasize(prov);
  	if (md.md_provsize != (uint64_t)mediasize) {
  		gctl_error(req, "Provider size mismatch.");
 @@ -845,20 +889,43 @@
  		return;
  	}
  
 +	if(header[0] != '\0') {
 +		hd = malloc(hdsize);
 +		if(hd == NULL) {
 +			gctl_error(req, "Cannot allocate %zd bytes of memory.", hdsize);
 +			return;
 +		}
 +
 +		bzero(hd, hdsize);
 +		eli_metadata_encode(&md, hd);
 +	} else {
 +		hdsize = sizeof(hd);
 +	}
 +
 +	gctl_ro_param(req, "hd", hdsize, hd);
  	gctl_ro_param(req, "key", sizeof(key), key);
  	if (gctl_issue(req) == NULL) {
  		if (verbose)
  			printf("Attached to %s.\n", prov);
  	}
  	bzero(key, sizeof(key));
 +	if(hd != NULL)
 +		free(hd);
  }
  
  static void
  eli_configure_detached(struct gctl_req *req, const char *prov, bool boot)
  {
  	struct g_eli_metadata md;
 +	const char *str, *header;
  
 -	if (eli_metadata_read(req, prov, &md) == -1)
 +	header = gctl_get_ascii(req, "header");
 +	if (header[0] != '\0')
 +		str = header;
 +	else
 +		str = prov;
 +
 +	if (eli_metadata_read(req, str, &md) == -1)
  		return;
  
  	if (boot && (md.md_flags & G_ELI_FLAG_BOOT)) {
 @@ -872,7 +939,13 @@
  			md.md_flags |= G_ELI_FLAG_BOOT;
  		else
  			md.md_flags &= ~G_ELI_FLAG_BOOT;
 -		eli_metadata_store(req, prov, &md);
 +
 +		if(header[0] != '\0')
 +			str = header;
 +		else
 +			str = prov;
 +
 +		eli_metadata_store(req, str, &md);
  	}
  	bzero(&md, sizeof(md));
  }
 @@ -880,7 +953,7 @@
  static void
  eli_configure(struct gctl_req *req)
  {
 -	const char *prov;
 +	const char *prov, *header;
  	bool boot, noboot;
  	int i, nargs;
  
 @@ -902,14 +975,29 @@
  		return;
  	}
  
 +	header = gctl_get_ascii(req, "header");
 +
  	/* First attached providers. */
 -	gctl_issue(req);
 +	if(header[0] != '\0') {
 +		if(nargs != 1) {
 +			gctl_error(req, "Too many arguments.");
 +			return;
 +		}
 +
 +		prov = gctl_get_ascii(req, "arg0");
 +		eli_configure_detached(req, prov, boot);
 +		return;
 +	} else {
 +		gctl_issue(req);
 +	}
 +
  	/* Now the rest. */
  	for (i = 0; i < nargs; i++) {
  		prov = gctl_get_ascii(req, "arg%d", i);
  		if (!eli_is_attached(prov))
  			eli_configure_detached(req, prov, boot);
  	}
 +
  }
  
  static void
 @@ -950,6 +1038,7 @@
  eli_setkey_detached(struct gctl_req *req, const char *prov,
   struct g_eli_metadata *md)
  {
 +	const char *header, *str;
  	unsigned char key[G_ELI_USERKEYLEN], mkey[G_ELI_DATAIVKEYLEN];
  	unsigned char *mkeydst;
  	unsigned int nkey;
 @@ -1035,7 +1124,14 @@
  	}
  
  	/* Store metadata with fresh key. */
 -	eli_metadata_store(req, prov, md);
 +	header = gctl_get_ascii(req, "header");
 +	if(header[0] != '\0')
 +		str = header;
 +	else
 +		str = prov;
 +
 +	eli_metadata_store(req, str, md);
 +
  	bzero(md, sizeof(*md));
  }
  
 @@ -1043,7 +1139,7 @@
  eli_setkey(struct gctl_req *req)
  {
  	struct g_eli_metadata md;
 -	const char *prov;
 +	const char *prov, *header, *str;
  	int nargs;
  
  	nargs = gctl_get_int(req, "nargs");
 @@ -1053,10 +1149,16 @@
  	}
  	prov = gctl_get_ascii(req, "arg0");
  
 -	if (eli_metadata_read(req, prov, &md) == -1)
 +	header = gctl_get_ascii(req, "header");
 +	if(header[0] != '\0')
 +		str = header;
 +	else
 +		str = prov;
 +
 +	if (eli_metadata_read(req, str, &md) == -1)
  		return;
  
 -	if (eli_is_attached(prov))
 +	if (eli_is_attached(prov) && header[0] == '\0')
  		eli_setkey_attached(req, &md);
  	else
  		eli_setkey_detached(req, prov, &md);
 @@ -1079,12 +1181,19 @@
  eli_delkey_detached(struct gctl_req *req, const char *prov)
  {
  	struct g_eli_metadata md;
 +	const char *header, *str;
  	unsigned char *mkeydst;
  	unsigned int nkey;
  	intmax_t val;
  	bool all, force;
  
 -	if (eli_metadata_read(req, prov, &md) == -1)
 +	header = gctl_get_ascii(req, "header");
 +	if(header[0] != '\0')
 +		str = header;
 +	else
 +		str = prov;
 +
 +	if (eli_metadata_read(req, str, &md) == -1)
  		return;
  
  	all = gctl_get_int(req, "all");
 @@ -1116,6 +1225,11 @@
  		arc4rand(mkeydst, G_ELI_MKEYLEN);
  	}
  
 +	if(header[0] != '\0')
 +		str = header;
 +	else
 +		str = prov;
 +
  	eli_metadata_store(req, prov, &md);
  	bzero(&md, sizeof(md));
  }
 @@ -1123,7 +1237,7 @@
  static void
  eli_delkey(struct gctl_req *req)
  {
 -	const char *prov;
 +	const char *prov, *header;
  	int nargs;
  
  	nargs = gctl_get_int(req, "nargs");
 @@ -1133,7 +1247,9 @@
  	}
  	prov = gctl_get_ascii(req, "arg0");
  
 -	if (eli_is_attached(prov))
 +	header = gctl_get_ascii(req, "header");
 +
 +	if (eli_is_attached(prov) && header[0] == '\0')
  		eli_delkey_attached(req, prov);
  	else
  		eli_delkey_detached(req, prov);
 @@ -1144,10 +1260,14 @@
  {
  	struct g_eli_metadata md;
  	unsigned char key[G_ELI_USERKEYLEN];
 -	const char *prov;
 +	unsigned char *hd;
 +	const char *prov, *str, *header;
  	off_t mediasize;
 +	ssize_t hdsize;
  	int nargs;
  
 +	hd = NULL;
 +
  	nargs = gctl_get_int(req, "nargs");
  	if (nargs != 1) {
  		gctl_error(req, "Invalid number of arguments.");
 @@ -1155,10 +1275,18 @@
  	}
  	prov = gctl_get_ascii(req, "arg0");
  
 -	if (eli_metadata_read(req, prov, &md) == -1)
 +	header = gctl_get_ascii(req, "header");
 +	if (header[0] != '\0')
 +		str = header;
 +	else
 +		str = prov;
 +
 +	if (eli_metadata_read(req, str, &md) == -1)
  		return;
  
  	mediasize = g_get_mediasize(prov);
 +	hdsize = g_get_sectorsize(prov);
 +
  	if (md.md_provsize != (uint64_t)mediasize) {
  		gctl_error(req, "Provider size mismatch.");
  		return;
 @@ -1169,12 +1297,30 @@
  		return;
  	}
  
 +	if(header[0] != '\0') {
 +		hd = malloc(hdsize);
 +		if(hd == NULL) {
 +			gctl_error(req, "Cannot allocate %zd bytes of memory.", hdsize);
 +			return;
 +		}
 +
 +		bzero(hd, hdsize);
 +		eli_metadata_encode(&md, hd);
 +	} else {
 +		hdsize = sizeof(hd);
 +	}
 +
 +	gctl_ro_param(req, "hd", hdsize, hd);
  	gctl_ro_param(req, "key", sizeof(key), key);
 +
  	if (gctl_issue(req) == NULL) {
  		if (verbose)
  			printf("Resumed %s.\n", prov);
  	}
  	bzero(key, sizeof(key));
 +
 +	if(hd != NULL)
 +		free(hd);
  }
  
  static int
 @@ -1469,11 +1615,11 @@
  eli_resize(struct gctl_req *req)
  {
  	struct g_eli_metadata md;
 -	const char *prov;
 +	const char *prov, *header;
  	unsigned char *sector;
  	ssize_t secsize;
  	off_t mediasize, oldsize;
 -	int nargs, provfd;
 +	int nargs, provfd, error;
  
  	nargs = gctl_get_int(req, "nargs");
  	if (nargs != 1) {
 @@ -1486,14 +1632,18 @@
  	sector = NULL;
  	secsize = 0;
  
 -	provfd = g_open(prov, 1);
 -	if (provfd == -1) {
 -		gctl_error(req, "Cannot open %s: %s.", prov, strerror(errno));
 -		goto out;
 +	header = gctl_get_ascii(req, "header");
 +
 +	if(header[0] == '\0') {
 +		provfd = g_open(prov, 1);
 +		if (provfd == -1) {
 +			gctl_error(req, "Cannot open %s: %s.", prov, strerror(errno));
 +			goto out;
 +		}
  	}
  
 -	mediasize = g_mediasize(provfd);
 -	secsize = g_sectorsize(provfd);
 +	mediasize = g_get_mediasize(prov);
 +	secsize = g_get_sectorsize(prov);
  	if (mediasize == -1 || secsize == -1) {
  		gctl_error(req, "Cannot get information about %s: %s.", prov,
  		    strerror(errno));
 @@ -1517,16 +1667,24 @@
  	}
  
  	/* Read metadata from the 'oldsize' offset. */
 -	if (pread(provfd, sector, secsize, oldsize - secsize) != secsize) {
 -		gctl_error(req, "Cannot read old metadata: %s.",
 -		    strerror(errno));
 -		goto out;
 -	}
 +	if(header[0] != '\0') {
 +		if (eli_metadata_read(req, header, &md) == -1) {
 +			gctl_error(req, "Cannot read old metadata: %s.",
 +					header);
 +			goto out;
 +		}
 +	} else {
 +		if (pread(provfd, sector, secsize, oldsize - secsize) != secsize) {
 +			gctl_error(req, "Cannot read old metadata: %s.",
 +				strerror(errno));
 +			goto out;
 +		}
  
 -	/* Check if this sector contains geli metadata. */
 -	if (eli_metadata_decode(sector, &md) != 0) {
 -		gctl_error(req, "MD5 hash mismatch: no metadata for oldsize.");
 -		goto out;
 +		/* Check if this sector contains geli metadata. */
 +		if (eli_metadata_decode(sector, &md) != 0) {
 +			gctl_error(req, "MD5 hash mismatch: no metadata for oldsize.");
 +			goto out;
 +		}
  	}
  
  	/*
 @@ -1544,15 +1702,25 @@
  	 */
  	md.md_provsize = mediasize;
  	eli_metadata_encode(&md, sector);
 -	if (pwrite(provfd, sector, secsize, mediasize - secsize) != secsize) {
 -		gctl_error(req, "Cannot write metadata: %s.", strerror(errno));
 -		goto out;
 -	}
 -	(void)g_flush(provfd);
  
 -	/* Now trash the old metadata. */
 -	if (eli_trash_metadata(req, prov, provfd, oldsize - secsize) == -1)
 -		goto out;
 +	if(header[0] != '\0') {
 +		error = eli_metadata_store(req, header, &md);
 +		if(error != 0) {
 +			gctl_error(req, "Cannot store header %s: %s.", header,
 +					strerror(error));
 +			goto out;
 +		}
 +	} else {
 +		if (pwrite(provfd, sector, secsize, mediasize - secsize) != secsize) {
 +			gctl_error(req, "Cannot write metadata: %s.", strerror(errno));
 +			goto out;
 +		}
 +		(void)g_flush(provfd);
 +
 +		/* Now trash the old metadata. */
 +		if (eli_trash_metadata(req, prov, provfd, oldsize - secsize) == -1)
 +			goto out;
 +	}
  out:
  	if (provfd >= 0)
  		(void)g_close(provfd);
 @@ -1592,7 +1760,7 @@
  eli_dump(struct gctl_req *req)
  {
  	struct g_eli_metadata md, tmpmd;
 -	const char *name;
 +	const char *name, *header;
  	int error, i, nargs;
  
  	nargs = gctl_get_int(req, "nargs");
 @@ -1601,15 +1769,40 @@
  		return;
  	}
  
 -	for (i = 0; i < nargs; i++) {
 -		name = gctl_get_ascii(req, "arg%d", i);
 -		error = g_metadata_read(name, (unsigned char *)&tmpmd,
 -		    sizeof(tmpmd), G_ELI_MAGIC);
 -		if (error != 0) {
 +	header = gctl_get_ascii(req, "header");
 +	if(header[0] != '\0') {
 +		if(nargs != 1) {
 +			gctl_error(req, "Too many arguments.");
 +			return;
 +		}
 +
 +		if (eli_metadata_read(req, header, &tmpmd) == -1)
 +			return;
 +
 +		name = gctl_get_ascii(req, "arg0");
 +
 +		if (strcmp(tmpmd.md_magic, G_ELI_MAGIC) != 0) {
 +			error = EINVAL;
  			fprintf(stderr, "Cannot read metadata from %s: %s.\n",
 -			    name, strerror(error));
 +					name, strerror(error));
  			gctl_error(req, "Not fully done.");
 -			continue;
 +			return;
 +		}
 +
 +		name = header;
 +	}
 +
 +	for (i = 0; i < nargs; i++) {
 +		if(header[0] == '\0') {
 +			name = gctl_get_ascii(req, "arg%d", i);
 +			error = g_metadata_read(name, (unsigned char *)&tmpmd,
 +				sizeof(tmpmd), G_ELI_MAGIC);
 +			if (error != 0) {
 +				fprintf(stderr, "Cannot read metadata from %s: %s.\n",
 +					name, strerror(error));
 +				gctl_error(req, "Not fully done.");
 +				continue;
 +			}
  		}
  		if (eli_metadata_decode((unsigned char *)&tmpmd, &md) != 0) {
  			fprintf(stderr, "MD5 hash mismatch for %s, skipping.\n",
 --- sbin/geom/class/eli/geli.8.orig	2013-04-29 01:45:56.000000000 +0300
 +++ sbin/geom/class/eli/geli.8	2013-05-05 16:49:57.642188841 +0300
 @@ -52,6 +52,7 @@
  .Nm
  .Cm init
  .Op Fl bPv
 +.Op Fl H Ar headerfile
  .Op Fl a Ar aalgo
  .Op Fl B Ar backupfile
  .Op Fl e Ar ealgo
 @@ -67,6 +68,7 @@
  .Nm
  .Cm attach
  .Op Fl dprv
 +.Op Fl H Ar headerfile
  .Op Fl j Ar passfile
  .Op Fl k Ar keyfile
  .Ar prov
 @@ -88,10 +90,12 @@
  .Nm
  .Cm configure
  .Op Fl bB
 +.Op Fl H Ar headerfile
  .Ar prov ...
  .Nm
  .Cm setkey
  .Op Fl pPv
 +.Op Fl H Ar headerfile
  .Op Fl i Ar iterations
  .Op Fl j Ar passfile
  .Op Fl J Ar newpassfile
 @@ -102,6 +106,7 @@
  .Nm
  .Cm delkey
  .Op Fl afv
 +.Op Fl H Ar headerfile
  .Op Fl n Ar keyno
  .Ar prov
  .Nm
 @@ -125,12 +130,14 @@
  .Nm
  .Cm resume
  .Op Fl pv
 +.Op Fl H Ar headerfile
  .Op Fl j Ar passfile
  .Op Fl k Ar keyfile
  .Ar prov
  .Nm
  .Cm resize
  .Op Fl v
 +.Op Fl H Ar headerfile
  .Fl s Ar oldsize
  .Ar prov
  .Nm
 @@ -140,6 +147,7 @@
  .Nm
  .Cm dump
  .Op Fl v
 +.Op Fl H Ar headerfile
  .Ar prov ...
  .Nm
  .Cm list
 @@ -240,6 +248,8 @@
  .Pp
  Additional options include:
  .Bl -tag -width ".Fl J Ar newpassfile"
 +.It Fl H Ar headerfile
 +Store GELI metadata (header) in the external file
  .It Fl a Ar aalgo
  Enable data integrity verification (authentication) using the given algorithm.
  This will reduce size of available storage and also reduce speed.
 @@ -341,6 +351,8 @@
  option for the
  .Cm detach
  subcommand.
 +.It Fl H Ar headerfile
 +Read metadata from a file instead from a provider
  .It Fl j Ar passfile
  Specifies a file which contains the passphrase or its part.
  For more information see the description of the
 @@ -415,7 +427,9 @@
  Change configuration of the given providers.
  .Pp
  Additional options include:
 -.Bl -tag -width ".Fl b"
 +.Bl -tag -width ".Fl H Ar headerfile"
 +.It Fl H Ar headerfile
 +Handle external metadata
  .It Fl b
  Set the BOOT flag on the given providers.
  For more information, see the description of the
 @@ -437,6 +451,8 @@
  .Pp
  Additional options include:
  .Bl -tag -width ".Fl J Ar newpassfile"
 +.It Fl H Ar headerfile
 +Handle external metadata
  .It Fl i Ar iterations
  Number of iterations to use with PKCS#5v2.
  If 0 is given, PKCS#5v2 will not be used.
 @@ -472,7 +488,9 @@
  subcommand.
  .Pp
  Additional options include:
 -.Bl -tag -width ".Fl a Ar keyno"
 +.Bl -tag -width ".Fl H Ar headerfile"
 +.It Fl H Ar headerfile
 +Handle external metadata
  .It Fl a
  Destroy all keys (does not need
  .Fl f
 @@ -567,7 +585,9 @@
  utility is stored is bad idea.
  .Pp
  Additional options include:
 -.Bl -tag -width ".Fl j Ar passfile"
 +.Bl -tag -width ".Fl H Ar headerfile"
 +.It Fl H Ar headerfile
 +Handle external metadata
  .It Fl j Ar passfile
  Specifies a file which contains the passphrase or its part.
  For more information see the description of the
 @@ -593,7 +613,9 @@
  provider and the provider size is updated.
  .Pp
  Additional options include:
 -.Bl -tag -width ".Fl s Ar oldsize"
 +.Bl -tag -width ".Fl H Ar headerfile"
 +.It Fl H Ar headerfile
 +Handle external metadata
  .It Fl s Ar oldsize
  The size of the provider before it was resized.
  .El
 @@ -764,6 +786,9 @@
  # dd if=/dev/random of=/dev/da1s3a bs=1m
  # dd if=/dev/random of=/boot/keys/da1s3a.key bs=128k count=1
  # geli init -b -P -K /boot/keys/da1s3a.key da1s3a
 +# dd if=/dev/random of=/dev/ada1 bs=1m
 +# dd if=/dev/random of=/boot/keys/ada1.key bs=8 count=8
 +# geli init -b -H /boot/hd/ada1.hd -P -K /boot/keys/ada1.key ada1
  .Ed
  .Pp
  The providers are initialized, now we have to add those lines to
 @@ -782,6 +807,13 @@
  geli_da1s3a_keyfile0_load="YES"
  geli_da1s3a_keyfile0_type="da1s3a:geli_keyfile0"
  geli_da1s3a_keyfile0_name="/boot/keys/da1s3a.key"
 +
 +geli_ada1_header_load="YES"
 +geli_ada1_header_type="ada1:geli_header"
 +geli_ada1_header_name="/boot/hd/ada1.hd"
 +geli_ada1_keyfile0_load="YES"
 +geli_ada1_keyfile0_type="ada1:geli_keyfile0"
 +geli_ada1_keyfile0_name="/boot/keys/ada1.key"
  .Ed
  .Pp
  Not only configure encryption, but also data integrity verification using
 --- sys/geom/eli/g_eli_ctl.c.orig	2013-05-04 01:21:45.381136674 +0300
 +++ sys/geom/eli/g_eli_ctl.c	2013-05-05 03:20:20.180243224 +0300
 @@ -56,10 +56,11 @@
  	struct g_eli_metadata md;
  	struct g_provider *pp;
  	const char *name;
 -	u_char *key, mkey[G_ELI_DATAIVKEYLEN];
 +	u_char *key, *hd, mkey[G_ELI_DATAIVKEYLEN];
  	int *nargs, *detach, *readonly;
  	int keysize, error;
  	u_int nkey;
 +	ssize_t hdsize;
  
  	g_topology_assert();
  
 @@ -97,11 +98,18 @@
  		gctl_error(req, "Provider %s is invalid.", name);
  		return;
  	}
 -	error = g_eli_read_metadata(mp, pp, &md);
 -	if (error != 0) {
 -		gctl_error(req, "Cannot read metadata from %s (error=%d).",
 -		    name, error);
 -		return;
 +
 +	hd = gctl_get_param(req, "hd", &hdsize);
 +
 +	if(hdsize == pp->sectorsize) {
 +		eli_metadata_decode(hd, &md);
 +	} else {
 +		error = g_eli_read_metadata(mp, pp, &md);
 +		if (error != 0) {
 +			gctl_error(req, "Cannot read metadata from %s (error=%d).",
 +				name, error);
 +			return;
 +		}
  	}
  	if (md.md_keys == 0x00) {
  		bzero(&md, sizeof(md));
 @@ -448,8 +456,8 @@
  		error = g_eli_read_metadata(mp, pp, &md);
  		if (error != 0) {
  			gctl_error(req,
 -			    "Cannot read metadata from %s (error=%d).",
 -			    prov, error);
 +				"Cannot read metadata from %s (error=%d).",
 +				prov, error);
  			continue;
  		}
  
 @@ -464,12 +472,13 @@
  		sector = malloc(pp->sectorsize, M_ELI, M_WAITOK | M_ZERO);
  		eli_metadata_encode(&md, sector);
  		error = g_write_data(cp, pp->mediasize - pp->sectorsize, sector,
 -		    pp->sectorsize);
 +			pp->sectorsize);
  		if (error != 0) {
  			gctl_error(req,
 -			    "Cannot store metadata on %s (error=%d).",
 -			    prov, error);
 +				"Cannot store metadata on %s (error=%d).",
 +				prov, error);
  		}
 +		
  		bzero(&md, sizeof(md));
  		bzero(sector, sizeof(sector));
  		free(sector, M_ELI);
 @@ -815,9 +824,10 @@
  	struct g_provider *pp;
  	struct g_consumer *cp;
  	const char *name;
 -	u_char *key, mkey[G_ELI_DATAIVKEYLEN];
 +	u_char *key, *hd, mkey[G_ELI_DATAIVKEYLEN];
  	int *nargs, keysize, error;
  	u_int nkey;
 +	ssize_t hdsize;
  
  	g_topology_assert();
  
 @@ -843,12 +853,20 @@
  	}
  	cp = LIST_FIRST(&sc->sc_geom->consumer);
  	pp = cp->provider;
 -	error = g_eli_read_metadata(mp, pp, &md);
 -	if (error != 0) {
 -		gctl_error(req, "Cannot read metadata from %s (error=%d).",
 -		    name, error);
 -		return;
 +
 +	hd = gctl_get_param(req, "hd", &hdsize);
 +
 +	if(hdsize == pp->sectorsize) {
 +		eli_metadata_decode(hd, &md);
 +	} else {
 +		error = g_eli_read_metadata(mp, pp, &md);
 +		if (error != 0) {
 +			gctl_error(req, "Cannot read metadata from %s (error=%d).",
 +				name, error);
 +			return;
 +		}
  	}
 +
  	if (md.md_keys == 0x00) {
  		bzero(&md, sizeof(md));
  		gctl_error(req, "No valid keys on %s.", pp->name);
 
 ----- End forwarded message -----

From: Mark Linimon <linimon@lonesome.com>
To: bug-followup@FreeBSD.org
Cc:  
Subject: Re: kern/178359: [geom] [patch] geom_eli: support external metadata
Date: Wed, 8 May 2013 16:35:55 -0500

 ----- Forwarded message from Andrew Romanenko <melanhit@gmail.com> -----
 
 Date: Tue, 07 May 2013 10:26:07 +0300
 From: Andrew Romanenko <melanhit@gmail.com>
 To: Scot Hetzel <swhetzel@gmail.com>
 Cc: freebsd-bugs@freebsd.org, freebsd-geom@freebsd.org
 Subject: Re: kern/178359: [geom] [patch] geom_eli: support external metadata
 User-Agent: Mozilla/5.0 (X11; Linux i686;
 	rv:17.0) Gecko/20130407 Thunderbird/17.0.5
 
 On 05/06/2013 07:32 AM, Scot Hetzel wrote:
 > On Sun, May 5, 2013 at 10:51 PM, Scot Hetzel <swhetzel@gmail.com> wrote:
 >> On Sun, May 5, 2013 at 7:54 PM,  <linimon@freebsd.org> wrote:
 >>> Old Synopsis: geom_eli: support external metadata
 >>> New Synopsis: [geom] [patch] geom_eli: support external metadata
 >>>
 >>> Responsible-Changed-From-To: freebsd-bugs->freebsd-geom
 >>> Responsible-Changed-By: linimon
 >>> Responsible-Changed-When: Mon May 6 00:54:16 UTC 2013
 >>> Responsible-Changed-Why:
 >>> Over to maintainer(s).
 >>>
 >>> http://www.freebsd.org/cgi/query-pr.cgi?pr=178359
 >> I had a quick look at the patch and noticed that it is using -H for
 >> init method, but -h for the attach, configure, setkey, delkey, resume,
 >> resize, and dump methods.
 >>
 >> The patch also shows that it removes the -h option from the init
 >> method.  I looked at the -Current and release/9.1.0 sources for
 >> geom_eli.8 and they don't have the -h option.  I suspect it was a
 >> local modification that was made before this patch was created.
 >>
 > My mistake, the -h option was in the geom_eli.c file, but only in the
 > comment for the commands.  According to the original commit:
 >
 > https://svnweb.freebsd.org/base?view=revision&revision=148456
 >
 > The -h was never in the class_commands for the init method.  Not sure
 > what it was there for (maybe to display the usuage?).
 >
 >> It would be best that init, attach, configure, setkey, delkey, resume,
 >> resize, and dump methods use the same option (-h or -H) to specify the
 >> headerfile.
 >>
 > So for now it looks like using -H would be the better option for all
 > these methods.
 >
 I forgot to include patch sys/geom/eli/g_eli.c (sorry, it's my mistake).
 Finished and full version of the patch in an attachment
 
 --- sbin/geom/class/eli/geom_eli.c.orig	2013-05-03 00:00:34.551720905 +0300
 +++ sbin/geom/class/eli/geom_eli.c	2013-05-05 23:57:52.631347936 +0300
 @@ -60,7 +60,6 @@
  
  #define	GELI_BACKUP_DIR	"/var/backups/"
  #define	GELI_ENC_ALGO	"aes"
 -
  static void eli_main(struct gctl_req *req, unsigned flags);
  static void eli_init(struct gctl_req *req);
  static void eli_attach(struct gctl_req *req);
 @@ -81,23 +80,23 @@
  /*
   * Available commands:
   *
 - * init [-bhPv] [-a aalgo] [-B backupfile] [-e ealgo] [-i iterations] [-l keylen] [-J newpassfile] [-K newkeyfile] prov
 + * init [-bPv] [-a aalgo] [-B backupfile] [-H headerfile] [-e ealgo] [-i iterations] [-l keylen] [-J newpassfile] [-K newkeyfile] prov
   * label - alias for 'init'
 - * attach [-dprv] [-j passfile] [-k keyfile] prov
 + * attach [-dprv] [-H headerfile] [-j passfile] [-k keyfile] prov
   * detach [-fl] prov ...
   * stop - alias for 'detach'
   * onetime [-d] [-a aalgo] [-e ealgo] [-l keylen] prov
 - * configure [-bB] prov ...
 - * setkey [-pPv] [-n keyno] [-j passfile] [-J newpassfile] [-k keyfile] [-K newkeyfile] prov
 - * delkey [-afv] [-n keyno] prov
 + * configure [-bB] [-H headerfile] prov ...
 + * setkey [-pPv] [-H headerfile] [-n keyno] [-j passfile] [-J newpassfile] [-k keyfile] [-K newkeyfile] prov
 + * delkey [-afv] [-H headerfile] [-n keyno] prov
   * suspend [-v] -a | prov ...
 - * resume [-pv] [-j passfile] [-k keyfile] prov
 + * resume [-pv] [-H headerfile] [-j passfile] [-k keyfile] prov
   * kill [-av] [prov ...]
   * backup [-v] prov file
   * restore [-fv] file prov
 - * resize [-v] -s oldsize prov
 + * resize [-v] [-H headerfile] -s oldsize prov
   * clear [-v] prov ...
 - * dump [-v] prov ...
 + * dump [-v] [-H headerfile] prov ...
   */
  struct g_command class_commands[] = {
  	{ "init", G_FLAG_VERBOSE, eli_main,
 @@ -112,9 +111,10 @@
  		{ 'l', "keylen", "0", G_TYPE_NUMBER },
  		{ 'P', "nonewpassphrase", NULL, G_TYPE_BOOL },
  		{ 's', "sectorsize", "0", G_TYPE_NUMBER },
 +		{ 'H', "header", "", G_TYPE_STRING },
  		G_OPT_SENTINEL
  	    },
 -	    "[-bPv] [-a aalgo] [-B backupfile] [-e ealgo] [-i iterations] [-l keylen] [-J newpassfile] [-K newkeyfile] [-s sectorsize] prov"
 +	    "[-bPv] [-H headerfile] [-a aalgo] [-B backupfile] [-e ealgo] [-i iterations] [-l keylen] [-J newpassfile] [-K newkeyfile] [-s sectorsize] prov"
  	},
  	{ "label", G_FLAG_VERBOSE, eli_main,
  	    {
 @@ -128,6 +128,7 @@
  		{ 'l', "keylen", "0", G_TYPE_NUMBER },
  		{ 'P', "nonewpassphrase", NULL, G_TYPE_BOOL },
  		{ 's', "sectorsize", "0", G_TYPE_NUMBER },
 +		{ 'H', "header", "", G_TYPE_STRING },
  		G_OPT_SENTINEL
  	    },
  	    "- an alias for 'init'"
 @@ -139,9 +140,10 @@
  		{ 'k', "keyfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI },
  		{ 'p', "nopassphrase", NULL, G_TYPE_BOOL },
  		{ 'r', "readonly", NULL, G_TYPE_BOOL },
 +		{ 'H', "header", "", G_TYPE_STRING },
  		G_OPT_SENTINEL
  	    },
 -	    "[-dprv] [-j passfile] [-k keyfile] prov"
 +	    "[-dprv] [-H headerfile] [-j passfile] [-k keyfile] prov"
  	},
  	{ "detach", 0, NULL,
  	    {
 @@ -174,9 +176,10 @@
  	    {
  		{ 'b', "boot", NULL, G_TYPE_BOOL },
  		{ 'B', "noboot", NULL, G_TYPE_BOOL },
 +		{ 'H', "header", "", G_TYPE_STRING },
  		G_OPT_SENTINEL
  	    },
 -	    "[-bB] prov ..."
 +	    "[-bB] [-H headerfile] prov ..."
  	},
  	{ "setkey", G_FLAG_VERBOSE, eli_main,
  	    {
 @@ -188,18 +191,20 @@
  		{ 'n', "keyno", "-1", G_TYPE_NUMBER },
  		{ 'p', "nopassphrase", NULL, G_TYPE_BOOL },
  		{ 'P', "nonewpassphrase", NULL, G_TYPE_BOOL },
 +		{ 'H', "header", "", G_TYPE_STRING },
  		G_OPT_SENTINEL
  	    },
 -	    "[-pPv] [-n keyno] [-i iterations] [-j passfile] [-J newpassfile] [-k keyfile] [-K newkeyfile] prov"
 +	    "[-pPv] [-H headerfile] [-n keyno] [-i iterations] [-j passfile] [-J newpassfile] [-k keyfile] [-K newkeyfile] prov"
  	},
  	{ "delkey", G_FLAG_VERBOSE, eli_main,
  	    {
  		{ 'a', "all", NULL, G_TYPE_BOOL },
  		{ 'f', "force", NULL, G_TYPE_BOOL },
  		{ 'n', "keyno", "-1", G_TYPE_NUMBER },
 +		{ 'H', "header", "", G_TYPE_STRING },
  		G_OPT_SENTINEL
  	    },
 -	    "[-afv] [-n keyno] prov"
 +	    "[-afv] [-H headerfile] [-n keyno] prov"
  	},
  	{ "suspend", G_FLAG_VERBOSE, NULL,
  	    {
 @@ -213,9 +218,10 @@
  		{ 'j', "passfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI },
  		{ 'k', "keyfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI },
  		{ 'p', "nopassphrase", NULL, G_TYPE_BOOL },
 +		{ 'H', "header", "", G_TYPE_STRING },
  		G_OPT_SENTINEL
  	    },
 -	    "[-pv] [-j passfile] [-k keyfile] prov"
 +	    "[-pv] [-H headerfile] [-j passfile] [-k keyfile] prov"
  	},
  	{ "kill", G_FLAG_VERBOSE, eli_main,
  	    {
 @@ -237,15 +243,20 @@
  	{ "resize", G_FLAG_VERBOSE, eli_main,
  	    {
  		{ 's', "oldsize", NULL, G_TYPE_NUMBER },
 +		{ 'H', "header", "", G_TYPE_STRING },
  		G_OPT_SENTINEL
  	    },
 -	    "[-v] -s oldsize prov"
 +	    "[-v] [-H headerfile] -s oldsize prov"
  	},
  	{ "clear", G_FLAG_VERBOSE, eli_main, G_NULL_OPTS,
  	    "[-v] prov ..."
  	},
 -	{ "dump", G_FLAG_VERBOSE, eli_main, G_NULL_OPTS,
 -	    "[-v] prov ..."
 +	{ "dump", G_FLAG_VERBOSE, eli_main,
 +		{
 +		{ 'H', "header", "", G_TYPE_STRING },
 +		G_OPT_SENTINEL
 +		},
 +	    "[-v] [-H headerfile]  prov ..."
  	},
  	G_CMD_SENTINEL
  };
 @@ -653,7 +664,7 @@
  	unsigned char sector[sizeof(struct g_eli_metadata)];
  	unsigned char key[G_ELI_USERKEYLEN];
  	char backfile[MAXPATHLEN];
 -	const char *str, *prov;
 +	const char *str, *prov, *header;
  	unsigned secsize;
  	off_t mediasize;
  	intmax_t val;
 @@ -776,17 +787,39 @@
  		return;
  	}
  
 +	header = gctl_get_ascii(req, "header");
 +
 +	/* Store header if it present */
 +	if(header[0] != '\0') {
 +		error = eli_metadata_store(req, header, &md);
 +
 +		if(error != 0) {
 +			gctl_error(req, "Cannot store header %s: %s.", header,
 +					strerror(error));
 +			return;
 +		}
 +
 +		str = gctl_get_ascii(req, "backupfile");
 +		if(str[0] != '\0')
 +			if(strcmp(str, "none") != 0)
 +				printf("Warning: options -B and -H are mutualy exlusive\n");
 +
 +		return;
 +	}
 +
  	eli_metadata_encode(&md, sector);
  	bzero(&md, sizeof(md));
 +
  	error = g_metadata_store(prov, sector, sizeof(sector));
  	bzero(sector, sizeof(sector));
  	if (error != 0) {
  		gctl_error(req, "Cannot store metadata on %s: %s.", prov,
 -		    strerror(error));
 +			strerror(error));
  		return;
  	}
  	if (verbose)
  		printf("Metadata value stored on %s.\n", prov);
 +
  	/* Backup metadata to a file. */
  	str = gctl_get_ascii(req, "backupfile");
  	if (str[0] != '\0') {
 @@ -820,10 +853,14 @@
  {
  	struct g_eli_metadata md;
  	unsigned char key[G_ELI_USERKEYLEN];
 -	const char *prov;
 +	unsigned char *hd;
 +	const char *prov, *str, *header;
  	off_t mediasize;
 +	ssize_t hdsize;
  	int nargs;
  
 +	hd = NULL;
 +
  	nargs = gctl_get_int(req, "nargs");
  	if (nargs != 1) {
  		gctl_error(req, "Invalid number of arguments.");
 @@ -831,9 +868,16 @@
  	}
  	prov = gctl_get_ascii(req, "arg0");
  
 -	if (eli_metadata_read(req, prov, &md) == -1)
 +	header = gctl_get_ascii(req, "header");
 +	if (header[0] != '\0')
 +		str = header;
 +	else
 +		str = prov;
 +
 +	if (eli_metadata_read(req, str, &md) == -1)
  		return;
  
 +	hdsize = g_get_sectorsize(prov);
  	mediasize = g_get_mediasize(prov);
  	if (md.md_provsize != (uint64_t)mediasize) {
  		gctl_error(req, "Provider size mismatch.");
 @@ -845,20 +889,43 @@
  		return;
  	}
  
 +	if(header[0] != '\0') {
 +		hd = malloc(hdsize);
 +		if(hd == NULL) {
 +			gctl_error(req, "Cannot allocate %zd bytes of memory.", hdsize);
 +			return;
 +		}
 +
 +		bzero(hd, hdsize);
 +		eli_metadata_encode(&md, hd);
 +	} else {
 +		hdsize = sizeof(hd);
 +	}
 +
 +	gctl_ro_param(req, "hd", hdsize, hd);
  	gctl_ro_param(req, "key", sizeof(key), key);
  	if (gctl_issue(req) == NULL) {
  		if (verbose)
  			printf("Attached to %s.\n", prov);
  	}
  	bzero(key, sizeof(key));
 +	if(hd != NULL)
 +		free(hd);
  }
  
  static void
  eli_configure_detached(struct gctl_req *req, const char *prov, bool boot)
  {
  	struct g_eli_metadata md;
 +	const char *str, *header;
  
 -	if (eli_metadata_read(req, prov, &md) == -1)
 +	header = gctl_get_ascii(req, "header");
 +	if (header[0] != '\0')
 +		str = header;
 +	else
 +		str = prov;
 +
 +	if (eli_metadata_read(req, str, &md) == -1)
  		return;
  
  	if (boot && (md.md_flags & G_ELI_FLAG_BOOT)) {
 @@ -872,7 +939,13 @@
  			md.md_flags |= G_ELI_FLAG_BOOT;
  		else
  			md.md_flags &= ~G_ELI_FLAG_BOOT;
 -		eli_metadata_store(req, prov, &md);
 +
 +		if(header[0] != '\0')
 +			str = header;
 +		else
 +			str = prov;
 +
 +		eli_metadata_store(req, str, &md);
  	}
  	bzero(&md, sizeof(md));
  }
 @@ -880,7 +953,7 @@
  static void
  eli_configure(struct gctl_req *req)
  {
 -	const char *prov;
 +	const char *prov, *header;
  	bool boot, noboot;
  	int i, nargs;
  
 @@ -902,14 +975,29 @@
  		return;
  	}
  
 +	header = gctl_get_ascii(req, "header");
 +
  	/* First attached providers. */
 -	gctl_issue(req);
 +	if(header[0] != '\0') {
 +		if(nargs != 1) {
 +			gctl_error(req, "Too many arguments.");
 +			return;
 +		}
 +
 +		prov = gctl_get_ascii(req, "arg0");
 +		eli_configure_detached(req, prov, boot);
 +		return;
 +	} else {
 +		gctl_issue(req);
 +	}
 +
  	/* Now the rest. */
  	for (i = 0; i < nargs; i++) {
  		prov = gctl_get_ascii(req, "arg%d", i);
  		if (!eli_is_attached(prov))
  			eli_configure_detached(req, prov, boot);
  	}
 +
  }
  
  static void
 @@ -950,6 +1038,7 @@
  eli_setkey_detached(struct gctl_req *req, const char *prov,
   struct g_eli_metadata *md)
  {
 +	const char *header, *str;
  	unsigned char key[G_ELI_USERKEYLEN], mkey[G_ELI_DATAIVKEYLEN];
  	unsigned char *mkeydst;
  	unsigned int nkey;
 @@ -1035,7 +1124,14 @@
  	}
  
  	/* Store metadata with fresh key. */
 -	eli_metadata_store(req, prov, md);
 +	header = gctl_get_ascii(req, "header");
 +	if(header[0] != '\0')
 +		str = header;
 +	else
 +		str = prov;
 +
 +	eli_metadata_store(req, str, md);
 +
  	bzero(md, sizeof(*md));
  }
  
 @@ -1043,7 +1139,7 @@
  eli_setkey(struct gctl_req *req)
  {
  	struct g_eli_metadata md;
 -	const char *prov;
 +	const char *prov, *header, *str;
  	int nargs;
  
  	nargs = gctl_get_int(req, "nargs");
 @@ -1053,10 +1149,16 @@
  	}
  	prov = gctl_get_ascii(req, "arg0");
  
 -	if (eli_metadata_read(req, prov, &md) == -1)
 +	header = gctl_get_ascii(req, "header");
 +	if(header[0] != '\0')
 +		str = header;
 +	else
 +		str = prov;
 +
 +	if (eli_metadata_read(req, str, &md) == -1)
  		return;
  
 -	if (eli_is_attached(prov))
 +	if (eli_is_attached(prov) && header[0] == '\0')
  		eli_setkey_attached(req, &md);
  	else
  		eli_setkey_detached(req, prov, &md);
 @@ -1079,12 +1181,19 @@
  eli_delkey_detached(struct gctl_req *req, const char *prov)
  {
  	struct g_eli_metadata md;
 +	const char *header, *str;
  	unsigned char *mkeydst;
  	unsigned int nkey;
  	intmax_t val;
  	bool all, force;
  
 -	if (eli_metadata_read(req, prov, &md) == -1)
 +	header = gctl_get_ascii(req, "header");
 +	if(header[0] != '\0')
 +		str = header;
 +	else
 +		str = prov;
 +
 +	if (eli_metadata_read(req, str, &md) == -1)
  		return;
  
  	all = gctl_get_int(req, "all");
 @@ -1116,6 +1225,11 @@
  		arc4rand(mkeydst, G_ELI_MKEYLEN);
  	}
  
 +	if(header[0] != '\0')
 +		str = header;
 +	else
 +		str = prov;
 +
  	eli_metadata_store(req, prov, &md);
  	bzero(&md, sizeof(md));
  }
 @@ -1123,7 +1237,7 @@
  static void
  eli_delkey(struct gctl_req *req)
  {
 -	const char *prov;
 +	const char *prov, *header;
  	int nargs;
  
  	nargs = gctl_get_int(req, "nargs");
 @@ -1133,7 +1247,9 @@
  	}
  	prov = gctl_get_ascii(req, "arg0");
  
 -	if (eli_is_attached(prov))
 +	header = gctl_get_ascii(req, "header");
 +
 +	if (eli_is_attached(prov) && header[0] == '\0')
  		eli_delkey_attached(req, prov);
  	else
  		eli_delkey_detached(req, prov);
 @@ -1144,10 +1260,14 @@
  {
  	struct g_eli_metadata md;
  	unsigned char key[G_ELI_USERKEYLEN];
 -	const char *prov;
 +	unsigned char *hd;
 +	const char *prov, *str, *header;
  	off_t mediasize;
 +	ssize_t hdsize;
  	int nargs;
  
 +	hd = NULL;
 +
  	nargs = gctl_get_int(req, "nargs");
  	if (nargs != 1) {
  		gctl_error(req, "Invalid number of arguments.");
 @@ -1155,10 +1275,18 @@
  	}
  	prov = gctl_get_ascii(req, "arg0");
  
 -	if (eli_metadata_read(req, prov, &md) == -1)
 +	header = gctl_get_ascii(req, "header");
 +	if (header[0] != '\0')
 +		str = header;
 +	else
 +		str = prov;
 +
 +	if (eli_metadata_read(req, str, &md) == -1)
  		return;
  
  	mediasize = g_get_mediasize(prov);
 +	hdsize = g_get_sectorsize(prov);
 +
  	if (md.md_provsize != (uint64_t)mediasize) {
  		gctl_error(req, "Provider size mismatch.");
  		return;
 @@ -1169,12 +1297,30 @@
  		return;
  	}
  
 +	if(header[0] != '\0') {
 +		hd = malloc(hdsize);
 +		if(hd == NULL) {
 +			gctl_error(req, "Cannot allocate %zd bytes of memory.", hdsize);
 +			return;
 +		}
 +
 +		bzero(hd, hdsize);
 +		eli_metadata_encode(&md, hd);
 +	} else {
 +		hdsize = sizeof(hd);
 +	}
 +
 +	gctl_ro_param(req, "hd", hdsize, hd);
  	gctl_ro_param(req, "key", sizeof(key), key);
 +
  	if (gctl_issue(req) == NULL) {
  		if (verbose)
  			printf("Resumed %s.\n", prov);
  	}
  	bzero(key, sizeof(key));
 +
 +	if(hd != NULL)
 +		free(hd);
  }
  
  static int
 @@ -1469,11 +1615,11 @@
  eli_resize(struct gctl_req *req)
  {
  	struct g_eli_metadata md;
 -	const char *prov;
 +	const char *prov, *header;
  	unsigned char *sector;
  	ssize_t secsize;
  	off_t mediasize, oldsize;
 -	int nargs, provfd;
 +	int nargs, provfd, error;
  
  	nargs = gctl_get_int(req, "nargs");
  	if (nargs != 1) {
 @@ -1486,14 +1632,18 @@
  	sector = NULL;
  	secsize = 0;
  
 -	provfd = g_open(prov, 1);
 -	if (provfd == -1) {
 -		gctl_error(req, "Cannot open %s: %s.", prov, strerror(errno));
 -		goto out;
 +	header = gctl_get_ascii(req, "header");
 +
 +	if(header[0] == '\0') {
 +		provfd = g_open(prov, 1);
 +		if (provfd == -1) {
 +			gctl_error(req, "Cannot open %s: %s.", prov, strerror(errno));
 +			goto out;
 +		}
  	}
  
 -	mediasize = g_mediasize(provfd);
 -	secsize = g_sectorsize(provfd);
 +	mediasize = g_get_mediasize(prov);
 +	secsize = g_get_sectorsize(prov);
  	if (mediasize == -1 || secsize == -1) {
  		gctl_error(req, "Cannot get information about %s: %s.", prov,
  		    strerror(errno));
 @@ -1517,16 +1667,24 @@
  	}
  
  	/* Read metadata from the 'oldsize' offset. */
 -	if (pread(provfd, sector, secsize, oldsize - secsize) != secsize) {
 -		gctl_error(req, "Cannot read old metadata: %s.",
 -		    strerror(errno));
 -		goto out;
 -	}
 +	if(header[0] != '\0') {
 +		if (eli_metadata_read(req, header, &md) == -1) {
 +			gctl_error(req, "Cannot read old metadata: %s.",
 +					header);
 +			goto out;
 +		}
 +	} else {
 +		if (pread(provfd, sector, secsize, oldsize - secsize) != secsize) {
 +			gctl_error(req, "Cannot read old metadata: %s.",
 +				strerror(errno));
 +			goto out;
 +		}
  
 -	/* Check if this sector contains geli metadata. */
 -	if (eli_metadata_decode(sector, &md) != 0) {
 -		gctl_error(req, "MD5 hash mismatch: no metadata for oldsize.");
 -		goto out;
 +		/* Check if this sector contains geli metadata. */
 +		if (eli_metadata_decode(sector, &md) != 0) {
 +			gctl_error(req, "MD5 hash mismatch: no metadata for oldsize.");
 +			goto out;
 +		}
  	}
  
  	/*
 @@ -1544,15 +1702,25 @@
  	 */
  	md.md_provsize = mediasize;
  	eli_metadata_encode(&md, sector);
 -	if (pwrite(provfd, sector, secsize, mediasize - secsize) != secsize) {
 -		gctl_error(req, "Cannot write metadata: %s.", strerror(errno));
 -		goto out;
 -	}
 -	(void)g_flush(provfd);
  
 -	/* Now trash the old metadata. */
 -	if (eli_trash_metadata(req, prov, provfd, oldsize - secsize) == -1)
 -		goto out;
 +	if(header[0] != '\0') {
 +		error = eli_metadata_store(req, header, &md);
 +		if(error != 0) {
 +			gctl_error(req, "Cannot store header %s: %s.", header,
 +					strerror(error));
 +			goto out;
 +		}
 +	} else {
 +		if (pwrite(provfd, sector, secsize, mediasize - secsize) != secsize) {
 +			gctl_error(req, "Cannot write metadata: %s.", strerror(errno));
 +			goto out;
 +		}
 +		(void)g_flush(provfd);
 +
 +		/* Now trash the old metadata. */
 +		if (eli_trash_metadata(req, prov, provfd, oldsize - secsize) == -1)
 +			goto out;
 +	}
  out:
  	if (provfd >= 0)
  		(void)g_close(provfd);
 @@ -1592,7 +1760,7 @@
  eli_dump(struct gctl_req *req)
  {
  	struct g_eli_metadata md, tmpmd;
 -	const char *name;
 +	const char *name, *header;
  	int error, i, nargs;
  
  	nargs = gctl_get_int(req, "nargs");
 @@ -1601,15 +1769,40 @@
  		return;
  	}
  
 -	for (i = 0; i < nargs; i++) {
 -		name = gctl_get_ascii(req, "arg%d", i);
 -		error = g_metadata_read(name, (unsigned char *)&tmpmd,
 -		    sizeof(tmpmd), G_ELI_MAGIC);
 -		if (error != 0) {
 +	header = gctl_get_ascii(req, "header");
 +	if(header[0] != '\0') {
 +		if(nargs != 1) {
 +			gctl_error(req, "Too many arguments.");
 +			return;
 +		}
 +
 +		if (eli_metadata_read(req, header, &tmpmd) == -1)
 +			return;
 +
 +		name = gctl_get_ascii(req, "arg0");
 +
 +		if (strcmp(tmpmd.md_magic, G_ELI_MAGIC) != 0) {
 +			error = EINVAL;
  			fprintf(stderr, "Cannot read metadata from %s: %s.\n",
 -			    name, strerror(error));
 +					name, strerror(error));
  			gctl_error(req, "Not fully done.");
 -			continue;
 +			return;
 +		}
 +
 +		name = header;
 +	}
 +
 +	for (i = 0; i < nargs; i++) {
 +		if(header[0] == '\0') {
 +			name = gctl_get_ascii(req, "arg%d", i);
 +			error = g_metadata_read(name, (unsigned char *)&tmpmd,
 +				sizeof(tmpmd), G_ELI_MAGIC);
 +			if (error != 0) {
 +				fprintf(stderr, "Cannot read metadata from %s: %s.\n",
 +					name, strerror(error));
 +				gctl_error(req, "Not fully done.");
 +				continue;
 +			}
  		}
  		if (eli_metadata_decode((unsigned char *)&tmpmd, &md) != 0) {
  			fprintf(stderr, "MD5 hash mismatch for %s, skipping.\n",
 --- sbin/geom/class/eli/geli.8.orig	2013-04-29 01:45:56.000000000 +0300
 +++ sbin/geom/class/eli/geli.8	2013-05-05 16:49:57.642188841 +0300
 @@ -52,6 +52,7 @@
  .Nm
  .Cm init
  .Op Fl bPv
 +.Op Fl H Ar headerfile
  .Op Fl a Ar aalgo
  .Op Fl B Ar backupfile
  .Op Fl e Ar ealgo
 @@ -67,6 +68,7 @@
  .Nm
  .Cm attach
  .Op Fl dprv
 +.Op Fl H Ar headerfile
  .Op Fl j Ar passfile
  .Op Fl k Ar keyfile
  .Ar prov
 @@ -88,10 +90,12 @@
  .Nm
  .Cm configure
  .Op Fl bB
 +.Op Fl H Ar headerfile
  .Ar prov ...
  .Nm
  .Cm setkey
  .Op Fl pPv
 +.Op Fl H Ar headerfile
  .Op Fl i Ar iterations
  .Op Fl j Ar passfile
  .Op Fl J Ar newpassfile
 @@ -102,6 +106,7 @@
  .Nm
  .Cm delkey
  .Op Fl afv
 +.Op Fl H Ar headerfile
  .Op Fl n Ar keyno
  .Ar prov
  .Nm
 @@ -125,12 +130,14 @@
  .Nm
  .Cm resume
  .Op Fl pv
 +.Op Fl H Ar headerfile
  .Op Fl j Ar passfile
  .Op Fl k Ar keyfile
  .Ar prov
  .Nm
  .Cm resize
  .Op Fl v
 +.Op Fl H Ar headerfile
  .Fl s Ar oldsize
  .Ar prov
  .Nm
 @@ -140,6 +147,7 @@
  .Nm
  .Cm dump
  .Op Fl v
 +.Op Fl H Ar headerfile
  .Ar prov ...
  .Nm
  .Cm list
 @@ -240,6 +248,8 @@
  .Pp
  Additional options include:
  .Bl -tag -width ".Fl J Ar newpassfile"
 +.It Fl H Ar headerfile
 +Store GELI metadata (header) in the external file
  .It Fl a Ar aalgo
  Enable data integrity verification (authentication) using the given algorithm.
  This will reduce size of available storage and also reduce speed.
 @@ -341,6 +351,8 @@
  option for the
  .Cm detach
  subcommand.
 +.It Fl H Ar headerfile
 +Read metadata from a file instead from a provider
  .It Fl j Ar passfile
  Specifies a file which contains the passphrase or its part.
  For more information see the description of the
 @@ -415,7 +427,9 @@
  Change configuration of the given providers.
  .Pp
  Additional options include:
 -.Bl -tag -width ".Fl b"
 +.Bl -tag -width ".Fl H Ar headerfile"
 +.It Fl H Ar headerfile
 +Handle external metadata
  .It Fl b
  Set the BOOT flag on the given providers.
  For more information, see the description of the
 @@ -437,6 +451,8 @@
  .Pp
  Additional options include:
  .Bl -tag -width ".Fl J Ar newpassfile"
 +.It Fl H Ar headerfile
 +Handle external metadata
  .It Fl i Ar iterations
  Number of iterations to use with PKCS#5v2.
  If 0 is given, PKCS#5v2 will not be used.
 @@ -472,7 +488,9 @@
  subcommand.
  .Pp
  Additional options include:
 -.Bl -tag -width ".Fl a Ar keyno"
 +.Bl -tag -width ".Fl H Ar headerfile"
 +.It Fl H Ar headerfile
 +Handle external metadata
  .It Fl a
  Destroy all keys (does not need
  .Fl f
 @@ -567,7 +585,9 @@
  utility is stored is bad idea.
  .Pp
  Additional options include:
 -.Bl -tag -width ".Fl j Ar passfile"
 +.Bl -tag -width ".Fl H Ar headerfile"
 +.It Fl H Ar headerfile
 +Handle external metadata
  .It Fl j Ar passfile
  Specifies a file which contains the passphrase or its part.
  For more information see the description of the
 @@ -593,7 +613,9 @@
  provider and the provider size is updated.
  .Pp
  Additional options include:
 -.Bl -tag -width ".Fl s Ar oldsize"
 +.Bl -tag -width ".Fl H Ar headerfile"
 +.It Fl H Ar headerfile
 +Handle external metadata
  .It Fl s Ar oldsize
  The size of the provider before it was resized.
  .El
 @@ -764,6 +786,9 @@
  # dd if=/dev/random of=/dev/da1s3a bs=1m
  # dd if=/dev/random of=/boot/keys/da1s3a.key bs=128k count=1
  # geli init -b -P -K /boot/keys/da1s3a.key da1s3a
 +# dd if=/dev/random of=/dev/ada1 bs=1m
 +# dd if=/dev/random of=/boot/keys/ada1.key bs=8 count=8
 +# geli init -b -H /boot/hd/ada1.hd -P -K /boot/keys/ada1.key ada1
  .Ed
  .Pp
  The providers are initialized, now we have to add those lines to
 @@ -782,6 +807,13 @@
  geli_da1s3a_keyfile0_load="YES"
  geli_da1s3a_keyfile0_type="da1s3a:geli_keyfile0"
  geli_da1s3a_keyfile0_name="/boot/keys/da1s3a.key"
 +
 +geli_ada1_header_load="YES"
 +geli_ada1_header_type="ada1:geli_header"
 +geli_ada1_header_name="/boot/hd/ada1.hd"
 +geli_ada1_keyfile0_load="YES"
 +geli_ada1_keyfile0_type="ada1:geli_keyfile0"
 +geli_ada1_keyfile0_name="/boot/keys/ada1.key"
  .Ed
  .Pp
  Not only configure encryption, but also data integrity verification using
 --- sys/geom/eli/g_eli_ctl.c.orig	2013-05-04 01:21:45.381136674 +0300
 +++ sys/geom/eli/g_eli_ctl.c	2013-05-05 03:20:20.180243224 +0300
 @@ -56,10 +56,11 @@
  	struct g_eli_metadata md;
  	struct g_provider *pp;
  	const char *name;
 -	u_char *key, mkey[G_ELI_DATAIVKEYLEN];
 +	u_char *key, *hd, mkey[G_ELI_DATAIVKEYLEN];
  	int *nargs, *detach, *readonly;
  	int keysize, error;
  	u_int nkey;
 +	ssize_t hdsize;
  
  	g_topology_assert();
  
 @@ -97,11 +98,18 @@
  		gctl_error(req, "Provider %s is invalid.", name);
  		return;
  	}
 -	error = g_eli_read_metadata(mp, pp, &md);
 -	if (error != 0) {
 -		gctl_error(req, "Cannot read metadata from %s (error=%d).",
 -		    name, error);
 -		return;
 +
 +	hd = gctl_get_param(req, "hd", &hdsize);
 +
 +	if(hdsize == pp->sectorsize) {
 +		eli_metadata_decode(hd, &md);
 +	} else {
 +		error = g_eli_read_metadata(mp, pp, &md);
 +		if (error != 0) {
 +			gctl_error(req, "Cannot read metadata from %s (error=%d).",
 +				name, error);
 +			return;
 +		}
  	}
  	if (md.md_keys == 0x00) {
  		bzero(&md, sizeof(md));
 @@ -448,8 +456,8 @@
  		error = g_eli_read_metadata(mp, pp, &md);
  		if (error != 0) {
  			gctl_error(req,
 -			    "Cannot read metadata from %s (error=%d).",
 -			    prov, error);
 +				"Cannot read metadata from %s (error=%d).",
 +				prov, error);
  			continue;
  		}
  
 @@ -464,12 +472,13 @@
  		sector = malloc(pp->sectorsize, M_ELI, M_WAITOK | M_ZERO);
  		eli_metadata_encode(&md, sector);
  		error = g_write_data(cp, pp->mediasize - pp->sectorsize, sector,
 -		    pp->sectorsize);
 +			pp->sectorsize);
  		if (error != 0) {
  			gctl_error(req,
 -			    "Cannot store metadata on %s (error=%d).",
 -			    prov, error);
 +				"Cannot store metadata on %s (error=%d).",
 +				prov, error);
  		}
 +		
  		bzero(&md, sizeof(md));
  		bzero(sector, sizeof(sector));
  		free(sector, M_ELI);
 @@ -815,9 +824,10 @@
  	struct g_provider *pp;
  	struct g_consumer *cp;
  	const char *name;
 -	u_char *key, mkey[G_ELI_DATAIVKEYLEN];
 +	u_char *key, *hd, mkey[G_ELI_DATAIVKEYLEN];
  	int *nargs, keysize, error;
  	u_int nkey;
 +	ssize_t hdsize;
  
  	g_topology_assert();
  
 @@ -843,12 +853,20 @@
  	}
  	cp = LIST_FIRST(&sc->sc_geom->consumer);
  	pp = cp->provider;
 -	error = g_eli_read_metadata(mp, pp, &md);
 -	if (error != 0) {
 -		gctl_error(req, "Cannot read metadata from %s (error=%d).",
 -		    name, error);
 -		return;
 +
 +	hd = gctl_get_param(req, "hd", &hdsize);
 +
 +	if(hdsize == pp->sectorsize) {
 +		eli_metadata_decode(hd, &md);
 +	} else {
 +		error = g_eli_read_metadata(mp, pp, &md);
 +		if (error != 0) {
 +			gctl_error(req, "Cannot read metadata from %s (error=%d).",
 +				name, error);
 +			return;
 +		}
  	}
 +
  	if (md.md_keys == 0x00) {
  		bzero(&md, sizeof(md));
  		gctl_error(req, "No valid keys on %s.", pp->name);
 --- sys/geom/eli/g_eli.c.orig	2013-05-07 10:08:29.000000000 +0300
 +++ sys/geom/eli/g_eli.c	2013-05-04 00:26:03.620416070 +0300
 @@ -1013,6 +1013,47 @@
  	}
  }
  
 +static int
 +g_eli_header_load(struct g_eli_metadata *md, const char *provider)
 +{
 +	unsigned char *headfile, *data;
 +	char *file, name[64];
 +	size_t size;
 +
 +	snprintf(name, sizeof(name), "%s:geli_header", provider);
 +	headfile = preload_search_by_type(name);
 +	if (headfile == NULL)
 +		return (1);
 +
 +	data = preload_fetch_addr(headfile);
 +	if (data == NULL) {
 +		G_ELI_DEBUG(0, "Cannot find header file data for %s.",
 +				name);
 +		return (1);
 +	}
 +
 +	size = preload_fetch_size(headfile);
 +	if (size == 0) {
 +		G_ELI_DEBUG(0, "Cannot find header file size for %s.",
 +				name);
 +		return (1);
 +	}
 +
 +	file = preload_search_info(headfile, MODINFO_NAME);
 +	if (file == NULL) {
 +		G_ELI_DEBUG(0, "Cannot find header file name for %s.",
 +				name);
 +		return (1);
 +	}
 +
 +	G_ELI_DEBUG(1, "Loaded header %s for %s (type: %s).", file,
 +			provider, name);
 +
 +	eli_metadata_decode(data, md);
 +
 +	return (0);
 +}
 +
  /*
   * Tasting is only made on boot.
   * We detect providers which should be attached before root is mounted.
 @@ -1036,9 +1077,11 @@
  
  	G_ELI_DEBUG(3, "Tasting %s.", pp->name);
  
 -	error = g_eli_read_metadata(mp, pp, &md);
 -	if (error != 0)
 -		return (NULL);
 +	if(g_eli_header_load(&md, pp->name) != 0) {
 +		error = g_eli_read_metadata(mp, pp, &md);
 +		if (error != 0)
 +			return (NULL);
 +	}
  	gp = NULL;
  
  	if (strcmp(md.md_magic, G_ELI_MAGIC) != 0)
 
 _______________________________________________
 freebsd-bugs@freebsd.org mailing list
 http://lists.freebsd.org/mailman/listinfo/freebsd-bugs
 To unsubscribe, send any mail to "freebsd-bugs-unsubscribe@freebsd.org"
 
 
 ----- End forwarded message -----
>Unformatted:
