Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c30dabfe authored by Jan Kara's avatar Jan Kara Committed by Al Viro
Browse files

fs: Push mnt_want_write() outside of i_mutex



Currently, mnt_want_write() is sometimes called with i_mutex held and sometimes
without it. This isn't really a problem because mnt_want_write() is a
non-blocking operation (essentially has a trylock semantics) but when the
function starts to handle also frozen filesystems, it will get a full lock
semantics and thus proper lock ordering has to be established. So move
all mnt_want_write() calls outside of i_mutex.

One non-trivial case needing conversion is kern_path_create() /
user_path_create() which didn't include mnt_want_write() but now needs to
because it acquires i_mutex.  Because there are virtual file systems which
don't bother with freeze / remount-ro protection we actually provide both
versions of the function - one which calls mnt_want_write() and one which does
not.

[AV: scratch the previous, mnt_want_write() has been moved to kern_path_create()
by now]

Signed-off-by: default avatarJan Kara <jack@suse.cz>
Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent 4fcf1c62
Loading
Loading
Loading
Loading
+25 −21
Original line number Original line Diff line number Diff line
@@ -2975,6 +2975,7 @@ struct dentry *kern_path_create(int dfd, const char *pathname, struct path *path
{
{
	struct dentry *dentry = ERR_PTR(-EEXIST);
	struct dentry *dentry = ERR_PTR(-EEXIST);
	struct nameidata nd;
	struct nameidata nd;
	int err2;
	int error = do_path_lookup(dfd, pathname, LOOKUP_PARENT, &nd);
	int error = do_path_lookup(dfd, pathname, LOOKUP_PARENT, &nd);
	if (error)
	if (error)
		return ERR_PTR(error);
		return ERR_PTR(error);
@@ -2988,6 +2989,8 @@ struct dentry *kern_path_create(int dfd, const char *pathname, struct path *path
	nd.flags &= ~LOOKUP_PARENT;
	nd.flags &= ~LOOKUP_PARENT;
	nd.flags |= LOOKUP_CREATE | LOOKUP_EXCL;
	nd.flags |= LOOKUP_CREATE | LOOKUP_EXCL;


	/* don't fail immediately if it's r/o, at least try to report other errors */
	err2 = mnt_want_write(nd.path.mnt);
	/*
	/*
	 * Do the final lookup.
	 * Do the final lookup.
	 */
	 */
@@ -3009,9 +3012,10 @@ struct dentry *kern_path_create(int dfd, const char *pathname, struct path *path
		error = -ENOENT;
		error = -ENOENT;
		goto fail;
		goto fail;
	}
	}
	error = mnt_want_write(nd.path.mnt);
	if (unlikely(err2)) {
	if (error)
		error = err2;
		goto fail;
		goto fail;
	}
	*path = nd.path;
	*path = nd.path;
	return dentry;
	return dentry;
fail:
fail:
@@ -3019,6 +3023,8 @@ struct dentry *kern_path_create(int dfd, const char *pathname, struct path *path
	dentry = ERR_PTR(error);
	dentry = ERR_PTR(error);
unlock:
unlock:
	mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
	mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
	if (!err2)
		mnt_drop_write(nd.path.mnt);
out:
out:
	path_put(&nd.path);
	path_put(&nd.path);
	return dentry;
	return dentry;
@@ -3266,6 +3272,9 @@ static long do_rmdir(int dfd, const char __user *pathname)
	}
	}


	nd.flags &= ~LOOKUP_PARENT;
	nd.flags &= ~LOOKUP_PARENT;
	error = mnt_want_write(nd.path.mnt);
	if (error)
		goto exit1;


	mutex_lock_nested(&nd.path.dentry->d_inode->i_mutex, I_MUTEX_PARENT);
	mutex_lock_nested(&nd.path.dentry->d_inode->i_mutex, I_MUTEX_PARENT);
	dentry = lookup_hash(&nd);
	dentry = lookup_hash(&nd);
@@ -3276,19 +3285,15 @@ static long do_rmdir(int dfd, const char __user *pathname)
		error = -ENOENT;
		error = -ENOENT;
		goto exit3;
		goto exit3;
	}
	}
	error = mnt_want_write(nd.path.mnt);
	if (error)
		goto exit3;
	error = security_path_rmdir(&nd.path, dentry);
	error = security_path_rmdir(&nd.path, dentry);
	if (error)
	if (error)
		goto exit4;
		goto exit3;
	error = vfs_rmdir(nd.path.dentry->d_inode, dentry);
	error = vfs_rmdir(nd.path.dentry->d_inode, dentry);
