xref: /dflybsd-src/contrib/lvm2/dist/tools/lvresize.c (revision 86d7f5d305c6adaa56ff4582ece9859d73106103)
186d7f5d3SJohn Marino /*	$NetBSD: lvresize.c,v 1.1.1.3 2009/12/02 00:25:53 haad Exp $	*/
286d7f5d3SJohn Marino 
386d7f5d3SJohn Marino /*
486d7f5d3SJohn Marino  * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
586d7f5d3SJohn Marino  * Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
686d7f5d3SJohn Marino  *
786d7f5d3SJohn Marino  * This file is part of LVM2.
886d7f5d3SJohn Marino  *
986d7f5d3SJohn Marino  * This copyrighted material is made available to anyone wishing to use,
1086d7f5d3SJohn Marino  * modify, copy, or redistribute it subject to the terms and conditions
1186d7f5d3SJohn Marino  * of the GNU Lesser General Public License v.2.1.
1286d7f5d3SJohn Marino  *
1386d7f5d3SJohn Marino  * You should have received a copy of the GNU Lesser General Public License
1486d7f5d3SJohn Marino  * along with this program; if not, write to the Free Software Foundation,
1586d7f5d3SJohn Marino  * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
1686d7f5d3SJohn Marino  */
1786d7f5d3SJohn Marino 
1886d7f5d3SJohn Marino #include "tools.h"
1986d7f5d3SJohn Marino 
2086d7f5d3SJohn Marino #define SIZE_BUF 128
2186d7f5d3SJohn Marino 
2286d7f5d3SJohn Marino struct lvresize_params {
2386d7f5d3SJohn Marino 	const char *vg_name;
2486d7f5d3SJohn Marino 	const char *lv_name;
2586d7f5d3SJohn Marino 
2686d7f5d3SJohn Marino 	uint32_t stripes;
2786d7f5d3SJohn Marino 	uint32_t stripe_size;
2886d7f5d3SJohn Marino 	uint32_t mirrors;
2986d7f5d3SJohn Marino 
3086d7f5d3SJohn Marino 	const struct segment_type *segtype;
3186d7f5d3SJohn Marino 
3286d7f5d3SJohn Marino 	/* size */
3386d7f5d3SJohn Marino 	uint32_t extents;
3486d7f5d3SJohn Marino 	uint64_t size;
3586d7f5d3SJohn Marino 	sign_t sign;
3686d7f5d3SJohn Marino 	percent_t percent;
3786d7f5d3SJohn Marino 
3886d7f5d3SJohn Marino 	enum {
3986d7f5d3SJohn Marino 		LV_ANY = 0,
4086d7f5d3SJohn Marino 		LV_REDUCE = 1,
4186d7f5d3SJohn Marino 		LV_EXTEND = 2
4286d7f5d3SJohn Marino 	} resize;
4386d7f5d3SJohn Marino 
4486d7f5d3SJohn Marino 	int resizefs;
4586d7f5d3SJohn Marino 	int nofsck;
4686d7f5d3SJohn Marino 
4786d7f5d3SJohn Marino 	int argc;
4886d7f5d3SJohn Marino 	char **argv;
4986d7f5d3SJohn Marino };
5086d7f5d3SJohn Marino 
_validate_stripesize(struct cmd_context * cmd,const struct volume_group * vg,struct lvresize_params * lp)5186d7f5d3SJohn Marino static int _validate_stripesize(struct cmd_context *cmd,
5286d7f5d3SJohn Marino 				const struct volume_group *vg,
5386d7f5d3SJohn Marino 				struct lvresize_params *lp)
5486d7f5d3SJohn Marino {
5586d7f5d3SJohn Marino 	if (arg_sign_value(cmd, stripesize_ARG, 0) == SIGN_MINUS) {
5686d7f5d3SJohn Marino 		log_error("Stripesize may not be negative.");
5786d7f5d3SJohn Marino 		return 0;
5886d7f5d3SJohn Marino 	}
5986d7f5d3SJohn Marino 
6086d7f5d3SJohn Marino 	if (arg_uint_value(cmd, stripesize_ARG, 0) > STRIPE_SIZE_LIMIT * 2) {
6186d7f5d3SJohn Marino 		log_error("Stripe size cannot be larger than %s",
6286d7f5d3SJohn Marino 			  display_size(cmd, (uint64_t) STRIPE_SIZE_LIMIT));
6386d7f5d3SJohn Marino 		return 0;
6486d7f5d3SJohn Marino 	}
6586d7f5d3SJohn Marino 
6686d7f5d3SJohn Marino 	if (!(vg->fid->fmt->features & FMT_SEGMENTS))
6786d7f5d3SJohn Marino 		log_warn("Varied stripesize not supported. Ignoring.");
6886d7f5d3SJohn Marino 	else if (arg_uint_value(cmd, stripesize_ARG, 0) > vg->extent_size * 2) {
6986d7f5d3SJohn Marino 		log_error("Reducing stripe size %s to maximum, "
7086d7f5d3SJohn Marino 			  "physical extent size %s",
7186d7f5d3SJohn Marino 			  display_size(cmd,
7286d7f5d3SJohn Marino 				       (uint64_t) arg_uint_value(cmd, stripesize_ARG, 0)),
7386d7f5d3SJohn Marino 			  display_size(cmd, (uint64_t) vg->extent_size));
7486d7f5d3SJohn Marino 		lp->stripe_size = vg->extent_size;
7586d7f5d3SJohn Marino 	} else
7686d7f5d3SJohn Marino 		lp->stripe_size = arg_uint_value(cmd, stripesize_ARG, 0);
7786d7f5d3SJohn Marino 
7886d7f5d3SJohn Marino 	if (lp->mirrors) {
7986d7f5d3SJohn Marino 		log_error("Mirrors and striping cannot be combined yet.");
8086d7f5d3SJohn Marino 		return 0;
8186d7f5d3SJohn Marino 	}
8286d7f5d3SJohn Marino 	if (lp->stripe_size & (lp->stripe_size - 1)) {
8386d7f5d3SJohn Marino 		log_error("Stripe size must be power of 2");
8486d7f5d3SJohn Marino 		return 0;
8586d7f5d3SJohn Marino 	}
8686d7f5d3SJohn Marino 
8786d7f5d3SJohn Marino 	return 1;
8886d7f5d3SJohn Marino }
8986d7f5d3SJohn Marino 
_request_confirmation(struct cmd_context * cmd,const struct volume_group * vg,const struct logical_volume * lv,const struct lvresize_params * lp)9086d7f5d3SJohn Marino static int _request_confirmation(struct cmd_context *cmd,
9186d7f5d3SJohn Marino 				 const struct volume_group *vg,
9286d7f5d3SJohn Marino 				 const struct logical_volume *lv,
9386d7f5d3SJohn Marino 				 const struct lvresize_params *lp)
9486d7f5d3SJohn Marino {
9586d7f5d3SJohn Marino 	struct lvinfo info;
9686d7f5d3SJohn Marino 
9786d7f5d3SJohn Marino 	memset(&info, 0, sizeof(info));
9886d7f5d3SJohn Marino 
9986d7f5d3SJohn Marino 	if (!lv_info(cmd, lv, &info, 1, 0) && driver_version(NULL, 0)) {
10086d7f5d3SJohn Marino 		log_error("lv_info failed: aborting");
10186d7f5d3SJohn Marino 		return 0;
10286d7f5d3SJohn Marino 	}
10386d7f5d3SJohn Marino 
10486d7f5d3SJohn Marino 	if (lp->resizefs) {
10586d7f5d3SJohn Marino 		if (!info.exists) {
10686d7f5d3SJohn Marino 			log_error("Logical volume %s must be activated "
10786d7f5d3SJohn Marino 				  "before resizing filesystem", lp->lv_name);
10886d7f5d3SJohn Marino 			return 0;
10986d7f5d3SJohn Marino 		}
11086d7f5d3SJohn Marino 		return 1;
11186d7f5d3SJohn Marino 	}
11286d7f5d3SJohn Marino 
11386d7f5d3SJohn Marino 	if (!info.exists)
11486d7f5d3SJohn Marino 		return 1;
11586d7f5d3SJohn Marino 
11686d7f5d3SJohn Marino 	log_warn("WARNING: Reducing active%s logical volume to %s",
11786d7f5d3SJohn Marino 		 info.open_count ? " and open" : "",
11886d7f5d3SJohn Marino 		 display_size(cmd, (uint64_t) lp->extents * vg->extent_size));
11986d7f5d3SJohn Marino 
12086d7f5d3SJohn Marino 	log_warn("THIS MAY DESTROY YOUR DATA (filesystem etc.)");
12186d7f5d3SJohn Marino 
12286d7f5d3SJohn Marino 	if (!arg_count(cmd, force_ARG)) {
12386d7f5d3SJohn Marino 		if (yes_no_prompt("Do you really want to reduce %s? [y/n]: ",
12486d7f5d3SJohn Marino 				  lp->lv_name) == 'n') {
12586d7f5d3SJohn Marino 			log_print("Logical volume %s NOT reduced", lp->lv_name);
12686d7f5d3SJohn Marino 			return 0;
12786d7f5d3SJohn Marino 		}
12886d7f5d3SJohn Marino 		if (sigint_caught())
12986d7f5d3SJohn Marino 			return 0;
13086d7f5d3SJohn Marino 	}
13186d7f5d3SJohn Marino 
13286d7f5d3SJohn Marino 	return 1;
13386d7f5d3SJohn Marino }
13486d7f5d3SJohn Marino 
13586d7f5d3SJohn Marino enum fsadm_cmd_e { FSADM_CMD_CHECK, FSADM_CMD_RESIZE };
13686d7f5d3SJohn Marino #define FSADM_CMD "fsadm"
13786d7f5d3SJohn Marino #define FSADM_CMD_MAX_ARGS 6
13886d7f5d3SJohn Marino 
13986d7f5d3SJohn Marino /*
14086d7f5d3SJohn Marino  * FSADM_CMD --dry-run --verbose --force check lv_path
14186d7f5d3SJohn Marino  * FSADM_CMD --dry-run --verbose --force resize lv_path size
14286d7f5d3SJohn Marino  */
_fsadm_cmd(struct cmd_context * cmd,const struct volume_group * vg,const struct lvresize_params * lp,enum fsadm_cmd_e fcmd)14386d7f5d3SJohn Marino static int _fsadm_cmd(struct cmd_context *cmd,
14486d7f5d3SJohn Marino 		      const struct volume_group *vg,
14586d7f5d3SJohn Marino 		      const struct lvresize_params *lp,
14686d7f5d3SJohn Marino 		      enum fsadm_cmd_e fcmd)
14786d7f5d3SJohn Marino {
14886d7f5d3SJohn Marino 	char lv_path[PATH_MAX];
14986d7f5d3SJohn Marino 	char size_buf[SIZE_BUF];
15086d7f5d3SJohn Marino 	const char *argv[FSADM_CMD_MAX_ARGS + 2];
15186d7f5d3SJohn Marino 	unsigned i = 0;
15286d7f5d3SJohn Marino 
15386d7f5d3SJohn Marino 	argv[i++] = FSADM_CMD;
15486d7f5d3SJohn Marino 
15586d7f5d3SJohn Marino 	if (test_mode())
15686d7f5d3SJohn Marino 		argv[i++] = "--dry-run";
15786d7f5d3SJohn Marino 
15886d7f5d3SJohn Marino 	if (verbose_level() >= _LOG_NOTICE)
15986d7f5d3SJohn Marino 		argv[i++] = "--verbose";
16086d7f5d3SJohn Marino 
16186d7f5d3SJohn Marino 	if (arg_count(cmd, force_ARG))
16286d7f5d3SJohn Marino 		argv[i++] = "--force";
16386d7f5d3SJohn Marino 
16486d7f5d3SJohn Marino 	argv[i++] = (fcmd == FSADM_CMD_RESIZE) ? "resize" : "check";
16586d7f5d3SJohn Marino 
16686d7f5d3SJohn Marino 	if (dm_snprintf(lv_path, PATH_MAX, "%s%s/%s", cmd->dev_dir, lp->vg_name,
16786d7f5d3SJohn Marino 			lp->lv_name) < 0) {
16886d7f5d3SJohn Marino 		log_error("Couldn't create LV path for %s", lp->lv_name);
16986d7f5d3SJohn Marino 		return 0;
17086d7f5d3SJohn Marino 	}
17186d7f5d3SJohn Marino 
17286d7f5d3SJohn Marino 	argv[i++] = lv_path;
17386d7f5d3SJohn Marino 
17486d7f5d3SJohn Marino 	if (fcmd == FSADM_CMD_RESIZE) {
17586d7f5d3SJohn Marino 		if (dm_snprintf(size_buf, SIZE_BUF, "%" PRIu64 "K",
17686d7f5d3SJohn Marino 				(uint64_t) lp->extents * vg->extent_size / 2) < 0) {
17786d7f5d3SJohn Marino 			log_error("Couldn't generate new LV size string");
17886d7f5d3SJohn Marino 			return 0;
17986d7f5d3SJohn Marino 		}
18086d7f5d3SJohn Marino 
18186d7f5d3SJohn Marino 		argv[i++] = size_buf;
18286d7f5d3SJohn Marino 	}
18386d7f5d3SJohn Marino 
18486d7f5d3SJohn Marino 	argv[i] = NULL;
18586d7f5d3SJohn Marino 
18686d7f5d3SJohn Marino 	return exec_cmd(cmd, argv);
18786d7f5d3SJohn Marino }
18886d7f5d3SJohn Marino 
_lvresize_params(struct cmd_context * cmd,int argc,char ** argv,struct lvresize_params * lp)18986d7f5d3SJohn Marino static int _lvresize_params(struct cmd_context *cmd, int argc, char **argv,
19086d7f5d3SJohn Marino 			    struct lvresize_params *lp)
19186d7f5d3SJohn Marino {
19286d7f5d3SJohn Marino 	const char *cmd_name;
19386d7f5d3SJohn Marino 	char *st;
19486d7f5d3SJohn Marino 	unsigned dev_dir_found = 0;
19586d7f5d3SJohn Marino 
19686d7f5d3SJohn Marino 	lp->sign = SIGN_NONE;
19786d7f5d3SJohn Marino 	lp->resize = LV_ANY;
19886d7f5d3SJohn Marino 
19986d7f5d3SJohn Marino 	cmd_name = command_name(cmd);
20086d7f5d3SJohn Marino 	if (!strcmp(cmd_name, "lvreduce"))
20186d7f5d3SJohn Marino 		lp->resize = LV_REDUCE;
20286d7f5d3SJohn Marino 	if (!strcmp(cmd_name, "lvextend"))
20386d7f5d3SJohn Marino 		lp->resize = LV_EXTEND;
20486d7f5d3SJohn Marino 
20586d7f5d3SJohn Marino 	/*
20686d7f5d3SJohn Marino 	 * Allow omission of extents and size if the user has given us
20786d7f5d3SJohn Marino 	 * one or more PVs.  Most likely, the intent was "resize this
20886d7f5d3SJohn Marino 	 * LV the best you can with these PVs"
20986d7f5d3SJohn Marino 	 */
21086d7f5d3SJohn Marino 	if ((arg_count(cmd, extents_ARG) + arg_count(cmd, size_ARG) == 0) &&
21186d7f5d3SJohn Marino 	    (argc >= 2)) {
21286d7f5d3SJohn Marino 		lp->extents = 100;
21386d7f5d3SJohn Marino 		lp->percent = PERCENT_PVS;
21486d7f5d3SJohn Marino 		lp->sign = SIGN_PLUS;
21586d7f5d3SJohn Marino 	} else if ((arg_count(cmd, extents_ARG) +
21686d7f5d3SJohn Marino 		    arg_count(cmd, size_ARG) != 1)) {
21786d7f5d3SJohn Marino 		log_error("Please specify either size or extents but not "
21886d7f5d3SJohn Marino 			  "both.");
21986d7f5d3SJohn Marino 		return 0;
22086d7f5d3SJohn Marino 	}
22186d7f5d3SJohn Marino 
22286d7f5d3SJohn Marino 	if (arg_count(cmd, extents_ARG)) {
22386d7f5d3SJohn Marino 		lp->extents = arg_uint_value(cmd, extents_ARG, 0);
22486d7f5d3SJohn Marino 		lp->sign = arg_sign_value(cmd, extents_ARG, SIGN_NONE);
22586d7f5d3SJohn Marino 		lp->percent = arg_percent_value(cmd, extents_ARG, PERCENT_NONE);
22686d7f5d3SJohn Marino 	}
22786d7f5d3SJohn Marino 
22886d7f5d3SJohn Marino 	/* Size returned in kilobyte units; held in sectors */
22986d7f5d3SJohn Marino 	if (arg_count(cmd, size_ARG)) {
23086d7f5d3SJohn Marino 		lp->size = arg_uint64_value(cmd, size_ARG, UINT64_C(0));
23186d7f5d3SJohn Marino 		lp->sign = arg_sign_value(cmd, size_ARG, SIGN_NONE);
23286d7f5d3SJohn Marino 		lp->percent = PERCENT_NONE;
23386d7f5d3SJohn Marino 	}
23486d7f5d3SJohn Marino 
23586d7f5d3SJohn Marino 	if (lp->resize == LV_EXTEND && lp->sign == SIGN_MINUS) {
23686d7f5d3SJohn Marino 		log_error("Negative argument not permitted - use lvreduce");
23786d7f5d3SJohn Marino 		return 0;
23886d7f5d3SJohn Marino 	}
23986d7f5d3SJohn Marino 
24086d7f5d3SJohn Marino 	if (lp->resize == LV_REDUCE && lp->sign == SIGN_PLUS) {
24186d7f5d3SJohn Marino 		log_error("Positive sign not permitted - use lvextend");
24286d7f5d3SJohn Marino 		return 0;
24386d7f5d3SJohn Marino 	}
24486d7f5d3SJohn Marino 
24586d7f5d3SJohn Marino 	lp->resizefs = arg_is_set(cmd, resizefs_ARG);
24686d7f5d3SJohn Marino 	lp->nofsck = arg_is_set(cmd, nofsck_ARG);
24786d7f5d3SJohn Marino 
24886d7f5d3SJohn Marino 	if (!argc) {
24986d7f5d3SJohn Marino 		log_error("Please provide the logical volume name");
25086d7f5d3SJohn Marino 		return 0;
25186d7f5d3SJohn Marino 	}
25286d7f5d3SJohn Marino 
25386d7f5d3SJohn Marino 	lp->lv_name = argv[0];
25486d7f5d3SJohn Marino 	argv++;
25586d7f5d3SJohn Marino 	argc--;
25686d7f5d3SJohn Marino 
25786d7f5d3SJohn Marino 	if (!(lp->lv_name = skip_dev_dir(cmd, lp->lv_name, &dev_dir_found)) ||
25886d7f5d3SJohn Marino 	    !(lp->vg_name = extract_vgname(cmd, lp->lv_name))) {
25986d7f5d3SJohn Marino 		log_error("Please provide a volume group name");
26086d7f5d3SJohn Marino 		return 0;
26186d7f5d3SJohn Marino 	}
26286d7f5d3SJohn Marino 
26386d7f5d3SJohn Marino 	if (!validate_name(lp->vg_name)) {
26486d7f5d3SJohn Marino 		log_error("Volume group name %s has invalid characters",
26586d7f5d3SJohn Marino 			  lp->vg_name);
26686d7f5d3SJohn Marino 		return 0;
26786d7f5d3SJohn Marino 	}
26886d7f5d3SJohn Marino 
26986d7f5d3SJohn Marino 	if ((st = strrchr(lp->lv_name, '/')))
27086d7f5d3SJohn Marino 		lp->lv_name = st + 1;
27186d7f5d3SJohn Marino 
27286d7f5d3SJohn Marino 	lp->argc = argc;
27386d7f5d3SJohn Marino 	lp->argv = argv;
27486d7f5d3SJohn Marino 
27586d7f5d3SJohn Marino 	return 1;
27686d7f5d3SJohn Marino }
27786d7f5d3SJohn Marino 
_lvresize(struct cmd_context * cmd,struct volume_group * vg,struct lvresize_params * lp)27886d7f5d3SJohn Marino static int _lvresize(struct cmd_context *cmd, struct volume_group *vg,
27986d7f5d3SJohn Marino 		     struct lvresize_params *lp)
28086d7f5d3SJohn Marino {
28186d7f5d3SJohn Marino 	struct logical_volume *lv;
28286d7f5d3SJohn Marino 	struct lvinfo info;
28386d7f5d3SJohn Marino 	uint32_t stripesize_extents = 0;
28486d7f5d3SJohn Marino 	uint32_t seg_stripes = 0, seg_stripesize = 0, seg_size = 0;
28586d7f5d3SJohn Marino 	uint32_t seg_mirrors = 0;
28686d7f5d3SJohn Marino 	uint32_t extents_used = 0;
28786d7f5d3SJohn Marino 	uint32_t size_rest;
28886d7f5d3SJohn Marino 	uint32_t pv_extent_count = 0;
28986d7f5d3SJohn Marino 	alloc_policy_t alloc;
29086d7f5d3SJohn Marino 	struct logical_volume *lock_lv;
29186d7f5d3SJohn Marino 	struct lv_list *lvl;
29286d7f5d3SJohn Marino 	struct lv_segment *seg;
29386d7f5d3SJohn Marino 	uint32_t seg_extents;
29486d7f5d3SJohn Marino 	uint32_t sz, str;
29586d7f5d3SJohn Marino 	struct dm_list *pvh = NULL;
29686d7f5d3SJohn Marino 
29786d7f5d3SJohn Marino 	/* does LV exist? */
29886d7f5d3SJohn Marino 	if (!(lvl = find_lv_in_vg(vg, lp->lv_name))) {
29986d7f5d3SJohn Marino 		log_error("Logical volume %s not found in volume group %s",
30086d7f5d3SJohn Marino 			  lp->lv_name, lp->vg_name);
30186d7f5d3SJohn Marino 		return ECMD_FAILED;
30286d7f5d3SJohn Marino 	}
30386d7f5d3SJohn Marino 
30486d7f5d3SJohn Marino 	if (arg_count(cmd, stripes_ARG)) {
30586d7f5d3SJohn Marino 		if (vg->fid->fmt->features & FMT_SEGMENTS)
30686d7f5d3SJohn Marino 			lp->stripes = arg_uint_value(cmd, stripes_ARG, 1);
30786d7f5d3SJohn Marino 		else
30886d7f5d3SJohn Marino 			log_warn("Varied striping not supported. Ignoring.");
30986d7f5d3SJohn Marino 	}
31086d7f5d3SJohn Marino 
31186d7f5d3SJohn Marino 	if (arg_count(cmd, mirrors_ARG)) {
31286d7f5d3SJohn Marino 		if (vg->fid->fmt->features & FMT_SEGMENTS)
31386d7f5d3SJohn Marino 			lp->mirrors = arg_uint_value(cmd, mirrors_ARG, 1) + 1;
31486d7f5d3SJohn Marino 		else
31586d7f5d3SJohn Marino 			log_warn("Mirrors not supported. Ignoring.");
31686d7f5d3SJohn Marino 		if (arg_sign_value(cmd, mirrors_ARG, 0) == SIGN_MINUS) {
31786d7f5d3SJohn Marino 			log_error("Mirrors argument may not be negative");
31886d7f5d3SJohn Marino 			return EINVALID_CMD_LINE;
31986d7f5d3SJohn Marino 		}
32086d7f5d3SJohn Marino 	}
32186d7f5d3SJohn Marino 
32286d7f5d3SJohn Marino 	if (arg_count(cmd, stripesize_ARG) &&
32386d7f5d3SJohn Marino 	    !_validate_stripesize(cmd, vg, lp))
32486d7f5d3SJohn Marino 		return EINVALID_CMD_LINE;
32586d7f5d3SJohn Marino 
32686d7f5d3SJohn Marino 	lv = lvl->lv;
32786d7f5d3SJohn Marino 
32886d7f5d3SJohn Marino 	if (lv->status & LOCKED) {
32986d7f5d3SJohn Marino 		log_error("Can't resize locked LV %s", lv->name);
33086d7f5d3SJohn Marino 		return ECMD_FAILED;
33186d7f5d3SJohn Marino 	}
33286d7f5d3SJohn Marino 
33386d7f5d3SJohn Marino 	if (lv->status & CONVERTING) {
33486d7f5d3SJohn Marino 		log_error("Can't resize %s while lvconvert in progress", lv->name);
33586d7f5d3SJohn Marino 		return ECMD_FAILED;
33686d7f5d3SJohn Marino 	}
33786d7f5d3SJohn Marino 
33886d7f5d3SJohn Marino 	alloc = arg_uint_value(cmd, alloc_ARG, lv->alloc);
33986d7f5d3SJohn Marino 
34086d7f5d3SJohn Marino 	if (lp->size) {
34186d7f5d3SJohn Marino 		if (lp->size % vg->extent_size) {
34286d7f5d3SJohn Marino 			if (lp->sign == SIGN_MINUS)
34386d7f5d3SJohn Marino 				lp->size -= lp->size % vg->extent_size;
34486d7f5d3SJohn Marino 			else
34586d7f5d3SJohn Marino 				lp->size += vg->extent_size -
34686d7f5d3SJohn Marino 				    (lp->size % vg->extent_size);
34786d7f5d3SJohn Marino 
34886d7f5d3SJohn Marino 			log_print("Rounding up size to full physical extent %s",
34986d7f5d3SJohn Marino 				  display_size(cmd, (uint64_t) lp->size));
35086d7f5d3SJohn Marino 		}
35186d7f5d3SJohn Marino 
35286d7f5d3SJohn Marino 		lp->extents = lp->size / vg->extent_size;
35386d7f5d3SJohn Marino 	}
35486d7f5d3SJohn Marino 
35586d7f5d3SJohn Marino 	if (!(pvh = lp->argc ? create_pv_list(cmd->mem, vg, lp->argc,
35686d7f5d3SJohn Marino 						     lp->argv, 1) : &vg->pvs)) {
35786d7f5d3SJohn Marino 		stack;
35886d7f5d3SJohn Marino 		return ECMD_FAILED;
35986d7f5d3SJohn Marino 	}
36086d7f5d3SJohn Marino 
36186d7f5d3SJohn Marino 	switch(lp->percent) {
36286d7f5d3SJohn Marino 		case PERCENT_VG:
36386d7f5d3SJohn Marino 			lp->extents = lp->extents * vg->extent_count / 100;
36486d7f5d3SJohn Marino 			break;
36586d7f5d3SJohn Marino 		case PERCENT_FREE:
36686d7f5d3SJohn Marino 			lp->extents = lp->extents * vg->free_count / 100;
36786d7f5d3SJohn Marino 			break;
36886d7f5d3SJohn Marino 		case PERCENT_LV:
36986d7f5d3SJohn Marino 			lp->extents = lp->extents * lv->le_count / 100;
37086d7f5d3SJohn Marino 			break;
37186d7f5d3SJohn Marino 		case PERCENT_PVS:
37286d7f5d3SJohn Marino 			if (lp->argc) {
37386d7f5d3SJohn Marino 				pv_extent_count = pv_list_extents_free(pvh);
37486d7f5d3SJohn Marino 				lp->extents = lp->extents * pv_extent_count / 100;
37586d7f5d3SJohn Marino 			} else
37686d7f5d3SJohn Marino 				lp->extents = lp->extents * vg->extent_count / 100;
37786d7f5d3SJohn Marino 			break;
37886d7f5d3SJohn Marino 		case PERCENT_NONE:
37986d7f5d3SJohn Marino 			break;
38086d7f5d3SJohn Marino 	}
38186d7f5d3SJohn Marino 
38286d7f5d3SJohn Marino 	if (lp->sign == SIGN_PLUS)
38386d7f5d3SJohn Marino 		lp->extents += lv->le_count;
38486d7f5d3SJohn Marino 
38586d7f5d3SJohn Marino 	if (lp->sign == SIGN_MINUS) {
38686d7f5d3SJohn Marino 		if (lp->extents >= lv->le_count) {
38786d7f5d3SJohn Marino 			log_error("Unable to reduce %s below 1 extent",
38886d7f5d3SJohn Marino 				  lp->lv_name);
38986d7f5d3SJohn Marino 			return EINVALID_CMD_LINE;
39086d7f5d3SJohn Marino 		}
39186d7f5d3SJohn Marino 
39286d7f5d3SJohn Marino 		lp->extents = lv->le_count - lp->extents;
39386d7f5d3SJohn Marino 	}
39486d7f5d3SJohn Marino 
39586d7f5d3SJohn Marino 	if (!lp->extents) {
39686d7f5d3SJohn Marino 		log_error("New size of 0 not permitted");
39786d7f5d3SJohn Marino 		return EINVALID_CMD_LINE;
39886d7f5d3SJohn Marino 	}
39986d7f5d3SJohn Marino 
40086d7f5d3SJohn Marino 	if (lp->extents == lv->le_count) {
40186d7f5d3SJohn Marino 		if (!lp->resizefs) {
40286d7f5d3SJohn Marino 			log_error("New size (%d extents) matches existing size "
40386d7f5d3SJohn Marino 				  "(%d extents)", lp->extents, lv->le_count);
40486d7f5d3SJohn Marino 			return EINVALID_CMD_LINE;
40586d7f5d3SJohn Marino 		}
40686d7f5d3SJohn Marino 		lp->resize = LV_EXTEND; /* lets pretend zero size extension */
40786d7f5d3SJohn Marino 	}
40886d7f5d3SJohn Marino 
40986d7f5d3SJohn Marino 	seg_size = lp->extents - lv->le_count;
41086d7f5d3SJohn Marino 
41186d7f5d3SJohn Marino 	/* Use segment type of last segment */
41286d7f5d3SJohn Marino 	dm_list_iterate_items(seg, &lv->segments) {
41386d7f5d3SJohn Marino 		lp->segtype = seg->segtype;
41486d7f5d3SJohn Marino 	}
41586d7f5d3SJohn Marino 
41686d7f5d3SJohn Marino 	/* FIXME Support LVs with mixed segment types */
41786d7f5d3SJohn Marino 	if (lp->segtype != arg_ptr_value(cmd, type_ARG, lp->segtype)) {
41886d7f5d3SJohn Marino 		log_error("VolumeType does not match (%s)", lp->segtype->name);
41986d7f5d3SJohn Marino 		return EINVALID_CMD_LINE;
42086d7f5d3SJohn Marino 	}
42186d7f5d3SJohn Marino 
42286d7f5d3SJohn Marino 	/* If extending, find stripes, stripesize & size of last segment */
42386d7f5d3SJohn Marino 	if ((lp->extents > lv->le_count) &&
42486d7f5d3SJohn Marino 	    !(lp->stripes == 1 || (lp->stripes > 1 && lp->stripe_size))) {
42586d7f5d3SJohn Marino 		dm_list_iterate_items(seg, &lv->segments) {
42686d7f5d3SJohn Marino 			if (!seg_is_striped(seg))
42786d7f5d3SJohn Marino 				continue;
42886d7f5d3SJohn Marino 
42986d7f5d3SJohn Marino 			sz = seg->stripe_size;
43086d7f5d3SJohn Marino 			str = seg->area_count;
43186d7f5d3SJohn Marino 
43286d7f5d3SJohn Marino 			if ((seg_stripesize && seg_stripesize != sz &&
43386d7f5d3SJohn Marino 			     !lp->stripe_size) ||
43486d7f5d3SJohn Marino 			    (seg_stripes && seg_stripes != str && !lp->stripes)) {
43586d7f5d3SJohn Marino 				log_error("Please specify number of "
43686d7f5d3SJohn Marino 					  "stripes (-i) and stripesize (-I)");
43786d7f5d3SJohn Marino 				return EINVALID_CMD_LINE;
43886d7f5d3SJohn Marino 			}
43986d7f5d3SJohn Marino 
44086d7f5d3SJohn Marino 			seg_stripesize = sz;
44186d7f5d3SJohn Marino 			seg_stripes = str;
44286d7f5d3SJohn Marino 		}
44386d7f5d3SJohn Marino 
44486d7f5d3SJohn Marino 		if (!lp->stripes)
44586d7f5d3SJohn Marino 			lp->stripes = seg_stripes;
44686d7f5d3SJohn Marino 
44786d7f5d3SJohn Marino 		if (!lp->stripe_size && lp->stripes > 1) {
44886d7f5d3SJohn Marino 			if (seg_stripesize) {
44986d7f5d3SJohn Marino 				log_print("Using stripesize of last segment %s",
45086d7f5d3SJohn Marino 					  display_size(cmd, (uint64_t) seg_stripesize));
45186d7f5d3SJohn Marino 				lp->stripe_size = seg_stripesize;
45286d7f5d3SJohn Marino 			} else {
45386d7f5d3SJohn Marino 				lp->stripe_size =
45486d7f5d3SJohn Marino 					find_config_tree_int(cmd,
45586d7f5d3SJohn Marino 							"metadata/stripesize",
45686d7f5d3SJohn Marino 							DEFAULT_STRIPESIZE) * 2;
45786d7f5d3SJohn Marino 				log_print("Using default stripesize %s",
45886d7f5d3SJohn Marino 					  display_size(cmd, (uint64_t) lp->stripe_size));
45986d7f5d3SJohn Marino 			}
46086d7f5d3SJohn Marino 		}
46186d7f5d3SJohn Marino 	}
46286d7f5d3SJohn Marino 
46386d7f5d3SJohn Marino 	/* If extending, find mirrors of last segment */
46486d7f5d3SJohn Marino 	if ((lp->extents > lv->le_count)) {
46586d7f5d3SJohn Marino 		dm_list_iterate_back_items(seg, &lv->segments) {
46686d7f5d3SJohn Marino 			if (seg_is_mirrored(seg))
46786d7f5d3SJohn Marino 				seg_mirrors = lv_mirror_count(seg->lv);
46886d7f5d3SJohn Marino 			else
46986d7f5d3SJohn Marino 				seg_mirrors = 0;
47086d7f5d3SJohn Marino 			break;
47186d7f5d3SJohn Marino 		}
47286d7f5d3SJohn Marino 		if (!arg_count(cmd, mirrors_ARG) && seg_mirrors) {
47386d7f5d3SJohn Marino 			log_print("Extending %" PRIu32 " mirror images.",
47486d7f5d3SJohn Marino 				  seg_mirrors);
47586d7f5d3SJohn Marino 			lp->mirrors = seg_mirrors;
47686d7f5d3SJohn Marino 		}
47786d7f5d3SJohn Marino 		if ((arg_count(cmd, mirrors_ARG) || seg_mirrors) &&
47886d7f5d3SJohn Marino 		    (lp->mirrors != seg_mirrors)) {
47986d7f5d3SJohn Marino 			log_error("Cannot vary number of mirrors in LV yet.");
48086d7f5d3SJohn Marino 			return EINVALID_CMD_LINE;
48186d7f5d3SJohn Marino 		}
48286d7f5d3SJohn Marino 	}
48386d7f5d3SJohn Marino 
48486d7f5d3SJohn Marino 	/* If reducing, find stripes, stripesize & size of last segment */
48586d7f5d3SJohn Marino 	if (lp->extents < lv->le_count) {
48686d7f5d3SJohn Marino 		extents_used = 0;
48786d7f5d3SJohn Marino 
48886d7f5d3SJohn Marino 		if (lp->stripes || lp->stripe_size || lp->mirrors)
48986d7f5d3SJohn Marino 			log_error("Ignoring stripes, stripesize and mirrors "
49086d7f5d3SJohn Marino 				  "arguments when reducing");
49186d7f5d3SJohn Marino 
49286d7f5d3SJohn Marino 		dm_list_iterate_items(seg, &lv->segments) {
49386d7f5d3SJohn Marino 			seg_extents = seg->len;
49486d7f5d3SJohn Marino 
49586d7f5d3SJohn Marino 			if (seg_is_striped(seg)) {
49686d7f5d3SJohn Marino 				seg_stripesize = seg->stripe_size;
49786d7f5d3SJohn Marino 				seg_stripes = seg->area_count;
49886d7f5d3SJohn Marino 			}
49986d7f5d3SJohn Marino 
50086d7f5d3SJohn Marino 			if (seg_is_mirrored(seg))
50186d7f5d3SJohn Marino 				seg_mirrors = lv_mirror_count(seg->lv);
50286d7f5d3SJohn Marino 			else
50386d7f5d3SJohn Marino 				seg_mirrors = 0;
50486d7f5d3SJohn Marino 
50586d7f5d3SJohn Marino 			if (lp->extents <= extents_used + seg_extents)
50686d7f5d3SJohn Marino 				break;
50786d7f5d3SJohn Marino 
50886d7f5d3SJohn Marino 			extents_used += seg_extents;
50986d7f5d3SJohn Marino 		}
51086d7f5d3SJohn Marino 
51186d7f5d3SJohn Marino 		seg_size = lp->extents - extents_used;
51286d7f5d3SJohn Marino 		lp->stripe_size = seg_stripesize;
51386d7f5d3SJohn Marino 		lp->stripes = seg_stripes;
51486d7f5d3SJohn Marino 		lp->mirrors = seg_mirrors;
51586d7f5d3SJohn Marino 	}
51686d7f5d3SJohn Marino 
51786d7f5d3SJohn Marino 	if (lp->stripes > 1 && !lp->stripe_size) {
51886d7f5d3SJohn Marino 		log_error("Stripesize for striped segment should not be 0!");
51986d7f5d3SJohn Marino 		return EINVALID_CMD_LINE;
52086d7f5d3SJohn Marino 	}
52186d7f5d3SJohn Marino 
52286d7f5d3SJohn Marino 	if ((lp->stripes > 1)) {
52386d7f5d3SJohn Marino 		if (!(stripesize_extents = lp->stripe_size / vg->extent_size))
52486d7f5d3SJohn Marino 			stripesize_extents = 1;
52586d7f5d3SJohn Marino 
52686d7f5d3SJohn Marino 		if ((size_rest = seg_size % (lp->stripes * stripesize_extents))) {
52786d7f5d3SJohn Marino 			log_print("Rounding size (%d extents) down to stripe "
52886d7f5d3SJohn Marino 				  "boundary size for segment (%d extents)",
52986d7f5d3SJohn Marino 				  lp->extents, lp->extents - size_rest);
53086d7f5d3SJohn Marino 			lp->extents = lp->extents - size_rest;
53186d7f5d3SJohn Marino 		}
53286d7f5d3SJohn Marino 
53386d7f5d3SJohn Marino 		if (lp->stripe_size < STRIPE_SIZE_MIN) {
53486d7f5d3SJohn Marino 			log_error("Invalid stripe size %s",
53586d7f5d3SJohn Marino 				  display_size(cmd, (uint64_t) lp->stripe_size));
53686d7f5d3SJohn Marino 			return EINVALID_CMD_LINE;
53786d7f5d3SJohn Marino 		}
53886d7f5d3SJohn Marino 	}
53986d7f5d3SJohn Marino 
54086d7f5d3SJohn Marino 	if (lp->extents < lv->le_count) {
54186d7f5d3SJohn Marino 		if (lp->resize == LV_EXTEND) {
54286d7f5d3SJohn Marino 			log_error("New size given (%d extents) not larger "
54386d7f5d3SJohn Marino 				  "than existing size (%d extents)",
54486d7f5d3SJohn Marino 				  lp->extents, lv->le_count);
54586d7f5d3SJohn Marino 			return EINVALID_CMD_LINE;
54686d7f5d3SJohn Marino 		}
54786d7f5d3SJohn Marino 		lp->resize = LV_REDUCE;
54886d7f5d3SJohn Marino 	} else if (lp->extents > lv->le_count) {
54986d7f5d3SJohn Marino 		if (lp->resize == LV_REDUCE) {
55086d7f5d3SJohn Marino 			log_error("New size given (%d extents) not less than "
55186d7f5d3SJohn Marino 				  "existing size (%d extents)", lp->extents,
55286d7f5d3SJohn Marino 				  lv->le_count);
55386d7f5d3SJohn Marino 			return EINVALID_CMD_LINE;
55486d7f5d3SJohn Marino 		}
55586d7f5d3SJohn Marino 		lp->resize = LV_EXTEND;
55686d7f5d3SJohn Marino 	}
55786d7f5d3SJohn Marino 
55886d7f5d3SJohn Marino 	if (lv_is_origin(lv)) {
55986d7f5d3SJohn Marino 		if (lp->resize == LV_REDUCE) {
56086d7f5d3SJohn Marino 			log_error("Snapshot origin volumes cannot be reduced "
56186d7f5d3SJohn Marino 				  "in size yet.");
56286d7f5d3SJohn Marino 			return ECMD_FAILED;
56386d7f5d3SJohn Marino 		}
56486d7f5d3SJohn Marino 
56586d7f5d3SJohn Marino 		memset(&info, 0, sizeof(info));
56686d7f5d3SJohn Marino 
56786d7f5d3SJohn Marino 		if (lv_info(cmd, lv, &info, 0, 0) && info.exists) {
56886d7f5d3SJohn Marino 			log_error("Snapshot origin volumes can be resized "
56986d7f5d3SJohn Marino 				  "only while inactive: try lvchange -an");
57086d7f5d3SJohn Marino 			return ECMD_FAILED;
57186d7f5d3SJohn Marino 		}
57286d7f5d3SJohn Marino 	}
57386d7f5d3SJohn Marino 
57486d7f5d3SJohn Marino 	if ((lp->resize == LV_REDUCE) && lp->argc)
57586d7f5d3SJohn Marino 		log_warn("Ignoring PVs on command line when reducing");
57686d7f5d3SJohn Marino 
57786d7f5d3SJohn Marino 	/* Request confirmation before operations that are often mistakes. */
57886d7f5d3SJohn Marino 	if ((lp->resizefs || (lp->resize == LV_REDUCE)) &&
57986d7f5d3SJohn Marino 	    !_request_confirmation(cmd, vg, lv, lp)) {
58086d7f5d3SJohn Marino 		stack;
58186d7f5d3SJohn Marino 		return ECMD_FAILED;
58286d7f5d3SJohn Marino 	}
58386d7f5d3SJohn Marino 
58486d7f5d3SJohn Marino 	if (lp->resizefs) {
58586d7f5d3SJohn Marino 		if (!lp->nofsck &&
58686d7f5d3SJohn Marino 		    !_fsadm_cmd(cmd, vg, lp, FSADM_CMD_CHECK)) {
58786d7f5d3SJohn Marino 			stack;
58886d7f5d3SJohn Marino 			return ECMD_FAILED;
58986d7f5d3SJohn Marino 		}
59086d7f5d3SJohn Marino 
59186d7f5d3SJohn Marino 		if ((lp->resize == LV_REDUCE) &&
59286d7f5d3SJohn Marino 		    !_fsadm_cmd(cmd, vg, lp, FSADM_CMD_RESIZE)) {
59386d7f5d3SJohn Marino 			stack;
59486d7f5d3SJohn Marino 			return ECMD_FAILED;
59586d7f5d3SJohn Marino 		}
59686d7f5d3SJohn Marino 	}
59786d7f5d3SJohn Marino 
59886d7f5d3SJohn Marino 	if (!archive(vg)) {
59986d7f5d3SJohn Marino 		stack;
60086d7f5d3SJohn Marino 		return ECMD_FAILED;
60186d7f5d3SJohn Marino 	}
60286d7f5d3SJohn Marino 
60386d7f5d3SJohn Marino 	log_print("%sing logical volume %s to %s",
60486d7f5d3SJohn Marino 		  (lp->resize == LV_REDUCE) ? "Reduc" : "Extend",
60586d7f5d3SJohn Marino 		  lp->lv_name,
60686d7f5d3SJohn Marino 		  display_size(cmd, (uint64_t) lp->extents * vg->extent_size));
60786d7f5d3SJohn Marino 
60886d7f5d3SJohn Marino 	if (lp->resize == LV_REDUCE) {
60986d7f5d3SJohn Marino 		if (!lv_reduce(lv, lv->le_count - lp->extents)) {
61086d7f5d3SJohn Marino 			stack;
61186d7f5d3SJohn Marino 			return ECMD_FAILED;
61286d7f5d3SJohn Marino 		}
61386d7f5d3SJohn Marino 	} else if ((lp->extents > lv->le_count) && /* Ensure we extend */
61486d7f5d3SJohn Marino 		   !lv_extend(lv, lp->segtype, lp->stripes,
61586d7f5d3SJohn Marino 			      lp->stripe_size, lp->mirrors,
61686d7f5d3SJohn Marino 			      lp->extents - lv->le_count,
61786d7f5d3SJohn Marino 			      NULL, 0u, 0u, pvh, alloc)) {
61886d7f5d3SJohn Marino 		stack;
61986d7f5d3SJohn Marino 		return ECMD_FAILED;
62086d7f5d3SJohn Marino 	}
62186d7f5d3SJohn Marino 
62286d7f5d3SJohn Marino 	/* store vg on disk(s) */
62386d7f5d3SJohn Marino 	if (!vg_write(vg)) {
62486d7f5d3SJohn Marino 		stack;
62586d7f5d3SJohn Marino 		return ECMD_FAILED;
62686d7f5d3SJohn Marino 	}
62786d7f5d3SJohn Marino 
62886d7f5d3SJohn Marino 	/* If snapshot, must suspend all associated devices */
62986d7f5d3SJohn Marino 	if (lv_is_cow(lv))
63086d7f5d3SJohn Marino 		lock_lv = origin_from_cow(lv);
63186d7f5d3SJohn Marino 	else
63286d7f5d3SJohn Marino 		lock_lv = lv;
63386d7f5d3SJohn Marino 
63486d7f5d3SJohn Marino 	if (!suspend_lv(cmd, lock_lv)) {
63586d7f5d3SJohn Marino 		log_error("Failed to suspend %s", lp->lv_name);
63686d7f5d3SJohn Marino 		vg_revert(vg);
63786d7f5d3SJohn Marino 		backup(vg);
63886d7f5d3SJohn Marino 		return ECMD_FAILED;
63986d7f5d3SJohn Marino 	}
64086d7f5d3SJohn Marino 
64186d7f5d3SJohn Marino 	if (!vg_commit(vg)) {
64286d7f5d3SJohn Marino 		stack;
64386d7f5d3SJohn Marino 		resume_lv(cmd, lock_lv);
64486d7f5d3SJohn Marino 		backup(vg);
64586d7f5d3SJohn Marino 		return ECMD_FAILED;
64686d7f5d3SJohn Marino 	}
64786d7f5d3SJohn Marino 
64886d7f5d3SJohn Marino 	if (!resume_lv(cmd, lock_lv)) {
64986d7f5d3SJohn Marino 		log_error("Problem reactivating %s", lp->lv_name);
65086d7f5d3SJohn Marino 		backup(vg);
65186d7f5d3SJohn Marino 		return ECMD_FAILED;
65286d7f5d3SJohn Marino 	}
65386d7f5d3SJohn Marino 
65486d7f5d3SJohn Marino 	backup(vg);
65586d7f5d3SJohn Marino 
65686d7f5d3SJohn Marino 	log_print("Logical volume %s successfully resized", lp->lv_name);
65786d7f5d3SJohn Marino 
65886d7f5d3SJohn Marino 	if (lp->resizefs && (lp->resize == LV_EXTEND) &&
65986d7f5d3SJohn Marino 	    !_fsadm_cmd(cmd, vg, lp, FSADM_CMD_RESIZE)) {
66086d7f5d3SJohn Marino 		stack;
66186d7f5d3SJohn Marino 		return ECMD_FAILED;
66286d7f5d3SJohn Marino 	}
66386d7f5d3SJohn Marino 
66486d7f5d3SJohn Marino 	return ECMD_PROCESSED;
66586d7f5d3SJohn Marino }
66686d7f5d3SJohn Marino 
lvresize(struct cmd_context * cmd,int argc,char ** argv)66786d7f5d3SJohn Marino int lvresize(struct cmd_context *cmd, int argc, char **argv)
66886d7f5d3SJohn Marino {
66986d7f5d3SJohn Marino 	struct lvresize_params lp;
67086d7f5d3SJohn Marino 	struct volume_group *vg;
67186d7f5d3SJohn Marino 	int r;
67286d7f5d3SJohn Marino 
67386d7f5d3SJohn Marino 	memset(&lp, 0, sizeof(lp));
67486d7f5d3SJohn Marino 
67586d7f5d3SJohn Marino 	if (!_lvresize_params(cmd, argc, argv, &lp))
67686d7f5d3SJohn Marino 		return EINVALID_CMD_LINE;
67786d7f5d3SJohn Marino 
67886d7f5d3SJohn Marino 	log_verbose("Finding volume group %s", lp.vg_name);
67986d7f5d3SJohn Marino 	vg = vg_read_for_update(cmd, lp.vg_name, NULL, 0);
68086d7f5d3SJohn Marino 	if (vg_read_error(vg)) {
68186d7f5d3SJohn Marino 		vg_release(vg);
68286d7f5d3SJohn Marino 		stack;
68386d7f5d3SJohn Marino 		return ECMD_FAILED;
68486d7f5d3SJohn Marino 	}
68586d7f5d3SJohn Marino 
68686d7f5d3SJohn Marino 	if (!(r = _lvresize(cmd, vg, &lp)))
68786d7f5d3SJohn Marino 		stack;
68886d7f5d3SJohn Marino 
68986d7f5d3SJohn Marino 	unlock_and_release_vg(cmd, vg, lp.vg_name);
69086d7f5d3SJohn Marino 
69186d7f5d3SJohn Marino 	return r;
69286d7f5d3SJohn Marino }
693