mirror of
				https://git.proxmox.com/git/mirror_zfs.git
				synced 2025-10-26 18:05:04 +03:00 
			
		
		
		
	Add support for zfs mount -R <filesystem>
This commit adds support for mounting a dataset along with all of it's children with '-R' flag for zfs mount. There can be scenarios where we want to mount all datasets under one hierarchy instead of mounting all datasets present on system with '-a' flag. '-R' flag should work on all root and non-root datasets. Usage information and man page has been updated for zfs mount. A test for verifying the behavior for '-R' flag is also added. Reviewed-by: Ameer Hamza <ahamza@ixsystems.com> Reviewed-by: Alexander Motin <mav@FreeBSD.org> Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov> Signed-off-by: Umer Saleem <usaleem@ixsystems.com> Closes #16015
This commit is contained in:
		
							parent
							
								
									9a7ef02f4d
								
							
						
					
					
						commit
						8a56047135
					
				| @ -309,7 +309,8 @@ get_usage(zfs_help_t idx) | ||||
| 		    "[filesystem|volume|snapshot] ...\n")); | ||||
| 	case HELP_MOUNT: | ||||
| 		return (gettext("\tmount\n" | ||||
| 		    "\tmount [-flvO] [-o opts] <-a | filesystem>\n")); | ||||
| 		    "\tmount [-flvO] [-o opts] <-a|-R filesystem|" | ||||
| 		    "filesystem>\n")); | ||||
| 	case HELP_PROMOTE: | ||||
| 		return (gettext("\tpromote <clone-filesystem>\n")); | ||||
| 	case HELP_RECEIVE: | ||||
| @ -6750,6 +6751,8 @@ zfs_do_holds(int argc, char **argv) | ||||
| #define	MOUNT_TIME 1		/* seconds */ | ||||
| 
 | ||||
| typedef struct get_all_state { | ||||
| 	char		**ga_datasets; | ||||
| 	int		ga_count; | ||||
| 	boolean_t	ga_verbose; | ||||
| 	get_all_cb_t	*ga_cbp; | ||||
| } get_all_state_t; | ||||
| @ -6796,19 +6799,35 @@ get_one_dataset(zfs_handle_t *zhp, void *data) | ||||
| 	return (0); | ||||
| } | ||||
| 
 | ||||
| static void | ||||
| get_all_datasets(get_all_cb_t *cbp, boolean_t verbose) | ||||
| static int | ||||
| get_recursive_datasets(zfs_handle_t *zhp, void *data) | ||||
| { | ||||
| 	get_all_state_t state = { | ||||
| 	    .ga_verbose = verbose, | ||||
| 	    .ga_cbp = cbp | ||||
| 	}; | ||||
| 	get_all_state_t *state = data; | ||||
| 	int len = strlen(zfs_get_name(zhp)); | ||||
| 	for (int i = 0; i < state->ga_count; ++i) { | ||||
| 		if (strcmp(state->ga_datasets[i], zfs_get_name(zhp)) == 0) | ||||
| 			return (get_one_dataset(zhp, data)); | ||||
| 		else if ((strncmp(state->ga_datasets[i], zfs_get_name(zhp), | ||||
| 		    len) == 0) && state->ga_datasets[i][len] == '/') { | ||||
| 			(void) zfs_iter_filesystems_v2(zhp, 0, | ||||
| 			    get_recursive_datasets, data); | ||||
| 		} | ||||
| 	} | ||||
| 	zfs_close(zhp); | ||||
| 	return (0); | ||||
| } | ||||
| 
 | ||||
| 	if (verbose) | ||||
| static void | ||||
| get_all_datasets(get_all_state_t *state) | ||||
| { | ||||
| 	if (state->ga_verbose) | ||||
| 		set_progress_header(gettext("Reading ZFS config")); | ||||
| 	(void) zfs_iter_root(g_zfs, get_one_dataset, &state); | ||||
| 	if (state->ga_datasets == NULL) | ||||
| 		(void) zfs_iter_root(g_zfs, get_one_dataset, state); | ||||
| 	else | ||||
| 		(void) zfs_iter_root(g_zfs, get_recursive_datasets, state); | ||||
| 
 | ||||
| 	if (verbose) | ||||
| 	if (state->ga_verbose) | ||||
| 		finish_progress(gettext("done.")); | ||||
| } | ||||
| 
 | ||||
