Commit 53070406 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull adfs updates from Al Viro:
 "adfs stuff for this cycle"

* 'work.adfs' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs: (42 commits)
  fs/adfs: bigdir: Fix an error code in adfs_fplus_read()
  Documentation: update adfs filesystem documentation
  fs/adfs: mostly divorse inode number from indirect disc address
  fs/adfs: super: add support for E and E+ floppy image formats
  fs/adfs: super: extract filesystem block probe
  fs/adfs: dir: remove debug in adfs_dir_update()
  fs/adfs: super: fix inode dropping
  fs/adfs: bigdir: implement directory update support
  fs/adfs: bigdir: calculate and validate directory checkbyte
  fs/adfs: bigdir: directory validation strengthening
  fs/adfs: bigdir: extract directory validation
  fs/adfs: bigdir: factor out directory entry offset calculation
  fs/adfs: newdir: split out directory commit from update
  fs/adfs: newdir: clean up adfs_f_update()
  fs/adfs: newdir: merge adfs_dir_read() into adfs_f_read()
  fs/adfs: newdir: improve directory validation
  fs/adfs: newdir: factor out directory format validation
  fs/adfs: dir: use pointers to access directory head/tails
  fs/adfs: dir: add more efficient iterate() per-format method
  fs/adfs: dir: switch to iterate_shared method
  ...
parents 6aee4bad 587065dc
Loading
Loading
Loading
Loading
+24 −0
Original line number Diff line number Diff line
Filesystems supported by ADFS
-----------------------------

The ADFS module supports the following Filecore formats which have:

- new maps
- new directories or big directories

In terms of the named formats, this means we support:

- E and E+, with or without boot block
- F and F+

We fully support reading files from these filesystems, and writing to
existing files within their existing allocation.  Essentially, we do
not support changing any of the filesystem metadata.

This is intended to support loopback mounted Linux native filesystems
on a RISC OS Filecore filesystem, but will allow the data within files
to be changed.

If write support (ADFS_FS_RW) is configured, we allow rudimentary
directory updates, specifically updating the access mode and timestamp.

Mount options for ADFS
----------------------

+22 −10
Original line number Diff line number Diff line
@@ -26,14 +26,13 @@ static inline u16 adfs_filetype(u32 loadaddr)
#define ADFS_NDA_PUBLIC_READ	(1 << 5)
#define ADFS_NDA_PUBLIC_WRITE	(1 << 6)

#include "dir_f.h"

/*
 * adfs file system inode data in memory
 */
struct adfs_inode_info {
	loff_t		mmu_private;
	__u32		parent_id;	/* parent indirect disc address	*/
	__u32		indaddr;	/* object indirect disc address	*/
	__u32		loadaddr;	/* RISC OS load address		*/
	__u32		execaddr;	/* RISC OS exec address		*/
	unsigned int	attr;		/* RISC OS permissions		*/
@@ -93,15 +92,19 @@ struct adfs_dir {

	int			nr_buffers;
	struct buffer_head	*bh[4];

	/* big directories need allocated buffers */
	struct buffer_head	**bh_fplus;
	struct buffer_head	**bhs;

	unsigned int		pos;
	__u32			parent_id;

	struct adfs_dirheader	dirhead;
	union  adfs_dirtail	dirtail;
	union {
		struct adfs_dirheader	*dirhead;
		struct adfs_bigdirheader *bighead;
	};
	union {
		struct adfs_newdirtail	*newtail;
		struct adfs_bigdirtail	*bigtail;
	};
};

/*
@@ -122,13 +125,13 @@ struct object_info {
struct adfs_dir_ops {
	int	(*read)(struct super_block *sb, unsigned int indaddr,
			unsigned int size, struct adfs_dir *dir);
	int	(*iterate)(struct adfs_dir *dir, struct dir_context *ctx);
	int	(*setpos)(struct adfs_dir *dir, unsigned int fpos);
	int	(*getnext)(struct adfs_dir *dir, struct object_info *obj);
	int	(*update)(struct adfs_dir *dir, struct object_info *obj);
	int	(*create)(struct adfs_dir *dir, struct object_info *obj);
	int	(*remove)(struct adfs_dir *dir, struct object_info *obj);
	int	(*sync)(struct adfs_dir *dir);
	void	(*free)(struct adfs_dir *dir);
	int	(*commit)(struct adfs_dir *dir);
};

struct adfs_discmap {
@@ -145,7 +148,9 @@ int adfs_notify_change(struct dentry *dentry, struct iattr *attr);

/* map.c */
int adfs_map_lookup(struct super_block *sb, u32 frag_id, unsigned int offset);
extern unsigned int adfs_map_free(struct super_block *sb);
void adfs_map_statfs(struct super_block *sb, struct kstatfs *buf);
struct adfs_discmap *adfs_read_map(struct super_block *sb, struct adfs_discrecord *dr);
void adfs_free_map(struct super_block *sb);

/* Misc */
__printf(3, 4)
@@ -167,6 +172,13 @@ extern const struct dentry_operations adfs_dentry_operations;
extern const struct adfs_dir_ops adfs_f_dir_ops;
extern const struct adfs_dir_ops adfs_fplus_dir_ops;

int adfs_dir_copyfrom(void *dst, struct adfs_dir *dir, unsigned int offset,
		      size_t len);
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);
+246 −68
Original line number Diff line number Diff line
@@ -6,12 +6,196 @@
 *
 *  Common directory handling for ADFS
 */
