--- LVM2.2.02.01/tools/commands.h.lvconvert-cow-to-snapshot 2006-04-04 13:44:46.000000000 +0100 +++ LVM2.2.02.01/tools/commands.h 2006-04-04 16:38:27.000000000 +0100 @@ -87,9 +87,18 @@ "\t[-m|--mirrors Mirrors]\n" "\t[-v|--verbose]\n" "\t[--version]" "\n" - "\tLogicalVolume[Path] [PhysicalVolume[Path]...]\n", + "\tLogicalVolume[Path] [PhysicalVolume[Path]...]\n\n" - alloc_ARG, mirrors_ARG, test_ARG) + "lvcreate -s|--snapshot\n" + "\t[-c|--chunksize]\n" + "\t[-d|--debug]\n" + "\t[-h|-?|--help]\n" + "\t[-v|--verbose]\n" + "\t[--version]" "\n" + "\t[-Z|--zero {y|n}]\n" + "\tLogicalVolume[Path] OriginalLogicalVolume[Path]\n\n", + + alloc_ARG, mirrors_ARG, snapshot_ARG, chunksize_ARG, zero_ARG) xx(lvcreate, "Create a logical volume", --- LVM2.2.02.01/tools/lvconvert.c.lvconvert-cow-to-snapshot 2006-04-04 13:44:59.000000000 +0100 +++ LVM2.2.02.01/tools/lvconvert.c 2006-04-04 16:40:44.000000000 +0100 @@ -15,7 +15,14 @@ #include "tools.h" struct lvconvert_params { + int snapshot; + int zero; + const char *lv_name; + + const char *origin; + uint32_t chunk_size; + uint32_t mirrors; alloc_policy_t alloc; @@ -30,17 +37,16 @@ { memset(lp, 0, sizeof(*lp)); + if (arg_count(cmd, snapshot_ARG)) + lp->snapshot = 1; + lp->alloc = ALLOC_INHERIT; if (arg_count(cmd, alloc_ARG)) lp->alloc = (alloc_policy_t) arg_uint_value(cmd, alloc_ARG, lp->alloc); - if (!arg_count(cmd, mirrors_ARG)) { - log_error("--mirrors argument required"); - return 0; - } - - lp->mirrors = arg_uint_value(cmd, mirrors_ARG, 0) + 1; + if (arg_count(cmd, mirrors_ARG)) + lp->mirrors = arg_uint_value(cmd, mirrors_ARG, 0) + 1; if (!argc) { log_error("Please give logical volume path"); @@ -50,6 +56,47 @@ lp->lv_name = argv[0]; argv++, argc--; + if (lp->snapshot) { + if (!activation()) { + log_error("Can't create snapshot without using " + "device-mapper kernel driver"); + return 0; + } + + if (lp->mirrors > 1) { + log_error("mirrors and snapshots are currently " + "incompatible"); + return 0; + } + + /* + * Should we zero the COW. + */ + lp->zero = strcmp(arg_str_value(cmd, zero_ARG, "n"), "n"); + + if (arg_sign_value(cmd, chunksize_ARG, 0) == SIGN_MINUS) { + log_error("Negative chunk size is invalid"); + return 0; + } + lp->chunk_size = 2 * arg_uint_value(cmd, chunksize_ARG, 8); + if (lp->chunk_size < 8 || lp->chunk_size > 1024 || + (lp->chunk_size & (lp->chunk_size - 1))) { + log_error("Chunk size must be a power of 2 in the " + "range 4K to 512K"); + return 0; + } + log_verbose("Setting chunksize to %d sectors.", lp->chunk_size); + + if (!argc) { + log_err("Please specify a logical volume to act as " + "the snapshot origin."); + return 0; + } + + lp->origin = argv[0]; + argv++, argc--; + } + lp->pv_count = argc; lp->pvs = argv; @@ -147,6 +194,67 @@ return 1; } +static int lvconvert_snapshot(struct cmd_context * cmd, struct logical_volume * lv, + struct lvconvert_params *lp) +{ + struct logical_volume *org; + + if (!(org = find_lv(lv->vg, lp->origin))) { + log_err("Couldn't find origin volume '%s'.", + lp->origin); + return 0; + } + + if (org->status & (LOCKED|PVMOVE) || lv_is_cow(org)) { + log_error("Unable to create a snapshot of a %s LV.", + org->status & LOCKED ? "locked" : + org->status & PVMOVE ? "pvmove" : "snapshot"); + return 0; + } + + if (lp->zero && !zero_lv(cmd, lv)) { + log_error("Aborting. Failed to wipe snapshot exception store."); + return 0; + } else { + log_error("WARNING: \"%s\" not zeroed", lv->name); + } + + if (!deactivate_lv(cmd, lv)) { + log_err("Couldn't deactivate LV %s.", lv->name); + return 0; + } + + if (!vg_add_snapshot(lv->vg->fid, NULL, org, lv, NULL, org->le_count, + lp->chunk_size)) { + log_err("Couldn't create snapshot."); + return 0; + } + + /* store vg on disk(s) */ + if (!vg_write(lv->vg)) + return 0; + + backup(lv->vg); + + if (!suspend_lv(cmd, org)) { + log_error("Failed to suspend origin %s", org->name); + vg_revert(lv->vg); + return 0; + } + + if (!vg_commit(lv->vg)) + return 0; + + if (!resume_lv(cmd, org)) { + log_error("Problem reactivating origin %s", org->name); + return 0; + } + + log_print("Logical volume %s converted to snapshot.", lv->name); + + return 1; +} + static int lvconvert_single(struct cmd_context *cmd, struct logical_volume *lv, void *handle) { @@ -181,12 +289,20 @@ return ECMD_FAILED; } + if (lp->snapshot) { + if (!archive(lv->vg)) + return ECMD_FAILED; + if (!lvconvert_snapshot(cmd, lv, lp)) + return ECMD_FAILED; + } + return ECMD_PROCESSED; } int lvconvert(struct cmd_context * cmd, int argc, char **argv) { const char *vg_name; + const char *vg_origin; char *st; int consistent = 1; struct volume_group *vg; @@ -209,6 +325,21 @@ if ((st = strrchr(lp.lv_name, '/'))) lp.lv_name = st + 1; + /* + * Ensure origin doesn't contain a different VG. + */ + if (lp.origin && strchr(lp.origin, '/')) { + if (!(vg_origin = extract_vgname(cmd, lp.origin))) + return 0; + + if (strcmp(vg_name, vg_origin)) { + log_error("Inconsistent volume group names " + "given: \"%s\" and \"%s\"", + vg_name, vg_origin); + return 0; + } + } + log_verbose("Checking for existing volume group \"%s\"", vg_name); if (!lock_vol(cmd, vg_name, LCK_VG_WRITE)) {