You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
6364 lines
184 KiB
6364 lines
184 KiB
configure | 93 ++++- |
|
configure.ac | 58 +++ |
|
daemons/clvmd/clvmd-command.c | 1 + |
|
daemons/clvmd/lvm-functions.c | 10 + |
|
daemons/clvmd/lvm-functions.h | 1 + |
|
daemons/lvmetad/lvmetad-client.h | 1 + |
|
daemons/lvmetad/lvmetad-core.c | 9 +- |
|
include/configure.h.in | 14 + |
|
lib/Makefile.in | 29 ++ |
|
lib/activate/activate.c | 13 + |
|
lib/activate/activate.h | 1 + |
|
lib/cache/lvmcache.c | 4 + |
|
lib/cache/lvmetad.c | 23 +- |
|
lib/commands/toolcontext.c | 24 ++ |
|
lib/config/config_settings.h | 22 +- |
|
lib/display/display.c | 9 +- |
|
lib/format1/.exported_symbols | 1 + |
|
lib/format1/Makefile.in | 33 ++ |
|
lib/format1/disk-rep.c | 761 ++++++++++++++++++++++++++++++++++++++ |
|
lib/format1/disk-rep.h | 250 +++++++++++++ |
|
lib/format1/format1.c | 630 +++++++++++++++++++++++++++++++ |
|
lib/format1/format1.h | 29 ++ |
|
lib/format1/import-export.c | 680 ++++++++++++++++++++++++++++++++++ |
|
lib/format1/import-extents.c | 377 +++++++++++++++++++ |
|
lib/format1/layout.c | 172 +++++++++ |
|
lib/format1/lvm1-label.c | 129 +++++++ |
|
lib/format1/lvm1-label.h | 23 ++ |
|
lib/format1/vg_number.c | 60 +++ |
|
lib/format_pool/.exported_symbols | 1 + |
|
lib/format_pool/Makefile.in | 30 ++ |
|
lib/format_pool/disk_rep.c | 409 ++++++++++++++++++++ |
|
lib/format_pool/disk_rep.h | 156 ++++++++ |
|
lib/format_pool/format_pool.c | 337 +++++++++++++++++ |
|
lib/format_pool/format_pool.h | 28 ++ |
|
lib/format_pool/import_export.c | 285 ++++++++++++++ |
|
lib/format_pool/pool_label.c | 104 ++++++ |
|
lib/format_pool/pool_label.h | 23 ++ |
|
lib/format_pool/sptype_names.h | 42 +++ |
|
lib/format_text/export.c | 2 + |
|
lib/format_text/format-text.c | 4 +- |
|
lib/format_text/import_vsn1.c | 9 +- |
|
lib/locking/locking.c | 40 ++ |
|
lib/metadata/lv_manip.c | 26 ++ |
|
lib/metadata/metadata-exported.h | 10 +- |
|
lib/metadata/metadata.c | 18 +- |
|
lib/metadata/segtype.h | 2 +- |
|
lib/metadata/snapshot_manip.c | 11 + |
|
lib/metadata/vg.c | 17 +- |
|
lib/metadata/vg.h | 1 + |
|
lib/report/report.c | 4 +- |
|
lib/striped/striped.c | 3 +- |
|
man/vgconvert.8_des | 7 +- |
|
test/shell/format-lvm1.sh | 38 ++ |
|
test/shell/lvm1-basic.sh | 36 ++ |
|
test/shell/snapshot-lvm1.sh | 35 ++ |
|
tools/args.h | 3 +- |
|
tools/lvconvert.c | 5 + |
|
tools/lvmcmdline.c | 58 ++- |
|
tools/pvscan.c | 5 +- |
|
tools/stub.h | 1 + |
|
tools/toollib.c | 29 +- |
|
tools/vals.h | 4 +- |
|
tools/vgchange.c | 10 + |
|
tools/vgconvert.c | 53 ++- |
|
tools/vgscan.c | 4 +- |
|
65 files changed, 5254 insertions(+), 53 deletions(-) |
|
create mode 100644 lib/format1/.exported_symbols |
|
create mode 100644 lib/format1/Makefile.in |
|
create mode 100644 lib/format1/disk-rep.c |
|
create mode 100644 lib/format1/disk-rep.h |
|
create mode 100644 lib/format1/format1.c |
|
create mode 100644 lib/format1/format1.h |
|
create mode 100644 lib/format1/import-export.c |
|
create mode 100644 lib/format1/import-extents.c |
|
create mode 100644 lib/format1/layout.c |
|
create mode 100644 lib/format1/lvm1-label.c |
|
create mode 100644 lib/format1/lvm1-label.h |
|
create mode 100644 lib/format1/vg_number.c |
|
create mode 100644 lib/format_pool/.exported_symbols |
|
create mode 100644 lib/format_pool/Makefile.in |
|
create mode 100644 lib/format_pool/disk_rep.c |
|
create mode 100644 lib/format_pool/disk_rep.h |
|
create mode 100644 lib/format_pool/format_pool.c |
|
create mode 100644 lib/format_pool/format_pool.h |
|
create mode 100644 lib/format_pool/import_export.c |
|
create mode 100644 lib/format_pool/pool_label.c |
|
create mode 100644 lib/format_pool/pool_label.h |
|
create mode 100644 lib/format_pool/sptype_names.h |
|
create mode 100644 test/shell/format-lvm1.sh |
|
create mode 100644 test/shell/lvm1-basic.sh |
|
create mode 100644 test/shell/snapshot-lvm1.sh |
|
|
|
diff --git a/configure b/configure |
|
index ef24b31..10b49c6 100755 |
|
--- a/configure |
|
+++ b/configure |
|
@@ -675,6 +675,7 @@ PYTHON_BINDINGS |
|
PYTHON3 |
|
PTHREAD_LIBS |
|
M_LIBS |
|
+POOL |
|
PKGCONFIG |
|
ODIRECT |
|
OCFDIR |
|
@@ -689,6 +690,8 @@ LVM_MINOR |
|
LVM_MAJOR |
|
LVM_LIBAPI |
|
LVM_VERSION |
|
+LVM1_FALLBACK |
|
+LVM1 |
|
LIB_SUFFIX |
|
LDDEPS |
|
JOBS |
|
@@ -716,6 +719,7 @@ DEFAULT_RAID10_SEGTYPE |
|
DEFAULT_PROFILE_SUBDIR |
|
DEFAULT_PID_DIR |
|
DEFAULT_MIRROR_SEGTYPE |
|
+DEFAULT_FALLBACK_TO_LVM1 |
|
DEFAULT_LOCK_DIR |
|
DEFAULT_DM_RUN_DIR |
|
DEFAULT_DATA_ALIGNMENT |
|
@@ -908,6 +912,9 @@ with_device_gid |
|
with_device_mode |
|
with_device_nodes_on |
|
with_default_name_mangling |
|
+enable_lvm1_fallback |
|
+with_lvm1 |
|
+with_pool |
|
with_cluster |
|
with_snapshots |
|
with_mirrors |
|
@@ -1673,6 +1680,8 @@ Optional Features: |
|
speeds up one-time build. |
|
--enable-static_link use this to link the tools to their libraries |
|
statically (default is dynamic linking |
|
+ --enable-lvm1_fallback use this to fall back and use LVM1 binaries if |
|
+ device-mapper is missing from the kernel |
|
--disable-thin_check_needs_check |
|
required if thin_check version is < 0.3.0 |
|
--disable-cache_check_needs_check |
|
@@ -1739,6 +1748,10 @@ Optional Packages: |
|
create nodes on resume or create [ON=resume] |
|
--with-default-name-mangling=MANGLING |
|
default name mangling: auto/none/hex [auto] |
|
+ --with-lvm1=TYPE LVM1 metadata support: internal/shared/none |
|
+ [internal] |
|
+ --with-pool=TYPE GFS pool read-only support: internal/shared/none |
|
+ [internal] |
|
--with-cluster=TYPE cluster LVM locking support: internal/shared/none |
|
[internal] |
|
--with-snapshots=TYPE snapshot support: internal/shared/none [internal] |
|
@@ -8372,6 +8385,78 @@ _ACEOF |
|
|
|
|
|
################################################################################ |
|
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to enable lvm1 fallback" >&5 |
|
+$as_echo_n "checking whether to enable lvm1 fallback... " >&6; } |
|
+# Check whether --enable-lvm1_fallback was given. |
|
+if test "${enable_lvm1_fallback+set}" = set; then : |
|
+ enableval=$enable_lvm1_fallback; LVM1_FALLBACK=$enableval |
|
+else |
|
+ LVM1_FALLBACK=no |
|
+fi |
|
+ |
|
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $LVM1_FALLBACK" >&5 |
|
+$as_echo "$LVM1_FALLBACK" >&6; } |
|
+ |
|
+if test "$LVM1_FALLBACK" = yes; then |
|
+ DEFAULT_FALLBACK_TO_LVM1=1 |
|
+ |
|
+$as_echo "#define LVM1_FALLBACK 1" >>confdefs.h |
|
+ |
|
+else |
|
+ DEFAULT_FALLBACK_TO_LVM1=0 |
|
+fi |
|
+ |
|
+cat >>confdefs.h <<_ACEOF |
|
+#define DEFAULT_FALLBACK_TO_LVM1 $DEFAULT_FALLBACK_TO_LVM1 |
|
+_ACEOF |
|
+ |
|
+ |
|
+################################################################################ |
|
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to include support for lvm1 metadata" >&5 |
|
+$as_echo_n "checking whether to include support for lvm1 metadata... " >&6; } |
|
+ |
|
+# Check whether --with-lvm1 was given. |
|
+if test "${with_lvm1+set}" = set; then : |
|
+ withval=$with_lvm1; LVM1=$withval |
|
+else |
|
+ LVM1=internal |
|
+fi |
|
+ |
|
+ |
|
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $LVM1" >&5 |
|
+$as_echo "$LVM1" >&6; } |
|
+ |
|
+case "$LVM1" in |
|
+ none|shared) ;; |
|
+ internal) |
|
+$as_echo "#define LVM1_INTERNAL 1" >>confdefs.h |
|
+ ;; |
|
+ *) as_fn_error $? "--with-lvm1 parameter invalid" "$LINENO" 5 ;; |
|
+esac |
|
+ |
|
+################################################################################ |
|
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to include support for GFS pool metadata" >&5 |
|
+$as_echo_n "checking whether to include support for GFS pool metadata... " >&6; } |
|
+ |
|
+# Check whether --with-pool was given. |
|
+if test "${with_pool+set}" = set; then : |
|
+ withval=$with_pool; POOL=$withval |
|
+else |
|
+ POOL=internal |
|
+fi |
|
+ |
|
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $POOL" >&5 |
|
+$as_echo "$POOL" >&6; } |
|
+ |
|
+case "$POOL" in |
|
+ none|shared) ;; |
|
+ internal) |
|
+$as_echo "#define POOL_INTERNAL 1" >>confdefs.h |
|
+ ;; |
|
+ *) as_fn_error $? "--with-pool parameter invalid" "$LINENO" 5 |
|
+esac |
|
+ |
|
+################################################################################ |
|
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to include support for cluster locking" >&5 |
|
$as_echo_n "checking whether to include support for cluster locking... " >&6; } |
|
|
|
@@ -15558,8 +15643,12 @@ _ACEOF |
|
|
|
|
|
|
|
+ |
|
+ |
|
+ |
|
+ |
|
################################################################################ |
|
-ac_config_files="$ac_config_files Makefile make.tmpl daemons/Makefile daemons/clvmd/Makefile daemons/cmirrord/Makefile daemons/dmeventd/Makefile daemons/dmeventd/libdevmapper-event.pc daemons/dmeventd/plugins/Makefile daemons/dmeventd/plugins/lvm2/Makefile daemons/dmeventd/plugins/raid/Makefile daemons/dmeventd/plugins/mirror/Makefile daemons/dmeventd/plugins/snapshot/Makefile daemons/dmeventd/plugins/thin/Makefile daemons/dmfilemapd/Makefile daemons/lvmdbusd/Makefile daemons/lvmdbusd/lvmdbusd daemons/lvmdbusd/lvmdb.py daemons/lvmdbusd/lvm_shell_proxy.py daemons/lvmdbusd/path.py daemons/lvmetad/Makefile daemons/lvmpolld/Makefile daemons/lvmlockd/Makefile device_mapper/Makefile conf/Makefile conf/example.conf conf/lvmlocal.conf conf/command_profile_template.profile conf/metadata_profile_template.profile include/.symlinks include/Makefile lib/Makefile lib/locking/Makefile include/lvm-version.h libdaemon/Makefile libdaemon/client/Makefile libdaemon/server/Makefile libdm/Makefile libdm/libdevmapper.pc liblvm/Makefile liblvm/liblvm2app.pc man/Makefile po/Makefile python/Makefile python/setup.py scripts/blkdeactivate.sh scripts/blk_availability_init_red_hat scripts/blk_availability_systemd_red_hat.service scripts/clvmd_init_red_hat scripts/cmirrord_init_red_hat scripts/com.redhat.lvmdbus1.service scripts/dm_event_systemd_red_hat.service scripts/dm_event_systemd_red_hat.socket scripts/lvm2_cluster_activation_red_hat.sh scripts/lvm2_cluster_activation_systemd_red_hat.service scripts/lvm2_clvmd_systemd_red_hat.service scripts/lvm2_cmirrord_systemd_red_hat.service scripts/lvm2_lvmdbusd_systemd_red_hat.service scripts/lvm2_lvmetad_init_red_hat scripts/lvm2_lvmetad_systemd_red_hat.service scripts/lvm2_lvmetad_systemd_red_hat.socket scripts/lvm2_lvmpolld_init_red_hat scripts/lvm2_lvmpolld_systemd_red_hat.service scripts/lvm2_lvmpolld_systemd_red_hat.socket scripts/lvm2_lvmlockd_systemd_red_hat.service scripts/lvm2_lvmlocking_systemd_red_hat.service scripts/lvm2_monitoring_init_red_hat scripts/lvm2_monitoring_systemd_red_hat.service scripts/lvm2_pvscan_systemd_red_hat@.service scripts/lvm2_tmpfiles_red_hat.conf scripts/lvmdump.sh scripts/Makefile test/Makefile test/api/Makefile test/api/python_lvm_unit.py test/unit/Makefile tools/Makefile udev/Makefile" |
|
+ac_config_files="$ac_config_files Makefile make.tmpl daemons/Makefile daemons/clvmd/Makefile daemons/cmirrord/Makefile daemons/dmeventd/Makefile daemons/dmeventd/libdevmapper-event.pc daemons/dmeventd/plugins/Makefile daemons/dmeventd/plugins/lvm2/Makefile daemons/dmeventd/plugins/raid/Makefile daemons/dmeventd/plugins/mirror/Makefile daemons/dmeventd/plugins/snapshot/Makefile daemons/dmeventd/plugins/thin/Makefile daemons/dmfilemapd/Makefile daemons/lvmdbusd/Makefile daemons/lvmdbusd/lvmdbusd daemons/lvmdbusd/lvmdb.py daemons/lvmdbusd/lvm_shell_proxy.py daemons/lvmdbusd/path.py daemons/lvmetad/Makefile daemons/lvmpolld/Makefile daemons/lvmlockd/Makefile device_mapper/Makefile conf/Makefile conf/example.conf conf/lvmlocal.conf conf/command_profile_template.profile conf/metadata_profile_template.profile include/.symlinks include/Makefile lib/Makefile lib/format1/Makefile lib/format_pool/Makefile lib/locking/Makefile include/lvm-version.h libdaemon/Makefile libdaemon/client/Makefile libdaemon/server/Makefile libdm/Makefile libdm/libdevmapper.pc liblvm/Makefile liblvm/liblvm2app.pc man/Makefile po/Makefile python/Makefile python/setup.py scripts/blkdeactivate.sh scripts/blk_availability_init_red_hat scripts/blk_availability_systemd_red_hat.service scripts/clvmd_init_red_hat scripts/cmirrord_init_red_hat scripts/com.redhat.lvmdbus1.service scripts/dm_event_systemd_red_hat.service scripts/dm_event_systemd_red_hat.socket scripts/lvm2_cluster_activation_red_hat.sh scripts/lvm2_cluster_activation_systemd_red_hat.service scripts/lvm2_clvmd_systemd_red_hat.service scripts/lvm2_cmirrord_systemd_red_hat.service scripts/lvm2_lvmdbusd_systemd_red_hat.service scripts/lvm2_lvmetad_init_red_hat scripts/lvm2_lvmetad_systemd_red_hat.service scripts/lvm2_lvmetad_systemd_red_hat.socket scripts/lvm2_lvmpolld_init_red_hat scripts/lvm2_lvmpolld_systemd_red_hat.service scripts/lvm2_lvmpolld_systemd_red_hat.socket scripts/lvm2_lvmlockd_systemd_red_hat.service scripts/lvm2_lvmlocking_systemd_red_hat.service scripts/lvm2_monitoring_init_red_hat scripts/lvm2_monitoring_systemd_red_hat.service scripts/lvm2_pvscan_systemd_red_hat@.service scripts/lvm2_tmpfiles_red_hat.conf scripts/lvmdump.sh scripts/Makefile test/Makefile test/api/Makefile test/api/python_lvm_unit.py test/unit/Makefile tools/Makefile udev/Makefile" |
|
|
|
cat >confcache <<\_ACEOF |
|
# This file is a shell script that caches the results of configure |
|
@@ -16285,6 +16374,8 @@ do |
|
"include/.symlinks") CONFIG_FILES="$CONFIG_FILES include/.symlinks" ;; |
|
"include/Makefile") CONFIG_FILES="$CONFIG_FILES include/Makefile" ;; |
|
"lib/Makefile") CONFIG_FILES="$CONFIG_FILES lib/Makefile" ;; |
|
+ "lib/format1/Makefile") CONFIG_FILES="$CONFIG_FILES lib/format1/Makefile" ;; |
|
+ "lib/format_pool/Makefile") CONFIG_FILES="$CONFIG_FILES lib/format_pool/Makefile" ;; |
|
"lib/locking/Makefile") CONFIG_FILES="$CONFIG_FILES lib/locking/Makefile" ;; |
|
"include/lvm-version.h") CONFIG_FILES="$CONFIG_FILES include/lvm-version.h" ;; |
|
"libdaemon/Makefile") CONFIG_FILES="$CONFIG_FILES libdaemon/Makefile" ;; |
|
diff --git a/configure.ac b/configure.ac |
|
index 935ea08..9fa0c76 100644 |
|
--- a/configure.ac |
|
+++ b/configure.ac |
|
@@ -283,6 +283,58 @@ AC_MSG_RESULT($MANGLING) |
|
AC_DEFINE_UNQUOTED([DEFAULT_DM_NAME_MANGLING], $mangling, [Define default name mangling behaviour]) |
|
|
|
################################################################################ |
|
+dnl -- LVM1 tool fallback option |
|
+AC_MSG_CHECKING(whether to enable lvm1 fallback) |
|
+AC_ARG_ENABLE(lvm1_fallback, |
|
+ AC_HELP_STRING([--enable-lvm1_fallback], |
|
+ [use this to fall back and use LVM1 binaries if |
|
+ device-mapper is missing from the kernel]), |
|
+ LVM1_FALLBACK=$enableval, LVM1_FALLBACK=no) |
|
+AC_MSG_RESULT($LVM1_FALLBACK) |
|
+ |
|
+if test "$LVM1_FALLBACK" = yes; then |
|
+ DEFAULT_FALLBACK_TO_LVM1=1 |
|
+ AC_DEFINE([LVM1_FALLBACK], 1, [Define to 1 if 'lvm' should fall back to using LVM1 binaries if device-mapper is missing from the kernel]) |
|
+else |
|
+ DEFAULT_FALLBACK_TO_LVM1=0 |
|
+fi |
|
+AC_DEFINE_UNQUOTED(DEFAULT_FALLBACK_TO_LVM1, [$DEFAULT_FALLBACK_TO_LVM1], |
|
+ [Fall back to LVM1 by default if device-mapper is missing from the kernel.]) |
|
+ |
|
+################################################################################ |
|
+dnl -- format1 inclusion type |
|
+AC_MSG_CHECKING(whether to include support for lvm1 metadata) |
|
+AC_ARG_WITH(lvm1, |
|
+ AC_HELP_STRING([--with-lvm1=TYPE], |
|
+ [LVM1 metadata support: internal/shared/none [internal]]), |
|
+ LVM1=$withval, LVM1=internal) |
|
+ |
|
+AC_MSG_RESULT($LVM1) |
|
+ |
|
+case "$LVM1" in |
|
+ none|shared) ;; |
|
+ internal) AC_DEFINE([LVM1_INTERNAL], 1, |
|
+ [Define to 1 to include built-in support for LVM1 metadata.]) ;; |
|
+ *) AC_MSG_ERROR([--with-lvm1 parameter invalid]) ;; |
|
+esac |
|
+ |
|
+################################################################################ |
|
+dnl -- format_pool inclusion type |
|
+AC_MSG_CHECKING(whether to include support for GFS pool metadata) |
|
+AC_ARG_WITH(pool, |
|
+ AC_HELP_STRING([--with-pool=TYPE], |
|
+ [GFS pool read-only support: internal/shared/none [internal]]), |
|
+ POOL=$withval, POOL=internal) |
|
+AC_MSG_RESULT($POOL) |
|
+ |
|
+case "$POOL" in |
|
+ none|shared) ;; |
|
+ internal) AC_DEFINE([POOL_INTERNAL], 1, |
|
+ [Define to 1 to include built-in support for GFS pool metadata.]) ;; |
|
+ *) AC_MSG_ERROR([--with-pool parameter invalid]) |
|
+esac |
|
+ |
|
+################################################################################ |
|
dnl -- cluster_locking inclusion type |
|
AC_MSG_CHECKING(whether to include support for cluster locking) |
|
AC_ARG_WITH(cluster, |
|
@@ -1967,6 +2019,7 @@ AC_SUBST(DEFAULT_CACHE_SUBDIR) |
|
AC_SUBST(DEFAULT_DATA_ALIGNMENT) |
|
AC_SUBST(DEFAULT_DM_RUN_DIR) |
|
AC_SUBST(DEFAULT_LOCK_DIR) |
|
+AC_SUBST(DEFAULT_FALLBACK_TO_LVM1) |
|
AC_SUBST(DEFAULT_MIRROR_SEGTYPE) |
|
AC_SUBST(DEFAULT_PID_DIR) |
|
AC_SUBST(DEFAULT_PROFILE_SUBDIR) |
|
@@ -1997,6 +2050,8 @@ AC_SUBST(JOBS) |
|
AC_SUBST(LDDEPS) |
|
AC_SUBST(LIBS) |
|
AC_SUBST(LIB_SUFFIX) |
|
+AC_SUBST(LVM1) |
|
+AC_SUBST(LVM1_FALLBACK) |
|
AC_SUBST(LVM_VERSION) |
|
AC_SUBST(LVM_LIBAPI) |
|
AC_SUBST(LVM_MAJOR) |
|
@@ -2013,6 +2068,7 @@ AC_SUBST(OCF) |
|
AC_SUBST(OCFDIR) |
|
AC_SUBST(ODIRECT) |
|
AC_SUBST(PKGCONFIG) |
|
+AC_SUBST(POOL) |
|
AC_SUBST(M_LIBS) |
|
AC_SUBST(PTHREAD_LIBS) |
|
AC_SUBST(PYTHON2) |
|
@@ -2117,6 +2173,8 @@ conf/metadata_profile_template.profile |
|
include/.symlinks |
|
include/Makefile |
|
lib/Makefile |
|
+lib/format1/Makefile |
|
+lib/format_pool/Makefile |
|
lib/locking/Makefile |
|
include/lvm-version.h |
|
libdaemon/Makefile |
|
diff --git a/daemons/clvmd/clvmd-command.c b/daemons/clvmd/clvmd-command.c |
|
index ce7f500..2971ecb 100644 |
|
--- a/daemons/clvmd/clvmd-command.c |
|
+++ b/daemons/clvmd/clvmd-command.c |
|
@@ -108,6 +108,7 @@ int do_command(struct local_client *client, struct clvm_header *msg, int msglen, |
|
lock_flags = args[1]; |
|
lockname = &args[2]; |
|
/* Check to see if the VG is in use by LVM1 */ |
|
+ status = do_check_lvm1(lockname); |
|
do_lock_vg(lock_cmd, lock_flags, lockname); |
|
break; |
|
|
|
diff --git a/daemons/clvmd/lvm-functions.c b/daemons/clvmd/lvm-functions.c |
|
index d6d395f..477c252 100644 |
|
--- a/daemons/clvmd/lvm-functions.c |
|
+++ b/daemons/clvmd/lvm-functions.c |
|
@@ -639,6 +639,16 @@ int post_lock_lv(unsigned char command, unsigned char lock_flags, |
|
return 0; |
|
} |
|
|
|
+/* Check if a VG is in use by LVM1 so we don't stomp on it */ |
|
+int do_check_lvm1(const char *vgname) |
|
+{ |
|
+ int status; |
|
+ |
|
+ status = check_lvm1_vg_inactive(cmd, vgname); |
|
+ |
|
+ return status == 1 ? 0 : EBUSY; |
|
+} |
|
+ |
|
int do_refresh_cache(void) |
|
{ |
|
DEBUGLOG("Refreshing context\n"); |
|
diff --git a/daemons/clvmd/lvm-functions.h b/daemons/clvmd/lvm-functions.h |
|
index 6785997..eac966f 100644 |
|
--- a/daemons/clvmd/lvm-functions.h |
|
+++ b/daemons/clvmd/lvm-functions.h |
|
@@ -25,6 +25,7 @@ extern int do_lock_lv(unsigned char lock_cmd, unsigned char lock_flags, |
|
extern const char *do_lock_query(char *resource); |
|
extern int post_lock_lv(unsigned char lock_cmd, unsigned char lock_flags, |
|
char *resource); |
|
+extern int do_check_lvm1(const char *vgname); |
|
extern int do_refresh_cache(void); |
|
extern int init_clvm(struct dm_hash_table *excl_uuid); |
|
extern void destroy_lvm(void); |
|
diff --git a/daemons/lvmetad/lvmetad-client.h b/daemons/lvmetad/lvmetad-client.h |
|
index be2623a..a2adfe6 100644 |
|
--- a/daemons/lvmetad/lvmetad-client.h |
|
+++ b/daemons/lvmetad/lvmetad-client.h |
|
@@ -22,6 +22,7 @@ |
|
#define LVMETAD_TOKEN_UPDATE_IN_PROGRESS "update in progress" |
|
|
|
#define LVMETAD_DISABLE_REASON_DIRECT "DIRECT" |
|
+#define LVMETAD_DISABLE_REASON_LVM1 "LVM1" |
|
#define LVMETAD_DISABLE_REASON_DUPLICATES "DUPLICATES" |
|
#define LVMETAD_DISABLE_REASON_VGRESTORE "VGRESTORE" |
|
#define LVMETAD_DISABLE_REASON_REPAIR "REPAIR" |
|
diff --git a/daemons/lvmetad/lvmetad-core.c b/daemons/lvmetad/lvmetad-core.c |
|
index 72473d7..f4f5f42 100644 |
|
--- a/daemons/lvmetad/lvmetad-core.c |
|
+++ b/daemons/lvmetad/lvmetad-core.c |
|
@@ -200,12 +200,12 @@ struct vg_info { |
|
#define GLFL_INVALID 0x00000001 |
|
#define GLFL_DISABLE 0x00000002 |
|
#define GLFL_DISABLE_REASON_DIRECT 0x00000004 |
|
- /* 0x00000008 */ |
|
+#define GLFL_DISABLE_REASON_LVM1 0x00000008 |
|
#define GLFL_DISABLE_REASON_DUPLICATES 0x00000010 |
|
#define GLFL_DISABLE_REASON_VGRESTORE 0x00000020 |
|
#define GLFL_DISABLE_REASON_REPAIR 0x00000040 |
|
|
|
-#define GLFL_DISABLE_REASON_ALL (GLFL_DISABLE_REASON_DIRECT | GLFL_DISABLE_REASON_REPAIR | GLFL_DISABLE_REASON_DUPLICATES | GLFL_DISABLE_REASON_VGRESTORE) |
|
+#define GLFL_DISABLE_REASON_ALL (GLFL_DISABLE_REASON_DIRECT | GLFL_DISABLE_REASON_REPAIR | GLFL_DISABLE_REASON_LVM1 | GLFL_DISABLE_REASON_DUPLICATES | GLFL_DISABLE_REASON_VGRESTORE) |
|
|
|
#define VGFL_INVALID 0x00000001 |
|
|
|
@@ -2369,6 +2369,8 @@ static response set_global_info(lvmetad_state *s, request r) |
|
reason_flags |= GLFL_DISABLE_REASON_DIRECT; |
|
if (strstr(reason, LVMETAD_DISABLE_REASON_REPAIR)) |
|
reason_flags |= GLFL_DISABLE_REASON_REPAIR; |
|
+ if (strstr(reason, LVMETAD_DISABLE_REASON_LVM1)) |
|
+ reason_flags |= GLFL_DISABLE_REASON_LVM1; |
|
if (strstr(reason, LVMETAD_DISABLE_REASON_DUPLICATES)) |
|
reason_flags |= GLFL_DISABLE_REASON_DUPLICATES; |
|
if (strstr(reason, LVMETAD_DISABLE_REASON_VGRESTORE)) |
|
@@ -2427,9 +2429,10 @@ static response get_global_info(lvmetad_state *s, request r) |
|
pid = (int)daemon_request_int(r, "pid", 0); |
|
|
|
if (s->flags & GLFL_DISABLE) { |
|
- snprintf(reason, REASON_BUF_SIZE, "%s%s%s%s", |
|
+ snprintf(reason, REASON_BUF_SIZE, "%s%s%s%s%s", |
|
(s->flags & GLFL_DISABLE_REASON_DIRECT) ? LVMETAD_DISABLE_REASON_DIRECT "," : "", |
|
(s->flags & GLFL_DISABLE_REASON_REPAIR) ? LVMETAD_DISABLE_REASON_REPAIR "," : "", |
|
+ (s->flags & GLFL_DISABLE_REASON_LVM1) ? LVMETAD_DISABLE_REASON_LVM1 "," : "", |
|
(s->flags & GLFL_DISABLE_REASON_DUPLICATES) ? LVMETAD_DISABLE_REASON_DUPLICATES "," : "", |
|
(s->flags & GLFL_DISABLE_REASON_VGRESTORE) ? LVMETAD_DISABLE_REASON_VGRESTORE "," : ""); |
|
} |
|
diff --git a/include/configure.h.in b/include/configure.h.in |
|
index 15fd150..605f1e1 100644 |
|
--- a/include/configure.h.in |
|
+++ b/include/configure.h.in |
|
@@ -72,6 +72,10 @@ |
|
/* Default system configuration directory. */ |
|
#undef DEFAULT_ETC_DIR |
|
|
|
+/* Fall back to LVM1 by default if device-mapper is missing from the kernel. |
|
+ */ |
|
+#undef DEFAULT_FALLBACK_TO_LVM1 |
|
+ |
|
/* Name of default locking directory. */ |
|
#undef DEFAULT_LOCK_DIR |
|
|
|
@@ -616,6 +620,13 @@ |
|
slash. */ |
|
#undef LSTAT_FOLLOWS_SLASHED_SYMLINK |
|
|
|
+/* Define to 1 if 'lvm' should fall back to using LVM1 binaries if |
|
+ device-mapper is missing from the kernel */ |
|
+#undef LVM1_FALLBACK |
|
+ |
|
+/* Define to 1 to include built-in support for LVM1 metadata. */ |
|
+#undef LVM1_INTERNAL |
|
+ |
|
/* Path to lvmetad pidfile. */ |
|
#undef LVMETAD_PIDFILE |
|
|
|
@@ -678,6 +689,9 @@ |
|
/* Define to the version of this package. */ |
|
#undef PACKAGE_VERSION |
|
|
|
+/* Define to 1 to include built-in support for GFS pool metadata. */ |
|
+#undef POOL_INTERNAL |
|
+ |
|
/* Define to 1 to include built-in support for raid. */ |
|
#undef RAID_INTERNAL |
|
|
|
diff --git a/lib/Makefile.in b/lib/Makefile.in |
|
index 1d42235..241cf09 100644 |
|
--- a/lib/Makefile.in |
|
+++ b/lib/Makefile.in |
|
@@ -16,6 +16,14 @@ srcdir = @srcdir@ |
|
top_srcdir = @top_srcdir@ |
|
top_builddir = @top_builddir@ |
|
|
|
+ifeq ("@LVM1@", "shared") |
|
+ SUBDIRS = format1 |
|
+endif |
|
+ |
|
+ifeq ("@POOL@", "shared") |
|
+ SUBDIRS += format_pool |
|
+endif |
|
+ |
|
ifeq ("@CLUSTER@", "shared") |
|
SUBDIRS += locking |
|
endif |
|
@@ -107,6 +115,25 @@ SOURCES =\ |
|
uuid/uuid.c \ |
|
zero/zero.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 |
|
+ |
|
+ifeq ("@POOL@", "internal") |
|
+ SOURCES +=\ |
|
+ format_pool/disk_rep.c \ |
|
+ format_pool/format_pool.c \ |
|
+ format_pool/import_export.c \ |
|
+ format_pool/pool_label.c |
|
+endif |
|
+ |
|
ifeq ("@CLUSTER@", "internal") |
|
SOURCES += locking/cluster_locking.c |
|
endif |
|
@@ -143,6 +170,8 @@ LIB_STATIC = $(LIB_NAME).a |
|
|
|
ifeq ($(MAKECMDGOALS),distclean) |
|
SUBDIRS =\ |
|
+ format1 \ |
|
+ format_pool \ |
|
notify \ |
|
locking |
|
endif |
|
diff --git a/lib/activate/activate.c b/lib/activate/activate.c |
|
index 56ec732..18f4b84 100644 |
|
--- a/lib/activate/activate.c |
|
+++ b/lib/activate/activate.c |
|
@@ -37,6 +37,19 @@ |
|
|
|
#define _skip(fmt, args...) log_very_verbose("Skipping: " fmt , ## args) |
|
|
|
+int lvm1_present(struct cmd_context *cmd) |
|
+{ |
|
+ static char path[PATH_MAX]; |
|
+ |
|
+ if (dm_snprintf(path, sizeof(path), "%s/lvm/global", cmd->proc_dir) |
|
+ < 0) { |
|
+ log_error("LVM1 proc global snprintf failed"); |
|
+ return 0; |
|
+ } |
|
+ |
|
+ return (path_exists(path)) ? 1 : 0; |
|
+} |
|
+ |
|
int list_segment_modules(struct dm_pool *mem, const struct lv_segment *seg, |
|
struct dm_list *modules) |
|
{ |
|
diff --git a/lib/activate/activate.h b/lib/activate/activate.h |
|
index d2c6db7..2fc74ce 100644 |
|
--- a/lib/activate/activate.h |
|
+++ b/lib/activate/activate.h |
|
@@ -91,6 +91,7 @@ int activation(void); |
|
|
|
int driver_version(char *version, size_t size); |
|
int library_version(char *version, size_t size); |
|
+int lvm1_present(struct cmd_context *cmd); |
|
|
|
int module_present(struct cmd_context *cmd, const char *target_name); |
|
int target_present_version(struct cmd_context *cmd, const char *target_name, |
|
diff --git a/lib/cache/lvmcache.c b/lib/cache/lvmcache.c |
|
index 3e681a2..2fba3ff 100644 |
|
--- a/lib/cache/lvmcache.c |
|
+++ b/lib/cache/lvmcache.c |
|
@@ -22,6 +22,8 @@ |
|
#include "memlock.h" |
|
#include "str_list.h" |
|
#include "format-text.h" |
|
+#include "format_pool.h" |
|
+#include "format1.h" |
|
#include "config.h" |
|
|
|
#include "lvmetad.h" |
|
@@ -543,6 +545,8 @@ void lvmcache_drop_metadata(const char *vgname, int drop_precommitted) |
|
/* For VG_ORPHANS, we need to invalidate all labels on orphan PVs. */ |
|
if (!strcmp(vgname, VG_ORPHANS)) { |
|
_drop_metadata(FMT_TEXT_ORPHAN_VG_NAME, 0); |
|
+ _drop_metadata(FMT_LVM1_ORPHAN_VG_NAME, 0); |
|
+ _drop_metadata(FMT_POOL_ORPHAN_VG_NAME, 0); |
|
} else |
|
_drop_metadata(vgname, drop_precommitted); |
|
} |
|
diff --git a/lib/cache/lvmetad.c b/lib/cache/lvmetad.c |
|
index a1ab41a..2a2d7ea 100644 |
|
--- a/lib/cache/lvmetad.c |
|
+++ b/lib/cache/lvmetad.c |
|
@@ -37,6 +37,8 @@ static const char *_lvmetad_socket = NULL; |
|
static struct cmd_context *_lvmetad_cmd = NULL; |
|
static int64_t _lvmetad_update_timeout; |
|
|
|
+static int _found_lvm1_metadata = 0; |
|
+ |
|
static struct volume_group *_lvmetad_pvscan_vg(struct cmd_context *cmd, struct volume_group *vg, const char *vgid, struct format_type *fmt); |
|
|
|
static uint64_t _monotonic_seconds(void) |
|
@@ -2277,6 +2279,18 @@ int lvmetad_pvscan_single(struct cmd_context *cmd, struct device *dev, |
|
if (!baton.fid) |
|
goto_bad; |
|
|
|
+ if (fmt->features & FMT_OBSOLETE) { |
|
+ fmt->ops->destroy_instance(baton.fid); |
|
+ log_warn("WARNING: Disabling lvmetad cache which does not support obsolete (lvm1) metadata."); |
|
+ lvmetad_set_disabled(cmd, LVMETAD_DISABLE_REASON_LVM1); |
|
+ _found_lvm1_metadata = 1; |
|
+ /* |
|
+ * return 1 (success) so that we'll continue to populate lvmetad |
|
+ * instead of leaving the update incomplete. |
|
+ */ |
|
+ return 1; |
|
+ } |
|
+ |
|
lvmcache_foreach_mda(info, _lvmetad_pvscan_single, &baton); |
|
|
|
if (!baton.vg) |
|
@@ -2440,9 +2454,11 @@ int lvmetad_pvscan_all_devs(struct cmd_context *cmd, int do_wait) |
|
} |
|
|
|
/* |
|
- * If lvmetad is disabled, and no duplicate PVs were seen, then re-enable lvmetad. |
|
+ * If lvmetad is disabled, and no lvm1 metadata was seen and no |
|
+ * duplicate PVs were seen, then re-enable lvmetad. |
|
*/ |
|
- if (lvmetad_is_disabled(cmd, &reason) && !lvmcache_found_duplicate_pvs()) { |
|
+ if (lvmetad_is_disabled(cmd, &reason) && |
|
+ !lvmcache_found_duplicate_pvs() && !_found_lvm1_metadata) { |
|
log_debug_lvmetad("Enabling lvmetad which was previously disabled."); |
|
lvmetad_clear_disabled(cmd); |
|
} |
|
@@ -3058,6 +3074,9 @@ int lvmetad_is_disabled(struct cmd_context *cmd, const char **reason) |
|
} else if (strstr(reply_reason, LVMETAD_DISABLE_REASON_REPAIR)) { |
|
*reason = "a repair command was run"; |
|
|
|
+ } else if (strstr(reply_reason, LVMETAD_DISABLE_REASON_LVM1)) { |
|
+ *reason = "LVM1 metadata was found"; |
|
+ |
|
} else if (strstr(reply_reason, LVMETAD_DISABLE_REASON_DUPLICATES)) { |
|
*reason = "duplicate PVs were found"; |
|
|
|
diff --git a/lib/commands/toolcontext.c b/lib/commands/toolcontext.c |
|
index c9596e2..2b72645 100644 |
|
--- a/lib/commands/toolcontext.c |
|
+++ b/lib/commands/toolcontext.c |
|
@@ -36,6 +36,14 @@ |
|
#include "sharedlib.h" |
|
#endif |
|
|
|
+#ifdef LVM1_INTERNAL |
|
+#include "format1.h" |
|
+#endif |
|
+ |
|
+#ifdef POOL_INTERNAL |
|
+#include "format_pool.h" |
|
+#endif |
|
+ |
|
#include <locale.h> |
|
#include <sys/stat.h> |
|
#include <sys/syscall.h> |
|
@@ -1134,12 +1142,14 @@ static struct dev_filter *_init_lvmetad_filter_chain(struct cmd_context *cmd) |
|
} |
|
nr_filt++; |
|
|
|
+#if 0 |
|
/* signature filter. Required. */ |
|
if (!(filters[nr_filt] = signature_filter_create(cmd->dev_types))) { |
|
log_error("Failed to create signature device filter"); |
|
goto bad; |
|
} |
|
nr_filt++; |
|
+#endif |
|
|
|
/* md component filter. Optional, non-critical. */ |
|
if (find_config_tree_bool(cmd, devices_md_component_detection_CFG, NULL)) { |
|
@@ -1340,6 +1350,20 @@ static int _init_formats(struct cmd_context *cmd) |
|
const struct dm_config_node *cn; |
|
#endif |
|
|
|
+#ifdef LVM1_INTERNAL |
|
+ if (!(fmt = init_lvm1_format(cmd))) |
|
+ return 0; |
|
+ fmt->library = NULL; |
|
+ dm_list_add(&cmd->formats, &fmt->list); |
|
+#endif |
|
+ |
|
+#ifdef POOL_INTERNAL |
|
+ if (!(fmt = init_pool_format(cmd))) |
|
+ return 0; |
|
+ fmt->library = NULL; |
|
+ dm_list_add(&cmd->formats, &fmt->list); |
|
+#endif |
|
+ |
|
#ifdef HAVE_LIBDL |
|
/* Load any formats in shared libs if not static */ |
|
if (!is_static() && |
|
diff --git a/lib/config/config_settings.h b/lib/config/config_settings.h |
|
index e98ca6d..6d79087 100644 |
|
--- a/lib/config/config_settings.h |
|
+++ b/lib/config/config_settings.h |
|
@@ -767,14 +767,26 @@ cfg(global_activation_CFG, "activation", global_CFG_SECTION, 0, CFG_TYPE_BOOL, D |
|
"is not present in the kernel, disabling this should suppress\n" |
|
"the error messages.\n") |
|
|
|
-cfg(global_fallback_to_lvm1_CFG, "fallback_to_lvm1", global_CFG_SECTION, CFG_DEFAULT_COMMENTED, CFG_TYPE_BOOL, 0, vsn(1, 0, 18), NULL, 0, NULL, |
|
- "This setting is no longer used.\n") |
|
+cfg(global_fallback_to_lvm1_CFG, "fallback_to_lvm1", global_CFG_SECTION, CFG_DEFAULT_COMMENTED, CFG_TYPE_BOOL, DEFAULT_FALLBACK_TO_LVM1, vsn(1, 0, 18), "@DEFAULT_FALLBACK_TO_LVM1@", 0, NULL, |
|
+ "Try running LVM1 tools if LVM cannot communicate with DM.\n" |
|
+ "This option only applies to 2.4 kernels and is provided to help\n" |
|
+ "switch between device-mapper kernels and LVM1 kernels. The LVM1\n" |
|
+ "tools need to be installed with .lvm1 suffices, e.g. vgscan.lvm1.\n" |
|
+ "They will stop working once the lvm2 on-disk metadata format is used.\n") |
|
|
|
cfg(global_format_CFG, "format", global_CFG_SECTION, CFG_DEFAULT_COMMENTED, CFG_TYPE_STRING, DEFAULT_FORMAT, vsn(1, 0, 0), NULL, 0, NULL, |
|
- "This setting is no longer used.\n") |
|
- |
|
+ "The default metadata format that commands should use.\n" |
|
+ "The -M 1|2 option overrides this setting.\n" |
|
+ "#\n" |
|
+ "Accepted values:\n" |
|
+ " lvm1\n" |
|
+ " lvm2\n" |
|
+ "#\n") |
|
+ |
|
cfg_array(global_format_libraries_CFG, "format_libraries", global_CFG_SECTION, CFG_DEFAULT_UNDEFINED, CFG_TYPE_STRING, NULL, vsn(1, 0, 0), NULL, 0, NULL, |
|
- "This setting is no longer used.") |
|
+ "Shared libraries that process different metadata formats.\n" |
|
+ "If support for LVM1 metadata was compiled as a shared library use\n" |
|
+ "format_libraries = \"liblvm2format1.so\"\n") |
|
|
|
cfg_array(global_segment_libraries_CFG, "segment_libraries", global_CFG_SECTION, CFG_DEFAULT_UNDEFINED, CFG_TYPE_STRING, NULL, vsn(1, 0, 18), NULL, 0, NULL, NULL) |
|
|
|
diff --git a/lib/display/display.c b/lib/display/display.c |
|
index 9b4be88..9a928d9 100644 |
|
--- a/lib/display/display.c |
|
+++ b/lib/display/display.c |
|
@@ -705,10 +705,13 @@ void vgdisplay_full(const struct volume_group *vg) |
|
|
|
log_print("--- Volume group ---"); |
|
log_print("VG Name %s", vg->name); |
|
- log_print("System ID %s", (vg->system_id && *vg->system_id) ? vg->system_id : ""); |
|
+ log_print("System ID %s", (vg->system_id && *vg->system_id) ? vg->system_id : vg->lvm1_system_id ? : ""); |
|
log_print("Format %s", vg->fid->fmt->name); |
|
- log_print("Metadata Areas %d", vg_mda_count(vg)); |
|
- log_print("Metadata Sequence No %d", vg->seqno); |
|
+ if (vg->fid->fmt->features & FMT_MDAS) { |
|
+ log_print("Metadata Areas %d", |
|
+ vg_mda_count(vg)); |
|
+ log_print("Metadata Sequence No %d", vg->seqno); |
|
+ } |
|
access_str = vg->status & (LVM_READ | LVM_WRITE); |
|
log_print("VG Access %s%s%s%s", |
|
access_str == (LVM_READ | LVM_WRITE) ? "read/write" : "", |
|
diff --git a/lib/format1/.exported_symbols b/lib/format1/.exported_symbols |
|
new file mode 100644 |
|
index 0000000..e9fac2e |
|
--- /dev/null |
|
+++ b/lib/format1/.exported_symbols |
|
@@ -0,0 +1 @@ |
|
+init_format |
|
diff --git a/lib/format1/Makefile.in b/lib/format1/Makefile.in |
|
new file mode 100644 |
|
index 0000000..4a905c0 |
|
--- /dev/null |
|
+++ b/lib/format1/Makefile.in |
|
@@ -0,0 +1,33 @@ |
|
+# |
|
+# Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved. |
|
+# Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved. |
|
+# |
|
+# This file is part of LVM2. |
|
+# |
|
+# This copyrighted material is made available to anyone wishing to use, |
|
+# modify, copy, or redistribute it subject to the terms and conditions |
|
+# of the GNU General Public License v.2. |
|
+# |
|
+# You should have received a copy of the GNU General Public License |
|
+# along with this program; if not, write to the Free Software Foundation, |
|
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
|
+ |
|
+srcdir = @srcdir@ |
|
+top_srcdir = @top_srcdir@ |
|
+top_builddir = @top_builddir@ |
|
+ |
|
+SOURCES =\ |
|
+ disk-rep.c \ |
|
+ format1.c \ |
|
+ import-export.c \ |
|
+ import-extents.c \ |
|
+ layout.c \ |
|
+ lvm1-label.c \ |
|
+ vg_number.c |
|
+ |
|
+LIB_SHARED = liblvm2format1.$(LIB_SUFFIX) |
|
+LIB_VERSION = $(LIB_VERSION_LVM) |
|
+ |
|
+include $(top_builddir)/make.tmpl |
|
+ |
|
+install: install_lvm2_plugin |
|
diff --git a/lib/format1/disk-rep.c b/lib/format1/disk-rep.c |
|
new file mode 100644 |
|
index 0000000..9169d82 |
|
--- /dev/null |
|
+++ b/lib/format1/disk-rep.c |
|
@@ -0,0 +1,761 @@ |
|
+/* |
|
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved. |
|
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved. |
|
+ * |
|
+ * This file is part of LVM2. |
|
+ * |
|
+ * This copyrighted material is made available to anyone wishing to use, |
|
+ * modify, copy, or redistribute it subject to the terms and conditions |
|
+ * of the GNU Lesser General Public License v.2.1. |
|
+ * |
|
+ * You should have received a copy of the GNU Lesser General Public License |
|
+ * along with this program; if not, write to the Free Software Foundation, |
|
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
|
+ */ |
|
+ |
|
+#include "lib.h" |
|
+#include "disk-rep.h" |
|
+#include "xlate.h" |
|
+#include "lvmcache.h" |
|
+#include "metadata-exported.h" |
|
+ |
|
+#include <fcntl.h> |
|
+ |
|
+#define xx16(v) disk->v = xlate16(disk->v) |
|
+#define xx32(v) disk->v = xlate32(disk->v) |
|
+#define xx64(v) disk->v = xlate64(disk->v) |
|
+ |
|
+/* |
|
+ * Functions to perform the endian conversion |
|
+ * between disk and core. The same code works |
|
+ * both ways of course. |
|
+ */ |
|
+static void _xlate_pvd(struct pv_disk *disk) |
|
+{ |
|
+ xx16(version); |
|
+ |
|
+ xx32(pv_on_disk.base); |
|
+ xx32(pv_on_disk.size); |
|
+ xx32(vg_on_disk.base); |
|
+ xx32(vg_on_disk.size); |
|
+ xx32(pv_uuidlist_on_disk.base); |
|
+ xx32(pv_uuidlist_on_disk.size); |
|
+ xx32(lv_on_disk.base); |
|
+ xx32(lv_on_disk.size); |
|
+ xx32(pe_on_disk.base); |
|
+ xx32(pe_on_disk.size); |
|
+ |
|
+ xx32(pv_major); |
|
+ xx32(pv_number); |
|
+ xx32(pv_status); |
|
+ xx32(pv_allocatable); |
|
+ xx32(pv_size); |
|
+ xx32(lv_cur); |
|
+ xx32(pe_size); |
|
+ xx32(pe_total); |
|
+ xx32(pe_allocated); |
|
+ xx32(pe_start); |
|
+} |
|
+ |
|
+static void _xlate_lvd(struct lv_disk *disk) |
|
+{ |
|
+ xx32(lv_access); |
|
+ xx32(lv_status); |
|
+ xx32(lv_open); |
|
+ xx32(lv_dev); |
|
+ xx32(lv_number); |
|
+ xx32(lv_mirror_copies); |
|
+ xx32(lv_recovery); |
|
+ xx32(lv_schedule); |
|
+ xx32(lv_size); |
|
+ xx32(lv_snapshot_minor); |
|
+ xx16(lv_chunk_size); |
|
+ xx16(dummy); |
|
+ xx32(lv_allocated_le); |
|
+ xx32(lv_stripes); |
|
+ xx32(lv_stripesize); |
|
+ xx32(lv_badblock); |
|
+ xx32(lv_allocation); |
|
+ xx32(lv_io_timeout); |
|
+ xx32(lv_read_ahead); |
|
+} |
|
+ |
|
+static void _xlate_vgd(struct vg_disk *disk) |
|
+{ |
|
+ xx32(vg_number); |
|
+ xx32(vg_access); |
|
+ xx32(vg_status); |
|
+ xx32(lv_max); |
|
+ xx32(lv_cur); |
|
+ xx32(lv_open); |
|
+ xx32(pv_max); |
|
+ xx32(pv_cur); |
|
+ xx32(pv_act); |
|
+ xx32(dummy); |
|
+ xx32(vgda); |
|
+ xx32(pe_size); |
|
+ xx32(pe_total); |
|
+ xx32(pe_allocated); |
|
+ xx32(pvg_total); |
|
+} |
|
+ |
|
+static void _xlate_extents(struct pe_disk *extents, uint32_t count) |
|
+{ |
|
+ unsigned i; |
|
+ |
|
+ for (i = 0; i < count; i++) { |
|
+ extents[i].lv_num = xlate16(extents[i].lv_num); |
|
+ extents[i].le_num = xlate16(extents[i].le_num); |
|
+ } |
|
+} |
|
+ |
|
+/* |
|
+ * Handle both minor metadata formats. |
|
+ */ |
|
+static int _munge_formats(struct pv_disk *pvd) |
|
+{ |
|
+ uint32_t pe_start; |
|
+ unsigned b, e; |
|
+ |
|
+ switch (pvd->version) { |
|
+ case 1: |
|
+ pvd->pe_start = ((pvd->pe_on_disk.base + |
|
+ pvd->pe_on_disk.size) >> SECTOR_SHIFT); |
|
+ break; |
|
+ |
|
+ case 2: |
|
+ pvd->version = 1; |
|
+ pe_start = pvd->pe_start << SECTOR_SHIFT; |
|
+ pvd->pe_on_disk.size = pe_start - pvd->pe_on_disk.base; |
|
+ break; |
|
+ |
|
+ default: |
|
+ return 0; |
|
+ } |
|
+ |
|
+ /* UUID too long? */ |
|
+ if (pvd->pv_uuid[ID_LEN]) { |
|
+ /* Retain ID_LEN chars from end */ |
|
+ for (e = ID_LEN; e < sizeof(pvd->pv_uuid); e++) { |
|
+ if (!pvd->pv_uuid[e]) { |
|
+ e--; |
|
+ break; |
|
+ } |
|
+ } |
|
+ for (b = 0; b < ID_LEN; b++) { |
|
+ pvd->pv_uuid[b] = pvd->pv_uuid[++e - ID_LEN]; |
|
+ /* FIXME Remove all invalid chars */ |
|
+ if (pvd->pv_uuid[b] == '/') |
|
+ pvd->pv_uuid[b] = '#'; |
|
+ } |
|
+ memset(&pvd->pv_uuid[ID_LEN], 0, sizeof(pvd->pv_uuid) - ID_LEN); |
|
+ } |
|
+ |
|
+ /* If UUID is missing, create one */ |
|
+ if (pvd->pv_uuid[0] == '\0') { |
|
+ uuid_from_num((char *)pvd->pv_uuid, pvd->pv_number); |
|
+ pvd->pv_uuid[ID_LEN] = '\0'; |
|
+ } |
|
+ |
|
+ return 1; |
|
+} |
|
+ |
|
+/* |
|
+ * If exported, remove "PV_EXP" from end of VG name |
|
+ */ |
|
+static void _munge_exported_vg(struct pv_disk *pvd) |
|
+{ |
|
+ int l; |
|
+ size_t s; |
|
+ |
|
+ /* Return if PV not in a VG */ |
|
+ if ((!*pvd->vg_name)) |
|
+ return; |
|
+ /* FIXME also check vgd->status & VG_EXPORTED? */ |
|
+ |
|
+ l = strlen((char *)pvd->vg_name); |
|
+ s = sizeof(EXPORTED_TAG); |
|
+ if (!strncmp((char *)pvd->vg_name + l - s + 1, EXPORTED_TAG, s)) { |
|
+ pvd->vg_name[l - s + 1] = '\0'; |
|
+ pvd->pv_status |= VG_EXPORTED; |
|
+ } |
|
+} |
|
+ |
|
+int munge_pvd(struct device *dev, struct pv_disk *pvd) |
|
+{ |
|
+ _xlate_pvd(pvd); |
|
+ |
|
+ if (pvd->id[0] != 'H' || pvd->id[1] != 'M') { |
|
+ log_very_verbose("%s does not have a valid LVM1 PV identifier", |
|
+ dev_name(dev)); |
|
+ return 0; |
|
+ } |
|
+ |
|
+ if (!_munge_formats(pvd)) { |
|
+ log_very_verbose("format1: Unknown metadata version %d " |
|
+ "found on %s", pvd->version, dev_name(dev)); |
|
+ return 0; |
|
+ } |
|
+ |
|
+ /* If VG is exported, set VG name back to the real name */ |
|
+ _munge_exported_vg(pvd); |
|
+ |
|
+ return 1; |
|
+} |
|
+ |
|
+static int _read_pvd(struct device *dev, struct pv_disk *pvd) |
|
+{ |
|
+ if (!dev_read_bytes(dev, UINT64_C(0), sizeof(*pvd), pvd)) { |
|
+ log_very_verbose("Failed to read PV data from %s", |
|
+ dev_name(dev)); |
|
+ return 0; |
|
+ } |
|
+ |
|
+ return munge_pvd(dev, pvd); |
|
+} |
|
+ |
|
+static int _read_lvd(struct device *dev, uint64_t pos, struct lv_disk *disk) |
|
+{ |
|
+ if (!dev_read_bytes(dev, pos, sizeof(*disk), disk)) |
|
+ return_0; |
|
+ |
|
+ _xlate_lvd(disk); |
|
+ |
|
+ return 1; |
|
+} |
|
+ |
|
+int read_vgd(struct device *dev, struct vg_disk *vgd, struct pv_disk *pvd) |
|
+{ |
|
+ uint64_t pos = pvd->vg_on_disk.base; |
|
+ |
|
+ if (!dev_read_bytes(dev, pos, sizeof(*vgd), vgd)) |
|
+ return_0; |
|
+ |
|
+ _xlate_vgd(vgd); |
|
+ |
|
+ if ((vgd->lv_max > MAX_LV) || (vgd->pv_max > MAX_PV)) |
|
+ return_0; |
|
+ |
|
+ /* If UUID is missing, create one */ |
|
+ if (vgd->vg_uuid[0] == '\0') |
|
+ uuid_from_num((char *)vgd->vg_uuid, vgd->vg_number); |
|
+ |
|
+ return 1; |
|
+} |
|
+ |
|
+static int _read_uuids(struct disk_list *data) |
|
+{ |
|
+ unsigned num_read = 0; |
|
+ struct uuid_list *ul; |
|
+ char buffer[NAME_LEN] __attribute__((aligned(8))); |
|
+ uint64_t pos = data->pvd.pv_uuidlist_on_disk.base; |
|
+ uint64_t end = pos + data->pvd.pv_uuidlist_on_disk.size; |
|
+ |
|
+ while (pos < end && num_read < data->vgd.pv_cur) { |
|
+ if (!dev_read_bytes(data->dev, pos, sizeof(buffer), buffer)) |
|
+ return_0; |
|
+ |
|
+ if (!(ul = dm_pool_alloc(data->mem, sizeof(*ul)))) |
|
+ return_0; |
|
+ |
|
+ memcpy(ul->uuid, buffer, NAME_LEN); |
|
+ ul->uuid[NAME_LEN - 1] = '\0'; |
|
+ |
|
+ dm_list_add(&data->uuids, &ul->list); |
|
+ |
|
+ pos += NAME_LEN; |
|
+ num_read++; |
|
+ } |
|
+ |
|
+ return 1; |
|
+} |
|
+ |
|
+static int _check_lvd(struct lv_disk *lvd) |
|
+{ |
|
+ return !(lvd->lv_name[0] == '\0'); |
|
+} |
|
+ |
|
+static int _read_lvs(struct disk_list *data) |
|
+{ |
|
+ unsigned int i, lvs_read = 0; |
|
+ uint64_t pos; |
|
+ struct lvd_list *ll; |
|
+ struct vg_disk *vgd = &data->vgd; |
|
+ |
|
+ for (i = 0; (i < vgd->lv_max) && (lvs_read < vgd->lv_cur); i++) { |
|
+ pos = data->pvd.lv_on_disk.base + (i * sizeof(struct lv_disk)); |
|
+ ll = dm_pool_alloc(data->mem, sizeof(*ll)); |
|
+ |
|
+ if (!ll) |
|
+ return_0; |
|
+ |
|
+ if (!_read_lvd(data->dev, pos, &ll->lvd)) |
|
+ return_0; |
|
+ |
|
+ if (!_check_lvd(&ll->lvd)) |
|
+ continue; |
|
+ |
|
+ lvs_read++; |
|
+ dm_list_add(&data->lvds, &ll->list); |
|
+ } |
|
+ |
|
+ return 1; |
|
+} |
|
+ |
|
+static int _read_extents(struct disk_list *data) |
|
+{ |
|
+ size_t len = sizeof(struct pe_disk) * data->pvd.pe_total; |
|
+ struct pe_disk *extents = dm_pool_alloc(data->mem, len); |
|
+ uint64_t pos = data->pvd.pe_on_disk.base; |
|
+ |
|
+ if (!extents) |
|
+ return_0; |
|
+ |
|
+ if (!dev_read_bytes(data->dev, pos, len, extents)) |
|
+ return_0; |
|
+ |
|
+ _xlate_extents(extents, data->pvd.pe_total); |
|
+ data->extents = extents; |
|
+ |
|
+ return 1; |
|
+} |
|
+ |
|
+static void __update_lvmcache(const struct format_type *fmt, |
|
+ struct disk_list *dl, |
|
+ struct device *dev, const char *vgid, |
|
+ unsigned exported) |
|
+{ |
|
+ struct lvmcache_info *info; |
|
+ const char *vgname = *((char *)dl->pvd.vg_name) ? |
|
+ (char *)dl->pvd.vg_name : fmt->orphan_vg_name; |
|
+ |
|
+ if (!(info = lvmcache_add(fmt->labeller, (char *)dl->pvd.pv_uuid, dev, |
|
+ vgname, vgid, exported ? EXPORTED_VG : 0))) { |
|
+ stack; |
|
+ return; |
|
+ } |
|
+ |
|
+ lvmcache_set_device_size(info, ((uint64_t)xlate32(dl->pvd.pv_size)) << SECTOR_SHIFT); |
|
+ lvmcache_del_mdas(info); |
|
+} |
|
+ |
|
+static struct disk_list *__read_disk(const struct format_type *fmt, |
|
+ struct device *dev, struct dm_pool *mem, |
|
+ const char *vg_name) |
|
+{ |
|
+ struct disk_list *dl = dm_pool_zalloc(mem, sizeof(*dl)); |
|
+ const char *name = dev_name(dev); |
|
+ |
|
+ if (!dl) |
|
+ return_NULL; |
|
+ |
|
+ dl->dev = dev; |
|
+ dl->mem = mem; |
|
+ dm_list_init(&dl->uuids); |
|
+ dm_list_init(&dl->lvds); |
|
+ |
|
+ if (!_read_pvd(dev, &dl->pvd)) |
|
+ goto_bad; |
|
+ |
|
+ /* |
|
+ * is it an orphan ? |
|
+ */ |
|
+ if (!*dl->pvd.vg_name) { |
|
+ log_very_verbose("%s is not a member of any format1 VG", name); |
|
+ |
|
+ __update_lvmcache(fmt, dl, dev, fmt->orphan_vg_name, 0); |
|
+ return (vg_name) ? NULL : dl; |
|
+ } |
|
+ |
|
+ if (!read_vgd(dl->dev, &dl->vgd, &dl->pvd)) { |
|
+ log_error("Failed to read VG data from PV (%s)", name); |
|
+ __update_lvmcache(fmt, dl, dev, fmt->orphan_vg_name, 0); |
|
+ goto bad; |
|
+ } |
|
+ |
|
+ if (vg_name && strcmp(vg_name, (char *)dl->pvd.vg_name)) { |
|
+ log_very_verbose("%s is not a member of the VG %s", |
|
+ name, vg_name); |
|
+ __update_lvmcache(fmt, dl, dev, fmt->orphan_vg_name, 0); |
|
+ goto bad; |
|
+ } |
|
+ |
|
+ __update_lvmcache(fmt, dl, dev, (char *)dl->vgd.vg_uuid, |
|
+ dl->vgd.vg_status & VG_EXPORTED); |
|
+ |
|
+ if (!_read_uuids(dl)) { |
|
+ log_error("Failed to read PV uuid list from %s", name); |
|
+ goto bad; |
|
+ } |
|
+ |
|
+ if (!_read_lvs(dl)) { |
|
+ log_error("Failed to read LV's from %s", name); |
|
+ goto bad; |
|
+ } |
|
+ |
|
+ if (!_read_extents(dl)) { |
|
+ log_error("Failed to read extents from %s", name); |
|
+ goto bad; |
|
+ } |
|
+ |
|
+ log_very_verbose("Found %s in %sVG %s", name, |
|
+ (dl->vgd.vg_status & VG_EXPORTED) ? "exported " : "", |
|
+ dl->pvd.vg_name); |
|
+ |
|
+ return dl; |
|
+ |
|
+ bad: |
|
+ dm_pool_free(dl->mem, dl); |
|
+ return NULL; |
|
+} |
|
+ |
|
+struct disk_list *read_disk(const struct format_type *fmt, struct device *dev, |
|
+ struct dm_pool *mem, const char *vg_name) |
|
+{ |
|
+ struct disk_list *dl; |
|
+ |
|
+ if (!dev_open_readonly(dev)) |
|
+ return_NULL; |
|
+ |
|
+ dl = __read_disk(fmt, dev, mem, vg_name); |
|
+ |
|
+ if (!dev_close(dev)) |
|
+ stack; |
|
+ |
|
+ return dl; |
|
+} |
|
+ |
|
+static void _add_pv_to_list(struct cmd_context *cmd, struct dm_list *head, struct disk_list *data) |
|
+{ |
|
+ struct pv_disk *pvd; |
|
+ struct disk_list *diskl; |
|
+ |
|
+ dm_list_iterate_items(diskl, head) { |
|
+ pvd = &diskl->pvd; |
|
+ if (!strncmp((char *)data->pvd.pv_uuid, (char *)pvd->pv_uuid, |
|
+ sizeof(pvd->pv_uuid))) { |
|
+ if (!dev_subsystem_part_major(cmd->dev_types, data->dev)) { |
|
+ log_very_verbose("Ignoring duplicate PV %s on " |
|
+ "%s", pvd->pv_uuid, |
|
+ dev_name(data->dev)); |
|
+ return; |
|
+ } |
|
+ log_very_verbose("Duplicate PV %s - using %s %s", |
|
+ pvd->pv_uuid, dev_subsystem_name(cmd->dev_types, data->dev), |
|
+ dev_name(data->dev)); |
|
+ dm_list_del(&diskl->list); |
|
+ break; |
|
+ } |
|
+ } |
|
+ dm_list_add(head, &data->list); |
|
+} |
|
+ |
|
+struct _read_pvs_in_vg_baton { |
|
+ const char *vg_name; |
|
+ struct dm_list *head; |
|
+ struct disk_list *data; |
|
+ struct dm_pool *mem; |
|
+ int empty; |
|
+}; |
|
+ |
|
+static int _read_pv_in_vg(struct lvmcache_info *info, void *baton) |
|
+{ |
|
+ struct _read_pvs_in_vg_baton *b = baton; |
|
+ |
|
+ b->empty = 0; |
|
+ |
|
+ if (!lvmcache_device(info) || |
|
+ !(b->data = read_disk(lvmcache_fmt(info), lvmcache_device(info), b->mem, b->vg_name))) |
|
+ return 0; /* stop here */ |
|
+ |
|
+ _add_pv_to_list(lvmcache_fmt(info)->cmd, b->head, b->data); |
|
+ return 1; |
|
+} |
|
+ |
|
+/* |
|
+ * Build a list of pv_d's structures, allocated from mem. |
|
+ * We keep track of the first object allocated from the pool |
|
+ * so we can free off all the memory if something goes wrong. |
|
+ */ |
|
+int read_pvs_in_vg(const struct format_type *fmt, const char *vg_name, |
|
+ struct dev_filter *filter, struct dm_pool *mem, |
|
+ struct dm_list *head) |
|
+{ |
|
+ struct dev_iter *iter; |
|
+ struct device *dev; |
|
+ struct lvmcache_vginfo *vginfo; |
|
+ struct _read_pvs_in_vg_baton baton; |
|
+ |
|
+ baton.head = head; |
|
+ baton.empty = 1; |
|
+ baton.data = NULL; |
|
+ baton.mem = mem; |
|
+ baton.vg_name = vg_name; |
|
+ |
|
+ /* Fast path if we already saw this VG and cached the list of PVs */ |
|
+ if (vg_name && (vginfo = lvmcache_vginfo_from_vgname(vg_name, NULL))) { |
|
+ |
|
+ lvmcache_foreach_pv(vginfo, _read_pv_in_vg, &baton); |
|
+ |
|
+ if (!baton.empty) { |
|
+ /* Did we find the whole VG? */ |
|
+ if (!vg_name || is_orphan_vg(vg_name) || |
|
+ (baton.data && *baton.data->pvd.vg_name && |
|
+ dm_list_size(head) == baton.data->vgd.pv_cur)) |
|
+ return 1; |
|
+ |
|
+ /* Failed */ |
|
+ dm_list_init(head); |
|
+ /* vgcache_del(vg_name); */ |
|
+ } |
|
+ } |
|
+ |
|
+ if (!(iter = dev_iter_create(filter, 1))) { |
|
+ log_error("read_pvs_in_vg: dev_iter_create failed"); |
|
+ return 0; |
|
+ } |
|
+ |
|
+ /* Otherwise do a complete scan */ |
|
+ for (dev = dev_iter_get(iter); dev; dev = dev_iter_get(iter)) { |
|
+ if ((baton.data = read_disk(fmt, dev, mem, vg_name))) { |
|
+ _add_pv_to_list(fmt->cmd, head, baton.data); |
|
+ } |
|
+ } |
|
+ dev_iter_destroy(iter); |
|
+ |
|
+ if (dm_list_empty(head)) |
|
+ return 0; |
|
+ |
|
+ return 1; |
|
+} |
|
+ |
|
+static int _write_vgd(struct disk_list *data) |
|
+{ |
|
+ struct vg_disk *vgd = &data->vgd; |
|
+ uint64_t pos = data->pvd.vg_on_disk.base; |
|
+ |
|
+ log_debug_metadata("Writing %s VG metadata to %s at %" PRIu64 " len %" PRIsize_t, |
|
+ data->pvd.vg_name, dev_name(data->dev), pos, sizeof(*vgd)); |
|
+ |
|
+ _xlate_vgd(vgd); |
|
+ if (!dev_write(data->dev, pos, sizeof(*vgd), DEV_IO_FMT1, vgd)) |
|
+ return_0; |
|
+ |
|
+ _xlate_vgd(vgd); |
|
+ |
|
+ return 1; |
|
+} |
|
+ |
|
+static int _write_uuids(struct disk_list *data) |
|
+{ |
|
+ struct uuid_list *ul; |
|
+ uint64_t pos = data->pvd.pv_uuidlist_on_disk.base; |
|
+ uint64_t end = pos + data->pvd.pv_uuidlist_on_disk.size; |
|
+ |
|
+ dm_list_iterate_items(ul, &data->uuids) { |
|
+ if (pos >= end) { |
|
+ log_error("Too many uuids to fit on %s", |
|
+ dev_name(data->dev)); |
|
+ return 0; |
|
+ } |
|
+ |
|
+ log_debug_metadata("Writing %s uuidlist to %s at %" PRIu64 " len %d", |
|
+ data->pvd.vg_name, dev_name(data->dev), |
|
+ pos, NAME_LEN); |
|
+ |
|
+ if (!dev_write(data->dev, pos, NAME_LEN, DEV_IO_FMT1, ul->uuid)) |
|
+ return_0; |
|
+ |
|
+ pos += NAME_LEN; |
|
+ } |
|
+ |
|
+ return 1; |
|
+} |
|
+ |
|
+static int _write_lvd(struct device *dev, uint64_t pos, struct lv_disk *disk) |
|
+{ |
|
+ log_debug_metadata("Writing %s LV %s metadata to %s at %" PRIu64 " len %" |
|
+ PRIsize_t, disk->vg_name, disk->lv_name, dev_name(dev), |
|
+ pos, sizeof(*disk)); |
|
+ |
|
+ _xlate_lvd(disk); |
|
+ if (!dev_write(dev, pos, sizeof(*disk), DEV_IO_FMT1, disk)) |
|
+ return_0; |
|
+ |
|
+ _xlate_lvd(disk); |
|
+ |
|
+ return 1; |
|
+} |
|
+ |
|
+static int _write_lvs(struct disk_list *data) |
|
+{ |
|
+ struct lvd_list *ll; |
|
+ uint64_t pos, offset; |
|
+ |
|
+ pos = data->pvd.lv_on_disk.base; |
|
+ |
|
+ if (!dev_set(data->dev, pos, data->pvd.lv_on_disk.size, DEV_IO_FMT1, 0)) { |
|
+ log_error("Couldn't zero lv area on device '%s'", |
|
+ dev_name(data->dev)); |
|
+ return 0; |
|
+ } |
|
+ |
|
+ dm_list_iterate_items(ll, &data->lvds) { |
|
+ offset = sizeof(struct lv_disk) * ll->lvd.lv_number; |
|
+ if (offset + sizeof(struct lv_disk) > data->pvd.lv_on_disk.size) { |
|
+ log_error("lv_number %d too large", ll->lvd.lv_number); |
|
+ return 0; |
|
+ } |
|
+ |
|
+ if (!_write_lvd(data->dev, pos + offset, &ll->lvd)) |
|
+ return_0; |
|
+ } |
|
+ |
|
+ return 1; |
|
+} |
|
+ |
|
+static int _write_extents(struct disk_list *data) |
|
+{ |
|
+ size_t len = sizeof(struct pe_disk) * data->pvd.pe_total; |
|
+ struct pe_disk *extents = data->extents; |
|
+ uint64_t pos = data->pvd.pe_on_disk.base; |
|
+ |
|
+ log_debug_metadata("Writing %s extents metadata to %s at %" PRIu64 " len %" |
|
+ PRIsize_t, data->pvd.vg_name, dev_name(data->dev), |
|
+ pos, len); |
|
+ |
|
+ _xlate_extents(extents, data->pvd.pe_total); |
|
+ if (!dev_write(data->dev, pos, len, DEV_IO_FMT1, extents)) |
|
+ return_0; |
|
+ |
|
+ _xlate_extents(extents, data->pvd.pe_total); |
|
+ |
|
+ return 1; |
|
+} |
|
+ |
|
+static int _write_pvd(struct disk_list *data) |
|
+{ |
|
+ char *buf; |
|
+ uint64_t pos = data->pvd.pv_on_disk.base; |
|
+ size_t size = data->pvd.pv_on_disk.size; |
|
+ |
|
+ if (size < sizeof(struct pv_disk)) { |
|
+ log_error("Invalid PV structure size."); |
|
+ return 0; |
|
+ } |
|
+ |
|
+ /* Make sure that the gap between the PV structure and |
|
+ the next one is zeroed in order to make non LVM tools |
|
+ happy (idea from AED) */ |
|
+ buf = dm_zalloc(size); |
|
+ if (!buf) { |
|
+ log_error("Couldn't allocate temporary PV buffer."); |
|
+ return 0; |
|
+ } |
|
+ |
|
+ memcpy(buf, &data->pvd, sizeof(struct pv_disk)); |
|
+ |
|
+ log_debug_metadata("Writing %s PV metadata to %s at %" PRIu64 " len %" |
|
+ PRIsize_t, data->pvd.vg_name, dev_name(data->dev), |
|
+ pos, size); |
|
+ |
|
+ _xlate_pvd((struct pv_disk *) buf); |
|
+ if (!dev_write(data->dev, pos, size, DEV_IO_FMT1, buf)) { |
|
+ dm_free(buf); |
|
+ return_0; |
|
+ } |
|
+ |
|
+ dm_free(buf); |
|
+ return 1; |
|
+} |
|
+ |
|
+/* |
|
+ * assumes the device has been opened. |
|
+ */ |
|
+static int __write_all_pvd(const struct format_type *fmt __attribute__((unused)), |
|
+ struct disk_list *data, int write_vg_metadata) |
|
+{ |
|
+ const char *pv_name = dev_name(data->dev); |
|
+ |
|
+ if (!_write_pvd(data)) { |
|
+ log_error("Failed to write PV structure onto %s", pv_name); |
|
+ return 0; |
|
+ } |
|
+ |
|
+ /* vgcache_add(data->pvd.vg_name, data->vgd.vg_uuid, data->dev, fmt); */ |
|
+ /* |
|
+ * Stop here for orphan PVs or if VG metadata write not requested. |
|
+ */ |
|
+ if ((data->pvd.vg_name[0] == '\0') || !write_vg_metadata) { |
|
+ /* if (!test_mode()) |
|
+ vgcache_add(data->pvd.vg_name, NULL, data->dev, fmt); */ |
|
+ return 1; |
|
+ } |
|
+ |
|
+ /* if (!test_mode()) |
|
+ vgcache_add(data->pvd.vg_name, data->vgd.vg_uuid, data->dev, |
|
+ fmt); */ |
|
+ |
|
+ if (!_write_vgd(data)) { |
|
+ log_error("Failed to write VG data to %s", pv_name); |
|
+ return 0; |
|
+ } |
|
+ |
|
+ if (!_write_uuids(data)) { |
|
+ log_error("Failed to write PV uuid list to %s", pv_name); |
|
+ return 0; |
|
+ } |
|
+ |
|
+ if (!_write_lvs(data)) { |
|
+ log_error("Failed to write LV's to %s", pv_name); |
|
+ return 0; |
|
+ } |
|
+ |
|
+ if (!_write_extents(data)) { |
|
+ log_error("Failed to write extents to %s", pv_name); |
|
+ return 0; |
|
+ } |
|
+ |
|
+ return 1; |
|
+} |
|
+ |
|
+/* |
|
+ * opens the device and hands to the above fn. |
|
+ */ |
|
+static int _write_all_pvd(const struct format_type *fmt, struct disk_list *data, int write_vg_metadata) |
|
+{ |
|
+ int r; |
|
+ |
|
+ if (!data->dev) |
|
+ return_0; |
|
+ |
|
+ if (!dev_open(data->dev)) |
|
+ return_0; |
|
+ |
|
+ r = __write_all_pvd(fmt, data, write_vg_metadata); |
|
+ |
|
+ if (!dev_close(data->dev)) |
|
+ stack; |
|
+ |
|
+ return r; |
|
+} |
|
+ |
|
+/* |
|
+ * Writes all the given pv's to disk. Does very |
|
+ * little sanity checking, so make sure correct |
|
+ * data is passed to here. |
|
+ */ |
|
+int write_disks(const struct format_type *fmt, struct dm_list *pvs, int write_vg_metadata) |
|
+{ |
|
+ struct disk_list *dl; |
|
+ |
|
+ dm_list_iterate_items(dl, pvs) { |
|
+ if (!(_write_all_pvd(fmt, dl, write_vg_metadata))) |
|
+ return_0; |
|
+ |
|
+ log_very_verbose("Successfully wrote data to %s", |
|
+ dev_name(dl->dev)); |
|
+ } |
|
+ |
|
+ return 1; |
|
+} |
|
diff --git a/lib/format1/disk-rep.h b/lib/format1/disk-rep.h |
|
new file mode 100644 |
|
index 0000000..221ae8e |
|
--- /dev/null |
|
+++ b/lib/format1/disk-rep.h |
|
@@ -0,0 +1,250 @@ |
|
+/* |
|
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved. |
|
+ * Copyright (C) 2004-2006 Red Hat, Inc. All rights reserved. |
|
+ * |
|
+ * This file is part of LVM2. |
|
+ * |
|
+ * This copyrighted material is made available to anyone wishing to use, |
|
+ * modify, copy, or redistribute it subject to the terms and conditions |
|
+ * of the GNU Lesser General Public License v.2.1. |
|
+ * |
|
+ * You should have received a copy of the GNU Lesser General Public License |
|
+ * along with this program; if not, write to the Free Software Foundation, |
|
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
|
+ */ |
|
+ |
|
+#ifndef DISK_REP_FORMAT1_H |
|
+#define DISK_REP_FORMAT1_H |
|
+ |
|
+#include "metadata.h" |
|
+#include "toolcontext.h" |
|
+ |
|
+#define MAX_PV 256 |
|
+#define MAX_LV 256 |
|
+#define MAX_VG 99 |
|
+ |
|
+#define LVM_BLK_MAJOR 58 |
|
+ |
|
+#define MAX_PV_SIZE ((uint32_t) -1) /* 2TB in sectors - 1 */ |
|
+#define PE_SIZE_PV_SIZE_REL 5 /* PV size must be at least 5 times PE size */ |
|
+#define MAX_LE_TOTAL 65534 /* 2^16 - 2 */ |
|
+#define MAX_PE_TOTAL ((uint32_t) -2) |
|
+ |
|
+#define UNMAPPED_EXTENT 0 |
|
+ |
|
+/* volume group */ |
|
+#define VG_ACTIVE 0x01 /* vg_status */ |
|
+#define VG_EXPORTED 0x02 /* " */ |
|
+#define VG_EXTENDABLE 0x04 /* " */ |
|
+ |
|
+#define VG_READ 0x01 /* vg_access */ |
|
+#define VG_WRITE 0x02 /* " */ |
|
+#define VG_CLUSTERED 0x04 /* " */ |
|
+#define VG_SHARED 0x08 /* " */ |
|
+ |
|
+/* logical volume */ |
|
+#define LV_ACTIVE 0x01 /* lv_status */ |
|
+#define LV_SPINDOWN 0x02 /* " */ |
|
+#define LV_PERSISTENT_MINOR 0x04 /* " */ |
|
+ |
|
+#define LV_READ 0x01 /* lv_access */ |
|
+#define LV_WRITE 0x02 /* " */ |
|
+#define LV_SNAPSHOT 0x04 /* " */ |
|
+#define LV_SNAPSHOT_ORG 0x08 /* " */ |
|
+ |
|
+#define LV_BADBLOCK_ON 0x01 /* lv_badblock */ |
|
+ |
|
+#define LV_STRICT 0x01 /* lv_allocation */ |
|
+#define LV_CONTIGUOUS 0x02 /* " */ |
|
+ |
|
+/* physical volume */ |
|
+#define PV_ACTIVE 0x01 /* pv_status */ |
|
+#define PV_ALLOCATABLE 0x02 /* pv_allocatable */ |
|
+ |
|
+#define EXPORTED_TAG "PV_EXP" /* Identifier for exported PV */ |
|
+#define IMPORTED_TAG "PV_IMP" /* Identifier for imported PV */ |
|
+ |
|
+struct data_area { |
|
+ uint32_t base; |
|
+ uint32_t size; |
|
+} __attribute__ ((packed)); |
|
+ |
|
+struct pv_disk { |
|
+ int8_t id[2]; |
|
+ uint16_t version; /* lvm version */ |
|
+ struct data_area pv_on_disk; |
|
+ struct data_area vg_on_disk; |
|
+ struct data_area pv_uuidlist_on_disk; |
|
+ struct data_area lv_on_disk; |
|
+ struct data_area pe_on_disk; |
|
+ int8_t pv_uuid[NAME_LEN]; |
|
+ int8_t vg_name[NAME_LEN]; |
|
+ int8_t system_id[NAME_LEN]; /* for vgexport/vgimport */ |
|
+ uint32_t pv_major; |
|
+ uint32_t pv_number; |
|
+ uint32_t pv_status; |
|
+ uint32_t pv_allocatable; |
|
+ uint32_t pv_size; |
|
+ uint32_t lv_cur; |
|
+ uint32_t pe_size; |
|
+ uint32_t pe_total; |
|
+ uint32_t pe_allocated; |
|
+ |
|
+ /* only present on version == 2 pv's */ |
|
+ uint32_t pe_start; |
|
+} __attribute__ ((packed)); |
|
+ |
|
+struct lv_disk { |
|
+ int8_t lv_name[NAME_LEN]; |
|
+ int8_t vg_name[NAME_LEN]; |
|
+ uint32_t lv_access; |
|
+ uint32_t lv_status; |
|
+ uint32_t lv_open; |
|
+ uint32_t lv_dev; |
|
+ uint32_t lv_number; |
|
+ uint32_t lv_mirror_copies; /* for future use */ |
|
+ uint32_t lv_recovery; /* " */ |
|
+ uint32_t lv_schedule; /* " */ |
|
+ uint32_t lv_size; |
|
+ uint32_t lv_snapshot_minor; /* minor number of original */ |
|
+ uint16_t lv_chunk_size; /* chunk size of snapshot */ |
|
+ uint16_t dummy; |
|
+ uint32_t lv_allocated_le; |
|
+ uint32_t lv_stripes; |
|
+ uint32_t lv_stripesize; |
|
+ uint32_t lv_badblock; /* for future use */ |
|
+ uint32_t lv_allocation; |
|
+ uint32_t lv_io_timeout; /* for future use */ |
|
+ uint32_t lv_read_ahead; |
|
+} __attribute__ ((packed)); |
|
+ |
|
+struct vg_disk { |
|
+ int8_t vg_uuid[ID_LEN]; /* volume group UUID */ |
|
+ int8_t vg_name_dummy[NAME_LEN - ID_LEN]; /* rest of v1 VG name */ |
|
+ uint32_t vg_number; /* volume group number */ |
|
+ uint32_t vg_access; /* read/write */ |
|
+ uint32_t vg_status; /* active or not */ |
|
+ uint32_t lv_max; /* maximum logical volumes */ |
|
+ uint32_t lv_cur; /* current logical volumes */ |
|
+ uint32_t lv_open; /* open logical volumes */ |
|
+ uint32_t pv_max; /* maximum physical volumes */ |
|
+ uint32_t pv_cur; /* current physical volumes FU */ |
|
+ uint32_t pv_act; /* active physical volumes */ |
|
+ uint32_t dummy; |
|
+ uint32_t vgda; /* volume group descriptor arrays FU */ |
|
+ uint32_t pe_size; /* physical extent size in sectors */ |
|
+ uint32_t pe_total; /* total of physical extents */ |
|
+ uint32_t pe_allocated; /* allocated physical extents */ |
|
+ uint32_t pvg_total; /* physical volume groups FU */ |
|
+} __attribute__ ((packed)); |
|
+ |
|
+struct pe_disk { |
|
+ uint16_t lv_num; |
|
+ uint16_t le_num; |
|
+} __attribute__ ((packed)); |
|
+ |
|
+struct uuid_list { |
|
+ struct dm_list list; |
|
+ char uuid[NAME_LEN] __attribute__((aligned(8))); |
|
+}; |
|
+ |
|
+struct lvd_list { |
|
+ struct dm_list list; |
|
+ struct lv_disk lvd; |
|
+}; |
|
+ |
|
+struct disk_list { |
|
+ struct dm_list list; |
|
+ struct dm_pool *mem; |
|
+ struct device *dev; |
|
+ |
|
+ struct pv_disk pvd __attribute__((aligned(8))); |
|
+ struct vg_disk vgd __attribute__((aligned(8))); |
|
+ struct dm_list uuids __attribute__((aligned(8))); |
|
+ struct dm_list lvds __attribute__((aligned(8))); |
|
+ struct pe_disk *extents __attribute__((aligned(8))); |
|
+}; |
|
+ |
|
+/* |
|
+ * Layout constants. |
|
+ */ |
|
+#define METADATA_ALIGN 4096UL |
|
+#define LVM1_PE_ALIGN (65536UL >> SECTOR_SHIFT) /* PE alignment */ |
|
+ |
|
+#define METADATA_BASE 0UL |
|
+#define PV_SIZE 1024UL |
|
+#define VG_SIZE 4096UL |
|
+ |
|
+/* |
|
+ * Functions to calculate layout info. |
|
+ */ |
|
+int calculate_layout(struct disk_list *dl); |
|
+int calculate_extent_count(struct physical_volume *pv, uint32_t extent_size, |
|
+ uint32_t max_extent_count, uint64_t pe_start); |
|
+ |
|
+/* |
|
+ * Low level io routines which read/write |
|
+ * disk_lists. |
|
+ */ |
|
+ |
|
+struct disk_list *read_disk(const struct format_type *fmt, struct device *dev, |
|
+ struct dm_pool *mem, const char *vg_name); |
|
+ |
|
+int read_pvs_in_vg(const struct format_type *fmt, const char *vg_name, |
|
+ struct dev_filter *filter, |
|
+ struct dm_pool *mem, struct dm_list *results); |
|
+ |
|
+int write_disks(const struct format_type *fmt, struct dm_list *pvds, |
|
+ int write_vg_metadata); |
|
+ |
|
+/* |
|
+ * Functions to translate to between disk and in |
|
+ * core structures. |
|
+ */ |
|
+int import_pv(const struct format_type *fmt, struct dm_pool *mem, |
|
+ struct device *dev, struct volume_group *vg, |
|
+ struct physical_volume *pv, struct pv_disk *pvd, |
|
+ struct vg_disk *vgd); |
|
+int export_pv(struct cmd_context *cmd, struct dm_pool *mem, |
|
+ struct volume_group *vg, |
|
+ struct pv_disk *pvd, struct physical_volume *pv); |
|
+ |
|
+int import_vg(struct dm_pool *mem, |
|
+ struct volume_group *vg, struct disk_list *dl); |
|
+int export_vg(struct vg_disk *vgd, struct volume_group *vg); |
|
+ |
|
+int import_lv(struct cmd_context *cmd, struct dm_pool *mem, |
|
+ struct logical_volume *lv, struct lv_disk *lvd); |
|
+ |
|
+int import_extents(struct cmd_context *cmd, struct volume_group *vg, |
|
+ struct dm_list *pvds); |
|
+int export_extents(struct disk_list *dl, uint32_t lv_num, |
|
+ struct logical_volume *lv, struct physical_volume *pv); |
|
+ |
|
+int import_pvs(const struct format_type *fmt, struct dm_pool *mem, |
|
+ struct volume_group *vg, struct dm_list *pvds); |
|
+ |
|
+int import_lvs(struct dm_pool *mem, struct volume_group *vg, struct dm_list *pvds); |
|
+int export_lvs(struct disk_list *dl, struct volume_group *vg, |
|
+ struct physical_volume *pv, const char *dev_dir); |
|
+ |
|
+int import_snapshots(struct dm_pool *mem, struct volume_group *vg, |
|
+ struct dm_list *pvds); |
|
+ |
|
+int export_uuids(struct disk_list *dl, struct volume_group *vg); |
|
+ |
|
+void export_numbers(struct dm_list *pvds, struct volume_group *vg); |
|
+ |
|
+void export_pv_act(struct dm_list *pvds); |
|
+int munge_pvd(struct device *dev, struct pv_disk *pvd); |
|
+int read_vgd(struct device *dev, struct vg_disk *vgd, struct pv_disk *pvd); |
|
+ |
|
+/* blech */ |
|
+int get_free_vg_number(struct format_instance *fid, struct dev_filter *filter, |
|
+ const char *candidate_vg, int *result); |
|
+int export_vg_number(struct format_instance *fid, struct dm_list *pvds, |
|
+ const char *vg_name, struct dev_filter *filter); |
|
+ |
|
+int generate_lvm1_system_id(struct cmd_context *cmd, char *s, const char *prefix); |
|
+ |
|
+#endif |
|
diff --git a/lib/format1/format1.c b/lib/format1/format1.c |
|
new file mode 100644 |
|
index 0000000..6e7e888 |
|
--- /dev/null |
|
+++ b/lib/format1/format1.c |
|
@@ -0,0 +1,630 @@ |
|
+/* |
|
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved. |
|
+ * Copyright (C) 2004-2012 Red Hat, Inc. All rights reserved. |
|
+ * |
|
+ * This file is part of LVM2. |
|
+ * |
|
+ * This copyrighted material is made available to anyone wishing to use, |
|
+ * modify, copy, or redistribute it subject to the terms and conditions |
|
+ * of the GNU Lesser General Public License v.2.1. |
|
+ * |
|
+ * You should have received a copy of the GNU Lesser General Public License |
|
+ * along with this program; if not, write to the Free Software Foundation, |
|
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
|
+ */ |
|
+ |
|
+#include "lib.h" |
|
+#include "disk-rep.h" |
|
+#include "limits.h" |
|
+#include "display.h" |
|
+#include "toolcontext.h" |
|
+#include "lvm1-label.h" |
|
+#include "format1.h" |
|
+#include "segtype.h" |
|
+#include "pv_alloc.h" |
|
+ |
|
+/* VG consistency checks */ |
|
+static int _check_vgs(struct dm_list *pvs, struct volume_group *vg) |
|
+{ |
|
+ struct dm_list *pvh, *t; |
|
+ struct disk_list *dl = NULL; |
|
+ struct disk_list *first = NULL; |
|
+ |
|
+ uint32_t pv_count = 0; |
|
+ uint32_t exported = 0; |
|
+ int first_time = 1; |
|
+ |
|
+ /* |
|
+ * If there are exported and unexported PVs, ignore exported ones. |
|
+ * This means an active VG won't be affected if disks are inserted |
|
+ * bearing an exported VG with the same name. |
|
+ */ |
|
+ dm_list_iterate_items(dl, pvs) { |
|
+ if (first_time) { |
|
+ exported = dl->pvd.pv_status & VG_EXPORTED; |
|
+ first_time = 0; |
|
+ continue; |
|
+ } |
|
+ |
|
+ if (exported != (dl->pvd.pv_status & VG_EXPORTED)) { |
|
+ /* Remove exported PVs */ |
|
+ dm_list_iterate_safe(pvh, t, pvs) { |
|
+ dl = dm_list_item(pvh, struct disk_list); |
|
+ if (dl->pvd.pv_status & VG_EXPORTED) |
|
+ dm_list_del(pvh); |
|
+ } |
|
+ break; |
|
+ } |
|
+ } |
|
+ |
|
+ /* Remove any PVs with VG structs that differ from the first */ |
|
+ dm_list_iterate_safe(pvh, t, pvs) { |
|
+ dl = dm_list_item(pvh, struct disk_list); |
|
+ |
|
+ if (!first) |
|
+ first = dl; |
|
+ |
|
+ else if (memcmp(&first->vgd, &dl->vgd, sizeof(first->vgd))) { |
|
+ log_error("VG data differs between PVs %s and %s", |
|
+ dev_name(first->dev), dev_name(dl->dev)); |
|
+ log_debug_metadata("VG data on %s: %s %s %" PRIu32 " %" PRIu32 |
|
+ " %" PRIu32 " %" PRIu32 " %" PRIu32 " %" |
|
+ PRIu32 " %" PRIu32 " %" PRIu32 " %" PRIu32 |
|
+ " %" PRIu32 " %" PRIu32 " %" PRIu32 " %" |
|
+ PRIu32 " %" PRIu32 " %" PRIu32, |
|
+ dev_name(first->dev), first->vgd.vg_uuid, |
|
+ first->vgd.vg_name_dummy, |
|
+ first->vgd.vg_number, first->vgd.vg_access, |
|
+ first->vgd.vg_status, first->vgd.lv_max, |
|
+ first->vgd.lv_cur, first->vgd.lv_open, |
|
+ first->vgd.pv_max, first->vgd.pv_cur, |
|
+ first->vgd.pv_act, first->vgd.dummy, |
|
+ first->vgd.vgda, first->vgd.pe_size, |
|
+ first->vgd.pe_total, first->vgd.pe_allocated, |
|
+ first->vgd.pvg_total); |
|
+ log_debug_metadata("VG data on %s: %s %s %" PRIu32 " %" PRIu32 |
|
+ " %" PRIu32 " %" PRIu32 " %" PRIu32 " %" |
|
+ PRIu32 " %" PRIu32 " %" PRIu32 " %" PRIu32 |
|
+ " %" PRIu32 " %" PRIu32 " %" PRIu32 " %" |
|
+ PRIu32 " %" PRIu32 " %" PRIu32, |
|
+ dev_name(dl->dev), dl->vgd.vg_uuid, |
|
+ dl->vgd.vg_name_dummy, dl->vgd.vg_number, |
|
+ dl->vgd.vg_access, dl->vgd.vg_status, |
|
+ dl->vgd.lv_max, dl->vgd.lv_cur, |
|
+ dl->vgd.lv_open, dl->vgd.pv_max, |
|
+ dl->vgd.pv_cur, dl->vgd.pv_act, dl->vgd.dummy, |
|
+ dl->vgd.vgda, dl->vgd.pe_size, |
|
+ dl->vgd.pe_total, dl->vgd.pe_allocated, |
|
+ dl->vgd.pvg_total); |
|
+ dm_list_del(pvh); |
|
+ return 0; |
|
+ } |
|
+ pv_count++; |
|
+ } |
|
+ |
|
+ /* On entry to fn, list known to be non-empty */ |
|
+ if (pv_count != first->vgd.pv_cur) { |
|
+ log_error("%d PV(s) found for VG %s: expected %d", |
|
+ pv_count, first->pvd.vg_name, first->vgd.pv_cur); |
|
+ vg->status |= PARTIAL_VG; |
|
+ } |
|
+ |
|
+ return 1; |
|
+} |
|
+ |
|
+static int _fix_partial_vg(struct volume_group *vg, struct dm_list *pvs) |
|
+{ |
|
+ uint32_t extent_count = 0; |
|
+ struct disk_list *dl; |
|
+ struct dm_list *pvh; |
|
+ struct pv_list *pvl; |
|
+ struct lv_list *ll; |
|
+ struct lv_segment *seg; |
|
+ |
|
+ /* |
|
+ * FIXME: code should remap missing segments to error segment. |
|
+ * Also current mapping code allocates 1 segment per missing extent. |
|
+ * For now bail out completely - allocated structures are not complete |
|
+ */ |
|
+ dm_list_iterate_items(ll, &vg->lvs) |
|
+ dm_list_iterate_items(seg, &ll->lv->segments) { |
|
+ |
|
+ /* area_count is always 1 here, s == 0 */ |
|
+ if (seg_type(seg, 0) != AREA_PV) |
|
+ continue; |
|
+ |
|
+ if (seg_pv(seg, 0)) |
|
+ continue; |
|
+ |
|
+ log_error("Partial mode support for missing lvm1 PVs and " |
|
+ "partially available LVs is currently not implemented."); |
|
+ return 0; |
|
+ } |
|
+ |
|
+ dm_list_iterate(pvh, pvs) { |
|
+ dl = dm_list_item(pvh, struct disk_list); |
|
+ extent_count += dl->pvd.pe_total; |
|
+ } |
|
+ |
|
+ /* FIXME: move this to one place to pv_manip */ |
|
+ if (!(pvl = dm_pool_zalloc(vg->vgmem, sizeof(*pvl))) || |
|
+ !(pvl->pv = dm_pool_zalloc(vg->vgmem, sizeof(*pvl->pv)))) |
|
+ return_0; |
|
+ |
|
+ /* Use vg uuid with replaced first chars to "missing" as missing PV UUID */ |
|
+ memcpy(&pvl->pv->id.uuid, vg->id.uuid, sizeof(pvl->pv->id.uuid)); |
|
+ memcpy(&pvl->pv->id.uuid, "missing", 7); |
|
+ |
|
+ if (!(pvl->pv->vg_name = dm_pool_strdup(vg->vgmem, vg->name))) |
|
+ goto_out; |
|
+ memcpy(&pvl->pv->vgid, &vg->id, sizeof(vg->id)); |
|
+ pvl->pv->status |= MISSING_PV; |
|
+ dm_list_init(&pvl->pv->tags); |
|
+ dm_list_init(&pvl->pv->segments); |
|
+ |
|
+ pvl->pv->pe_size = vg->extent_size; |
|
+ pvl->pv->pe_count = vg->extent_count - extent_count; |
|
+ if (!alloc_pv_segment_whole_pv(vg->vgmem, pvl->pv)) |
|
+ goto_out; |
|
+ |
|
+ add_pvl_to_vgs(vg, pvl); |
|
+ log_debug_metadata("%s: partial VG, allocated missing PV using %d extents.", |
|
+ vg->name, pvl->pv->pe_count); |
|
+ |
|
+ return 1; |
|
+out: |
|
+ dm_pool_free(vg->vgmem, pvl); |
|
+ return 0; |
|
+} |
|
+ |
|
+static struct volume_group *_format1_vg_read(struct format_instance *fid, |
|
+ const char *vg_name, |
|
+ struct metadata_area *mda __attribute__((unused)), |
|
+ struct cached_vg_fmtdata **vg_fmtdata __attribute__((unused)), |
|
+ unsigned *use_previous_vg __attribute__((unused))) |
|
+{ |
|
+ struct volume_group *vg; |
|
+ struct disk_list *dl; |
|
+ DM_LIST_INIT(pvs); |
|
+ |
|
+ /* Strip dev_dir if present */ |
|
+ if (vg_name) |
|
+ vg_name = strip_dir(vg_name, fid->fmt->cmd->dev_dir); |
|
+ |
|
+ if (!(vg = alloc_vg("format1_vg_read", fid->fmt->cmd, NULL))) |
|
+ return_NULL; |
|
+ |
|
+ if (!read_pvs_in_vg(fid->fmt, vg_name, fid->fmt->cmd->filter, |
|
+ vg->vgmem, &pvs)) |
|
+ goto_bad; |
|
+ |
|
+ if (dm_list_empty(&pvs)) |
|
+ goto_bad; |
|
+ |
|
+ if (!_check_vgs(&pvs, vg)) |
|
+ goto_bad; |
|
+ |
|
+ dl = dm_list_item(pvs.n, struct disk_list); |
|
+ |
|
+ if (!import_vg(vg->vgmem, vg, dl)) |
|
+ goto_bad; |
|
+ |
|
+ if (!import_pvs(fid->fmt, vg->vgmem, vg, &pvs)) |
|
+ goto_bad; |
|
+ |
|
+ if (!import_lvs(vg->vgmem, vg, &pvs)) |
|
+ goto_bad; |
|
+ |
|
+ if (!import_extents(fid->fmt->cmd, vg, &pvs)) |
|
+ goto_bad; |
|
+ |
|
+ /* FIXME: workaround - temporary assignment of fid */ |
|
+ vg->fid = fid; |
|
+ if (!import_snapshots(vg->vgmem, vg, &pvs)) { |
|
+ vg->fid = NULL; |
|
+ goto_bad; |
|
+ } |
|
+ vg->fid = NULL; |
|
+ |
|
+ /* Fix extents counts by adding missing PV if partial VG */ |
|
+ if ((vg->status & PARTIAL_VG) && !_fix_partial_vg(vg, &pvs)) |
|
+ goto_bad; |
|
+ |
|
+ vg_set_fid(vg, fid); |
|
+ |
|
+ return vg; |
|
+ |
|
+bad: |
|
+ release_vg(vg); |
|
+ |
|
+ return NULL; |
|
+} |
|
+ |
|
+static struct disk_list *_flatten_pv(struct format_instance *fid, |
|
+ struct dm_pool *mem, struct volume_group *vg, |
|
+ struct physical_volume *pv, |
|
+ const char *dev_dir) |
|
+{ |
|
+ struct disk_list *dl = dm_pool_alloc(mem, sizeof(*dl)); |
|
+ |
|
+ if (!dl) |
|
+ return_NULL; |
|
+ |
|
+ dl->mem = mem; |
|
+ dl->dev = pv->dev; |
|
+ |
|
+ dm_list_init(&dl->uuids); |
|
+ dm_list_init(&dl->lvds); |
|
+ |
|
+ if (!export_pv(fid->fmt->cmd, mem, vg, &dl->pvd, pv) || |
|
+ !export_vg(&dl->vgd, vg) || |
|
+ !export_uuids(dl, vg) || |
|
+ !export_lvs(dl, vg, pv, dev_dir) || !calculate_layout(dl)) { |
|
+ dm_pool_free(mem, dl); |
|
+ return_NULL; |
|
+ } |
|
+ |
|
+ return dl; |
|
+} |
|
+ |
|
+static int _flatten_vg(struct format_instance *fid, struct dm_pool *mem, |
|
+ struct volume_group *vg, |
|
+ struct dm_list *pvds, const char *dev_dir, |
|
+ struct dev_filter *filter) |
|
+{ |
|
+ struct pv_list *pvl; |
|
+ struct disk_list *data; |
|
+ |
|
+ dm_list_iterate_items(pvl, &vg->pvs) { |
|
+ if (!(data = _flatten_pv(fid, mem, vg, pvl->pv, dev_dir))) |
|
+ return_0; |
|
+ |
|
+ dm_list_add(pvds, &data->list); |
|
+ } |
|
+ |
|
+ export_numbers(pvds, vg); |
|
+ export_pv_act(pvds); |
|
+ |
|
+ if (!export_vg_number(fid, pvds, vg->name, filter)) |
|
+ return_0; |
|
+ |
|
+ return 1; |
|
+} |
|
+ |
|
+static int _format1_vg_write(struct format_instance *fid, struct volume_group *vg, |
|
+ struct metadata_area *mda __attribute__((unused))) |
|
+{ |
|
+ struct dm_pool *mem = dm_pool_create("lvm1 vg_write", VG_MEMPOOL_CHUNK); |
|
+ struct dm_list pvds; |
|
+ int r = 0; |
|
+ |
|
+ if (!mem) |
|
+ return_0; |
|
+ |
|
+ dm_list_init(&pvds); |
|
+ |
|
+ r = (_flatten_vg(fid, mem, vg, &pvds, fid->fmt->cmd->dev_dir, |
|
+ fid->fmt->cmd->filter) && |
|
+ write_disks(fid->fmt, &pvds, 1)); |
|
+ |
|
+ lvmcache_update_vg(vg, 0); |
|
+ dm_pool_destroy(mem); |
|
+ return r; |
|
+} |
|
+ |
|
+static int _format1_pv_read(const struct format_type *fmt, const char *pv_name, |
|
+ struct physical_volume *pv, int scan_label_only __attribute__((unused))) |
|
+{ |
|
+ struct dm_pool *mem = dm_pool_create("lvm1 pv_read", 1024); |
|
+ struct disk_list *dl; |
|
+ struct device *dev; |
|
+ int r = 0; |
|
+ |
|
+ log_very_verbose("Reading physical volume data %s from disk", pv_name); |
|
+ |
|
+ if (!mem) |
|
+ return_0; |
|
+ |
|
+ if (!(dev = dev_cache_get(pv_name, fmt->cmd->filter))) |
|
+ goto_out; |
|
+ |
|
+ if (!(dl = read_disk(fmt, dev, mem, NULL))) |
|
+ goto_out; |
|
+ |
|
+ if (!import_pv(fmt, fmt->cmd->mem, dl->dev, NULL, pv, &dl->pvd, &dl->vgd)) |
|
+ goto_out; |
|
+ |
|
+ pv->fmt = fmt; |
|
+ |
|
+ r = 1; |
|
+ |
|
+ out: |
|
+ dm_pool_destroy(mem); |
|
+ return r; |
|
+} |
|
+ |
|
+static int _format1_pv_initialise(const struct format_type * fmt, |
|
+ struct pv_create_args *pva, |
|
+ struct physical_volume * pv) |
|
+{ |
|
+ if (pv->size > MAX_PV_SIZE) |
|
+ pv->size--; |
|
+ if (pv->size > MAX_PV_SIZE) { |
|
+ log_error("Physical volumes cannot be bigger than %s", |
|
+ display_size(fmt->cmd, (uint64_t) MAX_PV_SIZE)); |
|
+ return 0; |
|
+ } |
|
+ |
|
+ /* Nothing more to do if extent size isn't provided */ |
|
+ if (!pva->extent_size) |
|
+ return 1; |
|
+ |
|
+ /* |
|
+ * This works out pe_start and pe_count. |
|
+ */ |
|
+ if (!calculate_extent_count(pv, pva->extent_size, pva->extent_count, pva->pe_start)) |
|
+ return_0; |
|
+ |
|
+ /* Retain existing extent locations exactly */ |
|
+ if (((pva->pe_start || pva->extent_count) && (pva->pe_start != pv->pe_start)) || |
|
+ (pva->extent_count && (pva->extent_count != pv->pe_count))) { |
|
+ log_error("Metadata would overwrite physical extents"); |
|
+ return 0; |
|
+ } |
|
+ |
|
+ return 1; |
|
+} |
|
+ |
|
+static int _format1_pv_setup(const struct format_type *fmt, |
|
+ struct physical_volume *pv, |
|
+ struct volume_group *vg) |
|
+{ |
|
+ struct pv_create_args pva = { .id = {{0}}, |
|
+ .idp = NULL, |
|
+ .ba_start = 0, |
|
+ .ba_size = 0, |
|
+ .pe_start = 0, |
|
+ .extent_count = 0, |
|
+ .extent_size = vg->extent_size}; |
|
+ |
|
+ return _format1_pv_initialise(fmt, &pva, pv); |
|
+} |
|
+ |
|
+static int _format1_lv_setup(struct format_instance *fid, struct logical_volume *lv) |
|
+{ |
|
+ uint64_t max_size = UINT_MAX; |
|
+ |
|
+ if (!*lv->lvid.s) |
|
+ lvid_from_lvnum(&lv->lvid, &lv->vg->id, find_free_lvnum(lv)); |
|
+ |
|
+ if (lv->le_count > MAX_LE_TOTAL) { |
|
+ log_error("logical volumes cannot contain more than " |
|
+ "%d extents.", MAX_LE_TOTAL); |
|
+ return 0; |
|
+ } |
|
+ if (lv->size > max_size) { |
|
+ log_error("logical volumes cannot be larger than %s", |
|
+ display_size(fid->fmt->cmd, max_size)); |
|
+ return 0; |
|
+ } |
|
+ |
|
+ return 1; |
|
+} |
|
+ |
|
+static int _format1_pv_write(const struct format_type *fmt, struct physical_volume *pv) |
|
+{ |
|
+ struct dm_pool *mem; |
|
+ struct disk_list *dl; |
|
+ struct dm_list pvs; |
|
+ struct lvmcache_info *info; |
|
+ int pe_count, pe_size, pe_start; |
|
+ int r = 1; |
|
+ |
|
+ if (!(info = lvmcache_add(fmt->labeller, (char *) &pv->id, pv->dev, |
|
+ pv->vg_name, NULL, 0))) |
|
+ return_0; |
|
+ |
|
+ lvmcache_update_pv(info, pv, fmt); |
|
+ lvmcache_del_mdas(info); |
|
+ lvmcache_del_das(info); |
|
+ lvmcache_del_bas(info); |
|
+ |
|
+ dm_list_init(&pvs); |
|
+ |
|
+ pe_count = pv->pe_count; |
|
+ pe_size = pv->pe_size; |
|
+ pe_start = pv->pe_start; |
|
+ |
|
+ /* Ensure any residual PE structure is gone */ |
|
+ pv->pe_size = pv->pe_count = 0; |
|
+ pv->pe_start = LVM1_PE_ALIGN; |
|
+ |
|
+ if (!(mem = dm_pool_create("lvm1 pv_write", 1024))) |
|
+ return_0; |
|
+ |
|
+ if (!(dl = dm_pool_alloc(mem, sizeof(*dl)))) |
|
+ goto_bad; |
|
+ |
|
+ dl->mem = mem; |
|
+ dl->dev = pv->dev; |
|
+ dm_list_init(&dl->uuids); |
|
+ dm_list_init(&dl->lvds); |
|
+ |
|
+ if (!export_pv(fmt->cmd, mem, NULL, &dl->pvd, pv)) |
|
+ goto_bad; |
|
+ |
|
+ /* must be set to be able to zero gap after PV structure in |
|
+ dev_write in order to make other disk tools happy */ |
|
+ dl->pvd.pv_on_disk.base = METADATA_BASE; |
|
+ dl->pvd.pv_on_disk.size = PV_SIZE; |
|
+ dl->pvd.pe_on_disk.base = LVM1_PE_ALIGN << SECTOR_SHIFT; |
|
+ |
|
+ dm_list_add(&pvs, &dl->list); |
|
+ if (!write_disks(fmt, &pvs, 0)) |
|
+ goto_bad; |
|
+ |
|
+ goto out; |
|
+ |
|
+ bad: |
|
+ r = 0; |
|
+ |
|
+ out: |
|
+ pv->pe_size = pe_size; |
|
+ pv->pe_count = pe_count; |
|
+ pv->pe_start = pe_start; |
|
+ |
|
+ dm_pool_destroy(mem); |
|
+ return r; |
|
+} |
|
+ |
|
+static int _format1_vg_setup(struct format_instance *fid, struct volume_group *vg) |
|
+{ |
|
+ /* just check max_pv and max_lv */ |
|
+ if (!vg->max_lv || vg->max_lv >= MAX_LV) |
|
+ vg->max_lv = MAX_LV - 1; |
|
+ |
|
+ if (!vg->max_pv || vg->max_pv >= MAX_PV) |
|
+ vg->max_pv = MAX_PV - 1; |
|
+ |
|
+ if (!vg_check_new_extent_size(vg->fid->fmt, vg->extent_size)) |
|
+ return_0; |
|
+ |
|
+ /* Generate lvm1_system_id if not yet set */ |
|
+ if (!*vg->lvm1_system_id && |
|
+ !generate_lvm1_system_id(vg->cmd, vg->lvm1_system_id, "")) |
|
+ return_0; |
|
+ |
|
+ return 1; |
|
+} |
|
+ |
|
+static int _format1_segtype_supported(struct format_instance *fid __attribute__((unused)), |
|
+ const struct segment_type *segtype) |
|
+{ |
|
+ if (!(segtype->flags & SEG_FORMAT1_SUPPORT)) |
|
+ return_0; |
|
+ |
|
+ return 1; |
|
+} |
|
+ |
|
+static struct metadata_area_ops _metadata_format1_ops = { |
|
+ .vg_read = _format1_vg_read, |
|
+ .vg_write = _format1_vg_write, |
|
+}; |
|
+ |
|
+static struct format_instance *_format1_create_instance(const struct format_type *fmt, |
|
+ const struct format_instance_ctx *fic) |
|
+{ |
|
+ struct format_instance *fid; |
|
+ struct metadata_area *mda; |
|
+ |
|
+ if (!(fid = alloc_fid(fmt, fic))) |
|
+ return_NULL; |
|
+ |
|
+ /* Define a NULL metadata area */ |
|
+ if (!(mda = dm_pool_zalloc(fid->mem, sizeof(*mda)))) { |
|
+ log_error("Unable to allocate metadata area structure " |
|
+ "for lvm1 format"); |
|
+ goto bad; |
|
+ } |
|
+ |
|
+ mda->ops = &_metadata_format1_ops; |
|
+ mda->metadata_locn = NULL; |
|
+ mda->status = 0; |
|
+ dm_list_add(&fid->metadata_areas_in_use, &mda->list); |
|
+ |
|
+ return fid; |
|
+ |
|
+bad: |
|
+ dm_pool_destroy(fid->mem); |
|
+ return NULL; |
|
+} |
|
+ |
|
+static void _format1_destroy_instance(struct format_instance *fid) |
|
+{ |
|
+ if (--fid->ref_count <= 1) |
|
+ dm_pool_destroy(fid->mem); |
|
+} |
|
+ |
|
+static void _format1_destroy(struct format_type *fmt) |
|
+{ |
|
+ if (fmt->orphan_vg) |
|
+ free_orphan_vg(fmt->orphan_vg); |
|
+ |
|
+ dm_free(fmt); |
|
+} |
|
+ |
|
+static struct format_handler _format1_ops = { |
|
+ .pv_read = _format1_pv_read, |
|
+ .pv_initialise = _format1_pv_initialise, |
|
+ .pv_setup = _format1_pv_setup, |
|
+ .pv_write = _format1_pv_write, |
|
+ .lv_setup = _format1_lv_setup, |
|
+ .vg_setup = _format1_vg_setup, |
|
+ .segtype_supported = _format1_segtype_supported, |
|
+ .create_instance = _format1_create_instance, |
|
+ .destroy_instance = _format1_destroy_instance, |
|
+ .destroy = _format1_destroy, |
|
+}; |
|
+ |
|
+#ifdef LVM1_INTERNAL |
|
+struct format_type *init_lvm1_format(struct cmd_context *cmd) |
|
+#else /* Shared */ |
|
+struct format_type *init_format(struct cmd_context *cmd); |
|
+struct format_type *init_format(struct cmd_context *cmd) |
|
+#endif |
|
+{ |
|
+ struct format_type *fmt = dm_malloc(sizeof(*fmt)); |
|
+ struct format_instance_ctx fic; |
|
+ struct format_instance *fid; |
|
+ |
|
+ if (!fmt) { |
|
+ log_error("Failed to allocate format1 format type structure."); |
|
+ return NULL; |
|
+ } |
|
+ |
|
+ fmt->cmd = cmd; |
|
+ fmt->ops = &_format1_ops; |
|
+ fmt->name = FMT_LVM1_NAME; |
|
+ fmt->alias = NULL; |
|
+ fmt->orphan_vg_name = FMT_LVM1_ORPHAN_VG_NAME; |
|
+ fmt->features = FMT_RESTRICTED_LVIDS | FMT_ORPHAN_ALLOCATABLE | |
|
+ FMT_RESTRICTED_READAHEAD | FMT_OBSOLETE | |
|
+ FMT_SYSTEMID_ON_PVS; |
|
+ fmt->private = NULL; |
|
+ |
|
+ dm_list_init(&fmt->mda_ops); |
|
+ |
|
+ if (!(fmt->labeller = lvm1_labeller_create(fmt))) { |
|
+ log_error("Couldn't create lvm1 label handler."); |
|
+ dm_free(fmt); |
|
+ return NULL; |
|
+ } |
|
+ |
|
+ if (!(label_register_handler(fmt->labeller))) { |
|
+ log_error("Couldn't register lvm1 label handler."); |
|
+ fmt->labeller->ops->destroy(fmt->labeller); |
|
+ dm_free(fmt); |
|
+ return NULL; |
|
+ } |
|
+ |
|
+ if (!(fmt->orphan_vg = alloc_vg("format1_orphan", cmd, fmt->orphan_vg_name))) { |
|
+ log_error("Couldn't create lvm1 orphan VG."); |
|
+ dm_free(fmt); |
|
+ return NULL; |
|
+ } |
|
+ |
|
+ fic.type = FMT_INSTANCE_AUX_MDAS; |
|
+ fic.context.vg_ref.vg_name = fmt->orphan_vg_name; |
|
+ fic.context.vg_ref.vg_id = NULL; |
|
+ |
|
+ if (!(fid = _format1_create_instance(fmt, &fic))) { |
|
+ _format1_destroy(fmt); |
|
+ return_NULL; |
|
+ } |
|
+ |
|
+ vg_set_fid(fmt->orphan_vg, fid); |
|
+ |
|
+ log_very_verbose("Initialised format: %s", fmt->name); |
|
+ |
|
+ return fmt; |
|
+} |
|
diff --git a/lib/format1/format1.h b/lib/format1/format1.h |
|
new file mode 100644 |
|
index 0000000..42ddc66 |
|
--- /dev/null |
|
+++ b/lib/format1/format1.h |
|
@@ -0,0 +1,29 @@ |
|
+/* |
|
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved. |
|
+ * Copyright (C) 2004 Red Hat, Inc. All rights reserved. |
|
+ * |
|
+ * This file is part of LVM2. |
|
+ * |
|
+ * This copyrighted material is made available to anyone wishing to use, |
|
+ * modify, copy, or redistribute it subject to the terms and conditions |
|
+ * of the GNU Lesser General Public License v.2.1. |
|
+ * |
|
+ * You should have received a copy of the GNU Lesser General Public License |
|
+ * along with this program; if not, write to the Free Software Foundation, |
|
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
|
+ */ |
|
+ |
|
+#ifndef _LVM_FORMAT1_H |
|
+#define _LVM_FORMAT1_H |
|
+ |
|
+#include "metadata.h" |
|
+#include "lvmcache.h" |
|
+ |
|
+#define FMT_LVM1_NAME "lvm1" |
|
+#define FMT_LVM1_ORPHAN_VG_NAME ORPHAN_VG_NAME(FMT_LVM1_NAME) |
|
+ |
|
+#ifdef LVM1_INTERNAL |
|
+struct format_type *init_lvm1_format(struct cmd_context *cmd); |
|
+#endif |
|
+ |
|
+#endif |
|
diff --git a/lib/format1/import-export.c b/lib/format1/import-export.c |
|
new file mode 100644 |
|
index 0000000..c29527b |
|
--- /dev/null |
|
+++ b/lib/format1/import-export.c |
|
@@ -0,0 +1,680 @@ |
|
+/* |
|
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved. |
|
+ * Copyright (C) 2004-2006 Red Hat, Inc. All rights reserved. |
|
+ * |
|
+ * This file is part of LVM2. |
|
+ * |
|
+ * This copyrighted material is made available to anyone wishing to use, |
|
+ * modify, copy, or redistribute it subject to the terms and conditions |
|
+ * of the GNU Lesser General Public License v.2.1. |
|
+ * |
|
+ * You should have received a copy of the GNU Lesser General Public License |
|
+ * along with this program; if not, write to the Free Software Foundation, |
|
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
|
+ */ |
|
+ |
|
+/* |
|
+ * Translates between disk and in-core formats. |
|
+ */ |
|
+ |
|
+#include "lib.h" |
|
+#include "disk-rep.h" |
|
+#include "lvm-string.h" |
|
+#include "toolcontext.h" |
|
+#include "segtype.h" |
|
+#include "pv_alloc.h" |
|
+#include "display.h" |
|
+#include "metadata.h" |
|
+ |
|
+#include <time.h> |
|
+ |
|
+static int _check_vg_name(const char *name) |
|
+{ |
|
+ return strlen(name) < NAME_LEN; |
|
+} |
|
+ |
|
+/* |
|
+ * Extracts the last part of a path. |
|
+ */ |
|
+static char *_create_lv_name(struct dm_pool *mem, const char *full_name) |
|
+{ |
|
+ const char *ptr = strrchr(full_name, '/'); |
|
+ |
|
+ if (!ptr) |
|
+ ptr = full_name; |
|
+ else |
|
+ ptr++; |
|
+ |
|
+ return dm_pool_strdup(mem, ptr); |
|
+} |
|
+ |
|
+int import_pv(const struct format_type *fmt, struct dm_pool *mem, |
|
+ struct device *dev, struct volume_group *vg, |
|
+ struct physical_volume *pv, struct pv_disk *pvd, |
|
+ struct vg_disk *vgd) |
|
+{ |
|
+ uint64_t size; |
|
+ |
|
+ memset(pv, 0, sizeof(*pv)); |
|
+ memcpy(&pv->id, pvd->pv_uuid, ID_LEN); |
|
+ |
|
+ pv->dev = dev; |
|
+ if (!*pvd->vg_name) |
|
+ pv->vg_name = fmt->orphan_vg_name; |
|
+ else if (!(pv->vg_name = dm_pool_strdup(mem, (char *)pvd->vg_name))) { |
|
+ log_error("Volume Group name allocation failed."); |
|
+ return 0; |
|
+ } |
|
+ |
|
+ memcpy(&pv->vgid, vgd->vg_uuid, sizeof(vg->id)); |
|
+ |
|
+ /* Store system_id from first PV if PV belongs to a VG */ |
|
+ if (vg && !*vg->lvm1_system_id) |
|
+ strncpy(vg->lvm1_system_id, (char *)pvd->system_id, NAME_LEN); |
|
+ |
|
+ if (vg && |
|
+ strncmp(vg->lvm1_system_id, (char *)pvd->system_id, sizeof(pvd->system_id))) |
|
+ log_very_verbose("System ID %s on %s differs from %s for " |
|
+ "volume group", pvd->system_id, |
|
+ pv_dev_name(pv), vg->lvm1_system_id); |
|
+ |
|
+ /* |
|
+ * If exported, we still need to flag in pv->status too because |
|
+ * we don't always have a struct volume_group when we need this. |
|
+ */ |
|
+ if (pvd->pv_status & VG_EXPORTED) |
|
+ pv->status |= EXPORTED_VG; |
|
+ |
|
+ if (pvd->pv_allocatable) |
|
+ pv->status |= ALLOCATABLE_PV; |
|
+ |
|
+ pv->size = pvd->pv_size; |
|
+ pv->pe_size = pvd->pe_size; |
|
+ pv->pe_start = pvd->pe_start; |
|
+ pv->pe_count = pvd->pe_total; |
|
+ pv->pe_alloc_count = 0; |
|
+ pv->pe_align = 0; |
|
+ pv->is_labelled = 0; /* format1 PVs have no label */ |
|
+ pv->label_sector = 0; |
|
+ |
|
+ /* Fix up pv size if missing or impossibly large */ |
|
+ if (!pv->size || pv->size > (1ULL << 62)) { |
|
+ if (!dev_get_size(dev, &pv->size)) { |
|
+ log_error("%s: Couldn't get size.", pv_dev_name(pv)); |
|
+ return 0; |
|
+ } |
|
+ log_verbose("Fixing up missing format1 size (%s) " |
|
+ "for PV %s", display_size(fmt->cmd, pv->size), |
|
+ pv_dev_name(pv)); |
|
+ if (vg) { |
|
+ size = pv->pe_count * (uint64_t) vg->extent_size + |
|
+ pv->pe_start; |
|
+ if (size > pv->size) |
|
+ log_warn("WARNING: Physical Volume %s is too " |
|
+ "large for underlying device", |
|
+ pv_dev_name(pv)); |
|
+ } |
|
+ } |
|
+ |
|
+ dm_list_init(&pv->tags); |
|
+ dm_list_init(&pv->segments); |
|
+ |
|
+ if (!alloc_pv_segment_whole_pv(mem, pv)) |
|
+ return_0; |
|
+ |
|
+ return 1; |
|
+} |
|
+ |
|
+int generate_lvm1_system_id(struct cmd_context *cmd, char *s, const char *prefix) |
|
+{ |
|
+ |
|
+ if (dm_snprintf(s, NAME_LEN, "%s%s" FMTu64, |
|
+ prefix, cmd->hostname, (uint64_t)time(NULL)) < 0) { |
|
+ log_error("Generated LVM1 format system_id too long"); |
|
+ return 0; |
|
+ } |
|
+ |
|
+ return 1; |
|
+} |
|
+ |
|
+int export_pv(struct cmd_context *cmd, struct dm_pool *mem __attribute__((unused)), |
|
+ struct volume_group *vg, |
|
+ struct pv_disk *pvd, struct physical_volume *pv) |
|
+{ |
|
+ memset(pvd, 0, sizeof(*pvd)); |
|
+ |
|
+ pvd->id[0] = 'H'; |
|
+ pvd->id[1] = 'M'; |
|
+ pvd->version = 1; |
|
+ |
|
+ memcpy(pvd->pv_uuid, pv->id.uuid, ID_LEN); |
|
+ |
|
+ if (pv->vg_name && !is_orphan(pv) && !(pv->status & UNLABELLED_PV)) { |
|
+ if (!_check_vg_name(pv->vg_name)) |
|
+ return_0; |
|
+ strncpy((char *)pvd->vg_name, pv->vg_name, sizeof(pvd->vg_name)); |
|
+ } |
|
+ |
|
+ /* Preserve existing system_id if it exists */ |
|
+ if (vg && vg->lvm1_system_id && *vg->lvm1_system_id) |
|
+ strncpy((char *)pvd->system_id, vg->lvm1_system_id, sizeof(pvd->system_id)); |
|
+ else if (vg && vg->system_id && *vg->system_id) |
|
+ strncpy((char *)pvd->system_id, vg->system_id, sizeof(pvd->system_id)); |
|
+ |
|
+ /* Is VG already exported or being exported? */ |
|
+ if (vg && vg_is_exported(vg)) { |
|
+ /* Does system_id need setting? */ |
|
+ if (!vg->lvm1_system_id || !*vg->lvm1_system_id || |
|
+ strncmp(vg->lvm1_system_id, EXPORTED_TAG, |
|
+ sizeof(EXPORTED_TAG) - 1)) { |
|
+ if (!generate_lvm1_system_id(cmd, (char *)pvd->system_id, EXPORTED_TAG)) |
|
+ return_0; |
|
+ } |
|
+ if (strlen((char *)pvd->vg_name) + sizeof(EXPORTED_TAG) > |
|
+ sizeof(pvd->vg_name)) { |
|
+ log_error("Volume group name %s too long to export", |
|
+ pvd->vg_name); |
|
+ return 0; |
|
+ } |
|
+ strcat((char *)pvd->vg_name, EXPORTED_TAG); |
|
+ } |
|
+ |
|
+ /* Is VG being imported? */ |
|
+ if (vg && !vg_is_exported(vg) && vg->lvm1_system_id && *vg->lvm1_system_id && |
|
+ !strncmp(vg->lvm1_system_id, EXPORTED_TAG, sizeof(EXPORTED_TAG) - 1)) { |
|
+ if (!generate_lvm1_system_id(cmd, (char *)pvd->system_id, IMPORTED_TAG)) |
|
+ return_0; |
|
+ } |
|
+ |
|
+ /* Generate system_id if PV is in VG */ |
|
+ if (!pvd->system_id[0]) |
|
+ if (!generate_lvm1_system_id(cmd, (char *)pvd->system_id, "")) |
|
+ return_0; |
|
+ |
|
+ /* Update internal system_id if we changed it */ |
|
+ if (vg && vg->lvm1_system_id && |
|
+ (!*vg->lvm1_system_id || |
|
+ strncmp(vg->lvm1_system_id, (char *)pvd->system_id, sizeof(pvd->system_id)))) |
|
+ strncpy(vg->lvm1_system_id, (char *)pvd->system_id, NAME_LEN); |
|
+ |
|
+ //pvd->pv_major = MAJOR(pv->dev); |
|
+ |
|
+ if (pv->status & ALLOCATABLE_PV) |
|
+ pvd->pv_allocatable = PV_ALLOCATABLE; |
|
+ |
|
+ pvd->pv_size = pv->size; |
|
+ pvd->lv_cur = 0; /* this is set when exporting the lv list */ |
|
+ if (vg) |
|
+ pvd->pe_size = vg->extent_size; |
|
+ else |
|
+ pvd->pe_size = pv->pe_size; |
|
+ pvd->pe_total = pv->pe_count; |
|
+ pvd->pe_allocated = pv->pe_alloc_count; |
|
+ pvd->pe_start = pv->pe_start; |
|
+ |
|
+ return 1; |
|
+} |
|
+ |
|
+int import_vg(struct dm_pool *mem, |
|
+ struct volume_group *vg, struct disk_list *dl) |
|
+{ |
|
+ struct vg_disk *vgd = &dl->vgd; |
|
+ memcpy(vg->id.uuid, vgd->vg_uuid, ID_LEN); |
|
+ |
|
+ if (!_check_vg_name((char *)dl->pvd.vg_name)) |
|
+ return_0; |
|
+ |
|
+ if (!(vg->name = dm_pool_strdup(mem, (char *)dl->pvd.vg_name))) |
|
+ return_0; |
|
+ |
|
+ if (!(vg->lvm1_system_id = dm_pool_zalloc(mem, NAME_LEN + 1))) |
|
+ return_0; |
|
+ |
|
+ if (vgd->vg_status & VG_EXPORTED) |
|
+ vg->status |= EXPORTED_VG; |
|
+ |
|
+ if (vgd->vg_status & VG_EXTENDABLE) |
|
+ vg->status |= RESIZEABLE_VG; |
|
+ |
|
+ if (vgd->vg_access & VG_READ) |
|
+ vg->status |= LVM_READ; |
|
+ |
|
+ if (vgd->vg_access & VG_WRITE) |
|
+ vg->status |= LVM_WRITE; |
|
+ |
|
+ if (vgd->vg_access & VG_CLUSTERED) |
|
+ vg->status |= CLUSTERED; |
|
+ |
|
+ if (vgd->vg_access & VG_SHARED) |
|
+ vg->status |= SHARED; |
|
+ |
|
+ vg->extent_size = vgd->pe_size; |
|
+ vg->extent_count = vgd->pe_total; |
|
+ vg->free_count = vgd->pe_total; |
|
+ vg->max_lv = vgd->lv_max; |
|
+ vg->max_pv = vgd->pv_max; |
|
+ vg->alloc = ALLOC_NORMAL; |
|
+ |
|
+ return 1; |
|
+} |
|
+ |
|
+int export_vg(struct vg_disk *vgd, struct volume_group *vg) |
|
+{ |
|
+ memset(vgd, 0, sizeof(*vgd)); |
|
+ memcpy(vgd->vg_uuid, vg->id.uuid, ID_LEN); |
|
+ |
|
+ if (vg->status & LVM_READ) |
|
+ vgd->vg_access |= VG_READ; |
|
+ |
|
+ if (vg->status & LVM_WRITE) |
|
+ vgd->vg_access |= VG_WRITE; |
|
+ |
|
+ if (vg_is_clustered(vg)) |
|
+ vgd->vg_access |= VG_CLUSTERED; |
|
+ |
|
+ if (vg->status & SHARED) |
|
+ vgd->vg_access |= VG_SHARED; |
|
+ |
|
+ if (vg_is_exported(vg)) |
|
+ vgd->vg_status |= VG_EXPORTED; |
|
+ |
|
+ if (vg_is_resizeable(vg)) |
|
+ vgd->vg_status |= VG_EXTENDABLE; |
|
+ |
|
+ vgd->lv_max = vg->max_lv; |
|
+ vgd->lv_cur = vg_visible_lvs(vg) + snapshot_count(vg); |
|
+ |
|
+ vgd->pv_max = vg->max_pv; |
|
+ vgd->pv_cur = vg->pv_count; |
|
+ |
|
+ vgd->pe_size = vg->extent_size; |
|
+ vgd->pe_total = vg->extent_count; |
|
+ vgd->pe_allocated = vg->extent_count - vg->free_count; |
|
+ |
|
+ return 1; |
|
+} |
|
+ |
|
+int import_lv(struct cmd_context *cmd, struct dm_pool *mem, |
|
+ struct logical_volume *lv, struct lv_disk *lvd) |
|
+{ |
|
+ if (!(lv->name = _create_lv_name(mem, (char *)lvd->lv_name))) |
|
+ return_0; |
|
+ |
|
+ lv->status |= VISIBLE_LV; |
|
+ |
|
+ if (lvd->lv_status & LV_SPINDOWN) |
|
+ lv->status |= SPINDOWN_LV; |
|
+ |
|
+ if (lvd->lv_status & LV_PERSISTENT_MINOR) { |
|
+ lv->status |= FIXED_MINOR; |
|
+ lv->minor = MINOR(lvd->lv_dev); |
|
+ lv->major = MAJOR(lvd->lv_dev); |
|
+ } else { |
|
+ lv->major = -1; |
|
+ lv->minor = -1; |
|
+ } |
|
+ |
|
+ if (lvd->lv_access & LV_READ) |
|
+ lv->status |= LVM_READ; |
|
+ |
|
+ if (lvd->lv_access & LV_WRITE) |
|
+ lv->status |= LVM_WRITE; |
|
+ |
|
+ if (lvd->lv_badblock) |
|
+ lv->status |= BADBLOCK_ON; |
|
+ |
|
+ /* Drop the unused LV_STRICT here */ |
|
+ if (lvd->lv_allocation & LV_CONTIGUOUS) |
|
+ lv->alloc = ALLOC_CONTIGUOUS; |
|
+ else |
|
+ lv->alloc = ALLOC_NORMAL; |
|
+ |
|
+ if (!lvd->lv_read_ahead) |
|
+ lv->read_ahead = cmd->default_settings.read_ahead; |
|
+ else |
|
+ lv->read_ahead = lvd->lv_read_ahead; |
|
+ |
|
+ lv->size = lvd->lv_size; |
|
+ lv->le_count = lvd->lv_allocated_le; |
|
+ |
|
+ return 1; |
|
+} |
|
+ |
|
+static void _export_lv(struct lv_disk *lvd, struct volume_group *vg, |
|
+ struct logical_volume *lv, const char *dev_dir) |
|
+{ |
|
+ memset(lvd, 0, sizeof(*lvd)); |
|
+ snprintf((char *)lvd->lv_name, sizeof(lvd->lv_name), "%s%s/%s", |
|
+ dev_dir, vg->name, lv->name); |
|
+ |
|
+ (void) dm_strncpy((char *)lvd->vg_name, vg->name, sizeof(lvd->vg_name)); |
|
+ |
|
+ if (lv->status & LVM_READ) |
|
+ lvd->lv_access |= LV_READ; |
|
+ |
|
+ if (lv->status & LVM_WRITE) |
|
+ lvd->lv_access |= LV_WRITE; |
|
+ |
|
+ if (lv->status & SPINDOWN_LV) |
|
+ lvd->lv_status |= LV_SPINDOWN; |
|
+ |
|
+ if (lv->status & FIXED_MINOR) { |
|
+ lvd->lv_status |= LV_PERSISTENT_MINOR; |
|
+ lvd->lv_dev = MKDEV(lv->major, lv->minor); |
|
+ } else { |
|
+ lvd->lv_dev = MKDEV(LVM_BLK_MAJOR, lvnum_from_lvid(&lv->lvid)); |
|
+ } |
|
+ |
|
+ if (lv->read_ahead == DM_READ_AHEAD_AUTO || |
|
+ lv->read_ahead == DM_READ_AHEAD_NONE) |
|
+ lvd->lv_read_ahead = 0; |
|
+ else |
|
+ lvd->lv_read_ahead = lv->read_ahead; |
|
+ |
|
+ lvd->lv_stripes = |
|
+ dm_list_item(lv->segments.n, struct lv_segment)->area_count; |
|
+ lvd->lv_stripesize = |
|
+ dm_list_item(lv->segments.n, struct lv_segment)->stripe_size; |
|
+ |
|
+ lvd->lv_size = lv->size; |
|
+ lvd->lv_allocated_le = lv->le_count; |
|
+ |
|
+ if (lv->status & BADBLOCK_ON) |
|
+ lvd->lv_badblock = LV_BADBLOCK_ON; |
|
+ |
|
+ if (lv->alloc == ALLOC_CONTIGUOUS) |
|
+ lvd->lv_allocation |= LV_CONTIGUOUS; |
|
+} |
|
+ |
|
+int export_extents(struct disk_list *dl, uint32_t lv_num, |
|
+ struct logical_volume *lv, struct physical_volume *pv) |
|
+{ |
|
+ struct pe_disk *ped; |
|
+ struct lv_segment *seg; |
|
+ uint32_t pe, s; |
|
+ |
|
+ dm_list_iterate_items(seg, &lv->segments) { |
|
+ for (s = 0; s < seg->area_count; s++) { |
|
+ if (!(seg->segtype->flags & SEG_FORMAT1_SUPPORT)) { |
|
+ log_error("Segment type %s in LV %s: " |
|
+ "unsupported by format1", |
|
+ lvseg_name(seg), lv->name); |
|
+ return 0; |
|
+ } |
|
+ if (seg_type(seg, s) != AREA_PV) { |
|
+ log_error("Non-PV stripe found in LV %s: " |
|
+ "unsupported by format1", lv->name); |
|
+ return 0; |
|
+ } |
|
+ if (seg_pv(seg, s) != pv) |
|
+ continue; /* not our pv */ |
|
+ |
|
+ for (pe = 0; pe < (seg->len / seg->area_count); pe++) { |
|
+ ped = &dl->extents[pe + seg_pe(seg, s)]; |
|
+ ped->lv_num = lv_num; |
|
+ ped->le_num = (seg->le / seg->area_count) + pe + |
|
+ s * (lv->le_count / seg->area_count); |
|
+ } |
|
+ } |
|
+ } |
|
+ |
|
+ return 1; |
|
+} |
|
+ |
|
+int import_pvs(const struct format_type *fmt, struct dm_pool *mem, |
|
+ struct volume_group *vg, struct dm_list *pvds) |
|
+{ |
|
+ struct disk_list *dl; |
|
+ struct pv_list *pvl; |
|
+ |
|
+ vg->pv_count = 0; |
|
+ dm_list_iterate_items(dl, pvds) { |
|
+ if (!(pvl = dm_pool_zalloc(mem, sizeof(*pvl))) || |
|
+ !(pvl->pv = dm_pool_alloc(mem, sizeof(*pvl->pv)))) |
|
+ return_0; |
|
+ |
|
+ if (!import_pv(fmt, mem, dl->dev, vg, pvl->pv, &dl->pvd, &dl->vgd)) |
|
+ return_0; |
|
+ |
|
+ pvl->pv->fmt = fmt; |
|
+ add_pvl_to_vgs(vg, pvl); |
|
+ } |
|
+ |
|
+ return 1; |
|
+} |
|
+ |
|
+static struct logical_volume *_add_lv(struct dm_pool *mem, |
|
+ struct volume_group *vg, |
|
+ struct lv_disk *lvd) |
|
+{ |
|
+ struct logical_volume *lv; |
|
+ |
|
+ if (!(lv = alloc_lv(mem))) |
|
+ return_NULL; |
|
+ |
|
+ lvid_from_lvnum(&lv->lvid, &vg->id, lvd->lv_number); |
|
+ |
|
+ if (!import_lv(vg->cmd, mem, lv, lvd)) |
|
+ goto_bad; |
|
+ |
|
+ if (!link_lv_to_vg(vg, lv)) |
|
+ goto_bad; |
|
+ |
|
+ return lv; |
|
+bad: |
|
+ dm_pool_free(mem, lv); |
|
+ return NULL; |
|
+} |
|
+ |
|
+int import_lvs(struct dm_pool *mem, struct volume_group *vg, struct dm_list *pvds) |
|
+{ |
|
+ struct disk_list *dl; |
|
+ struct lvd_list *ll; |
|
+ struct lv_disk *lvd; |
|
+ |
|
+ dm_list_iterate_items(dl, pvds) { |
|
+ dm_list_iterate_items(ll, &dl->lvds) { |
|
+ lvd = &ll->lvd; |
|
+ |
|
+ if (!find_lv(vg, (char *)lvd->lv_name) && |
|
+ !_add_lv(mem, vg, lvd)) |
|
+ return_0; |
|
+ } |
|
+ } |
|
+ |
|
+ return 1; |
|
+} |
|
+ |
|
+/* FIXME: tidy */ |
|
+int export_lvs(struct disk_list *dl, struct volume_group *vg, |
|
+ struct physical_volume *pv, const char *dev_dir) |
|
+{ |
|
+ int r = 0; |
|
+ struct lv_list *ll; |
|
+ struct lvd_list *lvdl; |
|
+ size_t len; |
|
+ uint32_t lv_num; |
|
+ struct dm_hash_table *lvd_hash; |
|
+ |
|
+ if (!_check_vg_name(vg->name)) |
|
+ return_0; |
|
+ |
|
+ if (!(lvd_hash = dm_hash_create(32))) |
|
+ return_0; |
|
+ |
|
+ /* |
|
+ * setup the pv's extents array |
|
+ */ |
|
+ len = sizeof(struct pe_disk) * dl->pvd.pe_total; |
|
+ if (!(dl->extents = dm_pool_zalloc(dl->mem, len))) |
|
+ goto_out; |
|
+ |
|
+ dm_list_iterate_items(ll, &vg->lvs) { |
|
+ if (lv_is_snapshot(ll->lv)) |
|
+ continue; |
|
+ |
|
+ if (!(lvdl = dm_pool_alloc(dl->mem, sizeof(*lvdl)))) |
|
+ goto_out; |
|
+ |
|
+ _export_lv(&lvdl->lvd, vg, ll->lv, dev_dir); |
|
+ |
|
+ lv_num = lvnum_from_lvid(&ll->lv->lvid); |
|
+ lvdl->lvd.lv_number = lv_num; |
|
+ |
|
+ if (!dm_hash_insert(lvd_hash, ll->lv->name, &lvdl->lvd)) |
|
+ goto_out; |
|
+ |
|
+ if (!export_extents(dl, lv_num + 1, ll->lv, pv)) |
|
+ goto_out; |
|
+ |
|
+ if (lv_is_origin(ll->lv)) |
|
+ lvdl->lvd.lv_access |= LV_SNAPSHOT_ORG; |
|
+ |
|
+ if (lv_is_cow(ll->lv)) { |
|
+ lvdl->lvd.lv_access |= LV_SNAPSHOT; |
|
+ lvdl->lvd.lv_chunk_size = ll->lv->snapshot->chunk_size; |
|
+ lvdl->lvd.lv_snapshot_minor = |
|
+ lvnum_from_lvid(&ll->lv->snapshot->origin->lvid); |
|
+ } |
|
+ |
|
+ dm_list_add(&dl->lvds, &lvdl->list); |
|
+ dl->pvd.lv_cur++; |
|
+ } |
|
+ |
|
+ r = 1; |
|
+ |
|
+ out: |
|
+ dm_hash_destroy(lvd_hash); |
|
+ return r; |
|
+} |
|
+ |
|
+/* |
|
+ * FIXME: More inefficient code. |
|
+ */ |
|
+int import_snapshots(struct dm_pool *mem __attribute__((unused)), struct volume_group *vg, |
|
+ struct dm_list *pvds) |
|
+{ |
|
+ struct logical_volume *lvs[MAX_LV] = { 0 }; |
|
+ struct disk_list *dl; |
|
+ struct lvd_list *ll; |
|
+ struct lv_disk *lvd; |
|
+ int lvnum; |
|
+ struct logical_volume *org, *cow; |
|
+ |
|
+ /* build an index of lv numbers */ |
|
+ dm_list_iterate_items(dl, pvds) { |
|
+ dm_list_iterate_items(ll, &dl->lvds) { |
|
+ lvd = &ll->lvd; |
|
+ |
|
+ lvnum = lvd->lv_number; |
|
+ |
|
+ if (lvnum >= MAX_LV) { |
|
+ log_error("Logical volume number " |
|
+ "out of bounds."); |
|
+ return 0; |
|
+ } |
|
+ |
|
+ if (!lvs[lvnum] && |
|
+ !(lvs[lvnum] = find_lv(vg, (char *)lvd->lv_name))) { |
|
+ log_error("Couldn't find logical volume '%s'.", |
|
+ lvd->lv_name); |
|
+ return 0; |
|
+ } |
|
+ } |
|
+ } |
|
+ |
|
+ /* |
|
+ * Now iterate through yet again adding the snapshots. |
|
+ */ |
|
+ dm_list_iterate_items(dl, pvds) { |
|
+ dm_list_iterate_items(ll, &dl->lvds) { |
|
+ lvd = &ll->lvd; |
|
+ |
|
+ if (!(lvd->lv_access & LV_SNAPSHOT)) |
|
+ continue; |
|
+ |
|
+ lvnum = lvd->lv_number; |
|
+ cow = lvs[lvnum]; |
|
+ if (!(org = lvs[lvd->lv_snapshot_minor])) { |
|
+ log_error("Couldn't find origin logical volume " |
|
+ "for snapshot '%s'.", lvd->lv_name); |
|
+ return 0; |
|
+ } |
|
+ |
|
+ /* we may have already added this snapshot */ |
|
+ if (lv_is_cow(cow)) |
|
+ continue; |
|
+ |
|
+ /* insert the snapshot */ |
|
+ if (!vg_add_snapshot(org, cow, NULL, |
|
+ org->le_count, |
|
+ lvd->lv_chunk_size)) { |
|
+ log_error("Couldn't add snapshot."); |
|
+ return 0; |
|
+ } |
|
+ } |
|
+ } |
|
+ |
|
+ return 1; |
|
+} |
|
+ |
|
+int export_uuids(struct disk_list *dl, struct volume_group *vg) |
|
+{ |
|
+ struct uuid_list *ul; |
|
+ struct pv_list *pvl; |
|
+ |
|
+ dm_list_iterate_items(pvl, &vg->pvs) { |
|
+ if (!(ul = dm_pool_alloc(dl->mem, sizeof(*ul)))) |
|
+ return_0; |
|
+ |
|
+ memset(ul->uuid, 0, sizeof(ul->uuid)); |
|
+ memcpy(ul->uuid, pvl->pv->id.uuid, ID_LEN); |
|
+ |
|
+ dm_list_add(&dl->uuids, &ul->list); |
|
+ } |
|
+ return 1; |
|
+} |
|
+ |
|
+/* |
|
+ * This calculates the nasty pv_number field |
|
+ * used by LVM1. |
|
+ */ |
|
+void export_numbers(struct dm_list *pvds, struct volume_group *vg __attribute__((unused))) |
|
+{ |
|
+ struct disk_list *dl; |
|
+ int pv_num = 1; |
|
+ |
|
+ dm_list_iterate_items(dl, pvds) |
|
+ dl->pvd.pv_number = pv_num++; |
|
+} |
|
+ |
|
+/* |
|
+ * Calculate vg_disk->pv_act. |
|
+ */ |
|
+void export_pv_act(struct dm_list *pvds) |
|
+{ |
|
+ struct disk_list *dl; |
|
+ int act = 0; |
|
+ |
|
+ dm_list_iterate_items(dl, pvds) |
|
+ if (dl->pvd.pv_status & PV_ACTIVE) |
|
+ act++; |
|
+ |
|
+ dm_list_iterate_items(dl, pvds) |
|
+ dl->vgd.pv_act = act; |
|
+} |
|
+ |
|
+int export_vg_number(struct format_instance *fid, struct dm_list *pvds, |
|
+ const char *vg_name, struct dev_filter *filter) |
|
+{ |
|
+ struct disk_list *dl; |
|
+ int vg_num; |
|
+ |
|
+ if (!get_free_vg_number(fid, filter, vg_name, &vg_num)) |
|
+ return_0; |
|
+ |
|
+ dm_list_iterate_items(dl, pvds) |
|
+ dl->vgd.vg_number = vg_num; |
|
+ |
|
+ return 1; |
|
+} |
|
diff --git a/lib/format1/import-extents.c b/lib/format1/import-extents.c |
|
new file mode 100644 |
|
index 0000000..c583741 |
|
--- /dev/null |
|
+++ b/lib/format1/import-extents.c |
|
@@ -0,0 +1,377 @@ |
|
+/* |
|
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved. |
|
+ * Copyright (C) 2004-2017 Red Hat, Inc. All rights reserved. |
|
+ * |
|
+ * This file is part of LVM2. |
|
+ * |
|
+ * This copyrighted material is made available to anyone wishing to use, |
|
+ * modify, copy, or redistribute it subject to the terms and conditions |
|
+ * of the GNU Lesser General Public License v.2.1. |
|
+ * |
|
+ * You should have received a copy of the GNU Lesser General Public License |
|
+ * along with this program; if not, write to the Free Software Foundation, |
|
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
|
+ */ |
|
+ |
|
+#include "lib.h" |
|
+#include "metadata.h" |
|
+#include "disk-rep.h" |
|
+#include "lv_alloc.h" |
|
+#include "display.h" |
|
+#include "segtype.h" |
|
+ |
|
+/* |
|
+ * After much thought I have decided it is easier, |
|
+ * and probably no less efficient, to convert the |
|
+ * pe->le map to a full le->pe map, and then |
|
+ * process this to get the segments form that |
|
+ * we're after. Any code which goes directly from |
|
+ * the pe->le map to segments would be gladly |
|
+ * accepted, if it is less complicated than this |
|
+ * file. |
|
+ */ |
|
+struct pe_specifier { |
|
+ struct physical_volume *pv; |
|
+ uint32_t pe; |
|
+}; |
|
+ |
|
+struct lv_map { |
|
+ struct logical_volume *lv; |
|
+ uint32_t stripes; |
|
+ uint32_t stripe_size; |
|
+ struct pe_specifier *map; |
|
+}; |
|
+ |
|
+static struct dm_hash_table *_create_lv_maps(struct dm_pool *mem, |
|
+ struct volume_group *vg) |
|
+{ |
|
+ struct dm_hash_table *maps = dm_hash_create(32); |
|
+ struct lv_list *ll; |
|
+ struct lv_map *lvm; |
|
+ |
|
+ if (!maps) { |
|
+ log_error("Unable to create hash table for holding " |
|
+ "extent maps."); |
|
+ return NULL; |
|
+ } |
|
+ |
|
+ dm_list_iterate_items(ll, &vg->lvs) { |
|
+ if (lv_is_snapshot(ll->lv)) |
|
+ continue; |
|
+ |
|
+ if (!(lvm = dm_pool_alloc(mem, sizeof(*lvm)))) |
|
+ goto_bad; |
|
+ |
|
+ lvm->lv = ll->lv; |
|
+ /* |
|
+ * Alloc 1 extra element, so the loop in _area_length() and |
|
+ * _check_stripe() finds the last map member as noncontinuous. |
|
+ */ |
|
+ if (!(lvm->map = dm_pool_zalloc(mem, sizeof(*lvm->map) |
|
+ * (ll->lv->le_count + 1)))) |
|
+ goto_bad; |
|
+ |
|
+ if (!dm_hash_insert(maps, ll->lv->name, lvm)) |
|
+ goto_bad; |
|
+ } |
|
+ |
|
+ return maps; |
|
+ |
|
+ bad: |
|
+ dm_hash_destroy(maps); |
|
+ return NULL; |
|
+} |
|
+ |
|
+static int _fill_lv_array(struct lv_map **lvs, |
|
+ struct dm_hash_table *maps, struct disk_list *dl) |
|
+{ |
|
+ struct lvd_list *ll; |
|
+ struct lv_map *lvm; |
|
+ |
|
+ memset(lvs, 0, sizeof(*lvs) * MAX_LV); |
|
+ |
|
+ dm_list_iterate_items(ll, &dl->lvds) { |
|
+ if (!(lvm = dm_hash_lookup(maps, strrchr((char *)ll->lvd.lv_name, '/') |
|
+ + 1))) { |
|
+ log_error("Physical volume (%s) contains an " |
|
+ "unknown logical volume (%s).", |
|
+ dev_name(dl->dev), ll->lvd.lv_name); |
|
+ return 0; |
|
+ } |
|
+ |
|
+ lvm->stripes = ll->lvd.lv_stripes; |
|
+ lvm->stripe_size = ll->lvd.lv_stripesize; |
|
+ |
|
+ lvs[ll->lvd.lv_number] = lvm; |
|
+ } |
|
+ |
|
+ return 1; |
|
+} |
|
+ |
|
+static int _fill_maps(struct dm_hash_table *maps, struct volume_group *vg, |
|
+ struct dm_list *pvds) |
|
+{ |
|
+ struct disk_list *dl; |
|
+ struct physical_volume *pv; |
|
+ struct lv_map *lvms[MAX_LV], *lvm; |
|
+ struct pe_disk *e; |
|
+ uint32_t i, lv_num, le; |
|
+ |
|
+ dm_list_iterate_items(dl, pvds) { |
|
+ if (!(pv = find_pv(vg, dl->dev))) { |
|
+ log_error("PV %s not found.", dl->dev->pvid); |
|
+ return 0; |
|
+ } |
|
+ e = dl->extents; |
|
+ |
|
+ /* build an array of lv's for this pv */ |
|
+ if (!_fill_lv_array(lvms, maps, dl)) |
|
+ return_0; |
|
+ |
|
+ for (i = 0; i < dl->pvd.pe_total; i++) { |
|
+ lv_num = e[i].lv_num; |
|
+ |
|
+ if (lv_num == UNMAPPED_EXTENT) |
|
+ continue; |
|
+ |
|
+ lv_num--; |
|
+ lvm = lvms[lv_num]; |
|
+ |
|
+ if (!lvm) { |
|
+ log_error("Invalid LV in extent map " |
|
+ "(PV %s, PE %" PRIu32 |
|
+ ", LV %" PRIu32 |
|
+ ", LE %" PRIu32 ")", |
|
+ dev_name(pv->dev), i, |
|
+ lv_num, e[i].le_num); |
|
+ return 0; |
|
+ } |
|
+ |
|
+ le = e[i].le_num; |
|
+ |
|
+ if (le >= lvm->lv->le_count) { |
|
+ log_error("logical extent number " |
|
+ "out of bounds"); |
|
+ return 0; |
|
+ } |
|
+ |
|
+ if (lvm->map[le].pv) { |
|
+ log_error("logical extent (%u) " |
|
+ "already mapped.", le); |
|
+ return 0; |
|
+ } |
|
+ |
|
+ lvm->map[le].pv = pv; |
|
+ lvm->map[le].pe = i; |
|
+ } |
|
+ } |
|
+ |
|
+ return 1; |
|
+} |
|
+ |
|
+static int _check_single_map(struct lv_map *lvm) |
|
+{ |
|
+ uint32_t i; |
|
+ |
|
+ for (i = 0; i < lvm->lv->le_count; i++) { |
|
+ if (!lvm->map[i].pv) { |
|
+ log_error("Logical volume (%s) contains an incomplete " |
|
+ "mapping table.", lvm->lv->name); |
|
+ return 0; |
|
+ } |
|
+ } |
|
+ |
|
+ return 1; |
|
+} |
|
+ |
|
+static int _check_maps_are_complete(struct dm_hash_table *maps) |
|
+{ |
|
+ struct dm_hash_node *n; |
|
+ struct lv_map *lvm; |
|
+ |
|
+ for (n = dm_hash_get_first(maps); n; n = dm_hash_get_next(maps, n)) { |
|
+ lvm = (struct lv_map *) dm_hash_get_data(maps, n); |
|
+ |
|
+ if (!_check_single_map(lvm)) |
|
+ return_0; |
|
+ } |
|
+ return 1; |
|
+} |
|
+ |
|
+static uint32_t _area_length(struct lv_map *lvm, uint32_t le) |
|
+{ |
|
+ uint32_t len = 0; |
|
+ |
|
+ do |
|
+ len++; |
|
+ while ((lvm->map[le + len].pv == lvm->map[le].pv) && |
|
+ (lvm->map[le].pv && |
|
+ lvm->map[le + len].pe == lvm->map[le].pe + len)); |
|
+ |
|
+ return len; |
|
+} |
|
+ |
|
+static int _read_linear(struct cmd_context *cmd, struct lv_map *lvm) |
|
+{ |
|
+ uint32_t le = 0, len; |
|
+ struct lv_segment *seg; |
|
+ struct segment_type *segtype; |
|
+ |
|
+ if (!(segtype = get_segtype_from_string(cmd, SEG_TYPE_NAME_STRIPED))) |
|
+ return_0; |
|
+ |
|
+ while (le < lvm->lv->le_count) { |
|
+ len = _area_length(lvm, le); |
|
+ |
|
+ if (!(seg = alloc_lv_segment(segtype, lvm->lv, le, len, 0, 0, 0, |
|
+ NULL, 1, len, 0, 0, 0, 0, NULL))) { |
|
+ log_error("Failed to allocate linear segment."); |
|
+ return 0; |
|
+ } |
|
+ |
|
+ if (!set_lv_segment_area_pv(seg, 0, lvm->map[le].pv, |
|
+ lvm->map[le].pe)) |
|
+ return_0; |
|
+ |
|
+ dm_list_add(&lvm->lv->segments, &seg->list); |
|
+ |
|
+ le += seg->len; |
|
+ } |
|
+ |
|
+ return 1; |
|
+} |
|
+ |
|
+static int _check_stripe(struct lv_map *lvm, uint32_t area_count, |
|
+ uint32_t area_len, uint32_t base_le, |
|
+ uint32_t total_area_len) |
|
+{ |
|
+ uint32_t st; |
|
+ |
|
+ /* |
|
+ * Is the next physical extent in every stripe adjacent to the last? |
|
+ */ |
|
+ for (st = 0; st < area_count; st++) |
|
+ if ((lvm->map[base_le + st * total_area_len + area_len].pv != |
|
+ lvm->map[base_le + st * total_area_len].pv) || |
|
+ (lvm->map[base_le + st * total_area_len].pv && |
|
+ lvm->map[base_le + st * total_area_len + area_len].pe != |
|
+ lvm->map[base_le + st * total_area_len].pe + area_len)) |
|
+ return 0; |
|
+ |
|
+ return 1; |
|
+} |
|
+ |
|
+static int _read_stripes(struct cmd_context *cmd, struct lv_map *lvm) |
|
+{ |
|
+ uint32_t st, first_area_le = 0, total_area_len; |
|
+ uint32_t area_len; |
|
+ struct lv_segment *seg; |
|
+ struct segment_type *segtype; |
|
+ |
|
+ /* |
|
+ * Work out overall striped length |
|
+ */ |
|
+ if (lvm->lv->le_count % lvm->stripes) { |
|
+ log_error("Number of stripes (%u) incompatible " |
|
+ "with logical extent count (%u) for %s", |
|
+ lvm->stripes, lvm->lv->le_count, lvm->lv->name); |
|
+ } |
|
+ |
|
+ total_area_len = lvm->lv->le_count / lvm->stripes; |
|
+ |
|
+ if (!(segtype = get_segtype_from_string(cmd, SEG_TYPE_NAME_STRIPED))) |
|
+ return_0; |
|
+ |
|
+ while (first_area_le < total_area_len) { |
|
+ area_len = 1; |
|
+ |
|
+ /* |
|
+ * Find how many extents are contiguous in all stripes |
|
+ * and so can form part of this segment |
|
+ */ |
|
+ while (_check_stripe(lvm, lvm->stripes, |
|
+ area_len, first_area_le, total_area_len)) |
|
+ area_len++; |
|
+ |
|
+ if (!(seg = alloc_lv_segment(segtype, lvm->lv, |
|
+ lvm->stripes * first_area_le, |
|
+ lvm->stripes * area_len, 0, |
|
+ 0, lvm->stripe_size, NULL, |
|
+ lvm->stripes, |
|
+ area_len, 0, 0, 0, 0, NULL))) { |
|
+ log_error("Failed to allocate striped segment."); |
|
+ return 0; |
|
+ } |
|
+ |
|
+ /* |
|
+ * Set up start positions of each stripe in this segment |
|
+ */ |
|
+ for (st = 0; st < seg->area_count; st++) |
|
+ if (!set_lv_segment_area_pv(seg, st, |
|
+ lvm->map[first_area_le + st * total_area_len].pv, |
|
+ lvm->map[first_area_le + st * total_area_len].pe)) |
|
+ return_0; |
|
+ |
|
+ dm_list_add(&lvm->lv->segments, &seg->list); |
|
+ |
|
+ first_area_le += area_len; |
|
+ } |
|
+ |
|
+ return 1; |
|
+} |
|
+ |
|
+static int _build_segments(struct cmd_context *cmd, struct lv_map *lvm) |
|
+{ |
|
+ return (lvm->stripes > 1 ? _read_stripes(cmd, lvm) : |
|
+ _read_linear(cmd, lvm)); |
|
+} |
|
+ |
|
+static int _build_all_segments(struct cmd_context *cmd, struct dm_hash_table *maps) |
|
+{ |
|
+ struct dm_hash_node *n; |
|
+ struct lv_map *lvm; |
|
+ |
|
+ for (n = dm_hash_get_first(maps); n; n = dm_hash_get_next(maps, n)) { |
|
+ lvm = (struct lv_map *) dm_hash_get_data(maps, n); |
|
+ if (!_build_segments(cmd, lvm)) |
|
+ return_0; |
|
+ } |
|
+ |
|
+ return 1; |
|
+} |
|
+ |
|
+int import_extents(struct cmd_context *cmd, struct volume_group *vg, |
|
+ struct dm_list *pvds) |
|
+{ |
|
+ int r = 0; |
|
+ struct dm_pool *scratch = dm_pool_create("lvm1 import_extents", 10 * 1024); |
|
+ struct dm_hash_table *maps; |
|
+ |
|
+ if (!scratch) |
|
+ return_0; |
|
+ |
|
+ if (!(maps = _create_lv_maps(scratch, vg))) { |
|
+ log_error("Couldn't allocate logical volume maps."); |
|
+ goto out; |
|
+ } |
|
+ |
|
+ if (!_fill_maps(maps, vg, pvds)) { |
|
+ log_error("Couldn't fill logical volume maps."); |
|
+ goto out; |
|
+ } |
|
+ |
|
+ if (!_check_maps_are_complete(maps) && !(vg->status & PARTIAL_VG)) |
|
+ goto_out; |
|
+ |
|
+ if (!_build_all_segments(cmd, maps)) { |
|
+ log_error("Couldn't build extent segments."); |
|
+ goto out; |
|
+ } |
|
+ r = 1; |
|
+ |
|
+ out: |
|
+ if (maps) |
|
+ dm_hash_destroy(maps); |
|
+ dm_pool_destroy(scratch); |
|
+ return r; |
|
+} |
|
diff --git a/lib/format1/layout.c b/lib/format1/layout.c |
|
new file mode 100644 |
|
index 0000000..380a983 |
|
--- /dev/null |
|
+++ b/lib/format1/layout.c |
|
@@ -0,0 +1,172 @@ |
|
+/* |
|
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved. |
|
+ * Copyright (C) 2004-2006 Red Hat, Inc. All rights reserved. |
|
+ * |
|
+ * This file is part of LVM2. |
|
+ * |
|
+ * This copyrighted material is made available to anyone wishing to use, |
|
+ * modify, copy, or redistribute it subject to the terms and conditions |
|
+ * of the GNU Lesser General Public License v.2.1. |
|
+ * |
|
+ * You should have received a copy of the GNU Lesser General Public License |
|
+ * along with this program; if not, write to the Free Software Foundation, |
|
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
|
+ */ |
|
+ |
|
+#include "lib.h" |
|
+#include "disk-rep.h" |
|
+ |
|
+/* |
|
+ * Only works with powers of 2. |
|
+ */ |
|
+static uint32_t _round_up(uint32_t n, uint32_t size) |
|
+{ |
|
+ size--; |
|
+ return (n + size) & ~size; |
|
+} |
|
+ |
|
+/* Unused. |
|
+static uint32_t _div_up(uint32_t n, uint32_t size) |
|
+{ |
|
+ return _round_up(n, size) / size; |
|
+} |
|
+*/ |
|
+ |
|
+/* |
|
+ * Each chunk of metadata should be aligned to |
|
+ * METADATA_ALIGN. |
|
+ */ |
|
+static uint32_t _next_base(struct data_area *area) |
|
+{ |
|
+ return _round_up(area->base + area->size, METADATA_ALIGN); |
|
+} |
|
+ |
|
+/* |
|
+ * Quick calculation based on pe_start. |
|
+ */ |
|
+static int _adjust_pe_on_disk(struct pv_disk *pvd) |
|
+{ |
|
+ uint32_t pe_start = pvd->pe_start << SECTOR_SHIFT; |
|
+ |
|
+ if (pe_start < pvd->pe_on_disk.base + pvd->pe_on_disk.size) |
|
+ return 0; |
|
+ |
|
+ pvd->pe_on_disk.size = pe_start - pvd->pe_on_disk.base; |
|
+ return 1; |
|
+} |
|
+ |
|
+static void _calc_simple_layout(struct pv_disk *pvd) |
|
+{ |
|
+ pvd->pv_on_disk.base = METADATA_BASE; |
|
+ pvd->pv_on_disk.size = PV_SIZE; |
|
+ |
|
+ pvd->vg_on_disk.base = _next_base(&pvd->pv_on_disk); |
|
+ pvd->vg_on_disk.size = VG_SIZE; |
|
+ |
|
+ pvd->pv_uuidlist_on_disk.base = _next_base(&pvd->vg_on_disk); |
|
+ pvd->pv_uuidlist_on_disk.size = MAX_PV * NAME_LEN; |
|
+ |
|
+ pvd->lv_on_disk.base = _next_base(&pvd->pv_uuidlist_on_disk); |
|
+ pvd->lv_on_disk.size = MAX_LV * sizeof(struct lv_disk); |
|
+ |
|
+ pvd->pe_on_disk.base = _next_base(&pvd->lv_on_disk); |
|
+ pvd->pe_on_disk.size = pvd->pe_total * sizeof(struct pe_disk); |
|
+} |
|
+ |
|
+static int _check_vg_limits(struct disk_list *dl) |
|
+{ |
|
+ if (dl->vgd.lv_max > MAX_LV) { |
|
+ log_error("MaxLogicalVolumes of %d exceeds format limit of %d " |
|
+ "for VG '%s'", dl->vgd.lv_max, MAX_LV - 1, |
|
+ dl->pvd.vg_name); |
|
+ return 0; |
|
+ } |
|
+ |
|
+ if (dl->vgd.pv_max > MAX_PV) { |
|
+ log_error("MaxPhysicalVolumes of %d exceeds format limit of %d " |
|
+ "for VG '%s'", dl->vgd.pv_max, MAX_PV - 1, |
|
+ dl->pvd.vg_name); |
|
+ return 0; |
|
+ } |
|
+ |
|
+ return 1; |
|
+} |
|
+ |
|
+/* |
|
+ * This assumes pe_count and pe_start have already |
|
+ * been calculated correctly. |
|
+ */ |
|
+int calculate_layout(struct disk_list *dl) |
|
+{ |
|
+ struct pv_disk *pvd = &dl->pvd; |
|
+ |
|
+ _calc_simple_layout(pvd); |
|
+ if (!_adjust_pe_on_disk(pvd)) { |
|
+ log_error("Insufficient space for metadata and PE's."); |
|
+ return 0; |
|
+ } |
|
+ |
|
+ if (!_check_vg_limits(dl)) |
|
+ return 0; |
|
+ |
|
+ return 1; |
|
+} |
|
+ |
|
+/* |
|
+ * The number of extents that can fit on a disk is metadata format dependant. |
|
+ * pe_start is any existing value for pe_start |
|
+ */ |
|
+int calculate_extent_count(struct physical_volume *pv, uint32_t extent_size, |
|
+ uint32_t max_extent_count, uint64_t pe_start) |
|
+{ |
|
+ struct pv_disk *pvd = dm_malloc(sizeof(*pvd)); |
|
+ uint32_t end; |
|
+ |
|
+ if (!pvd) |
|
+ return_0; |
|
+ |
|
+ /* |
|
+ * Guess how many extents will fit, bearing in mind that |
|
+ * one is going to be knocked off at the start of the |
|
+ * next loop. |
|
+ */ |
|
+ if (max_extent_count) |
|
+ pvd->pe_total = max_extent_count + 1; |
|
+ else |
|
+ pvd->pe_total = (pv->size / extent_size); |
|
+ |
|
+ if (pvd->pe_total < PE_SIZE_PV_SIZE_REL) { |
|
+ log_error("Too few extents on %s. Try smaller extent size.", |
|
+ pv_dev_name(pv)); |
|
+ dm_free(pvd); |
|
+ return 0; |
|
+ } |
|
+ |
|
+ do { |
|
+ pvd->pe_total--; |
|
+ _calc_simple_layout(pvd); |
|
+ end = ((pvd->pe_on_disk.base + pvd->pe_on_disk.size + |
|
+ SECTOR_SIZE - 1) >> SECTOR_SHIFT); |
|
+ |
|
+ if (pe_start && end < pe_start) |
|
+ end = pe_start; |
|
+ |
|
+ pvd->pe_start = _round_up(end, LVM1_PE_ALIGN); |
|
+ |
|
+ } while ((pvd->pe_start + ((uint64_t)pvd->pe_total * extent_size)) |
|
+ > pv->size); |
|
+ |
|
+ if (pvd->pe_total > MAX_PE_TOTAL) { |
|
+ log_error("Metadata extent limit (%u) exceeded for %s - " |
|
+ "%u required", MAX_PE_TOTAL, pv_dev_name(pv), |
|
+ pvd->pe_total); |
|
+ dm_free(pvd); |
|
+ return 0; |
|
+ } |
|
+ |
|
+ pv->pe_count = pvd->pe_total; |
|
+ pv->pe_start = pvd->pe_start; |
|
+ /* We can't set pe_size here without breaking LVM1 compatibility */ |
|
+ dm_free(pvd); |
|
+ return 1; |
|
+} |
|
diff --git a/lib/format1/lvm1-label.c b/lib/format1/lvm1-label.c |
|
new file mode 100644 |
|
index 0000000..691a05a |
|
--- /dev/null |
|
+++ b/lib/format1/lvm1-label.c |
|
@@ -0,0 +1,129 @@ |
|
+/* |
|
+ * Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved. |
|
+ * Copyright (C) 2004-2006 Red Hat, Inc. All rights reserved. |
|
+ * |
|
+ * This file is part of LVM2. |
|
+ * |
|
+ * This copyrighted material is made available to anyone wishing to use, |
|
+ * modify, copy, or redistribute it subject to the terms and conditions |
|
+ * of the GNU Lesser General Public License v.2.1. |
|
+ * |
|
+ * You should have received a copy of the GNU Lesser General Public License |
|
+ * along with this program; if not, write to the Free Software Foundation, |
|
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
|
+ */ |
|
+ |
|
+#include "lib.h" |
|
+#include "lvm1-label.h" |
|
+#include "disk-rep.h" |
|
+#include "label.h" |
|
+#include "metadata.h" |
|
+#include "xlate.h" |
|
+#include "format1.h" |
|
+ |
|
+#include <sys/stat.h> |
|
+#include <fcntl.h> |
|
+ |
|
+static void _not_supported(const char *op) |
|
+{ |
|
+ log_error("The '%s' operation is not supported for the lvm1 labeller.", |
|
+ op); |
|
+} |
|
+ |
|
+static int _lvm1_can_handle(struct labeller *l __attribute__((unused)), void *buf, uint64_t sector) |
|
+{ |
|
+ struct pv_disk *pvd = (struct pv_disk *) buf; |
|
+ uint32_t version; |
|
+ |
|
+ /* LVM1 label must always be in first sector */ |
|
+ if (sector) |
|
+ return 0; |
|
+ |
|
+ version = xlate16(pvd->version); |
|
+ |
|
+ if (pvd->id[0] == 'H' && pvd->id[1] == 'M' && |
|
+ (version == 1 || version == 2)) |
|
+ return 1; |
|
+ |
|
+ return 0; |
|
+} |
|
+ |
|
+static int _lvm1_write(struct label *label __attribute__((unused)), void *buf __attribute__((unused))) |
|
+{ |
|
+ _not_supported("write"); |
|
+ return 0; |
|
+} |
|
+ |
|
+static int _lvm1_read(struct labeller *l, struct device *dev, void *buf, |
|
+ struct label **label) |
|
+{ |
|
+ struct pv_disk *pvd = (struct pv_disk *) buf; |
|
+ struct vg_disk vgd; |
|
+ struct lvmcache_info *info; |
|
+ const char *vgid = FMT_LVM1_ORPHAN_VG_NAME; |
|
+ const char *vgname = FMT_LVM1_ORPHAN_VG_NAME; |
|
+ unsigned exported = 0; |
|
+ |
|
+ munge_pvd(dev, pvd); |
|
+ |
|
+ if (*pvd->vg_name) { |
|
+ if (!read_vgd(dev, &vgd, pvd)) |
|
+ return_0; |
|
+ vgid = (char *) vgd.vg_uuid; |
|
+ vgname = (char *) pvd->vg_name; |
|
+ exported = pvd->pv_status & VG_EXPORTED; |
|
+ } |
|
+ |
|
+ if (!(info = lvmcache_add(l, (char *)pvd->pv_uuid, dev, vgname, vgid, |
|
+ exported))) |
|
+ return_0; |
|
+ *label = lvmcache_get_label(info); |
|
+ |
|
+ lvmcache_set_device_size(info, ((uint64_t)xlate32(pvd->pv_size)) << SECTOR_SHIFT); |
|
+ lvmcache_set_ext_version(info, 0); |
|
+ lvmcache_set_ext_flags(info, 0); |
|
+ lvmcache_del_mdas(info); |
|
+ lvmcache_del_bas(info); |
|
+ |
|
+ return 1; |
|
+} |
|
+ |
|
+static int _lvm1_initialise_label(struct labeller *l __attribute__((unused)), struct label *label) |
|
+{ |
|
+ strcpy(label->type, "LVM1"); |
|
+ |
|
+ return 1; |
|
+} |
|
+ |
|
+static void _lvm1_destroy_label(struct labeller *l __attribute__((unused)), struct label *label __attribute__((unused))) |
|
+{ |
|
+} |
|
+ |
|
+static void _lvm1_destroy(struct labeller *l) |
|
+{ |
|
+ dm_free(l); |
|
+} |
|
+ |
|
+struct label_ops _lvm1_ops = { |
|
+ .can_handle = _lvm1_can_handle, |
|
+ .write = _lvm1_write, |
|
+ .read = _lvm1_read, |
|
+ .initialise_label = _lvm1_initialise_label, |
|
+ .destroy_label = _lvm1_destroy_label, |
|
+ .destroy = _lvm1_destroy, |
|
+}; |
|
+ |
|
+struct labeller *lvm1_labeller_create(struct format_type *fmt) |
|
+{ |
|
+ struct labeller *l; |
|
+ |
|
+ if (!(l = dm_malloc(sizeof(*l)))) { |
|
+ log_error("Couldn't allocate labeller object."); |
|
+ return NULL; |
|
+ } |
|
+ |
|
+ l->ops = &_lvm1_ops; |
|
+ l->fmt = fmt; |
|
+ |
|
+ return l; |
|
+} |
|
diff --git a/lib/format1/lvm1-label.h b/lib/format1/lvm1-label.h |
|
new file mode 100644 |
|
index 0000000..27f2f51 |
|
--- /dev/null |
|
+++ b/lib/format1/lvm1-label.h |
|
@@ -0,0 +1,23 @@ |
|
+/* |
|
+ * Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved. |
|
+ * Copyright (C) 2004 Red Hat, Inc. All rights reserved. |
|
+ * |
|
+ * This file is part of LVM2. |
|
+ * |
|
+ * This copyrighted material is made available to anyone wishing to use, |
|
+ * modify, copy, or redistribute it subject to the terms and conditions |
|
+ * of the GNU Lesser General Public License v.2.1. |
|
+ * |
|
+ * You should have received a copy of the GNU Lesser General Public License |
|
+ * along with this program; if not, write to the Free Software Foundation, |
|
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
|
+ */ |
|
+ |
|
+#ifndef _LVM_LVM1_LABEL_H |
|
+#define _LVM_LVM1_LABEL_H |
|
+ |
|
+#include "metadata.h" |
|
+ |
|
+struct labeller *lvm1_labeller_create(struct format_type *fmt); |
|
+ |
|
+#endif |
|
diff --git a/lib/format1/vg_number.c b/lib/format1/vg_number.c |
|
new file mode 100644 |
|
index 0000000..8b1a803 |
|
--- /dev/null |
|
+++ b/lib/format1/vg_number.c |
|
@@ -0,0 +1,60 @@ |
|
+/* |
|
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved. |
|
+ * Copyright (C) 2004-2006 Red Hat, Inc. All rights reserved. |
|
+ * |
|
+ * This file is part of LVM2. |
|
+ * |
|
+ * This copyrighted material is made available to anyone wishing to use, |
|
+ * modify, copy, or redistribute it subject to the terms and conditions |
|
+ * of the GNU Lesser General Public License v.2.1. |
|
+ * |
|
+ * You should have received a copy of the GNU Lesser General Public License |
|
+ * along with this program; if not, write to the Free Software Foundation, |
|
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
|
+ */ |
|
+ |
|
+#include "lib.h" |
|
+#include "disk-rep.h" |
|
+ |
|
+/* |
|
+ * FIXME: Quick hack. We can use caching to |
|
+ * prevent a total re-read, even so vg_number |
|
+ * causes the tools to check *every* pv. Yuck. |
|
+ * Put in separate file so it wouldn't contaminate |
|
+ * other code. |
|
+ */ |
|
+int get_free_vg_number(struct format_instance *fid, struct dev_filter *filter, |
|
+ const char *candidate_vg, int *result) |
|
+{ |
|
+ struct dm_list all_pvs; |
|
+ struct disk_list *dl; |
|
+ struct dm_pool *mem = dm_pool_create("lvm1 vg_number", 10 * 1024); |
|
+ int i, r = 0, numbers[MAX_VG] = { 0 }; |
|
+ |
|
+ dm_list_init(&all_pvs); |
|
+ |
|
+ if (!mem) |
|
+ return_0; |
|
+ |
|
+ if (!read_pvs_in_vg(fid->fmt, NULL, filter, mem, &all_pvs)) |
|
+ goto_out; |
|
+ |
|
+ dm_list_iterate_items(dl, &all_pvs) { |
|
+ if (!*dl->pvd.vg_name || !strcmp((char *)dl->pvd.vg_name, candidate_vg)) |
|
+ continue; |
|
+ |
|
+ numbers[dl->vgd.vg_number] = 1; |
|
+ } |
|
+ |
|
+ for (i = 0; i < MAX_VG; i++) { |
|
+ if (!numbers[i]) { |
|
+ r = 1; |
|
+ *result = i; |
|
+ break; |
|
+ } |
|
+ } |
|
+ |
|
+ out: |
|
+ dm_pool_destroy(mem); |
|
+ return r; |
|
+} |
|
diff --git a/lib/format_pool/.exported_symbols b/lib/format_pool/.exported_symbols |
|
new file mode 100644 |
|
index 0000000..e9fac2e |
|
--- /dev/null |
|
+++ b/lib/format_pool/.exported_symbols |
|
@@ -0,0 +1 @@ |
|
+init_format |
|
diff --git a/lib/format_pool/Makefile.in b/lib/format_pool/Makefile.in |
|
new file mode 100644 |
|
index 0000000..05d1b0c |
|
--- /dev/null |
|
+++ b/lib/format_pool/Makefile.in |
|
@@ -0,0 +1,30 @@ |
|
+# |
|
+# Copyright (C) 2003-2004 Sistina Software, Inc. All rights reserved. |
|
+# Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved. |
|
+# |
|
+# This file is part of LVM2. |
|
+# |
|
+# This copyrighted material is made available to anyone wishing to use, |
|
+# modify, copy, or redistribute it subject to the terms and conditions |
|
+# of the GNU General Public License v.2. |
|
+# |
|
+# You should have received a copy of the GNU General Public License |
|
+# along with this program; if not, write to the Free Software Foundation, |
|
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
|
+ |
|
+srcdir = @srcdir@ |
|
+top_srcdir = @top_srcdir@ |
|
+top_builddir = @top_builddir@ |
|
+ |
|
+SOURCES =\ |
|
+ disk_rep.c \ |
|
+ format_pool.c \ |
|
+ import_export.c \ |
|
+ pool_label.c |
|
+ |
|
+LIB_SHARED = liblvm2formatpool.$(LIB_SUFFIX) |
|
+LIB_VERSION = $(LIB_VERSION_LVM) |
|
+ |
|
+include $(top_builddir)/make.tmpl |
|
+ |
|
+install: install_lvm2_plugin |
|
diff --git a/lib/format_pool/disk_rep.c b/lib/format_pool/disk_rep.c |
|
new file mode 100644 |
|
index 0000000..fe9b03e |
|
--- /dev/null |
|
+++ b/lib/format_pool/disk_rep.c |
|
@@ -0,0 +1,409 @@ |
|
+/* |
|
+ * Copyright (C) 1997-2004 Sistina Software, Inc. All rights reserved. |
|
+ * Copyright (C) 2004-2006 Red Hat, Inc. All rights reserved. |
|
+ * |
|
+ * This file is part of LVM2. |
|
+ * |
|
+ * This copyrighted material is made available to anyone wishing to use, |
|
+ * modify, copy, or redistribute it subject to the terms and conditions |
|
+ * of the GNU Lesser General Public License v.2.1. |
|
+ * |
|
+ * You should have received a copy of the GNU Lesser General Public License |
|
+ * along with this program; if not, write to the Free Software Foundation, |
|
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
|
+ */ |
|
+ |
|
+#include "lib.h" |
|
+#include "label.h" |
|
+#include "metadata.h" |
|
+#include "lvmcache.h" |
|
+#include "xlate.h" |
|
+#include "disk_rep.h" |
|
+#include "toolcontext.h" |
|
+ |
|
+#include <assert.h> |
|
+ |
|
+/* FIXME: memcpy might not be portable */ |
|
+#define CPIN_8(x, y, z) {memcpy((x), (y), (z));} |
|
+#define CPOUT_8(x, y, z) {memcpy((y), (x), (z));} |
|
+#define CPIN_16(x, y) {(x) = xlate16_be((y));} |
|
+#define CPOUT_16(x, y) {(y) = xlate16_be((x));} |
|
+#define CPIN_32(x, y) {(x) = xlate32_be((y));} |
|
+#define CPOUT_32(x, y) {(y) = xlate32_be((x));} |
|
+#define CPIN_64(x, y) {(x) = xlate64_be((y));} |
|
+#define CPOUT_64(x, y) {(y) = xlate64_be((x));} |
|
+ |
|
+static int __read_pool_disk(const struct format_type *fmt, struct device *dev, |
|
+ struct dm_pool *mem __attribute__((unused)), struct pool_list *pl, |
|
+ const char *vg_name __attribute__((unused))) |
|
+{ |
|
+ char buf[512] __attribute__((aligned(8))); |
|
+ |
|
+ /* FIXME: Need to check the cache here first */ |
|
+ if (!dev_read(dev, UINT64_C(0), 512, DEV_IO_POOL, buf)) { |
|
+ log_very_verbose("Failed to read PV data from %s", |
|
+ dev_name(dev)); |
|
+ return 0; |
|
+ } |
|
+ |
|
+ if (!read_pool_label(pl, fmt->labeller, dev, buf, NULL)) |
|
+ return_0; |
|
+ |
|
+ return 1; |
|
+} |
|
+ |
|
+static void _add_pl_to_list(struct cmd_context *cmd, struct dm_list *head, struct pool_list *data) |
|
+{ |
|
+ struct pool_list *pl; |
|
+ |
|
+ dm_list_iterate_items(pl, head) { |
|
+ if (id_equal(&data->pv_uuid, &pl->pv_uuid)) { |
|
+ char uuid[ID_LEN + 7] __attribute__((aligned(8))); |
|
+ |
|
+ if (!id_write_format(&pl->pv_uuid, uuid, ID_LEN + 7)) |
|
+ stack; |
|
+ |
|
+ if (!dev_subsystem_part_major(cmd->dev_types, data->dev)) { |
|
+ log_very_verbose("Ignoring duplicate PV %s on " |
|
+ "%s", uuid, |
|
+ dev_name(data->dev)); |
|
+ return; |
|
+ } |
|
+ log_very_verbose("Duplicate PV %s - using %s %s", |
|
+ uuid, dev_subsystem_name(cmd->dev_types, data->dev), |
|
+ dev_name(data->dev)); |
|
+ dm_list_del(&pl->list); |
|
+ break; |
|
+ } |
|
+ } |
|
+ dm_list_add(head, &data->list); |
|
+} |
|
+ |
|
+int read_pool_label(struct pool_list *pl, struct labeller *l, |
|
+ struct device *dev, char *buf, struct label **label) |
|
+{ |
|
+ struct lvmcache_info *info; |
|
+ struct id pvid; |
|
+ struct id vgid; |
|
+ char uuid[ID_LEN + 7] __attribute__((aligned(8))); |
|
+ struct pool_disk *pd = &pl->pd; |
|
+ |
|
+ pool_label_in(pd, buf); |
|
+ |
|
+ get_pool_pv_uuid(&pvid, pd); |
|
+ if (!id_write_format(&pvid, uuid, ID_LEN + 7)) |
|
+ stack; |
|
+ log_debug_metadata("Calculated uuid %s for %s", uuid, dev_name(dev)); |
|
+ |
|
+ get_pool_vg_uuid(&vgid, pd); |
|
+ if (!id_write_format(&vgid, uuid, ID_LEN + 7)) |
|
+ stack; |
|
+ log_debug_metadata("Calculated uuid %s for %s", uuid, pd->pl_pool_name); |
|
+ |
|
+ if (!(info = lvmcache_add(l, (char *) &pvid, dev, pd->pl_pool_name, |
|
+ (char *) &vgid, 0))) |
|
+ return_0; |
|
+ if (label) |
|
+ *label = lvmcache_get_label(info); |
|
+ |
|
+ lvmcache_set_device_size(info, ((uint64_t)xlate32_be(pd->pl_blocks)) << SECTOR_SHIFT); |
|
+ lvmcache_set_ext_version(info, 0); |
|
+ lvmcache_set_ext_flags(info, 0); |
|
+ lvmcache_del_mdas(info); |
|
+ lvmcache_del_bas(info); |
|
+ |
|
+ pl->dev = dev; |
|
+ pl->pv = NULL; |
|
+ memcpy(&pl->pv_uuid, &pvid, sizeof(pvid)); |
|
+ |
|
+ return 1; |
|
+} |
|
+ |
|
+/** |
|
+ * pool_label_out - copies a pool_label_t into a char buffer |
|
+ * @pl: ptr to a pool_label_t struct |
|
+ * @buf: ptr to raw space where label info will be copied |
|
+ * |
|
+ * This function is important because it takes care of all of |
|
+ * the endian issues when copying to disk. This way, when |
|
+ * machines of different architectures are used, they will |
|
+ * be able to interpret ondisk labels correctly. Always use |
|
+ * this function before writing to disk. |
|
+ */ |
|
+void pool_label_out(struct pool_disk *pl, void *buf) |
|
+{ |
|
+ struct pool_disk *bufpl = (struct pool_disk *) buf; |
|
+ |
|
+ CPOUT_64(pl->pl_magic, bufpl->pl_magic); |
|
+ CPOUT_64(pl->pl_pool_id, bufpl->pl_pool_id); |
|
+ CPOUT_8(pl->pl_pool_name, bufpl->pl_pool_name, POOL_NAME_SIZE); |
|
+ CPOUT_32(pl->pl_version, bufpl->pl_version); |
|
+ CPOUT_32(pl->pl_subpools, bufpl->pl_subpools); |
|
+ CPOUT_32(pl->pl_sp_id, bufpl->pl_sp_id); |
|
+ CPOUT_32(pl->pl_sp_devs, bufpl->pl_sp_devs); |
|
+ CPOUT_32(pl->pl_sp_devid, bufpl->pl_sp_devid); |
|
+ CPOUT_32(pl->pl_sp_type, bufpl->pl_sp_type); |
|
+ CPOUT_64(pl->pl_blocks, bufpl->pl_blocks); |
|
+ CPOUT_32(pl->pl_striping, bufpl->pl_striping); |
|
+ CPOUT_32(pl->pl_sp_dmepdevs, bufpl->pl_sp_dmepdevs); |
|
+ CPOUT_32(pl->pl_sp_dmepid, bufpl->pl_sp_dmepid); |
|
+ CPOUT_32(pl->pl_sp_weight, bufpl->pl_sp_weight); |
|
+ CPOUT_32(pl->pl_minor, bufpl->pl_minor); |
|
+ CPOUT_32(pl->pl_padding, bufpl->pl_padding); |
|
+ CPOUT_8(pl->pl_reserve, bufpl->pl_reserve, 184); |
|
+} |
|
+ |
|
+/** |
|
+ * pool_label_in - copies a char buffer into a pool_label_t |
|
+ * @pl: ptr to a pool_label_t struct |
|
+ * @buf: ptr to raw space where label info is copied from |
|
+ * |
|
+ * This function is important because it takes care of all of |
|
+ * the endian issues when information from disk is about to be |
|
+ * used. This way, when machines of different architectures |
|
+ * are used, they will be able to interpret ondisk labels |
|
+ * correctly. Always use this function before using labels that |
|
+ * were read from disk. |
|
+ */ |
|
+void pool_label_in(struct pool_disk *pl, void *buf) |
|
+{ |
|
+ struct pool_disk *bufpl = (struct pool_disk *) buf; |
|
+ |
|
+ CPIN_64(pl->pl_magic, bufpl->pl_magic); |
|
+ CPIN_64(pl->pl_pool_id, bufpl->pl_pool_id); |
|
+ CPIN_8(pl->pl_pool_name, bufpl->pl_pool_name, POOL_NAME_SIZE); |
|
+ CPIN_32(pl->pl_version, bufpl->pl_version); |
|
+ CPIN_32(pl->pl_subpools, bufpl->pl_subpools); |
|
+ CPIN_32(pl->pl_sp_id, bufpl->pl_sp_id); |
|
+ CPIN_32(pl->pl_sp_devs, bufpl->pl_sp_devs); |
|
+ CPIN_32(pl->pl_sp_devid, bufpl->pl_sp_devid); |
|
+ CPIN_32(pl->pl_sp_type, bufpl->pl_sp_type); |
|
+ CPIN_64(pl->pl_blocks, bufpl->pl_blocks); |
|
+ CPIN_32(pl->pl_striping, bufpl->pl_striping); |
|
+ CPIN_32(pl->pl_sp_dmepdevs, bufpl->pl_sp_dmepdevs); |
|
+ CPIN_32(pl->pl_sp_dmepid, bufpl->pl_sp_dmepid); |
|
+ CPIN_32(pl->pl_sp_weight, bufpl->pl_sp_weight); |
|
+ CPIN_32(pl->pl_minor, bufpl->pl_minor); |
|
+ CPIN_32(pl->pl_padding, bufpl->pl_padding); |
|
+ CPIN_8(pl->pl_reserve, bufpl->pl_reserve, 184); |
|
+} |
|
+ |
|
+static char _calc_char(unsigned int id) |
|
+{ |
|
+ /* |
|
+ * [0-9A-Za-z!#] - 64 printable chars (6-bits) |
|
+ */ |
|
+ |
|
+ if (id < 10) |
|
+ return id + 48; |
|
+ if (id < 36) |
|
+ return (id - 10) + 65; |
|
+ if (id < 62) |
|
+ return (id - 36) + 97; |
|
+ if (id == 62) |
|
+ return '!'; |
|
+ if (id == 63) |
|
+ return '#'; |
|
+ |
|
+ return '%'; |
|
+} |
|
+ |
|
+void get_pool_uuid(char *uuid, uint64_t poolid, uint32_t spid, uint32_t devid) |
|
+{ |
|
+ int i; |
|
+ unsigned shifter = 0x003F; |
|
+ |
|
+ assert(ID_LEN == 32); |
|
+ memset(uuid, 0, ID_LEN); |
|
+ strcat(uuid, "POOL0000000000"); |
|
+ |
|
+ /* We grab the entire 64 bits (+2 that get shifted in) */ |
|
+ for (i = 13; i < 24; i++) { |
|
+ uuid[i] = _calc_char(((unsigned) poolid) & shifter); |
|
+ poolid = poolid >> 6; |
|
+ } |
|
+ |
|
+ /* We grab the entire 32 bits (+4 that get shifted in) */ |
|
+ for (i = 24; i < 30; i++) { |
|
+ uuid[i] = _calc_char((unsigned) (spid & shifter)); |
|
+ spid = spid >> 6; |
|
+ } |
|
+ |
|
+ /* |
|
+ * Since we can only have 128 devices, we only worry about the |
|
+ * last 12 bits |
|
+ */ |
|
+ for (i = 30; i < 32; i++) { |
|
+ uuid[i] = _calc_char((unsigned) (devid & shifter)); |
|
+ devid = devid >> 6; |
|
+ } |
|
+ |
|
+} |
|
+ |
|
+struct _read_pool_pv_baton { |
|
+ const struct format_type *fmt; |
|
+ struct dm_pool *mem, *tmpmem; |
|
+ struct pool_list *pl; |
|
+ struct dm_list *head; |
|
+ const char *vgname; |
|
+ uint32_t *sp_devs; |
|
+ uint32_t sp_count; |
|
+ int failed; |
|
+ int empty; |
|
+}; |
|
+ |
|
+static int _read_pool_pv(struct lvmcache_info *info, void *baton) |
|
+{ |
|
+ struct _read_pool_pv_baton *b = baton; |
|
+ |
|
+ b->empty = 0; |
|
+ |
|
+ if (lvmcache_device(info) && |
|
+ !(b->pl = read_pool_disk(b->fmt, lvmcache_device(info), b->mem, b->vgname))) |
|
+ return 0; |
|
+ |
|
+ /* |
|
+ * We need to keep track of the total expected number |
|
+ * of devices per subpool |
|
+ */ |
|
+ if (!b->sp_count) { |
|
+ /* FIXME pl left uninitialised if !info->dev */ |
|
+ if (!b->pl) { |
|
+ log_error(INTERNAL_ERROR "device is missing"); |
|
+ dm_pool_destroy(b->tmpmem); |
|
+ b->failed = 1; |
|
+ return 0; |
|
+ } |
|
+ b->sp_count = b->pl->pd.pl_subpools; |
|
+ if (!(b->sp_devs = |
|
+ dm_pool_zalloc(b->tmpmem, |
|
+ sizeof(uint32_t) * b->sp_count))) { |
|
+ log_error("Unable to allocate %d 32-bit uints", |
|
+ b->sp_count); |
|
+ dm_pool_destroy(b->tmpmem); |
|
+ b->failed = 1; |
|
+ return 0; |
|
+ } |
|
+ } |
|
+ |
|
+ /* |
|
+ * watch out for a pool label with a different subpool |
|
+ * count than the original - give up if it does |
|
+ */ |
|
+ if (b->sp_count != b->pl->pd.pl_subpools) |
|
+ return 0; |
|
+ |
|
+ _add_pl_to_list(lvmcache_fmt(info)->cmd, b->head, b->pl); |
|
+ |
|
+ if (b->sp_count > b->pl->pd.pl_sp_id && b->sp_devs[b->pl->pd.pl_sp_id] == 0) |
|
+ b->sp_devs[b->pl->pd.pl_sp_id] = b->pl->pd.pl_sp_devs; |
|
+ |
|
+ return 1; |
|
+} |
|
+ |
|
+static int _read_vg_pds(struct _read_pool_pv_baton *b, |
|
+ struct lvmcache_vginfo *vginfo, |
|
+ uint32_t *devcount) |
|
+{ |
|
+ uint32_t i; |
|
+ |
|
+ b->sp_count = 0; |
|
+ b->sp_devs = NULL; |
|
+ b->failed = 0; |
|
+ b->pl = NULL; |
|
+ |
|
+ /* FIXME: maybe should return a different error in memory |
|
+ * allocation failure */ |
|
+ if (!(b->tmpmem = dm_pool_create("pool read_vg", 512))) |
|
+ return_0; |
|
+ |
|
+ lvmcache_foreach_pv(vginfo, _read_pool_pv, b); |
|
+ |
|
+ *devcount = 0; |
|
+ for (i = 0; i < b->sp_count; i++) |
|
+ *devcount += b->sp_devs[i]; |
|
+ |
|
+ dm_pool_destroy(b->tmpmem); |
|
+ |
|
+ if (b->pl && *b->pl->pd.pl_pool_name) |
|
+ return 1; |
|
+ |
|
+ return 0; |
|
+ |
|
+} |
|
+ |
|
+int read_pool_pds(const struct format_type *fmt, const char *vg_name, |
|
+ struct dm_pool *mem, struct dm_list *pdhead) |
|
+{ |
|
+ struct lvmcache_vginfo *vginfo; |
|
+ uint32_t totaldevs; |
|
+ int full_scan = -1; |
|
+ |
|
+ struct _read_pool_pv_baton baton; |
|
+ |
|
+ baton.vgname = vg_name; |
|
+ baton.mem = mem; |
|
+ baton.fmt = fmt; |
|
+ baton.head = pdhead; |
|
+ baton.empty = 1; |
|
+ |
|
+ do { |
|
+ /* |
|
+ * If the cache scanning doesn't work, this will never work |
|
+ */ |
|
+ if (vg_name && (vginfo = lvmcache_vginfo_from_vgname(vg_name, NULL)) && |
|
+ _read_vg_pds(&baton, vginfo, &totaldevs) && !baton.empty) |
|
+ { |
|
+ /* |
|
+ * If we found all the devices we were expecting, return |
|
+ * success |
|
+ */ |
|
+ if (dm_list_size(pdhead) == totaldevs) |
|
+ return 1; |
|
+ |
|
+ /* |
|
+ * accept partial pool if we've done a full rescan of |
|
+ * the cache |
|
+ */ |
|
+ if (full_scan > 0) |
|
+ return 1; |
|
+ } |
|
+ |
|
+ /* Failed */ |
|
+ dm_list_init(pdhead); |
|
+ |
|
+ full_scan++; |
|
+ if (full_scan > 1) { |
|
+ log_debug_metadata("No devices for vg %s found in cache", |
|
+ vg_name); |
|
+ return 0; |
|
+ } |
|
+ lvmcache_label_scan(fmt->cmd); |
|
+ |
|
+ } while (1); |
|
+ |
|
+} |
|
+ |
|
+struct pool_list *read_pool_disk(const struct format_type *fmt, |
|
+ struct device *dev, struct dm_pool *mem, |
|
+ const char *vg_name) |
|
+{ |
|
+ struct pool_list *pl; |
|
+ |
|
+ if (!dev_open_readonly(dev)) |
|
+ return_NULL; |
|
+ |
|
+ if (!(pl = dm_pool_zalloc(mem, sizeof(*pl)))) { |
|
+ log_error("Unable to allocate pool list structure"); |
|
+ return 0; |
|
+ } |
|
+ |
|
+ if (!__read_pool_disk(fmt, dev, mem, pl, vg_name)) |
|
+ return_NULL; |
|
+ |
|
+ if (!dev_close(dev)) |
|
+ stack; |
|
+ |
|
+ return pl; |
|
+ |
|
+} |
|
diff --git a/lib/format_pool/disk_rep.h b/lib/format_pool/disk_rep.h |
|
new file mode 100644 |
|
index 0000000..37e942e |
|
--- /dev/null |
|
+++ b/lib/format_pool/disk_rep.h |
|
@@ -0,0 +1,156 @@ |
|
+/* |
|
+ * Copyright (C) 1997-2004 Sistina Software, Inc. All rights reserved. |
|
+ * Copyright (C) 2004-2006 Red Hat, Inc. All rights reserved. |
|
+ * |
|
+ * This file is part of LVM2. |
|
+ * |
|
+ * This copyrighted material is made available to anyone wishing to use, |
|
+ * modify, copy, or redistribute it subject to the terms and conditions |
|
+ * of the GNU Lesser General Public License v.2.1. |
|
+ * |
|
+ * You should have received a copy of the GNU Lesser General Public License |
|
+ * along with this program; if not, write to the Free Software Foundation, |
|
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
|
+ */ |
|
+ |
|
+#ifndef DISK_REP_FORMAT_POOL_H |
|
+#define DISK_REP_FORMAT_POOL_H |
|
+ |
|
+#include "label.h" |
|
+#include "metadata.h" |
|
+ |
|
+#define MINOR_OFFSET 65536 |
|
+ |
|
+/* From NSP.cf */ |
|
+#define NSPMajorVersion 4 |
|
+#define NSPMinorVersion 1 |
|
+#define NSPUpdateLevel 3 |
|
+ |
|
+/* From pool_std.h */ |
|
+#define POOL_NAME_SIZE (256) |
|
+#define POOL_MAGIC 0x011670 |
|
+#define POOL_MAJOR (121) |
|
+#define POOL_MAX_DEVICES 128 |
|
+ |
|
+/* When checking for version matching, the first two numbers ** |
|
+** are important for metadata formats, a.k.a pool labels. ** |
|
+** All the numbers are important when checking if the user ** |
|
+** space tools match up with the kernel module............. */ |
|
+#define POOL_VERSION (NSPMajorVersion << 16 | \ |
|
+ NSPMinorVersion << 8 | \ |
|
+ NSPUpdateLevel) |
|
+ |
|
+/* Pool label is at the head of every pool disk partition */ |
|
+#define SIZEOF_POOL_LABEL (8192) |
|
+ |
|
+/* in sectors */ |
|
+#define POOL_PE_SIZE (SIZEOF_POOL_LABEL >> SECTOR_SHIFT) |
|
+#define POOL_PE_START (SIZEOF_POOL_LABEL >> SECTOR_SHIFT) |
|
+ |
|
+/* Helper fxns */ |
|
+#define get_pool_vg_uuid(id, pd) do { get_pool_uuid((char *)(id), \ |
|
+ (pd)->pl_pool_id, 0, 0); \ |
|
+ } while(0) |
|
+#define get_pool_pv_uuid(id, pd) do { get_pool_uuid((char *)(id), \ |
|
+ (pd)->pl_pool_id, \ |
|
+ (pd)->pl_sp_id, \ |
|
+ (pd)->pl_sp_devid); \ |
|
+ } while(0) |
|
+#define get_pool_lv_uuid(id, pd) do { get_pool_uuid((char *)&(id)[0], \ |
|
+ (pd)->pl_pool_id, 0, 0); \ |
|
+ get_pool_uuid((char*)&(id)[1], \ |
|
+ (pd)->pl_pool_id, 0, 0); \ |
|
+ } while(0) |
|
+ |
|
+struct pool_disk; |
|
+struct pool_list; |
|
+struct user_subpool; |
|
+struct user_device; |
|
+ |
|
+struct pool_disk { |
|
+ uint64_t pl_magic; /* Pool magic number */ |
|
+ uint64_t pl_pool_id; /* Unique pool identifier */ |
|
+ char pl_pool_name[POOL_NAME_SIZE]; /* Name of pool */ |
|
+ uint32_t pl_version; /* Pool version */ |
|
+ uint32_t pl_subpools; /* Number of subpools in this pool */ |
|
+ uint32_t pl_sp_id; /* Subpool number within pool */ |
|
+ uint32_t pl_sp_devs; /* Number of data partitions in this subpool */ |
|
+ uint32_t pl_sp_devid; /* Partition number within subpool */ |
|
+ uint32_t pl_sp_type; /* Partition type */ |
|
+ uint64_t pl_blocks; /* Number of blocks in this partition */ |
|
+ uint32_t pl_striping; /* Striping size within subpool */ |
|
+ /* |
|
+ * If the number of DMEP devices is zero, then the next field ** |
|
+ * ** (pl_sp_dmepid) becomes the subpool ID for redirection. In ** |
|
+ * ** other words, if this subpool does not have the capability ** |
|
+ * ** to do DMEP, then it must specify which subpool will do it ** |
|
+ * ** in it's place |
|
+ */ |
|
+ |
|
+ /* |
|
+ * While the next 3 field are no longer used, they must stay to keep ** |
|
+ * ** backward compatibility........................................... |
|
+ */ |
|
+ uint32_t pl_sp_dmepdevs;/* Number of dmep devices in this subpool */ |
|
+ uint32_t pl_sp_dmepid; /* Dmep device number within subpool */ |
|
+ uint32_t pl_sp_weight; /* if dmep dev, pref to using it */ |
|
+ |
|
+ uint32_t pl_minor; /* the pool minor number */ |
|
+ uint32_t pl_padding; /* reminder - think about alignment */ |
|
+ |
|
+ /* |
|
+ * Even though we're zeroing out 8k at the front of the disk before |
|
+ * writing the label, putting this in |
|
+ */ |
|
+ char pl_reserve[184]; /* bump the structure size out to 512 bytes */ |
|
+}; |
|
+ |
|
+struct pool_list { |
|
+ struct dm_list list; |
|
+ struct pool_disk pd; |
|
+ struct physical_volume *pv; |
|
+ struct id pv_uuid; |
|
+ struct device *dev; |
|
+}; |
|
+ |
|
+struct user_subpool { |
|
+ uint32_t initialized; |
|
+ uint32_t id; |
|
+ uint32_t striping; |
|
+ uint32_t num_devs; |
|
+ uint32_t type; |
|
+ uint32_t dummy; |
|
+ struct user_device *devs; |
|
+}; |
|
+ |
|
+struct user_device { |
|
+ uint32_t initialized; |
|
+ uint32_t sp_id; |
|
+ uint32_t devid; |
|
+ uint32_t dummy; |
|
+ uint64_t blocks; |
|
+ struct physical_volume *pv; |
|
+}; |
|
+ |
|
+int read_pool_label(struct pool_list *pl, struct labeller *l, |
|
+ struct device *dev, char *buf, struct label **label); |
|
+void pool_label_out(struct pool_disk *pl, void *buf); |
|
+void pool_label_in(struct pool_disk *pl, void *buf); |
|
+void get_pool_uuid(char *uuid, uint64_t poolid, uint32_t spid, uint32_t devid); |
|
+int import_pool_vg(struct volume_group *vg, struct dm_pool *mem, struct dm_list *pls); |
|
+int import_pool_lvs(struct volume_group *vg, struct dm_pool *mem, |
|
+ struct dm_list *pls); |
|
+int import_pool_pvs(const struct format_type *fmt, struct volume_group *vg, |
|
+ struct dm_pool *mem, struct dm_list *pls); |
|
+int import_pool_pv(const struct format_type *fmt, struct dm_pool *mem, |
|
+ struct volume_group *vg, struct physical_volume *pv, |
|
+ struct pool_list *pl); |
|
+int import_pool_segments(struct dm_list *lvs, struct dm_pool *mem, |
|
+ struct user_subpool *usp, int sp_count); |
|
+int read_pool_pds(const struct format_type *fmt, const char *vgname, |
|
+ struct dm_pool *mem, struct dm_list *head); |
|
+struct pool_list *read_pool_disk(const struct format_type *fmt, |
|
+ struct device *dev, struct dm_pool *mem, |
|
+ const char *vg_name); |
|
+ |
|
+#endif /* DISK_REP_POOL_FORMAT_H */ |
|
diff --git a/lib/format_pool/format_pool.c b/lib/format_pool/format_pool.c |
|
new file mode 100644 |
|
index 0000000..f8223a3 |
|
--- /dev/null |
|
+++ b/lib/format_pool/format_pool.c |
|
@@ -0,0 +1,337 @@ |
|
+/* |
|
+ * Copyright (C) 1997-2004 Sistina Software, Inc. All rights reserved. |
|
+ * Copyright (C) 2004-2012 Red Hat, Inc. All rights reserved. |
|
+ * |
|
+ * This file is part of LVM2. |
|
+ * |
|
+ * This copyrighted material is made available to anyone wishing to use, |
|
+ * modify, copy, or redistribute it subject to the terms and conditions |
|
+ * of the GNU Lesser General Public License v.2.1. |
|
+ * |
|
+ * You should have received a copy of the GNU Lesser General Public License |
|
+ * along with this program; if not, write to the Free Software Foundation, |
|
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
|
+ */ |
|
+ |
|
+#include "lib.h" |
|
+#include "label.h" |
|
+#include "metadata.h" |
|
+#include "limits.h" |
|
+#include "display.h" |
|
+#include "toolcontext.h" |
|
+#include "lvmcache.h" |
|
+#include "disk_rep.h" |
|
+#include "format_pool.h" |
|
+#include "pool_label.h" |
|
+ |
|
+/* Must be called after pvs are imported */ |
|
+static struct user_subpool *_build_usp(struct dm_list *pls, struct dm_pool *mem, |
|
+ int *sps) |
|
+{ |
|
+ struct pool_list *pl; |
|
+ struct user_subpool *usp = NULL, *cur_sp = NULL; |
|
+ struct user_device *cur_dev = NULL; |
|
+ |
|
+ /* |
|
+ * FIXME: Need to do some checks here - I'm tempted to add a |
|
+ * user_pool structure and build the entire thing to check against. |
|
+ */ |
|
+ dm_list_iterate_items(pl, pls) { |
|
+ *sps = pl->pd.pl_subpools; |
|
+ if (!usp && (!(usp = dm_pool_zalloc(mem, sizeof(*usp) * (*sps))))) { |
|
+ log_error("Unable to allocate %d subpool structures", |
|
+ *sps); |
|
+ return 0; |
|
+ } |
|
+ |
|
+ if (cur_sp != &usp[pl->pd.pl_sp_id]) { |
|
+ cur_sp = &usp[pl->pd.pl_sp_id]; |
|
+ |
|
+ cur_sp->id = pl->pd.pl_sp_id; |
|
+ cur_sp->striping = pl->pd.pl_striping; |
|
+ cur_sp->num_devs = pl->pd.pl_sp_devs; |
|
+ cur_sp->type = pl->pd.pl_sp_type; |
|
+ cur_sp->initialized = 1; |
|
+ } |
|
+ |
|
+ if (!cur_sp->devs && |
|
+ (!(cur_sp->devs = |
|
+ dm_pool_zalloc(mem, |
|
+ sizeof(*usp->devs) * pl->pd.pl_sp_devs)))) { |
|
+ |
|
+ log_error("Unable to allocate %d pool_device " |
|
+ "structures", pl->pd.pl_sp_devs); |
|
+ return 0; |
|
+ } |
|
+ |
|
+ cur_dev = &cur_sp->devs[pl->pd.pl_sp_devid]; |
|
+ cur_dev->sp_id = cur_sp->id; |
|
+ cur_dev->devid = pl->pd.pl_sp_id; |
|
+ cur_dev->blocks = pl->pd.pl_blocks; |
|
+ cur_dev->pv = pl->pv; |
|
+ cur_dev->initialized = 1; |
|
+ } |
|
+ |
|
+ return usp; |
|
+} |
|
+ |
|
+static int _check_usp(const char *vgname, struct user_subpool *usp, int sp_count) |
|
+{ |
|
+ int i; |
|
+ unsigned j; |
|
+ |
|
+ for (i = 0; i < sp_count; i++) { |
|
+ if (!usp[i].initialized) { |
|
+ log_error("Missing subpool %d in pool %s", i, vgname); |
|
+ return 0; |
|
+ } |
|
+ for (j = 0; j < usp[i].num_devs; j++) { |
|
+ if (!usp[i].devs[j].initialized) { |
|
+ log_error("Missing device %u for subpool %d" |
|
+ " in pool %s", j, i, vgname); |
|
+ return 0; |
|
+ } |
|
+ |
|
+ } |
|
+ } |
|
+ |
|
+ return 1; |
|
+} |
|
+ |
|
+static struct volume_group *_pool_vg_read(struct format_instance *fid, |
|
+ const char *vg_name, |
|
+ struct metadata_area *mda __attribute__((unused)), |
|
+ struct cached_vg_fmtdata **vg_fmtdata __attribute__((unused)), |
|
+ unsigned *use_previous_vg __attribute__((unused))) |
|
+{ |
|
+ struct volume_group *vg; |
|
+ struct user_subpool *usp; |
|
+ int sp_count; |
|
+ DM_LIST_INIT(pds); |
|
+ |
|
+ /* We can safely ignore the mda passed in */ |
|
+ |
|
+ /* Strip dev_dir if present */ |
|
+ if (vg_name) |
|
+ vg_name = strip_dir(vg_name, fid->fmt->cmd->dev_dir); |
|
+ |
|
+ /* Set vg_name through read_pool_pds() */ |
|
+ if (!(vg = alloc_vg("pool_vg_read", fid->fmt->cmd, NULL))) |
|
+ return_NULL; |
|
+ |
|
+ /* Read all the pvs in the vg */ |
|
+ if (!read_pool_pds(fid->fmt, vg_name, vg->vgmem, &pds)) |
|
+ goto_bad; |
|
+ |
|
+ /* Setting pool seqno to 1 because the code always did this, |
|
+ * although we don't think it's needed. */ |
|
+ vg->seqno = 1; |
|
+ |
|
+ if (!import_pool_vg(vg, vg->vgmem, &pds)) |
|
+ goto_bad; |
|
+ |
|
+ if (!import_pool_pvs(fid->fmt, vg, vg->vgmem, &pds)) |
|
+ goto_bad; |
|
+ |
|
+ if (!import_pool_lvs(vg, vg->vgmem, &pds)) |
|
+ goto_bad; |
|
+ |
|
+ /* |
|
+ * I need an intermediate subpool structure that contains all the |
|
+ * relevant info for this. Then i can iterate through the subpool |
|
+ * structures for checking, and create the segments |
|
+ */ |
|
+ if (!(usp = _build_usp(&pds, vg->vgmem, &sp_count))) |
|
+ goto_bad; |
|
+ |
|
+ /* |
|
+ * check the subpool structures - we can't handle partial VGs in |
|
+ * the pool format, so this will error out if we're missing PVs |
|
+ */ |
|
+ if (!_check_usp(vg->name, usp, sp_count)) |
|
+ goto_bad; |
|
+ |
|
+ if (!import_pool_segments(&vg->lvs, vg->vgmem, usp, sp_count)) |
|
+ goto_bad; |
|
+ |
|
+ vg_set_fid(vg, fid); |
|
+ |
|
+ return vg; |
|
+ |
|
+bad: |
|
+ release_vg(vg); |
|
+ |
|
+ return NULL; |
|
+} |
|
+ |
|
+static int _pool_pv_initialise(const struct format_type *fmt __attribute__((unused)), |
|
+ struct pv_create_args *pva __attribute__((unused)), |
|
+ struct physical_volume *pv __attribute__((unused))) |
|
+{ |
|
+ return 1; |
|
+} |
|
+ |
|
+static int _pool_pv_setup(const struct format_type *fmt __attribute__((unused)), |
|
+ struct physical_volume *pv __attribute__((unused)), |
|
+ struct volume_group *vg __attribute__((unused))) |
|
+{ |
|
+ return 1; |
|
+} |
|
+ |
|
+static int _pool_pv_read(const struct format_type *fmt, const char *pv_name, |
|
+ struct physical_volume *pv, |
|
+ int scan_label_only __attribute__((unused))) |
|
+{ |
|
+ struct dm_pool *mem = dm_pool_create("pool pv_read", 1024); |
|
+ struct pool_list *pl; |
|
+ struct device *dev; |
|
+ int r = 0; |
|
+ |
|
+ log_very_verbose("Reading physical volume data %s from disk", pv_name); |
|
+ |
|
+ if (!mem) |
|
+ return_0; |
|
+ |
|
+ if (!(dev = dev_cache_get(pv_name, fmt->cmd->filter))) |
|
+ goto_out; |
|
+ |
|
+ /* |
|
+ * I need to read the disk and populate a pv structure here |
|
+ * I'll probably need to abstract some of this later for the |
|
+ * vg_read code |
|
+ */ |
|
+ if (!(pl = read_pool_disk(fmt, dev, mem, NULL))) |
|
+ goto_out; |
|
+ |
|
+ if (!import_pool_pv(fmt, fmt->cmd->mem, NULL, pv, pl)) |
|
+ goto_out; |
|
+ |
|
+ pv->fmt = fmt; |
|
+ |
|
+ r = 1; |
|
+ |
|
+ out: |
|
+ dm_pool_destroy(mem); |
|
+ return r; |
|
+} |
|
+ |
|
+/* *INDENT-OFF* */ |
|
+static struct metadata_area_ops _metadata_format_pool_ops = { |
|
+ .vg_read = _pool_vg_read, |
|
+}; |
|
+/* *INDENT-ON* */ |
|
+ |
|
+static struct format_instance *_pool_create_instance(const struct format_type *fmt, |
|
+ const struct format_instance_ctx *fic) |
|
+{ |
|
+ struct format_instance *fid; |
|
+ struct metadata_area *mda; |
|
+ |
|
+ if (!(fid = alloc_fid(fmt, fic))) |
|
+ return_NULL; |
|
+ |
|
+ /* Define a NULL metadata area */ |
|
+ if (!(mda = dm_pool_zalloc(fid->mem, sizeof(*mda)))) { |
|
+ log_error("Unable to allocate metadata area structure " |
|
+ "for pool format"); |
|
+ goto bad; |
|
+ } |
|
+ |
|
+ mda->ops = &_metadata_format_pool_ops; |
|
+ mda->metadata_locn = NULL; |
|
+ mda->status = 0; |
|
+ dm_list_add(&fid->metadata_areas_in_use, &mda->list); |
|
+ |
|
+ return fid; |
|
+ |
|
+bad: |
|
+ dm_pool_destroy(fid->mem); |
|
+ return NULL; |
|
+} |
|
+ |
|
+static void _pool_destroy_instance(struct format_instance *fid) |
|
+{ |
|
+ if (--fid->ref_count <= 1) |
|
+ dm_pool_destroy(fid->mem); |
|
+} |
|
+ |
|
+static void _pool_destroy(struct format_type *fmt) |
|
+{ |
|
+ if (fmt->orphan_vg) |
|
+ free_orphan_vg(fmt->orphan_vg); |
|
+ |
|
+ dm_free(fmt); |
|
+} |
|
+ |
|
+/* *INDENT-OFF* */ |
|
+static struct format_handler _format_pool_ops = { |
|
+ .pv_read = _pool_pv_read, |
|
+ .pv_initialise = _pool_pv_initialise, |
|
+ .pv_setup = _pool_pv_setup, |
|
+ .create_instance = _pool_create_instance, |
|
+ .destroy_instance = _pool_destroy_instance, |
|
+ .destroy = _pool_destroy, |
|
+}; |
|
+/* *INDENT-ON */ |
|
+ |
|
+#ifdef POOL_INTERNAL |
|
+struct format_type *init_pool_format(struct cmd_context *cmd) |
|
+#else /* Shared */ |
|
+struct format_type *init_format(struct cmd_context *cmd); |
|
+struct format_type *init_format(struct cmd_context *cmd) |
|
+#endif |
|
+{ |
|
+ struct format_type *fmt = dm_malloc(sizeof(*fmt)); |
|
+ struct format_instance_ctx fic; |
|
+ struct format_instance *fid; |
|
+ |
|
+ if (!fmt) { |
|
+ log_error("Unable to allocate format type structure for pool " |
|
+ "format"); |
|
+ return NULL; |
|
+ } |
|
+ |
|
+ fmt->cmd = cmd; |
|
+ fmt->ops = &_format_pool_ops; |
|
+ fmt->name = FMT_POOL_NAME; |
|
+ fmt->alias = NULL; |
|
+ fmt->orphan_vg_name = FMT_POOL_ORPHAN_VG_NAME; |
|
+ fmt->features = FMT_OBSOLETE; |
|
+ fmt->private = NULL; |
|
+ |
|
+ dm_list_init(&fmt->mda_ops); |
|
+ |
|
+ if (!(fmt->labeller = pool_labeller_create(fmt))) { |
|
+ log_error("Couldn't create pool label handler."); |
|
+ dm_free(fmt); |
|
+ return NULL; |
|
+ } |
|
+ |
|
+ if (!(label_register_handler(fmt->labeller))) { |
|
+ log_error("Couldn't register pool label handler."); |
|
+ fmt->labeller->ops->destroy(fmt->labeller); |
|
+ dm_free(fmt); |
|
+ return NULL; |
|
+ } |
|
+ |
|
+ if (!(fmt->orphan_vg = alloc_vg("pool_orphan", cmd, fmt->orphan_vg_name))) { |
|
+ log_error("Couldn't create pool orphan VG."); |
|
+ dm_free(fmt); |
|
+ return NULL; |
|
+ } |
|
+ |
|
+ fic.type = FMT_INSTANCE_AUX_MDAS; |
|
+ fic.context.vg_ref.vg_name = fmt->orphan_vg_name; |
|
+ fic.context.vg_ref.vg_id = NULL; |
|
+ |
|
+ if (!(fid = _pool_create_instance(fmt, &fic))) { |
|
+ _pool_destroy(fmt); |
|
+ return NULL; |
|
+ } |
|
+ |
|
+ vg_set_fid(fmt->orphan_vg, fid); |
|
+ |
|
+ log_very_verbose("Initialised format: %s", fmt->name); |
|
+ |
|
+ return fmt; |
|
+} |
|
diff --git a/lib/format_pool/format_pool.h b/lib/format_pool/format_pool.h |
|
new file mode 100644 |
|
index 0000000..8ad7eb5 |
|
--- /dev/null |
|
+++ b/lib/format_pool/format_pool.h |
|
@@ -0,0 +1,28 @@ |
|
+/* |
|
+ * Copyright (C) 2003-2004 Sistina Software, Inc. All rights reserved. |
|
+ * Copyright (C) 2004 Red Hat, Inc. All rights reserved. |
|
+ * |
|
+ * This file is part of LVM2. |
|
+ * |
|
+ * This copyrighted material is made available to anyone wishing to use, |
|
+ * modify, copy, or redistribute it subject to the terms and conditions |
|
+ * of the GNU Lesser General Public License v.2.1. |
|
+ * |
|
+ * You should have received a copy of the GNU Lesser General Public License |
|
+ * along with this program; if not, write to the Free Software Foundation, |
|
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
|
+ */ |
|
+ |
|
+#ifndef _LVM_FORMAT_POOL_H |
|
+#define _LVM_FORMAT_POOL_H |
|
+ |
|
+#include "metadata.h" |
|
+ |
|
+#define FMT_POOL_NAME "pool" |
|
+#define FMT_POOL_ORPHAN_VG_NAME ORPHAN_VG_NAME(FMT_POOL_NAME) |
|
+ |
|
+#ifdef POOL_INTERNAL |
|
+struct format_type *init_pool_format(struct cmd_context *cmd); |
|
+#endif |
|
+ |
|
+#endif |
|
diff --git a/lib/format_pool/import_export.c b/lib/format_pool/import_export.c |
|
new file mode 100644 |
|
index 0000000..f4097a7 |
|
--- /dev/null |
|
+++ b/lib/format_pool/import_export.c |
|
@@ -0,0 +1,285 @@ |
|
+/* |
|
+ * Copyright (C) 1997-2004 Sistina Software, Inc. All rights reserved. |
|
+ * Copyright (C) 2004-2017 Red Hat, Inc. All rights reserved. |
|
+ * |
|
+ * This file is part of LVM2. |
|
+ * |
|
+ * This copyrighted material is made available to anyone wishing to use, |
|
+ * modify, copy, or redistribute it subject to the terms and conditions |
|
+ * of the GNU Lesser General Public License v.2.1. |
|
+ * |
|
+ * You should have received a copy of the GNU Lesser General Public License |
|
+ * along with this program; if not, write to the Free Software Foundation, |
|
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
|
+ */ |
|
+ |
|
+#include "lib.h" |
|
+#include "label.h" |
|
+#include "metadata.h" |
|
+#include "disk_rep.h" |
|
+#include "sptype_names.h" |
|
+#include "lv_alloc.h" |
|
+#include "pv_alloc.h" |
|
+#include "str_list.h" |
|
+#include "display.h" |
|
+#include "segtype.h" |
|
+#include "toolcontext.h" |
|
+ |
|
+/* This file contains only imports at the moment... */ |
|
+ |
|
+int import_pool_vg(struct volume_group *vg, struct dm_pool *mem, struct dm_list *pls) |
|
+{ |
|
+ struct pool_list *pl; |
|
+ |
|
+ dm_list_iterate_items(pl, pls) { |
|
+ vg->extent_count += |
|
+ ((pl->pd.pl_blocks) / POOL_PE_SIZE); |
|
+ |
|
+ vg->free_count = vg->extent_count; |
|
+ |
|
+ if (vg->name) |
|
+ continue; |
|
+ |
|
+ vg->name = dm_pool_strdup(mem, pl->pd.pl_pool_name); |
|
+ get_pool_vg_uuid(&vg->id, &pl->pd); |
|
+ vg->extent_size = POOL_PE_SIZE; |
|
+ vg->status |= LVM_READ | LVM_WRITE | CLUSTERED | SHARED; |
|
+ vg->max_lv = 1; |
|
+ vg->max_pv = POOL_MAX_DEVICES; |
|
+ vg->alloc = ALLOC_NORMAL; |
|
+ } |
|
+ |
|
+ return 1; |
|
+} |
|
+ |
|
+int import_pool_lvs(struct volume_group *vg, struct dm_pool *mem, struct dm_list *pls) |
|
+{ |
|
+ struct pool_list *pl; |
|
+ struct logical_volume *lv; |
|
+ |
|
+ if (!(lv = alloc_lv(mem))) |
|
+ return_0; |
|
+ |
|
+ lv->status = 0; |
|
+ lv->alloc = ALLOC_NORMAL; |
|
+ lv->size = 0; |
|
+ lv->name = NULL; |
|
+ lv->le_count = 0; |
|
+ lv->read_ahead = vg->cmd->default_settings.read_ahead; |
|
+ |
|
+ dm_list_iterate_items(pl, pls) { |
|
+ lv->size += pl->pd.pl_blocks; |
|
+ |
|
+ if (lv->name) |
|
+ continue; |
|
+ |
|
+ if (!(lv->name = dm_pool_strdup(mem, pl->pd.pl_pool_name))) |
|
+ return_0; |
|
+ |
|
+ get_pool_lv_uuid(lv->lvid.id, &pl->pd); |
|
+ log_debug_metadata("Calculated lv uuid for lv %s: %s", lv->name, |
|
+ lv->lvid.s); |
|
+ |
|
+ lv->status |= VISIBLE_LV | LVM_READ | LVM_WRITE; |
|
+ lv->major = POOL_MAJOR; |
|
+ |
|
+ /* for pool a minor of 0 is dynamic */ |
|
+ if (pl->pd.pl_minor) { |
|
+ lv->status |= FIXED_MINOR; |
|
+ lv->minor = pl->pd.pl_minor + MINOR_OFFSET; |
|
+ } else { |
|
+ lv->minor = -1; |
|
+ } |
|
+ } |
|
+ |
|
+ lv->le_count = lv->size / POOL_PE_SIZE; |
|
+ |
|
+ return link_lv_to_vg(vg, lv); |
|
+} |
|
+ |
|
+int import_pool_pvs(const struct format_type *fmt, struct volume_group *vg, |
|
+ struct dm_pool *mem, struct dm_list *pls) |
|
+{ |
|
+ struct pv_list *pvl; |
|
+ struct pool_list *pl; |
|
+ |
|
+ dm_list_iterate_items(pl, pls) { |
|
+ if (!(pvl = dm_pool_zalloc(mem, sizeof(*pvl)))) { |
|
+ log_error("Unable to allocate pv list structure"); |
|
+ return 0; |
|
+ } |
|
+ if (!(pvl->pv = dm_pool_zalloc(mem, sizeof(*pvl->pv)))) { |
|
+ log_error("Unable to allocate pv structure"); |
|
+ return 0; |
|
+ } |
|
+ if (!import_pool_pv(fmt, mem, vg, pvl->pv, pl)) { |
|
+ return 0; |
|
+ } |
|
+ pl->pv = pvl->pv; |
|
+ pvl->mdas = NULL; |
|
+ pvl->pe_ranges = NULL; |
|
+ add_pvl_to_vgs(vg, pvl); |
|
+ } |
|
+ |
|
+ return 1; |
|
+} |
|
+ |
|
+int import_pool_pv(const struct format_type *fmt, struct dm_pool *mem, |
|
+ struct volume_group *vg, struct physical_volume *pv, |
|
+ struct pool_list *pl) |
|
+{ |
|
+ struct pool_disk *pd = &pl->pd; |
|
+ |
|
+ memset(pv, 0, sizeof(*pv)); |
|
+ |
|
+ get_pool_pv_uuid(&pv->id, pd); |
|
+ pv->fmt = fmt; |
|
+ |
|
+ pv->dev = pl->dev; |
|
+ if (!(pv->vg_name = dm_pool_strdup(mem, pd->pl_pool_name))) { |
|
+ log_error("Unable to duplicate vg_name string"); |
|
+ return 0; |
|
+ } |
|
+ if (vg != NULL) |
|
+ memcpy(&pv->vgid, &vg->id, sizeof(vg->id)); |
|
+ pv->status = 0; |
|
+ pv->size = pd->pl_blocks; |
|
+ pv->pe_size = POOL_PE_SIZE; |
|
+ pv->pe_start = POOL_PE_START; |
|
+ pv->pe_count = pv->size / POOL_PE_SIZE; |
|
+ pv->pe_alloc_count = 0; |
|
+ pv->pe_align = 0; |
|
+ |
|
+ dm_list_init(&pv->tags); |
|
+ dm_list_init(&pv->segments); |
|
+ |
|
+ if (!alloc_pv_segment_whole_pv(mem, pv)) |
|
+ return_0; |
|
+ |
|
+ return 1; |
|
+} |
|
+ |
|
+static const char *_cvt_sptype(uint32_t sptype) |
|
+{ |
|
+ int i; |
|
+ for (i = 0; sptype_names[i].name[0]; i++) { |
|
+ if (sptype == sptype_names[i].label) { |
|
+ break; |
|
+ } |
|
+ } |
|
+ log_debug_metadata("Found sptype %X and converted it to %s", |
|
+ sptype, sptype_names[i].name); |
|
+ return sptype_names[i].name; |
|
+} |
|
+ |
|
+static int _add_stripe_seg(struct dm_pool *mem, |
|
+ struct user_subpool *usp, struct logical_volume *lv, |
|
+ uint32_t *le_cur) |
|
+{ |
|
+ struct lv_segment *seg; |
|
+ struct segment_type *segtype; |
|
+ unsigned j; |
|
+ uint32_t area_len; |
|
+ |
|
+ if (!is_power_of_2(usp->striping)) { |
|
+ log_error("Stripe size must be a power of 2"); |
|
+ return 0; |
|
+ } |
|
+ |
|
+ area_len = (usp->devs[0].blocks) / POOL_PE_SIZE; |
|
+ |
|
+ if (!(segtype = get_segtype_from_string(lv->vg->cmd, SEG_TYPE_NAME_STRIPED))) |
|
+ return_0; |
|
+ |
|
+ if (!(seg = alloc_lv_segment(segtype, lv, *le_cur, |
|
+ area_len * usp->num_devs, 0, 0, |
|
+ usp->striping, NULL, usp->num_devs, |
|
+ area_len, 0, 0, 0, 0, NULL))) { |
|
+ log_error("Unable to allocate striped lv_segment structure"); |
|
+ return 0; |
|
+ } |
|
+ |
|
+ for (j = 0; j < usp->num_devs; j++) |
|
+ if (!set_lv_segment_area_pv(seg, j, usp->devs[j].pv, 0)) |
|
+ return_0; |
|
+ |
|
+ /* add the subpool type to the segment tag list */ |
|
+ if (!str_list_add(mem, &seg->tags, _cvt_sptype(usp->type))) { |
|
+ log_error("Allocation failed for str_list."); |
|
+ return 0; |
|
+ } |
|
+ |
|
+ dm_list_add(&lv->segments, &seg->list); |
|
+ |
|
+ *le_cur += seg->len; |
|
+ |
|
+ return 1; |
|
+} |
|
+ |
|
+static int _add_linear_seg(struct dm_pool *mem, |
|
+ struct user_subpool *usp, struct logical_volume *lv, |
|
+ uint32_t *le_cur) |
|
+{ |
|
+ struct lv_segment *seg; |
|
+ struct segment_type *segtype; |
|
+ unsigned j; |
|
+ uint32_t area_len; |
|
+ |
|
+ if (!(segtype = get_segtype_from_string(lv->vg->cmd, SEG_TYPE_NAME_STRIPED))) |
|
+ return_0; |
|
+ |
|
+ for (j = 0; j < usp->num_devs; j++) { |
|
+ area_len = (usp->devs[j].blocks) / POOL_PE_SIZE; |
|
+ |
|
+ if (!(seg = alloc_lv_segment(segtype, lv, *le_cur, |
|
+ area_len, 0, 0, usp->striping, |
|
+ NULL, 1, area_len, 0, |
|
+ POOL_PE_SIZE, 0, 0, NULL))) { |
|
+ log_error("Unable to allocate linear lv_segment " |
|
+ "structure"); |
|
+ return 0; |
|
+ } |
|
+ |
|
+ /* add the subpool type to the segment tag list */ |
|
+ if (!str_list_add(mem, &seg->tags, _cvt_sptype(usp->type))) { |
|
+ log_error("Allocation failed for str_list."); |
|
+ return 0; |
|
+ } |
|
+ |
|
+ if (!set_lv_segment_area_pv(seg, 0, usp->devs[j].pv, 0)) |
|
+ return_0; |
|
+ dm_list_add(&lv->segments, &seg->list); |
|
+ |
|
+ *le_cur += seg->len; |
|
+ } |
|
+ |
|
+ return 1; |
|
+} |
|
+ |
|
+int import_pool_segments(struct dm_list *lvs, struct dm_pool *mem, |
|
+ struct user_subpool *usp, int subpools) |
|
+{ |
|
+ struct lv_list *lvl; |
|
+ struct logical_volume *lv; |
|
+ uint32_t le_cur = 0; |
|
+ int i; |
|
+ |
|
+ dm_list_iterate_items(lvl, lvs) { |
|
+ lv = lvl->lv; |
|
+ |
|
+ if (lv_is_snapshot(lv)) |
|
+ continue; |
|
+ |
|
+ for (i = 0; i < subpools; i++) { |
|
+ if (usp[i].striping) { |
|
+ if (!_add_stripe_seg(mem, &usp[i], lv, &le_cur)) |
|
+ return_0; |
|
+ } else { |
|
+ if (!_add_linear_seg(mem, &usp[i], lv, &le_cur)) |
|
+ return_0; |
|
+ } |
|
+ } |
|
+ } |
|
+ |
|
+ return 1; |
|
+} |
|
diff --git a/lib/format_pool/pool_label.c b/lib/format_pool/pool_label.c |
|
new file mode 100644 |
|
index 0000000..2e30a7b |
|
--- /dev/null |
|
+++ b/lib/format_pool/pool_label.c |
|
@@ -0,0 +1,104 @@ |
|
+/* |
|
+ * Copyright (C) 1997-2004 Sistina Software, Inc. All rights reserved. |
|
+ * Copyright (C) 2004-2006 Red Hat, Inc. All rights reserved. |
|
+ * |
|
+ * This file is part of LVM2. |
|
+ * |
|
+ * This copyrighted material is made available to anyone wishing to use, |
|
+ * modify, copy, or redistribute it subject to the terms and conditions |
|
+ * of the GNU Lesser General Public License v.2.1. |
|
+ * |
|
+ * You should have received a copy of the GNU Lesser General Public License |
|
+ * along with this program; if not, write to the Free Software Foundation, |
|
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
|
+ */ |
|
+ |
|
+#include "lib.h" |
|
+#include "label.h" |
|
+#include "metadata.h" |
|
+#include "disk_rep.h" |
|
+#include "pool_label.h" |
|
+ |
|
+#include <sys/stat.h> |
|
+#include <fcntl.h> |
|
+ |
|
+static void _pool_not_supported(const char *op) |
|
+{ |
|
+ log_error("The '%s' operation is not supported for the pool labeller.", |
|
+ op); |
|
+} |
|
+ |
|
+static int _pool_can_handle(struct labeller *l __attribute__((unused)), void *buf, uint64_t sector) |
|
+{ |
|
+ |
|
+ struct pool_disk pd; |
|
+ |
|
+ /* |
|
+ * POOL label must always be in first sector |
|
+ */ |
|
+ if (sector) |
|
+ return 0; |
|
+ |
|
+ pool_label_in(&pd, buf); |
|
+ |
|
+ /* can ignore 8 rightmost bits for ondisk format check */ |
|
+ if ((pd.pl_magic == POOL_MAGIC) && |
|
+ (pd.pl_version >> 8 == POOL_VERSION >> 8)) |
|
+ return 1; |
|
+ |
|
+ return 0; |
|
+} |
|
+ |
|
+static int _pool_write(struct label *label __attribute__((unused)), void *buf __attribute__((unused))) |
|
+{ |
|
+ _pool_not_supported("write"); |
|
+ return 0; |
|
+} |
|
+ |
|
+static int _pool_read(struct labeller *l, struct device *dev, void *buf, |
|
+ struct label **label) |
|
+{ |
|
+ struct pool_list pl; |
|
+ |
|
+ return read_pool_label(&pl, l, dev, buf, label); |
|
+} |
|
+ |
|
+static int _pool_initialise_label(struct labeller *l __attribute__((unused)), struct label *label) |
|
+{ |
|
+ strcpy(label->type, "POOL"); |
|
+ |
|
+ return 1; |
|
+} |
|
+ |
|
+static void _pool_destroy_label(struct labeller *l __attribute__((unused)), struct label *label __attribute__((unused))) |
|
+{ |
|
+} |
|
+ |
|
+static void _label_pool_destroy(struct labeller *l) |
|
+{ |
|
+ dm_free(l); |
|
+} |
|
+ |
|
+struct label_ops _pool_ops = { |
|
+ .can_handle = _pool_can_handle, |
|
+ .write = _pool_write, |
|
+ .read = _pool_read, |
|
+ .initialise_label = _pool_initialise_label, |
|
+ .destroy_label = _pool_destroy_label, |
|
+ .destroy = _label_pool_destroy, |
|
+}; |
|
+ |
|
+struct labeller *pool_labeller_create(struct format_type *fmt) |
|
+{ |
|
+ struct labeller *l; |
|
+ |
|
+ if (!(l = dm_malloc(sizeof(*l)))) { |
|
+ log_error("Couldn't allocate labeller object."); |
|
+ return NULL; |
|
+ } |
|
+ |
|
+ l->ops = &_pool_ops; |
|
+ l->fmt = fmt; |
|
+ |
|
+ return l; |
|
+} |
|
diff --git a/lib/format_pool/pool_label.h b/lib/format_pool/pool_label.h |
|
new file mode 100644 |
|
index 0000000..1a529a4 |
|
--- /dev/null |
|
+++ b/lib/format_pool/pool_label.h |
|
@@ -0,0 +1,23 @@ |
|
+/* |
|
+ * Copyright (C) 1997-2004 Sistina Software, Inc. All rights reserved. |
|
+ * Copyright (C) 2004 Red Hat, Inc. All rights reserved. |
|
+ * |
|
+ * This file is part of LVM2. |
|
+ * |
|
+ * This copyrighted material is made available to anyone wishing to use, |
|
+ * modify, copy, or redistribute it subject to the terms and conditions |
|
+ * of the GNU Lesser General Public License v.2.1. |
|
+ * |
|
+ * You should have received a copy of the GNU Lesser General Public License |
|
+ * along with this program; if not, write to the Free Software Foundation, |
|
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
|
+ */ |
|
+ |
|
+#ifndef _LVM_POOL_LABEL_H |
|
+#define _LVM_POOL_LABEL_H |
|
+ |
|
+#include "metadata.h" |
|
+ |
|
+struct labeller *pool_labeller_create(struct format_type *fmt); |
|
+ |
|
+#endif |
|
diff --git a/lib/format_pool/sptype_names.h b/lib/format_pool/sptype_names.h |
|
new file mode 100644 |
|
index 0000000..5812adb |
|
--- /dev/null |
|
+++ b/lib/format_pool/sptype_names.h |
|
@@ -0,0 +1,42 @@ |
|
+/* |
|
+ * Copyright (C) 1997-2004 Sistina Software, Inc. All rights reserved. |
|
+ * Copyright (C) 2004-2005 Red Hat, Inc. All rights reserved. |
|
+ * |
|
+ * This file is part of LVM2. |
|
+ * |
|
+ * This copyrighted material is made available to anyone wishing to use, |
|
+ * modify, copy, or redistribute it subject to the terms and conditions |
|
+ * of the GNU Lesser General Public License v.2.1. |
|
+ * |
|
+ * You should have received a copy of the GNU Lesser General Public License |
|
+ * along with this program; if not, write to the Free Software Foundation, |
|
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
|
+ */ |
|
+ |
|
+#ifndef SPTYPE_NAMES_H |
|
+#define SPTYPE_NAMES_H |
|
+ |
|
+/* This must be kept up to date with sistina/pool/module/pool_sptypes.h */ |
|
+ |
|
+/* Generic Labels */ |
|
+#define SPTYPE_DATA (0x00000000) |
|
+ |
|
+/* GFS specific labels */ |
|
+#define SPTYPE_GFS_DATA (0x68011670) |
|
+#define SPTYPE_GFS_JOURNAL (0x69011670) |
|
+ |
|
+struct sptype_name { |
|
+ const char *name; |
|
+ uint32_t label; |
|
+}; |
|
+ |
|
+static const struct sptype_name sptype_names[] = { |
|
+ {"data", SPTYPE_DATA}, |
|
+ |
|
+ {"gfs_data", SPTYPE_GFS_DATA}, |
|
+ {"gfs_journal", SPTYPE_GFS_JOURNAL}, |
|
+ |
|
+ {"", 0x0} /* This must be the last flag. */ |
|
+}; |
|
+ |
|
+#endif |
|
diff --git a/lib/format_text/export.c b/lib/format_text/export.c |
|
index 7866d56..e535237 100644 |
|
--- a/lib/format_text/export.c |
|
+++ b/lib/format_text/export.c |
|
@@ -467,6 +467,8 @@ static int _print_vg(struct formatter *f, struct volume_group *vg) |
|
|
|
if (vg->system_id && *vg->system_id) |
|
outf(f, "system_id = \"%s\"", vg->system_id); |
|
+ else if (vg->lvm1_system_id && *vg->lvm1_system_id) |
|
+ outf(f, "system_id = \"%s\"", vg->lvm1_system_id); |
|
|
|
if (vg->lock_type) { |
|
outf(f, "lock_type = \"%s\"", vg->lock_type); |
|
diff --git a/lib/format_text/format-text.c b/lib/format_text/format-text.c |
|
index 5c7b72f..792d75a 100644 |
|
--- a/lib/format_text/format-text.c |
|
+++ b/lib/format_text/format-text.c |
|
@@ -2545,9 +2545,9 @@ struct format_type *create_text_format(struct cmd_context *cmd) |
|
fmt->name = FMT_TEXT_NAME; |
|
fmt->alias = FMT_TEXT_ALIAS; |
|
fmt->orphan_vg_name = ORPHAN_VG_NAME(FMT_TEXT_NAME); |
|
- fmt->features = FMT_SEGMENTS | FMT_TAGS | FMT_PRECOMMIT | |
|
+ fmt->features = FMT_SEGMENTS | FMT_MDAS | FMT_TAGS | FMT_PRECOMMIT | |
|
FMT_UNLIMITED_VOLS | FMT_RESIZE_PV | |
|
- FMT_UNLIMITED_STRIPESIZE | FMT_CONFIG_PROFILE | |
|
+ FMT_UNLIMITED_STRIPESIZE | FMT_BAS | FMT_CONFIG_PROFILE | |
|
FMT_NON_POWER2_EXTENTS | FMT_PV_FLAGS; |
|
|
|
if (!(mda_lists = dm_malloc(sizeof(struct mda_lists)))) { |
|
diff --git a/lib/format_text/import_vsn1.c b/lib/format_text/import_vsn1.c |
|
index 58f517e..e038a27 100644 |
|
--- a/lib/format_text/import_vsn1.c |
|
+++ b/lib/format_text/import_vsn1.c |
|
@@ -1084,8 +1084,15 @@ static struct volume_group *_read_vg(struct format_instance *fid, |
|
goto bad; |
|
} |
|
|
|
+ /* |
|
+ * A system id without WRITE_LOCKED is an old lvm1 system id. |
|
+ */ |
|
if (dm_config_get_str(vgn, "system_id", &system_id)) { |
|
- if (!(vg->system_id = dm_pool_strdup(vg->vgmem, system_id))) { |
|
+ if (!(vgstatus & LVM_WRITE_LOCKED)) { |
|
+ if (!(vg->lvm1_system_id = dm_pool_zalloc(vg->vgmem, NAME_LEN + 1))) |
|
+ goto_bad; |
|
+ strncpy(vg->lvm1_system_id, system_id, NAME_LEN); |
|
+ } else if (!(vg->system_id = dm_pool_strdup(vg->vgmem, system_id))) { |
|
log_error("Failed to allocate memory for system_id in _read_vg."); |
|
goto bad; |
|
} |
|
diff --git a/lib/locking/locking.c b/lib/locking/locking.c |
|
index 2584227..2b53553 100644 |
|
--- a/lib/locking/locking.c |
|
+++ b/lib/locking/locking.c |
|
@@ -221,6 +221,42 @@ void fin_locking(void) |
|
} |
|
|
|
/* |
|
+ * Does the LVM1 driver know of this VG name? |
|
+ */ |
|
+int check_lvm1_vg_inactive(struct cmd_context *cmd, const char *vgname) |
|
+{ |
|
+ struct stat info; |
|
+ char path[PATH_MAX]; |
|
+ |
|
+ /* We'll allow operations on orphans */ |
|
+ if (!is_real_vg(vgname)) |
|
+ return 1; |
|
+ |
|
+ /* LVM1 is only present in 2.4 kernels. */ |
|
+ if (strncmp(cmd->kernel_vsn, "2.4.", 4)) |
|
+ return 1; |
|
+ |
|
+ if (dm_snprintf(path, sizeof(path), "%s/lvm/VGs/%s", cmd->proc_dir, |
|
+ vgname) < 0) { |
|
+ log_error("LVM1 proc VG pathname too long for %s", vgname); |
|
+ return 0; |
|
+ } |
|
+ |
|
+ if (stat(path, &info) == 0) { |
|
+ log_error("%s exists: Is the original LVM driver using " |
|
+ "this volume group?", path); |
|
+ return 0; |
|
+ } |
|
+ |
|
+ if (errno != ENOENT && errno != ENOTDIR) { |
|
+ log_sys_error("stat", path); |
|
+ return 0; |
|
+ } |
|
+ |
|
+ return 1; |
|
+} |
|
+ |
|
+/* |
|
* VG locking is by VG name. |
|
* FIXME This should become VG uuid. |
|
*/ |
|
@@ -332,6 +368,10 @@ int lock_vol(struct cmd_context *cmd, const char *vol, uint32_t flags, const str |
|
lvmcache_drop_metadata(vol, 0); |
|
} |
|
|
|
+ /* Lock VG to change on-disk metadata. */ |
|
+ /* If LVM1 driver knows about the VG, it can't be accessed. */ |
|
+ if (!check_lvm1_vg_inactive(cmd, vol)) |
|
+ return_0; |
|
break; |
|
case LCK_LV: |
|
/* All LV locks are non-blocking. */ |
|
diff --git a/lib/metadata/lv_manip.c b/lib/metadata/lv_manip.c |
|
index e4293cc..1c6dc62 100644 |
|
--- a/lib/metadata/lv_manip.c |
|
+++ b/lib/metadata/lv_manip.c |
|
@@ -5981,6 +5981,8 @@ int lv_remove_single(struct cmd_context *cmd, struct logical_volume *lv, |
|
force_t force, int suppress_remove_message) |
|
{ |
|
struct volume_group *vg; |
|
+ struct logical_volume *format1_origin = NULL; |
|
+ int format1_reload_required = 0; |
|
int visible, historical; |
|
struct logical_volume *pool_lv = NULL; |
|
struct logical_volume *lock_lv = lv; |
|
@@ -6133,6 +6135,10 @@ int lv_remove_single(struct cmd_context *cmd, struct logical_volume *lv, |
|
} |
|
|
|
if (lv_is_cow(lv)) { |
|
+ /* Old format1 code */ |
|
+ if (!(lv->vg->fid->fmt->features & FMT_MDAS)) |
|
+ format1_origin = origin_from_cow(lv); |
|
+ |
|
log_verbose("Removing snapshot volume %s.", display_lvname(lv)); |
|
/* vg_remove_snapshot() will preload origin/former snapshots */ |
|
if (!vg_remove_snapshot(lv)) |
|
@@ -6188,10 +6194,30 @@ int lv_remove_single(struct cmd_context *cmd, struct logical_volume *lv, |
|
} |
|
} |
|
|
|
+ /* |
|
+ * Old format1 code: If no snapshots left reload without -real. |
|
+ */ |
|
+ if (format1_origin && !lv_is_origin(format1_origin)) { |
|
+ log_warn("WARNING: Support for snapshots with old LVM1-style metadata is deprecated."); |
|
+ log_warn("WARNING: Please use lvconvert to update to lvm2 metadata at your convenience."); |
|
+ format1_reload_required = 1; |
|
+ } |
|
+ |
|
/* store it on disks */ |
|
if (!vg_write(vg) || !vg_commit(vg)) |
|
return_0; |
|
|
|
+ /* format1 */ |
|
+ if (format1_reload_required) { |
|
+ if (!suspend_lv(cmd, format1_origin)) |
|
+ log_error("Failed to refresh %s without snapshot.", format1_origin->name); |
|
+ |
|
+ if (!resume_lv(cmd, format1_origin)) { |
|
+ log_error("Failed to resume %s.", format1_origin->name); |
|
+ return 0; |
|
+ } |
|
+ } |
|
+ |
|
/* Release unneeded blocks in thin pool */ |
|
/* TODO: defer when multiple LVs relased at once */ |
|
if (pool_lv && !update_pool_lv(pool_lv, 1)) { |
|
diff --git a/lib/metadata/metadata-exported.h b/lib/metadata/metadata-exported.h |
|
index f4fb112..377644b 100644 |
|
--- a/lib/metadata/metadata-exported.h |
|
+++ b/lib/metadata/metadata-exported.h |
|
@@ -149,7 +149,7 @@ |
|
|
|
/* Format features flags */ |
|
#define FMT_SEGMENTS 0x00000001U /* Arbitrary segment params? */ |
|
-// #define FMT_MDAS 0x00000002U /* Proper metadata areas? */ |
|
+#define FMT_MDAS 0x00000002U /* Proper metadata areas? */ |
|
#define FMT_TAGS 0x00000004U /* Tagging? */ |
|
#define FMT_UNLIMITED_VOLS 0x00000008U /* Unlimited PVs/LVs? */ |
|
#define FMT_RESTRICTED_LVIDS 0x00000010U /* LVID <= 255 */ |
|
@@ -158,13 +158,15 @@ |
|
#define FMT_RESIZE_PV 0x00000080U /* Supports pvresize? */ |
|
#define FMT_UNLIMITED_STRIPESIZE 0x00000100U /* Unlimited stripe size? */ |
|
#define FMT_RESTRICTED_READAHEAD 0x00000200U /* Readahead restricted to 2-120? */ |
|
-// #define FMT_BAS 0x000000400U /* Supports bootloader areas? */ |
|
+#define FMT_BAS 0x000000400U /* Supports bootloader areas? */ |
|
#define FMT_CONFIG_PROFILE 0x000000800U /* Supports configuration profiles? */ |
|
-// #define FMT_OBSOLETE 0x000001000U /* Obsolete format? */ |
|
+#define FMT_OBSOLETE 0x000001000U /* Obsolete format? */ |
|
#define FMT_NON_POWER2_EXTENTS 0x000002000U /* Non-power-of-2 extent sizes? */ |
|
-// #define FMT_SYSTEMID_ON_PVS 0x000004000U /* System ID is stored on PVs not VG */ |
|
+#define FMT_SYSTEMID_ON_PVS 0x000004000U /* System ID is stored on PVs not VG */ |
|
#define FMT_PV_FLAGS 0x000008000U /* Supports PV flags */ |
|
|
|
+#define systemid_on_pvs(vg) ((vg)->fid->fmt->features & FMT_SYSTEMID_ON_PVS) |
|
+ |
|
/* Mirror conversion type flags */ |
|
#define MIRROR_BY_SEG 0x00000001U /* segment-by-segment mirror */ |
|
#define MIRROR_BY_LV 0x00000002U /* mirror using whole mimage LVs */ |
|
diff --git a/lib/metadata/metadata.c b/lib/metadata/metadata.c |
|
index 2292568..e33a779 100644 |
|
--- a/lib/metadata/metadata.c |
|
+++ b/lib/metadata/metadata.c |
|
@@ -1011,6 +1011,8 @@ struct volume_group *vg_create(struct cmd_context *cmd, const char *vg_name) |
|
|
|
vg->status = (RESIZEABLE_VG | LVM_READ | LVM_WRITE); |
|
vg->system_id = NULL; |
|
+ if (!(vg->lvm1_system_id = dm_pool_zalloc(vg->vgmem, NAME_LEN + 1))) |
|
+ goto_bad; |
|
|
|
vg->extent_size = DEFAULT_EXTENT_SIZE * 2; |
|
vg->max_lv = DEFAULT_MAX_LV; |
|
@@ -2971,7 +2973,7 @@ int vg_write(struct volume_group *vg) |
|
return 0; |
|
} |
|
|
|
- if (!_vg_adjust_ignored_mdas(vg)) |
|
+ if ((vg->fid->fmt->features & FMT_MDAS) && !_vg_adjust_ignored_mdas(vg)) |
|
return_0; |
|
|
|
if (!vg_mda_used_count(vg)) { |
|
@@ -5375,6 +5377,15 @@ int is_system_id_allowed(struct cmd_context *cmd, const char *system_id) |
|
static int _access_vg_systemid(struct cmd_context *cmd, struct volume_group *vg) |
|
{ |
|
/* |
|
+ * LVM1 VGs must not be accessed if a new-style LVM2 system ID is set. |
|
+ */ |
|
+ if (cmd->system_id && systemid_on_pvs(vg)) { |
|
+ log_error("Cannot access VG %s with LVM1 system ID %s when host system ID is set.", |
|
+ vg->name, vg->lvm1_system_id); |
|
+ return 0; |
|
+ } |
|
+ |
|
+ /* |
|
* A few commands allow read-only access to foreign VGs. |
|
*/ |
|
if (cmd->include_foreign_vgs) |
|
@@ -5426,6 +5437,11 @@ static int _vg_access_permitted(struct cmd_context *cmd, struct volume_group *vg |
|
uint32_t lockd_state, uint32_t *failure) |
|
{ |
|
if (!is_real_vg(vg->name)) { |
|
+ /* Disallow use of LVM1 orphans when a host system ID is set. */ |
|
+ if (cmd->system_id && *cmd->system_id && systemid_on_pvs(vg)) { |
|
+ *failure |= FAILED_SYSTEMID; |
|
+ return_0; |
|
+ } |
|
return 1; |
|
} |
|
|
|
diff --git a/lib/metadata/segtype.h b/lib/metadata/segtype.h |
|
index 9c05836..309a246 100644 |
|
--- a/lib/metadata/segtype.h |
|
+++ b/lib/metadata/segtype.h |
|
@@ -32,7 +32,7 @@ struct dev_manager; |
|
#define SEG_AREAS_STRIPED (1ULL << 1) |
|
#define SEG_AREAS_MIRRORED (1ULL << 2) |
|
#define SEG_SNAPSHOT (1ULL << 3) |
|
-/* #define SEG_FORMAT1_SUPPORT (1ULL << 4) */ |
|
+#define SEG_FORMAT1_SUPPORT (1ULL << 4) |
|
#define SEG_VIRTUAL (1ULL << 5) |
|
#define SEG_CANNOT_BE_ZEROED (1ULL << 6) |
|
#define SEG_MONITORED (1ULL << 7) |
|
diff --git a/lib/metadata/snapshot_manip.c b/lib/metadata/snapshot_manip.c |
|
index 76d78f3..8357ea0 100644 |
|
--- a/lib/metadata/snapshot_manip.c |
|
+++ b/lib/metadata/snapshot_manip.c |
|
@@ -332,6 +332,17 @@ int vg_remove_snapshot(struct logical_volume *cow) |
|
cow->snapshot = NULL; |
|
lv_set_visible(cow); |
|
|
|
+ /* format1 must do the change in one step, with the commit last. */ |
|
+ if (!(origin->vg->fid->fmt->features & FMT_MDAS)) { |
|
+ /* Get the lock for COW volume */ |
|
+ if (is_origin_active && !activate_lv(cow->vg->cmd, cow)) { |
|
+ log_error("Unable to activate logical volume \"%s\"", |
|
+ cow->name); |
|
+ return 0; |
|
+ } |
|
+ return 1; |
|
+ } |
|
+ |
|
if (!vg_write(origin->vg)) |
|
return_0; |
|
|
|
diff --git a/lib/metadata/vg.c b/lib/metadata/vg.c |
|
index b8b1501..1020a67 100644 |
|
--- a/lib/metadata/vg.c |
|
+++ b/lib/metadata/vg.c |
|
@@ -42,6 +42,12 @@ struct volume_group *alloc_vg(const char *pool_name, struct cmd_context *cmd, |
|
return NULL; |
|
} |
|
|
|
+ if (!(vg->lvm1_system_id = dm_pool_zalloc(vgmem, NAME_LEN + 1))) { |
|
+ log_error("Failed to allocate VG systemd id."); |
|
+ dm_pool_destroy(vgmem); |
|
+ return NULL; |
|
+ } |
|
+ |
|
vg->system_id = ""; |
|
|
|
vg->cmd = cmd; |
|
@@ -172,7 +178,7 @@ char *vg_name_dup(const struct volume_group *vg) |
|
|
|
char *vg_system_id_dup(const struct volume_group *vg) |
|
{ |
|
- return dm_pool_strdup(vg->vgmem, vg->system_id ? : ""); |
|
+ return dm_pool_strdup(vg->vgmem, vg->system_id ? : vg->lvm1_system_id ? : ""); |
|
} |
|
|
|
char *vg_lock_type_dup(const struct volume_group *vg) |
|
@@ -671,11 +677,20 @@ int vg_set_system_id(struct volume_group *vg, const char *system_id) |
|
return 1; |
|
} |
|
|
|
+ if (systemid_on_pvs(vg)) { |
|
+ log_error("Metadata format %s does not support this type of system ID.", |
|
+ vg->fid->fmt->name); |
|
+ return 0; |
|
+ } |
|
+ |
|
if (!(vg->system_id = dm_pool_strdup(vg->vgmem, system_id))) { |
|
log_error("Failed to allocate memory for system_id in vg_set_system_id."); |
|
return 0; |
|
} |
|
|
|
+ if (vg->lvm1_system_id) |
|
+ *vg->lvm1_system_id = '\0'; |
|
+ |
|
return 1; |
|
} |
|
|
|
diff --git a/lib/metadata/vg.h b/lib/metadata/vg.h |
|
index 7ecfafe..29d88f8 100644 |
|
--- a/lib/metadata/vg.h |
|
+++ b/lib/metadata/vg.h |
|
@@ -70,6 +70,7 @@ struct volume_group { |
|
const char *name; |
|
const char *old_name; /* Set during vgrename and vgcfgrestore */ |
|
const char *system_id; |
|
+ char *lvm1_system_id; |
|
const char *lock_type; |
|
const char *lock_args; |
|
|
|
diff --git a/lib/report/report.c b/lib/report/report.c |
|
index 19f0f5c..8905d26 100644 |
|
--- a/lib/report/report.c |
|
+++ b/lib/report/report.c |
|
@@ -2845,7 +2845,7 @@ static int _vgsystemid_disp(struct dm_report *rh, struct dm_pool *mem, |
|
const void *data, void *private) |
|
{ |
|
const struct volume_group *vg = (const struct volume_group *) data; |
|
- const char *repstr = (vg->system_id && *vg->system_id) ? vg->system_id : ""; |
|
+ const char *repstr = (vg->system_id && *vg->system_id) ? vg->system_id : vg->lvm1_system_id ? : ""; |
|
|
|
return _field_string(rh, field, repstr); |
|
} |
|
@@ -3830,6 +3830,7 @@ static struct volume_group _dummy_vg = { |
|
.fid = &_dummy_fid, |
|
.name = "", |
|
.system_id = (char *) "", |
|
+ .lvm1_system_id = (char *) "", |
|
.pvs = DM_LIST_HEAD_INIT(_dummy_vg.pvs), |
|
.lvs = DM_LIST_HEAD_INIT(_dummy_vg.lvs), |
|
.historical_lvs = DM_LIST_HEAD_INIT(_dummy_vg.historical_lvs), |
|
@@ -3840,6 +3841,7 @@ static struct volume_group _unknown_vg = { |
|
.fid = &_dummy_fid, |
|
.name = "[unknown]", |
|
.system_id = (char *) "", |
|
+ .lvm1_system_id = (char *) "", |
|
.pvs = DM_LIST_HEAD_INIT(_unknown_vg.pvs), |
|
.lvs = DM_LIST_HEAD_INIT(_unknown_vg.lvs), |
|
.historical_lvs = DM_LIST_HEAD_INIT(_unknown_vg.historical_lvs), |
|
diff --git a/lib/striped/striped.c b/lib/striped/striped.c |
|
index 498b202..a2eaf80 100644 |
|
--- a/lib/striped/striped.c |
|
+++ b/lib/striped/striped.c |
|
@@ -239,7 +239,8 @@ static struct segment_type *_init_segtype(struct cmd_context *cmd, const char *n |
|
|
|
segtype->ops = &_striped_ops; |
|
segtype->name = name; |
|
- segtype->flags = target | SEG_CAN_SPLIT | SEG_AREAS_STRIPED; |
|
+ segtype->flags = target | SEG_CAN_SPLIT | SEG_AREAS_STRIPED | |
|
+ SEG_FORMAT1_SUPPORT; |
|
|
|
log_very_verbose("Initialised segtype: %s", segtype->name); |
|
return segtype; |
|
diff --git a/man/vgconvert.8_des b/man/vgconvert.8_des |
|
index 8519063..a0d34fd 100644 |
|
--- a/man/vgconvert.8_des |
|
+++ b/man/vgconvert.8_des |
|
@@ -1,3 +1,6 @@ |
|
-vgconvert converts VG metadata from one format to another. This command |
|
-is no longer used because this version of lvm no longer supports the LVM1 |
|
+vgconvert converts VG metadata from one format to another. The new |
|
+metadata format must be able to fit into the space provided by the old |
|
format. |
|
+ |
|
+Because the LVM1 format should no longer be used, this command is no |
|
+longer needed in general. |
|
diff --git a/test/shell/format-lvm1.sh b/test/shell/format-lvm1.sh |
|
new file mode 100644 |
|
index 0000000..b88579f |
|
--- /dev/null |
|
+++ b/test/shell/format-lvm1.sh |
|
@@ -0,0 +1,38 @@ |
|
+#!/usr/bin/env bash |
|
+ |
|
+# Copyright (C) 2008 Red Hat, Inc. All rights reserved. |
|
+# |
|
+# This copyrighted material is made available to anyone wishing to use, |
|
+# modify, copy, or redistribute it subject to the terms and conditions |
|
+# of the GNU General Public License v.2. |
|
+# |
|
+# You should have received a copy of the GNU General Public License |
|
+# along with this program; if not, write to the Free Software Foundation, |
|
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
|
+ |
|
+test_description='Test lvm1 format' |
|
+ |
|
+SKIP_WITH_LVMPOLLD=1 |
|
+ |
|
+. lib/inittest |
|
+ |
|
+aux prepare_devs 1 |
|
+ |
|
+if test -n "$LVM_TEST_LVM1" ; then |
|
+pvcreate -M1 "$dev1" |
|
+vgcreate -M1 $vg "$dev1" |
|
+check vg_field $vg fmt "lvm1" |
|
+fi |
|
+ |
|
+# TODO: if we decide to make using lvm1 with lvmetad an error, |
|
+# then if lvmetad is being used, then verify: |
|
+# not pvcreate -M1 "$dev1" |
|
+# not vgcreate -M1 $vg "$dev1" |
|
+# |
|
+# TODO: if we decide to allow using lvm1 with lvmetad, but disable lvmetad |
|
+# when it happens, then verify: |
|
+# pvcreate -M1 "$dev1" | tee err |
|
+# grep "disabled" err |
|
+# vgcreate -M1 $vg "$dev1" | tee err |
|
+# grep "disabled" err |
|
+ |
|
diff --git a/test/shell/lvm1-basic.sh b/test/shell/lvm1-basic.sh |
|
new file mode 100644 |
|
index 0000000..93509c4 |
|
--- /dev/null |
|
+++ b/test/shell/lvm1-basic.sh |
|
@@ -0,0 +1,36 @@ |
|
+#!/usr/bin/env bash |
|
+ |
|
+# Copyright (C) 2012 Red Hat, Inc. All rights reserved. |
|
+# |
|
+# This copyrighted material is made available to anyone wishing to use, |
|
+# modify, copy, or redistribute it subject to the terms and conditions |
|
+# of the GNU General Public License v.2. |
|
+# |
|
+# You should have received a copy of the GNU General Public License |
|
+# along with this program; if not, write to the Free Software Foundation, |
|
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
|
+ |
|
+SKIP_WITH_LVMLOCKD=1 |
|
+SKIP_WITHOUT_LVMETAD=1 |
|
+SKIP_WITH_LVMPOLLD=1 |
|
+ |
|
+. lib/inittest |
|
+ |
|
+aux prepare_devs 2 |
|
+pvcreate --metadatatype 1 "$dev1" |
|
+pvs | tee out |
|
+grep "$dev1" out |
|
+vgcreate --metadatatype 1 $vg1 "$dev1" |
|
+vgs | tee out |
|
+grep $vg1 out |
|
+pvs | tee out |
|
+grep "$dev1" out |
|
+ |
|
+# check for RHBZ 1080189 -- SEGV in lvremove/vgremove |
|
+pvcreate -ff -y --metadatatype 1 "$dev1" "$dev2" |
|
+vgcreate --metadatatype 1 $vg1 "$dev1" "$dev2" |
|
+lvcreate -l1 $vg1 "$dev1" |
|
+pvremove -ff -y "$dev2" |
|
+vgchange -an $vg1 |
|
+not lvremove $vg1 |
|
+not vgremove -ff -y $vg1 |
|
diff --git a/test/shell/snapshot-lvm1.sh b/test/shell/snapshot-lvm1.sh |
|
new file mode 100644 |
|
index 0000000..75c97cb |
|
--- /dev/null |
|
+++ b/test/shell/snapshot-lvm1.sh |
|
@@ -0,0 +1,35 @@ |
|
+#!/usr/bin/env bash |
|
+ |
|
+# Copyright (C) 2013 Red Hat, Inc. All rights reserved. |
|
+# |
|
+# This copyrighted material is made available to anyone wishing to use, |
|
+# modify, copy, or redistribute it subject to the terms and conditions |
|
+# of the GNU General Public License v.2. |
|
+# |
|
+# You should have received a copy of the GNU General Public License |
|
+# along with this program; if not, write to the Free Software Foundation, |
|
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
|
+ |
|
+# regression test for lvmetad reporting error: |
|
+# Internal error: LV snap_with_lvm1_meta (00000000000000000000000000000001) missing from preload metadata |
|
+ |
|
+SKIP_WITH_LVMLOCKD=1 |
|
+SKIP_WITH_LVMPOLLD=1 |
|
+ |
|
+. lib/inittest |
|
+ |
|
+aux prepare_devs 2 |
|
+get_devs |
|
+ |
|
+vgcreate --metadatatype 1 "$vg" "${DEVICES[@]}" |
|
+ |
|
+# Make origin volume |
|
+lvcreate -ae -l5 $vg -n origin |
|
+ |
|
+# Create a snap of origin |
|
+lvcreate -s $vg/origin -n snap_with_lvm1_meta -l4 |
|
+ |
|
+# Remove volume snapper/snap_with_lvm1_meta |
|
+lvremove -f $vg/snap_with_lvm1_meta |
|
+ |
|
+vgremove -ff $vg |
|
diff --git a/tools/args.h b/tools/args.h |
|
index 603a0cf..b80b8da 100644 |
|
--- a/tools/args.h |
|
+++ b/tools/args.h |
|
@@ -1207,7 +1207,8 @@ arg(mirrors_ARG, 'm', "mirrors", number_VAL, 0, 0, |
|
arg(metadatatype_ARG, 'M', "metadatatype", metadatatype_VAL, 0, 0, |
|
"Specifies the type of on-disk metadata to use.\n" |
|
"\\fBlvm2\\fP (or just \\fB2\\fP) is the current, standard format.\n" |
|
- "\\fBlvm1\\fP (or just \\fB1\\fP) is no longer used.\n") |
|
+ "\\fBlvm1\\fP (or just \\fB1\\fP) is a historical format that\n" |
|
+ "can be used for accessing old data.\n") |
|
|
|
arg(name_ARG, 'n', "name", string_VAL, 0, 0, |
|
"#lvcreate\n" |
|
diff --git a/tools/lvconvert.c b/tools/lvconvert.c |
|
index b149201..3ce228f 100644 |
|
--- a/tools/lvconvert.c |
|
+++ b/tools/lvconvert.c |
|
@@ -1770,6 +1770,11 @@ static int _lvconvert_splitsnapshot(struct cmd_context *cmd, struct logical_volu |
|
return 0; |
|
} |
|
|
|
+ if (!(vg->fid->fmt->features & FMT_MDAS)) { |
|
+ log_error("Unable to split off snapshot %s using old LVM1-style metadata.", cow_name); |
|
+ return 0; |
|
+ } |
|
+ |
|
if (is_lockd_type(vg->lock_type)) { |
|
/* FIXME: we need to create a lock for the new LV. */ |
|
log_error("Unable to split snapshots in VG with lock_type %s.", vg->lock_type); |
|
diff --git a/tools/lvmcmdline.c b/tools/lvmcmdline.c |
|
index 0dd24ec..0600b1c 100644 |
|
--- a/tools/lvmcmdline.c |
|
+++ b/tools/lvmcmdline.c |
|
@@ -23,6 +23,7 @@ |
|
|
|
#include "stub.h" |
|
#include "last-path-component.h" |
|
+#include "format1.h" |
|
|
|
#include <signal.h> |
|
#include <sys/stat.h> |
|
@@ -2899,6 +2900,13 @@ int lvm_run_command(struct cmd_context *cmd, int argc, char **argv) |
|
goto out; |
|
} |
|
|
|
+ if (!strcmp(cmd->fmt->name, FMT_LVM1_NAME) && lvmetad_used()) { |
|
+ log_warn("WARNING: Disabling lvmetad cache which does not support obsolete metadata."); |
|
+ lvmetad_set_disabled(cmd, LVMETAD_DISABLE_REASON_LVM1); |
|
+ log_warn("WARNING: Not using lvmetad because lvm1 format is used."); |
|
+ lvmetad_make_unused(cmd); |
|
+ } |
|
+ |
|
if (cmd->command->command_enum == lvconvert_repair_CMD) { |
|
log_warn("WARNING: Disabling lvmetad cache for repair command."); |
|
lvmetad_set_disabled(cmd, LVMETAD_DISABLE_REASON_REPAIR); |
|
@@ -2963,7 +2971,7 @@ int lvm_run_command(struct cmd_context *cmd, int argc, char **argv) |
|
* by different token values.) |
|
* |
|
* lvmetad may have been previously disabled (or disabled during the |
|
- * rescan done here) because duplicate devices were seen. |
|
+ * rescan done here) because duplicate devices or lvm1 metadata were seen. |
|
* In this case, disable the *use* of lvmetad by this command, reverting to |
|
* disk scanning. |
|
*/ |
|
@@ -3388,6 +3396,41 @@ static int _run_script(struct cmd_context *cmd, int argc, char **argv) |
|
return ret; |
|
} |
|
|
|
+/* |
|
+ * Determine whether we should fall back and exec the equivalent LVM1 tool |
|
+ */ |
|
+static int _lvm1_fallback(struct cmd_context *cmd) |
|
+{ |
|
+ char vsn[80]; |
|
+ int dm_present; |
|
+ |
|
+ if (!find_config_tree_bool(cmd, global_fallback_to_lvm1_CFG, NULL) || |
|
+ strncmp(cmd->kernel_vsn, "2.4.", 4)) |
|
+ return 0; |
|
+ |
|
+ log_suppress(1); |
|
+ dm_present = driver_version(vsn, sizeof(vsn)); |
|
+ log_suppress(0); |
|
+ |
|
+ if (dm_present || !lvm1_present(cmd)) |
|
+ return 0; |
|
+ |
|
+ return 1; |
|
+} |
|
+ |
|
+static void _exec_lvm1_command(char **argv) |
|
+{ |
|
+ char path[PATH_MAX]; |
|
+ |
|
+ if (dm_snprintf(path, sizeof(path), "%s.lvm1", argv[0]) < 0) { |
|
+ log_error("Failed to create LVM1 tool pathname"); |
|
+ return; |
|
+ } |
|
+ |
|
+ execvp(path, argv); |
|
+ log_sys_error("execvp", path); |
|
+} |
|
+ |
|
static void _nonroot_warning(void) |
|
{ |
|
if (getuid() || geteuid()) |
|
@@ -3477,6 +3520,19 @@ int lvm2_main(int argc, char **argv) |
|
} else |
|
run_name = dm_basename(argv[0]); |
|
|
|
+ if (_lvm1_fallback(cmd)) { |
|
+ /* Attempt to run equivalent LVM1 tool instead */ |
|
+ if (!argc) { |
|
+ log_error("Falling back to LVM1 tools, but no " |
|
+ "command specified."); |
|
+ ret = ECMD_FAILED; |
|
+ goto out; |
|
+ } |
|
+ _exec_lvm1_command(argv); |
|
+ ret = ECMD_FAILED; |
|
+ goto_out; |
|
+ } |
|
+ |
|
/* |
|
* Decide if we are running a shell or a command or a script. When |
|
* there is no run_name, it's a shell, when run_name is a recognized |
|
diff --git a/tools/pvscan.c b/tools/pvscan.c |
|
index 2915db5..d9ad097 100644 |
|
--- a/tools/pvscan.c |
|
+++ b/tools/pvscan.c |
|
@@ -631,7 +631,7 @@ out: |
|
* display the PV info. |
|
* |
|
* iii. If lvmetad is being used, but has been disabled (because of |
|
- * duplicate devs), or has a non-matching token |
|
+ * duplicate devs or lvm1 metadata), or has a non-matching token |
|
* (because the device filter is different from the device filter last |
|
* used to populate lvmetad), then 'pvscan' will begin by rescanning |
|
* devices to repopulate lvmetad. If lvmetad is enabled after the |
|
@@ -644,7 +644,8 @@ out: |
|
* attempt to repopulate the lvmetad cache by rescanning all devs |
|
* (regardless of whether lvmetad was previously disabled or had an |
|
* unmatching token.) lvmetad may be enabled or disabled after the |
|
- * rescan (depending on whether duplicate devs). |
|
+ * rescan (depending on whether duplicate devs or lvm1 metadata was |
|
+ * found). |
|
* |
|
* 3. The 'pvscan --cache <dev>' command will attempt to repopulate the |
|
* lvmetad cache by rescanning all devs if lvmetad has a non-matching |
|
diff --git a/tools/stub.h b/tools/stub.h |
|
index 1d58387..f03e5d3 100644 |
|
--- a/tools/stub.h |
|
+++ b/tools/stub.h |
|
@@ -37,6 +37,7 @@ int pvdata(struct cmd_context *cmd __attribute__((unused)), |
|
{ |
|
log_error("There's no 'pvdata' command in LVM2."); |
|
log_error("Use lvs, pvs, vgs instead; or use vgcfgbackup and read the text file backup."); |
|
+ log_error("Metadata in LVM1 format can still be displayed using LVM1's pvdata command."); |
|
return ECMD_FAILED; |
|
} |
|
|
|
diff --git a/tools/toollib.c b/tools/toollib.c |
|
index 413937f..b60ff06 100644 |
|
--- a/tools/toollib.c |
|
+++ b/tools/toollib.c |
|
@@ -14,6 +14,7 @@ |
|
*/ |
|
|
|
#include "tools.h" |
|
+#include "format1.h" |
|
#include "format-text.h" |
|
|
|
#include <sys/stat.h> |
|
@@ -4110,6 +4111,7 @@ static int _process_duplicate_pvs(struct cmd_context *cmd, |
|
.fid = &dummy_fid, |
|
.name = "", |
|
.system_id = (char *) "", |
|
+ .lvm1_system_id = (char *) "", |
|
.pvs = DM_LIST_HEAD_INIT(dummy_vg.pvs), |
|
.lvs = DM_LIST_HEAD_INIT(dummy_vg.lvs), |
|
.historical_lvs = DM_LIST_HEAD_INIT(dummy_vg.historical_lvs), |
|
@@ -4747,6 +4749,23 @@ int pvcreate_params_from_args(struct cmd_context *cmd, struct pvcreate_params *p |
|
pp->pva.label_sector = arg_int64_value(cmd, labelsector_ARG, |
|
DEFAULT_LABELSECTOR); |
|
|
|
+ if (!(cmd->fmt->features & FMT_MDAS) && |
|
+ (arg_is_set(cmd, pvmetadatacopies_ARG) || |
|
+ arg_is_set(cmd, metadatasize_ARG) || |
|
+ arg_is_set(cmd, dataalignment_ARG) || |
|
+ arg_is_set(cmd, dataalignmentoffset_ARG))) { |
|
+ log_error("Metadata and data alignment parameters only " |
|
+ "apply to text format."); |
|
+ return 0; |
|
+ } |
|
+ |
|
+ if (!(cmd->fmt->features & FMT_BAS) && |
|
+ arg_is_set(cmd, bootloaderareasize_ARG)) { |
|
+ log_error("Bootloader area parameters only " |
|
+ "apply to text format."); |
|
+ return 0; |
|
+ } |
|
+ |
|
if (arg_is_set(cmd, metadataignore_ARG)) |
|
pp->pva.metadataignore = arg_int_value(cmd, metadataignore_ARG, |
|
DEFAULT_PVMETADATAIGNORE); |
|
@@ -5106,7 +5125,10 @@ static int _pvcreate_check_single(struct cmd_context *cmd, |
|
pd->is_orphan_pv = 1; |
|
} |
|
|
|
- pp->orphan_vg_name = FMT_TEXT_ORPHAN_VG_NAME; |
|
+ if (!strcmp(vg->name, FMT_LVM1_ORPHAN_VG_NAME)) |
|
+ pp->orphan_vg_name = FMT_LVM1_ORPHAN_VG_NAME; |
|
+ else |
|
+ pp->orphan_vg_name = FMT_TEXT_ORPHAN_VG_NAME; |
|
} else { |
|
log_debug("Found pvcreate arg %s: device is not a PV.", pd->name); |
|
/* Device is not a PV. */ |
|
@@ -5335,7 +5357,10 @@ static int _pvremove_check_single(struct cmd_context *cmd, |
|
pd->is_orphan_pv = 1; |
|
} |
|
|
|
- pp->orphan_vg_name = FMT_TEXT_ORPHAN_VG_NAME; |
|
+ if (!strcmp(vg->name, FMT_LVM1_ORPHAN_VG_NAME)) |
|
+ pp->orphan_vg_name = FMT_LVM1_ORPHAN_VG_NAME; |
|
+ else |
|
+ pp->orphan_vg_name = FMT_TEXT_ORPHAN_VG_NAME; |
|
} else { |
|
/* FIXME: is it possible to reach here? */ |
|
log_debug("Found pvremove arg %s: device is not a PV.", pd->name); |
|
diff --git a/tools/vals.h b/tools/vals.h |
|
index 79c48b5..95dc8b2 100644 |
|
--- a/tools/vals.h |
|
+++ b/tools/vals.h |
|
@@ -77,7 +77,7 @@ |
|
* |
|
* FIXME: are there some specialized or irrelevant |
|
* options included in the usage text below that should |
|
- * be removed? |
|
+ * be removed? Should "lvm1" be removed? |
|
* |
|
* Size is a Number that takes an optional unit. |
|
* A full usage could be "Size[b|B|s|S|k|K|m|M|g|G|t|T|p|P|e|E]" |
|
@@ -126,7 +126,7 @@ val(sextents_VAL, sextents_arg, "SExtents", "[+|-]Number[PERCENT]") |
|
val(pextents_VAL, pextents_arg, "PExtents", "[+]Number[PERCENT]") |
|
val(nextents_VAL, nextents_arg, "NExtents", "[-]Number[PERCENT]") |
|
val(permission_VAL, permission_arg, "Permission", "rw|r") |
|
-val(metadatatype_VAL, metadatatype_arg, "MetadataType", "lvm2") |
|
+val(metadatatype_VAL, metadatatype_arg, "MetadataType", "lvm2|lvm1") |
|
val(units_VAL, string_arg, "Units", "r|R|h|H|b|B|s|S|k|K|m|M|g|G|t|T|p|P|e|E") |
|
val(segtype_VAL, segtype_arg, "SegType", "linear|striped|snapshot|mirror|raid|thin|cache|thin-pool|cache-pool") |
|
val(alloc_VAL, alloc_arg, "Alloc", "contiguous|cling|cling_by_tags|normal|anywhere|inherit") |
|
diff --git a/tools/vgchange.c b/tools/vgchange.c |
|
index 623517b..67be3ec 100644 |
|
--- a/tools/vgchange.c |
|
+++ b/tools/vgchange.c |
|
@@ -534,6 +534,13 @@ static int _vgchange_system_id(struct cmd_context *cmd, struct volume_group *vg) |
|
const char *system_id; |
|
const char *system_id_arg_str = arg_str_value(cmd, systemid_ARG, NULL); |
|
|
|
+ /* FIXME Merge with vg_set_system_id() */ |
|
+ if (systemid_on_pvs(vg)) { |
|
+ log_error("Metadata format %s does not support this type of system ID.", |
|
+ vg->fid->fmt->name); |
|
+ return 0; |
|
+ } |
|
+ |
|
if (!(system_id = system_id_from_string(cmd, system_id_arg_str))) { |
|
log_error("Unable to set system ID."); |
|
return 0; |
|
@@ -583,6 +590,9 @@ static int _vgchange_system_id(struct cmd_context *cmd, struct volume_group *vg) |
|
|
|
vg->system_id = system_id; |
|
|
|
+ if (vg->lvm1_system_id) |
|
+ *vg->lvm1_system_id = '\0'; |
|
+ |
|
return 1; |
|
} |
|
|
|
diff --git a/tools/vgconvert.c b/tools/vgconvert.c |
|
index ca9615c..8bdf8be 100644 |
|
--- a/tools/vgconvert.c |
|
+++ b/tools/vgconvert.c |
|
@@ -34,25 +34,29 @@ static int _vgconvert_single(struct cmd_context *cmd, const char *vg_name, |
|
return ECMD_FAILED; |
|
} |
|
|
|
- if (arg_sign_value(cmd, metadatasize_ARG, SIGN_NONE) == SIGN_MINUS) { |
|
- log_error("Metadata size may not be negative"); |
|
- return EINVALID_CMD_LINE; |
|
- } |
|
- |
|
- pva.pvmetadatasize = arg_uint64_value(cmd, metadatasize_ARG, UINT64_C(0)); |
|
- if (!pva.pvmetadatasize) |
|
- pva.pvmetadatasize = find_config_tree_int(cmd, metadata_pvmetadatasize_CFG, NULL); |
|
+ if (cmd->fmt->features & FMT_MDAS) { |
|
+ if (arg_sign_value(cmd, metadatasize_ARG, SIGN_NONE) == SIGN_MINUS) { |
|
+ log_error("Metadata size may not be negative"); |
|
+ return EINVALID_CMD_LINE; |
|
+ } |
|
|
|
- pva.pvmetadatacopies = arg_int_value(cmd, pvmetadatacopies_ARG, -1); |
|
- if (pva.pvmetadatacopies < 0) |
|
- pva.pvmetadatacopies = find_config_tree_int(cmd, metadata_pvmetadatacopies_CFG, NULL); |
|
+ pva.pvmetadatasize = arg_uint64_value(cmd, metadatasize_ARG, UINT64_C(0)); |
|
+ if (!pva.pvmetadatasize) |
|
+ pva.pvmetadatasize = find_config_tree_int(cmd, metadata_pvmetadatasize_CFG, NULL); |
|
|
|
- if (arg_sign_value(cmd, bootloaderareasize_ARG, SIGN_NONE) == SIGN_MINUS) { |
|
- log_error("Bootloader area size may not be negative"); |
|
- return EINVALID_CMD_LINE; |
|
+ pva.pvmetadatacopies = arg_int_value(cmd, pvmetadatacopies_ARG, -1); |
|
+ if (pva.pvmetadatacopies < 0) |
|
+ pva.pvmetadatacopies = find_config_tree_int(cmd, metadata_pvmetadatacopies_CFG, NULL); |
|
} |
|
|
|
- pva.ba_size = arg_uint64_value(cmd, bootloaderareasize_ARG, UINT64_C(0)); |
|
+ if (cmd->fmt->features & FMT_BAS) { |
|
+ if (arg_sign_value(cmd, bootloaderareasize_ARG, SIGN_NONE) == SIGN_MINUS) { |
|
+ log_error("Bootloader area size may not be negative"); |
|
+ return EINVALID_CMD_LINE; |
|
+ } |
|
+ |
|
+ pva.ba_size = arg_uint64_value(cmd, bootloaderareasize_ARG, UINT64_C(0)); |
|
+ } |
|
|
|
if (!vg_check_new_extent_size(cmd->fmt, vg->extent_size)) |
|
return_ECMD_FAILED; |
|
@@ -82,6 +86,13 @@ static int _vgconvert_single(struct cmd_context *cmd, const char *vg_name, |
|
return ECMD_FAILED; |
|
} |
|
|
|
+ /* New-style system ID supported? */ |
|
+ if (vg->system_id && *vg->system_id && (cmd->fmt->features & FMT_SYSTEMID_ON_PVS)) { |
|
+ log_error("Unable to convert VG %s while it has a system ID set (%s).", vg->name, |
|
+ vg->system_id); |
|
+ return ECMD_FAILED; |
|
+ } |
|
+ |
|
/* Attempt to change any LVIDs that are too big */ |
|
if (cmd->fmt->features & FMT_RESTRICTED_LVIDS) { |
|
dm_list_iterate_items(lvl, &vg->lvs) { |
|
@@ -146,6 +157,18 @@ int vgconvert(struct cmd_context *cmd, int argc, char **argv) |
|
return EINVALID_CMD_LINE; |
|
} |
|
|
|
+ if (!(cmd->fmt->features & FMT_MDAS) && |
|
+ arg_is_set(cmd, pvmetadatacopies_ARG)) { |
|
+ log_error("Metadata parameters only apply to text format"); |
|
+ return EINVALID_CMD_LINE; |
|
+ } |
|
+ |
|
+ if (!(cmd->fmt->features & FMT_BAS) && |
|
+ arg_is_set(cmd, bootloaderareasize_ARG)) { |
|
+ log_error("Bootloader area parameters only apply to text format"); |
|
+ return EINVALID_CMD_LINE; |
|
+ } |
|
+ |
|
return process_each_vg(cmd, argc, argv, NULL, NULL, READ_FOR_UPDATE, 0, NULL, |
|
&_vgconvert_single); |
|
} |
|
diff --git a/tools/vgscan.c b/tools/vgscan.c |
|
index f9fa382..1ec9083 100644 |
|
--- a/tools/vgscan.c |
|
+++ b/tools/vgscan.c |
|
@@ -44,7 +44,7 @@ static int _vgscan_single(struct cmd_context *cmd, const char *vg_name, |
|
* display the VG info. |
|
* |
|
* iii. If lvmetad is being used, but has been disabled (because of |
|
- * duplicate devs), or has a non-matching token |
|
+ * duplicate devs or lvm1 metadata), or has a non-matching token |
|
* (because the device filter is different from the device filter last |
|
* used to populate lvmetad), then 'vgscan' will begin by rescanning |
|
* devices to repopulate lvmetad. If lvmetad is enabled after the |
|
@@ -57,7 +57,7 @@ static int _vgscan_single(struct cmd_context *cmd, const char *vg_name, |
|
* the lvmetad cache by rescanning all devs (regardless of whether |
|
* lvmetad was previously disabled or had an unmatching token.) |
|
* lvmetad may be enabled or disabled after the rescan (depending |
|
- * on whether duplicate devs were found). |
|
+ * on whether duplicate devs or lvm1 metadata was found). |
|
* If enabled, then it will simply read and display VG info from the |
|
* lvmetad cache (like case 1.i.). If disabled, then it will |
|
* read all devices to display VG info (like case 1.ii.)
|
|
|