patch-2.1.52 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.51/linux/fs/namei.c linux/fs/namei.c
@@ -460,16 +460,42 @@
 	return dentry;
 }
 
-static inline struct inode *get_parent(struct dentry *dentry)
+static inline struct dentry *get_parent(struct dentry *dentry)
 {
-	return dentry->d_parent->d_inode;
+	return dget(dentry->d_parent);
 }
 
-static inline struct inode *lock_parent(struct dentry *dentry)
+static inline void unlock_dir(struct dentry *dir)
 {
-	struct inode *dir = dentry->d_parent->d_inode;
+	up(&dir->d_inode->i_sem);
+	dput(dir);
+}
 
-	down(&dir->i_sem);
+/*
+ * Locking the parent is needed to:
+ *  - serialize directory operations
+ *  - make sure the parent doesn't change from
+ *    under us in the middle of an operation.
+ *
+ * NOTE! Right now we'd rather use a "struct inode"
+ * for this, but as I expect things to move toward
+ * using dentries instead for most things it is
+ * probably better to start with the conceptually
+ * better interface of relying on a path of dentries.
+ */
+static inline struct dentry *lock_parent(struct dentry *dentry)
+{
+	struct dentry *dir = dget(dentry->d_parent);
+
+	down(&dir->d_inode->i_sem);
+
+	/* Un-hashed or moved?  Punt if so.. */
+	if (dir != dentry->d_parent || list_empty(&dentry->d_hash)) {
+		if (dir != dentry) {
+			unlock_dir(dir);
+			dir = ERR_PTR(-ENOENT);
+		}
+	}
 	return dir;
 }
 
@@ -501,9 +527,12 @@
 
 	acc_mode = ACC_MODE(flag);
 	if (flag & O_CREAT) {
-		struct inode *dir;
+		struct dentry *dir;
 
 		dir = lock_parent(dentry);
+		error = PTR_ERR(dir);
+		if (IS_ERR(dir))
+			goto exit;
 		/*
 		 * The existence test must be done _after_ getting the directory
 		 * semaphore - the dentry might otherwise change.
@@ -512,19 +541,19 @@
 			error = 0;
 			if (flag & O_EXCL)
 				error = -EEXIST;
-		} else if (IS_RDONLY(dir))
+		} else if (IS_RDONLY(dir->d_inode))
 			error = -EROFS;
-		else if (!dir->i_op || !dir->i_op->create)
+		else if (!dir->d_inode->i_op || !dir->d_inode->i_op->create)
 			error = -EACCES;
-		else if ((error = permission(dir,MAY_WRITE | MAY_EXEC)) == 0) {
-			if (dir->i_sb && dir->i_sb->dq_op)
-				dir->i_sb->dq_op->initialize(dir, -1);
-			error = dir->i_op->create(dir, dentry, mode);
+		else if ((error = permission(dir->d_inode,MAY_WRITE | MAY_EXEC)) == 0) {
+			if (dir->d_inode->i_sb && dir->d_inode->i_sb->dq_op)
+				dir->d_inode->i_sb->dq_op->initialize(dir->d_inode, -1);
+			error = dir->d_inode->i_op->create(dir->d_inode, dentry, mode);
 			/* Don't check for write permission, don't truncate */
 			acc_mode = 0;
 			flag &= ~O_TRUNC;
 		}
-		up(&dir->i_sem);
+		unlock_dir(dir);
 		if (error)
 			goto exit;
 	}
