From 1dfdfc94730bde781c87b25fd606f6dfaffe9097 Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 4 Jun 2019 14:49:30 +0100 Subject: fs/adfs: add helper to get discrecord from map Add a helper to get the disc record from the map, rather than open coding this in adfs_fill_super(). Signed-off-by: Russell King Signed-off-by: Al Viro --- fs/adfs/super.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'fs/adfs/super.c') diff --git a/fs/adfs/super.c b/fs/adfs/super.c index 2a83655c408f..533c9601a670 100644 --- a/fs/adfs/super.c +++ b/fs/adfs/super.c @@ -9,7 +9,6 @@ */ #include #include -#include #include #include #include @@ -463,7 +462,7 @@ static int adfs_fill_super(struct super_block *sb, void *data, int silent) */ sb->s_op = &adfs_sops; - dr = (struct adfs_discrecord *)(asb->s_map[0].dm_bh->b_data + 4); + dr = adfs_map_discrecord(asb->s_map); root_obj.parent_id = root_obj.file_id = le32_to_cpu(dr->root); root_obj.name_len = 0; -- cgit From 275f5b99d6d4e6fccb7cea6783460939856c1306 Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 4 Jun 2019 14:49:36 +0100 Subject: fs/adfs: add helper to get filesystem size Add a helper to get the filesystem size from the disc record and eliminate the "s_size" member of the adfs superblock structure. Signed-off-by: Russell King Signed-off-by: Al Viro --- fs/adfs/super.c | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) (limited to 'fs/adfs/super.c') diff --git a/fs/adfs/super.c b/fs/adfs/super.c index 533c9601a670..7f6d00467baa 100644 --- a/fs/adfs/super.c +++ b/fs/adfs/super.c @@ -220,12 +220,13 @@ static int adfs_statfs(struct dentry *dentry, struct kstatfs *buf) { struct super_block *sb = dentry->d_sb; struct adfs_sb_info *sbi = ADFS_SB(sb); + struct adfs_discrecord *dr = adfs_map_discrecord(sbi->s_map); u64 id = huge_encode_dev(sb->s_bdev->bd_dev); buf->f_type = ADFS_SUPER_MAGIC; buf->f_namelen = sbi->s_namelen; buf->f_bsize = sb->s_blocksize; - buf->f_blocks = sbi->s_size; + buf->f_blocks = adfs_disc_size(dr) >> sb->s_blocksize_bits; buf->f_files = sbi->s_ids_per_zone * sbi->s_map_size; buf->f_bavail = buf->f_bfree = adfs_map_free(sb); @@ -329,8 +330,7 @@ static struct adfs_discmap *adfs_read_map(struct super_block *sb, struct adfs_di i = zone - 1; dm[0].dm_startblk = 0; dm[0].dm_startbit = ADFS_DR_SIZE_BITS; - dm[i].dm_endbit = (le32_to_cpu(dr->disc_size_high) << (32 - dr->log2bpmb)) + - (le32_to_cpu(dr->disc_size) >> dr->log2bpmb) + + dm[i].dm_endbit = (adfs_disc_size(dr) >> dr->log2bpmb) + (ADFS_DR_SIZE_BITS - i * zone_size); if (adfs_checkmap(sb, dm)) @@ -346,16 +346,6 @@ error_free: return ERR_PTR(-EIO); } -static inline unsigned long adfs_discsize(struct adfs_discrecord *dr, int block_bits) -{ - unsigned long discsize; - - discsize = le32_to_cpu(dr->disc_size_high) << (32 - block_bits); - discsize |= le32_to_cpu(dr->disc_size) >> block_bits; - - return discsize; -} - static int adfs_fill_super(struct super_block *sb, void *data, int silent) { struct adfs_discrecord *dr; @@ -445,7 +435,6 @@ static int adfs_fill_super(struct super_block *sb, void *data, int silent) asb->s_idlen = dr->idlen; asb->s_map_size = dr->nzones | (dr->nzones_high << 8); asb->s_map2blk = dr->log2bpmb - dr->log2secsize; - asb->s_size = adfs_discsize(dr, sb->s_blocksize_bits); asb->s_version = dr->format_version; asb->s_log2sharesize = dr->log2sharesize; -- cgit From cb88b5a387dd9ba9c36fd76c4cdc187cdce5974c Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 4 Jun 2019 14:49:41 +0100 Subject: fs/adfs: use format_version from disc_record We only use the format version in one place during filesystem mount, so it is pointless storing it in the superblock structure. Also, we should be using the version from the disc record in the map rather than the boot block. Signed-off-by: Russell King Signed-off-by: Al Viro --- fs/adfs/super.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'fs/adfs/super.c') diff --git a/fs/adfs/super.c b/fs/adfs/super.c index 7f6d00467baa..c5607685788e 100644 --- a/fs/adfs/super.c +++ b/fs/adfs/super.c @@ -435,7 +435,6 @@ static int adfs_fill_super(struct super_block *sb, void *data, int silent) asb->s_idlen = dr->idlen; asb->s_map_size = dr->nzones | (dr->nzones_high << 8); asb->s_map2blk = dr->log2bpmb - dr->log2secsize; - asb->s_version = dr->format_version; asb->s_log2sharesize = dr->log2sharesize; asb->s_map = adfs_read_map(sb, dr); @@ -467,7 +466,7 @@ static int adfs_fill_super(struct super_block *sb, void *data, int silent) * If this is a F+ disk with variable length directories, * get the root_size from the disc record. */ - if (asb->s_version) { + if (dr->format_version) { root_obj.size = le32_to_cpu(dr->root_size); asb->s_dir = &adfs_fplus_dir_ops; asb->s_namelen = ADFS_FPLUS_NAME_LEN; -- cgit From 2e67080d87087fdba88059b1f63e4301ea0fad3a Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 4 Jun 2019 14:49:47 +0100 Subject: fs/adfs: use %pV for error messages Rather than using vsnprintf() with a temporary buffer on the stack, use %pV to print error messages. Signed-off-by: Russell King Signed-off-by: Al Viro --- fs/adfs/super.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'fs/adfs/super.c') diff --git a/fs/adfs/super.c b/fs/adfs/super.c index c5607685788e..315657a3bac7 100644 --- a/fs/adfs/super.c +++ b/fs/adfs/super.c @@ -24,16 +24,18 @@ void __adfs_error(struct super_block *sb, const char *function, const char *fmt, ...) { - char error_buf[128]; + struct va_format vaf; va_list args; va_start(args, fmt); - vsnprintf(error_buf, sizeof(error_buf), fmt, args); - va_end(args); + vaf.fmt = fmt; + vaf.va = &args; - printk(KERN_CRIT "ADFS-fs error (device %s)%s%s: %s\n", + printk(KERN_CRIT "ADFS-fs error (device %s)%s%s: %pV\n", sb->s_id, function ? ": " : "", - function ? function : "", error_buf); + function ? function : "", &vaf); + + va_end(args); } static int adfs_checkdiscrecord(struct adfs_discrecord *dr) -- cgit From ceb3b10613eba86ddf043345338e32673a27f87a Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 4 Jun 2019 14:49:52 +0100 Subject: fs/adfs: clean up error message printing Overhaul our message printing: - provide a consistent way to print messages: - filesystem corruption should be reported via adfs_error() - everything else should use adfs_msg() - clean up the error message printing when mounting a filesystem - fix the messages printed by the big directory format code to only use adfs_error() when there is filesystem corruption, otherwise use adfs_msg(). Signed-off-by: Russell King Signed-off-by: Al Viro --- fs/adfs/super.c | 45 +++++++++++++++++++++++++++++---------------- 1 file changed, 29 insertions(+), 16 deletions(-) (limited to 'fs/adfs/super.c') diff --git a/fs/adfs/super.c b/fs/adfs/super.c index 315657a3bac7..6910a9afa9fd 100644 --- a/fs/adfs/super.c +++ b/fs/adfs/super.c @@ -38,6 +38,18 @@ void __adfs_error(struct super_block *sb, const char *function, const char *fmt, va_end(args); } +void adfs_msg(struct super_block *sb, const char *pfx, const char *fmt, ...) +{ + struct va_format vaf; + va_list args; + + va_start(args, fmt); + vaf.fmt = fmt; + vaf.va = &args; + printk("%sADFS-fs (%s): %pV\n", pfx, sb->s_id, &vaf); + va_end(args); +} + static int adfs_checkdiscrecord(struct adfs_discrecord *dr) { int i; @@ -203,8 +215,9 @@ static int parse_options(struct super_block *sb, char *options) asb->s_ftsuffix = option; break; default: - printk("ADFS-fs: unrecognised mount option \"%s\" " - "or missing value\n", p); + adfs_msg(sb, KERN_ERR, + "unrecognised mount option \"%s\" or missing value", + p); return -EINVAL; } } @@ -377,7 +390,7 @@ static int adfs_fill_super(struct super_block *sb, void *data, int silent) sb_set_blocksize(sb, BLOCK_SIZE); if (!(bh = sb_bread(sb, ADFS_DISCRECORD / BLOCK_SIZE))) { - adfs_error(sb, "unable to read superblock"); + adfs_msg(sb, KERN_ERR, "error: unable to read superblock"); ret = -EIO; goto error; } @@ -385,11 +398,8 @@ static int adfs_fill_super(struct super_block *sb, void *data, int silent) b_data = bh->b_data + (ADFS_DISCRECORD % BLOCK_SIZE); if (adfs_checkbblk(b_data)) { - if (!silent) - printk("VFS: Can't find an adfs filesystem on dev " - "%s.\n", sb->s_id); ret = -EINVAL; - goto error_free_bh; + goto error_badfs; } dr = (struct adfs_discrecord *)(b_data + ADFS_DR_OFFSET); @@ -398,33 +408,31 @@ static int adfs_fill_super(struct super_block *sb, void *data, int silent) * Do some sanity checks on the ADFS disc record */ if (adfs_checkdiscrecord(dr)) { - if (!silent) - printk("VPS: Can't find an adfs filesystem on dev " - "%s.\n", sb->s_id); ret = -EINVAL; - goto error_free_bh; + goto error_badfs; } brelse(bh); if (sb_set_blocksize(sb, 1 << dr->log2secsize)) { bh = sb_bread(sb, ADFS_DISCRECORD / sb->s_blocksize); if (!bh) { - adfs_error(sb, "couldn't read superblock on " - "2nd try."); + adfs_msg(sb, KERN_ERR, + "error: couldn't read superblock on 2nd try."); ret = -EIO; goto error; } b_data = bh->b_data + (ADFS_DISCRECORD % sb->s_blocksize); if (adfs_checkbblk(b_data)) { - adfs_error(sb, "disc record mismatch, very weird!"); + adfs_msg(sb, KERN_ERR, + "error: disc record mismatch, very weird!"); ret = -EINVAL; goto error_free_bh; } dr = (struct adfs_discrecord *)(b_data + ADFS_DR_OFFSET); } else { if (!silent) - printk(KERN_ERR "VFS: Unsupported blocksize on dev " - "%s.\n", sb->s_id); + adfs_msg(sb, KERN_ERR, + "error: unsupported blocksize"); ret = -EINVAL; goto error; } @@ -497,6 +505,11 @@ static int adfs_fill_super(struct super_block *sb, void *data, int silent) } return 0; +error_badfs: + if (!silent) + adfs_msg(sb, KERN_ERR, + "error: can't find an ADFS filesystem on dev %s.", + sb->s_id); error_free_bh: brelse(bh); error: -- cgit From 5ed70bb47767d1f57a5e85e585a327917ded0373 Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 4 Jun 2019 14:49:57 +0100 Subject: fs/adfs: clean up indirect disc addresses and fragment IDs We use a variety of different names for the indirect disc address of the current object, use a variety of different types, and print it in a variety of different ways. Bring some consistency to this by naming it "indaddr", use u32 or __u32 as the type since it fits in 32-bits, and always print it with %06x (with no leading hex prefix.) When printing it was a directory identifer, use "dir %06x" otherwise use "object %06x". Do the same for fragment IDs and the parent indirect disc addresses. Signed-off-by: Russell King Signed-off-by: Al Viro --- fs/adfs/super.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs/adfs/super.c') diff --git a/fs/adfs/super.c b/fs/adfs/super.c index 6910a9afa9fd..4e913124fc2d 100644 --- a/fs/adfs/super.c +++ b/fs/adfs/super.c @@ -462,7 +462,7 @@ static int adfs_fill_super(struct super_block *sb, void *data, int silent) dr = adfs_map_discrecord(asb->s_map); - root_obj.parent_id = root_obj.file_id = le32_to_cpu(dr->root); + root_obj.parent_id = root_obj.indaddr = le32_to_cpu(dr->root); root_obj.name_len = 0; /* Set root object date as 01 Jan 1987 00:00:00 */ root_obj.loadaddr = 0xfff0003f; -- cgit From 421d3c0faa28890dbfb7d2a67f067f07c1123556 Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 4 Jun 2019 14:50:03 +0100 Subject: fs/adfs: super: correct superblock flags We don't support atime updates of any kind, and we ought to set the read-only bit if we are compiled without write support. Signed-off-by: Russell King Signed-off-by: Al Viro --- fs/adfs/super.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'fs/adfs/super.c') diff --git a/fs/adfs/super.c b/fs/adfs/super.c index 4e913124fc2d..b393905abe13 100644 --- a/fs/adfs/super.c +++ b/fs/adfs/super.c @@ -19,6 +19,8 @@ #include "dir_f.h" #include "dir_fplus.h" +#define ADFS_SB_FLAGS SB_NOATIME + #define ADFS_DEFAULT_OWNER_MASK S_IRWXU #define ADFS_DEFAULT_OTHER_MASK (S_IRWXG | S_IRWXO) @@ -227,7 +229,7 @@ static int parse_options(struct super_block *sb, char *options) static int adfs_remount(struct super_block *sb, int *flags, char *data) { sync_filesystem(sb); - *flags |= SB_NODIRATIME; + *flags |= ADFS_SB_FLAGS; return parse_options(sb, data); } @@ -371,7 +373,7 @@ static int adfs_fill_super(struct super_block *sb, void *data, int silent) struct inode *root; int ret = -EINVAL; - sb->s_flags |= SB_NODIRATIME; + sb->s_flags |= ADFS_SB_FLAGS; asb = kzalloc(sizeof(*asb), GFP_KERNEL); if (!asb) -- cgit From 4c5762f5f5e31d493678b0ee6f73585355dd3638 Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 4 Jun 2019 14:50:09 +0100 Subject: fs/adfs: super: safely update options on remount Only update the options on remount if we successfully parse all options, rather than updating those we've managed to parse. Signed-off-by: Russell King Signed-off-by: Al Viro --- fs/adfs/super.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) (limited to 'fs/adfs/super.c') diff --git a/fs/adfs/super.c b/fs/adfs/super.c index b393905abe13..2f81c1c29757 100644 --- a/fs/adfs/super.c +++ b/fs/adfs/super.c @@ -170,10 +170,10 @@ static const match_table_t tokens = { {Opt_err, NULL} }; -static int parse_options(struct super_block *sb, char *options) +static int parse_options(struct super_block *sb, struct adfs_sb_info *asb, + char *options) { char *p; - struct adfs_sb_info *asb = ADFS_SB(sb); int option; if (!options) @@ -228,9 +228,18 @@ static int parse_options(struct super_block *sb, char *options) static int adfs_remount(struct super_block *sb, int *flags, char *data) { + struct adfs_sb_info temp_asb; + int ret; + sync_filesystem(sb); *flags |= ADFS_SB_FLAGS; - return parse_options(sb, data); + + temp_asb = *ADFS_SB(sb); + ret = parse_options(sb, &temp_asb, data); + if (ret == 0) + *ADFS_SB(sb) = temp_asb; + + return ret; } static int adfs_statfs(struct dentry *dentry, struct kstatfs *buf) @@ -387,7 +396,7 @@ static int adfs_fill_super(struct super_block *sb, void *data, int silent) asb->s_other_mask = ADFS_DEFAULT_OTHER_MASK; asb->s_ftsuffix = 0; - if (parse_options(sb, data)) + if (parse_options(sb, asb, data)) goto error; sb_set_blocksize(sb, BLOCK_SIZE); -- cgit From 5808b14a1f52554de612fee85ef517199855e310 Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 4 Jun 2019 14:50:14 +0100 Subject: fs/adfs: super: fix use-after-free bug Fix a use-after-free bug during filesystem initialisation, where we access the disc record (which is stored in a buffer) after we have released the buffer. Signed-off-by: Russell King Signed-off-by: Al Viro --- fs/adfs/super.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'fs/adfs/super.c') diff --git a/fs/adfs/super.c b/fs/adfs/super.c index 2f81c1c29757..b1243433add7 100644 --- a/fs/adfs/super.c +++ b/fs/adfs/super.c @@ -378,6 +378,7 @@ static int adfs_fill_super(struct super_block *sb, void *data, int silent) struct buffer_head *bh; struct object_info root_obj; unsigned char *b_data; + unsigned int blocksize; struct adfs_sb_info *asb; struct inode *root; int ret = -EINVAL; @@ -423,8 +424,10 @@ static int adfs_fill_super(struct super_block *sb, void *data, int silent) goto error_badfs; } + blocksize = 1 << dr->log2secsize; brelse(bh); - if (sb_set_blocksize(sb, 1 << dr->log2secsize)) { + + if (sb_set_blocksize(sb, blocksize)) { bh = sb_bread(sb, ADFS_DISCRECORD / sb->s_blocksize); if (!bh) { adfs_msg(sb, KERN_ERR, -- cgit From 8616108de152447f99570dd45b6e3b8c71615fe5 Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 4 Jun 2019 14:50:19 +0100 Subject: fs/adfs: super: limit idlen according to directory type Limit idlen according to the directory type, as idlen (the size of a fragment ID) can not be more than 16 with the "new directory" layout. Signed-off-by: Russell King Signed-off-by: Al Viro --- fs/adfs/super.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'fs/adfs/super.c') diff --git a/fs/adfs/super.c b/fs/adfs/super.c index b1243433add7..d029ae10f8a0 100644 --- a/fs/adfs/super.c +++ b/fs/adfs/super.c @@ -54,6 +54,7 @@ void adfs_msg(struct super_block *sb, const char *pfx, const char *fmt, ...) static int adfs_checkdiscrecord(struct adfs_discrecord *dr) { + unsigned int max_idlen; int i; /* sector size must be 256, 512 or 1024 bytes */ @@ -73,8 +74,13 @@ static int adfs_checkdiscrecord(struct adfs_discrecord *dr) if (le32_to_cpu(dr->disc_size_high) >> dr->log2secsize) return 1; - /* idlen must be no greater than 19 v2 [1.0] */ - if (dr->idlen > 19) + /* + * Maximum idlen is limited to 16 bits for new directories by + * the three-byte storage of an indirect disc address. For + * big directories, idlen must be no greater than 19 v2 [1.0] + */ + max_idlen = dr->format_version ? 19 : 16; + if (dr->idlen > max_idlen) return 1; /* reserved bytes should be zero */ -- cgit From b4ed8f75c82876342b3399942427392ba5f3bbb5 Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 4 Jun 2019 14:50:24 +0100 Subject: fs/adfs: add time stamp and file type helpers Add some helpers to check whether the inode has a time stamp and file type, and to parse the file type from the load address. Signed-off-by: Russell King Signed-off-by: Al Viro --- fs/adfs/super.c | 1 - 1 file changed, 1 deletion(-) (limited to 'fs/adfs/super.c') diff --git a/fs/adfs/super.c b/fs/adfs/super.c index d029ae10f8a0..89b159c02b51 100644 --- a/fs/adfs/super.c +++ b/fs/adfs/super.c @@ -490,7 +490,6 @@ static int adfs_fill_super(struct super_block *sb, void *data, int silent) root_obj.size = ADFS_NEWDIR_SIZE; root_obj.attr = ADFS_NDA_DIRECTORY | ADFS_NDA_OWNER_READ | ADFS_NDA_OWNER_WRITE | ADFS_NDA_PUBLIC_READ; - root_obj.filetype = -1; /* * If this is a F+ disk with variable length directories, -- cgit