fs/adfs: dir: add generic directory reading
authorRussell King <rmk+kernel@armlinux.org.uk>
Mon, 9 Dec 2019 11:09:35 +0000 (11:09 +0000)
committerAl Viro <viro@zeniv.linux.org.uk>
Tue, 21 Jan 2020 01:12:41 +0000 (20:12 -0500)
Both directory formats code the mechanics of fetching the directory
buffers using their own implementations.  Consolidate these into one
implementation.

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

index 92cbc4b1d902d378d6c4429c91c92f5461d007cf..01d065937c015e1e779eeca6602b80a23c9ffa96 100644 (file)
@@ -170,6 +170,8 @@ int adfs_dir_copyfrom(void *dst, struct adfs_dir *dir, unsigned int offset,
 int adfs_dir_copyto(struct adfs_dir *dir, unsigned int offset, const void *src,
                    size_t len);
 void adfs_dir_relse(struct adfs_dir *dir);
+int adfs_dir_read_buffers(struct super_block *sb, u32 indaddr,
+                         unsigned int size, struct adfs_dir *dir);
 void adfs_object_fixup(struct adfs_dir *dir, struct object_info *obj);
 extern int adfs_dir_update(struct super_block *sb, struct object_info *obj,
                           int wait);
index 3c303074aa5ec1bc51781ea4f675e3e1c885ac98..b8e2a909fa3fd89e6f16c72012055453a98a49f3 100644 (file)
@@ -78,6 +78,55 @@ void adfs_dir_relse(struct adfs_dir *dir)
        dir->sb = NULL;
 }
 
