]> git.proxmox.com Git - mirror_zfs.git/commitdiff
Fix changelist mounted-dataset iteration
authorAlek P <alek-p@users.noreply.github.com>
Thu, 11 Oct 2018 04:13:13 +0000 (00:13 -0400)
committerBrian Behlendorf <behlendorf1@llnl.gov>
Thu, 11 Oct 2018 04:13:13 +0000 (21:13 -0700)
Commit 0c6d093 caused a regression in the inherit codepath.
The fix is to restrict the changelist iteration on mountpoints and
add proper handling for 'legacy' mountpoints

Reviewed by: Serapheim Dimitropoulos <serapheim.dimitro@delphix.com>
Reviewed by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Alek Pinchuk <apinchuk@datto.com>
Closes #7988
Closes #7991

include/libzfs_impl.h
lib/libzfs/libzfs_changelist.c
lib/libzfs/libzfs_dataset.c
lib/libzfs/libzfs_mount.c
tests/runfiles/linux.run
tests/zfs-tests/tests/functional/cli_root/zfs_inherit/Makefile.am
tests/zfs-tests/tests/functional/cli_root/zfs_inherit/zfs_inherit_mountpoint.ksh [new file with mode: 0755]
tests/zfs-tests/tests/functional/cli_root/zfs_rename/Makefile.am
tests/zfs-tests/tests/functional/cli_root/zfs_rename/zfs_rename_mountpoint.ksh [new file with mode: 0755]

index 959ba85b7918127028936da60cc54c552b901d67..568103f4be360710c44310f8b4f38389d8b8265b 100644 (file)
@@ -22,6 +22,7 @@
 /*
  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
  * Copyright (c) 2011, 2015 by Delphix. All rights reserved.
+ * Copyright (c) 2018 Datto Inc.
  */
 
 #ifndef        _LIBZFS_IMPL_H
@@ -146,6 +147,10 @@ int zprop_expand_list(libzfs_handle_t *hdl, zprop_list_t **plp,
  * mounted.
  */
 #define        CL_GATHER_MOUNT_ALWAYS  1
+/*
+ * changelist_gather() flag to force it to iterate on mounted datasets only
+ */
+#define        CL_GATHER_ITER_MOUNTED  2
 
 typedef struct prop_changelist prop_changelist_t;
 
index ae9f1d1799215890f62ea64c07b9452c1d5ef235..3101febc160518a456bfdca7d71b94dd3ccb2cc8 100644 (file)
@@ -553,39 +553,50 @@ change_one(zfs_handle_t *zhp, void *data)
        return (0);
 }
 
-/*ARGSUSED*/
 static int
-compare_mountpoints(const void *a, const void *b, void *unused)
+compare_props(const void *a, const void *b, zfs_prop_t prop)
 {
        const prop_changenode_t *ca = a;
        const prop_changenode_t *cb = b;
 
-       char mounta[MAXPATHLEN];
-       char mountb[MAXPATHLEN];
+       char propa[MAXPATHLEN];
+       char propb[MAXPATHLEN];
+
+       boolean_t haspropa, haspropb;
 
-       boolean_t hasmounta, hasmountb;
+       haspropa = (zfs_prop_get(ca->cn_handle, prop, propa, sizeof (propa),
+           NULL, NULL, 0, B_FALSE) == 0);
+       haspropb = (zfs_prop_get(cb->cn_handle, prop, propb, sizeof (propb),
+           NULL, NULL, 0, B_FALSE) == 0);
 
+       if (!haspropa && haspropb)
+               return (-1);
+       else if (haspropa && !haspropb)
+               return (1);
+       else if (!haspropa && !haspropb)
+               return (0);
+       else
+               return (strcmp(propb, propa));
+}
+
+/*ARGSUSED*/
+static int
+compare_mountpoints(const void *a, const void *b, void *unused)
+{
        /*
         * When unsharing or unmounting filesystems, we need to do it in
         * mountpoint order.  This allows the user to have a mountpoint
         * hierarchy that is different from the dataset hierarchy, and still
-        * allow it to be changed.  However, if either dataset doesn't have a
-        * mountpoint (because it is a volume or a snapshot), we place it at the
-        * end of the list, because it doesn't affect our change at all.
+        * allow it to be changed.
         */
-       hasmounta = (zfs_prop_get(ca->cn_handle, ZFS_PROP_MOUNTPOINT, mounta,
-           sizeof (mounta), NULL, NULL, 0, B_FALSE) == 0);
-       hasmountb = (zfs_prop_get(cb->cn_handle, ZFS_PROP_MOUNTPOINT, mountb,
-           sizeof (mountb), NULL, NULL, 0, B_FALSE) == 0);
+       return (compare_props(a, b, ZFS_PROP_MOUNTPOINT));
+}
 
