Commit 419a6e5e authored by Russell King's avatar Russell King Committed by Al Viro
Browse files

fs/adfs: dir: add generic directory reading



Both directory formats code the mechanics of fetching the directory
buffers using their own implementations.  Consolidate these into one
implementation.

Signed-off-by: default avatarRussell King <rmk+kernel@armlinux.org.uk>
Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent a317120b
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -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);
+49 −0
Original line number Diff line number Diff line
@@ -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)
{
+4 −20
Original line number Diff line number Diff line
@@ -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;
+19 −57
Original line number Diff line number Diff line
@@ -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;
	}