]> sourceware.org Git - lvm2.git/blob - tools/lvresize.c
Change alloc_areas to pe_ranges and allow suppression of availability checks.
[lvm2.git] / tools / lvresize.c
1 /*
2 * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
3 * Copyright (C) 2004 Red Hat, Inc. All rights reserved.
4 *
5 * This file is part of LVM2.
6 *
7 * This copyrighted material is made available to anyone wishing to use,
8 * modify, copy, or redistribute it subject to the terms and conditions
9 * of the GNU General Public License v.2.
10 *
11 * You should have received a copy of the GNU General Public License
12 * along with this program; if not, write to the Free Software Foundation,
13 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
14 */
15
16 #include "tools.h"
17
18 #define SIZE_BUF 128
19
20 struct lvresize_params {
21 const char *vg_name;
22 const char *lv_name;
23
24 uint32_t stripes;
25 uint32_t stripe_size;
26
27 struct segment_type *segtype;
28
29 /* size */
30 uint32_t extents;
31 uint64_t size;
32 sign_t sign;
33
34 enum {
35 LV_ANY = 0,
36 LV_REDUCE = 1,
37 LV_EXTEND = 2
38 } resize;
39
40 int resizefs;
41 int nofsck;
42
43 int argc;
44 char **argv;
45 };
46
47 static int _read_params(struct cmd_context *cmd, int argc, char **argv,
48 struct lvresize_params *lp)
49 {
50 const char *cmd_name;
51 char *st;
52
53 lp->sign = SIGN_NONE;
54 lp->resize = LV_ANY;
55
56 cmd_name = command_name(cmd);
57 if (!strcmp(cmd_name, "lvreduce"))
58 lp->resize = LV_REDUCE;
59 if (!strcmp(cmd_name, "lvextend"))
60 lp->resize = LV_EXTEND;
61
62 if (arg_count(cmd, extents_ARG) + arg_count(cmd, size_ARG) != 1) {
63 log_error("Please specify either size or extents (not both)");
64 return 0;
65 }
66
67 if (arg_count(cmd, extents_ARG)) {
68 lp->extents = arg_uint_value(cmd, extents_ARG, 0);
69 lp->sign = arg_sign_value(cmd, extents_ARG, SIGN_NONE);
70 }
71
72 /* Size returned in kilobyte units; held in sectors */
73 if (arg_count(cmd, size_ARG)) {
74 lp->size = arg_uint64_value(cmd, size_ARG, UINT64_C(0)) * 2;
75 lp->sign = arg_sign_value(cmd, size_ARG, SIGN_NONE);
76 }
77
78 if (lp->resize == LV_EXTEND && lp->sign == SIGN_MINUS) {
79 log_error("Negative argument not permitted - use lvreduce");
80 return 0;
81 }
82
83 if (lp->resize == LV_REDUCE && lp->sign == SIGN_PLUS) {
84 log_error("Positive sign not permitted - use lvextend");
85 return 0;
86 }
87
88 lp->resizefs = arg_count(cmd, resizefs_ARG) ? 1 : 0;
89 lp->nofsck = arg_count(cmd, nofsck_ARG) ? 1 : 0;
90
91 if (!argc) {
92 log_error("Please provide the logical volume name");
93 return 0;
94 }
95
96 lp->lv_name = argv[0];
97 argv++;
98 argc--;
99
100 if (!(lp->vg_name = extract_vgname(cmd, lp->lv_name))) {
101 log_error("Please provide a volume group name");
102 return 0;
103 }
104
105 if ((st = strrchr(lp->lv_name, '/')))
106 lp->lv_name = st + 1;
107
108 lp->argc = argc;
109 lp->argv = argv;
110
111 return 1;
112 }
113
114 static int _lvresize(struct cmd_context *cmd, struct lvresize_params *lp)
115 {
116 struct volume_group *vg;
117 struct logical_volume *lv;
118 struct snapshot *snap;
119 struct lvinfo info;
120 uint32_t stripesize_extents = 0;
121 uint32_t seg_stripes = 0, seg_stripesize = 0, seg_size = 0;
122 uint32_t extents_used = 0;
123 uint32_t size_rest;
124 alloc_policy_t alloc;
125 char *lock_lvid;
126 struct lv_list *lvl;
127 int consistent = 1;
128 struct lv_segment *seg;
129 uint32_t seg_extents;
130 uint32_t sz, str;
131 struct list *pvh = NULL;
132 char size_buf[SIZE_BUF];
133 char lv_path[PATH_MAX];
134
135 if (!(vg = vg_read(cmd, lp->vg_name, &consistent))) {
136 log_error("Volume group %s doesn't exist", lp->vg_name);
137 return ECMD_FAILED;
138 }
139
140 if (vg->status & EXPORTED_VG) {
141 log_error("Volume group %s is exported", vg->name);
142 return ECMD_FAILED;
143 }
144
145 if (!(vg->status & LVM_WRITE)) {
146 log_error("Volume group %s is read-only", lp->vg_name);
147 return ECMD_FAILED;
148 }
149
150 /* does LV exist? */
151 if (!(lvl = find_lv_in_vg(vg, lp->lv_name))) {
152 log_error("Logical volume %s not found in volume group %s",
153 lp->lv_name, lp->vg_name);
154 return ECMD_FAILED;
155 }
156
157 if (arg_count(cmd, stripes_ARG)) {
158 if (vg->fid->fmt->features & FMT_SEGMENTS)
159 lp->stripes = arg_uint_value(cmd, stripes_ARG, 1);
160 else
161 log_print("Varied striping not supported. Ignoring.");
162 }
163
164 if (arg_count(cmd, stripesize_ARG)) {
165 if (arg_sign_value(cmd, stripesize_ARG, 0) == SIGN_MINUS) {
166 log_error("Stripesize may not be negative.");
167 return ECMD_FAILED;
168 }
169 if (vg->fid->fmt->features & FMT_SEGMENTS)
170 lp->stripe_size = 2 * arg_uint_value(cmd,
171 stripesize_ARG, 0);
172 else
173 log_print("Varied stripesize not supported. Ignoring.");
174 }
175
176 lv = lvl->lv;
177
178 if (lv->status & LOCKED) {
179 log_error("Can't resize locked LV %s", lv->name);
180 return ECMD_FAILED;
181 }
182
183 alloc = (alloc_policy_t) arg_uint_value(cmd, alloc_ARG, lv->alloc);
184
185 if (lp->size) {
186 if (lp->size % vg->extent_size) {
187 if (lp->sign == SIGN_MINUS)
188 lp->size -= lp->size % vg->extent_size;
189 else
190 lp->size += vg->extent_size -
191 (lp->size % vg->extent_size);
192
193 log_print("Rounding up size to full physical extent %s",
194 display_size(cmd, (uint64_t) lp->size,
195 SIZE_SHORT));
196 }
197
198 lp->extents = lp->size / vg->extent_size;
199 }
200
201 if (lp->sign == SIGN_PLUS)
202 lp->extents += lv->le_count;
203
204 if (lp->sign == SIGN_MINUS) {
205 if (lp->extents >= lv->le_count) {
206 log_error("Unable to reduce %s below 1 extent",
207 lp->lv_name);
208 return EINVALID_CMD_LINE;
209 }
210
211 lp->extents = lv->le_count - lp->extents;
212 }
213
214 if (!lp->extents) {
215 log_error("New size of 0 not permitted");
216 return EINVALID_CMD_LINE;
217 }
218
219 if (lp->extents == lv->le_count) {
220 log_error("New size (%d extents) matches existing size "
221 "(%d extents)", lp->extents, lv->le_count);
222 return EINVALID_CMD_LINE;
223 }
224
225 seg_size = lp->extents - lv->le_count;
226
227 /* Use segment type of last segment */
228 list_iterate_items(seg, &lv->segments) {
229 lp->segtype = seg->segtype;
230 }
231
232 /* FIXME Support LVs with mixed segment types */
233 if (lp->segtype != (struct segment_type *) arg_ptr_value(cmd, type_ARG,
234 lp->segtype)) {
235 log_error("VolumeType does not match (%s)", lp->segtype->name);
236 return EINVALID_CMD_LINE;
237 }
238
239 /* If extending, find stripes, stripesize & size of last segment */
240 if ((lp->extents > lv->le_count) &&
241 !(lp->stripes == 1 || (lp->stripes > 1 && lp->stripe_size))) {
242 list_iterate_items(seg, &lv->segments) {
243 if (!(seg->segtype->flags & SEG_AREAS_STRIPED))
244 continue;
245
246 sz = seg->stripe_size;
247 str = seg->area_count;
248
249 if ((seg_stripesize && seg_stripesize != sz
250 && !lp->stripe_size) ||
251 (seg_stripes && seg_stripes != str && !lp->stripes)) {
252 log_error("Please specify number of "
253 "stripes (-i) and stripesize (-I)");
254 return EINVALID_CMD_LINE;
255 }
256
257 seg_stripesize = sz;
258 seg_stripes = str;
259 }
260
261 if (!lp->stripes)
262 lp->stripes = seg_stripes;
263
264 if (!lp->stripe_size && lp->stripes > 1) {
265 if (seg_stripesize) {
266 log_print("Using stripesize of last segment "
267 "%dKB", seg_stripesize / 2);
268 lp->stripe_size = seg_stripesize;
269 } else {
270 lp->stripe_size =
271 find_config_int(cmd->cft->root,
272 "metadata/stripesize",
273 DEFAULT_STRIPESIZE) * 2;
274 log_print("Using default stripesize %dKB",
275 lp->stripe_size / 2);
276 }
277 }
278 }
279
280 /* If reducing, find stripes, stripesize & size of last segment */
281 if (lp->extents < lv->le_count) {
282 extents_used = 0;
283
284 if (lp->stripes || lp->stripe_size)
285 log_error("Ignoring stripes and stripesize arguments "
286 "when reducing");
287
288 list_iterate_items(seg, &lv->segments) {
289 seg_extents = seg->len;
290
291 if (seg->segtype->flags & SEG_AREAS_STRIPED) {
292 seg_stripesize = seg->stripe_size;
293 seg_stripes = seg->area_count;
294 }
295
296 if (lp->extents <= extents_used + seg_extents)
297 break;
298
299 extents_used += seg_extents;
300 }
301
302 seg_size = lp->extents - extents_used;
303 lp->stripe_size = seg_stripesize;
304 lp->stripes = seg_stripes;
305 }
306
307 if (lp->stripes > 1 && !lp->stripe_size) {
308 log_error("Stripesize for striped segment should not be 0!");
309 return EINVALID_CMD_LINE;
310 }
311
312 if ((lp->stripes > 1)) {
313 if (!(stripesize_extents = lp->stripe_size / vg->extent_size))
314 stripesize_extents = 1;
315
316 if ((size_rest = seg_size % (lp->stripes * stripesize_extents))) {
317 log_print("Rounding size (%d extents) down to stripe "
318 "boundary size for segment (%d extents)",
319 lp->extents, lp->extents - size_rest);
320 lp->extents = lp->extents - size_rest;
321 }
322 }
323
324 if (lp->extents == lv->le_count) {
325 log_error("New size (%d extents) matches existing size "
326 "(%d extents)", lp->extents, lv->le_count);
327 return EINVALID_CMD_LINE;
328 }
329
330 if (lp->extents < lv->le_count) {
331 if (lp->resize == LV_EXTEND) {
332 log_error("New size given (%d extents) not larger "
333 "than existing size (%d extents)",
334 lp->extents, lv->le_count);
335 return EINVALID_CMD_LINE;
336 } else
337 lp->resize = LV_REDUCE;
338 }
339
340 if (lp->extents > lv->le_count) {
341 if (lp->resize == LV_REDUCE) {
342 log_error("New size given (%d extents) not less than "
343 "existing size (%d extents)", lp->extents,
344 lv->le_count);
345 return EINVALID_CMD_LINE;
346 } else
347 lp->resize = LV_EXTEND;
348 }
349
350 if (lp->resize == LV_REDUCE) {
351 if (lp->argc)
352 log_print("Ignoring PVs on command line when reducing");
353 } else if (!(pvh = lp->argc ? create_pv_list(cmd->mem, vg, lp->argc,
354 lp->argv, 1) : &vg->pvs)) {
355 stack;
356 return ECMD_FAILED;
357 }
358
359 if (lp->resize == LV_REDUCE || lp->resizefs) {
360 memset(&info, 0, sizeof(info));
361
362 if (!lv_info(lv, &info) && driver_version(NULL, 0)) {
363 log_error("lv_info failed: aborting");
364 return ECMD_FAILED;
365 }
366
367 if (lp->resizefs && !info.exists) {
368 log_error("Logical volume %s must be activated "
369 "before resizing filesystem", lp->lv_name);
370 return ECMD_FAILED;
371 }
372
373 if (info.exists && !lp->resizefs && (lp->resize == LV_REDUCE)) {
374 log_print("WARNING: Reducing active%s logical volume "
375 "to %s", info.open_count ? " and open" : "",
376 display_size(cmd, (uint64_t) lp->extents *
377 vg->extent_size,
378 SIZE_SHORT));
379
380 log_print("THIS MAY DESTROY YOUR DATA "
381 "(filesystem etc.)");
382
383 if (!arg_count(cmd, force_ARG)) {
384 if (yes_no_prompt("Do you really want to "
385 "reduce %s? [y/n]: ",
386 lp->lv_name) == 'n') {
387 log_print("Logical volume %s NOT "
388 "reduced", lp->lv_name);
389 return ECMD_FAILED;
390 }
391 }
392 }
393 }
394
395 if (lp->resizefs) {
396 if (lvm_snprintf(lv_path, PATH_MAX, "%s%s/%s", cmd->dev_dir,
397 lp->vg_name, lp->lv_name) < 0) {
398 log_error("Couldn't create LV path for %s",
399 lp->lv_name);
400 return ECMD_FAILED;
401 }
402
403 if (lvm_snprintf(size_buf, SIZE_BUF, "%" PRIu64,
404 (uint64_t) lp->extents * vg->extent_size / 2)
405 < 0) {
406 log_error("Couldn't generate new LV size string");
407 return ECMD_FAILED;
408 }
409
410 if (!lp->nofsck) {
411 if (!exec_cmd("fsadm", "check", lv_path, NULL)) {
412 stack;
413 return ECMD_FAILED;
414 }
415 }
416
417 if (lp->resize == LV_REDUCE) {
418 if (!exec_cmd("fsadm", "resize", lv_path, size_buf)) {
419 stack;
420 return ECMD_FAILED;
421 }
422 }
423 }
424
425 if (!archive(vg)) {
426 stack;
427 return ECMD_FAILED;
428 }
429
430 log_print("%sing logical volume %s to %s",
431 (lp->resize == LV_REDUCE) ? "Reduc" : "Extend",
432 lp->lv_name,
433 display_size(cmd, (uint64_t) lp->extents * vg->extent_size,
434 SIZE_SHORT));
435
436 if (lp->resize == LV_REDUCE) {
437 if (!lv_reduce(vg->fid, lv, lv->le_count - lp->extents)) {
438 stack;
439 return ECMD_FAILED;
440 }
441 } else if (!lv_extend(vg->fid, lv, lp->segtype, lp->stripes,
442 lp->stripe_size, 0u,
443 lp->extents - lv->le_count,
444 NULL, 0u, 0u, pvh, alloc)) {
445 stack;
446 return ECMD_FAILED;
447 }
448
449 /* store vg on disk(s) */
450 if (!vg_write(vg)) {
451 stack;
452 return ECMD_FAILED;
453 }
454
455 backup(vg);
456
457 /* If snapshot, must suspend all associated devices */
458 if ((snap = find_cow(lv)))
459 lock_lvid = snap->origin->lvid.s;
460 else
461 lock_lvid = lv->lvid.s;
462
463 if (!suspend_lv(cmd, lock_lvid)) {
464 log_error("Failed to suspend %s", lp->lv_name);
465 vg_revert(vg);
466 return ECMD_FAILED;
467 }
468
469 if (!vg_commit(vg)) {
470 stack;
471 resume_lv(cmd, lock_lvid);
472 return ECMD_FAILED;
473 }
474
475 if (!resume_lv(cmd, lock_lvid)) {
476 log_error("Problem reactivating %s", lp->lv_name);
477 return ECMD_FAILED;
478 }
479
480 log_print("Logical volume %s successfully resized", lp->lv_name);
481
482 if (lp->resizefs && (lp->resize == LV_EXTEND)) {
483 if (!exec_cmd("fsadm", "resize", lv_path, size_buf)) {
484 stack;
485 return ECMD_FAILED;
486 }
487 }
488
489 return ECMD_PROCESSED;
490 }
491
492 int lvresize(struct cmd_context *cmd, int argc, char **argv)
493 {
494 struct lvresize_params lp;
495 int r;
496
497 memset(&lp, 0, sizeof(lp));
498
499 if (!_read_params(cmd, argc, argv, &lp))
500 return EINVALID_CMD_LINE;
501
502 log_verbose("Finding volume group %s", lp.vg_name);
503 if (!lock_vol(cmd, lp.vg_name, LCK_VG_WRITE)) {
504 log_error("Can't get lock for %s", lp.vg_name);
505 return ECMD_FAILED;
506 }
507
508 if (!(r = _lvresize(cmd, &lp)))
509 stack;
510
511 unlock_vg(cmd, lp.vg_name);
512
513 return r;
514 }
This page took 0.062044 seconds and 6 git commands to generate.