Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

btrfs: fix per-subvolume RO/RW flags with new mount API

[BUG]
With util-linux 2.40.2, the 'mount' utility is already utilizing the new
mount API. e.g:

# strace mount -o subvol=subv1,ro /dev/test/scratch1 /mnt/test/
...
fsconfig(3, FSCONFIG_SET_STRING, "source", "/dev/mapper/test-scratch1", 0) = 0
fsconfig(3, FSCONFIG_SET_STRING, "subvol", "subv1", 0) = 0
fsconfig(3, FSCONFIG_SET_FLAG, "ro", NULL, 0) = 0
fsconfig(3, FSCONFIG_CMD_CREATE, NULL, NULL, 0) = 0
fsmount(3, FSMOUNT_CLOEXEC, 0) = 4
mount_setattr(4, "", AT_EMPTY_PATH, {attr_set=MOUNT_ATTR_RDONLY, attr_clr=0, propagation=0 /* MS_??? */, userns_fd=0}, 32) = 0
move_mount(4, "", AT_FDCWD, "/mnt/test", MOVE_MOUNT_F_EMPTY_PATH) = 0

But this leads to a new problem, that per-subvolume RO/RW mount no
longer works, if the initial mount is RO:

# mount -o subvol=subv1,ro /dev/test/scratch1 /mnt/test
# mount -o rw,subvol=subv2 /dev/test/scratch1 /mnt/scratch
# mount | grep mnt
/dev/mapper/test-scratch1 on /mnt/test type btrfs (ro,relatime,discard=async,space_cache=v2,subvolid=256,subvol=/subv1)
/dev/mapper/test-scratch1 on /mnt/scratch type btrfs (ro,relatime,discard=async,space_cache=v2,subvolid=257,subvol=/subv2)
# touch /mnt/scratch/foobar
touch: cannot touch '/mnt/scratch/foobar': Read-only file system

This is a common use cases on distros.

[CAUSE]
We have a workaround for remount to handle the RO->RW change, but if the
mount is using the new mount API, we do not do that, and rely on the
mount tool NOT to set the ro flag.

But that's not how the mount tool is doing for the new API:

fsconfig(3, FSCONFIG_SET_STRING, "source", "/dev/mapper/test-scratch1", 0) = 0
fsconfig(3, FSCONFIG_SET_STRING, "subvol", "subv1", 0) = 0
fsconfig(3, FSCONFIG_SET_FLAG, "ro", NULL, 0) = 0 <<<< Setting RO flag for super block
fsconfig(3, FSCONFIG_CMD_CREATE, NULL, NULL, 0) = 0
fsmount(3, FSMOUNT_CLOEXEC, 0) = 4
mount_setattr(4, "", AT_EMPTY_PATH, {attr_set=MOUNT_ATTR_RDONLY, attr_clr=0, propagation=0 /* MS_??? */, userns_fd=0}, 32) = 0
move_mount(4, "", AT_FDCWD, "/mnt/test", MOVE_MOUNT_F_EMPTY_PATH) = 0

This means we will set the super block RO at the first mount.

Later RW mount will not try to reconfigure the fs to RW because the
mount tool is already using the new API.

This totally breaks the per-subvolume RO/RW mount behavior.

[FIX]
Do not skip the reconfiguration even if using the new API. The old
comments are just expecting any mount tool to properly skip the RO flag
set even if we specify "ro", which is not the reality.

Update the comments regarding the backward compatibility on the kernel
level so it works with old and new mount utilities.

CC: stable@vger.kernel.org # 6.8+
Fixes: f044b318675f ("btrfs: handle the ro->rw transition for mounting different subvolumes")
Signed-off-by: Qu Wenruo <wqu@suse.com>
Reviewed-by: David Sterba <dsterba@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>

authored by

Qu Wenruo and committed by
David Sterba
cda7163d 77b0d113

+5 -20
+5 -20
fs/btrfs/super.c
··· 1979 1979 * fsconfig(FSCONFIG_SET_FLAG, "ro"). This option is seen by the filesystem 1980 1980 * in fc->sb_flags. 1981 1981 * 1982 - * This disambiguation has rather positive consequences. Mounting a subvolume 1983 - * ro will not also turn the superblock ro. Only the mount for the subvolume 1984 - * will become ro. 1985 - * 1986 - * So, if the superblock creation request comes from the new mount API the 1987 - * caller must have explicitly done: 1988 - * 1989 - * fsconfig(FSCONFIG_SET_FLAG, "ro") 1990 - * fsmount/mount_setattr(MOUNT_ATTR_RDONLY) 1991 - * 1992 - * IOW, at some point the caller must have explicitly turned the whole 1993 - * superblock ro and we shouldn't just undo it like we did for the old mount 1994 - * API. In any case, it lets us avoid the hack in the new mount API. 1995 - * 1996 - * Consequently, the remounting hack must only be used for requests originating 1997 - * from the old mount API and should be marked for full deprecation so it can be 1998 - * turned off in a couple of years. 1999 - * 2000 - * The new mount API has no reason to support this hack. 1982 + * But, currently the util-linux mount command already utilizes the new mount 1983 + * API and is still setting fsconfig(FSCONFIG_SET_FLAG, "ro") no matter if it's 1984 + * btrfs or not, setting the whole super block RO. To make per-subvolume mounting 1985 + * work with different options work we need to keep backward compatibility. 2001 1986 */ 2002 1987 static struct vfsmount *btrfs_reconfigure_for_mount(struct fs_context *fc) 2003 1988 { ··· 2004 2019 if (IS_ERR(mnt)) 2005 2020 return mnt; 2006 2021 2007 - if (!fc->oldapi || !ro2rw) 2022 + if (!ro2rw) 2008 2023 return mnt; 2009 2024 2010 2025 /* We need to convert to rw, call reconfigure. */