patch-2.4.4 linux/fs/affs/namei.c

Next file: linux/fs/affs/super.c
Previous file: linux/fs/affs/inode.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.4.3/linux/fs/affs/namei.c linux/fs/affs/namei.c
@@ -8,7 +8,6 @@
  *  (C) 1991  Linus Torvalds - minix filesystem
  */
 
-#define DEBUG 0
 #include <linux/sched.h>
 #include <linux/affs_fs.h>
 #include <linux/kernel.h>
@@ -21,283 +20,268 @@
 
 #include <linux/errno.h>
 
+typedef int (*toupper_t)(int);
+
 extern struct inode_operations affs_symlink_inode_operations;
 
+static int	 affs_toupper(int ch);
+static int	 affs_hash_dentry(struct dentry *, struct qstr *);
+static int       affs_compare_dentry(struct dentry *, struct qstr *, struct qstr *);
+static int	 affs_intl_toupper(int ch);
+static int	 affs_intl_hash_dentry(struct dentry *, struct qstr *);
+static int       affs_intl_compare_dentry(struct dentry *, struct qstr *, struct qstr *);
+
+struct dentry_operations affs_dentry_operations = {
+	d_hash:		affs_hash_dentry,
+	d_compare:	affs_compare_dentry,
+};
+
+struct dentry_operations affs_intl_dentry_operations = {
+	d_hash:		affs_intl_hash_dentry,
+	d_compare:	affs_intl_compare_dentry,
+};
+
+
 /* Simple toupper() for DOS\1 */
 
-static unsigned int
-affs_toupper(unsigned int ch)
+static int
+affs_toupper(int ch)
 {
 	return ch >= 'a' && ch <= 'z' ? ch -= ('a' - 'A') : ch;
 }
 
 /* International toupper() for DOS\3 ("international") */
 
-static unsigned int
-affs_intl_toupper(unsigned int ch)
+static int
+affs_intl_toupper(int ch)
 {
 	return (ch >= 'a' && ch <= 'z') || (ch >= 0xE0
 		&& ch <= 0xFE && ch != 0xF7) ?
 		ch - ('a' - 'A') : ch;
 }
 
-static int	 affs_hash_dentry(struct dentry *, struct qstr *);
-static int       affs_compare_dentry(struct dentry *, struct qstr *, struct qstr *);
-struct dentry_operations affs_dentry_operations = {
-	d_hash:		affs_hash_dentry,
-	d_compare:	affs_compare_dentry,
-};
+static inline toupper_t
+affs_get_toupper(struct super_block *sb)
+{
+	return AFFS_SB->s_flags & SF_INTL ? affs_intl_toupper : affs_toupper;
+}
 
 /*
  * Note: the dentry argument is the parent dentry.
  */
-static int
-affs_hash_dentry(struct dentry *dentry, struct qstr *qstr)
+static inline int
+__affs_hash_dentry(struct dentry *dentry, struct qstr *qstr, toupper_t toupper)
 {
-	unsigned int (*toupper)(unsigned int) = affs_toupper;
-	unsigned long	 hash;
-	int		 i;
+	const char *name = qstr->name;
+	unsigned long hash;
+	int i;
 
-	if ((i = affs_check_name(qstr->name,qstr->len)))
+	i = affs_check_name(qstr->name,qstr->len);
+	if (i)
 		return i;
 
-	/* Check whether to use the international 'toupper' routine */
-	if (AFFS_I2FSTYPE(dentry->d_inode))
-		toupper = affs_intl_toupper;
 	hash = init_name_hash();
-	for (i = 0; i < qstr->len && i < 30; i++)
-		hash = partial_name_hash(toupper(qstr->name[i]), hash);
+	i = MIN(qstr->len, 30);
+	for (; i > 0; name++, i--)
+		hash = partial_name_hash(toupper(*name), hash);
 	qstr->hash = end_name_hash(hash);
 
 	return 0;
 }
 
 static int
