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

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

diff -u --recursive --new-file v1.3.98/linux/fs/affs/namei.c linux/fs/affs/namei.c
@@ -1,187 +1,736 @@
 /*
  *  linux/fs/affs/namei.c
  *
- *  (C) 1993  Ray Burr - Modified for Amiga FFS filesystem.
+ *  (c) 1996  Hans-Joachim Widmaier - Heavily hacked up.
  *
- *  (C) 1992  Eric Youngdale Modified for ISO9660 filesystem.
+ *  (C) 1993  Ray Burr - Modified for Amiga FFS filesystem.
  *
  *  (C) 1991  Linus Torvalds - minix filesystem
  */
 
 #include <linux/sched.h>
 #include <linux/affs_fs.h>
+#include <linux/amigaffs.h>
 #include <linux/kernel.h>
 #include <linux/string.h>
 #include <linux/stat.h>
 #include <linux/fcntl.h>
+#include <linux/locks.h>
 #include <asm/segment.h>
 
 #include <linux/errno.h>
 
+/* Simple toupper() for DOS\1 */
 
-static inline int namecompare(int len, int maxlen,
-	const char * name, const char * buffer)
+static inline unsigned int
+affs_toupper(unsigned int ch)
 {
-	if (len >= maxlen || !buffer[len]) {
-		return strncmp (name, buffer, len) == 0;
-	}
-	return 0;
+	return ch >= 'a' && ch <= 'z' ? ch -= ('a' - 'A') : ch;
+}
+
+/* International toupper() for DOS\3 */
+
+static inline unsigned int
+affs_intl_toupper(unsigned int ch)
+{
+	return (ch >= 'a' && ch <= 'z') || (ch >= 0xE0
+		&& ch <= 0xFE && ch != 0xF7) ?
+		ch - ('a' - 'A') : ch;
 }
 
 /*
- * ok, we cannot use strncmp, as the name is not in our data space.
- * Thus we'll have to use affs_match. No big problem. Match also makes
- * some sanity tests.
- *
  * NOTE! unlike strncmp, affs_match returns 1 for success, 0 for failure.
  */
-static int affs_match(int len,const char * name, char * compare, int dlen)
+
+static int
+affs_match(const char *name, int len, const char *compare, int dlen, int intl)
 {
-	if (!compare) return 0;
+	if (!compare)
+		return 0;
+
+	if (len > 30)
+		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 0
-	if (len <= 2) printk("Match: %d %d %s %d %d \n",len,dlen,compare,de->name[0], dlen);
-#endif
-	
-	return namecompare(len,dlen,name,compare);
+	if (dlen != len)
+		return 0;
+	if (intl) {
+		while (dlen--) {
+			if (affs_intl_toupper(*name & 0xFF) != affs_intl_toupper(*compare & 0xFF))
+				return 0;
+			name++;
+			compare++;
+		}
+	} else {
+		while (dlen--) {
+			if (affs_toupper(*name & 0xFF) != affs_toupper(*compare & 0xFF))
+				return 0;
+			name++;
+			compare++;
+		}
+	}
+	return 1;
 }
 
-/* Avoid pulling in ctype stuff. */
-
-static int affs_toupper (int ch)
+int
+affs_hash_name(const char *name, int len, int intl, int hashsize)
 {
-	if (ch >= 'a' && ch <= 'z')
-		ch -= ('a' - 'A');
-	return ch;
-}
+	unsigned int i, x;
 
-static int affs_hash_name (const char *name, int len)
-{
-	int i, x;
+	if (len > 30)
+		len = 30;
 
 	x = len;
 	for (i = 0; i < len; i++)
-		x = (x * 13 + affs_toupper (name[i])) & 0x7ff;
-	return x % 72;  /* FIXME: Assumes 512 byte blocks. */
+		if (intl)
+			x = (x * 13 + affs_intl_toupper(name[i] & 0xFF)) & 0x7ff;
+		else
+			x = (x * 13 + affs_toupper(name[i] & 0xFF)) & 0x7ff;
+
+	return x % hashsize;
 }
 
