patch-2.3.99-pre6 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.3.99-pre5/linux/fs/namei.c linux/fs/namei.c
@@ -286,11 +286,12 @@
  *
  * We expect 'base' to be positive and a directory.
  */
-int walk_name(const char * name, unsigned lookup_flags, struct nameidata *nd)
+int walk_name(const char * name, struct nameidata *nd)
 {
 	struct dentry *dentry;
 	struct inode *inode;
 	int err;
+	unsigned int lookup_flags = nd->flags;
 
 	while (*name=='/')
 		name++;
@@ -301,9 +302,6 @@
 	if (current->link_count)
 		lookup_flags = LOOKUP_FOLLOW;
 
-	lookup_flags &= LOOKUP_FOLLOW | LOOKUP_DIRECTORY |
-			LOOKUP_SLASHOK | LOOKUP_POSITIVE | LOOKUP_PARENT;
-
 	/* At this point we know we have a real path component. */
 	for(;;) {
 		unsigned long hash;
@@ -379,10 +377,10 @@
 		err = -ENOENT;
 		inode = dentry->d_inode;
 		if (!inode)
-			break;
+			goto out_dput;
 		err = -ENOTDIR; 
 		if (!inode->i_op)
-			break;
+			goto out_dput;
 
 		if (inode->i_op->follow_link) {
 			err = do_follow_link(dentry, nd);
@@ -464,16 +462,23 @@
 		goto return_base;
 no_inode:
 		err = -ENOENT;
-		if (lookup_flags & LOOKUP_POSITIVE)
+		if (lookup_flags & (LOOKUP_POSITIVE|LOOKUP_DIRECTORY))
 			break;
-		if (lookup_flags & LOOKUP_DIRECTORY)
-			if (!(lookup_flags & LOOKUP_SLASHOK))
-				break;
 		goto return_base;
 lookup_parent:
 		nd->last = this;
+		nd->last_type = LAST_NORM;
+		if (this.name[0] != '.')
+			goto return_base;
+		if (this.len == 1)
+			nd->last_type = LAST_DOT;
+		else if (this.len == 2 && this.name[1] == '.')
+			nd->last_type = LAST_DOTDOT;
 return_base:
 		return 0;
+out_dput:
+		dput(dentry);
+		break;
 	}
 	dput(nd->dentry);
 	mntput(nd->mnt);
@@ -482,27 +487,20 @@
 }
 
 /* returns 1 if everything is done */
-static int __emul_lookup_dentry(const char *name, int lookup_flags,
-		struct nameidata *nd)
+static int __emul_lookup_dentry(const char *name, struct nameidata *nd)
 {
-	char *emul = __emul_prefix();
-
-	if (!emul)
-		return 0;
-
-	nd->mnt = mntget(current->fs->rootmnt);
-	nd->dentry = dget(current->fs->root);
-	if (walk_name(emul,LOOKUP_FOLLOW|LOOKUP_DIRECTORY|LOOKUP_POSITIVE,nd))
-		return 0;
-	if (walk_name(name, lookup_flags, nd))
+	nd->mnt = mntget(current->fs->altrootmnt);
+	nd->dentry = dget(current->fs->altroot);
+	if (walk_name(name, nd))
 		return 0;
 
 	if (!nd->dentry->d_inode) {
 		struct nameidata nd_root;
-		nd_root.last.len = 0;
+		nd_root.last_type = LAST_ROOT;
+		nd_root.flags = nd->flags;
 		nd_root.mnt = mntget(current->fs->rootmnt);
 		nd_root.dentry = dget(current->fs->root);
-		if (walk_name(name, lookup_flags, &nd_root))
+		if (walk_name(name, &nd_root))
 			return 1;
 		if (nd_root.dentry->d_inode) {
 			dput(nd->dentry);
@@ -518,11 +516,36 @@
 	return 1;
 }
 
+void set_fs_altroot(void)
+{
+	char *emul = __emul_prefix();
+	struct nameidata nd;
+	struct vfsmount *mnt = NULL, *oldmnt;
+	struct dentry *dentry = NULL, *olddentry;
+	if (emul) {
+		nd.mnt = mntget(current->fs->rootmnt);
+		nd.dentry = dget(current->fs->root);
+		nd.flags = LOOKUP_FOLLOW|LOOKUP_DIRECTORY|LOOKUP_POSITIVE;
+		if (walk_name(emul,&nd) == 0) {
+			mnt = nd.mnt;
+			dentry = nd.dentry;
+		}
+	}
+	oldmnt = current->fs->altrootmnt;
+	olddentry = current->fs->altroot;
+	current->fs->altrootmnt = mnt;
+	current->fs->altroot = dentry;
+	if (olddentry) {
+		dput(olddentry);
+		mntput(oldmnt);
+	}
+}
+
 static inline int
-walk_init_root(const char *name, unsigned flags, struct nameidata *nd)
+walk_init_root(const char *name, struct nameidata *nd)
 {
-	if (current->personality != PER_LINUX)
-		if (__emul_lookup_dentry(name,flags,nd))
+	if (current->fs->altroot && !(nd->flags & LOOKUP_NOALT))
+		if (__emul_lookup_dentry(name,nd))
 			return 0;
 	nd->mnt = mntget(current->fs->rootmnt);
 	nd->dentry = dget(current->fs->root);
@@ -531,9 +554,10 @@
 
 int walk_init(const char *name,unsigned int flags,struct nameidata *nd)
 {
-	nd->last.len = 0;
+	nd->last_type = LAST_ROOT; /* if there are only slashes... */
+	nd->flags = flags;
 	if (*name=='/')
-		return walk_init_root(name,flags,nd);
+		return walk_init_root(name,nd);
 	nd->mnt = mntget(current->fs->pwdmnt);
 	nd->dentry = dget(current->fs->pwd);
 	return 1;
@@ -545,7 +569,7 @@
 	int err = 0;
 
 	if (walk_init(name, lookup_flags, &nd))
-		err = walk_name(name, lookup_flags, &nd);
+		err = walk_name(name, &nd);
 	if (!err) {
 		mntput(nd.mnt);
 		return nd.dentry;
@@ -589,15 +613,10 @@
 		dentry = inode->i_op->lookup(inode, new);
 		if (!dentry)
 			dentry = new;
-		else {
+		else
 			dput(new);
-			if (IS_ERR(dentry))
-				goto out;
-		}
 	}
-
 out:
-	dput(base);
 	return dentry;
 }
 
@@ -689,6 +708,8 @@
 	int error;
 	if (!victim->d_inode || victim->d_parent->d_inode != dir)
 		return -ENOENT;
+	if (IS_DEADDIR(dir))
+		return -ENOENT;
 	error = permission(dir,MAY_WRITE | MAY_EXEC);
 	if (error)
 		return error;
@@ -720,6 +741,8 @@
 static inline int may_create(struct inode *dir, struct dentry *child) {
 	if (child->d_inode)
 		return -EEXIST;
+	if (IS_DEADDIR(dir))
+		return -ENOENT;
 	return permission(dir,MAY_WRITE | MAY_EXEC);
 }
 
@@ -790,7 +813,7 @@
 	acc_mode = ACC_MODE(flag);
 	if (!(flag & O_CREAT)) {
 		if (walk_init(pathname, lookup_flags(flag), nd))
-			error = walk_name(pathname, lookup_flags(flag), nd);
+			error = walk_name(pathname, nd);
 		if (error)
 			return error;
 
@@ -799,7 +822,7 @@
 		struct dentry *dir;
 
 		if (walk_init(pathname, LOOKUP_PARENT, nd))
-			error = walk_name(pathname, LOOKUP_PARENT, nd);
+			error = walk_name(pathname, nd);
 		if (error)
 			return error;
 		/*
@@ -810,9 +833,7 @@
 		 * luserdom and let him sod off - -EISDIR it is.
 		 */
 		error = -EISDIR;
-		if (!nd->last.len || (nd->last.name[0] == '.' &&
-		     (nd->last.len == 1 ||
-		      (nd->last.name[1] == '.' && nd->last.len == 2))))
+		if (nd->last_type != LAST_NORM)
 			goto exit;
 		/* same for foo/ */
 		if (nd->last.name[nd->last.len])
@@ -821,7 +842,7 @@
 		dir = dget(nd->dentry);
 		down(&dir->d_inode->i_sem);
 
-		dentry = lookup_hash(&nd->last, dget(nd->dentry));
+		dentry = lookup_hash(&nd->last, nd->dentry);
 		error = PTR_ERR(dentry);
 		if (IS_ERR(dentry)) {
 			up(&dir->d_inode->i_sem);
@@ -834,7 +855,7 @@
 			dput(dir);
 			error = -EEXIST;
 			if (flag & O_EXCL)
-				goto exit;
+				goto exit_dput;
 			if (dentry->d_inode->i_op &&
 			    dentry->d_inode->i_op->follow_link) {
 				/*
@@ -936,6 +957,8 @@
 
 	return 0;
 
+exit_dput:
+	dput(dentry);
 exit:
 	dput(nd->dentry);
 	mntput(nd->mnt);
@@ -948,16 +971,15 @@
 	struct dentry *dentry;
 	int err = 0;
 	if (walk_init(name, LOOKUP_PARENT, &nd))
-		err = walk_name(name, LOOKUP_PARENT, &nd);
+		err = walk_name(name, &nd);
 	dentry = ERR_PTR(err);
 	if (err)
 		goto out;
 	down(&nd.dentry->d_inode->i_sem);
 	dentry = ERR_PTR(-EEXIST);
-	if (!nd.last.len || (nd.last.name[0] == '.' &&
-	      (nd.last.len == 1 || (nd.last.name[1] == '.' && nd.last.len == 2))))
+	if (nd.last_type != LAST_NORM)
 		goto fail;
-	dentry = lookup_hash(&nd.last, dget(nd.dentry));
+	dentry = lookup_hash(&nd.last, nd.dentry);
 	if (IS_ERR(dentry))
 		goto fail;
 	if (!is_dir && nd.last.name[nd.last.len] && !dentry->d_inode)
@@ -1155,47 +1177,53 @@
 	double_down(&dir->i_zombie, &dentry->d_inode->i_zombie);
 	d_unhash(dentry);
 	error = dir->i_op->rmdir(dir, dentry);
+	if (!error)
+		dentry->d_inode->i_flags |= S_DEAD;
 	double_up(&dir->i_zombie, &dentry->d_inode->i_zombie);
 	dput(dentry);
 
 	return error;
 }
 
-static inline int do_rmdir(const char * name)
+asmlinkage long sys_rmdir(const char * pathname)
 {
-	int error;
-	struct dentry *dir;
+	int error = 0;
+	char * name;
 	struct dentry *dentry;
+	struct nameidata nd;
 
-	dentry = lookup_dentry(name, LOOKUP_POSITIVE);
-	error = PTR_ERR(dentry);
-	if (IS_ERR(dentry))
+	name = getname(pathname);
+	if(IS_ERR(name))
+		return PTR_ERR(name);
+	lock_kernel();
+
+	if (walk_init(name, LOOKUP_PARENT, &nd))
+		error = walk_name(name, &nd);
+	if (error)
 		goto exit;
 
-	dir = lock_parent(dentry);
-	error = -ENOENT;
-	if (check_parent(dir, dentry))
-		error = vfs_rmdir(dir->d_inode, dentry);
-	unlock_dir(dir);
-	dput(dentry);
+	switch(nd.last_type) {
+		case LAST_DOTDOT:
+			error = -ENOTEMPTY;
+			goto exit1;
+		case LAST_ROOT: case LAST_DOT:
+			error = -EBUSY;
+			goto exit1;
+	}
+	down(&nd.dentry->d_inode->i_sem);
+	dentry = lookup_hash(&nd.last, nd.dentry);
+	error = PTR_ERR(dentry);
+	if (!IS_ERR(dentry)) {
+		error = vfs_rmdir(nd.dentry->d_inode, dentry);
+		dput(dentry);
+	}
+	up(&nd.dentry->d_inode->i_sem);
+exit1:
+	dput(nd.dentry);
+	mntput(nd.mnt);
 exit:
-	return error;
-}
-
-asmlinkage long sys_rmdir(const char * pathname)
-{
-	int error;
-	char * tmp;
-
-	tmp = getname(pathname);
-	if(IS_ERR(tmp))
-		return PTR_ERR(tmp);
-	lock_kernel();
-	error = do_rmdir(tmp);
 	unlock_kernel();
-
-	putname(tmp);
-
+	putname(name);
 	return error;
 }
 
@@ -1216,42 +1244,50 @@
 	return error;
 }
 
-static int do_unlink(const char * name)
+asmlinkage long sys_unlink(const char * pathname)
 {
-	int error;
-	struct dentry *dir;
+	int error = 0;
+	char * name;
 	struct dentry *dentry;
+	struct nameidata nd;
 
-	dentry = lookup_dentry(name, LOOKUP_POSITIVE);
-	error = PTR_ERR(dentry);
-	if (IS_ERR(dentry))
-		goto exit;
-
-	dir = lock_parent(dentry);
-	error = -ENOENT;
-	if (check_parent(dir, dentry))
-		error = vfs_unlink(dir->d_inode, dentry);
+	name = getname(pathname);
+	if(IS_ERR(name))
+		return PTR_ERR(name);
+	lock_kernel();
 
-        unlock_dir(dir);
-	dput(dentry);
+	if (walk_init(name, LOOKUP_PARENT, &nd))
+		error = walk_name(name, &nd);
+	if (error)
+		goto exit;
+	error = -EISDIR;
+	if (nd.last_type != LAST_NORM)
+		goto exit1;
+	down(&nd.dentry->d_inode->i_sem);
+	dentry = lookup_hash(&nd.last, nd.dentry);
+	error = PTR_ERR(dentry);
+	if (!IS_ERR(dentry)) {
+		/* Why not before? Because we want correct error value */
+		if (nd.last.name[nd.last.len])
+			goto slashes;
+		error = vfs_unlink(nd.dentry->d_inode, dentry);
+	exit2:
+		dput(dentry);
+	}
+	up(&nd.dentry->d_inode->i_sem);
+exit1:
+	dput(nd.dentry);
+	mntput(nd.mnt);
 exit:
-	return error;
-}
-
-asmlinkage long sys_unlink(const char * pathname)
-{
-	int error;
-	char * tmp;
-
-	tmp = getname(pathname);
-	if(IS_ERR(tmp))
-		return PTR_ERR(tmp);
-	lock_kernel();
-	error = do_unlink(tmp);
 	unlock_kernel();
-	putname(tmp);
+	putname(name);
 
 	return error;
+
+slashes:
+	error = !dentry->d_inode ? -ENOENT :
+		S_ISDIR(dentry->d_inode->i_mode) ? -EISDIR : -ENOTDIR;
+	goto exit2;
 }
 
 int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname)
@@ -1468,6 +1504,8 @@
 			    &new_dir->i_zombie);
 	error = old_dir->i_op->rename(old_dir, old_dentry, new_dir, new_dentry);
 	if (target) {
+		if (!error)
+			target->i_flags |= S_DEAD;
 		triple_up(&old_dir->i_zombie,
 			  &new_dir->i_zombie,
 			  &target->i_zombie);
@@ -1534,41 +1572,72 @@
 
 static inline int do_rename(const char * oldname, const char * newname)
 {
-	int error;
+	int error = 0;
 	struct dentry * old_dir, * new_dir;
 	struct dentry * old_dentry, *new_dentry;
+	struct nameidata oldnd, newnd;
 
-	old_dentry = lookup_dentry(oldname, LOOKUP_POSITIVE);
+	if (walk_init(oldname, LOOKUP_PARENT, &oldnd))
+		error = walk_name(oldname, &oldnd);
 
-	error = PTR_ERR(old_dentry);
-	if (IS_ERR(old_dentry))
+	if (error)
 		goto exit;
 
-	{
-		unsigned int flags = 0;
-		if (S_ISDIR(old_dentry->d_inode->i_mode))
-			flags = LOOKUP_SLASHOK;
-		new_dentry = lookup_dentry(newname, flags);
-	}
+	if (walk_init(newname, LOOKUP_PARENT, &newnd))
+		error = walk_name(newname, &newnd);
+	if (error)
+		goto exit1;
 
-	error = PTR_ERR(new_dentry);
-	if (IS_ERR(new_dentry))
-		goto exit_old;
+	error = -EXDEV;
+	if (oldnd.mnt != newnd.mnt)
+		goto exit2;
 
-	new_dir = get_parent(new_dentry);
-	old_dir = get_parent(old_dentry);
+	old_dir = oldnd.dentry;
+	error = -EBUSY;
+	if (oldnd.last_type != LAST_NORM)
+		goto exit2;
+
+	new_dir = newnd.dentry;
+	if (newnd.last_type != LAST_NORM)
+		goto exit2;
 
 	double_lock(new_dir, old_dir);
 
+	old_dentry = lookup_hash(&oldnd.last, old_dir);
+	error = PTR_ERR(old_dentry);
+	if (IS_ERR(old_dentry))
+		goto exit3;
+	/* source must exist */
 	error = -ENOENT;
-	if (check_parent(old_dir, old_dentry) && check_parent(new_dir, new_dentry))
-		error = vfs_rename(old_dir->d_inode, old_dentry,
+	if (!old_dentry->d_inode)
+		goto exit4;
+	/* unless the source is a directory trailing slashes give -ENOTDIR */
+	if (!S_ISDIR(old_dentry->d_inode->i_mode)) {
+		error = -ENOTDIR;
+		if (oldnd.last.name[oldnd.last.len])
+			goto exit4;
+		if (newnd.last.name[newnd.last.len])
+			goto exit4;
+	}
+	new_dentry = lookup_hash(&newnd.last, new_dir);
+	error = PTR_ERR(new_dentry);
+	if (IS_ERR(new_dentry))
+		goto exit4;
+
+	error = vfs_rename(old_dir->d_inode, old_dentry,
 				   new_dir->d_inode, new_dentry);
 
-	double_unlock(new_dir, old_dir);
 	dput(new_dentry);
-exit_old:
+exit4:
 	dput(old_dentry);
+exit3:
+	double_up(&new_dir->d_inode->i_sem, &old_dir->d_inode->i_sem);
+exit2:
+	dput(newnd.dentry);
+	mntput(newnd.mnt);
+exit1:
+	dput(oldnd.dentry);
+	mntput(oldnd.mnt);
 exit:
 	return error;
 }
@@ -1620,11 +1689,11 @@
 	if (*link == '/') {
 		dput(nd->dentry);
 		mntput(nd->mnt);
-		if (!walk_init_root(link, LOOKUP_FOLLOW, nd))
+		if (!walk_init_root(link, nd))
 			/* weird __emul_prefix() stuff did it */
 			return 0;
 	}
-	return walk_name(link, LOOKUP_FOLLOW, nd);
+	return walk_name(link, nd);
 
 fail:
 	dput(nd->dentry);

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