diff options
104 files changed, 6156 insertions, 4327 deletions
@@ -6,8 +6,8 @@ LVM2 installation Ensure the device-mapper has been installed on the machine. The device-mapper should be in the kernel (look for 'device-mapper' - messages in the kernel logs) and /usr/include/libdevmapper.h should - be present. + messages in the kernel logs) and /usr/include/libdevmapper.h + and libdevmapper.so should be present. The device-mapper is available from: ftp://ftp.sistina.com/pub/LVM2/device-mapper/ @@ -17,9 +17,15 @@ LVM2 installation Run the 'configure' script from the top directory. - If you do not have GNU readline (http://www.gnu.org/directory/readline.html) - installed use - ./configure --disable-readline + If you wish to use the built-in LVM2 shell and have GNU readline + installed (http://www.gnu.org/directory/readline.html) use: + ./configure --enable-readline + + If you don't want to include the LVM1 backwards-compatibility code use: + ./configure --with-lvm1=none + + To separate the LVM1 support into a shared library loaded by lvm.conf use: + ./configure --with-lvm1=shared 3) Build and install LVM2. @@ -31,6 +37,9 @@ LVM2 installation The tools will work fine without a configuration file being present, but you ought to review the example file in doc/example.conf. - For example, specifying the devices that LVM2 is to use should + For example, specifying the devices that LVM2 is to use can make the tools run more efficiently - and avoid scanning /dev/cdrom! +Please also refer to the WHATS_NEW file and the manual pages for the +individual commands. + diff --git a/INTRO b/INTRO deleted file mode 100644 index bc54a164a..000000000 --- a/INTRO +++ /dev/null @@ -1,18 +0,0 @@ -An introduction to LVM2 -======================= - -Background - - -Compatibility with LVM1 - - -New features - - -Missing features - - -Future enhancements - - diff --git a/Makefile.in b/Makefile.in index ef815a4ec..ed1fd7ca3 100644 --- a/Makefile.in +++ b/Makefile.in @@ -23,7 +23,8 @@ VPATH = @srcdir@ SUBDIRS = include man lib tools ifeq ($(MAKECMDGOALS),distclean) - SUBDIRS += test/mm test/device test/format1 test/regex test/filters + SUBDIRS += lib/format1 \ + test/mm test/device test/format1 test/regex test/filters endif include make.tmpl @@ -1,10 +1,10 @@ -This directory contains a beta release of LMV2, the new version of +This directory contains a beta release of LVM2, the new version of the userland LVM tools designed for the new device-mapper for the Linux kernel. The device-mapper needs to be installed before compiling these LVM2 tools. -For more information about LVM2 read the INTRO file. +For more information about LVM2 read the WHATS_NEW file. Installation instructions are in INSTALL. This is beta-quality software, released for testing purposes only. @@ -1,28 +0,0 @@ -before 2.0 ------------ - -vgexport -vgimport -snapshots -pvmove -device-mapper support for 2.5 kernel series -review FIXMEs -extra validation & full consistency checks in format1 with LVM1 -partial activation (aka VG quorum) -error message review -locking during metadata changes -format2 with atomic transactions -bidirectional format1/format2 migration tool -persistent minors -statistics target and tool support -review tool exit codes for LVM1 compatibility - -before 2.1 ----------- - -e2fsadm -lvmsadc -lvmsar -pvdata -vgsplit -vgmknodes @@ -1 +1 @@ -1.95.10-cvs (2002-05-31) +1.95.11-cvs (2002-11-18) diff --git a/WHATS_NEW b/WHATS_NEW new file mode 100644 index 000000000..26c8fd340 --- /dev/null +++ b/WHATS_NEW @@ -0,0 +1,86 @@ +Mondy 18th November 2002 +======================== + +The new format of LVM metadata is ready for you to test! + We expect it to be more efficient and more robust than the original format. + It's more compact and supports transactional changes and replication. + Should things go wrong on a system, it's human-readable (and editable). + +Please report any problems you find to the mailing list, +linux-lvm@sistina.com. The software has NOT yet been thoroughly +tested and so quite possibly there'll still be some bugs in it. +Be aware of the disclaimer in the COPYING file. + +While testing, we recommend turning logging on in the configuration file +to provide us with diagnostic information: + log { + file="/tmp/lvm2.log" + level=6 + } + +You should schedule regular backups of your configuration file and +metadata backups and archives (normally kept under /etc/lvm). + +Please read docs/example.conf and "man lvm.conf" to find out more about +the configuration file. + +To convert an existing volume group called vg1 to the new format using +the default settings, use "vgconvert -M2 vg1". See "man vgconvert". + +-M (or --metadatatype in its long form) is a new flag to indicate which +format of metadata the command should use for anything it creates. +Currently, the valid types are "lvm1" and "lvm2" and they can be +abbreviated to "1" and "2" respectively. The default value for this +flag can be changed in the global section in the config file. + +Backwards-compatible support for the original LVM1 metadata format is +maintained, but it can be moved into a shared library or removed +completely with configure's --with-lvm1 option. + +Under LVM2, the basic unit of metadata is the volume group. Different +volume groups can use different formats of metadata - vg1 could use +the original LVM1 format while vg2 used the new format - but you can't +mix formats within a volume group. So to add a PV to an LVM2-format +volume group you must run "pvcreate -M2" on it, followed by "vgextend". + +With LVM2-format metadata, lvextend will let you specify striping +parameters. So an LV could consist of two or more "segments" - the +first segment could have 3 stripes while the second segment has just 2. + +LVM2 maintains a backup of the current metadata for each volume group +in /etc/lvm/backup, and puts copies of previous versions in +/etc/lvm/archive. "vgcfgbackup" and "vgcfgrestore" can be used to +create and restore from these files. If you fully understand what +you're doing, metadata can be changed by editing a copy of a current +backup file and using vgcfgrestore to reload it. + +Please read the pvcreate man page for more information on the new +format for metadata. + +All tools that can change things have a --test flag which can be used +to check the effect of a set of cmdline args without really making the +changes. + + +What's not finished? +==================== +The internal cache. If you turn on debugging output you'll see lots of +repeated disk reads, many of which will eventually get optimised out. + +--test sometimes causes a command to fail (e.g. vgconvert --test) even +though the real command would work: again, fixing this is waiting for +the work on the cache. + +Several of the tools do not yet contain the logic to handle full +recovery: combinations of pvcreate and vgcfgrestore may sometimes be +needed to restore metadata if a tool gets interrupted or crashes or +finds something unexpected. This applies particularly to tools that +work on more than one volume group at once (e.g. vgsplit). + +Display output. Some metadata information cannot yet be displayed. +Work has started on new display tools. + +Recovery tools to salvage "lost" metadata directly from the disks: +but we hope the new format will mean such tools are hardly ever needed! + + @@ -17,12 +17,15 @@ ac_help="$ac_help ac_help="$ac_help --with-group=GROUP Set the group owner of installed files " ac_help="$ac_help + --with-lvm1=TYPE LVM1 metadata support: internal/shared/none + [TYPE=internal] " +ac_help="$ac_help --enable-jobs=NUM Number of jobs to run simultaneously" ac_help="$ac_help --enable-static_link Use this to link the tools to the liblvm library statically. Default is dynamic linking" ac_help="$ac_help - --disable-readline Disable readline support" + --enable-readline Enable readline support" # Initialize some variables set by options. # The variables have the same names as the options, with @@ -559,7 +562,7 @@ do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:563: checking for $ac_word" >&5 +echo "configure:566: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_prog_AWK'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -591,7 +594,7 @@ done # Extract the first word of "gcc", so it can be a program name with args. set dummy gcc; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:595: checking for $ac_word" >&5 +echo "configure:598: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -621,7 +624,7 @@ if test -z "$CC"; then # Extract the first word of "cc", so it can be a program name with args. set dummy cc; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:625: checking for $ac_word" >&5 +echo "configure:628: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -672,7 +675,7 @@ fi # Extract the first word of "cl", so it can be a program name with args. set dummy cl; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:676: checking for $ac_word" >&5 +echo "configure:679: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -704,7 +707,7 @@ fi fi echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works""... $ac_c" 1>&6 -echo "configure:708: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works" >&5 +echo "configure:711: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works" >&5 ac_ext=c # CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. @@ -715,12 +718,12 @@ cross_compiling=$ac_cv_prog_cc_cross cat > conftest.$ac_ext << EOF -#line 719 "configure" +#line 722 "configure" #include "confdefs.h" main(){return(0);} EOF -if { (eval echo configure:724: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then +if { (eval echo configure:727: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then ac_cv_prog_cc_works=yes # If we can't run a trivial program, we are probably using a cross compiler. if (./conftest; exit) 2>/dev/null; then @@ -746,12 +749,12 @@ if test $ac_cv_prog_cc_works = no; then { echo "configure: error: installation or configuration problem: C compiler cannot create executables." 1>&2; exit 1; } fi echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler""... $ac_c" 1>&6 -echo "configure:750: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler" >&5 +echo "configure:753: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler" >&5 echo "$ac_t""$ac_cv_prog_cc_cross" 1>&6 cross_compiling=$ac_cv_prog_cc_cross echo $ac_n "checking whether we are using GNU C""... $ac_c" 1>&6 -echo "configure:755: checking whether we are using GNU C" >&5 +echo "configure:758: checking whether we are using GNU C" >&5 if eval "test \"`echo '$''{'ac_cv_prog_gcc'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -760,7 +763,7 @@ else yes; #endif EOF -if { ac_try='${CC-cc} -E conftest.c'; { (eval echo configure:764: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then +if { ac_try='${CC-cc} -E conftest.c'; { (eval echo configure:767: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then ac_cv_prog_gcc=yes else ac_cv_prog_gcc=no @@ -779,7 +782,7 @@ ac_test_CFLAGS="${CFLAGS+set}" ac_save_CFLAGS="$CFLAGS" CFLAGS= echo $ac_n "checking whether ${CC-cc} accepts -g""... $ac_c" 1>&6 -echo "configure:783: checking whether ${CC-cc} accepts -g" >&5 +echo "configure:786: checking whether ${CC-cc} accepts -g" >&5 if eval "test \"`echo '$''{'ac_cv_prog_cc_g'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -822,7 +825,7 @@ fi # SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" # ./install, which can be erroneously created by make from ./install.sh. echo $ac_n "checking for a BSD compatible install""... $ac_c" 1>&6 -echo "configure:826: checking for a BSD compatible install" >&5 +echo "configure:829: checking for a BSD compatible install" >&5 if test -z "$INSTALL"; then if eval "test \"`echo '$''{'ac_cv_path_install'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 @@ -875,7 +878,7 @@ test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL_PROGRAM}' test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' echo $ac_n "checking whether ln -s works""... $ac_c" 1>&6 -echo "configure:879: checking whether ln -s works" >&5 +echo "configure:882: checking whether ln -s works" >&5 if eval "test \"`echo '$''{'ac_cv_prog_LN_S'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -896,7 +899,7 @@ else fi echo $ac_n "checking whether ${MAKE-make} sets \${MAKE}""... $ac_c" 1>&6 -echo "configure:900: checking whether ${MAKE-make} sets \${MAKE}" >&5 +echo "configure:903: checking whether ${MAKE-make} sets \${MAKE}" >&5 set dummy ${MAKE-make}; ac_make=`echo "$2" | sed 'y%./+-%__p_%'` if eval "test \"`echo '$''{'ac_cv_prog_make_${ac_make}_set'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 @@ -925,7 +928,7 @@ fi # Extract the first word of "ranlib", so it can be a program name with args. set dummy ranlib; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:929: checking for $ac_word" >&5 +echo "configure:932: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_prog_RANLIB'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -958,12 +961,12 @@ for ac_hdr in dirent.h sys/ndir.h sys/dir.h ndir.h do ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'` echo $ac_n "checking for $ac_hdr that defines DIR""... $ac_c" 1>&6 -echo "configure:962: checking for $ac_hdr that defines DIR" >&5 +echo "configure:965: checking for $ac_hdr that defines DIR" >&5 if eval "test \"`echo '$''{'ac_cv_header_dirent_$ac_safe'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <<EOF -#line 967 "configure" +#line 970 "configure" #include "confdefs.h" #include <sys/types.h> #include <$ac_hdr> @@ -971,7 +974,7 @@ int main() { DIR *dirp = 0; ; return 0; } EOF -if { (eval echo configure:975: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then +if { (eval echo configure:978: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* eval "ac_cv_header_dirent_$ac_safe=yes" else @@ -996,7 +999,7 @@ done # Two versions of opendir et al. are in -ldir and -lx on SCO Xenix. if test $ac_header_dirent = dirent.h; then echo $ac_n "checking for opendir in -ldir""... $ac_c" 1>&6 -echo "configure:1000: checking for opendir in -ldir" >&5 +echo "configure:1003: checking for opendir in -ldir" >&5 ac_lib_var=`echo dir'_'opendir | sed 'y%./+-%__p_%'` if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 @@ -1004,7 +1007,7 @@ else ac_save_LIBS="$LIBS" LIBS="-ldir $LIBS" cat > conftest.$ac_ext <<EOF -#line 1008 "configure" +#line 1011 "configure" #include "confdefs.h" /* Override any gcc2 internal prototype to avoid an error. */ /* We use char because int might match the return type of a gcc2 @@ -1015,7 +1018,7 @@ int main() { opendir() ; return 0; } EOF -if { (eval echo configure:1019: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then +if { (eval echo configure:1022: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_lib_$ac_lib_var=yes" else @@ -1037,7 +1040,7 @@ fi else echo $ac_n "checking for opendir in -lx""... $ac_c" 1>&6 -echo "configure:1041: checking for opendir in -lx" >&5 +echo "configure:1044: checking for opendir in -lx" >&5 ac_lib_var=`echo x'_'opendir | sed 'y%./+-%__p_%'` if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 @@ -1045,7 +1048,7 @@ else ac_save_LIBS="$LIBS" LIBS="-lx $LIBS" cat > conftest.$ac_ext <<EOF -#line 1049 "configure" +#line 1052 "configure" #include "confdefs.h" /* Override any gcc2 internal prototype to avoid an error. */ /* We use char because int might match the return type of a gcc2 @@ -1056,7 +1059,7 @@ int main() { opendir() ; return 0; } EOF -if { (eval echo configure:1060: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then +if { (eval echo configure:1063: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_lib_$ac_lib_var=yes" else @@ -1079,7 +1082,7 @@ fi fi echo $ac_n "checking how to run the C preprocessor""... $ac_c" 1>&6 -echo "configure:1083: checking how to run the C preprocessor" >&5 +echo "configure:1086: checking how to run the C preprocessor" >&5 # On Suns, sometimes $CPP names a directory. if test -n "$CPP" && test -d "$CPP"; then CPP= @@ -1094,13 +1097,13 @@ else # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. cat > conftest.$ac_ext <<EOF -#line 1098 "configure" +#line 1101 "configure" #include "confdefs.h" #include <assert.h> Syntax Error EOF ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" -{ (eval echo configure:1104: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +{ (eval echo configure:1107: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` if test -z "$ac_err"; then : @@ -1111,13 +1114,13 @@ else rm -rf conftest* CPP="${CC-cc} -E -traditional-cpp" cat > conftest.$ac_ext <<EOF -#line 1115 "configure" +#line 1118 "configure" #include "confdefs.h" #include <assert.h> Syntax Error EOF ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" -{ (eval echo configure:1121: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +{ (eval echo configure:1124: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` if test -z "$ac_err"; then : @@ -1128,13 +1131,13 @@ else rm -rf conftest* CPP="${CC-cc} -nologo -E" cat > conftest.$ac_ext <<EOF -#line 1132 "configure" +#line 1135 "configure" #include "confdefs.h" #include <assert.h> Syntax Error EOF ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" -{ (eval echo configure:1138: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +{ (eval echo configure:1141: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` if test -z "$ac_err"; then : @@ -1159,12 +1162,12 @@ fi echo "$ac_t""$CPP" 1>&6 echo $ac_n "checking for ANSI C header files""... $ac_c" 1>&6 -echo "configure:1163: checking for ANSI C header files" >&5 +echo "configure:1166: checking for ANSI C header files" >&5 if eval "test \"`echo '$''{'ac_cv_header_stdc'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <<EOF -#line 1168 "configure" +#line 1171 "configure" #include "confdefs.h" #include <stdlib.h> #include <stdarg.h> @@ -1172,7 +1175,7 @@ else #include <float.h> EOF ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" -{ (eval echo configure:1176: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +{ (eval echo configure:1179: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` if test -z "$ac_err"; then rm -rf conftest* @@ -1189,7 +1192,7 @@ rm -f conftest* if test $ac_cv_header_stdc = yes; then # SunOS 4.x string.h does not declare mem*, contrary to ANSI. cat > conftest.$ac_ext <<EOF -#line 1193 "configure" +#line 1196 "configure" #include "confdefs.h" #include <string.h> EOF @@ -1207,7 +1210,7 @@ fi if test $ac_cv_header_stdc = yes; then # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. cat > conftest.$ac_ext <<EOF -#line 1211 "configure" +#line 1214 "configure" #include "confdefs.h" #include <stdlib.h> EOF @@ -1228,7 +1231,7 @@ if test "$cross_compiling" = yes; then : else cat > conftest.$ac_ext <<EOF -#line 1232 "configure" +#line 1235 "configure" #include "confdefs.h" #include <ctype.h> #define ISLOWER(c) ('a' <= (c) && (c) <= 'z') @@ -1239,7 +1242,7 @@ if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) exit(2); exit (0); } EOF -if { (eval echo configure:1243: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null +if { (eval echo configure:1246: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null then : else @@ -1266,17 +1269,17 @@ for ac_hdr in fcntl.h malloc.h sys/ioctl.h unistd.h do ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'` echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6 -echo "configure:1270: checking for $ac_hdr" >&5 +echo "configure:1273: checking for $ac_hdr" >&5 if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <<EOF -#line 1275 "configure" +#line 1278 "configure" #include "confdefs.h" #include <$ac_hdr> EOF ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" -{ (eval echo configure:1280: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +{ (eval echo configure:1283: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` if test -z "$ac_err"; then rm -rf conftest* @@ -1304,12 +1307,12 @@ done echo $ac_n "checking for working const""... $ac_c" 1>&6 -echo "configure:1308: checking for working const" >&5 +echo "configure:1311: checking for working const" >&5 if eval "test \"`echo '$''{'ac_cv_c_const'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <<EOF -#line 1313 "configure" +#line 1316 "configure" #include "confdefs.h" int main() { @@ -1358,7 +1361,7 @@ ccp = (char const *const *) p; ; return 0; } EOF -if { (eval echo configure:1362: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then +if { (eval echo configure:1365: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* ac_cv_c_const=yes else @@ -1379,21 +1382,21 @@ EOF fi echo $ac_n "checking for inline""... $ac_c" 1>&6 -echo "configure:1383: checking for inline" >&5 +echo "configure:1386: checking for inline" >&5 if eval "test \"`echo '$''{'ac_cv_c_inline'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else ac_cv_c_inline=no for ac_kw in inline __inline__ __inline; do cat > conftest.$ac_ext <<EOF -#line 1390 "configure" +#line 1393 "configure" #include "confdefs.h" int main() { } int $ac_kw foo() { ; return 0; } EOF -if { (eval echo configure:1397: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then +if { (eval echo configure:1400: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* ac_cv_c_inline=$ac_kw; break else @@ -1419,12 +1422,12 @@ EOF esac echo $ac_n "checking for off_t""... $ac_c" 1>&6 -echo "configure:1423: checking for off_t" >&5 +echo "configure:1426: checking for off_t" >&5 if eval "test \"`echo '$''{'ac_cv_type_off_t'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <<EOF -#line 1428 "configure" +#line 1431 "configure" #include "confdefs.h" #include <sys/types.h> #if STDC_HEADERS @@ -1452,12 +1455,12 @@ EOF fi echo $ac_n "checking for pid_t""... $ac_c" 1>&6 -echo "configure:1456: checking for pid_t" >&5 +echo "configure:1459: checking for pid_t" >&5 if eval "test \"`echo '$''{'ac_cv_type_pid_t'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <<EOF -#line 1461 "configure" +#line 1464 "configure" #include "confdefs.h" #include <sys/types.h> #if STDC_HEADERS @@ -1485,12 +1488,12 @@ EOF fi echo $ac_n "checking for size_t""... $ac_c" 1>&6 -echo "configure:1489: checking for size_t" >&5 +echo "configure:1492: checking for size_t" >&5 if eval "test \"`echo '$''{'ac_cv_type_size_t'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <<EOF -#line 1494 "configure" +#line 1497 "configure" #include "confdefs.h" #include <sys/types.h> #if STDC_HEADERS @@ -1518,12 +1521,12 @@ EOF fi echo $ac_n "checking for st_rdev in struct stat""... $ac_c" 1>&6 -echo "configure:1522: checking for st_rdev in struct stat" >&5 +echo "configure:1525: checking for st_rdev in struct stat" >&5 if eval "test \"`echo '$''{'ac_cv_struct_st_rdev'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <<EOF -#line 1527 "configure" +#line 1530 "configure" #include "confdefs.h" #include <sys/types.h> #include <sys/stat.h> @@ -1531,7 +1534,7 @@ int main() { struct stat s; s.st_rdev; ; return 0; } EOF -if { (eval echo configure:1535: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then +if { (eval echo configure:1538: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* ac_cv_struct_st_rdev=yes else @@ -1552,12 +1555,12 @@ EOF fi echo $ac_n "checking whether time.h and sys/time.h may both be included""... $ac_c" 1>&6 -echo "configure:1556: checking whether time.h and sys/time.h may both be included" >&5 +echo "configure:1559: checking whether time.h and sys/time.h may both be included" >&5 if eval "test \"`echo '$''{'ac_cv_header_time'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <<EOF -#line 1561 "configure" +#line 1564 "configure" #include "confdefs.h" #include <sys/types.h> #include <sys/time.h> @@ -1566,7 +1569,7 @@ int main() { struct tm *tp; ; return 0; } EOF -if { (eval echo configure:1570: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then +if { (eval echo configure:1573: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* ac_cv_header_time=yes else @@ -1607,6 +1610,21 @@ else fi +# Check whether --with-lvm1 or --without-lvm1 was given. +if test "${with_lvm1+set}" = set; then + withval="$with_lvm1" + LVM1="$withval" +else + LVM1="internal" +fi + + +if [ "x$LVM1" != xnone -a "x$LVM1" != xinternal -a "x$LVM1" != xshared ]; + then { echo "configure: error: --with-lvm1 parameter invalid +" 1>&2; exit 1; } + exit +fi; + # Check whether --enable-jobs or --disable-jobs was given. if test "${enable_jobs+set}" = set; then enableval="$enable_jobs" @@ -1631,7 +1649,7 @@ if test "${enable_readline+set}" = set; then \ READLINE=$enableval else - READLINE=yes + READLINE=no fi @@ -1641,13 +1659,13 @@ fi; if test $ac_cv_prog_gcc = yes; then echo $ac_n "checking whether ${CC-cc} needs -traditional""... $ac_c" 1>&6 -echo "configure:1645: checking whether ${CC-cc} needs -traditional" >&5 +echo "configure:1663: checking whether ${CC-cc} needs -traditional" >&5 if eval "test \"`echo '$''{'ac_cv_prog_gcc_traditional'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else ac_pattern="Autoconf.*'x'" cat > conftest.$ac_ext <<EOF -#line 1651 "configure" +#line 1669 "configure" #include "confdefs.h" #include <sgtty.h> Autoconf TIOCGETP @@ -1665,7 +1683,7 @@ rm -f conftest* if test $ac_cv_prog_gcc_traditional = no; then cat > conftest.$ac_ext <<EOF -#line 1669 "configure" +#line 1687 "configure" #include "confdefs.h" #include <termio.h> Autoconf TCGETA @@ -1687,12 +1705,12 @@ echo "$ac_t""$ac_cv_prog_gcc_traditional" 1>&6 fi echo $ac_n "checking return type of signal handlers""... $ac_c" 1>&6 -echo "configure:1691: checking return type of signal handlers" >&5 +echo "configure:1709: checking return type of signal handlers" >&5 if eval "test \"`echo '$''{'ac_cv_type_signal'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <<EOF -#line 1696 "configure" +#line 1714 "configure" #include "confdefs.h" #include <sys/types.h> #include <signal.h> @@ -1709,7 +1727,7 @@ int main() { int i; ; return 0; } EOF -if { (eval echo configure:1713: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then +if { (eval echo configure:1731: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* ac_cv_type_signal=void else @@ -1728,12 +1746,12 @@ EOF echo $ac_n "checking for vprintf""... $ac_c" 1>&6 -echo "configure:1732: checking for vprintf" >&5 +echo "configure:1750: checking for vprintf" >&5 if eval "test \"`echo '$''{'ac_cv_func_vprintf'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <<EOF -#line 1737 "configure" +#line 1755 "configure" #include "confdefs.h" /* System header to define __stub macros and hopefully few prototypes, which can conflict with char vprintf(); below. */ @@ -1756,7 +1774,7 @@ vprintf(); ; return 0; } EOF -if { (eval echo configure:1760: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then +if { (eval echo configure:1778: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_func_vprintf=yes" else @@ -1780,12 +1798,12 @@ fi if test "$ac_cv_func_vprintf" != yes; then echo $ac_n "checking for _doprnt""... $ac_c" 1>&6 -echo "configure:1784: checking for _doprnt" >&5 +echo "configure:1802: checking for _doprnt" >&5 if eval "test \"`echo '$''{'ac_cv_func__doprnt'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <<EOF -#line 1789 "configure" +#line 1807 "configure" #include "confdefs.h" /* System header to define __stub macros and hopefully few prototypes, which can conflict with char _doprnt(); below. */ @@ -1808,7 +1826,7 @@ _doprnt(); ; return 0; } EOF -if { (eval echo configure:1812: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then +if { (eval echo configure:1830: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_func__doprnt=yes" else @@ -1835,12 +1853,12 @@ fi for ac_func in mkdir rmdir uname do echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 -echo "configure:1839: checking for $ac_func" >&5 +echo "configure:1857: checking for $ac_func" >&5 if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <<EOF -#line 1844 "configure" +#line 1862 "configure" #include "confdefs.h" /* System header to define __stub macros and hopefully few prototypes, which can conflict with char $ac_func(); below. */ @@ -1863,7 +1881,7 @@ $ac_func(); ; return 0; } EOF -if { (eval echo configure:1867: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then +if { (eval echo configure:1885: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_func_$ac_func=yes" else @@ -1891,14 +1909,14 @@ done if test x$READLINE = xyes; then echo $ac_n "checking for library containing tgetent""... $ac_c" 1>&6 -echo "configure:1895: checking for library containing tgetent" >&5 +echo "configure:1913: checking for library containing tgetent" >&5 if eval "test \"`echo '$''{'ac_cv_search_tgetent'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else ac_func_search_save_LIBS="$LIBS" ac_cv_search_tgetent="no" cat > conftest.$ac_ext <<EOF -#line 1902 "configure" +#line 1920 "configure" #include "confdefs.h" /* Override any gcc2 internal prototype to avoid an error. */ /* We use char because int might match the return type of a gcc2 @@ -1909,7 +1927,7 @@ int main() { tgetent() ; return 0; } EOF -if { (eval echo configure:1913: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then +if { (eval echo configure:1931: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* ac_cv_search_tgetent="none required" else @@ -1920,7 +1938,7 @@ rm -f conftest* test "$ac_cv_search_tgetent" = "no" && for i in ncurses curses termcap termlib; do LIBS="-l$i $ac_func_search_save_LIBS" cat > conftest.$ac_ext <<EOF -#line 1924 "configure" +#line 1942 "configure" #include "confdefs.h" /* Override any gcc2 internal prototype to avoid an error. */ /* We use char because int might match the return type of a gcc2 @@ -1931,7 +1949,7 @@ int main() { tgetent() ; return 0; } EOF -if { (eval echo configure:1935: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then +if { (eval echo configure:1953: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* ac_cv_search_tgetent="-l$i" break @@ -1965,7 +1983,7 @@ fi if test x$READLINE = xyes; then echo $ac_n "checking for readline in -lreadline""... $ac_c" 1>&6 -echo "configure:1969: checking for readline in -lreadline" >&5 +echo "configure:1987: checking for readline in -lreadline" >&5 ac_lib_var=`echo readline'_'readline | sed 'y%./+-%__p_%'` if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 @@ -1973,7 +1991,7 @@ else ac_save_LIBS="$LIBS" LIBS="-lreadline $LIBS" cat > conftest.$ac_ext <<EOF -#line 1977 "configure" +#line 1995 "configure" #include "confdefs.h" /* Override any gcc2 internal prototype to avoid an error. */ /* We use char because int might match the return type of a gcc2 @@ -1984,7 +2002,7 @@ int main() { readline() ; return 0; } EOF -if { (eval echo configure:1988: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then +if { (eval echo configure:2006: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_lib_$ac_lib_var=yes" else @@ -2021,12 +2039,12 @@ package as well (which may be called readline-devel or something similar). fi echo $ac_n "checking for rl_completion_matches""... $ac_c" 1>&6 -echo "configure:2025: checking for rl_completion_matches" >&5 +echo "configure:2043: checking for rl_completion_matches" >&5 if eval "test \"`echo '$''{'ac_cv_func_rl_completion_matches'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <<EOF -#line 2030 "configure" +#line 2048 "configure" #include "confdefs.h" /* System header to define __stub macros and hopefully few prototypes, which can conflict with char rl_completion_matches(); below. */ @@ -2049,7 +2067,7 @@ rl_completion_matches(); ; return 0; } EOF -if { (eval echo configure:2053: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then +if { (eval echo configure:2071: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_func_rl_completion_matches=yes" else @@ -2085,6 +2103,7 @@ fi + trap '' 1 2 15 cat > confcache <<\EOF # This file is a shell script that caches the results of configure @@ -2203,6 +2222,7 @@ Makefile \ make.tmpl \ include/Makefile \ lib/Makefile \ +lib/format1/Makefile \ man/Makefile \ tools/Makefile \ tools/version.h \ @@ -2255,6 +2275,7 @@ s%@CPP@%$CPP%g s%@JOBS@%$JOBS%g s%@STATIC_LINK@%$STATIC_LINK%g s%@READLINE@%$READLINE%g +s%@LVM1@%$LVM1%g s%@HAVE_RL_COMPLETION_MATCHES@%$HAVE_RL_COMPLETION_MATCHES%g s%@OWNER@%$OWNER%g s%@GROUP@%$GROUP%g @@ -2305,6 +2326,7 @@ Makefile \ make.tmpl \ include/Makefile \ lib/Makefile \ +lib/format1/Makefile \ man/Makefile \ tools/Makefile \ tools/version.h \ diff --git a/configure.in b/configure.in index c95e8c6f8..28f87b809 100644 --- a/configure.in +++ b/configure.in @@ -61,15 +61,29 @@ AC_ARG_WITH(group, [ GROUP="$withval" ], [ GROUP="root" ]) +dnl -- format1 inclusion type +AC_ARG_WITH(lvm1, + [ --with-lvm1=TYPE LVM1 metadata support: internal/shared/none + [TYPE=internal] ], + [ LVM1="$withval" ], + [ LVM1="internal" ]) + +if [[ "x$LVM1" != xnone -a "x$LVM1" != xinternal -a "x$LVM1" != xshared ]]; + then AC_MSG_ERROR( +--with-lvm1 parameter invalid +) + exit +fi; + AC_ARG_ENABLE(jobs, [ --enable-jobs=NUM Number of jobs to run simultaneously], JOBS=-j$enableval, JOBS=) dnl Enables staticly linked tools AC_ARG_ENABLE(static_link, [ --enable-static_link Use this to link the tools to the liblvm library statically. Default is dynamic linking], STATIC_LINK=$enableval, STATIC_LINK=no) -dnl Disable readline -AC_ARG_ENABLE(readline, [ --disable-readline Disable readline support], \ -READLINE=$enableval, READLINE=yes) +dnl Enable readline +AC_ARG_ENABLE(readline, [ --enable-readline Enable readline support], \ +READLINE=$enableval, READLINE=no) dnl Mess with default exec_prefix if [[ "x$exec_prefix" = xNONE -a "x$prefix" = xNONE ]]; @@ -125,6 +139,7 @@ fi AC_SUBST(JOBS) AC_SUBST(STATIC_LINK) AC_SUBST(READLINE) +AC_SUBST(LVM1) AC_SUBST(HAVE_RL_COMPLETION_MATCHES) AC_SUBST(OWNER) AC_SUBST(GROUP) @@ -137,6 +152,7 @@ Makefile \ make.tmpl \ include/Makefile \ lib/Makefile \ +lib/format1/Makefile \ man/Makefile \ tools/Makefile \ tools/version.h \ diff --git a/doc/example.conf b/doc/example.conf index 60554a837..df97131ba 100644 --- a/doc/example.conf +++ b/doc/example.conf @@ -1,24 +1,25 @@ -# This is an example configuration file for the LVM2 system. It -# contains the default settings that would be used if there was no +# This is an example configuration file for the LVM2 system. +# It contains the default settings that would be used if there was no # /etc/lvm/lvm.conf file. -# Refer to 'man lvm.conf' for further information. +# +# Refer to 'man lvm.conf' for further information including the file layout. +# +# To put this file in a different directory and override /etc/lvm set +# the environment variable LVM_SYSTEM_DIR before running the tools. -# This section allows the user to configure which block devices should +# This section allows you to configure which block devices should # be used by the LVM system. devices { - # where do you want your volume groups to appear ? + # Where do you want your volume groups to appear ? dir = "/dev" # An array of directories that contain the device nodes you wish # to use with LVM2. - scan = "/dev" - - # A very important option, that allows you to tune the LVM2 system - # to just look at a restricted set of devices that you're - # interested in. + scan = [ "/dev" ] + # A filter that tells LVM2 to only use a restricted set of devices. # The filter consists of an array of regular expressions. These # expressions can be delimited by a character of your choice, and # prefixed with either an 'a' (for accept) or 'r' (for reject). @@ -26,51 +27,56 @@ devices { # Remember to run vgscan after you change this parameter. # By default we accept every block device: - filter = "a/.*/" + filter = [ "a/.*/" ] + + # Exclude the cdrom drive + # filter = [ "r|/dev/cdrom|" ] # When testing I like to work with just loopback devices: - # filter = ["a/loop/", "r/.*/"] + # filter = [ "a/loop/", "r/.*/" ] # Or maybe all loops and ide drives except hdc: - # filter =["a|loop|", "r|/dev/hdc|", "a|/dev/ide|", "r|.*|"] + # filter =[ "a|loop|", "r|/dev/hdc|", "a|/dev/ide|", "r|.*|" ] # Use anchors if you want to be really specific - # filter = ["a|^/dev/hda8$|", "r/.*/"] + # filter = [ "a|^/dev/hda8$|", "r/.*/" ] - # The results of all the filtering are cached on disk to avoid + # The results of the filtering are cached on disk to avoid # rescanning dud devices (which can take a very long time). By - # default this cache file is hidden in the /etc/lvm directory, it - # is human readable to aid filter debugging. + # default this cache file is hidden in the /etc/lvm directory. + # It is safe to delete this file. vgscan regenerates it. cache = "/etc/lvm/.cache" # You can turn off writing this cache file by setting this to 0. write_cache_state = 1 } -# A section that allows the user to configure the nature of the +# This section that allows you to configure the nature of the # information that LVM2 reports. log { - # Where should the log of error and debug messages go ? By - # default there is no log. - #file = "/var/log/lvm2.log" - - # Should we overwrite the last log. By default we append. - overwrite = 0 - - # There are 9 log levels, with 9 being the most verbose. - level = 3 - - # Controls the messages sent to stdout or stderr while running - # LVM2. There are three levels of verbosity, 3 being the most - # verbose. + # Controls the messages sent to stdout or stderr. + # There are three levels of verbosity, 3 being the most verbose. verbose = 0 # Should we send log messages through syslog? # 1 is yes; 0 is no. syslog = 1 - # Choose format of output messages + # Should we log error and debug messages to a file? + # By default there is no log file. + #file = "/var/log/lvm2.log" + + # Should we overwrite the log file each time the program is run? + # By default we append. + overwrite = 0 + + # What level of log messages should we send to the log file and/or syslog? + # There are 6 syslog-like log levels currently in use - 2 to 7 inclusive. + # 7 is the most verbose (LOG_DEBUG). + level = 0 + + # Format of output messages # Whether or not (1 or 0) to indent messages according to their severity indent = 1 @@ -78,10 +84,11 @@ log { command_names = 0 # A prefix to use before the message text (but after the command name, - # if selected) + # if selected). Default is two spaces, so you can see/grep the severity + # of each message. prefix = " " - # To make the messages look similar to the original LVM use: + # To make the messages look similar to the original LVM tools use: # indent = 0 # command_names = 1 # prefix = " -- " @@ -95,19 +102,20 @@ backup { # Should we maintain a backup of the current metadata configuration ? # Use 1 for Yes; 0 for No. - # Think very hard before turning this off. + # Think very hard before turning this off! backup = 1 # Where shall we keep it ? + # Remember to back up this directory regularly! backup_dir = "/etc/lvm/backup" - # Should we maintain an archive of old metadata configurations. # Use 1 for Yes; 0 for No. # On by default. Think very hard before turning this off. archive = 1 # Where should archived files go ? + # Remember to back up this directory regularly! archive_dir = "/etc/lvm/archive" # What is the minimum number of archive files you wish to keep ? @@ -117,20 +125,15 @@ backup { retain_days = 30 } -# Settings for the running LVM2 in shell mode. +# Settings for the running LVM2 in shell (readline) mode. shell { # Number of lines of history to store in ~/.lvm_history history_size = 100 } -# Metadata settings -metadata { - # List of directories holding copies of text format metadata - dirs = [ "/etc/lvm/metadata" ] -} -# Miscellaneous global settings +# Miscellaneous global LVM2 settings global { # The file creation mask for any files and directories created. @@ -145,10 +148,73 @@ global { # command. Defaults to off. test = 0 - # Default metadata format commands use - "lvm1" (default) or "text" - format = "lvm1" + # Whether or not to communicate with the kernel device-mapper. + # Set to 0 if you want to use the tools to manipulate LVM metadata + # without activating any logical volumes. + # If the device-mapper kernel driver is not present in your kernel + # setting this to 0 should suppress the error messages. + activation = 1 + + # The default metadata format that commands should use - "lvm1" or "lvm2". + # The command line override is -M1 or -M2. + # Defaults to "lvm1" if compiled in, else "lvm2". + # format = "lvm1" # Location of proc filesystem proc = "/proc" + + # Type of locking to use. Defaults to file-based locking (1). + # Turn locking off by setting to 0 (dangerous: risks metadata corruption + # if LVM2 commands get run concurrently). + locking_type = 1 + + # Local non-LV directory that holds file-based locks while commands are + # in progress. A directory like /tmp that may get wiped on reboot is OK. + locking_dir = "/var/lock/lvm" + + # Other entries can go here to allow you to load shared libraries + # e.g. if support for LVM1 metadata was compiled as a shared library use + # format_libraries = "liblvm2format1.so" + # Full pathnames can be given. + + # Search this directory first for shared libraries. + # library_dir = "/lib" } + +#################### +# Advanced section # +#################### + +# Metadata settings +# +# metadata { + # Default number of copies of metadata to hold on each PV. 0, 1 or 2. + # It's best to leave this at 2. + # You might want to override it from the command line with 0 or 1 + # when running pvcreate on new PVs which are to be added to large VGs. + + # pvmetadatacopies = 2 + + # Approximate default size of on-disk metadata areas in sectors. + # You should increase this if you have large volume groups or + # you want to retain a large on-disk history of your metadata changes. + + # pvmetadatasize = 255 + + # List of directories holding live copies of text format metadata. + # These directories must not be on logical volumes! + # It's possible to use LVM2 with a couple of directories here, + # preferably on different (non-LV) filesystems, and with no other + # on-disk metadata (pvmetadatacopies = 0). Or this can be in + # addition to on-disk metadata areas. + # The feature was originally added to simplify testing. + # + # Never edit any files in these directories by hand unless you + # you are absolutely sure you know what you are doing! Use + # the supplied toolset to make changes (e.g. vgcfgrestore). + + # dirs = [ "/etc/lvm/metadata", "/mnt/disk2/lvm/metadata2" ] +#} + + diff --git a/include/.symlinks b/include/.symlinks index 681709e9c..dc1449c46 100644 --- a/include/.symlinks +++ b/include/.symlinks @@ -1,4 +1,5 @@ ../lib/activate/activate.h +../lib/cache/cache.h ../lib/commands/errors.h ../lib/commands/toolcontext.h ../lib/config/config.h @@ -11,23 +12,25 @@ ../lib/device/dev-cache.h ../lib/device/device.h ../lib/display/display.h +../lib/display/display_formats.h ../lib/filters/filter-composite.h ../lib/filters/filter-persistent.h ../lib/filters/filter-regex.h ../lib/filters/filter.h ../lib/format1/format1.h -../lib/format1/lvm1_label.h +../lib/format1/lvm1-label.h ../lib/format_text/format-text.h ../lib/label/label.h -../lib/label/uuid-map.h ../lib/locking/locking.h ../lib/log/log.h ../lib/metadata/metadata.h ../lib/mm/dbg_malloc.h ../lib/mm/pool.h ../lib/mm/xlate.h +../lib/misc/crc.h +../lib/misc/lib.h ../lib/misc/lvm-file.h ../lib/misc/lvm-string.h +../lib/misc/sharedlib.h ../lib/regex/matcher.h ../lib/uuid/uuid.h -../lib/vgcache/vgcache.h diff --git a/lib/Makefile.in b/lib/Makefile.in index f3842ca57..d208a2e7d 100644 --- a/lib/Makefile.in +++ b/lib/Makefile.in @@ -8,10 +8,16 @@ srcdir = @srcdir@ top_srcdir = @top_srcdir@ VPATH = @srcdir@ +ifeq ("@LVM1@", "shared") + SUBDIRS = format1 +endif + SOURCES=\ activate/activate.c \ activate/dev_manager.c \ activate/fs.c \ + cache/cache.c \ + commands/toolcontext.c \ config/config.c \ datastruct/bitset.c \ datastruct/btree.c \ @@ -24,20 +30,14 @@ SOURCES=\ filters/filter-persistent.c \ filters/filter-regex.c \ filters/filter.c \ - format1/disk-rep.c \ - format1/format1.c \ - format1/import-export.c \ - format1/import-extents.c \ - format1/layout.c \ - format1/lvm1_label.c \ - format1/vg_number.c \ format_text/archive.c \ format_text/export.c \ format_text/flags.c \ format_text/format-text.c \ format_text/import.c \ + format_text/import_vsn1.c \ + format_text/text_label.c \ label/label.c \ - label/uuid-map.c \ locking/external_locking.c \ locking/file_locking.c \ locking/locking.c \ @@ -48,14 +48,26 @@ SOURCES=\ metadata/metadata.c \ metadata/pv_map.c \ metadata/snapshot_manip.c \ + misc/crc.c \ misc/lvm-file.c \ + misc/sharedlib.c \ mm/dbg_malloc.c \ mm/pool.c \ regex/matcher.c \ regex/parse_rx.c \ regex/ttree.c \ - uuid/uuid.c \ - vgcache/vgcache.c + uuid/uuid.c + +ifeq ("@LVM1@", "internal") + SOURCES+=\ + format1/disk-rep.c \ + format1/format1.c \ + format1/import-export.c \ + format1/import-extents.c \ + format1/layout.c \ + format1/lvm1-label.c \ + format1/vg_number.c +endif TARGETS=liblvm.a diff --git a/lib/format_text/archive.c b/lib/format_text/archive.c index 100ff9a93..f2397d26c 100644 --- a/lib/format_text/archive.c +++ b/lib/format_text/archive.c @@ -4,9 +4,9 @@ * This file is released under the LGPL. */ +#include "lib.h" #include "format-text.h" -#include "log.h" #include "pool.h" #include "config.h" #include "hash.h" @@ -17,7 +17,6 @@ #include <dirent.h> #include <unistd.h> -#include <sys/types.h> #include <sys/stat.h> #include <sys/file.h> #include <fcntl.h> @@ -52,7 +51,7 @@ struct archive_file { * Extract vg name and version number from a filename. */ static int _split_vg(const char *filename, char *vg, size_t vg_size, - uint32_t * index) + uint32_t *index) { int len, vg_len; char *dot, *underscore; @@ -202,7 +201,7 @@ static void _remove_expired(struct list *archives, uint32_t archives_size, return; /* Convert retain_days into the time after which we must retain */ - retain_time = time(NULL) - (time_t) retain_days * SECS_PER_DAY; + retain_time = time(NULL) - (time_t) retain_days *SECS_PER_DAY; /* Assume list is ordered oldest first (by index) */ list_iterate(bh, archives) { @@ -252,7 +251,7 @@ int archive_vg(struct volume_group *vg, return 0; } - if (!text_vg_export(fp, vg, desc)) { + if (!text_vg_export_file(vg, desc, fp)) { stack; fclose(fp); return 0; @@ -297,8 +296,7 @@ int archive_vg(struct volume_group *vg, return 1; } -static void _display_archive(struct cmd_context *cmd, struct uuid_map *um, - struct archive_file *af) +static void _display_archive(struct cmd_context *cmd, struct archive_file *af) { struct volume_group *vg = NULL; struct format_instance *tf; @@ -308,8 +306,9 @@ static void _display_archive(struct cmd_context *cmd, struct uuid_map *um, log_print("path:\t\t%s", af->path); - if (!(context = create_text_context(cmd->fmtt, af->path, NULL)) || - !(tf = cmd->fmtt->ops->create_instance(cmd->fmtt, NULL, context))) { + if (!(context = create_text_context(cmd, af->path, NULL)) || + !(tf = cmd->fmt_backup->ops->create_instance(cmd->fmt_backup, NULL, + context))) { log_error("Couldn't create text instance object."); return; } @@ -319,7 +318,7 @@ static void _display_archive(struct cmd_context *cmd, struct uuid_map *um, * retrieve the archive time and description. */ /* FIXME Use variation on _vg_read */ - if (!(vg = text_vg_import(tf, af->path, um, &when, &desc))) { + if (!(vg = text_vg_import_file(tf, af->path, &when, &desc))) { log_print("Unable to read archive file."); tf->fmt->ops->destroy_instance(tf); return; @@ -332,8 +331,7 @@ static void _display_archive(struct cmd_context *cmd, struct uuid_map *um, tf->fmt->ops->destroy_instance(tf); } -int archive_list(struct cmd_context *cmd, struct uuid_map *um, - const char *dir, const char *vg) +int archive_list(struct cmd_context *cmd, const char *dir, const char *vg) { struct list *archives, *ah; struct archive_file *af; @@ -349,7 +347,7 @@ int archive_list(struct cmd_context *cmd, struct uuid_map *um, list_iterate(ah, archives) { af = list_item(ah, struct archive_file); - _display_archive(cmd, um, af); + _display_archive(cmd, af); log_print(" "); } diff --git a/lib/format_text/export.c b/lib/format_text/export.c index a95540686..8c7e54572 100644 --- a/lib/format_text/export.c +++ b/lib/format_text/export.c @@ -4,40 +4,74 @@ * This file is released under the LGPL. */ +#include "lib.h" #include "import-export.h" #include "metadata.h" -#include "log.h" #include "hash.h" #include "pool.h" -#include "dbg_malloc.h" -#include "lvm-string.h" #include "display.h" +#include "lvm-string.h" -#include <stdio.h> #include <stdarg.h> #include <time.h> +#include <sys/utsname.h> - +struct formatter; +typedef int (*out_with_comment_fn) (struct formatter * f, const char *comment, + const char *fmt, va_list ap); +typedef void (*nl_fn) (struct formatter * f); +/* + * The first half of this file deals with + * exporting the vg, ie. writing it to a file. + */ struct formatter { struct pool *mem; /* pv names allocated from here */ struct hash_table *pv_names; /* dev_name -> pv_name (eg, pv1) */ - FILE *fp; /* where we're writing to */ - int indent; /* current level of indentation */ + union { + FILE *fp; /* where we're writing to */ + struct { + char *buf; + uint32_t size; + uint32_t used; + } buf; + } data; + + out_with_comment_fn out_with_comment; + nl_fn nl; + int indent; /* current level of indentation */ int error; + int header; /* 1 => comments at start; 0 => end */ }; +static struct utsname _utsname; + +static void _init(void) +{ + static int _initialised = 0; + + if (_initialised) + return; + + if (uname(&_utsname)) { + log_error("uname failed: %s", strerror(errno)); + memset(&_utsname, 0, sizeof(_utsname)); + } + + _initialised = 1; +} + /* * Formatting functions. */ -static void _out_size(struct formatter *f, uint64_t size, const char *fmt, ...) +static int _out_size(struct formatter *f, uint64_t size, const char *fmt, ...) __attribute__ ((format(printf, 3, 4))); -static void _out_hint(struct formatter *f, const char *fmt, ...) +static int _out_hint(struct formatter *f, const char *fmt, ...) __attribute__ ((format(printf, 2, 3))); -static void _out(struct formatter *f, const char *fmt, ...) +static int _out(struct formatter *f, const char *fmt, ...) __attribute__ ((format(printf, 2, 3))); #define MAX_INDENT 5 @@ -58,26 +92,39 @@ static void _dec_indent(struct formatter *f) /* * Newline function for prettier layout. */ -static void _nl(struct formatter *f) +static void _nl_file(struct formatter *f) { - fprintf(f->fp, "\n"); + fprintf(f->data.fp, "\n"); +} + +static void _nl_raw(struct formatter *f) +{ + if (f->data.buf.used >= f->data.buf.size - 1) + return; + + *f->data.buf.buf = '\n'; + f->data.buf.buf += 1; + f->data.buf.used += 1; + *f->data.buf.buf = '\0'; + + return; } #define COMMENT_TAB 6 -static void _out_with_comment(struct formatter *f, const char *comment, - const char *fmt, va_list ap) +static int _out_with_comment_file(struct formatter *f, const char *comment, + const char *fmt, va_list ap) { int i; char white_space[MAX_INDENT + 1]; - if (ferror(f->fp)) - return; + if (ferror(f->data.fp)) + return 0; for (i = 0; i < f->indent; i++) white_space[i] = '\t'; white_space[i] = '\0'; - fprintf(f->fp, white_space); - i = vfprintf(f->fp, fmt, ap); + fprintf(f->data.fp, white_space); + i = vfprintf(f->data.fp, fmt, ap); if (comment) { /* @@ -88,13 +135,34 @@ static void _out_with_comment(struct formatter *f, const char *comment, i++; do - fputc('\t', f->fp); + fputc('\t', f->data.fp); while (++i < COMMENT_TAB); - fprintf(f->fp, comment); + fprintf(f->data.fp, comment); } - fputc('\n', f->fp); + fputc('\n', f->data.fp); + + return 1; +} + +static int _out_with_comment_raw(struct formatter *f, const char *comment, + const char *fmt, va_list ap) +{ + int n; + + n = vsnprintf(f->data.buf.buf, f->data.buf.size - f->data.buf.used, + fmt, ap); + + if (n < 0 || (n > f->data.buf.size - f->data.buf.used - 1)) + return 0; + + f->data.buf.buf += n; + f->data.buf.used += n; + + f->nl(f); + + return 1; } /* @@ -109,7 +177,7 @@ static int _sectors_to_units(uint64_t sectors, char *buffer, size_t s) "Kilobytes", "Megabytes", "Gigabytes", - "Terrabytes", + "Terabytes", NULL }; @@ -129,43 +197,55 @@ static int _sectors_to_units(uint64_t sectors, char *buffer, size_t s) * Appends a comment giving a size in more easily * readable form (eg, 4M instead of 8096). */ -static void _out_size(struct formatter *f, uint64_t size, const char *fmt, ...) +static int _out_size(struct formatter *f, uint64_t size, const char *fmt, ...) { char buffer[64]; va_list ap; + int r; - _sectors_to_units(size, buffer, sizeof(buffer)); + if (!_sectors_to_units(size, buffer, sizeof(buffer))) + return 0; va_start(ap, fmt); - _out_with_comment(f, buffer, fmt, ap); + r = f->out_with_comment(f, buffer, fmt, ap); va_end(ap); + + return r; } /* * Appends a comment indicating that the line is * only a hint. */ -static void _out_hint(struct formatter *f, const char *fmt, ...) +static int _out_hint(struct formatter *f, const char *fmt, ...) { va_list ap; + int r; va_start(ap, fmt); - _out_with_comment(f, "# Hint only", fmt, ap); + r = f->out_with_comment(f, "# Hint only", fmt, ap); va_end(ap); + + return r; } /* * The normal output function. */ -static void _out(struct formatter *f, const char *fmt, ...) +static int _out(struct formatter *f, const char *fmt, ...) { va_list ap; + int r; va_start(ap, fmt); - _out_with_comment(f, NULL, fmt, ap); + r = f->out_with_comment(f, NULL, fmt, ap); va_end(ap); + + return r; } +#define _outf(args...) do {if (!_out(args)) {stack; return 0;}} while (0) + static int _print_header(struct formatter *f, struct volume_group *vg, const char *desc) { @@ -173,15 +253,17 @@ static int _print_header(struct formatter *f, t = time(NULL); - _out(f, - "# This file was originally generated by the LVM2 library\n" - "# Generated: %s", ctime(&t)); - - _out(f, CONTENTS_FIELD " = \"" CONTENTS_VALUE "\""); - _out(f, FORMAT_VERSION_FIELD " = %d\n", FORMAT_VERSION_VALUE); + _outf(f, "# Generated by LVM2: %s", ctime(&t)); + _outf(f, CONTENTS_FIELD " = \"" CONTENTS_VALUE "\""); + _outf(f, FORMAT_VERSION_FIELD " = %d", FORMAT_VERSION_VALUE); + f->nl(f); - _out(f, "description = \"%s\"", desc); - _out(f, "creation_time = %lu\n", t); + _outf(f, "description = \"%s\"", desc); + f->nl(f); + _outf(f, "creation_host = \"%s\"\t# %s %s %s %s %s", _utsname.nodename, + _utsname.sysname, _utsname.nodename, _utsname.release, + _utsname.version, _utsname.machine); + _outf(f, "creation_time = %lu\t# %s", t, ctime(&t)); return 1; } @@ -195,20 +277,23 @@ static int _print_vg(struct formatter *f, struct volume_group *vg) return 0; } - _out(f, "id = \"%s\"", buffer); + _outf(f, "id = \"%s\"", buffer); - _out(f, "seqno = %u", vg->seqno); + _outf(f, "seqno = %u", vg->seqno); if (!print_flags(vg->status, VG_FLAGS, buffer, sizeof(buffer))) { stack; return 0; } - _out(f, "status = %s", buffer); + _outf(f, "status = %s", buffer); if (vg->system_id && *vg->system_id) - _out(f, "system_id = \"%s\"", vg->system_id); - _out_size(f, vg->extent_size, "extent_size = %u", vg->extent_size); - _out(f, "max_lv = %u", vg->max_lv); - _out(f, "max_pv = %u", vg->max_pv); + _outf(f, "system_id = \"%s\"", vg->system_id); + if (!_out_size(f, vg->extent_size, "extent_size = %u", vg->extent_size)) { + stack; + return 0; + } + _outf(f, "max_lv = %u", vg->max_lv); + _outf(f, "max_pv = %u", vg->max_pv); return 1; } @@ -231,7 +316,7 @@ static int _print_pvs(struct formatter *f, struct volume_group *vg) char buffer[256]; const char *name; - _out(f, "physical_volumes {"); + _outf(f, "physical_volumes {"); _inc_indent(f); list_iterate(pvh, &vg->pvs) { @@ -243,8 +328,8 @@ static int _print_pvs(struct formatter *f, struct volume_group *vg) return 0; } - _nl(f); - _out(f, "%s {", name); + f->nl(f); + _outf(f, "%s {", name); _inc_indent(f); if (!id_write_format(&pv->id, buffer, sizeof(buffer))) { @@ -252,65 +337,93 @@ static int _print_pvs(struct formatter *f, struct volume_group *vg) return 0; } - _out(f, "id = \"%s\"", buffer); - _out_hint(f, "device = \"%s\"", dev_name(pv->dev)); - _nl(f); + _outf(f, "id = \"%s\"", buffer); + if (!_out_hint(f, "device = \"%s\"", dev_name(pv->dev))) { + stack; + return 0; + } + f->nl(f); if (!print_flags(pv->status, PV_FLAGS, buffer, sizeof(buffer))) { stack; return 0; } - _out(f, "status = %s", buffer); - _out(f, "pe_start = %llu", pv->pe_start); - _out_size(f, vg->extent_size * (uint64_t) pv->pe_count, - "pe_count = %u", pv->pe_count); + _outf(f, "status = %s", buffer); + _outf(f, "pe_start = %" PRIu64, pv->pe_start); + if (!_out_size(f, vg->extent_size * (uint64_t) pv->pe_count, + "pe_count = %u", pv->pe_count)) { + stack; + return 0; + } _dec_indent(f); - _out(f, "}"); + _outf(f, "}"); } _dec_indent(f); - _out(f, "}"); + _outf(f, "}"); return 1; } static int _print_segment(struct formatter *f, struct volume_group *vg, - int count, struct stripe_segment *seg) + int count, struct lv_segment *seg) { int s; const char *name; - _out(f, "segment%u {", count); + _outf(f, "segment%u {", count); _inc_indent(f); - _out(f, "start_extent = %u", seg->le); - _out_size(f, seg->len * vg->extent_size, "extent_count = %u", seg->len); - _out(f, "stripes = %u", seg->stripes); + _outf(f, "start_extent = %u", seg->le); + if (!_out_size(f, seg->len * vg->extent_size, "extent_count = %u", + seg->len)) { + stack; + return 0; + } - if (seg->stripes > 1) - _out_size(f, seg->stripe_size, - "stripe_size = %u", seg->stripe_size); + f->nl(f); + _outf(f, "type = \"%s\"", get_segtype_string(seg->type)); - _nl(f); - _out(f, "areas = ["); - _inc_indent(f); + switch (seg->type) { + case SEG_STRIPED: + _outf(f, "stripe_count = %u%s", seg->stripes, + (seg->stripes == 1) ? "\t# linear" : ""); - for (s = 0; s < seg->stripes; s++) { - if (!(name = _get_pv_name(f, seg->area[s].pv))) { - stack; - return 0; + if (seg->stripes > 1) + _out_size(f, seg->stripe_size, + "stripe_size = %u", seg->stripe_size); + + f->nl(f); + _outf(f, "stripes = ["); + _inc_indent(f); + + for (s = 0; s < seg->stripes; s++) { + if (!(name = _get_pv_name(f, seg->area[s].pv))) { + stack; + return 0; + } + + _outf(f, "\"%s\", %u%s", name, seg->area[s].pe, + (s == seg->stripes - 1) ? "" : ","); } - _out(f, "\"%s\", %u%s", name, seg->area[s].pe, - (s == seg->stripes - 1) ? "" : ","); + _dec_indent(f); + _outf(f, "]"); + break; + + case SEG_SNAPSHOT: + _outf(f, "chunk_size = %u", seg->chunk_size); + _outf(f, "origin = \"%s\"", seg->origin->name); + _outf(f, "cow_store = \"%s\"", seg->cow->name); + break; + case SEG_MIRROR: + /* mirrors = [ "lvol1", 10, ... ] */ + ; } _dec_indent(f); - _out(f, "]"); - - _dec_indent(f); - _out(f, "}"); + _outf(f, "}"); return 1; } @@ -326,11 +439,75 @@ static int _count_segments(struct logical_volume *lv) return r; } +static int _print_snapshot(struct formatter *f, struct snapshot *snap, + unsigned int count) +{ + char buffer[256]; + struct lv_segment seg; + + f->nl(f); + + _outf(f, "snapshot%u {", count); + _inc_indent(f); + + if (!id_write_format(&snap->id, buffer, sizeof(buffer))) { + stack; + return 0; + } + + _outf(f, "id = \"%s\"", buffer); + if (!print_flags(LVM_READ | LVM_WRITE | VISIBLE_LV, LV_FLAGS, + buffer, sizeof(buffer))) { + stack; + return 0; + } + + _outf(f, "status = %s", buffer); + _outf(f, "segment_count = 1"); + + f->nl(f); + + seg.type = SEG_SNAPSHOT; + seg.le = 0; + seg.len = snap->origin->le_count; + seg.origin = snap->origin; + seg.cow = snap->cow; + seg.chunk_size = snap->chunk_size; + + if (!_print_segment(f, snap->origin->vg, 1, &seg)) { + stack; + return 0; + } + + _dec_indent(f); + _outf(f, "}"); + + return 1; +} + +static int _print_snapshots(struct formatter *f, struct volume_group *vg) +{ + struct list *sh; + struct snapshot *s; + unsigned int count = 0; + + list_iterate(sh, &vg->snapshots) { + s = list_item(sh, struct snapshot_list)->snapshot; + + if (!_print_snapshot(f, s, count++)) { + stack; + return 0; + } + } + + return 1; +} + static int _print_lvs(struct formatter *f, struct volume_group *vg) { struct list *lvh, *segh; struct logical_volume *lv; - struct stripe_segment *seg; + struct lv_segment *seg; char buffer[256]; int seg_count; @@ -340,14 +517,14 @@ static int _print_lvs(struct formatter *f, struct volume_group *vg) if (list_empty(&vg->lvs)) return 1; - _out(f, "logical_volumes {"); + _outf(f, "logical_volumes {"); _inc_indent(f); list_iterate(lvh, &vg->lvs) { lv = list_item(lvh, struct lv_list)->lv; - _nl(f); - _out(f, "%s {", lv->name); + f->nl(f); + _outf(f, "%s {", lv->name); _inc_indent(f); /* FIXME: Write full lvid */ @@ -356,26 +533,27 @@ static int _print_lvs(struct formatter *f, struct volume_group *vg) return 0; } - _out(f, "id = \"%s\"", buffer); + _outf(f, "id = \"%s\"", buffer); - if (!print_flags(lv->status, LV_FLAGS, - buffer, sizeof(buffer))) { + if (!print_flags(lv->status, LV_FLAGS, buffer, sizeof(buffer))) { stack; return 0; } - _out(f, "status = %s", buffer); - _out(f, "allocation_policy = \"%s\"", - get_alloc_string(lv->alloc)); - _out(f, "read_ahead = %u", lv->read_ahead); + _outf(f, "status = %s", buffer); + if (lv->alloc != ALLOC_DEFAULT) + _outf(f, "allocation_policy = \"%s\"", + get_alloc_string(lv->alloc)); + if (lv->read_ahead) + _outf(f, "read_ahead = %u", lv->read_ahead); if (lv->minor >= 0) - _out(f, "minor = %d", lv->minor); - _out(f, "segment_count = %u", _count_segments(lv)); - _nl(f); + _outf(f, "minor = %d", lv->minor); + _outf(f, "segment_count = %u", _count_segments(lv)); + f->nl(f); seg_count = 1; list_iterate(segh, &lv->segments) { - seg = list_item(segh, struct stripe_segment); + seg = list_item(segh, struct lv_segment); if (!_print_segment(f, vg, seg_count++, seg)) { stack; @@ -384,59 +562,16 @@ static int _print_lvs(struct formatter *f, struct volume_group *vg) } _dec_indent(f); - _out(f, "}"); + _outf(f, "}"); } - _dec_indent(f); - _out(f, "}"); - - return 1; -} - -static int _print_snapshot(struct formatter *f, struct snapshot *s, - unsigned int count) -{ - _nl(f); - _out(f, "snapshot%u {", count); - _inc_indent(f); - - _out(f, "chunk_size = %u", s->chunk_size); - _out(f, "origin = \"%s\"", s->origin->name); - _out(f, "cow_store = \"%s\"", s->cow->name); - - _dec_indent(f); - _out(f, "}"); - - return 1; -} - -static int _print_snapshots(struct formatter *f, struct volume_group *vg) -{ - struct list *sh; - struct snapshot *s; - unsigned int count = 0; - - /* - * Don't bother with a snapshot section if there are no - * snapshots. - */ - if (list_empty(&vg->snapshots)) - return 1; - - _out(f, "snapshots {"); - _inc_indent(f); - - list_iterate(sh, &vg->snapshots) { - s = list_item(sh, struct snapshot_list)->snapshot; - - if (!_print_snapshot(f, s, count++)) { - stack; - return 0; - } + if (!_print_snapshots(f, vg)) { + stack; + return 0; } _dec_indent(f); - _out(f, "}"); + _outf(f, "}"); return 1; } @@ -494,19 +629,10 @@ static int _build_pv_names(struct formatter *f, struct volume_group *vg) return 0; } -int text_vg_export(FILE * fp, struct volume_group *vg, const char *desc) +static int _text_vg_export(struct formatter *f, + struct volume_group *vg, const char *desc) { int r = 0; - struct formatter *f; - - if (!(f = dbg_malloc(sizeof(*f)))) { - stack; - return 0; - } - - memset(f, 0, sizeof(*f)); - f->fp = fp; - f->indent = 0; if (!_build_pv_names(f, vg)) { stack; @@ -514,32 +640,34 @@ int text_vg_export(FILE * fp, struct volume_group *vg, const char *desc) } #define fail do {stack; goto out;} while(0) - if (!_print_header(f, vg, desc)) + if (f->header && !_print_header(f, vg, desc)) + fail; + + if (!_out(f, "%s {", vg->name)) fail; - _out(f, "%s {", vg->name); _inc_indent(f); if (!_print_vg(f, vg)) fail; - _nl(f); + f->nl(f); if (!_print_pvs(f, vg)) fail; - _nl(f); + f->nl(f); if (!_print_lvs(f, vg)) fail; - _nl(f); - if (!_print_snapshots(f, vg)) + _dec_indent(f); + if (!_out(f, "}")) fail; -#undef fail + if (!f->header && !_print_header(f, vg, desc)) + fail; - _dec_indent(f); - _out(f, "}"); - r = !ferror(f->fp); +#undef fail + r = 1; out: if (f->mem) @@ -548,6 +676,68 @@ int text_vg_export(FILE * fp, struct volume_group *vg, const char *desc) if (f->pv_names) hash_destroy(f->pv_names); + return r; +} + +int text_vg_export_file(struct volume_group *vg, const char *desc, FILE *fp) +{ + struct formatter *f; + int r; + + _init(); + + if (!(f = dbg_malloc(sizeof(*f)))) { + stack; + return 0; + } + + memset(f, 0, sizeof(*f)); + f->data.fp = fp; + f->indent = 0; + f->header = 1; + f->out_with_comment = &_out_with_comment_file; + f->nl = &_nl_file; + + r = _text_vg_export(f, vg, desc); + if (r) + r = !ferror(f->data.fp); dbg_free(f); return r; } + +/* Returns amount of buffer used incl. terminating NUL */ +int text_vg_export_raw(struct volume_group *vg, const char *desc, char *buf, + uint32_t size) +{ + struct formatter *f; + int r; + + _init(); + + if (!(f = dbg_malloc(sizeof(*f)))) { + stack; + return 0; + } + + memset(f, 0, sizeof(*f)); + f->data.buf.buf = buf; + f->data.buf.size = size; + f->indent = 0; + f->header = 0; + f->out_with_comment = &_out_with_comment_raw; + f->nl = &_nl_raw; + + if (!_text_vg_export(f, vg, desc)) { + stack; + r = 0; + goto out; + } + + r = f->data.buf.used + 1; + + out: + dbg_free(f); + return r; +} + +#undef _outf diff --git a/lib/format_text/flags.c b/lib/format_text/flags.c index 080e507dc..63f8a4795 100644 --- a/lib/format_text/flags.c +++ b/lib/format_text/flags.c @@ -4,7 +4,7 @@ * This file is released under the LGPL. */ -#include "log.h" +#include "lib.h" #include "metadata.h" #include "import-export.h" #include "lvm-string.h" @@ -60,7 +60,7 @@ static struct flag *_get_flags(int type) return NULL; } -static int _emit(char **buffer, size_t * size, const char *fmt, ...) +static int _emit(char **buffer, size_t *size, const char *fmt, ...) { size_t n; va_list ap; @@ -122,7 +122,7 @@ int print_flags(uint32_t status, int type, char *buffer, size_t size) return 1; } -int read_flags(uint32_t * status, int type, struct config_value *cv) +int read_flags(uint32_t *status, int type, struct config_value *cv) { int f; uint32_t s = 0; @@ -133,31 +133,30 @@ int read_flags(uint32_t * status, int type, struct config_value *cv) return 0; } - /* - * Only scan the flags if it wasn't an empty array. - */ - if (cv->type != CFG_EMPTY_ARRAY) { - while (cv) { - if (cv->type != CFG_STRING) { - log_err("Status value is not a string."); - return 0; - } + if (cv->type == CFG_EMPTY_ARRAY) + goto out; - for (f = 0; flags[f].description; f++) - if (!strcmp(flags[f].description, cv->v.str)) { - s |= flags[f].mask; - break; - } + while (cv) { + if (cv->type != CFG_STRING) { + log_err("Status value is not a string."); + return 0; + } - if (!flags[f].description) { - log_err("Unknown status flag '%s'.", cv->v.str); - return 0; + for (f = 0; flags[f].description; f++) + if (!strcmp(flags[f].description, cv->v.str)) { + s |= flags[f].mask; + break; } - cv = cv->next; + if (!flags[f].description) { + log_err("Unknown status flag '%s'.", cv->v.str); + return 0; } + + cv = cv->next; } + out: *status = s; return 1; } diff --git a/lib/format_text/format-text.c b/lib/format_text/format-text.c index 076e228e5..6f493e5aa 100644 --- a/lib/format_text/format-text.c +++ b/lib/format_text/format-text.c @@ -4,37 +4,43 @@ * This file is released under the LGPL. */ +#include "lib.h" #include "format-text.h" #include "import-export.h" - +#include "device.h" #include "lvm-file.h" -#include "log.h" #include "pool.h" #include "config.h" #include "hash.h" #include "display.h" -#include "dbg_malloc.h" #include "toolcontext.h" -#include "vgcache.h" #include "lvm-string.h" +#include "uuid.h" +#include "layout.h" +#include "crc.h" +#include "xlate.h" +#include "label.h" #include <unistd.h> -#include <sys/types.h> #include <sys/file.h> #include <limits.h> #include <dirent.h> +#include <ctype.h> -/* Arbitrary limits copied from format1/disk_rep.h */ -#define MAX_PV 256 -#define MAX_LV 256 -#define MAX_VG 99 -#define MAX_PV_SIZE ((uint32_t) -1) /* 2TB in sectors - 1 */ +static struct format_instance *_create_text_instance(struct format_type *fmt, + const char *vgname, + void *context); struct dir_list { struct list list; char dir[0]; }; +struct raw_list { + struct list list; + struct device_area dev_area; +}; + struct text_context { char *path_live; /* Path to file holding live metadata */ char *path_edit; /* Path to file holding edited metadata */ @@ -42,33 +48,13 @@ struct text_context { }; /* - * NOTE: Currently there can be only one vg per file. + * NOTE: Currently there can be only one vg per text file, and locking + * assumes VG's metadata is only held in metadata areas on PVs + * inside the VG. */ -static int _pv_setup(struct format_instance *fi, struct physical_volume *pv, - struct volume_group *vg) +static int _vg_setup(struct format_instance *fid, struct volume_group *vg) { - /* setup operations for the PV structure */ - if (pv->size > MAX_PV_SIZE) - pv->size--; - if (pv->size > MAX_PV_SIZE) { - /* FIXME Limit hardcoded */ - log_error("Physical volumes cannot be bigger than 2TB"); - return 0; - } - - return 1; -} - -static int _vg_setup(struct format_instance *fi, struct volume_group *vg) -{ - /* just check max_pv and max_lv */ - if (vg->max_lv >= MAX_LV) - vg->max_lv = MAX_LV - 1; - - if (vg->max_pv >= MAX_PV) - vg->max_pv = MAX_PV - 1; - if (vg->extent_size & (vg->extent_size - 1)) { log_error("Extent size must be power of 2"); return 0; @@ -77,7 +63,7 @@ static int _vg_setup(struct format_instance *fi, struct volume_group *vg) return 1; } -static int _lv_setup(struct format_instance *fi, struct logical_volume *lv) +static int _lv_setup(struct format_instance *fid, struct logical_volume *lv) { uint64_t max_size = UINT_MAX; @@ -94,16 +80,440 @@ static int _lv_setup(struct format_instance *fi, struct logical_volume *lv) return 1; } -static struct volume_group *_vg_read(struct format_instance *fi, - const char *vgname, void *mdl) +static void _xlate_mdah(struct mda_header *mdah) +{ + struct raw_locn *rl; + + mdah->version = xlate32(mdah->version); + mdah->start = xlate64(mdah->start); + mdah->size = xlate64(mdah->size); + + rl = &mdah->raw_locns[0]; + while (rl->offset) { + rl->checksum = xlate32(rl->checksum); + rl->offset = xlate64(rl->offset); + rl->size = xlate64(rl->size); + rl++; + } +} + +static struct mda_header *_raw_read_mda_header(struct format_type *fmt, + struct device_area *dev_area) +{ + struct mda_header *mdah; + + if (!(mdah = pool_alloc(fmt->cmd->mem, MDA_HEADER_SIZE))) { + log_error("struct mda_header allocation failed"); + return NULL; + } + + if (dev_read(dev_area->dev, dev_area->start, MDA_HEADER_SIZE, mdah) != + MDA_HEADER_SIZE) { + stack; + pool_free(fmt->cmd->mem, mdah); + return NULL; + } + + if (mdah->checksum_xl != xlate32(calc_crc(INITIAL_CRC, mdah->magic, + MDA_HEADER_SIZE - + sizeof(mdah->checksum_xl)))) { + log_error("Incorrect metadata area header checksum"); + return NULL; + } + + _xlate_mdah(mdah); + + if (strncmp(mdah->magic, FMTT_MAGIC, sizeof(mdah->magic))) { + log_error("Wrong magic number in metadata area header"); + return NULL; + } + + if (mdah->version != FMTT_VERSION) { + log_error("Incompatible metadata area header version: %d", + mdah->version); + return NULL; + } + + if (mdah->start != dev_area->start) { + log_error("Incorrect start sector in metadata area header: %" + PRIu64, mdah->start); + return NULL; + } + + return mdah; +} + +static int _raw_write_mda_header(struct format_type *fmt, + struct device *dev, + uint64_t start_byte, struct mda_header *mdah) +{ + strncpy(mdah->magic, FMTT_MAGIC, sizeof(mdah->magic)); + mdah->version = FMTT_VERSION; + mdah->start = start_byte; + + _xlate_mdah(mdah); + mdah->checksum_xl = xlate32(calc_crc(INITIAL_CRC, mdah->magic, + MDA_HEADER_SIZE - + sizeof(mdah->checksum_xl))); + + if (dev_write(dev, start_byte, MDA_HEADER_SIZE, mdah) + != MDA_HEADER_SIZE) { + stack; + pool_free(fmt->cmd->mem, mdah); + return 0; + } + + return 1; +} + +static struct raw_locn *_find_vg_rlocn(struct device_area *dev_area, + struct mda_header *mdah, + const char *vgname) +{ + int len; + char vgnamebuf[NAME_LEN + 2]; + struct raw_locn *rlocn; + + rlocn = mdah->raw_locns; + + /* FIXME Ignore if checksum incorrect!!! */ + while (rlocn->offset) { + if (dev_read(dev_area->dev, dev_area->start + rlocn->offset, + sizeof(vgnamebuf), vgnamebuf) + != sizeof(vgnamebuf)) { + stack; + return NULL; + } + if (!strncmp(vgnamebuf, vgname, len = strlen(vgname)) && + (isspace(vgnamebuf[len]) || vgnamebuf[len] == '{')) { + return rlocn; + } + rlocn++; + } + + return NULL; +} + +static struct raw_locn *_vg_posn(struct format_instance *fid, + struct device_area *dev_area, + const char *vgname) +{ + + struct mda_header *mdah; + + if (!(mdah = _raw_read_mda_header(fid->fmt, dev_area))) { + stack; + return NULL; + } + + return _find_vg_rlocn(dev_area, mdah, vgname); +} + +static int _raw_holds_vgname(struct format_instance *fid, + struct device_area *dev_area, const char *vgname) +{ + int r = 0; + + if (!dev_open(dev_area->dev, O_RDONLY)) { + stack; + return 0; + } + + if (_vg_posn(fid, dev_area, vgname)) + r = 1; + + if (!dev_close(dev_area->dev)) + stack; + + return r; +} + +static struct volume_group *_vg_read_raw_area(struct format_instance *fid, + const char *vgname, + struct device_area *area) +{ + struct volume_group *vg = NULL; + struct raw_locn *rlocn; + struct mda_header *mdah; + time_t when; + char *desc; + uint64_t wrap = 0; + + if (!dev_open(area->dev, O_RDONLY)) { + stack; + return NULL; + } + + if (!(mdah = _raw_read_mda_header(fid->fmt, area))) { + stack; + goto out; + } + + if (!(rlocn = _vg_posn(fid, area, vgname))) { + stack; + goto out; + } + + if (rlocn->offset + rlocn->size > mdah->size) + wrap = (rlocn->offset + rlocn->size) - mdah->size; + + if (wrap > rlocn->offset) { + log_error("VG %s metadata too large for circular buffer", + vg->name); + goto out; + } + + if (!(vg = text_vg_import_fd(fid, dev_name(area->dev), + dev_fd(area->dev), + area->start + rlocn->offset, + rlocn->size - wrap, + area->start + MDA_HEADER_SIZE, wrap, + calc_crc, rlocn->checksum, &when, + &desc))) { + stack; + goto out; + } + + out: + if (!dev_close(area->dev)) + stack; + + return vg; +} + +static struct volume_group *_vg_read_raw(struct format_instance *fid, + const char *vgname, + struct metadata_area *mda) +{ + struct mda_context *mdac = (struct mda_context *) mda->metadata_locn; + + return _vg_read_raw_area(fid, vgname, &mdac->area); +} + +static int _vg_write_raw(struct format_instance *fid, struct volume_group *vg, + struct metadata_area *mda) +{ + struct mda_context *mdac = (struct mda_context *) mda->metadata_locn; + struct raw_locn *rlocn; + struct mda_header *mdah; + struct physical_volume *pv; + struct list *pvh; + int r = 0; + uint64_t new_wrap = 0, old_wrap = 0; + + /* FIXME Essential fix! Make dynamic (realloc? pool?) */ + char buf[65536]; + int found = 0; + + /* Ignore any mda on a PV outside the VG. vgsplit relies on this */ + list_iterate(pvh, &vg->pvs) { + pv = list_item(pvh, struct pv_list)->pv; + if (pv->dev == mdac->area.dev) { + found = 1; + break; + } + } + + if (!found) + return 1; + + if (!dev_open(mdac->area.dev, O_RDWR)) { + stack; + return 0; + } + + if (!(mdah = _raw_read_mda_header(fid->fmt, &mdac->area))) { + stack; + goto out; + } + + if ((rlocn = _find_vg_rlocn(&mdac->area, mdah, vg->name))) { + /* Start of free space - round up to next sector; circular */ + mdac->rlocn.offset = + ((rlocn->offset + rlocn->size + + (SECTOR_SIZE - rlocn->size % SECTOR_SIZE) - + MDA_HEADER_SIZE) % (mdah->size - MDA_HEADER_SIZE)) + + MDA_HEADER_SIZE; + } else { + /* Find an empty slot */ + /* FIXME Assume only one VG per mdah for now */ + mdac->rlocn.offset = MDA_HEADER_SIZE; + } + + if (!(mdac->rlocn.size = text_vg_export_raw(vg, "", buf, sizeof(buf)))) { + log_error("VG %s metadata writing failed", vg->name); + goto out; + } + + if (mdac->rlocn.offset + mdac->rlocn.size > mdah->size) + new_wrap = (mdac->rlocn.offset + mdac->rlocn.size) - mdah->size; + + if (rlocn && (rlocn->offset + rlocn->size > mdah->size)) + old_wrap = (rlocn->offset + rlocn->size) - mdah->size; + + if ((new_wrap && old_wrap) || + (rlocn && ((new_wrap > rlocn->offset) || + (old_wrap && (mdac->rlocn.offset + mdac->rlocn.size > + rlocn->offset)))) || + (mdac->rlocn.size >= mdah->size)) { + log_error("VG %s metadata too large for circular buffer", + vg->name); + goto out; + } + + log_debug("Writing %s metadata to %s at %" PRIu64 " len %" PRIu64, + vg->name, dev_name(mdac->area.dev), mdac->area.start + + mdac->rlocn.offset, mdac->rlocn.size - new_wrap); + + /* Write text out, circularly */ + if (dev_write(mdac->area.dev, mdac->area.start + mdac->rlocn.offset, + mdac->rlocn.size - new_wrap, + buf) != mdac->rlocn.size - new_wrap) { + stack; + goto out; + } + + if (new_wrap) { + log_debug("Writing metadata to %s at %" PRIu64 " len %" PRIu64, + dev_name(mdac->area.dev), mdac->area.start + + MDA_HEADER_SIZE, new_wrap); + + if (dev_write(mdac->area.dev, + mdac->area.start + MDA_HEADER_SIZE, + new_wrap, buf + mdac->rlocn.size - new_wrap) + != new_wrap) { + stack; + goto out; + } + } + + mdac->rlocn.checksum = calc_crc(INITIAL_CRC, buf, + mdac->rlocn.size - new_wrap); + if (new_wrap) + mdac->rlocn.checksum = calc_crc(mdac->rlocn.checksum, + buf + mdac->rlocn.size - + new_wrap, new_wrap); + + r = 1; + + out: + if (!dev_close(mdac->area.dev)) + stack; + + return r; +} + +static int _vg_commit_raw(struct format_instance *fid, struct volume_group *vg, + struct metadata_area *mda) +{ + struct mda_context *mdac = (struct mda_context *) mda->metadata_locn; + struct mda_header *mdah; + struct raw_locn *rlocn; + struct physical_volume *pv; + struct list *pvh; + int r = 0; + int found = 0; + + /* Ignore any mda on a PV outside the VG. vgsplit relies on this */ + list_iterate(pvh, &vg->pvs) { + pv = list_item(pvh, struct pv_list)->pv; + if (pv->dev == mdac->area.dev) { + found = 1; + break; + } + } + + if (!found) + return 1; + + if (!dev_open(mdac->area.dev, O_RDWR)) { + stack; + return 0; + } + + if (!(mdah = _raw_read_mda_header(fid->fmt, &mdac->area))) { + stack; + goto out; + } + + if (!(rlocn = _find_vg_rlocn(&mdac->area, mdah, vg->name))) { + rlocn = &mdah->raw_locns[0]; + mdah->raw_locns[1].offset = 0; + } + + rlocn->offset = mdac->rlocn.offset; + rlocn->size = mdac->rlocn.size; + rlocn->checksum = mdac->rlocn.checksum; + + log_debug("Committing %s metadata (%u) to %s header at %" PRIu64, + vg->name, vg->seqno, dev_name(mdac->area.dev), + mdac->area.start); + if (!_raw_write_mda_header(fid->fmt, mdac->area.dev, mdac->area.start, + mdah)) { + log_error("Failed to write metadata area header"); + goto out; + } + + r = 1; + + out: + if (!dev_close(mdac->area.dev)) + stack; + + return r; +} + +static int _vg_remove_raw(struct format_instance *fid, struct volume_group *vg, + struct metadata_area *mda) +{ + struct mda_context *mdac = (struct mda_context *) mda->metadata_locn; + struct mda_header *mdah; + struct raw_locn *rlocn; + int r = 0; + + if (!dev_open(mdac->area.dev, O_RDWR)) { + stack; + return 0; + } + + if (!(mdah = _raw_read_mda_header(fid->fmt, &mdac->area))) { + stack; + goto out; + } + + if (!(rlocn = _find_vg_rlocn(&mdac->area, mdah, vg->name))) { + rlocn = &mdah->raw_locns[0]; + mdah->raw_locns[1].offset = 0; + } + + rlocn->offset = 0; + rlocn->size = 0; + rlocn->checksum = 0; + + if (!_raw_write_mda_header(fid->fmt, mdac->area.dev, mdac->area.start, + mdah)) { + log_error("Failed to write metadata area header"); + goto out; + } + + r = 1; + + out: + if (!dev_close(mdac->area.dev)) + stack; + + return r; +} + +static struct volume_group *_vg_read_file_name(struct format_instance *fid, + const char *vgname, + const char *path_live) { - struct text_context *tc = (struct text_context *) mdl; struct volume_group *vg; time_t when; char *desc; - if (!(vg = text_vg_import(fi, tc->path_live, fi->fmt->cmd->um, &when, - &desc))) { + if (!(vg = text_vg_import_file(fid, path_live, &when, &desc))) { stack; return NULL; } @@ -113,20 +523,30 @@ static struct volume_group *_vg_read(struct format_instance *fi, * text file (this restriction may remain). We need to * check that it contains the correct volume group. */ - if (strcmp(vgname, vg->name)) { - pool_free(fi->fmt->cmd->mem, vg); + if (vgname && strcmp(vgname, vg->name)) { + pool_free(fid->fmt->cmd->mem, vg); log_err("'%s' does not contain volume group '%s'.", - tc->path_live, vgname); + path_live, vgname); return NULL; - } + } else + log_debug("Read volume group %s from %s", vg->name, path_live); return vg; } -static int _vg_write(struct format_instance *fi, struct volume_group *vg, - void *mdl) +static struct volume_group *_vg_read_file(struct format_instance *fid, + const char *vgname, + struct metadata_area *mda) +{ + struct text_context *tc = (struct text_context *) mda->metadata_locn; + + return _vg_read_file_name(fid, vgname, tc->path_live); +} + +static int _vg_write_file(struct format_instance *fid, struct volume_group *vg, + struct metadata_area *mda) { - struct text_context *tc = (struct text_context *) mdl; + struct text_context *tc = (struct text_context *) mda->metadata_locn; FILE *fp; int fd; @@ -157,7 +577,9 @@ static int _vg_write(struct format_instance *fi, struct volume_group *vg, return 0; } - if (!text_vg_export(fp, vg, tc->desc)) { + log_debug("Writing %s metadata to %s", vg->name, temp_file); + + if (!text_vg_export_file(vg, tc->desc, fp)) { log_error("Failed to write metadata to %s.", temp_file); fclose(fp); return 0; @@ -175,6 +597,7 @@ static int _vg_write(struct format_instance *fi, struct volume_group *vg, } if (rename(temp_file, tc->path_edit)) { + log_debug("Renaming %s to %s", temp_file, tc->path_edit); log_error("%s: rename to %s failed: %s", temp_file, tc->path_edit, strerror(errno)); return 0; @@ -183,34 +606,77 @@ static int _vg_write(struct format_instance *fi, struct volume_group *vg, return 1; } -static int _pv_commit(struct format_instance *fi, struct physical_volume *pv, - void *mdl) +static int _vg_commit_file_backup(struct format_instance *fid, + struct volume_group *vg, + struct metadata_area *mda) { - // struct text_context *tc = (struct text_context *) mdl; + struct text_context *tc = (struct text_context *) mda->metadata_locn; + + if (test_mode()) { + log_verbose("Test mode: Skipping committing %s metadata (%u)", + vg->name, vg->seqno); + if (unlink(tc->path_edit)) { + log_debug("Unlinking %s", tc->path_edit); + log_sys_error("unlink", tc->path_edit); + return 0; + } + } else { + log_debug("Committing %s metadata (%u)", vg->name, vg->seqno); + log_debug("Renaming %s to %s", tc->path_edit, tc->path_live); + if (rename(tc->path_edit, tc->path_live)) { + log_error("%s: rename to %s failed: %s", tc->path_edit, + tc->path_edit, strerror(errno)); + return 0; + } + } + + sync(); return 1; } -static int _vg_commit(struct format_instance *fi, struct volume_group *vg, - void *mdl) +static int _vg_commit_file(struct format_instance *fid, struct volume_group *vg, + struct metadata_area *mda) { - struct text_context *tc = (struct text_context *) mdl; + struct text_context *tc = (struct text_context *) mda->metadata_locn; + char *slash; + char newname[PATH_MAX]; + int len; - if (rename(tc->path_edit, tc->path_live)) { - log_error("%s: rename to %s failed: %s", tc->path_edit, - tc->path_edit, strerror(errno)); + if (!_vg_commit_file_backup(fid, vg, mda)) return 0; - } - sync(); + /* vgrename? */ + if ((slash = rindex(tc->path_live, '/'))) + slash = slash + 1; + else + slash = tc->path_live; + + if (strcmp(slash, vg->name)) { + len = slash - tc->path_live; + strncpy(newname, tc->path_live, len); + strcpy(newname + len, vg->name); + log_debug("Renaming %s to %s", tc->path_live, newname); + if (test_mode()) + log_verbose("Test mode: Skipping rename"); + else { + if (rename(tc->path_live, newname)) { + log_error("%s: rename to %s failed: %s", + tc->path_live, newname, + strerror(errno)); + sync(); + return 0; + } + } + } return 1; } -static int _vg_remove(struct format_instance *fi, struct volume_group *vg, - void *mdl) +static int _vg_remove_file(struct format_instance *fid, struct volume_group *vg, + struct metadata_area *mda) { - struct text_context *tc = (struct text_context *) mdl; + struct text_context *tc = (struct text_context *) mda->metadata_locn; if (path_exists(tc->path_edit) && unlink(tc->path_edit)) { log_sys_error("unlink", tc->path_edit); @@ -227,44 +693,19 @@ static int _vg_remove(struct format_instance *fi, struct volume_group *vg, return 1; } -/* Add vgname to list if it's not already there */ -static int _add_vgname(struct format_type *fmt, struct list *names, - char *vgname) -{ - struct list *nlh; - struct name_list *nl; - - list_iterate(nlh, names) { - nl = list_item(nlh, struct name_list); - if (!strcmp(vgname, nl->name)) - return 1; - } - - vgcache_add(vgname, NULL, NULL, fmt); - - if (!(nl = pool_alloc(fmt->cmd->mem, sizeof(*nl)))) { - stack; - return 0; - } - - if (!(nl->name = pool_strdup(fmt->cmd->mem, vgname))) { - log_error("strdup %s failed", vgname); - return 0; - } - - list_add(names, &nl->list); - return 1; -} - -static struct list *_get_vgs(struct format_type *fmt, struct list *names) +static int _scan_file(struct format_type *fmt) { struct dirent *dirent; struct dir_list *dl; struct list *dlh, *dir_list; char *tmp; DIR *d; + struct volume_group *vg; + struct format_instance *fid; + char path[PATH_MAX]; + char *vgname; - dir_list = (struct list *) fmt->private; + dir_list = &((struct mda_lists *) fmt->private)->dirs; list_iterate(dlh, dir_list) { dl = list_item(dlh, struct dir_list); @@ -277,151 +718,468 @@ static struct list *_get_vgs(struct format_type *fmt, struct list *names) strcmp(dirent->d_name, "..") && (!(tmp = strstr(dirent->d_name, ".tmp")) || tmp != dirent->d_name + strlen(dirent->d_name) - - 4)) - if (!_add_vgname(fmt, names, dirent->d_name)) - return NULL; + - 4)) { + vgname = dirent->d_name; + if (lvm_snprintf(path, PATH_MAX, "%s/%s", + dl->dir, vgname) < 0) { + log_error("Name too long %s/%s", + dl->dir, vgname); + break; + } + + /* FIXME stat file to see if it's changed */ + fid = _create_text_instance(fmt, NULL, NULL); + if ((vg = _vg_read_file_name(fid, vgname, + path))) + cache_update_vg(vg); + } if (closedir(d)) log_sys_error("closedir", dl->dir); } - return names; + return 1; } -static struct list *_get_pvs(struct format_type *fmt, struct list *results) +int vgname_from_mda(struct format_type *fmt, struct device_area *dev_area, + char *buf, uint32_t size) { - struct pv_list *pvl, *rhl; - struct list *vgh; - struct list *pvh; - struct list *names = pool_alloc(fmt->cmd->mem, sizeof(*names)); - struct list *rh; - struct name_list *nl; - struct volume_group *vg; + struct raw_locn *rlocn; + struct mda_header *mdah; + int already_open; + int len; + int r = 0; + + already_open = dev_is_open(dev_area->dev); - list_init(names); - if (!_get_vgs(fmt, names)) { + if (!already_open && !dev_open(dev_area->dev, O_RDONLY)) { stack; - return NULL; + return 0; } - list_iterate(vgh, names) { + if (!(mdah = _raw_read_mda_header(fmt, dev_area))) { + stack; + goto out; + } - nl = list_item(vgh, struct name_list); - if (!(vg = vg_read(fmt->cmd, nl->name))) { - log_error("format_text: _get_pvs failed to read VG %s", - nl->name); - continue; + rlocn = mdah->raw_locns; + + while (rlocn->offset) { + if (dev_read(dev_area->dev, dev_area->start + rlocn->offset, + size, buf) != size) { + stack; + goto out; } - /* FIXME Use temp hash! */ - list_iterate(pvh, &vg->pvs) { - pvl = list_item(pvh, struct pv_list); - - /* If in use, remove from list of orphans */ - list_iterate(rh, results) { - rhl = list_item(rh, struct pv_list); - if (id_equal(&rhl->pv->id, &pvl->pv->id)) { - if (*rhl->pv->vg_name) - log_err("PV %s in two VGs " - "%s and %s", - dev_name(rhl->pv->dev), - rhl->pv->vg_name, - vg->name); - else - memcpy(&rhl->pv, &pvl->pv, - sizeof(struct - physical_volume)); - } - } + len = 0; + while (buf[len] && !isspace(buf[len]) && buf[len] != '{' && + len < size - 1) + len++; + buf[len] = '\0'; + + /* Ignore this entry if the characters aren't permissible */ + if (!validate_vgname(buf)) { + stack; + goto out; } + + r = 1; + break; + + /* FIXME Cope with returning a list */ + rlocn++; } - pool_free(fmt->cmd->mem, names); - return results; + out: + if (!already_open && dev_close(dev_area->dev)) + stack; + + return r; } -static int _pv_write(struct format_instance *fi, struct physical_volume *pv, - void *mdl) +static int _scan_raw(struct format_type *fmt) { - /* No on-disk PV structure change required! */ - /* FIXME vgcache could be wrong */ + struct raw_list *rl; + struct list *rlh, *raw_list; + char vgnamebuf[NAME_LEN + 2]; + struct volume_group *vg; + struct format_instance fid; + + raw_list = &((struct mda_lists *) fmt->private)->raws; + + fid.fmt = fmt; + list_init(&fid.metadata_areas); + + list_iterate(rlh, raw_list) { + rl = list_item(rlh, struct raw_list); + + /* FIXME We're reading mdah twice here... */ + if (vgname_from_mda(fmt, &rl->dev_area, vgnamebuf, + sizeof(vgnamebuf))) { + if ((vg = _vg_read_raw_area(&fid, vgnamebuf, + &rl->dev_area))) + cache_update_vg(vg); + } + } + return 1; - //return (fi->fmt->cmd->fmt1->ops->pv_write(fi, pv, NULL)); -/*** FIXME Not required? +} + +static int _scan(struct format_type *fmt) +{ + return (_scan_file(fmt) & _scan_raw(fmt)); +} + +/* For orphan, creates new mdas according to policy. + Always have an mda between end-of-label and PE_ALIGN boundary */ +static int _mda_setup(struct format_type *fmt, + uint64_t pe_start, uint64_t pe_end, + int pvmetadatacopies, + uint64_t pvmetadatasize, struct list *mdas, + struct physical_volume *pv, struct volume_group *vg) +{ + uint64_t mda_adjustment, disk_size, alignment; + uint64_t start1, mda_size1; /* First area - start of disk */ + uint64_t start2, mda_size2; /* Second area - end of disk */ + uint64_t wipe_size = 8 << SECTOR_SHIFT; + + if (!pvmetadatacopies) { + /* Space available for PEs */ + pv->size -= PE_ALIGN; + return 1; + } + + alignment = PE_ALIGN << SECTOR_SHIFT; + disk_size = pv->size << SECTOR_SHIFT; + pe_start <<= SECTOR_SHIFT; + pe_end <<= SECTOR_SHIFT; + + if (pe_end > disk_size) { + log_error("Physical extents end beyond end of device %s!", + dev_name(pv->dev)); + return 0; + } + + /* Requested metadatasize */ + mda_size1 = pvmetadatasize << SECTOR_SHIFT; + + /* Space available for PEs (before any mdas created) */ + pv->size -= LABEL_SCAN_SECTORS; + + /* Place mda straight after label area at start of disk */ + start1 = LABEL_SCAN_SIZE; + + /* Round up to PE_ALIGN boundary */ + mda_adjustment = (mda_size1 + start1) % alignment; + if (mda_adjustment) + mda_size1 += (alignment - mda_adjustment); + + /* If we already have PEs, avoid overlap */ + if (pe_start || pe_end) { + if (pe_start <= start1) + mda_size1 = 0; + else if (start1 + mda_size1 > pe_start) + mda_size1 = pe_start - start1; + } + + /* FIXME If creating new mdas, wipe them! */ + if (mda_size1) { + if (!add_mda(fmt, fmt->cmd->mem, mdas, pv->dev, start1, + mda_size1)) + return 0; + + if (!dev_zero(pv->dev, start1, + mda_size1 > wipe_size ? wipe_size : mda_size1)) { + log_error("Failed to wipe new metadata area"); + return 0; + } + + pv->size -= mda_size1 >> SECTOR_SHIFT; + if (pvmetadatacopies == 1) + return 1; + } else + start1 = 0; + + /* A second copy at end of disk */ + mda_size2 = pvmetadatasize << SECTOR_SHIFT; + + /* Ensure it's not going to be bigger than the disk! */ + if (mda_size2 > disk_size) + mda_size2 = disk_size - start1 - mda_size1; + + mda_adjustment = (disk_size - mda_size2) % alignment; + if (mda_adjustment) + mda_size2 += mda_adjustment; + + start2 = disk_size - mda_size2; + + /* If we already have PEs, avoid overlap */ + if (pe_start || pe_end) { + if (start2 < pe_end) { + mda_size2 -= (pe_end - start2); + start2 = pe_end; + } + } + + /* If we already have a first mda, avoid overlap */ + if (mda_size1) { + if (start2 < start1 + mda_size1) { + mda_size2 -= (start1 + mda_size1 - start2); + start2 = start1 + mda_size1; + } + /* No room for any PEs here now! */ + } + + if (mda_size2) { + if (!add_mda(fmt, fmt->cmd->mem, mdas, pv->dev, start2, + mda_size2)) + return 0; + if (!dev_zero(pv->dev, start2, + mda_size1 > wipe_size ? wipe_size : mda_size1)) { + log_error("Failed to wipe new metadata area"); + return 0; + } + pv->size -= mda_size2 >> SECTOR_SHIFT; + } else + return 0; + + return 1; +} + +/* Only for orphans */ +/* Set label_sector to -1 if rewriting existing label into same sector */ +static int _pv_write(struct format_type *fmt, struct physical_volume *pv, + struct list *mdas, int64_t label_sector) +{ + struct label *label; + struct cache_info *info; + struct mda_context *mdac; + struct list *mdash; + struct metadata_area *mda; + char buf[MDA_HEADER_SIZE]; + struct mda_header *mdah = (struct mda_header *) buf; + uint64_t adjustment; + + if (!(info = cache_add(fmt->labeller, (char *) &pv->id, pv->dev, + ORPHAN, NULL))) { + stack; + return 0; + } + label = info->label; + + if (label_sector != -1) + label->sector = label_sector; + + info->device_size = pv->size << SECTOR_SHIFT; + info->fmt = fmt; + + /* If mdas supplied, use them regardless of existing ones, */ + /* otherwise retain existing ones */ + if (mdas) { + if (info->mdas.n) + del_mdas(&info->mdas); + else + list_init(&info->mdas); + list_iterate(mdash, mdas) { + mda = list_item(mdash, struct metadata_area); + mdac = mda->metadata_locn; + log_debug("Creating metadata area on %s at sector %" + PRIu64 " size %" PRIu64 " sectors", + dev_name(mdac->area.dev), + mdac->area.start >> SECTOR_SHIFT, + mdac->area.size >> SECTOR_SHIFT); + add_mda(fmt, NULL, &info->mdas, mdac->area.dev, + mdac->area.start, mdac->area.size); + } + /* FIXME Temporary until mda creation supported by tools */ + } else if (!info->mdas.n) { + list_init(&info->mdas); + } + + if (info->das.n) + del_das(&info->das); + else + list_init(&info->das); + + /* Set pe_start to first aligned sector after any metadata + * areas that begin before pe_start */ + pv->pe_start = PE_ALIGN; + list_iterate(mdash, &info->mdas) { + mda = list_item(mdash, struct metadata_area); + mdac = (struct mda_context *) mda->metadata_locn; + if (pv->dev == mdac->area.dev && + (mdac->area.start < (pv->pe_start << SECTOR_SHIFT)) && + (mdac->area.start + mdac->area.size > + (pv->pe_start << SECTOR_SHIFT))) { + pv->pe_start = (mdac->area.start + mdac->area.size) + >> SECTOR_SHIFT; + adjustment = pv->pe_start % PE_ALIGN; + if (adjustment) + pv->pe_start += (PE_ALIGN - adjustment); + } + } + if (!add_da(fmt, NULL, &info->das, pv->pe_start << SECTOR_SHIFT, 0)) { + stack; + return 0; + } + + if (!dev_open(pv->dev, O_RDWR)) { + stack; + return 0; + } + + list_iterate(mdash, &info->mdas) { + mda = list_item(mdash, struct metadata_area); + mdac = mda->metadata_locn; + memset(&buf, 0, sizeof(buf)); + mdah->size = mdac->area.size; + if (!_raw_write_mda_header(fmt, mdac->area.dev, + mdac->area.start, mdah)) { + stack; + if (!dev_close(pv->dev)) + stack; + return 0; + } + } + + label_write(pv->dev, label); + + if (!dev_close(pv->dev)) { + stack; + return 0; + } + + return 1; +} + +static int _get_pv_from_vg(struct format_type *fmt, const char *vg_name, + const char *id, struct physical_volume *pv) +{ struct volume_group *vg; struct list *pvh; + struct pv_list *pvl; + int consistent = 0; - vg = _vg_read(fi, pv->vg_name); + if (!(vg = vg_read(fmt->cmd, vg_name, &consistent))) { + log_error("format_text: _vg_read failed to read VG %s", + vg_name); + return 0; + } - // Find the PV in this VG - if (vg) { - list_iterate(pvh, &vg->pvs) { - struct pv_list *vgpv = list_item(pvh, struct pv_list); - - if (id_equal(&pv->id, &vgpv->pv->id)) { - vgpv->pv->status = pv->status; - vgpv->pv->size = pv->size; - - // Not sure if it's worth doing these - vgpv->pv->pe_size = pv->pe_size; - vgpv->pv->pe_count = pv->pe_count; - vgpv->pv->pe_start = pv->pe_start; - vgpv->pv->pe_alloc_count = pv->pe_alloc_count; - - // Write it back - _vg_write(fi, vg); - pool_free(fi->fmt->cmd->mem, vg); - return 1; - } + if (!consistent) + log_error("Warning: Volume group %s is not consistent", + vg_name); + + list_iterate(pvh, &vg->pvs) { + pvl = list_item(pvh, struct pv_list); + if (id_equal(&pvl->pv->id, (struct id *) id)) { + memcpy(pv, pvl->pv, sizeof(*pv)); + return 1; } - pool_free(fi->fmt->cmd->mem, vg); } - - // Can't handle PVs not in a VG return 0; -***/ } -static int _pv_read(struct format_type *fmt, const char *pv_name, - struct physical_volume *pv) +static int _add_raw(struct list *raw_list, struct device_area *dev_area) { - struct pv_list *pvl; - struct list *vgh; - struct list *pvh; - struct list *names = pool_alloc(fmt->cmd->mem, sizeof(*names)); - struct name_list *nl; - struct volume_group *vg; - struct id *id; + struct raw_list *rl; + struct list *rlh; + + /* Already present? */ + list_iterate(rlh, raw_list) { + rl = list_item(rlh, struct raw_list); + /* FIXME Check size/overlap consistency too */ + if (rl->dev_area.dev == dev_area->dev && + rl->dev_area.start == dev_area->start) + return 1; + } - /* FIXME Push up to pv_read */ - if (!(id = uuid_map_lookup_label(fmt->cmd->mem, fmt->cmd->um, pv_name))) { + if (!(rl = dbg_malloc(sizeof(struct raw_list)))) { + log_error("_add_raw allocation failed"); + return 0; + } + memcpy(&rl->dev_area, dev_area, sizeof(*dev_area)); + list_add(raw_list, &rl->list); + + return 1; +} + +static int _pv_read(struct format_type *fmt, const char *pv_name, + struct physical_volume *pv, struct list *mdas) +{ + struct label *label; + struct device *dev; + struct cache_info *info; + struct metadata_area *mda, *mda_new; + struct mda_context *mdac, *mdac_new; + struct list *mdah, *dah; + struct data_area_list *da; + + if (!(dev = dev_cache_get(pv_name, fmt->cmd->filter))) { stack; return 0; } - list_init(names); - if (!_get_vgs(fmt, names)) { + if (!(label_read(dev, &label))) { stack; return 0; } + info = (struct cache_info *) label->info; + + /* Have we already cached vgname? */ + if (info->vginfo && info->vginfo->vgname && *info->vginfo->vgname && + _get_pv_from_vg(info->fmt, info->vginfo->vgname, info->dev->pvid, + pv)) { + return 1; + } + + /* Perform full scan and try again */ + cache_label_scan(fmt->cmd, 0); + + if (info->vginfo && info->vginfo->vgname && *info->vginfo->vgname && + _get_pv_from_vg(info->fmt, info->vginfo->vgname, info->dev->pvid, + pv)) { + return 1; + } + + /* Orphan */ + pv->dev = info->dev; + pv->fmt = info->fmt; + pv->size = info->device_size >> SECTOR_SHIFT; + pv->vg_name = ORPHAN; + memcpy(&pv->id, &info->dev->pvid, sizeof(pv->id)); + + /* Currently only support exactly one data area */ + if (list_size(&info->das) != 1) { + log_error("Must be exactly one data area (found %d) on PV %s", + list_size(&info->das), dev_name(dev)); + return 0; + } + list_iterate(dah, &info->das) { + da = list_item(dah, struct data_area_list); + pv->pe_start = da->disk_locn.offset >> SECTOR_SHIFT; + } - list_iterate(vgh, names) { + if (!mdas) + return 1; - nl = list_item(vgh, struct name_list); - if (!(vg = vg_read(fmt->cmd, nl->name))) { - log_error("format_text: _pv_read failed to read VG %s", - nl->name); + /* Add copy of mdas to supplied list */ + list_iterate(mdah, &info->mdas) { + mda = list_item(mdah, struct metadata_area); + mdac = (struct mda_context *) mda->metadata_locn; + if (!(mda_new = pool_alloc(fmt->cmd->mem, sizeof(*mda_new)))) { + log_error("metadata_area allocation failed"); return 0; } - list_iterate(pvh, &vg->pvs) { - pvl = list_item(pvh, struct pv_list); - if (id_equal(&pvl->pv->id, id)) { - memcpy(pv, pvl->pv, sizeof(*pv)); - break; - } + if (!(mdac_new = pool_alloc(fmt->cmd->mem, sizeof(*mdac_new)))) { + log_error("metadata_area allocation failed"); + return 0; } + memcpy(mda_new, mda, sizeof(*mda)); + memcpy(mdac_new, mdac, sizeof(*mdac)); + mda_new->metadata_locn = mdac_new; + list_add(mdas, &mda_new->list); } - pool_free(fmt->cmd->mem, names); return 1; } @@ -440,25 +1198,152 @@ static void _free_dirs(struct list *dir_list) } } +static void _free_raws(struct list *raw_list) +{ + struct list *rl, *tmp; + + list_iterate_safe(rl, tmp, raw_list) { + list_del(rl); + dbg_free(rl); + } +} + static void _destroy(struct format_type *fmt) { if (fmt->private) { - _free_dirs((struct list *) fmt->private); + _free_dirs(&((struct mda_lists *) fmt->private)->dirs); + _free_raws(&((struct mda_lists *) fmt->private)->raws); dbg_free(fmt->private); } dbg_free(fmt); } +static struct metadata_area_ops _metadata_text_file_ops = { + vg_read:_vg_read_file, + vg_write:_vg_write_file, + vg_remove:_vg_remove_file, + vg_commit:_vg_commit_file +}; + +static struct metadata_area_ops _metadata_text_file_backup_ops = { + vg_read:_vg_read_file, + vg_write:_vg_write_file, + vg_remove:_vg_remove_file, + vg_commit:_vg_commit_file_backup +}; + +static struct metadata_area_ops _metadata_text_raw_ops = { + vg_read:_vg_read_raw, + vg_write:_vg_write_raw, + vg_remove:_vg_remove_raw, + vg_commit:_vg_commit_raw +}; + +/* pvmetadatasize in sectors */ +static int _pv_setup(struct format_type *fmt, + uint64_t pe_start, uint32_t extent_count, + uint32_t extent_size, + int pvmetadatacopies, + uint64_t pvmetadatasize, struct list *mdas, + struct physical_volume *pv, struct volume_group *vg) +{ + struct metadata_area *mda, *mda_new, *mda2; + struct mda_context *mdac, *mdac_new, *mdac2; + struct list *pvmdas, *pvmdash, *mdash; + struct cache_info *info; + int found; + uint64_t pe_end = 0; + + /* FIXME if vg, adjust start/end of pe area to avoid mdas! */ + + /* FIXME Cope with pvchange */ + /* FIXME Merge code with _create_text_instance */ + + /* If new vg, add any further mdas on this PV to the fid's mda list */ + if (vg) { + /* Iterate through all mdas on this PV */ + if ((info = info_from_pvid(pv->dev->pvid))) { + pvmdas = &info->mdas; + list_iterate(pvmdash, pvmdas) { + mda = list_item(pvmdash, struct metadata_area); + mdac = + (struct mda_context *) mda->metadata_locn; + + /* FIXME Check it isn't already in use */ + + /* Ensure it isn't already on list */ + found = 0; + list_iterate(mdash, mdas) { + mda2 = + list_item(mdash, + struct metadata_area); + if (mda2->ops != + &_metadata_text_raw_ops) + continue; + mdac2 = + (struct mda_context *) mda2-> + metadata_locn; + if (!memcmp + (&mdac2->area, &mdac->area, + sizeof(mdac->area))) { + found = 1; + break; + } + } + if (found) + continue; + + if (!(mda_new = pool_alloc(fmt->cmd->mem, + sizeof(*mda_new)))) { + stack; + return 0; + } + + if (!(mdac_new = pool_alloc(fmt->cmd->mem, + sizeof(*mdac_new)))) + { + stack; + return 0; + } + /* FIXME multiple dev_areas inside area */ + memcpy(mda_new, mda, sizeof(*mda)); + memcpy(mdac_new, mdac, sizeof(*mdac)); + mda_new->metadata_locn = mdac_new; + list_add(mdas, &mda_new->list); + } + } + + /* Unlike LVM1, we don't store this outside a VG */ + /* FIXME Default from config file? vgextend cmdline flag? */ + pv->status |= ALLOCATABLE_PV; + } else { + if (extent_count) + pe_end = pe_start + extent_count * extent_size - 1; + if (!_mda_setup(fmt, pe_start, pe_end, pvmetadatacopies, + pvmetadatasize, mdas, pv, vg)) { + stack; + return 0; + } + + } + + return 1; +} + +/* NULL vgname means use only the supplied context e.g. an archive file */ static struct format_instance *_create_text_instance(struct format_type *fmt, const char *vgname, void *context) { struct format_instance *fid; - struct metadata_area *mda; + struct metadata_area *mda, *mda_new; + struct mda_context *mdac, *mdac_new; struct dir_list *dl; - struct list *dlh, *dir_list; + struct raw_list *rl; + struct list *dlh, *dir_list, *rlh, *raw_list, *mdas, *mdash, *infoh; char path[PATH_MAX]; + struct cache_vginfo *vginfo; if (!(fid = pool_alloc(fmt->cmd->mem, sizeof(*fid)))) { log_error("Couldn't allocate format instance object."); @@ -474,10 +1359,11 @@ static struct format_instance *_create_text_instance(struct format_type *fmt, stack; return NULL; } + mda->ops = &_metadata_text_file_backup_ops; mda->metadata_locn = context; list_add(&fid->metadata_areas, &mda->list); } else { - dir_list = (struct list *) fmt->private; + dir_list = &((struct mda_lists *) fmt->private)->dirs; list_iterate(dlh, dir_list) { dl = list_item(dlh, struct dir_list); @@ -488,21 +1374,83 @@ static struct format_instance *_create_text_instance(struct format_type *fmt, return NULL; } - context = create_text_context(fmt, path, NULL); + context = create_text_context(fmt->cmd, path, NULL); if (!(mda = pool_alloc(fmt->cmd->mem, sizeof(*mda)))) { stack; return NULL; } + mda->ops = &_metadata_text_file_ops; mda->metadata_locn = context; list_add(&fid->metadata_areas, &mda->list); } + + raw_list = &((struct mda_lists *) fmt->private)->raws; + + list_iterate(rlh, raw_list) { + rl = list_item(rlh, struct raw_list); + + /* FIXME Cache this; rescan below if some missing */ + if (!_raw_holds_vgname(fid, &rl->dev_area, vgname)) + continue; + + if (!(mda = pool_alloc(fmt->cmd->mem, sizeof(*mda)))) { + stack; + return NULL; + } + + if (!(mdac = pool_alloc(fmt->cmd->mem, sizeof(*mdac)))) { + stack; + return NULL; + } + mda->metadata_locn = mdac; + /* FIXME Allow multiple dev_areas inside area */ + memcpy(&mdac->area, &rl->dev_area, sizeof(mdac->area)); + mda->ops = &_metadata_text_raw_ops; + list_add(&fid->metadata_areas, &mda->list); + } + + /* Scan PVs in VG for any further MDAs */ + cache_label_scan(fmt->cmd, 0); + if (!(vginfo = vginfo_from_vgname(vgname))) { + stack; + goto out; + } + list_iterate(infoh, &vginfo->infos) { + mdas = &(list_item(infoh, struct cache_info)->mdas); + list_iterate(mdash, mdas) { + mda = list_item(mdash, struct metadata_area); + mdac = + (struct mda_context *) mda->metadata_locn; + + /* FIXME Check it holds this VG */ + if (!(mda_new = pool_alloc(fmt->cmd->mem, + sizeof(*mda_new)))) { + stack; + return NULL; + } + + if (!(mdac_new = pool_alloc(fmt->cmd->mem, + sizeof(*mdac_new)))) + { + stack; + return NULL; + } + /* FIXME multiple dev_areas inside area */ + memcpy(mda_new, mda, sizeof(*mda)); + memcpy(mdac_new, mdac, sizeof(*mdac)); + mda_new->metadata_locn = mdac_new; + list_add(&fid->metadata_areas, &mda_new->list); + } + } + /* FIXME Check raw metadata area count - rescan if required */ } + out: return fid; } -void *create_text_context(struct format_type *fmt, const char *path, +void *create_text_context(struct cmd_context *cmd, const char *path, const char *desc) { struct text_context *tc; @@ -514,17 +1462,17 @@ void *create_text_context(struct format_type *fmt, const char *path, return NULL; } - if (!(tc = pool_alloc(fmt->cmd->mem, sizeof(*tc)))) { + if (!(tc = pool_alloc(cmd->mem, sizeof(*tc)))) { stack; return NULL; } - if (!(tc->path_live = pool_strdup(fmt->cmd->mem, path))) { + if (!(tc->path_live = pool_strdup(cmd->mem, path))) { stack; goto no_mem; } - if (!(tc->path_edit = pool_alloc(fmt->cmd->mem, strlen(path) + 5))) { + if (!(tc->path_edit = pool_alloc(cmd->mem, strlen(path) + 5))) { stack; goto no_mem; } @@ -533,7 +1481,7 @@ void *create_text_context(struct format_type *fmt, const char *path, if (!desc) desc = ""; - if (!(tc->desc = pool_strdup(fmt->cmd->mem, desc))) { + if (!(tc->desc = pool_strdup(cmd->mem, desc))) { stack; goto no_mem; } @@ -541,28 +1489,22 @@ void *create_text_context(struct format_type *fmt, const char *path, return (void *) tc; no_mem: - pool_free(fmt->cmd->mem, tc); + pool_free(cmd->mem, tc); log_err("Couldn't allocate text format context object."); return NULL; } static struct format_handler _text_handler = { - get_vgs: _get_vgs, - get_pvs: _get_pvs, - pv_read: _pv_read, - pv_setup: _pv_setup, - pv_write: _pv_write, - pv_commit: _pv_commit, - vg_setup: _vg_setup, - lv_setup: _lv_setup, - vg_read: _vg_read, - vg_write: _vg_write, - vg_remove: _vg_remove, - vg_commit: _vg_commit, + scan:_scan, + pv_read:_pv_read, + pv_setup:_pv_setup, + pv_write:_pv_write, + vg_setup:_vg_setup, + lv_setup:_lv_setup, create_instance:_create_text_instance, destroy_instance:_destroy_instance, - destroy: _destroy + destroy:_destroy }; static int _add_dir(const char *dir, struct list *dir_list) @@ -574,6 +1516,7 @@ static int _add_dir(const char *dir, struct list *dir_list) log_error("_add_dir allocation failed"); return 0; } + log_very_verbose("Adding text format metadata dir: %s", dir); strcpy(dl->dir, dir); list_add(dir_list, &dl->list); return 1; @@ -582,12 +1525,64 @@ static int _add_dir(const char *dir, struct list *dir_list) return 0; } +static int _get_config_disk_area(struct cmd_context *cmd, + struct config_node *cn, struct list *raw_list) +{ + struct device_area dev_area; + char *id_str; + struct id id; + + if (!(cn = cn->child)) { + log_error("Empty metadata disk_area section of config file"); + return 0; + } + + if (!get_config_uint64(cn, "start_sector", '/', &dev_area.start)) { + log_error("Missing start_sector in metadata disk_area section " + "of config file"); + return 0; + } + dev_area.start <<= SECTOR_SHIFT; + + if (!get_config_uint64(cn, "size", '/', &dev_area.size)) { + log_error("Missing size in metadata disk_area section " + "of config file"); + return 0; + } + dev_area.size <<= SECTOR_SHIFT; + + if (!get_config_str(cn, "id", '/', &id_str)) { + log_error("Missing uuid in metadata disk_area section " + "of config file"); + return 0; + } + + if (!id_read_format(&id, id_str)) { + log_error("Invalid uuid in metadata disk_area section " + "of config file: %s", id_str); + return 0; + } + + if (!(dev_area.dev = device_from_pvid(cmd, &id))) { + char buffer[64]; + + if (!id_write_format(&id, buffer, sizeof(buffer))) + log_err("Couldn't find device."); + else + log_err("Couldn't find device with uuid '%s'.", buffer); + + return 0; + } + + return _add_raw(raw_list, &dev_area); +} + struct format_type *create_text_format(struct cmd_context *cmd) { struct format_type *fmt; struct config_node *cn; struct config_value *cv; - struct list *dir_list; + struct mda_lists *mda_lists; if (!(fmt = dbg_malloc(sizeof(*fmt)))) { stack; @@ -597,41 +1592,58 @@ struct format_type *create_text_format(struct cmd_context *cmd) fmt->cmd = cmd; fmt->ops = &_text_handler; fmt->name = FMT_TEXT_NAME; - fmt->features = FMT_SEGMENTS; + fmt->alias = FMT_TEXT_ALIAS; + fmt->features = FMT_SEGMENTS | FMT_MDAS; - if (!(dir_list = dbg_malloc(sizeof(struct list)))) { + if (!(mda_lists = dbg_malloc(sizeof(struct mda_lists)))) { log_error("Failed to allocate dir_list"); return NULL; } - list_init(dir_list); - fmt->private = (void *) dir_list; + list_init(&mda_lists->dirs); + list_init(&mda_lists->raws); + mda_lists->file_ops = &_metadata_text_file_ops; + mda_lists->raw_ops = &_metadata_text_raw_ops; + fmt->private = (void *) mda_lists; - if (!(cn = find_config_node(cmd->cf->root, "metadata/dirs", '/'))) { - log_verbose("metadata/dirs not in config file: Defaulting " - "to /etc/lvm/metadata"); - _add_dir("/etc/lvm/metadata", dir_list); - return fmt; + if (!(fmt->labeller = text_labeller_create(fmt))) { + log_error("Couldn't create text label handler."); + return NULL; } - for (cv = cn->v; cv; cv = cv->next) { - if (cv->type != CFG_STRING) { - log_error("Invalid string in config file: " - "metadata/dirs"); - goto err; + if (!(label_register_handler(FMT_TEXT_NAME, fmt->labeller))) { + log_error("Couldn't register text label handler."); + return NULL; + } + + if ((cn = find_config_node(cmd->cf->root, "metadata/dirs", '/'))) { + for (cv = cn->v; cv; cv = cv->next) { + if (cv->type != CFG_STRING) { + log_error("Invalid string in config file: " + "metadata/dirs"); + goto err; + } + + if (!_add_dir(cv->v.str, &mda_lists->dirs)) { + log_error("Failed to add %s to internal device " + "cache", cv->v.str); + goto err; + } } + } - if (!_add_dir(cv->v.str, dir_list)) { - log_error("Failed to add %s to internal device cache", - cv->v.str); + if (!(cn = find_config_node(cmd->cf->root, "metadata/disk_areas", '/'))) + return fmt; + + for (cn = cn->child; cn; cn = cn->sib) { + if (!_get_config_disk_area(cmd, cn, &mda_lists->raws)) goto err; - } } return fmt; err: - _free_dirs(dir_list); + _free_dirs(&mda_lists->dirs); dbg_free(fmt); return NULL; diff --git a/lib/format_text/format-text.h b/lib/format_text/format-text.h index d5c77c71d..f14c0adfb 100644 --- a/lib/format_text/format-text.h +++ b/lib/format_text/format-text.h @@ -9,7 +9,10 @@ #include "lvm-types.h" #include "metadata.h" -#include "uuid-map.h" +#include "pool.h" + +#define FMT_TEXT_NAME "lvm2" +#define FMT_TEXT_ALIAS "text" /* * Archives a vg config. 'retain_days' is the minimum number of @@ -19,21 +22,33 @@ */ int archive_vg(struct volume_group *vg, const char *dir, - const char *desc, - uint32_t retain_days, - uint32_t min_archive); + const char *desc, uint32_t retain_days, uint32_t min_archive); /* * Displays a list of vg backups in a particular archive directory. */ -int archive_list(struct cmd_context *cmd, struct uuid_map *um, - const char *dir, const char *vg); +int archive_list(struct cmd_context *cmd, const char *dir, const char *vg); /* * The text format can read and write a volume_group to a file. */ struct format_type *create_text_format(struct cmd_context *cmd); -void *create_text_context(struct format_type *fmt, const char *path, +void *create_text_context(struct cmd_context *cmd, const char *path, const char *desc); +struct labeller *text_labeller_create(struct format_type *fmt); + +int pvhdr_read(struct device *dev, char *buf); + +int add_da(struct format_type *fmt, struct pool *mem, struct list *das, + uint64_t start, uint64_t size); +void del_das(struct list *das); + +int add_mda(struct format_type *fmt, struct pool *mem, struct list *mdas, + struct device *dev, uint64_t start, uint64_t size); +void del_mdas(struct list *mdas); + +int vgname_from_mda(struct format_type *fmt, struct device_area *dev_area, + char *buf, uint32_t size); + #endif diff --git a/lib/format_text/import-export.h b/lib/format_text/import-export.h index f86249a95..f630285c9 100644 --- a/lib/format_text/import-export.h +++ b/lib/format_text/import-export.h @@ -10,7 +10,7 @@ #include "config.h" #include "lvm-types.h" #include "metadata.h" -#include "uuid-map.h" +#include "pool.h" #include <stdio.h> @@ -33,14 +33,32 @@ enum { LV_FLAGS }; +struct text_vg_version_ops { + int (*check_version) (struct config_tree * cf); + struct volume_group *(*read_vg) (struct format_instance * fid, + struct config_tree * cf); + void (*read_desc) (struct pool * mem, struct config_tree * cf, + time_t *when, char **desc); +}; + +struct text_vg_version_ops *text_vg_vsn1_init(void); + int print_flags(uint32_t status, int type, char *buffer, size_t size); int read_flags(uint32_t *status, int type, struct config_value *cv); - -int text_vg_export(FILE *fp, struct volume_group *vg, const char *desc); -struct volume_group *text_vg_import(struct format_instance *fid, - const char *file, - struct uuid_map *um, - time_t *when, char **desc); +int text_vg_export_file(struct volume_group *vg, const char *desc, FILE *fp); +int text_vg_export_raw(struct volume_group *vg, const char *desc, char *buf, + uint32_t size); +struct volume_group *text_vg_import_file(struct format_instance *fid, + const char *file, + time_t *when, char **desc); +struct volume_group *text_vg_import_fd(struct format_instance *fid, + const char *file, + int fd, + off_t offset, uint32_t size, + off_t offset2, uint32_t size2, + checksum_fn_t checksum_fn, + uint32_t checksum, + time_t *when, char **desc); #endif diff --git a/lib/format_text/import.c b/lib/format_text/import.c index 4028d71e7..8d8acd1e7 100644 --- a/lib/format_text/import.c +++ b/lib/format_text/import.c @@ -4,743 +4,80 @@ * This file is released under the LGPL. */ +#include "lib.h" #include "metadata.h" #include "import-export.h" #include "pool.h" -#include "log.h" -#include "uuid.h" +#include "display.h" #include "hash.h" #include "toolcontext.h" -#include "display.h" - -typedef int (*section_fn) (struct format_instance * fid, struct pool * mem, - struct volume_group * vg, struct config_node * pvn, - struct config_node * vgn, - struct hash_table * pv_hash, struct uuid_map * um); - -#define _read_int32(root, path, result) \ - get_config_uint32(root, path, '/', result) - -#define _read_uint32(root, path, result) \ - get_config_uint32(root, path, '/', result) - -#define _read_int64(root, path, result) \ - get_config_uint64(root, path, '/', result) - -/* - * Logs an attempt to read an invalid format file. - */ -static void _invalid_format(const char *str) -{ - log_error("Can't process text format file (%s)", str); -} - -/* - * Checks that the config file contains vg metadata, and that it - * we recognise the version number, - */ -static int _check_version(struct config_file *cf) -{ - struct config_node *cn; - struct config_value *cv; - - /* - * Check the contents field. - */ - if (!(cn = find_config_node(cf->root, CONTENTS_FIELD, '/'))) { - _invalid_format("missing contents field"); - return 0; - } - - cv = cn->v; - if (!cv || cv->type != CFG_STRING || strcmp(cv->v.str, CONTENTS_VALUE)) - { - _invalid_format("unrecognised contents field"); - return 0; - } - - /* - * Check the version number. - */ - if (!(cn = find_config_node(cf->root, FORMAT_VERSION_FIELD, '/'))) { - _invalid_format("missing version number"); - return 0; - } - - cv = cn->v; - if (!cv || cv->type != CFG_INT || cv->v.i != FORMAT_VERSION_VALUE) { - _invalid_format("unrecognised version number"); - return 0; - } - - return 1; -} - -static int _read_id(struct id *id, struct config_node *cn, const char *path) -{ - struct config_value *cv; - - if (!(cn = find_config_node(cn, path, '/'))) { - log_error("Couldn't find uuid."); - return 0; - } - - cv = cn->v; - if (!cv || !cv->v.str) { - log_error("uuid must be a string."); - return 0; - } - - if (!id_read_format(id, cv->v.str)) { - log_error("Invalid uuid."); - return 0; - } - - return 1; -} - -static int _read_pv(struct format_instance *fid, struct pool *mem, - struct volume_group *vg, struct config_node *pvn, - struct config_node *vgn, - struct hash_table *pv_hash, struct uuid_map *um) -{ - struct physical_volume *pv; - struct pv_list *pvl; - struct config_node *cn; - - if (!(pvl = pool_zalloc(mem, sizeof(*pvl))) || - !(pvl->pv = pool_zalloc(mem, sizeof(*pvl->pv)))) { - stack; - return 0; - } - - pv = pvl->pv; - - /* - * Add the pv to the pv hash for quick lookup when we read - * the lv segments. - */ - if (!hash_insert(pv_hash, pvn->key, pv)) { - stack; - return 0; - } - - if (!(pvn = pvn->child)) { - log_error("Empty pv section."); - return 0; - } - - if (!_read_id(&pv->id, pvn, "id")) { - log_error("Couldn't read uuid for volume group."); - return 0; - } - - /* - * Use the uuid map to convert the uuid into a device. - */ - if (!(pv->dev = uuid_map_lookup(um, &pv->id))) { - char buffer[64]; - - if (!id_write_format(&pv->id, buffer, sizeof(buffer))) - log_error("Couldn't find device."); - else - log_error("Couldn't find device with uuid '%s'.", buffer); - - if (partial_mode()) - vg->status |= PARTIAL_VG; - else - return 0; - } - - if (!(pv->vg_name = pool_strdup(mem, vg->name))) { - stack; - return 0; - } - - if (!(cn = find_config_node(pvn, "status", '/'))) { - log_error("Couldn't find status flags for physical volume."); - return 0; - } - - if (!(read_flags(&pv->status, PV_FLAGS, cn->v))) { - log_error("Couldn't read status flags for physical volume."); - return 0; - } - - if (!_read_int64(pvn, "pe_start", &pv->pe_start)) { - log_error("Couldn't read extent size for volume group."); - return 0; - } - - if (!_read_int32(pvn, "pe_count", &pv->pe_count)) { - log_error("Couldn't find extent count (pe_count) for " - "physical volume."); - return 0; - } - - /* adjust the volume group. */ - vg->extent_count += pv->pe_count; - vg->free_count += pv->pe_count; - - pv->pe_size = vg->extent_size; - pv->size = pv->pe_size * (uint64_t) pv->pe_count; - pv->pe_alloc_count = 0; - pv->fid = fid; - - vg->pv_count++; - list_add(&vg->pvs, &pvl->list); - - return 1; -} - -static void _insert_segment(struct logical_volume *lv, - struct stripe_segment *seg) -{ - struct list *segh; - struct stripe_segment *comp; - - list_iterate(segh, &lv->segments) { - comp = list_item(segh, struct stripe_segment); - - if (comp->le > seg->le) { - list_add(&comp->list, &seg->list); - return; - } - } - - lv->le_count += seg->len; - list_add(&lv->segments, &seg->list); -} - -static int _read_segment(struct pool *mem, struct volume_group *vg, - struct logical_volume *lv, struct config_node *sn, - struct hash_table *pv_hash) -{ - int s; - uint32_t stripes; - struct stripe_segment *seg; - struct config_node *cn; - struct config_value *cv; - const char *seg_name = sn->key; - - if (!(sn = sn->child)) { - log_error("Empty segment section."); - return 0; - } - - if (!_read_int32(sn, "stripes", &stripes)) { - log_error("Couldn't read 'stripes' for segment '%s'.", sn->key); - return 0; - } - - if (!(seg = pool_zalloc(mem, sizeof(*seg) + - (sizeof(seg->area[0]) * stripes)))) { - stack; - return 0; - } - seg->stripes = stripes; - seg->lv = lv; - - if (!_read_int32(sn, "start_extent", &seg->le)) { - log_error("Couldn't read 'start_extent' for segment '%s'.", - sn->key); - return 0; - } - - if (!_read_int32(sn, "extent_count", &seg->len)) { - log_error("Couldn't read 'extent_count' for segment '%s'.", - sn->key); - return 0; - } - - if (seg->stripes == 0) { - log_error("Zero stripes is *not* allowed for segment '%s'.", - sn->key); - return 0; - } - - if ((seg->stripes != 1) && - !_read_int32(sn, "stripe_size", &seg->stripe_size)) { - log_error("Couldn't read 'stripe_size' for segment '%s'.", - sn->key); - return 0; - } - - if (!(cn = find_config_node(sn, "areas", '/'))) { - log_error("Couldn't find 'areas' array for segment '%s'.", - sn->key); - return 0; - } - - /* - * Read the stripes from the 'areas' array. - * FIXME: we could move this to a separate function. - */ - for (cv = cn->v, s = 0; cv && s < seg->stripes; s++, cv = cv->next) { - - /* first we read the pv */ - const char *bad = "Badly formed areas array for segment '%s'."; - struct physical_volume *pv; - uint32_t allocated; - - if (cv->type != CFG_STRING) { - log_error(bad, sn->key); - return 0; - } - - if (!(pv = hash_lookup(pv_hash, cv->v.str))) { - log_error("Couldn't find physical volume '%s' for " - "segment '%s'.", - cv->v.str ? cv->v.str : "NULL", seg_name); - return 0; - } - - seg->area[s].pv = pv; - - if (!(cv = cv->next)) { - log_error(bad, sn->key); - return 0; - } - - if (cv->type != CFG_INT) { - log_error(bad, sn->key); - return 0; - } - - seg->area[s].pe = cv->v.i; - - /* - * Adjust the extent counts in the pv and vg. - */ - allocated = seg->len / seg->stripes; - pv->pe_alloc_count += allocated; - vg->free_count -= allocated; - } - - /* - * Check we read the correct number of stripes. - */ - if (cv || (s < seg->stripes)) { - log_error("Incorrect number of stripes in 'area' array " - "for segment '%s'.", seg_name); - return 0; - } - - /* - * Insert into correct part of segment list. - */ - _insert_segment(lv, seg); - return 1; -} - -static int _read_segments(struct pool *mem, struct volume_group *vg, - struct logical_volume *lv, struct config_node *lvn, - struct hash_table *pv_hash) +#include "cache.h" + +/* FIXME Use tidier inclusion method */ +static struct text_vg_version_ops *(_text_vsn_list[2]); + +struct volume_group *text_vg_import_fd(struct format_instance *fid, + const char *file, + int fd, + off_t offset, uint32_t size, + off_t offset2, uint32_t size2, + checksum_fn_t checksum_fn, + uint32_t checksum, + time_t *when, char **desc) { - struct config_node *sn; - int count = 0, seg_count; - - for (sn = lvn; sn; sn = sn->sib) { - - /* - * All sub-sections are assumed to be segments. - */ - if (!sn->v) { - if (!_read_segment(mem, vg, lv, sn, pv_hash)) { - stack; - return 0; - } - - count++; - } - } - - if (!_read_int32(lvn, "segment_count", &seg_count)) { - log_error("Couldn't read segment count for logical volume."); - return 0; - } - - if (seg_count != count) { - log_error("segment_count and actual number of segments " - "disagree."); - return 0; - } - - /* - * Check there are no gaps or overlaps in the lv. - */ - if (!lv_check_segments(lv)) { - stack; - return 0; - } - - /* - * Merge segments in case someones been editing things by hand. - */ - if (!lv_merge_segments(lv)) { - stack; - return 0; - } + struct volume_group *vg = NULL; + struct config_tree *cf; + struct text_vg_version_ops **vsn; - return 1; -} + static int _initialised = 0; -static int _read_lv(struct format_instance *fid, struct pool *mem, - struct volume_group *vg, struct config_node *lvn, - struct config_node *vgn, struct hash_table *pv_hash, - struct uuid_map *um) -{ - struct logical_volume *lv; - struct lv_list *lvl; - struct config_node *cn; - - if (!(lvl = pool_zalloc(mem, sizeof(*lvl))) || - !(lvl->lv = pool_zalloc(mem, sizeof(*lvl->lv)))) { - stack; - return 0; + if (!_initialised) { + _text_vsn_list[0] = text_vg_vsn1_init(); + _text_vsn_list[1] = NULL; + _initialised = 1; } - lv = lvl->lv; + *desc = NULL; + *when = 0; - if (!(lv->name = pool_strdup(mem, lvn->key))) { + if (!(cf = create_config_tree())) { stack; - return 0; - } - - if (!(lvn = lvn->child)) { - log_error("Empty logical volume section."); - return 0; - } - - lv->vg = vg; - - /* FIXME: read full lvid */ - if (!_read_id(&lv->lvid.id[1], lvn, "id")) { - log_error("Couldn't read uuid for logical volume %s.", lv->name); - return 0; - } - - memcpy(&lv->lvid.id[0], &lv->vg->id, sizeof(lv->lvid.id[0])); - - if (!(cn = find_config_node(lvn, "status", '/'))) { - log_error("Couldn't find status flags for logical volume."); - return 0; - } - - if (!(read_flags(&lv->status, LV_FLAGS, cn->v))) { - log_error("Couldn't read status flags for logical volume."); - return 0; + goto out; } - lv->minor = -1; - if ((lv->status & FIXED_MINOR) && - !_read_int32(lvn, "minor", &lv->minor)) { - log_error("Couldn't read 'minor' value for logical volume."); - return 0; + if ((fd == -1 && !read_config_file(cf, file)) || + (fd != -1 && !read_config_fd(cf, fd, file, offset, size, + offset2, size2, checksum_fn, + checksum))) { + log_error("Couldn't read volume group metadata."); + goto out; } - /* - * allocation_policy is optional since it is meaning less - * for things like mirrors and snapshots. Where it isn't - * specified we default to the next free policy. + /* + * Find a set of version functions that can read this file */ - lv->alloc = ALLOC_NEXT_FREE; - if ((cn = find_config_node(lvn, "allocation_policy", '/'))) { - struct config_value *cv = cn->v; - if (!cv || !cv->v.str) { - log_err("allocation_policy must be a string."); - return 0; - } - - lv->alloc = get_alloc_from_string(cv->v.str); - } - - if (!_read_int32(lvn, "read_ahead", &lv->read_ahead)) { - log_error("Couldn't read 'read_ahead' value for " - "logical volume."); - return 0; - } - - list_init(&lv->segments); - if (!_read_segments(mem, vg, lv, lvn, pv_hash)) { - stack; - return 0; - } - lv->size = (uint64_t) lv->le_count * (uint64_t) vg->extent_size; - - vg->lv_count++; - list_add(&vg->lvs, &lvl->list); - - return 1; -} - -static int _read_snapshot(struct format_instance *fid, struct pool *mem, - struct volume_group *vg, struct config_node *sn, - struct config_node *vgn, struct hash_table *pv_hash, - struct uuid_map *um) -{ - uint32_t chunk_size; - const char *org_name, *cow_name; - struct logical_volume *org, *cow; + for (vsn = &_text_vsn_list[0]; *vsn; vsn++) { + if (!(*vsn)->check_version(cf)) + continue; - if (!(sn = sn->child)) { - log_error("Empty snapshot section."); - return 0; - } - - if (!_read_uint32(sn, "chunk_size", &chunk_size)) { - log_error("Couldn't read chunk size for snapshot."); - return 0; - } - - if (!(cow_name = find_config_str(sn, "cow_store", '/', NULL))) { - log_error("Snapshot cow storage not specified."); - return 0; - } - - if (!(org_name = find_config_str(sn, "origin", '/', NULL))) { - log_error("Snapshot origin not specified."); - return 0; - } - - if (!(cow = find_lv(vg, cow_name))) { - log_error("Unknown logical volume specified for " - "snapshot cow store."); - return 0; - } - - if (!(org = find_lv(vg, org_name))) { - log_error("Unknown logical volume specified for " - "snapshot origin."); - return 0; - } - - if (!vg_add_snapshot(org, cow, 1, chunk_size)) { - stack; - return 0; - } - - return 1; -} - -static int _read_sections(struct format_instance *fid, - const char *section, section_fn fn, - struct pool *mem, - struct volume_group *vg, struct config_node *vgn, - struct hash_table *pv_hash, - struct uuid_map *um, int optional) -{ - struct config_node *n; - - if (!(n = find_config_node(vgn, section, '/'))) { - if (!optional) { - log_error("Couldn't find section '%s'.", section); - return 0; - } - - return 1; - } - - for (n = n->child; n; n = n->sib) { - if (!fn(fid, mem, vg, n, vgn, pv_hash, um)) { + if (!(vg = (*vsn)->read_vg(fid, cf))) { stack; - return 0; + goto out; } - } - - return 1; -} -static struct volume_group *_read_vg(struct format_instance *fid, - struct config_file *cf, - struct uuid_map *um) -{ - struct config_node *vgn, *cn; - struct volume_group *vg; - struct hash_table *pv_hash = NULL; - struct pool *mem = fid->fmt->cmd->mem; - - /* skip any top-level values */ - for (vgn = cf->root; (vgn && vgn->v); vgn = vgn->sib) ; - - if (!vgn) { - log_error("Couldn't find volume group in file."); - return NULL; - } - - if (!(vg = pool_zalloc(mem, sizeof(*vg)))) { - stack; - return NULL; + (*vsn)->read_desc(fid->fmt->cmd->mem, cf, when, desc); + break; } - vg->cmd = fid->fmt->cmd; - /* FIXME Determine format type from file contents */ - /* eg Set to instance of fmt1 here if reading a format1 backup? */ - vg->fid = fid; - - if (!(vg->name = pool_strdup(mem, vgn->key))) { - stack; - goto bad; - } - - if (!(vg->system_id = pool_zalloc(mem, NAME_LEN))) { - stack; - goto bad; - } - - vgn = vgn->child; - - if ((cn = find_config_node(vgn, "system_id", '/')) && cn->v) { - if (!cn->v->v.str) { - log_error("system_id must be a string"); - goto bad; - } - strncpy(vg->system_id, cn->v->v.str, NAME_LEN); - } - - if (!_read_id(&vg->id, vgn, "id")) { - log_error("Couldn't read uuid for volume group %s.", vg->name); - goto bad; - } - - if (!_read_int32(vgn, "seqno", &vg->seqno)) { - log_error("Couldn't read 'seqno' for volume group %s.", vg->name); - goto bad; - } - - if (!(cn = find_config_node(vgn, "status", '/'))) { - log_error("Couldn't find status flags for volume group %s.", - vg->name); - goto bad; - } - - if (!(read_flags(&vg->status, VG_FLAGS, cn->v))) { - log_error("Couldn't read status flags for volume group %s.", - vg->name); - goto bad; - } - - if (!_read_int32(vgn, "extent_size", &vg->extent_size)) { - log_error("Couldn't read extent size for volume group %s.", - vg->name); - goto bad; - } - - /* - * 'extent_count' and 'free_count' get filled in - * implicitly when reading in the pv's and lv's. - */ - - if (!_read_int32(vgn, "max_lv", &vg->max_lv)) { - log_error("Couldn't read 'max_lv' for volume group %s.", - vg->name); - goto bad; - } - - if (!_read_int32(vgn, "max_pv", &vg->max_pv)) { - log_error("Couldn't read 'max_pv' for volume group %s.", - vg->name); - goto bad; - } - - /* - * The pv hash memoises the pv section names -> pv - * structures. - */ - if (!(pv_hash = hash_create(32))) { - log_error("Couldn't create hash table."); - goto bad; - } - - list_init(&vg->pvs); - if (!_read_sections(fid, "physical_volumes", _read_pv, mem, vg, - vgn, pv_hash, um, 0)) { - log_error("Couldn't find all physical volumes for volume " - "group %s.", vg->name); - goto bad; - } - - list_init(&vg->lvs); - if (!_read_sections(fid, "logical_volumes", _read_lv, mem, vg, - vgn, pv_hash, um, 1)) { - log_error("Couldn't read all logical volumes for volume " - "group %s.", vg->name); - goto bad; - } - - list_init(&vg->snapshots); - if (!_read_sections(fid, "snapshots", _read_snapshot, mem, vg, - vgn, pv_hash, um, 1)) { - log_error("Couldn't read all snapshots for volume group %s.", - vg->name); - goto bad; - } - - hash_destroy(pv_hash); - - if (vg->status & PARTIAL_VG) { - vg->status &= ~LVM_WRITE; - vg->status |= LVM_READ; - } - - /* - * Finished. - */ + out: + destroy_config_tree(cf); return vg; - - bad: - if (pv_hash) - hash_destroy(pv_hash); - - pool_free(mem, vg); - return NULL; } -static void _read_desc(struct pool *mem, - struct config_file *cf, time_t * when, char **desc) +struct volume_group *text_vg_import_file(struct format_instance *fid, + const char *file, + time_t *when, char **desc) { - const char *d; - unsigned int u = 0u; - - d = find_config_str(cf->root, "description", '/', ""); - *desc = pool_strdup(mem, d); - - get_config_uint32(cf->root, "creation_time", '/', &u); - *when = u; -} - -struct volume_group *text_vg_import(struct format_instance *fid, - const char *file, - struct uuid_map *um, - time_t * when, char **desc) -{ - struct volume_group *vg = NULL; - struct config_file *cf; - - *desc = NULL; - *when = 0; - - if (!(cf = create_config_file())) { - stack; - goto out; - } - - if (!read_config(cf, file)) { - log_error("Couldn't read volume group file."); - goto out; - } - - if (!_check_version(cf)) - goto out; - - if (!(vg = _read_vg(fid, cf, um))) { - stack; - goto out; - } - - _read_desc(fid->fmt->cmd->mem, cf, when, desc); - - out: - destroy_config_file(cf); - return vg; + return text_vg_import_fd(fid, file, -1, 0, 0, 0, 0, NULL, 0, + when, desc); } diff --git a/lib/format_text/import_vsn1.c b/lib/format_text/import_vsn1.c new file mode 100644 index 000000000..cb944f7cc --- /dev/null +++ b/lib/format_text/import_vsn1.c @@ -0,0 +1,735 @@ +/* + * Copyright (C) 2001 Sistina Software (UK) Limited. + * + * This file is released under the LGPL. + */ + +#include "lib.h" +#include "metadata.h" +#include "import-export.h" +#include "pool.h" +#include "display.h" +#include "hash.h" +#include "toolcontext.h" +#include "cache.h" + +typedef int (*section_fn) (struct format_instance * fid, struct pool * mem, + struct volume_group * vg, struct config_node * pvn, + struct config_node * vgn, + struct hash_table * pv_hash); + +#define _read_int32(root, path, result) \ + get_config_uint32(root, path, '/', result) + +#define _read_uint32(root, path, result) \ + get_config_uint32(root, path, '/', result) + +#define _read_int64(root, path, result) \ + get_config_uint64(root, path, '/', result) + +/* + * Logs an attempt to read an invalid format file. + */ +static void _invalid_format(const char *str) +{ + log_error("Can't process text format file - %s.", str); +} + +/* + * Checks that the config file contains vg metadata, and that it + * we recognise the version number, + */ +static int _check_version(struct config_tree *cf) +{ + struct config_node *cn; + struct config_value *cv; + + /* + * Check the contents field. + */ + if (!(cn = find_config_node(cf->root, CONTENTS_FIELD, '/'))) { + _invalid_format("missing contents field"); + return 0; + } + + cv = cn->v; + if (!cv || cv->type != CFG_STRING || strcmp(cv->v.str, CONTENTS_VALUE)) { + _invalid_format("unrecognised contents field"); + return 0; + } + + /* + * Check the version number. + */ + if (!(cn = find_config_node(cf->root, FORMAT_VERSION_FIELD, '/'))) { + _invalid_format("missing version number"); + return 0; + } + + cv = cn->v; + if (!cv || cv->type != CFG_INT || cv->v.i != FORMAT_VERSION_VALUE) { + _invalid_format("unrecognised version number"); + return 0; + } + + return 1; +} + +static int _read_id(struct id *id, struct config_node *cn, const char *path) +{ + struct config_value *cv; + + if (!(cn = find_config_node(cn, path, '/'))) { + log_error("Couldn't find uuid."); + return 0; + } + + cv = cn->v; + if (!cv || !cv->v.str) { + log_error("uuid must be a string."); + return 0; + } + + if (!id_read_format(id, cv->v.str)) { + log_error("Invalid uuid."); + return 0; + } + + return 1; +} + +static int _read_pv(struct format_instance *fid, struct pool *mem, + struct volume_group *vg, struct config_node *pvn, + struct config_node *vgn, struct hash_table *pv_hash) +{ + struct physical_volume *pv; + struct pv_list *pvl; + struct config_node *cn; + + if (!(pvl = pool_zalloc(mem, sizeof(*pvl))) || + !(pvl->pv = pool_zalloc(mem, sizeof(*pvl->pv)))) { + stack; + return 0; + } + + pv = pvl->pv; + + /* + * Add the pv to the pv hash for quick lookup when we read + * the lv segments. + */ + if (!hash_insert(pv_hash, pvn->key, pv)) { + stack; + return 0; + } + + if (!(pvn = pvn->child)) { + log_error("Empty pv section."); + return 0; + } + + if (!_read_id(&pv->id, pvn, "id")) { + log_error("Couldn't read uuid for volume group."); + return 0; + } + + /* + * Convert the uuid into a device. + */ + if (!(pv->dev = device_from_pvid(fid->fmt->cmd, &pv->id))) { + char buffer[64]; + + if (!id_write_format(&pv->id, buffer, sizeof(buffer))) + log_error("Couldn't find device."); + else + log_error("Couldn't find device with uuid '%s'.", + buffer); + + if (partial_mode()) + vg->status |= PARTIAL_VG; + else + return 0; + } + + if (!(pv->vg_name = pool_strdup(mem, vg->name))) { + stack; + return 0; + } + + if (!(cn = find_config_node(pvn, "status", '/'))) { + log_error("Couldn't find status flags for physical volume."); + return 0; + } + + if (!(read_flags(&pv->status, PV_FLAGS, cn->v))) { + log_error("Couldn't read status flags for physical volume."); + return 0; + } + + if (!_read_int64(pvn, "pe_start", &pv->pe_start)) { + log_error("Couldn't read extent size for volume group."); + return 0; + } + + if (!_read_int32(pvn, "pe_count", &pv->pe_count)) { + log_error("Couldn't find extent count (pe_count) for " + "physical volume."); + return 0; + } + + /* adjust the volume group. */ + vg->extent_count += pv->pe_count; + vg->free_count += pv->pe_count; + + pv->pe_size = vg->extent_size; + pv->size = vg->extent_size * (uint64_t) pv->pe_count; + pv->pe_alloc_count = 0; + pv->fmt = fid->fmt; + + vg->pv_count++; + list_add(&vg->pvs, &pvl->list); + + return 1; +} + +static void _insert_segment(struct logical_volume *lv, struct lv_segment *seg) +{ + struct list *segh; + struct lv_segment *comp; + + list_iterate(segh, &lv->segments) { + comp = list_item(segh, struct lv_segment); + + if (comp->le > seg->le) { + list_add(&comp->list, &seg->list); + return; + } + } + + lv->le_count += seg->len; + list_add(&lv->segments, &seg->list); +} + +static int _read_segment(struct pool *mem, struct volume_group *vg, + struct logical_volume *lv, struct config_node *sn, + struct hash_table *pv_hash) +{ + int s; + uint32_t stripes = 0; + struct lv_segment *seg; + struct config_node *cn; + struct config_value *cv; + const char *seg_name = sn->key; + uint32_t start_extent, extent_count; + uint32_t chunk_size; + const char *org_name, *cow_name; + struct logical_volume *org, *cow; + segment_type_t segtype; + + if (!(sn = sn->child)) { + log_error("Empty segment section."); + return 0; + } + + if (!_read_int32(sn, "start_extent", &start_extent)) { + log_error("Couldn't read 'start_extent' for segment '%s'.", + sn->key); + return 0; + } + + if (!_read_int32(sn, "extent_count", &extent_count)) { + log_error("Couldn't read 'extent_count' for segment '%s'.", + sn->key); + return 0; + } + + segtype = SEG_STRIPED; /* Default */ + if ((cn = find_config_node(sn, "type", '/'))) { + cv = cn->v; + if (!cv || !cv->v.str) { + log_error("Segment type must be a string."); + return 0; + } + segtype = get_segtype_from_string(cv->v.str); + } + + if (segtype == SEG_STRIPED) { + if (!_read_int32(sn, "stripe_count", &stripes)) { + log_error("Couldn't read 'stripe_count' for " + "segment '%s'.", sn->key); + return 0; + } + } + + if (!(seg = pool_zalloc(mem, sizeof(*seg) + + (sizeof(seg->area[0]) * stripes)))) { + stack; + return 0; + } + + seg->lv = lv; + seg->le = start_extent; + seg->len = extent_count; + + switch (segtype) { + case SEG_MIRROR: + case SEG_SNAPSHOT: + lv->status |= SNAPSHOT; + + if (!_read_uint32(sn, "chunk_size", &chunk_size)) { + log_error("Couldn't read chunk size for snapshot."); + return 0; + } + + log_suppress(1); + + if (!(cow_name = find_config_str(sn, "cow_store", '/', NULL))) { + log_suppress(0); + log_error("Snapshot cow storage not specified."); + return 0; + } + + if (!(org_name = find_config_str(sn, "origin", '/', NULL))) { + log_suppress(0); + log_error("Snapshot origin not specified."); + return 0; + } + + log_suppress(0); + + if (!(cow = find_lv(vg, cow_name))) { + log_error("Unknown logical volume specified for " + "snapshot cow store."); + return 0; + } + + if (!(org = find_lv(vg, org_name))) { + log_error("Unknown logical volume specified for " + "snapshot origin."); + return 0; + } + + if (!vg_add_snapshot(org, cow, 1, &lv->lvid.id[1], chunk_size)) { + stack; + return 0; + } + break; + + case SEG_STRIPED: + seg->stripes = stripes; + + if (!seg->stripes) { + log_error("Zero stripes *not* allowed for segment '%s'", + sn->key); + return 0; + } + + if ((seg->stripes != 1) && + !_read_int32(sn, "stripe_size", &seg->stripe_size)) { + log_error("Couldn't read stripe_size for segment '%s'.", + sn->key); + return 0; + } + + if (!(cn = find_config_node(sn, "stripes", '/'))) { + log_error("Couldn't find stripes array for segment " + "'%s'.", sn->key); + return 0; + } + + for (cv = cn->v, s = 0; cv && s < seg->stripes; + s++, cv = cv->next) { + + /* first we read the pv */ + const char *bad = "Badly formed areas array for " + "segment '%s'."; + struct physical_volume *pv; + uint32_t allocated; + + if (cv->type != CFG_STRING) { + log_error(bad, sn->key); + return 0; + } + + if (!(pv = hash_lookup(pv_hash, cv->v.str))) { + log_error("Couldn't find physical volume '%s' " + "for segment '%s'.", + cv->v.str ? cv->v.str : "NULL", + seg_name); + return 0; + } + + seg->area[s].pv = pv; + + if (!(cv = cv->next)) { + log_error(bad, sn->key); + return 0; + } + + if (cv->type != CFG_INT) { + log_error(bad, sn->key); + return 0; + } + + seg->area[s].pe = cv->v.i; + + /* + * Adjust the extent counts in the pv and vg. + */ + allocated = seg->len / seg->stripes; + pv->pe_alloc_count += allocated; + vg->free_count -= allocated; + } + + /* + * Check we read the correct number of stripes. + */ + if (cv || (s < seg->stripes)) { + log_error("Incorrect number of stripes in 'area' array " + "for segment '%s'.", seg_name); + return 0; + } + + } + + /* + * Insert into correct part of segment list. + */ + _insert_segment(lv, seg); + return 1; +} + +static int _read_segments(struct pool *mem, struct volume_group *vg, + struct logical_volume *lv, struct config_node *lvn, + struct hash_table *pv_hash) +{ + struct config_node *sn; + int count = 0, seg_count; + + for (sn = lvn; sn; sn = sn->sib) { + + /* + * All sub-sections are assumed to be segments. + */ + if (!sn->v) { + if (!_read_segment(mem, vg, lv, sn, pv_hash)) { + stack; + return 0; + } + + count++; + } + /* FIXME Remove this restriction */ + if ((lv->status & SNAPSHOT) && count > 1) { + log_error("Only one segment permitted for snapshot"); + return 0; + } + } + + if (!_read_int32(lvn, "segment_count", &seg_count)) { + log_error("Couldn't read segment count for logical volume."); + return 0; + } + + if (seg_count != count) { + log_error("segment_count and actual number of segments " + "disagree."); + return 0; + } + + /* + * Check there are no gaps or overlaps in the lv. + */ + if (!lv_check_segments(lv)) { + stack; + return 0; + } + + /* + * Merge segments in case someones been editing things by hand. + */ + if (!lv_merge_segments(lv)) { + stack; + return 0; + } + + return 1; +} + +static int _read_lv(struct format_instance *fid, struct pool *mem, + struct volume_group *vg, struct config_node *lvn, + struct config_node *vgn, struct hash_table *pv_hash) +{ + struct logical_volume *lv; + struct lv_list *lvl; + struct config_node *cn; + + if (!(lvl = pool_zalloc(mem, sizeof(*lvl))) || + !(lvl->lv = pool_zalloc(mem, sizeof(*lvl->lv)))) { + stack; + return 0; + } + + lv = lvl->lv; + + if (!(lv->name = pool_strdup(mem, lvn->key))) { + stack; + return 0; + } + + if (!(lvn = lvn->child)) { + log_error("Empty logical volume section."); + return 0; + } + + lv->vg = vg; + + /* FIXME: read full lvid */ + if (!_read_id(&lv->lvid.id[1], lvn, "id")) { + log_error("Couldn't read uuid for logical volume %s.", + lv->name); + return 0; + } + + memcpy(&lv->lvid.id[0], &lv->vg->id, sizeof(lv->lvid.id[0])); + + if (!(cn = find_config_node(lvn, "status", '/'))) { + log_error("Couldn't find status flags for logical volume."); + return 0; + } + + if (!(read_flags(&lv->status, LV_FLAGS, cn->v))) { + log_error("Couldn't read status flags for logical volume."); + return 0; + } + + list_init(&lv->segments); + if (!_read_segments(mem, vg, lv, lvn, pv_hash)) { + stack; + return 0; + } + + lv->alloc = ALLOC_DEFAULT; + if ((cn = find_config_node(lvn, "allocation_policy", '/'))) { + struct config_value *cv = cn->v; + if (!cv || !cv->v.str) { + log_error("allocation_policy must be a string."); + return 0; + } + + lv->alloc = get_alloc_from_string(cv->v.str); + } + + /* read_ahead defaults to 0 */ + if (!_read_int32(lvn, "read_ahead", &lv->read_ahead)) + lv->read_ahead = 0; + + lv->size = (uint64_t) lv->le_count * (uint64_t) vg->extent_size; + + /* Skip this for now for snapshots */ + if (!(lv->status & SNAPSHOT)) { + lv->minor = -1; + if ((lv->status & FIXED_MINOR) && + !_read_int32(lvn, "minor", &lv->minor)) { + log_error("Couldn't read minor number for logical " + "volume."); + return 0; + } + + vg->lv_count++; + list_add(&vg->lvs, &lvl->list); + } + + return 1; +} + +static int _read_sections(struct format_instance *fid, + const char *section, section_fn fn, + struct pool *mem, + struct volume_group *vg, struct config_node *vgn, + struct hash_table *pv_hash, int optional) +{ + struct config_node *n; + + if (!(n = find_config_node(vgn, section, '/'))) { + if (!optional) { + log_error("Couldn't find section '%s'.", section); + return 0; + } + + return 1; + } + + for (n = n->child; n; n = n->sib) { + if (!fn(fid, mem, vg, n, vgn, pv_hash)) { + stack; + return 0; + } + } + + return 1; +} + +static struct volume_group *_read_vg(struct format_instance *fid, + struct config_tree *cf) +{ + struct config_node *vgn, *cn; + struct volume_group *vg; + struct hash_table *pv_hash = NULL; + struct pool *mem = fid->fmt->cmd->mem; + + /* skip any top-level values */ + for (vgn = cf->root; (vgn && vgn->v); vgn = vgn->sib) ; + + if (!vgn) { + log_error("Couldn't find volume group in file."); + return NULL; + } + + if (!(vg = pool_zalloc(mem, sizeof(*vg)))) { + stack; + return NULL; + } + vg->cmd = fid->fmt->cmd; + + /* FIXME Determine format type from file contents */ + /* eg Set to instance of fmt1 here if reading a format1 backup? */ + vg->fid = fid; + + if (!(vg->name = pool_strdup(mem, vgn->key))) { + stack; + goto bad; + } + + if (!(vg->system_id = pool_zalloc(mem, NAME_LEN))) { + stack; + goto bad; + } + + vgn = vgn->child; + + if ((cn = find_config_node(vgn, "system_id", '/')) && cn->v) { + if (!cn->v->v.str) { + log_error("system_id must be a string"); + goto bad; + } + strncpy(vg->system_id, cn->v->v.str, NAME_LEN); + } + + if (!_read_id(&vg->id, vgn, "id")) { + log_error("Couldn't read uuid for volume group %s.", vg->name); + goto bad; + } + + if (!_read_int32(vgn, "seqno", &vg->seqno)) { + log_error("Couldn't read 'seqno' for volume group %s.", + vg->name); + goto bad; + } + + if (!(cn = find_config_node(vgn, "status", '/'))) { + log_error("Couldn't find status flags for volume group %s.", + vg->name); + goto bad; + } + + if (!(read_flags(&vg->status, VG_FLAGS, cn->v))) { + log_error("Couldn't read status flags for volume group %s.", + vg->name); + goto bad; + } + + if (!_read_int32(vgn, "extent_size", &vg->extent_size)) { + log_error("Couldn't read extent size for volume group %s.", + vg->name); + goto bad; + } + + /* + * 'extent_count' and 'free_count' get filled in + * implicitly when reading in the pv's and lv's. + */ + + if (!_read_int32(vgn, "max_lv", &vg->max_lv)) { + log_error("Couldn't read 'max_lv' for volume group %s.", + vg->name); + goto bad; + } + + if (!_read_int32(vgn, "max_pv", &vg->max_pv)) { + log_error("Couldn't read 'max_pv' for volume group %s.", + vg->name); + goto bad; + } + + /* + * The pv hash memoises the pv section names -> pv + * structures. + */ + if (!(pv_hash = hash_create(32))) { + log_error("Couldn't create hash table."); + goto bad; + } + + list_init(&vg->pvs); + if (!_read_sections(fid, "physical_volumes", _read_pv, mem, vg, + vgn, pv_hash, 0)) { + log_error("Couldn't find all physical volumes for volume " + "group %s.", vg->name); + goto bad; + } + + list_init(&vg->lvs); + list_init(&vg->snapshots); + if (!_read_sections(fid, "logical_volumes", _read_lv, mem, vg, + vgn, pv_hash, 1)) { + log_error("Couldn't read all logical volumes for volume " + "group %s.", vg->name); + goto bad; + } + + hash_destroy(pv_hash); + + if (vg->status & PARTIAL_VG) { + vg->status &= ~LVM_WRITE; + vg->status |= LVM_READ; + } + + /* + * Finished. + */ + return vg; + + bad: + if (pv_hash) + hash_destroy(pv_hash); + + pool_free(mem, vg); + return NULL; +} + +static void _read_desc(struct pool *mem, + struct config_tree *cf, time_t *when, char **desc) +{ + const char *d; + unsigned int u = 0u; + + log_suppress(1); + d = find_config_str(cf->root, "description", '/', ""); + log_suppress(0); + *desc = pool_strdup(mem, d); + + get_config_uint32(cf->root, "creation_time", '/', &u); + *when = u; +} + +static struct text_vg_version_ops _vsn1_ops = { + check_version:_check_version, + read_vg:_read_vg, + read_desc:_read_desc +}; + +struct text_vg_version_ops *text_vg_vsn1_init(void) +{ + return &_vsn1_ops; +}; diff --git a/lib/format_text/layout.h b/lib/format_text/layout.h new file mode 100644 index 000000000..92ba02bb0 --- /dev/null +++ b/lib/format_text/layout.h @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2001 Sistina Software (UK) Limited. + * + * This file is released under the LGPL. + */ + +#ifndef _LVM_TEXT_LAYOUT_H +#define _LVM_TEXT_LAYOUT_H + +#include "config.h" +#include "lvm-types.h" +#include "metadata.h" +#include "uuid.h" + +/* On disk */ +struct disk_locn { + uint64_t offset; /* Offset in bytes to start sector */ + uint64_t size; /* Bytes */ +} __attribute__ ((packed)); + +/* Data areas (holding PEs) */ +struct data_area_list { + struct list list; + struct disk_locn disk_locn; +}; + +/* Fields with the suffix _xl should be xlate'd wherever they appear */ +/* On disk */ +struct pv_header { + uint8_t pv_uuid[ID_LEN]; + uint64_t device_size_xl; /* Bytes */ + + /* NULL-terminated list of data areas followed by */ + /* NULL-terminated list of metadata area headers */ + struct disk_locn disk_areas_xl[0]; /* Two lists */ +} __attribute__ ((packed)); + +/* On disk */ +struct raw_locn { + uint64_t offset; /* Offset in bytes to start sector */ + uint64_t size; /* Bytes */ + uint32_t checksum; + uint32_t filler; +} __attribute__ ((packed)); + +/* On disk */ +/* Structure size limited to one sector */ +struct mda_header { + uint32_t checksum_xl; /* Checksum of rest of mda_header */ + uint8_t magic[16]; /* To aid scans for metadata */ + uint32_t version; + uint64_t start; /* Absolute start byte of mda_header */ + uint64_t size; /* Size of metadata area */ + + struct raw_locn raw_locns[0]; /* NULL-terminated list */ +} __attribute__ ((packed)); + +struct mda_lists { + struct list dirs; + struct list raws; + struct metadata_area_ops *file_ops; + struct metadata_area_ops *raw_ops; +}; + +struct mda_context { + struct device_area area; + struct raw_locn rlocn; /* Store inbetween write and commit */ +}; + +/* FIXME Convert this at runtime */ +#define FMTT_MAGIC "\040\114\126\115\062\040\170\133\065\101\045\162\060\116\052\076" +#define FMTT_VERSION 1 +#define MDA_HEADER_SIZE 512 +#define LVM2_LABEL "LVM2 001" + +#endif diff --git a/lib/format_text/sample.vg b/lib/format_text/sample.vg deleted file mode 100644 index f4c28e4ab..000000000 --- a/lib/format_text/sample.vg +++ /dev/null @@ -1,67 +0,0 @@ -# An example volume group - -# YYYY-MM-DD HH:MM:SS -output_date = "2001-12-11 11:35:12" - -sample_volume_group { - - id = "ksjdlfksjldskjlsk" - status = ["ACTIVE"] - - extent_size = 8192 # 4 Megabytes - - max_lv = 99 - max_pv = 255 - - physical_volumes { - - pv1 { - id = "lksjdflksdlsk" - device = "/dev/hda1" # Hint only - - status = ["ALLOCATABLE"] - pe_start = 8192 - pe_count = 2048 # 8 Gigabytes - } - - pv2 { - id = "lksjdflksdlsk" - device = "/dev/hda2" # Hint only - - status = ["ALLOCATABLE"] - pe_start = 8192 - pe_count = 1024 # 4 Gigabytes - } - } - - logical_volumes { - - music { - status = ["ACTIVE"] - read_ahead = 1024 - - segment_count = 2 - - segment1 { - start_extent = 0 - extent_count = 1024 # 4 Gigabytes - stripes = 1 - - areas = [ - "pv1", 0 - ] - } - - segment2 { - start_extent = 1024 - extent_count = 2048 # 8 Gigabytes - stripes = 2 - stripe_size = 32 # 16 Kilobytes - - areas = [ - "pv1", 1024, - "pv2", 0 - ] - } - } -}
\ No newline at end of file diff --git a/lib/format_text/text_label.c b/lib/format_text/text_label.c new file mode 100644 index 000000000..b39d61ef8 --- /dev/null +++ b/lib/format_text/text_label.c @@ -0,0 +1,280 @@ +/* + * Copyright (C) 2002 Sistina Software (UK) Limited. + * + * This file is released under the LGPL. + */ + +#include "lib.h" +#include "format-text.h" +#include "layout.h" +#include "label.h" +#include "xlate.h" + +#include <sys/stat.h> +#include <fcntl.h> + +static int _can_handle(struct labeller *l, char *buf, uint64_t sector) +{ + struct label_header *lh = (struct label_header *) buf; + + if (!strncmp(lh->type, LVM2_LABEL, sizeof(lh->type))) + return 1; + + return 0; +} + +static int _write(struct label *label, char *buf) +{ + struct label_header *lh = (struct label_header *) buf; + struct pv_header *pvhdr; + struct cache_info *info; + struct disk_locn *pvh_dlocn_xl; + struct list *mdash, *dash; + struct metadata_area *mda; + struct mda_context *mdac; + struct data_area_list *da; + + /* FIXME Move to where label is created */ + strncpy(label->type, LVM2_LABEL, sizeof(label->type)); + + strncpy(lh->type, label->type, sizeof(label->type)); + + pvhdr = (struct pv_header *) ((void *) buf + xlate32(lh->offset_xl)); + info = (struct cache_info *) label->info; + pvhdr->device_size_xl = xlate64(info->device_size); + memcpy(pvhdr->pv_uuid, &info->dev->pvid, sizeof(struct id)); + + pvh_dlocn_xl = &pvhdr->disk_areas_xl[0]; + + /* List of data areas (holding PEs) */ + list_iterate(dash, &info->das) { + da = list_item(dash, struct data_area_list); + + pvh_dlocn_xl->offset = xlate64(da->disk_locn.offset); + pvh_dlocn_xl->size = xlate64(da->disk_locn.size); + pvh_dlocn_xl++; + } + + /* NULL-termination */ + pvh_dlocn_xl->offset = xlate64(0); + pvh_dlocn_xl->size = xlate64(0); + pvh_dlocn_xl++; + + /* List of metadata area header locations */ + list_iterate(mdash, &info->mdas) { + mda = list_item(mdash, struct metadata_area); + mdac = (struct mda_context *) mda->metadata_locn; + + if (mdac->area.dev != info->dev) + continue; + + pvh_dlocn_xl->offset = xlate64(mdac->area.start); + pvh_dlocn_xl->size = xlate64(mdac->area.size); + pvh_dlocn_xl++; + } + + /* NULL-termination */ + pvh_dlocn_xl->offset = xlate64(0); + pvh_dlocn_xl->size = xlate64(0); + + return 1; +} + +int add_da(struct format_type *fmt, struct pool *mem, struct list *das, + uint64_t start, uint64_t size) +{ + struct data_area_list *dal; + + if (!mem) { + if (!(dal = dbg_malloc(sizeof(*dal)))) { + log_error("struct data_area_list allocation failed"); + return 0; + } + } else { + if (!(dal = pool_alloc(mem, sizeof(*dal)))) { + log_error("struct data_area_list allocation failed"); + return 0; + } + } + + dal->disk_locn.offset = start; + dal->disk_locn.size = size; + + list_add(das, &dal->list); + + return 1; +} + +void del_das(struct list *das) +{ + struct list *dah, *tmp; + struct data_area_list *da; + + list_iterate_safe(dah, tmp, das) { + da = list_item(dah, struct data_area_list); + list_del(&da->list); + dbg_free(da); + } +} + +int add_mda(struct format_type *fmt, struct pool *mem, struct list *mdas, + struct device *dev, uint64_t start, uint64_t size) +{ +/* FIXME List size restricted by pv_header SECTOR_SIZE */ + struct metadata_area *mdal; + struct mda_lists *mda_lists = (struct mda_lists *) fmt->private; + struct mda_context *mdac; + + if (!mem) { + if (!(mdal = dbg_malloc(sizeof(struct metadata_area)))) { + log_error("struct mda_list allocation failed"); + return 0; + } + + if (!(mdac = dbg_malloc(sizeof(struct mda_context)))) { + log_error("struct mda_context allocation failed"); + dbg_free(mdal); + return 0; + } + } else { + if (!(mdal = pool_alloc(mem, sizeof(struct metadata_area)))) { + log_error("struct mda_list allocation failed"); + return 0; + } + + if (!(mdac = pool_alloc(mem, sizeof(struct mda_context)))) { + log_error("struct mda_context allocation failed"); + return 0; + } + } + + mdal->ops = mda_lists->raw_ops; + mdal->metadata_locn = mdac; + + mdac->area.dev = dev; + mdac->area.start = start; + mdac->area.size = size; + memset(&mdac->rlocn, 0, sizeof(mdac->rlocn)); + + list_add(mdas, &mdal->list); + return 1; +} + +void del_mdas(struct list *mdas) +{ + struct list *mdah, *tmp; + struct metadata_area *mda; + + list_iterate_safe(mdah, tmp, mdas) { + mda = list_item(mdah, struct metadata_area); + dbg_free(mda->metadata_locn); + list_del(&mda->list); + dbg_free(mda); + } +} + +static int _initialise_label(struct labeller *l, struct label *label) +{ + strncpy(label->type, LVM2_LABEL, sizeof(label->type)); + + return 1; +} + +static int _read(struct labeller *l, struct device *dev, char *buf, + struct label **label) +{ + struct label_header *lh = (struct label_header *) buf; + struct pv_header *pvhdr; + struct cache_info *info; + struct disk_locn *dlocn_xl; + uint64_t offset; + struct list *mdah; + struct metadata_area *mda; + char vgnamebuf[NAME_LEN + 2]; + struct mda_context *mdac; + + pvhdr = (struct pv_header *) ((void *) buf + xlate32(lh->offset_xl)); + + if (!(info = cache_add(l, pvhdr->pv_uuid, dev, NULL, NULL))) + return 0; + *label = info->label; + + info->device_size = xlate64(pvhdr->device_size_xl); + + if (info->das.n) + del_das(&info->das); + list_init(&info->das); + + if (info->mdas.n) + del_mdas(&info->mdas); + list_init(&info->mdas); + + /* Data areas holding the PEs */ + dlocn_xl = pvhdr->disk_areas_xl; + while ((offset = xlate64(dlocn_xl->offset))) { + add_da(info->fmt, NULL, &info->das, offset, + xlate64(dlocn_xl->size)); + dlocn_xl++; + } + + /* Metadata area headers */ + dlocn_xl++; + while ((offset = xlate64(dlocn_xl->offset))) { + add_mda(info->fmt, NULL, &info->mdas, dev, offset, + xlate64(dlocn_xl->size)); + dlocn_xl++; + } + + list_iterate(mdah, &info->mdas) { + mda = list_item(mdah, struct metadata_area); + mdac = (struct mda_context *) mda->metadata_locn; + if (vgname_from_mda(info->fmt, &mdac->area, vgnamebuf, + sizeof(vgnamebuf))) { + cache_update_vgname(info, vgnamebuf); + } + } + + info->status &= ~CACHE_INVALID; + + return 1; +} + +static void _destroy_label(struct labeller *l, struct label *label) +{ + struct cache_info *info = (struct cache_info *) label->info; + + if (info->mdas.n) + del_mdas(&info->mdas); + if (info->das.n) + del_das(&info->das); +} + +static void _destroy(struct labeller *l) +{ + dbg_free(l); +} + +struct label_ops _text_ops = { + can_handle:_can_handle, + write:_write, + read:_read, + verify:_can_handle, + initialise_label:_initialise_label, + destroy_label:_destroy_label, + destroy:_destroy +}; + +struct labeller *text_labeller_create(struct format_type *fmt) +{ + struct labeller *l; + + if (!(l = dbg_malloc(sizeof(*l)))) { + log_err("Couldn't allocate labeller object."); + return NULL; + } + + l->ops = &_text_ops; + l->private = (void *) fmt; + + return l; +} diff --git a/lib/label/label.c b/lib/label/label.c index dcc523ecc..d1618b70e 100644 --- a/lib/label/label.c +++ b/lib/label/label.c @@ -4,10 +4,18 @@ * This file is released under the LGPL. */ +#include "lib.h" #include "label.h" #include "list.h" -#include "dbg_malloc.h" -#include "log.h" +#include "crc.h" +#include "xlate.h" +#include "cache.h" + +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> + +/* FIXME Allow for larger labels? Restricted to single sector currently */ /* * Internal labeller struct. @@ -58,6 +66,7 @@ void label_exit(void) for (c = _labellers.n; c != &_labellers; c = n) { n = c->n; li = list_item(c, struct labeller_i); + li->l->ops->destroy(li->l); _free_li(li); } } @@ -89,64 +98,257 @@ struct labeller *label_get_handler(const char *name) return NULL; } -static struct labeller *_find_labeller(struct device *dev) +static struct labeller *_find_labeller(struct device *dev, char *buf, + uint64_t *label_sector) { struct list *lih; struct labeller_i *li; + struct labeller *r = NULL; + int already_open; + struct label_header *lh; + uint64_t sector; + int found = 0; + char readbuf[LABEL_SCAN_SIZE]; - list_iterate(lih, &_labellers) { - li = list_item(lih, struct labeller_i); - if (li->l->ops->can_handle(li->l, dev)) - return li->l; + already_open = dev_is_open(dev); + + if (!already_open && !dev_open(dev, O_RDONLY)) { + stack; + return NULL; } - log_debug("No label on device '%s'.", dev_name(dev)); - return NULL; + if (dev_read(dev, 0, LABEL_SCAN_SIZE, readbuf) != LABEL_SCAN_SIZE) { + log_debug("%s: Failed to read label area", dev_name(dev)); + goto out; + } + + /* Scan first few sectors for a valid label */ + for (sector = 0; sector < LABEL_SCAN_SECTORS; + sector += LABEL_SIZE >> SECTOR_SHIFT) { + lh = (struct label_header *) (readbuf + + (sector << SECTOR_SHIFT)); + + if (!strncmp(lh->id, LABEL_ID, sizeof(lh->id))) { + if (found) { + log_error("Ignoring additional label on %s at " + "sector %" PRIu64, dev_name(dev), + sector); + } + if (xlate64(lh->sector_xl) != sector) { + log_info("%s: Label for sector %" PRIu64 + " found at sector %" PRIu64 + " - ignoring", dev_name(dev), + xlate64(lh->sector_xl), sector); + continue; + } + if (calc_crc(INITIAL_CRC, &lh->offset_xl, LABEL_SIZE - + ((void *) &lh->offset_xl - (void *) lh)) != + xlate32(lh->crc_xl)) { + log_info("Label checksum incorrect on %s - " + "ignoring", dev_name(dev)); + continue; + } + if (found) + continue; + } + + list_iterate(lih, &_labellers) { + li = list_item(lih, struct labeller_i); + if (li->l->ops->can_handle(li->l, (char *) lh, sector)) { + log_very_verbose("%s: %s label detected", + dev_name(dev), li->name); + if (found) { + log_error("Ignoring additional label " + "on %s at sector %" PRIu64, + dev_name(dev), sector); + continue; + } + r = li->l; + memcpy(buf, lh, LABEL_SIZE); + if (label_sector) + *label_sector = sector; + found = 1; + break; + } + } + } + + if (!found) + log_very_verbose("%s: No label detected", dev_name(dev)); + + out: + if (!already_open && !dev_close(dev)) + stack; + + return r; } +/* FIXME Also wipe associated metadata area headers? */ int label_remove(struct device *dev) { - struct labeller *l; + char buf[LABEL_SIZE]; + char readbuf[LABEL_SCAN_SIZE]; + int r = 1; + uint64_t sector; + int wipe; + struct list *lih; + struct labeller_i *li; + struct label_header *lh; + + memset(buf, 0, LABEL_SIZE); - if (!(l = _find_labeller(dev))) { + log_very_verbose("Scanning for labels to wipe from %s", dev_name(dev)); + + if (!dev_open(dev, O_RDWR)) { stack; return 0; } - return l->ops->remove(l, dev); + if (dev_read(dev, 0, LABEL_SCAN_SIZE, readbuf) != LABEL_SCAN_SIZE) { + log_debug("%s: Failed to read label area", dev_name(dev)); + goto out; + } + + /* Scan first few sectors for anything looking like a label */ + for (sector = 0; sector < LABEL_SCAN_SECTORS; + sector += LABEL_SIZE >> SECTOR_SHIFT) { + lh = (struct label_header *) (readbuf + + (sector << SECTOR_SHIFT)); + + wipe = 0; + + if (!strncmp(lh->id, LABEL_ID, sizeof(lh->id))) { + if (xlate64(lh->sector_xl) == sector) + wipe = 1; + } else { + list_iterate(lih, &_labellers) { + li = list_item(lih, struct labeller_i); + if (li->l->ops->can_handle(li->l, (char *) lh, + sector)) { + wipe = 1; + break; + } + } + } + + if (wipe) { + log_info("%s: Wiping label at sector %" PRIu64, + dev_name(dev), sector); + if (dev_write(dev, sector << SECTOR_SHIFT, LABEL_SIZE, + buf) != LABEL_SIZE) { + log_error("Failed to remove label from %s at " + "sector %" PRIu64, dev_name(dev), + sector); + r = 0; + } + } + } + + out: + if (!dev_close(dev)) + stack; + + return r; } +/* FIXME Avoid repeated re-reading if cache lock held */ int label_read(struct device *dev, struct label **result) { + char buf[LABEL_SIZE]; + struct labeller *l; + uint64_t sector; int r; - struct list *lih; - struct labeller_i *li; - list_iterate(lih, &_labellers) { - li = list_item(lih, struct labeller_i); - if ((r = li->l->ops->read(li->l, dev, result))) { - (*result)->labeller = li->l; - return r; - } + if (!(l = _find_labeller(dev, buf, §or))) { + stack; + return 0; } - log_debug("No label on device '%s'.", dev_name(dev)); - return 0; + if ((r = l->ops->read(l, dev, buf, result)) && result && *result) + (*result)->sector = sector; + + return r; +} + +/* Caller may need to use label_get_handler to create label struct! */ +int label_write(struct device *dev, struct label *label) +{ + char buf[LABEL_SIZE]; + struct label_header *lh = (struct label_header *) buf; + int r = 1; + int already_open; + + if ((LABEL_SIZE + (label->sector << SECTOR_SHIFT)) > LABEL_SCAN_SIZE) { + log_error("Label sector %" PRIu64 " beyond range (%ld)", + label->sector, LABEL_SCAN_SECTORS); + return 0; + } + + memset(buf, 0, LABEL_SIZE); + + strncpy(lh->id, LABEL_ID, sizeof(lh->id)); + lh->sector_xl = xlate64(label->sector); + lh->offset_xl = xlate32(sizeof(*lh)); + + if (!label->labeller->ops->write(label, buf)) + return 0; + + lh->crc_xl = xlate32(calc_crc(INITIAL_CRC, &lh->offset_xl, LABEL_SIZE - + ((void *) &lh->offset_xl - (void *) lh))); + + already_open = dev_is_open(dev); + if (!already_open && dev_open(dev, O_RDWR)) { + stack; + return 0; + } + + log_info("%s: Writing label to sector %" PRIu64, dev_name(dev), + label->sector); + if (dev_write(dev, label->sector << SECTOR_SHIFT, LABEL_SIZE, buf) != + LABEL_SIZE) { + log_debug("Failed to write label to %s", dev_name(dev)); + r = 0; + } + + if (!already_open && dev_close(dev)) + stack; + + return r; } int label_verify(struct device *dev) { struct labeller *l; + char buf[LABEL_SIZE]; + uint64_t sector; - if (!(l = _find_labeller(dev))) { + if (!(l = _find_labeller(dev, buf, §or))) { stack; return 0; } - return l->ops->verify(l, dev); + return l->ops->verify(l, buf, sector); } -void label_destroy(struct label *lab) +void label_destroy(struct label *label) { - lab->labeller->ops->destroy_label(lab->labeller, lab); + label->labeller->ops->destroy_label(label->labeller, label); + dbg_free(label); +} + +struct label *label_create(struct labeller *labeller) +{ + struct label *label; + + if (!(label = dbg_malloc(sizeof(*label)))) { + log_error("label allocaction failed"); + return NULL; + } + memset(label, 0, sizeof(*label)); + + label->labeller = labeller; + + labeller->ops->initialise_label(labeller, label); + + return label; } diff --git a/lib/label/label.h b/lib/label/label.h index ec652c843..bd9549bb0 100644 --- a/lib/label/label.h +++ b/lib/label/label.h @@ -7,18 +7,31 @@ #ifndef _LVM_LABEL_H #define _LVM_LABEL_H +#include "cache.h" +#include "lvm-types.h" #include "uuid.h" #include "device.h" +#define LABEL_ID "LABELONE" +#define LABEL_SIZE SECTOR_SIZE /* Think very carefully before changing this */ +#define LABEL_SCAN_SECTORS 4L +#define LABEL_SCAN_SIZE (LABEL_SCAN_SECTORS << SECTOR_SHIFT) + +/* On disk - 32 bytes */ +struct label_header { + uint8_t id[8]; /* LABELONE */ + uint64_t sector_xl; /* Sector number of this label */ + uint32_t crc_xl; /* From next field to end of sector */ + uint32_t offset_xl; /* Offset from start of struct to contents */ + uint8_t type[8]; /* LVM2 001 */ +} __attribute__ ((packed)); + +/* In core */ struct label { - struct id id; - - char volume_type[32]; - uint32_t version[3]; - - void *extra_info; - + char type[8]; + uint64_t sector; struct labeller *labeller; + void *info; }; struct labeller; @@ -27,39 +40,38 @@ struct label_ops { /* * Is the device labelled with this format ? */ - int (*can_handle)(struct labeller *l, struct device *dev); + int (*can_handle) (struct labeller * l, char *buf, uint64_t sector); /* * Write a label to a volume. */ - int (*write)(struct labeller *l, - struct device *dev, struct label *label); + int (*write) (struct label * label, char *buf); /* - * Remove a label from a device. + * Read a label from a volume. */ - int (*remove)(struct labeller *l, struct device *dev); + int (*read) (struct labeller * l, struct device * dev, + char *buf, struct label ** label); /* - * Read a label from a volume. + * Additional consistency checks for the paranoid. */ - int (*read)(struct labeller *l, - struct device *dev, struct label **label); + int (*verify) (struct labeller * l, char *buf, uint64_t sector); /* - * Additional consistency checks for the paranoid. + * Populate label_type etc. */ - int (*verify)(struct labeller *l, struct device *dev); + int (*initialise_label) (struct labeller * l, struct label * label); /* * Destroy a previously read label. */ - void (*destroy_label)(struct labeller *l, struct label *label); + void (*destroy_label) (struct labeller * l, struct label * label); /* * Destructor. */ - void (*destroy)(struct labeller *l); + void (*destroy) (struct labeller * l); }; struct labeller { @@ -67,7 +79,6 @@ struct labeller { void *private; }; - int label_init(void); void label_exit(void); @@ -77,14 +88,9 @@ struct labeller *label_get_handler(const char *name); int label_remove(struct device *dev); int label_read(struct device *dev, struct label **result); +int label_write(struct device *dev, struct label *label); int label_verify(struct device *dev); -void label_destroy(struct label *lab); - -/* - * We'll support two label types: the 'pretend the - * LVM1 pv structure at the begining of the disk - * is a label' hack, and pjc's 1 sector labels at - * the front and back of the device. - */ +struct label *label_create(struct labeller *labeller); +void label_destroy(struct label *label); #endif diff --git a/lib/label/lvm2_label.c b/lib/label/lvm2_label.c deleted file mode 100644 index d02675c0e..000000000 --- a/lib/label/lvm2_label.c +++ /dev/null @@ -1,569 +0,0 @@ -/* - * Copyright (C) 2001-2002 Sistina Software - * - * This file is released under the LGPL. - */ - -#include <sys/types.h> -#include <sys/stat.h> -#include <fcntl.h> -#include <string.h> -#include <unistd.h> - -#include "device.h" -#include "dev-cache.h" -#include "log.h" -#include "pool.h" -#include "dbg_malloc.h" -#include "filter.h" -#include "label.h" -#include "lvm2_label.h" -#include "xlate.h" - -/* Label Magic is "LnXl" - error: imagination failure */ -#define LABEL_MAGIC 0x6c586e4c - -/* Size of blocks that dev_get_size() returns the number of */ -#define BLOCK_SIZE 512 - -/* This is just the "struct lvm2_label" with the data pointer removed */ -struct label_ondisk { - uint32_t magic; - uint32_t crc; - uint64_t label1_loc; - uint64_t label2_loc; - uint16_t datalen; - uint16_t pad; - - uint32_t version[3]; - char disk_type[32]; -}; - -struct filter_private { - void *mem; - char disk_type[32]; - uint32_t version[3]; - int version_match; -}; - -/* Calculate CRC32 of a buffer */ -static uint32_t crc32(uint32_t initial, const unsigned char *databuf, - size_t datalen) -{ - static const u_int crctab[] = { - 0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac, - 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c, - 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, - 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c - }; - uint32_t idx, crc = initial; - - for (idx = 0; idx < datalen; idx++) { - crc ^= *databuf++; - crc = (crc >> 4) ^ crctab[crc & 0xf]; - crc = (crc >> 4) ^ crctab[crc & 0xf]; - } - return crc; -} - -/* Calculate crc */ -static uint32_t calc_crc(struct label_ondisk *label, char *data) -{ - uint32_t crcval = 0xffffffff; - - crcval = crc32(crcval, (char *) &label->magic, sizeof(label->magic)); - crcval = - crc32(crcval, (char *) &label->label1_loc, - sizeof(label->label1_loc)); - crcval = - crc32(crcval, (char *) &label->label2_loc, - sizeof(label->label2_loc)); - crcval = - crc32(crcval, (char *) &label->datalen, sizeof(label->datalen)); - crcval = - crc32(crcval, (char *) &label->version, sizeof(label->version)); - crcval = - crc32(crcval, (char *) label->disk_type, strlen(label->disk_type)); - crcval = crc32(crcval, (char *) data, label->datalen); - - return crcval; -} - -/* Calculate the locations we should find the labels in */ -static inline void get_label_locations(uint64_t size, uint32_t sectsize, - long *first, long *second) -{ - *first = sectsize; - *second = size * BLOCK_SIZE - sectsize; -} - -/* Read a label off disk */ -static int lvm2_label_read(struct labeller *l, struct device *dev, - struct label **label) -{ - uint64_t size; - uint32_t sectsize; - char *block; - struct label_ondisk *ondisk; - int status; - int iter; - long offset[2]; - - if (!dev_get_size(dev, &size)) - return 0; - - if (!dev_get_sectsize(dev, §size)) - return 0; - - if (!dev_open(dev, O_RDONLY)) - return 0; - - block = dbg_malloc(sectsize); - if (!block) { - stack; - return 0; - } - ondisk = (struct label_ondisk *) block; - get_label_locations(size, sectsize, &offset[0], &offset[1]); - - /* If the first label is bad then use the second */ - for (iter = 0; iter <= 1; iter++) { - status = dev_read(dev, offset[iter], sectsize, block); - if (status) { - struct label *incore; - int i; - int found_nul; - - /* If the MAGIC doesn't match there's no point in - carrying on */ - if (xlate32(ondisk->magic) != LABEL_MAGIC) - continue; - - /* Look for a NUL in the disk_type string so we don't - SEGV is something has gone horribly wrong */ - found_nul = 0; - for (i = 0; i < sizeof(ondisk->disk_type); i++) - if (ondisk->disk_type[i] == '\0') - found_nul = 1; - - if (!found_nul) - continue; - - incore = dbg_malloc(sizeof(struct label)); - if (incore == NULL) { - return 0; - } - - /* Copy and convert endianness */ - strncpy(incore->volume_type, ondisk->disk_type, - sizeof(incore->volume_type)); - incore->version[0] = xlate32(ondisk->version[0]); - incore->version[1] = xlate32(ondisk->version[1]); - incore->version[2] = xlate32(ondisk->version[2]); - incore->extra_len = xlate16(ondisk->datalen); - incore->extra_info = - block + sizeof(struct label_ondisk); - - /* Make sure datalen is a sensible size too */ - if (incore->extra_len > sectsize) - continue; - - /* Check Crc */ - if (xlate32(ondisk->crc) != - calc_crc(ondisk, incore->extra_info)) { - log_error - ("Crc %d on device %s does not match. got %x, expected %x", - iter, dev_name(dev), xlate32(ondisk->crc), - calc_crc(ondisk, incore->extra_info)); - continue; - } - - /* Check label locations match our view of the device */ - if (xlate64(ondisk->label1_loc) != offset[0]) - log_error - ("Label 1 location is wrong in label %d - check block size of the device\n", - iter); - if (xlate64(ondisk->label2_loc) != offset[1]) - log_error - ("Label 2 location is wrong in label %d - the size of the device must have changed\n", - iter); - - /* Copy to user's data area */ - *label = incore; - incore->extra_info = dbg_malloc(incore->extra_len); - if (!incore->extra_info) { - stack; - return 0; - } - memcpy(incore->extra_info, - block + sizeof(struct label_ondisk), - incore->extra_len); - - dbg_free(block); - dev_close(dev); - return 1; - } - } - - dbg_free(block); - dev_close(dev); - - return 0; -} - -/* Write a label to a device */ -static int lvm2_label_write(struct labeller *l, struct device *dev, - struct label *label) -{ - uint64_t size; - uint32_t sectsize; - char *block; - struct label_ondisk *ondisk; - int status1, status2; - long offset[2]; - - if (!dev_get_size(dev, &size)) - return 0; - - if (!dev_get_sectsize(dev, §size)) - return 0; - - /* Can the metata fit in the remaining space ? */ - if (label->extra_len > sectsize - sizeof(struct label_ondisk)) - return 0; - - block = dbg_malloc(sizeof(struct label_ondisk) + label->extra_len); - if (!block) { - stack; - return 0; - } - ondisk = (struct label_ondisk *) block; - - get_label_locations(size, sectsize, &offset[0], &offset[1]); - - /* Make into ondisk format */ - ondisk->magic = xlate32(LABEL_MAGIC); - ondisk->version[0] = xlate32(label->version[0]); - ondisk->version[1] = xlate32(label->version[1]); - ondisk->version[2] = xlate32(label->version[2]); - ondisk->label1_loc = xlate64(offset[0]); - ondisk->label2_loc = xlate64(offset[1]); - ondisk->datalen = xlate16(label->extra_len); - strncpy(ondisk->disk_type, label->volume_type, - sizeof(ondisk->disk_type)); - memcpy(block + sizeof(struct label_ondisk), label->extra_info, - label->extra_len); - ondisk->crc = xlate32(calc_crc(ondisk, label->extra_info)); - - /* Write metadata to disk */ - if (!dev_open(dev, O_RDWR)) { - dbg_free(block); - return 0; - } - - status1 = - dev_write(dev, offset[0], - sizeof(struct label_ondisk) + label->extra_len, block); - if (!status1) - log_error("Error writing label 1\n"); - - /* Write another at the end of the device */ - status2 = - dev_write(dev, offset[1], - sizeof(struct label_ondisk) + label->extra_len, block); - if (!status2) { - char zerobuf[sizeof(struct label_ondisk)]; - log_error("Error writing label 2\n"); - - /* Wipe the first label so it doesn't get confusing */ - memset(zerobuf, 0, sizeof(struct label_ondisk)); - if (!dev_write - (dev, offset[0], sizeof(struct label_ondisk), - zerobuf)) log_error("Error erasing label 1\n"); - } - - dbg_free(block); - dev_close(dev); - - return ((status1 != 0) && (status2 != 0)); -} - -/* Return 1 for Yes, 0 for No */ -static int lvm2_is_labelled(struct labeller *l, struct device *dev) -{ - struct label *label; - int status; - - status = lvm2_label_read(l, dev, &label); - if (status) - label_free(label); - - return status; -} - -/* Check the device is labelled and has the right format_type */ -static int _accept_format(struct dev_filter *f, struct device *dev) -{ - struct label *l; - int status; - struct filter_private *fp = (struct filter_private *) f->private; - - status = lvm2_label_read(NULL, dev, &l); - - if (status) { - if (strcmp(l->volume_type, fp->disk_type) == 0) { - switch (fp->version_match) { - case VERSION_MATCH_EQUAL: - if (l->version[0] == fp->version[0] && - l->version[1] == fp->version[1] && - l->version[2] == fp->version[2]) - return 1; - break; - - case VERSION_MATCH_LESSTHAN: - if (l->version[0] == fp->version[0] && - l->version[1] < fp->version[1]) - return 1; - break; - - case VERSION_MATCH_LESSEQUAL: - if (l->version[0] == fp->version[0] && - l->version[1] <= fp->version[1]) - return 1; - break; - - case VERSION_MATCH_ANY: - return 1; - } - } - label_free(l); - } - return 0; -} - -/* We just want to know if it's labelled or not */ -static int _accept_label(struct dev_filter *f, struct device *dev) -{ - return lvm2_is_labelled(NULL, dev); -} - -static void _destroy(struct dev_filter *f) -{ - struct filter_private *fp = (struct filter_private *) f->private; -} - -/* A filter to find devices with a particular label type on them */ -struct dev_filter *lvm2_label_format_filter_create(char *disk_type, - uint32_t version[3], - int match_type) -{ - struct pool *mem; - struct filter_private *fp; - struct dev_filter *f; - - /* Validate the match type */ - if (match_type != VERSION_MATCH_EQUAL && - match_type != VERSION_MATCH_LESSTHAN && - match_type != VERSION_MATCH_LESSEQUAL && - match_type != VERSION_MATCH_ANY) - return 0; - - mem = pool_create(10 * 1024); - if (!mem) { - stack; - return NULL; - } - - if (!(f = pool_zalloc(mem, sizeof(*f)))) { - stack; - goto bad; - } - - if (!(fp = pool_zalloc(mem, sizeof(*fp)))) { - stack; - goto bad; - } - - fp->mem = mem; - strcpy(fp->disk_type, disk_type); - fp->version[0] = version[0]; - fp->version[1] = version[1]; - fp->version[2] = version[2]; - fp->version_match = match_type; - f->passes_filter = _accept_format; - f->destroy = _destroy; - f->private = fp; - - return f; - - bad: - pool_destroy(mem); - return NULL; -} - -/* A filter to find devices with any label on them */ -struct dev_filter *lvm2_label_filter_create() -{ - struct pool *mem = pool_create(10 * 1024); - struct filter_private *fp; - struct dev_filter *f; - - if (!mem) { - stack; - return NULL; - } - - if (!(f = pool_zalloc(mem, sizeof(*f)))) { - stack; - goto bad; - } - - if (!(fp = pool_zalloc(mem, sizeof(*fp)))) { - stack; - goto bad; - } - - fp->mem = mem; - f->passes_filter = _accept_label; - f->destroy = _destroy; - f->private = fp; - - return f; - - bad: - pool_destroy(mem); - return NULL; -} - -/* Return 1 if both labels are identical, 0 if not or there was an error */ -static int lvm2_labels_match(struct labeller *l, struct device *dev) -{ - uint64_t size; - uint32_t sectsize; - char *block1; - char *block2; - struct label_ondisk *ondisk1; - struct label_ondisk *ondisk2; - int status = 0; - long offset[2]; - - if (!dev_get_size(dev, &size)) - return 0; - - if (!dev_get_sectsize(dev, §size)) - return 0; - -/* Allocate some space for the blocks we are going to read in */ - block1 = dbg_malloc(sectsize); - if (!block1) { - stack; - return 0; - } - - block2 = dbg_malloc(sectsize); - if (!block2) { - stack; - dbg_free(block1); - return 0; - } - ondisk1 = (struct label_ondisk *) block1; - ondisk2 = (struct label_ondisk *) block2; - - get_label_locations(size, sectsize, &offset[0], &offset[1]); - - /* Fetch em */ - if (!dev_open(dev, O_RDONLY)) - goto finish; - - if (!dev_read(dev, offset[0], sectsize, block1)) - goto finish; - - if (!dev_read(dev, offset[1], sectsize, block2)) - goto finish; - - dev_close(dev); - - /* Is it labelled? */ - if (xlate32(ondisk1->magic) != LABEL_MAGIC) - goto finish; - - /* Compare the whole structs */ - if (memcmp(ondisk1, ondisk2, sizeof(struct label_ondisk)) != 0) - goto finish; - - /* OK, check the data area */ - if (memcmp(block1 + sizeof(struct label_ondisk), - block2 + sizeof(struct label_ondisk), - xlate16(ondisk1->datalen)) != 0) - goto finish; - - /* They match !! */ - status = 1; - - finish: - dbg_free(block2); - dbg_free(block1); - - return status; -} - -static int lvm2_label_remove(struct labeller *l, struct device *dev) -{ - uint64_t size; - uint32_t sectsize; - char block[BLOCK_SIZE]; - int status1, status2; - long offset[2]; - - if (!dev_get_size(dev, &size)) - return 0; - - if (!dev_get_sectsize(dev, §size)) - return 0; - - if (!dev_open(dev, O_RDWR)) { - dbg_free(block); - return 0; - } - - get_label_locations(size, sectsize, &offset[0], &offset[1]); - memset(block, 0, BLOCK_SIZE); - - /* Blank out the first label */ - status1 = dev_write(dev, offset[0], BLOCK_SIZE, block); - if (!status1) - log_error("Error erasing label 1\n"); - - /* ...and the other at the end of the device */ - status2 = dev_write(dev, offset[1], BLOCK_SIZE, block); - if (!status2) - log_error("Error erasing label 2\n"); - - dev_close(dev); - - return ((status1 != 0) && (status2 != 0)); -} - -static void lvm2_label_destroy(struct labeller *l) -{ -} - -static struct label_ops handler_ops = { - can_handle: lvm2_is_labelled, - write: lvm2_label_write, - remove: lvm2_label_remove, - read: lvm2_label_read, - verify: lvm2_labels_match, - destroy: lvm2_label_destroy, -}; - -static struct labeller this_labeller = { - private: NULL, - ops: &handler_ops, -}; - -/* Don't know how this gets called... */ -void lvm2_label_init() -{ - label_register_handler("LVM2", &this_labeller); -} diff --git a/lib/label/lvm2_label.h b/lib/label/lvm2_label.h deleted file mode 100644 index e98118469..000000000 --- a/lib/label/lvm2_label.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (C) 2001 Sistina Software (UK) Limited. - * - * This file is released under the GPL. - */ - -struct lvm2_label -{ - uint32_t magic; - uint32_t crc; - uint64_t label1_loc; - uint64_t label2_loc; - uint16_t datalen; - - char disk_type[32]; - uint32_t version[3]; - - char *data; -}; - -#define VERSION_MATCH_EQUAL 1 -#define VERSION_MATCH_LESSTHAN 2 -#define VERSION_MATCH_LESSEQUAL 3 -#define VERSION_MATCH_ANY 4 - -extern struct dev_filter *lvm2_label_filter_create(); -extern struct dev_filter *lvm2_label_format_filter_create(char *disk_type, uint32_t version[3], int match_type); diff --git a/lib/label/uuid-map.c b/lib/label/uuid-map.c deleted file mode 100644 index 0e31021c6..000000000 --- a/lib/label/uuid-map.c +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (C) 2001 Sistina Software (UK) Limited. - * - * This file is released under the LGPL. - */ - -#ifndef _LVM_UUID_MAP_H -#define _LVM_UUID_MAP_H - -#include "uuid-map.h" -#include "dev-cache.h" -#include "dbg_malloc.h" -#include "log.h" -#include "label.h" -#include "pool.h" - -struct uuid_map { - struct dev_filter *filter; -}; - -struct uuid_map *uuid_map_create(struct dev_filter *devices) -{ - struct uuid_map *um; - - if (!(um = dbg_malloc(sizeof(*um)))) { - log_err("Couldn't allocate uuid_map object."); - return NULL; - } - - um->filter = devices; - return um; -} - -void uuid_map_destroy(struct uuid_map *um) -{ - dbg_free(um); -} - -/* - * Simple, non-caching implementation to start with. - */ -struct device *uuid_map_lookup(struct uuid_map *um, struct id *id) -{ - struct dev_iter *iter; - struct device *dev; - struct label *lab; - - if (!(iter = dev_iter_create(um->filter))) { - stack; - return NULL; - } - - while ((dev = dev_iter_get(iter))) { - - if (!label_read(dev, &lab)) - continue; - - if (id_equal(id, &lab->id)) { - label_destroy(lab); - break; - } - - label_destroy(lab); - } - - dev_iter_destroy(iter); - return dev; -} - -struct id *uuid_map_lookup_label(struct pool *mem, struct uuid_map *um, - const char *name) -{ - struct device *dev; - struct label *lab; - struct id *id; - - if (!(dev = dev_cache_get(name, um->filter))) { - stack; - return NULL; - } - - if (!label_read(dev, &lab)) { - stack; - return NULL; - } - - if (!(id = pool_alloc(mem, sizeof(*id)))) { - stack; - label_destroy(lab); - return NULL; - } - memcpy(id, &lab->id, sizeof(*id)); - - label_destroy(lab); - - return id; -} - -#endif diff --git a/lib/label/uuid-map.h b/lib/label/uuid-map.h deleted file mode 100644 index aff7a02fd..000000000 --- a/lib/label/uuid-map.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (C) 2001 Sistina Software (UK) Limited. - * - * This file is released under the LGPL. - */ - -#ifndef _LVM_UUID_MAP_H -#define _LVM_UUID_MAP_H - -#include "uuid.h" -#include "dev-cache.h" -#include "pool.h" - -/* - * Holds a mapping from uuid -> device. - */ -struct uuid_map; - -struct uuid_map *uuid_map_create(struct dev_filter *devices); -void uuid_map_destroy(struct uuid_map *um); - -/* - * Find the device with a particular uuid. - */ -struct device *uuid_map_lookup(struct uuid_map *um, struct id *id); -struct id *uuid_map_lookup_label(struct pool *mem, struct uuid_map *um, - const char *name); - -#endif diff --git a/lib/metadata/lv_manip.c b/lib/metadata/lv_manip.c index 5a2136c66..569abfc3e 100644 --- a/lib/metadata/lv_manip.c +++ b/lib/metadata/lv_manip.c @@ -4,10 +4,9 @@ * This file is released under the LGPL. */ +#include "lib.h" #include "metadata.h" #include "pv_map.h" -#include "log.h" -#include "dbg_malloc.h" #include "lvm-string.h" #include "toolcontext.h" @@ -17,7 +16,7 @@ * These functions adjust the pe counts in pv's * after we've added or removed segments. */ -static void _get_extents(struct stripe_segment *seg) +static void _get_extents(struct lv_segment *seg) { int s, count; struct physical_volume *pv; @@ -29,7 +28,7 @@ static void _get_extents(struct stripe_segment *seg) } } -static void _put_extents(struct stripe_segment *seg) +static void _put_extents(struct lv_segment *seg) { int s, count; struct physical_volume *pv; @@ -43,9 +42,9 @@ static void _put_extents(struct stripe_segment *seg) } } -static struct stripe_segment *_alloc_segment(struct pool *mem, int stripes) +static struct lv_segment *_alloc_segment(struct pool *mem, int stripes) { - struct stripe_segment *seg; + struct lv_segment *seg; uint32_t len = sizeof(*seg) + (stripes * sizeof(seg->area[0])); if (!(seg = pool_zalloc(mem, len))) { @@ -58,13 +57,13 @@ static struct stripe_segment *_alloc_segment(struct pool *mem, int stripes) static int _alloc_stripe_area(struct logical_volume *lv, uint32_t stripes, uint32_t stripe_size, - struct pv_area **areas, uint32_t * index) + struct pv_area **areas, uint32_t *index) { uint32_t count = lv->le_count - *index; uint32_t per_area = count / stripes; uint32_t smallest = areas[stripes - 1]->count; uint32_t s; - struct stripe_segment *seg; + struct lv_segment *seg; if (smallest < per_area) per_area = smallest; @@ -75,6 +74,7 @@ static int _alloc_stripe_area(struct logical_volume *lv, uint32_t stripes, } seg->lv = lv; + seg->type = SEG_STRIPED; seg->le = *index; seg->len = per_area * stripes; seg->stripes = stripes; @@ -169,11 +169,11 @@ static int _alloc_striped(struct logical_volume *lv, * the complete area then the area is split, otherwise the area * is unlinked from the pv_map. */ -static int _alloc_linear_area(struct logical_volume *lv, uint32_t * index, +static int _alloc_linear_area(struct logical_volume *lv, uint32_t *index, struct pv_map *map, struct pv_area *pva) { uint32_t count, remaining; - struct stripe_segment *seg; + struct lv_segment *seg; count = pva->count; remaining = lv->le_count - *index; @@ -186,6 +186,7 @@ static int _alloc_linear_area(struct logical_volume *lv, uint32_t * index, } seg->lv = lv; + seg->type = SEG_STRIPED; seg->le = *index; seg->len = count; seg->stripe_size = 0; @@ -248,8 +249,8 @@ static int _alloc_contiguous(struct logical_volume *lv, * Areas just get allocated in order until the lv * is full. */ -static int _alloc_simple(struct logical_volume *lv, - struct list *pvms, uint32_t allocated) +static int _alloc_next_free(struct logical_volume *lv, + struct list *pvms, uint32_t allocated) { struct list *tmp1, *tmp2; struct pv_map *pvm; @@ -305,8 +306,8 @@ static int _allocate(struct volume_group *vg, struct logical_volume *lv, else if (lv->alloc == ALLOC_CONTIGUOUS) r = _alloc_contiguous(lv, pvms, allocated); - else if (lv->alloc == ALLOC_NEXT_FREE) - r = _alloc_simple(lv, pvms, allocated); + else if (lv->alloc == ALLOC_NEXT_FREE || lv->alloc == ALLOC_DEFAULT) + r = _alloc_next_free(lv, pvms, allocated); else { log_error("Unknown allocation policy: " @@ -322,7 +323,7 @@ static int _allocate(struct volume_group *vg, struct logical_volume *lv, * counts in pv's. */ for (segh = lv->segments.p; segh != old_tail; segh = segh->p) - _get_extents(list_item(segh, struct stripe_segment)); + _get_extents(list_item(segh, struct lv_segment)); } else { /* * Put the segment list back how we found it. @@ -456,12 +457,12 @@ int lv_reduce(struct format_instance *fi, struct logical_volume *lv, uint32_t extents) { struct list *segh; - struct stripe_segment *seg; + struct lv_segment *seg; uint32_t count = extents; for (segh = lv->segments.p; (segh != &lv->segments) && count; segh = segh->p) { - seg = list_item(segh, struct stripe_segment); + seg = list_item(segh, struct lv_segment); if (seg->len <= count) { /* remove this segment completely */ @@ -534,7 +535,7 @@ int lv_remove(struct volume_group *vg, struct logical_volume *lv) /* iterate through the lv's segments freeing off the pe's */ list_iterate(segh, &lv->segments) - _put_extents(list_item(segh, struct stripe_segment)); + _put_extents(list_item(segh, struct lv_segment)); vg->lv_count--; vg->free_count += lv->le_count; diff --git a/lib/metadata/merge.c b/lib/metadata/merge.c index 86220046f..67413a956 100644 --- a/lib/metadata/merge.c +++ b/lib/metadata/merge.c @@ -4,7 +4,7 @@ * This file is released under the LGPL. */ -#include "log.h" +#include "lib.h" #include "metadata.h" /* @@ -12,12 +12,14 @@ * successfully merged. If the do merge, 'first' * will be adjusted to contain both areas. */ -static int _merge(struct stripe_segment *first, struct stripe_segment *second) +static int _merge(struct lv_segment *first, struct lv_segment *second) { int s; uint32_t width; if (!first || + (first->type != SEG_STRIPED) || + (first->type != second->type) || (first->stripes != second->stripes) || (first->stripe_size != second->stripe_size)) return 0; @@ -39,10 +41,10 @@ static int _merge(struct stripe_segment *first, struct stripe_segment *second) int lv_merge_segments(struct logical_volume *lv) { struct list *segh; - struct stripe_segment *current, *prev = NULL; + struct lv_segment *current, *prev = NULL; list_iterate(segh, &lv->segments) { - current = list_item(segh, struct stripe_segment); + current = list_item(segh, struct lv_segment); if (_merge(prev, current)) list_del(¤t->list); diff --git a/lib/metadata/metadata.c b/lib/metadata/metadata.c index 668502322..8941f8261 100644 --- a/lib/metadata/metadata.c +++ b/lib/metadata/metadata.c @@ -4,24 +4,21 @@ * This file is released under the LGPL. */ -#include "log.h" +#include "lib.h" #include "pool.h" #include "device.h" -#include "dev-cache.h" #include "metadata.h" #include "toolcontext.h" #include "lvm-string.h" -#include "uuid.h" -#include "vgcache.h" +#include "cache.h" -#include <string.h> - -int _add_pv_to_vg(struct format_instance *fi, struct volume_group *vg, +int _add_pv_to_vg(struct format_instance *fid, struct volume_group *vg, const char *pv_name) { struct pv_list *pvl; struct physical_volume *pv; - struct pool *mem = fi->fmt->cmd->mem; + struct pool *mem = fid->fmt->cmd->mem; + struct list mdas; log_verbose("Adding physical volume '%s' to volume group '%s'", pv_name, vg->name); @@ -31,7 +28,8 @@ int _add_pv_to_vg(struct format_instance *fi, struct volume_group *vg, return 0; } - if (!(pv = pv_read(fi->fmt->cmd, pv_name))) { + list_init(&mdas); + if (!(pv = pv_read(fid->fmt->cmd, pv_name, &mdas, NULL))) { log_error("Failed to read existing physical volume '%s'", pv_name); return 0; @@ -43,6 +41,12 @@ int _add_pv_to_vg(struct format_instance *fi, struct volume_group *vg, return 0; } + if (pv->fmt != fid->fmt) { + log_error("Physical volume %s is of different format type (%s)", + pv_name, pv->fmt->name); + return 0; + } + if (!(pv->vg_name = pool_strdup(mem, vg->name))) { log_error("vg->name allocation failed for '%s'", pv_name); return 0; @@ -58,13 +62,14 @@ int _add_pv_to_vg(struct format_instance *fi, struct volume_group *vg, /* * The next two fields should be corrected - * by fi->pv_setup. + * by fid->pv_setup. */ - pv->pe_count = (pv->size - pv->pe_start) / pv->pe_size; + pv->pe_count = (pv->size - pv->pe_start) / vg->extent_size; pv->pe_alloc_count = 0; - if (!fi->fmt->ops->pv_setup(fi, pv, vg)) { + if (!fid->fmt->ops->pv_setup(fid->fmt, 0, 0, vg->extent_size, 0, 0, + &fid->metadata_areas, pv, vg)) { log_error("Format-specific setup of physical volume '%s' " "failed.", pv_name); return 0; @@ -93,19 +98,21 @@ int _add_pv_to_vg(struct format_instance *fi, struct volume_group *vg, return 1; } -int vg_extend(struct format_instance *fi, +int vg_extend(struct format_instance *fid, struct volume_group *vg, int pv_count, char **pv_names) { int i; /* attach each pv */ for (i = 0; i < pv_count; i++) - if (!_add_pv_to_vg(fi, vg, pv_names[i])) { + if (!_add_pv_to_vg(fid, vg, pv_names[i])) { log_error("Unable to add physical volume '%s' to " "volume group '%s'.", pv_names[i], vg->name); return 0; } +/* FIXME Decide whether to initialise and add new mdahs to format instance */ + return 1; } @@ -124,6 +131,7 @@ struct volume_group *vg_create(struct cmd_context *cmd, const char *vg_name, { struct volume_group *vg; struct pool *mem = cmd->mem; + int consistent = 0; if (!(vg = pool_zalloc(mem, sizeof(*vg)))) { stack; @@ -132,7 +140,7 @@ struct volume_group *vg_create(struct cmd_context *cmd, const char *vg_name, /* is this vg name already in use ? */ init_partial(1); - if (vg_read(cmd, vg_name)) { + if (vg_read(cmd, vg_name, &consistent)) { log_err("A volume group called '%s' already exists.", vg_name); goto bad; } @@ -198,11 +206,17 @@ struct volume_group *vg_create(struct cmd_context *cmd, const char *vg_name, return NULL; } -struct physical_volume *pv_create(struct format_instance *fid, - const char *name, - struct id *id, uint64_t size) +/* Sizes in sectors */ +struct physical_volume *pv_create(struct format_type *fmt, + struct device *dev, + struct id *id, uint64_t size, + uint64_t pe_start, + uint32_t existing_extent_count, + uint32_t existing_extent_size, + int pvmetadatacopies, + uint64_t pvmetadatasize, struct list *mdas) { - struct pool *mem = fid->fmt->cmd->mem; + struct pool *mem = fmt->cmd->mem; struct physical_volume *pv = pool_alloc(mem, sizeof(*pv)); if (!pv) { @@ -215,10 +229,7 @@ struct physical_volume *pv_create(struct format_instance *fid, else memcpy(&pv->id, id, sizeof(*id)); - if (!(pv->dev = dev_cache_get(name, fid->fmt->cmd->filter))) { - log_error("%s: Couldn't find device.", name); - goto bad; - } + pv->dev = dev; if (!(pv->vg_name = pool_alloc(mem, NAME_LEN))) { stack; @@ -229,22 +240,22 @@ struct physical_volume *pv_create(struct format_instance *fid, pv->status = ALLOCATABLE_PV; if (!dev_get_size(pv->dev, &pv->size)) { - log_error("%s: Couldn't get size.", name); + log_error("%s: Couldn't get size.", dev_name(pv->dev)); goto bad; } if (size) { if (size > pv->size) log_print("WARNING: %s: Overriding real size. " - "You could lose data.", name); + "You could lose data.", dev_name(pv->dev)); log_verbose("%s: Pretending size is %" PRIu64 " sectors.", - name, size); + dev_name(pv->dev), size); pv->size = size; } if (pv->size < PV_MIN_SIZE) { - log_error("%s: Size must exceed minimum of %lu sectors.", - name, PV_MIN_SIZE); + log_error("%s: Size must exceed minimum of %ld sectors.", + dev_name(pv->dev), PV_MIN_SIZE); goto bad; } @@ -252,11 +263,14 @@ struct physical_volume *pv_create(struct format_instance *fid, pv->pe_start = 0; pv->pe_count = 0; pv->pe_alloc_count = 0; - pv->fid = fid; + pv->fmt = fmt; - if (!fid->fmt->ops->pv_setup(fid, pv, NULL)) { + if (!fmt->ops->pv_setup(fmt, pe_start, existing_extent_count, + existing_extent_size, + pvmetadatacopies, pvmetadatasize, mdas, + pv, NULL)) { log_error("%s: Format-specific setup of physical volume " - "failed.", name); + "failed.", dev_name(pv->dev)); goto bad; } return pv; @@ -278,7 +292,21 @@ struct pv_list *find_pv_in_vg(struct volume_group *vg, const char *pv_name) } return NULL; +} + +struct physical_volume *find_pv_in_vg_by_uuid(struct volume_group *vg, + struct id *id) +{ + struct list *pvh; + struct pv_list *pvl; + list_iterate(pvh, &vg->pvs) { + pvl = list_item(pvh, struct pv_list); + if (id_equal(&pvl->pv->id, id)) + return pvl->pv; + } + + return NULL; } struct lv_list *find_lv_in_vg(struct volume_group *vg, const char *lv_name) @@ -339,16 +367,14 @@ struct physical_volume *find_pv(struct volume_group *vg, struct device *dev) int vg_remove(struct volume_group *vg) { struct list *mdah; - void *mdl; - - if (!vg->fid->fmt->ops->vg_remove) - return 1; + struct metadata_area *mda; /* FIXME Improve recovery situation? */ /* Remove each copy of the metadata */ list_iterate(mdah, &vg->fid->metadata_areas) { - mdl = list_item(mdah, struct metadata_area)->metadata_locn; - if (!vg->fid->fmt->ops->vg_remove(vg->fid, vg, mdl)) { + mda = list_item(mdah, struct metadata_area); + if (mda->ops->vg_remove && + !mda->ops->vg_remove(vg->fid, vg, mda)) { stack; return 0; } @@ -360,7 +386,8 @@ int vg_remove(struct volume_group *vg) int vg_write(struct volume_group *vg) { struct list *mdah; - void *mdl; + struct metadata_area *mda; + int cache_updated = 0; if (vg->status & PARTIAL_VG) { log_error("Cannot change metadata for partial volume group %s", @@ -368,24 +395,31 @@ int vg_write(struct volume_group *vg) return 0; } + if (list_empty(&vg->fid->metadata_areas)) { + log_error("Aborting vg_write: No metadata areas to write to!"); + return 0; + } + vg->seqno++; /* Write to each copy of the metadata area */ list_iterate(mdah, &vg->fid->metadata_areas) { - mdl = list_item(mdah, struct metadata_area)->metadata_locn; - if (!vg->fid->fmt->ops->vg_write(vg->fid, vg, mdl)) { + mda = list_item(mdah, struct metadata_area); + if (!mda->ops->vg_write(vg->fid, vg, mda)) { stack; return 0; } } - if (!vg->fid->fmt->ops->vg_commit) - return 1; - /* Commit to each copy of the metadata area */ list_iterate(mdah, &vg->fid->metadata_areas) { - mdl = list_item(mdah, struct metadata_area)->metadata_locn; - if (!vg->fid->fmt->ops->vg_commit(vg->fid, vg, mdl)) { + mda = list_item(mdah, struct metadata_area); + if (!cache_updated) { + cache_update_vg(vg); + cache_updated = 1; + } + if (mda->ops->vg_commit && + !mda->ops->vg_commit(vg->fid, vg, mda)) { stack; return 0; } @@ -394,46 +428,105 @@ int vg_write(struct volume_group *vg) return 1; } -struct volume_group *vg_read(struct cmd_context *cmd, const char *vg_name) +/* Make orphan PVs look like a VG */ +struct volume_group *_vg_read_orphans(struct cmd_context *cmd) +{ + struct cache_vginfo *vginfo; + struct list *ih; + struct device *dev; + struct pv_list *pvl; + struct volume_group *vg; + struct physical_volume *pv; + + if (!(vginfo = vginfo_from_vgname(ORPHAN))) { + stack; + return NULL; + } + + if (!(vg = pool_zalloc(cmd->mem, sizeof(*vg)))) { + log_error("vg allocation failed"); + return NULL; + } + list_init(&vg->pvs); + list_init(&vg->lvs); + list_init(&vg->snapshots); + vg->cmd = cmd; + if (!(vg->name = pool_strdup(cmd->mem, ORPHAN))) { + log_error("vg name allocation failed"); + return NULL; + } + + list_iterate(ih, &vginfo->infos) { + dev = list_item(ih, struct cache_info)->dev; + if (!(pv = pv_read(cmd, dev_name(dev), NULL, NULL))) { + continue; + } + if (!(pvl = pool_zalloc(cmd->mem, sizeof(*pvl)))) { + log_error("pv_list allocation failed"); + return NULL; + } + pvl->pv = pv; + list_add(&vg->pvs, &pvl->list); + vg->pv_count++; + } + + return vg; +} + +/* Caller sets consistent to 1 if it's safe for vg_read to correct + * inconsistent metadata on disk (i.e. the VG write lock is held). + * This guarantees only consistent metadata is returned unless PARTIAL_VG. + * If consistent is 0, caller must check whether consistent == 1 on return + * and take appropriate action if it isn't (e.g. abort; get write lock + * and call vg_read again). + */ +struct volume_group *vg_read(struct cmd_context *cmd, const char *vgname, + int *consistent) { struct format_instance *fid; struct format_type *fmt; struct volume_group *vg, *correct_vg; - struct list *mdah, *names; - void *mdl; + struct list *mdah; + struct metadata_area *mda; int inconsistent = 0, first_time = 1; - /* create format instance with appropriate metadata area */ - if (!(fmt = vgcache_find_format(vg_name))) { - /* Do full scan */ - if (!(names = get_vgs(cmd))) { - stack; - return NULL; - } - pool_free(cmd->mem, names); - if (!(fmt = vgcache_find_format(vg_name))) { - stack; - return NULL; + if (!*vgname) { + *consistent = 1; + return _vg_read_orphans(cmd); + } + + /* Find the vgname in the cache */ + /* If it's not there we must do full scan to be completely sure */ + if (!(fmt = fmt_from_vgname(vgname))) { + cache_label_scan(cmd, 0); + if (!(fmt = fmt_from_vgname(vgname))) { + cache_label_scan(cmd, 1); + if (!(fmt = fmt_from_vgname(vgname))) { + stack; + return NULL; + } } } - if (!(fid = fmt->ops->create_instance(fmt, vg_name, NULL))) { + /* create format instance with appropriate metadata area */ + if (!(fid = fmt->ops->create_instance(fmt, vgname, NULL))) { log_error("Failed to create format instance"); return NULL; } /* Ensure contents of all metadata areas match - else do recovery */ list_iterate(mdah, &fid->metadata_areas) { - mdl = list_item(mdah, struct metadata_area)->metadata_locn; - if (!(vg = fid->fmt->ops->vg_read(fid, vg_name, mdl))) { - inconsistent = 1; - continue; + mda = list_item(mdah, struct metadata_area); + if (!(vg = mda->ops->vg_read(fid, vgname, mda))) { + inconsistent = 1; + continue; } if (first_time) { correct_vg = vg; first_time = 0; continue; } + /* FIXME Also ensure contents same - checksum compare? */ if (correct_vg->seqno != vg->seqno) { inconsistent = 1; if (vg->seqno > correct_vg->seqno) @@ -447,7 +540,21 @@ struct volume_group *vg_read(struct cmd_context *cmd, const char *vg_name) return NULL; } + cache_update_vg(correct_vg); + if (inconsistent) { + if (!*consistent) + return correct_vg; + + /* Don't touch partial volume group metadata */ + /* Should be fixed manually with vgcfgbackup/restore etc. */ + if ((correct_vg->status & PARTIAL_VG)) { + log_error("Inconsistent metadata copies found for " + "partial volume group %s", vgname); + *consistent = 0; + return correct_vg; + } + log_print("Inconsistent metadata copies found - updating " "to use version %u", correct_vg->seqno); if (!vg_write(correct_vg)) { @@ -456,51 +563,121 @@ struct volume_group *vg_read(struct cmd_context *cmd, const char *vg_name) } } - vgcache_add(vg_name, correct_vg->id.uuid, NULL, fmt); + *consistent = 1; return correct_vg; } +/* This is only called by lv_from_lvid, which is only called from + * activate.c so we know the appropriate VG lock is already held and + * the vg_read is therefore safe. + */ struct volume_group *vg_read_by_vgid(struct cmd_context *cmd, const char *vgid) { char *vgname; - struct list *vgs, *vgh; + struct list *vgnames, *slh; struct volume_group *vg; + struct cache_vginfo *vginfo; + int consistent = 0; + + /* Is corresponding vgname already cached? */ + if ((vginfo = vginfo_from_vgid(vgid)) && + vginfo->vgname && *vginfo->vgname) { + if ((vg = vg_read(cmd, vginfo->vgname, &consistent)) && + !strncmp(vg->id.uuid, vgid, ID_LEN)) { + if (!consistent) { + log_error("Volume group %s metadata is " + "inconsistent", vginfo->vgname); + return NULL; + } + return vg; + } + } - if (!(vgs = get_vgs(cmd))) { + /* The slow way - full scan required to cope with vgrename */ + if (!(vgnames = get_vgs(cmd, 1))) { log_error("vg_read_by_vgid: get_vgs failed"); return NULL; } - list_iterate(vgh, vgs) { - vgname = list_item(vgh, struct name_list)->name; - if ((vg = vg_read(cmd, vgname)) && - !strncmp(vg->id.uuid, vgid, ID_LEN)) return vg; + list_iterate(slh, vgnames) { + vgname = list_item(slh, struct str_list)->str; + if (!vgname || !*vgname) + continue; /* FIXME Unnecessary? */ + consistent = 0; + if ((vg = vg_read(cmd, vgname, &consistent)) && + !strncmp(vg->id.uuid, vgid, ID_LEN)) { + if (!consistent) { + log_error("Volume group %s metadata is " + "inconsistent", vgname); + return NULL; + } + return vg; + } } - pool_free(cmd->mem, vgs); return NULL; } -/* FIXME Use label functions instead of PV functions? */ -struct physical_volume *pv_read(struct cmd_context *cmd, const char *pv_name) +/* Only called by activate.c */ +struct logical_volume *lv_from_lvid(struct cmd_context *cmd, const char *lvid_s) +{ + struct lv_list *lvl; + struct volume_group *vg; + union lvid *lvid; + + lvid = (union lvid *) lvid_s; + + log_very_verbose("Finding volume group for uuid %s", lvid_s); + if (!(vg = vg_read_by_vgid(cmd, lvid->id[0].uuid))) { + log_error("Volume group for uuid not found: %s", lvid_s); + return NULL; + } + + log_verbose("Found volume group \"%s\"", vg->name); + if (vg->status & EXPORTED_VG) { + log_error("Volume group \"%s\" is exported", vg->name); + return NULL; + } + if (!(lvl = find_lv_in_vg_by_lvid(vg, lvid))) { + log_very_verbose("Can't find logical volume id %s", lvid_s); + return NULL; + } + + return lvl->lv; +} + +/* FIXME Use label functions instead of PV functions */ +struct physical_volume *pv_read(struct cmd_context *cmd, const char *pv_name, + struct list *mdas, uint64_t *label_sector) { struct physical_volume *pv; + struct label *label; + struct cache_info *info; + struct device *dev; - if (!(pv = pool_zalloc(cmd->mem, sizeof(*pv)))) { - log_error("pv_list allocation for '%s' failed", pv_name); + if (!(dev = dev_cache_get(pv_name, cmd->filter))) { + stack; return 0; } - /* Member of a format1 VG? */ - if (!(cmd->fmt1->ops->pv_read(cmd->fmt1, pv_name, pv))) { - log_error("Failed to read existing physical volume '%s'", + if (!(label_read(dev, &label))) { + log_error("Failed to read label on physical volume %s", pv_name); return 0; } - /* Member of a format_text VG? */ - if (!(cmd->fmtt->ops->pv_read(cmd->fmtt, pv_name, pv))) { + info = (struct cache_info *) label->info; + if (label_sector && *label_sector) + *label_sector = label->sector; + + if (!(pv = pool_zalloc(cmd->mem, sizeof(*pv)))) { + log_error("pv_list allocation for '%s' failed", pv_name); + return 0; + } + + /* FIXME Move more common code up here */ + if (!(info->fmt->ops->pv_read(info->fmt, pv_name, pv, mdas))) { log_error("Failed to read existing physical volume '%s'", pv_name); return 0; @@ -512,30 +689,22 @@ struct physical_volume *pv_read(struct cmd_context *cmd, const char *pv_name) return pv; } -struct list *get_vgs(struct cmd_context *cmd) +/* May return empty list */ +struct list *get_vgs(struct cmd_context *cmd, int full_scan) { - struct list *names; - - if (!(names = pool_alloc(cmd->mem, sizeof(*names)))) { - log_error("VG name list allocation failed"); - return NULL; - } - - list_init(names); - - if (!cmd->fmt1->ops->get_vgs(cmd->fmt1, names) || - !cmd->fmtt->ops->get_vgs(cmd->fmtt, names) || - list_empty(names)) { - pool_free(cmd->mem, names); - return NULL; - } - - return names; + return cache_get_vgnames(cmd, full_scan); } struct list *get_pvs(struct cmd_context *cmd) { struct list *results; + char *vgname; + struct list *pvh, *tmp; + struct list *vgnames, *slh; + struct volume_group *vg; + int consistent = 0; + + cache_label_scan(cmd, 0); if (!(results = pool_alloc(cmd->mem, sizeof(*results)))) { log_error("PV list allocation failed"); @@ -544,40 +713,50 @@ struct list *get_pvs(struct cmd_context *cmd) list_init(results); - /* fmtt modifies fmt1 output */ - if (!cmd->fmt1->ops->get_pvs(cmd->fmt1, results) || - !cmd->fmtt->ops->get_pvs(cmd->fmtt, results)) { - pool_free(cmd->mem, results); + /* Get list of VGs */ + if (!(vgnames = get_vgs(cmd, 0))) { + log_error("get_pvs: get_vgs failed"); return NULL; } + /* Read every VG to ensure cache consistency */ + /* Orphan VG is last on list */ + init_partial(1); + list_iterate(slh, vgnames) { + vgname = list_item(slh, struct str_list)->str; + if (!vgname) + continue; /* FIXME Unnecessary? */ + consistent = 0; + if (!(vg = vg_read(cmd, vgname, &consistent))) { + stack; + continue; + } + if (!consistent) + log_print("Warning: Volume Group %s is not consistent", + vgname); + + /* Move PVs onto results list */ + list_iterate_safe(pvh, tmp, &vg->pvs) { + list_add(results, pvh); + } + } + init_partial(0); + return results; } -int pv_write(struct cmd_context *cmd, struct physical_volume *pv) +int pv_write(struct cmd_context *cmd, struct physical_volume *pv, + struct list *mdas, int64_t label_sector) { - struct list *mdah; - void *mdl; - - /* Write to each copy of the metadata area */ - list_iterate(mdah, &pv->fid->metadata_areas) { - mdl = list_item(mdah, struct metadata_area)->metadata_locn; - if (!pv->fid->fmt->ops->pv_write(pv->fid, pv, mdl)) { - stack; - return 0; - } + if (*pv->vg_name || pv->pe_alloc_count) { + log_error("Assertion failed: can't _pv_write non-orphan PV " + "(in VG %s)", pv->vg_name); + return 0; } - if (!pv->fid->fmt->ops->pv_commit) - return 1; - - /* Commit to each copy of the metadata area */ - list_iterate(mdah, &pv->fid->metadata_areas) { - mdl = list_item(mdah, struct metadata_area)->metadata_locn; - if (!pv->fid->fmt->ops->pv_commit(pv->fid, pv, mdl)) { - stack; - return 0; - } + if (!pv->fmt->ops->pv_write(pv->fmt, pv, mdas, label_sector)) { + stack; + return 0; } return 1; diff --git a/lib/metadata/metadata.h b/lib/metadata/metadata.h index 9908470fe..2c101048c 100644 --- a/lib/metadata/metadata.h +++ b/lib/metadata/metadata.h @@ -10,90 +10,126 @@ #ifndef _LVM_METADATA_H #define _LVM_METADATA_H -#include <sys/types.h> -#include <asm/page.h> +#include "ctype.h" #include "dev-cache.h" #include "list.h" #include "uuid.h" +#include <sys/types.h> +#include <asm/page.h> #define NAME_LEN 128 #define MAX_STRIPES 128 -#define SECTOR_SIZE 512 -#define STRIPE_SIZE_DEFAULT 16 /* 16KB */ -#define STRIPE_SIZE_MIN ( PAGE_SIZE/SECTOR_SIZE) /* PAGESIZE in sectors */ -#define STRIPE_SIZE_MAX ( 512L * 1024 / SECTOR_SIZE) /* 512 KB in sectors */ -#define PV_MIN_SIZE ( 512L * 1024 / SECTOR_SIZE) /* 512 KB in sectors */ -#define PE_ALIGN (65536UL / SECTOR_SIZE) /* PE alignment */ - +#define SECTOR_SHIFT 9L +#define SECTOR_SIZE ( 1L << SECTOR_SHIFT ) +#define STRIPE_SIZE_DEFAULT 16 /* 16KB */ +#define STRIPE_SIZE_MIN ( PAGE_SIZE >> SECTOR_SHIFT) /* PAGESIZE in sectors */ +#define STRIPE_SIZE_MAX ( 512L * 1024L >> SECTOR_SHIFT) /* 512 KB in sectors */ +#define PV_MIN_SIZE ( 512L * 1024L >> SECTOR_SHIFT) /* 512 KB in sectors */ +#define PE_ALIGN (65536UL >> SECTOR_SHIFT) /* PE alignment */ /* Various flags */ /* Note that the bits no longer necessarily correspond to LVM1 disk format */ -#define BIT(x) (1 << x) -#define EXPORTED_VG BIT(0) /* VG PV */ -#define RESIZEABLE_VG BIT(1) /* VG */ -#define PARTIAL_VG BIT(2) /* VG */ +#define PARTIAL_VG 0x00000001 /* VG */ +#define EXPORTED_VG 0x00000002 /* VG PV */ +#define RESIZEABLE_VG 0x00000004 /* VG */ -/* - * May any free extents on this PV be used or must they be left - * free? - */ -#define ALLOCATABLE_PV BIT(3) /* PV */ - -#define SPINDOWN_LV BIT(4) /* LV */ -#define BADBLOCK_ON BIT(5) /* LV */ -#define FIXED_MINOR BIT(6) /* LV */ -#define VISIBLE_LV BIT(7) /* LV */ - -/* - * FIXME: do we really set read/write for a whole vg ? - */ -#define LVM_READ BIT(8) /* LV VG */ -#define LVM_WRITE BIT(9) /* LV VG */ -#define CLUSTERED BIT(10) /* VG */ -#define SHARED BIT(11) /* VG */ +/* May any free extents on this PV be used or must they be left free? */ +#define ALLOCATABLE_PV 0x00000008 /* PV */ -#define FMT_SEGMENTS 0x00000001 /* Arbitrary segment parameters? */ +#define SPINDOWN_LV 0x00000010 /* LV */ +#define BADBLOCK_ON 0x00000020 /* LV */ +#define VISIBLE_LV 0x00000040 /* LV */ +#define FIXED_MINOR 0x00000080 /* LV */ +/* FIXME Remove when metadata restructuring is completed */ +#define SNAPSHOT 0x00001000 /* LV - temp internal use only */ -#define FMT_TEXT_NAME "text" -#define FMT_LVM1_NAME "lvm1" +#define LVM_READ 0x00000100 /* LV VG */ +#define LVM_WRITE 0x00000200 /* LV VG */ +#define CLUSTERED 0x00000400 /* VG */ +#define SHARED 0x00000800 /* VG */ +/* Format features flags */ +#define FMT_SEGMENTS 0x00000001 /* Arbitrary segment params? */ +#define FMT_MDAS 0x00000002 /* Proper metadata areas? */ typedef enum { + ALLOC_DEFAULT, ALLOC_NEXT_FREE, - ALLOC_STRICT, ALLOC_CONTIGUOUS - } alloc_policy_t; -struct physical_volume { - struct id id; - struct device *dev; - struct format_instance *fid; - char *vg_name; - - uint32_t status; - uint64_t size; - - /* physical extents */ - uint64_t pe_size; - uint64_t pe_start; - uint32_t pe_count; - uint32_t pe_alloc_count; -}; +typedef enum { + SEG_STRIPED, + SEG_SNAPSHOT, + SEG_MIRROR +} segment_type_t; struct cmd_context; +struct format_handler; +struct labeller; struct format_type { + struct list list; struct cmd_context *cmd; struct format_handler *ops; + struct labeller *labeller; const char *name; + const char *alias; uint32_t features; + void *library; void *private; }; +struct physical_volume { + struct id id; + struct device *dev; + struct format_type *fmt; + char *vg_name; + + uint32_t status; + uint64_t size; + + /* physical extents */ + uint64_t pe_size; + uint64_t pe_start; + uint32_t pe_count; + uint32_t pe_alloc_count; +}; + +struct metadata_area; +struct format_instance; + +/* Per-format per-metadata area operations */ +struct metadata_area_ops { + struct volume_group *(*vg_read) (struct format_instance * fi, + const char *vg_name, + struct metadata_area * mda); + /* + * Write out complete VG metadata. You must ensure internal + * consistency before calling. eg. PEs can't refer to PVs not + * part of the VG. + * + * It is also the responsibility of the caller to ensure external + * consistency, eg by calling pv_write() if removing PVs from + * a VG or calling vg_write() a second time if splitting a VG + * into two. + * + * vg_write() should not read or write from any PVs not included + * in the volume_group structure it is handed. + * (format1 currently breaks this rule.) + */ + int (*vg_write) (struct format_instance * fid, struct volume_group * vg, + struct metadata_area * mda); + int (*vg_commit) (struct format_instance * fid, + struct volume_group * vg, struct metadata_area * mda); + int (*vg_remove) (struct format_instance * fi, struct volume_group * vg, + struct metadata_area * mda); +}; + struct metadata_area { struct list list; + struct metadata_area_ops *ops; void *metadata_locn; }; @@ -105,27 +141,27 @@ struct format_instance { struct volume_group { struct cmd_context *cmd; struct format_instance *fid; - uint32_t seqno; /* Metadata sequence number */ + uint32_t seqno; /* Metadata sequence number */ struct id id; char *name; char *system_id; - uint32_t status; + uint32_t status; - uint32_t extent_size; - uint32_t extent_count; - uint32_t free_count; + uint32_t extent_size; + uint32_t extent_count; + uint32_t free_count; - uint32_t max_lv; - uint32_t max_pv; + uint32_t max_lv; + uint32_t max_pv; - /* physical volumes */ - uint32_t pv_count; + /* physical volumes */ + uint32_t pv_count; struct list pvs; - /* logical volumes */ - uint32_t lv_count; + /* logical volumes */ + uint32_t lv_count; struct list lvs; /* snapshots */ @@ -133,17 +169,23 @@ struct volume_group { struct list snapshots; }; -struct stripe_segment { +struct lv_segment { struct list list; - struct logical_volume *lv; + + segment_type_t type; uint32_t le; uint32_t len; + + /* FIXME Fields depend on segment type */ uint32_t stripe_size; uint32_t stripes; + struct logical_volume *origin; + struct logical_volume *cow; + uint32_t chunk_size; /* There will be one area for each stripe */ - struct { + struct { struct physical_volume *pv; uint32_t pe; } area[0]; @@ -151,22 +193,24 @@ struct stripe_segment { struct logical_volume { union lvid lvid; - char *name; + char *name; struct volume_group *vg; - uint32_t status; + uint32_t status; alloc_policy_t alloc; uint32_t read_ahead; int32_t minor; - uint64_t size; - uint32_t le_count; + uint64_t size; + uint32_t le_count; struct list segments; }; struct snapshot { + struct id id; + int persistent; /* boolean */ uint32_t chunk_size; /* in 512 byte sectors */ @@ -182,6 +226,7 @@ struct name_list { struct pv_list { struct list list; struct physical_volume *pv; + struct list *mdas; }; struct lv_list { @@ -195,133 +240,111 @@ struct snapshot_list { struct snapshot *snapshot; }; - +struct mda_list { + struct list list; + struct device_area mda; +}; /* * Ownership of objects passes to caller. */ struct format_handler { /* - * Returns a name_list of vg's. + * Scan any metadata areas that aren't referenced in PV labels */ - struct list *(*get_vgs)(struct format_type *fmt, struct list *names); - - /* - * Returns pv_list of fully-populated pv structures. - */ - struct list *(*get_pvs)(struct format_type *fmt, struct list *results); + int (*scan) (struct format_type * fmt); /* * Return PV with given path. */ - int (*pv_read)(struct format_type *fmt, - const char *pv_name, - struct physical_volume *pv); + int (*pv_read) (struct format_type * fmt, const char *pv_name, + struct physical_volume * pv, struct list * mdas); /* * Tweak an already filled out a pv ready for importing into a * vg. eg. pe_count is format specific. */ - int (*pv_setup)(struct format_instance *fi, struct physical_volume *pv, - struct volume_group *vg); + int (*pv_setup) (struct format_type * fmt, + uint64_t pe_start, uint32_t extent_count, + uint32_t extent_size, + int pvmetadatacopies, + uint64_t pvmetadatasize, struct list * mdas, + struct physical_volume * pv, struct volume_group * vg); /* * Write a PV structure to disk. Fails if the PV is in a VG ie * pv->vg_name must be null. */ - int (*pv_write)(struct format_instance *fi, struct physical_volume *pv, - void *mdl); - int (*pv_commit)(struct format_instance *fid, - struct physical_volume *pv, void *mdl); + int (*pv_write) (struct format_type * fmt, struct physical_volume * pv, + struct list * mdas, int64_t label_sector); /* * Tweak an already filled out a lv eg, check there * aren't too many extents. */ - int (*lv_setup)(struct format_instance *fi, struct logical_volume *lv); + int (*lv_setup) (struct format_instance * fi, + struct logical_volume * lv); /* * Tweak an already filled out vg. eg, max_pv is format * specific. */ - int (*vg_setup)(struct format_instance *fi, struct volume_group *vg); - int (*vg_remove)(struct format_instance *fi, struct volume_group *vg, - void *mdl); - - /* - * The name may be prefixed with the dev_dir from the - * job_context. - * mdl is the metadata location to use - */ - struct volume_group *(*vg_read)(struct format_instance *fi, - const char *vg_name, void *mdl); + int (*vg_setup) (struct format_instance * fi, struct volume_group * vg); /* - * Write out complete VG metadata. You must ensure internal - * consistency before calling. eg. PEs can't refer to PVs not - * part of the VG. - * - * It is also the responsibility of the caller to ensure external - * consistency, eg by calling pv_write() if removing PVs from - * a VG or calling vg_write() a second time if splitting a VG - * into two. - * - * vg_write() must not read or write from any PVs not included - * in the volume_group structure it is handed. Note: format1 - * does read all pv's currently. - */ - int (*vg_write)(struct format_instance *fid, struct volume_group *vg, - void *mdl); - - int (*vg_commit)(struct format_instance *fid, struct volume_group *vg, - void *mdl); - /* * Create format instance with a particular metadata area */ - struct format_instance *(*create_instance)(struct format_type *fmt, - const char *vgname, - void *context); + struct format_instance *(*create_instance) (struct format_type * fmt, + const char *vgname, + void *context); /* * Destructor for format instance */ - void (*destroy_instance)(struct format_instance *fid); + void (*destroy_instance) (struct format_instance * fid); /* * Destructor for format type */ - void (*destroy)(struct format_type *fmt); + void (*destroy) (struct format_type * fmt); }; /* * Utility functions */ int vg_write(struct volume_group *vg); -struct volume_group *vg_read(struct cmd_context *cmd, const char *vg_name); +struct volume_group *vg_read(struct cmd_context *cmd, const char *vg_name, + int *consistent); struct volume_group *vg_read_by_vgid(struct cmd_context *cmd, const char *vgid); -struct physical_volume *pv_read(struct cmd_context *cmd, const char *pv_name); +struct physical_volume *pv_read(struct cmd_context *cmd, const char *pv_name, + struct list *mdas, uint64_t *label_sector); struct list *get_pvs(struct cmd_context *cmd); -struct list *get_vgs(struct cmd_context *cmd); -int pv_write(struct cmd_context *cmd, struct physical_volume *pv); +/* Set full_scan to 1 to re-read every (filtered) device label */ +struct list *get_vgs(struct cmd_context *cmd, int full_scan); + +int pv_write(struct cmd_context *cmd, struct physical_volume *pv, + struct list *mdas, int64_t label_sector); -struct physical_volume *pv_create(struct format_instance *fi, - const char *name, +/* pe_start and pe_end relate to any existing data so that new metadata + * areas can avoid overlap */ +struct physical_volume *pv_create(struct format_type *fmt, + struct device *dev, struct id *id, - uint64_t size); + uint64_t size, + uint64_t pe_start, + uint32_t existing_extent_count, + uint32_t existing_extent_size, + int pvmetadatacopies, + uint64_t pvmetadatasize, struct list *mdas); struct volume_group *vg_create(struct cmd_context *cmd, const char *name, uint32_t extent_size, int max_pv, int max_lv, int pv_count, char **pv_names); int vg_remove(struct volume_group *vg); -/* - * This needs the format instance to check the - * pv's are orphaned. - */ -int vg_extend(struct format_instance *fi, - struct volume_group *vg, int pv_count, char **pv_names); - +int vg_extend(struct format_instance *fi, struct volume_group *vg, + int pv_count, char **pv_names); /* * Create a new LV within a given volume group. @@ -344,35 +367,33 @@ int lv_extend(struct format_instance *fi, struct logical_volume *lv, uint32_t stripes, uint32_t stripe_size, - uint32_t extents, - struct list *allocatable_pvs); + uint32_t extents, struct list *allocatable_pvs); /* lv must be part of vg->lvs */ int lv_remove(struct volume_group *vg, struct logical_volume *lv); - -/* FIXME: Move to other files */ -int id_eq(struct id *op1, struct id *op2); - /* Manipulate PV structures */ int pv_add(struct volume_group *vg, struct physical_volume *pv); int pv_remove(struct volume_group *vg, struct physical_volume *pv); -struct physical_volume *pv_find(struct volume_group *vg, - const char *pv_name); - +struct physical_volume *pv_find(struct volume_group *vg, const char *pv_name); /* Find a PV within a given VG */ struct pv_list *find_pv_in_vg(struct volume_group *vg, const char *pv_name); +struct physical_volume *find_pv_in_vg_by_uuid(struct volume_group *vg, + struct id *id); /* Find an LV within a given VG */ struct lv_list *find_lv_in_vg(struct volume_group *vg, const char *lv_name); -struct lv_list *find_lv_in_vg_by_lvid(struct volume_group *vg, +struct lv_list *find_lv_in_vg_by_lvid(struct volume_group *vg, union lvid *lvid); /* Return the VG that contains a given LV (based on path given in lv_name) */ /* or environment var */ struct volume_group *find_vg_with_lv(const char *lv_name); +/* Find LV with given lvid (used during activation) */ +struct logical_volume *lv_from_lvid(struct cmd_context *cmd, + const char *lvid_s); /* FIXME Merge these functions with ones above */ struct physical_volume *find_pv(struct volume_group *vg, struct device *dev); @@ -388,7 +409,6 @@ const char *strip_dir(const char *vg_name, const char *dir); */ int lv_check_segments(struct logical_volume *lv); - /* * Sometimes (eg, after an lvextend), it is possible to merge two * adjacent segments into a single segment. This function trys @@ -396,7 +416,6 @@ int lv_check_segments(struct logical_volume *lv); */ int lv_merge_segments(struct logical_volume *lv); - /* * Useful functions for managing snapshots. */ @@ -409,10 +428,30 @@ struct list *find_snapshots(struct logical_volume *lv); int vg_add_snapshot(struct logical_volume *origin, struct logical_volume *cow, - int persistent, - uint32_t chunk_size); + int persistent, struct id *id, uint32_t chunk_size); int vg_remove_snapshot(struct volume_group *vg, struct logical_volume *cow); +static inline int validate_vgname(const char *n) +{ + register char c; + register int len = 0; + + if (!n || !*n) + return 0; + + /* Hyphen used as VG-LV separator - ambiguity if LV starts with it */ + if (*n == '-') + return 0; + + while ((len++, c = *n++)) + if (!isalnum(c) && c != '.' && c != '_' && c != '-' && c != '+') + return 0; + + if (len > NAME_LEN) + return 0; + + return 1; +} #endif diff --git a/lib/metadata/pv_map.c b/lib/metadata/pv_map.c index a30019e8b..efde0848c 100644 --- a/lib/metadata/pv_map.c +++ b/lib/metadata/pv_map.c @@ -4,8 +4,8 @@ * This file is released under the LGPL. */ +#include "lib.h" #include "pv_map.h" -#include "log.h" #include "hash.h" #include <assert.h> @@ -73,7 +73,7 @@ static int _fill_bitsets(struct volume_group *vg, struct list *maps) struct pv_map *pvm; uint32_t s, pe; struct hash_table *hash; - struct stripe_segment *seg; + struct lv_segment *seg; int r = 0; if (!(hash = hash_create(128))) { @@ -95,7 +95,7 @@ static int _fill_bitsets(struct volume_group *vg, struct list *maps) lv = list_item(lvh, struct lv_list)->lv; list_iterate(segh, &lv->segments) { - seg = list_item(segh, struct stripe_segment); + seg = list_item(segh, struct lv_segment); for (s = 0; s < seg->stripes; s++) { for (pe = 0; pe < (seg->len / seg->stripes); @@ -142,7 +142,7 @@ static void _insert_area(struct list *head, struct pv_area *a) } static int _create_single_area(struct pool *mem, struct pv_map *pvm, - uint32_t * extent) + uint32_t *extent) { uint32_t e = *extent, b, count = pvm->pv->pe_count; struct pv_area *pva; diff --git a/lib/metadata/snapshot_manip.c b/lib/metadata/snapshot_manip.c index 06384ce85..23aeb18d8 100644 --- a/lib/metadata/snapshot_manip.c +++ b/lib/metadata/snapshot_manip.c @@ -4,7 +4,7 @@ * This file is released under the LGPL. */ -#include "log.h" +#include "lib.h" #include "metadata.h" #include "toolcontext.h" @@ -97,7 +97,7 @@ struct list *find_snapshots(struct logical_volume *lv) int vg_add_snapshot(struct logical_volume *origin, struct logical_volume *cow, - int persistent, uint32_t chunk_size) + int persistent, struct id *id, uint32_t chunk_size) { struct snapshot *s; struct snapshot_list *sl; @@ -121,12 +121,20 @@ int vg_add_snapshot(struct logical_volume *origin, s->origin = origin; s->cow = cow; + if (id) + s->id = *id; + else if (!id_create(&s->id)) { + log_error("Snapshot UUID creation failed"); + return 0; + } + if (!(sl = pool_alloc(mem, sizeof(*sl)))) { stack; pool_free(mem, s); return 0; } + cow->status &= ~VISIBLE_LV; sl->snapshot = s; list_add(&origin->vg->snapshots, &sl->list); diff --git a/make.tmpl.in b/make.tmpl.in index 21988cba3..e388b6989 100644 --- a/make.tmpl.in +++ b/make.tmpl.in @@ -48,7 +48,7 @@ MAKEFLAGS = @JOBS@ endif SUFFIXES= -SUFFIXES=.c .d .o +SUFFIXES=.c .d .o .so CFLAGS+=-Wall #CFLAGS+=-O2 @@ -57,10 +57,13 @@ CFLAGS+=-g -fno-omit-frame-pointer #CFLAGS+=-pg #LD_FLAGS=-pg -CFLAGS+=-D_REENTRANT -DDEBUG_MEM -DDEBUG -D_GNU_SOURCE +CFLAGS+=-DDEBUG_MEM -DDEBUG #CFLAGS+=-DDEBUG_POOL #CFLAGS+=-DBOUNDS_CHECK +LIB_VERSION := $(shell cat $(top_srcdir)/VERSION | \ + awk -F '.' '{printf "%s.%s",$$1,$$2}') + INCLUDES+=-I. -I$(top_srcdir)/include INC_LNS=$(top_srcdir)/include/.symlinks_created @@ -81,6 +84,10 @@ ifeq ("@HAVE_RL_COMPLETION_MATCHES@", "yes") CFLAGS += -DHAVE_RL_COMPLETION_MATCHES endif +ifeq ("@LVM1@", "internal") + CFLAGS += -DLVM1_INTERNAL +endif + OBJECTS=$(SOURCES:%.c=%.o) SUBDIRS.install := $(SUBDIRS:=.install) @@ -101,14 +108,21 @@ $(SUBDIRS.install): $(MAKE) -C $(@:.install=) install $(SUBDIRS.clean): - $(MAKE) -C $(@:.clean=) clean + -$(MAKE) -C $(@:.clean=) clean $(SUBDIRS.distclean): - $(MAKE) -C $(@:.distclean=) distclean + -$(MAKE) -C $(@:.distclean=) distclean %.o: %.c $(CC) -c $(INCLUDES) $(CFLAGS) $< -o $@ +%.so: %.o + $(CC) -c $(INCLUDES) $(CFLAGS) %< -o $@ + +%.so: $(OBJECTS) + $(CC) -shared -Wl,-soname,$(notdir $@).$(LIB_VERSION) \ + -Wl,--version-script,.export.sym $(OBJECTS) -o $@ + %.d: %.c set -e; FILE=`echo $@ | sed 's/\\//\\\\\\//g;s/\\.d//g'`; \ DEPS=`echo $(DEPS) | sed -e 's/\\//\\\\\\//g'`; \ diff --git a/man/Makefile.in b/man/Makefile.in index bd114b1c1..0b688cb48 100644 --- a/man/Makefile.in +++ b/man/Makefile.in @@ -23,8 +23,9 @@ VPATH = @srcdir@ MAN5=lvm.conf.5 MAN8=lvchange.8 lvcreate.8 lvdisplay.8 lvextend.8 lvm.8 lvmchange.8 \ lvreduce.8 lvremove.8 lvrename.8 lvscan.8 pvchange.8 \ - pvcreate.8 pvdisplay.8 pvscan.8 vgcfgbackup.8 vgchange.8 vgck.8 \ - vgcreate.8 vgdisplay.8 vgextend.8 vgmerge.8 vgreduce.8 vgremove.8 \ + pvcreate.8 pvdisplay.8 pvremove.8 pvscan.8 vgcfgbackup.8 \ + vgcfgrestore.8 vgchange.8 vgck.8 vgcreate.8 \ + vgconvert.8 vgdisplay.8 vgextend.8 vgmerge.8 vgreduce.8 vgremove.8 \ vgrename.8 vgscan.8 MAN5DIR=${mandir}/man5 MAN8DIR=${mandir}/man8 diff --git a/man/lvchange.8 b/man/lvchange.8 index 0b5fe2c10..f5fa1a42a 100644 --- a/man/lvchange.8 +++ b/man/lvchange.8 @@ -5,18 +5,17 @@ lvchange \- change attributes of a logical volume .B lvchange [\-A/\-\-autobackup y/n] [\-a/\-\-available y/n] [\-C/\-\-contiguous y/n] [\-d/\-\-debug] [\-h/\-?/\-\-help] +[\-\-ignorelockingfailure] +[\-M/\-\-persistent y/n] [\-\-minor minor] +[\-P/\-\-partial y/n] [\-p/\-\-permission r/w] [\-r/\-\-readahead ReadAheadSectors] +[\-t/\-\-test] [\-v/\-\-verbose] LogicalVolumePath [LogicalVolumePath...] .SH DESCRIPTION lvchange allows you to change the attributes of a logical volume. .SH OPTIONS See \fBlvm\fP for common options. .TP -.I \-A, \-\-autobackup y/n -Controls automatic backup of VG metadata after the change ( see -.B vgcfgbackup(8) -). Default is yes. -.TP .I \-a, \-\-available y/n Controls the availability of the logical volumes for use. This is (among others) useful for changing the logical volume's name @@ -25,18 +24,25 @@ This is (among others) useful for changing the logical volume's name ) safely. .TP .I \-C, \-\-contiguous y/n -Tries to set or resets the contiguous allocation policy for +Tries to set or reset the contiguous allocation policy for logical volumes. It's only possible to change a non-contiguous logical volume's allocation policy to contiguous, if all of the allocated physical extents are already contiguous. .TP +.I \-\-minor minor +Set the minor number. +.TP +.I \-M, \-\-persistent y/n +Set to y to make the minor number specified persistent. +.TP .I \-p, \-\-permission r/w Change access permission to read-only or read/write. .TP .I \-r, \-\-readahead ReadAheadSectors Change read ahead sector count per logical between 2 and 120. +Not used by device-mapper. .SH Examples -"lvchange -x n /dev/vg00/lvol1" prevents the allocation of any physical +"lvchange -x n vg00/lvol1" prevents the allocation of any physical extents on logical volume lvol1 in volume group vg00. .SH SEE ALSO .BR lvm (8), diff --git a/man/lvcreate.8 b/man/lvcreate.8 index da16d1702..ed4ab82c3 100644 --- a/man/lvcreate.8 +++ b/man/lvcreate.8 @@ -8,8 +8,10 @@ lvcreate \- create a logical volume in an existing volume group [\-i/\-\-stripes Stripes [\-I/\-\-stripesize StripeSize]] {\-l/\-\-extents LogicalExtentsNumber | \-L/\-\-size LogicalVolumeSize[kKmMgGtT]} +[\-M/\-\-persistent y/n] [\-\-minor minor] [\-n/\-\-name LogicalVolumeName] [\-p/\-\-permission r/rw] [\-r/\-\-readahead ReadAheadSectors] +[\-t/\-\-test] [\-v/\-\-verbose] [\-Z/\-\-zero y/n] VolumeGroupName [PhysicalVolumePath...] .br @@ -37,13 +39,6 @@ keep the contents of the original logical volume for backup purposes. .SH OPTIONS See \fBlvm\fP for common options. .TP -.I \-A, \-\-autobackup y/n -Controls automatic backup of VG metadata after the change ( see -.B vgcfgbackup(8) -). -.br -Default is yes. -.TP .I \-c, \-\-chunksize ChunkSize Power of 2 chunk size for the snapshot logical volume between 4k and 1024k. .TP @@ -73,6 +68,12 @@ G for gigabytes or T for terabytes is optional. .br Default unit is megabytes. .TP +.I \-\-minor minor +Set the minor number. +.TP +.I \-M, \-\-persistent y/n +Set to y to make the minor number specified persistent. +.TP .I \-n, \-\-name LogicalVolumeName The name for the new logical volume. .br @@ -86,6 +87,7 @@ Default is read and write. .TP .I \-r, \-\-readahead ReadAheadSectors Set read ahead sector count of this logical volume to a value between 2 and 120. +Ignored by device-mapper. .TP .I \-s, \-\-snapshot Create a snapshot logical volume (or snapshot) for an existing, so called @@ -122,7 +124,7 @@ contents of the original logical volume named /dev/vg00/lvol1 at snapshot logical volume creation time. If the original logical volume contains a file system, you can mount the snapshot logical volume on an arbitrary directory in order to access the contents of the filesystem to run -a backup while the original filesystem is updated. +a backup while the original filesystem continues to get updated. .SH SEE ALSO .BR lvm (8), diff --git a/man/lvdisplay.8 b/man/lvdisplay.8 index 0a9f68af8..401d3de1d 100644 --- a/man/lvdisplay.8 +++ b/man/lvdisplay.8 @@ -3,8 +3,10 @@ lvdisplay \- display attributes of a logical volume .SH SYNOPSIS .B lvdisplay -[\-c/\-\-colon] [\-d/\-\-debug] [\-D/\-\-disk] [\-h/\-?/\-\-help] -[\-v[v]/\-\-verbose] LogicalVolumePath [LogicalVolumePath...] +[\-c/\-\-colon] [\-d/\-\-debug] [\-h/\-?/\-\-help] +[\-\-ignorelockingfailure] +[\-\-maps] [\-P/\-\-partial] +[\-v/\-\-verbose] LogicalVolumePath [LogicalVolumePath...] .SH DESCRIPTION lvdisplay allows you to see the attributes of a logical volume like size, read/write status, snapshot information etc. @@ -12,7 +14,8 @@ like size, read/write status, snapshot information etc. See \fBlvm\fP for common options. .TP .I \-c, \-\-colon -Generate colon seperated output for easier parsing in scripts or programs. +Deprecated. To be replaced with a more-powerful reporting tool. +Generate colon separated output for easier parsing in scripts or programs. .nf The values are: @@ -33,11 +36,6 @@ The values are: .fi .TP -.I \-D, \-\-disk -Show attributes of the volume group descriptor array on disk(s). -Without this switch they are derived from kernel space. -Useful, if the volume group isn't active. -.TP .I \-m, \-\-maps Display the mapping of logical extents to physical volumes and physical extents. diff --git a/man/lvextend.8 b/man/lvextend.8 index 98c379be4..f9bb73aa5 100644 --- a/man/lvextend.8 +++ b/man/lvextend.8 @@ -4,8 +4,10 @@ lvextend \- extend the size of a logical volume .SH SYNOPSIS .B lvextend [\-A/\-\-autobackup y/n] [\-d/\-\-debug] [\-h/\-?/\-\-help] +[\-i/\-\-stripes Stripes [\-I/\-\-stripesize StripeSize]] {\-l/\-\-extents [+]LogicalExtentsNumber | \-L/\-\-size [+]LogicalVolumeSize[kKmMgGtT]} +[\-t/\-\-test] [\-v/\-\-verbose] LogicalVolumePath [PhysicalVolumePath...] .SH DESCRIPTION lvextend allows you to extend the size of a logical volume. @@ -15,11 +17,6 @@ for information to create snapshots) is supprted as well. .SH OPTIONS See \fBlvm\fP for common options. .TP -.I \-A, \-\-autobackup y/n -Controls automatic backup of VG metadata after the change ( see -.B vgcfgbackup(8) -). Default is yes. -.TP .I \-l, \-\-extents [+]LogicalExtentsNumber Extend or set the logical volume size in units of logical extents. With the + sign the value is added to the actual size @@ -30,6 +27,18 @@ Extend or set the logical volume size in units in units of megabytes. A size suffix of M for megabytes, G for gigabytes or T for terabytes is optional. With the + sign the value is added to the actual size of the logical volume and without it, the value is taken as an absolute one. +.TP +.I \-i, \-\-stripes Stripes +Gives the number of stripes for the extension. +Not applicable to PVs using the original metadata LVM format. +This is equal to the number of physical volumes to scatter +the logical volume. +.TP +.I \-I, \-\-stripesize StripeSize +Gives the number of kilobytes for the granularity of the stripes. +Not applicable to PVs using the original metadata LVM format. +.br +StripeSize must be 2^n (n = 2 to 9) .SH Examples "lvextend -L +54 /dev/vg01/lvol10 /dev/sdk3" tries to extend the size of that logical volume by 54MB on physical volume /dev/sdk3. @@ -25,26 +25,61 @@ A file containing a simple script with one command per line can also be given on the command line. The script can also be executed directly if the first line is #! followed by the absolute path of \fBlvm\fP. +.LP +Where commands take VG or LV names as arguments, the full path name is +optional. An LV called "lvol0" in a VG called "vg0" can be specified +as "vg0/lvol0". If a list of VGs is required but is left empty, a list of +all VGs will be substituted. If a list of LVs is required +but a VG is given, a list of all the LVs in that VG will be substituted. +So "lvdisplay vg0" will display all the LVs in "vg0". .SH OPTIONS -The following options can be used with every command and are not documented -on individual manual pages. +The following options are available for many of the commands and are +not documented on individual manual pages. .TP \fB-h | --help\fP \(em Display the help text. .TP -\fB--version\fP \(em Display the version. -Not implemented yet. +\fB--version\fP \(em Display version information. .TP \fB-v | --verbose\fP \(em Set verbose level. -Repeat from 1 to 3 times to determine the detail of messages +Repeat from 1 to 3 times to increase the detail of messages sent to stdout and stderr. Overrides config file setting. .TP \fB-d | --debug\fP \(em Set debug level. -Repeat from 1 to 6 times to determine the detail of messages sent +Repeat from 1 to 6 times to increase the detail of messages sent to the log file and/or syslog (if configured). Overrides config file setting. .TP \fB--quiet\fP \(em Suppress output and log messages. Overrides -d and -v. +.TP +\fB-t | --test\fP \(em Run in test mode. +Commands will not update metadata. +.TP +\fB--driverloaded\fP { \fBy\fP | \fBn\fP } +Whether or not the device-mapper kernel driver is loaded. +If you set this to \fBn\fP, no attempt will be made to contact the driver. +.TP +\fB-A | --autobackup\fP { \fBy\fP | \fBn\fP } +Whether or not to metadata should be backed up automatically after a change. +You are strongly advised not to disable this! +See +.B vgcfgbackup (8). +.TP +\fB-P | --partial\fP +When set, the tools will do their best to provide access to volume groups +that are only partially available. Where part of a logical volume is +missing, \fB/dev/ioerror\fP will be substituted, and you could use +\fBdmsetup (8)\fP to set this up to return I/O errors when accessed, +or create it as a large block device of nulls. Metadata may not be +changed with this option. To insert a replacement physical volume +of the same or large size use \fBpvcreate -u\fP to set the uuid to +match the original followed by \fBvgcfgrestore (8)\fP. +.TP +\fB--ignorelockingfailure\fP +This lets you proceed with read-only metadata operations such as +\fBlvchange -ay\fP and \fBvgchange -ay\fP even if the locking module fails. +One use for this is in a system init script if the lock directory +is mounted read-only when the script runs. .SH ENVIRONMENT VARIABLES .TP \fBLVM_SYSTEM_DIR\fP @@ -53,14 +88,9 @@ system files. Defaults to "/etc/lvm". .TP \fBHOME\fP -Directory containing .lvm_history if the internal shell +Directory containing .lvm_history if the internal readline shell is invoked. .TP -\fBLVM_AUTOBACKUP\fP -Set to "no" to disable automatic metadata -backups and archiving. Not recommended. -Defaults to "yes". -.TP \fBLVM_VG_NAME\fP The volume group name that is assumed for any reference to a logical volume that doesn't specify a path. diff --git a/man/lvmchange.8 b/man/lvmchange.8 index 3bc3a72ad..ef18c6b80 100644 --- a/man/lvmchange.8 +++ b/man/lvmchange.8 @@ -4,6 +4,7 @@ lvmchange \- change attributes of the logical volume manager .SH SYNOPSIS .B lvmchange .SH DESCRIPTION -lvmchange is not currently supported under LVM2 +lvmchange is not currently supported under LVM2, although +\fBdmsetup (8)\fP has a \fBremove_all\fP command. .SH SEE ALSO .BR dmsetup (8) diff --git a/man/lvreduce.8 b/man/lvreduce.8 index cc288f377..d9ce55e59 100644 --- a/man/lvreduce.8 +++ b/man/lvreduce.8 @@ -6,7 +6,8 @@ lvreduce \- reduce the size of a logical volume [\-A/\-\-autobackup y/n] [\-d/\-\-debug] [\-f/\-\-force] [\-h/\-?/\-\-help] {\-l/\-\-extents [\-]LogicalExtentsNumber | \-L/\-\-size [\-]LogicalVolumeSize[kKmMgGtT]} -[\-v/\-\-verbose] LogicalVolumePath +[\-t/\-\-test] +[\-v/\-\-verbose] LogicalVolume[Path] .SH DESCRIPTION lvreduce allows you to reduce the size of a logical volume. Be careful when reducing a logical volume's size, because data in the @@ -26,11 +27,6 @@ for information to create snapshots) is supported as well. .SH OPTIONS See \fBlvm\fP for common options. .TP -.I \-A, \-\-autobackup y/n -Controls automatic backup of VG metadata after the change ( see -.B vgcfgbackup(8) -). Default is yes. -.TP .I \-f, \-\-force Force size reduction without any question. .TP diff --git a/man/lvremove.8 b/man/lvremove.8 index 9e89c92b4..69f693b84 100644 --- a/man/lvremove.8 +++ b/man/lvremove.8 @@ -4,17 +4,14 @@ lvremove \- remove a logical volume .SH SYNOPSIS .B lvremove [\-A/\-\-autobackup y/n] [\-d/\-\-debug] [\-f/\-\-force] -[\-h/\-?/\-\-help] [\-v/\-\-verbose] LogicalVolumePath [LogicalVolumePath...] +[\-h/\-?/\-\-help] +[\-t/\-\-test] +[\-v/\-\-verbose] LogicalVolumePath [LogicalVolumePath...] .SH DESCRIPTION lvremove allows you to remove one or more inactive logical volumes. .SH OPTIONS See \fBlvm\fP for common options. .TP -.I \-A, \-\-autobackup y/n -Controls automatic backup of VG metadata after the change ( see -.B vgcfgbackup(8) -). Default is yes. -.TP .I \-f, \-\-force Force remove without confirmation. .SH Example diff --git a/man/lvrename.8 b/man/lvrename.8 index 470414132..11b4f24f6 100644 --- a/man/lvrename.8 +++ b/man/lvrename.8 @@ -5,7 +5,9 @@ lvrename \- rename a logical volume .B lvrename .RB [ \-A | \-\-autobackup " {" y | n }] .RB [ \-d | \-\-debug ] +.RB [ \-f | \-\-force ] .RB [ \-h | \-\-help ] +.RB [ \-t | \-\-test ] .RB [ \-v | \-\-verbose ] .RB [ \-\-version ] .TP @@ -20,11 +22,6 @@ to .IR NewLogicalVolume { Name | Path }. .SH OPTIONS See \fBlvm\fP for common options. -.TP -.BR \-A ", " \-\-autobackup " {" y | n } -Controls automatic backup of VG metadata after the change (see -.BR vgcfgbackup (8)). -Default is yes. .SH EXAMPLE To rename .B lvold diff --git a/man/lvscan.8 b/man/lvscan.8 index 39859551b..04b2b8732 100644 --- a/man/lvscan.8 +++ b/man/lvscan.8 @@ -5,8 +5,9 @@ lvscan \- scan (all disks) for logical volumes .B lvscan .RB [ \-b | \-\-blockdevice ] .RB [ \-d | \-\-debug ] -.RB [ \-D | \-\-disk ] .RB [ \-h | \-\-help ] +.RB [ \-\-ignorelockingfailure ] +.RB [ \-P | \-\-partial ] .RB [ \-v | \-\-verbose ] .SH DESCRIPTION .B lvscan @@ -18,10 +19,6 @@ See \fBlvm\fP for common options. .BR \-b ", " \-\-blockdevice Adds the device major and minor numbers to the display of each logical volume. -.TP -.BR \-D ", " \-\-disk -Scan for logical volumes on disk(s) instead of getting the information -from the kernel. .SH SEE ALSO .BR lvm (8), .BR lvcreate (8), diff --git a/man/pvchange.8 b/man/pvchange.8 index 41224d6e7..e18cb8903 100644 --- a/man/pvchange.8 +++ b/man/pvchange.8 @@ -4,6 +4,7 @@ pvchange \- change attributes of a physical volume .SH SYNOPSIS .B pvchange [\-A/\-\-autobackup y/n] [\-d/\-\-debug] [\-h/\-?/\-\-help] +[\-t/\-\-test] [\-v/\-\-verbose] [\-a/\-\-all] [\-x/\-\-allocatable y/n] [PhysicalVolumePath...] .SH DESCRIPTION pvchange allows you to change the allocation permissions of one or @@ -11,11 +12,6 @@ more physical volumes. .SH OPTIONS See \fBlvm\fP for common options. .TP -.I \-A, \-\-autobackup y/n -Controls automatic backup of VG metadata after the change ( see -.B vgcfgbackup(8) -). Default is yes. -.TP .I \-a, \-\-all If PhysicalVolumePath is not specified on the command line all physical volumes are searched for and used. diff --git a/man/pvcreate.8 b/man/pvcreate.8 index 681e3978c..5bec86fd8 100644 --- a/man/pvcreate.8 +++ b/man/pvcreate.8 @@ -7,8 +7,15 @@ pvcreate \- initialize a disk or partition for use by LVM .RB [ \-f [ f ]| \-\-force " [" \-\-force ]] .RB [ \-y | \-\-yes ] .RB [ \-h | \-\-help ] +.RB [ \-t | \-\-test ] .RB [ \-v | \-\-verbose ] -.RB [ \-V | \-\-version ] +.RB [ \-\-labelsector ] +.RB [ \-M | \-\-metadatatype type ] +.RB [ \-\-metadatacopies #copies ] +.RB [ \-\-metadatasize size ] +.RB [ \-\-restorefile file ] +.RB [ \-\-setphysicalvolumesize size ] +.RB [ \-\-version ] .IR PhysicalVolume " [" PhysicalVolume ...] .SH DESCRIPTION .B pvcreate @@ -17,7 +24,7 @@ initializes for later use by the Logical Volume Manager (LVM). Each .I PhysicalVolume can be a disk partition, whole disk, meta device, or loopback file. -For DOS disk partitions, the partition id must be set to 0x8e using +For DOS disk partitions, the partition id should be set to 0x8e using .BR fdisk "(8), " cfdisk "(8), " or a equivalent. For .B whole disk devices only @@ -47,11 +54,61 @@ In an emergency you can override this behaviour with -ff. Specify the uuid for the device. Without this option, \fBpvcreate\fP generates a random uuid. All of your physical volumes must have unique uuids. -You need to use this option to restore a backup of LVM metadata onto -a replacement device - see \fBvgcfgrestore\fP(8). +You need to use this option before restoring a backup of LVM metadata +onto a replacement device - see \fBvgcfgrestore\fP(8). .TP .BR \-y ", " \-\-yes Answer yes to all questions. +.SH NEW METADATA OPTIONS +LVM2 introduces a new format for storing metadata on disk. +This new format is more efficient and resilient than the format the +original version of LVM used and offers the advanced user greater +flexibility and control. +.sp +The new format may be selected on the command line with \fB-M2\fP or by +setting \fBformat = "lvm2"\fP in the \fBglobal\fP section of \fBlvm.conf\fP. +Each physical volume in the same volume group must use the same format, but +different volume groups on a machine may use different formats +simultaneously: the tools can handle both formats. +Additional formats can be added as shared libraries. +.sp +Additional tools for manipulating the locations and sizes of metadata areas +will be written in due course. Use the verbose/debug options on the tools +to see where the metadata areas are placed. +.TP +.BR \-\-metadatasize " size" +The approximate amount of space to be set aside for each metadata area. +(The size you specify may get rounded.) +.TP +.BR \-\-metadatacopies " copies" +The number of metadata areas to set aside on each PV. Currently +this can be 0, 1 or 2. +If set to 2 (the default), two copies of the volume group metadata +are held on the PV, one at the front of the PV and one at the end. +If set to 1, one copy is kept at the front of the PV (starting in the +5th sector). +If set to 0, no copies are kept on this PV - you might wish to use this +with VGs containing large numbers of PVs. But if you do this and +then later use \fBvgsplit\fP you must ensure that each VG is still going +to have a suitable number of copies of the metadata after the split! +.TP +.BR \-\-restorefile " file" +In conjunction with \fB--uuid\fP, this extracts the location and size +of the data on the PV from the file (produced by \fBvgcfgbackup\fP) +and ensures that the metadata that the program produces is consistent +with the contents of the file i.e. the physical extents will be in +the same place and not get overwritten by new metadata. This provides +a mechanism to upgrade the metadata format or to add/remove metadata +areas. Use with care. See also \fBvgconvert\fP(8). +.TP +.BR \-\-labelsector " sector" +By default the PV is labelled with an LVM2 identifier in its second +sector (sector 1). This lets you use a different sector near the +start of the disk (between 0 and 3 inclusive - see LABEL_SCAN_SECTORS +in the source). Use with care. +.TP +.BR \-\-setphysicalvolumesize " size" +Overrides the automatically-detected size of the PV. Use with care. .SH Example Initialize partition #4 on the third SCSI disk and the entire fifth SCSI disk for later use by LVM: @@ -61,4 +118,4 @@ SCSI disk for later use by LVM: .SH SEE ALSO .BR lvm "(8), " vgcreate "(8), " vgextend "(8), " lvcreate "(8), " .BR cfdisk "(8), " fdisk "(8), " losetup "(8), " mdadd "(8), " -.BR vgcfgrestore "(8)" +.BR vgcfgrestore "(8), " vgconvert "(8)" diff --git a/man/pvdisplay.8 b/man/pvdisplay.8 index 840a4f9aa..b1d0a0899 100644 --- a/man/pvdisplay.8 +++ b/man/pvdisplay.8 @@ -22,7 +22,7 @@ The values are: * physical volume device name * volume group name * physical volume size in kilobytes -* internal physical volume number +* internal physical volume number (obsolete) * physical volume status * physical volume (not) allocatable * current number of logical volumes on this physical volume diff --git a/man/pvremove.8 b/man/pvremove.8 new file mode 100644 index 000000000..7dcf42345 --- /dev/null +++ b/man/pvremove.8 @@ -0,0 +1,22 @@ +.TH PVREMOVE 8 "LVM TOOLS" "Sistina Software UK" \" -*- nroff -*- +.SH NAME +pvremove \- remove a physical volume +.SH SYNOPSIS +.B pvremove +.RB [ \-d | \-\-debug] +.RB [ \-f [ f ]| \-\-force " [" \-\-force ]] +.RB [\-h | \-\-help] +.RB [ \-t | \-\-test ] +.RB [ \-v [ v ]| \-\-verbose " [" \-\-verbose ]] +.RB [ \-y | \-\-yes ] +.IR PhysicalVolume " [" PhysicalVolume ...] +.SH DESCRIPTION +.B pvremove +wipes the label on a device so that LVM will no longer recognise it +as a physical volume. +.SH OPTIONS +See \fBlvm\fP for common options. +.SH SEE ALSO +.BR lvm (8), +.BR pvcreate (8), +.BR pvdisplay (8) diff --git a/man/pvscan.8 b/man/pvscan.8 index 747368572..b8440c8f1 100644 --- a/man/pvscan.8 +++ b/man/pvscan.8 @@ -6,6 +6,7 @@ pvscan \- scan all disks for physical volumes .RB [ \-d | \-\-debug] .RB [\-e | \-\-exported] .RB [\-h | \-\-help] +.RB [\-\-ignorelockingfailure] .RB [ \-n | \-\-novolumegroup] .RB [\-s | \-\-short] .RB [\-u | \-\-uuid] @@ -26,7 +27,7 @@ Only show physical volumes not belonging to any volume group. Short listing format. .TP .BR \-u ", " \-\-uuid -Show UUIDs (Unifrom Unique Identifiers) in addition to device special names. +Show UUIDs (Uniform Unique Identifiers) in addition to device special names. .SH SEE ALSO .BR lvm (8), .BR pvcreate (8), diff --git a/man/vgcfgbackup.8 b/man/vgcfgbackup.8 index f6b340fe4..85f473955 100644 --- a/man/vgcfgbackup.8 +++ b/man/vgcfgbackup.8 @@ -4,17 +4,19 @@ vgcfgbackup \- backup volume group descriptor area .SH SYNOPSIS .B vgcfgbackup .RB [ \-d | \-\-debug ] +.RB [ \-f | \-\-file " filename" ] .RB [ \-h | \-\-help ] +.RB [ \-\-ignorelockingfailure ] +.RB [ \-P | \-\-partial ] .RB [ \-v | \-\-verbose ] .RI [ VolumeGroupName ...] .SH DESCRIPTION .B vgcfgbackup allows you to backup the metadata -of one to all volume groups to files in -.IR /etc/lvm . -If you don't give any volume groups in the command line, all of them +of your volume groups. +If you don't name any volume groups pn the command line, all of them will be backed up. This DOESN'T backup user/system data in logical -volume(s)! +volume(s)! Backup /etc/lvm regularly too. .SH OPTIONS See \fBlvm\fP for common options. .SH SEE ALSO diff --git a/man/vgcfgrestore.8 b/man/vgcfgrestore.8 new file mode 100644 index 000000000..6ce8e6cf6 --- /dev/null +++ b/man/vgcfgrestore.8 @@ -0,0 +1,23 @@ +.TH VGCFGRESTORE 8 "LVM TOOLS" "Sistina Software UK" \" -*- nroff -*- +.SH NAME +vgcfgrestore \- restore volume group descriptor area +.SH SYNOPSIS +.B vgcfgrestore +.RB [ \-d | \-\-debug ] +.RB [ \-f | \-\-file " filename" ] +.RB [ \-l[l] | \-\-list [--list] ] +.RB [ \-h | \-\-help ] +.RB [ \-M | \-\-Metadatatype 1|2] +.RB [ \-n | \-\-name " VolumeGroupName" ] +.RB [ \-t | \-\-test ] +.RB [ \-v | \-\-verbose ] +.RI [ VolumeGroupName ...] +.SH DESCRIPTION +.B vgcfgrestore +allows you to restore the metadata +of your volume groups from a text backup file produced by \fBvgcfgbackup\fP. +.SH OPTIONS +See \fBlvm\fP for common options. +.SH SEE ALSO +.BR lvm (8), +.BR vgcreate (8) diff --git a/man/vgchange.8 b/man/vgchange.8 index 54f6b5c1b..e48efd21e 100644 --- a/man/vgchange.8 +++ b/man/vgchange.8 @@ -3,14 +3,17 @@ vgchange \- change attributes of a volume group .SH SYNOPSIS .B vgchange -.RB [ \-A | \-\-autobackup " {" y | n }] +.RB [\-A | \-\-autobackup " {" y | n }] .RB [\-a | \-\-available " {" y | n }] .RB [\-d | \-\-debug] -.RB [ \-h | \-\-help] +.RB [\-h | \-\-help] +.RB [\-\-ignorelockingfailure] .RB [\-l | \-\-logicalvolume .IR MaxLogicalVolumes ] -.RB [ \-v | \-\-verbose ] -.RB [ \-\-version ] +.RB [\-P | \-\-partial] +.RB [-t | \-\-test] +.RB [\-v | \-\-verbose] +.RB [\-\-version ] .RB [\-x | \-\-resizeable " {" y | n }] .RI [ VolumeGroupName ...] .SH DESCRIPTION @@ -20,12 +23,13 @@ Its main purpose is to activate and deactivate .IR VolumeGroupName , or all volume groups if none is specified. Only active volume groups are subject to changes and allow access to their logical volumes. -During volume group activation, if +[Not yet implemented: During volume group activation, if .B vgchange recognizes snapshot logical volumes which were dropped because they ran out of space, it displays a message informing the administrator that such snapshots should be removed (see .BR lvremove (8)). +] .SH OPTIONS See \fBlvm\fP for common options. .TP diff --git a/man/vgconvert.8 b/man/vgconvert.8 new file mode 100644 index 000000000..4de0da436 --- /dev/null +++ b/man/vgconvert.8 @@ -0,0 +1,38 @@ +.TH VGCONVERT 8 "LVM TOOLS" "Sistina Software UK" \" -*- nroff -*- +.SH NAME +vgconvert \- convert volume group metadata format +.SH SYNOPSIS +.B vgconvert +.RB [ \-d | \-\-debug ] +.RB [ \-h | \-\-help ] +.RB [ \-t | \-\-test ] +.RB [ \-v | \-\-verbose ] +.RB [ \-\-labelsector ] +.RB [ \-M | \-\-metadatatype type ] +.RB [ \-\-metadatacopies #copies ] +.RB [ \-\-metadatasize size ] +.RB [ \-\-version ] +.IR VolumeGroupName " [" VolumeGroupName ...] +.SH DESCRIPTION +.B vgconvert +converts +.I VolumeGroupName +metadata from one format to another provided that the metadata +fits into the same space. +.SH OPTIONS +See \fBlvm\fP(8) and \fBpvcreate\fP(8) for options. +.SH EXAMPLE +Convert volume group vg1 from LVM1 metadata format to the new LVM2 +metadata format. +.sp +.B vgconvert -M2 vg1 +.SH RECOVERY +Use \fBpvscan\fP(8) to see which PVs lost their metadata. +Run \fBpvcreate\fP(8) with the --uuid and --restorefile options on each +such PV to reformat it as it was, using the archive file that +\fBvgconvert\fP(8) created at the start of the procedure. +Finally run \fBvgcfgrestore\fP(8) with that archive file to restore +the original metadata. +.SH SEE ALSO +.BR lvm "(8), " pvcreate "(8)," +.BR vgcfgrestore "(8)" diff --git a/man/vgcreate.8 b/man/vgcreate.8 index 01241ea7f..6916bebf3 100644 --- a/man/vgcreate.8 +++ b/man/vgcreate.8 @@ -8,10 +8,12 @@ vgcreate \- create a volume group .RB [ \-h | \-\-help ] .RB [ \-l | \-\-maxlogicalvolumes .IR MaxLogicalVolumes ] +.RB [ -M | \-\-metadatatype type] .RB [ -p | \-\-maxphysicalvolumes .IR MaxPhysicalVolumes ] .RB [ \-s | \-\-physicalextentsize .IR PhysicalExtentSize [ \fBkKmMgGtT\fR ]] +.RB [ \-t | \-\-test ] .RB [ \-v | \-\-verbose ] .RB [ \-\-version ] .I VolumeGroupName PhysicalVolumePath diff --git a/man/vgdisplay.8 b/man/vgdisplay.8 index a4849408f..4f958bb90 100644 --- a/man/vgdisplay.8 +++ b/man/vgdisplay.8 @@ -6,8 +6,9 @@ vgdisplay \- display attributes of volume groups .RB [ \-A | \-\-activevolumegroups ] .RB [ \-c | \-\-colon ] .RB [ \-d | \-\-debug ] -.RB [ \-D | \-\-disk ] .RB [ \-h | \-\-help ] +.RB [ \-\-ignorelockingfailure ] +.RB [ \-P | \-\-partial ] .RB [ \-s | \-\-short ] .RB [ \-v [ v ]| \-\-verbose " [" \-\-verbose ]] .RB [ \-\-version ] @@ -50,11 +51,6 @@ The values are: .fi .TP -.BR \-D ", " \-\-disk -Show attributes from the volume group descriptor area on disk(s). -Without this switch they are shown from the kernel. -Useful if the volume group isn't activated. -.TP .BR \-s ", " \-\-short Give a short listing showing the existence of volume groups. .TP diff --git a/man/vgextend.8 b/man/vgextend.8 index cd81503ce..1e8d84aa6 100644 --- a/man/vgextend.8 +++ b/man/vgextend.8 @@ -3,7 +3,9 @@ vgextend \- add physical volumes to a volume group .SH SYNOPSIS .B vgextend -[\-A/\-\-autobackup y/n] [\-d/\-\-debug] [\-h/\-?/\-\-help] [\-v/\-\-verbose] +[\-A/\-\-autobackup y/n] [\-d/\-\-debug] [\-h/\-?/\-\-help] +[\-t/\-\-test] +[\-v/\-\-verbose] VolumeGroupName PhysicalDevicePath [PhysicalDevicePath...] .SH DESCRIPTION vgextend allows you to add one or more initialized physical volumes ( see @@ -11,11 +13,6 @@ vgextend allows you to add one or more initialized physical volumes ( see ) to an existing volume group to extend it in size. .SH OPTIONS See \fBlvm\fP for common options. -.TP -.I \-A, \-\-autobackup y/n -Controls automatic backup of VG metadata after the change ( see -.B vgcfgbackup(8) -). Default is yes. .SH Examples "vgextend vg00 /dev/sda4 /dev/sdn1" tries to extend the existing volume group "vg00" by the new physical volumes (see diff --git a/man/vgmerge.8 b/man/vgmerge.8 index 12c27934b..172d94841 100644 --- a/man/vgmerge.8 +++ b/man/vgmerge.8 @@ -13,11 +13,6 @@ are equal and physical and logical volume summaries of both volume groups fit into DestinationVolumeGroupName's limits. .SH OPTIONS See \fBlvm\fP for common options. -.TP -.I \-A, \-\-autobackup y/n -Controls automatic backup of VG metadata after the merge ( see -.B vgcfgbackup(8) -). Default is yes. .I \-l, \-\-list Display merged DestinationVolumeGroupName like "vgdisplay -v". .TP diff --git a/man/vgreduce.8 b/man/vgreduce.8 index d81ea9885..55ab1c246 100644 --- a/man/vgreduce.8 +++ b/man/vgreduce.8 @@ -4,6 +4,7 @@ vgreduce \- reduce a volume group .SH SYNOPSIS .B vgreduce [\-a/\-\-all] [\-A/\-\-autobackup y/n] [\-d/\-\-debug] [\-h/\-?/\-\-help] +[\-t/\-\-test] [\-v/\-\-verbose] VolumeGroupName [PhysicalVolumePath...] .SH DESCRIPTION @@ -14,11 +15,6 @@ See \fBlvm\fP for common options. .TP .I \-a, \-\-all Removes all empty physical volumes if none are given on command line. -.TP -.I \-A, \-\-autobackup y/n -Controls automatic backup of VG metadata after the change ( see -.B vgcfgbackup(8) -). Default is yes. .SH SEE ALSO .BR lvm (8), .BR vgextend (8) diff --git a/man/vgremove.8 b/man/vgremove.8 index 7a193102d..7096fe2f0 100644 --- a/man/vgremove.8 +++ b/man/vgremove.8 @@ -3,7 +3,7 @@ vgremove \- remove a volume group .SH SYNOPSIS .B vgremove -[\-d/\-\-debug] [\-h/\-?/\-\-help] [\-v/\-\-verbose] +[\-d/\-\-debug] [\-h/\-?/\-\-help] [\-t/\-\-test] [\-v/\-\-verbose] VolumeGroupName [VolumeGroupName...] .SH DESCRIPTION vgremove allows you to remove one or more volume groups. diff --git a/man/vgrename.8 b/man/vgrename.8 index 328703040..b0f570cdd 100644 --- a/man/vgrename.8 +++ b/man/vgrename.8 @@ -6,6 +6,7 @@ vgrename \- rename a volume group [\-A/\-\-autobackup y/n] [\-d/\-\-debug] [\-h/\-?/\-\-help] +[\-t/\-\-test] [\-v/\-\-verbose] OldVolumeGroupPath/\-Name NewVolumeGroupPath/\-Name .SH DESCRIPTION @@ -14,11 +15,6 @@ vgrename renames an existing (see ) volume group. .SH OPTIONS See \fBlvm\fP for common options. -.TP -.I \-A, \-\-autobackup y/n -Controls automatic backup of VG metadata after the change ( see -.B vgcfgbackup(8) -). Default is yes. .SH Examples "vgrename /dev/vg02 /dev/my_volume_group" renames existing volume group "vg02" to "my_volume_group". diff --git a/man/vgscan.8 b/man/vgscan.8 index 1715c0d09..2f596583e 100644 --- a/man/vgscan.8 +++ b/man/vgscan.8 @@ -3,7 +3,10 @@ vgscan \- scan all disks for volume groups and rebuild caches .SH SYNOPSIS .B vgscan -[\-d/\-\-debug] [\-h/\-?/\-\-help] [\-v/\-\-verbose] +[\-d/\-\-debug] [\-h/\-?/\-\-help] +[\-\-ignorelockingfailure] +[\-P/\-\-partial] +[\-v/\-\-verbose] .SH DESCRIPTION vgscan scans all SCSI, (E)IDE disks, multiple devices and a bunch of other disk devices in the system looking for LVM physical volumes diff --git a/scripts/vg_convert b/scripts/vg_convert new file mode 100755 index 000000000..0fa7e710b --- /dev/null +++ b/scripts/vg_convert @@ -0,0 +1,19 @@ +#!/bin/sh -x + +# Original script used to convert a VG from LVM1 to LVM2 metadata format. +# Superceded by 'vgconvert', but left here to show how to do it step-by-step. + +# Takes vgname as parameter. No error checking. Uses temp file 'lvmbackup'. + +echo "Please use the 'vgconvert' tool instead" +exit 1 + +./vgcfgbackup $1 || exit 1 +./vgcfgbackup --file lvmbackup $1 || exit 1 + +CMDS=`./pvscan -u | sed -ne "s/.*PV \(.*\) with UUID \(.*\) VG $1 .*/.\/pvcreate -ff -y -M lvm2 --restorefile lvmbackup -u \2 \1 ; /p"` + +sh -x -c "$CMDS" || exit 1 + +./vgcfgrestore --file lvmbackup -M lvm2 $1 || exit 1 + diff --git a/tools/Makefile.in b/tools/Makefile.in index 800b43eb6..42ae8aef7 100644 --- a/tools/Makefile.in +++ b/tools/Makefile.in @@ -37,6 +37,7 @@ SOURCES=\ pvchange.c \ pvcreate.c \ pvdisplay.c \ + pvremove.c \ pvscan.c \ toollib.c \ vgcfgbackup.c \ @@ -44,6 +45,7 @@ SOURCES=\ vgchange.c \ vgck.c \ vgcreate.c \ + vgconvert.c \ vgdisplay.c \ vgexport.c \ vgextend.c \ diff --git a/tools/archive.c b/tools/archive.c index 15251b516..8fdafc26f 100644 --- a/tools/archive.c +++ b/tools/archive.c @@ -4,18 +4,8 @@ * This file is released under the GPL. */ -#include "log.h" -#include "archive.h" -#include "dbg_malloc.h" -#include "format-text.h" -#include "lvm-string.h" -#include "toollib.h" - #include "tools.h" -#include <unistd.h> -#include <limits.h> - static struct { int enabled; char *dir; @@ -97,7 +87,7 @@ int archive(struct volume_group *vg) return 1; if (test_mode()) { - log_print("Test mode: Skipping archiving of volume group."); + log_verbose("Test mode: Skipping archiving of volume group."); return 1; } @@ -113,7 +103,7 @@ int archive(struct volume_group *vg) int archive_display(struct cmd_context *cmd, const char *vg_name) { - return archive_list(cmd, cmd->um, _archive_params.dir, vg_name); + return archive_list(cmd, _archive_params.dir, vg_name); } static struct { @@ -153,11 +143,8 @@ void backup_enable(int flag) static int __backup(struct volume_group *vg) { - int r; - struct format_instance *tf; char name[PATH_MAX]; char *desc; - void *context; if (!(desc = _build_desc(vg->cmd->mem, vg->cmd->cmd_line, 0))) { stack; @@ -173,20 +160,7 @@ static int __backup(struct volume_group *vg) log_verbose("Creating volume group backup \"%s\"", name); - if (!(context = create_text_context(vg->cmd->fmtt, name, desc)) || - !(tf = vg->cmd->fmtt->ops->create_instance(vg->cmd->fmtt, NULL, - context))) { - stack; - return 0; - } - - if (!(r = tf->fmt->ops->vg_write(tf, vg, context)) || - !(r = tf->fmt->ops->vg_commit(tf, vg, context))) - stack; - - tf->fmt->ops->destroy_instance(tf); - - return r; + return backup_to_file(name, desc, vg); } int backup(struct volume_group *vg) @@ -197,7 +171,7 @@ int backup(struct volume_group *vg) } if (test_mode()) { - log_print("Test mode: Skipping volume group backup."); + log_verbose("Test mode: Skipping volume group backup."); return 1; } @@ -227,57 +201,73 @@ int backup_remove(const char *vg_name) return 1; } -static struct volume_group *_read_vg(struct cmd_context *cmd, - const char *vg_name, const char *file) +struct volume_group *backup_read_vg(struct cmd_context *cmd, + const char *vg_name, const char *file) { struct volume_group *vg; struct format_instance *tf; + struct list *mdah; + struct metadata_area *mda; void *context; - if (!(context = create_text_context(cmd->fmtt, file, + if (!(context = create_text_context(cmd, file, cmd->cmd_line)) || - !(tf = cmd->fmtt->ops->create_instance(cmd->fmtt, NULL, context))) { + !(tf = cmd->fmt_backup->ops->create_instance(cmd->fmt_backup, NULL, + context))) { log_error("Couldn't create text format object."); return NULL; } - if (!(vg = tf->fmt->ops->vg_read(tf, vg_name, context))) - stack; + list_iterate(mdah, &tf->metadata_areas) { + mda = list_item(mdah, struct metadata_area); + if (!(vg = mda->ops->vg_read(tf, vg_name, mda))) + stack; + break; + } tf->fmt->ops->destroy_instance(tf); return vg; } -int backup_restore_from_file(struct cmd_context *cmd, const char *vg_name, - const char *file) +/* ORPHAN and VG locks held before calling this */ +int backup_restore_vg(struct cmd_context *cmd, struct volume_group *vg) { - struct volume_group *vg; + struct list *pvh; + struct physical_volume *pv; + struct cache_info *info; /* - * Read in the volume group. + * FIXME: Check that the PVs referenced in the backup are + * not members of other existing VGs. */ - if (!(vg = _read_vg(cmd, vg_name, file))) { - stack; + + /* Attempt to write out using currently active format */ + if (!(vg->fid = cmd->fmt->ops->create_instance(cmd->fmt, vg->name, + NULL))) { + log_error("Failed to allocate format instance"); return 0; } - /* - * Check that those pv's referenced in the backup are - * currently orphans or members of the vg.s - */ - /* - * FIXME: waiting for label code. - */ - - /* - * Write the vg. - */ - - /* FIXME How do I find what format to write out the VG in? */ - /* Must store the format type inside the backup? */ - if (!(vg->fid = cmd->fmt1->ops->create_instance(cmd->fmt1, NULL, NULL))) { - log_error("Failed to allocate format1 instance"); - return 0; + /* Add any metadata areas on the PVs */ + list_iterate(pvh, &vg->pvs) { + pv = list_item(pvh, struct pv_list)->pv; + if (!(info = info_from_pvid(pv->dev->pvid))) { + log_error("PV %s missing from cache", + dev_name(pv->dev)); + return 0; + } + if (cmd->fmt != info->fmt) { + log_error("PV %s is a different format (%s)", + dev_name(pv->dev), info->fmt->name); + return 0; + } + if (!vg->fid->fmt->ops-> + pv_setup(vg->fid->fmt, 0, 0, 0, 0, 0, + &vg->fid->metadata_areas, pv, vg)) { + log_error("Format-specific setup for %s failed", + dev_name(pv->dev)); + return 0; + } } if (!vg_write(vg)) { @@ -288,6 +278,23 @@ int backup_restore_from_file(struct cmd_context *cmd, const char *vg_name, return 1; } +/* ORPHAN and VG locks held before calling this */ +int backup_restore_from_file(struct cmd_context *cmd, const char *vg_name, + const char *file) +{ + struct volume_group *vg; + + /* + * Read in the volume group from the text file. + */ + if (!(vg = backup_read_vg(cmd, vg_name, file))) { + stack; + return 0; + } + + return backup_restore_vg(cmd, vg); +} + int backup_restore(struct cmd_context *cmd, const char *vg_name) { char path[PATH_MAX]; @@ -300,3 +307,38 @@ int backup_restore(struct cmd_context *cmd, const char *vg_name) return backup_restore_from_file(cmd, vg_name, path); } + +int backup_to_file(const char *file, const char *desc, struct volume_group *vg) +{ + int r; + struct format_instance *tf; + struct list *mdah; + struct metadata_area *mda; + void *context; + struct cmd_context *cmd; + + cmd = vg->cmd; + + if (!(context = create_text_context(cmd, file, desc)) || + !(tf = cmd->fmt_backup->ops->create_instance(cmd->fmt_backup, NULL, + context))) { + log_error("Couldn't create backup object."); + return 0; + } + + /* Write and commit the metadata area */ + list_iterate(mdah, &tf->metadata_areas) { + mda = list_item(mdah, struct metadata_area); + if (!(r = mda->ops->vg_write(tf, vg, mda))) { + stack; + continue; + } + if (mda->ops->vg_commit && + !(r = mda->ops->vg_commit(tf, vg, mda))) { + stack; + } + } + + tf->fmt->ops->destroy_instance(tf); + return r; +} diff --git a/tools/archive.h b/tools/archive.h index f0f34eb25..157ae805f 100644 --- a/tools/archive.h +++ b/tools/archive.h @@ -42,8 +42,13 @@ void backup_enable(int flag); int backup(struct volume_group *vg); int backup_remove(const char *vg_name); +struct volume_group *backup_read_vg(struct cmd_context *cmd, + const char *vg_name, const char *file); +int backup_restore_vg(struct cmd_context *cmd, struct volume_group *vg); int backup_restore_from_file(struct cmd_context *cmd, const char *vg_name, const char *file); int backup_restore(struct cmd_context *cmd, const char *vg_name); +int backup_to_file(const char *file, const char *desc, struct volume_group *vg); + #endif diff --git a/tools/args.h b/tools/args.h index ecfb1d12c..15d473499 100644 --- a/tools/args.h +++ b/tools/args.h @@ -12,6 +12,11 @@ arg(version_ARG, '\0', "version", NULL) arg(quiet_ARG, '\0', "quiet", NULL) arg(physicalvolumesize_ARG, '\0', "setphysicalvolumesize", size_arg) arg(ignorelockingfailure_ARG, '\0', "ignorelockingfailure", NULL) +arg(metadatacopies_ARG, '\0', "metadatacopies", int_arg) +arg(metadatasize_ARG, '\0', "metadatasize", size_arg) +arg(restorefile_ARG, '\0', "restorefile", string_arg) +arg(labelsector_ARG, '\0', "labelsector", int_arg) +arg(driverloaded_ARG, '\0', "driverloaded", yes_no_arg) /* Allow some variations */ arg(resizable_ARG, '\0', "resizable", yes_no_arg) @@ -77,4 +82,3 @@ arg(zero_ARG, 'Z', "zero", yes_no_arg) /* this should always be last */ arg(ARG_COUNT, '-', "", NULL) - diff --git a/tools/commands.h b/tools/commands.h index 672cb2f22..b2e4868c4 100644 --- a/tools/commands.h +++ b/tools/commands.h @@ -18,6 +18,21 @@ * */ +/*********** Replace with script? +xx(e2fsadm, + "Resize logical volume and ext2 filesystem", + "e2fsadm " + "[-d|--debug] " "[-h|--help] " "[-n|--nofsck]" "\n" + "\t{[-l|--extents] [+|-]LogicalExtentsNumber |" "\n" + "\t [-L|--size] [+|-]LogicalVolumeSize[kKmMgGtT]}" "\n" + "\t[-t|--test] " "\n" + "\t[-v|--verbose] " "\n" + "\t[--version] " "\n" + "\tLogicalVolumePath" "\n", + + extents_ARG, size_ARG, nofsck_ARG, test_ARG) +*********/ + xx(help, "Display help for commands", "help <command>" "\n") @@ -40,6 +55,7 @@ xx(lvchange, "\t[-C/--contiguous y/n]\n" "\t[-d/--debug]\n" "\t[-h/-?/--help]\n" + "\t[--ignorelockingfailure]\n" "\t[-M/--persistent y/n] [--minor minor]\n" "\t[-P/--partial] " "\n" "\t[-p/--permission r/rw]\n" @@ -84,14 +100,14 @@ xx(lvdisplay, "lvdisplay\n" "\t[-c/--colon]\n" "\t[-d/--debug]\n" - "\t[-D/--disk]\n" "\t[-h/-?/--help]\n" "\t[-m/--maps]\n" "\t[-P/--partial] " "\n" "\t[-v/--verbose]\n" "\tLogicalVolume[Path] [LogicalVolume[Path]...]\n", - colon_ARG, disk_ARG, maps_ARG, partial_ARG, ignorelockingfailure_ARG) + colon_ARG, disk_ARG, maps_ARG, partial_ARG, + ignorelockingfailure_ARG) xx(lvextend, "Add space to a logical volume", @@ -106,8 +122,8 @@ xx(lvextend, "\t[-v/--verbose]\n" "\tLogicalVolume[Path] [ PhysicalVolumePath... ]\n", - autobackup_ARG, extents_ARG, size_ARG, stripes_ARG, stripesize_ARG, - test_ARG) + autobackup_ARG, extents_ARG, size_ARG, stripes_ARG, + stripesize_ARG, test_ARG) xx(lvmchange, "With the device mapper, this is obsolete and does nothing.", @@ -212,7 +228,6 @@ xx(lvscan, "lvscan " "\n" "\t[-b|--blockdevice] " "\n" "\t[-d|--debug] " "\n" - "\t[-D|--disk]" "\n" "\t[-h|--help] " "\n" "\t[-P|--partial] " "\n" "\t[-v|--verbose] " "\n" @@ -237,18 +252,25 @@ xx(pvchange, xx(pvcreate, "Initialize physical volume(s) for use by LVM", "pvcreate " "\n" + "\t[--restorefile file]\n" "\t[-d|--debug]" "\n" "\t[-f[f]|--force [--force]] " "\n" "\t[-h|--help] " "\n" - "\t[-y|--yes]" "\n" - "\t[-s|--size PhysicalVolumeSize[kKmMgGtT]" "\n" + "\t[--labelsector sector] " "\n" + "\t[-M|--metadatatype 1|2]" "\n" + "\t[--metadatacopies #copies]" "\n" + "\t[--metadatasize MetadataSize[kKmMgGtT]]" "\n" + "\t[--setphysicalvolumesize PhysicalVolumeSize[kKmMgGtT]" "\n" "\t[-t|--test] " "\n" "\t[-u|--uuid uuid] " "\n" "\t[-v|--verbose] " "\n" + "\t[-y|--yes]" "\n" "\t[--version] " "\n" "\tPhysicalVolume [PhysicalVolume...]\n", - force_ARG, test_ARG, physicalvolumesize_ARG, uuidstr_ARG, yes_ARG) + force_ARG, test_ARG, labelsector_ARG, metadatatype_ARG, metadatacopies_ARG, + metadatasize_ARG, physicalvolumesize_ARG, restorefile_ARG, uuidstr_ARG, + yes_ARG) xx(pvdata, "Display the on-disk metadata for physical volume(s)", @@ -297,9 +319,26 @@ xx(pvmove, autobackup_ARG, force_ARG, name_ARG, test_ARG) +xx(pvremove, + "Remove LVM label(s) from physical volume(s)", + "pvremove " "\n" + "\t[-d|--debug]" "\n" + "\t[-f[f]|--force [--force]] " "\n" + "\t[-h|--help] " "\n" + "\t[-y|--yes]" "\n" + "\t[-t|--test] " "\n" + "\t[-v|--verbose] " "\n" + "\t[-y|--yes]" "\n" + "\t[--version] " "\n" + "\tPhysicalVolume [PhysicalVolume...]\n", + + force_ARG, test_ARG, yes_ARG) + xx(pvresize, "Resize a physical volume in use by a volume group", - "pvmove " + "Not implemented. Use pvcreate options.", +/*** + "pvresize " "[-A|--autobackup {y|n}] " "[-d|--debug] " "[-h|--help]\n\t" @@ -307,7 +346,7 @@ xx(pvresize, "[-v|--verbose] " "[--version]\n\t" "\tPhysicalVolumePath [PhysicalVolumePath...]\n", - +***/ autobackup_ARG, physicalvolumesize_ARG) xx(pvscan, @@ -343,6 +382,7 @@ xx(vgcfgrestore, "\t[-d|--debug] " "\n" "\t[-f|--file filename] " "\n" "\t[-l[l]|--list [--list]]" "\n" + "\t[-M|--metadatatype 1|2]" "\n" "\t[-n|--name VolumeGroupName] " "\n" "\t[-h|--help]" "\n" "\t[-t|--test] " "\n" @@ -350,7 +390,7 @@ xx(vgcfgrestore, "\t[--version] " "\n" "\tVolumeGroupName", - file_ARG, list_ARG, name_ARG, test_ARG) + file_ARG, list_ARG, metadatatype_ARG, name_ARG, test_ARG) xx(vgchange, "Change volume group attributes", @@ -379,6 +419,23 @@ xx(vgck, "\t[-v/--verbose]\n" "\t[VolumeGroupName...]\n" ) +xx(vgconvert, + "Change volume group metadata format", + "vgconvert " "\n" + "\t[-d|--debug]" "\n" + "\t[-h|--help] " "\n" + "\t[--labelsector sector] " "\n" + "\t[-M|--metadatatype 1|2]" "\n" + "\t[--metadatacopies #copies]" "\n" + "\t[--metadatasize MetadataSize[kKmMgGtT]]" "\n" + "\t[-t|--test] " "\n" + "\t[-v|--verbose] " "\n" + "\t[--version] " "\n" + "\tVolumeGroupName [VolumeGroupName...]\n", + + force_ARG, test_ARG, labelsector_ARG, metadatatype_ARG, metadatacopies_ARG, + metadatasize_ARG ) + xx(vgcreate, "Create a volume group", "vgcreate" "\n" @@ -386,7 +443,7 @@ xx(vgcreate, "\t[-d|--debug]" "\n" "\t[-h|--help]" "\n" "\t[-l|--maxlogicalvolumes MaxLogicalVolumes]" "\n" - "\t[-M|--metadatatype lvm1/text] " "\n" + "\t[-M|--metadatatype 1|2] " "\n" "\t[-p|--maxphysicalvolumes MaxPhysicalVolumes] " "\n" "\t[-s|--physicalextentsize PhysicalExtentSize[kKmMgGtT]] " "\n" "\t[-t|--test] " "\n" @@ -403,6 +460,7 @@ xx(vgdisplay, "\t[-c|--colon | -s|--short | -v|--verbose]" "\n" "\t[-d|--debug] " "\n" "\t[-h|--help] " "\n" + "\t[--ignorelockingfailure]" "\n" "\t[-P|--partial] " "\n" "\t[-A|--activevolumegroups | [-D|--disk]" "\n" "\t[--version]" "\n" @@ -499,7 +557,6 @@ xx(vgrename, "vgrename\n" "\t[-A/--autobackup y/n]\n" "\t[-d/--debug]\n" - "\t[-f/--force]\n" "\t[-h/-?/--help]\n" "\t[-t/--test]\n" "\t[-v/--verbose]\n" @@ -524,7 +581,7 @@ xx(vgsplit, "\t[-d|--debug] " "\n" "\t[-h|--help] " "\n" "\t[-l|--list]" "\n" - "\t[-M|--metadatatype lvm1/text] " "\n" + "\t[-M|--metadatatype 1|2] " "\n" "\t[-t|--test] " "\n" "\t[-v|--verbose] " "\n" "\t[--version]" "\n" diff --git a/tools/lvchange.c b/tools/lvchange.c index 59d5901cc..24cc73d09 100644 --- a/tools/lvchange.c +++ b/tools/lvchange.c @@ -20,120 +20,6 @@ #include "tools.h" -static int lvchange_single(struct cmd_context *cmd, struct logical_volume *lv); -static int lvchange_permission(struct cmd_context *cmd, - struct logical_volume *lv); -static int lvchange_availability(struct cmd_context *cmd, - struct logical_volume *lv); -static int lvchange_contiguous(struct cmd_context *cmd, - struct logical_volume *lv); -static int lvchange_readahead(struct cmd_context *cmd, - struct logical_volume *lv); -static int lvchange_persistent(struct cmd_context *cmd, - struct logical_volume *lv); - -int lvchange(struct cmd_context *cmd, int argc, char **argv) -{ - if (!arg_count(cmd, available_ARG) && !arg_count(cmd, contiguous_ARG) - && !arg_count(cmd, permission_ARG) && !arg_count(cmd, readahead_ARG) - && !arg_count(cmd, minor_ARG) && !arg_count(cmd, persistent_ARG)) { - log_error("One or more of -a, -C, -m, -M, -p or -r required"); - return EINVALID_CMD_LINE; - } - - if (arg_count(cmd, ignorelockingfailure_ARG) && - (arg_count(cmd, contiguous_ARG) || arg_count(cmd, permission_ARG) || - arg_count(cmd, readahead_ARG) || arg_count(cmd, persistent_ARG))) { - log_error("Only -a permitted with --ignorelockingfailure"); - return EINVALID_CMD_LINE; - } - - if (!argc) { - log_error("Please give logical volume path(s)"); - return EINVALID_CMD_LINE; - } - - if (arg_count(cmd, minor_ARG) && argc != 1) { - log_error("Only give one logical volume when specifying minor"); - return EINVALID_CMD_LINE; - } - - if (!driver_is_loaded()) - return ECMD_FAILED; - - return process_each_lv(cmd, argc, argv, LCK_VG_WRITE, &lvchange_single); -} - -static int lvchange_single(struct cmd_context *cmd, struct logical_volume *lv) -{ - int doit = 0; - int archived = 0; - - 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))) { - log_error("Only -a permitted with read-only volume " - "group \"%s\"", lv->vg->name); - return EINVALID_CMD_LINE; - } - - if (lv_is_origin(lv) && - (arg_count(cmd, contiguous_ARG) || arg_count(cmd, permission_ARG) || - arg_count(cmd, readahead_ARG) || arg_count(cmd, persistent_ARG))) { - log_error("Can't change logical volume \"%s\" under snapshot", - lv->name); - return ECMD_FAILED; - } - - if (lv_is_cow(lv)) { - log_error("Can't change snapshot logical volume \"%s\"", - lv->name); - return ECMD_FAILED; - } - - /* access permission change */ - if (arg_count(cmd, permission_ARG)) { - if (!archive(lv->vg)) - return ECMD_FAILED; - archived = 1; - doit += lvchange_permission(cmd, lv); - } - - /* allocation policy change */ - if (arg_count(cmd, contiguous_ARG)) { - if (!archived && !archive(lv->vg)) - return ECMD_FAILED; - archived = 1; - doit += lvchange_contiguous(cmd, lv); - } - - /* read ahead sector change */ - if (arg_count(cmd, readahead_ARG)) { - if (!archived && !archive(lv->vg)) - return ECMD_FAILED; - archived = 1; - doit += lvchange_readahead(cmd, lv); - } - - /* read ahead sector change */ - if (arg_count(cmd, persistent_ARG)) { - if (!archived && !archive(lv->vg)) - return ECMD_FAILED; - archived = 1; - doit += lvchange_persistent(cmd, lv); - } - - if (doit) - log_print("Logical volume \"%s\" changed", lv->name); - - /* availability change */ - if (arg_count(cmd, available_ARG)) - if (!lvchange_availability(cmd, lv)) - return ECMD_FAILED; - - return 0; -} - static int lvchange_permission(struct cmd_context *cmd, struct logical_volume *lv) { @@ -234,7 +120,7 @@ static int lvchange_contiguous(struct cmd_context *cmd, } /******** FIXME lv_check_contiguous? - if ((lv_allocation & ALLOC_CONTIGUOUS) + if (want_contiguous) && (ret = lv_check_contiguous(vg, lv_index + 1)) == FALSE) { log_error("No contiguous logical volume \"%s\"", lv->name); return 0; @@ -245,8 +131,8 @@ static int lvchange_contiguous(struct cmd_context *cmd, log_verbose("Setting contiguous allocation policy for \"%s\"", lv->name); } else { - lv->alloc = ALLOC_NEXT_FREE; - log_verbose("Removing contiguous allocation policy for \"%s\"", + lv->alloc = ALLOC_DEFAULT; + log_verbose("Reverting to default allocation policy for \"%s\"", lv->name); } @@ -371,3 +257,104 @@ static int lvchange_persistent(struct cmd_context *cmd, return 1; } + +static int lvchange_single(struct cmd_context *cmd, struct logical_volume *lv, + void *handle) +{ + int doit = 0; + int archived = 0; + + 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))) { + log_error("Only -a permitted with read-only volume " + "group \"%s\"", lv->vg->name); + return EINVALID_CMD_LINE; + } + + if (lv_is_origin(lv) && + (arg_count(cmd, contiguous_ARG) || arg_count(cmd, permission_ARG) || + arg_count(cmd, readahead_ARG) || arg_count(cmd, persistent_ARG))) { + log_error("Can't change logical volume \"%s\" under snapshot", + lv->name); + return ECMD_FAILED; + } + + if (lv_is_cow(lv)) { + log_error("Can't change snapshot logical volume \"%s\"", + lv->name); + return ECMD_FAILED; + } + + /* access permission change */ + if (arg_count(cmd, permission_ARG)) { + if (!archive(lv->vg)) + return ECMD_FAILED; + archived = 1; + doit += lvchange_permission(cmd, lv); + } + + /* allocation policy change */ + if (arg_count(cmd, contiguous_ARG)) { + if (!archived && !archive(lv->vg)) + return ECMD_FAILED; + archived = 1; + doit += lvchange_contiguous(cmd, lv); + } + + /* read ahead sector change */ + if (arg_count(cmd, readahead_ARG)) { + if (!archived && !archive(lv->vg)) + return ECMD_FAILED; + archived = 1; + doit += lvchange_readahead(cmd, lv); + } + + /* read ahead sector change */ + if (arg_count(cmd, persistent_ARG)) { + if (!archived && !archive(lv->vg)) + return ECMD_FAILED; + archived = 1; + doit += lvchange_persistent(cmd, lv); + } + + if (doit) + log_print("Logical volume \"%s\" changed", lv->name); + + /* availability change */ + if (arg_count(cmd, available_ARG)) + if (!lvchange_availability(cmd, lv)) + return ECMD_FAILED; + + return 0; +} + +int lvchange(struct cmd_context *cmd, int argc, char **argv) +{ + if (!arg_count(cmd, available_ARG) && !arg_count(cmd, contiguous_ARG) + && !arg_count(cmd, permission_ARG) && !arg_count(cmd, readahead_ARG) + && !arg_count(cmd, minor_ARG) && !arg_count(cmd, persistent_ARG)) { + log_error("One or more of -a, -C, -m, -M, -p or -r required"); + return EINVALID_CMD_LINE; + } + + if (arg_count(cmd, ignorelockingfailure_ARG) && + (arg_count(cmd, contiguous_ARG) || arg_count(cmd, permission_ARG) || + arg_count(cmd, readahead_ARG) || arg_count(cmd, persistent_ARG))) { + log_error("Only -a permitted with --ignorelockingfailure"); + return EINVALID_CMD_LINE; + } + + if (!argc) { + log_error("Please give logical volume path(s)"); + return EINVALID_CMD_LINE; + } + + if (arg_count(cmd, minor_ARG) && argc != 1) { + log_error("Only give one logical volume when specifying minor"); + return EINVALID_CMD_LINE; + } + + return process_each_lv(cmd, argc, argv, LCK_VG_WRITE, NULL, + &lvchange_single); +} diff --git a/tools/lvcreate.c b/tools/lvcreate.c index 3065806c8..59e9ed0ad 100644 --- a/tools/lvcreate.c +++ b/tools/lvcreate.c @@ -90,7 +90,7 @@ static int _read_name_params(struct lvcreate_params *lp, if (lp->lv_name && strchr(lp->lv_name, '/')) { if (!(lp->vg_name = extract_vgname(cmd, lp->lv_name))) - return 0; + return 0; if (strcmp(lp->vg_name, argv[0])) { log_error("Inconsistent volume group " @@ -109,6 +109,13 @@ static int _read_name_params(struct lvcreate_params *lp, if (lp->lv_name && (ptr = strrchr(lp->lv_name, '/'))) lp->lv_name = ptr + 1; + /* FIXME Remove this restriction eventually */ + if (lp->lv_name && !strncmp(lp->lv_name, "snapshot", 8)) { + log_error("Names starting \"snapshot\" are reserved. " + "Please choose a different LV name."); + return 0; + } + return 1; } @@ -312,20 +319,21 @@ static int _lvcreate(struct cmd_context *cmd, struct lvcreate_params *lp) { uint32_t size_rest; uint32_t status = 0; - alloc_policy_t alloc = ALLOC_NEXT_FREE; + alloc_policy_t alloc = ALLOC_DEFAULT; struct volume_group *vg; struct logical_volume *lv, *org; struct list *pvh; + int consistent = 1; if (lp->contiguous) alloc = ALLOC_CONTIGUOUS; - status |= lp->permission; + status |= lp->permission | VISIBLE_LV; /* does VG exist? */ log_verbose("Finding volume group \"%s\"", lp->vg_name); - if (!(vg = vg_read(cmd, lp->vg_name))) { + if (!(vg = vg_read(cmd, lp->vg_name, &consistent))) { log_error("Volume group \"%s\" doesn't exist", lp->vg_name); return 0; } @@ -391,20 +399,30 @@ static int _lvcreate(struct cmd_context *cmd, struct lvcreate_params *lp) lp->extents = lp->extents - size_rest + lp->stripes; } - if (lp->snapshot && !(org = find_lv(vg, lp->origin))) { - log_err("Couldn't find origin volume '%s'.", lp->origin); + if (!activation()) { + if (lp->snapshot) + log_error("Can't create snapshot without using " + "device-mapper kernel driver"); return 0; } - /* - * For now all logical volumes are visible. - */ - status |= VISIBLE_LV; - + if (lp->snapshot) { + if (!(org = find_lv(vg, lp->origin))) { + log_err("Couldn't find origin volume '%s'.", + lp->origin); + return 0; + } + if (lv_is_cow(org)) { + log_error("Snapshots of snapshots are not supported " + "yet."); + return 0; + } + } if (!(lv = lv_create(vg->fid, lp->lv_name, status, alloc, lp->stripes, lp->stripe_size, lp->extents, - vg, pvh))) return 0; + vg, pvh))) + return 0; if (lp->read_ahead) { log_verbose("Setting read ahead sectors"); @@ -421,16 +439,28 @@ static int _lvcreate(struct cmd_context *cmd, struct lvcreate_params *lp) return 0; /* store vg on disk(s) */ - if (!vg_write(vg)) + if (!vg_write(vg)) { return 0; + } - if (!lock_vol(cmd, lv->lvid.s, LCK_LV_ACTIVATE)) + if (!lock_vol(cmd, lv->lvid.s, LCK_LV_ACTIVATE)) { + /* FIXME Remove the failed lv we just added */ + log_error("Aborting. Failed to wipe snapshot " + "exception store. Remove new LV and retry."); return 0; + } - if (lp->zero || lp->snapshot) - _zero_lv(cmd, lv); - else - log_print("WARNING: \"%s\" not zeroed", lv->name); + if ((lp->zero || lp->snapshot) && activation()) { + if (!_zero_lv(cmd, lv) && lp->snapshot) { + /* FIXME Remove the failed lv we just added */ + log_error("Aborting. Failed to wipe snapshot " + "exception store. Remove new LV and retry."); + return 0; + } + } else { + log_error("WARNING: \"%s\" not zeroed", lv->name); + /* FIXME Remove the failed lv we just added */ + } if (lp->snapshot) { if (!lock_vol(cmd, lv->lvid.s, LCK_LV_DEACTIVATE)) { @@ -443,7 +473,7 @@ static int _lvcreate(struct cmd_context *cmd, struct lvcreate_params *lp) return 0; } - if (!vg_add_snapshot(org, lv, 1, lp->chunk_size)) { + if (!vg_add_snapshot(org, lv, 1, NULL, lp->chunk_size)) { log_err("Couldn't create snapshot."); return 0; } @@ -480,9 +510,6 @@ int lvcreate(struct cmd_context *cmd, int argc, char **argv) if (!_read_params(&lp, cmd, argc, argv)) return -EINVALID_CMD_LINE; - if (!driver_is_loaded()) - return ECMD_FAILED; - if (!lock_vol(cmd, lp.vg_name, LCK_VG_WRITE)) { log_error("Can't get lock for %s", lp.vg_name); return 0; diff --git a/tools/lvdisplay.c b/tools/lvdisplay.c index d421327c0..69971b1be 100644 --- a/tools/lvdisplay.c +++ b/tools/lvdisplay.c @@ -20,12 +20,13 @@ #include "tools.h" -int lvdisplay_single(struct cmd_context *cmd, struct logical_volume *lv) +int lvdisplay_single(struct cmd_context *cmd, struct logical_volume *lv, + void *handle) { if (arg_count(cmd, colon_ARG)) lvdisplay_colons(lv); else { - lvdisplay_full(cmd, lv); + lvdisplay_full(cmd, lv, handle); if (arg_count(cmd, maps_ARG)) lvdisplay_segments(lv); } @@ -42,8 +43,6 @@ int lvdisplay(struct cmd_context *cmd, int argc, char **argv) return EINVALID_CMD_LINE; } - if (!driver_is_loaded()) - return ECMD_FAILED; - - return process_each_lv(cmd, argc, argv, LCK_VG_READ, &lvdisplay_single); + return process_each_lv(cmd, argc, argv, LCK_VG_READ, NULL, + &lvdisplay_single); } diff --git a/tools/lvm.c b/tools/lvm.c index 366b906bc..526c1847a 100644 --- a/tools/lvm.c +++ b/tools/lvm.c @@ -5,9 +5,7 @@ */ #include "tools.h" -#include "archive.h" #include "defaults.h" -#include "lvm1_label.h" #include "label.h" #include "version.h" @@ -19,10 +17,7 @@ #include <syslog.h> #include <libgen.h> #include <sys/stat.h> -#include <ctype.h> #include <time.h> -#include <stdlib.h> -#include <locale.h> #ifdef READLINE_SUPPORT #include <readline/readline.h> @@ -46,136 +41,10 @@ struct arg the_args[ARG_COUNT + 1] = { static int _array_size; static int _num_commands; static struct command *_commands; -struct cmd_context *cmd; - -/* Whether or not to dump persistent filter state */ -static int _dump_filter; static int _interactive; -static FILE *_log; - -/* lvm1 label handler */ -static struct labeller *_lvm1_label; - -/* - * This structure only contains those options that - * can have a default and per command setting. - */ -struct config_info { - int debug; - int verbose; - int test; - int syslog; - const char *msg_prefix; - int cmd_name; /* Show command name? */ - - int archive; /* should we archive ? */ - int backup; /* should we backup ? */ - - struct format_type *fmt; - - mode_t umask; -}; - -static struct config_info _default_settings; -static struct config_info _current_settings; - -/* - * The lvm_sys_dir contains: - * - * o The lvm configuration (lvm.conf) - * o The persistent filter cache (.cache) - * o Volume group backups (/backup) - * o Archive of old vg configurations (/archive) - */ -static char _sys_dir[PATH_MAX] = "/etc/lvm"; -static char _dev_dir[PATH_MAX]; -static char _proc_dir[PATH_MAX]; - -/* static functions */ -static void register_commands(void); -static struct command *find_command(const char *name); -static void register_command(const char *name, command_fn fn, - const char *desc, const char *usage, ...); -static void create_new_command(const char *name, command_fn command, - const char *desc, const char *usage, - int nargs, int *args); - -static void alloc_command(void); -static void add_getopt_arg(int arg, char **ptr, struct option **o); -static int process_command_line(struct command *com, int *argc, char ***argv); -static struct arg *find_arg(struct command *com, int a); -static int process_common_commands(struct command *com); -static int run_command(int argc, char **argv); -static int init(void); -static void fin(void); -static int run_script(int argc, char **argv); - -#ifdef READLINE_SUPPORT -static int shell(void); -#endif - -static void display_help(void); - -int main(int argc, char **argv) -{ - char *namebase, *base; - int ret, alias = 0; - - if (!init()) - return -1; - - namebase = strdup(argv[0]); - base = basename(namebase); - while (*base == '/') - base++; - if (strcmp(base, "lvm")) - alias = 1; - free(namebase); - - register_commands(); - -#ifdef READLINE_SUPPORT - if (!alias && argc == 1) { - ret = shell(); - goto out; - } -#endif - - if (!alias) { - if (argc < 2) { - log_fatal("Please supply an LVM command."); - display_help(); - ret = EINVALID_CMD_LINE; - goto out; - } - - argc--; - argv++; - } - ret = run_command(argc, argv); - if ((ret == ENO_SUCH_CMD) && (!alias)) - ret = run_script(argc, argv); - if (ret == ENO_SUCH_CMD) - log_error("No such command. Try 'help'."); - - out: - fin(); - return ret; -} - -void usage(const char *name) -{ - struct command *com = find_command(name); - - if (!com) - return; - - log_error("%s: %s\n\n%s", com->name, com->desc, com->usage); -} - -int yes_no_arg(struct arg *a) +int yes_no_arg(struct cmd_context *cmd, struct arg *a) { a->sign = SIGN_NONE; @@ -191,18 +60,26 @@ int yes_no_arg(struct arg *a) return 1; } -int metadatatype_arg(struct arg *a) +int metadatatype_arg(struct cmd_context *cmd, struct arg *a) { - if (!strcasecmp(a->value, cmd->fmtt->name)) - a->ptr = cmd->fmtt; + struct format_type *fmt; + struct list *fmth; - else if (!strcasecmp(a->value, cmd->fmt1->name)) - a->ptr = cmd->fmt1; + char *format; - else - return 0; + format = a->value; - return 1; + list_iterate(fmth, &cmd->formats) { + fmt = list_item(fmth, struct format_type); + if (!strcasecmp(fmt->name, format) || + !strcasecmp(fmt->name + 3, format) || + (fmt->alias && !strcasecmp(fmt->alias, format))) { + a->ptr = fmt; + return 1; + } + } + + return 0; } int _get_int_arg(struct arg *a, char **ptr) @@ -236,7 +113,7 @@ int _get_int_arg(struct arg *a, char **ptr) return 1; } -int size_arg(struct arg *a) +int size_arg(struct cmd_context *cmd, struct arg *a) { char *ptr; int i; @@ -283,7 +160,7 @@ int size_arg(struct arg *a) return 1; } -int int_arg(struct arg *a) +int int_arg(struct cmd_context *cmd, struct arg *a) { char *ptr; @@ -293,7 +170,7 @@ int int_arg(struct arg *a) return 1; } -int int_arg_with_sign(struct arg *a) +int int_arg_with_sign(struct cmd_context *cmd, struct arg *a) { char *ptr; @@ -303,7 +180,7 @@ int int_arg_with_sign(struct arg *a) return 1; } -int minor_arg(struct arg *a) +int minor_arg(struct cmd_context *cmd, struct arg *a) { char *ptr; @@ -318,12 +195,12 @@ int minor_arg(struct arg *a) return 1; } -int string_arg(struct arg *a) +int string_arg(struct cmd_context *cmd, struct arg *a) { return 1; } -int permission_arg(struct arg *a) +int permission_arg(struct cmd_context *cmd, struct arg *a) { a->sign = SIGN_NONE; @@ -358,18 +235,45 @@ char yes_no_prompt(const char *prompt, ...) return c; } -static void register_commands() +static void __alloc(int size) +{ + if (!(_commands = dbg_realloc(_commands, sizeof(*_commands) * size))) { + log_fatal("Couldn't allocate memory."); + exit(ECMD_FAILED); + } + + _array_size = size; +} + +static void _alloc_command(void) { -#define xx(a, b, c...) register_command(# a, a, b, ## c, \ - debug_ARG, help_ARG, \ - version_ARG, verbose_ARG, \ - quiet_ARG, -1); -#include "commands.h" -#undef xx + if (!_array_size) + __alloc(32); + + if (_array_size <= _num_commands) + __alloc(2 * _array_size); } -static void register_command(const char *name, command_fn fn, - const char *desc, const char *usage, ...) +static void _create_new_command(const char *name, command_fn command, + const char *desc, const char *usage, + int nargs, int *args) +{ + struct command *nc; + + _alloc_command(); + + nc = _commands + _num_commands++; + + nc->name = name; + nc->desc = desc; + nc->usage = usage; + nc->fn = command; + nc->num_args = nargs; + nc->valid_args = args; +} + +static void _register_command(const char *name, command_fn fn, + const char *desc, const char *usage, ...) { int nargs = 0, i; int *args; @@ -394,10 +298,21 @@ static void register_command(const char *name, command_fn fn, va_end(ap); /* enter the command in the register */ - create_new_command(name, fn, desc, usage, nargs, args); + _create_new_command(name, fn, desc, usage, nargs, args); +} + +static void _register_commands() +{ +#define xx(a, b, c...) _register_command(# a, a, b, ## c, \ + driverloaded_ARG, \ + debug_ARG, help_ARG, \ + version_ARG, verbose_ARG, \ + quiet_ARG, -1); +#include "commands.h" +#undef xx } -static struct command *find_command(const char *name) +static struct command *_find_command(const char *name) { int i; char *namebase, *base; @@ -418,41 +333,14 @@ static struct command *find_command(const char *name) return _commands + i; } -static void create_new_command(const char *name, command_fn command, - const char *desc, const char *usage, - int nargs, int *args) -{ - struct command *nc; - - alloc_command(); - - nc = _commands + _num_commands++; - - nc->name = name; - nc->desc = desc; - nc->usage = usage; - nc->fn = command; - nc->num_args = nargs; - nc->valid_args = args; -} - -static void __alloc(int size) +void usage(const char *name) { - if (!(_commands = dbg_realloc(_commands, sizeof(*_commands) * size))) { - log_fatal("Couldn't allocate memory."); - exit(ECMD_FAILED); - } - - _array_size = size; -} + struct command *com = _find_command(name); -static void alloc_command(void) -{ - if (!_array_size) - __alloc(32); + if (!com) + return; - if (_array_size <= _num_commands) - __alloc(2 * _array_size); + log_error("%s: %s\n\n%s", com->name, com->desc, com->usage); } /* @@ -464,7 +352,7 @@ static void alloc_command(void) * we have only 1 ATM (--version) I think we can * live with this restriction. */ -static void add_getopt_arg(int arg, char **ptr, struct option **o) +static void _add_getopt_arg(int arg, char **ptr, struct option **o) { struct arg *a = the_args + arg; @@ -484,7 +372,29 @@ static void add_getopt_arg(int arg, char **ptr, struct option **o) } } -static int process_command_line(struct command *com, int *argc, char ***argv) +static struct arg *_find_arg(struct command *com, int opt) +{ + struct arg *a; + int i, arg; + + for (i = 0; i < com->num_args; i++) { + arg = com->valid_args[i]; + a = the_args + arg; + + /* + * opt should equal either the + * short arg, or the index into + * 'the_args'. + */ + if ((a->short_arg && (opt == a->short_arg)) || (opt == arg)) + return a; + } + + return 0; +} + +static int _process_command_line(struct cmd_context *cmd, int *argc, + char ***argv) { int i, opt; char str[((ARG_COUNT + 1) * 2) + 1], *ptr = str; @@ -501,8 +411,8 @@ static int process_command_line(struct command *com, int *argc, char ***argv) } /* fill in the short and long opts */ - for (i = 0; i < com->num_args; i++) - add_getopt_arg(com->valid_args[i], &ptr, &o); + for (i = 0; i < cmd->command->num_args; i++) + _add_getopt_arg(cmd->command->valid_args[i], &ptr, &o); *ptr = '\0'; memset(o, 0, sizeof(*o)); @@ -512,7 +422,7 @@ static int process_command_line(struct command *com, int *argc, char ***argv) optind = 0; while ((opt = getopt_long(*argc, *argv, str, opts, NULL)) >= 0) { - a = find_arg(com, opt); + a = _find_arg(cmd->command, opt); if (!a) { log_fatal("Unrecognised option."); @@ -528,7 +438,7 @@ static int process_command_line(struct command *com, int *argc, char ***argv) a->value = optarg; - if (!a->fn(a)) { + if (!a->fn(cmd, a)) { log_error("Invalid argument %s", optarg); return 0; } @@ -542,28 +452,7 @@ static int process_command_line(struct command *com, int *argc, char ***argv) return 1; } -static struct arg *find_arg(struct command *com, int opt) -{ - struct arg *a; - int i, arg; - - for (i = 0; i < com->num_args; i++) { - arg = com->valid_args[i]; - a = the_args + arg; - - /* - * opt should equal either the - * short arg, or the index into - * 'the_args'. - */ - if ((a->short_arg && (opt == a->short_arg)) || (opt == arg)) - return a; - } - - return 0; -} - -static int merge_synonym(int oldarg, int newarg) +static int _merge_synonym(struct cmd_context *cmd, int oldarg, int newarg) { struct arg *old, *new; @@ -600,37 +489,34 @@ int version(struct cmd_context *cmd, int argc, char **argv) return ECMD_PROCESSED; } -static int process_common_commands(struct command *com) +static int _get_settings(struct cmd_context *cmd) { - _current_settings = _default_settings; + cmd->current_settings = cmd->default_settings; if (arg_count(cmd, debug_ARG)) - _current_settings.debug = _LOG_FATAL + + cmd->current_settings.debug = _LOG_FATAL + (arg_count(cmd, debug_ARG) - 1); if (arg_count(cmd, verbose_ARG)) - _current_settings.verbose = arg_count(cmd, verbose_ARG); + cmd->current_settings.verbose = arg_count(cmd, verbose_ARG); if (arg_count(cmd, quiet_ARG)) { - _current_settings.debug = 0; - _current_settings.verbose = 0; + cmd->current_settings.debug = 0; + cmd->current_settings.verbose = 0; } if (arg_count(cmd, test_ARG)) - _current_settings.test = arg_count(cmd, test_ARG); + cmd->current_settings.test = arg_count(cmd, test_ARG); - if (arg_count(cmd, help_ARG)) { - usage(com->name); - return ECMD_PROCESSED; - } - - if (arg_count(cmd, version_ARG)) { - return version(cmd, 0, (char **) NULL); + if (arg_count(cmd, driverloaded_ARG)) { + cmd->current_settings.activation = + arg_int_value(cmd, driverloaded_ARG, + cmd->default_settings.activation); } if (arg_count(cmd, autobackup_ARG)) { - _current_settings.archive = 1; - _current_settings.backup = 1; + cmd->current_settings.archive = 1; + cmd->current_settings.backup = 1; } if (arg_count(cmd, partial_ARG)) { @@ -646,29 +532,31 @@ static int process_common_commands(struct command *com) init_ignorelockingfailure(0); /* Handle synonyms */ - if (!merge_synonym(resizable_ARG, resizeable_ARG) || - !merge_synonym(allocation_ARG, allocatable_ARG) || - !merge_synonym(allocation_ARG, resizeable_ARG)) - return ECMD_FAILED; + if (!_merge_synonym(cmd, resizable_ARG, resizeable_ARG) || + !_merge_synonym(cmd, allocation_ARG, allocatable_ARG) || + !_merge_synonym(cmd, allocation_ARG, resizeable_ARG)) + return EINVALID_CMD_LINE; - /* Zero indicates it's OK to continue processing this command */ + /* Zero indicates success */ return 0; } -int help(struct cmd_context *cmd, int argc, char **argv) +static int _process_common_commands(struct cmd_context *cmd) { - if (!argc) - display_help(); - else { - int i; - for (i = 0; i < argc; i++) - usage(argv[i]); + if (arg_count(cmd, help_ARG)) { + usage(cmd->command->name); + return ECMD_PROCESSED; } + if (arg_count(cmd, version_ARG)) { + return version(cmd, 0, (char **) NULL); + } + + /* Zero indicates it's OK to continue processing this command */ return 0; } -static void display_help(void) +static void _display_help(void) { int i; @@ -683,22 +571,38 @@ static void display_help(void) } } -static void _use_settings(struct config_info *settings) +int help(struct cmd_context *cmd, int argc, char **argv) +{ + if (!argc) + _display_help(); + else { + int i; + for (i = 0; i < argc; i++) + usage(argv[i]); + } + + return 0; +} + +static void _apply_settings(struct cmd_context *cmd) { - init_debug(settings->debug); - init_verbose(settings->verbose); - init_test(settings->test); + init_debug(cmd->current_settings.debug); + init_verbose(cmd->current_settings.verbose); + init_test(cmd->current_settings.test); + + init_msg_prefix(cmd->default_settings.msg_prefix); + init_cmd_name(cmd->default_settings.cmd_name); - init_msg_prefix(_default_settings.msg_prefix); - init_cmd_name(_default_settings.cmd_name); + archive_enable(cmd->current_settings.archive); + backup_enable(cmd->current_settings.backup); - archive_enable(settings->archive); - backup_enable(settings->backup); + set_activation(cmd->current_settings.activation); - cmd->fmt = arg_ptr_value(cmd, metadatatype_ARG, settings->fmt); + cmd->fmt = arg_ptr_value(cmd, metadatatype_ARG, + cmd->current_settings.fmt); } -static char *_copy_command_line(struct pool *mem, int argc, char **argv) +static char *_copy_command_line(struct cmd_context *cmd, int argc, char **argv) { int i; @@ -720,59 +624,68 @@ static char *_copy_command_line(struct pool *mem, int argc, char **argv) /* * Terminate. */ - if (!pool_grow_object(mem, "\0", 1)) + if (!pool_grow_object(cmd->mem, "\0", 1)) goto bad; - return pool_end_object(mem); + return pool_end_object(cmd->mem); bad: log_err("Couldn't copy command line."); - pool_abandon_object(mem); + pool_abandon_object(cmd->mem); return NULL; } -static int run_command(int argc, char **argv) +static int _run_command(struct cmd_context *cmd, int argc, char **argv) { int ret = 0; int locking_type; - if (!(cmd->cmd_line = _copy_command_line(cmd->mem, argc, argv))) + if (!(cmd->cmd_line = _copy_command_line(cmd, argc, argv))) return ECMD_FAILED; - if (!(cmd->command = find_command(argv[0]))) + if (!(cmd->command = _find_command(argv[0]))) return ENO_SUCH_CMD; - if (!process_command_line(cmd->command, &argc, &argv)) { + if (!_process_command_line(cmd, &argc, &argv)) { log_error("Error during parsing of command line."); return EINVALID_CMD_LINE; } set_cmd_name(cmd->command->name); - if ((ret = process_common_commands(cmd->command))) - return ret; + if (reload_config_file(&cmd->cf)) { + ; + /* FIXME Reinitialise various settings inc. logging, filters */ + } - _use_settings(&_current_settings); + if ((ret = _get_settings(cmd))) + goto out; + _apply_settings(cmd); + + if ((ret = _process_common_commands(cmd))) + goto out; locking_type = find_config_int(cmd->cf->root, "global/locking_type", '/', 1); if (!init_locking(locking_type, cmd->cf)) { log_error("Locking type %d initialisation failed.", locking_type); - return 0; + ret = ECMD_FAILED; + goto out; } ret = cmd->command->fn(cmd, argc, argv); fin_locking(); - /* - * set the debug and verbose levels back - * to the global default. We have to do - * this so the logging levels get set - * correctly for program exit. - */ - _use_settings(&_default_settings); + out: + if (test_mode()) { + log_verbose("Test mode: Wiping internal cache"); + cache_destroy(); + } + + cmd->current_settings = cmd->default_settings; + _apply_settings(cmd); /* * free off any memory the command used. @@ -785,7 +698,7 @@ static int run_command(int argc, char **argv) return ret; } -static int split(char *str, int *argc, char **argv, int max) +static int _split(char *str, int *argc, char **argv, int max) { char *b = str, *e; *argc = 0; @@ -818,62 +731,13 @@ static void _init_rand(void) srand((unsigned int) time(NULL) + (unsigned int) getpid()); } -static void __init_log(struct config_file *cf) -{ - char *open_mode = "a"; - - const char *log_file; - - _default_settings.syslog = - find_config_int(cf->root, "log/syslog", '/', 1); - if (_default_settings.syslog != 1) - fin_syslog(); - - if (_default_settings.syslog > 1) - init_syslog(_default_settings.syslog); - - _default_settings.debug = - find_config_int(cf->root, "log/level", '/', 0); - init_debug(_default_settings.debug); - - _default_settings.verbose = - find_config_int(cf->root, "log/verbose", '/', 0); - init_verbose(_default_settings.verbose); - - init_indent(find_config_int(cf->root, "log/indent", '/', 1)); - - _default_settings.msg_prefix = find_config_str(cf->root, "log/prefix", - '/', DEFAULT_MSG_PREFIX); - init_msg_prefix(_default_settings.msg_prefix); - - _default_settings.cmd_name = find_config_int(cf->root, - "log/command_names", '/', - DEFAULT_CMD_NAME); - init_cmd_name(_default_settings.cmd_name); - - _default_settings.test = find_config_int(cf->root, "global/test", - '/', 0); - if (find_config_int(cf->root, "log/overwrite", '/', 0)) - open_mode = "w"; - - log_file = find_config_str(cf->root, "log/file", '/', 0); - if (log_file) { - /* set up the logging */ - if (!(_log = fopen(log_file, open_mode))) - log_error("Couldn't open log file %s", log_file); - else - init_log(_log); - } - -} - -static int _init_backup(struct config_file *cf) +static int _init_backup(struct cmd_context *cmd, struct config_tree *cf) { int days, min; char default_dir[PATH_MAX]; const char *dir; - if (!_sys_dir) { + if (!cmd->sys_dir) { log_warn("WARNING: Metadata changes will NOT be backed up"); backup_init(""); archive_init("", 0, 0); @@ -881,7 +745,7 @@ static int _init_backup(struct config_file *cf) } /* set up archiving */ - _default_settings.archive = + cmd->default_settings.archive = find_config_bool(cmd->cf->root, "backup/archive", '/', DEFAULT_ARCHIVE_ENABLED); @@ -891,10 +755,11 @@ static int _init_backup(struct config_file *cf) min = find_config_int(cmd->cf->root, "backup/retain_min", '/', DEFAULT_ARCHIVE_NUMBER); - if (lvm_snprintf(default_dir, sizeof(default_dir), "%s/%s", _sys_dir, - DEFAULT_ARCHIVE_SUBDIR) == -1) { + if (lvm_snprintf + (default_dir, sizeof(default_dir), "%s/%s", cmd->sys_dir, + DEFAULT_ARCHIVE_SUBDIR) == -1) { log_err("Couldn't create default archive path '%s/%s'.", - _sys_dir, DEFAULT_ARCHIVE_SUBDIR); + cmd->sys_dir, DEFAULT_ARCHIVE_SUBDIR); return 0; } @@ -907,14 +772,15 @@ static int _init_backup(struct config_file *cf) } /* set up the backup */ - _default_settings.backup = + cmd->default_settings.backup = find_config_bool(cmd->cf->root, "backup/backup", '/', DEFAULT_BACKUP_ENABLED); - if (lvm_snprintf(default_dir, sizeof(default_dir), "%s/%s", _sys_dir, - DEFAULT_BACKUP_SUBDIR) == -1) { + if (lvm_snprintf + (default_dir, sizeof(default_dir), "%s/%s", cmd->sys_dir, + DEFAULT_BACKUP_SUBDIR) == -1) { log_err("Couldn't create default backup path '%s/%s'.", - _sys_dir, DEFAULT_BACKUP_SUBDIR); + cmd->sys_dir, DEFAULT_BACKUP_SUBDIR); return 0; } @@ -929,269 +795,26 @@ static int _init_backup(struct config_file *cf) return 1; } -static int dev_cache_setup(struct config_file *cf) -{ - struct config_node *cn; - struct config_value *cv; - - if (!dev_cache_init()) { - stack; - return 0; - } - - if (!(cn = find_config_node(cf->root, "devices/scan", '/'))) { - if (!dev_cache_add_dir("/dev")) { - log_error("Failed to add /dev to internal " - "device cache"); - return 0; - } - log_verbose - ("device/scan not in config file: Defaulting to /dev"); - return 1; - } - - for (cv = cn->v; cv; cv = cv->next) { - if (cv->type != CFG_STRING) { - log_error("Invalid string in config file: " - "devices/scan"); - return 0; - } - - if (!dev_cache_add_dir(cv->v.str)) { - log_error("Failed to add %s to internal device cache", - cv->v.str); - return 0; - } - } - - return 1; -} - -static struct dev_filter *filter_components_setup(struct config_file *cf) -{ - struct config_node *cn; - struct dev_filter *f1, *f2, *f3; - - if (!(f2 = lvm_type_filter_create(_proc_dir))) - return 0; - - if (!(cn = find_config_node(cf->root, "devices/filter", '/'))) { - log_debug("devices/filter not found in config file: no regex " - "filter installed"); - return f2; - } - - if (!(f1 = regex_filter_create(cn->v))) { - log_error("Failed to create regex device filter"); - return f2; - } - - if (!(f3 = composite_filter_create(2, f1, f2))) { - log_error("Failed to create composite device filter"); - return f2; - } - - return f3; -} - -static struct dev_filter *filter_setup(struct config_file *cf) -{ - const char *lvm_cache; - struct dev_filter *f3, *f4; - struct stat st; - char cache_file[PATH_MAX]; - - _dump_filter = 0; - - if (!(f3 = filter_components_setup(cmd->cf))) - return 0; - - if (lvm_snprintf(cache_file, sizeof(cache_file), - "%s/.cache", _sys_dir) < 0) { - log_error("Persistent cache filename too long ('%s/.cache').", - _sys_dir); - return 0; - } - - lvm_cache = find_config_str(cf->root, "devices/cache", '/', cache_file); - - if (!(f4 = persistent_filter_create(f3, lvm_cache))) { - log_error("Failed to create persistent device filter"); - return 0; - } - - /* Should we ever dump persistent filter state? */ - if (find_config_int(cf->root, "devices/write_cache_state", '/', 1)) - _dump_filter = 1; - - if (!*_sys_dir) - _dump_filter = 0; - - if (!stat(lvm_cache, &st) && !persistent_filter_load(f4)) - log_verbose("Failed to load existing device cache from %s", - lvm_cache); - - return f4; -} - -static struct uuid_map *_init_uuid_map(struct dev_filter *filter) -{ - label_init(); - - /* add in the lvm1 labeller */ - if (!(_lvm1_label = lvm1_labeller_create())) { - log_err("Couldn't create lvm1 label handler."); - return 0; - } - - if (!(label_register_handler("lvm1", _lvm1_label))) { - log_err("Couldn't register lvm1 label handler."); - return 0; - } - - return uuid_map_create(filter); -} - -static void _exit_uuid_map(void) -{ - uuid_map_destroy(cmd->um); - label_exit(); - _lvm1_label->ops->destroy(_lvm1_label); - _lvm1_label = NULL; -} - -static int _get_env_vars(void) -{ - const char *e; - - /* Set to "" to avoid using any system directory */ - if ((e = getenv("LVM_SYSTEM_DIR"))) { - if (lvm_snprintf(_sys_dir, sizeof(_sys_dir), "%s", e) < 0) { - log_error("LVM_SYSTEM_DIR environment variable " - "is too long."); - return 0; - } - } - - return 1; -} - -static int init(void) +static struct cmd_context *_init(void) { - struct stat info; - char config_file[PATH_MAX] = ""; - const char *format; - mode_t old_umask; - - if (!setlocale(LC_ALL, "")) - log_error("setlocale failed"); - - if (!_get_env_vars()) - return 0; + struct cmd_context *cmd; - /* Create system directory if it doesn't already exist */ - if (!create_dir(_sys_dir)) - return 0; - - if (!(cmd = dbg_malloc(sizeof(*cmd)))) { - log_error("Failed to allocate command context"); - return 0; - } - - cmd->args = &the_args[0]; - - if (!(cmd->cf = create_config_file())) { + if (!(cmd = create_toolcontext(&the_args[0]))) { stack; - return 0; + return NULL; } - /* Use LOG_USER for syslog messages by default */ - init_syslog(LOG_USER); - _init_rand(); - if (*_sys_dir && lvm_snprintf(config_file, sizeof(config_file), - "%s/lvm.conf", _sys_dir) < 0) { - log_error("lvm_sys_dir was too long"); - return 0; - } - - if (stat(config_file, &info) != -1 && - !read_config(cmd->cf, config_file)) { - log_error("Failed to load config file %s", config_file); - return 0; - } - - __init_log(cmd->cf); - - _default_settings.umask = find_config_int(cmd->cf->root, - "global/umask", '/', - DEFAULT_UMASK); - - if ((old_umask = umask((mode_t) _default_settings.umask)) != - (mode_t) _default_settings.umask) - log_verbose("Set umask to %04o", _default_settings.umask); - - if (lvm_snprintf(_dev_dir, sizeof(_dev_dir), "%s/", - find_config_str(cmd->cf->root, "devices/dir", - '/', DEFAULT_DEV_DIR)) < 0) { - log_error("Device directory given in config file too long"); - return 0; - } - - cmd->dev_dir = _dev_dir; - dm_set_dev_dir(cmd->dev_dir); - - dm_log_init(print_log); - - if (lvm_snprintf(_proc_dir, sizeof(_proc_dir), "%s", - find_config_str(cmd->cf->root, "global/proc", - '/', DEFAULT_PROC_DIR)) < 0) { - log_error("Device directory given in config file too long"); - return 0; - } + if (!_init_backup(cmd, cmd->cf)) + return NULL; - if (!_init_backup(cmd->cf)) - return 0; + _apply_settings(cmd); - if (!dev_cache_setup(cmd->cf)) - return 0; - - if (!(cmd->filter = filter_setup(cmd->cf))) { - log_error("Failed to set up internal device filters"); - return 0; - } - - /* the uuid map uses the filter */ - if (!(cmd->um = _init_uuid_map(cmd->filter))) { - log_err("Failed to set up the uuid map."); - return 0; - } - - if (!(cmd->mem = pool_create(4 * 1024))) { - log_error("Command pool creation failed"); - return 0; - } - - /* FIXME Replace with list, dynamic libs etc. */ - if (!(cmd->fmt1 = create_lvm1_format(cmd))) - return 0; - - if (!(cmd->fmtt = create_text_format(cmd))) - return 0; - - format = find_config_str(cmd->cf->root, "global/format", '/', - DEFAULT_FORMAT); - if (!strcasecmp(format, "text")) - _default_settings.fmt = cmd->fmtt; - else /* "lvm1" */ - _default_settings.fmt = cmd->fmt1; - - _use_settings(&_default_settings); - return 1; + return cmd; } -static void __fin_commands(void) +static void _fin_commands(struct cmd_context *cmd) { int i; @@ -1201,33 +824,16 @@ static void __fin_commands(void) dbg_free(_commands); } -static void fin(void) +static void _fin(struct cmd_context *cmd) { - if (_dump_filter) - persistent_filter_dump(cmd->filter); - - cmd->fmt1->ops->destroy(cmd->fmt1); - cmd->fmtt->ops->destroy(cmd->fmtt); - cmd->filter->destroy(cmd->filter); - pool_destroy(cmd->mem); - vgcache_destroy(); - dev_cache_exit(); - destroy_config_file(cmd->cf); archive_exit(); backup_exit(); - _exit_uuid_map(); - dbg_free(cmd); - __fin_commands(); + _fin_commands(cmd); - dump_memory(); - fin_log(); - fin_syslog(); - - if (_log) - fclose(_log); + destroy_toolcontext(cmd); } -static int run_script(int argc, char **argv) +static int _run_script(struct cmd_context *cmd, int argc, char **argv) { FILE *script; @@ -1253,7 +859,7 @@ static int run_script(int argc, char **argv) ret = EINVALID_CMD_LINE; break; } - if (split(buffer, &argc, argv, MAX_ARGS) == MAX_ARGS) { + if (_split(buffer, &argc, argv, MAX_ARGS) == MAX_ARGS) { buffer[50] = '\0'; log_error("Too many arguments: %s", buffer); ret = EINVALID_CMD_LINE; @@ -1263,7 +869,7 @@ static int run_script(int argc, char **argv) continue; if (!strcmp(argv[0], "quit") || !strcmp(argv[0], "exit")) break; - run_command(argc, argv); + _run_command(cmd, argc, argv); } fclose(script); @@ -1337,7 +943,7 @@ static char *_list_args(const char *text, int state) char c; if (!(c = (the_args + com->valid_args[match_no++])->short_arg)) - continue; + continue; sprintf(s, "-%c", c); if (!strncmp(text, s, len)) @@ -1394,7 +1000,7 @@ static int _hist_file(char *buffer, size_t size) return 1; } -static void _read_history(void) +static void _read_history(struct cmd_context *cmd) { char hist_file[PATH_MAX]; @@ -1420,7 +1026,7 @@ static void _write_history(void) log_very_verbose("Couldn't write history to %s.", hist_file); } -static int shell(void) +static int _shell(struct cmd_context *cmd) { int argc, ret; char *input = NULL, *args[MAX_ARGS], **argv; @@ -1428,7 +1034,7 @@ static int shell(void) rl_readline_name = "lvm"; rl_attempted_completion_function = (CPPFunction *) _completion; - _read_history(); + _read_history(cmd); _interactive = 1; while (1) { @@ -1449,7 +1055,7 @@ static int shell(void) argv = args; - if (split(input, &argc, argv, MAX_ARGS) == MAX_ARGS) { + if (_split(input, &argc, argv, MAX_ARGS) == MAX_ARGS) { log_error("Too many arguments, sorry."); continue; } @@ -1468,14 +1074,65 @@ static int shell(void) break; } - ret = run_command(argc, argv); + ret = _run_command(cmd, argc, argv); if (ret == ENO_SUCH_CMD) log_error("No such command '%s'. Try 'help'.", argv[0]); + + _write_history(); } - _write_history(); free(input); return 0; } + #endif + +int main(int argc, char **argv) +{ + char *namebase, *base; + int ret, alias = 0; + struct cmd_context *cmd; + + if (!(cmd = _init())) + return -1; + + namebase = strdup(argv[0]); + base = basename(namebase); + while (*base == '/') + base++; + if (strcmp(base, "lvm")) + alias = 1; + free(namebase); + + _register_commands(); + +#ifdef READLINE_SUPPORT + if (!alias && argc == 1) { + ret = _shell(cmd); + goto out; + } +#endif + + if (!alias) { + if (argc < 2) { + log_fatal("Please supply an LVM command."); + _display_help(); + ret = EINVALID_CMD_LINE; + goto out; + } + + argc--; + argv++; + } + + ret = _run_command(cmd, argc, argv); + if ((ret == ENO_SUCH_CMD) && (!alias)) + ret = _run_script(cmd, argc, argv); + if (ret == ENO_SUCH_CMD) + log_error("No such command. Try 'help'."); + + out: + _fin(cmd); + return ret; +} diff --git a/tools/lvmdiskscan.c b/tools/lvmdiskscan.c index c573517ab..e65f80afa 100644 --- a/tools/lvmdiskscan.c +++ b/tools/lvmdiskscan.c @@ -29,7 +29,7 @@ int lvmdiskscan(struct cmd_context *cmd, int argc, char **argv) uint64_t size; struct dev_iter *iter; struct device *dev; - struct physical_volume *pv; + struct label *label; if (arg_count(cmd, lvmpartition_ARG)) log_print("WARNING: only considering LVM devices"); @@ -44,7 +44,7 @@ int lvmdiskscan(struct cmd_context *cmd, int argc, char **argv) /* Do scan */ for (dev = dev_iter_get(iter); dev; dev = dev_iter_get(iter)) { /* Try if it is a PV first */ - if ((pv = pv_read(cmd, dev_name(dev)))) { + if ((label_read(dev, &label))) { if (!dev_get_size(dev, &size)) { log_error("Couldn't get size of \"%s\"", dev_name(dev)); diff --git a/tools/lvremove.c b/tools/lvremove.c index 0e93866ec..a41c1e59d 100644 --- a/tools/lvremove.c +++ b/tools/lvremove.c @@ -20,22 +20,8 @@ #include "tools.h" -static int lvremove_single(struct cmd_context *cmd, struct logical_volume *lv); - -int lvremove(struct cmd_context *cmd, int argc, char **argv) -{ - if (!argc) { - log_error("Please enter one or more logical volume paths"); - return EINVALID_CMD_LINE; - } - - if (!driver_is_loaded()) - return ECMD_FAILED; - - return process_each_lv(cmd, argc, argv, LCK_VG_READ, &lvremove_single); -} - -static int lvremove_single(struct cmd_context *cmd, struct logical_volume *lv) +static int lvremove_single(struct cmd_context *cmd, struct logical_volume *lv, + void *handle) { struct volume_group *vg; struct dm_info info; @@ -95,7 +81,7 @@ static int lvremove_single(struct cmd_context *cmd, struct logical_volume *lv) } /* store it on disks */ - if (vg_write(vg)) + if (!vg_write(vg)) return ECMD_FAILED; backup(vg); @@ -103,3 +89,14 @@ static int lvremove_single(struct cmd_context *cmd, struct logical_volume *lv) log_print("Logical volume \"%s\" successfully removed", lv->name); return 0; } + +int lvremove(struct cmd_context *cmd, int argc, char **argv) +{ + if (!argc) { + log_error("Please enter one or more logical volume paths"); + return EINVALID_CMD_LINE; + } + + return process_each_lv(cmd, argc, argv, LCK_VG_READ, NULL, + &lvremove_single); +} diff --git a/tools/lvrename.c b/tools/lvrename.c index e84e3c910..b061abd89 100644 --- a/tools/lvrename.c +++ b/tools/lvrename.c @@ -26,6 +26,7 @@ int lvrename(struct cmd_context *cmd, int argc, char **argv) char *lv_name_old, *lv_name_new; char *vg_name, *vg_name_new, *vg_name_old; char *st; + int consistent = 1; struct volume_group *vg; struct logical_volume *lv; @@ -53,7 +54,7 @@ int lvrename(struct cmd_context *cmd, int argc, char **argv) return EINVALID_CMD_LINE; } - if (!is_valid_chars(vg_name)) { + if (!validate_vgname(vg_name)) { log_error("Please provide a valid volume group name"); return EINVALID_CMD_LINE; } @@ -86,7 +87,14 @@ int lvrename(struct cmd_context *cmd, int argc, char **argv) return ECMD_FAILED; } - if (!is_valid_chars(lv_name_new)) { + /* FIXME Remove this restriction eventually */ + if (!strncmp(lv_name_new, "snapshot", 8)) { + log_error("Names starting \"snapshot\" are reserved. " + "Please choose a different LV name."); + return ECMD_FAILED; + } + + if (!validate_vgname(lv_name_new)) { log_error ("New logical volume name \"%s\" has invalid characters", lv_name_new); @@ -98,9 +106,6 @@ int lvrename(struct cmd_context *cmd, int argc, char **argv) return EINVALID_CMD_LINE; } - if (!driver_is_loaded()) - return ECMD_FAILED; - log_verbose("Checking for existing volume group \"%s\"", vg_name); if (!lock_vol(cmd, vg_name, LCK_VG_WRITE)) { @@ -108,7 +113,7 @@ int lvrename(struct cmd_context *cmd, int argc, char **argv) return ECMD_FAILED; } - if (!(vg = vg_read(cmd, vg_name))) { + if (!(vg = vg_read(cmd, vg_name, &consistent))) { log_error("Volume group \"%s\" doesn't exist", vg_name); goto error; } diff --git a/tools/lvresize.c b/tools/lvresize.c index 974c29b83..b058d8f51 100644 --- a/tools/lvresize.c +++ b/tools/lvresize.c @@ -39,6 +39,7 @@ int lvresize(struct cmd_context *cmd, int argc, char **argv) struct list *pvh, *segh; struct lv_list *lvl; int opt = 0; + int consistent = 1; enum { LV_ANY = 0, @@ -94,9 +95,6 @@ int lvresize(struct cmd_context *cmd, int argc, char **argv) if ((st = strrchr(lv_name, '/'))) lv_name = st + 1; - if (!driver_is_loaded()) - return ECMD_FAILED; - /* does VG exist? */ log_verbose("Finding volume group %s", vg_name); if (!lock_vol(cmd, vg_name, LCK_VG_WRITE)) { @@ -104,7 +102,7 @@ int lvresize(struct cmd_context *cmd, int argc, char **argv) return ECMD_FAILED; } - if (!(vg = vg_read(cmd, vg_name))) { + if (!(vg = vg_read(cmd, vg_name, &consistent))) { log_error("Volume group %s doesn't exist", vg_name); goto error; } @@ -193,10 +191,10 @@ int lvresize(struct cmd_context *cmd, int argc, char **argv) if (extents > lv->le_count && !(stripes == 1 || (stripes > 1 && stripesize))) { list_iterate(segh, &lv->segments) { - struct stripe_segment *seg; + struct lv_segment *seg; uint32_t sz, str; - seg = list_item(segh, struct stripe_segment); + seg = list_item(segh, struct lv_segment); sz = seg->stripe_size; str = seg->stripes; @@ -237,10 +235,10 @@ int lvresize(struct cmd_context *cmd, int argc, char **argv) "when reducing"); list_iterate(segh, &lv->segments) { - struct stripe_segment *seg; + struct lv_segment *seg; uint32_t seg_extents; - seg = list_item(segh, struct stripe_segment); + seg = list_item(segh, struct lv_segment); seg_extents = seg->len; seg_stripesize = seg->stripe_size; @@ -261,7 +259,7 @@ int lvresize(struct cmd_context *cmd, int argc, char **argv) log_error("Stripesize for striped segment should not be 0!"); goto error_cmdline; } - + if ((stripes > 1)) { if (!(stripesize_extents = stripesize / vg->extent_size)) stripesize_extents = 1; diff --git a/tools/lvscan.c b/tools/lvscan.c index 044e2e6ba..d0449288a 100644 --- a/tools/lvscan.c +++ b/tools/lvscan.c @@ -20,41 +20,8 @@ #include "tools.h" -static int lvscan_single(struct cmd_context *cmd, struct logical_volume *lv); - -int lvscan(struct cmd_context *cmd, int argc, char **argv) -{ - if (argc) { - log_error("No additional command line arguments allowed"); - return EINVALID_CMD_LINE; - } - - return process_each_lv(cmd, argc, argv, LCK_VG_READ, &lvscan_single); - -/*********** FIXME Count! Add private struct to process_each* -* if (!lv_total) -* log_print("no logical volumes found"); -* else { -* log_print -* ("%d logical volumes with %s total in %d volume group%s", -* lv_total, (dummy = -* display_size(lv_capacity_total / 2, SIZE_SHORT)), -* vg_total, vg_total == 1 ? "" : "s"); -* dbg_free(dummy); -* dummy = NULL; -* if (lv_active > 0) -* printf("%d active", lv_active); -* if (lv_active > 0 && lv_total - lv_active > 0) -* printf(" / "); -* if (lv_total - lv_active > 0) -* printf("%d inactive", lv_total - lv_active); -* printf(" logical volumes\n"); -* } -*************/ - -} - -static int lvscan_single(struct cmd_context *cmd, struct logical_volume *lv) +static int lvscan_single(struct cmd_context *cmd, struct logical_volume *lv, + void *handle) { struct dm_info info; int lv_total = 0; @@ -63,7 +30,6 @@ static int lvscan_single(struct cmd_context *cmd, struct logical_volume *lv) char *dummy; const char *active_str, *snapshot_str; -/* FIXME Add -D arg to skip this! */ if (lv_info(lv, &info) && info.exists) active_str = "ACTIVE "; else @@ -76,48 +42,28 @@ static int lvscan_single(struct cmd_context *cmd, struct logical_volume *lv) else snapshot_str = " "; -/********** FIXME Snapshot - if (lv->status & SNAPSHOT) - dummy = - display_size(lv->lv_remap_end * - lv->lv_chunk_size / 2, - SIZE_SHORT); - else -***********/ dummy = display_size(lv->size / 2, SIZE_SHORT); - log_print("%s%s '%s%s/%s' [%s]%s", active_str, snapshot_str, + log_print("%s%s '%s%s/%s' [%s] %s", active_str, snapshot_str, cmd->dev_dir, lv->vg->name, lv->name, dummy, get_alloc_string(lv->alloc)); dbg_free(dummy); - /* FIXME sprintf? */ - -/*********** FIXME Handle segments? - if (lv->segments[0]->stripes > 1 && !(lv->status & SNAPSHOT)) - log_print(" striped[%u]", lv->segments[0]->stripes); -****************/ - -/******** FIXME Device number display & Snapshot - if (arg_count(cmd,blockdevice_ARG)) - printf(" %d:%d", - MAJOR(lv->lv_dev), - MINOR(lv->lv_dev)); - else - if (lv->status & SNAPSHOT) - printf(" of %s", lv->lv_snapshot_org->name); -*****************/ - lv_total++; -/******** FIXME Snapshot - if (lv->status & SNAPSHOT) - lv_capacity_total += - lv->lv_remap_end * lv->lv_chunk_size - else -********/ lv_capacity_total += lv->size; return 0; } + +int lvscan(struct cmd_context *cmd, int argc, char **argv) +{ + if (argc) { + log_error("No additional command line arguments allowed"); + return EINVALID_CMD_LINE; + } + + return process_each_lv(cmd, argc, argv, LCK_VG_READ, NULL, + &lvscan_single); +} diff --git a/tools/pvchange.c b/tools/pvchange.c index b509f007c..189c94d33 100644 --- a/tools/pvchange.c +++ b/tools/pvchange.c @@ -20,76 +20,19 @@ #include "tools.h" -int pvchange_single(struct cmd_context *cmd, struct physical_volume *pv); +/* FIXME Locking. PVs in VG. */ -int pvchange(struct cmd_context *cmd, int argc, char **argv) -{ - int opt = 0; - int done = 0; - int total = 0; - - struct physical_volume *pv; - char *pv_name; - - struct list *pvh, *pvs; - - if (arg_count(cmd, allocatable_ARG) == 0) { - log_error("Please give the x option"); - return EINVALID_CMD_LINE; - } - - if (!(arg_count(cmd, all_ARG)) && !argc) { - log_error("Please give a physical volume path"); - return EINVALID_CMD_LINE; - } - - if (arg_count(cmd, all_ARG) && argc) { - log_error("Option a and PhysicalVolumePath are exclusive"); - return EINVALID_CMD_LINE; - } - - if (argc) { - log_verbose("Using physical volume(s) on command line"); - for (; opt < argc; opt++) { - pv_name = argv[opt]; - if (!(pv = pv_read(cmd, pv_name))) { - log_error - ("Failed to read physical volume \"%s\"", - pv_name); - continue; - } - total++; - done += pvchange_single(cmd, pv); - } - } else { - log_verbose("Scanning for physical volume names"); - if (!(pvs = get_pvs(cmd))) { - return ECMD_FAILED; - } - - list_iterate(pvh, pvs) { - total++; - done += pvchange_single(cmd, - list_item(pvh, - struct pv_list)->pv); - } - } - - log_print("%d physical volume%s changed / %d physical volume%s " - "not changed", - done, done > 1 ? "s" : "", - total - done, total - done > 1 ? "s" : ""); - - return 0; -} - -int pvchange_single(struct cmd_context *cmd, struct physical_volume *pv) +int pvchange_single(struct cmd_context *cmd, struct physical_volume *pv, + void *handle) { struct volume_group *vg = NULL; struct pv_list *pvl; + struct list mdas; + uint64_t sector; const char *pv_name = dev_name(pv->dev); + int consistent = 1; int allocatable = !strcmp(arg_str_value(cmd, allocatable_ARG, "n"), "y"); @@ -103,7 +46,7 @@ int pvchange_single(struct cmd_context *cmd, struct physical_volume *pv) return ECMD_FAILED; } - if (!(vg = vg_read(cmd, pv->vg_name))) { + if (!(vg = vg_read(cmd, pv->vg_name, &consistent))) { unlock_vg(cmd, pv->vg_name); log_error("Unable to find volume group of \"%s\"", pv_name); @@ -132,6 +75,18 @@ int pvchange_single(struct cmd_context *cmd, struct physical_volume *pv) pv = pvl->pv; if (!archive(vg)) return 0; + } else { + if (!lock_vol(cmd, ORPHAN, LCK_VG_WRITE)) { + log_error("Can't get lock for orphans"); + return ECMD_FAILED; + } + + if (!(pv = pv_read(cmd, pv_name, &mdas, §or))) { + unlock_vg(cmd, ORPHAN); + log_error("Unable to read PV \"%s\"", pv_name); + return 0; + } + } /* change allocatability for a PV */ @@ -140,6 +95,8 @@ int pvchange_single(struct cmd_context *cmd, struct physical_volume *pv) pv_name); if (*pv->vg_name) unlock_vg(cmd, pv->vg_name); + else + unlock_vg(cmd, ORPHAN); return 0; } @@ -148,6 +105,8 @@ int pvchange_single(struct cmd_context *cmd, struct physical_volume *pv) pv_name); if (*pv->vg_name) unlock_vg(cmd, pv->vg_name); + else + unlock_vg(cmd, ORPHAN); return 0; } @@ -172,14 +131,82 @@ int pvchange_single(struct cmd_context *cmd, struct physical_volume *pv) backup(vg); unlock_vg(cmd, pv->vg_name); } else { - if (!(pv_write(cmd, pv))) { + if (!(pv_write(cmd, pv, &mdas, sector))) { + unlock_vg(cmd, ORPHAN); log_error("Failed to store physical volume \"%s\"", pv_name); return 0; } + unlock_vg(cmd, ORPHAN); } log_print("Physical volume \"%s\" changed", pv_name); return 1; } + +int pvchange(struct cmd_context *cmd, int argc, char **argv) +{ + int opt = 0; + int done = 0; + int total = 0; + + struct physical_volume *pv; + char *pv_name; + + struct list *pvh, *pvs; + struct list mdas; + + list_init(&mdas); + + if (arg_count(cmd, allocatable_ARG) == 0) { + log_error("Please give the x option"); + return EINVALID_CMD_LINE; + } + + if (!(arg_count(cmd, all_ARG)) && !argc) { + log_error("Please give a physical volume path"); + return EINVALID_CMD_LINE; + } + + if (arg_count(cmd, all_ARG) && argc) { + log_error("Option a and PhysicalVolumePath are exclusive"); + return EINVALID_CMD_LINE; + } + + if (argc) { + log_verbose("Using physical volume(s) on command line"); + for (; opt < argc; opt++) { + pv_name = argv[opt]; + /* FIXME Read VG instead - pv_read will fail */ + if (!(pv = pv_read(cmd, pv_name, &mdas, NULL))) { + log_error + ("Failed to read physical volume \"%s\"", + pv_name); + continue; + } + total++; + done += pvchange_single(cmd, pv, NULL); + } + } else { + log_verbose("Scanning for physical volume names"); + if (!(pvs = get_pvs(cmd))) { + return ECMD_FAILED; + } + + list_iterate(pvh, pvs) { + total++; + done += pvchange_single(cmd, + list_item(pvh, + struct pv_list)->pv, + NULL); + } + } + + log_print("%d physical volume%s changed / %d physical volume%s " + "not changed", + done, done > 1 ? "s" : "", + total - done, total - done > 1 ? "s" : ""); + + return 0; +} diff --git a/tools/pvcreate.c b/tools/pvcreate.c index 0cc1d95fa..7ca11ede1 100644 --- a/tools/pvcreate.c +++ b/tools/pvcreate.c @@ -19,6 +19,7 @@ */ #include "tools.h" +#include "defaults.h" const char _really_init[] = "Really INITIALIZE physical volume \"%s\" of volume group \"%s\" [y/n]? "; @@ -39,7 +40,7 @@ static int pvcreate_check(struct cmd_context *cmd, const char *name) } /* is there a pv here already */ - if (!(pv = pv_read(cmd, name))) + if (!(pv = pv_read(cmd, name, NULL, NULL))) return 1; /* orphan ? */ @@ -72,20 +73,28 @@ static int pvcreate_check(struct cmd_context *cmd, const char *name) return 1; } -static void pvcreate_single(struct cmd_context *cmd, const char *pv_name) +static void pvcreate_single(struct cmd_context *cmd, const char *pv_name, + void *handle) { - struct physical_volume *pv; - struct format_instance *fid; + struct physical_volume *pv, *existing_pv; struct id id, *idp = NULL; char *uuid; uint64_t size = 0; struct device *dev; + struct list mdas; + int pvmetadatacopies; + uint64_t pvmetadatasize; + struct volume_group *vg; + char *restorefile; + uint64_t pe_start = 0; + uint32_t extent_count = 0, extent_size = 0; if (arg_count(cmd, uuidstr_ARG)) { uuid = arg_str_value(cmd, uuidstr_ARG, ""); if (!id_read_format(&id, uuid)) return; - if ((dev = uuid_map_lookup(cmd->um, &id))) { + if ((dev = device_from_pvid(cmd, &id)) && + (dev != dev_cache_get(pv_name, cmd->filter))) { log_error("uuid %s already in use on \"%s\"", uuid, dev_name(dev)); return; @@ -93,32 +102,84 @@ static void pvcreate_single(struct cmd_context *cmd, const char *pv_name) idp = &id; } - if (!pvcreate_check(cmd, pv_name)) + if (arg_count(cmd, restorefile_ARG)) { + restorefile = arg_str_value(cmd, restorefile_ARG, ""); + /* The uuid won't already exist */ + init_partial(1); + if (!(vg = backup_read_vg(cmd, NULL, restorefile))) { + log_error("Unable to read volume group from %s", + restorefile); + return; + } + init_partial(0); + if (!(existing_pv = find_pv_in_vg_by_uuid(vg, idp))) { + log_error("Can't find uuid %s in backup file %s", + uuid, restorefile); + return; + } + pe_start = existing_pv->pe_start; + extent_size = existing_pv->pe_size; + extent_count = existing_pv->pe_count; + } + + if (!lock_vol(cmd, "", LCK_VG_WRITE)) { + log_error("Can't get lock for orphan PVs"); return; + } + + if (!pvcreate_check(cmd, pv_name)) + goto error; size = arg_int64_value(cmd, physicalvolumesize_ARG, 0) * 2; - /* FIXME Use config file/cmd line to specify format */ - if (!(fid = cmd->fmt1->ops->create_instance(cmd->fmt1, NULL, NULL))) { - log_error("Failed to create format1 instance"); - return; + pvmetadatasize = arg_int64_value(cmd, metadatasize_ARG, 0) * 2; + if (!pvmetadatasize) + pvmetadatasize = find_config_int(cmd->cf->root, + "metadata/pvmetadatasize", + '/', DEFAULT_PVMETADATASIZE); + + pvmetadatacopies = arg_int_value(cmd, metadatacopies_ARG, -1); + if (pvmetadatacopies < 0) + pvmetadatacopies = find_config_int(cmd->cf->root, + "metadata/pvmetadatacopies", + '/', + DEFAULT_PVMETADATACOPIES); + + if (!(dev = dev_cache_get(pv_name, cmd->filter))) { + log_error("%s: Couldn't find device.", pv_name); + goto error; } - if (!(pv = pv_create(fid, pv_name, idp, size))) { + + list_init(&mdas); + if (!(pv = pv_create(cmd->fmt, dev, idp, size, pe_start, + extent_count, extent_size, + pvmetadatacopies, pvmetadatasize, &mdas))) { log_error("Failed to setup physical volume \"%s\"", pv_name); - return; + goto error; } log_verbose("Set up physical volume for \"%s\" with %" PRIu64 - " sectors", pv_name, pv->size); + " available sectors", pv_name, pv->size); + + /* Wipe existing label first */ + if (!label_remove(pv->dev)) { + log_error("Failed to wipe existing label on %s", pv_name); + goto error; + } log_very_verbose("Writing physical volume data to disk \"%s\"", pv_name); - if (!(pv_write(cmd, pv))) { + if (!(pv_write(cmd, pv, &mdas, arg_int_value(cmd, labelsector_ARG, + DEFAULT_LABELSECTOR)))) { log_error("Failed to write physical volume \"%s\"", pv_name); - return; + goto error; } log_print("Physical volume \"%s\" successfully created", pv_name); + + error: + unlock_vg(cmd, ""); + return; } int pvcreate(struct cmd_context *cmd, int argc, char **argv) @@ -130,6 +191,11 @@ int pvcreate(struct cmd_context *cmd, int argc, char **argv) return EINVALID_CMD_LINE; } + if (arg_count(cmd, restorefile_ARG) && !arg_count(cmd, uuidstr_ARG)) { + log_error("--uuid is required with --restorefile"); + return EINVALID_CMD_LINE; + } + if (arg_count(cmd, uuidstr_ARG) && argc != 1) { log_error("Can only set uuid on one volume at once"); return EINVALID_CMD_LINE; @@ -140,8 +206,27 @@ int pvcreate(struct cmd_context *cmd, int argc, char **argv) return EINVALID_CMD_LINE; } + if (arg_int_value(cmd, labelsector_ARG, 0) >= LABEL_SCAN_SECTORS) { + log_error("labelsector must be less than %lu", + LABEL_SCAN_SECTORS); + return EINVALID_CMD_LINE; + } + + if (!(cmd->fmt->features & FMT_MDAS) && + (arg_count(cmd, metadatacopies_ARG) || + arg_count(cmd, metadatasize_ARG))) { + log_error("Metadata parameters only apply to text format"); + return EINVALID_CMD_LINE; + } + + if (arg_count(cmd, metadatacopies_ARG) && + arg_int_value(cmd, metadatacopies_ARG, -1) > 2) { + log_error("Metadatacopies may only be 0, 1 or 2"); + return EINVALID_CMD_LINE; + } + for (i = 0; i < argc; i++) { - pvcreate_single(cmd, argv[i]); + pvcreate_single(cmd, argv[i], NULL); pool_empty(cmd->mem); } diff --git a/tools/pvdisplay.c b/tools/pvdisplay.c index b526cc9bc..ff4d121a1 100644 --- a/tools/pvdisplay.c +++ b/tools/pvdisplay.c @@ -20,44 +20,8 @@ #include "tools.h" -void pvdisplay_single(struct cmd_context *cmd, struct physical_volume *pv); - -int pvdisplay(struct cmd_context *cmd, int argc, char **argv) -{ - int opt = 0; - - struct list *pvh, *pvs; - struct physical_volume *pv; - - if (arg_count(cmd, colon_ARG) && arg_count(cmd, maps_ARG)) { - log_error("Option -v not allowed with option -c"); - return EINVALID_CMD_LINE; - } - - if (argc) { - log_very_verbose("Using physical volume(s) on command line"); - - for (; opt < argc; opt++) { - if (!(pv = pv_read(cmd, argv[opt]))) { - log_error("Failed to read physical " - "volume \"%s\"", argv[opt]); - continue; - } - pvdisplay_single(cmd, pv); - } - } else { - log_verbose("Scanning for physical volume names"); - if (!(pvs = get_pvs(cmd))) - return ECMD_FAILED; - - list_iterate(pvh, pvs) - pvdisplay_single(cmd, list_item(pvh, struct pv_list)->pv); - } - - return 0; -} - -void pvdisplay_single(struct cmd_context *cmd, struct physical_volume *pv) +void pvdisplay_single(struct cmd_context *cmd, struct physical_volume *pv, + void *handle) { char *sz; uint64_t size; @@ -80,50 +44,57 @@ void pvdisplay_single(struct cmd_context *cmd, struct physical_volume *pv) log_print("Physical volume \"%s\" of volume group \"%s\" " "is exported", pv_name, pv->vg_name); -/********* FIXME - log_error("no physical volume identifier on \"%s\"" , pv_name); -*********/ - if (!pv->vg_name) { log_print("\"%s\" is a new physical volume of \"%s\"", pv_name, (sz = display_size(size / 2, SIZE_SHORT))); dbg_free(sz); } -/* FIXME: Check active - no point? - log_very_verbose("checking physical volume activity" ); - pv_check_active ( pv->vg_name, pv->pv_name) - pv_status ( pv->vg_name, pv->pv_name, &pv) -*/ - -/* FIXME: Check consistency - do this when reading metadata BUT trigger mesgs - log_very_verbose("checking physical volume consistency" ); - ret = pv_check_consistency (pv) -*/ - if (arg_count(cmd, colon_ARG)) { pvdisplay_colons(pv); return; } - pvdisplay_full(pv); + pvdisplay_full(pv, handle); if (!arg_count(cmd, maps_ARG)) return; -/******* FIXME - if (pv->pe_alloc_count) { - if (!(pv->pe = pv_read_pe(pv_name, pv))) - goto pvdisplay_device_out; - if (!(lvs = pv_read_lvs(pv))) { - log_error("Failed to read LVs on \"%s\"", pv->pv_name); - goto pvdisplay_device_out; + return; +} + +int pvdisplay(struct cmd_context *cmd, int argc, char **argv) +{ + int opt = 0; + + struct list *pvh, *pvs; + struct physical_volume *pv; + + if (arg_count(cmd, colon_ARG) && arg_count(cmd, maps_ARG)) { + log_error("Option -v not allowed with option -c"); + return EINVALID_CMD_LINE; + } + + if (argc) { + log_very_verbose("Using physical volume(s) on command line"); + + for (; opt < argc; opt++) { + if (!(pv = pv_read(cmd, argv[opt], NULL, NULL))) { + log_error("Failed to read physical " + "volume \"%s\"", argv[opt]); + continue; + } + pvdisplay_single(cmd, pv, NULL); } - pv_display_pe_text(pv, pv->pe, lvs); - } else - log_print("no logical volume on physical volume \"%s\"", - pv_name); -**********/ + } else { + log_verbose("Scanning for physical volume names"); + if (!(pvs = get_pvs(cmd))) + return ECMD_FAILED; - return; + list_iterate(pvh, pvs) + pvdisplay_single(cmd, list_item(pvh, struct pv_list)->pv, + NULL); + } + + return 0; } diff --git a/tools/pvremove.c b/tools/pvremove.c new file mode 100644 index 000000000..3410378f9 --- /dev/null +++ b/tools/pvremove.c @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2002 Sistina Software + * + * pvcreate is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * pvcreate is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with LVM; see the file COPYING. If not, write to + * the Free Software Foundation, 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "tools.h" +#include "defaults.h" + +const char _really_wipe[] = + "Really WIPE LABELS from physical volume \"%s\" of volume group \"%s\" [y/n]? "; + +/* + * Decide whether it is "safe" to wipe the labels on this device. + * 0 indicates we may not. + */ +static int pvremove_check(struct cmd_context *cmd, const char *name) +{ + struct physical_volume *pv; + + /* is the partition type set correctly ? */ + if ((arg_count(cmd, force_ARG) < 1) && !is_lvm_partition(name)) { + log_error("%s: Not LVM partition type: use -f to override", + name); + return 0; + } + + /* is there a pv here already */ + if (!(pv = pv_read(cmd, name, NULL, NULL))) + return 1; + + /* orphan ? */ + if (!pv->vg_name[0]) + 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); + return 0; + } + + /* prompt */ + if (!arg_count(cmd, yes_ARG) && + yes_no_prompt(_really_wipe, name, pv->vg_name) == 'n') { + log_print("%s: physical volume label not removed", name); + return 0; + } + + if (arg_count(cmd, force_ARG)) { + log_print("WARNING: Wiping physical volume label from " + "%s%s%s%s", name, + pv->vg_name[0] ? " of volume group \"" : "", + pv->vg_name[0] ? pv->vg_name : "", + pv->vg_name[0] ? "\"" : ""); + } + + return 1; +} + +static void pvremove_single(struct cmd_context *cmd, const char *pv_name, + void *handle) +{ + struct device *dev; + + if (!lock_vol(cmd, "", LCK_VG_WRITE)) { + log_error("Can't get lock for orphan PVs"); + return; + } + + if (!pvremove_check(cmd, pv_name)) + goto error; + + if (!(dev = dev_cache_get(pv_name, cmd->filter))) { + log_error("%s: Couldn't find device.", pv_name); + goto error; + } + + /* Wipe existing label(s) */ + if (!label_remove(dev)) { + log_error("Failed to wipe existing label(s) on %s", pv_name); + goto error; + } + + log_print("Labels on physical volume \"%s\" successfully wiped", + pv_name); + + error: + unlock_vg(cmd, ""); + return; +} + +int pvremove(struct cmd_context *cmd, int argc, char **argv) +{ + int i; + + if (!argc) { + log_error("Please enter a physical volume path"); + return EINVALID_CMD_LINE; + } + + if (arg_count(cmd, yes_ARG) && !arg_count(cmd, force_ARG)) { + log_error("Option y can only be given with option f"); + return EINVALID_CMD_LINE; + } + + for (i = 0; i < argc; i++) { + pvremove_single(cmd, argv[i], NULL); + pool_empty(cmd->mem); + } + + return 0; +} diff --git a/tools/pvscan.c b/tools/pvscan.c index 868dea473..3f3b2f1ba 100644 --- a/tools/pvscan.c +++ b/tools/pvscan.c @@ -20,11 +20,95 @@ #include "tools.h" -void pvscan_display_single(struct cmd_context *cmd, struct physical_volume *pv); - int pv_max_name_len = 0; int vg_max_name_len = 0; +void pvscan_display_single(struct cmd_context *cmd, struct physical_volume *pv, + void *handle) +{ + char uuid[64]; + int vg_name_len = 0; + + char *s1, *s2; + + 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", dev_name(pv->dev)); + return; + } + + if (arg_count(cmd, verbose_ARG) > 1) { + /* FIXME As per pv_display! Drop through for now. */ + /* pv_show(pv); */ + + /* FIXME - Moved to Volume Group structure */ + /* log_print("System Id %s", pv->vg->system_id); */ + + /* log_print(" "); */ + /* return; */ + } + + memset(pv_tmp_name, 0, sizeof(pv_tmp_name)); + + vg_name_len = strlen(pv->vg_name) + 1; + + if (arg_count(cmd, uuid_ARG)) { + if (!id_write_format(&pv->id, uuid, sizeof(uuid))) { + stack; + return; + } + + sprintf(pv_tmp_name, "%-*s with UUID %s", + pv_max_name_len - 2, dev_name(pv->dev), uuid); + } else { + sprintf(pv_tmp_name, "%s", dev_name(pv->dev)); + } + + if (!*pv->vg_name) { + log_print("PV %-*s %-*s %s [%s]", + pv_max_name_len, pv_tmp_name, + vg_max_name_len, " ", + pv->fmt ? pv->fmt->name : " ", + (s1 = display_size(pv->size / 2, SIZE_SHORT))); + dbg_free(s1); + return; + } + + if (pv->status & EXPORTED_VG) { + strncpy(vg_name_this, pv->vg_name, 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, (s1 = + display_size(pv->pe_count * + pv->pe_size / 2, + SIZE_SHORT)), + (s2 = display_size((pv->pe_count - pv->pe_alloc_count) + * pv->pe_size / 2, SIZE_SHORT))); + dbg_free(s1); + dbg_free(s2); + return; + } + + sprintf(vg_tmp_name, "%s", pv->vg_name); + 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 : " ", + (s1 = display_size(pv->pe_count * pv->pe_size / 2, SIZE_SHORT)), + (s2 = + display_size((pv->pe_count - pv->pe_alloc_count) * pv->pe_size / + 2, SIZE_SHORT))); + dbg_free(s1); + dbg_free(s2); + + return; +} + int pvscan(struct cmd_context *cmd, int argc, char **argv) { int new_pvs_found = 0; @@ -56,6 +140,9 @@ int pvscan(struct cmd_context *cmd, int argc, char **argv) log_verbose("Wiping cache of LVM-capable devices"); persistent_filter_wipe(cmd->filter); + log_verbose("Wiping internal cache"); + cache_destroy(); + log_verbose("Walking through all physical volumes"); if (!(pvs = get_pvs(cmd))) return ECMD_FAILED; @@ -108,7 +195,8 @@ int pvscan(struct cmd_context *cmd, int argc, char **argv) vg_max_name_len += 2; list_iterate(pvh, pvs) - pvscan_display_single(cmd, list_item(pvh, struct pv_list)->pv); + pvscan_display_single(cmd, list_item(pvh, struct pv_list)->pv, + NULL); if (!pvs_found) { log_print("No matching physical volumes found"); @@ -128,87 +216,3 @@ int pvscan(struct cmd_context *cmd, int argc, char **argv) return 0; } - -void pvscan_display_single(struct cmd_context *cmd, struct physical_volume *pv) -{ - char uuid[64]; - int vg_name_len = 0; - - char *s1, *s2; - - 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", dev_name(pv->dev)); - return; - } - - if (arg_count(cmd, verbose_ARG) > 1) { - /* FIXME As per pv_display! Drop through for now. */ - /* pv_show(pv); */ - - /* FIXME - Moved to Volume Group structure */ - /* log_print("System Id %s", pv->vg->system_id); */ - - /* log_print(" "); */ - /* return; */ - } - - memset(pv_tmp_name, 0, sizeof(pv_tmp_name)); - - vg_name_len = strlen(pv->vg_name) + 1; - - if (arg_count(cmd, uuid_ARG)) { - if (!id_write_format(&pv->id, uuid, sizeof(uuid))) { - stack; - return; - } - - sprintf(pv_tmp_name, "%-*s with UUID %s", - pv_max_name_len - 2, dev_name(pv->dev), uuid); - } else { - sprintf(pv_tmp_name, "%s", dev_name(pv->dev)); - } - - if (!*pv->vg_name) { - log_print("PV %-*s %-*s [%s]", - pv_max_name_len, pv_tmp_name, - vg_max_name_len, " ", - (s1 = display_size(pv->size / 2, SIZE_SHORT))); - dbg_free(s1); - return; - } - - if (pv->status & EXPORTED_VG) { - strncpy(vg_name_this, pv->vg_name, 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, (s1 = - display_size(pv->pe_count * - pv->pe_size / 2, - SIZE_SHORT)), - (s2 = display_size((pv->pe_count - pv->pe_alloc_count) - * pv->pe_size / 2, SIZE_SHORT))); - dbg_free(s1); - dbg_free(s2); - return; - } - - sprintf(vg_tmp_name, "%s", pv->vg_name); - 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->fid ? pv->fid->fmt->name : " ", - (s1 = display_size(pv->pe_count * pv->pe_size / 2, SIZE_SHORT)), - (s2 = - display_size((pv->pe_count - pv->pe_alloc_count) * pv->pe_size / - 2, SIZE_SHORT))); - dbg_free(s1); - dbg_free(s2); - - return; -} diff --git a/tools/stub.h b/tools/stub.h index 185535c07..8ffefe134 100644 --- a/tools/stub.h +++ b/tools/stub.h @@ -6,10 +6,10 @@ #define unimplemented \ { log_error("Command not implemented yet."); return ECMD_FAILED;} +/*int e2fsadm(struct cmd_context *cmd, int argc, char **argv) unimplemented*/ int lvmsadc(struct cmd_context *cmd, int argc, char **argv) unimplemented int lvmsar(struct cmd_context *cmd, int argc, char **argv) unimplemented int pvdata(struct cmd_context *cmd, int argc, char **argv) unimplemented int pvmove(struct cmd_context *cmd, int argc, char **argv) unimplemented int pvresize(struct cmd_context *cmd, int argc, char **argv) unimplemented int vgmknodes(struct cmd_context *cmd, int argc, char **argv) unimplemented - diff --git a/tools/toollib.c b/tools/toollib.c index 9b628ab05..917b4ff05 100644 --- a/tools/toollib.c +++ b/tools/toollib.c @@ -9,8 +9,10 @@ #include <sys/stat.h> int process_each_lv_in_vg(struct cmd_context *cmd, struct volume_group *vg, + void *handle, int (*process_single) (struct cmd_context * cmd, - struct logical_volume * lv)) + struct logical_volume * lv, + void *handle)) { int ret_max = 0; int ret = 0; @@ -25,7 +27,7 @@ int process_each_lv_in_vg(struct cmd_context *cmd, struct volume_group *vg, list_iterate(lvh, &vg->lvs) { lv = list_item(lvh, struct lv_list)->lv; - ret = process_single(cmd, lv); + ret = process_single(cmd, lv, handle); if (ret > ret_max) ret_max = ret; } @@ -34,94 +36,163 @@ int process_each_lv_in_vg(struct cmd_context *cmd, struct volume_group *vg, } +struct volume_group *recover_vg(struct cmd_context *cmd, const char *vgname, + int lock_type) +{ + int consistent = 1; + + lock_type &= ~LCK_TYPE_MASK; + lock_type |= LCK_WRITE; + + if (!lock_vol(cmd, vgname, lock_type)) { + log_error("Can't lock %s for metadata recovery: skipping", + vgname); + return NULL; + } + + return vg_read(cmd, vgname, &consistent); +} + int process_each_lv(struct cmd_context *cmd, int argc, char **argv, - int lock_type, + int lock_type, void *handle, int (*process_single) (struct cmd_context * cmd, - struct logical_volume * lv)) + struct logical_volume * lv, + void *handle)) { int opt = 0; int ret_max = 0; int ret = 0; int vg_count = 0; + int consistent; - struct list *vgh, *vgs; + struct list *slh, *vgnames; struct volume_group *vg; struct logical_volume *lv; struct lv_list *lvl; - char *vg_name; + char *vgname; if (argc) { log_verbose("Using logical volume(s) on command line"); for (; opt < argc; opt++) { char *lv_name = argv[opt]; - - /* does VG exist? */ - if (!(vg_name = extract_vgname(cmd, lv_name))) { - if (ret_max < ECMD_FAILED) - ret_max = ECMD_FAILED; - continue; + int vgname_provided = 1; + + /* Do we have a vgname or lvname? */ + vgname = lv_name; + if (!strncmp(vgname, cmd->dev_dir, + strlen(cmd->dev_dir))) + vgname += strlen(cmd->dev_dir); + if (strchr(vgname, '/')) { + /* Must be an LV */ + vgname_provided = 0; + if (!(vgname = extract_vgname(cmd, lv_name))) { + if (ret_max < ECMD_FAILED) + ret_max = ECMD_FAILED; + continue; + } } - log_verbose("Finding volume group \"%s\"", vg_name); - if (!lock_vol(cmd, vg_name, lock_type)) { - log_error("Can't lock %s: skipping", vg_name); + log_verbose("Finding volume group \"%s\"", vgname); + if (!lock_vol(cmd, vgname, lock_type)) { + log_error("Can't lock %s: skipping", vgname); continue; } - if (!(vg = vg_read(cmd, vg_name))) { - log_error("Volume group \"%s\" doesn't exist", - vg_name); - if (ret_max < ECMD_FAILED) - ret_max = ECMD_FAILED; - unlock_vg(cmd, vg_name); - continue; + if (lock_type & LCK_WRITE) + consistent = 1; + else + consistent = 0; + if (!(vg = vg_read(cmd, vgname, &consistent)) || + !consistent) { + unlock_vg(cmd, vgname); + if (!vg) + log_error("Volume group \"%s\" " + "not found", vgname); + else + log_error("Volume group \"%s\" " + "inconsistent", vgname); + if (!vg || !(vg = + recover_vg(cmd, vgname, + lock_type))) { + unlock_vg(cmd, vgname); + if (ret_max < ECMD_FAILED) + ret_max = ECMD_FAILED; + continue; + } } if (vg->status & EXPORTED_VG) { log_error("Volume group \"%s\" is exported", vg->name); - unlock_vg(cmd, vg_name); + unlock_vg(cmd, vgname); return ECMD_FAILED; } + if (vgname_provided) { + if ((ret = + process_each_lv_in_vg(cmd, vg, handle, + process_single)) > + ret_max) + ret_max = ret; + unlock_vg(cmd, vgname); + continue; + } + if (!(lvl = find_lv_in_vg(vg, lv_name))) { log_error("Can't find logical volume \"%s\" " "in volume group \"%s\"", - lv_name, vg_name); + lv_name, vgname); if (ret_max < ECMD_FAILED) ret_max = ECMD_FAILED; - unlock_vg(cmd, vg_name); + unlock_vg(cmd, vgname); continue; } lv = lvl->lv; - if ((ret = process_single(cmd, lv)) > ret_max) + if ((ret = process_single(cmd, lv, handle)) > ret_max) ret_max = ret; - unlock_vg(cmd, vg_name); + unlock_vg(cmd, vgname); } } else { log_verbose("Finding all logical volumes"); - if (!(vgs = get_vgs(cmd))) { + if (!(vgnames = get_vgs(cmd, 0))) { log_error("No volume groups found"); return ECMD_FAILED; } - list_iterate(vgh, vgs) { - vg_name = list_item(vgh, struct name_list)->name; - if (!lock_vol(cmd, vg_name, lock_type)) { - log_error("Can't lock %s: skipping", vg_name); + list_iterate(slh, vgnames) { + vgname = list_item(slh, struct str_list)->str; + if (!vgname || !*vgname) + continue; /* FIXME Unnecessary? */ + if (!lock_vol(cmd, vgname, lock_type)) { + log_error("Can't lock %s: skipping", vgname); continue; } - if (!(vg = vg_read(cmd, vg_name))) { - log_error("Volume group \"%s\" not found", - vg_name); - if (ret_max < ECMD_FAILED) - ret_max = ECMD_FAILED; - unlock_vg(cmd, vg_name); - continue; + if (lock_type & LCK_WRITE) + consistent = 1; + else + consistent = 0; + if (!(vg = vg_read(cmd, vgname, &consistent)) || + !consistent) { + unlock_vg(cmd, vgname); + if (!vg) + log_error("Volume group \"%s\" " + "not found", vgname); + else + log_error("Volume group \"%s\" " + "inconsistent", vgname); + if (!vg || !(vg = + recover_vg(cmd, vgname, + lock_type))) { + unlock_vg(cmd, vgname); + if (ret_max < ECMD_FAILED) + ret_max = ECMD_FAILED; + continue; + } } - ret = process_each_lv_in_vg(cmd, vg, process_single); - unlock_vg(cmd, vg_name); + ret = process_each_lv_in_vg(cmd, vg, handle, + process_single); + unlock_vg(cmd, vgname); if (ret > ret_max) ret_max = ret; vg_count++; @@ -132,16 +203,18 @@ int process_each_lv(struct cmd_context *cmd, int argc, char **argv, } int process_each_vg(struct cmd_context *cmd, int argc, char **argv, - int lock_type, + int lock_type, int consistent, void *handle, int (*process_single) (struct cmd_context * cmd, - const char *vg_name)) + const char *vg_name, + struct volume_group * vg, + int consistent, void *handle)) { int opt = 0; int ret_max = 0; int ret = 0; - struct list *vgh; - struct list *vgs; + struct list *slh, *vgnames; + struct volume_group *vg; char *vg_name; char *dev_dir = cmd->dev_dir; @@ -161,24 +234,32 @@ int process_each_vg(struct cmd_context *cmd, int argc, char **argv, log_error("Can't lock %s: skipping", vg_name); continue; } - if ((ret = process_single(cmd, vg_name)) > ret_max) + log_verbose("Finding volume group \"%s\"", vg_name); + vg = vg_read(cmd, vg_name, &consistent); + if ((ret = process_single(cmd, vg_name, vg, consistent, + handle)) + > ret_max) ret_max = ret; unlock_vg(cmd, vg_name); } } else { log_verbose("Finding all volume groups"); - if (!(vgs = get_vgs(cmd))) { + if (!(vgnames = get_vgs(cmd, 0)) || list_empty(vgnames)) { log_error("No volume groups found"); return ECMD_FAILED; } - list_iterate(vgh, vgs) { - vg_name = list_item(vgh, struct name_list)->name; + list_iterate(slh, vgnames) { + vg_name = list_item(slh, struct str_list)->str; + if (!vg_name || !*vg_name) + continue; /* FIXME Unnecessary? */ if (!lock_vol(cmd, vg_name, lock_type)) { log_error("Can't lock %s: skipping", vg_name); continue; } - ret = process_single(cmd, vg_name); - + log_verbose("Finding volume group \"%s\"", vg_name); + vg = vg_read(cmd, vg_name, &consistent); + ret = process_single(cmd, vg_name, vg, consistent, + handle); if (ret > ret_max) ret_max = ret; unlock_vg(cmd, vg_name); @@ -189,9 +270,11 @@ int process_each_vg(struct cmd_context *cmd, int argc, char **argv, } int process_each_pv_in_vg(struct cmd_context *cmd, struct volume_group *vg, + void *handle, int (*process_single) (struct cmd_context * cmd, struct volume_group * vg, - struct physical_volume * pv)) + struct physical_volume * pv, + void *handle)) { int ret_max = 0; int ret = 0; @@ -201,7 +284,7 @@ int process_each_pv_in_vg(struct cmd_context *cmd, struct volume_group *vg, list_iterate(pvh, &vg->pvs) { pv = list_item(pvh, struct pv_list)->pv; - if ((ret = process_single(cmd, vg, pv)) > ret_max) + if ((ret = process_single(cmd, vg, pv, handle)) > ret_max) ret_max = ret; } @@ -209,10 +292,11 @@ int process_each_pv_in_vg(struct cmd_context *cmd, struct volume_group *vg, } int process_each_pv(struct cmd_context *cmd, int argc, char **argv, - struct volume_group *vg, + struct volume_group *vg, void *handle, int (*process_single) (struct cmd_context * cmd, struct volume_group * vg, - struct physical_volume * pv)) + struct physical_volume * pv, + void *handle)) { int opt = 0; int ret_max = 0; @@ -229,32 +313,18 @@ int process_each_pv(struct cmd_context *cmd, int argc, char **argv, vg->name); continue; } - ret = process_single(cmd, vg, pvl->pv); + ret = process_single(cmd, vg, pvl->pv, handle); if (ret > ret_max) ret_max = ret; } } else { log_verbose("Using all physical volume(s) in volume group"); - process_each_pv_in_vg(cmd, vg, process_single); + process_each_pv_in_vg(cmd, vg, handle, process_single); } return ret_max; } -int is_valid_chars(char *n) -{ - register char c; - - /* Hyphen used as VG-LV separator - ambiguity if LV starts with it */ - if (*n == '-') - return 0; - - while ((c = *n++)) - if (!isalnum(c) && c != '.' && c != '_' && c != '-' && c != '+') - return 0; - return 1; -} - char *extract_vgname(struct cmd_context *cmd, char *lv_name) { char *vg_name = lv_name; diff --git a/tools/toollib.h b/tools/toollib.h index 49f06fbea..8b5ef7ed7 100644 --- a/tools/toollib.h +++ b/tools/toollib.h @@ -29,30 +29,42 @@ int autobackup_init(const char *backup_dir, int keep_days, int keep_number, int autobackup); int autobackup(struct volume_group *vg); +struct volume_group *recover_vg(struct cmd_context *cmd, const char *vgname, + int lock_type); + int process_each_vg(struct cmd_context *cmd, int argc, char **argv, - int lock_type, + int lock_type, int consistent, void *handle, int (*process_single) (struct cmd_context * cmd, - const char *vg_name)); + const char *vg_name, + struct volume_group *vg, + int consistent, + void *handle)); int process_each_pv(struct cmd_context *cmd, int argc, char **argv, - struct volume_group *vg, + struct volume_group *vg, void *handle, int (*process_single) (struct cmd_context * cmd, struct volume_group * vg, - struct physical_volume * pv)); + struct physical_volume * pv, + void *handle)); + int process_each_lv(struct cmd_context *cmd, int argc, char **argv, - int lock_type, + int lock_type, void *handle, int (*process_single) (struct cmd_context * cmd, - struct logical_volume * lv)); + struct logical_volume * lv, + void *handle)); int process_each_pv_in_vg(struct cmd_context *cmd, struct volume_group *vg, + void *handle, int (*process_single) (struct cmd_context * cmd, struct volume_group * vg, - struct physical_volume * pv)); + struct physical_volume * pv, + void *handle)); + int process_each_lv_in_vg(struct cmd_context *cmd, struct volume_group *vg, + void *handle, int (*process_single) (struct cmd_context * cmd, - struct logical_volume * lv)); - -int is_valid_chars(char *n); + struct logical_volume * lv, + void *handle)); char *default_vgname(struct cmd_context *cmd); char *extract_vgname(struct cmd_context *cmd, char *lv_name); diff --git a/tools/tools.h b/tools/tools.h index cd148967f..2f253204f 100644 --- a/tools/tools.h +++ b/tools/tools.h @@ -7,39 +7,40 @@ #ifndef _LVM_TOOLS_H #define _LVM_TOOLS_H -#include "pool.h" -#include "dbg_malloc.h" -#include "list.h" -#include "log.h" -#include "lvm-string.h" -#include "lvm-file.h" -#include "metadata.h" +#define _GNU_SOURCE + +#include "activate.h" +#include "archive.h" +#include "cache.h" #include "config.h" +#include "dbg_malloc.h" #include "dev-cache.h" #include "device.h" -#include "vgcache.h" #include "display.h" #include "errors.h" #include "filter.h" -#include "filter-persistent.h" #include "filter-composite.h" +#include "filter-persistent.h" #include "filter-regex.h" -#include "format1.h" #include "format-text.h" -#include "toollib.h" -#include "activate.h" -#include "archive.h" +#include "metadata.h" +#include "list.h" #include "locking.h" +#include "log.h" +#include "lvm-file.h" +#include "lvm-string.h" +#include "pool.h" #include "toolcontext.h" +#include "toollib.h" -#include <stdio.h> +#include <ctype.h> +#include <limits.h> #include <stdarg.h> +#include <stdio.h> #include <stdlib.h> +#include <string.h> #include <unistd.h> #include <sys/types.h> -#include <ctype.h> -#include <string.h> -#include <limits.h> #define CMD_LEN 256 #define MAX_ARGS 64 @@ -68,7 +69,7 @@ typedef enum { struct arg { char short_arg; char *long_arg; - int (*fn) (struct arg * a); + int (*fn) (struct cmd_context * cmd, struct arg * a); int count; char *value; @@ -92,14 +93,14 @@ struct command { void usage(const char *name); /* the argument verify/normalise functions */ -int yes_no_arg(struct arg *a); -int size_arg(struct arg *a); -int int_arg(struct arg *a); -int int_arg_with_sign(struct arg *a); -int minor_arg(struct arg *a); -int string_arg(struct arg *a); -int permission_arg(struct arg *a); -int metadatatype_arg(struct arg *a); +int yes_no_arg(struct cmd_context *cmd, struct arg *a); +int size_arg(struct cmd_context *cmd, struct arg *a); +int int_arg(struct cmd_context *cmd, struct arg *a); +int int_arg_with_sign(struct cmd_context *cmd, struct arg *a); +int minor_arg(struct cmd_context *cmd, struct arg *a); +int string_arg(struct cmd_context *cmd, struct arg *a); +int permission_arg(struct cmd_context *cmd, struct arg *a); +int metadatatype_arg(struct cmd_context *cmd, struct arg *a); char yes_no_prompt(const char *prompt, ...); @@ -151,13 +152,4 @@ static inline const char *command_name(struct cmd_context *cmd) return cmd->command->name; } -static inline int driver_is_loaded(void) -{ - int i = driver_version(NULL, 0); - - if (!i) - log_error("device-mapper driver/module not loaded?"); - return i; -} - #endif diff --git a/tools/vgcfgbackup.c b/tools/vgcfgbackup.c index a78015894..46f90ec63 100644 --- a/tools/vgcfgbackup.c +++ b/tools/vgcfgbackup.c @@ -6,44 +6,29 @@ #include "tools.h" -#include <stdio.h> - -static int _backup_to_file(const char *file, struct volume_group *vg) -{ - int r; - struct format_instance *tf; - void *context; - - if (!(context = create_text_context(vg->cmd->fmtt, file, - vg->cmd->cmd_line)) || - !(tf = vg->cmd->fmtt->ops->create_instance(vg->cmd->fmtt, NULL, - context))) { - log_error("Couldn't create backup object."); - return 0; - } - - if (!(r = tf->fmt->ops->vg_write(tf, vg, context)) || - !(r = tf->fmt->ops->vg_commit(tf, vg, context))) - stack; - - tf->fmt->ops->destroy_instance(tf); - return r; -} - -static int vg_backup_single(struct cmd_context *cmd, const char *vg_name) +static int vg_backup_single(struct cmd_context *cmd, const char *vg_name, + struct volume_group *vg, int consistent, + void *handle) { - struct volume_group *vg; - - log_verbose("Checking for volume group \"%s\"", vg_name); - if (!(vg = vg_read(cmd, vg_name))) { + if (!vg) { log_error("Volume group \"%s\" not found", vg_name); return ECMD_FAILED; } + if (!consistent) + log_error("Warning: Volume group \"%s\" inconsistent", vg_name); + if (arg_count(cmd, file_ARG)) { - _backup_to_file(arg_value(cmd, file_ARG), vg); + backup_to_file(arg_value(cmd, file_ARG), vg->cmd->cmd_line, vg); } else { + if (!consistent) { + log_error("No backup taken: specify filename with -f " + "to backup an inconsistent VG"); + stack; + return ECMD_FAILED; + } + /* just use the normal backup code */ backup_enable(1); /* force a backup */ if (!backup(vg)) { @@ -58,8 +43,6 @@ static int vg_backup_single(struct cmd_context *cmd, const char *vg_name) int vgcfgbackup(struct cmd_context *cmd, int argc, char **argv) { - if (!driver_is_loaded()) - return ECMD_FAILED; - - return process_each_vg(cmd, argc, argv, LCK_VG_READ, &vg_backup_single); + return process_each_vg(cmd, argc, argv, LCK_VG_READ, 0, NULL, + &vg_backup_single); } diff --git a/tools/vgcfgrestore.c b/tools/vgcfgrestore.c index fe4978166..2c38ed643 100644 --- a/tools/vgcfgrestore.c +++ b/tools/vgcfgrestore.c @@ -13,9 +13,6 @@ int vgcfgrestore(struct cmd_context *cmd, int argc, char **argv) return ECMD_FAILED; } - if (!driver_is_loaded()) - return ECMD_FAILED; - /* * FIXME: overloading the -l arg for now to display a * list of archive files for a particular vg @@ -27,13 +24,28 @@ int vgcfgrestore(struct cmd_context *cmd, int argc, char **argv) return 0; } + if (!lock_vol(cmd, ORPHAN, LCK_VG_WRITE)) { + log_error("Unable to lock orphans"); + return ECMD_FAILED; + } + + if (!lock_vol(cmd, argv[0], LCK_VG_WRITE | LCK_NONBLOCK)) { + log_error("Unable to lock volume group %s", argv[0]); + unlock_vg(cmd, ORPHAN); + return ECMD_FAILED; + } + if (!(arg_count(cmd, file_ARG) ? backup_restore_from_file(cmd, argv[0], arg_str_value(cmd, file_ARG, "")) : backup_restore(cmd, argv[0]))) { + unlock_vg(cmd, argv[0]); + unlock_vg(cmd, ORPHAN); log_err("Restore failed."); return ECMD_FAILED; } + unlock_vg(cmd, argv[0]); + unlock_vg(cmd, ORPHAN); return 0; } diff --git a/tools/vgchange.c b/tools/vgchange.c index 3165ba884..9579bef36 100644 --- a/tools/vgchange.c +++ b/tools/vgchange.c @@ -20,11 +20,6 @@ #include "tools.h" -static int vgchange_single(struct cmd_context *cmd, const char *vg_name); -void vgchange_available(struct cmd_context *cmd, struct volume_group *vg); -void vgchange_resizeable(struct cmd_context *cmd, struct volume_group *vg); -void vgchange_logicalvolume(struct cmd_context *cmd, struct volume_group *vg); - static int _activate_lvs_in_vg(struct cmd_context *cmd, struct volume_group *vg, int lock) { @@ -48,72 +43,6 @@ static int _activate_lvs_in_vg(struct cmd_context *cmd, return count; } -int vgchange(struct cmd_context *cmd, int argc, char **argv) -{ - if (! - (arg_count(cmd, available_ARG) + arg_count(cmd, logicalvolume_ARG) + - arg_count(cmd, resizeable_ARG))) { - log_error("One of -a, -l or -x options required"); - return EINVALID_CMD_LINE; - } - - if (arg_count(cmd, available_ARG) + arg_count(cmd, logicalvolume_ARG) + - arg_count(cmd, resizeable_ARG) > 1) { - log_error("Only one of -a, -l or -x options allowed"); - return EINVALID_CMD_LINE; - } - - if (arg_count(cmd, ignorelockingfailure_ARG) && - !arg_count(cmd, available_ARG)) { - log_error("--ignorelockingfailure only available with -a"); - return EINVALID_CMD_LINE; - } - - if (arg_count(cmd, available_ARG) == 1 - && arg_count(cmd, autobackup_ARG)) { - log_error("-A option not necessary with -a option"); - return EINVALID_CMD_LINE; - } - - if (!driver_is_loaded()) - return ECMD_FAILED; - - return process_each_vg(cmd, argc, argv, - (arg_count(cmd, available_ARG)) ? - LCK_VG_READ : LCK_VG_WRITE, &vgchange_single); -} - -static int vgchange_single(struct cmd_context *cmd, const char *vg_name) -{ - struct volume_group *vg; - - if (!(vg = vg_read(cmd, vg_name))) { - log_error("Unable to find volume group \"%s\"", vg_name); - return ECMD_FAILED; - } - - if (!(vg->status & LVM_WRITE) && !arg_count(cmd, available_ARG)) { - log_error("Volume group \"%s\" is read-only", vg->name); - return ECMD_FAILED; - } - - if (vg->status & EXPORTED_VG) { - log_error("Volume group \"%s\" is exported", vg_name); - return ECMD_FAILED; - } - - if (arg_count(cmd, available_ARG)) - vgchange_available(cmd, vg); - - if (arg_count(cmd, resizeable_ARG)) - vgchange_resizeable(cmd, vg); - - if (arg_count(cmd, logicalvolume_ARG)) - vgchange_logicalvolume(cmd, vg); - - return 0; -} - void vgchange_available(struct cmd_context *cmd, struct volume_group *vg) { int lv_open, active; @@ -208,3 +137,74 @@ void vgchange_logicalvolume(struct cmd_context *cmd, struct volume_group *vg) return; } + +static int vgchange_single(struct cmd_context *cmd, const char *vg_name, + struct volume_group *vg, int consistent, + void *handle) +{ + if (!vg) { + log_error("Unable to find volume group \"%s\"", vg_name); + return ECMD_FAILED; + } + + if (!consistent) { + unlock_vg(cmd, vg_name); + log_error("Volume group \"%s\" inconsistent", vg_name); + if (!(vg = recover_vg(cmd, vg_name, LCK_VG_WRITE))) + return ECMD_FAILED; + } + + if (!(vg->status & LVM_WRITE) && !arg_count(cmd, available_ARG)) { + log_error("Volume group \"%s\" is read-only", vg->name); + return ECMD_FAILED; + } + + if (vg->status & EXPORTED_VG) { + log_error("Volume group \"%s\" is exported", vg_name); + return ECMD_FAILED; + } + + if (arg_count(cmd, available_ARG)) + vgchange_available(cmd, vg); + + if (arg_count(cmd, resizeable_ARG)) + vgchange_resizeable(cmd, vg); + + if (arg_count(cmd, logicalvolume_ARG)) + vgchange_logicalvolume(cmd, vg); + + return 0; +} + +int vgchange(struct cmd_context *cmd, int argc, char **argv) +{ + if (! + (arg_count(cmd, available_ARG) + arg_count(cmd, logicalvolume_ARG) + + arg_count(cmd, resizeable_ARG))) { + log_error("One of -a, -l or -x options required"); + return EINVALID_CMD_LINE; + } + + if (arg_count(cmd, available_ARG) + arg_count(cmd, logicalvolume_ARG) + + arg_count(cmd, resizeable_ARG) > 1) { + log_error("Only one of -a, -l or -x options allowed"); + return EINVALID_CMD_LINE; + } + + if (arg_count(cmd, ignorelockingfailure_ARG) && + !arg_count(cmd, available_ARG)) { + log_error("--ignorelockingfailure only available with -a"); + return EINVALID_CMD_LINE; + } + + if (arg_count(cmd, available_ARG) == 1 + && arg_count(cmd, autobackup_ARG)) { + log_error("-A option not necessary with -a option"); + return EINVALID_CMD_LINE; + } + + return process_each_vg(cmd, argc, argv, + (arg_count(cmd, available_ARG)) ? + LCK_VG_READ : LCK_VG_WRITE, 0, NULL, + &vgchange_single); +} diff --git a/tools/vgck.c b/tools/vgck.c index e90f53171..6a6eb1ec7 100644 --- a/tools/vgck.c +++ b/tools/vgck.c @@ -20,20 +20,15 @@ #include "tools.h" -static int vgck_single(struct cmd_context *cmd, const char *vg_name); - -int vgck(struct cmd_context *cmd, int argc, char **argv) +static int vgck_single(struct cmd_context *cmd, const char *vg_name, + struct volume_group *vg, int consistent, void *handle) { - return process_each_vg(cmd, argc, argv, LCK_VG_READ, &vgck_single); -} - -static int vgck_single(struct cmd_context *cmd, const char *vg_name) -{ - struct volume_group *vg; - - log_verbose("Checking volume group \"%s\"", vg_name); + if (!consistent) { + log_error("Volume group \"%s\" inconsistent", vg_name); + return ECMD_FAILED; + } - if (!(vg = vg_read(cmd, vg_name))) { + if (!vg) { log_error("Volume group \"%s\" not found", vg_name); return ECMD_FAILED; } @@ -43,12 +38,12 @@ static int vgck_single(struct cmd_context *cmd, const char *vg_name) return ECMD_FAILED; } -/******* FIXME Must be caught and logged by vg_read - log_error("not all physical volumes of volume group \"%s\" online", - log_error("volume group \"%s\" has physical volumes with ", - "invalid version", -********/ - /* FIXME: free */ return 0; } + +int vgck(struct cmd_context *cmd, int argc, char **argv) +{ + return process_each_vg(cmd, argc, argv, LCK_VG_READ, 0, NULL, + &vgck_single); +} diff --git a/tools/vgconvert.c b/tools/vgconvert.c new file mode 100644 index 000000000..91bd8886b --- /dev/null +++ b/tools/vgconvert.c @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2001 Sistina Software + * + * pvcreate is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * pvcreate is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with LVM; see the file COPYING. If not, write to + * the Free Software Foundation, 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "tools.h" +#include "defaults.h" + +static int vgconvert_single(struct cmd_context *cmd, const char *vg_name, + struct volume_group *vg, int consistent, + void *handle) +{ + struct physical_volume *pv, *existing_pv; + uint64_t size = 0; + struct list mdas; + int pvmetadatacopies = 0; + uint64_t pvmetadatasize = 0; + uint64_t pe_end = 0, pe_start = 0; + struct list *pvh; + + if (!vg) { + log_error("Unable to find volume group \"%s\"", vg_name); + return ECMD_FAILED; + } + + if (!consistent) { + unlock_vg(cmd, vg_name); + log_error("Volume group \"%s\" inconsistent", vg_name); + if (!(vg = recover_vg(cmd, vg_name, LCK_VG_WRITE))) + return ECMD_FAILED; + } + + if (!(vg->status & LVM_WRITE)) { + log_error("Volume group \"%s\" is read-only", vg->name); + return ECMD_FAILED; + } + + if (vg->status & EXPORTED_VG) { + log_error("Volume group \"%s\" is exported", vg_name); + return ECMD_FAILED; + } + + if (vg->fid->fmt == cmd->fmt) { + log_error("Volume group \"%s\" already uses format %s", + vg_name, cmd->fmt->name); + return ECMD_FAILED; + } + + if (cmd->fmt->features & FMT_MDAS) { + pvmetadatasize = arg_int64_value(cmd, metadatasize_ARG, 0) * 2; + if (!pvmetadatasize) + pvmetadatasize = + find_config_int(cmd->cf->root, + "metadata/pvmetadatasize", + '/', DEFAULT_PVMETADATASIZE); + + pvmetadatacopies = arg_int_value(cmd, metadatacopies_ARG, -1); + if (pvmetadatacopies < 0) + pvmetadatacopies = + find_config_int(cmd->cf->root, + "metadata/pvmetadatacopies", + '/', DEFAULT_PVMETADATACOPIES); + } + + if (!archive(vg)) { + log_error("Archive of \"%s\" metadata failed.", vg_name); + return ECMD_FAILED; + } + + list_iterate(pvh, &vg->pvs) { + existing_pv = list_item(pvh, struct pv_list)->pv; + + pe_start = existing_pv->pe_start; + pe_end = existing_pv->pe_count * existing_pv->pe_size + + pe_start - 1; + + list_init(&mdas); + if (!(pv = pv_create(cmd->fmt, existing_pv->dev, + &existing_pv->id, size, + pe_start, existing_pv->pe_count, + existing_pv->pe_size, pvmetadatacopies, + pvmetadatasize, &mdas))) { + log_error("Failed to setup physical volume \"%s\"", + dev_name(existing_pv->dev)); + log_error("Use pvcreate and vgcfgrestore to repair " + "from archived metadata."); + return ECMD_FAILED; + } + + log_verbose("Set up physical volume for \"%s\" with %" PRIu64 + " available sectors", dev_name(pv->dev), pv->size); + + /* Wipe existing label first */ + if (!label_remove(pv->dev)) { + log_error("Failed to wipe existing label on %s", + dev_name(pv->dev)); + log_error("Use pvcreate and vgcfgrestore to repair " + "from archived metadata."); + return ECMD_FAILED; + } + + log_very_verbose("Writing physical volume data to disk \"%s\"", + dev_name(pv->dev)); + if (!(pv_write(cmd, pv, &mdas, + arg_int_value(cmd, labelsector_ARG, + DEFAULT_LABELSECTOR)))) { + log_error("Failed to write physical volume \"%s\"", + dev_name(pv->dev)); + log_error("Use pvcreate and vgcfgrestore to repair " + "from archived metadata."); + return ECMD_FAILED; + } + log_verbose("Physical volume \"%s\" successfully created", + dev_name(pv->dev)); + + } + + log_verbose("Deleting existing metadata for VG %s", vg_name); + if (!vg_remove(vg)) { + log_error("Removal of existing metadata for %s failed.", + vg_name); + log_error("Use pvcreate and vgcfgrestore to repair " + "from archived metadata."); + return ECMD_FAILED; + } + + log_verbose("Writing metadata for VG %s using format %s", vg_name, + cmd->fmt->name); + if (!backup_restore_vg(cmd, vg)) { + log_error("Conversion failed for volume group %s.", vg_name); + log_error("Use pvcreate and vgcfgrestore to repair from " + "archived metadata."); + return ECMD_FAILED; + } + log_print("Volume group %s successfully converted", vg_name); + + backup(vg); + + return 0; +} + +int vgconvert(struct cmd_context *cmd, int argc, char **argv) +{ + if (!argc) { + log_error("Please enter volume group(s)"); + return EINVALID_CMD_LINE; + } + + if (arg_int_value(cmd, labelsector_ARG, 0) >= LABEL_SCAN_SECTORS) { + log_error("labelsector must be less than %lu", + LABEL_SCAN_SECTORS); + return EINVALID_CMD_LINE; + } + + if (!(cmd->fmt->features & FMT_MDAS) && + (arg_count(cmd, metadatacopies_ARG) || + arg_count(cmd, metadatasize_ARG))) { + log_error("Metadata parameters only apply to text format"); + return EINVALID_CMD_LINE; + } + + if (arg_count(cmd, metadatacopies_ARG) && + arg_int_value(cmd, metadatacopies_ARG, -1) > 2) { + log_error("Metadatacopies may only be 0, 1 or 2"); + return EINVALID_CMD_LINE; + } + + return process_each_vg(cmd, argc, argv, LCK_VG_WRITE, 0, NULL, + &vgconvert_single); +} diff --git a/tools/vgcreate.c b/tools/vgcreate.c index 7c092e0b0..23a8389ca 100644 --- a/tools/vgcreate.c +++ b/tools/vgcreate.c @@ -73,15 +73,12 @@ int vgcreate(struct cmd_context *cmd, int argc, char **argv) return ECMD_FAILED; } - if (!is_valid_chars(vg_name)) { + if (!validate_vgname(vg_name)) { log_error("New volume group name \"%s\" has invalid characters", vg_name); return ECMD_FAILED; } - if (!driver_is_loaded()) - return ECMD_FAILED; - /* Create the new VG */ if (!(vg = vg_create(cmd, vg_name, extent_size, max_pv, max_lv, argc - 1, argv + 1))) diff --git a/tools/vgdisplay.c b/tools/vgdisplay.c index 13f301120..72bc3b388 100644 --- a/tools/vgdisplay.c +++ b/tools/vgdisplay.c @@ -20,7 +20,45 @@ #include "tools.h" -static int vgdisplay_single(struct cmd_context *cmd, const char *vg_name); +static int vgdisplay_single(struct cmd_context *cmd, const char *vg_name, + struct volume_group *vg, int consistent, + void *handle) +{ + /* FIXME Do the active check here if activevolumegroups_ARG ? */ + if (!vg) { + log_error("Volume group \"%s\" doesn't exist", vg_name); + return ECMD_FAILED; + } + + if (!consistent) + log_error("WARNING: Volume group \"%s\" inconsistent", vg_name); + + if (vg->status & EXPORTED_VG) + log_print("WARNING: volume group \"%s\" is exported", vg_name); + + if (arg_count(cmd, colon_ARG)) { + vgdisplay_colons(vg); + return 0; + } + + if (arg_count(cmd, short_ARG)) { + vgdisplay_short(vg); + return 0; + } + + vgdisplay_full(vg); /* was vg_show */ + + if (arg_count(cmd, verbose_ARG)) { + vgdisplay_extents(vg); + + process_each_lv_in_vg(cmd, vg, NULL, &lvdisplay_full); + + log_print("--- Physical volumes ---"); + process_each_pv_in_vg(cmd, vg, NULL, &pvdisplay_short); + } + + return 0; +} int vgdisplay(struct cmd_context *cmd, int argc, char **argv) { @@ -34,11 +72,6 @@ int vgdisplay(struct cmd_context *cmd, int argc, char **argv) return EINVALID_CMD_LINE; } - if (!driver_is_loaded()) - return ECMD_FAILED; - - /* FIXME -D disk_ARG is now redundant */ - /********* FIXME: Do without this - or else 2(+) passes! Figure out longest volume group name for (c = opt; opt < argc; opt++) { @@ -48,7 +81,8 @@ int vgdisplay(struct cmd_context *cmd, int argc, char **argv) } **********/ - process_each_vg(cmd, argc, argv, LCK_VG_READ, &vgdisplay_single); + process_each_vg(cmd, argc, argv, LCK_VG_READ, 0, NULL, + &vgdisplay_single); /******** FIXME Need to count number processed Add this to process_each_vg if arg_count(cmd,activevolumegroups_ARG) ? @@ -64,43 +98,3 @@ int vgdisplay(struct cmd_context *cmd, int argc, char **argv) return 0; } - -static int vgdisplay_single(struct cmd_context *cmd, const char *vg_name) -{ - - struct volume_group *vg; - - /* FIXME Do the active check here if activevolumegroups_ARG ? */ - - log_very_verbose("Finding volume group \"%s\"", vg_name); - if (!(vg = vg_read(cmd, vg_name))) { - log_error("Volume group \"%s\" doesn't exist", vg_name); - return ECMD_FAILED; - } - - if (vg->status & EXPORTED_VG) - log_print("WARNING: volume group \"%s\" is exported", vg_name); - - if (arg_count(cmd, colon_ARG)) { - vgdisplay_colons(vg); - return 0; - } - - if (arg_count(cmd, short_ARG)) { - vgdisplay_short(vg); - return 0; - } - - vgdisplay_full(vg); /* was vg_show */ - - if (arg_count(cmd, verbose_ARG)) { - vgdisplay_extents(vg); - - process_each_lv_in_vg(cmd, vg, &lvdisplay_full); - - log_print("--- Physical volumes ---"); - process_each_pv_in_vg(cmd, vg, &pvdisplay_short); - } - - return 0; -} diff --git a/tools/vgexport.c b/tools/vgexport.c index 50fe605fc..5e521f015 100644 --- a/tools/vgexport.c +++ b/tools/vgexport.c @@ -20,32 +20,17 @@ #include "tools.h" -static int vgexport_single(struct cmd_context *cmd, const char *vg_name); - -int vgexport(struct cmd_context *cmd, int argc, char **argv) +static int vgexport_single(struct cmd_context *cmd, const char *vg_name, + struct volume_group *vg, int consistent, + void *handle) { - if (!argc && !arg_count(cmd, all_ARG)) { - log_error("Please supply volume groups or use -a for all."); - return ECMD_FAILED; - } - - if (argc && arg_count(cmd, all_ARG)) { - log_error("No arguments permitted when using -a for all."); - return ECMD_FAILED; + if (!vg) { + log_error("Unable to find volume group \"%s\"", vg_name); + goto error; } - if (!driver_is_loaded()) - return ECMD_FAILED; - - return process_each_vg(cmd, argc, argv, LCK_VG_READ, &vgexport_single); -} - -static int vgexport_single(struct cmd_context *cmd, const char *vg_name) -{ - struct volume_group *vg; - - if (!(vg = vg_read(cmd, vg_name))) { - log_error("Unable to find volume group \"%s\"", vg_name); + if (!consistent) { + log_error("Volume group %s inconsistent", vg_name); goto error; } @@ -82,3 +67,19 @@ static int vgexport_single(struct cmd_context *cmd, const char *vg_name) error: return ECMD_FAILED; } + +int vgexport(struct cmd_context *cmd, int argc, char **argv) +{ + if (!argc && !arg_count(cmd, all_ARG)) { + log_error("Please supply volume groups or use -a for all."); + return ECMD_FAILED; + } + + if (argc && arg_count(cmd, all_ARG)) { + log_error("No arguments permitted when using -a for all."); + return ECMD_FAILED; + } + + return process_each_vg(cmd, argc, argv, LCK_VG_WRITE, 1, NULL, + &vgexport_single); +} diff --git a/tools/vgextend.c b/tools/vgextend.c index 6b5adb9a6..0cb9f0731 100644 --- a/tools/vgextend.c +++ b/tools/vgextend.c @@ -24,6 +24,7 @@ int vgextend(struct cmd_context *cmd, int argc, char **argv) { char *vg_name; struct volume_group *vg = NULL; + int consistent = 1; if (!argc) { log_error("Please enter volume group name and " @@ -36,9 +37,6 @@ int vgextend(struct cmd_context *cmd, int argc, char **argv) return EINVALID_CMD_LINE; } - if (!driver_is_loaded()) - return ECMD_FAILED; - vg_name = argv[0]; argc--; argv++; @@ -55,7 +53,7 @@ int vgextend(struct cmd_context *cmd, int argc, char **argv) goto error; } - if (!(vg = vg_read(cmd, vg_name))) { + if (!(vg = vg_read(cmd, vg_name, &consistent)) || !consistent) { log_error("Volume group \"%s\" not found.", vg_name); goto error; } diff --git a/tools/vgimport.c b/tools/vgimport.c index bb72d7a70..d515caada 100644 --- a/tools/vgimport.c +++ b/tools/vgimport.c @@ -20,31 +20,11 @@ #include "tools.h" -static int vgimport_single(struct cmd_context *cmd, const char *vg_name); - -int vgimport(struct cmd_context *cmd, int argc, char **argv) -{ - if (!argc && !arg_count(cmd, all_ARG)) { - log_error("Please supply volume groups or use -a for all."); - return ECMD_FAILED; - } - - if (argc && arg_count(cmd, all_ARG)) { - log_error("No arguments permitted when using -a for all."); - return ECMD_FAILED; - } - - if (!driver_is_loaded()) - return ECMD_FAILED; - - return process_each_vg(cmd, argc, argv, LCK_VG_WRITE, &vgimport_single); -} - -static int vgimport_single(struct cmd_context *cmd, const char *vg_name) +static int vgimport_single(struct cmd_context *cmd, const char *vg_name, + struct volume_group *vg, int consistent, + void *handle) { - struct volume_group *vg; - - if (!(vg = vg_read(cmd, vg_name))) { + if (!vg || !consistent) { log_error("Unable to find exported volume group \"%s\"", vg_name); goto error; @@ -77,3 +57,19 @@ static int vgimport_single(struct cmd_context *cmd, const char *vg_name) error: return ECMD_FAILED; } + +int vgimport(struct cmd_context *cmd, int argc, char **argv) +{ + if (!argc && !arg_count(cmd, all_ARG)) { + log_error("Please supply volume groups or use -a for all."); + return ECMD_FAILED; + } + + if (argc && arg_count(cmd, all_ARG)) { + log_error("No arguments permitted when using -a for all."); + return ECMD_FAILED; + } + + return process_each_vg(cmd, argc, argv, LCK_VG_WRITE, 1, NULL, + &vgimport_single); +} diff --git a/tools/vgmerge.c b/tools/vgmerge.c index 8d6e7b444..6a705658f 100644 --- a/tools/vgmerge.c +++ b/tools/vgmerge.c @@ -21,41 +21,12 @@ #include "tools.h" int vgmerge_single(struct cmd_context *cmd, const char *vg_name_to, - const char *vg_name_from); - -int vgmerge(struct cmd_context *cmd, int argc, char **argv) -{ - char *vg_name_to; - int opt = 0; - int ret = 0, ret_max = 0; - - if (argc < 2) { - log_error("Please enter 2 or more volume groups to merge"); - return EINVALID_CMD_LINE; - } - - if (!driver_is_loaded()) - return ECMD_FAILED; - - vg_name_to = argv[0]; - argc--; - argv++; - - for (; opt < argc; opt++) { - ret = vgmerge_single(cmd, vg_name_to, argv[opt]); - if (ret > ret_max) - ret_max = ret; - } - - return ret_max; -} - -int vgmerge_single(struct cmd_context *cmd, const char *vg_name_to, const char *vg_name_from) { struct volume_group *vg_to, *vg_from; struct list *lvh1, *lvh2; int active; + int consistent = 1; if (!strcmp(vg_name_to, vg_name_from)) { log_error("Duplicate volume group name \"%s\"", vg_name_from); @@ -68,7 +39,7 @@ int vgmerge_single(struct cmd_context *cmd, const char *vg_name_to, return ECMD_FAILED; } - if (!(vg_to = vg_read(cmd, vg_name_to))) { + if (!(vg_to = vg_read(cmd, vg_name_to, &consistent)) || !consistent) { log_error("Volume group \"%s\" doesn't exist", vg_name_to); unlock_vg(cmd, vg_name_to); return ECMD_FAILED; @@ -93,7 +64,8 @@ int vgmerge_single(struct cmd_context *cmd, const char *vg_name_to, return ECMD_FAILED; } - if (!(vg_from = vg_read(cmd, vg_name_from))) { + consistent = 1; + if (!(vg_from = vg_read(cmd, vg_name_from, &consistent)) || !consistent) { log_error("Volume group \"%s\" doesn't exist", vg_name_from); goto error; } @@ -178,6 +150,14 @@ int vgmerge_single(struct cmd_context *cmd, const char *vg_name_to, list_del(lvh); list_add(&vg_to->lvs, lvh); } + + while (!list_empty(&vg_from->fid->metadata_areas)) { + struct list *mdah = vg_from->fid->metadata_areas.n; + + list_del(mdah); + list_add(&vg_to->fid->metadata_areas, mdah); + } + vg_to->lv_count += vg_from->lv_count; vg_to->extent_count += vg_from->extent_count; @@ -205,3 +185,27 @@ int vgmerge_single(struct cmd_context *cmd, const char *vg_name_to, unlock_vg(cmd, vg_name_to); return ECMD_FAILED; } + +int vgmerge(struct cmd_context *cmd, int argc, char **argv) +{ + char *vg_name_to; + int opt = 0; + int ret = 0, ret_max = 0; + + if (argc < 2) { + log_error("Please enter 2 or more volume groups to merge"); + return EINVALID_CMD_LINE; + } + + vg_name_to = argv[0]; + argc--; + argv++; + + for (; opt < argc; opt++) { + ret = vgmerge_single(cmd, vg_name_to, argv[opt]); + if (ret > ret_max) + ret_max = ret; + } + + return ret_max; +} diff --git a/tools/vgreduce.c b/tools/vgreduce.c index b2bd476be..a08557314 100644 --- a/tools/vgreduce.c +++ b/tools/vgreduce.c @@ -20,14 +20,64 @@ #include "tools.h" +/* Or take pv_name instead? */ static int vgreduce_single(struct cmd_context *cmd, struct volume_group *vg, - struct physical_volume *pv); + struct physical_volume *pv, void *handle) +{ + struct pv_list *pvl; + const char *name = dev_name(pv->dev); + + if (pv->pe_alloc_count) { + log_error("Physical volume \"%s\" still in use", name); + return ECMD_FAILED; + } + + if (vg->pv_count == 1) { + log_error("Can't remove final physical volume \"%s\" from " + "volume group \"%s\"", name, vg->name); + return ECMD_FAILED; + } + + pvl = find_pv_in_vg(vg, name); + + if (!archive(vg)) + return ECMD_FAILED; + log_verbose("Removing \"%s\" from volume group \"%s\"", name, vg->name); + + if (pvl) + list_del(&pvl->list); + + *pv->vg_name = '\0'; + vg->pv_count--; + vg->free_count -= pv->pe_count - pv->pe_alloc_count; + vg->extent_count -= pv->pe_count; + + if (!vg_write(vg)) { + log_error("Removal of physical volume \"%s\" from " + "\"%s\" failed", name, vg->name); + return ECMD_FAILED; + } + + if (!pv_write(cmd, pv, NULL, -1)) { + log_error("Failed to clear metadata from physical " + "volume \"%s\" " + "after removal from \"%s\"", name, vg->name); + return ECMD_FAILED; + } + + backup(vg); + + log_print("Removed \"%s\" from volume group \"%s\"", name, vg->name); + + return 0; +} int vgreduce(struct cmd_context *cmd, int argc, char **argv) { struct volume_group *vg; char *vg_name; int ret; + int consistent = 1; if (!argc) { log_error("Please give volume group name and " @@ -46,9 +96,6 @@ int vgreduce(struct cmd_context *cmd, int argc, char **argv) return EINVALID_CMD_LINE; } - if (!driver_is_loaded()) - return ECMD_FAILED; - vg_name = argv[0]; argv++; argc--; @@ -59,7 +106,7 @@ int vgreduce(struct cmd_context *cmd, int argc, char **argv) return ECMD_FAILED; } - if (!(vg = vg_read(cmd, vg_name))) { + if (!(vg = vg_read(cmd, vg_name, &consistent)) || !consistent) { log_error("Volume group \"%s\" doesn't exist", vg_name); unlock_vg(cmd, vg_name); return ECMD_FAILED; @@ -85,7 +132,7 @@ int vgreduce(struct cmd_context *cmd, int argc, char **argv) /* FIXME: Pass private structure through to all these functions */ /* and update in batch here? */ - ret = process_each_pv(cmd, argc, argv, vg, vgreduce_single); + ret = process_each_pv(cmd, argc, argv, vg, NULL, vgreduce_single); unlock_vg(cmd, vg_name); @@ -107,62 +154,3 @@ int vgreduce(struct cmd_context *cmd, int argc, char **argv) ********/ } - -/* Or take pv_name instead? */ -static int vgreduce_single(struct cmd_context *cmd, struct volume_group *vg, - struct physical_volume *pv) -{ - struct pv_list *pvl; - const char *name = dev_name(pv->dev); - - if (pv->pe_alloc_count) { - log_error("Physical volume \"%s\" still in use", name); - return ECMD_FAILED; - } - -/********* FIXME: Is this unnecessary after checking pe_alloc_count? - if (pv->lv_cur > 0) { - log_error ("can't reduce volume group \"%s\" by used physical volume \"%s\"", vg_name, error_pv_name); - } -*********/ - - if (vg->pv_count == 1) { - log_error("Can't remove final physical volume \"%s\" from " - "volume group \"%s\"", name, vg->name); - return ECMD_FAILED; - } - - pvl = find_pv_in_vg(vg, name); - - if (!archive(vg)) - return ECMD_FAILED; - - log_verbose("Removing \"%s\" from volume group \"%s\"", name, vg->name); - - if (pvl) - list_del(&pvl->list); - - *pv->vg_name = '\0'; - vg->pv_count--; - vg->free_count -= pv->pe_count - pv->pe_alloc_count; - vg->extent_count -= pv->pe_count; - - if (!vg_write(vg)) { - log_error("Removal of physical volume \"%s\" from " - "\"%s\" failed", name, vg->name); - return ECMD_FAILED; - } - - if (!pv_write(cmd, pv)) { - log_error("Failed to clear metadata from physical " - "volume \"%s\" " - "after removal from \"%s\"", name, vg->name); - return ECMD_FAILED; - } - - backup(vg); - - log_print("Removed \"%s\" from volume group \"%s\"", name, vg->name); - - return 0; -} diff --git a/tools/vgremove.c b/tools/vgremove.c index 018976f73..14a0fcf37 100644 --- a/tools/vgremove.c +++ b/tools/vgremove.c @@ -20,38 +20,15 @@ #include "tools.h" -static int vgremove_single(struct cmd_context *cmd, const char *vg_name); - -int vgremove(struct cmd_context *cmd, int argc, char **argv) -{ - int ret; - - if (!driver_is_loaded()) - return ECMD_FAILED; - - if (!lock_vol(cmd, "", LCK_VG_WRITE)) { - log_error("Can't get lock for orphan PVs"); - return ECMD_FAILED; - } - - ret = process_each_vg(cmd, argc, argv, - LCK_VG | LCK_WRITE | LCK_NONBLOCK, - &vgremove_single); - - unlock_vg(cmd, ""); - - return ret; -} - -static int vgremove_single(struct cmd_context *cmd, const char *vg_name) +static int vgremove_single(struct cmd_context *cmd, const char *vg_name, + struct volume_group *vg, int consistent, + void *handle) { - struct volume_group *vg; struct physical_volume *pv; struct list *pvh; int ret = 0; - log_verbose("Checking for volume group \"%s\"", vg_name); - if (!(vg = vg_read(cmd, vg_name))) { + if (!vg || !consistent) { log_error("Volume group \"%s\" doesn't exist", vg_name); return ECMD_FAILED; } @@ -87,7 +64,8 @@ static int vgremove_single(struct cmd_context *cmd, const char *vg_name) log_verbose("Removing physical volume \"%s\" from " "volume group \"%s\"", dev_name(pv->dev), vg_name); *pv->vg_name = '\0'; - if (!pv_write(cmd, pv)) { + /* FIXME Write to same sector label was read from */ + if (!pv_write(cmd, pv, NULL, -1)) { log_error("Failed to remove physical volume \"%s\"" " from volume group \"%s\"", dev_name(pv->dev), vg_name); @@ -104,3 +82,21 @@ static int vgremove_single(struct cmd_context *cmd, const char *vg_name) return ret; } + +int vgremove(struct cmd_context *cmd, int argc, char **argv) +{ + int ret; + + if (!lock_vol(cmd, "", LCK_VG_WRITE)) { + log_error("Can't get lock for orphan PVs"); + return ECMD_FAILED; + } + + ret = process_each_vg(cmd, argc, argv, + LCK_VG | LCK_WRITE | LCK_NONBLOCK, 1, NULL, + &vgremove_single); + + unlock_vg(cmd, ""); + + return ret; +} diff --git a/tools/vgrename.c b/tools/vgrename.c index 4ec3d9855..50c045121 100644 --- a/tools/vgrename.c +++ b/tools/vgrename.c @@ -24,6 +24,7 @@ int vgrename(struct cmd_context *cmd, int argc, char **argv) { char *dev_dir; int length; + int consistent = 1; char *vg_name_old, *vg_name_new; @@ -56,7 +57,7 @@ int vgrename(struct cmd_context *cmd, int argc, char **argv) return ECMD_FAILED; } - if (!is_valid_chars(vg_name_new)) { + if (!validate_vgname(vg_name_new)) { log_error("New volume group name \"%s\" has invalid characters", vg_name_new); return ECMD_FAILED; @@ -67,9 +68,6 @@ int vgrename(struct cmd_context *cmd, int argc, char **argv) return ECMD_FAILED; } - if (!driver_is_loaded()) - return ECMD_FAILED; - log_verbose("Checking for existing volume group \"%s\"", vg_name_old); if (!lock_vol(cmd, vg_name_old, LCK_VG_WRITE)) { @@ -77,7 +75,7 @@ int vgrename(struct cmd_context *cmd, int argc, char **argv) return ECMD_FAILED; } - if (!(vg_old = vg_read(cmd, vg_name_old))) { + if (!(vg_old = vg_read(cmd, vg_name_old, &consistent)) || !consistent) { log_error("Volume group \"%s\" doesn't exist", vg_name_old); unlock_vg(cmd, vg_name_old); return ECMD_FAILED; @@ -115,7 +113,8 @@ int vgrename(struct cmd_context *cmd, int argc, char **argv) return ECMD_FAILED; } - if ((vg_new = vg_read(cmd, vg_name_new))) { + consistent = 0; + if ((vg_new = vg_read(cmd, vg_name_new, &consistent))) { log_error("New volume group \"%s\" already exists", vg_name_new); goto error; @@ -133,18 +132,14 @@ int vgrename(struct cmd_context *cmd, int argc, char **argv) vg_name_new); } -/********** FIXME: Check within vg_write now - log_error("A new logical volume path exceeds " - "maximum of %d!", NAME_LEN - 2); - goto error; -*************/ - sprintf(old_path, "%s%s", dev_dir, vg_name_old); sprintf(new_path, "%s%s", dev_dir, vg_name_new); if (dir_exists(old_path)) { log_verbose("Renaming \"%s\" to \"%s\"", old_path, new_path); - if (rename(old_path, new_path)) { + if (test_mode()) + log_verbose("Test mode: Skipping rename."); + else if (rename(old_path, new_path)) { log_error("Renaming \"%s\" to \"%s\" failed: %s", old_path, new_path, strerror(errno)); goto error; @@ -175,19 +170,3 @@ int vgrename(struct cmd_context *cmd, int argc, char **argv) return ECMD_FAILED; } -/* FIXME: Moved into vg_write now */ -/******************* -char *lv_change_vgname(char *vg_name, char *lv_name) -{ - char *lv_name_ptr = NULL; - static char lv_name_buf[NAME_LEN] = { 0, }; - - ** check if lv_name includes a path - if ((lv_name_ptr = strrchr(lv_name, '/'))) { - lv_name_ptr++; - sprintf(lv_name_buf, "%s%s/%s%c", cmd->dev_dir, vg_name, - lv_name_ptr, 0);} - else - strncpy(lv_name_buf, lv_name, NAME_LEN - 1); return lv_name_buf;} - -**********************/ diff --git a/tools/vgscan.c b/tools/vgscan.c index 504f53478..0af067d79 100644 --- a/tools/vgscan.c +++ b/tools/vgscan.c @@ -20,7 +20,28 @@ #include "tools.h" -static int vgscan_single(struct cmd_context *cmd, const char *vg_name); +static int vgscan_single(struct cmd_context *cmd, const char *vg_name, + struct volume_group *vg, int consistent, void *handle) +{ + if (!vg) { + log_error("Volume group \"%s\" not found", vg_name); + return ECMD_FAILED; + } + + if (!consistent) { + unlock_vg(cmd, vg_name); + log_error("Volume group \"%s\" inconsistent", vg_name); + /* Don't allow partial switch to this program */ + if (!(vg = recover_vg(cmd, vg_name, LCK_VG_WRITE))) + return ECMD_FAILED; + } + + log_print("Found %svolume group \"%s\" using metadata type %s", + (vg->status & EXPORTED_VG) ? "exported " : "", vg_name, + vg->fid->fmt->name); + + return 0; +} int vgscan(struct cmd_context *cmd, int argc, char **argv) { @@ -32,28 +53,11 @@ int vgscan(struct cmd_context *cmd, int argc, char **argv) log_verbose("Wiping cache of LVM-capable devices"); persistent_filter_wipe(cmd->filter); - log_verbose("Wiping internal cache of PVs in VGs"); - vgcache_destroy(); + log_verbose("Wiping internal cache"); + cache_destroy(); log_print("Reading all physical volumes. This may take a while..."); - return process_each_vg(cmd, argc, argv, LCK_VG_READ, &vgscan_single); -} - -static int vgscan_single(struct cmd_context *cmd, const char *vg_name) -{ - struct volume_group *vg; - - log_verbose("Checking for volume group \"%s\"", vg_name); - - if (!(vg = vg_read(cmd, vg_name))) { - log_error("Volume group \"%s\" not found", vg_name); - return ECMD_FAILED; - } - - log_print("Found %svolume group \"%s\" using metadata type %s", - (vg->status & EXPORTED_VG) ? "exported " : "", vg_name, - vg->fid->fmt->name); - - return 0; + return process_each_vg(cmd, argc, argv, LCK_VG_READ, 1, NULL, + &vgscan_single); } diff --git a/tools/vgsplit.c b/tools/vgsplit.c index baa7d2e5b..00a77ff15 100644 --- a/tools/vgsplit.c +++ b/tools/vgsplit.c @@ -65,7 +65,7 @@ static int _move_lvs(struct volume_group *vg_from, struct volume_group *vg_to) { struct list *lvh, *lvht, *segh; struct logical_volume *lv; - struct stripe_segment *seg; + struct lv_segment *seg; struct physical_volume *pv; struct volume_group *vg_with; int s; @@ -77,7 +77,7 @@ static int _move_lvs(struct volume_group *vg_from, struct volume_group *vg_to) /* VG as each other */ vg_with = NULL; list_iterate(segh, &lv->segments) { - seg = list_item(segh, struct stripe_segment); + seg = list_item(segh, struct lv_segment); for (s = 0; s < seg->stripes; s++) { pv = seg->area[s].pv; if (vg_with) { @@ -164,6 +164,7 @@ int vgsplit(struct cmd_context *cmd, int argc, char **argv) struct volume_group *vg_to, *vg_from; int opt; int active; + int consistent = 1; if (argc < 3) { log_error("Existing VG, new VG and physical volumes required."); @@ -180,16 +181,13 @@ int vgsplit(struct cmd_context *cmd, int argc, char **argv) return ECMD_FAILED; } - if (!driver_is_loaded()) - return ECMD_FAILED; - log_verbose("Checking for volume group \"%s\"", vg_name_from); if (!lock_vol(cmd, vg_name_from, LCK_VG_WRITE)) { log_error("Can't get lock for %s", vg_name_from); return ECMD_FAILED; } - if (!(vg_from = vg_read(cmd, vg_name_from))) { + if (!(vg_from = vg_read(cmd, vg_name_from, &consistent)) || !consistent) { log_error("Volume group \"%s\" doesn't exist", vg_name_from); unlock_vg(cmd, vg_name_from); return ECMD_FAILED; @@ -214,7 +212,8 @@ int vgsplit(struct cmd_context *cmd, int argc, char **argv) return ECMD_FAILED; } - if ((vg_to = vg_read(cmd, vg_name_to))) { + consistent = 0; + if ((vg_to = vg_read(cmd, vg_name_to, &consistent))) { /* FIXME Remove this restriction */ log_error("Volume group \"%s\" already exists", vg_name_to); goto error; @@ -250,6 +249,12 @@ int vgsplit(struct cmd_context *cmd, int argc, char **argv) if (!(_move_snapshots(vg_from, vg_to))) goto error; + /* FIXME Split mdas properly somehow too! */ + /* Currently we cheat by sharing the format instance and relying on + * vg_write to ignore mdas outside the VG! Done this way, with text + * format, vg_from disappears for a short time. */ + vg_to->fid = vg_from->fid; + /* store it on disks */ log_verbose("Writing out updated volume groups"); @@ -271,6 +276,13 @@ int vgsplit(struct cmd_context *cmd, int argc, char **argv) backup(vg_from); /* Remove EXPORTED flag from new VG */ + consistent = 1; + if (!(vg_to = vg_read(cmd, vg_name_to, &consistent)) || !consistent) { + log_error("Volume group \"%s\" became inconsistent: please " + "fix manually", vg_name_to); + goto error; + } + vg_to->status &= ~EXPORTED_VG; if (!vg_write(vg_to)) |