-static struct buffer_head *affs_find_entry(struct inode *dir,
-	const char *name, int namelen, int *ino)
+static struct buffer_head *
+affs_find_entry(struct inode *dir, const char *name, int namelen,
+		unsigned long *ino)
 {
 	struct buffer_head *bh;
-	void *dir_data;
-	int key;
+	int	 intl;
+	ULONG	 key;
 
-	*ino = 0;
+	pr_debug("AFFS: find_entry(%.*s)=\n",namelen,name);
 
-	bh = affs_pread (dir, dir->i_ino, &dir_data);
+	intl = AFFS_I2FSTYPE(dir);
+	bh   = affs_bread(dir->i_dev,dir->i_ino,AFFS_I2BSIZE(dir));
 	if (!bh)
 		return NULL;
 
-	if (affs_match (namelen, name, ".", 1)) {
+	if (affs_match(name,namelen,".",1,intl)) {
 		*ino = dir->i_ino;
 		return bh;
 	}
-	if (affs_match (namelen, name, "..", 2)) {
-		*ino = affs_parent_ino (dir);
+	if (affs_match(name,namelen,"..",2,intl)) {
+		*ino = affs_parent_ino(dir);
 		return bh;
 	}
-	key = affs_get_key_entry (AFFS_I2BSIZE (dir), dir_data,
-				  affs_hash_name (name, namelen));
+
+	key = AFFS_GET_HASHENTRY(bh->b_data,affs_hash_name(name,namelen,intl,AFFS_I2HSIZE(dir)));
 
 	for (;;) {
 		char *cname;
 		int cnamelen;
 
-		brelse (bh);
-		if (key <= 0)
-			return NULL;
-		bh = affs_pread (dir, key, &dir_data);
+		affs_brelse(bh);
+		if (key == 0) {
+			bh = NULL;
+			break;
+		}
+		bh = affs_bread(dir->i_dev,key,AFFS_I2BSIZE(dir));
 		if (!bh)
-			return NULL;
-		cnamelen = affs_get_file_name (AFFS_I2BSIZE (dir),
-					       dir_data, &cname);
-		if (affs_match (namelen, name, cname, cnamelen))
 			break;
-		key = affs_get_fh_hash_link (AFFS_I2BSIZE (dir), dir_data);
+		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);
 	}
-		
+	pr_debug("%lu\n",key);
 	*ino = key;
-
 	return bh;
 }
 
-int affs_lookup(struct inode * dir,const char * name, int len,
-	struct inode ** result)
+int
+affs_lookup(struct inode *dir, const char *name, int len, struct inode **result)
 {
-	int ino;
+	int res;
+	unsigned long ino;
 	struct buffer_head *bh;
 
+	pr_debug("AFFS: lookup(%.*s)\n",len,name);
+
 	*result = NULL;
 	if (!dir)
 		return -ENOENT;
 
-#ifdef DEBUG
-	printk ("lookup: %d %d\n", dir->i_ino, len);
-#endif
+	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;
+		}
+	}
+	iput(dir);
+	return res;
+}
+
+int
+affs_unlink(struct inode *dir, const char *name, int len)
+{
+	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);
+
+	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))) {
+		goto unlink_done;
+	}
+	if (S_ISDIR(inode->i_mode)) {
+		retval = -EPERM;
+		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)))
+		goto unlink_done;
+
+	if ((retval = affs_fixup(bh,inode)))
+		goto unlink_done;
+
+	inode->i_nlink=0;
+	inode->i_dirt=1;
+	inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
+	dir->i_version = ++event;
+	dir->i_dirt=1;
+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)
+{
+	struct inode	*inode;
+	int		 error;
+	
+	pr_debug("AFFS: create(%lu,\"%.*s\",0%o)\n",dir->i_ino,len,name,mode);
+
 
-	if (!S_ISDIR(dir->i_mode)) {
+	*result = NULL;
+
+	if (!dir || !dir->i_sb) {
 		iput(dir);
-		return -ENOENT;
+		return -EINVAL;
 	}
-	if (!(bh = affs_find_entry(dir, name, len, &ino))) {
-	  iput(dir);
-	  return -ENOENT;
+	inode = affs_new_inode(dir);
+	if (!inode) {
+		iput (dir);
+		return -ENOSPC;
 	}
-	brelse(bh);
-	if (!(*result = iget(dir->i_sb, ino))) {
+	inode->i_op   = &affs_file_inode_operations;
+	inode->i_mode = mode;
+	error         = affs_add_entry(dir,NULL,inode,name,len,ST_FILE);
+	if (error) {
 		iput(dir);
-		return -EACCES;
+		inode->i_nlink = 0;
+		inode->i_dirt  = 1;
+		iput(inode);
+		return -ENOSPC;
 	}
-	iput (dir);
+	inode->u.affs_i.i_protect = mode_to_prot(inode->i_mode);
+
+	iput(dir);
+	*result = inode;
+
 	return 0;
+}
 
-#if 0
-	ino = 0;
-	while(cache.lock);
-	cache.lock = 1;
-	if (dir->i_dev == cache.dev && 
-	    dir->i_ino == cache.dir &&
-	    len == cache.dlen && 
-	    affs_match(len, name, cache.filename, cache.dlen))
-	  {
-	    ino = cache.ino;
-	    ino_back = dir->i_ino;
-	    /* These two cases are special, but since they are at the start
-	       of the directory, we can just as easily search there */
-	    if (cache.dlen == 1 && cache.filename[0] == '.') ino = 0;
-	    if (cache.dlen == 2 && cache.filename[0] == '.' && 
-		cache.filename[1] == '.') ino = 0;
-	  };
-	cache.lock = 0;
-
-	if (!ino) {
-	  if (!(bh = affs_find_entry(dir,name,len, &ino, &ino_back))) {
-	    iput(dir);
-	    return -ENOENT;
-	  }
-	  brelse(bh);
-	};
+int
+affs_mkdir(struct inode *dir, const char *name, int len, 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);
 
-	if (!(*result = iget(dir->i_sb,ino))) {
+	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 -EACCES;
+		return -EEXIST;
 	}
+	inode = affs_new_inode(dir);
+	if (!inode) {
+		iput (dir);
+		return -ENOSPC;
+	}
+	inode->i_op = &affs_dir_inode_operations;
+	error       = affs_add_entry(dir,NULL,inode,name,len,ST_USERDIR);
+	if (error) {
+		iput(dir);
+		inode->i_nlink = 0;
+		inode->i_dirt  = 1;
+		iput(inode);
+		return error;
+	}
+	inode->i_mode = S_IFDIR | (mode & 0777 & ~current->fs->umask);
+	inode->u.affs_i.i_protect = mode_to_prot(inode->i_mode);
 
-	/* We need this backlink for the .. entry */
-	
-	if (ino_back) (*result)->u.affs_i.i_backlink = ino_back; 
+	iput(dir);
+	iput(inode);
+
+	return 0;
+}
+
+static int
+empty_dir(struct buffer_head *bh, int hashsize)
+{
+	while (--hashsize >= 0) {
+		if (((struct dir_front *)bh->b_data)->hashtable[hashsize])
+			return 0;
+	}
+	return 1;
+}
+
+int
+affs_rmdir(struct inode *dir, const char *name, int len)
+{
+	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);
+
+	inode  = NULL;
+	retval = -ENOENT;
+	if (!(bh = affs_find_entry(dir,name,len,&ino))) {
+		goto rmdir_done;
+	}
+	if (!(inode = iget(dir->i_sb,ino))) {
+		goto rmdir_done;
+	}
+	retval = -EPERM;
+        if (!fsuser() && current->fsuid != inode->i_uid &&
+            current->fsuid != dir->i_uid)
+		goto rmdir_done;
+	if (inode->i_dev != dir->i_dev)
+		goto rmdir_done;
+	if (inode == dir)	/* we may not delete ".", but "../dir" is ok */
+		goto rmdir_done;
+	if (!S_ISDIR(inode->i_mode)) {
+		retval = -ENOTDIR;
+		goto rmdir_done;
+	}
+	if (!empty_dir(bh,AFFS_I2HSIZE(inode))) {
+		retval = -ENOTEMPTY;
+		goto rmdir_done;
+	}
+	if (inode->i_count > 1) {
+		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)))
+		goto rmdir_done;
+
+	if ((retval = affs_fixup(bh,inode)))
+		goto rmdir_done;
+
+	inode->i_nlink=0;
+	inode->i_dirt=1;
+	inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
+	dir->i_version = ++event;
+	dir->i_dirt=1;
+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)
+{
+	struct buffer_head	*bh;
+	struct inode		*inode;
+	char			*p;
+	unsigned long		 tmp;
+	int			 i, maxlen;
+	char			 c, lc;
+
+	pr_debug("AFFS: symlink(%lu,\"%.*s\" -> \"%s\")\n",dir->i_ino,len,name,symname);
+	printk("AFFS: symlink(%lu,\"%.*s\" -> \"%s\")\n",dir->i_ino,len,name,symname);
 	
+	maxlen = 4 * AFFS_I2HSIZE(dir) - 1;
+	inode  = affs_new_inode(dir);
+	if (!inode) {
+		iput(dir);
+		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;
+		inode->i_dirt  = 1;
+		iput(inode);
+		return -EIO;
+	}
+	i  = 0;
+	p  = ((struct slink_front *)bh->b_data)->symname;
+	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 (i < maxlen && (c = *symname++)) {
+		if (c == '.' && lc == '/' && *symname == '.' && symname[1] == '/') {
+			*p++ = '/';
+			i++;
+			symname += 2;
+			lc = '/';
+		} else if (c == '.' && lc == '/' && *symname == '/') {
+			symname++;
+			lc = '/';
+		} else {
+			*p++ = c;
+			lc   = c;
+			i++;
+		}
+		if (lc == '/')
+			while (*symname == '/')
+				symname++;
+	}
+	*p = 0;
+	mark_buffer_dirty(bh,1);
+	affs_brelse(bh);
+	inode->i_dirt = 1;
+	bh = affs_find_entry(dir,name,len,&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);
+	if (i) {
+		inode->i_nlink = 0;
+		inode->i_dirt  = 1;
+		iput(inode);
+		affs_brelse(bh);
+		iput(dir);
+		return i;
+	}
 	iput(dir);
+	iput(inode);
+	
 	return 0;
-#endif
+}
+
+int
+affs_link(struct inode *oldinode, struct inode *dir, const char *name, int len)
+{
+	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);
+
+	bh = affs_find_entry(dir,name,len,&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) {
+			printk("AFFS: link(): original does not exist.\n");
+			iput(dir);
+			return -ENOENT;
+		}
+	}
+	inode = affs_new_inode(dir);
+	if (!inode) {
+		iput(oldinode);
+		iput(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_original = oldinode->i_ino;
+	inode->u.affs_i.i_hlink    = 1;
+
+	if (S_ISDIR(oldinode->i_mode))
+		error = affs_add_entry(dir,oldinode,inode,name,len,ST_LINKDIR);
+	else
+		error = affs_add_entry(dir,oldinode,inode,name,len,ST_LINKFILE);
+	if (error) {
+		inode->i_nlink = 0;
+		inode->i_dirt  = 1;
+	}
+	iput(dir);
+	iput(inode);
+	iput(oldinode);
+
+	return error;
+}
+
+static int
+subdir(struct inode * new_inode, struct inode * old_inode)
+{
+    int ino;
+    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;
+}
+
+/* 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)
+{
+	struct inode		*old_inode;
+	struct inode		*new_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);
+	
+	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;
+
+	old_bh = affs_find_entry(old_dir,old_name,old_len,&old_ino);
+	if (!old_bh)
+		goto end_rename;
+	old_inode = __iget(old_dir->i_sb,old_ino,0);
+	if (!old_inode)
+		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,0);
+		if (!new_inode) {		/* What does this mean? */
+			affs_brelse(new_bh);
+			new_bh = NULL;
+		}
+	}
+	if (new_inode == old_inode) {		/* Won't happen */
+		retval = 0;
+		goto end_rename;
+	}
+	if (new_inode && S_ISDIR(new_inode->i_mode)) {
+		retval = -EISDIR;
+		if (!S_ISDIR(old_inode->i_mode))
+			goto end_rename;
+		retval = -EINVAL;
+		if (subdir(new_dir,old_inode))
+			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)
+			goto end_rename;
+	}
+	if (S_ISDIR(old_inode->i_mode)) {
+		retval = -ENOTDIR;
+		if (new_inode && !S_ISDIR(new_inode->i_mode))
+			goto end_rename;
+		retval = -EINVAL;
+		if (subdir(new_dir,old_inode))
+			goto end_rename;
+		if (affs_parent_ino(old_inode) != old_dir->i_ino)
+			goto end_rename;
+	}
+	/* Unlink destination if existant */
+	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;
+		new_dir->i_dirt    = 1;
+		new_inode->i_nlink = 0;
+		new_inode->i_dirt  = 1;
+	}
+	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));
+
+	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;
+	new_dir->i_dirt    = 1;
+	old_dir->i_dirt    = 1;
+	mark_buffer_dirty(old_bh,1);
+	
+end_rename:
+	affs_brelse(old_bh);
+	affs_brelse(new_bh);
+	iput(new_inode);
+	iput(old_inode);
+	iput(old_dir);
+	iput(new_dir);
+
+	return retval;
+}
+
+int
+affs_fixup(struct buffer_head *bh, struct inode *inode)
+{
+	ULONG			 key, link_key;
+	LONG			 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) {
+			printk("AFFS: fixup(): hard link without original: ino=%lu\n",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))) {
+				printk("AFFS: fixup(): cannot read inode %u\n",key);
+				return -ENOENT;
+			}
+			if (!ofinode->u.affs_i.i_hlink) {
+				printk("AFFS: fixup(): first link to %lu (%u) is not a link?\n",
+					inode->i_ino,key);
+				iput(ofinode);
+				return -ENOENT;
+			}
+			if (!(nbh = affs_bread(inode->i_dev,key,AFFS_I2BSIZE(inode)))) {
+				printk("AFFS: fixup(): cannot read block %u\n",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;
+			ofinode->i_dirt              = 1;
+			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)
+						printk("AFFS: fixup() inode %u in link chain is "
+						       "not a link\n",key);
+					ofinode->u.affs_i.i_original = link_key;
+					ofinode->i_dirt              = 1;
+					FILE_END(nbh->b_data,inode)->original = htonl(link_key);
+				} else
+					printk("AFFS: fixup(): cannot get inode %u\n",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 {
+		printk("AFFS: fixup(): secondary type=%d\n",type);
+		return -EBADF;
+	}
 }

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov with Sam's (original) version
of this