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

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
...@@ -2975,6 +2975,7 @@ struct dentry *kern_path_create(int dfd, const char *pathname, struct path *path ...@@ -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 ...@@ -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 ...@@ -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 ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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: exit2:
mnt_drop_write(nd.path.mnt);
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, ...@@ -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, ...@@ -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);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment