patch-2.2.6 linux/fs/namei.c

Next file: linux/fs/ncpfs/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.2.5/linux/fs/namei.c linux/fs/namei.c
@@ -279,7 +279,8 @@
 {
 	struct inode * inode = dentry->d_inode;
 
-	if (inode && inode->i_op && inode->i_op->follow_link) {
+	if ((follow & LOOKUP_FOLLOW)
+	    && inode && inode->i_op && inode->i_op->follow_link) {
 		if (current->link_count < 5) {
 			struct dentry * result;
 
@@ -403,9 +404,6 @@
 		/* Check mountpoints.. */
 		dentry = follow_mount(dentry);
 
-		if (!(flags & LOOKUP_FOLLOW))
-			break;
-
 		base = do_follow_link(base, dentry, flags);
 		if (IS_ERR(base))
 			goto return_base;
@@ -1034,17 +1032,13 @@
 	int error;
 
 	error = may_delete(dir, dentry, 0);
-	if (error)
-		goto exit_lock;
-
-	if (!dir->i_op || !dir->i_op->unlink)
-		goto exit_lock;
-
-	DQUOT_INIT(dir);
-
-	error = dir->i_op->unlink(dir, dentry);
-
-exit_lock:
+	if (!error) {
+		error = -EPERM;
+		if (dir->i_op && dir->i_op->unlink) {
+			DQUOT_INIT(dir);
+			error = dir->i_op->unlink(dir, dentry);
+		}
+	}
 	return error;
 }
 
@@ -1231,15 +1225,16 @@
 	return error;
 }
 
-int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
+int vfs_rename_dir(struct inode *old_dir, struct dentry *old_dentry,
 	       struct inode *new_dir, struct dentry *new_dentry)
 {
 	int error;
-	int isdir;
+	int need_rehash = 0;
 
-	isdir = S_ISDIR(old_dentry->d_inode->i_mode);
+	if (old_dentry->d_inode == new_dentry->d_inode)
+		return 0;
 
-	error = may_delete(old_dir, old_dentry, isdir); /* XXX */
+	error = may_delete(old_dir, old_dentry, 1);
 	if (error)
 		return error;
 
@@ -1249,18 +1244,89 @@
 	if (!new_dentry->d_inode)
 		error = may_create(new_dir, new_dentry);
 	else
-		error = may_delete(new_dir, new_dentry, isdir);
+		error = may_delete(new_dir, new_dentry, 1);
 	if (error)
 		return error;
 
 	if (!old_dir->i_op || !old_dir->i_op->rename)
 		return -EPERM;
 
+	/*
+	 * If we are going to change the parent - check write permissions,
+	 * we'll need to flip '..'.
+	 */
+	if (new_dir != old_dir) {
+		error = permission(old_dentry->d_inode, MAY_WRITE);
+	}
+	if (error)
+		return error;
+
 	DQUOT_INIT(old_dir);
 	DQUOT_INIT(new_dir);
+	down(&old_dir->i_sb->s_vfs_rename_sem);
+	error = -EINVAL;
+	if (is_subdir(new_dentry, old_dentry))
+		goto out_unlock;
+	if (new_dentry->d_inode) {
+		error = -EBUSY;
+		if (d_invalidate(new_dentry)<0)
+			goto out_unlock;
+		need_rehash = 1;
+	}
 	error = old_dir->i_op->rename(old_dir, old_dentry, new_dir, new_dentry);
-
+	if (need_rehash)
+		d_rehash(new_dentry);
+	if (!error)
+		d_move(old_dentry,new_dentry);
+out_unlock:
+	up(&old_dir->i_sb->s_vfs_rename_sem);
 	return error;
+}
+
+int vfs_rename_other(struct inode *old_dir, struct dentry *old_dentry,
+	       struct inode *new_dir, struct dentry *new_dentry)
+{
+	int error;
+
+	if (old_dentry->d_inode == new_dentry->d_inode)
+		return 0;
+
+	error = may_delete(old_dir, old_dentry, 0);
+	if (error)
+		return error;
+
+	if (new_dir->i_dev != old_dir->i_dev)
+		return -EXDEV;
+
+	if (!new_dentry->d_inode)
+		error = may_create(new_dir, new_dentry);
+	else
+		error = may_delete(new_dir, new_dentry, 0);
+	if (error)
+		return error;
+
+	if (!old_dir->i_op || !old_dir->i_op->rename)
+		return -EPERM;
+
+	DQUOT_INIT(old_dir);
+	DQUOT_INIT(new_dir);
+	error = old_dir->i_op->rename(old_dir, old_dentry, new_dir, new_dentry);
+	if (error)
+		return error;
+	/* The following d_move() should become unconditional */
+	if (!(old_dir->i_sb->s_flags & MS_ODD_RENAME)) {
+		d_move(old_dentry, new_dentry);
+	}
+	return 0;
+}
+
+int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
+	       struct inode *new_dir, struct dentry *new_dentry)
+{
+	if (S_ISDIR(old_dentry->d_inode->i_mode))
+		return vfs_rename_dir(old_dir,old_dentry,new_dir,new_dentry);
+	else
+		return vfs_rename_other(old_dir,old_dentry,new_dir,new_dentry);
 }
 
 static inline int do_rename(const char * oldname, const char * newname)

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