patch-2.1.89 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.88/linux/fs/affs/namei.c linux/fs/affs/namei.c
@@ -21,23 +21,17 @@
 
 #include <linux/errno.h>
 
-/* Simple toupper()/tolower() for DOS\1 */
+/* Simple toupper() for DOS\1 */
 
-static inline unsigned int
+static unsigned int
 affs_toupper(unsigned int ch)
 {
 	return ch >= 'a' && ch <= 'z' ? ch -= ('a' - 'A') : ch;
 }
 
-static inline unsigned int
-affs_tolower(unsigned int ch)
-{
-	return ch >= 'A' && ch <= 'Z' ? ch + ('a' - 'A') : ch;
-}
+/* International toupper() for DOS\3 ("international") */
 
-/* International toupper()/tolower() for DOS\3 ("international") */
-
-static inline unsigned int
+static unsigned int
 affs_intl_toupper(unsigned int ch)
 {
 	return (ch >= 'a' && ch <= 'z') || (ch >= 0xE0
@@ -45,23 +39,8 @@
 		ch - ('a' - 'A') : ch;
 }
 
-static inline unsigned int
-affs_intl_tolower(unsigned int ch)
-{
-	return (ch >= 'A' && ch <= 'Z') || (ch >= 0xC0
-		&& ch <= 0xDE && ch != 0xD7) ?
-		ch + ('a' - 'A') : ch;
-}
-
-/* We need 2 sets of dentry operations, since we cannot
- * determine the fs flavour in the callback routines.
- */
-
 static int	 affs_hash_dentry(struct dentry *, struct qstr *);
 static int       affs_compare_dentry(struct dentry *, struct qstr *, struct qstr *);
-static int	 affs_hash_dentry_intl(struct dentry *, struct qstr *);
-static int       affs_compare_dentry_intl(struct dentry *, struct qstr *, struct qstr *);
-
 struct dentry_operations affs_dentry_operations = {
 	NULL,			/* d_validate	*/
 	affs_hash_dentry,	/* d_hash	*/
@@ -69,24 +48,25 @@
 	NULL			/* d_delete	*/
 };
 
-struct dentry_operations affs_dentry_operations_intl = {
-	NULL,				/* d_validate	*/
-	affs_hash_dentry_intl,		/* d_hash	*/
-	affs_compare_dentry_intl,	/* d_compare	*/
-	NULL				/* d_delete	*/
-};
-
+/*
+ * Note: the dentry argument is the parent dentry.
+ */
 static int
 affs_hash_dentry(struct dentry *dentry, struct qstr *qstr)
 {
+	unsigned int (*toupper)(unsigned int) = affs_toupper;
 	unsigned long	 hash;
 	int		 i;
 
 	if ((i = affs_check_name(qstr->name,qstr->len)))
 		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(affs_tolower(qstr->name[i]),hash);
+		hash = partial_name_hash(toupper(qstr->name[i]), hash);
 	qstr->hash = end_name_hash(hash);
 
 	return 0;
@@ -95,6 +75,9 @@
 static int
 affs_compare_dentry(struct dentry *dentry, struct qstr *a, struct qstr *b)
 {
+	unsigned int (*toupper)(unsigned int) = affs_toupper;
+	int	 alen = a->len;
+	int      blen = b->len;
 	int	 i;
 
 	/* 'a' is the qstr of an already existing dentry, so the name
@@ -107,45 +90,19 @@
 	/* If the names are longer than the allowed 30 chars,
 	 * the excess is ignored, so their length may differ.
 	 */
-
-	if ((a->len < 30 || b->len < 30) && a->len != b->len)
+	if (alen > 30)
+		alen = 30;
+	if (blen > 30)
+		blen = 30;
+	if (alen != blen)
 		return 1;
 
-	for (i = 0; i < a->len && i < 30; i++)
-		if (affs_tolower(a->name[i]) != affs_tolower(b->name[i]))
-			return 1;
-	
-	return 0;
-}
-
-static int
-affs_hash_dentry_intl(struct dentry *dentry, struct qstr *qstr)
-{
-	unsigned long	 hash;
-	int		 i;
+	/* Check whether to use the international 'toupper' routine */
+	if (AFFS_I2FSTYPE(dentry->d_inode))
+		toupper = affs_intl_toupper;
 
-	if ((i = affs_check_name(qstr->name,qstr->len)))
-		return i;
-	hash = init_name_hash();
-	for (i = 0; i < qstr->len && i < 30; i++)
-		hash = partial_name_hash(affs_intl_tolower(qstr->name[i]),hash);
-	qstr->hash = end_name_hash(hash);
-
-	return 0;
-}
-
-static int
-affs_compare_dentry_intl(struct dentry *dentry, struct qstr *a, struct qstr *b)
-{
-	int	 i;
-
-	if (affs_check_name(b->name,b->len))
-		return 1;
-	if ((a->len < 30 || b->len < 30) && a->len != b->len)
-		return 1;
-
-	for (i = 0; i < a->len && i < 30; i++)
-		if (affs_intl_tolower(a->name[i]) != affs_intl_tolower(b->name[i]))
+	for (i = 0; i < alen; i++)
+		if (toupper(a->name[i]) != toupper(b->name[i]))
 			return 1;
 	
 	return 0;
@@ -158,6 +115,9 @@
 static int
 affs_match(const unsigned char *name, int len, const unsigned char *compare, int dlen, int intl)
 {
+	unsigned int	(*toupper)(unsigned int) = intl ? affs_intl_toupper : affs_toupper;
+	int		  i;
+
 	if (!compare)
 		return 0;
 
@@ -171,21 +131,9 @@
 		return 1;
 	if (dlen != len)
 		return 0;
-	if (intl) {
-		while (dlen--) {
-			if (affs_intl_toupper(*name) != affs_intl_toupper(*compare))
-				return 0;
-			name++;
-			compare++;
-		}
-	} else {
-		while (dlen--) {
-			if (affs_toupper(*name) != affs_toupper(*compare))
-				return 0;
-			name++;
-			compare++;
-		}
-	}
+	for (i = 0; i < len; i++)
+		if (toupper(name[i]) != toupper(compare[i]))
+			return 0;
 	return 1;
 }
 
@@ -211,23 +159,22 @@
 affs_find_entry(struct inode *dir, struct dentry *dentry, unsigned long *ino)
 {
 	struct buffer_head	*bh;
-	int			 intl;
+	int			 intl = AFFS_I2FSTYPE(dir);
 	s32			 key;
 	const char		*name = dentry->d_name.name;
 	int			 namelen = dentry->d_name.len;
 
 	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));
+	bh = affs_bread(dir->i_dev,dir->i_ino,AFFS_I2BSIZE(dir));
 	if (!bh)
 		return NULL;
 
-	if (affs_match(name,namelen,".",1,intl)) {
+	if (namelen == 1 && name[0] == '.') {
 		*ino = dir->i_ino;
 		return bh;
 	}
-	if (affs_match(name,namelen,"..",2,intl)) {
+	if (namelen == 2 && name[0] == '.' && name[1] == '.') {
 		*ino = affs_parent_ino(dir);
 		return bh;
 	}
@@ -239,10 +186,9 @@
 		int cnamelen;
 
 		affs_brelse(bh);
-		if (key == 0) {
-			bh = NULL;
+		bh = NULL;
+		if (key == 0)
 			break;
-		}
 		bh = affs_bread(dir->i_dev,key,AFFS_I2BSIZE(dir));
 		if (!bh)
 			break;
@@ -274,8 +220,7 @@
 		if (!inode)
 			return -EACCES;
 	}
-	dentry->d_op = AFFS_I2FSTYPE(dir) ? &affs_dentry_operations_intl
-					  : &affs_dentry_operations;
+	dentry->d_op = &affs_dentry_operations;
 	d_add(dentry,inode);
 	return 0;
 }
@@ -291,10 +236,7 @@
 	pr_debug("AFFS: unlink(dir=%ld,\"%.*s\")\n",dir->i_ino,
 		 (int)dentry->d_name.len,dentry->d_name.name);
 
-	bh      = NULL;
 	retval  = -ENOENT;
-	if (!dir)
-		goto unlink_done;
 	if (!(bh = affs_find_entry(dir,dentry,&ino)))
 		goto unlink_done;
 
@@ -312,8 +254,10 @@
 	inode->i_nlink = retval;
 	inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
 	mark_inode_dirty(inode);
-	retval = 0;
 	d_delete(dentry);
+	mark_inode_dirty(dir);
+	retval = 0;
+
 unlink_done:
 	affs_brelse(bh);
 	return retval;
@@ -328,11 +272,10 @@
 	pr_debug("AFFS: create(%lu,\"%.*s\",0%o)\n",dir->i_ino,(int)dentry->d_name.len,
 		 dentry->d_name.name,mode);
 
-	if (!dir)
-		return -ENOENT;
+	error = -ENOSPC;
 	inode = affs_new_inode(dir);
 	if (!inode)
-		return -ENOSPC;
+		goto out;
 
 	pr_debug(" -- ino=%lu\n",inode->i_ino);
 	if (dir->i_sb->u.affs_sb.s_flags & SF_OFS)
@@ -341,19 +284,22 @@
 		inode->i_op = &affs_file_inode_operations;
 
 	error = affs_add_entry(dir,NULL,inode,dentry,ST_FILE);
-	if (error) {
-		inode->i_nlink = 0;
-		mark_inode_dirty(inode);
-		iput(inode);
-		return error;
-	}
+	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);
+	mark_inode_dirty(inode);
 	dir->i_version = ++event;
 	mark_inode_dirty(dir);
-	d_instantiate(dentry,inode);
+out:
+	return error;
 
-	return 0;
+out_iput:
+	inode->i_nlink = 0;
+	mark_inode_dirty(inode);
+	iput(inode);
+	goto out;
 }
 
 int
@@ -365,25 +311,29 @@
 	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)
-		return -ENOSPC;
+		goto out;
 
 	inode->i_op = &affs_dir_inode_operations;
 	error       = affs_add_entry(dir,NULL,inode,dentry,ST_USERDIR);
-	if (error) {
-		inode->i_nlink = 0;
-		mark_inode_dirty(inode);
-		iput(inode);
-		return error;
-	}
+	if (error)
+		goto out_iput;
 	inode->i_mode = S_IFDIR | (mode & 0777 & ~current->fs->umask);
 	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);
-	d_instantiate(dentry,inode);
+out:
+	return error;
 
-	return 0;
+out_iput:
+	inode->i_nlink = 0;
+	mark_inode_dirty(inode);
+	iput(inode);
+	goto out;
 }
 
 static int
@@ -399,24 +349,18 @@
 int
 affs_rmdir(struct inode *dir, struct dentry *dentry)
 {
+	struct inode		*inode = dentry->d_inode;
 	int			 retval;
 	unsigned long		 ino;
-	struct inode		*inode;
 	struct buffer_head	*bh;
 
 	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 (!dir)
-		goto rmdir_done;
 	if (!(bh = affs_find_entry(dir,dentry,&ino)))
 		goto rmdir_done;
 
-	inode = dentry->d_inode;
-
 	retval = -EPERM;
         if (current->fsuid != inode->i_uid &&
             current->fsuid != dir->i_uid && !fsuser())
@@ -425,31 +369,31 @@
 		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;
+	retval = -ENOTDIR;
+	if (!S_ISDIR(inode->i_mode))
 		goto rmdir_done;
-	}
-	down(&inode->i_sem);
-	if (dentry->d_count > 1) {
+	/*
+	 * Make sure the directory is empty and the dentry isn't busy.
+	 */
+	if (dentry->d_count > 1)
 		shrink_dcache_parent(dentry);
-	}
-	up(&inode->i_sem);
-	if (!empty_dir(bh,AFFS_I2HSIZE(inode))) {
-		retval = -ENOTEMPTY;
+	retval = -ENOTEMPTY;
+	if (!empty_dir(bh,AFFS_I2HSIZE(inode)))
 		goto rmdir_done;
-	}
-	if (inode->i_count > 1) {
-		retval = -EBUSY;
+	retval = -EBUSY;
+	if (dentry->d_count > 1)
 		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;
+	mark_inode_dirty(dir);
 	mark_inode_dirty(inode);
 	d_delete(dentry);
+
 rmdir_done:
 	affs_brelse(bh);
 	return retval;
@@ -462,27 +406,25 @@
 	struct inode		*inode;
 	char			*p;
 	unsigned long		 tmp;
-	int			 i, maxlen;
+	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;
+	error = -ENOSPC;
 	inode  = affs_new_inode(dir);
 	if (!inode)
-		return -ENOSPC;
+		goto out;
 
 	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);
+	error = -EIO;
 	bh = affs_bread(inode->i_dev,inode->i_ino,AFFS_I2BSIZE(inode));
-	if (!bh) {
-		inode->i_nlink = 0;
-		mark_inode_dirty(inode);
-		iput(inode);
-		return -EIO;
-	}
+	if (!bh)
+		goto out_iput;
 	i  = 0;
 	p  = ((struct slink_front *)bh->b_data)->symname;
 	lc = '/';
@@ -514,25 +456,30 @@
 	mark_buffer_dirty(bh,1);
 	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) {
-		inode->i_nlink = 0;
-		iput(inode);
-		affs_brelse(bh);
-		return -EEXIST;
-	}
-	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);
-		return i;
-	}
-	dir->i_version = ++event;
+	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);
+	if (error)
+		goto out_release;
 	d_instantiate(dentry,inode);
-	
-	return 0;
+	dir->i_version = ++event;
+	mark_inode_dirty(dir);
+
+out:
+	return error;
+
+out_release:
+	affs_brelse(bh);
+out_iput:
+	inode->i_nlink = 0;
+	mark_inode_dirty(inode);
+	iput(inode);
+	goto out;
 }
 
 int
@@ -547,6 +494,7 @@
 	pr_debug("AFFS: link(%lu,%lu,\"%.*s\")\n",oldinode->i_ino,dir->i_ino,
 		 (int)dentry->d_name.len,dentry->d_name.name);
 
+	/* N.B. Do we need this test? The dentry must be negative ... */
 	bh = affs_find_entry(dir,dentry,&i);
 	if (bh) {
 		affs_brelse(bh);
@@ -556,8 +504,9 @@
 		affs_warning(dir->i_sb,"link","Impossible link to link");
 		return -EINVAL;
 	}
+	error = -ENOSPC;
 	if (!(inode = affs_new_inode(dir)))
-		return -ENOSPC;
+		goto out;
 
 	inode->i_op                = oldinode->i_op;
 	inode->u.affs_i.i_protect  = mode_to_prot(oldinode->i_mode);
@@ -573,6 +522,7 @@
 		inode->i_nlink = 0;
 	else {
 		dir->i_version = ++event;
+		mark_inode_dirty(dir);
 		mark_inode_dirty(oldinode);
 		oldinode->i_count++;
 		d_instantiate(dentry,oldinode);
@@ -580,6 +530,7 @@
 	mark_inode_dirty(inode);
 	iput(inode);
 
+out:
 	return error;
 }
 
@@ -600,7 +551,7 @@
 		 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)))
-		return retval;
+		goto out;
 
 	new_bh = NULL;
 	retval = -ENOENT;
@@ -630,8 +581,10 @@
 		if (!S_ISDIR(old_inode->i_mode))
 			goto end_rename;
 		retval = -EINVAL;
-		if (is_subdir(new_dentry,old_dentry))
+		if (is_subdir(new_dentry, old_dentry))
 			goto end_rename;
+		if (new_dentry->d_count > 1)
+			shrink_dcache_parent(new_dentry);
 		retval = -ENOTEMPTY;
 		if (!empty_dir(new_bh,AFFS_I2HSIZE(new_inode)))
 			goto end_rename;
@@ -644,7 +597,7 @@
 		if (new_inode && !S_ISDIR(new_inode->i_mode))
 			goto end_rename;
 		retval = -EINVAL;
-		if (is_subdir(new_dentry,old_dentry))
+		if (is_subdir(new_dentry, old_dentry))
 			goto end_rename;
 		if (affs_parent_ino(old_inode) != old_dir->i_ino)
 			goto end_rename;
@@ -681,6 +634,6 @@
 end_rename:
 	affs_brelse(old_bh);
 	affs_brelse(new_bh);
-
+out:
 	return retval;
 }

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