diff options
author | Jan Kara <jack@suse.cz> | 2012-06-12 10:20:30 -0400 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2012-07-30 17:02:49 -0400 |
commit | c30dabfe5d10c5fd70d882e5afb8f59f2942b194 (patch) | |
tree | 2a643a825fb91a1ae48c5673a9f5fcba891b6122 /fs | |
parent | 4fcf1c6205fcfc7a226a144ae4d83b7f5415cab8 (diff) |
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: Jan Kara <jack@suse.cz>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/namei.c | 46 |
1 files changed, 25 insertions, 21 deletions
diff --git a/fs/namei.c b/fs/namei.c index 35291ac6f42b..1b464390dde8 100644 --- a/fs/namei.c +++ b/fs/namei.c | |||
@@ -2975,6 +2975,7 @@ struct dentry *kern_path_create(int dfd, const char *pathname, struct path *path | |||
2975 | { | 2975 | { |
2976 | struct dentry *dentry = ERR_PTR(-EEXIST); | 2976 | struct dentry *dentry = ERR_PTR(-EEXIST); |
2977 | struct nameidata nd; | 2977 | struct nameidata nd; |
2978 | int err2; | ||
2978 | int error = do_path_lookup(dfd, pathname, LOOKUP_PARENT, &nd); | 2979 | int error = do_path_lookup(dfd, pathname, LOOKUP_PARENT, &nd); |
2979 | if (error) | 2980 | if (error) |
2980 | return ERR_PTR(error); | 2981 | return ERR_PTR(error); |
@@ -2988,6 +2989,8 @@ struct dentry *kern_path_create(int dfd, const char *pathname, struct path *path | |||
2988 | nd.flags &= ~LOOKUP_PARENT; | 2989 | nd.flags &= ~LOOKUP_PARENT; |
2989 | nd.flags |= LOOKUP_CREATE | LOOKUP_EXCL; | 2990 | nd.flags |= LOOKUP_CREATE | LOOKUP_EXCL; |
2990 | 2991 | ||
2992 | /* don't fail immediately if it's r/o, at least try to report other errors */ | ||
2993 | err2 = mnt_want_write(nd.path.mnt); | ||
2991 | /* | 2994 | /* |
2992 | * Do the final lookup. | 2995 | * Do the final lookup. |
2993 | */ | 2996 | */ |
@@ -3009,9 +3012,10 @@ struct dentry *kern_path_create(int dfd, const char *pathname, struct path *path | |||
3009 | error = -ENOENT; | 3012 | error = -ENOENT; |
3010 | goto fail; | 3013 | goto fail; |
3011 | } | 3014 | } |
3012 | error = mnt_want_write(nd.path.mnt); | 3015 | if (unlikely(err2)) { |
3013 | if (error) | 3016 | error = err2; |
3014 | goto fail; | 3017 | goto fail; |
3018 | } | ||
3015 | *path = nd.path; | 3019 | *path = nd.path; |
3016 | return dentry; | 3020 | return dentry; |
3017 | fail: | 3021 | fail: |
@@ -3019,6 +3023,8 @@ fail: | |||
3019 | dentry = ERR_PTR(error); | 3023 | dentry = ERR_PTR(error); |
3020 | unlock: | 3024 | unlock: |
3021 | mutex_unlock(&nd.path.dentry->d_inode->i_mutex); | 3025 | mutex_unlock(&nd.path.dentry->d_inode->i_mutex); |
3026 | if (!err2) | ||
3027 | mnt_drop_write(nd.path.mnt); | ||
3022 | out: | 3028 | out: |
3023 | path_put(&nd.path); | 3029 | path_put(&nd.path); |
3024 | return dentry; | 3030 | return dentry; |
@@ -3266,6 +3272,9 @@ static long do_rmdir(int dfd, const char __user *pathname) | |||
3266 | } | 3272 | } |
3267 | 3273 | ||
3268 | nd.flags &= ~LOOKUP_PARENT; | 3274 | nd.flags &= ~LOOKUP_PARENT; |
3275 | error = mnt_want_write(nd.path.mnt); | ||
3276 | if (error) | ||
3277 | goto exit1; | ||
3269 | 3278 | ||
3270 | mutex_lock_nested(&nd.path.dentry->d_inode->i_mutex, I_MUTEX_PARENT); | 3279 | mutex_lock_nested(&nd.path.dentry->d_inode->i_mutex, I_MUTEX_PARENT); |
3271 | dentry = lookup_hash(&nd); | 3280 | dentry = lookup_hash(&nd); |
@@ -3276,19 +3285,15 @@ static long do_rmdir(int dfd, const char __user *pathname) | |||
3276 | error = -ENOENT; | 3285 | error = -ENOENT; |
3277 | goto exit3; | 3286 | goto exit3; |
3278 | } | 3287 | } |
3279 | error = mnt_want_write(nd.path.mnt); | ||
3280 | if (error) | ||
3281 | goto exit3; | ||
3282 | error = security_path_rmdir(&nd.path, dentry); | 3288 | error = security_path_rmdir(&nd.path, dentry); |
3283 | if (error) | 3289 | if (error) |
3284 | goto exit4; | 3290 | goto exit3; |
3285 | error = vfs_rmdir(nd.path.dentry->d_inode, dentry); | 3291 | error = vfs_rmdir(nd.path.dentry->d_inode, dentry); |
3286 | exit4: | ||
3287 | mnt_drop_write(nd.path.mnt); | ||
3288 | exit3: | 3292 | exit3: |
3289 | dput(dentry); | 3293 | dput(dentry); |
3290 | exit2: | 3294 | exit2: |
3291 | mutex_unlock(&nd.path.dentry->d_inode->i_mutex); | 3295 | mutex_unlock(&nd.path.dentry->d_inode->i_mutex); |
3296 | mnt_drop_write(nd.path.mnt); | ||
3292 | exit1: | 3297 | exit1: |
3293 | path_put(&nd.path); | 3298 | path_put(&nd.path); |
3294 | putname(name); | 3299 | putname(name); |
@@ -3355,6 +3360,9 @@ static long do_unlinkat(int dfd, const char __user *pathname) | |||
3355 | goto exit1; | 3360 | goto exit1; |
3356 | 3361 | ||
3357 | nd.flags &= ~LOOKUP_PARENT; | 3362 | nd.flags &= ~LOOKUP_PARENT; |
3363 | error = mnt_want_write(nd.path.mnt); | ||
3364 | if (error) | ||
3365 | goto exit1; | ||
3358 | 3366 | ||
3359 | mutex_lock_nested(&nd.path.dentry->d_inode->i_mutex, I_MUTEX_PARENT); | 3367 | mutex_lock_nested(&nd.path.dentry->d_inode->i_mutex, I_MUTEX_PARENT); |
3360 | dentry = lookup_hash(&nd); | 3368 | dentry = lookup_hash(&nd); |
@@ -3367,21 +3375,17 @@ static long do_unlinkat(int dfd, const char __user *pathname) | |||
3367 | if (!inode) | 3375 | if (!inode) |
3368 | goto slashes; | 3376 | goto slashes; |
3369 | ihold(inode); | 3377 | ihold(inode); |
3370 | error = mnt_want_write(nd.path.mnt); | ||
3371 | if (error) | ||
3372 | goto exit2; | ||
3373 | error = security_path_unlink(&nd.path, dentry); | 3378 | error = security_path_unlink(&nd.path, dentry); |
3374 | if (error) | 3379 | if (error) |
3375 | goto exit3; | 3380 | goto exit2; |
3376 | error = vfs_unlink(nd.path.dentry->d_inode, dentry); | 3381 | error = vfs_unlink(nd.path.dentry->d_inode, dentry); |
3377 | exit3: | 3382 | exit2: |
3378 | mnt_drop_write(nd.path.mnt); | ||
3379 | exit2: | ||
3380 | dput(dentry); | 3383 | dput(dentry); |
3381 | } | 3384 | } |
3382 | mutex_unlock(&nd.path.dentry->d_inode->i_mutex); | 3385 | mutex_unlock(&nd.path.dentry->d_inode->i_mutex); |
3383 | if (inode) | 3386 | if (inode) |
3384 | iput(inode); /* truncate the inode here */ | 3387 | iput(inode); /* truncate the inode here */ |
3388 | mnt_drop_write(nd.path.mnt); | ||
3385 | exit1: | 3389 | exit1: |
3386 | path_put(&nd.path); | 3390 | path_put(&nd.path); |
3387 | putname(name); | 3391 | putname(name); |
@@ -3753,6 +3757,10 @@ SYSCALL_DEFINE4(renameat, int, olddfd, const char __user *, oldname, | |||
3753 | if (newnd.last_type != LAST_NORM) | 3757 | if (newnd.last_type != LAST_NORM) |
3754 | goto exit2; | 3758 | goto exit2; |
3755 | 3759 | ||
3760 | error = mnt_want_write(oldnd.path.mnt); | ||
3761 | if (error) | ||
3762 | goto exit2; | ||
3763 | |||
3756 | oldnd.flags &= ~LOOKUP_PARENT; | 3764 | oldnd.flags &= ~LOOKUP_PARENT; |
3757 | newnd.flags &= ~LOOKUP_PARENT; | 3765 | newnd.flags &= ~LOOKUP_PARENT; |
3758 | newnd.flags |= LOOKUP_RENAME_TARGET; | 3766 | newnd.flags |= LOOKUP_RENAME_TARGET; |
@@ -3788,23 +3796,19 @@ SYSCALL_DEFINE4(renameat, int, olddfd, const char __user *, oldname, | |||
3788 | if (new_dentry == trap) | 3796 | if (new_dentry == trap) |
3789 | goto exit5; | 3797 | goto exit5; |
3790 | 3798 | ||
3791 | error = mnt_want_write(oldnd.path.mnt); | ||
3792 | if (error) | ||
3793 | goto exit5; | ||
3794 | error = security_path_rename(&oldnd.path, old_dentry, | 3799 | error = security_path_rename(&oldnd.path, old_dentry, |
3795 | &newnd.path, new_dentry); | 3800 | &newnd.path, new_dentry); |
3796 | if (error) | 3801 | if (error) |
3797 | goto exit6; | 3802 | goto exit5; |
3798 | error = vfs_rename(old_dir->d_inode, old_dentry, | 3803 | error = vfs_rename(old_dir->d_inode, old_dentry, |
3799 | new_dir->d_inode, new_dentry); | 3804 | new_dir->d_inode, new_dentry); |
3800 | exit6: | ||
3801 | mnt_drop_write(oldnd.path.mnt); | ||
3802 | exit5: | 3805 | exit5: |
3803 | dput(new_dentry); | 3806 | dput(new_dentry); |
3804 | exit4: | 3807 | exit4: |
3805 | dput(old_dentry); | 3808 | dput(old_dentry); |
3806 | exit3: | 3809 | exit3: |
3807 | unlock_rename(new_dir, old_dir); | 3810 | unlock_rename(new_dir, old_dir); |
3811 | mnt_drop_write(oldnd.path.mnt); | ||
3808 | exit2: | 3812 | exit2: |
3809 | path_put(&newnd.path); | 3813 | path_put(&newnd.path); |
3810 | putname(to); | 3814 | putname(to); |