diff mbox series

[v2] btrfs: fix compat_ro checks against remount

Message ID 82e9c6f8afe4a58e3df60cf601530e14d42264a6.1671613891.git.wqu@suse.com (mailing list archive)
State New, archived
Headers show
Series [v2] btrfs: fix compat_ro checks against remount | expand

Commit Message

Qu Wenruo Dec. 21, 2022, 9:13 a.m. UTC
[BUG]
Even with commit 81d5d61454c3 ("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 moount 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]
Introduce a new argument, *new_flags, to indicate the new mount flags.
That argument can be NULL for the open_ctree() call site.

With that new argument, call site at btrfs_remount() can properly pass
the new super flags, and we can reject the RW remount correctly.

Reported-by: Chung-Chiang Cheng <shepjeng@gmail.com>
Fixes: 81d5d61454c3 ("btrfs: enhance unsupported compat RO flags handling")
Signed-off-by: Qu Wenruo <wqu@suse.com>
Reviewed-by: Anand Jain <anand.jain@oracle.com>
---
Changelog:
v2:
- Add a comment on why @rw_mount is calculated this way
  This will cover RW->RW and RW->RO remount cases, but since the
  fs is already RW, we should not has any unsupported compat_ro flags
  anyway.
---
 fs/btrfs/disk-io.c | 16 +++++++++++++---
 fs/btrfs/disk-io.h |  3 ++-
 fs/btrfs/super.c   |  2 +-
 3 files changed, 16 insertions(+), 5 deletions(-)

Comments

Filipe Manana Dec. 21, 2022, 11:57 a.m. UTC | #1
On Wed, Dec 21, 2022 at 9:26 AM Qu Wenruo <wqu@suse.com> wrote:
>
> [BUG]
> Even with commit 81d5d61454c3 ("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 moount 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]
> Introduce a new argument, *new_flags, to indicate the new mount flags.
> That argument can be NULL for the open_ctree() call site.
>
> With that new argument, call site at btrfs_remount() can properly pass
> the new super flags, and we can reject the RW remount correctly.
>
> Reported-by: Chung-Chiang Cheng <shepjeng@gmail.com>
> Fixes: 81d5d61454c3 ("btrfs: enhance unsupported compat RO flags handling")
> Signed-off-by: Qu Wenruo <wqu@suse.com>
> Reviewed-by: Anand Jain <anand.jain@oracle.com>
> ---
> Changelog:
> v2:
> - Add a comment on why @rw_mount is calculated this way
>   This will cover RW->RW and RW->RO remount cases, but since the
>   fs is already RW, we should not has any unsupported compat_ro flags
>   anyway.
> ---
>  fs/btrfs/disk-io.c | 16 +++++++++++++---
>  fs/btrfs/disk-io.h |  3 ++-
>  fs/btrfs/super.c   |  2 +-
>  3 files changed, 16 insertions(+), 5 deletions(-)
>
> diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c
> index 0888d484df80..973c2e41e132 100644
> --- a/fs/btrfs/disk-io.c
> +++ b/fs/btrfs/disk-io.c
> @@ -3391,12 +3391,22 @@ 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
>   * 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, struct super_block *sb,
> +                        int *new_flags)

Can we please pass new_flags by value instead?
It does not make any sense to pass by pointer - we don't want to
change its value...

>  {
>         struct btrfs_super_block *disk_super = fs_info->super_copy;
>         u64 incompat = btrfs_super_incompat_flags(disk_super);
>         const u64 compat_ro = btrfs_super_compat_ro_flags(disk_super);
>         const u64 compat_ro_unsupp = (compat_ro & ~BTRFS_FEATURE_COMPAT_RO_SUPP);
> +       /*
> +        * For RW mount or remount to RW, we shouldn't allow any unsupported
> +        * compat_ro flags. Here we just check if any of our sb or new flag
> +        * is RW.
> +        * This will cover cases like RW->RW and RW->RO, but since it's
> +        * already RW, we shouldn't have any unsupported compat_ro flags anyway.
> +        */
> +       bool rw_mount = (!sb_rdonly(sb) ||
> +                        (new_flags && !(*new_flags & SB_RDONLY)));

It also would make this expression shorter and easier to read...

>
>         if (incompat & ~BTRFS_FEATURE_INCOMPAT_SUPP) {
>                 btrfs_err(fs_info,
> @@ -3430,7 +3440,7 @@ int btrfs_check_features(struct btrfs_fs_info *fs_info, struct super_block *sb)
>         if (btrfs_super_nodesize(disk_super) > PAGE_SIZE)
>                 incompat |= BTRFS_FEATURE_INCOMPAT_BIG_METADATA;
>
> -       if (compat_ro_unsupp && !sb_rdonly(sb)) {
> +       if (compat_ro_unsupp && rw_mount) {
>                 btrfs_err(fs_info,
>         "cannot mount read-write because of unknown compat_ro features (0x%llx)",
>                        compat_ro);
> @@ -3633,7 +3643,7 @@ int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_device
>                 goto fail_alloc;
>         }
>
> -       ret = btrfs_check_features(fs_info, sb);
> +       ret = btrfs_check_features(fs_info, sb, NULL);

Here just pass 0 for flags.

>         if (ret < 0) {
>                 err = ret;
>                 goto fail_alloc;
> diff --git a/fs/btrfs/disk-io.h b/fs/btrfs/disk-io.h
> index 363935cfc084..e83453c7c429 100644
> --- a/fs/btrfs/disk-io.h
> +++ b/fs/btrfs/disk-io.h
> @@ -50,7 +50,8 @@ int __cold open_ctree(struct super_block *sb,
>  void __cold close_ctree(struct btrfs_fs_info *fs_info);
>  int btrfs_validate_super(struct btrfs_fs_info *fs_info,
>                          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, struct super_block *sb,
> +                        int *new_flags);
>  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_one_super(struct block_device *bdev,
> diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c
> index d5de18d6517e..bc2094aa9a40 100644
> --- a/fs/btrfs/super.c
> +++ b/fs/btrfs/super.c
> @@ -1705,7 +1705,7 @@ static int btrfs_remount(struct super_block *sb, int *flags, char *data)
>         if (ret)
>                 goto restore;
>
> -       ret = btrfs_check_features(fs_info, sb);
> +       ret = btrfs_check_features(fs_info, sb, flags);

And here pass *flags.

Thanks.

>         if (ret < 0)
>                 goto restore;
>
> --
> 2.39.0
>
David Sterba Dec. 21, 2022, 11:59 a.m. UTC | #2
On Wed, Dec 21, 2022 at 05:13:00PM +0800, Qu Wenruo wrote:
> [BUG]
> Even with commit 81d5d61454c3 ("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 moount 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]
> Introduce a new argument, *new_flags, to indicate the new mount flags.
> That argument can be NULL for the open_ctree() call site.
> 
> With that new argument, call site at btrfs_remount() can properly pass
> the new super flags, and we can reject the RW remount correctly.
> 
> Reported-by: Chung-Chiang Cheng <shepjeng@gmail.com>
> Fixes: 81d5d61454c3 ("btrfs: enhance unsupported compat RO flags handling")
> Signed-off-by: Qu Wenruo <wqu@suse.com>
> Reviewed-by: Anand Jain <anand.jain@oracle.com>
> ---
> Changelog:
> v2:
> - Add a comment on why @rw_mount is calculated this way
>   This will cover RW->RW and RW->RO remount cases, but since the
>   fs is already RW, we should not has any unsupported compat_ro flags
>   anyway.

Added to misc-next, thanks.

> -int btrfs_check_features(struct btrfs_fs_info *fs_info, struct super_block *sb)
> +int btrfs_check_features(struct btrfs_fs_info *fs_info, struct super_block *sb,
> +			 int *new_flags)

Flags should be unsigned as they do bitwise ops only but in this case
it's inherited from the remount_fs prototype (which actually gets
unsigned int from the struct fs_contex::sb_flags in legacy_reconfigure)
so we're stuck with int.
Filipe Manana Dec. 21, 2022, 12:11 p.m. UTC | #3
On Wed, Dec 21, 2022 at 11:57 AM Filipe Manana <fdmanana@kernel.org> wrote:
>
> On Wed, Dec 21, 2022 at 9:26 AM Qu Wenruo <wqu@suse.com> wrote:
> >
> > [BUG]
> > Even with commit 81d5d61454c3 ("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 moount 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]
> > Introduce a new argument, *new_flags, to indicate the new mount flags.
> > That argument can be NULL for the open_ctree() call site.
> >
> > With that new argument, call site at btrfs_remount() can properly pass
> > the new super flags, and we can reject the RW remount correctly.
> >
> > Reported-by: Chung-Chiang Cheng <shepjeng@gmail.com>
> > Fixes: 81d5d61454c3 ("btrfs: enhance unsupported compat RO flags handling")
> > Signed-off-by: Qu Wenruo <wqu@suse.com>
> > Reviewed-by: Anand Jain <anand.jain@oracle.com>
> > ---
> > Changelog:
> > v2:
> > - Add a comment on why @rw_mount is calculated this way
> >   This will cover RW->RW and RW->RO remount cases, but since the
> >   fs is already RW, we should not has any unsupported compat_ro flags
> >   anyway.
> > ---
> >  fs/btrfs/disk-io.c | 16 +++++++++++++---
> >  fs/btrfs/disk-io.h |  3 ++-
> >  fs/btrfs/super.c   |  2 +-
> >  3 files changed, 16 insertions(+), 5 deletions(-)
> >
> > diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c
> > index 0888d484df80..973c2e41e132 100644
> > --- a/fs/btrfs/disk-io.c
> > +++ b/fs/btrfs/disk-io.c
> > @@ -3391,12 +3391,22 @@ 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
> >   * 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, struct super_block *sb,
> > +                        int *new_flags)
>
> Can we please pass new_flags by value instead?
> It does not make any sense to pass by pointer - we don't want to
> change its value...

Or just make it a:   bool rw_switch

>
> >  {
> >         struct btrfs_super_block *disk_super = fs_info->super_copy;
> >         u64 incompat = btrfs_super_incompat_flags(disk_super);
> >         const u64 compat_ro = btrfs_super_compat_ro_flags(disk_super);
> >         const u64 compat_ro_unsupp = (compat_ro & ~BTRFS_FEATURE_COMPAT_RO_SUPP);
> > +       /*
> > +        * For RW mount or remount to RW, we shouldn't allow any unsupported
> > +        * compat_ro flags. Here we just check if any of our sb or new flag
> > +        * is RW.
> > +        * This will cover cases like RW->RW and RW->RO, but since it's
> > +        * already RW, we shouldn't have any unsupported compat_ro flags anyway.
> > +        */
> > +       bool rw_mount = (!sb_rdonly(sb) ||
> > +                        (new_flags && !(*new_flags & SB_RDONLY)));
>
> It also would make this expression shorter and easier to read...
>
> >
> >         if (incompat & ~BTRFS_FEATURE_INCOMPAT_SUPP) {
> >                 btrfs_err(fs_info,
> > @@ -3430,7 +3440,7 @@ int btrfs_check_features(struct btrfs_fs_info *fs_info, struct super_block *sb)
> >         if (btrfs_super_nodesize(disk_super) > PAGE_SIZE)
> >                 incompat |= BTRFS_FEATURE_INCOMPAT_BIG_METADATA;
> >
> > -       if (compat_ro_unsupp && !sb_rdonly(sb)) {
> > +       if (compat_ro_unsupp && rw_mount) {
> >                 btrfs_err(fs_info,
> >         "cannot mount read-write because of unknown compat_ro features (0x%llx)",
> >                        compat_ro);
> > @@ -3633,7 +3643,7 @@ int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_device
> >                 goto fail_alloc;
> >         }
> >
> > -       ret = btrfs_check_features(fs_info, sb);
> > +       ret = btrfs_check_features(fs_info, sb, NULL);
>
> Here just pass 0 for flags.

Here pass a false value.

>
> >         if (ret < 0) {
> >                 err = ret;
> >                 goto fail_alloc;
> > diff --git a/fs/btrfs/disk-io.h b/fs/btrfs/disk-io.h
> > index 363935cfc084..e83453c7c429 100644
> > --- a/fs/btrfs/disk-io.h
> > +++ b/fs/btrfs/disk-io.h
> > @@ -50,7 +50,8 @@ int __cold open_ctree(struct super_block *sb,
> >  void __cold close_ctree(struct btrfs_fs_info *fs_info);
> >  int btrfs_validate_super(struct btrfs_fs_info *fs_info,
> >                          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, struct super_block *sb,
> > +                        int *new_flags);
> >  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_one_super(struct block_device *bdev,
> > diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c
> > index d5de18d6517e..bc2094aa9a40 100644
> > --- a/fs/btrfs/super.c
> > +++ b/fs/btrfs/super.c
> > @@ -1705,7 +1705,7 @@ static int btrfs_remount(struct super_block *sb, int *flags, char *data)
> >         if (ret)
> >                 goto restore;
> >
> > -       ret = btrfs_check_features(fs_info, sb);
> > +       ret = btrfs_check_features(fs_info, sb, flags);
>
> And here pass *flags.

And here pass:  !(*flags & SB_RDONLY)

Makes the whole thing easier to read and shorter.

Thanks.


>
> Thanks.
>
> >         if (ret < 0)
> >                 goto restore;
> >
> > --
> > 2.39.0
> >
Qu Wenruo Dec. 21, 2022, 12:12 p.m. UTC | #4
On 2022/12/21 19:57, Filipe Manana wrote:
> On Wed, Dec 21, 2022 at 9:26 AM Qu Wenruo <wqu@suse.com> wrote:
>>
>> [BUG]
>> Even with commit 81d5d61454c3 ("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 moount 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]
>> Introduce a new argument, *new_flags, to indicate the new mount flags.
>> That argument can be NULL for the open_ctree() call site.
>>
>> With that new argument, call site at btrfs_remount() can properly pass
>> the new super flags, and we can reject the RW remount correctly.
>>
>> Reported-by: Chung-Chiang Cheng <shepjeng@gmail.com>
>> Fixes: 81d5d61454c3 ("btrfs: enhance unsupported compat RO flags handling")
>> Signed-off-by: Qu Wenruo <wqu@suse.com>
>> Reviewed-by: Anand Jain <anand.jain@oracle.com>
>> ---
>> Changelog:
>> v2:
>> - Add a comment on why @rw_mount is calculated this way
>>    This will cover RW->RW and RW->RO remount cases, but since the
>>    fs is already RW, we should not has any unsupported compat_ro flags
>>    anyway.
>> ---
>>   fs/btrfs/disk-io.c | 16 +++++++++++++---
>>   fs/btrfs/disk-io.h |  3 ++-
>>   fs/btrfs/super.c   |  2 +-
>>   3 files changed, 16 insertions(+), 5 deletions(-)
>>
>> diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c
>> index 0888d484df80..973c2e41e132 100644
>> --- a/fs/btrfs/disk-io.c
>> +++ b/fs/btrfs/disk-io.c
>> @@ -3391,12 +3391,22 @@ 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
>>    * 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, struct super_block *sb,
>> +                        int *new_flags)
> 
> Can we please pass new_flags by value instead?
> It does not make any sense to pass by pointer - we don't want to
> change its value...
> 
>>   {
>>          struct btrfs_super_block *disk_super = fs_info->super_copy;
>>          u64 incompat = btrfs_super_incompat_flags(disk_super);
>>          const u64 compat_ro = btrfs_super_compat_ro_flags(disk_super);
>>          const u64 compat_ro_unsupp = (compat_ro & ~BTRFS_FEATURE_COMPAT_RO_SUPP);
>> +       /*
>> +        * For RW mount or remount to RW, we shouldn't allow any unsupported
>> +        * compat_ro flags. Here we just check if any of our sb or new flag
>> +        * is RW.
>> +        * This will cover cases like RW->RW and RW->RO, but since it's
>> +        * already RW, we shouldn't have any unsupported compat_ro flags anyway.
>> +        */
>> +       bool rw_mount = (!sb_rdonly(sb) ||
>> +                        (new_flags && !(*new_flags & SB_RDONLY)));
> 
> It also would make this expression shorter and easier to read...
> 
>>
>>          if (incompat & ~BTRFS_FEATURE_INCOMPAT_SUPP) {
>>                  btrfs_err(fs_info,
>> @@ -3430,7 +3440,7 @@ int btrfs_check_features(struct btrfs_fs_info *fs_info, struct super_block *sb)
>>          if (btrfs_super_nodesize(disk_super) > PAGE_SIZE)
>>                  incompat |= BTRFS_FEATURE_INCOMPAT_BIG_METADATA;
>>
>> -       if (compat_ro_unsupp && !sb_rdonly(sb)) {
>> +       if (compat_ro_unsupp && rw_mount) {
>>                  btrfs_err(fs_info,
>>          "cannot mount read-write because of unknown compat_ro features (0x%llx)",
>>                         compat_ro);
>> @@ -3633,7 +3643,7 @@ int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_device
>>                  goto fail_alloc;
>>          }
>>
>> -       ret = btrfs_check_features(fs_info, sb);
>> +       ret = btrfs_check_features(fs_info, sb, NULL);
> 
> Here just pass 0 for flags.

Initially I avoided this because I'm not sure if it's bit flags.
But it turned out that super_block->s_flags is indeed bit flags.

So in that case, 0 is completely fine, and I believe we can also go 
s_flags directly instead of passing sb.
This would also solve the type mismatch problem mentioned by David.

I'll update it in the next version.

Thanks,
Qu
> 
>>          if (ret < 0) {
>>                  err = ret;
>>                  goto fail_alloc;
>> diff --git a/fs/btrfs/disk-io.h b/fs/btrfs/disk-io.h
>> index 363935cfc084..e83453c7c429 100644
>> --- a/fs/btrfs/disk-io.h
>> +++ b/fs/btrfs/disk-io.h
>> @@ -50,7 +50,8 @@ int __cold open_ctree(struct super_block *sb,
>>   void __cold close_ctree(struct btrfs_fs_info *fs_info);
>>   int btrfs_validate_super(struct btrfs_fs_info *fs_info,
>>                           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, struct super_block *sb,
>> +                        int *new_flags);
>>   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_one_super(struct block_device *bdev,
>> diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c
>> index d5de18d6517e..bc2094aa9a40 100644
>> --- a/fs/btrfs/super.c
>> +++ b/fs/btrfs/super.c
>> @@ -1705,7 +1705,7 @@ static int btrfs_remount(struct super_block *sb, int *flags, char *data)
>>          if (ret)
>>                  goto restore;
>>
>> -       ret = btrfs_check_features(fs_info, sb);
>> +       ret = btrfs_check_features(fs_info, sb, flags);
> 
> And here pass *flags.
> 
> Thanks.
> 
>>          if (ret < 0)
>>                  goto restore;
>>
>> --
>> 2.39.0
>>
Qu Wenruo Dec. 21, 2022, 12:26 p.m. UTC | #5
On 2022/12/21 20:11, Filipe Manana wrote:
[...]
>> Can we please pass new_flags by value instead?
>> It does not make any sense to pass by pointer - we don't want to
>> change its value...
> 
> Or just make it a:   bool rw_switch

OK, this inspired me more.

Just replace the @sb arugument with  bool @rw_mount.

[...]
>>> +       ret = btrfs_check_features(fs_info, sb, NULL);
>>
>> Here just pass 0 for flags.
> 
> Here pass a false value.

And pass !sb_rdonly(sb).

Thanks,
Qu

> 
>>
>>>          if (ret < 0) {
>>>                  err = ret;
>>>                  goto fail_alloc;
>>> diff --git a/fs/btrfs/disk-io.h b/fs/btrfs/disk-io.h
>>> index 363935cfc084..e83453c7c429 100644
>>> --- a/fs/btrfs/disk-io.h
>>> +++ b/fs/btrfs/disk-io.h
>>> @@ -50,7 +50,8 @@ int __cold open_ctree(struct super_block *sb,
>>>   void __cold close_ctree(struct btrfs_fs_info *fs_info);
>>>   int btrfs_validate_super(struct btrfs_fs_info *fs_info,
>>>                           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, struct super_block *sb,
>>> +                        int *new_flags);
>>>   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_one_super(struct block_device *bdev,
>>> diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c
>>> index d5de18d6517e..bc2094aa9a40 100644
>>> --- a/fs/btrfs/super.c
>>> +++ b/fs/btrfs/super.c
>>> @@ -1705,7 +1705,7 @@ static int btrfs_remount(struct super_block *sb, int *flags, char *data)
>>>          if (ret)
>>>                  goto restore;
>>>
>>> -       ret = btrfs_check_features(fs_info, sb);
>>> +       ret = btrfs_check_features(fs_info, sb, flags);
>>
>> And here pass *flags.
> 
> And here pass:  !(*flags & SB_RDONLY) >
> Makes the whole thing easier to read and shorter.
> 
> Thanks.
> 
> 
>>
>> Thanks.
>>
>>>          if (ret < 0)
>>>                  goto restore;
>>>
>>> --
>>> 2.39.0
>>>
diff mbox series

Patch

diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c
index 0888d484df80..973c2e41e132 100644
--- a/fs/btrfs/disk-io.c
+++ b/fs/btrfs/disk-io.c
@@ -3391,12 +3391,22 @@  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
  * 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, struct super_block *sb,
+			 int *new_flags)
 {
 	struct btrfs_super_block *disk_super = fs_info->super_copy;
 	u64 incompat = btrfs_super_incompat_flags(disk_super);
 	const u64 compat_ro = btrfs_super_compat_ro_flags(disk_super);
 	const u64 compat_ro_unsupp = (compat_ro & ~BTRFS_FEATURE_COMPAT_RO_SUPP);
+	/*
+	 * For RW mount or remount to RW, we shouldn't allow any unsupported
+	 * compat_ro flags. Here we just check if any of our sb or new flag
+	 * is RW.
+	 * This will cover cases like RW->RW and RW->RO, but since it's
+	 * already RW, we shouldn't have any unsupported compat_ro flags anyway.
+	 */
+	bool rw_mount = (!sb_rdonly(sb) ||
+			 (new_flags && !(*new_flags & SB_RDONLY)));
 
 	if (incompat & ~BTRFS_FEATURE_INCOMPAT_SUPP) {
 		btrfs_err(fs_info,
@@ -3430,7 +3440,7 @@  int btrfs_check_features(struct btrfs_fs_info *fs_info, struct super_block *sb)
 	if (btrfs_super_nodesize(disk_super) > PAGE_SIZE)
 		incompat |= BTRFS_FEATURE_INCOMPAT_BIG_METADATA;
 
-	if (compat_ro_unsupp && !sb_rdonly(sb)) {
+	if (compat_ro_unsupp && rw_mount) {
 		btrfs_err(fs_info,
 	"cannot mount read-write because of unknown compat_ro features (0x%llx)",
 		       compat_ro);
@@ -3633,7 +3643,7 @@  int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_device
 		goto fail_alloc;
 	}
 
-	ret = btrfs_check_features(fs_info, sb);
+	ret = btrfs_check_features(fs_info, sb, NULL);
 	if (ret < 0) {
 		err = ret;
 		goto fail_alloc;
diff --git a/fs/btrfs/disk-io.h b/fs/btrfs/disk-io.h
index 363935cfc084..e83453c7c429 100644
--- a/fs/btrfs/disk-io.h
+++ b/fs/btrfs/disk-io.h
@@ -50,7 +50,8 @@  int __cold open_ctree(struct super_block *sb,
 void __cold close_ctree(struct btrfs_fs_info *fs_info);
 int btrfs_validate_super(struct btrfs_fs_info *fs_info,
 			 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, struct super_block *sb,
+			 int *new_flags);
 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_one_super(struct block_device *bdev,
diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c
index d5de18d6517e..bc2094aa9a40 100644
--- a/fs/btrfs/super.c
+++ b/fs/btrfs/super.c
@@ -1705,7 +1705,7 @@  static int btrfs_remount(struct super_block *sb, int *flags, char *data)
 	if (ret)
 		goto restore;
 
-	ret = btrfs_check_features(fs_info, sb);
+	ret = btrfs_check_features(fs_info, sb, flags);
 	if (ret < 0)
 		goto restore;