-affs_compare_dentry(struct dentry *dentry, struct qstr *a, struct qstr *b)
+affs_hash_dentry(struct dentry *dentry, struct qstr *qstr)
+{
+	return __affs_hash_dentry(dentry, qstr, affs_toupper);
+}
+static int
+affs_intl_hash_dentry(struct dentry *dentry, struct qstr *qstr)
+{
+	return __affs_hash_dentry(dentry, qstr, affs_intl_toupper);
+}
+
+static inline int
+__affs_compare_dentry(struct dentry *dentry, struct qstr *a, struct qstr *b, toupper_t toupper)
 {
-	unsigned int (*toupper)(unsigned int) = affs_toupper;
-	int	 alen = a->len;
-	int      blen = b->len;
-	int	 i;
+	const u8 *aname = a->name;
+	const u8 *bname = b->name;
+	int len;
 
 	/* 'a' is the qstr of an already existing dentry, so the name
 	 * must be valid. 'b' must be validated first.
 	 */
-	
+
 	if (affs_check_name(b->name,b->len))
 		return 1;
 
 	/* If the names are longer than the allowed 30 chars,
 	 * the excess is ignored, so their length may differ.
 	 */
-	if (alen > 30)
-		alen = 30;
-	if (blen > 30)
-		blen = 30;
-	if (alen != blen)
+	len = a->len;
+	if (len >= 30) {
+		if (b->len < 30)
+			return 1;
+		len = 30;
+	} else if (len != b->len)
 		return 1;
 
-	/* Check whether to use the international 'toupper' routine */
-	if (AFFS_I2FSTYPE(dentry->d_inode))
-		toupper = affs_intl_toupper;
-
-	for (i = 0; i < alen; i++)
-		if (toupper(a->name[i]) != toupper(b->name[i]))
+	for (; len > 0; len--)
+		if (toupper(*aname++) != toupper(*bname++))
 			return 1;
-	
+
 	return 0;
 }
 
+static int
+affs_compare_dentry(struct dentry *dentry, struct qstr *a, struct qstr *b)
+{
+	return __affs_compare_dentry(dentry, a, b, affs_toupper);
+}
+static int
+affs_intl_compare_dentry(struct dentry *dentry, struct qstr *a, struct qstr *b)
+{
+	return __affs_compare_dentry(dentry, a, b, affs_intl_toupper);
+}
+
 /*
  * NOTE! unlike strncmp, affs_match returns 1 for success, 0 for failure.
  */
 
-static int
-affs_match(const unsigned char *name, int len, const unsigned char *compare, int dlen, int intl)
+static inline int
+affs_match(struct dentry *dentry, const u8 *name2, toupper_t toupper)
 {
-	unsigned int	(*toupper)(unsigned int) = intl ? affs_intl_toupper : affs_toupper;
-	int		  i;
-
-	if (!compare)
-		return 0;
+	const u8 *name = dentry->d_name.name;
+	int len = dentry->d_name.len;
 
-	if (len > 30)
+	if (len >= 30) {
+		if (*name2 < 30)
+			return 0;
 		len = 30;
-	if (dlen > 30)
-		dlen = 30;
-
-	/* "" means "." ---> so paths like "/usr/lib//libc.a" work */
-	if (!len && dlen == 1 && compare[0] == '.')
-		return 1;
-	if (dlen != len)
+	} else if (len != *name2)
 		return 0;
-	for (i = 0; i < len; i++)
-		if (toupper(name[i]) != toupper(compare[i]))
+
+	for (name2++; len > 0; len--)
+		if (toupper(*name++) != toupper(*name2++))
 			return 0;
 	return 1;
 }
 
 int
-affs_hash_name(const unsigned char *name, int len, int intl, int hashsize)
+affs_hash_name(struct super_block *sb, const u8 *name, unsigned int len)
 {
-	unsigned int i, x;
-
-	if (len > 30)
-		len = 30;
+	toupper_t toupper = affs_get_toupper(sb);
+	int hash;
 
-	x = len;
-	for (i = 0; i < len; i++)
-		if (intl)
-			x = (x * 13 + affs_intl_toupper(name[i] & 0xFF)) & 0x7ff;
-		else
-			x = (x * 13 + affs_toupper(name[i] & 0xFF)) & 0x7ff;
+	hash = len = MIN(len, 30);
+	for (; len > 0; len--)
+		hash = (hash * 13 + toupper(*name++)) & 0x7ff;
 
-	return x % hashsize;
+	return hash % AFFS_SB->s_hashsize;
 }
 
 static struct buffer_head *
