xref: /dragonfly/contrib/lvm2/dist/tools/lvresize.c (revision 25a2db75)
1 /*	$NetBSD: lvresize.c,v 1.1.1.3 2009/12/02 00:25:53 haad Exp $	*/
2 
3 /*
4  * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
5  * Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
6  *
7  * This file is part of LVM2.
8  *
9  * This copyrighted material is made available to anyone wishing to use,
10  * modify, copy, or redistribute it subject to the terms and conditions
11  * of the GNU Lesser General Public License v.2.1.
12  *
13  * You should have received a copy of the GNU Lesser General Public License
14  * along with this program; if not, write to the Free Software Foundation,
15  * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16  */
17 
18 #include "tools.h"
19 
20 #define SIZE_BUF 128
21 
22 struct lvresize_params {
23 	const char *vg_name;
24 	const char *lv_name;
25 
26 	uint32_t stripes;
27 	uint32_t stripe_size;
28 	uint32_t mirrors;
29 
30 	const struct segment_type *segtype;
31 
32 	/* size */
33 	uint32_t extents;
34 	uint64_t size;
35 	sign_t sign;
36 	percent_t percent;
37 
38 	enum {
39 		LV_ANY = 0,
40 		LV_REDUCE = 1,
41 		LV_EXTEND = 2
42 	} resize;
43 
44 	int resizefs;
45 	int nofsck;
46 
47 	int argc;
48 	char **argv;
49 };
50 
51 static int _validate_stripesize(struct cmd_context *cmd,
52 				const struct volume_group *vg,
53 				struct lvresize_params *lp)
54 {
55 	if (arg_sign_value(cmd, stripesize_ARG, 0) == SIGN_MINUS) {
56 		log_error("Stripesize may not be negative.");
57 		return 0;
58 	}
59 
60 	if (arg_uint_value(cmd, stripesize_ARG, 0) > STRIPE_SIZE_LIMIT * 2) {
61 		log_error("Stripe size cannot be larger than %s",
62 			  display_size(cmd, (uint64_t) STRIPE_SIZE_LIMIT));
63 		return 0;
64 	}
65 
66 	if (!(vg->fid->fmt->features & FMT_SEGMENTS))
67 		log_warn("Varied stripesize not supported. Ignoring.");
68 	else if (arg_uint_value(cmd, stripesize_ARG, 0) > vg->extent_size * 2) {
69 		log_error("Reducing stripe size %s to maximum, "
70 			  "physical extent size %s",
71 			  display_size(cmd,
72 				       (uint64_t) arg_uint_value(cmd, stripesize_ARG, 0)),
73 			  display_size(cmd, (uint64_t) vg->extent_size));
74 		lp->stripe_size = vg->extent_size;
75 	} else
76 		lp->stripe_size = arg_uint_value(cmd, stripesize_ARG, 0);
77 
78 	if (lp->mirrors) {
79 		log_error("Mirrors and striping cannot be combined yet.");
80 		return 0;
81 	}
82 	if (lp->stripe_size & (lp->stripe_size - 1)) {
83 		log_error("Stripe size must be power of 2");
84 		return 0;
85 	}
86 
87 	return 1;
88 }
89 
90 static int _request_confirmation(struct cmd_context *cmd,
91 				 const struct volume_group *vg,
92 				 const struct logical_volume *lv,
93 				 const struct lvresize_params *lp)
94 {
95 	struct lvinfo info;
96 
97 	memset(&info, 0, sizeof(info));
98 
99 	if (!lv_info(cmd, lv, &info, 1, 0) && driver_version(NULL, 0)) {
100 		log_error("lv_info failed: aborting");
101 		return 0;
102 	}
103 
104 	if (lp->resizefs) {
105 		if (!info.exists) {
106 			log_error("Logical volume %s must be activated "
107 				  "before resizing filesystem", lp->lv_name);
108 			return 0;
109 		}
110 		return 1;
111 	}
112 
113 	if (!info.exists)
114 		return 1;
115 
116 	log_warn("WARNING: Reducing active%s logical volume to %s",
117 		 info.open_count ? " and open" : "",
118 		 display_size(cmd, (uint64_t) lp->extents * vg->extent_size));
119 
120 	log_warn("THIS MAY DESTROY YOUR DATA (filesystem etc.)");
121 
122 	if (!arg_count(cmd, force_ARG)) {
123 		if (yes_no_prompt("Do you really want to reduce %s? [y/n]: ",
124 				  lp->lv_name) == 'n') {
125 			log_print("Logical volume %s NOT reduced", lp->lv_name);
126 			return 0;
127 		}
128 		if (sigint_caught())
129 			return 0;
130 	}
131 
132 	return 1;
133 }
134 
135 enum fsadm_cmd_e { FSADM_CMD_CHECK, FSADM_CMD_RESIZE };
136 #define FSADM_CMD "fsadm"
137 #define FSADM_CMD_MAX_ARGS 6
138 
139 /*
140  * FSADM_CMD --dry-run --verbose --force check lv_path
141  * FSADM_CMD --dry-run --verbose --force resize lv_path size
142  */
143 static int _fsadm_cmd(struct cmd_context *cmd,
144 		      const struct volume_group *vg,
145 		      const struct lvresize_params *lp,
146 		      enum fsadm_cmd_e fcmd)
147 {
148 	char lv_path[PATH_MAX];
149 	char size_buf[SIZE_BUF];
150 	const char *argv[FSADM_CMD_MAX_ARGS + 2];
151 	unsigned i = 0;
152 
153 	argv[i++] = FSADM_CMD;
154 
155 	if (test_mode())
156 		argv[i++] = "--dry-run";
157 
158 	if (verbose_level() >= _LOG_NOTICE)
159 		argv[i++] = "--verbose";
160 
161 	if (arg_count(cmd, force_ARG))
162 		argv[i++] = "--force";
163 
164 	argv[i++] = (fcmd == FSADM_CMD_RESIZE) ? "resize" : "check";
165 
166 	if (dm_snprintf(lv_path, PATH_MAX, "%s%s/%s", cmd->dev_dir, lp->vg_name,
167 			lp->lv_name) < 0) {
168 		log_error("Couldn't create LV path for %s", lp->lv_name);
169 		return 0;
170 	}
171 
172 	argv[i++] = lv_path;
173 
174 	if (fcmd == FSADM_CMD_RESIZE) {
175 		if (dm_snprintf(size_buf, SIZE_BUF, "%" PRIu64 "K",
176 				(uint64_t) lp->extents * vg->extent_size / 2) < 0) {
177 			log_error("Couldn't generate new LV size string");
178 			return 0;
179 		}
180 
181 		argv[i++] = size_buf;
182 	}
183 
184 	argv[i] = NULL;
185 
186 	return exec_cmd(cmd, argv);
187 }
188 
189 static int _lvresize_params(struct cmd_context *cmd, int argc, char **argv,
190 			    struct lvresize_params *lp)
191 {
192 	const char *cmd_name;
193 	char *st;
194 	unsigned dev_dir_found = 0;
195 
196 	lp->sign = SIGN_NONE;
197 	lp->resize = LV_ANY;
198 
199 	cmd_name = command_name(cmd);
200 	if (!strcmp(cmd_name, "lvreduce"))
201 		lp->resize = LV_REDUCE;
202 	if (!strcmp(cmd_name, "lvextend"))
203 		lp->resize = LV_EXTEND;
204 
205 	/*
206 	 * Allow omission of extents and size if the user has given us
207 	 * one or more PVs.  Most likely, the intent was "resize this
208 	 * LV the best you can with these PVs"
209 	 */
210 	if ((arg_count(cmd, extents_ARG) + arg_count(cmd, size_ARG) == 0) &&
211 	    (argc >= 2)) {
212 		lp->extents = 100;
213 		lp->percent = PERCENT_PVS;
214 		lp->sign = SIGN_PLUS;
215 	} else if ((arg_count(cmd, extents_ARG) +
216 		    arg_count(cmd, size_ARG) != 1)) {
217 		log_error("Please specify either size or extents but not "
218 			  "both.");
219 		return 0;
220 	}
221 
222 	if (arg_count(cmd, extents_ARG)) {
223 		lp->extents = arg_uint_value(cmd, extents_ARG, 0);
224 		lp->sign = arg_sign_value(cmd, extents_ARG, SIGN_NONE);
225 		lp->percent = arg_percent_value(cmd, extents_ARG, PERCENT_NONE);
226 	}
227 
228 	/* Size returned in kilobyte units; held in sectors */
229 	if (arg_count(cmd, size_ARG)) {
230 		lp->size = arg_uint64_value(cmd, size_ARG, UINT64_C(0));
231 		lp->sign = arg_sign_value(cmd, size_ARG, SIGN_NONE);
232 		lp->percent = PERCENT_NONE;
233 	}
234 
235 	if (lp->resize == LV_EXTEND && lp->sign == SIGN_MINUS) {
236 		log_error("Negative argument not permitted - use lvreduce");
237 		return 0;
238 	}
239 
240 	if (lp->resize == LV_REDUCE && lp->sign == SIGN_PLUS) {
241 		log_error("Positive sign not permitted - use lvextend");
242 		return 0;
243 	}
244 
245 	lp->resizefs = arg_is_set(cmd, resizefs_ARG);
246 	lp->nofsck = arg_is_set(cmd, nofsck_ARG);
247 
248 	if (!argc) {
249 		log_error("Please provide the logical volume name");
250 		return 0;
251 	}
252 
253 	lp->lv_name = argv[0];
254 	argv++;
255 	argc--;
256 
257 	if (!(lp->lv_name = skip_dev_dir(cmd, lp->lv_name, &dev_dir_found)) ||
258 	    !(lp->vg_name = extract_vgname(cmd, lp->lv_name))) {
259 		log_error("Please provide a volume group name");
260 		return 0;
261 	}
262 
263 	if (!validate_name(lp->vg_name)) {
264 		log_error("Volume group name %s has invalid characters",
265 			  lp->vg_name);
266 		return 0;
267 	}
268 
269 	if ((st = strrchr(lp->lv_name, '/')))
270 		lp->lv_name = st + 1;
271 
272 	lp->argc = argc;
273 	lp->argv = argv;
274 
275 	return 1;
276 }
277 
278 static int _lvresize(struct cmd_context *cmd, struct volume_group *vg,
279 		     struct lvresize_params *lp)
280 {
281 	struct logical_volume *lv;
282 	struct lvinfo info;
283 	uint32_t stripesize_extents = 0;
284 	uint32_t seg_stripes = 0, seg_stripesize = 0, seg_size = 0;
285 	uint32_t seg_mirrors = 0;
286 	uint32_t extents_used = 0;
287 	uint32_t size_rest;
288 	uint32_t pv_extent_count = 0;
289 	alloc_policy_t alloc;
290 	struct logical_volume *lock_lv;
291 	struct lv_list *lvl;
292 	struct lv_segment *seg;
293 	uint32_t seg_extents;
294 	uint32_t sz, str;
295 	struct dm_list *pvh = NULL;
296 
297 	/* does LV exist? */
298 	if (!(lvl = find_lv_in_vg(vg, lp->lv_name))) {
299 		log_error("Logical volume %s not found in volume group %s",
300 			  lp->lv_name, lp->vg_name);
301 		return ECMD_FAILED;
302 	}
303 
304 	if (arg_count(cmd, stripes_ARG)) {
305 		if (vg->fid->fmt->features & FMT_SEGMENTS)
306 			lp->stripes = arg_uint_value(cmd, stripes_ARG, 1);
307 		else
308 			log_warn("Varied striping not supported. Ignoring.");
309 	}
310 
311 	if (arg_count(cmd, mirrors_ARG)) {
312 		if (vg->fid->fmt->features & FMT_SEGMENTS)
313 			lp->mirrors = arg_uint_value(cmd, mirrors_ARG, 1) + 1;
314 		else
315 			log_warn("Mirrors not supported. Ignoring.");
316 		if (arg_sign_value(cmd, mirrors_ARG, 0) == SIGN_MINUS) {
317 			log_error("Mirrors argument may not be negative");
318 			return EINVALID_CMD_LINE;
319 		}
320 	}
321 
322 	if (arg_count(cmd, stripesize_ARG) &&
323 	    !_validate_stripesize(cmd, vg, lp))
324 		return EINVALID_CMD_LINE;
325 
326 	lv = lvl->lv;
327 
328 	if (lv->status & LOCKED) {
329 		log_error("Can't resize locked LV %s", lv->name);
330 		return ECMD_FAILED;
331 	}
332 
333 	if (lv->status & CONVERTING) {
334 		log_error("Can't resize %s while lvconvert in progress", lv->name);
335 		return ECMD_FAILED;
336 	}
337 
338 	alloc = arg_uint_value(cmd, alloc_ARG, lv->alloc);
339 
340 	if (lp->size) {
341 		if (lp->size % vg->extent_size) {
342 			if (lp->sign == SIGN_MINUS)
343 				lp->size -= lp->size % vg->extent_size;
344 			else
345 				lp->size += vg->extent_size -
346 				    (lp->size % vg->extent_size);
347 
348 			log_print("Rounding up size to full physical extent %s",
349 				  display_size(cmd, (uint64_t) lp->size));
350 		}
351 
352 		lp->extents = lp->size / vg->extent_size;
353 	}
354 
355 	if (!(pvh = lp->argc ? create_pv_list(cmd->mem, vg, lp->argc,
356 						     lp->argv, 1) : &vg->pvs)) {
357 		stack;
358 		return ECMD_FAILED;
359 	}
360 
361 	switch(lp->percent) {
362 		case PERCENT_VG:
363 			lp->extents = lp->extents * vg->extent_count / 100;
364 			break;
365 		case PERCENT_FREE:
366 			lp->extents = lp->extents * vg->free_count / 100;
367 			break;
368 		case PERCENT_LV:
369 			lp->extents = lp->extents * lv->le_count / 100;
370 			break;
371 		case PERCENT_PVS:
372 			if (lp->argc) {
373 				pv_extent_count = pv_list_extents_free(pvh);
374 				lp->extents = lp->extents * pv_extent_count / 100;
375 			} else
376 				lp->extents = lp->extents * vg->extent_count / 100;
377 			break;
378 		case PERCENT_NONE:
379 			break;
380 	}
381 
382 	if (lp->sign == SIGN_PLUS)
383 		lp->extents += lv->le_count;
384 
385 	if (lp->sign == SIGN_MINUS) {
386 		if (lp->extents >= lv->le_count) {
387 			log_error("Unable to reduce %s below 1 extent",
388 				  lp->lv_name);
389 			return EINVALID_CMD_LINE;
390 		}
391 
392 		lp->extents = lv->le_count - lp->extents;
393 	}
394 
395 	if (!lp->extents) {
396 		log_error("New size of 0 not permitted");
397 		return EINVALID_CMD_LINE;
398 	}
399 
400 	if (lp->extents == lv->le_count) {
401 		if (!lp->resizefs) {
402 			log_error("New size (%d extents) matches existing size "
403 				  "(%d extents)", lp->extents, lv->le_count);
404 			return EINVALID_CMD_LINE;
405 		}
406 		lp->resize = LV_EXTEND; /* lets pretend zero size extension */
407 	}
408 
409 	seg_size = lp->extents - lv->le_count;
410 
411 	/* Use segment type of last segment */
412 	dm_list_iterate_items(seg, &lv->segments) {
413 		lp->segtype = seg->segtype;
414 	}
415 
416 	/* FIXME Support LVs with mixed segment types */
417 	if (lp->segtype != arg_ptr_value(cmd, type_ARG, lp->segtype)) {
418 		log_error("VolumeType does not match (%s)", lp->segtype->name);
419 		return EINVALID_CMD_LINE;
420 	}
421 
422 	/* If extending, find stripes, stripesize & size of last segment */
423 	if ((lp->extents > lv->le_count) &&
424 	    !(lp->stripes == 1 || (lp->stripes > 1 && lp->stripe_size))) {
425 		dm_list_iterate_items(seg, &lv->segments) {
426 			if (!seg_is_striped(seg))
427 				continue;
428 
429 			sz = seg->stripe_size;
430 			str = seg->area_count;
431 
432 			if ((seg_stripesize && seg_stripesize != sz &&
433 			     !lp->stripe_size) ||
434 			    (seg_stripes && seg_stripes != str && !lp->stripes)) {
435 				log_error("Please specify number of "
436 					  "stripes (-i) and stripesize (-I)");
437 				return EINVALID_CMD_LINE;
438 			}
439 
440 			seg_stripesize = sz;
441 			seg_stripes = str;
442 		}
443 
444 		if (!lp->stripes)
445 			lp->stripes = seg_stripes;
446 
447 		if (!lp->stripe_size && lp->stripes > 1) {
448 			if (seg_stripesize) {
449 				log_print("Using stripesize of last segment %s",
450 					  display_size(cmd, (uint64_t) seg_stripesize));
451 				lp->stripe_size = seg_stripesize;
452 			} else {
453 				lp->stripe_size =
454 					find_config_tree_int(cmd,
455 							"metadata/stripesize",
456 							DEFAULT_STRIPESIZE) * 2;
457 				log_print("Using default stripesize %s",
458 					  display_size(cmd, (uint64_t) lp->stripe_size));
459 			}
460 		}
461 	}
462 
463 	/* If extending, find mirrors of last segment */
464 	if ((lp->extents > lv->le_count)) {
465 		dm_list_iterate_back_items(seg, &lv->segments) {
466 			if (seg_is_mirrored(seg))
467 				seg_mirrors = lv_mirror_count(seg->lv);
468 			else
469 				seg_mirrors = 0;
470 			break;
471 		}
472 		if (!arg_count(cmd, mirrors_ARG) && seg_mirrors) {
473 			log_print("Extending %" PRIu32 " mirror images.",
474 				  seg_mirrors);
475 			lp->mirrors = seg_mirrors;
476 		}
477 		if ((arg_count(cmd, mirrors_ARG) || seg_mirrors) &&
478 		    (lp->mirrors != seg_mirrors)) {
479 			log_error("Cannot vary number of mirrors in LV yet.");
480 			return EINVALID_CMD_LINE;
481 		}
482 	}
483 
484 	/* If reducing, find stripes, stripesize & size of last segment */
485 	if (lp->extents < lv->le_count) {
486 		extents_used = 0;
487 
488 		if (lp->stripes || lp->stripe_size || lp->mirrors)
489 			log_error("Ignoring stripes, stripesize and mirrors "
490 				  "arguments when reducing");
491 
492 		dm_list_iterate_items(seg, &lv->segments) {
493 			seg_extents = seg->len;
494 
495 			if (seg_is_striped(seg)) {
496 				seg_stripesize = seg->stripe_size;
497 				seg_stripes = seg->area_count;
498 			}
499 
500 			if (seg_is_mirrored(seg))
501 				seg_mirrors = lv_mirror_count(seg->lv);
502 			else
503 				seg_mirrors = 0;
504 
505 			if (lp->extents <= extents_used + seg_extents)
506 				break;
507 
508 			extents_used += seg_extents;
509 		}
510 
511 		seg_size = lp->extents - extents_used;
512 		lp->stripe_size = seg_stripesize;
513 		lp->stripes = seg_stripes;
514 		lp->mirrors = seg_mirrors;
515 	}
516 
517 	if (lp->stripes > 1 && !lp->stripe_size) {
518 		log_error("Stripesize for striped segment should not be 0!");
519 		return EINVALID_CMD_LINE;
520 	}
521 
522 	if ((lp->stripes > 1)) {
523 		if (!(stripesize_extents = lp->stripe_size / vg->extent_size))
524 			stripesize_extents = 1;
525 
526 		if ((size_rest = seg_size % (lp->stripes * stripesize_extents))) {
527 			log_print("Rounding size (%d extents) down to stripe "
528 				  "boundary size for segment (%d extents)",
529 				  lp->extents, lp->extents - size_rest);
530 			lp->extents = lp->extents - size_rest;
531 		}
532 
533 		if (lp->stripe_size < STRIPE_SIZE_MIN) {
534 			log_error("Invalid stripe size %s",
535 				  display_size(cmd, (uint64_t) lp->stripe_size));
536 			return EINVALID_CMD_LINE;
537 		}
538 	}
539 
540 	if (lp->extents < lv->le_count) {
541 		if (lp->resize == LV_EXTEND) {
542 			log_error("New size given (%d extents) not larger "
543 				  "than existing size (%d extents)",
544 				  lp->extents, lv->le_count);
545 			return EINVALID_CMD_LINE;
546 		}
547 		lp->resize = LV_REDUCE;
548 	} else if (lp->extents > lv->le_count) {
549 		if (lp->resize == LV_REDUCE) {
550 			log_error("New size given (%d extents) not less than "
551 				  "existing size (%d extents)", lp->extents,
552 				  lv->le_count);
553 			return EINVALID_CMD_LINE;
554 		}
555 		lp->resize = LV_EXTEND;
556 	}
557 
558 	if (lv_is_origin(lv)) {
559 		if (lp->resize == LV_REDUCE) {
560 			log_error("Snapshot origin volumes cannot be reduced "
561 				  "in size yet.");
562 			return ECMD_FAILED;
563 		}
564 
565 		memset(&info, 0, sizeof(info));
566 
567 		if (lv_info(cmd, lv, &info, 0, 0) && info.exists) {
568 			log_error("Snapshot origin volumes can be resized "
569 				  "only while inactive: try lvchange -an");
570 			return ECMD_FAILED;
571 		}
572 	}
573 
574 	if ((lp->resize == LV_REDUCE) && lp->argc)
575 		log_warn("Ignoring PVs on command line when reducing");
576 
577 	/* Request confirmation before operations that are often mistakes. */
578 	if ((lp->resizefs || (lp->resize == LV_REDUCE)) &&
579 	    !_request_confirmation(cmd, vg, lv, lp)) {
580 		stack;
581 		return ECMD_FAILED;
582 	}
583 
584 	if (lp->resizefs) {
585 		if (!lp->nofsck &&
586 		    !_fsadm_cmd(cmd, vg, lp, FSADM_CMD_CHECK)) {
587 			stack;
588 			return ECMD_FAILED;
589 		}
590 
591 		if ((lp->resize == LV_REDUCE) &&
592 		    !_fsadm_cmd(cmd, vg, lp, FSADM_CMD_RESIZE)) {
593 			stack;
594 			return ECMD_FAILED;
595 		}
596 	}
597 
598 	if (!archive(vg)) {
599 		stack;
600 		return ECMD_FAILED;
601 	}
602 
603 	log_print("%sing logical volume %s to %s",
604 		  (lp->resize == LV_REDUCE) ? "Reduc" : "Extend",
605 		  lp->lv_name,
606 		  display_size(cmd, (uint64_t) lp->extents * vg->extent_size));
607 
608 	if (lp->resize == LV_REDUCE) {
609 		if (!lv_reduce(lv, lv->le_count - lp->extents)) {
610 			stack;
611 			return ECMD_FAILED;
612 		}
613 	} else if ((lp->extents > lv->le_count) && /* Ensure we extend */
614 		   !lv_extend(lv, lp->segtype, lp->stripes,
615 			      lp->stripe_size, lp->mirrors,
616 			      lp->extents - lv->le_count,
617 			      NULL, 0u, 0u, pvh, alloc)) {
618 		stack;
619 		return ECMD_FAILED;
620 	}
621 
622 	/* store vg on disk(s) */
623 	if (!vg_write(vg)) {
624 		stack;
625 		return ECMD_FAILED;
626 	}
627 
628 	/* If snapshot, must suspend all associated devices */
629 	if (lv_is_cow(lv))
630 		lock_lv = origin_from_cow(lv);
631 	else
632 		lock_lv = lv;
633 
634 	if (!suspend_lv(cmd, lock_lv)) {
635 		log_error("Failed to suspend %s", lp->lv_name);
636 		vg_revert(vg);
637 		backup(vg);
638 		return ECMD_FAILED;
639 	}
640 
641 	if (!vg_commit(vg)) {
642 		stack;
643 		resume_lv(cmd, lock_lv);
644 		backup(vg);
645 		return ECMD_FAILED;
646 	}
647 
648 	if (!resume_lv(cmd, lock_lv)) {
649 		log_error("Problem reactivating %s", lp->lv_name);
650 		backup(vg);
651 		return ECMD_FAILED;
652 	}
653 
654 	backup(vg);
655 
656 	log_print("Logical volume %s successfully resized", lp->lv_name);
657 
658 	if (lp->resizefs && (lp->resize == LV_EXTEND) &&
659 	    !_fsadm_cmd(cmd, vg, lp, FSADM_CMD_RESIZE)) {
660 		stack;
661 		return ECMD_FAILED;
662 	}
663 
664 	return ECMD_PROCESSED;
665 }
666 
667 int lvresize(struct cmd_context *cmd, int argc, char **argv)
668 {
669 	struct lvresize_params lp;
670 	struct volume_group *vg;
671 	int r;
672 
673 	memset(&lp, 0, sizeof(lp));
674 
675 	if (!_lvresize_params(cmd, argc, argv, &lp))
676 		return EINVALID_CMD_LINE;
677 
678 	log_verbose("Finding volume group %s", lp.vg_name);
679 	vg = vg_read_for_update(cmd, lp.vg_name, NULL, 0);
680 	if (vg_read_error(vg)) {
681 		vg_release(vg);
682 		stack;
683 		return ECMD_FAILED;
684 	}
685 
686 	if (!(r = _lvresize(cmd, vg, &lp)))
687 		stack;
688 
689 	unlock_and_release_vg(cmd, vg, lp.vg_name);
690 
691 	return r;
692 }
693