From 9246667a22449b6c300ed0f0e153b3d5410f8363 Mon Sep 17 00:00:00 2001 From: QORTEC Date: Tue, 10 Oct 2023 18:06:08 -0400 Subject: [PATCH] Added limited/recursive operation to `zfs unmount` This commit introduces limited/recursive filesystem unmounting by leveraging the existing `zfs unmount -a` codebase with minor additions and modifications. Now, when running `zfs unmount <-a|-A> zpool/dataset`, the command will unmount all datasets that are the specified dataset itself or it's children, provided they are available. In addition, `-A` flag will also mount datasets with `canmount=noauto` property. Changes in `zfs_main.c`: - `HELP_UNMOUNT` - Updated `usage()` message to reflect the changes. - `unshare_unmount()` - Changed `do_all` from `int` to `boolean_t` - Added `boolean_t do_noauto` property; used for mounting datasets with `canmount=noauto` as `canmount=on`. - Added the `-A` flag; when used sets `do_noauto` to `B_TRUE`. - Updated argument check; displaies the correct error messages. - Limited the usage of `<-a|-A> filesystem` to `zfs unmount` only - Added a check; to validate that the specified filesystem is indeed a valid ZFS filesystem. - Modified the 'noauto' check; when unmounting datasets, if `do_noauto` is true, treat the unmount as if `canmount=on`. - Added a check; if `filesystem` is set, skips any datasets that are not `filesystem` itself or it's children. Signed-off-by: QORTEC --- cmd/zfs/zfs_main.c | 50 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 42 insertions(+), 8 deletions(-) diff --git a/cmd/zfs/zfs_main.c b/cmd/zfs/zfs_main.c index 3c96ddf973..8d82a96190 100644 --- a/cmd/zfs/zfs_main.c +++ b/cmd/zfs/zfs_main.c @@ -347,7 +347,7 @@ get_usage(zfs_help_t idx) "@ ...\n")); case HELP_UNMOUNT: return (gettext("\tunmount [-fu] " - "<-a | filesystem|mountpoint>\n")); + "<-a [filesystem] | [-A] filesystem|mountpoint>\n")); case HELP_UNSHARE: return (gettext("\tunshare " "<-a [nfs|smb] | filesystem|mountpoint>\n")); @@ -7488,7 +7488,8 @@ out: static int unshare_unmount(int op, int argc, char **argv) { - int do_all = 0; + boolean_t do_all = B_FALSE; + boolean_t do_noauto = B_FALSE; int flags = 0; int ret = 0; int c; @@ -7497,10 +7498,13 @@ unshare_unmount(int op, int argc, char **argv) char sharesmb[ZFS_MAXPROPLEN]; /* check options */ - while ((c = getopt(argc, argv, op == OP_SHARE ? ":a" : "afu")) != -1) { + while ((c = getopt(argc, argv, op == OP_SHARE ? ":a" : "aAfu")) != -1) { switch (c) { case 'a': - do_all = 1; + do_all = B_TRUE; + break; + case 'A': + do_noauto = B_TRUE; break; case 'f': flags |= MS_FORCE; @@ -7523,7 +7527,7 @@ unshare_unmount(int op, int argc, char **argv) argc -= optind; argv += optind; - if (do_all) { + if (do_all || do_noauto) { /* * We could make use of zfs_for_each() to walk all datasets in * the system, but this would be very inefficient, especially @@ -7555,11 +7559,31 @@ unshare_unmount(int op, int argc, char **argv) argv++; } - if (argc != 0) { + /* check number of arguments */ + if (do_noauto && argc < 1) { + (void) fprintf(stderr, gettext("missing " + "dataset argument\n")); + usage(B_FALSE); + } + if ((op == OP_SHARE && argc != 0) || argc > 1) { (void) fprintf(stderr, gettext("too many arguments\n")); usage(B_FALSE); } + /* + * Limit `-a filesystem` to unmount only + */ + const char *filesystem = NULL; + if (op == OP_MOUNT) + filesystem = argv[0]; + + /* + * Validate filesystem is actually a valid zfs filesystem + */ + if (filesystem != NULL && (zhp = zfs_open(g_zfs, + filesystem, ZFS_TYPE_FILESYSTEM)) == NULL) + return (1); + if (((pool = uu_avl_pool_create("unmount_pool", sizeof (unshare_unmount_node_t), offsetof(unshare_unmount_node_t, un_avlnode), @@ -7621,15 +7645,25 @@ unshare_unmount(int op, int argc, char **argv) NULL, NULL, 0, B_FALSE) == 0); if (strcmp(nfs_mnt_prop, "legacy") == 0) continue; - /* Ignore canmount=noauto mounts */ + /* + * Ignore canmount=noauto mounts if + * 'do_noauto' is set to false (default: false) + */ if (zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT) == - ZFS_CANMOUNT_NOAUTO) + ZFS_CANMOUNT_NOAUTO && !do_noauto) continue; break; default: break; } + /* + * Skip any dataset that's not related to filesystem. + */ + if (filesystem != NULL && + !zfs_dataset_related(zhp, filesystem)) + continue; + node = safe_malloc(sizeof (unshare_unmount_node_t)); node->un_zhp = zhp; node->un_mountp = safe_strdup(entry.mnt_mountp);