diff --git a/include/sys/dsl_dataset.h b/include/sys/dsl_dataset.h index 2e1f9847f34c..565efd173b75 100644 --- a/include/sys/dsl_dataset.h +++ b/include/sys/dsl_dataset.h @@ -268,6 +268,10 @@ typedef struct dsl_dataset { /* Protected by ds_lock; keep at end of struct for better locality */ char ds_snapname[ZFS_MAX_DATASET_NAME_LEN]; + +#ifdef __FreeBSD__ + char *ds_jailname; +#endif } dsl_dataset_t; static inline dsl_dataset_phys_t * diff --git a/include/sys/fs/zfs.h b/include/sys/fs/zfs.h index 49ab9d3db795..13db73f39204 100644 --- a/include/sys/fs/zfs.h +++ b/include/sys/fs/zfs.h @@ -203,6 +203,7 @@ typedef enum { ZFS_PROP_DEFAULTUSEROBJQUOTA, ZFS_PROP_DEFAULTGROUPOBJQUOTA, ZFS_PROP_DEFAULTPROJECTOBJQUOTA, + ZFS_PROP_ZONE, ZFS_NUM_PROPS } zfs_prop_t; diff --git a/lib/libzfs/libzfs.abi b/lib/libzfs/libzfs.abi index 184ea4a55b43..45a78cb5ac6a 100644 --- a/lib/libzfs/libzfs.abi +++ b/lib/libzfs/libzfs.abi @@ -2259,7 +2259,8 @@ - + + diff --git a/man/man7/zfsprops.7 b/man/man7/zfsprops.7 index 77e994b912b6..145cbf7037d2 100644 --- a/man/man7/zfsprops.7 +++ b/man/man7/zfsprops.7 @@ -2095,6 +2095,16 @@ for more information. Jails are a .Fx feature and this property is not available on other platforms. +.It Sy jail +This read-only property reports name of the jail that mounted the jailed +dataset. +The "0" name is used for datasets that are not mounted or not jailed. +This differs from the normal ZFS convention to print dash ('-') for unset +values, since it can be a valid jail name. +If the jail is renamed, the property will still report its old name from +the time the dataset was mounted. +The reported jail may no longer exist, while the dataset remains mounted. +The property is not revealed to jails themselves, the "0" is reported instead. .It Sy zoned Ns = Ns Sy off Ns | Ns Sy on Controls whether the dataset is managed from a non-global zone or namespace. See diff --git a/module/os/freebsd/zfs/zfs_vfsops.c b/module/os/freebsd/zfs/zfs_vfsops.c index 79b784288911..32d4fb7de9d4 100644 --- a/module/os/freebsd/zfs/zfs_vfsops.c +++ b/module/os/freebsd/zfs/zfs_vfsops.c @@ -1310,6 +1310,18 @@ zfs_domount(vfs_t *vfsp, char *osname) if (!zfsvfs->z_issnap) zfsctl_create(zfsvfs); + +#ifdef __FreeBSD__ + if (error == 0) { + /* zone dataset visiblity was checked before in zfs_mount() */ + struct prison *pr = curthread->td_ucred->cr_prison; + if (pr != &prison0) { + zfsvfs->z_os->os_dsl_dataset->ds_jailname = + kmem_strdup(pr->pr_name); + } + } +#endif + out: if (error) { dmu_objset_disown(zfsvfs->z_os, B_TRUE, zfsvfs); @@ -1783,6 +1795,12 @@ zfs_umount(vfs_t *vfsp, int fflag) dmu_objset_set_user(os, NULL); mutex_exit(&os->os_user_ptr_lock); +#ifdef __FreeBSD__ + if (os->os_dsl_dataset->ds_jailname) + kmem_strfree(os->os_dsl_dataset->ds_jailname); + os->os_dsl_dataset->ds_jailname = NULL; +#endif + /* * Finally release the objset */ diff --git a/module/zcommon/zfs_prop.c b/module/zcommon/zfs_prop.c index 864e3898b365..b58e94ce347b 100644 --- a/module/zcommon/zfs_prop.c +++ b/module/zcommon/zfs_prop.c @@ -516,9 +516,13 @@ zfs_prop_init(void) zprop_register_index(ZFS_PROP_ZONED, "jailed", 0, PROP_INHERIT, ZFS_TYPE_FILESYSTEM, "on | off", "JAILED", boolean_table, sfeatures); + zprop_register_string(ZFS_PROP_ZONE, "jail", NULL, PROP_READONLY, + ZFS_TYPE_FILESYSTEM, " | 0", "JAIL", sfeatures); #else zprop_register_index(ZFS_PROP_ZONED, "zoned", 0, PROP_INHERIT, ZFS_TYPE_FILESYSTEM, "on | off", "ZONED", boolean_table, sfeatures); + zprop_register_string(ZFS_PROP_ZONE, "zone", NULL, PROP_READONLY, + ZFS_TYPE_FILESYSTEM, "", "ZONE", sfeatures); #endif zprop_register_index(ZFS_PROP_VSCAN, "vscan", 0, PROP_INHERIT, ZFS_TYPE_FILESYSTEM, "on | off", "VSCAN", boolean_table, sfeatures); diff --git a/module/zfs/dsl_prop.c b/module/zfs/dsl_prop.c index 51f624da5689..65323f8268d5 100644 --- a/module/zfs/dsl_prop.c +++ b/module/zfs/dsl_prop.c @@ -1230,6 +1230,15 @@ dsl_prop_get_all_ds(dsl_dataset_t *ds, nvlist_t **nvp, goto out; } +#ifdef __FreeBSD__ + nvlist_t *propval = fnvlist_alloc(); + fnvlist_add_string(propval, ZPROP_VALUE, + (ds->ds_jailname && INGLOBALZONE(curproc)) ? + ds->ds_jailname : "0"); + fnvlist_add_nvlist(*nvp, "jail", propval); + nvlist_free(propval); +#endif + for (; dd != NULL; dd = dd->dd_parent) { if (dd != ds->ds_dir || (flags & DSL_PROP_GET_SNAPSHOT)) { if (flags & (DSL_PROP_GET_LOCAL | diff --git a/tests/runfiles/freebsd.run b/tests/runfiles/freebsd.run index ae39cb532dd0..03c1f0b048d8 100644 --- a/tests/runfiles/freebsd.run +++ b/tests/runfiles/freebsd.run @@ -23,7 +23,7 @@ failsafe = callbacks/zfs_failsafe tags = ['functional'] [tests/functional/cli_root/zfs_jail:FreeBSD] -tests = ['zfs_jail_001_pos'] +tests = ['zfs_jail_001_pos', 'zfs_jail_property'] tags = ['functional', 'cli_root', 'zfs_jail'] [tests/functional/pam:FreeBSD] diff --git a/tests/zfs-tests/tests/Makefile.am b/tests/zfs-tests/tests/Makefile.am index 1517f90e99a5..6d00c666c5d8 100644 --- a/tests/zfs-tests/tests/Makefile.am +++ b/tests/zfs-tests/tests/Makefile.am @@ -761,6 +761,7 @@ nobase_dist_datadir_zfs_tests_tests_SCRIPTS += \ functional/cli_root/zfs_jail/cleanup.ksh \ functional/cli_root/zfs_jail/setup.ksh \ functional/cli_root/zfs_jail/zfs_jail_001_pos.ksh \ + functional/cli_root/zfs_jail/zfs_jail_property.ksh \ functional/cli_root/zfs_load-key/cleanup.ksh \ functional/cli_root/zfs_load-key/setup.ksh \ functional/cli_root/zfs_load-key/zfs_load-key_all.ksh \ diff --git a/tests/zfs-tests/tests/functional/cli_root/zfs_jail/zfs_jail_001_pos.ksh b/tests/zfs-tests/tests/functional/cli_root/zfs_jail/zfs_jail_001_pos.ksh index 7ce03a4b9047..6f4c0aa4cbe5 100755 --- a/tests/zfs-tests/tests/functional/cli_root/zfs_jail/zfs_jail_001_pos.ksh +++ b/tests/zfs-tests/tests/functional/cli_root/zfs_jail/zfs_jail_001_pos.ksh @@ -35,7 +35,7 @@ # 1. Create a jail. # 2. Perform some basic ZFS operations on a dataset both in the host and # in the jail to confirm the dataset is functional in the host -# and hidden in in the jail. +# and hidden in the jail. # 3. Run `zfs jail` to expose the dataset in the jail. # 4. Perform some basic ZFS operations on the dataset both in the host and # in the jail to confirm the dataset is functional in the jail and host. diff --git a/tests/zfs-tests/tests/functional/cli_root/zfs_jail/zfs_jail_property.ksh b/tests/zfs-tests/tests/functional/cli_root/zfs_jail/zfs_jail_property.ksh new file mode 100755 index 000000000000..20ff75d9b790 --- /dev/null +++ b/tests/zfs-tests/tests/functional/cli_root/zfs_jail/zfs_jail_property.ksh @@ -0,0 +1,90 @@ +#!/bin/ksh -p +# SPDX-License-Identifier: CDDL-1.0 +# +# 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 2025 SkunkWerks, GmbH +# + +. $STF_SUITE/include/libtest.shlib + +# +# DESCRIPTION: +# Test basic use cases of "jail" zfs dataset property. +# +# STRATEGY: +# 1. Create a dataset. Verify. +# 2. Create a jail. Verify. +# 3. Jail the dataset. Verify. +# 4. Mount the dataset by the jail. Verify. +# 5. Unmount the dataset. Verify. +# + +verify_runnable "global" + +JAIL="testjail" +JAIL_CONF="$STF_SUITE/tests/functional/cli_root/zfs_jail/jail.conf" +DATASET="$TESTPOOL/dataset1" +DATASET_JAILED_MOUNTPOINT="/jailed" + +function cleanup +{ + if jls -j $JAIL name >/dev/null 2>&1; then + jail -r -f $JAIL_CONF $JAIL + fi +} + +log_onexit cleanup + +log_assert "Verify basic use cases of jail zfs property." + +# Root dataset has default value +log_must test "0" = "$(zfs get -o value -H jail $TESTPOOL)" + +# Create the dataset +log_must zfs create -o jailed=on -o mountpoint=$DATASET_JAILED_MOUNTPOINT $DATASET +log_must test "0" = "$(zfs get -o value -H jail $DATASET)" + +# Create the jail +log_must jail -c -f $JAIL_CONF $JAIL +log_mustnot jexec $JAIL zfs list $DATASET +log_must test "0" = "$(zfs get -o value -H jail $DATASET)" + +# Jail the dataset +log_must zfs jail $JAIL $DATASET +log_must jexec $JAIL zfs list $DATASET +log_must test "0" = "$(zfs get -o value -H jail $DATASET)" +log_must test "0" = "$(jexec $JAIL zfs get -o value -H jail $DATASET)" + +# Mount the dataset by the jail +log_must jexec $JAIL zfs mount $DATASET +# Now we see who mounted it +log_must test "$JAIL" = "$(zfs get -o value -H jail $DATASET)" +# But it is hidden from the jail itself +log_must test "0" = "$(jexec $JAIL zfs get -o value -H jail $DATASET)" + +# Unmount the dataset by the jail +log_must jexec $JAIL zfs unmount $DATASET +log_must test "0" = "$(zfs get -o value -H jail $DATASET)" +log_must test "0" = "$(jexec $JAIL zfs get -o value -H jail $DATASET)" + +log_pass "Verify basic use cases of jail zfs property."