btrfs: fix compat_ro checks against remount
commit2ba48b2004
upstream. [BUG] Even with commit81d5d61454
("btrfs: enhance unsupported compat RO flags handling"), btrfs can still mount a fs with unsupported compat_ro flags read-only, then remount it RW: # btrfs ins dump-super /dev/loop0 | grep compat_ro_flags -A 3 compat_ro_flags 0x403 ( FREE_SPACE_TREE | FREE_SPACE_TREE_VALID | unknown flag: 0x400 ) # mount /dev/loop0 /mnt/btrfs mount: /mnt/btrfs: wrong fs type, bad option, bad superblock on /dev/loop0, missing codepage or helper program, or other error. dmesg(1) may have more information after failed mount system call. ^^^ RW mount failed as expected ^^^ # dmesg -t | tail -n5 loop0: detected capacity change from 0 to 1048576 BTRFS: device fsid cb5b82f5-0fdd-4d81-9b4b-78533c324afa devid 1 transid 7 /dev/loop0 scanned by mount (1146) BTRFS info (device loop0): using crc32c (crc32c-intel) checksum algorithm BTRFS info (device loop0): using free space tree BTRFS error (device loop0): cannot mount read-write because of unknown compat_ro features (0x403) BTRFS error (device loop0): open_ctree failed # mount /dev/loop0 -o ro /mnt/btrfs # mount -o remount,rw /mnt/btrfs ^^^ RW remount succeeded unexpectedly ^^^ [CAUSE] Currently we use btrfs_check_features() to check compat_ro flags against our current mount flags. That function get reused between open_ctree() and btrfs_remount(). But for btrfs_remount(), the super block we passed in still has the old mount flags, thus btrfs_check_features() still believes we're mounting read-only. [FIX] Replace the existing @sb argument with @is_rw_mount. As originally we only use @sb to determine if the mount is RW. Now it's callers' responsibility to determine if the mount is RW, and since there are only two callers, the check is pretty simple: - caller in open_ctree() Just pass !sb_rdonly(). - caller in btrfs_remount() Pass !(*flags & SB_RDONLY), as our check should be against the new flags. Now we can correctly reject the RW remount: # mount /dev/loop0 -o ro /mnt/btrfs # mount -o remount,rw /mnt/btrfs mount: /mnt/btrfs: mount point not mounted or bad option. dmesg(1) may have more information after failed mount system call. # dmesg -t | tail -n 1 BTRFS error (device loop0: state M): cannot mount read-write because of unknown compat_ro features (0x403) Reported-by: Chung-Chiang Cheng <shepjeng@gmail.com> Fixes:81d5d61454
("btrfs: enhance unsupported compat RO flags handling") CC: stable@vger.kernel.org # 5.15+ Reviewed-by: Anand Jain <anand.jain@oracle.com> Signed-off-by: Qu Wenruo <wqu@suse.com> Reviewed-by: David Sterba <dsterba@suse.com> Signed-off-by: David Sterba <dsterba@suse.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
427a36cf81
commit
def94d5966
|
@ -3296,6 +3296,8 @@ int btrfs_start_pre_rw_mount(struct btrfs_fs_info *fs_info)
|
||||||
/*
|
/*
|
||||||
* Do various sanity and dependency checks of different features.
|
* Do various sanity and dependency checks of different features.
|
||||||
*
|
*
|
||||||
|
* @is_rw_mount: If the mount is read-write.
|
||||||
|
*
|
||||||
* This is the place for less strict checks (like for subpage or artificial
|
* This is the place for less strict checks (like for subpage or artificial
|
||||||
* feature dependencies).
|
* feature dependencies).
|
||||||
*
|
*
|
||||||
|
@ -3306,7 +3308,7 @@ int btrfs_start_pre_rw_mount(struct btrfs_fs_info *fs_info)
|
||||||
* (space cache related) can modify on-disk format like free space tree and
|
* (space cache related) can modify on-disk format like free space tree and
|
||||||
* screw up certain feature dependencies.
|
* screw up certain feature dependencies.
|
||||||
*/
|
*/
|
||||||
int btrfs_check_features(struct btrfs_fs_info *fs_info, struct super_block *sb)
|
int btrfs_check_features(struct btrfs_fs_info *fs_info, bool is_rw_mount)
|
||||||
{
|
{
|
||||||
struct btrfs_super_block *disk_super = fs_info->super_copy;
|
struct btrfs_super_block *disk_super = fs_info->super_copy;
|
||||||
u64 incompat = btrfs_super_incompat_flags(disk_super);
|
u64 incompat = btrfs_super_incompat_flags(disk_super);
|
||||||
|
@ -3345,7 +3347,7 @@ int btrfs_check_features(struct btrfs_fs_info *fs_info, struct super_block *sb)
|
||||||
if (btrfs_super_nodesize(disk_super) > PAGE_SIZE)
|
if (btrfs_super_nodesize(disk_super) > PAGE_SIZE)
|
||||||
incompat |= BTRFS_FEATURE_INCOMPAT_BIG_METADATA;
|
incompat |= BTRFS_FEATURE_INCOMPAT_BIG_METADATA;
|
||||||
|
|
||||||
if (compat_ro_unsupp && !sb_rdonly(sb)) {
|
if (compat_ro_unsupp && is_rw_mount) {
|
||||||
btrfs_err(fs_info,
|
btrfs_err(fs_info,
|
||||||
"cannot mount read-write because of unknown compat_ro features (0x%llx)",
|
"cannot mount read-write because of unknown compat_ro features (0x%llx)",
|
||||||
compat_ro);
|
compat_ro);
|
||||||
|
@ -3548,7 +3550,7 @@ int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_device
|
||||||
goto fail_alloc;
|
goto fail_alloc;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = btrfs_check_features(fs_info, sb);
|
ret = btrfs_check_features(fs_info, !sb_rdonly(sb));
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
err = ret;
|
err = ret;
|
||||||
goto fail_alloc;
|
goto fail_alloc;
|
||||||
|
|
|
@ -50,7 +50,7 @@ int __cold open_ctree(struct super_block *sb,
|
||||||
void __cold close_ctree(struct btrfs_fs_info *fs_info);
|
void __cold close_ctree(struct btrfs_fs_info *fs_info);
|
||||||
int btrfs_validate_super(struct btrfs_fs_info *fs_info,
|
int btrfs_validate_super(struct btrfs_fs_info *fs_info,
|
||||||
struct btrfs_super_block *sb, int mirror_num);
|
struct btrfs_super_block *sb, int mirror_num);
|
||||||
int btrfs_check_features(struct btrfs_fs_info *fs_info, struct super_block *sb);
|
int btrfs_check_features(struct btrfs_fs_info *fs_info, bool is_rw_mount);
|
||||||
int write_all_supers(struct btrfs_fs_info *fs_info, int max_mirrors);
|
int write_all_supers(struct btrfs_fs_info *fs_info, int max_mirrors);
|
||||||
struct btrfs_super_block *btrfs_read_dev_super(struct block_device *bdev);
|
struct btrfs_super_block *btrfs_read_dev_super(struct block_device *bdev);
|
||||||
struct btrfs_super_block *btrfs_read_dev_one_super(struct block_device *bdev,
|
struct btrfs_super_block *btrfs_read_dev_one_super(struct block_device *bdev,
|
||||||
|
|
|
@ -2014,7 +2014,7 @@ static int btrfs_remount(struct super_block *sb, int *flags, char *data)
|
||||||
if (ret)
|
if (ret)
|
||||||
goto restore;
|
goto restore;
|
||||||
|
|
||||||
ret = btrfs_check_features(fs_info, sb);
|
ret = btrfs_check_features(fs_info, !(*flags & SB_RDONLY));
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto restore;
|
goto restore;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue