libzfs: use mount_setattr for selective remount including legacy mounts

When a namespace property is changed via zfs set, libzfs remounts the
filesystem to propagate the new VFS mount flags. The current approach
uses mount(2) with MS_REMOUNT, which reads all namespace properties
from ZFS and applies them together. This has two problems:

1. Linux VFS resets unspecified per-mount flags on remount. If an
   administrator sets a temporary flag (e.g. mount -o remount,noatime),
   a subsequent zfs set on any namespace property clobbers it.

2. Two concurrent zfs set operations on different namespace properties
   can overwrite each other's mount flags.

Additionally, legacy datasets (mountpoint=legacy) were never remounted
on namespace property changes since zfs_is_mountable() returns false
for them.

Add zfs_mount_setattr() which uses mount_setattr(2) to selectively
update only the mount flags that correspond to the changed property.
For legacy datasets, /proc/mounts is iterated to update all
mountpoints. On kernels without mount_setattr (ENOSYS), non-legacy
datasets fall back to a full remount; legacy mounts are skipped to
avoid clobbering temporary flags.

Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Alexander Motin <alexander.motin@TrueNAS.com>
Signed-off-by: Ameer Hamza <ahamza@ixsystems.com>
Closes #18257
This commit is contained in:
Ameer Hamza
2026-03-09 23:06:22 +05:00
committed by GitHub
parent d45c8d6489
commit 1eace59060
7 changed files with 247 additions and 26 deletions
+42 -1
View File
@@ -103,6 +103,47 @@ zfs_share_protocol_name(enum sa_protocol protocol)
return (sa_protocol_names[protocol]);
}
/*
* Returns B_TRUE if the property is a namespace property that requires
* a remount to take effect.
*/
boolean_t
zfs_is_namespace_prop(zfs_prop_t prop)
{
switch (prop) {
case ZFS_PROP_ATIME:
case ZFS_PROP_RELATIME:
case ZFS_PROP_DEVICES:
case ZFS_PROP_EXEC:
case ZFS_PROP_SETUID:
case ZFS_PROP_READONLY:
case ZFS_PROP_XATTR:
case ZFS_PROP_NBMAND:
return (B_TRUE);
default:
return (B_FALSE);
}
}
/*
* Returns the ZFS_MNT_PROP_* flag for a namespace property.
*/
uint32_t
zfs_namespace_prop_flag(zfs_prop_t prop)
{
switch (prop) {
case ZFS_PROP_ATIME: return (ZFS_MNT_PROP_ATIME);
case ZFS_PROP_RELATIME: return (ZFS_MNT_PROP_RELATIME);
case ZFS_PROP_DEVICES: return (ZFS_MNT_PROP_DEVICES);
case ZFS_PROP_EXEC: return (ZFS_MNT_PROP_EXEC);
case ZFS_PROP_SETUID: return (ZFS_MNT_PROP_SETUID);
case ZFS_PROP_READONLY: return (ZFS_MNT_PROP_READONLY);
case ZFS_PROP_XATTR: return (ZFS_MNT_PROP_XATTR);
case ZFS_PROP_NBMAND: return (ZFS_MNT_PROP_NBMAND);
default: return (0);
}
}
static boolean_t
dir_is_empty_stat(const char *dirname)
{
@@ -225,7 +266,7 @@ zfs_is_mounted(zfs_handle_t *zhp, char **where)
* that the caller has verified the sanity of mounting the dataset at
* its mountpoint to the extent the caller wants.
*/
static boolean_t
boolean_t
zfs_is_mountable_internal(zfs_handle_t *zhp)
{
if (zfs_prop_get_int(zhp, ZFS_PROP_ZONED) &&