-       if (!hasmounta && hasmountb)
-               return (-1);
-       else if (hasmounta && !hasmountb)
-               return (1);
-       else if (!hasmounta && !hasmountb)
-               return (0);
-       else
-               return (strcmp(mountb, mounta));
+/*ARGSUSED*/
+static int
+compare_dataset_names(const void *a, const void *b, void *unused)
+{
+       return (compare_props(a, b, ZFS_PROP_NAME));
 }
 
 /*
@@ -630,7 +641,7 @@ changelist_gather(zfs_handle_t *zhp, zfs_prop_t prop, int gather_flags,
        clp->cl_pool = uu_avl_pool_create("changelist_pool",
            sizeof (prop_changenode_t),
            offsetof(prop_changenode_t, cn_treenode),
-           compare_mountpoints, 0);
+           legacy ? compare_dataset_names : compare_mountpoints, 0);
        if (clp->cl_pool == NULL) {
                assert(uu_error() == UU_ERROR_NO_MEMORY);
                (void) zfs_error(zhp->zfs_hdl, EZFS_NOMEM, "internal error");
@@ -687,7 +698,7 @@ changelist_gather(zfs_handle_t *zhp, zfs_prop_t prop, int gather_flags,
                clp->cl_shareprop = ZFS_PROP_SHARENFS;
 
        if (clp->cl_prop == ZFS_PROP_MOUNTPOINT &&
-           (clp->cl_gflags & CL_GATHER_MOUNT_ALWAYS) == 0) {
+           (clp->cl_gflags & CL_GATHER_ITER_MOUNTED)) {
                /*
                 * Instead of iterating through all of the dataset children we
                 * gather mounted dataset children from MNTTAB
index b6dd4f1666d48b55a7def52d8edfd65557b935a7..5767b2ee388ef3b37a14c3a84d17e61e19657473 100644 (file)
@@ -30,6 +30,7 @@
  * Copyright 2017 Nexenta Systems, Inc.
  * Copyright 2016 Igor Kozhukhov <ikozhukhov@gmail.com>
  * Copyright 2017-2018 RackTop Systems.
+ * Copyright (c) 2018 Datto Inc.
  */
 
 #include <ctype.h>
