summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--configure.ac20
-rw-r--r--include/configure.h.in7
-rw-r--r--lib/Makefile.in1
-rw-r--r--lib/device/dev-type.c85
-rw-r--r--lib/device/dev-type.h2
-rw-r--r--lib/device/filesystem.c284
-rw-r--r--lib/device/filesystem.h46
-rw-r--r--lib/metadata/lv_manip.c715
-rw-r--r--lib/metadata/metadata-exported.h3
-rw-r--r--lib/metadata/metadata.c4
-rw-r--r--lib/metadata/metadata.h2
-rw-r--r--test/shell/lvresize-fs.sh1120
-rw-r--r--test/shell/lvresize-full.sh7
-rw-r--r--test/shell/lvresize-mirror.sh2
-rw-r--r--test/shell/lvresize-rounding.sh10
-rw-r--r--tools/args.h24
-rw-r--r--tools/command-lines.in12
-rw-r--r--tools/lvresize.c59
18 files changed, 2312 insertions, 91 deletions
diff --git a/configure.ac b/configure.ac
index 6cdf1a7e6..fed75ba5b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1713,6 +1713,21 @@ AC_DEFINE_UNQUOTED(FSADM_PATH, ["$FSADM_PATH"], [Path to fsadm binary.])
LVMIMPORTVDO_PATH="$SBINDIR/lvm_import_vdo"
AC_DEFINE_UNQUOTED(LVMIMPORTVDO_PATH, ["$LVMIMPORTVDO_PATH"], [Path to lvm_import_vdo script.])
+MOUNT_PATH="/usr/bin/mount"
+AC_DEFINE_UNQUOTED(MOUNT_PATH, ["$MOUNT_PATH"], [Path to mount binary.])
+
+UMOUNT_PATH="/usr/bin/umount"
+AC_DEFINE_UNQUOTED(UMOUNT_PATH, ["$UMOUNT_PATH"], [Path to umount binary.])
+
+E2FSCK_PATH="$SBINDIR/e2fsck"
+AC_DEFINE_UNQUOTED(E2FSCK_PATH, ["$E2FSCK_PATH"], [Path to e2fsck binary.])
+
+RESIZE2FS_PATH="$SBINDIR/resize2fs"
+AC_DEFINE_UNQUOTED(RESIZE2FS_PATH, ["$RESIZE2FS_PATH"], [Path to resize2fs binary.])
+
+XFS_GROWFS_PATH="$SBINDIR/xfs_growfs"
+AC_DEFINE_UNQUOTED(XFS_GROWFS_PATH, ["$XFS_GROWFS_PATH"], [Path to xfs_growfs binary.])
+
################################################################################
dnl -- dmeventd pidfile and executable path
if test "$BUILD_DMEVENTD" = yes; then
@@ -1882,6 +1897,11 @@ AC_SUBST(DM_LIB_PATCHLEVEL)
AC_SUBST(ELDFLAGS)
AC_SUBST(FSADM)
AC_SUBST(FSADM_PATH)
+AC_SUBST(MOUNT_PATH)
+AC_SUBST(UMOUNT_PATH)
+AC_SUBST(E2FSCK_PATH)
+AC_SUBST(RESIZE2FS_PATH)
+AC_SUBST(XFS_GROWFS_PATH)
AC_SUBST(BLKDEACTIVATE)
AC_SUBST(HAVE_LIBDL)
AC_SUBST(HAVE_REALTIME)
diff --git a/include/configure.h.in b/include/configure.h.in
index e0d971bbf..58da11a02 100644
--- a/include/configure.h.in
+++ b/include/configure.h.in
@@ -132,8 +132,13 @@
/* Define to 1 to include the LVM editline shell. */
#undef EDITLINE_SUPPORT
-/* Path to fsadm binary. */
+/* Paths to binaries. */
#undef FSADM_PATH
+#undef MOUNT_PATH
+#undef UMOUNT_PATH
+#undef E2FSCK_PATH
+#undef RESIZE2FS_PATH
+#undef XFS_GROWFS_PATH
/* Define to use GNU versioning in the shared library. */
#undef GNU_SYMVER
diff --git a/lib/Makefile.in b/lib/Makefile.in
index 3ab5cb2f1..3380b28fb 100644
--- a/lib/Makefile.in
+++ b/lib/Makefile.in
@@ -40,6 +40,7 @@ SOURCES =\
device/dev-luks.c \
device/dev-dasd.c \
device/dev-lvm1-pool.c \
+ device/filesystem.c \
device/online.c \
device/parse_vpd.c \
display/display.c \
diff --git a/lib/device/dev-type.c b/lib/device/dev-type.c
index 939eb4aeb..d944e4f34 100644
--- a/lib/device/dev-type.c
+++ b/lib/device/dev-type.c
@@ -838,6 +838,86 @@ int get_fs_block_size(const char *pathname, uint32_t *fs_block_size)
return 0;
}
}
+
+int fs_get_blkid(const char *pathname, struct fs_info *fsi)
+{
+ blkid_probe probe = NULL;
+ const char *str;
+ size_t len;
+ uint64_t fslastblock = 0;
+ unsigned int fsblocksize = 0;
+ int no_block_size = 0, no_fslastblock = 0, no_fsblocksize = 0;
+ int rc;
+
+ if (!(probe = blkid_new_probe_from_filename(pathname))) {
+ log_error("Failed libblkid probe setup for %s", pathname);
+ return 0;
+ }
+
+ blkid_probe_enable_superblocks(probe, 1);
+ blkid_probe_set_superblocks_flags(probe,
+ BLKID_SUBLKS_LABEL | BLKID_SUBLKS_LABELRAW |
+ BLKID_SUBLKS_UUID | BLKID_SUBLKS_UUIDRAW |
+ BLKID_SUBLKS_TYPE | BLKID_SUBLKS_SECTYPE |
+ BLKID_SUBLKS_USAGE | BLKID_SUBLKS_VERSION |
+ BLKID_SUBLKS_MAGIC | BLKID_SUBLKS_FSINFO);
+ rc = blkid_do_safeprobe(probe);
+ if (rc < 0) {
+ log_error("Failed libblkid probe for %s", pathname);
+ blkid_free_probe(probe);
+ return 0;
+ } else if (rc == 1) {
+ /* no file system on the device */
+ log_print("No file system found on %s.", pathname);
+ fsi->nofs = 1;
+ blkid_free_probe(probe);
+ return 1;
+ }
+
+ if (!blkid_probe_lookup_value(probe, "TYPE", &str, &len) && len)
+ strncpy(fsi->fstype, str, sizeof(fsi->fstype)-1);
+ else {
+ /* any difference from blkid_do_safeprobe rc=1? */
+ log_print("No file system type on %s.", pathname);
+ fsi->nofs = 1;
+ blkid_free_probe(probe);
+ return 1;
+ }
+
+ if (!blkid_probe_lookup_value(probe, "BLOCK_SIZE", &str, &len) && len)
+ fsi->block_size_bytes = atoi(str);
+ else
+ no_block_size = 1;
+
+ if (!blkid_probe_lookup_value(probe, "FSLASTBLOCK", &str, &len) && len)
+ fslastblock = strtoull(str, NULL, 0);
+ else
+ no_fslastblock = 1;
+
+ if (!blkid_probe_lookup_value(probe, "FSBLOCKSIZE", &str, &len) && len)
+ fsblocksize = (unsigned int)atoi(str);
+ else
+ no_fsblocksize = 1;
+
+ blkid_free_probe(probe);
+
+ if (no_block_size || no_fslastblock || no_fsblocksize) {
+ log_print("Missing libblkid %s%s%sfor %s",
+ no_block_size ? "BLOCK_SIZE " : "",
+ no_fslastblock ? "FSLASTBLOCK " : "",
+ no_fsblocksize ? "FSBLOCKSIZE " : "",
+ pathname);
+ }
+
+ if (fslastblock && fsblocksize)
+ fsi->fs_last_byte = fslastblock * fsblocksize;
+
+ log_debug("libblkid TYPE %s BLOCK_SIZE %d FSLASTBLOCK %llu FSBLOCKSIZE %u fs_last_byte %llu",
+ fsi->fstype, fsi->block_size_bytes, (unsigned long long)fslastblock, fsblocksize,
+ (unsigned long long)fsi->fs_last_byte);
+ return 1;
+}
+
#else
int get_fs_block_size(const char *pathname, uint32_t *fs_block_size)
{
@@ -845,6 +925,11 @@ int get_fs_block_size(const char *pathname, uint32_t *fs_block_size)
*fs_block_size = 0;
return 0;
}
+int fs_get_blkid(const char *pathname, struct fs_info *fsi)
+{
+ log_debug("Disabled blkid for fs info.");
+ return 0;
+}
#endif
#ifdef BLKID_WIPING_SUPPORT
diff --git a/lib/device/dev-type.h b/lib/device/dev-type.h
index 17c997641..2d5ec159a 100644
--- a/lib/device/dev-type.h
+++ b/lib/device/dev-type.h
@@ -18,6 +18,7 @@
#include "lib/device/device.h"
#include "lib/display/display.h"
#include "lib/label/label.h"
+#include "lib/device/filesystem.h"
#define NUMBER_OF_MAJORS 4096
@@ -102,6 +103,7 @@ int dev_is_nvme(struct dev_types *dt, struct device *dev);
int dev_is_lv(struct device *dev);
int get_fs_block_size(const char *pathname, uint32_t *fs_block_size);
+int fs_get_blkid(const char *pathname, struct fs_info *fsi);
int dev_is_used_by_active_lv(struct cmd_context *cmd, struct device *dev, int *used_by_lv_count,
char **used_by_dm_name, char **used_by_vg_uuid, char **used_by_lv_uuid);
diff --git a/lib/device/filesystem.c b/lib/device/filesystem.c
new file mode 100644
index 000000000..183ed6322
--- /dev/null
+++ b/lib/device/filesystem.c
@@ -0,0 +1,284 @@
+/*
+ * Copyright (C) 2022 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 "base/memory/zalloc.h"
+#include "lib/misc/lib.h"
+#include "lib/commands/toolcontext.h"
+#include "lib/device/device.h"
+#include "lib/device/dev-type.h"
+#include "lib/misc/lvm-exec.h"
+
+#include <mntent.h>
+
+static int _get_lv_path(struct logical_volume *lv, char *lv_path)
+{
+ if (dm_snprintf(lv_path, PATH_MAX, "%s%s/%s", lv->vg->cmd->dev_dir,
+ lv->vg->name, lv->name) < 0) {
+ log_error("Couldn't create LV path for %s.", display_lvname(lv));
+ return 0;
+ }
+ return 1;
+}
+
+int fs_get_info(struct cmd_context *cmd, struct logical_volume *lv,
+ struct fs_info *fsi, int include_mount)
+{
+ char lv_path[PATH_MAX];
+ FILE *fme = NULL;
+ struct stat stlv;
+ struct stat stme;
+ struct mntent *me;
+ int ret;
+
+ if (!_get_lv_path(lv, lv_path))
+ return_0;
+
+ if (!fs_get_blkid(lv_path, fsi)) {
+ log_error("Missing libblkid file system info for %s", display_lvname(lv));
+ return 0;
+ }
+
+ if (fsi->nofs)
+ return 1;
+
+ if (!include_mount)
+ return 1;
+
+ if (stat(lv_path, &stlv) < 0)
+ return_0;
+
+ if (!(fme = setmntent("/etc/mtab", "r")))
+ return_0;
+
+ ret = 1;
+
+ while ((me = getmntent(fme))) {
+ if (strcmp(me->mnt_type, fsi->fstype))
+ continue;
+ if (me->mnt_dir[0] != '/')
+ continue;
+ if (me->mnt_fsname[0] != '/')
+ continue;
+ if (stat(me->mnt_dir, &stme) < 0)
+ continue;
+ if (stme.st_dev != stlv.st_rdev)
+ continue;
+
+ log_debug("fs_get_info %s is mounted \"%s\"", lv_path, me->mnt_dir);
+ fsi->mounted = 1;
+ strncpy(fsi->mount_dir, me->mnt_dir, PATH_MAX-1);
+ }
+ endmntent(fme);
+
+ fsi->unmounted = !fsi->mounted;
+ return ret;
+}
+
+#define FS_CMD_MAX_ARGS 6
+
+int fs_fsck_command(struct cmd_context *cmd, struct logical_volume *lv, struct fs_info *fsi)
+{
+ char lv_path[PATH_MAX];
+ const char *argv[FS_CMD_MAX_ARGS + 4];
+ int args = 0;
+ int status;
+
+ if (strncmp(fsi->fstype, "ext", 3)) {
+ log_error("fsck not supported for %s.", fsi->fstype);
+ return_0;
+ }
+
+ if (!_get_lv_path(lv, lv_path))
+ return_0;
+
+ /*
+ * ext234: e2fsck -f -p path
+ * TODO: replace -p with -y based on yes_ARG, or other?
+ */
+
+ argv[0] = E2FSCK_PATH; /* defined by configure */
+ argv[++args] = "-f";
+ argv[++args] = "-p";
+ argv[++args] = lv_path;
+ argv[++args] = NULL;
+
+ log_print("Checking file system %s with %s on %s...",
+ fsi->fstype, E2FSCK_PATH, display_lvname(lv));
+
+ if (!exec_cmd(cmd, argv, &status, 1)) {
+ log_error("e2fsck failed on %s.", display_lvname(lv));
+ return 0;
+ }
+
+ log_print("Checked file system %s on %s.", fsi->fstype, display_lvname(lv));
+ return 1;
+}
+
+int fs_reduce_command(struct cmd_context *cmd, struct logical_volume *lv, struct fs_info *fsi,
+ uint64_t newsize_bytes)
+{
+ char lv_path[PATH_MAX];
+ char newsize_kb_str[16] = { 0 };
+ const char *argv[FS_CMD_MAX_ARGS + 4];
+ int args = 0;
+ int status;
+
+ if (!_get_lv_path(lv, lv_path))
+ return_0;
+
+ if (!strncmp(fsi->fstype, "ext", 3)) {
+ if (dm_snprintf(newsize_kb_str, 16, "%lluk", (unsigned long long)(newsize_bytes/1024)) < 0)
+ return_0;
+
+ /*
+ * ext234 shrink: resize2fs path newsize
+ */
+ argv[0] = RESIZE2FS_PATH; /* defined by configure */
+ argv[++args] = lv_path;
+ argv[++args] = newsize_kb_str;
+ argv[++args] = NULL;
+
+ } else {
+ log_error("fs reduce not supported for %s.", fsi->fstype);
+ return_0;
+ }
+
+ log_print("Reducing file system %s on %s...", fsi->fstype, display_lvname(lv));
+
+ if (!exec_cmd(cmd, argv, &status, 1)) {
+ log_error("Failed to reduce %s file system on %s.", fsi->fstype, display_lvname(lv));
+ return 0;
+ }
+
+ log_print("Reduced file system %s to %s (%llu bytes) on %s.",
+ fsi->fstype, display_size(cmd, newsize_bytes/512),
+ (unsigned long long)newsize_bytes, display_lvname(lv));
+ return 1;
+}
+
+int fs_extend_command(struct cmd_context *cmd, struct logical_volume *lv, struct fs_info *fsi)
+{
+ char lv_path[PATH_MAX];
+ const char *argv[FS_CMD_MAX_ARGS + 4];
+ int args = 0;
+ int status;
+
+ if (!_get_lv_path(lv, lv_path))
+ return_0;
+
+ if (!strncmp(fsi->fstype, "ext", 3)) {
+ /* TODO: include -f if lvm command inclues -f ? */
+ argv[0] = RESIZE2FS_PATH; /* defined by configure */
+ argv[++args] = lv_path;
+ argv[++args] = NULL;
+
+ } else if (!strcmp(fsi->fstype, "xfs")) {
+ argv[0] = XFS_GROWFS_PATH; /* defined by configure */
+ argv[++args] = lv_path;
+ argv[++args] = NULL;
+
+ } else {
+ log_error("Extend not supported for %s file system.", fsi->fstype);
+ return_0;
+ }
+
+ log_print("Extending file system %s on %s...", fsi->fstype, display_lvname(lv));
+
+ if (!exec_cmd(cmd, argv, &status, 1)) {
+ log_error("Failed to extend %s file system on %s.", fsi->fstype, display_lvname(lv));
+ return 0;
+ }
+
+ log_print("Extended file system %s on %s.", fsi->fstype, display_lvname(lv));
+ return 1;
+}
+
+int fs_mount_command(struct cmd_context *cmd, struct logical_volume *lv, struct fs_info *fsi,
+ int reuse_mount_dir)
+{
+ char lv_path[PATH_MAX];
+ char mountdir[PATH_MAX];
+ const char *argv[FS_CMD_MAX_ARGS + 4];
+ int args = 0;
+ int status;
+
+ if (!_get_lv_path(lv, lv_path))
+ return_0;
+
+ if (reuse_mount_dir) {
+ if (!fsi->mount_dir[0]) {
+ log_error("Cannot remount fs without previous mount dir.");
+ return 0;
+ }
+ memcpy(mountdir, fsi->mount_dir, PATH_MAX);
+ } else {
+ if (dm_snprintf(mountdir, sizeof(mountdir), "/tmp/%s_XXXXXX", cmd->name) < 0)
+ return_0;
+ if (!mkdtemp(mountdir)) {
+ log_error("Failed to create temp dir for mount: %s", strerror(errno));
+ return 0;
+ }
+ memcpy(fsi->mount_dir, mountdir, PATH_MAX);
+ fsi->temp_mount_dir = 1;
+ }
+
+ argv[0] = MOUNT_PATH; /* defined by configure */
+ argv[++args] = lv_path;
+ argv[++args] = mountdir;
+ argv[++args] = NULL;
+
+ log_print("Mounting %s.", display_lvname(lv));
+
+ if (!exec_cmd(cmd, argv, &status, 1)) {
+ log_error("Failed to mount file system on %s at %s.", display_lvname(lv), mountdir);
+ return 0;
+ }
+
+ log_print("Mounted %s at %s dir %s.", display_lvname(lv),
+ reuse_mount_dir ? "original" : "temporary", mountdir);
+ return 1;
+}
+
+int fs_unmount_command(struct cmd_context *cmd, struct logical_volume *lv, struct fs_info *fsi)
+{
+ char lv_path[PATH_MAX];
+ const char *argv[FS_CMD_MAX_ARGS + 4];
+ int args = 0;
+ int status;
+
+ if (!_get_lv_path(lv, lv_path))
+ return_0;
+
+ argv[0] = UMOUNT_PATH; /* defined by configure */
+ argv[++args] = lv_path;
+ argv[++args] = NULL;
+
+ log_print("Unmounting %s.", display_lvname(lv));
+
+ if (!exec_cmd(cmd, argv, &status, 1)) {
+ log_error("Failed to unmount file system on %s.", display_lvname(lv));
+ return 0;
+ }
+
+ log_print("Unmounted %s.", display_lvname(lv));
+
+ if (fsi->temp_mount_dir) {
+ if (rmdir(fsi->mount_dir) < 0)
+ log_print("Error removing temp dir %s", fsi->mount_dir);
+ fsi->mount_dir[0] = '\0';
+ fsi->temp_mount_dir = 0;
+ }
+ return 1;
+}
+
diff --git a/lib/device/filesystem.h b/lib/device/filesystem.h
new file mode 100644
index 000000000..2fff4b29f
--- /dev/null
+++ b/lib/device/filesystem.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2022 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 _FILESYSTEM_H
+#define _FILESYSTEM_H
+
+struct fs_info {
+ char fstype[16];
+ char mount_dir[PATH_MAX];
+ unsigned int block_size_bytes; /* 512 or 4k */
+ uint64_t fs_last_byte; /* last byte on the device used by the fs */
+
+ unsigned nofs:1;
+ unsigned unmounted:1;
+ unsigned mounted:1;
+ unsigned temp_mount_dir:1;
+ /* for resizing */
+ unsigned needs_reduce:1;
+ unsigned needs_extend:1;
+ unsigned needs_fsck:1;
+ unsigned needs_unmount:1;
+ unsigned needs_mount:1;
+};
+
+int fs_get_info(struct cmd_context *cmd, struct logical_volume *lv,
+ struct fs_info *fsi, int include_mount);
+int fs_fsck_command(struct cmd_context *cmd, struct logical_volume *lv, struct fs_info *fsi);
+int fs_reduce_command(struct cmd_context *cmd, struct logical_volume *lv, struct fs_info *fsi,
+ uint64_t newsize_bytes);
+int fs_extend_command(struct cmd_context *cmd, struct logical_volume *lv, struct fs_info *fsi);
+int fs_mount_command(struct cmd_context *cmd, struct logical_volume *lv, struct fs_info *fsi,
+ int reuse_mount_dir);
+int fs_unmount_command(struct cmd_context *cmd, struct logical_volume *lv, struct fs_info *fsi);
+
+#endif
diff --git a/lib/metadata/lv_manip.c b/lib/metadata/lv_manip.c
index 1c301e873..0ae419d03 100644
--- a/lib/metadata/lv_manip.c
+++ b/lib/metadata/lv_manip.c
@@ -31,6 +31,7 @@
#include "lib/locking/lvmlockd.h"
#include "lib/label/label.h"
#include "lib/misc/lvm-signal.h"
+#include "lib/device/filesystem.h"
#ifdef HAVE_BLKZEROOUT
#include <sys/ioctl.h>
@@ -4946,13 +4947,6 @@ static int _lv_reduce_confirmation(struct logical_volume *lv,
return 0;
}
- if (lv_is_vdo(lv) && !info.exists) {
- log_error("Logical volume %s must be activated "
- "before reducing device size.",
- display_lvname(lv));
- return 0;
- }
-
if (!info.exists)
return 1;
@@ -5933,7 +5927,7 @@ static void _setup_params_for_extend_metadata(struct logical_volume *lv,
lp->percent = PERCENT_NONE;
lp->segtype = mseg->segtype;
lp->mirrors = seg_is_mirrored(mseg) ? lv_mirror_count(lv) : 0;
- lp->resizefs = 0;
+ lp->fsopt[0] = '\0';
lp->stripes = lp->mirrors ? mseg->area_count / lp->mirrors : 0;
lp->stripe_size = mseg->stripe_size;
}
@@ -5974,6 +5968,586 @@ static int _lv_resize_check_used(struct logical_volume *lv)
return 1;
}
+/*
+ * --fs checksize: check fs size and allow the lv to reduce if the fs is not
+ * using the affected space, i.e. the fs does not need to be
+ * resized. fail the command without reducing the fs or lv if
+ * the fs is using the affected space.
+ *
+ * --fs resize_remount: resize the fs, mounting/unmounting the fs as needed,
+ * but avoiding mounting/unmounted when possible.
+ *
+ * --fs resize_keepmount: resize the fs without changing the current
+ * mount/unmount state. fail the command without reducing the
+ * fs or lv if the fs resize would require mounting or unmounting.
+ *
+ * --fs resize_unmount: resize the fs only while it's unmounted ("offline"),
+ * unmounting the fs if needed. fail the commandn without
+ * reducing the fs or lv if the fs resize would require having
+ * the fs mounted.
+ *
+ * --fs resize_fsadm: old method using fsadm script to do everything
+ *
+ * --fs resize: equivalent to resize_remount.
+ */
+static int _fs_reduce_allow(struct cmd_context *cmd, struct logical_volume *lv,
+ struct lvresize_params *lp, uint64_t newsize_bytes,
+ struct fs_info *fsi)
+{
+ const char *fs_reduce_cmd = "";
+ const char *cmp = "";
+ int equal = 0, smaller = 0, larger = 0;
+ int is_ext_fstype = 0;
+ int confirm_mount_change = 0;
+
+ /*
+ * Allow reducing the LV for other fs types if the fs is not using
+ * space that's being reduced.
+ */
+ if (!strcmp(fsi->fstype, "ext2") ||
+ !strcmp(fsi->fstype, "ext3") ||
+ !strcmp(fsi->fstype, "ext4") ||
+ !strcmp(fsi->fstype, "xfs")) {
+ log_debug("Found fs %s last_byte %llu newsize_bytes %llu",
+ fsi->fstype,
+ (unsigned long long)fsi->fs_last_byte,
+ (unsigned long long)newsize_bytes);
+ if (!strncmp(fsi->fstype, "ext", 3)) {
+ is_ext_fstype = 1;
+ fs_reduce_cmd = " resize2fs";
+ }
+ }
+
+ if (!fsi->mounted)
+ log_print("File system %s found on %s.",
+ fsi->fstype, display_lvname(lv));
+ else
+ log_print("File system %s found on %s mounted at %s.",
+ fsi->fstype, display_lvname(lv), fsi->mount_dir);
+
+ if (!fsi->fs_last_byte) {
+ log_error("File system size unknown: update libblkid for FSLASTBLOCK, or see --fs resize_fsadm.");
+ return 0;
+ }
+
+ if ((equal = (fsi->fs_last_byte == newsize_bytes)))
+ cmp = "equal to";
+ else if ((smaller = (fsi->fs_last_byte < newsize_bytes)))
+ cmp = "smaller_than";
+ else if ((larger = (fsi->fs_last_byte > newsize_bytes)))
+ cmp = "larger than";
+
+ log_print("File system size (%s) is %s the requested LV size (%s).",
+ display_size(cmd, fsi->fs_last_byte/512), cmp,
+ display_size(cmd, newsize_bytes/512));
+
+ /*
+ * FS reduce is not needed, it's not using the affected space.
+ */
+ if (smaller || equal) {
+ log_print("File system reduce is not needed, skipping.");
+ fsi->needs_reduce = 0;
+ return 1;
+ }
+
+ /*
+ * FS reduce is required, but checksize does not allow it.
+ */
+ if (!strcmp(lp->fsopt, "checksize")) {
+ if (is_ext_fstype)
+ log_error("File system reduce is required (see resize2fs or --resizefs.)");
+ else
+ log_error("File system reduce is required and not supported (%s).", fsi->fstype);
+ return 0;
+ }
+
+ /*
+ * FS reduce required, ext* supports it, xfs does not.
+ */
+ if (is_ext_fstype) {
+ log_print("File system reduce is required using resize2fs.");
+ } else {
+ log_error("File system reduce is required and not supported (%s).", fsi->fstype);
+ return 0;
+ }
+
+ /*
+ * Set fstype-specific requirements for running fs resize command.
+ * ext2,3,4 require the fs to be unmounted to shrink with resize2fs,
+ * and they require e2fsck to be run first, unless resize2fs -f is used.
+ */
+ if (is_ext_fstype) {
+ /* it's traditional to run fsck before shrink */
+ if (!lp->nofsck)
+ fsi->needs_fsck = 1;
+
+ /* ext2,3,4 require fs to be unmounted to shrink */
+ if (fsi->mounted)
+ fsi->needs_unmount = 1;
+
+ fsi->needs_reduce = 1;
+ } else {
+ /*
+ * Shouldn't reach here since no other fs types get this far.
+ * A future fs supporting shrink may require the fs to be
+ * mounted or unmounted to run the fs shrink command.
+ * set fsi->needs_unmount or fs->needs_mount according to
+ * the fs-specific shrink command's requirement.
+ */
+ log_error("File system %s: fs reduce not implemented.", fsi->fstype);
+ return 0;
+ }
+
+ /*
+ * FS reduce may require mounting or unmounting, check the fsopt value
+ * from the user, and the current mount state to decide if fs resize
+ * can be done.
+ */
+ if (!strcmp(lp->fsopt, "resize_keepmount")) {
+ /* can't mount|unmount to run fs resize */
+ if (fsi->needs_mount) {
+ log_error("File system needs to be mounted to reduce fs (see resize_remount).");
+ return 0;
+ }
+ if (fsi->needs_unmount) {
+ log_error("File system needs to be unmounted to reduce fs (see resize_remount).");
+ return 0;
+ }
+ } else if (!strcmp(lp->fsopt, "resize_unmount")) {
+ /* we can unmount if needed to run fs resize */
+ if (fsi->needs_mount) {
+ log_error("File system needs to be mounted to reduce fs (see resize_remount).");
+ return 0;
+ }
+ } else if (!strcmp(lp->fsopt, "resize_remount")) {
+ /* we can mount|unmount as needed to run fs resize */
+ } else if (!strcmp(lp->fsopt, "resize")) {
+ /* equivalent to resize_remount, but confirm mount change */
+ if (fsi->needs_mount || fsi->needs_unmount)
+ confirm_mount_change = 1;
+ } else {
+ log_error("Unknown file system resize option: %s", lp->fsopt);
+ return 0;
+ }
+
+ /*
+ * If future file systems can be reduced while mounted, then suppress
+ * needs_fsck here if the fs is already mounted.
+ */
+
+ if (fsi->needs_unmount)
+ log_print("File system unmount is needed for reduce.");
+ if (fsi->needs_fsck)
+ log_print("File system fsck will be run before reduce.");
+ if (fsi->needs_mount)
+ log_print("File system mount is needed for reduce.");
+
+ /*
+ * Use a confirmation prompt because mount|unmount is needed, and
+ * the generic "resize" option was used (i.e. the user did not give
+ * specific direction about how to handle mounting|unmounting such as
+ * resize_unmount, resize_remount, resize_keepmount, so ask them.)
+ */
+ if (!lp->yes && confirm_mount_change) {
+ if (yes_no_prompt("Continue with %s file system reduce steps:%s%s%s%s?",
+ fsi->fstype,
+ fsi->needs_unmount ? " unmount," : "",
+ fsi->needs_fsck ? " fsck," : "",
+ fsi->needs_mount ? " mount," : "",
+ fsi->needs_reduce ? fs_reduce_cmd : "") == 'n') {
+ log_error("File system not reduced.");
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+static int _fs_extend_allow(struct cmd_context *cmd, struct logical_volume *lv,
+ struct lvresize_params *lp, struct fs_info *fsi)
+{
+ const char *fs_extend_cmd = "";
+ int is_ext_fstype = 0;
+ int confirm_mount_change = 0;
+
+ if (!strcmp(fsi->fstype, "ext2") ||
+ !strcmp(fsi->fstype, "ext3") ||
+ !strcmp(fsi->fstype, "ext4") ||
+ !strcmp(fsi->fstype, "xfs")) {
+ log_debug("Found fs %s last_byte %llu",
+ fsi->fstype, (unsigned long long)fsi->fs_last_byte);
+ if (!strncmp(fsi->fstype, "ext", 3))
+ is_ext_fstype = 1;
+ } else {
+ log_error("File system extend is not supported (%s).", fsi->fstype);
+ return 0;
+ }
+
+ if (!fsi->mounted)
+ log_print("File system %s found on %s.",
+ fsi->fstype, display_lvname(lv));
+ else
+ log_print("File system %s found on %s mounted at %s.",
+ fsi->fstype, display_lvname(lv), fsi->mount_dir);
+
+ /*
+ * FS extend may require mounting or unmounting, check the fsopt value
+ * from the user, and the current mount state to decide if fs extend
+ * can be done.
+ */
+
+ if (is_ext_fstype) {
+ fs_extend_cmd = " resize2fs";
+
+ /*
+ * ext* can be extended while it's mounted or unmounted. If
+ * the fs is unmounted, it's traditional to run fsck before
+ * running the fs extend.
+ *
+ * resize_keepmount: don't change mount condition.
+ * if mounted: fs_extend
+ * if unmounted: fsck, fs_extend
+ *
+ * resize_unmount: extend offline, so unmount first if mounted.
+ * if mounted: unmount, fsck, fs_extend
+ * if unmounted: fsck, fs_extend
+ *
+ * resize_remount|resize: do any mount or unmount that's necessary,
+ * avoiding unnecessary mounting/unmounting.
+ * if mounted: fs_extend
+ * if unmounted: fsck, fs_extend
+ */
+ if (!strcmp(lp->fsopt, "resize_keepmount")) {
+ if (fsi->mounted)
+ fsi->needs_extend = 1;
+ else if (fsi->unmounted) {
+ fsi->needs_fsck = 1;
+ fsi->needs_extend = 1;
+ }
+ } else if (!strcmp(lp->fsopt, "resize_unmount")) {
+ if (fsi->mounted) {
+ fsi->needs_unmount = 1;
+ fsi->needs_fsck = 1;
+ fsi->needs_extend = 1;
+ } else if (fsi->unmounted) {
+ fsi->needs_fsck = 1;
+ fsi->needs_extend = 1;
+ }
+ } else if (!strcmp(lp->fsopt, "resize_remount") || !strcmp(lp->fsopt, "resize")) {
+ if (fsi->mounted)
+ fsi->needs_extend = 1;
+ else if (fsi->unmounted) {
+ fsi->needs_fsck = 1;
+ fsi->needs_extend = 1;
+ }
+ }
+
+ if (lp->nofsck)
+ fsi->needs_fsck = 0;
+
+ } else if (!strcmp(fsi->fstype, "xfs")) {
+ fs_extend_cmd = " xfs_growfs";
+
+ /*
+ * xfs must be mounted to extend.
+ *
+ * resize_keepmount: don't change mount condition.
+ * if mounted: fs_extend
+ * if unmounted: fail
+ *
+ * resize_unmount: extend offline, so unmount first if mounted.
+ * if mounted: fail
+ * if unmounted: fail
+ *
+ * resize_remount|resize: do any mount or unmount that's necessary,
+ * avoiding unnecessary mounting/unmounting.
+ * if mounted: fs_extend
+ * if unmounted: mount, fs_extend
+ */
+ if (!strcmp(lp->fsopt, "resize_keepmount")) {
+ if (fsi->mounted)
+ fsi->needs_extend = 1;
+ else if (fsi->unmounted) {
+ log_error("File system must be mounted to extend (see resize_remount).");
+ return 0;
+ }
+ } else if (!strcmp(lp->fsopt, "resize_unmount")) {
+ log_error("File system must be mounted to extend (see resize_remount).");
+ return 0;
+ } else if (!strcmp(lp->fsopt, "resize_remount") || !strcmp(lp->fsopt, "resize")) {
+ if (fsi->mounted)
+ fsi->needs_extend = 1;
+ else if (fsi->unmounted) {
+ fsi->needs_mount = 1;
+ fsi->needs_extend = 1;
+ }
+ }
+
+ } else {
+ /* shouldn't reach here */
+ log_error("File system type %s not handled.", fsi->fstype);
+ return 0;
+ }
+
+ /*
+ * Skip needs_fsck if the fs is mounted and we can extend the fs while
+ * it's mounted.
+ */
+ if (fsi->mounted && !fsi->needs_unmount && fsi->needs_fsck) {
+ log_print("File system fsck skipped for extending mounted fs.");
+ fsi->needs_fsck = 0;
+ }
+
+ if (fsi->needs_unmount)
+ log_print("File system unmount is needed for extend.");
+ if (fsi->needs_fsck)
+ log_print("File system fsck will be run before extend.");
+ if (fsi->needs_mount)
+ log_print("File system mount is needed for extend.");
+
+ /*
+ * Use a confirmation prompt because mount|unmount is needed, and
+ * the generic "resize" option was used (i.e. the user did not give
+ * specific direction about how to handle mounting|unmounting such
+ * as resize_umount, resize_remount, resize_keepmount, so ask them.)
+ */
+ if (!strcmp(lp->fsopt, "resize") && (fsi->needs_mount || fsi->needs_unmount))
+ confirm_mount_change = 1;
+
+ if (!lp->yes && confirm_mount_change) {
+ if (yes_no_prompt("Continue with %s file system extend steps:%s%s%s%s? ",
+ fsi->fstype,
+ fsi->needs_unmount ? " unmount," : "",
+ fsi->needs_fsck ? " fsck," : "",
+ fsi->needs_mount ? " mount," : "",
+ fsi->needs_extend ? fs_extend_cmd : "") == 'n') {
+ log_error("File system not extended.");
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+static int _fs_reduce(struct cmd_context *cmd, struct logical_volume *lv,
+ struct lvresize_params *lp)
+{
+ struct fs_info fsinfo;
+ struct fs_info fsinfo2;
+ uint64_t newsize_bytes;
+ int mounted = 0, unmounted = 0;
+ int ret = 0;
+
+ memset(&fsinfo, 0, sizeof(fsinfo));
+ memset(&fsinfo2, 0, sizeof(fsinfo));
+
+ if (!fs_get_info(cmd, lv, &fsinfo, 1))
+ goto_out;
+
+ if (fsinfo.nofs) {
+ ret = 1;
+ goto_out;
+ }
+
+ /* extent_size units is SECTOR_SIZE (512) */
+ newsize_bytes = lp->extents * lv->vg->extent_size * SECTOR_SIZE;
+
+ /*
+ * Based on the --fs command option, the fs type, the last block used,
+ * and the mount state, determine if LV reduce is allowed. If not
+ * returns 0 and lvreduce should fail. If allowed, returns 1 and sets
+ * fsinfo.needs_* for any steps that are required to reduce the LV.
+ */
+ if (!_fs_reduce_allow(cmd, lv, lp, newsize_bytes, &fsinfo))
+ goto_out;
+
+ /*
+ * fs reduce is not needed to reduce the LV.
+ */
+ if (!fsinfo.needs_reduce) {
+ ret = 1;
+ goto_out;
+ }
+
+ if (test_mode()) {
+ if (fsinfo.needs_unmount)
+ log_print("Skip unmount in test mode.");
+ if (fsinfo.needs_fsck)
+ log_print("Skip fsck in test mode.");
+ if (fsinfo.needs_mount)
+ log_print("Skip mount in test mode.");
+ log_print("Skip fs reduce in test mode.");
+ ret = 1;
+ goto out;
+ }
+
+ /*
+ * mounting, unmounting, fsck, and shrink command can all take a long
+ * time to run, and this lvm command should not block other lvm
+ * commands from running during that time, so release the vg lock
+ * around the long-running steps, and reacquire after.
+ */
+ unlock_vg(cmd, lv->vg, lv->vg->name);
+
+ if (fsinfo.needs_unmount) {
+ if (!fs_unmount_command(cmd, lv, &fsinfo))
+ goto_out;
+ unmounted = 1;
+ }
+
+ if (fsinfo.needs_fsck) {
+ if (!fs_fsck_command(cmd, lv, &fsinfo))
+ goto_out;
+ }
+
+ if (fsinfo.needs_mount) {
+ if (!fs_mount_command(cmd, lv, &fsinfo, 0))
+ goto_out;
+ mounted = 1;
+ }
+
+ /*
+ * Fork/run the fs-specific reduce command.
+ */
+ if (!fs_reduce_command(cmd, lv, &fsinfo, newsize_bytes))
+ goto_out;
+
+ if (!lock_vol(cmd, lv->vg->name, LCK_VG_WRITE, NULL)) {
+ log_error("Failed to lock VG, cannot reduce LV.");
+ ret = 0;
+ goto out;
+ }
+
+ /*
+ * Check that the vg wasn't changed while it was unlocked.
+ * (can_use_one_scan: check just one mda in the vg for changes)
+ */
+ cmd->can_use_one_scan = 1;
+ if (scan_text_mismatch(cmd, lv->vg->name, NULL)) {
+ log_error("VG was changed during fs operations, cannot reduce LV.");
+ ret = 0;
+ goto out;
+ }
+
+ /*
+ * Re-check the fs last block which should now be less than the
+ * requested (reduced) LV size.
+ */
+ if (!fs_get_info(cmd, lv, &fsinfo2, 0))
+ goto_out;
+
+ if (fsinfo.fs_last_byte && (fsinfo2.fs_last_byte > newsize_bytes)) {
+ log_error("File system last byte %llu is greater than new LV size %llu bytes.",
+ (unsigned long long)fsinfo2.fs_last_byte,
+ (unsigned long long)newsize_bytes);
+ goto_out;
+ }
+
+ ret = 1;
+
+ /*
+ * put the fs back into the original mounted|unmounted state.
+ */
+ if (!strcmp(lp->fsopt, "resize_remount") || !strcmp(lp->fsopt, "resize")) {
+ if (mounted && !fs_unmount_command(cmd, lv, &fsinfo))
+ ret = 0;
+ if (unmounted && !fs_mount_command(cmd, lv, &fsinfo, 1))
+ ret = 0;
+ }
+ out:
+ return ret;
+}
+
+static int _fs_extend(struct cmd_context *cmd, struct logical_volume *lv,
+ struct lvresize_params *lp)
+{
+ struct fs_info fsinfo;
+ int mounted = 0, unmounted = 0;
+ int ret = 0;
+
+ memset(&fsinfo, 0, sizeof(fsinfo));
+
+ if (!fs_get_info(cmd, lv, &fsinfo, 1))
+ goto_out;
+
+ if (fsinfo.nofs) {
+ ret = 1;
+ goto_out;
+ }
+
+ /*
+ * Decide if fs should be extended based on the --fs option,
+ * the fs type and the mount state.
+ */
+ if (!_fs_extend_allow(cmd, lv, lp, &fsinfo))
+ goto_out;
+
+ /*
+ * fs extend is not needed
+ */
+ if (!fsinfo.needs_extend) {
+ ret = 1;
+ goto_out;
+ }
+
+ if (test_mode()) {
+ if (fsinfo.needs_unmount)
+ log_print("Skip unmount in test mode.");
+ if (fsinfo.needs_fsck)
+ log_print("Skip fsck in test mode.");
+ if (fsinfo.needs_mount)
+ log_print("Skip mount in test mode.");
+ log_print("Skip fs extend in test mode.");
+ ret = 1;
+ goto out;
+ }
+
+ /*
+ * mounting, unmounting and extend command can all take a long
+ * time to run, and this lvm command should not block other lvm
+ * commands from running during that time, so release the vg
+ * lock around the long-running steps.
+ */
+ unlock_vg(cmd, lv->vg, lv->vg->name);
+
+ if (fsinfo.needs_unmount) {
+ if (!fs_unmount_command(cmd, lv, &fsinfo))
+ goto_out;
+ unmounted = 1;
+ }
+
+ if (fsinfo.needs_fsck) {
+ if (!fs_fsck_command(cmd, lv, &fsinfo))
+ goto_out;
+ }
+
+ if (fsinfo.needs_mount) {
+ if (!fs_mount_command(cmd, lv, &fsinfo, 0))
+ goto_out;
+ mounted = 1;
+ }
+
+ /*
+ * Fork/run the fs-specific extend command.
+ */
+ if (!fs_extend_command(cmd, lv, &fsinfo))
+ goto_out;
+
+ ret = 1;
+
+ /*
+ * put the fs back into the original mounted|unmounted state.
+ */
+ if (!strcmp(lp->fsopt, "resize_remount") || !strcmp(lp->fsopt, "resize")) {
+ if (mounted && !fs_unmount_command(cmd, lv, &fsinfo))
+ ret = 0;
+ if (unmounted && !fs_mount_command(cmd, lv, &fsinfo, 1))
+ ret = 0;
+ }
+ out:
+ return ret;
+}
+
int lv_resize(struct cmd_context *cmd, struct logical_volume *lv,
struct lvresize_params *lp)
{
@@ -6019,26 +6593,6 @@ int lv_resize(struct cmd_context *cmd, struct logical_volume *lv,
}
/*
- * resizefs applies to the LV command arg, not to the top LV or lower
- * resizable LV.
- */
- if (lp->resizefs) {
- if (!lv_is_active(lv)) {
- log_error("Logical volume %s must be activated before resizing filesystem.",
- display_lvname(lv));
- return 0;
- }
- /* types of LVs that can hold a file system */
- if (!(lv_is_linear(lv) || lv_is_striped(lv) || lv_is_raid(lv) ||
- lv_is_mirror(lv) || lv_is_thin_volume(lv) || lv_is_vdo(lv) ||
- lv_is_cache(lv) || lv_is_writecache(lv))) {
- log_print_unless_silent("Ignoring --resizefs for LV type %s.",
- seg ? seg->segtype->name : "unknown");
- lp->resizefs = 0;
- }
- }
-
- /*
* Figure out which LVs are going to be extended, and set params
* to the requested extents/size for each. Some LVs are extended
* only by extending an underlying LV. Extending some top level
@@ -6189,10 +6743,6 @@ int lv_resize(struct cmd_context *cmd, struct logical_volume *lv,
if (!_lv_resize_check_type(lv_main, lp))
return_0;
-
- if (is_reduce && !main_size_matches && !lp->resizefs &&
- !_lv_reduce_confirmation(lv, lp))
- return_0;
}
/*
@@ -6261,46 +6811,85 @@ int lv_resize(struct cmd_context *cmd, struct logical_volume *lv,
* So active temporarily pool LV (with on disk metadata) then use
* suspend and resume and deactivate pool LV, instead of searching for
* an active thin volume.
+ *
+ * FIXME: why are thin pools activated where other LV types return
+ * error if inactive?
*/
if (lv_is_thin_pool(lv_top) && !lv_is_active(lv_top)) {
- if (!activation()) {
- log_error("Cannot resize %s without using device-mapper kernel driver.",
- display_lvname(lv_top));
- return_0;
+ if (!activation()) {
+ log_error("Cannot activate to resize %s without using device-mapper kernel driver.",
+ display_lvname(lv_top));
+ return 0;
}
if (!activate_lv(cmd, lv_top)) {
log_error("Failed to activate %s.", display_lvname(lv_top));
- return_0;
+ return 0;
}
activated = 1;
}
/*
+ * Disable fsopt checksize for lvextend.
+ */
+ if (is_extend && !strcmp(lp->fsopt, "checksize"))
+ lp->fsopt[0] = '\0';
+
+ /*
+ * Disable fsopt if LV type cannot hold a file system.
+ */
+ if (lp->fsopt[0] &&
+ !(lv_is_linear(lv) || lv_is_striped(lv) || lv_is_raid(lv) ||
+ lv_is_mirror(lv) || lv_is_thin_volume(lv) || lv_is_vdo(lv) ||
+ lv_is_cache(lv) || lv_is_writecache(lv))) {
+ log_print_unless_silent("Ignoring fs resizing options for LV type %s.",
+ seg ? seg->segtype->name : "unknown");
+ lp->fsopt[0] = '\0';
+ }
+
+ /*
+ * Any fs option requires the LV already be active.
+ * (not sure why the command doesn't activate)
+ */
+ if (lp->fsopt[0] && !lv_is_active(lv_top)) {
+ log_error("Logical volume %s must be active for file system %s.",
+ display_lvname(lv_top), lp->fsopt);
+ return 0;
+ }
+
+ /*
+ * Warn and confirm if checksize has been disabled for reduce.
+ */
+ if (is_reduce && !lp->fsopt[0] && !_lv_reduce_confirmation(lv_top, lp))
+ return_0;
+
+ /*
* If the LV is locked due to being active, this lock call is a no-op.
* Otherwise, this acquires a transient lock on the lv (not PERSISTENT)
*/
if (!lockd_lv_resize(cmd, lv_top, "ex", 0, lp))
goto_out;
- /*
- * Check the file system, and shrink the fs before reducing lv.
- * TODO: libblkid fs type, fs last_block, mount state,
- * unlock vg, mount|unmount if needed, fork fs shrink,
- * lock vg, rescan devs, recheck fs type last_block.
- * (at end mount|unmount if needed to restore initial state.)
- */
- if (lp->resizefs && !lp->nofsck &&
+ /* Part of old approach to fs handling using fsadm. */
+ if (!strcmp(lp->fsopt, "resize_fsadm") && !lp->nofsck &&
!_fsadm_cmd(FSADM_CMD_CHECK, lv_top, 0, lp->yes, lp->force, &status)) {
if (status != FSADM_CHECK_FAILS_FOR_MOUNTED) {
log_error("Filesystem check failed.");
goto out;
}
- /* some filesystems support online resize */
}
- if (lp->resizefs && is_reduce &&
- !_fsadm_cmd(FSADM_CMD_RESIZE, lv_top, lp->extents, lp->yes, lp->force, NULL)) {
- log_error("Filesystem resize failed.");
- goto out;
+
+ if (is_reduce && lp->fsopt[0]) {
+ if (!strcmp(lp->fsopt, "resize_fsadm")) {
+ /* Old approach to fs handling using fsadm. */
+ if (!_fsadm_cmd(FSADM_CMD_RESIZE, lv_top, lp->extents, lp->yes, lp->force, NULL)) {
+ log_error("Filesystem resize failed.");
+ goto out;
+ }
+ } else {
+ /* New approach to fs handling using fs info. */
+ if (!_fs_reduce(cmd, lv_top, lp))
+ goto_out;
+ }
}
/*
@@ -6372,16 +6961,22 @@ int lv_resize(struct cmd_context *cmd, struct logical_volume *lv,
stack;
}
- /*
- * Extend the file system.
- * TODO: libblkid fs type, mount state,
- * unlock vg, mount|unmount if needed, fork fs grow,
- * mount|unmount if needed to restore initial state.
- */
- if (lp->resizefs && is_extend &&
- !_fsadm_cmd(FSADM_CMD_RESIZE, lv_top, lp->extents, lp->yes, lp->force, NULL)) {
- log_warn("Filesystem resize failed.");
- goto out;
+ if (is_extend && lp->fsopt[0]) {
+ if (!strcmp(lp->fsopt, "resize_fsadm")) {
+ /* Old approach to fs handling using fsadm. */
+ if (!_fsadm_cmd(FSADM_CMD_RESIZE, lv_top, lp->extents, lp->yes, lp->force, NULL)) {
+ log_error("File system extend error.");
+ lp->extend_fs_error = 1;
+ goto out;
+ }
+ } else {
+ /* New approach to fs handling using fs info. */
+ if (!_fs_extend(cmd, lv_top, lp)) {
+ log_error("File system extend error.");
+ lp->extend_fs_error = 1;
+ goto out;
+ }
+ }
}
ret = 1;
diff --git a/lib/metadata/metadata-exported.h b/lib/metadata/metadata-exported.h
index 8b67ccece..70596b150 100644
--- a/lib/metadata/metadata-exported.h
+++ b/lib/metadata/metadata-exported.h
@@ -674,8 +674,8 @@ struct lvresize_params {
int force;
int nosync;
int nofsck;
- int resizefs;
int use_policies;
+ char fsopt[32]; /* set by --resizefs|--fs, empty for --fs ignore */
const struct segment_type *segtype;
unsigned mirrors;
@@ -695,6 +695,7 @@ struct lvresize_params {
int approx_alloc;
int extents_are_pes; /* Is 'extents' counting PEs or LEs? */
int size_changed; /* Was there actually a size change */
+ int extend_fs_error; /* FS extend error after LV extend success */
const char *lockopt;
char *lockd_lv_refresh_path; /* set during resize to use for refresh at the end */
diff --git a/lib/metadata/metadata.c b/lib/metadata/metadata.c
index 95f25eef8..30b2c1779 100644
--- a/lib/metadata/metadata.c
+++ b/lib/metadata/metadata.c
@@ -4533,7 +4533,7 @@ void vg_write_commit_bad_mdas(struct cmd_context *cmd, struct volume_group *vg)
* reread metadata.
*/
-static bool _scan_text_mismatch(struct cmd_context *cmd, const char *vgname, const char *vgid)
+bool scan_text_mismatch(struct cmd_context *cmd, const char *vgname, const char *vgid)
{
DM_LIST_INIT(mda_list);
struct mda_list *mdal, *safe;
@@ -4706,7 +4706,7 @@ static struct volume_group *_vg_read(struct cmd_context *cmd,
* probably unnecessary; all commands could likely just check a single mda.
*/
- if (lvmcache_scan_mismatch(cmd, vgname, vgid) || _scan_text_mismatch(cmd, vgname, vgid)) {
+ if (lvmcache_scan_mismatch(cmd, vgname, vgid) || scan_text_mismatch(cmd, vgname, vgid)) {
log_debug_metadata("Rescanning devices for %s %s", vgname, writing ? "rw" : "");
if (writing)
lvmcache_label_rescan_vg_rw(cmd, vgname, vgid);
diff --git a/lib/metadata/metadata.h b/lib/metadata/metadata.h
index 6c39ff845..5cebe9508 100644
--- a/lib/metadata/metadata.h
+++ b/lib/metadata/metadata.h
@@ -538,5 +538,7 @@ void set_pv_devices(struct format_instance *fid, struct volume_group *vg);
int get_visible_lvs_using_pv(struct cmd_context *cmd, struct volume_group *vg, struct device *dev,
struct dm_list *lvs_list);
+bool scan_text_mismatch(struct cmd_context *cmd, const char *vgname, const char *vgid);
+
#endif
diff --git a/test/shell/lvresize-fs.sh b/test/shell/lvresize-fs.sh
new file mode 100644
index 000000000..7331f804f
--- /dev/null
+++ b/test/shell/lvresize-fs.sh
@@ -0,0 +1,1120 @@
+#!/usr/bin/env bash
+
+# Copyright (C) 2007-2016 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_LVMPOLLD=1
+
+. lib/inittest
+
+aux prepare_vg 3 256
+
+# Test combinations of the following:
+# lvreduce / lvextend
+# no fs / ext4 / xfs
+# each --fs opt / no --fs opt / --resizefs
+# active / inactive
+# mounted / unmounted
+# fs size less than, equal to or greater than reduced lv size
+
+mount_dir="mnt"
+mkdir -p "$mount_dir"
+
+
+#
+# lvextend, no fs
+#
+
+# lvextend, no fs, active, no --fs
+lvcreate -n $lv -L 256M $vg
+aux wipefs_a "$DM_DEV_DIR/$vg/$lv"
+lvextend -L+200M $vg/$lv
+check lv_field $vg/$lv lv_size "456.00m"
+lvchange -an $vg/$lv
+lvremove $vg/$lv
+
+# lvextend, no fs, inactive, no --fs
+lvcreate -n $lv -L 256M $vg
+aux wipefs_a "$DM_DEV_DIR/$vg/$lv"
+lvchange -an $vg/$lv
+lvextend -L+200M $vg/$lv
+check lv_field $vg/$lv lv_size "456.00m"
+lvremove $vg/$lv
+
+# lvextend, no fs, active, --fs resize does nothing with no fs found
+lvcreate -n $lv -L 256M $vg
+aux wipefs_a "$DM_DEV_DIR/$vg/$lv"
+lvextend -L+200M --fs resize $vg/$lv
+check lv_field $vg/$lv lv_size "456.00m"
+lvchange -an $vg/$lv
+lvremove $vg/$lv
+
+# lvextend, no fs, inactive, --fs resize error requires active lv
+lvcreate -n $lv -L 256M $vg
+aux wipefs_a "$DM_DEV_DIR/$vg/$lv"
+lvchange -an $vg/$lv
+not lvextend -L+200M --fs resize $vg/$lv
+check lv_field $vg/$lv lv_size "256.00m"
+lvremove $vg/$lv
+
+#
+# lvextend, ext4
+#
+
+# lvextend, ext4, active, mounted, no --fs setting is same as --fs ignore
+lvcreate -n $lv -L 256M $vg
+mkfs.ext4 "$DM_DEV_DIR/$vg/$lv"
+mount "$DM_DEV_DIR/$vg/$lv" "$mount_dir"
+df --output=size "$mount_dir" |tee df1
+dd if=/dev/zero of="$mount_dir/zeros1" bs=1M count=200 conv=fdatasync
+lvextend -L+200M $vg/$lv
+check lv_field $vg/$lv lv_size "456.00m"
+# with no --fs used, the fs size should be the same
+df --output=size "$mount_dir" |tee df2
+diff df1 df2
+resize2fs "$DM_DEV_DIR/$vg/$lv"
+df --output=size "$mount_dir" |tee df3
+not diff df2 df3
+dd if=/dev/zero of="$mount_dir/zeros2" bs=1M count=200 conv=fdatasync
+umount "$mount_dir"
+lvchange -an $vg/$lv
+lvremove $vg/$lv
+
+# lvextend, ext4, inactive, --fs ignore
+lvcreate -n $lv -L 256M $vg
+mkfs.ext4 "$DM_DEV_DIR/$vg/$lv"
+lvchange -an $vg/$lv
+lvextend --fs ignore -L+200M $vg/$lv
+check lv_field $vg/$lv lv_size "456.00m"
+lvremove $vg/$lv
+
+# lvextend, ext4, active, mounted, --fs ignore
+lvcreate -n $lv -L 256M $vg
+mkfs.ext4 "$DM_DEV_DIR/$vg/$lv"
+mount "$DM_DEV_DIR/$vg/$lv" "$mount_dir"
+df --output=size "$mount_dir" |tee df1
+lvextend --fs ignore -L+200M $vg/$lv
+check lv_field $vg/$lv lv_size "456.00m"
+df --output=size "$mount_dir" |tee df2
+diff df1 df2
+umount "$mount_dir"
+lvchange -an $vg/$lv
+lvremove $vg/$lv
+
+# lvextend, ext4, active, mounted, --fs resize
+lvcreate -n $lv -L 256M $vg
+mkfs.ext4 "$DM_DEV_DIR/$vg/$lv"
+mount "$DM_DEV_DIR/$vg/$lv" "$mount_dir"
+df --output=size "$mount_dir" |tee df1
+dd if=/dev/zero of="$mount_dir/zeros1" bs=1M count=200 conv=fdatasync
+lvextend --fs resize -L+200M $vg/$lv
+check lv_field $vg/$lv lv_size "456.00m"
+df --output=size "$mount_dir" |tee df2
+not diff df1 df2
+dd if=/dev/zero of="$mount_dir/zeros2" bs=1M count=200 conv=fdatasync
+umount "$mount_dir"
+lvchange -an $vg/$lv
+lvremove $vg/$lv
+
+# lvextend, ext4, active, mounted, --resizefs (same as --fs resize)
+lvcreate -n $lv -L 256M $vg
+mkfs.ext4 "$DM_DEV_DIR/$vg/$lv"
+mount "$DM_DEV_DIR/$vg/$lv" "$mount_dir"
+df --output=size "$mount_dir" |tee df1
+dd if=/dev/zero of="$mount_dir/zeros1" bs=1M count=200 conv=fdatasync
+lvextend --resizefs -L+200M $vg/$lv
+check lv_field $vg/$lv lv_size "456.00m"
+df --output=size "$mount_dir" |tee df2
+not diff df1 df2
+dd if=/dev/zero of="$mount_dir/zeros2" bs=1M count=200 conv=fdatasync
+umount "$mount_dir"
+lvchange -an $vg/$lv
+lvremove $vg/$lv
+
+# lvextend, ext4, active, mounted, --fs resize_remount (same as --fs resize)
+lvcreate -n $lv -L 256M $vg
+mkfs.ext4 "$DM_DEV_DIR/$vg/$lv"
+mount "$DM_DEV_DIR/$vg/$lv" "$mount_dir"
+df --output=size "$mount_dir" |tee df1
+dd if=/dev/zero of="$mount_dir/zeros1" bs=1M count=200 conv=fdatasync
+lvextend --fs resize_remount -L+200M $vg/$lv
+check lv_field $vg/$lv lv_size "456.00m"
+df --output=size "$mount_dir" |tee df2
+not diff df1 df2
+dd if=/dev/zero of="$mount_dir/zeros2" bs=1M count=200 conv=fdatasync
+umount "$mount_dir"
+lvchange -an $vg/$lv
+lvremove $vg/$lv
+
+# lvextend, ext4, active, mounted, --fs resize_unmount
+lvcreate -n $lv -L 256M $vg
+mkfs.ext4 "$DM_DEV_DIR/$vg/$lv"
+mount "$DM_DEV_DIR/$vg/$lv" "$mount_dir"
+df --output=size "$mount_dir" |tee df1
+dd if=/dev/zero of="$mount_dir/zeros1" bs=1M count=200 conv=fdatasync
+lvextend --fs resize_unmount -L+200M $vg/$lv
+check lv_field $vg/$lv lv_size "456.00m"
+# resize_unmount leaves fs unmounted
+df -a | tee dfa
+not grep "$mount_dir" dfa
+mount "$DM_DEV_DIR/$vg/$lv" "$mount_dir"
+df --output=size "$mount_dir" |tee df2
+not diff df1 df2
+dd if=/dev/zero of="$mount_dir/zeros2" bs=1M count=200 conv=fdatasync
+umount "$mount_dir"
+lvchange -an $vg/$lv
+lvremove $vg/$lv
+
+# lvextend, ext4, active, mounted, --fs resize_keepmount
+lvcreate -n $lv -L 256M $vg
+mkfs.ext4 "$DM_DEV_DIR/$vg/$lv"
+mount "$DM_DEV_DIR/$vg/$lv" "$mount_dir"
+df --output=size "$mount_dir" |tee df1
+dd if=/dev/zero of="$mount_dir/zeros1" bs=1M count=200 conv=fdatasync
+lvextend --fs resize_keepmount -L+200M $vg/$lv
+check lv_field $vg/$lv lv_size "456.00m"
+df --output=size "$mount_dir" |tee df2
+not diff df1 df2
+dd if=/dev/zero of="$mount_dir/zeros2" bs=1M count=200 conv=fdatasync
+umount "$mount_dir"
+lvchange -an $vg/$lv
+lvremove $vg/$lv
+
+# lvextend, ext4, active, mounted, --fs resize_fsadm
+lvcreate -n $lv -L 256M $vg
+mkfs.ext4 "$DM_DEV_DIR/$vg/$lv"
+mount "$DM_DEV_DIR/$vg/$lv" "$mount_dir"
+df --output=size "$mount_dir" |tee df1
+dd if=/dev/zero of="$mount_dir/zeros1" bs=1M count=200 conv=fdatasync
+lvextend --fs resize_fsadm -L+200M $vg/$lv
+check lv_field $vg/$lv lv_size "456.00m"
+df --output=size "$mount_dir" |tee df2
+not diff df1 df2
+dd if=/dev/zero of="$mount_dir/zeros2" bs=1M count=200 conv=fdatasync
+umount "$mount_dir"
+lvchange -an $vg/$lv
+lvremove $vg/$lv
+
+# lvextend, ext4, active, unmounted, --fs resize_keepmount
+lvcreate -n $lv -L 256M $vg
+mkfs.ext4 "$DM_DEV_DIR/$vg/$lv"
+mount "$DM_DEV_DIR/$vg/$lv" "$mount_dir"
+df --output=size "$mount_dir" |tee df1
+dd if=/dev/zero of="$mount_dir/zeros1" bs=1M count=200 conv=fdatasync
+umount "$mount_dir"
+lvextend --fs resize_keepmount -L+200M $vg/$lv
+check lv_field $vg/$lv lv_size "456.00m"
+mount "$DM_DEV_DIR/$vg/$lv" "$mount_dir"
+df --output=size "$mount_dir" |tee df2
+not diff df1 df2
+dd if=/dev/zero of="$mount_dir/zeros2" bs=1M count=200 conv=fdatasync
+umount "$mount_dir"
+lvchange -an $vg/$lv
+lvremove $vg/$lv
+
+# lvextend, ext4, active, unmounted, --fs resize
+lvcreate -n $lv -L 256M $vg
+mkfs.ext4 "$DM_DEV_DIR/$vg/$lv"
+mount "$DM_DEV_DIR/$vg/$lv" "$mount_dir"
+df --output=size "$mount_dir" |tee df1
+dd if=/dev/zero of="$mount_dir/zeros1" bs=1M count=200 conv=fdatasync
+umount "$mount_dir"
+lvextend --fs resize -L+200M $vg/$lv
+check lv_field $vg/$lv lv_size "456.00m"
+mount "$DM_DEV_DIR/$vg/$lv" "$mount_dir"
+df --output=size "$mount_dir" |tee df2
+not diff df1 df2
+dd if=/dev/zero of="$mount_dir/zeros2" bs=1M count=200 conv=fdatasync
+umount "$mount_dir"
+lvchange -an $vg/$lv
+lvremove $vg/$lv
+
+# lvextend, ext4, active, unmounted, --fs resize_fsadm
+lvcreate -n $lv -L 256M $vg
+mkfs.ext4 "$DM_DEV_DIR/$vg/$lv"
+mount "$DM_DEV_DIR/$vg/$lv" "$mount_dir"
+df --output=size "$mount_dir" |tee df1
+dd if=/dev/zero of="$mount_dir/zeros1" bs=1M count=200 conv=fdatasync
+umount "$mount_dir"
+lvextend --fs resize_fsadm -L+200M $vg/$lv
+check lv_field $vg/$lv lv_size "456.00m"
+mount "$DM_DEV_DIR/$vg/$lv" "$mount_dir"
+df --output=size "$mount_dir" |tee df2
+not diff df1 df2
+dd if=/dev/zero of="$mount_dir/zeros2" bs=1M count=200 conv=fdatasync
+umount "$mount_dir"
+lvchange -an $vg/$lv
+lvremove $vg/$lv
+
+
+#
+# lvextend, xfs
+#
+
+# lvextend, xfs, active, mounted, no --fs setting is same as --fs ignore
+lvcreate -n $lv -L 256M $vg
+mkfs.xfs "$DM_DEV_DIR/$vg/$lv"
+mount "$DM_DEV_DIR/$vg/$lv" "$mount_dir"
+df --output=size "$mount_dir" |tee df1
+dd if=/dev/zero of="$mount_dir/zeros1" bs=1M count=200 conv=fdatasync
+lvextend -L+200M $vg/$lv
+check lv_field $vg/$lv lv_size "456.00m"
+# with no --fs used, the fs size should be the same
+df --output=size "$mount_dir" |tee df2
+diff df1 df2
+xfs_growfs "$DM_DEV_DIR/$vg/$lv"
+df --output=size "$mount_dir" |tee df3
+not diff df2 df3
+dd if=/dev/zero of="$mount_dir/zeros2" bs=1M count=200 conv=fdatasync
+umount "$mount_dir"
+lvchange -an $vg/$lv
+lvremove $vg/$lv
+
+# lvextend, xfs, inactive, --fs ignore
+lvcreate -n $lv -L 256M $vg
+mkfs.xfs "$DM_DEV_DIR/$vg/$lv"
+lvchange -an $vg/$lv
+lvextend --fs ignore -L+200M $vg/$lv
+check lv_field $vg/$lv lv_size "456.00m"
+lvremove $vg/$lv
+
+# lvextend, xfs, active, mounted, --fs ignore
+lvcreate -n $lv -L 256M $vg
+mkfs.xfs "$DM_DEV_DIR/$vg/$lv"
+mount "$DM_DEV_DIR/$vg/$lv" "$mount_dir"
+df --output=size "$mount_dir" |tee df1
+lvextend --fs ignore -L+200M $vg/$lv
+check lv_field $vg/$lv lv_size "456.00m"
+df --output=size "$mount_dir" |tee df2
+diff df1 df2
+umount "$mount_dir"
+lvchange -an $vg/$lv
+lvremove $vg/$lv
+
+# lvextend, xfs, active, mounted, --fs resize
+lvcreate -n $lv -L 256M $vg
+mkfs.xfs "$DM_DEV_DIR/$vg/$lv"
+mount "$DM_DEV_DIR/$vg/$lv" "$mount_dir"
+df --output=size "$mount_dir" |tee df1
+dd if=/dev/zero of="$mount_dir/zeros1" bs=1M count=200 conv=fdatasync
+lvextend --fs resize -L+200M $vg/$lv
+check lv_field $vg/$lv lv_size "456.00m"
+df --output=size "$mount_dir" |tee df2
+not diff df1 df2
+dd if=/dev/zero of="$mount_dir/zeros2" bs=1M count=200 conv=fdatasync
+umount "$mount_dir"
+lvchange -an $vg/$lv
+lvremove $vg/$lv
+
+# lvextend, xfs, active, mounted, --resizefs (same as --fs resize)
+lvcreate -n $lv -L 256M $vg
+mkfs.xfs "$DM_DEV_DIR/$vg/$lv"
+mount "$DM_DEV_DIR/$vg/$lv" "$mount_dir"
+df --output=size "$mount_dir" |tee df1
+dd if=/dev/zero of="$mount_dir/zeros1" bs=1M count=200 conv=fdatasync
+lvextend --resizefs -L+200M $vg/$lv
+check lv_field $vg/$lv lv_size "456.00m"
+df --output=size "$mount_dir" |tee df2
+not diff df1 df2
+dd if=/dev/zero of="$mount_dir/zeros2" bs=1M count=200 conv=fdatasync
+umount "$mount_dir"
+lvchange -an $vg/$lv
+lvremove $vg/$lv
+
+# lvextend, xfs, active, mounted, --fs resize_remount (same as --fs resize)
+lvcreate -n $lv -L 256M $vg
+mkfs.xfs "$DM_DEV_DIR/$vg/$lv"
+mount "$DM_DEV_DIR/$vg/$lv" "$mount_dir"
+df --output=size "$mount_dir" |tee df1
+dd if=/dev/zero of="$mount_dir/zeros1" bs=1M count=200 conv=fdatasync
+lvextend --fs resize_remount -L+200M $vg/$lv
+check lv_field $vg/$lv lv_size "456.00m"
+df --output=size "$mount_dir" |tee df2
+not diff df1 df2
+dd if=/dev/zero of="$mount_dir/zeros2" bs=1M count=200 conv=fdatasync
+umount "$mount_dir"
+lvchange -an $vg/$lv
+lvremove $vg/$lv
+
+# lvextend, xfs, active, mounted, --fs resize_unmount
+lvcreate -n $lv -L 256M $vg
+mkfs.xfs "$DM_DEV_DIR/$vg/$lv"
+mount "$DM_DEV_DIR/$vg/$lv" "$mount_dir"
+df --output=size "$mount_dir" |tee df1
+dd if=/dev/zero of="$mount_dir/zeros1" bs=1M count=200 conv=fdatasync
+# xfs_growfs requires the fs to be mounted, so extending the lv is
+# succeeds, then the xfs extend fails because it cannot be done unmounted
+not lvextend --fs resize_unmount -L+200M $vg/$lv
+check lv_field $vg/$lv lv_size "456.00m"
+df -a | tee dfa
+grep "$mount_dir" dfa
+df --output=size "$mount_dir" |tee df2
+# fs not extended so fs size not changed
+diff df1 df2
+umount "$mount_dir"
+lvchange -an $vg/$lv
+lvremove $vg/$lv
+
+# lvextend, xfs, active, mounted, --fs resize_keepmount
+lvcreate -n $lv -L 256M $vg
+mkfs.xfs "$DM_DEV_DIR/$vg/$lv"
+mount "$DM_DEV_DIR/$vg/$lv" "$mount_dir"
+df --output=size "$mount_dir" |tee df1
+dd if=/dev/zero of="$mount_dir/zeros1" bs=1M count=200 conv=fdatasync
+lvextend --fs resize_keepmount -L+200M $vg/$lv
+check lv_field $vg/$lv lv_size "456.00m"
+df --output=size "$mount_dir" |tee df2
+not diff df1 df2
+dd if=/dev/zero of="$mount_dir/zeros2" bs=1M count=200 conv=fdatasync
+umount "$mount_dir"
+lvchange -an $vg/$lv
+lvremove $vg/$lv
+
+# lvextend, xfs, active, mounted, --fs resize_fsadm
+lvcreate -n $lv -L 256M $vg
+mkfs.xfs "$DM_DEV_DIR/$vg/$lv"
+mount "$DM_DEV_DIR/$vg/$lv" "$mount_dir"
+df --output=size "$mount_dir" |tee df1
+dd if=/dev/zero of="$mount_dir/zeros1" bs=1M count=200 conv=fdatasync
+lvextend --fs resize_fsadm -L+200M $vg/$lv
+check lv_field $vg/$lv lv_size "456.00m"
+df --output=size "$mount_dir" |tee df2
+not diff df1 df2
+dd if=/dev/zero of="$mount_dir/zeros2" bs=1M count=200 conv=fdatasync
+umount "$mount_dir"
+lvchange -an $vg/$lv
+lvremove $vg/$lv
+
+# lvextend, xfs, active, unmounted, --fs resize_keepmount
+lvcreate -n $lv -L 256M $vg
+mkfs.xfs "$DM_DEV_DIR/$vg/$lv"
+mount "$DM_DEV_DIR/$vg/$lv" "$mount_dir"
+df --output=size "$mount_dir" |tee df1
+dd if=/dev/zero of="$mount_dir/zeros1" bs=1M count=200 conv=fdatasync
+umount "$mount_dir"
+# xfs_growfs requires the fs to be mounted to grow, so resize_keepmount
+# with an unmounted fs fails
+not lvextend --fs resize_keepmount -L+200M $vg/$lv
+check lv_field $vg/$lv lv_size "456.00m"
+mount "$DM_DEV_DIR/$vg/$lv" "$mount_dir"
+df --output=size "$mount_dir" |tee df2
+# fs not extended so fs size not changed
+diff df1 df2
+umount "$mount_dir"
+lvchange -an $vg/$lv
+lvremove $vg/$lv
+
+# lvextend, xfs, active, unmounted, --fs resize
+lvcreate -n $lv -L 256M $vg
+mkfs.xfs "$DM_DEV_DIR/$vg/$lv"
+mount "$DM_DEV_DIR/$vg/$lv" "$mount_dir"
+df --output=size "$mount_dir" |tee df1
+dd if=/dev/zero of="$mount_dir/zeros1" bs=1M count=200 conv=fdatasync
+umount "$mount_dir"
+# --yes needed because mount changes are required and plain "resize"
+# fsopt did not specify if the user wants to change mount state
+lvextend --yes --fs resize -L+200M $vg/$lv
+check lv_field $vg/$lv lv_size "456.00m"
+mount "$DM_DEV_DIR/$vg/$lv" "$mount_dir"
+df --output=size "$mount_dir" |tee df2
+not diff df1 df2
+dd if=/dev/zero of="$mount_dir/zeros2" bs=1M count=200 conv=fdatasync
+umount "$mount_dir"
+lvchange -an $vg/$lv
+lvremove $vg/$lv
+
+# lvextend, xfs, active, unmounted, --fs resize_fsadm
+lvcreate -n $lv -L 256M $vg
+mkfs.xfs "$DM_DEV_DIR/$vg/$lv"
+mount "$DM_DEV_DIR/$vg/$lv" "$mount_dir"
+df --output=size "$mount_dir" |tee df1
+dd if=/dev/zero of="$mount_dir/zeros1" bs=1M count=200 conv=fdatasync
+umount "$mount_dir"
+lvextend --fs resize_fsadm -L+200M $vg/$lv
+check lv_field $vg/$lv lv_size "456.00m"
+mount "$DM_DEV_DIR/$vg/$lv" "$mount_dir"
+df --output=size "$mount_dir" |tee df2
+not diff df1 df2
+dd if=/dev/zero of="$mount_dir/zeros2" bs=1M count=200 conv=fdatasync
+umount "$mount_dir"
+lvchange -an $vg/$lv
+lvremove $vg/$lv
+
+
+#
+# lvreduce, no fs
+#
+
+# lvreduce, no fs, active, no --fs setting is same as --fs checksize
+lvcreate -n $lv -L 456M $vg
+aux wipefs_a "$DM_DEV_DIR/$vg/$lv"
+lvreduce -L-200M $vg/$lv
+check lv_field $vg/$lv lv_size "256.00m"
+lvchange -an $vg/$lv
+lvremove $vg/$lv
+
+# lvreduce, no fs, inactive, no --fs setting is same as --fs checksize
+lvcreate -n $lv -L 456M $vg
+aux wipefs_a "$DM_DEV_DIR/$vg/$lv"
+lvchange -an $vg/$lv
+# lv must be active to do checksize, which is the default with no --fs setting
+not lvreduce -L-200M $vg/$lv
+check lv_field $vg/$lv lv_size "456.00m"
+lvchange -an $vg/$lv
+lvremove $vg/$lv
+
+# lvreduce, no fs, active, --fs resize does nothing with no fs found
+lvcreate -n $lv -L 456M $vg
+aux wipefs_a "$DM_DEV_DIR/$vg/$lv"
+lvreduce -L-200M --fs resize $vg/$lv
+check lv_field $vg/$lv lv_size "256.00m"
+lvchange -an $vg/$lv
+lvremove $vg/$lv
+
+# lvreduce, no fs, inactive, --fs ignore
+lvcreate -n $lv -L 456M $vg
+aux wipefs_a "$DM_DEV_DIR/$vg/$lv"
+lvchange -an $vg/$lv
+lvreduce -L-200M --fs ignore $vg/$lv
+check lv_field $vg/$lv lv_size "256.00m"
+lvchange -an $vg/$lv
+lvremove $vg/$lv
+
+
+#
+# lvreduce, ext4, no --fs setting and the equivalent --fs checksize
+# i.e. fs is not resized
+#
+
+# lvreduce, ext4, active, mounted, no --fs setting is same as --fs checksize
+# fs smaller than the reduced size
+lvcreate -n $lv -L 200M $vg
+mkfs.ext4 "$DM_DEV_DIR/$vg/$lv"
+lvextend -L+256M $vg/$lv
+mount "$DM_DEV_DIR/$vg/$lv" "$mount_dir"
+dd if=/dev/zero of="$mount_dir/zeros1" bs=1M count=128 conv=fdatasync
+df --output=size "$mount_dir" |tee df1
+# fs is 200M, reduced size is 216M, so no fs reduce is needed
+# todo: check that resize2fs was not run?
+lvreduce -L216M $vg/$lv
+check lv_field $vg/$lv lv_size "216.00m"
+df --output=size "$mount_dir" |tee df2
+# fs size unchanged
+diff df1 df2
+umount "$mount_dir"
+lvchange -an $vg/$lv
+lvremove $vg/$lv
+
+# lvreduce, ext4, active, mounted, no --fs setting is same as --fs checksize
+# fs equal to the reduced size
+lvcreate -n $lv -L 200M $vg
+mkfs.ext4 "$DM_DEV_DIR/$vg/$lv"
+lvextend -L+256M $vg/$lv
+mount "$DM_DEV_DIR/$vg/$lv" "$mount_dir"
+dd if=/dev/zero of="$mount_dir/zeros1" bs=1M count=128 conv=fdatasync
+df --output=size "$mount_dir" |tee df1
+# fs is 200M, reduced size is 200M, so no fs reduce is needed
+# todo: check that resize2fs was not run?
+lvreduce -L200M $vg/$lv
+check lv_field $vg/$lv lv_size "200.00m"
+df --output=size "$mount_dir" |tee df2
+# fs size unchanged
+diff df1 df2
+umount "$mount_dir"
+lvchange -an $vg/$lv
+lvremove $vg/$lv
+
+# lvreduce, ext4, active, mounted, no --fs setting is same as --fs checksize
+# fs larger than the reduced size, fs not using reduced space
+lvcreate -n $lv -L 456M $vg
+mkfs.ext4 "$DM_DEV_DIR/$vg/$lv"
+mount "$DM_DEV_DIR/$vg/$lv" "$mount_dir"
+dd if=/dev/zero of="$mount_dir/zeros1" bs=1M count=100 conv=fdatasync
+df --output=size "$mount_dir" |tee df1
+# lvreduce fails because fs needs to be reduced and checksize does not resize
+not lvreduce -L-200M $vg/$lv
+check lv_field $vg/$lv lv_size "456.00m"
+df --output=size "$mount_dir" |tee df2
+# fs size unchanged
+diff df1 df2
+umount "$mount_dir"
+lvchange -an $vg/$lv
+lvremove $vg/$lv
+
+# lvreduce, ext4, active, mounted, no --fs setting is same as --fs checksize
+# fs larger than the reduced size, fs is using reduced space
+lvcreate -n $lv -L 456M $vg
+mkfs.ext4 "$DM_DEV_DIR/$vg/$lv"
+mount "$DM_DEV_DIR/$vg/$lv" "$mount_dir"
+dd if=/dev/zero of="$mount_dir/zeros1" bs=1M count=300 conv=fdatasync
+df --output=size "$mount_dir" |tee df1
+# lvreduce fails because fs needs to be reduced and checksize does not resize
+not lvreduce -L-200M $vg/$lv
+check lv_field $vg/$lv lv_size "456.00m"
+df --output=size "$mount_dir" |tee df2
+# fs size unchanged
+diff df1 df2
+umount "$mount_dir"
+lvchange -an $vg/$lv
+lvremove $vg/$lv
+
+# repeat lvreduce tests with unmounted instead of mounted fs
+
+# lvreduce, ext4, active, unmounted, no --fs setting is same as --fs checksize
+# fs smaller than the reduced size
+lvcreate -n $lv -L 200M $vg
+mkfs.ext4 "$DM_DEV_DIR/$vg/$lv"
+lvextend -L+256M $vg/$lv
+mount "$DM_DEV_DIR/$vg/$lv" "$mount_dir"
+dd if=/dev/zero of="$mount_dir/zeros1" bs=1M count=128 conv=fdatasync
+df --output=size "$mount_dir" |tee df1
+umount "$mount_dir"
+# fs is 200M, reduced size is 216M, so no fs reduce is needed
+lvreduce -L216M $vg/$lv
+check lv_field $vg/$lv lv_size "216.00m"
+mount "$DM_DEV_DIR/$vg/$lv" "$mount_dir"
+df --output=size "$mount_dir" |tee df2
+# fs size unchanged
+diff df1 df2
+umount "$mount_dir"
+lvchange -an $vg/$lv
+lvremove $vg/$lv
+
+# lvreduce, ext4, active, unmounted, no --fs setting is same as --fs checksize
+# fs equal to the reduced size
+lvcreate -n $lv -L 200M $vg
+mkfs.ext4 "$DM_DEV_DIR/$vg/$lv"
+lvextend -L+256M $vg/$lv
+mount "$DM_DEV_DIR/$vg/$lv" "$mount_dir"
+dd if=/dev/zero of="$mount_dir/zeros1" bs=1M count=128 conv=fdatasync
+df --output=size "$mount_dir" |tee df1
+umount "$mount_dir"
+# fs is 200M, reduced size is 200M, so no fs reduce is needed
+lvreduce -L200M $vg/$lv
+check lv_field $vg/$lv lv_size "200.00m"
+mount "$DM_DEV_DIR/$vg/$lv" "$mount_dir"
+df --output=size "$mount_dir" |tee df2
+# fs size unchanged
+diff df1 df2
+umount "$mount_dir"
+lvchange -an $vg/$lv
+lvremove $vg/$lv
+
+# lvreduce, ext4, active, unmounted, no --fs setting is same as --fs checksize
+# fs larger than the reduced size, fs not using reduced space
+lvcreate -n $lv -L 456M $vg
+mkfs.ext4 "$DM_DEV_DIR/$vg/$lv"
+mount "$DM_DEV_DIR/$vg/$lv" "$mount_dir"
+dd if=/dev/zero of="$mount_dir/zeros1" bs=1M count=100 conv=fdatasync
+df --output=size "$mount_dir" |tee df1
+umount "$mount_dir"
+# lvreduce fails because fs needs to be reduced and checksize does not resize
+not lvreduce -L-200M $vg/$lv
+check lv_field $vg/$lv lv_size "456.00m"
+mount "$DM_DEV_DIR/$vg/$lv" "$mount_dir"
+df --output=size "$mount_dir" |tee df2
+# fs size unchanged
+diff df1 df2
+umount "$mount_dir"
+lvchange -an $vg/$lv
+lvremove $vg/$lv
+
+# lvreduce, ext4, active, unmounted, no --fs setting is same as --fs checksize
+# fs larger than the reduced size, fs is using reduced space
+lvcreate -n $lv -L 456M $vg
+mkfs.ext4 "$DM_DEV_DIR/$vg/$lv"
+mount "$DM_DEV_DIR/$vg/$lv" "$mount_dir"
+dd if=/dev/zero of="$mount_dir/zeros1" bs=1M count=300 conv=fdatasync
+df --output=size "$mount_dir" |tee df1
+umount "$mount_dir"
+# lvreduce fails because fs needs to be reduced and checksize does not resize
+not lvreduce -L-200M $vg/$lv
+check lv_field $vg/$lv lv_size "456.00m"
+mount "$DM_DEV_DIR/$vg/$lv" "$mount_dir"
+df --output=size "$mount_dir" |tee df2
+# fs size unchanged
+diff df1 df2
+umount "$mount_dir"
+lvchange -an $vg/$lv
+lvremove $vg/$lv
+
+# repeat a couple prev lvreduce that had no --fs setting,
+# now using --fs checksize to verify it's the same as using no --fs set
+
+# lvreduce, ext4, active, mounted, --fs checksize (same as no --fs set)
+# fs larger than the reduced size, fs not using reduced space
+lvcreate -n $lv -L 456M $vg
+mkfs.ext4 "$DM_DEV_DIR/$vg/$lv"
+mount "$DM_DEV_DIR/$vg/$lv" "$mount_dir"
+dd if=/dev/zero of="$mount_dir/zeros1" bs=1M count=100 conv=fdatasync
+df --output=size "$mount_dir" |tee df1
+# lvreduce fails because fs needs to be reduced and checksize does not resize
+not lvreduce --fs checksize -L-200M $vg/$lv
+check lv_field $vg/$lv lv_size "456.00m"
+df --output=size "$mount_dir" |tee df2
+# fs size unchanged
+diff df1 df2
+umount "$mount_dir"
+lvchange -an $vg/$lv
+lvremove $vg/$lv
+
+# lvreduce, ext4, active, unmounted, --fs checksize (same as no --fs set)
+# fs smaller than the reduced size
+lvcreate -n $lv -L 200M $vg
+mkfs.ext4 "$DM_DEV_DIR/$vg/$lv"
+lvextend -L+256M $vg/$lv
+mount "$DM_DEV_DIR/$vg/$lv" "$mount_dir"
+dd if=/dev/zero of="$mount_dir/zeros1" bs=1M count=128 conv=fdatasync
+df --output=size "$mount_dir" |tee df1
+umount "$mount_dir"
+# fs is 200M, reduced size is 216M, so no fs reduce is needed
+lvreduce --fs checksize -L216M $vg/$lv
+check lv_field $vg/$lv lv_size "216.00m"
+mount "$DM_DEV_DIR/$vg/$lv" "$mount_dir"
+df --output=size "$mount_dir" |tee df2
+# fs size unchanged
+diff df1 df2
+umount "$mount_dir"
+lvchange -an $vg/$lv
+lvremove $vg/$lv
+
+# lvreduce with inactive and no --fs setting fails because
+# default behavior is fs checksize which requires active lv
+
+# lvreduce, ext4, inactive, no --fs setting same as --fs checksize
+# fs larger than the reduced size, fs not using reduced space
+lvcreate -n $lv -L 456M $vg
+mkfs.ext4 "$DM_DEV_DIR/$vg/$lv"
+mount "$DM_DEV_DIR/$vg/$lv" "$mount_dir"
+dd if=/dev/zero of="$mount_dir/zeros1" bs=1M count=100 conv=fdatasync
+df --output=size "$mount_dir" |tee df1
+umount "$mount_dir"
+lvchange -an $vg/$lv
+# lvreduce fails because default is --fs checksize which requires active lv
+not lvreduce -L-200M $vg/$lv
+check lv_field $vg/$lv lv_size "456.00m"
+lvremove $vg/$lv
+
+#
+# lvreduce, ext4, --fs resize*
+#
+
+# lvreduce, ext4, active, mounted, --fs resize
+# fs smaller than the reduced size
+lvcreate -n $lv -L 200M $vg
+mkfs.ext4 "$DM_DEV_DIR/$vg/$lv"
+lvextend -L+256M $vg/$lv
+mount "$DM_DEV_DIR/$vg/$lv" "$mount_dir"
+dd if=/dev/zero of="$mount_dir/zeros1" bs=1M count=128 conv=fdatasync
+df --output=size "$mount_dir" |tee df1
+# fs is 200M, reduced size is 216M, so no fs reduce is needed
+lvreduce -L216M $vg/$lv
+check lv_field $vg/$lv lv_size "216.00m"
+df --output=size "$mount_dir" |tee df2
+# fs size unchanged
+diff df1 df2
+umount "$mount_dir"
+lvchange -an $vg/$lv
+lvremove $vg/$lv
+
+# lvreduce, ext4, active, mounted, --fs resize
+# fs equal to the reduced size
+lvcreate -n $lv -L 200M $vg
+mkfs.ext4 "$DM_DEV_DIR/$vg/$lv"
+lvextend -L+256M $vg/$lv
+mount "$DM_DEV_DIR/$vg/$lv" "$mount_dir"
+dd if=/dev/zero of="$mount_dir/zeros1" bs=1M count=128 conv=fdatasync
+df --output=size "$mount_dir" |tee df1
+# fs is 200M, reduced size is 200M, so no fs reduce is needed
+lvreduce -L200M $vg/$lv
+check lv_field $vg/$lv lv_size "200.00m"
+df --output=size "$mount_dir" |tee df2
+# fs size unchanged
+diff df1 df2
+umount "$mount_dir"
+lvchange -an $vg/$lv
+lvremove $vg/$lv
+
+# lvreduce, ext4, active, mounted, --fs resize
+# fs larger than the reduced size, fs not using reduced space
+lvcreate -n $lv -L 456M $vg
+mkfs.ext4 "$DM_DEV_DIR/$vg/$lv"
+mount "$DM_DEV_DIR/$vg/$lv" "$mount_dir"
+dd if=/dev/zero of="$mount_dir/zeros1" bs=1M count=100 conv=fdatasync
+df --output=size "$mount_dir" |tee df1
+# lvreduce runs resize2fs to shrink the fs
+lvreduce --yes --fs resize -L-200M $vg/$lv
+check lv_field $vg/$lv lv_size "256.00m"
+df --output=size "$mount_dir" |tee df2
+# fs size is changed
+not diff df1 df2
+umount "$mount_dir"
+lvchange -an $vg/$lv
+lvremove $vg/$lv
+
+# lvreduce, ext4, active, mounted, --fs resize
+# fs larger than the reduced size, fs is using reduced space
+lvcreate -n $lv -L 456M $vg
+mkfs.ext4 "$DM_DEV_DIR/$vg/$lv"
+mount "$DM_DEV_DIR/$vg/$lv" "$mount_dir"
+dd if=/dev/zero of="$mount_dir/zeros1" bs=1M count=100 conv=fdatasync
+dd if=/dev/zero of="$mount_dir/zeros2" bs=1M count=300 conv=fdatasync
+df --output=size "$mount_dir" |tee df1
+# lvreduce runs resize2fs to shrink the fs but resize2fs fails
+# the fs is not remounted after resize2fs fails because the
+# resize failure might leave the fs in an unknown state
+not lvreduce --yes --fs resize -L-200M $vg/$lv
+check lv_field $vg/$lv lv_size "456.00m"
+mount "$DM_DEV_DIR/$vg/$lv" "$mount_dir"
+df --output=size "$mount_dir" |tee df2
+# fs size is unchanged
+diff df1 df2
+umount "$mount_dir"
+lvchange -an $vg/$lv
+lvremove $vg/$lv
+
+# repeat with unmounted instead of mounted
+
+# lvreduce, ext4, active, unmounted, --fs resize
+# fs smaller than the reduced size
+lvcreate -n $lv -L 200M $vg
+mkfs.ext4 "$DM_DEV_DIR/$vg/$lv"
+lvextend -L+256M $vg/$lv
+mount "$DM_DEV_DIR/$vg/$lv" "$mount_dir"
+dd if=/dev/zero of="$mount_dir/zeros1" bs=1M count=128 conv=fdatasync
+df --output=size "$mount_dir" |tee df1
+umount "$mount_dir"
+# fs is 200M, reduced size is 216M, so no fs reduce is needed
+lvreduce -L216M $vg/$lv
+check lv_field $vg/$lv lv_size "216.00m"
+mount "$DM_DEV_DIR/$vg/$lv" "$mount_dir"
+df --output=size "$mount_dir" |tee df2
+# fs size unchanged
+diff df1 df2
+umount "$mount_dir"
+lvchange -an $vg/$lv
+lvremove $vg/$lv
+
+# lvreduce, ext4, active, unmounted, --fs resize
+# fs equal to the reduced size
+lvcreate -n $lv -L 200M $vg
+mkfs.ext4 "$DM_DEV_DIR/$vg/$lv"
+lvextend -L+256M $vg/$lv
+mount "$DM_DEV_DIR/$vg/$lv" "$mount_dir"
+dd if=/dev/zero of="$mount_dir/zeros1" bs=1M count=128 conv=fdatasync
+df --output=size "$mount_dir" |tee df1
+umount "$mount_dir"
+# fs is 200M, reduced size is 200M, so no fs reduce is needed
+lvreduce -L200M $vg/$lv
+check lv_field $vg/$lv lv_size "200.00m"
+mount "$DM_DEV_DIR/$vg/$lv" "$mount_dir"
+df --output=size "$mount_dir" |tee df2
+# fs size unchanged
+diff df1 df2
+umount "$mount_dir"
+lvchange -an $vg/$lv
+lvremove $vg/$lv
+
+# lvreduce, ext4, active, unmounted, --fs resize
+# fs larger than the reduced size, fs not using reduced space
+lvcreate -n $lv -L 456M $vg
+mkfs.ext4 "$DM_DEV_DIR/$vg/$lv"
+mount "$DM_DEV_DIR/$vg/$lv" "$mount_dir"
+dd if=/dev/zero of="$mount_dir/zeros1" bs=1M count=100 conv=fdatasync
+df --output=size "$mount_dir" |tee df1
+umount "$mount_dir"
+# lvreduce runs resize2fs to shrink the fs
+lvreduce --fs resize -L-200M $vg/$lv
+check lv_field $vg/$lv lv_size "256.00m"
+mount "$DM_DEV_DIR/$vg/$lv" "$mount_dir"
+df --output=size "$mount_dir" |tee df2
+# fs size is changed
+not diff df1 df2
+umount "$mount_dir"
+lvchange -an $vg/$lv
+lvremove $vg/$lv
+
+# lvreduce, ext4, active, unmounted, --fs resize
+# fs larger than the reduced size, fs is using reduced space
+lvcreate -n $lv -L 456M $vg
+mkfs.ext4 "$DM_DEV_DIR/$vg/$lv"
+mount "$DM_DEV_DIR/$vg/$lv" "$mount_dir"
+dd if=/dev/zero of="$mount_dir/zeros1" bs=1M count=100 conv=fdatasync
+dd if=/dev/zero of="$mount_dir/zeros2" bs=1M count=300 conv=fdatasync
+df --output=size "$mount_dir" |tee df1
+umount "$mount_dir"
+# lvreduce runs resize2fs to shrink the fs but resize2fs fails
+not lvreduce --yes --fs resize -L-200M $vg/$lv
+check lv_field $vg/$lv lv_size "456.00m"
+mount "$DM_DEV_DIR/$vg/$lv" "$mount_dir"
+df --output=size "$mount_dir" |tee df2
+# fs size is unchanged
+diff df1 df2
+umount "$mount_dir"
+lvchange -an $vg/$lv
+lvremove $vg/$lv
+
+# repeat resizes that shrink the fs, replacing --fs resize with
+# --fs resize_keepmount | resize_unmount | resize_fsadm
+# while mounted and unmounted
+
+# lvreduce, ext4, active, mounted, --fs resize_keepmount
+# fs larger than the reduced size, fs not using reduced space
+lvcreate -n $lv -L 456M $vg
+mkfs.ext4 "$DM_DEV_DIR/$vg/$lv"
+mount "$DM_DEV_DIR/$vg/$lv" "$mount_dir"
+dd if=/dev/zero of="$mount_dir/zeros1" bs=1M count=100 conv=fdatasync
+df --output=size "$mount_dir" |tee df1
+# lvreduce needs to unmount to run resize2fs but keepmount doesn't let it
+not lvreduce --fs resize_keepmount -L-200M $vg/$lv
+check lv_field $vg/$lv lv_size "456.00m"
+df --output=size "$mount_dir" |tee df2
+# fs size is unchanged
+diff df1 df2
+umount "$mount_dir"
+lvchange -an $vg/$lv
+lvremove $vg/$lv
+
+# lvreduce, ext4, active, unmounted, --fs resize_keepmount
+# fs larger than the reduced size, fs not using reduced space
+lvcreate -n $lv -L 456M $vg
+mkfs.ext4 "$DM_DEV_DIR/$vg/$lv"
+mount "$DM_DEV_DIR/$vg/$lv" "$mount_dir"
+dd if=/dev/zero of="$mount_dir/zeros1" bs=1M count=100 conv=fdatasync
+df --output=size "$mount_dir" |tee df1
+umount "$mount_dir"
+# lvreduce runs resize2fs to shrink the fs
+lvreduce --fs resize_keepmount -L-200M $vg/$lv
+check lv_field $vg/$lv lv_size "256.00m"
+mount "$DM_DEV_DIR/$vg/$lv" "$mount_dir"
+df --output=size "$mount_dir" |tee df2
+# fs size is changed
+not diff df1 df2
+umount "$mount_dir"
+lvchange -an $vg/$lv
+lvremove $vg/$lv
+
+# lvreduce, ext4, active, mounted, --fs resize_unmount
+# fs larger than the reduced size, fs not using reduced space
+lvcreate -n $lv -L 456M $vg
+mkfs.ext4 "$DM_DEV_DIR/$vg/$lv"
+mount "$DM_DEV_DIR/$vg/$lv" "$mount_dir"
+dd if=/dev/zero of="$mount_dir/zeros1" bs=1M count=100 conv=fdatasync
+df --output=size "$mount_dir" |tee df1
+# lvreduce runs resize2fs to shrink the fs
+# resize_unmount leaves the fs unmounted
+lvreduce --fs resize_unmount -L-200M $vg/$lv
+check lv_field $vg/$lv lv_size "256.00m"
+mount "$DM_DEV_DIR/$vg/$lv" "$mount_dir"
+df --output=size "$mount_dir" |tee df2
+# fs size is changed
+not diff df1 df2
+umount "$mount_dir"
+lvchange -an $vg/$lv
+lvremove $vg/$lv
+
+# lvreduce, ext4, active, unmounted, --fs resize_unmount
+# fs larger than the reduced size, fs not using reduced space
+lvcreate -n $lv -L 456M $vg
+mkfs.ext4 "$DM_DEV_DIR/$vg/$lv"
+mount "$DM_DEV_DIR/$vg/$lv" "$mount_dir"
+dd if=/dev/zero of="$mount_dir/zeros1" bs=1M count=100 conv=fdatasync
+df --output=size "$mount_dir" |tee df1
+umount "$mount_dir"
+# lvreduce runs resize2fs to shrink the fs
+lvreduce --fs resize_unmount -L-200M $vg/$lv
+check lv_field $vg/$lv lv_size "256.00m"
+mount "$DM_DEV_DIR/$vg/$lv" "$mount_dir"
+df --output=size "$mount_dir" |tee df2
+# fs size is changed
+not diff df1 df2
+umount "$mount_dir"
+lvchange -an $vg/$lv
+lvremove $vg/$lv
+
+# lvreduce, ext4, active, mounted, --fs resize_fsadm
+# fs larger than the reduced size, fs not using reduced space
+lvcreate -n $lv -L 456M $vg
+mkfs.ext4 "$DM_DEV_DIR/$vg/$lv"
+mount "$DM_DEV_DIR/$vg/$lv" "$mount_dir"
+dd if=/dev/zero of="$mount_dir/zeros1" bs=1M count=100 conv=fdatasync
+df --output=size "$mount_dir" |tee df1
+# lvreduce runs resize2fs to shrink the fs
+lvreduce --yes --fs resize_fsadm -L-200M $vg/$lv
+check lv_field $vg/$lv lv_size "256.00m"
+df --output=size "$mount_dir" |tee df2
+# fs size is changed
+not diff df1 df2
+umount "$mount_dir"
+lvchange -an $vg/$lv
+lvremove $vg/$lv
+
+# lvreduce, ext4, active, unmounted, --fs resize_fsadm
+# fs larger than the reduced size, fs not using reduced space
+lvcreate -n $lv -L 456M $vg
+mkfs.ext4 "$DM_DEV_DIR/$vg/$lv"
+mount "$DM_DEV_DIR/$vg/$lv" "$mount_dir"
+dd if=/dev/zero of="$mount_dir/zeros1" bs=1M count=100 conv=fdatasync
+df --output=size "$mount_dir" |tee df1
+umount "$mount_dir"
+# lvreduce runs resize2fs to shrink the fs
+lvreduce --fs resize_fsadm -L-200M $vg/$lv
+check lv_field $vg/$lv lv_size "256.00m"
+mount "$DM_DEV_DIR/$vg/$lv" "$mount_dir"
+df --output=size "$mount_dir" |tee df2
+# fs size is changed
+not diff df1 df2
+umount "$mount_dir"
+lvchange -an $vg/$lv
+lvremove $vg/$lv
+
+#
+# lvreduce, xfs (xfs does not support shrinking)
+#
+
+# lvreduce, xfs, active, mounted, no --fs setting is same as --fs checksize
+# fs smaller than the reduced size
+lvcreate -n $lv -L 200M $vg
+mkfs.xfs "$DM_DEV_DIR/$vg/$lv"
+lvextend -L+256M $vg/$lv
+mount "$DM_DEV_DIR/$vg/$lv" "$mount_dir"
+dd if=/dev/zero of="$mount_dir/zeros1" bs=1M count=128 conv=fdatasync
+df --output=size "$mount_dir" |tee df1
+# fs is 200M, reduced size is 216M, so no fs reduce is needed
+lvreduce -L216M $vg/$lv
+check lv_field $vg/$lv lv_size "216.00m"
+df --output=size "$mount_dir" |tee df2
+# fs size unchanged
+diff df1 df2
+umount "$mount_dir"
+lvchange -an $vg/$lv
+lvremove $vg/$lv
+
+# lvreduce, xfs, active, mounted, no --fs setting is same as --fs checksize
+# fs equal to the reduced size
+lvcreate -n $lv -L 200M $vg
+mkfs.xfs "$DM_DEV_DIR/$vg/$lv"
+lvextend -L+256M $vg/$lv
+mount "$DM_DEV_DIR/$vg/$lv" "$mount_dir"
+dd if=/dev/zero of="$mount_dir/zeros1" bs=1M count=128 conv=fdatasync
+df --output=size "$mount_dir" |tee df1
+# fs is 200M, reduced size is 200M, so no fs reduce is needed
+lvreduce -L200M $vg/$lv
+check lv_field $vg/$lv lv_size "200.00m"
+df --output=size "$mount_dir" |tee df2
+# fs size unchanged
+diff df1 df2
+umount "$mount_dir"
+lvchange -an $vg/$lv
+lvremove $vg/$lv
+
+# lvreduce, xfs, active, mounted, no --fs setting is same as --fs checksize
+# fs larger than the reduced size, fs not using reduced space
+lvcreate -n $lv -L 456M $vg
+mkfs.xfs "$DM_DEV_DIR/$vg/$lv"
+mount "$DM_DEV_DIR/$vg/$lv" "$mount_dir"
+dd if=/dev/zero of="$mount_dir/zeros1" bs=1M count=100 conv=fdatasync
+df --output=size "$mount_dir" |tee df1
+# lvreduce fails because fs needs to be reduced
+not lvreduce -L-200M $vg/$lv
+check lv_field $vg/$lv lv_size "456.00m"
+df --output=size "$mount_dir" |tee df2
+# fs size unchanged
+diff df1 df2
+umount "$mount_dir"
+lvchange -an $vg/$lv
+lvremove $vg/$lv
+
+# lvreduce, xfs, active, unmounted, no --fs setting is same as --fs checksize
+# fs smaller than the reduced size
+lvcreate -n $lv -L 200M $vg
+mkfs.xfs "$DM_DEV_DIR/$vg/$lv"
+lvextend -L+256M $vg/$lv
+mount "$DM_DEV_DIR/$vg/$lv" "$mount_dir"
+dd if=/dev/zero of="$mount_dir/zeros1" bs=1M count=128 conv=fdatasync
+df --output=size "$mount_dir" |tee df1
+umount "$mount_dir"
+# fs is 200M, reduced size is 216M, so no fs reduce is needed
+lvreduce -L216M $vg/$lv
+check lv_field $vg/$lv lv_size "216.00m"
+mount "$DM_DEV_DIR/$vg/$lv" "$mount_dir"
+df --output=size "$mount_dir" |tee df2
+# fs size unchanged
+diff df1 df2
+umount "$mount_dir"
+lvchange -an $vg/$lv
+lvremove $vg/$lv
+
+# lvreduce, xfs, active, mounted, --fs resize
+# fs smaller than the reduced size
+lvcreate -n $lv -L 200M $vg
+mkfs.xfs "$DM_DEV_DIR/$vg/$lv"
+lvextend -L+256M $vg/$lv
+mount "$DM_DEV_DIR/$vg/$lv" "$mount_dir"
+dd if=/dev/zero of="$mount_dir/zeros1" bs=1M count=128 conv=fdatasync
+df --output=size "$mount_dir" |tee df1
+# fs is 200M, reduced size is 216M, so no fs reduce is needed
+lvreduce -L216M $vg/$lv
+check lv_field $vg/$lv lv_size "216.00m"
+df --output=size "$mount_dir" |tee df2
+# fs size unchanged
+diff df1 df2
+umount "$mount_dir"
+lvchange -an $vg/$lv
+lvremove $vg/$lv
+
+# lvreduce, xfs, active, mounted, --fs resize
+# fs larger than the reduced size, fs not using reduced space
+lvcreate -n $lv -L 456M $vg
+mkfs.xfs "$DM_DEV_DIR/$vg/$lv"
+mount "$DM_DEV_DIR/$vg/$lv" "$mount_dir"
+dd if=/dev/zero of="$mount_dir/zeros1" bs=1M count=100 conv=fdatasync
+df --output=size "$mount_dir" |tee df1
+# lvreduce fails because xfs cannot shrink
+not lvreduce --yes --fs resize -L-200M $vg/$lv
+check lv_field $vg/$lv lv_size "456.00m"
+df --output=size "$mount_dir" |tee df2
+# fs size unchanged
+diff df1 df2
+umount "$mount_dir"
+lvchange -an $vg/$lv
+lvremove $vg/$lv
+
+# lvreduce, xfs, active, unmounted, --fs resize*
+# fs larger than the reduced size, fs not using reduced space
+lvcreate -n $lv -L 456M $vg
+mkfs.xfs "$DM_DEV_DIR/$vg/$lv"
+mount "$DM_DEV_DIR/$vg/$lv" "$mount_dir"
+dd if=/dev/zero of="$mount_dir/zeros1" bs=1M count=100 conv=fdatasync
+df --output=size "$mount_dir" |tee df1
+umount "$mount_dir"
+# lvreduce fails because xfs cannot shrink
+not lvreduce --yes --fs resize -L-200M $vg/$lv
+check lv_field $vg/$lv lv_size "456.00m"
+not lvreduce --yes --fs resize_remount -L-200M $vg/$lv
+check lv_field $vg/$lv lv_size "456.00m"
+not lvreduce --yes --fs resize_keepmount -L-200M $vg/$lv
+check lv_field $vg/$lv lv_size "456.00m"
+not lvreduce --yes --fs resize_unmount -L-200M $vg/$lv
+check lv_field $vg/$lv lv_size "456.00m"
+not lvreduce --yes --fs resize_fsadm -L-200M $vg/$lv
+check lv_field $vg/$lv lv_size "456.00m"
+not lvreduce --yes --resizefs -L-200M $vg/$lv
+check lv_field $vg/$lv lv_size "456.00m"
+mount "$DM_DEV_DIR/$vg/$lv" "$mount_dir"
+df --output=size "$mount_dir" |tee df2
+# fs size unchanged
+diff df1 df2
+umount "$mount_dir"
+lvchange -an $vg/$lv
+lvremove $vg/$lv
+
+vgremove -ff $vg
diff --git a/test/shell/lvresize-full.sh b/test/shell/lvresize-full.sh
index 91a709b8e..c295b003d 100644
--- a/test/shell/lvresize-full.sh
+++ b/test/shell/lvresize-full.sh
@@ -68,7 +68,10 @@ grep -v "20000 blocks" out
# Also check it fails when the user 'resize' volume without
# resizing fs and then retries with '-r'.
-lvreduce -f -l50%VG $vg/$lv1
-fail lvresize -r -f -l50%VG $vg/$lv1
+# The first lvreduce intentionally ignores the fs and intentionally
+# corrupts the fs so that the second lvresize will fail when it runs
+# fsck.
+lvreduce -f --fs ignore -l50%VG $vg/$lv1
+fail lvresize -r -f -l20%VG $vg/$lv1
lvremove -ff $vg
diff --git a/test/shell/lvresize-mirror.sh b/test/shell/lvresize-mirror.sh
index 61e4491bd..ee23f7f24 100644
--- a/test/shell/lvresize-mirror.sh
+++ b/test/shell/lvresize-mirror.sh
@@ -29,7 +29,7 @@ for deactivate in true false; do
check mirror_images_contiguous $vg $lv1
# reduce 2-way mirror
- lvreduce -f -l-2 $vg/$lv1
+ lvreduce -f --fs ignore -l-2 $vg/$lv1
check mirror $vg $lv1 "$dev3"
# extend 2-way mirror (cling if not contiguous)
diff --git a/test/shell/lvresize-rounding.sh b/test/shell/lvresize-rounding.sh
index 0cdce558f..c2251eb06 100644
--- a/test/shell/lvresize-rounding.sh
+++ b/test/shell/lvresize-rounding.sh
@@ -52,7 +52,7 @@ lvcreate -an -Zn -l1 -n $lv1 -i3 $vg
lvextend -l+100%FREE -i3 $vg/$lv1
check vg_field $vg vg_free_count 2
-lvreduce -f -l50%LV $vg/$lv1
+lvreduce -f --fs ignore -l50%LV $vg/$lv1
vgremove -f $vg
vgcreate $SHARED -s 4M $vg "$dev1" "$dev2" "$dev3"
@@ -72,21 +72,21 @@ lvextend -l+100%FREE $vg/lv
check vg_field $vg vg_free_count 0
# Rounds up and should reduce just by 3 extents
-lvreduce -f -l-4 $vg/lv
+lvreduce -f --fs ignore -l-4 $vg/lv
check vg_field $vg vg_free_count 3
# Should round up to 15 extents
lvextend -f -l+1 $vg/lv
check vg_field $vg vg_free_count 0
-lvreduce -f -l-4 $vg/lv
+lvreduce -f --fs ignore -l-4 $vg/lv
check vg_field $vg vg_free_count 3
lvextend -l90%VG $vg/lv
check vg_field $vg vg_free_count 0
-not lvreduce -f -l-10%LV $vg/lv
+not lvreduce -f --fs ignore -l-10%LV $vg/lv
check vg_field $vg vg_free_count 0
-lvreduce -f -l-20%LV $vg/lv
+lvreduce -f --fs ignore -l-20%LV $vg/lv
check vg_field $vg vg_free_count 3
diff --git a/tools/args.h b/tools/args.h
index baae333e1..ba94ce796 100644
--- a/tools/args.h
+++ b/tools/args.h
@@ -301,6 +301,27 @@ arg(foreign_ARG, '\0', "foreign", 0, 0, 0,
"Report/display foreign VGs that would otherwise be skipped.\n"
"See \\fBlvmsystemid\\fP(7) for more information about foreign VGs.\n")
+arg(fs_ARG, '\0', "fs", string_VAL, 0, 0,
+ "File system handling when resizing an LV.\n"
+ "\\fBchecksize\\fP: Only when reducing size, does nothing when extending.\n"
+ "Check the fs size and reduce the LV if the fs is not using the affected\n"
+ "space, i.e. the fs does not need to be shrunk. Fail the command without\n"
+ "reducing the fs or LV if the fs is using the affected space.\n"
+ "\\fBresize_remount\\fP: Resize the fs if needed. Mounts or unmounts the fs as\n"
+ "required (avoids mounting/unmounting when possible.)\n"
+ "Attempts to restore the original mount state when finished.\n"
+ "\\fBresize_keepmount\\fP: Resize the fs if needed, only if it can be done without\n"
+ "changing the current mount state. Fail the command without\n"
+ "resizing the fs or LV if an fs resize requires mounting or unmounting\n"
+ "\\fBresize_unmount\\fP: Resize the fs if needed, only while unmounted. Unmount the\n"
+ "fs if needed. Fail the command without resizing the fs\n"
+ "or LV if an fs resize is needed that requires the the fs to be mounted\n"
+ "\\fBresize\\fP: Equivalent to resize_remount.\n"
+ "\\fBresize_fsadm\\fP: Use the old method of calling fsadm to do handle the fs\n"
+ "(deprecated).\n"
+ "\\fBignore\\fP: Resize the LV without checking for or handling a file system.\n"
+ "WARNING: using ignore when reducing the LV size may cause data loss.\n")
+
arg(handlemissingpvs_ARG, '\0', "handlemissingpvs", 0, 0, 0,
"Allows a polling operation to continue when PVs are missing,\n"
"e.g. for repairs due to faulty devices.\n")
@@ -1447,7 +1468,8 @@ arg(readahead_ARG, 'r', "readahead", readahead_VAL, 0, 0,
"\\fBnone\\fP is equivalent to zero.\n")
arg(resizefs_ARG, 'r', "resizefs", 0, 0, 0,
- "Resize underlying filesystem together with the LV using \\fBfsadm\\fP(8).\n")
+ "Resize the file system on the LV.\n"
+ "Equivalent to --fs resize_remount. See --fs for more options.\n")
/* Not used */
arg(reset_ARG, 'R', "reset", 0, 0, 0, NULL)
diff --git a/tools/command-lines.in b/tools/command-lines.in
index dd033630b..13400e6f4 100644
--- a/tools/command-lines.in
+++ b/tools/command-lines.in
@@ -1379,7 +1379,7 @@ lvextend --size PSizeMB LV
OO: --alloc Alloc, --autobackup Bool, --force, --mirrors Number,
--nofsck, --nosync, --noudevsync, --reportformat ReportFmt, --resizefs,
--stripes Number, --stripesize SizeKB, --poolmetadatasize PSizeMB,
---type SegType
+--type SegType, --fs String
OP: PV ...
ID: lvextend_size
DESC: Extend an LV by a specified size.
@@ -1388,7 +1388,7 @@ lvextend LV PV ...
OO: --alloc Alloc, --autobackup Bool, --force, --mirrors Number,
--nofsck, --nosync, --noudevsync,
--reportformat ReportFmt, --resizefs, --stripes Number, --stripesize SizeKB,
---type SegType
+--type SegType, --fs String
ID: lvextend_pv
DESC: Extend an LV by specified PV extents.
@@ -1405,7 +1405,7 @@ lvextend --usepolicies LV_snapshot_thinpool_vdopool
OO: --alloc Alloc, --autobackup Bool, --force, --mirrors Number,
--nofsck, --nosync, --noudevsync,
--reportformat ReportFmt, --resizefs,
---type SegType
+--type SegType, --fs String
OP: PV ...
ID: lvextend_policy
DESC: Extend an LV according to a predefined policy.
@@ -1454,7 +1454,7 @@ DESC: Remove the devices file entry for the given PVID.
lvreduce --size NSizeMB LV
OO: --autobackup Bool, --force, --nofsck, --noudevsync,
---reportformat ReportFmt, --resizefs
+--reportformat ReportFmt, --resizefs, --fs String
ID: lvreduce_size
---
@@ -1484,7 +1484,7 @@ lvresize --size SSizeMB LV
OO: --alloc Alloc, --autobackup Bool, --force,
--nofsck, --nosync, --noudevsync, --reportformat ReportFmt, --resizefs,
--stripes Number, --stripesize SizeKB, --poolmetadatasize PSizeMB,
---type SegType
+--type SegType, --fs String
OP: PV ...
ID: lvresize_size
DESC: Resize an LV by a specified size.
@@ -1493,7 +1493,7 @@ lvresize LV PV ...
OO: --alloc Alloc, --autobackup Bool, --force,
--nofsck, --nosync, --noudevsync,
--reportformat ReportFmt, --resizefs, --stripes Number, --stripesize SizeKB,
---type SegType
+--type SegType, --fs String
ID: lvresize_pv
DESC: Resize an LV by specified PV extents.
diff --git a/tools/lvresize.c b/tools/lvresize.c
index a7148e52c..779ba7207 100644
--- a/tools/lvresize.c
+++ b/tools/lvresize.c
@@ -19,6 +19,7 @@ static int _lvresize_params(struct cmd_context *cmd, struct lvresize_params *lp)
{
const char *type_str = arg_str_value(cmd, type_ARG, NULL);
int only_linear = 0;
+ int set_fsopt = 0;
int set_extents_and_size = 0;
memset(lp, 0, sizeof(struct lvresize_params));
@@ -54,35 +55,31 @@ static int _lvresize_params(struct cmd_context *cmd, struct lvresize_params *lp)
lp->percent = PERCENT_PVS;
lp->sign = SIGN_PLUS;
lp->poolmetadata_size = 0;
- lp->resizefs = arg_is_set(cmd, resizefs_ARG);
- lp->nofsck = arg_is_set(cmd, nofsck_ARG);
+ set_fsopt = 1;
break;
case lvextend_size_CMD:
lp->resize = LV_EXTEND;
- lp->resizefs = arg_is_set(cmd, resizefs_ARG);
- lp->nofsck = arg_is_set(cmd, nofsck_ARG);
if ((lp->poolmetadata_size = arg_uint64_value(cmd, poolmetadatasize_ARG, 0)))
lp->poolmetadata_sign = arg_sign_value(cmd, poolmetadatasize_ARG, SIGN_NONE);
set_extents_and_size = 1;
+ set_fsopt = 1;
break;
case lvreduce_size_CMD:
lp->resize = LV_REDUCE;
lp->poolmetadata_size = 0;
- lp->resizefs = arg_is_set(cmd, resizefs_ARG);
- lp->nofsck = arg_is_set(cmd, nofsck_ARG);
set_extents_and_size = 1;
+ set_fsopt = 1;
break;
case lvresize_size_CMD:
lp->resize = LV_ANY;
lp->poolmetadata_size = arg_uint64_value(cmd, poolmetadatasize_ARG, 0);
- lp->resizefs = arg_is_set(cmd, resizefs_ARG);
- lp->nofsck = arg_is_set(cmd, nofsck_ARG);
if ((lp->poolmetadata_size = arg_uint64_value(cmd, poolmetadatasize_ARG, 0)))
lp->poolmetadata_sign = arg_sign_value(cmd, poolmetadatasize_ARG, SIGN_NONE);
set_extents_and_size = 1;
+ set_fsopt = 1;
break;
default:
@@ -90,6 +87,41 @@ static int _lvresize_params(struct cmd_context *cmd, struct lvresize_params *lp)
return 0;
};
+ if (set_fsopt) {
+ const char *str;
+ if ((str = arg_str_value(cmd, fs_ARG, NULL))) {
+ if (!strcmp(str, "checksize") ||
+ !strcmp(str, "resize") ||
+ !strcmp(str, "resize_remount") ||
+ !strcmp(str, "resize_keepmount") ||
+ !strcmp(str, "resize_unmount") ||
+ !strcmp(str, "resize_fsadm")) {
+ strncpy(lp->fsopt, str, sizeof(lp->fsopt)-1);
+ } else if (!strcmp(str, "ignore")) {
+ lp->fsopt[0] = '\0';
+ } else {
+ log_error("Unknown --fs value.");
+ return 0;
+ }
+ } else if (arg_is_set(cmd, resizefs_ARG)) {
+ /* --resizefs alone equates to --fs resize */
+ strncpy(lp->fsopt, "resize", sizeof(lp->fsopt)-1);
+ } else {
+ /*
+ * Use checksize when no fs option is specified.
+ * checksize with extend does nothing: the LV
+ * is extended and any fs is ignored.
+ * checksize with reduce checks for an fs that
+ * needs reducing: the LV is reduced only if the
+ * fs does not need to be reduced (or no fs.)
+ */
+ strncpy(lp->fsopt, "checksize", sizeof(lp->fsopt)-1);
+ }
+
+ if (lp->fsopt[0])
+ lp->nofsck = arg_is_set(cmd, nofsck_ARG);
+ }
+
if (set_extents_and_size) {
if ((lp->extents = arg_uint_value(cmd, extents_ARG, 0))) {
lp->sign = arg_sign_value(cmd, extents_ARG, 0);
@@ -237,6 +269,7 @@ static int _lvresize_single(struct cmd_context *cmd, struct logical_volume *lv,
struct processing_handle *handle)
{
struct lvresize_params *lp = (struct lvresize_params *) handle->custom_handle;
+ int ret;
if (cmd->position_argc > 1) {
/* First pos arg is required LV, remaining are optional PVs. */
@@ -245,11 +278,13 @@ static int _lvresize_single(struct cmd_context *cmd, struct logical_volume *lv,
} else
lp->pvh = &lv->vg->pvs;
- if (!lv_resize(cmd, lv, lp))
- return ECMD_FAILED;
+ ret = lv_resize(cmd, lv, lp);
- log_print_unless_silent("Logical volume %s successfully resized.",
- display_lvname(lv));
+ if (ret || lp->extend_fs_error)
+ log_print_unless_silent("Logical volume %s successfully resized.",
+ display_lvname(lv));
+ if (!ret)
+ return ECMD_FAILED;
return ECMD_PROCESSED;
}