#include <linux/slab.h>
#include "adfs.h"

/*
 * For future.  This should probably be per-directory.
 */
static DEFINE_RWLOCK(adfs_dir_lock);
static DECLARE_RWSEM(adfs_dir_rwsem);

int adfs_dir_copyfrom(void *dst, struct adfs_dir *dir, unsigned int offset,
		      size_t len)
{
	struct super_block *sb = dir->sb;
	unsigned int index, remain;

	index = offset >> sb->s_blocksize_bits;
	offset &= sb->s_blocksize - 1;
	remain = sb->s_blocksize - offset;
	if (index + (remain < len) >= dir->nr_buffers)
		return -EINVAL;

	if (remain < len) {
		memcpy(dst, dir->bhs[index]->b_data + offset, remain);
		dst += remain;
		len -= remain;
		index += 1;
		offset = 0;
	}

	memcpy(dst, dir->bhs[index]->b_data + offset, len);

	return 0;
}

int adfs_dir_copyto(struct adfs_dir *dir, unsigned int offset, const void *src,
		    size_t len)
{
	struct super_block *sb = dir->sb;
	unsigned int index, remain;

	index = offset >> sb->s_blocksize_bits;
	offset &= sb->s_blocksize - 1;
	remain = sb->s_blocksize - offset;
	if (index + (remain < len) >= dir->nr_buffers)
		return -EINVAL;

	if (remain < len) {
		memcpy(dir->bhs[index]->b_data + offset, src, remain);
		src += remain;
		len -= remain;
		index += 1;
		offset = 0;
	}

	memcpy(dir->bhs[index]->b_data + offset, src, len);

	return 0;
}

static void __adfs_dir_cleanup(struct adfs_dir *dir)
{
	dir->nr_buffers = 0;

	if (dir->bhs != dir->bh)
		kfree(dir->bhs);
	dir->bhs = NULL;
	dir->sb = NULL;
}

void adfs_dir_relse(struct adfs_dir *dir)
{
	unsigned int i;

	for (i = 0; i < dir->nr_buffers; i++)
		brelse(dir->bhs[i]);

	__adfs_dir_cleanup(dir);
}

static void adfs_dir_forget(struct adfs_dir *dir)
{
	unsigned int i;

	for (i = 0; i < dir->nr_buffers; i++)
		bforget(dir->bhs[i]);

	__adfs_dir_cleanup(dir);
}

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)
{
	dir->sb = sb;
	dir->bhs = dir->bh;
	dir->nr_buffers = 0;

	return ADFS_SB(sb)->s_dir->read(sb, indaddr, size, dir);
}

static int adfs_dir_read_inode(struct super_block *sb, struct inode *inode,
			       struct adfs_dir *dir)
{
	int ret;

	ret = adfs_dir_read(sb, ADFS_I(inode)->indaddr, inode->i_size, dir);
	if (ret)
		return ret;