-affs_find_entry(struct inode *dir, struct dentry *dentry, unsigned long *ino)
+affs_find_entry(struct inode *dir, struct dentry *dentry)
 {
-	struct buffer_head	*bh;
-	int			 intl = AFFS_I2FSTYPE(dir);
-	s32			 key;
-	const char		*name = dentry->d_name.name;
-	int			 namelen = dentry->d_name.len;
+	struct super_block *sb = dir->i_sb;
+	struct buffer_head *bh;
+	toupper_t toupper = affs_get_toupper(sb);
+	u32 key;
 
-	pr_debug("AFFS: find_entry(\"%.*s\")\n",namelen,name);
+	pr_debug("AFFS: find_entry(\"%.*s\")\n", (int)dentry->d_name.len, dentry->d_name.name);
 
-	bh = affs_bread(dir->i_dev,dir->i_ino,AFFS_I2BSIZE(dir));
+	bh = affs_bread(sb, dir->i_ino);
 	if (!bh)
-		return NULL;
+		return ERR_PTR(-EIO);
 
-	if (namelen == 1 && name[0] == '.') {
-		*ino = dir->i_ino;
-		return bh;
-	}
-	if (namelen == 2 && name[0] == '.' && name[1] == '.') {
-		*ino = affs_parent_ino(dir);
-		return bh;
-	}
-
-	key = AFFS_GET_HASHENTRY(bh->b_data,affs_hash_name(name,namelen,intl,AFFS_I2HSIZE(dir)));
+	key = be32_to_cpu(AFFS_HEAD(bh)->table[affs_hash_name(sb, dentry->d_name.name, dentry->d_name.len)]);
 
 	for (;;) {
-		unsigned char *cname;
-		int cnamelen;
-
 		affs_brelse(bh);
-		bh = NULL;
 		if (key == 0)
-			break;
-		bh = affs_bread(dir->i_dev,key,AFFS_I2BSIZE(dir));
+			return NULL;
+		bh = affs_bread(sb, key);
 		if (!bh)
-			break;
-		cnamelen = affs_get_file_name(AFFS_I2BSIZE(dir),bh->b_data,&cname);
-		if (affs_match(name,namelen,cname,cnamelen,intl))
-			break;
-		key = be32_to_cpu(FILE_END(bh->b_data,dir)->hash_chain);
+			return ERR_PTR(-EIO);
+		if (affs_match(dentry, AFFS_TAIL(sb, bh)->name, toupper))
+			return bh;
+		key = be32_to_cpu(AFFS_TAIL(sb, bh)->hash_chain);
 	}
-	*ino = key;
-	return bh;
 }
 
 struct dentry *
 affs_lookup(struct inode *dir, struct dentry *dentry)
 {
-	unsigned long		 ino;
-	struct buffer_head	*bh;
-	struct inode		*inode;
+	struct super_block *sb = dir->i_sb;
+	struct buffer_head *bh;
+	struct inode *inode = NULL;
 
 	pr_debug("AFFS: lookup(\"%.*s\")\n",(int)dentry->d_name.len,dentry->d_name.name);
 
-	inode = NULL;
-	bh = affs_find_entry(dir,dentry,&ino);
+	down(&AFFS_DIR->i_hash_lock);
+	bh = affs_find_entry(dir, dentry);
+	up(&AFFS_DIR->i_hash_lock);
+	if (IS_ERR(bh))
+		return ERR_PTR(PTR_ERR(bh));
 	if (bh) {
-		if (FILE_END(bh->b_data,dir)->original)
-			ino = be32_to_cpu(FILE_END(bh->b_data,dir)->original);
+		u32 ino = bh->b_blocknr;
+
+		/* store the real header ino in d_fsdata for faster lookups */
+		dentry->d_fsdata = (void *)(long)ino;
+		switch (be32_to_cpu(AFFS_TAIL(sb, bh)->stype)) {
+		case ST_LINKDIR:
+		case ST_LINKFILE:
+			ino = be32_to_cpu(AFFS_TAIL(sb, bh)->original);
+		}
 		affs_brelse(bh);
-		inode = iget(dir->i_sb,ino);
+		inode = iget(sb, ino);
 		if (!inode)
 			return ERR_PTR(-EACCES);
 	}
-	dentry->d_op = &affs_dentry_operations;
-	d_add(dentry,inode);
+	dentry->d_op = AFFS_SB->s_flags & SF_INTL ? &affs_intl_dentry_operations : &affs_dentry_operations;
+	d_add(dentry, inode);
 	return NULL;
 }
 
 int
 affs_unlink(struct inode *dir, struct dentry *dentry)
 {
-	int			 retval;
-	struct buffer_head	*bh;
-	unsigned long		 ino;
-	struct inode		*inode;
-
-	pr_debug("AFFS: unlink(dir=%ld,\"%.*s\")\n",dir->i_ino,
-		 (int)dentry->d_name.len,dentry->d_name.name);
+	pr_debug("AFFS: unlink(dir=%d, \"%.*s\")\n", (u32)dir->i_ino,
+		 (int)dentry->d_name.len, dentry->d_name.name);
 
-	retval  = -ENOENT;
-	if (!(bh = affs_find_entry(dir,dentry,&ino)))
-		goto unlink_done;
-
-	inode  = dentry->d_inode;
-
-	if ((retval = affs_remove_header(bh,inode)) < 0)
-		goto unlink_done;
-	
-	inode->i_nlink = retval;
-	inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
-	dir->i_version = ++event;
-	mark_inode_dirty(inode);
-	mark_inode_dirty(dir);
-	retval = 0;
+	if (!dentry->d_inode)
+		return -ENOENT;
 
-unlink_done:
-	affs_brelse(bh);
-	return retval;
+	return affs_remove_header(dentry);
 }
 
 int
 affs_create(struct inode *dir, struct dentry *dentry, int mode)
 {
+	struct super_block *sb = dir->i_sb;
 	struct inode	*inode;
 	int		 error;
-	
+
 	pr_debug("AFFS: create(%lu,\"%.*s\",0%o)\n",dir->i_ino,(int)dentry->d_name.len,
 		 dentry->d_name.name,mode);
 
-	error = -ENOSPC;
 	inode = affs_new_inode(dir);
 	if (!inode)
-		goto out;
+		return -ENOSPC;
 
-	pr_debug("AFFS: ino=%lu\n",inode->i_ino);
-	if (dir->i_sb->u.affs_sb.s_flags & SF_OFS) {
-		inode->i_op = &affs_file_inode_operations;
-		inode->i_fop = &affs_file_operations_ofs;
-	} else {
-		inode->i_op = &affs_file_inode_operations;
-		inode->i_fop = &affs_file_operations;
-		inode->i_mapping->a_ops = &affs_aops;
-		inode->u.affs_i.mmu_private = inode->i_size;
-	}
-	error = affs_add_entry(dir,NULL,inode,dentry,ST_FILE);
-	if (error)
-		goto out_iput;
 	inode->i_mode = mode;
-	inode->u.affs_i.i_protect = mode_to_prot(inode->i_mode);
-	d_instantiate(dentry,inode);
+	mode_to_prot(inode);
 	mark_inode_dirty(inode);
-	dir->i_version = ++event;
-	mark_inode_dirty(dir);
-out:
-	return error;
 
-out_iput:
-	inode->i_nlink = 0;
-	mark_inode_dirty(inode);
-	iput(inode);
-	goto out;
+	inode->i_op = &affs_file_inode_operations;
+	inode->i_fop = &affs_file_operations;
+	inode->i_mapping->a_ops = (AFFS_SB->s_flags & SF_OFS) ? &affs_aops_ofs : &affs_aops;
+	error = affs_add_entry(dir, inode, dentry, ST_FILE);
+	if (error) {
+		inode->i_nlink = 0;
+		iput(inode);
+		return error;
+	}
+	return 0;
 }
 
 int
