xref: /netbsd-src/external/gpl2/lvm2/dist/tools/lvresize.c (revision 7c604eea85b4f330dc75ffe65e947f4d73758aa0)
1*7c604eeaShaad /*	$NetBSD: lvresize.c,v 1.1.1.3 2009/12/02 00:25:53 haad Exp $	*/
256a34939Shaad 
356a34939Shaad /*
456a34939Shaad  * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
5*7c604eeaShaad  * Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
656a34939Shaad  *
756a34939Shaad  * This file is part of LVM2.
856a34939Shaad  *
956a34939Shaad  * This copyrighted material is made available to anyone wishing to use,
1056a34939Shaad  * modify, copy, or redistribute it subject to the terms and conditions
1156a34939Shaad  * of the GNU Lesser General Public License v.2.1.
1256a34939Shaad  *
1356a34939Shaad  * You should have received a copy of the GNU Lesser General Public License
1456a34939Shaad  * along with this program; if not, write to the Free Software Foundation,
1556a34939Shaad  * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
1656a34939Shaad  */
1756a34939Shaad 
1856a34939Shaad #include "tools.h"
1956a34939Shaad 
2056a34939Shaad #define SIZE_BUF 128
2156a34939Shaad 
2256a34939Shaad struct lvresize_params {
2356a34939Shaad 	const char *vg_name;
2456a34939Shaad 	const char *lv_name;
2556a34939Shaad 
2656a34939Shaad 	uint32_t stripes;
2756a34939Shaad 	uint32_t stripe_size;
2856a34939Shaad 	uint32_t mirrors;
2956a34939Shaad 
3056a34939Shaad 	const struct segment_type *segtype;
3156a34939Shaad 
3256a34939Shaad 	/* size */
3356a34939Shaad 	uint32_t extents;
3456a34939Shaad 	uint64_t size;
3556a34939Shaad 	sign_t sign;
3656a34939Shaad 	percent_t percent;
3756a34939Shaad 
3856a34939Shaad 	enum {
3956a34939Shaad 		LV_ANY = 0,
4056a34939Shaad 		LV_REDUCE = 1,
4156a34939Shaad 		LV_EXTEND = 2
4256a34939Shaad 	} resize;
4356a34939Shaad 
4456a34939Shaad 	int resizefs;
4556a34939Shaad 	int nofsck;
4656a34939Shaad 
4756a34939Shaad 	int argc;
4856a34939Shaad 	char **argv;
4956a34939Shaad };
5056a34939Shaad 
_validate_stripesize(struct cmd_context * cmd,const struct volume_group * vg,struct lvresize_params * lp)51*7c604eeaShaad static int _validate_stripesize(struct cmd_context *cmd,
5256a34939Shaad 				const struct volume_group *vg,
5356a34939Shaad 				struct lvresize_params *lp)
5456a34939Shaad {
5556a34939Shaad 	if (arg_sign_value(cmd, stripesize_ARG, 0) == SIGN_MINUS) {
5656a34939Shaad 		log_error("Stripesize may not be negative.");
5756a34939Shaad 		return 0;
5856a34939Shaad 	}
5956a34939Shaad 
6056a34939Shaad 	if (arg_uint_value(cmd, stripesize_ARG, 0) > STRIPE_SIZE_LIMIT * 2) {
6156a34939Shaad 		log_error("Stripe size cannot be larger than %s",
6256a34939Shaad 			  display_size(cmd, (uint64_t) STRIPE_SIZE_LIMIT));
6356a34939Shaad 		return 0;
6456a34939Shaad 	}
6556a34939Shaad 
6656a34939Shaad 	if (!(vg->fid->fmt->features & FMT_SEGMENTS))
6756a34939Shaad 		log_warn("Varied stripesize not supported. Ignoring.");
6856a34939Shaad 	else if (arg_uint_value(cmd, stripesize_ARG, 0) > vg->extent_size * 2) {
6956a34939Shaad 		log_error("Reducing stripe size %s to maximum, "
7056a34939Shaad 			  "physical extent size %s",
7156a34939Shaad 			  display_size(cmd,
7256a34939Shaad 				       (uint64_t) arg_uint_value(cmd, stripesize_ARG, 0)),
7356a34939Shaad 			  display_size(cmd, (uint64_t) vg->extent_size));
7456a34939Shaad 		lp->stripe_size = vg->extent_size;
7556a34939Shaad 	} else
7656a34939Shaad 		lp->stripe_size = arg_uint_value(cmd, stripesize_ARG, 0);
7756a34939Shaad 
7856a34939Shaad 	if (lp->mirrors) {
7956a34939Shaad 		log_error("Mirrors and striping cannot be combined yet.");
8056a34939Shaad 		return 0;
8156a34939Shaad 	}
8256a34939Shaad 	if (lp->stripe_size & (lp->stripe_size - 1)) {
8356a34939Shaad 		log_error("Stripe size must be power of 2");
8456a34939Shaad 		return 0;
8556a34939Shaad 	}
8656a34939Shaad 
8756a34939Shaad 	return 1;
8856a34939Shaad }
8956a34939Shaad 
_request_confirmation(struct cmd_context * cmd,const struct volume_group * vg,const struct logical_volume * lv,const struct lvresize_params * lp)90*7c604eeaShaad static int _request_confirmation(struct cmd_context *cmd,
9156a34939Shaad 				 const struct volume_group *vg,
9256a34939Shaad 				 const struct logical_volume *lv,
9356a34939Shaad 				 const struct lvresize_params *lp)
9456a34939Shaad {
9556a34939Shaad 	struct lvinfo info;
9656a34939Shaad 
9756a34939Shaad 	memset(&info, 0, sizeof(info));
9856a34939Shaad 
9956a34939Shaad 	if (!lv_info(cmd, lv, &info, 1, 0) && driver_version(NULL, 0)) {
10056a34939Shaad 		log_error("lv_info failed: aborting");
10156a34939Shaad 		return 0;
10256a34939Shaad 	}
10356a34939Shaad 
104*7c604eeaShaad 	if (lp->resizefs) {
105*7c604eeaShaad 		if (!info.exists) {
10656a34939Shaad 			log_error("Logical volume %s must be activated "
10756a34939Shaad 				  "before resizing filesystem", lp->lv_name);
10856a34939Shaad 			return 0;
10956a34939Shaad 		}
110*7c604eeaShaad 		return 1;
111*7c604eeaShaad 	}
11256a34939Shaad 
113*7c604eeaShaad 	if (!info.exists)
114*7c604eeaShaad 		return 1;
11556a34939Shaad 
116*7c604eeaShaad 	log_warn("WARNING: Reducing active%s logical volume to %s",
117*7c604eeaShaad 		 info.open_count ? " and open" : "",
118*7c604eeaShaad 		 display_size(cmd, (uint64_t) lp->extents * vg->extent_size));
119*7c604eeaShaad 
120*7c604eeaShaad 	log_warn("THIS MAY DESTROY YOUR DATA (filesystem etc.)");
12156a34939Shaad 
12256a34939Shaad 	if (!arg_count(cmd, force_ARG)) {
123*7c604eeaShaad 		if (yes_no_prompt("Do you really want to reduce %s? [y/n]: ",
12456a34939Shaad 				  lp->lv_name) == 'n') {
125*7c604eeaShaad 			log_print("Logical volume %s NOT reduced", lp->lv_name);
12656a34939Shaad 			return 0;
12756a34939Shaad 		}
12856a34939Shaad 		if (sigint_caught())
12956a34939Shaad 			return 0;
13056a34939Shaad 	}
13156a34939Shaad 
13256a34939Shaad 	return 1;
13356a34939Shaad }
13456a34939Shaad 
135*7c604eeaShaad enum fsadm_cmd_e { FSADM_CMD_CHECK, FSADM_CMD_RESIZE };
136*7c604eeaShaad #define FSADM_CMD "fsadm"
137*7c604eeaShaad #define FSADM_CMD_MAX_ARGS 6
138*7c604eeaShaad 
139*7c604eeaShaad /*
140*7c604eeaShaad  * FSADM_CMD --dry-run --verbose --force check lv_path
141*7c604eeaShaad  * FSADM_CMD --dry-run --verbose --force resize lv_path size
142*7c604eeaShaad  */
_fsadm_cmd(struct cmd_context * cmd,const struct volume_group * vg,const struct lvresize_params * lp,enum fsadm_cmd_e fcmd)143*7c604eeaShaad static int _fsadm_cmd(struct cmd_context *cmd,
14456a34939Shaad 		      const struct volume_group *vg,
145*7c604eeaShaad 		      const struct lvresize_params *lp,
146*7c604eeaShaad 		      enum fsadm_cmd_e fcmd)
14756a34939Shaad {
14856a34939Shaad 	char lv_path[PATH_MAX];
14956a34939Shaad 	char size_buf[SIZE_BUF];
150*7c604eeaShaad 	const char *argv[FSADM_CMD_MAX_ARGS + 2];
151*7c604eeaShaad 	unsigned i = 0;
15256a34939Shaad 
153*7c604eeaShaad 	argv[i++] = FSADM_CMD;
154*7c604eeaShaad 
155*7c604eeaShaad 	if (test_mode())
156*7c604eeaShaad 		argv[i++] = "--dry-run";
157*7c604eeaShaad 
158*7c604eeaShaad 	if (verbose_level() >= _LOG_NOTICE)
159*7c604eeaShaad 		argv[i++] = "--verbose";
160*7c604eeaShaad 
161*7c604eeaShaad 	if (arg_count(cmd, force_ARG))
162*7c604eeaShaad 		argv[i++] = "--force";
163*7c604eeaShaad 
164*7c604eeaShaad 	argv[i++] = (fcmd == FSADM_CMD_RESIZE) ? "resize" : "check";
165*7c604eeaShaad 
166*7c604eeaShaad 	if (dm_snprintf(lv_path, PATH_MAX, "%s%s/%s", cmd->dev_dir, lp->vg_name,
167*7c604eeaShaad 			lp->lv_name) < 0) {
168*7c604eeaShaad 		log_error("Couldn't create LV path for %s", lp->lv_name);
16956a34939Shaad 		return 0;
17056a34939Shaad 	}
17156a34939Shaad 
172*7c604eeaShaad 	argv[i++] = lv_path;
173*7c604eeaShaad 
174*7c604eeaShaad 	if (fcmd == FSADM_CMD_RESIZE) {
175bec4d750Shaad 		if (dm_snprintf(size_buf, SIZE_BUF, "%" PRIu64 "K",
17656a34939Shaad 				(uint64_t) lp->extents * vg->extent_size / 2) < 0) {
17756a34939Shaad 			log_error("Couldn't generate new LV size string");
17856a34939Shaad 			return 0;
17956a34939Shaad 		}
18056a34939Shaad 
181*7c604eeaShaad 		argv[i++] = size_buf;
182*7c604eeaShaad 	}
18356a34939Shaad 
184*7c604eeaShaad 	argv[i] = NULL;
18556a34939Shaad 
186*7c604eeaShaad 	return exec_cmd(cmd, argv);
18756a34939Shaad }
18856a34939Shaad 
_lvresize_params(struct cmd_context * cmd,int argc,char ** argv,struct lvresize_params * lp)18956a34939Shaad static int _lvresize_params(struct cmd_context *cmd, int argc, char **argv,
19056a34939Shaad 			    struct lvresize_params *lp)
19156a34939Shaad {
19256a34939Shaad 	const char *cmd_name;
19356a34939Shaad 	char *st;
19456a34939Shaad 	unsigned dev_dir_found = 0;
19556a34939Shaad 
19656a34939Shaad 	lp->sign = SIGN_NONE;
19756a34939Shaad 	lp->resize = LV_ANY;
19856a34939Shaad 
19956a34939Shaad 	cmd_name = command_name(cmd);
20056a34939Shaad 	if (!strcmp(cmd_name, "lvreduce"))
20156a34939Shaad 		lp->resize = LV_REDUCE;
20256a34939Shaad 	if (!strcmp(cmd_name, "lvextend"))
20356a34939Shaad 		lp->resize = LV_EXTEND;
20456a34939Shaad 
20556a34939Shaad 	/*
20656a34939Shaad 	 * Allow omission of extents and size if the user has given us
20756a34939Shaad 	 * one or more PVs.  Most likely, the intent was "resize this
20856a34939Shaad 	 * LV the best you can with these PVs"
20956a34939Shaad 	 */
21056a34939Shaad 	if ((arg_count(cmd, extents_ARG) + arg_count(cmd, size_ARG) == 0) &&
21156a34939Shaad 	    (argc >= 2)) {
21256a34939Shaad 		lp->extents = 100;
21356a34939Shaad 		lp->percent = PERCENT_PVS;
21456a34939Shaad 		lp->sign = SIGN_PLUS;
21556a34939Shaad 	} else if ((arg_count(cmd, extents_ARG) +
21656a34939Shaad 		    arg_count(cmd, size_ARG) != 1)) {
21756a34939Shaad 		log_error("Please specify either size or extents but not "
21856a34939Shaad 			  "both.");
21956a34939Shaad 		return 0;
22056a34939Shaad 	}
22156a34939Shaad 
22256a34939Shaad 	if (arg_count(cmd, extents_ARG)) {
22356a34939Shaad 		lp->extents = arg_uint_value(cmd, extents_ARG, 0);
22456a34939Shaad 		lp->sign = arg_sign_value(cmd, extents_ARG, SIGN_NONE);
22556a34939Shaad 		lp->percent = arg_percent_value(cmd, extents_ARG, PERCENT_NONE);
22656a34939Shaad 	}
22756a34939Shaad 
22856a34939Shaad 	/* Size returned in kilobyte units; held in sectors */
22956a34939Shaad 	if (arg_count(cmd, size_ARG)) {
23056a34939Shaad 		lp->size = arg_uint64_value(cmd, size_ARG, UINT64_C(0));
23156a34939Shaad 		lp->sign = arg_sign_value(cmd, size_ARG, SIGN_NONE);
23256a34939Shaad 		lp->percent = PERCENT_NONE;
23356a34939Shaad 	}
23456a34939Shaad 
23556a34939Shaad 	if (lp->resize == LV_EXTEND && lp->sign == SIGN_MINUS) {
23656a34939Shaad 		log_error("Negative argument not permitted - use lvreduce");
23756a34939Shaad 		return 0;
23856a34939Shaad 	}
23956a34939Shaad 
24056a34939Shaad 	if (lp->resize == LV_REDUCE && lp->sign == SIGN_PLUS) {
24156a34939Shaad 		log_error("Positive sign not permitted - use lvextend");
24256a34939Shaad 		return 0;
24356a34939Shaad 	}
24456a34939Shaad 
245*7c604eeaShaad 	lp->resizefs = arg_is_set(cmd, resizefs_ARG);
246*7c604eeaShaad 	lp->nofsck = arg_is_set(cmd, nofsck_ARG);
24756a34939Shaad 
24856a34939Shaad 	if (!argc) {
24956a34939Shaad 		log_error("Please provide the logical volume name");
25056a34939Shaad 		return 0;
25156a34939Shaad 	}
25256a34939Shaad 
25356a34939Shaad 	lp->lv_name = argv[0];
25456a34939Shaad 	argv++;
25556a34939Shaad 	argc--;
25656a34939Shaad 
25756a34939Shaad 	if (!(lp->lv_name = skip_dev_dir(cmd, lp->lv_name, &dev_dir_found)) ||
25856a34939Shaad 	    !(lp->vg_name = extract_vgname(cmd, lp->lv_name))) {
25956a34939Shaad 		log_error("Please provide a volume group name");
26056a34939Shaad 		return 0;
26156a34939Shaad 	}
26256a34939Shaad 
26356a34939Shaad 	if (!validate_name(lp->vg_name)) {
26456a34939Shaad 		log_error("Volume group name %s has invalid characters",
26556a34939Shaad 			  lp->vg_name);
26656a34939Shaad 		return 0;
26756a34939Shaad 	}
26856a34939Shaad 
26956a34939Shaad 	if ((st = strrchr(lp->lv_name, '/')))
27056a34939Shaad 		lp->lv_name = st + 1;
27156a34939Shaad 
27256a34939Shaad 	lp->argc = argc;
27356a34939Shaad 	lp->argv = argv;
27456a34939Shaad 
27556a34939Shaad 	return 1;
27656a34939Shaad }
27756a34939Shaad 
_lvresize(struct cmd_context * cmd,struct volume_group * vg,struct lvresize_params * lp)27856a34939Shaad static int _lvresize(struct cmd_context *cmd, struct volume_group *vg,
27956a34939Shaad 		     struct lvresize_params *lp)
28056a34939Shaad {
28156a34939Shaad 	struct logical_volume *lv;
28256a34939Shaad 	struct lvinfo info;
28356a34939Shaad 	uint32_t stripesize_extents = 0;
28456a34939Shaad 	uint32_t seg_stripes = 0, seg_stripesize = 0, seg_size = 0;
28556a34939Shaad 	uint32_t seg_mirrors = 0;
28656a34939Shaad 	uint32_t extents_used = 0;
28756a34939Shaad 	uint32_t size_rest;
28856a34939Shaad 	uint32_t pv_extent_count = 0;
28956a34939Shaad 	alloc_policy_t alloc;
29056a34939Shaad 	struct logical_volume *lock_lv;
29156a34939Shaad 	struct lv_list *lvl;
29256a34939Shaad 	struct lv_segment *seg;
29356a34939Shaad 	uint32_t seg_extents;
29456a34939Shaad 	uint32_t sz, str;
29556a34939Shaad 	struct dm_list *pvh = NULL;
29656a34939Shaad 
29756a34939Shaad 	/* does LV exist? */
29856a34939Shaad 	if (!(lvl = find_lv_in_vg(vg, lp->lv_name))) {
29956a34939Shaad 		log_error("Logical volume %s not found in volume group %s",
30056a34939Shaad 			  lp->lv_name, lp->vg_name);
30156a34939Shaad 		return ECMD_FAILED;
30256a34939Shaad 	}
30356a34939Shaad 
30456a34939Shaad 	if (arg_count(cmd, stripes_ARG)) {
30556a34939Shaad 		if (vg->fid->fmt->features & FMT_SEGMENTS)
30656a34939Shaad 			lp->stripes = arg_uint_value(cmd, stripes_ARG, 1);
30756a34939Shaad 		else
30856a34939Shaad 			log_warn("Varied striping not supported. Ignoring.");
30956a34939Shaad 	}
31056a34939Shaad 
31156a34939Shaad 	if (arg_count(cmd, mirrors_ARG)) {
31256a34939Shaad 		if (vg->fid->fmt->features & FMT_SEGMENTS)
31356a34939Shaad 			lp->mirrors = arg_uint_value(cmd, mirrors_ARG, 1) + 1;
31456a34939Shaad 		else
31556a34939Shaad 			log_warn("Mirrors not supported. Ignoring.");
31656a34939Shaad 		if (arg_sign_value(cmd, mirrors_ARG, 0) == SIGN_MINUS) {
31756a34939Shaad 			log_error("Mirrors argument may not be negative");
31856a34939Shaad 			return EINVALID_CMD_LINE;
31956a34939Shaad 		}
32056a34939Shaad 	}
32156a34939Shaad 
322*7c604eeaShaad 	if (arg_count(cmd, stripesize_ARG) &&
323*7c604eeaShaad 	    !_validate_stripesize(cmd, vg, lp))
32456a34939Shaad 		return EINVALID_CMD_LINE;
32556a34939Shaad 
32656a34939Shaad 	lv = lvl->lv;
32756a34939Shaad 
32856a34939Shaad 	if (lv->status & LOCKED) {
32956a34939Shaad 		log_error("Can't resize locked LV %s", lv->name);
33056a34939Shaad 		return ECMD_FAILED;
33156a34939Shaad 	}
33256a34939Shaad 
33356a34939Shaad 	if (lv->status & CONVERTING) {
33456a34939Shaad 		log_error("Can't resize %s while lvconvert in progress", lv->name);
33556a34939Shaad 		return ECMD_FAILED;
33656a34939Shaad 	}
33756a34939Shaad 
33856a34939Shaad 	alloc = arg_uint_value(cmd, alloc_ARG, lv->alloc);
33956a34939Shaad 
34056a34939Shaad 	if (lp->size) {
34156a34939Shaad 		if (lp->size % vg->extent_size) {
34256a34939Shaad 			if (lp->sign == SIGN_MINUS)
34356a34939Shaad 				lp->size -= lp->size % vg->extent_size;
34456a34939Shaad 			else
34556a34939Shaad 				lp->size += vg->extent_size -
34656a34939Shaad 				    (lp->size % vg->extent_size);
34756a34939Shaad 
34856a34939Shaad 			log_print("Rounding up size to full physical extent %s",
34956a34939Shaad 				  display_size(cmd, (uint64_t) lp->size));
35056a34939Shaad 		}
35156a34939Shaad 
35256a34939Shaad 		lp->extents = lp->size / vg->extent_size;
35356a34939Shaad 	}
35456a34939Shaad 
35556a34939Shaad 	if (!(pvh = lp->argc ? create_pv_list(cmd->mem, vg, lp->argc,
35656a34939Shaad 						     lp->argv, 1) : &vg->pvs)) {
35756a34939Shaad 		stack;
35856a34939Shaad 		return ECMD_FAILED;
35956a34939Shaad 	}
36056a34939Shaad 
36156a34939Shaad 	switch(lp->percent) {
36256a34939Shaad 		case PERCENT_VG:
36356a34939Shaad 			lp->extents = lp->extents * vg->extent_count / 100;
36456a34939Shaad 			break;
36556a34939Shaad 		case PERCENT_FREE:
36656a34939Shaad 			lp->extents = lp->extents * vg->free_count / 100;
36756a34939Shaad 			break;
36856a34939Shaad 		case PERCENT_LV:
36956a34939Shaad 			lp->extents = lp->extents * lv->le_count / 100;
37056a34939Shaad 			break;
37156a34939Shaad 		case PERCENT_PVS:
372*7c604eeaShaad 			if (lp->argc) {
37356a34939Shaad 				pv_extent_count = pv_list_extents_free(pvh);
37456a34939Shaad 				lp->extents = lp->extents * pv_extent_count / 100;
375*7c604eeaShaad 			} else
376*7c604eeaShaad 				lp->extents = lp->extents * vg->extent_count / 100;
37756a34939Shaad 			break;
37856a34939Shaad 		case PERCENT_NONE:
37956a34939Shaad 			break;
38056a34939Shaad 	}
38156a34939Shaad 
38256a34939Shaad 	if (lp->sign == SIGN_PLUS)
38356a34939Shaad 		lp->extents += lv->le_count;
38456a34939Shaad 
38556a34939Shaad 	if (lp->sign == SIGN_MINUS) {
38656a34939Shaad 		if (lp->extents >= lv->le_count) {
38756a34939Shaad 			log_error("Unable to reduce %s below 1 extent",
38856a34939Shaad 				  lp->lv_name);
38956a34939Shaad 			return EINVALID_CMD_LINE;
39056a34939Shaad 		}
39156a34939Shaad 
39256a34939Shaad 		lp->extents = lv->le_count - lp->extents;
39356a34939Shaad 	}
39456a34939Shaad 
39556a34939Shaad 	if (!lp->extents) {
39656a34939Shaad 		log_error("New size of 0 not permitted");
39756a34939Shaad 		return EINVALID_CMD_LINE;
39856a34939Shaad 	}
39956a34939Shaad 
40056a34939Shaad 	if (lp->extents == lv->le_count) {
401*7c604eeaShaad 		if (!lp->resizefs) {
40256a34939Shaad 			log_error("New size (%d extents) matches existing size "
40356a34939Shaad 				  "(%d extents)", lp->extents, lv->le_count);
40456a34939Shaad 			return EINVALID_CMD_LINE;
40556a34939Shaad 		}
406*7c604eeaShaad 		lp->resize = LV_EXTEND; /* lets pretend zero size extension */
407*7c604eeaShaad 	}
40856a34939Shaad 
40956a34939Shaad 	seg_size = lp->extents - lv->le_count;
41056a34939Shaad 
41156a34939Shaad 	/* Use segment type of last segment */
41256a34939Shaad 	dm_list_iterate_items(seg, &lv->segments) {
41356a34939Shaad 		lp->segtype = seg->segtype;
41456a34939Shaad 	}
41556a34939Shaad 
41656a34939Shaad 	/* FIXME Support LVs with mixed segment types */
41756a34939Shaad 	if (lp->segtype != arg_ptr_value(cmd, type_ARG, lp->segtype)) {
41856a34939Shaad 		log_error("VolumeType does not match (%s)", lp->segtype->name);
41956a34939Shaad 		return EINVALID_CMD_LINE;
42056a34939Shaad 	}
42156a34939Shaad 
42256a34939Shaad 	/* If extending, find stripes, stripesize & size of last segment */
42356a34939Shaad 	if ((lp->extents > lv->le_count) &&
42456a34939Shaad 	    !(lp->stripes == 1 || (lp->stripes > 1 && lp->stripe_size))) {
42556a34939Shaad 		dm_list_iterate_items(seg, &lv->segments) {
42656a34939Shaad 			if (!seg_is_striped(seg))
42756a34939Shaad 				continue;
42856a34939Shaad 
42956a34939Shaad 			sz = seg->stripe_size;
43056a34939Shaad 			str = seg->area_count;
43156a34939Shaad 
432*7c604eeaShaad 			if ((seg_stripesize && seg_stripesize != sz &&
433*7c604eeaShaad 			     !lp->stripe_size) ||
43456a34939Shaad 			    (seg_stripes && seg_stripes != str && !lp->stripes)) {
43556a34939Shaad 				log_error("Please specify number of "
43656a34939Shaad 					  "stripes (-i) and stripesize (-I)");
43756a34939Shaad 				return EINVALID_CMD_LINE;
43856a34939Shaad 			}
43956a34939Shaad 
44056a34939Shaad 			seg_stripesize = sz;
44156a34939Shaad 			seg_stripes = str;
44256a34939Shaad 		}
44356a34939Shaad 
44456a34939Shaad 		if (!lp->stripes)
44556a34939Shaad 			lp->stripes = seg_stripes;
44656a34939Shaad 
44756a34939Shaad 		if (!lp->stripe_size && lp->stripes > 1) {
44856a34939Shaad 			if (seg_stripesize) {
44956a34939Shaad 				log_print("Using stripesize of last segment %s",
45056a34939Shaad 					  display_size(cmd, (uint64_t) seg_stripesize));
45156a34939Shaad 				lp->stripe_size = seg_stripesize;
45256a34939Shaad 			} else {
45356a34939Shaad 				lp->stripe_size =
45456a34939Shaad 					find_config_tree_int(cmd,
45556a34939Shaad 							"metadata/stripesize",
45656a34939Shaad 							DEFAULT_STRIPESIZE) * 2;
45756a34939Shaad 				log_print("Using default stripesize %s",
45856a34939Shaad 					  display_size(cmd, (uint64_t) lp->stripe_size));
45956a34939Shaad 			}
46056a34939Shaad 		}
46156a34939Shaad 	}
46256a34939Shaad 
46356a34939Shaad 	/* If extending, find mirrors of last segment */
46456a34939Shaad 	if ((lp->extents > lv->le_count)) {
46556a34939Shaad 		dm_list_iterate_back_items(seg, &lv->segments) {
46656a34939Shaad 			if (seg_is_mirrored(seg))
46756a34939Shaad 				seg_mirrors = lv_mirror_count(seg->lv);
46856a34939Shaad 			else
46956a34939Shaad 				seg_mirrors = 0;
47056a34939Shaad 			break;
47156a34939Shaad 		}
47256a34939Shaad 		if (!arg_count(cmd, mirrors_ARG) && seg_mirrors) {
47356a34939Shaad 			log_print("Extending %" PRIu32 " mirror images.",
47456a34939Shaad 				  seg_mirrors);
47556a34939Shaad 			lp->mirrors = seg_mirrors;
47656a34939Shaad 		}
47756a34939Shaad 		if ((arg_count(cmd, mirrors_ARG) || seg_mirrors) &&
47856a34939Shaad 		    (lp->mirrors != seg_mirrors)) {
47956a34939Shaad 			log_error("Cannot vary number of mirrors in LV yet.");
48056a34939Shaad 			return EINVALID_CMD_LINE;
48156a34939Shaad 		}
48256a34939Shaad 	}
48356a34939Shaad 
48456a34939Shaad 	/* If reducing, find stripes, stripesize & size of last segment */
48556a34939Shaad 	if (lp->extents < lv->le_count) {
48656a34939Shaad 		extents_used = 0;
48756a34939Shaad 
48856a34939Shaad 		if (lp->stripes || lp->stripe_size || lp->mirrors)
48956a34939Shaad 			log_error("Ignoring stripes, stripesize and mirrors "
49056a34939Shaad 				  "arguments when reducing");
49156a34939Shaad 
49256a34939Shaad 		dm_list_iterate_items(seg, &lv->segments) {
49356a34939Shaad 			seg_extents = seg->len;
49456a34939Shaad 
49556a34939Shaad 			if (seg_is_striped(seg)) {
49656a34939Shaad 				seg_stripesize = seg->stripe_size;
49756a34939Shaad 				seg_stripes = seg->area_count;
49856a34939Shaad 			}
49956a34939Shaad 
50056a34939Shaad 			if (seg_is_mirrored(seg))
50156a34939Shaad 				seg_mirrors = lv_mirror_count(seg->lv);
50256a34939Shaad 			else
50356a34939Shaad 				seg_mirrors = 0;
50456a34939Shaad 
50556a34939Shaad 			if (lp->extents <= extents_used + seg_extents)
50656a34939Shaad 				break;
50756a34939Shaad 
50856a34939Shaad 			extents_used += seg_extents;
50956a34939Shaad 		}
51056a34939Shaad 
51156a34939Shaad 		seg_size = lp->extents - extents_used;
51256a34939Shaad 		lp->stripe_size = seg_stripesize;
51356a34939Shaad 		lp->stripes = seg_stripes;
51456a34939Shaad 		lp->mirrors = seg_mirrors;
51556a34939Shaad 	}
51656a34939Shaad 
51756a34939Shaad 	if (lp->stripes > 1 && !lp->stripe_size) {
51856a34939Shaad 		log_error("Stripesize for striped segment should not be 0!");
51956a34939Shaad 		return EINVALID_CMD_LINE;
52056a34939Shaad 	}
52156a34939Shaad 
52256a34939Shaad 	if ((lp->stripes > 1)) {
52356a34939Shaad 		if (!(stripesize_extents = lp->stripe_size / vg->extent_size))
52456a34939Shaad 			stripesize_extents = 1;
52556a34939Shaad 
52656a34939Shaad 		if ((size_rest = seg_size % (lp->stripes * stripesize_extents))) {
52756a34939Shaad 			log_print("Rounding size (%d extents) down to stripe "
52856a34939Shaad 				  "boundary size for segment (%d extents)",
52956a34939Shaad 				  lp->extents, lp->extents - size_rest);
53056a34939Shaad 			lp->extents = lp->extents - size_rest;
53156a34939Shaad 		}
53256a34939Shaad 
53356a34939Shaad 		if (lp->stripe_size < STRIPE_SIZE_MIN) {
53456a34939Shaad 			log_error("Invalid stripe size %s",
53556a34939Shaad 				  display_size(cmd, (uint64_t) lp->stripe_size));
53656a34939Shaad 			return EINVALID_CMD_LINE;
53756a34939Shaad 		}
53856a34939Shaad 	}
53956a34939Shaad 
54056a34939Shaad 	if (lp->extents < lv->le_count) {
54156a34939Shaad 		if (lp->resize == LV_EXTEND) {
54256a34939Shaad 			log_error("New size given (%d extents) not larger "
54356a34939Shaad 				  "than existing size (%d extents)",
54456a34939Shaad 				  lp->extents, lv->le_count);
54556a34939Shaad 			return EINVALID_CMD_LINE;
54656a34939Shaad 		}
547*7c604eeaShaad 		lp->resize = LV_REDUCE;
548*7c604eeaShaad 	} else if (lp->extents > lv->le_count) {
54956a34939Shaad 		if (lp->resize == LV_REDUCE) {
55056a34939Shaad 			log_error("New size given (%d extents) not less than "
55156a34939Shaad 				  "existing size (%d extents)", lp->extents,
55256a34939Shaad 				  lv->le_count);
55356a34939Shaad 			return EINVALID_CMD_LINE;
55456a34939Shaad 		}
555*7c604eeaShaad 		lp->resize = LV_EXTEND;
55656a34939Shaad 	}
55756a34939Shaad 
55856a34939Shaad 	if (lv_is_origin(lv)) {
55956a34939Shaad 		if (lp->resize == LV_REDUCE) {
56056a34939Shaad 			log_error("Snapshot origin volumes cannot be reduced "
56156a34939Shaad 				  "in size yet.");
56256a34939Shaad 			return ECMD_FAILED;
56356a34939Shaad 		}
56456a34939Shaad 
56556a34939Shaad 		memset(&info, 0, sizeof(info));
56656a34939Shaad 
56756a34939Shaad 		if (lv_info(cmd, lv, &info, 0, 0) && info.exists) {
56856a34939Shaad 			log_error("Snapshot origin volumes can be resized "
56956a34939Shaad 				  "only while inactive: try lvchange -an");
57056a34939Shaad 			return ECMD_FAILED;
57156a34939Shaad 		}
57256a34939Shaad 	}
57356a34939Shaad 
574*7c604eeaShaad 	if ((lp->resize == LV_REDUCE) && lp->argc)
57556a34939Shaad 		log_warn("Ignoring PVs on command line when reducing");
57656a34939Shaad 
577*7c604eeaShaad 	/* Request confirmation before operations that are often mistakes. */
578*7c604eeaShaad 	if ((lp->resizefs || (lp->resize == LV_REDUCE)) &&
579*7c604eeaShaad 	    !_request_confirmation(cmd, vg, lv, lp)) {
580*7c604eeaShaad 		stack;
58156a34939Shaad 		return ECMD_FAILED;
58256a34939Shaad 	}
58356a34939Shaad 
58456a34939Shaad 	if (lp->resizefs) {
585*7c604eeaShaad 		if (!lp->nofsck &&
586*7c604eeaShaad 		    !_fsadm_cmd(cmd, vg, lp, FSADM_CMD_CHECK)) {
587*7c604eeaShaad 			stack;
58856a34939Shaad 			return ECMD_FAILED;
58956a34939Shaad 		}
59056a34939Shaad 
591*7c604eeaShaad 		if ((lp->resize == LV_REDUCE) &&
592*7c604eeaShaad 		    !_fsadm_cmd(cmd, vg, lp, FSADM_CMD_RESIZE)) {
593*7c604eeaShaad 			stack;
594*7c604eeaShaad 			return ECMD_FAILED;
595*7c604eeaShaad 		}
596*7c604eeaShaad 	}
597*7c604eeaShaad 
59856a34939Shaad 	if (!archive(vg)) {
59956a34939Shaad 		stack;
60056a34939Shaad 		return ECMD_FAILED;
60156a34939Shaad 	}
60256a34939Shaad 
60356a34939Shaad 	log_print("%sing logical volume %s to %s",
60456a34939Shaad 		  (lp->resize == LV_REDUCE) ? "Reduc" : "Extend",
60556a34939Shaad 		  lp->lv_name,
60656a34939Shaad 		  display_size(cmd, (uint64_t) lp->extents * vg->extent_size));
60756a34939Shaad 
60856a34939Shaad 	if (lp->resize == LV_REDUCE) {
60956a34939Shaad 		if (!lv_reduce(lv, lv->le_count - lp->extents)) {
61056a34939Shaad 			stack;
61156a34939Shaad 			return ECMD_FAILED;
61256a34939Shaad 		}
613*7c604eeaShaad 	} else if ((lp->extents > lv->le_count) && /* Ensure we extend */
614*7c604eeaShaad 		   !lv_extend(lv, lp->segtype, lp->stripes,
61556a34939Shaad 			      lp->stripe_size, lp->mirrors,
61656a34939Shaad 			      lp->extents - lv->le_count,
61756a34939Shaad 			      NULL, 0u, 0u, pvh, alloc)) {
61856a34939Shaad 		stack;
61956a34939Shaad 		return ECMD_FAILED;
62056a34939Shaad 	}
62156a34939Shaad 
62256a34939Shaad 	/* store vg on disk(s) */
62356a34939Shaad 	if (!vg_write(vg)) {
62456a34939Shaad 		stack;
62556a34939Shaad 		return ECMD_FAILED;
62656a34939Shaad 	}
62756a34939Shaad 
62856a34939Shaad 	/* If snapshot, must suspend all associated devices */
62956a34939Shaad 	if (lv_is_cow(lv))
63056a34939Shaad 		lock_lv = origin_from_cow(lv);
63156a34939Shaad 	else
63256a34939Shaad 		lock_lv = lv;
63356a34939Shaad 
63456a34939Shaad 	if (!suspend_lv(cmd, lock_lv)) {
63556a34939Shaad 		log_error("Failed to suspend %s", lp->lv_name);
63656a34939Shaad 		vg_revert(vg);
637*7c604eeaShaad 		backup(vg);
63856a34939Shaad 		return ECMD_FAILED;
63956a34939Shaad 	}
64056a34939Shaad 
64156a34939Shaad 	if (!vg_commit(vg)) {
64256a34939Shaad 		stack;
64356a34939Shaad 		resume_lv(cmd, lock_lv);
644*7c604eeaShaad 		backup(vg);
64556a34939Shaad 		return ECMD_FAILED;
64656a34939Shaad 	}
64756a34939Shaad 
64856a34939Shaad 	if (!resume_lv(cmd, lock_lv)) {
64956a34939Shaad 		log_error("Problem reactivating %s", lp->lv_name);
650*7c604eeaShaad 		backup(vg);
65156a34939Shaad 		return ECMD_FAILED;
65256a34939Shaad 	}
65356a34939Shaad 
654*7c604eeaShaad 	backup(vg);
655*7c604eeaShaad 
65656a34939Shaad 	log_print("Logical volume %s successfully resized", lp->lv_name);
65756a34939Shaad 
658*7c604eeaShaad 	if (lp->resizefs && (lp->resize == LV_EXTEND) &&
659*7c604eeaShaad 	    !_fsadm_cmd(cmd, vg, lp, FSADM_CMD_RESIZE)) {
66056a34939Shaad 		stack;
66156a34939Shaad 		return ECMD_FAILED;
66256a34939Shaad 	}
66356a34939Shaad 
66456a34939Shaad 	return ECMD_PROCESSED;
66556a34939Shaad }
66656a34939Shaad 
lvresize(struct cmd_context * cmd,int argc,char ** argv)66756a34939Shaad int lvresize(struct cmd_context *cmd, int argc, char **argv)
66856a34939Shaad {
66956a34939Shaad 	struct lvresize_params lp;
67056a34939Shaad 	struct volume_group *vg;
67156a34939Shaad 	int r;
67256a34939Shaad 
67356a34939Shaad 	memset(&lp, 0, sizeof(lp));
67456a34939Shaad 
67556a34939Shaad 	if (!_lvresize_params(cmd, argc, argv, &lp))
67656a34939Shaad 		return EINVALID_CMD_LINE;
67756a34939Shaad 
67856a34939Shaad 	log_verbose("Finding volume group %s", lp.vg_name);
679*7c604eeaShaad 	vg = vg_read_for_update(cmd, lp.vg_name, NULL, 0);
680*7c604eeaShaad 	if (vg_read_error(vg)) {
681*7c604eeaShaad 		vg_release(vg);
68256a34939Shaad 		stack;
68356a34939Shaad 		return ECMD_FAILED;
68456a34939Shaad 	}
68556a34939Shaad 
68656a34939Shaad 	if (!(r = _lvresize(cmd, vg, &lp)))
68756a34939Shaad 		stack;
68856a34939Shaad 
689*7c604eeaShaad 	unlock_and_release_vg(cmd, vg, lp.vg_name);
69056a34939Shaad 
69156a34939Shaad 	return r;
69256a34939Shaad }
693