	if (ADFS_I(inode)->parent_id != dir->parent_id) {
		adfs_error(sb,
			   "parent directory id changed under me! (%06x but got %06x)\n",
			   ADFS_I(inode)->parent_id, dir->parent_id);
		adfs_dir_relse(dir);
		ret = -EIO;
	}

	return ret;
}

static void adfs_dir_mark_dirty(struct adfs_dir *dir)
{
	unsigned int i;

	/* Mark the buffers dirty */
	for (i = 0; i < dir->nr_buffers; i++)
		mark_buffer_dirty(dir->bhs[i]);
}

static int adfs_dir_sync(struct adfs_dir *dir)
{
	int err = 0;
	int i;

	for (i = dir->nr_buffers - 1; i >= 0; i--) {
		struct buffer_head *bh = dir->bhs[i];
		sync_dirty_buffer(bh);
		if (buffer_req(bh) && !buffer_uptodate(bh))
			err = -EIO;
	}

	return err;
}

void adfs_object_fixup(struct adfs_dir *dir, struct object_info *obj)
{
@@ -51,87 +235,90 @@ void adfs_object_fixup(struct adfs_dir *dir, struct object_info *obj)
	}
}

static int
adfs_readdir(struct file *file, struct dir_context *ctx)
static int adfs_iterate(struct file *file, struct dir_context *ctx)
{
	struct inode *inode = file_inode(file);
	struct super_block *sb = inode->i_sb;
	const struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir;
	struct object_info obj;
	struct adfs_dir dir;
	int ret = 0;

	if (ctx->pos >> 32)
		return 0;
	int ret;

	ret = ops->read(sb, inode->i_ino, inode->i_size, &dir);
	down_read(&adfs_dir_rwsem);
	ret = adfs_dir_read_inode(sb, inode, &dir);
	if (ret)
		return ret;
		goto unlock;

	if (ctx->pos == 0) {
		if (!dir_emit_dot(file, ctx))
			goto free_out;
			goto unlock_relse;
		ctx->pos = 1;
	}
	if (ctx->pos == 1) {
		if (!dir_emit(ctx, "..", 2, dir.parent_id, DT_DIR))
			goto free_out;
			goto unlock_relse;
		ctx->pos = 2;
	}

	read_lock(&adfs_dir_lock);
	ret = ops->iterate(&dir, ctx);

	ret = ops->setpos(&dir, ctx->pos - 2);
	if (ret)
		goto unlock_out;
	while (ops->getnext(&dir, &obj) == 0) {
		if (!dir_emit(ctx, obj.name, obj.name_len,
			      obj.indaddr, DT_UNKNOWN))
			break;
		ctx->pos++;
	}

unlock_out:
	read_unlock(&adfs_dir_lock);
unlock_relse:
	up_read(&adfs_dir_rwsem);
	adfs_dir_relse(&dir);
	return ret;

free_out:
	ops->free(&dir);
unlock:
	up_read(&adfs_dir_rwsem);
	return ret;
}

int
adfs_dir_update(struct super_block *sb, struct object_info *obj, int wait)
{
	int ret = -EINVAL;
#ifdef CONFIG_ADFS_FS_RW
	const struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir;
	struct adfs_dir dir;
	int ret;

	printk(KERN_INFO "adfs_dir_update: object %06x in dir %06x\n",
		 obj->indaddr, obj->parent_id);
	if (!IS_ENABLED(CONFIG_ADFS_FS_RW))
		return -EINVAL;

	if (!ops->update) {
		ret = -EINVAL;
		goto out;
	}
	if (!ops->update)
		return -EINVAL;

	ret = ops->read(sb, obj->parent_id, 0, &dir);
	down_write(&adfs_dir_rwsem);
	ret = adfs_dir_read(sb, obj->parent_id, 0, &dir);
	if (ret)
		goto out;
		goto unlock;

	write_lock(&adfs_dir_lock);
	ret = ops->update(&dir, obj);
	write_unlock(&adfs_dir_lock);
	if (ret)
		goto forget;

	if (wait) {
		int err = ops->sync(&dir);
		if (!ret)
			ret = err;
	}
	ret = ops->commit(&dir);
	if (ret)
		goto forget;
	up_write(&adfs_dir_rwsem);

	adfs_dir_mark_dirty(&dir);

	if (wait)
		ret = adfs_dir_sync(&dir);

	adfs_dir_relse(&dir);
	return ret;

	/*
	 * If the updated failed because the entry wasn't found, we can
	 * just release the buffers. If it was any other error, forget
	 * the dirtied buffers so they aren't written back to the media.
	 */
forget:
	if (ret == -ENOENT)
		adfs_dir_relse(&dir);
	else
		adfs_dir_forget(&dir);
unlock:
	up_write(&adfs_dir_rwsem);

	ops->free(&dir);
out:
#endif
	return ret;
}