@@ -305,121 +289,78 @@
 {
 	struct inode		*inode;
 	int			 error;
-	
+
 	pr_debug("AFFS: mkdir(%lu,\"%.*s\",0%o)\n",dir->i_ino,
 		 (int)dentry->d_name.len,dentry->d_name.name,mode);
 
-	error = -ENOSPC;
 	inode = affs_new_inode(dir);
 	if (!inode)
-		goto out;
+		return -ENOSPC;
+
+	inode->i_mode = S_IFDIR | mode;
+	mode_to_prot(inode);
 
 	inode->i_op = &affs_dir_inode_operations;
 	inode->i_fop = &affs_dir_operations;
-	error       = affs_add_entry(dir,NULL,inode,dentry,ST_USERDIR);
-	if (error)
-		goto out_iput;
-	inode->i_mode = S_IFDIR | S_ISVTX | mode;
-	inode->u.affs_i.i_protect = mode_to_prot(inode->i_mode);
-	d_instantiate(dentry,inode);
-	mark_inode_dirty(inode);
-	dir->i_version = ++event;
-	mark_inode_dirty(dir);
-out:
-	return error;
 
-out_iput:
-	inode->i_nlink = 0;
-	mark_inode_dirty(inode);
-	iput(inode);
-	goto out;
-}
-
-static int
-empty_dir(struct buffer_head *bh, int hashsize)
-{
-	while (--hashsize >= 0) {
-		if (((struct dir_front *)bh->b_data)->hashtable[hashsize])
-			return 0;
+	error = affs_add_entry(dir, inode, dentry, ST_USERDIR);
+	if (error) {
+		inode->i_nlink = 0;
+		mark_inode_dirty(inode);
+		iput(inode);
+		return error;
 	}
-	return 1;
+	return 0;
 }
 
 int
 affs_rmdir(struct inode *dir, struct dentry *dentry)
 {
-	struct inode		*inode = dentry->d_inode;
-	int			 retval;
-	unsigned long		 ino;
-	struct buffer_head	*bh;
-
-	pr_debug("AFFS: rmdir(dir=%lu,\"%.*s\")\n",dir->i_ino,
-		 (int)dentry->d_name.len,dentry->d_name.name);
-
-	retval = -ENOENT;
-	if (!(bh = affs_find_entry(dir,dentry,&ino)))
-		goto rmdir_done;
+	pr_debug("AFFS: rmdir(dir=%u, \"%.*s\")\n", (u32)dir->i_ino,
+		 (int)dentry->d_name.len, dentry->d_name.name);
 
-	/*
-	 * Make sure the directory is empty and the dentry isn't busy.
-	 */
-	retval = -ENOTEMPTY;
-	if (!empty_dir(bh,AFFS_I2HSIZE(inode)))
-		goto rmdir_done;
-	retval = -EBUSY;
-	if (!d_unhashed(dentry))
-		goto rmdir_done;
-
-	if ((retval = affs_remove_header(bh,inode)) < 0)
-		goto rmdir_done;
-	
-	inode->i_nlink = retval;
-	inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
-	retval         = 0;
-	dir->i_version = ++event;
-	mark_inode_dirty(dir);
-	mark_inode_dirty(inode);
+	if (!dentry->d_inode)
+		return -ENOENT;
 
-rmdir_done:
-	affs_brelse(bh);
-	return retval;
+	return affs_remove_header(dentry);
 }
 
 int
 affs_symlink(struct inode *dir, struct dentry *dentry, const char *symname)
 {
+	struct super_block	*sb = dir->i_sb;
 	struct buffer_head	*bh;
 	struct inode		*inode;
 	char			*p;
-	unsigned long		 tmp;
 	int			 i, maxlen, error;
 	char			 c, lc;
 
 	pr_debug("AFFS: symlink(%lu,\"%.*s\" -> \"%s\")\n",dir->i_ino,
 		 (int)dentry->d_name.len,dentry->d_name.name,symname);
-	
-	maxlen = 4 * AFFS_I2HSIZE(dir) - 1;
+
+	maxlen = AFFS_SB->s_hashsize * sizeof(u32) - 1;
 	error = -ENOSPC;
 	inode  = affs_new_inode(dir);
 	if (!inode)
-		goto out;
+		return -ENOSPC;
 
 	inode->i_op = &affs_symlink_inode_operations;
 	inode->i_data.a_ops = &affs_symlink_aops;
 	inode->i_mode = S_IFLNK | 0777;
-	inode->u.affs_i.i_protect = mode_to_prot(inode->i_mode);
+	mode_to_prot(inode);
+
 	error = -EIO;
-	bh = affs_bread(inode->i_dev,inode->i_ino,AFFS_I2BSIZE(inode));
+	bh = affs_bread(sb, inode->i_ino);
 	if (!bh)
-		goto out_iput;
+		goto err;
 	i  = 0;
-	p  = ((struct slink_front *)bh->b_data)->symname;
+	p  = (char *)AFFS_HEAD(bh)->table;
 	lc = '/';
 	if (*symname == '/') {
 		while (*symname == '/')
 			symname++;
-		while (inode->i_sb->u.affs_sb.s_volume[i])	/* Cannot overflow */
-			*p++ = inode->i_sb->u.affs_sb.s_volume[i++];
+		while (AFFS_SB->s_volume[i])	/* Cannot overflow */
+			*p++ = AFFS_SB->s_volume[i++];
 	}
 	while (i < maxlen && (c = *symname++)) {
 		if (c == '.' && lc == '/' && *symname == '.' && symname[1] == '/') {
@@ -444,159 +385,90 @@
 	affs_brelse(bh);
 	mark_inode_dirty(inode);
 
-	/* N.B. This test shouldn't be necessary ... dentry must be negative */
-	error = -EEXIST;
-	bh = affs_find_entry(dir,dentry,&tmp);
-	if (bh)
-		goto out_release;
-	/* N.B. Shouldn't we add the entry before dirtying the buffer? */
-	error = affs_add_entry(dir,NULL,inode,dentry,ST_SOFTLINK);
+	error = affs_add_entry(dir, inode, dentry, ST_SOFTLINK);
 	if (error)
-		goto out_release;
-	d_instantiate(dentry,inode);
-	dir->i_version = ++event;
-	mark_inode_dirty(dir);
+		goto err;
 
-out:
-	return error;
+	return 0;
 
-out_release:
-	affs_brelse(bh);
-out_iput:
+err:
 	inode->i_nlink = 0;
 	mark_inode_dirty(inode);
 	iput(inode);
-	goto out;
+	return error;
 }
 
 int
 affs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry)
 {
-	struct inode		*oldinode = old_dentry->d_inode;
-	struct inode		*inode;
-	struct buffer_head	*bh;
-	unsigned long		 i;
-	int			 error;
-	
-	pr_debug("AFFS: link(%lu,%lu,\"%.*s\")\n",oldinode->i_ino,dir->i_ino,
-		 (int)dentry->d_name.len,dentry->d_name.name);
+	struct inode *inode = old_dentry->d_inode;
+	int error;
 
-	/* N.B. Do we need this test? The dentry must be negative ... */
-	bh = affs_find_entry(dir,dentry,&i);
-	if (bh) {
-		affs_brelse(bh);
-		return -EEXIST;
-	}
-	if (oldinode->u.affs_i.i_hlink)	{	/* Cannot happen */
-		affs_warning(dir->i_sb,"link","Impossible link to link");
-		return -EINVAL;
-	}
-	error = -ENOSPC;
-	if (!(inode = affs_new_inode(dir)))
-		goto out;
+	pr_debug("AFFS: link(%u, %u, \"%.*s\")\n", (u32)inode->i_ino, (u32)dir->i_ino,
+		 (int)dentry->d_name.len,dentry->d_name.name);
 
-	inode->i_op                = oldinode->i_op;
-	inode->i_fop               = oldinode->i_fop;
-	inode->u.affs_i.i_protect  = mode_to_prot(oldinode->i_mode);
-	inode->u.affs_i.i_original = oldinode->i_ino;
-	inode->u.affs_i.i_hlink    = 1;
-	inode->i_mtime             = oldinode->i_mtime;
-
-	if (S_ISDIR(oldinode->i_mode))
-		error = affs_add_entry(dir,oldinode,inode,dentry,ST_LINKDIR);
-	else
-		error = affs_add_entry(dir,oldinode,inode,dentry,ST_LINKFILE);
-	if (error)
+	error = affs_add_entry(dir, inode, dentry, S_ISDIR(inode->i_mode) ? ST_LINKDIR : ST_LINKFILE);
+	if (error) {
 		inode->i_nlink = 0;
-	else {
-		dir->i_version = ++event;
-		mark_inode_dirty(dir);
-		mark_inode_dirty(oldinode);
-		atomic_inc(&oldinode->i_count);
-		d_instantiate(dentry,oldinode);
+		mark_inode_dirty(inode);
+		iput(inode);
+		return error;
 	}
-	mark_inode_dirty(inode);
-	iput(inode);
-
-out:
-	return error;
+	return 0;
 }
 
 int
 affs_rename(struct inode *old_dir, struct dentry *old_dentry,
 	    struct inode *new_dir, struct dentry *new_dentry)
 {
-	struct inode		*old_inode = old_dentry->d_inode;
-	struct inode		*new_inode = new_dentry->d_inode;
-	struct buffer_head	*old_bh;
-	struct buffer_head	*new_bh;
-	unsigned long		 old_ino;
-	unsigned long		 new_ino;
-	int			 retval;
-
-	pr_debug("AFFS: rename(old=%lu,\"%*s\" (inode=%p) to new=%lu,\"%*s\" (inode=%p))\n",
-		 old_dir->i_ino,old_dentry->d_name.len,old_dentry->d_name.name,old_inode,
-		 new_dir->i_ino,new_dentry->d_name.len,new_dentry->d_name.name,new_inode);
-	
-	if ((retval = affs_check_name(new_dentry->d_name.name,new_dentry->d_name.len)))
-		goto out;
+	struct super_block *sb = old_dir->i_sb;
+	struct inode *dir;
+	struct buffer_head *bh = NULL;
+	int retval;
+
+	pr_debug("AFFS: rename(old=%u,\"%*s\" to new=%u,\"%*s\")\n",
+		 (u32)old_dir->i_ino, (int)old_dentry->d_name.len, old_dentry->d_name.name,
+		 (u32)new_dir->i_ino, (int)new_dentry->d_name.len, new_dentry->d_name.name);
 
-	new_bh = NULL;
-	retval = -ENOENT;
-	old_bh = affs_find_entry(old_dir,old_dentry,&old_ino);
-	if (!old_bh)
-		goto end_rename;
-
-	new_bh = affs_find_entry(new_dir,new_dentry,&new_ino);
-	if (new_bh && !new_inode) {
-		affs_error(old_inode->i_sb,"affs_rename",
-			   "No inode for entry found (key=%lu)\n",new_ino);
-		goto end_rename;
-	}
-	if (S_ISDIR(old_inode->i_mode)) {
-		if (new_inode) {
-			retval = -EBUSY;
-			if (!d_unhashed(new_dentry))
-				goto end_rename;
-			retval = -ENOTEMPTY;
-			if (!empty_dir(new_bh,AFFS_I2HSIZE(new_inode)))
-				goto end_rename;
-		}
+	if ((retval = affs_check_name(new_dentry->d_name.name,new_dentry->d_name.len)))
+		goto done;
 
-		retval = -ENOENT;
-		if (affs_parent_ino(old_inode) != old_dir->i_ino)
-			goto end_rename;
-	}
 	/* Unlink destination if it already exists */
-	if (new_inode) {
-		if ((retval = affs_remove_header(new_bh,new_dir)) < 0)
-			goto end_rename;
-		new_inode->i_nlink = retval;
-		mark_inode_dirty(new_inode);
-		if (new_inode->i_ino == new_ino)
-			new_inode->i_nlink = 0;
+	if (new_dentry->d_inode) {
+		retval = affs_remove_header(new_dentry);
+		if (retval)
+			return retval;
 	}
+
+	retval = -EIO;
+	bh = affs_bread(sb, old_dentry->d_inode->i_ino);
+	if (!bh)
+		goto done;
+
 	/* Remove header from its parent directory. */
-	if ((retval = affs_remove_hash(old_bh,old_dir)))
-		goto end_rename;
+	dir = old_dir;
+	down(&AFFS_DIR->i_hash_lock);
+	retval = affs_remove_hash(dir, bh);
+	if (retval)
+		goto done_unlock;
+
+	up(&AFFS_DIR->i_hash_lock);
+
 	/* And insert it into the new directory with the new name. */
-	affs_copy_name(FILE_END(old_bh->b_data,old_inode)->file_name,new_dentry->d_name.name);
-	if ((retval = affs_insert_hash(new_dir->i_ino,old_bh,new_dir)))
-		goto end_rename;
-	affs_fix_checksum(AFFS_I2BSIZE(new_dir),old_bh->b_data,5);
-
-	new_dir->i_ctime   = new_dir->i_mtime = old_dir->i_ctime
-			   = old_dir->i_mtime = CURRENT_TIME;
-	new_dir->i_version = ++event;
-	old_dir->i_version = ++event;
-	retval             = 0;
-	mark_inode_dirty(new_dir);
-	mark_inode_dirty(old_dir);
-	mark_buffer_dirty(old_bh);
-	
-end_rename:
-	affs_brelse(old_bh);
-	affs_brelse(new_bh);
-out:
+	dir = new_dir;
+	affs_copy_name(AFFS_TAIL(sb, bh)->name, new_dentry);
+	affs_fix_checksum(sb, bh);
+	down(&AFFS_DIR->i_hash_lock);
+	retval = affs_insert_hash(new_dir, bh);
+	if (retval)
+		goto done_unlock;
+	up(&AFFS_DIR->i_hash_lock);
+
+done:
+	affs_brelse(bh);
 	return retval;
+
+done_unlock:
+	up(&AFFS_DIR->i_hash_lock);
+	goto done;
 }

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)