+int adfs_dir_read_buffers(struct super_block *sb, u32 indaddr,
+                         unsigned int size, struct adfs_dir *dir)
+{
+       struct buffer_head **bhs;
+       unsigned int i, num;
+       int block;
+
+       num = ALIGN(size, sb->s_blocksize) >> sb->s_blocksize_bits;
+       if (num > ARRAY_SIZE(dir->bh)) {
+               /* We only allow one extension */
+               if (dir->bhs != dir->bh)
+                       return -EINVAL;
+
+               bhs = kcalloc(num, sizeof(*bhs), GFP_KERNEL);
+               if (!bhs)
+                       return -ENOMEM;
+
+               if (dir->nr_buffers)
+                       memcpy(bhs, dir->bhs, dir->nr_buffers * sizeof(*bhs));
+
+               dir->bhs = bhs;
+       }
+
+       for (i = dir->nr_buffers; i < num; i++) {
+               block = __adfs_block_map(sb, indaddr, i);
+               if (!block) {
+                       adfs_error(sb, "dir %06x has a hole at offset %u",
+                                  indaddr, i);
+                       goto error;
+               }
+
+               dir->bhs[i] = sb_bread(sb, block);
+               if (!dir->bhs[i]) {
+                       adfs_error(sb,
+                                  "dir %06x failed read at offset %u, mapped block 0x%08x",
+                                  indaddr, i, block);
+                       goto error;
+               }
+
+               dir->nr_buffers++;
+       }
+       return 0;
+
+error:
+       adfs_dir_relse(dir);
+
+       return -EIO;
+}
+
 static int adfs_dir_read(struct super_block *sb, u32 indaddr,
                         unsigned int size, struct adfs_dir *dir)
 {
index 3c3b423577d2dda4ce0ecc633d940fbac6bda53b..027ee714f42b2962cfcb72f118dc42d33b9f235b 100644 (file)
@@ -126,7 +126,7 @@ static int adfs_dir_read(struct super_block *sb, u32 indaddr,
                         unsigned int size, struct adfs_dir *dir)
 {
        const unsigned int blocksize_bits = sb->s_blocksize_bits;
-       int blk;
+       int ret;
 
        /*
         * Directories which are not a multiple of 2048 bytes
@@ -135,24 +135,9 @@ static int adfs_dir_read(struct super_block *sb, u32 indaddr,
        if (size & 2047)
                goto bad_dir;
 
-       size >>= blocksize_bits;
-
-       for (blk = 0; blk < size; blk++) {
-               int phys;
-
-               phys = __adfs_block_map(sb, indaddr, blk);
-               if (!phys) {
-                       adfs_error(sb, "dir %06x has a hole at offset %d",
-                                  indaddr, blk);
-                       goto release_buffers;
-               }
-
-               dir->bh[blk] = sb_bread(sb, phys);
-               if (!dir->bh[blk])
-                       goto release_buffers;
-
-               dir->nr_buffers += 1;
-       }
+       ret = adfs_dir_read_buffers(sb, indaddr, size, dir);
+       if (ret)
+               return ret;
 
        memcpy(&dir->dirhead, bufoff(dir->bh, 0), sizeof(dir->dirhead));
        memcpy(&dir->dirtail, bufoff(dir->bh, 2007), sizeof(dir->dirtail));
@@ -172,7 +157,6 @@ static int adfs_dir_read(struct super_block *sb, u32 indaddr,
 
 bad_dir:
        adfs_error(sb, "dir %06x is corrupted", indaddr);
-release_buffers:
        adfs_dir_relse(dir);
 
        return -EIO;
index 6a07c0dfcc93cda2de52d4078f13ec97052e8a12..ae11236515d03bc276c6f3110d5a666c791ae53f 100644 (file)
@@ -4,87 +4,49 @@
  *
  *  Copyright (C) 1997-1999 Russell King
  */
-#include <linux/slab.h>
 #include "adfs.h"
 #include "dir_fplus.h"
 
-static int
-adfs_fplus_read(struct super_block *sb, unsigned int id, unsigned int sz, struct adfs_dir *dir)
+static int adfs_fplus_read(struct super_block *sb, u32 indaddr,
+                          unsigned int size, struct adfs_dir *dir)
 {
        struct adfs_bigdirheader *h;
        struct adfs_bigdirtail *t;
-       unsigned long block;
-       unsigned int blk, size;
-       int ret = -EIO;
-
-       block = __adfs_block_map(sb, id, 0);
-       if (!block) {
-               adfs_error(sb, "dir object %X has a hole at offset 0", id);
-               goto out;
-       }
+       unsigned int dirsize;
+       int ret;
 
-       dir->bhs[0] = sb_bread(sb, block);
-       if (!dir->bhs[0])
-               goto out;
-       dir->nr_buffers += 1;
+       /* Read first buffer */
+       ret = adfs_dir_read_buffers(sb, indaddr, sb->s_blocksize, dir);
+       if (ret)
+               return ret;
 
        h = (struct adfs_bigdirheader *)dir->bhs[0]->b_data;
-       size = le32_to_cpu(h->bigdirsize);
-       if (size != sz) {
+       dirsize = le32_to_cpu(h->bigdirsize);
+       if (dirsize != size) {
                adfs_msg(sb, KERN_WARNING,
-                        "directory header size %X does not match directory size %X",
-                        size, sz);
+                        "dir %06x header size %X does not match directory size %X",
+                        indaddr, dirsize, size);
        }
 
        if (h->bigdirversion[0] != 0 || h->bigdirversion[1] != 0 ||
            h->bigdirversion[2] != 0 || size & 2047 ||
            h->bigdirstartname != cpu_to_le32(BIGDIRSTARTNAME)) {
-               adfs_error(sb, "dir %06x has malformed header", id);
+               adfs_error(sb, "dir %06x has malformed header", indaddr);
                goto out;
        }
 
-       size >>= sb->s_blocksize_bits;
-       if (size > ARRAY_SIZE(dir->bh)) {
-               /* this directory is too big for fixed bh set, must allocate */
-               struct buffer_head **bhs =
-                       kcalloc(size, sizeof(struct buffer_head *),
-                               GFP_KERNEL);
-               if (!bhs) {
-                       adfs_msg(sb, KERN_ERR,
-                                "not enough memory for dir object %X (%d blocks)",
-                                id, size);
-                       ret = -ENOMEM;
-                       goto out;
-               }
-               dir->bhs = bhs;
-               /* copy over the pointer to the block that we've already read */
-               dir->bhs[0] = dir->bh[0];
-       }
-
-       for (blk = 1; blk < size; blk++) {
-               block = __adfs_block_map(sb, id, blk);
-               if (!block) {
-                       adfs_error(sb, "dir object %X has a hole at offset %d", id, blk);
-                       goto out;
-               }
-
-               dir->bhs[blk] = sb_bread(sb, block);
-               if (!dir->bhs[blk]) {
-                       adfs_error(sb,  "dir object %x failed read for offset %d, mapped block %lX",
-                                  id, blk, block);
-                       goto out;
-               }
-
-               dir->nr_buffers += 1;
-       }
+       /* Read remaining buffers */
+       ret = adfs_dir_read_buffers(sb, indaddr, dirsize, dir);
+       if (ret)
+               return ret;
 
        t = (struct adfs_bigdirtail *)
-               (dir->bhs[size - 1]->b_data + (sb->s_blocksize - 8));
+               (dir->bhs[dir->nr_buffers - 1]->b_data + (sb->s_blocksize - 8));
 
        if (t->bigdirendname != cpu_to_le32(BIGDIRENDNAME) ||
            t->bigdirendmasseq != h->startmasseq ||
            t->reserved[0] != 0 || t->reserved[1] != 0) {
-               adfs_error(sb, "dir %06x has malformed tail", id);
+               adfs_error(sb, "dir %06x has malformed tail", indaddr);
                goto out;
        }