From: Jan Kara <j...@suse.cz>

[ Upstream commit a9ad01bc759df79b0012f43ee52164391e31cd96 ]

There are certain filesystem features which we support for reading but
not for writing. We properly refuse to mount such filesystems read-write
however for some features (such as read-only partitions), we don't check
for these features when remounting the filesystem from read-only to
read-write. Thus such filesystems could be remounted read-write leading
to strange behavior (most likely crashes).

Fix the problem by marking in superblock whether the filesystem has some
features that are supported in read-only mode and check this flag during
remount.

Signed-off-by: Jan Kara <j...@suse.cz>
Signed-off-by: Sasha Levin <sas...@kernel.org>
---
 fs/udf/super.c  | 30 ++++++++++++++++--------------
 fs/udf/udf_sb.h |  2 ++
 2 files changed, 18 insertions(+), 14 deletions(-)

diff --git a/fs/udf/super.c b/fs/udf/super.c
index 6f515651a2c2..b997e3116e37 100644
--- a/fs/udf/super.c
+++ b/fs/udf/super.c
@@ -613,14 +613,11 @@ static int udf_remount_fs(struct super_block *sb, int 
*flags, char *options)
        struct udf_options uopt;
        struct udf_sb_info *sbi = UDF_SB(sb);
        int error = 0;
-       struct logicalVolIntegrityDescImpUse *lvidiu = udf_sb_lvidiu(sb);
+
+       if (!(*flags & SB_RDONLY) && UDF_QUERY_FLAG(sb, UDF_FLAG_RW_INCOMPAT))
+               return -EACCES;
 
        sync_filesystem(sb);
-       if (lvidiu) {
-               int write_rev = le16_to_cpu(lvidiu->minUDFWriteRev);
-               if (write_rev > UDF_MAX_WRITE_VERSION && !(*flags & SB_RDONLY))
-                       return -EACCES;
-       }
 
        uopt.flags = sbi->s_flags;
        uopt.uid   = sbi->s_uid;
@@ -1257,6 +1254,7 @@ static int udf_load_partdesc(struct super_block *sb, 
sector_t block)
                        ret = -EACCES;
                        goto out_bh;
                }
+               UDF_SET_FLAG(sb, UDF_FLAG_RW_INCOMPAT);
                ret = udf_load_vat(sb, i, type1_idx);
                if (ret < 0)
                        goto out_bh;
@@ -2155,10 +2153,12 @@ static int udf_fill_super(struct super_block *sb, void 
*options, int silent)
                                UDF_MAX_READ_VERSION);
                        ret = -EINVAL;
                        goto error_out;
-               } else if (minUDFWriteRev > UDF_MAX_WRITE_VERSION &&
-                          !sb_rdonly(sb)) {
-                       ret = -EACCES;
-                       goto error_out;
+               } else if (minUDFWriteRev > UDF_MAX_WRITE_VERSION) {
+                       if (!sb_rdonly(sb)) {
+                               ret = -EACCES;
+                               goto error_out;
+                       }
+                       UDF_SET_FLAG(sb, UDF_FLAG_RW_INCOMPAT);
                }
 
                sbi->s_udfrev = minUDFWriteRev;
@@ -2176,10 +2176,12 @@ static int udf_fill_super(struct super_block *sb, void 
*options, int silent)
        }
 
        if (sbi->s_partmaps[sbi->s_partition].s_partition_flags &
-                       UDF_PART_FLAG_READ_ONLY &&
-           !sb_rdonly(sb)) {
-               ret = -EACCES;
-               goto error_out;
+                       UDF_PART_FLAG_READ_ONLY) {
+               if (!sb_rdonly(sb)) {
+                       ret = -EACCES;
+                       goto error_out;
+               }
+               UDF_SET_FLAG(sb, UDF_FLAG_RW_INCOMPAT);
        }
 
        if (udf_find_fileset(sb, &fileset, &rootdir)) {
diff --git a/fs/udf/udf_sb.h b/fs/udf/udf_sb.h
index 9424d7cab790..d12e507e9eb2 100644
--- a/fs/udf/udf_sb.h
+++ b/fs/udf/udf_sb.h
@@ -30,6 +30,8 @@
 #define UDF_FLAG_LASTBLOCK_SET 16
 #define UDF_FLAG_BLOCKSIZE_SET 17
 #define UDF_FLAG_INCONSISTENT  18
+#define UDF_FLAG_RW_INCOMPAT   19      /* Set when we find RW incompatible
+                                        * feature */
 
 #define UDF_PART_FLAG_UNALLOC_BITMAP   0x0001
 #define UDF_PART_FLAG_UNALLOC_TABLE    0x0002
-- 
2.17.1

Reply via email to