diff options
author | Anas Nashif <anas.nashif@intel.com> | 2013-03-05 01:47:43 -0800 |
---|---|---|
committer | Anas Nashif <anas.nashif@intel.com> | 2013-03-05 01:47:43 -0800 |
commit | 44a3c2255bc480c82f34db156553a595606d8a0b (patch) | |
tree | 5e6df96a6c6e40207cb3a711860e16b543918c0d /tools | |
parent | 8bd28eea831fd5215c12e6fcecc8e9a772398ed9 (diff) | |
download | device-mapper-44a3c2255bc480c82f34db156553a595606d8a0b.tar.gz device-mapper-44a3c2255bc480c82f34db156553a595606d8a0b.tar.bz2 device-mapper-44a3c2255bc480c82f34db156553a595606d8a0b.zip |
Imported Upstream version 2.02.98upstream/2.02.98upstream/1.02.77
Diffstat (limited to 'tools')
45 files changed, 4011 insertions, 1906 deletions
diff --git a/tools/Makefile.in b/tools/Makefile.in index 70a1b61..15843f0 100644 --- a/tools/Makefile.in +++ b/tools/Makefile.in @@ -1,6 +1,6 @@ # # Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved. -# Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved. +# Copyright (C) 2004-2012 Red Hat, Inc. All rights reserved. # # This file is part of LVM2. # @@ -132,7 +132,7 @@ dmsetup.static: dmsetup.o $(interfacebuilddir)/libdevmapper.a all: device-mapper lvm: $(OBJECTS) lvm.o $(top_builddir)/lib/liblvm-internal.a - $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(OBJECTS) lvm.o \ + $(CC) $(CFLAGS) $(LDFLAGS) $(ELDFLAGS) -o $@ $(OBJECTS) lvm.o \ $(LVMLIBS) $(READLINE_LIBS) $(LIBS) -rdynamic lvm.static: $(OBJECTS) lvm-static.o $(top_builddir)/lib/liblvm-internal.a $(interfacebuilddir)/libdevmapper.a @@ -203,5 +203,3 @@ install_device-mapper: $(INSTALL_DMSETUP_TARGETS) install_lvm2: $(INSTALL_LVM_TARGETS) install: install_lvm2 install_device-mapper - -DISTCLEAN_TARGETS += .exported_symbols_generated diff --git a/tools/args.h b/tools/args.h index ff7514b..0d9605a 100644 --- a/tools/args.h +++ b/tools/args.h @@ -18,7 +18,6 @@ */ /* *INDENT-OFF* */ arg(version_ARG, '\0', "version", NULL, 0) -arg(quiet_ARG, '\0', "quiet", NULL, 0) arg(physicalvolumesize_ARG, '\0', "setphysicalvolumesize", size_mb_arg, 0) arg(ignorelockingfailure_ARG, '\0', "ignorelockingfailure", NULL, 0) arg(nolocking_ARG, '\0', "nolocking", NULL, 0) @@ -44,7 +43,7 @@ arg(addtag_ARG, '\0', "addtag", tag_arg, ARG_GROUPABLE) arg(deltag_ARG, '\0', "deltag", tag_arg, ARG_GROUPABLE) arg(refresh_ARG, '\0', "refresh", NULL, 0) arg(mknodes_ARG, '\0', "mknodes", NULL, 0) -arg(minor_ARG, '\0', "minor", minor_arg, 0) +arg(minor_ARG, '\0', "minor", int_arg, ARG_GROUPABLE) arg(type_ARG, '\0', "type", segtype_arg, 0) arg(alloc_ARG, '\0', "alloc", alloc_arg, 0) arg(separator_ARG, '\0', "separator", string_arg, 0) @@ -54,11 +53,14 @@ arg(resync_ARG, '\0', "resync", NULL, 0) arg(corelog_ARG, '\0', "corelog", NULL, 0) arg(mirrorlog_ARG, '\0', "mirrorlog", string_arg, 0) arg(splitmirrors_ARG, '\0', "splitmirrors", int_arg, 0) +arg(trackchanges_ARG, '\0', "trackchanges", NULL, 0) +arg(replace_ARG, '\0', "replace", string_arg, ARG_GROUPABLE) arg(repair_ARG, '\0', "repair", NULL, 0) arg(use_policies_ARG, '\0', "use-policies", NULL, 0) arg(monitor_ARG, '\0', "monitor", yes_no_arg, 0) arg(config_ARG, '\0', "config", string_arg, 0) arg(trustcache_ARG, '\0', "trustcache", NULL, 0) +arg(cache_ARG, '\0', "cache", NULL, 0) arg(ignoremonitoring_ARG, '\0', "ignoremonitoring", NULL, 0) arg(nameprefixes_ARG, '\0', "nameprefixes", NULL, 0) arg(unquoted_ARG, '\0', "unquoted", NULL, 0) @@ -66,20 +68,24 @@ arg(rows_ARG, '\0', "rows", NULL, 0) arg(dataalignment_ARG, '\0', "dataalignment", size_kb_arg, 0) arg(dataalignmentoffset_ARG, '\0', "dataalignmentoffset", size_kb_arg, 0) arg(virtualoriginsize_ARG, '\0', "virtualoriginsize", size_mb_arg, 0) -arg(virtualsize_ARG, '\0', "virtualsize", size_mb_arg, 0) arg(noudevsync_ARG, '\0', "noudevsync", NULL, 0) arg(poll_ARG, '\0', "poll", yes_no_arg, 0) +arg(poolmetadata_ARG, '\0', "poolmetadata", string_arg, 0) +arg(poolmetadatasize_ARG, '\0', "poolmetadatasize", size_mb_arg, 0) +arg(discards_ARG, '\0', "discards", discards_arg, 0) arg(stripes_long_ARG, '\0', "stripes", int_arg, 0) arg(sysinit_ARG, '\0', "sysinit", NULL, 0) +arg(thinpool_ARG, '\0', "thinpool", string_arg, 0) /* Allow some variations */ arg(resizable_ARG, '\0', "resizable", yes_no_arg, 0) arg(allocation_ARG, '\0', "allocation", yes_no_arg, 0) +arg(available_ARG, '\0', "available", activation_arg, 0) /* * ... and now the short args. */ -arg(available_ARG, 'a', "available", yes_no_excl_arg, 0) +arg(activate_ARG, 'a', "activate", activation_arg, 0) arg(all_ARG, 'a', "all", NULL, 0) arg(autobackup_ARG, 'A', "autobackup", yes_no_arg, 0) arg(activevolumegroups_ARG, 'A', "activevolumegroups", NULL, 0) @@ -111,7 +117,7 @@ arg(size_ARG, 'L', "size", size_mb_arg, 0) arg(logicalextent_ARG, 'L', "logicalextent", int_arg_with_sign, 0) arg(persistent_ARG, 'M', "persistent", yes_no_arg, 0) arg(merge_ARG, '\0', "merge", NULL, 0) -arg(major_ARG, 'j', "major", major_arg, 0) +arg(major_ARG, 'j', "major", int_arg, ARG_GROUPABLE) arg(mirrors_ARG, 'm', "mirrors", int_arg_with_sign, 0) arg(metadatatype_ARG, 'M', "metadatatype", metadatatype_arg, 0) arg(maps_ARG, 'm', "maps", NULL, 0) @@ -125,6 +131,7 @@ arg(permission_ARG, 'p', "permission", permission_arg, 0) arg(maxphysicalvolumes_ARG, 'p', "maxphysicalvolumes", int_arg, 0) arg(partial_ARG, 'P', "partial", NULL, 0) arg(physicalvolume_ARG, 'P', "physicalvolume", NULL, 0) +arg(quiet_ARG, 'q', "quiet", NULL, ARG_COUNTABLE) arg(readahead_ARG, 'r', "readahead", readahead_arg, 0) arg(resizefs_ARG, 'r', "resizefs", NULL, 0) arg(reset_ARG, 'R', "reset", NULL, 0) @@ -133,12 +140,14 @@ arg(physicalextentsize_ARG, 's', "physicalextentsize", size_mb_arg, 0) arg(stdin_ARG, 's', "stdin", NULL, 0) arg(snapshot_ARG, 's', "snapshot", NULL, 0) arg(short_ARG, 's', "short", NULL, 0) +arg(thin_ARG, 'T', "thin", NULL, 0) arg(test_ARG, 't', "test", NULL, 0) arg(uuid_ARG, 'u', "uuid", NULL, 0) arg(uuidstr_ARG, 'u', "uuid", string_arg, 0) arg(uuidlist_ARG, 'U', "uuidlist", NULL, 0) arg(verbose_ARG, 'v', "verbose", NULL, ARG_COUNTABLE) arg(volumegroup_ARG, 'V', "volumegroup", NULL, 0) +arg(virtualsize_ARG, 'V', "virtualsize", size_mb_arg, 0) arg(allocatable_ARG, 'x', "allocatable", yes_no_arg, 0) arg(resizeable_ARG, 'x', "resizeable", yes_no_arg, 0) arg(yes_ARG, 'y', "yes", NULL, 0) diff --git a/tools/commands.h b/tools/commands.h index fa177cd..6415d34 100644 --- a/tools/commands.h +++ b/tools/commands.h @@ -1,6 +1,6 @@ /* * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved. - * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved. + * Copyright (C) 2004-2012 Red Hat, Inc. All rights reserved. * * This file is part of LVM2. * @@ -61,7 +61,7 @@ xx(lvchange, CACHE_VGMETADATA | PERMITTED_READ_ONLY, "lvchange\n" "\t[-A|--autobackup y|n]\n" - "\t[-a|--available [e|l]y|n]\n" + "\t[-a|--activate [a|e|l]{y|n}]\n" "\t[--addtag Tag]\n" "\t[--alloc AllocationPolicy]\n" "\t[-C|--contiguous y|n]\n" @@ -69,6 +69,7 @@ xx(lvchange, "\t[--deltag Tag]\n" "\t[-f|--force]\n" "\t[-h|--help]\n" + "\t[--discards {ignore|nopassdown|passdown}]\n" "\t[--ignorelockingfailure]\n" "\t[--ignoremonitoring]\n" "\t[--monitor {y|n}]\n" @@ -84,21 +85,25 @@ xx(lvchange, "\t[-t|--test]\n" "\t[-v|--verbose]\n" "\t[-y|--yes]\n" - "\t[--version]" "\n" + "\t[--version]\n" + "\t[-Z|--zero {y|n}]\n" "\tLogicalVolume[Path] [LogicalVolume[Path]...]\n", - alloc_ARG, autobackup_ARG, available_ARG, contiguous_ARG, force_ARG, - ignorelockingfailure_ARG, ignoremonitoring_ARG, major_ARG, minor_ARG, - monitor_ARG, noudevsync_ARG, partial_ARG, permission_ARG, persistent_ARG, - poll_ARG, readahead_ARG, resync_ARG, refresh_ARG, addtag_ARG, deltag_ARG, - sysinit_ARG, test_ARG, yes_ARG) + alloc_ARG, autobackup_ARG, activate_ARG, available_ARG, contiguous_ARG, + discards_ARG, force_ARG, ignorelockingfailure_ARG, ignoremonitoring_ARG, + major_ARG, minor_ARG, monitor_ARG, noudevsync_ARG, partial_ARG, + permission_ARG, persistent_ARG, poll_ARG, readahead_ARG, resync_ARG, + refresh_ARG, addtag_ARG, deltag_ARG, sysinit_ARG, test_ARG, yes_ARG, + zero_ARG) xx(lvconvert, "Change logical volume layout", 0, "lvconvert " "[-m|--mirrors Mirrors [{--mirrorlog {disk|core|mirrored}|--corelog}]]\n" + "\t[--type SegmentType]\n" "\t[--repair [--use-policies]]\n" + "\t[--replace PhysicalVolume]\n" "\t[-R|--regionsize MirrorLogRegionSize]\n" "\t[--alloc AllocationPolicy]\n" "\t[-b|--background]\n" @@ -114,6 +119,7 @@ xx(lvconvert, "\tLogicalVolume[Path] [PhysicalVolume[Path]...]\n\n" "lvconvert " + "[--splitmirrors Images --trackchanges]\n" "[--splitmirrors Images --name SplitLogicalVolumeName]\n" "\tLogicalVolume[Path] [SplittablePhysicalVolume[Path]...]\n\n" @@ -135,12 +141,21 @@ xx(lvconvert, "\t[-d|--debug]\n" "\t[-h|-?|--help]\n" "\t[-v|--verbose]\n" - "\tSnapshotLogicalVolume[Path]\n", + "\tLogicalVolume[Path]\n\n" + + "lvconvert " + "--thinpool ThinPoolLogicalVolume[Path]\n" + "\t[--chunksize size]\n" + "\t[--discards {ignore|nopassdown|passdown}]\n" + "\t[[--poolmetadatasize size] | --poolmetadata ThinMetadataLogicalVolume[Path]]\n" + "\t[-Z|--zero {y|n}]\n" + "\t[-d|--debug] [-h|-?|--help] [-v|--verbose]\n", alloc_ARG, background_ARG, chunksize_ARG, corelog_ARG, interval_ARG, merge_ARG, mirrorlog_ARG, mirrors_ARG, name_ARG, noudevsync_ARG, - regionsize_ARG, repair_ARG, snapshot_ARG, splitmirrors_ARG, - stripes_long_ARG, stripesize_ARG, test_ARG, + regionsize_ARG, repair_ARG, replace_ARG, snapshot_ARG, splitmirrors_ARG, + trackchanges_ARG, type_ARG, stripes_long_ARG, stripesize_ARG, test_ARG, + chunksize_ARG, discards_ARG, poolmetadata_ARG, poolmetadatasize_ARG, thinpool_ARG, use_policies_ARG, yes_ARG, force_ARG, zero_ARG) xx(lvcreate, @@ -148,6 +163,7 @@ xx(lvcreate, 0, "lvcreate " "\n" "\t[-A|--autobackup {y|n}]\n" + "\t[-a|--activate [a|e|l]{y|n}]\n" "\t[--addtag Tag]\n" "\t[--alloc AllocationPolicy]\n" "\t[-C|--contiguous {y|n}]\n" @@ -165,6 +181,10 @@ xx(lvcreate, "\t[-p|--permission {r|rw}]\n" "\t[-r|--readahead ReadAheadSectors|auto|none]\n" "\t[-R|--regionsize MirrorLogRegionSize]\n" + "\t[-T|--thin [-c|--chunksize ChunkSize]\n" + "\t [--discards {ignore|nopassdown|passdown}]\n" + "\t [--poolmetadatasize MetadataSize[bBsSkKmMgG]]]\n" + "\t[--thinpool ThinPoolLogicalVolume{Name|Path}]\n" "\t[-t|--test]\n" "\t[--type VolumeType]\n" "\t[-v|--verbose]\n" @@ -174,36 +194,42 @@ xx(lvcreate, "lvcreate \n" "\t{ {-s|--snapshot} OriginalLogicalVolume[Path] |\n" - "\t [-s|--snapshot] VolumeGroupName[Path] --virtualsize VirtualSize}\n" + "\t [-s|--snapshot] VolumeGroupName[Path] -V|--virtualsize VirtualSize}\n" + "\t {-T|--thin} VolumeGroupName[Path][/PoolLogicalVolume] \n" + "\t -V|--virtualsize VirtualSize}\n" "\t[-c|--chunksize]\n" "\t[-A|--autobackup {y|n}]\n" "\t[--addtag Tag]\n" "\t[--alloc AllocationPolicy]\n" "\t[-C|--contiguous {y|n}]\n" "\t[-d|--debug]\n" + "\t[--discards {ignore|nopassdown|passdown}]\n" "\t[-h|-?|--help]\n" "\t[--ignoremonitoring]\n" "\t[--monitor {y|n}]\n" "\t[-i|--stripes Stripes [-I|--stripesize StripeSize]]\n" "\t{-l|--extents LogicalExtentsNumber[%{VG|FREE|ORIGIN}] |\n" "\t -L|--size LogicalVolumeSize[bBsSkKmMgGtTpPeE]}\n" + "\t[--poolmetadatasize Size[bBsSkKmMgG]]\n" "\t[-M|--persistent {y|n}] [--major major] [--minor minor]\n" "\t[-n|--name LogicalVolumeName]\n" "\t[--noudevsync]\n" "\t[-p|--permission {r|rw}]\n" "\t[-r|--readahead ReadAheadSectors|auto|none]\n" "\t[-t|--test]\n" + "\t[--thinpool ThinPoolLogicalVolume[Path]]\n" "\t[-v|--verbose]\n" "\t[--version]\n" "\t[PhysicalVolumePath...]\n\n", - addtag_ARG, alloc_ARG, autobackup_ARG, chunksize_ARG, contiguous_ARG, - corelog_ARG, extents_ARG, ignoremonitoring_ARG, major_ARG, minor_ARG, - mirrorlog_ARG, mirrors_ARG, monitor_ARG, name_ARG, nosync_ARG, noudevsync_ARG, - permission_ARG, persistent_ARG, readahead_ARG, regionsize_ARG, size_ARG, - snapshot_ARG, stripes_ARG, stripesize_ARG, test_ARG, type_ARG, - virtualoriginsize_ARG, virtualsize_ARG, zero_ARG) + addtag_ARG, alloc_ARG, autobackup_ARG, activate_ARG, available_ARG, + chunksize_ARG, contiguous_ARG, corelog_ARG, discards_ARG, extents_ARG, + ignoremonitoring_ARG, major_ARG, minor_ARG, mirrorlog_ARG, mirrors_ARG, + monitor_ARG, name_ARG, nosync_ARG, noudevsync_ARG, permission_ARG, + persistent_ARG, readahead_ARG, regionsize_ARG, size_ARG, snapshot_ARG, + stripes_ARG, stripesize_ARG, test_ARG, thin_ARG, thinpool_ARG, type_ARG, + virtualoriginsize_ARG, poolmetadatasize_ARG, virtualsize_ARG, zero_ARG) xx(lvdisplay, "Display information about a logical volume", @@ -259,6 +285,7 @@ xx(lvextend, "\t{-l|--extents [+]LogicalExtentsNumber[%{VG|LV|PVS|FREE|ORIGIN}] |\n" "\t -L|--size [+]LogicalVolumeSize[bBsSkKmMgGtTpPeE]}\n" "\t[-m|--mirrors Mirrors]\n" + "\t[--nosync]\n" "\t[--use-policies]\n" "\t[-n|--nofsck]\n" "\t[--noudevsync]\n" @@ -270,7 +297,7 @@ xx(lvextend, "\tLogicalVolume[Path] [ PhysicalVolumePath... ]\n", alloc_ARG, autobackup_ARG, extents_ARG, force_ARG, mirrors_ARG, - nofsck_ARG, noudevsync_ARG, resizefs_ARG, size_ARG, stripes_ARG, + nofsck_ARG, nosync_ARG, noudevsync_ARG, resizefs_ARG, size_ARG, stripes_ARG, stripesize_ARG, test_ARG, type_ARG, use_policies_ARG) xx(lvmchange, @@ -361,7 +388,7 @@ xx(lvremove, xx(lvrename, "Rename a logical volume", 0, - "lvrename " + "lvrename\n" "\t[-A|--autobackup {y|n}] " "\n" "\t[-d|--debug] " "\n" "\t[-h|-?|--help] " "\n" @@ -653,6 +680,8 @@ xx(pvscan, "List all physical volumes", PERMITTED_READ_ONLY, "pvscan " "\n" + "\t[-a|--activate ay]\n" + "\t[--cache [ DevicePath | --major major --minor minor]...]\n" "\t[-d|--debug] " "\n" "\t{-e|--exported | -n|--novolumegroup} " "\n" "\t[-h|-?|--help]" "\n" @@ -663,8 +692,9 @@ xx(pvscan, "\t[-v|--verbose] " "\n" "\t[--version]\n", - exported_ARG, ignorelockingfailure_ARG, novolumegroup_ARG, partial_ARG, - short_ARG, uuid_ARG) + activate_ARG, available_ARG, cache_ARG, exported_ARG, + ignorelockingfailure_ARG, major_ARG, minor_ARG, + novolumegroup_ARG, partial_ARG, short_ARG, uuid_ARG) xx(segtypes, "List available segment types", @@ -723,7 +753,7 @@ xx(vgchange, "\t[-u|--uuid] " "\n" "\t[-v|--verbose] " "\n" "\t[--version]" "\n" - "\t{-a|--available [e|l]{y|n} |" "\n" + "\t{-a|--activate [a|e|l]{y|n} |" "\n" "\t -c|--clustered {y|n} |" "\n" "\t -x|--resizeable {y|n} |" "\n" "\t -l|--logicalvolume MaxLogicalVolumes |" "\n" @@ -733,11 +763,11 @@ xx(vgchange, "\t --deltag Tag}\n" "\t[VolumeGroupName...]\n", - addtag_ARG, alloc_ARG, allocation_ARG, autobackup_ARG, available_ARG, - clustered_ARG, deltag_ARG, ignorelockingfailure_ARG, ignoremonitoring_ARG, - logicalvolume_ARG, maxphysicalvolumes_ARG, monitor_ARG, noudevsync_ARG, - metadatacopies_ARG, vgmetadatacopies_ARG, partial_ARG, - physicalextentsize_ARG, poll_ARG, refresh_ARG, resizeable_ARG, + addtag_ARG, alloc_ARG, allocation_ARG, autobackup_ARG, activate_ARG, + available_ARG, clustered_ARG, deltag_ARG, ignorelockingfailure_ARG, + ignoremonitoring_ARG, logicalvolume_ARG, maxphysicalvolumes_ARG, + monitor_ARG, noudevsync_ARG, metadatacopies_ARG, vgmetadatacopies_ARG, + partial_ARG, physicalextentsize_ARG, poll_ARG, refresh_ARG, resizeable_ARG, resizable_ARG, sysinit_ARG, test_ARG, uuid_ARG) xx(vgck, @@ -993,6 +1023,7 @@ xx(vgscan, "Search for all volume groups", PERMITTED_READ_ONLY, "vgscan " + "\t[--cache]\n" "\t[-d|--debug]\n" "\t[-h|--help]\n" "\t[--ignorelockingfailure]\n" @@ -1001,7 +1032,7 @@ xx(vgscan, "\t[-v|--verbose]\n" "\t[--version]" "\n", - ignorelockingfailure_ARG, mknodes_ARG, partial_ARG) + cache_ARG, ignorelockingfailure_ARG, mknodes_ARG, partial_ARG) xx(vgsplit, "Move physical volumes into a new or existing volume group", diff --git a/tools/dmsetup.c b/tools/dmsetup.c index 2eeee7f..196c170 100644 --- a/tools/dmsetup.c +++ b/tools/dmsetup.c @@ -1,6 +1,6 @@ /* * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved. - * Copyright (C) 2004-2008 Red Hat, Inc. All rights reserved. + * Copyright (C) 2004-2012 Red Hat, Inc. All rights reserved. * Copyright (C) 2005-2007 NEC Corporation * * This file is part of the device-mapper userspace tools. @@ -30,7 +30,6 @@ #include <dirent.h> #include <errno.h> #include <unistd.h> -#include <libgen.h> #include <sys/wait.h> #include <unistd.h> #include <sys/param.h> @@ -45,7 +44,6 @@ # include <sys/types.h> # include <sys/ipc.h> # include <sys/sem.h> -# define LIBUDEV_I_KNOW_THE_API_IS_SUBJECT_TO_CHANGE # include <libudev.h> #endif @@ -118,12 +116,16 @@ extern char *optarg; */ enum { READ_ONLY = 0, + ADD_NODE_ON_CREATE_ARG, + ADD_NODE_ON_RESUME_ARG, + CHECKS_ARG, COLS_ARG, EXEC_ARG, FORCE_ARG, GID_ARG, HELP_ARG, INACTIVE_ARG, + MANGLENAME_ARG, MAJOR_ARG, MINOR_ARG, MODE_ARG, @@ -138,6 +140,7 @@ enum { NOUDEVSYNC_ARG, OPTIONS_ARG, READAHEAD_ARG, + RETRY_ARG, ROWS_ARG, SEPARATOR_ARG, SETUUID_ARG, @@ -151,6 +154,7 @@ enum { UNQUOTED_ARG, UUID_ARG, VERBOSE_ARG, + VERIFYUDEV_ARG, VERSION_ARG, YES_ARG, NUM_SWITCHES @@ -164,6 +168,12 @@ typedef enum { DR_NAME = 16 } report_type_t; +typedef enum { + DN_DEVNO, /* Major and minor number pair */ + DN_BLK, /* Block device name (e.g. dm-0) */ + DN_MAP /* Map name (for dm devices only, equal to DN_BLK otherwise) */ +} dev_name_t; + static int _switches[NUM_SWITCHES]; static int _int_args[NUM_SWITCHES]; static char *_string_args[NUM_SWITCHES]; @@ -178,18 +188,22 @@ static int _udev_only; static struct dm_tree *_dtree; static struct dm_report *_report; static report_type_t _report_type; +static dev_name_t _dev_name_type; /* * Commands */ -typedef int (*command_fn) (int argc, char **argv, void *data); +struct command; +#define CMD_ARGS const struct command *cmd, int argc, char **argv, struct dm_names *names, int multiple_devices +typedef int (*command_fn) (CMD_ARGS); struct command { const char *name; const char *help; int min_args; int max_args; + int repeatable_cmd; /* Repeat to process device list? */ command_fn fn; }; @@ -312,6 +326,9 @@ static struct dm_task *_get_deps_task(int major, int minor) if (_switches[INACTIVE_ARG] && !dm_task_query_inactive_table(dmt)) goto err; + if (_switches[CHECKS_ARG] && !dm_task_enable_checks(dmt)) + goto err; + if (!dm_task_run(dmt)) goto err; @@ -362,7 +379,11 @@ static struct dm_split_name *_get_split_name(const char *uuid, const char *name, return NULL; } - split_name->subsystem = _extract_uuid_prefix(uuid, separator); + if (!(split_name->subsystem = _extract_uuid_prefix(uuid, separator))) { + dm_free(split_name); + return_NULL; + } + split_name->vg_name = split_name->lv_name = split_name->lv_layer = (char *) ""; @@ -405,16 +426,24 @@ static int _display_info_cols(struct dm_task *dmt, struct dm_info *info) obj.split_name = NULL; if (_report_type & DR_TREE) - obj.tree_node = dm_tree_find_node(_dtree, info->major, info->minor); + if (!(obj.tree_node = dm_tree_find_node(_dtree, info->major, info->minor))) { + log_error("Cannot find node %d:%d.", info->major, info->minor); + goto out; + } if (_report_type & DR_DEPS) - obj.deps_task = _get_deps_task(info->major, info->minor); + if (!(obj.deps_task = _get_deps_task(info->major, info->minor))) { + log_error("Cannot get deps for %d:%d.", info->major, info->minor); + goto out; + } if (_report_type & DR_NAME) - obj.split_name = _get_split_name(dm_task_get_uuid(dmt), dm_task_get_name(dmt), '-'); + if (!(obj.split_name = _get_split_name(dm_task_get_uuid(dmt), + dm_task_get_name(dmt), '-'))) + goto_out; if (!dm_report_object(_report, &obj)) - goto out; + goto_out; r = 1; @@ -505,7 +534,23 @@ static int _set_task_device(struct dm_task *dmt, const char *name, int optional) return 1; } -static int _load(int argc, char **argv, void *data __attribute__((unused))) +static int _set_task_add_node(struct dm_task *dmt) +{ + if (!dm_task_set_add_node(dmt, DEFAULT_DM_ADD_NODE)) + return 0; + + if (_switches[ADD_NODE_ON_RESUME_ARG] && + !dm_task_set_add_node(dmt, DM_ADD_NODE_ON_RESUME)) + return 0; + + if (_switches[ADD_NODE_ON_CREATE_ARG] && + !dm_task_set_add_node(dmt, DM_ADD_NODE_ON_CREATE)) + return 0; + + return 1; +} + +static int _load(CMD_ARGS) { int r = 0; struct dm_task *dmt; @@ -551,6 +596,9 @@ static int _load(int argc, char **argv, void *data __attribute__((unused))) if (_switches[INACTIVE_ARG] && !dm_task_query_inactive_table(dmt)) goto out; + if (_switches[CHECKS_ARG] && !dm_task_enable_checks(dmt)) + goto out; + if (!dm_task_run(dmt)) goto out; @@ -565,7 +613,7 @@ static int _load(int argc, char **argv, void *data __attribute__((unused))) return r; } -static int _create(int argc, char **argv, void *data __attribute__((unused))) +static int _create(CMD_ARGS) { int r = 0; struct dm_task *dmt; @@ -624,11 +672,17 @@ static int _create(int argc, char **argv, void *data __attribute__((unused))) udev_flags |= DM_UDEV_DISABLE_DM_RULES_FLAG | DM_UDEV_DISABLE_SUBSYSTEM_RULES_FLAG; - if (_udev_cookie) { + if (_switches[CHECKS_ARG] && !dm_task_enable_checks(dmt)) + goto out; + + if (!_set_task_add_node(dmt)) + goto out; + + if (_udev_cookie) cookie = _udev_cookie; - if (_udev_only) - udev_flags |= DM_UDEV_DISABLE_LIBRARY_FALLBACK; - } + + if (_udev_only) + udev_flags |= DM_UDEV_DISABLE_LIBRARY_FALLBACK; if (!dm_task_set_cookie(dmt, &cookie, udev_flags) || !dm_task_run(dmt)) @@ -636,26 +690,19 @@ static int _create(int argc, char **argv, void *data __attribute__((unused))) r = 1; + out: if (!_udev_cookie) (void) dm_udev_wait(cookie); - if (_switches[VERBOSE_ARG]) + if (r && _switches[VERBOSE_ARG]) r = _display_info(dmt); dm_task_destroy(dmt); return r; - - out: - if (!_udev_cookie) - (void) dm_udev_wait(cookie); - dm_task_destroy(dmt); - - return r; } -static int _rename(int argc, char **argv, void *data __attribute__((unused))) -{ +static int _do_rename(const char *name, const char *new_name, const char *new_uuid) { int r = 0; struct dm_task *dmt; uint32_t cookie = 0; @@ -665,13 +712,13 @@ static int _rename(int argc, char **argv, void *data __attribute__((unused))) return 0; /* FIXME Kernel doesn't support uuid or device number here yet */ - if (!_set_task_device(dmt, (argc == 3) ? argv[1] : NULL, 0)) + if (!_set_task_device(dmt, name, 0)) goto out; - if (_switches[SETUUID_ARG]) { - if (!dm_task_set_newuuid(dmt, argv[argc - 1])) + if (new_uuid) { + if (!dm_task_set_newuuid(dmt, new_uuid)) goto out; - } else if (!dm_task_set_newname(dmt, argv[argc - 1])) + } else if (!new_name || !dm_task_set_newname(dmt, new_name)) goto out; if (_switches[NOOPENCOUNT_ARG] && !dm_task_no_open_count(dmt)) @@ -680,15 +727,18 @@ static int _rename(int argc, char **argv, void *data __attribute__((unused))) if (_switches[INACTIVE_ARG] && !dm_task_query_inactive_table(dmt)) goto out; + if (_switches[CHECKS_ARG] && !dm_task_enable_checks(dmt)) + goto out; + if (_switches[NOUDEVRULES_ARG]) udev_flags |= DM_UDEV_DISABLE_DM_RULES_FLAG | DM_UDEV_DISABLE_SUBSYSTEM_RULES_FLAG; - if (_udev_cookie) { + if (_udev_cookie) cookie = _udev_cookie; - if (_udev_only) - udev_flags |= DM_UDEV_DISABLE_LIBRARY_FALLBACK; - } + + if (_udev_only) + udev_flags |= DM_UDEV_DISABLE_LIBRARY_FALLBACK; if (!dm_task_set_cookie(dmt, &cookie, udev_flags) || !dm_task_run(dmt)) @@ -699,12 +749,22 @@ static int _rename(int argc, char **argv, void *data __attribute__((unused))) out: if (!_udev_cookie) (void) dm_udev_wait(cookie); + dm_task_destroy(dmt); return r; } -static int _message(int argc, char **argv, void *data __attribute__((unused))) +static int _rename(CMD_ARGS) +{ + const char *name = (argc == 3) ? argv[1] : NULL; + + return _switches[SETUUID_ARG] ? _do_rename(name, NULL, argv[argc - 1]) : + _do_rename(name, argv[argc - 1], NULL); + +} + +static int _message(CMD_ARGS) { int r = 0, i; size_t sz = 1; @@ -747,17 +807,22 @@ static int _message(int argc, char **argv, void *data __attribute__((unused))) strcat(str, argv[i]); } - if (!dm_task_set_message(dmt, str)) - goto out; + i = dm_task_set_message(dmt, str); dm_free(str); + if (!i) + goto out; + if (_switches[NOOPENCOUNT_ARG] && !dm_task_no_open_count(dmt)) goto out; if (_switches[INACTIVE_ARG] && !dm_task_query_inactive_table(dmt)) goto out; + if (_switches[CHECKS_ARG] && !dm_task_enable_checks(dmt)) + goto out; + if (!dm_task_run(dmt)) goto out; @@ -769,7 +834,7 @@ static int _message(int argc, char **argv, void *data __attribute__((unused))) return r; } -static int _setgeometry(int argc, char **argv, void *data __attribute__((unused))) +static int _setgeometry(CMD_ARGS) { int r = 0; struct dm_task *dmt; @@ -796,6 +861,9 @@ static int _setgeometry(int argc, char **argv, void *data __attribute__((unused) if (_switches[INACTIVE_ARG] && !dm_task_query_inactive_table(dmt)) goto out; + if (_switches[CHECKS_ARG] && !dm_task_enable_checks(dmt)) + goto out; + /* run the task */ if (!dm_task_run(dmt)) goto out; @@ -808,7 +876,7 @@ static int _setgeometry(int argc, char **argv, void *data __attribute__((unused) return r; } -static int _splitname(int argc, char **argv, void *data __attribute__((unused))) +static int _splitname(CMD_ARGS) { struct dmsetup_report_obj obj; int r = 1; @@ -817,8 +885,9 @@ static int _splitname(int argc, char **argv, void *data __attribute__((unused))) obj.info = NULL; obj.deps_task = NULL; obj.tree_node = NULL; - obj.split_name = _get_split_name((argc == 3) ? argv[2] : "LVM", - argv[1], '\0'); + if (!(obj.split_name = _get_split_name((argc == 3) ? argv[2] : "LVM", + argv[1], '\0'))) + return_0; r = dm_report_object(_report, &obj); _destroy_split_name(obj.split_name); @@ -842,7 +911,7 @@ static uint32_t _get_cookie_value(const char *str_value) return (uint32_t) value; } -static int _udevflags(int args, char **argv, void *data __attribute__((unused))) +static int _udevflags(CMD_ARGS) { uint32_t cookie; uint16_t flags; @@ -884,7 +953,7 @@ static int _udevflags(int args, char **argv, void *data __attribute__((unused))) return 1; } -static int _udevcomplete(int argc, char **argv, void *data __attribute__((unused))) +static int _udevcomplete(CMD_ARGS) { uint32_t cookie; @@ -907,32 +976,30 @@ static int _udevcomplete(int argc, char **argv, void *data __attribute__((unused } #ifndef UDEV_SYNC_SUPPORT -static const char _cmd_not_supported[] = "Command not supported. Recompile with \"--enable-udev-sync\" to enable."; +static const char _cmd_not_supported[] = "Command not supported. Recompile with \"--enable-udev_sync\" to enable."; -static int _udevcreatecookie(int argc, char **argv, - void *data __attribute__((unused))) +static int _udevcreatecookie(CMD_ARGS) { log_error(_cmd_not_supported); return 0; } -static int _udevreleasecookie(int argc, char **argv, - void *data __attribute__((unused))) +static int _udevreleasecookie(CMD_ARGS) { log_error(_cmd_not_supported); return 0; } -static int _udevcomplete_all(int argc __attribute__((unused)), char **argv __attribute__((unused)), void *data __attribute__((unused))) +static int _udevcomplete_all(CMD_ARGS) { log_error(_cmd_not_supported); return 0; } -static int _udevcookies(int argc __attribute__((unused)), char **argv __attribute__((unused)), void *data __attribute__((unused))) +static int _udevcookies(CMD_ARGS) { log_error(_cmd_not_supported); @@ -942,11 +1009,9 @@ static int _udevcookies(int argc __attribute__((unused)), char **argv __attribut #else /* UDEV_SYNC_SUPPORT */ static int _set_up_udev_support(const char *dev_dir) { - struct udev *udev; - const char *udev_dev_dir; - size_t udev_dev_dir_len; int dirs_diff; const char *env; + size_t len = strlen(dev_dir), udev_dir_len = strlen(DM_UDEV_DEV_DIR); if (_switches[NOUDEVSYNC_ARG]) dm_udev_set_sync_support(0); @@ -964,14 +1029,6 @@ static int _set_up_udev_support(const char *dev_dir) " defined by --udevcookie option.", _udev_cookie); - if (!(udev = udev_new()) || - !(udev_dev_dir = udev_get_dev_path(udev)) || - !*udev_dev_dir) { - log_error("Could not get udev dev path."); - return 0; - } - udev_dev_dir_len = strlen(udev_dev_dir); - /* * Normally, there's always a fallback action by libdevmapper if udev * has not done its job correctly, e.g. the nodes were not created. @@ -983,13 +1040,18 @@ static int _set_up_udev_support(const char *dev_dir) * is the same as "dev path" used by libdevmapper. */ - /* There's always a slash at the end of dev_dir. But check udev_dev_dir! */ - if (udev_dev_dir[udev_dev_dir_len - 1] != '/') - dirs_diff = strncmp(dev_dir, udev_dev_dir, udev_dev_dir_len); - else - dirs_diff = strcmp(dev_dir, udev_dev_dir); - _udev_only = _udev_cookie && !dirs_diff; + /* + * DM_UDEV_DEV_DIR always has '/' at its end. + * If the dev_dir does not have it, be sure + * to make the right comparison without the '/' char! + */ + if (dev_dir[len - 1] != '/') + udev_dir_len--; + + dirs_diff = udev_dir_len != len || + strncmp(DM_UDEV_DEV_DIR, dev_dir, len); + _udev_only = !dirs_diff && (_udev_cookie || !_switches[VERIFYUDEV_ARG]); if (dirs_diff) { log_debug("The path %s used for creating device nodes that is " @@ -998,16 +1060,14 @@ static int _set_up_udev_support(const char *dev_dir) "about udev not working correctly while processing " "particular nodes will be suppressed. These nodes " "and symlinks will be managed in each directory " - "separately.", dev_dir, udev_dev_dir); + "separately.", dev_dir, DM_UDEV_DEV_DIR); dm_udev_set_checking(0); } - udev_unref(udev); return 1; } -static int _udevcreatecookie(int argc, char **argv, - void *data __attribute__((unused))) +static int _udevcreatecookie(CMD_ARGS) { uint32_t cookie; @@ -1020,8 +1080,7 @@ static int _udevcreatecookie(int argc, char **argv, return 1; } -static int _udevreleasecookie(int argc, char **argv, - void *data __attribute__((unused))) +static int _udevreleasecookie(CMD_ARGS) { if (argv[1] && !(_udev_cookie = _get_cookie_value(argv[1]))) return 0; @@ -1034,6 +1093,7 @@ static int _udevreleasecookie(int argc, char **argv, return dm_udev_wait(_udev_cookie); } +__attribute__((format(printf, 1, 2))) static char _yes_no_prompt(const char *prompt, ...) { int c = 0, ret = 0; @@ -1062,16 +1122,25 @@ static char _yes_no_prompt(const char *prompt, ...) return ret; } -static int _udevcomplete_all(int argc __attribute__((unused)), char **argv __attribute__((unused)), void *data __attribute__((unused))) +static int _udevcomplete_all(CMD_ARGS) { int max_id, id, sid; struct seminfo sinfo; struct semid_ds sdata; int counter = 0; + int skipped = 0; + unsigned age = 0; + time_t t; + + if (argc == 2 && (sscanf(argv[1], "%i", &age) != 1)) { + log_error("Failed to read age_in_minutes parameter."); + return 0; + } if (!_switches[YES_ARG]) { - log_warn("This operation will destroy all semaphores with keys " + log_warn("This operation will destroy all semaphores %s%.0d%swith keys " "that have a prefix %" PRIu16 " (0x%" PRIx16 ").", + age ? "older than " : "", age, age ? " minutes " : "", DM_COOKIE_MAGIC, DM_COOKIE_MAGIC); if (_yes_no_prompt("Do you really want to continue? [y/n]: ") == 'n') { @@ -1092,6 +1161,13 @@ static int _udevcomplete_all(int argc __attribute__((unused)), char **argv __att continue; if (sdata.sem_perm.__key >> 16 == DM_COOKIE_MAGIC) { + t = time(NULL); + + if (sdata.sem_ctime + age * 60 > t || + sdata.sem_otime + age * 60 > t) { + skipped++; + continue; + } if (semctl(sid, 0, IPC_RMID, 0) < 0) { log_error("Could not cleanup notification semaphore " "with semid %d and cookie value " @@ -1105,26 +1181,27 @@ static int _udevcomplete_all(int argc __attribute__((unused)), char **argv __att } log_print("%d semaphores with keys prefixed by " - "%" PRIu16 " (0x%" PRIx16 ") destroyed.", - counter, DM_COOKIE_MAGIC, DM_COOKIE_MAGIC); + "%" PRIu16 " (0x%" PRIx16 ") destroyed. %d skipped.", + counter, DM_COOKIE_MAGIC, DM_COOKIE_MAGIC, skipped); return 1; } -static int _udevcookies(int argc __attribute__((unused)), char **argv __attribute__((unused)), void *data __attribute__((unused))) +static int _udevcookies(CMD_ARGS) { int max_id, id, sid; struct seminfo sinfo; struct semid_ds sdata; int val; - char *time_str; + char otime_str[26], ctime_str[26]; + char *otimes, *ctimes; if ((max_id = semctl(0, 0, SEM_INFO, &sinfo)) < 0) { log_sys_error("sem_ctl", "SEM_INFO"); return 0; } - printf("cookie semid value last_semop_time\n"); + printf("Cookie Semid Value Last semop time Last change time\n"); for (id = 0; id <= max_id; id++) { if ((sid = semctl(id, 0, SEM_STAT, &sdata)) < 0) @@ -1139,10 +1216,14 @@ static int _udevcookies(int argc __attribute__((unused)), char **argv __attribut continue; } - time_str = ctime((const time_t *) &sdata.sem_otime); + if ((otimes = ctime_r((const time_t *) &sdata.sem_otime, (char *)&otime_str))) + otime_str[strlen(otimes)-1] = '\0'; + if ((ctimes = ctime_r((const time_t *) &sdata.sem_ctime, (char *)&ctime_str))) + ctime_str[strlen(ctimes)-1] = '\0'; - printf("0x%-10x %-10d %-10d %s", sdata.sem_perm.__key, - sid, val, time_str ? time_str : "unknown\n"); + printf("0x%-10x %-10d %-10d %s %s\n", sdata.sem_perm.__key, + sid, val, otimes ? : "unknown", + ctimes? : "unknown"); } } @@ -1150,7 +1231,7 @@ static int _udevcookies(int argc __attribute__((unused)), char **argv __attribut } #endif /* UDEV_SYNC_SUPPORT */ -static int _version(int argc __attribute__((unused)), char **argv __attribute__((unused)), void *data __attribute__((unused))) +static int _version(CMD_ARGS) { char version[80]; @@ -1196,6 +1277,13 @@ static int _simple(int task, const char *name, uint32_t event_nr, int display) if (_switches[NOLOCKFS_ARG] && !dm_task_skip_lockfs(dmt)) goto out; + if (_switches[CHECKS_ARG] && !dm_task_enable_checks(dmt)) + goto out; + + /* FIXME: needs to coperate with udev */ + if (!_set_task_add_node(dmt)) + goto out; + if (_switches[READAHEAD_ARG] && !dm_task_set_read_ahead(dmt, _int_args[READAHEAD_ARG], _read_ahead_flags)) @@ -1205,44 +1293,48 @@ static int _simple(int task, const char *name, uint32_t event_nr, int display) udev_flags |= DM_UDEV_DISABLE_DM_RULES_FLAG | DM_UDEV_DISABLE_SUBSYSTEM_RULES_FLAG; - if (_udev_cookie) { + if (_udev_cookie) cookie = _udev_cookie; - if (_udev_only) - udev_flags |= DM_UDEV_DISABLE_LIBRARY_FALLBACK; - } + + if (_udev_only) + udev_flags |= DM_UDEV_DISABLE_LIBRARY_FALLBACK; if (udev_wait_flag && !dm_task_set_cookie(dmt, &cookie, udev_flags)) goto out; - r = dm_task_run(dmt); + if (_switches[RETRY_ARG] && task == DM_DEVICE_REMOVE) + dm_task_retry_remove(dmt); - if (r && display && _switches[VERBOSE_ARG]) - r = _display_info(dmt); + r = dm_task_run(dmt); out: if (!_udev_cookie && udev_wait_flag) (void) dm_udev_wait(cookie); + if (r && display && _switches[VERBOSE_ARG]) + r = _display_info(dmt); + dm_task_destroy(dmt); + return r; } -static int _suspend(int argc, char **argv, void *data __attribute__((unused))) +static int _suspend(CMD_ARGS) { return _simple(DM_DEVICE_SUSPEND, argc > 1 ? argv[1] : NULL, 0, 1); } -static int _resume(int argc, char **argv, void *data __attribute__((unused))) +static int _resume(CMD_ARGS) { return _simple(DM_DEVICE_RESUME, argc > 1 ? argv[1] : NULL, 0, 1); } -static int _clear(int argc, char **argv, void *data __attribute__((unused))) +static int _clear(CMD_ARGS) { return _simple(DM_DEVICE_CLEAR, argc > 1 ? argv[1] : NULL, 0, 1); } -static int _wait(int argc, char **argv, void *data __attribute__((unused))) +static int _wait(CMD_ARGS) { const char *name = NULL; @@ -1259,8 +1351,8 @@ static int _wait(int argc, char **argv, void *data __attribute__((unused))) (argc > 1) ? (uint32_t) atoi(argv[argc - 1]) : 0, 1); } -static int _process_all(int argc, char **argv, int silent, - int (*fn) (int argc, char **argv, void *data)) +static int _process_all(const struct command *cmd, int argc, char **argv, int silent, + int (*fn) (CMD_ARGS)) { int r = 1; struct dm_names *names; @@ -1271,6 +1363,9 @@ static int _process_all(int argc, char **argv, int silent, if (!(dmt = dm_task_create(DM_DEVICE_LIST))) return 0; + if (_switches[CHECKS_ARG] && !dm_task_enable_checks(dmt)) + goto out; + if (!dm_task_run(dmt)) { r = 0; goto out; @@ -1289,7 +1384,7 @@ static int _process_all(int argc, char **argv, int silent, do { names = (struct dm_names *)((char *) names + next); - if (!fn(argc, argv, names)) + if (!fn(cmd, argc, argv, names, 1)) r = 0; next = names->next; } while (next); @@ -1319,6 +1414,9 @@ static uint64_t _get_device_size(const char *name) if (_switches[INACTIVE_ARG] && !dm_task_query_inactive_table(dmt)) goto out; + if (_switches[CHECKS_ARG] && !dm_task_enable_checks(dmt)) + goto out; + if (!dm_task_run(dmt)) goto out; @@ -1336,18 +1434,14 @@ static uint64_t _get_device_size(const char *name) return size; } -static int _error_device(int argc __attribute__((unused)), char **argv __attribute__((unused)), void *data) +static int _error_device(CMD_ARGS) { - struct dm_names *names = (struct dm_names *) data; struct dm_task *dmt; const char *name; uint64_t size; int r = 0; - if (data) - name = names->name; - else - name = argv[1]; + name = names ? names->name : argv[1]; size = _get_device_size(name); @@ -1369,6 +1463,9 @@ static int _error_device(int argc __attribute__((unused)), char **argv __attribu if (_switches[INACTIVE_ARG] && !dm_task_query_inactive_table(dmt)) goto error; + if (_switches[CHECKS_ARG] && !dm_task_enable_checks(dmt)) + goto error; + if (!dm_task_run(dmt)) goto error; @@ -1384,22 +1481,22 @@ error: return r; } -static int _remove(int argc, char **argv, void *data __attribute__((unused))) +static int _remove(CMD_ARGS) { if (_switches[FORCE_ARG] && argc > 1) - (void) _error_device(argc, argv, NULL); + (void) _error_device(cmd, argc, argv, NULL, 0); return _simple(DM_DEVICE_REMOVE, argc > 1 ? argv[1] : NULL, 0, 0); } -static int _count_devices(int argc __attribute__((unused)), char **argv __attribute__((unused)), void *data __attribute__((unused))) +static int _count_devices(CMD_ARGS) { _num_devices++; return 1; } -static int _remove_all(int argc __attribute__((unused)), char **argv __attribute__((unused)), void *data __attribute__((unused))) +static int _remove_all(CMD_ARGS) { int r; @@ -1410,17 +1507,17 @@ static int _remove_all(int argc __attribute__((unused)), char **argv __attribute return r; _num_devices = 0; - r |= _process_all(argc, argv, 1, _count_devices); + r |= _process_all(cmd, argc, argv, 1, _count_devices); /* No devices left? */ if (!_num_devices) return r; - r |= _process_all(argc, argv, 1, _error_device); + r |= _process_all(cmd, argc, argv, 1, _error_device); r |= _simple(DM_DEVICE_REMOVE_ALL, "", 0, 0) | dm_mknodes(NULL); _num_devices = 0; - r |= _process_all(argc, argv, 1, _count_devices); + r |= _process_all(cmd, argc, argv, 1, _count_devices); if (!_num_devices) return r; @@ -1437,7 +1534,7 @@ static void _display_dev(struct dm_task *dmt, const char *name) printf("%s\t(%u, %u)\n", name, info.major, info.minor); } -static int _mknodes(int argc, char **argv, void *data __attribute__((unused))) +static int _mknodes(CMD_ARGS) { return dm_mknodes(argc > 1 ? argv[1] : NULL); } @@ -1501,7 +1598,7 @@ static int _exec_command(const char *name) return 1; } -static int _status(int argc, char **argv, void *data) +static int _status(CMD_ARGS) { int r = 0; struct dm_task *dmt; @@ -1509,31 +1606,29 @@ static int _status(int argc, char **argv, void *data) uint64_t start, length; char *target_type = NULL; char *params, *c; - int cmd; - struct dm_names *names = (struct dm_names *) data; + int cmdno; const char *name = NULL; int matched = 0; int ls_only = 0; struct dm_info info; - if (data) + if (names) name = names->name; else { if (argc == 1 && !_switches[UUID_ARG] && !_switches[MAJOR_ARG]) - return _process_all(argc, argv, 0, _status); - if (argc == 2) - name = argv[1]; + return _process_all(cmd, argc, argv, 0, _status); + name = argv[1]; } - if (!strcmp(argv[0], "table")) - cmd = DM_DEVICE_TABLE; + if (!strcmp(cmd->name, "table")) + cmdno = DM_DEVICE_TABLE; else - cmd = DM_DEVICE_STATUS; + cmdno = DM_DEVICE_STATUS; - if (!strcmp(argv[0], "ls")) + if (!strcmp(cmd->name, "ls")) ls_only = 1; - if (!(dmt = dm_task_create(cmd))) + if (!(dmt = dm_task_create(cmdno))) return 0; if (!_set_task_device(dmt, name, 0)) @@ -1545,6 +1640,12 @@ static int _status(int argc, char **argv, void *data) if (_switches[INACTIVE_ARG] && !dm_task_query_inactive_table(dmt)) goto out; + if (_switches[CHECKS_ARG] && !dm_task_enable_checks(dmt)) + goto out; + + if (_switches[NOFLUSH_ARG] && !dm_task_no_flush(dmt)) + goto out; + if (!dm_task_run(dmt)) goto out; @@ -1571,12 +1672,12 @@ static int _status(int argc, char **argv, void *data) _switches[VERBOSE_ARG]) { if (!matched && _switches[VERBOSE_ARG]) _display_info(dmt); - if (data && !_switches[VERBOSE_ARG]) + if (multiple_devices && !_switches[VERBOSE_ARG]) printf("%s: ", name); if (target_type) { /* Suppress encryption key */ if (!_switches[SHOWKEYS_ARG] && - cmd == DM_DEVICE_TABLE && + cmdno == DM_DEVICE_TABLE && !strcmp(target_type, "crypt")) { c = params; while (*c && *c != ' ') @@ -1594,7 +1695,7 @@ static int _status(int argc, char **argv, void *data) matched = 1; } while (next); - if (data && _switches[VERBOSE_ARG] && matched && !ls_only) + if (multiple_devices && _switches[VERBOSE_ARG] && matched && !ls_only) printf("\n"); if (matched && _switches[EXEC_ARG] && _command && !_exec_command(name)) @@ -1608,7 +1709,7 @@ static int _status(int argc, char **argv, void *data) } /* Show target names and their version numbers */ -static int _targets(int argc __attribute__((unused)), char **argv __attribute__((unused)), void *data __attribute__((unused))) +static int _targets(CMD_ARGS) { int r = 0; struct dm_task *dmt; @@ -1618,6 +1719,9 @@ static int _targets(int argc __attribute__((unused)), char **argv __attribute__( if (!(dmt = dm_task_create(DM_DEVICE_LIST_VERSIONS))) return 0; + if (_switches[CHECKS_ARG] && !dm_task_enable_checks(dmt)) + goto out; + if (!dm_task_run(dmt)) goto out; @@ -1640,21 +1744,19 @@ static int _targets(int argc __attribute__((unused)), char **argv __attribute__( return r; } -static int _info(int argc, char **argv, void *data) +static int _info(CMD_ARGS) { int r = 0; struct dm_task *dmt; - struct dm_names *names = (struct dm_names *) data; char *name = NULL; - if (data) + if (names) name = names->name; else { if (argc == 1 && !_switches[UUID_ARG] && !_switches[MAJOR_ARG]) - return _process_all(argc, argv, 0, _info); - if (argc == 2) - name = argv[1]; + return _process_all(cmd, argc, argv, 0, _info); + name = argv[1]; } if (!(dmt = dm_task_create(DM_DEVICE_INFO))) @@ -1669,6 +1771,9 @@ static int _info(int argc, char **argv, void *data) if (_switches[INACTIVE_ARG] && !dm_task_query_inactive_table(dmt)) goto out; + if (_switches[CHECKS_ARG] && !dm_task_enable_checks(dmt)) + goto out; + if (!dm_task_run(dmt)) goto out; @@ -1679,23 +1784,23 @@ static int _info(int argc, char **argv, void *data) return r; } -static int _deps(int argc, char **argv, void *data) +static int _deps(CMD_ARGS) { int r = 0; uint32_t i; struct dm_deps *deps; struct dm_task *dmt; struct dm_info info; - struct dm_names *names = (struct dm_names *) data; char *name = NULL; + char dev_name[PATH_MAX]; + int major, minor; - if (data) + if (names) name = names->name; else { if (argc == 1 && !_switches[UUID_ARG] && !_switches[MAJOR_ARG]) - return _process_all(argc, argv, 0, _deps); - if (argc == 2) - name = argv[1]; + return _process_all(cmd, argc, argv, 0, _deps); + name = argv[1]; } if (!(dmt = dm_task_create(DM_DEVICE_DEPS))) @@ -1710,6 +1815,9 @@ static int _deps(int argc, char **argv, void *data) if (_switches[INACTIVE_ARG] && !dm_task_query_inactive_table(dmt)) goto out; + if (_switches[CHECKS_ARG] && !dm_task_enable_checks(dmt)) + goto out; + if (!dm_task_run(dmt)) goto out; @@ -1728,17 +1836,24 @@ static int _deps(int argc, char **argv, void *data) if (_switches[VERBOSE_ARG]) _display_info(dmt); - if (data && !_switches[VERBOSE_ARG]) + if (multiple_devices && !_switches[VERBOSE_ARG]) printf("%s: ", name); printf("%d dependencies\t:", deps->count); - for (i = 0; i < deps->count; i++) - printf(" (%d, %d)", - (int) MAJOR(deps->device[i]), - (int) MINOR(deps->device[i])); + for (i = 0; i < deps->count; i++) { + major = (int) MAJOR(deps->device[i]); + minor = (int) MINOR(deps->device[i]); + + if ((_dev_name_type == DN_BLK || _dev_name_type == DN_MAP) && + dm_device_get_name(major, minor, _dev_name_type == DN_BLK, + dev_name, PATH_MAX)) + printf(" (%s)", dev_name); + else + printf(" (%d, %d)", major, minor); + } printf("\n"); - if (data && _switches[VERBOSE_ARG]) + if (multiple_devices && _switches[VERBOSE_ARG]) printf("\n"); r = 1; @@ -1748,12 +1863,21 @@ static int _deps(int argc, char **argv, void *data) return r; } -static int _display_name(int argc __attribute__((unused)), char **argv __attribute__((unused)), void *data) +static int _display_name(CMD_ARGS) { - struct dm_names *names = (struct dm_names *) data; + char dev_name[PATH_MAX]; + + if (!names) + return 1; - printf("%s\t(%d, %d)\n", names->name, - (int) MAJOR(names->dev), (int) MINOR(names->dev)); + if ((_dev_name_type == DN_BLK || _dev_name_type == DN_MAP) && + dm_device_get_name((int) MAJOR(names->dev), (int) MINOR(names->dev), + _dev_name_type == DN_BLK, dev_name, PATH_MAX)) + printf("%s\t(%s)\n", names->name, dev_name); + else + printf("%s\t(%d:%d)\n", names->name, + (int) MAJOR(names->dev), + (int) MINOR(names->dev)); return 1; } @@ -1764,6 +1888,7 @@ static int _display_name(int argc __attribute__((unused)), char **argv __attribu enum { TR_DEVICE=0, /* display device major:minor number */ + TR_BLKDEVNAME, /* display device kernel name */ TR_TABLE, TR_STATUS, TR_ACTIVE, @@ -1970,6 +2095,11 @@ static void _display_tree_attributes(struct dm_tree_node *node) _out_char(']'); } +/* FIXME Display table or status line. (Disallow both?) */ +static void _display_tree_targets(struct dm_tree_node *node, unsigned depth) +{ +} + static void _display_tree_node(struct dm_tree_node *node, unsigned depth, unsigned first_child __attribute__((unused)), unsigned last_child, unsigned has_children) @@ -1978,6 +2108,7 @@ static void _display_tree_node(struct dm_tree_node *node, unsigned depth, const char *name; const struct dm_info *info; int first_on_line = 0; + char dev_name[PATH_MAX]; /* Sub-tree for targets has 2 more depth */ if (depth + 2 > MAX_DEPTH) @@ -1985,7 +2116,8 @@ static void _display_tree_node(struct dm_tree_node *node, unsigned depth, name = dm_tree_node_get_name(node); - if ((!name || !*name) && !_tree_switches[TR_DEVICE]) + if ((!name || !*name) && + (!_tree_switches[TR_DEVICE] && !_tree_switches[TR_BLKDEVNAME])) return; /* Indicate whether there are more nodes at this depth */ @@ -2010,6 +2142,13 @@ static void _display_tree_node(struct dm_tree_node *node, unsigned depth, info = dm_tree_node_get_info(node); + if (_tree_switches[TR_BLKDEVNAME] && + dm_device_get_name(info->major, info->minor, 1, dev_name, PATH_MAX)) { + _out_string(name ? " <" : "<"); + _out_string(dev_name); + _out_char('>'); + } + if (_tree_switches[TR_DEVICE]) { _out_string(name ? " (" : "("); (void) _out_int(info->major); @@ -2030,7 +2169,7 @@ static void _display_tree_node(struct dm_tree_node *node, unsigned depth, if (TR_PRINT_TARGETS) { _tree_more[depth + 1] = has_children; - // FIXME _display_tree_targets(name, depth + 2); + _display_tree_targets(node, depth + 2); } } @@ -2063,11 +2202,10 @@ static void _display_tree_walk_children(struct dm_tree_node *node, } } -static int _add_dep(int argc __attribute__((unused)), char **argv __attribute__((unused)), void *data) +static int _add_dep(CMD_ARGS) { - struct dm_names *names = (struct dm_names *) data; - - if (!dm_tree_add_dev(_dtree, (unsigned) MAJOR(names->dev), (unsigned) MINOR(names->dev))) + if (names && + !dm_tree_add_dev(_dtree, (unsigned) MAJOR(names->dev), (unsigned) MINOR(names->dev))) return 0; return 1; @@ -2076,7 +2214,7 @@ static int _add_dep(int argc __attribute__((unused)), char **argv __attribute__( /* * Create and walk dependency tree */ -static int _build_whole_deptree(void) +static int _build_whole_deptree(const struct command *cmd) { if (_dtree) return 1; @@ -2084,17 +2222,15 @@ static int _build_whole_deptree(void) if (!(_dtree = dm_tree_create())) return 0; - if (!_process_all(0, NULL, 0, _add_dep)) + if (!_process_all(cmd, 0, NULL, 0, _add_dep)) return 0; return 1; } -static int _display_tree(int argc __attribute__((unused)), - char **argv __attribute__((unused)), - void *data __attribute__((unused))) +static int _display_tree(CMD_ARGS) { - if (!_build_whole_deptree()) + if (!_build_whole_deptree(cmd)) return 0; _display_tree_walk_children(dm_tree_find_node(_dtree, 0, 0), 0); @@ -2138,6 +2274,38 @@ static int _dm_name_disp(struct dm_report *rh, return dm_report_field_string(rh, field, &name); } +static int _dm_mangled_name_disp(struct dm_report *rh, + struct dm_pool *mem __attribute__((unused)), + struct dm_report_field *field, const void *data, + void *private __attribute__((unused))) +{ + char *name; + int r = 0; + + if ((name = dm_task_get_name_mangled((const struct dm_task *) data))) { + r = dm_report_field_string(rh, field, (const char * const *) &name); + dm_free(name); + } + + return r; +} + +static int _dm_unmangled_name_disp(struct dm_report *rh, + struct dm_pool *mem __attribute__((unused)), + struct dm_report_field *field, const void *data, + void *private __attribute__((unused))) +{ + char *name; + int r = 0; + + if ((name = dm_task_get_name_unmangled((const struct dm_task *) data))) { + r = dm_report_field_string(rh, field, (const char * const *) &name); + dm_free(name); + } + + return r; +} + static int _dm_uuid_disp(struct dm_report *rh, struct dm_pool *mem __attribute__((unused)), struct dm_report_field *field, @@ -2151,6 +2319,38 @@ static int _dm_uuid_disp(struct dm_report *rh, return dm_report_field_string(rh, field, &uuid); } +static int _dm_mangled_uuid_disp(struct dm_report *rh, + struct dm_pool *mem __attribute__((unused)), + struct dm_report_field *field, + const void *data, void *private __attribute__((unused))) +{ + char *uuid; + int r = 0; + + if ((uuid = dm_task_get_uuid_mangled((const struct dm_task *) data))) { + r = dm_report_field_string(rh, field, (const char * const *) &uuid); + dm_free(uuid); + } + + return r; +} + +static int _dm_unmangled_uuid_disp(struct dm_report *rh, + struct dm_pool *mem __attribute__((unused)), + struct dm_report_field *field, + const void *data, void *private __attribute__((unused))) +{ + char *uuid; + int r = 0; + + if ((uuid = dm_task_get_uuid_unmangled((const struct dm_task *) data))) { + r = dm_report_field_string(rh, field, (const char * const *) &uuid); + dm_free(uuid); + } + + return r; +} + static int _dm_read_ahead_disp(struct dm_report *rh, struct dm_pool *mem __attribute__((unused)), struct dm_report_field *field, const void *data, @@ -2164,6 +2364,24 @@ static int _dm_read_ahead_disp(struct dm_report *rh, return dm_report_field_uint32(rh, field, &value); } +static int _dm_blk_name_disp(struct dm_report *rh, + struct dm_pool *mem __attribute__((unused)), + struct dm_report_field *field, const void *data, + void *private __attribute__((unused))) +{ + char dev_name[PATH_MAX]; + const char *s = dev_name; + const struct dm_info *info = data; + + if (!dm_device_get_name(info->major, info->minor, 1, dev_name, PATH_MAX)) { + log_error("Could not resolve block device name for %d:%d.", + info->major, info->minor); + return 0; + } + + return dm_report_field_string(rh, field, &s); +} + static int _dm_info_status_disp(struct dm_report *rh, struct dm_pool *mem __attribute__((unused)), struct dm_report_field *field, const void *data, @@ -2243,7 +2461,7 @@ static int _dm_info_devno_disp(struct dm_report *rh, struct dm_pool *mem, struct dm_report_field *field, const void *data, void *private) { - char buf[DM_MAX_TYPE_NAME], *repstr; + char buf[PATH_MAX], *repstr; const struct dm_info *info = data; if (!dm_pool_begin_object(mem, 8)) { @@ -2251,10 +2469,17 @@ static int _dm_info_devno_disp(struct dm_report *rh, struct dm_pool *mem, return 0; } - if (dm_snprintf(buf, sizeof(buf), "%d:%d", - info->major, info->minor) < 0) { - log_error("dm_pool_alloc failed"); - goto out_abandon; + if (private) { + if (!dm_device_get_name(info->major, info->minor, + 1, buf, PATH_MAX)) + goto out_abandon; + } + else { + if (dm_snprintf(buf, sizeof(buf), "%d:%d", + info->major, info->minor) < 0) { + log_error("dm_pool_alloc failed"); + goto out_abandon; + } } if (!dm_pool_grow_object(mem, buf, strlen(buf) + 1)) { @@ -2395,13 +2620,14 @@ static int _dm_tree_parents_count_disp(struct dm_report *rh, return dm_report_field_int(rh, field, &num_parent); } -static int _dm_deps_disp(struct dm_report *rh, struct dm_pool *mem, - struct dm_report_field *field, const void *data, - void *private) +static int _dm_deps_disp_common(struct dm_report *rh, struct dm_pool*mem, + struct dm_report_field *field, const void *data, + void *private, int disp_blk_dev_names) { const struct dm_deps *deps = data; - int i; - char buf[DM_MAX_TYPE_NAME], *repstr; + char buf[PATH_MAX], *repstr; + int major, minor; + unsigned i; if (!dm_pool_begin_object(mem, 16)) { log_error("dm_pool_begin_object failed"); @@ -2409,16 +2635,27 @@ static int _dm_deps_disp(struct dm_report *rh, struct dm_pool *mem, } for (i = 0; i < deps->count; i++) { - if (dm_snprintf(buf, sizeof(buf), "%d:%d", - (int) MAJOR(deps->device[i]), - (int) MINOR(deps->device[i])) < 0) { + major = (int) MAJOR(deps->device[i]); + minor = (int) MINOR(deps->device[i]); + + if (disp_blk_dev_names) { + if (!dm_device_get_name(major, minor, 1, buf, PATH_MAX)) { + log_error("Could not resolve block device " + "name for %d:%d.", major, minor); + goto out_abandon; + } + } + else if (dm_snprintf(buf, sizeof(buf), "%d:%d", + major, minor) < 0) { log_error("dm_snprintf failed"); goto out_abandon; } + if (!dm_pool_grow_object(mem, buf, 0)) { log_error("dm_pool_grow_object failed"); goto out_abandon; } + if (i + 1 < deps->count && !dm_pool_grow_object(mem, ",", 1)) { log_error("dm_pool_grow_object failed"); goto out_abandon; @@ -2439,12 +2676,26 @@ static int _dm_deps_disp(struct dm_report *rh, struct dm_pool *mem, return 0; } +static int _dm_deps_disp(struct dm_report *rh, struct dm_pool *mem, + struct dm_report_field *field, const void *data, + void *private) +{ + return _dm_deps_disp_common(rh, mem, field, data, private, 0); +} + +static int _dm_deps_blk_names_disp(struct dm_report *rh, struct dm_pool *mem, + struct dm_report_field *field, + const void *data, void *private) +{ + return _dm_deps_disp_common(rh, mem, field, data, private, 1); +} + static int _dm_subsystem_disp(struct dm_report *rh, struct dm_pool *mem __attribute__((unused)), struct dm_report_field *field, const void *data, void *private __attribute__((unused))) { - return dm_report_field_string(rh, field, (const char **) data); + return dm_report_field_string(rh, field, (const char *const *) data); } static int _dm_vg_name_disp(struct dm_report *rh, @@ -2453,7 +2704,7 @@ static int _dm_vg_name_disp(struct dm_report *rh, void *private __attribute__((unused))) { - return dm_report_field_string(rh, field, (const char **) data); + return dm_report_field_string(rh, field, (const char *const *) data); } static int _dm_lv_name_disp(struct dm_report *rh, @@ -2462,7 +2713,7 @@ static int _dm_lv_name_disp(struct dm_report *rh, void *private __attribute__((unused))) { - return dm_report_field_string(rh, field, (const char **) data); + return dm_report_field_string(rh, field, (const char *const *) data); } static int _dm_lv_layer_name_disp(struct dm_report *rh, @@ -2471,7 +2722,7 @@ static int _dm_lv_layer_name_disp(struct dm_report *rh, void *private __attribute__((unused))) { - return dm_report_field_string(rh, field, (const char **) data); + return dm_report_field_string(rh, field, (const char *const *) data); } static void *_task_get_obj(void *obj) @@ -2518,11 +2769,16 @@ static const struct dm_report_object_type _report_types[] = { static const struct dm_report_field_type _report_fields[] = { /* *INDENT-OFF* */ FIELD_F(TASK, STR, "Name", 16, dm_name, "name", "Name of mapped device.") +FIELD_F(TASK, STR, "MangledName", 16, dm_mangled_name, "mangled_name", "Mangled name of mapped device.") +FIELD_F(TASK, STR, "UnmangledName", 16, dm_unmangled_name, "unmangled_name", "Unmangled name of mapped device.") FIELD_F(TASK, STR, "UUID", 32, dm_uuid, "uuid", "Unique (optional) identifier for mapped device.") +FIELD_F(TASK, STR, "MangledUUID", 32, dm_mangled_uuid, "mangled_uuid", "Mangled unique (optional) identifier for mapped device.") +FIELD_F(TASK, STR, "UnmangledUUID", 32, dm_unmangled_uuid, "unmangled_uuid", "Unmangled unique (optional) identifier for mapped device.") /* FIXME Next one should be INFO */ FIELD_F(TASK, NUM, "RAhead", 6, dm_read_ahead, "read_ahead", "Read ahead in sectors.") +FIELD_F(INFO, STR, "BlkDevName", 16, dm_blk_name, "blkdevname", "Name of block device.") FIELD_F(INFO, STR, "Stat", 4, dm_info_status, "attr", "(L)ive, (I)nactive, (s)uspended, (r)ead-only, read-(w)rite.") FIELD_F(INFO, STR, "Tables", 6, dm_info_table_loaded, "tables_loaded", "Which of the live and inactive table slots are filled.") FIELD_F(INFO, STR, "Suspended", 9, dm_info_suspended, "suspended", "Whether the device is suspended.") @@ -2535,8 +2791,9 @@ FIELD_O(INFO, dm_info, NUM, "Targ", target_count, 4, int32, "segments", "Number FIELD_O(INFO, dm_info, NUM, "Event", event_nr, 6, uint32, "events", "Number of most recent event.") FIELD_O(DEPS, dm_deps, NUM, "#Devs", count, 5, int32, "device_count", "Number of devices used by this one.") -FIELD_F(TREE, STR, "DevNames", 8, dm_deps_names, "devs_used", "List of names of mapped devices used by this one.") -FIELD_F(DEPS, STR, "DevNos", 6, dm_deps, "devnos_used", "List of device numbers of devices used by this one.") +FIELD_F(TREE, STR, "DevNamesUsed", 16, dm_deps_names, "devs_used", "List of names of mapped devices used by this one.") +FIELD_F(DEPS, STR, "DevNosUsed", 16, dm_deps, "devnos_used", "List of device numbers of devices used by this one.") +FIELD_F(DEPS, STR, "BlkDevNamesUsed", 16, dm_deps_blk_names, "blkdevs_used", "List of names of block devices used by this one.") FIELD_F(TREE, NUM, "#Refs", 5, dm_tree_parents_count, "device_ref_count", "Number of mapped devices referencing this one.") FIELD_F(TREE, STR, "RefNames", 8, dm_tree_parents_names, "names_using_dev", "List of names of mapped devices using this one.") @@ -2559,7 +2816,7 @@ FIELD_O(NAME, dm_split_name, STR, "LVLayer", lv_layer, 7, dm_lv_layer_name, "lv_ static const char *default_report_options = "name,major,minor,attr,open,segments,events,uuid"; static const char *splitname_report_options = "vg_name,lv_name,lv_layer"; -static int _report_init(struct command *c) +static int _report_init(const struct command *cmd) { char *options = (char *) default_report_options; const char *keys = ""; @@ -2570,7 +2827,7 @@ static int _report_init(struct command *c) size_t len = 0; int r = 0; - if (c && !strcmp(c->name, "splitname")) + if (cmd && !strcmp(cmd->name, "splitname")) options = (char *) splitname_report_options; /* emulate old dmsetup behaviour */ @@ -2616,7 +2873,7 @@ static int _report_init(struct command *c) if (_switches[SORT_ARG] && _string_args[SORT_ARG]) { keys = _string_args[SORT_ARG]; buffered = 1; - if (c && (!strcmp(c->name, "status") || !strcmp(c->name, "table"))) { + if (cmd && (!strcmp(cmd->name, "status") || !strcmp(cmd->name, "table"))) { err("--sort is not yet supported with status and table"); goto out; } @@ -2650,7 +2907,7 @@ static int _report_init(struct command *c) options, separator, flags, keys, NULL))) goto out; - if ((_report_type & DR_TREE) && !_build_whole_deptree()) { + if ((_report_type & DR_TREE) && !_build_whole_deptree(cmd)) { err("Internal device dependency tree creation failed."); goto out; } @@ -2673,56 +2930,149 @@ out: /* * List devices */ -static int _ls(int argc, char **argv, void *data) +static int _ls(CMD_ARGS) { if ((_switches[TARGET_ARG] && _target) || (_switches[EXEC_ARG] && _command)) - return _status(argc, argv, data); + return _status(cmd, argc, argv, NULL, 0); else if ((_switches[TREE_ARG])) - return _display_tree(argc, argv, data); + return _display_tree(cmd, 0, NULL, NULL, 0); else - return _process_all(argc, argv, 0, _display_name); + return _process_all(cmd, argc, argv, 0, _display_name); } -static int _help(int argc, char **argv, void *data); +static int _mangle(CMD_ARGS) +{ + const char *name, *uuid; + char *new_name = NULL, *new_uuid = NULL; + struct dm_task *dmt; + struct dm_info info; + int r = 0; + int target_format; + + if (names) + name = names->name; + else { + if (argc == 1 && !_switches[UUID_ARG] && !_switches[MAJOR_ARG]) + return _process_all(cmd, argc, argv, 0, _mangle); + name = argv[1]; + } + + if (!(dmt = dm_task_create(DM_DEVICE_STATUS))) + return 0; + + if (!(_set_task_device(dmt, name, 0))) + goto out; + + if (!_switches[CHECKS_ARG] && !dm_task_enable_checks(dmt)) + goto out; + + if (!dm_task_run(dmt)) + goto out; + + if (!dm_task_get_info(dmt, &info) || !info.exists) + goto out; + + uuid = dm_task_get_uuid(dmt); + + target_format = _switches[MANGLENAME_ARG] ? _int_args[MANGLENAME_ARG] + : DEFAULT_DM_NAME_MANGLING; + + if (target_format == DM_STRING_MANGLING_AUTO) { + if (strstr(name, "\\x5cx")) { + log_error("The name \"%s\" seems to be mangled more than once. " + "Manual intervention required to rename the device.", name); + goto out; + } + if (strstr(uuid, "\\x5cx")) { + log_error("The UUID \"%s\" seems to be mangled more than once. " + "Manual intervention required to correct the device UUID.", uuid); + goto out; + } + } + + if (target_format == DM_STRING_MANGLING_NONE) { + if (!(new_name = dm_task_get_name_unmangled(dmt))) + goto out; + if (!(new_uuid = dm_task_get_uuid_unmangled(dmt))) + goto out; + } + else { + if (!(new_name = dm_task_get_name_mangled(dmt))) + goto out; + if (!(new_uuid = dm_task_get_uuid_mangled(dmt))) + goto out; + } + + /* We can't rename the UUID, the device must be reactivated manually. */ + if (strcmp(uuid, new_uuid)) { + log_error("%s: %s: UUID in incorrect form. ", name, uuid); + log_error("Unable to change device UUID. The device must be deactivated first."); + r = 0; + goto out; + } + + /* Nothing to do if the name is in correct form already. */ + if (!strcmp(name, new_name)) { + log_print("%s: %s: name %salready in correct form", name, + *uuid ? uuid : "[no UUID]", *uuid ? "and UUID " : ""); + r = 1; + goto out; + } + else + log_print("%s: renaming to %s", name, new_name); + + /* Rename to correct form of the name. */ + r = _do_rename(name, new_name, NULL); + +out: + dm_free(new_name); + dm_free(new_uuid); + dm_task_destroy(dmt); + return r; +} + +static int _help(CMD_ARGS); /* * Dispatch table */ static struct command _commands[] = { - {"help", "[-c|-C|--columns]", 0, 0, _help}, + {"help", "[-c|-C|--columns]", 0, 0, 0, _help}, {"create", "<dev_name> [-j|--major <major> -m|--minor <minor>]\n" "\t [-U|--uid <uid>] [-G|--gid <gid>] [-M|--mode <octal_mode>]\n" - "\t [-u|uuid <uuid>]\n" + "\t [-u|uuid <uuid>] [{--addnodeonresume|--addnodeoncreate}]\n" "\t [--notable | --table <table> | <table_file>]", - 1, 2, _create}, - {"remove", "[-f|--force] <device>", 0, 1, _remove}, - {"remove_all", "[-f|--force]", 0, 0, _remove_all}, - {"suspend", "[--noflush] <device>", 0, 1, _suspend}, - {"resume", "<device>", 0, 1, _resume}, - {"load", "<device> [<table_file>]", 0, 2, _load}, - {"clear", "<device>", 0, 1, _clear}, - {"reload", "<device> [<table_file>]", 0, 2, _load}, - {"rename", "<device> [--setuuid] <new_name_or_uuid>", 1, 2, _rename}, - {"message", "<device> <sector> <message>", 2, -1, _message}, - {"ls", "[--target <target_type>] [--exec <command>] [--tree [-o options]]", 0, 0, _ls}, - {"info", "[<device>]", 0, 1, _info}, - {"deps", "[<device>]", 0, 1, _deps}, - {"status", "[<device>] [--target <target_type>]", 0, 1, _status}, - {"table", "[<device>] [--target <target_type>] [--showkeys]", 0, 1, _status}, - {"wait", "<device> [<event_nr>]", 0, 2, _wait}, - {"mknodes", "[<device>]", 0, 1, _mknodes}, - {"udevcreatecookie", "", 0, 0, _udevcreatecookie}, - {"udevreleasecookie", "[<cookie>]", 0, 1, _udevreleasecookie}, - {"udevflags", "<cookie>", 1, 1, _udevflags}, - {"udevcomplete", "<cookie>", 1, 1, _udevcomplete}, - {"udevcomplete_all", "", 0, 0, _udevcomplete_all}, - {"udevcookies", "", 0, 0, _udevcookies}, - {"targets", "", 0, 0, _targets}, - {"version", "", 0, 0, _version}, - {"setgeometry", "<device> <cyl> <head> <sect> <start>", 5, 5, _setgeometry}, - {"splitname", "<device> [<subsystem>]", 1, 2, _splitname}, - {NULL, NULL, 0, 0, NULL} + 1, 2,0, _create}, + {"remove", "[-f|--force] <device>", 0, -1, 1, _remove}, + {"remove_all", "[-f|--force]", 0, 0, 0, _remove_all}, + {"suspend", "[--noflush] <device>", 0, -1, 1, _suspend}, + {"resume", "<device> [{--addnodeonresume|--addnodeoncreate}]", 0, -1, 1, _resume}, + {"load", "<device> [<table_file>]", 0, 2, 0, _load}, + {"clear", "<device>", 0, -1, 1, _clear}, + {"reload", "<device> [<table_file>]", 0, 2, 0, _load}, + {"wipe_table", "<device>", 0, -1, 1, _error_device}, + {"rename", "<device> [--setuuid] <new_name_or_uuid>", 1, 2, 0, _rename}, + {"message", "<device> <sector> <message>", 2, -1, 0, _message}, + {"ls", "[--target <target_type>] [--exec <command>] [-o options] [--tree]", 0, 0, 0, _ls}, + {"info", "[<device>]", 0, -1, 1, _info}, + {"deps", "[-o options] [<device>]", 0, -1, 1, _deps}, + {"status", "[<device>] [--noflush] [--target <target_type>]", 0, -1, 1, _status}, + {"table", "[<device>] [--target <target_type>] [--showkeys]", 0, -1, 1, _status}, + {"wait", "<device> [<event_nr>] [--noflush]", 0, 2, 0, _wait}, + {"mknodes", "[<device>]", 0, -1, 1, _mknodes}, + {"mangle", "[<device>]", 0, -1, 1, _mangle}, + {"udevcreatecookie", "", 0, 0, 0, _udevcreatecookie}, + {"udevreleasecookie", "[<cookie>]", 0, 1, 0, _udevreleasecookie}, + {"udevflags", "<cookie>", 1, 1, 0, _udevflags}, + {"udevcomplete", "<cookie>", 1, 1, 0, _udevcomplete}, + {"udevcomplete_all", "<age_in_minutes>", 0, 1, 0, _udevcomplete_all}, + {"udevcookies", "", 0, 0, 0, _udevcookies}, + {"targets", "", 0, 0, 0, _targets}, + {"version", "", 0, 0, 0, _version}, + {"setgeometry", "<device> <cyl> <head> <sect> <start>", 5, 5, 0, _setgeometry}, + {"splitname", "<device> [<subsystem>]", 1, 2, 0, _splitname}, + {NULL, NULL, 0, 0, 0, NULL} }; static void _usage(FILE *out) @@ -2731,20 +3081,22 @@ static void _usage(FILE *out) fprintf(out, "Usage:\n\n"); fprintf(out, "dmsetup [--version] [-h|--help [-c|-C|--columns]]\n" - " [-v|--verbose [-v|--verbose ...]]\n" + " [--checks] [--manglename <mangling_mode>] [-v|--verbose [-v|--verbose ...]]\n" " [-r|--readonly] [--noopencount] [--nolockfs] [--inactive]\n" - " [--udevcookie] [--noudevrules] [--noudevsync] [-y|--yes]\n" - " [--readahead [+]<sectors>|auto|none]\n" + " [--udevcookie [cookie]] [--noudevrules] [--noudevsync] [--verifyudev]\n" + " [-y|--yes] [--readahead [+]<sectors>|auto|none] [--retry]\n" " [-c|-C|--columns] [-o <fields>] [-O|--sort <sort_fields>]\n" " [--nameprefixes] [--noheadings] [--separator <separator>]\n\n"); for (i = 0; _commands[i].name; i++) fprintf(out, "\t%s %s\n", _commands[i].name, _commands[i].help); fprintf(out, "\n<device> may be device name or -u <uuid> or " "-j <major> -m <minor>\n"); + fprintf(out, "<mangling_mode> is one of 'none', 'auto' and 'hex'.\n"); fprintf(out, "<fields> are comma-separated. Use 'help -c' for list.\n"); fprintf(out, "Table_file contents may be supplied on stdin.\n"); - fprintf(out, "Tree options are: ascii, utf, vt100; compact, inverted, notrunc;\n" - " [no]device, active, open, rw and uuid.\n"); + fprintf(out, "Options are: devno, devname, blkdevname.\n"); + fprintf(out, "Tree specific options are: ascii, utf, vt100; compact, inverted, notrunc;\n" + " blkdevname, [no]device, active, open, rw and uuid.\n"); fprintf(out, "\n"); } @@ -2755,9 +3107,7 @@ static void _losetup_usage(FILE *out) "[-o offset] [-f|loop_device] [file]\n\n"); } -static int _help(int argc __attribute__((unused)), - char **argv __attribute__((unused)), - void *data __attribute__((unused))) +static int _help(CMD_ARGS) { _usage(stderr); @@ -2770,7 +3120,7 @@ static int _help(int argc __attribute__((unused)), dm_report_free(_report); _report = NULL; } - (void) _report_init(NULL); + (void) _report_init(cmd); } return 1; @@ -2810,6 +3160,8 @@ static int _process_tree_options(const char *options) ; if (!strncmp(s, "device", len)) _tree_switches[TR_DEVICE] = 1; + else if (!strncmp(s, "blkdevname", len)) + _tree_switches[TR_BLKDEVNAME] = 1; else if (!strncmp(s, "nodevice", len)) _tree_switches[TR_DEVICE] = 0; else if (!strncmp(s, "status", len)) @@ -2865,6 +3217,8 @@ static char *_get_abspath(const char *path) _path = canonicalize_file_name(path); #else /* FIXME Provide alternative */ + log_error(INTERNAL_ERROR "Unimplemented _get_abspath."); + _path = NULL; #endif return _path; } @@ -2872,7 +3226,7 @@ static char *_get_abspath(const char *path) static char *parse_loop_device_name(const char *dev, const char *dev_dir) { char *buf; - char *device; + char *device = NULL; if (!(buf = dm_malloc(PATH_MAX))) return NULL; @@ -2890,7 +3244,8 @@ static char *parse_loop_device_name(const char *dev, const char *dev_dir) device[strlen(dev_dir)] != '/') goto error; - strncpy(buf, strrchr(device, '/') + 1, (size_t) PATH_MAX); + strncpy(buf, strrchr(device, '/') + 1, PATH_MAX - 1); + buf[PATH_MAX - 1] = '\0'; dm_free(device); } else { @@ -2904,7 +3259,9 @@ static char *parse_loop_device_name(const char *dev, const char *dev_dir) return buf; error: + dm_free(device); dm_free(buf); + return NULL; } @@ -2952,7 +3309,8 @@ static int _loop_table(char *table, size_t tlen, char *file, blksize = fsbuf.f_frsize; #endif - close(fd); + if (close(fd)) + log_sys_error("close", file); if (dm_snprintf(table, tlen, "%llu %llu loop %s %llu\n", 0ULL, (long long unsigned)sectors, file, (long long unsigned)off) < 0) @@ -2964,15 +3322,15 @@ static int _loop_table(char *table, size_t tlen, char *file, return 1; error: - if (fd > -1) - close(fd); + if (fd > -1 && close(fd)) + log_sys_error("close", file); + return 0; } static int _process_losetup_switches(const char *base, int *argc, char ***argv, const char *dev_dir) { - static int ind; int c; int encrypt_loop = 0, delete = 0, find = 0, show_all = 0; char *device_name = NULL; @@ -2987,8 +3345,8 @@ static int _process_losetup_switches(const char *base, int *argc, char ***argv, optarg = 0; optind = OPTIND_INIT; - while ((ind = -1, c = GETOPTLONG_FN(*argc, *argv, "ade:fo:v", - long_options, NULL)) != -1 ) { + while ((c = GETOPTLONG_FN(*argc, *argv, "ade:fo:v", + long_options, NULL)) != -1 ) { if (c == ':' || c == '?') return 0; if (c == 'a') @@ -3065,9 +3423,9 @@ static int _process_losetup_switches(const char *base, int *argc, char ***argv, return 0; } - /* FIXME Missing free */ _table = dm_malloc(LOOP_TABLE_SIZE); - if (!_loop_table(_table, (size_t) LOOP_TABLE_SIZE, loop_file, device_name, offset)) { + if (!_table || + !_loop_table(_table, (size_t) LOOP_TABLE_SIZE, loop_file, device_name, offset)) { fprintf(stderr, "Could not build device-mapper table for %s\n", (*argv)[0]); dm_free(device_name); return 0; @@ -3080,21 +3438,68 @@ static int _process_losetup_switches(const char *base, int *argc, char ***argv, return 1; } +static int _process_options(const char *options) +{ + const char *s, *end; + size_t len; + + /* Tree options are processed separately. */ + if (_switches[TREE_ARG]) + return _process_tree_options(_string_args[OPTIONS_ARG]); + + /* Column options are processed separately by _report_init (called later). */ + if (_switches[COLS_ARG]) + return 1; + + /* No options specified. */ + if (!_switches[OPTIONS_ARG]) + return 1; + + /* Set defaults. */ + _dev_name_type = DN_DEVNO; + + /* Parse. */ + for (s = options; s && *s; s++) { + len = 0; + for (end = s; *end && *end != ','; end++, len++) + ; + if (!strncmp(s, "devno", len)) + _dev_name_type = DN_DEVNO; + else if (!strncmp(s, "blkdevname", len)) + _dev_name_type = DN_BLK; + else if (!strncmp(s, "devname", len)) + _dev_name_type = DN_MAP; + else { + fprintf(stderr, "Option not recognised: %s\n", s); + return 0; + } + + if (!*end) + break; + s = end; + } + + return 1; +} + static int _process_switches(int *argc, char ***argv, const char *dev_dir) { - char *base, *namebase, *s; + const char *base; + char *namebase, *s; static int ind; int c, r; #ifdef HAVE_GETOPTLONG static struct option long_options[] = { {"readonly", 0, &ind, READ_ONLY}, + {"checks", 0, &ind, CHECKS_ARG}, {"columns", 0, &ind, COLS_ARG}, {"exec", 1, &ind, EXEC_ARG}, {"force", 0, &ind, FORCE_ARG}, {"gid", 1, &ind, GID_ARG}, {"help", 0, &ind, HELP_ARG}, {"inactive", 0, &ind, INACTIVE_ARG}, + {"manglename", 1, &ind, MANGLENAME_ARG}, {"major", 1, &ind, MAJOR_ARG}, {"minor", 1, &ind, MINOR_ARG}, {"mode", 1, &ind, MODE_ARG}, @@ -3109,6 +3514,7 @@ static int _process_switches(int *argc, char ***argv, const char *dev_dir) {"noudevsync", 0, &ind, NOUDEVSYNC_ARG}, {"options", 1, &ind, OPTIONS_ARG}, {"readahead", 1, &ind, READAHEAD_ARG}, + {"retry", 0, &ind, RETRY_ARG}, {"rows", 0, &ind, ROWS_ARG}, {"separator", 1, &ind, SEPARATOR_ARG}, {"setuuid", 0, &ind, SETUUID_ARG}, @@ -3122,8 +3528,11 @@ static int _process_switches(int *argc, char ***argv, const char *dev_dir) {"unbuffered", 0, &ind, UNBUFFERED_ARG}, {"unquoted", 0, &ind, UNQUOTED_ARG}, {"verbose", 1, &ind, VERBOSE_ARG}, + {"verifyudev", 0, &ind, VERIFYUDEV_ARG}, {"version", 0, &ind, VERSION_ARG}, {"yes", 0, &ind, YES_ARG}, + {"addnodeonresume", 0, &ind, ADD_NODE_ON_RESUME_ARG}, + {"addnodeoncreate", 0, &ind, ADD_NODE_ON_CREATE_ARG}, {0, 0, 0, 0} }; #else @@ -3137,8 +3546,11 @@ static int _process_switches(int *argc, char ***argv, const char *dev_dir) memset(&_int_args, 0, sizeof(_int_args)); _read_ahead_flags = 0; - namebase = strdup((*argv)[0]); - base = basename(namebase); + if (!(namebase = strdup((*argv)[0]))) { + fprintf(stderr, "Failed to duplicate name.\n"); + return 0; + } + base = dm_basename(namebase); if (!strcmp(base, "devmap_name")) { free(namebase); @@ -3221,6 +3633,12 @@ static int _process_switches(int *argc, char ***argv, const char *dev_dir) } if (c == 'y' || ind == YES_ARG) _switches[YES_ARG]++; + if (ind == ADD_NODE_ON_RESUME_ARG) + _switches[ADD_NODE_ON_RESUME_ARG]++; + if (ind == ADD_NODE_ON_CREATE_ARG) + _switches[ADD_NODE_ON_CREATE_ARG]++; + if (ind == CHECKS_ARG) + _switches[CHECKS_ARG]++; if (ind == UDEVCOOKIE_ARG) { _switches[UDEVCOOKIE_ARG]++; _udev_cookie = _get_cookie_value(optarg); @@ -3229,6 +3647,8 @@ static int _process_switches(int *argc, char ***argv, const char *dev_dir) _switches[NOUDEVRULES_ARG]++; if (ind == NOUDEVSYNC_ARG) _switches[NOUDEVSYNC_ARG]++; + if (ind == VERIFYUDEV_ARG) + _switches[VERIFYUDEV_ARG]++; if (c == 'G' || ind == GID_ARG) { _switches[GID_ARG]++; _int_args[GID_ARG] = atoi(optarg); @@ -3242,32 +3662,46 @@ static int _process_switches(int *argc, char ***argv, const char *dev_dir) /* FIXME Accept modes as per chmod */ _int_args[MODE_ARG] = (int) strtol(optarg, NULL, 8); } - if ((ind == EXEC_ARG)) { + if (ind == EXEC_ARG) { _switches[EXEC_ARG]++; _command = optarg; } - if ((ind == TARGET_ARG)) { + if (ind == TARGET_ARG) { _switches[TARGET_ARG]++; _target = optarg; } - if ((ind == INACTIVE_ARG)) - _switches[INACTIVE_ARG]++; - if ((ind == NAMEPREFIXES_ARG)) + if (ind == INACTIVE_ARG) + _switches[INACTIVE_ARG]++; + if ((ind == MANGLENAME_ARG)) { + _switches[MANGLENAME_ARG]++; + if (!strcasecmp(optarg, "none")) + _int_args[MANGLENAME_ARG] = DM_STRING_MANGLING_NONE; + else if (!strcasecmp(optarg, "auto")) + _int_args[MANGLENAME_ARG] = DM_STRING_MANGLING_AUTO; + else if (!strcasecmp(optarg, "hex")) + _int_args[MANGLENAME_ARG] = DM_STRING_MANGLING_HEX; + else { + log_error("Unknown name mangling mode"); + return 0; + } + dm_set_name_mangling_mode((dm_string_mangling_t) _int_args[MANGLENAME_ARG]); + } + if (ind == NAMEPREFIXES_ARG) _switches[NAMEPREFIXES_ARG]++; - if ((ind == NOFLUSH_ARG)) + if (ind == NOFLUSH_ARG) _switches[NOFLUSH_ARG]++; - if ((ind == NOHEADINGS_ARG)) + if (ind == NOHEADINGS_ARG) _switches[NOHEADINGS_ARG]++; - if ((ind == NOLOCKFS_ARG)) + if (ind == NOLOCKFS_ARG) _switches[NOLOCKFS_ARG]++; - if ((ind == NOOPENCOUNT_ARG)) + if (ind == NOOPENCOUNT_ARG) _switches[NOOPENCOUNT_ARG]++; - if ((ind == READAHEAD_ARG)) { + if (ind == READAHEAD_ARG) { _switches[READAHEAD_ARG]++; if (!strcasecmp(optarg, "auto")) _int_args[READAHEAD_ARG] = DM_READ_AHEAD_AUTO; else if (!strcasecmp(optarg, "none")) - _int_args[READAHEAD_ARG] = DM_READ_AHEAD_NONE; + _int_args[READAHEAD_ARG] = DM_READ_AHEAD_NONE; else { for (s = optarg; isspace(*s); s++) ; @@ -3282,21 +3716,26 @@ static int _process_switches(int *argc, char ***argv, const char *dev_dir) } } } - if ((ind == ROWS_ARG)) + if (ind == RETRY_ARG) + _switches[RETRY_ARG]++; + if (ind == ROWS_ARG) _switches[ROWS_ARG]++; - if ((ind == SETUUID_ARG)) + if (ind == SETUUID_ARG) _switches[SETUUID_ARG]++; - if ((ind == SHOWKEYS_ARG)) + if (ind == SHOWKEYS_ARG) _switches[SHOWKEYS_ARG]++; - if ((ind == TABLE_ARG)) { + if (ind == TABLE_ARG) { _switches[TABLE_ARG]++; - _table = optarg; + if (!(_table = dm_strdup(optarg))) { + log_error("Could not allocate memory for table string."); + return 0; + } } - if ((ind == TREE_ARG)) + if (ind == TREE_ARG) _switches[TREE_ARG]++; - if ((ind == UNQUOTED_ARG)) + if (ind == UNQUOTED_ARG) _switches[UNQUOTED_ARG]++; - if ((ind == VERSION_ARG)) + if (ind == VERSION_ARG) _switches[VERSION_ARG]++; } @@ -3310,7 +3749,7 @@ static int _process_switches(int *argc, char ***argv, const char *dev_dir) return 0; } - if (_switches[TREE_ARG] && !_process_tree_options(_string_args[OPTIONS_ARG])) + if (!_process_options(_string_args[OPTIONS_ARG])) return 0; if (_switches[TABLE_ARG] && _switches[NOTABLE_ARG]) { @@ -3318,6 +3757,11 @@ static int _process_switches(int *argc, char ***argv, const char *dev_dir) return 0; } + if (_switches[ADD_NODE_ON_RESUME_ARG] && _switches[ADD_NODE_ON_CREATE_ARG]) { + fprintf(stderr, "--addnodeonresume and --addnodeoncreate are incompatible.\n"); + return 0; + } + *argv += optind; *argc -= optind; return 1; @@ -3325,9 +3769,10 @@ static int _process_switches(int *argc, char ***argv, const char *dev_dir) int main(int argc, char **argv) { - struct command *c; int r = 1; const char *dev_dir; + const struct command *cmd; + int multiple_devices; (void) setlocale(LC_ALL, ""); @@ -3346,12 +3791,12 @@ int main(int argc, char **argv) } if (_switches[HELP_ARG]) { - c = _find_command("help"); + cmd = _find_command("help"); goto doit; } if (_switches[VERSION_ARG]) { - c = _find_command("version"); + cmd = _find_command("version"); goto doit; } @@ -3360,27 +3805,30 @@ int main(int argc, char **argv) goto out; } - if (!(c = _find_command(argv[0]))) { + if (!(cmd = _find_command(argv[0]))) { fprintf(stderr, "Unknown command\n"); _usage(stderr); goto out; } - if (argc < c->min_args + 1 || - (c->max_args >= 0 && argc > c->max_args + 1)) { + if (argc < cmd->min_args + 1 || + (cmd->max_args >= 0 && argc > cmd->max_args + 1)) { fprintf(stderr, "Incorrect number of arguments\n"); _usage(stderr); goto out; } - if (!_switches[COLS_ARG] && !strcmp(c->name, "splitname")) + if (!_switches[COLS_ARG] && !strcmp(cmd->name, "splitname")) _switches[COLS_ARG]++; + if (!strcmp(cmd->name, "mangle")) + dm_set_name_mangling_mode(DM_STRING_MANGLING_NONE); + if (_switches[COLS_ARG]) { - if (!_report_init(c)) + if (!_report_init(cmd)) goto out; if (!_report) { - if (!strcmp(c->name, "info")) + if (!strcmp(cmd->name, "info")) r = 0; /* info -c -o help */ goto out; } @@ -3392,10 +3840,14 @@ int main(int argc, char **argv) #endif doit: - if (!c->fn(argc, argv, NULL)) { - fprintf(stderr, "Command failed\n"); - goto out; - } + multiple_devices = (cmd->repeatable_cmd && argc != 2 && + (argc != 1 || (!_switches[UUID_ARG] && !_switches[MAJOR_ARG]))); + do { + if (!cmd->fn(cmd, argc--, argv++, NULL, multiple_devices)) { + fprintf(stderr, "Command failed\n"); + goto out; + } + } while (cmd->repeatable_cmd && argc > 1); r = 0; @@ -3408,5 +3860,7 @@ out: if (_dtree) dm_tree_free(_dtree); + dm_free(_table); + return r; } diff --git a/tools/dumpconfig.c b/tools/dumpconfig.c index 981147c..c5f5226 100644 --- a/tools/dumpconfig.c +++ b/tools/dumpconfig.c @@ -19,7 +19,7 @@ int dumpconfig(struct cmd_context *cmd, int argc, char **argv) { const char *file = arg_str_value(cmd, file_ARG, NULL); - if (!write_config_file(cmd->cft, file, argc, argv)) { + if (!config_write(cmd->cft, file, argc, argv)) { stack; return ECMD_FAILED; } diff --git a/tools/lvchange.c b/tools/lvchange.c index 8161d22..04facdd 100644 --- a/tools/lvchange.c +++ b/tools/lvchange.c @@ -1,6 +1,6 @@ /* * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved. - * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved. + * Copyright (C) 2004-2011 Red Hat, Inc. All rights reserved. * * This file is part of LVM2. * @@ -43,6 +43,20 @@ static int lvchange_permission(struct cmd_context *cmd, return 0; } + /* Not allowed to change permissions on RAID sub-LVs directly */ + if ((lv->status & RAID_META) || (lv->status & RAID_IMAGE)) { + log_error("Cannot change permissions of RAID %s \"%s\"", + (lv->status & RAID_IMAGE) ? "image" : + "metadata area", lv->name); + return 0; + } + + if (!(lv_access & LVM_WRITE) && lv_is_thin_pool(lv)) { + log_error("Change permissions of thin pool \"%s\" not " + "yes supported.", lv->name); + return 0; + } + if (lv_access & LVM_WRITE) { lv->status |= LVM_WRITE; log_verbose("Setting logical volume \"%s\" read/write", @@ -81,12 +95,90 @@ out: return r; } +static int lvchange_pool_update(struct cmd_context *cmd, + struct logical_volume *lv) +{ + int r = 0; + int update = 0; + unsigned val; + thin_discards_t discards; + + if (!lv_is_thin_pool(lv)) { + log_error("Logical volume \"%s\" is not a thin pool.", lv->name); + return 0; + } + + if (arg_count(cmd, discards_ARG)) { + discards = (thin_discards_t) arg_uint_value(cmd, discards_ARG, THIN_DISCARDS_IGNORE); + if (discards != first_seg(lv)->discards) { + if ((discards != THIN_DISCARDS_IGNORE) && + (first_seg(lv)->chunk_size & + (first_seg(lv)->chunk_size - 1))) + log_error("Cannot change discards state for " + "logical volume \"%s\" " + "with non power of 2 chunk size.", lv->name); + else if (((discards == THIN_DISCARDS_IGNORE) || + (first_seg(lv)->discards == THIN_DISCARDS_IGNORE)) && + lv_is_active(lv)) + log_error("Cannot change discards state for active " + "logical volume \"%s\".", lv->name); + else { + first_seg(lv)->discards = discards; + update++; + } + } else + log_error("Logical volume \"%s\" already uses --discards %s.", + lv->name, get_pool_discards_name(discards)); + } + + if (arg_count(cmd, zero_ARG)) { + val = arg_uint_value(cmd, zero_ARG, 1); + if (val != first_seg(lv)->zero_new_blocks) { + first_seg(lv)->zero_new_blocks = val; + update++; + } else + log_error("Logical volume \"%s\" already %szero new blocks.", + lv->name, val ? "" : "does not "); + } + + if (!update) + return 0; + + log_very_verbose("Updating logical volume \"%s\" on disk(s).", lv->name); + if (!vg_write(lv->vg)) + return_0; + + if (!suspend_lv_origin(cmd, lv)) { + log_error("Failed to update active %s/%s (deactivation is needed).", + lv->vg->name, lv->name); + vg_revert(lv->vg); + goto out; + } + + if (!vg_commit(lv->vg)) { + if (!resume_lv_origin(cmd, lv)) + stack; + goto_out; + } + + if (!resume_lv_origin(cmd, lv)) { + log_error("Problem reactivating %s.", lv->name); + goto out; + } + + r = 1; +out: + backup(lv->vg); + return r; +} + static int lvchange_monitoring(struct cmd_context *cmd, struct logical_volume *lv) { struct lvinfo info; - if (!lv_info(cmd, lv, 0, &info, 0, 0) || !info.exists) { + if (!lv_info(cmd, lv, lv_is_thin_pool(lv) ? 1 : 0, + &info, 0, 0) || !info.exists) { log_error("Logical volume, %s, is not active", lv->name); return 0; } @@ -118,12 +210,20 @@ static int lvchange_background_polling(struct cmd_context *cmd, return 1; } -static int lvchange_availability(struct cmd_context *cmd, - struct logical_volume *lv) +static int _lvchange_activate(struct cmd_context *cmd, struct logical_volume *lv) { int activate; - activate = arg_uint_value(cmd, available_ARG, 0); + activate = arg_uint_value(cmd, activate_ARG, 0); + + if (lv_is_cow(lv) && !lv_is_virtual_origin(origin_from_cow(lv))) + lv = origin_from_cow(lv); + + if (activate == CHANGE_AAY) { + if (!lv_passes_auto_activation_filter(cmd, lv)) + return 1; + activate = CHANGE_ALY; + } if (activate == CHANGE_ALN) { log_verbose("Deactivating logical volume \"%s\" locally", @@ -135,7 +235,9 @@ static int lvchange_availability(struct cmd_context *cmd, if (!deactivate_lv(cmd, lv)) return_0; } else { - if (lv_is_origin(lv) || (activate == CHANGE_AE)) { + if ((activate == CHANGE_AE) || + lv_is_origin(lv) || + lv_is_thin_type(lv)) { log_verbose("Activating logical volume \"%s\" " "exclusively", lv->name); if (!activate_lv_excl(cmd, lv)) @@ -166,18 +268,82 @@ static int lvchange_refresh(struct cmd_context *cmd, struct logical_volume *lv) return lv_refresh(cmd, lv); } +static int detach_metadata_devices(struct lv_segment *seg, struct dm_list *list) +{ + uint32_t s; + uint32_t num_meta_lvs; + struct cmd_context *cmd = seg->lv->vg->cmd; + struct lv_list *lvl; + + num_meta_lvs = seg_is_raid(seg) ? seg->area_count : !!seg->log_lv; + + if (!num_meta_lvs) + return_0; + + if (!(lvl = dm_pool_alloc(cmd->mem, sizeof(*lvl) * num_meta_lvs))) + return_0; + + if (seg_is_raid(seg)) { + for (s = 0; s < seg->area_count; s++) { + if (!seg_metalv(seg, s)) + return_0; /* Trap this future possibility */ + + lvl[s].lv = seg_metalv(seg, s); + lv_set_visible(lvl[s].lv); + + dm_list_add(list, &lvl[s].list); + } + return 1; + } + + lvl[0].lv = detach_mirror_log(seg); + dm_list_add(list, &lvl[0].list); + + return 1; +} + +static int attach_metadata_devices(struct lv_segment *seg, struct dm_list *list) +{ + struct cmd_context *cmd = seg->lv->vg->cmd; + struct lv_list *lvl, *tmp; + + if (seg_is_raid(seg)) { + dm_list_iterate_items_safe(lvl, tmp, list) { + lv_set_hidden(lvl->lv); + dm_pool_free(cmd->mem, lvl); + } + return 1; + } + + dm_list_iterate_items(lvl, list) + break; /* get first item */ + + if (!attach_mirror_log(seg, lvl->lv)) { + dm_pool_free(cmd->mem, lvl); + return_0; + } + + dm_pool_free(cmd->mem, lvl); + + return 1; +} + static int lvchange_resync(struct cmd_context *cmd, struct logical_volume *lv) { int active = 0; int monitored; struct lvinfo info; - struct logical_volume *log_lv; + struct lv_segment *seg = first_seg(lv); + struct dm_list device_list; + struct lv_list *lvl; - if (!(lv->status & MIRRORED)) { - log_error("Unable to resync %s because it is not mirrored.", + dm_list_init(&device_list); + + if (!(lv->status & MIRRORED) && !seg_is_raid(seg)) { + log_error("Unable to resync %s. It is not RAID or mirrored.", lv->name); - return 1; + return 0; } if (lv->status & PVMOVE) { @@ -216,7 +382,8 @@ static int lvchange_resync(struct cmd_context *cmd, /* Activate exclusively to ensure no nodes still have LV active */ monitored = dmeventd_monitor_mode(); - init_dmeventd_monitor(0); + if (monitored != DMEVENTD_MONITOR_IGNORE) + init_dmeventd_monitor(0); if (!deactivate_lv(cmd, lv)) { log_error("Unable to deactivate %s for resync", lv->name); @@ -229,81 +396,109 @@ static int lvchange_resync(struct cmd_context *cmd, return 0; } - init_dmeventd_monitor(monitored); - - log_lv = first_seg(lv)->log_lv; + if (monitored != DMEVENTD_MONITOR_IGNORE) + init_dmeventd_monitor(monitored); + init_mirror_in_sync(0); - log_very_verbose("Starting resync of %s%s%s mirror \"%s\"", + log_very_verbose("Starting resync of %s%s%s%s \"%s\"", (active) ? "active " : "", vg_is_clustered(lv->vg) ? "clustered " : "", - (log_lv) ? "disk-logged" : "core-logged", - lv->name); + (seg->log_lv) ? "disk-logged " : + seg_is_raid(seg) ? "" : "core-logged ", + seg->segtype->ops->name(seg), lv->name); /* - * If this mirror has a core log (i.e. !log_lv), + * If this mirror has a core log (i.e. !seg->log_lv), * then simply deactivating/activating will cause * it to reset the sync status. We only need to * worry about persistent logs. */ - if (!log_lv && !(lv->status & MIRROR_NOTSYNCED)) { + if (!seg_is_raid(seg) && !seg->log_lv) { + if (lv->status & LV_NOTSYNCED) { + lv->status &= ~LV_NOTSYNCED; + log_very_verbose("Updating logical volume \"%s\"" + " on disk(s)", lv->name); + if (!vg_write(lv->vg) || !vg_commit(lv->vg)) { + log_error("Failed to update metadata on disk."); + return 0; + } + } + if (active && !activate_lv(cmd, lv)) { log_error("Failed to reactivate %s to resynchronize " "mirror", lv->name); return 0; } + return 1; } - lv->status &= ~MIRROR_NOTSYNCED; + /* + * Now we handle mirrors with log devices + */ + lv->status &= ~LV_NOTSYNCED; - if (log_lv) { - /* Separate mirror log so we can clear it */ - detach_mirror_log(first_seg(lv)); + /* Separate mirror log or metadata devices so we can clear them */ + if (!detach_metadata_devices(seg, &device_list)) { + log_error("Failed to clear %s %s for %s", + seg->segtype->name, seg_is_raid(seg) ? + "metadata area" : "mirror log", lv->name); + return 0; + } - if (!vg_write(lv->vg)) { - log_error("Failed to write intermediate VG metadata."); - if (!attach_mirror_log(first_seg(lv), log_lv)) - stack; - if (active && !activate_lv(cmd, lv)) - stack; - return 0; - } + if (!vg_write(lv->vg)) { + log_error("Failed to write intermediate VG metadata."); + if (!attach_metadata_devices(seg, &device_list)) + stack; + if (active && !activate_lv(cmd, lv)) + stack; + return 0; + } - if (!vg_commit(lv->vg)) { - log_error("Failed to commit intermediate VG metadata."); - if (!attach_mirror_log(first_seg(lv), log_lv)) - stack; - if (active && !activate_lv(cmd, lv)) - stack; - return 0; - } + if (!vg_commit(lv->vg)) { + log_error("Failed to commit intermediate VG metadata."); + if (!attach_metadata_devices(seg, &device_list)) + stack; + if (active && !activate_lv(cmd, lv)) + stack; + return 0; + } - backup(lv->vg); + backup(lv->vg); - if (!activate_lv(cmd, log_lv)) { + dm_list_iterate_items(lvl, &device_list) { + if (!activate_lv(cmd, lvl->lv)) { log_error("Unable to activate %s for mirror log resync", - log_lv->name); + lvl->lv->name); return 0; } - log_very_verbose("Clearing log device %s", log_lv->name); - if (!set_lv(cmd, log_lv, log_lv->size, 0)) { - log_error("Unable to reset sync status for %s", lv->name); - if (!deactivate_lv(cmd, log_lv)) + log_very_verbose("Clearing %s device %s", + (seg_is_raid(seg)) ? "metadata" : "log", + lvl->lv->name); + if (!set_lv(cmd, lvl->lv, lvl->lv->size, 0)) { + log_error("Unable to reset sync status for %s", + lv->name); + if (!deactivate_lv(cmd, lvl->lv)) log_error("Failed to deactivate log LV after " "wiping failed"); return 0; } - if (!deactivate_lv(cmd, log_lv)) { - log_error("Unable to deactivate log LV %s after wiping " - "for resync", log_lv->name); + if (!deactivate_lv(cmd, lvl->lv)) { + log_error("Unable to deactivate %s LV %s " + "after wiping for resync", + (seg_is_raid(seg)) ? "metadata" : "log", + lvl->lv->name); return 0; } + } - /* Put mirror log back in place */ - if (!attach_mirror_log(first_seg(lv), log_lv)) - stack; + /* Put metadata sub-LVs back in place */ + if (!attach_metadata_devices(seg, &device_list)) { + log_error("Failed to reattach %s device after clearing", + (seg_is_raid(seg)) ? "metadata" : "log"); + return 0; } log_very_verbose("Updating logical volume \"%s\" on disk(s)", lv->name); @@ -327,7 +522,7 @@ static int lvchange_alloc(struct cmd_context *cmd, struct logical_volume *lv) want_contiguous = strcmp(arg_str_value(cmd, contiguous_ARG, "n"), "n"); alloc = want_contiguous ? ALLOC_CONTIGUOUS : ALLOC_INHERIT; - alloc = arg_uint_value(cmd, alloc_ARG, alloc); + alloc = (alloc_policy_t) arg_uint_value(cmd, alloc_ARG, alloc); if (alloc == lv->alloc) { log_error("Allocation policy of logical volume \"%s\" is " @@ -426,6 +621,7 @@ static int lvchange_persistent(struct cmd_context *cmd, { struct lvinfo info; int active = 0; + int32_t major, minor; if (!strcmp(arg_str_value(cmd, persistent_ARG, "n"), "n")) { if (!(lv->status & FIXED_MINOR)) { @@ -443,12 +639,26 @@ static int lvchange_persistent(struct cmd_context *cmd, log_error("Minor number must be specified with -My"); return 0; } + if (arg_count(cmd, major_ARG) > 1) { + log_error("Option -j/--major may not be repeated."); + return 0; + } + if (arg_count(cmd, minor_ARG) > 1) { + log_error("Option --minor may not be repeated."); + return 0; + } if (!arg_count(cmd, major_ARG) && lv->major < 0) { log_error("Major number must be specified with -My"); return 0; } if (lv_info(cmd, lv, 0, &info, 0, 0) && info.exists) active = 1; + + major = arg_int_value(cmd, major_ARG, lv->major); + minor = arg_int_value(cmd, minor_ARG, lv->minor); + if (!major_minor_valid(cmd, lv->vg->fid->fmt, major, minor)) + return 0; + if (active && !arg_count(cmd, force_ARG) && yes_no_prompt("Logical volume %s will be " "deactivated temporarily. " @@ -467,8 +677,8 @@ static int lvchange_persistent(struct cmd_context *cmd, return 0; } lv->status |= FIXED_MINOR; - lv->minor = arg_int_value(cmd, minor_ARG, lv->minor); - lv->major = arg_int_value(cmd, major_ARG, lv->major); + lv->minor = minor; + lv->major = major; log_verbose("Setting persistent device number to (%d, %d) " "for \"%s\"", lv->major, lv->minor, lv->name); @@ -491,24 +701,10 @@ static int lvchange_persistent(struct cmd_context *cmd, return 1; } -static int lvchange_tag(struct cmd_context *cmd, struct logical_volume *lv, - int arg) +static int lvchange_tag(struct cmd_context *cmd, struct logical_volume *lv, int arg) { - const char *tag; - struct arg_value_group_list *current_group; - - dm_list_iterate_items(current_group, &cmd->arg_value_groups) { - if (!grouped_arg_is_set(current_group->arg_values, arg)) - continue; - - if (!(tag = grouped_arg_str_value(current_group->arg_values, arg, NULL))) { - log_error("Failed to get tag"); - return 0; - } - - if (!lv_change_tag(lv, tag, arg == addtag_ARG)) - return_0; - } + if (!change_tag(cmd, NULL, lv, NULL, arg)) + return_0; log_very_verbose("Updating logical volume \"%s\" on disk(s)", lv->name); @@ -525,19 +721,22 @@ static int lvchange_single(struct cmd_context *cmd, struct logical_volume *lv, void *handle __attribute__((unused))) { int doit = 0, docmds = 0; - int dmeventd_mode, archived = 0; + int archived = 0; struct logical_volume *origin; + char snaps_msg[128]; if (!(lv->vg->status & LVM_WRITE) && (arg_count(cmd, contiguous_ARG) || arg_count(cmd, permission_ARG) || arg_count(cmd, readahead_ARG) || arg_count(cmd, persistent_ARG) || + arg_count(cmd, discards_ARG) || + arg_count(cmd, zero_ARG) || arg_count(cmd, alloc_ARG))) { log_error("Only -a permitted with read-only volume " "group \"%s\"", lv->vg->name); return EINVALID_CMD_LINE; } - if (lv_is_origin(lv) && + if (lv_is_origin(lv) && !lv_is_thin_volume(lv) && (arg_count(cmd, contiguous_ARG) || arg_count(cmd, permission_ARG) || arg_count(cmd, readahead_ARG) || arg_count(cmd, persistent_ARG) || arg_count(cmd, alloc_ARG))) { @@ -546,16 +745,29 @@ static int lvchange_single(struct cmd_context *cmd, struct logical_volume *lv, return ECMD_FAILED; } - if (lv_is_cow(lv) && !lv_is_virtual_origin(origin_from_cow(lv)) && - arg_count(cmd, available_ARG)) { - log_error("Can't change snapshot logical volume \"%s\"", - lv->name); - return ECMD_FAILED; + if (lv_is_cow(lv) && !lv_is_virtual_origin(origin = origin_from_cow(lv)) && + arg_count(cmd, activate_ARG)) { + if (origin->origin_count < 2) + snaps_msg[0] = '\0'; + else if (dm_snprintf(snaps_msg, sizeof(snaps_msg), + " and %u other snapshot(s)", + origin->origin_count - 1) < 0) { + log_error("Failed to prepare message."); + return ECMD_FAILED; + } + + if (!arg_count(cmd, yes_ARG) && + (yes_no_prompt("Change of snapshot %s will also change its" + " origin %s%s. Proceed? [y/n]: ", lv->name, + origin->name, snaps_msg) == 'n')) { + log_error("Logical volume %s not changed.", lv->name); + return ECMD_FAILED; + } } if (lv->status & PVMOVE) { log_error("Unable to change pvmove LV %s", lv->name); - if (arg_count(cmd, available_ARG)) + if (arg_count(cmd, activate_ARG)) log_error("Use 'pvmove --abort' to abandon a pvmove"); return ECMD_FAILED; } @@ -572,7 +784,7 @@ static int lvchange_single(struct cmd_context *cmd, struct logical_volume *lv, } /* If LV is sparse, activate origin instead */ - if (arg_count(cmd, available_ARG) && lv_is_cow(lv) && + if (arg_count(cmd, activate_ARG) && lv_is_cow(lv) && lv_is_virtual_origin(origin = origin_from_cow(lv))) lv = origin; @@ -582,11 +794,6 @@ static int lvchange_single(struct cmd_context *cmd, struct logical_volume *lv, return ECMD_FAILED; } - if (!get_activation_monitoring_mode(cmd, lv->vg, &dmeventd_mode)) - return ECMD_FAILED; - - init_dmeventd_monitor(dmeventd_mode); - /* * FIXME: DEFAULT_BACKGROUND_POLLING should be "unspecified". * If --poll is explicitly provided use it; otherwise polling @@ -648,6 +855,17 @@ static int lvchange_single(struct cmd_context *cmd, struct logical_volume *lv, } } + if (arg_count(cmd, discards_ARG) || + arg_count(cmd, zero_ARG)) { + if (!archived && !archive(lv->vg)) { + stack; + return ECMD_FAILED; + } + archived = 1; + doit += lvchange_pool_update(cmd, lv); + docmds++; + } + /* add tag */ if (arg_count(cmd, addtag_ARG)) { if (!archived && !archive(lv->vg)) { @@ -671,7 +889,7 @@ static int lvchange_single(struct cmd_context *cmd, struct logical_volume *lv, } if (doit) - log_print("Logical volume \"%s\" changed", lv->name); + log_print_unless_silent("Logical volume \"%s\" changed", lv->name); if (arg_count(cmd, resync_ARG)) if (!lvchange_resync(cmd, lv)) { @@ -679,9 +897,9 @@ static int lvchange_single(struct cmd_context *cmd, struct logical_volume *lv, return ECMD_FAILED; } - /* availability change */ - if (arg_count(cmd, available_ARG)) { - if (!lvchange_availability(cmd, lv)) { + /* activation change */ + if (arg_count(cmd, activate_ARG)) { + if (!_lvchange_activate(cmd, lv)) { stack; return ECMD_FAILED; } @@ -693,7 +911,7 @@ static int lvchange_single(struct cmd_context *cmd, struct logical_volume *lv, return ECMD_FAILED; } - if (!arg_count(cmd, available_ARG) && + if (!arg_count(cmd, activate_ARG) && !arg_count(cmd, refresh_ARG) && arg_count(cmd, monitor_ARG)) { if (!lvchange_monitoring(cmd, lv)) { @@ -702,7 +920,7 @@ static int lvchange_single(struct cmd_context *cmd, struct logical_volume *lv, } } - if (!arg_count(cmd, available_ARG) && + if (!arg_count(cmd, activate_ARG) && !arg_count(cmd, refresh_ARG) && arg_count(cmd, poll_ARG)) { if (!lvchange_background_polling(cmd, lv)) { @@ -721,24 +939,35 @@ static int lvchange_single(struct cmd_context *cmd, struct logical_volume *lv, int lvchange(struct cmd_context *cmd, int argc, char **argv) { - int update = /* options other than -a, --refresh, --monitor or --poll */ - arg_count(cmd, contiguous_ARG) || arg_count(cmd, permission_ARG) || - arg_count(cmd, readahead_ARG) || arg_count(cmd, persistent_ARG) || - arg_count(cmd, addtag_ARG) || arg_count(cmd, deltag_ARG) || - arg_count(cmd, resync_ARG) || arg_count(cmd, alloc_ARG); + /* + * Options that update metadata should be listed in one of + * the two lists below (i.e. options other than -a, --refresh, + * --monitor or --poll). + */ + int update_partial_safe = /* options safe to update if partial */ + arg_count(cmd, contiguous_ARG) || + arg_count(cmd, permission_ARG) || + arg_count(cmd, readahead_ARG) || + arg_count(cmd, persistent_ARG) || + arg_count(cmd, addtag_ARG) || + arg_count(cmd, deltag_ARG); + int update_partial_unsafe = + arg_count(cmd, resync_ARG) || + arg_count(cmd, alloc_ARG) || + arg_count(cmd, discards_ARG) || + arg_count(cmd, zero_ARG); + int update = update_partial_safe || update_partial_unsafe; if (!update && - !arg_count(cmd, available_ARG) && !arg_count(cmd, refresh_ARG) && - !arg_count(cmd, monitor_ARG) && !arg_count(cmd, poll_ARG) && - /* for persistent_ARG */ - !arg_count(cmd, minor_ARG) && !arg_count(cmd, major_ARG)) { - log_error("Need 1 or more of -a, -C, -j, -m, -M, -p, -r, " + !arg_count(cmd, activate_ARG) && !arg_count(cmd, refresh_ARG) && + !arg_count(cmd, monitor_ARG) && !arg_count(cmd, poll_ARG)) { + log_error("Need 1 or more of -a, -C, -M, -p, -r, -Z, " "--resync, --refresh, --alloc, --addtag, --deltag, " - "--monitor or --poll"); + "--monitor, --poll or --discards"); return EINVALID_CMD_LINE; } - if (arg_count(cmd, available_ARG) && arg_count(cmd, refresh_ARG)) { + if (arg_count(cmd, activate_ARG) && arg_count(cmd, refresh_ARG)) { log_error("Only one of -a and --refresh permitted."); return EINVALID_CMD_LINE; } @@ -749,7 +978,7 @@ int lvchange(struct cmd_context *cmd, int argc, char **argv) return EINVALID_CMD_LINE; } - if (!update) + if (!update || !update_partial_unsafe) cmd->handles_missing_pvs = 1; if (!argc) { @@ -778,6 +1007,13 @@ int lvchange(struct cmd_context *cmd, int argc, char **argv) return EINVALID_CMD_LINE; } + if (arg_count(cmd, sysinit_ARG) && lvmetad_active() && + arg_uint_value(cmd, activate_ARG, 0) == CHANGE_AAY) { + log_warn("lvmetad is active while using --sysinit -a ay, " + "skipping manual activation"); + return ECMD_PROCESSED; + } + return process_each_lv(cmd, argc, argv, update ? READ_FOR_UPDATE : 0, NULL, &lvchange_single); diff --git a/tools/lvconvert.c b/tools/lvconvert.c index 7c7826b..132a69d 100644 --- a/tools/lvconvert.c +++ b/tools/lvconvert.c @@ -20,6 +20,7 @@ struct lvconvert_params { int snapshot; int merge; + int merge_mirror; int zero; const char *origin; @@ -39,16 +40,24 @@ struct lvconvert_params { uint32_t stripes; uint32_t stripe_size; - struct segment_type *segtype; + const struct segment_type *segtype; alloc_policy_t alloc; int pv_count; char **pvs; struct dm_list *pvh; - struct dm_list *failed_pvs; + + int replace_pv_count; + char **replace_pvs; + struct dm_list *replace_pvh; struct logical_volume *lv_to_poll; + + uint64_t poolmetadata_size; + const char *pool_data_lv_name; + const char *pool_metadata_lv_name; + thin_discards_t discards; }; static int _lvconvert_name_params(struct lvconvert_params *lp, @@ -81,6 +90,15 @@ static int _lvconvert_name_params(struct lvconvert_params *lp, lp->origin = ptr + 1; } + if (!*pargc && lp->pool_data_lv_name) { + if (!lp->vg_name || !validate_name(lp->vg_name)) { + log_error("Please provide a valid volume group name."); + return 0; + } + lp->lv_name = lp->pool_data_lv_name; + return 1; /* Create metadata LV on it's own */ + } + if (!*pargc) { log_error("Please provide logical volume path"); return 0; @@ -108,7 +126,10 @@ static int _lvconvert_name_params(struct lvconvert_params *lp, if ((ptr = strrchr(lp->lv_name_full, '/'))) lp->lv_name = ptr + 1; - if (!apply_lvname_restrictions(lp->lv_name)) + if (!lp->merge_mirror && + !strstr(lp->lv_name, "_tdata") && + !strstr(lp->lv_name, "_tmeta") && + !apply_lvname_restrictions(lp->lv_name)) return_0; if (*pargc && lp->snapshot) { @@ -116,12 +137,20 @@ static int _lvconvert_name_params(struct lvconvert_params *lp, return 0; } + if (lp->pool_data_lv_name && lp->lv_name && lp->poolmetadata_size) { + log_error("Please specify either metadata logical volume or its size."); + return 0; + } + return 1; } static int _read_params(struct lvconvert_params *lp, struct cmd_context *cmd, int argc, char **argv) { + int i; + const char *tmp_str; + struct arg_value_group_list *group; int region_size; int pagesize = lvm_getpagesize(); @@ -131,7 +160,7 @@ static int _read_params(struct lvconvert_params *lp, struct cmd_context *cmd, (arg_count(cmd, mirrorlog_ARG) || arg_count(cmd, mirrors_ARG) || arg_count(cmd, repair_ARG))) { log_error("--snapshot or --merge argument cannot be mixed " - "with --mirrors, --repair or --log"); + "with --mirrors, --repair or --mirrorlog"); return 0; } @@ -152,6 +181,33 @@ static int _read_params(struct lvconvert_params *lp, struct cmd_context *cmd, return 0; } + if (arg_count(cmd, thinpool_ARG)) { + if (arg_count(cmd, merge_ARG)) { + log_error("--thinpool and --merge are mutually exlusive."); + return 0; + } + if (arg_count(cmd, mirrors_ARG)) { + log_error("--thinpool and --mirrors are mutually exlusive."); + return 0; + } + if (arg_count(cmd, repair_ARG)) { + log_error("--thinpool and --repair are mutually exlusive."); + return 0; + } + if (arg_count(cmd, snapshot_ARG)) { + log_error("--thinpool and --snapshot are mutually exlusive."); + return 0; + } + if (arg_count(cmd, splitmirrors_ARG)) { + log_error("--thinpool and --splitmirrors are mutually exlusive."); + return 0; + } + lp->discards = (thin_discards_t) arg_uint_value(cmd, discards_ARG, THIN_DISCARDS_PASSDOWN); + } else if (arg_count(cmd, discards_ARG)) { + log_error("--discards is only valid with --thinpool."); + return 0; + } + /* * The '--splitmirrors n' argument is equivalent to '--mirrors -n' * (note the minus sign), except that it signifies the additional @@ -159,21 +215,28 @@ static int _read_params(struct lvconvert_params *lp, struct cmd_context *cmd, * discarding it. */ if (arg_count(cmd, splitmirrors_ARG)) { - if (!arg_count(cmd, name_ARG)) { + if (!arg_count(cmd, name_ARG) && + !arg_count(cmd, trackchanges_ARG)) { log_error("Please name the new logical volume using '--name'"); return 0; } lp->lv_split_name = arg_value(cmd, name_ARG); - if (!apply_lvname_restrictions(lp->lv_split_name)) - return_0; + if (lp->lv_split_name) { + if (strchr(lp->lv_split_name, '/')) { + if (!(lp->vg_name = extract_vgname(cmd, lp->lv_split_name))) + return_0; + + /* Strip VG from lv_split_name */ + if ((tmp_str = strrchr(lp->lv_split_name, '/'))) + lp->lv_split_name = tmp_str + 1; + } - lp->keep_mimages = 1; - if (arg_sign_value(cmd, mirrors_ARG, 0) == SIGN_MINUS) { - log_error("Argument to --splitmirrors" - " cannot be negative"); - return 0; + if (!apply_lvname_restrictions(lp->lv_split_name)) + return_0; } + + lp->keep_mimages = 1; lp->mirrors = arg_uint_value(cmd, splitmirrors_ARG, 0); lp->mirrors_sign = SIGN_MINUS; } else if (arg_count(cmd, name_ARG)) { @@ -182,8 +245,12 @@ static int _read_params(struct lvconvert_params *lp, struct cmd_context *cmd, return 0; } - if (arg_count(cmd, merge_ARG)) - lp->merge = 1; + if (arg_count(cmd, merge_ARG)) { + if ((argc == 1) && strstr(argv[0], "_rimage_")) + lp->merge_mirror = 1; + else + lp->merge = 1; + } if (arg_count(cmd, mirrors_ARG)) { /* @@ -192,10 +259,10 @@ static int _read_params(struct lvconvert_params *lp, struct cmd_context *cmd, * versus an additional qualifying argument being added here. */ lp->mirrors = arg_uint_value(cmd, mirrors_ARG, 0); - lp->mirrors_sign = arg_sign_value(cmd, mirrors_ARG, 0); + lp->mirrors_sign = arg_sign_value(cmd, mirrors_ARG, SIGN_NONE); } - lp->alloc = arg_uint_value(cmd, alloc_ARG, ALLOC_INHERIT); + lp->alloc = (alloc_policy_t) arg_uint_value(cmd, alloc_ARG, ALLOC_INHERIT); /* There are three types of lvconvert. */ if (lp->merge) { /* Snapshot merge */ @@ -221,7 +288,7 @@ static int _read_params(struct lvconvert_params *lp, struct cmd_context *cmd, return 0; } - if (arg_sign_value(cmd, chunksize_ARG, 0) == SIGN_MINUS) { + if (arg_sign_value(cmd, chunksize_ARG, SIGN_NONE) == SIGN_MINUS) { log_error("Negative chunk size is invalid"); return 0; } @@ -242,15 +309,99 @@ static int _read_params(struct lvconvert_params *lp, struct cmd_context *cmd, SEG_CANNOT_BE_ZEROED) ? "n" : "y"), "n"); - } else { /* Mirrors */ + } else if (arg_count(cmd, replace_ARG)) { /* RAID device replacement */ + lp->replace_pv_count = arg_count(cmd, replace_ARG); + lp->replace_pvs = dm_pool_alloc(cmd->mem, sizeof(char *) * lp->replace_pv_count); + if (!lp->replace_pvs) + return_0; + + i = 0; + dm_list_iterate_items(group, &cmd->arg_value_groups) { + if (!grouped_arg_is_set(group->arg_values, replace_ARG)) + continue; + if (!(tmp_str = grouped_arg_str_value(group->arg_values, + replace_ARG, + NULL))) { + log_error("Failed to get '--replace' argument"); + return 0; + } + if (!(lp->replace_pvs[i++] = dm_pool_strdup(cmd->mem, + tmp_str))) + return_0; + } + } else if (arg_count(cmd, thinpool_ARG)) { + if (!(lp->pool_data_lv_name = arg_str_value(cmd, thinpool_ARG, NULL))) { + log_error("Missing pool logical volume name."); + return 0; + } + + if (arg_count(cmd, poolmetadata_ARG)) { + lp->pool_metadata_lv_name = arg_str_value(cmd, poolmetadata_ARG, ""); + } else if (arg_count(cmd, poolmetadatasize_ARG)) { + if (arg_sign_value(cmd, poolmetadatasize_ARG, SIGN_NONE) == SIGN_MINUS) { + log_error("Negative pool metadata size is invalid."); + return 0; + } + lp->poolmetadata_size = arg_uint64_value(cmd, poolmetadatasize_ARG, UINT64_C(0)); + + if (lp->poolmetadata_size > (2 * DEFAULT_THIN_POOL_MAX_METADATA_SIZE)) { + if (arg_count(cmd, poolmetadatasize_ARG)) + log_warn("WARNING: Maximum supported pool metadata size is 16GB."); + lp->poolmetadata_size = 2 * DEFAULT_THIN_POOL_MAX_METADATA_SIZE; + } else if (lp->poolmetadata_size < (2 * DEFAULT_THIN_POOL_MIN_METADATA_SIZE)) { + if (arg_count(cmd, poolmetadatasize_ARG)) + log_warn("WARNING: Minimum supported pool metadata size is 2M."); + lp->poolmetadata_size = 2 * DEFAULT_THIN_POOL_MIN_METADATA_SIZE; + } + + log_verbose("Setting pool metadata size to %" PRIu64 " sectors.", + lp->poolmetadata_size); + } + + if (arg_count(cmd, chunksize_ARG)) { + if (arg_sign_value(cmd, chunksize_ARG, SIGN_NONE) == SIGN_MINUS) { + log_error("Negative chunk size is invalid."); + return 0; + } + lp->chunk_size = arg_uint_value(cmd, chunksize_ARG, + DM_THIN_MIN_DATA_BLOCK_SIZE); + + if ((lp->chunk_size < DM_THIN_MIN_DATA_BLOCK_SIZE) || + (lp->chunk_size > DM_THIN_MAX_DATA_BLOCK_SIZE)) { + log_error("Chunk size must be in the range %uK to %uK.", + (DM_THIN_MIN_DATA_BLOCK_SIZE / 2), + (DM_THIN_MAX_DATA_BLOCK_SIZE / 2)); + return 0; + } + } else + lp->chunk_size = DM_THIN_MIN_DATA_BLOCK_SIZE; + + log_verbose("Setting pool metadata chunk size to %u sectors.", + lp->chunk_size); + + if (arg_count(cmd, zero_ARG)) + lp->zero = strcmp(arg_str_value(cmd, zero_ARG, "y"), "n"); + + /* If --thinpool contains VG name, extract it. */ + if ((tmp_str = strchr(lp->pool_data_lv_name, (int) '/'))) { + if (!(lp->vg_name = extract_vgname(cmd, lp->pool_data_lv_name))) + return 0; + /* Strip VG from pool */ + lp->pool_data_lv_name = tmp_str + 1; + } + + lp->segtype = get_segtype_from_string(cmd, arg_str_value(cmd, type_ARG, "thin-pool")); + if (!lp->segtype) + return_0; + } else { /* Mirrors (and some RAID functions) */ if (arg_count(cmd, chunksize_ARG)) { log_error("--chunksize is only available with " - "snapshots"); + "snapshots or thin pools."); return 0; } if (arg_count(cmd, zero_ARG)) { - log_error("--zero is only available with snapshots"); + log_error("--zero is only available with snapshots or thin pools."); return 0; } @@ -260,7 +411,7 @@ static int _read_params(struct lvconvert_params *lp, struct cmd_context *cmd, */ if (arg_count(cmd, regionsize_ARG)) { - if (arg_sign_value(cmd, regionsize_ARG, 0) == + if (arg_sign_value(cmd, regionsize_ARG, SIGN_NONE) == SIGN_MINUS) { log_error("Negative regionsize is invalid"); return 0; @@ -303,11 +454,12 @@ static int _read_params(struct lvconvert_params *lp, struct cmd_context *cmd, return 0; } - if (!(lp->segtype = get_segtype_from_string(cmd, "mirror"))) + lp->segtype = get_segtype_from_string(cmd, arg_str_value(cmd, type_ARG, "mirror")); + if (!lp->segtype) return_0; } - if (activation() && lp->segtype->ops->target_present && + if (activation() && lp->segtype && lp->segtype->ops->target_present && !lp->segtype->ops->target_present(cmd, NULL, NULL)) { log_error("%s: Required device-mapper target(s) not " "detected in your kernel", lp->segtype->name); @@ -319,7 +471,6 @@ static int _read_params(struct lvconvert_params *lp, struct cmd_context *cmd, lp->pv_count = argc; lp->pvs = argv; - lp->failed_pvs = NULL; return 1; } @@ -342,7 +493,7 @@ static struct logical_volume *_get_lvconvert_lv(struct cmd_context *cmd __attrib struct volume_group *vg, const char *name, const char *uuid, - uint32_t lv_type __attribute__((unused))) + uint64_t lv_type __attribute__((unused))) { struct logical_volume *lv = find_lv(vg, name); @@ -352,23 +503,12 @@ static struct logical_volume *_get_lvconvert_lv(struct cmd_context *cmd __attrib return lv; } -static int _finish_lvconvert_mirror(struct cmd_context *cmd, - struct volume_group *vg, - struct logical_volume *lv, - struct dm_list *lvs_changed __attribute__((unused))) +static int _reload_lv(struct cmd_context *cmd, + struct volume_group *vg, + struct logical_volume *lv) { int r = 0; - if (!(lv->status & CONVERTING)) - return 1; - - if (!collapse_mirrored_lv(lv)) { - log_error("Failed to remove temporary sync layer."); - return 0; - } - - lv->status &= ~CONVERTING; - log_very_verbose("Updating logical volume \"%s\" on disk(s)", lv->name); if (!vg_write(vg)) @@ -381,7 +521,8 @@ static int _finish_lvconvert_mirror(struct cmd_context *cmd, } if (!vg_commit(vg)) { - resume_lv(cmd, lv); + if (!resume_lv(cmd, lv)) + stack; goto_out; } @@ -393,12 +534,36 @@ static int _finish_lvconvert_mirror(struct cmd_context *cmd, } r = 1; - log_print("Logical volume %s converted.", lv->name); out: backup(vg); return r; } +static int _finish_lvconvert_mirror(struct cmd_context *cmd, + struct volume_group *vg, + struct logical_volume *lv, + struct dm_list *lvs_changed __attribute__((unused))) +{ + if (!(lv->status & CONVERTING)) + return 1; + + if (!collapse_mirrored_lv(lv)) { + log_error("Failed to remove temporary sync layer."); + return 0; + } + + lv->status &= ~CONVERTING; + + log_very_verbose("Updating logical volume \"%s\" on disk(s)", lv->name); + + if (!(_reload_lv(cmd, vg, lv))) + return_0; + + log_print_unless_silent("Logical volume %s converted.", lv->name); + + return 1; +} + static int _finish_lvconvert_merge(struct cmd_context *cmd, struct volume_group *vg, struct logical_volume *lv, @@ -410,7 +575,7 @@ static int _finish_lvconvert_merge(struct cmd_context *cmd, return 0; } - log_print("Merge of snapshot into logical volume %s has finished.", lv->name); + log_print_unless_silent("Merge of snapshot into logical volume %s has finished.", lv->name); if (!lv_remove_single(cmd, snap_seg->cow, DONT_PROMPT)) { log_error("Could not remove snapshot %s merged into %s.", snap_seg->cow->name, lv->name); @@ -433,14 +598,17 @@ static progress_t _poll_merge_progress(struct cmd_context *cmd, } else if (percent == PERCENT_INVALID) { log_error("%s: Merging snapshot invalidated. Aborting merge.", lv->name); return PROGRESS_CHECK_FAILED; + } else if (percent == PERCENT_MERGE_FAILED) { + log_error("%s: Merge failed. Retry merge or inspect manually.", lv->name); + return PROGRESS_CHECK_FAILED; } if (parms->progress_display) - log_print("%s: %s: %.1f%%", lv->name, parms->progress_title, - percent_to_float(percent)); + log_print_unless_silent("%s: %s: %.1f%%", lv->name, parms->progress_title, + 100.0 - percent_to_float(percent)); else log_verbose("%s: %s: %.1f%%", lv->name, parms->progress_title, - percent_to_float(percent)); + 100.0 - percent_to_float(percent)); if (percent == PERCENT_0) return PROGRESS_FINISHED_ALL; @@ -481,7 +649,7 @@ int lvconvert_poll(struct cmd_context *cmd, struct logical_volume *lv, if (!uuid || !lv_full_name) return_0; - if (!dm_snprintf(lv_full_name, len, "%s/%s", lv->vg->name, lv->name)) + if (dm_snprintf(lv_full_name, len, "%s/%s", lv->vg->name, lv->name) < 0) return_0; memcpy(uuid, &lv->lvid, sizeof(lv->lvid)); @@ -538,36 +706,51 @@ static int _insert_lvconvert_layer(struct cmd_context *cmd, return 1; } -static int _area_missing(struct lv_segment *lvseg, int s) -{ - if (seg_type(lvseg, s) == AREA_LV) { - if (seg_lv(lvseg, s)->status & PARTIAL_LV) - return 1; - } else if ((seg_type(lvseg, s) == AREA_PV) && - (is_missing_pv(seg_pv(lvseg, s)))) - return 1; - - return 0; -} - -/* FIXME we want to handle mirror stacks here... */ static int _failed_mirrors_count(struct logical_volume *lv) { struct lv_segment *lvseg; int ret = 0; - int s; + unsigned s; dm_list_iterate_items(lvseg, &lv->segments) { if (!seg_is_mirrored(lvseg)) return -1; - for (s = 0; s < lvseg->area_count; s++) - if (_area_missing(lvseg, s)) - ret++; + for (s = 0; s < lvseg->area_count; s++) { + if (seg_type(lvseg, s) == AREA_LV) { + if (is_temporary_mirror_layer(seg_lv(lvseg, s))) + ret += _failed_mirrors_count(seg_lv(lvseg, s)); + else if (seg_lv(lvseg, s)->status & PARTIAL_LV) + ++ ret; + else if (seg_type(lvseg, s) == AREA_PV && + is_missing_pv(seg_pv(lvseg, s))) + ++ret; + } + } } return ret; } +static int _failed_logs_count(struct logical_volume *lv) +{ + int ret = 0; + unsigned s; + struct logical_volume *log_lv = first_seg(lv)->log_lv; + if (log_lv && (log_lv->status & PARTIAL_LV)) { + if (log_lv->status & MIRRORED) + ret += _failed_mirrors_count(log_lv); + else + ret += 1; + } + for (s = 0; s < first_seg(lv)->area_count; s++) { + if (seg_type(first_seg(lv), s) == AREA_LV && + is_temporary_mirror_layer(seg_lv(first_seg(lv), s))) + ret += _failed_logs_count(seg_lv(first_seg(lv), s)); + } + return ret; +} + + static struct dm_list *_failed_pv_list(struct volume_group *vg) { struct dm_list *failed_pvs; @@ -684,10 +867,10 @@ static int _get_log_count(struct logical_volume *lv) struct logical_volume *log_lv; log_lv = first_seg(_original_lv(lv))->log_lv; - if (!log_lv) - return 0; + if (log_lv) + return lv_mirror_count(log_lv); - return lv_mirror_count(log_lv); + return 0; } static int _lv_update_mirrored_log(struct logical_volume *lv, @@ -724,8 +907,10 @@ static int _lv_update_log_type(struct cmd_context *cmd, struct dm_list *operable_pvs, int log_count) { - uint32_t region_size; int old_log_count; + uint32_t region_size = (lp) ? lp->region_size : + first_seg(lv)->region_size; + alloc_policy_t alloc = (lp) ? lp->alloc : lv->alloc; struct logical_volume *original_lv; struct logical_volume *log_lv; @@ -734,18 +919,6 @@ static int _lv_update_log_type(struct cmd_context *cmd, return 1; original_lv = _original_lv(lv); - region_size = adjusted_mirror_region_size(lv->vg->extent_size, - lv->le_count, - lp->region_size); - - /* Add a log where there is none */ - if (!old_log_count) { - if (!add_mirror_log(cmd, original_lv, log_count, - region_size, operable_pvs, lp->alloc)) - return_0; - return 1; - } - /* Remove an existing log completely */ if (!log_count) { if (!remove_mirror_log(cmd, original_lv, operable_pvs, @@ -759,13 +932,28 @@ static int _lv_update_log_type(struct cmd_context *cmd, /* Adding redundancy to the log */ if (old_log_count < log_count) { - log_error("Adding log redundancy not supported yet."); - log_error("Try converting the log to 'core' first."); - return_0; + region_size = adjusted_mirror_region_size(lv->vg->extent_size, + lv->le_count, + region_size); + + if (!add_mirror_log(cmd, original_lv, log_count, + region_size, operable_pvs, alloc)) + return_0; + /* + * FIXME: This simple approach won't work in cluster mirrors, + * but it doesn't matter because we don't support + * mirrored logs in cluster mirrors. + */ + if (old_log_count && + !_reload_lv(cmd, log_lv->vg, log_lv)) + return_0; + + return 1; } /* Reducing redundancy of the log */ - return remove_mirror_images(log_lv, log_count, is_mirror_image_removable, operable_pvs, 1U); + return remove_mirror_images(log_lv, log_count, + is_mirror_image_removable, operable_pvs, 1U); } /* @@ -790,6 +978,7 @@ static void _remove_missing_empty_pv(struct volume_group *vg, struct dm_list *re vg->free_count -= pvl_vg->pv->pe_count; vg->extent_count -= pvl_vg->pv->pe_count; del_pvl_from_vgs(vg, pvl_vg); + free_pv_fid(pvl_vg->pv); removed++; } @@ -960,33 +1149,6 @@ static int _lvconvert_mirrors_parse_params(struct cmd_context *cmd, return 1; } -static int _reload_lv(struct cmd_context *cmd, struct logical_volume *lv) -{ - log_very_verbose("Updating logical volume \"%s\" on disk(s)", lv->name); - - if (!vg_write(lv->vg)) - return_0; - - if (!suspend_lv(cmd, lv)) { - log_error("Failed to lock %s", lv->name); - vg_revert(lv->vg); - return 0; - } - - if (!vg_commit(lv->vg)) { - if (!resume_lv(cmd, lv)) - stack; - return_0; - } - - log_very_verbose("Updating \"%s\" in kernel", lv->name); - - if (!resume_lv(cmd, lv)) { - log_error("Problem reactivating %s", lv->name); - return 0; - } - return 1; -} /* * _lvconvert_mirrors_aux @@ -1036,10 +1198,9 @@ static int _lvconvert_mirrors_aux(struct cmd_context *cmd, */ if (!lv_add_mirrors(cmd, lv, new_mimage_count - 1, lp->stripes, lp->stripe_size, region_size, new_log_count, operable_pvs, - lp->alloc, MIRROR_BY_LV)) { - stack; - return 0; - } + lp->alloc, MIRROR_BY_LV)) + return_0; + if (lp->wait_completion) lp->need_polling = 1; @@ -1050,7 +1211,7 @@ static int _lvconvert_mirrors_aux(struct cmd_context *cmd, * Up-convert m-way mirror to n-way mirror */ if (new_mimage_count > old_mimage_count) { - if (lv->status & MIRROR_NOTSYNCED) { + if (lv->status & LV_NOTSYNCED) { log_error("Can't add mirror to out-of-sync mirrored " "LV: use lvchange --resync first."); return 0; @@ -1080,16 +1241,6 @@ static int _lvconvert_mirrors_aux(struct cmd_context *cmd, } /* - * Is there already a convert in progress? We do not - * currently allow more than one. - */ - if (find_temporary_mirror(lv) || (lv->status & CONVERTING)) { - log_error("%s is already being converted. Unable to start another conversion.", - lv->name); - return 0; - } - - /* * Log addition/removal should be done before the layer * insertion to make the end result consistent with * linear-to-mirror conversion. @@ -1109,7 +1260,8 @@ static int _lvconvert_mirrors_aux(struct cmd_context *cmd, /* FIXME: can't have multiple mlogs. force corelog. */ if (!lv_add_mirrors(cmd, lv, - new_mimage_count - old_mimage_count, lp->stripes, lp->stripe_size, + new_mimage_count - old_mimage_count, + lp->stripes, lp->stripe_size, region_size, 0U, operable_pvs, lp->alloc, MIRROR_BY_LV)) { layer_lv = seg_lv(first_seg(lv), 0); @@ -1145,6 +1297,11 @@ static int _lvconvert_mirrors_aux(struct cmd_context *cmd, /* Reduce number of mirrors */ if (lp->keep_mimages) { + if (arg_count(cmd, trackchanges_ARG)) { + log_error("--trackchanges is not available " + "to 'mirror' segment type"); + return 0; + } if (!lv_split_mirror_images(lv, lp->lv_split_name, nmc, operable_pvs)) return 0; @@ -1169,9 +1326,50 @@ out: out_skip_log_convert: - if (!_reload_lv(cmd, lv)) + if (!_reload_lv(cmd, lv->vg, lv)) + return_0; + + return 1; +} + +int mirror_remove_missing(struct cmd_context *cmd, + struct logical_volume *lv, int force) +{ + struct dm_list *failed_pvs; + int log_count = _get_log_count(lv) - _failed_logs_count(lv); + + if (!(failed_pvs = _failed_pv_list(lv->vg))) + return_0; + + /* No point in keeping a log if the result is not a mirror */ + if (_failed_mirrors_count(lv) + 1 >= lv_mirror_count(lv)) + log_count = 0; + + if (force && _failed_mirrors_count(lv) == lv_mirror_count(lv)) { + log_error("No usable images left in %s.", lv->name); + return lv_remove_with_dependencies(cmd, lv, DONT_PROMPT, 0); + } + + /* + * We must adjust the log first, or the entire mirror + * will get stuck during a suspend. + */ + if (!_lv_update_mirrored_log(lv, failed_pvs, log_count)) + return 0; + + if (_failed_mirrors_count(lv) > 0 && + !lv_remove_mirrors(cmd, lv, _failed_mirrors_count(lv), + log_count ? 0U : 1U, + _is_partial_lv, NULL, 0)) + return 0; + + if (!_lv_update_log_type(cmd, NULL, lv, failed_pvs, + log_count)) return 0; + if (!_reload_lv(cmd, lv->vg, lv)) + return_0; + return 1; } @@ -1187,16 +1385,16 @@ out_skip_log_convert: */ static int _lvconvert_mirrors_repair(struct cmd_context *cmd, struct logical_volume *lv, - struct lvconvert_params *lp, - uint32_t old_mimage_count, - uint32_t old_log_count) + struct lvconvert_params *lp) { - int failed_log = 0; - int failed_mirrors = 0; - int replace_log = 0; - int replace_mirrors = 0; - uint32_t new_log_count, log_count; - struct logical_volume *log_lv; + int failed_logs = 0; + int failed_mimages = 0; + int replace_logs = 0; + int replace_mimages = 0; + uint32_t log_count; + + uint32_t original_mimages = lv_mirror_count(lv); + uint32_t original_logs = _get_log_count(lv); cmd->handles_missing_pvs = 1; cmd->partial_activation = 1; @@ -1209,93 +1407,43 @@ static int _lvconvert_mirrors_repair(struct cmd_context *cmd, return 1; } - /* - * Count the failed mimages - negative if 'lv' is not a mirror - */ - if ((failed_mirrors = _failed_mirrors_count(lv)) < 0) - return_0; + failed_mimages = _failed_mirrors_count(lv); + failed_logs = _failed_logs_count(lv); - lp->mirrors = old_mimage_count - failed_mirrors; + mirror_remove_missing(cmd, lv, 0); - if (lp->mirrors != old_mimage_count) + if (failed_mimages) log_error("Mirror status: %d of %d images failed.", - failed_mirrors, old_mimage_count); + failed_mimages, original_mimages); /* * Count the failed log devices */ - new_log_count = old_log_count; - log_lv = first_seg(lv)->log_lv; - if (log_lv) { - new_log_count = lv_mirror_count(log_lv); - if (log_lv->status & PARTIAL_LV) { - failed_log = 1; - if (log_lv->status & MIRRORED) - new_log_count -= _failed_mirrors_count(log_lv); - else - new_log_count = 0; - } - } - if (old_log_count != new_log_count) - log_error("Mirror log status: %d of %d images failed%s", - old_log_count - new_log_count, old_log_count, - (!new_log_count) ? " - switching to core" : ""); + if (failed_logs) + log_error("Mirror log status: %d of %d images failed.", + failed_logs, original_logs); /* * Find out our policies */ - _lvconvert_mirrors_repair_ask(cmd, failed_log, failed_mirrors, - &replace_log, &replace_mirrors); - - /* - * First phase - remove faulty devices - */ - if (!(lp->failed_pvs = _failed_pv_list(lv->vg))) - return_0; - - log_count = new_log_count; - - /* - * We must adjust the log first, or the entire mirror - * will get stuck during a suspend. - */ - if (!_lv_update_mirrored_log(lv, lp->failed_pvs, log_count)) - return 0; - - if (lp->mirrors == 1) - log_count = 0; - - if (failed_mirrors) { - if (!lv_remove_mirrors(cmd, lv, failed_mirrors, - log_count ? 0U : 1U, - _is_partial_lv, NULL, 0)) - return 0; - } - - if (!_lv_update_log_type(cmd, lp, lv, lp->failed_pvs, - log_count)) - return 0; - - if (!_reload_lv(cmd, lv)) - return 0; + _lvconvert_mirrors_repair_ask(cmd, failed_logs, failed_mimages, + &replace_logs, &replace_mimages); /* * Second phase - replace faulty devices */ - - if (replace_mirrors) - lp->mirrors = old_mimage_count; + lp->mirrors = replace_mimages ? original_mimages : (original_mimages - failed_mimages); /* * It does not make sense to replace the log if the volume is no longer * a mirror. */ - if (!replace_mirrors && lp->mirrors == 1) - replace_log = 0; + if (lp->mirrors == 1) + replace_logs = 0; - log_count = replace_log ? old_log_count : new_log_count; + log_count = replace_logs ? original_logs : (original_logs - failed_logs); - while (replace_mirrors || replace_log) { + while (replace_mimages || replace_logs) { log_warn("Trying to up-convert to %d images, %d logs.", lp->mirrors, log_count); if (_lvconvert_mirrors_aux(cmd, lv, lp, NULL, lp->mirrors, log_count)) @@ -1310,12 +1458,12 @@ static int _lvconvert_mirrors_repair(struct cmd_context *cmd, } } - if (replace_mirrors && lp->mirrors != old_mimage_count) + if (replace_mimages && lv_mirror_count(lv) != original_mimages) log_warn("WARNING: Failed to replace %d of %d images in volume %s", - old_mimage_count - lp->mirrors, old_mimage_count, lv->name); - if (replace_log && log_count != old_log_count) + original_mimages - lv_mirror_count(lv), original_mimages, lv->name); + if (replace_logs && _get_log_count(lv) != original_logs) log_warn("WARNING: Failed to replace %d of %d logs in volume %s", - old_log_count - log_count, old_log_count, lv->name); + original_logs - _get_log_count(lv), original_logs, lv->name); /* if (!arg_count(cmd, use_policies_ARG) && (lp->mirrors != old_mimage_count || log_count != old_log_count)) @@ -1340,6 +1488,19 @@ static int _lvconvert_mirrors(struct cmd_context *cmd, uint32_t new_mimage_count; uint32_t new_log_count; + if (lp->merge_mirror) { + log_error("Unable to merge mirror images" + "of segment type 'mirror'"); + return 0; + } + + /* TODO: decide what should be done here */ + if (lv_is_thin_type(lv)) { + log_error("Converting segment type for %s/%s to mirror is not yet supported.", + lv->vg->name, lv->name); + return 0; + } + /* Adjust mimage and/or log count */ if (!_lvconvert_mirrors_parse_params(cmd, lv, lp, &old_mimage_count, &old_log_count, @@ -1349,8 +1510,8 @@ static int _lvconvert_mirrors(struct cmd_context *cmd, if (((old_mimage_count < new_mimage_count && old_log_count > new_log_count) || (old_mimage_count > new_mimage_count && old_log_count < new_log_count)) && lp->pv_count) { - log_error("Cannot both allocate and free extents when specifying physical" - " volumes to use."); + log_error("Cannot both allocate and free extents when " + "specifying physical volumes to use."); log_error("Please specify the operation in two steps."); return 0; } @@ -1361,27 +1522,162 @@ static int _lvconvert_mirrors(struct cmd_context *cmd, return 1; if (repair) - return _lvconvert_mirrors_repair(cmd, lv, lp, - old_mimage_count, - old_log_count); + return _lvconvert_mirrors_repair(cmd, lv, lp); if (!_lvconvert_mirrors_aux(cmd, lv, lp, NULL, new_mimage_count, new_log_count)) return 0; if (!lp->need_polling) - log_print("Logical volume %s converted.", lv->name); + log_print_unless_silent("Logical volume %s converted.", lv->name); backup(lv->vg); return 1; } +static int is_valid_raid_conversion(const struct segment_type *from_segtype, + const struct segment_type *to_segtype) +{ + if (from_segtype == to_segtype) + return 1; + + if (!segtype_is_raid(from_segtype) && !segtype_is_raid(to_segtype)) + return_0; /* Not converting to or from RAID? */ + + return 1; +} + +static void _lvconvert_raid_repair_ask(struct cmd_context *cmd, int *replace_dev) +{ + const char *dev_policy = NULL; + + int force = arg_count(cmd, force_ARG); + int yes = arg_count(cmd, yes_ARG); + + *replace_dev = 0; + + if (arg_count(cmd, use_policies_ARG)) { + dev_policy = find_config_tree_str(cmd, "activation/raid_fault_policy", DEFAULT_RAID_FAULT_POLICY); + + if (!strcmp(dev_policy, "allocate") || + !strcmp(dev_policy, "replace")) + *replace_dev = 1; + /* else if (!strcmp(dev_policy, "anything_else")) -- ignore */ + return; + } + + if (yes) { + *replace_dev = 1; + return; + } + + if (force != PROMPT) + return; + + if (yes_no_prompt("Attempt to replace failed RAID images " + "(requires full device resync)? [y/n]: ") == 'y') { + *replace_dev = 1; + } +} + +static int lvconvert_raid(struct logical_volume *lv, struct lvconvert_params *lp) +{ + int replace = 0; + int uninitialized_var(image_count); + struct dm_list *failed_pvs; + struct cmd_context *cmd = lv->vg->cmd; + struct lv_segment *seg = first_seg(lv); + + if (!arg_count(cmd, type_ARG)) + lp->segtype = seg->segtype; + + /* Can only change image count for raid1 and linear */ + if (arg_count(cmd, mirrors_ARG) && + !seg_is_mirrored(seg) && !seg_is_linear(seg)) { + log_error("'--mirrors/-m' is not compatible with %s", + seg->segtype->ops->name(seg)); + return 0; + } + + if (!is_valid_raid_conversion(seg->segtype, lp->segtype)) { + log_error("Unable to convert %s/%s from %s to %s", + lv->vg->name, lv->name, + seg->segtype->ops->name(seg), lp->segtype->name); + return 0; + } + + /* Change number of RAID1 images */ + if (arg_count(cmd, mirrors_ARG) || arg_count(cmd, splitmirrors_ARG)) { + image_count = lv_raid_image_count(lv); + if (lp->mirrors_sign == SIGN_PLUS) + image_count += lp->mirrors; + else if (lp->mirrors_sign == SIGN_MINUS) + image_count -= lp->mirrors; + else + image_count = lp->mirrors + 1; + + if (image_count < 1) { + log_error("Unable to %s images by specified amount", + arg_count(cmd, splitmirrors_ARG) ? + "split" : "reduce"); + return 0; + } + } + + if (lp->merge_mirror) + return lv_raid_merge(lv); + + if (arg_count(cmd, trackchanges_ARG)) + return lv_raid_split_and_track(lv, lp->pvh); + + if (arg_count(cmd, splitmirrors_ARG)) + return lv_raid_split(lv, lp->lv_split_name, + image_count, lp->pvh); + + if (arg_count(cmd, mirrors_ARG)) + return lv_raid_change_image_count(lv, image_count, lp->pvh); + + if (arg_count(cmd, type_ARG)) + return lv_raid_reshape(lv, lp->segtype); + + if (arg_count(cmd, replace_ARG)) + return lv_raid_replace(lv, lp->replace_pvh, lp->pvh); + + if (arg_count(cmd, repair_ARG)) { + _lvconvert_raid_repair_ask(cmd, &replace); + + if (replace) { + if (!(failed_pvs = _failed_pv_list(lv->vg))) + return_0; + + if (!lv_raid_replace(lv, failed_pvs, lp->pvh)) { + log_error("Failed to replace faulty devices in" + " %s/%s.", lv->vg->name, lv->name); + return 0; + } + + log_print_unless_silent("Faulty devices in %s/%s successfully" + " replaced.", lv->vg->name, lv->name); + return 1; + } + + /* "warn" if policy not set to replace */ + if (arg_count(cmd, use_policies_ARG)) + log_error("Use 'lvconvert --repair %s/%s' to " + "replace failed device", + lv->vg->name, lv->name); + return 1; + } + + log_error("Conversion operation not yet supported."); + return 0; +} + static int lvconvert_snapshot(struct cmd_context *cmd, struct logical_volume *lv, struct lvconvert_params *lp) { struct logical_volume *org; - int r = 0; if (!(org = find_lv(lv->vg, lp->origin))) { log_error("Couldn't find origin volume '%s'.", lp->origin); @@ -1395,7 +1691,7 @@ static int lvconvert_snapshot(struct cmd_context *cmd, } if (org->status & (LOCKED|PVMOVE|MIRRORED) || lv_is_cow(org)) { - log_error("Unable to create a snapshot of a %s LV.", + log_error("Unable to convert an LV into a snapshot of a %s LV.", org->status & LOCKED ? "locked" : org->status & PVMOVE ? "pvmove" : org->status & MIRRORED ? "mirrored" : @@ -1422,28 +1718,12 @@ static int lvconvert_snapshot(struct cmd_context *cmd, } /* store vg on disk(s) */ - if (!vg_write(lv->vg)) + if (!_reload_lv(cmd, lv->vg, lv)) return_0; - if (!suspend_lv(cmd, org)) { - log_error("Failed to suspend origin %s", org->name); - vg_revert(lv->vg); - goto out; - } - - if (!vg_commit(lv->vg)) - goto_out; + log_print_unless_silent("Logical volume %s converted to snapshot.", lv->name); - if (!resume_lv(cmd, org)) { - log_error("Problem reactivating origin %s", org->name); - goto out; - } - - log_print("Logical volume %s converted to snapshot.", lv->name); - r = 1; -out: - backup(lv->vg); - return r; + return 1; } static int lvconvert_merge(struct cmd_context *cmd, @@ -1485,7 +1765,7 @@ static int lvconvert_merge(struct cmd_context *cmd, } if (lv_info(cmd, lv, 0, &info, 1, 0)) { if (info.open_count) { - log_print("Can't merge when snapshot is open"); + log_print_unless_silent("Can't merge when snapshot is open"); merge_on_activate = 1; } } @@ -1501,8 +1781,8 @@ static int lvconvert_merge(struct cmd_context *cmd, if (!vg_commit(lv->vg)) return_0; r = 1; - log_print("Merging of snapshot %s will start " - "next activation.", lv->name); + log_print_unless_silent("Merging of snapshot %s will start " + "next activation.", lv->name); goto out; } @@ -1528,16 +1808,162 @@ static int lvconvert_merge(struct cmd_context *cmd, lp->lv_to_poll = origin; r = 1; - log_print("Merging of volume %s started.", lv->name); + log_print_unless_silent("Merging of volume %s started.", lv->name); out: backup(lv->vg); return r; } +/* + * Thin lvconvert version which + * rename metadata + * convert/layers thinpool over data + * attach metadata + */ +static int _lvconvert_thinpool(struct cmd_context *cmd, + struct logical_volume *pool_lv, + struct lvconvert_params *lp) +{ + int r = 0; + char *name; + int len; + struct lv_segment *seg; + struct logical_volume *data_lv; + struct logical_volume *metadata_lv; + + if (lv_is_thin_type(pool_lv)) { + log_error("Can't use thin logical volume %s/%s for thin pool data.", + pool_lv->vg->name, pool_lv->name); + return 0; + } + + /* We are changing target type, so deactivate first */ + if (!deactivate_lv(cmd, pool_lv)) { + log_error("Can't deactivate logical volume %s/%s.", + pool_lv->vg->name, pool_lv->name); + return 0; + } + + if (lp->pool_metadata_lv_name) { + metadata_lv = find_lv(pool_lv->vg, lp->pool_metadata_lv_name); + if (!metadata_lv) { + log_error("Unknown metadata LV %s", lp->pool_metadata_lv_name); + return 0; + } + if (metadata_lv == pool_lv) { + log_error("Can't use same LV for thin data and metadata LV %s", + lp->pool_metadata_lv_name); + return 0; + } + if (lv_is_thin_type(metadata_lv)) { + log_error("Can't use thin pool logical volume %s/%s " + "for thin pool metadata.", + metadata_lv->vg->name, metadata_lv->name); + return 0; + } + } else if (arg_count(cmd, poolmetadatasize_ARG)) { + /* FIXME: allocate metadata LV! */ + metadata_lv = NULL; + log_error("Uncreated metadata."); + return 0; + } else { + log_error("Uknown metadata."); + return 0; + } + + len = strlen(pool_lv->name) + 16; + if (!(name = dm_pool_alloc(pool_lv->vg->vgmem, len))) { + log_error("Cannot allocate new name."); + return 0; + } + + if (!lv_is_active(metadata_lv)) { + if (!deactivate_lv(cmd, metadata_lv)) { + log_error("Can't deactivate logical volume %s/%s.", + metadata_lv->vg->name, metadata_lv->name); + return 0; + } + if (!activate_lv_local(cmd, metadata_lv)) { + log_error("Aborting. Failed to activate thin metadata lv."); + return 0; + } + } + + if (!set_lv(cmd, metadata_lv, UINT64_C(0), 0)) { + log_error("Aborting. Failed to wipe thin metadata lv."); + return 0; + } + + if (!deactivate_lv(cmd, metadata_lv)) { + log_error("Aborting. Failed to deactivate thin metadata lv. " + "Manual intervention required."); + return 0; + } + + if (dm_snprintf(name, len, "%s_tmeta", pool_lv->name) < 0) + return_0; + + /* Rename deactivated metadata LV to have _tmeta suffix */ + /* Implicit checks if metadata_lv is visible */ + if (!lv_rename_update(cmd, metadata_lv, name, 0)) + return_0; + + /* + * Since we wish to have underlaying dev, to match _tdata + * rename data LV first, also checks for visible LV + */ + /* FIXME: any more types prohibited here? */ + /* FIXME: revert renamed LVs in fail path? */ + + /* FIXME: common code with metadata/thin_manip.c extend_pool() */ + /* Create layer _tdata */ + if (!(data_lv = insert_layer_for_lv(pool_lv->vg->cmd, pool_lv, + pool_lv->status, "_tdata"))) + return_0; + + seg = first_seg(pool_lv); + seg->segtype = lp->segtype; + seg->lv->status |= THIN_POOL; + + seg->chunk_size = lp->chunk_size; + seg->zero_new_blocks = lp->zero ? 1 : 0; + seg->discards = lp->discards; + seg->low_water_mark = 0; + seg->transaction_id = 0; + + if (!attach_pool_metadata_lv(seg, metadata_lv)) + return_0; + + /* Drop reference as attach_pool_data_lv() takes it again */ + remove_seg_from_segs_using_this_lv(data_lv, seg); + if (!attach_pool_data_lv(seg, data_lv)) + return_0; + + if (!vg_write(pool_lv->vg) || !vg_commit(pool_lv->vg)) + return_0; + + if (!activate_lv_excl(cmd, pool_lv)) { + log_error("Failed to activate pool logical volume %s/%s.", + pool_lv->vg->name, pool_lv->name); + goto out; + } + + log_print_unless_silent("Converted %s/%s to thin pool.", + pool_lv->vg->name, pool_lv->name); + + r = 1; +out: + backup(pool_lv->vg); + return r; +} + static int _lvconvert_single(struct cmd_context *cmd, struct logical_volume *lv, void *handle) { struct lvconvert_params *lp = handle; + struct dm_list *failed_pvs; + struct lvinfo info; + percent_t snap_percent; if (lv->status & LOCKED) { log_error("Cannot convert locked LV %s", lv->name); @@ -1555,19 +1981,30 @@ static int _lvconvert_single(struct cmd_context *cmd, struct logical_volume *lv, return ECMD_FAILED; } - if (arg_count(cmd, repair_ARG) && !(lv->status & MIRRORED)) { + if (arg_count(cmd, repair_ARG) && + !(lv->status & MIRRORED) && !(lv->status & RAID)) { if (arg_count(cmd, use_policies_ARG)) return ECMD_PROCESSED; /* nothing to be done here */ log_error("Can't repair non-mirrored LV \"%s\".", lv->name); return ECMD_FAILED; } + if (!lp->segtype) + lp->segtype = first_seg(lv)->segtype; + if (lp->merge) { if (!lv_is_cow(lv)) { - log_error("Logical volume \"%s\" is not a snapshot", + log_error("\"%s\" is not a mergeable logical volume", lv->name); return ECMD_FAILED; } + if (lv_info(lv->vg->cmd, lv, 0, &info, 1, 0) + && info.exists && info.live_table && + (!lv_snapshot_percent(lv, &snap_percent) || + snap_percent == PERCENT_INVALID)) { + log_error("Unable to merge invalidated snapshot LV \"%s\"", lv->name); + return ECMD_FAILED; + } if (!archive(lv->vg)) { stack; return ECMD_FAILED; @@ -1589,6 +2026,34 @@ static int _lvconvert_single(struct cmd_context *cmd, struct logical_volume *lv, stack; return ECMD_FAILED; } + } else if (arg_count(cmd, thinpool_ARG)) { + if (!archive(lv->vg)) { + stack; + return ECMD_FAILED; + } + if (!_lvconvert_thinpool(cmd, lv, lp)) { + stack; + return ECMD_FAILED; + } + } else if (segtype_is_raid(lp->segtype) || + (lv->status & RAID) || lp->merge_mirror) { + if (!archive(lv->vg)) { + stack; + return ECMD_FAILED; + } + if (!lvconvert_raid(lv, lp)) { + stack; + return ECMD_FAILED; + } + + if (!(failed_pvs = _failed_pv_list(lv->vg))) { + stack; + return ECMD_FAILED; + } + + /* If repairing and using policies, remove missing PVs from VG */ + if (arg_count(cmd, repair_ARG) && arg_count(cmd, use_policies_ARG)) + _remove_missing_empty_pv(lv->vg, failed_pvs); } else if (arg_count(cmd, mirrors_ARG) || arg_count(cmd, splitmirrors_ARG) || (lv->status & MIRRORED)) { @@ -1601,9 +2066,14 @@ static int _lvconvert_single(struct cmd_context *cmd, struct logical_volume *lv, return ECMD_FAILED; } + if (!(failed_pvs = _failed_pv_list(lv->vg))) { + stack; + return ECMD_FAILED; + } + /* If repairing and using policies, remove missing PVs from VG */ if (arg_count(cmd, repair_ARG) && arg_count(cmd, use_policies_ARG)) - _remove_missing_empty_pv(lv->vg, lp->failed_pvs); + _remove_missing_empty_pv(lv->vg, failed_pvs); } return ECMD_PROCESSED; @@ -1619,7 +2089,7 @@ static struct logical_volume *get_vg_lock_and_logical_volume(struct cmd_context { /* * Returns NULL if the requested LV doesn't exist; - * otherwise the caller must free_vg(lv->vg) + * otherwise the caller must release_vg(lv->vg) * - it is also up to the caller to unlock_vg() as needed */ struct volume_group *vg; @@ -1627,13 +2097,13 @@ static struct logical_volume *get_vg_lock_and_logical_volume(struct cmd_context vg = _get_lvconvert_vg(cmd, vg_name, NULL); if (vg_read_error(vg)) { - free_vg(vg); + release_vg(vg); return_NULL; } if (!(lv = _get_lvconvert_lv(cmd, vg, lv_name, NULL, 0))) { log_error("Can't find LV %s in VG %s", lv_name, vg_name); - unlock_and_free_vg(cmd, vg, vg_name); + unlock_and_release_vg(cmd, vg, vg_name); return NULL; } @@ -1645,8 +2115,8 @@ static int poll_logical_volume(struct cmd_context *cmd, struct logical_volume *l { struct lvinfo info; - if (!lv_info(cmd, lv, 0, &info, 1, 0) || !info.exists) { - log_print("Conversion starts after activation."); + if (!lv_info(cmd, lv, 0, &info, 0, 0) || !info.exists) { + log_print_unless_silent("Conversion starts after activation."); return ECMD_PROCESSED; } return lvconvert_poll(cmd, lv, wait_completion ? 0 : 1U); @@ -1677,6 +2147,12 @@ static int lvconvert_single(struct cmd_context *cmd, struct lvconvert_params *lp } else lp->pvh = &lv->vg->pvs; + if (lp->replace_pv_count && + !(lp->replace_pvh = create_pv_list(cmd->mem, lv->vg, + lp->replace_pv_count, + lp->replace_pvs, 0))) + goto_bad; + lp->lv_to_poll = lv; ret = _lvconvert_single(cmd, lv, lp); bad: @@ -1686,7 +2162,7 @@ bad: ret = poll_logical_volume(cmd, lp->lv_to_poll, lp->wait_completion); - free_vg(lv->vg); + release_vg(lv->vg); out: init_ignore_suspended_devices(saved_ignore_suspended_devices); return ret; @@ -1736,7 +2212,7 @@ static int lvconvert_merge_single(struct cmd_context *cmd, struct logical_volume } } - free_vg(refreshed_lv->vg); + release_vg(refreshed_lv->vg); return ret; } diff --git a/tools/lvcreate.c b/tools/lvcreate.c index 13b5926..3ea8f46 100644 --- a/tools/lvcreate.c +++ b/tools/lvcreate.c @@ -1,6 +1,6 @@ /* * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved. - * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved. + * Copyright (C) 2004-2012 Red Hat, Inc. All rights reserved. * * This file is part of LVM2. * @@ -14,7 +14,6 @@ */ #include "tools.h" -#include "lv_alloc.h" #include <fcntl.h> @@ -25,46 +24,122 @@ struct lvcreate_cmdline_params { int pv_count; }; +static int _set_vg_name(struct lvcreate_params *lp, const char *vg_name) +{ + /* Can't do anything */ + if (!vg_name) + return 1; + + /* If VG name already known, ensure this 2nd copy is identical */ + if (lp->vg_name && strcmp(lp->vg_name, vg_name)) { + log_error("Inconsistent volume group names " + "given: \"%s\" and \"%s\"", + lp->vg_name, vg_name); + return 0; + } + lp->vg_name = vg_name; + + return 1; +} + static int _lvcreate_name_params(struct lvcreate_params *lp, struct cmd_context *cmd, int *pargc, char ***pargv) { int argc = *pargc; char **argv = *pargv, *ptr; - char *vg_name; + const char *vg_name; + + lp->pool = arg_str_value(cmd, thinpool_ARG, NULL); + + /* If --thinpool contains VG name, extract it. */ + if (lp->pool && strchr(lp->pool, '/')) { + if (!(lp->vg_name = extract_vgname(cmd, lp->pool))) + return 0; + /* Strip VG from pool */ + if ((ptr = strrchr(lp->pool, (int) '/'))) + lp->pool = ptr + 1; + } lp->lv_name = arg_str_value(cmd, name_ARG, NULL); + /* If --name contains VG name, extract it. */ + if (lp->lv_name && strchr(lp->lv_name, '/')) { + if (!_set_vg_name(lp, extract_vgname(cmd, lp->lv_name))) + return_0; + + /* Strip VG from lv_name */ + if ((ptr = strrchr(lp->lv_name, (int) '/'))) + lp->lv_name = ptr + 1; + } + + /* Need an origin? */ if (lp->snapshot && !arg_count(cmd, virtualsize_ARG)) { + /* argv[0] might be origin or vg/origin */ if (!argc) { log_error("Please specify a logical volume to act as " "the snapshot origin."); return 0; } - lp->origin = argv[0]; - (*pargv)++, (*pargc)--; - if (!(lp->vg_name = extract_vgname(cmd, lp->origin))) { + lp->origin = skip_dev_dir(cmd, argv[0], NULL); + if (strrchr(lp->origin, '/')) { + if (!_set_vg_name(lp, extract_vgname(cmd, lp->origin))) + return_0; + + /* Strip the volume group from the origin */ + if ((ptr = strrchr(lp->origin, (int) '/'))) + lp->origin = ptr + 1; + } + + if (!lp->vg_name && + !_set_vg_name(lp, extract_vgname(cmd, NULL))) + return_0; + + if (!lp->vg_name) { log_error("The origin name should include the " "volume group."); return 0; } - /* Strip the volume group from the origin */ - if ((ptr = strrchr(lp->origin, (int) '/'))) - lp->origin = ptr + 1; + (*pargv)++, (*pargc)--; + } else if (seg_is_thin(lp) && !lp->pool && argc) { + /* argv[0] might be vg or vg/Pool */ + + vg_name = skip_dev_dir(cmd, argv[0], NULL); + if (!strrchr(vg_name, '/')) { + if (!_set_vg_name(lp, vg_name)) + return_0; + } else { + lp->pool = vg_name; + if (!_set_vg_name(lp, extract_vgname(cmd, lp->pool))) + return_0; + + if (!lp->vg_name && + !_set_vg_name(lp, extract_vgname(cmd, NULL))) + return_0; + + if (!lp->vg_name) { + log_error("The pool name should include the " + "volume group."); + return 0; + } + + /* Strip the volume group */ + if ((ptr = strrchr(lp->pool, (int) '/'))) + lp->pool = ptr + 1; + } + (*pargv)++, (*pargc)--; } else { /* - * If VG not on command line, try -n arg and then - * environment. + * If VG not on command line, try environment default. */ if (!argc) { - if (!(lp->vg_name = extract_vgname(cmd, lp->lv_name))) { + if (!lp->vg_name && !(lp->vg_name = extract_vgname(cmd, NULL))) { log_error("Please provide a volume group name"); return 0; } - } else { vg_name = skip_dev_dir(cmd, argv[0], NULL); if (strrchr(vg_name, '/')) { @@ -73,25 +148,9 @@ static int _lvcreate_name_params(struct lvcreate_params *lp, return 0; } - /* - * Ensure lv_name doesn't contain a - * different VG. - */ - if (lp->lv_name && strchr(lp->lv_name, '/')) { - if (!(lp->vg_name = - extract_vgname(cmd, lp->lv_name))) - return 0; - - if (strcmp(lp->vg_name, vg_name)) { - log_error("Inconsistent volume group " - "names " - "given: \"%s\" and \"%s\"", - lp->vg_name, vg_name); - return 0; - } - } + if (!_set_vg_name(lp, vg_name)) + return_0; - lp->vg_name = vg_name; (*pargv)++, (*pargc)--; } } @@ -103,9 +162,6 @@ static int _lvcreate_name_params(struct lvcreate_params *lp, } if (lp->lv_name) { - if ((ptr = strrchr(lp->lv_name, '/'))) - lp->lv_name = ptr + 1; - if (!apply_lvname_restrictions(lp->lv_name)) return_0; @@ -116,12 +172,59 @@ static int _lvcreate_name_params(struct lvcreate_params *lp, } } + if (lp->pool) { + if (!apply_lvname_restrictions(lp->pool)) + return_0; + + if (!validate_name(lp->pool)) { + log_error("Logical volume name \"%s\" is invalid", + lp->pool); + return 0; + } + + if (lp->lv_name && !strcmp(lp->lv_name, lp->pool)) { + log_error("Logical volume name %s and pool name %s must be different.", + lp->lv_name, lp->pool); + return 0; + } + } + + return 1; +} + +/* + * Normal snapshot or thinly-provisioned snapshot? + */ +static int _determine_snapshot_type(struct volume_group *vg, + struct lvcreate_params *lp) +{ + struct lv_list *lvl; + + if (!(lvl = find_lv_in_vg(vg, lp->origin))) { + log_error("Snapshot origin LV %s not found in Volume group %s.", + lp->origin, vg->name); + return 0; + } + + if (!arg_count(vg->cmd, extents_ARG) && !arg_count(vg->cmd, size_ARG)) { + if (!lv_is_thin_volume(lvl->lv)) { + log_error("Please specify either size or extents with snapshots."); + return 0; + } + + lp->thin = 1; + if (!(lp->segtype = get_segtype_from_string(vg->cmd, "thin"))) + return_0; + + lp->pool = first_seg(lvl->lv)->pool_lv->name; + } + return 1; } /* * Update extents parameters based on other parameters which affect the size - * calcuation. + * calculation. * NOTE: We must do this here because of the percent_t typedef and because we * need the vg. */ @@ -131,6 +234,9 @@ static int _update_extents_params(struct volume_group *vg, { uint32_t pv_extent_count; struct logical_volume *origin = NULL; + int changed = 0; + uint32_t size_rest; + uint32_t stripesize_extents; if (lcp->size && !(lp->extents = extents_from_size(vg->cmd, lcp->size, @@ -155,17 +261,17 @@ static int _update_extents_params(struct volume_group *vg, switch(lcp->percent) { case PERCENT_VG: - lp->extents = lp->extents * vg->extent_count / 100; + lp->extents = percent_of_extents(lp->extents, vg->extent_count, 0); break; case PERCENT_FREE: - lp->extents = lp->extents * vg->free_count / 100; + lp->extents = percent_of_extents(lp->extents, vg->free_count, 0); break; case PERCENT_PVS: if (!lcp->pv_count) - lp->extents = lp->extents * vg->extent_count / 100; + lp->extents = percent_of_extents(lp->extents, vg->extent_count, 0); else { pv_extent_count = pv_list_extents_free(lp->pvh); - lp->extents = lp->extents * pv_extent_count / 100; + lp->extents = percent_of_extents(lp->extents, pv_extent_count, 0); } break; case PERCENT_LV: @@ -183,11 +289,63 @@ static int _update_extents_params(struct volume_group *vg, log_error(INTERNAL_ERROR "Couldn't find origin volume."); return 0; } - lp->extents = lp->extents * origin->le_count / 100; + lp->extents = percent_of_extents(lp->extents, origin->le_count, 0); break; case PERCENT_NONE: break; } + + if (!(stripesize_extents = lp->stripe_size / vg->extent_size)) + stripesize_extents = 1; + + if ((lcp->percent != PERCENT_NONE) && lp->stripes && + (size_rest = lp->extents % (lp->stripes * stripesize_extents)) && + (vg->free_count < lp->extents - size_rest + (lp->stripes * stripesize_extents))) { + log_print_unless_silent("Rounding size (%d extents) down to stripe boundary " + "size (%d extents)", lp->extents, + lp->extents - size_rest); + lp->extents = lp->extents - size_rest; + } + + if (lp->create_thin_pool) { + if (!arg_count(vg->cmd, poolmetadatasize_ARG)) { + /* Defaults to nr_pool_blocks * 64b */ + lp->poolmetadatasize = (uint64_t) lp->extents * vg->extent_size / + (uint64_t) (lp->chunk_size * (SECTOR_SIZE / UINT64_C(64))); + + /* Check if we could eventually use bigger chunk size */ + if (!arg_count(vg->cmd, chunksize_ARG)) { + while ((lp->poolmetadatasize > + (DEFAULT_THIN_POOL_OPTIMAL_SIZE / SECTOR_SIZE)) && + (lp->chunk_size < DM_THIN_MAX_DATA_BLOCK_SIZE)) { + lp->chunk_size <<= 1; + lp->poolmetadatasize >>= 1; + changed++; + } + if (changed) + log_verbose("Changed chunksize to %u sectors.", + lp->chunk_size); + } + } + + if (lp->poolmetadatasize > (2 * DEFAULT_THIN_POOL_MAX_METADATA_SIZE)) { + if (arg_count(vg->cmd, poolmetadatasize_ARG)) + log_warn("WARNING: Maximum supported pool metadata size is 16GB."); + lp->poolmetadatasize = 2 * DEFAULT_THIN_POOL_MAX_METADATA_SIZE; + } else if (lp->poolmetadatasize < (2 * DEFAULT_THIN_POOL_MIN_METADATA_SIZE)) { + if (arg_count(vg->cmd, poolmetadatasize_ARG)) + log_warn("WARNING: Minimum supported pool metadata size is 2M."); + lp->poolmetadatasize = 2 * DEFAULT_THIN_POOL_MIN_METADATA_SIZE; + } + + log_verbose("Setting pool metadata size to %" PRIu64 " sectors.", + lp->poolmetadatasize); + + if (!(lp->poolmetadataextents = + extents_from_size(vg->cmd, lp->poolmetadatasize, vg->extent_size))) + return_0; + } + return 1; } @@ -195,13 +353,18 @@ static int _read_size_params(struct lvcreate_params *lp, struct lvcreate_cmdline_params *lcp, struct cmd_context *cmd) { - if (arg_count(cmd, extents_ARG) + arg_count(cmd, size_ARG) != 1) { + if (arg_count(cmd, extents_ARG) && arg_count(cmd, size_ARG)) { log_error("Please specify either size or extents (not both)"); return 0; } + if (!lp->thin && !lp->snapshot && !arg_count(cmd, extents_ARG) && !arg_count(cmd, size_ARG)) { + log_error("Please specify either size or extents"); + return 0; + } + if (arg_count(cmd, extents_ARG)) { - if (arg_sign_value(cmd, extents_ARG, 0) == SIGN_MINUS) { + if (arg_sign_value(cmd, extents_ARG, SIGN_NONE) == SIGN_MINUS) { log_error("Negative number of extents is invalid"); return 0; } @@ -211,7 +374,7 @@ static int _read_size_params(struct lvcreate_params *lp, /* Size returned in kilobyte units; held in sectors */ if (arg_count(cmd, size_ARG)) { - if (arg_sign_value(cmd, size_ARG, 0) == SIGN_MINUS) { + if (arg_sign_value(cmd, size_ARG, SIGN_NONE) == SIGN_MINUS) { log_error("Negative size is invalid"); return 0; } @@ -219,9 +382,29 @@ static int _read_size_params(struct lvcreate_params *lp, lcp->percent = PERCENT_NONE; } + /* If size/extents given with thin, then we are creating a thin pool */ + if (lp->thin && (arg_count(cmd, size_ARG) || arg_count(cmd, extents_ARG))) + lp->create_thin_pool = 1; + + if (arg_count(cmd, poolmetadatasize_ARG)) { + if (!seg_is_thin(lp)) { + log_error("--poolmetadatasize may only be specified when allocating the thin pool."); + return 0; + } + if (arg_sign_value(cmd, poolmetadatasize_ARG, SIGN_NONE) == SIGN_MINUS) { + log_error("Negative poolmetadatasize is invalid."); + return 0; + } + lp->poolmetadatasize = arg_uint64_value(cmd, poolmetadatasize_ARG, UINT64_C(0)); + } + /* Size returned in kilobyte units; held in sectors */ if (arg_count(cmd, virtualsize_ARG)) { - if (arg_sign_value(cmd, virtualsize_ARG, 0) == SIGN_MINUS) { + if (seg_is_thin_pool(lp)) { + log_error("Virtual size in incompatible with thin_pool segment type."); + return 0; + } + if (arg_sign_value(cmd, virtualsize_ARG, SIGN_NONE) == SIGN_MINUS) { log_error("Negative virtual origin size is invalid"); return 0; } @@ -231,6 +414,12 @@ static int _read_size_params(struct lvcreate_params *lp, log_error("Virtual origin size may not be zero"); return 0; } + } else { + /* No virtual size given, so no thin LV to create. */ + if (seg_is_thin_volume(lp) && !(lp->segtype = get_segtype_from_string(cmd, "thin-pool"))) + return_0; + + lp->thin = 0; } return 1; @@ -297,7 +486,7 @@ static int _read_mirror_params(struct lvcreate_params *lp, lp->nosync = arg_is_set(cmd, nosync_ARG); if (arg_count(cmd, regionsize_ARG)) { - if (arg_sign_value(cmd, regionsize_ARG, 0) == SIGN_MINUS) { + if (arg_sign_value(cmd, regionsize_ARG, SIGN_NONE) == SIGN_MINUS) { log_error("Negative regionsize is invalid"); return 0; } @@ -320,15 +509,177 @@ static int _read_mirror_params(struct lvcreate_params *lp, return 1; } +static int _read_raid_params(struct lvcreate_params *lp, + struct cmd_context *cmd) +{ + if (!segtype_is_raid(lp->segtype)) + return 1; + + if (arg_count(cmd, corelog_ARG) || + arg_count(cmd, mirrorlog_ARG)) { + log_error("Log options not applicable to %s segtype", + lp->segtype->name); + return 0; + } + + /* + * get_stripe_params is called before _read_raid_params + * and already sets: + * lp->stripes + * lp->stripe_size + * + * For RAID 4/5/6, these values must be set. + */ + if (!segtype_is_mirrored(lp->segtype) && + (lp->stripes <= lp->segtype->parity_devs)) { + log_error("Number of stripes must be at least %d for %s", + lp->segtype->parity_devs + 1, lp->segtype->name); + return 0; + } + + /* + * RAID types without a mirror component do not take '-m' arg + */ + if (!segtype_is_mirrored(lp->segtype) && + arg_count(cmd, mirrors_ARG)) { + log_error("Mirror argument cannot be used with segment type, %s", + lp->segtype->name); + return 0; + } + + /* + * RAID1 does not take a stripe arg + */ + if ((lp->stripes > 1) && + segtype_is_mirrored(lp->segtype) && + strcmp(lp->segtype->name, "raid10")) { + log_error("Stripe argument cannot be used with segment type, %s", + lp->segtype->name); + return 0; + } + + /* + * _read_mirror_params is called before _read_raid_params + * and already sets: + * lp->nosync + * lp->region_size + * + * But let's ensure that programmers don't reorder + * that by checking and warning if they aren't set. + */ + if (!lp->region_size) { + log_error(INTERNAL_ERROR "region_size not set."); + return 0; + } + + return 1; +} + +static int _read_activation_params(struct lvcreate_params *lp, struct cmd_context *cmd, + struct volume_group *vg) +{ + unsigned pagesize; + + lp->activate = (activation_change_t) + arg_uint_value(cmd, activate_ARG, CHANGE_AY); + + if (lp->activate == CHANGE_AN || lp->activate == CHANGE_ALN) { + if (lp->zero && !seg_is_thin(lp)) { + log_error("--activate n requires --zero n"); + return 0; + } + } else if (lp->activate == CHANGE_AAY) { + if (arg_count(cmd, zero_ARG)) { + log_error("-Z is incompatible with --activate a"); + return 0; + } + lp->zero = 0; + } + + /* + * Read ahead. + */ + lp->read_ahead = arg_uint_value(cmd, readahead_ARG, + cmd->default_settings.read_ahead); + pagesize = lvm_getpagesize() >> SECTOR_SHIFT; + if (lp->read_ahead != DM_READ_AHEAD_AUTO && + lp->read_ahead != DM_READ_AHEAD_NONE && + lp->read_ahead % pagesize) { + if (lp->read_ahead < pagesize) + lp->read_ahead = pagesize; + else + lp->read_ahead = (lp->read_ahead / pagesize) * pagesize; + log_warn("WARNING: Overriding readahead to %u sectors, a multiple " + "of %uK page size.", lp->read_ahead, pagesize >> 1); + } + + /* + * Permissions. + */ + lp->permission = arg_uint_value(cmd, permission_ARG, + LVM_READ | LVM_WRITE); + + /* Must not zero read only volume */ + if (!(lp->permission & LVM_WRITE)) + lp->zero = 0; + + if (arg_count(cmd, major_ARG) > 1) { + log_error("Option -j/--major may not be repeated."); + return 0; + } + + if (arg_count(cmd, minor_ARG) > 1) { + log_error("Option --minor may not be repeated."); + return 0; + } + + lp->minor = arg_int_value(cmd, minor_ARG, -1); + lp->major = arg_int_value(cmd, major_ARG, -1); + + /* Persistent minor */ + if (arg_count(cmd, persistent_ARG)) { + if (lp->create_thin_pool && !lp->thin) { + log_error("--persistent is not permitted when creating a thin pool device."); + return 0; + } + if (!strcmp(arg_str_value(cmd, persistent_ARG, "n"), "y")) { + if (lp->minor == -1) { + log_error("Please specify minor number with " + "--minor when using -My"); + return 0; + } + if (lp->major == -1) { + log_error("Please specify major number with " + "--major when using -My"); + return 0; + } + if (!major_minor_valid(cmd, vg->fid->fmt, lp->major, lp->minor)) + return 0; + } else { + if ((lp->minor != -1) || (lp->major != -1)) { + log_error("--major and --minor incompatible " + "with -Mn"); + return 0; + } + } + } else if (arg_count(cmd, minor_ARG) || arg_count(cmd, major_ARG)) { + log_error("--major and --minor require -My"); + return 0; + } + + return 1; +} + static int _lvcreate_params(struct lvcreate_params *lp, struct lvcreate_cmdline_params *lcp, struct cmd_context *cmd, int argc, char **argv) { int contiguous; - unsigned pagesize; struct arg_value_group_list *current_group; + const char *segtype_str; const char *tag; + unsigned attr = 0; memset(lp, 0, sizeof(*lp)); memset(lcp, 0, sizeof(*lcp)); @@ -337,64 +688,96 @@ static int _lvcreate_params(struct lvcreate_params *lp, /* * Check selected options are compatible and determine segtype */ - lp->segtype = get_segtype_from_string(cmd, arg_str_value(cmd, type_ARG, "striped")); +// FIXME -m0 implies *striped* + if ((arg_count(cmd, thin_ARG) || arg_count(cmd, thinpool_ARG)) && + arg_count(cmd,mirrors_ARG)) { + log_error("--thin,--thinpool and --mirrors are incompatible."); + return 0; + } + +// FIXME -m0 implies *striped* + + /* Set default segtype */ + if (arg_count(cmd, mirrors_ARG)) + /* + * FIXME: Add default setting for when -i and -m arguments + * are both given. We should default to "raid10". + */ + segtype_str = find_config_tree_str(cmd, "global/mirror_segtype_default", DEFAULT_MIRROR_SEGTYPE); + else if (arg_count(cmd, thin_ARG) || arg_count(cmd, thinpool_ARG)) + segtype_str = "thin"; + else + segtype_str = "striped"; + + segtype_str = arg_str_value(cmd, type_ARG, segtype_str); + + if (!(lp->segtype = get_segtype_from_string(cmd, segtype_str))) + return_0; + + if (seg_unknown(lp)) { + log_error("Unable to create LV with unknown segment type %s.", segtype_str); + return 0; + } if (arg_count(cmd, snapshot_ARG) || seg_is_snapshot(lp) || - arg_count(cmd, virtualsize_ARG)) + (!seg_is_thin(lp) && arg_count(cmd, virtualsize_ARG))) lp->snapshot = 1; + if (seg_is_thin_pool(lp)) { + if (lp->snapshot) { + log_error("Snapshots are incompatible with thin_pool segment_type."); + return 0; + } + lp->create_thin_pool = 1; + } + + if (seg_is_thin_volume(lp)) + lp->thin = 1; + lp->mirrors = 1; - /* Default to 2 mirrored areas if --type mirror */ - if (seg_is_mirrored(lp)) + /* Default to 2 mirrored areas if '--type mirror|raid1|raid10' */ + if (segtype_is_mirrored(lp->segtype)) lp->mirrors = 2; if (arg_count(cmd, mirrors_ARG)) { lp->mirrors = arg_uint_value(cmd, mirrors_ARG, 0) + 1; - if (lp->mirrors == 1) - log_print("Redundant mirrors argument: default is 0"); - if (arg_sign_value(cmd, mirrors_ARG, 0) == SIGN_MINUS) { - log_error("Mirrors argument may not be negative"); - return 0; + if (lp->mirrors == 1) { + if (segtype_is_mirrored(lp->segtype)) { + log_error("--mirrors must be at least 1 with segment type %s.", lp->segtype->name); + return 0; + } + log_print_unless_silent("Redundant mirrors argument: default is 0"); } - } - if (lp->snapshot) { - if (arg_count(cmd, zero_ARG)) { - log_error("-Z is incompatible with snapshots"); - return 0; - } - if (arg_sign_value(cmd, chunksize_ARG, 0) == SIGN_MINUS) { - log_error("Negative chunk size is invalid"); - return 0; - } - lp->chunk_size = 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"); + if ((lp->mirrors > 2) && !strcmp(lp->segtype->name, "raid10")) { + /* + * FIXME: When RAID10 is no longer limited to + * 2-way mirror, 'lv_mirror_count()' + * must also change for RAID10. + */ + log_error("RAID10 currently supports " + "only 2-way mirroring (i.e. '-m 1')"); return 0; } - log_verbose("Setting chunksize to %d sectors.", lp->chunk_size); - if (!(lp->segtype = get_segtype_from_string(cmd, "snapshot"))) - return_0; - } else { - if (arg_count(cmd, chunksize_ARG)) { - log_error("-c is only available with snapshots"); + if (arg_sign_value(cmd, mirrors_ARG, SIGN_NONE) == SIGN_MINUS) { + log_error("Mirrors argument may not be negative"); return 0; } } - if (lp->mirrors > 1) { + if (lp->snapshot && arg_count(cmd, zero_ARG)) { + log_error("-Z is incompatible with snapshots"); + return 0; + } + + if (segtype_is_mirrored(lp->segtype) || segtype_is_raid(lp->segtype)) { if (lp->snapshot) { log_error("mirrors and snapshots are currently " "incompatible"); return 0; } - - if (!(lp->segtype = get_segtype_from_string(cmd, "striped"))) - return_0; } else { if (arg_count(cmd, corelog_ARG)) { log_error("--corelog is only available with mirrors"); @@ -413,42 +796,93 @@ static int _lvcreate_params(struct lvcreate_params *lp, } if (activation() && lp->segtype->ops->target_present && - !lp->segtype->ops->target_present(cmd, NULL, NULL)) { + !lp->segtype->ops->target_present(cmd, NULL, &attr)) { log_error("%s: Required device-mapper target(s) not " "detected in your kernel", lp->segtype->name); return 0; + } else if (!strcmp(lp->segtype->name, "raid10")) { + uint32_t maj, min, patchlevel; + if (!target_version("raid", &maj, &min, &patchlevel)) { + log_error("Failed to determine version of RAID kernel module"); + return 0; + } + if ((maj != 1) || (min < 3)) { + log_error("RAID module does not support RAID10"); + return 0; + } } - if (!get_activation_monitoring_mode(cmd, NULL, - &lp->activation_monitoring)) - return_0; - if (!_lvcreate_name_params(lp, cmd, &argc, &argv) || !_read_size_params(lp, lcp, cmd) || !get_stripe_params(cmd, &lp->stripes, &lp->stripe_size) || - !_read_mirror_params(lp, cmd)) + !_read_mirror_params(lp, cmd) || + !_read_raid_params(lp, cmd)) return_0; + if (lp->create_thin_pool) + lp->discards = (thin_discards_t) arg_uint_value(cmd, discards_ARG, THIN_DISCARDS_PASSDOWN); + else if (arg_count(cmd, discards_ARG)) { + log_error("--discards is only available for thin pool creation."); + return 0; + } + + if (lp->snapshot && lp->thin && arg_count(cmd, chunksize_ARG)) + log_warn("WARNING: Ignoring --chunksize with thin snapshots."); + else if (lp->thin && !lp->create_thin_pool) { + if (arg_count(cmd, chunksize_ARG)) + log_warn("WARNING: Ignoring --chunksize when using an existing pool."); + } else if (lp->snapshot || lp->create_thin_pool) { + if (arg_sign_value(cmd, chunksize_ARG, SIGN_NONE) == SIGN_MINUS) { + log_error("Negative chunk size is invalid"); + return 0; + } + if (lp->snapshot) { + lp->chunk_size = 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; + } + } else { + lp->chunk_size = arg_uint_value(cmd, chunksize_ARG, + DM_THIN_MIN_DATA_BLOCK_SIZE); + if ((lp->chunk_size < DM_THIN_MIN_DATA_BLOCK_SIZE) || + (lp->chunk_size > DM_THIN_MAX_DATA_BLOCK_SIZE)) { + log_error("Chunk size must be in the range %uK to %uK", + (DM_THIN_MIN_DATA_BLOCK_SIZE / 2), + (DM_THIN_MAX_DATA_BLOCK_SIZE / 2)); + return 0; + } + if (!(attr & THIN_FEATURE_BLOCK_SIZE) && + (lp->chunk_size & (lp->chunk_size - 1))) { + log_error("Chunk size must be a power of 2 for this thin target version."); + return 0; + } else if (lp->chunk_size & (DM_THIN_MIN_DATA_BLOCK_SIZE - 1)) { + log_error("Chunk size must be multiple of %uK.", + DM_THIN_MIN_DATA_BLOCK_SIZE / 2); + return 0; + } else if ((lp->discards != THIN_DISCARDS_IGNORE) && + (lp->chunk_size & (lp->chunk_size - 1))) { + log_warn("WARNING: Using discards ignore for chunk size non power of 2."); + lp->discards = THIN_DISCARDS_IGNORE; + } + } + log_verbose("Setting chunksize to %u sectors.", lp->chunk_size); + + if (!lp->thin && lp->snapshot && !(lp->segtype = get_segtype_from_string(cmd, "snapshot"))) + return_0; + } else if (arg_count(cmd, chunksize_ARG)) { + log_error("-c is only available with snapshots and thin pools"); + return 0; + } + /* * Should we zero the lv. */ lp->zero = strcmp(arg_str_value(cmd, zero_ARG, (lp->segtype->flags & SEG_CANNOT_BE_ZEROED) ? "n" : "y"), "n"); - /* - * Alloc policy - */ - contiguous = strcmp(arg_str_value(cmd, contiguous_ARG, "n"), "n"); - - lp->alloc = contiguous ? ALLOC_CONTIGUOUS : ALLOC_INHERIT; - - lp->alloc = arg_uint_value(cmd, alloc_ARG, lp->alloc); - - if (contiguous && (lp->alloc != ALLOC_CONTIGUOUS)) { - log_error("Conflicting contiguous and alloc arguments"); - return 0; - } - if (lp->mirrors > DEFAULT_MIRROR_MAX_IMAGES) { log_error("Only up to %d images in mirror supported currently.", DEFAULT_MIRROR_MAX_IMAGES); @@ -456,57 +890,16 @@ static int _lvcreate_params(struct lvcreate_params *lp, } /* - * Read ahead. + * Allocation parameters */ - lp->read_ahead = arg_uint_value(cmd, readahead_ARG, - cmd->default_settings.read_ahead); - pagesize = lvm_getpagesize() >> SECTOR_SHIFT; - if (lp->read_ahead != DM_READ_AHEAD_AUTO && - lp->read_ahead != DM_READ_AHEAD_NONE && - lp->read_ahead % pagesize) { - if (lp->read_ahead < pagesize) - lp->read_ahead = pagesize; - else - lp->read_ahead = (lp->read_ahead / pagesize) * pagesize; - log_warn("WARNING: Overriding readahead to %u sectors, a multiple " - "of %uK page size.", lp->read_ahead, pagesize >> 1); - } - - /* - * Permissions. - */ - lp->permission = arg_uint_value(cmd, permission_ARG, - LVM_READ | LVM_WRITE); + contiguous = strcmp(arg_str_value(cmd, contiguous_ARG, "n"), "n"); - /* Must not zero read only volume */ - if (!(lp->permission & LVM_WRITE)) - lp->zero = 0; + lp->alloc = contiguous ? ALLOC_CONTIGUOUS : ALLOC_INHERIT; - lp->minor = arg_int_value(cmd, minor_ARG, -1); - lp->major = arg_int_value(cmd, major_ARG, -1); + lp->alloc = (alloc_policy_t) arg_uint_value(cmd, alloc_ARG, lp->alloc); - /* Persistent minor */ - if (arg_count(cmd, persistent_ARG)) { - if (!strcmp(arg_str_value(cmd, persistent_ARG, "n"), "y")) { - if (lp->minor == -1) { - log_error("Please specify minor number with " - "--minor when using -My"); - return 0; - } - if (lp->major == -1) { - log_error("Please specify major number with " - "--major when using -My"); - return 0; - } - } else { - if ((lp->minor != -1) || (lp->major != -1)) { - log_error("--major and --minor incompatible " - "with -Mn"); - return 0; - } - } - } else if (arg_count(cmd, minor_ARG) || arg_count(cmd, major_ARG)) { - log_error("--major and --minor require -My"); + if (contiguous && (lp->alloc != ALLOC_CONTIGUOUS)) { + log_error("Conflicting contiguous and alloc arguments"); return 0; } @@ -520,10 +913,10 @@ static int _lvcreate_params(struct lvcreate_params *lp, } if (!str_list_add(cmd->mem, &lp->tags, tag)) { - log_error("Unable to allocate memory for tag %s", tag); + log_error("Unable to allocate memory for tag %s", tag); return 0; } - } + } lcp->pv_count = argc; lcp->pvs = argv; @@ -531,6 +924,144 @@ static int _lvcreate_params(struct lvcreate_params *lp, return 1; } +static int _check_thin_parameters(struct volume_group *vg, struct lvcreate_params *lp, + struct lvcreate_cmdline_params *lcp) +{ + struct lv_list *lvl; + + if (!lp->thin && !lp->create_thin_pool) { + log_error("Please specify device size(s)."); + return 0; + } + + if (lp->thin && !lp->create_thin_pool) { + if (arg_count(vg->cmd, chunksize_ARG)) { + log_error("Only specify --chunksize when originally creating the thin pool."); + return 0; + } + + if (lcp->pv_count) { + log_error("Only specify Physical volumes when allocating the thin pool."); + return 0; + } + + if (arg_count(vg->cmd, alloc_ARG)) { + log_error("--alloc may only be specified when allocating the thin pool."); + return 0; + } + + if (arg_count(vg->cmd, poolmetadatasize_ARG)) { + log_error("--poolmetadatasize may only be specified when allocating the thin pool."); + return 0; + } + + if (arg_count(vg->cmd, stripesize_ARG)) { + log_error("--stripesize may only be specified when allocating the thin pool."); + return 0; + } + + if (arg_count(vg->cmd, stripes_ARG)) { + log_error("--stripes may only be specified when allocating the thin pool."); + return 0; + } + + if (arg_count(vg->cmd, contiguous_ARG)) { + log_error("--contiguous may only be specified when allocating the thin pool."); + return 0; + } + + if (arg_count(vg->cmd, zero_ARG)) { + log_error("--zero may only be specified when allocating the thin pool."); + return 0; + } + } + + if (lp->create_thin_pool && lp->pool) { + if (find_lv_in_vg(vg, lp->pool)) { + log_error("Pool %s already exists in Volume group %s.", lp->pool, vg->name); + return 0; + } + } else if (lp->pool) { + if (!(lvl = find_lv_in_vg(vg, lp->pool))) { + log_error("Pool %s not found in Volume group %s.", lp->pool, vg->name); + return 0; + } + if (!lv_is_thin_pool(lvl->lv)) { + log_error("Logical volume %s is not a thin pool.", lp->pool); + return 0; + } + } else if (!lp->create_thin_pool) { + log_error("Please specify name of existing pool."); + return 0; + } + + if (!lp->thin && lp->lv_name) { + log_error("--name may only be given when creating a new thin Logical volume or snapshot."); + return 0; + } + + if (!lp->thin) { + if (arg_count(vg->cmd, readahead_ARG)) { + log_error("--readhead may only be given when creating a new thin Logical volume or snapshot."); + return 0; + } + if (arg_count(vg->cmd, permission_ARG)) { + log_error("--permission may only be given when creating a new thin Logical volume or snapshot."); + return 0; + } + if (arg_count(vg->cmd, persistent_ARG)) { + log_error("--persistent may only be given when creating a new thin Logical volume or snapshot."); + return 0; + } + } + + return 1; +} + +/* + * Ensure the set of thin parameters extracted from the command line is consistent. + */ +static int _validate_internal_thin_processing(const struct lvcreate_params *lp) +{ + int r = 1; + + /* + The final state should be one of: + thin create_thin_pool snapshot origin pool + 1 1 0 0 y/n - create new pool and a thin LV in it + 1 0 0 0 y - create new thin LV in existing pool + 0 1 0 0 y/n - create new pool only + 1 0 1 1 y - create thin snapshot of existing thin LV + */ + + if (!lp->create_thin_pool && !lp->pool) { + log_error(INTERNAL_ERROR "--thinpool not identified."); + r = 0; + } + + if ((lp->snapshot && !lp->origin) || (!lp->snapshot && lp->origin)) { + log_error(INTERNAL_ERROR "Inconsistent snapshot and origin parameters identified."); + r = 0; + } + + if (lp->snapshot && (lp->create_thin_pool || !lp->thin)) { + log_error(INTERNAL_ERROR "Inconsistent thin and snapshot parameters identified."); + r = 0; + } + + if (!lp->thin && !lp->create_thin_pool) { + log_error(INTERNAL_ERROR "Failed to identify what type of thin target to use."); + r = 0; + } + + if (seg_is_thin_pool(lp) && lp->thin) { + log_error(INTERNAL_ERROR "Thin volume cannot be created with thin pool segment type."); + r = 0; + } + + return r; +} + int lvcreate(struct cmd_context *cmd, int argc, char **argv) { int r = ECMD_PROCESSED; @@ -538,29 +1069,62 @@ int lvcreate(struct cmd_context *cmd, int argc, char **argv) struct lvcreate_cmdline_params lcp; struct volume_group *vg; - memset(&lp, 0, sizeof(lp)); - if (!_lvcreate_params(&lp, &lcp, cmd, argc, argv)) return EINVALID_CMD_LINE; log_verbose("Finding volume group \"%s\"", lp.vg_name); vg = vg_read_for_update(cmd, lp.vg_name, NULL, 0); if (vg_read_error(vg)) { - free_vg(vg); + release_vg(vg); stack; return ECMD_FAILED; } + if (lp.snapshot && lp.origin && !_determine_snapshot_type(vg, &lp)) { + r = ECMD_FAILED; + goto_out; + } + + if (seg_is_thin(&lp) && !_check_thin_parameters(vg, &lp, &lcp)) { + r = ECMD_FAILED; + goto_out; + } + + /* + * Check activation parameters to support inactive thin snapshot creation + * FIXME: anything else needs to be moved past _determine_snapshot_type()? + */ + if (!_read_activation_params(&lp, cmd, vg)) { + r = ECMD_FAILED; + goto_out; + } + if (!_update_extents_params(vg, &lp, &lcp)) { r = ECMD_FAILED; goto_out; } + if (seg_is_thin(&lp) && !_validate_internal_thin_processing(&lp)) { + r = ECMD_FAILED; + goto_out; + } + + if (lp.create_thin_pool) + log_verbose("Making thin pool %s in VG %s using segtype %s", + lp.pool ? : "with generated name", lp.vg_name, lp.segtype->name); + + if (lp.thin) + log_verbose("Making thin LV %s in pool %s in VG %s%s%s using segtype %s", + lp.lv_name ? : "with generated name", + lp.pool ? : "with generated name", lp.vg_name, + lp.snapshot ? " as snapshot of " : "", + lp.snapshot ? lp.origin : "", lp.segtype->name); + if (!lv_create_single(vg, &lp)) { stack; r = ECMD_FAILED; } out: - unlock_and_free_vg(cmd, vg, lp.vg_name); + unlock_and_release_vg(cmd, vg, lp.vg_name); return r; } diff --git a/tools/lvm.c b/tools/lvm.c index cec9f80..425549a 100644 --- a/tools/lvm.c +++ b/tools/lvm.c @@ -60,7 +60,7 @@ static char *_list_args(const char *text, int state) /* Initialise if this is a new completion attempt */ if (!state) { char *s = rl_line_buffer; - int j = 0; + int j; match_no = 0; com = NULL; @@ -167,7 +167,6 @@ static void _read_history(struct cmd_context *cmd) stifle_history(find_config_tree_int(cmd, "shell/history_size", DEFAULT_MAX_HISTORY)); - } static void _write_history(void) @@ -200,6 +199,7 @@ int lvm_shell(struct cmd_context *cmd, struct cmdline_context *cmdline) /* EOF */ if (!input) { + /* readline sends prompt to stdout */ printf("\n"); break; } diff --git a/tools/lvm2cmd.h b/tools/lvm2cmd.h index 66651d8..bf93787 100644 --- a/tools/lvm2cmd.h +++ b/tools/lvm2cmd.h @@ -52,6 +52,12 @@ void lvm2_log_fn(lvm2_log_fn_t log_fn); void *lvm2_init(void); /* + * Disable any dmeventd calls that the library may otherwise do. Useful to avoid + * recursive calls from dmeventd to itself. + */ +void lvm2_disable_dmeventd_monitoring(void *handle); + +/* * Set log level (as above) if using built-in logging function. * Default is LVM2_LOG_PRINT. Use LVM2_LOG_SUPPRESS to suppress output. */ diff --git a/tools/lvmcmdlib.c b/tools/lvmcmdlib.c index 6b2bc9d..6b641c2 100644 --- a/tools/lvmcmdlib.c +++ b/tools/lvmcmdlib.c @@ -17,13 +17,10 @@ #include "lvm2cmdline.h" #include "label.h" #include "memlock.h" -#include "lvm-version.h" #include "lvm2cmd.h" #include <signal.h> -#include <syslog.h> -#include <libgen.h> #include <sys/stat.h> #include <time.h> #include <sys/resource.h> @@ -97,6 +94,10 @@ int lvm2_run(void *handle, const char *cmdline) return ret; } +void lvm2_disable_dmeventd_monitoring(void *handle) { + init_dmeventd_monitor(DMEVENTD_MONITOR_IGNORE); +} + void lvm2_log_level(void *handle, int level) { struct cmd_context *cmd = (struct cmd_context *) handle; diff --git a/tools/lvmcmdline.c b/tools/lvmcmdline.c index 836dbaa..39a8c58 100644 --- a/tools/lvmcmdline.c +++ b/tools/lvmcmdline.c @@ -1,6 +1,6 @@ /* * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved. - * Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved. + * Copyright (C) 2004-2012 Red Hat, Inc. All rights reserved. * * This file is part of LVM2. * @@ -19,15 +19,14 @@ #include "lvm-version.h" #include "stub.h" -#include "lvm2cmd.h" #include "last-path-component.h" #include <signal.h> -#include <syslog.h> -#include <libgen.h> #include <sys/stat.h> #include <time.h> #include <sys/resource.h> +#include <dirent.h> +#include <paths.h> #ifdef HAVE_GETOPTLONG # include <getopt.h> @@ -42,11 +41,6 @@ extern char *optarg; # define OPTIND_INIT 1 #endif -#ifdef UDEV_SYNC_SUPPORT -# define LIBUDEV_I_KNOW_THE_API_IS_SUBJECT_TO_CHANGE -# include <libudev.h> -#endif - /* * Table of valid switches */ @@ -94,9 +88,29 @@ const char *grouped_arg_str_value(const struct arg_values *av, int a, const char return grouped_arg_count(av, a) ? av[a].value : def; } +int32_t grouped_arg_int_value(const struct arg_values *av, int a, const int32_t def) +{ + return grouped_arg_count(av, a) ? av[a].i_value : def; +} + +int32_t first_grouped_arg_int_value(struct cmd_context *cmd, int a, const int32_t def) +{ + struct arg_value_group_list *current_group; + struct arg_values *av; + + dm_list_iterate_items(current_group, &cmd->arg_value_groups) { + av = current_group->arg_values; + if (grouped_arg_count(av, a)) + return grouped_arg_int_value(av, a, def); + } + + return def; +} + int32_t arg_int_value(struct cmd_context *cmd, int a, const int32_t def) { - return arg_count(cmd, a) ? cmd->arg_values[a].i_value : def; + return (_cmdline.arg_props[a].flags & ARG_GROUPABLE) ? + first_grouped_arg_int_value(cmd, a, def) : (arg_count(cmd, a) ? cmd->arg_values[a].i_value : def); } uint32_t arg_uint_value(struct cmd_context *cmd, int a, const uint32_t def) @@ -157,7 +171,7 @@ int yes_no_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_value return 1; } -int yes_no_excl_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_values *av) +int activation_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_values *av) { av->sign = SIGN_NONE; av->percent = PERCENT_NONE; @@ -173,6 +187,12 @@ int yes_no_excl_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_ av->ui_value = CHANGE_AY; } + else if (!strcmp(av->value, "a") || !strcmp(av->value, "ay") || + !strcmp(av->value, "ya")) { + av->i_value = CHANGE_AAY; + av->ui_value = CHANGE_AAY; + } + else if (!strcmp(av->value, "n") || !strcmp(av->value, "en") || !strcmp(av->value, "ne")) { av->i_value = CHANGE_AN; @@ -195,6 +215,19 @@ int yes_no_excl_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_ return 1; } +int discards_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_values *av) +{ + thin_discards_t discards; + + if (!get_pool_discards(av->value, &discards)) + return_0; + + av->i_value = discards; + av->ui_value = discards; + + return 1; +} + int metadatatype_arg(struct cmd_context *cmd, struct arg_values *av) { return get_format_by_name(cmd, av->value) ? 1 : 0; @@ -279,8 +312,8 @@ static int _size_arg(struct cmd_context *cmd __attribute__((unused)), struct arg if (i < 0) { return 0; } else if (i == 7) { - /* sectors */ - v = v; + /* v is already in sectors */ + ; } else if (i == 6) { /* bytes */ v_tmp = (uint64_t) v; @@ -373,38 +406,6 @@ int int_arg_with_sign_and_percent(struct cmd_context *cmd __attribute__((unused) return 1; } -int minor_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_values *av) -{ - char *ptr; - - if (!_get_int_arg(av, &ptr) || (*ptr) || (av->sign == SIGN_MINUS)) - return 0; - - if (av->i_value > 255) { - log_error("Minor number outside range 0-255"); - return 0; - } - - return 1; -} - -int major_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_values *av) -{ - char *ptr; - - if (!_get_int_arg(av, &ptr) || (*ptr) || (av->sign == SIGN_MINUS)) - return 0; - - if (av->i_value > 255) { - log_error("Major number outside range 0-255"); - return 0; - } - - /* FIXME Also Check against /proc/devices */ - - return 1; -} - int string_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_values *av __attribute__((unused))) { @@ -506,6 +507,35 @@ int metadatacopies_arg(struct cmd_context *cmd, struct arg_values *av) return int_arg(cmd, av); } +int major_minor_valid(const struct cmd_context *cmd, const struct format_type *fmt, + int32_t major, int32_t minor) +{ + if (!strncmp(cmd->kernel_vsn, "2.4.", 4) || + (fmt->features & FMT_RESTRICTED_LVIDS)) { + if (major < 0 || major > 255) { + log_error("Major number outside range 0-255"); + return 0; + } + if (minor < 0 || minor > 255) { + log_error("Minor number outside range 0-255"); + return 0; + } + } else { + /* 12 bits for major number */ + if (major < 0 || major > 4095) { + log_error("Major number outside range 0-4095"); + return 0; + } + /* 20 bits for minor number */ + if (minor < 0 || minor > 1048575) { + log_error("Minor number outside range 0-1048575"); + return 0; + } + } + + return 1; +} + static void __alloc(int size) { if (!(_cmdline.commands = dm_realloc(_cmdline.commands, sizeof(*_cmdline.commands) * size))) { @@ -646,7 +676,7 @@ static void _add_getopt_arg(int arg, char **ptr, struct option **o) if (a->short_arg) (*o)->val = a->short_arg; else - (*o)->val = arg; + (*o)->val = arg + 128; (*o)++; } #endif @@ -667,7 +697,7 @@ static int _find_arg(struct command *com, int opt) * the_args. */ if ((a->short_arg && (opt == a->short_arg)) || - (!a->short_arg && (opt == arg))) + (!a->short_arg && (opt == (arg + 128)))) return arg; } @@ -819,6 +849,9 @@ static int _get_settings(struct cmd_context *cmd) cmd->current_settings.verbose = 0; } + if (arg_count(cmd, quiet_ARG) > 1) + cmd->current_settings.silent = 1; + if (arg_count(cmd, test_ARG)) cmd->current_settings.test = arg_count(cmd, test_ARG); @@ -835,7 +868,7 @@ static int _get_settings(struct cmd_context *cmd) if (arg_count(cmd, partial_ARG)) { cmd->partial_activation = 1; - log_print("Partial mode. Incomplete logical volumes will be processed."); + log_warn("PARTIAL MODE. Incomplete logical volumes will be processed."); } if (arg_count(cmd, ignorelockingfailure_ARG) || arg_count(cmd, sysinit_ARG)) @@ -843,6 +876,9 @@ static int _get_settings(struct cmd_context *cmd) else init_ignorelockingfailure(0); + if (!arg_count(cmd, sysinit_ARG)) + lvmetad_warning(); + if (arg_count(cmd, nosuffix_ARG)) cmd->current_settings.suffix = 0; @@ -865,14 +901,17 @@ static int _get_settings(struct cmd_context *cmd) } else init_trust_cache(0); - if (arg_count(cmd, noudevsync_ARG)) + if (arg_count(cmd, noudevsync_ARG)) { cmd->current_settings.udev_sync = 0; + cmd->current_settings.udev_fallback = 1; + } /* Handle synonyms */ if (!_merge_synonym(cmd, resizable_ARG, resizeable_ARG) || !_merge_synonym(cmd, allocation_ARG, allocatable_ARG) || !_merge_synonym(cmd, allocation_ARG, resizeable_ARG) || - !_merge_synonym(cmd, virtualoriginsize_ARG, virtualsize_ARG)) + !_merge_synonym(cmd, virtualoriginsize_ARG, virtualsize_ARG) || + !_merge_synonym(cmd, available_ARG, activate_ARG)) return EINVALID_CMD_LINE; if ((!strncmp(cmd->command->name, "pv", 2) && @@ -935,9 +974,11 @@ static void _apply_settings(struct cmd_context *cmd) { init_debug(cmd->current_settings.debug); init_verbose(cmd->current_settings.verbose + VERBOSE_BASE_LEVEL); + init_silent(cmd->current_settings.silent); init_test(cmd->current_settings.test); init_full_scan_done(0); init_mirror_in_sync(0); + init_dmeventd_monitor(DEFAULT_DMEVENTD_MONITOR); init_msg_prefix(cmd->default_settings.msg_prefix); init_cmd_name(cmd->default_settings.cmd_name); @@ -953,47 +994,6 @@ static void _apply_settings(struct cmd_context *cmd) cmd->handles_missing_pvs = 0; } -static int _set_udev_checking(struct cmd_context *cmd) -{ -#ifdef UDEV_SYNC_SUPPORT - struct udev *udev; - const char *udev_dev_dir; - size_t udev_dev_dir_len; - int dirs_diff; - - if (!(udev = udev_new()) || - !(udev_dev_dir = udev_get_dev_path(udev)) || - !*udev_dev_dir) { - log_error("Could not get udev dev path."); - return 0; - } - udev_dev_dir_len = strlen(udev_dev_dir); - - /* There's always a slash at the end of dev_dir. But check udev_dev_dir! */ - if (udev_dev_dir[udev_dev_dir_len - 1] != '/') - dirs_diff = strncmp(cmd->dev_dir, udev_dev_dir, - udev_dev_dir_len); - else - dirs_diff = strcmp(cmd->dev_dir, udev_dev_dir); - - if (dirs_diff) { - log_debug("The path %s used for creating device nodes and " - "symlinks that is set in the configuration differs " - "from the path %s that is used by udev. All warnings " - "about udev not working correctly while processing " - "particular nodes and symlinks will be suppressed. " - "These nodes and symlinks will be managed in each " - "directory separately.", - cmd->dev_dir, udev_dev_dir); - dm_udev_set_checking(0); - init_udev_checking(0); - } - - udev_unref(udev); -#endif - return 1; -} - static const char *_copy_command_line(struct cmd_context *cmd, int argc, char **argv) { int i, space; @@ -1040,6 +1040,8 @@ int lvm_run_command(struct cmd_context *cmd, int argc, char **argv) { int ret = 0; int locking_type; + int monitoring; + struct dm_config_tree *old_cft; init_error_message_produced(0); @@ -1064,8 +1066,7 @@ int lvm_run_command(struct cmd_context *cmd, int argc, char **argv) set_cmd_name(cmd->command->name); if (arg_count(cmd, config_ARG)) - if (override_config_tree_from_string(cmd, - arg_str_value(cmd, config_ARG, ""))) { + if (override_config_tree_from_string(cmd, arg_str_value(cmd, config_ARG, ""))) { ret = EINVALID_CMD_LINE; goto_out; } @@ -1073,10 +1074,9 @@ int lvm_run_command(struct cmd_context *cmd, int argc, char **argv) if (arg_count(cmd, config_ARG) || !cmd->config_valid || config_files_changed(cmd)) { /* Reinitialise various settings inc. logging, filters */ if (!refresh_toolcontext(cmd)) { - if (cmd->cft_override) { - destroy_config_tree(cmd->cft_override); - cmd->cft_override = NULL; - } + old_cft = remove_overridden_config_tree(cmd); + if (old_cft) + dm_config_destroy(old_cft); log_error("Updated config file invalid. Aborting."); return ECMD_FAILED; } @@ -1086,17 +1086,21 @@ int lvm_run_command(struct cmd_context *cmd, int argc, char **argv) goto_out; _apply_settings(cmd); + if (!get_activation_monitoring_mode(cmd, &monitoring)) + goto_out; + init_dmeventd_monitor(monitoring); + log_debug("Processing: %s", cmd->cmd_line); #ifdef O_DIRECT_SUPPORT log_debug("O_DIRECT will be used"); #endif - if (!_set_udev_checking(cmd)) - goto_out; - - if ((ret = _process_common_commands(cmd))) - goto_out; + if ((ret = _process_common_commands(cmd))) { + if (ret != ECMD_PROCESSED) + stack; + goto out; + } if (cmd->metadata_read_only && !(cmd->command->flags & PERMITTED_READ_ONLY)) { @@ -1125,9 +1129,8 @@ int lvm_run_command(struct cmd_context *cmd, int argc, char **argv) lvmcache_destroy(cmd, 1); } - if (cmd->cft_override) { - destroy_config_tree(cmd->cft_override); - cmd->cft_override = NULL; + if ((old_cft = remove_overridden_config_tree(cmd))) { + dm_config_destroy(old_cft); /* Move this? */ if (!refresh_toolcontext(cmd)) stack; @@ -1187,6 +1190,39 @@ int lvm_split(char *str, int *argc, char **argv, int max) return *argc; } +/* Make sure we have always valid filedescriptors 0,1,2 */ +static int _check_standard_fds(void) +{ + int err = is_valid_fd(STDERR_FILENO); + + if (!is_valid_fd(STDIN_FILENO) && + !(stdin = fopen(_PATH_DEVNULL, "r"))) { + if (err) + perror("stdin stream open"); + else + printf("stdin stream open: %s\n", + strerror(errno)); + return 0; + } + + if (!is_valid_fd(STDOUT_FILENO) && + !(stdout = fopen(_PATH_DEVNULL, "w"))) { + if (err) + perror("stdout stream open"); + /* else no stdout */ + return 0; + } + + if (!is_valid_fd(STDERR_FILENO) && + !(stderr = fopen(_PATH_DEVNULL, "w"))) { + printf("stderr stream open: %s\n", + strerror(errno)); + return 0; + } + + return 1; +} + static const char *_get_cmdline(pid_t pid) { static char _proc_cmdline[32]; @@ -1195,7 +1231,7 @@ static const char *_get_cmdline(pid_t pid) snprintf(buf, sizeof(buf), DEFAULT_PROC_DIR "/%u/cmdline", pid); /* FIXME Use generic read code. */ - if ((fd = open(buf, O_RDONLY)) > 0) { + if ((fd = open(buf, O_RDONLY)) >= 0) { if ((n = read(fd, _proc_cmdline, sizeof(_proc_cmdline) - 1)) < 0) { log_sys_error("read", buf); n = 0; @@ -1232,7 +1268,7 @@ static void _close_descriptor(int fd, unsigned suppress_warnings, const char *filename; /* Ignore bad file descriptors */ - if (fcntl(fd, F_GETFD) == -1 && errno == EBADF) + if (!is_valid_fd(fd)) return; if (!suppress_warnings) @@ -1254,39 +1290,70 @@ static void _close_descriptor(int fd, unsigned suppress_warnings, fprintf(stderr, " Parent PID %" PRIpid_t ": %s\n", ppid, parent_cmdline); } -static void _close_stray_fds(const char *command) +static int _close_stray_fds(const char *command) { +#ifndef VALGRIND_POOL struct rlimit rlim; int fd; unsigned suppress_warnings = 0; pid_t ppid = getppid(); const char *parent_cmdline = _get_cmdline(ppid); - - if (getrlimit(RLIMIT_NOFILE, &rlim) < 0) { - fprintf(stderr, "getrlimit(RLIMIT_NOFILE) failed: %s\n", - strerror(errno)); - return; - } + static const char _fd_dir[] = DEFAULT_PROC_DIR "/self/fd"; + struct dirent *dirent; + DIR *d; if (getenv("LVM_SUPPRESS_FD_WARNINGS")) suppress_warnings = 1; - for (fd = 3; fd < rlim.rlim_cur; fd++) - _close_descriptor(fd, suppress_warnings, command, ppid, - parent_cmdline); + if (!(d = opendir(_fd_dir))) { + if (errno != ENOENT) { + log_sys_error("opendir", _fd_dir); + return 0; /* broken system */ + } + + /* Path does not exist, use the old way */ + if (getrlimit(RLIMIT_NOFILE, &rlim) < 0) { + log_sys_error("getrlimit", "RLIMIT_NOFILE"); + return 1; + } + + for (fd = 3; fd < (int)rlim.rlim_cur; fd++) + _close_descriptor(fd, suppress_warnings, command, ppid, + parent_cmdline); + return 1; + } + + while ((dirent = readdir(d))) { + fd = atoi(dirent->d_name); + if (fd > 2 && fd != dirfd(d)) + _close_descriptor(fd, suppress_warnings, + command, ppid, parent_cmdline); + } + + if (closedir(d)) + log_sys_error("closedir", _fd_dir); +#endif + + return 1; } struct cmd_context *init_lvm(void) { struct cmd_context *cmd; - if (!(cmd = create_toolcontext(0, NULL))) + if (!udev_init_library_context()) + stack; + + if (!(cmd = create_toolcontext(0, NULL, 1, 0))) { + udev_fin_library_context(); return_NULL; + } _cmdline.arg_props = &_arg_props[0]; if (stored_errno()) { destroy_toolcontext(cmd); + udev_fin_library_context(); return_NULL; } @@ -1311,6 +1378,7 @@ void lvm_fin(struct cmd_context *cmd) { _fin_commands(); destroy_toolcontext(cmd); + udev_fin_library_context(); } static int _run_script(struct cmd_context *cmd, int argc, char **argv) @@ -1421,14 +1489,21 @@ int lvm2_main(int argc, char **argv) strcmp(base, "initrd-lvm")) alias = 1; - _close_stray_fds(base); + if (!_check_standard_fds()) + return -1; + + if (!_close_stray_fds(base)) + return -1; if (is_static() && strcmp(base, "lvm.static") && path_exists(LVM_SHARED_PATH) && !getenv("LVM_DID_EXEC")) { - setenv("LVM_DID_EXEC", base, 1); - execvp(LVM_SHARED_PATH, argv); - unsetenv("LVM_DID_EXEC"); + if (setenv("LVM_DID_EXEC", base, 1)) + log_sys_error("setenv", "LVM_DID_EXEC"); + if (execvp(LVM_SHARED_PATH, argv) == -1) + log_sys_error("execvp", "LVM_SHARED_PATH"); + if (unsetenv("LVM_DID_EXEC")) + log_sys_error("unsetenv", "LVM_DID_EXEC"); } /* "version" command is simple enough so it doesn't need any complex init */ diff --git a/tools/lvmdiskscan.c b/tools/lvmdiskscan.c index 9127c15..df3f072 100644 --- a/tools/lvmdiskscan.c +++ b/tools/lvmdiskscan.c @@ -72,15 +72,18 @@ static int _check_device(struct cmd_context *cmd, struct device *dev) char buffer; uint64_t size; - if (!dev_open(dev)) { - return 0; - } + if (!dev_open_readonly(dev)) + return_0; + if (!dev_read(dev, UINT64_C(0), (size_t) 1, &buffer)) { - dev_close(dev); + stack; + if (!dev_close(dev)) + stack; return 0; } if (!dev_get_size(dev, &size)) { log_error("Couldn't get size of \"%s\"", dev_name(dev)); + size = 0; } _print(cmd, dev, size, NULL); _count(dev, &disks_found, &parts_found); diff --git a/tools/lvremove.c b/tools/lvremove.c index 0252149..54094b1 100644 --- a/tools/lvremove.c +++ b/tools/lvremove.c @@ -26,7 +26,7 @@ static int lvremove_single(struct cmd_context *cmd, struct logical_volume *lv, if (lv_is_cow(lv) && lv_is_virtual_origin(origin = origin_from_cow(lv))) lv = origin; - if (!lv_remove_with_dependencies(cmd, lv, arg_count(cmd, force_ARG), 0)) { + if (!lv_remove_with_dependencies(cmd, lv, (force_t) arg_count(cmd, force_ARG), 0)) { stack; return ECMD_FAILED; } diff --git a/tools/lvrename.c b/tools/lvrename.c index db47a8b..3dc21dc 100644 --- a/tools/lvrename.c +++ b/tools/lvrename.c @@ -104,7 +104,7 @@ int lvrename(struct cmd_context *cmd, int argc, char **argv) log_verbose("Checking for existing volume group \"%s\"", vg_name); vg = vg_read_for_update(cmd, vg_name, NULL, 0); if (vg_read_error(vg)) { - free_vg(vg); + release_vg(vg); stack; return ECMD_FAILED; } @@ -115,14 +115,29 @@ int lvrename(struct cmd_context *cmd, int argc, char **argv) goto error; } + if (lvl->lv->status & (RAID_IMAGE | RAID_META)) { + log_error("Cannot rename a RAID %s directly", + (lvl->lv->status & RAID_IMAGE) ? "image" : + "metadata area"); + r = ECMD_FAILED; + goto error; + } + + if (lv_is_raid_with_tracking(lvl->lv)) { + log_error("Cannot rename %s while it is tracking a split image", + lvl->lv->name); + r = ECMD_FAILED; + goto error; + } + if (!lv_rename(cmd, lvl->lv, lv_name_new)) goto error; - log_print("Renamed \"%s\" to \"%s\" in volume group \"%s\"", - lv_name_old, lv_name_new, vg_name); + log_print_unless_silent("Renamed \"%s\" to \"%s\" in volume group \"%s\"", + lv_name_old, lv_name_new, vg_name); r = ECMD_PROCESSED; error: - unlock_and_free_vg(cmd, vg, vg_name); + unlock_and_release_vg(cmd, vg, vg_name); return r; } diff --git a/tools/lvresize.c b/tools/lvresize.c index c970f3f..4c9580d 100644 --- a/tools/lvresize.c +++ b/tools/lvresize.c @@ -1,6 +1,6 @@ /* * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved. - * Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved. + * Copyright (C) 2004-2012 Red Hat, Inc. All rights reserved. * * This file is part of LVM2. * @@ -50,12 +50,12 @@ static int _validate_stripesize(struct cmd_context *cmd, const struct volume_group *vg, struct lvresize_params *lp) { - if (arg_sign_value(cmd, stripesize_ARG, 0) == SIGN_MINUS) { + if (arg_sign_value(cmd, stripesize_ARG, SIGN_NONE) == SIGN_MINUS) { log_error("Stripesize may not be negative."); return 0; } - if (arg_uint_value(cmd, stripesize_ARG, 0) > STRIPE_SIZE_LIMIT * 2) { + if (arg_uint64_value(cmd, stripesize_ARG, 0) > STRIPE_SIZE_LIMIT * 2) { log_error("Stripe size cannot be larger than %s", display_size(cmd, (uint64_t) STRIPE_SIZE_LIMIT)); return 0; @@ -63,7 +63,7 @@ static int _validate_stripesize(struct cmd_context *cmd, if (!(vg->fid->fmt->features & FMT_SEGMENTS)) log_warn("Varied stripesize not supported. Ignoring."); - else if (arg_uint_value(cmd, stripesize_ARG, 0) > vg->extent_size * 2) { + else if (arg_uint_value(cmd, stripesize_ARG, 0) > (uint64_t) vg->extent_size * 2) { log_error("Reducing stripe size %s to maximum, " "physical extent size %s", display_size(cmd, @@ -86,9 +86,7 @@ static int _request_confirmation(struct cmd_context *cmd, const struct logical_volume *lv, const struct lvresize_params *lp) { - struct lvinfo info; - - memset(&info, 0, sizeof(info)); + struct lvinfo info = { 0 }; if (!lv_info(cmd, lv, 0, &info, 1, 0) && driver_version(NULL, 0)) { log_error("lv_info failed: aborting"); @@ -159,6 +157,9 @@ static int _fsadm_cmd(struct cmd_context *cmd, argv[i++] = (fcmd == FSADM_CMD_RESIZE) ? "resize" : "check"; + if (status) + *status = -1; + if (dm_snprintf(lv_path, PATH_MAX, "%s%s/%s", cmd->dev_dir, lp->vg_name, lp->lv_name) < 0) { log_error("Couldn't create LV path for %s", lp->lv_name); @@ -179,7 +180,7 @@ static int _fsadm_cmd(struct cmd_context *cmd, argv[i] = NULL; - return exec_cmd(cmd, argv, status); + return exec_cmd(cmd, argv, status, 1); } static int _lvresize_params(struct cmd_context *cmd, int argc, char **argv, @@ -285,37 +286,93 @@ static int _adjust_policy_params(struct cmd_context *cmd, percent_t percent; int policy_threshold, policy_amount; - policy_threshold = - find_config_tree_int(cmd, "activation/snapshot_autoextend_threshold", - DEFAULT_SNAPSHOT_AUTOEXTEND_THRESHOLD) * PERCENT_1; - policy_amount = - find_config_tree_int(cmd, "activation/snapshot_autoextend_percent", - DEFAULT_SNAPSHOT_AUTOEXTEND_PERCENT); + if (lv_is_thin_pool(lv)) { + policy_threshold = + find_config_tree_int(cmd, "activation/thin_pool_autoextend_threshold", + DEFAULT_THIN_POOL_AUTOEXTEND_THRESHOLD) * PERCENT_1; + policy_amount = + find_config_tree_int(cmd, "activation/thin_pool_autoextend_percent", + DEFAULT_THIN_POOL_AUTOEXTEND_PERCENT); + if (!policy_amount && policy_threshold < PERCENT_100) + return 0; + } else { + policy_threshold = + find_config_tree_int(cmd, "activation/snapshot_autoextend_threshold", + DEFAULT_SNAPSHOT_AUTOEXTEND_THRESHOLD) * PERCENT_1; + policy_amount = + find_config_tree_int(cmd, "activation/snapshot_autoextend_percent", + DEFAULT_SNAPSHOT_AUTOEXTEND_PERCENT); + } if (policy_threshold >= PERCENT_100) return 1; /* nothing to do */ - if (!lv_snapshot_percent(lv, &percent)) - return_0; + if (lv_is_thin_pool(lv)) { + if (!lv_thin_pool_percent(lv, 1, &percent)) + return_0; + if (percent > policy_threshold) { + /* FIXME: metadata resize support missing */ + log_error("Resize for %s/%s is not yet supported.", + lp->vg_name, lp->lv_name); + return ECMD_FAILED; + } - if (!(PERCENT_0 < percent && percent < PERCENT_100) || percent <= policy_threshold) - return 1; /* nothing to do */ + if (!lv_thin_pool_percent(lv, 0, &percent)) + return_0; + if (!(PERCENT_0 < percent && percent <= PERCENT_100) || + percent <= policy_threshold) + return 1; /* nothing to do */ + } else { + if (!lv_snapshot_percent(lv, &percent)) + return_0; + if (!(PERCENT_0 < percent && percent < PERCENT_100) || percent <= policy_threshold) + return 1; /* nothing to do */ + } lp->extents = policy_amount; + return 1; } +static uint32_t lvseg_get_stripes(struct lv_segment *seg, uint32_t *stripesize) +{ + uint32_t s; + struct lv_segment *seg_mirr; + + /* If segment mirrored, check if images are striped */ + if (seg_is_mirrored(seg)) + for (s = 0; s < seg->area_count; s++) { + if (seg_type(seg, s) != AREA_LV) + continue; + seg_mirr = first_seg(seg_lv(seg, s)); + + if (seg_is_striped(seg_mirr)) { + seg = seg_mirr; + break; + } + } + + + if (seg_is_striped(seg)) { + *stripesize = seg->stripe_size; + return seg->area_count; + } + + *stripesize = 0; + return 0; +} + static int _lvresize(struct cmd_context *cmd, struct volume_group *vg, struct lvresize_params *lp) { struct logical_volume *lv; struct lvinfo info; - uint32_t stripesize_extents = 0; - uint32_t seg_stripes = 0, seg_stripesize = 0, seg_size = 0; + uint32_t stripesize_extents; + uint32_t seg_stripes = 0, seg_stripesize = 0, seg_size; uint32_t seg_mirrors = 0; - uint32_t extents_used = 0; + uint32_t extents_used; uint32_t size_rest; - uint32_t pv_extent_count = 0; + uint32_t pv_extent_count; alloc_policy_t alloc; struct logical_volume *lock_lv; struct lv_list *lvl; @@ -333,6 +390,19 @@ static int _lvresize(struct cmd_context *cmd, struct volume_group *vg, return ECMD_FAILED; } + if (lvl->lv->status & (RAID_IMAGE | RAID_META)) { + log_error("Cannot resize a RAID %s directly", + (lvl->lv->status & RAID_IMAGE) ? "image" : + "metadata area"); + return ECMD_FAILED; + } + + if (lv_is_raid_with_tracking(lvl->lv)) { + log_error("Cannot resize %s while it is tracking a split image", + lvl->lv->name); + return ECMD_FAILED; + } + if (arg_count(cmd, stripes_ARG)) { if (vg->fid->fmt->features & FMT_SEGMENTS) lp->stripes = arg_uint_value(cmd, stripes_ARG, 1); @@ -345,7 +415,7 @@ static int _lvresize(struct cmd_context *cmd, struct volume_group *vg, lp->mirrors = arg_uint_value(cmd, mirrors_ARG, 1) + 1; else log_warn("Mirrors not supported. Ignoring."); - if (arg_sign_value(cmd, mirrors_ARG, 0) == SIGN_MINUS) { + if (arg_sign_value(cmd, mirrors_ARG, SIGN_NONE) == SIGN_MINUS) { log_error("Mirrors argument may not be negative"); return EINVALID_CMD_LINE; } @@ -358,11 +428,13 @@ static int _lvresize(struct cmd_context *cmd, struct volume_group *vg, lv = lvl->lv; if (use_policy) { - if (!lv_is_cow(lv)) { - log_error("Can't use policy-based resize for non-snapshot volumes."); + if (!lv_is_cow(lv) && + !lv_is_thin_pool(lv)) { + log_error("Policy-based resize is supported only for snapshot and thin pool volumes."); return ECMD_FAILED; } - _adjust_policy_params(cmd, lv, lp); + if (!_adjust_policy_params(cmd, lv, lp)) + return ECMD_FAILED; } if (!lv_is_visible(lv)) { @@ -380,8 +452,14 @@ static int _lvresize(struct cmd_context *cmd, struct volume_group *vg, return ECMD_FAILED; } - alloc = arg_uint_value(cmd, alloc_ARG, lv->alloc); + alloc = (alloc_policy_t) arg_uint_value(cmd, alloc_ARG, lv->alloc); + /* + * First adjust to an exact multiple of extent size. + * When extending by a relative amount we round that amount up. + * When reducing by a relative amount we remove at most that amount. + * When changing to an absolute size, we round that size up. + */ if (lp->size) { if (lp->size % vg->extent_size) { if (lp->sign == SIGN_MINUS) @@ -390,8 +468,8 @@ static int _lvresize(struct cmd_context *cmd, struct volume_group *vg, lp->size += vg->extent_size - (lp->size % vg->extent_size); - log_print("Rounding up size to full physical extent %s", - display_size(cmd, (uint64_t) lp->size)); + log_print_unless_silent("Rounding size to boundary between physical extents: %s", + display_size(cmd, lp->size)); } lp->extents = lp->size / vg->extent_size; @@ -405,34 +483,46 @@ static int _lvresize(struct cmd_context *cmd, struct volume_group *vg, switch(lp->percent) { case PERCENT_VG: - lp->extents = lp->extents * vg->extent_count / 100; + lp->extents = percent_of_extents(lp->extents, vg->extent_count, + (lp->sign != SIGN_MINUS)); break; case PERCENT_FREE: - lp->extents = lp->extents * vg->free_count / 100; + lp->extents = percent_of_extents(lp->extents, vg->free_count, + (lp->sign != SIGN_MINUS)); break; case PERCENT_LV: - lp->extents = lp->extents * lv->le_count / 100; + lp->extents = percent_of_extents(lp->extents, lv->le_count, + (lp->sign != SIGN_MINUS)); break; case PERCENT_PVS: if (lp->argc) { pv_extent_count = pv_list_extents_free(pvh); - lp->extents = lp->extents * pv_extent_count / 100; + lp->extents = percent_of_extents(lp->extents, pv_extent_count, + (lp->sign != SIGN_MINUS)); } else - lp->extents = lp->extents * vg->extent_count / 100; + lp->extents = percent_of_extents(lp->extents, vg->extent_count, + (lp->sign != SIGN_MINUS)); break; case PERCENT_ORIGIN: if (!lv_is_cow(lv)) { log_error("Specified LV does not have an origin LV."); return EINVALID_CMD_LINE; } - lp->extents = lp->extents * origin_from_cow(lv)->le_count / 100; + lp->extents = percent_of_extents(lp->extents, origin_from_cow(lv)->le_count, + (lp->sign != SIGN_MINUS)); break; case PERCENT_NONE: break; } - if (lp->sign == SIGN_PLUS) + if (lp->sign == SIGN_PLUS) { + if (lp->extents >= (MAX_EXTENT_COUNT - lv->le_count)) { + log_error("Unable to extend %s by %u extents, exceeds limit (%u).", + lp->lv_name, lv->le_count, MAX_EXTENT_COUNT); + return EINVALID_CMD_LINE; + } lp->extents += lv->le_count; + } if (lp->sign == SIGN_MINUS) { if (lp->extents >= lv->le_count) { @@ -476,6 +566,13 @@ static int _lvresize(struct cmd_context *cmd, struct volume_group *vg, /* If extending, find mirrors of last segment */ if ((lp->extents > lv->le_count)) { + /* + * Has the user specified that they would like the additional + * extents of a mirror not to have an initial sync? + */ + if (seg_is_mirrored(first_seg(lv)) && arg_count(cmd, nosync_ARG)) + lv->status |= LV_NOTSYNCED; + dm_list_iterate_back_items(mirr_seg, &lv->segments) { if (seg_is_mirrored(mirr_seg)) seg_mirrors = lv_mirror_count(mirr_seg->lv); @@ -483,9 +580,10 @@ static int _lvresize(struct cmd_context *cmd, struct volume_group *vg, seg_mirrors = 0; break; } + if (!arg_count(cmd, mirrors_ARG) && seg_mirrors) { - log_print("Extending %" PRIu32 " mirror images.", - seg_mirrors); + log_print_unless_silent("Extending %" PRIu32 " mirror images.", + seg_mirrors); lp->mirrors = seg_mirrors; } if ((arg_count(cmd, mirrors_ARG) || seg_mirrors) && @@ -493,18 +591,30 @@ static int _lvresize(struct cmd_context *cmd, struct volume_group *vg, log_error("Cannot vary number of mirrors in LV yet."); return EINVALID_CMD_LINE; } + + if (seg_mirrors && !strcmp(mirr_seg->segtype->name, "raid10")) { + lp->stripes = mirr_seg->area_count / seg_mirrors; + lp->stripe_size = mirr_seg->stripe_size; + } } /* If extending, find stripes, stripesize & size of last segment */ if ((lp->extents > lv->le_count) && - !(lp->stripes == 1 || (lp->stripes > 1 && lp->stripe_size))) { + !(lp->stripes == 1 || (lp->stripes > 1 && lp->stripe_size)) && + strcmp(mirr_seg->segtype->name, "raid10")) { /* FIXME Don't assume mirror seg will always be AREA_LV */ - dm_list_iterate_items(seg, seg_mirrors ? &seg_lv(mirr_seg, 0)->segments : &lv->segments) { - if (!seg_is_striped(seg)) + /* FIXME We will need to support resize for metadata LV as well, + * and data LV could be any type (i.e. mirror)) */ + dm_list_iterate_items(seg, seg_mirrors ? &seg_lv(mirr_seg, 0)->segments : + lv_is_thin_pool(lv) ? &seg_lv(first_seg(lv), 0)->segments : &lv->segments) { + /* Allow through "striped" and RAID 4/5/6/10 */ + if (!seg_is_striped(seg) && + (!seg_is_raid(seg) || seg_is_mirrored(seg)) && + strcmp(seg->segtype->name, "raid10")) continue; sz = seg->stripe_size; - str = seg->area_count; + str = seg->area_count - lp->segtype->parity_devs; if ((seg_stripesize && seg_stripesize != sz && sz && !lp->stripe_size) || @@ -520,19 +630,24 @@ static int _lvresize(struct cmd_context *cmd, struct volume_group *vg, if (!lp->stripes) lp->stripes = seg_stripes; + else if (seg_is_raid(first_seg(lv)) && + (lp->stripes != seg_stripes)) { + log_error("Unable to extend \"%s\" segment type with different number of stripes.", first_seg(lv)->segtype->ops->name(first_seg(lv))); + return ECMD_FAILED; + } if (!lp->stripe_size && lp->stripes > 1) { if (seg_stripesize) { - log_print("Using stripesize of last segment %s", - display_size(cmd, (uint64_t) seg_stripesize)); + log_print_unless_silent("Using stripesize of last segment %s", + display_size(cmd, (uint64_t) seg_stripesize)); lp->stripe_size = seg_stripesize; } else { lp->stripe_size = find_config_tree_int(cmd, "metadata/stripesize", DEFAULT_STRIPESIZE) * 2; - log_print("Using default stripesize %s", - display_size(cmd, (uint64_t) lp->stripe_size)); + log_print_unless_silent("Using default stripesize %s", + display_size(cmd, (uint64_t) lp->stripe_size)); } } } @@ -548,10 +663,8 @@ static int _lvresize(struct cmd_context *cmd, struct volume_group *vg, dm_list_iterate_items(seg, &lv->segments) { seg_extents = seg->len; - if (seg_is_striped(seg)) { - seg_stripesize = seg->stripe_size; - seg_stripes = seg->area_count; - } + /* Check for underlying stripe sizes */ + seg_stripes = lvseg_get_stripes(seg, &seg_stripesize); if (seg_is_mirrored(seg)) seg_mirrors = lv_mirror_count(seg->lv); @@ -575,22 +688,35 @@ static int _lvresize(struct cmd_context *cmd, struct volume_group *vg, return EINVALID_CMD_LINE; } - if ((lp->stripes > 1)) { - if (!(stripesize_extents = lp->stripe_size / vg->extent_size)) - stripesize_extents = 1; - - if ((size_rest = seg_size % (lp->stripes * stripesize_extents))) { - log_print("Rounding size (%d extents) down to stripe " - "boundary size for segment (%d extents)", - lp->extents, lp->extents - size_rest); - lp->extents = lp->extents - size_rest; - } - + if (lp->stripes > 1) { if (lp->stripe_size < STRIPE_SIZE_MIN) { log_error("Invalid stripe size %s", display_size(cmd, (uint64_t) lp->stripe_size)); return EINVALID_CMD_LINE; } + + if (!(stripesize_extents = lp->stripe_size / vg->extent_size)) + stripesize_extents = 1; + + size_rest = seg_size % (lp->stripes * stripesize_extents); + /* Round toward the original size. */ + if (size_rest && + ((lp->extents < lv->le_count) || + !lp->percent || + (vg->free_count >= (lp->extents - lv->le_count - size_rest + + (lp->stripes * stripesize_extents))))) { + log_print_unless_silent("Rounding size (%d extents) up to stripe " + "boundary size for segment (%d extents)", + lp->extents, lp->extents - size_rest + + (lp->stripes * stripesize_extents)); + lp->extents = lp->extents - size_rest + + (lp->stripes * stripesize_extents); + } else if (size_rest) { + log_print_unless_silent("Rounding size (%d extents) down to stripe " + "boundary size for segment (%d extents)", + lp->extents, lp->extents - size_rest); + lp->extents = lp->extents - size_rest; + } } if (lp->extents < lv->le_count) { @@ -609,6 +735,15 @@ static int _lvresize(struct cmd_context *cmd, struct volume_group *vg, return EINVALID_CMD_LINE; } lp->resize = LV_EXTEND; + } else if (lp->extents == lv->le_count) { + if (use_policy) + return ECMD_PROCESSED; /* Nothing to do. */ + if (!lp->resizefs) { + log_error("New size (%d extents) matches existing size " + "(%d extents)", lp->extents, lv->le_count); + return EINVALID_CMD_LINE; + } + lp->resize = LV_EXTEND; } if (lv_is_origin(lv)) { @@ -618,8 +753,6 @@ static int _lvresize(struct cmd_context *cmd, struct volume_group *vg, return ECMD_FAILED; } - memset(&info, 0, sizeof(info)); - if (lv_info(cmd, lv, 0, &info, 0, 0) && info.exists) { log_error("Snapshot origin volumes can be resized " "only while inactive: try lvchange -an"); @@ -627,6 +760,18 @@ static int _lvresize(struct cmd_context *cmd, struct volume_group *vg, } } + if (lv_is_thin_pool(lv)) { + if (lp->resize == LV_REDUCE) { + log_error("Thin pool volumes cannot be reduced in size yet."); + return ECMD_FAILED; + } + + if (lp->resizefs) { + log_warn("Thin pool volumes do not have filesystem."); + lp->resizefs = 0; + } + } + if ((lp->resize == LV_REDUCE) && lp->argc) log_warn("Ignoring PVs on command line when reducing"); @@ -641,15 +786,15 @@ static int _lvresize(struct cmd_context *cmd, struct volume_group *vg, if (!lp->nofsck && !_fsadm_cmd(cmd, vg, lp, FSADM_CMD_CHECK, &status)) { if (status != FSADM_CHECK_FAILS_FOR_MOUNTED) { - stack; + log_error("Filesystem check failed."); return ECMD_FAILED; } - /* some filesystems supports online resize */ + /* some filesystems supports online resize */ } if ((lp->resize == LV_REDUCE) && !_fsadm_cmd(cmd, vg, lp, FSADM_CMD_RESIZE, NULL)) { - stack; + log_error("Filesystem resize failed."); return ECMD_FAILED; } } @@ -659,10 +804,10 @@ static int _lvresize(struct cmd_context *cmd, struct volume_group *vg, return ECMD_FAILED; } - log_print("%sing logical volume %s to %s", - (lp->resize == LV_REDUCE) ? "Reduc" : "Extend", - lp->lv_name, - display_size(cmd, (uint64_t) lp->extents * vg->extent_size)); + log_print_unless_silent("%sing logical volume %s to %s", + (lp->resize == LV_REDUCE) ? "Reduc" : "Extend", + lp->lv_name, + display_size(cmd, (uint64_t) lp->extents * vg->extent_size)); if (lp->resize == LV_REDUCE) { if (!lv_reduce(lv, lv->le_count - lp->extents)) { @@ -670,10 +815,11 @@ static int _lvresize(struct cmd_context *cmd, struct volume_group *vg, return ECMD_FAILED; } } else if ((lp->extents > lv->le_count) && /* Ensure we extend */ - !lv_extend(lv, lp->segtype, lp->stripes, - lp->stripe_size, lp->mirrors, - lp->extents - lv->le_count, - NULL, 0u, 0u, pvh, alloc)) { + !lv_extend(lv, lp->segtype, + lp->stripes, lp->stripe_size, + lp->mirrors, first_seg(lv)->region_size, + lp->extents - lv->le_count, NULL, + pvh, alloc)) { stack; return ECMD_FAILED; } @@ -713,7 +859,21 @@ static int _lvresize(struct cmd_context *cmd, struct volume_group *vg, backup(vg); - log_print("Logical volume %s successfully resized", lp->lv_name); + /* + * Update lvm pool metadata (drop messages) if the pool has been + * resumed and do a pool active/deactivate in other case. + * + * Note: Active thin pool can be waiting for resize. + * + * FIXME: Activate only when thin volume is active + */ + if (lv_is_thin_pool(lv) && + !update_pool_lv(lv, !lv_is_active(lv))) { + stack; + return ECMD_FAILED; + } + + log_print_unless_silent("Logical volume %s successfully resized", lp->lv_name); if (lp->resizefs && (lp->resize == LV_EXTEND) && !_fsadm_cmd(cmd, vg, lp, FSADM_CMD_RESIZE, NULL)) { @@ -726,19 +886,17 @@ static int _lvresize(struct cmd_context *cmd, struct volume_group *vg, int lvresize(struct cmd_context *cmd, int argc, char **argv) { - struct lvresize_params lp; + struct lvresize_params lp = { 0 }; struct volume_group *vg; int r; - memset(&lp, 0, sizeof(lp)); - if (!_lvresize_params(cmd, argc, argv, &lp)) return EINVALID_CMD_LINE; log_verbose("Finding volume group %s", lp.vg_name); vg = vg_read_for_update(cmd, lp.vg_name, NULL, 0); if (vg_read_error(vg)) { - free_vg(vg); + release_vg(vg); stack; return ECMD_FAILED; } @@ -746,7 +904,7 @@ int lvresize(struct cmd_context *cmd, int argc, char **argv) if (!(r = _lvresize(cmd, vg, &lp))) stack; - unlock_and_free_vg(cmd, vg, lp.vg_name); + unlock_and_release_vg(cmd, vg, lp.vg_name); return r; } diff --git a/tools/lvscan.c b/tools/lvscan.c index 638d2f5..636ac45 100644 --- a/tools/lvscan.c +++ b/tools/lvscan.c @@ -19,8 +19,6 @@ static int lvscan_single(struct cmd_context *cmd, struct logical_volume *lv, void *handle __attribute__((unused))) { struct lvinfo info; - int lv_total = 0; - uint64_t lv_capacity_total = 0; int inkernel, snap_active = 1; struct lv_segment *snap_seg = NULL; percent_t snap_percent; /* fused, fsize; */ @@ -30,7 +28,7 @@ static int lvscan_single(struct cmd_context *cmd, struct logical_volume *lv, if (!arg_count(cmd, all_ARG) && !lv_is_visible(lv)) return ECMD_PROCESSED; - inkernel = lv_info(cmd, lv, 0, &info, 1, 0) && info.exists; + inkernel = lv_info(cmd, lv, 0, &info, 0, 0) && info.exists; if (lv_is_origin(lv)) { dm_list_iterate_items_gen(snap_seg, &lv->snapshot_segs, origin_list) { @@ -61,14 +59,10 @@ static int lvscan_single(struct cmd_context *cmd, struct logical_volume *lv, else snapshot_str = " "; - log_print("%s%s '%s%s/%s' [%s] %s", active_str, snapshot_str, - cmd->dev_dir, lv->vg->name, lv->name, - display_size(cmd, lv->size), - get_alloc_string(lv->alloc)); - - lv_total++; - - lv_capacity_total += lv->size; + log_print_unless_silent("%s%s '%s%s/%s' [%s] %s", active_str, snapshot_str, + cmd->dev_dir, lv->vg->name, lv->name, + display_size(cmd, lv->size), + get_alloc_string(lv->alloc)); return ECMD_PROCESSED; } diff --git a/tools/polldaemon.c b/tools/polldaemon.c index 50579ba..a9138a1 100644 --- a/tools/polldaemon.c +++ b/tools/polldaemon.c @@ -32,6 +32,8 @@ static void _sigchld_handler(int sig __attribute__((unused))) */ static int _become_daemon(struct cmd_context *cmd) { + static const char devnull[] = "/dev/null"; + int null_fd; pid_t pid; struct sigaction act = { {_sigchld_handler}, @@ -42,6 +44,8 @@ static int _become_daemon(struct cmd_context *cmd) sigaction(SIGCHLD, &act, NULL); + sync_local_dev_names(cmd); /* Flush ops and reset dm cookie */ + if ((pid = fork()) == -1) { log_error("fork failed: %s", strerror(errno)); return -1; @@ -55,16 +59,33 @@ static int _become_daemon(struct cmd_context *cmd) if (setsid() == -1) log_error("Background process failed to setsid: %s", strerror(errno)); - init_verbose(VERBOSE_BASE_LEVEL); - close(STDIN_FILENO); - close(STDOUT_FILENO); - close(STDERR_FILENO); + /* For poll debugging it's best to disable for compilation */ +#if 1 + if ((null_fd = open(devnull, O_RDWR)) == -1) { + log_sys_error("open", devnull); + _exit(ECMD_FAILED); + } + + if ((dup2(null_fd, STDIN_FILENO) < 0) || /* reopen stdin */ + (dup2(null_fd, STDOUT_FILENO) < 0) || /* reopen stdout */ + (dup2(null_fd, STDERR_FILENO) < 0)) { /* reopen stderr */ + log_sys_error("dup2", "redirect"); + (void) close(null_fd); + _exit(ECMD_FAILED); + } + + if (null_fd > STDERR_FILENO) + (void) close(null_fd); + init_verbose(VERBOSE_BASE_LEVEL); +#endif strncpy(*cmd->argv, "(lvm2)", strlen(*cmd->argv)); reset_locking(); - lvmcache_init(); + if (!lvmcache_init()) + /* FIXME Clean up properly here */ + _exit(ECMD_FAILED); dev_close_all(); return 1; @@ -87,8 +108,8 @@ progress_t poll_mirror_progress(struct cmd_context *cmd, overall_percent = copy_percent(lv); if (parms->progress_display) - log_print("%s: %s: %.1f%%", name, parms->progress_title, - percent_to_float(overall_percent)); + log_print_unless_silent("%s: %s: %.1f%%", name, parms->progress_title, + percent_to_float(overall_percent)); else log_verbose("%s: %s: %.1f%%", name, parms->progress_title, percent_to_float(overall_percent)); @@ -144,7 +165,7 @@ static int _check_lv_status(struct cmd_context *cmd, /* Finished? Or progress to next segment? */ if (progress == PROGRESS_FINISHED_ALL) { if (!parms->poll_fns->finish_copy(cmd, vg, lv, lvs_changed)) - return 0; + return_0; } else { if (parms->poll_fns->update_metadata && !parms->poll_fns->update_metadata(cmd, vg, lv, lvs_changed, 0)) { @@ -183,26 +204,34 @@ static int _wait_for_single_lv(struct cmd_context *cmd, const char *name, const /* Locks the (possibly renamed) VG again */ vg = parms->poll_fns->get_copy_vg(cmd, name, uuid); if (vg_read_error(vg)) { - free_vg(vg); + release_vg(vg); log_error("ABORTING: Can't reread VG for %s", name); /* What more could we do here? */ return 0; } - if (!(lv = parms->poll_fns->get_copy_lv(cmd, vg, name, uuid, - parms->lv_type))) { + lv = parms->poll_fns->get_copy_lv(cmd, vg, name, uuid, parms->lv_type); + + if (!lv && parms->lv_type == PVMOVE) { + log_print_unless_silent("%s: no pvmove in progress - already finished or aborted.", + name); + unlock_and_release_vg(cmd, vg, vg->name); + return 1; + } + + if (!lv) { log_error("ABORTING: Can't find LV in %s for %s", vg->name, name); - unlock_and_free_vg(cmd, vg, vg->name); + unlock_and_release_vg(cmd, vg, vg->name); return 0; } if (!_check_lv_status(cmd, vg, lv, name, parms, &finished)) { - unlock_and_free_vg(cmd, vg, vg->name); - return 0; + unlock_and_release_vg(cmd, vg, vg->name); + return_0; } - unlock_and_free_vg(cmd, vg, vg->name); + unlock_and_release_vg(cmd, vg, vg->name); /* * FIXME Sleeping after testing, while preferred, also works around @@ -232,6 +261,11 @@ static int _poll_vg(struct cmd_context *cmd, const char *vgname, const char *name; int finished; + if (!parms) { + log_error(INTERNAL_ERROR "Handle is undefined."); + return ECMD_FAILED; + } + dm_list_iterate_items(lvl, &vg->lvs) { lv = lvl->lv; if (!(lv->status & parms->lv_type)) @@ -271,7 +305,7 @@ static void _poll_for_all_vgs(struct cmd_context *cmd, */ int poll_daemon(struct cmd_context *cmd, const char *name, const char *uuid, unsigned background, - uint32_t lv_type, struct poll_functions *poll_fns, + uint64_t lv_type, struct poll_functions *poll_fns, const char *progress_title) { struct daemon_parms parms; @@ -281,7 +315,7 @@ int poll_daemon(struct cmd_context *cmd, const char *name, const char *uuid, parms.aborting = arg_is_set(cmd, abort_ARG); parms.background = background; - interval_sign = arg_sign_value(cmd, interval_ARG, 0); + interval_sign = arg_sign_value(cmd, interval_ARG, SIGN_NONE); if (interval_sign == SIGN_MINUS) log_error("Argument to --interval cannot be negative"); parms.interval = arg_uint_value(cmd, interval_ARG, diff --git a/tools/polldaemon.h b/tools/polldaemon.h index 8ebbb25..78ea783 100644 --- a/tools/polldaemon.h +++ b/tools/polldaemon.h @@ -36,7 +36,7 @@ struct poll_functions { struct volume_group *vg, const char *name, const char *uuid, - uint32_t lv_type); + uint64_t lv_type); progress_t (*poll_progress)(struct cmd_context *cmd, struct logical_volume *lv, const char *name, @@ -59,13 +59,13 @@ struct daemon_parms { unsigned outstanding_count; unsigned progress_display; const char *progress_title; - uint32_t lv_type; + uint64_t lv_type; struct poll_functions *poll_fns; }; int poll_daemon(struct cmd_context *cmd, const char *name, const char *uuid, unsigned background, - uint32_t lv_type, struct poll_functions *poll_fns, + uint64_t lv_type, struct poll_functions *poll_fns, const char *progress_title); progress_t poll_mirror_progress(struct cmd_context *cmd, diff --git a/tools/pvchange.c b/tools/pvchange.c index 28e71b8..70f1e62 100644 --- a/tools/pvchange.c +++ b/tools/pvchange.c @@ -15,8 +15,6 @@ #include "tools.h" -/* FIXME Locking. PVs in VG. */ - static int _pvchange_single(struct cmd_context *cmd, struct volume_group *vg, struct physical_volume *pv, void *handle __attribute__((unused))) @@ -27,21 +25,14 @@ static int _pvchange_single(struct cmd_context *cmd, struct volume_group *vg, uint64_t orig_pe_start; const char *pv_name = pv_dev_name(pv); - const char *tag = NULL; const char *orig_vg_name; char uuid[64] __attribute__((aligned(8))); int allocatable = 0; - int tagarg = 0; - int r = 0; + int tagargs = 0; int mda_ignore = 0; - struct arg_value_group_list *current_group; - - if (arg_count(cmd, addtag_ARG)) - tagarg = addtag_ARG; - else if (arg_count(cmd, deltag_ARG)) - tagarg = deltag_ARG; + tagargs = arg_count(cmd, addtag_ARG) + arg_count(cmd, deltag_ARG); if (arg_count(cmd, allocatable_ARG)) allocatable = !strcmp(arg_str_value(cmd, allocatable_ARG, "n"), @@ -52,20 +43,20 @@ static int _pvchange_single(struct cmd_context *cmd, struct volume_group *vg, /* If in a VG, must change using volume group. */ if (!is_orphan(pv)) { - if (tagarg && !(vg->fid->fmt->features & FMT_TAGS)) { + if (tagargs && !(vg->fid->fmt->features & FMT_TAGS)) { log_error("Volume group containing %s does not " "support tags", pv_name); - goto out; + return 0; } if (arg_count(cmd, uuid_ARG) && lvs_in_vg_activated(vg)) { log_error("Volume group containing %s has active " "logical volumes", pv_name); - goto out; + return 0; } if (!archive(vg)) - goto out; + return 0; } else { - if (tagarg) { + if (tagargs) { log_error("Can't change tag on Physical Volume %s not " "in volume group", pv_name); return 0; @@ -77,22 +68,20 @@ static int _pvchange_single(struct cmd_context *cmd, struct volume_group *vg, !(pv->fmt->features & FMT_ORPHAN_ALLOCATABLE)) { log_error("Allocatability not supported by orphan " "%s format PV %s", pv->fmt->name, pv_name); - goto out; + return 0; } /* change allocatability for a PV */ if (allocatable && (pv_status(pv) & ALLOCATABLE_PV)) { log_error("Physical volume \"%s\" is already " "allocatable", pv_name); - r = 1; - goto out; + return 1; } if (!allocatable && !(pv_status(pv) & ALLOCATABLE_PV)) { log_error("Physical volume \"%s\" is already " "unallocatable", pv_name); - r = 1; - goto out; + return 1; } if (allocatable) { @@ -104,50 +93,41 @@ static int _pvchange_single(struct cmd_context *cmd, struct volume_group *vg, "allocatable", pv_name); pv->status &= ~ALLOCATABLE_PV; } - } else if (tagarg) { - /* tag or deltag */ + } - dm_list_iterate_items(current_group, &cmd->arg_value_groups) { - if (!grouped_arg_is_set(current_group->arg_values, tagarg)) - continue; + if (tagargs) { + /* tag or deltag */ + if (arg_count(cmd, addtag_ARG) && !change_tag(cmd, NULL, NULL, pv, addtag_ARG)) + return_0; - if (!(tag = grouped_arg_str_value(current_group->arg_values, tagarg, NULL))) { - log_error("Failed to get tag"); - goto out; - } + if (arg_count(cmd, deltag_ARG) && !change_tag(cmd, NULL, NULL, pv, deltag_ARG)) + return_0; + + } - if ((tagarg == addtag_ARG)) { - if (!str_list_add(cmd->mem, &pv->tags, tag)) { - log_error("Failed to add tag %s to physical " - "volume %s", tag, pv_name); - goto out; - } - } else if (!str_list_del(&pv->tags, tag)) { - log_error("Failed to remove tag %s from " - "physical volume" "%s", tag, pv_name); - goto out; - } - } - } else if (arg_count(cmd, metadataignore_ARG)) { + if (arg_count(cmd, metadataignore_ARG)) { if ((vg_mda_copies(vg) != VGMETADATACOPIES_UNMANAGED) && (arg_count(cmd, force_ARG) == PROMPT) && yes_no_prompt("Override preferred number of copies " "of VG %s metadata? [y/n]: ", pv_vg_name(pv)) == 'n') { log_error("Physical volume %s not changed", pv_name); - goto out; + return 0; } if (!pv_change_metadataignore(pv, mda_ignore)) - goto out; - } else { + return_0; + } + + if (arg_count(cmd, uuid_ARG)) { /* --uuid: Change PV ID randomly */ + memcpy(&pv->old_id, &pv->id, sizeof(pv->id)); if (!id_create(&pv->id)) { log_error("Failed to generate new random UUID for %s.", pv_name); - goto out; + return 0; } if (!id_write_format(&pv->id, uuid, sizeof(uuid))) - goto_out; + return 0; log_verbose("Changing uuid of %s to %s.", pv_name, uuid); if (!is_orphan(pv)) { orig_vg_name = pv_vg_name(pv); @@ -160,10 +140,10 @@ static int _pvchange_single(struct cmd_context *cmd, struct volume_group *vg, pv->vg_name = pv->fmt->orphan_vg_name; pv->pe_alloc_count = 0; - if (!(pv_write(cmd, pv, NULL, INT64_C(-1)))) { + if (!(pv_write(cmd, pv, 0))) { log_error("pv_write with new uuid failed " "for %s.", pv_name); - goto out; + return 0; } pv->vg_name = orig_vg_name; pv->pe_alloc_count = orig_pe_alloc_count; @@ -179,20 +159,18 @@ static int _pvchange_single(struct cmd_context *cmd, struct volume_group *vg, if (!vg_write(vg) || !vg_commit(vg)) { log_error("Failed to store physical volume \"%s\" in " "volume group \"%s\"", pv_name, vg->name); - goto out; + return 0; } backup(vg); - } else if (!(pv_write(cmd, pv, NULL, INT64_C(-1)))) { + } else if (!(pv_write(cmd, pv, 0))) { log_error("Failed to store physical volume \"%s\"", pv_name); - goto out; + return 0; } - log_print("Physical volume \"%s\" changed", pv_name); - r = 1; -out: - return r; + log_print_unless_silent("Physical volume \"%s\" changed", pv_name); + return 1; } int pvchange(struct cmd_context *cmd, int argc, char **argv) @@ -209,11 +187,11 @@ int pvchange(struct cmd_context *cmd, int argc, char **argv) struct dm_list *vgnames; struct str_list *sll; - if (arg_count(cmd, allocatable_ARG) + arg_is_set(cmd, addtag_ARG) + + if (!(arg_count(cmd, allocatable_ARG) + arg_is_set(cmd, addtag_ARG) + arg_is_set(cmd, deltag_ARG) + arg_count(cmd, uuid_ARG) + - arg_count(cmd, metadataignore_ARG) != 1) { - log_error("Please give exactly one option of -x, -uuid, " - "--addtag or --deltag"); + arg_count(cmd, metadataignore_ARG))) { + log_error("Please give one or more of -x, -uuid, " + "--addtag, --deltag or --metadataignore"); return EINVALID_CMD_LINE; } @@ -231,7 +209,7 @@ int pvchange(struct cmd_context *cmd, int argc, char **argv) log_verbose("Using physical volume(s) on command line"); for (; opt < argc; opt++) { pv_name = argv[opt]; - unescape_colons_and_at_signs(pv_name, NULL, NULL); + dm_unescape_colons_and_at_signs(pv_name, NULL, NULL); vg_name = find_vgname_from_pvname(cmd, pv_name); if (!vg_name) { log_error("Failed to read physical volume %s", @@ -240,7 +218,7 @@ int pvchange(struct cmd_context *cmd, int argc, char **argv) } vg = vg_read_for_update(cmd, vg_name, NULL, 0); if (vg_read_error(vg)) { - free_vg(vg); + release_vg(vg); stack; continue; } @@ -254,7 +232,7 @@ int pvchange(struct cmd_context *cmd, int argc, char **argv) total++; done += _pvchange_single(cmd, vg, pvl->pv, NULL); - unlock_and_free_vg(cmd, vg, vg_name); + unlock_and_release_vg(cmd, vg, vg_name); } } else { log_verbose("Scanning for physical volume names"); @@ -275,7 +253,7 @@ int pvchange(struct cmd_context *cmd, int argc, char **argv) dm_list_iterate_items(sll, vgnames) { vg = vg_read_for_update(cmd, sll->str, NULL, 0); if (vg_read_error(vg)) { - free_vg(vg); + release_vg(vg); stack; continue; } @@ -285,16 +263,16 @@ int pvchange(struct cmd_context *cmd, int argc, char **argv) pvl->pv, NULL); } - unlock_and_free_vg(cmd, vg, sll->str); + unlock_and_release_vg(cmd, vg, sll->str); } } + unlock_vg(cmd, VG_GLOBAL); } - unlock_vg(cmd, VG_GLOBAL); - log_print("%d physical volume%s changed / %d physical volume%s " - "not changed", - done, done == 1 ? "" : "s", - total - done, (total - done) == 1 ? "" : "s"); + log_print_unless_silent("%d physical volume%s changed / %d physical volume%s " + "not changed", + done, done == 1 ? "" : "s", + total - done, (total - done) == 1 ? "" : "s"); return (total == done) ? ECMD_PROCESSED : ECMD_FAILED; } diff --git a/tools/pvck.c b/tools/pvck.c index 2ec7597..e45e77a 100644 --- a/tools/pvck.c +++ b/tools/pvck.c @@ -31,7 +31,7 @@ int pvck(struct cmd_context *cmd, int argc, char **argv) /* FIXME: warning and/or check if in use? */ log_verbose("Scanning %s", argv[i]); - unescape_colons_and_at_signs(argv[i], NULL, NULL); + dm_unescape_colons_and_at_signs(argv[i], NULL, NULL); pv_analyze(cmd, argv[i], arg_uint64_value(cmd, labelsector_ARG, UINT64_C(0))); diff --git a/tools/pvcreate.c b/tools/pvcreate.c index a955d37..5c12acb 100644 --- a/tools/pvcreate.c +++ b/tools/pvcreate.c @@ -56,6 +56,7 @@ static int pvcreate_restore_params_validate(struct cmd_context *cmd, if (!id_read_format(&pp->id, uuid)) return 0; pp->idp = &pp->id; + lvmcache_seed_infos_from_lvmetad(cmd); /* need to check for UUID dups */ } if (arg_count(cmd, restorefile_ARG)) { @@ -74,10 +75,10 @@ static int pvcreate_restore_params_validate(struct cmd_context *cmd, pp->pe_start = pv_pe_start(existing_pvl->pv); pp->extent_size = pv_pe_size(existing_pvl->pv); pp->extent_count = pv_pe_count(existing_pvl->pv); - free_vg(vg); + release_vg(vg); } - if (arg_sign_value(cmd, physicalvolumesize_ARG, 0) == SIGN_MINUS) { + if (arg_sign_value(cmd, physicalvolumesize_ARG, SIGN_NONE) == SIGN_MINUS) { log_error("Physical volume size may not be negative"); return 0; } @@ -93,6 +94,7 @@ int pvcreate(struct cmd_context *cmd, int argc, char **argv) int i; int ret = ECMD_PROCESSED; struct pvcreate_params pp; + struct physical_volume *pv; pvcreate_params_set_defaults(&pp); @@ -109,9 +111,9 @@ int pvcreate(struct cmd_context *cmd, int argc, char **argv) return ECMD_FAILED; } - unescape_colons_and_at_signs(argv[i], NULL, NULL); + dm_unescape_colons_and_at_signs(argv[i], NULL, NULL); - if (!pvcreate_single(cmd, argv[i], &pp)) { + if (!(pv = pvcreate_single(cmd, argv[i], &pp, 1))) { stack; ret = ECMD_FAILED; } diff --git a/tools/pvdisplay.c b/tools/pvdisplay.c index 1ae0339..c6cd412 100644 --- a/tools/pvdisplay.c +++ b/tools/pvdisplay.c @@ -32,7 +32,7 @@ static int _pvdisplay_single(struct cmd_context *cmd, vg = vg_read(cmd, vg_name, (char *)&pv->vgid, 0); if (vg_read_error(vg)) { log_error("Skipping volume group %s", vg_name); - free_vg(vg); + release_vg(vg); /* FIXME If CLUSTERED should return ECMD_PROCESSED here */ return ECMD_FAILED; } @@ -54,7 +54,7 @@ static int _pvdisplay_single(struct cmd_context *cmd, if (is_orphan(pv)) size = pv_size(pv); else - size = (pv_pe_count(pv) - pv_pe_alloc_count(pv)) * + size = (uint64_t)(pv_pe_count(pv) - pv_pe_alloc_count(pv)) * pv_pe_size(pv); if (arg_count(cmd, short_ARG)) { @@ -85,7 +85,7 @@ out: if (vg_name) unlock_vg(cmd, vg_name); if (!old_vg) - free_vg(vg); + release_vg(vg); return ret; } diff --git a/tools/pvmove.c b/tools/pvmove.c index 51f442f..9649f11 100644 --- a/tools/pvmove.c +++ b/tools/pvmove.c @@ -18,6 +18,7 @@ #include "display.h" #define PVMOVE_FIRST_TIME 0x00000001 /* Called for first time */ +#define PVMOVE_EXCLUSIVE 0x00000002 /* Require exclusive LV */ static int _pvmove_target_present(struct cmd_context *cmd, int clustered) { @@ -121,7 +122,7 @@ static struct dm_list *_get_allocatable_pvs(struct cmd_context *cmd, int argc, } /* Remove PV if full */ - if ((pvl->pv->pe_count == pvl->pv->pe_alloc_count)) + if (pvl->pv->pe_count == pvl->pv->pe_alloc_count) dm_list_del(&pvl->list); } @@ -174,13 +175,16 @@ static struct logical_volume *_set_up_pvmove_lv(struct cmd_context *cmd, const char *lv_name, struct dm_list *allocatable_pvs, alloc_policy_t alloc, - struct dm_list **lvs_changed) + struct dm_list **lvs_changed, + unsigned *exclusive) { struct logical_volume *lv_mirr, *lv; struct lv_list *lvl; uint32_t log_count = 0; int lv_found = 0; int lv_skipped = 0; + int lv_active_count = 0; + int lv_exclusive_count = 0; /* FIXME Cope with non-contiguous => splitting existing segments */ if (!(lv_mirr = lv_create_empty("pvmove%d", NULL, @@ -202,7 +206,7 @@ static struct logical_volume *_set_up_pvmove_lv(struct cmd_context *cmd, /* Find segments to be moved and set up mirrors */ dm_list_iterate_items(lvl, &vg->lvs) { lv = lvl->lv; - if ((lv == lv_mirr)) + if (lv == lv_mirr) continue; if (lv_name) { if (strcmp(lv->name, lv_name)) @@ -211,29 +215,45 @@ static struct logical_volume *_set_up_pvmove_lv(struct cmd_context *cmd, } if (lv_is_origin(lv) || lv_is_cow(lv)) { lv_skipped = 1; - log_print("Skipping snapshot-related LV %s", lv->name); + log_print_unless_silent("Skipping snapshot-related LV %s", lv->name); continue; } if (lv->status & MIRRORED) { lv_skipped = 1; - log_print("Skipping mirror LV %s", lv->name); + log_print_unless_silent("Skipping mirror LV %s", lv->name); continue; } if (lv->status & MIRROR_LOG) { lv_skipped = 1; - log_print("Skipping mirror log LV %s", lv->name); + log_print_unless_silent("Skipping mirror log LV %s", lv->name); continue; } if (lv->status & MIRROR_IMAGE) { lv_skipped = 1; - log_print("Skipping mirror image LV %s", lv->name); + log_print_unless_silent("Skipping mirror image LV %s", lv->name); continue; } if (lv->status & LOCKED) { lv_skipped = 1; - log_print("Skipping locked LV %s", lv->name); + log_print_unless_silent("Skipping locked LV %s", lv->name); continue; } + + if (vg_is_clustered(vg) && + lv_is_active_exclusive_remotely(lv)) { + lv_skipped = 1; + log_print_unless_silent("Skipping LV %s which is activated " + "exclusively on remote node.", lv->name); + continue; + } + + if (vg_is_clustered(vg)) { + if (lv_is_active_exclusive_locally(lv)) + lv_exclusive_count++; + else if (lv_is_active(lv)) + lv_active_count++; + } + if (!_insert_pvmove_mirrors(cmd, lv_mirr, source_pvl, lv, *lvs_changed)) return_NULL; @@ -254,6 +274,25 @@ static struct logical_volume *_set_up_pvmove_lv(struct cmd_context *cmd, return NULL; } + if (vg_is_clustered(vg) && lv_active_count && *exclusive) { + log_error("Cannot move in clustered VG %s, " + "clustered mirror (cmirror) not detected " + "and LVs are activated non-exclusively.", + vg->name); + return NULL; + } + + if (vg_is_clustered(vg) && lv_exclusive_count) { + if (lv_active_count) { + log_error("Cannot move in clustered VG %s " + "if some LVs are activated " + "exclusively while others don't.", + vg->name); + return NULL; + } + *exclusive = 1; + } + if (!lv_add_mirrors(cmd, lv_mirr, 1, 1, 0, 0, log_count, allocatable_pvs, alloc, MIRROR_BY_SEG)) { log_error("Failed to convert pvmove LV to mirrored"); @@ -273,7 +312,7 @@ static int _activate_lv(struct cmd_context *cmd, struct logical_volume *lv_mirr, { int r = 0; - if (exclusive) + if (exclusive || lv_is_active_exclusive(lv_mirr)) r = activate_lv_excl(cmd, lv_mirr); else r = activate_lv(cmd, lv_mirr); @@ -305,11 +344,60 @@ static int _detach_pvmove_mirror(struct cmd_context *cmd, return 1; } +static int _suspend_lvs(struct cmd_context *cmd, unsigned first_time, + struct logical_volume *lv_mirr, + struct dm_list *lvs_changed, + struct volume_group *vg_to_revert) +{ + /* + * Suspend lvs_changed the first time. + * Suspend mirrors on subsequent calls. + */ + if (first_time) { + if (!suspend_lvs(cmd, lvs_changed, vg_to_revert)) + return_0; + } else if (!suspend_lv(cmd, lv_mirr)) { + if (vg_to_revert) + vg_revert(vg_to_revert); + return_0; + } + + return 1; +} + +static int _resume_lvs(struct cmd_context *cmd, unsigned first_time, + struct logical_volume *lv_mirr, + struct dm_list *lvs_changed) +{ + /* + * Suspend lvs_changed the first time. + * Suspend mirrors on subsequent calls. + */ + + if (first_time) { + if (!resume_lvs(cmd, lvs_changed)) { + log_error("Unable to resume logical volumes"); + return 0; + } + } else if (!resume_lv(cmd, lv_mirr)) { + log_error("Unable to reactivate logical volume \"%s\"", + lv_mirr->name); + return 0; + } + + return 1; +} + +/* + * Called to set up initial pvmove LV and to advance the mirror + * to successive sections of it. + * (Not called after the last section completes.) + */ static int _update_metadata(struct cmd_context *cmd, struct volume_group *vg, struct logical_volume *lv_mirr, struct dm_list *lvs_changed, unsigned flags) { - unsigned exclusive = _pvmove_is_exclusive(cmd, vg); + unsigned exclusive = (flags & PVMOVE_EXCLUSIVE) ? 1 : 0; unsigned first_time = (flags & PVMOVE_FIRST_TIME) ? 1 : 0; int r = 0; @@ -319,38 +407,31 @@ static int _update_metadata(struct cmd_context *cmd, struct volume_group *vg, return 0; } - /* Suspend lvs_changed */ - if (!suspend_lvs(cmd, lvs_changed)) { - vg_revert(vg); - goto_out; - } - - /* Suspend mirrors on subsequent calls */ - if (!first_time) { - if (!suspend_lv(cmd, lv_mirr)) { - if (!resume_lvs(cmd, lvs_changed)) - stack; - vg_revert(vg); - goto_out; - } + if (!_suspend_lvs(cmd, first_time, lv_mirr, lvs_changed, vg)) { + log_error("ABORTING: Temporary pvmove mirror %s failed.", first_time ? "activation" : "reload"); + /* FIXME Add a recovery path for first time too. */ + if (!first_time && !revert_lv(cmd, lv_mirr)) + stack; + return 0; } /* Commit on-disk metadata */ if (!vg_commit(vg)) { log_error("ABORTING: Volume group metadata update failed."); - if (!first_time) - if (!resume_lv(cmd, lv_mirr)) - stack; - if (!resume_lvs(cmd, lvs_changed)) + if (!_resume_lvs(cmd, first_time, lv_mirr, lvs_changed)) + stack; + if (!first_time && !revert_lv(cmd, lv_mirr)) stack; - vg_revert(vg); - goto out; + return 0; } /* Activate the temporary mirror LV */ /* Only the first mirror segment gets activated as a mirror */ /* FIXME: Add option to use a log */ if (first_time) { + if (!exclusive && _pvmove_is_exclusive(cmd, vg)) + exclusive = 1; + if (!_activate_lv(cmd, lv_mirr, exclusive)) { if (test_mode()) { r = 1; @@ -358,46 +439,22 @@ static int _update_metadata(struct cmd_context *cmd, struct volume_group *vg, } /* - * FIXME: review ordering of operations above, - * temporary mirror should be preloaded in suspend. - * Also banned operation here when suspended. - * Nothing changed yet, try to revert pvmove. + * FIXME Run --abort internally here. */ - log_error("Temporary pvmove mirror activation failed."); - - /* Ensure that temporary mrror is deactivate even on other nodes. */ - (void)deactivate_lv(cmd, lv_mirr); - - /* Revert metadata */ - if (!_detach_pvmove_mirror(cmd, lv_mirr) || - !lv_remove(lv_mirr) || - !vg_write(vg) || !vg_commit(vg)) - log_error("ABORTING: Restoring original configuration " - "before pvmove failed. Run pvmove --abort."); - - /* Unsuspend LVs */ - if(!resume_lvs(cmd, lvs_changed)) - stack; - - goto out; + log_error("ABORTING: Temporary pvmove mirror activation failed. Run pvmove --abort."); + goto_out; } - } else if (!resume_lv(cmd, lv_mirr)) { - log_error("Unable to reactivate logical volume \"%s\"", - lv_mirr->name); - if (!resume_lvs(cmd, lvs_changed)) - stack; - goto out; - } - - /* Unsuspend LVs */ - if (!resume_lvs(cmd, lvs_changed)) { - log_error("Unable to resume logical volumes"); - goto out; } r = 1; + out: - backup(vg); + if (!_resume_lvs(cmd, first_time, lv_mirr, lvs_changed)) + r = 0; + + if (r) + backup(vg); + return r; } @@ -413,7 +470,7 @@ static int _set_up_pvmove(struct cmd_context *cmd, const char *pv_name, struct dm_list *lvs_changed; struct physical_volume *pv; struct logical_volume *lv_mirr; - unsigned first_time = 1; + unsigned flags = PVMOVE_FIRST_TIME; unsigned exclusive; int r = ECMD_FAILED; @@ -431,11 +488,13 @@ static int _set_up_pvmove(struct cmd_context *cmd, const char *pv_name, if (!(lv_name = _extract_lvname(cmd, pv_vg_name(pv), arg_value(cmd, name_ARG)))) { stack; + free_pv_fid(pv); return EINVALID_CMD_LINE; } if (!validate_name(lv_name)) { log_error("Logical volume name %s is invalid", lv_name); + free_pv_fid(pv); return EINVALID_CMD_LINE; } } @@ -445,7 +504,7 @@ static int _set_up_pvmove(struct cmd_context *cmd, const char *pv_name, vg = _get_vg(cmd, pv_vg_name(pv)); if (vg_read_error(vg)) { - free_vg(vg); + release_vg(vg); stack; return ECMD_FAILED; } @@ -453,7 +512,7 @@ static int _set_up_pvmove(struct cmd_context *cmd, const char *pv_name, exclusive = _pvmove_is_exclusive(cmd, vg); if ((lv_mirr = find_pvmove_lv(vg, pv_dev(pv), PVMOVE))) { - log_print("Detected pvmove in progress for %s", pv_name); + log_print_unless_silent("Detected pvmove in progress for %s", pv_name); if (argc || lv_name) log_error("Ignoring remaining command line arguments"); @@ -468,14 +527,14 @@ static int _set_up_pvmove(struct cmd_context *cmd, const char *pv_name, goto out; } - first_time = 0; + flags &= ~PVMOVE_FIRST_TIME; } else { /* Determine PE ranges to be moved */ if (!(source_pvl = create_pv_list(cmd->mem, vg, 1, &pv_name_arg, 0))) goto_out; - alloc = arg_uint_value(cmd, alloc_ARG, ALLOC_INHERIT); + alloc = (alloc_policy_t) arg_uint_value(cmd, alloc_ARG, ALLOC_INHERIT); if (alloc == ALLOC_INHERIT) alloc = vg->alloc; @@ -489,7 +548,7 @@ static int _set_up_pvmove(struct cmd_context *cmd, const char *pv_name, if (!(lv_mirr = _set_up_pvmove_lv(cmd, vg, source_pvl, lv_name, allocatable_pvs, alloc, - &lvs_changed))) + &lvs_changed, &exclusive))) goto_out; } @@ -501,16 +560,19 @@ static int _set_up_pvmove(struct cmd_context *cmd, const char *pv_name, /* init_pvmove(1); */ /* vg->status |= PVMOVE; */ - if (first_time) { + if (flags & PVMOVE_FIRST_TIME) { + if (exclusive) + flags |= PVMOVE_EXCLUSIVE; if (!_update_metadata - (cmd, vg, lv_mirr, lvs_changed, PVMOVE_FIRST_TIME)) + (cmd, vg, lv_mirr, lvs_changed, flags)) goto_out; } /* LVs are all in status LOCKED */ r = ECMD_PROCESSED; out: - unlock_and_free_vg(cmd, vg, pv_vg_name(pv)); + free_pv_fid(pv); + unlock_and_release_vg(cmd, vg, pv_vg_name(pv)); return r; } @@ -521,7 +583,8 @@ static int _finish_pvmove(struct cmd_context *cmd, struct volume_group *vg, int r = 1; if (!dm_list_empty(lvs_changed) && - !_detach_pvmove_mirror(cmd, lv_mirr)) { + (!_detach_pvmove_mirror(cmd, lv_mirr) || + !replace_lv_with_error_segment(lv_mirr))) { log_error("ABORTING: Removal of temporary mirror failed"); return 0; } @@ -533,26 +596,21 @@ static int _finish_pvmove(struct cmd_context *cmd, struct volume_group *vg, return 0; } - /* Suspend LVs changed */ - if (!suspend_lvs(cmd, lvs_changed)) { - log_error("Locking LVs to remove temporary mirror failed"); - r = 0; - } - - /* Suspend mirror LV to flush pending I/O */ - if (!suspend_lv(cmd, lv_mirr)) { - log_error("Suspension of temporary mirror LV failed"); - r = 0; + /* Suspend LVs changed (implicitly suspends lv_mirr) */ + if (!suspend_lvs(cmd, lvs_changed, vg)) { + log_error("ABORTING: Locking LVs to remove temporary mirror failed"); + if (!revert_lv(cmd, lv_mirr)) + stack; + return 0; } /* Store metadata without dependencies on mirror segments */ if (!vg_commit(vg)) { log_error("ABORTING: Failed to write new data locations " "to disk."); - vg_revert(vg); - if (!resume_lv(cmd, lv_mirr)) + if (!revert_lv(cmd, lv_mirr)) stack; - if (!resume_lvs(cmd, lvs_changed)) + if (!revert_lvs(cmd, lvs_changed)) stack; return 0; } @@ -600,6 +658,7 @@ static struct volume_group *_get_move_vg(struct cmd_context *cmd, const char *uuid __attribute__((unused))) { struct physical_volume *pv; + struct volume_group *vg; /* Reread all metadata in case it got changed */ if (!(pv = find_pv_by_name(cmd, name))) { @@ -608,7 +667,10 @@ static struct volume_group *_get_move_vg(struct cmd_context *cmd, return NULL; } - return _get_vg(cmd, pv_vg_name(pv)); + vg = _get_vg(cmd, pv_vg_name(pv)); + free_pv_fid(pv); + + return vg; } static struct poll_functions _pvmove_fns = { @@ -649,7 +711,7 @@ int pvmove(struct cmd_context *cmd, int argc, char **argv) return ECMD_FAILED; } - unescape_colons_and_at_signs(pv_name, &colon, NULL); + dm_unescape_colons_and_at_signs(pv_name, &colon, NULL); /* Drop any PE lists from PV name */ if (colon) diff --git a/tools/pvremove.c b/tools/pvremove.c index a8717e0..823c069 100644 --- a/tools/pvremove.c +++ b/tools/pvremove.c @@ -25,15 +25,12 @@ const char _really_wipe[] = static int pvremove_check(struct cmd_context *cmd, const char *name) { struct physical_volume *pv; - struct dm_list mdas; - - dm_list_init(&mdas); /* FIXME Check partition type is LVM unless --force is given */ /* Is there a pv here already? */ /* If not, this is an error unless you used -f. */ - if (!(pv = pv_read(cmd, name, &mdas, NULL, 1, 0))) { + if (!(pv = pv_read(cmd, name, 1, 0))) { if (arg_count(cmd, force_ARG)) return 1; log_error("Physical Volume %s not found", name); @@ -47,35 +44,39 @@ static int pvremove_check(struct cmd_context *cmd, const char *name) * means checking every VG by scanning every * PV on the system. */ - if (is_orphan(pv) && !dm_list_size(&mdas)) { + if (is_orphan(pv) && !dm_list_size(&pv->fid->metadata_areas_in_use) && + !dm_list_size(&pv->fid->metadata_areas_ignored)) { if (!scan_vgs_for_pvs(cmd, 0)) { log_error("Rescan for PVs without metadata areas " "failed."); - return 0; + goto bad; } - if (!(pv = pv_read(cmd, name, NULL, NULL, 1, 0))) { + free_pv_fid(pv); + if (!(pv = pv_read(cmd, name, 1, 0))) { log_error("Failed to read physical volume %s", name); - return 0; + goto bad; } } /* orphan ? */ - if (is_orphan(pv)) + if (is_orphan(pv)) { + free_pv_fid(pv); return 1; + } /* Allow partial & exported VGs to be destroyed. */ /* we must have -ff to overwrite a non orphan */ if (arg_count(cmd, force_ARG) < 2) { - log_error("Can't pvremove physical volume \"%s\" of " - "volume group \"%s\" without -ff", name, pv_vg_name(pv)); - return 0; + log_error("PV %s belongs to Volume Group %s so please use vgreduce first.", name, pv_vg_name(pv)); + log_error("(If you are certain you need pvremove, then confirm by using --force twice.)"); + goto bad; } /* prompt */ if (!arg_count(cmd, yes_ARG) && yes_no_prompt(_really_wipe, name, pv_vg_name(pv)) == 'n') { log_error("%s: physical volume label not removed", name); - return 0; + goto bad; } if (arg_count(cmd, force_ARG)) { @@ -86,7 +87,12 @@ static int pvremove_check(struct cmd_context *cmd, const char *name) !is_orphan(pv) ? "\"" : ""); } + free_pv_fid(pv); return 1; + +bad: + free_pv_fid(pv); + return 0; } static int pvremove_single(struct cmd_context *cmd, const char *pv_name, @@ -101,33 +107,36 @@ static int pvremove_single(struct cmd_context *cmd, const char *pv_name, } if (!pvremove_check(cmd, pv_name)) - goto error; + goto out; if (!(dev = dev_cache_get(pv_name, cmd->filter))) { log_error("%s: Couldn't find device. Check your filters?", pv_name); - goto error; + goto out; } if (!dev_test_excl(dev)) { /* FIXME Detect whether device-mapper is still using the device */ log_error("Can't open %s exclusively - not removing. " "Mounted filesystem?", dev_name(dev)); - goto error; + goto out; } /* Wipe existing label(s) */ if (!label_remove(dev)) { log_error("Failed to wipe existing label(s) on %s", pv_name); - goto error; + goto out; } - log_print("Labels on physical volume \"%s\" successfully wiped", - pv_name); + if (!lvmetad_pv_gone_by_dev(dev, NULL)) + goto_out; + + log_print_unless_silent("Labels on physical volume \"%s\" successfully wiped", + pv_name); ret = ECMD_PROCESSED; - error: +out: unlock_vg(cmd, VG_ORPHANS); return ret; @@ -144,7 +153,7 @@ int pvremove(struct cmd_context *cmd, int argc, char **argv) } for (i = 0; i < argc; i++) { - unescape_colons_and_at_signs(argv[i], NULL, NULL); + dm_unescape_colons_and_at_signs(argv[i], NULL, NULL); r = pvremove_single(cmd, argv[i], NULL); if (r > ret) ret = r; diff --git a/tools/pvresize.c b/tools/pvresize.c index 8582ef4..2f0693a 100644 --- a/tools/pvresize.c +++ b/tools/pvresize.c @@ -15,6 +15,7 @@ */ #include "tools.h" +#include "metadata.h" struct pvresize_params { uint64_t new_size; @@ -30,16 +31,11 @@ static int _pv_resize_single(struct cmd_context *cmd, { struct pv_list *pvl; uint64_t size = 0; - uint32_t new_pe_count = 0; int r = 0; - struct dm_list mdas; const char *pv_name = pv_dev_name(pv); const char *vg_name = pv_vg_name(pv); - struct lvmcache_info *info; - int mda_count = 0; struct volume_group *old_vg = vg; - - dm_list_init(&mdas); + int vg_needs_pv_write = 0; if (is_orphan_vg(vg_name)) { if (!lock_vol(cmd, vg_name, LCK_VG_WRITE)) { @@ -47,18 +43,16 @@ static int _pv_resize_single(struct cmd_context *cmd, return 0; } - if (!(pv = pv_read(cmd, pv_name, &mdas, NULL, 1, 0))) { + if (!(pv = pv_read(cmd, pv_name, 1, 0))) { unlock_vg(cmd, vg_name); log_error("Unable to read PV \"%s\"", pv_name); return 0; } - - mda_count = dm_list_size(&mdas); } else { vg = vg_read_for_update(cmd, vg_name, NULL, 0); if (vg_read_error(vg)) { - free_vg(vg); + release_vg(vg); log_error("Unable to read volume group \"%s\".", vg_name); return 0; @@ -72,24 +66,10 @@ static int _pv_resize_single(struct cmd_context *cmd, pv = pvl->pv; - if (!(info = info_from_pvid(pv->dev->pvid, 0))) { - log_error("Can't get info for PV %s in volume group %s", - pv_name, vg->name); - goto out; - } - - mda_count = dm_list_size(&info->mdas); - if (!archive(vg)) goto out; } - /* FIXME Create function to test compatibility properly */ - if (mda_count > 1) { - log_error("%s: too many metadata areas for pvresize", pv_name); - goto out; - } - if (!(pv->fmt->features & FMT_RESIZE_PV)) { log_error("Physical volume %s format does not support resizing.", pv_name); @@ -111,40 +91,24 @@ static int _pv_resize_single(struct cmd_context *cmd, size = new_size; } - if (size < PV_MIN_SIZE) { - log_error("%s: Size must exceed minimum of %ld sectors.", - pv_name, PV_MIN_SIZE); - goto out; - } - - if (size < pv_pe_start(pv)) { - log_error("%s: Size must exceed physical extent start of " - "%" PRIu64 " sectors.", pv_name, pv_pe_start(pv)); - goto out; - } - - pv->size = size; + log_verbose("Resizing volume \"%s\" to %" PRIu64 " sectors.", + pv_name, pv_size(pv)); - if (vg) { - pv->size -= pv_pe_start(pv); - new_pe_count = pv_size(pv) / vg->extent_size; + if (!pv_resize(pv, vg, size)) + goto_out; - if (!new_pe_count) { - log_error("%s: Size must leave space for at " - "least one physical extent of " - "%" PRIu32 " sectors.", pv_name, - pv_pe_size(pv)); - goto out; - } + log_verbose("Updating physical volume \"%s\"", pv_name); - if (!pv_resize(pv, vg, new_pe_count)) - goto_out; + /* Write PV label only if this an orphan PV or it has 2nd mda. */ + if ((is_orphan_vg(vg_name) || + (vg_needs_pv_write = (fid_get_mda_indexed(vg->fid, + (const char *) &pv->id, ID_LEN, 1) != NULL))) && + !pv_write(cmd, pv, 1)) { + log_error("Failed to store physical volume \"%s\"", + pv_name); + goto out; } - log_verbose("Resizing volume \"%s\" to %" PRIu64 " sectors.", - pv_name, pv_size(pv)); - - log_verbose("Updating physical volume \"%s\"", pv_name); if (!is_orphan_vg(vg_name)) { if (!vg_write(vg) || !vg_commit(vg)) { log_error("Failed to store physical volume \"%s\" in " @@ -152,19 +116,20 @@ static int _pv_resize_single(struct cmd_context *cmd, goto out; } backup(vg); - } else if (!(pv_write(cmd, pv, NULL, INT64_C(-1)))) { - log_error("Failed to store physical volume \"%s\"", - pv_name); - goto out; } - log_print("Physical volume \"%s\" changed", pv_name); + log_print_unless_silent("Physical volume \"%s\" changed", pv_name); r = 1; out: + if (!r && vg_needs_pv_write) + log_error("Use pvcreate and vgcfgrestore " + "to repair from archived metadata."); unlock_vg(cmd, vg_name); + if (is_orphan_vg(vg_name)) + free_pv_fid(pv); if (!old_vg) - free_vg(vg); + release_vg(vg); return r; } @@ -197,7 +162,7 @@ int pvresize(struct cmd_context *cmd, int argc, char **argv) return EINVALID_CMD_LINE; } - if (arg_sign_value(cmd, physicalvolumesize_ARG, 0) == SIGN_MINUS) { + if (arg_sign_value(cmd, physicalvolumesize_ARG, SIGN_NONE) == SIGN_MINUS) { log_error("Physical volume size may not be negative"); return 0; } @@ -211,8 +176,8 @@ int pvresize(struct cmd_context *cmd, int argc, char **argv) ret = process_each_pv(cmd, argc, argv, NULL, READ_FOR_UPDATE, 0, ¶ms, _pvresize_single); - log_print("%d physical volume(s) resized / %d physical volume(s) " - "not resized", params.done, params.total - params.done); + log_print_unless_silent("%d physical volume(s) resized / %d physical volume(s) " + "not resized", params.done, params.total - params.done); return ret; } diff --git a/tools/pvscan.c b/tools/pvscan.c index b24b7ab..3d5ddef 100644 --- a/tools/pvscan.c +++ b/tools/pvscan.c @@ -1,6 +1,6 @@ /* * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved. - * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved. + * Copyright (C) 2004-2012 Red Hat, Inc. All rights reserved. * * This file is part of LVM2. * @@ -15,6 +15,9 @@ #include "tools.h" +#include "lvmetad.h" +#include "lvmcache.h" + int pv_max_name_len = 0; int vg_max_name_len = 0; @@ -25,13 +28,13 @@ static void _pvscan_display_single(struct cmd_context *cmd, char uuid[64] __attribute__((aligned(8))); unsigned vg_name_len = 0; - char pv_tmp_name[NAME_LEN] = { 0, }; - char vg_tmp_name[NAME_LEN] = { 0, }; - char vg_name_this[NAME_LEN] = { 0, }; + char pv_tmp_name[NAME_LEN] = { 0 }; + char vg_tmp_name[NAME_LEN] = { 0 }; + char vg_name_this[NAME_LEN] = { 0 }; /* short listing? */ if (arg_count(cmd, short_ARG) > 0) { - log_print("%s", pv_dev_name(pv)); + log_print_unless_silent("%s", pv_dev_name(pv)); return; } @@ -46,8 +49,6 @@ static void _pvscan_display_single(struct cmd_context *cmd, /* return; */ } - memset(pv_tmp_name, 0, sizeof(pv_tmp_name)); - vg_name_len = strlen(pv_vg_name(pv)) + 1; if (arg_count(cmd, uuid_ARG)) { @@ -63,41 +64,156 @@ static void _pvscan_display_single(struct cmd_context *cmd, } if (is_orphan(pv)) { - log_print("PV %-*s %-*s %s [%s]", - pv_max_name_len, pv_tmp_name, - vg_max_name_len, " ", - pv->fmt ? pv->fmt->name : " ", - display_size(cmd, pv_size(pv))); + log_print_unless_silent("PV %-*s %-*s %s [%s]", + pv_max_name_len, pv_tmp_name, + vg_max_name_len, " ", + pv->fmt ? pv->fmt->name : " ", + display_size(cmd, pv_size(pv))); return; } if (pv_status(pv) & EXPORTED_VG) { strncpy(vg_name_this, pv_vg_name(pv), vg_name_len); - log_print("PV %-*s is in exported VG %s " - "[%s / %s free]", - pv_max_name_len, pv_tmp_name, - vg_name_this, - display_size(cmd, (uint64_t) pv_pe_count(pv) * - pv_pe_size(pv)), - display_size(cmd, (uint64_t) (pv_pe_count(pv) - - pv_pe_alloc_count(pv)) - * pv_pe_size(pv))); + log_print_unless_silent("PV %-*s is in exported VG %s " + "[%s / %s free]", + pv_max_name_len, pv_tmp_name, + vg_name_this, + display_size(cmd, (uint64_t) pv_pe_count(pv) * pv_pe_size(pv)), + display_size(cmd, (uint64_t) (pv_pe_count(pv) - pv_pe_alloc_count(pv)) * pv_pe_size(pv))); return; } sprintf(vg_tmp_name, "%s", pv_vg_name(pv)); - log_print("PV %-*s VG %-*s %s [%s / %s free]", pv_max_name_len, - pv_tmp_name, vg_max_name_len, vg_tmp_name, - pv->fmt ? pv->fmt->name : " ", - display_size(cmd, (uint64_t) pv_pe_count(pv) * - pv_pe_size(pv)), - display_size(cmd, (uint64_t) (pv_pe_count(pv) - - pv_pe_alloc_count(pv)) * - pv_pe_size(pv))); + log_print_unless_silent("PV %-*s VG %-*s %s [%s / %s free]", pv_max_name_len, + pv_tmp_name, vg_max_name_len, vg_tmp_name, + pv->fmt ? pv->fmt->name : " ", + display_size(cmd, (uint64_t) pv_pe_count(pv) * pv_pe_size(pv)), + display_size(cmd, (uint64_t) (pv_pe_count(pv) - pv_pe_alloc_count(pv)) * pv_pe_size(pv))); +} + +static int _auto_activation_handler(struct volume_group *vg, int partial, + activation_change_t activate) +{ + /* TODO: add support for partial and clustered VGs */ + if (partial || vg_is_clustered(vg)) + return 1; + + if (!vgchange_activate(vg->cmd, vg, activate)) { + log_error("%s: autoactivation failed.", vg->name); + return 0; + } + + return 1; +} + +static int _pvscan_lvmetad(struct cmd_context *cmd, int argc, char **argv) +{ + int ret = ECMD_PROCESSED; + struct device *dev; + const char *pv_name; + int32_t major = -1; + int32_t minor = -1; + int devno_args = 0; + struct arg_value_group_list *current_group; + dev_t devno; + char *buf; + activation_handler handler = NULL; + + if (arg_count(cmd, activate_ARG)) { + if (arg_uint_value(cmd, activate_ARG, CHANGE_AAY) != CHANGE_AAY) { + log_error("Only --activate ay allowed with pvscan."); + return 0; + } + handler = _auto_activation_handler; + } + + if (arg_count(cmd, major_ARG) + arg_count(cmd, minor_ARG)) + devno_args = 1; + + if (devno_args && (!arg_count(cmd, major_ARG) || !arg_count(cmd, minor_ARG))) { + log_error("Both --major and --minor required to identify devices."); + return EINVALID_CMD_LINE; + } + + if (!lock_vol(cmd, VG_GLOBAL, LCK_VG_READ)) { + log_error("Unable to obtain global lock."); + return ECMD_FAILED; + } + + /* Scan everything? */ + if (!argc && !devno_args) { + if (!lvmetad_pvscan_all_devs(cmd, handler)) + ret = ECMD_FAILED; + goto out; + } + + log_verbose("Using physical volume(s) on command line"); + + /* Process any command line PVs first. */ + while (argc--) { + pv_name = *argv++; + dev = dev_cache_get(pv_name, NULL); + if (!dev) { + log_error("Physical Volume %s not found.", pv_name); + ret = ECMD_FAILED; + continue; + } + + if (!lvmetad_pvscan_single(cmd, dev, handler)) { + ret = ECMD_FAILED; + break; + } + if (sigint_caught()) + break; + } + + if (!devno_args) + goto out; + + /* Process any grouped --major --minor args */ + dm_list_iterate_items(current_group, &cmd->arg_value_groups) { + major = grouped_arg_int_value(current_group->arg_values, major_ARG, major); + minor = grouped_arg_int_value(current_group->arg_values, minor_ARG, minor); + + if (major < 0 || minor < 0) + continue; + + devno = MKDEV((dev_t)major, minor); + + if (!(dev = dev_cache_get_by_devt(devno, NULL))) { + if (!dm_asprintf(&buf, "%" PRIi32 ":%" PRIi32, major, minor)) + stack; + /* FIXME Filters? */ + if (!lvmetad_pv_gone(devno, buf ? : "", handler)) { + ret = ECMD_FAILED; + if (buf) + dm_free(buf); + break; + } + + log_print_unless_silent("Device %s not found. " + "Cleared from lvmetad cache.", buf ? : ""); + if (buf) + dm_free(buf); + continue; + } + + if (!lvmetad_pvscan_single(cmd, dev, handler)) { + ret = ECMD_FAILED; + break; + } + + if (sigint_caught()) + break; + } + +out: + unlock_vg(cmd, VG_GLOBAL); + + return ret; } -int pvscan(struct cmd_context *cmd, int argc __attribute__((unused)), - char **argv __attribute__((unused))) +int pvscan(struct cmd_context *cmd, int argc, char **argv) { int new_pvs_found = 0; int pvs_found = 0; @@ -113,6 +229,19 @@ int pvscan(struct cmd_context *cmd, int argc __attribute__((unused)), pv_max_name_len = 0; vg_max_name_len = 0; + if (arg_count(cmd, cache_ARG)) + return _pvscan_lvmetad(cmd, argc, argv); + + if (arg_count(cmd, activate_ARG)) { + log_error("--activate is only valid with --cache."); + return EINVALID_CMD_LINE; + } + + if (arg_count(cmd, major_ARG) + arg_count(cmd, minor_ARG)) { + log_error("--major and --minor are only valid with --cache."); + return EINVALID_CMD_LINE; + } + if (arg_count(cmd, novolumegroup_ARG) && arg_count(cmd, exported_ARG)) { log_error("Options -e and -n are incompatible"); return EINVALID_CMD_LINE; @@ -128,9 +257,14 @@ int pvscan(struct cmd_context *cmd, int argc __attribute__((unused)), return ECMD_FAILED; } - persistent_filter_wipe(cmd->filter); + if (cmd->filter->wipe) + cmd->filter->wipe(cmd->filter); lvmcache_destroy(cmd, 1); + /* populate lvmcache */ + if (!lvmetad_vg_list_to_lvmcache(cmd)) + stack; + log_verbose("Walking through all physical volumes"); if (!(pvslist = get_pvs(cmd))) { unlock_vg(cmd, VG_GLOBAL); @@ -143,9 +277,10 @@ int pvscan(struct cmd_context *cmd, int argc __attribute__((unused)), pv = pvl->pv; if ((arg_count(cmd, exported_ARG) - && !(pv_status(pv) & EXPORTED_VG)) - || (arg_count(cmd, novolumegroup_ARG) && (!is_orphan(pv)))) { + && !(pv_status(pv) & EXPORTED_VG)) || + (arg_count(cmd, novolumegroup_ARG) && (!is_orphan(pv)))) { dm_list_del(&pvl->list); + free_pv_fid(pv); continue; } @@ -183,21 +318,23 @@ int pvscan(struct cmd_context *cmd, int argc __attribute__((unused)), pv_max_name_len += 2; vg_max_name_len += 2; - dm_list_iterate_items(pvl, pvslist) - _pvscan_display_single(cmd, pvl->pv, NULL); + dm_list_iterate_items(pvl, pvslist) { + _pvscan_display_single(cmd, pvl->pv, NULL); + free_pv_fid(pvl->pv); + } if (!pvs_found) { - log_print("No matching physical volumes found"); + log_print_unless_silent("No matching physical volumes found"); unlock_vg(cmd, VG_GLOBAL); return ECMD_PROCESSED; } - log_print("Total: %d [%s] / in use: %d [%s] / in no VG: %d [%s]", - pvs_found, - display_size(cmd, size_total), - pvs_found - new_pvs_found, - display_size(cmd, (size_total - size_new)), - new_pvs_found, display_size(cmd, size_new)); + log_print_unless_silent("Total: %d [%s] / in use: %d [%s] / in no VG: %d [%s]", + pvs_found, + display_size(cmd, size_total), + pvs_found - new_pvs_found, + display_size(cmd, (size_total - size_new)), + new_pvs_found, display_size(cmd, size_new)); unlock_vg(cmd, VG_GLOBAL); diff --git a/tools/reporter.c b/tools/reporter.c index fbd0c27..1c8e39d 100644 --- a/tools/reporter.c +++ b/tools/reporter.c @@ -51,7 +51,6 @@ static int _segs_single(struct cmd_context *cmd __attribute__((unused)), return ECMD_PROCESSED; } - static int _pvsegs_sub_single(struct cmd_context *cmd, struct volume_group *vg, struct pv_segment *pvseg, void *handle) @@ -61,15 +60,13 @@ static int _pvsegs_sub_single(struct cmd_context *cmd, struct volume_group _free_vg = { .cmd = cmd, - .name = (char *)"", + .name = "", + .vgmem = NULL, }; - if (!(_free_vg.vgmem = dm_pool_create("_free_vg", 10240))) - return ECMD_FAILED; - struct logical_volume _free_logical_volume = { .vg = vg ?: &_free_vg, - .name = (char *) "", + .name = "", .snapshot = NULL, .status = VISIBLE_LV, .major = -1, @@ -109,8 +106,8 @@ static int _pvsegs_sub_single(struct cmd_context *cmd, ret = ECMD_FAILED; goto_out; } + out: - free_vg(&_free_vg); return ret; } @@ -145,7 +142,7 @@ static int _pvs_single(struct cmd_context *cmd, struct volume_group *vg, vg = vg_read(cmd, vg_name, (char *)&pv->vgid, 0); if (vg_read_error(vg)) { log_error("Skipping volume group %s", vg_name); - free_vg(vg); + release_vg(vg); return ECMD_FAILED; } @@ -185,7 +182,7 @@ out: unlock_vg(cmd, vg_name); if (!old_vg) - free_vg(vg); + release_vg(vg); return ret; } @@ -318,6 +315,9 @@ static int _report(struct cmd_context *cmd, int argc, char **argv, "report/pvsegs_cols_verbose", DEFAULT_PVSEGS_COLS_VERB); break; + default: + log_error(INTERNAL_ERROR "Unknown report type."); + return ECMD_FAILED; } /* If -o supplied use it, else use default for report_type */ diff --git a/tools/toollib.c b/tools/toollib.c index f76aacd..3fe1c14 100644 --- a/tools/toollib.c +++ b/tools/toollib.c @@ -14,11 +14,7 @@ */ #include "tools.h" -#include "lv_alloc.h" -#include "xlate.h" - #include <sys/stat.h> -#include <sys/wait.h> const char *command_name(struct cmd_context *cmd) { @@ -28,7 +24,7 @@ const char *command_name(struct cmd_context *cmd) /* * Strip dev_dir if present */ -char *skip_dev_dir(struct cmd_context *cmd, const char *vg_name, +const char *skip_dev_dir(struct cmd_context *cmd, const char *vg_name, unsigned *dev_dir_found) { const char *dmdir = dm_dir(); @@ -54,7 +50,7 @@ char *skip_dev_dir(struct cmd_context *cmd, const char *vg_name, *layer) { log_error("skip_dev_dir: Couldn't split up device name %s", vg_name); - return (char *) vg_name; + return vg_name; } vglv_sz = strlen(vgname) + strlen(lvname) + 2; if (!(vglv = dm_pool_alloc(cmd->mem, vglv_sz)) || @@ -62,7 +58,7 @@ char *skip_dev_dir(struct cmd_context *cmd, const char *vg_name, *lvname ? "/" : "", lvname) < 0) { log_error("vg/lv string alloc failed"); - return (char *) vg_name; + return vg_name; } return vglv; } @@ -76,7 +72,7 @@ char *skip_dev_dir(struct cmd_context *cmd, const char *vg_name, } else if (dev_dir_found) *dev_dir_found = 0; - return (char *) vg_name; + return vg_name; } /* @@ -119,10 +115,20 @@ int process_each_lv_in_vg(struct cmd_context *cmd, process_all = 1; } + /* + * FIXME: In case of remove it goes through deleted entries, + * but it works since entries are allocated from vg mem pool. + */ dm_list_iterate_items(lvl, &vg->lvs) { if (lvl->lv->status & SNAPSHOT) continue; + /* Skip availability change for non-virt snaps when processing all LVs */ + /* FIXME: pass process_all to process_single_lv() */ + if (process_all && arg_count(cmd, activate_ARG) && + lv_is_cow(lvl->lv) && !lv_is_virtual_origin(origin_from_cow(lvl->lv))) + continue; + if (lv_is_virtual_origin(lvl->lv) && !arg_count(cmd, all_ARG)) continue; @@ -169,11 +175,17 @@ int process_each_lv_in_vg(struct cmd_context *cmd, } if (ret > ret_max) ret_max = ret; - if (sigint_caught()) + if (sigint_caught()) { + stack; return ret_max; + } } if (lvargs_supplied && lvargs_matched != dm_list_size(arg_lvnames)) { + /* + * FIXME: lvm supports removal of LV with all its dependencies + * this leads to miscalculation that depends on the order of args. + */ log_error("One or more specified logical volume(s) not found."); if (ret_max < ECMD_FAILED) ret_max = ECMD_FAILED; @@ -253,8 +265,10 @@ int process_each_lv(struct cmd_context *cmd, int argc, char **argv, while (*lv_name == '/') lv_name++; if (!(vgname = extract_vgname(cmd, vgname))) { - if (ret_max < ECMD_FAILED) + if (ret_max < ECMD_FAILED) { + stack; ret_max = ECMD_FAILED; + } continue; } } else if (!dev_dir_found && @@ -295,6 +309,8 @@ int process_each_lv(struct cmd_context *cmd, int argc, char **argv, if (!argc || !dm_list_empty(&tags)) { log_verbose("Finding all logical volumes"); + if (!lvmetad_vg_list_to_lvmcache(cmd)) + stack; if (!(vgnames = get_vgnames(cmd, 0)) || dm_list_empty(vgnames)) { log_error("No volume groups found"); return ret_max; @@ -347,8 +363,12 @@ int process_each_lv(struct cmd_context *cmd, int argc, char **argv, ret = process_each_lv_in_vg(cmd, cvl_vg->vg, &lvnames, tags_arg, &failed_lvnames, handle, process_single_lv); - if (ret != ECMD_PROCESSED || - dm_list_empty(&failed_lvnames)) + if (ret != ECMD_PROCESSED) { + stack; + break; + } + + if (dm_list_empty(&failed_lvnames)) break; /* Try again with failed LVs in this VG */ @@ -357,6 +377,7 @@ int process_each_lv(struct cmd_context *cmd, int argc, char **argv, free_cmd_vgs(&cmd_vgs); if (!cmd_vg_read(cmd, &cmd_vgs)) { + stack; ret = ECMD_FAILED; /* break */ break; } @@ -366,8 +387,10 @@ int process_each_lv(struct cmd_context *cmd, int argc, char **argv, free_cmd_vgs(&cmd_vgs); /* FIXME: logic for breaking command is not consistent */ - if (sigint_caught()) + if (sigint_caught()) { + stack; return ECMD_FAILED; + } } return ret_max; @@ -392,7 +415,7 @@ int process_each_segment_in_pv(struct cmd_context *cmd, vg = vg_read(cmd, vg_name, NULL, 0); if (vg_read_error(vg)) { - free_vg(vg); + release_vg(vg); log_error("Skipping volume group %s", vg_name); return ECMD_FAILED; } @@ -404,7 +427,7 @@ int process_each_segment_in_pv(struct cmd_context *cmd, if (!(pvl = find_pv_in_vg(vg, pv_dev_name(pv)))) { log_error("Unable to find %s in volume group %s", pv_dev_name(pv), vg_name); - unlock_and_free_vg(cmd, vg, vg_name); + unlock_and_release_vg(cmd, vg, vg_name); return ECMD_FAILED; } @@ -427,7 +450,7 @@ int process_each_segment_in_pv(struct cmd_context *cmd, if (vg_name) unlock_vg(cmd, vg_name); if (!old_vg) - free_vg(vg); + release_vg(vg); return ret_max; } @@ -472,7 +495,7 @@ static int _process_one_vg(struct cmd_context *cmd, const char *vg_name, for (;;) { /* FIXME: consistent handling of command break */ if (sigint_caught()) { - ret = ECMD_FAILED; + ret = ECMD_FAILED; break; } if (!cmd_vg_read(cmd, &cmd_vgs)) @@ -564,13 +587,15 @@ int process_each_vg(struct cmd_context *cmd, int argc, char **argv, if (!argc || !dm_list_empty(&tags)) { log_verbose("Finding all volume groups"); + if (!lvmetad_vg_list_to_lvmcache(cmd)) + stack; if (!(vgids = get_vgids(cmd, 0)) || dm_list_empty(vgids)) { log_error("No volume groups found"); return ret_max; } dm_list_iterate_items(sl, vgids) { vgid = sl->str; - if (!(vgid) || !(vg_name = vgname_from_vgid(cmd->mem, vgid))) + if (!(vgid) || !(vg_name = lvmcache_vgname_from_vgid(cmd->mem, vgid))) continue; ret_max = _process_one_vg(cmd, vg_name, vgid, &tags, &arg_vgnames, @@ -640,15 +665,17 @@ static int _process_all_devs(struct cmd_context *cmd, void *handle, } while ((dev = dev_iter_get(iter))) { - if (!(pv = pv_read(cmd, dev_name(dev), NULL, NULL, 0, 0))) { + if (!(pv = pv_read(cmd, dev_name(dev), 0, 0))) { memset(&pv_dummy, 0, sizeof(pv_dummy)); dm_list_init(&pv_dummy.tags); dm_list_init(&pv_dummy.segments); pv_dummy.dev = dev; - pv_dummy.fmt = NULL; pv = &pv_dummy; } ret = process_single_pv(cmd, NULL, pv, handle); + + free_pv_fid(pv); + if (ret > ret_max) ret_max = ret; if (sigint_caught()) @@ -682,7 +709,6 @@ int process_each_pv(struct cmd_context *cmd, int argc, char **argv, struct str_list *sll; char *at_sign, *tagname; int scanned = 0; - struct dm_list mdas; dm_list_init(&tags); @@ -694,7 +720,7 @@ int process_each_pv(struct cmd_context *cmd, int argc, char **argv, if (argc) { log_verbose("Using physical volume(s) on command line"); for (; opt < argc; opt++) { - unescape_colons_and_at_signs(argv[opt], NULL, &at_sign); + dm_unescape_colons_and_at_signs(argv[opt], NULL, &at_sign); if (at_sign && (at_sign == argv[opt])) { tagname = at_sign + 1; @@ -724,10 +750,8 @@ int process_each_pv(struct cmd_context *cmd, int argc, char **argv, } pv = pvl->pv; } else { - - dm_list_init(&mdas); - if (!(pv = pv_read(cmd, argv[opt], &mdas, - NULL, 1, scan_label_only))) { + if (!(pv = pv_read(cmd, argv[opt], + 1, scan_label_only))) { log_error("Failed to read physical " "volume \"%s\"", argv[opt]); ret_max = ECMD_FAILED; @@ -742,7 +766,7 @@ int process_each_pv(struct cmd_context *cmd, int argc, char **argv, * PV on the system. */ if (!scanned && is_orphan(pv) && - !dm_list_size(&mdas)) { + !dm_list_size(&pv->fid->metadata_areas_in_use)) { if (!scan_label_only && !scan_vgs_for_pvs(cmd, 1)) { stack; @@ -750,8 +774,9 @@ int process_each_pv(struct cmd_context *cmd, int argc, char **argv, continue; } scanned = 1; + free_pv_fid(pv); if (!(pv = pv_read(cmd, argv[opt], - NULL, NULL, 1, + 1, scan_label_only))) { log_error("Failed to read " "physical volume " @@ -763,6 +788,14 @@ int process_each_pv(struct cmd_context *cmd, int argc, char **argv, } ret = process_single_pv(cmd, vg, pv, handle); + + /* + * Free PV only if we called pv_read before, + * otherwise the PV structure is part of the VG. + */ + if (!vg) + free_pv_fid(pv); + if (ret > ret_max) ret_max = ret; if (sigint_caught()) @@ -774,7 +807,7 @@ int process_each_pv(struct cmd_context *cmd, int argc, char **argv, vg = vg_read(cmd, sll->str, NULL, flags); if (vg_read_error(vg)) { ret_max = ECMD_FAILED; - free_vg(vg); + release_vg(vg); stack; continue; } @@ -783,7 +816,7 @@ int process_each_pv(struct cmd_context *cmd, int argc, char **argv, handle, process_single_pv); - unlock_and_free_vg(cmd, vg, sll->str); + unlock_and_release_vg(cmd, vg, sll->str); if (ret > ret_max) ret_max = ret; @@ -810,12 +843,14 @@ int process_each_pv(struct cmd_context *cmd, int argc, char **argv, } else { log_verbose("Scanning for physical volume names"); + lvmcache_seed_infos_from_lvmetad(cmd); if (!(pvslist = get_pvs(cmd))) goto bad; dm_list_iterate_items(pvl, pvslist) { ret = process_single_pv(cmd, NULL, pvl->pv, handle); + free_pv_fid(pvl->pv); if (ret > ret_max) ret_max = ret; if (sigint_caught()) @@ -898,7 +933,7 @@ const char *extract_vgname(struct cmd_context *cmd, const char *lv_name) */ char *default_vgname(struct cmd_context *cmd) { - char *vg_path; + const char *vg_path; /* Take default VG from environment? */ vg_path = getenv("LVM_VG_NAME"); @@ -959,10 +994,11 @@ static int xstrtouint32(const char *s, char **p, int base, uint32_t *result) errno = 0; ul = strtoul(s, p, base); - if (errno || *p == s || (uint32_t) ul != ul) - return -1; + if (errno || *p == s || ul > UINT32_MAX) + return 0; *result = ul; - return 0; + + return 1; } static int _parse_pes(struct dm_pool *mem, char *c, struct dm_list *pe_ranges, @@ -994,7 +1030,7 @@ static int _parse_pes(struct dm_pool *mem, char *c, struct dm_list *pe_ranges, /* Start extent given? */ if (isdigit(*c)) { - if (xstrtouint32(c, &endptr, 10, &start)) + if (!xstrtouint32(c, &endptr, 10, &start)) goto error; c = endptr; /* Just one number given? */ @@ -1005,7 +1041,7 @@ static int _parse_pes(struct dm_pool *mem, char *c, struct dm_list *pe_ranges, if (*c == '-') { c++; if (isdigit(*c)) { - if (xstrtouint32(c, &endptr, 10, &end)) + if (!xstrtouint32(c, &endptr, 10, &end)) goto error; c = endptr; } @@ -1107,7 +1143,7 @@ struct dm_list *create_pv_list(struct dm_pool *mem, struct volume_group *vg, int dm_list_init(&arg_pvnames); for (i = 0; i < argc; i++) { - unescape_colons_and_at_signs(argv[i], &colon, &at_sign); + dm_unescape_colons_and_at_signs(argv[i], &colon, &at_sign); if (at_sign && (at_sign == argv[i])) { tagname = at_sign + 1; @@ -1212,7 +1248,7 @@ int vgcreate_params_set_from_args(struct cmd_context *cmd, vp_def->max_lv); vp_new->max_pv = arg_uint_value(cmd, maxphysicalvolumes_ARG, vp_def->max_pv); - vp_new->alloc = arg_uint_value(cmd, alloc_ARG, vp_def->alloc); + vp_new->alloc = (alloc_policy_t) arg_uint_value(cmd, alloc_ARG, vp_def->alloc); /* Units of 512-byte sectors */ vp_new->extent_size = @@ -1226,17 +1262,23 @@ int vgcreate_params_set_from_args(struct cmd_context *cmd, /* Default depends on current locking type */ vp_new->clustered = locking_is_clustered(); - if (arg_sign_value(cmd, physicalextentsize_ARG, 0) == SIGN_MINUS) { + if (arg_sign_value(cmd, physicalextentsize_ARG, SIGN_NONE) == SIGN_MINUS) { log_error("Physical extent size may not be negative"); return 1; } - if (arg_sign_value(cmd, maxlogicalvolumes_ARG, 0) == SIGN_MINUS) { + if (arg_uint64_value(cmd, physicalextentsize_ARG, 0) > MAX_EXTENT_SIZE) { + log_error("Physical extent size cannot be larger than %s", + display_size(cmd, (uint64_t) MAX_EXTENT_SIZE)); + return 1; + } + + if (arg_sign_value(cmd, maxlogicalvolumes_ARG, SIGN_NONE) == SIGN_MINUS) { log_error("Max Logical Volumes may not be negative"); return 1; } - if (arg_sign_value(cmd, maxphysicalvolumes_ARG, 0) == SIGN_MINUS) { + if (arg_sign_value(cmd, maxphysicalvolumes_ARG, SIGN_NONE) == SIGN_MINUS) { log_error("Max Physical Volumes may not be negative"); return 1; } @@ -1295,10 +1337,17 @@ int vg_refresh_visible(struct cmd_context *cmd, struct volume_group *vg) struct lv_list *lvl; int r = 1; - dm_list_iterate_items(lvl, &vg->lvs) + sigint_allow(); + dm_list_iterate_items(lvl, &vg->lvs) { + if (sigint_caught()) + return_0; + if (lv_is_visible(lvl->lv)) if (!lv_refresh(cmd, lvl->lv)) r = 0; + } + + sigint_restore(); return r; } @@ -1343,7 +1392,7 @@ int pvcreate_params_validate(struct cmd_context *cmd, } pp->yes = arg_count(cmd, yes_ARG); - pp->force = arg_count(cmd, force_ARG); + pp->force = (force_t) arg_count(cmd, force_ARG); if (arg_int_value(cmd, labelsector_ARG, 0) >= LABEL_SCAN_SECTORS) { log_error("labelsector must be less than %lu", @@ -1391,13 +1440,13 @@ int pvcreate_params_validate(struct cmd_context *cmd, if (arg_count(cmd, zero_ARG)) pp->zero = strcmp(arg_str_value(cmd, zero_ARG, "y"), "n"); - if (arg_sign_value(cmd, dataalignment_ARG, 0) == SIGN_MINUS) { + if (arg_sign_value(cmd, dataalignment_ARG, SIGN_NONE) == SIGN_MINUS) { log_error("Physical volume data alignment may not be negative"); return 0; } pp->data_alignment = arg_uint64_value(cmd, dataalignment_ARG, UINT64_C(0)); - if (pp->data_alignment > ULONG_MAX) { + if (pp->data_alignment > UINT32_MAX) { log_error("Physical volume data alignment is too big."); return 0; } @@ -1410,13 +1459,13 @@ int pvcreate_params_validate(struct cmd_context *cmd, pp->data_alignment = 0; } - if (arg_sign_value(cmd, dataalignmentoffset_ARG, 0) == SIGN_MINUS) { + if (arg_sign_value(cmd, dataalignmentoffset_ARG, SIGN_NONE) == SIGN_MINUS) { log_error("Physical volume data alignment offset may not be negative"); return 0; } pp->data_alignment_offset = arg_uint64_value(cmd, dataalignmentoffset_ARG, UINT64_C(0)); - if (pp->data_alignment_offset > ULONG_MAX) { + if (pp->data_alignment_offset > UINT32_MAX) { log_error("Physical volume data alignment offset is too big."); return 0; } @@ -1428,7 +1477,7 @@ int pvcreate_params_validate(struct cmd_context *cmd, pp->data_alignment_offset = 0; } - if (arg_sign_value(cmd, metadatasize_ARG, 0) == SIGN_MINUS) { + if (arg_sign_value(cmd, metadatasize_ARG, SIGN_NONE) == SIGN_MINUS) { log_error("Metadata size may not be negative"); return 0; } @@ -1449,7 +1498,6 @@ int pvcreate_params_validate(struct cmd_context *cmd, } int get_activation_monitoring_mode(struct cmd_context *cmd, - struct volume_group *vg, int *monitoring_mode) { *monitoring_mode = DEFAULT_DMEVENTD_MONITOR; @@ -1470,16 +1518,6 @@ int get_activation_monitoring_mode(struct cmd_context *cmd, DEFAULT_DMEVENTD_MONITOR)) *monitoring_mode = DMEVENTD_MONITOR_IGNORE; - if (vg && vg_is_clustered(vg) && - *monitoring_mode == DMEVENTD_MONITOR_IGNORE) { - log_error("%s is incompatible with clustered Volume Group " - "\"%s\": Skipping.", - (arg_count(cmd, ignoremonitoring_ARG) ? - "--ignoremonitoring" : "activation/monitoring=0"), - vg->name); - return 0; - } - return 1; } @@ -1490,13 +1528,13 @@ static int _validate_stripe_params(struct cmd_context *cmd, uint32_t *stripes, uint32_t *stripe_size) { if (*stripes == 1 && *stripe_size) { - log_print("Ignoring stripesize argument with single stripe"); + log_print_unless_silent("Ignoring stripesize argument with single stripe"); *stripe_size = 0; } if (*stripes > 1 && !*stripe_size) { *stripe_size = find_config_tree_int(cmd, "metadata/stripesize", DEFAULT_STRIPESIZE) * 2; - log_print("Using default stripesize %s", + log_print_unless_silent("Using default stripesize %s", display_size(cmd, (uint64_t) *stripe_size)); } @@ -1525,16 +1563,16 @@ static int _validate_stripe_params(struct cmd_context *cmd, uint32_t *stripes, int get_stripe_params(struct cmd_context *cmd, uint32_t *stripes, uint32_t *stripe_size) { /* stripes_long_ARG takes precedence (for lvconvert) */ - *stripes = arg_uint_value(cmd, arg_count(cmd, stripes_long_ARG) ? stripes_long_ARG : stripes_ARG, 1); + *stripes = arg_uint_value(cmd, arg_count(cmd, stripes_long_ARG) ? stripes_long_ARG : stripes_ARG, 1); *stripe_size = arg_uint_value(cmd, stripesize_ARG, 0); if (*stripe_size) { - if (arg_sign_value(cmd, stripesize_ARG, 0) == SIGN_MINUS) { + if (arg_sign_value(cmd, stripesize_ARG, SIGN_NONE) == SIGN_MINUS) { log_error("Negative stripesize is invalid"); return 0; } - if(*stripe_size > STRIPE_SIZE_LIMIT * 2) { + if(arg_uint64_value(cmd, stripesize_ARG, 0) > STRIPE_SIZE_LIMIT * 2) { log_error("Stripe size cannot be larger than %s", display_size(cmd, (uint64_t) STRIPE_SIZE_LIMIT)); return 0; @@ -1544,3 +1582,51 @@ int get_stripe_params(struct cmd_context *cmd, uint32_t *stripes, uint32_t *stri return _validate_stripe_params(cmd, stripes, stripe_size); } +/* FIXME move to lib */ +static int _pv_change_tag(struct physical_volume *pv, const char *tag, int addtag) +{ + if (addtag) { + if (!str_list_add(pv->fmt->cmd->mem, &pv->tags, tag)) { + log_error("Failed to add tag %s to physical volume %s", + tag, pv_dev_name(pv)); + return 0; + } + } else + str_list_del(&pv->tags, tag); + + return 1; +} + +/* Set exactly one of VG, LV or PV */ +int change_tag(struct cmd_context *cmd, struct volume_group *vg, + struct logical_volume *lv, struct physical_volume *pv, int arg) +{ + const char *tag; + struct arg_value_group_list *current_group; + + dm_list_iterate_items(current_group, &cmd->arg_value_groups) { + if (!grouped_arg_is_set(current_group->arg_values, arg)) + continue; + + if (!(tag = grouped_arg_str_value(current_group->arg_values, arg, NULL))) { + log_error("Failed to get tag"); + return 0; + } + + if (vg && !vg_change_tag(vg, tag, arg == addtag_ARG)) + return_0; + else if (lv && !lv_change_tag(lv, tag, arg == addtag_ARG)) + return_0; + else if (pv && !_pv_change_tag(pv, tag, arg == addtag_ARG)) + return_0; + } + + return 1; +} + +/* Return percents of extents and avoid overflow, with optional roundup */ +uint32_t percent_of_extents(uint32_t percents, uint32_t count, int roundup) +{ + return (uint32_t)(((uint64_t)percents * (uint64_t)count + + ((roundup) ? 99 : 0)) / 100); +} diff --git a/tools/toollib.h b/tools/toollib.h index 71e516f..b3b0a7c 100644 --- a/tools/toollib.h +++ b/tools/toollib.h @@ -84,8 +84,8 @@ int process_each_lv_in_vg(struct cmd_context *cmd, char *default_vgname(struct cmd_context *cmd); const char *extract_vgname(struct cmd_context *cmd, const char *lv_name); -char *skip_dev_dir(struct cmd_context *cmd, const char *vg_name, - unsigned *dev_dir_found); +const char *skip_dev_dir(struct cmd_context *cmd, const char *vg_name, + unsigned *dev_dir_found); /* * Builds a list of pv's from the names in argv. Used in @@ -110,9 +110,11 @@ int pvcreate_params_validate(struct cmd_context *cmd, struct pvcreate_params *pp); int get_activation_monitoring_mode(struct cmd_context *cmd, - struct volume_group *vg, int *monitoring_mode); int get_stripe_params(struct cmd_context *cmd, uint32_t *stripes, uint32_t *stripe_size); +int change_tag(struct cmd_context *cmd, struct volume_group *vg, + struct logical_volume *lv, struct physical_volume *pv, int arg); + #endif diff --git a/tools/tools.h b/tools/tools.h index cfbceeb..a3ad9fd 100644 --- a/tools/tools.h +++ b/tools/tools.h @@ -28,6 +28,7 @@ #include "activate.h" #include "archiver.h" #include "lvmcache.h" +#include "lvmetad.h" #include "config.h" #include "defaults.h" #include "dev-cache.h" @@ -87,14 +88,6 @@ typedef enum { PERCENT_ORIGIN } percent_type_t; -enum { - CHANGE_AY = 0, - CHANGE_AN = 1, - CHANGE_AE = 2, - CHANGE_ALY = 3, - CHANGE_ALN = 4 -}; - #define ARG_COUNTABLE 0x00000001 /* E.g. -vvvv */ #define ARG_GROUPABLE 0x00000002 /* E.g. --addtag */ @@ -145,7 +138,8 @@ void usage(const char *name); /* the argument verify/normalise functions */ int yes_no_arg(struct cmd_context *cmd, struct arg_values *av); -int yes_no_excl_arg(struct cmd_context *cmd, struct arg_values *av); +int activation_arg(struct cmd_context *cmd, struct arg_values *av); +int discards_arg(struct cmd_context *cmd, struct arg_values *av); int size_kb_arg(struct cmd_context *cmd, struct arg_values *av); int size_mb_arg(struct cmd_context *cmd, struct arg_values *av); int int_arg(struct cmd_context *cmd, struct arg_values *av); @@ -162,6 +156,8 @@ int segtype_arg(struct cmd_context *cmd, struct arg_values *av); int alloc_arg(struct cmd_context *cmd, struct arg_values *av); int readahead_arg(struct cmd_context *cmd, struct arg_values *av); int metadatacopies_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_values *av); +int major_minor_valid(const struct cmd_context * cmd, const struct format_type *fmt, + int32_t major, int32_t minor); /* we use the enums to access the switches */ unsigned arg_count(const struct cmd_context *cmd, int a); @@ -169,6 +165,7 @@ unsigned arg_is_set(const struct cmd_context *cmd, int a); const char *arg_value(struct cmd_context *cmd, int a); const char *arg_str_value(struct cmd_context *cmd, int a, const char *def); int32_t arg_int_value(struct cmd_context *cmd, int a, const int32_t def); +int32_t first_grouped_arg_int_value(struct cmd_context *cmd, int a, const int32_t def); uint32_t arg_uint_value(struct cmd_context *cmd, int a, const uint32_t def); int64_t arg_int64_value(struct cmd_context *cmd, int a, const int64_t def); uint64_t arg_uint64_value(struct cmd_context *cmd, int a, const uint64_t def); @@ -180,10 +177,18 @@ int arg_count_increment(struct cmd_context *cmd, int a); unsigned grouped_arg_count(const struct arg_values *av, int a); unsigned grouped_arg_is_set(const struct arg_values *av, int a); const char *grouped_arg_str_value(const struct arg_values *av, int a, const char *def); +int32_t grouped_arg_int_value(const struct arg_values *av, int a, const int32_t def); const char *command_name(struct cmd_context *cmd); int pvmove_poll(struct cmd_context *cmd, const char *pv, unsigned background); int lvconvert_poll(struct cmd_context *cmd, struct logical_volume *lv, unsigned background); +int mirror_remove_missing(struct cmd_context *cmd, + struct logical_volume *lv, int force); + +uint32_t percent_of_extents(uint32_t percents, uint32_t count, int roundup); + +int vgchange_activate(struct cmd_context *cmd, struct volume_group *vg, + activation_change_t activate); #endif diff --git a/tools/vgcfgbackup.c b/tools/vgcfgbackup.c index 2be3949..32948d8 100644 --- a/tools/vgcfgbackup.c +++ b/tools/vgcfgbackup.c @@ -28,7 +28,7 @@ static char *_expand_filename(const char *template, const char *vg_name, return NULL; } - if (snprintf(filename, PATH_MAX, template, vg_name) < 0) { + if (dm_snprintf(filename, PATH_MAX, template, vg_name) < 0) { log_error("Error processing filename template %s", template); dm_free(filename); @@ -81,7 +81,7 @@ static int vg_backup_single(struct cmd_context *cmd, const char *vg_name, } } - log_print("Volume group \"%s\" successfully backed up.", vg_name); + log_print_unless_silent("Volume group \"%s\" successfully backed up.", vg_name); return ECMD_PROCESSED; } diff --git a/tools/vgcfgrestore.c b/tools/vgcfgrestore.c index dc0158f..d62df99 100644 --- a/tools/vgcfgrestore.c +++ b/tools/vgcfgrestore.c @@ -17,7 +17,7 @@ int vgcfgrestore(struct cmd_context *cmd, int argc, char **argv) { - char *vg_name = NULL; + const char *vg_name = NULL; if (argc == 1) { vg_name = skip_dev_dir(cmd, argv[0], NULL); @@ -45,6 +45,8 @@ int vgcfgrestore(struct cmd_context *cmd, int argc, char **argv) return ECMD_PROCESSED; } + lvmcache_seed_infos_from_lvmetad(cmd); + if (!lock_vol(cmd, vg_name, LCK_VG_WRITE)) { log_error("Unable to lock volume group %s", vg_name); return ECMD_FAILED; @@ -68,7 +70,7 @@ int vgcfgrestore(struct cmd_context *cmd, int argc, char **argv) return ECMD_FAILED; } - log_print("Restored volume group %s", vg_name); + log_print_unless_silent("Restored volume group %s", vg_name); unlock_vg(cmd, VG_ORPHANS); unlock_vg(cmd, vg_name); diff --git a/tools/vgchange.c b/tools/vgchange.c index bf7ebd8..a897a85 100644 --- a/tools/vgchange.c +++ b/tools/vgchange.c @@ -24,21 +24,19 @@ static int _monitor_lvs_in_vg(struct cmd_context *cmd, struct lv_list *lvl; struct logical_volume *lv; struct lvinfo info; - int lv_active; int r = 1; dm_list_iterate_items(lvl, &vg->lvs) { lv = lvl->lv; - if (!lv_info(cmd, lv, 0, &info, 0, 0)) - lv_active = 0; - else - lv_active = info.exists; - + if (!lv_info(cmd, lv, lv_is_thin_pool(lv) ? 1 : 0, + &info, 0, 0) || + !info.exists) + continue; /* * FIXME: Need to consider all cases... PVMOVE, etc */ - if ((lv->status & PVMOVE) || !lv_active) + if (lv->status & PVMOVE) continue; if (!monitor_dev_for_events(cmd, lv, 0, reg)) { @@ -83,19 +81,27 @@ static int _poll_lvs_in_vg(struct cmd_context *cmd, return count; } -static int _activate_lvs_in_vg(struct cmd_context *cmd, - struct volume_group *vg, int activate) +static int _activate_lvs_in_vg(struct cmd_context *cmd, struct volume_group *vg, + activation_change_t activate) { struct lv_list *lvl; struct logical_volume *lv; int count = 0, expected_count = 0; + sigint_allow(); dm_list_iterate_items(lvl, &vg->lvs) { + if (sigint_caught()) + return_0; + lv = lvl->lv; if (!lv_is_visible(lv)) continue; + /* If LV is sparse, activate origin instead */ + if (lv_is_cow(lv) && lv_is_virtual_origin(origin_from_cow(lv))) + lv = origin_from_cow(lv); + /* Only request activation of snapshot origin devices */ if ((lv->status & SNAPSHOT) || lv_is_cow(lv)) continue; @@ -115,6 +121,19 @@ static int _activate_lvs_in_vg(struct cmd_context *cmd, ((lv->status & PVMOVE) )) continue; + /* + * If the LV is active exclusive remotely, + * then ignore it here + */ + if (lv_is_active_exclusive_remotely(lv)) { + log_verbose("%s/%s is exclusively active on" + " a remote node", vg->name, lv->name); + continue; + } + + if (activate == CHANGE_AAY && !lv_passes_auto_activation_filter(cmd, lv)) + continue; + expected_count++; if (activate == CHANGE_AN) { @@ -127,12 +146,15 @@ static int _activate_lvs_in_vg(struct cmd_context *cmd, stack; continue; } - } else if (lv_is_origin(lv) || (activate == CHANGE_AE)) { + } else if ((activate == CHANGE_AE) || + lv_is_origin(lv) || + lv_is_thin_type(lv)) { + /* FIXME: duplicated test code with lvchange */ if (!activate_lv_excl(cmd, lv)) { stack; continue; } - } else if (activate == CHANGE_ALY) { + } else if (activate == CHANGE_AAY || activate == CHANGE_ALY) { if (!activate_lv_local(cmd, lv)) { stack; continue; @@ -150,6 +172,8 @@ static int _activate_lvs_in_vg(struct cmd_context *cmd, count++; } + sigint_restore(); + if (expected_count) log_verbose("%s %d logical volumes in volume group %s", (activate == CHANGE_AN || activate == CHANGE_ALN)? @@ -167,9 +191,9 @@ static int _vgchange_monitoring(struct cmd_context *cmd, struct volume_group *vg dmeventd_monitor_mode() != DMEVENTD_MONITOR_IGNORE) { if (!_monitor_lvs_in_vg(cmd, vg, dmeventd_monitor_mode(), &monitored)) r = 0; - log_print("%d logical volume(s) in volume group " - "\"%s\" %smonitored", - monitored, vg->name, (dmeventd_monitor_mode()) ? "" : "un"); + log_print_unless_silent("%d logical volume(s) in volume group " + "\"%s\" %smonitored", + monitored, vg->name, (dmeventd_monitor_mode()) ? "" : "un"); } return r; @@ -182,43 +206,40 @@ static int _vgchange_background_polling(struct cmd_context *cmd, struct volume_g if (lvs_in_vg_activated(vg) && background_polling()) { polled = _poll_lvs_in_vg(cmd, vg); if (polled) - log_print("Background polling started for %d logical volume(s) " - "in volume group \"%s\"", - polled, vg->name); + log_print_unless_silent("Background polling started for %d logical volume(s) " + "in volume group \"%s\"", + polled, vg->name); } return 1; } -static int _vgchange_available(struct cmd_context *cmd, struct volume_group *vg) +int vgchange_activate(struct cmd_context *cmd, struct volume_group *vg, + activation_change_t activate) { - int lv_open, active, monitored = 0; - int available, r = 1; - int activate = 1; + int lv_open, active, monitored = 0, r = 1, do_activate = 1; + + if ((activate == CHANGE_AN) || (activate == CHANGE_ALN)) + do_activate = 0; /* * Safe, since we never write out new metadata here. Required for * partial activation to work. */ - cmd->handles_missing_pvs = 1; - - available = arg_uint_value(cmd, available_ARG, 0); - - if ((available == CHANGE_AN) || (available == CHANGE_ALN)) - activate = 0; + cmd->handles_missing_pvs = 1; /* FIXME: Force argument to deactivate them? */ - if (!activate && (lv_open = lvs_in_vg_opened(vg))) { + if (!do_activate && (lv_open = lvs_in_vg_opened(vg))) { log_error("Can't deactivate volume group \"%s\" with %d open " "logical volume(s)", vg->name, lv_open); return 0; } /* FIXME Move into library where clvmd can use it */ - if (activate) + if (do_activate) check_current_backup(vg); - if (activate && (active = lvs_in_vg_activated(vg))) { + if (do_activate && (active = lvs_in_vg_activated(vg))) { log_verbose("%d logical volume(s) in volume group \"%s\" " "already active", active, vg->name); if (dmeventd_monitor_mode() != DMEVENTD_MONITOR_IGNORE) { @@ -231,13 +252,13 @@ static int _vgchange_available(struct cmd_context *cmd, struct volume_group *vg) } } - if (!_activate_lvs_in_vg(cmd, vg, available)) + if (!_activate_lvs_in_vg(cmd, vg, activate)) r = 0; /* Print message only if there was not found a missing VG */ if (!vg->cmd_missing_vgs) - log_print("%d logical volume(s) in volume group \"%s\" now active", - lvs_in_vg_activated(vg), vg->name); + log_print_unless_silent("%d logical volume(s) in volume group \"%s\" now active", + lvs_in_vg_activated(vg), vg->name); return r; } @@ -257,7 +278,7 @@ static int _vgchange_alloc(struct cmd_context *cmd, struct volume_group *vg) { alloc_policy_t alloc; - alloc = arg_uint_value(cmd, alloc_ARG, ALLOC_NORMAL); + alloc = (alloc_policy_t) arg_uint_value(cmd, alloc_ARG, ALLOC_NORMAL); /* FIXME: make consistent with vg_set_alloc_policy() */ if (alloc == vg->alloc) { @@ -346,6 +367,12 @@ static int _vgchange_pesize(struct cmd_context *cmd, struct volume_group *vg) { uint32_t extent_size; + if (arg_uint64_value(cmd, physicalextentsize_ARG, 0) > MAX_EXTENT_SIZE) { + log_error("Physical extent size cannot be larger than %s", + display_size(cmd, (uint64_t) MAX_EXTENT_SIZE)); + return 1; + } + extent_size = arg_uint_value(cmd, physicalextentsize_ARG, 0); /* FIXME: remove check - redundant with vg_change_pesize */ if (extent_size == vg->extent_size) { @@ -360,37 +387,14 @@ static int _vgchange_pesize(struct cmd_context *cmd, struct volume_group *vg) return 1; } -static int _vgchange_tag(struct cmd_context *cmd, struct volume_group *vg, - int arg) -{ - const char *tag; - struct arg_value_group_list *current_group; - - dm_list_iterate_items(current_group, &cmd->arg_value_groups) { - if (!grouped_arg_is_set(current_group->arg_values, arg)) - continue; - - if (!(tag = grouped_arg_str_value(current_group->arg_values, arg, NULL))) { - log_error("Failed to get tag"); - return 0; - } - - if (!vg_change_tag(vg, tag, arg == addtag_ARG)) - return_0; - - } - - return 1; -} - static int _vgchange_addtag(struct cmd_context *cmd, struct volume_group *vg) { - return _vgchange_tag(cmd, vg, addtag_ARG); + return change_tag(cmd, vg, NULL, NULL, addtag_ARG); } static int _vgchange_deltag(struct cmd_context *cmd, struct volume_group *vg) { - return _vgchange_tag(cmd, vg, deltag_ARG); + return change_tag(cmd, vg, NULL, NULL, deltag_ARG); } static int _vgchange_uuid(struct cmd_context *cmd __attribute__((unused)), @@ -441,7 +445,6 @@ static int vgchange_single(struct cmd_context *cmd, const char *vg_name, struct volume_group *vg, void *handle __attribute__((unused))) { - int dmeventd_mode; int archived = 0; int i; @@ -467,11 +470,6 @@ static int vgchange_single(struct cmd_context *cmd, const char *vg_name, return ECMD_FAILED; } - if (!get_activation_monitoring_mode(cmd, vg, &dmeventd_mode)) - return ECMD_FAILED; - - init_dmeventd_monitor(dmeventd_mode); - /* * FIXME: DEFAULT_BACKGROUND_POLLING should be "unspecified". * If --poll is explicitly provided use it; otherwise polling @@ -507,11 +505,12 @@ static int vgchange_single(struct cmd_context *cmd, const char *vg_name, backup(vg); - log_print("Volume group \"%s\" successfully changed", vg->name); + log_print_unless_silent("Volume group \"%s\" successfully changed", vg->name); } - if (arg_count(cmd, available_ARG)) { - if (!_vgchange_available(cmd, vg)) + if (arg_count(cmd, activate_ARG)) { + if (!vgchange_activate(cmd, vg, (activation_change_t) + arg_uint_value(cmd, activate_ARG, CHANGE_AY))) return ECMD_FAILED; } @@ -521,7 +520,7 @@ static int vgchange_single(struct cmd_context *cmd, const char *vg_name, return ECMD_FAILED; } - if (!arg_count(cmd, available_ARG) && + if (!arg_count(cmd, activate_ARG) && !arg_count(cmd, refresh_ARG) && arg_count(cmd, monitor_ARG)) { /* -ay* will have already done monitoring changes */ @@ -530,7 +529,7 @@ static int vgchange_single(struct cmd_context *cmd, const char *vg_name, } if (!arg_count(cmd, refresh_ARG) && - arg_count(cmd, poll_ARG)) + background_polling()) if (!_vgchange_background_polling(cmd, vg)) return ECMD_FAILED; @@ -540,20 +539,22 @@ static int vgchange_single(struct cmd_context *cmd, const char *vg_name, int vgchange(struct cmd_context *cmd, int argc, char **argv) { /* Update commands that can be combined */ - int update = + int update_partial_safe = + arg_count(cmd, deltag_ARG) || + arg_count(cmd, addtag_ARG); + int update_partial_unsafe = arg_count(cmd, logicalvolume_ARG) || arg_count(cmd, maxphysicalvolumes_ARG) || arg_count(cmd, resizeable_ARG) || - arg_count(cmd, deltag_ARG) || - arg_count(cmd, addtag_ARG) || arg_count(cmd, uuid_ARG) || arg_count(cmd, physicalextentsize_ARG) || arg_count(cmd, clustered_ARG) || arg_count(cmd, alloc_ARG) || arg_count(cmd, vgmetadatacopies_ARG); + int update = update_partial_safe || update_partial_unsafe; if (!update && - !arg_count(cmd, available_ARG) && + !arg_count(cmd, activate_ARG) && !arg_count(cmd, monitor_ARG) && !arg_count(cmd, poll_ARG) && !arg_count(cmd, refresh_ARG)) { @@ -564,7 +565,7 @@ int vgchange(struct cmd_context *cmd, int argc, char **argv) return EINVALID_CMD_LINE; } - if (arg_count(cmd, available_ARG) && arg_count(cmd, refresh_ARG)) { + if (arg_count(cmd, activate_ARG) && arg_count(cmd, refresh_ARG)) { log_error("Only one of -a and --refresh permitted."); return EINVALID_CMD_LINE; } @@ -575,9 +576,9 @@ int vgchange(struct cmd_context *cmd, int argc, char **argv) return EINVALID_CMD_LINE; } - if (arg_count(cmd, available_ARG) && + if (arg_count(cmd, activate_ARG) && (arg_count(cmd, monitor_ARG) || arg_count(cmd, poll_ARG))) { - int activate = arg_uint_value(cmd, available_ARG, 0); + int activate = arg_uint_value(cmd, activate_ARG, 0); if (activate == CHANGE_AN || activate == CHANGE_ALN) { log_error("Only -ay* allowed with --monitor or --poll."); return EINVALID_CMD_LINE; @@ -589,24 +590,34 @@ int vgchange(struct cmd_context *cmd, int argc, char **argv) return EINVALID_CMD_LINE; } - if (arg_count(cmd, available_ARG) == 1 + if (arg_count(cmd, activate_ARG) == 1 && arg_count(cmd, autobackup_ARG)) { log_error("-A option not necessary with -a option"); return EINVALID_CMD_LINE; } if (arg_count(cmd, maxphysicalvolumes_ARG) && - arg_sign_value(cmd, maxphysicalvolumes_ARG, 0) == SIGN_MINUS) { + arg_sign_value(cmd, maxphysicalvolumes_ARG, SIGN_NONE) == SIGN_MINUS) { log_error("MaxPhysicalVolumes may not be negative"); return EINVALID_CMD_LINE; } if (arg_count(cmd, physicalextentsize_ARG) && - arg_sign_value(cmd, physicalextentsize_ARG, 0) == SIGN_MINUS) { + arg_sign_value(cmd, physicalextentsize_ARG, SIGN_NONE) == SIGN_MINUS) { log_error("Physical extent size may not be negative"); return EINVALID_CMD_LINE; } + if (arg_count(cmd, sysinit_ARG) && lvmetad_active() && + arg_uint_value(cmd, activate_ARG, 0) == CHANGE_AAY) { + log_warn("lvmetad is active while using --sysinit -a ay, " + "skipping manual activation"); + return ECMD_PROCESSED; + } + + if (!update || !update_partial_unsafe) + cmd->handles_missing_pvs = 1; + return process_each_vg(cmd, argc, argv, update ? READ_FOR_UPDATE : 0, NULL, &vgchange_single); } diff --git a/tools/vgconvert.c b/tools/vgconvert.c index acae0fc..2ba4095 100644 --- a/tools/vgconvert.c +++ b/tools/vgconvert.c @@ -22,11 +22,9 @@ static int vgconvert_single(struct cmd_context *cmd, const char *vg_name, struct physical_volume *pv, *existing_pv; struct logical_volume *lv; struct lv_list *lvl; - uint64_t size = 0; - struct dm_list mdas; int pvmetadatacopies = 0; uint64_t pvmetadatasize = 0; - uint64_t pe_end = 0, pe_start = 0; + uint64_t pe_start = 0; struct pv_list *pvl; int change_made = 0; struct lvinfo info; @@ -44,7 +42,7 @@ static int vgconvert_single(struct cmd_context *cmd, const char *vg_name, } if (cmd->fmt->features & FMT_MDAS) { - if (arg_sign_value(cmd, metadatasize_ARG, 0) == SIGN_MINUS) { + if (arg_sign_value(cmd, metadatasize_ARG, SIGN_NONE) == SIGN_MINUS) { log_error("Metadata size may not be negative"); return EINVALID_CMD_LINE; } @@ -119,15 +117,15 @@ static int vgconvert_single(struct cmd_context *cmd, const char *vg_name, existing_pv = pvl->pv; pe_start = pv_pe_start(existing_pv); - pe_end = pv_pe_count(existing_pv) * pv_pe_size(existing_pv) - + pe_start - 1; + /* pe_end = pv_pe_count(existing_pv) * pv_pe_size(existing_pv) + pe_start - 1; */ - dm_list_init(&mdas); if (!(pv = pv_create(cmd, pv_dev(existing_pv), - &existing_pv->id, size, 0, 0, + &existing_pv->id, 0, 0, 0, pe_start, pv_pe_count(existing_pv), - pv_pe_size(existing_pv), pvmetadatacopies, - pvmetadatasize, 0, &mdas))) { + pv_pe_size(existing_pv), + arg_int64_value(cmd, labelsector_ARG, + DEFAULT_LABELSECTOR), + pvmetadatacopies, pvmetadatasize, 0))) { log_error("Failed to setup physical volume \"%s\"", pv_dev_name(existing_pv)); if (change_made) @@ -153,9 +151,7 @@ static int vgconvert_single(struct cmd_context *cmd, const char *vg_name, log_very_verbose("Writing physical volume data to disk \"%s\"", pv_dev_name(pv)); - if (!(pv_write(cmd, pv, &mdas, - arg_int64_value(cmd, labelsector_ARG, - DEFAULT_LABELSECTOR)))) { + if (!(pv_write(cmd, pv, 0))) { log_error("Failed to write physical volume \"%s\"", pv_dev_name(pv)); log_error("Use pvcreate and vgcfgrestore to repair " @@ -164,7 +160,6 @@ static int vgconvert_single(struct cmd_context *cmd, const char *vg_name, } log_verbose("Physical volume \"%s\" successfully created", pv_dev_name(pv)); - } log_verbose("Deleting existing metadata for VG %s", vg_name); @@ -191,7 +186,7 @@ static int vgconvert_single(struct cmd_context *cmd, const char *vg_name, "archived metadata."); return ECMD_FAILED; } - log_print("Volume group %s successfully converted", vg_name); + log_print_unless_silent("Volume group %s successfully converted", vg_name); backup(vg); diff --git a/tools/vgcreate.c b/tools/vgcreate.c index 49574f3..2b9fb2f 100644 --- a/tools/vgcreate.c +++ b/tools/vgcreate.c @@ -49,6 +49,8 @@ int vgcreate(struct cmd_context *cmd, int argc, char **argv) if (vgcreate_params_validate(cmd, &vp_new)) return EINVALID_CMD_LINE; + lvmcache_seed_infos_from_lvmetad(cmd); + /* Create the new VG */ vg = vg_create(cmd, vp_new.vg_name); if (vg_read_error(vg)) { @@ -56,7 +58,7 @@ int vgcreate(struct cmd_context *cmd, int argc, char **argv) log_error("A volume group called %s already exists.", vp_new.vg_name); else log_error("Can't get lock for %s.", vp_new.vg_name); - free_vg(vg); + release_vg(vg); return ECMD_FAILED; } @@ -74,7 +76,7 @@ int vgcreate(struct cmd_context *cmd, int argc, char **argv) } /* attach the pv's */ - if (!vg_extend(vg, argc, argv, &pp)) + if (!vg_extend(vg, argc, (const char* const*)argv, &pp)) goto_bad; if (vp_new.max_lv != vg->max_lv) @@ -117,16 +119,16 @@ int vgcreate(struct cmd_context *cmd, int argc, char **argv) backup(vg); - log_print("%s%colume group \"%s\" successfully created", - clustered_message, *clustered_message ? 'v' : 'V', vg->name); + log_print_unless_silent("%s%colume group \"%s\" successfully created", + clustered_message, *clustered_message ? 'v' : 'V', vg->name); - free_vg(vg); + release_vg(vg); return ECMD_PROCESSED; bad: unlock_vg(cmd, VG_ORPHANS); bad_orphan: - free_vg(vg); + release_vg(vg); unlock_vg(cmd, vp_new.vg_name); return ECMD_FAILED; } diff --git a/tools/vgexport.c b/tools/vgexport.c index a1043d2..c573619 100644 --- a/tools/vgexport.c +++ b/tools/vgexport.c @@ -44,7 +44,7 @@ static int vgexport_single(struct cmd_context *cmd __attribute__((unused)), backup(vg); - log_print("Volume group \"%s\" successfully exported", vg->name); + log_print_unless_silent("Volume group \"%s\" successfully exported", vg->name); return ECMD_PROCESSED; diff --git a/tools/vgextend.c b/tools/vgextend.c index a0c166b..2ce7edb 100644 --- a/tools/vgextend.c +++ b/tools/vgextend.c @@ -40,7 +40,7 @@ static int _restore_pv(struct volume_group *vg, char *pv_name) int vgextend(struct cmd_context *cmd, int argc, char **argv) { - char *vg_name; + const char *vg_name; struct volume_group *vg = NULL; int r = ECMD_FAILED; struct pvcreate_params pp; @@ -66,13 +66,18 @@ int vgextend(struct cmd_context *cmd, int argc, char **argv) return EINVALID_CMD_LINE; } - if (arg_count(cmd, restoremissing_ARG)) - cmd->handles_missing_pvs = 1; + /* + * It is always ok to add new PVs to a VG - even if there are + * missing PVs. No LVs are affected by this operation, but + * repair processes - particularly for RAID segtypes - can + * be facilitated. + */ + cmd->handles_missing_pvs = 1; log_verbose("Checking for volume group \"%s\"", vg_name); vg = vg_read_for_update(cmd, vg_name, NULL, 0); if (vg_read_error(vg)) { - free_vg(vg); + release_vg(vg); stack; return ECMD_FAILED; } @@ -92,7 +97,7 @@ int vgextend(struct cmd_context *cmd, int argc, char **argv) } else { /* no --restore, normal vgextend */ if (!lock_vol(cmd, VG_ORPHANS, LCK_VG_WRITE)) { log_error("Can't get lock for orphan PVs"); - unlock_and_free_vg(cmd, vg, vg_name); + unlock_and_release_vg(cmd, vg, vg_name); return ECMD_FAILED; } @@ -107,7 +112,7 @@ int vgextend(struct cmd_context *cmd, int argc, char **argv) } /* extend vg */ - if (!vg_extend(vg, argc, argv, &pp)) + if (!vg_extend(vg, argc, (const char* const*)argv, &pp)) goto_bad; if (arg_count(cmd, metadataignore_ARG) && @@ -129,12 +134,12 @@ int vgextend(struct cmd_context *cmd, int argc, char **argv) goto_bad; backup(vg); - log_print("Volume group \"%s\" successfully extended", vg_name); + log_print_unless_silent("Volume group \"%s\" successfully extended", vg_name); r = ECMD_PROCESSED; bad: if (!arg_count(cmd, restoremissing_ARG)) unlock_vg(cmd, VG_ORPHANS); - unlock_and_free_vg(cmd, vg, vg_name); + unlock_and_release_vg(cmd, vg, vg_name); return r; } diff --git a/tools/vgimport.c b/tools/vgimport.c index 284f536..5badcb5 100644 --- a/tools/vgimport.c +++ b/tools/vgimport.c @@ -48,7 +48,7 @@ static int vgimport_single(struct cmd_context *cmd __attribute__((unused)), backup(vg); - log_print("Volume group \"%s\" successfully imported", vg->name); + log_print_unless_silent("Volume group \"%s\" successfully imported", vg->name); return ECMD_PROCESSED; diff --git a/tools/vgmerge.c b/tools/vgmerge.c index 391764f..2cecb05 100644 --- a/tools/vgmerge.c +++ b/tools/vgmerge.c @@ -22,7 +22,7 @@ static struct volume_group *_vgmerge_vg_read(struct cmd_context *cmd, log_verbose("Checking for volume group \"%s\"", vg_name); vg = vg_read_for_update(cmd, vg_name, NULL, 0); if (vg_read_error(vg)) { - free_vg(vg); + release_vg(vg); return NULL; } return vg; @@ -54,7 +54,7 @@ static int _vgmerge_single(struct cmd_context *cmd, const char *vg_name_to, vg_to = _vgmerge_vg_read(cmd, vg_name_to); if (!vg_to) { stack; - unlock_and_free_vg(cmd, vg_from, vg_name_from); + unlock_and_release_vg(cmd, vg_from, vg_name_from); return ECMD_FAILED; } } else { @@ -67,7 +67,7 @@ static int _vgmerge_single(struct cmd_context *cmd, const char *vg_name_to, vg_from = _vgmerge_vg_read(cmd, vg_name_from); if (!vg_from) { stack; - unlock_and_free_vg(cmd, vg_to, vg_name_to); + unlock_and_release_vg(cmd, vg_to, vg_name_to); return ECMD_FAILED; } } @@ -80,7 +80,8 @@ static int _vgmerge_single(struct cmd_context *cmd, const char *vg_name_to, if (!archive(vg_from) || !archive(vg_to)) goto_bad; - drop_cached_metadata(vg_from); + if (!drop_cached_metadata(vg_from)) + stack; /* Merge volume groups */ dm_list_iterate_items_safe(pvl, tpvl, &vg_from->pvs) { @@ -147,23 +148,23 @@ static int _vgmerge_single(struct cmd_context *cmd, const char *vg_name_to, /* FIXME Remove /dev/vgfrom */ backup(vg_to); - log_print("Volume group \"%s\" successfully merged into \"%s\"", - vg_from->name, vg_to->name); + log_print_unless_silent("Volume group \"%s\" successfully merged into \"%s\"", + vg_from->name, vg_to->name); r = ECMD_PROCESSED; bad: - if (lock_vg_from_first) { - unlock_and_free_vg(cmd, vg_to, vg_name_to); - unlock_and_free_vg(cmd, vg_from, vg_name_from); - } else { - unlock_and_free_vg(cmd, vg_from, vg_name_from); - unlock_and_free_vg(cmd, vg_to, vg_name_to); - } + /* + * Note: as vg_to is referencing moved elements from vg_from + * the order of release_vg calls is mandatory. + */ + unlock_and_release_vg(cmd, vg_to, vg_name_to); + unlock_and_release_vg(cmd, vg_from, vg_name_from); + return r; } int vgmerge(struct cmd_context *cmd, int argc, char **argv) { - char *vg_name_to, *vg_name_from; + const char *vg_name_to, *vg_name_from; int opt = 0; int ret = 0, ret_max = 0; diff --git a/tools/vgreduce.c b/tools/vgreduce.c index 4aa8f01..1b04a49 100644 --- a/tools/vgreduce.c +++ b/tools/vgreduce.c @@ -14,7 +14,6 @@ */ #include "tools.h" -#include "lv_alloc.h" static int _remove_pv(struct volume_group *vg, struct pv_list *pvl, int silent) { @@ -40,91 +39,7 @@ static int _remove_pv(struct volume_group *vg, struct pv_list *pvl, int silent) vg->free_count -= pvl->pv->pe_count; vg->extent_count -= pvl->pv->pe_count; del_pvl_from_vgs(vg, pvl); - - return 1; -} - -static int _remove_lv(struct cmd_context *cmd, struct logical_volume *lv, - int *list_unsafe, struct dm_list *lvs_changed) -{ - struct lv_segment *snap_seg; - struct dm_list *snh, *snht; - struct logical_volume *cow; - struct lv_list *lvl; - struct lvinfo info; - int first = 1; - - log_verbose("%s/%s has missing extents: removing (including " - "dependencies)", lv->vg->name, lv->name); - - /* FIXME Cope properly with stacked devices & snapshots. */ - - /* If snapshot device is missing, deactivate origin. */ - if (lv_is_cow(lv) && (snap_seg = find_cow(lv))) { - log_verbose("Deactivating (if active) logical volume %s " - "(origin of %s)", snap_seg->origin->name, lv->name); - - if (!test_mode() && !deactivate_lv(cmd, snap_seg->origin)) { - log_error("Failed to deactivate LV %s", - snap_seg->origin->name); - return 0; - } - - /* Use the origin LV */ - lv = snap_seg->origin; - } - - /* Remove snapshot dependencies */ - dm_list_iterate_safe(snh, snht, &lv->snapshot_segs) { - snap_seg = dm_list_struct_base(snh, struct lv_segment, - origin_list); - cow = snap_seg->cow; - - if (first && !test_mode() && - !deactivate_lv(cmd, snap_seg->origin)) { - log_error("Failed to deactivate LV %s", - snap_seg->origin->name); - return 0; - } - - *list_unsafe = 1; /* May remove caller's lvht! */ - if (!vg_remove_snapshot(cow)) - return_0; - log_verbose("Removing LV %s from VG %s", cow->name, - lv->vg->name); - if (!lv_remove(cow)) - return_0; - - first = 0; - } - - /* - * If LV is active, replace it with error segment - * and add to list of LVs to be removed later. - * Doesn't apply to snapshots/origins yet - they're already deactivated. - */ - /* - * If the LV is a part of mirror segment, - * the mirrored LV also should be cleaned up. - * Clean-up is currently done by caller (_make_vg_consistent()). - */ - if ((lv_info(cmd, lv, 0, &info, 0, 0) && info.exists) || - find_mirror_seg(first_seg(lv))) { - if (!replace_lv_with_error_segment(lv)) - return_0; - - if (!(lvl = dm_pool_alloc(cmd->mem, sizeof(*lvl)))) { - log_error("lv_list alloc failed"); - return 0; - } - lvl->lv = lv; - dm_list_add(lvs_changed, &lvl->list); - } else { - /* Remove LV immediately. */ - log_verbose("Removing LV %s from VG %s", lv->name, lv->vg->name); - if (!lv_remove(lv)) - return_0; - } + free_pv_fid(pvl->pv); return 1; } @@ -144,8 +59,8 @@ static int _consolidate_vg(struct cmd_context *cmd, struct volume_group *vg) if (!r) { cmd->handles_missing_pvs = 1; - log_warn("WARNING: There are still partial LVs in VG %s.", vg->name); - log_warn("To remove them unconditionally use: vgreduce --removemissing --force."); + log_error("There are still partial LVs in VG %s.", vg->name); + log_error("To remove them unconditionally use: vgreduce --removemissing --force."); log_warn("Proceeding to remove empty missing PVs."); } @@ -161,215 +76,41 @@ static int _consolidate_vg(struct cmd_context *cmd, struct volume_group *vg) static int _make_vg_consistent(struct cmd_context *cmd, struct volume_group *vg) { - struct dm_list *pvh, *pvht; - struct dm_list *lvh, *lvht; - struct pv_list *pvl; - struct lv_list *lvl, *lvl2, *lvlt; + struct lv_list *lvl; struct logical_volume *lv; - struct physical_volume *pv; - struct lv_segment *seg, *mirrored_seg; - unsigned s; - uint32_t mimages, remove_log; - int list_unsafe, only_mirror_images_found; - DM_LIST_INIT(lvs_changed); - only_mirror_images_found = 1; - - /* Deactivate & remove necessary LVs */ - restart_loop: - list_unsafe = 0; /* Set if we delete a different list-member */ - - dm_list_iterate_safe(lvh, lvht, &vg->lvs) { - lv = dm_list_item(lvh, struct lv_list)->lv; - - /* Are any segments of this LV on missing PVs? */ - dm_list_iterate_items(seg, &lv->segments) { - for (s = 0; s < seg->area_count; s++) { - if (seg_type(seg, s) != AREA_PV) - continue; - - /* FIXME Also check for segs on deleted LVs (incl pvmove) */ - - pv = seg_pv(seg, s); - if (!pv || !pv_dev(pv) || - is_missing_pv(pv)) { - if (arg_count(cmd, mirrorsonly_ARG) && - !(lv->status & MIRROR_IMAGE)) { - log_error("Non-mirror-image LV %s found: can't remove.", lv->name); - only_mirror_images_found = 0; - continue; - } - if (!_remove_lv(cmd, lv, &list_unsafe, &lvs_changed)) - return_0; - if (list_unsafe) - goto restart_loop; - } - } - } - } - - if (!only_mirror_images_found) { - log_error("Aborting because --mirrorsonly was specified."); - return 0; - } - /* - * Remove missing PVs. FIXME: This duplicates _consolidate_vg above, - * but we cannot use that right now, since the LV removal code in this - * function leaves the VG in a "somewhat inconsistent" state and - * _consolidate_vg doesn't like that -- specifically, mirrors are fixed - * up *after* the PVs are removed. All this should be gradually - * superseded by lvconvert --repair. - */ - dm_list_iterate_safe(pvh, pvht, &vg->pvs) { - pvl = dm_list_item(pvh, struct pv_list); - if (pvl->pv->dev && !is_missing_pv(pvl->pv)) - continue; - if (!_remove_pv(vg, pvl, 0)) - return_0; - } + cmd->partial_activation = 1; - /* FIXME Recovery. For now people must clean up by hand. */ + restart: + vg_mark_partial_lvs(vg, 1); - if (!dm_list_empty(&lvs_changed)) { - if (!vg_write(vg)) { - log_error("Failed to write out a consistent VG for %s", - vg->name); - return 0; - } + dm_list_iterate_items(lvl, &vg->lvs) { + lv = lvl->lv; - if (!test_mode()) { - /* Suspend lvs_changed */ - if (!suspend_lvs(cmd, &lvs_changed)) { - stack; - vg_revert(vg); - return 0; - } - } - - if (!vg_commit(vg)) { - log_error("Failed to commit consistent VG for %s", - vg->name); - vg_revert(vg); - return 0; - } - - if (!test_mode()) { - if (!resume_lvs(cmd, &lvs_changed)) { - log_error("Failed to resume LVs using error segments."); - return 0; - } - } - - lvs_changed_altered: - /* Remove lost mirror images from mirrors */ - dm_list_iterate_items(lvl, &vg->lvs) { - mirrored_seg_altered: - mirrored_seg = first_seg(lvl->lv); - if (!seg_is_mirrored(mirrored_seg)) - continue; - - mimages = mirrored_seg->area_count; - remove_log = 0; - - for (s = 0; s < mirrored_seg->area_count; s++) { - dm_list_iterate_items_safe(lvl2, lvlt, &lvs_changed) { - if (seg_type(mirrored_seg, s) != AREA_LV || - lvl2->lv != seg_lv(mirrored_seg, s)) - continue; - dm_list_del(&lvl2->list); - if (!shift_mirror_images(mirrored_seg, s)) - return_0; - mimages--; /* FIXME Assumes uniqueness */ - } - } - - if (mirrored_seg->log_lv) { - dm_list_iterate_items(seg, &mirrored_seg->log_lv->segments) { - /* FIXME: The second test shouldn't be required */ - if ((seg->segtype == - get_segtype_from_string(vg->cmd, "error"))) { - log_print("The log device for %s/%s has failed.", - vg->name, mirrored_seg->lv->name); - remove_log = 1; - break; - } - if (!strcmp(seg->segtype->name, "error")) { - log_print("Log device for %s/%s has failed.", - vg->name, mirrored_seg->lv->name); - remove_log = 1; - break; - } - } - } - - if ((mimages != mirrored_seg->area_count) || remove_log){ - if (!reconfigure_mirror_images(mirrored_seg, mimages, - NULL, remove_log)) + /* Are any segments of this LV on missing PVs? */ + if (lv->status & PARTIAL_LV) { + if (lv->status & MIRRORED) { + if (!mirror_remove_missing(cmd, lv, 1)) return_0; - - if (!vg_write(vg)) { - log_error("Failed to write out updated " - "VG for %s", vg->name); - return 0; - } - - if (!vg_commit(vg)) { - log_error("Failed to commit updated VG " - "for %s", vg->name); - vg_revert(vg); - return 0; - } - - /* mirrored LV no longer has valid mimages. - * So add it to lvs_changed for removal. - * For this LV may be an area of other mirror, - * restart the loop. */ - if (!mimages) { - if (!_remove_lv(cmd, lvl->lv, - &list_unsafe, &lvs_changed)) - return_0; - goto lvs_changed_altered; - } - - /* As a result of reconfigure_mirror_images(), - * first_seg(lv) may now be different seg. - * e.g. a temporary layer might be removed. - * So check the mirrored_seg again. */ - goto mirrored_seg_altered; + goto restart; } - } - /* Deactivate error LVs */ - if (!test_mode()) { - dm_list_iterate_items_safe(lvl, lvlt, &lvs_changed) { - log_verbose("Deactivating (if active) logical volume %s", - lvl->lv->name); - - if (!deactivate_lv(cmd, lvl->lv)) { - log_error("Failed to deactivate LV %s", - lvl->lv->name); - /* - * We failed to deactivate. - * Probably because this was a mirror log. - * Don't try to lv_remove it. - * Continue work on others. - */ - dm_list_del(&lvl->list); - } + if (arg_count(cmd, mirrorsonly_ARG) &&!(lv->status & MIRRORED)) { + log_error("Non-mirror-image LV %s found: can't remove.", lv->name); + continue; } - } - /* Remove remaining LVs */ - dm_list_iterate_items(lvl, &lvs_changed) { - log_verbose("Removing LV %s from VG %s", lvl->lv->name, - lvl->lv->vg->name); - /* Skip LVs already removed by mirror code */ - if (find_lv_in_vg(vg, lvl->lv->name) && - !lv_remove(lvl->lv)) - return_0; + if (!lv_is_visible(lv)) + continue; + log_warn("Removing partial LV %s.", lv->name); + if (!lv_remove_with_dependencies(cmd, lv, DONT_PROMPT, 0)) + return_0; + goto restart; } } + _consolidate_vg(cmd, vg); + return 1; } @@ -383,6 +124,11 @@ static int _vgreduce_single(struct cmd_context *cmd, struct volume_group *vg, int r = ECMD_FAILED; const char *name = pv_dev_name(pv); + if (!vg) { + log_error(INTERNAL_ERROR "VG is NULL."); + return ECMD_FAILED; + } + if (pv_pe_alloc_count(pv)) { log_error("Physical volume \"%s\" still in use", name); return ECMD_FAILED; @@ -438,7 +184,7 @@ static int _vgreduce_single(struct cmd_context *cmd, struct volume_group *vg, goto bad; } - if (!pv_write(cmd, pv, NULL, INT64_C(-1))) { + if (!pv_write(cmd, pv, 0)) { log_error("Failed to clear metadata from physical " "volume \"%s\" " "after removal from \"%s\"", name, vg->name); @@ -447,21 +193,24 @@ static int _vgreduce_single(struct cmd_context *cmd, struct volume_group *vg, backup(vg); - log_print("Removed \"%s\" from volume group \"%s\"", name, vg->name); + log_print_unless_silent("Removed \"%s\" from volume group \"%s\"", name, vg->name); r = ECMD_PROCESSED; bad: - unlock_and_free_vg(cmd, orphan_vg, VG_ORPHANS); + if (pvl) + free_pv_fid(pvl->pv); + unlock_and_release_vg(cmd, orphan_vg, VG_ORPHANS); return r; } int vgreduce(struct cmd_context *cmd, int argc, char **argv) { struct volume_group *vg; - char *vg_name; + const char *vg_name; int ret = ECMD_FAILED; int fixed = 1; int repairing = arg_count(cmd, removemissing_ARG); int saved_ignore_suspended_devices = ignore_suspended_devices(); + int locked = 0; if (!argc && !repairing) { log_error("Please give volume group name and " @@ -469,7 +218,7 @@ int vgreduce(struct cmd_context *cmd, int argc, char **argv) return EINVALID_CMD_LINE; } - if (!argc && repairing) { + if (!argc) { /* repairing */ log_error("Please give volume group name"); return EINVALID_CMD_LINE; } @@ -516,6 +265,8 @@ int vgreduce(struct cmd_context *cmd, int argc, char **argv) && !arg_count(cmd, removemissing_ARG)) goto_out; + locked = !vg_read_error(vg); + if (repairing) { if (!vg_read_error(vg) && !vg_missing_pv_count(vg)) { log_error("Volume group \"%s\" is already consistent", @@ -524,13 +275,14 @@ int vgreduce(struct cmd_context *cmd, int argc, char **argv) goto out; } - free_vg(vg); + release_vg(vg); log_verbose("Trying to open VG %s for recovery...", vg_name); vg = vg_read_for_update(cmd, vg_name, NULL, READ_ALLOW_INCONSISTENT | READ_ALLOW_EXPORTED); + locked |= !vg_read_error(vg); if (vg_read_error(vg) && vg_read_error(vg) != FAILED_READ_ONLY && vg_read_error(vg) != FAILED_INCONSISTENT) goto_out; @@ -552,8 +304,8 @@ int vgreduce(struct cmd_context *cmd, int argc, char **argv) backup(vg); if (fixed) { - log_print("Wrote out consistent volume group %s", - vg_name); + log_print_unless_silent("Wrote out consistent volume group %s", + vg_name); ret = ECMD_PROCESSED; } else ret = ECMD_FAILED; @@ -570,7 +322,10 @@ int vgreduce(struct cmd_context *cmd, int argc, char **argv) } out: init_ignore_suspended_devices(saved_ignore_suspended_devices); - unlock_and_free_vg(cmd, vg, vg_name); + if (locked) + unlock_vg(cmd, vg_name); + + release_vg(vg); return ret; diff --git a/tools/vgremove.c b/tools/vgremove.c index 67e3767..6804f2a 100644 --- a/tools/vgremove.c +++ b/tools/vgremove.c @@ -29,7 +29,7 @@ static int vgremove_single(struct cmd_context *cmd, const char *vg_name, lv_count = vg_visible_lvs(vg); - force = arg_count(cmd, force_ARG); + force = (force_t) arg_count(cmd, force_ARG); if (lv_count) { if (force == PROMPT) { if ((missing = vg_missing_pv_count(vg))) diff --git a/tools/vgrename.c b/tools/vgrename.c index 98be9a9..b476602 100644 --- a/tools/vgrename.c +++ b/tools/vgrename.c @@ -25,7 +25,7 @@ static struct volume_group *_get_old_vg_for_rename(struct cmd_context *cmd, nevertheless. */ vg = vg_read_for_update(cmd, vg_name_old, vgid, READ_ALLOW_EXPORTED); if (vg_read_error(vg)) { - free_vg(vg); + release_vg(vg); return_NULL; } @@ -63,7 +63,7 @@ static int vg_rename_path(struct cmd_context *cmd, const char *old_vg_path, int found_id = 0; struct dm_list *vgids; struct str_list *sl; - char *vg_name_new; + const char *vg_name_new; const char *vgid = NULL, *vg_name, *vg_name_old; char old_path[NAME_LEN], new_path[NAME_LEN]; struct volume_group *vg = NULL; @@ -79,6 +79,10 @@ static int vg_rename_path(struct cmd_context *cmd, const char *old_vg_path, log_verbose("Checking for existing volume group \"%s\"", vg_name_old); + /* populate lvmcache */ + if (!lvmetad_vg_list_to_lvmcache(cmd)) + stack; + /* Avoid duplicates */ if (!(vgids = get_vgids(cmd, 0)) || dm_list_empty(vgids)) { log_error("No complete volume groups found"); @@ -87,7 +91,7 @@ static int vg_rename_path(struct cmd_context *cmd, const char *old_vg_path, dm_list_iterate_items(sl, vgids) { vgid = sl->str; - if (!vgid || !(vg_name = vgname_from_vgid(NULL, vgid))) + if (!vgid || !(vg_name = lvmcache_vgname_from_vgid(NULL, vgid))) continue; if (!strcmp(vg_name, vg_name_old)) { if (match) { @@ -102,7 +106,7 @@ static int vg_rename_path(struct cmd_context *cmd, const char *old_vg_path, log_suppress(2); found_id = id_read_format(&id, vg_name_old); log_suppress(0); - if (found_id && (vg_name = vgname_from_vgid(cmd->mem, (char *)id.uuid))) { + if (found_id && (vg_name = lvmcache_vgname_from_vgid(cmd->mem, (char *)id.uuid))) { vg_name_old = vg_name; vgid = (char *)id.uuid; } else @@ -117,7 +121,7 @@ static int vg_rename_path(struct cmd_context *cmd, const char *old_vg_path, return_0; if (!_lock_new_vg_for_rename(cmd, vg_name_new)) { - unlock_and_free_vg(cmd, vg, vg_name_old); + unlock_and_release_vg(cmd, vg, vg_name_old); return_0; } } else { @@ -135,7 +139,8 @@ static int vg_rename_path(struct cmd_context *cmd, const char *old_vg_path, goto error; /* Remove references based on old name */ - drop_cached_metadata(vg); + if (!drop_cached_metadata(vg)) + stack; /* Change the volume group name */ vg_rename(cmd, vg, vg_name_new); @@ -164,17 +169,20 @@ static int vg_rename_path(struct cmd_context *cmd, const char *old_vg_path, } } - backup(vg); - backup_remove(cmd, vg_name_old); + if (!backup(vg)) + stack; + if (!backup_remove(cmd, vg_name_old)) + stack; unlock_vg(cmd, vg_name_new); - unlock_and_free_vg(cmd, vg, vg_name_old); + unlock_and_release_vg(cmd, vg, vg_name_old); - log_print("Volume group \"%s\" successfully renamed to \"%s\"", - vg_name_old, vg_name_new); + log_print_unless_silent("Volume group \"%s\" successfully renamed to \"%s\"", + vg_name_old, vg_name_new); /* FIXME lvmcache corruption - vginfo duplicated instead of renamed */ - persistent_filter_wipe(cmd->filter); + if (cmd->filter->wipe) + cmd->filter->wipe(cmd->filter); lvmcache_destroy(cmd, 1); return 1; @@ -182,9 +190,9 @@ static int vg_rename_path(struct cmd_context *cmd, const char *old_vg_path, error: if (lock_vg_old_first) { unlock_vg(cmd, vg_name_new); - unlock_and_free_vg(cmd, vg, vg_name_old); + unlock_and_release_vg(cmd, vg, vg_name_old); } else { - unlock_and_free_vg(cmd, vg, vg_name_old); + unlock_and_release_vg(cmd, vg, vg_name_old); unlock_vg(cmd, vg_name_new); } return 0; diff --git a/tools/vgscan.c b/tools/vgscan.c index 4e12914..99124ef 100644 --- a/tools/vgscan.c +++ b/tools/vgscan.c @@ -19,9 +19,9 @@ static int vgscan_single(struct cmd_context *cmd, const char *vg_name, struct volume_group *vg, void *handle __attribute__((unused))) { - log_print("Found %svolume group \"%s\" using metadata type %s", - vg_is_exported(vg) ? "exported " : "", vg_name, - vg->fid->fmt->name); + log_print_unless_silent("Found %svolume group \"%s\" using metadata type %s", + vg_is_exported(vg) ? "exported " : "", vg_name, + vg->fid->fmt->name); check_current_backup(vg); @@ -42,10 +42,23 @@ int vgscan(struct cmd_context *cmd, int argc, char **argv) return ECMD_FAILED; } - persistent_filter_wipe(cmd->filter); + if (cmd->filter->wipe) + cmd->filter->wipe(cmd->filter); lvmcache_destroy(cmd, 1); - log_print("Reading all physical volumes. This may take a while..."); + if (arg_count(cmd, cache_ARG)) { + if (lvmetad_active()) { + if (!lvmetad_pvscan_all_devs(cmd, NULL)) + return ECMD_FAILED; + } + else { + log_error("Cannot proceed since lvmetad is not active."); + unlock_vg(cmd, VG_GLOBAL); + return ECMD_FAILED; + } + } + + log_print_unless_silent("Reading all physical volumes. This may take a while..."); maxret = process_each_vg(cmd, argc, argv, 0, NULL, &vgscan_single); diff --git a/tools/vgsplit.c b/tools/vgsplit.c index bd2f0ab..3bbb9fa 100644 --- a/tools/vgsplit.c +++ b/tools/vgsplit.c @@ -163,7 +163,7 @@ static int _move_mirrors(struct volume_group *vg_from, { struct dm_list *lvh, *lvht; struct logical_volume *lv; - struct lv_segment *seg; + struct lv_segment *seg, *log_seg; unsigned s, seg_in, log_in; dm_list_iterate_safe(lvh, lvht, &vg_from->lvs) { @@ -179,7 +179,20 @@ static int _move_mirrors(struct volume_group *vg_from, if (_lv_is_in_vg(vg_to, seg_lv(seg, s))) seg_in++; - log_in = (!seg->log_lv || _lv_is_in_vg(vg_to, seg->log_lv)); + log_in = !seg->log_lv; + if (seg->log_lv) { + log_seg = first_seg(seg->log_lv); + if (seg_is_mirrored(log_seg)) { + log_in = 1; + + /* Ensure each log dev is in vg_to */ + for (s = 0; s < log_seg->area_count; s++) + log_in = log_in && + _lv_is_in_vg(vg_to, + seg_lv(log_seg, s)); + } else + log_in = _lv_is_in_vg(vg_to, seg->log_lv); + } if ((seg_in && seg_in < seg->area_count) || (seg_in && seg->log_lv && !log_in) || @@ -224,16 +237,16 @@ static struct volume_group *_vgsplit_to(struct cmd_context *cmd, vg_to = vg_create(cmd, vg_name_to); if (vg_read_error(vg_to) == FAILED_LOCKING) { log_error("Can't get lock for %s", vg_name_to); - free_vg(vg_to); + release_vg(vg_to); return NULL; } if (vg_read_error(vg_to) == FAILED_EXIST) { *existing_vg = 1; - free_vg(vg_to); + release_vg(vg_to); vg_to = vg_read_for_update(cmd, vg_name_to, NULL, 0); if (vg_read_error(vg_to)) { - free_vg(vg_to); + release_vg(vg_to); stack; return NULL; } @@ -259,7 +272,7 @@ static struct volume_group *_vgsplit_from(struct cmd_context *cmd, vg_from = vg_read_for_update(cmd, vg_name_from, NULL, 0); if (vg_read_error(vg_from)) { - free_vg(vg_from); + release_vg(vg_from); return NULL; } return vg_from; @@ -281,7 +294,7 @@ int vgsplit(struct cmd_context *cmd, int argc, char **argv) { struct vgcreate_params vp_new; struct vgcreate_params vp_def; - char *vg_name_from, *vg_name_to; + const char *vg_name_from, *vg_name_to; struct volume_group *vg_to = NULL, *vg_from = NULL; int opt; int existing_vg = 0; @@ -334,7 +347,7 @@ int vgsplit(struct cmd_context *cmd, int argc, char **argv) vg_to = _vgsplit_to(cmd, vg_name_to, &existing_vg); if (!vg_to) { - unlock_and_free_vg(cmd, vg_from, vg_name_from); + unlock_and_release_vg(cmd, vg_from, vg_name_from); stack; return ECMD_FAILED; } @@ -346,7 +359,7 @@ int vgsplit(struct cmd_context *cmd, int argc, char **argv) } vg_from = _vgsplit_from(cmd, vg_name_from); if (!vg_from) { - unlock_and_free_vg(cmd, vg_to, vg_name_to); + unlock_and_release_vg(cmd, vg_to, vg_name_to); stack; return ECMD_FAILED; } @@ -395,7 +408,7 @@ int vgsplit(struct cmd_context *cmd, int argc, char **argv) /* Move PVs across to new structure */ for (opt = 0; opt < argc; opt++) { - unescape_colons_and_at_signs(argv[opt], NULL, NULL); + dm_unescape_colons_and_at_signs(argv[opt], NULL, NULL); if (!move_pv(vg_from, vg_to, argv[opt])) goto_bad; } @@ -463,7 +476,7 @@ int vgsplit(struct cmd_context *cmd, int argc, char **argv) * Finally, remove the EXPORTED flag from the new VG and write it out. */ if (!test_mode()) { - free_vg(vg_to); + release_vg(vg_to); vg_to = vg_read_for_update(cmd, vg_name_to, NULL, READ_ALLOW_EXPORTED); if (vg_read_error(vg_to)) { @@ -480,19 +493,19 @@ int vgsplit(struct cmd_context *cmd, int argc, char **argv) backup(vg_to); - log_print("%s volume group \"%s\" successfully split from \"%s\"", - existing_vg ? "Existing" : "New", - vg_to->name, vg_from->name); + log_print_unless_silent("%s volume group \"%s\" successfully split from \"%s\"", + existing_vg ? "Existing" : "New", + vg_to->name, vg_from->name); r = ECMD_PROCESSED; bad: - if (lock_vg_from_first) { - unlock_and_free_vg(cmd, vg_to, vg_name_to); - unlock_and_free_vg(cmd, vg_from, vg_name_from); - } else { - unlock_and_free_vg(cmd, vg_from, vg_name_from); - unlock_and_free_vg(cmd, vg_to, vg_name_to); - } + /* + * vg_to references elements moved from vg_from + * so vg_to has to be freed first. + */ + unlock_and_release_vg(cmd, vg_to, vg_name_to); + unlock_and_release_vg(cmd, vg_from, vg_name_from); + return r; } |