@@ -4548,7 +4549,8 @@ zfs_rename(zfs_handle_t *zhp, const char *target, boolean_t recursive,
                        goto error;
                }
        } else if (zhp->zfs_type != ZFS_TYPE_SNAPSHOT) {
-               if ((cl = changelist_gather(zhp, ZFS_PROP_NAME, 0,
+               if ((cl = changelist_gather(zhp, ZFS_PROP_NAME,
+                   CL_GATHER_ITER_MOUNTED,
                    force_unmount ? MS_FORCE : 0)) == NULL)
                        return (-1);
 
index 252112e24bd3f802ef4d12b3364da8c59a0d95ca..23e45d0d3ec5e0e9e740b34994872aac11461fc4 100644 (file)
@@ -25,6 +25,7 @@
  * Copyright (c) 2014, 2015 by Delphix. All rights reserved.
  * Copyright 2016 Igor Kozhukhov <ikozhukhov@gmail.com>
  * Copyright 2017 RackTop Systems.
+ * Copyright (c) 2018 Datto Inc.
  */
 
 /*
@@ -699,7 +700,8 @@ zfs_unmountall(zfs_handle_t *zhp, int flags)
        prop_changelist_t *clp;
        int ret;
 
-       clp = changelist_gather(zhp, ZFS_PROP_MOUNTPOINT, 0, flags);
+       clp = changelist_gather(zhp, ZFS_PROP_MOUNTPOINT,
+           CL_GATHER_ITER_MOUNTED, 0);
        if (clp == NULL)
                return (-1);
 
index 776aff4d4c0dedf7b91aa2f94a472172b1eabf98..d33c55d9cfa4677de12095af8907fb0a158de446 100644 (file)
@@ -166,7 +166,8 @@ tests = ['zfs_get_001_pos', 'zfs_get_002_pos', 'zfs_get_003_pos',
 tags = ['functional', 'cli_root', 'zfs_get']
 
 [tests/functional/cli_root/zfs_inherit]
-tests = ['zfs_inherit_001_neg', 'zfs_inherit_002_neg', 'zfs_inherit_003_pos']
+tests = ['zfs_inherit_001_neg', 'zfs_inherit_002_neg', 'zfs_inherit_003_pos',
+    'zfs_inherit_mountpoint']
 tags = ['functional', 'cli_root', 'zfs_inherit']
 
 [tests/functional/cli_root/zfs_load-key]
@@ -218,7 +219,7 @@ tests = ['zfs_rename_001_pos', 'zfs_rename_002_pos', 'zfs_rename_003_pos',
     'zfs_rename_007_pos', 'zfs_rename_008_pos', 'zfs_rename_009_neg',
     'zfs_rename_010_neg', 'zfs_rename_011_pos', 'zfs_rename_012_neg',
     'zfs_rename_013_pos', 'zfs_rename_014_neg', 'zfs_rename_encrypted_child',
-    'zfs_rename_to_encrypted']
+    'zfs_rename_to_encrypted', 'zfs_rename_mountpoint']
 tags = ['functional', 'cli_root', 'zfs_rename']
 
 [tests/functional/cli_root/zfs_reservation]
index 104ee069c11a86b32d7aad56b051013930b84f48..95a51ec757eadb3c384e744c9f630c2652d8ea2d 100644 (file)
@@ -4,4 +4,5 @@ dist_pkgdata_SCRIPTS = \
        setup.ksh \
        zfs_inherit_001_neg.ksh \
        zfs_inherit_002_neg.ksh \
-       zfs_inherit_003_pos.ksh
+       zfs_inherit_003_pos.ksh \
+       zfs_inherit_mountpoint.ksh
diff --git a/tests/zfs-tests/tests/functional/cli_root/zfs_inherit/zfs_inherit_mountpoint.ksh b/tests/zfs-tests/tests/functional/cli_root/zfs_inherit/zfs_inherit_mountpoint.ksh
new file mode 100755 (executable)
index 0000000..9c12515
--- /dev/null
@@ -0,0 +1,62 @@
+#!/bin/ksh -p
+#
+# CDDL HEADER START
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source.  A copy is of the CDDL is also available via the Internet
+# at http://www.illumos.org/license/CDDL.
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2018 Datto Inc.
+#
+
+. $STF_SUITE/include/libtest.shlib
+
+#
+# DESCRIPTION:
+#      zfs inherit should inherit mountpoint on mountpoint=none children
+#
+# STRATEGY:
+#      1. Create a set of nested datasets with mountpoint=none
+#      2. Verify datasets aren't mounted
+#      3. Inherit mountpoint and verify all datasets are now mounted
+#
+
+verify_runnable "both"
+
+function inherit_cleanup
+{
+       log_must zfs destroy -fR $TESTPOOL/inherit_test
+}
+
+log_onexit inherit_cleanup
+
+
+log_must zfs create -o mountpoint=none $TESTPOOL/inherit_test
+log_must zfs create $TESTPOOL/inherit_test/child
+
+if ismounted $TESTPOOL/inherit_test; then
+       log_fail "$TESTPOOL/inherit_test is mounted"
+fi
+if ismounted $TESTPOOL/inherit_test/child; then
+       log_fail "$TESTPOOL/inherit_test/child is mounted"
+fi
+
+log_must zfs inherit mountpoint $TESTPOOL/inherit_test
+
+if ! ismounted $TESTPOOL/inherit_test; then
+       log_fail "$TESTPOOL/inherit_test is not mounted"
+fi
+if ! ismounted $TESTPOOL/inherit_test/child; then
+       log_fail "$TESTPOOL/inherit_test/child is not mounted"
+fi
+
+log_pass "Verified mountpoint for mountpoint=none children inherited."
index 974538dd9891bae1c4c0e7f07c69453c91810914..406e27881523afd96fb47374e36ce14d7658141c 100644 (file)
@@ -17,7 +17,8 @@ dist_pkgdata_SCRIPTS = \
        zfs_rename_013_pos.ksh \
        zfs_rename_014_neg.ksh \
        zfs_rename_encrypted_child.ksh \
-       zfs_rename_to_encrypted.ksh
+       zfs_rename_to_encrypted.ksh \
+       zfs_rename_mountpoint.ksh
 
 dist_pkgdata_DATA = \
        zfs_rename.cfg \
diff --git a/tests/zfs-tests/tests/functional/cli_root/zfs_rename/zfs_rename_mountpoint.ksh b/tests/zfs-tests/tests/functional/cli_root/zfs_rename/zfs_rename_mountpoint.ksh
new file mode 100755 (executable)
index 0000000..4d2b94d
--- /dev/null
@@ -0,0 +1,88 @@
+#!/bin/ksh -p
+#
+# CDDL HEADER START
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source.  A copy is of the CDDL is also available via the Internet
+# at http://www.illumos.org/license/CDDL.
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2018 Datto Inc.
+#
+
+. $STF_SUITE/include/libtest.shlib
+
+#
+# DESCRIPTION:
+#      zfs rename should rename datasets even for mountpoint=none children
+#
+# STRATEGY:
+#      1. Create a set of nested datasets with mountpoint=none
+#      2. Verify datasets aren't mounted except for the parent
+#      3. Rename mountpoint and verify all child datasets are renamed
+#
+
+verify_runnable "both"
+
+function rename_cleanup
+{
+       log_note zfs destroy -fR $TESTPOOL/rename_test
+       log_note zfs destroy -fR $TESTPOOL/renamed
+}
+
+log_onexit rename_cleanup
+
+
+log_must zfs create $TESTPOOL/rename_test
+log_must zfs create $TESTPOOL/rename_test/ds
+log_must zfs create -o mountpoint=none $TESTPOOL/rename_test/child
+log_must zfs create $TESTPOOL/rename_test/child/grandchild
+
+if ! ismounted $TESTPOOL/rename_test; then
+       log_fail "$TESTPOOL/rename_test is not mounted"
+fi
+if ! ismounted $TESTPOOL/rename_test/ds; then
+       log_fail "$TESTPOOL/rename_test/ds is not mounted"
+fi
+if ismounted $TESTPOOL/rename_test/child; then
+       log_fail "$TESTPOOL/rename_test/child is mounted"
+fi
+if ismounted $TESTPOOL/rename_test/child/grandchild; then
+       log_fail "$TESTPOOL/rename_test/child/grandchild is mounted"
+fi
+
+log_must zfs rename $TESTPOOL/rename_test $TESTPOOL/renamed
+
+log_mustnot zfs list $TESTPOOL/rename_test
+log_mustnot zfs list $TESTPOOL/rename_test/ds
+log_mustnot zfs list $TESTPOOL/rename_test/child
+log_mustnot zfs list $TESTPOOL/rename_test/child/grandchild
+
+log_must zfs list $TESTPOOL/renamed
+log_must zfs list $TESTPOOL/renamed/ds
+log_must zfs list $TESTPOOL/renamed/child
+log_must zfs list $TESTPOOL/renamed/child/grandchild
+
+if ! ismounted $TESTPOOL/renamed; then
+        log_must zfs get all $TESTPOOL/renamed
+       log_fail "$TESTPOOL/renamed is not mounted"
+fi
+if ! ismounted $TESTPOOL/renamed/ds; then
+       log_fail "$TESTPOOL/renamed/ds is not mounted"
+fi
+if ismounted $TESTPOOL/renamed/child; then
+       log_fail "$TESTPOOL/renamed/child is mounted"
+fi
+if ismounted $TESTPOOL/renamed/child/grandchild; then
+       log_fail "$TESTPOOL/renamed/child/grandchild is mounted"
+fi
+
+log_pass "Verified rename for mountpoint=none children."