From 133842392acf8e5f78af625d897f0861939ca765 Mon Sep 17 00:00:00 2001
From: Alasdair Kergon <agk@redhat.com>
Date: Sat, 29 Apr 2006 22:08:43 +0000
Subject: [PATCH] Improve stripe size validation. Increase maximum stripe size
 limit to physical extent size for lvm2 metadata.

---
 WHATS_NEW                     |  2 ++
 lib/format_text/format-text.c |  3 ++-
 lib/metadata/metadata.h       |  2 ++
 man/lvcreate.8                |  4 +++-
 tools/lvcreate.c              | 35 ++++++++++++++++++++++++-----
 tools/lvresize.c              | 42 +++++++++++++++++++++++++++++------
 6 files changed, 73 insertions(+), 15 deletions(-)

diff --git a/WHATS_NEW b/WHATS_NEW
index 7bd1653e0..9ff0176ab 100644
--- a/WHATS_NEW
+++ b/WHATS_NEW
@@ -1,5 +1,7 @@
 Version 2.02.06 -
 =================================
+  Improve stripe size validation.
+  Increase maximum stripe size limit to physical extent size for lvm2 metadata.
   Fix activation code to check for pre-existing mirror logs.
   Tighten region size validation.
   Ignore empty strings in config files.
diff --git a/lib/format_text/format-text.c b/lib/format_text/format-text.c
index cd579950a..3ca480b80 100644
--- a/lib/format_text/format-text.c
+++ b/lib/format_text/format-text.c
@@ -1754,7 +1754,8 @@ struct format_type *create_text_format(struct cmd_context *cmd)
 	fmt->name = FMT_TEXT_NAME;
 	fmt->alias = FMT_TEXT_ALIAS;
 	fmt->features = FMT_SEGMENTS | FMT_MDAS | FMT_TAGS | FMT_PRECOMMIT |