exit4:
	mnt_drop_write(nd.path.mnt);
exit3:
exit3:
	dput(dentry);
	dput(dentry);
exit2:
exit2:
	mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
	mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
	mnt_drop_write(nd.path.mnt);
exit1:
exit1:
	path_put(&nd.path);
	path_put(&nd.path);
	putname(name);
	putname(name);
@@ -3355,6 +3360,9 @@ static long do_unlinkat(int dfd, const char __user *pathname)
		goto exit1;
		goto exit1;


	nd.flags &= ~LOOKUP_PARENT;
	nd.flags &= ~LOOKUP_PARENT;
	error = mnt_want_write(nd.path.mnt);
	if (error)
		goto exit1;


	mutex_lock_nested(&nd.path.dentry->d_inode->i_mutex, I_MUTEX_PARENT);
	mutex_lock_nested(&nd.path.dentry->d_inode->i_mutex, I_MUTEX_PARENT);
	dentry = lookup_hash(&nd);
	dentry = lookup_hash(&nd);
@@ -3367,21 +3375,17 @@ static long do_unlinkat(int dfd, const char __user *pathname)
		if (!inode)
		if (!inode)
			goto slashes;
			goto slashes;
		ihold(inode);
		ihold(inode);
		error = mnt_want_write(nd.path.mnt);
		if (error)
			goto exit2;
		error = security_path_unlink(&nd.path, dentry);
		error = security_path_unlink(&nd.path, dentry);
		if (error)
		if (error)
			goto exit3;
			goto exit2;
		error = vfs_unlink(nd.path.dentry->d_inode, dentry);
		error = vfs_unlink(nd.path.dentry->d_inode, dentry);
exit3:
		mnt_drop_write(nd.path.mnt);
exit2:
exit2:
		dput(dentry);
		dput(dentry);
	}
	}
	mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
	mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
	if (inode)
	if (inode)
		iput(inode);	/* truncate the inode here */
		iput(inode);	/* truncate the inode here */
	mnt_drop_write(nd.path.mnt);
exit1:
exit1:
	path_put(&nd.path);
	path_put(&nd.path);
	putname(name);
	putname(name);
@@ -3753,6 +3757,10 @@ SYSCALL_DEFINE4(renameat, int, olddfd, const char __user *, oldname,
	if (newnd.last_type != LAST_NORM)
	if (newnd.last_type != LAST_NORM)
		goto exit2;
		goto exit2;


	error = mnt_want_write(oldnd.path.mnt);
	if (error)
		goto exit2;

	oldnd.flags &= ~LOOKUP_PARENT;
	oldnd.flags &= ~LOOKUP_PARENT;
	newnd.flags &= ~LOOKUP_PARENT;
	newnd.flags &= ~LOOKUP_PARENT;
	newnd.flags |= LOOKUP_RENAME_TARGET;
	newnd.flags |= LOOKUP_RENAME_TARGET;
@@ -3788,23 +3796,19 @@ SYSCALL_DEFINE4(renameat, int, olddfd, const char __user *, oldname,
	if (new_dentry == trap)
	if (new_dentry == trap)
		goto exit5;
		goto exit5;


	error = mnt_want_write(oldnd.path.mnt);
	if (error)
		goto exit5;
	error = security_path_rename(&oldnd.path, old_dentry,
	error = security_path_rename(&oldnd.path, old_dentry,
				     &newnd.path, new_dentry);
				     &newnd.path, new_dentry);
	if (error)
	if (error)
		goto exit6;
		goto exit5;
	error = vfs_rename(old_dir->d_inode, old_dentry,
	error = vfs_rename(old_dir->d_inode, old_dentry,
				   new_dir->d_inode, new_dentry);
				   new_dir->d_inode, new_dentry);
exit6:
	mnt_drop_write(oldnd.path.mnt);
exit5:
exit5:
	dput(new_dentry);
	dput(new_dentry);
exit4:
exit4:
	dput(old_dentry);
	dput(old_dentry);
exit3:
exit3:
	unlock_rename(new_dir, old_dir);
	unlock_rename(new_dir, old_dir);
	mnt_drop_write(oldnd.path.mnt);
exit2:
exit2:
	path_put(&newnd.path);
	path_put(&newnd.path);
	putname(to);
	putname(to);