2024-06-04 11:27:12

by Li Lingfeng

[permalink] [raw]
Subject: [PATCH RFC 2/2] NFSv4: set sb_flags to second superblock

From: Li Lingfeng <[email protected]>

During the process of mounting an NFSv4 client, two superblocks will be
created in sequence. The first superblock corresponds to the root
directory exported by the server, and the second superblock corresponds to
the directory that will be actually mounted. The first superblock will
eventually be destroyed.
The flag passed from user mode will only be passed to the first
superblock, resulting in the actual used superblock not carrying the flag
passed from user mode(fs_context_for_submount() will set sb_flags as 0).

If the 'ro' parameter is used in two consecutive mount commands, only the
first execution will create a new vfsmount, and the kernel will return
EBUSY on the second execution. However, if a remount command with the 'ro'
parameter is executed between the two mount commands, both mount commands
will create new vfsmounts.

The superblock generated after the first mount command does not have the
'ro' flag, and the read-only status of the file system is implemented by
checking the read-only flag of the vfsmount. After executing the remount
command, the 'ro' flag will be added to the superblock. When the second
mount command is executed, the comparison result between the superblock
with the 'ro' flag and the fs_context without the flag in the
nfs_compare_mount_options() function will be different, resulting in the
creation of a new vfsmount.

This problem can be reproduced by performing the following operations:
mount -t nfs -o ro,vers=4.0 192.168.240.250:/sdb /mnt/sdb
mount -t nfs -o remount,ro,vers=4.0 192.168.240.250:/sdb /mnt/sdb
mount -t nfs -o ro,vers=4.0 192.168.240.250:/sdb /mnt/sdb
Two vfsmounts are generated:
[root@localhost ~]# mount | grep nfs
192.168.240.250:/sdb on /mnt/sdb type nfs4 (ro,relatime,vers=4.0,
rsize=1048576,wsize=1048576,namlen=255,hard,proto=tcp,timeo=600,retrans=2,
sec=sys,clientaddr=192.168.240.251,local_lock=none,addr=192.168.240.250)
192.168.240.250:/sdb on /mnt/sdb type nfs4 (ro,relatime,vers=4.0,
rsize=1048576,wsize=1048576,namlen=255,hard,proto=tcp,timeo=600,retrans=2,
sec=sys,clientaddr=192.168.240.251,local_lock=none,addr=192.168.240.250)

Fix this by setting sb_flags to second superblock.

Signed-off-by: Li Lingfeng <[email protected]>
---
fs/nfs/namespace.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/fs/nfs/namespace.c b/fs/nfs/namespace.c
index 887aeacedebd..8b3d75af60d4 100644
--- a/fs/nfs/namespace.c
+++ b/fs/nfs/namespace.c
@@ -158,7 +158,7 @@ struct vfsmount *nfs_d_automount(struct path *path, unsigned int sb_flags)
/* Open a new filesystem context, transferring parameters from the
* parent superblock, including the network namespace.
*/
- fc = fs_context_for_submount(path->mnt->mnt_sb->s_type, path->dentry, 0);
+ fc = fs_context_for_submount(path->mnt->mnt_sb->s_type, path->dentry, sb_flags);
if (IS_ERR(fc))
return ERR_CAST(fc);

--
2.39.2



2024-06-14 03:14:46

by Li Lingfeng

[permalink] [raw]
Subject: Re: [PATCH RFC 2/2] NFSv4: set sb_flags to second superblock

I think this may be a problem, but I'm unable to come up with a suitable
solution. Would you mind providing some suggestions?

在 2024/6/4 19:26, Li Lingfeng 写道:
> From: Li Lingfeng <[email protected]>
>
> During the process of mounting an NFSv4 client, two superblocks will be
> created in sequence. The first superblock corresponds to the root
> directory exported by the server, and the second superblock corresponds to
> the directory that will be actually mounted. The first superblock will
> eventually be destroyed.
> The flag passed from user mode will only be passed to the first
> superblock, resulting in the actual used superblock not carrying the flag
> passed from user mode(fs_context_for_submount() will set sb_flags as 0).
>
> If the 'ro' parameter is used in two consecutive mount commands, only the
> first execution will create a new vfsmount, and the kernel will return
> EBUSY on the second execution. However, if a remount command with the 'ro'
> parameter is executed between the two mount commands, both mount commands
> will create new vfsmounts.
>
> The superblock generated after the first mount command does not have the
> 'ro' flag, and the read-only status of the file system is implemented by
> checking the read-only flag of the vfsmount. After executing the remount
> command, the 'ro' flag will be added to the superblock. When the second
> mount command is executed, the comparison result between the superblock
> with the 'ro' flag and the fs_context without the flag in the
> nfs_compare_mount_options() function will be different, resulting in the
> creation of a new vfsmount.
>
> This problem can be reproduced by performing the following operations:
> mount -t nfs -o ro,vers=4.0 192.168.240.250:/sdb /mnt/sdb
> mount -t nfs -o remount,ro,vers=4.0 192.168.240.250:/sdb /mnt/sdb
> mount -t nfs -o ro,vers=4.0 192.168.240.250:/sdb /mnt/sdb
> Two vfsmounts are generated:
> [root@localhost ~]# mount | grep nfs
> 192.168.240.250:/sdb on /mnt/sdb type nfs4 (ro,relatime,vers=4.0,
> rsize=1048576,wsize=1048576,namlen=255,hard,proto=tcp,timeo=600,retrans=2,
> sec=sys,clientaddr=192.168.240.251,local_lock=none,addr=192.168.240.250)
> 192.168.240.250:/sdb on /mnt/sdb type nfs4 (ro,relatime,vers=4.0,
> rsize=1048576,wsize=1048576,namlen=255,hard,proto=tcp,timeo=600,retrans=2,
> sec=sys,clientaddr=192.168.240.251,local_lock=none,addr=192.168.240.250)
>
> Fix this by setting sb_flags to second superblock.
>
> Signed-off-by: Li Lingfeng <[email protected]>
> ---
> fs/nfs/namespace.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/fs/nfs/namespace.c b/fs/nfs/namespace.c
> index 887aeacedebd..8b3d75af60d4 100644
> --- a/fs/nfs/namespace.c
> +++ b/fs/nfs/namespace.c
> @@ -158,7 +158,7 @@ struct vfsmount *nfs_d_automount(struct path *path, unsigned int sb_flags)
> /* Open a new filesystem context, transferring parameters from the
> * parent superblock, including the network namespace.
> */
> - fc = fs_context_for_submount(path->mnt->mnt_sb->s_type, path->dentry, 0);
> + fc = fs_context_for_submount(path->mnt->mnt_sb->s_type, path->dentry, sb_flags);
> if (IS_ERR(fc))
> return ERR_CAST(fc);
>