@@ -167,25 +354,14 @@ static int adfs_dir_lookup_byname(struct inode *inode, const struct qstr *qstr,
	u32 name_len;
	int ret;

	ret = ops->read(sb, inode->i_ino, inode->i_size, &dir);
	down_read(&adfs_dir_rwsem);
	ret = adfs_dir_read_inode(sb, inode, &dir);
	if (ret)
		goto out;

	if (ADFS_I(inode)->parent_id != dir.parent_id) {
		adfs_error(sb,
			   "parent directory changed under me! (%06x but got %06x)\n",
			   ADFS_I(inode)->parent_id, dir.parent_id);
		ret = -EIO;
		goto free_out;
	}

	obj->parent_id = inode->i_ino;

	read_lock(&adfs_dir_lock);
		goto unlock;

	ret = ops->setpos(&dir, 0);
	if (ret)
		goto unlock_out;
		goto unlock_relse;

	ret = -ENOENT;
	name = qstr->name;
@@ -196,20 +372,22 @@ static int adfs_dir_lookup_byname(struct inode *inode, const struct qstr *qstr,
			break;
		}
	}
	obj->parent_id = ADFS_I(inode)->indaddr;

unlock_out:
	read_unlock(&adfs_dir_lock);
unlock_relse:
	up_read(&adfs_dir_rwsem);
	adfs_dir_relse(&dir);
	return ret;

free_out:
	ops->free(&dir);
out:
unlock:
	up_read(&adfs_dir_rwsem);
	return ret;
}

const struct file_operations adfs_dir_operations = {
	.read		= generic_read_dir,
	.llseek		= generic_file_llseek,
	.iterate	= adfs_readdir,
	.iterate_shared	= adfs_iterate,
	.fsync		= generic_file_fsync,
};

+82 −220
Original line number Diff line number Diff line
@@ -9,8 +9,6 @@
#include "adfs.h"
#include "dir_f.h"

static void adfs_f_free(struct adfs_dir *dir);

/*
 * Read an (unaligned) value of length 1..4 bytes
 */
@@ -60,7 +58,7 @@ static inline void adfs_writeval(unsigned char *p, int len, unsigned int val)
#define bufoff(_bh,_idx)			\
	({ int _buf = _idx >> blocksize_bits;	\
	   int _off = _idx - (_buf << blocksize_bits);\
	  (u8 *)(_bh[_buf]->b_data + _off);	\
	  (void *)(_bh[_buf]->b_data + _off);	\
	})

/*
@@ -123,65 +121,49 @@ adfs_dir_checkbyte(const struct adfs_dir *dir)
	return (dircheck ^ (dircheck >> 8) ^ (dircheck >> 16) ^ (dircheck >> 24)) & 0xff;
}

/* Read and check that a directory is valid */
static int adfs_dir_read(struct super_block *sb, u32 indaddr,
			 unsigned int size, struct adfs_dir *dir)
static int adfs_f_validate(struct adfs_dir *dir)
{
	const unsigned int blocksize_bits = sb->s_blocksize_bits;
	int blk = 0;

	/*
	 * Directories which are not a multiple of 2048 bytes
	 * are considered bad v2 [3.6]
	 */
	if (size & 2047)
		goto bad_dir;

	size >>= blocksize_bits;

	dir->nr_buffers = 0;
	dir->sb = sb;

	for (blk = 0; blk < size; blk++) {
		int phys;
	struct adfs_dirheader *head = dir->dirhead;
	struct adfs_newdirtail *tail = dir->newtail;

	if (head->startmasseq != tail->endmasseq ||
	    tail->dirlastmask || tail->reserved[0] || tail->reserved[1] ||
	    (memcmp(&head->startname, "Nick", 4) &&
	     memcmp(&head->startname, "Hugo", 4)) ||
	    memcmp(&head->startname, &tail->endname, 4) ||
	    adfs_dir_checkbyte(dir) != tail->dircheckbyte)
		return -EIO;

		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;
	return 0;
}

		dir->bh[blk] = sb_bread(sb, phys);
		if (!dir->bh[blk])
			goto release_buffers;
	}