| @ -7154,18 +7173,22 @@ static int | ||||
| share_mount(int op, int argc, char **argv) | ||||
| { | ||||
| 	int do_all = 0; | ||||
| 	int recursive = 0; | ||||
| 	boolean_t verbose = B_FALSE; | ||||
| 	int c, ret = 0; | ||||
| 	char *options = NULL; | ||||
| 	int flags = 0; | ||||
| 
 | ||||
| 	/* check options */ | ||||
| 	while ((c = getopt(argc, argv, op == OP_MOUNT ? ":alvo:Of" : "al")) | ||||
| 	while ((c = getopt(argc, argv, op == OP_MOUNT ? ":aRlvo:Of" : "al")) | ||||
| 	    != -1) { | ||||
| 		switch (c) { | ||||
| 		case 'a': | ||||
| 			do_all = 1; | ||||
| 			break; | ||||
| 		case 'R': | ||||
| 			recursive = 1; | ||||
| 			break; | ||||
| 		case 'v': | ||||
| 			verbose = B_TRUE; | ||||
| 			break; | ||||
| @ -7207,7 +7230,7 @@ share_mount(int op, int argc, char **argv) | ||||
| 	argv += optind; | ||||
| 
 | ||||
| 	/* check number of arguments */ | ||||
| 	if (do_all) { | ||||
| 	if (do_all || recursive) { | ||||
| 		enum sa_protocol protocol = SA_NO_PROTOCOL; | ||||
| 
 | ||||
| 		if (op == OP_SHARE && argc > 0) { | ||||
| @ -7216,14 +7239,38 @@ share_mount(int op, int argc, char **argv) | ||||
| 			argv++; | ||||
| 		} | ||||
| 
 | ||||
| 		if (argc != 0) { | ||||
| 		if (argc != 0 && do_all) { | ||||
| 			(void) fprintf(stderr, gettext("too many arguments\n")); | ||||
| 			usage(B_FALSE); | ||||
| 		} | ||||
| 
 | ||||
| 		if (argc == 0 && recursive) { | ||||
| 			(void) fprintf(stderr, | ||||
| 			    gettext("no dataset provided\n")); | ||||
| 			usage(B_FALSE); | ||||
| 		} | ||||
| 
 | ||||
| 		start_progress_timer(); | ||||
| 		get_all_cb_t cb = { 0 }; | ||||
| 		get_all_datasets(&cb, verbose); | ||||
| 		get_all_state_t state = { 0 }; | ||||
| 		if (argc == 0) { | ||||
| 			state.ga_datasets = NULL; | ||||
| 			state.ga_count = -1; | ||||
| 		} else { | ||||
| 			zfs_handle_t *zhp; | ||||
| 			for (int i = 0; i < argc; i++) { | ||||
| 				zhp = zfs_open(g_zfs, argv[i], | ||||
| 				    ZFS_TYPE_FILESYSTEM); | ||||
| 				if (zhp == NULL) | ||||
| 					usage(B_FALSE); | ||||
| 				zfs_close(zhp); | ||||
| 			} | ||||
| 			state.ga_datasets = argv; | ||||
| 			state.ga_count = argc; | ||||
| 		} | ||||
| 		state.ga_verbose = verbose; | ||||
| 		state.ga_cbp = &cb; | ||||
| 		get_all_datasets(&state); | ||||
| 
 | ||||
| 		if (cb.cb_used == 0) { | ||||
| 			free(options); | ||||
|  | ||||
| @ -43,7 +43,7 @@ | ||||
| .Cm mount | ||||
| .Op Fl Oflv | ||||
| .Op Fl o Ar options | ||||
| .Fl a Ns | Ns Ar filesystem | ||||
| .Fl a Ns | Ns Fl R Ar filesystem Ns | Ns Ar filesystem | ||||
| .Nm zfs | ||||
| .Cm unmount | ||||
| .Op Fl fu | ||||
| @ -61,7 +61,7 @@ Displays all ZFS file systems currently mounted. | ||||
| .Cm mount | ||||
| .Op Fl Oflv | ||||
| .Op Fl o Ar options | ||||
| .Fl a Ns | Ns Ar filesystem | ||||
| .Fl a Ns | Ns Fl R Ar filesystem Ns | Ns Ar filesystem | ||||
| .Xc | ||||
| Mount ZFS filesystem on a path described by its | ||||
| .Sy mountpoint | ||||
| @ -83,6 +83,8 @@ for more information. | ||||
| .It Fl a | ||||
| Mount all available ZFS file systems. | ||||
| Invoked automatically as part of the boot process if configured. | ||||
| .It Fl R | ||||
| Mount the specified filesystems along with all their children. | ||||
| .It Ar filesystem | ||||
| Mount the specified filesystem. | ||||
| .It Fl o Ar options | ||||
|  | ||||
| @ -246,7 +246,7 @@ tests = ['zfs_mount_001_pos', 'zfs_mount_002_pos', 'zfs_mount_003_pos', | ||||
|     'zfs_mount_009_neg', 'zfs_mount_010_neg', 'zfs_mount_011_neg', | ||||
|     'zfs_mount_012_pos', 'zfs_mount_all_001_pos', 'zfs_mount_encrypted', | ||||
|     'zfs_mount_remount', 'zfs_mount_all_fail', 'zfs_mount_all_mountpoints', | ||||
|     'zfs_mount_test_race'] | ||||
|     'zfs_mount_test_race', 'zfs_mount_recursive'] | ||||
| tags = ['functional', 'cli_root', 'zfs_mount'] | ||||
| 
 | ||||
| [tests/functional/cli_root/zfs_program] | ||||
|  | ||||
| @ -155,7 +155,8 @@ tests = ['zfs_mount_001_pos', 'zfs_mount_002_pos', 'zfs_mount_003_pos', | ||||
|     'zfs_mount_004_pos', 'zfs_mount_005_pos', 'zfs_mount_007_pos', | ||||
|     'zfs_mount_009_neg', 'zfs_mount_010_neg', 'zfs_mount_011_neg', | ||||
|     'zfs_mount_012_pos', 'zfs_mount_encrypted', 'zfs_mount_remount', | ||||
|     'zfs_mount_all_fail', 'zfs_mount_all_mountpoints', 'zfs_mount_test_race'] | ||||
|     'zfs_mount_all_fail', 'zfs_mount_all_mountpoints', | ||||
|     'zfs_mount_test_race', 'zfs_mount_recursive'] | ||||
| tags = ['functional', 'cli_root', 'zfs_mount'] | ||||
| 
 | ||||
| [tests/functional/cli_root/zfs_program] | ||||
|  | ||||
| @ -769,6 +769,7 @@ nobase_dist_datadir_zfs_tests_tests_SCRIPTS += \ | ||||
| 	functional/cli_root/zfs_mount/zfs_mount_all_fail.ksh \
 | ||||
| 	functional/cli_root/zfs_mount/zfs_mount_all_mountpoints.ksh \
 | ||||
| 	functional/cli_root/zfs_mount/zfs_mount_encrypted.ksh \
 | ||||
| 	functional/cli_root/zfs_mount/zfs_mount_recursive.ksh \
 | ||||
| 	functional/cli_root/zfs_mount/zfs_mount_remount.ksh \
 | ||||
| 	functional/cli_root/zfs_mount/zfs_mount_test_race.ksh \
 | ||||
| 	functional/cli_root/zfs_mount/zfs_multi_mount.ksh \
 | ||||
|  | ||||
| @ -31,6 +31,7 @@ | ||||
| export mountcmd=mount | ||||
| export mountforce="$mountcmd -f" | ||||
| export mountall="$mountcmd -a" | ||||
| export mountrecursive="$mountcmd -R" | ||||
| 
 | ||||
| export unmountcmd=unmount | ||||
| export unmountforce="$unmountcmd -f" | ||||
|  | ||||
							
								
								
									
										146
									
								
								tests/zfs-tests/tests/functional/cli_root/zfs_mount/zfs_mount_recursive.ksh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										146
									
								
								tests/zfs-tests/tests/functional/cli_root/zfs_mount/zfs_mount_recursive.ksh
									
									
									
									
									
										Executable file
									
								
							| @ -0,0 +1,146 @@ | ||||
| #!/bin/ksh -p | ||||
| # | ||||
| # CDDL HEADER START | ||||
| # | ||||
| # The contents of this file are subject to the terms of the | ||||
| # Common Development and Distribution License (the "License"). | ||||
| # You may not use this file except in compliance with the License. | ||||
| # | ||||
| # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE | ||||
| # or https://opensource.org/licenses/CDDL-1.0. | ||||
| # See the License for the specific language governing permissions | ||||
| # and limitations under the License. | ||||
| # | ||||
| # When distributing Covered Code, include this CDDL HEADER in each | ||||
| # file and include the License file at usr/src/OPENSOLARIS.LICENSE. | ||||
| # If applicable, add the following below this CDDL HEADER, with the | ||||
| # fields enclosed by brackets "[]" replaced with your own identifying | ||||
| # information: Portions Copyright [yyyy] [name of copyright owner] | ||||
| # | ||||
| # CDDL HEADER END | ||||
| # | ||||
| 
 | ||||
| # | ||||
| # Copyright 2024, iXsystems Inc. All rights reserved. | ||||
| # | ||||
| 
 | ||||
| . $STF_SUITE/include/libtest.shlib | ||||
| . $STF_SUITE/tests/functional/cli_root/zfs_mount/zfs_mount.kshlib | ||||
| 
 | ||||
| # | ||||
| # DESCRIPTION: | ||||
| # Verify zfs mount -R <filesystems/s> functionality. | ||||
| # | ||||
| # STRATEGY: | ||||
| # 1. Create nested datasets | ||||
| # 2. Unmount all datasets | ||||
| # 3. Recusrively mount root datasets, this should mount all datasets | ||||
| #    present in a pool | ||||
| # 4. Unmount all datasets | ||||
| # 5. Recusrsively mount child datasets with children. This should mount | ||||
| #    child datasets, but not the root dataset or parent datasets | ||||
| # 6. Unmount all datasets | ||||
| # 7. Mount root dataset recursively again and confirm all child | ||||
| #    datasets are mounted. | ||||
| # | ||||
| 
 | ||||
| verify_runnable "both" | ||||
| 
 | ||||
| function cleanup | ||||
| { | ||||
| 	log_must datasetexists $TESTPOOL/$TESTFS1 && \ | ||||
| 		destroy_dataset $TESTPOOL/$TESTFS1 -R | ||||
| 	log_must datasetexists $TESTPOOL/$TESTFS2 && \ | ||||
| 		destroy_dataset $TESTPOOL/$TESTFS2 -R | ||||
| 	log_must datasetexists $TESTPOOL/$TESTFS3 && \ | ||||
| 		destroy_dataset $TESTPOOL/$TESTFS3 -R | ||||
| } | ||||
| 
 | ||||
| function setup_all | ||||
| { | ||||
| 	log_must datasetexists $TESTPOOL/$TESTFS || zfs create $TESTPOOL/$TESTFS | ||||
| 	log_must zfs create $TESTPOOL/$TESTFS1 | ||||
| 	log_must zfs create $TESTPOOL/$TESTFS2 | ||||
| 	log_must zfs create $TESTPOOL/$TESTFS3 | ||||
| 	log_must zfs create $TESTPOOL/$TESTFS2/child1 | ||||
| 	log_must zfs create $TESTPOOL/$TESTFS2/child2 | ||||
| 	log_must zfs create $TESTPOOL/$TESTFS2/child3 | ||||
| 	log_must zfs create $TESTPOOL/$TESTFS2/child2/subchild | ||||
| 	log_must zfs create $TESTPOOL/$TESTFS3/child | ||||
| } | ||||
| 
 | ||||
| log_assert "Verify that 'zfs $mountrecursive' successfully, " \ | ||||
| 	"mounts the dataset along with all its children." | ||||
| 
 | ||||
| log_onexit cleanup | ||||
| 
 | ||||
| log_must setup_all | ||||
| 
 | ||||
| log_must zfs $unmountall | ||||
| 
 | ||||
| log_must zfs $mountrecursive $TESTPOOL | ||||
| 
 | ||||
| log_must mounted $TESTPOOL | ||||
| log_must mounted $TESTPOOL/$TESTFS | ||||
| log_must mounted $TESTPOOL/$TESTFS1 | ||||
| log_must mounted $TESTPOOL/$TESTFS2 | ||||
| log_must mounted $TESTPOOL/$TESTFS3 | ||||
| log_must mounted $TESTPOOL/$TESTFS2/child1 | ||||
| log_must mounted $TESTPOOL/$TESTFS2/child2 | ||||
| log_must mounted $TESTPOOL/$TESTFS2/child3 | ||||
| log_must mounted $TESTPOOL/$TESTFS2/child2/subchild | ||||
| log_must mounted $TESTPOOL/$TESTFS3/child | ||||
| 
 | ||||
| log_must zfs $unmountall | ||||
| 
 | ||||
| log_mustnot mounted $TESTPOOL | ||||
| log_mustnot mounted $TESTPOOL/$TESTFS | ||||
| log_mustnot mounted $TESTPOOL/$TESTFS1 | ||||
| log_mustnot mounted $TESTPOOL/$TESTFS2 | ||||
| log_mustnot mounted $TESTPOOL/$TESTFS3 | ||||
| log_mustnot mounted $TESTPOOL/$TESTFS2/child1 | ||||
| log_mustnot mounted $TESTPOOL/$TESTFS2/child2 | ||||
| log_mustnot mounted $TESTPOOL/$TESTFS2/child3 | ||||
| log_mustnot mounted $TESTPOOL/$TESTFS2/child2/subchild | ||||
| log_mustnot mounted $TESTPOOL/$TESTFS3/child | ||||
| 
 | ||||
| log_must zfs $mountrecursive $TESTPOOL/$TESTFS2 $TESTPOOL/$TESTFS3 | ||||
| 
 | ||||
| log_mustnot mounted $TESTPOOL | ||||
| log_mustnot mounted $TESTPOOL/$TESTFS | ||||
| log_mustnot mounted $TESTPOOL/$TESTFS1 | ||||
| log_must mounted $TESTPOOL/$TESTFS2 | ||||
| log_must mounted $TESTPOOL/$TESTFS3 | ||||
| log_must mounted $TESTPOOL/$TESTFS2/child1 | ||||
| log_must mounted $TESTPOOL/$TESTFS2/child2 | ||||
| log_must mounted $TESTPOOL/$TESTFS2/child3 | ||||
| log_must mounted $TESTPOOL/$TESTFS2/child2/subchild | ||||
| log_must mounted $TESTPOOL/$TESTFS3/child | ||||
| 
 | ||||
| log_must zfs $unmountall | ||||
| 
 | ||||
| log_mustnot mounted $TESTPOOL | ||||
| log_mustnot mounted $TESTPOOL/$TESTFS | ||||
| log_mustnot mounted $TESTPOOL/$TESTFS1 | ||||
| log_mustnot mounted $TESTPOOL/$TESTFS2 | ||||
| log_mustnot mounted $TESTPOOL/$TESTFS3 | ||||
| log_mustnot mounted $TESTPOOL/$TESTFS2/child1 | ||||
| log_mustnot mounted $TESTPOOL/$TESTFS2/child2 | ||||
| log_mustnot mounted $TESTPOOL/$TESTFS2/child3 | ||||
| log_mustnot mounted $TESTPOOL/$TESTFS2/child2/subchild | ||||
| log_mustnot mounted $TESTPOOL/$TESTFS3/child | ||||
| 
 | ||||
| log_must zfs $mountrecursive $TESTPOOL/$TESTFS2/child2 | ||||
| 
 | ||||
| log_must mounted $TESTPOOL/$TESTFS2/child2 | ||||
| log_must mounted $TESTPOOL/$TESTFS2/child2/subchild | ||||
| log_mustnot mounted $TESTPOOL | ||||
| log_mustnot mounted $TESTPOOL/$TESTFS | ||||
| log_mustnot mounted $TESTPOOL/$TESTFS1 | ||||
| log_mustnot mounted $TESTPOOL/$TESTFS2 | ||||
| log_mustnot mounted $TESTPOOL/$TESTFS3 | ||||
| log_mustnot mounted $TESTPOOL/$TESTFS2/child1 | ||||
| log_mustnot mounted $TESTPOOL/$TESTFS2/child3 | ||||
| log_mustnot mounted $TESTPOOL/$TESTFS3/child | ||||
| 
 | ||||
| log_pass "'zfs $mountrecursive' behaves as expected." | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 Umer Saleem
						Umer Saleem