diff options
author | Jan Kara <jack@suse.cz> | 2019-08-29 08:11:54 -0400 |
---|---|---|
committer | Jan Kara <jack@suse.cz> | 2019-09-04 12:19:25 -0400 |
commit | 2dee5aac05565933c5bf6ad4acd4f9bcd6ea2ff7 (patch) | |
tree | c2658834b67964da21a1be9cb47e21234b4da369 /fs/udf/super.c | |
parent | c3367a1b47d590f97109cd4b5189e750fb26c0f1 (diff) |
udf: Verify domain identifier fields
OSTA UDF standard defines that domain identifier in logical volume
descriptor and file set descriptor should contain a particular string
and the identifier suffix contains flags possibly making media
write-protected. Verify these constraints and allow only read-only mount
if they are not met.
Tested-by: Steven J. Magnani <steve@digidescorp.com>
Reviewed-by: Steven J. Magnani <steve@digidescorp.com>
Signed-off-by: Jan Kara <jack@suse.cz>
Diffstat (limited to 'fs/udf/super.c')
-rw-r--r-- | fs/udf/super.c | 91 |
1 files changed, 64 insertions, 27 deletions
diff --git a/fs/udf/super.c b/fs/udf/super.c index 56da1e1680ea..9e72a9e98ab3 100644 --- a/fs/udf/super.c +++ b/fs/udf/super.c | |||
@@ -94,8 +94,8 @@ static int udf_remount_fs(struct super_block *, int *, char *); | |||
94 | static void udf_load_logicalvolint(struct super_block *, struct kernel_extent_ad); | 94 | static void udf_load_logicalvolint(struct super_block *, struct kernel_extent_ad); |
95 | static int udf_find_fileset(struct super_block *, struct kernel_lb_addr *, | 95 | static int udf_find_fileset(struct super_block *, struct kernel_lb_addr *, |
96 | struct kernel_lb_addr *); | 96 | struct kernel_lb_addr *); |
97 | static void udf_load_fileset(struct super_block *, struct buffer_head *, | 97 | static int udf_load_fileset(struct super_block *, struct fileSetDesc *, |
98 | struct kernel_lb_addr *); | 98 | struct kernel_lb_addr *); |
99 | static void udf_open_lvid(struct super_block *); | 99 | static void udf_open_lvid(struct super_block *); |
100 | static void udf_close_lvid(struct super_block *); | 100 | static void udf_close_lvid(struct super_block *); |
101 | static unsigned int udf_count_free(struct super_block *); | 101 | static unsigned int udf_count_free(struct super_block *); |
@@ -775,28 +775,27 @@ static int udf_find_fileset(struct super_block *sb, | |||
775 | { | 775 | { |
776 | struct buffer_head *bh = NULL; | 776 | struct buffer_head *bh = NULL; |
777 | uint16_t ident; | 777 | uint16_t ident; |
778 | int ret; | ||
778 | 779 | ||
779 | if (fileset->logicalBlockNum != 0xFFFFFFFF || | 780 | if (fileset->logicalBlockNum == 0xFFFFFFFF && |
780 | fileset->partitionReferenceNum != 0xFFFF) { | 781 | fileset->partitionReferenceNum == 0xFFFF) |
781 | bh = udf_read_ptagged(sb, fileset, 0, &ident); | 782 | return -EINVAL; |
782 | |||
783 | if (!bh) { | ||
784 | return 1; | ||
785 | } else if (ident != TAG_IDENT_FSD) { | ||
786 | brelse(bh); | ||
787 | return 1; | ||
788 | } | ||
789 | |||
790 | udf_debug("Fileset at block=%u, partition=%u\n", | ||
791 | fileset->logicalBlockNum, | ||
792 | fileset->partitionReferenceNum); | ||
793 | 783 | ||
794 | UDF_SB(sb)->s_partition = fileset->partitionReferenceNum; | 784 | bh = udf_read_ptagged(sb, fileset, 0, &ident); |
795 | udf_load_fileset(sb, bh, root); | 785 | if (!bh) |
786 | return -EIO; | ||
787 | if (ident != TAG_IDENT_FSD) { | ||
796 | brelse(bh); | 788 | brelse(bh); |
797 | return 0; | 789 | return -EINVAL; |
798 | } | 790 | } |
799 | return 1; | 791 | |
792 | udf_debug("Fileset at block=%u, partition=%u\n", | ||
793 | fileset->logicalBlockNum, fileset->partitionReferenceNum); | ||
794 | |||
795 | UDF_SB(sb)->s_partition = fileset->partitionReferenceNum; | ||
796 | ret = udf_load_fileset(sb, (struct fileSetDesc *)bh->b_data, root); | ||
797 | brelse(bh); | ||
798 | return ret; | ||
800 | } | 799 | } |
801 | 800 | ||
802 | /* | 801 | /* |
@@ -952,19 +951,53 @@ static int udf_load_metadata_files(struct super_block *sb, int partition, | |||
952 | return 0; | 951 | return 0; |
953 | } | 952 | } |
954 | 953 | ||
955 | static void udf_load_fileset(struct super_block *sb, struct buffer_head *bh, | 954 | static int udf_verify_domain_identifier(struct super_block *sb, |
956 | struct kernel_lb_addr *root) | 955 | struct regid *ident, char *dname) |
957 | { | 956 | { |
958 | struct fileSetDesc *fset; | 957 | struct domainEntityIDSuffix *suffix; |
959 | 958 | ||
960 | fset = (struct fileSetDesc *)bh->b_data; | 959 | if (memcmp(ident->ident, UDF_ID_COMPLIANT, strlen(UDF_ID_COMPLIANT))) { |
960 | udf_warn(sb, "Not OSTA UDF compliant %s descriptor.\n", dname); | ||
961 | goto force_ro; | ||
962 | } | ||
963 | if (ident->flags & (1 << ENTITYID_FLAGS_DIRTY)) { | ||
964 | udf_warn(sb, "Possibly not OSTA UDF compliant %s descriptor.\n", | ||
965 | dname); | ||
966 | goto force_ro; | ||
967 | } | ||
968 | suffix = (struct domainEntityIDSuffix *)ident->identSuffix; | ||
969 | if (suffix->flags & (1 << ENTITYIDSUFFIX_FLAGS_HARDWRITEPROTECT) || | ||
970 | suffix->flags & (1 << ENTITYIDSUFFIX_FLAGS_SOFTWRITEPROTECT)) { | ||
971 | if (!sb_rdonly(sb)) { | ||
972 | udf_warn(sb, "Descriptor for %s marked write protected." | ||
973 | " Forcing read only mount.\n", dname); | ||
974 | } | ||
975 | goto force_ro; | ||
976 | } | ||
977 | return 0; | ||
961 | 978 | ||
962 | *root = lelb_to_cpu(fset->rootDirectoryICB.extLocation); | 979 | force_ro: |
980 | if (!sb_rdonly(sb)) | ||
981 | return -EACCES; | ||
982 | UDF_SET_FLAG(sb, UDF_FLAG_RW_INCOMPAT); | ||
983 | return 0; | ||
984 | } | ||
963 | 985 | ||
986 | static int udf_load_fileset(struct super_block *sb, struct fileSetDesc *fset, | ||
987 | struct kernel_lb_addr *root) | ||
988 | { | ||
989 | int ret; | ||
990 | |||
991 | ret = udf_verify_domain_identifier(sb, &fset->domainIdent, "file set"); | ||
992 | if (ret < 0) | ||
993 | return ret; | ||
994 | |||
995 | *root = lelb_to_cpu(fset->rootDirectoryICB.extLocation); | ||
964 | UDF_SB(sb)->s_serial_number = le16_to_cpu(fset->descTag.tagSerialNum); | 996 | UDF_SB(sb)->s_serial_number = le16_to_cpu(fset->descTag.tagSerialNum); |
965 | 997 | ||
966 | udf_debug("Rootdir at block=%u, partition=%u\n", | 998 | udf_debug("Rootdir at block=%u, partition=%u\n", |
967 | root->logicalBlockNum, root->partitionReferenceNum); | 999 | root->logicalBlockNum, root->partitionReferenceNum); |
1000 | return 0; | ||
968 | } | 1001 | } |
969 | 1002 | ||
970 | int udf_compute_nr_groups(struct super_block *sb, u32 partition) | 1003 | int udf_compute_nr_groups(struct super_block *sb, u32 partition) |
@@ -1375,6 +1408,10 @@ static int udf_load_logicalvol(struct super_block *sb, sector_t block, | |||
1375 | goto out_bh; | 1408 | goto out_bh; |
1376 | } | 1409 | } |
1377 | 1410 | ||
1411 | ret = udf_verify_domain_identifier(sb, &lvd->domainIdent, | ||
1412 | "logical volume"); | ||
1413 | if (ret) | ||
1414 | goto out_bh; | ||
1378 | ret = udf_sb_alloc_partition_maps(sb, le32_to_cpu(lvd->numPartitionMaps)); | 1415 | ret = udf_sb_alloc_partition_maps(sb, le32_to_cpu(lvd->numPartitionMaps)); |
1379 | if (ret) | 1416 | if (ret) |
1380 | goto out_bh; | 1417 | goto out_bh; |
@@ -2227,9 +2264,9 @@ static int udf_fill_super(struct super_block *sb, void *options, int silent) | |||
2227 | UDF_SET_FLAG(sb, UDF_FLAG_RW_INCOMPAT); | 2264 | UDF_SET_FLAG(sb, UDF_FLAG_RW_INCOMPAT); |
2228 | } | 2265 | } |
2229 | 2266 | ||
2230 | if (udf_find_fileset(sb, &fileset, &rootdir)) { | 2267 | ret = udf_find_fileset(sb, &fileset, &rootdir); |
2268 | if (ret < 0) { | ||
2231 | udf_warn(sb, "No fileset found\n"); | 2269 | udf_warn(sb, "No fileset found\n"); |
2232 | ret = -EINVAL; | ||
2233 | goto error_out; | 2270 | goto error_out; |
2234 | } | 2271 | } |
2235 | 2272 | ||