@@ -600,7 +629,7 @@
 struct dentry * do_mknod(const char * filename, int mode, dev_t dev)
 {
 	int error;
-	struct inode *dir;
+	struct dentry *dir;
 	struct dentry *dentry, *retval;
 
 	mode &= ~current->fs->umask;
@@ -609,33 +638,37 @@
 		return dentry;
 
 	dir = lock_parent(dentry);
+	retval = dir;
+	if (IS_ERR(dir))
+		goto exit;
 
 	retval = ERR_PTR(-EEXIST);
 	if (dentry->d_inode)
 		goto exit_lock;
 
 	retval = ERR_PTR(-EROFS);
-	if (IS_RDONLY(dir))
+	if (IS_RDONLY(dir->d_inode))
 		goto exit_lock;
 
-	error = permission(dir,MAY_WRITE | MAY_EXEC);
+	error = permission(dir->d_inode,MAY_WRITE | MAY_EXEC);
 	retval = ERR_PTR(error);
 	if (error)
 		goto exit_lock;
 
 	retval = ERR_PTR(-EPERM);
-	if (!dir->i_op || !dir->i_op->mknod)
+	if (!dir->d_inode->i_op || !dir->d_inode->i_op->mknod)
 		goto exit_lock;
 
-	if (dir->i_sb && dir->i_sb->dq_op)
-		dir->i_sb->dq_op->initialize(dir, -1);
-	error = dir->i_op->mknod(dir, dentry, mode, dev);
+	if (dir->d_inode->i_sb && dir->d_inode->i_sb->dq_op)
+		dir->d_inode->i_sb->dq_op->initialize(dir->d_inode, -1);
+	error = dir->d_inode->i_op->mknod(dir->d_inode, dentry, mode, dev);
 	retval = ERR_PTR(error);
 	if (!error)
 		retval = dget(dentry);
 
 exit_lock:
-	up(&dir->i_sem);
+	unlock_dir(dir);
+exit:
 	dput(dentry);
 	return retval;
 }
