]> sourceware.org Git - lvm2.git/blob - tools/lvresize.c
Prevent snapshot origin resizing.
[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 if (lv_is_origin(lv)) {
184 log_error("Snapshot origin volumes cannot be resized yet.");
185 return ECMD_FAILED;
186 }
187
188 alloc = (alloc_policy_t) arg_uint_value(cmd, alloc_ARG, lv->alloc);
189
190 if (lp->size) {
191 if (lp->size % vg->extent_size) {
192 if (lp->sign == SIGN_MINUS)
193 lp->size -= lp->size % vg->extent_size;
194 else
195 lp->size += vg->extent_size -
196 (lp->size % vg->extent_size);
197
198 log_print("Rounding up size to full physical extent %s",
199 display_size(cmd, (uint64_t) lp->size,
200 SIZE_SHORT));
201 }
202
203 lp->extents = lp->size / vg->extent_size;
204 }
205
206 if (lp->sign == SIGN_PLUS)
207 lp->extents += lv->le_count;
208
209 if (lp->sign == SIGN_MINUS) {
210 if (lp->extents >= lv->le_count) {
211 log_error("Unable to reduce %s below 1 extent",
212 lp->lv_name);
213 return EINVALID_CMD_LINE;
214 }
215
216 lp->extents = lv->le_count - lp->extents;
217 }
218
219 if (!lp->extents) {
220 log_error("New size of 0 not permitted");
221 return EINVALID_CMD_LINE;
222 }
223
224 if (lp->extents == lv->le_count) {
225 log_error("New size (%d extents) matches existing size "
226 "(%d extents)", lp->extents, lv->le_count);
227 return EINVALID_CMD_LINE;
228 }
229
230 seg_size = lp->extents - lv->le_count;
231
232 /* Use segment type of last segment */
233 list_iterate_items(seg, &lv->segments) {
234 lp->segtype = seg->segtype;
235 }
236
237 /* FIXME Support LVs with mixed segment types */
238 if (lp->segtype != (struct segment_type *) arg_ptr_value(cmd, type_ARG,
239 lp->segtype)) {
240 log_error("VolumeType does not match (%s)", lp->segtype->name);
241 return EINVALID_CMD_LINE;
242 }
243
244 /* If extending, find stripes, stripesize & size of last segment */
245 if ((lp->extents > lv->le_count) &&
246 !(lp->stripes == 1 || (lp->stripes > 1 && lp->stripe_size))) {
247 list_iterate_items(seg, &lv->segments) {
248 if (!(seg->segtype->flags & SEG_AREAS_STRIPED))
249 continue;
250
251 sz = seg->stripe_size;
252 str = seg->area_count;
253
254 if ((seg_stripesize && seg_stripesize != sz
255 && !lp->stripe_size) ||
256 (seg_stripes && seg_stripes != str && !lp->stripes)) {
257 log_error("Please specify number of "
258 "stripes (-i) and stripesize (-I)");
259 return EINVALID_CMD_LINE;
260 }
261
262 seg_stripesize = sz;
263 seg_stripes = str;
264 }
265
266 if (!lp->stripes)
267 lp->stripes = seg_stripes;
268
269 if (!lp->stripe_size && lp->stripes > 1) {
270 if (seg_stripesize) {
271 log_print("Using stripesize of last segment "
272 "%dKB", seg_stripesize / 2);
273 lp->stripe_size = seg_stripesize;
274 } else {
275 lp->stripe_size =
276 find_config_int(cmd->cft->root,
277 "metadata/stripesize",
278 DEFAULT_STRIPESIZE) * 2;
279 log_print("Using default stripesize %dKB",
280 lp->stripe_size / 2);
281 }
282 }
283 }
284
285 /* If reducing, find stripes, stripesize & size of last segment */
286 if (lp->extents < lv->le_count) {
287 extents_used = 0;
288
289 if (lp->stripes || lp->stripe_size)
290 log_error("Ignoring stripes and stripesize arguments "
291 "when reducing");
292
293 list_iterate_items(seg, &lv->segments) {
294 seg_extents = seg->len;
295
296 if (seg->segtype->flags & SEG_AREAS_STRIPED) {
297 seg_stripesize = seg->stripe_size;
298 seg_stripes = seg->area_count;
299 }
300
301 if (lp->extents <= extents_used + seg_extents)
302 break;
303
304 extents_used += seg_extents;
305 }
306
307 seg_size = lp->extents - extents_used;
308 lp->stripe_size = seg_stripesize;
309 lp->stripes = seg_stripes;
310 }
311
312 if (lp->stripes > 1 && !lp->stripe_size) {
313 log_error("Stripesize for striped segment should not be 0!");
314 return EINVALID_CMD_LINE;
315 }
316
317 if ((lp->stripes > 1)) {
318 if (!(stripesize_extents = lp->stripe_size / vg->extent_size))
319 stripesize_extents = 1;
320
321 if ((size_rest = seg_size % (lp->stripes * stripesize_extents))) {
322 log_print("Rounding size (%d extents) down to stripe "
323 "boundary size for segment (%d extents)",
324 lp->extents, lp->extents - size_rest);
325 lp->extents = lp->extents - size_rest;
326 }
327 }
328
329 if (lp->extents == lv->le_count) {
330 log_error("New size (%d extents) matches existing size "
331 "(%d extents)", lp->extents, lv->le_count);
332 return EINVALID_CMD_LINE;
333 }
334
335 if (lp->extents < lv->le_count) {
336 if (lp->resize == LV_EXTEND) {
337 log_error("New size given (%d extents) not larger "
338 "than existing size (%d extents)",
339 lp->extents, lv->le_count);
340 return EINVALID_CMD_LINE;
341 } else
342 lp->resize = LV_REDUCE;
343 }
344
345 if (lp->extents > lv->le_count) {
346 if (lp->resize == LV_REDUCE) {
347 log_error("New size given (%d extents) not less than "
348 "existing size (%d extents)", lp->extents,
349 lv->le_count);
350 return EINVALID_CMD_LINE;
351 } else
352 lp->resize = LV_EXTEND;
353 }
354
355 if (lp->resize == LV_REDUCE) {
356 if (lp->argc)
357 log_print("Ignoring PVs on command line when reducing");
358 } else if (!(pvh = lp->argc ? create_pv_list(cmd->mem, vg, lp->argc,
359 lp->argv, 1) : &vg->pvs)) {
360 stack;
361 return ECMD_FAILED;
362 }
363
364 if (lp->resize == LV_REDUCE || lp->resizefs) {
365 memset(&info, 0, sizeof(info));
366
367 if (!lv_info(lv, &info) && driver_version(NULL, 0)) {
368 log_error("lv_info failed: aborting");
369 return ECMD_FAILED;
370 }
371
372 if (lp->resizefs && !info.exists) {
373 log_error("Logical volume %s must be activated "
374 "before resizing filesystem", lp->lv_name);
375 return ECMD_FAILED;
376 }
377
378 if (info.exists && !lp->resizefs && (lp->resize == LV_REDUCE)) {
379 log_print("WARNING: Reducing active%s logical volume "
380 "to %s", info.open_count ? " and open" : "",
381 display_size(cmd, (uint64_t) lp->extents *
382 vg->extent_size,
383 SIZE_SHORT));
384
385 log_print("THIS MAY DESTROY YOUR DATA "
386 "(filesystem etc.)");
387
388 if (!arg_count(cmd, force_ARG)) {
389 if (yes_no_prompt("Do you really want to "
390 "reduce %s? [y/n]: ",
391 lp->lv_name) == 'n') {
392 log_print("Logical volume %s NOT "
393 "reduced", lp->lv_name);
394 return ECMD_FAILED;
395 }
396 }
397 }
398 }
399
400 if (lp->resizefs) {
401 if (lvm_snprintf(lv_path, PATH_MAX, "%s%s/%s", cmd->dev_dir,
402 lp->vg_name, lp->lv_name) < 0) {
403 log_error("Couldn't create LV path for %s",
404 lp->lv_name);
405 return ECMD_FAILED;
406 }
407
408 if (lvm_snprintf(size_buf, SIZE_BUF, "%" PRIu64,
409 (uint64_t) lp->extents * vg->extent_size / 2)
410 < 0) {
411 log_error("Couldn't generate new LV size string");
412 return ECMD_FAILED;
413 }
414
415 if (!lp->nofsck) {
416 if (!exec_cmd("fsadm", "check", lv_path, NULL)) {
417 stack;
418 return ECMD_FAILED;
419 }
420 }
421
422 if (lp->resize == LV_REDUCE) {
423 if (!exec_cmd("fsadm", "resize", lv_path, size_buf)) {
424 stack;
425 return ECMD_FAILED;
426 }
427 }
428 }
429
430 if (!archive(vg)) {
431 stack;
432 return ECMD_FAILED;
433 }
434
435 log_print("%sing logical volume %s to %s",
436 (lp->resize == LV_REDUCE) ? "Reduc" : "Extend",
437 lp->lv_name,
438 display_size(cmd, (uint64_t) lp->extents * vg->extent_size,
439 SIZE_SHORT));
440
441 if (lp->resize == LV_REDUCE) {
442 if (!lv_reduce(vg->fid, lv, lv->le_count - lp->extents)) {
443 stack;
444 return ECMD_FAILED;
445 }
446 } else if (!lv_extend(vg->fid, lv, lp->segtype, lp->stripes,
447 lp->stripe_size, 0u,
448 lp->extents - lv->le_count,
449 NULL, 0u, 0u, pvh, alloc)) {
450 stack;
451 return ECMD_FAILED;
452 }
453
454 /* store vg on disk(s) */
455 if (!vg_write(vg)) {
456 stack;
457 return ECMD_FAILED;
458 }
459
460 backup(vg);
461
462 /* If snapshot, must suspend all associated devices */
463 if ((snap = find_cow(lv)))
464 lock_lvid = snap->origin->lvid.s;
465 else
466 lock_lvid = lv->lvid.s;
467
468 if (!suspend_lv(cmd, lock_lvid)) {
469 log_error("Failed to suspend %s", lp->lv_name);
470 vg_revert(vg);
471 return ECMD_FAILED;
472 }
473
474 if (!vg_commit(vg)) {
475 stack;
476 resume_lv(cmd, lock_lvid);
477 return ECMD_FAILED;
478 }
479
480 if (!resume_lv(cmd, lock_lvid)) {
481 log_error("Problem reactivating %s", lp->lv_name);
482 return ECMD_FAILED;
483 }
484
485 log_print("Logical volume %s successfully resized", lp->lv_name);
486
487 if (lp->resizefs && (lp->resize == LV_EXTEND)) {
488 if (!exec_cmd("fsadm", "resize", lv_path, size_buf)) {
489 stack;
490 return ECMD_FAILED;
491 }
492 }
493
494 return ECMD_PROCESSED;
495 }
496
497 int lvresize(struct cmd_context *cmd, int argc, char **argv)
498 {
499 struct lvresize_params lp;
500 int r;
501
502 memset(&lp, 0, sizeof(lp));
503
504 if (!_read_params(cmd, argc, argv, &lp))
505 return EINVALID_CMD_LINE;
506
507 log_verbose("Finding volume group %s", lp.vg_name);
508 if (!lock_vol(cmd, lp.vg_name, LCK_VG_WRITE)) {
509 log_error("Can't get lock for %s", lp.vg_name);
510 return ECMD_FAILED;
511 }
512
513 if (!(r = _lvresize(cmd, &lp)))
514 stack;
515
516 unlock_vg(cmd, lp.vg_name);
517
518 return r;
519 }
This page took 0.06357 seconds and 6 git commands to generate.