patch-2.1.71 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.1.70/linux/fs/affs/namei.c linux/fs/affs/namei.c
@@ -8,6 +8,7 @@
  *  (C) 1991  Linus Torvalds - minix filesystem
  */
 
+#define DEBUG 0
 #include <linux/sched.h>
 #include <linux/affs_fs.h>
 #include <linux/kernel.h>
@@ -20,8 +21,6 @@
 
 #include <linux/errno.h>
 
-static int affs_fixup(struct buffer_head *bh, struct inode *inode);
-
 /* Simple toupper() for DOS\1 */
 
 static inline unsigned int
@@ -45,7 +44,7 @@
  */
 
 static int
-affs_match(const char *name, int len, const char *compare, int dlen, int intl)
+affs_match(const unsigned char *name, int len, const unsigned char *compare, int dlen, int intl)
 {
 	if (!compare)
 		return 0;
@@ -62,14 +61,14 @@
 		return 0;
 	if (intl) {
 		while (dlen--) {
-			if (affs_intl_toupper(*name & 0xFF) != affs_intl_toupper(*compare & 0xFF))
+			if (affs_intl_toupper(*name) != affs_intl_toupper(*compare))
 				return 0;
 			name++;
 			compare++;
 		}
 	} else {
 		while (dlen--) {
-			if (affs_toupper(*name & 0xFF) != affs_toupper(*compare & 0xFF))
+			if (affs_toupper(*name) != affs_toupper(*compare))
 				return 0;
 			name++;
 			compare++;
@@ -79,7 +78,7 @@
 }
 
 int
-affs_hash_name(const char *name, int len, int intl, int hashsize)
+affs_hash_name(const unsigned char *name, int len, int intl, int hashsize)
 {
 	unsigned int i, x;
 
@@ -97,14 +96,15 @@
 }
 
 static struct buffer_head *
-affs_find_entry(struct inode *dir, const char *name, int namelen,
-		unsigned long *ino)
+affs_find_entry(struct inode *dir, struct dentry *dentry, unsigned long *ino)
 {
-	struct buffer_head *bh;
-	int	 intl;
-	s32	 key;
+	struct buffer_head	*bh;
+	int			 intl;
+	s32			 key;
+	const char		*name = dentry->d_name.name;
+	int			 namelen = dentry->d_name.len;
 
-	pr_debug("AFFS: find_entry(%.*s)=\n",namelen,name);
+	pr_debug("AFFS: find_entry(\"%.*s\")\n",namelen,name);
 
 	intl = AFFS_I2FSTYPE(dir);
 	bh   = affs_bread(dir->i_dev,dir->i_ino,AFFS_I2BSIZE(dir));
@@ -123,7 +123,7 @@
 	key = AFFS_GET_HASHENTRY(bh->b_data,affs_hash_name(name,namelen,intl,AFFS_I2HSIZE(dir)));
 
 	for (;;) {
-		char *cname;
+		unsigned char *cname;
 		int cnamelen;
 
 		affs_brelse(bh);
@@ -137,154 +137,127 @@
 		cnamelen = affs_get_file_name(AFFS_I2BSIZE(dir),bh->b_data,&cname);
 		if (affs_match(name,namelen,cname,cnamelen,intl))
 			break;
-		key = htonl(FILE_END(bh->b_data,dir)->hash_chain);
+		key = be32_to_cpu(FILE_END(bh->b_data,dir)->hash_chain);
 	}
 	*ino = key;
 	return bh;
 }
 
 int
-affs_lookup(struct inode *dir, const char *name, int len, struct inode **result)
+affs_lookup(struct inode *dir, struct dentry *dentry)
 {
-	int res;
-	unsigned long ino;
-	struct buffer_head *bh;
-
-	pr_debug("AFFS: lookup(%.*s)\n",len,name);
+	unsigned long		 ino;
+	struct buffer_head	*bh;
+	struct inode		*inode;
 
-	*result = NULL;
-	if (!dir)
-		return -ENOENT;
+	pr_debug("AFFS: lookup(\"%.*s\")\n",(int)dentry->d_name.len,dentry->d_name.name);
 
-	res = -ENOENT;
-	if (S_ISDIR(dir->i_mode)) {
-		if ((bh = affs_find_entry(dir,name,len,&ino))) {
-			if (FILE_END(bh->b_data,dir)->original)
-				ino = htonl(FILE_END(bh->b_data,dir)->original);
-			affs_brelse(bh);
-			if ((*result = iget(dir->i_sb,ino))) 
-				res = 0;
-			else
-				res = -EACCES;
-		}
+	inode = NULL;
+	bh = affs_find_entry(dir,dentry,&ino);
+	if (bh) {
+		if (FILE_END(bh->b_data,dir)->original)
+			ino = be32_to_cpu(FILE_END(bh->b_data,dir)->original);
+		affs_brelse(bh);
+		inode = iget(dir->i_sb,ino);
+		if (!inode)
+			return -EACCES;
 	}
-	iput(dir);
-	return res;
+	d_add(dentry,inode);
+	return 0;
 }
 
 int
-affs_unlink(struct inode *dir, const char *name, int len)
+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,len,name);
+	pr_debug("AFFS: unlink(dir=%ld,\"%.*s\")\n",dir->i_ino,
+		 (int)dentry->d_name.len,dentry->d_name.name);
 
 	bh      = NULL;
-	inode   = NULL;
 	retval  = -ENOENT;
-	if (!(bh = affs_find_entry(dir,name,len,&ino))) {
-		goto unlink_done;
-	}
-	if (!(inode = iget(dir->i_sb,ino))) {
+	if (!dir)
 		goto unlink_done;
-	}
-	if (S_ISDIR(inode->i_mode)) {
-		retval = -EPERM;
+	if (!(bh = affs_find_entry(dir,dentry,&ino)))
 		goto unlink_done;
-	}
 
-	if ((retval = affs_fix_hash_pred(dir,affs_hash_name(name,len,AFFS_I2FSTYPE(dir),
-					 AFFS_I2HSIZE(dir)) + 6,ino,
-					 FILE_END(bh->b_data,dir)->hash_chain)))
+	inode  = dentry->d_inode;
+	retval = -EPERM;
+	if (S_ISDIR(inode->i_mode))
 		goto unlink_done;
-
-	if ((retval = affs_fixup(bh,inode)))
+	if (current->fsuid != inode->i_uid &&
+	    current->fsuid != dir->i_uid && !fsuser())
 		goto unlink_done;
 
-	inode->i_nlink=0;
-	mark_inode_dirty(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(dir);
+	mark_inode_dirty(inode);
+	retval = 0;
+	d_delete(dentry);
 unlink_done:
 	affs_brelse(bh);
-	iput(inode);
-	iput(dir);
 	return retval;
 }
 
 int
-affs_create(struct inode *dir, const char *name, int len, int mode, struct inode **result)
+affs_create(struct inode *dir, struct dentry *dentry, int mode)
 {
 	struct inode	*inode;
 	int		 error;
 	
-	pr_debug("AFFS: create(%lu,\"%.*s\",0%o)\n",dir->i_ino,len,name,mode);
+	pr_debug("AFFS: create(%lu,\"%.*s\",0%o)\n",dir->i_ino,(int)dentry->d_name.len,
+		 dentry->d_name.name,mode);
 
-	*result = NULL;
-	if (!dir || !dir->i_sb) {
-		iput(dir);
-		return -EINVAL;
-	}
+	if (!dir)
+		return -ENOENT;
 	inode = affs_new_inode(dir);
-	if (!inode) {
-		iput (dir);
+	if (!inode)
 		return -ENOSPC;
-	}
-	inode->i_mode = mode;
+
+	pr_debug(" -- ino=%lu\n",inode->i_ino);
 	if (dir->i_sb->u.affs_sb.s_flags & SF_OFS)
 		inode->i_op = &affs_file_inode_operations_ofs;
 	else
 		inode->i_op = &affs_file_inode_operations;
 
-	error = affs_add_entry(dir,NULL,inode,name,len,ST_FILE);
+	error = affs_add_entry(dir,NULL,inode,dentry,ST_FILE);
 	if (error) {
-		iput(dir);
 		inode->i_nlink = 0;
 		mark_inode_dirty(inode);
 		iput(inode);
-		return -ENOSPC;
+		return error;
 	}
+	inode->i_mode = mode;
 	inode->u.affs_i.i_protect = mode_to_prot(inode->i_mode);
-
-	iput(dir);
-	*result = inode;
+	dir->i_version = ++event;
+	mark_inode_dirty(dir);
+	d_instantiate(dentry,inode);
 
 	return 0;
 }
 
 int
-affs_mkdir(struct inode *dir, const char *name, int len, int mode)
+affs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
 {
 	struct inode		*inode;
-	struct buffer_head	*bh;
-	unsigned long		 i;
 	int			 error;
 	
-	pr_debug("AFFS: mkdir(%lu,\"%.*s\",0%o)\n",dir->i_ino,len,name,mode);
+	pr_debug("AFFS: mkdir(%lu,\"%.*s\",0%o)\n",dir->i_ino,
+		 (int)dentry->d_name.len,dentry->d_name.name,mode);
 
-	if (!dir || !dir->i_sb) {
-		iput(dir);
-		return -EINVAL;
-	}
-	bh = affs_find_entry(dir,name,len,&i);
-	if (bh) {
-		affs_brelse(bh);
-		iput(dir);
-		return -EEXIST;
-	}
 	inode = affs_new_inode(dir);
-	if (!inode) {
-		iput (dir);
+	if (!inode)
 		return -ENOSPC;
-	}
+
 	inode->i_op = &affs_dir_inode_operations;
-	error       = affs_add_entry(dir,NULL,inode,name,len,ST_USERDIR);
+	error       = affs_add_entry(dir,NULL,inode,dentry,ST_USERDIR);
 	if (error) {
-		iput(dir);
 		inode->i_nlink = 0;
 		mark_inode_dirty(inode);
 		iput(inode);
@@ -292,9 +265,9 @@
 	}
 	inode->i_mode = S_IFDIR | (mode & 0777 & ~current->fs->umask);
 	inode->u.affs_i.i_protect = mode_to_prot(inode->i_mode);
-
-	iput(dir);
-	iput(inode);
+	dir->i_version = ++event;
+	mark_inode_dirty(dir);
+	d_instantiate(dentry,inode);
 
 	return 0;
 }
@@ -310,26 +283,29 @@
 }
 
 int
-affs_rmdir(struct inode *dir, const char *name, int len)
+affs_rmdir(struct inode *dir, struct dentry *dentry)
 {
 	int			 retval;
 	unsigned long		 ino;
 	struct inode		*inode;
 	struct buffer_head	*bh;
 
-	pr_debug("AFFS: rmdir(dir=%lu,\"%.*s\")\n",dir->i_ino,len,name);
+	pr_debug("AFFS: rmdir(dir=%lu,\"%.*s\")\n",dir->i_ino,
+		 (int)dentry->d_name.len,dentry->d_name.name);
 
 	inode  = NULL;
+	bh     = NULL;
 	retval = -ENOENT;
-	if (!(bh = affs_find_entry(dir,name,len,&ino))) {
+	if (!dir)
 		goto rmdir_done;
-	}
-	if (!(inode = iget(dir->i_sb,ino))) {
+	if (!(bh = affs_find_entry(dir,dentry,&ino)))
 		goto rmdir_done;
-	}
+
+	inode = dentry->d_inode;
+
 	retval = -EPERM;
-        if (!fsuser() && current->fsuid != inode->i_uid &&
-            current->fsuid != dir->i_uid)
+        if (current->fsuid != inode->i_uid &&
+            current->fsuid != dir->i_uid && !fsuser())
 		goto rmdir_done;
 	if (inode->i_dev != dir->i_dev)
 		goto rmdir_done;
@@ -339,6 +315,11 @@
 		retval = -ENOTDIR;
 		goto rmdir_done;
 	}
+	down(&inode->i_sem);
+	if (dentry->d_count > 1) {
+		shrink_dcache_parent(dentry);
+	}
+	up(&inode->i_sem);
 	if (!empty_dir(bh,AFFS_I2HSIZE(inode))) {
 		retval = -ENOTEMPTY;
 		goto rmdir_done;
@@ -347,28 +328,21 @@
 		retval = -EBUSY;
 		goto rmdir_done;
 	}
-	if ((retval = affs_fix_hash_pred(dir,affs_hash_name(name,len,AFFS_I2FSTYPE(dir),
-					 AFFS_I2HSIZE(dir)) + 6,ino,
-					 FILE_END(bh->b_data,dir)->hash_chain)))
+	if ((retval = affs_remove_header(bh,inode)) < 0)
 		goto rmdir_done;
-
-	if ((retval = affs_fixup(bh,inode)))
-		goto rmdir_done;
-
-	inode->i_nlink=0;
-	mark_inode_dirty(inode);
+	
+	inode->i_nlink = retval;
 	inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
-	dir->i_version = ++event;
-	mark_inode_dirty(dir);
+	retval         = 0;
+	mark_inode_dirty(inode);
+	d_delete(dentry);
 rmdir_done:
-	iput(dir);
-	iput(inode);
 	affs_brelse(bh);
 	return retval;
 }
 
 int