@@ -682,7 +715,7 @@
 static inline int do_mkdir(const char * pathname, int mode)
 {
 	int error;
-	struct inode *dir;
+	struct dentry *dir;
 	struct dentry *dentry;
 
 	dentry = lookup_dentry(pathname, NULL, 1);
@@ -692,29 +725,33 @@
 
 	dir = lock_parent(dentry);
 
+	error = PTR_ERR(dir);
+	if (IS_ERR(dir))
+		goto exit;
+
 	error = -EEXIST;
 	if (dentry->d_inode)
 		goto exit_lock;
 
 	error = -EROFS;
-	if (IS_RDONLY(dir))
+	if (IS_RDONLY(dir->d_inode))
 		goto exit_lock;
 
-	error = permission(dir,MAY_WRITE | MAY_EXEC);
+	error = permission(dir->d_inode,MAY_WRITE | MAY_EXEC);
 	if (error)
 		goto exit_lock;
 
 	error = -EPERM;
-	if (!dir->i_op || !dir->i_op->mkdir)
+	if (!dir->d_inode->i_op || !dir->d_inode->i_op->mkdir)
 		goto exit_lock;
 
-	if (dir->i_sb && dir->i_sb->dq_op)
-		dir->i_sb->dq_op->initialize(dir, -1);
+	if (dir->d_inode->i_sb && dir->d_inode->i_sb->dq_op)
+		dir->d_inode->i_sb->dq_op->initialize(dir->d_inode, -1);
 	mode &= 0777 & ~current->fs->umask;
-	error = dir->i_op->mkdir(dir, dentry, mode);
+	error = dir->d_inode->i_op->mkdir(dir->d_inode, dentry, mode);
 
 exit_lock:
-	up(&dir->i_sem);
+	unlock_dir(dir);
 	dput(dentry);
 exit:
 	return error;
@@ -739,7 +776,7 @@
 static inline int do_rmdir(const char * name)
 {
 	int error;
-	struct inode *dir;
+	struct dentry *dir;
 	struct dentry *dentry;
 
 	dentry = lookup_dentry(name, NULL, 0);
@@ -748,15 +785,20 @@
 		goto exit;
 
 	dir = lock_parent(dentry);
+
+	error = PTR_ERR(dir);
+	if (IS_ERR(dir))
+		goto exit;
+
 	error = -ENOENT;
 	if (!dentry->d_inode)
 		goto exit_lock;
 
 	error = -EROFS;
-	if (IS_RDONLY(dir))
+	if (IS_RDONLY(dir->d_inode))
 		goto exit_lock;
 
-	error = permission(dir,MAY_WRITE | MAY_EXEC);
+	error = permission(dir->d_inode,MAY_WRITE | MAY_EXEC);
 	if (error)
 		goto exit_lock;
 
@@ -764,25 +806,25 @@
 	 * A subdirectory cannot be removed from an append-only directory.
 	 */
 	error = -EPERM;
-	if (IS_APPEND(dir))
+	if (IS_APPEND(dir->d_inode))
 		goto exit_lock;
 
 	/* Disallow removals of mountpoints. */
 	error = -EBUSY;
-	if (dentry->d_covers != dentry)
+	if (dentry == dir)
 		goto exit_lock;
 
 	error = -EPERM;
-	if (!dir->i_op || !dir->i_op->rmdir)
+	if (!dir->d_inode->i_op || !dir->d_inode->i_op->rmdir)
 		goto exit_lock;
 
-	if (dir->i_sb && dir->i_sb->dq_op)
-		dir->i_sb->dq_op->initialize(dir, -1);
+	if (dir->d_inode->i_sb && dir->d_inode->i_sb->dq_op)
+		dir->d_inode->i_sb->dq_op->initialize(dir->d_inode, -1);
 
-	error = dir->i_op->rmdir(dir, dentry);
+	error = dir->d_inode->i_op->rmdir(dir->d_inode, dentry);
 
 exit_lock:
-        up(&dir->i_sem);
+        unlock_dir(dir);
 	dput(dentry);
 exit:
 	return error;
@@ -807,7 +849,7 @@
 static inline int do_unlink(const char * name)
 {
 	int error;
-	struct inode *dir;
+	struct dentry *dir;
 	struct dentry *dentry;
 
 	dentry = lookup_dentry(name, NULL, 0);
@@ -817,15 +859,24 @@
 
 	dir = lock_parent(dentry);
 
+	error = PTR_ERR(dir);
+	if (IS_ERR(dir))
+		goto exit;
+
 	error = -ENOENT;
 	if (!dentry->d_inode)
 		goto exit_lock;
 
+	/* Mount point? */
+	error = -EBUSY;
+	if (dentry == dir)
+		goto exit_lock;
+
 	error = -EROFS;
-	if (IS_RDONLY(dir))
+	if (IS_RDONLY(dir->d_inode))
 		goto exit_lock;
 
-	error = permission(dir,MAY_WRITE | MAY_EXEC);
+	error = permission(dir->d_inode,MAY_WRITE | MAY_EXEC);
 	if (error)
 		goto exit_lock;
 
@@ -833,20 +884,20 @@
 	 * A file cannot be removed from an append-only directory.
 	 */
 	error = -EPERM;
-	if (IS_APPEND(dir))
+	if (IS_APPEND(dir->d_inode))
 		goto exit_lock;
 
 	error = -EPERM;
-	if (!dir->i_op || !dir->i_op->unlink)
+	if (!dir->d_inode->i_op || !dir->d_inode->i_op->unlink)
 		goto exit_lock;
 
-	if (dir->i_sb && dir->i_sb->dq_op)
-		dir->i_sb->dq_op->initialize(dir, -1);
+	if (dir->d_inode->i_sb && dir->d_inode->i_sb->dq_op)
+		dir->d_inode->i_sb->dq_op->initialize(dir->d_inode, -1);
 
-	error = dir->i_op->unlink(dir, dentry);
+	error = dir->d_inode->i_op->unlink(dir->d_inode, dentry);
 
 exit_lock:
-        up(&dir->i_sem);
+        unlock_dir(dir);
 	dput(dentry);
 exit:
 	return error;
@@ -871,7 +922,7 @@
 static inline int do_symlink(const char * oldname, const char * newname)
 {
 	int error;
-	struct inode *dir;
+	struct dentry *dir;
 	struct dentry *dentry;
 
 	dentry = lookup_dentry(newname, NULL, 0);
@@ -882,28 +933,32 @@
 
 	dir = lock_parent(dentry);
 
+	error = PTR_ERR(dir);
+	if (IS_ERR(dir))
+		goto exit;
+
 	error = -EEXIST;
 	if (dentry->d_inode)
 		goto exit_lock;
 
 	error = -EROFS;
-	if (IS_RDONLY(dir))
+	if (IS_RDONLY(dir->d_inode))
 		goto exit_lock;
 
-	error = permission(dir,MAY_WRITE | MAY_EXEC);
+	error = permission(dir->d_inode,MAY_WRITE | MAY_EXEC);
 	if (error)
 		goto exit_lock;
 
 	error = -EPERM;
-	if (!dir->i_op || !dir->i_op->symlink)
+	if (!dir->d_inode->i_op || !dir->d_inode->i_op->symlink)
 		goto exit_lock;
 
-	if (dir->i_sb && dir->i_sb->dq_op)
-		dir->i_sb->dq_op->initialize(dir, -1);
-	error = dir->i_op->symlink(dir, dentry, oldname);
+	if (dir->d_inode->i_sb && dir->d_inode->i_sb->dq_op)
+		dir->d_inode->i_sb->dq_op->initialize(dir->d_inode, -1);
+	error = dir->d_inode->i_op->symlink(dir->d_inode, dentry, oldname);
 
 exit_lock:
-	up(&dir->i_sem);
+	unlock_dir(dir);
 	dput(dentry);
 exit:
 	return error;
@@ -933,8 +988,8 @@
 
 static inline int do_link(const char * oldname, const char * newname)
 {
-	struct dentry *old_dentry, *new_dentry;
-	struct inode *dir, *inode;
+	struct dentry *old_dentry, *new_dentry, *dir;
+	struct inode *inode;
 	int error;
 
 	old_dentry = lookup_dentry(oldname, NULL, 1);
@@ -949,6 +1004,10 @@
 
 	dir = lock_parent(new_dentry);
 
+	error = PTR_ERR(dir);
+	if (IS_ERR(dir))
+		goto exit;
+
 	error = -ENOENT;
 	inode = old_dentry->d_inode;
 	if (!inode)
@@ -959,14 +1018,14 @@
 		goto exit_lock;
 
 	error = -EROFS;
-	if (IS_RDONLY(dir))
+	if (IS_RDONLY(dir->d_inode))
 		goto exit_lock;
 
 	error = -EXDEV;
-	if (dir->i_dev != inode->i_dev)
+	if (dir->d_inode->i_dev != inode->i_dev)
 		goto exit_lock;
 
-	error = permission(dir, MAY_WRITE | MAY_EXEC);
+	error = permission(dir->d_inode, MAY_WRITE | MAY_EXEC);
 	if (error)
 		goto exit_lock;
 
@@ -978,15 +1037,15 @@
 		goto exit_lock;
 
 	error = -EPERM;
-	if (!dir->i_op || !dir->i_op->link)
+	if (!dir->d_inode->i_op || !dir->d_inode->i_op->link)
 		goto exit_lock;
 
-	if (dir->i_sb && dir->i_sb->dq_op)
-		dir->i_sb->dq_op->initialize(dir, -1);
-	error = dir->i_op->link(inode, dir, new_dentry);
+	if (dir->d_inode->i_sb && dir->d_inode->i_sb->dq_op)
+		dir->d_inode->i_sb->dq_op->initialize(dir->d_inode, -1);
+	error = dir->d_inode->i_op->link(inode, dir->d_inode, new_dentry);
 
 exit_lock:
-	up(&dir->i_sem);
+	unlock_dir(dir);
 	dput(new_dentry);
 exit_old:
 	dput(old_dentry);
@@ -1020,30 +1079,37 @@
  * Whee.. Deadlock country. Happily there is only one VFS
  * operation that does this..
  */
-static inline void double_down(struct semaphore *s1, struct semaphore *s2)
+static inline void double_lock(struct dentry *d1, struct dentry *d2)
 {
-	if ((unsigned long) s1 < (unsigned long) s2) {
-		down(s1);
-		down(s2);
-	} else if (s1 == s2) {
-		down(s1);
-	} else {
-		down(s2);
+	struct semaphore *s1 = &d1->d_inode->i_sem;
+	struct semaphore *s2 = &d2->d_inode->i_sem;
+
+	if (s1 != s2) {
+		if ((unsigned long) s1 < (unsigned long) s2) {
+			struct semaphore *tmp = s2;
+			s2 = s1; s1 = tmp;
+		}
 		down(s1);
 	}
+	down(s2);
 }
 
-static inline void double_up(struct semaphore *s1, struct semaphore *s2)
+static inline void double_unlock(struct dentry *d1, struct dentry *d2)
 {
+	struct semaphore *s1 = &d1->d_inode->i_sem;
+	struct semaphore *s2 = &d2->d_inode->i_sem;
+
 	up(s1);
 	if (s1 != s2)
 		up(s2);
+	dput(d1);
+	dput(d2);
 }
 
 static inline int do_rename(const char * oldname, const char * newname)
 {
 	int error;
-	struct inode * old_dir, * new_dir;
+	struct dentry * old_dir, * new_dir;
 	struct dentry * old_dentry, *new_dentry;
 
 	old_dentry = lookup_dentry(oldname, NULL, 0);
@@ -1061,52 +1127,49 @@
 	new_dir = get_parent(new_dentry);
 	old_dir = get_parent(old_dentry);
 
-	double_down(&new_dir->i_sem, &old_dir->i_sem);
+	double_lock(new_dir, old_dir);
 
 	error = -ENOENT;
 	if (!old_dentry->d_inode)
 		goto exit_lock;
 
-	error = permission(old_dir,MAY_WRITE | MAY_EXEC);
+	error = permission(old_dir->d_inode,MAY_WRITE | MAY_EXEC);
 	if (error)
 		goto exit_lock;
-	error = permission(new_dir,MAY_WRITE | MAY_EXEC);
+	error = permission(new_dir->d_inode,MAY_WRITE | MAY_EXEC);
 	if (error)
 		goto exit_lock;
 
-	/*
-	 * Disallow moves of mountpoints.  There is no technical 
-	 * reason for this, but user level stuff gets too confused.
-	 */
+	/* Disallow moves of mountpoints. */
 	error = -EBUSY;
-	if (old_dentry->d_covers != old_dentry)
+	if (old_dir == old_dentry || new_dir == new_dentry)
 		goto exit_lock;
 
 	error = -EXDEV;
-	if (new_dir->i_dev != old_dir->i_dev)
+	if (new_dir->d_inode->i_dev != old_dir->d_inode->i_dev)
 		goto exit_lock;
 
 	error = -EROFS;
-	if (IS_RDONLY(new_dir) || IS_RDONLY(old_dir))
+	if (IS_RDONLY(new_dir->d_inode) || IS_RDONLY(old_dir->d_inode))
 		goto exit_lock;
 
 	/*
 	 * A file cannot be removed from an append-only directory.
 	 */
 	error = -EPERM;
-	if (IS_APPEND(old_dir))
+	if (IS_APPEND(old_dir->d_inode))
 		goto exit_lock;
 
 	error = -EPERM;
-	if (!old_dir->i_op || !old_dir->i_op->rename)
+	if (!old_dir->d_inode->i_op || !old_dir->d_inode->i_op->rename)
 		goto exit_lock;
 
-	if (new_dir->i_sb && new_dir->i_sb->dq_op)
-		new_dir->i_sb->dq_op->initialize(new_dir, -1);
-	error = old_dir->i_op->rename(old_dir, old_dentry, new_dir, new_dentry);
+	if (new_dir->d_inode->i_sb && new_dir->d_inode->i_sb->dq_op)
+		new_dir->d_inode->i_sb->dq_op->initialize(new_dir->d_inode, -1);
+	error = old_dir->d_inode->i_op->rename(old_dir->d_inode, old_dentry, new_dir->d_inode, new_dentry);
 
 exit_lock:
-	double_up(&new_dir->i_sem, &old_dir->i_sem);
+	double_unlock(new_dir, old_dir);
 	dput(new_dentry);
 exit_old:
 	dput(old_dentry);

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