/* Read and check that a directory is valid */
static int adfs_f_read(struct super_block *sb, u32 indaddr, unsigned int size,
		       struct adfs_dir *dir)
{
	const unsigned int blocksize_bits = sb->s_blocksize_bits;
	int ret;

	memcpy(&dir->dirhead, bufoff(dir->bh, 0), sizeof(dir->dirhead));
	memcpy(&dir->dirtail, bufoff(dir->bh, 2007), sizeof(dir->dirtail));
	if (size && size != ADFS_NEWDIR_SIZE)
		return -EIO;

	if (dir->dirhead.startmasseq != dir->dirtail.new.endmasseq ||
	    memcmp(&dir->dirhead.startname, &dir->dirtail.new.endname, 4))
		goto bad_dir;
	ret = adfs_dir_read_buffers(sb, indaddr, ADFS_NEWDIR_SIZE, dir);
	if (ret)
		return ret;

	if (memcmp(&dir->dirhead.startname, "Nick", 4) &&
	    memcmp(&dir->dirhead.startname, "Hugo", 4))
		goto bad_dir;
	dir->dirhead = bufoff(dir->bh, 0);
	dir->newtail = bufoff(dir->bh, 2007);

	if (adfs_dir_checkbyte(dir) != dir->dirtail.new.dircheckbyte)
	if (adfs_f_validate(dir))
		goto bad_dir;

	dir->nr_buffers = blk;
	dir->parent_id = adfs_readval(dir->newtail->dirparent, 3);

	return 0;

bad_dir:
	adfs_error(sb, "dir %06x is corrupted", indaddr);
release_buffers:
	for (blk -= 1; blk >= 0; blk -= 1)
		brelse(dir->bh[blk]);

	dir->sb = NULL;
	adfs_dir_relse(dir);

	return -EIO;
}
@@ -232,24 +214,12 @@ adfs_obj2dir(struct adfs_direntry *de, struct object_info *obj)
static int
__adfs_dir_get(struct adfs_dir *dir, int pos, struct object_info *obj)
{
	struct super_block *sb = dir->sb;
	struct adfs_direntry de;
	int thissize, buffer, offset;

	buffer = pos >> sb->s_blocksize_bits;

	if (buffer > dir->nr_buffers)
		return -EINVAL;

	offset = pos & (sb->s_blocksize - 1);
	thissize = sb->s_blocksize - offset;
	if (thissize > 26)
		thissize = 26;
	int ret;

	memcpy(&de, dir->bh[buffer]->b_data + offset, thissize);
	if (thissize != 26)
		memcpy(((char *)&de) + thissize, dir->bh[buffer + 1]->b_data,
		       26 - thissize);
	ret = adfs_dir_copyfrom(&de, dir, pos, 26);
	if (ret)
		return ret;

	if (!de.dirobname[0])
		return -ENOENT;
@@ -259,89 +229,6 @@ __adfs_dir_get(struct adfs_dir *dir, int pos, struct object_info *obj)
	return 0;
}

static int
__adfs_dir_put(struct adfs_dir *dir, int pos, struct object_info *obj)
{
	struct super_block *sb = dir->sb;
	struct adfs_direntry de;
	int thissize, buffer, offset;

	buffer = pos >> sb->s_blocksize_bits;

	if (buffer > dir->nr_buffers)
		return -EINVAL;

	offset = pos & (sb->s_blocksize - 1);
	thissize = sb->s_blocksize - offset;
	if (thissize > 26)
		thissize = 26;

	/*
	 * Get the entry in total
	 */
	memcpy(&de, dir->bh[buffer]->b_data + offset, thissize);
	if (thissize != 26)
		memcpy(((char *)&de) + thissize, dir->bh[buffer + 1]->b_data,
		       26 - thissize);

	/*
	 * update it
	 */
	adfs_obj2dir(&de, obj);

	/*
	 * Put the new entry back
	 */
	memcpy(dir->bh[buffer]->b_data + offset, &de, thissize);
	if (thissize != 26)
		memcpy(dir->bh[buffer + 1]->b_data, ((char *)&de) + thissize,
		       26 - thissize);

	return 0;
}