-affs_symlink(struct inode *dir, const char *name, int len, const char *symname)
+affs_symlink(struct inode *dir, struct dentry *dentry, const char *symname)
 {
 	struct buffer_head	*bh;
 	struct inode		*inode;
@@ -377,20 +351,19 @@
 	int			 i, maxlen;
 	char			 c, lc;
 
-	pr_debug("AFFS: symlink(%lu,\"%.*s\" -> \"%s\")\n",dir->i_ino,len,name,symname);
+	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;
 	inode  = affs_new_inode(dir);
-	if (!inode) {
-		iput(dir);
+	if (!inode)
 		return -ENOSPC;
-	}
+
 	inode->i_op   = &affs_symlink_inode_operations;
 	inode->i_mode = S_IFLNK | 0777;
 	inode->u.affs_i.i_protect = mode_to_prot(inode->i_mode);
 	bh = affs_bread(inode->i_dev,inode->i_ino,AFFS_I2BSIZE(inode));
 	if (!bh) {
-		iput(dir);
 		inode->i_nlink = 0;
 		mark_inode_dirty(inode);
 		iput(inode);
@@ -427,157 +400,135 @@
 	mark_buffer_dirty(bh,1);
 	affs_brelse(bh);
 	mark_inode_dirty(inode);
-	bh = affs_find_entry(dir,name,len,&tmp);
+	bh = affs_find_entry(dir,dentry,&tmp);
 	if (bh) {
 		inode->i_nlink = 0;
 		iput(inode);
 		affs_brelse(bh);
-		iput(dir);
 		return -EEXIST;
 	}
-	i = affs_add_entry(dir,NULL,inode,name,len,ST_SOFTLINK);
+	i = affs_add_entry(dir,NULL,inode,dentry,ST_SOFTLINK);
 	if (i) {
 		inode->i_nlink = 0;
 		mark_inode_dirty(inode);
 		iput(inode);
 		affs_brelse(bh);
-		iput(dir);
 		return i;
 	}
-	iput(dir);
-	iput(inode);
+	dir->i_version = ++event;
+	d_instantiate(dentry,inode);
 	
 	return 0;
 }
 
 int
-affs_link(struct inode *oldinode, struct inode *dir, const char *name, int len)
+affs_link(struct inode *oldinode, struct inode *dir, struct dentry *dentry)
 {
 	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,len,name);
+	pr_debug("AFFS: link(%lu,%lu,\"%.*s\")\n",oldinode->i_ino,dir->i_ino,
+		 (int)dentry->d_name.len,dentry->d_name.name);
 
-	bh = affs_find_entry(dir,name,len,&i);
+	bh = affs_find_entry(dir,dentry,&i);
 	if (bh) {
 		affs_brelse(bh);
-		iput(oldinode);
-		iput(dir);
 		return -EEXIST;
 	}
-	if (oldinode->u.affs_i.i_hlink) {
-		i = oldinode->u.affs_i.i_original;
-		iput(oldinode);
-		oldinode = iget(dir->i_sb,i);
-		if (!oldinode) {
-			affs_error(oldinode->i_sb,"link","Cannot get original from link");
-			iput(dir);
-			return -ENOENT;
-		}
+	if (oldinode->u.affs_i.i_hlink)	{	/* Cannot happen */
+		affs_warning(dir->i_sb,"link","Impossible link to link");
+		return -EINVAL;
 	}
-	inode = affs_new_inode(dir);
-	if (!inode) {
-		iput(oldinode);
-		iput(dir);
+	if (!(inode = affs_new_inode(dir)))
 		return -ENOSPC;
-	}
+
 	inode->i_op                = oldinode->i_op;
-	inode->i_mode              = oldinode->i_mode;
-	inode->i_uid               = oldinode->i_uid;
-	inode->i_gid               = oldinode->i_gid;
-	inode->u.affs_i.i_protect  = mode_to_prot(inode->i_mode);
+	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,name,len,ST_LINKDIR);
+		error = affs_add_entry(dir,oldinode,inode,dentry,ST_LINKDIR);
 	else
-		error = affs_add_entry(dir,oldinode,inode,name,len,ST_LINKFILE);
-	if (error) {
+		error = affs_add_entry(dir,oldinode,inode,dentry,ST_LINKFILE);
+	if (error)
 		inode->i_nlink = 0;
-		mark_inode_dirty(inode);
+	else {
+		dir->i_version = ++event;
+		mark_inode_dirty(oldinode);
+		oldinode->i_count++;
+		d_instantiate(dentry,oldinode);
 	}
-	iput(dir);
+	mark_inode_dirty(inode);
 	iput(inode);
-	iput(oldinode);
 
 	return error;
 }
 
+/* This is copied from the ext2 fs. No need to reinvent the wheel. */
+
 static int
-subdir(struct inode *new_inode, struct inode *old_inode)
+subdir(struct dentry * new_dentry, struct dentry * old_dentry)
 {
-    int ino;
-    int result;
+	int result;
 
-    new_inode->i_count++;
-    result = 0;
-    for (;;) {
-        if (new_inode == old_inode) {
-	    result = 1;
-	    break;
-	}
-	if (new_inode->i_dev != old_inode->i_dev)
-	    break;
-	ino = new_inode->i_ino;
-	if (affs_lookup(new_inode,"..",2,&new_inode))
-	    break;
-	if (new_inode->i_ino == ino)
-	    break;
-    }
-    iput(new_inode);
-    return result;
+	result = 0;
+	for (;;) {
+		if (new_dentry != old_dentry) {
+			struct dentry * parent = new_dentry->d_parent;
+			if (parent == new_dentry)
+				break;
+			new_dentry = parent;
+			continue;
+		}
+		result = 1;
+		break;
+	}
+	return result;
 }
 
-/* I'm afraid this might not be race proof. Maybe next time. */
-
 int
-affs_rename(struct inode *old_dir, const char *old_name, int old_len,
-	    struct inode *new_dir, const char *new_name, int new_len)
+affs_rename(struct inode *old_dir, struct dentry *old_dentry,
+	    struct inode *new_dir, struct dentry *new_dentry)
 {
-	struct inode		*old_inode;
-	struct inode		*new_inode;
+	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\" to new=%lu,\"%*s\")\n",old_dir->i_ino,old_len,old_name,
-		 new_dir->i_ino,new_len,new_name);
+	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 (new_len > 30)
-		new_len = 30;
-	goto start_up;
-retry:
-	affs_brelse(old_bh);
-	affs_brelse(new_bh);
-	iput(new_inode);
-	iput(old_inode);
-	current->counter = 0;
-	schedule();
-start_up:
-	old_inode = new_inode = NULL;
-	old_bh    = new_bh = NULL;
-	retval    = -ENOENT;
+	if ((retval = affs_check_name(new_dentry->d_name.name,new_dentry->d_name.len)))
+		return retval;
 
-	old_bh = affs_find_entry(old_dir,old_name,old_len,&old_ino);
+	new_bh = NULL;
+	retval = -ENOENT;
+	old_bh = affs_find_entry(old_dir,old_dentry,&old_ino);
 	if (!old_bh)
 		goto end_rename;
-	old_inode = iget(old_dir->i_sb,old_ino);
-	if (!old_inode)
+
+	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;
-	new_bh = affs_find_entry(new_dir,new_name,new_len,&new_ino);
-	if (new_bh) {
-		new_inode = iget(new_dir->i_sb,new_ino);
-		if (!new_inode) {		/* What does this mean? */
-			affs_brelse(new_bh);
-			new_bh = NULL;
-		}
 	}
-	if (new_inode == old_inode) {		/* Won't happen */
+	if (new_inode == old_inode) {
+		if (old_ino == new_ino) {	/* Filename might have changed case	*/
+			retval = new_dentry->d_name.len < 31 ? new_dentry->d_name.len : 30;
+			strncpy(DIR_END(old_bh->b_data,old_inode)->dir_name + 1,
+				new_dentry->d_name.name,retval);
+			DIR_END(old_bh->b_data,old_inode)->dir_name[0] = retval;
+			goto new_checksum;
+		}
 		retval = 0;
 		goto end_rename;
 	}
@@ -586,13 +537,13 @@
 		if (!S_ISDIR(old_inode->i_mode))
 			goto end_rename;
 		retval = -EINVAL;
-		if (subdir(new_dir,old_inode))
+		if (subdir(new_dentry,old_dentry))
 			goto end_rename;
 		retval = -ENOTEMPTY;
 		if (!empty_dir(new_bh,AFFS_I2HSIZE(new_inode)))
 			goto end_rename;
 		retval = -EBUSY;
-		if (new_inode->i_count > 1)
+		if (new_dentry->d_count > 1)
 			goto end_rename;
 	}
 	if (S_ISDIR(old_inode->i_mode)) {
@@ -600,142 +551,43 @@
 		if (new_inode && !S_ISDIR(new_inode->i_mode))
 			goto end_rename;
 		retval = -EINVAL;
-		if (subdir(new_dir,old_inode))
+		if (subdir(new_dentry,old_dentry))
 			goto end_rename;
 		if (affs_parent_ino(old_inode) != old_dir->i_ino)
 			goto end_rename;
 	}
-	/* Unlink destination if existent */
+	/* Unlink destination if it already exists */
 	if (new_inode) {
-		if ((retval = affs_fix_hash_pred(new_dir,affs_hash_name(new_name,new_len,
-		                                 AFFS_I2FSTYPE(new_dir),AFFS_I2HSIZE(new_dir)) + 6,
-						 new_ino,
-						 FILE_END(new_bh->b_data,new_dir)->hash_chain)))
-			goto retry;
-		if ((retval = affs_fixup(new_bh,new_inode)))
-			goto retry;
-		mark_buffer_dirty(new_bh,1);
-		new_dir->i_version = ++event;
-		mark_inode_dirty(new_dir);
-		new_inode->i_nlink = 0;
+		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;
 	}
-	retval = affs_fix_hash_pred(old_dir,affs_hash_name(old_name,old_len,AFFS_I2FSTYPE(old_dir),
-				    AFFS_I2HSIZE(old_dir)) + 6,old_ino,
-				    FILE_END(old_bh->b_data,old_dir)->hash_chain);
-	if (retval)
-		goto retry;
-
-	retval = affs_add_entry(new_dir,NULL,old_inode,new_name,new_len,
-				htonl(FILE_END(old_bh->b_data,old_dir)->secondary_type));
+	/* Remove header from its parent directory. */
+	if ((retval = affs_remove_hash(old_bh,old_dir)))
+		goto end_rename;
+	/* 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;
+new_checksum:
+	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_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,1);
+	d_move(old_dentry,new_dentry);
 	
 end_rename:
 	affs_brelse(old_bh);
 	affs_brelse(new_bh);
-	iput(new_inode);
-	iput(old_inode);
-	iput(old_dir);
-	iput(new_dir);
 
 	return retval;
-}
-
-static int
-affs_fixup(struct buffer_head *bh, struct inode *inode)
-{
-	s32			 key, link_key;
-	s32			 type;
-	struct buffer_head	*nbh;
-	struct inode		*ofinode;
-
-	type = htonl(FILE_END(bh->b_data,inode)->secondary_type);
-	if (type == ST_LINKFILE || type == ST_LINKDIR) {
-		key = htonl(LINK_END(bh->b_data,inode)->original);
-		LINK_END(bh->b_data,inode)->original = 0;
-		if (!key) {
-			affs_error(inode->i_sb,"fixup","Hard link without original: ino=%lu",
-				   inode->i_ino);
-			return -ENOENT;
-		}
-		if (!(ofinode = iget(inode->i_sb,key)))
-			return -ENOENT;
-		type = affs_fix_link_pred(ofinode,inode->i_ino,
-					  FILE_END(bh->b_data,inode)->link_chain);
-		iput(ofinode);
-		return type;
-	} else if (type == ST_FILE || type == ST_USERDIR) {
-		if ((key = htonl(FILE_END(bh->b_data,inode)->link_chain))) {
-			/* Get first link, turn it to a file */
-			if (!(ofinode = iget(inode->i_sb,key))) {
-				affs_error(inode->i_sb,"fixup","Cannot read block %d",key);
-				return -ENOENT;
-			}
-			if (!ofinode->u.affs_i.i_hlink) {
-				affs_error(inode->i_sb,"fixup",
-					   "First link to %lu (%d) is not a link",
-					   inode->i_ino,key);
-				iput(ofinode);
-				return -ENOENT;
-			}
-			if (!(nbh = affs_bread(inode->i_dev,key,AFFS_I2BSIZE(inode)))) {
-				affs_error(inode->i_sb,"fixup","Cannot read block %d",key);
-				iput(ofinode);
-				return -ENOENT;
-			}
-			lock_super(inode->i_sb);
-			memcpy(nbh->b_data + 8,bh->b_data + 8,AFFS_I2BSIZE(inode) - 208);
-			FILE_END(nbh->b_data,inode)->byte_size = FILE_END(bh->b_data,inode)->
-									  byte_size;
-			FILE_END(nbh->b_data,inode)->extension = FILE_END(bh->b_data,inode)->
-									  extension;
-			FILE_END(nbh->b_data,inode)->secondary_type = FILE_END(bh->b_data,inode)->
-									  secondary_type;
-			FILE_END(nbh->b_data,inode)->original = 0;
-
-			ofinode->u.affs_i.i_original = 0;
-			ofinode->u.affs_i.i_hlink    = 0;
-			ofinode->i_size              = inode->i_size;
-			ofinode->i_uid               = inode->i_uid;
-			ofinode->i_gid               = inode->i_gid;
-			mark_inode_dirty(ofinode);
-			link_key                     = ofinode->i_ino;
-
-			/* Let all remaining links point to the new file */
-			while (1) {
-				affs_fix_checksum(AFFS_I2BSIZE(inode),nbh->b_data,5);
-				mark_buffer_dirty(nbh,1);
-				key = htonl(FILE_END(nbh->b_data,inode)->link_chain);
-				affs_brelse(nbh);
-				iput(ofinode);
-				if (!key || !(nbh = affs_bread(inode->i_dev,key,AFFS_I2BSIZE(inode))))
-					break;
-				if ((ofinode = iget(inode->i_sb,key))) {
-					if (!ofinode->u.affs_i.i_hlink)
-						affs_error(inode->i_sb,"fixup",
-							   "Inode %d in link chain is not a link",
-							   key);
-					ofinode->u.affs_i.i_original = link_key;
-					mark_inode_dirty(ofinode);
-					FILE_END(nbh->b_data,inode)->original = htonl(link_key);
-				} else
-					affs_error(inode->i_sb,"fixup","Cannot read block %d",key);
-			}
-			/* Turn old inode to a link */
-			inode->u.affs_i.i_hlink = 1;
-			unlock_super(inode->i_sb);
-		}
-		return 0;
-	} else if (type == ST_SOFTLINK) {
-		return 0;
-	} else {
-		affs_error(inode->i_sb,"fixup","Bad secondary type (%d)",type);
-		return -EBADF;
-	}
 }

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov