patch-2.1.123 linux/fs/namei.c

Next file: linux/fs/nfs/dir.c
Previous file: linux/fs/msdos/namei.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.1.122/linux/fs/namei.c linux/fs/namei.c
@@ -280,7 +280,19 @@
 	return result;
 }
 
-static struct dentry * do_follow_link(struct dentry *base, struct dentry *dentry)
+/*
+ * The bitmask for a lookup event:
+ *  - follow links at the end
+ *  - require a directory
+ *  - ending slashes ok even for nonexistent files
+ *  - internal "there are more path compnents" flag
+ */
+#define LOOKUP_FOLLOW		(1)
+#define LOOKUP_DIRECTORY	(2)
+#define LOOKUP_SLASHOK		(4)
+#define LOOKUP_CONTINUE		(8)
+
+static struct dentry * do_follow_link(struct dentry *base, struct dentry *dentry, unsigned int follow)
 {
 	struct inode * inode = dentry->d_inode;
 
@@ -290,7 +302,7 @@
 
 			current->link_count++;
 			/* This eats the base */
-			result = inode->i_op->follow_link(dentry, base);
+			result = inode->i_op->follow_link(dentry, base, follow);
 			current->link_count--;
 			dput(dentry);
 			return result;
@@ -320,9 +332,10 @@
  * This is the basic name resolution function, turning a pathname
  * into the final dentry.
  */
-struct dentry * lookup_dentry(const char * name, struct dentry * base, int follow_link)
+struct dentry * lookup_dentry(const char * name, struct dentry * base, unsigned int lookup_flags)
 {
 	struct dentry * dentry;
+	struct inode *inode;
 
 	if (*name == '/') {
 		if (base)
@@ -338,22 +351,16 @@
 	if (!*name)
 		goto return_base;
 
+	inode = base->d_inode;
+	lookup_flags &= LOOKUP_FOLLOW | LOOKUP_DIRECTORY | LOOKUP_SLASHOK;
+
 	/* At this point we know we have a real path component. */
 	for(;;) {
 		int err;
 		unsigned long hash;
 		struct qstr this;
-		struct inode *inode;
-		char c, follow;
-
-		dentry = ERR_PTR(-ENOENT);
-		inode = base->d_inode;
-		if (!inode)
-			break;
-
-		dentry = ERR_PTR(-ENOTDIR);
-		if (!inode->i_op || !inode->i_op->lookup)
-			break;
+		unsigned int flags;
+		unsigned int c;
 
 		err = permission(inode, MAY_EXEC);
 		dentry = ERR_PTR(err);
@@ -361,23 +368,28 @@
 			break;
 
 		this.name = name;
-		c = *name;
+		c = *(const unsigned char *)name;
 
 		hash = init_name_hash();
 		do {
+			name++;
 			hash = partial_name_hash(c, hash);
-			c = *++name;
+			c = *(const unsigned char *)name;
 		} while (c && (c != '/'));
 		this.len = name - (const char *) this.name;
 		this.hash = end_name_hash(hash);
 
 		/* remove trailing slashes? */
-		follow = follow_link;
+		flags = lookup_flags;
 		if (c) {
-			follow |= c;
+			char tmp;
+
+			flags |= LOOKUP_FOLLOW | LOOKUP_DIRECTORY;
 			do {
-				c = *++name;
-			} while (c == '/');
+				tmp = *++name;
+			} while (tmp == '/');
+			if (tmp)
+				flags |= LOOKUP_CONTINUE;
 		}
 
 		/*
@@ -407,15 +419,47 @@
 		/* Check mountpoints.. */
 		dentry = follow_mount(dentry);
 
-		if (!follow)
+		if (!(flags & LOOKUP_FOLLOW))
 			break;
 
-		base = do_follow_link(base, dentry);
-		if (c && !IS_ERR(base))
-			continue;
+		base = do_follow_link(base, dentry, flags);
+		if (IS_ERR(base))
+			goto return_base;
 
+		inode = base->d_inode;
+		if (flags & LOOKUP_DIRECTORY) {
+			if (!inode)
+				goto no_inode;
+			dentry = ERR_PTR(-ENOTDIR); 
+			if (!inode->i_op || !inode->i_op->lookup)
+				break;
+			if (flags & LOOKUP_CONTINUE)
+				continue;
+		}
 return_base:
 		return base;
+/*
+ * The case of a nonexisting file is special.
+ *
+ * In the middle of a pathname lookup (ie when
+ * LOOKUP_CONTINUE is set), it's an obvious
+ * error and returns ENOENT.
+ *
+ * At the end of a pathname lookup it's legal,
+ * and we return a negative dentry. However, we
+ * get here only if there were trailing slashes,
+ * which is legal only if we know it's supposed
+ * to be a directory (ie "mkdir"). Thus the
+ * LOOKUP_SLASHOK flag.
+ */
+no_inode:
+		dentry = ERR_PTR(-ENOENT);
+		if (flags & LOOKUP_CONTINUE)
+			break;
+		if (flags & LOOKUP_SLASHOK)
+			goto return_base;
+		dentry = ERR_PTR(-ENOTDIR);
+		break;
 	}
 	dput(base);
 	return dentry;
@@ -431,7 +475,7 @@
  * namei exists in two versions: namei/lnamei. The only difference is
  * that namei follows links, while lnamei does not.
  */
-struct dentry * __namei(const char *pathname, int follow_link)
+struct dentry * __namei(const char *pathname, unsigned int lookup_flags)
 {
 	char *name;
 	struct dentry *dentry;
@@ -439,7 +483,7 @@
 	name = getname(pathname);
 	dentry = (struct dentry *) name;
 	if (!IS_ERR(name)) {
-		dentry = lookup_dentry(name, NULL, follow_link);
+		dentry = lookup_dentry(name, NULL, lookup_flags);
 		putname(name);
 		if (!IS_ERR(dentry)) {
 			if (!dentry->d_inode) {
@@ -637,7 +681,7 @@
 	struct dentry *dentry, *retval;
 
 	mode &= ~current->fs->umask;
-	dentry = lookup_dentry(filename, NULL, 1);
+	dentry = lookup_dentry(filename, NULL, LOOKUP_FOLLOW);
 	if (IS_ERR(dentry))
 		return dentry;
 
@@ -721,7 +765,7 @@
 	struct dentry *dir;
 	struct dentry *dentry;
 
-	dentry = lookup_dentry(pathname, NULL, 0);
+	dentry = lookup_dentry(pathname, NULL, LOOKUP_SLASHOK);
 	error = PTR_ERR(dentry);
 	if (IS_ERR(dentry))
 		goto exit;
@@ -1128,7 +1172,16 @@
 	if (IS_ERR(old_dentry))
 		goto exit;
 
-	new_dentry = lookup_dentry(newname, NULL, 0);
+	error = -ENOENT;
+	if (!old_dentry->d_inode)
+		goto exit;
+
+	{
+		unsigned int flags = 0;
+		if (S_ISDIR(old_dentry->d_inode->i_mode))
+			flags = LOOKUP_SLASHOK;
+		new_dentry = lookup_dentry(newname, NULL, flags);
+	}
 
 	error = PTR_ERR(new_dentry);
 	if (IS_ERR(new_dentry))
@@ -1138,10 +1191,6 @@
 	old_dir = get_parent(old_dentry);
 
 	double_lock(new_dir, old_dir);
-
-	error = -ENOENT;
-	if (!old_dentry->d_inode)
-		goto exit_lock;
 
 	error = permission(old_dir->d_inode,MAY_WRITE | MAY_EXEC);
 	if (error)

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