/*
 * the caller is responsible for holding the necessary
 * locks.
 */
static int adfs_dir_find_entry(struct adfs_dir *dir, u32 indaddr)
{
	int pos, ret;

	ret = -ENOENT;

	for (pos = 5; pos < ADFS_NUM_DIR_ENTRIES * 26 + 5; pos += 26) {
		struct object_info obj;

		if (!__adfs_dir_get(dir, pos, &obj))
			break;

		if (obj.indaddr == indaddr) {
			ret = pos;
			break;
		}
	}

	return ret;
}

static int adfs_f_read(struct super_block *sb, u32 indaddr, unsigned int size,
		       struct adfs_dir *dir)
{
	int ret;

	if (size != ADFS_NEWDIR_SIZE)
		return -EIO;

	ret = adfs_dir_read(sb, indaddr, size, dir);
	if (ret)
		adfs_error(sb, "unable to read directory");
	else
		dir->parent_id = adfs_readval(dir->dirtail.new.dirparent, 3);

	return ret;
}

static int
adfs_f_setpos(struct adfs_dir *dir, unsigned int fpos)
{
@@ -364,99 +251,74 @@ adfs_f_getnext(struct adfs_dir *dir, struct object_info *obj)
	return ret;
}

static int
adfs_f_update(struct adfs_dir *dir, struct object_info *obj)
static int adfs_f_iterate(struct adfs_dir *dir, struct dir_context *ctx)
{
	struct super_block *sb = dir->sb;
	int ret, i;
	struct object_info obj;
	int pos = 5 + (ctx->pos - 2) * 26;

	ret = adfs_dir_find_entry(dir, obj->indaddr);
	if (ret < 0) {
		adfs_error(dir->sb, "unable to locate entry to update");
		goto out;
	while (ctx->pos < 2 + ADFS_NUM_DIR_ENTRIES) {
		if (__adfs_dir_get(dir, pos, &obj))
			break;
		if (!dir_emit(ctx, obj.name, obj.name_len,
			      obj.indaddr, DT_UNKNOWN))
			break;
		pos += 26;
		ctx->pos++;
	}
	return 0;
}

	__adfs_dir_put(dir, ret, obj);
 
	/*
	 * Increment directory sequence number
	 */
	dir->bh[0]->b_data[0] += 1;
	dir->bh[dir->nr_buffers - 1]->b_data[sb->s_blocksize - 6] += 1;

	ret = adfs_dir_checkbyte(dir);
	/*
	 * Update directory check byte
	 */
	dir->bh[dir->nr_buffers - 1]->b_data[sb->s_blocksize - 1] = ret;

#if 1
static int adfs_f_update(struct adfs_dir *dir, struct object_info *obj)
{
	const unsigned int blocksize_bits = sb->s_blocksize_bits;

	memcpy(&dir->dirhead, bufoff(dir->bh, 0), sizeof(dir->dirhead));
	memcpy(&dir->dirtail, bufoff(dir->bh, 2007), sizeof(dir->dirtail));

	if (dir->dirhead.startmasseq != dir->dirtail.new.endmasseq ||
	    memcmp(&dir->dirhead.startname, &dir->dirtail.new.endname, 4))
		goto bad_dir;
	struct adfs_direntry de;
	int offset, ret;

	if (memcmp(&dir->dirhead.startname, "Nick", 4) &&
	    memcmp(&dir->dirhead.startname, "Hugo", 4))
		goto bad_dir;
	offset = 5 - (int)sizeof(de);

	if (adfs_dir_checkbyte(dir) != dir->dirtail.new.dircheckbyte)
		goto bad_dir;
	do {
		offset += sizeof(de);
		ret = adfs_dir_copyfrom(&de, dir, offset, sizeof(de));
		if (ret) {
			adfs_error(dir->sb, "error reading directory entry");
			return -ENOENT;
		}
#endif
	for (i = dir->nr_buffers - 1; i >= 0; i--)
		mark_buffer_dirty(dir->bh[i]);

	ret = 0;
out:
	return ret;
#if 1
bad_dir:
	adfs_error(dir->sb, "whoops!  I broke a directory!");
	return -EIO;
#endif
		if (!de.dirobname[0]) {
			adfs_error(dir->sb, "unable to locate entry to update");
			return -ENOENT;
		}
	} while (adfs_readval(de.dirinddiscadd, 3) != obj->indaddr);

static int
adfs_f_sync(struct adfs_dir *dir)
{
	int err = 0;
	int i;

	for (i = dir->nr_buffers - 1; i >= 0; i--) {
		struct buffer_head *bh = dir->bh[i];
		sync_dirty_buffer(bh);
		if (buffer_req(bh) && !buffer_uptodate(bh))
			err = -EIO;
	}
	/* Update the directory entry with the new object state */
	adfs_obj2dir(&de, obj);

	return err;
	/* Write the directory entry back to the directory */
	return adfs_dir_copyto(dir, offset, &de, 26);
}

static void
adfs_f_free(struct adfs_dir *dir)
static int adfs_f_commit(struct adfs_dir *dir)
{
	int i;
	int ret;

	for (i = dir->nr_buffers - 1; i >= 0; i--) {
		brelse(dir->bh[i]);
		dir->bh[i] = NULL;
	}
	/* Increment directory sequence number */
	dir->dirhead->startmasseq += 1;
	dir->newtail->endmasseq += 1;

	/* Update directory check byte */
	dir->newtail->dircheckbyte = adfs_dir_checkbyte(dir);

	dir->nr_buffers = 0;
	dir->sb = NULL;
	/* Make sure the directory still validates correctly */
	ret = adfs_f_validate(dir);
	if (ret)
		adfs_msg(dir->sb, KERN_ERR, "error: update broke directory");

	return ret;
}

const struct adfs_dir_ops adfs_f_dir_ops = {
	.read		= adfs_f_read,
	.iterate	= adfs_f_iterate,
	.setpos		= adfs_f_setpos,
	.getnext	= adfs_f_getnext,
	.update		= adfs_f_update,
	.sync		= adfs_f_sync,
	.free		= adfs_f_free
	.commit		= adfs_f_commit,
};
+28 −24
Original line number Diff line number Diff line
@@ -13,9 +13,9 @@
 * Directory header
 */
struct adfs_dirheader {
	unsigned char startmasseq;
	unsigned char startname[4];
};
	__u8 startmasseq;
	__u8 startname[4];
} __attribute__((packed));

#define ADFS_NEWDIR_SIZE	2048
#define ADFS_NUM_DIR_ENTRIES	77
@@ -31,32 +31,36 @@ struct adfs_direntry {
	__u8 dirlen[4];
	__u8 dirinddiscadd[3];
	__u8 newdiratts;
};
} __attribute__((packed));

/*
 * Directory tail
 */
union adfs_dirtail {
	struct {
		unsigned char dirlastmask;
struct adfs_olddirtail {
	__u8 dirlastmask;
	char dirname[10];
		unsigned char dirparent[3];
	__u8 dirparent[3];
	char dirtitle[19];
		unsigned char reserved[14];
		unsigned char endmasseq;
		unsigned char endname[4];
		unsigned char dircheckbyte;
	} old;
	struct {
		unsigned char dirlastmask;
		unsigned char reserved[2];
		unsigned char dirparent[3];
	__u8 reserved[14];
	__u8 endmasseq;
	__u8 endname[4];
	__u8 dircheckbyte;
} __attribute__((packed));

struct adfs_newdirtail {
	__u8 dirlastmask;
	__u8 reserved[2];
	__u8 dirparent[3];
	char dirtitle[19];
	char dirname[10];
		unsigned char endmasseq;
		unsigned char endname[4];
		unsigned char dircheckbyte;
	} new;
	__u8 endmasseq;
	__u8 endname[4];
	__u8 dircheckbyte;
} __attribute__((packed));

union adfs_dirtail {
	struct adfs_olddirtail old;
	struct adfs_newdirtail new;
};

#endif
Loading