-			FMT_UNLIMITED_VOLS | FMT_RESIZE_PV;
+			FMT_UNLIMITED_VOLS | FMT_RESIZE_PV |
+			FMT_UNLIMITED_STRIPESIZE;
 
 	if (!(mda_lists = dm_malloc(sizeof(struct mda_lists)))) {
 		log_error("Failed to allocate dir_list");
diff --git a/lib/metadata/metadata.h b/lib/metadata/metadata.h
index f9b732dfc..c3f2b0d21 100644
--- a/lib/metadata/metadata.h
+++ b/lib/metadata/metadata.h
@@ -31,6 +31,7 @@
 #define SECTOR_SIZE ( 1L << SECTOR_SHIFT )
 #define STRIPE_SIZE_MIN ( getpagesize() >> SECTOR_SHIFT)	/* PAGESIZE in sectors */
 #define STRIPE_SIZE_MAX ( 512L * 1024L >> SECTOR_SHIFT)	/* 512 KB in sectors */
+#define STRIPE_SIZE_LIMIT ((UINT_MAX >> 2) + 1)
 #define PV_MIN_SIZE ( 512L * 1024L >> SECTOR_SHIFT)	/* 512 KB in sectors */
 #define PE_ALIGN (65536UL >> SECTOR_SHIFT)	/* PE alignment */
 #define MAX_RESTRICTED_LVS 255	/* Used by FMT_RESTRICTED_LVIDS */
@@ -74,6 +75,7 @@
 #define FMT_ORPHAN_ALLOCATABLE	0x00000020	/* Orphan PV allocatable? */
 #define FMT_PRECOMMIT		0x00000040	/* Supports pre-commit? */
 #define FMT_RESIZE_PV		0x00000080	/* Supports pvresize? */
+#define FMT_UNLIMITED_STRIPESIZE 0x00000080	/* Unlimited stripe size? */
 
 typedef enum {
 	ALLOC_INVALID,
diff --git a/man/lvcreate.8 b/man/lvcreate.8
index 881cdc4c4..a3717ab6a 100644
--- a/man/lvcreate.8
+++ b/man/lvcreate.8
@@ -58,7 +58,9 @@ the logical volume.
 .I \-I, \-\-stripesize StripeSize
 Gives the number of kilobytes for the granularity of the stripes.
 .br
-StripeSize must be 2^n (n = 2 to 9)
+StripeSize must be 2^n (n = 2 to 9) for metadata in LVM1 format.
+For metadata in LVM2 format, the stripe size may be a larger
+power of 2 but must not exceed the physical extent size.
 .TP
 .I \-l, \-\-extents LogicalExtentsNumber
 Gives the number of logical extents to allocate for the new
diff --git a/tools/lvcreate.c b/tools/lvcreate.c
index f19117458..7464929be 100644
--- a/tools/lvcreate.c
+++ b/tools/lvcreate.c
@@ -181,6 +181,10 @@ static int _read_size_params(struct lvcreate_params *lp,
 	return 1;
 }
 
+/* The stripe size is limited by the size of a uint32_t, but since the
+ * value given by the user is doubled, and the final result must be a
+ * power of 2, we must divide UINT_MAX by four and add 1 (to round it
+ * up to the power of 2) */
 static int _read_stripe_params(struct lvcreate_params *lp,
 			       struct cmd_context *cmd,
 			       int *pargc, char ***pargv)
@@ -192,6 +196,12 @@ static int _read_stripe_params(struct lvcreate_params *lp,
 			log_error("Negative stripesize is invalid");
 			return 0;
 		}
+		/* Check to make sure we won't overflow lp->stripe_size */
+		if(arg_uint_value(cmd, stripesize_ARG, 0) > STRIPE_SIZE_LIMIT) {
+			log_error("Stripe size cannot be larger than %s",
+				  display_size(cmd, STRIPE_SIZE_LIMIT, SIZE_SHORT));
+			return 0;
+		}
 		lp->stripe_size = 2 * arg_uint_value(cmd, stripesize_ARG, 0);
 	}
 
@@ -204,7 +214,8 @@ static int _read_stripe_params(struct lvcreate_params *lp,
 		lp->stripe_size = find_config_int(cmd->cft->root,
 						  "metadata/stripesize",
 						  DEFAULT_STRIPESIZE) * 2;
-		log_print("Using default stripesize %dKB", lp->stripe_size / 2);
+		log_print("Using default stripesize %s",
+			  display_size(cmd, lp->stripe_size, SIZE_SHORT));
 	}
 
 	if (argc && (unsigned) argc < lp->stripes) {
@@ -219,10 +230,11 @@ static int _read_stripe_params(struct lvcreate_params *lp,
 		return 0;
 	}
 
+	/* MAX size check is in _lvcreate */
 	if (lp->stripes > 1 && (lp->stripe_size < STRIPE_SIZE_MIN ||
-				lp->stripe_size > STRIPE_SIZE_MAX ||
 				lp->stripe_size & (lp->stripe_size - 1))) {
-		log_error("Invalid stripe size %d", lp->stripe_size);
+		log_error("Invalid stripe size %s",
+			  display_size(cmd, lp->stripe_size, SIZE_SHORT));
 		return 0;
 	}
 
@@ -505,12 +517,23 @@ static int _lvcreate(struct cmd_context *cmd, struct lvcreate_params *lp)
 		pvh = &vg->pvs;
 
 	if (lp->stripe_size > vg->extent_size) {
-		log_error("Setting stripe size %d KB to physical extent "
-			  "size %u KB", lp->stripe_size / 2,
-			  vg->extent_size / 2);
+		log_error("Reducing requested stripe size %s to maximum, "
+			  "physical extent size %s",
+			  display_size(cmd, lp->stripe_size, SIZE_SHORT),
+			  display_size(cmd, vg->extent_size, SIZE_SHORT));
 		lp->stripe_size = vg->extent_size;
 	}
 
+	/* Need to check the vg's format to verify this - the cmd format isn't setup properly yet */
+	if (lp->stripes > 1 &&
+	    !(vg->fid->fmt->features & FMT_UNLIMITED_STRIPESIZE) &&
+	    (lp->stripe_size > STRIPE_SIZE_MAX)) {
+		log_error("Stripe size may not exceed %s",
+			  display_size(cmd, STRIPE_SIZE_MAX,
+				       SIZE_SHORT));
+		return 0;
+	}
+
 	if (lp->size) {
 		/* No of 512-byte sectors */
 		tmp_size = lp->size;
diff --git a/tools/lvresize.c b/tools/lvresize.c
index 094c7f5a0..f3956fa26 100644
--- a/tools/lvresize.c
+++ b/tools/lvresize.c
@@ -178,15 +178,35 @@ static int _lvresize(struct cmd_context *cmd, struct lvresize_params *lp)
 			log_error("Stripesize may not be negative.");
 			return ECMD_FAILED;
 		}
-		if (vg->fid->fmt->features & FMT_SEGMENTS)
+
+		if (arg_uint_value(cmd, stripesize_ARG, 0) > STRIPE_SIZE_LIMIT) {
+			log_error("Stripe size cannot be larger than %s",
+				  display_size(cmd, STRIPE_SIZE_LIMIT, SIZE_SHORT));
+			return 0;
+		}
+
+		if (!(vg->fid->fmt->features & FMT_SEGMENTS))
+			log_print("Varied stripesize not supported. Ignoring.");
+		else if (arg_uint_value(cmd, stripesize_ARG, 0) > vg->extent_size) {
+                	log_error("Reducing stripe size %s to maximum, "
+				  "physical extent size %s",
+				  display_size(cmd,
+					arg_uint_value(cmd, stripesize_ARG, 0) * 2,
+					SIZE_SHORT),
+	                          display_size(cmd, vg->extent_size, SIZE_SHORT));
+                	lp->stripe_size = vg->extent_size;
+        	} else
 			lp->stripe_size = 2 * arg_uint_value(cmd,
 							     stripesize_ARG, 0);
-		else
-			log_print("Varied stripesize not supported. Ignoring.");
+
 		if (lp->mirrors) {
 			log_error("Mirrors and striping cannot be combined yet.");
 			return ECMD_FAILED;
 		}
+		if (lp->stripe_size & (lp->stripe_size - 1)) {
+			log_error("Stripe size must be power of 2");
+			return 0;
+		}
 	}
 
 	lv = lvl->lv;
@@ -279,16 +299,18 @@ static int _lvresize(struct cmd_context *cmd, struct lvresize_params *lp)
 
 		if (!lp->stripe_size && lp->stripes > 1) {
 			if (seg_stripesize) {
-				log_print("Using stripesize of last segment "
-					  "%dKB", seg_stripesize / 2);
+				log_print("Using stripesize of last segment %s",
+					  display_size(cmd, seg_stripesize,
+						       SIZE_SHORT));
 				lp->stripe_size = seg_stripesize;
 			} else {
 				lp->stripe_size =
 					find_config_int(cmd->cft->root,
 							"metadata/stripesize",
 							DEFAULT_STRIPESIZE) * 2;
-				log_print("Using default stripesize %dKB",
-					  lp->stripe_size / 2);
+				log_print("Using default stripesize %s",
+					  display_size(cmd, lp->stripe_size,
+						       SIZE_SHORT));
 			}
 		}
 	}
@@ -362,6 +384,12 @@ static int _lvresize(struct cmd_context *cmd, struct lvresize_params *lp)
 				  lp->extents, lp->extents - size_rest);
 			lp->extents = lp->extents - size_rest;
 		}
+
+		if (lp->stripe_size < STRIPE_SIZE_MIN) {
+			log_error("Invalid stripe size %s",
+				  display_size(cmd, lp->stripe_size, SIZE_SHORT));
+			return 0;
+		}
 	}
 
 	if (lp->extents == lv->le_count) {