]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
fs/adfs: super: extract filesystem block probe
authorRussell King <rmk+kernel@armlinux.org.uk>
Mon, 9 Dec 2019 11:11:28 +0000 (11:11 +0000)
committerAl Viro <viro@zeniv.linux.org.uk>
Tue, 21 Jan 2020 01:12:42 +0000 (20:12 -0500)
Separate the filesystem block probing from the superblock filling so
we can support other ADFS filesystem formats, such as the single-zone
E and E+ floppy image formats which do not have a boot block.

Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
fs/adfs/super.c

index 9c93122925cf434c2647282ee7898b7ab788e8b1..4c06b2d5a861f21d49be3210f7292bbd69271803 100644 (file)
@@ -277,13 +277,80 @@ static const struct super_operations adfs_sops = {
        .show_options   = adfs_show_options,
 };
 
-static int adfs_fill_super(struct super_block *sb, void *data, int silent)
+static int adfs_probe(struct super_block *sb, unsigned int offset, int silent,
+                     int (*validate)(struct super_block *sb,
+                                     struct buffer_head *bh,
+                                     struct adfs_discrecord **bhp))
 {
+       struct adfs_sb_info *asb = ADFS_SB(sb);
        struct adfs_discrecord *dr;
        struct buffer_head *bh;
-       struct object_info root_obj;
+       unsigned int blocksize = BLOCK_SIZE;
+       int ret, try;
+
+       for (try = 0; try < 2; try++) {
+               /* try to set the requested block size */
+               if (sb->s_blocksize != blocksize &&
+                   !sb_set_blocksize(sb, blocksize)) {
+                       if (!silent)
+                               adfs_msg(sb, KERN_ERR,
+                                        "error: unsupported blocksize");
+                       return -EINVAL;
+               }
+
+               /* read the buffer */
+               bh = sb_bread(sb, offset >> sb->s_blocksize_bits);
+               if (!bh) {
+                       adfs_msg(sb, KERN_ERR,
+                                "error: unable to read block %u, try %d",
+                                offset >> sb->s_blocksize_bits, try);
+                       return -EIO;
+               }
+
+               /* validate it */
+               ret = validate(sb, bh, &dr);
+               if (ret) {
+                       brelse(bh);
+                       return ret;
+               }
+
+               /* does the block size match the filesystem block size? */
+               blocksize = 1 << dr->log2secsize;
+               if (sb->s_blocksize == blocksize) {
+                       asb->s_map = adfs_read_map(sb, dr);
+                       brelse(bh);
+                       return PTR_ERR_OR_ZERO(asb->s_map);
+               }
+
+               brelse(bh);
+       }
+
+       return -EIO;
+}
+
+static int adfs_validate_bblk(struct super_block *sb, struct buffer_head *bh,
+                             struct adfs_discrecord **drp)
+{
+       struct adfs_discrecord *dr;
        unsigned char *b_data;
-       unsigned int blocksize;
+
+       b_data = bh->b_data + (ADFS_DISCRECORD % sb->s_blocksize);
+       if (adfs_checkbblk(b_data))
+               return -EILSEQ;
+
+       /* Do some sanity checks on the ADFS disc record */
+       dr = (struct adfs_discrecord *)(b_data + ADFS_DR_OFFSET);
+       if (adfs_checkdiscrecord(dr))
+               return -EILSEQ;
+
+       *drp = dr;
+       return 0;
+}
+
+static int adfs_fill_super(struct super_block *sb, void *data, int silent)
+{
+       struct adfs_discrecord *dr;
+       struct object_info root_obj;
        struct adfs_sb_info *asb;
        struct inode *root;
        int ret = -EINVAL;
@@ -308,72 +375,19 @@ static int adfs_fill_super(struct super_block *sb, void *data, int silent)
        if (parse_options(sb, asb, data))
                goto error;
 
-       sb_set_blocksize(sb, BLOCK_SIZE);
-       if (!(bh = sb_bread(sb, ADFS_DISCRECORD / BLOCK_SIZE))) {
-               adfs_msg(sb, KERN_ERR, "error: unable to read superblock");
-               ret = -EIO;
-               goto error;
-       }
-
-       b_data = bh->b_data + (ADFS_DISCRECORD % BLOCK_SIZE);
-
-       if (adfs_checkbblk(b_data)) {
-               ret = -EINVAL;
-               goto error_badfs;
-       }
-
-       dr = (struct adfs_discrecord *)(b_data + ADFS_DR_OFFSET);
-
-       /*
-        * Do some sanity checks on the ADFS disc record
-        */
-       if (adfs_checkdiscrecord(dr)) {
-               ret = -EINVAL;
-               goto error_badfs;
-       }
-
-       blocksize = 1 << dr->log2secsize;
-       brelse(bh);
-
-       if (sb_set_blocksize(sb, blocksize)) {
-               bh = sb_bread(sb, ADFS_DISCRECORD / sb->s_blocksize);
-               if (!bh) {
-                       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_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 {
+       /* Try to probe the filesystem boot block */
+       ret = adfs_probe(sb, ADFS_DISCRECORD, silent, adfs_validate_bblk);
+       if (ret == -EILSEQ) {
                if (!silent)
                        adfs_msg(sb, KERN_ERR,
-                                "error: unsupported blocksize");
+                                "error: can't find an ADFS filesystem on dev %s.",
+                                sb->s_id);
                ret = -EINVAL;
-               goto error;
        }
+       if (ret)
+               goto error;
 
-       /*
-        * blocksize on this device should now be set to the ADFS log2secsize
-        */
-
-       asb->s_map = adfs_read_map(sb, dr);
-       if (IS_ERR(asb->s_map)) {
-               ret =  PTR_ERR(asb->s_map);
-               goto error_free_bh;
-       }
-
-       brelse(bh);
-
-       /*
-        * set up enough so that we can read an inode
-        */
+       /* set up enough so that we can read an inode */
        sb->s_op = &adfs_sops;
 
        dr = adfs_map_discrecord(asb->s_map);
@@ -417,13 +431,6 @@ 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:
        sb->s_fs_info = NULL;
        kfree(asb);