diff options
author | Dave Hansen <haveblue@us.ibm.com> | 2008-02-15 17:37:42 -0500 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2008-04-19 00:29:24 -0400 |
commit | 74f9fdfa1f229284ee1ea58fa47f2cdeeb12f6fe (patch) | |
tree | 6f368c91caf23e1795645d018c8ac971a4dceada /fs/utimes.c | |
parent | cdb70f3f74b31576cc4d707a3d3b00d159cab8bb (diff) |
[PATCH] r/o bind mounts: elevate write count for do_utimes()
Now includes fix for oops seen by akpm.
"never let a libc developer write your kernel code" - hch
"nor, apparently, a kernel developer" - akpm
Acked-by: Al Viro <viro@ZenIV.linux.org.uk>
Signed-off-by: Christoph Hellwig <hch@lst.de>
Cc: Valdis Kletnieks <Valdis.Kletnieks@vt.edu>
Cc: Balbir Singh <balbir@in.ibm.com>
Signed-off-by: Dave Hansen <haveblue@us.ibm.com>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'fs/utimes.c')
-rw-r--r-- | fs/utimes.c | 18 |
1 files changed, 12 insertions, 6 deletions
diff --git a/fs/utimes.c b/fs/utimes.c index b18da9c0b97f..a2bef77dc9c9 100644 --- a/fs/utimes.c +++ b/fs/utimes.c | |||
@@ -2,6 +2,7 @@ | |||
2 | #include <linux/file.h> | 2 | #include <linux/file.h> |
3 | #include <linux/fs.h> | 3 | #include <linux/fs.h> |
4 | #include <linux/linkage.h> | 4 | #include <linux/linkage.h> |
5 | #include <linux/mount.h> | ||
5 | #include <linux/namei.h> | 6 | #include <linux/namei.h> |
6 | #include <linux/sched.h> | 7 | #include <linux/sched.h> |
7 | #include <linux/stat.h> | 8 | #include <linux/stat.h> |
@@ -59,6 +60,7 @@ long do_utimes(int dfd, char __user *filename, struct timespec *times, int flags | |||
59 | struct inode *inode; | 60 | struct inode *inode; |
60 | struct iattr newattrs; | 61 | struct iattr newattrs; |
61 | struct file *f = NULL; | 62 | struct file *f = NULL; |
63 | struct vfsmount *mnt; | ||
62 | 64 | ||
63 | error = -EINVAL; | 65 | error = -EINVAL; |
64 | if (times && (!nsec_valid(times[0].tv_nsec) || | 66 | if (times && (!nsec_valid(times[0].tv_nsec) || |
@@ -79,18 +81,20 @@ long do_utimes(int dfd, char __user *filename, struct timespec *times, int flags | |||
79 | if (!f) | 81 | if (!f) |
80 | goto out; | 82 | goto out; |
81 | dentry = f->f_path.dentry; | 83 | dentry = f->f_path.dentry; |
84 | mnt = f->f_path.mnt; | ||
82 | } else { | 85 | } else { |
83 | error = __user_walk_fd(dfd, filename, (flags & AT_SYMLINK_NOFOLLOW) ? 0 : LOOKUP_FOLLOW, &nd); | 86 | error = __user_walk_fd(dfd, filename, (flags & AT_SYMLINK_NOFOLLOW) ? 0 : LOOKUP_FOLLOW, &nd); |
84 | if (error) | 87 | if (error) |
85 | goto out; | 88 | goto out; |
86 | 89 | ||
87 | dentry = nd.path.dentry; | 90 | dentry = nd.path.dentry; |
91 | mnt = nd.path.mnt; | ||
88 | } | 92 | } |
89 | 93 | ||
90 | inode = dentry->d_inode; | 94 | inode = dentry->d_inode; |
91 | 95 | ||
92 | error = -EROFS; | 96 | error = mnt_want_write(mnt); |
93 | if (IS_RDONLY(inode)) | 97 | if (error) |
94 | goto dput_and_out; | 98 | goto dput_and_out; |
95 | 99 | ||
96 | /* Don't worry, the checks are done in inode_change_ok() */ | 100 | /* Don't worry, the checks are done in inode_change_ok() */ |
@@ -98,7 +102,7 @@ long do_utimes(int dfd, char __user *filename, struct timespec *times, int flags | |||
98 | if (times) { | 102 | if (times) { |
99 | error = -EPERM; | 103 | error = -EPERM; |
100 | if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) | 104 | if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) |
101 | goto dput_and_out; | 105 | goto mnt_drop_write_and_out; |
102 | 106 | ||
103 | if (times[0].tv_nsec == UTIME_OMIT) | 107 | if (times[0].tv_nsec == UTIME_OMIT) |
104 | newattrs.ia_valid &= ~ATTR_ATIME; | 108 | newattrs.ia_valid &= ~ATTR_ATIME; |
@@ -118,22 +122,24 @@ long do_utimes(int dfd, char __user *filename, struct timespec *times, int flags | |||
118 | } else { | 122 | } else { |
119 | error = -EACCES; | 123 | error = -EACCES; |
120 | if (IS_IMMUTABLE(inode)) | 124 | if (IS_IMMUTABLE(inode)) |
121 | goto dput_and_out; | 125 | goto mnt_drop_write_and_out; |
122 | 126 | ||
123 | if (!is_owner_or_cap(inode)) { | 127 | if (!is_owner_or_cap(inode)) { |
124 | if (f) { | 128 | if (f) { |
125 | if (!(f->f_mode & FMODE_WRITE)) | 129 | if (!(f->f_mode & FMODE_WRITE)) |
126 | goto dput_and_out; | 130 | goto mnt_drop_write_and_out; |
127 | } else { | 131 | } else { |
128 | error = vfs_permission(&nd, MAY_WRITE); | 132 | error = vfs_permission(&nd, MAY_WRITE); |
129 | if (error) | 133 | if (error) |
130 | goto dput_and_out; | 134 | goto mnt_drop_write_and_out; |
131 | } | 135 | } |
132 | } | 136 | } |
133 | } | 137 | } |
134 | mutex_lock(&inode->i_mutex); | 138 | mutex_lock(&inode->i_mutex); |
135 | error = notify_change(dentry, &newattrs); | 139 | error = notify_change(dentry, &newattrs); |
136 | mutex_unlock(&inode->i_mutex); | 140 | mutex_unlock(&inode->i_mutex); |
141 | mnt_drop_write_and_out: | ||
142 | mnt_drop_write(mnt); | ||
137 | dput_and_out: | 143 | dput_and_out: |
138 | if (f) | 144 | if (f) |
139 | fput(f); | 145 | fput(f); |