diff options
Diffstat (limited to 'fs/adfs/dir_f.c')
| -rw-r--r-- | fs/adfs/dir_f.c | 302 | 
1 files changed, 82 insertions, 220 deletions
| diff --git a/fs/adfs/dir_f.c b/fs/adfs/dir_f.c index c1a950c7400a..30d526fecc3f 100644 --- a/fs/adfs/dir_f.c +++ b/fs/adfs/dir_f.c @@ -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; @@ -260,89 +230,6 @@ __adfs_dir_get(struct adfs_dir *dir, int pos, struct object_info *obj)  }  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)  {  	if (fpos >= ADFS_NUM_DIR_ENTRIES) @@ -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 -	{ -	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)); +static int adfs_f_update(struct adfs_dir *dir, struct object_info *obj) +{ +	struct adfs_direntry de; +	int offset, ret; -	if (dir->dirhead.startmasseq != dir->dirtail.new.endmasseq || -	    memcmp(&dir->dirhead.startname, &dir->dirtail.new.endname, 4)) -		goto bad_dir; +	offset = 5 - (int)sizeof(de); -	if (memcmp(&dir->dirhead.startname, "Nick", 4) && -	    memcmp(&dir->dirhead.startname, "Hugo", 4)) -		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; +		} +		if (!de.dirobname[0]) { +			adfs_error(dir->sb, "unable to locate entry to update"); +			return -ENOENT; +		} +	} while (adfs_readval(de.dirinddiscadd, 3) != obj->indaddr); -	if (adfs_dir_checkbyte(dir) != dir->dirtail.new.dircheckbyte) -		goto bad_dir; -	} -#endif -	for (i = dir->nr_buffers - 1; i >= 0; i--) -		mark_buffer_dirty(dir->bh[i]); +	/* Update the directory entry with the new object state */ +	adfs_obj2dir(&de, obj); -	ret = 0; -out: -	return ret; -#if 1 -bad_dir: -	adfs_error(dir->sb, "whoops!  I broke a directory!"); -	return -EIO; -#endif +	/* Write the directory entry back to the directory */ +	return adfs_dir_copyto(dir, offset, &de, 26);  } -static int -adfs_f_sync(struct adfs_dir *dir) +static int adfs_f_commit(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; -	} +	int ret; -	return err; -} +	/* Increment directory sequence number */ +	dir->dirhead->startmasseq += 1; +	dir->newtail->endmasseq += 1; -static void -adfs_f_free(struct adfs_dir *dir) -{ -	int i; +	/* Update directory check byte */ +	dir->newtail->dircheckbyte = adfs_dir_checkbyte(dir); -	for (i = dir->nr_buffers - 1; i >= 0; i--) { -		brelse(dir->bh[i]); -		dir->bh[i] = 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"); -	dir->nr_buffers = 0; -	dir->sb = NULL; +	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,  }; |